使用 Mobilenet 和 Keras 來做遷移學習

本文爲 AI 研習社編譯的技術博客,原標題 : Transfer Learning using Mobilenet and Keras 作者 | Ferhat Culfaz 翻譯 | 胡瑛皓 校對 | 醬番梨 整理 | 菠蘿妹 原文鏈接: https://towardsdatascience.com/transfer-learning-using-mobilenet-and-keras-c75daf7ff299 注:本文的相關鏈接請點擊文末【閱讀原文】進行訪問

使用Mobilenet和Keras來做遷移學習

本文以notebook的實例的形式講解。首先用Mobilenet分類狗的圖片,然後演示一張不能正確分類的藍雀圖片,然後用遷移學習和Mobilenet重新訓練,使這張圖片得到正確分類。例子中舉的是二分類,當然也可以按需要進行增加更多類型,看具體硬件和計算時間的限制。

Mobilenet論文地址:https://arxiv.org/pdf/1704.04861.pdf

MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications, Howard et al, 2017.

Mobilenet採用輕量級架構,會用它進行訓練。它使用深度可分離卷積操作,意思是說其採用的是單通道卷積操作,而不是混合三種顏色然後進行扁平化操作。其具有過濾輸入通道的效果。或就像論文作者解釋的: “ MobileNets運用深度卷積操作,對每個輸入通道應用單個過濾操作。然後逐點用1×1卷積合併深度卷積的結果。用標準卷積將輸入進行過濾合併到一組新的輸出。深度可分離卷積再將其分開成2層,一層用於過濾,另一層用於合併。上述分解顯著減少了計算量和模型大小。”

逐點卷積和深度卷積的區別

Mobilenet的整體架構是這樣的,其中包含30層:

  1. 步長2的卷積層
  2. 深度卷積
  3. 逐點卷積層使通道數翻倍
  4. 步長爲2的深度卷積層
  5. 逐點卷積層使通道數翻倍等

等等

Mobilenet 完整架構

其維護成本很低,因而性能速度都很好。目前也有一些受青睞的預訓練模型,模型的大小可在適應內存或磁盤上,與所用到的參數成正比。模型的速度和能耗與MACs(Multiply-Accumulates)數量成正比,該指標用以衡量乘法操作與累加操作的數量。

好,現在我們開始編碼!

本文代碼地址如下: https://github.com/ferhat00/Deep-Learning/tree/master/Transfer%20Learning%20CNN

首先,加載一些必要的包和庫。

import keras
from keras import backend as K
from keras.layers.core import Dense, Activation
from keras.optimizers import Adam
from keras.metrics import categorical_crossentropy
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
from keras.models import Model
from keras.applications import imagenet_utils
from keras.layers import Dense,GlobalAveragePooling2D
from keras.applications import MobileNet
from keras.applications.mobilenet import preprocess_input
import numpy as np
from IPython.display import Image
from keras.optimizers import Adam

我們從Keras導入預訓練模型。

mobile = keras.applications.mobilenet.MobileNet()
def prepare_image(file):
    img_path = ''
    img = image.load_img(img_path + file, target_size=(224, 224))
    img_array = image.img_to_array(img)
    img_array_expanded_dims = np.expand_dims(img_array, axis=0)
    return keras.applications.mobilenet.preprocess_input(img_array_expanded_dims)

我們試試看分類不同品種的狗狗。

Image(filename='German_Shepherd.jpg')
preprocessed_image = prepare_image('German_Shepherd.jpg')
predictions = mobile.predict(preprocessed_image)
results = imagenet_utils.decode_predictions(predictions)
results

輸出:

[[('n02106662', 'German_shepherd', 0.9796372),
  ('n02105162', 'malinois', 0.020184083),
  ('n02091467', 'Norwegian_elkhound', 0.00015799515),
  ('n02116738', 'African_hunting_dog', 5.2901587e-06),
  ('n02105251', 'briard', 3.9127376e-06)]]
Image(filename='labrador1.jpg')
preprocessed_image = prepare_image('labrador1.jpg')
predictions = mobile.predict(preprocessed_image)
results = imagenet_utils.decode_predictions(predictions)
results

輸出:

[[(‘n02099712’, ‘Labrador_retriever’, 0.73073703),
 (‘n02087394’, ‘Rhodesian_ridgeback’, 0.03984367),
 (‘n02092339’, ‘Weimaraner’, 0.03359009),
 (‘n02109047’, ‘Great_Dane’, 0.028944707),
 (‘n02110341’, ‘dalmatian’, 0.022403581)]]
Image(filename='poodle1.jpg')
preprocessed_image = prepare_image('poodle1.jpg')
predictions = mobile.predict(preprocessed_image)
results = imagenet_utils.decode_predictions(predictions)
results

輸出:

[[('n02113799', 'standard_poodle', 0.5650911),
  ('n02113712', 'miniature_poodle', 0.37279922),
  ('n02102973', 'Irish_water_spaniel', 0.053150617),
  ('n02113624', 'toy_poodle', 0.0072146286),
  ('n02093859', 'Kerry_blue_terrier', 0.0013652634)]]

目前爲止一切都很好。分類器區分出不同品種的狗。那麼讓我們拿一種鳥類圖片來試試,這裏用藍雀圖片。

Image(filename='blue_tit.jpg')

藍雀

preprocessed_image = prepare_image('blue_tit.jpg')
predictions = mobile.predict(preprocessed_image)
results = imagenet_utils.decode_predictions(predictions)
results

輸出:

[[('n01592084', 'chickadee', 0.95554715),
  ('n01530575', 'brambling', 0.012973112),
  ('n01828970', 'bee_eater', 0.012916375),
  ('n01532829', 'house_finch', 0.010978725),
  ('n01580077', 'jay', 0.0020677084)]]

可以看到,分類器不能識別藍雀。圖片被錯分爲山雀(chickadee)。這是一種北美本土的鳥類,有一些微妙的不同:

山雀

我們調一下 Mobilenet 的架構,然後重新訓練頂部幾層,進行遷移學習。 要達成這個,拿一些圖片來訓練這個模型。這裏會讓模型學習藍雀和烏鴉。這裏就不用手工下載訓練用到的圖片了,用谷歌圖像搜索,然後下載這些圖片,google_images_download包很好用,加一下引用就行了。

地址: https://github.com/hardikvasa/google-images-download

!pip install google_images_download
from google_images_download import google_images_download
response = google_images_download.googleimagesdownload()
arguments = {"keywords":"blue tit","limit":100,"print_urls":False,"format":"jpg", "size":">400*300"}
paths = response.download(arguments)
arguments = {"keywords":"crow","limit":100,"print_urls":False, "format":"jpg", "size":">400*300"}
paths = response.download(arguments)

現在我們重用MobileNet,會下載一個輕量級存檔文件(17Mb), 凍結其基礎層,在模型頂部增加幾層,然後進行訓練。注意本文只訓練一個二分類器,區分藍雀和烏鴉。

base_model=MobileNet(weights='imagenet',include_top=False) #imports the mobilenet model and discards the last 1000 neuron layer.

x=base_model.output
x=GlobalAveragePooling2D()(x)
x=Dense(1024,activation='relu')(x) #we add dense layers so that the model can learn more complex functions and classify for better results.
x=Dense(1024,activation='relu')(x) #dense layer 2
x=Dense(512,activation='relu')(x) #dense layer 3
preds=Dense(2,activation='softmax')(x) #final layer with softmax activation

我們來看一下模型的架構

for i,layer in enumerate(model.layers):
  print(i,layer.name)

這裏採用Imagenet數據集預訓練的權重。保證所有權重是不可被訓練(凍結)的。只訓練最後幾個dense層。

for layer in model.layers:
    layer.trainable=False
# or if we want to set the first 20 layers of the network to be non-trainable
for layer in model.layers[:20]:
    layer.trainable=False
for layer in model.layers[20:]:
    layer.trainable=True

把訓練數據載入ImageDataGenerator。指定一下路徑,它會自動將數據以批次形式供給訓練,簡化了編碼過程。

train_datagen=ImageDataGenerator(preprocessing_function=preprocess_input) #included in our dependencies

train_generator=train_datagen.flow_from_directory('C:/Users/Ferhat/Python Code/Workshop/Tensoorflow transfer learning/downloads',
                                                 target_size=(224,224),
                                                 color_mode='rgb',
                                                 batch_size=32,
                                                 class_mode='categorical',
                                                 shuffle=True)

編譯模型。現在開始訓練。在GTX1070 GPU環境下,訓練不到2分鐘。

model.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])
# Adam optimizer
# loss function will be categorical cross entropy
# evaluation metric will be accuracy

step_size_train=train_generator.n//train_generator.batch_size
model.fit_generator(generator=train_generator,
                   steps_per_epoch=step_size_train,
                   epochs=10)
Epoch 1/10
5/5 [==============================] - 5s 952ms/step - loss: 0.9098 - acc: 0.6562
Epoch 2/10
5/5 [==============================] - 3s 563ms/step - loss: 0.0503 - acc: 0.9686
Epoch 3/10
5/5 [==============================] - 3s 687ms/step - loss: 0.0236 - acc: 0.9930
Epoch 4/10
5/5 [==============================] - 4s 716ms/step - loss: 7.5358e-04 - acc: 1.0000
Epoch 5/10
5/5 [==============================] - 3s 522ms/step - loss: 0.0021 - acc: 1.0000
Epoch 6/10
5/5 [==============================] - 4s 780ms/step - loss: 0.0353 - acc: 0.9937
Epoch 7/10
5/5 [==============================] - 3s 654ms/step - loss: 0.0905 - acc: 0.9938
Epoch 8/10
5/5 [==============================] - 4s 890ms/step - loss: 0.0047 - acc: 1.0000
Epoch 9/10
5/5 [==============================] - 3s 649ms/step - loss: 0.0377 - acc: 0.9867
Epoch 10/10
5/5 [==============================] - 5s 929ms/step - loss: 0.0125 - acc: 1.0000

模型訓練好了。我們來測試一些獨立輸入的圖片,檢查一下預測情況。

def load_image(img_path, show=False):

    img = image.load_img(img_path, target_size=(150, 150))
    img_tensor = image.img_to_array(img)                    # (height, width, channels)
    img_tensor = np.expand_dims(img_tensor, axis=0)         # (1, height, width, channels), add a dimension because the model expects this shape: (batch_size, height, width, channels)
    img_tensor /= 255.                                      # imshow expects values in the range [0, 1]

    if show:
        plt.imshow(img_tensor[0])                           
        plt.axis('off')
        plt.show()

    return img_tensor
  
#img_path = 'C:/Users/Ferhat/Python Code/Workshop/Tensoorflow transfer learning/blue_tit.jpg'
img_path = 'C:/Users/Ferhat/Python Code/Workshop/Tensoorflow transfer learning/crow.jpg'
new_image = load_image(img_path)

pred = model.predict(new_image)

pred

輸出:

array([[4.5191143e-15, 1.0000000e+00]], dtype=float32)

結果顯示,分類器準確的預測了烏鴉,此處藍雀圖像被註釋掉了。

烏鴉

本文中的方法可被進一步擴展到更多圖像類型的分類上,分類數增加抽象效果會更好。這種方法是輕量級、可快速實現的CNN遷移學習方法。當然這也取決於速度、準確度、採用的硬件以及你投入的時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章