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

 

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