# 局部连接、权值共享、多核卷积
# 局部连接
传统的神经网络是一种层级的结构,由输入层、隐藏层、输出层构成,每一层神经元与下一层神经元完全连接,神经元之间不存在同层之间的连接,也不存在跨层连接。相比传统神经网络,卷积神经网络引入感受野 receptive field
的概念(局部连接),即每一个神经元只与上一层的部分神经元相连接,只感受局部的图像,而非整体的图像。
# 权值共享
其次,每一个神经元都可视为一个滤波器,同一个神经元使用一个固定的卷积核,固定的卷积核卷积上一层的整张图像。假设卷积核为 m*m
,进行权值共享时总参数个数为 m*m*channels
;若不进行此步,总参数量变为原来的若干倍 W*H*channels
。如果在局部连接中,每个神经元都对应 100
个参数,一共 10^6
个神经元,如果这 10^6
个神经元的 100
个参数都是相等的,那么参数数目就变为 100
了。
以上两步降低了参数量,加速训练速度。
图片来自# 轻松理解局部连接和权值共享
# 多核卷积
在处理图像时,为了提供特征多样性,构建了多卷积核,提出了通道维度;通过使用多个卷积核来提取多个特征,选择多个卷积核,生成不同的图像。这些不同图像可以理解为不同通道。
# 卷积神经网络的基本单元
- 卷积层
- 激活函数层
- 池化层
- 归一化层(可能有)
- 全连接层
# 卷积层
感受野阐述了卷积层中每个神经元与输入数据体之间的连接方式,但是尚未讨论输入数据体中神经元的数量,以及它们的排列方式。以下几个超参数控制着输出数据体的尺寸:
- 卷积核的尺寸、
- 输出数据体的深度:和使用的滤波器的数量一致,而每个滤波器在输入数据中寻找一些不同的东西,即图像的某些特征。
- 滑动滤波器的步长:当步长为
1
,滤波器每次移动1
个像素;当步长为2
,滤波器滑动时每次移动2
个像素,当然步长也可以是不常用的3
,或者更大的数字,但这些在实际中很少使用)。这个操作会让输出数据体在空间上变小。 - 零填充尺寸:原图边界点被使用的次数较少,经过卷积,特征无法清晰展示,所以需要将外围扩充;为什么用
0
来填充;因为0
乘任何数还是0
,卷积之后对原来的结果没有影响。零填充P
为原图外围包着几层0
。
Accepts a volume of size
如果不进行零填充,输出尺寸为、
加入零填充
一般说来,当步长 时,零填充的值是,这样能保证输入和输出数据体有相同的空间尺寸。
Example
the answer is 32*32*10
And the number of parameters in this layer?
each filter has 5*5*3 + 1 = 76 params (+1 for bias)
so the answer is 76 * 10 = 760
# 激活函数层
激活函数本身就是 neuron model
的一部分,不是从属于 CNN
或者是 RNN
。
如果输入变化很小,导致输出结构发生截然不同的结果,这种情况是我们不希望看到的,为了模拟更细微的变化,输入和输出数值不只是 0
到 1
,可以是 0
和 1
之间的任何数。
对于图像,我们主要采用了卷积的方式来处理,也就是对每个像素点赋予一个权值,这个操作显然就是线性的。但是对于我们样本来说,不一定是线性可分的,为了解决这个问题,我们可以进行线性变化,或者我们引入非线性因素,解决线性模型所不能解决的问题由于线性模型的表达力不够,所以引入激活函数加入非线性因素,将一个特征空间的向量通过非线性变换映射到另一个空间,实现线性可分。
常用的激活函数: Sigmoid
、 ReLU
、 Leaky ReLU
、 Maxout
Sigmoid
依据链式法则求导时,梯度从后向前传播,每传递一层梯度值都会减小为原来的 0.25
倍,如果网络足够深,那么梯度在穿过多层后会变得非常小,接近于 0
,即梯度消失现象。
此外,经 Sigmoid
激活函数线性输出后的值并不满足均值为 0
,会使后层得到非 0
信号作为神经元的输入,使得参数在反向传播的时候会偏向更新(即都往正方向或都往负方向),收敛缓慢。
ReLU
针对 Sigmoid
梯度饱和导致训练收敛缓慢的问题, AlexNet
中首次引入了 ReLU
激活函数。
- 计算开销小,
Sigmoid
的正向传播有指数运算和倒数运算,而ReLU
是线性输出,反向传播中,Sigmoid
有指数运算,而ReLU
有输出的部分,导数始终为1
; - 稀疏性,
ReLU
会使一部分神经元的输出为0
,即网络会变得稀疏,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
ReLU
并非没有缺点, ReLU
以非零为中心且在零点处不可微;此外,由于 ReLU
在 x<0
时梯度为 0
,这样就导致负的梯度在这个 ReLU
被置零,而且这个神经元有可能再也不会被任何数据激活,正所谓神经元坏死影响神经网络的性能,决定了其使用场合只能用于隐藏层。
Leaky ReLU
针对 ReLU
中负梯度导致神经元坏死的情况,将输入小于 0
的部分赋予微小的梯度 a
。
对于该激活函数输入小于 0
的部分,也可以得到一些值,而不是没有。
# 池化层
通常在连续的卷积层之间会周期性地插入一个池化层。其作用是逐渐降低数据体的空间尺寸,进一步地降低了参数量,降低计算资源耗费,也能有效控制过拟合。
池化层的超参数有过滤器的 size
以及步长 stride
池化层没有参数,卷积层参数有神经网络需要学习的权重和偏差,而池化层只是给定了一个规则,即该层卷积核的参数已经被人为设定, MAX POOLING
还是 MEAN POOLING
,无需要学习,所以没有参数。
所以有没有重叠的可能?
对于池化层,一般会让每个区域没有重叠,因为我们只是想 down sampling
,只用得到一个值来表示这个区域,接着直接看下一个区域,以此类推即可。
Accepts a volume of size W1*H1*D1
Introduces zero parameters since it computes a fixed function of the input
Note : that it is not common to use zero-padding for pooling layers.
MAX POOLING
最大值子采样的核 W
中各权重值中只有一个为 1
,其余均为 0
,核 W
中为 1
的位置对应 InputX
被核 W
覆盖部分值最大的位置。最大值子采样的效果是把原图缩减至原来的 1/4
,并保留每个 2*2
区域的最强输入。
MEAN POOLING
均值子采样的核 W
中每个权重都是 0.25
,核 W
在原图 InputX
上的滑动的步长为 2
。均值子采样的效果相当于把原图模糊缩减至原来的 1/4
。
# 全连接层
全连接层的作用主要是实现分类,该层对模型影响的参数有以下三个
- 全连接层的总层数(长度)
- 单个全连接层的神经元数(宽度)
- 激活函数
如果全连接层宽度不变,增加长度
神经元个数增加,模型复杂度提升;全连接层数加深,模型非线性表达能力提高。理论上都可以提高模型的学习能力。
如果全连接层长度不变,增加宽度
神经元个数增加,模型复杂度提升。理论上可以提高模型的学习能力。
难度长度和宽度都是越多越好?
肯定不是,学习能力太好容易造成过拟合,并且运算时间增加,效率变低。
下图为 LeNet
来输出输入图像数字类别( 0-9
)的概率。
卷积层 C5
有 120
个卷积核,卷积核大小为 5*5
,因此生成 120
个特征图,没个特征图大小为 1*1
,每个 feature map
都与上一层( S4
)所有特征图连接。所以该全连接层有 120*(5*5*16+1)=48120
个连接。
全连接层 F6
,共有 84
个神经元与 C5
层进行全连接,所以需要训练的参数是 (120+1)*84=10164
,如同传统神经网络, F6
层需要计算输入向量和权重向量之间的点积,再加上一个 bias
,然后将其传递给 Sigmoid
函数将数值规范化。
# 常用的优化函数
RMSprop、Adam、Adagrad、Momentum、批量梯度下降、随机梯度下降( SGD
)、小批量梯度下降
参考# 深度学习中常用的优化算法(optimizer)
# 典型的卷积神经网络
# AlexNet
Alexnet Block Diagram (source:oreilly.com)
- 采用了非线性激活函数
ReLU
- 采用数据增强
- 提出了
DropOut
防止过拟合 - 采用并行化
GPU
进行训练
# VGGNet
smaller filters ,deeper networks
VGG
相比 AlexNet
的一个改进是采用连续的几个 3x3
的卷积核代替 AlexNet
中的较大卷积核( 11x11
, 7x7
, 5x5
)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。
VGG16 Block Diagram (source: neurohive.io)
WHY use smaller filters?( 3*3
conv)
Stack of two 3*3
conv( stride 1
) layers has same effective receptive field as one 5*5
conv layer . Similarly , the effect of one 7*7
( 11*11
) conv layer can be achieved by implementing three (five) 3*3
conv layers with a stride of one .
这样做的主要目的是在保证具有相同感受野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。
# ResNet
针对深度学习的模型退化问题,当网络层数越深,理论上模型应当更优,但网络模型的效果在训练集和测试集上表现都不好。需要注意的是,这里的退化既不是梯度消失或者梯度爆炸,也不是过拟合;梯度消失说明根本没有进行学习,过拟合说明训练集上误差低,只是测试集上误差高。网络模型退化是由于冗余的网络层学习了不是恒等映射的参数造成的。
F(x) is a residual mapping with respect to identity.
现有一个浅层网络,我们想通过向上堆积新层来建立深层网络,一个极端情况是增加的层没有学习,仅仅复制浅层网络的特征,这样新层就是恒等映射。
对于一个堆积层结构,当输入为 x
时,其学习到的特征记为 H(x)
,现在我们希望其可以学习到残差 F(x)=H(x)-x
,其原始的学习特征就是 F(x)+x
。
当残差为 0
时,此时堆积层仅仅做了上图右侧的 short cut
(恒等映射),至少网络的性能不会下降;实际上残差不会为 0
,就会使得堆积层在输入特征的基础上学习到新的特征,从而拥有更好的性能。
ResNet
解决了因网络加深而导致模型退化的问题,参数能够满足 H(x)=x
不就可以了,为什么要设计残差项 F(x)=0
?
对于网络退化,在引入 ResNet
之前,我们想让该层学习到的参数能够满足 H(x)=x
,即输入是 x
,经过该冗余层后,输出仍然为 x
。但实际上想要达到这种学习目的比较难。
而 ResNet
使用上图的结构,让 H(x)=F(x)+x
,要想让该冗余层能够恒等映射,只需要学习残差项 F(x)=0
。学习 F(x)=0
比学习 H(x)=x
要简单,原因有两点:一般每层网络中的参数初始化偏向于 0
,冗余层学习 F(x)=0
的更新参数能够更快收敛; ReLU
能在输入小于零时直接得到 0
的结果。
考虑到时间开销,Deep Residual Learning for Image Recognition 中提出了优化原来的残差学习结构为瓶颈结构。
The three layers are 1*1
, 3*3
, and 1*1
convolutions, where the 1*1
layers are responsible for reducing and then increasing(restoring) dimensions, leaving the 3*3
layer a bottleneck with smaller input/output dimensions.
首尾的 1*1
卷积分别用来削减和恢复维度,相比于原结构,只留下了中间 3*3
瓶颈,这样网络的参数减少了很多,深度也加深了。
# 编程题(实验 3)
考察重点
网络的定义、如何获取定义好的网络(分类器)、定义损失函数(均方差、交叉熵)
def convolutional_neural_network(img): | |
# 第一个卷积 - 池化层 | |
conv1=fluid.layers.conv2d(input=img, # 输入图像 | |
num_filters=20, # 卷积核大小 | |
filter_size=5, # 卷积核数量,它与输出的通道相同 | |
act="relu") # 激活函数 | |
pool1 = fluid.layers.pool2d( | |
input=conv1, # 输入 | |
pool_size=2, # 池化核大小 | |
pool_type='max', # 池化类型 | |
pool_stride=2) # 池化步长 | |
conv_pool_1 = fluid.layers.batch_norm(pool1) | |
# 第二个卷积 - 池化层 | |
conv2=fluid.layers.conv2d(input=conv_pool_1, | |
num_filters=50, | |
filter_size=5, | |
act="relu") | |
pool2 = fluid.layers.pool2d( | |
input=conv2, | |
pool_size=2, | |
pool_type='max', | |
pool_stride=2, | |
global_pooling=False) | |
conv_pool_2 = fluid.layers.batch_norm(pool2) | |
# 第三个卷积 - 池化层 | |
conv3=fluid.layers.conv2d(input=conv_pool_2, num_filters=50, filter_size=5, act="relu") | |
pool3 = fluid.layers.pool2d( | |
input=conv3, | |
pool_size=2, | |
pool_type='max', | |
pool_stride=2, | |
global_pooling=False) | |
# 以 softmax 为激活函数的全连接输出层, 10 类数据输出 10 个数字 | |
prediction = fluid.layers.fc(input=pool3, | |
size=10, | |
act='softmax') | |
return prediction |
# 获取分类器,用 cnn 进行分类 | |
predict = convolutional_neural_network(images) |
import paddle | |
import paddle.fluid as fluid | |
定义损失函数和准确率 | |
cost1 = fluid.layers.cross_entropy(input=predict, label=label) # 交叉熵 | |
cost2 = fluid.layers.mse_loss(input=predict, label=label) # 均方差 | |
avg_cost = fluid.layers.mean(cost1) # 计算 cost 中所有元素的平均值 | |
acc = fluid.layers.accuracy(input=predict, label=label) # 使用输入和标签计算准确率 |