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 = 1def __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 = 4def __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 = xout = 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 = 64self.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.expansionfor _ 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 = 1def __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 = 16self.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.expansionreturn 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.mzph.cn/news/605118.shtml

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

相关文章

Vue3+Pinia实现持久化动态主题切换

PC端主题切换大家都用过&#xff0c;下面用Vue3Pinia实现一下这个过程; 【源码地址】 1、准备工作 npm install pinia npm install pinia-plugin-persist2、基础配置 // main.js import { createApp } from vue import App from ./App.vue import bootstrap from "../bo…

红日靶场 4

靶场配置 ​ 733 x 668899 x 819 ​ ​ 733 x 6161466 x 1232 ​ ​ 733 x 6261449 x 1237 ​ ​ 733 x 6301450 x 1247 ​ IP 地址分配&#xff1a; Win7: 192.168.183.133(内网)Ubuntu: 192.168.183.134(内网) 192.168.120.137(外网)DC: 192.168.183.130(内网)Kali…

Python综合数据分析_根据订单求RFM值

文章目录 0.导入数据1.数据可视化2.数据清洗3.特征工程4.构建User用户表5.求R值6.求F值7.求M值 0.导入数据 import pandas as pd #导入Pandas df_sales pd.read_csv(订单.csv) #载入数据 df_sales.head() #显示头几行数据 1.数据可视化 import matplotlib.pyplot as plt #导…

js逆向第13例:猿人学第6题js混淆-回溯赛

文章目录 m是加密字符串怎么来的?浏览器环境检测本地运行的js代码任务六:采集全部5页的彩票数据,计算全部中奖的总金额(包含一、二、三等奖) 此题总体难度低于第5题,老规矩还是查看控制台请求地址https://match.yuanrenxue.cn/api/match/6?m=rPRDgpbV3Wd%252FyPfURQAkxK…

使用Vite创建vue3工程

介绍 使用Vite构建工具&#xff0c;创建Vue3工程 示例 第一步&#xff1a;执行创建项目的命令&#xff0c;study-front-vue3是项目名称 npm init vite-app study-front-vue3第二步&#xff1a;进入项目文件夹&#xff0c;执行命令&#xff0c;安装模块 cd study-front-vue…

使用qtquick调用python程序,pytorch

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…

程序员为什么不能一次把功能写好,是因为他不想吗

引言 交流一下为什么他做的功能这么多Bug 大家好&#xff0c;最近看到一个有趣的问题&#xff1a; 程序员为什么要不能一次性写好&#xff0c;需要一直改Bug&#xff1f; 在我看来&#xff0c;程序员也是人&#xff0c;并非机器。 拿这个问题去质问程序员&#xff0c;答案无…

Java学习,一文掌握Java之SpringBoot框架学习文集(3)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

【网络】网络层协议ARP和IP协议转发流程

目录 一、IP概述 1.1 IP简介 1.2 IP协议 二、IP地址与硬件地址 三、地址解析协议ARP 3.1 ARP协议简介 3.2 ARP工作流程 3.3 ARP的四种典型情况 四、IP协议的转发流 一、IP概述 1.1 IP简介 IP地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地址…

[VUE]1-创建vue工程

目录 基于脚手架创建前端工程 1、环境要求 2、操作过程 3、工程结构 4、启动前端服务 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c…

如何写html邮件 —— 参考主流outook、gmail、qq邮箱渲染邮件过程

文章目录 ⭐前言⭐outlook渲染邮件⭐gmail邮箱渲染邮件⭐qq邮箱渲染邮件 ⭐编写html邮件&#x1f496;table表格的属性&#x1f496;文本&#x1f496;图片&#x1f496;按钮&#x1f496;背景图片 ⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于 …

云卷云舒:【实战篇】对象存储迁移

云卷云舒&#xff1a;【实战篇】MySQL迁移-CSDN博客 1. 简介 对象存储与块存储、文件存储并列为云计算三大存储模型。提供海量存储空间服务&#xff0c;具备快速的数据存取性能、高可靠和数据安全性&#xff0c;通过标准的RESTful API接口和丰富的SDK包来提供服务&#xff0c…

3D模型UV展开原理

今年早些时候&#xff0c;我为 MAKE 杂志写了一篇教程&#xff0c;介绍如何制作视频游戏角色的毛绒动物。 该技术采用给定的角色 3D 模型及其纹理&#xff0c;并以编程方式生成缝纫图案。 虽然我已经编写了一般摘要并将源代码上传到 GitHub&#xff0c;但我在这里编写了对使这一…

Presto CLI学习

1. 序言 作为Presto的客户端之一&#xff0c;Presto CLI是一个基于终端的交互式shell&#xff0c;对应presto源码中的presto-cli模块 Presto CLI的本质是一个self-executing jar —— presto-cli-version-executable.jar&#xff0c;就像一个普通的UNIX可执行文件 因此&#…

olap/spark-tungsten:codegen

15721这一章没什么好说的&#xff0c;不再贴课程内容了。codegen和simd在工业界一般只会选一种实现。比如phothon之前用codegen&#xff0c;然后改成了向量化引擎。一般gen的都是weld IR/LLVM IR/当前语言&#xff0c;gen成C的也要检查是不是有本地预编译版本&#xff0c;要不没…

城市建设模拟游戏:鼠托邦 RATOPIA 中文免安装版

《鼠托邦》是一款由独立游戏开发团队Cassel Games开发的基地建设模拟游戏。在游戏中&#xff0c;玩家需要管理一个庞大的地下鼠国&#xff0c;打造理想中的“鼠托邦”。玩家可以化身为糖果派对游戏中的老鼠女王&#xff0c;带领老鼠民众建设城市、勘探地下领域以扩展生存空间。…

Web前端-JavaScript(ES6)

文章目录 1.ES5数组新方法1.1 数组方法forEach遍历数组1.2 数组方法filter过滤数组1.3 数组方法some1.4 some和forEach和filter的区别1.5 find()1.6 findIndex()1.7 trim去除字符串两端的空格1.8 获取对象的属性名1.9 Object.defineProperty 2.ES6语法2.1 ES6概述2.2 为什么使用…

CTF数据分析题详解

目录 题目一(1.pcap) 题目二(2.pcap) 题目三(3.pcap) 题目四(4.pcap) CTF流量分析经典例题详解-CSDN博客 本文章涉及的所有题目附件下载地址&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/18mWo5vn1zp_XbmcQrMOKRA 提取码&#xff1a;hrc4 声明&#xff1a;这里…

WorkPlus AI助理为企业提供智能客服的机器人解决方案

在数字化时代&#xff0c;企业面临着客户服务的重要挑战。AI客服机器人成为了提升客户体验和提高工作效率的关键工具。作为一款优秀的AI助理&#xff0c;WorkPlus AI助理以其智能化的特点和卓越的功能&#xff0c;为企业提供了全新的客服机器人解决方案。 为什么选择WorkPlus A…

Python机器学习入门必学必会:机器学习与Python基础

1.机器学习常见的基础概念 根据输入数据是否具有“响应变量”信息&#xff0c;机器学习被分为“监督式学习”和“非监督式学习”。“监督式学习”即输入数据中即有X变量&#xff0c;也有y变量&#xff0c;特色在于使用“特征&#xff08;X变量&#xff09;”来预测“响应变量&…