直接pad_sequence会出错是因为rnn无法自动忽略padding,导致梯度错误反传;需配合pack_padded_sequence和pad_packed_sequence实现动态长度处理。

为什么直接 pad_sequence 会出错?
PyTorch 的 RNN、LSTM、GRU 默认要求输入是「时间步在第 0 维」的张量,且所有序列必须等长。如果你用 pad_sequence 填充后直接送入,模型能跑通,但梯度会错误地反传到 padding 位置——因为 RNN 没法自动忽略填充值。
真正需要的是:让 RNN 在前向时跳过 padding 位置,在反向时也不更新对应时刻的隐藏状态。这靠 pack_padded_sequence 和 pad_packed_sequence 配合完成,而不是只靠 padding。
常见错误现象:
- 模型 loss 不下降或震荡剧烈
- RuntimeError: input.size(0) == batch_size expected(没按 batch_first=True 一致设置)
- 隐藏状态维度对不上(比如忘记 output, (h_n, c_n) = lstm(packed) 中 h_n 是最后一层最后时刻的隐藏态,不是每个时间步的)
pack_padded_sequence 的三个关键参数怎么设?
pack_padded_sequence 要求输入已经是按长度降序排列的 batch,否则会报错或静默出错。它不检查顺序,只按你给的 lengths 截断。
-
input:形状为(seq_len, batch, feature)或(batch, seq_len, feature),取决于batch_first参数 -
lengths:1Dtorch.Tensor,含每个样本的真实长度,必须严格降序(如[12, 8, 5]),不能是[8, 12, 5] -
batch_first=True时,input是(batch, seq_len, feature),但lengths仍按 batch 维顺序给;此时pack_padded_sequence内部会先 transpose 再 pack,所以输出仍是(packed_seq_len, batch, feature)
实操建议:
- 先用 torch.nn.utils.rnn.pad_sequence 填充(batch_first=True)
- 再用 torch.argsort(lengths, descending=True) 得到排序索引,重排填充后的 batch 和 lengths
- 最后调用 pack_padded_sequence(x_sorted, lengths_sorted, batch_first=True, enforce_sorted=True)
unpack 后的 output 形状为什么和输入不一致?
pad_packed_sequence 返回两个值:output(填充回原 batch shape 的张量)和 input_lengths(就是你传进去的 lengths)。但注意:output 的 shape 取决于你 pack 时的 batch_first 设置:
- 若 pack 时设了
batch_first=True,则output是(batch, max_len, hidden_size * num_directions) - 若没设(默认 False),则
output是(max_len, batch, ...)
容易踩的坑:
- 忘记 pad_packed_sequence 的 total_length 参数:如果不指定,它只返回实际最长序列长度,可能导致后续操作维度不匹配(比如和未 padding 的 label 对齐失败)
- 错误地把 output[:, -1, :] 当作句向量:变长序列中,-1 是 padding 位置。应该用 output[torch.arange(batch_size), lengths - 1] 取每个样本最后一个非 padding 时刻的输出
要不要用 PackedSequence 做 Attention?
绝大多数自定义 attention(比如 dot-product attention)无法直接处理 PackedSequence,因为它不是普通 tensor。你得先 pad_packed_sequence 回完整 shape,再 mask 掉 padding 位置。
实操建议:
- 用 torch.nn.functional.pad 或 pad_packed_sequence(..., total_length=max_len) 得到完整 output
- 构造 mask:例如 mask = torch.arange(max_len)[None, :] (shape <code>(batch, max_len))
- 在 softmax 前把 padding 位置的 score 设为极小值:score.masked_fill_(~mask.unsqueeze(1), float('-inf'))
别指望 PackedSequence 能加速 attention 计算——padding mask + broadcast 才是标准做法。Pack/Unpack 只对 RNN 类原生支持变长的模块有意义。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











