深度学习用于计算机视觉

深度学习用于计算机视觉

文章目录

  • 深度学习用于计算机视觉
    • 1、卷积神经网络简介
      • 1.1、卷积运算
        • 1.1.1、理解边界效应与填充
        • 1.1.2、理解卷积步幅
      • 1.2、最大池化运算
    • 2、在小型数据集上从头开始训练一个卷积神经网络
      • 2.1、深度学习与小数据问题的相关性
      • 2.2、下载数据
      • 2.3、构建网络
      • 2.4、数据预处理
      • 2.5、使用数据增强

卷积神经网络,也叫 convnet,它是计算机视觉应用几乎都在使用的一种深度学习模型。你将学到将卷积神经网络应用于图像分类问题,特别是那些训练数据集较小的问题。

1、卷积神经网络简介

# 实例化一个小型的卷积神经网络
from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
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(64, (3, 3), activation='relu'))
  • 卷积神经网络接收形状为 (image_height, image_width, image_channels) 的输入张量(不包括批量维度)

    本例中设置卷积神经网络处理大小为 (28, 28, 1) 的输入张量,这正是 MNIST 图像的格式。我们向第一层传入参数 input_shape=(28, 28, 1) 来完成此设置

我们来看一下卷积神经网络的架构:

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
=================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0
_________________________________________________________________
  • 可以看到,每个 Conv2D 层和 MaxPooling2D 层的输出都是一个形状为 (height, width, channels) 的 3D 张量宽度和高度两个维度的尺寸通常会随着网络加深而变小通道数量由传入 Conv2D 层的第一个参数所控制( 32 或 64)

  • 下一步是将最后的输出张量[大小为 (3, 3, 64)]输入到一个密集连接分类器网络中,即 Dense 层的堆叠;

    这些分类器可以处理 1D 向量,而当前的输出是 3D 张量。首先,我们需要将 3D 输出展平为 1D。

# 在卷积神经网络上添加分类器
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
  • 我们将进行 10 类别分类,最后一层使用带 10 个输出的 softmax 激活。现在网络的架构如下:
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 576)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                36928     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________
  • 下面我们在 MNIST 数字图像上训练这个卷积神经网络:
# 在 MNIST 图像上训练卷积神经网络
from keras.datasets import mnist
from keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# 拟合模型
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)
  • 在测试集上评估:
# 在测试数据上对模型进行评估
test_loss, test_acc = model.evaluate(test_images, test_labels)
# 313/313 [==============================] - 2s 6ms/step - loss: 0.0291 - accuracy: 0.9909
test_acc
# 0.9908999800682068
  • 与密集连接模型相比,为什么这个简单卷积神经网络的效果这么好?要回答这个问题,我们来深入了解 Conv2D 层和 MaxPooling2D 层的作用。

1.1、卷积运算

密集连接层和卷积层的根本区别在于, Dense 层从输入特征空间中学到的是全局模式(比如对于 MNIST 数字,全局模式就是涉及所有像素的模式),而卷积层学到的是局部模式。对于图像来说,学到的就是在输入图像的二维小窗口中发现的模式。在上面的例子中,这些窗口的大小都是 3× 3。

深度学习用于计算机视觉_第1张图片

这个重要特性使卷积神经网络具有以下两个有趣的性质:

  1. 卷积神经网络学到的模式具有平移不变性( translation invariant)卷积神经网络在图像右下角学到某个模式之后,它可以在任何地方识别这个模式对于密集连接网络来说,如果模式出现在新的位置,它只能重新学习这个模式。这使得卷积神经网络在处理图像时可以高效利用数据(因为视觉世界从根本上具有平移不变性),它只需要更少的训练样本就可以学到具有泛化能力的数据表示。
  2. 卷积神经网络可以学到模式的空间层次结构( spatial hierarchies of patterns)第一个卷积层将学习较小的局部模式(比如边缘),第二个卷积层将学习由第一层特征组成的更大的模式,以此类推这使得卷积神经网络可以有效地学习越来越复杂、越来越抽象的视觉概念(因为视觉世界从根本上具有空间层次结构)。
  • 对于包含两个空间轴( 高度和宽度)和一个深度轴(也叫通道轴)的 3D 张量,其卷积也叫特征图( feature map)

    卷积运算从输入特征图中提取图像块,并对所有这些图块应用相同的变换,生成输出特征图( output feature map)

    该输出特征图仍是一个 3D 张量,具有宽度和高度,其深度可以任意取值,因为输出深度是层的参数,深度轴的不同通道不再像 RGB 输入那样代表特定颜色,而是代表过滤器( filter)过滤器对输入数据的某一方面进行编码

  • 在 MNIST 示例中,第一个卷积层接收一个大小为 (28, 28, 1) 的特征图,并输出一个大小为 (26, 26, 32) 的特征图,即它在输入上计算 32 个过滤器

    对于这 32 个输出通道,每个通道都包含一个 26× 26 的数值网格,它是过滤器对输入的响应图( response map),表示这个过滤器模式在输入中不同位置的响应

    深度学习用于计算机视觉_第2张图片

    这也是特征图这一术语的含义:深度轴的每个维度都是一个特征(或过滤器),而 2D 张量 output[:, :, n] 是这个过滤器在输入上的响应的二维空间图(map)

  • 卷积由以下两个关键参数所定义:

    1. 从输入中提取的图块尺寸:这些图块的大小通常是 3 × 3 或 5 × 5;
    2. 输出特征图的深度:卷积所计算的过滤器的数量。
  • 对于 Keras 的 Conv2D 层,这些参数都是向层传入的前几个参数:

    Conv2D(output_depth, (window_height, window_width))

  • 卷积的工作原理:

    1. 在 3D 输入特征图上滑动( slide)这些 3× 3 或 5× 5 的窗口,在每个可能的位置停止并提取周围特征的 3D 图块[形状为 (window_height, window_width, input_depth)]
    2. 然后每个 3D 图块与学到的同一个权重矩阵[叫作卷积核( convolution kernel)]做张量积,转换成形状为 (output_depth,) 的 1D 向量
    3. 然后对所有这些向量进行空间重组,使其转换为形状为 (height, width, output_depth) 的 3D 输出特征图

    输出特征图中的每个空间位置都对应于输入特征图中的相同位置(比如输出的右下角包含了输入右下角的信息)

深度学习用于计算机视觉_第3张图片

  • 输出的宽度和高度可能与输入的宽度和高度不同。不同的原因可能有两点:
    1. 边界效应,可以通过对输入特征图进行填充来抵消;
    2. 使用了步幅( stride)。

1.1.1、理解边界效应与填充

  • 假设有一个 5× 5 的特征图(共 25 个方块)。其中只有 9 个方块可以作为中心放入一个3× 3 的窗口,这 9 个方块形成一个 3× 3 的网格。

    深度学习用于计算机视觉_第4张图片

    因此,输出特征图的尺寸是 3× 3。它比输入尺寸小了一点,在本例中沿着每个维度都正好缩小了 2 个方块。

    在前一个例子中你也可以看到这种边界效应的作用:开始的输入尺寸为 28× 28,经过第一个卷积层之后尺寸变为
    26× 26。

  • 如果你希望输出特征图的空间维度与输入相同,那么可以使用填充( padding)

    填充是在输入特征图的每一边添加适当数目的行和列,使得每个输入方块都能作为卷积窗口的中心

    对于 3× 3 的窗口,在左右各添加一列,在上下各添加一行。对于 5× 5 的窗口,各添加两行和两列。

    深度学习用于计算机视觉_第5张图片

  • 对于 Conv2D 层,可以通过 padding 参数来设置填充,这个参数有两个取值:

    1. "valid" 表示不使用填充(只使用有效的窗口位置)
    2. "same" 表示“填充后输出的宽度和高度与输入相同”

    padding 参数的默认值为 “valid”。

1.1.2、理解卷积步幅

  • 影响输出尺寸的另一个因素是步幅的概念。

    两个连续窗口的距离是卷积的一个参数,叫作步幅,默认值为 1

    也可以使用步进卷积( strided convolution),即步幅大于 1 的卷积。

    用步幅为 2 的 3× 3 卷积从 5× 5 输入中提取的图块(无填充):

    深度学习用于计算机视觉_第6张图片

  • 步幅为 2 意味着特征图的宽度和高度都被做了 2 倍下采样(除了边界效应引起的变化)。

    虽然步进卷积对某些类型的模型可能有用,但在实践中很少使用。

  • 为了对特征图进行下采样,我们不用步幅,而是通常使用最大池化( max-pooling)运算。

1.2、最大池化运算

  • 在卷积神经网络示例中,你可能注意到,在每个 MaxPooling2D 层之后,特征图的尺寸都会减半。

    这就是最大池化的作用:对特征图进行下采样,与步进卷积类似

  • 最大池化是从输入特征图中提取窗口,并输出每个通道的最大值

    它的概念与卷积类似,但是最大池化使用硬编码的 max 张量运算对局部图块进行变换,而不是使用学到的线性变换(卷积核)

    最大池化与卷积的最大不同之处在于,最大池化通常使用 2× 2 的窗口和步幅 2,其目的是将特征图下采样 2 倍。与此相对的是,卷积通常使用 3× 3 窗口和步幅 1

  • 为什么要用这种方式对特征图下采样?为什么不删除最大池化层, 一直保留较大的特征图?

    1. 这种架构不利于学习特征的空间层级结构。我们需要让最后一个卷积层的特征包含输入的整体信息。
    2. 最后一层的特征图对每个样本有过多的元素。这对于这样一个小模型来说太多了,会导致严重的过拟合。
  • 简而言之,使用下采样的原因,一是减少需要处理的特征图的元素个数,二是通过让连续卷积层的观察窗口越来越大(即窗口覆盖原始输入的比例越来越大),从而引入空间过滤器的层级结构

  • 最大池化不是实现这种下采样的唯一方法;

    但最大池化的效果往往比这些替代方法更好。
    简而言之,原因在于特征中往往编码了某种模式或概念在特征图的不同位置是否存在(因此得名特征图),而观察不同特征的最大值而不是平均值能够给出更多的信息


2、在小型数据集上从头开始训练一个卷积神经网络

  • “很少的”样本可能是几百张图像,也可能是几万张图像

    数据集中包含 4000 张猫和狗的图像( 2000 张猫的图像, 2000 张狗的图像)。我们将 2000 张图像用于训练, 1000 张用于验证, 1000 张用于测试;

  • 解决这一问题的基本策略:

    1. 使用已有的少量数据从头开始训练一个新模型

      首先,在 2000 个训练样本上训练一个简单的小型卷积神经网络,不做任何正则化,为模型目标设定一个基准。这会得到 71% 的分类精度;此时主要的问题在于过拟合

    2. 数据增强( data augmentation)

      它在计算机视觉领域是一种非常强大的降低过拟合的技术。使用数据增强之后,网络精度将提高到 82%;

    3. 用预训练的网络做特征提取

      得到的精度范围在 90%~96%;

    4. 对预训练的网络进行微调

      最终精度为 97%。

    总而言之,这三种策略——从头开始训练一个小型模型、使用预训练的网络做特征提取、对预训练的网络进行微调——构成了你的工具箱,未来可用于解决小型数据集的图像分类问题。

2.1、深度学习与小数据问题的相关性

  • 仅在有大量数据可用时,深度学习才有效。这种说法部分正确:

    深度学习的一个基本特性就是能够独立地在训练数据中找到有趣的特征,无须人为的特征工程,而这只在拥有大量训练样本时才能实现

  • 所谓“大量”样本是相对的,即相对于你所要训练网络的大小和深度而言:

    只用几十个样本训练卷积神经网络就解决一个复杂问题是不可能的,但如果模型很小,并做了很好的正则化,同时任务非常简单,那么几百个样本可能就足够了。

    由于卷积神经网络学到的是局部的、平移不变的特征,它对于感知问题可以高效地利用数据

    虽然数据相对较少,但在非常小的图像数据集上从头开始训练一个卷积神经网络,仍然可以得到不错的结果,而且无须任何自定义的特征工程

  • 深度学习模型本质上具有高度的可复用性

    已有一个在大规模数据集上训练的图像分类模型或语音转文本模型,你只需做很小的修改就能将其复用于完全不同的问题。

2.2、下载数据

import os, shutil

# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = 'G:\\practice\\python\\kaggle\\train'

# The directory where we will
# store our smaller dataset
base_dir = 'G:\\practice\\python\\kaggle\\cats_and_dogs_small'
os.mkdir(base_dir)

# Directories for our training,
# validation and test splits
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)

# Directory with our training cat pictures
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)

# Directory with our training dog pictures
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)

# Directory with our validation cat pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)

# Directory with our validation dog pictures
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)

# Directory with our validation cat pictures
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)

# Directory with our validation dog pictures
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)

# Copy first 1000 cat images to train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)

# Copy next 500 cat images to validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)

# Copy next 500 cat images to test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

# Copy first 1000 dog images to train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)

# Copy next 500 dog images to validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)

# Copy next 500 dog images to test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)
  • 有 2000 张训练图像、 1000 张验证图像和 1000 张测试图像。

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

2.3、构建网络

  • 面对的是一个二分类问题所以网络最后一层是使用 sigmoid 激活的单一单元(大小为 1 的 Dense 层)。这个单元将对某个类别的概率进行编码。
from keras import layers
from 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_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_3 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0
_________________________________________________________________
  • 在编译这一步,和前面一样,我们将使用 RMSprop 优化器。因为网络最后一层是单一 sigmoid单元,所以我们将使用二元交叉熵作为损失函数。
# 配置模型用于训练
from keras import optimizers

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

2.4、数据预处理

  • 将数据输入神经网络之前,应该将数据格式化为经过预处理的浮点数张量

    现在,数据以 JPEG 文件的形式保存在硬盘中,所以数据预处理步骤大致如下:

    1. 读取图像文件;
    2. 将 JPEG 文件解码为 RGB 像素网格;
    3. 将这些像素网格转换为浮点数张量;
    4. 将像素值( 0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值) 。
  • Keras 拥有自动完成这些步骤的工具。 Keras 有一个图像处理辅助工具的模块,位于 keras.preprocessing.image。特别地,它包含 ImageDataGenerator 类,可以快速创建 Python 生成器,能够将硬盘上的图像文件自动转换为预处理好的张量批量。

# 使用 ImageDataGenerator 从目录中读取图像
from keras.preprocessing.image import ImageDataGenerator

# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=20,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
  • 利用生成器,我们让模型对数据进行拟合。使用 fit_generator 方法来拟合,它在数据生成器上的效果和 fit 相同(fit_generator 方法已经过时了,直接使用 fit 方法即可)
    1. 它的第一个参数应该是一个 Python 生成器,可以不停地生成输入和目标组成的批量;
    2. 因为数据是不断生成的,所以 Keras 模型要知道每一轮需要从生成器中抽取多少个样本。这是 steps_per_epoch 参数的作用:从生成器中抽取 steps_per_epoch 个批量后(即运行了 steps_per_epoch 次梯度下降),拟合过程将进入下一个轮次
    3. 可以传入一个 validation_data 参数,其作用和在 fit 方法中类似;这个参数可以是一个数据生成器,但也可以是 Numpy 数组组成的元组。如果向 validation_data 传入一个生成器,那么这个生成器应该能够不停地生成验证数据批量,因此你还需要指定 validation_steps 参数,说明需要从验证生成器中抽取多少个批次用于评估
# 利用批量生成器拟合模型
history = model.fit(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

2.5、使用数据增强

  • 过拟合的原因是学习样本太少,导致无法训练出能够泛化到新数据的模型。

    数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加( augment)样本

    其目标是,模型在训练时不会两次查看完全相同的图像。

    在 Keras 中,这可以通过对 ImageDataGenerator 实例读取的图像执行多次随机变换来实现

    # 利用 ImageDataGenerator 来设置数据增强
    datagen = ImageDataGenerator(
          rotation_range=40,
          width_shift_range=0.2,
          height_shift_range=0.2,
          shear_range=0.2,
          zoom_range=0.2,
          horizontal_flip=True,
          fill_mode='nearest')
    
    • rotation_range 是角度值(在 0~180 范围内),表示图像随机旋转的角度范围;
    • width_shift 和 height_shift 是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例);
    • shear_range 是随机错切变换的角度;
    • zoom_range 是图像随机缩放的范围;
    • horizontal_flip 是随机将一半图像水平翻转。如果没有水平不对称的假设(比如真实世界的图像),这种做法是有意义的;
    • fill_mode是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移。
  • 如果你使用这种数据增强来训练一个新网络,那么网络将不会两次看到同样的输入

    但网络看到的输入仍然是高度相关的,因为这些输入都来自于少量的原始图像;你无法生成新信息,而只能混合现有信息因此,这种方法可能不足以完全消除过拟合

    为了进一步降低过拟合,你还需要向模型中添加一个 Dropout 层,添加到密集连接分类器之前:

# 定义一个包含 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'])
# 利用数据增强生成器训练卷积神经网络

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

# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        # All images will be resized to 150x150
        target_size=(150, 150),
        batch_size=32,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

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

你可能感兴趣的:(Python深度学习,计算机视觉)