第一章 手把手教你深度学习和实战-----线性回归+梯度下降法
第二章 手把手教你深度学习和实战-----逻辑回归算法
第三章 手把手教你深度学习和实战-----全连接神经网络
第四章 手把手教你深度学习和实战-----卷积神经网络
第五章 手把手教你深度学习和实战-----循环神经网络
相比较图像这类数据在空间上有一定的特点外,还有一类数据是在时间上有一定的特点的,这类的数据称之为时间序列数据。从定义上来说,就是一串按时间维度索引的数据。比如,自然语言是就是一个很典型的时间序列数据,每个字或者每个词是根据时间维度上不断输出出来,具有很强的先后时间顺序。除了自然语言数据之外还有天气数据、车流量数据、电力负荷数据等数据是时间序列数据,这类数据都是按时间维度索引的数据。
时间序列数据有一个最重要的特征是前一段时间维度的数据对后面一段时刻的数据有着很大的影响,例如自然语言,有这样的一句话:“我在中国生活了很多年,因此我会很流利的说出__”。很显然这里的空格应该填“中文”,“汉语”,“普通话”等和中文相关的词。如果这时候在空格里面填“日语”、“英语”等其他的国家的语言的话,上面这句话就会很容易让人产生歧义,因为前面的“我在中国生活了很多年”,这段话对后面产生很大的影响。
例如天气数据,一份天气数据中包含着每天的温度,湿度,气压、风速是否下雨等气象特征,前一段时刻的气象特征数据必然影响着后面一段时间的气象数据,因此生活当中的温度往往都是缓和的周期变化,有很明显的周期性,季节性变动。
对于这样的时间序列数据,全连接神经网络和卷积神经网络都不能很好的考虑到时间序列数据之间的序列关系。因为全连接神经网络和卷积神经网络都属于前向反馈网络,模型的最终的输出和模型本身没有关联。而循环神经网络模型的前一刻时刻最终输出可以作为下一个时刻的输入从而学习到前面时刻的信息,因此循环神经网络可以很好的学习到时间序列数据中的序列关系,从而在处理时间序列数据的时候往往有很好的效果。
循环神经网络中比较经典基础的神经网络是RNN神经网络,这个网络基本上阐述了循环神经网络的运行原理,因此想弄懂循环神经网络中的其他改进的神经网络例如LSTM和GRU的话,必须要首先弄懂RNN神经网络。 如上图所示,为RNN神经网络的基本结构,说句实话当我第一次看到这个结构的时候是一脸懵的,从网络结构上来说,这个网络结构图比全连接神经网络和卷积神经网络难懂多了。难懂的原因是因为这个图貌似太简单了,貌似很难从图中理解循环神经网络,下面对这个图进行讲解。
X t X_t Xt为输入的时间序列数据,输入的数据按时刻输入的,也就是数据的输入是有先后顺序的。
h t \mathrm{h}_t ht为每个时刻数据通过神经网络计算的隐藏层的输出,因此A就是RNN神经网络的隐藏层,同时这个输出会和下一时刻输入的数据相加,然后作为新的输入送入到神经网络中进行神经网络的计算;
最后输出,不断循环重复上述的过程直至最后一个时刻的输入数据运算结束。由于RNN神经网络是对输入的数据不断进行循环计算的,因此相对来说RNN神经网络的计算速度比较慢。
貌似,上述的RNN神经网络的运行过程还不是那么直观,那么可以将上述的图展开成如下的形状应该容易理解一点。这里要特别提示:看这个图最好不要从全局去看,这样容易将你带入理解的误区,而是应该从左往右一个时刻一个时刻的计算单元去看。这样才能更好的理解RNN神经网络是逐个时刻进行计算的,因此前面时刻对后面时刻的输出是有影响的。 如上图所示,输入数据包含0时刻到t时刻的数据征。
首先输入0时刻的数据特征进行神经网络的计算得到隐藏层输出 h 0 \mathrm{h}_0 h0,将这个隐藏层输出 h 0 \mathrm{h}_0 h0和1时刻的输入 X 1 X_1 X1进行加权求和,此时1时刻的输入中已经包含了0时刻的数据信息了;
然后再将上述的加权求和后的数据输入到神经网络中进行计算得到1时刻的隐藏层输出 h 1 \mathrm{h}_1 h1 ,再将 h 1 \mathrm{h}_1 h1和 X 2 X_2 X2进行加权求和,此时2时刻的输入已经包含了0、1的信息了;
再将加权求和后的数据进行神经网络计算,得到2时刻的隐藏层输出 h 2 \mathrm{h}_2 h2 ,不断循环上述过程直到t时刻结束,得到t时刻的隐藏层输出 h t \mathrm{h}_t ht ;
从理论上讲这个 h t \mathrm{h}_t ht 输出是由0到t-1时刻的输出和t时刻的输入决定的,因此RNN神经网络就具有记忆功能,对前面时刻的输入的数据的记忆从而影响后面时刻的输出。
下面从RNN神经网络的具体数据表达式来理解RNN神经网络,如下式所示为RNN神经网络的数学表达式。 O t = g ( V ⋅ h t ) h t = f ( U ⋅ X t + W ⋅ h t − 1 ) \begin{gathered} O_t=g\left(V \cdot h_t\right) \\ h_t=f\left(U \cdot X_t+W \cdot h_{t-1}\right) \end{gathered} Ot=g(V⋅ht)ht=f(U⋅Xt+W⋅ht−1) 其中, X t X_t Xt为t时刻的输入特征数据, U U U为对应的参数权重; h t − 1 h_{t-1} ht−1为前一时刻的隐藏层的输出, W W W 为对应的参数权重; h t \mathrm{h}_t ht为当前时刻的隐藏层的输出, V V V为对应的权重参数; O t O_t Ot为当前t时刻的输出层的输出。因此将权重画到图中可以得到如下图所示:
此图相对一开始的RNN神经网络单元多了输出层,如果此时我们将右边的W去掉,那么此时的RNN神经网络就变成了全连接神经网络,具体的图如下所示。
当然上述的全连接神经网络,看起来是有点变扭的,因为我经常看到的全连接的神经网络的图是如下图所示的。
因此再将上述的全连接神经网络的图中加入上述的前一时刻的隐藏层输出和其对应的权重,那么就变成了如图所示的图:
按如下的对应关系应该就很清晰将上述的两个图对应起来了。
上述的对比图应该更容易帮助我们理解RNN神经网络,初学者在学习RNN的时候很容易将RNN神经网络中图中的中间层理解为只有一个神经元节点,因为可能很多人绘图为了突出其中的循环的特点而忽略其输入是一个特征向量,也就是特征向量中含有一个或多个输入值,同时隐藏层的也可以设置一个或者多个神经元,这就容易造成理解上的困难。
循环神经网络中都用到了权重共享的概念,在深度学习中,卷积神经网络也用到了权重共享的概念。在卷积神经网络中,卷积层的卷积核就是其权重,卷积核在特征图上进行滑动,不断和特征图中的数值进行计算,因此一个特征图共享了一组权重,而不像全连接神经网络一样,每个输入的数据特征都有自己的权重参数,这样会让计算机的计算量大大提升,同时也更容易使网络模型过拟合,从而模型的泛化性不强。
仔细观察循环神经网络的模型图(如上图),就会发现神经网络的不同时刻的计算的权重W、V、U是一样的,这就是意味着这些权重是每个时刻共享的。这样的设置使RNN神经网络的输出可以使不定长的,输入的数据按时间序列顺序不断输入神经网络就可以得到对应的输出了,因此循环神经网络的网络参数要比全连接神经网络参数要少很多,从而神经网络的参数更新的计算量要少很多,这大大减少了计算机的计算量。
下面来用一个案例来巩固RNN神经网络的知识点。
假设现在有一个RNN神经网络的网络结构是由输入层、一层隐藏层、和输出层组成。输入的数据含有3个时间步、每个时间步含有2个数据特征,因此RNN神经网络的输入层含有2个神经元、同时设定隐藏层含有2个神经元、神经网络输出层有两个输出,因此输出层含有两个神经元;如下图所示:
该神经网络的输出数据如下式所示: [ 1 1 ] [ 1 1 ] [ 2 2 ] \left[\begin{array}{l} 1 \\ 1 \end{array}\right]\left[\begin{array}{l} 1 \\ 1 \end{array}\right]\left[\begin{array}{l} 2 \\ 2 \end{array}\right] [11][11][22] 为了方便计算的同时也可以体现RNN神经网络的特点,设定神经网络的所有权重的大小都为1且没有偏置,激活函数也都是线性激活函数。同时在计算第一个时间步的输入的时候没有前一时刻的隐藏层输出值作为输入的,因此设定此时的隐藏层输出值为[0,0]。此时第一个时间步的计算如下图所示:
按RNN神经网络的计算公式可以计算出,隐藏层的2个神经元的值分别为:
第1个神经元: 1 × 1 + 1 × 1 + 0 × 1 + 0 × 1 = 2 1 \times 1+1 \times 1+0 \times 1+0 \times 1=2 1×1+1×1+0×1+0×1=2 第2个神经元: 1 × 1 + 1 × 1 + 0 × 1 + 0 × 1 = 2 1 \times 1+1 \times 1+0 \times 1+0 \times 1=2 1×1+1×1+0×1+0×1=2 输出层第1个输出: 2 × 1 + 2 × 1 = 4 2 \times 1+2 \times 1=4 2×1+2×1=4 输出层第2个输出: 2 × 1 + 2 × 1 = 4 2 \times 1+2 \times 1=4 2×1+2×1=4 此时RNN神经网络的隐藏层的值更新为[2,2],输入第2个时间步中的数据,具体计算如下图所示:
因为每个时刻的权重值是共享的,因此该时刻的权重是不变的,都是为1。因此隐藏层的2个神经元的值分别为:
第1个神经元: 1 × 1 + 1 × 1 + 2 × 1 + 2 × 1 = 6 1 \times 1+1 \times 1+2 \times 1+2 \times 1=6 1×1+1×1+2×1+2×1=6 第2个神经元: 1 × 1 + 1 × 1 + 2 × 1 + 2 × 1 = 6 1 \times 1+1 \times 1+2 \times 1+2 \times 1=6 1×1+1×1+2×1+2×1=6 输出层第1个输出: 6 × 1 + 6 × 1 = 12 6 \times 1+6 \times 1=12 6×1+6×1=12 输出层第2个输出: 6 × 1 + 6 × 1 = 12 6 \times 1+6 \times 1=12 6×1+6×1=12 此时RNN神经网络的隐藏层的值更新为[6,6],输入第3个时间步中的数据,具体计算如下图所示:
隐藏层的2个神经元的值分别为:
第1个神经元: 2 × 1 + 2 × 1 + 6 × 1 + 6 × 1 = 16 2 \times 1+2 \times 1+6 \times 1+6 \times 1=16 2×1+2×1+6×1+6×1=16 第2个神经元: 2 × 1 + 2 × 1 + 6 × 1 + 6 × 1 = 16 2 \times 1+2 \times 1+6 \times 1+6 \times 1=16 2×1+2×1+6×1+6×1=16 输出层第1个输出: 16 × 1 + 16 × 1 = 32 16 \times 1+16 \times 1=32 16×1+16×1=32 输出层第2个输出: 16 × 1 + 16 × 1 = 32 16 \times 1+16 \times 1=32 16×1+16×1=32 最终得到每个时刻的输出值为: [ 4 4 ] [ 12 12 ] [ 32 32 ] \left[\begin{array}{l} 4 \\ 4 \end{array}\right]\left[\begin{array}{l} 12 \\ 12 \end{array}\right]\left[\begin{array}{l} 32 \\ 32 \end{array}\right] [44][1212][3232] 至此,一个完整的RNN结构我们已经经历了一遍,我们注意到,每一时刻的输出结果都与上一时刻的输入有着非常大的关系,同时就算不同时刻输入的数据是一样也会导致输出不一样的。如果我们将输入序列换个顺序,那么我们得到的结果也将是截然不同,这就是RNN的特性,可以处理序列数据,同时对序列也很敏感。
RNN神经网络虽然从理论上可以学习到任意时刻中的有用信息,但是在神经网络的反向传播进行参数更新的时候容易出现梯度爆炸和梯度消失的问题,从而导致梯度值过大或者过小,此时就可能会导致参数不能更新的情况。
为了更加细致的了解这个问题出现的原因,需要从求解RNN神经网络参数的梯度来讲解,首先RNN神经网络的数学模型公式如下所示: O t = g ( V ⋅ h t ) h t = f ( U ⋅ X t + W ⋅ h t − 1 ) \begin{gathered} O_t=g\left(V \cdot h_t\right) \\ h_t=f\left(U \cdot X_t+W \cdot h_{t-1}\right) \end{gathered} Ot=g(V⋅ht)ht=f(U⋅Xt+W⋅ht−1) 现在利用求参数W来讲解RNN神经网络出现梯度爆炸和梯度消失的原因。
假设存在误差 ∂ L t \partial L_t ∂Lt求误差对参数W的导数。RNN神经网络中反向传播算法利用的是时间反向传播算法(BPTT),需要求解所有时间步的梯度之后,利用多变量链式求导法则求导得到的梯度如下式所示: ∂ L t ∂ w = ∑ i = 0 i = t ∂ L t ∂ 0 t ∂ 0 t ∂ h t ∂ h t ∂ h i ∂ h i ∂ w \frac{\partial L_t}{\partial w}=\sum_{i=0}^{i=t} \frac{\partial L_t}{\partial 0_t} \frac{\partial 0_t}{\partial h_t} \frac{\partial h_t}{\partial h_i} \frac{\partial h_i}{\partial w} ∂w∂Lt=i=0∑i=t∂0t∂Lt∂ht∂0t∂hi∂ht∂w∂hi 其中除了 ∂ h t ∂ h i \frac{\partial h_t}{\partial h_i} ∂hi∂ht其他的偏导数相对来说都很好计算,利用链式求导法则对 ∂ h t ∂ h i \frac{\partial h_t}{\partial h_i} ∂hi∂ht进行展开求导得到如下式所示: ∂ h t ∂ h i = ∂ h t ∂ h t − 1 ⋅ ∂ h t − 1 ∂ h t − 2 ⋯ ∂ h i + 1 ∂ h i = ∏ k = i k = t − 1 ∂ h k + 1 ∂ h k \frac{\partial h_t}{\partial h_i}=\frac{\partial h_t}{\partial h_{t-1}} \cdot \frac{\partial h_{t-1}}{\partial h_{t-2}} \cdots \frac{\partial h_{i+1}}{\partial h_i}=\prod_{k=i}^{k=t-1} \frac{\partial h_{k+1}}{\partial h_k} ∂hi∂ht=∂ht−1∂ht⋅∂ht−2∂ht−1⋯∂hi∂hi+1=k=i∏k=t−1∂hk∂hk+1 假设现在我们已经知道: ∂ h k ∂ h k − 1 = f ′ ⋅ w \frac{\partial h_k}{\partial h_{k-1}}=f^{\prime} \cdot w ∂hk−1∂hk=f′⋅w 其中 f ′ f^{\prime} f′为激活函数的导数,假设以sigmoid为例, f ∈ ( 0 , 1 ) f \in(0,1) f∈(0,1),那么其导数为 f ′ = f ( 1 − f ) ∈ ( 0 , 1 4 ) f^{\prime}=f(1-f) \in\left(0, \frac{1}{4}\right) f′=f(1−f)∈(0,41) ,那么当W<4的时候, ∂ h k ∂ h k − 1 < 1 \frac{\partial h_k}{\partial h_{k-1}}<1 ∂hk−1∂hk<1,经过多次相乘以后, ∂ L t ∂ w \frac{\partial L_t}{\partial w} ∂w∂Lt逐渐接近0,此时就出现了梯度消失。 那么当W>4的时候, ∂ h k ∂ h k − 1 > 1 \frac{\partial h_k}{\partial h_{k-1}}>1 ∂hk−1∂hk>1,经过多次相乘以后, ∂ L t ∂ w \frac{\partial L_t}{\partial w} ∂w∂Lt 逐渐变大,此时出现梯度爆炸。
这里要注意的是误差对参数的导数是累加的, ∂ L t ∂ w = ∑ i = 0 i = t ∂ L t ∂ 0 t ∂ 0 t ∂ h t ∂ h t ∂ h i ∂ h i ∂ w \frac{\partial L_t}{\partial w}=\sum_{i=0}^{i=t} \frac{\partial L_t}{\partial 0_t} \frac{\partial 0_t}{\partial h_t} \frac{\partial h_t}{\partial h_i} \frac{\partial h_i}{\partial w} ∂w∂Lt=∑i=0i=t∂0t∂Lt∂ht∂0t∂hi∂ht∂w∂hi ,那么意味着整个误差对参数的总梯度并不会消失,因此比较近距离的梯度是一直存在的,当其占主导地位的时候,模型就无法建立远距离的依赖性。
由于RNN神经网络在神经网络参数更新的时候容易出现梯度消失或者梯度爆炸的情况,这样会导致神经网络不能很好的学习较长序列中的信息,因此RNN神经网络只具有短时的记忆。好在经过学者的努力,对RNN神经网络进行了改进,发明了LSTM神经网络,LSTM可以在一定的程度上缓解梯度消失的问题,同时相对RNN神经网络来说有更好的长时间序列的学习能力。因此目前,只要提到循环神经网络一般都是指的LSTM神经网络。但是RNN神经作为LSTM的基础,在学习LSTM神经网络之前一定要把RNN搞明白,否则在学习LSTM网络的时候会更加的难以理解,LSTM神经网络相对RNN只是神经网络单元中多了一些控制门的计算,因此只要在RNN神经网络的基础上搞清楚这些控制门的计算,就可以理解LSTM神经网络了。为了证明RNN到LSTM的转变的确就是神经网络单元中出现了一些控制门的相对复杂的运算。我们可以将上述的RNN神经网络图转化为如下图所示:
而从RNN神经网络改进后的LSTM神经网络的结构图可以如下图所示:
从RNN神经网络结构图和LSTM神经网络结构图对比可以发现,除了神经网络单元计算复杂了一点以外,其他的基本上和RNN是一样的。因此只要搞清楚LSTM神经网络的网络单元,然后按照RNN神经网络的运行方式对输入数据进行前向传播和反向传播和最终的神经网络的输出。因此,在这里要再次重申一下,在学习理解LSTM神经网络之前,一定要把RNN神经网络的原理和整个运行方式搞懂。首先将LSTM神经网络的数学模型摆出,具体如下式所示: f t = σ ( W f ⋅ [ h t − 1 , X t ] + b f ) i t = σ ( W i ⋅ [ h t − 1 , X t ] + b i ) C ~ t = tanh ( W C ⋅ [ h t − 1 , X t ] + b C ) C t = f t ∗ C t − 1 + i t ∗ C ~ t o t = σ ( W o [ h t − 1 , X t ] + b o ) h t = o t ∗ tanh ( C t ) \begin{gathered} f_t=\sigma\left(W_f \cdot\left[h_{t-1}, X_t\right]+b_f\right) \\ 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) \\ C_t=f_t * C_{t-1}+i_t * \tilde{C}_t \\ o_t=\sigma\left(W_o\left[h_{t-1}, X_t\right]+b_o\right) \\ h_t=o_t * \tanh \left(C_t\right) \end{gathered} ft=σ(Wf⋅[ht−1,Xt]+bf)it=σ(Wi⋅[ht−1,Xt]+bi)C~t=tanh(WC⋅[ht−1,Xt]+bC)Ct=ft∗Ct−1+it∗C~tot=σ(Wo[ht−1,Xt]+bo)ht=ot∗tanh(Ct) 可以从公式上看来的确要比RNN神经网络复杂许多,但是也不要害怕,其实一切都是纸老虎。如果仔细去分析的话就会发现没有看起来那么复杂,下面就对LSTM神经网络单元进行具体的分析。
如上图所示为LSTM神经网络的单元结构。上图中σ表示的sigmoid激活函数,sigmoid是把值映射到0~1之间,这样的设置有助于更新或忘记信息。tanh为tanh激活函数。
因为任何数乘以0都得0,这部分信息就会剔除掉,同样的,任何数乘以1都得到它本身,这部分信息就会完美地保存下来。相当于是1则记住,是0则忘掉,要么是0-1之间的数来有选择的记住数据。因此LSTM神经网络的原则是:因记忆能力有限,记住重要的,忘记无关紧要的。
此外,对于图中使用的各种元素的图标中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。粉色的圈代表pointwise的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。
LSTM最重要的设计是传输带,具体如下图所示:
LSTM就是依靠这条传输带来保存之前经过筛选的有用信息,并利用这些信息参与当前运算,从而使得当前时刻的输出是由之前筛选后的信息和当前的输入信息综合影响输出的。相比较RNN神经网络一股脑的将之前的所有信息都作为输入有着本质的改进。
LSTM拥有三种类型的门结构:遗忘门、输入门和输出门,来保护和控制C(细胞)的状态。下面,我们来介绍这三个门。
在LSTM中的第一步是决定我们会从细胞状态中丢弃什么信息。这个决定通过一个称为“遗忘门”的结构完成。该忘记门会读取上一个输出和当前输入,做一个sigmoid的非线性映射,然后输出一个向量(该向量每一个维度的值都在0到1之间,1表示完全保留,0表示完全舍弃,相当于记住了重要的,忘记了无关紧要的),这个向量和细胞状态相乘,将细胞状态中的信息有用的留下来,没有用的丢弃掉。
遗忘门的在LSTM神经网络单元结构的计算部分如图所示。具体的计算公式如下所示: f t = σ ( W f ⋅ [ h t − 1 , X t ] + b f ) f_t=\sigma\left(W_f \cdot\left[h_{t-1}, X_t\right]+b_f\right) ft=σ(Wf⋅[ht−1,Xt]+bf) 如果上述的公式不好理解的话,可以通过如下的图来更好的解释遗忘门的计算过程。
首先将向量 h t − 1 h_{t-1} ht−1和 X t X_t Xt进行连接操作得到一个新的向量,然后计算参数矩阵 W f W_f Wf和连接后的新向量的乘积,再将这个乘积的结果和偏置 b f b_f bf求和,然后经过sigmoid激活函数进行函数映射,得到向量 f t f_t ft,这个向量 f t f_t ft的每一个元素都是在0-1之间。很显然这里的参数 W f W_f Wf需要通过反向传播进从训练数据中学习。
输入门中一共有两步计算,这两步的计算公式如下所示,其中向量i_t的计算过程和遗忘门很类似。
i t = σ ( W i ⋅ [ h t − 1 , X t ] + b i ) C ~ t = tanh ( W C ⋅ [ h t − 1 , X t ] + b C ) \begin{aligned} 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{aligned} itC~t=σ(Wi⋅[ht−1,Xt]+bi)=tanh(WC⋅[ht−1,Xt]+bC) i t i_t it的具体计算过程可以通过如下的图来表达:
首先将向量 h t − 1 h_{t-1} ht−1和 X t X_t Xt进行连接操作得到一个新的向量,然后计算参数矩阵 W i W_i Wi和连接后的新向量的乘积,再将这个乘积的结果和偏置 b i b_i bi求和,探后经过sigmoid激活函数进行函数映射,得到向量 i t i_t it,这个向量 i t i_t it的每一个元素都是在0-1之间。很显然这里的参数 W i W_i Wi需要通过反向传播进从训练数据中学习。可以看到这个操作几乎和输入门是一样的,只不过是参数矩阵不一样。
C ~ t \tilde{C}_t C~t的计算过程可以通过如下的图来表达:
首先将向量 h t − 1 h_{t-1} ht−1和 X t X_t Xt进行连接操作得到一个新的向量,然后计算参数矩阵 W C W_C WC和连接后的新向量的乘积,再将这个乘积的结果经过tanh激活函数进行非线性映射激活,得到向量 C ~ t \tilde{C}_t C~t,这里的向量 C ~ t \tilde{C}_t C~t的每一个元素都是在[-1,1]之间。很显然这里的参数 W C W_C WC需要通过反向传播进从训练数据中学习。这里和遗忘门不同的是利用了激活函数tanh。
此时已经计算出 f t 、 i t 、 C ~ t f_t 、 i_t、 \tilde{C}_t ft、it、C~t,可以利用这些矩阵向量对传送带上的细胞状态 C t − 1 C_{t-1} Ct−1进行更新了。
具体的计算公式如下所示:需要注意的是,这里利用的是矩阵的对应元素相乘,利用的是符号“*”,而前面的遗忘门和输入门中利用的是矩阵乘法。 C t = f t ∗ C t − 1 + i t ∗ C ~ t C_t=f_t * C_{t-1}+i_t * \tilde{C}_t Ct=ft∗Ct−1+it∗C~t 具体的计算过程可以用如下的图来表示:
首先利用向量f_t和向量 C t − 1 C_{t-1} Ct−1进行对应元素相乘,遗忘门计算出的向量f_t中的元素都是在0-1之间,因此可以选择性的遗忘向量 C t − 1 C_{t-1} Ct−1中的元素从而得到一个新的向量;然后利用输入门计算出的向量 i t i_{t} it和 C t C_{t} Ct进行对应元素相乘这时也得到了一个新的向量,此时将两个新的向量进行求和,这样就更新得到传送带上的细胞状态 C t C_{t} Ct,这个细胞状态包含经过筛选的之前的信息和当前时刻的信息。
完成了细胞状态的更新后,就到了最后一步计算LSTM的输入了,这个输入是由输出计算得出。
输出门的具体计算公式如下图所示,很显然由两部分组成: o t = σ ( W o [ h t − 1 , X t ] + b o ) h t = o t ∗ tanh ( C t ) \begin{aligned} &o_t=\sigma\left(W_o\left[h_{t-1}, X_t\right]+b_o\right) \\ &h_t=o_t * \tanh \left(C_t\right) \end{aligned} ot=σ(Wo[ht−1,Xt]+bo)ht=ot∗tanh(Ct) 第一部分需要计算向量o_t,o_t的计算过程可以用如图表示:
从图中可以看出,这里的向量 O t O_t Ot的计算和前面的遗忘门向量 f t f_t ft、输入门向量 i t i_t it基本上是一样的,只是其中的参数矩阵是不一样的。首先将向量 h ( t − 1 ) h_(t-1) h(t−1)和 X t X_t Xt进行连接操作得到一个新的向量,然后计算参数矩阵 W o W_o Wo和连接后的新向量的乘积,再将这个乘积的结果经过sigmoid激活函数进行函数映射,得到向量 o t o_t ot,这个向量 o t o_t ot的每一个元素都是在0-1之间。这里的参数 W o W_o Wo需要通过反向传播进从训练数据中学习。
具体的计算过程可以用如下的图来表示:
最后一步计算LSTM的输出 h t h_t ht,首先将细胞状态向量 C t C_t Ct经过激活函数tanh激活得到一个新的向量,将向量的所有元素映射到[-1,1]之间。然后将向量 o t o_t ot和新向量对应元素相乘得到LSTM的输出向量 h t h_t ht。但是从图中可以看出,这里的 h t h_t ht有两个输出方向,一份是作为LSTM的输出,还有一份是作为下一个时刻的输入。
至此LSTM神经网络的结构就讲完了,通过上面的分析可以知道LSTM一共有4个参数矩阵分别为 W f W_f Wf、 W i W_i Wi、 W C W_C WC、 W o W_o Wo,这些参数矩阵可以利用梯度下降法更新参数更新,从而最终得到一个LSTM模型来帮忙我们完成对应场景中的任务。
在RNN神经网络中,导致梯度消失的原因是 ∏ k = i k = t − 1 ∂ h k + 1 ∂ h k \prod_{k=i}^{k=t-1} \frac{\partial h_{k+1}}{\partial h_k} ∏k=ik=t−1∂hk∂hk+1这个公式中的每个项都是小于0的情况下导致梯度连乘最后接近于0从而出现梯度消失的情况,因此出现递归是导致梯度消失的主要原因。STM神经网络作为RNN神经网络的改进版本缓解了RNN神经网络的梯度消失。
在LSTM神经网络模型的数学模型中,出现了 C t C_t Ct、 C ( t − 1 ) C_(t-1) C(t−1)这一递归现象,此时我们求一下 ∂ C t ∂ C t − 1 \frac{\partial C_t}{\partial C_{t-1}} ∂Ct−1∂Ct的偏导数。这里注意 f t f_t ft、 i t i_t it、 C ~ t \tilde{C}_t C~t都是 C ( t − 1 ) C_(t-1) C(t−1)的复合函数,因此求导的过程如下所示: ∂ C t ∂ C t − 1 = ∂ C t ∂ f t ∂ f t ∂ h t − 1 ∂ h t − 1 ∂ C t − 1 + ∂ C t ∂ i t ∂ i t ∂ h t − 1 ∂ h t − 1 ∂ C t − 1 + ∂ C t ∂ C ~ t ∂ C ~ t ∂ h t − 1 ∂ h t − 1 ∂ C t − 1 + ∂ C t ∂ C t − 1 \begin{aligned} \frac{\partial C_t}{\partial C_{t-1}} &=\frac{\partial C_t}{\partial f_t} \frac{\partial f_t}{\partial h_{t-1}} \frac{\partial h_{t-1}}{\partial C_{t-1}}+\frac{\partial C_t}{\partial i_t} \frac{\partial i_t}{\partial h_{t-1}} \frac{\partial h_{t-1}}{\partial C_{t-1}} \\ &+\frac{\partial C_t}{\partial \tilde{C}_t} \frac{\partial \tilde{C}_t}{\partial h_{t-1}} \frac{\partial h_{t-1}}{\partial C_{t-1}}+\frac{\partial C_t}{\partial C_{t-1}} \end{aligned} ∂Ct−1∂Ct=∂ft∂Ct∂ht−1∂ft∂Ct−1∂ht−1+∂it∂Ct∂ht−1∂it∂Ct−1∂ht−1+∂C~t∂Ct∂ht−1∂C~t∂Ct−1∂ht−1+∂Ct−1∂Ct 将上述的公式进行一个化简求解: ∂ C t ∂ C t − 1 = C t − 1 σ ′ ( ⋅ ) W f ∗ o t − 1 tanh ′ ( C t − 1 ) + C ~ t σ ′ ( ⋅ ) W i ∗ o t − 1 tanh ′ ( C t − 1 ) + i t tanh ′ ( ⋅ ) W C ∗ o t − 1 tanh ′ ( C t − 1 ) + f t \begin{aligned} \frac{\partial C_t}{\partial C_{t-1}} &=C_{t-1} \sigma^{\prime}(\cdot) W_f * o_{t-1} \tanh ^{\prime}\left(C_{t-1}\right) \\ &+\tilde{C}_t \sigma^{\prime}(\cdot) W_i * o_{t-1} \tanh ^{\prime}\left(C_{t-1}\right) \\ &+i_t \tanh ^{\prime}(\cdot) W_C * o_{t-1} \tanh ^{\prime}\left(C_{t-1}\right) \\ &+f_t \end{aligned} ∂Ct−1∂Ct=Ct−1σ′(⋅)Wf∗ot−1tanh′(Ct−1)+C~tσ′(⋅)Wi∗ot−1tanh′(Ct−1)+ittanh′(⋅)WC∗ot−1tanh′(Ct−1)+ft 从求导的结果中可以看到一个结果,那就是 ∂ C t ∂ C t − 1 \frac{\partial C_t}{\partial C_{t-1}} ∂Ct−1∂Ct的导数结果是一个求和状态,同时其中还有一项是f_t,这一项是遗忘门的输出值,当遗忘门完全保留的时候值为1,当完全舍弃的时候值为0。很显然当 f t f_t ft接近1的时候 ∂ C t ∂ C t − 1 \frac{\partial C_t}{\partial C_{t-1}} ∂Ct−1∂Ct等4项求和, ∏ k = i k = t − 1 ∂ h k + 1 ∂ h k \prod_{k=i}^{k=t-1} \frac{\partial h_{k+1}}{\partial h_k} ∏k=ik=t−1∂hk∂hk+1就不会接近于0,因此缓解了梯度消失的问题,其实LSTM神经网络没有根本上解决梯度消失的问题,只是在 C t C_t Ct到 C ( t − 1 ) C_(t-1) C(t−1)这条路上解决梯度消失的问题,其他的路依然存在梯度消失的问题。
这里需要强调的是:LSTM不是让所有远距离的梯度值都不会消散,而是只让具有时序关键信息位置的梯度可以一直传递。如果我们的任务比较依赖于历史信息,那么就会接近于1,这时候历史的梯度信息也正好不容易消失;如果很接近于0,那么就说明我们的任务不依赖于历史信息,这时候就算梯度消失也无妨了。
本文从RNN神经网络的基本单元讲解到LSTM神经网络的讲解,从模型的角度来说,RNN神经网络和LSTM神经网络的最大区别就是LSTM神经网络多了几个门单元和一个传送带来控制学习到的时间序列信息的重要性,从而对有用信息进行保留,无用的信息进行去除的目的,同时LSTM神经网络也从一定程度上缓解了RNN神经网络的梯度消失问题。但是RNN神经网络还是基础,从模型的运行角度来说,RNN神经网络和LSTM神经网络的运行是几乎一模一样的,因此必要在学习搞懂RNN神经网络的基础上去学习LSTM神经网络。