You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch中如何将不同长度序列打包为批次并忽略填充部分

PyTorch可变长度序列处理方案

1. 序列填充实现

可变长度序列的填充推荐在DataLoader的collate_fn中实现,可按每个批次的最大序列长度动态填充,减少冗余计算,解决默认拼接逻辑要求样本维度一致的报错。
具体实现代码:

from torch.utils.data import DataLoader
from torch.nn.utils.rnn import pad_sequence
import torch
import torch.nn as nn
import torch.nn.functional as F

def collate_fn(batch):
    # batch为Dataset.__getitem__返回的样本列表,每个样本结构为(seq_tensor, label),其中seq_tensor维度为(x, 128)
    seqs = [item[0] for item in batch]
    labels = [item[1] for item in batch]
    # 记录每个序列的原始长度
    seq_lens = torch.tensor([len(s) for s in seqs], dtype=torch.long)
    # 执行填充,默认补0,可通过padding_value参数自定义填充值
    padded_seqs = pad_sequence(seqs, batch_first=True) # 输出维度为(batch_size, max_len, 128)
    return padded_seqs, seq_lens, torch.tensor(labels)

# DataLoader传入自定义collate_fn即可完成批次自动填充
dataloader = DataLoader(your_dataset, batch_size=32, collate_fn=collate_fn)

2. 填充内容过滤实现

通过有效位置掩码(mask)即可实现填充内容的过滤,全程采用张量运算,不需要编写Python循环,效率和原生算子一致。

2.1 前馈子网络批量计算

对每个序列元素独立计算的nn.Sequential子网络,可直接对填充后的张量做维度转换后批量计算:

class SeqModel(nn.Module):
    def __init__(self, feature_dim=128, hidden_dim=64, out_dim=1):
        super().__init__()
        self.feature_extractor = nn.Sequential(
            nn.Linear(feature_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, out_dim)
        )
    
    def forward(self, padded_seqs, seq_lens):
        batch_size, max_len, feat_dim = padded_seqs.shape
        # 维度转换为(batch_size*max_len, feat_dim),批量过子网络
        flat_feats = padded_seqs.reshape(-1, feat_dim)
        flat_outputs = self.feature_extractor(flat_feats)
        # 恢复维度为(batch_size, max_len, out_dim)
        seq_outputs = flat_outputs.reshape(batch_size, max_len, -1)
        
        # 生成有效位置掩码,维度为(batch_size, max_len),有效位置为True,填充位置为False
        mask = torch.arange(max_len, device=seq_lens.device)[None, :] < seq_lens[:, None]
        mask = mask.unsqueeze(-1) # 扩展维度和输出对齐,方便后续计算
        
        # 填充位置输出置0,不影响后续累加/平均计算
        seq_outputs = seq_outputs * mask
        
        # 示例:生成样本级输出,可选求和/平均
        # 求和实现:填充位置为0,无额外影响
        sample_sum_output = seq_outputs.sum(dim=1)
        # 平均实现:除以对应样本的有效长度
        # sample_avg_output = seq_outputs.sum(dim=1) / seq_lens.unsqueeze(-1)
        
        return sample_sum_output, seq_outputs, mask

2.2 损失计算忽略填充部分

如果是序列元素级损失,通过掩码过滤填充位置的损失值即可:

def sequence_loss(seq_outputs, target, mask):
    # 计算无归约的损失
    loss = F.mse_loss(seq_outputs, target, reduction='none')
    # 过滤填充位置损失
    loss = loss * mask
    # 按有效元素数量做归一化
    avg_loss = loss.sum() / mask.sum()
    return avg_loss

方案说明

该方案完全适配前馈网络场景,不需要依赖RNN类模块的内置填充处理能力,所有运算均支持自动微分,梯度计算和反向传播不受填充内容影响。

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 11:09:01