RNN、LSTM反向传播推导详解

在做吴恩达深度学习课程相关作业时,顺便进行了RNN和LSTM的反向传播推导。顺便记录如下,希望能对你有所帮助~

RNN前向与反向:

模型的整体结构如下图所示,输入的是序列x、输出y,长度为Tx。

 

BasicRNN 前向传播

 现在我们单独对每个cell进行公式推导,最终整个模型的公式其实就是单个cell的循环调用。

 下图是单个cell的具体结构图,以及前向传播的公式,非常的简洁明了 


Basic RNN的反向传播很简单,直接上图:

LSTM前向传播

 

LSTM单个cell的反向传播比Basic RNN看起来要复杂很多,主要变化就是添加了三个门:遗忘门、更新门和输出门。但是我们理清楚单个cell接收到的所有梯度,就很容易理解了。 
(1)当前cell中a< t >通过反向传播得到的梯度同样有两个部分 
    - 当前输出y^< t >代入损失函数,对a< t >求导得到的da< t >1 
    - 输入到下一个cell的a< t >传回的梯度da< t >2 
(2)当前cell还要接受输入到下一个cell的c< t >传回的梯度dc< t >1

 

为了便于理解,现在图上标记一些符号:

注意其中的 da<t> 是同 da<t>1与da<t>2 反向来的,而 dc<t> 是由 dc<t>1与da<t> 反向来的

LSTM反向传播详细推导(第一种推导法)

PS:这里是本人根据吴恩达作业结合自己理解推导出来的。之所以自己宁愿手撕推导一遍,再(二)中讲明原因。

第一步计算反向传播:

第二步计算反向传播:

第三步计算反向传播:

第四步计算反向传播:

 

第五步计算反向传播:

注意第五步这里的 W前 W 后要分别对应 a 和 x 的维数。

至此,整个LSTM反向传播完成~~~

LSTM反向传播详细推导(第二种推导法)

PS: 之所以手推了上面的(一)。是因为一开始看吴恩达作业列的式子时,有些不明白式子怎么会这样,例如dot = da_next * np.tanh(c_next) * ot * (1 - ot),但是后来结合了代码去看,发现ot * (1 - ot)是把应该在后面才求导的式子中部分计算直接在前面给计算了,例如dwf的求导,这里就不用ot * (1 - ot)。。。当然这样结果是对的,只是一开始真把我搞糊涂了。(这里的dc并没有像上面那样直接单独算出整体结果

注意:  图中标记的1部分是计算dWo时才用到的,但是下面的dWo没列出,这里提前列出来了。。。(8)(9)(10)后面的下划线部分都是一样提前计算了,真正的反向求导只是中扩号里面的。一开始觉得很奇怪,后来结合整体结合才看明白了。至于为什么提前计算,就是为了后面的公式写起来简单吧~

          (8)式中画叉的应该是图错了,把那个去掉。

实现代码如下:

def lstm_cell_backward(da_next, dc_next, cache):
    """
    Implement the backward pass for the LSTM-cell (single time-step).

    Arguments:
    da_next -- Gradients of next hidden state, of shape (n_a, m)
    dc_next -- Gradients of next cell state, of shape (n_a, m)
    cache -- cache storing information from the forward pass

    Returns:
    gradients -- python dictionary containing:
                        dxt -- Gradient of input data at time-step t, of shape (n_x, m)
                        da_prev -- Gradient w.r.t. the previous hidden state, numpy array of shape (n_a, m)
                        dc_prev -- Gradient w.r.t. the previous memory state, of shape (n_a, m, T_x)
                        dWf -- Gradient w.r.t. the weight matrix of the forget gate, numpy array of shape (n_a, n_a + n_x)
                        dWi -- Gradient w.r.t. the weight matrix of the input gate, numpy array of shape (n_a, n_a + n_x)
                        dWc -- Gradient w.r.t. the weight matrix of the memory gate, numpy array of shape (n_a, n_a + n_x)
                        dWo -- Gradient w.r.t. the weight matrix of the save gate, numpy array of shape (n_a, n_a + n_x)
                        dbf -- Gradient w.r.t. biases of the forget gate, of shape (n_a, 1)
                        dbi -- Gradient w.r.t. biases of the update gate, of shape (n_a, 1)
                        dbc -- Gradient w.r.t. biases of the memory gate, of shape (n_a, 1)
                        dbo -- Gradient w.r.t. biases of the save gate, of shape (n_a, 1)
    """

    # Retrieve information from "cache"
    (a_next, c_next, a_prev, c_prev, ft, it, cct, ot, xt, parameters) = cache
    
    ### START CODE HERE ###
    # Retrieve dimensions from xt's and a_next's shape (≈2 lines)
    n_x, m = xt.shape
    n_a, m = a_next.shape
    
    # Compute gates related derivatives, you can find their values can be found by looking carefully at equations (7) to (10) (≈4 lines)
    dot = da_next * np.tanh(c_next) * ot * (1 - ot)
    dcct = (dc_next * it + ot * (1 - np.square(np.tanh(c_next))) * it * da_next) * (1 - np.square(cct))
    dit = (dc_next * cct + ot * (1 - np.square(np.tanh(c_next))) * cct * da_next) * it * (1 - it)
    dft = (dc_next * c_prev + ot *(1 - np.square(np.tanh(c_next))) * c_prev * da_next) * ft * (1 - ft)
 

    # Compute parameters related derivatives. Use equations (11)-(14) (≈8 lines)
    dWf = np.dot(dft,np.concatenate((a_prev, xt), axis=0).T)
    dWi = np.dot(dit,np.concatenate((a_prev, xt), axis=0).T)
    dWc = np.dot(dcct,np.concatenate((a_prev, xt), axis=0).T)
    dWo = np.dot(dot,np.concatenate((a_prev, xt), axis=0).T)
    dbf = np.sum(dft, axis=1 ,keepdims = True)
    dbi = np.sum(dit, axis=1, keepdims = True)
    dbc = np.sum(dcct, axis=1,  keepdims = True)
    dbo = np.sum(dot, axis=1, keepdims = True)

    # Compute derivatives w.r.t previous hidden state, previous memory state and input. Use equations (15)-(17). (≈3 lines)
    da_prev = np.dot(parameters['Wf'][:,:n_a].T,dft)+np.dot(parameters['Wi'][:,:n_a].T,dit)+np.dot(parameters['Wc'][:,:n_a].T,dcct)+np.dot(parameters['Wo'][:,:n_a].T,dot)
    dc_prev = dc_next*ft+ot*(1-np.square(np.tanh(c_next)))*ft*da_next
    dxt = np.dot(parameters['Wf'][:,n_a:].T,dft)+np.dot(parameters['Wi'][:,n_a:].T,dit)+np.dot(parameters['Wc'][:,n_a:].T,dcct)+np.dot(parameters['Wo'][:,n_a:].T,dot)
    # parameters['Wf'][:, :n_a].T 每一行的 第 0 到 n_a-1 列的数据取出来
    # parameters['Wf'][:, n_a:].T 每一行的 第 n_a 到最后列的数据取出来
    ### END CODE HERE ###
    
    # Save gradients in dictionary
    gradients = {"dxt": dxt, "da_prev": da_prev, "dc_prev": dc_prev, "dWf": dWf,"dbf": dbf, "dWi": dWi,"dbi": dbi,
                "dWc": dWc,"dbc": dbc, "dWo": dWo,"dbo": dbo}

    return gradients

 

  • 16
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
RNN(循环神经网络)是一种能够处理序列数据的神经网络模型。它通过自循环的方式将前一时刻的隐藏状态信息传递给下一时刻,以此来建模序列中的时序关系。 RNN反向传播算法主要包含以下几个步骤: 1. 初始化:首先,我们需要初始化模型的参数,包括权重和偏置。这些参数会在反向传播过程中根据损失函数来进行调整。 2. 前向传播:在反向传播之前,我们需要先进行一次前向传播。假设我们有一个包含T个时刻的序列数据,每个时刻的输入是一个D维的向量,隐藏状态的维度为H。对于每个时刻t,我们先计算当前时刻的隐藏状态,根据当前时刻的输入数据和前一时刻的隐藏状态: ht = activation(Wx * Xt + Wh * ht-1 + b) 其中,Wx和Wh分别是输入与隐藏状态之间的权重矩阵,b是偏置项,activation是激活函数。 3. 计算损失函数:根据预测结果和真实结果计算损失函数,常见的损失函数包括均方差误差和交叉熵等。损失函数衡量了模型的预测与真实结果之间的差距。 4. 反向传播:在RNN中,由于隐藏状态之间存在时序关系,我们需要考虑到每个时刻的梯度对前一时刻的梯度的影响。首先,我们计算当前时刻的梯度: dht = dout + dht+1 其中,dout是损失函数对当前时刻的输出的导数。然后,我们利用当前时刻的梯度来计算当前时刻的权重矩阵和偏置项的梯度: dWx += Xt * dht dWh += ht-1 * dht db += dht 接下来,我们计算对前一时刻隐藏状态的梯度: dht-1 = Wh * dht 最后,我们利用当前时刻的梯度和前一时刻的梯度来计算损失函数对输入的导数: dXt = Wx * dht + dXt+1 这样就完成了一个时刻的反向传播。重复以上步骤,可以依次计算每个时刻的梯度,从而完成整个反向传播的过程。 5. 更新参数:最后,利用计算得到的梯度信息更新模型的参数。采用梯度下降法,通过调整参数,使得损失函数尽可能地减小。 总结起来,RNN反向传播算法通过自循环的方式将梯度从当前时刻传递到前一时刻,并利用当前时刻和前一时刻的梯度来计算参数的梯度,然后通过梯度下降法来更新参数,从而优化模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值