ResNet网络结构详解及代码复现
1. ResNet论文详解
1.1. Introduction
一般网络越深,特征就越丰富,模型效果也就越好。在深度重要的驱动下,出现了2个问题:
梯度消失和梯度爆炸:
- 梯度消失:误差梯度<1,当网络层数增多时,最终求的梯度会以指数形式衰减
- 梯度爆炸:误差梯度>1,当网络层数增多时,最终求的梯度会以指数形式增加
- 解决方式:
Xavier 初始化、Kaiming 初始化等
Batch Normalization
退化问题:在适当深度的模型中添加更多的层会导致更高的训练误差,如下图:
在本文中,我们通过残差结构来解决退化问题。
原本是通过堆叠非线性层来适合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. 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})+Wsx(WsW_sWs用来匹配维度)
残差函数FFF是灵活的,主要表现层数的个数和层的类别上
- 在本文的实验中涉及的FFF,它有两层或三层,也可以有更多层,但是如果只有一层,则就类似于线性层:y=W1x+xy=W_1x+xy=W1x+x,我们没有观察到任何优势。
- 尽管上面公式表示法是全连接层,但它同样适用于卷积层
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可知:
34
层的残差网络比18
层残差网络有着相当低的训练误差,并可推广到验证数据,这说明退化问题得到很好的解决,这说明了残差网络在极深网络的有效性- 通过比较
18
层网络和18
层残差网络,发现残差网络收敛的更快
1.4.2 Identity VS. Projection Shortcuts
在表3中,我们比较了3个选项(projection:在文中指利用1x1卷积来进行改变维度)
A
:用0
填充以增加维度B
:projection shortcuts
用于增加维度,其他shortcuts
为单位映射C
:所有shortcuts
均为projection
由表3可知:
B
略优于A
:A
中的0
填充的维度没有进行残差学习C
略优于B
:多个projection
引入了额外的参数A
、B
和C
的细小差异表明: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 训练结果如下
- 训练数据集与验证集大小以及训练参数
Using 3306 images for training, 364 images for validation
Using cuda GeForce RTX 2060 device for training
lr: 0.0001
batch_size: 16
- 使用自己定义的网络训练结果
[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
- 使用预训练模型参数训练结果
[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网络结构详解及代码复现相关推荐
- AlexNet网络结构详解与代码复现
参考内容来自up:3.1 AlexNet网络结构详解与花分类数据集下载_哔哩哔哩_bilibili up主的CSDN博客:太阳花的小绿豆的博客_CSDN博客-深度学习,软件安装,Tensorflow领 ...
- GoogLeNet网络结构详解及代码复现
1. GoogLeNet论文详解 Abstract: 提出了GoogLeNet网络结构--22层,此设计允许在保证计算预算不变的前提下,增加网络的深度和宽度,这个网络结构是基于Hebbian原则和多尺 ...
- pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介
前言 最近在b站发现了一个非常好的 计算机视觉 + pytorch 的教程,相见恨晚,能让初学者少走很多弯路. 因此决定按着up给的教程路线:图像分类→目标检测→-一步步学习用pytorch实现深度学 ...
- ResNet网络结构详解,网络搭建,迁移学习
前言: 参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt:https://github.com/WZMIAOMIAO/deep-le ...
- CV领域Transformer这一篇就够了(原理详解+pytorch代码复现)
文章目录 前言 一.注意力机制 1.1注意力机制通俗理解 1.2注意力机制计算公式 1.3注意力机制计算过程 1.4注意力机制代码 二.自注意力机制 2.1 注意力机制和自注意力机制的区别 2.2 编 ...
- ResNet、ResNeXt详解以及代码实现
目录 ResNet网络结构详解 resnet的创新 残差块Residul Block 整体网络结构 ResNet代码实现 ResNeXt详解 组卷积 更新了Block ResNeXt整体结构 ResN ...
- ResNet网络详解与keras实现
ResNet网络详解与keras实现 ResNet网络详解与keras实现 Resnet网络的概览 Pascal_VOC数据集 第一层目录 第二层目录 第三层目录 梯度退化 Residual Lear ...
- ResNet网络详解并使用pytorch搭建模型、并基于迁移学习训练
1.ResNet网络详解 网络中的创新点: (1)超深的网络结构(突破1000层) (2)提出residual模块 (3)使用Batch Normalization加速训练(丢弃dropout) (1 ...
- MGN网络详解以及代码分析
MGN网络详解以及代码分析 最近阅读了云从科技最新的关于REID的论文以及相关的博客和代码,算法是基于MGN,关于网络的部分,这里记录一些自己的学习笔记. 以下是我参考的博客和代码的网址 博客: ht ...
- ResNet结构详解
ResNet结构详解 ResNet的层数34,50,101到底指什么? 首先看ResNet34的对比图 然后再看这个表 ResNet 到底是个什么结构 ResNet-34 虚线结构 ResNet-50 ...
最新文章
- github下载源码也用命令进行安装包的联系
- HttpHandler与HttpModule区别
- [SQL Server]无法创建 SSIS 运行时对象,请验证 DTS.dll 是否可用及是否已注册
- tomcat与jvm的关系分析
- 【AI研究院】360无死角认识一下女神的线上美容院-“美图秀秀”
- 【Linux】一步一步学Linux——pidof命令(122)
- 使用redis实现订阅功能
- 使用Lombok简化开发及无效解决方案
- 聚宝盆,只要你上网就可以挣钱
- 华为手机应用鸿蒙os,华为手机内置应用逐渐向鸿蒙 OS 靠拢
- java dom4j 读取 xml_java使用dom4j读取xml示例
- VS2010远程调试
- 三、kylin读写分离集群部署
- Android适配阿拉伯语、波斯语语系
- CreatorPrimer | 飞机大战(一)
- 知乎里怎么看个人简介_如何做一份优秀的简历?
- 如何编辑PDF文件?简单好用的编辑方法分享
- 滑铁卢计算机专业世界最新排名,滑铁卢大学计算机专业全球排名
- 腾讯云的云直播和即时通讯IM开发(全网最详细之一)
- 用python提取不同的两列数据对比_比较两列数据fram中的值
热门文章
- GD32F103学习笔记(1)——搭建环境、编译烧写
- GD32F103单片机内部FLASH按半字读写驱动
- (全程图解)Axure RP8.0安装、破解、汉化教程
- msfconsole常用搜索命令search
- java单例模式代码
- windows照片查看器没了_装机必备|Windows 上用得最爽的18款高效软件
- java基础题100道
- 计算机测试的论文,计算机软件测试毕业论文(定稿).doc
- html酷狗客户端音乐官网代码表,仿酷狗html5手机音乐播放器主要部分代码
- TM1638数码管显示板(8数码管+16按键)单片机C语言驱动程序(显示功能)