本次案例遵循一個基本的機器學習工作流程:
(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()