本章,首先介绍CNN从哪里来,构建模块是怎样的,以及如何使用TF实现
The Architecture of the Visual Cortex
视觉皮层神经元有一块小的局部接受野(receptive field),即只对视野的局部区域视觉刺激做出反应。不同神经元的接受野可能会有重复,一起平铺在整个视觉区域,可能作用于不用方向。
CNN由此逐渐形成,LeNet-5除了熟知的全连接层、S形激活函数以外,引入了卷积层和池化层两个新的构建块。
为什么用部分连接层而不是全连接层DNN进行图像识别的原因是,全连接层对小图片集(MNIST)作用,但由于大图片神经元,连接数过多,全连接层就不能很好工作。
Convolutional Layer
CNN最重要的构建块就是卷积层:
如上所示,第一卷积层的6个神经元不会连接到输入神经元的美俄像素,而只与接受野内的像素连接。第二卷积层的每个神经元仅连接第一层中小矩阵内的神经元。总结来说,这种结构允许网络集中在第一个隐藏层的低级特征中,在下一个隐藏层组装成比较高阶的特征。这种特性使得CNN在图像识别方面效果好。
我们目前看到的许多神经网络都有 由长神经组成的层,我们必须将图片平滑到一维才能提供给神经网络。现在每层用二位表示更能将神经元与输入相匹配。
本层i行,j列的神经元与上次层输入中的i到i+f_h-1行,j到j+f_w-1列神经元相关联,f_h和f_w表示接受野的高和宽,为了使本层与上一层同宽高,通常在周围填充0——零填充(zero padding)。
下图所示可以通过间隔接受野的方式使得大的输入层连接到更小的层,两个连续接受野之间的距离叫步幅。如下图 5x7输入层(加零填充)连接3x4,使用3x3接受野,步幅为2(不确定)。即位于上一层的i行、j列神经元与下层is_h到is_h+f_h-1行,js_w到js_w+f_w-1列相连接,其中s_h和s_w分别代表垂直和水平方向步幅。
Filters
神经元权重大小可由接受野大小表示。
如上图所示的两个权重集合,称为过滤器,垂直线过滤器白线为1,其余 为0,神经元忽略除白线外的内容。水平线过滤器忽略接受野除白色外的部分。
输入图像分别引用两过滤器后,输出特征图1、特征图2.
Stacking Multiple Feature Maps(特征图叠加)
如下图所示,一个薄层表示一个卷积层(由大小相同的特征图组成),所以用三维表示。
同一个特征图所有神经元拥有相同参数(权重与偏差)。卷积层同时对输入使用多个过滤器,使其 能检测到输入的多个特征。
同一特征图神经元既可以显著减少模型中参数数量,而且一旦识别某个地方某个模式,就可以最亲爱任何地方识别该模式(传统DNN只能特定位置识别该模式).
下面输入图像也是有多个子层(RGB),灰度图通常只有一个通道。
卷积层l的特征图k上的i行j列神经元,与上一层l-1层(穿过本层所有特征图)的神经元相连接,位置在is_w到is_w+f_w-1行,js_h到js_h+f_h-1列。
所有位于不同特征图i行j列神经元都连接到前一层输出中完全相同的神经元。
下用公式表明,作用是计算所有输出的加权值+偏置参数:
- z (i, j, k)是卷积层l特征图k上的神经元在i行j列输出。
- s_h、s_w分别是水平方向垂直方向步幅,f_h、f_w分笔试接受野的高和宽,f_n’是l-1层特征图数量
- x(i′, j′, k′)是l-1层位于i’行j’列的特征图k’神经元输出
- b_k为特征图k(l层)偏置参数,即特征图k微调亮度按钮
- w(u, v, k′ ,k)是l层中任意特征图k与位于k’的u行v列输入之间的连接权重
TensorFlow Implementation
import numpy as np
from sklearn.datasets import load_sample_images
# Load sample images
china = load_sample_image("china.jpg")
flower = load_sample_image("flower.jpg")
dataset = np.array([china, flower], dtype=np.float32)
batch_size, height, width, channels = dataset.shape
# Create 2 filters
filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)#创建两个7*7过滤器
filters[:, 3, :, 0] = 1 # vertical line
filters[3, :, :, 1] = 1 # horizontal line
# Create a graph with input X plus a convolutional layer applying the 2 filters
X = tf.placeholder(tf.float32, shape=(None, height, width, channels))
convolution = tf.nn.conv2d(X, filters, strides=[1,2,2,1], padding="SAME")
#用卷积层构建构建两张图象(0填充,步幅为2)
#conv2d X小批次,filters过滤器(四维张量)
#strides [1,sh,sw,1],中间俩为垂直水平步幅,外面俩为1,可能用来指定批次步幅(跳实例),通道步幅(跳特征图或通道)
#padding :SAME 0填充 neural_output = neural_output/stride,向上取整
# VALID 非0填充 忽略右侧和底部的行和列
with tf.Session() as sess:
output = sess.run(convolution, feed_dict={X: dataset})
plt.imshow(output[0, :, :, 1], cmap="gray") # plot 1st image's 2nd feature map
plt.show()
卷积层有很多超参数,必须选择过滤器数量、高、宽、步幅以及填充数值。用交叉验证寻找正确参数过于耗费时间,有很多CNN架构稍后会被讨论。
Memory Requirements
CNN领一下问题是需要大量内存,训练期间尤甚(反向椽笔的反向传递需要在正向传递中计算所有中间值)。
所以当内存不足时:
- 减小mini-batch 尺寸
- 减少步幅或删除几个图层来降低维度
- 使用16浮点取代32浮点
- 分发CNN到多台设备上
Pooling Layer
池化层的目的是通过对输入图像进行二次采样来减小计算负载,内存利用率和参数数量,从而降低over-fitting风险。减小输入图像大小可以使网络容忍一定的图样移位在(位置不变性)。
池化层和卷积层一样,每个神经元都连接到上一层输出中矩形的有限个神经元,必须定义接受野的大小、步幅、填充类型,但池化神经元没有权重,而使用聚合函数(mean or max)聚合输出。
如图所示为最大池化层(max pooling layer):池化内核为 2x2,步幅为2,无填充,每个内核最大值才会进入下一层,其他输入丢弃
两个方向输入减少为1/2,总共面积减少75%,即减少75%输入值。
池化层在各个输入通道独立工作,输出深度输入深度相同,可以选择在深度维度叠加(图像高宽不变,通道减少)
TF实现:
创建张量,过滤器
batch_size, height, width, channels = dataset.shape
filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters[:, 3, :, 0] = 1 # vertical line
filters[3, :, :, 1] = 1 # horizontal line
X = tf.placeholder(tf.float32, shape=(None, height, width, channels))
max_pool = tf.nn.max_pool(X, ksize=[1,2,2,1], strides=[1,2,2,1],padding="VALID")
#ksize第一个元素必为1(不支持多个实例相加),不支持空间维度和深度维度相加,ksize[1]、ksize[2]同时为1,否则ksize[3]=1
with tf.Session() as sess:
output = sess.run(max_pool, feed_dict={X: dataset})
#创建平均池化层(用avg_pool代替max_pool)
plt.imshow(output[0].astype(np.uint8)) # plot the output for the 1st image
plt.show()
CNN Architectures
典型的CNN结构如下所示:
堆叠几个卷积层(每个卷积层通常都有一个ReLU层),然后是一个池化层,接着是另外几个卷积层(+ReLU)再接一个池化层……图像越来越小,越来越深(有更多的特征图),栈顶添加了几个全连接层组成的常规前反馈神经网络,最后层输出预测。(卷积内核不要设置太大)
下面介绍经典的LeNet-5架构,然后三个ILVRC获奖者:AlexNex、GoogLeNet、ResNet。
LeNet-5
- MNIST 28X28被0填充到32X32,输入到网络之前进行归一化处理。其余不部分不使用填充(图像尺寸越来越小)
- 池化层:每个神经元计算输入,乘一个可学习系数(每个特征图 一个)并添加一个可学习偏置参数(每个特征图一个),最终应用激活函数。
- 大多数C3图中神经元只连接到S2图中三到四个神经元
- 输出层:每个神经元输出其输入向量和权值向量之间的欧几里得距离的平方。每个输出衡量该图片属于某类的可能性,交叉熵代价函数可以很大程度上减少不良预测,产生更大的梯度并因此更快地收敛
AlexNet
网址
直接将卷积层堆叠到其它层上。
为了减小过度拟合,使用两种正则化:
- 训练期间输出层F8、F9使用淘汰策略(50%)
- 使用偏移、水平翻转、改变光照条件来训练移动移动数据
本地响应归一化(LRN):C1、C3层ReLU后的使用具有竞争性的归一化步骤,能够使得最强激活来抑制不同特征图的神经元,改善泛化:
字母含义介绍:
例如,r=2且神经元强激活,它将一直其上面和下面特征图的神经元激活
Alex超参数设置(r=2, α = 0.00002, β = 0.75,k=1),用TF的 local_response_normaliza
tion()实现
ZF Net是AlexNet变体,只调整了些超参数。
GoogLeNet
GoogLeNet比CNN更深,通过初始化模块使子网变成可能,参数数量只是AlexNet1/10.
3+3+2(S)表示3X3内核,步幅为2,SAME填充。输入信号首先复制到四个不同的层,所有的卷积函数使用ReLU激活函数,第二组卷积层使用不同内核大小(1X1,3X3,5X5),这样能改用捕捉到不同尺寸的图像模式,每层步长为1,填充为SAME,所以输入输出都有相同高宽,使其可以沿着最终的Depth Concat的深度维度连接到所有输出(从所有的四个顶部的卷积层堆叠特征图)。该级联层可以使用TF的concat(),axis=3(深度)。
内核为1X1卷积层目的:
- 配置成输出特征图比输入少,作为瓶颈降低纬度。3X3,5X5之前使用
- 每个卷积层对[1X1,3X3],[1X1,5X5]作用像同一个强大单独的卷积层,捕捉更复杂的模式,卷积层能够扫描图像中两层神经网络。
将整个初始化模块看成一个超级卷积层,能够输出一些用以捕捉各种尺寸复杂模式的特征图。
每个卷积层的卷积内核数量是一个超参数,但每个初始层都有6个或更多个超参数。
GoogLeNet包括9个初始化模块(带转筒的框),每个包含三层,每个卷积层和每个池化层输出的特征图数量显示在该内核尺寸之前,初始化模块的6个数字表示模块中每个卷积层输出特征图的数量。
所有卷积层都有ReLU激活函数。
- 前两层图像长宽/4(面积/16)
- 一个本地响应标准化层确保之前的图层学习各种各样的特征
- 后面接着两个卷积层,第一个像瓶颈层,可以看做单一智能卷积层
- 本地响应标准化层可确保前面的图层捕捉到各种各样的模式
- 最大池化层将图像高宽减小为1/2增快计算
- 为了减少纬度加快计算,九个初始化模块与最大池化层堆叠
- 平均池化层使用具有相同尺寸,填充为VALID的内核,输入1X1特征图——全局平均池化。有效强制前面的各层产生特征图,特征图实际上是每个目标类的置信图(其他类型特征被平均步骤破坏),无需在CNN顶部配置几个完全连接层(AlexNet等),从而大大减少了网络中参数数量,降低过度配置风险。
- 最后一层放弃正则化,使用一个具有softmax激活函数的完全连接层输出估计类的概率。
原始GoogLeNet还包括位于第三个第六个初始化模块上的两个辅助分类器,由一个平均池化层、一个卷积层,两个全连接层,一个softmax激活层组成,损失(缩小70%)加到整体损失中,目的是解决梯度消失和网络规范问题。作用相对较小。
ResNet
网址
使用了快捷连接(跳过连接):输入到一个层中的信号也被添加到位于堆栈上面的层的输出端。
训练神经网络是的目标函数h(x)模型化。若将输入x添加到网络输出(添加跳过连接),则网络强制模型化为f(x)=h(x)-x,称为残差学习。
初始化一个常规神经网络时,权重接近0,网络输出接近0,添加跳过连接只是输出一个输入的复制。(对认证函数建模),若目标函数接近认证函数将加快训练速度。
若添加多个跳过连接,即一些层学习还没有开始,网络就可以开始处理进程。由于跳过连接,信号轻松跨过这个网络。深度残差网络看做残差单元,就是一个具有跳过连接的小型网络。
看下ResNet架构,除了没有淘汰层与谷歌net特别相似,中间是一个很深的简单残差单元(由两个具有BN和ReLU激活功能的卷积层组成)使用内核并保留维度空间(步幅为1,填充为SAME)
特征图数量是每个残差单元两倍,同时高宽减半(步幅为2的卷积层),此时不能直接添加到残差单元输出(形状不同),所以需要输入通过一个具有步幅为2和输出正常数量特征图的1X1卷积层。
ResNet-34是34层ResNet(只计算卷积层和全连接层),包含3个输出64个特征图的残差单元(RU),4个包含129特征图的RU,6个包含256特征图的RU和3个包含512张特征图的RU:
更深层的ResNets,如-152,输出残差单元略欧不同,这里不深究,有需要看书。
TF其他卷积层:
- conv1d()用于一维输入卷积层,如一维数组,同时接受野覆盖几个相邻单词
- conv3d()用于创建三维输入卷积层,例如三维PET扫描
- atrous_conv2d()用于创建带孔卷积层。
- conv2d_transpose()用于 创建转置卷积层,用来进行图像提升采样
- depthwise_conv2d()用来创建深度卷积层,将每个过滤器应用到每个独立输入通道。
- separable_conv2d()用于创建可分离卷积层,首先类似一个深度卷积层,然后应用一个卷积层输出特征图。