十二、計(jì)算機(jī)視覺(jué)中的深度學(xué)習(xí):從零開(kāi)始訓(xùn)練卷積網(wǎng)絡(luò)


文章代碼來(lái)源:《deep learning on keras》腺兴,非常好的一本書(shū)左电,大家如果英語(yǔ)好,推薦直接閱讀該書(shū)页响,如果時(shí)間不夠篓足,可以看看此系列文章,文章為我自己翻譯的內(nèi)容加上自己的一些思考闰蚕,水平有限栈拖,多有不足,請(qǐng)多指正没陡,翻譯版權(quán)所有涩哟,若有轉(zhuǎn)載,請(qǐng)先聯(lián)系本人盼玄。
個(gè)人方向?yàn)閿?shù)值計(jì)算贴彼,日后會(huì)向深度學(xué)習(xí)和計(jì)算問(wèn)題的融合方面靠近,若有相近專業(yè)人士埃儿,歡迎聯(lián)系器仗。


系列文章:
一、搭建屬于你的第一個(gè)神經(jīng)網(wǎng)絡(luò)
二童番、訓(xùn)練完的網(wǎng)絡(luò)去哪里找
三精钮、【keras實(shí)戰(zhàn)】波士頓房?jī)r(jià)預(yù)測(cè)
四、keras的function API
五剃斧、keras callbacks使用
六轨香、機(jī)器學(xué)習(xí)基礎(chǔ)Ⅰ:機(jī)器學(xué)習(xí)的四個(gè)標(biāo)簽
七、機(jī)器學(xué)習(xí)基礎(chǔ)Ⅱ:評(píng)估機(jī)器學(xué)習(xí)模型
八悯衬、機(jī)器學(xué)習(xí)基礎(chǔ)Ⅲ:數(shù)據(jù)預(yù)處理弹沽、特征工程和特征學(xué)習(xí)
九檀夹、機(jī)器學(xué)習(xí)基礎(chǔ)Ⅳ:過(guò)擬合和欠擬合
十、機(jī)器學(xué)習(xí)基礎(chǔ)Ⅴ:機(jī)器學(xué)習(xí)的一般流程十一策橘、計(jì)算機(jī)視覺(jué)中的深度學(xué)習(xí):卷積神經(jīng)網(wǎng)絡(luò)介紹
十二炸渡、計(jì)算機(jī)視覺(jué)中的深度學(xué)習(xí):從零開(kāi)始訓(xùn)練卷積網(wǎng)絡(luò)
十三、計(jì)算機(jī)視覺(jué)中的深度學(xué)習(xí):使用預(yù)訓(xùn)練網(wǎng)絡(luò)
十四丽已、計(jì)算機(jī)視覺(jué)中的神經(jīng)網(wǎng)絡(luò):可視化卷積網(wǎng)絡(luò)所學(xué)到的東西


用很少的數(shù)據(jù)來(lái)訓(xùn)練圖像分類模型在實(shí)踐中很常見(jiàn)蚌堵,如果你是計(jì)算機(jī)視覺(jué)背景專業(yè)出身的。
有少量樣本意味著有幾百到幾萬(wàn)張圖片沛婴。作為實(shí)例教學(xué)吼畏,我們將會(huì)集中注意力于貓狗分類,數(shù)據(jù)集中含有2000張貓嘁灯,2000張狗泻蚊。我們將會(huì)使用2000張用來(lái)訓(xùn)練,1000張用來(lái)驗(yàn)證丑婿,最后1000張用來(lái)測(cè)試性雄。
在這部分,我們將回顧一個(gè)基本的解決這個(gè)問(wèn)題的方法:從零訓(xùn)練一個(gè)新的模型羹奉。我們從直接在我們的2000個(gè)訓(xùn)練樣本上訓(xùn)練一個(gè)小卷積網(wǎng)絡(luò)開(kāi)始秒旋,沒(méi)有任何正則化,建立一個(gè)能達(dá)到的最低水平诀拭。我們的分類準(zhǔn)確率達(dá)到了71%迁筛。這那一點(diǎn),我們的主要問(wèn)題在過(guò)擬合上耕挨。我們將會(huì)介紹數(shù)據(jù)增加细卧,一種在計(jì)算機(jī)視覺(jué)中有效預(yù)防過(guò)擬合的方法。通過(guò)利用數(shù)據(jù)增加俗孝,我們把我們的網(wǎng)絡(luò)的準(zhǔn)確率提升到了82%酒甸。
在接下來(lái)的部分魄健,我們將會(huì)回顧兩個(gè)重要的應(yīng)用在深度學(xué)習(xí)小樣本的方法:在預(yù)訓(xùn)練的網(wǎng)絡(luò)上做特征提雀陈痢(這將幫助我們達(dá)到90%到96%的準(zhǔn)確率)以及調(diào)好參數(shù)的預(yù)訓(xùn)練網(wǎng)絡(luò)(可以將準(zhǔn)確率提升到97%)。一起來(lái)說(shuō)沽瘦,這三個(gè)方法——從零訓(xùn)練小模型革骨,使用預(yù)訓(xùn)練模型來(lái)做特征提取,調(diào)節(jié)預(yù)處理模型的參數(shù)——將會(huì)組成你以后解決計(jì)算機(jī)視覺(jué)問(wèn)題中的小數(shù)據(jù)集時(shí)的工具包析恋。

小數(shù)據(jù)問(wèn)題的深度學(xué)習(xí)關(guān)聯(lián)

你有的時(shí)候會(huì)聽(tīng)到深度學(xué)習(xí)只有當(dāng)有很多數(shù)據(jù)的時(shí)候才起作用良哲。這在一定程度上是一個(gè)有效的點(diǎn):一個(gè)深度學(xué)習(xí)的基本特征是它能找到訓(xùn)練數(shù)據(jù)本身的有意思的特征,不需要任何人工特征工程助隧,這也只能在有很多訓(xùn)練樣本的時(shí)候是可行的筑凫。這對(duì)于輸入樣本有比較高的維數(shù)時(shí)尤為正確,比如說(shuō)圖像。
然而巍实,構(gòu)成很多樣本的都是相關(guān)的滓技。不可能通過(guò)十來(lái)個(gè)樣本就訓(xùn)練一個(gè)網(wǎng)絡(luò)去解決復(fù)雜的問(wèn)題,但是對(duì)于比較小的棚潦,正則化好的模型令漂,數(shù)百個(gè)樣本也足夠了。因?yàn)榫矸e網(wǎng)絡(luò)學(xué)習(xí)局部丸边,具有平移不變性的特征叠必,具有很高的數(shù)據(jù)效率。在一個(gè)很小的圖像數(shù)據(jù)集上從零開(kāi)始訓(xùn)練一個(gè)卷積網(wǎng)絡(luò)妹窖,仍將產(chǎn)生合理的結(jié)果纬朝,盡管缺少數(shù)據(jù),無(wú)需任何自定義的特征工程骄呼。你將在這一部分看到玄组。
但是,深度學(xué)習(xí)模型是自然能高度重新設(shè)計(jì)的:你能將一個(gè)模型用到不同的數(shù)據(jù)集上谒麦,只需要一丁點(diǎn)的改動(dòng)即可俄讹。特別的,很多訓(xùn)練好的模型都能下載了绕德,能夠用來(lái)引導(dǎo)小數(shù)據(jù)的情況患膛。

下載數(shù)據(jù)

The cats vs. dogs dataset在keras里面沒(méi)有,但是在Kaggle里面的2013下載到耻蛇。
下載后的數(shù)據(jù)如下所示:

Samples from the cats vs. dogs dataset. Sizes were not modified: the samples are heterogenous in size, appearance, etc.

不出意料的踪蹬,在2013的Kaggle競(jìng)賽中,使用convnets的贏得了比賽臣咖。達(dá)到了95%的準(zhǔn)確率跃捣,接下來(lái)我們得到的會(huì)很接近這個(gè)準(zhǔn)確率,我們實(shí)際使用的樣本還不足原本競(jìng)賽給出數(shù)據(jù)的10%夺蛇,競(jìng)賽包含25000個(gè)貓狗圖疚漆,大小為543MB(壓縮后)。在下載和解壓后刁赦,我們將會(huì)生成一個(gè)新的數(shù)據(jù)集娶聘,包含三個(gè)子集:一個(gè)貓狗各有1000個(gè)樣本的訓(xùn)練集,各有500個(gè)樣本的驗(yàn)證集甚脉,和各有500個(gè)樣本的測(cè)試集丸升。
接下來(lái)就是幾行做這個(gè)的代碼:

import os, shutil
# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = '/Users/fchollet/Downloads/kaggle_original_data'
# The directory where we will
# store our smaller dataset
base_dir = '/Users/fchollet/Downloads/cats_and_dogs_small'
os.mkdir(base_dir)
# Directories for our training,
# validation and test splits
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)
# Directory with our training cat pictures
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)
# Directory with our training dog pictures
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)
# Directory with our validation cat pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)
# Directory with our validation dog pictures
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)
# Directory with our validation cat pictures
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)
# Directory with our validation dog pictures
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)
# Copy first 1000 cat images to train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(train_cats_dir, fname)
 shutil.copyfile(src, dst)
# Copy next 500 cat images to validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(validation_cats_dir, fname)
 shutil.copyfile(src, dst)
# Copy next 500 cat images to test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
# Copy first 1000 dog images to train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(train_dogs_dir, fname)
 shutil.copyfile(src, dst)
# Copy next 500 dog images to validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(validation_dogs_dir, fname)
 shutil.copyfile(src, dst)
# Copy next 500 dog images to test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
 src = os.path.join(original_dataset_dir, fname)
 dst = os.path.join(test_dogs_dir, fname)
 shutil.copyfile(src, dst)

首先給出原始數(shù)據(jù)和新數(shù)據(jù)集的存放位置。
再在新數(shù)據(jù)集目錄下分別寫(xiě)訓(xùn)練集牺氨、驗(yàn)證集狡耻、測(cè)試集的位置墩剖。
再分別在三個(gè)集合下面建立貓和狗的文件夾。
最后夷狰,把原數(shù)據(jù)集里面的前1000個(gè)數(shù)據(jù)放進(jìn)新訓(xùn)練集涛碑,接下來(lái)的500個(gè)放進(jìn)驗(yàn)證集孵淘,再接下來(lái)五百個(gè)放進(jìn)測(cè)試集瘫证。
最后使用程序數(shù)一數(shù)我們放對(duì)了嗎揉阎?

>>> print('total training cat images:', len(os.listdir(train_cats_dir)))
total training cat images: 1000
>>> print('total training dog images:', len(os.listdir(train_dogs_dir)))
total training dog images: 1000
>>> print('total validation cat images:', len(os.listdir(validation_cats_dir)))
total validation cat images: 500
>>> print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
total validation dog images: 500
>>> print('total test cat images:', len(os.listdir(test_cats_dir)))
total test cat images: 500
>>> print('total test dog images:', len(os.listdir(test_dogs_dir)))
total test dog images: 500

這樣一來(lái),我們就得到了所需小數(shù)據(jù)集毙籽。

構(gòu)建我們的神經(jīng)網(wǎng)絡(luò)

我們已經(jīng)在MNIST中構(gòu)建了一個(gè)小的卷積神經(jīng)網(wǎng)絡(luò)毡庆,所以你應(yīng)該對(duì)這個(gè)很熟坑赡。我們將會(huì)重復(fù)使用相同的生成框架:我們的卷積網(wǎng)絡(luò)就是一些卷積層和最大池化層的堆疊。
然而么抗,由于我們?cè)诮鉀Q大點(diǎn)的圖像和更加復(fù)雜的問(wèn)題蝇刀,我們要讓我們的網(wǎng)絡(luò)相應(yīng)的也更大:將會(huì)有更多的卷積層和最大池化層的組合。這將擴(kuò)大網(wǎng)絡(luò)的容量捆探,并減少特征映射的大小站粟,使得他們?cè)诶鞂硬粫?huì)過(guò)大奴烙。這里,由于我們輸入的大小從150\times 150開(kāi)始(隨便選的一個(gè))恰起,我們最終得到了7\times 7的特征映射趾牧。
注意特征映射的深度從32提升到了128翘单,同時(shí)特征映射的大小在下降(從148\times 1487\times 7)
由于我們?cè)诠粢粋€(gè)二分類問(wèn)題哄芜,我們的網(wǎng)絡(luò)最終只需要一個(gè)單元。

from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
 input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

看一下結(jié)構(gòu)

>>> model.summary()
Layer (type) Output Shape Param #
================================================================
conv2d_1 (Conv2D) (None, 148, 148, 32) 896
________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (None, 74, 74, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (None, 72, 72, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (None, 36, 36, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (None, 34, 34, 128) 73856
________________________________________________________________
maxpooling2d_3 (MaxPooling2D) (None, 17, 17, 128) 0
________________________________________________________________
conv2d_4 (Conv2D) (None, 15, 15, 128) 147584
________________________________________________________________
maxpooling2d_4 (MaxPooling2D) (None, 7, 7, 128) 0
________________________________________________________________
flatten_1 (Flatten) (None, 6272) 0
________________________________________________________________
dense_1 (Dense) (None, 512) 3211776
________________________________________________________________
dense_2 (Dense) (None, 1) 513
================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

在最后compilation的步驟,我們會(huì)像往常一樣剧腻,使用RMSprop優(yōu)化器涂屁,因?yàn)槲覀兪褂靡粋€(gè)sigmoid單元在我們的模型最后拆又,我們將使用二進(jìn)交叉熵作為損失函數(shù),記住栈源,你要是不知道怎么選這些東西了凉翻,可以翻一翻之前列的表捻激。

from keras import optimizers
model.compile(loss='binary_crossentropy',
 optimizer=optimizers.RMSprop(lr=1e-4),
 metrics=['acc'])

數(shù)據(jù)預(yù)處理

你現(xiàn)在已經(jīng)知道胞谭,數(shù)據(jù)在喂進(jìn)網(wǎng)絡(luò)之前需要預(yù)處理成浮點(diǎn)數(shù)張量。目前我們的數(shù)據(jù)來(lái)自于JPEG文件调俘,所以其處理步驟大致為:

  • 讀圖片文件
  • 將JPEG解碼為RBG
  • 將它們轉(zhuǎn)化為浮點(diǎn)張量
  • 將像素點(diǎn)的值歸一化

這看起來(lái)有點(diǎn)冗雜彩库,但所幸先蒋,keras能夠自動(dòng)做完上述步驟竞漾。keras有一個(gè)圖像處理幫助工具窥翩,位于keras.preprocessing.image寇蚊。特別的棍好,其包括ImageDataGenerator類借笙,能夠快速設(shè)置Pyhon的生成器,從而快速將磁盤(pán)上圖片文件加入預(yù)處理張量批次姚垃。這就是我們將要使用的积糯。

注意:理解Python中的生成器(generators)

Python的生成器是一個(gè)對(duì)象谦纱,像一個(gè)迭代器一樣工作跨嘉,即一個(gè)對(duì)象可以使用for/in操作符。生成器使用yield操作符來(lái)建成梦重。
這里有一個(gè)用生成器生成整數(shù)的例子:

def generator():
 i = 0
 while True:
 i += 1
 yield i
for item in generator():
 print(item)
 if item > 4:
 break

1
2
3
4
5

使用圖像數(shù)據(jù)生成器來(lái)從目錄中讀取圖片

from keras.preprocessing.image import ImageDataGenerator
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
 # This is the target directory
 train_dir,
 # All images will be resized to 150x150
 target_size=(150, 150),
 batch_size=20,
 # Since we use binary_crossentropy loss, we need binary labels
 class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
 validation_dir,
 target_size=(150, 150),
 batch_size=20,
 class_mode='binary')

講解一下代碼:
首先定義train_datagen這個(gè)生成器琴拧,重置像素點(diǎn)大小的命令寫(xiě)在括號(hào)中蚓胸,然后validation_generator實(shí)際得到的是一個(gè)張量除师,張量形狀為(20,150,150,3)汛聚,而生成這個(gè)所用的就是生成器的flow_from_directory屬性,第一個(gè)參數(shù)填文件目錄八毯,第二個(gè)參數(shù)填將圖片重置的大小话速,第三個(gè)參數(shù)填每一批次取得個(gè)數(shù)芯侥,最后一個(gè)參數(shù)填標(biāo)簽類別柱查。

展示數(shù)據(jù)和標(biāo)簽

>>> for data_batch, labels_batch in train_generator:
>>> print('data batch shape:', data_batch.shape)
>>> print('labels batch shape:', labels_batch.shape)
>>> break
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)

讓我們開(kāi)始用生成器來(lái)擬合我們的模型唉工。我們使用fit_generator方法來(lái)進(jìn)行,這個(gè)我們的fit是等價(jià)的雹熬。先放代碼:

history = model.fit_generator(
 train_generator,
 steps_per_epoch=100,
 epochs=30,
 validation_data=validation_generator,
 validation_steps=50)

第一個(gè)參數(shù)是我們生成器竿报,第二個(gè)參數(shù)是每一批需要進(jìn)行的步數(shù)继谚,由于我們生成器每次生成20個(gè)數(shù)據(jù)花履,所以需要100步才能遍歷完2000個(gè)數(shù)據(jù),驗(yàn)證集的類似知道為什么是50.
每次訓(xùn)練完以后保存模型是個(gè)好習(xí)慣:

model.save('cats_and_dogs_small_1.h5')

接下來(lái)畫(huà)出訓(xùn)練和驗(yàn)證的損失值和成功率:

import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
Training and validation accuracy (training values as dots, validation values as solid lines)

Training and validation loss (training values as dots, validation values as solid lines)

這組圖表明過(guò)擬合了,我們的訓(xùn)練準(zhǔn)確率隨時(shí)間線性增加葬荷,直到最后接近100%宠漩,但我們的驗(yàn)證集停滯在了70-72%懊直。我們驗(yàn)證損失在5批次達(dá)到最小值以后就停滯了室囊,盡管訓(xùn)練損失持續(xù)降低,直到最后接近0.
因?yàn)槲覀冎挥昧撕苌俚挠?xùn)練數(shù)據(jù)2000個(gè)盼铁,過(guò)擬合是我們最關(guān)心的饶火。你已經(jīng)知道一系列方法去防止過(guò)擬合,比如dropout和權(quán)重衰減(L2正則化)当辐。我們現(xiàn)在要介紹一種新的缘揪,特別針對(duì)于計(jì)算機(jī)視覺(jué)的义桂,常常被用在深度學(xué)習(xí)模型中處理數(shù)據(jù)的:數(shù)據(jù)增加澡刹。

使用數(shù)據(jù)增加

過(guò)擬合是由于樣本太少造成的,導(dǎo)致我們無(wú)法訓(xùn)練模型去泛化新數(shù)據(jù)陆赋。給定無(wú)限的數(shù)據(jù)攒岛,我們的模型就會(huì)暴露在各種可能的數(shù)據(jù)分布情況中:我們從不會(huì)過(guò)擬合胞锰。數(shù)據(jù)增加采用了從存在的訓(xùn)練樣本中生成更多訓(xùn)練數(shù)據(jù)的方法嗅榕,通過(guò)一系列隨機(jī)的變換到可辨識(shí)的其它圖像凌那,來(lái)增加樣本數(shù)量。目的是在訓(xùn)練的時(shí)候赦肋,我們的模型不會(huì)重復(fù)看到同一張圖片兩次佃乘。這幫助模型暴露在更多數(shù)據(jù)面前,從而有更好的泛化性庞呕。
在keras里面千扶,我們可以通過(guò)ImageDataGenerator來(lái)生成一系列隨機(jī)變換骆捧。讓我們從一個(gè)例子開(kāi)始:

datagen = 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')

這里只列出了一小部分選項(xiàng)敛苇,想要了解更多枫攀,請(qǐng)看keras文檔株茶。
讓我們很快看一遍我們寫(xiě)了什么:

  • rotation_range是一個(gè)角度值(0-180)启盛,是隨機(jī)轉(zhuǎn)動(dòng)圖片的角度范圍僵闯。
  • width_shift和height_shift是隨機(jī)改變圖片對(duì)應(yīng)維度的比例。
  • shear_range是隨機(jī)剪切的比例
  • zoom_range是在圖片內(nèi)隨機(jī)縮放的比例
  • horizontal_flip是隨機(jī)將圖片水平翻轉(zhuǎn)社裆,當(dāng)沒(méi)有水平對(duì)稱假設(shè)時(shí)泳秀。
  • fill_mode在新出來(lái)像素以后榄攀,我們選擇填充的策略航攒。

讓我們看一看圖像增加:

# This is module with image preprocessing utilities
from keras.preprocessing import image
fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
# We pick one image to "augment"
img_path = fnames[3]
# Read the image and resize it
img = image.load_img(img_path, target_size=(150, 150))
# Convert it to a Numpy array with shape (150, 150, 3)
x = image.img_to_array(img)
# Reshape it to (1, 150, 150, 3)
x = x.reshape((1,) + x.shape)
# The .flow() command below generates batches of randomly transformed images.
# It will loop indefinitely, so we need to `break` the loop at some point!
i = 0
for batch in datagen.flow(x, batch_size=1):
 plt.figure(i)
 imgplot = plt.imshow(image.array_to_img(batch[0]))
 i += 1
 if i % 4 == 0:
 break
plt.show()
Generation of cat pictures via random data augmentation

雖然我們可以保證訓(xùn)練過(guò)程中币他,模型不會(huì)看到相同的兩張圖,但是畢竟我們只是對(duì)原圖混合了一下彰阴,并沒(méi)有增加什么新的信息尿这,所以無(wú)法完全避免過(guò)擬合庆杜,為了進(jìn)一步抗擊過(guò)擬合晃财,我們加入了dropout層:

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
 input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy',
 optimizer=optimizers.RMSprop(lr=1e-4),
 metrics=['acc'])

接下來(lái)讓我們使用數(shù)據(jù)增強(qiáng)和dropout來(lái)訓(xùn)練網(wǎng)絡(luò):

train_datagen = ImageDataGenerator(
 rescale=1./255,
 rotation_range=40,
 width_shift_range=0.2,
 height_shift_range=0.2,
 shear_range=0.2,
 zoom_range=0.2,
 horizontal_flip=True,)
# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
 # This is the target directory
 train_dir,
 # All images will be resized to 150x150
 target_size=(150, 150),
 batch_size=32,
 # Since we use binary_crossentropy loss, we need binary labels
 class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
 validation_dir,
 target_size=(150, 150),
 batch_size=32,
 class_mode='binary')
history = model.fit_generator(
 train_generator,
 steps_per_epoch=100,
 epochs=100,
 validation_data=validation_generator,
 validation_steps=50)

保存我們的模型:

model.save('cats_and_dogs_small_2.h5')

讓我們?cè)佼?huà)出訓(xùn)練和驗(yàn)證的結(jié)果看看:


Training and validation accuracy (training values as dots, validation values as solid lines)

Training and validation loss (training values as dots, validation values as solid lines)

多虧了數(shù)據(jù)增強(qiáng)和dropout,我們不再過(guò)擬合了:訓(xùn)練曲線和驗(yàn)證曲線十分相近钢猛。我們現(xiàn)在能夠達(dá)到82%的準(zhǔn)確率命迈,比未正則化的模型要提高了15%躺翻。
通過(guò)利用正則化方法,或者更進(jìn)一步:調(diào)參數(shù)踊淳,我們能達(dá)到更好的準(zhǔn)確率陕靠,近乎86-87%剪芥。然而税肪,這證明從零開(kāi)始訓(xùn)練我們的卷積網(wǎng)絡(luò)已經(jīng)難以更好了榜田,因?yàn)槲覀冎挥泻苌俚臄?shù)據(jù)來(lái)處理箭券。下一步我們提高準(zhǔn)確率的方法是利用預(yù)訓(xùn)練的網(wǎng)絡(luò)辩块,這將在接下來(lái)兩部分進(jìn)行講解荆永。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末具钥,一起剝皮案震驚了整個(gè)濱河市氓拼,隨后出現(xiàn)的幾起案子抵碟,更是在濱河造成了極大的恐慌拟逮,老刑警劉巖敦迄,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罚屋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡撕彤,警方通過(guò)查閱死者的電腦和手機(jī)羹铅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)职员,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)焊切,“玉大人,你說(shuō)我怎么就攤上這事糙箍∩詈唬” “怎么了诺苹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵掌呜,是天一觀的道長(zhǎng)坪哄。 經(jīng)常有香客問(wèn)我翩肌,道長(zhǎng),這世上最難降的妖魔是什么兑宇? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任隶糕,我火速辦了婚禮枚驻,結(jié)果婚禮上再登,老公的妹妹穿的比我還像新娘灾常。我一直安慰自己钞瀑,他們只是感情好雕什,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著磷雇,像睡著了一般躏救。 火紅的嫁衣襯著肌膚如雪盒使。 梳的紋絲不亂的頭發(fā)上少办,一...
    開(kāi)封第一講書(shū)人閱讀 49,929評(píng)論 1 290
  • 那天英妓,我揣著相機(jī)與錄音蔓纠,去河邊找鬼。 笑死航闺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的懈叹。 我是一名探鬼主播分扎,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼墨状,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼菲饼!你這毒婦竟也來(lái)了宏悦?” 一聲冷哼從身側(cè)響起包吝,我...
    開(kāi)封第一講書(shū)人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诗越,失蹤者是張志新(化名)和其女友劉穎息堂,沒(méi)想到半個(gè)月后荣堰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體持隧,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屡拨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年呀狼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哥艇。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绝编,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出貌踏,到底是詐尸還是另有隱情十饥,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布祖乳,位于F島的核電站逗堵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏眷昆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一亚斋、第九天 我趴在偏房一處隱蔽的房頂上張望作媚。 院中可真熱鬧,春花似錦帅刊、人聲如沸纸泡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)弟灼。三九已至级解,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間田绑,已是汗流浹背勤哗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掩驱,地道東北人芒划。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓阎姥,卻偏偏與公主長(zhǎng)得像续膳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秀又,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容