16.深度学习用于计算机视觉:卷积神经网络简介

卷积神经网络简介

先看一个简单的卷积神经网络实例,使用卷积神经网络对MNIST数字进行分类。下面的代码将会展示一个简单的卷积神经网络。它是Conv2D层和MaxPooling2D层的堆叠。

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图像的格式。通过以下方法查看目前卷积神经网络的架构。

>>> model.summary()
_________________________________________________________________
 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 (MaxPooling  (None, 5, 5, 64)         0         
 2D)                                                             
                                                                 
 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或54)。

下一步就是将最后的输出张量【大小为(3,3,64)】输入到一个密集连接分类器网络中,即Dense层的堆叠。这些分类器可以处理1D向量,而当前的输出是3D张量。

首先需要将3D张量输出展平为1D,然后再上面添加几个Dense层。

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'))
# 以下为添加的Dense层
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

我们将10类别分类,最后一层使用带10个输出的softmax激活。

>>> model.summary()

_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_3 (Conv2D)           (None, 26, 26, 32)        320       
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 13, 13, 32)       0         
 2D)                                                             
                                                                 
 conv2d_4 (Conv2D)           (None, 11, 11, 64)        18496     
                                                                 
 max_pooling2d_3 (MaxPooling  (None, 5, 5, 64)         0         
 2D)                                                             
                                                                 
 conv2d_5 (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
_________________________________________________________________

再进入两个Dense层之前,形状为(3,3,64)的输出被展平为(576,)的向量。

现在将其带入到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)
>>> test_acc
0.9912999868392944

可以得到网络的测试精度为99%。

卷积运算

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

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

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

对于包含两个空间轴(高度和宽度)和一个深度轴(也叫通道轴)的3D张量,其卷积也叫特征图。对于RGB图像,深度轴的维度大小等于3,因为图像有3个颜色通道:红色、绿色和蓝色。对于黑白图像(比如MNIST数字图像),深度等于1(表示灰度等级)。卷积运算从输入特征图中提取图块,并对所有这些图块应用相同的变换,生成输出特征图。改输出特征图仍是一个3D张量,仍具有宽度和高度,其深度可以任意取值,因为输出深度是层的参数,深度轴的不同通道不再像RGB输入那样代表特定颜色,而是代表过滤器。过滤器对输入数据的某一方面进行编码,比如,单个过滤器可以从更高层次编码这样一个概念:“输入中包含一张脸”。

在MNIST示例中,第一个卷积层接收一个大小为(28,28,1)的特征图,并输出一个大小为(26,26,32)的特征图,即它在输入上计算32个过滤器。对于这32个输出通道,每个通道都包含一个26×26的数值网络,它是过滤器对输入的响应图,表示这个过滤器模式在输入中不同位置的响应。这也是特征图这一术语的含义:深度轴的每个维度都是一个特征(或过滤器),而2D张量output[ :, :, n]是这个过滤器在输入上的响应的二维空间图。

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

  • 从输入中提取的图块尺寸:这些土块的大小通常是3×3或5×5。
  • 输出特征图的深度:卷积所计算的过滤器的数量。

对于 Keras 的 Conv2D 层,这些参数都是向层传入的前几个参数:

Conv2D(output_depth, (window_height, window_width))
# output_depth : 输出特征图的深度
# window_height,window_width : 提取的图块的高度和宽度

卷积的工作原理:在3D输入特征图上滑动这些3×3或5×5的窗口,在每个可能的位置停止并提取周围特征的3D图块,该图块的形状为(window_height, window_width, input_depth)。然后每个3D图块与学到的同一个权重矩阵(叫作卷积核)做张量积,转换成形状为(output_depth, ) 的1D向量。然后对所有这些向量进行空间重组,使其形状转换为 (height, width, output_depth) 的3D输出特征图。输出特征图中的每个空间位置都对应于输入特征图中的相同位置(比如输出的右下角包含了输入右下角的信息)。举例,利用3×3的窗口,向量 output[i, j, : ] 来自3D图块 input [ i-1:i+1, j-1:j+1, : ]。

注意:输出的宽度和高度可能与输入的宽度和高度不同。不同的原因可能有两个点。

  • 边界效应,可以通过对输入特征图进行填充来抵消。

    假设有一个5×5的特征图(共25个方块)。其中只有9个方块可以作为中心放入一个3×3的网络。因此,输出特征图的尺寸是3×3,它比输入尺寸小了一点,在本例中沿着每个维度都正好缩小了两个方块。在前一个例子中你也看到这种边界效应的作用:开始输入尺寸为28×28,经过第一个卷积层之后尺寸变成了26×26。

在这里插入图片描述

如果你希望输出特征图的空间维度与输入相同,那么就可以使用填充。填充是在输入特征图的每一边添加适当数目的行和列,使得每个输入方块都能作为卷积窗口的中心。对于3×3的窗口,在左右各添加一列,在上下各添加一行。对于5×5的窗口,各添加两行两列。

在这里插入图片描述

对于Conv2D层,可以通过padding参数来设置填充,这个参数有两个取值:“valid”表示不使用填充(只是用有效地窗口位置);“same”表示“填充后输出的宽度和高度与输入相同”。padding参数的默认值为“valid”。

  • 使用了步幅

    影响输出尺寸的另一个因素是步幅的概念。目前为止,对卷积的描述都假设卷积窗口的中心方块都是相邻的。但两个连续窗口的距离是卷积的一个参数,叫做步幅,默认值为。也可以使用进步卷积,即步幅大于1的卷积。下图为步幅为2的3×3卷积从5×5中提取的图块。

在这里插入图片描述

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

最大池化运算

在卷积神经网络示例中,你可能注意到,在每个 MaxPooling2D 层之后,特征图的尺寸都会减半。例如,在第一个 Max Pooling2D层之前,特征图的尺寸是26×26,但最大池化运算将器减半为13×13。这就是最大池化的作用:对特征图进行下采样,与进步卷积类似。

最大池化是从输入特征图中提取窗口,并输出每个通道的最大值。它的概念与卷积类似,但是最大池化使用硬编码的max张量运算对局部图块进行变换,而不是使用学到的线性变换(卷积核)。最大化池与卷积的最大不同之处在于,最大化池通常使用2×2的窗口和步幅2,其目的是将特征图下采样2倍。于此相对的是,卷积通常使用3×3窗口和步幅1。

这时候模型的卷积基如下所示:

model_no_max_pool = models.Sequential()
model_no_max_pool.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28,1 )))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activation='relu'))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activation='relu'))
>>> model_no_max_pool.summary()
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_12 (Conv2D)          (None, 26, 26, 32)        320       
                                                                 
 conv2d_13 (Conv2D)          (None, 24, 24, 64)        18496     
                                                                 
 conv2d_14 (Conv2D)          (None, 22, 22, 64)        36928     
                                                                 
=================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0
_____________________________

这种架构有以下问题:

  • 这种架构不利于学习特征的空间层级结构。第三层的3×3窗口只包含初始输入的7×7 窗口中所包含的信息。卷积神经网络学到的高级模式相对于初始输入来说仍然很小,这可能不足以学会对数字进行分类(你可以试试仅通过7像素×7像素的窗口观察图像来识别其中的数字)。我们需要让最后一个卷积层的特征包含输入的整体信息。
  • 最后一层的特征图对每个样本共有 22×22×64=30976 个元素。这太多了,如果你将其展平并在上面添加一个大小为512的Dense层,那一层将会有1580万个参数。这对于这样一个小模型来说太多了,会造成严重的过拟合。

简而言之,使用下采样的原因,一是减少需要处理的特征图的元素个数,二是通过让连续卷积层的观察窗口越来越大(即窗口覆盖原始输入的比例越来越大),从而引入空间过滤器的层级结构。

实现下采样的方法有:

  1. 最大池化
  2. 在前一个卷积层使用步幅
  3. 平均池化:将每个局部输入图块变换为取该图块各通道的平均值,而不是最大值

一般来说,最大池化的效果往往比其他替代方法好,原因在于特征中往往编码了某种模式或概念在特征图的不同位置是否存在,而观察不同特征的最大值而不是平均值能够给出更多的信息。因此,最合理的子采样策略是首先生成密集的特征图(通过无进步的卷积),然后观察特征每个小图块上的最大激活,而不是查看输入的稀疏窗口(通过进步卷积)或输入图块取平均,因为后两种方法可能导致错过或淡化特征是否存在的信息。

取该图块各通道的平均值,而不是最大值**

一般来说,最大池化的效果往往比其他替代方法好,原因在于特征中往往编码了某种模式或概念在特征图的不同位置是否存在,而观察不同特征的最大值而不是平均值能够给出更多的信息。因此,最合理的子采样策略是首先生成密集的特征图(通过无进步的卷积),然后观察特征每个小图块上的最大激活,而不是查看输入的稀疏窗口(通过进步卷积)或输入图块取平均,因为后两种方法可能导致错过或淡化特征是否存在的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值