1. 程式人生 > >【火爐煉AI】深度學習007-Keras微調進一步提升效能

【火爐煉AI】深度學習007-Keras微調進一步提升效能

【火爐煉AI】深度學習007-Keras微調進一步提升效能

(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, Keras 2.1.6, Tensorflow 1.9.0)

本文使用微調(Fine-tune)技術來提升模型的效能,是前面的兩篇文章(編號為005和006)的延續。前面我們通過遷移學習(文章006)將這個貓狗大戰二分類問題的預測準確率提升到了90%左右,但是還能不能進一步提升了?

前面我們沒有對VGG16的卷積層進行引數的優化,那麼我們這裡就可以來優化這部分的引數,當然,優化是很細微的調整,所以稱為Fine-tune。

微調也不是對VGG16的所有結構引數都進行調整,而是僅僅調整後面幾個卷積層的引數,因為有很多學者發現,對不同的資料集,VGG16提取的特徵在底層基本一樣,主要區別在於高層,即卷積層的後面幾層,所以只需要對這幾層進行修改即可。如下我們要調整的層數為:

可以看出,主要調整Conv block 5,前面的4個block不需調整。

我這篇博文主要參考了:keras系列︱影象多分類訓練與利用bottleneck features進行微調(三),這篇博文也是參考的Building powerful image classification models using very little data

,但我發現這兩篇博文有很多地方的程式碼跑不起來,主要原因可能是Keras或Tensorflow升級造成的,所以我做了一些必要的修改。


1. 準備資料集

這一部分和前面文章(編號005)一模一樣,故而省略。


2. 構建模型

在模型的構建上,相當於是取VGG16模型的“身子”,再加上自己定義的模型作為“頭”,將這兩部分加在一起,組成一個完整的模型,“身子”模型仍然採用VGG16在ImageNet上得到的weights,而“頭”模型則採用我們上一篇文章中訓練得到的weights,然後用這個模型來訓練和預測,將“身子”的一部分weights和“頭”的全部weights都進行調整。

這裡有幾個注意點:

1,自己定義的分類器模型作為“頭”,但是“頭”的weights不能是隨機初始值,而應該用已經訓練好的weights,因為隨機初始值會產生較大的梯隊,會破壞前面VGG16卷積層的預訓練後weights。

2,微調僅僅針對VGG16網路的後面幾層卷積層,而不是全部卷積層,是為了防止過擬合,整個模型結構具有絕大的熵容量,因此有很高的過擬合傾向。並且底層的特徵更加具有一般性,不需要調整。

3,在learning rate上,訓練“頭”的weights時可以用較大的lr,使其快速收斂,而在微調階段,需要用較小的lr,使其效能達到最優,並且使用的optimizer也通常使用SGD而不是其他自適應學習率的優化演算法,比如RMSProp,就是為了保證每次改進的幅度比較低,以免破壞VGG16提取的特徵。

# 上面的build_model2中間的my_model.layers[:25]要修改為my_model.layers[:15],原博文此處也是錯的。
# 4,構建模型
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras import applications
from keras import optimizers
from keras.models import Model
def build_model2():
    base_model = applications.VGG16(weights='imagenet', include_top=False,input_shape=(IMG_W, IMG_H,IMG_CH))
    # 此處我們只需要卷積層不需要全連線層,故而inclue_top=False,一定要設定input_shape,否則後面會報錯
    # 這一步使用applications模組自帶的VGG16函式直接載入了模型和引數,作為我們自己模型的“身子”
    
    # 下面定義我們自己的分類器,作為我們自己模型的“頭”
    top_model = Sequential()
    top_model.add(Flatten(input_shape=base_model.output_shape[1:])) # 如果沒有設定input_shape,這個地方報錯,顯示output_shape有很多None
    top_model.add(Dense(256, activation='relu'))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(1, activation='sigmoid')) # 二分類問題
    
    top_model.load_weights('E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/top_FC_model') 
    # 上面定義了模型結構,此處要把訓練好的引數載入進來,
    
    my_model = Model(inputs=base_model.input, outputs=top_model(base_model.output)) # 將“身子”和“頭”組裝到一起
    # my_model就是我們組裝好的完整的模型,也已經載入了各自的weights
    
    # 普通的模型需要對所有層的weights進行訓練調整,但是此處我們只調整VGG16的後面幾個卷積層,所以前面的卷積層要凍結起來
    for layer in my_model.layers[:15]: # 15層之前都是不需訓練的,原博文此處也是錯的。我暈。。。。
        layer.trainable = False
        
    # 模型的配置
    my_model.compile(loss='binary_crossentropy',
                  optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), # 使用一個非常小的lr來微調
                  metrics=['accuracy'])
    return my_model
複製程式碼

這個模型的構建中需要將不訓練的層凍結,如上設定trainable=False即可,下面進行微調訓練:

# 開始用train set來微調模型的引數
print('start to train model2')
my_model2=build_model2()
history_ft2 = my_model2.fit_generator(
        train_generator,
        steps_per_epoch=train_samples_num // batch_size,
        epochs=epochs,
        validation_data=val_generator,
        validation_steps=val_samples_num // batch_size)
複製程式碼

-------------------------------------輸---------出--------------------------------

start to train model2 Epoch 1/20 125/125 [==============================] - 727s 6s/step - loss: 0.3288 - acc: 0.8865 - val_loss: 0.2991 - val_acc: 0.9113 Epoch 2/20 125/125 [==============================] - 732s 6s/step - loss: 0.1822 - acc: 0.9395 - val_loss: 0.2988 - val_acc: 0.9113 Epoch 3/20 125/125 [==============================] - 724s 6s/step - loss: 0.1557 - acc: 0.9445 - val_loss: 0.2742 - val_acc: 0.9125

...

Epoch 18/20 125/125 [==============================] - 703s 6s/step - loss: 0.0260 - acc: 0.9905 - val_loss: 0.3304 - val_acc: 0.9313 Epoch 19/20 125/125 [==============================] - 704s 6s/step - loss: 0.0138 - acc: 0.9955 - val_loss: 0.3267 - val_acc: 0.9413 Epoch 20/20 125/125 [==============================] - 705s 6s/step - loss: 0.0103 - acc: 0.9960 - val_loss: 0.3551 - val_acc: 0.9325

--------------------------------------------完-------------------------------------

在我這個破筆記本上訓練非常耗時,20個epoch花了將近四個小時,唉。所以就不打算繼續訓練下去了。看一下loss和acc的結果:

可以看出test上的acc還可以繼續改進一下,train 和test上的loss也沒有達到平臺期,可以增大epoch繼續訓練。但是也可以看出有些過擬合現象。

########################小**********結###############################

1,Fine-Tune的核心是對原始的骨架網路(此處為VGG16)進行引數的微調,所以需要用非常小的學習率,而且要用SGD優化器。

2,在使用微調之後,準確率從90%左右提升到了約93%,增加epoch數目可以提升的更多。

#################################################################


注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。