Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Laurence001
d2l-zh
提交
9ca01f76
D
d2l-zh
项目概览
Laurence001
/
d2l-zh
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
d2l-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
9ca01f76
编写于
12月 17, 2020
作者:
R
Rachel Hu
提交者:
Aston Zhang
2月 17, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
init“
上级
c9a12113
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
32 addition
and
35 deletion
+32
-35
chapter_convolutional-modern/resnet.md
chapter_convolutional-modern/resnet.md
+32
-35
未找到文件。
chapter_convolutional-modern/resnet.md
浏览文件 @
9ca01f76
# 残
余网络
# 残
差网络(ResNet)
:label:
`sec_resnet`
随着我们设计越来越深
入的网络,必须了解添加图层如何增加网络的复杂性和表现力。更重要的是设计网络的能力,其中添加图层可使网络更具表现力,而不仅仅是不同。为了取得一些进展,我们需要一些数学
。
随着我们设计越来越深
的网络,了解添加层如何增加网络的复杂性和表现力变得越来越重要。更重要的是设计网络的能力,在这种网络中,添加层会使网络更具表现力,而不仅仅是与众不同。为了取得一些进展,我们需要一些数学知识
。
## 函数类
考虑
$
\m
athcal{F}$,即特定网络架构(以及学习速率和其他超参数设置)可以达到的功能类别。也就是说,对于所有 $f
\i
n
\m
athcal{F}$,存在一些参数集(例如,权重和偏差),可以通过对合适的数据集进行培训来获得。让我们假设 $f^
*$ 是我们真正想找到的 “真相” 功能。如果它是在 $\mathcal{F}$, 我们是在良好的状态,但通常我们不会很幸运.相反,我们会尝试找到一些 $f^*
_
\m
athcal{F}$,这是我们在 $
\m
athcal{F}$ 内最好的选择。例如,给定一个包含要素 $
\m
athbf{X}$ 和标签 $
\m
athbf{y}$ 的数据集,我们可能会
尝试通过解决以下优化问题来找到它:
考虑
$
\m
athcal{F}$,一种特定网络架构(连同学习速率和其他超参数设置)可以达到的功能类别。也就是说,对于所有$f
\i
n
\m
athcal{F}$,存在一些参数集(例如权重和偏差),这些参数可以通过在合适的数据集上进行训练而获得。让我们假设$f^
*$是我们真正想要找到的“真相”函数。如果是$\mathcal{F}$,我们的状态很好,但通常我们不会那么幸运。相反,我们将尝试找到一些$f^*
_
\m
athcal{F}$,这是我们在$
\m
athcal{F}$中的最佳选择。例如,给定一个具有$
\m
athbf{X}$特性和$
\m
athbf{y}$标签的数据集,我们可以
尝试通过解决以下优化问题来找到它:
$$f^
*
_\mathcal{F}
\stackrel{\mathrm{def}}{=}
\mathop{\mathrm{argmin}}_
f L(
\m
athbf{X},
\m
athbf{y}, f)
\t
ext{ subject to } f
\i
n
\m
athcal{F}.$$
$$f^
*
_\mathcal{F}
:=
\mathop{\mathrm{argmin}}_
f L(
\m
athbf{X},
\m
athbf{y}, f)
\t
ext{ subject to } f
\i
n
\m
athcal{F}.$$
只有合理的假设是,如果我们设计一个不同
和更强大的架构 $
\m
athcal{F}'$,我们应该得到更好的结果。换句话说,我们预计 $f^
*_{\mathcal{F}'}$ 比 $f^*_{\mathcal{F}}$ “更好”。但是,如果 $\mathcal{F} \not\subseteq \mathcal{F}'$ 没有保证,甚至会发生这种情况。事实上,$f^*_
{
\m
athcal{F}'}$ 可能更糟糕。如 :numref:
`fig_functionclasses`
所示,对于非嵌套函数类,较大的函数类并不总是靠近 “真实” 函数 $f^
*$。例如,在 :numref:`fig_functionclasses` 的左侧,虽然 $\mathcal{F}_3$ 比 $\mathcal{F}_3$ 接近 $f^*
$,但是 $
\m
athcal{F}_6$ 会移动,并且不能保证进一步增加复杂性可以减少从 $f^
*
$ 的距离。使用嵌套函数类,其中 $
\m
athcal{F}_1
\s
ubseteq
\l
dots
\s
ubseteq
\m
athcal{F}_6$ 位于 :numref:
`fig_functionclasses`
的右侧,我们可以避免非嵌套函数类中的
上述问题。
只有合理的假设是,如果我们设计一个不同
的、更强大的体系结构$
\m
athcal{F}'$,我们将获得更好的结果。换句话说,我们预计$f^
*_{\mathcal{F}'}$比$f^*_{\mathcal{F}}$“更好”。然而,如果$\mathcal{F} \not\subseteq \mathcal{F}'$,则无法保证这种情况会发生。事实上,$f^*_
{
\m
athcal{F}'}$可能更糟。如:numref:
`fig_functionclasses`
所示,对于非嵌套函数类,较大的函数类并不总是向“真”函数$f^
*$靠拢。例如,在:numref:`fig_functionclasses`的左边,虽然$\mathcal{F}_3$比$f^*
$更接近$f^
*$,但$\mathcal{F}_6$却离开了,并且不能保证进一步增加复杂性可以减少与$f^*
$的距离。对于嵌套函数类:numref:
`fig_functionclasses`
右侧的$
\m
athcal{F}_1
\s
ubseteq
\l
dots
\s
ubseteq
\m
athcal{F}_6$,我们可以从非嵌套函数类中避免
上述问题。
![
For non-nested function classes, a larger (indicated by area) function class does not guarantee to get closer to the "truth" function ($f^*$). This does not happen in nested function classes.
](
../img/functionclasses.svg
)
:label:
`fig_functionclasses`
因此,只有当较大的函数类包含较小的函数类时,我们才能保证增加它们
会严格增加网络的表达能力。对于深度神经网络,如果我们可以将新添加的层训练成一个身份函数 $f(
\m
athbf{x}) =
\m
athbf{x}$,新模型将与原始模型一样有效。由于新模型可能会得到更好的解决方案来拟合训练数据集,因此添加的图层可能会更轻松地减少训练错误
。
因此,只有当较大的函数类包含较小的函数类时,我们才能保证增加它们
严格地增加网络的表达能力。对于深度神经网络,如果我们能将新增加的层训练成一个识别函数$f(
\m
athbf{x}) =
\m
athbf{x}$,新模型将与原模型一样有效。由于新模型可能会得到更好的解决方案来适应训练数据集,因此增加的层可能会更容易减少训练误差
。
这是他等人
的问题. 在工作非常深的计算机视觉模型 :cite:
`He.Zhang.Ren.ea.2016`
时考虑.他们提出的
* 残留网络 *
(
*Resnet*
) 的核心是,每个额外的图层都应更容易地包含身份函数作为其元素之一。这些考虑相当深刻,但它们导致了一个令人惊讶的简单解决方案,一个
* 残余块 *
。凭借它,RESnet 在 2015 年赢得了 iMageNet 大规模视觉识别挑战赛。该设计对如何构建深度神经网络具有
深远的影响。
这是他等人
提出的问题。当工作在非常深的计算机视觉模型:cite:
`He.Zhang.Ren.ea.2016`
。他们提出的“残差网络”(
*ResNet*
)的核心思想是,每个附加层都应该更容易地包含身份函数作为其元素之一。这些考虑是相当深刻的,但他们导致了一个惊人的简单的解决方案,一个
*剩余块*
。凭借它,ResNet赢得了2015年ImageNet大规模视觉识别挑战赛。这个设计对如何建立深层神经网络产生了
深远的影响。
## 残
余
块
## 残
差
块
让我们
专注于神经网络的局部部分,如 :numref:
`fig_residual_block`
所示。用 $
\m
athbf{x}$ 表示输入。我们假设我们想要通过学习获得的所需底层映射是 $f(
\m
athbf{x})$,用作顶部激活函数的输入。在 :numref:
`fig_residual_block`
的左侧,虚线框中的部分必须直接了解映射 $f(
\m
athbf{x})$。在右侧,点线框中的部分需要了解
* 残差映射 *
$f(
\m
athbf{x}) -
\m
athbf{x}$,这是残差块如何派生其名称。如果身份映射 $f(
\m
athbf{x}) =
\m
athbf{x}$ 是所需的底层映射,残差映射就更容易学习:我们只需要将上层权重层(例如,完全连接的层和卷积层)的权重和偏差推到零。:numref:
`fig_residual_block`
中的右图显示了 ResNet 的
* 残余块 *
,其中将层输入 $
\m
athbf{x}$ 传送到加法运算符的实线称为
* 残差连接 *
(或
* 快捷连接 *
)。使用残余块时,输入可以通过跨层的残余连接更快地
传播。
让我们
关注神经网络的局部部分,如:numref:
`fig_residual_block`
所示。用$
\m
athbf{x}$表示输入。我们假设我们希望通过学习获得的所需底层映射是$f(
\m
athbf{x})$,用作顶部激活函数的输入。在:numref:
`fig_residual_block`
的左边,虚线框内的部分必须直接学习映射$f(
\m
athbf{x})$。在右边,虚线框内的部分需要学习
*残差映射*
$f(
\m
athbf{x}) -
\m
athbf{x}$,这就是残差块如何获得其名称的。如果身份映射$f(
\m
athbf{x}) =
\m
athbf{x}$是所需的底层映射,则残差映射更容易学习:我们只需将虚线框内上部权重层(例如,完全连接层和卷积层)的权重和偏差推到零。:numref:
`fig_residual_block`
中的右图说明了ResNet的
*剩余块*
,其中携带层输入$
\m
athbf{x}$到加法运算符的实线称为
*剩余连接*
(或
*快捷连接*
)。使用剩余块,输入可以通过层间的剩余连接更快地向前
传播。
![
A regular block (left) and a residual block (right).
](
../img/residual-block.svg
)
:label:
`fig_residual_block`
R
ENet 采用 VGG 的完整卷积层设计。残余模块具有两个 $3
\t
imes 3$ 卷积层,具有相同数量的输出通道。每个卷积层后跟一个批量归一化层和一个 RELU 激活函数。然后,我们跳过这两个卷积操作,直接在最终的 RELU 激活函数之前添加输入。这种设计要求两个卷积图层的输出必须与输入的形状相同,以便它们可以相加在一起。如果我们想要更改通道数量,我们需要引入额外的 $1
\t
imes 1$ 卷积层,以便将输入转换为加法操作
所需的形状。让我们看看下面的代码。
R
esNet遵循VGG的完整$3
\t
imes 3$卷积层设计。剩余块具有两个$3
\t
imes 3$个卷积层,具有相同数量的输出信道。每个卷积层后面是一个批处理规范化层和一个ReLU激活函数。然后,我们跳过这两个卷积运算,直接在最终的ReLU激活函数之前添加输入。这种设计要求两个卷积层的输出必须与输入具有相同的形状,以便将它们相加。如果我们想要改变通道的数量,我们需要引入一个额外的$1
\t
imes 1$卷积层来将输入转换成加法运算
所需的形状。让我们看看下面的代码。
```
{.python .input}
from d2l import mxnet as d2l
...
...
@@ -34,7 +34,6 @@ from mxnet.gluon import nn
npx.set_np()
class Residual(nn.Block): #@save
"""The Residual block of ResNet."""
def __init__(self, num_channels, use_1x1conv=False, strides=1, **kwargs):
super().__init__(**kwargs)
self.conv1 = nn.Conv2D(num_channels, kernel_size=3, padding=1,
...
...
@@ -64,7 +63,6 @@ from torch import nn
from torch.nn import functional as F
class Residual(nn.Module): #@save
"""The Residual block of ResNet."""
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
...
...
@@ -96,7 +94,6 @@ from d2l import tensorflow as d2l
import tensorflow as tf
class Residual(tf.keras.Model): #@save
"""The Residual block of ResNet."""
def __init__(self, num_channels, use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = tf.keras.layers.Conv2D(
...
...
@@ -119,12 +116,12 @@ class Residual(tf.keras.Model): #@save
return tf.keras.activations.relu(Y)
```
此代码生成两种类型的网络:一种是在
`use_1x1conv=False`
应用 RELU 非线性度之前将输入添加到输出中,另一种是我们在添加前通过 $1
\t
imes 1$ 卷积调整通道和分辨率。:numref:
`fig_resnet_block`
说明了这一点:
此代码生成两种类型的网络:一种是在
`use_1x1conv=False`
应用ReLU非线性之前将输入添加到输出,另一种是在添加之前通过$1
\t
imes 1$卷积调整信道和分辨率。:numref:
`fig_resnet_block`
说明了这一点:
![
ResNet block with and without $1 \times 1$ convolution.
](
../img/resnet-block.svg
)
:label:
`fig_resnet_block`
现在让我们看
一下输入和输出形状相同
的情况。
现在让我们看
看输入和输出是相同形状
的情况。
```
{.python .input}
blk = Residual(3)
...
...
@@ -169,9 +166,9 @@ blk = Residual(6, use_1x1conv=True, strides=2)
blk(X).shape
```
##
资源网
模型
##
ResNet
模型
R
ENet 的前两层与我们前面描述的 Googlenet 相同:$7
\t
imes 7$ 卷积层有 64 个输出通道,步幅为 2 的最大池层后面是 $3
\t
imes 3$,步幅为 2。不同之处在于在 ResNet 中每个卷积图层之后添加的批量归一化图
层。
R
esNet的前两层与我们前面描述的GoogLeNet是相同的:$7
\t
imes 7$卷积层有64个输出通道,步长为2,后面是$3
\t
imes 3$最大池层,步长为2。区别是在ResNet中每个卷积层之后添加的批处理规范化
层。
```
{.python .input}
net = nn.Sequential()
...
...
@@ -196,9 +193,9 @@ b1 = tf.keras.models.Sequential([
tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')])
```
Goog
lenet 使用四个模块组成的模块。然而,ResNet 使用由残余块组成的四个模块,每个模块都使用具有相同数量输出通道的几个残余块。第一个模块中的通道数与输入通道数相同。由于已经使用了步幅为 2 的最大池层,因此没有必要减少高度和宽度。在每个后续模块的第一个残余块中,通道数量与前一个模块相比
增加了一倍,并且高度和宽度减半。
Goog
LeNet使用了由Inception块组成的四个模块。然而,ResNet使用由剩余块组成的四个模块,每个模块使用具有相同数量输出信道的几个剩余块。第一个模块中的通道数与输入通道数相同。由于已经使用了跨距为2的最大池层,因此不必减小高度和宽度。在每个后续模块的第一剩余块中,与前一模块相比,信道数量
增加了一倍,并且高度和宽度减半。
现在,我们实现这个模块。
请注意,在第一个模块上
执行了特殊处理。
现在,我们实现这个模块。
注意,对第一个模块
执行了特殊处理。
```
{.python .input}
def resnet_block(num_channels, num_residuals, first_block=False):
...
...
@@ -245,7 +242,7 @@ class ResnetBlock(tf.keras.layers.Layer):
return X
```
然后,我们将所有模块添加到
ResNet 中。在这里,每个模块使用两个残
余块。
然后,我们将所有模块添加到
ResNet中。这里,每个模块使用两个剩
余块。
```
{.python .input}
net.add(resnet_block(64, 2, first_block=True),
...
...
@@ -270,7 +267,7 @@ b4 = ResnetBlock(256, 2)
b5 = ResnetBlock(512, 2)
```
最后,就像
Googlenet 一样,我们添加了一个全局平均池图层,然后是完全连接的图
层输出。
最后,就像
GoogLeNet一样,我们添加了一个全局平均池层,然后是完全连接的
层输出。
```
{.python .input}
net.add(nn.GlobalAvgPool2D(), nn.Dense(10))
...
...
@@ -307,12 +304,12 @@ def net():
tf.keras.layers.Dense(units=10)])
```
每个模块
中有 4 个卷积层(不包括 $1
\t
imes 1$ 卷积层)。再加上第一个 $7
\t
imes 7$ 卷积层和最后一个完全连接的层,共有 18 个层。因此,这种模式通常被称为 RENET-18。通过在模块中配置不同数量的通道和残余块,我们可以创建不同的 ResNet 模型,例如更深的 152 层 Resnet-152。虽然 ResNet 的主要体系结构与 Googlenet 的体系结构相似,但 ResNet 的结构更简单、更容易修改。所有这些因素都导致人们迅速和广泛地使用了资源信息网
。
每个模块
有4个卷积层(不包括$1
\t
imes 1$卷积层)。加上第一个$7
\t
imes 7$卷积层和最后一个全连通层,共有18个层。因此,这种模型通常被称为ResNet-18。通过在模块中配置不同数量的信道和剩余块,我们可以创建不同的ResNet模型,例如更深的152层ResNet-152。虽然ResNet的主要架构与GoogLeNet相似,但是ResNet的结构更简单、更容易修改。所有这些因素导致ResNet的迅速和广泛的使用。:numref:
`fig_resnet18`
描述了完整的ResNet-18
。
![
The ResNet-18 architecture.
](
../img/resnet18.svg
)
:label:
`fig_resnet18`
在
培训 ResNet 之前,让我们观察在 ResNet 中不同模块之间输入形状如何变化。与之前的所有体系结构一样,分辨率随着通道数量的增加而降低,直到全局平均池图层聚合所有要素
为止。
在
训练ResNet之前,让我们观察一下ResNet中不同模块的输入形状是如何变化的。在所有以前的架构中,分辨率降低,而通道数量增加,直到全局平均池层聚集所有特性
为止。
```
{.python .input}
X = np.random.uniform(size=(1, 1, 224, 224))
...
...
@@ -338,9 +335,9 @@ for layer in net().layers:
print(layer.__class__.__name__,'output shape:\t', X.shape)
```
##
培训
##
训练 ResNet
我们训练 Resnet 上的时尚 MNist 数据集, 就像以前一样.
同之前一样,我们在 Fashion-MNIST 数据集上训练 ResNet。
```
{.python .input}
#@tab all
...
...
@@ -349,20 +346,20 @@ train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr)
```
##
摘要
##
小结
*
嵌套函数类是
可取的。在深度神经网络中学习一个额外的
层作为身份函数(尽管这是一个极端情况)应该很容易。
*
残差映射可以更容易地学习
身份
函数,例如将权重层中的参数推到零。
*
我们可以通过具有残余块来训练一个有效的深度神经网络。输入可以通过跨层
的残余连接更快地向前传播。
*
ResNet
对后续深度神经网络的设计具有重大影响,无论是卷积性还是顺序性质
。
*
嵌套函数类是
理想的。在深层神经网络中学习另一
层作为身份函数(尽管这是一个极端情况)应该很容易。
*
残差映射可以更容易地学习
同一
函数,例如将权重层中的参数推到零。
*
利用残差块可以训练出一个有效的深层神经网络。输入可以通过层间
的残余连接更快地向前传播。
*
ResNet
对随后的深层神经网络的设计产生了重大影响,无论是卷积的还是序列的
。
## 练习
1.
:numref:
`fig_inception`
中的 “启动” 块与剩余块之间的主要区别是什么?删除 “启动” 块中的某些路径后,它们彼此之间的关系如何
?
1.
请参阅 RENet 文件 :cite:
`He.Zhang.Ren.ea.2016`
中的表
1,以实现不同的变体。
1.
对于更深层次的网络,ResNet
引入了一种 “瓶颈” 体系结构,以降低模型复杂性。尝试
实现它。
1.
在
后续版本的 ResNet 中,作者将 “卷积、批量规范化和激活” 结构更改为 “批量规范化、激活和卷积” 结构。自己做这一改进。有关详细信息,请参阅 :cite:
`He.Zhang.Ren.ea.2016*1`
中的图
1。
1.
为什么
我们不能在没有绑定的情况下增加函数的复杂性,即使函数类是嵌套的
?
1.
:numref:
`fig_inception`
中的起始块与剩余块之间的主要区别是什么?在删除了Inception块中的一些路径之后,它们是如何相互关联的
?
1.
参考ResNet论文:cite:
`He.Zhang.Ren.ea.2016`
中的表
1,以实现不同的变体。
1.
对于更深层次的网络,ResNet
引入了“瓶颈”架构来降低模型的复杂性。试着去
实现它。
1.
在
ResNet的后续版本中,作者将“卷积、批处理规范化和激活”结构更改为“批处理规范化、激活和卷积”结构。你自己做这个改进。详见:cite:
`He.Zhang.Ren.ea.2016*1`
中的图
1。
1.
为什么
即使函数类是嵌套的,我们为什么不能不加限制地增加函数的复杂性呢
?
:begin_tab:
`mxnet`
[
Discussions
](
https://discuss.d2l.ai/t/85
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录