gru.md 10.4 KB
Newer Older
A
rename  
Aston Zhang 已提交
1
# 门控循环单元(GRU)
A
Aston Zhang 已提交
2

M
muli 已提交
3
上一节介绍了循环神经网络中的梯度计算方法。我们发现,循环神经网络的梯度由于有累乘,它可能会衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。通常由于这个原因,循环神经网络在实际中较难捕捉时间步距离较大的样本之间的依赖关系,因为信息传递时容易衰减或爆炸。
A
Aston Zhang 已提交
4

M
muli 已提交
5
门控循环神经网络(gated recurrent neural network)的提出,是为了更好地捕捉时间序列中时间步距离较大的依赖关系。它们通过可以学习的门来控制信息的流动,使得信息要么简单通过或者要么被阻挡。其中,门控循环单元(gated recurrent unit,简称GRU)是一种常用的门控循环神经网络 [1, 2]。另一种常见门控循环神经网络,长短期记忆,则将在下一节介绍。
A
Aston Zhang 已提交
6 7 8 9


## 门控循环单元

M
muli 已提交
10
下面将介绍门控循环单元的设计。它引入了重置门和更新门的概念,从而修改了循环神经网络中隐藏状态的计算方式。
A
Aston Zhang 已提交
11

A
add gru  
Aston Zhang 已提交
12
### 重置门和更新门
A
Aston Zhang 已提交
13

M
muli 已提交
14
如图6.4所示,门控循环单元中的重置门(reset gate)和更新门(update gate)均由输入为当前时间步输入$\boldsymbol{X}_t$与上一时间步隐藏状态$\boldsymbol{H}_{t-1}$,且激活函数为sigmoid函数的全连接层计算得出。
M
Mu Li 已提交
15

A
Aston Zhang 已提交
16 17

![门控循环单元中重置门和更新门的计算。](../img/gru_1.svg)
M
Mu Li 已提交
18 19 20


具体来说,假设隐藏单元个数为$h$,给定时间步$t$的小批量输入$\boldsymbol{X}_t \in \mathbb{R}^{n \times d}$(样本数为$n$,输入个数为$d$)和上一时间步隐藏状态$\boldsymbol{H}_{t-1} \in \mathbb{R}^{n \times h}$。重置门$\boldsymbol{R}_t \in \mathbb{R}^{n \times h}$和更新门$\boldsymbol{Z}_t \in \mathbb{R}^{n \times h}$的计算如下:
A
Aston Zhang 已提交
21

A
add gru  
Aston Zhang 已提交
22 23 24
$$
\begin{aligned}
\boldsymbol{R}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xr} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hr} + \boldsymbol{b}_r),\\
A
Aston Zhang 已提交
25
\boldsymbol{Z}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xz} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hz} + \boldsymbol{b}_z),
A
add gru  
Aston Zhang 已提交
26 27
\end{aligned}
$$
A
Aston Zhang 已提交
28

A
Aston Zhang 已提交
29
其中$\boldsymbol{W}_{xr}, \boldsymbol{W}_{xz} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}_{hr}, \boldsymbol{W}_{hz} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_r, \boldsymbol{b}_z \in \mathbb{R}^{1 \times h}$是偏移参数。激活函数$\sigma$是sigmoid函数。[“多层感知机”](../chapter_deep-learning-basics/mlp.md)一节中介绍过,sigmoid函数可以将元素的值变换到0和1之间。因此,重置门$\boldsymbol{R}_t$和更新门$\boldsymbol{Z}_t$中每个元素的值域都是$[0, 1]$。
A
Aston Zhang 已提交
30

A
add gru  
Aston Zhang 已提交
31
### 候选隐藏状态
A
Aston Zhang 已提交
32

M
muli 已提交
33 34 35
接下来,门控循环单元将计算候选隐藏状态来辅助稍后的隐藏状态计算。如图6.5所示,我们将当前时间步重置门的输出与上一时间步隐藏状态做按元素乘法。如果重置门中元素值接近0,那么意味着重置对应隐藏状态元素为0,即丢弃(或者说重置)来自上一时间步的历史信息。如果元素值接近1,那么表示通过来自上一时间步的信息。

然后,将重置后的上一时间步的隐藏状态与当前时间步的输入合并,再通过含tanh激活函数的全连接层计算出候选隐藏状态,其所有元素的值域为$[-1, 1]$。
A
Aston Zhang 已提交
36

A
Aston Zhang 已提交
37
![门控循环单元中候选隐藏状态的计算。这里的乘号是按元素乘法。](../img/gru_2.svg)
M
Mu Li 已提交
38

A
Aston Zhang 已提交
39
具体来说,时间步$t$的候选隐藏状态$\tilde{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h}$的计算为
A
Aston Zhang 已提交
40

M
muli 已提交
41
$$\tilde{\boldsymbol{H}}_t = \text{tanh}(\boldsymbol{X}_t \boldsymbol{W}_{xh} + \left(\boldsymbol{R}_t \odot \boldsymbol{H}_{t-1}\right) \boldsymbol{W}_{hh} + \boldsymbol{b}_h),$$
A
Aston Zhang 已提交
42

M
muli 已提交
43
其中$\boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_h \in \mathbb{R}^{1 \times h}$是偏移参数。从上面这个公式可以看出,重置门控制了包含时间序列历史信息的上一个时间步的隐藏状态如何流入当前时间步的候选隐藏状态,它可以用来丢弃与预测未来无关的历史信息。
A
Aston Zhang 已提交
44

A
add gru  
Aston Zhang 已提交
45
### 隐藏状态
A
Aston Zhang 已提交
46

A
Aston Zhang 已提交
47
最后,时间步$t$的隐藏状态$\boldsymbol{H}_t \in \mathbb{R}^{n \times h}$的计算使用当前时间步的更新门$\boldsymbol{Z}_t$来对上一时间步的隐藏状态$\boldsymbol{H}_{t-1}$和当前时间步的候选隐藏状态$\tilde{\boldsymbol{H}}_t$做组合:
A
Aston Zhang 已提交
48

A
add gru  
Aston Zhang 已提交
49
$$\boldsymbol{H}_t = \boldsymbol{Z}_t \odot \boldsymbol{H}_{t-1}  + (1 - \boldsymbol{Z}_t) \odot \tilde{\boldsymbol{H}}_t.$$
A
Aston Zhang 已提交
50

M
Mu Li 已提交
51

A
Aston Zhang 已提交
52
![门控循环单元中隐藏状态的计算。这里的乘号是按元素乘法。](../img/gru_3.svg)
M
Mu Li 已提交
53 54


A
Aston Zhang 已提交
55
值得注意的是,更新门可以控制隐藏状态应该如何被包含当前时间步信息的候选隐藏状态所更新,如图6.6所示。假设更新门在时间步$t'$到$t$($t' < t$)之间一直近似1。那么,在时间步$t'$到$t$之间的输入信息几乎没有流入时间步$t$的隐藏状态$\boldsymbol{H}_t$。实际上,这可以看作是较早时刻的隐藏状态$\boldsymbol{H}_{t'-1}$一直通过时间保存并传递至当前时间步$t$。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。
A
Aston Zhang 已提交
56

A
Add GRU  
Aston Zhang 已提交
57
我们对门控循环单元的设计稍作总结:
A
Aston Zhang 已提交
58

A
add gru  
Aston Zhang 已提交
59 60
* 重置门有助于捕捉时间序列里短期的依赖关系。
* 更新门有助于捕捉时间序列里长期的依赖关系。
A
Aston Zhang 已提交
61

M
muli 已提交
62
## 载入数据集
A
Aston Zhang 已提交
63

M
muli 已提交
64
为了实现并展示门控循环单元,我们依然使用周杰伦歌词数据集来训练模型作词。这里除门控循环单元以外的实现已在[“循环神经网络”](rnn.md)一节中介绍。我们先导入包和模块,并读取数据集。
A
Aston Zhang 已提交
65

M
muli 已提交
66
```{.python .input  n=1}
67 68 69
import sys
sys.path.insert(0, '..')

A
utils  
Aston Zhang 已提交
70 71
import gluonbook as gb
from mxnet import nd
M
muli 已提交
72 73
from mxnet.gluon import rnn

A
Aston Zhang 已提交
74 75
(corpus_indices, char_to_idx, idx_to_char,
 vocab_size) = gb.load_data_jay_lyrics()
A
Aston Zhang 已提交
76 77
```

M
muli 已提交
78 79 80
## GRU的从零开始实现


A
Add GRU  
Aston Zhang 已提交
81
### 初始化模型参数
A
Aston Zhang 已提交
82

A
add gru  
Aston Zhang 已提交
83
以下部分对模型参数进行初始化。超参数`num_hiddens`定义了隐藏单元的个数。
A
Aston Zhang 已提交
84

M
muli 已提交
85
```{.python .input  n=2}
A
utils  
Aston Zhang 已提交
86 87 88
num_inputs = vocab_size
num_hiddens = 256
num_outputs = vocab_size
M
muli 已提交
89
ctx = gb.try_gpu()
A
Aston Zhang 已提交
90 91

def get_params():
A
Aston Zhang 已提交
92 93 94 95 96 97 98 99
    def _one(shape):
        return nd.random.normal(scale=0.01, shape=shape, ctx=ctx)
    
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                nd.zeros(num_hiddens, ctx=ctx))  
 
M
muli 已提交
100 101 102
    W_xz, W_hz, b_z = _three()  # 更新门参数。    
    W_xr, W_hr, b_r = _three()  # 重置门参数。    
    W_xh, W_hh, b_h = _three()  # 候选隐藏状态参数。
A
utils  
Aston Zhang 已提交
103
    # 输出层参数。
M
muli 已提交
104
    W_hy = _one((num_hiddens, num_outputs))
A
utils  
Aston Zhang 已提交
105
    b_y = nd.zeros(num_outputs, ctx=ctx)
M
muli 已提交
106
    # 创建梯度。
A
Aston Zhang 已提交
107 108 109 110 111 112
    params = [W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hy, b_y]
    for param in params:
        param.attach_grad()
    return params
```

M
muli 已提交
113 114 115 116 117 118 119 120
### 定义模型

首先定义隐藏状态初始化函数,同`init_rnn_state`一样它返回由一个形状为(`batch_size``num_hiddens`)的值为0的NDArray组成的列表。

```{.python .input  n=3}
def init_gru_state(batch_size, num_hiddens, ctx):
    return (nd.zeros(shape=(batch_size, num_hiddens), ctx=ctx), )
```
A
Aston Zhang 已提交
121

A
add gru  
Aston Zhang 已提交
122
下面根据门控循环单元的计算表达式定义模型。
A
Aston Zhang 已提交
123

A
Aston Zhang 已提交
124
```{.python .input  n=4}
M
muli 已提交
125
def gru(inputs, state, params):
A
Aston Zhang 已提交
126
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hy, b_y = params
M
muli 已提交
127
    H, = state
A
Aston Zhang 已提交
128 129 130 131 132 133 134 135
    outputs = []
    for X in inputs:        
        Z = nd.sigmoid(nd.dot(X, W_xz) + nd.dot(H, W_hz) + b_z)
        R = nd.sigmoid(nd.dot(X, W_xr) + nd.dot(H, W_hr) + b_r)
        H_tilda = nd.tanh(nd.dot(X, W_xh) + R * nd.dot(H, W_hh) + b_h)
        H = Z * H + (1 - Z) * H_tilda
        Y = nd.dot(H, W_hy) + b_y
        outputs.append(Y)
M
muli 已提交
136
    return outputs, (H,)
A
Aston Zhang 已提交
137 138
```

A
add gru  
Aston Zhang 已提交
139
### 训练模型并创作歌词
A
Aston Zhang 已提交
140

M
muli 已提交
141 142
使用同前一节类似的超参数训练,但我们这里减少了迭代周期数,且训练模型时只采用了相邻采样。

A
Aston Zhang 已提交
143
```{.python .input  n=5}
M
muli 已提交
144
num_epochs = 160
A
Aston Zhang 已提交
145
num_steps = 35
A
utils  
Aston Zhang 已提交
146
batch_size = 32
M
muli 已提交
147 148
lr = 1e2
clipping_theta = 1e-2
A
utils  
Aston Zhang 已提交
149
prefixes = ['分开', '不分开']
M
muli 已提交
150
pred_period = 40
M
muli 已提交
151
pred_len = 50
M
muli 已提交
152 153 154 155 156
```

设置好超参数后,我们将训练模型并跟据前缀“分开”和“不分开”分别创作长度为50个字符的一段歌词。我们每过30个迭代周期便根据当前训练的模型创作一段歌词。。

```{.python .input}
A
Aston Zhang 已提交
157 158 159 160 161
gb.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
                         vocab_size, ctx, corpus_indices, idx_to_char,
                         char_to_idx, False, num_epochs, num_steps, lr,
                         clipping_theta, batch_size, pred_period, pred_len,
                         prefixes)
M
muli 已提交
162 163 164 165 166 167 168 169 170
```

## GRU的Gluon实现

在Gluon中我们直接调用rnn模块中的GRU类即可。

```{.python .input  n=6}
gru_layer = rnn.GRU(num_hiddens)
model = gb.RNNModel(gru_layer, vocab_size)
A
utils  
Aston Zhang 已提交
171

A
Aston Zhang 已提交
172 173 174 175
gb.train_and_predict_rnn_gluon(model, num_hiddens, vocab_size, ctx,
                               corpus_indices, idx_to_char, char_to_idx,
                               num_epochs, num_steps, lr, clipping_theta,
                               batch_size, pred_period, pred_len, prefixes)
A
Aston Zhang 已提交
176 177
```

A
Aston Zhang 已提交
178
## 小结
A
Aston Zhang 已提交
179

夏鲁豫 已提交
180
* 门控循环神经网络可以更好地捕捉时间序列中时间步距离较大的依赖关系,它包括门控循环单元和长短期记忆。
A
add gru  
Aston Zhang 已提交
181 182 183
* 门控循环单元引入了门的概念,从而修改了循环神经网络中隐藏状态的计算方式。它包括重置门、更新门、候选隐藏状态和隐藏状态。
* 重置门有助于捕捉时间序列里短期的依赖关系。
* 更新门有助于捕捉时间序列里长期的依赖关系。
A
Aston Zhang 已提交
184 185 186 187


## 练习

A
Aston Zhang 已提交
188
* 假设时间步$t' < t$。如果我们只希望用时间步$t'$的输入来预测时间步$t$的输出,每个时间步的重置门和更新门的值最好是多少?
A
add gru  
Aston Zhang 已提交
189 190 191
* 调调超参数,观察并分析对运行时间、困惑度以及创作歌词的结果造成的影响。
* 在相同条件下,比较门控循环单元和循环神经网络的运行时间。

A
Aston Zhang 已提交
192

A
Aston Zhang 已提交
193 194
## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/4042)

A
Aston Zhang 已提交
195
![](../img/qr_gru.svg)
A
add gru  
Aston Zhang 已提交
196 197 198

## 参考文献

A
Aston Zhang 已提交
199
[1] Cho, K., Van Merriënboer, B., Bahdanau, D., & Bengio, Y. (2014). On the properties of neural machine translation: Encoder-decoder approaches. arXiv preprint arXiv:1409.1259.
A
add gru  
Aston Zhang 已提交
200

A
Aston Zhang 已提交
201
[2] Chung, J., Gulcehre, C., Cho, K., & Bengio, Y. (2014). Empirical evaluation of gated recurrent neural networks on sequence modeling. arXiv preprint arXiv:1412.3555.