2019年1月20日 星期日

深度學習 - Kaggle - Dog & Cat - 使用預訓練的卷積神經網絡

深度學習 - Kaggle - Dog & Cat - 使用預訓練的卷積神經網絡

在cat & dog 的學習過程,我們只使用了較少的數據集,所以得到的精度不比在大型數據庫訓練的精準度。
然而在處理較小型數據問題時,使用預先訓練好的模型,其在大規模數據學習到的特徵在不同問題之間的可移植性,是深度學習與許多早期淺層學習方法相比的重要優勢,它使得深度學習對小數據問題也非常有效。

使用預訓練網絡有兩種方法:特徵提取(feature extraction)微調模型(fine-tuning)


特徵提取(feature extraction)

對於卷積神經網絡而言,特徵提取就是取出之前訓練好的網絡的卷積基(trained convolution base),在上面運行新數據,然後在輸出上面訓練一個新的分類器(new classifier)。
簡而言之,就是將輸出層刪掉,保留卷積層,當作特徵提取機,將經過卷積基後提取的特徵再訓練分類成我們要處理的問題。

我們練習使用VGG16預訓練模型的卷積基
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(150, 150, 3))

weights指定模型初始化的權重檢查點。
include_top 指定模型最後是否包含密集連接分類器。默認情況下,這個密集連接分類器對應於ImageNet 的1000 個類別。因為我們打算使用自己的密集連接分類器(只有兩個類別:cat 和dog),所以不需要包含它。

可以看看True & False 的差異 :
include_top=True 會包含最後三個密集連接層(黃色框內),其最後會將訓練結果做1000個分類,在這邊,我們只需要分類貓&狗,所以不需要使用到1000個分類,這是相當浪費計算資源的。


接著,我們要將訓練資料透過 conv_base 的 predict 方法來提取特徵。
目的: 將train, validation, test 的資料都用conv_base 過濾出特徵,輸出形狀為(4,4,512) 的array。

因為會使用到3次,將其寫成函數

#使用ImageDataGenerator 將圖片rescale
import numpy as np
from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20

def extract_features(directory, sample_count):
    #建立兩個zero array 存放特徵
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    #利用 Keras 內建的 generator 將生成數據輸入至 conv_base
    generator = datagen.flow_from_directory(directory,
                                        target_size=(150, 150),
                                        batch_size=batch_size,
                                        class_mode='binary')
    #跑個for 迴圈,批次將 conv_base 的輸出存進 features,labels array。
    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


#將資料丟進函式中,得到輸出特徵的數據
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)


#將數據reshape ,才能連結至我們自訂的密集連接層作訓練。
train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))


#建立模型網絡
from keras import layers
from keras import models
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'))


#選擇優化器RMSprop、損失函數binary_crossentropy、指標acc
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
              loss='binary_crossentropy',
              metrics=['acc'])


#將訓練&驗證資料丟進fit_generator訓練
history = model.fit(train_features, train_labels,
                    epochs=30,
                    batch_size=20,
                    validation_data=(validation_features, validation_labels))

scores = model.evaluate(test_features,test_labels, steps=50)
print(scores)


#將訓練過程畫出來
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()




在測試數據上的[loss,acc]
[0.26085495948791504, 0.8880000114440918]

利用預訓練模型特徵提取,連接至自定義的全連接層訓練後,提高精度到達了88%
後續再練習微調模型(fine-tuning)

沒有留言:

張貼留言