深度學習 - 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%了!
沒有留言:
張貼留言