이번 포스팅에서는 캔, 페트병, 종이컵의 custom data를 이용한

전이 학습 transfer learning을 진행해 보려고 합니다

 

 

 

전이학습의 포인트는 기존에 학습되어 있는 모델을 가지고 자신의 모델을 학습 키시는 것인데

여기서 원래 학습되어있는 부분을 무너트리지 않는 선에서 미세조정을 해야한다는 것입니다

 

 

저의 경우 데이터는 적지만 모델이 어느정도 유사성이 있다고 판단하였고

특징을 추출하는 convolution base 부분은 전부 동결시키고

이미지 분류를 실시하는 Classifier 부분만 학습을 진행할 예정입니다

 

 

진행을 위해 224*224의 gray scale 이미지 1860장을 수집하였고

1650 장을 training + validation data

210 장을 test data로 이용하였습니다

 

더보기

모든 이미지를 물체가 중앙에 오도록 전처리하였습니다

전처리를 진행할 때 생각해 두셔야 하는 것이

 

이 모델을 실제로 써야 할 때 역시 같은 방법으로 전처리를 해야 한다는 것입니다

 

전처리 과정은 이 쪽

https://blueberry-kyu.tistory.com/7

 

pre-trained model로는 DenseNet201

가중치는 imagenet을 이용하였습니다

 

 

사용한 버전

  • tensorflow = 2.2.0

 

import tensorflow as tf
size = (224, 224, 3)

base_model = tf.keras.applications.DenseNet201(
    include_top=False,
    pooling='None',
    weights='imagenet',
    input_shape=size,
)
# base_model.summary()

pre-trained model 가져오기

summary() 이용해서 모델 구성 확인 가능 (너무 길다)

 

import tensorflow as tf
from tensorflow.keras import models, layers

model = models.Sequential()
model.add(base_model)
model.add(layers.Flatten(name='Flatten'))
model.add(layers.Dense(256, activation='relu', name='Dense_1'))
model.add(layers.Dropout(0.5, name='Dropout'))
model.add(layers.Dense(3, activation='softmax', name='Dense_2'))

model.summary()

model.summary()

print(len(model.trainable_weights))
base_model.trainable = False
print(len(model.trainable_weights))

606

4

pre-trained model 부분만 동결시키기

직접 추가한 Fully Conected layer 만 남은 것을 알 수 있습니다

 

from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os
import numpy as np

Path = "경로설정"

imageGenerator = ImageDataGenerator(
    rescale=1./255,
    validation_split=0.2,
    rotation_range=30,
    zoom_range=[1.0,1.4],
    shear_range=0.1,
    brightness_range=[0.9,1.2],
    horizontal_flip=True,
    vertical_flip=True,
    fill_mode='nearest',
)

trainGen = imageGenerator.flow_from_directory(
    os.path.join(Path, 'training_set'),
    target_size=size[0:1],
    shuffle=False,
    subset='training'
)

validationGen = imageGenerator.flow_from_directory(
    os.path.join(Path, 'training_set'),
    target_size=size[0:1],
    shuffle=False,
    subset='validation'
)
x_train=np.concatenate([trainGen.next()[0] for i in range(trainGen.__len__())])
y_train=np.concatenate([trainGen.next()[1] for i in range(trainGen.__len__())])
x_val=np.concatenate([validationGen.next()[0] for i in range(validationGen.__len__())])
y_val=np.concatenate([validationGen.next()[1] for i in range(validationGen.__len__())])
import matplotlib.pyplot as plt

num_sample = 3

random_idxs = np.random.randint(trainGen.samples, size=num_sample)

def Trash(label):
    if label == 0: trash = 'can'
    elif label == 1: trash = 'paper'
    else: trash = 'plastic'
    return trash
    
plt.figure(figsize=(num_sample*3, num_sample*2))
for i, idx in enumerate(random_idxs):
    img = x_train[idx, :]
    label = y_train[idx]
    
    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(img)
    label_num = np.argmax(label)
    trash = Trash(label_num)
    
    plt.title("Index: {}, Label: {}".format(idx, trash))

training data 에서 랜덤 하게 3가지를 확인할 수 있습니다

셀을 반복 실행하면 다른 값도 볼 수 있어요

 

from tensorflow.keras import optimizers
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
              metrics=['acc'])
              
early_stopping = EarlyStopping(patience=3, monitor='val_loss',
                                 restore_best_weights=True)

optimizer는 안정적인 학습을 위해 SGD를 사용하였고

이미 학습이 되어있는 부분을 망치지 않기 위해 작은 learning rate를 사용하였습니다

 

또한 과적합이 되는 것을 방지하기 위해 EarlyStopping을 이용하여 val_loss가 3번 이상 나아지지 않으면 학습을 중단시키도록 하였습니다

 

베스트 모델을 저장시키는 ModelCheckpoint를 사용하셔도 좋습니다

 

epochs = 20

history = model.fit(x_train, y_train,epochs=epochs,
                   validation_data=(x_val, y_val),
                    callbacks=[early_stopping]
                    )

...

13 epoch 째 이후 3번 이상 더 나아지지 않아 학습이 중단되었습니다

 

history.history.keys()

dict_keys(['loss', 'acc', 'val_loss', 'val_acc'])

 

import matplotlib.pyplot as plt

history_dict = history.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss)+1)
fig = plt.figure(figsize=(12, 6))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color='blue', label='train_loss')
ax1.plot(epochs, val_loss, color='red', label='val_loss')
ax1.set_title('Train ans Validation loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('loss')
ax1.grid()
ax1.legend()

accuracy = history_dict['acc']
val_accuracy = history_dict['val_acc']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, accuracy, color='blue', label='train_accuracy')
ax2.plot(epochs, val_accuracy, color='red', label='val_accuracy')
ax2.set_title('Train ans Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.grid()
ax2.legend()

plt.show()

확실히 transfer learning을 이용하니 초반부터 높은 정확도와 낮은 손실이 나옴을 볼 수 있습니다

 

testGenerator = ImageDataGenerator(
    rescale=1./255
)

testGen = testGenerator.flow_from_directory(
    os.path.join(Path, 'test_set'),
    target_size=size[0:1],
    shuffle=False,
)
x_test=np.concatenate([testGen.next()[0] for i in range(testGen.__len__())])
y_test=np.concatenate([testGen.next()[1] for i in range(testGen.__len__())])

test data 가져오기

 

model.evaluate(x_test, y_test)

7/7 [==============================] - 19s 3s/step - loss: 0.0778 - acc: 0.9619

[0.07779338210821152, 0.961904764175415]

 

96%의 정확도가 나왔습니다 와!

 

num_sample = 3

random_idxs = np.random.randint(teatGen.samples, size=num_sample)

plt.figure(figsize=(num_sample*3, num_sample*2))
for i, idx in enumerate(random_idxs):
    img = x_test[idx, :]
    label = y_test[idx]

    pred_ysi = model.predict(x_test[random_idxs])
    arg_pred_yi = np.argmax(pred_ysi, axis=1)
    lab_trash = Trash(np.argmax(label))
    pred_trash = Trash(arg_pred_yi[i])
    
    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(img)
    plt.title("Label: {}, pred: {}".format(lab_trash, pred_trash))

test data 에서 랜덤 하게 3가지를 예측하고 정답과 함께 출력합니다

셀을 반복 실행하면 다른 값을 볼 수 있습니다

우측과 같이 틀린 예측도 있음을 볼 수 있습니다

 

y_true = np.argmax(y_test, axis=-1)

pred_ys = model.predict(x_test)
y_pred = np.argmax(pred_ys, axis=-1)

def Labels(num_list):
    label_list = []
    for num in num_list:
        if num==0:
            label_list.append('can')
        elif num==1:
            label_list.append('paper')
        else:
            label_list.append('plastic')
    return label_list

y_true = Labels(y_true)
y_pred = Labels(y_pred)
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns

plt.figure(figsize=(9, 9))
cm = confusion_matrix(y_true, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('predicted Label')
plt.ylabel('True Label')
plt.show()

0 == can

1 == paper

2 == plastic

confusion matrix는 다음과 같이 그려졌습니다

클래스당 70개의 test data 이니

 

캔은 모두 정확히 판단 하였는데

페트병을 캔으로 인식한 error가 많네요

 

model.save('recycling_model.h5')

모델 저장하기~

+ Recent posts