课程链接:https://aistudio.baidu.com/aistudio/course/introduce/1767
致谢:感谢百度的课程分享,感谢老师的讲授,在此次的课程中自己是感到了收获满满。
看朱老师手敲代码是一种享受,期待下一次课程。
提纲
- 卷积:空洞卷积
- 池化:Adaptive Pool
- 填充:paddings=[h_diff//2, h_diff - h_diff//2, w_diff//2, w_diff - w_diff//2]
- 感受野、多尺度:不同层的特征图、不同尺度卷积核、空洞卷积
- 上采样 / 特征图变大的三种方式:双线性插值、反卷积、反池化
- 信息融合的方式:直接相加、concat + 1x1 卷积
- 上下文
- AlexNet
PS :
- a,b = 1 是将数值1同时赋值给a、b 两个变量
- 网络安全:美团密码不能是身份证中连续数字
卷积(Convolution)
卷积:
- 卷:翻转 + 滑动
- 积:加权求和
整体看来是这么个过程:
翻转——>滑动——>叠加——>滑动——>叠加——>滑动——>叠加…
此图出处:链接
为什么实际过程中没有对卷积核进行翻转操作?
因为卷积核的参数是可学习的,将其翻转之后在学习和直接对不翻转的卷积核进行学习,学习到的卷积核结果是一样的(一样的体现:二者差的只是一个翻转,如果翻转后,二者会完全一样)。
卷积公式:
空洞卷积(Dilated Convolution):
空洞卷积
- 空洞卷积,中间空出来的位置填0 ,卷积核只是看起来是变大了
- 空洞卷积优点:增大感受野
空洞卷积公式:
此图出处:链接
- 其中 D ,是空洞卷积中的参数,表示的是将原
参考文章:
- 从不同角度理解卷积:知乎链接
- 关于卷积的一些想了解的问题:CSDN链接
- 卷积核一定越大越好?(不一定,因为卷积核越大,记录的信息越多,计算量越大,不利于模型深度的增加。)
- 每层卷积只能用一种尺寸的卷积核?(Inception结构,一层卷积中使用了不同尺度的卷积模块。但参数量增加导致计算量增加了)
- 怎样才能减少卷积层参数量?(1x1 卷积降维)
- 能否让固定大小的卷积核看到更大范围的区域?(Dilated convolution,空洞卷积)
- 未来一些趋势:
- 卷积核方面:
- 大卷积核用多个小卷积核代替;
- 单一尺寸卷积核用多尺寸卷积核代替;
- 固定形状卷积核趋于使用可变形卷积核;
- 使用1×1卷积核(bottleneck结构)。
- 卷积层通道方面:
- 标准卷积用depthwise卷积代替;(depthwise卷积,对输入层的每个通道独立进行卷积运算)
- 使用分组卷积;
- 分组卷积前使用channel shuffle;
- 通道加权计算。
- 卷积层连接方面:
- 使用skip connection,让模型更深;(残差块的连接方式,为了解决在训练的过程中梯度爆炸和梯度消失问题。)
- densely connection,使每一层都融合上其它层的特征输出(DenseNet)
- 卷积核方面:
- 如何理解空洞卷积:知乎链接,主要是有两个gif动图,形象的描述了正常卷积和空洞卷积。
填充
paddings=[h_diff//2, h_diff - h_diff//2, w_diff//2, w_diff - w_diff//2]
- 注意:右侧、下侧的填充参数应该是h_diff - h_diff//2 、 w_diff - w_diff//2 ,而不应该是单纯的填充参数除 2。
池化
- 最大池化
- 平均池化
- Adaptive Pool:池化结果可以不满足 H=W
Adaptive Pool
- 行列是分开计算的, i 就是指具体的第 i 行 或 第i列 。
- 区别:
- 正常的池化操作,Hin/Hout 在 H 和 W 维度,二者是相等的。
- Adaptive Pool,Hin/Hout 在 H 和 W 维度,二者是可以不同的。
感受野
感受野:在卷积神经网络中,感受野(Receptive Field)的定义是卷积神经网络每一层输出的特征图(feature map)上的像素点在输入图片上映射的区域大小。再通俗点的解释是,特征图上的一个点对应输入图上的区域。
为什么需要多尺度?
- 距离不同,在原图上所占区域不同:近大远小。
- 个体差异:距离相同,人和猫在原图上所占区域不同。
上采样(特征图变大的三种方式)
参考链接:博客园链接
问:反池化时:填回去的时候怎么知道填在哪?
答:有一些框架在实现 UnPooling 时需要给一个 Indices 来记录原来哪些最大值所在的 block 中的位置信息。
反卷积计算过程:
- 第一步:将卷积核做一个翻转(水平翻转 + 垂直翻转 --> 最终是关于原来的一个中心翻转)。(PS:标准卷积过程是先对卷积核做180度的中心翻转,然后进行滑窗卷积过程)
- 第二步:padding ,简单来说,填充实现的目标是:卷积核右下角元素与数据左上角元素能够重合。(填充 = (卷积核大小 - 1 )* 2 , 乘 2 原因是因为在上下左右两侧都需要进行填充)。
- 第三步:执行正常的卷积过程,卷积结果就是上采样得出的结果。
矩阵角度理解反卷积
信息融合的方式:
- 直接相加:后面再接其他卷积核进行学习
- concat + 1x1 卷积(降维,否则计算量太大)
concat 举例
上下文
图1:上下文
上下文是什么?
- 物体空间依赖:人脸是人体的一部分
- 语义类别依赖:厨房中常见厨具(共现关系),厨房中不应该出现床(互斥关系)
- 场景先验:在知道背景是操场的情况下,应该出现草地、足球,而不应该出现床或者厨具。
通俗理解来说:一篇文章,给你摘录一小段,没前没后,你读不懂,是因为没有了语境,也就是没有了语言环境的存在,一段话说了什么,要通过上下文(文章的上下文)来推断(链接 )。
而在深度学习中,上下文信息对当前像素位置的信息判断也是有帮助的,举个例子:有一个物体,你对它的预测是 汽车 或者是 船 且预测的可能性是相同的,这时候如果你知道它周围的像素都是属于 水 那一类,自然就应该将该物体分类为船,而不是汽车。在这里“ 它周围的像素都是属于 水 那一类 ” 就是上下文。
AlexNet
- 数据集:ImageNet LSVRC-2010 ,其拥有高清图片120万张,分类1000个。
- 实验结果:top-1 和 top-5 的错误率分别为 37.5% 和 17.0% 。
- 扩展: ILSVRC-2012 竞赛的数据集,top-5 测试集错误率 15.3% ,而第二名实现的错误率为26.2%。
- 实验环境:两块GTX 580 3GB GPU上花费五到六天的时间来训练。(将网络分布在两个GPU上训练的原因)
- 图像预处理:将图像下采样到256×256的固定分辨率。先对原图进行缩放,使得短边长度变到256,在进行中心裁剪到 256x256 , 然后对每个像素中减去训练集的像素均值,没有除方差所以是在像素的(中心)原始RGB值上训练了我们的网络。
- 使用的激活函数:Relu (Rectified Linear Units),修正线性单元(函数是非线性函数)。
- 局部相应归一化(Local Response Normalization)
- 重叠池化(Overlapping Pooling)。
- 数据增强:
- 平移图像、水平翻转、十个裁剪((4角+1中心)*2 水平翻转)
- 给图像增加一些随机的光照。【光照、彩色变换,color jittering】:对RGB空间做PCA,然后对主成分做一个(0,0.1)的高斯扰动。结果让错误率下降了1%。
- dropout 防止过拟合。
问:为什么要使用更大的数据集?
答:目前MNIST数字识别任务的最低错误率(<0.3%)基本达到了人类的识别水平[4]。但是物体在现实环境中可能表现出相当大的变化性,所以要学会识别它们,就必须使用更大的训练集。
卷积神经网络(CNN)的容量(模型的学习能力),可以通过改变它的深度和宽度来实现。
与具有相同大小的层的标准前馈神经网络相比(例如FC),CNN具有更少的连接和参数,因此它们更容易训练,而其理论最优性能可能稍微弱一些。
问:为什么使用局部相应归一化?
答:对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。参考链接
问:为什么使用重叠最大池化?
答:此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。参考链接
AlexNet 网络整体结构
本图来自链接: 简书链接
什么是 Top-1 和 Top-5
AlexNet 网络代码
# 定义 AlexNet 网络结构
class AlexNet(fluid.dygraph.Layer):
def __init__(self, num_classes=1):
super(AlexNet, self).__init__()
# AlexNet与LeNet一样也会同时使用卷积和池化层提取图像特征
# 与LeNet不同的是激活函数换成了‘relu’
self.conv1 = Conv2D(num_channels=3, num_filters=96, filter_size=11, stride=4, padding=5, act='relu')
self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
self.conv2 = Conv2D(num_channels=96, num_filters=256, filter_size=5, stride=1, padding=2, act='relu')
self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
self.conv3 = Conv2D(num_channels=256, num_filters=384, filter_size=3, stride=1, padding=1, act='relu')
self.conv4 = Conv2D(num_channels=384, num_filters=384, filter_size=3, stride=1, padding=1, act='relu')
self.conv5 = Conv2D(num_channels=384, num_filters=256, filter_size=3, stride=1, padding=1, act='relu')
self.pool5 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
self.fc1 = Linear(input_dim=12544, output_dim=4096, act='relu')
self.drop_ratio1 = 0.5
self.fc2 = Linear(input_dim=4096, output_dim=4096, act='relu')
self.drop_ratio2 = 0.5
self.fc3 = Linear(input_dim=4096, output_dim=num_classes)
def forward(self, x):
x = self.conv1(x)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.conv5(x)
x = self.pool5(x)
x = fluid.layers.reshape(x, [x.shape[0], -1])
x = self.fc1(x)
# 在全连接之后使用dropout抑制过拟合
x= fluid.layers.dropout(x, self.drop_ratio1)
x = self.fc2(x)
# 在全连接之后使用dropout抑制过拟合
x = fluid.layers.dropout(x, self.drop_ratio2)
x = self.fc3(x)
return x