Tensorflow學習筆記No.8

VioletOrz發表於2020-10-24

使用VGG16網路進行遷移學習

使用在ImageNet資料上預訓練的VGG16網路模型對貓狗資料集進行分類識別。

1.預訓練網路

預訓練網路是一個儲存好的,已經在大型資料集上訓練好的卷積神經網路。

如果這個資料集足夠大且通用,那麼預訓練網路學習到的模型引數可以有效的對圖片進行特徵提取。即使新問題與原本的資料完全不同,但學習到的特徵提取方法依然可以在不同的問題之間進行移植,進而可以在全新的資料集上提取到有效的特徵。對這些有效的高階特徵進行分類可以大大提高模型分類的準確率

遷移學習主要適用於已有資料相對較少的情況,如果擁有的資料量足夠大,即使不需要遷移學習也能夠得到非常高的準確率。

2.如何使用與訓練網路

2.1載入影像並建立資料集

首先,讀入貓狗資料集中的圖片。(實現過程的詳細說明在Tensorflow學習筆記No.5中,這裡不再贅述)

 

 1 import tensorflow as tf
 2 import numpy as np
 3 import pandas as pd
 4 import matplotlib.pyplot as plt
 5 %matplotlib inline
 6 import pathlib
 7 import random
 8 
 9 data_root = pathlib.Path('../input/cat-and-dog/training_set/training_set')
10 
11 all_image_path = list(data_root.glob('*/*.jpg'))
12 random.shuffle(all_image_path)
13 image_count = len(all_image_path)
14 
15 label_name = sorted([item.name for item in data_root.glob('*')])
16 name_to_indx = dict((name, indx) for indx, name in enumerate(label_name))
17 
18 all_image_path = [str(path) for path in all_image_path]
19 all_image_label = [name_to_indx[pathlib.Path(p).parent.name] for p in all_image_path]
20 
21 def load_pregrosess_image(path, label):
22     image = tf.io.read_file(path)
23     image = tf.image.decode_jpeg(image, channels = 3)
24     image = tf.image.resize(image, [256, 256])
25     image = tf.cast(image, tf.float32)
26     image = image / 255
27     return image, label
28 
29 train_image_ds = tf.data.Dataset.from_tensor_slices((all_image_path, all_image_label))
30 
31 AUTOTUNE = tf.data.experimental.AUTOTUNE
32 dataset = train_image_ds.map(load_pregrosess_image, num_parallel_calls = AUTOTUNE)
33 
34 BATCHSIZE = 16
35 train_count = int(image_count * 0.8)
36 test_count = image_count - train_count
37 
38 train_dataset = dataset.take(train_count)
39 test_dataset = dataset.skip(train_count)
40 
41 train_dataset = train_dataset.shuffle(train_count).repeat().batch(BATCHSIZE)
42 test_dataset = test_dataset.repeat().batch(BATCHSIZE)

 

2.2載入與訓練網路並構建網路模型

與訓練的網路由兩個部分構成,訓練好的卷積基和訓練好的分類器。我們需要使用訓練好的卷積基來提取特徵,並使用自定義的分類器對自己的資料集進行分類識別。

如下圖所示:

 

 

訓練過程中,我們僅僅對自定義的分類器進行訓練,而不訓練預訓練好的卷積基部分。

 

預訓練的卷積基可以非常好的提取影像的某些特徵,在訓練過程中,由於分類器是一個全新的沒有訓練過的分類器,在訓練初期會產生很大的loss值,由於資料量較少,如果不對預訓練的卷積基進行凍結(不更新引數)處理,產生的loss值經梯度傳遞會對預訓練的卷積基造成非常大的影響,且由於可訓練資料較少兒難以恢復,所以只對自定義的分類器進行訓練,而不訓練卷積基。

 

 首先從tf.keras.applications中建立一個預訓練VGG16的卷積基。

1 cov_base = tf.keras.applications.VGG16(weights = 'imagenet', include_top = False)

weight是我們要使用的模型權重,我們使用經imagenet訓練過的模型的權重資訊進行遷移學習。

include_top是指,是否使用預訓練的分類器。在遷移學習過程中我們使用自定義的分類器,所以引數為False。

然後我們對建立好的卷積基進行凍結處理,凍結所有的可訓練引數。

1 cov_base.trainable = False

使用keras.Sequential()建立網路模型。

1 model = tf.keras.Sequential()
2 model.add(cov_base)
3 model.add(tf.keras.layers.GlobalAveragePooling2D())
4 model.add(tf.keras.layers.Dense(512, activation = 'relu'))
5 model.add(tf.keras.layers.Dense(1, activation = 'sigmoid'))

在模型中加入卷積基和自定義的分類器。

模型結構如下圖所示:

我們得到了一個可訓練引數僅為263,169的預訓練VGG16網路模型。

2.3使用自定義資料訓練分類器

此時模型已經搭建完畢,我們使用之前處理好的資料對它進行訓練。

 1 model.compile(optimizer = 'adam',
 2               loss = 'binary_crossentropy',
 3               metrics = ['acc']
 4              )
 5 
 6 history = model.fit(train_dataset,
 7                     steps_per_epoch = train_count // BATCHSIZE,
 8                     epochs = 10,
 9                     validation_data = test_dataset,
10                     validation_steps = test_count // BATCHSIZE
11                    )
12 
13 plt.plot(history.epoch, history.history.get('acc'), label = 'acc')
14 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')

訓練結果如下圖所示:

模型在訓練集和測試機上的正確率均達到了94%左右,而且僅僅經過了10個epoch就達到了這樣的效果,足以看出遷移學習在小規模資料上的優勢。

3.微調

雖然使用預訓練網路可以輕易的達到94%左右的正確率,但是,如果我們還想繼續提高這個正確率該怎樣進行調整呢?

所謂微調,是凍結卷積基底部的卷積層,共同訓練新新增的分類器和卷積基頂部的部分卷積層。

根據卷積神經網路提取特徵的原理我們不難發現,越底層的卷積層提取到的影像特徵越抽象越細小,而頂層的卷積層提取到的特徵更大,更加的接近我們能直接觀察到的資料特徵,由於我們需要訓練的資料和預訓練時使用的資料不盡相同,所以越頂層的卷積層提取到的特徵與我們所需要的特徵差別越大所以,我們只凍結底部的卷積層,將頂部的卷積層與訓練好的分類器共同訓練,會得到更好的擬合效果。

只有分類器以及訓練好了,才能微調卷積基的頂部卷積層,否則由於訓練初期的誤差很大,會將卷積層之前學習到的引數破壞掉。

所以我們對卷積基進行解凍,並只對底部的卷積進行凍結。

1 cov_base.trainable = True
2 for layers in cov_base.layers[:-3]:
3     layers.trainable = False

然後將模型繼續進行訓練。

 1 model.compile(optimizer = tf.keras.optimizers.Adam(lr = 0.0001),
 2               loss = 'binary_crossentropy',
 3               metrics = ['acc']
 4              )
 5 
 6 history = model.fit(train_dataset,
 7                     steps_per_epoch = train_count // BATCHSIZE,
 8                     epochs = 20,
 9                     initial_epoch = 10,
10                     validation_data = test_dataset,
11                     validation_steps = test_count // BATCHSIZE
12                    )
13 
14 plt.plot(history.epoch, history.history.get('acc'), label = 'acc')
15 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')

注意將學習率調小,以便儘可能的達到loss的極小值點。

得到的結果如下圖所示:

模型再訓練集上達到了近乎100%的準確率,在測試集上也達到了96%左右準確率,微調的效果還是較為明顯的。

 

那麼關於遷移學習的介紹到這裡就結束了o(* ̄▽ ̄*)o,後續會更新更多內容。

 

相關文章