十二、計(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的生成器,從而快速將磁盤上圖片文件加入預(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閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

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