2019年1月21日 星期一

深度學習 - Kaggle - Dog & Cat - 微調模型訓練

深度學習 - Kaggle - Dog & Cat - 微調模型訓練

先前使用了預訓練模型VGG16來提取特徵,提取到特徵之後再利用自定義的密集連接層做訓練。其訓練速度很快就能得到不錯的結果。

這邊要練習如何使用微調模型(fine-tuning) 來拓展 conv_base 卷積基,然後在輸入數據上端到端地運行模型,而不是透過 predict 的方法將輸出再調整形狀輸入自定義的全連接層。

直接上實際操作程式碼:
#資料夾分類處理後的路徑
original_dataset_dir ='C:\\Users\\lido_lee\\Downloads\\kaggle_original_data'
base_dir = 'C:\\Users\\lido_lee\\Downloads\\cats_and_dogs_small'
train_dir = 'C:\\Users\\lido_lee\\Downloads\\cats_and_dogs_small\\train'
validation_dir = 'C:\\Users\\lido_lee\\Downloads\\cats_and_dogs_small\\validation
'test_dir = 'C:\\Users\\lido_lee\\Downloads\\cats_and_dogs_small\\test'

from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(150, 150, 3))
#凍結可訓練的權重向量
conv_base.trainable = False

#使用ImageDataGenerator 數據增強
from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1./255,
                                   rotation_range=40,
                                   width_shift_range=0.2,
                                   height_shift_range=0.2,
                                   shear_range=0.2,
                                   zoom_range=0.2,
                                   horizontal_flip=True,
                                   fill_mode='nearest')

#測試數據不可使用數據增
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size=(150, 150),
                                                    batch_size=20,
                                                    class_mode='binary')

validation_generator = test_datagen.flow_from_directory(validation_dir,
                                                        target_size=(150, 150),
                                                        batch_size=20,
                                                        class_mode='binary')

#建立模型網絡
from keras import models
from keras import layers
model = models.Sequential()
#直接將 conv_base 添加進來,就像添加一個層一樣。
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

#選擇優化器RMSprop、損失函數binary_crossentropy、指標acc
from keras import optimizers
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-5),
              metrics=['acc'])

#將訓練&驗證資料丟進fit_generator訓練
history = model.fit_generator(train_generator,
                              steps_per_epoch=100,
                              epochs=30,
                              validation_data=validation_generator,
                              validation_steps=50)

#訓練後的模型評估測試數據
test_generator = test_datagen.flow_from_directory(test_dir,
                                                  target_size=(150, 150),
                                                  batch_size=20,
                                                  class_mode='binary')

test_loss, test_acc = model.evaluate_generator(test_generator, steps=50)
print('test acc:', test_acc)

test acc: 0.8780000007152557

#將訓練好的模型儲存起來
model.save('cats_and_dogs_small_fine_tuning.h5')

#將訓練過程畫出來
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b+', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b+', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()



可以從圖看到 Validation 的精度竟然高於Training ,Loss 也低於Training,這讓我很納悶

我再加入Dropout層再跑一次看看。



加入dropout層後,train & val 的acc差距變更大了,上網google了一下,找到了可以解釋的原因。
因為在訓練的過程,我們使用了大量的數據增強,對訓練資料做旋轉、拉伸、鏡像等...導致模型訓練上更困難,而驗證數據一般是不做數據增強的,故在驗證數據上的表現會比訓練數據上好。



而加入dropout層同理,可以抽象的理解為隨機屏蔽掉一些 feature,只用一部分 feature 來識別,這樣留下來的這些 feature 就更強了。等測試val的時候全部神經元一起上,表現就更好了。

我們在未fine-tuning 的情況下,得到 test acc: 0.878 ,接著我們來微調模型。
首先我們要先將凍結的 conv_base 解凍,然後把不需要用到的層再凍結起來。我們將微調最後三個卷積層,也就是說,直到block4_pool 的所有層都應該被凍結,而block5_conv1、block5_conv2 和block5_conv3 三層應該是可訓練的。

修改:
#凍結可訓練的權重向量
conv_base.trainable = False
改成以下:
#先解凍後,再將 conv4 以前的層都凍結起來。
conv_base.trainable = True
set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        set_trainable = True
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

跌代100次後將圖畫出
history = model.fit_generator(train_generator,
                              steps_per_epoch=100,
                              epochs=100,
                              validation_data=validation_generator,
                              validation_steps=50)


test acc: 0.934
經過100次訓練後...好久。最終我們只用了2000張照片,就可以達到測試精度93.4%了!






沒有留言:

張貼留言