verl 中的 SPPO 自博弈偏好优化配方:从算法原理到 MATH 复现实践
2026/9/13 23:01:44 网站建设 项目流程

verl 中的 SPPO 自博弈偏好优化配方:从算法原理到 MATH 复现实践

【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl

Self-Play Preference Optimization(SPPO)是一种不依赖 GPT-4 等强外部信号、仅靠模型与自身历史策略对弈即可完成对齐的偏好优化方法。本文基于 verl 官方社区配方文档 docs/algo/sppo.md,完整讲解 SPPO 的核心思想、在 verl 框架中的复现步骤(含 MATH 数据集预处理、依赖安装、训练脚本执行),并结合仓库源码(数据脚本、规则奖励、KL 控制与 PPO 配置)给出源码级佐证,帮助你从零复现"Qwen2.5-7B-Instruct 在 MATH 上从 46.6 分提升至 65.6 分"的完整实验。

SPPO 算法背景与核心思想

SPPO 对应论文Self-Play Preference Optimization for Language Model Alignment(arXiv: 2405.00675),由 Yue Wu、Zhiqing Sun、Huizhuo Yuan 等学者提出。verl 在 docs/algo/sppo.md 中提供了该论文的社区配方(recipe)实现,实现作者为 Yuhao Yang 与 Chenyang Zhao。

该方法的核心动机是:传统对齐方法(如基于 GPT-4 生成偏好对或响应的 RLHF/DPO 流程)依赖昂贵且不稳定的外部信号。SPPO 则通过**自博弈(self-play)**机制,让当前策略与自身(或其历史版本)生成的数据进行偏好优化,从而在不借助强外部模型的情况下显著提升 LLM 性能。文档明确指出,SPPO 可以超越采用迭代式直接偏好优化(iterative DPO)训练的模型。

SPPO 具备两个层面的保证:

  • 理论层面:算法有理论根基,能够保证 LLM 在一般的、甚至不可传递(intransitive)的偏好关系下,收敛到 von Neumann winner,即纳什均衡(Nash equilibrium)意义上的最优策略;
  • 实证层面:论文通过多个数据集的广泛评测验证了有效性,verl 配方则进一步在 MATH 数据集上给出了可复现的工程验证。

verl 中的 SPPO 配方:定位与组织

在 verl 仓库中,算法配方文档统一组织在 docs/algo/ 目录下(如 ppo.md、dpo_extension.rst、sppo.md、spin.md 等),并在 docs/index.rst 中登记索引。项目 README 也将 SPPO 列为 LLM 对齐配方之一(见 README.md)。

需要说明的是:SPPO 的训练脚本(即文档中调用的recipe/sppo/run_qwen2.5-7b_rm.sh)由 verl-recipe 社区配方仓库维护,当前仓库的recipe/目录下未内置该脚本;本文后续章节将给出完整的复现命令与每一步的仓库内依据,确保即便脚本不在当前仓库,你也能按照 verl 的标准 PPO 训练流程跑通实验。

环境准备:安装 verl 与 SGLang 依赖

SPPO 实验基于 verl 的标准训练框架,官方推荐的安装方式如下:

git clone git@github.com:verl-project/verl.git cd verl python3 -m uv pip install -e ".[sglang]"

其中-e表示可编辑安装,[sglang]表示安装 SGLang 推理后端对应的 extras 依赖。从仓库的 setup.py 可以看到,sglangextras 具体包含:

  • sglang[srt,openai]==0.5.8:SGLang 推理服务端与 OpenAI 兼容接口;
  • torch==2.9.1:与 SGLang 配套的 PyTorch 版本;
  • tensordict>=0.8.0,<=0.10.0,!=0.9.0:verl 数据传输所需的张量字典库。

setup.py中还定义了gpu(liger-kernel、flash-attn)、vllmmath(math-verify)、test等可选依赖组,SPPO 实验按文档使用[sglang]即可。

flash-attn 安装失败的兜底方案

文档特别提示:安装过程偶尔会失败在 flash-attn 上。若遇到该情况,可手动分步安装:

python3 -m uv pip install wheel python3 -m uv pip install packaging python3 -m uv pip install flash-attn --no-build-isolation --no-deps

--no-build-isolation表示复用当前环境已安装的 wheel/packaging 进行构建,--no-deps跳过依赖自动解析,从而绕开常见的构建环境问题。

数据准备:预处理 MATH 数据集

实验使用 MATH 数据集(对应 HuggingFace 上的DigitalLearningGmbH/MATH-lighteval镜像仓库),预处理脚本为仓库内的 examples/data_preprocess/math_dataset.py:

python3 examples/data_preprocess/math_dataset.py --local_dir ~/data/math

从脚本源码(examples/data_preprocess/math_dataset.py)可以看到其核心逻辑:

  1. 加载数据源:默认从 HuggingFace 加载DigitalLearningGmbH/MATH-lighteval,也可通过--local_dataset_path指向本地原始数据;
  2. 构造指令:在每个 problem 后拼接指令Let's think step by step and output the final answer within \boxed{}.,引导模型逐步思考并以\boxed{}输出最终答案;
  3. 提取标准答案:通过last_boxed_only_stringremove_boxed(来自 verl/utils/reward_score/math_reward.py)从官方 solution 中提取\boxed{}内的 ground truth;
  4. 组织数据字段:每条数据包含data_sourceprompt(OpenAI 风格的{"role": "user", "content": ...})、ability: "math"reward_model: {"style": "rule", "ground_truth": solution}以及extra_info,其中reward_model字段声明了该任务使用规则奖励(rule-based reward)
  5. 输出格式:在--local_dir指定目录下生成train.parquettest.parquet,并额外保存train_example.jsontest_example.json各一条样本便于检查;若传入--hdfs_dir还会同步到 HDFS。

对应的规则奖励实现在 verl/utils/reward_score/math_reward.py:compute_score从模型输出中提取最后一个\boxed{}内的答案,经字符串规范化(is_equiv/strip_string)与 ground truth 比对,相等得 1.0,否则为 0.0。这套"抽取 boxed 答案 + 规范化比对"的机制,正是 MATH 上 SPPO 训练奖励信号的来源。

下载基座模型并运行训练

下载 Qwen2.5-7B-Instruct

hf download Qwen/Qwen2.5-7B-Instruct --local-dir $HOME/models/Qwen2.5-7B-Instruct

使用 HuggingFace CLI 的hf download将基座模型下载到本地$HOME/models/Qwen2.5-7B-Instruct

运行 SPPO 训练脚本

export WANDB_API_KEY=<YOUR_WANDB_API_KEY> export CUDA_VISIBLE_DEVICES=0,1,2,3 bash recipe/sppo/run_qwen2.5-7b_rm.sh
  • WANDB_API_KEY:实验跟踪(wandb)所需,verl 默认 logger 包含consolewandb(见 verl/trainer/config/ppo_trainer.yaml),不配置该环境变量会导致 wandb 上报失败;
  • CUDA_VISIBLE_DEVICES=0,1,2,3:指定 4 张 GPU 用于训练,对应 verl 的 Ray worker 资源分配;
  • run_qwen2.5-7b_rm.sh:SPPO 社区配方脚本,内部会以 verl 的标准 PPO 训练入口(verl/trainer/main_ppo.py)启动 20 个 epoch 的 SPPO 训练,并启用参考模型(ref)以计算 KL 惩罚。

训练入口与配置落点

从 verl 的 PPO 训练配置 verl/trainer/config/ppo_trainer.yaml 可以看出,这类配方脚本实际控制的关键配置包括:

  • trainer.total_epochs:训练轮数,SPPO 实验为 20 轮;
  • trainer.project_name/trainer.experiment_name:实验跟踪与 checkpoint 目录命名(默认落盘到checkpoints/${trainer.project_name}/${trainer.experiment_name});
  • actor_rollout_ref.hybrid_engine: true:actor 与 rollout 共享引擎,是 verl 默认的高效混合引擎模式;
  • algorithm.gamma: 1.0algorithm.lam: 1.0:GAE 折扣与 bias-variance 权衡参数,SPPO 这类 token 级奖励场景通常取 1.0(配置类定义见 verl/trainer/config/algorithm.py)。

实验结果:MATH 上的 20 轮提升

文档给出的核心实验结果是:

  • 起点:Qwen2.5-7B-Instruct 初始 MATH 得分46.6
  • 终点:经过20 个 epoch的 SPPO 训练后得分65.6
  • 该成绩使模型大约跻身 [MATH leaderboard] 前 20 名。

文档同时给出了重要的评估口径说明:verl 内部的评估指标可能与 Qwen2.5-7B-Instruct 的官方评测方法不完全一致,因此为了口径统一与公平比较,只报告基于 verl 评估框架的结果。这意味着复现时若与你看到的第三方基准分数存在偏差,属于评估方法差异,而非训练失效。

结合仓库源码,这一评估口径的实现依据在于:verl 的 MATH 规则奖励(verl/utils/reward_score/math_reward.py)与 EleutherAI lm-evaluation-harness 的 hendrycks_math 任务同源(文件头部注明了改编来源),但具体字符串规范化、\boxed{}解析策略与官方评测管道仍有细节差异,这正是文档提醒"内部评估指标可能不完全对齐"的原因。

源码级原理支撑:SPPO 训练框架中的 KL 控制与奖励机制

SPPO 属于偏好优化类算法,其 verl 实现运行在通用 PPO 训练框架之上,理解框架的 KL 惩罚与奖励注入机制,有助于深入理解训练行为。

参考模型与 in-reward KL 惩罚

verl 的 PPO 配置支持两种 KL 惩罚路径:actor loss 内置的use_kl_loss,以及将 KL 直接计入 token 级奖励的use_kl_in_reward(配置项见 verl/trainer/config/ppo_trainer.yaml,类型定义见 verl/trainer/config/algorithm.py)。当启用 in-reward KL 时,参考模型(ref)会被加载,训练流程调用 verl/trainer/ppo/ray_trainer.py 中的apply_kl_penalty,依据当前策略与参考策略的对数概率差计算 KL 估计并施加到 token 级奖励上——这正是 SPPO 这类"以自身为参照"对齐方法在工程上落地的关键机制。

KL 估计方式与自适应控制器

algorithm.kl_penalty决定 KL 的估计方式,verl 支持kl(默认)、absmselow_var_klfull等选项,其具体数学形式实现在 verl/trainer/ppo/core_algos.py 中:

  • kl/k1:直接取logprob - ref_logprob(正向 KL 估计);
  • abs:取绝对差值;
  • mse/k2:取0.5 * (logprob - ref_logprob)^2
  • low_var_kl/k3:Schulman 的低方差 KL 近似ratio - kl - 1,并对中间量做数值稳定截断;
  • full:需要逐 token 词汇表 logits,当前实现抛NotImplementedError

KL 系数既可固定(FixedKLController),也可使用自适应控制器(verl/trainer/ppo/core_algos.py):AdaptiveKLController依据current_kl / target_kl的比例误差(截断在 ±0.2)按horizon步长动态调整kl_coef。对应配置项为 verl/trainer/config/algorithm.py 中的KLControlConfigtypekl_coefhorizontarget_kl)。配方脚本可按需在algorithm.kl_ctrl下覆盖这些字段。

优势估计

SPPO 配方基于 token 级奖励,verl 通过algorithm.adv_estimator选择优势估计器(gaegrporeinforce_plus_plus等,见 verl/trainer/config/algorithm.py),配合gammalam完成 GAE 计算。从源码结构看,SPPO 与 DPO 类方法共享同一套 reward + advantage + policy update 流水线,区别主要在于训练数据由自博弈生成,这正体现了 verl "算法配方与训练框架解耦"的设计理念。

常见问题与注意事项

  • flash-attn 编译失败:按上文兜底方案先装wheelpackaging,再以--no-build-isolation --no-deps安装 flash-attn;
  • wandb 无法上报:确认WANDB_API_KEY已正确导出;若无需外部跟踪,可在配置中将trainer.logger收敛为["console"]
  • GPU 资源:文档示例使用 4 张 GPU(CUDA_VISIBLE_DEVICES=0,1,2,3),需要根据实际显存调整trainer.n_gpus_per_node与 batch 相关参数(train_batch_sizemax_prompt_lengthmax_response_length等,均在 verl/trainer/config/ppo_trainer.yaml 中可查);
  • 结果可比性:verl 内部评估口径与官方评测存在差异,对比第三方分数时需保持同一框架内比较;
  • 数据下载lighteval/MATH原仓库在 HuggingFace 已不可用,脚本已切换至DigitalLearningGmbH/MATH-lighteval镜像(见 examples/data_preprocess/math_dataset.py),无需手动处理。

总结

SPPO 为 LLM 对齐提供了一条不依赖 GPT-4 等强外部信号的路径:通过自博弈与理论上的纳什均衡收敛保证,在 MATH 等基准上取得了超越迭代 DPO 的效果。verl 社区配方完整覆盖了从数据预处理(examples/data_preprocess/math_dataset.py)、规则奖励(verl/utils/reward_score/math_reward.py)到训练框架(verl/trainer/config/ppo_trainer.yaml、verl/trainer/ppo/ray_trainer.py)的全链路,使得"Qwen2.5-7B-Instruct 在 MATH 上 46.6 → 65.6"的复现只需寥寥几条命令即可完成。若想进一步了解 SPPO 与 SPIN 等其他自博弈配方的异同,可继续阅读仓库中的 spin.md 与 baseline.md。

【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询