resnet18 pytorch_pytorch全连接层

resnet18 pytorch_pytorch全连接层创建各版本的ResNet模型,ResNet18,ResNet34,ResNet50,ResNet101,ResNet152原文地址:https://arxiv.org/pdf/1512.03385.pdf论文就不解读了,大部分解读都是翻译,看的似懂非懂,自己搞懂就行了。最近想着实现一下经典的网络结构,看了原文之后,根据原文代码结构开始实现。起初去搜了下各种版本的实现,发现很多博客都是错误百出,有些博文都发布几年了,错误还是没人发现,评论区几十号人不知道是真懂还是装懂,颇有些无奈啊。因此打算自己手动实

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

创建各版本的ResNet模型,ResNet18,ResNet34,ResNet50,ResNet101,ResNet152

原文地址: https://arxiv.org/pdf/1512.03385.pdf


论文就不解读了,大部分解读都是翻译,看的似懂非懂,自己搞懂就行了。

最近想着实现一下经典的网络结构,看了原文之后,根据原文代码结构开始实现。

起初去搜了下各种版本的实现,发现很多博客都是错误百出,有些博文都发布几年了,错误还是没人发现,评论区几十号人不知道是真懂还是装懂,颇有些无奈啊。

因此打算自己手动实现网络结构,锻炼下自己的代码能力,也加深对网络结构的理解。

写完之后也很欣慰,毕竟一直认为自己是个菜鸡,最近竟然接连不断的发现很多博文的错误之处,而且很多人看后都没发现的,想想自己似乎还有点小水平。

最后在一套代码里,实现了各版本ResNet,为了方便。

其实最后还是觉得应该每个网络分开写比较好。因为不同版本的网络内部操作是有很大差异的,本文下面的代码是将ResidualBlock和 BottleNeckBlock分开写的,但是在维度的变换上差异还是很复杂,一方面想提高代码的复用性,另一方面也受制于复杂度。所以最后写出的算不上高复用性的精简代码。勉强能用。关于ResNet的结构,除各版本分开写之外,重复的block其实也可以分开写,因为BottleNeckBlock的维度变换太复杂,参数变换多,能分开就分开,复杂度小的地方可以复用。


以下是网络结构和实现代码,检验后都是对的;水平有限,如发现有错误,欢迎评论告知!

1 残差结构图

resnet18 pytorch_pytorch全连接层

2 VGG-19与ResNet34结构比较

在这里插入图片描述

3 ResNet各版本的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qLEx5XJg-1602504703995)(C:\Users\tony\AppData\Roaming\Typora\typora-user-images\image-20201012200856046.png)]

4 代码实现各版本

import torch.nn as nn
from torch.nn import functional as F

class ResNetModel(nn.Module):
    """ 实现通用的ResNet模块,可根据需要定义 """
    def __init__(self, num_classes=1000, layer_num=[],bottleneck = False):
        super(ResNetModel, self).__init__()

        #conv1
        self.pre = nn.Sequential(
            #in 224*224*3
            nn.Conv2d(3,64,7,2,3,bias=False),   #输入通道3,输出通道64,卷积核7*7*64,步长2,根据以上计算出padding=3
            #out 112*112*64
            nn.BatchNorm2d(64),     #输入通道C = 64

            nn.ReLU(inplace=True),   #inplace=True, 进行覆盖操作
            # out 112*112*64
            nn.MaxPool2d(3,2,1),    #池化核3*3,步长2,计算得出padding=1;
            # out 56*56*64
        )

        if bottleneck:  #resnet50以上使用BottleNeckBlock
            self.residualBlocks1 = self.add_layers(64, 256, layer_num[0], 64, bottleneck=bottleneck)
            self.residualBlocks2 = self.add_layers(128, 512, layer_num[1], 256, 2,bottleneck)
            self.residualBlocks3 = self.add_layers(256, 1024, layer_num[2], 512, 2,bottleneck)
            self.residualBlocks4 = self.add_layers(512, 2048, layer_num[3], 1024, 2,bottleneck)

            self.fc = nn.Linear(2048, num_classes)
        else:   #resnet34使用普通ResidualBlock
            self.residualBlocks1 = self.add_layers(64,64,layer_num[0])
            self.residualBlocks2 = self.add_layers(64,128,layer_num[1])
            self.residualBlocks3 = self.add_layers(128,256,layer_num[2])
            self.residualBlocks4 = self.add_layers(256,512,layer_num[3])
            self.fc = nn.Linear(512, num_classes)

    def add_layers(self, inchannel, outchannel, nums, pre_channel=64, stride=1, bottleneck=False):
        layers = []
        if bottleneck is False:

            #添加大模块首层, 首层需要判断inchannel == outchannel ?
            #跨维度需要stride=2,shortcut也需要1*1卷积扩维

            layers.append(ResidualBlock(inchannel,outchannel))

            #添加剩余nums-1层
            for i in range(1,nums):
                layers.append(ResidualBlock(outchannel,outchannel))
            return nn.Sequential(*layers)
        else:   #resnet50使用bottleneck
            #传递每个block的shortcut,shortcut可以根据是否传递pre_channel进行推断

            #添加首层,首层需要传递上一批blocks的channel
            layers.append(BottleNeckBlock(inchannel,outchannel,pre_channel,stride))
            for i in range(1,nums): #添加n-1个剩余blocks,正常通道转换,不传递pre_channel
                layers.append(BottleNeckBlock(inchannel,outchannel))
            return nn.Sequential(*layers)

    def forward(self, x):
        x = self.pre(x)
        x = self.residualBlocks1(x)
        x = self.residualBlocks2(x)
        x = self.residualBlocks3(x)
        x = self.residualBlocks4(x)

        x = F.avg_pool2d(x, 7)
        x = x.view(x.size(0), -1)
        return self.fc(x)


class ResidualBlock(nn.Module):
    ''' 定义普通残差模块 resnet34为普通残差块,resnet50为瓶颈结构 '''
    def __init__(self, inchannel, outchannel, stride=1, padding=1, shortcut=None):
        super(ResidualBlock, self).__init__()
        #resblock的首层,首层如果跨维度,卷积stride=2,shortcut需要1*1卷积扩维
        if inchannel != outchannel:
            stride= 2
            shortcut=nn.Sequential(
                nn.Conv2d(inchannel,outchannel,1,stride,bias=False),
                nn.BatchNorm2d(outchannel)
            )

        # 定义残差块的左部分
        self.left = nn.Sequential(
            nn.Conv2d(inchannel, outchannel, 3, stride, padding, bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),

            nn.Conv2d(outchannel, outchannel, 3, 1, padding, bias=False),
            nn.BatchNorm2d(outchannel),

        )

        #定义右部分
        self.right = shortcut

    def forward(self, x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)
        out = out + residual
        return F.relu(out)

class BottleNeckBlock(nn.Module):
    ''' 定义resnet50的瓶颈结构 '''
    def __init__(self,inchannel,outchannel, pre_channel=None, stride=1,shortcut=None):
        super(BottleNeckBlock, self).__init__()
        #首个bottleneck需要承接上一批blocks的输出channel
        if pre_channel is None:     #为空则表示不是首个bottleneck,
            pre_channel = outchannel    #正常通道转换


        else:   # 传递了pre_channel,表示为首个block,需要shortcut
            shortcut = nn.Sequential(
                nn.Conv2d(pre_channel,outchannel,1,stride,0,bias=False),
                nn.BatchNorm2d(outchannel)
            )

        self.left = nn.Sequential(
            #1*1,inchannel
            nn.Conv2d(pre_channel, inchannel, 1, stride, 0, bias=False),
            nn.BatchNorm2d(inchannel),
            nn.ReLU(inplace=True),
            #3*3,inchannel
            nn.Conv2d(inchannel,inchannel,3,1,1,bias=False),
            nn.BatchNorm2d(inchannel),
            nn.ReLU(inplace=True),
            #1*1,outchannel
            nn.Conv2d(inchannel,outchannel,1,1,0,bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),
        )
        self.right = shortcut

    def forward(self,x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)
        return F.relu(out+residual)


if __name__ == '__main__':
    # channel_nums = [64,128,256,512,1024,2048]

    num_classes = 6

    #layers = 18, 34, 50, 101, 152
    layer_nums = [[2,2,2,2],[3,4,6,3],[3,4,6,3],[3,4,23,3],[3,8,36,3]]
    #选择resnet版本,
    # resnet18 ——0;resnet34——1,resnet-50——2,resnet-101——3,resnet-152——4
    i = 3;
    bottleneck = i >= 2   #i<2, false,使用普通的ResidualBlock; i>=2,true,使用BottleNeckBlock

    model = ResNetModel(num_classes,layer_nums[i],bottleneck)
    print(model)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/185104.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 【性能评估】ROC曲线

    【性能评估】ROC曲线1.混淆矩阵(confusionmatrix)&nbsp;&nbsp;&nbsp;针对预测值和真实值之间的关系,我们可以将样本分为四个部分,分别是:&nbsp;&nbsp;&nbsp;真正例(TruePositive,TP):预测值和真实值都为1&nbsp;&nbsp;&nbsp;假正例(FalsePositive,FP):预测值为1,真实值为0&nbsp;&nbs…

  • php 工厂模式

    php 工厂模式一、 什么是工厂模式工厂类调用自身静态方法来生产对象实例工厂类:负责生成其他对象的类或方法途径:调用自身静态方法结果:一个实例对象工厂模式有一个关键的构造,根据一般原则命名为Fac

  • PyCharm、Itellij IDEA最好用的插件和主题[通俗易懂]

    PyCharm、Itellij IDEA最好用的插件和主题[通俗易懂]免费分享我用过相对来说最好的几款PyCharm、ItellijIDEA的插件和主题

  • Java练习—-》求字符串中的最长回文子串

    Java练习—-》求字符串中的最长回文子串手贱,做了一道对于我来说挺难的题目嘿嘿!挺有意思的,分享一下文章目录前言一,题目二,思路图形解析代码前言第一次把自己的解题思维写出来,可能写的不太好,请给位原谅,哈哈哈哈额,如果有错的,请各位大佬帮我指出来哈,谢谢!!(^U^)ノ~YO一,题目求一串字符串的最长回文子串,这里以cabacabae为例二,思路图形解析第一步:观察这串字符串—》第二步:找出最长回文子串,并设数—》说明:在这里,假设知道最长回文子串,那这里的resCenter和maxRigth,reslengthgs

    2022年10月16日
  • quotename mysql,我如何在mySQL中创建一个QUOTENAME函数

    quotename mysql,我如何在mySQL中创建一个QUOTENAME函数IwouldliketocreateaQUOTENAME()functioninmySQLliketheonethatexistsinM$SQLServer.Thisiswhatitdoes:QUOTENAMEreturnsaUnicodestringwiththedelimitersaddedtomaketheinputstr…

  • volatile关键字作用

    volatile关键字作用一、作用简述内存可见性:保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。 屏蔽JVM指令重排序(防止JVM编译源码生成class时使用重排序)…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号