TRL documentation
减少内存使用
减少内存使用
训练工作流通常可以优化以降低内存消耗,TRL 提供了多项内置功能来帮助实现这一点。
下面,我们将概述这些技术,并建议尝试不同的组合,以找出最适合您特定环境的配置。
每种方法都包含受支持训练器的示例。如果您不确定某项技术是否与您的训练器兼容,请查看相应训练器的文档。
有关其他策略(例如所有训练器均支持的梯度检查点),请参阅transformers性能指南。
截断
数据集中的序列长度可能差异很大。在对数据进行批处理时,序列会被填充以匹配该批次中最长的序列,这可能导致内存占用过高,即使大多数序列相对较短也是如此。

为了降低内存占用,将序列截断至合理长度至关重要。虽然 TRL 训练器默认会截断序列,但你可能需要根据具体使用场景调整默认的截断长度。
DPO 截断通过 max_length 进行控制,它会截断组合后的 prompt+completion 序列。

要设置截断参数,请使用以下代码片段:
from trl import DPOConfig
training_args = DPOConfig(..., max_length=...)旧版参数
max_prompt_length和max_completion_length现已移除;请在训练前对数据集中过长的提示/补全进行过滤或预截断。
SFT 截断通过 max_length 参数应用于输入序列。

要设置截断参数,请使用以下代码片段:
from trl import SFTConfig
training_args = SFTConfig(..., max_length=...)如何选择合适的 max_length 值?
如果 max_length 设置得过小,大量 token 将被丢弃,无法参与训练。如果设置得过大,内存占用可能会激增,从而引发内存溢出(OOM)错误。在未使用 packing 或 padding-free 的情况下,较大的 max_length 还可能导致训练效率低下,因为其中包含大量 padding token。
为了帮助您选择合适的值,我们提供了一个实用工具,用于可视化数据集中的序列长度分布。
打包
此技术仅适用于 SFT 训练以及使用 FlashAttention(或其变体)的配置。
截断存在以下几个缺点:
- 信息丢失:序列末尾的重要标记可能会被丢弃。
- 选择截断长度:过短会丢失数据;过长会降低效率。
Packing 通过将多个序列分组到同一训练行中,并将每行填充至 max_length,从而缓解这些问题。

TRL 使用 最佳适应递减(BFD) 装箱算法来实现打包,该算法在高效地分组序列的同时最大限度地减少填充。当序列长度超过 max_length 时,不同的策略决定了如何处理溢出的 token。
TRL 支持三种策略:
"bfd"(默认值):使用 Best-Fit Decreasing 打包。如果序列长度超过max_length,则丢弃溢出的 token。"bfd_split":使用 Best-Fit Decreasing packing,但在打包之前,长序列会被拆分为长度 ≤max_length的块。这保留了所有 token,并遵循了 Fewer Truncations Improve Language Modeling 中提出的方法。"wrapped":所有 token 被拼接成一个流,然后分割为固定长度的块。这种策略最大限度地减少了填充,但可能会混合不相关的样本。该策略对应于文献中描述的 concatenate-then-split 预处理方法(例如 Fewer Truncations Improve Language Modeling)。其缺点在于会破坏数据集中很大一部分序列的连续性,从而损害性能,这一点在 Qwen3-Coder-Next Technical Report 中有所讨论。
如果所有序列的长度都小于
max_length,bfd和bfd_split的行为完全相同,因为无需进行截断或拆分。
from trl import SFTConfig
training_args = SFTConfig(
...,
packing=True,
packing_strategy="bfd",
max_length=512,
)用于参数高效微调的 PEFT
诸如 LoRA 等参数高效微调(PEFT)方法,是降低训练期间内存占用最有效的技术之一。PEFT 方法不训练模型的所有参数,而是仅训练少量适配器参数,从而大幅降低内存需求,并使得在有限硬件上微调更大规模的模型成为可能。
有关使用 PEFT 与 TRL 的详细信息,包括各种适配器方法、量化选项和高级配置,请参阅 PEFT 集成。
要使用 PEFT 来减少内存占用:
from datasets import load_dataset
from peft import LoraConfig
from trl import SFTTrainer
dataset = load_dataset("trl-lib/Capybara", split="train")
peft_config = LoraConfig()
trainer = SFTTrainer(
model="Qwen/Qwen2.5-0.5B",
train_dataset=dataset,
peft_config=peft_config,
)PEFT 可以与其他内存缩减技术(例如 4 位或 8 位量化)结合使用,以实现更大的内存节省。有关量化的示例,请参阅 PEFT 集成。
无填充
无填充批处理(Padding-free batching)是降低内存占用的另一种方法。在此方法中,首先采样一个批次,然后将其展平为单个序列,从而避免填充。与打包(packing)不同,打包通过组合不同样本的部分可能导致序列不完整,而无填充批处理可确保所有序列保持完整且不被截断。

Padding-free 功能在 DPOTrainer 中暂时不可用:自 DPO 重构以来,设置
padding_free=True会发出警告并回退到标准填充。该功能计划在后续更新中恢复。
from trl import DPOConfig
training_args = DPOConfig(..., padding_free=True, model_init_kwargs={"attn_implementation": "kernels-community/flash-attn2"})from trl import SFTConfig
training_args = SFTConfig(..., padding_free=True, model_init_kwargs={"attn_implementation": "kernels-community/flash-attn2"})激活值卸载
激活卸载是一种内存效率技术,它在前向传播期间将激活张量临时移至 CPU 内存,并仅在反向传播需要时将其移回,从而降低 GPU 显存(VRAM)的使用量。这可以显著降低峰值内存使用量,代价是训练时间略有增加。
要在您的 SFT 训练配置中启用激活值卸载:
from trl import SFTConfig
training_args = SFTConfig(..., activation_offloading=True)在底层,激活卸载通过实现 PyTorch 的 saved_tensors_hooks 来拦截前向传播过程中的激活值。它会根据张量的大小和上下文智能地管理哪些张量需要卸载,从而避免卸载那些效率低下的输出张量。为了优化性能,它可以通过一个标志位(默认为 true)使用 CUDA 流,将计算与 CPU-GPU 数据传输重叠执行。
将序列填充为倍数
目前,此技术仅支持 SFT 和 Reward 训练器。
启用此选项后,可确保所有序列都填充为指定值的倍数。
通过将序列长度对齐到对内存友好的边界,这可以在某些硬件上提高计算效率。
from trl import SFTConfig
training_args = SFTConfig(..., pad_to_multiple_of=2048)from trl import RewardConfig
training_args = RewardConfig(..., pad_to_multiple_of=2048)PyTorch 缓存分配器
在长时间运行中,尤其是在线强化学习(GRPO、RLOO、Online DPO)场景下,GPU 内存可能会发生碎片化。设置 expandable_segments:True 可以让 PyTorch 的缓存分配器扩展现有内存段,从而减少已分配内存与已保留内存之间的差距:
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
export PYTORCH_ALLOC_CONF=expandable_segments:True # canonical name since PyTorch 2.10在 vLLM 睡眠模式(
vllm_enable_sleep_mode=True)下,vLLM 仅读取PYTORCH_CUDA_ALLOC_CONF以绕过此设置,因此请勿仅设置PYTORCH_ALLOC_CONF。
在线方法中禁用生成时的模型收集
在使用 DeepSpeed ZeRO-3 时,模型权重会分片分布在多个 GPU 上。在线方法涉及在训练过程中从模型生成补全内容。在此步骤中,模型权重会临时汇集到单个 GPU 上进行生成。对于非常大的模型,这种汇集操作可能导致 OOM 错误,如该 issue 所述:#2250。
如果遇到此问题,可以通过设置以下参数来禁用生成过程中模型权重的收集:
from trl import GRPOConfig
training_args = GRPOConfig(..., ds3_gather_for_generation=False)from trl.experimental.online_dpo import OnlineDPOConfig
training_args = OnlineDPOConfig(..., ds3_gather_for_generation=False)from trl import RLOOConfig
training_args = RLOOConfig(..., ds3_gather_for_generation=False)此调整可防止模型权重被收集,从而避免 OOM 错误,但可能会导致生成速度变慢。
vLLM 睡眠模式
当使用 vLLM 作为在线训练方法的生成后端时,您可以启用 sleep mode,在优化步骤期间将 vLLM 的参数和缓存卸载到 CPU 内存中,并在需要执行权重同步和生成时重新加载回 GPU 显存。
from trl import GRPOConfig
training_args = GRPOConfig(..., vllm_enable_sleep_mode=True)from trl import RLOOConfig
training_args = RLOOConfig(..., vllm_enable_sleep_mode=True)将 vLLM 的权重和缓存卸载有助于保持较低的 GPU 内存占用,这在训练大型模型或使用有限的 GPU 资源时尤为有益。然而,从睡眠模式唤醒 vLLM 引擎会引入一些主机-设备传输延迟,这可能会对训练速度产生轻微影响。
梯度检查点
梯度检查点通过不在前向传播期间存储所有中间激活值,而是在反向传播期间重新计算它们,从而以计算换取内存。
from trl import SFTConfig
training_args = SFTConfig(..., gradient_checkpointing=True)所有训练器默认启用梯度检查点以优化内存使用。如有需要,可通过设置
gradient_checkpointing=False将其禁用。
选择性激活检查点
使用 SFTTrainer,你可以在前向传播过程中保存注意力输出,而不是在反向传播中重新计算它。这可以恢复长上下文下大部分因检查点导致的性能下降,代价是每层额外增加一个隐藏状态大小的张量。它会强制使用非重入检查点。
from trl import SFTConfig
training_args = SFTConfig(
...,
gradient_checkpointing=True,
gradient_checkpointing_kwargs={"selective": True},
)有关更多内存优化技术,请参阅 Transformers 性能指南。
Update on GitHub