Skip to content

Commit

Permalink
rnn-concise polish done
Browse files Browse the repository at this point in the history
  • Loading branch information
goldmermaid committed Nov 13, 2021
1 parent 249e848 commit 2529851
Showing 1 changed file with 36 additions and 16 deletions.
52 changes: 36 additions & 16 deletions chapter_recurrent-neural-networks/rnn-concise.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# 循环神经网络的简洁实现
:label:`sec_rnn-concise`

虽然 :numref:`sec_rnn_scratch`对了解循环神经网络的实现方式具有指导意义,但并不方便。本节将展示如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。我们仍然从读取时光机器数据集开始。
虽然 :numref:`sec_rnn_scratch`
对了解循环神经网络的实现方式具有指导意义,但并不方便。
本节将展示如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。
我们仍然从读取时光机器数据集开始。

```{.python .input}
from d2l import mxnet as d2l
Expand Down Expand Up @@ -34,7 +37,10 @@ train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

## [**定义模型**]

高级API提供了循环神经网络的实现。我们构造了一个具有256个隐藏单元的单隐藏层的循环神经网络层`rnn_layer`。事实上,我们还没有讨论多层的意义——这将在 :numref:`sec_deep_rnn`中介绍。现在,仅需要将多层理解为一层循环神经网络的输出被用作下一层循环神经网络的输入就足够了。
高级API提供了循环神经网络的实现。
我们构造一个具有256个隐藏单元的单隐藏层的循环神经网络层`rnn_layer`
事实上,我们还没有讨论多层循环神经网络的意义(这将在 :numref:`sec_deep_rnn`中介绍)。
现在,你仅需要将多层理解为一层循环神经网络的输出被用作下一层循环神经网络的输入就足够了。

```{.python .input}
num_hiddens = 256
Expand All @@ -58,7 +64,10 @@ rnn_layer = tf.keras.layers.RNN(rnn_cell, time_major=True,
```

:begin_tab:`mxnet`
初始化隐状态是简单的,只需要调用成员函数`begin_state`即可。函数将返回一个列表(`state`),列表中包含了初始隐状态用于小批量数据中的每个样本,其形状为(隐藏层数,批量大小,隐藏单元数)。对于以后要介绍的一些模型(例如长-短期记忆网络),这样的列表还会包含其他信息。
初始化隐状态是简单的,只需要调用成员函数`begin_state`即可。
函数将返回一个列表(`state`),列表中包含了初始隐状态用于小批量数据中的每个样本,
其形状为(隐藏层数,批量大小,隐藏单元数)。
对于以后要介绍的一些模型(例如长-短期记忆网络),这样的列表还会包含其他信息。
:end_tab:

:begin_tab:`pytorch`
Expand All @@ -82,10 +91,16 @@ state = rnn_cell.get_initial_state(batch_size=batch_size, dtype=tf.float32)
state.shape
```

[**通过一个隐状态和一个输入,我们就可以用更新后的隐状态计算输出。**]需要强调的是,`rnn_layer`的“输出”(`Y`)不涉及输出层的计算:它是指每个时间步的隐状态,这些隐状态可以用作后续输出层的输入。
[**通过一个隐状态和一个输入,我们就可以用更新后的隐状态计算输出。**]
需要强调的是,`rnn_layer`的“输出”(`Y`)不涉及输出层的计算:
它是指每个时间步的隐状态,这些隐状态可以用作后续输出层的输入。

:begin_tab:`mxnet`
此外,`rnn_layer`返回的更新后的隐状态(`state_new`)是指小批量数据的最后时间步的隐状态。这个隐状态可以用来初始化顺序分区中一个迭代周期内下一个小批量数据的隐状态。对于多个隐藏层,每一层的隐状态将存储在(`state_new`)变量中。至于稍后要介绍的某些模型(例如,长-短期记忆),此变量还包含其他信息。
此外,`rnn_layer`返回的更新后的隐状态(`state_new`
是指小批量数据的最后时间步的隐状态。
这个隐状态可以用来初始化顺序分区中一个迭代周期内下一个小批量数据的隐状态。
对于多个隐藏层,每一层的隐状态将存储在(`state_new`)变量中。
至于稍后要介绍的某些模型(例如,长-短期记忆),此变量还包含其他信息。
:end_tab:

```{.python .input}
Expand All @@ -108,7 +123,9 @@ Y, state_new = rnn_layer(X, state)
Y.shape, len(state_new), state_new[0].shape
```

与 :numref:`sec_rnn_scratch`类似,[**我们为一个完整的循环神经网络模型定义了一个`RNNModel`**]。注意,`rnn_layer`只包含隐藏的循环层,我们还需要创建一个单独的输出层。
与 :numref:`sec_rnn_scratch`类似,
[**我们为一个完整的循环神经网络模型定义了一个`RNNModel`**]
注意,`rnn_layer`只包含隐藏的循环层,我们还需要创建一个单独的输出层。

```{.python .input}
#@save
Expand All @@ -123,8 +140,8 @@ class RNNModel(nn.Block):
def forward(self, inputs, state):
X = npx.one_hot(inputs.T, self.vocab_size)
Y, state = self.rnn(X, state)
# 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)
# 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)
# 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)
# 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)
output = self.dense(Y.reshape(-1, Y.shape[-1]))
return output, state
Expand All @@ -142,7 +159,7 @@ class RNNModel(nn.Module):
self.rnn = rnn_layer
self.vocab_size = vocab_size
self.num_hiddens = self.rnn.hidden_size
# 如果RNN是双向的(之后将介绍),`num_directions`应该是2,否则应该是1
# 如果RNN是双向的(之后将介绍),`num_directions`应该是2,否则应该是1
if not self.rnn.bidirectional:
self.num_directions = 1
self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
Expand All @@ -154,7 +171,7 @@ class RNNModel(nn.Module):
X = F.one_hot(inputs.T.long(), self.vocab_size)
X = X.to(torch.float32)
Y, state = self.rnn(X, state)
# 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)
# 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)
# 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)。
output = self.linear(Y.reshape((-1, Y.shape[-1])))
return output, state
Expand Down Expand Up @@ -225,7 +242,9 @@ with strategy.scope():
d2l.predict_ch8('time traveller', 10, net, vocab)
```

很明显,这种模型根本不能输出好的结果。接下来,我们使用 :numref:`sec_rnn_scratch`中定义的超参数调用`train_ch8`,并且[**使用高级API训练模型**]
很明显,这种模型根本不能输出好的结果。
接下来,我们使用 :numref:`sec_rnn_scratch`
定义的超参数调用`train_ch8`,并且[**使用高级API训练模型**]

```{.python .input}
num_epochs, lr = 500, 1
Expand All @@ -244,19 +263,20 @@ num_epochs, lr = 500, 1
d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, strategy)
```

与上一节相比,由于深度学习框架的高级API对代码进行了更多的优化,该模型在较短的时间内实现了较低的困惑度。
与上一节相比,由于深度学习框架的高级API对代码进行了更多的优化,
该模型在较短的时间内达到了较低的困惑度。

## 小结

* 深度学习框架的高级API提供了循环神经网络层的实现。
* 高级API的循环神经网络层返回一个输出和一个更新后的隐状态,这个输出不涉及输出层的计算
* 使用高级API实现的循环神经网络相比从零开始实现可以得到更快的训练速度
* 高级API的循环神经网络层返回一个输出和一个更新后的隐状态,我们还需要计算整个模型的输出层
* 相比从零开始实现的循环神经网络,使用高级API实现可以加速训练

## 练习

1. 你能使用高级API使循环神经网络模型过拟合吗
1. 尝试使用高级API,你能使循环神经网络模型过拟合吗
1. 如果在循环神经网络模型中增加隐藏层的数量会发生什么?你能使模型正常工作吗?
1. 使用循环神经网络实现 :numref:`sec_sequence`的自回归模型。
1. 尝试使用循环神经网络实现 :numref:`sec_sequence`的自回归模型。

:begin_tab:`mxnet`
[Discussions](https://discuss.d2l.ai/t/2105)
Expand Down

0 comments on commit 2529851

Please sign in to comment.