verl 多模态 RL 训练实战指南:基于 Geo3K 的 GRPO 图像推理训练全流程
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
verl(HybridFlow)现已原生支持多模态强化学习训练,可对视觉语言模型(VLM)执行 GRPO/PPO 等后训练任务。本文以 Geo3K 几何推理数据集 + Qwen2.5-VL-7B-Instruct 为例,完整讲解从数据集预处理、模型下载到 FSDP × vLLM/SGLang 多模态 GRPO 训练的三个核心步骤,并结合仓库源码与脚本深入拆解多模态数据格式、关键训练参数及其底层实现,帮助读者在 NVIDIA GPU 与 Ascend NPU 上快速复现多模态 RL 流水线。
多模态 RL 支持现状
根据 multi_modal_example.rst 的说明,verl 已支持多模态训练,目前可通过FSDP(actor 训练后端)+ vLLM/SGLang(rollout 推理后端)的组合启动多模态 RL 任务,Megatron 支持也已进入实现阶段。这意味着图像、视频等视觉输入可以与文本一起参与 RL 后训练——模型不仅要在<think>推理过程中给出逐步推理,还要把最终答案放入\boxed{}中,由规则奖励函数进行评判。
仓库中与多模态训练直接相关的配套资源包括:
| 用途 | 文件 |
|---|---|
| 数据集预处理 | geo3k.py、openr1mm.py、tinyllava_video_r1.py |
| FSDP 训练脚本 | run_qwen2_5_vl_7b_fsdp.sh、run_qwen3_vl_8b_fsdp.sh |
| Megatron 训练脚本 | run_qwen2_5_vl_7b_megatron.sh |
| 多模态数据加载 | rl_dataset.py(image_key/video_key与视觉信息处理) |
| 训练入口 | main_ppo.py |
快速开始:三步启动多模态 GRPO 训练
Step 1:准备数据集(Geo3K 预处理)
运行仓库提供的预处理脚本,将 Hugging Face 上的hiyouga/geometry3k数据集转换为 verl 标准 parquet 格式:
# it will be saved in the $HOME/data/geo3k folder python examples/data_preprocess/geo3k.py从源码看,该脚本的核心逻辑在 geo3k.py 中完成:
- 数据源:默认从
hiyouga/geometry3k加载 train/test 两个 split;若已存在本地原始数据,可通过--local_dataset_path直接指定本地路径加载,避免重复下载。 - 指令注入:为每个问题拼接统一的推理指令模板——要求模型先以内部独白形式思考,推理过程必须放在
<think> </think>标签内,最终答案必须放入\boxed{}中。 - 字段重组:将原始
problem、answer、images重组为 verl 的 RL 数据集 schema,其中prompt是包含 role/content 的对话结构,images存放原始图像字节,reward_model标注为 rule 风格、以ground_truth为评分依据。 - 并行加速:使用
num_proc=8并行执行 map,最终输出train.parquet与test.parquet。 - 可选 HDFS 导出:传入
--hdfs_dir时会将本地 parquet 拷贝到 HDFS,适合大规模多机训练场景(底层使用 hdfs_io.py 的copy/makedirs工具)。
注意脚本参数:--local_dir已废弃,推荐使用--local_save_dir(默认~/data/geo3k)。
Step 2:下载模型
通过 transformers 的 pipeline 触发模型下载,将 Qwen2.5-VL-7B-Instruct 拉取到本地缓存:
# download the model from huggingface python3 -c "import transformers; transformers.pipeline(model='Qwen/Qwen2.5-VL-7B-Instruct')"之后即可在训练脚本中通过MODEL_PATH环境变量指向该模型(默认Qwen/Qwen2.5-VL-7B-Instruct,也支持替换为本地已下载的模型路径)。
Step 3:执行 GRPO 多模态训练
运行仓库自带的训练脚本,即会通过 Ray 拉起 actor(FSDP 训练)与 rollout(vLLM/SGLang 推理)等分布式角色,完成整个多模态 GRPO 训练流程:
# run the task bash examples/grpo_trainer/run_qwen2_5_vl_7b_fsdp.sh该脚本兼容 NVIDIA GPU 与 Ascend NPU(DEVICE通过探测torch_npu自动识别),推理后端通过INFER_BACKEND环境变量在vllm | sglang | trtllm之间切换,默认vllm。
训练脚本参数全解
run_qwen2_5_vl_7b_fsdp.sh 将全部配置组织为若干参数数组,最终以python3 -m verl.trainer.main_ppo的 Hydra 覆盖参数形式传入。以下按功能组拆解其默认值与含义:
数据组(DATA)
| 参数 | 默认值 | 说明 |
|---|---|---|
algorithm.adv_estimator | grpo | 优势估计器,本示例使用 GRPO |
algorithm.use_kl_in_reward | False | KL 项不并入奖励,由 actor 侧use_kl_loss单独控制 |
data.train_files/data.val_files | $HOME/data/geo3k/train.parquet/test.parquet | 训练/验证数据路径 |
data.image_key | images | 多模态样本中图像字段的 key,与预处理脚本输出的列名对应 |
data.train_batch_size | 512 | 每个训练 step 的样本数 |
data.max_prompt_length/data.max_response_length | 1024 / 2048 | prompt 与 response 的最大 token 长度 |
data.filter_overlong_prompts | True | 过滤超长 prompt 样本 |
data.truncation | 'error' | 截断策略;设为error表示超出长度直接报错而非静默截断 |
模型组(MODEL)
actor_rollout_ref.model.path:模型路径(可被MODEL_PATH覆盖);actor_rollout_ref.model.use_remove_padding=True:训练时去除 padding,与动态 batch 配合节省显存;actor_rollout_ref.model.enable_gradient_checkpointing=True:开启梯度检查点,用计算换显存,对 7B 级 VLM 尤为关键。
Actor 训练组(ACTOR)
actor_rollout_ref.actor.strategy=fsdp2(EXTRA 组):使用 FSDP2 作为 actor 分布式策略;actor_rollout_ref.actor.optim.lr=1e-6:学习率;actor_rollout_ref.actor.ppo_mini_batch_size=128:PPO mini-batch 大小;actor_rollout_ref.actor.use_dynamic_bsz=True与ppo_max_token_len_per_gpu=24576:按 token 而非样本数动态分配 batch,最大化显存利用率;actor_rollout_ref.actor.use_kl_loss=True、kl_loss_coef=0.01、kl_loss_type=low_var_kl:低方差 KL 惩罚,稳定策略更新;actor_rollout_ref.actor.fsdp_config.param_offload=False/optimizer_offload=False:GPU 上不 offload 参数与优化器(NPU 场景会在 EXTRA 中开启 offload)。
Rollout 推理组(ROLLOUT)
actor_rollout_ref.rollout.name=${INFER_BACKEND}:推理后端(vllm/sglang/trtllm);actor_rollout_ref.rollout.tensor_model_parallel_size=2:vLLM 的 TP 并行度;actor_rollout_ref.rollout.gpu_memory_utilization=0.6:推理引擎可用的显存比例;actor_rollout_ref.rollout.n=5:每个 prompt 采样的 rollout 条数(GRPO 依赖组内多条样本计算相对优势);log_prob_use_dynamic_bsz=True与log_prob_max_token_len_per_gpu:对 actor/ref 的 log-prob 计算同样启用动态 batch。
训练器组(TRAINER)
trainer.logger='["console","wandb"]':同时输出到控制台与 wandb;trainer.project_name=verl_grpo_geo3k、trainer.experiment_name=qwen2_5_vl_7b_${INFER_BACKEND}_fsdp:实验命名;trainer.n_gpus_per_node=8、trainer.nnodes=1:单机 8 卡(NPU 场景自动调整为 16);trainer.total_epochs=15、save_freq=20、test_freq=5:总轮数、checkpoint 保存频率与验证频率。
设备差异与启动方式
脚本针对 GPU/NPU 做了差异化配置:NPU 场景下会将mm_processor_cache_gb设为 0(不缓存视觉 processor,降低显存占用)、关闭 fused kernels 与多阶段唤醒、调低显存利用率至 0.5 并调整log_prob_micro_batch_size_per_gpu。启动时默认通过uv run --frozen --all-packages --extra <backend> --extra fsdp运行以匹配 uv.lock 中的依赖组合,若需使用系统 Python 可设置VERL_USE_UV=0。
多模态数据格式与底层处理
图像数据(Geo3K / OpenR1-MM)
预处理脚本产出的样本包含data_source、prompt、images、ability、reward_model、extra_info六个字段。其中prompt是标准对话结构:
"prompt": [ { "role": "user", "content": prompt, # 文本问题 + 推理指令 } ], "images": images, # 与图像占位符对应的图像字节 "reward_model": {"style": "rule", "ground_truth": answer},openr1mm.py 展示了另一种图像数据处理细节:对lmms-lab/multimodal-open-r1-8k-verified,图像以原始字节 dict({"bytes": ...})形式保留在 parquet 中,不做解码、不做 resize——Qwen 视觉 processor 会在运行时按需缩放,既避免有损重编码,也让 RL 阶段的动态 batch 处理更高效。该脚本还会以seed=42做 90/10 的 train/test 切分并只保留 6 个目标列。
视频数据(TinyLLaVA-Video-R1)
tinyllava_video_r1.py 展示了 verl 对视频输入的支持方式:prompt 中使用<video>占位符,videos字段记录视频绝对路径,并附带采样参数:
video_entry = {"video": video_path} video_entry["fps"] = video_fps # 默认 1,视频采样帧率 video_entry["max_frames"] = video_max_frames # 默认 32,最大帧数该脚本同时演示了完整的视频数据集准备流程(下载 → 解压 → 预处理),并在 map 过程中对缺失视频文件给出 WARN 提示。
数据集加载源码视角
多模态数据在训练时由 rl_dataset.py 中的RLHFDataset处理:
- 字段名通过
image_key(默认images)与video_key(默认videos)配置读取,与训练脚本中data.image_key=images一一对应; - 样本进入训练前调用
_process_multi_modal_info统一抽取图像/视频/音频,并通过process_vision_info(内部在线程池中执行)交给视觉 processor 完成 tokenize 与张量化; - 在处理对话式多模态输入时,会依据
<image>/<video>占位符与多模态内容的顺序对应关系维护image_offset/video_offset游标,确保每个占位符替换为正确的视觉 token,并在处理结束后断言所有视觉内容都被消费(image_offset == len(images)),防止占位符与资源错位; - 处理完成后从原始行中移除
image_key/video_key列,避免视觉原始数据混入后续特征字典。
进阶扩展:Qwen3-VL 与 Megatron 后端
Qwen3-VL 基线(GPU/NPU 通用)
run_qwen3_vl_8b_fsdp.sh 是仓库中 Qwen3-VL-8B 在 Geo3K 上的标准基线脚本,其整体结构与此前脚本一致,差异点包括:
- 模型默认
Qwen/Qwen3-VL-8B-Instruct,实验名自动附加时间戳; - 训练/验证数据路径通过
TRAIN_FILE/TEST_FILE单独可配; - NPU 分支额外导出
HCCL_CONNECT_TIMEOUT、HCCL_HOST_SOCKET_PORT_RANGE等集合通信环境变量,并设置RAY_EXPERIMENTAL_NOSET_ASCEND_RT_VISIBLE_DEVICES=1; - NPU 场景可通过
SP_SIZE配置 Ulysses 序列并行,GPU 场景则开启 fused kernels 与free_cache_engine(释放推理缓存引擎以降低显存碎片)。
Megatron 训练后端
run_qwen2_5_vl_7b_megatron.sh 展示了多模态 + Megatron 的组合:通过model_engine=megatron切换训练后端,并新增actor_rollout_ref.actor.megatron.*系列参数:
tensor_model_parallel_size=2、pipeline_model_parallel_size=2:Megatron 的张量/流水线并行度;use_mbridge=True:启用 Megatron 与推理引擎之间的通信桥(megatron bridge),负责 actor 权重与 rollout 后端之间的高效同步;- ref 策略模型配置同样的 TP/PP 与
use_mbridge,保证 rollout 阶段的 log-prob 计算与 actor 并行方案一致。
该脚本要求启动时使用--extra vllm --extra megatron的依赖组合(uv.lock 中对应 extra)。
常见问题与调优提示
- 显存不足:优先检查
data.filter_overlong_prompts、enable_gradient_checkpointing、use_remove_padding,并确认rollout.gpu_memory_utilization未超过实际空闲显存;NPU 场景建议跟随脚本默认值(0.5)并开启 actor/ref 的参数 offload。 - 动态 batch 报错:
ppo_max_token_len_per_gpu需根据单卡显存与 VLM 视觉 token 开销调整,图像会使单条 prompt 的实际 token 数显著大于纯文本估计。 - 视觉 token 对齐失败:若出现
image_offset >= len(images)之类的断言错误,说明样本中<image>占位符数量与images列表长度不匹配,请检查预处理脚本中占位符与图像列的对应关系。 - 多卡/多机扩展:设置
trainer.nnodes与trainer.n_gpus_per_node即可横向扩展;数据量大时可在预处理阶段通过--hdfs_dir将 parquet 上推到 HDFS 供所有节点读取。
小结
通过geo3k.py数据预处理、模型下载与run_qwen2_5_vl_7b_fsdp.sh训练脚本三个步骤,即可在 verl 中完成一次完整的视觉语言模型 GRPO 后训练。verl 的多模态支持统一了图像(images)与视频(videos)两类输入的处理路径,并以image_key/video_key字段解耦数据 schema 与训练逻辑;训练端既支持 FSDP2(GPU/NPU 通用),也支持 Megatron(TP/PP 并行),推理端可在 vLLM/SGLang/TRTLLM 间切换。对于想要深入多模态 RL 研究的开发者,上述源码与脚本是绝佳的起点。
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考