【火爐煉AI】深度學習009-用Keras遷移學習提升效能(多分類問題)

煉丹老頑童發表於2019-03-02

【火爐煉AI】深度學習009-用Keras遷移學習提升效能(多分類問題)

(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, Keras 2.1.6, Tensorflow 1.9.0)

本文是仿照前面的文章【火爐煉AI】深度學習006-移花接木-用Keras遷移學習提升效能,原文是針對二分類問題,使用遷移學習的方式來提升準確率,此處我用遷移學習的方式來提升多分類問題的準確率。

同時,在我前面的文章中【火爐煉AI】深度學習008-Keras解決多分類問題,使用普通的小型3層卷積網路+2層全連線層實現了多分類的85%左右的準確率, 此處我將用遷移學習的方式來進一步提升效能。


1. 準備資料集

與上一篇文章相同。


2. 模型的構建和訓練

使用遷移學習包括兩個步驟:第一步是用VGG16網路提取bottleneck features,然後儲存這些特徵,第二步建立自己的分類器,然後用用儲存的特徵來訓練分類器,優化weights。下面是第一步的程式碼,只注意一個地方:要修改為:class_mode='categorical'

# 此處的訓練集和測試集並不是原始圖片的train set和test set,而是用VGG16對圖片提取的特徵,這些特徵組成新的train set和test set
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras import applications
def save_bottlebeck_features():
    datagen = ImageDataGenerator(rescale=1. / 255) # 不需圖片增強

    # build the VGG16 network
    model = applications.VGG16(include_top=False, weights='imagenet') 
    # 使用imagenet的weights作為VGG16的初始weights,由於只是特徵提取,故而只取前面的卷積層而不需要DenseLayer,故而include_top=False

    generator = datagen.flow_from_directory( # 產生train set
        train_data_dir,
        target_size=(IMG_W, IMG_H),
        batch_size=batch_size,
        class_mode='categorical', # 這個地方要修改,要不然出錯
        shuffle=False) # 必須為False,否則順序打亂之後,和後面的label對應不上。
    bottleneck_features_train = model.predict_generator(
        generator, train_samples_num // batch_size) 
    np.save(os.path.join(save_folder,'bottleneck_features_train.npy'), bottleneck_features_train)
    print('bottleneck features of train set is saved.')

    generator = datagen.flow_from_directory(
        val_data_dir,
        target_size=(IMG_W, IMG_H),
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)
    bottleneck_features_validation = model.predict_generator(
        generator, val_samples_num // batch_size)
    np.save(os.path.join(save_folder,'bottleneck_features_val.npy'),bottleneck_features_validation)
    print('bottleneck features of test set is saved.')
複製程式碼

第二步: 定義自己的分類器,專門適用於本專案的多分類問題,要將最後的Dense層修改為:model.add(Dense(class_num, activation='softmax')),並且loss使用'categorical_crossentropy'。

def my_model():
    '''
    自定義一個模型,該模型僅僅相當於一個分類器,只包含有全連線層,對提取的特徵進行分類即可
    :return:
    '''
    # 模型的結構
    model = Sequential()
    model.add(Flatten(input_shape=train_data.shape[1:])) # 將所有data進行flatten
    model.add(Dense(256, activation='relu')) # 256個全連線單元
    model.add(Dropout(0.5)) # dropout正則
    model.add(Dense(class_num, activation='softmax')) # 與二分類不同之處:要用Dense(class_num)和softmax

    # 模型的配置
    model.compile(optimizer='rmsprop',
                  loss='categorical_crossentropy',
                  metrics=['accuracy']) # model的optimizer等

    return model
複製程式碼

然後使用儲存的特徵對該分類器進行訓練,訓練時有一個地方要注意:在二分類問題時,我們直接將labels定義為np.array([0]*80+[1]*80),但是多分類問題,需要用to_categorical進行轉換,然後再放入到fit中。

from keras.utils import to_categorical
# 只需要訓練分類器模型即可,不需要訓練特徵提取器
train_data = np.load(os.path.join(save_folder,'bottleneck_features_train.npy')) # 載入訓練圖片集的所有圖片的VGG16-notop特徵
train_labels = np.array([0] * 80 + [1] * 80+ [2]*80+[3]*80+[4]*80)
# label是每個類別80張圖片,共5個類別
# 設定標籤,並規範成Keras預設格式
train_labels = to_categorical(train_labels, class_num)

validation_data = np.load(os.path.join(save_folder,'bottleneck_features_val.npy'))
validation_labels = np.array([0] * 20 + [1] * 20+ [2]*20+[3]*20+[4]*20)
validation_labels = to_categorical(validation_labels, class_num)

# 構建分類器模型
clf_model=my_model()
history_ft = clf_model.fit(train_data, train_labels,
              epochs=epochs,
              batch_size=batch_size,
              validation_data=(validation_data, validation_labels))
複製程式碼

-------------------------------------輸---------出--------------------------------

Train on 400 samples, validate on 100 samples Epoch 1/50 400/400 [==============================] - 1s 2ms/step - loss: 4.8496 - acc: 0.4725 - val_loss: 3.4864 - val_acc: 0.6900 Epoch 2/50 400/400 [==============================] - 0s 878us/step - loss: 3.4436 - acc: 0.7400 - val_loss: 3.3577 - val_acc: 0.7400 Epoch 3/50 400/400 [==============================] - 0s 845us/step - loss: 0.6014 - acc: 0.9300 - val_loss: 0.1798 - val_acc: 0.9600

...

Epoch 48/50 400/400 [==============================] - 0s 860us/step - loss: 5.4962e-06 - acc: 1.0000 - val_loss: 0.3184 - val_acc: 0.9500 Epoch 49/50 400/400 [==============================] - 0s 853us/step - loss: 0.0438 - acc: 0.9850 - val_loss: 0.2470 - val_acc: 0.9600 Epoch 50/50 400/400 [==============================] - 0s 855us/step - loss: 5.5508e-04 - acc: 1.0000 - val_loss: 0.2798 - val_acc: 0.9700

--------------------------------------------完-------------------------------------

最後將acc和loss繪圖,如下:

【火爐煉AI】深度學習009-用Keras遷移學習提升效能(多分類問題)

可以看出最後的test acc達到了0.96以上。

########################小**********結###############################

1,多分類的遷移學習問題需要注意幾個地方:提取bottleneck features時要設定class_mode='categorical'。構建自己的分類器時要把輸出層的Dense修改,然後把啟用函式和損失函式修改為多分類對應的函式。在做labels時,要先用to_categorical轉變為one-hot形式再輸入到模型中進行fit.

#################################################################


注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。

相關文章