【李沐论文精读】Resnet精读

论文地址:Deep Residual Learning for Image Recognition

参考:撑起计算机视觉半边天的ResNet【论文精读】、ResNet论文逐段精读【论文精读】、【李沐论文精读系列】

一、导论

        深度神经网络的优点:可以加很多层把网络变得特别深,然后不同程度的层会得到不同等级的feature,比如低级的视觉特征或者是高级的语义特征。但是学一个好的网络,就是简简单单的把所有网络堆在一起就行了吗?如果这样,网络做深就行了。

提出问题:随着网络越来越深,梯度就会出现爆炸或者消失

  • 解决办法就是:1、在初始化的时候要做好一点,就是权重在随机初始化的时候,权重不要特别大也不要特别小。2、在中间加入一些normalization,包括BN(batch normalization)可以使得校验每个层之间的那些输出和他的梯度的均值和方差相对来说比较深的网络是可以训练的,避免有一些层特别大,有一些层特别小。使用了这些技术之后是能够训练(能够收敛),虽然现在能够收敛了,但是当网络变深的时候,性能其实是变差的(精度会变差)
  • 文章提出出现精度变差的问题不是因为层数变多了,模型变复杂了导致的过拟合,而是因为训练误差也变高了(overfitting是说训练误差变得很低,但是测试误差变得很高),训练误差和测试误差都变高了,所以他不是overfitting。虽然网络是收敛的,但是好像没有训练出一个好的结果

深入讲述了深度增加了之后精度也会变差

  • 考虑一个比较浅一点的网络和他对应的比较深的版本(在浅的网络中再多加一些层进去),如果钱的网络效果还不错的话,神的网络是不应该变差的:深的网络新加的那些层,总是可以把这些层学习的变成一个identity mapping(输入是x,输出也是x,等价于可以把一些权重学成比如说简单的n分之一,是的输入和输出是一一对应的),但是实际情况是,虽然理论上权重是可以学习成这样,但是实际上做不到:假设让SGD去优化,深层学到一个跟那些浅层网络精度比较好的一样的结果,上面的层变成identity(相对于浅层神经网络,深层神经网络中多加的那些层全部变成identity),这样的话精度不应该会变差,应该是跟浅层神经网络是一样的,但是实际上SGD找不到这种最优解。
  • 这篇文章提出显式地构造出一个identity mapping,使得深层的神经网络不会变的比相对较浅的神经网络更差,它将其称为deep residual learning framework。
  • 要学的东西叫做H(x),假设现在已经有了一个浅的神经网络,他的输出是x,然后要在这个浅的神经网络上面再新加一些层,让它变得更深。新加的那些层不要直接去学H(x),而是应该去学H(x)-xx是原始的浅层神经网络已经学到的一些东西,新加的层不要重新去学习,而是去学习学到的东西和真实的东西之间的残差,最后整个神经网络的输出等价于浅层神经网络的输出x和新加的神经网络学习残差的输出之和,将优化目标从H(x)转变成为了H(x)-x

  • 上图中最下面的红色方框表示所要学习的H(x)
  • 蓝色方框表示原始的浅层神经网络
  • 红色阴影方框表示新加的层
  • o表示最终整个神经网络的输出
  • 这样的好处是:只是加了一个东西进来,没有任何可以学的参数,不会增加任何的模型复杂度,也不会使计算变得更加复杂,而且这个网络跟之前一样,也是可以训练的,没有任何改变。

下面这张图就对应了上一张图的简笔画。

二、related work

        残差连接如何处理输入和输出的形状是不同的情况

  • 第一个方案是在输入和输出上分别添加一些额外的0,使得这两个形状能够对应起来然后可以相加
  • 第二个方案是之前提到过的全连接怎么做投影,做到卷积上,是通过一个叫做1*1的卷积层,这个卷积层的特点是在空间维度上不做任何东西,主要是在通道维度上做改变。所以只要选取一个1*1的卷积使得输出通道是输入通道的两倍,这样就能将残差连接的输入和输出进行对比了。在ResNet中,如果把输出通道数翻了两倍,那么输入的高和宽通常都会被减半,所以在做1*1的卷积的时候,同样也会使步幅为2,这样的话使得高宽和通道上都能够匹配上。

        implementation中讲了实验的一些细节

  • 把短边随机的采样到256和480(AlexNet是直接将短边变成256,而这里是随机的)。随机放的比较大的好处是做随机切割,切割成224*224的时候,随机性会更多一点
  • 将每一个pixel的均值都减掉了
  • 使用了颜色的增强(AlexNet上用的是PCA,现在我们所使用的是比较简单的RGB上面的,调节各个地方的亮度、饱和度等)
  • 使用了BN(batch normalization)
  • 所有的权重全部是跟另外一个paper中的一样(作者自己的另外一篇文章)。注意写论文的时候,尽量能够让别人不要去查找别的文献就能够知道你所做的事情
  • 批量大小是56,学习率是0.1,然后每一次当错误率比较平的时候除以10
  • 模型训练了60*10^4个批量。建议最好不要写这种iteration,因为他跟批量大小是相关的,如果变了一个批量大小,他就会发生改变,所以现在一般会说迭代了多少遍数据,相对来说稳定一点
  • 这里没有使用dropout,因为没有全连接层,所以dropout没有太大作用
  • 在测试的时候使用了标准的10个crop testing(给定一张测试图片,会在里面随机的或者是按照一定规则的去采样10个图片出来,然后再每个子图上面做预测,最后将结果做平均)。这样的好处是因为训练的时候每次是随机把图片拿出来,测试的时候也大概进行模拟这个过程,另外做10次预测能够降低方差。
  • 采样的时候是在不同的分辨率上去做采样,这样在测试的时候做的工作量比较多,但是在实际过程中使用比较少。

三、实验

 3.1 不同配置的ResNet结构

  • 上表是整个ResNet不同架构之间的构成信息(5个版本)
  • 第一个7*7的卷积是一样的
  • 接下来的pooling层也是一样的
  • 最后的全连接层也是一样的(最后是一个全局的pooling然后再加一个1000的全连接层做输出)
  • 不同的架构之间,主要是中间部分不一样,也就是那些复制的卷积层是不同的
  • conv2.x:x表示里面有很多不同的层(块)
  • 【3*3,64】:46是通道数
  • 模型的结构为什么取成表中的结构,论文中并没有细讲,这些超参数是作者自己调出来的,实际上这些参数可以通过一些网络架构的自动选取
  • flops:整个网络要计算多少个浮点数运算。卷积层的浮点运算等价于输入的高乘以宽乘以通道数乘以输出通道数再乘以核的窗口的高和宽
3.2 残差结构效果对比

  • 上图中比较了18层和34层在有残差连接和没有残差连接的结果
  • 左图中,红色曲线表示34的验证精度(或者说是测试精度)
  • 左图中,粉色曲线表示的是34的训练精度
  • 一开始训练精度是要比测试精度高的,因为在一开始的时候使用了大量的数据增强,使得寻来你误差相对来说是比较大的,而在测试的时候没有做数据增强,噪音比较低,所以一开始的测试误差是比较低的
  • 图中曲线的数值部分是由于学习率的下降,每一次乘以0.1,对整个曲线来说下降就比较明显。为什么现在不使用乘0.1这种方法:在什么时候乘时机不好掌控,如果乘的太早,会后期收敛无力,晚一点乘的话,一开始找的方向更准一点,对后期来说是比较好的
  • 上图主要是想说明在有残差连接的时候,34比28要好;另外对于34来说,有残差连接会好很多;其次,有了残差连接以后,收敛速度会快很多,核心思想是说,在所有的超参数都一定的情况下,有残差的连接收敛会快,而且后期会好。
3.3 残差结构中,输入输出维度不一致如何处理

        A. pad补0,使维度一致;
        B. 维度不一致的时候,使其映射到统一维度,比如使用全连接或者是CNN中的1×1卷积(输出通道是输入的两倍)。
        C. 不管输入输出维度是否一致,都进行投影映射。(就算输入输出的形状是一样的,一样可以在连接的时候做个1*1的卷积,但是输入和输出通道数是一样的,做一次投影)

        从上述结果可以看到,B和C效果差不多,都比A好。但是做映射会增加很多复杂度,考虑到ResNet中大部分情况输入输出维度是一样的(也就是4个模块衔接时通道数会变),作者最后采用了方案B

3.4 深层ResNet引入瓶颈结构Bottleneck

        在ResNet-50及以上的结构中,模型更深了,可以学习更多的参数,所以通道数也要变大。比如前面模型配置表中,ResNet-50/101/152的第一个残差模块输出都是256维,增加了4倍。

        如果残差结构还是和之前一样,计算量就增加的太多了(增加16倍),划不来。所以重新设计了Bottleneck结构,将输入从256维降为64维,然后经过一个3×3卷积,再升维回256维。这样操作之后,复杂度和左侧图是差不多的。这也是为啥ResNet-50对比ResNet-34理论计算量变化不大的原因。(实际上1×1卷积计算效率不高,所以ResNet-50计算还是要贵一些)

3.5 代码实现

        resnet中残差块有两种:(use_1x1conv=True/False)

  1. 步幅为2 ,高宽减半,通道数增加。所以shortcut连接部分会加一个1×1卷积层改变通道数
  2. 步幅为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)

四、结论

        本身的论文并没有给结论。但是在这里讨论一下为什么ResNet训练起来比较快?

  • 一方面是因为梯度上保持的比较好,新加一些层的话,加的层越多,梯度的乘法就越多,因为梯度比较小,一般是在0附近的高斯分布,所以就会导致在很深的时候就会比较小(梯度消失)。虽然batch normalization或者其他东西能够对这种状况进行改善,但是实际上相对来说还是比较小,但是如果加了一个ResNet的话,它的好处就是在原有的基础上加上了浅层网络的梯度,深层的网络梯度很小没有关系,浅层网络可以进行训练,变成了加法,一个小的数加上一个大的数,相对来说梯度还是会比较大的。也就是说,不管后面新加的层数有多少,前面浅层网络的梯度始终是有用的,这就是从误差反向传播的角度来解释为什么训练的比较快。

  • 在CIFAR上面加到了1000层以上,没有做任何特别的regularization,然后效果很好,overfitting有一点点但是不大。SGD收敛是没有意义的,SGD的收敛就是训练不动了,收敛是最好收敛在比较好的地方。做深的时候,用简单的机器训练根本就跑不动,根本就不会得到比较好的结果,所以只看收敛的话意义不大,但是在加了残差连接的情况下,因为梯度比较大,所以就没那么容易收敛,所以导致一直能够往前(SGD的精髓就是能够一直能跑的动,如果哪一天跑不动了,梯度没了就完了,就会卡在一个地方出不去了,所以它的精髓就在于需要梯度够大,要一直能够跑,因为有噪音的存在,所以慢慢的他总是会收敛的,所以只要保证梯度一直够大,其实到最后的结果就会比较好)

        为什么ResNet在CIFAR-10那么小的数据集上他的过拟合不那么明显?

        虽然模型很深,参数很多,但是因为模型是这么构造的,所以使得他内在的模型复杂度其实不是很高,也就是说,很有可能加了残差链接之后,使得模型的复杂度降低了,一旦模型的复杂度降低了,其实过拟合就没那么严重了

  • 所谓的模型复杂度降低了不是说不能够表示别的东西了,而是能够找到一个不那么复杂的模型去拟合数据,就如作者所说,不加残差连接的时候,理论上也能够学出一个有一个identity的东西(不要后面的东西),但是实际上做不到,因为没有引导整个网络这么走的话,其实理论上的结果它根本过不去,所以一定是得手动的把这个结果加进去,使得它更容易训练出一个简单的模型来拟合数据的情况下,等价于把模型的复杂度降低了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/431477.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java进阶-测试方法

来学习一下软件测试相关的方法,了解一下黑盒测试和白盒测试,以及后面要用到的JUnit单元测试。JUnit单元测试也属于白盒测试,这次内容较少且相对简单。 一、软件测试方法 1、黑盒测试 不需要写代码,给输入值,看程序…

打家劫舍(java版)

📑前言 本文主要是【动态规划】——打家劫舍(java版)的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 🌄每日一…

RT-DETR改进最新LSKNet结构:顶会ICCV2023|原创改进遥感旋转目标检测SOTA!大选择性卷积核的领域首次探索

💡本篇内容:RT-DETR改进最新LSKNet结构:顶会ICCV2023|原创改进遥感旋转目标检测SOTA!大选择性卷积核的领域首次探索 💡🚀🚀🚀本博客 RT-DETR 遥感旋转目标检测SOTA&…

GIS之深度学习10:运行Faster RCNN算法

(未完成,待补充) 获取Faster RCNN源码(开源的很多) 替换自己的数据集(图片标签文件) 打开终端,进入gpupytorch环境 运行voc_annotation.py文件生成与训练文件 E:\DeepLearningMode…

万物皆可模块化分解

引言 为何要模块化,这里的主体是人,客体是事物。当事物很小时,人可以很轻松的解决;但是当事物远大于人能处理的范围时,我们就可以考虑对它进行模块化分解。模块化是一种解决复杂问题的方式,放之四海而皆可…

SPI总线知识总结

1 SPI的时钟极性CPOL和时钟相位CPHA的设置 1.1 SPI数据传输位数 SPI传输数据过程中总是先发送或接收高字节数据,每个时钟周期接收器或发送器左移一位数据。对于小于16位的数据,在发送前必须左对齐,如果接收的数据小于16位,则采用软…

C++基于多设计模式下的同步异步日志系统day7(终)

C基于多设计模式下的同步&异步日志系统day7(终) 📟作者主页:慢热的陕西人 🌴专栏链接:C基于多设计模式下的同步&异步日志系统 📣欢迎各位大佬👍点赞🔥关注&#…

CAN总线位时序的介绍

CAN控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。 显性电平对应逻辑 0,CAN_H 和 CAN_L 之差为 2.5V 左右。而隐性电平对应逻辑 1&#xff0c…

深入理解现代JavaScript:从语言特性到应用实践

💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 JavaScript作为一门动态、解释性脚本语言&…

前端面试题 ===> 【JavaScript - 高级】

公众号:需要以下pdf,关注下方 2023已经过完了,让我们来把今年的面试题统计号,来备战今年的金三银四!所以,不管你是社招还是校招,下面这份前端面试工程师高频面试题,请收好。 JavaScr…

步进电机驱动器接法

实物 参数 共阳极: 使能给高电平有效 共阴极: 使能给低电平有效 整体接线 参考内容 B站UP范辉

基于Java SSM springboot+VUE+redis实现的前后端分类版网上商城项目

基于Java SSM springbootVUEredis实现的前后端分类版网上商城项目 博主介绍:多年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐…

深入理解Lambda表达式:基础概念与实战演练【第114篇—python:Lambda表达式】

深入理解Lambda表达式:基础概念与实战演练 在现代编程语言中,Lambda表达式作为一种轻量级的匿名函数形式,越来越受到程序员的青睐。特别是在函数式编程兴起的今天,Lambda表达式在简化代码、提高可读性方面发挥着重要作用。本文将…

【Web】浅浅地聊JDBC java.sql.Driver的SPI后门

目录 SPI定义 SPI核心方法和类 最简单的SPIdemo演示 回顾JCBC基本流程 为什么JDBC要有SPI JDBC java.sql.Driver后门利用与验证 SPI定义 SPI: Service Provider Interface 官方定义: 直译过来是服务提供者接口,学名为服务发现机制 它通…

加油站“变身”快充站,探讨充电新模式

摘要:新能源汽车规模化发展的同时,充电不便利的痛点愈发明显。在未来的新能源汽车行业发展当中,充电的矛盾要远远大于造车的矛盾,解决好充电的问题成为电动汽车行业发展的一个突出问题。解决充电补能问题,重要的方式之…

【牛客】VL60 使用握手信号实现跨时钟域数据传输

题目描述 分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加…

(vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)

(vue)el-checkbox 实现展示区分 label 和 value&#xff08;展示值与选中获取值需不同&#xff09; 后端数据 解决方法 在 el-checkbox 标签中间传入要展示的文本即可&#xff0c;代码如下&#xff1a; <el-checkbox-groupv-model"formInline.processFieldList"…

【C语言】指针超级无敌金刚霹雳进阶(但不难,还是基础)

点击这里访问我的博客主页~~ 对指针概念还不太清楚的点击这里访问上一篇指针初阶2.0 上上篇指针初阶1.0 谢谢各位大佬的支持咯 今天我们一起来学习指针进阶内容 指针进阶 一、指针变量1、字符指针变量2、数组指针变量①数组指针变量的定义②数组指针变量的初始化 3、函数指…

代码随想录第50天|● 123.买卖股票的最佳时机III ● 188.买卖股票的最佳时机IV

文章目录 ● 123.买卖股票的最佳时机III思路代码一&#xff1a;dp二维数组代码二&#xff1a;四个数存储 ● 188.买卖股票的最佳时机IV思路&#xff1a;代码&#xff1a; ● 123.买卖股票的最佳时机III 思路 dp[i][j]中 i表示第i天&#xff0c;j为 [0 - 4] 五个状态&#xff0…

C++ string类详解及模拟实现

目录 【本节目标】 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 1.2 面试题(暂不做讲解) 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明&#xff08;注意下面我只讲解最常用的接口&#xff09; 3. string类的模拟实现 3.1string类常用…