一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

面向小數(shù)據(jù)集構(gòu)建圖像分類模型

 田杰4 2017-01-26

面向小數(shù)據(jù)集構(gòu)建圖像分類模型

文章信息

本文地址:http://blog./building-powerful-image-classification-models-using-very-little-data.html

本文作者:Francois Chollet

概述

在本文中,我們將提供一些面向小數(shù)據(jù)集(幾百張到幾千張圖片)構(gòu)造高效、實(shí)用的圖像分類器的方法。

本文將探討如下幾種方法:

  • 從圖片中直接訓(xùn)練一個小網(wǎng)絡(luò)(作為基準(zhǔn)方法)

  • 利用預(yù)訓(xùn)練網(wǎng)絡(luò)的bottleneck(瓶頸)特征

  • fine-tune預(yù)訓(xùn)練網(wǎng)絡(luò)的高層

本文需要使用的Keras模塊有:

  • fit_generator:用于從Python生成器中訓(xùn)練網(wǎng)絡(luò)

  • ImageDataGenerator:用于實(shí)時數(shù)據(jù)提升

  • 層參數(shù)凍結(jié)和模型fine-tune


配置情況

我們的實(shí)驗基于下面的配置

  • 2000張訓(xùn)練圖片構(gòu)成的數(shù)據(jù)集,一共兩個類別,每類1000張

  • 安裝有Keras,SciPy,PIL的機(jī)器,如果有NVIDIA GPU那就更好了,但因為我們面對的是小數(shù)據(jù)集,沒有也可以。

  • 數(shù)據(jù)集按照下面的形式存放

data/ train/ dogs/ dog001.jpg dog002.jpg ... cats/ cat001/jpg cat002.jpg ... validation/ dogs/ dog001.jpg dog002.jpg ... cats/ cat001/jpg cat002.jpg ...

這份數(shù)據(jù)集來源于Kaggle,原數(shù)據(jù)集有12500只貓和12500只狗,我們只取了各個類的前1000張圖片。另外我們還從各個類中取了400張額外圖片用于測試。

下面是數(shù)據(jù)集的一些示例圖片,圖片的數(shù)量非常少,這對于圖像分類來說是個大麻煩。但現(xiàn)實(shí)是,很多真實(shí)世界圖片獲取是很困難的,我們能得到的樣本數(shù)目確實(shí)很有限(比如醫(yī)學(xué)圖像,每張正樣本都意味著一個承受痛苦的病人:()。對數(shù)據(jù)科學(xué)家而言,我們應(yīng)該有能夠榨取少量數(shù)據(jù)的全部價值的能力,而不是簡單的伸手要更多的數(shù)據(jù)。

cats_and_dogs

在Kaggle的貓狗大戰(zhàn)競賽種,參賽者通過使用現(xiàn)代的深度學(xué)習(xí)技術(shù)達(dá)到了98%的正確率,我們只使用了全部數(shù)據(jù)的8%,因此這個問題對我們來說更難。


針對小數(shù)據(jù)集的深度學(xué)習(xí)

我經(jīng)常聽到的一種說法是,深度學(xué)習(xí)只有在你擁有海量數(shù)據(jù)時才有意義。雖然這種說法并不是完全不對,但卻具有較強(qiáng)的誤導(dǎo)性。當(dāng)然,深度學(xué)習(xí)強(qiáng)調(diào)從數(shù)據(jù)中自動學(xué)習(xí)特征的能力,沒有足夠的訓(xùn)練樣本,這幾乎是不可能的。尤其是當(dāng)輸入的數(shù)據(jù)維度很高(如圖片)時。然而,卷積神經(jīng)網(wǎng)絡(luò)作為深度學(xué)習(xí)的支柱,被設(shè)計為針對“感知”問題最好的模型之一(如圖像分類問題),即使只有很少的數(shù)據(jù),網(wǎng)絡(luò)也能把特征學(xué)的不錯。針對小數(shù)據(jù)集的神經(jīng)網(wǎng)絡(luò)依然能夠得到合理的結(jié)果,并不需要任何手工的特征工程。一言以蔽之,卷積神經(jīng)網(wǎng)絡(luò)大法好!

另一方面,深度學(xué)習(xí)模型天然就具有可重用的特性:比方說,你可以把一個在大規(guī)模數(shù)據(jù)上訓(xùn)練好的圖像分類或語音識別的模型重用在另一個很不一樣的問題上,而只需要做有限的一點(diǎn)改動。尤其在計算機(jī)視覺領(lǐng)域,許多預(yù)訓(xùn)練的模型現(xiàn)在都被公開下載,并被重用在其他問題上以提升在小數(shù)據(jù)集上的性能。


數(shù)據(jù)預(yù)處理與數(shù)據(jù)提升

為了盡量利用我們有限的訓(xùn)練數(shù)據(jù),我們將通過一系列隨機(jī)變換堆數(shù)據(jù)進(jìn)行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利于我們抑制過擬合,使得模型的泛化能力更好。

在Keras中,這個步驟可以通過keras.preprocessing.image.ImageGenerator來實(shí)現(xiàn),這個類使你可以:

  • 在訓(xùn)練過程中,設(shè)置要施行的隨機(jī)變換

  • 通過.flow.flow_from_directory(directory)方法實(shí)例化一個針對圖像batch的生成器,這些生成器可以被用作keras模型相關(guān)方法的輸入,如fit_generator,evaluate_generatorpredict_generator

現(xiàn)在讓我們看個例子:

from keras.preprocessing.image import ImageDataGeneratordatagen = ImageDataGenerator( rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest')

上面顯示的只是一部分選項,請閱讀文檔的相關(guān)部分來查看全部可用的選項。我們來快速的瀏覽一下這些選項的含義:

  • rotation_range是一個0~180的度數(shù),用來指定隨機(jī)選擇圖片的角度。

  • width_shiftheight_shift用來指定水平和豎直方向隨機(jī)移動的程度,這是兩個0~1之間的比例。

  • rescale值將在執(zhí)行其他處理前乘到整個圖像上,我們的圖像在RGB通道都是0~255的整數(shù),這樣的操作可能使圖像的值過高或過低,所以我們將這個值定為0~1之間的數(shù)。

  • shear_range是用來進(jìn)行剪切變換的程度,參考剪切變換

  • zoom_range用來進(jìn)行隨機(jī)的放大

  • horizontal_flip隨機(jī)的對圖片進(jìn)行水平翻轉(zhuǎn),這個參數(shù)適用于水平翻轉(zhuǎn)不影響圖片語義的時候

  • fill_mode用來指定當(dāng)需要進(jìn)行像素填充,如旋轉(zhuǎn),水平和豎直位移時,如何填充新出現(xiàn)的像素

下面我們使用這個工具來生成圖片,并將它們保存在一個臨時文件夾中,這樣我們可以感覺一下數(shù)據(jù)提升究竟做了什么事。為了使圖片能夠展示出來,這里沒有使用rescaling

from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_imgdatagen = ImageDataGenerator( rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest')img = load_img('data/train/cats/cat.0.jpg') # this is a PIL imagex = img_to_array(img) # this is a Numpy array with shape (3, 150, 150)x = x.reshape((1,) + x.shape) # this is a Numpy array with shape (1, 3, 150, 150)# the .flow() command below generates batches of randomly transformed images# and saves the results to the `preview/` directoryi = 0for batch in datagen.flow(x, batch_size=1, save_to_dir='preview', save_prefix='cat', save_format='jpeg'): i += 1 if i > 20: break # otherwise the generator would loop indefinitely

下面是一張圖片被提升以后得到的多個結(jié)果:

cat_data_augmentation


在小數(shù)據(jù)集上訓(xùn)練神經(jīng)網(wǎng)絡(luò):40行代碼達(dá)到80%的準(zhǔn)確率

進(jìn)行圖像分類的正確工具是卷積網(wǎng)絡(luò),所以我們來試試用卷積神經(jīng)網(wǎng)絡(luò)搭建一個初級的模型。因為我們的樣本數(shù)很少,所以我們應(yīng)該對過擬合的問題多加注意。當(dāng)一個模型從很少的樣本中學(xué)習(xí)到不能推廣到新數(shù)據(jù)的模式時,我們稱為出現(xiàn)了過擬合的問題。過擬合發(fā)生時,模型試圖使用不相關(guān)的特征來進(jìn)行預(yù)測。例如,你有三張伐木工人的照片,有三張水手的照片。六張照片中只有一個伐木工人戴了帽子,如果你認(rèn)為戴帽子是能將伐木工人與水手區(qū)別開的特征,那么此時你就是一個差勁的分類器。

數(shù)據(jù)提升是對抗過擬合問題的一個武器,但還不夠,因為提升過的數(shù)據(jù)仍然是高度相關(guān)的。對抗過擬合的你應(yīng)該主要關(guān)注的是模型的“熵容量”——模型允許存儲的信息量。能夠存儲更多信息的模型能夠利用更多的特征取得更好的性能,但也有存儲不相關(guān)特征的風(fēng)險。另一方面,只能存儲少量信息的模型會將存儲的特征主要集中在真正相關(guān)的特征上,并有更好的泛化性能。

有很多不同的方法來調(diào)整模型的“熵容量”,常見的一種選擇是調(diào)整模型的參數(shù)數(shù)目,即模型的層數(shù)和每層的規(guī)模。另一種方法是對權(quán)重進(jìn)行正則化約束,如L1或L2.這種約束會使模型的權(quán)重偏向較小的值。

在我們的模型里,我們使用了很小的卷積網(wǎng)絡(luò),只有很少的幾層,每層的濾波器數(shù)目也不多。再加上數(shù)據(jù)提升和Dropout,就差不多了。Dropout通過防止一層看到兩次完全一樣的模式來防止過擬合,相當(dāng)于也是一種數(shù)據(jù)提升的方法。(你可以說dropout和數(shù)據(jù)提升都在隨機(jī)擾亂數(shù)據(jù)的相關(guān)性)

下面展示的代碼是我們的第一個模型,一個很簡單的3層卷積加上ReLU激活函數(shù),再接max-pooling層。這個結(jié)構(gòu)和Yann LeCun在1990年發(fā)布的圖像分類器很相似(除了ReLU)

這個實(shí)驗的全部代碼在這里

from keras.models import Sequentialfrom keras.layers import Convolution2D, MaxPooling2Dfrom keras.layers import Activation, Dropout, Flatten, Densemodel = Sequential()model.add(Convolution2D(32, 3, 3, input_shape=(3, 150, 150)))model.add(Activation('relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Convolution2D(32, 3, 3))model.add(Activation('relu'))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Convolution2D(64, 3, 3))model.add(Activation('relu'))model.add(MaxPooling2D(pool_size=(2, 2)))# the model so far outputs 3D feature maps (height, width, features)

然后我們接了兩個全連接網(wǎng)絡(luò),并以單個神經(jīng)元和sigmoid激活結(jié)束模型。這種選擇會產(chǎn)生二分類的結(jié)果,與這種配置相適應(yīng),我們使用binary_crossentropy作為損失函數(shù)。

model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectorsmodel.add(Dense(64))model.add(Activation('relu'))model.add(Dropout(0.5))model.add(Dense(1))model.add(Activation('sigmoid'))model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

然后我們開始準(zhǔn)備數(shù)據(jù),使用.flow_from_directory()來從我們的jpgs圖片中直接產(chǎn)生數(shù)據(jù)和標(biāo)簽。

# this is the augmentation configuration we will use for trainingtrain_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)# this is the augmentation configuration we will use for testing:# only rescalingtest_datagen = ImageDataGenerator(rescale=1./255)# this is a generator that will read pictures found in# subfolers of 'data/train', and indefinitely generate# batches of augmented image datatrain_generator = train_datagen.flow_from_directory( 'data/train', # this is the target directory target_size=(150, 150), # all images will be resized to 150x150 batch_size=32, class_mode='binary') # since we use binary_crossentropy loss, we need binary labels# this is a similar generator, for validation datavalidation_generator = test_datagen.flow_from_directory( 'data/validation', target_size=(150, 150), batch_size=32, class_mode='binary')

然后我們可以用這個生成器來訓(xùn)練網(wǎng)絡(luò)了,在GPU上每個epoch耗時20~30秒,在CPU上耗時300~400秒,所以如果你不是很著急,在CPU上跑這個模型也是完全可以的。

model.fit_generator( train_generator, samples_per_epoch=2000, nb_epoch=50, validation_data=validation_generator, nb_val_samples=800)model.save_weights('first_try.h5') # always save your weights after training or during training

這個模型在50個epoch后的準(zhǔn)確率為79%~81%,別忘了我們只用了8%的數(shù)據(jù),也沒有花時間來做模型和超參數(shù)的優(yōu)化。在Kaggle中,這個模型已經(jīng)可以進(jìn)前100名了(一共215隊參與),估計剩下的115隊都沒有用深度學(xué)習(xí):)

注意這個準(zhǔn)確率的變化可能會比較大,因為準(zhǔn)確率本來就是一個變化較高的評估參數(shù),而且我們只有800個樣本用來測試。比較好的驗證方法是使用K折交叉驗證,但每輪驗證中我們都要訓(xùn)練一個模型。


使用預(yù)訓(xùn)練網(wǎng)絡(luò)的bottleneck特征:一分鐘達(dá)到90%的正確率

一個稍微講究一點(diǎn)的辦法是,利用在大規(guī)模數(shù)據(jù)集上預(yù)訓(xùn)練好的網(wǎng)絡(luò)。這樣的網(wǎng)絡(luò)在多數(shù)的計算機(jī)視覺問題上都能取得不錯的特征,利用這樣的特征可以讓我們獲得更高的準(zhǔn)確率。

我們將使用vgg-16網(wǎng)絡(luò),該網(wǎng)絡(luò)在ImageNet數(shù)據(jù)集上進(jìn)行訓(xùn)練,這個模型我們之前提到過了。因為ImageNet數(shù)據(jù)集包含多種“貓”類和多種“狗”類,這個模型已經(jīng)能夠?qū)W習(xí)與我們這個數(shù)據(jù)集相關(guān)的特征了。事實(shí)上,簡單的記錄原來網(wǎng)絡(luò)的輸出而不用bottleneck特征就已經(jīng)足夠把我們的問題解決的不錯了。不過我們這里講的方法對其他的類似問題有更好的推廣性,包括在ImageNet中沒有出現(xiàn)的類別的分類問題。

VGG-16的網(wǎng)絡(luò)結(jié)構(gòu)如下:

vgg_16

我們的方法是這樣的,我們將利用網(wǎng)絡(luò)的卷積層部分,把全連接以上的部分拋掉。然后在我們的訓(xùn)練集和測試集上跑一遍,將得到的輸出(即“bottleneck feature”,網(wǎng)絡(luò)在全連接之前的最后一層激活的feature map)記錄在兩個numpy array里。然后我們基于記錄下來的特征訓(xùn)練一個全連接網(wǎng)絡(luò)。

我們將這些特征保存為離線形式,而不是將我們的全連接模型直接加到網(wǎng)絡(luò)上并凍結(jié)之前的層參數(shù)進(jìn)行訓(xùn)練的原因是處于計算效率的考慮。運(yùn)行VGG網(wǎng)絡(luò)的代價是非常高昂的,尤其是在CPU上運(yùn)行,所以我們只想運(yùn)行一次。這也是我們不進(jìn)行數(shù)據(jù)提升的原因。

我們不再贅述如何搭建vgg-16網(wǎng)絡(luò)了,這件事之前已經(jīng)說過,在keras的example里也可以找到。但讓我們看看如何記錄bottleneck特征。

generator = datagen.flow_from_directory( 'data/train', target_size=(150, 150), batch_size=32, class_mode=None, # this means our generator will only yield batches of data, no labels shuffle=False) # our data will be in order, so all first 1000 images will be cats, then 1000 dogs# the predict_generator method returns the output of a model, given# a generator that yields batches of numpy databottleneck_features_train = model.predict_generator(generator, 2000)# save the output as a Numpy arraynp.save(open('bottleneck_features_train.npy', 'w'), bottleneck_features_train)generator = datagen.flow_from_directory( 'data/validation', target_size=(150, 150), batch_size=32, class_mode=None, shuffle=False)bottleneck_features_validation = model.predict_generator(generator, 800)np.save(open('bottleneck_features_validation.npy', 'w'), bottleneck_features_validation)

記錄完畢后我們可以將數(shù)據(jù)載入,用于訓(xùn)練我們的全連接網(wǎng)絡(luò):

train_data = np.load(open('bottleneck_features_train.npy'))# the features were saved in order, so recreating the labels is easytrain_labels = np.array([0] * 1000 + [1] * 1000)validation_data = np.load(open('bottleneck_features_validation.npy'))validation_labels = np.array([0] * 400 + [1] * 400)model = Sequential()model.add(Flatten(input_shape=train_data.shape[1:]))model.add(Dense(256, activation='relu'))model.add(Dropout(0.5))model.add(Dense(1, activation='sigmoid'))model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])model.fit(train_data, train_labels, nb_epoch=50, batch_size=32, validation_data=(validation_data, validation_labels))model.save_weights('bottleneck_fc_model.h5')

因為特征的size很小,模型在CPU上跑的也會很快,大概1s一個epoch,最后我們的準(zhǔn)確率是90%~91%,這么好的結(jié)果多半歸功于預(yù)訓(xùn)練的vgg網(wǎng)絡(luò)幫助我們提取特征。


在預(yù)訓(xùn)練的網(wǎng)絡(luò)上fine-tune

為了進(jìn)一步提高之前的結(jié)果,我們可以試著fine-tune網(wǎng)絡(luò)的后面幾層。Fine-tune以一個預(yù)訓(xùn)練好的網(wǎng)絡(luò)為基礎(chǔ),在新的數(shù)據(jù)集上重新訓(xùn)練一小部分權(quán)重。在這個實(shí)驗中,fine-tune分三個步驟

  • 搭建vgg-16并載入權(quán)重

  • 將之前定義的全連接網(wǎng)絡(luò)加在模型的頂部,并載入權(quán)重

  • 凍結(jié)vgg16網(wǎng)絡(luò)的一部分參數(shù)

vgg16_modified

注意:

  • 為了進(jìn)行fine-tune,所有的層都應(yīng)該以訓(xùn)練好的權(quán)重為初始值,例如,你不能將隨機(jī)初始的全連接放在預(yù)訓(xùn)練的卷積層之上,這是因為由隨機(jī)權(quán)重產(chǎn)生的大地圖將會破壞卷積層預(yù)訓(xùn)練的權(quán)重。在我們的情形中,這就是為什么我們首先訓(xùn)練頂層分類器,然后再基于它進(jìn)行fine-tune的原因

  • 我們選擇只fine-tune最后的卷積塊,而不是整個網(wǎng)絡(luò),這是為了防止過擬合。整個網(wǎng)絡(luò)具有巨大的熵容量,因此具有很高的過擬合傾向。由底層卷積模塊學(xué)習(xí)到的特征更加一般,更加不具有抽象性,因此我們要保持前兩個卷積塊(學(xué)習(xí)一般特征)不動,只fine-tune后面的卷積塊(學(xué)習(xí)特別的特征)。

  • fine-tune應(yīng)該在很低的學(xué)習(xí)率下進(jìn)行,通常使用SGD優(yōu)化而不是其他自適應(yīng)學(xué)習(xí)率的優(yōu)化算法,如RMSProp。這是為了保證更新的幅度保持在較低的程度,以免毀壞預(yù)訓(xùn)練的特征。

代碼如下,首先在初始化好的vgg網(wǎng)絡(luò)上添加我們預(yù)訓(xùn)練好的模型:

# build a classifier model to put on top of the convolutional modeltop_model = Sequential()top_model.add(Flatten(input_shape=model.output_shape[1:]))top_model.add(Dense(256, activation='relu'))top_model.add(Dropout(0.5))top_model.add(Dense(1, activation='sigmoid'))# note that it is necessary to start with a fully-trained# classifier, including the top classifier,# in order to successfully do fine-tuningtop_model.load_weights(top_model_weights_path)# add the model on top of the convolutional basemodel.add(top_model)

然后將最后一個卷積塊前的卷積層參數(shù)凍結(jié):

# set the first 25 layers (up to the last conv block)# to non-trainable (weights will not be updated)for layer in model.layers[:25]: layer.trainable = False# compile the model with a SGD/momentum optimizer# and a very slow learning rate.model.compile(loss='binary_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), metrics=['accuracy'])

然后以很低的學(xué)習(xí)率進(jìn)行訓(xùn)練:

# prepare data augmentation configurationtrain_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(img_height, img_width), batch_size=32, class_mode='binary')validation_generator = test_datagen.flow_from_directory( validation_data_dir, target_size=(img_height, img_width), batch_size=32, class_mode='binary')# fine-tune the modelmodel.fit_generator( train_generator, samples_per_epoch=nb_train_samples, nb_epoch=nb_epoch, validation_data=validation_generator, nb_val_samples=nb_validation_samples)

在50個epoch之后該方法的準(zhǔn)確率為94%,非常成功

通過下面的方法你可以達(dá)到95%以上的正確率:

  • 更加強(qiáng)烈的數(shù)據(jù)提升

  • 更加強(qiáng)烈的dropout

  • 使用L1和L2正則項(也稱為權(quán)重衰減)

  • fine-tune更多的卷積塊(配合更大的正則)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲一区二区三区国产| 久久国产人妻一区二区免费| 91亚洲熟女少妇在线观看| 国产二级一级内射视频播放| 久久精品亚洲欧美日韩| 国产精品久久熟女吞精| 国内尹人香蕉综合在线| 欧美国产日本免费不卡| 日韩欧美亚洲综合在线| 男生和女生哪个更好色| 男人和女人草逼免费视频| 日韩精品一区二区三区四区| 69精品一区二区蜜桃视频| 高潮日韩福利在线观看| 夜夜嗨激情五月天精品| 在线精品首页中文字幕亚洲| 欧美自拍偷自拍亚洲精品| 欧美野外在线刺激在线观看 | 国产情侣激情在线对白| 69老司机精品视频在线观看| 欧美字幕一区二区三区| 国内精品美女福利av在线| 欧美人妻一区二区三区| 久久精品国产亚洲av麻豆尤物| 国产日韩欧美一区二区| 欧美成人高清在线播放| 欧美成人免费视频午夜色| 天堂网中文字幕在线视频| 98精品永久免费视频| 国产午夜福利不卡片在线观看| 日本人妻精品有码字幕| 99视频精品免费视频| 一级欧美一级欧美在线播| 91偷拍与自偷拍精品| 肥白女人日韩中文视频| 国产高清精品福利私拍| 国产黄色高清内射熟女视频| 一区二区三区四区亚洲专区| 国产精品午夜视频免费观看| 日韩精品毛片视频免费看| 日本女人亚洲国产性高潮视频|