1 一些经典CNN模型

这些经典CNN的架构已经没有学习的必要,只要知道它们引入了什么好的训练方法即可。

1.1 LeNet

最开始Yann Lecun发明的CNN即是LeNet,用来做手写数字的识别,LeNet-5是2层卷积+2层全连接+1层高斯连接(现在已经不怎么用了)。每层卷积操作后面还跟着下采样,这里的下采样就是使用例如隔行采样的传统下采样方法。

1.2 AlexNet

2012年Hinton的学生发明了AlexNet,5个卷积层+3个全连接层,当在两块GTX580上一起训练了一周,在ImageNet上提升了10%以上。AlexNet里引入了max pooling来做下采样,激活函数上引入了ReLU,还引入Dropout来防止过拟合。

1.3 VGG

2014年牛津大学一个视觉实验室发明了VGG,它一共有6个版本,层数从11到19层不等。VGG主要发现了使用更小的卷积核能达到更好的训练效果,如resnet和densenet区别 resnet和densenet哪个更好_DenseNet或者resnet和densenet区别 resnet和densenet哪个更好_DenseNet_02的卷积核,不仅速度更快而且效果也更好。

1.4 GoogLeNet

同年Google的GoogLeNet取得了ImageNet的第一名,它一共有22层。GoogLeNet除了和VGG一样探索出使用小卷积核能达到更好效果之外,还发现了对同一层使用不同尺寸的卷积核和pooling核,然后把结果concat到一起(结果也就是多了一个维度,然后用了几种不同的卷积核,这个维度上的数字就是几),能取得更好的效果。这可以抽象一点理解成综合了不同尺寸的感受野,自然能感受到更准确的信息。

1.5 层数高=精度好?

实验发现并不是简单的堆叠更多的层就能提高准确率,反而是在层数过多之后使得准确率下降。这是因为网络中层数太多导致参数过多,没法在能接受的时间内取得较好的训练效果,后面的ResNet即较好的解决了这个问题,使得网络能向更深层次探索。

2 ResNet

2.1 简述

ResNet(深度残差网络)是MSRA的何恺明研究出来的。像前面说的,如果网络的层数过多,在反向传播链式法则求导时候,一堆导数乘在一起常常会导致误差积累、梯度弥散(一堆小于1的相乘)、梯度爆炸(一堆大于1的相乘) 等问题,导致一直训练也没有让情况变好。

ResNet是基于这样的考虑,如已有一个22层的网络,现在想在其基础上做一个30层的网络,让它的最坏情况就是退化成22层的网络结构。所以可以加入恒等快捷连接(identity shortcut connection),直接绕过中间一部分层,使得梯度可以直接传递到回去的某一层:

resnet和densenet区别 resnet和densenet哪个更好_ResNet_03


如上图中存在直接回到第22层的回边,所以resnet和densenet区别 resnet和densenet哪个更好_神经网络_04,向第22层传播梯度没有衰减。上图中输出是resnet和densenet区别 resnet和densenet哪个更好_DenseNet_05,其中resnet和densenet区别 resnet和densenet哪个更好_神经网络_06的部分选择性的学习,这里的resnet和densenet区别 resnet和densenet哪个更好_神经网络_06即是要学习的残差部分。

resnet和densenet区别 resnet和densenet哪个更好_ResNet_08

更多参见这篇回答。回答中也说明了为什么残差单元里至少要有两层卷积层,实际使用时发现2~3层卷积层比较合适。因为最后要做的加法是一个Element Wise的加法,所以要保证残差单元的输入输出通道数和shape都要一致。如果shape不一致,要在短接线那里改成乘上一个矩阵,即resnet和densenet区别 resnet和densenet哪个更好_resnet和densenet区别_09来做维度变换;如果channel数不一致,可以用前面学的resnet和densenet区别 resnet和densenet哪个更好_DenseNet_02卷积或者补0的方式。

老师PPT里还提到设计残差单元的一个技巧,这里在保证输入输出格式一致的情况下,先把通道数减少(这里从256减少到64),到了单元输出的时候再把通道数变回去,这样可以有效减少参数量。可以看到例子里从600K变成70K的参数量:

resnet和densenet区别 resnet和densenet哪个更好_深度学习_11

2.2 实现

直接用老师源码,这里只放核心部分——残差块的实现,完整代码见仓库

特别注意其中channel转换的部分,保证短路输出的resnet和densenet区别 resnet和densenet哪个更好_DenseNet_12能和残差块输出的resnet和densenet区别 resnet和densenet哪个更好_神经网络_06相加。

from torch.nn import functional as F
from torch import nn


class ResBlk(nn.Module):
    """ResNet残差块定义"""

    def __init__(self, ch_in, ch_out):
        """传入输入输出的通道数"""
        super().__init__()
        self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(ch_out)
        self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(ch_out)

        # 这里定义转换部分,如果输入输出通道数不一样,为了能进行element wise
        # 要用1*1卷积把channel变成一样的
        self.extra = nn.Sequential()  # 如果输入输出一样,这里扔进去x出来也是x
        if ch_out != ch_in:
            # [b, ch_in, h, w] => [b, ch_out, h, w]
            self.extra = nn.Sequential(
                nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=1),
                nn.BatchNorm2d(ch_out)
            )

    def forward(self, x):
        """前向传播过程"""
        # 在这里把各个层组织起来,并使用relu激活
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        # 最终结果要和x进行element wise加法
        out = self.extra(x) + out
        return out

3 DenseNet

普通的CNN:

resnet和densenet区别 resnet和densenet哪个更好_ResNet_14


使用ResNet加了快捷连接,快捷连接部分要和残差块进行element wise:

resnet和densenet区别 resnet和densenet哪个更好_ResNet_15


DenseNet中进行了密集连接,即每一层都接受前面的任意一层作为额外输入,并且为了把前面的信息进行综合,这里合并使用的是concat操作而不是element wise加法。所以DenseNet中channel的数量会越来越多。