循环神经网络(Recurrent Neural Network,RNN)
循环神经网络是时间预测的模型,目的是用来处理序列数据,根据当前和历史的输入进行实现预测。
传统的神经网络模型是从输入层到隐含层在到输出层,层与层之间是全连接的,但是每层之间的节点的无连接的。
RNN模型图:
RNN网络也包含输入层、隐藏层和输出层。RNN网络中的隐藏层的信息除了给本时刻传递到输出层之外同时为下一个时刻的隐藏层提供输入。隐藏层内部的神经单元可以自连也可以互连。(方便记忆:一个循环神经网络是由隐藏层相连的多个感知机构成。【个人理解,有错请指正】)
循环神经网络的构造
我们先看循环神经网络的具体构造。假设(oldsymbol{X}_t in mathbb{R}^{n imes d})是时间步(t)的小批量输入,(oldsymbol{H}_t in mathbb{R}^{n imes h})是该时间步的隐藏变量,则:
其中,(oldsymbol{W}_{xh} in mathbb{R}^{d imes h}),(oldsymbol{W}_{hh} in mathbb{R}^{h imes h}),(oldsymbol{b}_{h} in mathbb{R}^{1 imes h}),(phi)函数是非线性激活函数。由于引入了(oldsymbol{H}_{t-1} oldsymbol{W}_{hh}),(H_{t})能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。由于(H_{t})的计算基于(H_{t-1}),上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。
在时间步(t),输出层的输出为:
其中(oldsymbol{W}_{hq} in mathbb{R}^{h imes q}),(oldsymbol{b}_q in mathbb{R}^{1 imes q})。
input的大小是(batch_size, step)
output的大小是(batch_size, step)
Ht的大小是(num_input, num_hidden)
参数函数初始化:
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
# num_inputs: d
# num_hiddens: h, 隐藏单元的个数是超参数
# num_outputs: q
def get_params():
def _one(shape):
param = torch.zeros(shape, device=device, dtype=torch.float32)
nn.init.normal_(param, 0, 0.01)
return torch.nn.Parameter(param)
# 隐藏层参数
W_xh = _one((num_inputs, num_hiddens))
W_hh = _one((num_hiddens, num_hiddens))
b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device))
# 输出层参数
W_hq = _one((num_hiddens, num_outputs))
b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device))
return (W_xh, W_hh, b_h, W_hq, b_q)
def rnn(inputs, state, params):
# inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵
W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
for X in inputs:
H = torch.tanh(torch.matmul(X, W_xh) + torch.matmul(H, W_hh) + b_h)
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)
以上根据rnn公式编写rnn模型的定义。
采样方法和隐藏层状态的初始化:
采用相邻采样仅在每个训练周期开始的时候初始化隐藏层状态是因为相邻的两个批量在原始数据上是连续的;
采用随机采样需要在每个小批量更新前初始化隐藏层状态因为因为每一个样本包含完整的时间序列信息。