diff --git a/docs/chapter01_DL-intro/deep-learning-intro.md b/docs/chapter01_DL-intro/deep-learning-intro.md
index d9a5b980c..d468b6629 100644
--- a/docs/chapter01_DL-intro/deep-learning-intro.md
+++ b/docs/chapter01_DL-intro/deep-learning-intro.md
@@ -25,7 +25,7 @@
即使是在中世纪,数学家也热衷于利用统计学来做出估计。例如,在[雅各比·科贝尔(1460--1533)](https://www.maa.org/press/periodicals/convergence/mathematical-treasures-jacob-kobels-geometry)的几何书中记载了使用16名男子的平均脚长来估计男子的平均脚长。
-
+
图1.1 在中世纪,16名男子的平均脚长被用来估计男子的平均脚长
diff --git a/docs/chapter02_prerequisite/2.1_install.md b/docs/chapter02_prerequisite/2.1_install.md
index 624338392..9a2ba703d 100644
--- a/docs/chapter02_prerequisite/2.1_install.md
+++ b/docs/chapter02_prerequisite/2.1_install.md
@@ -11,7 +11,7 @@ Anaconda是Python的一个开源发行版本,主要面向科学计算。我们
## 2.1.2 Jupyter
在没有notebook之前,在IT领域是这样工作的:在普通的 Python shell 或者在IDE(集成开发环境)如Pycharm中写代码,然后在word中写文档来说明你的项目。这个过程很反锁,通常是写完代码,再写文档的时候我还的重头回顾一遍代码。最蛋疼的地方在于,有些数据分析的中间结果,还得重新跑代码,然后把结果弄到文档里给客户看。有了notebook之后,世界突然美好了许多,因为notebook可以直接在代码旁写出叙述性文档,而不是另外编写单独的文档。也就是它可以能将代码、文档等这一切集中到一处,让用户一目了然。如下图所示。
-
+
Jupyter Notebook 已迅速成为数据分析,机器学习的必备工具。因为它可以让数据分析师集中精力向用户解释整个分析过程。
diff --git a/docs/chapter03_DL-basics/3.11_underfit-overfit.md b/docs/chapter03_DL-basics/3.11_underfit-overfit.md
index 9fdf2fedc..b32931257 100644
--- a/docs/chapter03_DL-basics/3.11_underfit-overfit.md
+++ b/docs/chapter03_DL-basics/3.11_underfit-overfit.md
@@ -52,7 +52,7 @@ $$
因为高阶多项式函数模型参数更多,模型函数的选择空间更大,所以高阶多项式函数比低阶多项式函数的复杂度更高。因此,高阶多项式函数比低阶多项式函数更容易在相同的训练数据集上得到更低的训练误差。给定训练数据集,模型复杂度和误差之间的关系通常如图3.4所示。给定训练数据集,如果模型的复杂度过低,很容易出现欠拟合;如果模型复杂度过高,很容易出现过拟合。应对欠拟合和过拟合的一个办法是针对数据集选择合适复杂度的模型。
-
+
图3.4 模型复杂度对欠拟合和过拟合的影响
@@ -168,7 +168,7 @@ weight: tensor([[ 1.1982, -3.3992, 5.6002]])
bias: tensor([5.0014])
```
-
+
@@ -187,7 +187,7 @@ weight: tensor([[19.4123]])
bias: tensor([0.5805])
```
-
+
### 3.11.4.5 训练样本不足(过拟合)
@@ -205,7 +205,7 @@ weight: tensor([[1.4741, 2.1198, 2.5674]])
bias: tensor([3.1207])
```
-
+
我们将在接下来的两个小节继续讨论过拟合问题以及应对过拟合的方法。
diff --git a/docs/chapter03_DL-basics/3.12_weight-decay.md b/docs/chapter03_DL-basics/3.12_weight-decay.md
index 4d2d115a1..7ca6d384a 100644
--- a/docs/chapter03_DL-basics/3.12_weight-decay.md
+++ b/docs/chapter03_DL-basics/3.12_weight-decay.md
@@ -126,7 +126,7 @@ fit_and_plot(lambd=0)
L2 norm of w: 15.114808082580566
```
-
+
### 3.12.3.5 使用权重衰减
@@ -142,7 +142,7 @@ fit_and_plot(lambd=3)
L2 norm of w: 0.035220853984355927
```
-
+
## 3.12.4 简洁实现
@@ -188,7 +188,7 @@ fit_and_plot_pytorch(0)
L2 norm of w: 12.86785888671875
```
-
+
``` python
@@ -200,7 +200,7 @@ fit_and_plot_pytorch(3)
L2 norm of w: 0.09631537646055222
```
-
+
## 小结
diff --git a/docs/chapter03_DL-basics/3.13_dropout.md b/docs/chapter03_DL-basics/3.13_dropout.md
index a82921e4c..25ea798ed 100644
--- a/docs/chapter03_DL-basics/3.13_dropout.md
+++ b/docs/chapter03_DL-basics/3.13_dropout.md
@@ -25,7 +25,7 @@ $$
即**丢弃法不改变其输入的期望值**。让我们对图3.3中的隐藏层使用丢弃法,一种可能的结果如图3.5所示,其中$h_2$和$h_5$被清零。这时输出值的计算不再依赖$h_2$和$h_5$,在反向传播时,与这两个隐藏单元相关的权重的梯度均为0。由于在训练中隐藏层神经元的丢弃是随机的,即$h_1, \ldots, h_5$都有可能被清零,输出层的计算无法过度依赖$h_1, \ldots, h_5$中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,一般不使用丢弃法。
-
+
图3.5 隐藏层使用了丢弃法的多层感知机
diff --git a/docs/chapter03_DL-basics/3.14_backprop.md b/docs/chapter03_DL-basics/3.14_backprop.md
index d8f2aff90..88bc25c1c 100644
--- a/docs/chapter03_DL-basics/3.14_backprop.md
+++ b/docs/chapter03_DL-basics/3.14_backprop.md
@@ -36,7 +36,7 @@ $$J = L + s.$$
我们通常绘制计算图来可视化运算符和变量在计算中的依赖关系。图3.6绘制了本节中样例模型正向传播的计算图,其中左下角是输入,右上角是输出。可以看到,图中箭头方向大多是向右和向上,其中方框代表变量,圆圈代表运算符,箭头表示从输入到输出之间的依赖关系。
-
+
图3.6 正向传播的计算图
diff --git a/docs/chapter03_DL-basics/3.16_kaggle-house-price.md b/docs/chapter03_DL-basics/3.16_kaggle-house-price.md
index 4314cee2f..225af4549 100644
--- a/docs/chapter03_DL-basics/3.16_kaggle-house-price.md
+++ b/docs/chapter03_DL-basics/3.16_kaggle-house-price.md
@@ -7,7 +7,7 @@
[Kaggle](https://www.kaggle.com)是一个著名的供机器学习爱好者交流的平台。图3.7展示了Kaggle网站的首页。为了便于提交结果,需要注册Kaggle账号。
-
+
图3.7 Kaggle网站首页
@@ -15,7 +15,7 @@
-
+
图3.8 房价预测比赛的网页信息。比赛数据集可通过点击“Data”标签获取
图3.8展示了房价预测比赛的网页信息。
@@ -67,7 +67,7 @@ test_data.shape # 输出 (1459, 80)
``` python
train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]]
```
-
+
可以看到第一个特征是Id,它能帮助模型记住每个训练样本,但难以推广到测试样本,所以我们不使用它来训练。我们将所有的训练数据和测试数据的79个特征按样本连结。
@@ -210,7 +210,7 @@ fold 3, train rmse 0.237733, valid rmse 0.218747
fold 4, train rmse 0.230720, valid rmse 0.258712
5-fold validation: avg train rmse 0.234155, avg valid rmse 0.241113
```
-
+
## 3.16.6 模型选择
@@ -252,12 +252,12 @@ train_and_pred(train_features, test_features, train_labels, test_data, num_epoch
```
train rmse 0.229943
```
-
+
上述代码执行完之后会生成一个submission.csv文件。这个文件是符合Kaggle比赛要求的提交格式的。这时,我们可以在Kaggle上提交我们预测得出的结果,并且查看与测试数据集上真实房价(标签)的误差。具体来说有以下几个步骤:登录Kaggle网站,访问房价预测比赛网页,并点击右侧“Submit Predictions”或“Late Submission”按钮;然后,点击页面下方“Upload Submission File”图标所在的虚线框选择需要提交的预测结果文件;最后,点击页面最下方的“Make Submission”按钮就可以查看结果了,如图3.9所示。
-
+
图3.9 Kaggle预测房价比赛的预测结果提交页面
diff --git a/docs/chapter03_DL-basics/3.1_linear-regression.md b/docs/chapter03_DL-basics/3.1_linear-regression.md
index 766a52193..d1cfa77eb 100644
--- a/docs/chapter03_DL-basics/3.1_linear-regression.md
+++ b/docs/chapter03_DL-basics/3.1_linear-regression.md
@@ -82,7 +82,7 @@ $$
在深度学习中,我们可以使用神经网络图直观地表现模型结构。为了更清晰地展示线性回归作为神经网络的结构,图3.1使用神经网络图表示本节中介绍的线性回归模型。神经网络图隐去了模型参数权重和偏差。
-
+
图3.1 线性回归是一个单层神经网络
diff --git a/docs/chapter03_DL-basics/3.2_linear-regression-scratch.md b/docs/chapter03_DL-basics/3.2_linear-regression-scratch.md
index 6921052ce..768ca545b 100644
--- a/docs/chapter03_DL-basics/3.2_linear-regression-scratch.md
+++ b/docs/chapter03_DL-basics/3.2_linear-regression-scratch.md
@@ -63,7 +63,7 @@ set_figsize()
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
```
-
+
我们将上面的`plt`作图函数以及`use_svg_display`函数和`set_figsize`函数定义在`d2lzh_pytorch`包里。以后在作图时,我们将直接调用`d2lzh_pytorch.plt`。由于`plt`在`d2lzh_pytorch`包中是一个全局变量,我们在作图前只需要调用`d2lzh_pytorch.set_figsize()`即可打印矢量图并设置图的尺寸。
diff --git a/docs/chapter03_DL-basics/3.4_softmax-regression.md b/docs/chapter03_DL-basics/3.4_softmax-regression.md
index ad311d27d..ea0b2c8cc 100644
--- a/docs/chapter03_DL-basics/3.4_softmax-regression.md
+++ b/docs/chapter03_DL-basics/3.4_softmax-regression.md
@@ -25,7 +25,7 @@ $$
图3.2用神经网络图描绘了上面的计算。softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出$o_1, o_2, o_3$的计算都要依赖于所有的输入$x_1, x_2, x_3, x_4$,softmax回归的输出层也是一个全连接层。
-
+
图3.2 softmax回归是一个单层神经网络
diff --git a/docs/chapter03_DL-basics/3.5_fashion-mnist.md b/docs/chapter03_DL-basics/3.5_fashion-mnist.md
index 49a751385..9e516b37f 100644
--- a/docs/chapter03_DL-basics/3.5_fashion-mnist.md
+++ b/docs/chapter03_DL-basics/3.5_fashion-mnist.md
@@ -94,7 +94,7 @@ show_fashion_mnist(X, get_fashion_mnist_labels(y))
```
-
+
## 3.5.2 读取小批量
diff --git a/docs/chapter03_DL-basics/3.6_softmax-regression-scratch.md b/docs/chapter03_DL-basics/3.6_softmax-regression-scratch.md
index 36c4491c6..566a2f646 100644
--- a/docs/chapter03_DL-basics/3.6_softmax-regression-scratch.md
+++ b/docs/chapter03_DL-basics/3.6_softmax-regression-scratch.md
@@ -214,7 +214,7 @@ titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
d2l.show_fashion_mnist(X[0:9], titles[0:9])
```
-
+
## 小结
diff --git a/docs/chapter03_DL-basics/3.8_mlp.md b/docs/chapter03_DL-basics/3.8_mlp.md
index f8dda6395..abb38fbbc 100644
--- a/docs/chapter03_DL-basics/3.8_mlp.md
+++ b/docs/chapter03_DL-basics/3.8_mlp.md
@@ -7,7 +7,7 @@
多层感知机在单层神经网络的基础上引入了一到多个隐藏层(hidden layer)。隐藏层位于输入层和输出层之间。图3.3展示了一个多层感知机的神经网络图,它含有一个隐藏层,该层中有5个隐藏单元。
-
+
图3.3 带有隐藏层的多层感知机
@@ -72,7 +72,7 @@ xyplot(x, y, 'relu')
```
-
+
显然,当输入为负数时,ReLU函数的导数为0;当输入为正数时,ReLU函数的导数为1。尽管输入为0时ReLU函数不可导,但是我们可以取此处的导数为0。下面绘制ReLU函数的导数。
@@ -82,7 +82,7 @@ y.sum().backward()
xyplot(x, x.grad, 'grad of relu')
```
-
+
### 3.8.2.2 sigmoid函数
@@ -98,7 +98,7 @@ y = x.sigmoid()
xyplot(x, y, 'sigmoid')
```
-
+
依据链式法则,sigmoid函数的导数
@@ -114,7 +114,7 @@ y.sum().backward()
xyplot(x, x.grad, 'grad of sigmoid')
```
-
+
### 3.8.2.3 tanh函数
@@ -130,7 +130,7 @@ y = x.tanh()
xyplot(x, y, 'tanh')
```
-
+
依据链式法则,tanh函数的导数
@@ -145,7 +145,7 @@ y.sum().backward()
xyplot(x, x.grad, 'grad of tanh')
```
-
+
## 3.8.3 多层感知机
diff --git a/docs/chapter05_CNN/5.11_resnet.md b/docs/chapter05_CNN/5.11_resnet.md
index b7fd52d70..81249605f 100644
--- a/docs/chapter05_CNN/5.11_resnet.md
+++ b/docs/chapter05_CNN/5.11_resnet.md
@@ -8,7 +8,7 @@
让我们聚焦于神经网络局部。如图5.9所示,设输入为$\boldsymbol{x}$。假设我们希望学出的理想映射为$f(\boldsymbol{x})$,从而作为图5.9上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射$f(\boldsymbol{x})$,而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射$f(\boldsymbol{x})-\boldsymbol{x}$。残差映射在实际中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射$f(\boldsymbol{x})$。我们只需将图5.9中右图虚线框内上方的加权运算(如仿射)的权重和偏差参数学成0,那么$f(\boldsymbol{x})$即为恒等映射。实际中,当理想映射$f(\boldsymbol{x})$极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。图5.9右图也是ResNet的基础块,即残差块(residual block)。在残差块中,输入可通过跨层的数据线路更快地向前传播。
-
+
图5.9 普通的网络结构(左)与加入残差连接的网络结构(右)
diff --git a/docs/chapter05_CNN/5.12_densenet.md b/docs/chapter05_CNN/5.12_densenet.md
index 68f3d4a05..105ae6ca0 100644
--- a/docs/chapter05_CNN/5.12_densenet.md
+++ b/docs/chapter05_CNN/5.12_densenet.md
@@ -3,7 +3,7 @@
ResNet中的跨层连接设计引申出了数个后续工作。本节我们介绍其中的一个:稠密连接网络(DenseNet) [1]。 它与ResNet的主要区别如图5.10所示。
-
+
图5.10 ResNet(左)与DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结
diff --git a/docs/chapter05_CNN/5.1_conv-layer.md b/docs/chapter05_CNN/5.1_conv-layer.md
index 418a87fe5..6b2f6ef46 100644
--- a/docs/chapter05_CNN/5.1_conv-layer.md
+++ b/docs/chapter05_CNN/5.1_conv-layer.md
@@ -9,7 +9,7 @@
我们用一个具体例子来解释二维互相关运算的含义。如图5.1所示,输入是一个高和宽均为3的二维数组。我们将该数组的形状记为$3 \times 3$或(3,3)。核数组的高和宽分别为2。该数组在卷积计算中又称卷积核或过滤器(filter)。卷积核窗口(又称卷积窗口)的形状取决于卷积核的高和宽,即$2 \times 2$。图5.1中的阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:$0\times0+1\times1+3\times2+4\times3=19$。
-
+
图5.1 二维互相关运算
diff --git a/docs/chapter05_CNN/5.2_padding-and-strides.md b/docs/chapter05_CNN/5.2_padding-and-strides.md
index 3d632102a..9dc1998de 100644
--- a/docs/chapter05_CNN/5.2_padding-and-strides.md
+++ b/docs/chapter05_CNN/5.2_padding-and-strides.md
@@ -11,7 +11,7 @@ $$(n_h-k_h+1) \times (n_w-k_w+1).$$
填充(padding)是指在输入高和宽的两侧填充元素(通常是0元素)。图5.2里我们在原输入高和宽的两侧分别添加了值为0的元素,使得输入高和宽从3变成了5,并导致输出高和宽由2增加到4。图5.2中的阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:$0\times0+0\times1+0\times2+0\times3=0$。
-
+
图5.2 在输入的高和宽两侧分别填充了0元素的二维互相关计算
@@ -68,7 +68,7 @@ torch.Size([8, 8])
目前我们看到的例子里,在高和宽两个方向上步幅均为1。我们也可以使用更大步幅。图5.3展示了在高上步幅为3、在宽上步幅为2的二维互相关运算。可以看到,输出第一列第二个元素时,卷积窗口向下滑动了3行,而在输出第一行第二个元素时卷积窗口向右滑动了2列。当卷积窗口在输入上再向右滑动2列时,由于输入元素无法填满窗口,无结果输出。图5.3中的阴影部分为输出元素及其计算所使用的输入和核数组元素:$0\times0+0\times1+1\times2+2\times3=8$、$0\times0+6\times1+0\times2+0\times3=6$。
-
+
图5.3 高和宽上步幅分别为3和2的二维互相关运算
diff --git a/docs/chapter05_CNN/5.3_channels.md b/docs/chapter05_CNN/5.3_channels.md
index 041d15052..6e275dfdf 100644
--- a/docs/chapter05_CNN/5.3_channels.md
+++ b/docs/chapter05_CNN/5.3_channels.md
@@ -11,7 +11,7 @@
图5.4展示了含2个输入通道的二维互相关计算的例子。在每个通道上,二维输入数组与二维核数组做互相关运算,再按通道相加即得到输出。图5.4中阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:$(1\times1+2\times2+4\times3+5\times4)+(0\times0+1\times1+3\times2+4\times3)=56$。
-
+
图5.4 含2个输入通道的互相关计算
@@ -90,7 +90,7 @@ tensor([[[ 56., 72.],
最后我们讨论卷积窗口形状为$1\times 1$($k_h=k_w=1$)的多通道卷积层。我们通常称之为$1\times 1$卷积层,并将其中的卷积运算称为$1\times 1$卷积。因为使用了最小窗口,$1\times 1$卷积失去了卷积层可以识别高和宽维度上相邻元素构成的模式的功能。实际上,$1\times 1$卷积的主要计算发生在通道维上。图5.5展示了使用输入通道数为3、输出通道数为2的$1\times 1$卷积核的互相关计算。值得注意的是,输入和输出具有相同的高和宽。输出中的每个元素来自输入中在高和宽上相同位置的元素在不同通道之间的按权重累加。假设我们将通道维当作特征维,将高和宽维度上的元素当成数据样本,**那么$1\times 1$卷积层的作用与全连接层等价**。
-
+
图5.5 1x1卷积核的互相关计算。输入和输出具有相同的高和宽
diff --git a/docs/chapter05_CNN/5.4_pooling.md b/docs/chapter05_CNN/5.4_pooling.md
index 902e91136..43a36c443 100644
--- a/docs/chapter05_CNN/5.4_pooling.md
+++ b/docs/chapter05_CNN/5.4_pooling.md
@@ -9,7 +9,7 @@
同卷积层一样,池化层每次对输入数据的一个固定形状窗口(又称池化窗口)中的元素计算输出。不同于卷积层里计算输入和核的互相关性,池化层直接计算池化窗口内元素的最大值或者平均值。该运算也分别叫做最大池化或平均池化。在二维最大池化中,池化窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。当池化窗口滑动到某一位置时,窗口中的输入子数组的最大值即输出数组中相应位置的元素。
-
+
图5.6 池化窗口形状为 2 x 2 的最大池化
diff --git a/docs/chapter05_CNN/5.5_lenet.md b/docs/chapter05_CNN/5.5_lenet.md
index 1341ae046..b33c90dec 100644
--- a/docs/chapter05_CNN/5.5_lenet.md
+++ b/docs/chapter05_CNN/5.5_lenet.md
@@ -10,7 +10,7 @@
卷积神经网络就是含卷积层的网络。本节里我们将介绍一个早期用来识别手写数字图像的卷积神经网络:LeNet [1]。这个名字来源于LeNet论文的第一作者Yann LeCun。LeNet展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当时最先进的结果。这个奠基性的工作第一次将卷积神经网络推上舞台,为世人所知。LeNet的网络结构如下图所示。
-
+
LeNet网络结构
diff --git a/docs/chapter05_CNN/5.6_alexnet.md b/docs/chapter05_CNN/5.6_alexnet.md
index 47107d621..07c01a6e1 100644
--- a/docs/chapter05_CNN/5.6_alexnet.md
+++ b/docs/chapter05_CNN/5.6_alexnet.md
@@ -37,7 +37,7 @@
2012年,AlexNet横空出世。这个模型的名字来源于论文第一作者的姓名Alex Krizhevsky [1]。AlexNet使用了8层卷积神经网络,并以很大的优势赢得了ImageNet 2012图像识别挑战赛。它首次证明了学习到的特征可以超越手工设计的特征,从而一举打破计算机视觉研究的前状。
-
+
AlexNet网络结构
diff --git a/docs/chapter05_CNN/5.8_nin.md b/docs/chapter05_CNN/5.8_nin.md
index e3a9e8839..7d2e4f2f4 100644
--- a/docs/chapter05_CNN/5.8_nin.md
+++ b/docs/chapter05_CNN/5.8_nin.md
@@ -8,7 +8,7 @@
我们知道,卷积层的输入和输出通常是四维数组(样本,通道,高,宽),而全连接层的输入和输出则通常是二维数组(样本,特征)。如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。回忆在5.3节(多输入通道和多输出通道)里介绍的$1\times 1$卷积层。它可以看成全连接层,其中空间维度(高和宽)上的每个元素相当于样本,通道相当于特征。因此,NiN使用$1\times 1$卷积层来替代全连接层,从而使空间信息能够自然传递到后面的层中去。图5.7对比了NiN同AlexNet和VGG等网络在结构上的主要区别。
-
+
图5.7 左图是AlexNet和VGG的网络结构局部,右图是NiN的网络结构局部
diff --git a/docs/chapter05_CNN/5.9_googlenet.md b/docs/chapter05_CNN/5.9_googlenet.md
index 7f2d50b7c..e8374f048 100644
--- a/docs/chapter05_CNN/5.9_googlenet.md
+++ b/docs/chapter05_CNN/5.9_googlenet.md
@@ -8,7 +8,7 @@
GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与上一节介绍的NiN块相比,这个基础块在结构上更加复杂,如图5.8所示。
-
+
图5.8 Inception块的结构
diff --git a/docs/chapter06_RNN/6.10_bi-rnn.md b/docs/chapter06_RNN/6.10_bi-rnn.md
index fe68fc802..233f4c264 100644
--- a/docs/chapter06_RNN/6.10_bi-rnn.md
+++ b/docs/chapter06_RNN/6.10_bi-rnn.md
@@ -3,7 +3,7 @@
之前介绍的循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的,因此它们都将信息通过隐藏状态从前往后传递。有时候,当前时间步也可能由后面时间步决定。例如,当我们写下一个句子时,可能会根据句子后面的词来修改句子前面的用词。双向循环神经网络通过增加从后往前传递信息的隐藏层来更灵活地处理这类信息。图6.12演示了一个含单隐藏层的双向循环神经网络的架构。
-
+
图6.12 双向循环神经网络的架构
diff --git a/docs/chapter06_RNN/6.2_rnn.md b/docs/chapter06_RNN/6.2_rnn.md
index a55d54701..444bf542e 100644
--- a/docs/chapter06_RNN/6.2_rnn.md
+++ b/docs/chapter06_RNN/6.2_rnn.md
@@ -35,7 +35,7 @@ $$\boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q.$$
图6.1展示了循环神经网络在3个相邻时间步的计算逻辑。在时间步$t$,隐藏状态的计算可以看成是将输入$\boldsymbol{X}_t$和前一时间步隐藏状态$\boldsymbol{H}_{t-1}$连结后输入一个激活函数为$\phi$的全连接层。该全连接层的输出就是当前时间步的隐藏状态$\boldsymbol{H}_t$,且模型参数为$\boldsymbol{W}_{xh}$与$\boldsymbol{W}_{hh}$的连结,偏差为$\boldsymbol{b}_h$。当前时间步$t$的隐藏状态$\boldsymbol{H}_t$将参与下一个时间步$t+1$的隐藏状态$\boldsymbol{H}_{t+1}$的计算,并输入到当前时间步的全连接输出层。
-
+
图6.1 含隐藏状态的循环神经网络
@@ -72,7 +72,7 @@ tensor([[ 5.2633, -3.2288, 0.6037, -1.3321],
最后我们介绍如何应用循环神经网络来构建一个语言模型。设小批量中样本数为1,文本序列为“想”“要”“有”“直”“升”“机”。图6.2演示了如何使用循环神经网络基于当前和过去的字符来预测下一个字符。在训练时,我们对每个时间步的输出层输出使用softmax运算,然后使用交叉熵损失函数来计算它与标签的误差。在图6.2中,由于隐藏层中隐藏状态的循环计算,时间步3的输出$\boldsymbol{O}_3$取决于文本序列“想”“要”“有”。 由于训练数据中该序列的下一个词为“直”,时间步3的损失将取决于该时间步基于序列“想”“要”“有”生成下一个词的概率分布与该时间步的标签“直”。
-
+
图6.2 基于字符级循环神经网络的语言模型。
diff --git a/docs/chapter06_RNN/6.5_rnn-pytorch.md b/docs/chapter06_RNN/6.5_rnn-pytorch.md
index a64bb4ef5..f12a552c7 100644
--- a/docs/chapter06_RNN/6.5_rnn-pytorch.md
+++ b/docs/chapter06_RNN/6.5_rnn-pytorch.md
@@ -31,7 +31,7 @@ rnn_layer = nn.RNN(input_size=vocab_size, hidden_size=num_hiddens)
与上一节中实现的循环神经网络不同,这里`rnn_layer`的输入形状为(时间步数, 批量大小, 输入个数)。其中输入个数即one-hot向量长度(词典大小)。此外,`rnn_layer`作为`nn.RNN`实例,在前向计算后会分别返回输出和隐藏状态h,其中输出指的是隐藏层在**各个时间步**上计算并输出的隐藏状态,它们通常作为后续输出层的输入。需要强调的是,该“输出”本身并不涉及输出层计算,形状为(时间步数, 批量大小, 隐藏单元个数)。而`nn.RNN`实例在前向计算返回的隐藏状态指的是隐藏层在**最后时间步**的隐藏状态:当隐藏层有多层时,每一层的隐藏状态都会记录在该变量中;对于像长短期记忆(LSTM),隐藏状态是一个元组(h, c),即hidden state和cell state。我们会在本章的后面介绍长短期记忆和深度循环神经网络。关于循环神经网络(以LSTM为例)的输出,可以参考下图([图片来源](https://stackoverflow.com/questions/48302810/whats-the-difference-between-hidden-and-output-in-pytorch-lstm/48305882))。
-
+
循环神经网络(以LSTM为例)的输出
diff --git a/docs/chapter06_RNN/6.6_bptt.md b/docs/chapter06_RNN/6.6_bptt.md
index 471f0b703..e9bea26d8 100644
--- a/docs/chapter06_RNN/6.6_bptt.md
+++ b/docs/chapter06_RNN/6.6_bptt.md
@@ -33,7 +33,7 @@ $$
为了可视化循环神经网络中模型变量和参数在计算中的依赖关系,我们可以绘制模型计算图,如图6.3所示。例如,时间步3的隐藏状态$\boldsymbol{h}_3$的计算依赖模型参数$\boldsymbol{W}_{hx}$、$\boldsymbol{W}_{hh}$、上一时间步隐藏状态$\boldsymbol{h}_2$以及当前时间步输入$\boldsymbol{x}_3$。
-
+
图6.3 时间步数为3的循环神经网络模型计算中的依赖关系。方框代表变量(无阴影)或参数(有阴影),圆圈代表运算符
diff --git a/docs/chapter06_RNN/6.7_gru.md b/docs/chapter06_RNN/6.7_gru.md
index 2929a6ff4..e206a281b 100644
--- a/docs/chapter06_RNN/6.7_gru.md
+++ b/docs/chapter06_RNN/6.7_gru.md
@@ -14,7 +14,7 @@
如图6.4所示,门控循环单元中的重置门和更新门的输入均为当前时间步输入$\boldsymbol{X}_t$与上一时间步隐藏状态$\boldsymbol{H}_{t-1}$,输出由激活函数为sigmoid函数的全连接层计算得到。
-
+
图6.4 门控循环单元中重置门和更新门的计算
@@ -35,7 +35,7 @@ $$
接下来,门控循环单元将计算候选隐藏状态来辅助稍后的隐藏状态计算。如图6.5所示,我们将当前时间步重置门的输出与上一时间步隐藏状态做按元素乘法(符号为$\odot$)。如果重置门中元素值接近0,那么意味着重置对应隐藏状态元素为0,即丢弃上一时间步的隐藏状态。如果元素值接近1,那么表示保留上一时间步的隐藏状态。然后,将按元素乘法的结果与当前时间步的输入连结,再通过含激活函数tanh的全连接层计算出候选隐藏状态,其所有元素的值域为$[-1, 1]$。
-
+
图6.5 门控循环单元中候选隐藏状态的计算
@@ -52,7 +52,7 @@ $$\tilde{\boldsymbol{H}}_t = \text{tanh}(\boldsymbol{X}_t \boldsymbol{W}_{xh} +
$$\boldsymbol{H}_t = \boldsymbol{Z}_t \odot \boldsymbol{H}_{t-1} + (1 - \boldsymbol{Z}_t) \odot \tilde{\boldsymbol{H}}_t.$$
-
+
图6.6 门控循环单元中隐藏状态的计算
diff --git a/docs/chapter06_RNN/6.8_lstm.md b/docs/chapter06_RNN/6.8_lstm.md
index 8e548f58e..0e0eb1f84 100644
--- a/docs/chapter06_RNN/6.8_lstm.md
+++ b/docs/chapter06_RNN/6.8_lstm.md
@@ -12,7 +12,7 @@ LSTM 中引入了3个门,即输入门(input gate)、遗忘门(forget gat
与门控循环单元中的重置门和更新门一样,如图6.7所示,长短期记忆的门的输入均为当前时间步输入$\boldsymbol{X}_t$与上一时间步隐藏状态$\boldsymbol{H}_{t-1}$,输出由激活函数为sigmoid函数的全连接层计算得到。如此一来,这3个门元素的值域均为$[0,1]$。
-
+
图6.7 长短期记忆中输入门、遗忘门和输出门的计算
@@ -35,7 +35,7 @@ $$
接下来,长短期记忆需要计算候选记忆细胞$\tilde{\boldsymbol{C}}_t$。它的计算与上面介绍的3个门类似,但使用了值域在$[-1, 1]$的tanh函数作为激活函数,如图6.8所示。
-
+
图6.8 长短期记忆中候选记忆细胞的计算
@@ -59,7 +59,7 @@ $$\boldsymbol{C}_t = \boldsymbol{F}_t \odot \boldsymbol{C}_{t-1} + \boldsymbol{I
如图6.9所示,遗忘门控制上一时间步的记忆细胞$\boldsymbol{C}_{t-1}$中的信息是否传递到当前时间步,而输入门则控制当前时间步的输入$\boldsymbol{X}_t$通过候选记忆细胞$\tilde{\boldsymbol{C}}_t$如何流入当前时间步的记忆细胞。如果遗忘门一直近似1且输入门一直近似0,过去的记忆细胞将一直通过时间保存并传递至当前时间步。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。
-
+
图6.9 长短期记忆中记忆细胞的计算
@@ -73,7 +73,7 @@ $$\boldsymbol{H}_t = \boldsymbol{O}_t \odot \text{tanh}(\boldsymbol{C}_t).$$
这里的tanh函数确保隐藏状态元素值在-1到1之间。需要注意的是,当输出门近似1时,记忆细胞信息将传递到隐藏状态供输出层使用;当输出门近似0时,记忆细胞信息只自己保留。图6.10展示了长短期记忆中隐藏状态的计算。
-
+
图6.10 长短期记忆中隐藏状态的计算
diff --git a/docs/chapter06_RNN/6.9_deep-rnn.md b/docs/chapter06_RNN/6.9_deep-rnn.md
index 4124e0378..9ab4f7132 100644
--- a/docs/chapter06_RNN/6.9_deep-rnn.md
+++ b/docs/chapter06_RNN/6.9_deep-rnn.md
@@ -3,7 +3,7 @@
本章到目前为止介绍的循环神经网络只有一个单向的隐藏层,在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。图6.11演示了一个有$L$个隐藏层的深度循环神经网络,每个隐藏状态不断传递至当前层的下一时间步和当前时间步的下一层。
-
+
图6.11 深度循环神经网络的架构
diff --git a/docs/chapter07_optimization/7.1_optimization-intro.md b/docs/chapter07_optimization/7.1_optimization-intro.md
index 706d71a2b..f4ec093a7 100644
--- a/docs/chapter07_optimization/7.1_optimization-intro.md
+++ b/docs/chapter07_optimization/7.1_optimization-intro.md
@@ -55,7 +55,7 @@ d2l.plt.ylabel('f(x)');
```
-
+
@@ -80,7 +80,7 @@ d2l.plt.xlabel('x')
d2l.plt.ylabel('f(x)');
```
-
+
再举个定义在二维空间的函数的例子,例如:
@@ -104,7 +104,7 @@ d2l.plt.xlabel('x')
d2l.plt.ylabel('y');
```
-
+
在图的鞍点位置,目标函数在$x$轴方向上是局部最小值,但在$y$轴方向上是局部最大值。
diff --git a/docs/chapter07_optimization/7.2_gd-sgd.md b/docs/chapter07_optimization/7.2_gd-sgd.md
index 79ccf70fb..04582a4e5 100644
--- a/docs/chapter07_optimization/7.2_gd-sgd.md
+++ b/docs/chapter07_optimization/7.2_gd-sgd.md
@@ -70,7 +70,7 @@ def show_trace(res):
show_trace(res)
```
-
+
## 7.2.2 学习率
@@ -87,7 +87,7 @@ show_trace(gd(0.05))
epoch 10, x: 3.4867844009999995
```
-
+
如果使用过大的学习率,$\left|\eta f'(x)\right|$可能会过大从而使前面提到的一阶泰勒展开公式不再成立:这时我们无法保证迭代$x$会降低$f(x)$的值。
@@ -102,7 +102,7 @@ show_trace(gd(1.1))
epoch 10, x: 61.917364224000096
```
-
+
## 7.2.3 多维梯度下降
@@ -167,7 +167,7 @@ show_trace_2d(f_2d, train_2d(gd_2d))
epoch 20, x1 -0.057646, x2 -0.000073
```
-
+
## 7.2.4 随机梯度下降
@@ -206,7 +206,7 @@ show_trace_2d(f_2d, train_2d(sgd_2d))
epoch 20, x1 -0.047150, x2 -0.075628
```
-
+
可以看到,随机梯度下降中自变量的迭代轨迹相对于梯度下降中的来说更为曲折。这是由于实验所添加的噪声使模拟的随机梯度的准确度下降。在实际中,这些噪声通常指训练数据集中的无意义的干扰。
diff --git a/docs/chapter07_optimization/7.3_minibatch-sgd.md b/docs/chapter07_optimization/7.3_minibatch-sgd.md
index e8006d11a..b997ba072 100644
--- a/docs/chapter07_optimization/7.3_minibatch-sgd.md
+++ b/docs/chapter07_optimization/7.3_minibatch-sgd.md
@@ -108,7 +108,7 @@ train_sgd(1, 1500, 6)
loss: 0.243605, 0.014335 sec per epoch
```
-
+
当批量大小为1时,优化使用的是随机梯度下降。为了简化实现,有关(小批量)随机梯度下降的实验中,我们未对学习率进行自我衰减,而是直接采用较小的常数学习率。随机梯度下降中,每处理一个样本会更新一次自变量(模型参数),一个迭代周期里会对自变量进行1,500次更新。可以看到,目标函数值的下降在1个迭代周期后就变得较为平缓。
@@ -121,7 +121,7 @@ train_sgd(0.005, 1)
loss: 0.243433, 0.270011 sec per epoch
```
-
+
@@ -137,7 +137,7 @@ train_sgd(0.05, 10)
loss: 0.242805, 0.078792 sec per epoch
```
-
+
## 7.3.3 简洁实现
@@ -190,7 +190,7 @@ train_pytorch_ch7(optim.SGD, {"lr": 0.05}, features, labels, 10)
loss: 0.245491, 0.044150 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter07_optimization/7.4_momentum.md b/docs/chapter07_optimization/7.4_momentum.md
index c506debc5..b22dffb0b 100644
--- a/docs/chapter07_optimization/7.4_momentum.md
+++ b/docs/chapter07_optimization/7.4_momentum.md
@@ -30,7 +30,7 @@ epoch 20, x1 -0.943467, x2 -0.000073
```
-
+
可以看到,同一位置上,目标函数在竖直方向($x_2$轴方向)比在水平方向($x_1$轴方向)的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢。
@@ -46,7 +46,7 @@ d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1 -0.387814, x2 -1673.365109
```
-
+
## 7.4.2 动量法
@@ -79,7 +79,7 @@ d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1 -0.062843, x2 0.001202
```
-
+
可以看到使用较小的学习率$\eta=0.4$和动量超参数$\gamma=0.5$时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。下面使用较大的学习率$\eta=0.6$,此时自变量也不再发散。
@@ -93,7 +93,7 @@ d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1 0.007188, x2 0.002553
```
-
+
### 7.4.2.1 指数加权移动平均
@@ -163,7 +163,7 @@ d2l.train_ch7(sgd_momentum, init_momentum_states(),
loss: 0.245518, 0.042304 sec per epoch
```
-
+
将动量超参数`momentum`增大到0.9,这时依然可以看成是特殊的小批量随机梯度下降:其小批量随机梯度为最近10个时间步的10倍小批量梯度的加权平均。我们先保持学习率0.02不变。
@@ -178,7 +178,7 @@ d2l.train_ch7(sgd_momentum, init_momentum_states(),
loss: 0.252046, 0.095708 sec per epoch
```
-
+
可见目标函数值在后期迭代过程中的变化不够平滑。直觉上,10倍小批量梯度比2倍小批量梯度大了5倍,我们可以试着将学习率减小到原来的1/5。此时目标函数值在下降了一段时间后变化更加平滑。
@@ -193,7 +193,7 @@ d2l.train_ch7(sgd_momentum, init_momentum_states(),
loss: 0.242905, 0.073496 sec per epoch
```
-
+
## 7.4.4 简洁实现
@@ -209,7 +209,7 @@ d2l.train_pytorch_ch7(torch.optim.SGD, {'lr': 0.004, 'momentum': 0.9},
loss: 0.253280, 0.060247 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter07_optimization/7.5_adagrad.md b/docs/chapter07_optimization/7.5_adagrad.md
index 3b75881eb..12b95d282 100644
--- a/docs/chapter07_optimization/7.5_adagrad.md
+++ b/docs/chapter07_optimization/7.5_adagrad.md
@@ -56,7 +56,7 @@ epoch 20, x1 -2.382563, x2 -0.158591
```
-
+
下面将学习率增大到2。可以看到自变量更为迅速地逼近了最优解。
@@ -70,7 +70,7 @@ d2l.show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))
epoch 20, x1 -0.002295, x2 -0.000000
```
-
+
## 7.5.3 从零开始实现
@@ -104,7 +104,7 @@ loss: 0.243675, 0.049749 sec per epoch
```
-
+
## 7.5.4 简洁实现
@@ -120,7 +120,7 @@ loss: 0.243147, 0.040675 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter07_optimization/7.6_rmsprop.md b/docs/chapter07_optimization/7.6_rmsprop.md
index 5d1361d6c..d83b306d3 100644
--- a/docs/chapter07_optimization/7.6_rmsprop.md
+++ b/docs/chapter07_optimization/7.6_rmsprop.md
@@ -44,7 +44,7 @@ d2l.show_trace_2d(f_2d, d2l.train_2d(rmsprop_2d))
epoch 20, x1 -0.010599, x2 0.000000
```
-
+
## 7.6.2 从零开始实现
@@ -78,7 +78,7 @@ d2l.train_ch7(rmsprop, init_rmsprop_states(), {'lr': 0.01, 'gamma': 0.9},
loss: 0.243452, 0.049984 sec per epoch
```
-
+
## 7.6.3 简洁实现
@@ -95,7 +95,7 @@ d2l.train_pytorch_ch7(torch.optim.RMSprop, {'lr': 0.01, 'alpha': 0.9},
loss: 0.243676, 0.043637 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter07_optimization/7.7_adadelta.md b/docs/chapter07_optimization/7.7_adadelta.md
index 3a0c8609b..6c2eaf956 100644
--- a/docs/chapter07_optimization/7.7_adadelta.md
+++ b/docs/chapter07_optimization/7.7_adadelta.md
@@ -60,7 +60,7 @@ d2l.train_ch7(adadelta, init_adadelta_states(), {'rho': 0.9}, features, labels)
loss: 0.243728, 0.062991 sec per epoch
```
-
+
## 7.7.3 简洁实现
@@ -76,7 +76,7 @@ d2l.train_pytorch_ch7(torch.optim.Adadelta, {'rho': 0.9}, features, labels)
loss: 0.242104, 0.047702 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter07_optimization/7.8_adam.md b/docs/chapter07_optimization/7.8_adam.md
index ecf3ae9fa..04988ef1b 100644
--- a/docs/chapter07_optimization/7.8_adam.md
+++ b/docs/chapter07_optimization/7.8_adam.md
@@ -70,7 +70,7 @@ d2l.train_ch7(adam, init_adam_states(), {'lr': 0.01, 't': 1}, features, labels)
loss: 0.245370, 0.065155 sec per epoch
```
-
+
@@ -86,7 +86,7 @@ d2l.train_pytorch_ch7(torch.optim.Adam, {'lr': 0.01}, features, labels)
loss: 0.242066, 0.056867 sec per epoch
```
-
+
## 小结
diff --git a/docs/chapter09_computer-vision/9.1_image-augmentation.md b/docs/chapter09_computer-vision/9.1_image-augmentation.md
index 6b62535c1..1bde894ea 100644
--- a/docs/chapter09_computer-vision/9.1_image-augmentation.md
+++ b/docs/chapter09_computer-vision/9.1_image-augmentation.md
@@ -25,7 +25,7 @@ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
``` python
d2l.set_figsize()
-img = Image.open('../../img/cat1.jpg')
+img = Image.open('../img/cat1.jpg')
d2l.plt.imshow(img)
```
@@ -52,7 +52,7 @@ def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):
show_images(Y, num_rows, num_cols, scale)
```
-
+
### 9.1.1.1 翻转和裁剪
@@ -63,7 +63,7 @@ def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):
apply(img, torchvision.transforms.RandomHorizontalFlip())
```
-
+
上下翻转不如左右翻转通用。但是至少对于样例图像,上下翻转不会造成识别障碍。下面我们创建`RandomVerticalFlip`实例来实现一半概率的图像垂直(上下)翻转。
@@ -72,7 +72,7 @@ apply(img, torchvision.transforms.RandomHorizontalFlip())
apply(img, torchvision.transforms.RandomVerticalFlip())
```
-
+
在我们使用的样例图像里,猫在图像正中间,但一般情况下可能不是这样。在5.4节(池化层)里我们解释了池化层能降低卷积层对目标位置的敏感度。除此之外,我们还可以通过对图像随机裁剪来让物体以不同的比例出现在图像的不同位置,这同样能够降低模型对目标位置的敏感性。
@@ -84,7 +84,7 @@ shape_aug = torchvision.transforms.RandomResizedCrop(200, scale=(0.1, 1), ratio=
apply(img, shape_aug)
```
-
+
### 9.1.1.2 变化颜色
@@ -95,7 +95,7 @@ apply(img, shape_aug)
apply(img, torchvision.transforms.ColorJitter(brightness=0.5))
```
-
+
我们也可以随机变化图像的色调。
@@ -103,7 +103,7 @@ apply(img, torchvision.transforms.ColorJitter(brightness=0.5))
apply(img, torchvision.transforms.ColorJitter(hue=0.5))
```
-
+
类似地,我们也可以随机变化图像的对比度。
@@ -111,7 +111,7 @@ apply(img, torchvision.transforms.ColorJitter(hue=0.5))
apply(img, torchvision.transforms.ColorJitter(contrast=0.5))
```
-
+
我们也可以同时设置如何随机变化图像的亮度(`brightness`)、对比度(`contrast`)、饱和度(`saturation`)和色调(`hue`)。
@@ -122,7 +122,7 @@ color_aug = torchvision.transforms.ColorJitter(
apply(img, color_aug)
```
-
+
### 9.1.1.3 叠加多个图像增广方法
@@ -135,7 +135,7 @@ augs = torchvision.transforms.Compose([
apply(img, augs)
```
-
+
## 9.1.2 使用图像增广训练模型
@@ -148,7 +148,7 @@ all_imges = torchvision.datasets.CIFAR10(train=True, root="~/Datasets/CIFAR", do
show_images([all_imges[i][0] for i in range(32)], 4, 8, scale=0.8);
```
-
+
**为了在预测时得到确定的结果,我们通常只将图像增广应用在训练样本上,而不在预测时使用含随机操作的图像增广**。在这里我们只使用最简单的随机左右翻转。此外,我们使用`ToTensor`将小批量图像转成PyTorch需要的格式,即形状为(批量大小, 通道数, 高, 宽)、值域在0到1之间且类型为32位浮点数。
diff --git a/docs/chapter09_computer-vision/9.2_fine-tuning.md b/docs/chapter09_computer-vision/9.2_fine-tuning.md
index 41bf20600..8411741c5 100644
--- a/docs/chapter09_computer-vision/9.2_fine-tuning.md
+++ b/docs/chapter09_computer-vision/9.2_fine-tuning.md
@@ -16,7 +16,7 @@
4. 在目标数据集(如椅子数据集)上训练目标模型。我们将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。
-
+
图9.1 微调
@@ -73,7 +73,7 @@ not_hotdogs = [train_imgs[-i - 1][0] for i in range(8)]
d2l.show_images(hotdogs + not_hotdogs, 2, 8, scale=1.4);
```
-
+
在训练时,我们先从图像中裁剪出随机大小和随机高宽比的一块随机区域,然后将该区域缩放为高和宽均为224像素的输入。测试时,我们将图像的高和宽均缩放为256像素,然后从中裁剪出高和宽均为224像素的中心区域作为输入。此外,我们对RGB(红、绿、蓝)三个颜色通道的数值做标准化:每个数值减去该通道所有数值的平均值,再除以该通道所有数值的标准差作为输出。
diff --git a/docs/chapter09_computer-vision/9.3_bounding-box.md b/docs/chapter09_computer-vision/9.3_bounding-box.md
index 8511466ee..b0d3464aa 100644
--- a/docs/chapter09_computer-vision/9.3_bounding-box.md
+++ b/docs/chapter09_computer-vision/9.3_bounding-box.md
@@ -19,11 +19,11 @@ import d2lzh_pytorch as d2l
``` python
d2l.set_figsize()
-img = Image.open('../../img/catdog.jpg')
+img = Image.open('../img/catdog.jpg')
d2l.plt.imshow(img); # 加分号只显示图
```
-
+
## 9.3.1 边界框
@@ -55,7 +55,7 @@ fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red'));
```
输出:
-
+
## 小结
diff --git a/docs/chapter09_computer-vision/9.4_anchor.md b/docs/chapter09_computer-vision/9.4_anchor.md
index c7b25fdb6..19af929c1 100644
--- a/docs/chapter09_computer-vision/9.4_anchor.md
+++ b/docs/chapter09_computer-vision/9.4_anchor.md
@@ -34,7 +34,7 @@ $$(s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s
``` python
d2l.set_figsize()
-img = Image.open('../../img/catdog.jpg')
+img = Image.open('../img/catdog.jpg')
w, h = img.size
print("w = %d, h = %d" % (w, h)) # w = 728, h = 561
@@ -128,7 +128,7 @@ show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,
['s=0.75, r=1', 's=0.75, r=2', 's=0.55, r=0.5', 's=0.5, r=1', 's=0.25, r=1'])
```
-
+
## 9.4.2 交并比
@@ -140,7 +140,7 @@ $$J(\mathcal{A},\mathcal{B}) = \frac{\left|\mathcal{A} \cap \mathcal{B}\right|}{
实际上,我们可以把边界框内的像素区域看成是像素的集合。如此一来,我们可以用两个边界框的像素集合的Jaccard系数衡量这两个边界框的相似度。当衡量两个边界框的相似度时,我们通常将Jaccard系数称为交并比(Intersection over Union,IoU),即两个边界框相交面积与相并面积之比,如图9.2所示。交并比的取值范围在0和1之间:0表示两个边界框无重合像素,1表示两个边界框相等。
-
+
图9.2 交并比是两个边界框相交面积与相并面积之比
@@ -208,7 +208,7 @@ def compute_jaccard(set_1, set_2):
如图9.3(左)所示,假设矩阵$\boldsymbol{X}$中最大值为$x_{23}$,我们将为锚框$A_2$分配真实边界框$B_3$。然后,丢弃矩阵中第2行和第3列的所有元素,找出剩余阴影部分的最大元素$x_{71}$,为锚框$A_7$分配真实边界框$B_1$。接着如图9.3(中)所示,丢弃矩阵中第7行和第1列的所有元素,找出剩余阴影部分的最大元素$x_{54}$,为锚框$A_5$分配真实边界框$B_4$。最后如图9.3(右)所示,丢弃矩阵中第5行和第4列的所有元素,找出剩余阴影部分的最大元素$x_{92}$,为锚框$A_9$分配真实边界框$B_2$。之后,我们只需遍历除去$A_2, A_5, A_7, A_9$的剩余锚框,并根据阈值判断是否为剩余锚框分配真实边界框。
-
+
图9.3 为锚框分配真实边界框
@@ -239,7 +239,7 @@ show_bboxes(fig.axes, ground_truth[:, 1:] * bbox_scale, ['dog', 'cat'], 'k')
show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']);
```
-
+
@@ -428,7 +428,7 @@ show_bboxes(fig.axes, anchors * bbox_scale,
['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9'])
```
-
+
@@ -559,7 +559,7 @@ for i in output[0].detach().cpu().numpy():
show_bboxes(fig.axes, [torch.tensor(i[2:]) * bbox_scale], label)
```
-
+
实践中,我们可以在执行非极大值抑制前将置信度较低的预测边界框移除,从而减小非极大值抑制的计算量。我们还可以筛选非极大值抑制的输出,例如,只保留其中置信度较高的结果作为最终输出。
diff --git a/docs/chapter10_natural-language-processing/10.10_beam-search.md b/docs/chapter10_natural-language-processing/10.10_beam-search.md
index 848e7ab38..553ea1100 100644
--- a/docs/chapter10_natural-language-processing/10.10_beam-search.md
+++ b/docs/chapter10_natural-language-processing/10.10_beam-search.md
@@ -20,7 +20,7 @@ $$
下面来看一个例子。假设输出词典里面有“A”“B”“C”和“<eos>”这4个词。图10.9中每个时间步下的4个数字分别代表了该时间步生成“A”“B”“C”和“<eos>”这4个词的条件概率。在每个时间步,贪婪搜索选取条件概率最大的词。因此,图10.9中将生成输出序列“A”“B”“C”“<eos>”。该输出序列的条件概率是$0.5\times0.4\times0.4\times0.6 = 0.048$。
-
+
图10.9 在每个时间步,贪婪搜索选取条件概率最大的词
@@ -28,7 +28,7 @@ $$
接下来,观察图10.10演示的例子。与图10.9中不同,图10.10在时间步2中选取了条件概率第二大的词“C”。由于时间步3所基于的时间步1和2的输出子序列由图10.9中的“A”“B”变为了图10.10中的“A”“C”,图10.10中时间步3生成各个词的条件概率发生了变化。我们选取条件概率最大的词“B”。此时时间步4所基于的前3个时间步的输出子序列为“A”“C”“B”,与图10.9中的“A”“B”“C”不同。因此,图10.10中时间步4生成各个词的条件概率也与图10.9中的不同。我们发现,此时的输出序列“A”“C”“B”“<eos>”的条件概率是$0.5\times0.3\times0.6\times0.6=0.054$,大于贪婪搜索得到的输出序列的条件概率。因此,贪婪搜索得到的输出序列“A”“B”“C”“<eos>”并非最优输出序列。
-
+
图10.10 在时间步2选取条件概率第二大的词“C”
@@ -45,7 +45,7 @@ $$
束搜索(beam search)是对贪婪搜索的一个改进算法。它有一个束宽(beam size)超参数。我们将它设为$k$。在时间步1时,选取当前时间步条件概率最大的$k$个词,分别组成$k$个候选输出序列的首词。在之后的每个时间步,基于上个时间步的$k$个候选输出序列,从$k\left|\mathcal{Y}\right|$个可能的输出序列中选取条件概率最大的$k$个,作为该时间步的候选输出序列。最终,我们从各个时间步的候选输出序列中筛选出包含特殊符号“<eos>”的序列,并将它们中所有特殊符号“<eos>”后面的子序列舍弃,得到最终候选输出序列的集合。
-
+
图10.11 束搜索的过程。束宽为2,输出序列最大长度为3。候选输出序列有A、C、AB、CE、ABD和CED
diff --git a/docs/chapter10_natural-language-processing/10.11_attention.md b/docs/chapter10_natural-language-processing/10.11_attention.md
index 410a39547..cbabbe979 100644
--- a/docs/chapter10_natural-language-processing/10.11_attention.md
+++ b/docs/chapter10_natural-language-processing/10.11_attention.md
@@ -19,7 +19,7 @@ $$\boldsymbol{s}_{t'} = g(\boldsymbol{y}_{t'-1}, \boldsymbol{c}_{t'}, \boldsymbo
我们先描述第一个关键点,即计算背景变量。图10.12描绘了注意力机制如何为解码器在时间步2计算背景变量。首先,函数$a$根据解码器在时间步1的隐藏状态和编码器在各个时间步的隐藏状态计算softmax运算的输入。softmax运算输出概率分布并对编码器各个时间步的隐藏状态做加权平均,从而得到背景变量。
-
+
图10.12 编码器—解码器上的注意力机制
diff --git a/docs/chapter10_natural-language-processing/10.1_word2vec.md b/docs/chapter10_natural-language-processing/10.1_word2vec.md
index 22d0f6380..08791d125 100644
--- a/docs/chapter10_natural-language-processing/10.1_word2vec.md
+++ b/docs/chapter10_natural-language-processing/10.1_word2vec.md
@@ -30,7 +30,7 @@ $$P(\textrm{``the"}\mid\textrm{``loves"})\cdot P(\textrm{``man"}\mid\textrm{``lo
-
+
图10.1 跳字模型关心给定中心词生成背景词的条件概率
@@ -81,7 +81,7 @@ $$P(\textrm{``loves"}\mid\textrm{``the"},\textrm{``man"},\textrm{``his"},\textrm
-
+
图10.2 连续词袋模型关心给定背景词生成中心词的条件概率
diff --git a/docs/chapter10_natural-language-processing/10.2_approx-training.md b/docs/chapter10_natural-language-processing/10.2_approx-training.md
index 865927feb..9ede21a51 100644
--- a/docs/chapter10_natural-language-processing/10.2_approx-training.md
+++ b/docs/chapter10_natural-language-processing/10.2_approx-training.md
@@ -56,7 +56,7 @@ $$
层序softmax是另一种近似训练法。它使用了二叉树这一数据结构,树的每个叶结点代表词典$\mathcal{V}$中的每个词。
-
+
图10.3 层序softmax。二叉树的每个叶结点代表着词典的每个词
diff --git a/docs/chapter10_natural-language-processing/10.8_sentiment-analysis-cnn.md b/docs/chapter10_natural-language-processing/10.8_sentiment-analysis-cnn.md
index 3d5395432..92d66add0 100644
--- a/docs/chapter10_natural-language-processing/10.8_sentiment-analysis-cnn.md
+++ b/docs/chapter10_natural-language-processing/10.8_sentiment-analysis-cnn.md
@@ -27,7 +27,7 @@ DATA_ROOT = "/S1/CSCL/tangss/Datasets"
在介绍模型前我们先来解释一维卷积层的工作原理。与二维卷积层一样,一维卷积层使用一维的互相关运算。在一维互相关运算中,卷积窗口从输入数组的最左方开始,按从左往右的顺序,依次在输入数组上滑动。当卷积窗口滑动到某一位置时,窗口中的输入子数组与核数组按元素相乘并求和,得到输出数组中相应位置的元素。如图10.4所示,输入是一个宽为7的一维数组,核数组的宽为2。可以看到输出的宽度为$7-2+1=6$,且第一个元素是由输入的最左边的宽为2的子数组与核数组按元素相乘后再相加得到的:$0\times1+1\times2=2$。
-
+
图10.4 一维互相关运算
@@ -56,7 +56,7 @@ tensor([ 2., 5., 8., 11., 14., 17.])
多输入通道的一维互相关运算也与多输入通道的二维互相关运算类似:在每个通道上,将核与相应的输入做一维互相关运算,并将通道之间的结果相加得到输出结果。图10.5展示了含3个输入通道的一维互相关运算,其中阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:$0\times1+1\times2+1\times3+2\times4+2\times(-1)+3\times(-3)=2$。
-
+
图10.5 含3个输入通道的一维互相关运算
@@ -81,7 +81,7 @@ tensor([ 2., 8., 14., 20., 26., 32.])
由二维互相关运算的定义可知,多输入通道的一维互相关运算可以看作单输入通道的二维互相关运算。如图10.6所示,我们也可以将图10.5中多输入通道的一维互相关运算以等价的单输入通道的二维互相关运算呈现。这里核的高等于输入的高。图10.6中的阴影部分为第一个输出元素及其计算所使用的输入和核数组元素:$2\times(-1)+3\times(-3)+1\times3+2\times4+0\times1+1\times2=2$。
-
+
图10.6 单输入通道的二维互相关运算
@@ -131,7 +131,7 @@ textCNN模型主要使用了一维卷积层和时序最大池化层。假设输
图10.7用一个例子解释了textCNN的设计。这里的输入是一个有11个词的句子,每个词用6维词向量表示。因此输入序列的宽为11,输入通道数为6。给定2个一维卷积核,核宽分别为2和4,输出通道数分别设为4和5。因此,一维卷积计算后,4个输出通道的宽为$11-2+1=10$,而其他5个通道的宽为$11-4+1=8$。尽管每个通道的宽不同,我们依然可以对各个通道做时序最大池化,并将9个通道的池化输出连结成一个9维向量。最终,使用全连接将9维向量变换为2维输出,即正面情感和负面情感的预测。
-
+
图10.7 textCNN的设计
diff --git a/docs/chapter10_natural-language-processing/10.9_seq2seq.md b/docs/chapter10_natural-language-processing/10.9_seq2seq.md
index 720fc2238..027fe4ff4 100644
--- a/docs/chapter10_natural-language-processing/10.9_seq2seq.md
+++ b/docs/chapter10_natural-language-processing/10.9_seq2seq.md
@@ -11,7 +11,7 @@
图10.8描述了使用编码器—解码器将上述英语句子翻译成法语句子的一种方法。在训练数据集中,我们可以在每个句子后附上特殊符号“<eos>”(end of sequence)以表示序列的终止。编码器每个时间步的输入依次为英语句子中的单词、标点和特殊符号“<eos>”。图10.8中使用了编码器在最终时间步的隐藏状态作为输入句子的表征或编码信息。解码器在各个时间步中使用输入句子的编码信息和上个时间步的输出以及隐藏状态作为输入。我们希望解码器在各个时间步能正确依次输出翻译后的法语单词、标点和特殊符号"<eos>"。需要注意的是,解码器在最初时间步的输入用到了一个表示序列开始的特殊符号"<bos>"(beginning of sequence)。
-
+
图10.8 使用编码器—解码器将句子由英语翻译成法语。编码器和解码器分别为循环神经网络