不错的学习资源:CS 230 - Recurrent Neural Networks Cheatsheet

循环神经网络Recurrent Neural Network,RNN)是一类用于处理序列数据的神经网络。就像卷积网络是专门用于处理网格化数据的神经网络,循环神经网络是专门用于处理序列的神经网络。正如卷积网络可以很容易地扩展到具有很大宽度和高度的图像,以及处理大小可变的图像,循环网络可以扩展到更长的序列(比不基于序列的特化网络长得多)。大多数循环网络也能处理可变长度的序列。

简单循环神经网络

循环神经网络的基本想法是:在序列数据的每一个位置上重复使用相同的前馈神经网络,并将相邻位置的神经网络连接起来;用前馈神经网络隐层的输出表示当前位置的“状态”,假设当前位置的状态依赖于当前位置的输入和之前位置的状态。这样就可以表示和学习序列数据中的局部和全局特征,并解决序列建模中的关键问题。
循环神经网络的基本模型是简单循环神经网络Simple Recurrent Neural Network,S-RNN)。称以下的神经网络为简单循环神经网络。神经网络以序列数据 x1,x2,,xTx_1, x_2, \cdots, x_T 为输入,每一项是一个实数向量。在每一个位置上重复使用同一个神经网络结构。在第 tt 个位置上 (t=1,2,,T)(t = 1, 2, \cdots, T),神经网络的隐层或中间层以 xtx_tht1h_{t-1} 为输入,以 hth_t 为输出,其间有以下关系成立:

ht=tanh(Uht1+Wxt+b)\begin{align} h_t = \tanh (U \cdot h_{t-1} + W \cdot x_t + b) \end{align}

其中,xtx_t 表示第 tt 个位置上的输入,是一个实数向量 (xt,1,xt,2,,xt,n)T(x_{t,1}, x_{t,2}, \cdots, x_{t,n})^Tht1h_{t-1} 表示第 t1t-1 个位置的状态,也是一个实数向量 (ht1,1,ht1,2,,ht1,m)T(h_{t-1,1}, h_{t-1,2}, \cdots, h_{t-1,m})^Thth_t 表示第 tt 个位置的状态 (ht,1,ht,2,,ht,m)T(h_{t,1}, h_{t,2}, \cdots, h_{t,m})^T,也是一个实数向量;U,WU, W 是权重矩阵;bb 是偏置向量。神经网络的输出层以 hth_t 为输入,ptp_t 为输出,有以下关系成立:

pt=softmax(Vht+c)\begin{align} p_t = \operatorname{softmax}(V \cdot h_t + c) \end{align}

其中,ptp_t 表示第 tt 个位置上的输出,是一个概率向量 (pt,1,pt,2,,pt,l)T(p_{t,1}, p_{t,2}, \cdots, p_{t,l})^T,满足 pt,i0p_{t,i} \geq 0 (i=1,2,,l)(i = 1, 2, \cdots, l)i=1lpt,i=1\sum_{i=1}^{l} p_{t,i} = 1VV 是权重矩阵;cc 是偏置向量。神经网络输出序列数据 p1,p2,,pTp_1, p_2, \cdots, p_T,每一项是一个概率向量。
以上公式还可以写作

rt=Uht1+Wxt+bht=tanh(rt)zt=Vht+cpt=softmax(zt)\begin{align} r_t & = U \cdot h_{t-1} + W \cdot x_t + b \\ h_t & = \tanh (r_t) \\ z_t & = V \cdot h_t + c \\ p_t & = \operatorname{softmax}(z_t) \end{align}

其中,rtr_t 是隐层的净输入向量,ztz_t 是输出层的净输入向量。隐层的激活函数通常是双曲正切函数,也可以是其他激活函数;输出层的激活函数通常是软最大化函数。
简单循环神经网络架构

循环神经网络的定义中不仅涉及输入的空间和输出的空间,而且涉及状态的空间,并且状态的空间起着重要作用。这一点与一般的前馈神经网络不同。S-RNN对依次给定的输入的实数向量序列 x1,x2,,xTx_1, x_2, \cdots, x_T,首先依次生成状态的实数向量序列 h1,h2,,hTh_1, h_2, \cdots, h_T,然后再依次生成输出的概率向量序列 p1,p2,,pTp_1, p_2, \cdots, p_T。在这个过程中,起核心作用的是式(1)的非线性变换,意味着当前位置的状态 hth_t 由当前位置的输入 xtx_t 和之前位置的状态 ht1h_{t-1} 决定。按顺序反向递归,可以看出每一个位置的状态表示的是到这个位置为止的序列数据的局部特征及全局特征,也称作短距离依存关系和长距离依存关系。
循环神经网络是自回归模型(Auto-regressive Model),也就是说,在序列数据的每一个位置上的预测只使用之前位置的信息,适用于时间序列的预测。循环神经网络的计算需要在序列数据上依次进行。循环神经网络的优点是可以处理任意长度的序列数据,缺点是不能进行并行化处理以提高计算效率
循环神经网络具有强大的表示能力,是动态系统的通用模型。

学习算法

考虑梯度的反向传播,关键是计算Lzt\frac{\partial L}{\partial z_t}Lrt\frac{\partial L}{\partial r_t}。注意隐层各个位置的净输入之间也有依存关系,所以需要先计算第 TT 个位置的偏导数,再依次计算第 T1T-1 到第 11 个位置的偏导数。第 TT 个位置的偏导数 LrT\frac{\partial L}{\partial r_T} 只需通过 LzT\frac{\partial L}{\partial z_T} 计算.

Lzt=LLtLtzt=ytpt,t=1,2,,TLrT=zTrTLzT=hTrTzThTLzT=diag(Itanh2rT)VTLzTLrt=Lrt+1rt+1rt+Lztztrt=htrt(rt+1htLrt+1+zthtLzt)\begin{align} \frac{\partial L}{\partial z_t} & = \frac{\partial L}{\partial L_t} \frac{\partial L_t}{\partial z_t} = y_t - p_t, \quad t = 1, 2, \cdots, T \\ \frac{\partial L}{\partial r_T} & = \frac{\partial z_T}{\partial r_T} \frac{\partial L}{\partial z_T} = \frac{\partial h_T}{\partial r_T} \frac{\partial z_T}{\partial h_T} \frac{\partial L}{\partial z_T} = \text{diag}\left( I - \tanh^2 r_T \right) \cdot V^T \cdot \frac{\partial L}{\partial z_T} \\ \frac{\partial L}{\partial r_t} & = \frac{\partial L}{\partial r_{t+1}} \frac{\partial r_{t+1}}{\partial r_t} + \frac{\partial L}{\partial z_t} \frac{\partial z_t}{\partial r_t} = \frac{\partial h_t}{\partial r_t} \left( \frac{\partial r_{t+1}}{\partial h_t} \frac{\partial L}{\partial r_{t+1}} + \frac{\partial z_t}{\partial h_t} \frac{\partial L}{\partial z_t} \right) \end{align}

下图显示在神经网络上梯度反向传播的过程。梯度 Lrt\frac{\partial L}{\partial r_t} 的计算从第 TT 个位置到第 11 个位置依次进行。因为与序列(时间)的顺序是相反的,所以这个算法被称为随时间的反向传播算法Back Propagation Through Time, BPTT)。

简单循环神经的反向传播

Lc=t=1TztcLzt=t=1TLztLV=t=1TztVLzt=t=1TLzthtTLb=t=1TrtbLrt=t=1TLrtLU=t=1TrtULrt=t=1TLrtht1TLW=t=1TrtWLrt=t=1TLrtxtT\begin{align} \frac{\partial L}{\partial c} & = \sum_{t=1}^{T} \frac{\partial z_t}{\partial c} \frac{\partial L}{\partial z_t} = \sum_{t=1}^{T} \frac{\partial L}{\partial z_t} \\ \frac{\partial L}{\partial V} & = \sum_{t=1}^{T} \frac{\partial z_t}{\partial V} \frac{\partial L}{\partial z_t} = \sum_{t=1}^{T} \frac{\partial L}{\partial z_t} \cdot h_t^T \\ \frac{\partial L}{\partial b} & = \sum_{t=1}^{T} \frac{\partial r_t}{\partial b} \frac{\partial L}{\partial r_t} = \sum_{t=1}^{T} \frac{\partial L}{\partial r_t} \\ \frac{\partial L}{\partial U} & = \sum_{t=1}^{T} \frac{\partial r_t}{\partial U} \frac{\partial L}{\partial r_t} = \sum_{t=1}^{T} \frac{\partial L}{\partial r_t} \cdot h_{t-1}^T \\ \frac{\partial L}{\partial W} & = \sum_{t=1}^{T} \frac{\partial r_t}{\partial W} \frac{\partial L}{\partial r_t} = \sum_{t=1}^{T} \frac{\partial L}{\partial r_t} \cdot x_t^T \end{align}

这里 htV,ZtV,htU,rtU,htW,rtW\frac{\partial h_t}{\partial V}, \frac{\partial Z_t}{\partial V}, \frac{\partial h_t}{\partial U}, \frac{\partial r_t}{\partial U}, \frac{\partial h_t}{\partial W}, \frac{\partial r_t}{\partial W} 是张量。
在循环神经网络的学习过程中,会产生梯度消失和梯度爆炸。造成消失与爆炸的原因与前馈神经网络相同。反向传播的计算依赖以下矩阵的连乘,有可能使得到的矩阵的一些元素趋近0或无穷大。

At=diag(1tanh2rt)UTA_t = \text{diag} \left( 1 - \tanh^2 r_t \right) \cdot U^T

循环神经网络的梯度消失与梯度爆炸更严重,因为矩阵的连乘接近矩阵的连续自乘,而前馈神经网络一般不是。
为避免梯度消失和梯度爆炸,可以使用LSTM和GRU。

长短时记忆网络

不错的学习资源:Understanding LSTM Networks – colah’s blog
长短期记忆网络Long Short-Term Memory, LSTM)是一种特殊的循环神经网络,能够有效解决传统RNN在长序列训练中的梯度消失或爆炸问题。LSTM通过引入遗忘门、输入门和输出门三种门控机制,以及细胞状态的核心结构,实现了对信息的长期记忆和选择性遗忘。遗忘门决定哪些信息从细胞状态中丢弃,输入门确定哪些新信息存入细胞状态,而输出门则控制当前时刻的输出。这种门控机制使LSTM能够捕捉序列中的长期依赖关系,适用于语音识别、机器翻译和时间序列预测等任务。

以下的循环神经网络称为长短期记忆网络。在循环网络的每一个位置上有状态和记忆元,以及输入门、遗忘门、输出门,构成一个单元。第 tt 个位置上 (t=1,2,,T)(t=1,2,\cdots,T) 的单元是以当前位置的输入 xtx_t、之前位置的记忆元 ct1c_{t-1}、之前位置的状态 ht1h_{t-1} 为输入,以当前位置的状态 hth_t 和当前位置的记忆元 ctc_t 为输出的函数,由以下方式计算。

it=σ(Utht1+Wtxt+bt)ft=σ(Ufht1+Wfxt+bf)σt=σ(Uoht1+Woxt+bo)c~t=tanh(Ucht1+Wcxt+bc)ct=itc~t+ftct1ht=σttanh(ct)\begin{align} i_t & = \sigma(U_t \cdot h_{t-1} + W_t \cdot x_t + b_t) \\ f_t & = \sigma(U_f \cdot h_{t-1} + W_f \cdot x_t + b_f) \\ \sigma_t & = \sigma(U_o \cdot h_{t-1} + W_o \cdot x_t + b_o) \\ \tilde{c}_t & = \tanh(U_c \cdot h_{t-1} + W_c \cdot x_t + b_c) \\ c_t & = i_t \odot \tilde{c}_t + f_t \odot c_{t-1} \\ h_t & = \sigma_t \odot \tanh(c_t) \end{align}

这里 iti_t 是输入门,ftf_t 是遗忘门,σt\sigma_t 是输出门,c~t\tilde{c}_t 是中间结果。状态 hth_t、记忆元 ctc_t、输入门 iti_t、遗忘门 ftf_t、输出门 σt\sigma_t 都是向量,其维度相同。
LSTM单元结构

门控循环单元网络

门控循环单元Gated Recurrent Unit, GRU)是循环神经网络的一种简化变体,通过融合LSTM中的遗忘门和输入门为单一的更新门,并合并细胞状态与隐藏状态,减少了模型参数且保持了与LSTM相近的性能。GRU的核心结构包含重置门和更新门:重置门控制前一时刻隐藏状态对当前候选状态的影响,更新门决定隐藏状态的更新程度。这种设计使GRU在训练效率上优于LSTM,尤其适合处理中等长度的序列任务,如文本生成、语音建模和视频分析。GRU的简洁性使其在计算资源有限时成为LSTM的高效替代方案,同时仍能有效缓解梯度消失问题。
以下的循环神经网络称为门控循环单元网络。在循环网络的每一个位置上有状态及重置门、更新门,构成一个单元。第 tt 个位置上 (t=1,2,,T)(t=1,2,\cdots,T) 的单元是以当前位置的输入 xtx_t、当前位置的状态 ht1h_{t-1} 为输入,以当前位置的状态 hth_t 为输出的函数,按以下方式计算。

rt=σ(Urht1+Wrxt+br)zt=σ(Uzht1+Wzxt+bz)h~t=tanh(Uhrtht1+Whxt+bh)ht=(1zt)h~t+ztht1\begin{align} r_t & = \sigma(U_r \cdot h_{t-1} + W_r \cdot x_t + b_r) \\ z_t & = \sigma(U_z \cdot h_{t-1} + W_z \cdot x_t + b_z) \\ \tilde{h}_t & = \tanh(U_h \cdot r_t \odot h_{t-1} + W_h \cdot x_t + b_h) \\ h_t & = (\boldsymbol{1} - z_t) \odot \tilde{h}_t + z_t \odot h_{t-1} \end{align}

这里 rtr_t 是重置门,ztz_t 是更新门,h~t\tilde{h}_t 是中间结果。状态 hth_t、重置门 rtr_t、更新门 ztz_t 都是向量,其维度相同。rtr_t控制历史信息对新状态生成的参与度,ztz_t控制历史信息对最终输出的保留比例。rt=0r_t = 0,中间结果中之前位置的信息不做保留;zt=1z_t = 1,模型忽略新输入,完全复制历史状态ht=ht1h_t = h_{t-1}zt=0z_t = 0,模型忽略历史,完全采用新候选状态ht=h~th_t = \tilde{h}_t

GRU单元结构

深度循环神经网络

简单循环神经网络只有一个隐层或中间层。可以扩展到有多个隐层的神经网络,称为深度循环神经网络。多个隐层的状态之间存在层次化关系,模型具有更强的表示能力。拥有 ll 个隐层的深度循环神经网络在第 tt 个位置的定义如下。
第1个隐层是

ht(1)=tanh(U(1)ht1(1)+W(1)xt+b(1))\begin{align} h_t^{(1)} = \tanh(U^{(1)} \cdot h_{t-1}^{(1)} + W^{(1)} \cdot x_t + b^{(1)}) \end{align}

ll 个隐层是

ht(l)=tanh(U(l)ht1(l)+W(l)ht(l1)+b(l))\begin{align} h_t^{(l)} = \tanh(U^{(l)} \cdot h_{t-1}^{(l)} + W^{(l)} \cdot h_t^{(l-1)} + b^{(l)}) \end{align}

输出层是

pt=softmax(Vht(t)+c)\begin{align} p_{t} = \text{softmax}(V\cdot h_{t}^{(t)}+c) \end{align}

深度循环神经网络结构

深度学习实战#3 风电功率预测

这个是今年统计建模大赛我们队伍的选题,我担任建模手。虽然现在来看当时的论文、工作略显青涩,但是这仍然可以作为一个比较好的示例。
数据集来源于来自中国国家电网可再生能源发电预测大赛的太阳能和风电数据 | Scientific Data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LSTMModel(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size):
super().__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers

self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
self.dropout = nn.Dropout(0.2)

def forward(self, x):
h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)

out, _ = self.lstm(x, (h0, c0))
out = self.dropout(out[:, -1, :]) # 取最后一个时间步的输出
out = self.fc(out)
return out
输出结果:风电功率预测(额定功率:99MW  数据量:7000条)