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

A
add gru  
Aston Zhang 已提交
3
上一节介绍了循环神经网络中的梯度计算方法。我们发现,循环神经网络的梯度可能会衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。给定一个时间序列,例如文本序列,循环神经网络在实际中较难捕捉时间步距离较大的词之间的依赖关系。
A
Aston Zhang 已提交
4

A
add gru  
Aston Zhang 已提交
5
门控循环神经网络(gated recurrent neural network)的提出,是为了更好地捕捉时间序列中时间步距离较大的依赖关系。其中,门控循环单元(gated recurrent unit,简称GRU)是一种常用的门控循环神经网络 [1, 2]。我们将在下一节介绍另一种门控循环神经网络:长短期记忆。
A
Aston Zhang 已提交
6 7 8 9


## 门控循环单元

A
add gru  
Aston Zhang 已提交
10
下面将介绍门控循环单元的设计。它引入了门的概念,从而修改了循环神经网络中隐藏状态的计算方式。输出层的设计不变。
A
Aston Zhang 已提交
11 12


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

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

A
add gru  
Aston Zhang 已提交
17 18 19 20 21 22
$$
\begin{aligned}
\boldsymbol{R}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xr} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hr} + \boldsymbol{b}_r),\\
\boldsymbol{Z}_t = \sigma(\boldsymbol{X}_t \boldsymbol{W}_{xz} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hz} + \boldsymbol{b}_z).
\end{aligned}
$$
A
Aston Zhang 已提交
23

A
Aston Zhang 已提交
24
其中$\boldsymbol{W}_{xr}, \boldsymbol{W}_{xz} \in \mathbb{R}^{x \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 已提交
25

A
add gru  
Aston Zhang 已提交
26
我们可以通过元素值域在$[0, 1]$的更新门和重置门来控制隐藏状态中信息的流动:这通常可以应用按元素乘法符$\odot$。
A
Aston Zhang 已提交
27 28


A
add gru  
Aston Zhang 已提交
29
### 候选隐藏状态
A
Aston Zhang 已提交
30

A
add gru  
Aston Zhang 已提交
31
接下来,时间步$t$的候选隐藏状态$\tilde{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h}$的计算使用了值域在$[-1, 1]$的tanh函数做激活函数。它在之前描述的循环神经网络隐藏状态表达式的基础上,引入了重置门和按元素乘法:
A
Aston Zhang 已提交
32

A
add gru  
Aston Zhang 已提交
33
$$\tilde{\boldsymbol{H}}_t = \text{tanh}(\boldsymbol{X}_t \boldsymbol{W}_{xh} + \boldsymbol{R}_t \odot \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h),$$
A
Aston Zhang 已提交
34

A
add gru  
Aston Zhang 已提交
35
其中$\boldsymbol{W}_{xh} \in \mathbb{R}^{x \times h}$和$\boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_h \in \mathbb{R}^{1 \times h}$是偏移参数。需要注意的是,候选隐藏状态使用了重置门,从而控制包含时间序列历史信息的上一个时间步的隐藏状态如何流入当前时间步的候选隐藏状态。如果重置门近似0,上一个隐藏状态将被丢弃。因此,重置门可以丢弃与预测未来无关的历史信息。
A
Aston Zhang 已提交
36 37


A
add gru  
Aston Zhang 已提交
38
### 隐藏状态
A
Aston Zhang 已提交
39

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

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

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

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

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



A
Add GRU  
Aston Zhang 已提交
55
## 实验
A
Aston Zhang 已提交
56 57


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


A
add gru  
Aston Zhang 已提交
61
### 处理数据
A
Aston Zhang 已提交
62

A
add gru  
Aston Zhang 已提交
63
我们先读取并简单处理数据集。
A
Aston Zhang 已提交
64

A
add gru  
Aston Zhang 已提交
65
```{.python .input  n=2}
A
utils  
Aston Zhang 已提交
66 67 68 69
import sys
sys.path.append('..')
import gluonbook as gb
from mxnet import nd
A
Aston Zhang 已提交
70
import zipfile
A
utils  
Aston Zhang 已提交
71

A
Aston Zhang 已提交
72 73 74 75 76 77 78 79 80
with zipfile.ZipFile('../data/jaychou_lyrics.txt.zip', 'r') as zin:
    zin.extractall('../data/')
with open('../data/jaychou_lyrics.txt') as f:
    corpus_chars = f.read()

corpus_chars = corpus_chars.replace('\n', ' ').replace('\r', ' ')
corpus_chars = corpus_chars[0:20000]
idx_to_char = list(set(corpus_chars))
char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])
A
Add GRU  
Aston Zhang 已提交
81
corpus_indices = [char_to_idx[char] for char in corpus_chars]
A
Aston Zhang 已提交
82 83 84
vocab_size = len(char_to_idx)
```

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

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

A
Aston Zhang 已提交
89
```{.python .input  n=3}
A
rnn  
Aston Zhang 已提交
90
ctx = gb.try_gpu()
A
utils  
Aston Zhang 已提交
91 92 93
num_inputs = vocab_size
num_hiddens = 256
num_outputs = vocab_size
A
Aston Zhang 已提交
94 95

def get_params():
A
utils  
Aston Zhang 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    # 更新门参数。
    W_xz = nd.random_normal(scale=0.01, shape=(num_inputs, num_hiddens),
                            ctx=ctx)
    W_hz = nd.random_normal(scale=0.01, shape=(num_hiddens, num_hiddens),
                            ctx=ctx)
    b_z = nd.zeros(num_hiddens, ctx=ctx)
    # 重置门参数。
    W_xr = nd.random_normal(scale=0.01, shape=(num_inputs, num_hiddens),
                            ctx=ctx)
    W_hr = nd.random_normal(scale=0.01, shape=(num_hiddens, num_hiddens),
                            ctx=ctx)
    b_r = nd.zeros(num_hiddens, ctx=ctx)
    # 候选隐藏状态参数。
    W_xh = nd.random_normal(scale=0.01, shape=(num_inputs, num_hiddens),
                            ctx=ctx)
    W_hh = nd.random_normal(scale=0.01, shape=(num_hiddens, num_hiddens),
                            ctx=ctx)
    b_h = nd.zeros(num_hiddens, ctx=ctx)
    # 输出层参数。
    W_hy = nd.random_normal(scale=0.01, shape=(num_hiddens, num_outputs),
                            ctx=ctx)
    b_y = nd.zeros(num_outputs, ctx=ctx)
A
Aston Zhang 已提交
118 119 120 121 122 123 124 125 126

    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
```

## 定义模型

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

A
Aston Zhang 已提交
129
```{.python .input  n=4}
A
Aston Zhang 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142
def gru_rnn(inputs, H, *params):
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hy, b_y = params
    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)
    return (outputs, H)
```

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

A
add gru  
Aston Zhang 已提交
145
设置好超参数后,我们将训练模型并跟据前缀“分开”和“不分开”分别创作长度为100个字符的一段歌词。我们每过30个迭代周期便根据当前训练的模型创作一段歌词。训练模型时采用了相邻采样。
A
Aston Zhang 已提交
146

A
Aston Zhang 已提交
147
```{.python .input  n=5}
A
utils  
Aston Zhang 已提交
148
get_inputs = gb.to_onehot
A
Aston Zhang 已提交
149
num_epochs = 150
A
Aston Zhang 已提交
150
num_steps = 35
A
utils  
Aston Zhang 已提交
151
batch_size = 32
A
Aston Zhang 已提交
152
lr = 0.25
A
utils  
Aston Zhang 已提交
153 154
clipping_theta = 5
prefixes = ['分开', '不分开']
A
Aston Zhang 已提交
155
pred_period = 30
A
utils  
Aston Zhang 已提交
156 157 158 159 160 161 162
pred_len = 100

gb.train_and_predict_rnn(gru_rnn, False, num_epochs, num_steps, num_hiddens,
                         lr, clipping_theta, batch_size, vocab_size,
                         pred_period, pred_len, prefixes, get_params,
                         get_inputs, ctx, corpus_indices, idx_to_char,
                         char_to_idx)
A
Aston Zhang 已提交
163 164
```

A
Aston Zhang 已提交
165
## 小结
A
Aston Zhang 已提交
166

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


## 练习

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

A
Aston Zhang 已提交
179

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

A
Aston Zhang 已提交
182
![](../img/qr_gru.svg)
A
add gru  
Aston Zhang 已提交
183 184 185

## 参考文献

A
Aston Zhang 已提交
186
[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 已提交
187

A
Aston Zhang 已提交
188
[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.