深度學習Tensorflow?2.4?完成遷移學習和模型微調(diào)
前言
本文使用 cpu 的 tensorflow 2.4 完成遷移學習和模型微調(diào),并使用訓練好的模型完成貓狗圖片分類任務。
預訓練模型在 NLP 中最常見的可能就是 BERT 了,在 CV 中我們此次用到了 MobileNetV2 ,它也是一個輕量化預訓練模型,它已經(jīng)經(jīng)過大量的圖片分類任務的訓練,里面保存了一個可以通用的去捕獲圖片特征的模型網(wǎng)絡結(jié)構(gòu),其可以通用地提取出圖片的有意義特征。這些特征捕獲功能可以輕松遷移到其他圖片任務上幫助其完成特征提取工作。
本文的工作,就是在 MobileNetV2 基礎上加入我們自定義的若干網(wǎng)絡層,通過使用大量的貓狗數(shù)據(jù)先對新添加的分類器層進行遷移學習,然后結(jié)合基礎模型的最后幾層與我們的自定義分類器一起進行微調(diào)訓練,就可以輕松獲得一個效果很好的貓狗圖片分類模型,而不必基于大量數(shù)據(jù)集從頭開始訓練一個大型模型,這樣會很耗時間。
實現(xiàn)過程
1. 獲取數(shù)據(jù)
首先我們要使用 tensorflow 的內(nèi)置函數(shù),從網(wǎng)絡上下載貓狗圖片集,訓練數(shù)據(jù)中的貓、狗圖片各 1000 張,驗證數(shù)據(jù)中的貓、狗圖片各 500 張。每張圖片的大小都是(160, 160, 3),每個 batch 中有 32 張圖片。
import matplotlib.pyplot as plt import numpy as np import os import tensorflow as tf URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip' BATCH_SIZE = 32 IMG_SIZE = (160, 160) path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=URL, extract=True) PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered') train_dir = os.path.join(PATH, 'train') validation_dir = os.path.join(PATH, 'validation') train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE) validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE) class_names = train_dataset.class_names
在這里我們挑選了部分圖片和標簽進行展示。
plt.figure(figsize=(10, 10)) for images, labels in train_dataset.take(1): for i in range(3): ax = plt.subplot(3, 3, i + 1) plt.imshow(images[i].numpy().astype("uint8")) plt.title(class_names[labels[i]]) plt.axis("off")
2. 數(shù)據(jù)擴充與數(shù)據(jù)縮放
(1)由于我們沒有測試集數(shù)據(jù),使用 tf.data.experimental.cardinality 確定驗證集中有多少個 batch 的數(shù)據(jù),然后將其中的 20% 的 batch 編程測試集。
val_batches = tf.data.experimental.cardinality(validation_dataset) test_dataset = validation_dataset.take(val_batches // 5) validation_dataset = validation_dataset.skip(val_batches // 5)
(2)為了保證在加載數(shù)據(jù)的時候不會出現(xiàn) I/O 不會阻塞,我們在從磁盤加載完數(shù)據(jù)之后,使用 cache 會將數(shù)據(jù)保存在內(nèi)存中,確保在訓練模型過程中數(shù)據(jù)的獲取不會成為訓練速度的瓶頸。如果說要保存的數(shù)據(jù)量太大,可以使用 cache 創(chuàng)建磁盤緩存提高數(shù)據(jù)的讀取效率。另外我們還使用 prefetch 在訓練過程中可以并行執(zhí)行數(shù)據(jù)的預獲取。
AUTOTUNE = tf.data.AUTOTUNE train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE) validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE) test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)
(3)因為我們沒有大量的圖片數(shù)據(jù),我們將現(xiàn)有的圖片數(shù)據(jù)進行旋轉(zhuǎn)或者翻轉(zhuǎn)操作可以增加樣本的多樣性,這樣也有助于減少過擬合現(xiàn)象。RandomFlip 函數(shù)將根據(jù) mode 屬性將圖片進行水平或垂直翻轉(zhuǎn)圖像。"可選的有 "horizontal" 、 "vertical" 或 "horizontal_and_vertical" 。"horizontal" 是左右翻轉(zhuǎn), "vertical" 是上下翻轉(zhuǎn)。RandomRotation 函數(shù)通過設置 factor 來講圖片進行旋轉(zhuǎn),假如 factor=0.2 會將圖片在 [-20% * 2pi, 20% * 2pi] 范圍內(nèi)隨機旋轉(zhuǎn)。
data_augmentation = tf.keras.Sequential([ tf.keras.layers.RandomFlip('horizontal'), tf.keras.layers.RandomRotation(0.1), ])
我們這里將隨便使用一張圖片,使用數(shù)據(jù)增強的方法對其進行變化,第一張圖是原圖,其他的都是進行了變化的圖片
for image, _ in train_dataset.take(1): plt.figure(figsize=(10, 10)) first_image = image[0] for i in range(3): if i==0: ax = plt.subplot(3, 3, i + 1) plt.imshow(first_image/ 255) plt.axis('off') else: ax = plt.subplot(3, 3, i + 1) augmented_image = data_augmentation(tf.expand_dims(first_image, 0)) plt.imshow(augmented_image[0] / 255) plt.axis('off')
(4)由于 MobileNetV2 模型的輸入值范圍是在 [-1, 1] 范圍內(nèi),但此時我們的圖片數(shù)據(jù)中的像素值處于 [0, 255] 范圍內(nèi),所以要重新縮放這些像素值, Rescaling 函數(shù)可以實現(xiàn)該操作。如果將 [0, 255] 范圍的輸入縮放到 [0, 1] 的范圍,我們可以通過設置參數(shù) scale=1./255 來實現(xiàn)。如果將 [0, 255] 范圍內(nèi)的輸入縮放到 [-1, 1] 范圍內(nèi),我們可以通過設置參數(shù) scale=1./127.5, offset=-1 來實現(xiàn)。
rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)
3. 遷移學習
(1)我們將 MobileNet V2 模型當做一個基礎模型,此模型已基于 ImageNet 數(shù)據(jù)集進行完美的預訓練,一般來說最后一層就是一個分類器,我們選擇將在 MobileNet V2 的倒數(shù)第二個網(wǎng)絡層上搭建自己的分類器,與最后一層相比倒數(shù)第二層能夠保留更豐富的圖片特征。在實際操作中我們實例化 MobileNetV2 模型,通過指定 include_top=False 參數(shù),可以加載不包括最頂層的整個預訓練網(wǎng)絡結(jié)果。 該模型的作用就是將(160,160,3)大小的圖片轉(zhuǎn)換為(5,5,1280)的特征輸出。
(2)第一層是將我們的訓練數(shù)據(jù)都進行數(shù)據(jù)增強操作,也就是隨機的翻轉(zhuǎn)和旋轉(zhuǎn)。
(3)第二層是接收輸入為 (160,160,3)大小的圖片的輸入層。
(4)第三層是我們直接拿來用的 MobileNetV2 ,因為我們要直接使用基礎模型的圖片特征提取能力,所以為了在訓練過程中其權重不發(fā)生變化,我們將基礎模型中的權重參數(shù)都凍結(jié)。
(5)第四層是一個池化層,可以將每個 batch 從 (32,5,5,1280) 壓縮為 (32,1280) 大小的輸出。
(6)第五層是 Dropout ,防止過擬合。
(7)第六層是對該圖片一個預測值,也就是 logit 。如果是正數(shù)預測標簽 1 ,如果是負數(shù)預測標簽 0 。
(8)我們從模型的 summary 中可以看到,此時我們的模型中的 2259265 個參數(shù)被凍結(jié),只有 1281 個參數(shù)是可以訓練的,它們是分屬兩個變量的可訓練參數(shù),即最后一個全連接層即權重 1280 個可訓練參數(shù)和偏差 1 個可訓練參數(shù)。
IMG_SHAPE = IMG_SIZE + (3,) inputs = tf.keras.Input(shape=(160, 160, 3)) x = data_augmentation(inputs) x = tf.keras.applications.mobilenet_v2.preprocess_input(x) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet') base_model.trainable = False x = base_model(x, training=False) x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = tf.keras.layers.Dense(1)(x) model = tf.keras.Model(inputs, outputs) model.summary()
模型結(jié)構(gòu)如下:
Model: "model_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_8 (InputLayer) [(None, 160, 160, 3)] 0 sequential_2 (Sequential) (None, 160, 160, 3) 0 tf.math.truediv_3 (TFOpLamb (None, 160, 160, 3) 0 da) tf.math.subtract_3 (TFOpLam (None, 160, 160, 3) 0 bda) mobilenetv2_1.00_160 (Funct (None, 5, 5, 1280) 2257984 ional) global_average_pooling2d_3 (None, 1280) 0 (GlobalAveragePooling2D) dropout_2 (Dropout) (None, 1280) 0 dense_2 (Dense) (None, 1) 1281 ================================================================= Total params: 2,259,265 Trainable params: 1,281 Non-trainable params: 2,257,984
可以看到模型中的可訓練的 2 個變量:
model.trainable_variables
結(jié)果如下:
[<tf.Variable 'dense_2/kernel:0' shape=(1280, 1) dtype=float32, numpy=
array([[ 0.08899798],
[-0.06681276],
[ 0.00906871],
...,
[-0.00114891],
[-0.01134416],
[-0.02000826]], dtype=float32)>,
<tf.Variable 'dense_2/bias:0' shape=(1,) dtype=float32, numpy=array([0.03746362], dtype=float32)>]
(9)選擇 Adam 優(yōu)化器,將我們的學習率設置為 0.0003 。選擇 BinaryCrossentropy 作為損失函數(shù)。選擇常規(guī)的 accuracy 作為評估指標。此時我們先對基礎模型訓練 10 個 epoch 。
lr = 0.0003 initial_epochs = 10 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=lr), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=['accuracy']) history = model.fit(train_dataset, validation_data=test_dataset, epochs=initial_epochs)
訓練過程如下:
Epoch 1/10 63/63 [==============================] - 24s 352ms/step - loss: 0.5517 - accuracy: 0.6835 - val_loss: 0.2720 - val_accuracy: 0.8958 Epoch 2/10 63/63 [==============================] - 21s 327ms/step - loss: 0.2792 - accuracy: 0.8865 - val_loss: 0.1499 - val_accuracy: 0.9531 ... Epoch 9/10 63/63 [==============================] - 20s 321ms/step - loss: 0.1075 - accuracy: 0.9530 - val_loss: 0.0766 - val_accuracy: 0.9740 Epoch 10/10 63/63 [==============================] - 21s 329ms/step - loss: 0.1040 - accuracy: 0.9560 - val_loss: 0.0742 - val_accuracy: 0.9740
(10)使用測試數(shù)據(jù)對模型進行評估。
model.evaluate(validation_dataset)
結(jié)果如下:
loss: 0.0664 - accuracy: 0.9765
4. 微調(diào)
(1)在上面的操作中,我們僅僅在 MobileNetV2 基礎模型的頂部添加了一層池化層、一層 Dropout、一層全連接層作為我們的自定義分類器。預訓練模型 MobileNetV2 中權重在訓練過程中未曾發(fā)生過更新。
(2)我們還有一種被稱為微調(diào)的方法,可以在訓練基礎模型權重的同時,同時訓練我們上面自定義添加的用于分類的分類器。這個微調(diào)的訓練過程可以將基礎模型的通用的圖片特征提取能力調(diào)整為專門提取本任務中貓狗數(shù)據(jù)集特征的能力。這個微調(diào)的操作只能在我們進行了上面的遷移學習操作之后才能進行,否則如果一開始直接將基礎模型和我們自定義的若干層分類器一起進行訓練,則會由于隨機初始化的分類器導致整個模型的更新梯度太大,從而使得基礎模型喪失了其預訓練的有效能力。其次在微調(diào)過程中我們應該選擇性地去微調(diào)少量頂部的網(wǎng)絡層而不是整個 MobileNet 模型,因為在卷積神經(jīng)網(wǎng)絡中,低層的網(wǎng)絡層一般捕獲到的是通用的圖片特征,這個能力可以泛化應用到幾乎所有類型的圖片,但是越往頂部的網(wǎng)絡層,越來越聚焦于捕獲訓練時所用到的訓練數(shù)據(jù)的特征,而微調(diào)的目標正是是讓這個模型更加適用于所用的專門的數(shù)據(jù)集,也就是本次進行的貓狗圖片。
(3)MobileNetV2 模型一共有 154 層結(jié)構(gòu),我們將模型的前 100 層的參數(shù)進行凍結(jié),對頂部的 54 層網(wǎng)絡結(jié)構(gòu)中的參數(shù)與我們自定義的分類器的若干層一起進行訓練,我們通過打印模型的 summary 可以看到,此時共有 54 個可以訓練的變量,這些變量中共有可訓練的參數(shù) 1862721 個。
base_model.trainable = True fine_tune_at = 100 for layer in base_model.layers[:fine_tune_at]: layer.trainable = False print("%d trainable variables "%len(model.trainable_variables)) model.summary()
結(jié)果如下:
56 trainable variables
Model: "model_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_8 (InputLayer) [(None, 160, 160, 3)] 0
sequential_2 (Sequential) (None, 160, 160, 3) 0
tf.math.truediv_3 (TFOpLamb (None, 160, 160, 3) 0
da)
tf.math.subtract_3 (TFOpLam (None, 160, 160, 3) 0
bda)
mobilenetv2_1.00_160 (Funct (None, 5, 5, 1280) 2257984
ional)
global_average_pooling2d_3 (None, 1280) 0
(GlobalAveragePooling2D)
dropout_2 (Dropout) (None, 1280) 0
dense_2 (Dense) (None, 1) 1281
=================================================================
Total params: 2,259,265
Trainable params: 1,862,721
Non-trainable params: 396,544
(4)之前我們對基礎模型訓練了 10 個 epoch ,現(xiàn)在我們進行微調(diào)過程中再對模型訓練 10 個 epoch ,且我們將從上面訓練結(jié)束的 epoch 開始恢復并進行現(xiàn)在的訓練過程。
(5)這里我們改用 RMSprop 優(yōu)化器,且因為此時我們是要對整體模型進行微調(diào),所以設置的學習率比之前降低 10 倍,否則如果較大會很快產(chǎn)生過擬合。
fine_tune_epochs = 10 total_epochs = initial_epochs + fine_tune_epochs model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer = tf.keras.optimizers.RMSprop(learning_rate=lr/10), metrics=['accuracy']) history_fine = model.fit(train_dataset, epochs=total_epochs, initial_epoch=history.epoch[-1], validation_data=validation_dataset)
訓練結(jié)果輸出:
Epoch 10/20
63/63 [==============================] - 39s 561ms/step - loss: 0.1073 - accuracy: 0.9570 - val_loss: 0.1017 - val_accuracy: 0.9592
Epoch 11/20
63/63 [==============================] - 34s 538ms/step - loss: 0.0688 - accuracy: 0.9725 - val_loss: 0.0448 - val_accuracy: 0.9827
...
Epoch 19/20
63/63 [==============================] - 34s 537ms/step - loss: 0.0244 - accuracy: 0.9900 - val_loss: 0.0709 - val_accuracy: 0.9777
Epoch 20/20
63/63 [==============================] - 33s 528ms/step - loss: 0.0220 - accuracy: 0.9905 - val_loss: 0.0566 - val_accuracy: 0.9851
(6)使用測試數(shù)據(jù)對模型進行評估。
model.evaluate(test_dataset)
結(jié)果輸出:
loss: 0.0544 - accuracy: 0.9792
之前模型最后的驗證準確率為 0.9740 ,測試準確率為 0.9765 ,而經(jīng)過微調(diào)的模型,最后餓驗證準確率為 0.9851 ,測試準確率為 0.9792 ,都有所提升。
5. 預測
我們隨機挑選一個 batch 進行預測,并將圖片與預測標簽進行顯示,結(jié)果表明預測全都正確。
image_batch, label_batch = test_dataset.as_numpy_iterator().next() predictions = model.predict_on_batch(image_batch).flatten() predictions = tf.nn.sigmoid(predictions) predictions = tf.where(predictions < 0.5, 0, 1) plt.figure(figsize=(10, 10)) for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(image_batch[i].astype("uint8")) plt.title(class_names[predictions[i]]) plt.axis("off")
以上就是深度學習Tensorflow 2.4 完成遷移學習和模型微調(diào)的詳細內(nèi)容,更多關于Tensorflow 遷移學習模型微調(diào)的資料請關注腳本之家其它相關文章!
- 深度學習Tensorflow2.8?使用?BERT?進行文本分類
- pytorch_pretrained_bert如何將tensorflow模型轉(zhuǎn)化為pytorch模型
- 使用TensorFlow創(chuàng)建生成式對抗網(wǎng)絡GAN案例
- javascript命名約定(變量?函數(shù)?類?組件)
- Tensorflow2.4從頭訓練Word?Embedding實現(xiàn)文本分類
- Tensorflow 2.4 搭建單層和多層 Bi-LSTM 模型
- 深度學習Tensorflow2.8實現(xiàn)GRU文本生成任務詳解
- 深度學習TextRNN的tensorflow1.14實現(xiàn)示例
- tensorflow2.10使用BERT實現(xiàn)Semantic Similarity過程解析
相關文章
Python利用PyPDF2庫實現(xiàn)輕松提取PDF文本
ython中的PyPDF2庫是一個非常有用的工具,無論您是需要分析PDF文檔中的內(nèi)容還是需要在文檔中搜索特定的信息,PyPDF2都可以幫助您輕松實現(xiàn)這些任務,下面我們就來學習一下如何利用PyPDF2提取PDF文本吧2023-09-09使用Pandas進行Excel數(shù)據(jù)處理的操作和技巧
在數(shù)據(jù)處理和分析的過程中,Excel是一個非常常見的工具,然而,當數(shù)據(jù)量變大,操作復雜度增加時,Excel的效率和功能可能無法滿足需求,Pandas是一個強大的Python數(shù)據(jù)處理庫,本文將介紹如何使用Pandas進行Excel數(shù)據(jù)處理,并展示一些常見的操作和技巧2023-11-11Python報錯TypeError: unsupported operand的問題分析和解決方法
TypeError: unsupported operand 是Python中常見的一類錯誤,通常在嘗試對不兼容的數(shù)據(jù)類型進行操作時發(fā)生,比如,當你嘗試對字符串和整數(shù)進行加法操作時,Python會拋出這一錯誤,所以本文給大家介紹了Python報錯TypeError: unsupported operand的問題解決2024-09-09教你怎么用python實現(xiàn)字符串轉(zhuǎn)日期
今天教各位小伙伴怎么用python實現(xiàn)字符串轉(zhuǎn)日期,文中有非常詳細的代碼示例,對正在學習python的小伙伴很有幫助,需要的朋友可以參考下2021-05-05Python快速實現(xiàn)分列轉(zhuǎn)到行的示例代碼
這篇文章主要為大家詳細介紹了如何利用Python快速實現(xiàn)分列轉(zhuǎn)到行的效果,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學一下2023-03-03