久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

面向小數(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ā)表

    請遵守用戶 評論公約

    類似文章 更多