pad_sequence、pack_padded_sequence、pad_packed_sequence 序列填充与打包详解
这三个函数是处理变长序列的核心工具,专门解决 NLP、时间序列等场景中「样本长度不一致」的问题,让 RNN/LSTM/GRU 能高效处理数据。
实际场景
在深度学习中,一个 batch 的序列必须长度相同(比如句子长度、时间步长度),但实际数据中每个样本长度不一样:
-
例:batch 有 3 个句子,长度分别为
[5, 3, 4] -
直接输入 RNN 会报错,必须统一长度
这三个函数分工明确:
| 函数 | 输入 | 输出 | 核心作用 |
|---|---|---|---|
pad_sequence | 变长序列列表 | 等长填充张量 | 统一序列长度 |
pack_padded_sequence | 填充张量 + 真实长度 | PackedSequence | 去除填充,加速 RNN |
pad_packed_sequence | PackedSequence | 填充张量 + 长度 | 还原序列,方便后续计算 |
pad_sequence:序列填充(最基础)
作用
将多个长度不同的一维序列,填充到相同长度,生成标准 batch 张量。
核心参数
-
sequences:列表形式的变长序列(每个元素是 tensor) -
batch_first:True→ 输出形状[batch_size, seq_len](最常用);False→[seq_len, batch_size] -
padding_value:填充值,默认0
使用示例
import torch
from torch.nn.utils.rnn import pad_sequence
# 1. 构造变长序列(3个样本,长度分别5、3、4)
seq1 = torch.tensor([1, 2, 3, 4, 5]) # 长度5
seq2 = torch.tensor([6, 7, 8]) # 长度3
seq3 = torch.tensor([9, 10, 11, 12]) # 长度4
sequences = [seq1, seq2, seq3]
# 2. 填充(batch_first=True 最常用)
padded_seq = pad_sequence(sequences, batch_first=True, padding_value=0)
print("填充后的序列:")
print(padded_seq)
print("形状:", padded_seq.shape) # [3, 5] → batch=3,最大长度=5
输出结果
填充后的序列: 直接按照最长的填充
tensor([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 0, 0],
[ 9, 10, 11, 12, 0]])
形状: torch.Size([3, 5])
pack_padded_sequence:打包填充序列(加速 RNN)
作用
去除填充值(0),把填充后的等长序列压缩成紧凑的 PackedSequence 对象。
-
RNN 计算时 只计算真实数据,会跳过填充值,不浪费算力在填充值上 ,大幅提升效率
-
必须传入每个序列的真实长度
核心参数
-
sequence:pad_sequence 输出的填充序列 -
lengths:每个样本的真实长度(必须从大到小排序!) -
batch_first:和 pad_sequence 保持一致
示例
import torch
from torch.nn.utils.rnn import pad_sequence,pad_packed_sequence
from torch.nn.utils.rnn import pack_padded_sequence
# 1. 按照长度降序排列
seq1 = torch.tensor([1, 2, 3, 4, 5]) # 长度5
seq3 = torch.tensor([9, 10, 11, 12]) # 长度4
seq2 = torch.tensor([6, 7, 8]) # 长度3
sequences = [seq1,seq3, seq2]
# 2. 填充(batch_first=True 最常用)
padded_seq = pad_sequence(sequences, batch_first=True, padding_value=0)
print(padded_seq)
# 1. 每个序列的真实长度(必须 降序排列!)
lengths = torch.tensor([5, 4, 3]) # 对应 seq1、seq3、seq2(一定要排序)
# 2. 打包(去除填充值)
packed_seq = pack_padded_sequence(
padded_seq,
lengths=lengths,
batch_first=True,
enforce_sorted=True # 表示已按长度降序排序,
)
print(packed_seq.data)
print(packed_seq.batch_sizes)
输出结果
tensor([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 0, 0],
[ 9, 10, 11, 12, 0]])
tensor([ 1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 0, 5])
tensor([3, 3, 3, 2, 1])
坑!!!!!!
输入序列必须按真实长度 降序排列!
-
例:长度
5 > 4 > 3,对应样本顺序:seq1、seq3、seq2
代码
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pad_packed_sequence
from torch.utils.data import Dataset, DataLoader
import torch
from torch.nn.utils.rnn import pad_sequence,pad_packed_sequence
from torch.nn.utils.rnn import pack_padded_sequence
# 1. 按照长度降序排列
seq1 = torch.tensor([1, 2, 3, 4, 5]) # 长度5
seq3 = torch.tensor([9, 10, 11, 12]) # 长度4
seq2 = torch.tensor([6, 7, 8]) # 长度3
sequences = [seq1,seq3, seq2]
# 2. 填充(batch_first=True 最常用)
padded_seq = pad_sequence(sequences, batch_first=True, padding_value=0)
length = torch.tensor([5, 4, 3])
batch_x_l =(padded_seq, length)
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.embedding = nn.Embedding(num_embeddings=20, embedding_dim=5, padding_idx=0)
self.rnn = nn.RNN(input_size=5, hidden_size=10, num_layers=1, batch_first=True)
def forward(self, x_l):
x =x_l[0]
length = x_l[1]
embd = self.embedding(x)
packed_seq = pack_padded_sequence(embd, length, batch_first=True)
y = self.rnn(packed_seq)
return y
model = Model()
y, h = model(batch_x_l)
print(type( y))
print(y.data.shape)
print(y.batch_sizes)
输出结果
<class 'torch.nn.utils.rnn.PackedSequence'>
torch.Size([12, 10])
tensor([3, 3, 3, 2, 1])
pad_packed_sequence:填充打包对象
作用
把 RNN 输出的打包 PackedSequence 对象,还原成填充前的等长序列, 因为之前喂给模型的是数据是 : tensor([ 1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 0, 5]) 批次 : tensor([3, 3, 3, 2, 1])
模型输出 torch.Size([12, 10]) 批次 : tensor([3, 3, 3, 2, 1]) 所以还需要将rnn 输出的模型形状变成 [3,5], 方便后续做损失计算、预测。
核心参数
-
sequence:打包后的 PackedSequence 对象 -
batch_first:和之前保持一致 -
padding_value:填充值(默认 0)
使用示例
import torch
from torch.nn.utils.rnn import pad_packed_sequence, pad_sequence, pack_padded_sequence
# 1. 按照长度降序排列
seq1 = torch.tensor([1, 2, 3, 4, 5]) # 长度5
seq3 = torch.tensor([9, 10, 11, 12]) # 长度4
seq2 = torch.tensor([6, 7, 8]) # 长度3
sequences = [seq1,seq3, seq2]
# 2. 填充(batch_first=True 最常用)
padded_seq = pad_sequence(sequences, batch_first=True, padding_value=0)
print("填充后的序列:")
print(padded_seq)
# 1. 每个序列的真实长度(必须 降序排列!)
lengths = torch.tensor([5, 4, 3]) # 对应 seq1、seq3、seq2(一定要排序)
# 2. 打包(去除填充值)
packed_seq = pack_padded_sequence(
padded_seq,
lengths=lengths,
batch_first=True,
enforce_sorted=True # 表示已按长度降序排序,
)
print("打包后的序列:")
print(packed_seq.data)
print(packed_seq.batch_sizes)
# 1. 解包还原
unpacked_seq, unpacked_lengths = pad_packed_sequence(
packed_seq,
batch_first=True,
padding_value=0
)
print("解包后的序列:")
print(unpacked_seq)
print("解包后的长度:", unpacked_lengths)
输出结果
解包后的序列:
tensor([[ 1, 2, 3, 4, 5],
[ 9, 10, 11, 12, 0],
[ 6, 7, 8, 0, 0]])
torch.Size([3, 5])
解包后的长度: tensor([5, 4, 3])
✅ 解包结果和原始填充序列完全一致!
完整流水线(RNN 标准流程)
实际项目中,三个函数配合使用,标准流程:
-
原始变长序列 →
pad_sequence→ 填充序列 -
填充序列 →
pack_padded_sequence→ 打包序列(输入 RNN) -
RNN 输出打包序列 →
pad_packed_sequence→ 还原填充序列
完整代码
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pad_packed_sequence
from torch.utils.data import Dataset, DataLoader
class MyData(Dataset):
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx]
def collate_fn(data):
data.sort(key=lambda x: len(x), reverse=True)
seq_len = [s.size(0) for s in data]
data = pad_sequence(data, batch_first=True)
return data, seq_len
a = torch.tensor([1,2,3,4])
b = torch.tensor([5,6,7])
c = torch.tensor([7,8])
d = torch.tensor([9])
train_x = [a, b, c, d]
data = MyData(train_x)
data_loader = DataLoader(data, batch_size=2, collate_fn=collate_fn)
batch_x_l = iter(data_loader).__next__()
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.embedding = nn.Embedding(num_embeddings=20, embedding_dim=5, padding_idx=0)
self.rnn = nn.RNN(input_size=5, hidden_size=10, num_layers=1, batch_first=True)
def forward(self, x_l):
x =x_l[0]
length = x_l[1]
embd = self.embedding(x)
packed_seq = pack_padded_sequence(embd, length, batch_first=True)
y = self.rnn(packed_seq)
return y
print("batch_x:", batch_x_l)
model = Model()
y, h = model(batch_x_l)
print(type( y))
print(y)
out_pad, out_len = pad_packed_sequence(y, batch_first=True)
print(out_pad.shape)
print(out_len)
print(out_pad)
总结
-
pad_sequence:解决「batch 长度不一致」问题,用 0 填充成等长序列;
-
pack_padded_sequence:压缩填充值,让 RNN 不计算无效填充,提升效率;
-
pad_packed_sequence:把 RNN 输出还原为填充格式,适配后续任务;
2186



被折叠的 条评论
为什么被折叠?



