如何在少量資料的情況下改善 CNN 模型訓練的效果?
前面的教學有提過神經網路會需要大量的資料來進行訓練,尤其是 CCN 應用在影像訓練的領域,時常遇到標記的訓練圖片數量不足。為了在這樣的情況也能獲得不錯的訓練效果,我們接下來會陸續介紹三種技巧,可以用來提高 CNN 網路訓練的效率與成效。
接下來要介紹的三種方法分別為:
- 資料擴增法 (Data augmentation)
- 預先訓練神經網路的特徵萃取法 (Feature extraction with a pertrained network)
- 微調預先訓練神經網路法 (Fine-tuning a pretrained network)
從 Kaggle 下載與準備訓練資料
Kaggle 下載的檔案全部都是圖片,由於我們想要從頭訓練一個可以分辨貓或狗的 CNN 網路,開始以前需要先將影像資料進行處理,轉換為可以送進 Keras 網路進行訓練的「張量」。並且依據我們選用的損失函數方法,處理對應標準答案的格式。
由於我們今天不會用到全部的資料,我們只會在兩種類別取用 1000 張進行訓練,500 張進行測試。接著利用「資料擴增工法」來提高正確率。詳細的資料前處理可以參考 GitHub 程式碼 keras-cat-vs-dog-datagen.ipynb,裡面也有比較完整的說明。
透過「資料擴增法」增加可以進行訓練的樣本數量
如果我們的訓練影像 Dataset 不足,可以透過資料擴增法增加樣本數量。實現方法為將原本的影像進行隨機縮放、旋轉、平移、翻轉等影像處理技巧,來創造更多的訓練樣本。假設樣本真的很難取得,那麼我們也可以透過資料擴增法,將影像資料做一點「變化」,稍微加工一下這樣就有更多資料可以讓模型進行學習。Keras ImageDataGenerator 可以幫助我們實現影像資料擴增,這樣的工作有點像是針對取樣的目標多拍幾張照片的意思。
以下的對照組使用 2,000 張貓狗照片樣本進行訓練,差不多在 5 Epoch 時就達到最小值,正確率約 0.7,少量資料造成 Over Fitting,效果不佳~數據如下:
再來透過影像資料產生更多樣本,透過以下的 Python 把訓練資料進行一些影像處理:
ImageDataGenerator( 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') # 邊界像素填補,由於影像調整後周圍會出現缺少的像素,設定 nearest 會以最接近的像素填補
以下四張照片是透過 ImageDataGenerator 產生的樣本,可以看到同一張照片經過影像處理產生了更多的學習樣本。
當我將貓與狗的訓練樣本由 2,000 擴增為 3,200,並且重新建立模型(詳細的程式碼可以參考 GitHub),這裡我們有動一點手腳,就是在卷積層傳入全連接層 (Full Connection Layer) 的時候加入了 Dropout Layer,這樣會隨機丟棄 50% 的資訊,用意是不要讓網路的學習過於狹隘,不然很容易造成 Over Fitting。完成的網路模型如下:
from keras import layers from keras import models model = models.Sequential() model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3))) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(128, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(128, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Flatten()) model.add(layers.Dropout(0.5)) # 加入 Dropout 0.5 隨機丟棄 50% model.add(layers.Dense(512, activation='relu')) model.add(layers.Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4), metrics=['acc']) model.summary()
透過這樣的修改,可以將正確率提昇至 0.85。結果如下:
完整的程式碼可以參考 CoLab Keras 利用資料擴增法訓練貓狗分類器 與 GitHub ipynb 範例檔案。
這一篇文章介紹透過資料擴增法製造更多的樣本數,解此提昇模型訓練的準確性。接下來我們會介紹另一個常用的方法,可以透過已經訓練好的模型進行二次訓練,也是目前常用的訓練方式,敬請期待...