1. ResNet论文详解

1.1. Introduction

一般网络越深,特征就越丰富,模型效果也就越好。在深度重要的驱动下,出现了2个问题:

  1. 梯度消失和梯度爆炸:

    • 梯度消失:误差梯度<1,当网络层数增多时,最终求的梯度会以指数形式衰减
    • 梯度爆炸:误差梯度>1,当网络层数增多时,最终求的梯度会以指数形式增加
    • 解决方式:
      1. Xavier 初始化、Kaiming 初始化等

      2. Batch Normalization

  2. 退化问题:在适当深度的模型中添加更多的层会导致更高的训练误差,如下图:

在本文中,我们通过残差结构来解决退化问题。

原本是通过堆叠非线性层来适合H(x)H(x)H(x),现在是让这些非线性层来适合F(x)F(x)F(x),原始映射被表示为:H(x):=F(x)+xH(x):=F(x)+xH(x):=F(x)+x

  • H(x):H(x):H(x): 原本需要学习的映射
  • F(x):F(x):F(x): 现在需要学习的映射
  • x:x:x: 单位映射,可以跳过一层或多层的连接(shortcut connection)

实验表示:

  1. 极深残差网络很容易优化
  2. 很容易获得网络深度带来的准确性的提高

1.2. Deep Residual Learning

1.2.1 Residual Learning

退化问题表明:很难通过多个非线性层来逼近单位映射,因为如果可以的话,那么更深的模型的训练误差应该不大于更浅层的对应模型。而对应残差网络,如果单位映射为最优的话,求解器可以简单地将多个非线性层地权值置为0从而逼近单位映射。

1.2.2 Identity Mapping by Shortcuts

在本文中,残差块定义如下:

  • xxx和FFF的维度一致:y=F(x,{Wi})+xy=F(x,\{W_i\})+xy=F(x,{Wi​})+x
  • xxx和FFF的维度不一致:y=F(x,{Wi})+Wsxy=F(x,\{W_i\})+W_sxy=F(x,{Wi​})+Ws​x(WsW_sWs​用来匹配维度)

残差函数FFF是灵活的,主要表现层数的个数和层的类别上

  1. 在本文的实验中涉及的FFF,它有两层或三层,也可以有更多层,但是如果只有一层,则就类似于线性层:y=W1x+xy=W_1x+xy=W1​x+x,我们没有观察到任何优势。
  2. 尽管上面公式表示法是全连接层,但它同样适用于卷积层

1.3. Network Architectures

在一般网络结构的基础上,插入shortcut connection,将网络变为对应的残差网络。

当输入和输出的维数相同时:对应于上图的实线shortcut connection,处理方式:

  • 直接使用单位映射

当输入和输出的维数不相同时:对应于上图的虚线shortcut connection,处理方式:

  • shortcut connection仍然使用单位映射,增加维数用0填充,此方法不引入额外的参数
  • 使用1x1卷积来匹配维度,文中称为projection shortcut

当跨越两种尺寸的特征图时,执行步幅为2的。有上表黄色部分可知。

1.4. Experiments

1.4.1 Residual Networks VS. Plain Networks

Plain Networks

上图表示:34层网络比18层网络具有更高的验证误差,尽管18层网络的解空间是34层网络的子空间,但在整个训练过程中,34层网络的训练误差都比较大,这说明了退化问题

我们认为这种优化困难不可能是梯度消失引起的,因为这些网络都使用了BN进行训练,保证前向传播的信号具有非零方差。我们还验证了反向传播的梯度在BN上表现出健康的范数。所以向前和向后的信号都不会消失。

Residual Networks

从图4和表2可知:

  1. 34层的残差网络比18层残差网络有着相当低的训练误差,并可推广到验证数据,这说明退化问题得到很好的解决,这说明了残差网络在极深网络的有效性
  2. 通过比较18层网络和18层残差网络,发现残差网络收敛的更快

1.4.2 Identity VS. Projection Shortcuts

在表3中,我们比较了3个选项(projection:在文中指利用1x1卷积来进行改变维度)

  1. A:用0填充以增加维度
  2. Bprojection shortcuts用于增加维度,其他shortcuts为单位映射
  3. C:所有shortcuts均为projection

由表3可知:

  • B略优于AA中的0填充的维度没有进行残差学习
  • C略优于B:多个projection引入了额外的参数
  • ABC的细小差异表明:projection对于解决退化问题不是必须的,因此在本文的其余部分,我们不使用C以减少内存/时间复杂度和模型的规模,使用B

1.4.3 Deeper Bottleneck Architectures

1x1卷积用于升维或降维,可以减少网络的参数

  • 上图左:256x3x3x256+256x3x3x256=1179648
  • 上图右:256x1x1x64+64x3x3x64+64x1x1x256=69632

故在更深的残差网络中,使用上图右的结构,可以大大减少网络的参数,减小模型的规模

2. 基于Pytorch代码复现

2.1 模型搭建

import torch.nn as nn
import torch
from torchsummary import summary
import torchvision.models as modelsclass BasicBlock(nn.Module):expansion = 1def __init__(self, in_channel, out_channel, stride=1, downsample=None):super(BasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.relu = nn.ReLU(inplace=True)self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, kernel_size=3, stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)self.downsample = downsampledef forward(self, x):identity = xif self.downsample is not None:identity = self.downsample(x)out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out += identityout = self.relu(out)return outclass Bottleneck(nn.Module):expansion = 4def __init__(self, in_channel, out_channel, stride=1, downsample=None):super(Bottleneck, self).__init__()self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1, stride=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn2 = nn.Conv2d(out_channel)self.conv3 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel*self.expansion, kernel_size=1, stride=1, bias=False)self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)self.relu = nn.ReLU(inplace=True)self.downsample = downsampledef forward(self, x):identity = xif self.downsample is not None:identity = self.downsample(x)out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out = self.relu(out)out = self.conv3(out)out = self.bn3(out)out += identityout = self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, block_num, num_classes=1000, include_top=True):super(ResNet, self).__init__()self.include_top = include_topself.in_channel = 64self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2, padding=3, bias=False)self.bn1 = nn.BatchNorm2d(self.in_channel)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, block_num[0])self.layer2 = self._make_layer(block, 128, block_num[1], stride=2)self.layer3 = self._make_layer(block, 256, block_num[2], stride=2)self.layer4 = self._make_layer(block, 512, block_num[3], stride=2)if self.include_top:self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')def _make_layer(self, block, channel, block_num, stride=1):downsample = Noneif stride != 1 or self.in_channel != channel * block.expansion:downsample = nn.Sequential(nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(channel*block.expansion))layers = []layers.append(block(self.in_channel, channel, downsample=downsample, stride=stride))self.in_channel = channel * block.expansionfor _ in range(1, block_num):layers.append(block(self.in_channel, channel))return nn.Sequential(*layers)def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.maxpool(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)if self.include_top:x = self.avgpool(x)x = torch.flatten(x, 1)x = self.fc(x)return xdef resnet34(num_classes=1000, include_top=True):return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)def resnet101(num_classes=1000, include_top=True):return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top)def read_resnet34():device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model = models.resnet34(pretrained=True)model.to(device)print(model)summary(model, input_size=(3, 224, 224))def get_resnet34(flag, num_classes):if flag:net = models.resnet34(pretrained=True)num_input = net.fc.in_featuresnet.fc = nn.Linear(num_input, num_classes)else:net = resnet34(num_classes)return net

2.2 训练结果如下

  1. 训练数据集与验证集大小以及训练参数
Using 3306 images for training, 364 images for validation
Using cuda GeForce RTX 2060 device for training
lr: 0.0001
batch_size: 16
  1. 使用自己定义的网络训练结果

[epoch 1/10] train_loss: 1.309 val_acc: 0.555
[epoch 2/10] train_loss: 1.146 val_acc: 0.604
[epoch 3/10] train_loss: 1.029 val_acc: 0.643
[epoch 4/10] train_loss: 0.935 val_acc: 0.695
[epoch 5/10] train_loss: 0.919 val_acc: 0.615
[epoch 6/10] train_loss: 0.860 val_acc: 0.723
[epoch 7/10] train_loss: 0.841 val_acc: 0.690
[epoch 8/10] train_loss: 0.819 val_acc: 0.725
[epoch 9/10] train_loss: 0.800 val_acc: 0.745
[epoch 10/10] train_loss: 0.783 val_acc: 0.725
Best acc: 0.745
Finished Training
Train 耗时为:281.2s
  1. 使用预训练模型参数训练结果
[epoch 1/10] train_loss: 0.492 val_acc: 0.896
[epoch 2/10] train_loss: 0.327 val_acc: 0.896
[epoch 3/10] train_loss: 0.285 val_acc: 0.909
[epoch 4/10] train_loss: 0.273 val_acc: 0.904
[epoch 5/10] train_loss: 0.205 val_acc: 0.901
[epoch 6/10] train_loss: 0.245 val_acc: 0.898
[epoch 7/10] train_loss: 0.200 val_acc: 0.923
[epoch 8/10] train_loss: 0.196 val_acc: 0.923
[epoch 9/10] train_loss: 0.179 val_acc: 0.929
[epoch 10/10] train_loss: 0.173 val_acc: 0.926
Best acc: 0.929
Finished Training
Train 耗时为:281.3s

上一篇:GoogLeNet
下一篇:DenseNet
完整代码

ResNet网络结构详解及代码复现相关推荐

  1. AlexNet网络结构详解与代码复现

    参考内容来自up:3.1 AlexNet网络结构详解与花分类数据集下载_哔哩哔哩_bilibili up主的CSDN博客:太阳花的小绿豆的博客_CSDN博客-深度学习,软件安装,Tensorflow领 ...

  2. GoogLeNet网络结构详解及代码复现

    1. GoogLeNet论文详解 Abstract: 提出了GoogLeNet网络结构--22层,此设计允许在保证计算预算不变的前提下,增加网络的深度和宽度,这个网络结构是基于Hebbian原则和多尺 ...

  3. pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介

    前言 最近在b站发现了一个非常好的 计算机视觉 + pytorch 的教程,相见恨晚,能让初学者少走很多弯路. 因此决定按着up给的教程路线:图像分类→目标检测→-一步步学习用pytorch实现深度学 ...

  4. ResNet网络结构详解,网络搭建,迁移学习

    前言: 参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt:https://github.com/WZMIAOMIAO/deep-le ...

  5. CV领域Transformer这一篇就够了(原理详解+pytorch代码复现)

    文章目录 前言 一.注意力机制 1.1注意力机制通俗理解 1.2注意力机制计算公式 1.3注意力机制计算过程 1.4注意力机制代码 二.自注意力机制 2.1 注意力机制和自注意力机制的区别 2.2 编 ...

  6. ResNet、ResNeXt详解以及代码实现

    目录 ResNet网络结构详解 resnet的创新 残差块Residul Block 整体网络结构 ResNet代码实现 ResNeXt详解 组卷积 更新了Block ResNeXt整体结构 ResN ...

  7. ResNet网络详解与keras实现

    ResNet网络详解与keras实现 ResNet网络详解与keras实现 Resnet网络的概览 Pascal_VOC数据集 第一层目录 第二层目录 第三层目录 梯度退化 Residual Lear ...

  8. ResNet网络详解并使用pytorch搭建模型、并基于迁移学习训练

    1.ResNet网络详解 网络中的创新点: (1)超深的网络结构(突破1000层) (2)提出residual模块 (3)使用Batch Normalization加速训练(丢弃dropout) (1 ...

  9. MGN网络详解以及代码分析

    MGN网络详解以及代码分析 最近阅读了云从科技最新的关于REID的论文以及相关的博客和代码,算法是基于MGN,关于网络的部分,这里记录一些自己的学习笔记. 以下是我参考的博客和代码的网址 博客: ht ...

  10. ResNet结构详解

    ResNet结构详解 ResNet的层数34,50,101到底指什么? 首先看ResNet34的对比图 然后再看这个表 ResNet 到底是个什么结构 ResNet-34 虚线结构 ResNet-50 ...

最新文章

  1. github下载源码也用命令进行安装包的联系
  2. HttpHandler与HttpModule区别
  3. [SQL Server]无法创建 SSIS 运行时对象,请验证 DTS.dll 是否可用及是否已注册
  4. tomcat与jvm的关系分析
  5. 【AI研究院】360无死角认识一下女神的线上美容院-“美图秀秀”
  6. 【Linux】一步一步学Linux——pidof命令(122)
  7. 使用redis实现订阅功能
  8. 使用Lombok简化开发及无效解决方案
  9. 聚宝盆,只要你上网就可以挣钱
  10. 华为手机应用鸿蒙os,华为手机内置应用逐渐向鸿蒙 OS 靠拢
  11. java dom4j 读取 xml_java使用dom4j读取xml示例
  12. VS2010远程调试
  13. 三、kylin读写分离集群部署
  14. Android适配阿拉伯语、波斯语语系
  15. CreatorPrimer | 飞机大战(一)
  16. 知乎里怎么看个人简介_如何做一份优秀的简历?
  17. 如何编辑PDF文件?简单好用的编辑方法分享
  18. 滑铁卢计算机专业世界最新排名,滑铁卢大学计算机专业全球排名
  19. 腾讯云的云直播和即时通讯IM开发(全网最详细之一)
  20. 用python提取不同的两列数据对比_比较两列数据fram中的值

热门文章

  1. GD32F103学习笔记(1)——搭建环境、编译烧写
  2. GD32F103单片机内部FLASH按半字读写驱动
  3. (全程图解)Axure RP8.0安装、破解、汉化教程
  4. msfconsole常用搜索命令search
  5. java单例模式代码
  6. windows照片查看器没了_装机必备|Windows 上用得最爽的18款高效软件
  7. java基础题100道
  8. 计算机测试的论文,计算机软件测试毕业论文(定稿).doc
  9. html酷狗客户端音乐官网代码表,仿酷狗html5手机音乐播放器主要部分代码
  10. TM1638数码管显示板(8数码管+16按键)单片机C语言驱动程序(显示功能)