深度学习【5】循环神经网络(RNN)反向传播算法(BPTT)理解

本文介绍了循环神经网络RNN的前向传播算法及其与传统BP网络的区别,并详细解析了BPTT(Backpropagation Through Time)反向传播算法的实现过程,强调了在时间序列上的误差如何通过链式法则传播,以及参数更新的特点。

http://blog.youkuaiyun.com/linmingan/article/details/50958304

循环神经网络的反向传播算法其实只是BP算法的一个简单变体而已。

首先我们先看看循环神经网络的前向传播算法:

需要注意的是,该RNN中前一时刻到当前时刻只有一个权重矩阵,该权重矩阵与时间并没有什么关系。整个前向传播算法与BP网络的前向传播算法的差别是多了一个前一时刻隐藏层的信息而已。在我们这边的前向传播算法可能与大家平时看到的会有点出入,因为这个前向传播算法将传播过程中的各个阶段都拆分开来表示。在进入激活函数前先用额外的两个变量表示,分别是进入隐藏层激活函数e前的和进入输出层激活函数g前的。进行这样的拆分是为了更好的实现反向传播算法,即链式求导法则。

RNN的前向传播算法是以时间的推移进行的,其实就是一些时序上的数据,比如音频中分帧后的得到的一系列的音频信号帧。RNN的输入数据与其他神经网络如DNN的输入数据不同的是,RNN的输入数据样本不能打

<think>我们正在讨论循环神经网络RNN)中的反向传播算法,特别是BPTT(Backpropagation Through Time)。用户要求了解反向传播算法RNN中如何实现参数更新。根据引用内容,BPTTRNN中用于训练的特殊反向传播技术,它通过间展开RNN,将其视为一个深度前馈网络,然后应用反向传播计算梯度并更新参数。 核心要点: 1. RNN的参数在间步之间共享(即U, W, V, b, c等参数在所有间步都是相同的)。 2. BPTT的基本思想是将RNN间序列展开,形成一个虚拟的深层网络,每一层对应一个间步。 3. 在展开的网络中,使用标准的反向传播算法计算梯度,但由于参数共享,我们需要将所有间步上相同参数的梯度累加起来,然后进行更新。 步骤: 1. 前向传播:从间t=1到t=T(序列长度)进行前向计算,保存每个间步的隐藏状态和输出。 2. 计算损失:通常是对每个间步的损失求和(如交叉熵损失)。 3. 反向传播:从t=T开始,反向计算每个间步的梯度,一直回溯到t=1。 4. 梯度累加:由于参数共享,每个间步的梯度都会贡献给相同的参数,因此需要将每个间步的梯度累加。 5. 参数更新:使用累加的梯度(通常也会除以序列长度或批次大小进行归一化)来更新参数。 数学推导: 考虑一个简单的RNN模型,其结构如下: - 输入:$x_t$(间步t的输入向量) - 隐藏状态:$h_t = \tanh(W_{xh} x_t + W_{hh} h_{t-1} + b_h)$ - 输出:$y_t = \text{softmax}(W_{hy} h_t + b_y)$ 损失函数:假设我们使用交叉熵损失,对于每个间步的损失为$L_t = - \sum y_{t, \text{true}} \log y_{t, \text{pred}}$,总损失$L = \sum_{t=1}^T L_t$。 在反向传播中,我们需要计算损失函数对参数$W_{xh}, W_{hh}, W_{hy}, b_h, b_y$的梯度。 以$W_{hh}$为例,由于它在每个间步都被使用,因此梯度计算为: $$\frac{\partial L}{\partial W_{hh}} = \sum_{t=1}^T \frac{\partial L}{\partial h_t} \frac{\partial h_t}{\partial W_{hh}}$$ 但是注意,$h_t$不仅依赖于当前间步的$W_{hh}$,还通过$h_{t-1}$依赖于之前所有间步的$W_{hh}$(因为$h_{t-1}$也是$W_{hh}$的函数)。因此,我们需要使用链式法则沿着间展开。 具体地,对于$h_t$,它的梯度来自两部分: - 直接来自当前间步的输出层(通过$y_t$) - 来自下一个间步的隐藏状态(因为$h_t$会影响到$h_{t+1}$) 因此,在反向传播,从最后一个间步T开始,我们计算损失对$h_T$的梯度,然后传递到$h_{T-1}$,依此类推直到$h_1$。 定义: - $\delta_t = \frac{\partial L}{\partial h_t}$ 表示损失对t刻隐藏状态的梯度。 那么,$\delta_t$的计算包括: - 来自当前间步的输出层:$\frac{\partial L_t}{\partial h_t} = (y_t - \text{true\_label}_t) \cdot W_{hy}^\top$(这里假设是分类任务,softmax输出) - 来自下一个间步的隐藏状态:$\frac{\partial L}{\partial h_{t+1}} \cdot \frac{\partial h_{t+1}}{\partial h_t} = \delta_{t+1} \cdot W_{hh}^\top \cdot (1 - h_t^2)$ (因为$h_{t+1}$中使用了$h_t$,且激活函数是tanh,其导数为$1-\tanh^2$) 所以: $$\delta_t = W_{hy}^\top (y_t - \text{true\_label}_t) + W_{hh}^\top \delta_{t+1} \cdot (1 - h_t^2)$$ 注意:对于最后一个间步T,没有来自未来的梯度,所以$\delta_T = W_{hy}^\top (y_T - \text{true\_label}_T)$。 然后,对于参数$W_{hh}$,梯度为: $$\frac{\partial L}{\partial W_{hh}} = \sum_{t=1}^T \delta_t \cdot (1 - h_t^2) \cdot h_{t-1}^\top$$ 其中,$h_{t-1}$是上一刻的隐藏状态(注意:在t=1,$h_0$通常是初始化为0向量)。 同样,对于其他参数,如$W_{xh}$: $$\frac{\partial L}{\partial W_{xh}} = \sum_{t=1}^T \delta_t \cdot (1 - h_t^2) \cdot x_t^\top$$ 对于偏置$b_h$: $$\frac{\partial L}{\partial b_h} = \sum_{t=1}^T \delta_t \cdot (1 - h_t^2)$$ 对于输出层的参数$W_{hy}$和$b_y$,由于它们不依赖于间(每个间步独立),所以梯度计算更直接: $$\frac{\partial L}{\partial W_{hy}} = \sum_{t=1}^T (y_t - \text{true\_label}_t) \cdot h_t^\top$$ $$\frac{\partial L}{\partial b_y} = \sum_{t=1}^T (y_t - \text{true\_label}_t)$$ 最后,使用梯度下降更新参数: $$W = W - \eta \frac{\partial L}{\partial W}$$ 其中$\eta$是学习率。 注意:在实际实现中,由于序列可能很长,BPTT可能会遇到梯度消失或爆炸问题。因此,通常使用截断的BPTT(Truncated BPTT),即只反向传播固定步数(例如,只回传k步)。 总结步骤: 1. 前向传播:计算并保存每个间步的输入、隐藏状态、输出。 2. 计算每个间步的损失,并累加得到总损失。 3. 反向传播:从最后一个间步开始,计算损失对隐藏状态的梯度($\delta_t$),并反向传递,同累加各个参数的梯度。 4. 使用累加的梯度更新参数。 注意:参数共享意味着在更新,所有间步的相同参数使用同一个梯度(即累加后的梯度)进行更新。 引用说明: 根据引用[1][2][3],BPTTRNN训练中使用的特殊反向传播算法,通过间展开,将RNN视为深度网络,然后进行反向传播,同参数共享是RNN的特点,因此梯度更新需要对所有间步的梯度进行累加[^1][^2][^3]。 下面,我们将按照上述思路组织回答。</think>### 循环神经网络RNN)中的反向传播算法BPTT)实现详解 循环神经网络通过**间展开结构**和**参数共享机制**实现序列建模,其反向传播采用**通过反向传播BPTT算法**,核心是通过间维度展开网络结构并计算梯度。以下是具体实现步骤: --- #### 一、BPTT 算法核心原理 1. **间展开结构** 将长度为 $T$ 的序列展开为 $T$ 层前馈网络,每层对应一个间步: $$ \text{RNN} \rightarrow \underbrace{\text{NN}_1 \rightarrow \text{NN}_2 \rightarrow \cdots \rightarrow \text{NN}_T}_{\text{间展开}} $$ 其中每层共享相同参数 $(W_{xh}, W_{hh}, W_{hy}, b_h, b_y)$[^1][^2]。 2. **梯度传播路径** 损失函数 $L$ 的梯度需沿反向传播: $$ \frac{\partial L}{\partial \theta} = \sum_{t=1}^T \frac{\partial L_t}{\partial \theta} \quad (\theta \in \{W_{xh}, W_{hh}, \cdots\}) $$ 梯度需从 $t=T$ 逐层传递至 $t=1$[^2][^3]。 --- #### 二、BPTT 具体实现步骤 ##### 1. 前向传播(保存中间状态) ```python # 初始化隐藏状态 h_0 = np.zeros(hidden_size) for t in range(1, T+1): # 计算当前隐藏状态 h_t = np.tanh(W_{xh} @ x_t + W_{hh} @ h_{t-1} + b_h) # 计算输出 y_t = softmax(W_{hy} @ h_t + b_y) # 保存 h_t 用于反向传播 cache[t] = (x_t, h_t, h_{t-1}) ``` ##### 2. 反向传播(梯度计算) 以参数 $W_{hh}$ 为例,梯度计算需考虑间依赖: $$ \frac{\partial L}{\partial W_{hh}} = \sum_{t=1}^T \frac{\partial L}{\partial h_t} \frac{\partial h_t}{\partial W_{hh}} + \frac{\partial L}{\partial h_t} \frac{\partial h_t}{\partial h_{t-1}} \frac{\partial h_{t-1}}{\partial W_{hh}} + \cdots $$ 具体实现: ```python # 初始化梯度 dW_{hh} = np.zeros_like(W_{hh}) dh_next = np.zeros_like(h_T) # 从最后间步开始 for t in reversed(range(1, T+1)): x_t, h_t, h_{t-1} = cache[t] # 计算当前 h_t 的梯度 (来自输出层 + 下一间步) dh_t = dL/dy_t @ W_{hy} * (1 - h_t**2) + dh_next # 累积 W_{hh} 梯度 dW_{hh} += dh_t @ h_{t-1}.T # 计算传递到前一间步的梯度 dh_next = dh_t @ W_{hh} * (1 - h_{t-1}**2) ``` ##### 3. 参数更新(梯度累加) 所有间步共享参数,梯度需累加后统一更新: $$ W_{hh} \leftarrow W_{hh} - \eta \cdot \frac{1}{T} \sum_{t=1}^T \frac{\partial L_t}{\partial W_{hh}} $$ 其中 $\eta$ 为学习率[^2][^3]。 --- #### 三、BPTT 的数学推导(以 $W_{hh}$ 为例) 1. **隐藏状态梯度**: $$ \delta_t = \frac{\partial L}{\partial h_t} = \underbrace{\frac{\partial L}{\partial y_t} \frac{\partial y_t}{\partial h_t}}_{\text{输出层梯度}} + \underbrace{\frac{\partial L}{\partial h_{t+1}} \frac{\partial h_{t+1}}{\partial h_t}}_{\text{间传播}} $$ 2. **参数梯度分解**: $$ \frac{\partial L}{\partial W_{hh}} = \sum_{k=1}^T \sum_{t=k}^T \frac{\partial L}{\partial h_t} \frac{\partial h_t}{\partial h_k} \frac{\partial h_k}{\partial W_{hh}} $$ 3. **梯度计算通式**: $$ \frac{\partial h_t}{\partial h_k} = \prod_{j=k}^{t-1} \left( \frac{\partial h_{j+1}}{\partial h_j} \right) = \prod_{j=k}^{t-1} W_{hh}^\top \cdot \text{diag}(1 - h_j^2) $$ 其中 $\text{diag}(1 - h_j^2)$ 是 $\tanh$ 激活函数的 Jacobian 矩阵[^1][^3]。 --- #### 四、BPTT 的优化挑战 | 问题 | 原因 | 解决方案 | |---------------------|----------------------------------------------------------------------|------------------------------| | **梯度消失** | 长期依赖中 $\prod_{j=k}^{t-1} \| \frac{\partial h_{j+1}}{\partial h_j} \| \ll 1$ | LSTM/GRU 门控机制[^3] | | **梯度爆炸** | $\prod \| \cdot \| \gg 1$ | 梯度裁剪(Gradient Clipping)| | **计算复杂度高** | $O(T^2)$ 间复杂度和 $O(T)$ 空间复杂度 | 截断 BPTT(Truncated BPTT) | > **截断 BPTT 示例**:仅回传 $k$ 步梯度 > $$ \frac{\partial \tilde{L}}{\partial W_{hh}} = \sum_{t=\max(1,T-k)}^T \frac{\partial L_t}{\partial W_{hh}} $$ --- ### 总结 BPTT 算法通过间展开将 RNN 转化为虚拟深度网络,在反向传播: 1. **按间逆序计算梯度**:从 $t=T$ 到 $t=1$ 逐层回溯 2. **梯度累加更新**:共享参数需聚合所有间步梯度 3. **处理序依赖**:通过 $\frac{\partial h_t}{\partial h_{t-1}}$ 传递历史信息[^1][^2][^3] 该算法虽存在梯度问题,但为 RNN 训练提供了理论基础,后续改进模型(如 LSTM)通过门控机制优化了梯度流动。 ---
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值