文章代碼來(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ù)如下所示:
不出意料的踪蹬,在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ò)大奴烙。這里,由于我們輸入的大小從開(kāi)始(隨便選的一個(gè))恰起,我們最終得到了的特征映射趾牧。
注意特征映射的深度從32提升到了128翘单,同時(shí)特征映射的大小在下降(從到)
由于我們?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()
這組圖表明過(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()
雖然我們可以保證訓(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é)果看看:
多虧了數(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)行講解荆永。