使用VGG16網路進行遷移學習
使用在ImageNet資料上預訓練的VGG16網路模型對貓狗資料集進行分類識別。
1.預訓練網路
預訓練網路是一個儲存好的,已經在大型資料集上訓練好的卷積神經網路。
如果這個資料集足夠大且通用,那麼預訓練網路學習到的模型引數可以有效的對圖片進行特徵提取。即使新問題與原本的資料完全不同,但學習到的特徵提取方法依然可以在不同的問題之間進行移植,進而可以在全新的資料集上提取到有效的特徵。對這些有效的高階特徵進行分類可以大大提高模型分類的準確率。
遷移學習主要適用於已有資料相對較少的情況,如果擁有的資料量足夠大,即使不需要遷移學習也能夠得到非常高的準確率。
2.如何使用與訓練網路
2.1載入影像並建立資料集
首先,讀入貓狗資料集中的圖片。(實現過程的詳細說明在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)
2.2載入與訓練網路並構建網路模型
與訓練的網路由兩個部分構成,訓練好的卷積基和訓練好的分類器。我們需要使用訓練好的卷積基來提取特徵,並使用自定義的分類器對自己的資料集進行分類識別。
如下圖所示:
訓練過程中,我們僅僅對自定義的分類器進行訓練,而不訓練預訓練好的卷積基部分。
預訓練的卷積基可以非常好的提取影像的某些特徵,在訓練過程中,由於分類器是一個全新的沒有訓練過的分類器,在訓練初期會產生很大的loss值,由於資料量較少,如果不對預訓練的卷積基進行凍結(不更新引數)處理,產生的loss值經梯度傳遞會對預訓練的卷積基造成非常大的影響,且由於可訓練資料較少兒難以恢復,所以只對自定義的分類器進行訓練,而不訓練卷積基。
首先從tf.keras.applications中建立一個預訓練VGG16的卷積基。
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網路模型。
2.3使用自定義資料訓練分類器
此時模型已經搭建完畢,我們使用之前處理好的資料對它進行訓練。
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就達到了這樣的效果,足以看出遷移學習在小規模資料上的優勢。
3.微調
雖然使用預訓練網路可以輕易的達到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,後續會更新更多內容。