(tensorflow v2.4.0)
www.kaggle.com/c/dog-breed-identification
Kaggle의 Dog Breed Identification을 tensorflow의 pre-trained InceptionV3 모델을 Feature Extraction으로 사용해서 구현해보도록 하겠습니다.
1. Data 준비
제공되는 Data는 다음과 같습니다.
- 학습용 Dog image 10222장
- 학습 data의 label.csv
- 제출용 Dog image 10357장
이번에 Kaggle API를 사용해서 Data를 Colab에서 다운받고 진행을 해보려고 했으나, Data 구조가 달라져서 API로는 D모든 Data를 정상적으로 받을 수가 없다고 합니다. 따라서, 참고 서적에서 제공하는 자료를 가지고 진행해보겠습니다.
import os
import pandas as pd
import tensorflow as tf
os.environ['KAGGLE_USERNAME'] = # 캐글 ID
os.environ['KAGGLE_KEY'] = # 캐글 API Token
제출은 Kaggle API로 진행해보려고 합니다. 따라서, 환경 변수를 추가해주어야 하는데, ID와 API Token은 Account에 들어가서 확인할 수 있습니다.
Kaggle 사이트 오른쪽 위에 있는 동그란 이미지(본인의 사진)을 클릭하고, Account에 접속한 후에
Create New API Token을 클릭하면 json파일을 다운받을 수 있습니다. 그리고 json파일을 열어보면 ID와 Key가 있습니다.
데이터를 다운받고, 우선 train.zip의 압축을 해제합니다.
tf.keras.utils.get_file('/content/labels.csv', 'http://bit.ly/2GDxsYS')
tf.keras.utils.get_file('/content/sample_submission.csv', 'http://bit.ly/2GGnMNd')
tf.keras.utils.get_file('/content/train.zip', 'http://bit.ly/31nIyel')
tf.keras.utils.get_file('/content/test.zip', 'http://bit.ly/2GHEsnO')
!unzip train.zip
그리고 train 데이터의 label.csv를 확인해봅니다.
label_text = pd.read_csv('labels.csv')
print(label_text.head())
print()
print(label_text.info())
label.csv는 id와 breed로 이루어져있는데, id는 파일명으로 되어있는 것을 확인할 수 있습니다.
그리고 총 10222개의 이미지가 존재하는 것을 확인할 수 있습니다.
label_text['breed'].nunique()
'breed' 종류의 수를 확인해보면 data에 있는 개의 종류가 120개라는 것을 확인할 수 있습니다.
data의 처음 9개의 이미지를 확인해봅시다.
import PIL.Image as Image
import matplotlib.pyplot as plt
plt.figure(figsize=(12,12))
for c in range(9):
image_id = label_text.loc[c, 'id']
plt.subplot(3,3,c+1)
plt.imshow(plt.imread('/content/train/' + image_id + '.jpg'))
plt.title(str(c) + ', ' + label_text.loc[c, 'breed'])
plt.axis('off')
plt.show()
다양한 각도와 배경의 이미지를 확인할 수 있습니다.
2. Data generator 생성
ImageDataGenerator를 학습에 사용할 예정입니다. ImageDataGenerator의 method인 flow_from_directory를 사용해서 사용될 data를 읽어야하는데, 클래스별로 폴더가 따로 존재해야 합니다.
따라서, data generator를 생성하기 전에 학습 data들을 클래스별로 폴더를 생성하고 이미지 파일들을 각 폴더로 옮겨주도록 하겠습니다.
import shutil
os.mkdir('/content/train_sub')
for i in range(len(label_text)):
if os.path.exists('/content/train_sub/' + label_text.loc[i]['breed']) == False:
os.mkdir('/content/train_sub/' + label_text.loc[i]['breed'])
source = '/content/train/' + label_text.loc[i]['id'] + '.jpg'
dest = '/content/train_sub/' + label_text.loc[i]['breed'] + '/' + label_text.loc[i]['id'] + '.jpg'
shutil.move(source, dest)
각 클래스별로 폴더가 생성되고 이미지들이 옮겨졌습니다.
이제 사용될 data generator를 만들어줍니다. 학습용과 검증용 두 가지의 generator를 생성하며, 학습용에는 랜덤으로 좌우반전, 기울임, 줌, 평행이동 등을 적용해서 data augmentation을 적용하도록 합니다.
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
image_size = 299
batch_size = 32
train_datagen = ImageDataGenerator(rescale=1./255., horizontal_flip=True, shear_range=0.2, zoom_range=0.2, width_shift_range=0.2, height_shift_range=0.2, validation_split=0.25)
valid_datagen = ImageDataGenerator(rescale=1./255., validation_split=0.25)
train_generator = train_datagen.flow_from_directory(directory="/content/train_sub/", subset="training", batch_size=batch_size, seed=42, shuffle=True, class_mode="categorical", target_size=(image_size, image_size))
valid_generator = valid_datagen.flow_from_directory(directory="/content/train_sub/", subset="validation", batch_size=1, seed=42, shuffle=True, class_mode="categorical", target_size=(image_size, image_size))
학습용 데이터의 25%를 검증으로 사용하기 때문에, 총 학습용 data image는 7718개, 검증용 image는 2504개로 구성되었습니다.
3. Model 정의
tensorflow에서 제공되는 pre-trained된 InceptionV3 모델을 feature extraction으로 사용할 예정입니다.
inceptionV3 = tf.keras.applications.InceptionV3(include_top=True, weights='imagenet')
inceptionV3.summary()
inceptionV3 모델을 불러오면, prediction layer도 존재하기 때문에 분리해주고, 120 종류의 클래스를 분류하기 위한 Dense layer를 구성해주도록 하겠습니다.
feature_extraction으로 사용될 부분만 골라서 model로 구성하고, 모든 layer들이 학습이 되지 않도록 동결하였습니다.
feature_extraction = tf.keras.Model(inceptionV3.input, inceptionV3.layers[-2].output)
feature_extraction.trainable = False
feature_extraction.summary()
그리고, 예측에 사용될 layer를 추가해서 모델을 구성해주도록 합니다.
model = tf.keras.models.Sequential(
[feature_extraction,
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(1024, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(120, activation='softmax')]
)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['acc'])
model.summary()
4. 학습
총 20 epoch를 진행할 예정인데, 10 epoch에서의 결과도 제출해보도록 하겠습니다.
그전에 제출 데이터(test.zip) 또한 generator로 생성하고 진행하도록 하겠습니다. test.zip 파일의 압축을 해제합니다.
!unzip test.zip
마찬가지로 클래스별로 폴더를 생성해야하지만, 클래스를 알지못하므로 unknown 폴더로 복사해서 모든 이미지파일이 하나의 폴더에 존재하도록 해주었습니다.
os.mkdir('/content/test_sub/')
os.mkdir('/content/test_sub/unknown/')
for i in range(len(submission)):
shutil.copy('/content/test/' + submission.loc[i]['id'] + '.jpg', '/content/test_sub/unknown/')
그리고 generator를 만들어줍니다.
test_datagen=ImageDataGenerator(rescale=1./255.)
test_generator=test_datagen.flow_from_directory(directory="/content/test_sub/",batch_size=1,seed=42,shuffle=False,target_size=(299, 299))
이제 학습을 진행해보도록 하겠습니다. 우선 10 epoch 동안의 학습입니다.
model.fit(train_generator, epochs=10, validation_data=valid_generator)
랜덤으로 3개의 이미지를 선택해, 예측 결과를 우선 살펴보도록 하겠습니다.
unique_Y = label_text['breed'].unique()
unique_sorted_Y = sorted(unique_Y)
print(unique_sorted_Y)
import random
import cv2
import numpy as np
plt.figure(figsize=(16,16))
for c in range(3):
image_path = random.choice(valid_generator.filepaths)
# 이미지 표시
plt.subplot(3,2,c*2+1)
plt.imshow(plt.imread(image_path))
real_y = image_path.split('/')[3]
plt.title(real_y)
plt.axis('off')
idx = unique_sorted_Y.index(real_y)
# 예측값 표시
plt.subplot(3,2,c*2+2)
img = cv2.imread(image_path)
img = cv2.resize(img, dsize=(299, 299))
img = img / 255.0
img = np.expand_dims(img, axis=0)
# 모델을 이용한 예측
prediction = model.predict(img)[0]
# 가장 높은 확률의 예측값 5개를 뽑음
top_5_predict = prediction.argsort()[::-1][:5]
labels = [unique_sorted_Y[index] for index in top_5_predict]
color = ['gray'] * 5
if idx in top_5_predict:
color[top_5_predict.tolist().index(idx)] = 'green'
color = color[::-1]
plt.barh(range(5), prediction[top_5_predict][::-1] * 100, color=color)
plt.yticks(range(5), labels[::-1])
가장 높은 확률을 가지는 5개의 예측 결과를 표시했는데.... 개에 대해서 잘 몰라서, 제대로 예측이 되었는지는 잘 모르겠네요.. ㅎㅎㅎ......
일단 10 epoch 학습의 모델을 통해서 결과를 뽑아내고, .csv파일로 저장하여 제출해보도록 하겠습니다.
submission = pd.read_csv('sample_submission.csv')
print(submission.head())
print()
print(submission.info())
제출되는 csv파일은 id와 120개의 종의 확률로 총 121개의 column을 가집니다. sample_submission.csv 파일을 읽어서 이를 토대로 예측 확률을 채워나가도록 하겠습니다.
test_Y = model.predict(test_generator, verbose=1)
test data의 예측결과를 test_Y 변수에 저장하고,
for i in range(len(test_Y)):
for j in range(len(test_Y[i])):
breed_column = unique_sorted_Y[j]
submission.loc[i, breed_column] = test_Y[i, j]
print(submission.iloc[:5, :5])
submission.to_csv('dogbreed_submission_inceptionV3_epoch10.csv', index=False)
각 예측 확률을 submission pandas DataFrame에 저장하고, 마지막으로 저장해줍니다.
그리고 kaggle API를 통해서 제출합니다.
!kaggle competitions submit -c dog-breed-identification -f dogbreed_submission_inceptionV3_epoch10.csv -m "epoch10"
캐글 사이트에서 My Submissions를 확인하면 Score가 0.35672로 나오는 것을 확인할 수 있습니다. 리더보드의 결과를 보면 약 520등 정도의 순위에 위치하는 점수입니다.
10 epoch 더 학습을 하고 결과를 확인해보도록 하겠습니다.
model.fit(train_generator, epochs=10, validation_data=valid_generator)
validation loss는 10 epoch 학습 때보다 조금 더 증가한 것을 확인할 수 있습니다. 학습을 진행하면서 과적합이 진행되는 것으로 추측됩니다.
결과를 저장하고, 제출해보도록 하겠습니다.
test_Y = model.predict(test_generator, verbose=1)
for i in range(len(test_Y)):
for j in range(len(test_Y[i])):
breed_column = unique_sorted_Y[j]
submission.loc[i, breed_column] = test_Y[i, j]
submission.to_csv('dogbreed_submission_inceptionV3_epoch20.csv', index=False)
!kaggle competitions submit -c dog-breed-identification -f dogbreed_submission_inceptionV3_epoch20.csv -m "epoch20"
조금 더 Score가 높게 나왔습니다. Score는 multiclass loss로 더 높을수록 error가 더 많다는 의미입니다. 따라서, 10 epoch의 결과보다 20 epoch의 결과가 약간 더 좋지 않은 것으로 나왔습니다.
(참고로 모든 class 예측 확률을 1/120=0.0083으로 하고 제출하면 loss는 4.78749가 나옵니다. sample_submission.csv파일을 제출하면 확인할 수 있습니다.)
리더보드를 확인하면 정말 낮은 score도 존재하는데 추후에 score를 줄일 수 있는 방법을 시도해봐야겠습니다.
- 참고
시작하세요! 텐서플로 2.0 프로그래밍
'ML & DL > tensorflow' 카테고리의 다른 글
[tensorflow] Custom Loss (Huber Loss, Contrastive Loss 구현) (0) | 2021.01.11 |
---|---|
[tensorflow] Siamese Network (Fashion MNIST 비교 모델 구현) (0) | 2021.01.11 |
[tensorflow] naver 영화 리뷰 감성 분석 (1) | 2020.12.24 |
감성분류 on IMDB datasets (2) (0) | 2020.12.24 |
감성분류 on IMDB datasets (1) (0) | 2020.12.24 |
댓글