NNDL 作业10:第六章课后题(LSTM | GRU)

目录

习题6-3  当使用公式(6.50)作为循环神经网络得状态更新公式时,分析其可能存在梯度爆炸的原因并给出解决办法.

习题6-4 推导LSTM网络中参数的梯度,并分析其避免梯度消失的效果​编辑 

习题6-5 推导GRU网络中参数的梯度,并分析其避免梯度消失的效果 

附加题 6-1P 什么时候应该用GRU? 什么时候用LSTM? 

附加题 6-2P LSTM BP推导,并用Numpy实现 

总结 


习题6-3  当使用公式(6.50)作为循环神经网络得状态更新公式时,分析其可能存在梯度爆炸的原因并给出解决办法.


 公式6.50:h_{t}=h_{t-1}+g(x_{t},h_{t-1};\Theta )
令 z_{k}=Uh_{k-1}+Wx_{k}+b为在第k时刻函数g(·)的输入,在计算公式6.34中的误差项  \delta _{t,k}=\frac{\partial L_{t}}{\partial z_{k}}时,梯度可能过大,从而导致梯度过大问题。
解决方法:使用长短期记忆神经网络。 

习题6-4 推导LSTM网络中参数的梯度,并分析其避免梯度消失的效果​编辑 

LSTM结构图: 

 

  • 遗忘门f_{t}:控制上一个隐藏状态要遗忘多少信息
  • 输入门i_{t}:当前状态的候选状态有多少信息需要保存
  • 输出门o_{t}:当前隐藏状态有多少需要输出给外部状态

 

 

LSTM 中梯度的传播有很多条路径,但C_{t}=C_{t-1}f_{t}+a_{t}i_{t}这条路径上只有逐元素相乘和相加的操作,梯度流最稳定;但是其他路径上梯度流与普通 RNN 类似,照样会发生相同的权重矩阵反复连乘。

由于总的远距离梯度 = 各条路径的远距离梯度之和,即便其他远距离路径梯度消失了,只要保证有一条远距离路径(就是上面说的那条高速公路)梯度不消失,总的远距离梯度就不会消失(正常梯度 + 消失梯度 = 正常梯度)。因此 LSTM 通过改善一条路径上的梯度问题拯救了总体的远距离梯度。

习题6-5 推导GRU网络中参数的梯度,并分析其避免梯度消失的效果 

 GRU结构图:

 

 

 

它只有两个门,对应输出更新门(update gate)向量:和重置门(reset gate)向量:,更新门负责控制上一时刻状态信息对当前时刻状态的影响,更新门的值越大说明上一时刻的状态信息带入越多。而重置门负责控制忽略前一时刻的状态信息的程度,重置门的值越小说明忽略的越多。注意前两个,更新门和重置门的表达式,表示两个向量连接,表示矩阵相乘,表示sigmoid函数。

接下来,“重置”之后的重置门向量 与前一时刻状态卷积 ,再将与输入进行拼接,再通过激活函数tanh来将数据放缩到-1~1的范围内。这里包含了输入数据,并且将上一时刻状态的卷积结果添加到当前的隐藏状态,通过此方法来记忆当前时刻的状态。

最后一个步骤是更新记忆阶段,此阶段同时进遗忘和记忆两个步骤,使用同一个门控同时进行遗忘和选择记忆(LSTM是多个门控制)

附加题 6-1P 什么时候应该用GRU? 什么时候用LSTM? 

  • 一般来说两者效果差不多,性能在很多任务上也不分伯仲。GRU参数更少,收敛更快;数据量很大时,LSTM效果会更好一些,因为LSTM参数也比GRU参数多一些。
  • 但是GRU通过更新门来控制上一时刻的信息传递和当前时刻计算的隐层信息传递。GRU中由于是一个参数进行控制,因而可以选择完全记住上一时刻而不需要当前计算的隐层值,或者完全选择当前计算的隐层值而忽略上一时刻的所有信息,最后一种情况就是无论是上一时刻的信息还是当前计算的隐层值都选择传递到当前时刻隐层值,只是选择的比重不同。而LSTM是由两个参数(遗忘门和输入门)来控制更新的,他们之间并不想GRU中一样只是由一个参数控制,因而在比重选择方面跟GRU有着很大的区别,例如它可以既不选择上一时刻的信息,也不选择当前计算的隐层值信息(输入门拒绝输入,遗忘门选择遗忘)。
  • GRU要在上一时刻的隐层信息的基础上乘上一个重置门,而LSTM无需门来对其控制,LSTM必须考虑上一时刻的隐层信息对当前隐层的影响,而GRU则可选择是否考虑上一时刻的隐层信息对当前时刻的影响。

附加题 6-2P LSTM BP推导,并用Numpy实现 

 

import numpy as np
def sigmoid(x):
    return 1/(1+np.exp(-x))
 
def softmax(x):
    e_x = np.exp(x-np.max(x))# 防溢出
    return e_x/e_x.sum(axis=0)
 
 
def LSTM_CELL_Forward(xt, h_prev, C_prev, parameters):
    """
    Arguments:
    xt:时间步“t”处输入的数据 shape(n_x,m)
    h_prev:时间步“t-1”的隐藏状态 shape(n_h,m)
    C_prev:时间步“t-1”的memory状态 shape(n_h,m)
    parameters
        Wf 遗忘门的权重矩阵 shape(n_h,n_h+n_x)
        bf 遗忘门的偏置 shape(n_h,1)
        Wi 输入门的权重矩阵 shape(n_h,n_h+n_x)
        bi 输入门的偏置 shape(n_h,1)
        Wc 第一个“tanh”的权重矩阵 shape(n_h,n_h+n_x)
        bc 第一个“tanh”的偏差 shape(n_h,1)
        Wo 输出门的权重矩阵 shape(n_h,n_h+n_x)
        bo 输出门的偏置 shape(n_h,1)
        Wy 将隐藏状态与输出关联的权重矩阵 shape(n_y,n_h)
        by 隐藏状态与输出相关的偏置 shape(n_y,1)
    Returns:
    h_next -- 下一个隐藏状态 shape(n_h,m)
    c_next -- 下一个memory状态 shape(n_h,m)
    yt_pred -- 时间步长“t”的预测 shape(n_y,m)
    """
    # 获取参数字典中各个参数
    Wf = parameters["Wf"]
    bf = parameters["bf"]
    Wi = parameters["Wi"]
    bi = parameters["bi"]
    Wc = parameters["Wc"]
    bc = parameters["bc"]
    Wo = parameters["Wo"]
    bo = parameters["bo"]
    Wy = parameters["Wy"]
    by = parameters["by"]
 
    # 获取 xt 和 Wy 的维度参数
    n_x, m = xt.shape
    n_y, n_h = Wy.shape
 
    # 拼接 h_prev 和 xt
    concat = np.zeros((n_x + n_h, m))
    concat[: n_h, :] = h_prev
    concat[n_h:, :] = xt
 
    # 计算遗忘门、输入门、记忆细胞候选值、下一时间步的记忆细胞、输出门和下一时间步的隐状态值
    ft = sigmoid(np.dot(Wf, concat) + bf)
    it = sigmoid(np.dot(Wi, concat) + bi)
    cct = np.tanh(np.dot(Wc, concat) + bc)
    c_next = ft * C_prev + it * cct
    ot = sigmoid(np.dot(Wo, concat) + bo)
    h_next = ot * np.tanh(c_next)
 
    # LSTM单元的计算预测
    yt_pred = softmax(np.dot(Wy, h_next) + by)
 
    return h_next, c_next, yt_pred
np.random.seed(1)
xt = np.random.randn(3,10)
h_prev = np.random.randn(5,10)
c_prev = np.random.randn(5,10)
Wf = np.random.randn(5, 5+3)
bf = np.random.randn(5,1)
Wi = np.random.randn(5, 5+3)
bi = np.random.randn(5,1)
Wo = np.random.randn(5, 5+3)
bo = np.random.randn(5,1)
Wc = np.random.randn(5, 5+3)
bc = np.random.randn(5,1)
Wy = np.random.randn(2,5)
by = np.random.randn(2,1)
 
parameters = {"Wf": Wf, "Wi": Wi, "Wo": Wo, "Wc": Wc, "Wy": Wy, "bf": bf, "bi": bi, "bo": bo, "bc": bc, "by": by}
 
h_next, c_next, yt = LSTM_CELL_Forward(xt, h_prev, c_prev, parameters)
print("a_next[4] = ", h_next[4])
print("a_next.shape = ", c_next.shape)
print("c_next[2] = ", c_next[2])
print("c_next.shape = ", c_next.shape)
print("yt[1] =", yt[1])
print("yt.shape = ", yt.shape)
 
h_numpy :
 [[ 0.055856  0.234159  0.138457]
 [ 0.094461  0.245843  0.224411]
 [ 0.020396  0.086745  0.082545]
 [-0.003794  0.040677  0.063094]]
h_torch :
 [[ 0.055856  0.234159  0.138457]
 [ 0.094461  0.245843  0.224411]
 [ 0.020396  0.086745  0.082545]
 [-0.003794  0.040677  0.063094]]
---------------------------------
c_numpy :
 [[ 0.092093  0.384992  0.213364]
 [ 0.151362  0.424671  0.318313]
 [ 0.033245  0.141979  0.120822]
 [-0.0061    0.062946  0.094999]]
c_torch :
 [[ 0.092093  0.384992  0.213364]
 [ 0.151362  0.424671  0.318313]
 [ 0.033245  0.141979  0.120822]
 [-0.0061    0.062946  0.094999]]
---------------------------------
dx_numpy :
 [[-0.144016  0.029775]
 [-0.229789  0.140921]
 [-0.246041 -0.009354]
 [-0.088844  0.036652]]
dx_torch :
 [[-0.144016  0.029775]
 [-0.229789  0.140921]
 [-0.246041 -0.009354]
 [-0.088844  0.036652]]
---------------------------------
w_ih_grad_numpy :
 [[-0.056788 -0.036448]
 [ 0.018742  0.014428]
 [ 0.007827  0.024828]
 [ 0.07856   0.05437 ]
 [ 0.061267  0.045952]
 [ 0.083886  0.0655  ]
 [ 0.229755  0.156008]
 [ 0.345218  0.251984]
 [ 0.430385  0.376664]
 [ 0.014239  0.011767]
 [ 0.054866  0.044531]
 [ 0.04654   0.048565]]
w_ih_grad_torch :
 [[-0.056788 -0.036448]
 [ 0.018742  0.014428]
 [ 0.007827  0.024828]
 [ 0.07856   0.05437 ]
 [ 0.061267  0.045952]
 [ 0.083886  0.0655  ]
 [ 0.229755  0.156008]
 [ 0.345218  0.251984]
 [ 0.430385  0.376664]
 [ 0.014239  0.011767]
 [ 0.054866  0.044531]
 [ 0.04654   0.048565]]
---------------------------------
w_hh_grad_numpy :
 [[-0.037698 -0.048568 -0.021069]
 [ 0.016749  0.016277  0.007556]
 [ 0.035743  0.02156   0.000111]
 [ 0.060824  0.069505  0.029101]
 [ 0.060402  0.051634  0.025643]
 [ 0.068116  0.06966   0.035544]
 [ 0.168965  0.217076  0.075904]
 [ 0.248277  0.290927  0.138279]
 [ 0.384974  0.401949  0.167006]
 [ 0.015448  0.0139    0.005158]
 [ 0.057147  0.048975  0.022261]
 [ 0.057297  0.048308  0.017745]]
w_hh_grad_torch :
 [[-0.037698 -0.048568 -0.021069]
 [ 0.016749  0.016277  0.007556]
 [ 0.035743  0.02156   0.000111]
 [ 0.060824  0.069505  0.029101]
 [ 0.060402  0.051634  0.025643]
 [ 0.068116  0.06966   0.035544]
 [ 0.168965  0.217076  0.075904]
 [ 0.248277  0.290927  0.138279]
 [ 0.384974  0.401949  0.167006]
 [ 0.015448  0.0139    0.005158]
 [ 0.057147  0.048975  0.022261]
 [ 0.057297  0.048308  0.017745]]
---------------------------------
b_ih_grad_numpy :
 [-0.084682  0.032588  0.046412  0.126449  0.111421  0.139337  0.361956
  0.539519  0.761838  0.027649  0.103695  0.099405]
b_ih_grad_torch :
 [-0.084682  0.032588  0.046412  0.126449  0.111421  0.139337  0.361956
  0.539519  0.761838  0.027649  0.103695  0.099405]
---------------------------------
b_hh_grad_numpy :
 [-0.084682  0.032588  0.046412  0.126449  0.111421  0.139337  0.361956
  0.539519  0.761838  0.027649  0.103695  0.099405]
b_hh_grad_torch :
 [-0.084682  0.032588  0.046412  0.126449  0.111421  0.139337  0.361956
  0.539519  0.761838  0.027649  0.103695  0.099405]

Process finished with exit code 0 

总结 

本次作业通过推导对 LSTM 、GRU有了进一步的了解,GRU参数更少,收敛更快;数据量很大时,LSTM效果会更好一些,因为LSTM参数也比GRU参数多一些。

 

 

 

 


版权声明:本文为m0_63172447原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。