使用圖像增強來訓練小數據集(完成狗貓數據集的兩階段分類實驗,原始數據直接訓練和數據增強後訓練)

@ 什麼是 overfitting(過擬合)數據增強

overfitting(\color{#FFDDAA}{過}\color{#FFC8B4}{擬}\color{#FFCCCC}{合}): 過擬合(over-fitting)也稱爲過學習,它的直觀表現是算法在訓練集上表現好,但在測試集上表現不好,泛化性能差。過擬合是在模型參數擬合過程中由於訓練數據包含抽樣誤差,在訓練時複雜的模型將抽樣誤差也進行了擬合導致的。

所謂 抽樣誤差 ,是指抽樣得到的樣本集和整體數據集之間的偏差。

直觀來看,引起過擬合的可能原因有以下幾點

  • 模型本身過於複雜,以至於擬合了訓練樣本集中的噪聲。此時需要選用更簡單的模型,或者對模型進行裁剪。
  • 訓練樣本太少或者缺乏代表性。此時需要增加樣本數,或者增加樣本的多樣性。
  • 訓練樣本噪聲的干擾,導致模型擬合了這些噪聲,這時需要剔除噪聲數據或者改用對噪聲不敏感的模型。

數據增強: data augmentation,它的意思是讓有限的數據通過某種變換操作產生更多的等價數據的過程。數據增強主要用來防止過擬合,用於dataset較小的時候。

數據增強可以分爲,有監督的數據增強 無監督的數據增強 方法

有監督的數據增強分爲 單樣本數據增強 多樣本數據增強 方法;

無監督的數據增強分爲 生成新的數據 學習增強策略 兩個方向。

  
數據增強類型 初略解釋
單樣本數據增強 所謂單樣本數據增強,即增強一個樣本的時候,全部圍繞着該樣本本身進行操作,包括幾何變換類(包括翻轉,旋轉,裁剪,變形,縮放...),顏色變換類(包括噪聲、模糊、顏色變換、擦除、填充...)等
多樣本數據增強 不同於單樣本數據增強,多樣本數據增強方法利用多個樣本來產生新的樣本,包括SMOTE、 SamplePairing、mixup等
生成新的數據 通過模型學習數據的分佈,隨機生成與訓練數據集分佈一致的圖片,代表方法GAN

學習增強策略         
通過模型,學習出適合當前任務的數據增強方法,代表方法AutoAugment

解決計算機視覺運算應用到小數據集問題的策略

  • 從頭開始訓練一個小型模型
  • 使用預先訓練的模型進行特徵提取
  • 微調預先訓練的模型

一、查看運行的環境

(這裏Tensorflow和 Keras的版本要對應,否則要出錯!)
Platform Windows-7-6.1.7601-SP1
Tensorflow version 1.2.1
Keras version 2.1.2
import platform
import tensorflow
import keras

print("Platform: {}".format(platform.platform()))
print("Tensorflow version: {}".format(tensorflow.__version__))
print("Keras version: {}".format(keras.__version__))

二、 數據集準備

下載圖像數據集train.zip,在同一目錄下(Home)新建子目錄"data",把下載的圖像數據集train.zip複製到"data"目錄並解壓縮。具體如圖所示
在這裏插入圖片描述

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
from IPython.display import Image
import os

根目錄路徑

root_dir = os.getcwd()

存放圖像數據集的目錄

data_path = os.path.join(root_dir,'data')
import os,shutil

原始數據集的路徑

original_dataset_dir = os.path.join(data_path,'train')

存儲小數據集的目標

base_dir = os.path.join(data_path,'cats_and_dogs_small')
if not os.path.exists(base_dir):
    os.mkdir(base_dir)

訓練圖像的目錄

train_dir = os.path.join(base_dir,'train')
if not os.path.exists(train_dir):
    os.mkdir(train_dir)

驗證圖像的目錄

validation_dir = os.path.join(base_dir,'validation')
if not os.path.exists(validation_dir):
    os.mkdir(validation_dir)

測試資料的目錄

test_dir = os.path.join(base_dir,'test')
if not os.path.exists(test_dir):
    os.mkdir(test_dir)

貓的圖片的訓練資料的目錄

train_cats_dir = os.path.join(train_dir,'cats')
ifnot os.path.exists(train_cats_dir):
    os.mkdir(train_cats_dir)

狗的圖片的訓練資料的目錄

train_dogs_dir = os.path.join(train_dir,'dogs')
if not os.path.exists(train_dogs_dir):
    os.mkdir(train_dogs_dir)

貓的圖片的驗證集目錄

validation_cats_dir = os.path.join(validation_dir,'cats')
if not os.path.exists(validation_cats_dir):
    os.mkdir(validation_cats_dir)

狗的圖片的驗證集目錄

validation_dogs_dir = os.path.join(validation_dir,'dogs')
if not os.path.exists(validation_dogs_dir):
    os.mkdir(validation_dogs_dir)

貓的圖片的測試數據集目錄

test_cats_dir = os.path.join(test_dir,'cats')
if not os.path.exists(test_cats_dir):
    os.mkdir(test_cats_dir)

狗的圖片的測試數據集目錄

test_dogs_dir = os.path.join(test_dir,'dogs')
if not os.path.exists(test_dogs_dir):
    os.mkdir(test_dogs_dir)

複製前600個貓的圖片到train_cats_dir

fnames = ['cat.{}.jpg'.format(i) for i in range(600)]

for fname in fnames:
    src = os.path.join(original_dataset_dir,fname)
    dst = os.path.join(train_cats_dir,fname)
    if not os.path.exists(dst):
        shutil.copyfile(src,dst)
print("Copy next 600 cat images to train_cats_dir complete!")
Copy next 600 cat images to train_cats_dir complete!

複製後面400個貓的圖片到validation_cats_dir

fnames = ['cat.{}.jpg'.format(i) for i in range(1000,1400)]

for fname in fnames:
    src = os.path.join(original_dataset_dir,fname)
    dst = os.path.join(validation_cats_dir,fname)
    if not os.path.exists(dst):
        shutil.copyfile(src,dst)
print('Copy next 400 cat images to validation_cats_dir complete!')
Copy next 400 cat images to validation_cats_dir complete!

複製400張貓的圖片到test_cats_dir

fnames = ['cat.{}.jpg'.format(i) for i in range(1500,1900)]

for fname in fnames:
    src = os.path.join(original_dataset_dir,fname)
    dst = os.path.join(test_cats_dir,fname)
    if not os.path.exists(dst):
        shutil.copyfile(src,dst)
print("Copy next 400 cat images to test_cats_dir complete!")
Copy next 400 cat images to test_cats_dir complete!

複製前600張狗的圖片到train_dogs_dir

fnames = ['dog.{}.jpg'.format(i) for i in range(600)]

for fname in fnames:
    src = os.path.join(original_dataset_dir,fname)
    dst = os.path.join(train_dogs_dir,fname)
    if not os.path.exists(dst):
        shutil.copyfile(src,dst)
print("Copy first 600 dog images to train_dogs_dir complete!")
Copy first 600 dog images to train_dogs_dir complete!

複製後面400個狗的圖片到validation_dogs_dir

fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1400)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    if not os.path.exists(dst):
        shutil.copyfile(src, dst)

print('Copy next 400 dog images to validation_dogs_dir complete!')
Copy next 400 dog images to validation_dogs_dir complete!

複製400張狗的圖片到test_dogs_dir

fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 1900)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    if not os.path.exists(dst):
        shutil.copyfile(src, dst)

print('Copy next 400 dog images to test_dogs_dir complete!')
Copy next 400 dog images to test_dogs_dir complete!

進行一次檢查,計算每個分組中有多少張照片(訓練/驗證/測試)

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

有1200個訓練圖像,然後是600個驗證圖像,600個測試圖像,其中每個分類都有相同數量的樣本,是一個平衡的二元分類問題,意味着分類準確度將是合適的度量標準。

三、資料預處理

網絡的預處理步驟:

  • 讀入圖像
  • 將JPEG內容解碼爲RGB網格的像素
  • 將其轉換爲浮點張量
  • 將像素值(0和255之間)重新縮放到[0,1]間隔

數據應該先被格式化成適當的預處理浮點張量,然後才能輸入到神經網絡中。

from keras.preprocessing.image import ImageDataGenerator

# 所有的圖像將重新進行歸一化處理 Rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

# 直接從目錄讀取圖像數據
train_generator = train_datagen.flow_from_directory(
    # 訓練圖像的目錄
    train_dir,
    # 所有圖像大小會被轉換成150x150
    target_size=(150,150),
    # 每次產生20個圖像的批次
    batch_size = 20,
    # 由於這是一個二元分類問題,y的label值也會被轉換成二元的標籤
    class_mode = 'binary')

# 直接從目錄讀取圖像數據
validation_generator = test_datagen.flow_from_directory(
    validation_dir,
    target_size=(150,150),
    batch_size = 20,
    class_mode='binary')
Found 1200 images belonging to 2 classes.
Found 800 images belonging to 2 classes.

圖像張量生成器(generator)的輸出,它產生150x150 RGB圖像(形狀"(20,150,150,3)")和二進制標籤(形狀"(20,)")的批次張量。20是每個批次中的樣品數(批次大小)

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,)

四、網絡模型

卷積網絡(convnets)將是一組交替的Conv2D(具有relu激活)和MaxPooling2D層。從大小150x150(有點任意選擇)的輸入開始,我們最終得到了尺寸爲7x7的Flatten層之前的特徵圖。

注意特徵圖的深度在網絡中逐漸增加(從32到128),而特徵圖的大小正在減少(從148x148到7x7)。這是一個你將在幾乎所有的卷積網絡(convnets)結構中會看到的模式。

由於我們正在處理二元分類問題,所以我們用一個神經元(一個大小爲1的密集層(Dense))和一個sigmoid激活函數來結束網絡。該神經元將會被用來查看圖像歸屬於那一類或另一類的概率。

from keras import layers
from keras import models
from keras.utils import plot_model

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

看特徵圖的尺寸如何隨着每個連續的圖層而改變,打印網絡結構

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_5 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_6 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainabl params: 3,453,121
Non-trainable params: 0
_________________________________________________________________

在編譯步驟裏,使用RMSprop優化器。由於用一個單一的神經元(Sigmoid的激活函數)結束了網絡,將使用二進制交叉熵(binary crossentropy)作爲損失函數。

from keras import optimizers

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

五、訓練

history = model.fit_generator(train_generator,
                             steps_per_epoch=100,
                             epochs=30,
                             validation_data=validation_generator,
                             validation_steps=50)
Epoch 1/30
100/100 [==============================] - 199s 2s/step - loss: 0.6854 - acc: 0.5455 - val_loss: 0.6889 - val_acc: 0.5050
Epoch 2/30
100/100 [==============================] - 197s 2s/step - loss: 0.6512 - acc: 0.6280 - val_loss: 0.6540 - val_acc: 0.6210
Epoch 3/30
100/100 [==============================] - 193s 2s/step - loss: 0.5878 - acc: 0.6890 - val_loss: 0.6632 - val_acc: 0.6040
Epoch 4/30
100/100 [==============================] - 193s 2s/step - loss: 0.5357 - acc: 0.7235 - val_loss: 0.6050 - val_acc: 0.6750
Epoch 5/30
100/100 [==============================] - 193s 2s/step - loss: 0.4880 - acc: 0.7625 - val_loss: 0.6080 - val_acc: 0.6860
Epoch 6/30
100/100 [==============================] - 198s 2s/step - loss: 0.4441 - acc: 0.7980 - val_loss: 0.6200 - val_acc: 0.6800
Epoch 7/30
100/100 [==============================] - 204s 2s/step - loss: 0.3964 - acc: 0.8155 - val_loss: 0.6721 - val_acc: 0.6650
Epoch 8/30
100/100 [==============================] - 200s 2s/step - loss: 0.3592 - acc: 0.8415 - val_loss: 0.6579 - val_acc: 0.6740
Epoch 9/30
100/100 [==============================] - 198s 2s/step - loss: 0.3179 - acc: 0.8660 - val_loss: 0.6550 - val_acc: 0.6940
Epoch 10/30
100/100 [==============================] - 208s 2s/step - loss: 0.2785 - acc: 0.8865 - val_loss: 0.6938 - val_acc: 0.6920
Epoch 11/30
100/100 [==============================] - 214s 2s/step - loss: 0.2472 - acc: 0.9040 - val_loss: 0.7478 - val_acc: 0.6890
Epoch 12/30
100/100 [==============================] - 215s 2s/step - loss: 0.2189 - acc: 0.9160 - val_loss: 0.7710 - val_acc: 0.7030
Epoch 13/30
100/100 [==============================] - 206s 2s/step - loss: 0.1859 - acc: 0.9290 - val_loss: 0.7461 - val_acc: 0.7120
Epoch 14/30
100/100 [==============================] - 199s 2s/step - loss: 0.1484 - acc: 0.9495 - val_loss: 0.8131 - val_acc: 0.6930
Epoch 15/30
100/100 [==============================] - 208s 2s/step - loss: 0.1313 - acc: 0.9585 - val_loss: 1.0099 - val_acc: 0.6970
Epoch 16/30
100/100 [==============================] - 213s 2s/step - loss: 0.1059 - acc: 0.9675 - val_loss: 0.9962 - val_acc: 0.6960
Epoch 17/30
100/100 [==============================] - 209s 2s/step - loss: 0.0831 - acc: 0.9715 - val_loss: 1.1409 - val_acc: 0.6780
Epoch 18/30
100/100 [==============================] - 202s 2s/step - loss: 0.0769 - acc: 0.9770 - val_loss: 1.1242 - val_acc: 0.7020
Epoch 19/30
100/100 [==============================] - 209s 2s/step - loss: 0.0583 - acc: 0.9810 - val_loss: 1.2149 - val_acc: 0.7000
Epoch 20/30
100/100 [==============================] - 197s 2s/step - loss: 0.0478 - acc: 0.9875 - val_loss: 1.2307 - val_acc: 0.7060
Epoch 21/30
100/100 [==============================] - 198s 2s/step - loss: 0.0417 - acc: 0.9895 - val_loss: 1.3518 - val_acc: 0.6810
Epoch 22/30
100/100 [==============================] - 196s 2s/step - loss: 0.0397 - acc: 0.9875 - val_loss: 1.3366 - val_acc: 0.7160
Epoch 23/30
100/100 [==============================] - 203s 2s/step - loss: 0.0282 - acc: 0.9930 - val_loss: 1.3750 - val_acc: 0.6870
Epoch 24/30
100/100 [==============================] - 199s 2s/step - loss: 0.0203 - acc: 0.9940 - val_loss: 1.4814 - val_acc: 0.7020
Epoch 25/30
100/100 [==============================] - 202s 2s/step - loss: 0.0235 - acc: 0.9920 - val_loss: 1.5932 - val_acc: 0.6940
Epoch 26/30
100/100 [==============================] - 205s 2s/step - loss: 0.0207 - acc: 0.9935 - val_loss: 3.0777 - val_acc: 0.6060
Epoch 27/30
100/100 [==============================] - 213s 2s/step - loss: 0.0253 - acc: 0.9935 - val_loss: 1.7332 - val_acc: 0.6850
Epoch 28/30
100/100 [==============================] - 207s 2s/step - loss: 0.0171 - acc: 0.9940 - val_loss: 1.5884 - val_acc: 0.6910
Epoch 29/30
100/100 [==============================] - 197s 2s/step - loss: 0.0083 - acc: 0.9980 - val_loss: 1.7505 - val_acc: 0.6990
Epoch 30/30
100/100 [==============================] - 197s 2s/step - loss: 0.0178 - acc: 0.9930 - val_loss: 1.6967 - val_acc: 0.6940

訓練完成後把模型保存下來

model.save('cats_and_dogs_small_2.h5') 

使用圖表來秀出在訓練過程中模型對訓練和驗證數據的損失(loss)和準確性(accuracy)數據

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(len(acc))

plt.plot(epochs,acc,label='Training acc')
plt.plot(epochs,val_acc,label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,loss,label='Training loss')
plt.plot(epochs,val_loss,label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

在這裏插入圖片描述
在這裏插入圖片描述
上面圖標顯示了過度擬合(overfitting)的特徵。我們的訓練精確度隨着時間線性增長,直到接近100%,然而我們的驗證精度卻停在70%~72%。我們的驗證損失在第五個循環(epochs)之後達到最小值,然後停頓,而訓練損失在線性上保持下降直到接近0。

因爲我們只有相對較少的訓練數據(2000個),過度擬合將成爲我們的首要的關注點。你已經知道了許多可以幫助減輕過度擬合的技巧。例如Dropout和權重衰減(L2正則化)。我們現在要引入一個新的,特定於電腦視覺影像,並在使用深度學習模型處理圖像時幾乎普遍使用的技巧:數據擴充(data augmentation)。

六、使用數據填充

過度擬合是由於樣本數量太少而導致的,導致我們無法訓練能夠推廣到新數據的模型。

給定無限數據,我們的模型將暴露在手頭數據分佈的每個可能的方向:我們永遠不會過度。數據增加採用從現有訓練樣本生成更多訓練數據的方法,通過產生可信的圖像的多個隨機變換來"增加"樣本。目標是在訓練的時候,我們的模型永遠不會再看到完全相同的畫面兩次。這有助於模型暴學習到數據的更多方面,並更好地推廣。

在keras中,可以通過配置對我們的ImageDataGenerator實例讀取的圖像執行多個隨機變換來完成。

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

這些只是列出一些可用的選項(更多選項,可以參考keras文檔)。快速看一下這些參數:

  • rotation_range是以度(0-180)爲單位的值,它是隨機旋轉圖片的範圍。
  • width_shift和height_shift是範圍(佔總寬度或高度的一小部分),用於縱向或橫向隨機轉換圖片。
  • shear_range用於隨機剪切變換。
  • zoom_range用於隨機放大圖片內容。
  • horizontal_flip用於在沒有水平不對稱假設(例如真實世界圖片)的情況下水平地隨機翻轉一半圖像。
  • fill_mode是用於填充新創建的像素的策略,可以在旋轉或寬/高移位後顯示。

看增強後的圖像:

import matplotlib.pyplot as plt
from keras.preprocessing import image

# 取得訓練數據集中貓的圖片列表
fnames = [os.path.join(train_cats_dir,fname) for fname in os.listdir(train_cats_dir)]

# 取一個圖像
img_path = fnames[29]

# 讀入圖像並進行大小處理
img = image.load_img(img_path,target_size=(150,150))

# 轉換成Numpy array 並且shape(150,150,3)
x = image.img_to_array(img)

# 重新Reshape成(1,150,150,3)以便輸入到模型中
x = x.reshape((1,) + x.shape)

# 通過flow()方法將會隨機產生新的圖像
# 它會無線循環,所以我們需要在某個時候“斷開”循環
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()

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
如果我們使用這種數據增強配置來訓練一個新的網絡,我們的網絡將永遠不會看到相同重複的輸入。然而,它看到的輸入仍然是相互關聯的,因爲它們來自少量的原始圖像 - 我們不能產生新的信息,我們只能重新混合現有的信息。因此,這可能不足以完全擺脫過度擬合(overfitting)。爲了進一步克服過度擬合(overfitting),我們還將在密集連接(densely-connected)的分類器之前添加一個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'])

使用數據填充(data augmentation)和dropout來訓練我們的網絡

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)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
# 這是圖像資料的目錄
train_dir,
# 所有的圖像大小會被轉換成150x150
target_size=(150,150),
batch_size=32,
# 由於這是一個二元分類問題,y的label值也會被轉換成二元的標籤
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=50,
                             validation_data=validation_generator,
                             validation_steps=50)
Found 1200 images belonging to 2 classes.
Found 800 images belonging to 2 classes.
Epoch 1/50
100/100 [==============================] - 314s 3s/step - loss: 0.6923 - acc: 0.5144 - val_loss: 0.6824 - val_acc: 0.5925
Epoch 2/50
100/100 [==============================] - 308s 3s/step - loss: 0.6757 - acc: 0.5847 - val_loss: 0.6538 - val_acc: 0.6050
Epoch 3/50
100/100 [==============================] - 323s 3s/step - loss: 0.6595 - acc: 0.6072 - val_loss: 0.6367 - val_acc: 0.6312
Epoch 4/50
100/100 [==============================] - 310s 3s/step - loss: 0.6513 - acc: 0.6178 - val_loss: 0.6296 - val_acc: 0.6375
Epoch 5/50
100/100 [==============================] - 305s 3s/step - loss: 0.6292 - acc: 0.6384 - val_loss: 0.6251 - val_acc: 0.6412
Epoch 6/50
100/100 [==============================] - 323s 3s/step - loss: 0.6186 - acc: 0.6409 - val_loss: 0.5853 - val_acc: 0.6850
Epoch 7/50
100/100 [==============================] - 326s 3s/step - loss: 0.5994 - acc: 0.6653 - val_loss: 0.6025 - val_acc: 0.6675
Epoch 8/50
100/100 [==============================] - 330s 3s/step - loss: 0.5914 - acc: 0.6719 - val_loss: 0.5797 - val_acc: 0.6787
Epoch 9/50
100/100 [==============================] - 311s 3s/step - loss: 0.5738 - acc: 0.6956 - val_loss: 0.5685 - val_acc: 0.6825
Epoch 10/50
100/100 [==============================] - 316s 3s/step - loss: 0.5791 - acc: 0.6881 - val_loss: 0.5778 - val_acc: 0.6837
Epoch 11/50
100/100 [==============================] - 313s 3s/step - loss: 0.5674 - acc: 0.6991 - val_loss: 0.5428 - val_acc: 0.7350
Epoch 12/50
100/100 [==============================] - 318s 3s/step - loss: 0.5584 - acc: 0.7075 - val_loss: 0.5377 - val_acc: 0.7300
Epoch 13/50
100/100 [==============================] - 320s 3s/step - loss: 0.5501 - acc: 0.7144 - val_loss: 0.5348 - val_acc: 0.7050
Epoch 14/50
100/100 [==============================] - 310s 3s/step - loss: 0.5424 - acc: 0.7194 - val_loss: 0.5290 - val_acc: 0.7188
Epoch 15/50
100/100 [==============================] - 309s 3s/step - loss: 0.5357 - acc: 0.7269 - val_loss: 0.5169 - val_acc: 0.7250
Epoch 16/50
100/100 [==============================] - 311s 3s/step - loss: 0.5364 - acc: 0.7309 - val_loss: 0.5184 - val_acc: 0.7450
Epoch 17/50
100/100 [==============================] - 305s 3s/step - loss: 0.5291 - acc: 0.7331 - val_loss: 0.5098 - val_acc: 0.7375
Epoch 18/50
100/100 [==============================] - 314s 3s/step - loss: 0.5249 - acc: 0.7322 - val_loss: 0.5514 - val_acc: 0.7087
Epoch 19/50
100/100 [==============================] - 313s 3s/step - loss: 0.5151 - acc: 0.7356 - val_loss: 0.5113 - val_acc: 0.7500
Epoch 20/50
100/100 [==============================] - 318s 3s/step - loss: 0.5173 - acc: 0.7466 - val_loss: 0.5023 - val_acc: 0.7475
Epoch 21/50
100/100 [==============================] - 310s 3s/step - loss: 0.5124 - acc: 0.7419 - val_loss: 0.4960 - val_acc: 0.7412
Epoch 22/50
100/100 [==============================] - 306s 3s/step - loss: 0.5062 - acc: 0.7481 - val_loss: 0.5011 - val_acc: 0.7475
Epoch 23/50
100/100 [==============================] - 306s 3s/step - loss: 0.5036 - acc: 0.7522 - val_loss: 0.5758 - val_acc: 0.7013
Epoch 24/50
100/100 [==============================] - 309s 3s/step - loss: 0.5075 - acc: 0.7428 - val_loss: 0.5008 - val_acc: 0.7575
Epoch 25/50
100/100 [==============================] - 306s 3s/step - loss: 0.4856 - acc: 0.7703 - val_loss: 0.5587 - val_acc: 0.7188
Epoch 26/50
100/100 [==============================] - 306s 3s/step - loss: 0.5064 - acc: 0.7431 - val_loss: 0.5091 - val_acc: 0.7350
Epoch 27/50
100/100 [==============================] - 304s 3s/step - loss: 0.4979 - acc: 0.7528 - val_loss: 0.4903 - val_acc: 0.7538
Epoch 28/50
100/100 [==============================] - 315s 3s/step - loss: 0.4837 - acc: 0.7684 - val_loss: 0.4861 - val_acc: 0.7575
Epoch 29/50
100/100 [==============================] - 307s 3s/step - loss: 0.4809 - acc: 0.7675 - val_loss: 0.5019 - val_acc: 0.7438
Epoch 30/50
100/100 [==============================] - 313s 3s/step - loss: 0.4730 - acc: 0.7675 - val_loss: 0.4818 - val_acc: 0.7650
Epoch 31/50
100/100 [==============================] - 311s 3s/step - loss: 0.4684 - acc: 0.7722 - val_loss: 0.4748 - val_acc: 0.7750
Epoch 32/50
100/100 [==============================] - 304s 3s/step - loss: 0.4729 - acc: 0.7706 - val_loss: 0.4859 - val_acc: 0.7562
Epoch 33/50
100/100 [==============================] - 304s 3s/step - loss: 0.4720 - acc: 0.7690 - val_loss: 0.4828 - val_acc: 0.7588
Epoch 34/50
100/100 [==============================] - 305s 3s/step - loss: 0.4576 - acc: 0.7791 - val_loss: 0.4704 - val_acc: 0.7638
Epoch 35/50
100/100 [==============================] - 307s 3s/step - loss: 0.4622 - acc: 0.7772 - val_loss: 0.5016 - val_acc: 0.7488
Epoch 36/50
100/100 [==============================] - 308s 3s/step - loss: 0.4478 - acc: 0.7912 - val_loss: 0.4732 - val_acc: 0.7662
Epoch 37/50
100/100 [==============================] - 312s 3s/step - loss: 0.4517 - acc: 0.7847 - val_loss: 0.4694 - val_acc: 0.7800
Epoch 38/50
100/100 [==============================] - 309s 3s/step - loss: 0.4617 - acc: 0.7903 - val_loss: 0.4896 - val_acc: 0.7600
Epoch 39/50
100/100 [==============================] - 322s 3s/step - loss: 0.4453 - acc: 0.7869 - val_loss: 0.5066 - val_acc: 0.7425
Epoch 40/50
100/100 [==============================] - 314s 3s/step - loss: 0.4376 - acc: 0.7931 - val_loss: 0.4877 - val_acc: 0.7650
Epoch 41/50
100/100 [==============================] - 312s 3s/step - loss: 0.4461 - acc: 0.7894 - val_loss: 0.4752 - val_acc: 0.7512
Epoch 42/50
100/100 [==============================] - 327s 3s/step - loss: 0.4432 - acc: 0.7856 - val_loss: 0.4652 - val_acc: 0.7825
Epoch 43/50
100/100 [==============================] - 310s 3s/step - loss: 0.4267 - acc: 0.8066 - val_loss: 0.4942 - val_acc: 0.7612
Epoch 44/50
100/100 [==============================] - 310s 3s/step - loss: 0.4440 - acc: 0.7925 - val_loss: 0.4828 - val_acc: 0.7575
Epoch 45/50
100/100 [==============================] - 306s 3s/step - loss: 0.4260 - acc: 0.7978 - val_loss: 0.5165 - val_acc: 0.7638
Epoch 46/50
100/100 [==============================] - 306s 3s/step - loss: 0.4272 - acc: 0.8003 - val_loss: 0.4672 - val_acc: 0.7812
Epoch 47/50
100/100 [==============================] - 306s 3s/step - loss: 0.4151 - acc: 0.8056 - val_loss: 0.4658 - val_acc: 0.7775
Epoch 48/50
100/100 [==============================] - 315s 3s/step - loss: 0.4130 - acc: 0.8122 - val_loss: 0.6227 - val_acc: 0.7013
Epoch 49/50
100/100 [==============================] - 317s 3s/step - loss: 0.4318 - acc: 0.8016 - val_loss: 0.4559 - val_acc: 0.7925
Epoch 50/50
100/100 [==============================] - 310s 3s/step - loss: 0.4062 - acc: 0.8144 - val_loss: 0.4602 - val_acc: 0.7812

保存模型,將在convnet可視化裏使用它

model.save('cats_and_dogs_small_3.h5')

看一遍結果:

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs,acc,label='Training acc')
plt.plot(epochs,val_acc,label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,loss, label='Training loss')
plt.plot(epochs,val_loss,label='Validation acc')
plt.title('Training and validation loss')
plt.legend()

plt.show()

在這裏插入圖片描述
在這裏插入圖片描述
由於數據增加(data augmentation)和dropout使用,不再有過度擬合(overfitting)的問題:訓練曲線相當密切地跟隨着驗證曲線。現在能夠達到82%的準確度,比非正規的模型相比改善了15%。通過進一步利用正規化技術,及調整網絡參數(例如每個卷積層的濾波器數量或網絡層數),可以獲得更好的準確度,可能高達86~87%。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章