深度学习-循环神经网络(RNN)
循环神经网络(Recurrent Neural Network)
上一章我们已经介绍了CNN,可能我们会想这里为什么还需要构建一种新的网络RNN呢?因为现实生活中存在很多序列化结构,我们需要建立一种更优秀的序列数据模型。
- 文本:字母和词汇的序列
- 语音:音节的序列
- 视频:图像帧的序列
- 时态数据:气象观测数据,股票交易数据、房价数据等

循环神经网络是一种人工神经网络,它的节点间的连接形成一个遵循时间序列的有向图,它的核心思想是,样本间存在顺序关系,每个样本和它之前的样本存在关联。通过神经网络在时序上的展开,我们能够找到样本之间的序列相关性。
下面给出RNN的一般结构:

其中各个符号的表示:x_t,s_t,o_t分别表示的是t时刻的输入、记忆和输出,U,V,W是RNN的连接权重,b_s,b_o是RNN的偏置,\sigma,\varphi是激活函数,\sigma通常选tanh或sigmoid,\varphi通常选用softmax。
其中 softmax 函数,用于分类问题的概率计算。本质上是将一个K维的任意实数向量压缩 (映射)成另一个K维的实数向量,其中向量中的每个元素取值都介于(0,1)之间。
\sigma(\vec{z})_{i}=\frac{e^{z_{i}}}{\sum_{j=1}^{K} e^{z_{j}}}
RNN的一般结构
-
Elman Network

-
Jordan Network

各种不同的RNN结构

RNN训练算法 - BPTT
我们先来回顾一下BP算法,就是定义损失函数 Loss 来表示输出 \hat{y} 和真实标签 y 的误差,通过链式法则自顶向下求得 Loss 对网络权重的偏导。沿梯度的反方向更新权重的值, 直到 Loss 收敛。而这里的 BPTT 算法就是加上了时序演化,后面的两个字母 TT 就是 Through Time。

我们先定义输出函数:
\begin{array}{l}s_{t}=\tanh \left(U x_{t}+W s_{t-1}\right) \\ \hat{y}_{t}=\operatorname{softmax}\left(V s_{t}\right)\end{array}
再定义损失函数:
\begin{aligned} E_{t}\left(y_{t}, \hat{y}_{t}\right) =-y_{t} \log \hat{y}_{t} \\ E(y, \hat{y}) =\sum_{t} E_{t}\left(y_{t}, \hat{y}_{t}\right) \\ =-\sum_{t} y_{t} \log \hat{y}_{t}\end{aligned}

我们分别求损失函数 E 对 U、V、W的梯度:
\begin{array}{l}\frac{\partial E}{\partial V}=\sum_{t} \frac{\partial E_{t}}{\partial V} \\ \frac{\partial E}{\partial W}=\sum_{t} \frac{\partial E_{t}}{\partial W} \\ \frac{\partial E}{\partial U}=\sum_{t} \frac{\partial E_{t}}{\partial U}\end{array}
- 求 E 对 V 的梯度,先求 E_3 对 V 的梯度
\begin{aligned} \frac{\partial E_{3}}{\partial V} &=\frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial V} \\ &=\frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial z_{3}} \frac{\partial z_{3}}{\partial V} \end{aligned}
其中 z_3 = V s_3,然后求和即可。
- 求 E 对 W 的梯度,先求 E_3 对 W 的梯度
\begin{array}{c}\frac{\partial E_{3}}{\partial W}=\frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}} \frac{\partial s_{3}}{\partial W} \\ s_{3}=\tanh \left(U x_{3}+W s_{2}\right) \\ \frac{\partial E_{3}}{\partial W}=\sum_{k=0}^{3} \frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}} \frac{\partial s_{3}}{\partial s_{k}} \frac{\partial s_{k}}{\partial W} \\ \frac{\partial E_{3}}{\partial W}=\sum_{k=0}^{3} \frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}}\left(\prod_{j=k+1}^{3} \frac{\partial s_{j}}{\partial s_{j-1}}\right) \frac{\partial s_{k}}{\partial W}\end{array}
其中: s_3 依赖于 s_2,而 s_2 又依赖于 s_1 和 W ,依赖关系 一直传递到 t = 0 的时刻。因此,当我们计算对于 W 的偏导数时,不能把 s_2 看作是常数项!
- 求 E 对 U 的梯度,先求 E_3 对 U 的梯度
\begin{array}{c}\frac{\partial E_{3}}{\partial W}=\frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}} \frac{\partial s_{3}}{\partial U} \\ s_{3}=\tanh \left(U x_{3}+W s_{2}\right) \\ \frac{\partial E_{3}}{\partial U}=\sum_{k=0}^{3} \frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}} \frac{\partial s_{3}}{\partial s_{k}} \frac{\partial s_{k}}{\partial U}\end{array}
长短时记忆网络
在RNN中,存在一个很重要的问题,就是梯度消失问题,一开始我们不能有效的解决长时依赖问题,其中梯度消失的原因有两个:BPTT算法和激活函数Tanh
\frac{\partial E_{3}}{\partial W}=\sum_{k=0}^{3} \frac{\partial E_{3}}{\partial \hat{y}_{3}} \frac{\partial \hat{y}_{3}}{\partial s_{3}}\left(\prod_{j=k+1}^{3} \frac{\partial s_{j}}{\partial s_{j-1}}\right) \frac{\partial s_{k}}{\partial W}
有两种解决方案,分别是ReLU函数和门控RNN(LSTM).
LSTM
LSTM,即长短时记忆网络,于1997年被Sepp Hochreiter 和Jürgen Schmidhuber提出来,LSTM是一种用于深度学习领域的人工循环神经网络(RNN)结构。一个LSTM单元由输入门、输出门和遗忘门组成,三个门控制信息进出单元。

- LSTM依靠贯穿隐藏层的细胞状态实现隐藏单元之间的信息传递,其中只有少量的线性操作
- LSTM引入了“门”机制对细胞状态信息进行添加或删除,由此实现长程记忆
- “门”机制由一个Sigmoid激活函数层和一个向量点乘操作组成,Sigmoid层的输出控制了信息传递的比例
遗忘门 :LSTM通过遗忘门(forget gate)实现对细胞状态信息遗忘程度的控制,输出当前状态的遗忘权重,取决于 h_{t−1} 和 x_t.
f_{t}=\sigma\left(W_{f} \cdot\left[h_{t-1}, x_{t}\right]+b_{f}\right)

输入门 :LSTM通过输入门(input gate)实现对细胞状态输入接收程度的控制,输出当前输入信息的接受权重,取决于 h_{t−1} 和 x_t.
\begin{array}{c}i_{t}=\sigma\left(W_{i} \cdot\left[h_{t-1}, x_{t}\right]+b_{i}\right) \\ \tilde{C}_{t}=\tanh \left(W_{C} \cdot\left[h_{t-1}, x_{t}\right]+b_{C}\right)\end{array}

输出门 :LSTM通过输出门(output gate)实现对细胞状态输出认可程度的控制,输出当前输出信息的认可权重,取决于 h_{t−1} 和 x_t.
o_{t}=\sigma\left(W_{o} \cdot\left[h_{t-1}, x_{t}\right]+b_{o}\right)

状态更新 :“门”机制对细胞状态信息 进行添加或删除,由此实现长程记忆。
\begin{array}{c}C_{t}=f_{t} * C_{t-1}+i_{t} * \tilde{C}_{t} \\ h_{t}=o_{t} * \tanh \left(C_{t}\right)\end{array}

下面给出一个标准化的RNN例子
#构造RNN网络,x的维度5,隐层的维度10,网络的层数2
rnn_ seq = nn.RNN(5, 10,2)
#构造一个输入序列,长为6,batch是3,特征是5
X =V(torch. randn(6, 3,5))
#out,ht = rnn_ seq(x, h0) # h0可以指定或者不指定
out,ht = rnn_ seq(x)
# q1:这里out、ht的size是多少呢? out:6*3*10, ht:2*3*10
#输入维度50,隐层100维,两层
Lstm_ seq = nn.LSTM(50, 100,num layers=2 )
#输入序列seq= 10,batch =3,输入维度=50
lstm input = torch. randn(10,3,50)
out, (h, c) = lstm_ seq(lstm_ _input) #使用默认的全0隐藏状态
