CNN——ResNet

news2024/11/15 23:57:23

         深度残差网络(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'会自动使用最新最好的。

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

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

相关文章

Mac 16g约等于Windows多少g?

Mac 16g 内存等于 Windows 320g 内存 何为“黄金内存”&#xff1f; Mac 的内存是用黄金做的&#xff0c;而 Windows 的内存是用铁做的。 黄金的密度是 19.32 g/cm&#xff0c;而铁的密度是 7.874 g/cm。 因此&#xff0c;16g 的黄金体积是 0.082 cm&#xff0c;而 16g 的铁…

交换机_05VLAN

一、VLAN技术的引入 VLAN&#xff08;Virtual Lan&#xff09;主要应用在交换机上 一台交换机默认情况下连接一个广播域&#xff0c;因为默认情况下所有的接口都是属于同一个vlan的&#xff0c;默认vlan1&#xff0c;所以是在同一个广播域中。 结合交换机工作原理&#xff0…

Python 利用PYQT5设计基于RSA算法盲签名的匿名化电子支付系统设计与实现

基于RSA算法的盲签名算法 David Chaum 于1982年提出盲签名的概念&#xff0c;并利用RSA算法设计了第一个盲签名方案. 该方案的安全性基于大整数分解问题 盲签名的步骤 1.密钥生成 签名者执行以下步骤生成密钥对: ①签名者选择两个大素数p,q&#xff0c; 计算npq&#xff0…

OpenCV-18图像的翻转和旋转

一、图像的翻转 使用API---cv.flip&#xff08;src, flipCode&#xff09; flipCode 0表示上下翻转 flipCode > 0表示左右翻转 flipCode < 0上下 左右翻转 或者使用np的翻转src[: : -1,: : -1]实现上下翻转。 示例代码如下&#xff1a; import cv2 import numpy…

GPT(Generative Pre-Training)论文解读及源码实现(二)

本篇为gpt2的pytorch实现&#xff0c;参考 nanoGPT nanoGPT如何使用见后面第5节 1 数据准备及预处理 data/shakespeare/prepare.py 文件源码分析 1.1 数据划分 下载数据后90%作为训练集&#xff0c;10%作为验证集 with open(input_file_path, r) as f:data f.read() n …

yolo 分割label格式标注信息图片显示可视化查看

参考: https://github.com/ultralytics/ultralytics/issues/3137 https://blog.csdn.net/weixin_42357472/article/details/135218349?spm=1001.2014.3001.5501 需要把坐标信息在图片上显示 代码 1)只画出了坐标边缘 import cv2 import numpy as np from random impor…

html 原生网页使用ElementPlus 日期控件el-date-picker换成中文

项目&#xff1a; 原生的html,加jQuery使用不习惯&#xff0c;新html页面导入vue3,element plus做界面&#xff0c;现在需要把日历上英文切成中文。 最终效果&#xff1a; 导入能让element plus日历变成中文脚本&#xff1a; elementplus, vue3对应的js都可以通过创建一个vu…

idea 以文本形式输出 SpringBoot项目 目录结构

第1步&#xff1a;AltF12 打开 Terminal 终端 第2步&#xff1a;cd 到 项目路径下 第3步&#xff1a;使用 tree 命令 结果 D:. ├─.mvn │ └─wrapper ├─applog │ └─logs ├─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zhangziwa …

【软件测试】学习笔记-如何做好单元测试

什么是单元测试&#xff1f; 在正式开始今天的话题之前&#xff0c;我先给你分享一个工厂生产电视机的例子。 工厂首先会将各种电子元器件按照图纸组装在一起构成各个功能电路板&#xff0c;比如供电板、音视频解码板、射频接收板等&#xff0c;然后再将这些电路板组装起来构…

【计算机网络】网络编程套接字socket--UDP/TCP简单服务器实现/TCP协议通信流程

文章目录 一、预备知识1.IP和端口号2.TCP协议和UDP协议3.网络字节序 二、socket编程接口1.socket 常见API2.sockaddr结构 三、UDP服务器相关重要接口介绍sendtorecvfrompopen 1.udpServer.hpp2.udpServer.cc3.udpClient.hpp4.udpClient.cc5.onlineUser.hpp 四、TCP服务器socket…

高性能、可扩展、分布式对象存储系统MinIO的介绍、部署步骤以及代码示例

详细介绍 MinIO 是一款流行的开源对象存储系统&#xff0c;设计上兼容 Amazon S3 API&#xff0c;主要用于私有云和边缘计算场景。它提供了高性能、高可用性以及易于管理的对象存储服务。以下是 MinIO 的详细介绍及优缺点&#xff1a; 架构与特性&#xff1a; 开源与跨平台&am…

HTML---JavaScript操作DOM对象

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 本章目标 了解DOM的分类和节点间的关系熟练使用JavaScript操作DOM节点 访问DOM节点 能够熟练的进行节点的创建、添加、删除、替换等 能够熟练的设置元素的样式 能够灵活运用JavaScript获取元素…

SpringBoot学习(八)-SpringBoot + Dubbo + zookeeper

分布式DubboZookeeper 1、分布式理论 1&#xff09;什么是分布式系统&#xff1f; 在《分布式系统原理与范型》一书中有如下定义&#xff1a;“分布式系统是若干独立计算机的集合&#xff0c;这些计算机对于用户来说就像单个相关系统”&#xff1b; 分布式系统是由一组通过…

【AI视野·今日CV 计算机视觉论文速览 第284期】Fri, 5 Jan 2024

AI视野今日CS.CV 计算机视觉论文速览 Fri, 5 Jan 2024 Totally 62 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Learning to Prompt with Text Only Supervision for Vision-Language Models Authors Muhammad Uzair Khattak, Muhammad F…

2024.1.7-实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功)

实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功) 目录 最终效果 原文链接 https://onedayxyy.cn/docs/prometheus-grafana-ecs 参考模板 https://i4t.com/ https://grafana.frps.cn &#x1f530; 额&#xff0c;注意哦: 他这个是通过frp来…

【Flutter 开发实战】Dart 基础篇:从了解背景开始

想要学会用 Flutter 开发 App&#xff0c;就不可避免的要学习另一门很有意思的编程语言 —— Dart。很多小伙伴可能在学习 Flutter 之前可能都没听说过这门编程语言&#xff0c;我也是一样&#xff0c;还以为 Dart 是为了 Flutter 而诞生的&#xff1b;然而&#xff0c;当我们去…

网页设计与制作web前端设计html+css+js成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站(HTML静态网页项目实战)附源码】

网页设计与制作web前端设计htmlcssjs成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站&#xff08;HTML静态网页项目实战&#xff09;附源码】 https://www.bilibili.com/video/BV1Hp4y1o7RY/?share_sourcecopy_web&vd_sourced43766e8ddfffd1f1a1165a3e72d7605

分布式系统架构设计之分布式消息队列基础知识

随着微服务、大数据和云计算的普及&#xff0c;分布式系统已经成为现代软件架构的核心。在分布式系统中&#xff0c;各个组件间的通信和数据交换尤其重要&#xff0c;而消息队列正是实现这一目标的关键技术之一。 在分布式架构设计过程中&#xff0c;架构师们需要对消息队列有…

爬虫瑞数4案例:网上房地产

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、瑞数简介 瑞数动态安全 Botgate&#xff08;机器人防火墙&#xff09;以“动态安全”技术为核心&#xff0c;通过动态封装…

深度解析基于模糊数学的C均值聚类算法

深度解析基于模糊数学的C均值聚类算法 模糊C均值聚类 (FCM)聚类步骤&#xff1a;FCM Python代码&#xff1a; 模糊C均值聚类 (FCM) 在数据挖掘和聚类分析领域&#xff0c;C均值聚类是一种广泛应用的方法。模糊C均值聚类&#xff08;FCM&#xff09;是C均值聚类的自然升级版。相…