【火爐煉AI】深度學習006-移花接木-用Keras遷移學習提高性能

【火爐煉AI】深度學習006-移花接木-用Keras遷移學習提高性能

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

上一篇文章咱們用本身定義的模型來解決了二分類問題,在20個回合的訓練以後獲得了大約74%的準確率,一方面是咱們的epoch過小的緣由,另一方面也是因爲模型太簡單,結構簡單,故而不能作太複雜的事情,那麼怎麼提高預測的準確率了?一個有效的方法就是遷移學習。git

遷移學習其本質就是移花接木:將其餘大數據集(好比ImageNet等)上獲得的網絡結構及其weights應用到一個新的項目中來,好比此處的貓狗二分類問題。固然,ImageNet中確定有貓和狗這兩個類別,能夠說此處的小數據集是ImageNet的一個子集,可是,對於和ImageNet徹底沒有任何關係的其餘數據集,遷移學習也有必定的效果,固然,對於兩個數據集的相關性比較差的數據集,使用遷移學習可能效果不太好。github

具體作法是:使用一個成熟的網絡結構(好比此處用VGG16)和參數,把它的全鏈接層所有都去掉,只保留卷積層,這些卷積層能夠當作是圖片的特徵提取器(獲得的特徵被稱爲bottleneck features),而全鏈接層是分類器,對這些圖片的特徵進行有效分類。對於新項目,咱們要分類的類別數目並非ImageNet的1000類,而是好比此處的2類。故而分類器對咱們毫無用處,咱們須要建立和訓練本身的分類器。以下爲VGG16網絡的結構:網絡

其中的Conv block 1-5 都是卷積層和池化層,組成了圖片的特徵提取器,然後面的Flatten和Dense組成了分類器。app

此處咱們將Conv block 1-5的結構和參數都移接過來,在組裝上本身的分類器便可。性能

在訓練時,咱們能夠先我上一篇博文同樣,創建圖片數據流,將圖片數據流導入到VGG16模型中提取特徵,而後將這些特徵送入到自定義的分類器中訓練,優化自定義分類器的參數,可是這種方式訓練速度很慢,此處咱們用VGG16的卷積層統一提取全部圖片的特徵,將這些特徵保存,而後直接加載特徵來訓練,加載數字比加載圖片要快的多,故而訓練也快得多。學習

我這篇博文主要參考了:keras系列︱圖像多分類訓練與利用bottleneck features進行微調(三),這篇博文也是參考的Building powerful image classification models using very little data,但我發現這兩篇博文有不少地方的代碼跑不起來,主要緣由多是Keras或Tensorflow升級形成的,因此我作了一些必要的修改。測試


1. 準備數據集

首先使用預訓練好的模型VGG16來提取train set和test set圖片的特徵,而後將這些特徵保存,這些特徵實際上就是numpy.ndarray,故而能夠保存爲數字,而後加載這些數字來訓練。大數據

# 此處的訓練集和測試集並非原始圖片的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=None, 
        shuffle=False) # 必須爲False,不然順序打亂以後,和後面的label對應不上。
    bottleneck_features_train = model.predict_generator(
        generator, train_samples_num // batch_size) # 若是是32,這個除法獲得的是62,拋棄了小數,故而獲得1984個sample
    np.save('E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/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=None,
        shuffle=False)
    bottleneck_features_validation = model.predict_generator(
        generator, val_samples_num // batch_size)
    np.save('E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/bottleneck_features_val.npy',bottleneck_features_validation)
    print('bottleneck features of test set is saved.')
複製代碼

通過上面的代碼,trainset圖片集的特徵被保存到E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/bottleneck_features_train.npy文件中,而test set的特徵也被保存到../bottleneck_features_val.npy中。優化


2. 構建模型並訓練

很顯然,此處咱們並不要提取圖片的各類特徵,前面的VGG16已經幫咱們作完了這件事,因此咱們只須要對這些特徵進行分類便可,因此至關於咱們只創建一個分類器模型就能夠。

用keras創建一個簡單的二分類模型,以下:

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(1, activation='sigmoid')) # 此處定義的模型只有後面的全鏈接層,因爲是本項目特殊的,故而須要自定義

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

    return model
複製代碼

模型雖然創建好了,但咱們要訓練裏面的參數,使用剛剛VGG16提取的特徵來進行訓練:

# 只須要訓練分類器模型便可,不須要訓練特徵提取器
train_data = np.load('E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/bottleneck_features_train.npy') # 加載訓練圖片集的全部圖片的VGG16-notop特徵
train_labels = np.array(
    [0] * int((train_samples_num / 2)) + [1] * int((train_samples_num / 2)))
# label是1000個cat,1000個dog,因爲此處VGG16特徵提取時是按照順序,故而[0]表示cat,1表示dog

validation_data = np.load('E:\PyProjects\DataSet\FireAI\DeepLearning\FireAI006/bottleneck_features_val.npy')
validation_labels = np.array(
    [0] * int((val_samples_num / 2)) + [1] * int((val_samples_num / 2)))

# 構建分類器模型
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 2000 samples, validate on 800 samples Epoch 1/20 2000/2000 [==============================] - 6s 3ms/step - loss: 0.8426 - acc: 0.7455 - val_loss: 0.4280 - val_acc: 0.8063 Epoch 2/20 2000/2000 [==============================] - 5s 3ms/step - loss: 0.3928 - acc: 0.8365 - val_loss: 0.3078 - val_acc: 0.8675 Epoch 3/20 2000/2000 [==============================] - 5s 3ms/step - loss: 0.3144 - acc: 0.8720 - val_loss: 0.4106 - val_acc: 0.8588

.......

Epoch 18/20 2000/2000 [==============================] - 5s 3ms/step - loss: 0.0479 - acc: 0.9830 - val_loss: 0.5380 - val_acc: 0.9025 Epoch 19/20 2000/2000 [==============================] - 5s 3ms/step - loss: 0.0600 - acc: 0.9775 - val_loss: 0.5357 - val_acc: 0.8988 Epoch 20/20 2000/2000 [==============================] - 5s 3ms/step - loss: 0.0551 - acc: 0.9810 - val_loss: 0.6057 - val_acc: 0.8825

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

將訓練過程當中的loss和acc繪圖以下:

很顯然,在第5個epoch以後,train set和test set出現了很明顯的分離,代表後面出現了比較強烈的過擬合,可是在test set上的準確率仍然有90%左右。

能夠看出,相對上一篇文章咱們本身定義的三層卷積層+兩層全鏈接層的網絡結構,用VGG16網絡結構的方法獲得的準確率更高一些,並且訓練所須要的時間也更少。

注意一點:此處咱們並無訓練VGG16中的任何參數,而僅僅訓練本身定義的分類器模型中的參數。

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

1,遷移學習就是使用已經存在的模型及其參數,使用該模型來提取圖片的特徵,而後構建本身的分類器,對這些特徵進行分類便可。

2,此處咱們並無訓練已存在模型的結構和參數,僅僅是訓練自定義的分類器,若是要訓練已存在模型的參數,那就是微調(Fine-tune)的範疇了

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


注:本部分代碼已經所有上傳到(個人github)上,歡迎下載。

相關文章
相關標籤/搜索