ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ms-swift GRPO 进阶:REAL 损失——把奖励当作标签的组内分类视角

ms-swift GRPO 进阶:REAL 损失——把奖励当作标签的组内分类视角 ms-swift GRPO 进阶REAL 损失——把奖励当作标签的组内分类视角【免费下载链接】swiftUse PEFT or Full-parameter to CPT/SFT/DPO/GRPO 600 LLMs (Qwen3.6, DeepSeek-V4, GLM-5.1, InternLM3, Llama4, ...) and 300 MLLMs (Qwen3-VL, Qwen3-Omni, InternVL3.5, Ovis2.5, GLM4.5v, Gemma4, Llava, Phi4, ...) (AAAI 2025).项目地址: https://gitcode.com/GitHub_Trending/swift1/swift在 RLVR可验证奖励强化学习训练中GRPO 的逐 token 梯度权重既可能给错了对象也可能被少数过度自信的负样本劫持。本文围绕 ms-swift 中loss_typereal的进阶研究实现讲清楚 REALRewards as Labels如何用组内分类替代 advantage 估计来重构 GRPO 优化目标先剖析 GRPO 梯度中梯度错配Gradient Misassignment与梯度主导Gradient Domination两个问题的数学来源再给出 REAL 的分类 logit、损失函数与有界梯度权重的推导最后结合仓库源码逐行印证其实现、参数配置--loss_type real/--real_tau、官方训练脚本 real.sh 以及必须满足的per_device_train_batch_size可被num_generations整除的约束条件。读完本文你可以直接在 ms-swift 的swift rlhf流程中启用 REAL 训练并理解其在源码层面与教师蒸馏、RLSD 等特性不兼容的原因。背景与动机GRPO 目标函数的两处梯度病GRPO 的目标函数与逐 token 梯度权重GRPO 的优化目标为$$ J_{\mathrm{GRPO}}(\theta)\mathbb{E}{q,o\sim\pi{\mathrm{od}}(\cdot|q)}\left[\frac{1}{|o|}\sum_{t1}^{|o|}\left(\min\left(\rho_tA_t,\mathrm{clip}(\rho_t,1-\epsilon,1\epsilon)A_t\right)\right)\right] $$其中$\rho_t \frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$ 为概率比probability ratio$A_t$ 为优势函数advantage function。对应的梯度为$$ \nabla_{\theta} J_{\mathrm{GRPO}} \mathbb { E } \left[ \frac { 1 } { | o | } \sum _ { t 1 } ^ { | o | } \mathbb { I } _ { \mathrm { clip } } \cdot A _ { t } e ^ { s _ { t } } \nabla _ { \theta } \log \pi _ { \theta } \left( o _ { t } | q \right) \right] $$其中$s_t \log \frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$ 为相对对数概率relative log-probability$\mathbb{I}_{\mathrm{clip}}$ 为裁剪指示函数。因此 GRPO 中每个 token 的梯度权重取绝对值为$$ |\mathcal{W}{\mathrm{GRPO}}|\left{ \begin{array} {ll}\left|A\cdot e^s\right|, \mathrm{if~}\mathbb{I}{\mathrm{clip}}1, \ 0, \text{otherwise.} \end{array}\right. $$可以看到梯度权重由 advantage 幅度 $A$ 与指数项 $e^s$ 共同决定后者正是问题的根源。问题一正样本的梯度错配Gradient Misassignment对正样本$A0$而言随着相对对数概率 $s$ 减小梯度幅值 $|A\cdot e^s|$ 也随之减小。这与直觉相悖模型当前把握较小$s$ 低但作答正确的 token本应获得更大的学习信号而 GRPO 反而把权重给了模型已经很自信的 token导致欠训练的 token 学习信号不足——这就是梯度错配。问题二负样本的梯度主导Gradient Domination对负样本$A0$而言随着 $s$ 减小$|A\cdot e^s|$ 并不减小而是指数级增大此时 $s$ 越负说明旧策略下该 token 概率被当前策略压得越低对应 $e^s$ 越大。结果是组内少数几个过度自信的坏 token主导了整个梯度淹没同组其他负样本的信号且该权重没有上界可能导致不稳定、过大的参数更新。从 ms-swift 源码结构看GRPO 的 token 级裁剪权重正体现在 grpo_trainer.py 中coef_1/coef_2与advantages的乘积项上即 $\rho \cdot A$ 的经典 PPO-clip 形式与上述公式一一对应。针对上述两个问题REAL 的思路是不再估计 advantage 再逐 token 加权而是把奖励直接当作分类标签做组内in-group分类训练。REAL 的数学形式化从 advantage 估计到组内分类分类 logit序列级相对对数概率REAL 为每个样本 $k$ 定义如下分类 logit$$ \bar{s}^k\frac{1}{|o^k|}\sum_{t1}^{|o^k|}\left(\log\frac{\pi_\theta(o_t^k\mid q)}{\pi_{\mathrm{old}}(o_t^k\mid q)}\right) $$即该样本在当前策略下相对旧策略的逐 token 对数概率差在序列上的平均值。它的符号具有明确语义$\bar{s}^k 0$该样本在当前策略下比在旧策略下更可能 → 模型倾向于提升promote该样本$\bar{s}^k 0$该样本在当前策略下更不可能 → 模型倾向于抑制suppress该样本。REAL 损失函数$$ \mathcal{L}{REAL}\log\left(1\sum{\mathcal{O}}e^{-\bar{s}^i/\tau}\right)\log\left(1\sum{\mathcal{O}_-}e^{\bar{s}^j/\tau}\right) $$其中 $\mathcal{O}$、$\mathcal{O}-$ 分别为组内奖励为正/负的样本集合$\tau$ 为温度参数。两项都是 softplus/logsumexp 型结构正样本项希望把 $\bar{s}^i$ 推正负样本项希望把 $\bar{s}^j$ 推负而常数 1 提供了无样本时损失为 0的自然基线。梯度性质单调且有界REAL 的梯度权重绝对值为$$ |\mathcal{W}{\mathrm{REAL}}| \begin{cases} \frac{1}{\tau}\frac{1}{1C{}e^{\bar{s}^{k}/\tau}}, r1 \ \ \frac{1}{\tau}\frac{1}{1C_{-}e^{-\bar{s}^{k}/\tau}}, r0 \end{cases} $$$r\in{0,1}$ 为样本奖励标签$C_\pm$ 为组内同类样本数相关的常数。由此带来两点关键改进正好对应 GRPO 的两个病灶有界性权重幅值上界为 $1/\tau$负样本不再能产生指数级放大的梯度消除了 Gradient Domination单调性对正样本$\bar{s}$ 越负模型当前越不自信但答案正确权重越大——学习信号被正确地分配给欠训练样本解决了 Gradient Misassignment。ms-swift 的参数文档 args_mixin.py 对该性质的描述与推导一致REAL induces monotonic and bounded gradient weighting with magnitude upper-bounded by 1/tau。参数配置参数类型默认值说明--loss_typestr-设置为real--real_taufloat0.5温度参数控制分类决策边界的锐度决策边界越锐权重区分度越强同时决定权重上界 $1/\tau$对应源码定义见 args_mixin.py# REAL https://arxiv.org/abs/2602.05630 real_tau: float 0.5除这两个显式参数外启用 REAL 还会触发一些隐式约束源码中已做强制处理奖励归一化被强制关闭。在 rlhf_args.py 中当loss_type real时scale_rewards会被强制置为none并打印警告因为 REAL 的分组正/负划分直接使用奖励的原始符号见后文group_rewards 0的判断组内标准化会破坏该语义与教师蒸馏OPD-RL互斥。real损失将 advantage 归约为序列级标量与教师逐 token 的 signed log-ratio 冲突rlhf_args.py 与 grpo_trainer.py 均会在参数校验和训练入口处直接抛出ValueError与 RLSD 优势重加权互斥。advantage_reweightrlsd同样不支持loss_typereal原因见 rlhf_args.py。源码实现走读REAL 损失在 grpo_trainer.py 中的落地以下按 grpo_trainer.py 的执行顺序逐段对照上面的数学公式。第一步计算序列级 logit $\bar{s}^k$L1096-1102global_scores (log_ratio * completion_mask).sum(-1) / completion_mask.sum(-1).clamp(min1.0) group_scores global_scores.view(-1, self.num_generations) seq_advantages (advantages * completion_mask).sum(-1) / completion_mask.sum(-1).clamp(min1.0) group_rewards seq_advantages.view(-1, self.num_generations)log_ratio per_token_logps - old_per_token_logps即逐 token 的 $\log(\pi_\theta/\pi_{\mathrm{old}})$按completion_mask做有效 token 平均后得到 $\bar{s}^k$随后通过view(-1, num_generations)把展平的 batch 重新切回每个 prompt 一组的形状——这正是组内分类的前提也解释了后文batch size 必须可被num_generations整除的约束从何而来。第二步按奖励符号划分正负标签并过滤无效组L1104-1109pos_mask (group_rewards 0) neg_mask (group_rewards 0) valid_mask (pos_mask.sum(dim1) ! 0) (neg_mask.sum(dim1) ! 0) if not valid_mask.any(): loss torch.tensor(0., deviceglobal_scores.device) * global_scores.mean()组内必须同时存在正、负样本才参与损失计算单侧组没有对比信号若当前 batch 所有组都无效则损失置 0保留计算图以便 backward。第三步用 logsumexp 实现 $\log(1\sum e^{\cdot/\tau})$L1111-1126scaled_scores batch_scores / self.real_tau zeros torch.zeros(batch_scores.size(0), 1, devicebatch_scores.device, dtypebatch_scores.dtype) # Negative Loss: log(1 sum(e^{S_neg})) neg_input scaled_scores.masked_fill(~batch_neg_mask, float(-inf)) neg_loss torch.logsumexp(torch.cat([neg_input, zeros], dim1), dim1) # Positive Loss: log(1 sum(e^{-S_pos})) pos_input (-scaled_scores).masked_fill(~batch_pos_mask, float(-inf)) pos_loss torch.logsumexp(torch.cat([pos_input, zeros], dim1), dim1) loss (neg_loss pos_loss).sum() / group_rewards.size(0)这里用一个巧妙的数值技巧实现公式中的常数项 1把每行拼一个 0 元素再取logsumexp$e^01$ 即成为和式中的1非目标类别用-inf掩掉$e^{-\infty}0$使 logsumexp 只累加对应类别。负样本项为 $\log(1\sum_{\mathcal O_-} e^{\bar s^j/\tau})$正样本项取 $-\bar s^i/\tau$ 后同理最终按组数group_rewards.size(0)归一。第四步KL 正则独立叠加L1128-1130if self.beta ! 0.0: kl_loss (per_token_kl * completion_mask).sum() / completion_mask.sum().clamp(min1.0) loss loss kl_loss * self.beta注意 REAL 与标准 GRPO 的一个重要结构差异由于分类损失是序列级的token 级策略损失在 grpo_trainer.py 中被直接置零per_token_loss torch.zeros_like(per_token_logps)KL 项不再混入 token 级 loss而是单独以序列级 token 平均的形式、乘以beta叠加到总损失上。这与 PPO/GRPO 分支中per_token_loss self.beta * per_token_kl的写法不同阅读指标日志时需注意kl的含义是叠加项而非权重内嵌项。另外REAL 分支不统计任何裁剪指标grpo_trainer.py 中elif self.loss_type in [sapo, real]: pass因为分类损失本身没有 clip 机制——这也是用有界权重替代裁剪这一设计思想在工程上的自然体现。训练脚本参考仓库提供的完整参考脚本为 examples/train/grpo/internal/real.sh采用 vLLM server 模式 rollout 双卡全参训练 Qwen3-1.7B 的数学任务核心片段如下完整脚本含 rollout 服务启动部分CUDA_VISIBLE_DEVICES2 \ swift rollout \ --model Qwen/Qwen3-1.7B NPROC_PER_NODE2 \ CUDA_VISIBLE_DEVICES0,1 \ swift rlhf \ --rlhf_type grpo \ --model Qwen/Qwen3-1.7B \ --dataset AI-MO/NuminaMath-TIR#5000 \ --enable_thinking false \ --reward_funcs accuracy \ --use_vllm true \ --vllm_mode server \ --vllm_server_host 127.0.0.1 \ --vllm_server_port 8000 \ --tuner_type full \ --torch_dtype bfloat16 \ --load_from_cache_file true \ --max_completion_length 4096 \ --num_train_epochs 1 \ --per_device_train_batch_size 8 \ --learning_rate 2e-6 \ --gradient_accumulation_steps 1 \ --save_total_limit 2 \ --save_steps 500 \ --logging_steps 1 \ --warmup_ratio 0.05 \ --dataloader_num_workers 4 \ --num_generations 8 \ --temperature 0.6 \ --system You are a helpful math assistant. Solve the problem step by step and put your final answer within \\boxed{}. \ --log_completions true \ --num_iterations 1 \ --beta 0.001 \ --loss_type real \ --deepspeed zero2几个与 REAL 直接相关的取值值得注意--loss_type real是开关配合默认--real_tau 0.5脚本未显式设置即使用默认值--num_generations 8与--per_device_train_batch_size 8恰好整除满足下述组完整性要求该脚本中--tuner_type full与多卡并行下每个训练 batch 内的样本数需能按 8 个一组完整切分--beta 0.001保留了一个较小的 KL 正则对应源码中叠加的beta * kl_loss项--reward_funcs accuracy提供环境奖励REAL 的正负标签划分依赖奖励符号因此必须配置 reward function对数学题即为答案正确性。重要注意事项与适用限制batch 必须包含完整组配置训练参数时必须保证per_device_train_batch_size能被num_generations整除。这是组内分类正确性的硬性前提源码中global_scores.view(-1, self.num_generations)依赖展平后的样本数 组数 × 每组生成数才能正确还原组结构grpo_trainer.py。若不能整除组边界会被错位切分正负标签与 logit 的对应关系全部错乱。单侧组被跳过如前述实现细节组内奖励全正或全负时该组不贡献损失valid_mask过滤。当奖励信号稀疏、大量组只出现单一符号时有效梯度会比标准 GRPO 少此时可考虑调高num_generations增大组内方差或检查奖励函数区分度。不兼容特性速查从源码校验逻辑rlhf_args.py、rlhf_args.py、grpo_trainer.py确认loss_typereal与以下特性互斥配置时会被直接拒绝--teacher_model/--teacher_model_serverOPD-RL 教师蒸馏--advantage_reweight rlsd任何依赖奖励组内归一化的设置scale_rewards强制为none。小结REAL 用奖励即标签的组内二分类替换了 GRPO 的 advantage 估计与逐 token 加权分类 logit $\bar{s}^k$ 只依赖新旧策略的序列级对数概率差损失函数 $\log(1\sum e^{\pm\bar s/\tau})$ 的梯度权重单调且上界为 $1/\tau$从数学上同时消除了正样本梯度错配与负样本梯度主导。在 ms-swift 中只需在swift rlhf --rlhf_type grpo基础上追加--loss_type real可选--real_tau默认 0.5并注意 batch 组完整性与上述互斥约束即可复现 examples/train/grpo/internal/real.sh 所示的完整训练流程实现细节可进一步对照 swift/rlhf_trainers/grpo_trainer.py 与 swift/rlhf_trainers/args_mixin.py 深入阅读。【免费下载链接】swiftUse PEFT or Full-parameter to CPT/SFT/DPO/GRPO 600 LLMs (Qwen3.6, DeepSeek-V4, GLM-5.1, InternLM3, Llama4, ...) and 300 MLLMs (Qwen3-VL, Qwen3-Omni, InternVL3.5, Ovis2.5, GLM4.5v, Gemma4, Llava, Phi4, ...) (AAAI 2025).项目地址: https://gitcode.com/GitHub_Trending/swift1/swift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表