1. 程式人生 > >深度學習:預訓練

深度學習:預訓練

遷移學習:可以使我們在他人訓練過的模型基礎上進行小改動便可投入使用。

1. 什麼是遷移學習?

神經網路需要用資料來訓練,它從資料中獲得資訊,進而把它們轉換成相應的權重。這些權重能夠被提取出來,遷移到其他的神經網路中,我們“遷移”了這些學來的特徵,就不需要從零開始訓練一個神經網路了 。針對訓練資料集小,防止過擬合使用。通常在計算機視覺imagenet 進行預訓練。

2. 什麼是預訓練模型?

簡單來說,預訓練模型(pre-trained model)是前人為了解決類似問題所創造出來的模型。你在解決問題的時候,不用從零開始訓練一個新模型,可以從在類似問題中訓練過的模型入手。

比如說,如果你想做一輛自動駕駛汽車,可以花數年時間從零開始構建一個性能優良的影象識別演算法,也可以從Google在ImageNet資料集上訓練得到的inception model(一個預訓練模型)起步,來識別影象。

一個預訓練模型可能對於你的應用中並不是100%的準確對口,但是它可以為你節省大量功夫。

3.怎樣使用預訓練模型?

當在訓練經網路的時候我們的目標是什麼?我們希望網路能夠在多次正向反向迭代的過程中,找到合適的權重。

通過使用之前在大資料集上經過訓練的預訓練模型,我們可以直接使用相應的結構和權重,將它們應用到我們正在面對的問題上。這被稱作是“遷移學習”,即將預訓練的模型“遷移”到我們正在應對的特定問題中。

ImageNet資料集已經被廣泛用作訓練集,因為它規模足夠大(包括120萬張圖片),有助於訓練普適模型。ImageNet的訓練目標,是將所有的圖片正確地劃分到1000個分類條目下。這1000個分類基本上都來源於我們的日常生活,比如說貓貓狗狗的種類,各種家庭用品,日常通勤工具等等。

在遷移學習中,這些預訓練的網路對於ImageNet資料集外的圖片也表現出了很好的泛化效能。

既然預訓練模型已經訓練得很好,我們就不會在短時間內去修改過多的權重,在遷移學習中用到它的時候,往往只是進行微調(fine tune)

在修改模型的過程中,我們通過會採用比一般訓練模型更低的學習速率。

5. 微調模型的方法

特徵提取

我們可以將預訓練模型當做特徵提取裝置來使用。具體的做法是,將輸出層去掉,然後將剩下的整個網路當做一個固定的特徵提取機,從而應用到新的資料集中。

採用預訓練模型的結構

我們還可以採用預訓練模型的結構,但先將所有的權重隨機化,然後依據自己的資料集進行訓練。

訓練特定層,凍結其他層

另一種使用預訓練模型的方法是對它進行部分的訓練。具體的做法是,將模型起始的一些層的權重保持不變,重新訓練後面的層,得到新的權重。在這個過程中,我們可以多次進行嘗試,從而能夠依據結果找到frozen layers和retrain layers之間的最佳搭配。

如何使用與訓練模型,是由資料集大小和新舊資料集(預訓練的資料集和我們要解決的資料集)之間資料的相似度來決定的。

下圖表展示了在各種情況下應該如何使用預訓練模型:

場景一:資料集小,資料相似度高(與pre-trained model的訓練資料相比而言)

在這種情況下,因為資料與預訓練模型的訓練資料相似度很高,因此我們不需要重新訓練模型。我們只需要將輸出層改制成符合問題情境下的結構就好。

我們使用預處理模型作為模式提取器。

比如說我們使用在ImageNet上訓練的模型來辨認一組新照片中的小貓小狗。在這裡,需要被辨認的圖片與ImageNet庫中的圖片類似,但是我們的輸出結果中只需要兩項——貓或者狗。

在這個例子中,我們需要做的就是把dense layer和最終softmax layer的輸出從1000個類別改為2個類別。

場景二:資料集小,資料相似度不高

在這種情況下,我們可以凍結預訓練模型中的前k個層中的權重,然後重新訓練後面的n-k個層,當然最後一層也需要根據相應的輸出格式來進行修改。

因為資料的相似度不高,重新訓練的過程就變得非常關鍵。而新資料集大小的不足,則是通過凍結預訓練模型的前k層進行彌補。

場景三:資料集大,資料相似度不高

在這種情況下,因為我們有一個很大的資料集,所以神經網路的訓練過程將會比較有效率。然而,因為實際資料與預訓練模型的訓練資料之間存在很大差異,採用預訓練模型將不會是一種高效的方式。

因此最好的方法還是將預處理模型中的權重全都初始化後在新資料集的基礎上重頭開始訓練。

場景四:資料集大,資料相似度高

這就是最理想的情況,採用預訓練模型會變得非常高效。最好的運用方式是保持模型原有的結構和初始權重不變,隨後在新資料集的基礎上重新訓練。

5. 在手寫數字識別中使用預訓練模型

現在,讓我們嘗試來用預訓練模型去解決一個簡單的問題。

我曾經使用vgg16作為預訓練的模型結構,並把它應用到手寫數字識別上。

讓我們先來看看這個問題對應著之前四種場景中的哪一種。我們的訓練集(MNIST)有大約60,000張左右的手寫數字圖片,這樣的資料集顯然是偏小的。所以這個問題應該屬於場景一或場景二。

我們可以嘗試把兩種對應的方法都用一下,看看最終的效果。

只重新訓練輸出層 & dense layer

這裡我們採用vgg16作為特徵提取器。隨後這些特徵,會被傳遞到依據我們資料集訓練的dense layer上。輸出層同樣由與我們問題相對應的softmax層函式所取代。

在vgg16中,輸出層是一個擁有1000個類別的softmax層。我們把這層去掉,換上一層只有10個類別的softmax層。我們只訓練這些層,然後就進行數字識別的嘗試。


# importing required librariesfrom keras.models import Sequentialfrom scipy.misc import imread
get_ipython().magic('matplotlib inline')import matplotlib.pyplot as pltimport numpy as npimport kerasfrom keras.layers import Denseimport pandas as pdfrom 

keras.applications.vgg16 import VGG16from keras.preprocessing import imagefrom 
keras.applications.vgg16 import preprocess_inputimport numpy as npfrom 
keras.applications.vgg16 import decode_predictions
train=pd.read_csv("R/Data/Train/train.csv")
test=pd.read_csv("R/Data/test.csv")
train_path="R/Data/Train/Images/train/"
test_path="R/Data/Train/Images/test/"from scipy.misc import imresize# preparing the train 

datasettrain_img=[]for i in range(len(train)):

    temp_img=image.load_img(train_path+train['filename'][i],target_size=(224,224))

    temp_img=image.img_to_array(temp_img)

    train_img.append(temp_img)#converting train images to array and applying mean subtraction processingtrain_img=np.array(train_img) 

train_img=preprocess_input(train_img)# applying the same procedure with the test datasettest_img=[]for i in range(len(test)):

    temp_img=image.load_img(test_path+test['filename'][i],target_size=(224,224))

    temp_img=image.img_to_array(temp_img)

    test_img.append(temp_img)

test_img=np.array(test_img) 
test_img=preprocess_input(test_img)# loading VGG16 model weightsmodel = VGG16(weights='imagenet', include_top=False)# Extracting features from the train dataset using the VGG16 pre-trained modelfeatures_train=model.predict(train_img)# Extracting features from the train dataset using the VGG16 pre-trained modelfeatures_test=model.predict(test_img)# flattening the layers to conform to MLP inputtrain_x=features_train.reshape(49000,25088)# converting target variable to arraytrain_y=np.asarray(train['label'])# performing one-hot encoding for the target variabletrain_y=pd.get_dummies(train_y)
train_y=np.array(train_y)# creating training and validation setfrom sklearn.model_selection import train_test_split
X_train, X_valid, Y_train, Y_valid=train_test_split(train_x,train_y,test_size=0.3, random_state=42)# creating a mlp modelfrom keras.layers import Dense, Activation
model=Sequential()

model.add(Dense(1000, input_dim=25088, activation='relu',kernel_initializer='uniform'))
keras.layers.core.Dropout(0.3, noise_shape=None, seed=None)

model.add(Dense(500,input_dim=1000,activation='sigmoid'))
keras.layers.core.Dropout(0.4, noise_shape=None, seed=None)

model.add(Dense(150,input_dim=500,activation='sigmoid'))
keras.layers.core.Dropout(0.2, noise_shape=None, seed=None)

model.add(Dense(units=10))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy', optimizer="adam", metrics=['accuracy'])# fitting the model model.fit(X_train, Y_train, epochs=20, batch_size=128,validation_data=(X_valid,Y_valid))

凍結最初幾層網路的權重

這裡我們將會把vgg16網路的前8層進行凍結,然後對後面的網路重新進行訓練。這麼做是因為最初的幾層網路捕獲的是曲線、邊緣這種普遍的特徵,這跟我們的問題是相關的。我們想要保證這些權重不變,讓網路在學習過程中重點關注這個資料集特有的一些特徵,從而對後面的網路進行調整。

from keras.models import Sequentialfrom scipy.misc import imread
get_ipython().magic('matplotlib inline')import matplotlib.pyplot as pltimport numpy as npimport kerasfrom keras.layers import Denseimport pandas as pdfrom keras.applications.vgg16 import VGG16from keras.preprocessing import imagefrom keras.applications.vgg16 import preprocess_inputimport numpy as npfrom keras.applications.vgg16 import decode_predictionsfrom keras.utils.np_utils import to_categoricalfrom sklearn.preprocessing import LabelEncoderfrom keras.models import Sequentialfrom keras.optimizers import SGDfrom keras.layers import Input, Dense, Convolution2D, MaxPooling2D, AveragePooling2D, ZeroPadding2D, Dropout, Flatten, merge, Reshape, Activationfrom sklearn.metrics import log_loss

train=pd.read_csv("R/Data/Train/train.csv")
test=pd.read_csv("R/Data/test.csv")
train_path="R/Data/Train/Images/train/"test_path="R/Data/Train/Images/test/"from scipy.misc import imresize

train_img=[]for i in range(len(train)):

    temp_img=image.load_img(train_path+train['filename'][i],target_size=(224,224))

    temp_img=image.img_to_array(temp_img)

    train_img.append(temp_img)

train_img=np.array(train_img) 
train_img=preprocess_input(train_img)

test_img=[]for i in range(len(test)):

temp_img=image.load_img(test_path+test['filename'][i],target_size=(224,224))

    temp_img=image.img_to_array(temp_img)

    test_img.append(temp_img)

test_img=np.array(test_img) 
test_img=preprocess_input(test_img)from keras.models import Modeldef vgg16_model(img_rows, img_cols, channel=1, num_classes=None):

    model = VGG16(weights='imagenet', include_top=True)

    model.layers.pop()

    model.outputs = [model.layers[-1].output]

    model.layers[-1].outbound_nodes = []

          x=Dense(num_classes, activation='softmax')(model.output)

    model=Model(model.input,x)#To set the first 8 layers to non-trainable (weights will not be updated)

          for layer in model.layers[:8]:

       layer.trainable = False# Learning rate is changed to 0.001
    sgd = SGD(lr=1e-3, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])    return model

train_y=np.asarray(train['label'])

le = LabelEncoder()

train_y = le.fit_transform(train_y)

train_y=to_categorical(train_y)

train_y=np.array(train_y)from sklearn.model_selection import train_test_split
X_train, X_valid, Y_train, Y_valid=train_test_split(train_img,train_y,test_size=0.2, random_state=42)# Example to fine-tune on 3000 samples from Cifar10img_rows, img_cols = 224, 224 # Resolution of inputschannel = 3num_classes = 10 batch_size = 16 nb_epoch = 10# Load our modelmodel = vgg16_model(img_rows, img_cols, channel, num_classes)

model.summary()# Start Fine-tuningmodel.fit(X_train, Y_train,batch_size=batch_size,epochs=nb_epoch,shuffle=True,verbose=1,validation_data=(X_valid, Y_valid))# Make predictionspredictions_valid = model.predict(X_valid, batch_size=batch_size, verbose=1)# Cross-entropy loss scorescore = log_loss(Y_valid, predictions_valid)

相關資源

原文: https://www.analyticsvidhya.com/blog/2017/06/transfer-learning-the-art-of-fine-tuning-a-pre-trained-model/

VGG-16: https://gist.github.com/baraldilorenzo/07d7802847aaad0a35d3

Keras庫中的ImageNet預訓練模型: https://keras.io/applications/

手寫數字資料集MNIST: http://yann.lecun.com/exdb/mnist/