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

本次案例遵循一個基本的機器學習工作流程:

(1)檢查和理解數據

(2)建立輸入管道

(3)建立模型

(4)訓練模型

(5)測試模型

(6)改進模型並重復該過程

 

一.導入所需要的包

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

二.加載數據

1.下載數據

        首先下載數據集https://www.kaggle.com/c/dogs-vs-cats/data。本次使用了一個過濾版的狗和貓數據集。數據集中包括以下三個文件和目錄:test文件夾、train文件夾和一個csv文件。

 

2.取樣數據

        新建一個python項目,命名爲ImageClassification,在下面創建一個dataset用於存放數據集,main.py用於編寫python代碼。其中dataset包含train訓練集和validation驗證集兩部分,各自包含cats和dogs兩個種類樣例。再此案例中,train/cats下放了cat.0.jpg ~ cat.999.jpg共1000張圖片,train/dogs下放了cat.0.jpg ~ cat.999.jpg共1000張圖片,validation/cats下放了cat.2000.jpg ~ cat.2499.jpg共500張圖片,validation/dogs下放了dog.2000.jpg ~ dog.2499.jpg共500張圖片。

3.設置數據集存放路徑

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

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

 

三.設置預處理數據集和訓練網絡時要使用的變量。

batch_size = 128
epochs = 15
IMG_HEIGHT = 150
IMG_WIDTH = 150

 

四.數據準備

將圖像格式化成經過適當預處理的浮點型tensors,然後輸入網絡:
1.從磁盤讀取圖像。
2.解碼這些圖像的內容,並根據它們的RGB內容將其轉換成適當的網格格式。
3.把它們轉換成浮點張量。
4.將張量從0到255之間的值重新縮放到0到1之間的值,因爲神經網絡更喜歡處理小的輸入值。

【注】所有這些任務都可以用tf.keras提供的ImageDataGenerator類來完成。它可以從磁盤讀取圖像,並將它們預處理成適當的張量。它還將設置發生器,將這些圖像轉換成一批張量——這對訓練網絡很有幫助。

(1)生成訓練數據集和驗證數據集。

# 生成我們的訓練數據集和驗證數據集
train_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)
validation_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)

(2)在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
                                                           directory=train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                           class_mode='binary')
# output:Found 2000 images belonging to 2 classes.
val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
                                                              directory=validation_dir,
                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                              class_mode='binary')
# output:Found 1000 images belonging to 2 classes.

 

五.可視化訓練圖像(非必須)

通過從訓練生成器中提取一批圖像(在本例中爲32幅圖像)來可視化訓練圖像,然後用matplotlib繪製其中五幅圖像。

sample_training_images, _ = next(train_data_gen)
# next函數:從數據集中返回一個批處理。
# 返回值:(x_train,y_train)的形式,其中x_train是訓練特徵,y_train是其標籤。丟棄標籤,只顯示訓練圖像。


# 該函數將圖像繪製成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()


plotImages(sample_training_images[:5])

 

六.創建模型

該模型由三個卷積塊組成,每個卷積塊中有一個最大池層。有一個完全連接的層,上面有512個單元,由relu激活功能激活。

model = 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.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.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(1)
])

 

七.編譯模型

這邊選擇ADAM優化器二進制交叉熵損失函數。傳遞metrics參數可以查看每個訓練時期的訓練和驗證準確性。

model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])
model.summary()        # 查看網絡的所有層

打印結果:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 150, 150, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (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         
_________________________________________________________________
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
_________________________________________________________________

八.訓練模型

使用ImageDataGenerator類的fit_generator方法來訓練網絡。

history = model.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()

結果如下圖:

從圖中可以看出,訓練精度和驗證精度相差很大,模型在驗證集上僅達到70%左右的精度。 讓我們看看哪裏出了問題,並嘗試提高模型的整體性能。不足之處及處理請看下一篇:tensorflow2實現圖像分類:以貓狗數據集爲案例(下)

 

完整代碼:

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 = 15
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

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

# 生成訓練數據集和驗證數據集
train_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)
validation_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255)

# 在爲訓練和驗證圖像定義生成器之後,flow_from_directory方法從磁盤加載圖像,應用重新縮放,並將圖像調整到所需的尺寸。
train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
                                                           directory=train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                           class_mode='binary')
# output:Found 2000 images belonging to 2 classes.
val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
                                                              directory=validation_dir,
                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                              class_mode='binary')
# output:Found 1000 images belonging to 2 classes.


# 可視化訓練圖像:通過從訓練生成器中提取一批圖像(在本例中爲32幅圖像)來可視化訓練圖像,然後用matplotlib繪製其中五幅圖像。

sample_training_images, _ = next(train_data_gen)
# next函數:從數據集中返回一個批處理。
# 返回值:(x_train,y_train)的形式,其中x_train是訓練特徵,y_train是其標籤。丟棄標籤,只顯示訓練圖像。


# 該函數將圖像繪製成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()


plotImages(sample_training_images[:5])

# 創建模型:該模型由三個卷積塊組成,每個卷積塊中有一個最大池層。有一個完全連接的層,上面有512個單元,由relu激活功能激活。

model = 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.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.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(1)
])

# 編譯模型:這邊選擇ADAM優化器和二進制交叉熵損失函數。傳遞metrics參數查看每個訓練時期的訓練和驗證準確性。
model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])
model.summary()

# 訓練模型:使用ImageDataGenerator類的fit_generator方法來訓練網絡。
history = model.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()

 

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