# ResNet代码实现及原理——学习记录

Pytorch版源代码下载地址：
https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py

ResNet

ResNet原理及具体细节不过多介绍，网上很多大佬总结的很好，我主要就是记录自己学习ResNet的过程，感觉是重点和难点的部分，话不多说进入正题。

1、深度残差网络（Deep Residual Network）

2、残差结构
ResNet中使用了一种连接方式叫做” shortcut connection“,顾名思义，就是抄近道的意思。

y= F(x) + x

y=F(x)+Wx

out += identity


if self.downsample is not None:
identity = self.downsample(x)
out += identity


3、两种残差块设计：

basic block中包含两个卷积层，卷积核数量相同，卷积核均为3×3，输入输出均为64通道，可直接相加。该block主要使用在相对浅层网络，比如ResNet-34。
bottle neck的结构是前两组滤波核数量相同，第三层滤波核数量是前两组的4倍，第二层尺寸3×3，其余两层尺寸是1×1，主要目的就是为了降低参数的数目，第一个64通道数的1×1卷积把256维通道数（channel)降到64维通道，然后在最后通过一个256通道的1×1卷积恢复。


class BasicBlock(nn.Module):
expansion = 1

def __init__(
self,
inplanes,
planes,
stride=1,
downsample=None,
groups=1,
base_width=64,
dilation=1,
norm_layer=None):
super(BasicBlock, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
if groups != 1 or base_width != 64:
raise ValueError('BasicBlock only supports groups=1 and base_width=64')
if dilation > 1:
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")

self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = norm_layer(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = norm_layer(planes)
self.downsample = downsample
self.stride = stride

def forward(self, x):
identity = x

out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)

out = self.conv2(out)
out = self.bn2(out)

if self.downsample is not None:
identity = self.downsample(x)

out += identity
out = self.relu(out)

return out


class Bottleneck(nn.Module):
expansion = 4

def __init__(
self,
inplanes,
planes,
stride=1,
downsample=None,
groups=1,
base_width=64,
dilation=1,
norm_layer=None):
super(Bottleneck, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
width = int(planes * (base_width / 64.)) * groups

self.conv1 = conv1x1(inplanes, width)
self.bn1 = norm_layer(width)
self.conv2 = conv3x3(width, width, stride, groups, dilation)
self.bn2 = norm_layer(width)

self.conv3 = conv1x1(width, planes * self.expansion)
self.bn3 = norm_layer(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride

def forward(self, x):
identity = 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)

if self.downsample is not None:
identity = self.downsample(x)

out += identity
out = self.relu(out)

return out


4、ResNet整体网络结构

import torch
import torch.nn as nn

__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',
'resnet152', 'resnext50_32x4d', 'resnext101_32x8d',
'wide_resnet50_2', 'wide_resnet101_2']

model_urls = {
}

def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,

def conv1x1(in_planes, out_planes, stride=1):
"""1x1 convolution"""
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)

class BasicBlock(nn.Module):
expansion = 1

def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
base_width=64, dilation=1, norm_layer=None):
super(BasicBlock, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
if groups != 1 or base_width != 64:
raise ValueError('BasicBlock only supports groups=1 and base_width=64')
if dilation > 1:
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")

self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = norm_layer(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = norm_layer(planes)
self.downsample = downsample
self.stride = stride

def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)

out = self.conv2(out)
out = self.bn2(out)

if self.downsample is not None:
identity = self.downsample(x)

out += identity
out = self.relu(out)

return out

class Bottleneck(nn.Module):

expansion = 4

def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
base_width=64, dilation=1, norm_layer=None):
super(Bottleneck, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
width = int(planes * (base_width / 64.)) * groups

self.conv1 = conv1x1(inplanes, width)
self.bn1 = norm_layer(width)
self.conv2 = conv3x3(width, width, stride, groups, dilation)
self.bn2 = norm_layer(width)
self.conv3 = conv1x1(width, planes * self.expansion)
self.bn3 = norm_layer(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride

def forward(self, x):
identity = 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)

if self.downsample is not None:
identity = self.downsample(x)

out += identity
out = self.relu(out)

return out

class ResNet(nn.Module):

def __init__(self, block, layers, num_classes=6, zero_init_residual=False,
groups=1, width_per_group=64, replace_stride_with_dilation=None,
norm_layer=None):
super(ResNet, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
self._norm_layer = norm_layer

self.inplanes = 64
self.dilation = 1
if replace_stride_with_dilation is None:

replace_stride_with_dilation = [False, False, False]
if len(replace_stride_with_dilation) != 3:
raise ValueError("replace_stride_with_dilation should be None "
"or a 3-element tuple, got {}".format(replace_stride_with_dilation))
self.groups = groups
self.base_width = width_per_group
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,
bias=False)
self.bn1 = norm_layer(self.inplanes)
self.relu = nn.ReLU(inplace=True)
self.layer1 = self._make_layer(block, 64, layers[0])
self.layer2 = self._make_layer(block, 128, layers[1], stride=2,
dilate=replace_stride_with_dilation[0])
self.layer3 = self._make_layer(block, 256, layers[2], stride=2,
dilate=replace_stride_with_dilation[1])
self.layer4 = self._make_layer(block, 512, layers[3], stride=2,
dilate=replace_stride_with_dilation[2])
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')
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)

if zero_init_residual:
for m in self.modules():
if isinstance(m, Bottleneck):
nn.init.constant_(m.bn3.weight, 0)
elif isinstance(m, BasicBlock):
nn.init.constant_(m.bn2.weight, 0)

def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
norm_layer = self._norm_layer
downsample = None
previous_dilation = self.dilation
if dilate:
self.dilation *= stride
stride = 1
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
conv1x1(self.inplanes, planes * block.expansion, stride),
norm_layer(planes * block.expansion),
)

layers = []
layers.append(block(self.inplanes, planes, stride, downsample, self.groups,
self.base_width, previous_dilation, norm_layer))
self.inplanes = planes * block.expansion

for _ in range(1, blocks):
layers.append(block(self.inplanes, planes, groups=self.groups,
base_width=self.base_width, dilation=self.dilation,
norm_layer=norm_layer))

return nn.Sequential(*layers)

def _forward_impl(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)

x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)

return x

def forward(self, x):
return self._forward_impl(x)

def _resnet(arch, block, layers, pretrained, progress, **kwargs):
model = ResNet(block, layers, **kwargs)

return model

def resnet18(pretrained=False, progress=True, **kwargs):
r"""ResNet-18 model from
"Deep Residual Learning for Image Recognition" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, progress,
**kwargs)

def resnet34(pretrained=False, progress=True, **kwargs):
r"""ResNet-34 model from
"Deep Residual Learning for Image Recognition" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, progress,
**kwargs)

def resnet50(pretrained=False, progress=True, **kwargs):
r"""ResNet-50 model from
"Deep Residual Learning for Image Recognition" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress,
**kwargs)

def resnet101(pretrained=False, progress=True, **kwargs):
r"""ResNet-101 model from
"Deep Residual Learning for Image Recognition" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress,
**kwargs)

def resnet152(pretrained=False, progress=True, **kwargs):
r"""ResNet-152 model from
"Deep Residual Learning for Image Recognition" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, progress,
**kwargs)

def resnext50_32x4d(pretrained=False, progress=True, **kwargs):
r"""ResNeXt-50 32x4d model from
"Aggregated Residual Transformation for Deep Neural Networks" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
kwargs['groups'] = 32
kwargs['width_per_group'] = 4
return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3],
pretrained, progress, **kwargs)

def resnext101_32x8d(pretrained=False, progress=True, **kwargs):
r"""ResNeXt-101 32x8d model from
"Aggregated Residual Transformation for Deep Neural Networks" _
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
kwargs['groups'] = 32
kwargs['width_per_group'] = 8
return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3],
pretrained, progress, **kwargs)

def wide_resnet50_2(pretrained=False, progress=True, **kwargs):
r"""Wide ResNet-50-2 model from
"Wide Residual Networks" _
The model is the same as ResNet except for the bottleneck number of channels
which is twice larger in every block. The number of channels in outer 1x1
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
channels, and in Wide ResNet-50-2 has 2048-1024-2048.

Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
kwargs['width_per_group'] = 64 * 2
return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3],
pretrained, progress, **kwargs)

def wide_resnet101_2(pretrained=False, progress=True, **kwargs):
r"""Wide ResNet-101-2 model from
"Wide Residual Networks" _
The model is the same as ResNet except for the bottleneck number of channels
which is twice larger in every block. The number of channels in outer 1x1
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
channels, and in Wide ResNet-50-2 has 2048-1024-2048.

Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
kwargs['width_per_group'] = 64 * 2
return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3],
pretrained, progress, **kwargs)

def test():
net = resnet34()
img = torch.randn(1, 3, 224, 224)
y = net(img)
print(y.size())

if __name__ == '__main__':
test()



Original: https://blog.csdn.net/retainenergy/article/details/123554292
Author: 下雨天不下雨
Title: ResNet代码实现及原理——学习记录

(0)

### 大家都在看

• #### 懒出天际–语音鼠标，解放双手，靠嘴使唤鼠标。SAPI语音识别，WINAPI鼠标消息

一、背景调查结合语音识别与MFC鼠标消息，就拥有了语音鼠标。不同于市面上，科大讯飞，百度AI的科技同类产品的功能：语音转文字，语音播报，这些。这回是彻底靠语音移动鼠标，单击，双击，…

人工智能 2023年5月25日
023
• #### 清晰、幽默、轻松地深入理解YOLOv5网络结构和一些细节（查阅无数资料文献总结）

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年8月27日
0121
• #### python取对数及作对数差在绘制散点图中的作用

文章目录 前言 一、diff()运算 二、三种情况下的散点图 * 1.取数据 2.绘制散点图 总结 前言 问题背景：在做两变量散点图分析其相关性时，在某本书上看到了如下操作： tr…

人工智能 2023年7月6日
028
• #### 3D Object Detection经典论文整理【分类/下载/代码/笔记】

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年12月5日
0101
• #### 【python代码实现】人工神经网络分类算法及其实战案例（股票价格波动分析）

目录 前置知识 * 1、前言 2、人工神经网络模型 – 2.1、神经元模型与单层神经网络 2.2、多层人工神经网络模型 人工神经网络分类算法 * 1、构建数据集 2、响…

人工智能 2023年6月30日
031
• #### 论文研读-机器学习可视化-面向可视解释的零样本分类主动学习

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年12月11日
072
• #### 目标检测算法

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年11月16日
0201
• #### python怎么添加csv模式_python写入csv教程：Python的CSV模块，怎么新建为添加模式

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年12月4日
088
• #### 数据挖掘简答题

什么是数据挖掘 什么是分类，什么是聚类，分类和聚类的区别 KDD(知识发现)的基本过程 知识发现处理过程的模型 从广义的观点，数据挖掘是从大型数据集（可能是不完全的，有噪音的，不确…

人工智能 2023年5月31日
018
• #### 基于深度学习的人脸表情识别实现

1. 前言 关于人脸识别，其实我一直以来做过蛮多的项目，有些是整理出帖子了，有些是没有整理出，因为学业比较繁重，所以也是断断续续的来整理。最近刚好是五一假期，也出不去，就想着来整理…

人工智能 2023年5月26日
019
• #### 14天学习训练营之 初识Pygame

目录 学习知识点 PyGame 之第一个 PyGame 程序 导入模块 初始化 ​​1.screen 2. 游戏业务 学习笔记 当 init () 的时候，它在干什么？ init …

人工智能 2023年7月30日
014
• #### 【ROS】利用ROS将KITTI数据集点云数据投影到2D图像

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年12月20日
0102
• #### Gavin老师Transformer直播课感悟 – NLP信息提取中的CRF Modeling详解

注入产生的原理: 数据库设置为GBK编码: 宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而…

人工智能 2022年9月27日
0174
• #### 深度学习论文: YOLO-Z: Improving small object detection in YOLOv5 for autonomous vehicles及其PyTorch实现

深度学习论文: YOLO-Z: Improving small object detection in YOLOv5 for autonomous vehicles及其PyTorc…

人工智能 2023年7月10日
014
• #### pandas中DataFrame数据合并及分组

划分之后可使用count计数： df_1 = df.groupby(‘key1’) print(df_1.count()) 输出结果为： key2 data1 data2 key1…

人工智能 2023年6月19日
024
• #### YOLOv5训练自己的数据集

YOLOv5训练自己的数据集 文件夹前期大致结构 一、创建VOCdata文件夹 在VOCdata文件夹中创建两个子文件夹，分为训练和测试。 将原始图片放入其中文件夹里的old文件夹…

人工智能 2023年7月10日
019