前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >pytorch笔记:04)resnet网络&解决输入图像大小问题「建议收藏」

pytorch笔记:04)resnet网络&解决输入图像大小问题「建议收藏」

作者头像
全栈程序员站长
发布于 2022-09-01 08:11:02
发布于 2022-09-01 08:11:02
4.8K00
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

因为torchvision对resnet18-resnet152进行了封装实现,因而想跟踪下源码

首先看张核心的resnet层次结构图(图1),它诠释了resnet18-152是如何搭建的,其中resnet18和resnet34结构类似,而resnet50-resnet152结构类似。下面先看resnet18的源码

图1

resnet18 首先是models.resnet18函数的调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def resnet18(pretrained=False, **kwargs):
    """Constructs a ResNet-18 model.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    #[2, 2, 2, 2]和结构图[]X2是对应的
    model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
    if pretrained: #加载模型权重
        model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))
    return model

这里涉及到了一个BasicBlock类(resnet18和34),这样的一个结构我们称为一个block。在block内部conv都使用了padding,因此输入的in_img_size和out_img_size都是56×56,在图2右边的shortcut只需要改变输入的channel的大小,输入block的输入tensor和输出tensor就可以相加(详细内容)

图2

事实上图2是Bottleneck类(用于resnet50-152,稍后分析),其和BasicBlock差不多,图3为图2的精简版(ps:可以把下图视为为一个box_block,即多个block叠加在一起,x3说明有3个上图一样的结构串起来):

图3

BasicBlock类,可以对比结构图中的resnet18和resnet34,类中expansion =1,其表示block内部最后一个卷积的输出channel与第一个卷积的输出channel比值,即: e x p a n s i o n = l a s t _ b l o c k _ c h a n n e l / f i r s t _ b l o c k _ c h a n n e l expansion= last\_block\_channel/first\_block\_channel expansion=last_block_channel/first_block_channel

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)
                     
class BasicBlock(nn.Module):
    expansion = 1
	#inplanes其实就是channel,叫法不同
    def __init__(self, inplanes, planes, stride=1, downsample=None):
        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 = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

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

        out = self.conv2(out)
        out = self.bn2(out)
		#把shortcut那的channel的维度统一
        if self.downsample is not None:
            residual = self.downsample(x)

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

        return out

接下来是ResNet类,其和我们通常定义的模型差不多一个__init__()+forward(),代码有点长,我们一步步来分析:

  1. 参考前面的结构图,所有的resnet的第一个conv层都是一样的,输出channel=64
  2. 然后到了self.layer1 = self._make_layer(block, 64, layers[0]),这里的layers[0]=2,然后我们进入到_make_layer函数,由于stride=1或当前的输入channel和上一个块的输出channel一样,因而可以直接相加
  3. self.layer2 = self._make_layer(block, 128, layers[1], stride=2),此时planes=128而self.inplanes=64(上一个box_block的输出channel),此时channel不一致,需要对输出的x扩维后才能相加,而downsample 实现的就是该功能(ps:这里只有box_block中的第一个block需要downsample,为何?看图4)
  4. self.layer3 = self._make_layer(block, 256, layers[2], stride=2),此时planes=256而self.inplanes=128为,此时也需要扩维后才能相加,layer4 同理。

图4

图4中下标2,3,4和上面的步骤对应,图中箭头旁数值表示block输入或者输出的channel数。 具体看图4-2,上一个box_block的最后一个block输出channel为64(亦是下一个box_block的输入channel),而当前的box_block的第一个block的输出为128,在此需要扩维才能相加。然后到了当前box_block的第2个block,其输入channel和输出channel是一致的,因此无需扩维 (ps:在shotcut中若维度或者feature_size不一致则需要downsample,这里仅从维度方面进行阐述)。 就是说在box_block内部,只需要对第1个block进行扩维,因为在box_block内,第一个block输出channel和剩下的保持一致了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ResNet(nn.Module):
    def __init__(self, block, layers, num_classes=1000):
        self.inplanes = 64
        super(ResNet, self).__init__()
        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)
        self.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.AvgPool2d(7, stride=1)
        self.fc = nn.Linear(512 * block.expansion, num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, block, planes, blocks, stride=1):
        #downsample 主要用来处理H(x)=F(x)+x中F(x)和xchannel维度不匹配问题
        downsample = None
        #self.inplanes为上个box_block的输出channel,planes为当前box_block块的输入channel
        #在shotcut中若维度或者feature_size不一致则需要downsample 
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        #只在这里传递了stride=2的参数,因而一个box_block中的图片大小只在第一次除以2
        layers.append(block(self.inplanes, planes, stride, downsample))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        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)

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x

resnet152 resnet152和resnet18差不多,Bottleneck类替换了BasicBlock,[3, 8, 36, 3]也和上面结构图对应。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def resnet152(pretrained=False, **kwargs):
    """Constructs a ResNet-152 model. Args: pretrained (bool): If True, returns a model pre-trained on ImageNet """
    model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['resnet152']))
    return model

Bottleneck类,这里需要注意的是 expansion = 4,前面2个block的channel没有变,最后一个变成了第一个的4倍,具体可看本文的第2个图。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(Bottleneck, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                               padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

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

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

        return out

图像输入大小问题: 在旧版的torchvision中,其预训练权重的默认图片大小为224224,若图片大小经模型后缩小后和最后一层全连接层不匹配,则会抛出异常,比如输入大小256256 新版已经兼容了输入图片的大小,方法就是使用AdaptiveAvgPool2d

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
RuntimeError: size mismatch, m1: [1 x 8192], m2: [2048 x 1000] at c:\miniconda2\conda-bld\pytorch-cpu_1519449358620\work\torch\lib\th\generic/THTensorMath.c:1434

首先我们看下,resnet在哪些地方改变了输出图像的大小

conv和pool层的输出大小都可以根据下面公式计算得出 H o u t = f l o o r ( ( H i n + 2 ∗ p a d d i n g [ 0 ] − k e r n e l _ s i z e [ 0 ] ) / s t r i d e [ 0 ] ) + 1 H_{out} = floor((H_{in} + 2 * padding[0] – kernel\_size[0]) / stride[0]) + 1 Hout​=floor((Hin​+2∗padding[0]−kernel_size[0])/stride[0])+1 W o u t = f l o o r ( ( W i n + 2 ∗ p a d d i n g [ 1 ] − k e r n e l _ s i z e [ 1 ] ) / s t r i d e [ 1 ] ) + 1 W_{out} = floor((W_{in} + 2 * padding[1] -kernel\_size[1] ) / stride[1] )+ 1 Wout​=floor((Win​+2∗padding[1]−kernel_size[1])/stride[1])+1

方法1: 可以根据输入图片的大小和上面的公式先计算出最后一层神经元的数目calc_num,然后在替换掉fc层即可,比如

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
model =  torchvision.models.resnet152()
model.fc = nn.Linear(calc_num, nb_classes)

但方法需要自己计算calc_num,推荐使用方法2

方法2: 暴力的方法,就是不管卷积层的输出大小,取其平均值做为输出,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
model  = torchvision.models.resnet152()
model.avgpool = nn.AdaptiveAvgPool2d((1, 1))

第一次研究pytorch,请大神门轻喷

reference: resnet详细介绍 deeper bottleneck Architectures详细介绍

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/141262.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
YOLOv8轻量级:Ghostnet、G_ghost、Ghostnetv2家族大作战(三):华为GhostNet再升级,全系列硬件最优极简AI网络G_ghost
引入到yolov8,Bottleneck与c2f结合,代替backbone中的所有c2f。
AI小怪兽
2023/11/01
1.5K0
【最强ResNet改进系列】Res2Net:一种新的多尺度网络结构,性能提升显著
【导读】2020年,在各大CV顶会上又出现了许多基于ResNet改进的工作,比如:Res2Net,ResNeSt,IResNet,SCNet等等。为了更好的了解ResNet整个体系脉络的发展,我们开设了一个最强ResNet改进系列专题,主要为大家介绍2020年最新发表在顶会顶刊上基于ResNet改进的论文,这些论文的创新点很值得参考借鉴!本文是【最强ResNet改进系列】第一篇文章,本文我们将着重讲解Res2Net,该论文已被TPAMI2020录用,另外ResNeSt的论文解读见:【CV中的注意力机制】史上最强"ResNet"变体--ResNeSt,下一篇我们将直接来讲解IResNet
深度学习技术前沿公众号博主
2020/08/11
10.7K0
PyTorch源码解读之torchvision.models「建议收藏」
PyTorch框架中有一个非常重要且好用的包:torchvision,该包主要由3个子包组成,分别是:torchvision.datasets、torchvision.models、torchvision.transforms。这3个子包的具体介绍可以参考官网:http://pytorch.org/docs/master/torchvision/index.html。具体代码可以参考github:https://github.com/pytorch/vision/tree/master/torchvision。
全栈程序员站长
2022/09/07
9420
明月深度学习实践004:ResNet网络结构学习
ResNet可谓大名鼎鼎了,一直遵循拿来主义,没有好好去学习它,当然,作为一个提出来快五年的网络结构,已经有太多人写过它了,不好下笔。
明月AI
2021/10/28
1.2K0
明月深度学习实践004:ResNet网络结构学习
ResNet详细解读
这篇文章是Deep Residual Learning for Image Recognition 的翻译,精简部分内容的同时补充了相关的概念,如有错误,敬请指正。
全栈程序员站长
2022/09/01
2.1K0
ResNet详细解读
通过和resnet18和resnet50理解PyTorch的ResNet模块
resnet和resnext的框架基本相同的,这里先学习下resnet的构建,感觉高度模块化,很方便。本文算是对 PyTorch源码解读之torchvision.modelsResNet代码的详细理解,另外,强烈推荐这位大神的PyTorch的教程!
全栈程序员站长
2022/09/01
1.7K0
通过和resnet18和resnet50理解PyTorch的ResNet模块
ResNet+FPN实现+白嫖代码「建议收藏」
===========================================================
全栈程序员站长
2022/08/20
1.2K0
ResNet+FPN实现+白嫖代码「建议收藏」
【猫狗数据集】使用预训练的resnet18模型
链接:https://pan.baidu.com/s/1l1AnBgkAAEhh0vI5_loWKw 提取码:2xq4
西西嘛呦
2020/08/26
3K0
【猫狗数据集】使用预训练的resnet18模型
resnet34 pytorch_pytorch环境搭建
导师的课题需要用到图片分类;入门萌新啥也不会,只需要实现这个功能,给出初步效果,不需要花太多时间了解内部逻辑。经过一周的摸索,建好环境、pytorch,终于找到整套的代码和数据集,实现了一个小小的分类。记录一下使用方法,避免后续使用时遗忘。感谢各位大佬的开源代码和注释!
全栈程序员站长
2022/09/27
8500
resnet34 pytorch_pytorch环境搭建
PyTorch建立resnet34和resnet101代码[通俗易懂]
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/10
7610
深入理解ResNet原理解析及代码实现
梯度消失和梯度爆炸的问题阻止了刚开始的收敛,这一问题通过初始化归一化和中间层归一化得到了解决。解决了收敛的问题后又出现了退化的现象:随着层数加深,准确率升高然后急剧下降。且这种退化不是由过拟合造成,且向网络中添加适当多层导致了更大的训练误差。随着网络深度的增加,模型精度并不总是提升,并且这个问题并不是由过拟合(overfitting)造成的,因为网络加深后不仅测试误差变高了,它的训练误差竟然也变高了。作者提出,这可能是因为更深的网络会伴随梯度消失/爆炸问题,从而阻碍网络的收敛。这种加深网络深度但网络性能却下降的现象被称为退化问题。也就是说,随着深度的增加出现了明显的退化,网络的训练误差和测试误差均出现了明显的增长,ResNet就是为了解决这种退化问题而诞生的。
狼啸风云
2020/04/01
5.6K0
深入理解ResNet原理解析及代码实现
【CV中的Attention机制】ECCV 2018 Convolutional Block Attention Module
这是【CV中的Attention机制】系列的第三篇文章。目前cv领域借鉴了nlp领域的attention机制以后生产出了很多有用的基于attention机制的论文,attention机制也是在2019年论文中非常火。这篇cbam虽然是在2018年提出的,但是其影响力比较深远,在很多领域都用到了该模块,所以一起来看一下这个模块有什么独到之处,并学着实现它。
BBuf
2020/02/12
2K0
【CV中的Attention机制】ECCV 2018 Convolutional Block Attention Module
FPN(特征图金字塔网络)理论基础与具体实现
论文地址:Feature Pyramid Networks for Object Detection
全栈程序员站长
2022/09/06
1.2K0
FPN(特征图金字塔网络)理论基础与具体实现
深度残差收缩网络(完整PyTorch程序)
深度残差收缩网络是建立在三个部分的基础之上的,包括残差网络、注意力机制和软阈值化。
用户7621073
2021/04/14
4.2K0
深度残差收缩网络(完整PyTorch程序)
PyTorch实现的ResNet50、ResNet101和ResNet152
PyTorch实现的ResNet50、ResNet101和ResNet152 PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks
全栈程序员站长
2022/11/10
9200
PyTorch实现的ResNet50、ResNet101和ResNet152
打通多个视觉任务的全能Backbone:HRNet
在人体姿态识别这类的任务中,需要生成一个高分辨率的heatmap来进行关键点检测。这就与一般的网络结构比如VGGNet的要求不同,因为VGGNet最终得到的feature map分辨率很低,损失了空间结构。
BBuf
2020/04/23
1.5K0
打通多个视觉任务的全能Backbone:HRNet
Pytorch预训练模型以及修改
pytorch中自带几种常用的深度学习网络预训练模型,torchvision.models包中包含alexnet、densenet、inception、resnet、squeezenet、vgg等常用网络结构,并且提供了预训练模型,可通过调用来读取网络结构和预训练模型(模型参数)。往往为了加快学习进度,训练的初期直接加载pretrain模型中预先训练好的参数。加载model如下所示:
狼啸风云
2020/05/06
20.6K0
用 Python 从零开始构建 ResNet
作者 | SHAKHADRI313 译者 | 王强 策划 | 刘燕 近年来,深度学习和计算机视觉领域取得了一系列突破。特别是行业引入了非常深的卷积神经网络后,在这些模型的帮助下,图像识别和图像分类等问题取得了非常好的成果。 因此这些年来,深度学习架构变得越来越深(层越来越多)以解决越来越复杂的任务,这也有助于提高分类和识别任务的性能,并让它们表现稳健。 但当我们继续向神经网络添加更多层时,模型训练起来也越来越困难,模型的准确度开始饱和,然后还会下降。于是 ResNet 诞生了,让我们摆脱了这种窘境,并能帮助
深度学习与Python
2023/04/01
4200
用 Python 从零开始构建 ResNet
rock数据集ResNet34实现「建议收藏」
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/10
5170
rock数据集ResNet34实现「建议收藏」
resnet18模型
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/141235.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/01
3700
推荐阅读
相关推荐
YOLOv8轻量级:Ghostnet、G_ghost、Ghostnetv2家族大作战(三):华为GhostNet再升级,全系列硬件最优极简AI网络G_ghost
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档