尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
VeRL 框架 RL 微调大语言模型,token-level loss 聚合方式详解
考虑这个场景在 RL 微调大模型的过程中我们让模型生成了一批回答每个回答是一串文字然后我们给每个回答里的每个字token算了一个“损失值”可以理解为这个字写得“好不好”的评分损失越大说明越不好。同时我们还有一个“掩码”mask标记哪些字是有效的比如忽略填充的无意义字。此时为了最小化损失值我们需要把这堆零散的字级别的损失汇总成一个总的损失值然后用这个总损失去更新模型。这个汇总方式可以有很多种就像我们计算平均分可以用“所有字的总分除以字数”也可以用“每句话的平均分再平均”等等不同的汇总方式会影响模型学习的方向。VeRL 将这一部分封装为agg_loss函数以下为该函数的具体实现# 位于 verl/trainer/ppo/core_algos.pydefagg_loss(loss_mat:torch.Tensor,loss_mask:torch.Tensor,loss_agg_mode:str,dp_size:int1,batch_num_tokens:Optional[int]None,global_batch_size:Optional[int]None,loss_scale_factor:Optional[int]None,): Aggregate the loss across global batch to ensure the loss is invariant to fsdp/megatron parallelism. NOTE: The returned loss has different behaviors for different backend: - FSDP: the loss is directly used for backward. - Megatron: the loss should be scaled by num_microbatches and cp_size for pp schedule. Args: loss_mat: micro batch loss matrix, (bs, response_length) loss_mask: micro batch loss mask, (bs, response_length) loss_agg_mode: method to aggregate the loss matrix into a scalar dp_size: data parallel size batch_num_tokens: number of valid tokens in global batch global_batch_size: global batch size loss_scale_factor: scale factor for seq-mean-token-sum-norm mode. If None, uses loss_mask.shape[-1]. Set this to a constant value to ensure consistent normalization throughout training. Returns: loss: a scalar torch.Tensor aggregated loss ifloss_agg_modetoken-mean:ifbatch_num_tokensisNone:batch_num_tokensloss_mask.sum()lossverl_F.masked_sum(loss_mat,loss_mask)/batch_num_tokens*dp_sizeelifloss_agg_modeseq-mean-token-sum:seq_lossestorch.sum(loss_mat*loss_mask,dim-1)# token-sumseq_mask(torch.sum(loss_mask,dim-1)0).float()# exclude fully masked sequencesifglobal_batch_sizeisNone:global_batch_sizeseq_mask.sum()lossverl_F.masked_sum(seq_losses,seq_mask)/global_batch_size*dp_size# seq-meanelifloss_agg_modeseq-mean-token-mean:seq_masktorch.sum(loss_mask,dim-1)# per-sequence token countseq_lossestorch.sum(loss_mat*loss_mask,dim-1)/(seq_mask1e-8)# token-meanseq_mask(seq_mask0).float()# exclude fully masked sequencesifglobal_batch_sizeisNone:global_batch_sizeseq_mask.sum()lossverl_F.masked_sum(seq_losses,seq_mask)/global_batch_size*dp_size# seq-meanelifloss_agg_modeseq-mean-token-sum-norm:seq_lossestorch.sum(loss_mat*loss_mask,dim-1)ifloss_scale_factorisNone:loss_scale_factorloss_mask.shape[-1]losstorch.sum(seq_losses)/loss_scale_factorelse:raiseValueError(fInvalid loss_agg_mode:{loss_agg_mode})returnloss1 输入参数回顾loss_mat: 形状(bs, response_length)每个 token 的损失值例如策略梯度损失或 KL 散度。loss_mask: 相同形状标记哪些 token 是有效的1 表示有效0 表示 padding 或忽略。loss_agg_mode: 聚合方式可选值有token-mean,seq-mean-token-sum,seq-mean-token-mean,seq-mean-token-sum-norm。dp_size: 数据并行度分布式训练时使用的 GPU 数量用于将局部损失缩放为全局等价损失。batch_num_tokens: 全局批次中有效 token 的总数跨所有 GPU。如果未提供会在token-mean模式中自动计算局部和。global_batch_size: 全局批次中有效序列的数量。如果未提供会在某些模式中自动使用局部有效序列数。loss_scale_factor: 仅用于seq-mean-token-sum-norm模式作为分母的固定缩放因子。如果未提供默认使用loss_mask.shape[-1]即固定的响应长度。2 聚合模式详解2.1token-mean所有有效 token 的平均损失lossverl_F.masked_sum(loss_mat,loss_mask)/batch_num_tokens*dp_size直观理解把每个回答里所有有效字的损失加起来然后除以所有有效字的总数。最后得到一个“平均每个字有多差”的值。计算先计算所有有效 token 的损失总和masked_sum相当于(loss_mat * loss_mask).sum()然后除以有效 token 总数batch_num_tokens得到每个 token 的平均损失。最后乘以dp_size使结果相当于全局 token 平均损失在分布式训练中每个 GPU 只计算局部平均乘以dp_size是为了统一到全局视角。作用这是最直观的平均方式每个有效 token 对损失的贡献权重相等。verl 的 GRPO 默认使用此模式因为它简单稳定尤其适合长文本生成任务。2.2seq-mean-token-sum先对每个序列内 token 损失求和再对所有序列求平均seq_lossestorch.sum(loss_mat*loss_mask,dim-1)# 每个序列的总损失seq_mask(torch.sum(loss_mask,dim-1)0).float()# 哪些序列至少有一个有效 tokenlossverl_F.masked_sum(seq_losses,seq_mask)/global_batch_size*dp_size直观理解第一步对每个回答把里面所有有效字的损失加起来得到“这个回答的总损失”。第二步把这些回答的总损失加起来再除以回答的数量得到“平均每个回答的总损失”。把每个回答当成一个整体先看这个回答整句话写得有多差总损失然后看看所有回答的平均整句损失。这样一个很长的回答虽然字多但它的总损失可能会很大所以它对最终结果的影响也比较大。计算对每个序列将有效 token 的损失相加得到序列级总损失seq_losses。生成序列掩码seq_mask有效序列为 1全填充序列为 0。对有效序列的总损失求和除以全局序列数global_batch_size得到序列平均总损失。最后乘以dp_size。作用这种聚合方式将每个序列视为一个样本不论序列长度如何每个序列对损失的贡献权重相同。因此长序列的 token 级损失会被隐式地“压缩”成序列级损失可能更适合某些任务。2.3seq-mean-token-mean先对每个序列内 token 损失求平均再对所有序列求平均seq_masktorch.sum(loss_mask,dim-1)# 每个序列的有效 token 数seq_lossestorch.sum(loss_mat*loss_mask,dim-1)/(seq_mask1e-8)# 每个序列的平均损失seq_mask(seq_mask0).float()# 有效序列掩码lossverl_F.masked_sum(seq_losses,seq_mask)/global_batch_size*dp_size直观理解第一步对每个回答把里面所有有效字的损失加起来除以这个回答的有效字数得到“这个回答每个字的平均损失”。第二步把这些回答的平均字损失加起来除以回答的数量得到“平均每个回答的平均字损失”。先看每个回答里平均每个字写得有多差然后把这些平均值再平均。这样不管一个回答是长是短它对最终结果的贡献只取决于它内部字的平均质量而不是总损失。这样可以避免长句子因为字多而主导训练。计算计算每个序列的有效 token 数seq_mask即序列长度。对每个序列计算有效 token 损失的平均值seq_losses。对有效序列的平均损失求和除以全局序列数得到序列平均损失的平均。作用这种模式是原始 GRPO 论文中使用的聚合方式。它将每个序列的损失归一化到相同尺度每个序列的平均损失然后再平均。这样做的动机是让每个序列对梯度的贡献相等避免长序列因 token 多而主导梯度。但在长思维链场景中每个序列的平均损失可能波动较大导致训练不稳定因此 verl 默认未采用此模式。2.4seq-mean-token-sum-norm所有序列的总损失之和除以一个固定常数seq_lossestorch.sum(loss_mat*loss_mask,dim-1)# 每个序列的总损失ifloss_scale_factorisNone:loss_scale_factorloss_mask.shape[-1]# 默认使用固定的响应长度losstorch.sum(seq_losses)/loss_scale_factor直观理解第一步把每个回答的总损失算出来和模式2一样。第二步把所有回答的总损失加起来然后除以一个固定的数字比如你预设的最大回答长度或者直接使用固定的响应长度。这个固定数字在训练过程中不变。通俗理解把所有回答的“总损失”加起来然后除以一个固定的大数。这个固定的大数不随当前批次回答的长度变化所以最终损失的大小不受句子长短的影响。这样可以避免模型为了降低损失而故意把句子写得很长因为长句子总损失更大除以固定数后可能仍然大但不会因为短句子分母小而被放大。计算先计算每个序列的总损失然后对所有序列的总损失求和最后除以一个固定的缩放因子loss_scale_factor可以是一个常数如最大响应长度。注意这里没有乘以dp_size也没有使用global_batch_size。作用这是 DrGRPO 中引入的模式旨在消除长度偏差。在标准 GRPO 中组内奖励归一化可能导致模型偏向生成长响应尤其是错误答案时而此模式通过使用固定的分母例如预定义的最大响应长度来保证损失大小不受序列实际长度影响从而缓解优化偏差。如果loss_scale_factor设置为一个全局常数如 2048那么不论当前批次序列长度如何损失的计算都会除以这个常数使得梯度更新步长与长度解耦。3 为什么需要这么多聚合模式在强化学习训练 LLM 时损失的计算方式会影响梯度更新的方向和幅度。不同的聚合策略对应着不同的假设token-mean每个 token 同等重要适合希望模型在 token 级别精细调整的场景。seq-mean-token-sum每个序列同等重要但保留序列内 token 损失的原始和适合希望模型优化序列整体回报的任务。seq-mean-token-mean每个序列同等重要且每个序列的损失被归一化到 token 平均适合希望平衡长短序列贡献的场景。seq-mean-token-sum-norm消除长度对损失的影响专门用于纠正 GRPO 可能产生的长度偏差。4 分布式训练的注意事项参数dp_size用于将局部损失单个 GPU 上的聚合结果放大到全局等效值。因为在分布式训练中每个 GPU 只处理一部分数据计算出的损失通常是局部平均但反向传播时需要保证梯度与全局 batch 大小匹配。乘以dp_size是一种常见的做法相当于将局部损失视为全局损失的一部分然后通过 all-reduce 平均梯度时能得到正确结果。batch_num_tokens和global_batch_size通常由框架在全局范围内计算并传入确保聚合时使用的是全局统计数据而不是局部统计。
RELATED

相关推荐

解读 VeRL 框架源码:PPO 和 GRPO 算法如何计算优势 (Advantage)

解读 VeRL 框架源码:PPO 和 GRPO 算法如何计算优势 (Advantage)

最近正在入门大模型 RL 微调,阅读 VeRL 框架代码。在这篇博客中,我们从 VeRL 源码入手,解读一下 ppo 和 grpo 算法的 advantage 计算方法: # 来自 verl/trainer/ppo/core_algos.py register_adv_est(AdvantageEstimator.GAE) # …

📅 2026/11/5 1:30:57
轻量级 jQuery 折叠插件 jQuery-Collapse:3 分钟读懂内容渐进式披露的终极方案

轻量级 jQuery 折叠插件 jQuery-Collapse:3 分钟读懂内容渐进式披露的终极方案

【免费下载链接】jQuery-Collapse A lightweight (~1kb) jQuery plugin that enables expanding and collapsing content 项目地址: https://gitcode.com/gh_mirrors/jq/jQuery-Collapse 点击查看 免费下载 网页内容太多时,直接全部展示会让用户感到 ov…

📅 2026/11/5 1:30:57
C++将字符串转换为整数和浮点数的几种方法

C++将字符串转换为整数和浮点数的几种方法

1. 常见的字符串转整数方法1.1 atoi()atoi 是C语言中最简单、最基础的字符串转整数函数。它接受一个字符串并返回对应的整数值。1int atoi(const char *str);优点:简单易用,适合处理非常基础的字符串转整数操作。缺点:没有错误处理机制。如果…

📅 2026/11/5 1:30:57
MORE NEWS

更多资讯

📰

2025年Data Agent技术演进与行业应用解析

1. 榜单背景与行业价值2025年中国大数据产业正经历从规模扩张向价值深挖的关键转型期。Data Agent(数据智能体)作为融合大模型与垂直场景的新范式,正在重构企业数据应用的基础架构。金猿奖作为国内大数据领域最具公信力的行业奖项之一&#x…

📰

有感FOC三环实现:时序分层、PI整定与轨迹规划

简介:面向电机控制与嵌入式开发学习者的有感FOC位置、速度与电流三环算法实现资料,围绕坐标变换、PID调节、SVPWM调制、编码器反馈与电流采样等关键环节展开,代码在STM32F4平台上运行,适合希望理解有感控制链路、搭建实验样机或对…

📰

STM32 FATFS SD卡读写:diskio、扇区寻址与写同步避坑

简介:面向嵌入式学习者与需在STM32上实现存储读写的开发者,这份资料展示KEIL环境下通过SPI或SDIO驱动SD卡并挂载FATFS的完整思路,涉及引脚配置、扇区读写、FATFS裁剪及f_open、f_read等文件操作,也包含未检测到卡、文件不存在、读…

📰

AI时代工程师转型:从编码到架构设计的技能升级

1. 从代码编写到架构设计:AI如何重塑工程师角色十年前我刚入行时,工程师的核心竞争力是能写出高效可靠的代码。现在打开GitHub Copilot,它能在几秒内生成比我当年更规范的函数实现。上周团队新来的应届生用GPT-4重构了我们的日志模块&#xf…

📰

基于DWVD与MCNN的机械故障诊断方法研究

1. 项目概述这个项目提出了一种创新的机械故障诊断方法,将时频分析领域的离散韦格纳分布(DWVD)与深度学习中的多尺度卷积神经网络(MCNN)相结合。作为一名长期从事机械状态监测的工程师,我深知传统故障诊断方…

📰

GEC2410 WinCE底层实验包:手册与源码对照实战指南

简介:这份资源面向嵌入式系统初学者与进阶开发者,围绕三星S3C2410(ARM920T内核)硬件平台与Windows CE系统的集成开发展开,提供实验手册与配套源码,帮助读者理解从系统架构、硬件接口到驱动与应用程序开发的…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬