ResNet结构的pytorch实现

article/2025/8/22 21:22:21

文章目录

      • 1.ResNet的创新
          • 1)亮点
          • 2)原因
      • 2.ResNet的结构
          • 1)浅层的残差结构
          • 2)深层的残差结构
          • 3)总结
      • 3.Batch Normalization
      • 4.参考代码

1.ResNet的创新

现在重新稍微系统的介绍一下ResNet网络结构。 ResNet结构首先通过一个卷积层然后有一个池化层,然后通过一系列的残差结构,最后再通过一个平均池化下采样操作,以及一个全连接层的得到了一个输出。ResNet网络可以达到很深的层数的原因就是不断的堆叠残差结构而来的。

1)亮点

网络中的亮点 :

  • 超深的网络结构( 突破1000 层)
  • 提出residual 模块
  • 使用Batch Normalization 加速训练( 丢弃dropout)

但是,一般来说,并不是一直的加深神经网络的结构就会得到一个更好的结果,一般太深的网络会出现过拟合的现象严重,可能还没有一些浅层网络要好。
在这里插入图片描述

2)原因

其中有两个原因:

  • 梯度消失或梯度爆炸

当层数过多的时候,假设每一层的误差梯度都是一个小于1的数值,当进行方向传播的过程中,每向前传播一层,都要乘以一个小于1的误差梯度,当网络越来越深时,所成的小于1的系数也就越来越多,此时梯度便越趋近于0,这样梯度便会越来越小。这便会造成梯度消失的现象。

而当所成的误差梯度是一个大于1的系数,而随着网络层数的加深,梯度便会越来越大,这便会造成梯度爆炸的现象。

  • 退化问题(degradation problem)

当解决了梯度消失或者梯度爆炸的问题之后,其实网络的效果可能还是不尽如意,还可能有退化问题。为此,ResNet提出了残差结构来解决这个退化问题。 也正是因为有这个残差的结构,所以才可以搭建这么深的网络。
在这里插入图片描述

2.ResNet的结构

残差结构如图所示
在这里插入图片描述
作图是针对ResNet-18/34层浅层网络的结构,右图是ResNet-50/101/152层深层网络的结构,其中注意:主分支与shortcut 的输出特征矩阵shape。

一下表格为网络的一些主要参数
在这里插入图片描述
可以看见,不同层数的网络结构其实框架是类似的,不同的至少堆叠的残差结构的数量。

1)浅层的残差结构

在这里插入图片描述
需要注意,有些残差结构的ShortCut是实线,而有的是虚线,这两者是不同的。对于左图来说,ShortCut是实线,这表明输入与输出的shape是一样的,所以可以直接的进行相加。而对于右图来说,其输入的shape与输出的shape是不一样的,这时候需要调整步长stribe与kernel size来使得两条路(主分支与捷径分支)所处理好的shape是一模一样的。

2)深层的残差结构

在这里插入图片描述
同样的,需要注意,主分支与shortcut 的输出特征矩阵shape必须相同,同样的通过步长来调整。

但是注意原论文中:

右侧虚线残差结构的主分支上、第一个1x1卷积层的步距是2,第二个3x3卷积层的步距是1.

而在pytorch官方实现的过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,这样能够在ImageNet的top1上提升大概0.5%的准确率。

所以在conv3_x,conv4_x,conv5_x中所对应的残差结构的第一层,都是指虚线的残差结构,其他的残差结构是实线的残差结构。

3)总结

对于每个大模块中的第一个残差结构,需要通过虚线分支来调整残差结构的输入与输出是同一个shape。此时使用了下采样的操作函数。
对于每个大模块中的其他剩余的残差结构,只需要通过实线分支来调整残差网络结构,因为其输出和输入本身就是同一个shape的。

对于第一个大模块的第一个残差结构,其第二个3x3的卷积中,步长是1的,而其他的三个大模块的步长均为2.
在每一个大模块的维度变换中,主要是第一个残差结构使得shape减半,而模块中其他的残差结构都是没有改变shape的。也真因为没有改变shape,所以这些残差结构才可以直接的通过实线进行相加。

3.Batch Normalization

Batch Normalization的目的是使我们的一批(Batch)特征矩阵feature map满足均值为0,方差为1的分布规律。
在这里插入图片描述
其中:
μ,σ_2在正向传播过程中统计得到
γ,β在反向传播过程中训练得到

Batch Normalization是google团队在2015年论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》提出的。通过该方法能够加速网络的收敛并提升准确率。

具体的相关原理见:Batch Normalization详解以及pytorch实验

4.参考代码

import torch
import torch.nn as nn# 分类数目
num_class = 5
# 各层数目
resnet18_params = [2, 2, 2, 2]
resnet34_params = [3, 4, 6, 3]
resnet50_params = [3, 4, 6, 3]
resnet101_params = [3, 4, 23, 3]
resnet152_params = [3, 8, 36, 3]# 定义Conv1层
def Conv1(in_planes, places, stride=2):return nn.Sequential(nn.Conv2d(in_channels=in_planes,out_channels=places,kernel_size=7,stride=stride,padding=3, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))# 浅层的残差结构
class BasicBlock(nn.Module):def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 1):super(BasicBlock,self).__init__()self.expansion = expansionself.downsampling = downsampling# torch.Size([1, 64, 56, 56]), stride = 1# torch.Size([1, 128, 28, 28]), stride = 2# torch.Size([1, 256, 14, 14]), stride = 2# torch.Size([1, 512, 7, 7]), stride = 2self.basicblock = nn.Sequential(nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(places * self.expansion),)# torch.Size([1, 64, 56, 56])# torch.Size([1, 128, 28, 28])# torch.Size([1, 256, 14, 14])# torch.Size([1, 512, 7, 7])# 每个大模块的第一个残差结构需要改变步长if self.downsampling:self.downsample = nn.Sequential(nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(places*self.expansion))self.relu = nn.ReLU(inplace=True)def forward(self, x):# 实线分支residual = xout = self.basicblock(x)# 虚线分支if self.downsampling:residual = self.downsample(x)out += residualout = self.relu(out)return out# 深层的残差结构
class Bottleneck(nn.Module):# 注意:默认 downsampling=Falsedef __init__(self,in_places,places, stride=1,downsampling=False, expansion = 4):super(Bottleneck,self).__init__()self.expansion = expansionself.downsampling = downsamplingself.bottleneck = nn.Sequential(# torch.Size([1, 64, 56, 56]),stride=1# torch.Size([1, 128, 56, 56]),stride=1# torch.Size([1, 256, 28, 28]), stride=1# torch.Size([1, 512, 14, 14]), stride=1nn.Conv2d(in_channels=in_places,out_channels=places,kernel_size=1,stride=1, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),# torch.Size([1, 64, 56, 56]),stride=1# torch.Size([1, 128, 28, 28]), stride=2# torch.Size([1, 256, 14, 14]), stride=2# torch.Size([1, 512, 7, 7]), stride=2nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),# torch.Size([1, 256, 56, 56]),stride=1# torch.Size([1, 512, 28, 28]), stride=1# torch.Size([1, 1024, 14, 14]), stride=1# torch.Size([1, 2048, 7, 7]), stride=1nn.Conv2d(in_channels=places, out_channels=places * self.expansion, kernel_size=1, stride=1, bias=False),nn.BatchNorm2d(places * self.expansion),)# torch.Size([1, 256, 56, 56])# torch.Size([1, 512, 28, 28])# torch.Size([1, 1024, 14, 14])# torch.Size([1, 2048, 7, 7])if self.downsampling:self.downsample = nn.Sequential(nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(places*self.expansion))self.relu = nn.ReLU(inplace=True)def forward(self, x):# 实线分支residual = xout = self.bottleneck(x)# 虚线分支if self.downsampling:residual = self.downsample(x)out += residualout = self.relu(out)return outclass ResNet(nn.Module):def __init__(self,blocks, blockkinds, num_classes=num_class):super(ResNet,self).__init__()self.blockkinds = blockkindsself.conv1 = Conv1(in_planes = 3, places= 64)# 对应浅层网络结构if self.blockkinds == BasicBlock:self.expansion = 1# 64 -> 64self.layer1 = self.make_layer(in_places=64, places=64, block=blocks[0], stride=1)# 64 -> 128self.layer2 = self.make_layer(in_places=64, places=128, block=blocks[1], stride=2)# 128 -> 256self.layer3 = self.make_layer(in_places=128, places=256, block=blocks[2], stride=2)# 256 -> 512self.layer4 = self.make_layer(in_places=256, places=512, block=blocks[3], stride=2)self.fc = nn.Linear(512, num_classes)# 对应深层网络结构if self.blockkinds == Bottleneck:self.expansion = 4# 64 -> 64self.layer1 = self.make_layer(in_places = 64, places= 64, block=blocks[0], stride=1)# 256 -> 128self.layer2 = self.make_layer(in_places = 256,places=128, block=blocks[1], stride=2)# 512 -> 256self.layer3 = self.make_layer(in_places=512,places=256, block=blocks[2], stride=2)# 1024 -> 512self.layer4 = self.make_layer(in_places=1024,places=512, block=blocks[3], stride=2)self.fc = nn.Linear(2048, num_classes)self.avgpool = nn.AvgPool2d(7, stride=1)# 初始化网络结构for m in self.modules():if isinstance(m, nn.Conv2d):# 采用了何凯明的初始化方法nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')elif isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)def make_layer(self, in_places, places, block, stride):layers = []# torch.Size([1, 64, 56, 56])  -> torch.Size([1, 256, 56, 56]), stride=1 故w,h不变# torch.Size([1, 256, 56, 56]) -> torch.Size([1, 512, 28, 28]), stride=2 故w,h变# torch.Size([1, 512, 28, 28]) -> torch.Size([1, 1024, 14, 14]),stride=2 故w,h变# torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 2048, 7, 7]), stride=2 故w,h变# 此步需要通过虚线分支,downsampling=Truelayers.append(self.blockkinds(in_places, places, stride, downsampling =True))# torch.Size([1, 256, 56, 56]) -> torch.Size([1, 256, 56, 56])# torch.Size([1, 512, 28, 28]) -> torch.Size([1, 512, 28, 28])# torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 1024, 14, 14])# torch.Size([1, 2048, 7, 7]) -> torch.Size([1, 2048, 7, 7])# print("places*self.expansion:", places*self.expansion)# print("block:", block)# 此步需要通过实线分支,downsampling=False, 每个大模块的第一个残差结构需要改变步长for i in range(1, block):layers.append(self.blockkinds(places*self.expansion, places))return nn.Sequential(*layers)def forward(self, x):# conv1层x = self.conv1(x)   # torch.Size([1, 64, 56, 56])# conv2_x层x = self.layer1(x)  # torch.Size([1, 256, 56, 56])# conv3_x层x = self.layer2(x)  # torch.Size([1, 512, 28, 28])# conv4_x层x = self.layer3(x)  # torch.Size([1, 1024, 14, 14])# conv5_x层x = self.layer4(x)  # torch.Size([1, 2048, 7, 7])x = self.avgpool(x) # torch.Size([1, 2048, 1, 1]) / torch.Size([1, 512])x = x.view(x.size(0), -1)   # torch.Size([1, 2048]) / torch.Size([1, 512])x = self.fc(x)      # torch.Size([1, 5])return xdef ResNet18():return ResNet(resnet18_params, BasicBlock)def ResNet34():return ResNet(resnet34_params, BasicBlock)def ResNet50():return ResNet(resnet50_params, Bottleneck)def ResNet101():return ResNet(resnet101_params, Bottleneck)def ResNet152():return ResNet(resnet152_params, Bottleneck)if __name__=='__main__':# model = torchvision.models.resnet50()# 模型测试# model = ResNet18()# model = ResNet34()# model = ResNet50()# model = ResNet101()model = ResNet152()# print(model)input = torch.randn(1, 3, 224, 224)out = model(input)print(out.shape)

参考:
https://www.bilibili.com/video/BV1T7411T7wa
推荐一个b站的up主视频:霹雳吧啦Wz,理论讲解得很好


http://chatgpt.dhexx.cn/article/qwZprjP5.shtml

相关文章

Resnet论文解读

摘要: 提出了一种残差结构,这些残差网络的集合在ImageNet测试集上实现了3.57%的误差。该结果在ILSVRC 2015分类任务中获得第一名。 介绍: 主要讲述的就是针对 深度越深,精确度达到了一种饱和的状态,然后就会产生退化…

了解ResNet

产生背景 网络的层数越多,就能够提取更多的特征,但是如果只是简单的增加深度,就会导致梯度爆炸。也有文章提出,解决该问题的方法是正则化初始化和中间的正则化层,但是这样会导致另一个问题,退化,网络的层数…

ResNet网络

1.1.ResNet的提出 残差网络(ResNet) 是由来自Microsoft Research的4位学者提出的卷积神经网络,在2015年的ImageNet大规模视觉识别竞赛(ImageNet Large Scale Visual Recognition Challenge, ILSVRC)中获得了图像分类和物体识别的优胜。 网络…

ResNet详解——通俗易懂版

ResNet学习 什么是ResNet为什么要引入ResNet?ResNet详细解说 本篇博客主要是自己对论文的一些解读以及参考一些博客后的理解,如若有不对之处,请各位道友指出。多谢! 2015年刚提出ResNet的Paper 2016对ResNet进行改进之后的Paper …

CNN经典网络模型(五):ResNet简介及代码实现(PyTorch超详细注释版)

目录 一、开发背景 二、网络结构 三、模型特点 四、代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五、参考内容 一、开发背景 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的, 斩获2015年ImageNet竞赛…

ResNet详解+PyTorch实现

1.Resnet简介 深度残差网络(Deep residual network, ResNet)的提出是CNN图像史上的一件里程碑事件,由于其在公开数据上展现的优势,作者何凯明也因此摘得CVPR2016最佳论文奖。 Resnet是残差网络(Residual Network)的缩写&#xff…

点估计

1.说明: 设总体 X 的分布函数形式已知, 但它的一个或多个参数为未知, 借助于总体 X 的一个样本来估计总体未知参数的值的问题称为点估计问题. 在统计问题中往往先使用最大似然估计法, 在最大似然估计法使用不方便时, 再用矩估计法. 2.常用构造估计量的方法 1&#…

【应用统计学】参数统计-点估计与估计量的评价标准

一、点估计 参数的点估计就是根据样本构造一个统计量,作为总体未知参数的估计。这个统计量称为未知参数的估计量。 在统计中,经常使用的点估计量有: 二、估计量的评价标准 1、无偏性 无偏性即指估计量抽样分布的数学期望等于总体参数的真值。 2、有效…

功能点估算方法,如何让估算偏差更小?

1、何为软件功能点 ​ ​软件功能点是站在业务角度对软件规模的一种度量,功能点的多少代表软件规模的大小,这里说的功能点是标准的功能点,按照标准的估算方法,每个人对特定需求估算出的功能点数是一致的。 功能点估算方法&…

三点估算法

施工时间划分为乐观时间、最可能时间、悲观时间 乐观时间:也就是工作顺利情况下的时间为a 最可能时间:最可能时间,就是完成某道工序的最可能完成时间m 悲观时间:最悲观的时间就是工作进行不利所用时间b。 活动历时均值(或估计值)(乐观估计4最可能估计悲观估计)/6 …

点估计、区间估计(利用回归方程进行预测)

回归模型经过各种检验并标明符合预定的要求后,可利用它来预测因变量。预测(predict)是指通过自变量x的取值来预测因变量y的取值。 1、点估计 利用估计的方程,对于x的一个特定值 ,求出y的一个估计值就是点估计。点估计分为两种&…

数理统计中的点估计

• 统计推断的基本问题有二:估计问题,和假设检验问题. • 本章讨论总体参数的点估计和区间估计.理解这两种估计的思想,掌握求参数估计量的方法和评判估计量好坏的标准. 点估计 问题的提出 设灯泡寿命 T~N(μ,σ2) ,但参数 μ 和 σ2 未知. 现在要求通过对总体抽样得到的…

统计学-点估计和区间估计

点估计和区间估计 点估计 矩估计法 正态分布是一种统计量,目的是描述总体的某一性质。而矩则是描述这些样本值的分布情况,无论几阶矩,无外乎是描述整体的疏密情况。K阶矩分为原点矩和中心矩: 前者是绝对的:1阶就是平均…

点估计和区间估计——统计学概念

概念简介: 点估计和区间估计是通过样本统计量估计总体参数的两种方法。点估计是在抽样推断中不考虑抽样误差,直接以抽样指标代替全体指标的一种推断方法。因为个别样本的抽样指标不等于全体指标,所以,用抽样指标直接代替全体指标&…

【定量分析、量化金融与统计学】统计推断基础(3)---点估计、区间估计

一、前言 我发现很多人学了很久的统计学,仍然搞不清楚什么是点估计、区间估计,总是概念混淆,那今天我们来盘一盘统计推断基础的点估计、区间估计。这个系列统计推断基础5部分分别是: 总体、样本、标准差、标准误【定量分析、量化…

【数据统计】— 峰度、偏度、点估计、区间估计、矩估计、最小二乘估计

【数据统计】— 峰度、偏度、点估计、区间估计、矩估计、最小二乘估计 四分位差异众比率变异系数利用数据指标指导建模思路 形状变化数据分布形态峰度: 度量数据在中心聚集程度偏度 利用数据指标指导建模思路 参数估计点估计区间估计矩估计举例:黑白球(矩…

7.1 参数的点估计

小结: 点估计是一种统计推断方法,它用于通过样本数据估计总体参数的值。在统计学中,总体是指一个包含所有个体的集合,而样本是从总体中选出的一部分个体。总体参数是总体的某种特征,如平均值、标准差、比例等。 点估…

【数理统计】参数估计及相关(点估计、矩估计法、最大似然估计、原点矩中心距)

1 基础知识 1.1 常见分布的期望和方差 1.2 对数运算法则 log ⁡ a ( M N ) log ⁡ a M log ⁡ a N log ⁡ a ( M / N ) log ⁡ a M − log ⁡ a N log ⁡ a ( 1 / N ) − log ⁡ a N log ⁡ a M n n log ⁡ a M \log _{a}(M N)\log _{a} M\log _{a} N \\ \log _{a}(M / N…

二、机器学习基础11(点估计)

点估计:用实际样本的一个指标来估计总体的一个指标的一种估计方法。点估计举例:比如说,我们想要了解中国人的平均身高,那么在大街上随便找了一个人,通过测量这个人的身高来估计中国人的平均身高水平;或者在…

统计学之参数估计(点估计和参数估计)含例题和解答

统计学之参数估计 参数点估计矩估计法极大似然估计法点估计的评价准则(无偏性一致性有效性) 区间估计主要公式置信区间区间估计的内容总体均值的区间估计(大样本)总体均值的区间估计(小样本)单一总体均值的区间估计总结两个总体均值之差的区间估计(大样本…