02. Dog-Breed-Identification (강아지 품종 분류) - InceptionNet (2)
5. Generator 생성
전 페이지의 파일 구조를 기반으로 데이터를 증강하기 위해 generator를 생성해볼 것이다.
전처리하기 위해 이미지의 픽셀 정보 확인
import cv2
temp_path = "/content/train_sub/affenpinscher/00ca18751837cd6a22813f8e221f7819.jpg"
temp_img = cv2.imread(temp_path)
print(temp_img.shape)
print('이미지 픽셀의 최소값:',temp_img.min()) # 0
print('이미지 픽셀의 최대값:',temp_img.max()) # 255
데이터 생성기 만들어 데이터 보강
# 데이터 생성하는 생성기 만들기 + 전처리
train_datagen = ImageDataGenerator(
validation_split = 0.1,
rescale = 1./255.,
# 데이터 보강
rotation_range = 40,
width_shift_range = 0.2,
height_shift_range = 0.2,
shear_range = 0.2,
fill_mode = 'nearest'
)
valid_datagen = ImageDataGenerator(
validation_split = 0.1,
rescale = 1./255.
)
이미지 사이즈 및 batch_size 지정
이미지 사이즈는 내 맘대로 정하였고, batch_size는 T4로 돌리기 위해 16정도로 하였다.
image_size = 299
batch_size = 16
구체적으로 어디에 사용할지 세팅! (train/valid 따로 처리)
train_generator = train_datagen.flow_from_directory(
# 1) 이미지를 어디서 가져올지 지정
train_data_sub_folder,
# 2) 이 셋의 용도 지정
subset = 'training',
# 3) 입력에 대한 모양 지정
target_size = (image_size, image_size),
# 4) 기타옵션
seed = 42, shuffle = True,
batch_size = batch_size,
class_mode = 'categorical'
)
valid_generator = valid_datagen.flow_from_directory(
# 1) 이미지를 어디서 가져올 것인지 지정
train_data_sub_folder,
# 2) 이 셋의 용도 지정
subset = 'validation',
# 3) 입력에 대한 모양 지정
target_size = (image_size, image_size),
# 4) 기타옵션
seed = 42, shuffle = False,
batch_size = batch_size,
class_mode = 'categorical'
)
6. 모델 구성
InceptionV3 모델에서 ImageNet을 통해 사전 학습된 가중치를 가져올 것이다.
하지만, 이 경우엔 최종적으로 1000개의 분류를 진행하였지만, 나는 120개의 견종을 분류해야하므로 최상위 분류층은 제외하고 가져오도록 하겠다!
추후에 이 부분은 Transer Learning을 통해 새로운 분류층을 만들어서 조정할 것이다.
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from keras.applications.inception_v3 import InceptionV3
from keras.layers import Input
# imagenet으로 학습된 가중치 가져오며, 출력층
base_model = InceptionV3(weights='imagenet', include_top=False)
base_model.summary()
모델의 계층 구조 확인
utils의 plot_model을 통해 모델의 구조를 시각화해서 확인할 수 있으므로 모델의 구조를 확인하고 싶을 때 사용하면 좋다.
from tensorflow.keras.utils import plot_model
plot_model(base_model, show_shapes=True)
모델 사용 방법
방법 1) 그대로 사용하기
for layer in base_model.layers:
layer.trainable = False
방법 2) 구조는 좋다고 판단하여 처음부터 끝까지 내 데이터로 다시 학습하기
for layer in base_model.layers:
layer.trainable = True
방법 3) 앞의 이미지 추출은 그대로 사용하되, 중간에 특징 뽑아내는 레이어는 내 데이터로 학습하기
for layer in base_model.layers[:249]:
layer.trainable = False
for layer in base_model.layers[249:]:
layer.trainable = True
이러한 방법들이 있지만, 일단 방법1로 해보았다.
기존의 InceptionV3는 ImageNet 셋으로 학습하여 최종적으로 1000종류로 분류하였지만, 여기서는 120종으로 분류하여야 하므로 output 쪽을 수정해야겠다.
출력 부분을 120종에 맞게 수정
# 모델 설계 관련 모듈
from keras.models import Model
from keras.layers import Dense, GlobalAveragePooling2D, Dropout, MaxPooling2D
from keras import backend as K
from keras.layers import Activation, Conv2D
from keras.applications.inception_v3 import preprocess_input
from keras import optimizers
num_classes = len(os.listdir(train_data_sub_folder))
# 출력 부분을 120종에 맞춰 수정
x = GlobalAveragePooling2D()(x)
x = Dropout(0.2)(x)
x = Dense(1024, activation='relu')(x)
x = Dropout(0.2)(x)
x = Dense(1024, activation='relu')(x)
x = Dropout(0.2)(x)
x = Dense(512, activation='relu')(x)
pred = Dense(num_classes, activation='softmax')(x)
model = Model(inputs=base_model.input,
outputs=pred)
Model Compile
이미 하위 폴더 구조로 만들었으므로 굳이 원핫인코딩을 해주지 않아도 되므로 loss는 sparse_categorical_crossentropy 대신 categorical_crossentropy 로 사용하였다.
my_adam = optimizers.Adam(learning_rate=0.001, beta_1 =0.9, beta_2=0.999)
model.compile(optimizer=my_adam,
loss='categorical_croessentropy',
metrics=['accuracy'])
CheckPoint 설정
cp_path = 'training/cp--{epoch:04d}.ckpt'
cp_dir = os.path.dirname(cp_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(
cp_path,
verbose = 1,
save_weights_only = True
)
es_callback = tf.keras.callbacks.EarlyStopping(
monitor="val_loss",
patience = 10 # 참을 횟수 (갱신이 되지 않는 횟수)
)
7. test 문제 풀기
test 문제를 풀 generator 생성하여 모델에 전달
test_datagen = ImageDataGenerator(
rescale = 1./255.
)
test_generator = test_datagen.flow_from_directory(
directory = '/content',
classes = ['test'],
class_mode = 'categorical',
shuffle = False,
batch_size = batch_size
)
test_generator.reset()
output = model.predict(test_generator)
8. sample_submission에 결과 저장
sample_submission.csv 파일 불러오기
df_submission_v1 = pd.read_csv("/content/sample_submission.csv")
df_submission_v1.head()
id_list = []
for f_path in test_generator.filenames:
# 1) 서브 폴더 경로 제거
# 2) 뒤에 확장자 제거
f_full_name = os.path.basename(f_path)
f_name = os.path.splitext(f_full_name)[0]
id_list.append(f_name)
sample 양식에 내가 test 푼 결과값 저장
120개에 대한 각각의 확률값으로 저장된다.
df_submission_v1["id"] = id_list
df_submission_v1.iloc[:,1:] = output
df_submission_v1.head()