Resnet模型详解

news2024/11/26 0:47:56

1、Resnet是什么?

Resnet是一种深度神经网络架构,被广泛用于计算机视觉任务,特别是图像分类。它是由微软研究院的研究员于2015年提出的,是深度学习领域的重要里程碑之一。

2、网络退化问题

理论上来讲,随着网络的层数的增加,网络能够进行更加复杂的特征提取,可以取得更好的结果。但是实验发现深度网络出现了退化问题,如下图所示。网络深度增加时,网络准确度出现饱和,之后甚至还快速下降。而且这种下降不是因为过拟合引起的,而是因为在适当的深度模型上添加更多的层会导致了更高的训练误差,从而使其下降。

图1 网络深度对比(来源:Resnet的论文)

当你使用深度神经网络进行训练时,网络层可以被看作是一系列的函数堆叠,每个函数代表一个网络层的操作,这里我们就记作f(x)。在反向传播过程中,梯度是通过链式法则逐层计算得出的。假设每个操作的梯度都小于1,因为多个小于1的数相乘可能会导致结果变得更小。在神经网络中,随着反向传播的逐层传递,梯度可能会逐渐变得非常小,甚至接近于零,这就是梯度消失问题。

而如果经过网络层操作后的输出值大于1,那么反向传播时梯度可能会相应地增大。这种情况下,梯度爆炸问题可能会出现。梯度爆炸问题指的是在深度神经网络中,梯度逐渐放大,导致底层网络的参数更新过大,甚至可能导致数值溢出。

3、残差结构

在ResNet提出之前,所有的神经网络都是通过卷积层和池化层的叠加组成的。所以,Resnet对后面计算机视觉的发展影响是巨大的。

图2 残差结构(来源:Resnet的论文)

它这里完成的一个很简单的过程,我先举一个例子:

想象一张经过神经网络处理后的低分辨率图像。为了提高图像的质量,我们引入了一个创新的思想:将原始高分辨率图像与低分辨率图像之间的差异提取出来,形成了一个残差图像。这个残差图像代表了低分辨率图像与目标高分辨率图像之间的差异或缺失的细节。

​图3 残差图像

 然后,我们将这个残差图像与低分辨率图像相加,得到一个结合了低分辨率信息和残差细节的新图像。这个新图像作为下一个神经网络层的输入,使网络能够同时利用原始低分辨率信息和残差细节信息进行更精确的学习。

图4 残差+低分辨率图像

通过这种方式,我们的神经网络能够逐步地从低分辨率图像中提取信息,并通过残差图像的相加操作将遗漏的细节加回来。这使得网络能够更有效地进行图像恢复或其他任务,提高了模型的性能和准确性。

我相信我已经成功表达了残差结构的思想和操作过程。其实这个思想也并非是resnet创新的,在我们过去的其他领域中早已有这种思想,ResNet将这一思想引入了计算机视觉领域,并在深度神经网络中的训练中取得了重要突破。这种创新在一定程度上解决了深层神经网络训练中的梯度消失和梯度爆炸问题,使得网络能够更深更准确地学习特征和表示。

4、Resnet网络结构

(1)对于相同的输出特征图尺寸,层具有相同数量的滤波器

(2)当feature map大小降低一半时,feature map的数量增加一倍【过滤器(可以看作是卷积核的集合)的数量增加一倍】,这保持了网络层的复杂度。然后通过步长为2的卷积层直接执行下采样。

网络结构具体如下图所示:

8b612e130e2ad5ac04f7bc4f8e1ed7dd.png

图5 左为VGG-19,中为34个参数层的简单网络,右为34个参数层的残差网络

ResNet相比普通网络每两层间增加了短路机制,这就形成了残差学习,其中实线表示快捷连接,虚线表示feature map数量发生了改变。

有两种情况需要考虑:

(1)输入和输出具有相同的维度时(对应实线部分):

直接使用恒等快捷连接

12534cd86e604f1782361149f07c5eda.png

(2)维度增加(当快捷连接跨越两种尺寸的特征图时,它们执行时步长为2):

①快捷连接仍然执行恒等映射,额外填充零输入以增加维度。这样就不会引入额外的参数。

②用下面公式的投影快捷连接用于匹配维度(由1×1卷积完成)

c44e781d717d4b2b8686c2bf4129d23e.png

论文中也提供了更详细的结构,如下图所示:

5、使用Pytorch实现Resnet

本来是按照论文手写的代码,但用的时候发现维度不匹配,用不了预训练权重,所以这里就照着pytorch源码进行了修改。

import torch
import torchvision
import torch.nn as nn
import torchsummary
from torch.hub import load_state_dict_from_url

model_urls = {
    "resnet18" : "https://download.pytorch.org/models/resnet18-f37072fd.pth",
    "resnet34" : "https://download.pytorch.org/models/resnet34-b627a593.pth",
    "resnet50" : "https://download.pytorch.org/models/resnet50-0676ba61.pth",
    "resnet101" : "https://download.pytorch.org/models/resnet101-63fe2227.pth",
    "resnet152" : "https://download.pytorch.org/models/resnet152-394f9c45.pth",
}

cfgs = {
    "resnet18": [2, 2, 2, 2],
    "resnet34": [3, 4, 6, 3],
    "resnet50": [3, 4, 6, 3],
    "resnet101": [3, 4, 23, 3],
    "resnet152": [3, 8, 36, 3],
}

def conv1x1(in_planes, out_planes, stride = 1):
    """1x1 convolution"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=(1,1), stride=(stride,stride), bias=False)
def conv3x3(in_planes, out_planes, stride= 1, groups=1, dilation=1):
    """3x3 convolution with padding"""
    return nn.Conv2d(in_planes,out_planes,kernel_size=(3,3),stride=(stride,stride),padding=dilation,groups=groups,bias=False,dilation=(dilation,dilation))
class BasicBlock(nn.Module):
    expansion = 1
    def __init__(self,inplanes: int,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.0)) * 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,in_channels=3,num_classes = 1000,zero_init_residual = False,groups = 1,
        width_per_group = 64,replace_stride_with_dilation = None,):
        super(ResNet,self).__init__()
        norm_layer = nn.BatchNorm2d
        self._norm_layer = norm_layer
        self.num=num_classes
        self.inplanes = 64
        self.dilation = 1

        self.groups = groups
        self.base_width = width_per_group
        self.conv1 = nn.Conv2d(in_channels, self.inplanes, kernel_size=(7,7), stride=(2,2), padding=3, bias=False)
        self.bn1 = norm_layer(self.inplanes)
        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, dilate=False)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=False)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=False)
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * block.expansion, self.num)

        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) and m.bn3.weight is not None:
                    nn.init.constant_(m.bn3.weight, 0)  # type: ignore[arg-type]
                elif isinstance(m, BasicBlock) and m.bn2.weight is not None:
                    nn.init.constant_(m.bn2.weight, 0)  # type: ignore[arg-type]

    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(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 resnet(in_channels, num_classes, mode='resnet50', pretrained=False):
    if mode == "resnet18" or mode == "resnet34":
        block = BasicBlock
    else:
        block = Bottleneck
    model = ResNet(block, cfgs[mode], in_channels=in_channels, num_classes=num_classes)
    if pretrained:
        state_dict = load_state_dict_from_url(model_urls[mode], model_dir='./model', progress=True)  # 预训练模型地址
        if num_classes != 1000:
            num_new_classes = num_classes
            fc_weight = state_dict['fc.weight']
            fc_bias = state_dict['fc.bias']
            fc_weight_new = fc_weight[:num_new_classes, :]
            fc_bias_new = fc_bias[:num_new_classes]
            state_dict['fc.weight'] = fc_weight_new
            state_dict['fc.bias'] = fc_bias_new
        model.load_state_dict(state_dict)
    return model

这种写法是按照先前VGG那样写的,这样有助于使用同一个model_urls和cfgs。

BasicBlock类中的init()函数是先定义网络架构,forward()的函数是前向传播,实现的功能就是残差块:

Bottleneck类是另一种blcok类型,同上,init()函数是预定义网络架构,forward函数是进行前向传播。该block中有三个卷积,分别是1x1,3x3,1x1,分别完成的功能就是维度压缩,卷积,恢复维度,所以bottleneck实现的功能就是对通道数进行压缩,再放大。

注意:这里的plane不再是输出的通道数,输出通道数应该就是plane*expansion,即4*plane。

在ss

  • resnet18: BasicBlock, [2, 2, 2, 2]
  • resnet34: BasicBlock, [3, 4, 6, 3]
  • resnet50: Bottleneck, [3, 4, 6, 3]
  • resnet101:  Bottleneck, [3, 4, 23, 3]
  • resnet152:  Bottleneck, [3, 8, 36, 3]

这个后面的结构是作者自己挑的一个参数,所以不用管它为什么。BasicBlock主要用于resnet18和34,Bottleneck用于resnet50,101和152。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/922628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货分享:可证明安全的隐私计算

“隐语”是开源的可信隐私计算框架,内置 MPC、TEE、同态等多种密态计算虚拟设备供灵活选择,提供丰富的联邦学习算法和差分隐私机制 开源项目:github.com/secretflowgitee.com/secretflow 以下文章来源于DataFunTalk ,作者洪澄 D…

【实训项目】易行APP设计(c2c二手交易平台)

1.设计摘要 1.1市场背景 随着经济的迅速发展与科技日新月异的进步,家庭内的各项物品更新换代频率越来越快,人们购买新商品后越来越多旧的商品积压需要处理;在互联网电商的各种营销刺激下,消费者非常容易形成“冲动”消费&#x…

【Spring】一次性打包学透 Spring | 阿Q送书第五期

文章目录 如何竭尽可能确保大家学透Spring1. 内容全面且细致2. 主题实用且本土化3. 案例系统且完善4. 知识有趣且深刻 关于作者丁雪丰业内专家推图书热卖留言提前获赠书 不知从何时开始,Spring 这个词开始频繁地出现在 Java 服务端开发者的日常工作中,很…

leetcode:只出现一次的数字Ⅲ(详解)

题目: 给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。 你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。 示例 1&…

没消费?复购难?不如试试即拼七人拼团模式

在当今的社会环境下,快消品、大健康、美容等行业一直是人们生活中不可或缺的一部分。它们各具特色,不断满足大众的需求,因此受到广泛欢迎。但由于市场品种繁多、竞争激烈,消费者的选择也变得更加多样化。为了提高各行业的竞争性&a…

EPS导出为带cass属性的dwg文件

1、在EPS软件中打开一幅画好的地形图,如下: 2、点击菜单栏里面的工具--运行脚本--数据转换--cass10.具体操作如下: 3、设置输出模式,具体如下图。 4、输出结果如下,在cass中打开输出的dwg文件,结果如下&…

多核异构核间通信Mailbox vs rpmsg

目录 一、关键术语解释 二、Mailbox与rpmsg对比 三、rpmsg传输流程 异构核间数据通过共享内存实现数据传递,通过中断来触发发送、接收。 一、关键术语解释 IPC Inter-Processor Communication MailBox IP which provides queued interrupt mechanism for comm…

压力传感器模拟信号(频率)转数字信号的问题

压力传感器模拟信号(频率)转数字信号的问题 三河凡科科技飞讯教学篇:压力传感器模拟信号(频率)转数字信号是现代控制系统中十分重要的一个问题。在许多工业应用中,压力传感器模拟信号需要被准确地转换成数…

MyBatis进阶:告别SQL注入!MyBatis分页与特殊字符的正确使用方式

目录 引言 一、使用正确的方式实现分页 1.1.什么是分页 1.2.MyBatis中的分页实现方式 1.3.避免SQL注入的技巧 二、特殊字符的正确使用方式 2.1.什么是特殊字符 2.2.特殊字符在SQL查询中的作用 2.3.如何避免特殊字符引起的问题 2.3.1.使用CDATA区段 2.3.2.使用实体引…

微服务中间件--MQ服务异步通信

MQ服务异步通信 MQ服务异步通信a.消息可靠性1) 生产者消息确认2) 消息持久化3) 消费者消息确认4) 消费者失败重试4.a) 本地重试4.b) 失败策略 b.死信交换机1) 初识死信交换机2) TTL3) 延迟队列a) 安装延迟队列插件b) SpringAMQP使用延迟队列插件 c.惰性队列1) 消息堆积问题2) 惰…

redis实战-缓存三剑客穿透击穿雪崩解决方案

缓存穿透 定义 缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库,造成数据库压力,也让缓存没有发挥出应有的作用 解决方案 缓存空对象 当我们客户端…

《Go 语言第一课》课程学习笔记(十一)

控制结构 if 的“快乐路径”原则 针对程序的分支结构,Go 提供了 if 和 switch-case 两种语句形式;而针对循环结构,Go 只保留了 for 这一种循环语句形式。 if 语句 if 语句是 Go 语言中提供的一种分支控制结构,它也是 Go 中最常…

人机对抗智能-部分可观测异步智能体协同(POAC)

环境链接:数据中心-人机对抗智能 (ia.ac.cn)http://turingai.ia.ac.cn/data_center/show/10 1.环境配置 Ubuntu 20.04 Anaconda python版本3.6 1.1 安装torch0.4.1失败 参考文章: 安装torch0.4.1的神坑_torch0.4.1_DEMO_Tian的博客-CSDN博客 co…

外卖点餐系统开发定制:数字化餐饮体验的新里程

在现代社会,外卖已经成为了人们日常生活的一部分。为了更好地满足消费者的需求,外卖点餐系统开发定制成为了餐饮业的一个重要方向。通过数字化技术,商家能够为消费者提供更加个性化、便捷的订餐体验。本文将深入探讨外卖点餐系统开发定制&…

《中国区块链发展报告(2023)》发布 和数集团推动区块链发展

北京区块链技术应用协会与社会科学文献出版社日前在京共同发布《区块链蓝皮书:中国区块链发展报告(2023)》。蓝皮书归纳梳理了2022年区块链产业发展现状及趋势,并结合行业热点Web3.0、AIGC,探讨我国区块链发展的热点话…

Vue脚手架安装(全网最详细)

目录 1、环境准备 1.1 安装node 1.1.2 判断你是否安装成功 1.1.3 在命令提示符中查看node版本 1.2 安装webpack 1.3 安装vue-cli3.x以上 2、创建工程 2.1 创建 2.2 选择 2.2.1 选择自定义设置: 2.2.2 选择Vue版本: 2.2.3 是否使用历史模式选择…

应用在红外遥控领域中的心率传感信号接收芯片

远程遥控技术又称为遥控技术,是指实现对被控目标的遥远控制,在工业控制、航空航天、家电领域应用广泛。红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实…

力扣HOT100.4,两个正序数组的中位数,拓展寻找第K小

题目:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 输入:nums1 [1,3], nums2 [2] 输出:2.00000 解释:合并数组 [1,2,3] ,中位…

不会编写正则表达式?试试“biu正则”,体验点一下就给你想要的正则表达式!

biu正则介绍 “biu正则”是一款非常实用的正则表达式生成工具。它的主要功能是帮助用户快速生成各种正则表达式,从而减少编写正则表达式的时间。比如,如果您需要编写一个匹配邮箱的正则表达式,只需要输入一个邮箱地址,点击“Clik…

横扫“盲区”、“看透”缺陷,维视智造推出短波红外相机

在可见光领域,工业相机的视觉应用已经十分成熟,但在日常的客户咨询中,我们也经常接到一些“超纲需求”——客户想要检测“白底上的白色缺陷”、“不透明包装内的透明物体有无”等,均属于可见光无法实现的检测,而市面上…