1. 引言
在 verl 中,和序列长度相关的配置看起来很多,但本质上都围绕三个问题展开:
- 样本能不能进入系统
- 样本能不能被 Rollout 正常生成
- 生成后的样本能不能被 PPO 稳定训练
很多混淆都来自于没有区分这三层边界。比如:
- 把
data.max_prompt_length误当成模型上下文窗口 - rollout 已经能跑,但 PPO 更新阶段 OOM,却仍去调
max_model_len
理解这些参数的关键,需要把它们放回完整的数据流中去看。
2. 参数分层总览
| 层次 | 代表参数 | 控制内容 |
|---|---|---|
| 数据层 | data.max_prompt_length data.max_response_length data.filter_overlong_prompts data.truncation |
样本是否保留、prompt 如何裁剪、response 最多生成多长 |
| Rollout 层 | actor_rollout_ref.rollout.max_model_len actor_rollout_ref.rollout.max_num_batched_tokens |
单条样本总上下文是否可容纳、批级推理 token 预算 |
| PPO 训练层 | actor_rollout_ref.actor.ppo_max_token_len_per_gpu critic.ppo_max_token_len_per_gpu |
actor / critic 在训练阶段单卡最多承载多少 token |
可以将它们理解为三道连续关卡:
- 数据层:决定样本能否进入系统
- Rollout 层:决定样本能否被生成
- 训练层:决定样本能否训得动
3. 一条样本在 verl 中的长度处理流程
下面这张图最适合从全局理解这些参数的分工:
否
是
True
False
error
left/right/middle
是
否
是
否
是
否
原始样本
数据预处理
prompt 是否超过
data.max_prompt_length
进入 Rollout
filter_overlong_prompts?
过滤掉样本
truncation 策略
报错
截断后保留
实际总长度是否超过
rollout.max_model_len
Rollout 阶段报错/拒绝
生成 response
长度上限由 data.max_response_length 控制
批级 token 总量是否超过
rollout.max_num_batched_tokens
拆分调度/降低吞吐
生成完成
PPO 训练
单卡 token 是否超过
actor/critic.ppo_max_token_len_per_gpu
训练阶段 OOM 或需缩小批量
正常完成 PPO 更新
这张图对应的逻辑可以概括为:
data.max_prompt_length、filter_overlong_prompts、truncation:决定 prompt 如何被接纳data.max_response_length、rollout.max_model_len:决定单条样本能否完成生成rollout.max_num_batched_tokens:决定 rollout 阶段一批样本能否高效调度actor/critic.ppo_max_token_len_per_gpu:决定 PPO 阶段单卡训练是否会爆显存
4. 数据层:决定 prompt 如何进入系统
数据层主要处理两个问题:
- prompt 最长允许多长
- 超长时是过滤、报错还是截断
4.1 data.max_prompt_length
data.max_prompt_length 表示 prompt 的最大长度上限。
它约束的是:
输入 prompt 本身最多允许多长。
注意,它不是模型的总上下文窗口,也不是训练阶段的 token 预算。
4.2 data.max_response_length
data.max_response_length 表示 rollout 阶段最多生成多少 response token。
它虽然位于 data.* 下,但作用并不只在数据层,而是会一路影响:
- rollout 的最长生成长度
- 样本总长度
- rollout 显存压力
- PPO 训练阶段的 token 预算估计
可以把它理解为:
生成阶段的最大新 token 数上限
4.3 data.filter_overlong_prompts
这个参数控制:
当 prompt 超过
data.max_prompt_length时,是否直接过滤掉样本。
True:直接丢弃超长样本False:不直接丢弃,交由truncation或报错逻辑处理
它回答的是:
这条超长样本还要不要?
4.4 data.truncation
data.truncation 用于控制:
如果样本不被过滤,那么超长 prompt 应如何截断。
支持的常见取值如下:
| 取值 | 含义 | 常见场景 |
|---|---|---|
error |
超长直接报错 | 严格暴露数据问题 |
left |
从左截断,保留后半部分 | 多轮对话,保留最近上下文 |
right |
从右截断,保留前半部分 | instruction 输入,保留前缀 |
middle |
保留首尾,删除中间 | 头尾都重要的特殊结构输入 |
4.5 数据层参数速记
| 参数 | 核心问题 |
|---|---|
data.max_prompt_length |
prompt 最长允许多长 |
data.filter_overlong_prompts |
超长样本要不要直接删 |
data.truncation |
不删的话怎么裁 |
data.max_response_length |
后续最多生成多长 |
一句话总结:
数据层负责决定 样本能否进入系统,以及如何被修整为合法输入。
5. Rollout 层:决定样本能否被正常生成
通过数据层,只说明样本"可用";并不意味着它一定能被 Rollout 成功处理。
Rollout 层还要解决两个问题:
- 单条样本总长度是否超过模型窗口
- 一个 batch 的总 token 是否超过推理预算
5.1 actor_rollout_ref.rollout.max_model_len
max_model_len 表示 Rollout 引擎能处理的 单条样本总长度上限,通常对应:
prompt length+response length \text{prompt length} + \text{response length} prompt length+response length
它的本质是:
单样本级别的上下文窗口约束
因此它和 data.max_prompt_length 的区别非常关键:
max_prompt_length:只限制 promptmax_model_len:限制 prompt + response 的总长度
最基本的安全关系通常是:
rollout.max_model_len≥data.max_prompt_length+data.max_response_length \text{rollout.max\_model\_len} \ge \text{data.max\_prompt\_length} + \text{data.max\_response\_length} rollout.max_model_len≥data.max_prompt_length+data.max_response_length
但这只是理论下限。实际工程中还要考虑:
- chat template
- system prompt
- role token
- BOS / EOS
- 多模态占位 token
所以更稳妥的理解应当是:
max_model_len要覆盖 实际送入模型的 prompt 长度 与 最大 response 长度 之和。
5.2 actor_rollout_ref.rollout.max_num_batched_tokens
这个参数控制:
rollout 阶段,一个 batch 一次最多允许处理多少 token。
它限制的是 整批总量,而不是单条样本长度。
所以它和 max_model_len 的区别可以简化为:
| 参数 | 控制对象 | 本质 |
|---|---|---|
rollout.max_model_len |
单条样本总长度 | 单样本上下文窗口 |
rollout.max_num_batched_tokens |
一个 batch 的 token 总量 | 批级推理预算 |
一句话区分:
max_model_len管"单条有多长",max_num_batched_tokens管"一批有多重"。
6. PPO 训练层:决定样本能否训得动
rollout 成功生成后,样本还需要进入 actor / critic 的 PPO 更新阶段。
此时问题已经从"能不能生成"变成了"能不能训练"。
训练阶段更关注的是:
- 前向与反向传播
- 激活保存
- 优化器状态
- 单卡显存压力
因此,这一层的核心是 单卡训练 token 预算。
6.1 actor_rollout_ref.actor.ppo_max_token_len_per_gpu
该参数表示:
actor 在 PPO 训练阶段,单张 GPU 一次允许承载的最大 token 数。
它控制的是 训练时单卡预算,而不是 rollout 长度。
换句话说,它回答的是:
actor 单卡一次 PPO update 最多能吃下多少 token?
如果这个值过大,就可能出现:
- rollout 正常
- 一进入 PPO 更新就 OOM
6.2 critic.ppo_max_token_len_per_gpu
该参数的含义与 actor 对应,只是作用对象变成了 critic:
critic 在 PPO 训练阶段,单张 GPU 一次允许承载的最大 token 数。
之所以要单独配置,是因为 actor 与 critic 的显存压力不一定完全一致。
7. 参数之间的核心关系
从整体链路看,这些参数之间的关系可以浓缩为四条。
7.1 样本准入关系
prompt 首先要满足:
- 不超过
data.max_prompt_length - 或者经过
filter_overlong_prompts/truncation处理后变为合法长度
7.2 Rollout 单样本长度关系
进入 Rollout 后,单条样本需要满足:
实际 prompt 长度+生成长度≤rollout.max_model_len \text{实际 prompt 长度} + \text{生成长度} \le \text{rollout.max\_model\_len} 实际 prompt 长度+生成长度≤rollout.max_model_len
其中:
生成长度≤data.max_response_length \text{生成长度} \le \text{data.max\_response\_length} 生成长度≤data.max_response_length
因此,一个更实用的配置思路是:
rollout.max_model_len≥data.max_prompt_length+data.max_response_length+模板冗余 \text{rollout.max\_model\_len} \ge \text{data.max\_prompt\_length} + \text{data.max\_response\_length} + \text{模板冗余} rollout.max_model_len≥data.max_prompt_length+data.max_response_length+模板冗余
7.3 Rollout 批级预算关系
在批处理层面,还需要满足:
∑tokens in rollout batch≤rollout.max_num_batched_tokens \sum \text{tokens in rollout batch} \le \text{rollout.max\_num\_batched\_tokens} ∑tokens in rollout batch≤rollout.max_num_batched_tokens
7.4 PPO 单卡预算关系
进入训练后,还需满足:
∑tokens on one GPU during PPO≤actor/critic.ppo_max_token_len_per_gpu \sum \text{tokens on one GPU during PPO} \le \text{actor/critic.ppo\_max\_token\_len\_per\_gpu} ∑tokens on one GPU during PPO≤actor/critic.ppo_max_token_len_per_gpu
因此,系统里其实同时存在两类"token 总量上限":
- rollout 阶段的 批级总量上限
- PPO 阶段的 单卡训练总量上限
它们名字相似,但作用阶段完全不同。
8. 最容易搞混的参数对照
这是最值得单独记住的一张表:
| 参数 A | 参数 B | 区别 |
|---|---|---|
data.max_prompt_length |
rollout.max_model_len |
前者只限制 prompt,后者限制 prompt + response 总长度 |
data.max_response_length |
rollout.max_model_len |
前者控制新生成长度,后者控制总上下文窗口 |
rollout.max_model_len |
rollout.max_num_batched_tokens |
前者管单条样本,后者管整批 token 总量 |
rollout.max_num_batched_tokens |
ppo_max_token_len_per_gpu |
前者管 rollout 批预算,后者管训练单卡预算 |
filter_overlong_prompts |
truncation |
前者决定删不删,后者决定怎么裁 |
如果只想记一句最简洁的话:
max_*_length更偏"单条长度上限",*_token_len_per_gpu/max_num_batched_tokens更偏"总量预算"。
9. 推荐的配置思路
实际调参时,建议按下面顺序思考,而不是一上来就盯着某一个参数。
9.1 先定任务尺度
先确定:
data.max_prompt_lengthdata.max_response_length
也就是先回答:任务需要多长输入、最多多长输出。
9.2 再定 Rollout 窗口
确保:
rollout.max_model_len \text{rollout.max\_model\_len} rollout.max_model_len
能够容纳:
实际 prompt+max response \text{实际 prompt} + \text{max response} 实际 prompt+max response
并给模板留出余量。
9.3 再定 Rollout 批预算
根据显存与吞吐设置:
actor_rollout_ref.rollout.max_num_batched_tokens
如果 rollout 阶段 OOM 或吞吐异常低,这里通常是重点排查项。
9.4 最后调 PPO 单卡预算
根据 actor / critic 显存情况设置:
actor_rollout_ref.actor.ppo_max_token_len_per_gpucritic.ppo_max_token_len_per_gpu
如果现象是 rollout 正常但更新阶段 OOM,通常优先检查这里。
10. 几条实用经验
10.1 max_model_len 不要卡得太死
理论上:
max_model_len=max_prompt_length+max_response_length \text{max\_model\_len} = \text{max\_prompt\_length} + \text{max\_response\_length} max_model_len=max_prompt_length+max_response_length
似乎就够,但实际经常不够,因为模板会额外消耗 token。
10.2 rollout 能跑,不代表 PPO 能跑
rollout 主要受:
max_model_lenmax_num_batched_tokens
约束;而 PPO 更新主要受:
actor.ppo_max_token_len_per_gpucritic.ppo_max_token_len_per_gpu
约束。
两种 OOM 本质上不是同一个问题。
10.3 data.max_response_length 的影响比看上去更大
虽然它写在 data.* 下,但它实际上会一路影响:
- 生成长度
- 样本总长度
- rollout 显存
- PPO 训练预算
它是贯穿整条链路的重要参数。
11. 总结
verl 中的序列长度相关参数,表面上都在控制 token 长度,但实际上分属三层:
- 数据层:样本是否进入系统
- Rollout 层:样本是否能被模型生成
- 训练层:样本是否能被稳定训练
其中最核心的几项可以这样记:
data.max_prompt_length:prompt 最长多长data.max_response_length:最多生成多长rollout.max_model_len:单条样本总上下文最多多长rollout.max_num_batched_tokens:rollout 一批最多处理多少 tokenactor/critic.ppo_max_token_len_per_gpu:训练时单卡最多承载多少 token
真正理解这些参数的关键,不是单独看名字,而是顺着完整链路去看:
先看样本准入,再看单样本窗口,再看 rollout 批预算,最后看 PPO 单卡训练预算。