技术

Agent调优 Agent评估 OS Agent Agent与软件开发 提升Agent能力——上下文工程 llm评测 rl微调 分布式Agent与A2A deepresearch梳理 mcp学习 SSE 和 WebSocket 是什么? AutoGen学习 Python ioc 从0到1构建一个db 上下文记忆——AI Agent native 的任务存储机制 线性RAG的进化——agentic rag 图数据库的一些考量 推理LLM梳理 Agent演进 LLM预训练 向量数据库的一些考量 fastapi+sqlalchemy进行项目开发 LLM微调实践 Python协程实现 Agent Functon Calling LLamaIndex入门 另一种微服务架构Multi-Agent Python虚拟机 LangGraph工作流编排 Python实践 增强型LLM——Agent 激发LLM涌现——提示工程 LLM微调理论 大佬谈LLM LLM外挂知识库 LLMOps 多模态LLM Python一些比较有意思的库 Transformers源码学习 LangChain源码学习 通用分布式计算引擎Ray Python并发 go依赖注入 go collection gc的基本原理 golang性能分析及优化 数据湖 高性能计算与存储 Linux2.1.13网络源代码学习 《大数据经典论文解读》 三驾马车学习 Spark 内存管理及调优 Yarn学习 从Spark部署模式开始讲源码分析 容器狂占内存资源怎么办? 多角度理解一致性 golang io使用及优化模式 Flink学习 c++学习 学习ebpf go设计哲学 ceph学习 学习mesh kvm虚拟化 学习MQ go编译器以及defer实现 学习go 为什么要有堆栈 汇编语言 计算机组成原理 运行时和库 Prometheus client mysql 事务 mysql 事务的隔离级别 mysql 索引 坏味道 学习分布式 学习网络 学习Linux go堆内存分配 golang 系统调用与阻塞处理 Goroutine 调度过程 重新认识cpu mosn有的没的 负载均衡泛谈 单元测试的新解读 《Redis核心技术与实现》笔记 《Prometheus监控实战》笔记 Prometheus 告警学习 calico源码分析 对容器云平台的理解 Prometheus 源码分析 并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go channel codereview gc分析 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes扩缩容 神经网络模型优化 直觉上理解深度学习 如何学习机器学习 TIDB源码分析 什么是云原生 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 共识算法 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全访问机制 jvm crash分析 Prometheus 学习 Kubernetes监控 Kubernetes 控制器模型 容器日志采集 容器狂占资源怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes类型系统 源码分析体会 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 线程排队 jib源码分析之细节 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI——容器网络是如何打通的 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 精简代码的利器——lombok 学习 《深入剖析kubernetes》笔记 编程语言那些事儿 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 如何分发计算 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 calico学习 AQS——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记 log4j学习 为什么netty比较难懂? 递归、回溯、动态规划 apollo client源码分析及看待面向对象设计 学习并发 docker运行java项目的常见问题 OpenTSDB 入门 spring事务小结 分布式事务 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型和jvm内存布局 java exception Linux IO学习 netty内存管理 测试环境docker化实践 netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker namespace和cgroup zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 深度学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM执行 git maven/ant/gradle/make使用 再看tcp kv系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 Go基础 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

架构

rl与sft 大模型infra综述 OpenTelemetry及生态 大模型可观测性 grpo演进 rlhf演进 agent框架 reward演进 大模型RLHF框架 大模型rl后训练系统 GPU与CUDA RL闲谈 MCTS与LLM rl与post-train rl入门 从Transformer到DeepSeek bert rerank微调 大模型推理tips RAG向量检索与微调 dddfirework源码分析 RAG与知识图谱 大模型推理服务框架vLLM 大模型推理服务框架 模型服务化(未完成) 大模型Post-Training 大模型训练 大模型推理 从Attention到Transformer k8s设备管理 ddd从理念到代码 如何应用LLM 语言模型的发展 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 kubevela源码分析 容器和CPU那些事儿 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 AutoML和AutoDL 特征平台 实时训练 分布式链路追踪 K8S YAML 资源清单管理方案 tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 推荐系统embedding原理及实践 机器学习中的python调用c 机器学习训练框架概述 tensornet源码分析 大模型训练和推理 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 从混部到统一调度 对序列建模——从RNN到Attention pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 多活 volcano特性源码分析 推理服务 kubebuilder 学习 mpi 学习pytorch client-go学习 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tensorflow学习 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 学习rpc BFF openkruise学习 可观察性和监控系统 基于Kubernetes选主及应用 《许式伟的架构课》笔记 Admission Controller 与 Admission Webhook 发布平台系统设计 k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? 《软件设计之美》笔记 mecha 架构学习 Kubernetes events学习及应用 CRI——kubelet与容器引擎之间的接口 资源调度泛谈 业务系统设计原则 grpc学习 元编程 以应用为中心 istio学习 下一代微服务Service Mesh 《实现领域驱动设计》笔记 概率论 serverless 泛谈 《架构整洁之道》笔记 处理复杂性 那些年追过的并发 服务器端编程 网络通信协议 架构大杂烩 如何学习架构 《反应式设计模式》笔记 项目的演化特点 反应式架构摸索 函数式编程的设计模式 服务化 ddd反模式——CRUD的败笔 研发效能平台 重新看面向对象设计 业务系统设计的一些体会 函数式编程 《左耳听风》笔记 业务程序猿眼中的微服务管理 DDD实践——CQRS 项目隔离——案例研究 《编程的本质》笔记 系统故障排查汇总及教训 平台支持类系统的几个点 代码腾挪的艺术 abtest 系统设计汇总 《从0开始学架构》笔记 初级权限系统设计 领域驱动理念 现有上传协议分析 移动网络下的文件上传要注意的几个问题 推送系统的几个基本问题 做配置中心要想好的几个基本问题 不同层面的异步 分层那些事儿 用户认证问题 资源的分配与回收——池 消息/任务队列

标签

k8s设备管理 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 kubevela源码分析 容器和CPU那些事儿 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 K8S YAML 资源清单管理方案 从混部到统一调度 volcano特性源码分析 kubebuilder 学习 client-go学习 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 openkruise学习 基于Kubernetes选主及应用 Admission Controller 与 Admission Webhook k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? Kubernetes events学习及应用 CRI——kubelet与容器引擎之间的接口 资源调度泛谈 如何学习Kubernetes 以应用为中心 kubernetes operator kubernetes扩缩容 serverless 泛谈 什么是云原生 自定义CNI IPAM docker和k8s安全访问机制 Kubernetes监控 Kubernetes 控制器模型 Kubernetes资源调度——scheduler Kubernetes类型系统 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 Kubernetes源码分析——从kubectl开始 kubernetes yaml配置 CNI——容器网络是如何打通的 当我在说PaaS时,我在说什么 《深入剖析kubernetes》笔记 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件
Agent调优 Agent评估 OS Agent rl与sft 大模型infra综述 Agent与软件开发 提升Agent能力——上下文工程 llm评测 大模型可观测性 rl微调 grpo演进 rlhf演进 agent框架 分布式Agent与A2A reward演进 deepresearch梳理 mcp学习 大模型RLHF框架 大模型rl后训练系统 GPU与CUDA RL闲谈 MCTS与LLM rl与post-train rl入门 AutoGen学习 从Transformer到DeepSeek 上下文记忆——AI Agent native 的任务存储机制 线性RAG的进化——agentic rag bert rerank微调 大模型推理tips 推理LLM梳理 Agent演进 LLM预训练 RAG向量检索与微调 LLM微调实践 RAG与知识图谱 大模型推理服务框架vLLM Agent Functon Calling LLamaIndex入门 另一种微服务架构Multi-Agent LangGraph工作流编排 大模型推理服务框架 模型服务化(未完成) 大模型Post-Training 大模型训练 大模型推理 从Attention到Transformer 增强型LLM——Agent 激发LLM涌现——提示工程 LLM微调理论 大佬谈LLM LLM外挂知识库 LLMOps 多模态LLM Transformers源码学习 LangChain源码学习 如何应用LLM 语言模型的发展 AutoML和AutoDL 特征平台 实时训练 tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 推荐系统embedding原理及实践 机器学习中的python调用c 机器学习训练框架概述 tensornet源码分析 大模型训练和推理 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 对序列建模——从RNN到Attention pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 推理服务 mpi 学习pytorch 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tensorflow学习 kaggle泰坦尼克问题实践 神经网络模型优化 概率论 直觉上理解深度学习 如何学习机器学习 深度学习泛谈

rl与sft

2025年07月21日

简介

  SFT RFT
训练方式 对于给定Query,直接学习Demonstration(记忆) 采样多组Responses,根据奖励鼓励/惩罚(利用+探索)
训练目标 最大化生成训练语料的概率,通过极大似然估计训练 最大化未来累积奖励,通过策略梯度训练
学习模式 注入外部分布的知识,存在暴露误差问题,泛化性差 利用+探索自身采样结果中优质的解答模式,泛化性更好
学习规律 由于内外分布的偏差,SFT初期会遗忘模型内部的知识,性能下降;后期容易过拟合,熵降低 随着训练推进,模型逐步学会”简单“问题,出现熵坍缩现象;但困难问题上存在稀疏奖励现象,难以学习

在传统认知中,RL 与 SFT 的区别主要体现在两个方面:

  1. 负样本的利用。SFT只是模仿学习正例,SFT+DPO需要大量的标注数据来cover bad case。而RL不仅对好的样本奖励,也要对坏的样本惩罚。无论是简单的策略梯度,还是GRPO、Reinforce、PPO这些算法,原理都是一致的,本质上只是在设计不同的奖励/惩罚的粒度(token/macro action/seq等等)和力度(需不需要引入baseline,要不要考虑KL限制,要不要clip等等)
  2. (样本的多样性)。rl允许使用当前模型在线产出的样本训练自身(仅on-policy模式)。SFT一般学习的都是人工标注或者其他模型生成的样本(即蒸馏),SFT阶段的Rejection Sampling技术也是自己采样训练自己,很像是RL,只不过没有惩罚信号(也可以引入负例进一步做DPO)。而RL允许当前模型实时采样样本,并依据这些样本训练自身。
    1. SFT需要更多的标注数据,RL可以通过固定的规则奖励(例如math 和code只需要判断最终结果)与奖励模型提供的正负反馈不断迭代,让模型自己探索符合人类偏好的回复策略,可以减少训练数据的要求。

​在标准的LLM训练流程中,通常包含三个阶段:Pre-training、SFT和RL。Pre-training阶段采用自回归的方式在海量数据上完成预训练,为后续的Post-training奠定基础。Post-training通常分为SFT和RL,这两个阶段均需要一个多样性丰富的prompt集合D

  1. 在该阶段对于prompt $x~D$,会采用专家撰写、人工合成或者强模型蒸馏的方法来构造高质量的响应。
  2. ​RL通常在SFT阶段后进行。在On-Policy的设定下,对于prompt $x~D$,通常会从当前策略$\pi_theta$中采样响应y。GRPO是PPO的一种无critic模型的变种,针对同一个prompt x,会同时采样G个响应 ${y_i}_{i=1}^G$,每个响应 $y_i$ 对应于一个标量奖励 $R_i$。在标准的PPO中需要critic模型来辅助计算优势,GRPO则采用组内标准化实现优势的近似计算 $A(i,t)= \frac{R_i−mean([R_1,…,R_G])}{std([R_1,…,R_G])}$,这里 $A(i,t)$是指第i个响应的第t个token的优势。

VLM RL的tech Report中,RL的版本整体涨点在1-2左右,而SFT最高可做到5-6个点。监督学习的上限是老师,而强化学习可以通过学习经验超越老师。

SFT不足

对模型自我进化的思考与设计

  1. SFT不擅长理解和遵循『不要做什么』指令:和传统的分类任务不同,语言模型的SFT是一个生成任务,不具备很强的排他性。在传统的分类任务中,优化模型使得目标类的概率变大,很容易导致其他类别的概率变小。而在语言模型的SFT中,输出往往不是唯一的,同时为了保留语言模型的知识,优化正例时并不会将正例的概率优化成1而将其他可能的回复的概率优化变成0。换句话说,SFT只是告诉了模型训练数据中的回复是可行的,但模型无法像普通分类任务那样利用排他性确定哪些输出是不可行(对于排他性比较好的任务,语言模型的分布本身就有较好的排他性,提升SFT训练样本的概率会大幅降低其他回复的概率)。因此,对于一些回复多样性较高的任务,SFT很难避免模型生成不恰当的回复。从排他性角度来看,SFT擅长『要做什么』指令,但不擅长『不要做什么』指令,尤其是在输出灵活性很高的任务上如创作、摘要。例如对于『生成一篇100字以内的文章』这一指令,其隐含的『不要做什么』的指令是『文章不要超过100字』。对于这一指令,SFT阶段会给模型一个100字以内的文章,但这并不足以让模型确定超过100字的回复是不可行的或者说概率是极小的。如果一定要用SFT来解决这类问题,那么只能不断增加正例数量,挤压负例的概率。但因为这类任务下的正例排他性非常低,这样做的效率也是非常低的。因此,不建议依赖SFT来提升模型对于『不要做什么』指令的理解和遵循能力。
  2. SFT不应该追求统一的输出形式,而应该追求多元的输出形式:在预定义输出标准的指导下,不少同学追求在SFT阶段对齐用户(或产品、RD)的偏好,固化模型的输出形式;或者选择一种初始模型表现较好的形式作为模型的默认回复形式,比如推理任务下默认使用CoT形式进行回复,信息抽取任务中默认不给解释。这样的做法会给模型的能力升级带来很大的困难:
    1. 不利于后期输出形式的调整:随着新老用户的交替以及用户对模型需求的变化,模型的期望输出形式也会发生改变。如果在SFT阶段就固化了模型的输出形式,后期调整模型输出形式需要对SFT进行大规模的改动——大量输出形式不再合适的数据的存在使得增加少量具有新输出形式的SFT数据很难起作用,须先对已有数据进行大规模的退场。
    2. 不利于对不同尺寸模型的适配:不同模型的能力是不同的,大模型可能可以使用简洁的方式回复给定问题,而小模型可能需要更复杂的输出方式。如果只给定一种回复模式,那么注定很难同时满足多个不同的模型:为大模型建设的SFT数据可能无法很好地调教小模型,而为小模型构建的SFT数据对大模型而言过于累赘低效。
    3. 不利于实现千人千面、千题千面:从用户层面来看,不同的用户对回复的倾向是不同的。ToB用户可能更喜欢简洁的回复,而ToC更喜欢具备详细解释的回复。统一的输出形式很难满足不同用户的需求,也无法充分挖掘用户的偏好反馈为将来的千人千面做储备。从任务层面来看,即使是同一个任务,不同case的难易程度及其恰当的回复形式也是不同的。对于比较难的case,模型可能需要使用CoT或者更复杂的Agent模式进行回复;而对于简单的case,模型可以直接回复答案。一致性地采用复杂的输出模式会造成资源的浪费,而一致性地采用简单的回复模式又可能导致模型在一些复杂cases上犯错。SFT阶段固化输出形式无法实现不同能力的兼容,会导致顾此失彼(比如有些平台需要两套不同的接口来区分Agent和非Agent)
    4. 不利于模型自我进化:一个比较理想的模型能力进化路径是从复杂输出模式出发,不断强化模型简单输出模式下的能力,不断形成正确性和简洁性的新平衡。而如果在SFT阶段固化模型的输出形式,限制了模型的探索能力,不利于模型在PPO阶段通过大规模探索自动实现能力的进化。
    5. 不利于形成护城河:因为模型输出的模式化,其他平台可以大规模抓取我方模型的输出,快速对齐我方模型。又因为没有及时引入用户信息,导致无法有效形成用户越多-效果越好-用户越多的飞轮。
    6. 不利于争取其他平台的用户:这主要是不同平台的输出标准不同,固化SFT的输出标准会增加用户从其他平台迁移到我方平台的成本和难度。
  3. “SFT高原”。这一现象是指即使在增加了大规模的有标签数据集之后,模型的性能提升也微乎其微。SFT在这些任务中显得力不从心,这表明它在某些复杂场景下存在固有的性能瓶颈。出现“SFT高原”的原因在于,SFT本质上是一种模式记忆和泛化。对于需要复杂推理、逻辑链或涉及组合爆炸的任务,仅仅依靠增加数据量是无法让模型掌握其内在逻辑的。模型可能会记住训练集中的特定模式,但无法将这些模式推广到全新的、未曾见过的复杂结构中。这种现象揭示了SFT在处理高度复杂、需要动态规划和逻辑推理的任务时的局限性。它表明为了突破这一瓶颈,需要一种能提供更细粒度、更动态反馈的学习机制。RL允许模型通过试错来学习,即使某个输出不完美,只要它比之前的尝试更接近目标,就能获得正向奖励。PS:循循善诱而不是棍棒教育。

因此,SFT阶段应该尽可能避免做形式偏好(既包括展示形式,也包括得到输出的路径)的对齐,而是将其放到RM、PPO阶段去完成(后面会介绍如何在RM系统中引入用户偏好来避免上述问题)。SFT更多的是让模型知道给定指令情况下可能可以怎么回复,提高潜在可行回复的采样概率,尽可能保留模型输出的多样性,以便于后期高效调整偏好(比如对于可回答的问题在SFT阶段也可以引入据答)。

PPO擅长学习『不要做什么』。得益于RM评估的高效性,PPO阶段可以对回复进行大规模采样和评估,此时不仅可以告诉模型什么样的回复是可行的,还可以告诉模型什么样的回复是不可行的(SFT阶段正例样本较少,引入负例容易让模型过拟合负例,将负例中的一些正确pattern的概率也给错误地降低了)。因此,相较于SFT,PPO更擅长学习『不要做什么』,在这个阶段,应加强否定指令的建设。

PPO阶段除了引入用户偏好收敛输出形式外还有一个重要任务是实现能力的进化。典型的Online PPO是从SFT模型的输出空间中筛选出RM得分更高的子空间,所以其最终能力是受限于SFT模型的。当SFT能力覆盖了期望进化方向时,比如期望方向是越简洁越好,同时给定一个query,模型能生成各种复杂度,或正确或错误的输出,此时模型能通过对应的RM模型实现在该方向上能力的进化。而当SFT能力没有覆盖期望的进化方向时,比如模型很难生成既简单又正确的输出,普通的Online PPO无法有效采样出目标方向更好的输出,此时模型进化能力会受限。这种情况下,最直接的方式是先用SFT扩展模型能力边界。但其实也可以直接在PPO过程中扩展模型的能力边界——在PPO阶段引入offline数据,比如从繁琐但正确的回复出发,自动合成简洁且正确的回复,然后用importance sampling的学习方式将该样本引入到PPO的采样集合并进行学习。

与sft 对比

强化学习发展这十年随着这两年强化学习的大力发展,强化学习衍生出了许许多多的子课题方向,除了最经典的online RL以外,例如offline model-free RL,model-based RL,RLHF,multi-agent,risk-sensitive,inverse RL等等,要给这些子课题找共性非常困难。而在传统教科书中,强化学习的标准制定时由于这些子课题还未广泛引入关注,导致定义早已过时。举个例子,例如强化学习强调智能体跟环境交互,而offline RL方向偏说我就不跟环境交互。再例如强化学习强调无需人类标签还是采用奖励信号(相当于“标签/标准答案”的简化版),RLHF说我就不是这样。最开始,人们对强化学习应用的要求也非常严格,在强化学习应用的论文描述里必须有以下内容:

  1. 非常准确的状态空间和动作空间定义
  2. 必须存在状态转移函数,不允许单步决策,也就是一个动作就gameover
  3. 必须有过程奖励,且需要存在牺牲短期的过程奖励而获取最大累计回报的case案例 说个开玩笑的话,如果DS的文章放到几年前RL的审稿人手里,他大概率会得到这样的回复:这只是采用了策略梯度的方式将不可导的损失/奖励函数用于优化神经网络参数而已,请不要说自己使用了强化学习。这导致像作者这样的old school,在看到最新的强化学习应用文章时,总会试图问文章作者几个基础的问题,状态是啥,动作是啥,奖励是啥。但其实现在很多文章已经不考虑这些问题了。

RL for LLM本质理解为更清晰地理解 RL(强化学习),我们可以从它与监督学习的联系入手。监督学习(SFT)的损失函数可以表达为:

\[L = -\mathbb{E}_{x \sim p(x)} \mathbb{E}_{y \sim \pi^*(\cdot \mid x)} \left[ \log \pi(y \mid x) \right]\]

其中,$x$ 是提示(prompt),$y$ 是模型的生成内容,$\pi^*$ 是代表人类专家答案的真实分布,$\pi$ 是模型的输出策略分布。其目标是最小化模型分布与专家分布之间的差异。SFT 的 loss 是目标分布(通常是 one-hot)和模型分布的交叉熵函数。PS:或者说,让人工标注的输出(正确答案)概率尽可能大。

RL 的损失函数则可视为一种加权监督学习:

\[L = -\mathbb{E}_{x \sim p(x)} \mathbb{E}_{y \sim \pi(\cdot \mid x)} \left[ \omega(x, y) \log \pi(y \mid x) \right]\]

已知 transformer 的每个 step 的输出是一个 logits 向量,对这个 logits 做一次 softmax 函数就得到了每个 token 被选中的概率。如果 Advantage 大于零,logits 向量在当前 token 维度的梯度是负数,这个 logit 会朝增大的方向优化,logits 向量在其他所有维度的梯度都是正数,剩余的 vocab_size - 1 个 logit 都会朝着减小的方向优化;反之亦然。

核心区别

  • 引入了权重项 $\omega(x, y)$ PS: 策略梯度算法通常对应Advantage,RL 杂谈SFT 仅仅是 RL advantage 全为 1 时的一个特例罢了,SFT 是一种全部样本都为 off_policy,只计算正例 loss,且 advantage 均为 1 的 RL
  • 代表人类专家答案的真实分布被替换为模型自己的输出策略分布。PS:不再有“正确答案”,模型输出一个 action(token)后,若带来高奖励,就让这一步的 log-prob 增大。若带来低奖励,就让这一步的 log-prob 减小。

可能有人想说,既然只是加权的监督学习,那 RL 有什么特别的呢?RL的特殊性在于其权重 $\omega(x, y)$ 的设计以及训练数据的来源,这使其区别于旨在调整样本重要性的传统加权学习:

  1. RL 中的 $A_t$ 并不是一个加权常数,$A_t$ 的“完整”写法是 $A_t(\pi)$,这是一个随着 $\pi$ 的变化而发生变化的函数,更麻烦的是,这是一个黑箱函数,无法对 $A_t(\pi)$ 求导,而只能通过采样来评估,采样必然存在误差,进而导致训练不稳定。与之相对的,SFT 的 $A_t$ 实打实就是常数 1。
  2. 权重可为负值,实现“趋利避害”:传统加权学习的权重通常为非负,用于强调重要样本。而RL中的权重可以为负。负权重指导模型“避免”生成特定的行为,而不仅仅是“模仿”期望的行为。这种“惩罚”机制是模型能够快速抛弃失败策略、探索未知但可能更优策略空间(Exploration)的关键,也是其能力超越训练数据限制的根本原因。PS:sft 只是提高期望输出的概率,而rl 可以降低非期望输出的概率
  3. 数据来源与权重结合,形成自洽的优化闭环:RL用于学习的数据由模型自身生成。这一特点与上述的奖惩机制相结合,形成了一个强大的自优化循环:模型生成行为 -> 获得带有正或负权重的反馈 -> 调整策略以增加高权重行为、减少低权重行为 -> 生成更高质量的新行为。通过这一循环,只要权重(以及背后的reward)设计得当,模型便有潜力实现持续的自我迭代与能力攀升,最终达到超越人类的性能水平。

RL 为什么不如 SFT稳定

  1. infra 上的难度 RL 确实较之于 SFT高了不止一个档次,系统级的 bug 存在于训练框架的各个角落。训推一致性修复的时候,计算 logprob 的时候考虑 temperature、top_p、top_k 这些了吗?reward shaping 的时候,reward 从 0 / 1 变成小数,剔除全零全一样本时会兼容这些吗?模型 rollout 的时候,出现了多个</think>的时候,有考虑过怎么处理这种数据吗?这些都是常见的小 bug,megatron、vllm、sglang 的 bug 角度更是刁钻
  2. 另一方面,数据的干净程度相差过远。SFT 的数据筛选会有一套较为复杂的流程,利用各种规则加模型来进行过滤,甚至会人工一条条的进行 review。相比之下, RL 给数据打 reward 的时候通常没有如此复杂的清洗流程,往往只是外挂了一个 reward model 去比较 model_response 与 ground_truth 是否一致。一旦遇到较为困难的 prompt,ground_truth 非常复杂,一致性判别的准确率能有个 90% 都已经算很好了。
  3. 最后再补一条个人的暴论,我认为 LLM 中的 RL 算法如逆水行舟,不进则退,它的每条训练样本都是带有一些“毒性”的,如果这些样本没有 explore 到有价值的信息,模型便会朝崩溃更进一步。具体来说,RL 的每条正样本,都在让语言模型朝着过拟合的方向去学习,即进一步强化那些本就是高概率的语料;RL 的每条负样本,则都是在破坏语言模型的分布,将某个 token 的概率分给其他所有的 token,会将语言模型的分布引向一个未知的方向(负样本的存在也可能是 RL 容易崩溃的关键)。反观 SFT ,每条语料都是我们精心构造的,我们不仅知道这些语料的分布情况,也会想方设法的调超参数来避免模型在这些语料上过拟合。

在上述框架下,当前RL for LLM的研究主要围绕以下几个核心问题展开:

  1. 如何从“用于验证的弱监督信号(reward)”推导出每个样本的权重$\omega(x, y)$ ? 例如,PPO中学习的价值函数(value function),以及DeepSeek GRPO中从批次数据中估计的优势函数(advantage function),都是对该问题的不同解法。
  2. 承接上一点,如何高效、准确地获取“用于验证的弱监督信号(reward)”? 从RLHF可学习的reward model,到DeepSeek的rule-based reward,都是在解决这个问题
  3. RL的两个核心步骤 ——a.基于加权监督学习的模型更新 与 b.新样本的生成——二者的交互节奏是怎样的?是生成一个样本就更新一次(完全在线),还是生成N个样本后更新一次或N次?先前批次生成的样本在后续迭代中是否应该被舍弃(on-policy vs. off-policy的权衡)?PPO/GRPO算法中的修正比值、clip参数、生成批次大小、更新的epoch数目等,都与这个核心节奏问题紧密相关。
  4. RL训练应选择怎样的提示(prompt)分布 p(x)? 以数学问题为例,如何设计一个问题序列(课程),才能最大化模型的学习效率?
  5. 进行RL训练前,基础模型(base model)需要具备哪些先决能力,才能有效支撑后续的强化学习过程? 以上几点构成了当前RL for LLM研究的基本蓝图。对这些问题的不断探索和解答,将持续推动大型语言模型向着更高级的智能形态迈进。

既然 RL 容易训崩溃,那都有哪些技巧可以稳定训练呢,这里简单罗列一下:

  1. entropy collapse:训练的时候加不加 entropy loss,至今仍未达成共识。
  2. CLIP:至少一半的强化工作都围绕 clip 做文章,这些工作分析的非常有道理,实际用起来却乏善可陈。
  3. Token Mask:对高熵 / 低熵 token 做特殊的逻辑,或鼓励某些 token 的学习,或阻止某些 token 的更新,也是重点雕花区域。
  4. Reward Shape:
  5. 控制 reward 样本中 0 / 1 的分布在一个区间范围内;
  6. 用 pass@K 代替 pass@1 作为优化目标;
  7. 用 test case 的通过率作为 reward;
  8. length penalty;
  9. 训推一致性:当下最热的话题,以 tis、icepop 最为火热,但可以说和算法没啥关系,全看 infra 功底。

我个人不喜欢加太多技巧,尤其是 entropy_loss、kl_loss 这种不太知道会对模型产生什么影响的技巧。完全是为了在 RL 的过程中控制模型不去崩溃,让那条训练曲线更好看。大多数实验中,熵炸、grad_norm 炸都属于表象,阻止它不如去分析它,以熵为例:

  1. 为什么会熵增?按理说训练是一个确定性增加的过程应该熵减,但是你的模型确实在增,那就说明训练的过程中:高概率 token 常被当作负例,或者是低概率 token 被常当作正例;
  2. 为什么会熵减过快?rollout 多样性差呗。是不是调整下 rollout temperature 和 rollout prompt 要比加一个 entropy loss 更合理些。

总结下来,任何技巧的本质,都是在帮助模型寻找一个适合它的训练数据分布。因此,分析 rollout 数据分布的变化,优先级要始终领先于尝试引入某个稳定训练的技巧。这些技巧在稳定某次训练的同时,也会掩盖训练崩溃的原因。但同时,若某个技巧确实有用,也可以反过来推哪种数据分布“有利于/有损于”模型的学习:例如, off_policy 和训推不一致会引起崩溃,是不是在间接说明“一个整体上与模型分布很接近,但却在个别 token 上和模型分布差异很大的样本”可能是一种不太适合模型的数据。引入训练技巧必然会引起训练数据分布的变化,有些分布的变化是在我们预期之内的,有些分布的变化则是我们预期之外且不知情的。CISPO 的作者就曾分享过:在 off_policy 的时候, 被 clip 掉的 token 是具有多重分布的,概率值与当前模型的分布不一致只是其所具有的一个分布,“概率低但影响 long cot涌现”则是这些 token 的另外一个分布。作为训练者,我们往往不能留意到所有分布的变化,从而总结出一些错误的结论。这里我并不是反对所有技巧,而是认为:在使用技巧的时候,我们需要知道自己设计的新的loss 会让哪种分布的 token 得到促进 / 抑制?如果无法得知,那就别加。

那么话说回来了,RL到底有没有任何时候都有效且毫无副作用的技巧呢?有的,洗数据!训 reward model!

  1. 先说数据吧,今年大家普遍进入了 post train 深水区之后(从 math、gsm8k 进阶到 aime、imo),一个很严重的问题就是:训模型者看不懂数据了,没有办法通过肉眼看解题过程来判断数据质量了。而训模型者日常批量清洗数据的手段,往往都存在一个问题:“无法区分难题和错题”。
  2. 难题有什么特点?模型多次采样后,屡屡犯错,偶尔灵机一动,做对了。
  3. 错题有什么特点?模型多次采样后,基本都做对,但是因为和 ground_truth 不一致屡屡被判错,偶尔脑子抽风做错了,好巧不巧和 ground_truth 一致了。

不要以为错题的答案是离谱到一眼就能看出来的那种,事实上,错题往往是十分接近 ground_truth 且非常具有迷惑性的。这里我举几个例子:

  1. 一张票 2 块钱,9 块钱能买几张票?我们以为错题的答案会是 356 张这种离谱的数字,其实是 4.5 张;
  2. 一个一元七次方程,错题的答案给了 3 个实数解,我们 review 的时候,反代入进去发现是对的,留下了这道题目。但在训练的时候,模型拿着3个实数解和4个复数解,高高兴兴的去找 reward_model 要奖励的时候,反手被打了 0 分,这对模型是多大的心理阴影呀。 目前的开源 RL 数据的质量真的是一言难尽。没辙,要么请专业的硕博理科生去标注,要么用启发式的规则去清洗,在不够干净的数据上只能得到错误的实验结论。
  3. 再说 reward model,千万不要以为所谓的 rule_based reward model 真的就是靠 rule 来打分的,或者是靠 math_verify 这种规则库 。有很多情况下,靠 rule 几乎无解:
  4. 问题是盈利__%?标准答案是 96,而模型输出了“盈利96%”,模型活该拿 0 分吗?
  5. 标准答案是 3.14,模型输出了$\pi$、圆周率、3.1415926,模型活该拿 0 分吗? reward 要准,我建议使用 generate reward(相对于rule-based reward),而且得是能力巨强的那种。这个模型需要读的懂题目要求的输出格式、 ground_truth 的等价变换,以及各种复杂的高阶公式。除了较强的知识能力外,模型还要具备很强的指令遵循能力,否则它容易自己亲自下场解题。

rl 与sft 融合

为什么要融合SFT和RL RL虽然能够有效提升模型的推理能力,但一个重要的前提是基础模型本身具备了一定的相关能力。在RL训练中,通过多次rollout能够采样到正确的轨迹,这样通过RL才能进一步提升。这无疑限制了RL的探索空间。因此,主流的方式是通过SFT赋予模型一些基础能力,然后在进一步利用RL来提升相关能力。但是一些研究认为两阶段的方式并不是最优的。PS: 建议细读,对理解sft 与rl ,以及多阶段post-train 很有帮助

rl与熵

信息论里的熵,概念其实非常直观。它衡量的是一个系统或一个概率分布的不确定性或者说“惊奇程度”。信息论之父香农(Shannon)用一个优美的公式量化了它。对于一个离散变量X,其概率分布为$ P(x) $,那么它的信息熵$ H(P) $定义为:$ H(P) = -\sum_{x} P(x) \log_{2} P(x) $

  • $ P(x) $是事件$ x $发生的概率。
  • $ \log_{2} P(x) $可以理解为事件$ x $发生所包含的“信息量”或“惊奇程度”(概率越低,信息量越大)。
  • 前面的负号是为了确保熵值为正。
  • 整个公式就是对所有可能事件的“期望信息量”或“期望惊奇程度”进行加权平均。

对于LLM来说,在生成第t个词时,它实际上是面对一个巨大的概率分布——词汇表里每个词成为下一个词的概率。如果模型对下一个词非常确定(例如,“一言为定,驷马难……”后面几乎必然是“追”),那么这个概率分布的熵就很低。如果模型很纠结(例如,一个故事的开头),多个词都有可能,那么熵就很高。

既然熵衡量了不确定性(探索能力),那跟LLM的训练有什么关系?在LLM训练中,我们有一个“真实世界的分布”(用P表示),也就是人类语言中,给定上文后,下一个词的真实概率分布。同时,我们还有一个“我们的模型通过训练拟合出来的分布”(用Q表示),也就是LLM自己预测的、对下一个词的概率分布。我们的目标,就是让模型Q尽可能地去逼近真实世界P。怎么衡量这个“逼近”程度呢?交叉熵 (Cross-Entropy) 就登场了。它衡量的是,当我们用模型的“有偏认知”(Q)去预测和编码“客观事实”(P)时,所需要的平均信息量。它的公式是:

\(H(P, Q) = -\sum_{x} P(x) \log_{2} Q(x)\)。

可以看到它和熵的公式很像,只是$\log$里的概率从$P(x)$换成了$Q(x)$。然而由于我们的模型Q总是不完美的,所以用它来编码P的代价 $H(P, Q)$,必然会比用P自己最优的编码代价$H(P)$要高。多出来的这部分代价就是KL散度 (Kullback-Leibler Divergence)。它精确地量化了两个概率分布之间的“距离”(虽然不是严格意义上的数学距离)。KL散度的公式如下:

$ D_{KL}(P \parallel Q) = H(P, Q) - H(P) = \sum_{x} P(x) \log_{2} \frac{P(x)}{Q(x)} $

我们可以发现交叉熵,信息熵和KL散度之间存在以下关系:

\[\text{交叉熵}(H(P, Q)) = \text{信息熵}(H(P)) + \text{KL散度}(D_{KL}(P \parallel Q))\]

在训练LLM时,我们通常使用最小化交叉熵损失函数作为训练目标。但是我们接触到的真实数据(各种训练语料)是固定的,所以真实世界的信息熵 $H(P)$是一个我们无法改变的常数。因此,我们的优化目标就等价于最小化模型预测Q和真实数据P之间的KL散度。这就是交叉熵损失函数在整个深度学习领域如此流行的根本原因。它让我们有了一个明确的优化目标:让模型拟合的分布无限接近于真实分布。

Reasoning LLM(五):熵缩过程与能力边界RL在当前形式下不足以激励LLMs超越基础模型的推理能力。通过多次采样(k次)模型的输出,在数学、代码、视觉问题上,RLVR训练的模型在小k值(如k=1)时表现优于基础模型,但在大k值时,基础模型的pass@k分数与RL模型相当甚至更高。这表明基础模型在大量采样下能够解决RL模型解决的问题,且RL训练并未引入新的推理模式,而是通过偏向高奖励的路径来提高采样效率(sampling efficiency)。

“熵坍缩”:即在 RL 训练中策略熵在早期训练阶段急剧下降,导致策略模型过于自信,进而导致探索能力的减弱与策略性能的饱和(熵的变化与一个关键因素——动作概率和优势函数(Advantage)的协方差——有反比关系)。当模型选择一个高概率的动作(token),而这个动作又带来了高奖励(高Advantage)时,强化学习算法会大力强化这个选择。这种“强强联合”的更新,会导致这个高概率动作的概率变得更高,其他动作的概率被压制,从而使得整个概率分布的熵急剧下降。在RL训练初期,模型很容易找到一些“低垂的果实”,即一些简单、高回报的捷径。于是模型疯狂地在这些路径上进行自我强化,导致协方差持续为正,熵一路狂跌,最终“熵崩溃”,探索能力耗尽。为了解决这个问题,论文提出了Clip-Cov和KL-Cov等方法,核心思想就是限制那些高协方差token的更新幅度。翻译成大白话就是:“我知道你这个选择又自信又正确,但你先别太激动,悠着点更新,给别的可能性留点机会。”

如果对 Token 熵进行模式分析,可以发现,在 CoT 推理中,大多数 token 的熵很低,用于保证输出的语义连贯性,而少数 token 的熵很高。这些高熵的 token 通常作为推理路径中的“分叉点”(forks),引导模型走向不同的推理路径,用于保证输出的多样性。例如,高熵 token 常见于逻辑连接词(如“Thus”、“perhaps”等),而低熵 token 则多为单词后缀或数学表达式的组成部分。通过仅对高熵 token 进行策略梯度更新(相对全量更新、只更新低熵Token),可以显著提高模型的推理性能,且这种方法在更大模型上效果更明显。那80%的低熵部分,模型在SFT阶段已经学得很好了,再用RL去“用力”,反而是浪费计算资源,甚至可能破坏模型的语言流畅性。

也有工作研究了正负样本对 RL 的训练结果的影响,实验发现,仅使用负样本进行训练(NSR)在Pass@k的整个范围内都能显著提升模型性能,甚至在某些情况下超过了PPO和GRPO等常用强化学习算法。与NSR相反,仅使用正样本进行训练(PSR)虽然能提高Pass@1,但在较大的k值下性能下降,导致输出多样性降低。通过跟踪模型的熵,发现NSR在整个训练过程中保持了较高的熵,而PSR则迅速降低了熵。这表明NSR在训练过程中保持了模型的输出多样性。通过分析可知,PSR通过增加正确响应的logit值,同时降低其他所有token的logit值,导致输出分布变得过于集中,减少了多样性。NSR通过降低错误响应的logit值,并将概率质量重新分配给其他候选token,这种重新分配是基于模型先验的,有助于保持多样性。NSR通过抑制错误响应和根据模型先验重新分配概率质量,有效地细化了模型的现有知识,而不是引入全新的行为。