fbpx

Keras 教學 - 透過預先訓練神經網路特徵萃取法快速訓練高準確度模型

預先訓練神經網路的特徵萃取法

我們知道 CNN 卷積層的訓練需要很多數據與運算資源,在資料集不足的情況下,從頭訓練卷積層不是一個聰明的方法。何不直接採用大神訓練好的卷積層,加速 AI Time to Market。這樣的方法稱為預先訓練神經網路的特徵萃取法 (Feature extraction with a pertrained network),由於在 CNN 架構中訓練 Convolutional Layer 成本是很高的,太少的樣本不足以訓練出具有特徵擷取意義的網路。Convolutional Layer 如下:

Convolutional Base

Convolutional 主要用來擷取特徵,在不同的業務需求下,前端對於影響的特徵擷取其實是可以共用的。所以我們想站在巨人的肩膀上,直接使用一些典型網路已經訓練好的 Convolutional Base 來擷取特徵,透過特徵重新訓練自己的 Full Connection 分類網路,加速產品 Time To Market 的時間。

什麼是 CNN 特徵?

卷積神經網路是一種深度學習模型,其中包含了許多卷積層和池化層。這些層的目的是對輸入數據進行過濾和降維,以提取出有用的特徵。這些提取出來的特徵稱為 CNN 特徵。CNN 特徵通常用來解決圖像識別和分類任務,因為它們能夠很好地捕捉圖像中的細節和結構信息。例如,在臉部識別任務中,CNN 特徵可能會捕捉到眼睛、鼻子和嘴巴的位置和形狀等信息,以此來識別不同的人臉。在使用 CNN 特徵時,通常會先對輸入數據進行預處理,例如對圖像進行縮放和正規化,然后再輸入到卷積神經網路中進行訓練。訓練完成后,可以提取出網路中的 CNN 特徵,並將它們用作其他模型的輸入,例如分類器或識別器等等。

目前已經有許多不同的特徵萃取法可以用於 CNN 訓練,例如:

  • 卷積層:卷積層將對輸入數據進行卷積運算,並使用過濾器來提取特徵。卷積層通常會結合多個過濾器,以提取出不同的特徵。
  • 池化層:池化層會對輸入數據進行下采樣,以縮小數據的維度並提取出重要的特徵。
  • 全連接層:全連接層會對輸入數據進行線性轉換,以提取出高維度數據中的有用特徵。

在 CNN 訓練過程中,通常會使用多個卷積層和池化層來進行特徵萃取,然後再使用全連接層進行分類或識別。通常情況下,較深的網路能夠更好地捕捉較細節的特徵,但也會需要更多的訓練數據和更長的訓練時間。

由於卷積網路可以學習影像特徵,越接近 Input 的 Layer 所能理解的特徵越「普適」而且抽象,像是紋理、顏色等等。越後面的 Layer 所學習到的特徵越接近物體的描述,像是耳朵、眼睛等等。目前已經有很多已經訓練好的 CNN Model 可以使用,Keras 也內建許多常用的模型,接下來我們透過常用的 VGG16 Model 實現對 Full Connection Layer 重新訓練來提昇辨識效果。

載入 Keras VGG16 模型進行重新訓練

我們先透過 keras.applications 載入 VGG16 網路並且顯示網路資訊,如下:

# 載入 VGG 網路 Convolutional Base
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(150, 150, 3))
conv_base.summary()

VGG16 Network

透過已經訓練好的卷積網路來萃取特徵,這裡的程式將圖片影像直接送入卷積網路,將計算出的數值儲存起來,後續會用來訓練分類網路。範例程式如下:

def extract_features(directory, sample_count):
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    generator = datagen.flow_from_directory(
        directory,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode='binary')
    i = 0
    for inputs_batch, labels_batch in generator:
        features_batch = conv_base.predict(inputs_batch)
        features[i * batch_size : (i + 1) * batch_size] = features_batch
        labels[i * batch_size : (i + 1) * batch_size] = labels_batch
        i += 1
        if i * batch_size >= sample_count:
            break
    return features, labels

上面的程式有一個重點,就是「features_batch = conv_base.predict(inputs_batch)」表示要訓練的圖片先透過既有的 CNN 抽取特徵。

接下來開始透過擷取好的特徵重新訓練分類網路,這裡是用特徵來訓練而不是圖片喔,如下:

from keras import models
from keras import layers
from keras import optimizers

model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
              loss='binary_crossentropy',
              metrics=['acc'])

history = model.fit(train_features, train_labels,
                    epochs=30,
                    batch_size=20,
                    validation_data=(validation_features, validation_labels))

最後驗證一下正確率:

透過這樣的方法可以獲得將約 0.9 的正確性,效果比前一篇介紹的資料擴增法又好上一點。如果合併兩種方法 (資料擴充 + 特增萃取),成本會高一些,但是可以提升到 0.95 的正確性。這裡附上完整的 Python CoLab 預先訓練神經網路的特徵萃取法範例,同步提供 GitHub File「Keras 利用預先訓練神經網路特徵萃取法訓練貓狗分類器」。

Keras 除了 VGG 以外還有提供更多典型網路,像是:DenseNet, NASNet, InceptionResNetV2, ResNet 50, Inception v3, Xception, VGG16, VGG19, MobileNet, MobileNet V2 等等,有興趣的程序猿可以玩看看,依據我之前的測試,根據不同的場景不同的 Model 都會有不同的表現。今天先介紹到這裡,下次見.......