其他分享
首页 > 其他分享> > 深度学习--使用数据增强在小型图像数据集上训练一个卷积神经网络

深度学习--使用数据增强在小型图像数据集上训练一个卷积神经网络

作者:互联网

文章目录

 

前言

深度学习的一个基本特性就是能够独立地在训练数据中找到有趣的特征,无须人为的特征工程,而这只在拥有大量训练样本时才能实现。特别是对于输入样本的维度非常高(比如图像)的问题。
所谓“大量”样本是相对的,即相对于你所要训练网络的大小和深度而言。但如果模型很小, 并做了很好的正则化(提高范化能力),同时任务非常简单,那么几百个样本可能就足够了。在现实生活中,使用很少的数据来训练一个图像分类模型是很常见的情况。
但数据集太小很容易产生过拟合现象。文章将介绍解决数据集小这一问题的基本策略–数据增强,在少量数据上训练一个模型。

一、数据集

实验使用猫狗分类数据集,是由 Kaggle 在 2013 年末公开并作为一项计算视觉竞赛的一部分。获取数据集方法:
1.可以从 https://www.kaggle.com/c/dogs-vs-cats/data 下载原始数据集,这个数据集包含 25 000 张猫狗图像(每个类别都有 12 500 张);
2.或者在此资源链接下载已经划分好的小数据集:
链接https://pan.baidu.com/s/1coyhbEZlWePhud4Akk58Gg 提取码:W28D
数据集中包含 4000 张猫和狗的图像(猫狗各一半)。将 2000 张图像用于训练,1000 张用于验证,1000张用于测试。

二、训练一个基准模型

首先,在 2000 个训练样本上训练一个简单的小型卷积神经网络,不做任何正则化,为模型目标设定一个基准。
1.获取数据集目录

base_dir = './data/cats_and_dogs_small'#保存小数据集的目录
#分别对应划分后的训练、 验证和测试的目录
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')
# (猫的训练图像目录)
train_cats_dir = os.path.join(train_dir, 'cats')
# (狗的训练图像目录)
train_dogs_dir = os.path.join(train_dir, 'dogs')
#(猫的验证图像目录)
validation_cats_dir = os.path.join(validation_dir, 'cats')
# (狗的验证图像目录)
validation_dogs_dir = os.path.join(validation_dir, 'dogs')    
#(猫的测试图像目录)
test_cats_dir = os.path.join(test_dir, 'cats')
# (狗的测试图像目录)
test_dogs_dir = os.path.join(test_dir, 'dogs')

每个分组中两个类别的样本数相同,这是一个平衡的二分类问题,分类精度可作为衡量成功的指标。

2.构建网络
卷积神经网络由 Conv2D 层(使用 relu 激活)和 MaxPooling2D 层交替堆叠构成。
由于这里要处理的是大的图像和更复杂的问题,需要使用较大的网络,使用二 个 Conv2D+MaxPooling2D 的组合。这既可以增大网络容量,也可以进一步减小特征图的尺寸, 使其在连接 Flatten 层时尺寸不会太大。因为面对的是一个二分类问题,所以网络最后一层是使用 sigmoid 激活的单一单元(大小为1 的 Dense 层)。这个单元将对某个类别的概率进行编码。

from tensorflow.keras import layers
from tensorflow.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"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten (Flatten)            (None, 6272)              0         
_________________________________________________________________
dense (Dense)                (None, 512)               3211776   
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

设置初始输入的尺寸为 150×150,所以最后在 Flatten 层之前的特征图大小为 7×7。
网络中特征图的深度在逐渐增大(从 32 增大到 128),而特征图的尺寸在逐渐减小(从148×148 减小到 7×7)。这几乎是所有卷积神经网络的模式。
3.编译网络
因为是二分类问题,所以使用’binary_crossentropy’损失函数。

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

4.数据预处理
将数据输入神经网络之前,应该将数据格式化为经过预处理的浮点数张量。 现在,数据以 JPEG 文件的形式保存在硬盘中,预处理步骤大致如下:
(1) 读取图像文件。
(2) 将 JPEG 文件解码为 RGB 像素网格。
(3) 将这些像素网格转换为浮点数张量。
(4) 将像素值(0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。
这些任务可以交给Keras库来完成, Keras 有一个图像处理辅助工具的模块,位于keras.preprocessing.image。它包含 ImageDataGenerator 类,可以快速创建 Python 生成器,能够将硬盘上的图像文件自动转换为预处理好的张量批量。

from tensorflow.keras.preprocessing.image import ImageDataGenerator
# (将所有图像乘以 1/255 缩放)
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
#训练集
train_generator = train_datagen.flow_from_directory(
        # (目标目录)
        train_dir,
        # (将所有图像的大小调整为 150×150)
        target_size=(150, 150),
        batch_size=20,
        # (因为使用了 binary_crossentropy损失,所以需要用二进制标签)
        class_mode='binary')
#验证集
validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
#看一下其中一个生成器的输出
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,)

看到生成器的输出:它生成了 150×150 的 RGB 图像[形状为 (20,150, 150, 3)]与二进制标签[形状为 (20,)]组成的批量。每个批量中包含20个样本。

5.利用生成器训练模型
使用 fit_generator 方法在数据生成器上来拟合模型,它的效果和 fit 相同,就好像训练集数据是从一个 Python 生成器输入,比如上边预处理中的train_generator就作为 fit_generator 的第一个参数,代表要进行训练的训练集。steps_per_epoch 参数的作用是知道每一轮需要从生成器中抽取多少个样本。从生成器中抽取steps_per_epoch 个批量后,将进入下一个轮次。
例子中,每个批量包含 20 个样本,所以读取完所有 2000 个样本需要 100 个批量。

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

在这里插入图片描述
迭代了30次,训练精度随着时间线性增加,直到接近 100%,而验证精度则停留在 70%~72%。模型得到 71% 的分类精度,这是不做任何正则化的,在 2000 个训练样本上训练的一个简单的小型卷积神经网络,为此数据集模型训练目标设定一个基准。
绘制该模型训练的损失

import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(loss))
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()

在这里插入图片描述
能明显看出过拟合的特征。验证损失仅在 5 轮后就达到最小值,然后保持不变,而训练损失则一直线性下降,直到接近于 0。

因为训练样本相对较少(2000 个),所以过拟合是需要问题。这一篇文章介绍过几种降低过拟合的技巧,比如dropout 和权重衰减。现在将使用一种针对于计算机视觉领域的新方法,在用深度学习模型处理图像时几乎都会用到这种方法,它就是数据增强。

二、使用数据增强在小型数据集上训练一个神经网络

数据增强(data augmentation),它在计算机视觉领域是一种非常强大的降低过拟合的技术。
过拟合的原因是学习样本太少,导致无法训练出能够泛化到新数据的模型。如果拥有无限的数据,那么模型能够观察到数据分布的所有内容,这样就永远不会过拟合。
数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加样本。其目标是,模型在训练时不会两次查看完全相同的图像。这让模型能够观察到数据的更多内容,从而具有更好的泛化能力。在 Keras中,这可以通过对 ImageDataGenerator实例读取的图像执行多次随机变换来实现
1.利用ImageDataGenerator对数据进行增强,并用生成器预处理

#数据增强
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,)

# (注意,不能增强验证数据)
test_datagen = ImageDataGenerator(rescale=1./255)
#训练集
train_generator = train_datagen.flow_from_directory(
        # (目标目录)
        train_dir,
        # (将所有图像的大小调整为 150×150)
        target_size=(150, 150),
        batch_size=32,
        #(因为使用了 binary_crossentropy损失,所以需要用二进制标签)
        class_mode='binary')
#验证集
validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

2.构造并编译模型
使用这种数据增强来训练网络,网络将不会两次看到同样的输入,但网络看到的输入仍然是高度相关的,这种方法不足以完全消除过拟合,为了进一步降低过拟合,向模型中添加了一个Dropout层,添加在密集链接分类器之前。

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

3.利用数据增强生成器训练卷积神经网络
训练这个使用了数据增强和 dropout 的网络

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=100,
      validation_data=validation_generator,
      validation_steps=50)

在这里插入图片描述
迭代了100次,网络精度将提高到 82%。比未正则化的基准模型相对比例提高了 15%。通过进一步使用正则化方法以及调节网络参数可以得到更高的精度。如果只靠训练自己的卷积神经网络,再想提高精度就十分困难,因为可用的数据太少。想要再进一步提高精度,下一步将需要使用预训练的模型,将一个保存好的优秀网络,移植到自己的网络上,它使得深度学习对小数据问题非常有效。

标签:layers,150,训练,--,卷积,add,集上,model,dir
来源: https://www.cnblogs.com/mariow/p/16182991.html