1. RNN 的状态递推
序列模型在时间步
同一组参数在所有时间步共享,因此 RNN 可以处理不同长度的序列。隐藏状态不是一个独立数据库,而是模型根据历史输入压缩出的连续表示。
flowchart LR
X1[x1] --> H1[h1]
H1 --> H2[h2]
X2[x2] --> H2
H2 --> H3[h3]
X3[x3] --> H3
2. PyTorch 中的输入和输出形状
import torch
from torch import nn
class SequenceClassifier(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super().__init__()
self.rnn = nn.RNN(
input_size=input_size,
hidden_size=hidden_size,
batch_first=True,
)
self.classifier = nn.Linear(hidden_size, num_classes)
def forward(self, x):
outputs, h_n = self.rnn(x)
final_state = h_n[-1]
return self.classifier(final_state)
x = torch.rand(32, 5, 10) # batch, sequence, feature
model = SequenceClassifier(10, 20, 2)
logits = model(x) # (32, 2)
当 batch_first=True:
- 输入:
(batch, sequence_length, input_size); - 每步输出:
(batch, sequence_length, hidden_size * directions); - 最终状态
h_n:(layers * directions, batch, hidden_size)。
源笔记使用 outputs[:, -1, :] 作为序列表示,对等长、无 padding 的简单输入可行。对变长序列,最后一列可能只是 <pad>;应根据真实长度取状态、使用 pack_padded_sequence,或通过 mask 做池化。
3. 为什么基本 RNN 难以处理长依赖
反向传播需要沿时间轴不断乘以雅可比矩阵:
- 连乘范数持续小于 1,容易梯度消失;
- 连乘范数持续大于 1,容易梯度爆炸;
- 每个时间步依赖前一步,限制训练并行性。
常见应对:
- LSTM / GRU 用门控控制信息保留与更新;
- 梯度裁剪限制爆炸;
- 合理初始化、归一化和学习率;
- 能并行建模的场景可考虑 CNN 或 Transformer。
4. LSTM、GRU 和双向结构
| 结构 | 核心机制 | 特点 |
|---|---|---|
| 基本 RNN | 单一隐藏状态 | 结构简单,长依赖能力有限 |
| LSTM | 输入门、遗忘门、输出门和 cell state | 参数较多,显式控制长期记忆 |
| GRU | 更新门、重置门 | 比 LSTM 更简洁,常有相近表现 |
| 双向 RNN | 正向与反向各处理一次 | 适合可同时看到完整上下文的任务,不适合因果在线生成 |
模型选择应由数据规模、时延、序列长度和因果约束决定,而不是默认某一种总是更好。
5. 自然语言推断任务
NLI 输入一对句子:前提(premise)与假设(hypothesis),输出二者关系:
- entailment:前提支持假设;
- contradiction:前提与假设矛盾;
- neutral:前提不足以判断假设真伪。
SNLI 数据处理链:
flowchart LR
A[原始句对与标签] --> B[清洗与分词]
B --> C[构建词表]
C --> D[截断 / padding]
D --> E[前提张量 + 假设张量]
E --> F[DataLoader]
F --> G[三分类模型]
源 Notebook 的关键形状是:
- 前提:
(batch_size, num_steps); - 假设:
(batch_size, num_steps); - 标签:
(batch_size,)。
padding token 不应参与有效信息聚合。自行实现注意力或池化时,需要显式 mask。
6. Decomposable Attention 的三阶段
natural-language-inference-attention.ipynb 不使用循环网络,而是采用可分解注意力模型:
- Attend:计算前提 token 与假设 token 的两两相似度,并生成对齐表示。
- Compare:分别比较每个 token 与其对齐结果。
- Aggregate:对比较结果聚合,再完成三分类。
flowchart TB
P[Premise tokens] --> A[Attend]
H[Hypothesis tokens] --> A
A --> CP[Compare premise]
A --> CH[Compare hypothesis]
CP --> AG[Aggregate]
CH --> AG
AG --> Y[Entailment / Contradiction / Neutral]
它说明了一个重要思想:句对任务不一定先把各自压缩为单个向量;先做跨句 token 对齐,再聚合局部比较结果,往往更直接。
7. NLI 训练容易踩的坑
- 数据划分或句子对重复造成泄漏。
- 把
<pad>纳入平均池化或注意力分数。 - 未保存词表与 token 到索引映射,导致权重无法复现。
- 把类别编号含义写死但未保存映射。
- 只看 accuracy,忽略三类召回率和混淆矩阵。
- 用测试集反复调最大序列长度、词表大小或模型结构。
- 预训练词向量与词表索引没有正确对齐。
8. 何时使用哪种序列建模方式
| 场景 | 可优先考虑 |
|---|---|
| 小规模时间序列、状态递推明确 | GRU / LSTM |
| 在线、严格因果、低时延 | 单向 RNN/GRU 或因果模型 |
| 句对局部对齐 | Decomposable Attention 或交叉注意力 |
| 长序列、训练并行性重要 | Transformer 或高效注意力变体 |
| 资源受限、序列较短 | 小型 GRU / 1D CNN |
来源映射
RNN.md自然语言推断.mdnatural-language-inference-and-dataset.ipynbnatural-language-inference-attention.ipynbd2l-zh\pytorch\chapter_recurrent-neural-networks\d2l-zh\pytorch\chapter_natural-language-processing-applications\
其中两份 NLI Notebook 与完整 d2l-zh 目录主要属于教材学习材料,本笔记只提炼其概念和实验结构,不把教材内容视作个人原创代码。