卷积神经网络(CNN)简介

我们首先来看一个简单的卷积神经网络示例,即使用卷积神经网络对MNIST数字进行分类,它是Conv2D层和MaxPooling2D层的堆叠

小型卷积神经网络实例化

1
2
3
4
5
6
7
8
9
10
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'))
print(model.summary())

卷积神经网络接收形状为(image_height, image_width, image_channels)的输入张量,本例中由于是MNIST图像,故传入(28,28,1)

以下是目前卷积神经网络的架构:

可以看到,每个Conv2D层和MaxPooling2D层的输出都是一个形状为(height, width, channel)的3D张量,宽度和高度会随着网络的深入而不断缩小,而通道数量由前一个的Conv2D层控制。

分类器的添加

由于分类器处理的是1D张量,故使用flatten层:

1
2
3
4
5
6
7
8
9
10
11
12
13
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'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
print(model.summary())

下面是其结构图

我可以看到在进入两个Dense层之前,形状(3,3,64)的输出被展平为(576,)的向量

下面我们开始训练这个卷积神经网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from keras import layers
from keras import models
from keras.datasets import mnist
from keras.utils import to_categorical

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'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
(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)
print(test_acc)

训练结果如下:

我们发现精度达到了98.8%,效果非常好。为什么效果如此显著?我们接下来就来深入了解Conv2D层和MaxPooling2D层的作用

#卷积运算

密集连接层和卷积层的根本区别在于,Dense层从输入特征空间中学到的是全局模式,而卷积层学到的是局部模式。

这个重要特性使之具有以下两个有趣的性质:

​ 1、卷积神经网络学到的模式具有平移不变性。卷积神经网络在图像的右下角学到某个模式之后,它可以在任何地方识别这个模式。这使得卷积神经网络在处理图像时可以高效利用数据(因为视觉世界从根本上具有平移不变性)。它只需更少的训练样本就能够学到具有泛化能力的数据表示

​ 2、卷积神经网络可以学到模式的空间层次结构。第一个卷积层将学习较小的局部模式,第二个卷积层将学习由第一层特征组成的更大的模式。这使得卷积神经网络可以学习越来越复杂、越来越抽象的视觉概念(因为视觉世界在根本上具有空间层次结构)

​ 对于包含两个空间轴和一个深度轴的3D张量,其卷积也叫特征图。对于RGB图像,其深度轴为3。卷积运算从输入特征图中提取图块,并对所有这些图块应用相同的运算,生成输出特征图。

在刚才的MNIST示例中,第一个卷积层接收一个大小为(28,28,1)的特征图,并输出一个大小为(26,26,32)的特征图,即他在输入上计算32个过滤器。对于这32个输出通道,每个通道都包含一个26x26的数值网络,它是过滤器对输入的响应图,表示这个过滤器模式在输入不同位置中的响应。

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

​ 1、从输入中提取的图块尺寸:图块的大小通常为3x3或5x5

​ 2、输出特征图的深度:卷积所计算的过滤器的数量。本例中第一层深度为32,最后一层为64

对于keras的Conv2D层,这些参数都是向层传入的前几个参数:Conv2D(output_depth, (window_height, window_width))

卷积的工作原理:在3D输入特征上滑动这些3X3或5X5的窗口,在每个可能的位置停止并提取特征的3D图块。然后每个3D图块与学到的同一个权重矩阵(叫做卷积和)做张量积,转换为1D向量。然后重组后输出3D特征图。

由于边界效应的存在,如果希望输出特征图的空间维度与输入相同,那么可以使用填充。填充是在输入特征图的每一边添加适当数目的行和列。对于Conv2D层,可以通过padding来设置填充,valid表示不使用填充,same表示使用,默认值为valid。

最大池化运算

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

最大池化是从输入特征图中提取窗口,并输出每个通道的最大值。它的概念与卷积类似,但不同之处在于最大池化通常使用2X2的窗口和步幅2,其目的是将特征图下采样2倍。于此相对的是,卷积通常使用3X3窗口和步幅1

Author: YihangBao
Link: https://roarboil.github.io/2020/01/22/cnnintro/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.