CNN——ResNet

         深度残差网络(Deep residual network, ResNet)的提出是CNN图像史上的一件里程碑事件,并且让深度学习真正可以继续做下去,斩获2016 CVPR Best Paper。此外ResNet的作者都是中国人,一作何恺明。ResNet被提出以后很多的网络都使用或借鉴了这个结构。该论文的被引用量更是突破了10w+。

论文地址:[1512.03385] Deep Residual Learning for Image Recognition (arxiv.org)

        在2015的5个竞赛中都获得了第一名而且远远甩开第二名。 

1. ResNet概述        

1.1 研究背景

        无论是VGG还是GoogleNet都表明增加网络的深度的重要性,网络越深可以提取到越高级的特征。

        那么是否意味着直接简单的将网络堆深是不是就可以了?

        增加网络深度首先会带来的问题就是梯度消失或者梯度爆炸导致难以收敛,但这个问题已经可以通过合适的权重初始化手段,Xavier初始化,MSRA 初始化还有Batch Normalization解决。

        另一个问题便是网络退化现象,这既不是梯度消失或者梯度爆炸导致也不是过拟合导致的,如下图所示,56层网络在训练集和测试集上的误差都比20层网络大。并且他们经过实验发现即使更深的网络使用3倍的迭代次数退化现象仍存在。

                        

1.2 残差学习

        于是论文提出了一个残差学习框架用来训练那些非常深的神经网络,重新定义了网络的学习方式,让网络可以直接学习输入信息与输出信息的差异(即残差),然后将浅层的特征与深层的特征直接相加进行融合,即使残差为0,通过一个Shortcut connection也可以实现identity mapping恒等映射,通俗理解就是即使什么都没学到,也不会比原来更差。经过这个结构Shortcut connection既没有引入额外的参数,也没有增加计算的复杂度(相加的复杂度很低,微不足道)。

残差网络

  • 易于优化收敛
  • 解决退化问题
  • 让网络可以更深,准确率大大提升 

       通过这个结构,论文中提出了ResNet合集,随着深度增加网络性能还在增加,在高达152层时,网络仍然有着较低的复杂度(比VGG16还低),并且在 ImageNet的测试集上进行评测,达到了3.57%的错误率,这一结果赢得 ILSVRC 2015 分类比赛的第一名,超过了人类的水平。最后甚至还实验了超过1000层的网络。

1.3 Resnet机理

        resnet有效的真实原因还有待研究,原论文也只是做出了猜测,并没有很严格的理论证明。后续也有非常多的人做出了不一样的或者进一步的论证

1.恒等映射这一路梯度是1,可以防止梯度消失。虽然网络退化和梯度消失没什么关系,但Resnet确实可以防止梯度消失,加快收敛速度

2.集成学习。ResNet相当于几个网络的集成。

3.神经网络难以拟合恒等映射

        纵观深度神经网络的发展,为了让网络能力越来越强,在神经网络引入了很多非线性。这也使得特征随着层层前向传播得到完整保留(什么也不做)的可能性都微乎其微,有的时候 “什么都不做”反而是最好的,但是“什么都不做”(恒等映射)恰好是当前神经网络最难做到的东西之一。可以认为Residual Learning的初衷,其实是让模型的内部结构至少有恒等映射的能力。以保证在堆叠网络的过程中,网络至少不会因为继续堆叠而产生退化。

4.

还有一些别的说法

        众所周知,深度学习和炼丹一样,ResNet有效因为因为实验结果就是好。

1.4 结论

1.残差网络可以解决网络退化问题,可以重新让网络变得更深,性能越好

2.残差网络收敛速度更快

2. 网络结构详解 

1.ResNet18,34,50,101,152

        网络结构类似于VGG,除第一个卷积层外,全部采用3×3卷积,padding=1。除第一次下采样外,下采样通过步长为2的卷积来实现。此外还在卷积后激活前使用了BN。

        其中,根据Block类型,可以将这五种ResNet分为两类:(1) 一种基于BasicBlock,浅层网络ResNet18, 34都由BasicBlock搭成;(2) 另一种基于Bottleneck,深层网络ResNet50, 101, 152乃至更深的网络,都由Bottleneck搭成。Block相当于积木,每个layer都由若干Block搭建而成,再由4层layer组成整个网络

        BasicBlock包括两个3×3卷积(如下图),除了conv2_x通过最大池化降采样外,每一层第一个BasicBlock的第一次卷积步长为2进行下采样。

                                                        

        对于下采样的残差连接,会出现两者尺寸和通道数不一样的问题,无法直接相加,论文中给出了两种方案,(A)浅层使用1×1卷积核步长为2同时不改变通道数,仍然使用恒等映射直接相加,但是对于新增加的维度则全部使用0来代替。这种方案不增加任何的参数。(B)还是使用1x1的卷积步长为2,但是输出的通道数设置需要和下采样后的通道数匹配(如下图)。

                                        

        论文中对比了两种方案的效果,还加了一种C方案:无论是否降采样都使用1×1卷积后再连接,实际中不会用C方案,因为他增加了很多参数。由于参数量A<B<C,所以误差也会有一点点不同。A方案已经可以解决退化的问题且不会增加额外的参数,但通过0填充的维度确实没有进行残差学习。一般采用B方案

                        ​​​​​​​

        为了减低深层次网络参数量和计算量,深层次网络采用了Bottleneck先通过1×1卷积降维四倍(减少通道数)再使用3×3卷积通道数不变,最后通过1×1卷积升回维度。

        同样的除了conv2_x通过最大池化降采样外,每一层第一个Bottleneck进行下采样,1×1卷积降维两倍,3×3卷积步长为2进行下采样,1×1卷积升高4倍维度。

  

        最后使用全局平均池化,连接一个输出为1000的全连接层,使用Softmax完成分类。

2.ResNet20,32,44,56,110

        论文在CIFAR-10数据集也进行了实验。针对32×32的小尺寸这个网络变种只有三个阶段。第一个7×7卷积变成3×3,步长也为1,没了最大池化层。

        设置卷积层个数n={3,5,7,9,18,200},可以得到ResNet20,32,44,56,110。此外设置n=200,得到了1202层。在训练这个1000多层的网络的时候没有遇到训练退化的问题,并且他的训练误差小于0.1%。尽管1202层网络的训练误差和110层网络是相似的,但是它的测试结果却要差于110层的网络。论文猜测可能是由于过拟合造成的。因为对于这样的一个小数据集,也许并不需要一个有着1202层网络进行训练。

3. ResNet在Pytorch实现

1.手动实现ResNet

1.ResNet18,34,50,101,152

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

# 定义基本块 BasicBlock
class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_channels, out_channels, stride=1):
        super(BasicBlock, self).__init__()
        # 第一个卷积层,3x3卷积核,stride用于控制步幅
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)  # 批归一化
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)

        self.shortcut = nn.Sequential()
        # 如果步幅不为1或输入通道数不等于输出通道数*expansion,使用额外的卷积层来匹配维度
        if stride != 1 or in_channels != self.expansion * out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(self.expansion * out_channels)
            )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)  # 将残差连接到输出
        out = F.relu(out)
        return out

# 定义瓶颈结构 Bottleneck
class Bottleneck(nn.Module):
    expansion = 4

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

        self.shortcut = nn.Sequential()
        # 如果步幅不为1或输入通道数不等于输出通道数*expansion,使用额外的卷积层来匹配维度
        if stride != 1 or in_channels != out_channels * self.expansion:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels * self.expansion)
            )

    def forward(self, x):
        residual = x

        out = self.relu(self.bn1(self.conv1(x)))
        out = self.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))

        residual = self.shortcut(residual)  # 匹配维度

        out += residual  # 将残差连接到输出
        out = self.relu(out)
        return out

# 定义ResNet模型
class ResNet(nn.Module):
    def __init__(self, block, layers, num_classes=1000):
        super(ResNet, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0], stride=1)
        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))
        self.fc = nn.Linear(512 * block.expansion, num_classes)

    def _make_layer(self, block, out_channels, blocks, stride=1):
        layers = []
        layers.append(block(self.in_channels, out_channels, stride))
        self.in_channels = out_channels * block.expansion
        for _ in range(1, blocks):
            layers.append(block(self.in_channels, out_channels))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(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

# 不同深度的ResNet模型的创建函数
def ResNet18():
    return ResNet(BasicBlock, [2, 2, 2, 2])

def ResNet34():
    return ResNet(BasicBlock, [3, 4, 6, 3])

def ResNet50():
    return ResNet(Bottleneck, [3, 4, 6, 3])

def ResNet101():
    return ResNet(Bottleneck, [3, 4, 23, 3])

def ResNet152():
    return ResNet(Bottleneck, [3, 8, 36, 3])

2.ResNet20,32,44,56,110

# 定义基本块 BasicBlock
class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_channels, out_channels, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != self.expansion * out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(self.expansion * out_channels)
            )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

# 定义ResNet模型
class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=1000):
        super(ResNet, self).__init__()
        self.in_channels = 16
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(64 * block.expansion, num_classes)

    def _make_layer(self, block, out_channels, num_blocks, stride):
        strides = [stride] + [1] * (num_blocks - 1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels * block.expansion
        return nn.Sequential(*layers)

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

# 创建不同深度的ResNet模型的函数
def ResNet20():
    return ResNet(BasicBlock, [3, 3, 3])

def ResNet32():
    return ResNet(BasicBlock, [5, 5, 5])

def ResNet44():
    return ResNet(BasicBlock, [7, 7, 7])

def ResNet56():
    return ResNet(BasicBlock, [9, 9, 9])

def ResNet110():
    return ResNet(BasicBlock, [18, 18, 18])

2.使用Pytorch官方实现 

ResNet — Torchvision 0.16 documentation (pytorch.org)ResNet — Torchvision 0.16 documentation (pytorch.org)


model = resnet18(weights='DEFAULT') # model = resnet18(weights='DEFAULT')
model = resnet34() # model = resnet34(weights='DEFAULT')
model = resnet50() # model = resnet50(weights='DEFAULT')
model = resnet101() # model = resnet101(weights='DEFAULT')
model = resnet152() #  model = resnet152(weights='DEFAULT')

        50层以上有两个版本的预训练权重,V2基于新的训练技巧。当然weights='DEFAULT'会自动使用最新最好的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

e2studio开发LPS28DFW气压计(1)----轮询获取气压计数据

e2studio开发LPS28DFW气压计.1--轮询获取气压计数据 概述视频教学样品申请完整代码下载产品特性通信模式速率新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user…

单调栈与单调队列

目录 单调栈 单调队列 单调栈 题目如下&#xff1a; 如何维护一个单调栈&#xff1f; 单调递增栈&#xff1a;在保持栈内元素单调递增的前提下&#xff08;如果栈顶元素大于要入栈的元素&#xff0c;将将其弹出&#xff09;&#xff0c;将新元素入栈。单调递减栈&#xff1…

Leetcode2487. 从链表中移除节点

Every day a Leetcode 题目来源&#xff1a;2487. 从链表中移除节点 解法1&#xff1a;单调栈 遍历链表&#xff0c;建立一个单调栈&#xff08;单调递减&#xff09;。 再用头插法将单调栈的元素建立一个新的链表&#xff0c;返回该链表的头结点。 代码&#xff1a; /*…

湖南大学-计算机网路-2023期末考试【部分原题回忆】

前言 计算机网络第一门考&#xff0c;而且没考好&#xff0c;回忆起来的原题不多。 这门学科学的最认真&#xff0c;复习的最久&#xff0c;考的最差。 教材使用这本书&#xff1a; 简答题&#xff08;6*530分&#xff09; MTU和MSS分别是什么&#xff0c;联系是什么&#x…

深入了解static关键字的作用和应用--java面向对象学习

Static修饰成员变量 Static是什么 叫静态&#xff0c;可以修饰成员变量&#xff0c;成员方法 成员变量按有无static修饰分俩种&#xff1a; 类变量&#xff1a;有static修饰&#xff0c;属于类&#xff0c;在计算机里只有一份&#xff0c;会被类的全部对…

VMware NAT 模式,网关无法ping通 网关解决办法

开启红框服务即可。。 参考&#xff1a;VMware NAT 模式&#xff0c;网关无法ping通 网关解决办法_vmware设置net,本机ping不通网关-CSDN博客

StarRocks 在小红书自助分析场景的应用与实践

作者&#xff1a;小红书 OLAP 研发负责人 王成 近两年 StarRocks 一直是小红书 OLAP 引擎体系里非常重要的部分&#xff0c;过去一年&#xff0c;小红书的 StarRocks 使用规模呈现出翻倍的增长速度&#xff0c;目前整体规模已经达到 30 个集群&#xff0c;CPU 规模已经达到了 3…

传感数据分析——高通滤波与低通滤波

传感数据分析——高通滤波与低通滤波 文章目录 传感数据分析——高通滤波与低通滤波前言一、运行环境二、Python实现总结 前言 对于传感信号而言&#xff0c;我们可以提取其中的高频信息和低频信息&#xff0c;低频信息往往是信号的趋势&#xff0c;高频信息往往是一些突变或异…

vue3+echarts应用——深度遍历html的dom结构并用树图进行可视化

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐html数据解析&#x1f496; html字符串转为html对象&#x1f496; 深度遍历html对象内容 ⭐echarts 树图的渲染&#x1f496; 处理html内容为树状结构&#x1f496; 渲染树状图&#x1f496; inscode代码块 ⭐总结⭐结束 ⭐前言 大…

(低级错误)IDEA/Goland报错连接数据库失败:URL错误和权限问题。

前言 做毕设ing&#xff0c;使用Goland自带的数据库工具连接服务器的数据库。报错 错误: Malformed database URL, failed to parse the main URL sections. (view)服务器是华为云&#xff0c;使用宝塔面板。数据库版本5.6.50。 排查过程 鉴于Goland报错报的狗屁不是&#…

hfish蜜罐docker部署

centos 安装 docker-CSDN博客Docker下载部署 Docker是我们推荐的部署方式之一&#xff0c;当前的版本拥有以下特性&#xff1a; 自动升级&#xff1a;每小时请求最新镜像进行升级&#xff0c;升级不会丢失数据。数据持久化&#xff1a;在宿主机/usr/share/hfish目录下建立dat…

[足式机器人]Part3 机构运动学与动力学分析与建模 Ch00-1 坐标系与概念基准

本文仅供学习使用&#xff0c;总结很多本现有讲述运动学或动力学书籍后的总结&#xff0c;从矢量的角度进行分析&#xff0c;方法比较传统&#xff0c;但更易理解&#xff0c;并且现有的看似抽象方法&#xff0c;两者本质上并无不同。 2024年底本人学位论文发表后方可摘抄 若有…

编码风格之(3)GUN软件标准风格(1)

GNU软件编码标准风格(1) Author&#xff1a;Onceday Date: 2023年12月26日 漫漫长路&#xff0c;才刚刚开始… 本文主要翻译自《GNU编码标准》(GNU Coding Standards)一文。 参考文档: Linux kernel coding style — The Linux Kernel documentationGNU Coding Standards …

prometheus 黑盒监控

黑盒监控 “白盒监控” 是需要把对应的Exporter程序安装到被监控的目标主机上&#xff0c;从而实现对主机各种资源以及状态的数据采集工作 ”黑盒监控“ 是不需要把Exporter程序部署到被监控的目标主机上&#xff0c;比如全球的网络质量的稳定性&#xff0c;通常用ping操作&am…

【网络技术】【Kali Linux】Wireshark嗅探(八)动态主机配置协议(DHCP)

一、实验目的 本次实验使用 Wireshark &#xff08;“网鲨”&#xff09;流量分析工具进行网络流量嗅探&#xff0c;旨在初步了解动态主机配置协议&#xff08;DHCP协议&#xff09;的工作原理。 二、DHCP协议概述 动态主机配置协议&#xff08; D ynamic H ost C onfigurat…

Transformer - Attention is all you need 论文阅读

虽然是跑路来NLP&#xff0c;但是还是立flag说要做个project&#xff0c;结果kaggle上的入门project给的例子用的是BERT&#xff0c;还提到这一方法属于transformer&#xff0c;所以大概率读完这一篇之后&#xff0c;会再看BERT的论文这个样子。 在李宏毅的NLP课程中多次提到了…

Latex + Overleaf 论文写作新手笔记

.tex 文件main.tex 文件 Latex 的文档层次结构不同文档类型的层次结构report 6 层结构实例article 5 层结构实例 Latex 语法图表插入与引用使用 figure 环境来插入图片使用 ref 命令来引用已有的图表格的插入与引用 代码块列表无序列表 itemize有序列表 enumerate 学位论文项目…

基于SSM的企业员工管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

实验笔记之——Linux实现COLMAP

之前博客跑instant-NGP的时候&#xff0c;除了用官方的数据集&#xff0c;用自己的数据则是通过手机采集&#xff0c;同时获得pose与image。但是这种获取的方式对于3D gaussian而言&#xff0c;并不支持对应的数据格式&#xff0c;为此采用COLMAP来根据image获取pose&#xff0…

Java网络编程之IP,端口号,通信协议(UDP,TCP)

目录 1.软件架构2.网络编程三要素3.IP1.IPV42.IPV6 4.端口号5.协议1.UDP协议1.单播2.组播3.广播 2.TCP协议1.三次握手2.四次挥手 1.软件架构 ①C/S&#xff1a;客户端/服务器 在用户本地需要下载安装客户端程序&#xff0c;在远程有一个服务器端程序。 优点&#xff1a;画面精美…