pre-trained VGG를 사용한 blood cell classification (1) 본문

ML & DL/이것저것..

pre-trained VGG를 사용한 blood cell classification (1)

eremo2002 2019. 1. 4. 15:26

blood cell dataset을 가지고 pre-trained vgg16 네트워크를 사용하여 classification을 해보았다.

Imagenet으로 사전훈련된 vgg16의 F.C layer를 제외한 feature extraction부분의 레이어만 가져왔고 sequential model을 사용하여 새로운 classifier를 만들어 vgg에 연결하였다.

학습이 진행되는 동안 기존 vgg의 weight값들은 변하지 않게 프리징시켰고 F.C layer만 학습되도록 만들었다.




나는 이제 트레이닝을 하기 전, 다음과 같은 부분을 고민해보았다.


이미지넷으로 트레이닝 된 네트워크를 사용해서, 한 번도 본 적 없는 데이터셋도 잘 분류할 수 있을까?


feature extraction layer들이 blood cell이라는 굉장히 관련 없는 도메인에서도 feature를 잘 뽑아낼 수 있을까?


만약 성능이 좋지 않다면, conv layer들의 파라미터등를 전부 다 트레이닝 하는 것이 좋을까? 일부분만 하는 것이 좋을까?


다시 트레이닝을 해야 한다면 처음부터 weight값을 초기화하는 것과 imagenet으로 초기화해서 학습하는 두 가지 방법 중에 무엇이 더 효과적일까?



나는 이미지넷으로 이미 트레이닝 된 네트워크가 다른 도메인을 가지는 문제에선 성능이 좋지 않을 것 같다고 생각하였다.

왜냐하면 네트워크의 파라미터들은 imagenet dataset이라는 도메인의 문제를 풀기 위해 최적화된 값들이기 때문이다. 

하지만 F.C layer의 파라미터들은 새로 업데이트가 되고 feature를 추출한다는 건 크게 다르지 않으니 어찌보면 어느정도(?) 될 수도 있지 않을까? 어찌됐든 일단은 해보자..






ImageDataGenerator를 사용하여 255로 나눠주는 전처리만 적용하였다.

os.path.join을 사용하여 train, validation, test datset의 경로를 불러온다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from keras import models, layers
from keras.applications import VGG16
from keras import Input
from keras.models import Model
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers, initializers, regularizers, metrics
from keras.callbacks import ModelCheckpoint
import os
from glob import glob
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import math
 
 
 
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
 
train_dir = os.path.join('./dataset/1/images/train')
val_dir = os.path.join('./dataset/1/images/val')
test_dir = os.path.join('./dataset/1/images/test')
cs






flow_from_directory를 사용하여 해당 경로의 데이터의 배치, 타겟사이즈 등을 조절한다.


1
2
3
4
train_generator = train_datagen.flow_from_directory(train_dir, batch_size=16, target_size=(220200), color_mode='rgb')
val_generator = val_datagen.flow_from_directory(val_dir, batch_size=16, target_size=(220200), color_mode='rgb')
 
input_tensor = Input(shape=(2202003), dtype='float32', name='input')
cs








pre-trained 네트워크를 불러온다.

weights='imagenet'으로 값을 주면 이미지넷으로 트레이닝 된 모델을 불러올 수 있다.

include_top은 상단의 F.C 레이어를 포함할 것인이 아닌지 결정하는 파라미터이다.

나는 classifier를 새로 만들었기 때문에 include_top을 False로 주었다.

또한 feature extraction 파라미터들은 이미지넷으로 학습된 값들을 그대로 사용할 것이기 때문에 trainable의 속성을 False로 하였다.


1
2
3
pre_trained_vgg = VGG16(weights='imagenet', include_top=False, input_shape=(2202003))
pre_trained_vgg.trainable = False
pre_trained_vgg.summary()
cs







모델 구조를 출력해보면 다음과 같이 F.C layer가 날아간(?) 구조만 남게된다.

그리고 업데이트되지 않게 만들었기 때문에 전체 파라미터는 Non-trainable params가 된다.











이후 Sequential 구조를 사용하여 4096-2048-1024-4 F.C layer를 만들어 vgg뒷단에 연결해준다.


1
2
3
4
5
6
7
8
9
10
additional_model = models.Sequential()
additional_model.add(pre_trained_vgg)
additional_model.add(layers.Flatten())
additional_model.add(layers.Dense(4096, activation='relu'))
additional_model.add(layers.Dense(2048, activation='relu'))
additional_model.add(layers.Dense(1024, activation='relu'))
additional_model.add(layers.Dense(4, activation='softmax'))
 
 
additional_model.summary()
cs









vgg에 연결한 뒤 네트워크 구조를 출력해보면 아래와 같이 잘 연결된 것을 확인할 수 있다.











콜백 checkpoint를 사용하여 모델과 파라미터를 저장한다.

나는 save_best_only를 통해 가장 최선의 결과가 나온 웨이트 값만 over write되어 저장할 수 있게 하였다.

best 값의 판단 기준은 monitor와 mode를 통해 이전보다 loss가 가장 적을 때만 저장하도록 하였다.


또한 steps_per_epoch, validation_steps은 train, validation data의 개수와 batch_size에 따라 설정되도록 수정하였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
checkpoint = ModelCheckpoint(filepath='pretrained_VGG_weight.hdf5'
            monitor='loss'
            mode='min'
            save_best_only=True)
 
additional_model.compile(loss='categorical_crossentropy', optimizer=optimizers.RMSprop(lr=2e-5), metrics=['acc'])
 
 
history = additional_model.fit_generator(train_generator, 
            steps_per_epoch=math.ceil(train_generator.n / train_generator.batch_size), 
            epochs=300
            validation_data=val_generator, 
            validation_steps=math.ceil(val_generator.n / val_generator.batch_size), 
            callbacks=[checkpoint])
cs












전체 소스코드는 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from keras import models, layers
from keras.applications import VGG16
from keras import Input
from keras.models import Model
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers, initializers, regularizers, metrics
from keras.callbacks import ModelCheckpoint
import os
from glob import glob
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import math
 
 
 
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
 
train_dir = os.path.join('./dataset/1/images/train')
val_dir = os.path.join('./dataset/1/images/val')
test_dir = os.path.join('./dataset/1/images/test')
 
 
 
 
train_generator = train_datagen.flow_from_directory(train_dir, batch_size=16, target_size=(220200), color_mode='rgb')
val_generator = val_datagen.flow_from_directory(val_dir, batch_size=16, target_size=(220200), color_mode='rgb')
 
input_tensor = Input(shape=(2202003), dtype='float32', name='input')
 
 
pre_trained_vgg = VGG16(weights='imagenet', include_top=False, input_shape=(2202003))
pre_trained_vgg.trainable = False
pre_trained_vgg.summary()
 
additional_model = models.Sequential()
additional_model.add(pre_trained_vgg)
additional_model.add(layers.Flatten())
additional_model.add(layers.Dense(4096, activation='relu'))
additional_model.add(layers.Dense(2048, activation='relu'))
additional_model.add(layers.Dense(1024, activation='relu'))
additional_model.add(layers.Dense(4, activation='softmax'))
 
 
additional_model.summary()
 
 
checkpoint = ModelCheckpoint(filepath='pretrained_VGG_weight.hdf5'
            monitor='loss'
            mode='min'
            save_best_only=True)
 
additional_model.compile(loss='categorical_crossentropy', optimizer=optimizers.RMSprop(lr=2e-5), metrics=['acc'])
 
 
history = additional_model.fit_generator(train_generator, 
            steps_per_epoch=math.ceil(train_generator.n / train_generator.batch_size), 
            epochs=300
            validation_data=val_generator, 
            validation_steps=math.ceil(val_generator.n / val_generator.batch_size), 
            callbacks=[checkpoint])
 
# number of train & validation samples 
# print(train_generator.n)
# print(val_generator.n)
 
# number of train & val batch_size
# print(train_generator.batch_size)
# print(val_generator.batch_size)
 
 
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
 
epochs = range(1len(acc) + 1)
 
plt.plot(epochs, acc, 'b', label='Training acc')
plt.plot(epochs, val_acc, 'r', label='Validation acc')
plt.title('Accuracy')
plt.legend()
plt.figure()
 
plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Loss')
plt.legend()
 
plt.show()
cs













총 300에폭을 돌린 결과는 다음과 같다.






training performance는 굉장히 빠르게 개선되었다. 4에폭부터 정확도가 90%를 넘기 시작했고

training acc와 loss는 126에폭에서 최고성능을 달성하였다. 정확도가 100%에 도달하여 더 이상 loss가 떨어지지 않았다.


반면 validation 정확도는 60%를 채 넘지 못하였고 127에폭부터 loss와 accuracy가 고정된 값으로 출력되었다. 오버피팅이 매우 심하게 발생하였다. 



왜 이러한 문제가 생겼을까?


우선 train data와 validation data가 매우 유사한 분포를 보이지만 완전히 똑같지는 않다. 따라서 training dataset에 over fitting될 가능성은 여전히 존재한다. 그리고 training acc가 100%를 달성하기 전, 1~125에폭까지 validation loss는 거의 지속적으로 증가하고 있다. 학습 과정이 처음부터 끝까지 training data에 over fitting되고 있음을 보여준다.


사실 내가 보기엔 train data와 validation data가 정확히 어떤 차이가 있는 지 잘 모르겠다. 그만큼 두 data의 분포가 매우 유사하다.

그런데 트레이닝 정확도는 엄청난 속도로 개선되었음에도 불구하고 왜 validation 정확도는 올라가지 않는 것일까?

이미지넷으로 학습된 feature extraction 파라미터들이 blood cell이라는 도메인에서는 성능을 제대로 보여주지 못 하는 걸까?

그렇다면 왜 트레이닝 dataset에서는 잘 작동하는 걸까??




나는 이제 over fitting을 방지할 수 있는 테크닉 적용하거나 vgg의 파라미터들도 업데이트가 가능해지도록 학습을 다시 진행하면서 성능이 어떻게 변화하는지 알아볼 것이다.











Comments