使用在ImageNet數據上預訓練的VGG16網絡模型對貓狗數據集進行分類識別。html
預訓練網絡是一個保存好的,已經在大型數據集上訓練好的卷積神經網絡。網絡
若是這個數據集足夠大且通用,那麼預訓練網絡學習到的模型參數能夠有效的對圖片進行特徵提取。即便新問題與本來的數據徹底不一樣,但學習到的特徵提取方法依然能夠在不一樣的問題之間進行移植,進而能夠在全新的數據集上提取到有效的特徵。對這些有效的高級特徵進行分類能夠大大提升模型分類的準確率。app
遷移學習主要適用於已有數據相對較少的狀況,若是擁有的數據量足夠大,即便不須要遷移學習也可以獲得很是高的準確率。dom
首先,讀入貓狗數據集中的圖片。(實現過程的詳細說明在Tensorflow學習筆記No.5中,這裏再也不贅述)學習
1 import tensorflow as tf 2 import numpy as np 3 import pandas as pd 4 import matplotlib.pyplot as plt 5 %matplotlib inline 6 import pathlib 7 import random 8 9 data_root = pathlib.Path('../input/cat-and-dog/training_set/training_set') 10 11 all_image_path = list(data_root.glob('*/*.jpg')) 12 random.shuffle(all_image_path) 13 image_count = len(all_image_path) 14 15 label_name = sorted([item.name for item in data_root.glob('*')]) 16 name_to_indx = dict((name, indx) for indx, name in enumerate(label_name)) 17 18 all_image_path = [str(path) for path in all_image_path] 19 all_image_label = [name_to_indx[pathlib.Path(p).parent.name] for p in all_image_path] 20 21 def load_pregrosess_image(path, label): 22 image = tf.io.read_file(path) 23 image = tf.image.decode_jpeg(image, channels = 3) 24 image = tf.image.resize(image, [256, 256]) 25 image = tf.cast(image, tf.float32) 26 image = image / 255 27 return image, label 28 29 train_image_ds = tf.data.Dataset.from_tensor_slices((all_image_path, all_image_label)) 30 31 AUTOTUNE = tf.data.experimental.AUTOTUNE 32 dataset = train_image_ds.map(load_pregrosess_image, num_parallel_calls = AUTOTUNE) 33 34 BATCHSIZE = 16 35 train_count = int(image_count * 0.8) 36 test_count = image_count - train_count 37 38 train_dataset = dataset.take(train_count) 39 test_dataset = dataset.skip(train_count) 40 41 train_dataset = train_dataset.shuffle(train_count).repeat().batch(BATCHSIZE) 42 test_dataset = test_dataset.repeat().batch(BATCHSIZE)
與訓練的網絡由兩個部分構成,訓練好的卷積基和訓練好的分類器。咱們須要使用訓練好的卷積基來提取特徵,並使用自定義的分類器對本身的數據集進行分類識別。測試
以下圖所示:spa
訓練過程當中,咱們僅僅對自定義的分類器進行訓練,而不訓練預訓練好的卷積基部分。code
預訓練的卷積基能夠很是好的提取圖像的某些特徵,在訓練過程當中,因爲分類器是一個全新的沒有訓練過的分類器,在訓練初期會產生很大的loss值,因爲數據量較少,若是不對預訓練的卷積基進行凍結(不更新參數)處理,產生的loss值經梯度傳遞會對預訓練的卷積基形成很是大的影響,且因爲可訓練數據較少兒難以恢復,因此只對自定義的分類器進行訓練,而不訓練卷積基。htm
首先從tf.keras.applications中建立一個預訓練VGG16的卷積基。blog
1 cov_base = tf.keras.applications.VGG16(weights = 'imagenet', include_top = False)
weight是咱們要使用的模型權重,咱們使用經imagenet訓練過的模型的權重信息進行遷移學習。
include_top是指,是否使用預訓練的分類器。在遷移學習過程當中咱們使用自定義的分類器,因此參數爲False。
而後咱們對建立好的卷積基進行凍結處理,凍結全部的可訓練參數。
1 cov_base.trainable = False
使用keras.Sequential()建立網絡模型。
1 model = tf.keras.Sequential() 2 model.add(cov_base) 3 model.add(tf.keras.layers.GlobalAveragePooling2D()) 4 model.add(tf.keras.layers.Dense(512, activation = 'relu')) 5 model.add(tf.keras.layers.Dense(1, activation = 'sigmoid'))
在模型中加入卷積基和自定義的分類器。
模型結構以下圖所示:
咱們獲得了一個可訓練參數僅爲263,169的預訓練VGG16網絡模型。
此時模型已經搭建完畢,咱們使用以前處理好的數據對它進行訓練。
1 model.compile(optimizer = 'adam', 2 loss = 'binary_crossentropy', 3 metrics = ['acc'] 4 ) 5 6 history = model.fit(train_dataset, 7 steps_per_epoch = train_count // BATCHSIZE, 8 epochs = 10, 9 validation_data = test_dataset, 10 validation_steps = test_count // BATCHSIZE 11 ) 12 13 plt.plot(history.epoch, history.history.get('acc'), label = 'acc') 14 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')
訓練結果以下圖所示:
模型在訓練集和測試機上的正確率均達到了94%左右,並且僅僅通過了10個epoch就達到了這樣的效果,足以看出遷移學習在小規模數據上的優點。
雖然使用預訓練網絡能夠輕易的達到94%左右的正確率,可是,若是咱們還想繼續提升這個正確率該怎樣進行調整呢?
所謂微調,是凍結卷積基底部的卷積層,共同訓練新添加的分類器和卷積基頂部的部分卷積層。
根據卷積神經網絡提取特徵的原理咱們不難發現,越底層的卷積層提取到的圖像特徵越抽象越細小,而頂層的卷積層提取到的特徵更大,更加的接近咱們能直接觀察到的數據特徵,因爲咱們須要訓練的數據和預訓練時使用的數據不盡相同,因此越頂層的卷積層提取到的特徵與咱們所須要的特徵差異越大。因此,咱們只凍結底部的卷積層,將頂部的卷積層與訓練好的分類器共同訓練,會獲得更好的擬合效果。
只有分類器以及訓練好了,才能微調卷積基的頂部卷積層,不然因爲訓練初期的偏差很大,會將卷積層以前學習到的參數破壞掉。
因此咱們對卷積基進行解凍,並只對底部的卷積進行凍結。
1 cov_base.trainable = True 2 for layers in cov_base.layers[:-3]: 3 layers.trainable = False
而後將模型繼續進行訓練。
1 model.compile(optimizer = tf.keras.optimizers.Adam(lr = 0.0001), 2 loss = 'binary_crossentropy', 3 metrics = ['acc'] 4 ) 5 6 history = model.fit(train_dataset, 7 steps_per_epoch = train_count // BATCHSIZE, 8 epochs = 20, 9 initial_epoch = 10, 10 validation_data = test_dataset, 11 validation_steps = test_count // BATCHSIZE 12 ) 13 14 plt.plot(history.epoch, history.history.get('acc'), label = 'acc') 15 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')
注意將學習率調小,以便儘量的達到loss的極小值點。
獲得的結果以下圖所示:
模型再訓練集上達到了近乎100%的準確率,在測試集上也達到了96%左右準確率,微調的效果仍是較爲明顯的。
那麼關於遷移學習的介紹到這裏就結束了o(* ̄▽ ̄*)o,後續會更新更多內容。