循环神经网络 RNN
RNN 用共享循环单元和隐藏状态把历史信息传向后续时间步。
学习目标
- 标量 RNN 记住上一步
- RNN 的参数量会随序列长度增加吗?
- 说明:有限隐藏状态不是完整历史副本,普通 RNN 的长依赖与状态重置都需要特别检查。
为什么需要这个概念
循环神经网络在时间上重复使用同一组参数,通过隐藏状态连接相邻时刻。普通 RNN 常以线性组合加 tanh 更新状态。
学习前需要掌握
背景与问题
序列的当前含义往往依赖先前内容。RNN 将过去压缩进隐藏状态,再与当前输入共同更新,使同一单元可以逐步处理不同长度的数据。
概念定义
循环神经网络在时间上重复使用同一组参数,通过隐藏状态连接相邻时刻。普通 RNN 常以线性组合加 tanh 更新状态。
直观理解
边读句子边维护一份会更新的笔记;每个新词既参考自身,也参考之前的笔记。
核心原理
状态更新可写为 h_t=tanh(W_x x_t+W_h h_prev+b)。输入、循环权重在所有时间步共享,所以增加序列长度增加计算步数,不自动增加模型参数。初始状态通常为零,也可由任务另行指定。
训练按时间展开后执行 BPTT,梯度沿循环链相乘。反复乘较小或较大局部导数会造成梯度消失或爆炸,这也是长依赖较难学习的原因;裁剪只能限制爆炸,不能直接恢复已经消失的信息。
PyTorch batch_first 只控制输入与 output 的轴,最终状态仍按层数×方向数、batch、hidden 排列。变长输入应使用 packed sequence 或明确 mask,直接取 out[:, -1] 可能取到 padding 后的状态。
数学表达
核心公式
循环神经网络在时间上重复使用同一组参数,通过隐藏状态连接相邻时刻。普通 RNN 常以线性组合加 tanh 更新状态。
变量含义
Nbatch sizeD/H特征或隐藏维度
计算步骤
- 1h1=tanh(1),约 0.761594。
- 2h2=tanh(h1),约 0.642015。
代码对应位置
示例 1标量 RNN 记住上一步的最小验证:使用完整构造输入核对标量 RNN 记住上一步,输出只表示本例计算。
变量解释
| 变量 | 含义 |
|---|---|
N | batch size |
D/H | 特征或隐藏维度 |
完整数值示例
标量 RNN 记住上一步
已知条件
- h0=0,输入为 1、0
- 输入权重与循环权重均为 1,无偏置
计算步骤
- h1=tanh(1),约 0.761594。
- h2=tanh(h1),约 0.642015。
计算结果
第二步输入为零,状态仍保留第一步影响。
处理前后对比
- 有限隐藏状态不是完整历史副本,普通 RNN 的长依赖与状态重置都需要特别检查。
标量 RNN 记住上一步
如果没有循环项,第二步输出就是 tanh(0)=0。保留下来的 0.642 显示状态依赖过去,但每次 tanh 也会改变幅度,不能把隐藏状态当作无损历史存储。
同一权重被用两次,反向的贡献会汇总到这组共享参数。在线推理中可以携带状态;跨独立样本则必须重置,避免串入上一条序列的信息。
计算与实现步骤
- h1=tanh(1),约 0.761594。
- h2=tanh(h1),约 0.642015。
代码实现
示例 1
标量 RNN 记住上一步的最小验证
example_01.py用途:使用完整构造输入核对标量 RNN 记住上一步,输出只表示本例计算。
import torch
from torch import nn
rnn = nn.RNN(1, 1, bias=False, batch_first=True)
with torch.no_grad():
rnn.weight_ih_l0.fill_(1.)
rnn.weight_hh_l0.fill_(1.)
out, h = rnn(torch.tensor([[[1.], [0.]]]))
print([round(v, 6) for v in out.detach().flatten().tolist()])
print("output:", tuple(out.shape), "hidden:", tuple(h.shape))代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
使用完整构造输入核对标量 RNN 记住上一步,输出只表示本例计算。
Step 1 · 1–9 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import torch
from torch import nn
rnn = nn.RNN(1, 1, bias=False, batch_first=True)
with torch.no_grad():
rnn.weight_ih_l0.fill_(1.)
rnn.weight_hh_l0.fill_(1.)
out, h = rnn(torch.tensor([[[1.], [0.]]]))
print([round(v, 6) for v in out.detach().flatten().tolist()])
print("output:", tuple(out.shape), "hidden:", tuple(h.shape))预期输出或运行结果
[0.761594, 0.642015] output: (1, 2, 1) hidden: (1, 1, 1)
常见错误 · 4 条
- 长依赖梯度消失
- padding 更新状态
- batch_first 误解
- 确认样本轴、特征轴以及 X 与 y 的第一维完全对应。
实际应用
- 在线传感器序列处理
- 小规模文本和时间序列基线
常见错误
- 长依赖梯度消失
- padding 更新状态
- batch_first 误解
常见问题
RNN 的参数量会随序列长度增加吗?
同一层的参数在时间上共享,因此不随长度增加;计算量和反向保存的激活通常会随长度增加。
这个示例能说明什么边界?
有限隐藏状态不是完整历史副本,普通 RNN 的长依赖与状态重置都需要特别检查。
推荐学习资料
参考库不会生成虚假资源或无效外部链接。