使用預訓練網絡(遷移學習)
預訓練網絡是一個保存好的之前已在大型數據集(大規模圖像分類任務)上訓練好的卷積神經網絡
如果這個原始數據集足夠大且足夠通用,那么預訓練網絡學到的特征的空間層次結構可以作為有效的提取視覺世界特征的模型。
即使新問題和新任務與原始任務完全不同學習到的特征在不同問題之間是可移植的,這也是深度學習與淺層學習方法的一個重要優勢。它使得深度學習對於小數據問題非常的有效。
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有兩個很大的缺點
-
- 網絡架構weight數量相當大,很消耗磁盤空間。
-
- 訓練非常慢
由於其全連接節點的數量較多,再加上網絡比較深,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)