前言
上一章为大家介绍过深度学习的基础和多层感知机 MLP 的应用,本章开始将深入讲解卷积神经网络的实用场景。
卷积神经网络 CNN(Convolutional Neural Networks,ConvNet)是一种特殊的深度学习神经网络,近年来在物体识别、图像重绘、视频分析等多个层面得到了广泛的应用。
本文将以VGG16预训练模型为例子,从人脸识别、预训练模型、图片风格迁移、滤波分析、热力图等多过领域介绍 CNN 的应用。
目录
一、卷积神经网络的原理
卷积神经网络 CNN 是由多个块组成,每个块都具有两种层:卷积层 Conv 和池化层 Pooling,卷积层 Conv 通过卷积核(也称滤波器)进行卷积运算后,由激活函数输出到池化层,再通过池化运算,如此迭代多次后,由最后的一个块通过输出层全连接进行数据输出,完成卷积神经网络的整个过程(如下图)。
可能听起来可能有点复杂,其实可以把 “卷积层 ——> 激活层 ——> 池化层” 作为一个重复块看待,经过多层重复后再由全连接输出,下面将从卷积层和池化层两个方面分别介绍 CNN 流程。
1.1 卷积层
1.1.1 滤波器(卷积核)
假设在卷积层中,有一个 1*6*6 的图形作为输入数据,这里把它称为输入特征图,它经过一个 3*3 的滤波器(也被称为卷积核)进行卷积运算,即从 input 左上角 3*3 的受野区开始计算其张量积,每完成一次计算向右移动,步幅为1,完成此计算后,就会得出一个 1*4*4 的输出特征图(如下图)。
卷积核的形状必须大于等于 2 * 2 ,一般为 3*3 或者 5*5,其步幅可以自定义,一般为 1。如果卷积核步幅为 3,那么一个输入特征图为1*6*6经过步幅为3的3*3卷积核后,输出特征图就会变为 1*2*2, 如此类推。
1.1.2 偏置值
正如正常等式 f(x)=w*x+h 一样,卷积运算也有偏置值,偏置值的运算就是为每个值都加上此张量,如果下图
![](https://i-blog.csdnimg.cn/blog_migrate/9d7371f887eaad4a0688341862dbe187.png)
1.1.3 填充
经过卷积核运算后,输出特征图会比输入特征图维度少2,也就是一个1*3*3 的输入特征图与一个步幅为1的3*3卷积核运算后,会得出一个 1*1*1 的输出特征图。然而,这时候如果需要得出一个1*3*3 的输出特征图,只需要把 padding 参数设置为 same,系统就会自动把输入称征图变形状为 1*5*5,用 0 来填充再进行运行。
![](https://i-blog.csdnimg.cn/blog_migrate/c81412f7b0fcb7f4f7e1089aeb515215.png)
1.1.4 三维数据卷积运算
上图所介绍的都是二维数据的卷积运算,如果遇到图像处理时,例如 RGB 图片,往往还需要处理一个数据就是通道 channel,一般 RGB 图片的 channel 数为 3(红、绿、蓝),而 monochrome 图片的 channel 数为 1,根据格式不同略有差别。因此卷积核有三维数据的运算,当输入特征图为三维数据时,其卷积核也会变为三维,并将每个维度的结果相加得出输出特征输出图。
我们可以把这个三维的输入特征图看成是一幅 n 个 channel 的图片输入操作,如果图片为 n 个 channel 大小为 width*height 的输入特征图,卷积核的形状是 n*3*3,最后输出特征图将是 1*owitdh * oheight 。
![](https://i-blog.csdnimg.cn/blog_migrate/330971ee782a2806c1e3dedf152a36e7.png)
此时,若用到 w 个卷积核,然后再加入 w 个偏置值,那 n 个 channel 大小为 height * width 的输入特征图将会输出 w 层大小为 oheight * owidth 的输出特征图
![](https://i-blog.csdnimg.cn/blog_migrate/a7cb6246b55d9d035b9df22e2dbd9ab7.png)
1.1.5 TensorFlow 中的 卷积类 ConvNet
在 TensorFlow 1.x 中需要添加一个卷积层,可使用 tf.nn.conv2d 函数
1 def conv2d_v2(input, filters, strides, padding, 2 data_format="NHWC", dilations=None, 3 name=None):
- input: 类型为 `float32`, `float64`,`int32`, `uint8`, `int16`, `int8`, `int64`, `bfloat16`, `uint16`, `half`, `uint32`, `uint64`其中一种的张量,形状为 [batch, in_height, in_width, depth],代表输入的批量数据
- filters: 类型为 float32、float64、halt 的张量,输入类型与 input 相同,代表卷积核格式,例如 [1,3,3,1]
- strides:int 类型列表,长度为4,用于控制卷积核的移动步幅,与上述参数相同,也是四维的,[1,1,1,1],其中第一个1和最后一个1是固定值,中间的两个数代表在x轴和y轴的移动步长。
- padding:str 类型 [ SAME','VALID'] 之一,用于选择填充的算法,SAME 是填充边界,VALID 是当不足以移动时直接舍弃。
- data_formate:str类型 [‘NHWC’、'NCHW' ] 之一,默认值为 NHWC,指定输入和输出数据的格式。NHWC 时数据格式为 [batch, in_height, in_width, in_channels],NCHW时数据格式为 [batch,in_channels,in_heihgt,in_width]
- dilations:int类型列表,格式必须为 [1, rate_height, rate_width, 1],指定填充边界时的步幅。当边界无需填充时,其默认值为 None,当需要填充边界时,其默认值为 [1,1,1,1]
- name: str 类型,名称
在 TensorFlow 2.x 中可以直接使用 Conv2D 类,其常用参数与 TensorFlow 1.x 类似,注意数据输入格式略有不同。
1 class Conv2D(Conv): 2 def __init__(self,filters,kernel_size,strides=(1, 1),padding='valid', 3 data_format=None,dilation_rate=(1, 1),groups=1,activation=None, 4 use_bias=True,kernel_initializer='glorot_uniform',bias_initializer='zeros', 5 kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None, 6 kernel_constraint=None, bias_constraint=None, 7 **kwargs):
- filters:int 类型,代表卷积核的数量
- kernel_size:int 类型列表,形状 [int,int],代表卷积核的形状
- strides:int 类型列表,默认值为 (1 , 1 ) 与 tf.nn.conv2d 不同,其形状为 [int , int ] 二维数组,用于控制卷积核的移动步幅。
- padding:"valid" 或 "same" (大小写敏感),用于选择填充的算法,same 是填充边界,valid 是当不足以移动时直接舍弃。
- data_formate:str类型 [ channels_last , channels_first ] 之一,默认值为 channels_last,指定输入和输出数据的格式。channels_first 时数据格式为 [batch, channels, height, width],channels_last 时数据格式为 [batch,heihgt,width, in_channels]
- dilations:int类型列表,格式必须为 [ rate_height, rate_width ],默认值为 [1 , 1 ],指定填充边界时的步幅。
- group: int 类型,默认为1,指定输入数据中,沿 channel 轴分割的组的数量
- activation: str 类型,默认为 None,要使用的激活函数。 如果你不指定,则不使用激活函数 (即线性激活: a(x) = x)。
- use_bias: bool 类型,默认为 True,指该层是否使用偏置向量。
- kernel_initializer: str 类型,默认为 glorot_uniflorm, kernel 权值矩阵的初始化器 (详见 keras.initializers)。
- bias_initializer: str 类型,默认为 zeros ,偏置向量的初始化器 (详见 keras.initializers) 。
- kernel_regularizer: str 类型,默认为 None,运用到 kernel 权值矩阵的正则化函数 (详见 keras.regularizers)。
- bias_regularizer: 运用到偏置向量的正则化函数 (详见 keras.regularizers)。
- activity_regularizer: 运用到层输出(它的激活值)的正则化函数 (详见 keras.regularizers)。
- kernel_constraint: 运用到 kernel 权值矩阵的约束函数 (详见 keras.constraints)。
- bias_constraint: 运用到偏置向量的约束函数 (详见 keras.constraints)。
1.2 最大池化层
池化层分为最大池化层 MaxPool 与均值池化层 AvgPool,其实就是缩小width和height的运算,比如按步幅 2 进行 2*2 MaxPool,相当于在 2*2 的区域中获取最大值运算,取出最大的值。其池化大小往往也步幅相同,即 2 * 2 的池化步幅为2 ,3 * 3 的池化步幅为3,如此类推。
![](https://i-blog.csdnimg.cn/blog_migrate/98ff8a72346856bbb9a1b3c9b031e5dd.png)
在 TensorFlow 1.x 中需要添加最大池化层,可使用 tf.nn.max_pool 函数
1 def max_pool(value, ksize, strides, padding, 2 data_format="NHWC", name=None, input=None):
- input: 类型为 `float32` 的 4维张量,形状为 [batch, in_height, in_width, depth],代表输入的批量数据
- ksize: 类型为 int 的整数列表,代表池化区域的格式,例如 [1 , 2 , 2 , 1]
- strides:int 类型列表,长度为4,用于控制池化的移动步数,与上述参数相同,也是四维的,例如[1,1,1,1],其中第一个1和最后一个1是固定值,中间的两个数代表在x轴和y轴的移动步长。
- padding:str 类型 [ SAME','VALID'] 之一,用于选择填充的算法,SAME 是填充边界,VALID 是当不足以移动时直接舍弃。
- data_formate:str类型 [‘NHWC’、'NCHW' ] 之一,默认值为 NHWC,指定输入和输出数据的格式。NHWC 时数据格式为 [batch, in_height, in_width, in_channels],NCHW时数据格式为 [batch,in_channels,in_heihgt,in_width]
- name: str 类型,名称
在 TensorFlow 2.x 中可通过 MaxPooling2D 类生成最大池化层
1 class MaxPooling2D(Pooling2D): 2 def __init__(self, pool_size=(2, 2), strides=None, 3 padding='valid', data_format=None, **kwargs):
- pool_size:int 类型的整数列表,长为2,默认为(2,2),代表池化层在两个方向(竖直,水平)采样范围。
- strides:int 类型列表,步长为2的整数列表,默认为 None,用于控制池化的移动步数。当使用 None 时,默认 与pool_size 相同。
- padding:"valid" 或 "same" (大小写敏感),用于选择填充的算法,same 是填充边界,valid 是当不足以移动时直接舍弃。
- data_format:字符串,“channels_first”或“channels_last”之一,代表图像的通道维的位置。默认值为 channels_last,指定输入和输出数据的格式。channels_first 时数据格式为 [batch, channels, height, width],channels_last 时数据格式为 [batch,heihgt,width, channels]
1.3 卷积神经网络 CNN 的优势
对比起多层感知机,卷积神经网络有其天生的优势。由于多层感知机使用的是全转接层,因此当输入图形数据例如 RGB 图形为三维形状时,需要先对图像进行变形。例如常用的 MNIST 28*28*1 的数字,输入前会先将其转化为 784 的数据形式。如此一来相当于把所有的神经元看作同一维度处理,这样会把原有的三维数据间所隐藏的关联标志,相距像素等重要信息丢弃。
而从上面的例子可以看到,使用卷积神经网络会以三维数据的形式接收输入数据,同时以三维的形式输出到下一层。因此使用 CNN 可以最大程度保存图形原来的特征,这也是近年来 CNN 被广泛应用于图像、视频、人脸识别等领域的原因。