跟傳統(tǒng)的監(jiān)督式機器學習算法相比,深度神經(jīng)網(wǎng)絡目前最大的劣勢是什么? 貴。 尤其是當我們在嘗試處理現(xiàn)實生活中諸如圖像識別、聲音辨識等實際問題的時候。一旦你的模型中包含一些隱藏層時,增添多一層隱藏層將會花費巨大的計算資源。 慶幸的是,有一種叫做“遷移學習”的方式,可以使我們在他人訓練過的模型基礎上進行小改動便可投入使用。在這篇文章中,我將會講述如何使用預訓練模型來加速解決問題的過程。 注:這篇文章默認讀者對于神經(jīng)網(wǎng)絡和深度學習有著一定的了解,如果你不了解深度學習,那么我強烈建議你先了解一下深度學習的基礎概念: 1. 什么是遷移學習? 2. 什么是預訓練模型? 3. 為什么我們使用預訓練模型?-結合生活實例 4. 我們可以怎樣運用預訓練模型? 提取特征(extract features) 優(yōu)化模型(fine tune the model) 5. 優(yōu)化模型的方式 6. 在數(shù)字識別中使用預訓練模型 只針對輸出密集層(output dense layer)的重新訓練 凍結初始幾層網(wǎng)絡的權重因子 為了對遷移學習產(chǎn)生一個直觀的認識,不妨拿老師與學生之間的關系做類比。 一位老師通常在ta所教授的領域有著多年豐富的經(jīng)驗,在這些積累的基礎上,老師們能夠在課堂上教授給學生們該領域最簡明扼要的內(nèi)容。這個過程可以看做是老手與新手之間的“信息遷移”。 這個過程在神經(jīng)網(wǎng)絡中也適用。 我們知道,神經(jīng)網(wǎng)絡需要用數(shù)據(jù)來訓練,它從數(shù)據(jù)中獲得信息,進而把它們轉(zhuǎn)換成相應的權重。這些權重能夠被提取出來,遷移到其他的神經(jīng)網(wǎng)絡中,我們“遷移”了這些學來的特征,就不需要從零開始訓練一個神經(jīng)網(wǎng)絡了 。 現(xiàn)在,讓我們從自身進化的角度來討論這種遷移學習的重要性。這是Tim Urban最近在waitbutwhy.com上的一篇文章中提出的觀點。 Tim說,在語言發(fā)明之前,每一代人類都需要自身重新習得很多知識,這也是知識從上一代到下一代一增長緩慢的原因。 隨后,我們發(fā)明了語言,這為知識在世代間的傳遞提供了載體,下圖是在語言發(fā)明后,同樣時間尺度下知識增長速度的示意圖。 是不是看起來很牛逼?而通過權重的傳遞來進行遷移學習和人類在世代交替中通過語言傳播知識,是一個道理。 簡單來說,預訓練模型(pre-trained model)是前人為了解決類似問題所創(chuàng)造出來的模型。你在解決問題的時候,不用從零開始訓練一個新模型,可以從在類似問題中訓練過的模型入手。 比如說,如果你想做一輛自動駕駛汽車,可以花數(shù)年時間從零開始構建一個性能優(yōu)良的圖像識別算法,也可以從Google在ImageNet數(shù)據(jù)集上訓練得到的inception model(一個預訓練模型)起步,來識別圖像。 一個預訓練模型可能對于你的應用中并不是100%的準確對口,但是它可以為你節(jié)省大量功夫。 接下來,我會舉個例子來說明。 上周我一直在嘗試解決Crowdanalytix platform上的一個問題:從手機圖片中分辨場景。 這是一個圖像分類的問題,訓練數(shù)據(jù)集中有4591張圖片,測試集中有1200張圖片。我們的任務是將圖片相應地分到16個類別中。在對圖片進行一些預處理后,我首先采用一個簡單的MLP(Multi-later Perceptron)模型,結構如下圖所示: 在對輸入圖片(224*224*3)平整化后,為了簡化上述結構,我用了三個各含有500個神經(jīng)元的隱藏層。在輸出層中,共有16個神經(jīng)元對應著十六個類別。 我只能將訓練的準確率控制在6.8%,這是個很不理想的結果。我嘗試對隱藏層、隱層中神經(jīng)元的數(shù)量以及drop out速率進行調(diào)整,但準確度都沒有太大的提升。而如果增加隱藏層和其中神經(jīng)元的數(shù)量,每個周期的運行時間則會增加20s以上。(我的開發(fā)環(huán)境是12GB VRAM,Titan X GPU) 下面是我用上文所述結構的MLP模型訓練輸出的結果。 可以看出,除非指數(shù)級地增加訓練時長,MLP模型無法提供給我更好的結果。因此,我轉(zhuǎn)而采用CNN(卷積神經(jīng)網(wǎng)絡),看看他們在這個數(shù)據(jù)集上的表現(xiàn),以及是否能夠提高訓練的準確度。 CNN的結構如下: 我使用了3個卷積的模塊,每個模塊由以下部分組成:
最后一個卷積模塊輸出的結果經(jīng)過平整化后會被傳遞到一個擁有64的神經(jīng)元的隱藏層上,隨后通過一個drop out rate = 0.5處理后傳遞到輸出層。 最終訓練的結果記錄如下: 準確率15.75%,盡管與MLP模型相比有所提升,但每個周期的運行時間也增加了。 而更重要的是,數(shù)據(jù)集中最大類別所含圖片數(shù)量約占總數(shù)17.6%左右。 只要把所有的圖片都歸到最大的類別,我們就能夠得到比MLP、CNN訓練出來的模型更好的結果(ノへ ̄、)。 此外,增加更多的卷積模塊也會大大增加訓練時長。 于是,我轉(zhuǎn)而去采用預訓練模型,這樣我不需要重新訓練我的整個結構,只需要針對其中的幾層進行訓練即可。 因此,我采用了在ImageNet數(shù)據(jù)集上預先訓練好的VGG16模型,這個模型可以在Keras庫中找到。 模型的結構如下所示: 在VGG16結構的基礎上,我只將softmax層的1000個輸出改為16個,從而適應我們這個問題的情景,隨后重新訓練了dense layer。 跟MLP和CNN相比,這個結構的準確率能夠達到70%。同時,使用VGG16最大的好處是大大減少了訓練時間,只需要針對dense layer進行訓練,所需時間基本可以忽略。 當在訓練經(jīng)網(wǎng)絡的時候我們的目標是什么?我們希望網(wǎng)絡能夠在多次正向反向迭代的過程中,找到合適的權重。 通過使用之前在大數(shù)據(jù)集上經(jīng)過訓練的預訓練模型,我們可以直接使用相應的結構和權重,將它們應用到我們正在面對的問題上。這被稱作是“遷移學習”,即將預訓練的模型“遷移”到我們正在應對的特定問題中。 在選擇預訓練模型的時候你需要非常仔細,如果你的問題與預訓練模型訓練情景下有很大的出入,那么模型所得到的預測結果將會非常不準確。 舉例來說,如果把一個原本用于語音識別的模型用來做用戶識別,那結果肯定是不理想的。 幸運的是,Keras庫中有許多這類預訓練的結構。 ImageNet數(shù)據(jù)集已經(jīng)被廣泛用作訓練集,因為它規(guī)模足夠大(包括120萬張圖片),有助于訓練普適模型。ImageNet的訓練目標,是將所有的圖片正確地劃分到1000個分類條目下。這1000個分類基本上都來源于我們的日常生活,比如說貓貓狗狗的種類,各種家庭用品,日常通勤工具等等。 在遷移學習中,這些預訓練的網(wǎng)絡對于ImageNet數(shù)據(jù)集外的圖片也表現(xiàn)出了很好的泛化性能。 既然預訓練模型已經(jīng)訓練得很好,我們就不會在短時間內(nèi)去修改過多的權重,在遷移學習中用到它的時候,往往只是進行微調(diào)(fine tune)。 在修改模型的過程中,我們通過會采用比一般訓練模型更低的學習速率。 特征提取 我們可以將預訓練模型當做特征提取裝置來使用。具體的做法是,將輸出層去掉,然后將剩下的整個網(wǎng)絡當做一個固定的特征提取機,從而應用到新的數(shù)據(jù)集中。 采用預訓練模型的結構 我們還可以采用預訓練模型的結構,但先將所有的權重隨機化,然后依據(jù)自己的數(shù)據(jù)集進行訓練。 訓練特定層,凍結其他層 另一種使用預訓練模型的方法是對它進行部分的訓練。具體的做法是,將模型起始的一些層的權重保持不變,重新訓練后面的層,得到新的權重。在這個過程中,我們可以多次進行嘗試,從而能夠依據(jù)結果找到frozen layers和retrain layers之間的最佳搭配。 如何使用與訓練模型,是由數(shù)據(jù)集大小和新舊數(shù)據(jù)集(預訓練的數(shù)據(jù)集和我們要解決的數(shù)據(jù)集)之間數(shù)據(jù)的相似度來決定的。 下圖表展示了在各種情況下應該如何使用預訓練模型: 場景一:數(shù)據(jù)集小,數(shù)據(jù)相似度高(與pre-trained model的訓練數(shù)據(jù)相比而言) 在這種情況下,因為數(shù)據(jù)與預訓練模型的訓練數(shù)據(jù)相似度很高,因此我們不需要重新訓練模型。我們只需要將輸出層改制成符合問題情境下的結構就好。 我們使用預處理模型作為模式提取器。 比如說我們使用在ImageNet上訓練的模型來辨認一組新照片中的小貓小狗。在這里,需要被辨認的圖片與ImageNet庫中的圖片類似,但是我們的輸出結果中只需要兩項——貓或者狗。 在這個例子中,我們需要做的就是把dense layer和最終softmax layer的輸出從1000個類別改為2個類別。 場景二:數(shù)據(jù)集小,數(shù)據(jù)相似度不高 在這種情況下,我們可以凍結預訓練模型中的前k個層中的權重,然后重新訓練后面的n-k個層,當然最后一層也需要根據(jù)相應的輸出格式來進行修改。 因為數(shù)據(jù)的相似度不高,重新訓練的過程就變得非常關鍵。而新數(shù)據(jù)集大小的不足,則是通過凍結預訓練模型的前k層進行彌補。 場景三:數(shù)據(jù)集大,數(shù)據(jù)相似度不高 在這種情況下,因為我們有一個很大的數(shù)據(jù)集,所以神經(jīng)網(wǎng)絡的訓練過程將會比較有效率。然而,因為實際數(shù)據(jù)與預訓練模型的訓練數(shù)據(jù)之間存在很大差異,采用預訓練模型將不會是一種高效的方式。 因此最好的方法還是將預處理模型中的權重全都初始化后在新數(shù)據(jù)集的基礎上重頭開始訓練。 場景四:數(shù)據(jù)集大,數(shù)據(jù)相似度高 這就是最理想的情況,采用預訓練模型會變得非常高效。最好的運用方式是保持模型原有的結構和初始權重不變,隨后在新數(shù)據(jù)集的基礎上重新訓練。 現(xiàn)在,讓我們嘗試來用預訓練模型去解決一個簡單的問題。 我曾經(jīng)使用vgg16作為預訓練的模型結構,并把它應用到手寫數(shù)字識別上。 讓我們先來看看這個問題對應著之前四種場景中的哪一種。我們的訓練集(MNIST)有大約60,000張左右的手寫數(shù)字圖片,這樣的數(shù)據(jù)集顯然是偏小的。所以這個問題應該屬于場景一或場景二。 我們可以嘗試把兩種對應的方法都用一下,看看最終的效果。 只重新訓練輸出層 & dense layer 這里我們采用vgg16作為特征提取器。隨后這些特征,會被傳遞到依據(jù)我們數(shù)據(jù)集訓練的dense layer上。輸出層同樣由與我們問題相對應的softmax層函數(shù)所取代。 在vgg16中,輸出層是一個擁有1000個類別的softmax層。我們把這層去掉,換上一層只有10個類別的softmax層。我們只訓練這些層,然后就進行數(shù)字識別的嘗試。 # 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 imagefromimport preprocess_inputimport numpy as npfromimport 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=(224224)) 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=(224224)) 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)features_test=model.predict(test_img)# flattening the layers to conform to MLP inputtrain_x=features_train.reshape(4900025088)# 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'0.4, noise_shape=None, seed=None) model.add(Dense(150,input_dim=500,activation='sigmoid'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)) 凍結最初幾層網(wǎng)絡的權重 這里我們將會把vgg16網(wǎng)絡的前8層進行凍結,然后對后面的網(wǎng)絡重新進行訓練。這么做是因為最初的幾層網(wǎng)絡捕獲的是曲線、邊緣這種普遍的特征,這跟我們的問題是相關的。我們想要保證這些權重不變,讓網(wǎng)絡在學習過程中重點關注這個數(shù)據(jù)集特有的一些特征,從而對后面的網(wǎng)絡進行調(diào)整。 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 imagefromimport preprocess_inputimport numpy as npfromimport 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=(224224)) 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=(224224)) 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 Modeldefvgg16_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) 原文: VGG-16: Keras庫中的ImageNet預訓練模型: 手寫數(shù)字數(shù)據(jù)集MNIST: 【完】 一則通知量子位正在組建自動駕駛技術群,面向研究自動駕駛相關領域的在校學生或一線工程師。李開復、王詠剛、王乃巖、王弢等大牛都在群里。),備注“自動駕駛”申請加入哈~ 招聘△ 掃碼強行關注『量子位』 |
|
來自: 昵稱16619343 > 《辦公技能》