性能调优指南 ============================== 最后更新时间:2025/07/17。 作者:`盛光明 `_,`郑嘉丽 `_ 在本节中,我们将讨论如何调优 verl 中所有阶段的性能,包括: 1. 滚动生成吞吐量。 2. 为序列打包(即数据打包和去除填充)启用 ``use_remove_padding=True``。 3. 前向和后向计算的批次大小调优 4. 为获得更高的吞吐量启用 ``use_dynamic_bsz=True``。 5. 利用 Ulysses Sequence Parallel 进行长上下文训练 6. LigerKernel 用于 SFT 性能优化 7. FSDP 训练后端的预取前向计算 8. 来自 logits 的熵计算的内存优化 滚动生成调优 -------------------------- verl 目前支持两个滚动后端:vLLM 和 TGI(SGLang 支持即将推出)。 以下是调优基于 vLLM 的滚动的关键因素。在调优之前,我们建议将 ``actor_rollout_ref.rollout.disable_log_stats`` 设置为 ``False``,以便记录滚动统计信息。 - 增加 ``gpu_memory_utilization``。 - 对于 vLLM v0.7.0 及更高版本,vLLM 实例将仅使用**总**内存的一部分 ``gpu_memory_utilization``。 - 对于 SGLang,它是用于模型权重和 KV 缓存等**静态**内存的可用 GPU 内存的比例。然而,在推理过程中,剩余的 (1-gpu_memory_utilization) 也会被使用。 但是,如果模型参数和优化器状态未卸载,使用过高的比例可能会导致 OOM(内存不足)。 通常,0.5 到 0.7 之间的值可以在高吞吐量和避免 OOM 之间取得良好平衡。 注意:由于 ``gpu_memory_utilization`` 的定义因推理引擎而异,一个引擎的有效值可能会导致另一个引擎 OOM。 - 调整 ``max_num_seqs`` 或 ``max_num_batched_tokens``。 如果日志中 GPU 缓存利用率相对较低,增加 ``max_num_seqs`` 或 ``max_num_batched_tokens`` 可以增大解码阶段的有效批次大小,从而允许每个批次处理更多并发请求。 我们建议将 ``max_num_batched_tokens`` 设置为大于 2048 以获得更高的吞吐量。 - 使用较小的 ``tensor_parallel_size``。 当 GPU 资源允许时,较小的张量并行大小会生成更多的 vLLM 副本。 数据并行(DP)可以比张量并行(TP)产生更高的吞吐量,但也会增加 KVCache 的消耗。 仔细权衡更多副本和更高内存使用之间的取舍。 我们在 `HybridFlow 论文 `_ 的第 8.4 节中评估了这种权衡。 - 使用 ``cudagraph_capture_sizes`` 平衡性能和内存。 如果设置了 ``cudagraph_capture_sizes``,vLLM 将尝试为不同的批次大小捕获模型执行图。 由于 cudagraph 内存无法卸载到 CPU,在 actor 更新运行时,内存会保留在 GPU 上。 使用较小的批次大小可以避免 OOM,但会略微降低吞吐量。 必须设置 ``enforce_eager=False`` 才能使用 ``cudagraph_capture_sizes``。 有关抢占和分块预填充等更多调优细节,请参阅 `vLLM 官方调优指南 `_ 为获得最佳性能,我们建议使用 vLLM v0.8.3 或更高版本。有关详细信息,请参阅 https://github.com/volcengine/verl/blob/main/docs/README_vllm0.8.md。 启用移除填充(序列打包) ----------------------------------------- 当前,对于 llama、mistral、gemma1 和 qwen 系列模型,用户可以启用 `use_remove_padding=True` 来利用 transformers 库提供的序列打包实现。 对于其他模型,transformers 库也可能支持,但我们尚未进行测试。 用户可以将所需的模型配置添加到 ``test_transformer.py `_ 文件中。 并通过运行以下命令来测试其功能: .. code-block:: bash pytest -s tests/models/test_transformer.py 如果测试通过,您可以将所需的模型添加到 ``registry.py `_ 文件中。 然后,您就可以享受序列打包带来的性能提升了,并欢迎将您测试过的模型贡献(PR)给 verl! 批次大小调优 ----------------- 为在预制数据(即模型前向计算)和模型更新(即 actor/critic 前向/后向计算)中实现更高的吞吐量, 用户可能需要针对不同的计算调优 ``*micro_batch_size_per_gpu``。 在 verl 中,设置批次大小的核心原则是: - **算法指标**(训练批次大小、PPO mini-batch 大小)是*全局*的(从单个控制器角度看), 在每个 worker 中进行归一化。请参阅 `归一化代码 `_。 - **性能相关参数**(micro batch size、动态批次大小的最大 token 长度)是*局部*参数,用于定义每 GPU 的数据分配。 请参阅 `归一化代码 `_。 .. note:: 在您的训练脚本中,请使用 ``*micro_batch_size_per_gpu`` 而不是 ``*micro_batch_size``。 这样您就不需要考虑 ``micro_batch_size`` 的归一化,并且 ``micro_batch_size`` 将被弃用。 批次大小调优技巧 """""""""""""""""""""" 因此,用户可能需要调优 ``*micro_batch_size_per_gpu`` 来加速训练。以下是一些技巧: 1. **启用梯度检查点**: 设置 ``actor_rollout_ref.model.enable_gradient_checkpointing=True`` 和 ``critic.model.enable_gradient_checkpointing=True``。 这通常允许更大的 micro batch 大小,并且对大型 mini-batch 训练有利。 2. 尽可能增加 ``*micro_batch_size_per_gpu``,直到其等于归一化后的 ``mini_batch_size``。 3. **使用更大的仅前向参数**: 仅前向参数,如 ``actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu``、 ``actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu``、``critic.forward_micro_batch_size_per_gpu`` 可以比训练相关的 micro batch 大小(例如 ``actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu``、``critic.ppo_micro_batch_size_per_gpu``)更大(例如 2 倍)。 4. **允许 Critic 和 Reward 模型使用更大的 micro batch 大小**: Critic 和 Reward 模型的 micro batch 大小可以大于 Actor 模型。这是因为 Actor 模型在最后一层的词汇量(vocab size)要大得多。 5. **启用激活卸载**: 设置 ``actor_rollout_ref.model.enable_activation_offload=True`` 和 ``critic.model.enable_activation_offload=True``。 这通常与梯度检查点一起使用以获得更大的 micro batch 大小,并且目前仅在 FSDP 后端可用。 动态批次大小调优 ----------------------------- 动态批次大小是一种允许模型在单次前向计算中处理相似数量 token 的技术(具有不同的实际批次大小)。 这可以显著提高训练效率并减少内存使用。 要利用此技术,用户可以在 actor、ref、critic 和 reward 模型中设置 ``use_dynamic_bsz=True``。 当 ``use_dynamic_bsz=True`` 时,用户无需调优 ``*micro_batch_size_per_gpu``。 相反,用户应调优以下参数: - ``actor_rollout_ref.actor.ppo_max_token_len_per_gpu``, ``critic.ppo_max_token_len_per_gpu``: 在 ``update_policy`` 和 ``update_critic`` 的前向和后向计算中要处理的最大 token 数。 - ``actor_rollout_ref.ref.log_prob_max_token_len_per_gpu`` 和 ``actor_rollout_ref.rollout.log_prob_max_token_len_per_gpu``: 在 ``compute_log_prob`` 和 ``compute_ref_log_prob`` 的前向计算中要处理的最大 token 数。 - ``critic.forward_micro_batch_size_per_gpu``, ``reward_model.forward_micro_batch_size_per_gpu``: 在 ``compute_values``, ``compute_rm_score`` 的前向计算中要处理的最大 token 数。 动态批次大小调优技巧 """""""""""""""""""""""""""""" 以下是一些调优上述参数的技巧: 1. **增加** ``actor_rollout_ref.actor.ppo_max_token_len_per_gpu`` 将其至少设置为 (max_prompt_length + max_response_length) 的 2 倍。我们在 `run_qwen2-7b_rm_seq_balance.sh `_ 中将其设置为 3 倍。 尝试增加它以获得更高的吞吐量。 2. **仅前向参数可以更大**: 与非动态批次场景类似,仅前向的 token 限制可以超过用于前向/后向操作的限制。 3. **为 Critic 和 Reward 模型使用更大的限制**: Critic 和 Reward 参数可以设置为 Actor 限制的至少 2 倍。例如,我们在这里将其设置为 4 倍: `run_qwen2-7b_rm_seq_balance.sh `_ .. :math:`\text{critic.ppo_max_token_len_per_gpu} = 2 \times \text{actor.ppo_max_token_len_per_gpu})`。 Ulysses Sequence Parallel 用于长上下文训练 ---------------------------------------------------- 要利用此技术,用户可以在 actor、ref、critic 和 reward 模型中设置 ``ulysses_sequence_parallel_size>1``。 我们支持不同的模型使用不同的 ulysses_sequence_parallel_size。 要训练长序列(>32k),用户可能需要减小 ``*micro_batch_size_per_gpu`` 和 ``*max_token_len_per_gpu`` 以避免 OOM。 LigerKernel 用于 SFT ---------------------- LigerKernel 是一个用于监督微调 (SFT) 的高性能内核,可以提高训练效率。要在 SFT 训练中启用 LigerKernel: 1. 通过 ``pip3 install liger-kernel`` 安装 liger-kernel。在 SFT 配置文件(例如 ``verl/trainer/config/sft_trainer.yaml``)中,设置 ``use_liger`` 参数: .. code-block:: yaml model: use_liger: True # 为 SFT 启用 LigerKernel 2. 默认值为 ``False``。仅当您想使用 LigerKernel 的优化时才启用它。 3. LigerKernel 在提高 SFT 场景下的训练性能方面特别有用。 FSDP 训练后端的预取前向计算 ---------------------- 在训练阶段,用户可以通过设置 ``fsdp_config.forward_prefetch=True`` 来启用 FSDP 中的前向预取。例如,``actor_rollout_ref.actor.fsdp_config.forward_prefetch=True``。此配置在完成当前前向计算之前预取下一个前向 all-gather 操作,将通信与计算重叠,从而提高效率。有关更多详细信息,请参阅 `FSDP forward_prefetch `_ 文档。 .. note:: 不支持后向预取,因为 ``BACKWARD_POST`` 策略在嵌套模块的情况下可能预取不正确。有关详细信息,请参阅 `FSDP 文档 `_ 迁移到 FSDP2 ---------------------- FSDP2 相较于 FSDP1 提供了显著的改进。根据 `PyTorch TorchTitan 基准测试 `_: - 平均 GPU 内存使用量降低 7% - BF16 训练吞吐量提高 1.5% - 与 DTensor 和分片参数(per-parameter sharding)的组合性更好 **在 VERL 中启用 FSDP2:** .. code-block:: python # 在 actor 配置中启用 FSDP2 actor_rollout_ref.actor.strategy="fsdp2" .. note:: FSDP2 需要 PyTorch 2.1+,并且推荐用于具有 transformer 架构的模型。 来自 logits 的熵计算的内存优化 ---------------------- ``logits`` 张量(通常形状为 ``[bsz*seq_len, voc]``)可能会消耗大量内存。当使用 ``compute_entropy_from_logits`` 时,内存使用量约为 ``[bsz*seq_len, voc] × (4 字节 (float32) + 2 字节 (softmax+logsumexp 的 autocast) + 1 字节 (softmax 输出))``。 为降低此内存峰值,请通过设置启用分块计算: ``actor_rollout_ref.ref.entropy_from_logits_with_chunking = True`` 这会以 ``[chunk_size, voc]``(例如 2048)的形状分块处理张量,而不是处理完整的序列长度,仅在前向计算期间有效。 此外,在训练期间,标准的梯度检查点(``enable_gradient_checkpointing=True``)不适用于熵计算。为在此场景下降低内存峰值,请设置: ``actor_rollout_ref.actor.entropy_checkpointing = True`` 这会针对熵计算专门启用熵的重新计算,从而降低训练期间的内存使用量。