Task1.9 RNN 进阶

GRU

RNN存在的问题:梯度较容易出现衰减或爆炸(BPTT)
⻔控循环神经⽹络:捕捉时间序列中时间步距离较⼤的依赖关系
RNN:

Image Name

$$
H_{t} = ϕ(X_{t}W_{xh} + H_{t-1}W_{hh} + b_{h})
$$
GRU:

Image Name

$$
R_{t} = σ(X_tW_{xr} + H_{t−1}W_{hr} + b_r)\
Z_{t} = σ(X_tW_{xz} + H_{t−1}W_{hz} + b_z)\
\widetilde{H}t = tanh(X_tW{xh} + (R_t ⊙H_{t−1})W_{hh} + b_h)\
H_t = Z_t⊙H_{t−1} + (1−Z_t)⊙\widetilde{H}_t
$$
• 重置⻔有助于捕捉时间序列⾥短期的依赖关系;
• 更新⻔有助于捕捉时间序列⾥⻓期的依赖关系。

载入数据集

1
2
import os
os.listdir('/home/kesci/input')
['d2lzh1981', 'houseprices2807', 'jaychou_lyrics4703', 'd2l_jay9460']
1
2
3
4
import numpy as np
import torch
from torch import nn, optim
import torch.nn.functional as F
1
2
3
4
5
6
import sys
sys.path.append("../input/")
import d2l_jay9460 as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()

初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():
def _one(shape):
ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32) #正态分布
# 0均值 var = 0.01
return torch.nn.Parameter(ts, requires_grad=True)
def _three():
return (_one((num_inputs, num_hiddens)),
_one((num_hiddens, num_hiddens)),
torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))

W_xz, W_hz, b_z = _three() # 更新门参数
W_xr, W_hr, b_r = _three() # 重置门参数
W_xh, W_hh, b_h = _three() # 候选隐藏状态参数

# 输出层参数
W_hq = _one((num_hiddens, num_outputs))
b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q]) # 11 paras

def init_gru_state(batch_size, num_hiddens, device): #隐藏状态初始化
return (torch.zeros((batch_size, num_hiddens), device=device), )
will use cpu

GRU模型

1
2
3
4
5
6
7
8
9
10
11
12
def gru(inputs, state, params):
W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
for X in inputs:
Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
H = Z * H + (1 - Z) * H_tilda
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)

训练模型

1
2
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']
1
2
3
4
5
d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
vocab_size, device, 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 152.268495, time 1.31 sec
 - 分开 我想你你 我不这 我不不 我想你你 我不这 我不不 我想你你 我不这 我不不 我想你你 我不这 我
 - 不分开 我想你你 我不这 我不不 我想你你 我不这 我不不 我想你你 我不这 我不不 我想你你 我不这 我
epoch 80, perplexity 32.902482, time 1.29 sec
 - 分开 一直在人截棍 哼哼哈兮 快使用双截棍 哼哼哈兮 快使用双截棍 哼哼哈兮 快使用双截棍 哼哼哈兮 快
 - 不分开 你爱我 别你 我想要这样 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 我不要再想 
epoch 120, perplexity 5.031946, time 1.26 sec
 - 分开 一直心酒 你的完空 恨谁风空  没有用双截棍 哼哼哈兮 快使用双截棍 哼哼哈兮 快使用双截棍 哼哼
 - 不分开 爱过走的太快就像龙卷风 不能再能我 再你这这不舍 后知后觉 我跟了这节奏 我该好好生活 不知不觉 
epoch 160, perplexity 1.491664, time 1.31 sec
 - 分开 我想想这样的脑袋有问题 随便说说 其实我早已经猜透看透不想多说 只是我怕眼泪撑不住 不懂 你的黑色
 - 不分开 你已经离开我 不知不觉 我跟了这节奏 后知后觉 又过了一个秋 后知后觉 我该好好生活 我该好好生活

简洁实现

1
2
3
4
5
6
7
8
9
10
11
num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

lr = 1e-2 # 注意调整学习率
gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
epoch 40, perplexity 1.018378, time 0.95 sec
 - 分开的玩笑 想通 却又再考倒我 说散 你想很久了吧? 败给你的黑色幽默 说散 你想很久了吧? 我的认真败
 - 不分开暴风圈来不及逃 我不能再想 我不能再想 我不 我不 我不能 爱情走的太快就像龙卷风 不能承受我已无处
epoch 80, perplexity 1.012368, time 0.98 sec
 - 分开的玩笑 想通 却又再考倒我 说散 你想很久了吧? 败给你的黑色幽默 说散 你想很久了吧? 我的认真败
 - 不分开爱玩笑 想通 却又再考倒我 说散 你想很久了吧? 败给你的黑色幽默 说散 你想很久了吧? 我的认真败
epoch 120, perplexity 1.013130, time 0.91 sec
 - 分开的可爱女人 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让我感动的可爱女人 坏坏的让我
 - 不分开不知不觉 你已经离开我 不知不觉 我跟了这节奏 后知后觉 又过了一个秋 后知后觉 我该好好生活 我该
epoch 160, perplexity 1.008711, time 0.96 sec
 - 分开的可爱女人 漂亮的让我面红的可爱女人 温柔的让我心疼的可爱女人 透明的让我感动的可爱女人 坏坏的让我
 - 不分开始打呼 管家是一只会说法语举止优雅的猪 吸血前会念约翰福音做为弥补 拥有一双蓝色眼睛的凯萨琳公主 专

LSTM

** 长短期记忆long short-term memory **:
遗忘门:控制上一时间步的记忆细胞
输入门:控制当前时间步的输入
输出门:控制从记忆细胞到隐藏状态
记忆细胞:⼀种特殊的隐藏状态的信息的流动

Image Name

$$
I_t = σ(X_tW_{xi} + H_{t−1}W_{hi} + b_i) \
F_t = σ(X_tW_{xf} + H_{t−1}W_{hf} + b_f)\
O_t = σ(X_tW_{xo} + H_{t−1}W_{ho} + b_o)\
\widetilde{C}t = tanh(X_tW{xc} + H_{t−1}W_{hc} + b_c)\
C_t = F_t ⊙C_{t−1} + I_t ⊙\widetilde{C}_t\
H_t = O_t⊙tanh(C_t)
$$

初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():
def _one(shape):
ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
return torch.nn.Parameter(ts, requires_grad=True)
def _three():
return (_one((num_inputs, num_hiddens)),
_one((num_hiddens, num_hiddens)),
torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))

W_xi, W_hi, b_i = _three() # 输入门参数
W_xf, W_hf, b_f = _three() # 遗忘门参数
W_xo, W_ho, b_o = _three() # 输出门参数
W_xc, W_hc, b_c = _three() # 候选记忆细胞参数

# 输出层参数
W_hq = _one((num_hiddens, num_outputs))
b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])

def init_lstm_state(batch_size, num_hiddens, device):
return (torch.zeros((batch_size, num_hiddens), device=device),
torch.zeros((batch_size, num_hiddens), device=device))
will use cpu

LSTM模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * C.tanh()
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H, C)

训练模型

1
2
3
4
5
6
7
8
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,
vocab_size, device, 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 211.056591, time 1.64 sec
 - 分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我
 - 不分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我
epoch 80, perplexity 65.091712, time 1.53 sec
 - 分开 我想你这你 我不要 我不 我不要 我不要 我不要 我不要 我不要 我不要 我不要 我不要 我不要 
 - 不分开 我想你你想你 我想想这你 我不要 我不要 我不要 我不要 我不要 我不要 我不要 我不要 我不要 
epoch 120, perplexity 17.263918, time 1.54 sec
 - 分开 我想你这生微 一天个对医药 我想这这样活 你天样 一直走 我想就好样 你不的节活 后知后觉 我该了
 - 不分开 我想你的生笑 你天  又你的我面听 一发抖 快给我抬起头 有话去对医药 说知后觉 我想了这节活 后
epoch 160, perplexity 3.906676, time 1.56 sec
 - 分开 你说的话不起 你学着碌的落 快什么 什什么 什么开有在留留 干什么 干什么 什么我有有片自 快使用
 - 不分开我 想要你 你想我 想要再 我不再再了快 说说去对医药箱 说说  想想了久了着? 我不想想想你 你你

简洁实现

1
2
3
4
5
6
7
8
9
10
11
num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

lr = 1e-2 # 注意调整学习率
lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)
model = d2l.RNNModel(lstm_layer, vocab_size)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
epoch 40, perplexity 1.028067, time 1.05 sec
 - 分开始我担 有AB血型的公老鼠 恍恍惚惚 是谁的脚步 银制茶壶 装蟑螂蜘蛛 辛辛苦苦 全家怕日出 白色蜡
 - 不分开 我好好好生活 我叫你已经很久 别想躲 说你眼睛看着我 别发抖 快给我抬起头 有话去对医药箱说 别怪
epoch 80, perplexity 1.021706, time 1.07 sec
 - 分开始我担 在小村外的溪边河口 默默的在等著我 家乡的爹娘早已苍老了轮廓 娘子我欠你太多 一壶好酒 再来
 - 不分开 我叫我的爱  你叫我学习你把你当榜样  好多的假像 妈妈常说乖听你爸的话  你叫我怎么跟你像 不要
epoch 120, perplexity 1.012656, time 1.02 sec
 - 分开始我攻 我的认真败给黑色幽默 走过了很多地方 我来到伊斯坦堡 就像是童话故事  有教堂有城堡 每天忙
 - 不分开不多难道 快攻抢篮板球 得分都靠我 你拿着球不投 又不会掩护我 选你这种队友 瞎透了我 说你说 分数
epoch 160, perplexity 1.010791, time 1.08 sec
 - 分开始我呼 在人有一切 真的可以 我想要将我的寂寞封闭 然后在这里 不限日期 然后将过去 慢慢温习 让我
 - 不分开 我叫我学爱你看棒球 想这样没担忧 唱着歌 一直走 我想就这样牵着你的手不放开 爱可不可以简简单单没

深度循环神经网络

Image Name

$$
\boldsymbol{H}t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}{xh}^{(1)} + \boldsymbol{H}{t-1}^{(1)} \boldsymbol{W}{hh}^{(1)} + \boldsymbol{b}h^{(1)})\
\boldsymbol{H}_t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}
{xh}^{(\ell)} + \boldsymbol{H}{t-1}^{(\ell)} \boldsymbol{W}{hh}^{(\ell)} + \boldsymbol{b}h^{(\ell)})\
\boldsymbol{O}_t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}
{hq} + \boldsymbol{b}_q
$$

1
2
3
4
5
6
7
8
9
10
import numpy as np
import torch
from torch import nn, optim

import sys
sys.path.append("../input/")
import d2l_jay9460 as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()
1
2
3
4
5
6
7
8
9
10
11
12
13
14


num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

lr = 1e-2 # 注意调整学习率

gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
epoch 40, perplexity 1.779664, time 1.50 sec
 - 分开 我想多 一场默剧 你的完美主义 太彻底 让我连恨都难以下笔 将真心抽离写成日记 像是一场默剧 你的
 - 不分开想要再想 我不多 我有多烦恼  没有你烦我有多烦恼多难熬  没有你烦我有多烦恼多难熬  没有你烦我有
epoch 80, perplexity 1.017581, time 1.49 sec
 - 分开 我想一定是我听错弄错搞错 拜托 我想是你的脑袋有问题 随便说说 其实我早已经猜透看透不想多说 只是
 - 不分开 那场悲剧 是你完美演出的一场戏 宁愿心碎哭泣 再狠狠忘记 你爱过我的证据 让晶莹的泪滴 闪烁成回忆
epoch 120, perplexity 1.015036, time 1.50 sec
 - 分开 我有多难熬 我没有你烦 我有多烦恼  没有你烦我有多烦恼多难熬  穿过云层 我试著努力向你奔跑 爱
 - 不分开 我有多难熬 我没有你烦 我有多烦恼  没有你烦我有多烦恼多难熬  穿过云层 我试著努力向你奔跑 爱
epoch 160, perplexity 1.010326, time 1.51 sec
 - 分开 我有多难熬  没有你在 我有多难熬  没有你在我有多难熬多烦恼  没有你烦 我有多烦恼  没有你烦
 - 不分开 我有多难熬 我跟了这节奏 后知后觉 又过了一个秋 后知后觉 我该好好生活 我该好好生活 不知不觉 
1
2
3
4
5
6
gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=6)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
epoch 40, perplexity 275.835469, time 3.79 sec
 - 分开                                                  
 - 不分开                                                  
epoch 80, perplexity 274.543441, time 3.88 sec
 - 分开                                                  
 - 不分开                                                  
epoch 120, perplexity 274.099434, time 3.97 sec
 - 分开                                                  
 - 不分开                                                  
epoch 160, perplexity 273.963849, time 4.07 sec
 - 分开                                                  
 - 不分开                                                  

双向循环神经网络

Image Name

$$
\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} $$
$$
\boldsymbol{H}_t=(\overrightarrow{\boldsymbol{H}}
{t}, \overleftarrow{\boldsymbol{H}}_t)
$$

$$
\boldsymbol{O}t = \boldsymbol{H}_t \boldsymbol{W}{hq} + \boldsymbol{b}_q
$$

1
2
3
4
5
6
7
8
9
10
11
12
num_hiddens=128
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e-2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

lr = 1e-2 # 注意调整学习率

gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens,bidirectional=True)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
corpus_indices, idx_to_char, char_to_idx,
num_epochs, num_steps, lr, clipping_theta,
batch_size, pred_period, pred_len, prefixes)
epoch 40, perplexity 1.001314, time 0.98 sec
 - 分开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开
 - 不分开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开
epoch 80, perplexity 1.000417, time 0.98 sec
 - 分开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开
 - 不分开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开
epoch 120, perplexity 1.000207, time 0.99 sec
 - 分开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开
 - 不分开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开
epoch 160, perplexity 1.000124, time 0.94 sec
 - 分开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开始开
 - 不分开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开不开

------ 本文结束------
0%