iT邦幫忙

6

Transfer Learning with Keras!

嗨,各位好久不見啦!

最近因為 系上學長要求(誤),開始學習遷移式學習(transfer learning)順便記錄過程。目前也還在學習中,所以有誤或有更專業方法的話,歡迎各位不吝嗇給予反饋與建議!是說本來想說鐵人比賽結束就不用了,不過發現自己的文章有人追蹤訂閱,所以就還是寫寫東西好了。

什麼是遷移學習?

遷移學習就是把已經訓練過的模型參數遷移到新的模型,幫助新模型的訓練,加快新模型的訓練的效率,因為不需要從零開始學習,當然網路上可以找到更詳細的介紹,這裡就不細說明囉。

那直接開始吧!

  • optional 通常使用這種會跑警告的套件我都會先把警告隱藏哈(看到警告心情會不好。)
import warnings
warnings.filterwarnings('ignore')

如標題所示,這裡我主要是用keras而不是tensorflow,keras是已經被包裝過的深度學習套件,適合新手(我)使用!當然除了深度學習套件以外還有基本的Data Science會用到的套件(在我的鐵人文章有介紹過)。

import numpy as np
import keras
from keras import backend as K
from keras.models import Sequential
from keras.layers import Activation
from keras.layers.core import Dense, Flatten
from keras.optimizers import Adam
from keras.metrics import categorical_crossentropy
from keras.preprocessing.image import ImageDataGenerator
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import *
from sklearn.metrics import confusion_matrix
from keras.models import load_model
import itertools
import matplotlib.pyplot as plt
from PIL import Image

目錄tree:

分別有訓練train測試test驗證valid資料集,裡面分別放入的資料夾,裡面再放入貓和狗的圖片檔。

.
├── transfer-learning-with-keras.ipynb
├── test/
│   ├── cats/
│   └── dogs/
├── train/
│   ├── cats/
│   └── dogs/
└── valid/
    ├── cats/
    └── dogs/    

定義目錄路徑:

train_path = './train'
valid_path = './valid'
test_path = './test'

圖片預處理:

ImageDataGenerator顧名思義就是用來產生圖片資料的:
用以生成一個批次的圖像數據。訓練時該函數會無限生成數據,直到達到規定的epoch次數為止。

train_batches = ImageDataGenerator().flow_from_directory(train_path, target_size=(224,224), classes=['dogs', 'cats'], batch_size=10)
valid_batches = ImageDataGenerator().flow_from_directory(valid_path, target_size=(224,224), classes=['dogs', 'cats'], batch_size=4)
test_batches = ImageDataGenerator().flow_from_directory(test_path, target_size=(224,224), classes=['dogs', 'cats'], batch_size=10)
  • flow_from_directory()
    以文件夾路徑為參數,生成經過數據提升/歸一化後的數據,在一個無限循環中無限制生產批次資料。

  • classes
    是可選參數,為子文件夾的列表,如上我們分別為['dogs','cats']的分類,默認為無。若未提供,則該類別列表將從目錄下的子文件夾名稱/結構自動推斷。每一個子文件夾都會被認為是一個新的類!

  • target_size=(224,224)
    整數元組默認為(256,256),圖像將被調整大小成該尺寸,因為我基於VGG16模型,所以這裡設定為(224,224)。

輸出:

Found 40 images belonging to 2 classes.
Found 10 images belonging to 2 classes.
Found 10 images belonging to 2 classes.

可以來看一下大小:

print(train_batches.image_shape)
# (224, 224, 3)

vgg16模型

引入keras內VGG16模型,若為第一次引入使用,則需要稍等待下載:

vgg16_model = keras.applications.vgg16.VGG16()

建立自己的模型:

model = Sequential()
model.summary()

可以看到:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________

接著我們將VGG16的layer加到我們的model內:

for layer in vgg16_model.layers:
    model.add(layer)
model.summary()

可以看到結果:


_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________

將頂層predictions拿掉,基本上遷移式學習不只是刪掉原本輸出,不過因為資料類型的關係這裡只把最後一層刪掉重新訓練。

model.layers.pop()

因為VGG16原本有1000個分類,但這裡我們要改成2種分類:
改變之前layer的trainable參數為False,因為我們現在只需要訓練最後一層。

for layer in model.layers:
    layer.trainable = False

最後加上最後一層:
可以看到這裡我們是改成2,因為我們只要01,分別代表就行了:

model.add(Dense(2, activation='softmax'))
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
dense_5 (Dense)              (None, 2)                 2002      
=================================================================
Total params: 134,262,546
Trainable params: 2,002
Non-trainable params: 134,260,544
_________________________________________________________________

Compile模型:

model.compile(Adam(lr=.00002122), loss='categorical_crossentropy', metrics=['accuracy'])

開始訓練啦!

設定裡面的參數,包含訓練的以及驗證的資料集,epochs設定為10次,因為資料量小,所以steps_per_epochvalidation_steps我不需要設定太大,這邊就依照你的資料量大小改變囉!

model.fit_generator(train_batches, steps_per_epoch=10, validation_data=valid_batches, validation_steps=4, epochs=10, verbose=2)

等待完成:

Epoch 1/10 - 6s - loss: 0.6749 - acc: 0.7756 - val_loss: 0.6456 - val_acc: 1.0000
Epoch 2/10 - 2s - loss: 0.6679 - acc: 0.7856 - val_loss: 0.6674 - val_acc: 0.7143
Epoch 3/10 - 2s - loss: 0.6586 - acc: 0.8672 - val_loss: 0.6757 - val_acc: 0.7143
Epoch 4/10 - 2s - loss: 0.6547 - acc: 0.8980 - val_loss: 0.6460 - val_acc: 1.0000
Epoch 5/10 - 2s - loss: 0.6461 - acc: 0.9898 - val_loss: 0.6455 - val_acc: 1.0000
Epoch 6/10 - 2s - loss: 0.6443 - acc: 1.0000 - val_loss: 0.6441 - val_acc: 1.0000
Epoch 7/10 - 2s - loss: 0.6443 - acc: 1.0000 - val_loss: 0.6475 - val_acc: 1.0000
Epoch 8/10 - 2s - loss: 0.6442 - acc: 1.0000 - val_loss: 0.6512 - val_acc: 0.9286
Epoch 9/10 - 2s - loss: 0.6440 - acc: 1.0000 - val_loss: 0.6583 - val_acc: 0.8571
Epoch 10/10 - 2s - loss: 0.6445 - acc: 1.0000 - val_loss: 0.6511 - val_acc: 0.9286

因為資料量不多,所以非常的快,可以看到acc準確度到100%了,非常開心。並不是每次訓練都會非常的快,所以記得在最後面都要寫上儲存的方法把模型存起來才行啊!

儲存model:

model.save("cat-dog-model-base-VGG16.h5")

好的,那這就是今天的介紹!非常感謝大家的收看!(好久沒有寫文章了。)

更多參考文件:

關於圖片處理:
keras Image preprocessing
關於預訓練模型:
keras Applications


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
rtfgvb74125
iT邦新手 4 級 ‧ 2021-04-11 01:27:29

請問老師為甚麼要加這一行,不加會影響訓練嗎?

for layer in model.layers:
    layer.trainable = False
0
howardpai
iT邦新手 5 級 ‧ 2023-03-23 18:08:32

請問老師我訓練的accuracy忽高忽低一直都上不去https://ithelp.ithome.com.tw/upload/images/20230323/20153961Z0CZjBK0In.jpg
是不適數據集的問題

我要留言

立即登入留言