iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 9
3

ImageNet 競賽的冠軍們

ImageNet 每年舉辦的競賽(ILSVRC)這幾年產生了不少的CNN冠軍,歷屆比賽的模型演進非常精彩,簡單敘述如下:

  1. 2012年冠軍 AlexNet 錯誤率比前一年減少超過10%,且首度引用 Dropout 層。
  2. 2014年亞軍 VGGNet 承襲 AlexNet 思路,建立更多層的模型,達到 16及19 個隱藏層。
  3. 2014年圖像分類冠軍 GoogNet & Inception 同時使用多種不同大小的Kernel,讓系統決定最佳的Kernel。Inception 引入 Batch Normalization 等觀念,參見Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
  4. 2015年冠軍 ResNets 發現 20 層以上的模型前面幾層會發生優化退化(degradation)的狀況,因而提出以『殘差』(Residual)解決問題,參見Deep Residual Learning for Image Recognition

Keras把它們都收錄進框架內,稱為Keras Applications ,包括下列幾項:

  1. Xception
  2. VGG16
  3. VGG19
  4. ResNet50
  5. InceptionV3
  6. InceptionResNetV2
  7. MobileNet

它們的模型結構資訊如下表:
https://ithelp.ithome.com.tw/upload/images/20171205/20001976Dd9xTYEEVi.png
註:

  1. Top-1表示只預測一次且正確的機率。
  2. Top-5表示預測五次只要一次猜對就算正確的機率。
  3. Size:記憶體的最高佔據量。
  4. Parameters:參數的數量,愈多就須計算愈久。
  5. Depth:filters的數目。

除了 Xception and MobileNet,其他的Applications都與 TensorFlow 及 Theano 相容,我們就挑 VGG16 為例子,解釋如何使用。這些模型的隱藏層數都很多,也使用了大量的資料作訓練,一般電腦可能要執行很久,等結果可能要很多輪的咖啡XD,因此,Keras將研發團隊精心調校的模型及執行結果收集進來,一般使用者就不用自
己訓練模型,可以直接套用,故稱為預先訓練的模型(pre-trained models)。

關於 VGG

由於,這些模型使用了大量資料作訓練,且使用非常多層的處理,例如 VGG 使用ImageNet 100萬張圖片,共 1000 種類別,幾乎涵蓋日常生活看到的事物,例如動物、交通工具...等,訓練出來的模型,就變成一種『通用解決方案』(Generic Solution),如果要辨識照片內事物屬於這1000類,例如貓、狗、大象等,就可以直接拿VGG模型來用了,反之,如果,要辨識的內容不屬於1000類,也可以換掉input卷積層,只利用中間層萃取的特徵,這種能力稱為『Transfer Learning』,比較嚴謹的定義如下:

Transfer learning allows you to transfer knowledge from one model to another. For example, you could transfer image recognition knowledge from a cat recognition app to a radiology diagnosis. Implementing transfer learning involves retraining the last few layers of the network used for a similar application domain with much more data. The idea is that hidden units earlier in the network have a much broader application which is usually not specific to the exact task that you are using the network for. In summary, transfer learning works when both tasks have the same input features and when the task you are trying to learn from has much more data than the task you are trying to train.
資料來源:Deep Learning Specialization by Andrew Ng — 21 Lessons Learned

VGG16/VGG19 模型結構

VGG 是英國牛津大學 Visual Geometry Group 的縮寫,主要貢獻是使用更多的隱藏層,大量的圖片訓練,提高準確率至90%。VGG16/VGG19分別為16層(13個卷積層及3個全連接層)與19層(16個卷積層及3個全連接層),結構圖如下。
https://ithelp.ithome.com.tw/upload/images/20171206/200019764r3qCPSJxX.png
圖. VGG16 結構圖,圖片來源:Building powerful image classification models using very little data
https://ithelp.ithome.com.tw/upload/images/20171206/20001976yeCo1PvEOs.jpg
圖. VGG19 結構圖,圖片來源:Applied Deep Learning 11/03 Convolutional Neural Networks

VGG16 使用方法

乍看到 Keras 的官方文件 ,就傻眼了,竟然只有寥寥一頁,除了參數說明,沒了!! 花了一整天的搜尋,總算搞懂怎麼用,迫不及待的想跟大家分享。

所有Applications執行都只要一行指令,就可以把模型及權重載入程式中,例如,載入VGG16的指令如下:
model = VGG16(weights='imagenet', include_top=True)
相關參數說明:

  1. include_top:是否包含頂部(Top) 3層『完全連階層』(fully-connected layers)。
    • include_top = False:只利用VGG16萃取特徵,後面的分類處理,都要自己設計。反之,就是全盤接受VGG16,只是要改變輸入而已。
    • 注意!! 頂部(Top)指的是位於結構圖最後面,因為它是一個『後進先出法』的概念,一層一層堆疊上去,最上面一層就是頂部(Top)。
  2. weights:使用的權重,分兩種
    • imagenet:即使用ImageNet的預先訓練的資料,約100萬張圖片,判斷1000類別的日常事物,例如動物、交通工具...等,我們通常選這一項。
    • None:隨機起始值,我沒試過,請有興趣的讀者自行測試。

如果你不用Applications,要自己寫也很簡單,程式碼如下:

from keras.models
import Sequential
from keras.layers import Dense, Activation, Dropout, Flatten
from keras.layers import Conv2D
from keras.layers import MaxPooling2D

input_shape = (224, 224, 3)

model = Sequential([
    Conv2D(64, (3, 3), input_shape=input_shape, padding='same',
           activation='relu'),
    Conv2D(64, (3, 3), activation='relu', padding='same'),
    MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    Conv2D(128, (3, 3), activation='relu', padding='same',),
    MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
    Conv2D(256, (3, 3), activation='relu', padding='same',),
    Conv2D(256, (3, 3), activation='relu', padding='same',),
    Conv2D(256, (3, 3), activation='relu', padding='same',),
    MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    Conv2D(512, (3, 3), activation='relu', padding='same',),
    MaxPooling2D(pool_size=(2, 2), strides=(2, 2)),
    Flatten(),
    Dense(4096, activation='relu'),
    Dense(4096, activation='relu'),
    Dense(1000, activation='softmax')
])

model.summary()

VGG16 實作

我們就來看一支範例程式,我們使用include_top=True,全盤採納VGG16模型,僅改變輸入為一張圖片(tiger.jpg),程式碼如下,我在程式中加了註解,請參考這裡 ,本範例為vgg16.py:

from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

# include_top=True,表示會載入完整的 VGG16 模型,包括加在最後3層的卷積層
# include_top=False,表示會載入 VGG16 的模型,不包括加在最後3層的卷積層,通常是取得 Features
# 若下載失敗,請先刪除 c:\<使用者>\.keras\models\vgg16_weights_tf_dim_ordering_tf_kernels.h5
model = VGG16(weights='imagenet', include_top=True) 

# Input:要辨識的影像
img_path = 'tiger.jpg'
#img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

# 預測,取得features,維度為 (1,7,7,512)
features = model.predict(x)
# 取得前三個最可能的類別及機率
print('Predicted:', decode_predictions(features, top=3)[0])

程式說明

幾點心得整理如下:

  1. model = VGG16(...) 會將預先訓練的模型存放在 c:<使用者>.keras\models\資料夾。
  2. 模型及權重參數會存放在 vgg16_weights_tf_dim_ordering_tf_kernels.h5 檔案內,不用重新訓練。
  3. 模型辨識的類別存放在 imagenet_class_index.json,每一類(Class)含三個欄位:序號、類別代號及類別名稱。
  4. 注意 !! Keras不會判斷模型檔是否與目前指令要下載得是否同一型態,只要檔案存在,就不會重新下載,若之前下載的檔案有問題,因為Keras不會重新下載,會造成後續程式錯誤,所以,如有錯誤,可手動刪除檔案。
  5. 若是使用model = VGG16(weights='imagenet', include_top=False),就要加頂部三層。
  6. 要顯示模型結構,可執行 print(model.summary())。
  7. 要儲存模型結構圖,可執行下列程式碼,但要先安裝Graphviz及pydot、graphviz套件:
    • 下載Graphviz ,安裝後將bin目錄放到環境變數的Path中。
    • pip install pydot
    • pip install graphviz
from keras.utils import plot_model
plot_model(model, to_file='model.png')
  1. 要改變ouput的分類法,可以執行下列程式碼:
# 從頂部移出一層
model.layers.pop()
model.outputs = [model.layers[-1].output]
model.layers[-1].outbound_nodes = []
# 加一層,只辨識10類
from keras.layers import Dense
num_classes=10
x=Dense(num_classes, activation='softmax')(model.output)
# 重新建立模型結構
model=Model(model.input,x)

實驗

我又做了幾個有趣的實驗:

  1. 把照片換成『熊讚』,執行結果變成是『泰迪熊』(teddy),哈哈,ImageNet應該不會有『熊讚』的照片,不過,辨識出來還是一隻熊,也不錯啦;第二大機率值為『手套』(mitten),不知道是否把熊讚的『手』辨識成『手套』,Who knows?
  2. 把照片換成兩種動物『熊讚與狗』,辨識結果前三名都是『貴賓狗』(poodle),『哈士奇』變成『貴賓狗』,模型內可能沒有納入很多狗的品種,另外,它只會優先辨識出一種事物,『熊讚』被晾在一邊了,但是,模型的優先順序是甚麼呢,Who knows?
  3. 把照片換成『台灣黑熊』,執行結果是『美國黑熊』(American_black_bear),不是『泰迪熊』(teddy);不錯喔,VGG16還能辨別真熊與吉祥物的差別。
  4. 最後把照片換成『太陽花』,被誤認成『香蕉』的機率趨近於0(5.92174e-05)。
  5. Neural Network 技術讓我們不用對照片去背,就可以辨識主體事物,傑克,真是太神奇了。

https://ithelp.ithome.com.tw/upload/images/20171206/20001976iZDCd0ez35.jpg
圖. 『熊讚』,圖片來源:熊讚爆紅升格市府吉祥物 前進雙橡園
https://ithelp.ithome.com.tw/upload/images/20171206/20001976oRZNQugNBD.jpg
圖. 『熊讚與狗』,圖片來源:陽明山那場雪救了「熊讚」 狂刷存在感屢創熊式奇蹟

結語

我們如何選擇要用哪一個預先訓練的模型? 請參考下圖,它列出各種模型運算次數與準確度的比較,圓圈大小是參數的多寡,我們可以根據問題的型態與運算資源的多寡,來決定使用哪一個預先訓練的模型。另外,我喜歡它的標題『Standing on the shoulders of giants』,使用預先訓練的模型就是站在巨人的肩膀上,沒有龐大資源,也可以有變通之道。https://ithelp.ithome.com.tw/upload/images/20171205/20001976Zei7WS0UjO.png
圖. 各種預先訓練模型的比較,圖片來源:Learning Deep Learning with Keras

VGG 19 與 VGG16 類似,只是它更多層,在後續篇章的『風格轉換』(Style Transfer)中會使用到。下一篇,我們繼續使用 VGG16 套用在照片相似度的比對上,說明如何找出相同主題的照片,也讓我們更熟悉如何活用這些Applications。


上一篇
Day 08:CNN 模型設計
下一篇
Day 10:CNN 應用 -- 找出相似的照片
系列文
以100張圖理解 Neural Network -- 觀念與實踐31

2 則留言

0
KYO
iT邦新手 5 級 ‧ 2018-02-22 17:34:33

您好,我是初學機器學習,請問一下:
model = VGG16(weights='imagenet', include_top=False),就要加頂部三層。(一定要三層嗎?)

那您在 9.要改變ouput的分類法中
為什麼得先pop掉一層,才再加入新的一層(分成10類) (一定要先pop嗎?)
那這樣您在頂部並沒有加回三層?

謝謝您。

  1. 不一定要置換三層,你可以依需要處理,但因為 VGG 頂部三層為『完全連接層』(Dense),通常要調整參數或Activation Function,會全部改掉。
  2. VGG 是辨識 1000 類物體,如果只要辨識 10 類,最上一層應如下:
    Dense(10, activation='softmax')
0
chadchang
iT邦新手 5 級 ‧ 2019-01-25 10:03:50

1.想請問下面這兩行是甚麼意思,因為我有看到原本程式碼已經有pop拿掉最後一層了
model.outputs = [model.layers[-1].output]
model.layers[-1].outbound_nodes = []
2.想問下面這行是在多+一層然後把這層跟原本的mdoel.output再連接再一起嗎?
x=Dense(num_classes, activation='softmax')(model.output)

我要留言

立即登入留言