MXNet双向循环神经网络----单个隐藏层的双向循环神经网络(程序)
《动手学深度学习》第六章 第10节的练习题,个人解答。
下图演示了一个含单隐藏层的双向循环神经网络的架构。
下面我们来介绍具体的定义。
给定时间步
t
t
t的小批量输入
X
t
∈
R
n
×
d
\boldsymbol{X}_t \in \mathbb{R}^{n \times d}
Xt∈Rn×d(样本数为
n
n
n,输入个数为
d
d
d)和隐藏层激活函数为
ϕ
\phi
ϕ。在双向循环神经网络的架构中,
设该时间步正向隐藏状态为
H
→
t
∈
R
n
×
h
\overrightarrow{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h}
Ht∈Rn×h(正向隐藏单元个数为
h
h
h),
反向隐藏状态为
H
←
t
∈
R
n
×
h
\overleftarrow{\boldsymbol{H}}_t \in \mathbb{R}^{n \times h}
Ht∈Rn×h(反向隐藏单元个数为
h
h
h)。我们可以分别计算正向隐藏状态和反向隐藏状态:
H → t = ϕ ( X t W x h ( f ) + H → t − 1 W h h ( f ) + b h ( f ) ) , H ← t = ϕ ( X t W x h ( b ) + H ← t + 1 W h h ( b ) + b h ( b ) ) , \begin{aligned} \overrightarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}_{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol{b}_h^{(f)}),\\ \overleftarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}_{t+1} \boldsymbol{W}_{hh}^{(b)} + \boldsymbol{b}_h^{(b)}), \end{aligned} HtHt=ϕ(XtWxh(f)+Ht−1Whh(f)+bh(f)),=ϕ(XtWxh(b)+Ht+1Whh(b)+bh(b)),
其中权重 W x h ( f ) ∈ R d × h \boldsymbol{W}_{xh}^{(f)} \in \mathbb{R}^{d \times h} Wxh(f)∈Rd×h、 W h h ( f ) ∈ R h × h \boldsymbol{W}_{hh}^{(f)} \in \mathbb{R}^{h \times h} Whh(f)∈Rh×h、 W x h ( b ) ∈ R d × h \boldsymbol{W}_{xh}^{(b)} \in \mathbb{R}^{d \times h} Wxh(b)∈Rd×h、 W h h ( b ) ∈ R h × h \boldsymbol{W}_{hh}^{(b)} \in \mathbb{R}^{h \times h} Whh(b)∈Rh×h和偏差 b h ( f ) ∈ R 1 × h \boldsymbol{b}_h^{(f)} \in \mathbb{R}^{1 \times h} bh(f)∈R1×h、 b h ( b ) ∈ R 1 × h \boldsymbol{b}_h^{(b)} \in \mathbb{R}^{1 \times h} bh(b)∈R1×h均为模型参数。
然后我们连结两个方向的隐藏状态 H → t \overrightarrow{\boldsymbol{H}}_t Ht和 H ← t \overleftarrow{\boldsymbol{H}}_t Ht来得到隐藏状态 H t ∈ R n × 2 h \boldsymbol{H}_t \in \mathbb{R}^{n \times 2h} Ht∈Rn×2h,并将其输入到输出层。输出层计算输出 O t ∈ R n × q \boldsymbol{O}_t \in \mathbb{R}^{n \times q} Ot∈Rn×q(输出个数为 q q q):
O t = H t W h q + b q , \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q, Ot=HtWhq+bq,
其中权重 W h q ∈ R 2 h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{2h \times q} Whq∈R2h×q和偏差 b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bq∈R1×q为输出层的模型参数。不同方向上的隐藏单元个数也可以不同。
小结
- 双向循环神经网络在每个时间步的隐藏状态同时取决于该时间步之前和之后的子序列(包括当前时间步的输入)。
练习
- 参考上图设计含多个隐藏层的双向循环神经网络。
import d2lzh as d2l
from mxnet import nd
from mxnet.gluon import rnn
(corpus_indices, char_to_idx, idx_to_char,
vocab_size) = d2l.load_data_jay_lyrics()
初始化模型参数
创建公式中出现的参数,并初始化。
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
ctx = d2l.try_gpu()
def get_params():
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))
W_xhf, W_hhf, b_hf = _three() # 前向参数
W_xhb, W_hhb, b_hb = _three() # 反向参数
# 输出层参数
W_hq = _one((2*num_hiddens, num_outputs))
b_q = nd.zeros(num_outputs, ctx=ctx)
# 附上梯度
params = [W_xhf, W_hhf, b_hf, W_xhb, W_hhb, b_hb,
W_hq, b_q]
for param in params:
param.attach_grad()
return params
定义模型
根据两个方向的隐藏状态 H → t \overrightarrow{\boldsymbol{H}}_t Ht和 H ← t \overleftarrow{\boldsymbol{H}}_t Ht,以及输出层计算输出 O t \boldsymbol{O}_t Ot的计算公式创建双向循环神经网络模型。
def init_bi_state(batch_size, num_hiddens, ctx):
return (nd.zeros(shape=(batch_size, num_hiddens), ctx=ctx),
nd.zeros(shape=(batch_size, num_hiddens), ctx=ctx),)
def birnn(inputs, state, params):
[W_xhf, W_hhf, b_hf,W_xhb, W_hhb, b_hb,
W_hq, b_q] = params
(Hf,Hb,) = state
outputs = []
for X in inputs:
Hf = nd.sigmoid(nd.dot(X, W_xhf) + nd.dot(Hf, W_hhf) + b_hf)
Hb = nd.sigmoid(nd.dot(X, W_xhb) + nd.dot(Hb, W_hhb) + b_hb)
H = nd.concat(Hf,Hb,dim=1)
Y = nd.dot(H, W_hq) + b_q
outputs.append(Y)
return outputs, (Hf, Hb,)
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-1 #lr设小后,没输出
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
中间测试
[W_xhf, W_hhf, b_hf,W_xhb, W_hhb, b_hb,W_hq, b_q] = get_params()
(Hf,Hb,) = init_bi_state(batch_size, num_hiddens, ctx)
Hf,Hb,W_xhf
H = nd.concat(Hf,Hb,dim=1)
H
[[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]]
<NDArray 32x512 @gpu(0)>
训练预测
d2l.train_and_predict_rnn(birnn, get_params, init_bi_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)
epoch 40, perplexity 153.796754, time 1.32 sec
- 分开 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我
- 不分开哼不能 我不能 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不
epoch 80, perplexity 33.458324, time 1.34 sec
- 分开 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够不起
- 不分开始的我 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够不起 我不能够
epoch 120, perplexity 10.509801, time 1.32 sec
- 分开 我 这里什么 不会B血 我想就这样牵着你的手 不会B血 我想就这样牵着你的手 不会B血 我想就这样
- 不分开不 我不能再想 我不能再想 我不能再想 我不能再想 我不能再想 我不能再想 我不能再想 我不能再想
epoch 160, perplexity 4.527863, time 1.29 sec
- 分开的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我
- 不分开我爱你 一朵莫默默默离开这样打我妈妈这样牵着你的手不放开离开我 不能承受我已无处可躲可以演戏 快使用