深度學習-Tensorflow2.2-預訓練網路{7}-遷移學習基礎針對小資料集-19

gemoumou發表於2020-11-21

使用預訓練網路(遷移學習)

預訓練網路是一個儲存好的之前已在大型資料集(大規模影像分類任務)上訓練好的卷積神經網路

如果這個原始資料集足夠大且足夠通用,那麼預訓練網路學到的特徵的空間層次結構可以作為有效的提取視覺世界特徵的模型。

即使新問題和新任務與原始任務完全不同學習到的特徵在不同問題之間是可移植的,這也是深度學習與淺層學習方法的一個重要優勢。它使得深度學習對於小資料問題非常的有效。

Keras內建預訓練網路Keras庫中包含VGG16、VGG19\ResNet50、Inception v3、Xception等經典的模型架構。

  • ImageNet

    • ImageNet是一個手動標註好類別的圖片資料庫(為了機器視
      覺研究),目前已有22,000個類別。

    • 當我們在深度學習和卷積神經網路的背景下聽到“ImageNet”一詞時,我們可能會提到ImageNet視覺識別比賽,稱為ILSVRC。

    • 這個圖片分類比賽是訓練一個模型,能夠將輸入圖片正確分
      類到1000個類別中的某個類別。訓練集120萬,驗證集5萬,測試集10萬。

    • 這1,000個圖片類別是我們在日常生活中遇到的,例如狗,貓,各種家居物品,車輛型別等等。

    • 在影像分類方面,ImageNet比賽準確率已經作為計算機視覺分類演算法的基準。自2012年以來,卷積神經網路和深度學習技術主導了這一比賽的排行榜。

  • VGG16與VGG19

    • 在2014年,VGG模型架構由Simonyan和Zisserman提出,在“極深的大規模影像識別卷積網路”(Very Deep Convolutional Networks for Large Scale Image Recognition)這篇論文中有介紹

    • VGG模型結構簡單有效,前幾層僅使用3×3卷積核來增加網路深度,通過max pooling(最大池化)依次減少每層的神經元數量,最後三層分別是2個有4096個神經元的全連線層和一個輸出softmax層。
      在這裡插入圖片描述
      在這裡插入圖片描述

  • VGG有兩個很大的缺點

      1. 網路架構weight數量相當大,很消耗磁碟空間。
      1. 訓練非常慢
        由於其全連線節點的數量較多,再加上網路比較深,VGG16有533MB+,VGG19有574MB。這使得部署VGG比較耗時。
        在這裡插入圖片描述
  • 微調

    • 所謂微調:凍結模型庫的底部的卷積層,共同訓練新新增的分類器層和頂部部分卷積層。這允許我們“微調”基礎模型中的高階特徵表示,以使它們與特定任務更相關。。

    • 只有分類器已經訓練好了,才能微調卷積基的頂部卷積層。如果有沒有這樣的話,剛開始的訓練誤差很大,微調之前這些卷積層學到的表示會被破壞掉

  • 微調步驟

    • 一、在預訓練卷積基上新增自定義層
    • 二、凍結卷積基所有層
    • 三、訓練新增的分類層
    • 四、解凍卷積基的一部分層
    • 五、聯合訓練解凍的卷積層和新增的自定義層

案例

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os
gpu_ok = tf.test.is_gpu_available()
print("tf version:", tf.__version__)
print("use GPU", gpu_ok) # 判斷是否使用gpu進行訓練

在這裡插入圖片描述

# 定義好keras,layers方便後面呼叫
keras = tf.keras
layers = tf.keras.layers
train_image_path = glob.glob("./dc/maogou/*.jpg")
test_image_path = glob.glob("./dc/maogou_test/*.jpg") # 獲取圖片資料集
len(train_image_path),len(test_image_path)

在這裡插入圖片描述

train_image_path[995:1005],test_image_path[593:600]

在這裡插入圖片描述

# 定義目標值  如果是貓 = 1    是狗 = 0
train_image_label = [int(p.split("\\")[1].split(".")[0]=="cat") for p in train_image_path]
test_image_label = [int(p.split("\\")[1].split(".")[0]=="cat") for p in test_image_path]
train_image_label[995:1005],test_image_label[593:600]

在這裡插入圖片描述

# 圖片載入與預處理
def load_preprosess_image(path,lable):
    image = tf.io.read_file(path) # 讀取圖片路徑
    image = tf.image.decode_jpeg(image,channels=3) # 對圖片進行解碼(jpeg格式圖片,channels=3 將讀入圖片統一為三通道)
    image = tf.image.resize(image,[256,256])# 對圖片進行變形360*360畫素(非裁剪)
    image = tf.cast(image,tf.float32) # 改變圖片格式
    image = image/255 # 對圖片進行歸一化
    #lable = tf.reshape(lable,[1]) # 把目標值轉換成2維形狀  如:[1,2,3] =>[[1],[2],[3]]
    return image,lable
# 建立資料集  (包含了 路徑 以及目標值)
train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))
AUTOTUNE = tf.data.experimental.AUTOTUNE   # AUTOTUNE 根據計算機cpu個數自動進行計算
train_image_ds = train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE) # 對所有圖片以及目標值進行計算 使用AUTOTUNE 根據計算機cpu個數自動進行計算
test_image_ds = test_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
train_image_ds ,test_image_ds# 檢視處理後圖片的格式

在這裡插入圖片描述

for img,label in train_image_ds.take(2):
    plt.imshow(img)

在這裡插入圖片描述

for img,label in test_image_ds.take(600):
    plt.imshow(img)

在這裡插入圖片描述

BATCH_SIZE = 64 # 中文為批,一個batch由若干條資料構成。batch是進行網路優化的基本單位,網路引數的每一輪優化需要使用一個batch。
# batch中的樣本是被並行處理的。與單個樣本相比,一個batch的資料能更好的模擬資料集的分佈,
#batch越大則對輸入資料分佈模擬的越好,反應在網路訓練上,則體現為能讓網路訓練的方向“更加正確”。
#但另一方面,一個batch也只能讓網路的引數更新一次,因此網路引數的迭代會較慢。
#在測試網路的時候,應該在條件的允許的範圍內儘量使用更大的batch,這樣計算效率會更高。
train_count = len(train_image_path) # 檢視資料的個數
test_count = len(test_image_path)
train_count,test_count

在這裡插入圖片描述

train_image_ds = train_image_ds.shuffle(train_count).repeat().batch(BATCH_SIZE) # 亂序
#train_image_ds = train_image_ds.prefetch(AUTOTUNE) #  prefetch 該函式有一個後臺執行緒和一個內部快取區,在資料被請求前,
# 就從 dataset 中預載入一些資料(進一步提高效能)  prefetch(AUTOTUNE) 根據計算機效能自動分配資料條數

test_image_ds = test_image_ds.repeat().batch(BATCH_SIZE) 
#test_image_ds = test_image_ds.prefetch(AUTOTUNE) 

使用keras內建經典網路實現

# weights="imagenet"使用預訓練權重,include_top= False不包含全連結層
covn_base = keras.applications.VGG16(weights="imagenet",include_top= False) 
covn_base.summary()

在這裡插入圖片描述

# 新增全連線層
model = keras.Sequential() # 建立一個順序模型
model.add(covn_base) # 新增vgg16卷積機
model.add(layers.GlobalAveragePooling2D()) # 全域性平均池化,深度神經網路中經常使用的一個層,使用前後的
#尺寸分別為[B,H,W,C]->[B,C].特別要注意,這個層使用之後特徵圖尺寸的維度變成了2維而非4維。
model.add(layers.Dense(512,activation="relu")) # 輸出512個單元
model.add(layers.Dense(1,activation="sigmoid")) # 輸出1個單元,sigmoid概率值
model.summary() # Trainable params: 14,977,857

在這裡插入圖片描述

covn_base.trainable = False # 因covn_base已經訓練好了所以我們設定false不可訓練(凍結)
model.summary() # 設定了covn_base.trainable = False後再次檢視模型建構Trainable params: 263,169

在這裡插入圖片描述

# 編譯模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.001),
             loss="binary_crossentropy",
             metrics=["acc"])
# 訓練模型
history = model.fit(
    train_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    epochs=12,
    validation_data=test_image_ds,
    validation_steps=test_count//BATCH_SIZE)

在這裡插入圖片描述
微調

covn_base.trainable = True  # 解凍所有卷積層
len(covn_base.layers) # 一共19個層
fine_tune_at = -3 # 定義倒數3層
for layers in covn_base.layers[:fine_tune_at]:
    layers.trainable = False # 除了倒數3層其他層全部凍結
# 編譯模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.0005/10), # 使用更低學習速率
             loss="binary_crossentropy",
             metrics=["acc"])
# 訓練模型
initial_epochs = 12 # 前面訓練了12次
fine_tune_epochs = 10  # 對新的資料訓練10次
total_epochs = initial_epochs+fine_tune_epochs # 總共次數

history = model.fit(
    train_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    epochs=total_epochs, # 總共需要訓練的次數
    
    initial_epoch=initial_epochs, # 初始化時已經訓練過多少次
    validation_data=test_image_ds,
    validation_steps=test_count//BATCH_SIZE)

在這裡插入圖片描述

常見預訓練網路

  • 可用的模型
    在 ImageNet 上預訓練過的用於影像分類的模型:
    VGG16 VGG19 ResNet50
    InceptionV3 InceptionResNetV2 Xception
    MobileNet MobileNetV2
    DenseNet NASNet

  • 模型概覽

  • 在這裡插入圖片描述

  • Xception

使用例項:
tf.keras.applications.xception.Xception(
include_top=True, weights='imagenet', input_tensor=None, 
input_shape=None, pooling=None, classes=1000
)

在 ImageNet 上預訓練的 Xception V1 模型,在 ImageNet 上,該模型取得了驗證集 top1 0.790 和 top5 0.945 的準確率。

注意該模型只支援 channels_last 的維度順序(高度、寬度、通道)。

模型預設輸入尺寸是 299x299。

  • Xception重要引數
    pooling: 可選,

當 include_top 為 False 時,該引數指定了特徵提取時的池化方式。

None 代表不池化,直接輸出最後一層卷積層的輸出,該輸出是一個 4D 張量。

‘avg’ 代表全域性平均池化(GlobalAveragePooling2D),相當於在最後一層卷積層後面再加一層全域性平均池化層,輸出是一個 2D 張量。

‘max’ 代表全域性最大池化。

其他訓練網路引數引數新增連結描述

案例

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os
os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true'
gpu_ok = tf.test.is_gpu_available()
print("tf version:", tf.__version__)
print("use GPU", gpu_ok) # 判斷是否使用gpu進行訓練

在這裡插入圖片描述

keras = tf.keras
layers = tf.keras.layers
train_image_path = glob.glob("./dc/maogou/*.jpg")
test_image_path = glob.glob("./dc/maogou_test/*.jpg") # 獲取圖片資料集
len(train_image_path),len(test_image_path)

在這裡插入圖片描述

train_image_path[995:1005],test_image_path[593:600]

在這裡插入圖片描述

# 定義目標值  如果是貓 = 1    是狗 = 0
train_image_label = [int(p.split("\\")[1].split(".")[0]=="cat") for p in train_image_path]
test_image_label = [int(p.split("\\")[1].split(".")[0]=="cat") for p in test_image_path]
train_image_label[995:1005],test_image_label[593:600]

在這裡插入圖片描述

# 圖片載入與預處理
def load_preprosess_image(path,lable):
    image = tf.io.read_file(path) # 讀取圖片路徑
    image = tf.image.decode_jpeg(image,channels=3) # 對圖片進行解碼(jpeg格式圖片,channels=3 將讀入圖片統一為三通道)
    image = tf.image.resize(image,[256,256])# 對圖片進行變形360*360畫素(非裁剪)
    image = tf.cast(image,tf.float32) # 改變圖片格式
    image = image/255 # 對圖片進行歸一化
    #lable = tf.reshape(lable,[1]) # 把目標值轉換成2維形狀  如:[1,2,3] =>[[1],[2],[3]]
    return image,lable
# 建立資料集  (包含了 路徑 以及目標值)
train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))
AUTOTUNE = tf.data.experimental.AUTOTUNE   # AUTOTUNE 根據計算機cpu個數自動進行計算
train_image_ds = train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE) # 對所有圖片以及目標值進行計算 使用AUTOTUNE 根據計算機cpu個數自動進行計算
test_image_ds = test_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
train_image_ds ,test_image_ds# 檢視處理後圖片的格式

在這裡插入圖片描述
在這裡插入圖片描述

BATCH_SIZE = 64 # 中文為批,一個batch由若干條資料構成。batch是進行網路優化的基本單位,網路引數的每一輪優化需要使用一個batch。
# batch中的樣本是被並行處理的。與單個樣本相比,一個batch的資料能更好的模擬資料集的分佈,
#batch越大則對輸入資料分佈模擬的越好,反應在網路訓練上,則體現為能讓網路訓練的方向“更加正確”。
#但另一方面,一個batch也只能讓網路的引數更新一次,因此網路引數的迭代會較慢。
#在測試網路的時候,應該在條件的允許的範圍內儘量使用更大的batch,這樣計算效率會更高。
train_count = len(train_image_path) # 檢視資料的個數
test_count = len(test_image_path)
train_image_ds = train_image_ds.shuffle(train_count).repeat().batch(BATCH_SIZE) # 亂序
#train_image_ds = train_image_ds.prefetch(AUTOTUNE) #  prefetch 該函式有一個後臺執行緒和一個內部快取區,在資料被請求前,
# 就從 dataset 中預載入一些資料(進一步提高效能)  prefetch(AUTOTUNE) 根據計算機效能自動分配資料條數

test_image_ds = test_image_ds.repeat().batch(BATCH_SIZE) 
#test_image_ds = test_image_ds.prefetch(AUTOTUNE) 
# 建立卷積機   weights="imagenet"使用預訓練權重,include_top= False不包含全連結層
covn_base = keras.applications.xception.Xception(weights="imagenet",
                                                 include_top= False,
                                                 input_shape=(256,256,3),
                                                 pooling="avg") 
covn_base.trainable = False # 因covn_base已經訓練好了所以我們設定false不可訓練(凍結)
covn_base.summary()

在這裡插入圖片描述

# 新增全連線層
model = keras.Sequential() # 建立一個順序模型
model.add(covn_base) # 新增vgg16卷積機
model.add(layers.Dense(512,activation="relu")) # 輸出512個單元
model.add(layers.Dense(1,activation="sigmoid")) # 輸出1個單元,sigmoid概率值
model.summary() # 

在這裡插入圖片描述

# 編譯模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.001),
             loss="binary_crossentropy",
             metrics=["acc"])
initial_epochs = 5
# 訓練模型
history = model.fit(
    train_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    epochs=initial_epochs,
    validation_data=test_image_ds,
    validation_steps=test_count//BATCH_SIZE)

在這裡插入圖片描述
微調

covn_base.trainable = True  # 解凍所有卷積層
len(covn_base.layers) # 總層數

在這裡插入圖片描述

fine_tune_at = -33 # 定義倒數33層
for layers in covn_base.layers[:fine_tune_at]:
    layers.trainable = False # 除了倒數33層其他層全部凍結
# 編譯模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.0005/10), # 使用更低學習速率
             loss="binary_crossentropy",
             metrics=["acc"])
# 訓練模型
fine_tune_epochs = 5  # 微調後再次訓練5次
total_epochs = initial_epochs+fine_tune_epochs # 總共次數

history = model.fit(
    train_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    epochs=total_epochs, # 總共需要訓練的次數
    
    initial_epoch=initial_epochs, # 初始化時已經訓練過多少次
    validation_data=test_image_ds,
    validation_steps=test_count//BATCH_SIZE)

在這裡插入圖片描述

相關文章