前言
本節繼續學習循環神經網絡
- GRU
- LSTM
- 雙向循環神經網絡
1、門控循環單元(GRU)
- 當時間步數較大或者時間步較小時,循環神經⽹絡的梯度較容易出現衰減或爆炸
- 裁剪梯度可以應對梯度爆炸,但無法解決梯度衰減
- 門控循環神經網絡(gated recurrent neural network)的提出,正是爲了更好地捕捉時間序列中時間步距離較大的依賴關係
- 有GRU和LSTM兩種
GRU引入重置門和更新門
重置門和更新門
- 重置⻔有助於捕捉時間序列⾥短期的依賴關係
- 更新⻔有助於捕捉時間序列⾥⻓期的依賴關係
候選隱藏狀態 - 當前時間步重置⻔的輸出與上⼀時間步隱藏狀態做按元素乘法(符號爲⊙)
- 將按元素乘法的結果與當前時間步的輸⼊連結
- 再通過含激活函數tanh的全連接層計算出候選隱藏狀態
隱藏狀態
- 上一時間步的隱藏狀態和當前候選隱藏狀態做個組合
實現
import d2lzh as d2l
from mxnet import nd
from mxnet.gluon import rnn
"""實現GRU"""
# 數據,周杰倫歌詞
(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_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 = nd.zeros(num_outputs, ctx=ctx)
# 附上梯度
params = [W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q]
for param in params:
param.attach_grad()
return params
# 模型
def init_gru_state(batch_size, num_hiddens, ctx):
return (nd.zeros(shape=(batch_size, num_hiddens), ctx=ctx), )
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 = 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) + nd.dot(R * H, W_hh) + b_h)
H = Z * H + (1 - Z) * H_tilda
Y = nd.dot(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)
# 訓練
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開'] #每過40個迭代週期便根據當前訓練的模型創作一段歌詞。
d2l.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)
2、長短期記憶(LSTM)
LSTM 中引⼊了3個⻔,即輸⼊⻔(input gate)、遺忘⻔(forget gate)和輸出⻔(output gate),
以及與隱藏狀態形狀相同的記憶細胞
三個門
- 遺忘⻔控制上⼀時間步的記憶細胞中的信息是否傳遞到當前時間步
- 輸⼊⻔則控制當前時間步的輸⼊通過候選記憶細胞如何流⼊當前時間步的記憶細胞
- 如果遺忘⻔⼀直近似1且輸⼊⻔⼀直近似0,過去的記憶細胞將⼀直通過時間保存並傳遞⾄當前時間步。這設計可以應對循環神經⽹絡中的梯度衰減問題,並更好地捕捉時間序列中時間步距離較⼤的依賴關係
- 通過輸出門來控制從記憶細胞到隱藏狀態的信息的流動
候選記憶細胞
類似於GRU的候選隱藏狀態
記憶細胞
類似於GRU的隱藏狀態,做個組合
隱藏狀態
- tanh函數確保隱藏狀態元素值在-1到1之間
- 當輸出⻔近似1時,記憶細胞信息將傳遞到隱藏狀態供輸出層使⽤
- 當輸出⻔近似0時,記憶細胞信息只⾃⼰保留
實現
import d2lzh as d2l
from mxnet import nd
from mxnet.gluon import rnn
"""實現LSTM"""
# 數據,周杰倫歌詞
(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_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 = nd.zeros(num_outputs, ctx=ctx)
# 附上梯度
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]
for param in params:
param.attach_grad()
return params
# 模型
def init_lstm_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 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 = nd.sigmoid(nd.dot(X, W_xi) + nd.dot(H, W_hi) + b_i)
F = nd.sigmoid(nd.dot(X, W_xf) + nd.dot(H, W_hf) + b_f)
O = nd.sigmoid(nd.dot(X, W_xo) + nd.dot(H, W_ho) + b_o)
C_tilda = nd.tanh(nd.dot(X, W_xc) + nd.dot(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * C.tanh()
Y = nd.dot(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H, C)
# 訓練
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開'] #每過40個迭代週期便根據當前訓練的模型創作一段歌詞。
d2l.train_and_predict_rnn(lstm, get_params, init_lstm_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)
3、雙向循環神經網絡
輸出層
結語
進一步學習了RNN