在学习 GRPO 等一系列后训练算法之后,我打算自己动手跑一遍后训练的流程,看看能得到什么样的结果,并加深对相关技术细节的理解。
我采用了 EasyR1 训练框架,该框架支持多模态的强化学习训练,因此选择 Qwen2.5-VL-3B 作为实验模型,并使用 Geometry3K 数据集进行训练。
我自己使用的实验仓库对原来的 EasyR1 框架进行了一些调整,以更好地适配集群的配置,不过并没有修改算法实现。
有关环境的配置的过程可以参考我的另一篇博客,本文主要说明训练的预设、过程,并对训练的结果进行分析,训练的可视化结果和详细信息可见 WandB。
训练预设
基于 EasyR1 框架,在 4 张 RTX 3090 24GB 上进行 BF16 全参数训练,数据集为 Geometry3K,输入为几何题文本和图片,模型按照 R1V 模板输出 <think> 推理和 <answer> 答案,奖励由格式与准确率等权组成。
训练采用 GRPO 优化算法,具体的参数设置如下表:
| 参数含义 | EasyR1 默认值 | 本实验设置 |
|---|---|---|
| 模型 | Qwen/Qwen2.5-VL-3B-Instruct | Qwen/Qwen2.5-VL-3B-Instruct |
| 训练数据集 | hiyouga/geometry3k@train | hiyouga/geometry3k@train |
| 验证数据集 | hiyouga/geometry3k@test | hiyouga/geometry3k@test |
| 强化学习算法 | GRPO | GRPO |
| 数值精度 (Bits) | AMP(adamw) | BF16(adamw_bf16、bf16) |
| GPU 数量 | 2 | 4 |
| Rollout batch size (每 step 的问题数) | 512 | 32 |
| 每个问题生成的回答数 | 5 | 4 |
| Actor minibatch size (每次更新的问题数) | 128 | 16 |
| 最大 prompt token 数 | 2048 | 2048 |
| 最大 response token 数 | 2048 | 512 |
| 单张图像最大像素数 | 4194304 | 524288 |
| vLLM 可使用的 GPU 显存比例 | 0.6 | 0.5 |
| vLLM tensor parallel 数 | 1 | 4 |
| Actor 学习率 | 1e-6 | 1e-6 |
| KL loss 系数 | 1e-2 | 1e-2 |
| LoRA rank | 0 | 0 |
| 每批 rollout 的策略更新轮数 | 1 | 1 |
| 训练 epoch 数 | 15 | 15,实际在 Step 195 停止 |
| 验证间隔 | 5 step | 5 step |
| checkpoint 保存间隔 | 5 step | 5 step |
| 验证时记录的生成样例数 | 3 | 8 |
训练期间单卡最高分配显存约 13.4GB,最高保留显存约 18.8GB,没有发生 CUDA OOM,说明 4 张 RTX 3090 能够承载这组参数。
训练过程
Geometry3K 过滤过长样本后,每个 epoch 为 65 steps,因此原计划的 15 epoch 约为 975 steps,预计训练用时在 50 小时左右。
首次训练运行到 Step 160,约为 2.46 epoch,此时多个 Ray worker 进入不可中断的 D 状态,nvidia-smi 无法返回,系统日志持续出现:
NVRM: Xid 74, NVLink MINION fatal error训练日志中没有 OOM、NCCL timeout 或 Python traceback,Step 160 的验证和 checkpoint 也已完成,因此中断原因是集群 GPU/NVLink 的底层故障,而不是训练参数或代码错误。
之后管理员说明该集群开机运行一段时间后存在 GPU 掉卡问题,考虑到继续运行到 Step 975 很可能再次中断,本次实验将目标缩短为 3 epoch,即 Step 195。
节点恢复后,从 global_step_160 继续训练。checkpoint 包含模型、优化器、额外训练状态和 DataLoader 状态,因此能够恢复完整训练进度。
续训不改变参数,只增加 checkpoint 路径,并使用 trainer.max_steps=195 限定停止位置:
export WANDB_ENTITY=<wandb-entity>export WANDB_RUN_ID=<wandb-run-id>export WANDB_RESUME="must"
RUN_DIR="/data/<username>/Qwen2.5-VL-3B-GRPO-Reproduction-with-EasyR1/checkpoints/Qwen2.5-VL-3B GRPO Reproduction with EasyR1/qwen2_5_vl_3b_geo_grpo_3090_full"CKPT="$RUN_DIR/global_step_160"MODEL_DIR="/data/<username>/models/Qwen2.5-VL-3B-Instruct"WANDB_PROJECT_NAME="Qwen2.5-VL-3B GRPO Reproduction with EasyR1"
CUDA_VISIBLE_DEVICES=0,1,2,3 \MODEL_PATH="$MODEL_DIR" \N_GPUS=4 \ROLLOUT_BATCH_SIZE=32 \GLOBAL_BATCH_SIZE=16 \MICRO_BATCH_SIZE_UPDATE=1 \MICRO_BATCH_SIZE_EXPERIENCE=1 \ROLLOUT_N=4 \MAX_PROMPT_LENGTH=2048 \MAX_RESPONSE_LENGTH=512 \MAX_PIXELS=524288 \GPU_MEMORY_UTILIZATION=0.5 \LEARNING_RATE=1e-6 \KL_COEF=1e-2 \LORA_RANK=0 \TOTAL_EPOCHS=15 \VAL_FREQ=5 \SAVE_FREQ=5 \EXPERIMENT_NAME=qwen2_5_vl_3b_geo_grpo_3090_full \LOGGER="['file','wandb']" \bash examples/qwen2_5_vl_3b_geo3k_grpo.sh \trainer.project_name="'$WANDB_PROJECT_NAME'" \trainer.load_checkpoint_path="$CKPT" \trainer.max_steps=195TOTAL_EPOCHS=15 用于保持原始配置,实际停止位置由优先级更高的 trainer.max_steps=195 控制。续训继续写入原 W&B run,并最终正常完成 Step 195。最终 checkpoint 大小约 29GB,说明模型和训练状态均已成功保存。
结果分析
整体效果
根据验证奖励曲线,验证准确率从 Step 0 的 3.83% 提升至最高 34.44%,最终稳定在 32.45%,表明 GRPO 训练显著提升了模型在 Geometry3K 上的表现。
需要说明的是,Step 0 的准确率并不能直接反映基础模型的真实能力。本实验修改了输出模板,并调整了最大生成长度和图像像素上限等配置,因此其初始结果与其他实验不具备严格可比性。
从训练曲线来看,format reward 在训练初期便快速提升至较高水平,说明模型较快适应了新的输出要求。因此,较低的初始准确率可能与训练和评测配置相较于原始模型指令微调阶段存在较大差异有关,模型需要一定训练过程来适应新的生成约束和任务形式。
作为参考,EasyR1 官方 Baseline 中,Qwen2.5-VL-3B-Instruct 在默认配置下的初始准确率约为 24%,经过 GRPO 训练后达到约 38%。
由于训练步数、生成长度、图像分辨率等设置与官方实验存在差异,无法进行严格横向比较。但本实验在仅训练约 3 个 epoch、图像像素上限约为官方默认配置 1/8 的条件下,仍取得了 34.44% 的最高验证准确率,从结果量级来看与官方报告处于相近水平,说明本次实验复现了 GRPO 在 Geometry3K 上的性能提升趋势,并验证了多模态 RL 后训练流程的有效性。
训练奖励仅反映当前随机 batch 的表现,容易受到样本难度和生成随机性的影响;验证奖励基于固定验证集计算,更能稳定反映模型整体能力。因此分析主要以验证奖励为依据。
训练阶段
根据验证奖励的折线图,可以将训练分为以下阶段:
- Step 0–20:format reward 从接近 0 快速提升至 96% 以上,而 accuracy reward 一度降至 1% 以下,说明这一阶段模型主要在学习目标输出格式。
- Step 25–120:format reward 基本稳定在 96% 以上,接近饱和,accuracy reward 从 13.14% 提升至 32.78%,表明训练重点逐渐从格式遵循转向几何理解和答案正确性。
- Step 125–195:验证奖励的增长明显放缓,并在一定范围内波动,说明训练进入阶段性平台。验证准确率没有持续下降,因此目前没有明显的过拟合迹象。考虑到本实验仅训练了 3 个 epoch,后续继续训练仍有可能进一步提升模型能力。
训练稳定性
根据图中的指标,可以得到以下结论:
- PPO KL 较低:更新前后策略给出的 token 概率差异很小,说明每次参数更新幅度较保守。
- KL Loss 没有持续增长:训练后的模型没有不断远离初始参考模型。
- 平均梯度范数约为 0.72:大部分更新低于裁剪阈值 1.0;少量尖峰很快恢复,不属于梯度爆炸。
- Clip Fraction 接近 0:绝大多数概率变化处于 PPO 允许范围内,不需要裁剪。
- 策略梯度损失没有持续放大:pg_loss 在 -0.026~0.082 之间波动,没有持续放大或出现 NaN、Inf。由于每个 step 都会重新采样并计算优势,该指标不需要持续下降。
总体来看,各项指标虽存在正常波动,但均未出现持续恶化或异常值,说明训练过程整体稳定,没有发生梯度爆炸、策略更新失控或数值发散。与此同时,较低的 KL 和 Clip Fraction 也表明本次策略更新相对保守。
响应长度
响应平均长度整体呈先下降、后趋于稳定并略有回升的趋势:验证集从 Step 0 的 368.98 token 降至 Step 195 的 239.88 token,截断率也从 27.72% 大幅降至 2.35%,说明回答更精简且更少触及 512-token 上限。
生成样例
固定的8道验证样例中,Step 0 全部得 0 分,主要原因是模型尚未掌握 <think> 和 <answer> 格式;到 Step 25,8 道题均符合格式,其中 2 道答案正确;最后 Step 195则有 4 道正确、4 道仅获得格式分。
仔细检查 4 个正确答案,其中 3 个的过程清晰完整,弧长题虽然计算和答案正确,但额外引入了图中未给出的圆周角,推理存在不必要且缺乏依据的步骤。

训练效率与资源使用
普通训练 step 平均耗时约 118.9 秒,其中 Actor 更新约 62.1 秒,是主要计算开销。普通 step 的单卡吞吐量约为 190 tokens/s;执行验证时,单次验证平均耗时约 245.5 秒,导致该 step 的总耗时增至约 387.1 秒,统计吞吐量降至约 58 tokens/s。
训练期间单卡实际峰值显存约 13.45 GB,PyTorch 预留峰值显存约 18.87 GB,均未超过 RTX 3090 的 24 GB 显存,且全程没有发生 CUDA OOM。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






