一文看懂遷移學習:怎樣用預訓練模型搞定深度學習? ——重用神經網絡的結構

這裏寫圖片描述

這裏寫圖片描述

以上示例都是人類的遷移學習的能力。html

遷移學習是什麼?node

所謂遷移學習,或者領域適應Domain Adaptation,通常就是要將從源領域(Source Domain)學習到的東西應用到目標領域(Target Domain)上去。源領域和目標領域之間每每有gap/domain discrepancy(源領域的數據和目標領域的數據遵循不一樣的分佈)。python

遷移學習可以將適用於大數據的模型遷移到小數據上,實現個性化遷移。git

遷移什麼,怎麼遷移,何時能遷移,這是遷移學習要解決的主要問題。github

遷移學習能解決那些問題?算法

小數據的問題。比方說新開一個網店,賣一種新的糕點,沒有任何的數據,就沒法創建模型對用戶進行推薦。但用戶買一個東西會反映到用戶可能還會買另一個東西,因此若是知道用戶在另一個領域,比方說賣飲料,已經有了不少不少的數據,利用這些數據建一個模型,結合用戶買飲料的習慣和買糕點的習慣的關聯,就能夠把飲料的推薦模型給成功地遷移到糕點的領域,這樣,在數據很少的狀況下能夠成功推薦一些用戶可能喜歡的糕點。這個例子就說明,有兩個領域,一個領域已經有不少的數據,能成功地建一個模型,有一個領域數據很少,可是和前面那個領域是關聯的,就能夠把那個模型給遷移過來。
個性化的問題。好比每一個人都但願本身的手機可以記住一些習慣,這樣不用每次都去設定它,怎麼才能讓手機記住這一點呢?其實能夠經過遷移學習把一個通用的用戶使用手機的模型遷移到個性化的數據上面。網絡

遷移學習四種實現方法
這裏寫圖片描述app

1. 樣本遷移 Instance-based Transfer Learningdom

這裏寫圖片描述

通常是對樣本進行加權,給比較重要的樣本較大的權重。
樣本遷移即在數據集(源領域)中找到與目標領域類似的數據,把這個數據放大多倍,與目標領域的數據進行匹配。其特色是:須要對不一樣例子加權;須要用數據進行訓練。iphone

2. 特徵遷移 Feature-based Transfer Learning

這裏寫圖片描述

在特徵空間進行遷移,通常須要把源領域和目標領域的特徵投影到同一個特徵空間裏進行。

特徵遷移是經過觀察源領域圖像與目標域圖像之間的共同特徵,而後利用觀察所得的共同特徵在不一樣層級的特徵間進行自動遷移。

3. 模型遷移 Model-based Transfer Learning

這裏寫圖片描述

整個模型應用到目標領域去,好比目前經常使用的對預訓練好的深度網絡作微調,也能夠叫作參數遷移。

模型遷移利用上千萬的圖象訓練一個圖象識別的系統,當咱們遇到一個新的圖象領域,就不用再去找幾千萬個圖象來訓練了,能夠原來的圖像識別系統遷移到新的領域,因此在新的領域只用幾萬張圖片一樣可以獲取相同的效果。模型遷移的一個好處是能夠和深度學習結合起來,咱們能夠區分不一樣層次可遷移的度,類似度比較高的那些層次他們被遷移的可能性就大一些

4. 關係遷移 Relational Transfer Learning

社會網絡,社交網絡之間的遷移。

這裏寫圖片描述

前沿的遷移學習方向

Reinforcement Transfer Learning
怎麼遷移智能體學習到的知識:好比我學會了一個遊戲,那麼我在另外一個類似的遊戲裏面也是能夠應用一些相似的策略的

Transitive Transfer Learning
傳遞性遷移學習,兩個domain之間若是相隔得太遠,那麼咱們就插入一些intermediate domains,一步步作遷移

Source-Free Transfer Learning
不知道是哪一個源領域

最後用一張圖總結一下深度學習、強化學習、遷移學習的趨勢

這裏寫圖片描述

參考資料:

https://mp.weixin.qq.com/s?__biz=MzAwMjM2Njg2Nw==&mid=2653144126&idx=1&sn=d9633d71ed89590100422c85f6bdb845
http://mp.weixin.qq.com/s?__biz=MzI3MTA0MTk1MA==&mid=2651982064&idx=1&sn=92e65d423db5aa79d8c8c782afc19111&scene=1&srcid=0426Sj6blqQWPuyUb8qCswf3&from=singlemessage&isappinstalled=0#wechat_redirect
http://geek.csdn.net/news/detail/92051
http://www.leiphone.com/news/201612/hF1AX5yNwcxtf005.html
https://zhuanlan.zhihu.com/p/22023097

 

下面就是深度學習模型重用例子。遷移學習!

 

跟傳統的監督式機器學習算法相比,深度神經網絡目前最大的劣勢是什麼?

貴。

尤爲是當咱們在嘗試處理現實生活中諸如圖像識別、聲音辨識等實際問題的時候。一旦你的模型中包含一些隱藏層時,增添多一層隱藏層將會花費巨大的計算資源。

慶幸的是,有一種叫作「遷移學習」的方式,可使咱們在他人訓練過的模型基礎上進行小改動即可投入使用。在這篇文章中,我將會講述如何使用預訓練模型來加速解決問題的過程。

注:這篇文章默認讀者對於神經網絡和深度學習有着必定的瞭解,若是你不瞭解深度學習,那麼我強烈建議你先了解一下深度學習的基礎概念:

深度學習入門者必看:25個你必定要知道的概念

目錄

1.什麼是遷移學習?

2.什麼是預訓練模型?

3.爲何咱們使用預訓練模型?-結合生活實例

4.咱們能夠怎樣運用預訓練模型?

▪提取特徵(extract features)

▪優化模型(fine tune the model)

5.優化模型的方式

6.在數字識別中使用預訓練模型

▪只針對輸出密集層(output dense layer)的從新訓練

▪凍結初始幾層網絡的權重因子

1. 什麼是遷移學習?

爲了對遷移學習產生一個直觀的認識,不妨拿老師與學生之間的關係作類比。

一位老師一般在ta所教授的領域有着多年豐富的經驗,在這些積累的基礎上,老師們可以在課堂上教授給學生們該領域最簡明扼要的內容。這個過程能夠看作是老手與新手之間的「信息遷移」。

這個過程在神經網絡中也適用。

咱們知道,神經網絡須要用數據來訓練,它從數據中得到信息,進而把它們轉換成相應的權重。這些權重可以被提取出來,遷移到其餘的神經網絡中,咱們「遷移」了這些學來的特徵,就不須要從零開始訓練一個神經網絡了 。

如今,讓咱們從自身進化的角度來討論這種遷移學習的重要性。這是Tim Urban最近在waitbutwhy.com上的一篇文章中提出的觀點。

Tim說,在語言發明以前,每一代人類都須要自身從新習得不少知識,這也是知識從上一代到下一代一增加緩慢的緣由。

隨後,咱們發明了語言,這爲知識在世代間的傳遞提供了載體,下圖是在語言發明後,一樣時間尺度下知識增加速度的示意圖。

是否是看起來很牛逼?而經過權重的傳遞來進行遷移學習和人類在世代交替中經過語言傳播知識,是一個道理。

2. 什麼是預訓練模型?

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

好比說,若是你想作一輛自動駕駛汽車,能夠花數年時間從零開始構建一個性能優良的圖像識別算法,也能夠從Google在ImageNet數據集上訓練獲得的inception model(一個預訓練模型)起步,來識別圖像。

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

接下來,我會舉個例子來講明。

3. 爲何咱們要用預訓練模型?

上週我一直在嘗試解決Crowdanalytix platform上的一個問題:從手機圖片中分辨場景。

這是一個圖像分類的問題,訓練數據集中有4591張圖片,測試集中有1200張圖片。咱們的任務是將圖片相應地分到16個類別中。在對圖片進行一些預處理後,我首先採用一個簡單的MLP(Multi-later Perceptron)模型,結構以下圖所示:

在對輸入圖片(224*224*3)平整化後,爲了簡化上述結構,我用了三個各含有500個神經元的隱藏層。在輸出層中,共有16個神經元對應着十六個類別。

我只能將訓練的準確率控制在6.8%,這是個很不理想的結果。我嘗試對隱藏層、隱層中神經元的數量以及drop out速率進行調整,但準確度都沒有太大的提高。而若是增長隱藏層和其中神經元的數量,每一個週期的運行時間則會增長20s以上。(個人開發環境是12GB VRAM,Titan X GPU)

下面是我用上文所述結構的MLP模型訓練輸出的結果。

能夠看出,除非指數級地增長訓練時長,MLP模型沒法提供給我更好的結果。所以,我轉而採用CNN(卷積神經網絡),看看他們在這個數據集上的表現,以及是否可以提升訓練的準確度。

CNN的結構以下:

我使用了3個卷積的模塊,每一個模塊由如下部分組成:

  • 32個5*5的filter

  • 線性整流函數(ReLU)做爲激活函數

  • 4*4的最大值池化層

最後一個卷積模塊輸出的結果通過平整化後會被傳遞到一個擁有64的神經元的隱藏層上,隨後經過一個drop out rate = 0.5處理後傳遞到輸出層。

最終訓練的結果記錄以下:

準確率15.75%,儘管與MLP模型相比有所提高,但每一個週期的運行時間也增長了。

而更重要的是,數據集中最大類別所含圖片數量約佔總數17.6%左右。

只要把全部的圖片都歸到最大的類別,咱們就可以獲得比MLP、CNN訓練出來的模型更好的結果(ノへ ̄、)。

此外,增長更多的卷積模塊也會大大增長訓練時長。

因而,我轉而去採用預訓練模型,這樣我不須要從新訓練個人整個結構,只須要針對其中的幾層進行訓練便可。

所以,我採用了在ImageNet數據集上預先訓練好的VGG16模型,這個模型能夠在Keras庫中找到。

模型的結構以下所示:

在VGG16結構的基礎上,我只將softmax層的1000個輸出改成16個,從而適應咱們這個問題的情景,隨後從新訓練了dense layer。

跟MLP和CNN相比,這個結構的準確率可以達到70%。同時,使用VGG16最大的好處是大大減小了訓練時間,只須要針對dense layer進行訓練,所需時間基本能夠忽略。

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

當在訓練經網絡的時候咱們的目標是什麼?咱們但願網絡可以在屢次正向反向迭代的過程當中,找到合適的權重。

經過使用以前在大數據集上通過訓練的預訓練模型,咱們能夠直接使用相應的結構和權重,將它們應用到咱們正在面對的問題上。這被稱做是「遷移學習」,即將預訓練的模型「遷移」到咱們正在應對的特定問題中。

在選擇預訓練模型的時候你須要很是仔細,若是你的問題與預訓練模型訓練情景下有很大的出入,那麼模型所獲得的預測結果將會很是不許確。

舉例來講,若是把一個本來用於語音識別的模型用來作用戶識別,那結果確定是不理想的。

幸運的是,Keras庫中有許多這類預訓練的結構。

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層進行彌補。

場景三:數據集大,數據類似度不高

在這種狀況下,由於咱們有一個很大的數據集,因此神經網絡的訓練過程將會比較有效率。然而,由於實際數據與預訓練模型的訓練數據之間存在很大差別,採用預訓練模型將不會是一種高效的方式。

所以最好的方法仍是將預處理模型中的權重全都初始化後在新數據集的基礎上重頭開始訓練。

場景四:數據集大,數據類似度高

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

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

如今,讓咱們嘗試來用預訓練模型去解決一個簡單的問題。

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

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

咱們能夠嘗試把兩種對應的方法都用一下,看看最終的效果。

只從新訓練輸出層 & dense layer

這裏咱們採用vgg16做爲特徵提取器。隨後這些特徵,會被傳遞到依據咱們數據集訓練的dense layer上。輸出層一樣由與咱們問題相對應的softmax層函數所取代。

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

# importing required librariesfromkeras.models importSequential fromscipy.miscimportimreadget_ipython().magic( 'matplotlib inline') importmatplotlib.pyplot aspltimportnumpy asnp importkeras fromkeras.layers importDense importpandas aspdfromkeras.applications.vgg16 importVGG16 fromkeras.preprocessing importimagefromkeras.applications.vgg16 importpreprocess_input importnumpy asnpfromkeras.applications.vgg16 importdecode_predictionstrain=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/"fromscipy.miscimportimresize # preparing the train datasettrain_img=[] fori inrange(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=[] fori inrange(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 setfromsklearn.model_selection importtrain_test_splitX_train, X_valid, Y_train, Y_valid=train_test_split(train_x,train_y,test_size= 0.3, random_state= 42) # creating a mlp modelfromkeras.layers importDense, Activationmodel=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 modelmodel.fit(X_train, Y_train, epochs= 20, batch_size= 128,validation_data=(X_valid,Y_valid))

凍結最初幾層網絡的權重

這裏咱們將會把vgg16網絡的前8層進行凍結,而後對後面的網絡從新進行訓練。這麼作是由於最初的幾層網絡捕獲的是曲線、邊緣這種廣泛的特徵,這跟咱們的問題是相關的。咱們想要保證這些權重不變,讓網絡在學習過程當中重點關注這個數據集特有的一些特徵,從而對後面的網絡進行調整。

fromkeras.models importSequential fromscipy.misc importimreadget_ipython().magic('matplotlib inline') importmatplotlib.pyplot asplt importnumpy asnp importkerasfromkeras.layers importDense importpandas aspd fromkeras.applications.vgg16importVGG16 fromkeras.preprocessing importimage fromkeras.applications.vgg16importpreprocess_input importnumpy asnp fromkeras.applications.vgg16importdecode_predictions fromkeras.utils.np_utils importto_categoricalfromsklearn.preprocessing importLabelEncoder fromkeras.models importSequentialfromkeras.optimizers importSGD fromkeras.layers importInput, Dense, Convolution2D, MaxPooling2D, AveragePooling2D, ZeroPadding2D, Dropout, Flatten, merge, Reshape, Activation fromsklearn.metrics importlog_losstrain=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/"fromscipy.miscimportimresizetrain_img=[] fori inrange(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=[] foriinrange(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) fromkeras.models importModeldefvgg16_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)forlayer inmodel.layers[: 8]: layer.trainable =False# Learning rate is changed to 0.001sgd = SGD(lr= 1e-3, decay= 1e-6, momentum=0.9, nesterov= True) model.compile(optimizer=sgd, loss= 'categorical_crossentropy', metrics=[ 'accuracy']) returnmodeltrain_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)fromsklearn.model_selection importtrain_test_splitX_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 = 10batch_size = 16nb_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/

相關文章
相關標籤/搜索