文章目录
- 一、CNN模型原理
- 1.1 图像
- 1.2 DNN图像分类的问题
- 1.3 卷积原理
- 1.4 池化原理
- 1.5 Flatten
- 1.6 卷积池化总结
- 二、卷积池化计算
- 2.1. 初识卷积
- 2.2. CNN中的卷积层
- 2.2.1 二维卷积
- 2.2.2 三维卷积
- 2.2.3 卷积计算公式
- 2.3 填充(padding)、步幅(stride)和通道(channel)
- 2.3.1 填充
- 2.3.2 步幅
- 2.3.3 多通道
- 2.3.4 小结
- 2.4 CNN中的池化层
- 2.5 CNN前向传播算法
- 2.6 CNN反向传播算法
- 三、深入卷积层
- 3.1 1×1卷积
- 3.2 VGGnet:使用块、小尺寸卷积效果好
- 3.3 NiN
- 3.4 inception宽度卷积核和GoogLeNet
- 3.4 Depth wise和Pointwise降低运算量
- 3.5 SENet、CBAM特征通道加权卷积
- 3.5.1 SENet
- 3.5.2 CBAM
- 3.6 inception几个改进版
- 3.6.1 Inception2
- 3.6.2 Inception3
- 3.6.3 Xception、inception4
- 3.6.4 inception ResNetV1&2
- 3.7 Resnet
- 3.8 Resnext
- 3.8.1 模型效果
- 3.8.2 Group Conv组卷积
- 3.8.3 模型结构
- 3.9 树叶分类竞赛
参考动手深度学习《卷积神经网络》、《现代卷积神经网络》、《深度学习在图像处理中的应用》
一、CNN模型原理
1.1 图像
- 图像具有平移不变性和旋转不变性。即对图像的平移或者轻微旋转不改变其类别。图像可以用像素点来表示,存储为一个三维矩阵(长×宽×channels)
- 黑白图片channels=1,即每个像素点只有灰度值。彩色图像channels=3,每个像素点由RGB三原色组成,对应一个三维向量,值域[0,255]。一般0表示白色,255表示黑色
1.2 DNN图像分类的问题
如果直接将图像根据各像素点的向量作为图片特征输入模型,例如LR、SVM、DNN等模型进行分类,理论上可行,但是面临以下问题:
- 图像的平移旋转、手写数字笔迹的变化等,会造成输入图像特征矩阵的剧烈变化,影响分类结果。即不抗平移旋转等。
- 一般图像像素很高,如果直接DNN这样全连接处理,计算量太大,耗时太长;参数太多需要大量训练样本
1.3 卷积原理
- 人类认识图片的原理:不纠结图像每个位置具体的像素值,重点是每个区域像素点组成的几何形状,以及几何形状的相对位置和搭配。也就是图像中更抽象的轮廓信息。
- 模型需要对平移、旋转、笔迹变化等不敏感且参数较少。
卷积原理:
- CNN通过卷积核对图像的各个子区域进行特征提取,而不是直接从像素上提取特征。子区域称为感受野。
- 卷积运算:图片感受野的像素值与卷积核的像素值进行按位相乘后求和,加上偏置之后过一个激活函数(一般是Relu)得到特征图Feature Map。
- 特征图:卷积结果不再是像素值,而是感受野形状和卷积核的匹配程度,称之为特征图Feature Map。卷积后都是输出特定形状的强度值,与卷积核形状差异过大的感受野输出为0(经过Relu激活),所以卷积核也叫滤波器Filter。
卷积的特点:
- 使用一个多通道卷积核对多通道图像卷积,结果仍是单通道图像(多通道分别卷积后加和得到最终结果)。要想保持多通道结果,就得使用多个卷积核。同一个通道的卷积结果是一类特征(同一个卷积核计算的结果)。
上图演示了一个具有两个输入通道的二维互相关运算的示例。阴影部分是第一个输出元素以及用于计算这个输出的输入和核张量元素: (1×1+2×2+4×3+5×4)+(0×0+1×1+3×2+4×3)=56 - 卷积核的参数为待学习参数,可以通过模型训练自动得到。
- 图像识别中有很多复杂的识别任务,如果每个图像对应一个卷积核,那么卷积核会很大,参数过多。另外复杂图像形状各异,但是基本元素种类差不多。所以CNN使用多个不同尺寸的卷积核进行基本形状的提取
- 假设图像大小为矩阵,卷积核的尺寸为矩阵,图像边缘像素填充:P,卷积的步伐为S,那么经过一层这样的卷积后出来的图像为:W=(N-K+2P)/S+1。当步幅为1,padding=1时,卷积后图像尺寸不变。
1.4 池化原理
池化层的作用:缩减图像尺寸;克服卷积时对位置的敏感(轻微平移旋转);间接增大后续卷积的感受野;降低运算量和参数量
- 消除相邻感受野的信息冗余现象(相邻感受野形状差异不大)
- 池化操作对子区域内的轻微改变不敏感,所以可以克服图像平移。轻微旋转的影响
- 缩减特征图,增大后续卷积操作的感受野,以便后续提取更宏观的特征。(比如经过2×2池化,池化后图像每个像素对应于原图像2×2的感受野,此时在用3×3卷积,那么卷积后每个像素对应于6×6的感受野)
- 降低运算量和参数量
池化的特点:
- 只需要设定池化层大小和池化标准(最大池化或均值池化、中位数池化),就可以进行池化计算,没有参数需要学习
- 最大池化提取特征能力较强,但是容易被噪声干扰。均值池化相对稳定,对噪声不敏感
- 池化在各个通道上独立进行
- 池化步长一般会参考感受野尺寸。当二者相等时,池化时没有交集
池化技巧:
如果有20个卷积层,max池化和ave池化混用,怎么安排?应该是前面的层用最大池化,尽可能的提取特征;后面层用ave池化,减少尺寸抗平移。
因为一开始就用平均池化,把特征平均掉了,就很难恢复了。所以是先提取特征再去噪。训练样本足够多的时候,不太容易被噪声所影响,直接用max池化。(足够的样本可以平均噪声)
而在不同场景用的池化操作也不一样:
- 人脸识别:公司打卡系统 。对特征要求高,需要把每个人的五官特点提取出来(max pool)
- 人脸检测:画面是否有人 。要求低,大概轮廓出来即可(ave pool)
1.5 Flatten
- 卷积-池化输出是一个多通道的而为特征,而最后softmax分类,softmax只能作用于一个向量。所以需要对卷积0池化结果进行拉平操作,也就是Flatten。
- Flatten没有参数,例如一个7×7×10的矩阵,会被拉平成490维向量。
1.6 卷积池化总结
CNN使用不同卷积核提取图像中特定的形状特征,配合池化操作进一步缩减图像尺寸,稳定特征。对低级特征进行卷积计算,得到相对高级的特征。所以多层卷积-池化,层层堆叠可以提取复杂特征。
需要注意的是:
- 一个CNN模型只能处理一种尺寸的图像,所以实际中需要将输入图像全部处理成同一尺寸
- 为了防止数值溢出和激活函数饱和造成的梯度消失或者梯度爆炸,输入图像像素值会归一化至[0,1]
- CNN中每个通道代表同一类特征(同一个卷积核计算的结果),所以可以对同一个通道的数值进行批归一化。分别计算n个通道上的n组均值和方差。
- 对于28×28×1的图像,如果全连接并保持图像尺寸不变,则参数量为28×28×1×28×28×1=614656个。如果进行3×3卷积并保持尺寸不变,参数量为28×28×1×3×3。可以理解为隐藏层每个神经元只与输入层9个神经元相连,其它连接都被剪枝,各个位置参数共享(隐藏层还是576神经元,但是前后层都是9*9相连)所以CNN是通过权重共享和局部感受野(剪枝)对DNN全连接进行简化。
二、卷积池化计算
一个常见的CNN例子如下图:
2.1. 初识卷积
微积分中卷积的表达式为:
离散形式是:
这个式子如果用矩阵表示可以为:
其中星号表示卷积。
如果是二维的卷积,则表示式为:
在CNN中,虽然我们也是说卷积,但是我们的卷积公式和严格意义数学中的定义稍有不同,比如对于二维的卷积,定义为:
这个式子虽然从数学上讲不是严格意义上的卷积,但是大牛们都这么叫了,那么我们也跟着这么叫了。后面讲的CNN的卷积都是指的上面的最后一个式子。
其中,我们叫W为我们的卷积核,而X则为我们的输入。如果X是一个二维输入的矩阵,而W也是一个二维的矩阵。但是如果X是多维张量,那么W也是一个多维的张量。
2.2. CNN中的卷积层
图像卷积:对输入的图像的不同局部的矩阵和卷积核矩阵各个位置的元素相乘,然后相加得到。
举个例子如下:
- 输入二维的3x4的矩阵
- 卷积核是一个2x2的矩阵
- 卷积步长为1(一次移动一个像素来卷积)
- 首先我们对输入的左上角2x2局部和卷积核卷积,即各个位置的元素相乘再相加,得到的输出矩阵S的的元素,值为。
- 我们将输入的局部向右平移一个像素,现在是(b,c,f,g)四个元素构成的矩阵和卷积核来卷积,这样我们得到了输出矩阵S的的元素
- 同样的方法,我们可以得到输出矩阵S的的元素。
图像卷积,回想我们的上一节的卷积公式,其实就是对输入的图像的不同局部的矩阵和卷积核矩阵各个位置的元素相乘,然后相加得到。
2.2.1 二维卷积
举例如下:
- 输入二维的3x4的矩阵,卷积核是一个2x2的矩阵。步幅S=1。首先我们对输入的左上角2x2局部和卷积核卷积,即各个位置的元素相乘再相加,得到的输出矩阵S的的元素,值为。
- 接着我们将输入的局部向右平移一个像素,现在是(b,c,f,g)四个元素构成的矩阵和卷积核来卷积,这样我们得到了输出矩阵S的的元素
- 同样的方法,我们可以得到输出矩阵S的的元素。
最终我们得到卷积输出的矩阵为一个2x3的矩阵S。
假设图像大小为矩阵,卷积核的尺寸为矩阵,图像边缘像素填充:P,卷积的步伐为S,那么经过一层这样的卷积后出来的图像为:W=(N-K+2P)/S+1
2.2.2 三维卷积
输入是多维的情况,在斯坦福大学的cs231n的课程上,有一个动态的例子
- 输入3个7x7的矩阵。(原输入是3个5x5的矩阵,padding=1)
- 卷积核:
- 两个卷积核,卷积核的个数是即模板的个数。
- 卷积核也是三维,且最后一维和输入维数第三维channel数一样。卷积核W0的单个子矩阵维度为3x3,加上最后一维为3,最终是一个3x3x3的张量。
- 步幅为2
- 张量的卷积:两个张量的3个子矩阵卷积后,再把卷积的结果相加后再加上偏置。
每个卷积核卷积的结果是一个3x3的矩阵,卷积的结果是一个3x3x2的张量。把上面的卷积过程用数学公式表达出来就是:
其中,为输入矩阵的个数,或者是张量的最后一维的维数。代表第k个输入矩阵(channel个)。代表卷积核的第k个子卷积核矩阵。即卷积核对应的输出矩阵的对应位置元素的值。
- 激活:对于卷积后的输出,一般会通过ReLU激活函数,将输出的张量中的小于0的位置对应的元素值都变为0。
2.2.3 卷积计算公式
表示图像的第行第列元素;用表示卷积核filter第m行第n列权重,用表示filter的偏置项;用表示特征图Feature Map的第i行第j列元素;用表示激活函数(这个例子选择relu函数作为激活函数)。使用下列公式计算卷积:
如果卷积前的图像深度为D,那么相应的filter的深度也必须为D。我们扩展一下上式,得到了深度大于1的卷积计算公式:
W2是卷积后Feature Map的宽度;W1是卷积前图像的宽度;F是filter的宽度;P是Zero Padding数量。D是深度(卷积核个数);F是卷积核filter的矩阵维数;表示filter的第d层第m行第n列权重;表示Feature Map图像的第d层第i行第j列像素。
每个卷积层可以有多个卷积核filter。每个filter和原始图像进行卷积后,都可以得到一个Feature Map。因此,卷积后Feature Map的深度(个数)和卷积层的filter个数是相同的。
2.3 填充(padding)、步幅(stride)和通道(channel)
- 连续的卷积后原始图像的边界丢失了许多有用信息,而填充是解决此问题最有效的方法,即在输入图像的边界填充元素(通常填充元素是 0 )。填充还用于保持图片形状不变。
- 有时,我们可能希望大幅降低图像的宽度和高度。例如,如果我们发现原始的输入分辨率十分冗余。步幅则可以在这类情况下提供帮助。
2.3.1 填充
- 在许多情况下,我们需要设置和,使输入和输出具有相同的高度和宽度。
这样可以在构建网络时更容易地预测每个图层的输出形状。 - CNN中卷积核的高度和宽度通常为奇数,例如1、3、5或7。这样保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。
2.3.2 步幅
在计算互相关时,卷积窗口从输入张量的左上角开始向下、向右滑动,我们将每次滑动元素的数量称为步幅(stride)。有时候为了高效计算或是缩减采样次数,可以设置步幅大于1。
上图是垂直步幅为,水平步幅为的二维互相关运算。着色部分是输出元素以及用于输出计算的输入和内核张量元素:、。
- 为了简洁起见,当输入高度和宽度两侧的填充数量分别为和时,我们称之为填充。当时,填充是。同理,当高度和宽度上的步幅分别为和时,我们称之为步幅。当时的步幅为时,步幅为。
- 默认情况下,填充为0,步幅为1
- 在实践中,我们很少使用不一致的步幅或填充,也就是说,我们通常有和。
2.3.3 多通道
- 使用一个多通道卷积核对多通道图像卷积,结果仍是单通道图像(多通道分别卷积后加和得到最终结果)。要想保持多通道结果,就得使用多个卷积核。同一个通道的卷积结果是一类特征(同一个卷积核计算的结果)。
- 每个输出通道可以识别特定的模式(可以认为是每个通道的卷积核都是学习到不同的参数,匹配特定的模式)
- 下一层输入通道核,识别并组合输入中的模式(比如上图输出通道6,这6种模式组合起来进入下一个卷积层作为输入)
复杂度计算:
上图计算一次卷积是100×100×5×5×64×64=10.24亿次浮点运算,大约是1G的运算量
如果10层卷积运算,有百万样本,运算量是10Plops。
2.3.4 小结
- 填充可以增加输出的高度和宽度。这常用来使输出与输入具有相同的高和宽。
- 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的(是一个大于的整数)。
- 填充和步幅可用于有效地调整数据的维度。
- 多输出通道类似提取多种图片的特征,多输入通道是组合上一层提取的特征,并再次使用多个卷积核来提取特征。(个人理解)一般每个通道的卷积核参数不一样,但是尺寸大小一样,这样方便计算。(如果大小不一样,就要分别进行卷积计算)
2.4 CNN中的池化层
- 池化,就是对输入张量的各个子矩阵进行压缩,将输入子矩阵的每nxn个元素变成一个元素,所以需要一个池化标准。
- 常见的池化标准有2个,MAX或者是Average。即取对应区域的最大值或者平均值作为池化后的元素值。
2x2最大池化,步幅为2时,池化操作如下:
2.5 CNN前向传播算法
输入:1个图片样本,CNN模型的层数L和所有隐藏层的类型,对于卷积层,要定义卷积核的大小K,卷积核子矩阵的维度F,填充大小P,步幅S。对于池化层,要定义池化区域大小k和池化标准(MAX或Average),对于全连接层,要定义全连接层的激活函数(输出层除外)和各层的神经元个数。
输出:CNN模型的输出
。
=2 to (层数):
层是卷积层,则输出为(*表示卷积,而不是矩阵乘法)
(这里要定义卷积核个数,卷积核中每个子矩阵大小,填充padding(以下简称P)和填充padding(以下简称P))
b) 如果第层是池化层,则输出为:
需要定义池化大小和池化标准,池化层没有激活函数
层是全连接层,则输出为:
输出层第L层:
2.6 CNN反向传播算法
三、深入卷积层
3.1 1×1卷积
- 卷积的本质是有效提取相邻像素间的相关特征,而1×1卷积显然没有此作用。
- 1×1卷积不识别空间模式,只融合通道。并常用来改变通道数,降低运算量和参数量。同时增加一次非线性变化,提升网络拟合能力。
- 输出中的每个元素都是从输入图像中同一位置的元素的线性组合。我们可以将1×1卷积层看作是在
每个像素位置应用的全连接层,以个输入值转换为 - 对于一个的图像,进行卷积操作,得到的图像,且参数量仅有(忽略偏置)。
- 时起到降维的作用,降低其它卷积操作的运算量。但是降维太厉害会丢失很多信息。
- 时起到升维作用(增加通道数),可以让后续卷积层提取更加丰富的特征
- 所以可以先用1×1卷积改变通道数,再进行后续卷积操作,这个是Depth wise提出的。
3.2 VGGnet:使用块、小尺寸卷积效果好
- 卷积的尺寸决定卷积的视野,越大则提取的特征越宏观。但是大尺寸卷积,参数量和运算量都很大,而多个小尺寸卷积可以达到相同的效果,且参数量更小。还可以多次进行激活操作,提高拟合能力。例如:
一个5×5卷积参数量25,可以替换成两个3×3卷积。,参数量为18。每个3×3卷积可以替换成3×1卷积加1×3卷积,参数量为12。 - 使用卷积块来设计神经网络。使用块的想法首先出现在牛津大学的视觉几何组(visualgeometry group)的VGG网络中。通过使用循环和子程序,可以很容易地在任何现代深度学习框架的代码中实现这些重复的架构。
3.3 NiN
AlexNet、VGG等卷积之后都接了两个全连接层,而全连接层参数量是很大的,也容易造成过拟合。
- 卷积层参数:
- n类全连接层参数:
- LeNet 16x5x5x120 = 48k
AlexNet 256x5x5x4096 = 26M
VGG 512x7x7x4096 = 102M
3.4 inception宽度卷积核和GoogLeNet
GoogLeNet重点是解决了什么样⼤⼩的卷积核最合适的问题(使⽤不同⼤⼩的卷积核组合是有利的)
实际CNN识别中,会遇到识别物体尺寸不一的情况。不同尺寸的物体需要不同尺寸的卷积核来提取特征。如果增加网络深度来处理,会造成:
- 训练集不够大,则过拟合
- 深层网络容易梯度消失,模型难以优化
- 简单堆叠较大的卷积层浪费计算资源
为了使卷积层适应不同的物体尺寸,一般会在同一层网络中并列使用多种尺寸的卷积,以定位不同尺寸的物体。此时增加了网络宽度,而不会增加其深度。
2016年google的inception模型首先提出,结构如下:
- 使用四条不同超参数的卷积层和池化层抽取不同的信息,最后结果进行级联(按通道拼在一起)。池化层是抗位置敏感性。
- 多个卷积核级联造成通道数过多,所以可以在卷积前、3×3池化后分别进行1×1卷积进行降维,同时提高网络非线性程度。
- 最终输出和输入图像尺寸相同,但是通道数可以不一样。
- 与单个3x3或5x5卷积层相比,初始块具有更少的参数和更低的计算复杂度
不同功能混合(多样的功能类)
卷积核计算高效(良好的泛化)
多个inception堆叠就是GoogLeNet:
这样会有一个问题: 网络太深造成梯度消失,前面的层基本就没有什么信息了(梯度消失学不到)。所以中间层引入两个辅助分类器,并配以辅助损失函数。防止前层网络信息丢失。具体地:
- 三个黄色和椭圆模块是做三次分类。即在3.6.9层inception时输出做分类。
- 三个分类器的任务完全一样,。的系数应该高一些,具体权重可以查。辅助分类器只用来训练,不用于推断
- 训练时三个分类器一起训练,使用的时候只用最后一层。最后一个inception使用全局平均池化(没有要求最后一个inception输出通道等于类别数)
GoogleNet知识点:
- inception
- 深层网络可以从中间抽取loss来训练,防止过拟合。
- 启发:网络太深,涉及梯度消失时,都可以这样搞:中间层可以抽出loss来一起学习。(shortcut也可以,一个道理,可能还好一点,比较方便)。
- 借鉴了NiN使用大量1×1卷积替代全连接层,最后用了全局平均汇聚层。
- 后续有一系列的改进,到V3、V4精度才真正提上来,并一直在被使用。但是太复杂了,其实不怎么受欢迎。(实际使用GoogleNet结构最好不要大改,通道数可以倍增倍减这样动动。)
GoogleNet:
- 第一个红色框住的模块叫stem(根源),就是传统的卷积。
- 后面九个模块都是inception,再过一个全连接层,最后过softmax进行分类。
GluonCV 模型“动物园”https://gluon-cv.mxnet.io/model_zoo/classification.html
3.4 Depth wise和Pointwise降低运算量
- 传统卷积:一个卷积核卷积图像的所有通道,参数过多,运算量大。
- 运算量(忽略偏置):
参数量(忽略偏置): - Depth wise卷积:一个卷积核只卷积一个通道。输出图像通道数和输入时不变。缺点是每个通道独立卷积运算,没有利用同一位置上不同通道的信息
- Pointwise卷积:使用多个1×1标准卷积,将Depth wise卷积结果的各通道特征加权求和,得到新的特征图
- 运算量(忽略偏置):
参数量(忽略偏置):
3.5 SENet、CBAM特征通道加权卷积
3.5.1 SENet
可参考《CNN卷积神经网络之SENet及代码》 SENet:卷积操作中,每个通道对应一类特征。而不同特征对最终任务结果贡献是不一样的,所以考虑调整各通道的权重。
- SE模块,对各通道中所有数值进行全局平均,此操作称为Squeeze。比如28×28×128的图像,操作后得到128×1的向量。
- 此向量输入全连接网络,经过sigmoid输出128维向量,每个维度值域为(0,1),表示各个通道的权重
- 在正常卷积中改为各通道加权求和,得到最终结果
- Squeeze建立channel间的依赖关系;Excitation重新校准特征。二者结合强调有用特征抑制无用特征
- 能有效提升模型性能,提高准确率。几乎可以无脑添加到backbone中。根据论文,SE block应该加在Inception block之后,ResNet网络应该加在shortcut之前,将前后对应的通道数对应上即可
3.5.2 CBAM
除了通道权重,CBAM还考虑空间权重,即:图像中心区域比周围区域更重要,由此设置不同位置的空间权重。CBAM将空间注意力和通道注意力结合起来。
Channel attention module:
- 输入特征图F,经过两个并行的最大值池化和平均池化将C×H×W的特征图变成C×1×1的大小
- 经过一个共享神经网络Shared MLP(Conv/Linear,ReLU,Conv/Linear),压缩通道数C/r (reduction=16),再扩张回C,得到两个激活后的结果。
- 最后将二者相加再接一个sigmoid得到权重channel_out,再加权求和。
此步骤与SENet不同之处是加了一个并行的最大值池化,提取到的高层特征更全面,更丰富。
Channel attention module:
将上一步得到的结果通过最大值池化和平均池化分成两个大小为H×W×1的张量,然后通过Concat操作将二者堆叠在一起(C为2),再通过卷积操作将通道变为1同时保证H和W不变,经过一个sigmoid得到spatial_out,最后spatial_out乘上一步的输入变回C×H×W,完成空间注意力操作
总结:
- 实验表明:通道注意力在空间注意力之前效果更好
- 加入CBAM模块不一定会给网络带来性能上的提升,受自身网络还有数据等其他因素影响,甚至会下降。如果网络模型的泛化能力已经很强,而你的数据集不是benchmarks而是自己采集的数据集的话,不建议加入CBAM模块。要根据自己的数据、网络等因素综合考量。
3.6 inception几个改进版
google对inception进行改造,出现了inception1→inception2→inception3→Xception→inception4→inception ResNetV1→inception →ResNetV2。
3.6.1 Inception2
变种A是基础版的5×5改成两个3×3,B是3×3拆成两个,C是拆成的两个并联。
对卷积核进行了几种改造。但是设计思想都是:
1.大量应用1×1卷积核和n×1卷积核(1×3和3×1)
2.大量应用小卷积核(没有超过3乘3的)
3.并联卷积
3.6.2 Inception3
最大贡献:标签平滑防止过拟合
- 对于逻辑回归来说,单个样本。y’∈(0,1)是一个闭区间,预测值y’只能无限逼近0和1,但是永远取不到0或1。即单个样本没有极小值。
- 这样在拟合的时候随着梯度下降,y’不断向0或1逼近,w会不断增大。而如果标签y=1做平滑改成y=0.97,y’就可以取到这个值,w就不会无限增大,所以避免了过拟合。
- 也可以看做对标签适当注入噪声防止过拟合。(LR可以看做二分类的softmax,所以此处也适用)
- 加正则项主要是让模型在测试集上的效果尽可能和训练集效果一样好,标签平滑让模本本身有一个好的性能(防止标签打错等噪声)。
3.6.3 Xception、inception4
- Xception:3×3正常卷积变成Depth wise(上一节讲过)
- inception4是改变了stem
3.6.4 inception ResNetV1&2
inception ResNetV1&2最主要思想就是与shortcut结合。inception模块的输出和模块输入直接按位相加。即对应channel对应位置的元素相加。这样就要求输出的channel和尺寸要和输出的一致。而一般不池化尺寸可以不变,channel数通过最后一层的1×1卷积核来调整。
至于中间的细节,右侧的构造为啥是这样的,都是试验碰出来的,没必要纠结。
3.7 Resnet
) )。这种现象在嵌套函数类中不会发生。因此, 只有当较复杂的函数类包含较小的函数类时,我们才能确保提升它们的性能。
对于深度神经⽹络,如果我们能将新添加的层训练成恒等映射(identity function)f(x) = x,新模型和原模型将同样有效。同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。针对这一问题,何恺明等人提出了残差往络(ResNet)[He et al., 2016a]。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。 残差网络的核心思想是:每个附加层都应该更容易地包含原始函数作为其元素之一。
Resnet也是借鉴shortcut思想,因为网络太深必然会碰到梯度消失的问题。然后就是一堆小卷积核,每两层抄一次近道是试验出来的效果。抄近道就必须保持前后的channel数一致。
7.6. 残差网络(ResNet)答疑
- 为啥f(x)=x+g(x)可以保证模型至少不会变坏?g(x)难道不会学的比较差反而影响f(x)效果吗?
这是因为g(x)也是训练出来的,如果模型发现只用f(x)=x效果就很好,而加上g(x)对loss的下降几乎没什么贡献,那么梯度反传的时候,g(x)部分几乎拿不到梯度,权重就很小,这样g(x)对整个模型几乎没什么影响。所以这就是为啥resnet加深时通常不会让模型效果变差。- 为啥这个网络叫残差网络?残差概念体现在哪?
以resnet51举例,可以认为是先训练一个resnet18的基础模型来拟合,剩下的与真实值的误差(残差)用剩下的层进一步拟合(先训练下层的基础block,剩下没训练好的上层继续fit。这就是残差Residual的由来)- 训练acc是永远大于测试acc吗?是不是意味着图片识别永远达不到100%识别
测试acc是有可能大于训练acc的,这是因为训练时构造数据可能会加入一些误差,而测试集没有。模型识别率也不会达到100%,因为ImageNet数据集本身标识就有2%-5% 的错误。所以追求100%没必要。(一般不能假设数据集100%分类正确,因为可能有些样本人都很难分)
残差块(residual block):
虚线是添加的层,直接添加层会更改原特征类
假设我们的原始输入为x,模型希望学出的理想映射为f(x)。下图左图虚线框中的部分需要直接拟合出该映射f(x),而右侧虚线框中的部分则需要拟合出残差映射f(x) − x。
残差映射在现实中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射f(x),我们只需将下图中右图虚线框内上方的加权运算(如仿射)的权重和偏置参数设成0,那么f(x)即为恒等映射。实际中,当理想映射f(x)极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。在残差块中,输入可通过跨层数据线路更快地向前传播。
resnet中残差块有两种:(use_1x1cnotallow=True/False)
- 步幅为2 ,高宽减半,通道数增加。所以shortcut连接部分会加一个1×1卷积层改变通道数
- 步幅为1,高宽不变
- 残差块代码实现:
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
class Residual(nn.Module): #@save
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels,
kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)#每个bn都有自己的参数要学习,所以需要定义两个
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
Y += X
return F.relu(Y)
resnet18:类似VGG和GoogLeNet,但是替换了resnet块。一般用resnet34或者50,很少上100,除非刷榜。
Resnet18代码:
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
def resnet_block(input_channels, num_channels, num_residuals,
first_block=False):
blk = []
for i in range(num_residuals):
if i == 0 and not first_block:
blk.append(Residual(input_channels, num_channels,
use_1x1conv=True, strides=2))
else:
blk.append(Residual(num_channels, num_channels))
return blk
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))#resnet_block是一个列表,*表示全部展开
net = nn.Sequential(b1, b2, b3, b4, b5,
nn.AdaptiveAvgPool2d((1,1)),
nn.Flatten(), nn.Linear(512, 10))
3.8 Resnext
论文:Aggregated Residual Transformations for Deep Neural Networks PyTorch代码:https://github.com/miraclewkf/ResNeXt-PyTorch
参考文章《ResNeXt算法详解》、讲解视频《ResNeXt网络结构》
3.8.1 模型效果
ResNeXt 结构可以在不增加参数复杂度的前提下提高准确率,同时还减少了超参数的数量。 (得益于子模块的拓扑结构一样)。
对比 ResNeXt 101和另外几种网络,在不同入网尺寸下,错误更少,效果都更好。
- 左图:统计resnet50和resnext50在ImageNet上top1的错误率。蓝色、橙色实线分别是二者在验证集上的错误率
- 右图 :统计resnet101和resnext101在ImageNet上top1的错误率
3.8.2 Group Conv组卷积
,卷积核个数为n(输出矩阵channel数)。组卷积分成g个组(group)
如下图所示,将输入矩阵的channel划分为两个组(蓝色和绿色),然后对每个组分别进行卷积操作,最后将其在channel维度拼接,得到最终卷积结果。这样算下来,组卷积参数个数是常规卷积的1/g。
3.8.3 模型结构
ResNeXt和ResNet的最本质的区别在于其中使用新的block替换后者中的block:
下面的block模块使用了分组卷积,在数学计算上完全等价:
- 图a:等价于图b。输入channel=4先进行32个分支的1×1的卷积再相加,等价于32个分支先concat拼接成channel=128的输入,再经过256个1×1的卷积。下面举例说明:
图b:可等价转为图c,各层对应。具体的,将输入分为32个分支,每个分支都经过4个1×1卷积核4个3×3的卷积,所以可以合并为图c的结构。
图c:
- 首先通过128个1×1卷积核将channel从256降为128
- 使用g=32的分组卷积, 卷积核大小3×3,个数还是128,这一层输入输出通道数不变
- 通过256个1×1卷积核将channel从128升为256,和原来不变
- 卷积输出结果和输入相加
根据模块c,就可以搭建ResNeXt50:
- 32×4d中,32表示group数,也就是conv中的C的值;4表示每个组中卷积核的个数为4
- 可以看出,ResNeXt50和ResNet50网络结构一样,原有block替换成新的block就行。
- 为啥g=32?作者通过实验发现随着g的增大,错误率越来越低,最后选了32。
- 作者还说过,一般残差block如果结果少于三层是没有多大意义的。
全文看下来,作者的核心创新点就在于提出了 aggregrated transformations,用一种平行堆叠相同拓扑结构的blocks代替原来 ResNet 的三层卷积的block,在不明显增加参数量级的情况下提升了模型的准确率,同时由于拓扑结构相同,超参数也减少了,便于模型移植。
3.9 树叶分类竞赛
图片分类竞赛地址:https://www.kaggle.com/c/classify-leaves
176类树叶,每类至少50张图片。公榜私榜数据集五五分。