【学习笔记】resnet-18 pytorch源代码解读

article/2025/11/10 15:22:28

目录

  • ResNet-18网络结构简图
  • ResNet-18的代码结构
    • 残差块结构
    • ResNet类
      • 构造方法和forward
      • _make_layer方法
  • 完整的ResNet-18结构图

使用的resnet-18的源代码来源于 PyTorch1.0torchvision0.2.2

ResNet-18网络结构简图

ResNet(Residual Neural Network)来源于微软研究院的Kaiming He等人的论文《Deep Residual Learning for Image Recognition》。ResNet-18的网络简图如下图(假设网络的输入的张量的形状为 3 × 64 × 64 3\times 64\times 64 3×64×64

resnet18简要结构图

如图resnet的结构分为四个stage,完整的ResNet-18的结构图在最后。

ResNet-18的代码结构

pytorch中定义了resnet-18,resnet-34,resnet-50,resnet-101,resnet-152,在pytorch中使用resnet-18的方法如下:

from torchvision import models
resnet = models.resnet18(pretrained=True)

其中pretrained参数表示是否载入在ImageNet上预训练的模型。通过models.resnet18函数载入网络模型,该函数的定义如下

def resnet18(pretrained=False, **kwargs):"""构建一个ResNet-18模型参数:pretrained (bool): 若为True则返回在ImageNet上预训练的模型"""model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))return model

使用其他的ResNet模型时,使用对应的名字的函数就行,函数返回的是一个ResNet类型的实例,这个类定义了resnet网络的结构,ResNet类的结构如下:

class ResNet(nn.Module):def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):"""定义ResNet网络的结构参数:block (BasicBlock / Bottleneck): 残差块类型layers (list): 每一个stage的残差块的数目,长度为4num_classes (int): 类别数目zero_init_residual (bool): 若为True则将每个残差块的最后一个BN层初始化为零,这样残差分支从零开始每一个残差分支,每一个残差块表现的就像一个恒等映射,根据https://arxiv.org/abs/1706.02677这可以将模型的性能提升0.2~0.3%"""super(ResNet, self).__init__()# __init__def _make_layer(self, block, planes, blocks, stride=1):# _make_layer functiondef forward(self, x):# forward function

残差块结构

注意上面的ResNet类的代码中的block参数,这个参数定义了残差块的结构,分为两种:

  1. BasicBlock:resnet-18和resnet-34的残差块结构;
  2. Bottleneck:resnet-50,resnet-101和resnet-152的残差块结构;

这里仅关注resnet-18因此我们只关注BasicBlock类,这个类的结构如下:

def conv3x3(in_planes, out_planes, stride=1):"""3x3 convolution with padding"""return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,padding=1, bias=False)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 = 1def __init__(self, inplanes, planes, stride=1, downsample=None):"""定义BasicBlock残差块类参数:inplanes (int): 输入的Feature Map的通道数planes (int): 第一个卷积层输出的Feature Map的通道数stride (int, optional): 第一个卷积层的步长downsample (nn.Sequential, optional): 旁路下采样的操作注意:残差块输出的Feature Map的通道数是planes*expansion"""super(BasicBlock, self).__init__()self.conv1 = conv3x3(inplanes, planes, stride)self.bn1 = nn.BatchNorm2d(planes)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(planes, planes)self.bn2 = nn.BatchNorm2d(planes)self.downsample = downsampleself.stride = stridedef forward(self, x):identity = xout = 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 += identityout = self.relu(out)return out

首先BasicBlock类有一个类属性,BasicBlock.expansion这个类属性的值为1,另外在 Bottleneck类中也有这个类属性,值为4,这个类属性表示残差块的输入的Feature Map的通道数为 i n p l a n e s inplanes inplanes,输出的通道数为 p l a n e s × e x p a n s i o n planes \times expansion planes×expansion
out ( p l a n e s × e x p a n s i o n ) × H ′ × W ′ = B l o c k ( x i n p l a n e s × H × W ) \textbf{\textit{out}}_{(planes \times expansion) \times H' \times W'}=Block(\textit{\textbf{x}}_{inplanes \times H \times W}) out(planes×expansion)×H×W=Block(xinplanes×H×W)

BasicBlock定义的网络结构如下所示:
BasicBlock

ResNet类

构造方法和forward

首先是构造方法和forward方法

class ResNet(nn.Module):def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):"""定义ResNet网络的结构参数:block (BasicBlock / Bottleneck): 残差块类型layers (list): 每一个stage的残差块的数目,长度为4num_classes (int): 类别数目zero_init_residual (bool): 若为True则将每个残差块的最后一个BN层初始化为零,这样残差分支从零开始每一个残差分支,每一个残差块表现的就像一个恒等映射,根据https://arxiv.org/abs/1706.02677这可以将模型的性能提升0.2~0.3%"""super(ResNet, self).__init__()self.inplanes = 64  # 第一个残差块的输入通道数self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)# Stage1 ~ Stage4self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2)self.layer3 = self._make_layer(block, 256, layers[2], stride=2)self.layer4 = self._make_layer(block, 512, layers[3], stride=2)self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # GAPself.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.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 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)x = self.avgpool(x)x = x.view(x.size(0), -1)x = self.fc(x)return x

ResNet类定义的网络结构如下图所示:
ResNet

_make_layer方法

ResNet类中还有一个方法是_make_layer在这个方法中,定义了ResNet网络的一个Stage的结构,代码如下:

def _make_layer(self, block, planes, blocks, stride=1):"""定义ResNet的一个Stage的结构参数:block (BasicBlock / Bottleneck): 残差块结构plane (int): 残差块中第一个卷积层的输出通道数bloacks (int): 当前Stage中的残差块的数目stride (int): 残差块中第一个卷积层的步长"""downsample = Noneif stride != 1 or self.inplanes != planes * block.expansion:downsample = nn.Sequential(conv1x1(self.inplanes, planes * block.expansion, stride),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride, downsample))self.inplanes = planes * block.expansionfor _ in range(1, blocks):layers.append(block(self.inplanes, planes))return nn.Sequential(*layers)

首先是为第一个残差块定义downsample结构,当残差块的输入和输出的尺寸不一致或者通道数不一致的时候就会需要下采样结构,下采样结构由一个1x1卷积层和一个BatchNorm层组成。之后定义了 b l o c k s blocks blocks个残差块(在ResNet-18中每一个Stage均有两个残差块),只有第一个残差块需要下采样层。

在ResNet中,Stage1中输入通道数和输出通道数相同,并且使用的是stride为1的卷积,因此在Stage1中不需要有下采样层,其余Stage中钧需要有下采样层。

完整的ResNet-18结构图

以输入 3 × 64 × 64 3 \times 64 \times 64 3×64×64图像为例。

ResNet18


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

相关文章

ResNet18详细原理(含tensorflow版源码)

ResNet18原理 ResNet18是一个经典的深度卷积神经网络模型,由微软亚洲研究院提出,用于参加2015年的ImageNet图像分类比赛。ResNet18的名称来源于网络中包含的18个卷积层。 ResNet18的基本结构如下: 输入层:接收大小为224x224的RG…

resnet18

前言 在前篇vgg16之后,无法成功训练vgg16,发现是自己电脑可用的显存太低了,遂放弃。 在2015 ILSVRC&COCO比赛中,何恺明团队提出的Resnet网络斩获第一,这是一个经典的网络。李沐说过,如果要学习一个CNN网…

ResNet18网络的具体构成

一、基础 RetNet网络的基础是残差块。 以下是原始论文所给出的最基础的残差块。后续可以对单残差块进行处理,如加入池化,批量化归一等各种操作。 二、最基本的的ResNet18 ResNet18的基本含义是,网络的基本架构是ResNet,网络的深…

【神经网络】(10) Resnet18、34 残差网络复现,附python完整代码

各位同学好,今天和大家分享一下 TensorFlow 深度学习中如何搭载 Resnet18 和 Resnet34 残差神经网络,残差网络利用 shotcut 的方法成功解决了网络退化的问题,在训练集和校验集上,都证明了的更深的网络错误率越小。 论文中给出的具…

Resnet 18网络模型

1. 残差网络:(Resnet) 残差块: 让我们聚焦于神经网络局部:如图左侧所示,假设我们的原始输入为x,而希望学出的理想映射为f(x)(作为上方激活函数的输入)。左图虚线框中…

【ResNet】Pytorch从零构建ResNet18

Pytorch从零构建ResNet 第一章 从零构建ResNet18 第二章 从零构建ResNet50 文章目录 Pytorch从零构建ResNet前言一、ResNet是什么?1. 残差学习2. ResNet具体结构 二、ResNet分步骤实现三、完整例子测试总结 前言 ResNet 目前是应用很广的网络基础框架,所…

HTML+CSS 简单的顶部导航栏菜单制作

导航栏的制作: 技术要求: CSSHTML各类标签 实现目的: 制作导航栏菜单 代码分析: 基本样式清除无序列原点删除下划线删除文字默认居中a标签设置块级元素伪类选择器对a状态修饰 分步实现: 分三栏布局:…

WEB前端(7)—— 简单的 HTML+CSS 导航栏案例

适合每个新手的导航栏&#xff1a; 代码与运行效果如图&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>导航栏</title><style type"text/css">ul{/*设置导航栏的框框*/margin: 30px…

CSS — 导航栏篇(一)

Navigation Bar Navigation Bar 是什么&#xff1f;这就是每个网站都会有的导航栏&#xff0c;本文将会带你接触导航栏的世界。首先我们需要了解导航栏的作用——它能快速帮助用户进行需求选择。一个清晰的导航栏能让用户第一时间了解网站的基本模块功能&#xff0c;而且作为网…

CSS + HTML导航栏效果

今天写了一个导航栏&#xff0c;需要的效果如下&#xff1a; 实现的代码思路如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>导航栏</title><style type"text/css&qu…

html+css创建侧边导航栏

效果&#xff1a; 代码&#xff1a; .left{position: fixed;width: 250px;height: 2000px;background-color: rgb(100, 93, 93);float: left;text-align: center; } .nav a{display: block;width: 247px;height: 70px;background-color: rgb(100, 93, 93);color: rgb(254, 254…

HTML5+CSS3制作底部导航栏

目录 前言 一、底部导航栏示例图 二、HTML框架 1.一号盒子 2.二号盒子 总结 ​​​​​​ 前言 在日常的网上冲浪中&#xff0c;我们常常在网页最底部&#xff0c;看到一大堆链接&#xff0c;非常整齐&#xff0c;一目了然&#xff0c;那么是如何实现的呢&#xff1f;..…

网页制作之侧边导航栏(只用HTML实现)

话不多说&#xff0c;上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" con…

CSS【导航栏】

导航栏链接列表 作为标准的HTML基础一个导航栏是必须的。在我们的例子中我们将建立一个标准的HTML列表导航栏。导航条基本上是一个链接列表&#xff0c;所以使用 <ul> 和 <li>元素非常有意义&#xff1a; <!DOCTYPE html><html><head><meta…

html中网页导航栏设置

以下内容是摘抄博客&#xff1a;https://www.runoob.com/css/css-navbar.html 设计导航窗口在左侧的显示如下&#xff1a; 代码部分则如下&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>菜鸟教程(runoo…

html左侧导航栏右侧显示内容

效果图 代码 复制下来直接运行就可以 <!doctype html> <html lang "en"><head><meta charset "UTF-8"><meta name "viewport"content "widthdevice-width, user-scalableno, initial-scale1.0, maximum-s…

导航栏的HTML的布局方式

1.利用浮动完成布局 以小米导航栏为例 <style>* {padding: 0;margin: 0;/* 通配符全选&#xff0c;取消内外边距的小缝隙 // 不建议使用通配符 */}header {width: 100%;background-color: #333333;/* 设置背景 */}div {width: 1226px;height: 40px;margin: auto;/* 设…

html里制作简单导航栏

今天简单的做了一下网页里的导航栏。 效果如下&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>实验3</title><style type"text/css">ul{/*设置导航栏的框框*/margi…

html中关于侧边导航栏和导航栏的编写

侧边导航栏 <style>.box{width: 50px;height: 50px;background-color: #483957;transition: width .5s,background-color .2s;}.box:hover{background-color: #004FCB;width: 200px;cursor: pointer;}.a1{position: fixed;right: 40px;top: 200px;float: right;}</st…

Html顶部导航栏实现

顶部导航nav栏实现&#xff08;包括一级菜单&#xff0c;二级菜单&#xff09; 实现效果&#xff1a; 代码如下~ Html部分&#xff1a; <!doctype html> <html> <head> <meta charset"utf-8"> <title>顶部导航栏</title> <…