Python--基於卷積神經網絡的圖像分類

什麼是過擬合

過擬合是指爲了得到一致假設而使假設變得過度嚴格。避免過擬合是分類器設計中的一個核心任務。通常採用增大數據量和測試樣本集的方法對分類器性能進行評價。

定義
給定一個假設空間H,一個假設h屬於H,如果存在其他的假設h’屬於H,使得在訓練樣例上h的錯誤率比h’小,但在整個實例分佈上h’比h的錯誤率小,那麼就說假設h過度擬合訓練數據。

一個假設在訓練數據上能夠獲得比其他假設更好的擬合, 但是在訓練數據外的數據集上卻不能很好地擬合數據,此時認爲這個假設出現了過擬合的現象。出現這種現象的主要原因是訓練數據中存在噪音或者訓練數據太少

什麼是數據增強

數據增強
數據增強主要用來防止過擬合,用於dataset較小的時候。
當然除了數據增強,還有正則項/dropout等方式可以防止過擬合。那接下來討論下常見的數據增強方法。

1)隨機旋轉
隨機旋轉一般情況下是對輸入圖像隨機旋轉[0,360)
2)隨機裁剪
隨機裁剪是對輸入圖像隨機切割掉一部分
3)色彩抖動
色彩抖動指的是在顏色空間如RGB中,每個通道隨機抖動一定的程度。在實際的使用中,該方法不常用,在很多場景下反而會使實驗結果變差
4)高斯噪聲
是指在圖像中隨機加入少量的噪聲。該方法對防止過擬合比較有效,這會讓神經網絡不能擬合輸入圖像的所有特徵
5)水平翻轉
6)豎直翻轉

python代碼

import keras
keras.__version__



# 對小數據集使用卷積網絡

本筆記本包含[ Python深度學習 ](https://www.manning.com/books/deep-learning-with-python?a_aid=keras&a_bid=76564dff)第5章第2節中提供的代碼示例。請注意,原始文本具有更多內容,尤其是進一步的說明和附圖:在本筆記本中,您將僅找到源代碼和相關注釋。

在小數據集上從頭訓練convnet

通常只需要很少的數據就可以訓練圖像分類模型,這是一種常見的情況,如果您在專業環境中進行計算機視覺,您可能會在實踐中遇到自己。

具有“少量”樣本可能意味着從幾百到幾萬張圖像。作爲一個實際示例,我們將重點關注在包含4000張貓和狗(2000只貓,2000只狗)圖片的數據集中,將圖像分類爲“狗”還是“貓”。我們將使用2000 張圖片進行培訓,使用1000張圖片進行驗證,最後使用1000張圖片進行測試。

在本節中,我們將介紹解決該問題的一種基本策略:從頭開始訓練我們擁有的少量數據的新模型。我們將首先對2000個培訓樣本進行一次簡單的convnet培訓,而無需進行任何正則化,從而爲可以實現的目標設定基準。這將使我們的分類精度達到71%。到那時,我們的主要問題將是過度擬合。然後,我們將介紹數據增強,這是一種緩解計算機視覺過度擬合的強大技術。通過利用數據增強,我們將改善我們的網絡以達到82%的準確性。

在下一部分中,我們將介紹另外兩種將深度學習應用於小型數據集的基本技術:*
使用預先訓練的網絡進行特徵提取*(這將使我們的準確度達到90%至93%),並且很好-調整預訓練的網絡(這將使我們的最終精度達到95%)。這三種策略一起使用-從頭開始訓練一個小型模型,使用預訓練的模型進行特徵提取以及對預訓練的模型進行微調-將構成您將來解決使用小工具進行計算機視覺問題的工具箱數據集。

深度學習與小數據問題的相關性

有時您會聽到深度學習僅在有大量數據可用時才起作用。這在一定程度上是正確的:深度學習的一個基本特徵是,它能夠自己在訓練數據中找到有趣的特徵,而無需進行人工特徵工程,而只有在有大量訓練示例時才能實現可用。對於輸入樣本非常高維的問題(例如圖像),尤其如此。

但是,構成樣本的“批次”是相對的-對於初學者而言,與您要訓練的網絡的大小和深度有關。僅用幾十個樣本就不可能訓練卷積網絡來解決複雜的問題,但是如果模型很小且規則良好,並且任務很簡單,那麼數百個就足夠了。因爲卷積學習局部不變的平移特徵,所以它們在感知問題上的數據效率很高。儘管相對缺乏數據,但在非常小的圖像數據集上從頭開始訓練卷積網絡仍會產生合理的結果,而無需任何自定義特徵工程。您將在本節中看到這一點。

但是更重要的是,深度學習模型本質上具有高度的可重複性:例如,您可以採用在大規模數據集上訓練的圖像分類或語音到文本模型,然後僅需很小的更改就可以將其重用於一個明顯不同的問題。具體來說,在計算機視覺的情況下,許多預先訓練的模型(通常在ImageNet數據集上進行訓練)現在可以公開下載,並且可以用於從很少的數據中引導功能強大的視覺模型。這就是我們在下一節中要做的。

現在,讓我們開始使用數據。

下載數據

我們將使用的貓與狗數據集未與Keras打包在一起。Kavgle.com 在2013年底的計算機視覺競賽中提供了它,當時卷積網絡還不是很主流。您可以下載原始數據集:https://www.kaggle.com/c/dogs-vs-cats/data(你需要創建一個帳戶Kaggle如果您還沒有一個-不要”不用擔心,這個過程很輕鬆)。

毫不奇怪,2013年的貓對狗Kaggle比賽由使用卷積網絡的參賽者贏得。最好的輸入可以達到95%的準確性。在我們自己的示例中,即使我們將在少於競爭對手可用數據的10%的數據上訓練模型,也將相當接近此準確性(在下一部分中)。該原始數據集包含25,000張貓和狗的圖像(每類12,500張),並且大小爲543MB(壓縮)。下載並解壓縮後,我們將創建一個包含三個子集的新數據集:一個訓練集,每個類別有1000個樣本;一個驗證集,每個類別有500個樣本;最後是一個測試集,每個類別有500個樣本。

這是執行此操作的幾行代碼:

import os, shutil
# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = './mycatdog'

# The directory where we will
# store our smaller dataset
base_dir = './out1'
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(10)]
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(10, 20)]
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(20, 30)]
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(10)]
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(10, 20)]
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(20, 30)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

作爲健全性檢查,讓我們計算一下每個訓練分組(訓練/驗證/測試)中有多少張圖片:

print('total training cat images:', len(os.listdir(train_cats_dir)))
 總訓練貓的圖像:10
print('total training dog images:', len(os.listdir(train_dogs_dir)))
 總訓練狗的圖像:10
print('total validation cat images:', len(os.listdir(validation_cats_dir)))
 總驗證貓的圖像:10
print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
 總驗證狗的圖像:10
print('total test cat images:', len(os.listdir(test_cats_dir)))
 總驗證狗的圖像:10
print('total test dog images:', len(os.listdir(test_dogs_dir)))
 總驗證狗的圖像:10

所以我們確實有2000個訓練圖像,然後是1000個驗證圖像和1000個測試圖像。在每個分組中,每個類別中的樣本數均相同:這是一個平衡的二進制分類問題,這意味着分類準確性將是成功與否的適當衡量標準。

建立我們的網絡

在上一個示例中,我們已經爲MNIST構建了一個小型卷積網絡,因此您應該熟悉它們。我們將重複使用相同的基本結構:我們convnet將是交替的堆棧Conv2D(帶RELU激活)和MaxPooling2D層。

然而,由於我們處理的是更大的圖像和更復雜的問題,我們將盡我們的網絡因此大:它有一個更Conv2D + MaxPooling2D階段。這既可以增加網絡的容量,又可以進一步減小要素圖的大小,這樣當我們到達“扁平化”層時,它們就不會太大。在這裏,由於我們從大小爲150x150的輸入(某種程度上是任意選擇)開始,因此我們在“展平”層之前得到了大小爲7x7的要素圖。

請注意,特徵圖的深度在網絡中逐漸增加(從32到128),而特徵圖的大小在減小(從148x148到7x7)。在幾乎所有的卷積網絡中都會看到這種模式。

由於我們正在攻擊二進制分類問題,因此我們以單個單元(大小爲1 的“密集”層)和“ sigmoid ”激活來結束網絡。該單元將對網絡正在查看一個或另一個類別的概率進行編碼。

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

讓我們來看看要素地圖的尺寸是如何隨每個連續圖層而變化的:

model.summary()
Model: "sequential_1"
_________________________________________________________________
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_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
_________________________________________________________________

對於我們的編譯步驟,我們將與去RMSprop優化如常。由於我們以單個S形單位結束網絡,因此我們將使用二進制交叉熵作爲損失(提醒一下,請參閱第4章第5節中的表格,以瞭解在各種情況下要使用的損失函數的備忘單)。

from keras import optimizers

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

數據預處理

如您現在所知,在將數據饋入我們的網絡之前,應將其格式化爲經過適當預處理的浮點張量。當前,我們的數據以JPEG文件的形式位於驅動器上,因此將其放入網絡的步驟大致如下:

  • 讀取圖片文件。* 將JPEG內容解碼爲RBG像素網格。* 將它們轉換爲浮點張量。* 將像素值(0到255之間)重新縮放爲[0,1]間隔(如您所知,神經網絡更喜歡處理較小的輸入值)。

似乎有些令人生畏,但值得慶幸的是Keras擁有實用程序來自動完成這些步驟。Keras與圖像的模塊處理輔助工具,位於keras.preprocessing.image。特別是,它包含的類ImageDataGenerator,它允許快速設置Python生成器,可以自動打開圖像文件在磁盤上爲預處理張量的批次。這就是我們將在這裏使用的。

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')
Found 20 images belonging to 2 classes.
Found 20 images belonging to 2 classes.

實測值20個圖像屬於2類。 找到20個屬於2類的圖像。讓我們看一看這些生成器之一的輸出:它生成一批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,)

讓我們使用生成器將模型擬合到數據。我們用它做fit_generator方法,相當於適合數據生成像我們這樣。它期望Python生成器作爲第一個參數,像我們一樣,將無限期地產生大量輸入和目標。由於數據是無休止地生成的,因此生成器需要在聲明一個時期之前知道示例要從生成器中抽取多少個樣本。這是角色steps_per_epoch說法:具有延伸後steps_per_epoch從批次發電機,具有運行後,即steps_per_epoch梯度下降步驟,擬合過程將進入下一個時代。在我們的示例中,批次爲20個樣本,因此將需要100個批次,直到我們看到我們的目標是2000個樣本。期望該生成器無休止地生成驗證數據的批次,因此您還應該指定validation_steps參數,該參數告訴過程從驗證生成器中抽取多少批次進行評估。

history = model.fit_generator(
      train_generator,
      steps_per_epoch=80,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)
Epoch 1/30
80/80 [==============================] - 48s 605ms/step - loss: 0.2500 - acc: 0.9200 - val_loss: 0.6217 - val_acc: 0.6000
Epoch 2/30
80/80 [==============================] - 45s 560ms/step - loss: 0.0012 - acc: 1.0000 - val_loss: 1.0331 - val_acc: 0.5500
Epoch 3/30
80/80 [==============================] - 46s 573ms/step - loss: 6.8918e-06 - acc: 1.0000 - val_loss: 1.5310 - val_acc: 0.5500
Epoch 4/30
80/80 [==============================] - 42s 529ms/step - loss: 1.1915e-07 - acc: 1.0000 - val_loss: 1.9704 - val_acc: 0.5500
Epoch 5/30
80/80 [==============================] - 42s 525ms/step - loss: 1.6333e-08 - acc: 1.0000 - val_loss: 2.2112 - val_acc: 0.5500
Epoch 6/30
80/80 [==============================] - 41s 512ms/step - loss: 1.1360e-08 - acc: 1.0000 - val_loss: 2.2967 - val_acc: 0.5500
Epoch 7/30
80/80 [==============================] - 41s 517ms/step - loss: 1.0666e-08 - acc: 1.0000 - val_loss: 2.3855 - val_acc: 0.5500
Epoch 8/30
80/80 [==============================] - 41s 517ms/step - loss: 1.0514e-08 - acc: 1.0000 - val_loss: 2.4290 - val_acc: 0.5500
Epoch 9/30
80/80 [==============================] - 41s 508ms/step - loss: 1.0513e-08 - acc: 1.0000 - val_loss: 2.4661 - val_acc: 0.5500
Epoch 10/30
80/80 [==============================] - 40s 505ms/step - loss: 1.0506e-08 - acc: 1.0000 - val_loss: 2.4857 - val_acc: 0.5500
Epoch 11/30
80/80 [==============================] - 40s 506ms/step - loss: 1.0329e-08 - acc: 1.0000 - val_loss: 2.5173 - val_acc: 0.5500
Epoch 12/30
80/80 [==============================] - 43s 537ms/step - loss: 1.0348e-08 - acc: 1.0000 - val_loss: 2.5537 - val_acc: 0.5500
Epoch 13/30
80/80 [==============================] - 47s 583ms/step - loss: 1.0316e-08 - acc: 1.0000 - val_loss: 2.5501 - val_acc: 0.5500
Epoch 14/30
80/80 [==============================] - 44s 545ms/step - loss: 1.0264e-08 - acc: 1.0000 - val_loss: 2.5886 - val_acc: 0.5500
Epoch 15/30
80/80 [==============================] - 40s 506ms/step - loss: 1.0294e-08 - acc: 1.0000 - val_loss: 2.5845 - val_acc: 0.5500
Epoch 16/30
80/80 [==============================] - 43s 537ms/step - loss: 1.0186e-08 - acc: 1.0000 - val_loss: 2.6096 - val_acc: 0.5500
Epoch 17/30
80/80 [==============================] - 45s 558ms/step - loss: 1.0184e-08 - acc: 1.0000 - val_loss: 2.6252 - val_acc: 0.5500
Epoch 18/30
80/80 [==============================] - 43s 538ms/step - loss: 1.0217e-08 - acc: 1.0000 - val_loss: 2.6405 - val_acc: 0.5500
Epoch 19/30
80/80 [==============================] - 42s 520ms/step - loss: 1.0199e-08 - acc: 1.0000 - val_loss: 2.6236 - val_acc: 0.5500
Epoch 20/30
80/80 [==============================] - 46s 575ms/step - loss: 9.8130e-09 - acc: 1.0000 - val_loss: 2.6521 - val_acc: 0.5500
Epoch 21/30
80/80 [==============================] - 43s 542ms/step - loss: 1.0129e-08 - acc: 1.0000 - val_loss: 2.6751 - val_acc: 0.5500
Epoch 22/30
80/80 [==============================] - 44s 550ms/step - loss: 1.0231e-08 - acc: 1.0000 - val_loss: 2.6755 - val_acc: 0.5500
Epoch 23/30
80/80 [==============================] - 42s 530ms/step - loss: 1.0166e-08 - acc: 1.0000 - val_loss: 2.6928 - val_acc: 0.5500
Epoch 24/30
80/80 [==============================] - 44s 552ms/step - loss: 1.0187e-08 - acc: 1.0000 - val_loss: 2.7008 - val_acc: 0.5500
Epoch 25/30
80/80 [==============================] - 43s 536ms/step - loss: 1.0173e-08 - acc: 1.0000 - val_loss: 2.7132 - val_acc: 0.5500
Epoch 26/30
80/80 [==============================] - 40s 502ms/step - loss: 1.0042e-08 - acc: 1.0000 - val_loss: 2.7103 - val_acc: 0.5500
Epoch 27/30
80/80 [==============================] - 41s 512ms/step - loss: 1.0207e-08 - acc: 1.0000 - val_loss: 2.7195 - val_acc: 0.5500
Epoch 28/30
80/80 [==============================] - 44s 554ms/step - loss: 1.0174e-08 - acc: 1.0000 - val_loss: 2.7336 - val_acc: 0.5500
Epoch 29/30
80/80 [==============================] - 41s 511ms/step - loss: 1.0177e-08 - acc: 1.0000 - val_loss: 2.7431 - val_acc: 0.5500
Epoch 30/30
80/80 [==============================] - 40s 501ms/step - loss: 1.0213e-08 - acc: 1.0000 - val_loss: 2.7475 - val_acc: 0.5500

在培訓後,始終保存您的模型是一個好的做法:

model.save('cats_and_dogs_small_1.h5')

讓我們在培訓和驗證數據上繪製模型的損失和準確性:

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

在這裏插入圖片描述
在這裏插入圖片描述
這些曲線圖具有過度擬合的特點。我們的訓練精度隨着時間線性增加,直到接近100%,而我們的

驗證準確度暫停在70-72%。我們的驗證損失在五個階段後達到最小,然後停滯,而培訓損失

一直線性遞減直到接近0。

因爲我們只有相對較少的訓練樣本(2000年),所以過度擬合將是我們最關心的問題。你已經知道

有助於緩解過度擬合的技術數量,如輟學和體重衰減(L2正則化)。我們現在要

介紹一種新的,專門針對計算機視覺的,在使用深度學習模型處理圖像時幾乎普遍使用的:*數據

增強*。

使用數據擴充

過度擬合是由於樣本太少而無法學習,使得我們無法訓練一個能夠推廣到新數據的模型。

給定無限的數據,我們的模型將暴露在手頭數據分佈的所有可能方面:我們永遠不會過度擬合。數據

增強採用的方法是從現有的訓練樣本中生成更多的訓練數據,通過一個數字“增強”樣本

產生可信的圖像的隨機變換。我們的目標是在訓練的時候,我們的模型永遠不會看到完全一樣的結果

想象兩次。這有助於模型暴露於數據的更多方面,並更好地進行泛化。

在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)爲單位的值,在這個範圍內可以隨機旋轉圖片。
縮放範圍”用於在圖片內隨機縮放。水平翻轉”是指在沒有水平假設的情況下,將一半圖像隨機地水平翻轉不對稱(如真實世界的圖片)。填充模式”是用於填充新創建的像素的策略,可以在旋轉或寬度/高度移動後顯示。
讓我們看看我們的增強圖像:

# 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()

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

如果我們使用這種數據增強配置訓練一個新網絡,我們的網絡將永遠不會看到兩次相同的輸入。但是,輸入它所看到的仍然是高度相關的,因爲它們來自於少量的原始圖像——我們無法產生新的信息,我們只能重新混合現有的信息。因此,這可能還不足以完全擺脫過度裝修。繼續戰鬥過度擬合,我們還將在密連接分類器之前向模型添加一個退出層:

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

讓我們使用數據擴充和退出來訓練我們的網絡:

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=50,
      epochs=80,
      validation_data=validation_generator,
      validation_steps=50)
Found 20 images belonging to 2 classes.
Found 20 images belonging to 2 classes.
Epoch 1/80
50/50 [==============================] - 29s 573ms/step - loss: 0.6646 - acc: 0.6150 - val_loss: 0.8158 - val_acc: 0.5500
Epoch 2/80
50/50 [==============================] - 29s 587ms/step - loss: 0.5737 - acc: 0.6990 - val_loss: 0.9219 - val_acc: 0.3500
Epoch 3/80
50/50 [==============================] - 32s 638ms/step - loss: 0.4845 - acc: 0.7580 - val_loss: 1.2549 - val_acc: 0.3500
Epoch 4/80
50/50 [==============================] - 32s 635ms/step - loss: 0.3809 - acc: 0.8210 - val_loss: 1.6812 - val_acc: 0.3500
Epoch 5/80
50/50 [==============================] - 30s 594ms/step - loss: 0.3138 - acc: 0.8610 - val_loss: 2.2329 - val_acc: 0.5500
Epoch 6/80
50/50 [==============================] - 29s 577ms/step - loss: 0.2694 - acc: 0.8830 - val_loss: 2.1424 - val_acc: 0.3500
Epoch 7/80
50/50 [==============================] - 31s 630ms/step - loss: 0.2192 - acc: 0.9170 - val_loss: 2.4715 - val_acc: 0.3000
Epoch 8/80
50/50 [==============================] - 35s 696ms/step - loss: 0.1949 - acc: 0.9210 - val_loss: 3.2708 - val_acc: 0.5500
Epoch 9/80
50/50 [==============================] - 29s 589ms/step - loss: 0.1587 - acc: 0.9380 - val_loss: 3.2843 - val_acc: 0.4500
Epoch 10/80
50/50 [==============================] - 29s 581ms/step - loss: 0.1644 - acc: 0.9340 - val_loss: 3.6380 - val_acc: 0.5000
Epoch 11/80
50/50 [==============================] - 30s 605ms/step - loss: 0.1490 - acc: 0.9470 - val_loss: 3.6353 - val_acc: 0.3000
Epoch 12/80
50/50 [==============================] - 31s 617ms/step - loss: 0.1269 - acc: 0.9520 - val_loss: 3.8901 - val_acc: 0.5000
Epoch 13/80
50/50 [==============================] - 32s 636ms/step - loss: 0.1205 - acc: 0.9560 - val_loss: 4.1789 - val_acc: 0.5500
Epoch 14/80
50/50 [==============================] - 33s 655ms/step - loss: 0.0848 - acc: 0.9670 - val_loss: 4.3144 - val_acc: 0.3500
Epoch 15/80
50/50 [==============================] - 30s 600ms/step - loss: 0.0801 - acc: 0.9710 - val_loss: 5.1957 - val_acc: 0.4500
Epoch 16/80
50/50 [==============================] - 33s 655ms/step - loss: 0.0832 - acc: 0.9690 - val_loss: 5.0150 - val_acc: 0.3500
Epoch 17/80
50/50 [==============================] - 32s 635ms/step - loss: 0.0747 - acc: 0.9740 - val_loss: 5.4591 - val_acc: 0.5000
Epoch 18/80
50/50 [==============================] - 31s 630ms/step - loss: 0.0741 - acc: 0.9750 - val_loss: 5.6205 - val_acc: 0.3500
Epoch 19/80
50/50 [==============================] - 30s 609ms/step - loss: 0.0583 - acc: 0.9800 - val_loss: 5.7334 - val_acc: 0.5500
Epoch 20/80
50/50 [==============================] - 30s 601ms/step - loss: 0.0773 - acc: 0.9830 - val_loss: 5.7338 - val_acc: 0.3500
Epoch 21/80
50/50 [==============================] - 30s 603ms/step - loss: 0.0353 - acc: 0.9880 - val_loss: 6.3216 - val_acc: 0.3500
Epoch 22/80
50/50 [==============================] - 30s 603ms/step - loss: 0.0422 - acc: 0.9850 - val_loss: 6.6349 - val_acc: 0.3500
Epoch 23/80
50/50 [==============================] - 31s 627ms/step - loss: 0.0518 - acc: 0.9860 - val_loss: 6.9036 - val_acc: 0.5000
Epoch 24/80
50/50 [==============================] - 31s 621ms/step - loss: 0.0738 - acc: 0.9810 - val_loss: 6.6649 - val_acc: 0.3500
Epoch 25/80
50/50 [==============================] - 30s 606ms/step - loss: 0.0146 - acc: 0.9980 - val_loss: 7.7122 - val_acc: 0.5000
Epoch 26/80
50/50 [==============================] - 34s 672ms/step - loss: 0.0453 - acc: 0.9880 - val_loss: 7.4843 - val_acc: 0.4500
Epoch 27/80
50/50 [==============================] - 32s 639ms/step - loss: 0.0367 - acc: 0.9870 - val_loss: 7.6371 - val_acc: 0.5000
Epoch 28/80
50/50 [==============================] - 30s 597ms/step - loss: 0.0241 - acc: 0.9950 - val_loss: 8.1453 - val_acc: 0.5000
Epoch 29/80
50/50 [==============================] - 30s 592ms/step - loss: 0.0326 - acc: 0.9920 - val_loss: 8.2151 - val_acc: 0.4500
Epoch 30/80
50/50 [==============================] - 30s 606ms/step - loss: 0.0502 - acc: 0.9890 - val_loss: 8.4432 - val_acc: 0.5000
Epoch 31/80
50/50 [==============================] - 30s 597ms/step - loss: 0.0163 - acc: 0.9970 - val_loss: 8.0083 - val_acc: 0.5000
Epoch 32/80
50/50 [==============================] - 29s 582ms/step - loss: 0.0362 - acc: 0.9920 - val_loss: 7.9444 - val_acc: 0.5000
Epoch 33/80
50/50 [==============================] - 30s 609ms/step - loss: 0.0346 - acc: 0.9880 - val_loss: 8.3872 - val_acc: 0.5000
Epoch 34/80
50/50 [==============================] - 29s 584ms/step - loss: 0.0258 - acc: 0.9950 - val_loss: 8.8647 - val_acc: 0.5000
Epoch 35/80
50/50 [==============================] - 29s 588ms/step - loss: 0.0362 - acc: 0.9880 - val_loss: 8.4899 - val_acc: 0.5500
Epoch 36/80
50/50 [==============================] - 30s 595ms/step - loss: 0.0276 - acc: 0.9920 - val_loss: 8.2845 - val_acc: 0.5000
Epoch 37/80
50/50 [==============================] - 29s 582ms/step - loss: 0.0189 - acc: 0.9940 - val_loss: 8.6437 - val_acc: 0.5000
Epoch 38/80
50/50 [==============================] - 31s 611ms/step - loss: 0.0092 - acc: 0.9960 - val_loss: 8.5518 - val_acc: 0.4500
Epoch 39/80
50/50 [==============================] - 29s 586ms/step - loss: 0.0230 - acc: 0.9920 - val_loss: 8.7936 - val_acc: 0.3500
Epoch 40/80
50/50 [==============================] - 29s 580ms/step - loss: 0.0162 - acc: 0.9960 - val_loss: 9.2228 - val_acc: 0.4500
Epoch 41/80
50/50 [==============================] - 29s 582ms/step - loss: 0.0231 - acc: 0.9960 - val_loss: 9.2710 - val_acc: 0.4500
Epoch 42/80
50/50 [==============================] - 29s 586ms/step - loss: 0.0372 - acc: 0.9960 - val_loss: 10.0153 - val_acc: 0.5000
Epoch 43/80
50/50 [==============================] - 29s 586ms/step - loss: 0.0080 - acc: 0.9970 - val_loss: 11.3972 - val_acc: 0.5000
Epoch 44/80
50/50 [==============================] - 29s 583ms/step - loss: 0.0292 - acc: 0.9950 - val_loss: 10.8015 - val_acc: 0.3000
Epoch 45/80
50/50 [==============================] - 29s 578ms/step - loss: 0.0438 - acc: 0.9890 - val_loss: 10.0119 - val_acc: 0.4500
Epoch 46/80
50/50 [==============================] - 29s 579ms/step - loss: 0.0271 - acc: 0.9900 - val_loss: 10.0108 - val_acc: 0.5000
Epoch 47/80
50/50 [==============================] - 30s 597ms/step - loss: 0.0106 - acc: 0.9970 - val_loss: 10.5471 - val_acc: 0.5500
Epoch 48/80
50/50 [==============================] - 29s 590ms/step - loss: 0.0323 - acc: 0.9930 - val_loss: 9.5484 - val_acc: 0.3000
Epoch 49/80
50/50 [==============================] - 29s 583ms/step - loss: 0.0283 - acc: 0.9960 - val_loss: 10.2763 - val_acc: 0.5000
Epoch 50/80
50/50 [==============================] - 31s 619ms/step - loss: 0.0344 - acc: 0.9920 - val_loss: 9.9814 - val_acc: 0.4500
Epoch 51/80
50/50 [==============================] - 29s 572ms/step - loss: 0.0175 - acc: 0.9920 - val_loss: 9.7044 - val_acc: 0.5500
Epoch 52/80
50/50 [==============================] - 29s 577ms/step - loss: 0.0112 - acc: 0.9980 - val_loss: 11.7453 - val_acc: 0.5500
Epoch 53/80
50/50 [==============================] - 29s 588ms/step - loss: 0.0221 - acc: 0.9930 - val_loss: 10.2242 - val_acc: 0.4500
Epoch 54/80
50/50 [==============================] - 29s 574ms/step - loss: 0.0165 - acc: 0.9970 - val_loss: 10.1135 - val_acc: 0.5000
Epoch 55/80
50/50 [==============================] - 28s 565ms/step - loss: 0.0057 - acc: 0.9980 - val_loss: 10.6202 - val_acc: 0.4000
Epoch 56/80
50/50 [==============================] - 28s 566ms/step - loss: 0.0350 - acc: 0.9930 - val_loss: 11.1900 - val_acc: 0.5000
Epoch 57/80
50/50 [==============================] - 28s 567ms/step - loss: 0.0022 - acc: 1.0000 - val_loss: 11.1339 - val_acc: 0.4500
Epoch 58/80
50/50 [==============================] - 28s 566ms/step - loss: 0.0343 - acc: 0.9940 - val_loss: 11.2391 - val_acc: 0.4500
Epoch 59/80
50/50 [==============================] - 29s 571ms/step - loss: 0.0167 - acc: 0.9950 - val_loss: 11.1921 - val_acc: 0.5000
Epoch 60/80
50/50 [==============================] - 28s 568ms/step - loss: 0.0060 - acc: 0.9980 - val_loss: 11.5785 - val_acc: 0.4500
Epoch 61/80
50/50 [==============================] - 29s 578ms/step - loss: 0.0451 - acc: 0.9950 - val_loss: 11.7180 - val_acc: 0.5000
Epoch 62/80
50/50 [==============================] - 28s 567ms/step - loss: 0.0036 - acc: 0.9990 - val_loss: 14.4340 - val_acc: 0.5000
Epoch 63/80
50/50 [==============================] - 28s 566ms/step - loss: 0.0267 - acc: 0.9940 - val_loss: 11.9684 - val_acc: 0.4000
Epoch 64/80
50/50 [==============================] - 31s 622ms/step - loss: 0.0079 - acc: 0.9970 - val_loss: 11.3762 - val_acc: 0.4000
Epoch 65/80
50/50 [==============================] - 28s 557ms/step - loss: 0.0695 - acc: 0.9870 - val_loss: 12.1722 - val_acc: 0.4500
Epoch 66/80
50/50 [==============================] - 28s 568ms/step - loss: 0.0130 - acc: 0.9940 - val_loss: 12.1978 - val_acc: 0.4500
Epoch 67/80
50/50 [==============================] - 28s 565ms/step - loss: 0.0188 - acc: 0.9970 - val_loss: 11.7762 - val_acc: 0.4000
Epoch 68/80
50/50 [==============================] - 28s 557ms/step - loss: 0.0011 - acc: 1.0000 - val_loss: 13.2410 - val_acc: 0.4500
Epoch 69/80
50/50 [==============================] - 28s 564ms/step - loss: 0.0431 - acc: 0.9960 - val_loss: 12.7333 - val_acc: 0.4500
Epoch 70/80
50/50 [==============================] - 28s 562ms/step - loss: 0.0104 - acc: 0.9980 - val_loss: 13.0937 - val_acc: 0.5000
Epoch 71/80
50/50 [==============================] - 28s 561ms/step - loss: 0.0140 - acc: 0.9970 - val_loss: 12.4452 - val_acc: 0.3500
Epoch 72/80
50/50 [==============================] - 28s 553ms/step - loss: 0.0212 - acc: 0.9960 - val_loss: 12.7357 - val_acc: 0.4500
Epoch 73/80
50/50 [==============================] - 28s 562ms/step - loss: 0.0036 - acc: 0.9990 - val_loss: 13.4253 - val_acc: 0.5000
Epoch 74/80
50/50 [==============================] - 28s 561ms/step - loss: 0.0036 - acc: 0.9980 - val_loss: 12.7524 - val_acc: 0.4500
Epoch 75/80
50/50 [==============================] - 28s 561ms/step - loss: 0.0068 - acc: 0.9990 - val_loss: 12.5598 - val_acc: 0.4500
Epoch 76/80
50/50 [==============================] - 28s 556ms/step - loss: 0.0240 - acc: 0.9960 - val_loss: 13.6739 - val_acc: 0.5000
Epoch 77/80
50/50 [==============================] - 28s 555ms/step - loss: 0.0099 - acc: 0.9970 - val_loss: 13.9698 - val_acc: 0.5000
Epoch 78/80
50/50 [==============================] - 28s 550ms/step - loss: 0.0027 - acc: 1.0000 - val_loss: 13.4390 - val_acc: 0.4500
Epoch 79/80
50/50 [==============================] - 28s 555ms/step - loss: 0.0390 - acc: 0.9960 - val_loss: 13.6321 - val_acc: 0.4500
Epoch 80/80
50/50 [==============================] - 28s 559ms/step - loss: 0.0023 - acc: 0.9990 - val_loss: 13.6142 - val_acc: 0.4000

讓我們保存我們的模型——我們將在convnet可視化部分中使用它。

model.save('cats_and_dogs_small_2.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, '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()

在這裏插入圖片描述

在這裏插入圖片描述

由於數據的增加和丟失,我們不再過度擬合:訓練曲線非常接近於驗證

曲線。我們現在能夠達到82%的精度,比非正則模型相對提高了15%。

通過進一步利用正則化技術和調整網絡參數(例如每個卷積的濾波器數量層,或者網絡中的層數),我們可以得到更好的精度,可能高達86-87%。然而,這將證明僅僅通過從頭開始訓練我們自己的convnet就很難再上一層樓,因爲我們要處理的數據太少了。作爲一個下一步,爲了提高我們在這個問題上的準確性,我們必須利用一個預先訓練過的模型,這將是下兩個問題的重點部分。

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