《动手学深度学习 Pytorch版》 7.6 残差网络(ResNet)

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

7.6.1 函数类

如果把模型看作一个函数,我们设计的更强大的模型则可以看作范围更大的函数。为了使函数能逐渐靠拢到最优解,应尽量使函数嵌套,以减少不必要的偏移。

如下图,更复杂的非嵌套函数不一定能保证更接近真正的函数。只有当较复杂的函数类包含较小的函数类时,我们才能确保提高它们的性能。

在这里插入图片描述

7.6.2 残差块

何恺明等人针对上述问题提出了残差网络。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。残差网络的核心思想是:每个附加层都应该更容易地包含原始函数作为其元素之一。

假设原始输入是 x x x,而希望学习的理想映射为 f ( x ) f(x) f(x),则残差块需要拟合的便是残差映射 f ( x ) − x f(x)-x f(x)x。残差映射在现实中更容易优化,也更容易捕获恒等函数的细微波动。之后再和 x x x 进行加法从而使整个模型重新变成 f ( x ) f(x) f(x),这里的加法会更有益于靠近数据端的层的训练,因为乘法中的梯度波动会极大的影响链式法则的结果,而在残差块中输入可以通过加法通路更快的前向传播。

此即为正常块和残差块的区别:

在这里插入图片描述

ResNet 沿用了 VGG 完整的卷积层设计。残差块里首先有2个有相同输出通道数的 3 × 3 3\times 3 3×3 卷积层。每个卷积层后接一个批量规范化层和 ReLU 激活函数。然后通过跨层数据通路跳过这 2 个卷积运算,将输入直接加在最后的 ReLU 激活函数前。这样的设计需要 2 个卷积层的输出与输入形状一样才能使它们可以相加。

class Residual(nn.Module):  #@savedef __init__(self, input_channels, num_channels,use_1x1conv=False, strides=1):super().__init__()self.conv1 = nn.Conv2d(input_channels, num_channels,kernel_size=3, padding=1, stride=strides)self.conv2 = nn.Conv2d(num_channels, num_channels,kernel_size=3, padding=1)if use_1x1conv:self.conv3 = nn.Conv2d(input_channels, num_channels,kernel_size=1, stride=strides)else:self.conv3 = Noneself.bn1 = nn.BatchNorm2d(num_channels)self.bn2 = nn.BatchNorm2d(num_channels)def forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = self.bn2(self.conv2(Y))if self.conv3:X = self.conv3(X)Y += Xreturn F.relu(Y)

残差块如果想改变通道数,就需要引入一个额外的 1 × 1 1\times1 1×1 卷积层来将输入变换成需要的形状后再做相加运算。上述类在 use_1x1conv=False 时,应用在 ReLU 非线性函数之前,将输入添加到输出;在当 use_1x1conv=True 时,添加通过 1 × 1 1\times1 1×1 卷积调整通道和分辨率。

在这里插入图片描述

blk = Residual(3, 3)
X = torch.rand(4, 3, 6, 6)
Y = blk(X)
Y.shape  # 输入形状和输出形状一致
torch.Size([4, 3, 6, 6])
blk = Residual(3, 6, use_1x1conv=True, strides=2)  # 增加输出通道数的同时 减半输出的高度和宽度
blk(X).shape
torch.Size([4, 6, 3, 3])

7.6.3 ResNet 模型

每个模块有 4 个卷积层(不包括恒等映射的 1 × 1 1\times 1 1×1 卷积层)。加上第一个 $ 7\times 7$ 卷积层和最后一个全连接层,共有18层。因此,这种模型通常被称为 ResNet-18。虽然 ResNet 的主体架构跟 GoogLeNet 类似,但 ResNet 架构更简单,修改也更方便。

在这里插入图片描述

ResNet 的前两层跟 GoogLeNet 一样,在输出通道数为 64、步幅为 2 的 7 × 7 7\times7 7×7 卷积层后,接步幅为 2 的 3 × 3 3\times3 3×3 最大汇聚层。不同之处在于 ResNet 每个卷积层后增加了批量规范化层。

b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

ResNet 在后面使用了 4 个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。第一个模块的通道数同输入通道数一致。由于之前已经使用了步幅为 2 的最大汇聚层,所以无须减小高和宽。之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。

def resnet_block(input_channels, num_channels, num_residuals,first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:  # 第一块特别处理blk.append(Residual(input_channels, num_channels,use_1x1conv=True, strides=2))else:blk.append(Residual(num_channels, num_channels))return blk

每个模块使用 2 个残差块,最后加入全局平均汇聚层,以及全连接层输出。

b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))
net = nn.Sequential(b1, b2, b3, b4, b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10))
X = torch.rand(size=(1, 1, 224, 224))
for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape:\t', X.shape)
Sequential output shape:	 torch.Size([1, 64, 56, 56])
Sequential output shape:	 torch.Size([1, 64, 56, 56])
Sequential output shape:	 torch.Size([1, 128, 28, 28])
Sequential output shape:	 torch.Size([1, 256, 14, 14])
Sequential output shape:	 torch.Size([1, 512, 7, 7])
AdaptiveAvgPool2d output shape:	 torch.Size([1, 512, 1, 1])
Flatten output shape:	 torch.Size([1, 512])
Linear output shape:	 torch.Size([1, 10])

7.6.4 训练模型

lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())  # 大约需要十五分钟,慎跑
loss 0.010, train acc 0.998, test acc 0.913
731.5 examples/sec on cuda:0

在这里插入图片描述

练习

(1)图 7-5 中的 Inception 块与残差块之间的主要区别是什么?在删除了 Inception 块中的一些路径之后,它们是如何相互关联的?

残差块并没有像 Inception 那样使用太多并行路径。和 Inception 的相似之处在于都使用了并联的 1 × 1 1\times 1 1×1的卷积核。


(2)参考 ResNet 论文中的表 1,以实现不同的变体。

在这里插入图片描述

b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
b2 = nn.Sequential(*resnet_block(64, 64, 3, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 4))
b4 = nn.Sequential(*resnet_block(128, 256, 6))
b5 = nn.Sequential(*resnet_block(256, 512, 3))
net34 = nn.Sequential(b1, b2, b3, b4, b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10))lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net34, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())  # 大约需要二十五分钟,慎跑
loss 0.048, train acc 0.983, test acc 0.885
429.5 examples/sec on cuda:0

在这里插入图片描述

ResNet-34 还是阶梯状下降,只不过台阶变低了。起步就不如18,最终精度也不如 ResNet-18。


(3)对于更深层的网络,ResNet 引入了“bottleneck”架构来降低模型复杂度。请尝试它。

class Residual_bottleneck(nn.Module):def __init__(self, input_channels, mid_channels, num_channels,use_1x1conv=False, strides=1):super().__init__()# 下面改成 bottleneckself.conv1 = nn.Conv2d(input_channels, mid_channels,kernel_size=1)self.conv2 = nn.Conv2d(mid_channels, mid_channels,kernel_size=3, padding=1, stride=strides)self.conv3 = nn.Conv2d(mid_channels, num_channels,kernel_size=1)if use_1x1conv:self.conv4 = nn.Conv2d(input_channels, num_channels,kernel_size=1, stride=strides)else:self.conv4 = Noneself.bn1 = nn.BatchNorm2d(mid_channels)self.bn2 = nn.BatchNorm2d(mid_channels)self.bn3 = nn.BatchNorm2d(num_channels)def forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = F.relu(self.bn2(self.conv2(Y)))Y = self.bn3(self.conv3(Y))if self.conv4:X = self.conv4(X)Y += Xreturn F.relu(Y)b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))def resnet_block_bottleneck(input_channels, mid_channels, num_channels, num_residuals,first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:  # 第一块特别处理blk.append(Residual_bottleneck(input_channels, mid_channels, num_channels,use_1x1conv=True, strides=2))else:blk.append(Residual_bottleneck(num_channels, mid_channels, num_channels))return blkb2 = nn.Sequential(*resnet_block_bottleneck(64, 16, 64, 3, first_block=True))
b3 = nn.Sequential(*resnet_block_bottleneck(64, 32, 128, 4))
b4 = nn.Sequential(*resnet_block_bottleneck(128, 64, 256, 6))
b5 = nn.Sequential(*resnet_block_bottleneck(256, 128, 512, 3))net1 = nn.Sequential(b1, b2, b3, b4, b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10))lr, num_epochs, batch_size = 0.05, 10, 64
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net1, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())  # 大约需要十五分钟,慎跑
loss 0.115, train acc 0.957, test acc 0.915
887.9 examples/sec on cuda:0

在这里插入图片描述

ResNet-50 跑不了一点,十分钟一个batch都跑不完。还是给 ResNet-34 强行换上 bottleneck 吧。

可以说提速效果显著,训练嘎嘎快,精度还反升了。


(4)在 ResNet 的后续版本中,作者将“卷积层、批量规范化层和激活层”架构更改为“批量规范化层、激活层和卷积层”架构。请尝试做这个改进。详见参考文献[57]中的图1。

class Residual_change(nn.Module):def __init__(self, input_channels, num_channels,use_1x1conv=False, strides=1):super().__init__()self.conv1 = nn.Conv2d(input_channels, num_channels,kernel_size=3, padding=1, stride=strides)self.conv2 = nn.Conv2d(num_channels, num_channels,kernel_size=3, padding=1)if use_1x1conv:self.conv3 = nn.Conv2d(input_channels, num_channels,kernel_size=1, stride=strides)else:self.conv3 = Noneself.bn1 = nn.BatchNorm2d(input_channels)self.bn2 = nn.BatchNorm2d(num_channels)def forward(self, X):  # 修改顺序Y = self.conv1(F.relu(self.bn1(X)))Y = self.conv2(F.relu(self.bn2(Y)))if self.conv3:X = self.conv3(X)Y += Xreturn Yb1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))def resnet_block_change(input_channels, num_channels, num_residuals,first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(Residual_change(input_channels, num_channels,use_1x1conv=True, strides=2))else:blk.append(Residual_change(num_channels, num_channels))return blkb2 = nn.Sequential(*resnet_block_change(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block_change(64, 128, 2))
b4 = nn.Sequential(*resnet_block_change(128, 256, 2))
b5 = nn.Sequential(*resnet_block_change(256, 512, 2))net2 = nn.Sequential(b1, b2, b3, b4, b5, nn.BatchNorm2d(512), nn.ReLU(),  # 如果最后不再上个BatchNorm2d则会完全不收敛nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(), nn.Linear(512, 10))lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net2, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())  # 大约需要二十五分钟,慎跑
loss 0.039, train acc 0.988, test acc 0.905
724.1 examples/sec on cuda:0

在这里插入图片描述

精度有所下降

在这里插入图片描述


(5)为什么即使函数类是嵌套的,我们也仍然要限制增加函数的复杂度呢?

限制复杂度是永远不变的主题,复杂度高更易过拟合,可解释性成吨下降。

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

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

相关文章

web:[极客大挑战 2019]LoveSQL

题目 打开页面显示如下 查看源代码,查到一个check.php,还是get传参 尝试账号密码输入 题目名为sql,用万能密码 1or 11# 或 admin or 11 给了一段乱码,也不是flag 查看字段数 /check.php?usernameadmin order by 3%23&pass…

PDF文件超出上传大小?三分钟学会PDF压缩

PDF作为一种流行的文档格式,被广泛用于各种场合,然而有时候PDF文件的大小超出了上传限制,这时候我们就需要采取一些措施来减小PDF文件的大小,下面就给大家分享几个方法,一起来学习下吧~ 方法一:嗨格式压缩大…

Acer宏碁笔记本暗影骑士轻刃AN715-51原装出厂Windows10系统工厂模式镜像

系统自带所有驱动、NITROSENSE风扇键盘灯控制中心、Office办公软件、出厂主题壁纸、系统属性Acer宏基专属的LOGO标志、 Acer Care Center、Quick Access等预装程序 下载链接:https://pan.baidu.com/s/1FDCP5EONlk0o12CYFXbhrg?pwdvazt 所需要工具:32G…

uni-app 实现凸起的 tabbar 底部导航栏

效果图 在 pages.json 中设置隐藏自带的 tabbar 导航栏 "custom": true, // 开启自定义tabBar(不填每次原来的tabbar在重新加载时都回闪现) 新建一个 custom-tabbar.vue 自定义组件页面 custom-tabbar.vue <!-- 自定义底部导航栏 --> <template><v…

网络基础(了解网络知识的前提)

前言 在正式学习网络之前&#xff0c;我们需要了解的一些关于计算机网络的基本知识&#xff0c;本文主要阐述这些基本知识&#xff0c;带着大家一步一步迈进互联网网络的世界&#xff1b; 一、局域网与广域网的概念 在正式了解这些概念的前提是我们要搞懂网络出现的意义&#x…

Uniapp实现APP云打包

一. 基础配置 二. APP图标配置 1. 点击浏览 选取图标(注&#xff1a;图片格式为png) 2. 点击自动生成所有图标并替换 三. 点击发行 并选择云打包 四. 去开发者中心获取证书 我这里是已经获取好的&#xff0c;没有获取的话&#xff0c;按照提示获取即可&#xff0c;非常简单…

Ubuntu系统Linux内核安装和使用

安装&#xff1a; 检查树莓派Linux版本&#xff0c;我的是6.1 uname -r 内核下载链接&#xff1a; Raspberry Pi GitHub 找对应版本下载 导入之后&#xff0c;解压安装即可 unzip linux-rpi-6.1.y.zip 其他内容 treee 指令安装 sudo apt-get install tree 使用这…

【ICCV 2023 Oral】High-Quality Entity Segmentation分享

为什么会看这篇文章呢&#xff1f;因为要搞所谓分割大模型&#xff0c;为什么要搞分割大模型&#xff0c;因为最终我们要搞得是&#xff0c;业内领先的全自动标注系统。&#xff08;标完都不需要人工再修正&#xff01;&#xff01;&#xff01;&#xff09; OK&#xff0c;仰…

JavaScript系列从入门到精通系列第十篇:JavaScript中的相等运算符与条件运算符

文章目录 一&#xff1a;相等运算符 1&#xff1a; 2&#xff1a;! 3&#xff1a;与! (一)&#xff1a; (二)&#xff1a;! 二&#xff1a;条件运算符 1&#xff1a;语法 2&#xff1a;使用 3&#xff1a;容易挨打的写法 一&#xff1a;相等运算符 用于比较两个值是…

No151.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显示( proteus仿真+程序+原理图+报告+讲解视频)

51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显示( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4.原理图5. 设计报告6. 设计资料内容清单 51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显…

最新AI智能写作系统ChatGPT源码/支持GPT4.0+GPT联网提问/支持ai绘画Midjourney+Prompt+MJ以图生图+思维导图生成

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…

多维时序 | MATLAB实现PSO-BP多变量时间序列预测(粒子群优化BP神经网络)

多维时序 | MATLAB实现PSO-BP多变量时间序列预测(粒子群优化BP神经网络) 目录 多维时序 | MATLAB实现PSO-BP多变量时间序列预测(粒子群优化BP神经网络)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PSO-BP粒子群优化BP神经网络多变量时间序列预测&#xff…

《YOLOv5:从入门到实战》报错解决 专栏答疑

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。《YOLOv5&#xff1a;从入门到实战》专栏上线后&#xff0c;部分同学在学习过程中提出了一些问题&#xff0c;笔者相信这些问题其他同学也有可能遇到。为了让大家可以更好地学习本专栏内容&#xff0c;笔者特意推出了该篇专…

QT6.5.2编译PostgreSql驱动

一、环境 1、操作系统&#xff1a;win11 2、qt版本&#xff1a;6.5.2 3、PostgreSql版本:14.9 二、下载qbase源码 1、下载地址&#xff1a;https://github.com/qt/qtbase/tree/6.5.2 将下载的源码文件解压指定的的目录&#xff0c;找到src/plugins/sqldrivers根据自己的实…

2023年【安徽省安全员C证】模拟考试题及安徽省安全员C证实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【安徽省安全员C证】模拟考试题及安徽省安全员C证实操考试视频&#xff0c;包含安徽省安全员C证模拟考试题答案和解析及安徽省安全员C证实操考试视频练习。安全生产模拟考试一点通结合国家安徽省安全员C证考试最…

Three.js加载360全景图片/视频

Three.js加载360全景图片/视频 效果 原理 将全景图片/视频作为texture引入到three.js场景中将贴图与球形网格模型融合&#xff0c;将球模型当做成环境容器使用处理视频时需要以dom为载体&#xff0c;加载与控制视频动作每次渲染时更新当前texture&#xff0c;以达到视频播放效…

Jenkins 权限管理

关于Role-based Authorization Strategy 使用Jenkins自身的权限管理过于粗糙&#xff0c;无法对单个、一类项目做管理&#xff0c;我们可以使用 Role-based Authorization Strategy插件来管理项目、角色。 首先安装该插件&#xff1a;在Jenkins查看该插件有无安装 在Jenkins-…

基于css变量轻松实现网站的主题切换功能

我们经常看到一些网站都有主题切换&#xff0c;例如vue官方文档。那他是怎么实现的呢&#xff1f; 检查元素&#xff0c;发现点击切换时&#xff0c;html元素会动态的添加和移除一个class:dark&#xff0c;然后页面主题色就变了。仔细想想&#xff0c;这要是放在以前&#xff0…

[ruby on rails] postgres sql explain 优化

一、查看执行计划 sql User.all.to_sql # 不会实际执行查询 puts ActiveRecord::Base.connection.explain(sql)# 会实际执行查询&#xff0c;再列出计划 User.all.explain# 会实际执行查询&#xff0c;再列出计划 ActiveRecord::Base.connection.execute(EXPLAIN (ANALYZE, V…