TensorFlow2.0 学习笔记(四):迁移学习(MobileNetV2)
歡迎關(guān)注WX公眾號:【程序員管小亮】
專欄——TensorFlow學(xué)習(xí)筆記
文章目錄
- 歡迎關(guān)注WX公眾號:【程序員管小亮】
- 專欄——TensorFlow學(xué)習(xí)筆記
- 一、數(shù)據(jù)處理
- 1.1_下載數(shù)據(jù)集
- 1.2_數(shù)據(jù)預(yù)處理
- 1.3_數(shù)據(jù)增強
- 1.4_數(shù)據(jù)可視化
- 二、構(gòu)建模型
- 2.1_可視化模型
- 2.2_設(shè)置訓(xùn)練參數(shù)
- 2.3_編譯和訓(xùn)練模型
- 2.4_可視化訓(xùn)練指標(biāo)
- 三、使用預(yù)訓(xùn)練的模型
- 3.1_下載預(yù)訓(xùn)練模型
- 3.2_添加分類層
- 3.3_訓(xùn)練Mobile模型
- 3.4_微調(diào)預(yù)訓(xùn)練網(wǎng)絡(luò)
- 推薦閱讀
- 參考文章
下面我們準(zhǔn)備使用 Keras 中預(yù)定義的經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)進行遷移學(xué)習(xí)。
一、數(shù)據(jù)處理
1.1_下載數(shù)據(jù)集
import matplotlib.pyplot as plt import numpy as np import tensorflow as tf import tensorflow_datasets as tfds print(tf.__version__)
tensorflow_datasets 中包含了許多數(shù)據(jù)集,我們可以按照需求添加自己的數(shù)據(jù)集,下面列出所有可用的數(shù)據(jù)集:
這里即將使用的 tf_flowers 數(shù)據(jù)集,其大小為 218MB,返回值為 FeaturesDict 對象,尚未進行分割。
- 由于該數(shù)據(jù)集尚未定義標(biāo)準(zhǔn)分割形式,首先將利用 subsplit 函數(shù)將數(shù)據(jù)集分割為三部分,80% 用于訓(xùn)練,10% 用于驗證,10% 用于測試;
- 其次如果想加載整個數(shù)據(jù)集到內(nèi)存中,可以設(shè)置 batch_size=-1;
- 然后使用 tfds.load() 函數(shù)來下載數(shù)據(jù),需要特別注意設(shè)置參數(shù) as_supervised=True,這樣函數(shù)就會返回 tf.data.Dataset,一個二元組 (input, label) ,而不是返回 FeaturesDict ,因為二元組的形式更方便理解和使用;
- 最后再指定 with_info=True,這樣就可以得到函數(shù)處理的信息,以便加深對數(shù)據(jù)的理解。
代碼如下:
import tensorflow_datasets as tfdsSPLIT_WEIGHTS = (8, 1, 1) splits = tfds.Split.TRAIN.subsplit(weighted=SPLIT_WEIGHTS) (raw_train, raw_validation, raw_test), metadata = tfds.load(name="tf_flowers",with_info=True,split=list(splits),# batch_size=-1,as_supervised=True)print(raw_train) print(raw_validation) print(raw_test)1.2_數(shù)據(jù)預(yù)處理
從 tensorflow_datasets 中下載的數(shù)據(jù)集包含很多不同尺寸的圖片,因此需要將這些圖像的尺寸調(diào)整為固定的大小,并且將所有像素值都進行標(biāo)準(zhǔn)化,使得像素值的變化范圍都在 0~1 之間。盡管這些操作顯得繁瑣無用,但是必須進行這些預(yù)處理操作,因為在訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)之前,必須指定它的輸入維度。不僅如此,網(wǎng)絡(luò)中最后全連接層的 shape 取決于 CNN 的輸入維度,因此這些預(yù)處理的操作是很有必要的。最明顯的例子就是上面復(fù)現(xiàn)的 LeNet 的 flatten 層,如果你不知道確切的維度,那么就會各種報錯。。。
如下所示,構(gòu)建一個函數(shù) format_exmaple(),并將它傳遞給 raw_train, raw_validation 和 raw_test 的映射函數(shù),從而完成對數(shù)據(jù)的預(yù)處理。需要指明的是,format_exmaple() 的參數(shù)和傳遞給 tfds.load() 的參數(shù)有關(guān):
- 如果 as_supervised=True,那么 tfds.load() 將下載一個二元組 (image, labels) ,該二元組將作為參數(shù)傳遞給 format_exmaple();
- 如果 as_supervised=False,那么 tfds.load() 將下載一個字典 <image,lable> ,該字典將作為參數(shù)傳遞給 format_exmaple() 。
除此之外
-
還對 train 對象調(diào)用 .shuffle(BUFFER_SIZE) ,用于打亂訓(xùn)練集的順序,該操作能夠消除樣本的 次序偏差。
-
再用 .batch(BATCH_SIZE) 來定義這三類數(shù)據(jù)集的 batch 大小,這里 batch 的大小設(shè)置為 32 。
-
最后用 .prefetch() 在后臺預(yù)加載數(shù)據(jù),該操作能夠在模型訓(xùn)練的時候進行,從而減少訓(xùn)練時間。
下圖直觀地描述了 .prefetch() 的作用。
不采取 prefetch 操作,CPU 和 GPU/TPU 的大部分時間都處在空閑狀態(tài)。
采取 prefetch 操作后,CPU 和 GPU/TPU 的空閑時間顯著較少。
在該步驟中,有幾點值得注意:
- 操作順序很重要。
- 如果先執(zhí)行 .shuffle() 操作,再執(zhí)行 .repeat() 操作,那么將進行跨 batch 的數(shù)據(jù)打亂操作,每個 epoch 中的 batch 數(shù)據(jù)都是被提前打亂的,而不用每次加載一個 batch 就打亂依一次它的數(shù)據(jù)順序;
- 如果先執(zhí)行 .repeat() 操作,再執(zhí)行 .shuffle() 操作,那么每次只有單個 batch 內(nèi)的數(shù)據(jù)次序被打亂,而不會進行跨 batch 的數(shù)據(jù)打亂操作。
- 將 buffer_size 設(shè)置為和數(shù)據(jù)集大小一樣,這樣數(shù)據(jù)能夠被充分的打亂,但是 buffer_size 過大會導(dǎo)致消耗更多的內(nèi)存。
- 在開始進行打亂操作之前,系統(tǒng)會分配一個緩沖區(qū),用于存放即將進行打亂的數(shù)據(jù),因此在數(shù)據(jù)集開始工作之前,過大的 buffer_size 會導(dǎo)致一定的延時。
- 在緩沖區(qū)沒有完全釋放之前,正在執(zhí)行打亂操作的數(shù)據(jù)集不會報告數(shù)據(jù)集的結(jié)尾,而數(shù)據(jù)集會被 .repeat() 重啟,這將會又一次導(dǎo)致延時。
其中 tf.data.experimental.AUTOTUNE 可以讓程序自動的選擇最優(yōu)的線程并行個數(shù)。
1.3_數(shù)據(jù)增強
在訓(xùn)練階段,對數(shù)據(jù)進行實時增廣操作,而不是手動的將這些增廣圖像添加到數(shù)據(jù)上。
代碼如下:
def augment_data(image, label):print("擴展數(shù)據(jù)調(diào)用!")# 將圖片隨機進行水平翻轉(zhuǎn)image = tf.image.random_flip_left_right(image)# 隨機設(shè)置圖片的對比度image = tf.image.random_contrast(image, lower=0.0, upper=1.0)# 將圖片隨機進行垂直翻轉(zhuǎn)# image = tf.image.random_flip_up_down(img)# 隨機設(shè)置圖片的亮度# image = tf.image.random_brightness(img, max_delta=0.5)# 隨機設(shè)置圖片的色度# image = tf.image.random_hue(img, max_delta=0.3)# 隨機設(shè)置圖片的飽和度# image = tf.image.random_saturation(img, lower=0.3, upper=0.5)# 增加更多選擇return image, labeltrain = train.map(augment_data) SHUFFLE_BUFFER_SIZE = 1024 BATCH_SIZE = 32train = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE) validation = validation.batch(BATCH_SIZE) test = test.batch(BATCH_SIZE)train = train.prefetch(tf.data.experimental.AUTOTUNE) print(train) print(validation) print(test)print(metadata)
其中 metadata 是 tfds.load 的返回值之一 ds_info : tfds.core.DatasetInfo。請注意 ds_info,無論 split 請求如何,對象都會記錄整個數(shù)據(jù)集。Split-specific 的信息可在 ds_info.splits 中找到。
1.4_數(shù)據(jù)可視化
通過可視化數(shù)據(jù)集中的一些隨機樣本,不僅可以發(fā)現(xiàn)其中存在的異常或者偏差,還可以發(fā)現(xiàn)特定類別的圖像的變化或相似程度。使用 train.take() 可以批量獲取數(shù)據(jù)集,并將其轉(zhuǎn)化為 numpy 數(shù)組, tfds.as_numpy(train) 也具有相同的作用,如下代碼所示:
# 獲取將標(biāo)簽索引轉(zhuǎn)換為字符串的函數(shù) get_label_name = metadata.features['label'].int2str在這里,我們逐批獲取數(shù)據(jù)集,并在將其傳遞給 plotting 函數(shù)之前,將其轉(zhuǎn)換為 numpy 數(shù)組。
plt.figure(figsize=(12,12)) for batch in train.take(1):for i in range(9):image, label = batch[0][i], batch[1][i]plt.subplot(3,3,i+1)plt.imshow(image.numpy())plt.title(get_label_name(label.numpy()))plt.grid(False)plt.show()print(image.shape) print(np.min(image)) print(np.max(image))
我們還可以使用 tfds.as_numpy(train) 而不是 train.take() 直接獲取 numpy 數(shù)組。
二、構(gòu)建模型
tf.keras 是一個符合 Keras API 標(biāo)準(zhǔn)的 TensorFlow 實現(xiàn),它是一個用于構(gòu)建和訓(xùn)練模型的高級 API,而且對 TensorFlow 特定功能的支持相當(dāng)好(例如 eager execution 和 tf.data 管道)。tf.keras 不僅讓 TensorFlow 變得更加易于使用,而且還保留了它的靈活和高效。
from tensorflow import keras print(keras.__version__)張量 (image_height, image_width, color_channels) 作為模型的輸入,在這里不用考慮 batch 的大小。黑白圖像只有一個顏色通道,而彩色圖像具有三個顏色通道 (R,G,B) 。
- 在這里,采用彩色圖像作為輸入,輸入圖像尺寸為 (128,128,3) ,將該參數(shù)傳遞給 shape,從而完成輸入層的構(gòu)建。
- 接下來,將用一種很常見的模式構(gòu)建 CNN 的卷積部分:一系列堆疊的 Conv2D 層和 MaxPooling2D 層。
- 最后,將卷積部分的輸出((28,28,64) 的張量)饋送到一個或多個全連接層中,從而實現(xiàn)分類。
注意,全連接層的輸入必須是一維的向量,而卷積部分的輸出卻是三維的張量。因此需要先將三維的張量展平成一維的向量,然后再將該向量輸入到全連接層中。數(shù)據(jù)集中有 5 個類別,這些信息可以從數(shù)據(jù)集的元數(shù)據(jù)中獲取。因此,模型最后一個全連接層的輸出是一個長度為 5 的向量,再用 softmax 函數(shù)對它進行激活,至此就構(gòu)建好了 CNN 模型。
2.1_可視化模型
.summary() 為我們提供了完整的模型架構(gòu)。
simple_model = create_model() simple_model.summary() keras.utils.plot_model(simple_model, 'flower_model_with_shape_info.png', show_shapes=True)2.2_設(shè)置訓(xùn)練參數(shù)
我們指定了 tensorboard 保存日志的目錄。
import datetime, oslog_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") os.makedirs(log_dir)我們還得到了在訓(xùn)練集、驗證集和測試集中的示例數(shù)的值。
- steps_per_epoch:在一個 epoch 中訓(xùn)練模型的批數(shù)。其計算方法是將訓(xùn)練實例的數(shù)量除以每批的大小。
- validation_steps:與 steps_per_epoch 相同,但適用于驗證數(shù)據(jù)集。
2.3_編譯和訓(xùn)練模型
import osdef train_model(model):model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])# Creating Keras callbackstensorboard_callback = keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)model_checkpoint_callback = keras.callbacks.ModelCheckpoint('training_checkpoints/weights.{epoch:02d}-{val_loss:.2f}.hdf5', period=5)os.makedirs('training_checkpoints/', exist_ok=True)early_stopping_checkpoint = keras.callbacks.EarlyStopping(patience=5)history = model.fit(train.repeat(),epochs=5,steps_per_epoch=steps_per_epoch,validation_data=validation.repeat(),validation_steps=validation_steps,callbacks=[tensorboard_callback,model_checkpoint_callback,early_stopping_checkpoint])return history history = train_model(simple_model)2.4_可視化訓(xùn)練指標(biāo)
TF2.0 中的另一個新特性是能夠在 jupyter 筆記本內(nèi)部使用成熟的 tensorboard。在開始模型訓(xùn)練之前,先啟動tensorboard,這樣就可以在模型訓(xùn)練時查看指標(biāo)。
%load_ext tensorboard.notebook %tensorboard --logdir logs # 使用以下命令停止TensorBoard # 需要傳遞進程的ID !kill 100276我們將訓(xùn)練集和驗證集上的評估指標(biāo)進行了可視化,該指標(biāo)為 train_model() 的返回值,使用 Matplotlib 繪制曲線圖:
acc = history.history['accuracy'] val_acc = history.history['val_accuracy']loss = history.history['loss'] val_loss = history.history['val_loss']plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.legend(loc='lower right') plt.ylabel('Accuracy') plt.title('Training and Validation Accuracy')plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.legend(loc='upper right') plt.ylabel('Cross Entropy') plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()
這些可視化圖能讓我們更加深入了解模型的訓(xùn)練程度。在模型訓(xùn)練過程中,確保訓(xùn)練集和驗證集的精度在逐漸增加,而損失逐漸減少,這是非常重要的。
-
如果訓(xùn)練精度高但驗證精度低,那么模型很可能出現(xiàn)了 過擬合。這時需要對進行 數(shù)據(jù)增廣,或者直接從網(wǎng)上下載更多的圖像,從而增加訓(xùn)練集。此外,還可以采用一些防止過擬合的技術(shù),例如 Dropout 或者 BatchNormalisation 等等,詳細的可以看這個博客——深度學(xué)習(xí)100問之神經(jīng)網(wǎng)絡(luò)中解決過擬合的幾種方法。
-
如果訓(xùn)練精度和驗證精度都較高,但是驗證精度比訓(xùn)練精度略高,那么驗證集很可能包含較多易于分類的圖像。有時我們使用 Dropout 和 BatchNorm 等技術(shù)來防止過擬合,但是這些操作會為訓(xùn)練過程添加一些 隨機性,使得訓(xùn)練更加困難,因此模型在驗證集上表現(xiàn)會更好些。
稍微拓展一點講,由于訓(xùn)練集的評估指標(biāo)是對一個 epoch 的平均估計,而驗證集的評估指標(biāo)卻是在這個 epoch 結(jié)束后,再對驗證集進行評估的,因此驗證集所用的模型可以說要比訓(xùn)練集的模型訓(xùn)練的更久一些。
Jupyer notebook 中的 TensorBoard 視圖:
三、使用預(yù)訓(xùn)練的模型
在上面,我們訓(xùn)練了一個簡單的 CNN 模型,它給出了大約 60% 的準(zhǔn)確率。通過使用更大、更復(fù)雜的模型,獲得更高的準(zhǔn)確率,預(yù)訓(xùn)練模型 是一個很好的選擇,可以直接使用 預(yù)訓(xùn)練模型 來完成分類任務(wù),因為 預(yù)訓(xùn)練模型 通常已經(jīng)在大型的數(shù)據(jù)集上進行過訓(xùn)練,通常用于完成大型的圖像分類任務(wù)。
當(dāng)然也可以運用 遷移學(xué)習(xí) 的方法,只使用預(yù)訓(xùn)練模型的一部分,重新構(gòu)建屬于自己的模型。簡單來講,遷移學(xué)習(xí) 可以理解為:一個在足夠大的數(shù)據(jù)集上經(jīng)過訓(xùn)練的模型,能夠有效地作為視覺感知的通用模型,通過使用該模型的特征映射,我們就可以構(gòu)建一個魯棒性很強的模型,而不需要很多的數(shù)據(jù)去訓(xùn)練。
3.1_下載預(yù)訓(xùn)練模型
tf.keras.applications 中有一些預(yù)定義好的經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)(Application應(yīng)用),如下:
可以直接調(diào)用這些經(jīng)典的卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)(甚至載入預(yù)訓(xùn)練的參數(shù)),而無需手動定義網(wǎng)絡(luò)結(jié)構(gòu)。
例如,本次將要用到的模型是由谷歌開發(fā)的 MobileNetV2 網(wǎng)絡(luò)結(jié)構(gòu),該模型已經(jīng)在 ImageNet 數(shù)據(jù)集上進行過預(yù)訓(xùn)練,共含有 1.4M 張圖像,而且學(xué)習(xí)了常見的 1000 種物體的基本特征,因此,該模型具有強大的特征提取能力。
model = tf.keras.applications.MobileNetV2()當(dāng)執(zhí)行以上代碼時,TensorFlow 會自動從網(wǎng)絡(luò)上下載 MobileNetV2 網(wǎng)絡(luò)結(jié)構(gòu),因此在第一次執(zhí)行代碼時需要具備網(wǎng)絡(luò)連接。但是每個網(wǎng)絡(luò)結(jié)構(gòu)具有自己特定的詳細參數(shù)設(shè)置,比如我們這里的 tf.keras.applications.MobileNetV2,詳細的可以看這個 Keras 文檔;一些共通的常用參數(shù)如下:
-
input_shape :輸入張量的形狀(不含第一維的 Batch),大多默認為 224 × 224 × 3 。一般而言,模型對輸入張量的大小有下限,長和寬至少為 32 × 32 或 75 × 75;
-
include_top :在網(wǎng)絡(luò)的最后是否包含全連接層,默認為 True;
-
weights :預(yù)訓(xùn)練權(quán)值,默認為 imagenet,即為當(dāng)前模型載入在 ImageNet 數(shù)據(jù)集上預(yù)訓(xùn)練的權(quán)值。如需隨機初始化變量可設(shè)為 None;
-
classes :分類數(shù),默認為 1000。修改該參數(shù)需要 include_top 參數(shù)為 True 且 weights 參數(shù)為 None。
模型下載時,需要指定參數(shù) include_top=False,這樣網(wǎng)絡(luò)的最后就不包含全連接層了,因為我們只想使用該模型進行特征提取,而不是直接使用該模型進行分類。此外,它還將我們的輸入維度限制為該模型所訓(xùn)練的維度,默認值:299x299。預(yù)訓(xùn)練模型 的分類模塊通常受原始的分類任務(wù)限制,如果想將 預(yù)訓(xùn)練模型 用在新的分類任務(wù)上,需要自己構(gòu)建模型的分類模塊,而且需要將該模塊在新的數(shù)據(jù)集上進行訓(xùn)練,這樣才能使模型適應(yīng)新的分類任務(wù)。
現(xiàn)在使用 MobileNetV2 網(wǎng)絡(luò)在 tf_flowers 分類數(shù)據(jù)集上進行訓(xùn)練(為了代碼簡短高效,直接使用了 TensorFlow Datasets 和 tf.data 載入和預(yù)處理數(shù)據(jù))。通過將 weights 設(shè)置為 imagenet,隨機初始化變量且使用在 imagenet 上的預(yù)訓(xùn)練權(quán)值,同時不設(shè)置 classes,默認對應(yīng)于 1000 分類的數(shù)據(jù)集。
from tensorflow import keras # 從預(yù)先訓(xùn)練的模型MobileNetV2導(dǎo)入為基礎(chǔ)模型 base_model = keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
我們將 預(yù)訓(xùn)練模型 當(dāng)做一個特征提取器,輸入 (128,128,3) 的圖像,得到 (4,4,1280) 的輸出特征。特征提取器可以理解為一個特征映射過程,最終的輸出特征是輸入的多維表示,在新的特征空間中,更加利于圖像的分類。
注:在編譯和訓(xùn)練模型之前,凍結(jié)卷積模塊是很重要的。通過凍結(jié)(或設(shè)置 layer.trainable=false),我們可以防止在訓(xùn)練期間更新這些層中的權(quán)重。
3.2_添加分類層
由于指定了參數(shù) include_top=False,下載的 MobileNetV2 模型不包含最頂層的分類層,因此我們需要添加一個新的分類層,而且它是為 tf_flowers 所專門定制的。
要從特征塊生成預(yù)測,請使用 keras.layers.GlobalAveragePooling2D() 層在 2x2 空間位置上平均,以將特征轉(zhuǎn)換為每個圖像的單個 1280 元素向量。在頂部,應(yīng)用 keras.layers.Dense 層將這些特征轉(zhuǎn)換為每個圖像的單個預(yù)測。然后使用 tf.keras.sequential api來堆疊特征提取程序和這兩個層,在訓(xùn)練之前不要忘記編譯模型。
代碼如下:
def build_model():model = keras.Sequential([base_model,keras.layers.GlobalAveragePooling2D(),keras.layers.Dense(metadata.features['label'].num_classes, activation='softmax')])model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])return modelMobile_model = build_model() Mobile_model.summary()3.3_訓(xùn)練Mobile模型
# 訓(xùn)練前評估模型(可選) loss0, accuracy0 = Mobile_model.evaluate(validation.repeat(), steps = validation_steps) log_dir = os.path.join("logs","fit",datetime.datetime.now().strftime("%Y%m%d-%H%M%S"), ) os.makedirs(log_dir)# Creating Keras callbacks tensorboard_callback = keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1) model_checkpoint_callback = keras.callbacks.ModelCheckpoint('training_checkpoints/weights.{epoch:02d}-{val_loss:.2f}.hdf5', period=5) os.makedirs('training_checkpoints/', exist_ok=True) early_stopping_checkpoint = keras.callbacks.EarlyStopping(patience=5)history = Mobile_model.fit(train.repeat(),epochs=5,steps_per_epoch = steps_per_epoch,validation_data=validation.repeat(), validation_steps=validation_steps,callbacks=[tensorboard_callback,model_checkpoint_callback,early_stopping_checkpoint]) Train for 91 steps, validate for 11 stepsEpoch 1/5 91/91 [===========] - 134s 1s/step - loss: 1.0087 - accuracy: 0.6075 - val_loss: 1.2909 - val_accuracy: 0.5511 Epoch 2/5 91/91 [===========] - 139s 2s/step - loss: 0.6338 - accuracy: 0.7720 - val_loss: 0.7929 - val_accuracy: 0.7131 Epoch 3/5 91/91 [===========] - 130s 1s/step - loss: 0.5578 - accuracy: 0.7959 - val_loss: 0.8869 - val_accuracy: 0.7131 Epoch 4/5 91/91 [===========] - 140s 2s/step - loss: 0.5147 - accuracy: 0.8108 - val_loss: 0.8605 - val_accuracy: 0.7330 Epoch 5/5 91/91 [===========] - 137s 2s/step - loss: 0.4757 - accuracy: 0.8264 - val_loss: 0.9297 - val_accuracy: 0.6903經(jīng)過5個階段的訓(xùn)練,我們得到了約 70% 的準(zhǔn)確率,繪制了訓(xùn)練和驗證精度/損失的學(xué)習(xí)曲線。
acc = history.history['accuracy'] val_acc = history.history['val_accuracy']loss = history.history['loss'] val_loss = history.history['val_loss']plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.legend(loc='lower right') plt.ylabel('Accuracy') plt.title('Training and Validation Accuracy')plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.legend(loc='upper right') plt.ylabel('Cross Entropy') plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()
開始訓(xùn)練預(yù)訓(xùn)練模型后,訓(xùn)練集和驗證集的評估指標(biāo)隨著訓(xùn)練 epoch 的變化。
從圖中可以看到,驗證集的精度高略低于訓(xùn)練集的精度。使用測試集來評估模型可以進一步驗證模型的泛化能力。如果想讓模型取得更好的效果,對模型進行微調(diào)。
# 保存Keras模型 Mobile_model.save('Mobile_v2_128_tf_flowes.h5') loaded_model = keras.models.load_model('Mobile_v2_128_tf_flowes.h5') loaded_model.evaluate(test)3.4_微調(diào)預(yù)訓(xùn)練網(wǎng)絡(luò)
在特征提取實驗中,我們只在 Mobile_v2 的基礎(chǔ)模型上訓(xùn)練了幾層。訓(xùn)練過程中,訓(xùn)練前網(wǎng)絡(luò)的權(quán)值不更新。進一步提高性能的一種方法是在訓(xùn)練頂級分類器的同時 微調(diào) 預(yù)訓(xùn)練模型 頂層的權(quán)重。訓(xùn)練過程將強制將權(quán)重從通用特征映射調(diào)整到與我們的數(shù)據(jù)集特定關(guān)聯(lián)的特征。
注意:只有在預(yù)先訓(xùn)練的模型設(shè)置為 non-trainable 的 top-level 分類器之后,才能嘗試此操作。如果在預(yù)先訓(xùn)練的模型上添加一個隨機初始化的分類器并嘗試聯(lián)合訓(xùn)練所有層,則梯度更新的幅度將過大(由于來自分類器的隨機權(quán)重),并且預(yù)先訓(xùn)練的模型將忘記它所學(xué)到的一切。
此外,微調(diào) 預(yù)訓(xùn)練模型 的頂層而不是 預(yù)訓(xùn)練模型 的所有層背后的原因如下:在 convnet 中,一個層越高,它就越專業(yè)。convnet 的前幾層學(xué)習(xí)了非常簡單和通用的特性,這些特性幾乎可以推廣到所有類型的圖像,但是當(dāng)你往上走的時候,這些特性對模型所訓(xùn)練的數(shù)據(jù)集越來越具體。微調(diào) 的目標(biāo)是使這些專門的特性適應(yīng)新的數(shù)據(jù)集。
# 取消凍結(jié)模型的頂層 base_model.trainable = True # 讓我們看看基本模型中有多少層 print("Number of layers in the base model: ", len(base_model.layers)) # Fine tune from this layer onwards fine_tune_at = 129# Freeze all the layers before the `fine_tune_at` layer for layer in base_model.layers[:fine_tune_at]:layer.trainable = False# Compile the model using a much-lower training rate. Mobile_model_model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.0001),loss='sparse_categorical_crossentropy',metrics=['accuracy']) Mobile_model_model.summary() history_fine = Mobile_model.fit(train.repeat(), steps_per_epoch = steps_per_epoch,epochs=10, initial_epoch = 5,validation_data=validation.repeat(), validation_steps=validation_steps,callbacks=[tensorboard_callback,model_checkpoint_callback,early_stopping_checkpoint]) Train for 91 steps, validate for 11 steps Epoch 6/10 91/91 [=========] - 148s 2s/step - loss: 0.4520 - accuracy: 0.8359 - val_loss: 0.9207 - val_accuracy: 0.7273 Epoch 7/10 91/91 [=========] - 139s 2s/step - loss: 0.3368 - accuracy: 0.8773 - val_loss: 0.8071 - val_accuracy: 0.7614 Epoch 8/10 91/91 [=========] - 141s 2s/step - loss: 0.2874 - accuracy: 0.8954 - val_loss: 0.7512 - val_accuracy: 0.7784 Epoch 9/10 91/91 [=========] - 127s 1s/step - loss: 0.2340 - accuracy: 0.9175 - val_loss: 0.7784 - val_accuracy: 0.7812 Epoch 10/10 91/91 [=========] - 136s 1s/step - loss: 0.2130 - accuracy: 0.9200 - val_loss: 0.8010 - val_accuracy: 0.7812注意:如果訓(xùn)練數(shù)據(jù)集相當(dāng)小,并且與 MobileNet V2 所使用的原始數(shù)據(jù)集相似,那么微調(diào)可能會導(dǎo)致過度擬合。
acc += history_fine.history['accuracy'] val_acc += history_fine.history['val_accuracy']loss += history_fine.history['loss'] val_loss += history_fine.history['val_loss']initial_epochs=5plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy')plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()
總之,我們介紹了如何使用預(yù)先訓(xùn)練的模型進行轉(zhuǎn)移學(xué)習(xí)以提高準(zhǔn)確性:
- 使用 預(yù)訓(xùn)練模型 特征提取:在處理小數(shù)據(jù)集時,通常會利用在同一域中的較大數(shù)據(jù)集上訓(xùn)練的模型所學(xué)習(xí)的特征,這是通過實例化 預(yù)訓(xùn)練模型 并在上面添加一個全連接分類器來完成的。訓(xùn)練前的模型被凍結(jié),訓(xùn)練過程中只更新分類器的權(quán)值,在這種情況下,卷積部分提取與每個圖像相關(guān)聯(lián)的所有特征,并且訓(xùn)練一個在給定特征集上確定類別的分類器。
- 微調(diào) 預(yù)訓(xùn)練模型:為了進一步提高性能,可能需要通過 微調(diào) 將 預(yù)訓(xùn)練模型 的頂層重新利用到新的數(shù)據(jù)集。在這種情況下,調(diào)整權(quán)重以便學(xué)習(xí)特定于我們數(shù)據(jù)集的高度指定和高維特征。只有當(dāng)訓(xùn)練數(shù)據(jù)集很大并且非常類似于預(yù)先訓(xùn)練模型所使用的原始數(shù)據(jù)集時,這才有意義。
推薦閱讀
- TensorFlow2.0 學(xué)習(xí)筆記(一):TensorFlow 2.0 的安裝和環(huán)境配置以及上手初體驗
- TensorFlow2.0 學(xué)習(xí)筆記(二):多層感知機(MLP)
- TensorFlow2.0 學(xué)習(xí)筆記(三):卷積神經(jīng)網(wǎng)絡(luò)(CNN)
- TensorFlow2.0 學(xué)習(xí)筆記(四):遷移學(xué)習(xí)(MobileNetV2)
- TensorFlow2.0 學(xué)習(xí)筆記(五):循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)
參考文章
- TensorFlow 官方文檔
- 簡單粗暴 TensorFlow 2.0
- tfds.load
- https://github.com/himanshurawlani/practical_intro_to_tf2
總結(jié)
以上是生活随笔為你收集整理的TensorFlow2.0 学习笔记(四):迁移学习(MobileNetV2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker启动nginx后挂了_Doc
- 下一篇: java c3p0获取主键_Tomcat