tensorflow2實現圖像分類:以貓狗數據集爲案例(下)

上節實現完的精確度和loss繪製圖如下。從圖中可以看出,訓練精度和驗證精度相差很大,模型在驗證集上僅達到70%左右的精度。 本節研究其問題,並嘗試提高模型的整體性能。

 

過擬合

        在上面的圖中,訓練精度隨着時間線性增加,而驗證精度在訓練過程中停滯在70%左右。此外,訓練和驗證準確性之間的差異是顯而易見的——這是過擬合的標誌。 當訓練樣本數量較少時,模型有時會從訓練樣本中的噪聲或不需要的細節中學習,從而對新樣本的模型性能產生負面影響。這種現象被稱爲過擬合。這意味着該模型在新的數據集上將很難推廣。 在訓練過程中有多種方法來對抗過度訓練。在本案例中將使用數據擴充,並在我們的模型中添加dropout

 

數據擴充

        過擬合通常發生在有少量訓練樣本的時候。解決這個問題的一種方法是擴充數據集,使其具有足夠數量的訓練示例。數據擴充採用從現有訓練樣本中產生更多訓練數據的方法,通過使用產生可信圖像的隨機變換來擴充樣本。目標是模型在訓練中永遠不會看到完全相同的圖片兩次。這有助於將模型展示給數據的更多方面,並更好地進行概括。

實現:使用在tf.keras中的ImageDataGenerator類實現這一點。將不同的轉換傳遞給數據集,它將在訓練過程中負責應用它。

擴充和可視化數據

首先對數據集應用隨機水平翻轉增強,並查看轉換後各個圖像的外觀。

(1)應用水平翻轉

horizontal_flip作爲參數傳遞給ImageDataGenerator類,並將其設置爲“True”以應用此增強。

代碼實現:

# 該函數將圖像繪製成1行5列的網格形式,圖像放置在每一列中。
def plotImages(images_arr):
    fig, axes = plt.subplots(1, 5, figsize=(20, 20))
    axes = axes.flatten()
    for img, ax in zip(images_arr, axes):
        ax.imshow(img)
        ax.axis('off')
    plt.tight_layout()
    plt.show()



# 將horizontal_flip作爲參數傳遞給ImageDataGenerator類,並將其設置爲“True”以應用此增強。
image_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255, horizontal_flip=True)


# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

實現效果:

 

(2)隨機旋轉圖像

觀察一個不同的叫做旋轉的增強,並隨機地將45度旋轉應用到訓練例子中。

代碼實現:

# 將rotation_range作爲參數傳遞給ImageDataGenerator類,並將其設置爲45。
image_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255, rotation_range=45)

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))
augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

實現效果:

 

(3)應用縮放增量

對數據集應用縮放增強功能,將圖像隨機放大50%。

代碼實現:

# zoom_range from 0 - 1 where 1 = 100%.
image_gen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255, zoom_range=0.5)

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))
augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

實現效果:

 

把它們組合在一起

應用所有先前的增強。在此對訓練圖像應用了重新縮放、45度旋轉、寬度移動、高度移動、水平翻轉和縮放增強

代碼實現:

image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1. / 255,
    rotation_range=45,
    width_shift_range=.15,
    height_shift_range=.15,
    horizontal_flip=True,
    zoom_range=0.5
)

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                                     directory=train_dir,
                                                     shuffle=True,
                                                     target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                     class_mode='binary')
augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

實現效果:

 

創建驗證集數據生成器

通常,只對訓練示例應用數據擴充。在這種情況下,只需重新縮放驗證圖像,並使用ImageDataGenerator將它們轉換爲批處理。

# 創建驗證集數據生成器
image_gen_val = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)

val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                                 directory=validation_dir,
                                                 target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                 class_mode='binary')

Dropout

        另一種減少過擬合的技術是在網絡中引入Dropout。這是一種正則化形式,它強制網絡中的權重只取小值,這使得權重值的分佈更加規則,並且網絡可以減少對小訓練樣本的過度擬合。

        當將Dropout應用到一個層時,它會在訓練過程中從所應用的層中隨機丟棄(設置爲零)數量的輸出單位。Dropout取一個分數作爲它的輸入值,形式如0.1、0.2、0.4等。這意味着從應用層隨機放棄10%、20%或40%的輸出單位。 當將0.1 D應用於某一層時,它會在每個訓練週期中隨機殺死10%的輸出單位。 用這個新的三維特性創建一個網絡體系結構,並將其應用於不同的卷積和完全連接的層。

 

創建一個有Dropouts的新網絡

       在這裏,您將Dropout應用於第一個和最後一個最大池層。在每個訓練期間,應用Dropout將隨機設置20%的神經元爲零。這有助於避免過度擬合訓練數據集。

# 創建包含Dropouts的模型
model_new = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, 3, padding='same', activation='relu',
                           input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(1)
])

編譯模型:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 150, 150, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 75, 75, 16)        0         
_________________________________________________________________
dropout (Dropout)            (None, 75, 75, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 75, 75, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 37, 37, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 37, 37, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 18, 18, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 18, 18, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 20736)             0         
_________________________________________________________________
dense (Dense)                (None, 512)               10617344  
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 10,641,441
Trainable params: 10,641,441
Non-trainable params: 0
_________________________________________________________________

訓練模型

在成功地將數據擴充引入訓練樣例並向網絡中添加Dropout後,訓練此新網絡:

history = model_new.fit_generator(
    train_data_gen,
    steps_per_epoch=total_train // batch_size,
    epochs=epochs,
    validation_data=val_data_gen,
    validation_steps=total_val // batch_size
)

 

可視化模型

想象一下訓練後的新模型,你會發現過度擬合明顯比以前少了。在對模型進行更多時期的訓練後,精確度應該會提高。

如以下是將epochs = 15修改成epochs = 30的效果,其他效果自己嘗試:

完整代碼如下:

import tensorflow as tf
import os
import numpy as np
import matplotlib.pyplot as plt

"""
本教程遵循一個基本的機器學習工作流程:
-檢查和理解數據
-建立輸入管道
-建立模型
-訓練模型
-測試模型
-改進模型並重復該過程
"""

base_dir = './dataset/'
train_dir = os.path.join(base_dir, 'train/')
validation_dir = os.path.join(base_dir, 'validation/')
train_cats_dir = os.path.join(train_dir, 'cats')  # directory with our training cat pictures
train_dogs_dir = os.path.join(train_dir, 'dogs')  # directory with our training dog pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')  # directory with our validation cat pictures
validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # directory with our validation dog pictures

"""
    爲方便起見,設置預處理數據集和訓練網絡時要使用的變量。
"""
batch_size = 128
epochs = 30
IMG_HEIGHT = 150
IMG_WIDTH = 150

num_cats_tr = len(os.listdir(train_cats_dir))  # total training cat images: 1000
num_dogs_tr = len(os.listdir(train_dogs_dir))  # total training dog images: 1000

num_cats_val = len(os.listdir(validation_cats_dir))  # total validation cat images: 500
num_dogs_val = len(os.listdir(validation_dogs_dir))  # total validation dog images: 500

total_train = num_cats_tr + num_dogs_tr  # Total training images: 2000
total_val = num_cats_val + num_dogs_val  # Total validation images: 1000


# 該函數將圖像繪製成1行5列的網格形式,圖像放置在每一列中。
def plotImages(images_arr):
    fig, axes = plt.subplots(1, 5, figsize=(20, 20))
    axes = axes.flatten()
    for img, ax in zip(images_arr, axes):
        ax.imshow(img)
        ax.axis('off')
    plt.tight_layout()
    plt.show()


"""
數據準備
    將圖像格式化成經過適當預處理的浮點張量,然後輸入網絡:
    - 從磁盤讀取圖像。
    - 解碼這些圖像的內容,並根據它們的RGB內容將其轉換成適當的網格格式。
    - 把它們轉換成浮點張量。
    - 將張量從0到255之間的值重新縮放到0到1之間的值,因爲神經網絡更喜歡處理小的輸入值。
    幸運的是,所有這些任務都可以用tf.keras提供的ImageDataGenerator類來完成。
    它可以從磁盤讀取圖像,並將它們預處理成適當的張量。它還將設置發生器,將這些圖像轉換成一批張量——這對訓練網絡很有幫助。
"""

image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1. / 255,
    rotation_range=45,
    width_shift_range=.15,
    height_shift_range=.15,
    horizontal_flip=True,
    zoom_range=0.5
)

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                                     directory=train_dir,
                                                     shuffle=True,
                                                     target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                     class_mode='binary')
augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

# 創建驗證集數據生成器
image_gen_val = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)

val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                                 directory=validation_dir,
                                                 target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                 class_mode='binary')

# 創建模型
model_new = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, 3, padding='same', activation='relu',
                           input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(1)
])
#
# # 編譯模型
# # 這邊選擇ADAM優化器和二進制交叉熵損失函數。要查看每個訓練時期的訓練和驗證準確性,請傳遞metrics參數。
model_new.compile(optimizer='adam',
                  loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                  metrics=['accuracy'])

model_new.summary()

# 在成功地將數據擴充引入訓練樣例並向網絡中添加Dropout後,訓練此新網絡:
history = model_new.fit_generator(
    train_data_gen,
    steps_per_epoch=total_train // batch_size,
    epochs=epochs,
    validation_data=val_data_gen,
    validation_steps=total_val // batch_size
)

# 可視化模型
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

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

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

 

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