5

deep learning笔记:记首次ResNet实战

 2 years ago
source link: https://gsy00517.github.io/deep-learning20200113174731/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
deep learning笔记:记首次ResNet实战

deep learning笔记:记首次ResNet实战

发表于 2020-01-13 | 更新于: 2020-02-15 | 分类于 人工智能 | 1 | 阅读次数:
字数统计: 2.2k字 | 阅读时长 ≈ 8分钟

实践出真知。在之前的博文deep-learning笔记:使网络能够更深——ResNet简介与pytorch实现中,我对何恺明大神的CVPR最佳论文中提出的残差网络做了简单介绍。而就在第二年(2016年),何恺明的团队就发表了“Identity Mappings in Deep Residual Networks”这篇文章,分析了ResNet成功的关键因素——residual block背后的算法,并对residual block以及after-addition activation进行改进,通过一系列的ablation experiments验证了,在residual block和after-addition activation上都使用identity mapping(恒等映射)时,能对模型训练产生很好的效果。不知道为什么,我今天从arXiv上download这篇paper的时候发现上不去了,莫非现在上arXiv也要科学上网了?
本次实战主要是基于之前的ResNet实现和flyAI平台,并结合上面提到的何恺明团队分析ResNet的论文做出一些改进,并检验效果。

References

电子文献:
https://blog.csdn.net/Sandwichsauce/article/details/89162570
https://www.jianshu.com/p/184799230f20
https://blog.csdn.net/wspba/article/details/60572886
https://www.cnblogs.com/4991tcl/p/10395574.html
https://blog.csdn.net/DuinoDu/article/details/80435127

参考文献:
[1]Identity Mappings in Deep Residual Networks


ablation experiments

在上面我提到了这个名词,中文翻译是“消融实验”。或许在阅读论文的过程中会接触到这个名词,如果仅根据字面翻译的话或许会很纳闷。
在查找了一定的资料后,我对这种方法有了大致地了解。
ablation的原本释义是通过机械方法切除身体组织,如手术,从身体中去除尤指器官以及异常生长的有害物质。
事实上,这种方法类似于物理实验中的控制变量法,即当在一个新提出的模型中同时改变了多个条件或者参数,那么为了分析和检验,在接下去的消融实验中,会一一控制每个条件或者参数不变,来根据结果分析到底是哪个条件或者参数对模型的优化、影响更大。
在机器学习、特别是复杂的深度神经网络的背景下,科研工作者们已经采用“消融研究”来描述去除网络的某些部分的过程,以便更好地理解网络的行为。


ResNet的分析与改进

  1. 在2015年ResNet首次发布的时候,设计的残差单元在最后的输出之前是要经过一个激活函数的。而在2016年新提出的残差单元中,去掉了这个激活函数,并通过实验证明新提出的残差单元训练更简单。 这种新的构造的关键在于不仅仅是在残差单元的内部,而是在整个网络中创建一个“直接”的计算传播路径来分析深度残差网络。通过构造这样一个“干净”的信息通路,可以在前向和反向传播阶段,使信号能够直接的从一个单元传递到其他任意一个单元。实验表明,当框架接近于上面的状态时,训练会变得更加简单。
  2. shortcut

    对于恒等跳跃连接$h(x_{l})=x_{l}$,作者设计了5种新的连接方式来与原本的方式作对比,设计以及实验结果如下所示: 其中fail表示测试误差超过了20%。实验结果表明,原本的连接方式误差衰减最快,同时误差也最低,而其他形式的shortcut都产生了较大的损失和误差。
    作者认为,shortcut连接中的操作 (缩放、门控、1×1的卷积以及dropout) 会阻碍信息的传递,以致于对优化造成困难。
    此外,虽然1×1的卷积shortcut连接引入了更多的参数,本应该比恒等shortcut连接具有更加强大的表达能力。但是它的效果并不好,这表明了这些模型退化问题的原因是优化问题,而不是表达能力的问题。
  3. 对于激活函数的设置,作者设计了如下几种方式进行比较: 在这里,作者将激活项分为了预激活(pre-activation)和后激活(post-activation)。通过实验可以发现,将ReLU和BN都放在预激活中,即full pre-activation最为有效。

ResNet实战

根据论文中的实验结果,我使用了新的残差模块进行实践。并结合在deep-learning笔记:学习率衰减与批归一化中的分析总结对BN层的位置选取作了简单调整。在本次实验中,我尝试使用了StepLR阶梯式衰减和连续衰减两种学习率衰减方式,事实证明,使用StepLR阶梯式衰减的效果在这里要略好一些(连续衰减前期学得太快,后面大半部分都学不动了…)。
首次训练的结果并不理想,于是我加大了学习率每次衰减的幅度,即让最后阶段的学习率更小,这使我的模型的评分提高了不少。
由于训练资源有限,我没能进行更深(仅设置了10层左右)、更久(每次仅进行20个epoch)的训练,但在每个batch中,最高的accuracy也能达到65%左右,平均大约能超过50%。相比之前使用浅层网络仅能达到20%左右的accuracy,这已经提升不少了。然而最终的打分还是没有显著提高,因此我思考是否存在过拟合的问题。为此我尝试着在全连接层和捷径连接中加入dropout正则化来提高在测试集中的泛化能力,结果最终打分仅提高了0.1,而训练时间稍短。由于我除了dropout之外并没有改变网络的层数等影响参数量的因素,因此似乎与何大神在论文中original版和dropout版shortchut的比较有一些矛盾,但的确还是说明了dropout在这里的作用微乎其微,优化模型时可以排除至考虑范围之外了。


遇到的问题

  1. TabError: inconsistent use of tabs and spaces in indentation

    当我在flyAI提供的窗口中修改代码并提交GPU训练时,就出现了这个报错。它说我在缩进时错误的使用了制表符和空格。于是我只好把报错处的缩进删除并重敲tab缩进,问题就得到了解决。
    如果使用PyCharm等IDE的话,这个错误会直接显示出来,即在缩进处会有灰色的颜色警告,将光标移过去就会有具体报错。这就省得提交GPU之后才能收到报错,所以以后写代码、改代码能用IDE还是用起来好啦。
  2. RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation

    这是在shortcut残差连接时遇到的一个报错,上网后发现原因很简单:版本问题。在新版的pytorch中,由于0.4.0之后把Varible和Tensor融合为一个Tensor,因此inplace操作在之前对Varible时还能用,但现在只有Tensor,就会出错了。
    解决的办法是将x += self.shortcut(x1)替换成x = x + self.shortcut(x1)
    若网络很大,找起来很麻烦,可以在网络的中间变量加一句x.backward(),看会不会报错,如果不会的话,那就说明至少这之前是没毛病的。
  3. 张量第一维是batch size

    起初,我根据输入的torch.Size([64, 1, 128, 128]),使用如下函数将输出拍平成1维的:

    def num_flat_features(self, x):
    size = x.size()[0:]
    num_features = 1
    for s in size:
    num_features *= s
    return num_features

    同时,为了匹配,我将第一个全连接层的输入乘上了64。其实这个时候我已经开始怀疑这个64是哪来的了,为什么这个张量第一维尺度有64。
    直到后来平台报错,我才意识到这个表示的不是数据的维度,而是我设计的batch size。
    为此我将上面的代码调整如下:

    def num_flat_features(self, x):
    size = x.size()[1:]
    num_features = 1
    for s in size:
    num_features *= s
    return num_features

    如此,问题得到解决,最终的输出应该是batch size乘上总类别数的一个张量。


arXiv

文前提到了上arXiv下论文要科学上网的事情,后来我发现了一个中科院理论物理所的一个备选镜像,但是好像不是特别稳定,不过还是先留在这里吧,万一的话可以拿来试试。
一般一些科研工作者会在论文发布之前上传到arXiv以防止自己的idea被别人用了。估计主要是为了防止类似牛顿莱布尼兹之争这种事吧。


碰到底线咯 后面没有啦

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK