更新時間:2023-06-07 來源:黑馬程序員 瀏覽量:
如何在只有6萬張圖像的MNIST訓(xùn)練數(shù)據(jù)集上訓(xùn)練模型。學(xué)術(shù)界當下使用最廣泛的大規(guī)模圖像數(shù)據(jù)集ImageNet,它有超過1,000萬的圖像和1,000類的物體。然而,我們平常接觸到數(shù)據(jù)集的規(guī)模通常在這兩者之間。假設(shè)我們想從圖像中識別出不同種類的椅子,然后將購買鏈接推薦給用戶。一種可能的方法是先找出100種常見的椅子,為每種椅子拍攝1,000張不同角度的圖像,然后在收集到的圖像數(shù)據(jù)集上訓(xùn)練一個分類模型。另外一種解決辦法是應(yīng)用遷移學(xué)習(transfer learning),將從源數(shù)據(jù)集學(xué)到的知識遷移到目標數(shù)據(jù)集上。例如,雖然ImageNet數(shù)據(jù)集的圖像大多跟椅子無關(guān),但在該數(shù)據(jù)集上訓(xùn)練的模型可以抽取較通用的圖像特征,從而能夠幫助識別邊緣、紋理、形狀和物體組成等。這些類似的特征對于識別椅子也可能同樣有效。
微調(diào)由以下4步構(gòu)成。
1.在源數(shù)據(jù)集(如ImageNet數(shù)據(jù)集)上預(yù)訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)模型,即源模型。
2.創(chuàng)建一個新的神經(jīng)網(wǎng)絡(luò)模型,即目標模型。它復(fù)制了源模型上除了輸出層外的所有模型設(shè)計及其參數(shù)。我們假設(shè)這些模型參數(shù)包含了源數(shù)據(jù)集上學(xué)習到的知識,且這些知識同樣適用于目標數(shù)據(jù)集。我們還假設(shè)源模型的輸出層跟源數(shù)據(jù)集的標簽緊密相關(guān),因此在目標模型中不予采用。
3.為目標模型添加一個輸出大小為目標數(shù)據(jù)集類別個數(shù)的輸出層,并隨機初始化該層的模型參數(shù)。
4.在目標數(shù)據(jù)集(如椅子數(shù)據(jù)集)上訓(xùn)練目標模型。我們將從頭訓(xùn)練輸出層,而其余層的參數(shù)都是基于源模型的參數(shù)微調(diào)得到的。
當目標數(shù)據(jù)集遠小于源數(shù)據(jù)集時,微調(diào)有助于提升模型的泛化能力。
接下來我們利用微調(diào)模型來實踐一個具體的例子:熱狗識別。將基于一個小數(shù)據(jù)集對在ImageNet數(shù)據(jù)集上訓(xùn)練好的ResNet模型進行微調(diào)。該小數(shù)據(jù)集含有數(shù)千張熱狗或者其他事物的圖像。我們將使用微調(diào)得到的模型來識別一張圖像中是否包含熱狗。
首先,導(dǎo)入實驗所需的工具包。
import tensorflow as tf import numpy as np
我們首先將數(shù)據(jù)集放在路徑hotdog/data之下:
每個類別文件夾里面是圖像文件。
上一節(jié)中我們介紹了ImageDataGenerator進行圖像增強,我們可以通過以下方法讀取圖像文件,該方法以文件夾路徑為參數(shù),生成經(jīng)過圖像增強后的結(jié)果,并產(chǎn)生batch數(shù)據(jù):
flow_from_directory(self, directory, target_size=(256, 256), color_mode='rgb', classes=None, class_mode='categorical', batch_size=32, shuffle=True, seed=None, save_to_dir=None)
主要參數(shù):
directory: 目標文件夾路徑,對于每一個類對應(yīng)一個子文件夾,該子文件夾中任何JPG、PNG、BNP、PPM的圖片都可以讀取。
target_size: 默認為(256, 256),圖像將被resize成該尺寸。
batch_size: batch數(shù)據(jù)的大小,默認32。
shuffle: 是否打亂數(shù)據(jù),默認為True。
我們創(chuàng)建兩個tf.keras.preprocessing.image.ImageDataGenerator實例來分別讀取訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集中的所有圖像文件。將訓(xùn)練集圖片全部處理為高和寬均為224像素的輸入。此外,我們對RGB(紅、綠、藍)三個顏色通道的數(shù)值做標準化。
# 獲取數(shù)據(jù)集 import pathlib train_dir = 'transferdata/train' test_dir = 'transferdata/test' # 獲取訓(xùn)練集數(shù)據(jù) train_dir = pathlib.Path(train_dir) train_count = len(list(train_dir.glob('*/*.jpg'))) # 獲取測試集數(shù)據(jù) test_dir = pathlib.Path(test_dir) test_count = len(list(test_dir.glob('*/*.jpg'))) # 創(chuàng)建imageDataGenerator進行圖像處理 image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255) # 設(shè)置參數(shù) BATCH_SIZE = 32 IMG_HEIGHT = 224 IMG_WIDTH = 224 # 獲取訓(xùn)練數(shù)據(jù) train_data_gen = image_generator.flow_from_directory(directory=str(train_dir), batch_size=BATCH_SIZE, target_size=(IMG_HEIGHT, IMG_WIDTH), shuffle=True) # 獲取測試數(shù)據(jù) test_data_gen = image_generator.flow_from_directory(directory=str(test_dir), batch_size=BATCH_SIZE, target_size=(IMG_HEIGHT, IMG_WIDTH), shuffle=True)
下面我們隨機取1個batch的圖片然后繪制出來。
import matplotlib.pyplot as plt # 顯示圖像 def show_batch(image_batch, label_batch): plt.figure(figsize=(10,10)) for n in range(15): ax = plt.subplot(5,5,n+1) plt.imshow(image_batch[n]) plt.axis('off') # 隨機選擇一個batch的圖像 image_batch, label_batch = next(train_data_gen) # 圖像顯示 show_batch(image_batch, label_batch)
我們使用在ImageNet數(shù)據(jù)集上預(yù)訓(xùn)練的ResNet-50作為源模型。這里指定weights='imagenet'來自動下載并加載預(yù)訓(xùn)練的模型參數(shù)。在第一次使用時需要聯(lián)網(wǎng)下載模型參數(shù)。
Keras應(yīng)用程序(keras.applications)是具有預(yù)先訓(xùn)練權(quán)值的固定架構(gòu),該類封裝了很多重量級的網(wǎng)絡(luò)架構(gòu),如下圖所示:
實現(xiàn)時實例化模型架構(gòu):
tf.keras.applications.ResNet50( include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000, **kwargs)
主要參數(shù):
include_top: 是否包括頂層的全連接層。
weights: None 代表隨機初始化, 'imagenet' 代表加載在 ImageNet 上預(yù)訓(xùn)練的權(quán)值。
input_shape: 可選,輸入尺寸元組,僅當 include_top=False 時有效,否則輸入形狀必須是 (224, 224, 3)(channels_last 格式)或 (3, 224, 224)(channels_first 格式)。它必須為 3 個輸入通道,且寬高必須不小于 32,比如 (200, 200, 3) 是一個合法的輸入尺寸。
在該案例中我們使用resNet50預(yù)訓(xùn)練模型構(gòu)建模型:
# 加載預(yù)訓(xùn)練模型 ResNet50 = tf.keras.applications.ResNet50(weights='imagenet', input_shape=(224,224,3)) # 設(shè)置所有層不可訓(xùn)練 for layer in ResNet50.layers: layer.trainable = False # 設(shè)置模型 net = tf.keras.models.Sequential() # 預(yù)訓(xùn)練模型 net.add(ResNet50) # 展開 net.add(tf.keras.layers.Flatten()) # 二分類的全連接層 net.add(tf.keras.layers.Dense(2, activation='softmax'))
接下來我們使用之前定義好的ImageGenerator將訓(xùn)練集圖片送入ResNet50進行訓(xùn)練。
# 模型編譯:指定優(yōu)化器,損失函數(shù)和評價指標 net.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 模型訓(xùn)練:指定數(shù)據(jù),每一個epoch中只運行10個迭代,指定驗證數(shù)據(jù)集 history = net.fit( train_data_gen, steps_per_epoch=10, epochs=3, validation_data=test_data_gen, validation_steps=10 )
Epoch 1/3 10/10 [==============================] - 28s 3s/step - loss: 0.6931 - accuracy: 0.5031 - val_loss: 0.6930 - val_accuracy: 0.5094 Epoch 2/3 10/10 [==============================] - 29s 3s/step - loss: 0.6932 - accuracy: 0.5094 - val_loss: 0.6935 - val_accuracy: 0.4812 Epoch 3/3 10/10 [==============================] - 31s 3s/step - loss: 0.6935 - accuracy: 0.4844 - val_loss: 0.6933 - val_accuracy: 0.4875