[{"content":"贴一下可以玩的shortcode。\n音乐播放 播放列表 夏日口袋专辑： 播放单曲 最爱的一首（我是紬厨）： 视频播放 bilibili 有多P可以选择集数\nadmonition 类型有：note、abstract、info、tip、success、question、warning、failure、danger、bug、example、quote。 一个 技巧 横幅 mapbox 在 OpenStreetMap 查看大图 ","date":"2023-03-07T00:00:00Z","permalink":"/shortcode%E7%BD%AE%E9%A1%B6/","title":"shortcode(置顶)"},{"content":"GRPO 训练排查：策略坍塌 vs 训练崩溃 [!info] TL;DR 核心思想：GRPO 训出问题先分类——策略坍塌（policy collapse，算法层：GRPO 目标函数的内在倾向，什么都没做错也会往那个方向走）和训练崩溃（training crash，工程层：两侧概率不一致、loss mask 写错、bf16 精度不够，是 bug 不是超参问题）是两种病，排查路径完全不同，混着查等于两边都查不干净。 最大收获：排查顺序是固定的——先排工程层（bug 是 0/1 的，能被穷尽排除），排干净再动算法层；且二分法按时间尺度切并不严谨，复杂情况要按信号链四问（前提关系）来切。 一句话方法论：抓崩溃前后各 10~20 条 rollout 人工读——3 分钟完成的分类，读曲线要半小时还可能分错。 监控补篇（第 2 篇，09-01 并入）：GRPO 无 critic，唯一信号来自组内相对优势——信号健康度可直接观测、必须观测。平均熵只是报警器；pass@k 取 k=group size 免费算但有四个缺口，且必须配各自调优的 temperature；题内多样性与跨题可区分度是能反向走的两个量，只测前者会漏掉最典型的”高熵模板化”坍塌——补一个一次前向的检索准确率即可堵上。 信号健康度补篇（第 3 篇，09-02 并入）：50% 零方差报警线是错的——占比是 G 与题集难度的函数，要比对预测基线；clip 上界下界分开计、按 advantage 符号解读；两侧引擎 logprob 差 mean\u0026lt;0.001 健康、\u0026gt;0.01 停训（阈值只适用 mean 不适用 max）；长度爆涨先按对错拆再定性；预警看 ESS ratio，确认看 grad norm。 策略坍塌五条 root cause（第 4 篇，09-02 并入）：σ 归一化不是 bug 也不该一刀切关——单样本权重主要由组内稀罕度而非信号可靠性决定；少数派 advantage 上界 ≈ (G-1)/√G（G=16 时 3.75，不是无限）；Dr. GRPO 去 σ 是按各自 σ 缩放不是乘常数（因此同 lr A/B 会误判”方法无效”）；DAPO 动态采样和 Dr. GRPO 去 σ 处理不同区间（p=0/1 vs 极端但非零方差），先拆零方差构成（全对/全错）再决定。\nclip 的不对称与稀有 token（第 5 篇，09-08 并入）：默认 [0.8, 1.2] 在 log 空间不对称——下侧 (log 0.8 ≈ -0.223) 比上侧 (log 1.2 ≈ +0.182) 多 ~22%，先撞下界；几何对称点 ε = √(1−ε_low)/(1+ε_high) ≈ 0.236（DAPO 用 0.28 是工程近似）；低概率 token 撞上界是 softmax 局部几何的必然（log r ≈ (1−p)·Δ 一阶近似，p=0.05 时 19× 灵敏）；稀有 token 需要约 7.3 次有效更新才能从 0.05 升到 0.3，普通 token 不到 1 次；clip 命中梯度归零要 ratio 与 advantage 同号才会发生（异号时梯度本身就小）；调 clip 不是”开紧/松”，是调几何（5.1 几何对称 / 5.2 控制熵 / 5.3 扫描看 clip_frac_by_sign 与有效 step 比）；熵下降不能单独作为调 clip 的依据——必须配随机 reward 基线对照（系统下降才归因于 clip，组间下降是熵的自然衰减）；截断样本（loss mask）与 clip 是两件事——前者控制样本层（不鼓励长答），后者控制 token 层（防止大更新），统计要分开。\n训练崩溃的分层排查（第 6 篇，09-15 并入）：崩溃的诊断与策略坍塌正交——后者是算法层内在倾向，前者是工程层 0/1 bug（概率口径不一致、mask 写错、数值精度不够、optimizer state 不匹配），没有”更合适的学习率”只有写对和写错。核心原则“前一层不通过，后一层曲线没有解释意义”——按”读 20 条 rollout → 概率一致性 → 梯度累积 → mask → 数值精度 → 优化器与超参 → 奖励”顺序查。第 1 步锁第一个出错 step（最后正常 / 首个异常 / 异常后一步三态保存，比单看崩溃后曲线截图价值大）。1 步最小一致性测试：冻结权重、关 dropout、固定 RNG，同一批 token 跑 A（训练侧）/ B（同侧再跑）/ C（推理侧）——A=B 验训练框架可重复、A=C 验两侧概率一致；硬判据是未训先大面积撞 clip（权重没动就报策略更新）= 必须停训修口径。1.5 步梯度累积等价性：N 条一次反向 vs 1 条累积 N 次比，方向不同或 norm 差 accumulation/world size 整数倍 = 缩放错。2 步 mask 要打逐 token 表（position / token 文本 / 来源 / attention mask / loss mask / 是否有 reward），运行时断言 loss mask 之和 = completion token 数。3 步沿数值路径找第一个 NaN/Inf——bf16 下两 logprob 相减会量化成 0；log-ratio clamp 必须在 exp 之前（先 exp 后 clamp 反向 0 × Inf = NaN，clamp 不是 PPO clip 替代品）。4 步前 4 层通过后才动 optimizer 和 reward——同时记裁剪前/后 grad norm、|Δθ|/|θ|、ratio P95/P99、upper/lower clip fraction，按形状分流（grad clip 每步都中 = 根因未解；checkpoint 恢复即崩 = 回到更早且匹配的 checkpoint 重放）。面试信号：候选人说”先要最后正常 step / 第一异常 batch / 原始 rollout / ratio / mask”才像真排过；上来就读 lr 和 KL 是看板习惯。\n原文：刘明（LLMCat，亚马逊高级数据科学家）“LLM 面试官笔记”系列。本笔记覆盖第 1 篇《如何排查 GRPO 训练问题》（公众号 ｜ 知乎）+ 第 2 篇《如何监控模型的多样性》（知乎）+ 第 3 篇《怎么监控训练信号的健康度》（2026-08-31 发布，知乎未索引到独立链接）+ 第 4 篇《策略坍塌五条 root cause：除以 σ》（2026-09-02 发布，知乎未索引到独立链接）+ 第 5 篇《clip 的不对称与稀有 token》（2026-09-04 发布，知乎未索引到独立链接）+ 第 6 篇《训练崩溃：分层排查》（2026-09-07 发布，知乎未索引到独立链接；“两侧引擎概率不一致的量级”待发布，见 [[#系列追踪]]）。\n笔记地图 [[#排查流程全景|排查流程全景]] [[#排查前的三个基础要求|排查前的三个基础要求]] [[#二分法：策略坍塌 vs 训练崩溃|二分法]] [[#二分法的边界：转化与信号链|二分法的边界（转化 \u0026amp; 信号链四问）]] [[#最常见的错误排查：降 lr|降 lr 陷阱]] [[#快速判断：读 rollout 原文|读 rollout 原文]] [[#四条原则（总结）|四条原则]] [[#策略坍塌的五条 root cause（系列第 4 篇）：除以 σ|策略坍塌 root cause（第 4 篇）]] [[#一、先确定问题发生在哪一步|五条 root cause 全景]] [[#二、从一组具体分数看 σ 到底改变了什么|σ 归一化的代价（少数派放大）]] [[#三、去掉 σ 的本质|去掉 σ（Dr. GRPO）的本质]] [[#四、过滤零方差组的目的|过滤零方差组（DAPO 动态采样）]] [[#五、去掉 σ 和过滤零方差组怎么配合|两者怎么配合（不同区间）]] [[#六、脱敏实验记录|脱敏实验记录]] [[#clip 的不对称与稀有 token（系列第 5 篇）：为什么低概率 token 更容易撞上界|clip 不对称与稀有 token（第 5 篇）]] [[#一、clip 到底在裁什么|clip 到底在裁什么]] [[#1.1 log-ratio 的几何不对称|① log-ratio 几何不对称（22% 偏置）]] [[#1.2 0.25 几何对称点|② 几何对称点 ε 表]] [[#二、低概率 token 为什么更容易撞上界|低概率 token 撞上界的必然性]] [[#2.1 真实更新计算|① softmax 真实更新计算]] [[#2.2 被上界裁掉的 token 概率低，能说明什么|② 撞上界的 token 是稀有 + 想增]] [[#三、稀有 token 的问题|稀有 token 的学习路径（7.3 次有效更新）]] [[#四、命中 clip 后，什么梯度被丢掉了|clip 真正截断的是同号极端]] [[#五、如何调整上下界|调 clip 是调几何（5.1 / 5.2 / 5.3）]] [[#六、为什么”熵在下降”不能单独作为调参依据|熵降不能单独调 clip（随机 reward 基线）]] [[#七、截断样本 vs clip|截断样本 vs clip（mask 与 clip 统计分开）]] [[#训练崩溃的分层排查（系列第 6 篇）：从便宜的 0/1 检查开始|训练崩溃分层排查（第 6 篇）]] [[#零、先锁定”第一个出问题的 step”|零、锁定首个异常 step（三态保存）]] [[#一、第 1 步：先证明新旧概率可以比较|第 1 步 概率一致性（A/B/C 三次计算）]] [[#1.1 最小一致性测试|① 最小一致性测试（未训先撞 clip 必停训）]] [[#1.2 权重同步不能只看”调用成功”|② 权重同步三类检查]] [[#二、第 1.5 步：验证梯度加工流水线|第 1.5 步 梯度累积等价性]] [[#2.1 梯度累积等价性测试|① 累积 vs 一次反向]] [[#2.2 用简单目标测试”梯度能否到达参数”|② 普通 NLL 能到参的底线测试]] [[#三、第 2 步：逐 token 检查 mask 和数据边界|第 2 步 逐 token mask 检查]] [[#3.1 不要只看 mask 的 shape|① 逐 token 表]] [[#3.2 多轮和工具调用最容易静默出错|② 多轮/工具 mask 易错]] [[#3.3 截断样本要同时检查三处|③ 截断样本的三处开关]] [[#四、第 3 步：沿着数值路径找第一个 NaN/Inf|第 3 步 数值路径查 NaN/Inf]] [[#4.1 关键差值和指数运算使用 fp32|① fp32 归约/相减/指数]] [[#4.2 先限制 log-ratio，再取指数|② clamp 必须在 exp 之前]] [[#4.3 几种”限制”不要混在一起|③ 四种 clip/clamp 不能凭数值猜]] [[#4.4 不要用 log(softmax(x) + eps) 掩盖问题|④ 不用 log(softmax+eps) 掩盖问题]] [[#五、前四层通过后，才进入优化器和 reward|第 4 步 优化器与 reward]] [[#5.1 第 4 步：判断是不是更新步子太大|① 更新步子形状分流]] [[#5.2 回滚时把故障证据一起留下|② 回滚留证据]] [[#5.3 第 5 步：沿 reward 到 advantage 的路径检查|③ reward→advantage 路径]] [[#5.4 reward hacking 不能只靠一条曲线确诊|④ hacking 单变量对照]] [[#六、把整套排查压缩成一张决策表|六、决策表（避免跨层解释）]] [[#监控看板（系列第 2 篇）：信号健康度可直接观测|监控看板（第 2 篇）]] [[#1. token entropy：只是个粗糙报警器|① token entropy（报警器）]] [[#2. 组内去重率：熵坍塌最直观的表现|② 组内去重率]] [[#3. pass@k：最关键，但有四个缺口|③ pass@k（四缺口 + temperature 陷阱）]] [[#4. 高不确定性位置的熵：补救平均熵的稀释|④ 条件熵（补稀释）]] [[#5. 跨输入可区分度：本篇最重要的一节|⑤ 跨题可区分度（检索准确率）]] [[#多样性组小结：五组指标|多样性组小结]] [[#信号健康度组（系列第 3 篇）：更工程，也更容易被指标名字骗|信号健康度组（第 3 篇）]] [[#1. 零方差 group 占比：50% 报警线是错的|⑥ 零方差组占比（50% 阈值是错的）]] [[#2. 重要性比与 clip fraction：别把两个方向合并成一个人|⑦ 重要性比 / clip fraction]] [[#3. 两侧引擎的 logprob 差：最容易被忽略，也最致命|⑧ 两侧引擎 logprob 差]] [[#4. 长度、grad norm、reward 分项：已经表现出来的结果|⑨ 长度 / grad norm / reward 分项]] [[#5. ESS：batch 里有多少样本真正起了作用|⑩ ESS（预警）]] [[#信号健康度组小结：按一条顺序看|观看顺序小结]] [[#我的批注|批注 \u0026amp; 行动项]] [[#系列追踪|系列追踪]] 排查流程全景 flowchart TD A[\u0026quot;训练出问题：reward 异常\u0026quot;] --\u0026gt; B[\u0026quot;第一步：抓崩溃前后各 10~20 条 rollout 人工读\u0026quot;] B --\u0026gt; C[\u0026quot;先查工程层（0/1 的 bug）\u0026lt;br/\u0026gt;两侧 logprob 一致性 → loss mask → 精度\u0026quot;] C -- \u0026quot;排除干净\u0026quot; --\u0026gt; D[\u0026quot;再查算法层\u0026lt;br/\u0026gt;改优势估计 / 裁剪 / 奖励设计\u0026quot;] B -- \u0026quot;输出同质化、像模板\u0026quot; --\u0026gt; D D --\u0026gt; E[\u0026quot;还没定位？按信号链四问逐层切\u0026quot;] E --\u0026gt; F[\u0026quot;① 梯度和我认为的 match 吗\u0026quot;] F --\u0026gt; G[\u0026quot;② reward 在测我以为的东西吗\u0026quot;] G --\u0026gt; H[\u0026quot;③ 几条样本在决定更新\u0026quot;] H --\u0026gt; I[\u0026quot;④ 策略还在看题吗\u0026quot;] [!note]- 为什么顺序是”先工程、后算法”？ 工程层问题要么有要么没有（0/1），适合快速穷尽排除；策略坍塌是 GRPO 目标函数的内在倾向——“你什么都没做错，它也会往那个方向走”，不存在”修好”一说，只能设计上缓解。混着查 = 两边都查不干净。\n排查前的三个基础要求 不管从哪个角度切入排查，真正要关心的是三件事：\n先分类再动手。策略坍塌和训练崩溃解决方式差异很大，不可混为一谈。 要有 observability 意识。你有没有监控、监控了什么？更关键的：改了某配置指标动了，怎么知道是配置起了作用，还是恰好这批数据不一样？ 分析每个方案的代价。直接叠各种 PO（DAPO / XXPO）不是专业做法——每个 trick 都有适用前提，叠在一起用，连哪个在起作用都分不出来。 二分法：策略坍塌 vs 训练崩溃 策略坍塌（算法层） 训练崩溃（工程层） 典型现象 reward 还在缓慢涨，输出越来越像模板 某个 step 之后 reward 断崖到 0 曲线特征 entropy 单调下降；pass@1 微涨、pass@k 明显掉 grad norm 突然 10x 尖刺 输出特征 同一 prompt 采 16 条几乎一模一样 复读、乱码、中英混杂、!!!!!! 发生时间 训练中后期，渐进 突发，往往能定位到具体 step root cause 优化目标 / 信号设计 工程一致性（概率不一致、mask 覆盖写错、bf16 精度不够） 解决方式 改优势估计、改裁剪、改奖励 查 logprob 一致性、查 mask、查精度 训练崩溃这类问题调超参永远调不好——它压根不是超参问题，而是 bug。\n二分法的边界：转化与信号链 现实世界是复杂的，二分法只是粗略总结、不严谨，不能只看表格定位问题。\n两类问题会互相转化 一个常见链路：\n熵先一路掉到底 → 每个位置只剩一个答案 → 输出退化成复读 → reward 断崖式暴跌 最后一步看到的结果很容易被归因为训练崩溃，但病根在第一步的策略坍塌。反过来，坍塌的成因里往往混着 reward 设计、一批数据被反复用多轮——这些也不是纯算法问题。\n所以排查姿态要变：不是”确定它是哪类”，而是先按训练崩溃去查工程，排除干净之后才轮到策略坍塌类。面试中能主动指出这两类会互相转化，是很大的加分项。\n时间尺度 ≠ root cause “渐进 vs 突发”和”算法层 vs 工程层”并不一一对应：\n[!example]- 两个反例（表象与病根错位） 反例 1：有效样本数退化——渐进式（几十步的过程），符合策略坍塌”渐进 + 中后期”的表象，但病根在工程/系统层（异步、数据陈旧）。要查的是工程层，不是优化目标。\n反例 2：多轮工具调用崩溃——属于训练崩溃，表现为突发 grad norm 爆炸，但病根在信号设计：工具返回的内容把分布带偏、逐轮复合。不是 mask 或精度问题。\n进阶切法：信号链四问 按信号链区分，四层之间是前提关系，不是并列关系：\n层 问题 查什么 不通过的后果 ① 梯度和我认为的 match 吗 两侧 logprob 一致性、loss mask 覆盖范围、梯度累积等价性 后面三层的所有观测都没有意义 ② reward 在测我以为的东西吗 分项拆解、验证器的 false negative、与训练不同源的 held-out ③④ 在精确地优化一个错的东西 ③ 这一步的更新由几条样本决定 有效样本数、零方差组占比 —— ④ 策略还在看题吗 组内去重、跨输入的可区分度、pass@k —— 这个切分给出了二分法给不出的依赖结构——因为”渐进和突发”之间没有前提关系。多数情况二分法够用；复杂情况按四问切。\n最常见的错误排查：降 lr 缺经验的候选人标准回答：“训练崩溃之后，把 lr 从 xxx 降到 yyy 就好了。”\n降 lr 确实”有效”：它把所有事情都拖慢了，包括 bug 本身——原来几百 step 就爆，现在几千 step 才爆。 如果训练本来只跑几千 step，你会以为问题解决了。这不是修好，是把爆炸推迟到了你的视野之外。 同类回答：加大 grad clip 力度、拉长 warmup、翻倍 batch size——都能让曲线变好看，因为都在降低单步实际更新量。 [!warning] 判断标准 修复手段是「让每一个 step 走得更小」→ 大概率没定位到根因，只是把它稀释了。\n但降 lr 当诊断手段完全没问题：这是个合理的二分实验——信任域问题随 lr 线性缓解，bug 不会。\n快速判断：读 rollout 原文 抓崩溃前后各 10~20 条 rollout，人工一条条读。三类问题在原文里一眼可见：\nreward hacking：结构完整、格式漂亮，但内容在绕开真正的任务——该解题的地方在复述题干、该给答案的地方给了模板化的推理过程。 大量截断：回答在句子中间断掉、JSON 没闭合、推理写到一半没了。 乱码 / 复读：!!!!!!、同一个短语重复几十遍、中英混杂到读不通。 而这三类在 reward 曲线上可能都只表现为”掉到 0”。从曲线上看，不如直接看原文。\n四条原则（总结） 先分类，再动手。坍塌和崩溃的排查路径完全不同；顺序固定：先排工程层（0/1），排干净再动算法层。 分类法本身要能被质疑。二分是按时间尺度切的，有用但不严谨；按信号链切（梯度对不对 → reward 在测什么 → 几条样本在决定 → 还看不看题）更严格，因为四层之间是前提关系。 降 lr 是诊断，不是解释。任何「让每一步走得更小」的手段，都只是在稀释根因。 读原文在读曲线之前。3 分钟人工阅读能完成的分类，曲线上要花半小时，还可能分错。 策略坍塌的五条 root cause（系列第 4 篇）：除以 σ [!info] 第 4 篇核心 上一篇里”训练 reward 涨、pass@1 涨但 pass@16 跌破 base”——这是策略坍塌的典型现象。本篇追其中一个具体问题：一组回答已经打完分，这些分数是怎么变成更新权重的？ 主线分析 σ 归一化（GRPO 默认 advantage 公式里的除以 σ）具体改变了什么、代价是什么。\n五条 root cause 对应训练流程的不同环节、破坏不同东西：不要看到坍塌就直接上 clip-higher——改错地方等于白改。\n一、先确定问题发生在哪一步 信号链环节 可能的问题 破坏了什么 给回答打分 reward 太粗 大量 group 全对/全错，组内无可比信息 reward → advantage 除以 σ 接近全对/全错时少数派获大单样本权重 advantage → 参数更新 clip 不对称 低概率、探索的 token 更易被裁（第 5 篇） 限制策略漂移 KL 配置 太强学不动；实现错误时无真实约束 重复使用 rollout 数据变旧 当前策略已变，却反复学旧策略的产物 reward 已让多数 group 没方差时问题在 clip 之前；ratio 混进陈旧数据时单改 σ 也解决不了。本篇主线是第二行：reward 已有高低之分，但经过组内标准化后，更新权重是否符合我们对信号可靠性的判断。σ 归一化不是坍塌的唯一原因，也不是看到坍塌就该关闭的开关。\n标准 GRPO advantage：\n\\[\\hat{A}_i = \\frac{r_i - \\mu}{\\sigma}\\]\n直觉：正数提高该回答 token 的概率，负数压低；绝对值越大对本次更新影响越大。减均值负责”谁比同组平均更好”，除以 σ负责”这组差异按多大音量进入训练”——但 σ 只知道分数有多分散，不知道这些分差究竟代表可靠能力，还是偶然采样和 reward 噪声。\n二、从一组具体分数看 σ 到底改变了什么 二值 reward（对=1、错=0）最能说明问题。设组内 10 条：9 对 1 错，μ=0.9，σ=0.3。\n仅减均值：每条正确回答分差 = +0.1；唯一错误 = −0.9。少数派离均值最远，单样本权重已经更大；正负和仍为 0。\n除以 σ 后整体放大：每条正确 = +0.1/0.3 ≈ +0.33；唯一错误 = −0.9/0.3 = −3.00。少数派拿到最大绝对权重。\n不同 p 下的放大（用总体 σ，便于观察）：\n组内正确率 p 正确回答 A 错误回答 A 被重点放大的样本 0.5 +1.00 −1.00 平衡 0.9 +0.33 −3.00 少数的错误回答 0.1 +3.00 −0.33 少数的正确回答 除以 σ 后，组内少数派拿到最大权重：难题里偶然答对的 A 随 p 趋近 0 放大，简单题里偶然答错的 A 随 p 趋近 1 放大（原文配图） 核心洞察：单样本权重主要由”在组内有多稀少”决定，而不是由”我们对其有多确信”决定。难题里唯一答对的可能是真探索、也可能只是猜中；简单题里唯一答错可能暴露稳定缺陷、也可能只是采样失误——σ 归一化无法区分，只按组内分数的位置分配权重。\n2.1 通用公式 二值 reward 下 σ = √[p(1−p)]，所以：\n\\[A_{对} = \\frac{1-p}{\\sqrt{p(1-p)}} = \\sqrt{\\frac{1-p}{p}}, \\qquad A_{错} = -\\sqrt{\\frac{p}{1-p}}\\]\n结论：p 越接近 0，正确回答 A 越大；p 越接近 1，错误回答 A 越大；p≈0.5 时两侧平衡。\n少数派绝对权重也可写成 \\(\\sqrt{(1-p)/p}\\)（错误 \\(-\\sqrt{p/(1-p)}\\)）。同一标准化两个观察角度：\n题与题之间：接近全对/全错的 group σ 越小，原始分差被换算到更大尺度。 一道题内部：少数派离均值最远，单样本绝对权重最大。 最终风险取决于这些少数派样本是否可靠：reward 准确时大权重可能有用；reward 有噪声时它会把偶然胜出推得很重。\n2.2 上限由 group size 决定，不是无限 公式在 p→0/1 时看似发散，但 group size G 有限。极端是 1 少数派 + (G−1) 多数派：\n无偏 std（主流框架）：少数派上界 \\(|A_{min}| \\le (G-1)/\\sqrt{G}\\)——G=16 → 3.75，G=8 → 2.47。 总体 std：少数派上界约 \\(\\sqrt{G-1}\\)。 放大明显但不是无限大——G 决定的硬上限；ε 只会把数值压小，不能突破它。\n这条上界对连续 reward 同样成立（均值和 σ 来自同一组，无论 reward 是连续值还是分项加权和，单样本权重仍受 G 限制）。\n连续 reward 的额外风险：σ 归一化抹掉了原始分差的绝对大小。一组回答总分都在 0.5000、唯一一条 0.5001——差异可能是打分器噪声，但 σ 同样很小时除以之后仍可能得到接近 3 的 advantage。多个 reward 加权后，正确性得分全相同时，最后一点方差可能只来自标点/长度/某个弱 reward 分项——σ 归一化仍把它扩展成正常强度信号。\n[!warning] 真正该监控的两件事 ① 标准化后的 advantage 有多大； ② 标准化之前的 reward range、σ、以及各 reward 分项的方差。\n只看前者，看不出模型正在追逐一丁点分数噪声。\n三、去掉 σ 的本质（Dr. GRPO） Dr. GRPO：advantage 只减组均值、不除以 σ——\\(A_i = r_i - \\mu\\)，落在 \\([r_{min}-\\mu,\\ r_{max}-\\mu]\\) 内，二值 reward 下即 \\([-1, +1]\\)。在 9 对 1 错的例子里：\\(A_{对} = +0.1\\)，\\(A_{错} = -0.9\\)，权重从 ±0.33 / ±3.00 回到 +0.1 / −0.9。\n去掉 σ 并不会让正负权重相等，也不会消除少数派效应（少数派仍离均值最远）。它真正改变的是：这道题不再因为 σ 小而被强行拉到统一尺度。但同时改变了两件事：\n不同 prompt 不再各自被强行缩放到相近方差，原始 reward 分差重新决定更新强度； 二值 reward 下接近全对/全错的 group 会明显变弱，整体有效更新幅度通常也下降。 不考虑 ε 时，同一个 group 的两种 advantage 满足：\n\\[\\hat{A}_{去\\sigma} = \\sigma \\cdot \\hat{A}_{原}\\]\n也就是说，去掉 σ 不是给所有样本乘一个常数，而是按各自 σ 缩放——σ 小的 group 被压得更多，σ 大的被压得较少。\n这造成容易误判的 A/B：\n保留 σ 的 run 学得快，但可能更容易放大少数派噪声； 去掉 σ 的 run 学得慢，看上去像方法无效，其实一部分只是整体更新变弱了。 [!tip] 至少跑三条对照曲线 1. 原始 σ 归一化； 2. 去掉 σ，保持原 lr； 3. 去掉 σ，同时按观测到的平均 σ 适当补偿 lr。\n第三条不是机械地乘常数——全局 lr 无法完全抵消每个 group 不同的 σ。这条对照只帮分离”权重结构变了”与”整体步子变小了”两个效应；具体补多少仍要结合 grad norm、KL、实际参数更新量判断。\n3.1 σ 归一化的价值 归一化在做逐 prompt 的自适应缩放：不管原始分差是 0.001 还是 1，都尽量换算到相近尺度，让不同 prompt 给出接近强度的训练信号。\n但 reward 方差小有两种完全不同的含义：\n分差小但排序可靠 → 值得学； 分差小是因为大家几乎一样，剩下只是 reward 噪声或次要分项。 σ 归一化无法区分两者。去掉它会降低第二类噪声的影响，但也会让”差距小却可靠”的偏好信号变弱。这是 trade-off，不是一次没代价的 bug 修复。\n3.2 σ 和长度归一化的区别 Dr. GRPO 还删了回答长度归一化 \\(1/\\lvert o_i \\rvert\\)。两项对应不同偏置：\n除以 σ：改变不同难度题和组内少数派的权重 → difficulty bias。 除以回答长度：让长回答的单位 token 惩罚更轻，尤其减弱长错误回答的负 advantage → length bias。 论文把长度失控归因于 \\(1/\\lvert o_i \\rvert\\) 而不是 σ，两项一起删除但没单独消融。遇到错误回答越来越长时，先检查 loss 是否按回答长度做了归一化。\n四、过滤零方差组的目的（DAPO 动态采样） 一组 G 条全对或全错时 σ=0，无论是否除以 σ advantage 都是 0——去掉 σ 只能处理”仍有方差但正确率极端”的 group，处理不了严格的全对/全错。\nDAPO dynamic sampling 走另一条路：丢掉零方差 group、继续采样直到凑够指定数量的有效 group，让 batch 里每个 group 都至少含两种 reward。提升了训练数据利用率，但不是没有成本。\n4.1 代价一：生成开销取决于数据难度 零方差少时，重采样可被流水线隐藏；六七成 group 都是全对或全错时，补齐 batch 要生成大量额外回答。所以不要直接照搬论文时间开销结论——需要同时记录：零方差组比例、为补齐 batch 实际生成了多少 group、生成能否与训练并行。\n4.2 代价二：训练目标的权重变了 零方差 group 本来就没任务梯度，问题不在”删掉了什么信号”，而在”删掉后用什么补进来”。例：普通采样一次得 10 个 group，只有 4 个有高低分；动态采样补到 10 个有效 group → 每 step 看到的有效比较更多、更新通常更强。\n但不同 prompt 被保留的概率并不相同：\n中等难度题更易同时采到对和错 → 更频繁进入 batch； 几乎全对/全错的题要反复重采样 → 一直进不来。 训练数据因此被重新加权。至于零梯度 token 是否仍占 loss 归一化分母，要看框架 reduce 方式——这影响动态采样把更新放大多少，但不改变核心事实：每步训练的 prompt 分布已经变了。\n4.3 代价三：已掌握的能力更容易被看不见 常见说法”全对题被过滤、模型没梯度、最后遗忘”因果不准确——全对 group 在原始 GRPO 里 advantage 也是 0，本就没任务梯度。\n真正的变化是：补满有效 group 后，中等题/难题在每步更新中占比提高。模型持续沿这些题的方向移动，可能逐渐离开已掌握的能力区域。\n更麻烦的是：动态采样让 batch 表面一直满着，看不出底层有效 prompt 池正在缩小。需要单独保留一个已掌握题目的回归集，定期检查简单能力是否退化。\n[!note]- 作者自承 “我个人通过机制推断，我自己其实没有复现出来过’训练 reward 上涨、简单集准确率同步下降’的完整曲线。正是因为训练 batch 看不见被过滤的简单题，所以我们才需要额外回归集来验证，而不是直接把退化当成既定事实。”\n几十道题的小回归集只能做报警器，不能做精确量尺——样本太少时几个点的上下波动未必有意义。\n4.4 零方差很多时，先分清是全对还是全错 相同零方差占比，处理方向可能完全相反：\n全对为主：数据对当前模型太简单 → 应增难度/区分度。 全错为主：可能是验证器过严 / false negative，或题目超出模型能力 → 应先检查判题，再考虑 SFT/课程/中间难度。 只看到”零方差占 70%“不能决定换不换数据，关键是这 70% 由全对还是全错组成。给全错 group 注入一条可信的满分参考回答可以人为制造组内比较，但改变了采样分布和训练目标，不能当作无偏修复。全对 group 需要反方向的对照样本，不能注入同样的满分。\n五、去掉 σ 和过滤零方差组怎么配合 两个方法常被说成同一问题的两种解法，其实处理不同区域：\ngroup 状态 去掉 σ 过滤零方差组 全对或全错（p=0/1） 无作用（分子=0） 有作用：移出重采样 接近全对/全错但仍有方差 有作用：压低少数派极端权重 不命中 正确率接近 0.5 主要改变整体尺度 通常不命中 两个手段解决的是不同区间：去掉 σ 与过滤零方差组互补、不重叠、也不是同一旋钮的两端（原文配图） 两个例子记住：\np=1：所有回答都对，advantage 分子全是 0 → 去不去 σ 都没区别，只有过滤/换题/引入对照才能得到比较信号。 p=0.9：仍有错误回答，过滤不处理；二值 reward 下，去掉 σ 把错误回答权重从 −3.00 降回 −0.9。 DAPO 选动态采样、Dr. GRPO 选去掉 σ——不是对同一个开关意见相反，而是瞄准两个不同的失效区间。\n[!tip] 实践落地顺序 1. 先统计零方差 group 的构成：全对多少、全错多少； 2. 再看非零方差 group 的正确率分布，是否大量集中在 0.1、0.9 两端； 3. 全对多 → 优先调数据难度；全错多 → 先检查验证器和 base 能力； 4. 两端但非零方差的 group 多 → A/B 去掉 σ，并同时做 lr 补偿对照； 5. 确实需要动态采样时，同时监控额外生成成本和简单题回归集。\n面试追问这个组合很常见——能说清”p=0/1 时去掉 σ 没用、p≈1 时过滤没用”，比背方法名更能说明候选人理解了实现。\n六、脱敏实验记录 “我有一次观察到明显的坍塌。下一轮同时做了三项修改：刷新参考策略、减小 KL 系数、把训练题型混得更杂。第二轮没有再次出现同样的坍塌。”\n这个结果只能说明：换了一整套配方以后现象消失了。不能说明三项里哪一项有效，更不能证明三项都有效——没有做单变量消融。\n如果要提假设，作者更怀疑题型混合度起了主要作用：题型更杂 → 更多 group 能产生 reward 方差 → 有效信号不再只集中在少数题型。但仍是机制推测。\n刷新参考策略和减小 KL 都会放松对策略漂移的限制，从直觉上甚至未必有利于保留多样性。因此不能因为第二轮没塌就把这两项写成”抗坍塌配方”——验证原因仍需分别 A/B。\n总结 策略坍塌不只是”熵变低”——是训练把概率越来越集中到少数路径，同时丢掉原本存在的其他解法。要修它，先判断是哪一环让比较信号失真。\nσ 归一化把每个 prompt 的 reward 分差重新缩放。接近全对/全错时，少数派离均值最远 → 拿到最大单样本权重。权重不是无限的：无偏 std、极端比例下上界 ≈ (G−1)/√G（G=16 → 3.75）。该上限对连续 reward 同样成立。 连续 reward 的额外风险：标准化抹掉原始分差的绝对大小——一点打分器噪声或无关 reward 分项的细小差别也会被扩展成正常强度 advantage。监控必须同时看标准化前的 reward range / σ / 各分项。 去掉 σ（Dr. GRPO）的本质是保留原始 reward 分差：分差小的 group 少更新，分差大的多更新。能压低难度两端 group 的极端权重，但通常也会降低总体更新幅度——A/B 时需把”权重结构变了”和”整体步子变小了”分开观察。 过滤零方差 group（DAPO 动态采样）解决另一件事：全对/全错时根本没有比较信号。能提高有效 batch 比例，却增加生成开销、重新加权 prompt 分布、并让有效题池的收缩更难被看见。 实际不是”去 σ 和动态采样二选一”，而是先看 group 落在哪个区间：严格零方差、接近零方差但仍有少数派，还是中间难度。不同区间不同修法、不同代价。 clip 的不对称与稀有 token（系列第 5 篇）：为什么低概率 token 更容易撞上界 [!info] 第 5 篇核心 默认 clip 区间 [0.8, 1.2] 在数值上看似对称，在 log 空间却不对称——下侧宽度 (log 0.8 ≈ -0.223) 比上侧 (log 1.2 ≈ +0.182) 多 ~22%；更要紧的是这与低概率 token 的几何形态叠加在一起：低概率 token 对更新更敏感，先撞的不是”对称侧”而是 softmax 局部几何规定的那一侧。修法不是”开紧/松”，是调几何。同时提醒：熵在下降本身不能作为调 clip 的依据——必须配随机 reward 基线对照，否则容易把熵的自然衰减误判为 clip 过紧。\n一、clip 到底在裁什么 GRPO 的重要性比 \\(r_t(\\theta)=\\frac{\\pi_\\theta(a_t|s_t)}{\\pi_{\\theta_{\\text{old}}}(a_t|s_t)}\\)，clip 的标准实现是 PPO-style：\n\\[L^{\\text{CLIP}}(\\theta) = \\mathbb{E}_t\\Big[\\min\\big(r_t(\\theta)\\cdot A_t,\\; \\text{clip}(r_t(\\theta),\\, 1-\\varepsilon_{\\text{low}},\\, 1+\\varepsilon_{\\text{high}})\\cdot A_t\\big)\\Big]\\]\n\\(\\min\\) 把两个量取小：\n未裁剪项：\\(r_t \\cdot A_t\\)，梯度无限制 裁剪项：\\(\\text{clip}(r_t, 1{-}\\varepsilon_{\\text{low}}, 1{+}\\varepsilon_{\\text{high}}) \\cdot A_t\\)，超出区间后用区间端点代替 min 在不同符号下的行为如下表（默认 \\([\\varepsilon_{\\text{low}}, \\varepsilon_{\\text{high}}] = [0.2, 0.2]\\)，即 clip 区间 \\([0.8, 1.2]\\)）：\n\\(r_t\\) 位置 \\(A_t\\) 符号 未裁剪项 裁剪项 min 取谁 梯度 \\(r_t \u0026lt; 0.8\\) \\(A_t \u0026gt; 0\\)（想增） \\(0.8 \\cdot A_t\\) \\(r_t \\cdot A_t \u0026lt; 0.8 A_t\\) 裁剪项（\\(r_t A_t\\)） 仍计算 \\(r_t \u0026lt; 0.8\\) \\(A_t \u0026lt; 0\\)（想减） \\(r_t \\cdot A_t \u0026gt; 0.8 A_t\\)（\\(A_t\\) 负、\\(r_t\\) 小 → 乘积偏 0） \\(0.8 \\cdot A_t\\) 裁剪项（\\(0.8 A_t\\)） 仍计算 \\(r_t \u0026gt; 1.2\\) \\(A_t \u0026gt; 0\\) \\(r_t \\cdot A_t \u0026gt; 1.2 A_t\\) \\(1.2 \\cdot A_t\\) 裁剪项（\\(1.2 A_t\\)） 仍计算 \\(r_t \u0026gt; 1.2\\) \\(A_t \u0026lt; 0\\)（想降） \\(1.2 \\cdot A_t\\)（负得更大） \\(r_t \\cdot A_t \u0026lt; 1.2 A_t\\)（负得更小） 未裁剪项（\\(r_t A_t\\)） 仍计算 \\(0.8 \\le r_t \\le 1.2\\) 任意 未裁剪项与裁剪项相等 — 未裁剪项 无 clip 干扰 [!warning] 关键修正 clip 命中梯度不会归零——只是上限用区间端点代替。真正梯度归零要 min 取到的项作为常数对待——这只在 \\(r_t\\) 落在区间外且与 \\(A_t\\) 同号时发生（表中第 1、3 行）。\n\\(A_t \u0026gt; 0\\)、\\(r_t \u0026lt; 0.8\\)：想增但增不动 → 真被截断，梯度为 0 \\(A_t \u0026lt; 0\\)、\\(r_t \u0026gt; 1.2\\)：想减但减不动 → 真被截断，梯度为 0 \\(A_t \u0026lt; 0\\)、\\(r_t \u0026lt; 0.8\\)（第 2 行）：未裁剪项已经很小，clip 后只是用 \\(0.8 A_t\\) 替代，梯度照算 \\(A_t \u0026gt; 0\\)、\\(r_t \u0026gt; 1.2\\)（第 4 行）：未裁剪项更大，“clip 限制”被绕开，梯度照算 把”上界/下界被裁”理解为”概率被绑死”是错的——min 的非对称性意味着 clip 实际只在期望方向与运动方向相反时起作用。这也是为什么很多实现报告 clip_frac 很高、loss 仍在下降：因为命中里很大一部分是第 2/4 行（梯度照算）。\n1.1 log-ratio 的几何不对称 ![Pasted image 20260908160131.jpg]\n图 1：默认 [0.8, 1.2] 在 ratio 空间看起来对称，转到 log 空间并不对称——下侧宽度比上侧多 ~22%。\n数值 0.8 和 1.2 距 1 的”距离”相同（都差 0.2），但 log(0.8) ≈ -0.223、log(1.2) ≈ +0.182：\n下界宽度：\\(|\\log 0.8| \\approx 0.223\\) 上界宽度：\\(|\\log 1.2| \\approx 0.182\\) 比例：\\(0.223 / 0.182 \\approx 1.225\\) ——下界宽度比上界多 ~22.5% 更一般的，若区间为 \\([1{-}\\varepsilon_{\\text{low}},\\; 1{+}\\varepsilon_{\\text{high}}]\\)：\n\\[\\frac{\\log(1/(1-\\varepsilon_{\\text{low}}))}{\\log(1+\\varepsilon_{\\text{high}})} = \\frac{-\\log(1-\\varepsilon_{\\text{low}})}{\\log(1+\\varepsilon_{\\text{high}})}\\]\nTaylor 展开：\\(-\\log(1-x) \\approx x + x^2/2 + x^3/3 + \\cdots\\)，\\(\\log(1+x) \\approx x - x^2/2 + x^3/3 - \\cdots\\)。当 \\(\\varepsilon_{\\text{low}} = \\varepsilon_{\\text{high}} = \\varepsilon\\) 时，比值 \\(\\approx 1 + \\varepsilon + O(\\varepsilon^2)\\)——总是略大于 1。即默认设置在 log 空间就是”下宽上窄”，下界更难命中（ratio 需下降 22% 才到下界，上升 18% 就到上界）。\n1.2 0.25 几何对称点 真正的几何对称是两侧 log 距离相等：\n\\[-\\log(1-\\varepsilon_{\\text{low}}) = \\log(1+\\varepsilon_{\\text{high}})\\]\n\\[\\Rightarrow \\varepsilon_{\\text{high}} = \\frac{\\varepsilon_{\\text{low}}}{1-\\varepsilon_{\\text{low}}}\\]\n\\(\\varepsilon_{\\text{low}}\\) 对称所需 \\(\\varepsilon_{\\text{high}}\\) 区间 0.20 0.25 [0.80, 1.25] 0.25 0.333… [0.75, 1.333] 0.28（DAPO） 0.389 [0.72, 1.389] 0.32 0.471 [0.68, 1.471] DAPO 的 [0.72, 1.389] 不是 0.28 对称点而是经验选——作者说”为防止熵坍塌扩了上界”，是有意保留的几何不对称。\n二、低概率 token 为什么更容易撞上界 重要性比的灵敏度随 token 概率下降而放大。一阶近似：\n设参数更新 \\(\\theta \\to \\theta + \\eta \\nabla_\\theta \\log \\pi_\\theta(a_t|s_t)\\)，对数概率增量\n\\[\\Delta \\log \\pi_\\theta(a_t|s_t) \\approx \\eta \\cdot \\|\\nabla\\|^2 \\cdot (1 - p_t)\\]\n其中 \\(p_t = \\pi_{\\theta_{\\text{old}}}(a_t|s_t)\\)。因此重要性比变化\n\\[\\log r_t \\approx \\log \\pi_\\theta(a_t|s_t) - \\log \\pi_{\\theta_{\\text{old}}}(a_t|s_t) \\approx (1-p_t) \\cdot \\eta \\cdot \\|\\nabla\\|^2\\]\n\\(p_t\\) 一阶近似系数 \\((1-p_t)\\) 含义 0.05 0.95 灵敏度近满 0.30 0.70 仍高 0.60 0.40 中等 0.90 0.10 已稳定 直觉：softmax 概率低的 token 对 logits 变化敏感——一个微小梯度就能把它推过 clip 边界。概率高的 token 已经接近饱和，几乎不可能被同幅度更新撞出上界。\n2.1 真实更新计算 实际更新受 \\(\\text{softmax}(z)\\) 局部几何约束。设 logits \\(z\\) 对应目标 token \\(a\\)，\\(z_a\\) 上升 \\(\\delta\\)，其他 logits 不变：\n\\[\\pi_\\theta(a|s) = \\frac{e^{z_a + \\delta}}{e^{z_a + \\delta} + \\sum_{j \\ne a} e^{z_j}} = \\frac{e^\\delta}{e^\\delta + (1-p)/p}\\]\n因此\n\\[r = \\frac{\\pi_\\theta(a)}{\\pi_{\\theta_{\\text{old}}}(a)} = \\frac{p\\, e^\\delta}{p\\, e^\\delta + (1-p)} = \\frac{p}{p + (1-p)\\,e^{-\\delta}}\\]\n这个函数在 \\(p\\) 小、\\(\\delta \u0026gt; 0\\) 时迅速超过 1.2：\n\\(p\\) \\(\\delta = 0.5\\) \\(\\delta = 1.0\\) \\(\\delta = 2.0\\) 0.05 \\(r \\approx 1.27\\)（撞上界） \\(r \\approx 1.59\\) \\(r \\approx 2.41\\) 0.30 \\(r \\approx 1.15\\) \\(r \\approx 1.34\\) \\(r \\approx 1.81\\) 0.60 \\(r \\approx 1.06\\) \\(r \\approx 1.15\\) \\(r \\approx 1.34\\) 0.90 \\(r \\approx 1.01\\) \\(r \\approx 1.02\\) \\(r \\approx 1.05\\) [!tip] 一句话总结 clip 上界被撞 = 低概率 token + 正 advantage + 同号更新——三个条件都要满足。前两个是数据/算法给的，第三个由 logits 局部曲率决定。低概率 token + 正 advantage 在 GRPO 中系统性频繁（少数派 token 通常是新出现的解法），所以”撞上界”几乎是个结构性现象，不是偶发。\n2.2 被上界裁掉的 token 概率低，能说明什么 被上界裁掉意味着：\n该 token 在老策略下是小概率事件——它本来出现就少 新策略想把它推上去——大概率是新出现的有效解法 被 clip 阻止继续推——意味着这个方向学不到更多 后果是稀有但可能正确的 token 学不动。但这未必是 bug——多数时候稀有 token 稀有是有原因的（拼写错、句法奇怪、对解题无用），clip 阻止它们过度放大反而是想要的。\n三、稀有 token 的问题 稀有 token 想从概率 0.05 长到 0.3，假设每次有效更新提升对数概率 0.3（合理估计）：\n\\[\\Delta p \\approx (1-p) \\cdot \\Delta \\log p \\approx (1-0.05) \\cdot 0.3 = 0.285\\]\n一次有效更新 p 从 0.05 到 0.33——但这是理想情况，实际还要 clip 介入、还要 advantage 不为 0。保守估计每次有效更新提升 \\(p\\) 约 0.03~0.04，要 7~10 次。\n更现实的估计是反过来想：0.05 → 0.3 需要 \\(p\\) 增长 6 倍，即 \\(\\log p\\) 增长 \\(\\log 6 \\approx 1.79\\)。按一次有效更新给 0.25 计，约 7.3 次有效更新。\n对比：0.6 → 0.9，\\(\\log p\\) 增长 \\(\\log 1.5 \\approx 0.405\\)——不到 2 次有效更新。频次 token 撞上界只是单步误差，稀有 token 撞上界是断它整个学习路径。\n四、命中 clip 后，什么梯度被丢掉了 回到 PPO min 公式：\n\\[L^{\\text{CLIP}} = \\min\\big(r_t A_t,\\; \\text{clip}(r_t, 1{-}\\varepsilon, 1{+}\\varepsilon) A_t\\big)\\]\n梯度\n\\[\\frac{\\partial L^{\\text{CLIP}}}{\\partial \\theta} = \\begin{cases} A_t \\cdot \\frac{\\partial r_t}{\\partial \\theta} \u0026amp; \\text{若} \\min \\text{取未裁剪项} \\\\ 0 \u0026amp; \\text{若} \\min \\text{取裁剪项且裁剪项常数化} \\\\ \\text{端点梯度} \u0026amp; \\text{若} \\min \\text{取裁剪项且端点代入} \\end{cases}\\]\n真正梯度归零只在第 2 种情况——“裁剪项常数化”。这要求 min 取裁剪项（即 \\(r_t\\) 在区间外 + \\(r_t A_t\\) 大于裁剪后的 \\(A_t\\) 端点值），且梯度对 \\(r_t\\) 求导后常数化。\n由 §一表分析：\n\\(A_t \u0026gt; 0\\)、\\(r_t \u0026gt; 1.2\\)：第 4 行——min 取未裁剪项（\\(1.2 A_t\\)），梯度照算！ \\(A_t \u0026lt; 0\\)、\\(r_t \u0026lt; 0.8\\)：第 2 行——min 取裁剪项（\\(0.8 A_t\\)），梯度照算（\\(0.8 A_t\\) 仍是 \\(A_t\\) 的常数倍） \\(A_t \u0026gt; 0\\)、\\(r_t \u0026lt; 0.8\\)：第 1 行——min 取未裁剪项（\\(r_t A_t\\)），梯度照算（被允许继续减） \\(A_t \u0026lt; 0\\)、\\(r_t \u0026gt; 1.2\\)：第 3 行——min 取未裁剪项（\\(r_t A_t\\)），梯度照算（被允许继续加） 修正：\n真正的”截断”只发生在 \\(A_t \u0026gt; 0\\) 且 \\(r_t \u0026gt; 1.2\\)（想增但截断）——上界 真正的”截断”只发生在 \\(A_t \u0026lt; 0\\) 且 \\(r_t \u0026lt; 0.8\\)（想减但截断）——下界 也就是说 clip 真正阻止的是 ratio 往 advantage 同方向走过头，不会阻止 ratio 往 advantage 反方向走（反方向走本身对 loss 是有利的——min 取更小的 \\(r_t A_t\\)，未裁剪项本来就更小，clip 不会更小）。\n[!warning] clip 命中方向 简化为：clip 截断与 \\(A_t\\) 同号的 ratio 极端——想涨不让涨太多，想跌不让跌太多。不会截断 ratio 与 \\(A_t\\) 异号的极端（那是 loss 主动选的小项）。\n五、如何调整上下界 调整 clip 不是”开紧”或”松”，是调几何：\n5.1 调成几何对称 把 [0.8, 1.2] 换成 [0.84, 1.16]——log 0.84 ≈ -0.174，log 1.16 ≈ +0.148，比值 1.18（原 1.22）。想要完全对称，按 §1.2 表换 [0.75, 1.25] 或 [0.8, 1.25]。\n适合场景：怀疑 clip 下界比上界更限制某些 token 类别；想消除”先撞下界”的不对称。\n5.2 控制熵 若想鼓励探索（避免熵坍塌），放宽上界（如 DAPO 的 [0.72, 1.389]，把上界调到 ~1.4），让概率上升更自由；鼓励利用（已坍塌想回拉），压紧上下界，让所有大幅更新都被截断——但代价是有效 step 比例降低。\n5.3 扫描时看什么 调完 clip 不要只看 reward 曲线——看：\nclip_frac_by_sign：上界被裁次数 / 下界被裁次数 / 总裁次数，分开计 有效 step 比例（unclipped_step / total_step）：clip 命中后还有多少 step 在更新 A_t \u0026gt; 0 时 clip_frac 与 A_t \u0026lt; 0 时 clip_frac——两者都高说明 clip 在限制学习；一边高说明单边过紧 不是看 clip_frac 一个数——一个聚合数把上下界合并就把诊断信息丢了。\n六、为什么”熵在下降”不能单独作为调参依据 ![Pasted image 20260908160132.jpg]\n图 2：random reward baseline 控制实验——熵下降必须和随机 reward 基线比较才能归因于 clip。\n直觉：训练过程中熵本来就在下降——softmax 概率越来越集中在高 reward 路径上，是 RL 训练的固有倾向。clip 只影响”熵下降的速度”，不影响”熵是否下降”。\n所以：\n看到熵在下降 → 不能断定”clip 过紧限制了探索” 看到熵稳定 → 不能断定”clip 设置完美” 看到熵暴涨 → 反而可能是真问题（policy 远离 reference、KL 失控） 判断 clip 对熵的实际影响：配随机 reward 基线——同一份 rollout 数据，reward 随机打乱（破掉相关性但保留分布），看熵下降曲线。如果随机 reward 下熵也在类似速率下降，说明熵下降不归因于 clip 设置；只有”真 reward 下熵降速明显更快”才有调 clip 的依据。\n[!tip] 实战做法 在训练脚本里同时跑一组 random_reward_baseline——同一个 prompt 同一组回答，reward 随机打乱。两条熵曲线对比：差距 = clip 与 reward 的耦合效应；平行 = 熵下降是熵本身的演化。\n七、截断样本 vs clip 容易混淆的两个机制：\n截断样本（loss mask）：训练样本层，把超长/不合规的回答整个 mask 掉（不参与 loss 计算） clip：token 层，对单个 token 的更新做幅度限制 7.1 mask 不会鼓励长回答 loss mask 的语义是”这个回答不参与训练”——不是”截断到某长度”。如果用 mask 把超过 4096 token 的回答 mask 掉，模型不会学到”回答短一点就能拿到 reward”——只会学到”长回答不进入 loss 也不被鼓励”。\n要让模型学短答，奖励设计里需要显式惩罚长度（如 Dr. GRPO 的长度归一化），而不是靠 mask。\n7.2 mask vs advantage 的统计口径 mask 后留在 batch 里的样本：\n比例变了：原本 100 条 mask 掉 20 条 → 有效 batch 80 → 有效样本减少 样本分布变了：被 mask 掉的可能恰好是”答得长但对了”的样本 → 留下”短答”的比例上升 → 统计意义上”短答更受鼓励”——这是 mask 的副作用，不是设计意图 advantage 计算未变：GRPO 在 prompt 内归一化——同一 prompt 内 mask 与否不影响 advantage 统计时要分开：\n截断率：被 mask 掉的样本 / 总样本 → 监控 mask 副作用 clip_frac：clip 命中的 token / 总 token → 监控 clip 副作用 两件事不要合并——它们影响的是不同环节（数据层 vs 优化层） 总结 默认 [0.8, 1.2] 数值对称、log 不对称，下侧宽 ~22%——任何调参前先意识到这个偏置。 低概率 token 撞上界是 softmax 几何必然——(1-p) 系数放大灵敏度，不是偶发；要判断”是不是 bug”看 token 是否稀有且正确。 clip 真正截断的是 ratio 与 advantage 同号的极端——异号时 clip 被绕开（未裁剪项更小）。clip_frac 高但 loss 仍在降，多半命中的是异号情形。 调 clip 是调几何：5.1 几何对称 / 5.2 控制熵（宽上界鼓励探索，紧上下界鼓励利用）/ 5.3 扫描看 clip_frac_by_sign 与有效 step 比。 熵下降本身不能作为调 clip 的依据——配随机 reward 基线对照：差距 = clip 与 reward 耦合效应；平行 = 熵自然衰减。 截断样本（mask）与 clip 是两件事——前者样本层（不鼓励长答是 mask 副作用而非设计意图），后者 token 层；统计分开计，不要合并。 训练崩溃的分层排查（系列第 6 篇）：从便宜的 0/1 检查开始 [!info] 第 6 篇核心 前面几篇（1、4、5）讲策略坍塌——reward 可能还在涨，但回答逐渐变单一，是算法层”目标函数的内在倾向”，什么都没做错也会往那个方向走。这一篇讲训练崩溃——另一种更突然的故障，是工程层 0/1 的 bug（概率口径不一致、mask 写错、数值精度不够、optimizer state 不匹配），没有”更合适的学习率”，只有写对和写错。常见表现：reward 断崖 → 0；KL/grad norm/ratio 突然跳高；输出变复读/乱码/截断；loss/梯度出 NaN/Inf；从某 checkpoint 继续训练又在相似位置崩。最浪费时间的做法是立刻扫学习率——崩溃的根因通常在概率、mask、数值路径上。\n![Pasted image 20260915092949.jpg]\n图 1：训练崩溃的分层排查顺序——从读 rollout、概率一致性、梯度累积开始，再查 mask、精度、超参和奖励。判断标准简单：“前一层不通过，后一层的曲线就没有解释意义”——例如 ratio 分子分母不是同一套概率口径时，clip fraction / KL / lr 扫描全部失去原义。因此先做便宜、结论接近”通过/不通过”的检查，最后才讨论需要权衡的超参和 reward。\nflowchart TD A[\u0026quot;0 读 20 条 rollout\u0026lt;br/\u0026gt;直接分 hack / 截断 / 乱码\u0026quot;] --\u0026gt; B[\u0026quot;1 两侧概率一致性\u0026lt;br/\u0026gt;A=B 验训练框架可重复\u0026lt;br/\u0026gt;A=C 验两侧一致\u0026quot;] B --\u0026gt; C[\u0026quot;1.5 梯度累积\u0026lt;br/\u0026gt;等价性 + 单样本 NLL 能到参\u0026quot;] C --\u0026gt; D[\u0026quot;2 mask 与数据边界\u0026lt;br/\u0026gt;逐 token 表 / 工具 token / padding\u0026quot;] D --\u0026gt; E[\u0026quot;3 数值精度\u0026lt;br/\u0026gt;fp32 归约 / log 空间 clamp\u0026quot;] E --\u0026gt; F[\u0026quot;4 优化器与超参\u0026lt;br/\u0026gt;lr / grad clip / optimizer state\u0026quot;] F --\u0026gt; G[\u0026quot;5 奖励函数\u0026lt;br/\u0026gt;离群值 / hacking 单变量对照\u0026quot;] A -. \u0026quot;前一层不通过\u0026lt;br/\u0026gt;后一层曲线没解释意义\u0026quot; .-\u0026gt; B B -. \u0026quot;未训先撞 clip = 必停训修口径\u0026quot; .-\u0026gt; C C -. \u0026quot;norm 差整数倍 = 缩放错\u0026quot; .-\u0026gt; D D -. \u0026quot;loss mask 之和 ≠ completion 数 = mask 错\u0026quot; .-\u0026gt; E E -. \u0026quot;第一个非有限值 = 上游 log-ratio 已爆\u0026quot; .-\u0026gt; F F -. \u0026quot;grad clip 每步都中 = 根因未解\u0026quot; .-\u0026gt; G 零、先锁定”第一个出问题的 step” 不要只保存崩溃后的曲线截图——真正有价值的是三份状态：\n最后一个正常 step（baseline） 第一个出现异常的 step（事发点） 异常之后的一个 step（确认是否持续） 把这三个 step 的以下信息全部落盘：\nprompt、完整 rollout 和截断标记 reward 总分及每个分项 old/current logprob、log-ratio 和 ratio 分位数（P50/P95/P99/max） attention mask、loss mask 和有效 token 数 clip fraction、KL、裁剪前和裁剪后的 grad norm batch ID、随机种子、checkpoint 和 optimizer state 然后先读二十条左右的原始输出，不要急着看均值。直接现象到优先排查点的快速映射：\n直接看到的现象 优先跳到哪 复读 / 乱码 / 语言突然变化 概率一致性或数值精度（第 1 / 3 步） 大量回答同时撞 max length mask、长度处理和 reward（第 2 / 5 步） 回答看起来正常，但 reward 全变成 0 verifier 或 reward 数据（第 5 步） 只有少数固定样本触发异常 脏数据、异常 reward 或 mask（第 2 / 5 步） 没有异常文本，但参数更新突然放大 梯度管线、精度或优化器（第 1.5 / 3 / 4 步） 如果可以复现，再做一次最重要的区分：\n同一个 batch 每次都在同一位置失败 → 优先查数据、mask、reward、确定性数值错误 换 batch 也总在相近 global step 失败 → 优先查学习率调度、optimizer state、数据陈旧、累计漂移 失败位置完全随机 → 再考虑异步权重同步、未初始化值、非确定 kernel 或硬件问题 [!tip] 这一步的目标 不是马上找到根因，而是把”整个训练坏了”缩小成一个可以反复重放的最小故障窗口——有了这个窗口，才能做控制变量实验（改一个变量、重放、比较），否则每跑一次都是噪音。\n一、第 1 步：先证明新旧概率可以比较 GRPO/PPO 的重要性比\n\\[r_t(\\theta) = \\frac{\\pi_\\theta(a_t|q, o_{\u0026lt;t})}{\\pi_{\\theta_{\\text{old}}}(a_t|q, o_{\u0026lt;t})}\\]\n分子是当前训练策略的概率，分母是生成 rollout 时旧策略的概率。这个比值有一个基本前提：两边除了参数版本，其他计算口径必须一致。\n需要对齐或明确记录：\ntoken IDs、chat template、特殊 token prompt 与 completion 的边界 position IDs 和 attention mask 推理引擎返回的是原始模型 logprob，还是经过 temperature / top-p / 重复惩罚等采样处理后的 logprob 权重版本、LoRA 合并方式、tied embeddings 量化、MoE router、推理 / 训练 kernel 的实现差异 如果两边口径不同，ratio 中混入的就不只是”策略更新”，还包含两套实现造成的额外 policy mismatch。对训练者来说像测量误差；但从算法上说，真正生成 token 的行为分布已经和训练侧假定的 \\(\\pi_{\\text{old}}\\) 不同——无论怎么命名，都不能把它静默当成一次正常的参数更新。\n1.1 最小一致性测试 冻结权重，不做 optimizer step，关闭 dropout、固定 batch 组织；如有随机算子，每次前向恢复到同一 RNG state。然后对同一批完整 token 序列做三次计算：\n在训练框架中前向一次，得到 logprob A 不改权重，再在训练框架中前向一次，得到 logprob B 用生成 rollout 的推理引擎计算同一批 token，得到 logprob C（尽量读取采样处理之前的原始 logprob；接口只能返回处理后值时，必须单独记下这些处理，不能和训练侧直接混比） 分别比较：\nA vs B：检查训练框架自身是否可重复 A vs C：检查推理侧和训练侧是否一致 至少记录 mean / P99 / max 的 \\(|\\Delta \\log p|\\)，并按 token 位置、序列长度、常见/低频 token 拆分。不要只看平均值——少数异常 token 才可能是 ratio 和梯度爆炸的起点。\n[!warning] 不要照抄全行业阈值 先在确认干净的配置上量出 baseline，再看故障 run 是否明显偏离。这里有两个不同的判据：\nA vs B：参数和实现都没变，\\(\\exp(A - B)\\) 应非常接近 1；不接近 1 → 查随机性、输入、训练框架自身的可重复性 A vs C：两套实现可以有小的数值差，但差异应接近干净 baseline，且不应让大量 token 的 ratio 在尚未更新时就撞到 PPO clip 真正的硬判据不是要求两套后端逐 bit 相同，而是——\n权重完全没变时，实现差异不能被训练目标误认为一次明显的策略更新；如果尚未训练，ratio 就已经大面积撞 clip，应先停止调参并修正概率口径。\n1.2 权重同步不能只看”调用成功” 很多同步问题不会报错。建议至少增加三类检查：\n同步完成后随机抽几层参数，比较训练侧源权重和推理侧目标权重的 checksum 或数值摘要 固定一个 prompt，先保存训练侧当前权重的首 token logits，再检查同步后的推理侧是否接近 把生成时的 policy version 写进 rollout 元数据，训练时确认 old logprob 确实来自该版本，并限制数据陈旧程度 [!warning] 不要走极端 不能”只要版本不同就拒绝”——PPO/GRPO 本来就允许当前策略和生成数据的旧策略不同，否则也不需要 importance ratio。真正要拒绝的是版本来源不明、old logprob 与生成权重对不上、或者超过系统允许陈旧范围的数据。\nLoRA merge、共享输入输出 embedding、分片参数、量化副本尤其容易出现”流程执行了，但某部分权重没更新”的情况。\n二、第 1.5 步：验证梯度加工流水线 logprob 算对了，并不代表参数最终收到了正确梯度。逐 token loss 还要经过：\ntoken/sequence 聚合 micro-batch 缩放 gradient accumulation data-parallel rank 间归约 grad clip 和 optimizer step 任意一层多除或少除一次 batch size、漏掉一个 micro-batch，都会改变实际更新幅度。曲线可能看起来”只是学习率不合适”，但怎么扫都不稳定。\n2.1 梯度累积等价性测试 固定同一批 \\(N\\) 条样本和相同参数，关闭 dropout 或固定每次前向的随机数，并保持样本顺序、mask、有效 token 完全一致。然后比较两种执行方式：\n一次把 \\(N\\) 条样本作为一个 batch 反向 每次放 1 条，累积 \\(N\\) 次后再更新 比较时不要只盯某个参数的绝对误差。更有用的是：\n两次 loss 是否采用同一种 denominator（如都除以有效 token 总数，而不是一边按样本求均值、另一边按全部 token 求均值） 梯度总 norm 的相对差 梯度向量 cosine similarity 一次 optimizer step 后参数 delta的相对差 fp32 单卡下应非常接近；bf16、分布式归约、不同 kernel 会带来更大舍入误差，不适合机械规定”必须小于 \\(10^{-6}\\)“。如果方向明显不同、或 norm 恰好差 accumulation steps / world size / 有效 token 数这类整数倍，通常就是缩放或归约写错了。\n2.2 用简单目标测试”梯度能否到达参数” [!warning] 常用但不稳妥的测试 “拿几条 RL 样本，要求 PPO loss 降到 0”——不是稳妥的通用测试，因为 clip 本来就会让一部分样本提前进入零梯度区。\n更干净的办法是暂时绕开 reward、ratio 和 clip，用一个小的确定性监督目标：\n固定 4~8 条 prompt-completion 只对 completion token 计算普通 NLL 使用 fp32、小模型或少量可训练参数 重复训练，确认 loss 能稳定下降并接近过拟合 如果这个测试都失败，优先检查参数冻结、loss mask、梯度累积、分布式归约、学习率和 optimizer step——此时没必要继续检查 GRPO 特有的超参。\n但这个测试只证明”普通监督 loss 的梯度能到达参数”，不能证明 GRPO 特有的 advantage、ratio、clip、policy loss 都写对了。基础链路通过以后，还要分别为这些模块做小规模数值对照。\n三、第 2 步：逐 token 检查 mask 和数据边界 这里至少有两种 mask：\nattention mask：模型在前向时能不能看见这个位置 loss mask：这个位置是否作为训练目标产生梯度 模型可以”看见但不学习”某些 token。例如在纯 policy loss 中，工具返回、环境 observation、用户输入都需要作为上下文被看见，但不应作为模型生成的目标。如果额外混入了 SFT 等辅助目标，则要为不同目标分别定义 mask，不能共用一张表含糊带过。\n3.1 不要只看 mask 的 shape 最有效的检查方式，是把一条真实样本逐 token 打印成表：\nposition token 文本 来源 attention mask loss mask 是否有 reward/advantage 0…prompt 末尾 system/user 输入 1 0 0 completion assistant 模型生成 1 1 1 tool result environment 环境返回 1 0 0 padding pad 补齐 0 0 0 再加几条运行时断言：\nloss_mask.sum() == 真正参与训练的 completion token 数 纯 policy loss 中，padding、prompt、environment token 的 loss mask 必须为 0 EOS 是否训练，要和生成停止规则一致 每个被 mask 的样本都要有明确原因和计数 3.2 多轮和工具调用最容易静默出错 在最常见的 agent 训练设置中，工具返回的 token 通常应满足：\nattention mask = 1（模型要读取工具结果继续决策） loss mask = 0（不应学习”假装自己是工具”） 自定义 rollout 时，不能假设通用 attention mask 会自动推导出正确的 loss mask——必须沿着 loss 构造代码确认最终使用的布尔 mask。\n3.3 截断样本要同时检查三处 回答撞到 max length 后，至少要明确：\n它的 reward 如何定义 它是否参与组内 reward mean/std 它的 token 是否进入 policy loss 这三个开关不是一回事。只在 loss 中 mask 掉样本、却仍让它的 0 reward 参与 advantage 统计，会继续改变同组其他回答的权重。反过来，如果在统计前排除，有效 group size 和零方差比例也会变化。\n四、第 3 步：沿着数值路径找第一个 NaN/Inf 不要只在 optimizer step 后检查参数。应该沿计算顺序逐层加 finite assertion——哪个张量最先出现非有限值，根因通常就在它之前一两步。\n4.1 关键差值和指数运算使用 fp32 bf16 适合大矩阵乘法，但有效位数有限。GRPO 中最敏感的往往不是单个大数，而是两个接近的 logprob 相减：\n\\[\\Delta \\log p = \\log \\pi_\\theta(a_t) - \\log \\pi_{\\theta_{\\text{old}}}(a_t)\\]\n如果两边先以低精度存储，小差异可能被量化成 0，或出现明显相对误差。实践中至少应确保：\nlog-softmax 的归约使用 fp32 gather 出来的 selected-token logprob 在做差前转成 fp32 log-ratio、KL 估计和 exp 前的保护逻辑在 fp32 中执行 不等于要求整个模型的 logits 永久以 fp32 保存——重点是把精度放在归约、相减、指数这些敏感环节。\n4.2 先限制 log-ratio，再取指数 ratio 的计算是\n\\[r = \\exp(\\Delta \\log p)\\]\n如果 \\(\\Delta \\log p\\) 已经大到让 exp 溢出，ratio 会先变成 Inf。之后再 clamp ratio，前向结果看似回到有限值，反向仍可能遇到：\n0 × Inf = NaN 或 clamp 的局部梯度为 0 配合 exp 的反向乘以 Inf 正确顺序是：先在 log 空间 clamp，再取 exp。\n![Pasted image 20260915092950.jpg]\n图 2：先 exp 后 clamp → 反向 0 × Inf = NaN，数值拉回来了，梯度已经坏了；先 clamp log 空间再 exp → 梯度仍有限。这个 clamp 是防止异常值把计算图炸掉的保险丝，不是 PPO clip 的替代品。还要记录它的命中率——正常训练如果频繁撞到保险丝，应该回头查概率一致性和数据陈旧，而不是继续收紧它。\n4.3 几种”限制”不要混在一起 不同代码库里可能出现下面几类限制，作用位置和目的完全不同：\n机制 作用位置 目的 命中是否应该常见 log-ratio numerical clamp exp 之前 防止溢出和非有限值 正常应极少 PPO ratio clip objective 内的 \\((1-\\varepsilon_{\\text{low}}, 1+\\varepsilon_{\\text{high}})\\) 根据 advantage 符号，限制 ratio 继续向有利方向变化所带来的收益 训练中可正常命中 importance-weight cap importance weight 直接限制过大权重对估计的影响 取决于算法设计 Dual-Clip PPO 负 advantage 样本的 surrogate objective 给负 advantage 下的极端目标值增加额外下界 仅采用该算法时出现 标准 PPO/GRPO 不一定包含后两项；看到配置名时，应先确认公式和作用位置，不能只根据名字猜实现。\n[!warning] 数值”长得像”不能判断是哪一种 不能通过”参数值看起来像 0.2、2 或 20”来判断它属于哪一种，必须看它作用在计算图的哪个位置。\n举例：把 numerical clamp 误设成 \\(\\Delta \\log p \\in [-0.2, 0.2]\\)，等价于提前把 ratio 压进 \\([\\exp(-0.2), \\exp(0.2)] \\approx [0.819, 1.221]\\)——几乎贴着默认 PPO clip 区间，而且不看 advantage 符号。这样会在 PPO objective 之前先切一遍梯度，改变算法本身。\n相比之下，\\([-20, 20]\\) 这类较宽范围更像异常兜底。这里的 20 也不是 fp32 指数溢出边界，更不是需要全行业统一遵守的常数——选择多宽要结合 dtype、实现、正常 log-ratio 分布和监控；关键是它不应成为日常限制策略更新的主要机制。\n4.4 不要用 log(softmax(x) + eps) 掩盖问题 在正常数值范围内，数值稳定的 log-softmax 会直接计算 logprob，不需要先把极小概率显式算出来。如果先做 softmax，极小概率可能在有限精度下变成 0，再取 log 就会得到 \\(\\log 0 = -\\infty\\)。极端 logits、输入中原本存在的 \\(\\pm\\infty\\)、以及用于 padding 的哨兵值仍需单独检查——不能因为用了 log-softmax 就默认一定安全。\n因此正确顺序是直接使用数值稳定的 log-softmax，而不是给概率统一加 eps。后者会：\n人为抬高极小概率对应的 logprob 让新旧策略的 ratio 产生系统偏差 把本来可以定位的概率不一致隐藏成错误梯度 padding 和无效位置如果使用 \\(-\\infty\\) 作为哨兵值，必须在做 logprob 差、KL、reduce 之前用布尔 mask 排除。不要让无效位置先参与算术，再指望最后乘 0 消掉——\\(\\infty \\times 0\\) 仍可能得到 NaN。\n五、前四层通过后，才进入优化器和 reward 走到这里，至少已经确认：概率口径可比较、梯度能正确累积、mask 对齐、数值路径有限。现在才适合讨论”更新是否太激进”和”reward 是否把模型带错了”。\n5.1 第 4 步：判断是不是更新步子太大 不要先背一个学习率数量级——相同 learning rate 在不同模型规模、有效 token batch、gradient accumulation、优化器、advantage 尺度下，实际参数更新可以差很多。\n每个 step 建议同时记录：\n裁剪前 grad norm 裁剪后 grad norm，以及 grad clip 命中比例 参数相对更新量 \\(|\\Delta\\theta| / |\\theta|\\) KL、ratio 的 P50 / P95 / P99 / max upper / lower clip fraction rollout 被重复训练了多少个 epoch 几种常见形状及下一步：\n现象 更像什么 下一步 grad norm、KL 和 ratio 分位数连续数十步一起抬升 学习率、数据复用或 KL 约束不合适 单独减小一个变量做复现 只在一个固定 batch 突然尖刺 reward 离群、异常序列或 mask 重放该 batch，逐样本拆梯度 grad clip 几乎每步都命中 更新长期依赖保险丝 查 advantage 尺度、loss denominator、学习率 grad norm 正常但参数 delta 异常 optimizer state、混合精度缩放或分片更新 比较 optimizer 前后的参数与 state checkpoint 恢复后立刻再次崩溃 故障状态、数据或恢复流程仍在 回到更早且相互匹配的 model + optimizer checkpoint，重放相同数据 [!tip] grad clip 的定位 通常可以把 grad clip 作为最后一道保护，但它不是算法成立的必要条件，也不能保证更新方向正确。如果每一步都靠 grad clip 才不爆，说明根因还没有解决。\n5.2 回滚时把故障证据一起留下 更稳妥的回滚流程是：\n保存触发故障的 batch IDs、rollout、reward、随机种子 保存崩溃前 checkpoint 与 optimizer state 从更早 checkpoint 重放同一 batch 修改一个变量，再次重放比较 临时 skip 一个 batch 可以让训练继续，但不能作为长期修复。若总是跳过梯度最大的 batch，训练数据会被系统性地重新加权；分布式训练中，各 rank 还必须对 skip 决策保持一致。\n5.3 第 5 步：沿 reward 到 advantage 的路径检查 不要只看 reward 总分曲线。对第一个异常 batch，按样本打印：\n样本 reward 分项 总 reward group mean/std advantage 有效 token 数 重点检查：\nNaN / Inf / 解析失败 / verifier timeout 如何记分 各 reward 分项的量纲是否一致 是否有单个分项突然改变范围 group 内 reward range / std 是否突然异常 截断、格式失败、真正答错是否被混成同一个分数 极高 / 极低 reward 对应的原始回答是否合理 如果 advantage 使用组内标准差归一化，那么在缩放系数为正、整组统一缩放、且归一化中的 eps 可以忽略时，把整个 group 的 reward 乘一个常数会在标准化中抵消——但实际实现通常带有 eps，标准差很小时不一定完全抵消；如果乘的是负数，advantage 的方向还会反转。\n单个异常 reward 则不会被这种机制自动消掉——它会改变组内 mean，通常也会改变 std，因此整组 advantage 都需要重新计算；如果分数跨过了其他样本，还会改变排序。如果没有除以标准差，reward 量纲变化会更直接地改变梯度尺度。排查时必须先确认代码实际使用的是哪种 advantage，而不能只凭”做了 GRPO”来猜。\n5.4 reward hacking 不能只靠一条曲线确诊 总 reward 上涨、正确性不动，值得怀疑，但还不是确诊——训练早期模型可能先学会格式，正确性暂时没变化。\n更可靠的检查组合是：\n人工阅读 reward 最高和增长最快的样本 分开画 correctness、format、length 等分项 使用独立 verifier或 held-out 规则复判 看某个分项上涨时，任务准确率是否同步变化 将可疑分项关闭或打乱，做单变量对照 只有模型只学会触发某个打分规则、而独立正确率和人工判断没有改善，才能更有把握地叫 reward hacking。\n六、把整套排查压缩成一张决策表 拿到一个崩溃 run 时，可以按下面的结果直接分流：\n检查结果 暂时结论 下一步 在关闭随机性的复现测试中，权重没变但 ratio 已明显偏离 1 概率口径或权重同步错误 停止训练，修第 1 层 大 batch 与梯度累积结果方向不同 loss 缩放或梯度归约错误 修第 1.5 层 token 表中本应排除的 prompt/tool/padding 进入 loss mask 错误 修第 2 层 第一个非有限值出现在 logprob / log-ratio / exp 数值路径有问题，或上游已产生异常大的 log-ratio 先修第 3 层，再追查异常值来源 前四层通过，KL 与参数 delta 持续增长 更新过激或 rollout 过旧 做单变量超参实验 只在固定样本或 reward 分项上尖刺 数据、verifier 或 reward 错误 重放并拆样本 [!tip] 决策表不是为了让 debug 整齐 这张表的目的是避免跨层解释——概率计算没通过时，不解释 entropy；mask 没通过时，不解释 reward hacking；数值已出现 Inf 时，也不靠降低学习率把它暂时压住。\n面试信号：作者会让候选人面对”reward 突然归零”的案例，只说最先要看的几项原始数据。如果回答从学习率和 KL 开始，通常说明他习惯读看板；如果先要最后正常 step / 第一异常 batch / 原始 rollout / ratio / mask，才更像真正做过训练排查。\n总结 训练崩溃的排查可以归结为一句话：\n先找到第一个错误的张量，再讨论最后一条异常的曲线。\n实践顺序是：锁定首个异常 step → 验证新旧概率 → 验证梯度累积 → 逐 token 检查 mask → 沿数值路径寻找第一个 NaN/Inf；这些都通过以后，才调整 optimizer、clip、KL 和 reward。\n这样做的价值不是”顺序更优雅”，而是每一层都有明确的通过条件。只有前一层成立，后一层指标才保留原来的解释意义。\n[!note]- 预告：第 7 篇 第 1 步的展开——两侧引擎概率不一致的量级到底有多大、怎么诊断，以及 MoE 上为什么这个问题天然更严重。\n监控看板（系列第 2 篇）：信号健康度可直接观测 [!info] 第 2 篇核心 网上分享 GRPO 优化时通常直接分析 clip-higher、dynamic sampling 的价值，但更该问的是前一步：作者当时看到了什么，才决定用这个的？ GRPO 没有 critic，唯一的学习信号来自同一个 prompt 采出的一组回答间的相对优势——所以信号本身的健康度可以直接观测，也必须观测。一组里所有回答 reward 都一样 = 这组压根没有信号，这件事在曲线上看得见，不需要猜。\n指标分两组：多样性组（模型还会几种解法、还在不在探索，本篇）+ 信号健康度组（第 3 篇，见下文）。多样性组有个共同的陷阱：它们看起来在测同一件事，其实漏掉了一整个维度（跨题可区分度，见第 5 节）。\nflowchart LR M[\u0026quot;GRPO 监控两组指标\u0026quot;] --\u0026gt; A[\u0026quot;多样性组（第 2 篇）\u0026lt;br/\u0026gt;还会几种解法 / 还在不在探索\u0026quot;] M --\u0026gt; B[\u0026quot;信号健康度组（第 3 篇）\u0026lt;br/\u0026gt;更工程 · 更容易被指标名字骗\u0026quot;] A --\u0026gt; A1[\u0026quot;① token entropy\u0026lt;br/\u0026gt;粗糙报警器\u0026quot;] A --\u0026gt; A2[\u0026quot;② 组内去重率 / 编辑距离\u0026lt;br/\u0026gt;最直观\u0026quot;] A --\u0026gt; A3[\u0026quot;③ pass@k\u0026lt;br/\u0026gt;最关键 · k=group size 免费\u0026quot;] A --\u0026gt; A4[\u0026quot;④ 高不确定性位置的熵\u0026lt;br/\u0026gt;补平均熵的稀释\u0026quot;] A --\u0026gt; A5[\u0026quot;⑤ 跨题可区分度（检索准确率）\u0026lt;br/\u0026gt;一次前向 · 防高熵模板化\u0026quot;] B --\u0026gt; B1[\u0026quot;① 零方差组占比\u0026lt;br/\u0026gt;实测 vs 难度分布预测基线\u0026quot;] B --\u0026gt; B2[\u0026quot;② 重要性比 / clip fraction\u0026lt;br/\u0026gt;上界下界分开计\u0026quot;] B --\u0026gt; B3[\u0026quot;③ 两侧引擎 logprob 差\u0026lt;br/\u0026gt;mean / p99 / max\u0026quot;] B --\u0026gt; B4[\u0026quot;④ 长度 · grad norm · reward 分项\u0026lt;br/\u0026gt;按对错拆 / 裁剪前 / 拆分项\u0026quot;] B --\u0026gt; B5[\u0026quot;⑤ ESS ratio\u0026lt;br/\u0026gt;先于 grad norm 的预警\u0026quot;] 1. token entropy：只是个粗糙报警器 每个生成位置模型对整个词表输出一个概率分布，该分布的熵就是 token entropy（可对整条回答取平均）。\n含义：熵高 = 还在犹豫，好几个候选词概率都不低；熵低 = 已经确定。 正常：训练中缓慢下降——模型本来就在变得更确定，这是策略锐化该做的事。 危险信号：下降速度快且一路趋近 0——每个位置只剩一个答案，采 16 条得 16 条一样的，探索彻底停止。 但它没有大家理解的那么有价值，三个原因：\n平均熵稳住了什么都证明不了——被格式 token 稀释（第 4 节展开）。 熵下降本身不是问题。任何一次有效的 RL 都会让熵下降，这就是策略在锐化；拿”熵在降”当坍塌证据是不可证伪的——它同时描述了健康训练。真正看的是下降速率 + 同期 pass@k 有没有跟着掉。 掉到接近 0 也不能直接确诊坍塌。熵低但对不同题还给不同推理 = 在压缩不是坍塌，模型可能反而非常稳定；而最坏情况——对所有题写类似话术——熵反而是高的（第 5 节展开）。 [!warning] 熵不是越高越好 熵过高时答案不稳定、难收敛，连输出格式都保不住。要维持的是一个目标区间，不是一个方向。\n定位：每 step 都有、好观测，但顶多算粗糙报警器，不是判据。\n2. 组内去重率：熵坍塌最直观的表现 做法：GRPO 每个 prompt 采一组（如 16 条），组内去重看剩几条。 为什么比平均熵好：平均熵把上万个 token 位置糊成一个数，很不敏感；“16 条去重只剩 2 条”一眼看懂。 危险信号：去重条数持续下降；降到 2~3 条基本就是坍塌——GRPO 的梯度来自组内比较，组内全一样就没有任何可比的东西。 更灵敏的版本：编辑距离的分布 / 中位数。完全字符相同其实不多，更常见的是”结构一模一样、只有几个数字不同”，编辑距离中位数比去重条数更灵敏。 3. pass@k：最关键，但有四个缺口 定义与为什么最关键：\npass@1 = 采一条就对的比例 = 最擅长的路走得多准；pass@k = k 条里至少一条对 = 总共还会几种解法。 RL 很容易把前者提上去、同时把后者压下去——它做的事就是把已有某条路径的概率推高，代价是别的路径被压没。 危险信号：pass@1 在涨、pass@k 在跌，尤其跌破 base model → 这轮 RL 没教新东西，只是把模型变窄了。 曲线形状：两条线都随 k 上升，区别在形状——RL 那条很快见顶（只会一条路，采再多也是同一条）；base 那条一直往上爬（还留着好几种解法）。两线存在交叉点，位置取决于任务和模型，交叉点之后 base 反而更高。 k 怎么取：\nk = group size（如 16）：训练本来就要采，pass@16 从 rollout 免费算出，每 step 都有——最常看的曲线。 评测集另跑到 32 封顶：论文里 k 到几百上千是把曲线画完整，日常没必要、成本线性涨。代价是只能看见比较严重的坍塌。 反超可以出现在 pass@16：作者见过一次——16 条去重只剩 2 条、pass@1 涨几个点、pass@16 低于 base、交叉点落在 8~16 之间。坍塌够狠时小 k 就能看出来；反之不成立：pass@16 看不出差别 ≠ 没问题，只是没严重到这个预算能看见。 四个 gap（免费曲线的坑）：\n# 缺口 后果 / 对策 1 base 侧要额外跑 “免费”只是 RL 侧 2 训练 prompt ≠ 评测集 rollout 上的 pass@k 天然被污染、通常偏高 3 每个 step 的题不一样 抖动大半来自题目难度组成而非策略变化；跨 step 不可直接比，除非固定 probe 集 4 开了零方差组过滤会被系统性压低 全对组被剔掉 → 剩余子集正确率天然更低，且随全对组占比上升持续下漂 → 自己造出一条”像坍塌”的曲线。要算就在过滤之前算 实操：rollout 曲线只当同 batch 内的相对信号看趋势；要和 base 比，固定一个几十道题的 probe 集，隔段时间两个模型各按自己调好的 temperature 各跑一次。\n[!warning] temperature 陷阱 两个模型的最优 temperature 不一样，哪边更高没有普适规律（机制上：RL 锐化后熵更低，要恢复同等有效熵得给 RL 加温度，base 本来就散；但作者也遇到过反例，给不出原因）。 经验：只比 pass@k 不扫 temperature = 没比；两边各自扫一遍、各取最优点再比。用训练时的温度同时评两个模型，比较天然偏向 RL——那个温度是 RL 训出来的工作点。\n4. 高不确定性位置的熵：补救平均熵的稀释 为什么平均熵弱：长回答里绝大多数 token 是语法、连接词、格式符号，这些位置熵天然不低且基本不变；真正决定解题路径的是少数分叉 token（选哪种方法、要不要回头验算、用哪个公式）。这几个 token 全部确定化，对平均熵的影响可能不到 1%，但序列级多样性已经死了。 改法：只统计 top-1 概率低于阈值位置的熵，绕开格式 token 的稀释。 新问题：条件集随策略锐化不断收缩（策略越确定，“高不确定性”位置越少，极端时集合变空）→ 跨 step 不可直接比较。 三个解法：① 固定位置集（冻结参考模型一次性选定，之后一直用）；② 集合大小和数值一起报；③ 分位数替代绝对阈值（每个 batch 取熵最高的 20% 位置，集合大小恒定）——最简单，很多框架已内置。 5. 跨输入可区分度：本篇最重要的一节 前四个指标都只看”同一道题”，没看”换一道题”。“答案不重复” ≠ “答案贴合题目”。\n模型 A：同题采 16 次答案几乎一样；但题一换，立刻换公式、换推理路径 → 可能只是更确定、更简洁，不一定坍塌。 模型 B：同题采 16 次措辞都不一样，看似很多样；但无论数学、代码还是逻辑题，都是同一套开场、自查、收尾 → 看似热闹，实际已模板化。 平均熵、组内去重、pass@k、条件熵都区分不了 A 和 B——它们缺了另一个问题：换一道题，推理会不会跟着换？\n两个维度（互不替代）：\n题内多样性 = \\(H(Z \\mid X)\\)：固定一道题重复采样，能得到多少种不同答案 跨题可区分度 = \\(I(Z;X)\\)：换题之后，推理内容是否明显不同 \\[H(Z) = H(Z \\mid X) + I(Z;X)\\]\n（原文公式渲染丢失，按信息论恒等式补全。）总体变化既可能来自”同题多解”，也可能来自”不同题触发不同推理”；只盯前一部分，就会漏掉”所有题套同一模板”的坍塌。最反直觉的是高熵也会坍塌——每次都换几个词（熵不低、去重正常），但所有题沿用同一个推理骨架。\n怎么测：检索准确率（让回答”找回自己的题目”）\nbatch 里有 P 道题，每题都有已生成的回答； 对每条回答分别计算：放回自己题目后的 logprob vs 放到其他 P-1 道题后的 logprob； 模型若真的看题组织推理，“回答 A + 题目 A”匹配分数应最高 → 统计每条回答能否找回自己的题目 = 检索准确率。 准确率高：输入依赖还在； 接近 \\(1/P\\)（随机猜题水平）：模板化严重。 成本：复用训练 rollout，只补一次前向，不需重新采样。 为什么零方差组会加剧模板化：一组 reward 全一样时，任务本身不给”这道题应该怎么改”的方向，但 KL、entropy bonus 等正则项仍然产生更新——而且这些更新对所有输入通用。这一步模型收到的不是”题 A 这样答、题 B 那样答”，而是”不管什么题，统一往这个方向调一点”。零方差组多，这种输入无关更新反复累积，题目间原有差异被慢慢抹平。\n两个反常组合（必须放一起看）：\n组合 诊断 熵低 + 检索准确率高 健康压缩：更确定了，但仍看题下菜 熵高 + 检索准确率 ≈ 随机 隐蔽的模板坍塌：用词多变，但所有题同一骨架 平均熵有时连报警方向都可能是反的。\n多样性组小结：五组指标 指标 测什么 危险信号 主要缺口 平均 token entropy 还有多少不确定性 快速下降趋近 0 稳住≠没事；下降≠生病；≈0≠确诊；被格式 token 稀释 组内去重率（/编辑距离） 16 条里实际几条不一样 持续降到 2~3 条 严格去重不灵敏，看编辑距离中位数 pass@k 能力上限有没有变窄 pass@1 涨、pass@k 跌破 base 污染 / 跨 step 不可比 / 零方差过滤压低 / base 要另跑；必须配各自调优的 temperature 高不确定性位置的熵 分叉 token 还活没活 条件集收缩变空 跨 step 不可比 → 用分位数（top 20%） 检索准确率 换题推理跟着换吗 ≈ 1/P 只补一次前向；与题内多样性可能反向 第 2 篇收官四条：\n平均熵成本最低也最弱——稳住什么都证明不了、掉到 0 也不能确诊，只值得当随手报警线。 pass@k 的 k 取 group size（免费），但要清楚免费曲线的四个缺口。 报 pass@k 必须报 temperature，两边各自调优后再比。 题内多样性和跨题可区分度是两个能反向走的量；只测前者的看板测不到”输出像模板”这个最典型的坍塌，补上只需一次前向。 信号健康度组（系列第 3 篇）：更工程，也更容易被指标名字骗 [!info] 第 3 篇核心 这一组比多样性组更工程、更容易被指标名字骗。全篇只回答五个问题：\n这一批 rollout 里，有多少组根本没提供学习信号？（零方差组占比） 模型更新后，离生成这些数据时的旧策略有多远？（重要性比 / clip fraction） 采样引擎和训练框架算出来的概率是不是同一回事？（两侧 logprob 差） 长度、梯度和总 reward 的异常，分别该怎么拆？（结果类指标） 表面上有几百条样本时，真正决定梯度的到底有几条？（ESS） 前四个是在找故障，最后一个是在找故障出现前的预警。\n1. 零方差 group 占比：50% 报警线是错的 零方差 group：同一 prompt 采出的那组回答 reward 完全一样（全对或全错）。\n[!warning] 错误观点：「超过 50% 就报警」 零方差组占比会随 group size \\(G\\) 自动变化。给定题集解题率分布，占比的期望是 \\[\\mathbb{E}_{x}\\left[p^{G} + (1-p)^{G}\\right]\\] 例：某类题单条 50% 做对，采 4 条时全对/全错并不稀奇；采 32 条时极难发生。题没变，只是 \\(G\\) 变了，自然占比就大幅变化 → 固定 50% 这条线在小 G 下一直报警，在大 G 下一直沉默。\n正确判据：实测占比对比题集难度分布预测出来的基线——把上面的期望算出来当基准，实测显著超出才叫异常。\n零方差组的自然基线取决于 group size：同一条 50% 报警线无法跨 G 使用（原文配图） 如果实测和预测吻合 → 问题不在训练动力学，而在题集难度分布：该换数据，不是该开过滤。这个指标有的框架已内置：frac_reward_zero_std，不用自己写。\n危害深入（比”没信号”更糟）：\n稀释有效梯度：DAPO 式 token 级归一化把整个 batch 的 loss 除以 token 总数——零 advantage token 对分子贡献为 0，却留在分母里。1000 个 token 里 500 个来自零方差组 → 有效梯度整体稀释，效果上像同时减小了 batch size 和 lr。注意压低系数是零 advantage token 占比不是 group 占比（全对组通常偏短、全错组通常偏长甚至撞截断），要估准得量 token 级比例。 只剩回退正则：开了 KL loss / entropy bonus 时，这些组不是完全没梯度，而是只剩一个把模型往参考策略拉回去的正则项，一点 reward 信号都没有——比单纯无效更糟：在用算力做纯回退，且方向与输入无关，会持续抹平题目间差异（第 2 篇 §5 模板化机制的实锤）。 （第 1 篇视角）组内无可比对象 → 没有学习信号。 2. 重要性比与 clip fraction：别把两个方向合并成一个人 GRPO 先采一批数据、再拿这批数据更新模型；做到第二、第三个更新 step 时模型已经变了，回答却还是旧模型生成的。ratio 量的是现在的模型和生成这条数据时的模型差了多少：\n\\[\\text{ratio} = \\frac{\\pi_{\\theta}(\\text{token})}{\\pi_{old}(\\text{token})}\\]\n可以当成概率涨跌倍数读：1 = 没变，1.05 = 涨 5%，0.8 = 降 20%。健康训练里大多数 token 的 ratio 应接近 1。\nclip 在做什么：PPO/GRPO 把 ratio 强限在 \\([1-\\epsilon,\\ 1+\\epsilon]\\)（典型 \\(\\epsilon=0.2\\)），超出部分梯度不再生效，防一步走太远；clip fraction = 撞边界的 token 比例。 危险信号 ①：clip fraction 突然涨到 20% 以上 → 更新步子普遍太大，或数据已经太旧。 危险信号 ②：ratio 出现极端离群值（个别 token 到 \\(e^5\\) 量级）——均值会完全藏住，要看分位数和最大值。 [!warning] 上界和下界必须分开看 把两侧合成一个总 clip fraction，像只报”今天一共进出 100 万人”却不说是流入还是流出——总量一样，方向可能完全相反。 典型坍塌前兆：上界几乎不命中、下界大量命中——许多 token 概率在往下掉，很少有 token 能往上探索；一合并，这个结构就消失了。（作者个人总结，多数框架不内置，要自己写代码。）\n读数前提：这两个计数在所有框架的实现里都按 advantage 符号门控，且结构上必然如此——PPO 的 \\(min\\) 决定了上界只在 \\(\\hat{A}\u0026gt;0\\) 时可能命中、下界只在 \\(\\hat{A}\u0026lt;0\\) 时可能命中。“下界命中很多”可能是策略真在大面积压概率，也可能只是这个 batch 里负 advantage 本来就更多——看上下界必须把正负 advantage 的数量一起分析。\n3. 两侧引擎的 logprob 差：最容易被忽略，也最致命 现代 RL 训练两套系统配合：推理引擎（vLLM / SGLang）生成，训练框架（FSDP / Megatron）算梯度。同一条序列、同一份权重，两套东西算出的 token 概率并不完全一样（kernel、batch 组织、量化方式不同）——像两把刻度略有偏差的尺子：一把量”旧概率”、一把量”新概率”，读数一除，以为看到的是模型变化，其实混着尺子本身的误差。\n为什么致命：直接拿推理引擎返回的 logprob 当 \\(\\pi_{old}\\) → ratio 的分子分母来自两个不同的实现 → 即使模型参数一个字没变，ratio 也不等于 1，算法把这个偏差误读成策略已经漂移很远，并据此调整梯度。（第 1 篇”训练崩溃”类的经典 root cause，这里给出量化手段。）\n怎么量：同一批 token 分别送进两套引擎，算每个 token 的 \\(\\lvert \\Delta \\log p \\rvert\\)，至少看三个数：\n统计量 看什么 判据 mean 整体误差是否普遍变大 \u0026lt; 0.001 通常健康；接近/超过 0.01 应停下检查；先用确认正常的配置跑出自己的 baseline。是否停训主要看它 p99 / p99.9 最异常的 1% / 0.1% token 是否越来越多、越来越重 比单独看 max 稳定 max 最极端的单个 token 差多少 定位具体异常样本；0.01 这条线只适用 mean 不适用 max——健康训练也会偶发极端值，按它报警几乎每个 batch 都响 均值为什么不够：一万个 token 里 9990 个差 0.001、10 个差异巨大，均值仍可能很小、看似正常，但那 10 个 token 仍可能撞 clip 边界、产生异常梯度——“整体是否一致”和”有没有少数危险 token”要分开检查。\n4. 长度、grad norm、reward 分项：已经表现出来的结果 4.1 长度爆涨别直接判 hack 看均值、p99，加上截断率。长度单调爆涨时，先把长度按对错拆开，分别画 \\(\\mathbb{E}[\\lvert o \\rvert \\mid \\text{答对}]\\) 和 \\(\\mathbb{E}[\\lvert o \\rvert \\mid \\text{答错}]\\)：\n两条一起涨、且答对那条涨得更快 → 这才是长度 hack → 查 reward 里哪个分项和篇幅正相关，改 reward。 只有答错那条在涨 → 不是 hack，是 \\(1/\\lvert o_i \\rvert\\) 长度归一化的机械后果：错误样本的负 advantage 被长度摊薄，写长一点能少罚一点 → 改 loss 聚合（token-level loss，或干掉 \\(1/\\lvert o_i \\rvert\\)——DAPO 的小 trick 就是这种场景才需要）。 两种情况的解决方式相反。此时去加长度惩罚是错的方向，且不只是无效：组归一化下给错误答案加连续长度惩罚容易产生发散的 advantage，结构上就容易把 reward 训崩。「单调爆涨就是长度 hack」把现象和机制搞混了——爆涨只说明长度这个维度上有压力失衡，压力从哪来要拆开才知道。\n长度塌缩到几十个 token：反方向，模型学到「别说长话」，通常是截断样本的处理方式出了问题。 截断率飙升要警惕：大量样本在撞 max_len，这些样本的 reward 通常是废的。 4.2 grad norm 要看裁剪前的 主流框架默认记的基本都是裁剪前的（裁剪函数的返回值本来就是缩放前那个 norm）：wandb 上 grad_norm: 8.3 而配置 max_grad_norm=1.0 不是 bug，是这一步梯度被整体缩小了 8.3 倍。自己写的时候先 log 再剪裁，否则从曲线上看不到尖刺到底有多大。危险信号：突然出现 10 倍以上的尖刺。\n4.3 reward 必须按分项拆开 总 reward 是最容易骗人的指标：格式分、长度分能单独把它顶上去，而真正在意的正确性分项可能一动不动——只看总分，会把「学会了排版」当成「学会了解题」。\n但别把训练早期误判成 hacking：模型先学格式、后学正确性，「格式分涨、正确性平」是正常形状。真正要看的是这个状态持续了多久，以及正确性分项到 plateau 之后有没有开始下滑。\n5. ESS：batch 里有多少样本真正起了作用 batch size 只表示放进了多少样本，不保证每条对梯度的影响差不多。若少数几条的重要性权重特别大、其余几乎可忽略，名义上用了 512 条，实际主要由那几条决定：\n\\[ESS = \\frac{\\left(\\sum_i w_i\\right)^{2}}{\\sum_i w_i^{2}}, \\qquad \\mathrm{Var}(\\hat{g}) \\approx \\frac{\\mathrm{Var}(g)}{ESS}\\]\n\\(w_i\\) 是重要性权重。公式不用死记，抓两种情况：权重均匀 → ESS 接近真实 batch size；权重集中在少数样本 → ESS 明显偏低。ESS = 8（batch 512）不是说只剩 8 条有非零梯度，而是从梯度估计稳定性看，这一步大致只相当于 8 条权重均匀的样本。实操更常看 ESS ratio = ESS / batch_size，换 batch size 曲线仍可比。\n5.1 为什么它比 grad norm 更早 训练失稳通常有积累过程：重要性权重越来越集中 → KL 和 grad norm 开始异常 → 最后才反映到 reward 上。\n训练崩溃通常不是突然发生的：ESS ratio 下降 → KL 上升 → grad norm 尖刺 → reward 断崖（原文配图） ESS ratio 的优势是逐步下降，往往能在 grad norm 尖刺之前给出信号；grad norm 尖刺更像一次明确的异常事件——看到它时，训练通常已进入需要止损定位的阶段。不必把图中先后顺序理解成每次严格成立，更准确的说法：ESS 观察的是风险如何集中，grad norm 观察的是这种集中是否已经产生了剧烈更新——前者适合预警，后者适合确认不稳。\n5.2 ESS 下降后，先判断是哪种偏差 ratio 变得不均匀至少有两种来源，处理方式完全不同：\n策略陈旧：生成回答后模型又更新了多次，当前策略与旧策略真实拉开距离 → 从更新次数、异步延迟、ppo_epochs 入手。 两侧引擎不一致：权重没变，只是采样引擎和训练框架算的 logprob 不同 → 回到第 3 节检查。 两种偏差最终都进 ratio、都拖低 ESS。只看一条合并后的曲线，很容易把引擎误差当成”数据太旧”，错误地去调 ppo_epochs。\n信号健康度组小结：按一条顺序看 这篇不适合背固定阈值，实用的是按顺序看训练信号：\ngroup 里有没有可比较的 reward——零方差组没有任务梯度，但正常占比取决于 G 和题集难度，不能用固定 50% 判断； ratio 本身是否可信——两侧引擎可能给出不同 logprob；整体一致性看 mean，少数异常 token 看 p99 / p99.9 / max； 梯度是否开始被少数样本控制——ESS ratio 持续下降通常先于 grad norm 尖刺；解释之前先排除两侧引擎不一致； 长度、grad norm、reward 分项——更接近已表现出来的结果：长度按对错拆、grad norm 看裁剪前、reward 拆成分项。 整套监控回答的其实是同一个问题：这一批数据提供了多少可信的比较信号，这些信号有没有被少数样本或工程误差带偏，最后又以什么形式反映到模型更新上。\n[!note]- 预告：第 4 篇 策略坍塌的具体排查：五条 root cause，以及为什么「去掉 std 归一化」这个改动的代价被普遍低估。见 [[#系列追踪]]。\n我的批注 ⚡ “工程层 0/1、算法层内在倾向”这个排序依据很锋利：bug 可以被穷尽排除，目标函数的倾向只能设计缓解——所以顺序天然是先 bug 后倾向。 ⚡ 信号链四问的本质是把”静态分类”换成”依赖链”：第 1 层不过关，后面观测全部作废。这比二分法抗混淆得多，也解释了为什么”渐进/突发”表象会骗人。 ⚡ 面试考察逻辑值得学习：不问八股，从”问题怎么发现的 → 哪条曲线看到征兆 → 怎么决定改哪个 → 改完遇到什么新问题”逐层 dive deep，编造的经历答不了追问。 ✅ 上面的 ❓ 已解决（09-01 并入第 2 篇，见上文监控看板）：组内去重率（直观数条数 / 编辑距离中位数）、pass@k（k = group size 免费）、跨题可区分度 = 检索准确率（复用 rollout 补一次前向，随机线为 1/P）。 ⚡ 零方差组有两条独立的危害路径：① 组内无可比 → 没有学习信号（第 1 篇视角）；② 正则项（KL / entropy bonus）的输入无关更新反复累积 → 模板化（第 2 篇视角）。DAPO 的 dynamic sampling 冲着 ① 去，检索准确率监控防 ②。 ⚡ “高熵坍塌”修正了我对坍塌的直觉：熵和去重率都正常、看板全绿，但所有题同一骨架——题内分布根本不变，所以只看题内分布的指标对它全盲。监控要防的是”指标都绿但模型已死”。 ⚡ temperature 是被评模型自身的属性，不是实验常量：用训练温度评 base 是系统性偏向 RL 的不公平比较。推广：任何对比实验里，一侧调优过的工作点参数必须对两侧各自重调。 ⚡ “50% 零方差报警线”是固定阈值思维的反面教材：占比是 G 和题集难度分布的函数（\\(\\mathbb{E}[p^G+(1-p)^G]\\)），阈值必须换成”实测 vs 预测基线”；实测吻合基线 → 该换数据，不是开过滤。 ⚡ 合并/平均是监控指标的头号敌人，第 3 篇连给三例：合并 clip fraction 藏住”上界不命中 + 下界大量命中”的坍塌前兆；mean |Δlogp| 藏住少数危险 token；总 reward 把”学会了排版”顶成”学会了解题”。对策统一：拆流向、看分位数、拆分项。 ⚡ 零方差组的危害集齐三层：① 组内无可比 → 无学习信号（第 1 篇）；② token 级归一化下稀释有效梯度 ≈ 同时减小 batch size 和 lr（注意是 token 占比不是组占比）；③ 开 KL/entropy bonus 时只剩输入无关的回退正则，持续抹平题目差异（第 2 篇模板化机制的实锤）。 ⚡ 长度按对错拆是”先分类再动手”的又一实例：同是长度爆涨，答对涨更快是 hack（改 reward），只有答错涨是 \\(1/\\lvert o_i \\rvert\\) 归一化的机械后果（改 loss 聚合）——解法相反，此时加长度惩罚还会因发散 advantage 把训练弄崩。 ⚡ ESS 与 grad norm 的分工一句话：“ESS 看风险怎么集中，grad norm 看集中是否已产生剧烈更新”——预警看前者，确认看后者。 🔗 token-level loss / 干掉 \\(1/\\lvert o_i \\rvert\\) → [[dapo]] 的 loss 聚合方式 ⚡ σ 归一化的核心机制是”单样本权重主要由组内稀罕度决定，不由信号可靠性决定”——这反转了通常直觉：“大权重 = 重要信号”。在 GRPO σ 归一化下，“大权重 = 组内位置极端”，含义完全不同。所以看到优势大不要默认它是好信号，要先问”它在组内有多孤立”。 ⚡ (G−1)/√G 这个硬上限回答了一个反直觉直觉：advantage 看起来能无限放大但其实是有限博弈；理解这条上界能一眼判断实验里出现的 A=5/10 是不是超界——超界几乎一定是数据或 reward bug，不是训练动力学。 ⚡ “去掉 σ 不是乘常数” 是 Dr. GRPO A/B 实验最常被误读的地方：表面看 lr 没变，实际每个 group 的等效 lr 都按自身 σ 缩放了——σ 小的被压更多、σ 大的被压更少。“同 lr 上去跑一遍”看到的”方法无效”，一部分就是这层结构性变化，不是权重本身错了。 ⚡ 动态采样的最大盲点不在训练曲线，而在底层 prompt 池缩小——batch 表面永远满着，看不出哪些题已经被悄悄排挤出去。唯一可靠的探测方式是单独的”已掌握回归集”，且必须明白这只是报警器不是精确量尺。 ⚡ σ 与 \\(1/\\lvert o_i \\rvert\\) 是两条正交的偏置——difficulty bias vs length bias。Dr. GRPO 一起删了两个但没单独消融，所以”长度失控归因 1/|o|“这件事的证据强度并不高。看到长度爆涨时排错顺序应是：length norm → σ → reward hack（按数据出现先后排，不是按怀疑程度）。 🔗 动态采样 → [[dapo]]；Dr. GRPO 的去掉 σ → 第三方实现/讨论见 [[grpo]] 笔记 ⚡ “clip 命中梯度归零”是个常见误传——PPO min 在 \\(r_t\\) 与 \\(A_t\\) 异号时根本不截断（min 取到的是更小的未裁剪项，clip 端点只是备选）。真正梯度归零只在 \\(r_t\\) 与 \\(A_t\\) 同号且 \\(r_t\\) 越界时——上界只在 \\(\\hat A\u0026gt;0\\)、\\(r_t\u0026gt;1.2\\) 时真截断；下界只在 \\(\\hat A\u0026lt;0\\)、\\(r_t\u0026lt;0.8\\) 时真截断。框架报告的 clip_frac 高、loss 仍在降的常见解释：命中里很多是异号情形（梯度照算）。 ⚡ 默认 [0.8, 1.2] 是数值对称、log 不对称：log 0.8 ≈ -0.223 比 log 1.2 ≈ +0.182 宽 ~22%。这个偏置平时不显，但叠加低概率 token 灵敏度放大（log r ≈ (1-p)·Δ），先撞下界几乎是必然——除非主动调成几何对称（[0.8, 1.25] 或 [0.75, 1.25]）。 ⚡ 几何对称点公式 ε_high = ε_low/(1-ε_low) 给了一个直接参考：DAPO 的 [0.72, 1.389] 不是 0.28 对称点（那是 0.389）而是经验选——作者承认”有意保留不对称防止熵坍塌”，意味着 clip 设计不是数学游戏而是经验偏置。 ⚡ “稀有 token 学不动”是被低估的副作用——0.05 → 0.3 需要 ~7.3 次有效更新（log 6 / 0.25），但 clip 会把多数更新截断在它刚刚冒头的瞬间。频次 token 撞上界是单步误差，稀有 token 撞上界是断整个学习路径——遇到”某 token 出现频次始终涨不上去”时优先怀疑 clip，而非数据。 ⚡ “熵在下降”是 RL 训练的固有倾向，不是 clip 的判据。把熵下降归因于 clip 设置过紧是监控偷懒——必须配随机 reward 基线（同一份 rollout，reward 打乱），两条曲线平行下降 = 熵自然衰减（clip 无责）；差距 = clip 与 reward 的耦合效应（clip 有责）。这条控制变量思路应该推广到所有”调 X 后看指标 Y”的实验。 ⚡ mask ≠ clip：loss mask 是样本层（不参与 loss 也不是”截短到某长度”），不会让模型学到”答短点有奖励”——那是 mask 的副作用而非设计意图。要短答奖励要显式长度惩罚（Dr. GRPO 的 1/|o| 是 reward 设计，不是 mask）。统计时截断率与 clip_frac 必须分开计，不要合并。 ⚡ 调 clip 不是”开紧/松”——是调几何：5.1 几何对称（消除下界偏置）；5.2 控制熵（宽上界鼓励探索，紧上下界鼓励利用）；5.3 扫描看 clip_frac_by_sign 与有效 step 比，而不是看 clip_frac 一个数。合并 clip_frac 又是第 3 篇”合并是监控头号敌人”的第 N 个例子。 ⚡ “训练崩溃没有更合适的学习率，只有写对和写错”——一句话定性策略坍塌与训练崩溃的边界：前者是算法层倾向（可调缓解），后者是工程层 0/1 bug（只能穷尽排除）。立刻扫学习率是训练崩溃排查最浪费时间的反应——因为根因通常在概率/mask/数值路径上，与 lr 无关。 ⚡ “前一层不通过，后一层曲线没有解释意义”是第 6 篇最锋利的诊断原则——同一句在多处复用：ratio 口径不一致时 clip fraction / KL / lr 扫描全部失效；mask 不对时 advantage 解读失效；数值已 Inf 时优化器调整失效。这套分层在每条具体排查动作前都要默念一次——避免”曲线绿就当没事，曲线红就当超参”的看板习惯。 ⚡ 最小一致性测试 A/B/C 是排查的硬起手——冻结权重、关 dropout、固定 RNG，同一批 token 在训练侧跑两次 + 推理侧跑一次。A=B 验训练框架可重复；A=C 验两侧概率一致。未训先大面积撞 PPO clip（权重没动就报策略更新）是真正的硬判据——比任何阈值都直接：训练目标把实现差异误读为一次明显策略更新时，必须停训修口径。 ⚡ log-ratio clamp 必须在 exp 之前——先 exp 后 clamp → 反向 0 × Inf = NaN，数值拉回来了梯度已经坏了。这个错误在 bf16 下尤其隐蔽（前向 clamp 出来有限值，反向时才炸）。clamp 是异常保险丝不是 PPO clip 替代品；频繁撞到保险丝应该回头查概率一致性和数据陈旧，不是继续收紧它。 ⚡ “四种 clip/clamp 不能凭数值猜作用位置”是第 6 篇的另一把手术刀——numerical clamp（exp 前，防溢出）、PPO ratio clip（objective 内，按 advantage 符号限制 ratio 继续向有利方向）、importance-weight cap（限制权重本身）、Dual-Clip PPO（负 advantage 样本的额外下界）。数值”长得像 0.2/2/20”完全不能判断是哪一种——错把 clamp(±0.2) 当作 numerical clamp 实际就在 PPO 之前先切一遍梯度，改变算法本身。 ⚡ “checkpoint 恢复即崩 = 回到更早且相互匹配的 model+optimizer checkpoint 重放”——这是回滚原则里最容易被忽略的一句：optimizer state（Adam 的 m/v）必须和 model state 同步回滚，只回滚 model 不回滚 optimizer 会留下更隐蔽的次生 bug。“更早且匹配”是关键词，不是”更早”就够。 ⚡ “先要最后正常 step / 第一异常 batch / 原始 rollout / ratio / mask 才像真排过”——面试信号：上来就读 lr 和 KL 是看板习惯。三态保存（最后正常 / 首个异常 / 异常后一步）比单看崩溃后曲线截图价值大得多——有了这个最小故障窗口才能做控制变量实验（改一个变量、重放、比较），否则每跑一次都是噪音。 🔗 零方差组 / 有效样本数 → [[dapo]] 的动态采样就是冲着这个去的 🔗 熵坍塌 → [[entropy(reasoning)]]、[[CURE：Critical-Token-Guided Re-concatenation for Entropy-collapse Prevention]] 🔗 多轮工具调用崩溃 → [[SimpleTIR：End-to-End Reinforcement Learning for Multi-Turn Tool-Integrated Reasoning]]、[[RAGEN：Understanding Self-Evolution in LLM Agents via Multi-Turn Reinforcement Learning]] 🔗 GRPO 本体与实现 → [[grpo]]、两侧概率不一致的分布视角 → [[SFT、RL 与 OPD：分布视角]] 自测：面试官的追问链 [!question]- “分享一个训练出问题的案例”——之后会被怎么追问？ 追问链：遇到的是策略坍塌还是训练崩溃？→ 当时看到的情况是什么、查到了什么？→ 在哪条曲线上看到征兆？→ 当时怎么决定改哪个？→ 改完之后又遇到了什么新问题？\n红灯回答：“我用 DAPO / XXPO 里某个优化解决了某个问题”，但答不上具体怎么发现的。\n绿灯回答：讲具体案例，最后落到”组内 reward 没有方差 / 两侧算出来的概率对不上 / 某个指标看着正常但其实没有意义”这类只有亲手排过才知道的细节。\n[!question]- 第 2 篇补充：怎么确认候选人真的自己搭过看板？ 作者几乎不直接问跨题可区分度这一节。但如果候选人讲完监控后自己主动提到”同一题内的多样性”和”跨题的可区分度”不是一回事，基本可以确认他真的搭过看板。同类细节还有：“pass@k 要在零方差过滤之前算”“对比 base 必须各扫 temperature”“熵下降要结合 pass@k 一起看”——都是亲手踩过坑才说得出的。\n[!question]- 第 3 篇补充：ESS 的两连追问 作者面 ESS 不听公式，听两件事：① 为什么它比 batch size 更接近”真正参与梯度的样本数”——权重集中时 \\(\\mathrm{Var}(\\hat{g}) \\approx \\mathrm{Var}(g)/ESS\\)，梯度估计的稳定性只等效于 ESS 条权重均匀的样本；② ESS 下降后如何区分策略陈旧和两侧引擎不一致——先查同权重下两侧 logprob 差；把引擎误差误判成数据太旧，会错误地去调 ppo_epochs。\n[!question]- 第 4 篇补充：σ 归一化与动态采样的作用范围 面试追问两个方法时最常听到的两种背法——背”Dr. GRPO 去掉 σ”和”DAPO 动态采样”，但说不出各自瞄的区域。能讲清“p=0/1 时去掉 σ 没用（分子本来就是 0），p≈1 时过滤零方差没用（条件不命中）” 通常比背方法名更能说明理解了实现；进一步能说出“去掉 σ 是按各自 σ 缩放不是乘常数”也基本能确认动手做过 A/B。\n[!question]- 第 5 篇补充：clip 真的截断了什么 面试追问 clip 的两面镜子：① “clip 命中梯度归零”的常见误解——能讲清 PPO min 在 ratio 与 advantage 异号时根本不截断（取的是更小的未裁剪项），真正归零只在同号越界时（A\u0026gt;0, r\u0026gt;1.2 上界 / A\u0026lt;0, r\u0026lt;0.8 下界）——基本能确认读过 PPO 论文且不只背公式；② “熵在下降”作为调 clip 的依据——能主动说”必须配随机 reward 基线对照，否则是把熵的自然衰减误判为 clip 过紧”，说明理解了控制变量思路；③ mask 与 clip 的边界——能区分”截断样本（数据层）“与”clip（token 层）“是两件事、统计分开、且 mask 不会让模型学到”答短点有奖励”，比背 DAPO/Dr. GRPO 的 trick 更能说明理解了实现。\n[!question]- 第 6 篇补充：训练崩溃排查的硬起手 面试信号比方法更重要：候选人说”先要最后正常 step / 第一异常 batch / 原始 rollout / ratio / mask”才像真排过；上来就读 lr 和 KL 是看板习惯。具体方法层面能讲清三件事基本能确认动手做过：① A/B/C 最小一致性测试——冻结权重、关 dropout、固定 RNG，同一批 token 在训练侧跑两次 + 推理侧跑一次，未训先大面积撞 PPO clip = 必停训修口径（硬判据不是阈值）；② log-ratio clamp 必须在 exp 之前——能讲出”先 exp 后 clamp 反向 0 × Inf = NaN，数值拉回来了梯度已经坏了”，且区分”clamp 是异常保险丝不是 PPO clip 替代品”；③ 四种 clip/clamp 不能凭数值猜作用位置——numerical clamp / PPO clip / importance-weight cap / Dual-Clip PPO，数值长得像 0.2 / 2 / 20 都不能判断是哪一种，必须看作用图位置，错把 clamp(±0.2) 当 numerical clamp 实际在 PPO 之前先切一遍梯度改变算法本身。\n行动项 对照 [[metrics总结]] 检查当前训练监控是否覆盖信号链四问：logprob 一致性 / reward 分项 / 零方差组占比 / pass@k 补两个便宜指标：组内去重率（升级为编辑距离中位数）+ 检索准确率（复用 rollout、补一次前向，对照 1/P 随机线） pass@k 固定 k = group size，在零方差过滤之前算；跨 step 只看同 batch 相对趋势；跨模型对比用固定 probe 集 + 各自扫 temperature 信号健康度组四件套：零方差组实测 vs 难度分布预测基线（frac_reward_zero_std）；两侧引擎 |Δlogp| 的 mean / p99 / max；上界下界 clip 分开计数（按 advantage 符号解读）；ESS ratio 确认框架 grad norm 记录的是裁剪前数值，且先 log 再 clip σ 归一化双指标：标准化的 A 有多大 + 标准化之前的 reward range / σ / 各 reward 分项（连续 reward 的关键） A/B 去掉 σ 时跑三条曲线（保留 / 去 σ 同 lr / 去 σ 补偿 lr），分离”权重结构变了”和”整体步子变小了”两个效应 零方差 group 拆解：全对多少 / 全错多少（比例相同但处理方向相反）；比例高时分别看是数据太简单还是验证器过严 设”已掌握题目”回归集，定期检查简单能力是否退化（动态采样的代价；只能是报警器，不是精确量尺） 出现长度失控时先查 loss 是否按回答长度归一化（\\(1/\\lvert o_i \\rvert\\)），再去怀疑 σ 给 rollout 加定期落盘 + 人工抽读流程（读原文在读曲线之前） 面试准备：整理一个自己经手的排查案例，按”追问链”自问自答一遍 clip 上下界分开计：clip_frac_upper / clip_frac_lower / unclipped_step_ratio，且按 advantage 符号门控解读（PPO min 决定的） 跑一组 random reward baseline：同一份 rollout、reward 打乱；对照真 reward 的熵曲线——平行 = 熵自然衰减（clip 无责），差距 = 耦合效应（可调 clip） clip 调参前单独扫一侧：先固定上界 1.2 扫下界 [0.6, 0.7, 0.8, 0.84, 0.88]，再固定下界扫上界；不要同时调两侧 截断率与 clip_frac 严格分两个指标：截断率（样本层 / 监控 mask 副作用）/ clip_frac（token 层 / 监控 clip 副作用），不要合并 稀有 token 监控：每个 prompt 记录有效 step 数（unclipped_step_count）——token 概率涨不动时优先怀疑 clip 而非数据稀疏 给崩溃 run 跑一次 A/B/C 最小一致性测试：冻结权重、关 dropout、固定 RNG，同一批 token 在训练侧跑两次 + 推理侧跑一次；记录 mean / P99 / max |Δlogp|，未训先撞 PPO clip 必停训修口径（硬判据） 权重同步加三类检查：随机抽几层 checksum / 保存首 token logits 训练侧 vs 同步后推理侧对比 / rollout 元数据记 policy version 并核对 old logprob 来源 沿计算路径加 finite assertion：logprob 差、log-ratio、exp、KL 估计、ratio 之后 clamp 之前；第一个非有限值之前的张量是定位起点 log-ratio 数值 clamp 必须在 exp 之前——代码 review 时检查这个顺序；并记录命中率，频繁撞保险丝回头查概率一致性和数据陈旧 回滚时同时存崩溃前 checkpoint + optimizer state + 故障 batch ID / rollout / reward / 随机种子；从更早相互匹配的 (model, optimizer) checkpoint 重放，改一个变量再重放 给 reward 加分项独立 log（correctness / format / length / verifier 各项）+ 独立 verifier / held-out 复判；只总分上涨不能确诊 hacking 延伸阅读：[[RL面试题2026]]；追系列第 7 篇《两侧引擎概率不一致的量级 + MoE 的特殊性》（见 [[#系列追踪]]） 系列追踪 篇 主题 状态 1 如何排查 GRPO 训练问题（策略坍塌 vs 训练崩溃） ✅ 本笔记上半部分 · 知乎 2 如何监控模型的多样性 ✅ 本笔记下半部分 · 知乎 3 怎么监控训练信号的健康度 ✅ 本笔记下半部分（多样性组之后）· 2026-08-31 发布 4 策略坍塌的五条 root cause（除以 σ） ✅ 本笔记「排查之后、监控之前」· 2026-09-02 发布 5 clip 的不对称与稀有 token（默认 [0.8,1.2] log 空间下宽 ~22%，低概率 token 撞上界是 softmax 必然；调几何而非开紧/松；熵降不能单独作为调 clip 依据） ✅ 本笔记「root cause 之后、监控看板之前」· 2026-09-04 发布 6 训练崩溃的分层排查（前一层不通过后一层没解释意义；最小一致性 A/B/C；log-ratio clamp 必须在 exp 之前；4 种 clip/clamp 不能凭数值猜） ✅ 本笔记「clip 之后、监控看板之前」· 2026-09-07 发布 7 两侧引擎概率不一致的量级 + MoE 的特殊性 ⏳ 待发布 作者（LLMCat，亚马逊高级数据科学家）其他相关文章：\n置顶 · GRPO 训练踩坑实录：63% → 96% tool selection（7B SubAgent 替代 Mixtral 8x22B，20+ 版本迭代） LLM 算法面试官：怎么看待 GRPO/DAPO 换汤不换药？ 投标 Agent 训练复盘：2/4 渐进式训练 SFT→DPO→GRPO · 3/4 Reward 设计（二元 → 多维 RM+GenRM）· 4/4 弃权：让模型了解自己的能力边界 Search-R1 训练运维 Agent 复盘 云上事故根因分析 Agent 训练复盘 通用 Agent 开发笔记：拆解 Claude Code 与 Cursor 的设计与 prompt 相关笔记 TABLE date AS 日期, tags FROM \u0026quot;LLM Training/RLHF\u0026quot; WHERE file.name != this.file.name SORT date DESC LIMIT 8 ","date":"2026-08-31T00:00:00Z","permalink":"/grpo%E8%AE%AD%E7%BB%83%E6%8E%92%E6%9F%A5%E7%AD%96%E7%95%A5%E5%9D%8D%E5%A1%8C-vs-%E8%AE%AD%E7%BB%83%E5%B4%A9%E6%BA%83/","title":"GRPO训练排查：策略坍塌 vs 训练崩溃"},{"content":"RSI：Recursive Self-Improvement 分类图谱 [!info] TL;DR 核心思想：RSI（Recursive Self-Improvement）= 让 LLM / Agent 不仅完成任务，还能利用自己产生的经验、数据、反馈甚至代码，迭代地改进自己。近义词极多（self-evolve / self-play / self-refine / self-reward / self-distill），但闭环程度和”被改进的东西”各不相同。 最大收获：所有工作都沿同一条主线推进——逐步把人移出 improvement loop。Test-Time 侧，改进对象从 output → weights → Agent 本身（persistence 递增）；Training-Time 侧，从自造 supervision（zero-label）→ 自造 curriculum（zero-data）→ 自造改进策略（auto research）。 为什么重要：闭环每扩展一层，最终都撞上同一个问题——verification 是否可靠。现状是绝大部分 self-evolving 工作仍停留在 human-on-the-loop，真正的 closed loop 远未实现。\n笔记地图 §0 目标与全景 §1 Test-Time RSI：三级递进 §2 Training-Time RSI：zero-label / zero-data / auto research §3 Verifier：真正的瓶颈 我的批注（待补充） §0 目标：把人移出 improvement loop 阶段 人的角色 human-in-the-loop AI 提出改进，但每次修改需人确认 human-on-the-loop data / reward / verifier 已自动产生，人监督结果、控制部署 closed loop 系统自己产生、验证并应用改进，无需人工审核 按”自我改进发生的阶段”，现有方法分两大类：\nflowchart TB subgraph TT[\u0026quot;Test-Time RSI：改进发生在部署过程中\u0026quot;] direction LR A[\u0026quot;Self-refine\u0026lt;br\u0026gt;改当前 output\u0026quot;] --\u0026gt; B[\u0026quot;TTT / TTRL\u0026lt;br\u0026gt;改 weights\u0026quot;] --\u0026gt; C[\u0026quot;Harness Evolution\u0026lt;br\u0026gt;改 Agent 本身\u0026quot;] end subgraph TR[\u0026quot;Training-Time RSI：改进写进下一轮训练\u0026quot;] direction LR D[\u0026quot;zero-label\u0026lt;br\u0026gt;自造 supervision\u0026quot;] --\u0026gt; E[\u0026quot;zero-data\u0026lt;br\u0026gt;自造 curriculum\u0026quot;] --\u0026gt; F[\u0026quot;auto research\u0026lt;br\u0026gt;自造改进策略\u0026quot;] end C ==\u0026gt;|\u0026quot;persistence 递增\u0026quot;| D TT --\u0026gt; G[\u0026quot;共同闸门：Verifier\u0026lt;br\u0026gt;这次更新真的更好吗？\u0026quot;] TR --\u0026gt; G G -.-\u0026gt;|\u0026quot;未解\u0026quot;| H[\u0026quot;grounding 与 policy-verifier co-drift\u0026quot;] §1 Test-Time RSI：persistence 的三级递进 严格说是 Deployment-Time RSI：self-improvement 直接发生在部署过程中，而非等下一轮 offline training。各方法的真正差别在于改进能保留多久；同时人从每一步的 critique / revision 参与者，退到对整个 loop 的监督者。\n层级 改进对象 改进保留多久 代表 ① Self-critique \u0026amp; Self-refine 当前 output episode 结束即消失 Generate → Critique → Refine ② Test-Time Training（TTT） weights 当前 query 内持续 TTRL、ETTRL ③ Harness Evolution Prompt / Tool / Memory / Skill / Workflow / Agent Code 跨 task 长期沉淀 Darwin Gödel Machine、SkillMaster ① Self-critique \u0026amp; Self-refine —— 改当前 output 权重冻结，针对当前 query 反复”生成 → 检查 → 修改”。关键在 verification signal：单靠模型自我 critique 并不稳定，有效做法通常引入 execution feedback、retrieval、solver 或 judge 来提供 grounding。但 refine 得再好，经验也随 episode 结束消失——提升的是单次回答，不是长期能力。\n② Test-Time Training —— 改进写入 weights TTT 不只改 output，而是直接更新参数，让当前 query 的 experience 有机会写进 weights：\nExperience → Update → Better Model → New Experience\n这一范式可与不同后训练算法结合，热门的一类即 Test-Time RL（TTRL），vault 内已有细读：[[TTRL：Test-Time Reinforcement Learning|TTRL]]（majority voting 伪标签 + GRPO）及其改进 [[ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM|ETTRL]]。\n③ Harness Evolution —— 改 Agent 本身 优化对象从 output / weights 扩展到整个 Harness：Prompt、Tool、Memory、Skill、Workflow，甚至 Agent Code。\n从真实 interaction 与失败 trajectory 积累经验：Computer-use Agent 从 failure 生成 training experience；Experience Graph 长期保存 branch / execution / failure / repair。 SkillMaster、SkillSmith、Socratic-SWE：让 Agent 自动生成、修改、组合自己的 skills 和 tools（prompt 层的对应工作是 [[GEPA：REFLECTIVE PROMPT EVOLUTION CAN OUTPERFORM REINFORCEMENT LEARNING|GEPA]]）。 更激进的 Gödel Agent、Darwin Gödel Machine：直接 self-modify Agent code，搜索并保留更好的自身实现。 这里真正重要的变化是 accumulation：一次 response refinement 会消失，一次 test-time update 保留一段时间，而新的 memory / skill / tool / workflow 可跨 task 持续存在，成为下一轮 evolution 的基础。vault 内对应：[[ReasoningBank：Scaling Agent Self-Evolving with Reasoning Memor|ReasoningBank]]、[[AGENT WORKFLOW MEMORY]]、[[RAGEN：Understanding Self-Evolution in LLM Agents via Multi-Turn Reinforcement Learning|RAGEN]]。\n本文 §1 主线：改进从当前 output，逐渐进入 weights，最后沉淀到 Agent 本身。\n§2 Training-Time RSI：自己产生 learning signal 核心变化：系统自己产生 learning signal 并送回 training loop，improvement 通过参数更新跨 iteration 持续积累，直接进入下一轮 model。人从直接提供 label / reward，退到设计 training rule、监督整个 loop。\n（1）zero-label：人出题，模型自造 supervision Problem 仍由外部提供，但 supervision 由模型自己产生。三条路线：\n路线 learning signal 代表 Fine-tuning 自筛高质量 trajectory 作下一轮 SFT 数据 STaR（仍需正确答案）→ 只用 unlabeled questions + self-verification RL（self-rewarding RL） ① 内生信号：confidence / likelihood / entropy / self-certainty；② group 级：majority voting 伪 reward [[RLPR：EXTRAPOLATING RLVR TO GENERAL DOMAINS WITHOUT VERIFIERS|RLPR]]、[[GENERALIST REWARD MODELS：FOUND INSIDE LARGE LANGUAGE MODELS|Generalist RM]]、[[TTRL：Test-Time Reinforcement Learning|TTRL]]、[[The Majority is not always right：RL training for solution aggregation|Majority]] Distillation（on-policy self-distillation） student 自己 rollout，teacher 在其实际轨迹上给 dense token-level supervision OPSD（同一模型既当 student 又当 teacher，但仍需 verified solution）→ U-OPSD（连 reference label 也去掉，用 multi-rollout majority consensus 造 pseudo-solution） 三条路线分别用 trajectory、reward、token-level distribution，但本质相同：把原本由人提供的 supervision 搬进模型自己的 improvement loop。\n[!warning] zero-label 的两大风险 - self-confirming loop：generator 与 verifier 共享相似能力与 bias，错误沿 “model bias → biased evaluation → biased learning signal → stronger bias” 不断写回模型。 - training collapse：即使 reward 完全正确，训练动态也可能不稳定——在用真实 binary verifier 的 code RL 中出现过典型 rise-and-collapse，KL / EWC 等约束也无法完全阻止。\n（2）zero-data：连”学什么”也自己定 zero-label 仍需外部出题；zero-data 进一步让模型自己生成 problem / curriculum，即 self-play：Absolute Zero、[[R-Zero：Self-Evolving Reasoning LLM from Zero Data|R-Zero]] 采用 proposer / challenger ↔︎ solver 的 co-evolution；Agent0 把这种关系扩展到 agentic task，curriculum agent 与 tool-using executor 从零外部数据共同 evolve。\n[!example]+ co-evolution 长什么样（以 R-Zero 为例） challenger 根据 solver 当前能力生成新问题 → solver 解题 → 多数投票产生 pseudo label → 双方被重新训练 → 更强的 solver 反过来改变”下一轮值得出什么题”。curriculum 不来自人，而来自 solver 自身的能力边界。同路线另见 [[BEYOND PASS@1：SELF-PLAY WITH VARIATIONAL PROBLEM SYNTHESIS SUSTAINS RLVR|Variational Problem Synthesis]]。\nproblem 来源 learning signal 交给模型什么 zero-label 人出题 自造 how to learn zero-data 自造 自造 what to learn 但有一条边界：problem 与 learning signal 动态变化了，improvement procedure 仍由人预设——majority voting、uncertainty reward、filtering rule 等 evaluation mechanism 不会随 solver 提升而自动变强。\n[!warning] zero-data 的两大风险 - diversity collapse：proposer 收敛到一小类最容易满足 reward 的问题，curriculum 越来越窄。 - reward grounding failure：reward 无法锚定真实能力提升时，proposer 与 solver 的 co-evolution 共同偏离真正有价值的 learning frontier。\n（3）auto research：自造 improvement strategy 再往前一步：self-improvement 从 curriculum generation 扩展到 research process 本身。research agent 根据模型当前表现分析 failure、提出 hypothesis、选择 data / training recipe、运行实验，并按 evaluation result 持续调整后续 research decision。\nself-generated 的东西 zero-label supervision zero-data curriculum auto research improvement strategy §3 Verifier：RSI 的真正瓶颈 把两节放在一起，闭环实际在逐层扩展：\nanswer → experience → learning signal → problem / curriculum → verifier\nverification 很可能比 generation 更接近 RSI 的核心瓶颈——一个 self-improvement loop 能否持续，取决于它依赖的 evaluation signal 是否可靠。\n任务域 verification 手段 特点 math / code proof checker、unit test、execution feedback 易形成稳定自动 loop，但 criterion 预先固定 → bounded self-improvement open-ended agent / creative / research taste 需评价 novelty、usefulness、importance “什么算更好”本身难定义 → 越接近 open-ended RSI 越难 近期开始出现直接针对 verifier 本身的 self-improvement：\nSelf-Trained Verification：把 verifier 当训练对象，让 verification capability 随 iteration 提升。 Self-evolving Deep Research Agent：agent 能力演化时，同步更新评价 research output 的 rubric / verifier。 Meta-evaluation：不只评价 output，而是评价 evaluator 本身。 Red Queen Gödel Machine：agent 与 evaluator 共同 evolve，evaluation criterion 不再固定。 注意与 R-Zero 的区别：R-Zero 的 evaluation signal 随 solver 状态动态变化，但 majority voting、uncertainty reward 等 verification mechanism 仍是人为设定；上面这些工作进入 evolution loop 的，是 verifier 的能力乃至 criterion 本身。\n[!question] 全文悬而未决的核心问题 verifier 可以被 meta-verifier 评价，evaluator 可以与 agent co-evolve，但不断变化的 evaluation mechanism 如何保持与真实目标的 grounding？如何让 verifier 持续进化、又不与 policy 共同 drift（co-drift）？这是 closed-loop RSI 最关键也最难的问题之一，目前没有成熟答案。\n我的批注 ⚡ 全文最有力的框架是两条线：persistence（output → weights → Agent）与闭环逐层扩展（supervision → curriculum → strategy → verifier），把一堆 self-* 术语统一在”人逐步退出 loop”之下。 ⚡ §3 的判断与 [[Review：自进化Agent的RL Memory与Skill方法批判]] 形成互证：那边说 memory / skill 工作 over claim、真问题是 credit assignment；这边说 generation 侧已基本闭环、瓶颈全压到 verification——都指向 evaluation 被高估、verification 被低估。 ❓ verifier 自进化四方向（Self-Trained Verification / meta-evaluation / Red Queen）都还较新，值得各建一条 paper 笔记跟踪。 ❓ training collapse 机理文中未展开，可对照 [[CURE：Critical-Token-Guided Re-concatenation for Entropy-collapse Prevention|CURE]]（entropy collapse）与 [[ProRL：Prolonged Reinforcement Learning Expands Reasoning Boundaries in Large Language Models|ProRL]]。 行动项 精读参考文献：arXiv:2607.07663、《A Taxonomy of Self-Evolving Agents》(lsl.zone)、Anthropic《Recursive Self-Improvement》 建卡：Absolute Zero、Agent0、Darwin Gödel Machine、OPSD / U-OPSD、Red Queen Gödel Machine 想清楚：自己的 RLVR 实验里，怎么监测 reward / verifier 与 policy 的 co-drift？ 相关笔记 TABLE WITHOUT ID file.link AS \u0026quot;笔记\u0026quot;, date AS \u0026quot;日期\u0026quot; FROM \u0026quot;Research/Papers\u0026quot; WHERE contains(tags, \u0026quot;Reading\u0026quot;) AND (contains(file.name, \u0026quot;Self-Evolv\u0026quot;) OR contains(file.name, \u0026quot;自进化\u0026quot;) OR contains(file.name, \u0026quot;Self-Improvement\u0026quot;) OR contains(file.name, \u0026quot;R-Zero\u0026quot;) OR contains(file.name, \u0026quot;TTRL\u0026quot;)) SORT date DESC 参考文献 arXiv:2607.07663 A Taxonomy of Self-Evolving Agents (lsl.zone) Anthropic：Recursive Self-Improvement ","date":"2026-08-31T00:00:00Z","permalink":"/rsirecursive-self-improvement-%E5%88%86%E7%B1%BB%E5%9B%BE%E8%B0%B1/","title":"RSI：Recursive Self-Improvement 分类图谱"},{"content":"","date":"2026-08-03T00:00:00Z","permalink":"/nope/","title":"NoPE"},{"content":"","date":"2026-07-30T00:00:00Z","permalink":"/%E9%9D%A2%E7%BB%8F_2026/","title":"LLM 算法岗面试手册 2026"},{"content":"MOPD：Multi-Teacher On-Policy Distillation [!info] TL;DR 核心问题：经领域 RL 训练后的 LLM 在单领域很强，但如何把多个领域（Math / Code / IF / SWE）专家融合成一个统一模型？ 核心方法：Multi-Teacher On-Policy Distillation —— 三阶段流水线（General SFT → 各领域 RL 并行得 teacher → 多 teacher 蒸馏）。学生自己 rollout（on-policy），每个 prompt 路由到对应 domain teacher，teacher prefill 给 per-token log-prob，用 reverse KL 让学生模仿。 关键结果：在 Qwen3-30B-A3B 上 normalized score 0.937（vs RL Teacher 1.000、Mix-RL 0.882、Cascade RL 0.775、Param-Merge 0.328–0.857）。跨 domain 极差仅 0.044，最均匀。已在 MiMo-V2-Flash (309B) 生产训练中部署。\n阅读地图 §1 问题与动机 §2 四种已有范式与不足 §3 MOPD 三阶段流水线 §4 反向 KL 的两种实现 §5 关键实验结果 §6 关键发现：same-origin / PG vs Top-K / 多轮迭代 §1 问题：为什么要融合多领域专家？ 经领域 RL 训练后的 LLM 在单领域很强（math、code、instruction-following、SWE 各自 SOTA），但如何把多个领域的能力合并到一个模型里，仍然是开放的工业难题。论文一针见血地指出：\n[!quote] 论文核心论点 Existing paradigms trade off learning efficiency, peak capability, and stability. \u0026gt; 现有方法在学习效率 / 峰值能力 / 训练稳定性三者间做取舍，没有兼顾的方案。\n背后的具体矛盾：领域 RL 让模型在某个 domain 走得很远，但跨 domain 的优化信号会互相干扰——简单的”把数据混起来继续 RL”会引发 see-saw effect，某个能力涨了、另一个能力就掉。\n§2 四种已有范式为什么不够？ 范式 做法 主要缺点 Mix-RL 把所有 domain 的 prompt 混合到一个数据集联合 RL 跨 domain 信号互相干扰，see-saw effect（顾此失彼） Cascade RL 依次训练各 domain（一个接一个） 早期能力灾难性遗忘，稳定性风险被放大 Off-Policy SFT 在 teacher 的 rollout 上做 SFT exposure bias——学生分布与 teacher 失配，推理时学生走出 teacher 没覆盖的路径就翻车 Param-Merge 权重平均 / Task Arithmetic 训练不稳定，通常达不到任一 teacher 的水平 [!warning] 一个具体的失败数字 在 Qwen3-30B-A3B 上，Param-Merge（简单平均）的 normalized score 仅 0.328，远低于任一 teacher（= 1.0）。即使改进为 Task Arithmetic，也只能到 0.857，仍有 0.143 的 headroom 吃不到。\n四种方案覆盖了”数据视角 / 时序视角 / 监督视角 / 参数视角”，但都没做到让一个模型在多 domain 上同时逼近各自的 teacher。\n§3 MOPD 流水线（三阶段） flowchart LR A[\u0026quot;Stage 1\u0026lt;br/\u0026gt;General SFT\u0026lt;br/\u0026gt;通用 SFT 训练\u0026quot;] --\u0026gt; B[\u0026quot;Stage 2\u0026lt;br/\u0026gt;Per-domain RL\u0026lt;br/\u0026gt;各领域并行 RL\u0026quot;] B --\u0026gt; T1[\u0026quot;Math Teacher\u0026quot;] B --\u0026gt; T2[\u0026quot;Code Teacher\u0026quot;] B --\u0026gt; T3[\u0026quot;IF Teacher\u0026quot;] B --\u0026gt; T4[\u0026quot;SWE Teacher\u0026quot;] T1 --\u0026gt; C[\u0026quot;Stage 3\u0026lt;br/\u0026gt;MOPD Distillation\u0026lt;br/\u0026gt;多 teacher 蒸馏\u0026quot;] T2 --\u0026gt; C T3 --\u0026gt; C T4 --\u0026gt; C C --\u0026gt; D[\u0026quot;统一 Student\u0026lt;br/\u0026gt;多能力模型\u0026quot;] 关键设计： - Stage 1 通用 SFT：得到一个共享起点，让后续各 domain RL 不至于完全分叉 - Stage 2 各 domain RL 并行：recipe 解耦、风险隔离（一个 domain 训崩不影响其他） - Stage 3 on-policy 蒸馏： - 学生自己 rollout（不是用 teacher 的 rollout）→ 没有 exposure bias - 每个 prompt 路由到对应 domain teacher → teacher 用 prefill 给 per-token log-prob - 训练目标 = reverse KL（学生分布向 teacher 看齐）\n[!note] 为什么 routing + on-policy 重要 routing 保证信号归属明确（math prompt 的 loss 只来自 math teacher，不被 code teacher 干扰）；on-policy 保证分布匹配（学生在训练时探索的路径就是推理时会走的路径）。\n§4 反向 KL 的两种实现 4.1 通用 reverse-KL 形式 \\[\\mathcal{L}_{\\mathrm{rev\\text{-}KL}}=\\mathbb{E}_{x,y\\sim\\pi_{\\theta}}\\Big[\\tfrac{1}{|y|}\\sum_{t}\\sum_{v}\\pi_{\\theta}(v)\\log\\tfrac{\\pi_{\\theta}(v)}{\\pi_{\\phi_{d}}(v)}\\Big]\\]\n反向 KL = teacher 在 student 分布下的期望。Student 选择哪些 token，teacher 就评估那些 token 的概率比。这与 forward KL（teacher 在 teacher 分布下的期望，mode-covering）相反——reverse KL 是 mode-seeking，鼓励 student “敢于走 teacher 走过的路径”，但不支持 student 探索 teacher 之外的路。\n4.2 两种等价实现 [!example]+ 两种 loss 形式（折叠查看完整推导）\nPolicy-Gradient 形式 (Eq. 4) —— 需要完整 teacher 分布 \\[\\hat{A}^{\\mathrm{clip}}_{\\mathrm{MOPD},t}=\\mathrm{clip}\\big(\\log\\pi_{\\phi_{d}}(y_t)-\\log\\pi_{\\theta}(y_t),\\, -A_{\\max},\\, +A_{\\max}\\big)\\] \\[\\mathcal{L}^{\\mathrm{PG}}_{\\mathrm{MOPD}}(\\theta)=-\\mathbb{E}_{x,y}\\Big[\\tfrac{1}{|y|}\\sum_{t}\\hat{A}^{\\mathrm{clip}}_{\\mathrm{MOPD},t}\\log\\pi_{\\theta}(y_t)\\Big]\\]\nTop-K 形式 (Eq. 5) —— 只用 teacher 的 top-k token \\[\\mathcal{L}^{\\mathrm{TopK}}_{\\mathrm{MOPD}}(\\theta)=\\mathbb{E}_{x,y}\\Big[\\tfrac{1}{|y|}\\sum_{t}\\sum_{v\\in\\mathcal{T}^{d}_{t}}\\big[\\pi_{\\theta}(v)\\log\\tfrac{\\pi_{\\theta}(v)}{\\pi_{\\phi_{d}}(v)}-\\pi_{\\theta}(v)+\\pi_{\\phi_{d}}(v)\\big]\\Big]\\]\n核心区别： - PG 形式需要完整 \\(\\pi_{\\phi_d}(\\cdot)\\) 分布（显存开销大） - Top-K 形式只用 teacher 的 top-k token（\\(\\mathcal{T}^d_t\\)），加 bias-correction 项 \\(\\pi_{\\phi_d}(v) - \\pi_\\theta(v)\\) 保证 \\(\\pi_\\theta = \\pi_{\\phi_d}\\) 时 loss 最小——显存友好 - 同源 teacher 下两者差距很小（PG 0.937 vs Top-K 0.909）\n§5 关键实验结果（Qwen3-30B-A3B） 方法 Norm. Score vs RL Teacher 备注 RL Teacher（任一） 1.000 — 各 domain teacher 的参考上界 Mix-RL 0.882 −0.118 see-saw Cascade RL 0.775 −0.225 遗忘最严重 Off-Policy Finetune 0.824 −0.176 exposure bias Param-Merge (Avg) 0.328 −0.672 简单权重平均，几乎全废 Param-Merge (Task Arith.) 0.857 −0.143 线性算术，仍有 headroom MOPD (本文) 0.937 −0.063 headroom 吃得最多 统一性（uniform profile）：MOPD 跨 Math / IF / SWE 三个 domain 分数极差仅 0.044，而 Cascade RL 极差达 0.41——MOPD 是唯一跨 domain 均匀的方案。\n样本效率：在 IF / SWE 上，MOPD 比 Mix-RL 节省约 5× 样本。\n大规模验证：在 MiMo-V2-Flash (309B) 上，MOPD 在 Math / Code / IF / SWE / Tool Use 大多数 benchmark 上持平或超过 teacher。\n§6 关键发现（论文核心 insight） [!tip] 关键发现 \u0026amp; 工程优势 三大 insight： 1. 必须 same-origin teacher：把 Math teacher 换成 Qwen3-235B-A22B（5× 初始 KL：0.19 vs 同源 0.04），训练渐进退化，Top-K 形式甚至灾难性发散。多 teacher 蒸馏对 teacher-student 的”亲缘关系”非常敏感。 2. PG vs Top-K 同源 teacher 下差距很小：Norm. score 0.937 vs 0.909（4.8 个百分点）。显存允许时优先 PG；Top-K 是省 memory 的等价替代。 3. 多轮迭代持续提升：Iter-2 normalized 到 0.986（比 Iter-1 +0.049），说明学生作为新一轮 teacher 还能继续压榨性能。\n论文没说但值得注意的工程优势： - Dense per-token supervision → 低方差、高样本效率（解释了 5× sample saving） - Recipe 解耦 → Stage 2 各 domain 独立，故障不会污染其他 domain - 可直接 deploy 到生产训练 → MiMo-V2-Flash 309B 已经在用\n我的批注 ⚡ 亮点：把”routing + on-policy + reverse KL”三件事打包成一个高度工程化、且直接 deploy 到 309B 生产训练的框架；这种”方法 + 系统 + 生产验证”三位一体的论文非常少见 ❓ 疑问：Top-K 形式在 cross-origin teacher 下灾难性发散——是梯度方向错还是 KL 估计的方差爆炸？需要看附录 §4.4 的稳定性分析 ❓ 疑问：Iter-2 把学生当新 teacher 继续蒸馏，会不会引入能力放大但分布偏移的问题？论文没有给”为什么会收敛”的理论分析 🔗 相关笔记： [[Research/Papers/LLM-RL/Group Sequence Policy Optimization]] —— GSPO 也是 on-policy 思路，可与 MOPD 的 on-policy 设计做对比 [[Research/Papers/LLM-RL/GVPO：Group Variance Policy Optimization for Large Language Model Post-Training]] —— 同属 post-training 优化 [[Research/Papers/tech-report]] —— MiMo-V2-Flash 技术报告（如果有）应该会更详细地介绍 309B 的训练细节 行动项 读论文 §4.4.1–4.4.3 关于 same-origin teacher 的消融，理解 cross-origin 退化的根因 查 MiMo-V2-Flash 技术报告，看 production 训练中 MOPD 的具体超参和 scaling 行为 思考：能否把 MOPD 的 routing + on-policy 思路推广到 pre-train + SFT + RLHF 多源 的混合蒸馏？ 复现：用 Qwen3-30B-A3B + 两个 domain teacher，跑小规模 MOPD 验证 normalized score 的 headroom 收敛 相关论文（自动聚合） TABLE date, title FROM #LLM-RL WHERE !contains(file.name, \u0026quot;MOPD\u0026quot;) SORT date DESC LIMIT 5 元数据\narXiv: 2606.30406v1 出版：2026 年 6 月 类别：cs.CL, cs.LG 作者：Wenhan Ma, Jianyu Wei, Liang Zhao, Hailin Zhang, Bangjun Xiao, Lei Li, Qibin Yang, Bofei Gao, Yudong Wang, Rang Li, Jinhao Dong, Zhifang Sui, Fuli Luo 单位：Xiaomi MiMo 团队 + 清华大学 ","date":"2026-07-29T00:00:00Z","permalink":"/mopdmulti-teacher-on-policy-distillation-for-capability-integration-in-llm-post-training/","title":"MOPD：Multi-Teacher On-Policy Distillation for Capability Integration in LLM Post-Training"},{"content":" [!info] 来源 整理自 ali（@waterloo_intern）于 2026-07-27 发布的 X Article：22580: From GPT2 to Kimi3, Explained。本文不是逐句翻译，而是对原文论证主线的中文重组。\n一句话总结 从 GPT-2 到 Kimi K3，不只是把参数量扩大了 22580 倍。更关键的演进是：模型不断重新设计信息如何存储、旧信息如何被更新或遗忘，以及需要的信息如何被检索。\nKimi K3 最终组合了四种互补能力：\nKDA：以固定大小的循环状态保存长序列信息； 周期性 MLA：保留对 token 上下文的精确 softmax 检索； 稀疏 MoE：增加参数容量，但避免每个 token 激活全部参数； AttnRes：让深层网络选择性读取较早层的表示。 文章主线 GPT-2 / Softmax Attention ↓ KV cache 随序列增长，访存开销大 Linear Attention ↓ 固定状态高效，但信息会相互干扰 DeltaNet ↓ 能精确替换关联，但缺少全局遗忘 Gated DeltaNet ↓ 增加可学习衰减，但控制仍较粗 Kimi Delta Attention（KDA） ↓ 逐通道精细控制记忆衰减 Kimi K3 = KDA + 周期性 MLA + Latent MoE + AttnRes 1. GPT-2：标准 Decoder-only Transformer GPT-2 的输入由 token embedding 和 positional embedding 相加得到。每个 Transformer block 包含 self-attention 和 MLP，并通过残差连接组织起来。最后的 hidden state 经 language-model head 映射为词表 logits。\n自回归生成时，每一步只用最后一个位置的 logits 选择下一个 token，但模型需要利用此前全部 token。若每次都重新计算历史 token 的 K、V 投影，会产生大量重复工作，因此引入 KV cache 保存历史 token 的 key 和 value。\nKV cache 解决了重复计算，却带来新的问题：\ncache 容量随序列长度 \\(N\\) 线性增长； decode 每一步都需要从 HBM 读取历史 K、V； 长上下文下容易形成显存容量与内存带宽瓶颈。 原文以约 124M 参数的 GPT-2 为基准：词表约 50k、12 层、12 个注意力头、hidden size 为 768。若按 Kimi K3 的 2.8T 总参数计算，一个 Kimi K3 约等于 22580 个 GPT-2 的参数量。\n22580 这个数字揭示的是尺度差异，但全文真正讨论的是：这些新增容量被放在了哪里，以及它们解决了什么问题。\n2. Linear Attention：把增长的 KV cache 压缩为固定状态 标准 softmax attention 可以简写为：\n\\[ \\operatorname{Attn}(Q,K,V)=\\operatorname{softmax}(QK^T)V \\]\nsoftmax 作用在 \\(QK^T\\) 之后，因此不能简单改变矩阵乘法顺序。Linear Attention 对 \\(Q\\)、\\(K\\) 分别应用非负特征映射 \\(\\phi\\)，例如 \\(\\mathrm{ELU}(x)+1\\)：\n\\[ \\operatorname{Attn}(Q,K,V) \\approx \\frac{\\phi(Q)(\\phi(K)^TV)}{\\phi(Q)(\\phi(K)^T\\mathbf{1})} \\]\n利用乘法结合律，可以先维护固定大小的状态：\n\\[ S_t=S_{t-1}+\\phi(k_t)^Tv_t \\]\n随后用 query 从状态中读取：\n\\[ o_t=\\phi(q_t)S_t \\]\n这样，历史 K、V 不再沿 token 维不断拼接，而被折叠进一个固定的 \\(D\\times D\\) 状态。其优势是 decode 状态不随序列长度增长。\n代价是：\\(\\phi\\) 对 softmax kernel 的表达能力有限，因此可能损失精确性。Linear Attention 保留了“非负打分—归一化—对 value 加权平均”的基本契约，但使用了更容易重排计算、也更弱的特征映射。\n3. DeltaNet：固定容量记忆需要覆盖策略 Linear Attention 的核心状态是有限容量的关联记忆。若不断执行加法更新：\n\\[ S_t=S_{t-1}+k_t^Tv_t \\]\n不同 key-value 关联会被写入同一个矩阵。当序列长度远大于状态容量时，关联会互相干扰，而且旧信息从未被移除。\nDelta rule 的思路不是直接叠加新 value，而是先读取当前 key 已经指向的内容，再只写入差值：\n\\[ v_{old}=k_tS_{t-1} \\]\n\\[ u_t=\\beta_t(v_t-v_{old}) \\]\n\\[ S_t=S_{t-1}+k_t^Tu_t \\]\n其中 \\(\\beta_t\\) 控制写入强度。直观上，这相当于：\n在 key 指向的位置读取旧关联； 计算目标 value 与旧 value 的差； 删除旧信息并写入新信息。 因此，DeltaNet 能对某个特定关联做更精确的替换，而不是让信息无止境叠加。\nChunk-wise 并行 Delta rule 看起来是严格递归的：第 \\(t\\) 步依赖第 \\(t-1\\) 步状态，不利于 GPU prefill。DeltaNet 通过数学重参数化和 chunk-wise 算法提高并行性：\n序列被切成大小为 \\(C\\) 的 chunk； chunk 内采用类似 masked attention 的并行矩阵运算； chunk 间通过固定状态递归传递信息。 两个极端是：\n\\(C=N\\)：退化为标准 \\(O(N^2)\\) attention； \\(C=1\\)：退化为逐 token 的 linear attention。 中间的 \\(C\\) 在 FLOPs 与 GPU 矩阵乘法效率之间折中。原文指出实践中常用 64 或 128，因为这类粒度更适合 tensor core。\\(C=1\\) 的 FLOPs 最少，但 wall-clock time 不一定最好。\n4. Gated DeltaNet：在精确替换之外加入遗忘 Delta rule 能替换某个 key 对应的关联，却不擅长：\n上下文切换时一次性清除多条旧关联； 让整个记忆随时间衰减； 主动释放有限状态中的容量。 Mamba 类更新提供了整体衰减思路：\n\\[ S_t=\\alpha_tS_{t-1}+k_t^Tv_t \\]\n其中 \\(0\\leq\\alpha_t\\leq1\\)：\n\\(\\alpha_t\\approx1\\)：保留旧状态； \\(\\alpha_t\\approx0\\)：清空旧状态。 但统一的标量衰减会等比例遗忘所有关联，无法区分不同信息的重要性。Gated DeltaNet 因而将两者结合：\nDelta rule 负责定向替换某一关联； gate 负责让旧状态自适应衰减。 其本质是同时具备“写什么”和“忘多少”的能力。\n5. Kimi Delta Attention：从标量遗忘到逐通道遗忘 Kimi Linear 的关键改进是将粗粒度标量 gate 变为更细粒度的逐通道 decay。也就是说，不再让整个状态按同一比例遗忘，而是让每个 channel 学习不同的衰减值。\n这给固定容量记忆增加了更有针对性的容量：不同通道可以保留不同时间尺度、不同重要程度的信息。\n原文将 Kimi Linear 相对 DeltaNet Transformer 的变化概括为：\n使用 KDA 与 Multi-head Latent Attention（MLA） 交错的混合结构； 以 Mixture-of-Experts（MoE） 替代普通 MLP； 通过 alpha projection 为 DeltaNet 增加逐通道记忆控制能力。 这里的扩容不是盲目堆参数，而是把容量放到有明确功能的位置：提高对记忆衰减的控制精度。\n6. Kimi K3：混合记忆、检索与稀疏容量 据原文介绍，Kimi K3 的 language backbone 包含 23 个四层 macrocycle。每个 macrocycle 中：\n前 3 层使用 Kimi Delta Attention； 第 4 层使用 Multi-head Latent Attention； 第 1 层使用 dense FFN； 其余层使用 latent MoE。 相对 Kimi Linear，Kimi K3 还加入或强化了：\n更大规模； 每 12 层一次的 blockwise AttnRes； MLA query LoRA 与 output gating； latent-space MoE； SiTU activation； Gated MLA。 6.1 KDA 与 MLA 为什么共存 两种机制承担不同角色：\nKDA 将历史压缩到固定大小状态，decode 高效，但必然丢失部分信息； MLA 保留基于 softmax 的 token 级检索，可以精确访问上下文，但成本随上下文增长。 因此，Kimi K3 不是完全用 linear attention 替代 softmax attention，而是采用混合结构：大部分层使用常量状态的 KDA，周期性插入 MLA 修复精确检索能力。\n6.2 Latent MoE 传统 MoE 用 router 将 token 分配给少数专家。原文给出的 Kimi K3 配置为：\n共 898 个专家； 2 个 shared experts 处理所有 token； 其余 896 个专家中，每个 token 选择 16 个。 MoE 增加总参数容量，但每个 token 只激活一小部分专家，从而避免计算量与总参数量同比增长。\nKimi K3 还让专家在压缩后的 latent space 中工作。原文称这种设计接近将 FLOPs 减半，并抵消新 activation 带来的部分 kernel 开销。\n6.3 SiTU 与工程实现 Kimi K3 用 SiTU 替换传统 SwiGLU 路径中的激活方式。原文强调了一个重要的系统事实：架构上的数学改进不等于实际推理更快。\n如果没有 fused kernel，新 activation 路径可能接近原路径的 3 倍慢；因此模型架构必须与 kernel 实现共同设计。latent-space experts 的计算压缩，就是抵消额外操作的一种方式。\n7. AttnRes：沿网络深度选择性读取历史表示 普通残差流可以写成：\n\\[ h_l=h_1+\\sum_{i=1}^{l-1}f_i(h_i) \\]\n其中所有前层输出以同等权重累加。这样有两个问题：\n不同层可能需要不同类型的早期表示，但无法选择； 随深度增加，残差不断累积，后层需要产生更大的输出才能影响整体状态，可能造成 residual dilution、hidden-state growth 和训练不稳定。 AttnRes 为每项增加可学习权重：\n\\[ h_l=\\alpha_0h_1+\\sum_{i=1}^{l-1}\\alpha_if_i(h_i) \\]\n权重 \\(\\alpha_i\\) 由 query-key 点积得到，并经归一化后形成对早期 residual states 的加权组合。因此当前层不必只依赖直接前驱，而能选择性检索更早、更有用的深度表示。\nKimi K3 并非每层执行一次 AttnRes，而是在固定 block 边界应用。原文描述为每 12 层一次；23 个四层 macrocycle 共 92 层，对应 8 个 block 表示。这样在控制开销的同时，保留大部分收益。\n原文给出的结果是：AttnRes 约增加 2% 推理延迟，同时带来：\n对早期表示的选择性检索，缓解 residual dilution 与 hidden-state growth； 约 1.25 倍 compute advantage。 AttnRes 与 MLA 解决的是同一类问题的两个方向：\nMLA 沿序列维度检索此前 token； AttnRes 沿网络深度检索此前层表示。 8. 如何理解整条演进链 架构 主要收益 暴露的问题 下一步改进 Softmax Attention + KV cache token 级精确检索 KV cache 随 \\(N\\) 增长，访存成本高 固定状态 Linear Attention 常量大小循环状态 加法写入导致关联干扰 Delta rule DeltaNet 精确替换指定关联 缺少整体遗忘和上下文切换能力 Gate / decay Gated DeltaNet 写入与整体衰减结合 标量衰减过于粗粒度 逐通道 decay KDA / Kimi Linear 精细记忆管理 固定状态仍会丢信息 周期性 MLA Kimi K3 混合检索、稀疏容量与深度检索 系统复杂度与 kernel 实现要求提高 软硬件协同优化 核心洞见 1. 固定容量记忆必须有淘汰策略 只要状态维度固定，容量就一定有限。纯加法更新迟早会使不同关联互相干扰。因此模型必须学习：\n哪些信息值得保留； 哪些关联应该替换； 何时需要衰减或清空； 固定状态丢失的信息应从哪里重新检索。 这也是 gate、routing、decay 和 attention 反复出现的共同原因。\n2. Attention 的核心价值是选择性读取 attention 不只是一种 \\(O(N^2)\\) 算子，更重要的是它提供了内容寻址的 selective read：根据当前 query 选择与之相关的信息。\nKimi K3 在三个维度使用了“选择”：\nKDA 的 gate 选择保留或忘记哪些状态； MoE router 选择激活哪些专家； MLA 与 AttnRes 分别在 token 维和 depth 维选择读取哪些表示。 3. Scaling 仍然重要，但容量必须有功能位置 22580 倍参数差距并不能单独解释架构进步。原文的主张是：每一次扩容都应该针对前一代系统的明确瓶颈，例如：\n用状态矩阵替代无限增长的 cache； 用 Delta rule 修复关联覆盖问题； 用 channel-wise decay 提升记忆控制精度； 用 MoE 增加稀疏容量； 用 MLA 和 AttnRes 补回固定状态无法保存的信息。 4. 理论 FLOPs 最低不代表实际最快 chunk size、tensor core 粒度、kernel fusion、HBM 访问和中间张量布局都会影响实际性能。因此理解现代模型不能只看公式，还要同时看：\nprefill 与 decode 的不同瓶颈； 计算复杂度与访存复杂度； GPU 是否能高效执行该算子； 新结构是否有成熟 fused kernel。 我的归纳 可以把 Kimi K3 看成一个多层级记忆系统：\nKDA 是工作记忆：容量固定、更新快，适合大多数 token； MLA 是上下文档案检索：成本更高，但能精确回看原始 token； AttnRes 是深度记忆检索：从早期网络层恢复被后续残差稀释的表示； MoE 是知识容量池：按需激活不同参数子集。 这解释了为什么最终方案是混合架构，而不是让某一种 attention 完全取代其他机制：不同机制在容量、精度、计算和访存之间有不同的 Pareto 前沿。\n阅读时需要注意 原文是一篇个人 worklog，包含作者基于论文与实现的解释，不等同于 Kimi 官方技术报告。 “2.8T”“898 experts”“AttnRes 开销约 2%”“1.25x compute advantage”等数字均按原文记录；严谨引用时应继续回溯 Kimi K3 官方报告或代码。 原文包含大量示意图和实现片段。本笔记保留了概念与关键公式，没有逐张搬运图片或逐段复制代码。 原文标题写作 GPT2、Kimi3，正文写作 GPT-2、KimiK3；本文统一记为 GPT-2 与 Kimi K3。 参考 ali：22580: From GPT2 to Kimi3, Explained ali 的 X 主页 ","date":"2026-07-29T00:00:00Z","permalink":"/%E4%BB%8Egpt-2%E5%88%B0kimi-k3/","title":"从 GPT-2 到 Kimi K3：大模型架构如何演进"},{"content":" 一篇对当前 “用 RL 提升 memory / skill refinement” 这条线的批判性综述。作者把现状分成 4 类：SkillRL、MemRL、纯 prompt memory、search-augmented RL。本笔记整理作者的核心观点,并与 vault 内已有的相关论文笔记交叉链接。\n总论点 真正的”真问题”不是 memory 本身,而是 credit assignment。\n在 agent RL 中,难点不是”存点东西”,而是哪一步该被奖励,哪段经验对哪一步有用。 当前大量 “self-evolving / 自进化” 的工作,核心 memory / skill 主要靠 prompting 或外部模型生成,把”外部经验整理”说成”RL 学会自己整理出 skill”——over claim 重于 RL 实质。 普遍现象:benchmark 节节攀高,但 per-step latency、retrieval infra、memory poisoning / staleness、cache invalidation、rollout 成本、offline / online mismatch 几乎都不讨论。baselines 也偏弱(no-memory、naive full-context、passive semantic retrieval)。 ⚠️ 关键公平性提醒: 既然号称”self-evolving”,就不应该在 runtime 之前对模型做黑箱 SFT。一旦做了 SFT,模型就已经从历史轨迹中学到环境细节,根本没有 “evolve” 的意义。\n一、SkillRL Paper: arxiv.org/pdf/2602.08234 — Evolving Agents via Recursive Skill-Augmented Reinforcement Learning\n公开定位 原始 trajectory → 蒸馏成层级 SkillBank → RL 训练时检索进 prompt。在 ALFWorld、WebShop 和 7 个 search tasks 上优于 baseline。\n值得肯定 真问题: raw trajectory 太噪、太长、太贵。抽象成 rule / heuristic 再注入,确实比直接塞原始日志更省 token、更高效。 蒸馏方向是对的。 但水分很大 “self-evolving” ≠ RL 学 skill SkillBank 是通过 experience-based distillation 构建和更新的。 不是 end-to-end 把 skill writer / retriever 放进 policy gradient 里学。 本质是外部抽取 + prompt 注入,不是 RL 学 skill。 代码里用 gpt-o3 这种闭源大模型做 skill 蒸馏,效果能不好吗? 更像是 experience distillation pipeline,不是”RL 学 skill”系统。 有效部分主要来自表示层的 abstraction,不来自 RL 机制本身。 评测不公 没有 test set,直接用 validation set 评测。 失败的 cases 拿去更新 skill bank,接着再评测。 跟 Reflexion 类似的”路数”,现实里几乎不可能这样做。 相关工作 SKILL0: In-Context Agentic Reinforcement Learning for Skill Internalization — 其实是 GiGPO (verl-agent) 项目上的一点魔改,加了 memory 机制。沿用 GiGPO 的那几个 benchmark。 GiGPO 原项目: alfworld 只评测了 in-distribution split,没有测 out-of-distribution split。后续这些 incremental 文章一律避而不谈。 二、SLEA-RL Paper: arxiv.org/pdf/2603.18079 — Step-Level Experience Augmented Reinforcement Learning for Multi-Turn Agentic Training\n核心主张 在多轮环境里,task-level retrieval 会失配,所以要做 step-level retrieval,并配合 step-level advantage。\n方向上对的两个点 retrieval 与 state 真正绑定 — 不再跟初始 instruction 锁死,而是跟当前 observation 走。这是对的。 正面碰 step-level credit assignment — 比很多”memory 论文”更重要的一步。 工业落地的顾虑 clustering 脆: 用 observation clustering 做 cluster-indexed retrieval。论文环境里有效,但 UI 变、DOM 变、tool schema 变怎么办? observation 模态一换,cluster 还能沿用吗? 系统太重: 同时引入 per-step retrieval、clustering / index、memory admission、step-level advantage estimation。latency 增加,failure mode 增加,可解释性反而下降。 对 benchmark 友好,对 production drift 敏感: 论文环境 ≠ 线上环境,这种 mismatch 决定了很多”方向没错”的论文工业价值不高。 局限 “self-evolving” 依然不是 fully learned memory。experience library 通过 semantic analysis 演化,而非 gradient update。 虽然比 SkillRL 更靠近 credit assignment,但 memory 演化本身仍不是 end-to-end 学出来的。 定位 适合回答”step-level retrieval 值不值?““credit assignment 能不能更细?”这类偏 research prototype 的问题,而非”线上 agent 怎么搭建”。\n三、MemRL Paper: arxiv.org/pdf/2601.03192 — Self-Evolving Agents via Runtime Reinforcement Learning on Episodic Memory\n公开主张 冻结 LLM,避免改参数。 把 plasticity 放到 episodic memory 上。 Two-Phase Retrieval: 先语义相关,再按 learned Q-value / utility 选。 utility 通过环境反馈持续更新。 评价:开创了一个方法 至少在学一个真正该学的东西: 用环境反馈给 memory item 学 utility。 这比纯 embedding retrieval 前进了一大步。 语义相似 ≠ 决策有用,表面相似 ≠ 可复用。MemRL 方向上更接近”真正的 memory RL”。 工业契合度 把”模型能力稳定”和”记忆可塑”分开,和工业实践很像。 现实里不想频繁动基座模型(API 模型贵、合规要求高、灰度上线严格),把 learning 放到外部 memory / retrieval 层通常更现实。 工程上: 一个主模型 + 一个 embedding provider + API 接口就能跑起来,除 vllm 推理外不需要 GPU 算力,实作可行。 严重问题 utility / Q-value 估计依赖环境反馈,反馈稀疏时还是难。\nmemory item 的 non-stationarity 很麻烦。\n论文省略了”sparse memory repo 到底能存多大,会不会爆炸”的讨论。\n最严重 — 高度怀疑是不是真的 RL:\n在当前框架下,retrieval 路径基本是确定性的:\n第一阶段: 给定 query,embedding 确定的 → 相似度排序确定。 从 query 扩展到 memory ID: top query groups 选出后,memory IDs 按既定规则查出,这一步也确定。 第二阶段打分: candidate memories 给定后,similarity 和 Q 值固定,hybrid scoring 和排序也确定。 最终选择: vanilla topk 是确定性的,tri-channel bucket selection 也是确定性的。 在 configs/rl_alf_config.yaml 中 epsilon = 0(LLM temperature 也为 0),没有 exploration。\n结论: 这不是 RL 意义上的 exploration method,而是:\n对 memory bank 的 exploration 较弱。 对 memory 的 credit coverage 也较弱。 path-dependent 严重:\nEpoch 1 构建初始 memory bank。 Epoch 2 起,retrieval 已经受 Epoch 1 memory bank 强烈影响。 retrieval 基本 greedy,早期被选中的 memories 不断被重复使用和更新。 Epoch 1 对后续训练轨迹的影响 disproportionately 大。 后续 epochs 不是独立学习轮次,而是在已带偏置的 memory state 上做 refinement。 后果: 早期好的 memories 被放大;早期差的、噪声 memories 也被锁定;大量 memories 始终没被充分测试。 四、Prompt-based memory (工业上最有用,学术上最容易被低估) 代表工作: - Mem0 — arxiv.org/pdf/2504.19413 - ExpeL — ojs.aaai.org/…/29936 - MemP — arxiv.org/abs/2508.06433 — vault 笔记: [[Memp：Exploring Agent Procedural Memory]] - SimpleMem — arxiv.org/abs/2601.02553\n共同形态 外部 memory store + retrieval + in-context guidance。都属于prompt 注入流派,不是端到端把 memory 学进参数。\n为什么工业价值最高 满足工业最看重的四件事:\n不依赖训练: 不需要每周重训 policy,不需要昂贵 rollout,不需要复杂 GRPO infra。 模型无关: 尤其适合黑盒 API 模型。ExpeL 动机里就明确提到 proprietary APIs 下无法做 parametric update。 可控: memory schema 可查; retrieval 可调; 错了能删; 支持灰度和回滚。 延迟 / 成本可优化: SimpleMem 这类把 token budget 当一等公民处理,工业里非常重要。 明显上限 不能真正改变 base policy。 只能”提醒”模型,不一定真学会。 retrieval 质量差时会把噪声喂进去。 落地建议 今天最该先上什么: Mem0 / SimpleMem 这类外部 memory 系统,远比 fancy 的 RL-memory 联训更值。 不是因为理论更强,而是满足工业最重要的指标: ROI、可控性、维护成本、上线速度。\nvault 内已有相关笔记: - [[Mem0：Building Production -Ready AI Agents with Scalable Long-Term Memory]] - [[Memp：Exploring Agent Procedural Memory]] - [[ReasoningBank：Scaling Agent Self-Evolving with Reasoning Memor]]\n五、Search-augmented RL 代表工作: ZeroSearch、StepSearch、EvolveR\n定位 让模型在 step-by-step reasoning 中主动生成搜索查询,用 outcome-based reward 做 RL。学的是 search / retrieval 本身,不是积累经验记忆。\n适用场景 open-domain QA、citation-heavy search、product / document search、multi-hop retrieval。\n优点 reward 比较干净(rule-based: 是否搜到、是否答对、是否命中)。 没什么 delay,适合可验证检索任务。 局限 现实场景复杂得多: action correctness、tool parameterization、session state drift、auth / permission、UI fragility、exception handling。 学会检索 ≠ 学会 agent。 vault 内已有: - [[ZEROSEARCH：Incentivize the Search Capability of LLMs without Searching]] - [[Search-R1：Training LLMs to Reason and Leverage Search Engines with RL]]\n六、整体批判 灌水 / 噪音多 — 普遍披着 “self-evolving RL” 的 over claim 外衣讲故事。 核心机制靠 prompt / 外部模型,但表述成”RL 学会自己整理出 skill”。 benchmark 节节攀高,但 per-step latency、retrieval infra、memory poisoning、cache invalidation、rollout 成本、offline / online mismatch 几乎都不讨论。 baselines 偏弱 — 常去比 no-memory、naive full-context、passive semantic retrieval 这种明显处于劣势的方法。 最严重的公平性问题: 既然号称 self-evolving,就不应该在 runtime 之前做黑箱 SFT。一旦 SFT,模型就已经从历史轨迹中学到了环境细节(好轨迹 / 坏轨迹、各种状态、工具 schema 都在 SFT 数据里),根本就没有 “evolve” 的意义。 七、本人总结 四类方法对比一览:\n类别 核心思路 RL 实质 工业落地 代表论文 SkillRL 蒸馏 trajectory → SkillBank → prompt 注入 弱(主要是 experience distillation) 低 SkillRL, SKILL0 SLEA-RL step-level retrieval + step-level advantage 中(碰 credit assignment) 中低(系统太重) SLEA-RL MemRL 冻结 LLM,学 memory utility (Q-value) 中(epsilon=0, exploration 弱) 中(实作可行,但 path-dependent) MemRL Prompt memory 外部 store + retrieval + in-context 无 高 Mem0, ExpeL, MemP, SimpleMem Search-augmented RL 学检索策略 + outcome reward 中(rule-based reward) 中 ZeroSearch, StepSearch, EvolveR 两个判断标准: - 真 RL 还是假 RL: 是否在 policy gradient 里端到端学 memory / skill writer / retriever? 还是只是 prompt 注入? - 真公平还是假公平: 有没有 test set? 有没有 out-of-distribution split? 有没有在 runtime 之前偷偷 SFT?\n如果两问答案都偏负,这篇论文的工业价值大概率不如一篇朴素的 prompt-based memory 工作。\n相关笔记 [[RSI：Recursive Self-Improvement 分类图谱]] —— 同主题分类图谱：test-time / training-time RSI 的闭环逐层扩展，以及 “verifier 才是 closed-loop RSI 的真瓶颈”——可与本文 “credit assignment 才是真问题” 互证。 ","date":"2026-07-23T00:00:00Z","permalink":"/review%E8%87%AA%E8%BF%9B%E5%8C%96agent%E7%9A%84rl-memory%E4%B8%8Eskill%E6%96%B9%E6%B3%95%E6%89%B9%E5%88%A4/","title":"Review：自进化Agent的RL Memory与Skill方法批判"},{"content":" 来源：verl 主线 V1 重构 · 给后训练工作者的三问\n仓库：verl-project/verl\n关联笔记： - [[Engineering/coding/verl/verl总体概览]] - [[Engineering/coding/verl/agent_loop]] - [[Engineering/coding/verl/veRL_Ray执行流程源码解析]] - [[Engineering/coding/verl/veRL_colocate实现源码解析]] - [[Engineering/coding/verl/DataProto]]\nverl 主线 pipeline 大改：我该关心的三件事 主入口 main_ppo.py 已迁到 V1：TransferQueue 数据仓库 + AgentLoop 服务化 rollout + 可插拔 trainer 模式，默认 trainer.use_v1=true。本文只回答三个真正关心的问题——为什么改、老脚本还能不能跑、我的自由度提高了多少。\n范围： V0.8.0 前后主线重构 关键 PR： #6067 · #6710 · #6823 入口命令： python -m verl.trainer.main_ppo（未变） TL;DR · 一屏结论 为什么改： 老主线假设「同步、定长、单轮」，为让 Agent（多轮/工具/变长/异步 off-policy）成为一等公民，把生成与训练解耦。 老脚本还能跑吗： 能。入口命令没变，默认 use_v1=true + trainer_mode=sync ≈ 老同步 PPO；常规 GRPO/PPO 脚本基本原样可跑。想要异步只是加几行配置。 自由度： 大幅提高。生成逻辑 / 采样策略 / 调度模式 / PPO 每个阶段都成了可插拔扩展点——多数定制不用再碰主循环。 Q1：为什么要动主线？目的是什么？ 一句话：老 pipeline 的三条隐含假设（同步、定长、单轮）挡住了 Agent 训练。大改就是把这三条拆掉。\n吞吐：生成与训练可并行，慢样本（多轮/长）不再拖住整批 GPU Agent：多轮对话 + 工具调用 + 变长/多输出轨迹成为一等公民 可插拔：同步↔︎异步、on↔︎off-policy、各阶段实现都变成可替换配置 老范式 V0 的局限 同步生成：trainer 阻塞等一整批 rollout 完成才继续。 定长批次：一个大 tensor batch 在 trainer 内流转，假设样本形状规整。 单轮为主：prompt→response 一来一回，rollout 与 trainer 共享进程/显存。 trainer 什么都管：worker、资源池、数据集、训练循环全塞进一个 RayPPOTrainer。 Agent 训练 V1 的需求 变长 + 多输出：一条轨迹可能几十轮、带工具返回，每 session 多个 output。 异步 / off-policy：允许用「稍旧权重」生成的轨迹继续训练，换取吞吐。 服务化 rollout：推理引擎（vLLM/SGLang）作为独立 server，AgentLoop 像调 API 一样发多轮请求。 解耦数据通道：生成侧与训练侧各自并行，中间要一个能存变长数据、带元数据的「仓库」。 图 A · V0 单体同步 → V1 解耦异步 V0 · RayPPOTrainer（单体 · 同步） - Dataloader → Actor + Rollout（共卡）→ PPO 更新 - 同一进程 · 同步 generate - 串行循环：① generate（阻塞整批）→ ② 训练 → ③ update → 下一轮\nV1 · 生成 / 数据 / 训练 / 权重同步 四块解耦 - Dataloader → AgentLoopManager（fire-and-forget · 每样本一 async 任务）→ LLM Servers（vLLM / SGLang 副本） - TransferQueue：KV 仓库 · {uid}_{sid}_{idx} · tags(staleness) - PPOTrainer.step()：actor / critic / ref · 读字段→算→写回 - CheckpointEngine：异步权重同步\n说明：上半（V0）虚线大框代表「一个进程内的 RayPPOTrainer」，Dataloader → 共卡的 Actor+Rollout → PPO 更新，串行循环必须等一整批 generate 阻塞完成才能训练，rollout 和训练抢同一批 GPU。下半（V1）生成侧：Dataloader → AgentLoopManager → LLM Server 副本，慢样本不再拖住快样本。中间深色块 TransferQueue 是唯一数据交汇点，key 形如 {uid}_{sid}_{idx}。下排训练侧 PPOTrainer.step() 与 TransferQueue 双向交互。右侧橙色虚线 = 权重同步，训练完的权重经 CheckpointEngine 异步回灌给 LLM Servers，这是生成侧与训练侧唯一的强耦合点。\nV1 把老的「大 trainer」拆成三个组件：生成侧（AgentLoopManager + LLM Server）、数据桥（TransferQueue）、训练侧（PPOTrainer + CheckpointEngine）。\nQ2：老脚本还能跑吗？怎么快速适配？ 先给定心丸：入口命令没变——还是 python3 -m verl.trainer.main_ppo ...。默认 trainer.use_v1=true + trainer.v1.trainer_mode=sync，行为≈老的同步 PPO。仓库里 examples/grpo_trainer/*.sh 没写 use_v1 / trainer_mode，照样跑。临时退回旧实现：trainer.use_v1=false（走 main_ppo_v0.py），但它 deprecated、计划 v0.9.0 删除。\n启动脚本新增旋钮 配置项 默认 作用 trainer.use_v1 true 走 V1 主线；false 回退旧 main_ppo_v0.py（将删） trainer.v1.trainer_mode sync sync / colocate_async / separate_async trainer.v1.sampler.max_off_policy_threshold 8 一条轨迹最多允许跨几个权重版本 trainer.v1.sampler.max_off_policy_strategy drop 超阈值：drop 丢弃 / wait 等待 trainer.v1.colocate_async.num_warmup_batches 1 异步前预热多少批生成 trainer.v1.separate_async.num_warmup_batches 4 分离异步的预热批数 trainer.v1.separate_async.parameter_sync_step 4 每几步把新权重同步给 rollout actor_rollout_ref.rollout.agent.default_agent_loop single_turn_agent 默认用哪种 agent loop 同一个脚本：同步照跑；想开异步只加两行 # 常规同步（等价旧行为，无需任何改动） python3 -m verl.trainer.main_ppo \\ algorithm.adv_estimator=grpo \\ data.train_files=... actor_rollout_ref.model.path=... \\ trainer.n_gpus_per_node=8 trainer.nnodes=1 # 默认已是 trainer.use_v1=true, trainer.v1.trainer_mode=sync # 想开异步（共卡 + partial rollout）：追加两行即可 trainer.v1.trainer_mode=colocate_async \\ trainer.v1.colocate_async.num_warmup_batches=1 迁移清单 直接子类化旧 worker、依赖 verl/interactions、curriculum sampler、dynamic dataset、旧 tool examples：在 #6067、#6302 等 [BREAKING] 里被废弃/迁移，需改到 engine 与 AgentLoop 体系。 自定义过 rollout / 生成逻辑：改写一个 AgentLoop（见 Q3 骨架）。 reward / critic：reward 走 RewardLoopManager，配置路径 reward.reward_model.*。 separate_async 硬约束：train_batch_size == ppo_mini_batch_size、checkpoint_engine.backend 必须非 naive（nccl/nixl/mooncake）、colocate reward 不支持（需 reward.reward_model.enable_resource_pool=true）。 选哪种 trainer_mode？ trainer_mode 布局 partial rollout 权重同步时机 适用场景 sync colocate（共享显存） 关闭 每步：sample 后 sleep，train 后 update 单轮/短轨迹、要稳要复现（默认） colocate_async colocate 开启 每步 update + resume_generation；带 warmup 共卡想提吞吐、容忍轻度 off-policy separate_async 物理分卡 + 空闲切换 开启 按 parameter_sync_step 周期同步 大规模 / 长 agent 轨迹、最高吞吐 图 B · 时间线视角 sync：生成与训练完全不重叠 · 最 on-policy · partial rollout 关闭 colocate_async：共卡 · 生成（partial rollout）在后台持续 + warmup 批次 · 与训练时间上重叠 · 轻度 off-policy separate_async：分卡 · Rollout pool 持续流式生成 + 持续训练 · 两组 GPU 全程并行 · 按 parameter_sync_step 周期同步权重 三行主流程代码完全相同，差异只在 on_sample_end / on_step_end 两个钩子里怎么调 CheckpointEngine。\nGPU 分配：共卡 vs 分卡 三种模式中真正改变 GPU 分配的只有 separate_async。\nsync / colocate_async：1 个池（训练卡 + rollout hybrid 时分复用），卡数 = trainer.n_gpus_per_node × nnodes separate_async：2 个池（独立 rollout 池 + 训练卡空闲兼职），卡数 = 训练池同上 + rollout.n_gpus_per_node × rollout.nnodes separate_async 三条硬约束的根因 train_batch_size == ppo_mini_batch_size：全异步要求每个训练步恰好推进一个策略版本——一次 sample() → 一次梯度更新 → 同步一次权重。若一批被拆成多个 mini-batch 做多次 SGD，一个 step 内策略就跳了好几个版本，staleness 记账失真。 checkpoint_engine.backend != naive：naive（ColocatedCheckpointEngine）只做同卡 colocated 的 all_gather。分卡后必须换成 nccl/hccl、nixl/mooncake 等跨设备/跨节点后端。 colocate reward 不支持：colocate reward 靠「rollout 睡觉、腾出显存」窗口临时加载 reward model。separate_async 的独立 rollout 卡从不停机，没有这个窗口，所以 reward model 必须自己占独立卡。 异步流水线：塞 1 取 1 + 预热 V1 主循环是生产者/消费者流水线：每步 _add_batch_to_generate()（非阻塞）塞入 1 批 prompt；replay_buffer.sample()（阻塞轮询）等到够 1 批才取走（优先取最旧的压 staleness）。\n预热深度对比：\n模式 num_warmup_batches 默认 原因 sync 无（0） 刻意不重叠 colocate_async 1 共卡浅浅重叠一步 separate_async 4（+ parameter_sync_step=4） 分卡全程并行，流水线更深 一句话：预热深度 ≈ 流水线深度 ≈ off-policy staleness 预算。\ncolocate_async 一个 step 的解剖 塞入下一批 prompt（非阻塞，rollout 后台开跑） sample() 取走够一批的完成轨迹 on_sample_end：abort_replicas()（partial rollout）+ sleep_replicas()（rollout 交出显存/KV cache） 训练各阶段：此刻共享的卡归训练独占 on_step_end：update_weights() + resume_generation_replicas()（带新权重续跑半截轨迹） 快速选型：先 sync 跑通（几乎零改动）；生成把卡闲置严重、能接受轻度 off-policy，再上 colocate_async；轨迹很长/规模很大、且愿意配非 naive 权重后端，才上 separate_async。\nQ3：训练流程的自由度提高了多少？ V1 把「生成逻辑 / 采样策略 / 调度模式 / PPO 每个阶段」做成了可插拔扩展点。多数定制只需写一个子类 + 一行配置，不用碰主循环。\n想改的东西 怎么接入 入口 / 配置 生成 / 多轮 / 工具逻辑 写一个 AgentLoop 子类 @register + rollout.agent.agent_loop_config_path 整个 rollout 调度器 自定义 AgentLoopManager rollout.agent.agent_loop_manager_class 采样 / off-policy 策略 自定义 ReplayBuffer 子类 trainer.v1.sampler.custom_sampler.{path,name} 调度模式（同步/异步布局） 注册新 trainer_mode @register_trainer + 生命周期钩子 PPO 单个阶段（adv / logprob / …） override PPOTrainer._compute_* 子类 + register_trainer off-policy 修正（IS / decoupled） 配置 rollout_correction algorithm.rollout_correction.* reward（规则 / 模型） RewardLoopManager / reward server reward.reward_model.* PPO 每个阶段都是独立方法：\n# verl/trainer/ppo/v1/trainer_base.py :: PPOTrainer.step() _add_batch_to_generate → replay_buffer.sample → [_compute_reward_colocate] → _balance_batch → _compute_old_log_prob → [_compute_ref_log_prob] → [_compute_values] → _compute_advantage → [_update_critic] → _update_actor # 生命周期钩子： on_train_begin / on_step_begin / on_sample_begin / on_sample_end / on_step_end 示例 1：改「生成逻辑」· 写一个 AgentLoop 内置参考：single_turn_agent_loop.py（单轮）、tool_agent_loop.py（ReAct 多轮 + 工具）。\n# my_agent_loop.py from typing import Any from verl.experimental.agent_loop.agent_loop import ( AgentLoopBase, AgentLoopOutput, register, ) @register(\u0026quot;my_agent\u0026quot;) class MyAgentLoop(AgentLoopBase): async def run(self, sampling_params: dict[str, Any], **kwargs) -\u0026gt; AgentLoopOutput: messages = list(kwargs[\u0026quot;raw_prompt\u0026quot;]) prompt_ids = await self.apply_chat_template(messages) # 多轮就在这里 while 循环：生成 → 解析工具调用 → 追加观测 → 再生成 out = await self.server_manager.generate( request_id=uuid, prompt_ids=prompt_ids, sampling_params=sampling_params, ) return AgentLoopOutput( prompt_ids=prompt_ids, response_ids=out.token_ids, response_mask=[1] * len(out.token_ids), # 工具返回段可置 0 num_turns=2, ) 接入：\n# my_agents.yaml - name: my_agent _target_: my_agent_loop.MyAgentLoop actor_rollout_ref.rollout.agent.agent_loop_config_path=my_agents.yaml \\ actor_rollout_ref.rollout.agent.default_agent_loop=my_agent 示例 2：改「采样 / off-policy 策略」· 自定义 ReplayBuffer # my_sampler.py from verl.trainer.ppo.v1.replay_buffer import ReplayBuffer class MyReplayBuffer(ReplayBuffer): def sample(self, global_steps: int, partition_id: str, batch_size: int): batch, metrics = super().sample(global_steps, partition_id, batch_size) return batch, metrics trainer.v1.sampler.custom_sampler.path=my_sampler.py \\ trainer.v1.sampler.custom_sampler.name=MyReplayBuffer \\ trainer.v1.sampler.max_off_policy_threshold=8 示例 3：改「调度 / 权重同步时机」· 注册新 trainer_mode # my_trainer.py from verl.trainer.ppo.v1 import PPOTrainer, register_trainer @register_trainer(\u0026quot;my_mode\u0026quot;) class MyTrainer(PPOTrainer): def on_sample_end(self): self.checkpoint_manager.sleep_replicas() def on_step_end(self): if self.global_steps % 2 == 0: self.checkpoint_manager.update_weights(self.global_steps) 接入：trainer.v1.trainer_mode=my_mode。改 PPO 阶段同理 override _compute_advantage 等方法。\n番外 · 放到 slime / AReaL 里看 三家框架都在解同一道题——解耦生成与训练、加一个数据桥、异步同步权重、控制 staleness。差异主要在抽象哲学和解耦彻底程度。\nverl 依赖的 TransferQueue==0.1.8 是可复用独立组件（Ascend 开源）；verl 的异步 off-policy 修正直接指向 AReaL 论文——trainer_separate_async.py 里写着 # TODO: Support Decoupled PPO: https://arxiv.org/abs/2505.24298。\n图 C · 共同骨架 框架 生成 数据桥 训练 verl LLM Server + AgentLoop TransferQueue PPOTrainer（可插拔模式） slime SGLang + sgl-router Data Buffer Megatron 循环（裸写） AReaL rollout workers（可中断） Replay Buffer trainer workers（全异步） 维度 verl V1 slime AReaL 数据桥 TransferQueue Data Buffer Replay Buffer（用一次） 解耦 / 布局 sync / colocate_async / separate_async 三档 一个 --colocate 开关 完全分离集群 off-policy 控制 max_off_policy_threshold + rollout_correction over-sample + APRIL max_head_offpolicyness + decoupled PPO trainer 抽象哲学 结构化基类 + 注册表 + 钩子 刻意不封装，循环裸露 algorithm-first（AReaL-lite） slime · 极简抽象 三模块 + Data Buffer（Megatron + SGLang + Data Buffer）；靠 Ray 的 ray.get 位置切同步/异步；训练循环裸露在入口脚本。slime 明确不用 trainer 类包裹；与 verl「结构化基类 + 注册表 + 钩子」正好相反。长尾优化（APRIL）：over-sample → 够了 abort 其余 → 半截轨迹存 buffer、下轮续跑。对应 verl 的 partial rollout + min/max_global_steps。\n来源：THUDM/slime · 介绍博客\nAReaL · 完全异步 + decoupled PPO fully asynchronous：rollout worker 不等待、持续流式；trainer 攒够一批就更新，更新后同步权重回 rollout。两把稳定器：rollout.max_head_offpolicyness（=0 退化为同步，异步常用 2–8）+ actor.use_decoupled_loss（解耦行为策略 π_behav 与近端策略 π_prox）。论文报告相比同步系统最高 2.77× 加速且性能不降。verl separate_async 的 TODO 直指这篇；verl 的 rollout_correction 与 bypass_mode，本质就是这套修正在 verl 的落地。\n来源：inclusionAI/AReaL · 论文 arXiv:2505.24298\n一句话行动指南 先别慌： 现有 GRPO/PPO 脚本大概率照跑——默认 V1 sync ≈ 老同步 PPO，入口命令没变。 要吞吐/长轨迹： 加 trainer.v1.trainer_mode=colocate_async（或规模够大再上 separate_async + 非 naive 权重后端）。 要定制： 优先走三个扩展点——AgentLoop（生成）/ ReplayBuffer（采样）/ trainer_mode（调度），别再改主循环。 别踩坑： 若老代码子类化了旧 worker 或依赖 interactions / curriculum / dynamic dataset，需按 #6067 迁到 engine；use_v1=false 只是临时退路（v0.9.0 移除）。 本页基于本地 verl 仓库源码（分支 gsm8k @ 5c24e81d）梳理，面向已了解 verl 的后训练工作者。代码骨架基于当前源码 API，实验阶段接口可能微调，落地前请对照仓库确认。\n源码： verl-project/verl 关键文件： verl/trainer/main_ppo.py · verl/trainer/ppo/v1/trainer_base.py · verl/experimental/agent_loop/agent_loop.py 关键 PR： #6067 · #6710 · #6823 对比框架： THUDM/slime · inclusionAI/AReaL · AReaL 论文 ","date":"2026-07-23T00:00:00Z","permalink":"/verl-v1-%E4%B8%BB%E7%BA%BF%E9%87%8D%E6%9E%84/","title":"verl V1 主线重构 · 给后训练工作者的三问"},{"content":" 来源：DeepSeekV4 Infra Deep Dive · 第四节 4.4: PyTorch 训练显存管理：从 Caching Allocator 到 Stream 同步\n系列：可灵 AI infra 训练团队出品，全文系列见原文末链接。本文是该系列的 4.4 一篇，专门讲训练显存怎么分、怎么 OOM、怎么省。\n笔记以”按生命周期分四类”为主线展开。比按”哪一项占得多”分类更能看清 OOM 卡在哪一环：从初始化到 step 结束，四类显存分别在什么时刻驻留、用什么手段能挪走，挪到哪里去。\n01 allocated 与 reserved 的语义 这一段是后面所有判断的前提。\nallocated = caching allocator 实际切给存活 tensor 的字节数 reserved = allocator 从驱动一次性申请到的 segment 总量；驱动一次分一大块连续显存，叫一个 segment 两者永远满足 reserved ≥ allocated，差出来的是 allocator 持有的、暂时既不还驱动也没分出去的空闲 block，也就是碎片。\nreserved = allocated + 碎片 + 瞬态峰值被抬高后留下的「台阶」 「台阶」是 OOM 判断里最容易踩的坑：caching allocator 的 free 只把 block 退回池里复用、并不向驱动退还，整段 segment 全部空闲才有资格归还。瞬态峰值那几块大 block 常常和长寿命 block（激活缓存、通信预留）位于同一 segment，始终无法同时空出来，于是 reserved 只升难降——它记录的不是此刻用了多少，而是训练至今某一瞬间最多需要过多少连续显存。\n「allocated 不高却 OOM」通常就是这道 reserved 台阶造成的。\n02 四类训练显存：按生命周期划分 训练显存的增量主要来自反向传播。推理时单卡 HBM 上基本只有 weights，加一份随生成长度增长的 KV cache；训练多出的十几倍几乎都来自反向——chain rule 要求前向中间结果和梯度副本一直驻留到反向用完，于是 activation、梯度、optimizer state、重算中间值才同时占用卡上 HBM。下面四类是同一个反向需求在不同生命周期上的分布，去掉反向（即推理）它们全部消失。\n按生命周期划分，关注每一类显存什么时候分配、什么时候释放、有没有可能跨 step 还被保留。四类按占用从高到低排列：当前最高的那类降下来后，下一类就成为主导项。\n类别 生命周期 单卡量级（V3） 恒定占用（weights / grads / opt state） 跨 step 不变 68.5 GB Activation（forward saved） 单 step 单峰 131 GB（未压缩） 计算峰值（kernel 内瞬态） μs 尖峰 单层 GiB 级 通信 buffer（NCCL 预留） 跨 step 常驻 数百 MiB ~ 1 GB（EP all-to-all + DP all-reduce） 下面逐类拆开。\n02.1 恒定占用：weights / gradients / optimizer states 生命周期跨 step 不变：从初始化那一刻分配出来，持续保留到训练结束。每一步前向 / 反向 / optimizer 都要读它们，没有任何释放窗口。\n体现在 reserved 曲线上：训练启动后几个 step 内，reserved 分几次跳到一个稳定值，此后整段训练再不变。\n必须提前一次性分配。DDP 下模型刚 build 出来就占满。FSDP / ZeRO 是特例，按 rank 分片到多卡。除了沿 DP 副本维度分片的 FSDP / ZeRO，TP（按 head / hidden 划分）、PP（按层划分）、EP（按 expert 划分）也都在削减这条恒定基线，只是各占一个不同的维度。\n02.2 Activation：前向累积、反向释放 生命周期由 chain rule 决定：前向算到第 i 层时产生的中间结果，要保留到反向传播回第 i 层、读取完成后才释放。中间隔着「forward 跑完剩下层 + backward 倒回这一层」几十毫秒到秒级的窗口——跨 kernel 却不跨 step。\nallocated 曲线上是随 forward 单调累积、随 backward 单调消费的单峰曲线——前向到达峰值时全部 activation 都保留在 HBM 上，backward 每过一层释放一份。这段累积-释放曲线是 Activation Checkpointing 的优化目标。\n每个 micro-batch 临时申请：每一步开始前 activation 都不存在；前向算到哪层，那层中间结果就被 allocator 划出 block；分配路径仍留在 caching allocator 内部。\n上一个 micro-batch 释放的同档 block 留在池里复用。warmup 之后几乎所有 activation 分配都在 allocator 内部命中已有 block，驱动那条较慢的路径只在分桶不匹配（请求大小不落进任一现成 block 的尺寸区间）或 segment 已用尽时才走。\n02.3 单个 kernel 内的瞬态峰值 进入 kernel 时申请、退出 kernel 立刻释放，存在时间只有毫秒甚至微秒级。MoE 的 expert FFN intermediate、FlashAttention backward 的 work tensor、cuBLAS GEMM 的 workspace 都属于这一类。\n在 MemTrace 上表现为一道 kernel 刻度宽的尖峰，按时间平均下来几乎看不出来，瞬时却常常达到单层 GiB 级。因为存在时间太短、不进入前向保存的激活，手算 saved activation 时容易漏掉。\n常驻还是临时：看上去是临时的，kernel 跑完就标记为空闲。但 caching allocator 的 free 只把 block 退回池里复用、并不向驱动退还，整段 segment 全部空闲才有资格归还；瞬态峰值那几块大 block 常常和长寿命 block 位于同一 segment，始终无法同时空出来，于是 reserved 只升难降。\nallocated 曲线规律地起落，reserved 却是台阶式抬升。\n这类瞬时峰值的根本解决办法是 fused kernel，把多个 kernel 的中间结果保留在 SRAM、不写回 HBM，单次数据量不会产生几 GiB 的瞬态 buffer。\n02.4 通信 buffer：集合通信的中转缓冲 训练一开始就预分配，此后一直占用。NCCL 按上限一次性预留每条通道的发送 / 接收缓冲、ring 上的中转 buffer、reduce 算法的工作区。\n尺寸由四个因素共同决定：message size、rank 数、NCCL 选定的算法（ring / tree / NVLS）、bucket 聚合。\n若是等训练主循环跑起来之后才临时申请，cudaMalloc 会阻塞发起它的 CPU 线程，并触发 device-wide 同步：整卡所有 stream 已入队的工作要先排空，卡在集合通信的关键路径上。中后期 reserved 已接近上限，驱动这一刻可能分配不出连续的几百 MiB，等到通信发不出去才发现显存不够，训练 hang 在集合通信上。预分配在启动时一次性解决这些不确定性，跑起来之后通信 buffer 占用恒定不变，不再与主计算争用驱动。\n02.5 通信 stream：overlap 与显存池分裂的取舍 同一条 stream 上的 kernel 严格按提交顺序串行执行，通信和计算共用同一条 stream 时，allreduce 一发就阻塞后面的 GEMM，重叠执行无从谈起。\n为了 overlap，NCCL 的集合通信通常跑在独立的通信 stream 上：集合通信 kernel 独立排队执行，主计算 stream 同时还能发 GEMM。这不是强制要求——集合通信留在 default stream 一样能跑通，只是退化成串行，但 buffer 进入主池反倒不分裂、碎片更少。只有真正存在数据依赖的关键点才用 wait_event 显式同步，比如 backward 算到 dW 那一刻等待上一步 allreduce 的 event。\n副作用是显存池跟着分裂。caching allocator 按「一个池只服务一条 stream」的规则过滤空闲 block（隔离用于保证正确性：A stream 的 kernel 可能还没读完这块 block，B stream 拿去写会发生数据竞争）。主计算 stream 的池占满之后，通信 stream 名下整段 segment 即便全部空闲也无法被主计算 stream 复用，OOM 由此产生。\n反过来，通信 stream 上要是没多少计算 / 通信可重叠，或显存已经紧张到池分裂的额外开销划不来，就不要分出去——让通信留在主 stream，用一点串行换回不分裂的显存池更划算。\n03 四类合计：DeepSeek V3 建模 四类合计对 V4 同样成立。下表 per-GPU 数字取自 NVIDIA Megatron Core MoE 报告对 V3 配置的建模（报告设 256 GPU、PP4×VPP4×EP64，对标 H100 80 GB 容量）：\n类别 per-GPU 占用 生命周期 恒定占用（weights + gradients + optimizer states） 68.5 GB 全程恒定 Activation（forward saved，未压缩） 131 GB 单峰 计算峰值（MoE 专家 FFN 中间值等单 kernel 瞬态） 单层 GiB 级 μs 尖峰 通信 buffer（NCCL EP all-to-all + DP allreduce 预留） 数百 MiB ~ 1 GB 量级 全程恒定 表中通信 buffer 只计 EP all-to-all + DP allreduce 预留；再加上 PP send/recv（点对点，非集合通信）预留，整体 ≈ 3 GB。\n第一行给出恒定基线 68.5 GB，只剩 11.5 GB 供动态显存使用。第二行 Activation 131 GB 是这点余量的十倍有余——recompute、FP8 storage、offload 都是要把它压缩到 11.5 GB 以内；压缩不下去，就只能继续划分恒定基线，腾出更多余量。\n未优化前合计 199.5 GB ≈ 2.5× H100。目标是把 per-GPU 总占用压回 80 GB；131 GB 的 activation 是首要压缩目标。\n03.1 真实峰值不是各类最大值的简单相加 四类峰并不在同一时刻。判断某套配置会不会超出容量，要逐时刻看占用：\n恒定占用与通信 buffer 是两条水平线 activation 是一个单峰 计算峰值是一个尖峰 某一刻的占用 = 那一刻各类的瞬时值相加；真峰值 = 这个逐时刻之和在整个 step 里取到的最大值。峰值时刻 \\(t^*\\) 会随配置变化：\n不开 recompute 时，activation 单峰停在 forward 末端，\\(t^*\\) 位于那里。 开 full recompute 后，forward 末端那段保存的激活被削平，但 backward 早期出现一个新窗口：层间保存的激活还没卸掉几层、当前层 recompute 重算出的 forward intermediate、当前层新分配的 grad buffer 三类在同一窗口叠加，\\(t^*\\) 从 forward 末端移到 backward 早期。 这只是把它推迟到反向、并非删除。峰值是否下降取决于移入窗口的高度：低于被削平那段就降低（V3 full recompute 即把峰值从 199.5 GB 压到 ~85 GB），要是比削平那段还高，反而会更早 OOM。\n04 MemTrace 与 OOM 诊断 04.1 怎么打 snapshot torch.cuda.memory._record_memory_history(max_entries=100000) # 跑几个 step torch.cuda.memory._dump_snapshot(path) 得到的 pickle 文件里，每一次 alloc / free 都带着时间戳、字节数、所在 stream 和 Python stack trace。\n把 allocated 和 reserved 按时间画出来，是 MemTrace 时间线；取 allocated 最高那一刻各类的瞬时值、按 tensor 归类排列，是峰值占用分解。\n04.2 案例：peak ≈ 93.5 GB（部分 recompute 后） 下面这张 MemTrace 时间线，浅色区域是 allocated，深色线是 reserved；峰值落在 backward 早期 recompute 窗口，OPT 段另有约 10 GB 的临时 m / v 副本尖峰。\nMemTrace 下面这张是同一时刻峰值截面按 tensor 来源分解，合计约 93.5 GB，高出 H100 80 GB 上限约 13.5 GB。\n峰值占用分解 同一时刻的占用分解里，六项是：\n分量 大小 说明 恒定占用 68.5 GB Megatron 报告 Table 3 拆为 BF16 weights+gradients 36.4 GB + FP32 main weights+optimizer states 32.1 GB 通信 buffer 3 GB PP send/recv（P2P）+ 各集合通信预留池 Activation 保存的激活 12 GB 部分 recompute 后还没被反向消费完 recompute forward intermediate 2 GB 当前层重算出的中间量 grad buffer 3 GB 当前层 backward 临时分配的 dY/dX/dW allocator overhead 5 GB reserved − allocated 之间的碎片余量 这六类与开头四类并非一一对应：Activation 在峰值处拆成层间保存的激活和 recompute intermediate（计算峰值瞬态归入后者的重算窗口），另起 grad buffer 与 allocator overhead；恒定占用与通信 buffer 在占用分解里保持不变。\n04.3 OOM 的两种典型情况 情况 A：reserved 与 allocated 都接近上限。 空间已经用尽，瓶颈在峰值时刻各类的同时占用量。看峰值时刻哪一类（恒定基线、层间激活缓存、瞬态、还是 allocator 空隙）的量级最大，针对最大的那类优化。开 recompute 才有意义，它压缩的层间激活缓存正是触及上限的主因。\n情况 B：reserved 已达上限，allocated 却低出好几 GB。 可用字节并没有真的被填满，实际是 reserved 被虚高抬起。抬高 reserved 的三种成因都不靠多用显存：\n瞬态峰值被抬进 reserved 后不再回落形成的那道台阶 shape 变化造成的碎片 多 stream 分池后一条 stream 的空闲填不进另一条的请求 再开 recompute 去压缩 allocated 没有收益——压缩的那一维根本不是瓶颈。\nOOM 判断要同时看峰值占用分解（时间维：某一刻同时被持有多少）和 allocator 碎片状态（空间维：池能不能凑出这一刻要的连续大块）。低于上限的峰值遇到碎片化的池照样 OOM；等于上限的峰值若 shape 固定、池无碎片，反而能分配成功。\n04.4 失效条件 一次 snapshot 只是某个 step、某张 rank 的快照。MoE 路由逐 step 波动，负载最重的那张 rank 也不固定在同一张卡上——这一回没碰到峰值，不能推导出下一个 step、另一张卡也安全。要做出判断，得在几个高负载 step、几张可疑 rank 上各采样分析一次，看差值是稳定接近上限还是偶发接近上限。\n04.5 各分量能否静态估计 各分量字节量可以静态估到 ± 数 GB；真正无法静态预测的只有两样：\n峰值落点 \\(t^*\\)（取决于运行时 stream 交错、各 kernel 时长、MoE 路由逐 step 波动） allocator 碎片余量 都只能事后从 snapshot 里分析出来。\n05 优化手段：四类各自的取舍 05.1 恒定占用：分片、降精度、offload V3 配置下，恒定占用这三块不分片的话，单 rank 要装：\n\\[ m_{const} = \\underbrace{2N}_{\\text{BF16 weights}} + \\underbrace{2N}_{\\text{BF16 gradients}} + \\underbrace{(4+4)N}_{\\text{Adam FP32 m/v}} \\approx 218 \\text{ GB} \\]\n若加 FP32 master weight（4 bytes/param），完整是 16 bytes/param ≈ 291 GB。前面表里的 68.5 GB 是 Megatron 报告口径下按 V3 并行度切分后的 per-GPU 值。\n分片 工具 划分对象 关键代价 FSDP / ZeRO-3 三类全分片（沿 DP 维） 每层 all-gather + reduce-scatter；sharding 组越大通信越靠近关键路径 ZeRO-1 / ZeRO-2 仅分片 opt state / +grad 通信量低于 ZeRO-3，省得少 Megatron TP 按 head / hidden 维切 attention 和 MLP 权重 同时划分该层 activation；每层 4 次 all-reduce，节点内 NVLink 限 8-way PP 按层切分（每 rank 持 \\(L/p\\) 层） 跨 stage send/recv 通信；bubble 代价；不动 activation 峰值 EP 按 expert 切分（每 rank 持 \\(1/e\\) 份 expert） 主要削减 MoE 参数大头；dense 部分不动 四种切法各对应一个维度，互不争用同一维度，可以相乘：\n\\[ \\text{per-GPU 恒定占用} = \\frac{\\text{全量}}{\\prod \\text{各相关并行维}} \\]\n降精度 FP8 weight：把 forward / backward 实际计算、并参与 DP all-gather 的那份工作权重从 BF16 降到 FP8，单 weights 一项再省 2×；FP32 master weight 仍保持高精度。改变的是 forward / backward 的计算精度与 all-gather 的通信精度，optimizer 更新仍在 FP32 计算。 FP8 optimizer state：把 Adam 的 m / v 也降到 FP8。 失效条件：master weight、m / v 这类「极小增量反复累加进一个大基数」的量，仅靠 scaling 还不够。每步要加的更新值比基数小几个数量级，量化后落在基数尾数能分辨的台阶之下，加法被整个舍去，即 swamping（大基数把小增量淹没）。表现为参数实际不更新、loss 曲线无报错却不再下降。所以这类累加量必须保留一份高精度累加器。\n外移 Optimizer state offload → CPU：通过 PCIe Gen5（≈ 64 GB/s 单向）移到 CPU 内存 Weights → NVMe：再往后退一档到 NVMe（PCIe + filesystem，≈ 7 GB/s 单向） 从 NVMe 把 weights 拉回 GPU 计算一层比 HBM 直接读取慢两个数量级，MFU 会大幅下降，所以只在「单卡资源严重受限、必须容纳大模型」的场景才用。\n降精度只在划分完之后单项仍占几 GB 以上时才划算。FSDP 划分到 256 rank 后 per-GPU optimizer state 已经只有 0.57 GB，再降到 FP8 也只节省约 0.4 GB，不值得为这点字节数增加复杂度。\n05.2 Activation 缓存的压缩手段 Activation 缓存是峰值的大头，V3 配置下未压缩前每卡就有 131 GB（约 1.6× 一张 H100）。\nRecompute（时间换空间） 策略 做法 代价 Full layer recompute 每层只 save 一个 layer-input 2BSD，反向把整层 forward + backward 一并跑 forward:backward ≈ 1:2 原本是 1+2=3 份算力，full recompute 变成 1+(1+2)=4 份，wall-time ≈ +33% Selective recompute 只对 attention / MLP / LayerNorm 某几种 op 重算 节省的缓存少、wall-time 也少；Megatron 最初做的是只重算 attention 的 5BHS² 项，但 FA 之后 5BHS² 已不在 HBM，意义变小 模块级混合 按 nn.Module 边界标记 checkpoint，不同 module 独立决定 V3/V4 这种 MoE 主导的架构里常比 full layer 更省时间 Offload（搬到 CPU / NVMe） Activation → CPU offload：走 PCIe Gen5 ≈ 64 GB/s 单向 Activation → NVMe offload：再慢 10× 以上，预训练中几乎不用 关键：必须把 PCIe DMA 和 forward / backward 计算 overlap：移下去要赶在下一层 forward 还没需要缓存之前完成，移回来要在反向使用缓存之前预取完。两个方向都得对准时序，任一边没与计算重叠 MFU 就下降——recompute 只在反向单点插入一段重算，offload 要双向与计算重叠，调度余量小得多。\n序列并行（长上下文） 按 sequence 维切到多 rank 上。序列越长、S 相关的那几项越主导，把 S 划分到多 rank 是唯一随长度同步缩小数据量的方式。\n工具 划分对象 代价 Megatron SP 补充 TP，划分 TP 没覆盖的 10sbh（LayerNorm、dropout、block 输入激活） 仅动 TP 没划到的部分 CP / Ring Attention 每卡 S/N，attention kernel 内数据量随 S/N 缩小 跨 rank 通信 DeepSpeed-Ulysses 两次 all-to-all 在 sequence ↔︎ head 间交换维度 P 受 attention head 整除限制；GQA/MQA 下常受 KV head 数限制 USP（Ulysses + Ring-Attention） 同时叠加 long-context 训练目前的常见组合 FP8 storage 把前向保存下来的中间值每元素的字节数从 16 bit 降到 8 bit。activation 是一次性写、一次性读的量，配 scaling 和反量化即可。\nFused kernel（中间量留 SRAM） SRAM 和 HBM 的容量、带宽都差一个数量级，中间量每在 HBM 上往返一趟就消耗一份带宽。fuse 把数据量沿某个维度划分成小到能整个放进 SRAM 的 tile，逐 tile 算完只把最终结果写回 HBM，中间 tensor 始终留在片上、既不占 HBM 也不付往返带宽。融合的边界由 SRAM 预算限定。\n判据不在中间量是否瞬时，而看 consumer 要的每个输出 tile，是不是只依赖产出它的那一个 CTA（一个 thread block）：\n依赖都在本 CTA 就留片上：逐元素 pointwise，或归约轴整条留在 CTA 内 streaming 要跨 CTA 读取就必须物化——在 producer 已切散到多 CTA 的那根轴上做 reduction / transpose / shuffle 归约本身不必然导致物化：只要归约轴留在 CTA 内 streaming（FA 的 KV、norm 的 \\(D\\)，running state 小到进寄存器），归约也能片上；物化只在归约轴被 tile 到多个 CTA 时才出现。\n代表实例：\nFlashAttention 系：把 QK^T、softmax 输出 P、dS、dP 全部留在 SRAM tile 内。HBM 上只剩 O ∈ [B,S,D] 和 LSE ∈ [B,H,S]。参考参数 (\\(B=4, H=32, S=8192, D=4096\\), BF16) 下，原本几块约 40 GiB，FA 后只剩 0.254 GiB fused LayerNorm / RMSNorm + residual：归约轴 \\(D\\) 整条留在 CTA 内 fused MoE token permute：路由后的 token 重排融进 grouped-GEMM 的数据准备路径（Megatron 开关 moe_permute_fusion） fused cross-entropy（长 vocab）：\\(V \\ge 128k\\) 时把 logits 和 NLL loss 合并到一个 kernel 内，按 row 流式计算 05.3 计算峰值瞬态的压缩 瞬态是 kernel 执行中临时占用的 HBM，规模 = f(B, 每卡 token 数, dtype 位宽)。recompute 和 offload 对瞬态均无效：瞬态是 kernel 内 μs 级的瞬时量，recompute 针对的是跨 kernel 存下来给反向用的 saved activation，削减它反而新开一个重算窗口、又产生一份瞬态；offload 走 PCIe，搬运延迟比 μs 慢几个数量级，根本来不及在 kernel 算完前把数据量移出。\n走 fused kernel / tiling 这条路：不去缩小数据量的尺寸，而是让中间量根本不写进 HBM。\nfused MoE expert FFN 是个典型反例：把 expert 内部的 up_proj + gate_proj + 激活（SwiGLU）+ down_proj 几步在 expert 内 fuse，能不能留片上取决于 kernel 怎么 tiling，而不取决于「融没融」。标准实现（Megatron-Core grouped GEMM、V3 同理）下这份 [C_local, 2n] 实际会落 HBM：FC1 把输出按 N 维切散到多个 CTA，FC2 又要沿同一维（FC1 的输出维 F）跨 CTA 归约。这正是「跨 CTA 读取就得物化」那种情形，只能过 HBM/L2。即便把 FC1 + 激活 + FC2 强融进一个 megakernel 也没用——一个 kernel 不等于一个 CTA，各 stage 间仍要在 global memory 上会合。\n要真留片上，得换成每个 CTA 独占 token 行块、自己做完整 \\(F\\) 归约的 FA 式 tiling，代价是降低 GEMM 并行度 / 吞吐。V3 训练路径同理走 grouped GEMM、这份激活照样落 HBM：按 V3 配置 (\\(B=1, S=4096\\)) 静态估算，\\([C_local, 2n]\\) 单层约 0.3 GiB / 卡，不是单层峰值主导项；主峰是随路由数 K 放大的进出口张量（\\([C_local, D]\\) 量级、随 K 累加）。\ncuBLAS workspace shrink：CUBLAS_WORKSPACE_CONFIG=:4096:8 固定 workspace 上限（多 stream 共享 handle 时换取确定性），可省下几十个 handle 累积的几 GiB；代价是某些 SplitK / 多阶段 GEMM 算法用不了，吞吐可能略降。\n05.4 通信 buffer：减少 staging 份数与尺寸 NCCL 在每个 GPU 上为每条通信 stream 注册一段 staging buffer，尺寸按最坏情况一次预分配。V3 配置下每卡每方向 EP all-to-all staging 约百 MiB 级（某配置下估算约 230 MiB）。\n调度（bucket 聚合） 把同一层多个参数的 gradient 合并成一次 collective。bucket 小则小消息多、staging 总量大、带宽利用率低；bucket 大则单次 staging 大、还要等更多 grad 准备好才发，反向关键路径等待时间长。\n工具 默认 bucket 说明 DDP bucket_cap_mb 25 MiB 经典 PyTorch 默认 FSDP 无 MiB 级阈值 合并粒度由 fully_shard 包裹的 module 单元决定 DeepSpeed ZeRO reduce_bucket_size 5e8 元素 ≈ 1 GB DeepSpeed 风格 配置（NCCL 环境变量） NCCL_BUFFSIZE × channel 数 = 每个 rank 预留的总 staging（Simple 默认 4 MiB/channel） NCCL_NTHREADS：每 channel 线程数，决定 staging 复用速度 NCCL_ALGO / NCCL_PROTO：选 ring / tree / collnet / nvls 算法与协议（LL 有效带宽 50% / LL128 95% / Simple 最高延迟也最高） 算法 / 拓扑 算法 特点 硬件前提 Ring 每跳一份 staging，N 卡 ring 一共 N 份；带宽最优，latency 随 N 线性涨 通用 Tree 二叉树聚合，latency log(N) 通用 Collnet 借交换机 / fabric 做 in-network aggregation SHARP 硬件 NVLS 走 NVLink SHARP，把 reduction offload 到 NVSwitch 域；几乎不在 GPU 显存里留 staging NVSwitch + NVLink SHARP 复用（对称内存直接写远端） NVSHMEM：rank A 可直接 nvshmem_put 把数据写到 rank B 显存上，省去本地 + 远端 staging DeepEP：早期 / legacy 版本用 NVSHMEM 省去 EP all-to-all 两侧的中转拷贝（某配置下每卡每方向 ≈ 230 MiB） 05.5 对照表 「优先级」一列表示当前配置下的收益/代价排序：先动富余资源，后动紧缺资源。它会随配置变：通信已是反向关键路径瓶颈时再做恒定占用的拆分反而不划算。\n显存类型 典型手段顺序 取舍逻辑 恒定占用 FSDP / ZeRO \u0026gt; FP8 master + opt state \u0026gt; offload 先按 rank 摊薄常驻状态；分片收益耗尽后，再考虑降精度或 offload Activation 激活缓存 recompute \u0026gt; FP8 storage / offload（视代价）\u0026gt; 序列并行类（长上下文）\u0026gt; fused kernel 通常先用 recompute；FP8 storage / offload 取决于数值风险和带宽；CP / Ulysses 主要改善负载均衡 计算峰值 瞬态 fused kernel（FA / MoE FFN）\u0026gt; kernel 内 tiling 中间量留 SRAM，省 HBM 往返 通信 buffer bucket 调度 \u0026gt; algorithm 选择 \u0026gt; NVSHMEM bucket 调度通常先改；算法/拓扑依赖硬件；NVSHMEM 用有界对称内存替换随跳数增长的 staging 四类显存与工具的映射 05.6 各手段的失效条件 FSDP / ZeRO：sharding 组扩大到 all-gather 慢于反向计算时就不再划算——通信从「藏在计算后面」变成关键路径瓶颈 FP8 storage：失效在反复累加那一类量（swamping），只有一次性读写的激活才换得安全 Offload：搬运时间藏不进计算就反过来拖慢，PCIe 来回的延迟进入关键路径 fused kernel：标准 grouped-GEMM 实现下反向会失效——反向要把它整张 fan-out 给多个梯度 GEMM，forward 那种「逐 tile 流过、从不整张落地」的办法用不上 NVSHMEM：用单边写替换掉随跳数增长的 staging，但同步和完成语义要自己管 06 CP/SP/PP/TP 各分片哪一段显存 06.1 想降低恒定 68.5 GB：用 FSDP / ZeRO 每加一份 DP 维就 \\(/N\\)。分到极致的代价是每步都要 all-gather + reduce-scatter，再往下分省下的显存会被新增的通信开销抵消——这时要退回到 HSDP（hybrid sharding，组内分片、组间用 DDP）或限制 sharding 组大小。\nPP（按层划分）、EP（按 expert 划分）同样把恒定占用降到这条基线，但它们对激活缓存帮助有限。\n06.2 层间激活缓存靠什么分片 设每 step 的 Global Token Size = \\(G\\)，切 sequence 后每个 rank 持有 \\(G/N\\) 个 token，attention 数据量总和没变。\n固定 Global Batch Size 时，划分序列会让 Local Batch Size 上涨，省下的又被原样涨回：总卡数固定，新拿去划分 sequence 的卡只能从 DP 维分出，DP 度一降，\\(global\\_batch = DP \\times local\\_batch\\) 里每个 DP rank 摊到的 local batch 就成倍上涨。这种抵消对所有 activation 项都成立：\n\\[ \\text{CP 沿 S 切每项} \\times \\frac{1}{N}, \\quad \\text{batch 涨回} \\times N \\;\\Rightarrow\\; \\text{单卡峰值净变化趋近零} \\]\n抵消假设涨上来的 local batch 由并发 micro-batch 承担——峰值激活只由同时在飞的 micro-batch 数决定。若改用梯度累积让多出来的 batch 串行跑，峰值激活并不回弹，CP 切出的 \\(S/N\\) 不被抵消、仍净省。\n例外条件：当训练目标是训更长 context、要容纳单卡放不下的 sequence 时，CP 加卡用来容纳更长 \\(S\\)、不从 DP 分卡，local batch 不回弹——此时相对「不切 sequence 直接训练同样长度」，划分 sequence 才真正压低单卡 activation。\n06.3 反向当前层的临时量与 kernel 数据量：TP 或 fused kernel 峰值窗口里的动态量分两种：\nChain rule 决定的反向临时量：重算出的 forward intermediate、与它同形状的中间量梯度（MLP/MoE 这档宽 \\(2n\\)），以及 dY、dX、dQ、dK、dV 这类宽 D 的边界梯度——这类不能删，只能用 TP 减少每卡持有的 head / hidden 切片（attention / MLP 数据量同样划分 \\(/t\\)），代价是每层 4 次 all-reduce、对 NVLink 带宽敏感 真正 μs 级的 kernel workspace：靠 fused kernel 或 workspace 配置缩小 06.4 PP 对激活缓存帮助有限 PP 大小 \\(p\\) 在激活缓存公式里被直接约掉：1F1B 第一阶段同时在飞 \\(m_{inflight} = p\\) 个 micro-batch（\\(m \\ge p\\) 时）。原因：1F1B 启动时先逐级跑完 \\(p\\) 个 micro-batch 的前向、把 \\(p\\) 级流水线填满，才轮到第一个 micro-batch 的反向，同时驻留 \\(p\\) 份激活缓存，正好与 PP 度 \\(p\\) 约掉。\nSchedule multiplier 说明 1F1B \\(1\\times\\) 经典做法 VPP（virtual pipeline, \\(m_v\\) chunks） \\(1 + \\frac{p-1}{p \\cdot m_v}\\) \\(p=16, m_v=4\\) 时 ≈ 1.23× DualPipe \\(1 + \\frac{1}{PP}\\) DeepSeek V3 双向流水线；另需双份模型参数副本（独立代价，V3 靠大 EP 摊薄） Korthikanti et al. 2022 (arXiv:2205.05198 §4.2.3) 给出的最优 schedule，bubble 最小化的代价是内存压力集中在 pipeline 第一个 stage。\nSchedule multiplier 讨论只在不开 recompute 时才显著：那时激活缓存是整层中间结果，V3 未压缩前每卡 131 GB，\\(1\\times\\) 与 \\(1.25\\times\\) 能差出三十多 GB。开启 Full Recompute 后，每层只 save 一个 layer-input \\(2BSD\\)，激活缓存缩到 3.5 GiB（V3 量级），multiplier 差异不到 1 GiB。\n06.5 分片维度对照 工具 划分对象 量级效果 Activation Checkpointing 层间激活缓存 缩减到只剩 layer-input \\(2BSD\\) Activation Offload 层间激活缓存（移到 CPU） 受 PCIe 带宽限制，覆盖不了 μs 级 kernel workspace FSDP / ZeRO 恒定占用 按 DP 度数 N 划分，每卡 /N PP（pipeline） 恒定占用（按层 \\(L/p\\) 摊） 每 stage 参数 /p；不划分激活缓存（一卡放 \\(L/p\\) 层、激活缓存 \\(\\propto L/p \\times m_{inflight}\\)，\\(p\\) 在两头约掉） EP（expert parallel） 恒定占用里的 expert 权重 每卡 expert /e；划分的是 MoE 参数大头，dense 部分不动 TP（Megatron） 恒定 + 激活缓存 + kernel 数据量（按 head / hidden 维划分） attention 数据量 /t、MLP 中间 /t；受 NVLink 带宽限制，最多 8-way Megatron SP TP 未覆盖的 sequence 维 \\(10sbh\\)（每 rank 各存一份）/t；不动 attention CP / Ring Attention 全局 sequence 维 每卡 \\(S/N\\)；Global Token 固定时作用是负载均衡 DeepSpeed-Ulysses sequence + head 两次 all-to-all 同 CP，但 P 受 attention head 整除限制（GQA/MQA 下常受限于 KV head 数，可经 KV replication 放宽） 小 micro-batch \\(B\\) \\(B\\) 瞬态量随 B 降低；代价是单 kernel 数据量变小、算力利用率下降，且 PP 下 m 会变大（激活缓存总量不变） FlashAttention attention 的 \\(S^2\\) 项 移到 SRAM，HBM 只剩 \\(2BSD\\)（O）+ \\(4BHS\\)（LSE，FP32） 07 FA 之后 HBM 上的残留 今天主流训练框架默认用 FlashAttention，标准 attention 那个 \\([B, H, S, S]\\) 不再 materialize 到 HBM。\n峰值主要来自 FA 之后还留在 HBM 上的：MoE 层的 dispatch / permute / expert FFN 中间值，以及 Full Recompute 重算窗口里的中间值。\nFA 那段的参考参数 (\\(B=4, H=32, S=8192, D=4096\\), BF16) 是单 EP rank、单层、单 micro-batch 的尺度——真实训练里还要再乘上 PP、EP 和 micro-batch 数。\n未启用 fused expert kernel 的配置下，MoE FFN intermediate \\([C_local, 2n]\\) 会成为主导项之一。启用 fused kernel（把 SwiGLU 融进 FC1 epilogue）后，\\(2n\\) 宽的 pre-activation \\(H\\) 在 forward 不再单独落 HBM——逐 tile 在 epilogue 里算完即被消费。但这条收益对反向容易失效：反向算 expert FFN 梯度时，forward intermediate \\([C_local, 2n]\\) 必须在场——无论是 save-for-backward 存下来的，还是 full recompute 现算出来的——它本来就得写出去。此刻还要同时生成它同形状的梯度 \\(dH\\)（同样 \\([C_local, 2n]\\)）。forward 不必把它整张落地（随算随被下一段消费），backward 却要整张物化在 HBM 上 fan-out 给多个梯度 GEMM——失效的根因是这道刚性物化边界，不是「tile 超过 SRAM 容量」的问题。\n08 Full Recompute 之后峰值仍达 85 GB：差值来自哪几项 上面 MemTrace 占用分解那 93.5 GB，是部分 recompute（层间激活缓存还残留 12 GB）时截下来的快照；这里改为分析 full recompute 把层间激活缓存降到下界后，峰值还剩多少、能不能回到 80 GB 以内。三个配置档的峰值依次递减：\n未优化 199.5 GB 部分 recompute 削到 93.5 GB full recompute 继续降低 按 per-GPU 视角逐项算（EP 路由波动下最重 rank 只会更吃紧），V3 配置（\\(PP=16, L=61, S=4096, B=1, D=7168\\), BF16）下六类分量：\n分量 大小 说明 恒定占用 68.5 GB 已切分后的 per-GPU 数 通信 buffer 3 GB EP all-to-all + DP allreduce + PP send/recv 层间激活缓存 3.5 GiB full recompute 后只留 layer-input \\(2BSD\\)；1F1B 下总激活缓存与 \\(p\\) 无关 当前层重算 forward intermediate ≈ 2 GB recompute 换来的是时间、不是让这部分消失——重算的那一瞬间该层所有中间产物都保留在 HBM 上；MoE 是大头（进出口张量 \\([C_local, D]\\) 随 K 累加） 当前层 grad buffer ≈ 3 GB dY + dX + 当前层 dW（用完即并入梯度存储） allocator overhead ≈ 5 GB reserved − allocated，V3 预训练 shape 稳定，碎片率低 合计：\n\\[ 68.5 + 3 + 3.76 + 2 + 3 + 5 \\approx 85 \\text{ GB} \\]\n这一组分量可代入估算：\n\\[ \\text{恒定基线} \\approx \\frac{218 \\text{ GB}}{\\prod \\text{FSDP} \\times \\text{TP} \\times \\text{PP} \\times \\text{EP}} \\]\n\\[ \\text{层间激活缓存} \\approx 2BSD \\cdot \\frac{L}{p} \\cdot \\text{multiplier} \\cdot \\text{recompute\\_factor} \\]\n其中 multiplier 是 PP 调度系数（1F1B ≈ 1×、VPP ≈ 1.23×），recompute_factor 是该粒度下每层的保留比例（full recompute 取 1，不开则是整层中间结果的倍数）。\n若峰值超出容量 5 GB，同一时刻占用最大的是恒定那 68.5 GB，该动的就是它：进一步分片（FSDP / TP / PP / EP）或压精度（FP8 master weight / optimizer state）。层间激活缓存只剩 3.5 GiB，再加大重算收益已经很小。\n09 Hopper → Blackwell：PP 依赖随硬件扩容下降 之前讨论的前提是单卡 HBM 容量与 NVLink 域大小固定不变；这两项参数一旦变了，最先受影响的就是 PP。\nPP 主要解决两件事：\n单卡 HBM 容纳不下整个模型，只能按层把恒定占用分摊到多卡 模型必须跨节点划分时，PP 的跨 stage 通信量比 TP/EP 小得多，能用较低带宽的连接承载——代价是流水线 bubble 单卡 HBM 从 H100 80 GB → H200 141 GB → B200（HGX B200 常见可用 180 GB HBM3e，物理口径 192 GB），一张卡能容纳的层数随之上升，容量约束首先放宽。更关键的是 NVLink 域的扩张：Hopper 一域 8 卡，到 NVL72 一域 72 卡。原来必须跨节点、只能交给 PP 走低带宽连接的那部分 TP/EP，现在能整块放进同一个高带宽 NVLink 域里，低带宽连接的约束也随之消失。这两条约束都放宽后，PP 那份 bubble 代价就不再划算，对 PP 的依赖会逐步下降。\n10 一些个人化整理 把全文拆成三句话总结：\nOOM 看的是「峰值时刻各类同时占用」，不是某一类的最大值；reserved 容易被瞬态峰值抬成台阶只升难降。 降显存最划算的顺序：先用 FSDP / ZeRO 把恒定占用摊薄；再用 recompute / fused kernel 砍 activation 峰值；最后才考虑 FP8 / offload 这些有数值或带宽代价的手段。 2026 年的硬件趋势让 PP 的必要性下降：HBM 增大 + NVLink 域扩张，把跨节点划分的硬约束松开了；未来大概率是 TP / EP / CP 在 NVLink 域内整块摊开、PP 留给极少数真正跨域的场景。 跟 [[UltraScale Playbook 笔记]] 互为补充：那本讲「怎么选并行度」，这篇讲「选完之后峰值落在哪、会不会 OOM」。两份笔记里 FSDP / ZeRO / TP 的描述口径一致，差异主要在本文把 MoE + Recompute 的反向窗口单独拎出来画了峰值时刻分解。\n参考 原文：DeepSeekV4 Infra Deep Dive · 第四节 4.4: PyTorch 训练显存管理 同系列： 第一节: infra 的设计原则 第二节: Hybrid Attention - 问题演进视角 第三节：在缓存命中率就是一切的世界里，PagedAttention 失效后会怎样？ 4.1: Deepseek V4 为什么不能再按 module 做 checkpoint？ 4.2: Activation Checkpointing 的四档粒度 4.3: Tensor-level AC 的三个工程问题：joint graph、最小子图、storage reuse 配套阅读： [[UltraScale Playbook 笔记]] Korthikanti et al. 2022 (arXiv:2205.05198) — Reducing Activation Recomputation in Large Transformer Models Liu et al. 2023 (arXiv:2310.01889) — Ring Attention with Blockwise Transformers Jacobs et al. 2023 (arXiv:2309.14509) — DeepSpeed-Ulysses Fang et al. 2024 (arXiv:2405.07719) — USP: A Unified Sequence Parallelism Approach ","date":"2026-07-07T00:00:00Z","permalink":"/deepseek-v4-infra-4.4-%E8%AE%AD%E7%BB%83%E6%98%BE%E5%AD%98%E7%AE%A1%E7%90%86/","title":"DeepSeek V4 Infra Deep Dive 4.4: PyTorch 训练显存管理"},{"content":" 来源: 整理自 @sheriyuo 的长文 RL Interview Questions 2026 (原文链接) 与 @vivek_2332 的逐题答案。\nsheriyuo 在知乎也发过中文版: https://zhuanlan.zhihu.com/p/2046740446353811230\nsheriyuo 在原文中提到几点: 没严格区分 LLM RL 与 Agentic RL; 几乎每道题都能继续追问, 没有给出参考答案; 现代 RL 招聘越来越要求全栈理解, 算法岗也会问 infra, 反之亦然; 数据相关问题没收录 (太看经验); 死记硬背不够, 深度理解更重要。\nVivek 在一些题上明确说 “not sure” 或 “haven’t read the paper”, 我在对应位置标注了 ⚠️。\n算法 (Algorithm) 1. 为什么用 Actor-Critic 而不是纯 Critic? 纯 value-based 方法 (DQN 这类) 处理不了大的 / 连续的动作空间, 而且只能给出 greedy policy。纯 policy gradient 能处理它们, 但更新方差高。Actor-Critic 直接保留策略, 用 critic 来压低这个方差。两全其美。\n2. KL 散度、交叉熵、MLE 之间有什么关系? \\(CE(p,q) = KL(p,q) + H(p)\\), 最小化任意一项最终收敛到同一处。MLE 本质上就是数据分布和模型分布之间的 KL。\n3. 不同 RL 场景下奖励该怎么设计? 主要看你建的是什么环境。可验证的 (数学、代码) 比不可验证的 (写作) 容易奖励得多, 原因显而易见。最重要的是环境不能被 reward hack, 不能有一条通往错误解的捷径。数据和奖励都得仔细写。\n4. importance sampling、rejection sampling、其它 Monte Carlo 方法在 RL 里怎么用? IS 用于 async RL 复用更早 policy 产生的 off-policy 样本。RS 用于数据过滤, 可以过滤 SFT 数据, 也可以扔掉过易 / 过难的样本。广义上都是 Monte Carlo, 用样本估期望而不是精确计算。\n5. PPO 和 GRPO 里 advantage 怎么算? 为啥要减 baseline? std 归一化真的必需吗? PPO 有 critic + reward model。GRPO 干掉这两个, 它的 “critic” 来自多次 rollout 取均值当 baseline。baseline 降低方差, 这样不是每个 rollout 都得到正向推动, 构成组内零和。std 归一化不必要, 参考 Dr.GRPO。\n6. RL 训练和 test-time scaling 在探索方式上有什么不同? RL 是把领域 intelligence 注入权重。test-time scaling 是在已有 intelligence 前提下, 找最优解的路径, 用更多 thinking token 去探索不同方案。一个塑权重, 一个花推理预算。\n7. PPO clipping 怎么工作的? 为啥取 min? 不 clip 会怎样? CISPO 区别在哪? clip 是为了防止策略突变, 不同样本的 ratio 方差会变得很高。min 来自 TRPO 的 trust region 思想, 它让更新保持悲观, 只在有利时才动。CISPO 不一样, 它 clip importance weight 而不是整个目标, 这样所有 token 仍有梯度流过, 保留更多信号。\n8. GRPO 为什么加 KL 惩罚? KL 怎么算? 为什么 DAPO、GSPO 等方法把它去掉? KL 在是因为已经有一个不错的基座模型, 你不想离它太远。但又希望通过 RL 学新领域。它被去掉是因为同样的约束反而阻止你学到足够远, 这不是我们想要的。所以去掉 KL 在 RLVR 下反而更稳。\n9. ⚠️ LLM 训练时 loss 不小心被 AllReduce 多次会怎样? Vivek 自己说没看懂这题。\n10. DPO 的 reward function 是什么? 会出现 reward hacking 吗? 怎么缓解? DPO 数据里隐式带 reward (标记好坏样本), 不像 PPO 需要单独的 reward model。Reward hacking 还是会发生 (见 @willccbb), 因为你追逐的解有一条后门。目前没有单一解法, 环境必须严格压测。可观察 reward 突然飙升或 rollout 长度暴涨这类信号来抓。\n11. MoE 模型里 train-inference mismatch 的解决方法有哪些? trainer-inference mismatch 来源有几个: 不同 engine、不同 expert 选择、不同 kernel、量化、舍入 (参考 Thinking Machines 博客)。一种修法是 replaying expert selection, 让推理和训练选同样的 expert。广义上是要让 rollout policy 和训练 policy 数值上一致, 否则 importance weight 会错。\n12. RL 训练时 group size、learning rate、PPO epochs、generation length 怎么选? group size 就是更多 rollout、更多可学习的路径, 常用 8/16。 lr 可控, \\(1 \\times 10^{-6}\\) 量级。 PPO epochs 通常 1, 多会让数据更 off-policy 不稳定。 generation length 完全看任务, 不交代任务没法量化。 13. 对比 GRPO, Dr.GRPO / DAPO / GSPO / CISPO / SAPO / DPPO / MaxRL / SimKO 各自怎么改进了训练? 有什么局限? 基本都是 GRPO 的变体, 主要改稳定性。lab 规模下选哪个差别不大, 但有差异:\nDr.GRPO: 去 std 归一化。 DAPO: clip-higher、动态采样、token 级 loss、超长过滤。 GSPO: sequence-level importance sampling 而非 token-level, 对 MoE 好很多。 CISPO: clip weight 不 clip objective。 MaxRL: 解决多样性坍缩。 SAPO / DPPO: ⚠️ Vivek 没读过论文。 14. TRPO、DPPO、AReaL 怎么在 RL 目标上施加 trust-region 约束? TRPO 是带约束的优化, 留在 KL 边界内。PPO 用 clip 近似的也是这个。AReaL 在 async 设置下用 staleness / off-policyness 代替硬 KL。\n15. RL 能从根本上扩展 LLM 的能力边界吗? 目前论文证据倾向于 RL 主要锐化基座模型已有能力, 而不是形成新能力。不过这是一个有趣的方向, 只要探索够多、样本够多样, 我不完全确定它做不到。\n16. ⚠️ 基于 ProRL 等工作, 怎么思考 RL 训练的边界扩展? Vivek 没读这篇。\n17. OPD 比传统 RL 和 SFT 有什么改进? 应用场景? OPD 把 RL 的 on-policy 探索跟 SFT 的稠密信号结合起来。RL 的弱点之一是稀疏奖励, OPD 让 teacher 在学生自己 (on-policy) 的 rollout 上打分 (关键是 on-policy), 就修了这个。用来低开销迁移能力, 最近是这个方向的热趋势。\n18. LLM 推理能力在哪个训练阶段出现? 不能完全确定, 但 CoT 起到很大作用。还有预训练给基线 intelligence, 后训练用 reasoning 和 test-time compute 放大 intelligence。\n19. ⚠️ 从 DeepSeek R1 到 V3.2 到未来 V4, 引入了哪些 RL 相关改进? MoE 上 RL 有什么不同? Vivek 不记得 V4 的细节。\n基础设施 (Infrastructure) 1. 忽略 CPU offload, GRPO 训练时内存里有几份模型副本? 各种优化能省多少? 主要取决于算法和 KL。GRPO 带 KL 时需要训练 policy、计算 KL 的 reference policy、推理模型。优化内存可以从去掉 KL 开始, 干掉 reference model。还有一些优化比如 NVFP4 量化省显存, 分布式训练 (FSDP / EP / TP 把模型和 optimizer state 切到多卡) 也帮很大。可以把推理那份量化, 把 optimizer state 切片每 rank 只持有部分。\n2. 分布式推理: KV cache 传输优化和多 GPU 通信策略? KV cache 传输对 disaggregated prefill / decode 很重要。通过 NVLink 或逐层 overlap 传输 KV / 权重来优化。多 GPU 看是同节点还是跨节点。总的来说 TP 帮助很大, 重的 KV 移动尽量留在单节点内。\n3. INT8 vs FP8 的权衡? 训练和推理各偏好哪种精度? FP8 的指数位多, 更适合训练。INT8 在固定范围内精度更高, 更适合推理权重。所以训练用 FP8, 推理 serving 用 INT8。\n4. RL rollout 里的长尾问题是什么? 怎么处理? batch 内 rollout 长度长尾。可用 PipelineRL、continuous batching 或早期截断。常见于 async 场景。\n5. Continuous batching 在 RL 训练里引入什么问题? vLLM 和 SGLang 的区别? Continuous batching 把不同 next-token 预测阶段的 decoding 混合在一起。vLLM 用 PagedAttention, SGLang 用 RadixAttention (前缀共享)。具体读 paper。RL 这里要注意序列在不同时间结束时, 对齐完整轨迹和 logprobs。\n6. 怎么衡量 vLLM 和 SGLang 的利用率? 训练时怎么评估 KV cache 利用率? 通过吞吐量 (tok/s)、KV cache 利用率、GPU 利用率。KV cache 看 occupancy。也看是否内存受限, 以及 rollout 是否被驱逐。GPU 利用率低通常意味着在等东西 (rollout 同步或 CPU) 而不是计算受限。两个 engine 都暴露这些 scheduler / cache 指标。\n7. 大规模多节点 RL 训练里 backprop 怎么实现? 跟常规 backprop 差不多, 加一些 trick: FSDP、EP、TP。超长上下文加 CP (context parallel)。理想上没人用 PP, 太复杂, 大多数规模下没必要。\n8. 有哪些异步 RL 框架? 它们解决什么同步瓶颈? 异步 RL 框架: Prime-RL、verl、TRL。瓶颈归结为三件事:\nrollout 的 staleness 及管理方式 (IS + staleness bounds); trainer-inference mismatch; token-in token-out。 解决 mismatch 的办法: 让 rollout engine 和 trainer 数值上保持一致, replaying expert / router 决策, 在 trainer 侧算 logprobs 而不是信推理引擎。\n9. ⚠️ AReaL 或其它部分 rollout 框架里, 是否保留旧 policy 的 KV cache? Vivek 不确定 AReaL, 但有些方法保留, 有些没有。两种做法之间实际没太大性能差异。\n10. Expert Parallelism (EP) 如何影响 MoE 吞吐量? EP 对 MoE 模型影响很大, 把一批 experts 放到一个节点或一组 GPU 上, 每个持有一小部分。它通过并行 expert FFN 提升吞吐量, 但要为 token 路由到对应 expert 增加 all-to-all 通信。实际收益是 all-to-all 能跟计算 overlap, 以及 expert 负载均衡程度。\n11. 长上下文训练里 compute-communication overlap 怎么设计? Megatron 和 FSDP 在并行策略上有什么区别? 关键是通信和计算并行起来。FSDP 把 grads / params / optim 切片, 按需 gather, 简单很多。Megatron 用 DP + PP + TP (3D 并行), 通信更显式。Megatron 这块我不太熟, 用得少。\n12. 怎么开启确定性执行? batch invariance 是什么? 是什么引起的? 跟 atomic add 有关吗? atomic add 能解决问题吗? 需要 reduction 顺序一致、相同的 kernel、batch invariant 的 op。参考 Thinking Machines 博客。\n13. AReaL 和 Slime 对 RL rollout 瓶颈的理解有什么区别? AReaL 我能聊: 它完全 async, 带 staleness 控制, 把 rollout 生成和训练解耦, 让 trainer 不用等最慢的 rollout, 然后用 max off-policy steps 或 importance sampling 或过阈值的 sample 直接丢弃来控制 staleness。Slime 这块 ⚠️ Vivek 没仔细读, 需要再读一下。\n14. 完全异步 RL 训练里 staleness 怎么理解? 实际典型值? Staleness = 生成 rollout 的 policy 和当前训练 policy 之间的差距。我跑的实验通常是落后 1–4 步。Staleness 大了会让 IS 不稳定, ratio 会爆。\n15. ⚠️ slime 里的数据流怎么走? 怎么跟 Megatron 整合? loss 怎么算? Vivek 同样不确定 Slime。\n16. VeRL、TRL、Unsloth、AReaL、slime 之间选一个怎么选? 都不用, 用 Prime Intellect。(原推的吐槽/梗)\n参考链接 sheriyuo 长文 (英文): https://x.com/sheriyuo/status/2063295181131247674 sheriyuo 知乎版: https://zhuanlan.zhihu.com/p/2046740446353811230 vivek 答案: https://x.com/vivek_2332/status/2063566811749331353 Thinking Machines (defeating nondeterminism): https://thinkingmachines.ai/blog/defeating-nondeterminism-llm-inference/ Dr.GRPO 论文: https://arxiv.org/abs/2503.20783 ","date":"2026-06-30T00:00:00Z","permalink":"/rl%E9%9D%A2%E8%AF%95%E9%A2%982026/","title":"RL 面试题 2026"},{"content":" 来源：The Ultra-Scale Playbook: Training LLMs on GPU Clusters（Hugging Face nanotron 团队，2025）\nBibTeX: Tazi et al., “The Ultra-Scale Playbook: Training LLMs on GPU Clusters”, 2025.\n配套仓库：nanotron（生产级）、picotron（教学级）\n原书基于 4100+ 组分布式实验、16k+ 测试运行 的实测数据写成，是当前最系统的分布式 LLM 训练手册。本文按主线整理：先把”为什么会爆显存”的代价讲清楚，再讲 5 种并行 + ZeRO 怎么对付这些代价，最后给出选型决策三步法。所有公式、表格、决策规则均保留原文细节。\n01 高层概览：三大挑战与权衡 整本书的所有技术都围绕三个挑战展开：\n显存使用（硬约束）：单步放不下，训练直接挂。 计算效率：让硬件尽量在算，而不是在等数据 / 等别的 GPU。 通信开销：通信是 GPU 闲置的根源。要分别利用好 node 内（NVLink，快）和 node 间（IB/Ethernet，慢）的带宽，并尽量把通信与计算 overlap。 核心 insight：这三者可以互相交换——比如 activation recomputation 用算力换显存，TP 用显存换通信。找平衡 是分布式训练的关键。\n02 单卡训练基础 Batch Size 的三种单位 LLM 社区普遍按 token 报 batch size（而不是 sample 数）：\nbst = bs * seq（token 总数） bs = gbs = mbs × grad_acc（单卡或单节点） bs = gbs = mbs × grad_acc × dp（分布式） 近期 LLM 训练的最佳 batch size 区间是 4M – 60M tokens / step。Llama 1 用 ~4M tokens 训 1.4T tokens；DeepSeek-V3 用 ~60M tokens 训 14T tokens。DeepSeek-V3 还做了 batch 渐进：从 3072 序列 → 15360 序列（前 469B tokens），之后保持 15360。\n显存占用四大件 训练时显存里有四样东西：\n项目 说明 模型权重 推理、backward 都要用 梯度 backward 算出 优化器状态 Adam 至少要存 momentum + variance 激活值 forward 留的中间张量，backward 要用 额外开销：CUDA kernel 通常占 1-2 GB（torch.ones((1,1)).to(\"cuda\") 后用 nvidia-smi 可见），加上 buffer / fragmentation，可忽略但要心里有数。\n权重 / 梯度 / 优化器状态 Transformer 参数总量公式（不含 positional embedding）：\n\\[ N = h \\cdot v + L \\cdot (12 h^2 + 13 h) + 2h \\]\n\\(h\\) = hidden dim，\\(v\\) = vocab size，\\(L\\) = 层数 主导项是 \\(h^2\\)，所以模型做大时 \\(h^2\\) 先爆 FP32 全精度训练：\n\\[ m_{params} = 4N,\\quad m_{grad} = 4N,\\quad m_{opt} = (4+4)N \\]\nBF16 混合精度训练（默认）：\n\\[ m_{params} = 2N,\\quad m_{grad} = 2N,\\quad m_{params\\_fp32} = 4N,\\quad m_{opt} = (4+4)N \\]\n注意：m_{params_fp32} 是所谓的 master weights。有些库（如 nanotron）把梯度也用 FP32 累加，牺牲内存换数值稳定。\n直观感受：\n模型规模 FP32 或 BF16 w/o FP32 grad BF16 w/ FP32 grad 1B 16 GB 20 GB 7B 112 GB 140 GB 70B 1120 GB 1400 GB 405B 6480 GB 8100 GB 7B 模型就已经超过单张 H100（80 GB）了——这是后面所有分布式方案的动机。\nActivation 显存 每个 transformer layer 的 activation 显存（mixed precision）：\n\\[ m_{act} = L \\cdot seq \\cdot bs \\cdot h \\cdot \\left(34 + \\frac{5 \\cdot n_{heads} \\cdot seq}{h}\\right) \\]\n随 \\(bs\\) 线性 增长，随 \\(seq\\) 二次 增长 长序列 / 大 batch 下，activation 是最大头——比 weights / grads / optimizer state 加起来还多 实测观察（Llama 系，\\(bs=1\\)）：短序列（≤2K）activation 可忽略；到 4K+ 开始显著。\nActivation Recomputation（gradient checkpointing） 基本思路：forward 时丢一些 activation，backward 时再算回来——用算力换显存。\n策略 做法 代价 收益 Full 每个 transformer layer 边界处 checkpoint +30-40% compute 最大显存节省 Selective 丢 attention（重算便宜），checkpoint FFN GPT-3 175B：70% activation 显存减少，仅 2.7% compute 几乎免费 DeepSeek-V3 还把 attention 激活本身也压得更小（MLA，Multi-Head Latent Attention），进一步减少选择性 checkpoint 要存的量。\n两个易混概念： - HFU (Hardware FLOPS Utilization)：含 recomputation 的实际 FLOPs 算利用率 - MFU (Model FLOPS Utilization)：只算 forward + backward 必要的 FLOPs 算利用率——更反映模型本身的硬件利用\n跨硬件对比时更推荐 MFU（如果某卡显存大到能省去 recomputation，那它的 HFU 低但训练更快是合理的）。\n现代训练基本默认用 Flash Attention，它原生把 attention 矩阵的 S / P 用 online softmax 不落地，所以绝大多数用户已经在享用 selective recomputation。\nGradient Accumulation 把全局 batch 切成 micro-batch，依次做前向 / 反向，累积梯度 再做一步 optimizer：\n\\[ bs = gbs = mbs \\times grad\\_acc \\]\n关键 insight：求和 / 求平均是等价的（按 \\(grad\\_acc\\) 数除即可），所以 GBS 一定时训练动态完全一致。Gradient accumulation 不改变训练，只解决显存——是分块处理大 batch 的最朴素办法。\n03 Data Parallelism（DP） 把模型复制到 \\(N\\) 个 GPU 上，每个 GPU 跑不同 micro-batch（不同数据切片）。最朴素 DP 的通信是 backward 后做一次 all-reduce 同步梯度。\n三个 DP 优化 优化 1：把梯度同步与 backward overlap\n用 register_post_accumulate_grad_hook 在每个参数上注册 hook，该参数的梯度一算完就立刻启动 all-reduce——而不是等全部 backward 完再统一同步。bucket 内是连续触发。\n优化 2：梯度 bucketing\nGPU 操作对大 tensor更友好。把参数按 bucket（如默认 25 MB）分组，每个 bucket 内部触发一次 all-reduce，减少 kernel launch 次数。\n优化 3：与 Gradient Accumulation 的配合\n只在真正需要 reduce 的那次 backward 上同步，其余 micro-step 用 model.no_sync() 跳过通信：\n\\[ bs = gbs = mbs \\times grad\\_acc \\times dp \\]\nDP 的局限 每个 DP rank 仍然要存一整套 optimizer state、grad、param。复制造成显存冗余——这就是 ZeRO 出场的动机。\n04 ZeRO（Zero Redundancy Optimizer） 沿 DP 轴把 optimizer state、grad、param 分片到各 rank，消除冗余；分片代价是额外通信。\nStage 分片内容 通信量 ZeRO-1 optimizer state 2Ψ（等价 DP） ZeRO-2 + gradient 2Ψ ZeRO-3（FSDP） + parameter 3Ψ 通用内存公式（每 rank）：\n\\[ m_{rank} = \\frac{2\\Psi + 2\\Psi + k\\Psi}{N_d} \\]\n\\(\\Psi\\) 是参数量，\\(k\\) 是 optimizer state 的内存倍数（Adam 是 12），\\(N_d\\) 是 DP 度。\nZeRO-3 在 PyTorch 里也叫 FSDP（Fully Sharded Data Parallel）。下文混用。\nZeRO-1 forward / backward 跟朴素 DP 一样，每 rank 都有完整 bf16 参数 + 梯度 backward 后：reduce-scatter（注意是 reduce-scatter，比 all-reduce 快 2 倍）梯度 → 每 rank 只保留 \\(1/N_d\\) 梯度 optimizer step：每 rank 只更新自己那 \\(1/N_d\\) 个 FP32 master weight optimizer 之后：all-gather bf16 参数到所有 rank reduce-scatter 是 ZeRO 引入的第三个集合通信原语（前面 DP 只有 all-reduce）。\n优化：all-gather 的通信可以与 optimizer step overlap（先把已更新的部分 gather），或与 forward 重叠（每 layer 用之前才 gather）。\nZeRO-2 把梯度也按 \\(1/N_d\\) 分片——backward 直接 reduce-scatter，省掉一份全量梯度。通信与 ZeRO-1 相同但显存更低：\n\\[ \\frac{2\\Psi + (2\\Psi + k\\Psi)}{N_d} \\]\n通常 ZeRO-2 是性价比最高的选择。\nZeRO-3 参数也分片。流程：\nforward：进入 layer \\(i\\) 之前 all-gather 该 layer 参数，用完立刻丢弃 backward：再 all-gather（PyTorch 实现里 backward 也需要完整参数算 Wgrad） backward 末尾 reduce-scatter 梯度 每层通信 2 * num_layers - 1 次额外 all-gather，总通信量 3Ψ（vs ZeRO-2 的 2Ψ）。但可以用 prefetching：forward layer \\(n\\) 时后台 all-gather layer \\(n+1\\)；backward layer \\(n\\) 时后台 all-gather layer \\(n-1\\)。DP 度不宜超过 ~512，否则通信延迟 \u0026gt; 计算时长，prefetch 撑不住。\nDP / ZeRO 总结 DP 只能 scale 到”单层能塞进一张 GPU”的模型。ZeRO 把参数 / 梯度 / 优化器状态按 DP 轴分片，但分不了 activation——activation 仍随 sequence length / batch size 涨。要继续打破天花板，得换维度——TP。\n05 Tensor Parallelism（TP） 数学基础 矩阵乘 \\(A \\cdot B\\) 的两种分片方式：\n列分片：\\(A \\cdot [B_1, B_2, \\dots] = [AB_1, AB_2, \\dots]\\)——结果直接拆开，不需通信 行分片：\\([A_1, A_2, \\dots] \\cdot [B_1; B_2; \\dots] = \\sum_i A_i B_i\\)——结果求和，需 all-reduce Transformer Block 内的 TP 切法 一个 transformer block 有两次大矩阵乘：\nAttention：Q、K、V 三个矩阵按列分片（Q、K、V 各自独立切），输出投影按行分片 FFN：先 Column Linear（gate + up），再 Row Linear（down） 约束： - TP 度不能超过 Q/K/V 的 head 数（GQA 下要 ≤ KV head 数）。比如 Llama-3 70B 用 GQA，KV head 8，所以 TP=8 恰好到顶。 - TP 通信发生在每一次 forward / backward——所以 TP 度大会被通信拖垮。\n通信模式小结 矩阵类型 切法 forward 通信 backward 通信 Column Linear 切输出维 all-gather 输出 reduce-scatter 梯度 Row Linear 切输入维 reduce-scatter 输出 all-gather 梯度 每次 Column + Row 一对共 2 * seq * bs * h / TP 字节/层，远多于 ZeRO-3（Ψ = L * 16h²）。所以 TP 通常只放在 node 内部（8 卡 NVLink 域内），跨 node 改用其他维度。\n06 Sequence Parallelism（SP） TP 把 hidden 维切了，但 Dropout、LayerNorm 这些 per-token 算子 没切到，它们在 TP 通信间隙里反而是 full sequence 的 full hidden——浪费。\nSP 把 TP 区域外（即 LN / Dropout / Residual）按 sequence 维切，配合 TP 通信交错：\n阶段 TP only TP + SP Enter TP（Column Linear 前） h sharded, s full h sharded, s all-gather to full TP 区域 h sharded, s full h sharded, s full Exit TP（Row Linear 后） h full + all-reduce, s full h full + reduce-scatter, s reduce-scatter to sharded SP 区域 h full, s full h full, s sharded 关键收益：Row Linear 之后那个 all-reduce 被替换成 reduce-scatter，省下一半通信量（因为接下来 SP 区域只需要 sharded 状态）。Megatron-LM 的论文里 SP 带来 ~10-15% 的吞吐提升。\n07 Context Parallelism（CP） 当 sequence 长度到 128K+，即使有 SP + full recomputation，activation 仍然随 seq length 线性涨——单 node 装不下。\nCP 的核心：把 TP 内部 也按 sequence 切。MLP、LayerNorm 这些 per-token 算子天然能处理 sequence 切片，不需要通信；只有 attention 需要全 sequence 的 K/V——这就是 Ring Attention 出场的地方。\nRing Attention 每个 GPU 在每个时间步做三件事：\n非阻塞地把自己的 K/V 发给下一个 GPU 本地算当前拥有的 Q·Kᵀ·V 等待上一个 GPU 的 K/V 到位，回到步骤 1 4 个 GPU、4 个 token 要做 4 个 step，每 step 是 send + local compute + recv 的流水。\nZig-Zag Ring Attention 朴素 Ring Attention 在 causal mask 下负载不均：第一个 GPU 一开始就拥有最早的 token，几乎不用等别人；最后一个 GPU 要等三轮。Zig-Zag 把 token 按交叉顺序分配到 GPU，让每个 GPU 都有”早 token + 晚 token”的混合，attention mask 上的工作量均匀。\nAllGather vs All-to-All 实现 两种方式实现 CP 通信：\nAllGather：一次同步收齐所有 K/V——显存峰值高（每卡临时存完整 K/V），但通信模式简单 All-to-All（Ring）：按 ring 一片一片传——显存友好（每卡只多存一片），但有多步基础延迟 一般 All-to-All 更省显存，AllGather 更简单。\n与 Flash Attention 的关系 CP 和 Flash Attention 都用 online softmax 思想避免存完整的注意力矩阵：Flash Attention 是单卡内的”算完一块丢一块”，CP 是多卡间的”算完一片丢一片”。可以叠用。\n08 Pipeline Parallelism（PP） 当模型单卡装不下，再叠一张卡也装不下时，需要把整层切到多卡：layers 1-4 在 GPU 0，layers 5-8 在 GPU 1……\nPP 通信只发生在 layer 之间传 activation，通信频率远低于 TP，所以跨 node 也能跑（IB 带宽够）。\nBubble 问题 朴素 forward→backward，所有 GPU 流水线排开会出现大量”等下一批 micro-batch”的空白——bubble。\n设 \\(p\\) = pipeline 度，\\(m\\) = micro-batch 数，\\(t_f\\) / \\(t_b\\) = 单 micro-batch 在单 stage 的 forward / backward 时间（一般 \\(t_b \\approx 2 t_f\\)）：\n\\[ r_{bubble} = \\frac{(p-1)(t_f+t_b)}{m(t_f+t_b)} = \\frac{p-1}{m} \\]\n调度方案演进 调度 思路 显存 Bubble AFAB（All-Forward-All-Backward） 先做完全部 forward，再做 backward 存 \\(m\\) 个 micro-batch 激活（爆炸） \\(\\frac{p-1}{m}\\) 1F1B 稳态阶段交叉做 forward / backward 存 \\(p\\) 个 micro-batch 激活（可控） \\(\\frac{p-1}{m}\\) Interleaved 每 GPU 持有 \\(v\\) 个 model chunk，micro-batch 在 chunk 间循环 同 1F1B \\(\\frac{p-1}{v \\cdot m}\\)（\\(v\\) 倍降幅） Llama 3.1 用 1F1B + Interleaved Stages + 可调 depth-first / breadth-first 调度。\n实测经验：跨 node 时 PP 比 TP 强很多——同样从 1 node（p=8）扩到 2 node（p=16），PP 只损失 ~14%，TP 要损失 ~43%。所以跨 node 用 PP 是更好的默认选择。\nZeroBubble 与 DualPipe 进一步把 backward 拆细：矩阵乘的 backward 实际是两件事——输入梯度 B（下层 backward 需要）和权重梯度 W（只在 optimizer step 前需要）。W 可以灵活地填进 bubble里。\nZeroBubble（论文）：这种细粒度切分，理论上能达到 ZB-H2 schedule 的零 bubble DualPipe（DeepSeek-V3）：在 ZeroBubble 基础上加双向流——两股 micro-batch 从 PP 维两端向中间走，进一步压缩空闲时间。DeepSeek V3 用 DualPipe 报告 “near-zero all-to-all overhead” 但调度本身需要 ILP 求解——工程复杂。\n09 Expert Parallelism（EP） 专门给 MoE。每个 expert 的 FFN 互相独立，按 expert 把 FFN 拆到不同 GPU——比 TP 轻得多（不用切矩阵乘，只路由 token）。\n关键 trick：DeepSeek-V3 限制每个 token 最多发到 M=4 个 node，让 token 尽量留在同一 node，减少跨 node 的 all-to-all。\n通常 EP 与 DP 联合使用：DP 切 batch，EP 切 expert——两个维度正交。\n有些人把 EP 视为 DP 的子集（“用 specialized routing 而非同构复制”），但概念上是独立的。\n10 5D Parallelism 综合 五种并行 + 三档 ZeRO 的对比：\n方法 分片维度 主要省什么 主要代价 DP batch activation（按 mbs） 受最大 batch 限制 PP model layers params bubble + 调度复杂 TP/SP hidden dim / seq params + activation 要高带宽通信 CP seq activation attention 通信开销 EP expert dim expert params 要 MoE + 路由通信 ZeRO-1 DP 副本间分片 optimizer state params 通信 ZeRO-2 + gradients 同上 同上 ZeRO-3 + params 同上 同上 PP vs ZeRO-3 两者都是”沿模型深度切 param”，但有重要区别：\nZeRO-3 Pipeline Parallelism 每卡存的 层的 \\(1/N_d\\) 完整的一层（或多个 chunk） 通信的是 权重 activation 编排 模型无关 模型无关（但要平衡层数） 实现难点 分片 + 通信调度 高效 PP schedule Scaling 偏好 大 mbs / seq_len（藏通信） 大 grad_acc（藏 bubble） DeepSeek-V3 训练用 PP + ZeRO-1（注意是 ZeRO-1 不是 ZeRO-3），是 PP 与 ZeRO-1 / ZeRO-2 组合的典型例子。ZeRO-3 与 PP 不常组合——叠加通信代价太大，除非你能把 GBS 撑得很高。\n组合原则 TP (with SP) ↔︎ PP / ZeRO-3：天然互补，TP 拆权重、PP / ZeRO-3 拆层 TP 通信频繁 → 留在 node 内（NVLink） PP / ZeRO-3 通信少 / 可 prefetch → 跨 node CP ↔︎ EP：互相独立，可一起用 CP ↔︎ TP：互补，TP 切 hidden、CP 切 seq EP ↔︎ DP：标准组合 经验法则：组织 GPU group 时，TP 组 ≤ node 大小（8），PP / ZeRO 跨 node。\n11 选型决策三步法 Step 1：把单卡训练跑通 GPU-rich case（卡多）：\n模型规模 推荐 \u0026lt; 10B 单种并行即可：TP 或 ZeRO-3 / DP + Full Recompute，跨 8 卡 10B-100B（\u0026gt; 8 卡） 选一个：(TP=8 + PP) / (TP=8 + ZeRO-3) / 纯 ZeRO-3 512+ 卡 纯 ZeRO-3 通信代价上来了——考虑 DP + TP 或 DP + PP 1024+ 卡 推荐 TP=8 + DP（ZeRO-2）+ PP 特殊情况： - 长序列 → 叠 CP（跨 node） - MoE → 叠 EP（跨 node）\nGPU-poor case（卡少）： - 开启 full activation recomputation（牺牲 30-40% 速度换显存） - 增大 grad_acc\nStep 2：凑到目标 GBS GBS 不对就要调：\nGBS 太小：扩 DP / 增 grad_acc；长序列下可以加 CP GBS 太大：减 DP；长序列下减 CP Step 3：优化吞吐 TP 度一路向上加到接近 node size（8），把其他并行度降下来 DP + ZeRO-3 同步扩，保持 GBS 不变 DP 通信撑不住时换 PP 单变量微调不同并行度 多试几个 mbs，找 GBS / 模型 / 计算 / 通信的最佳平衡 实测经验（4100+ 实验总结） 实验条件：seq=4096，GBS=1M tokens，8 卡 / node 的 H100 集群。\n节点越多效率越掉——小模型尤其明显（compute-to-model ratio 低），GBS 上限 1M 也限制了用大 batch 补偿 大模型反而被 memory 卡——节点少时装不下，装得下也跑在 GPU 显存极限附近，效率低 实现质量决定胜负：刚开始 TP 比 PP 快，PP 优化完后反超，再优化 TP 通信 overlap 又回到领先——没有永远的最优，要看实现 想在自己的集群复现可以用 nanotron 的 benchmark 脚本。\n12 GPU 底层：Kernel、Flash Attention、Mixed Precision GPU 体系结构速览（NVIDIA） 计算侧：132 个 SM（Streaming Multiprocessors），每 SM 128 个 core，H100 共 16,896 core 存储侧（按层级）：Registers（线程私有）→ Shared Memory / L1 cache（SM 内共享）→ L2 cache（SM 间共享）→ Global Memory（HBM，80GB，最慢） Kernel：跑在 GPU 上的代码，用 CUDA / Triton 写，编译成 PTX 调度：threads → warps（32 threads）→ blocks（一 block 在一 SM 上）→ grid Kernel 优化的四个核心 Memory Coalescing：相邻 threads 访问连续地址，合并成一次内存事务 Tiling：把大矩阵切成小块，让数据块留在 shared memory 里复用（GEMM 的核心） Thread Coarsening：每个 thread 处理多个数据元素，摊销调度开销 Minimizing Control Divergence：让 warp 内 threads 走相同分支，避免 SIMT 的半 warp 闲置 Fused Kernel CPU 与 GPU 操作可以异步——host 不必等 kernel 完成。Fused Kernel 把连续的点状操作（LayerNorm、激活函数等）打包进一个 kernel，避免 GPU ↔︎ HBM 来回倒数据。\nHorace He 的 Making Deep Learning Go Brrr From First Principles 是经典入门。\nFlash Attention 1 / 2 / 3 朴素的 attention 要在 HBM 上物化 \\(S\\) 和 \\(P\\) 两个大矩阵——HBM 带宽成了瓶颈。\nFlash Attention 的 trick： - 把 S 切成能在 SM shared memory 装下的小块 - online softmax：不物化完整 S/P，只保存 max、sum 这些归一化所需的标量 - 直接在 SRAM 里累加部分 O 输出\n效果： - 显存大幅降低（不存 \\(S\\)、\\(P\\)） - 顺带去掉 \\(S^2\\) 的 naïve attention 复杂度影响\nFlash Attention 2、3 主要是进一步压榨 GPU：减少非 matmul 操作数（FA2）、更精细的 warp / block 划分（FA2）、针对 Hopper Tensor Core 的 FP8 优化（FA3）。\nFA 对 attention pattern 有约束，要更灵活的 variant 看 FlexAttention。\nMixed Precision Training 格式 bits sign exponent mantissa 备注 float32 32 1 8 23 默认 float16 16 1 5 10 范围小 bfloat16 16 1 8 7 brain float，范围同 fp32 fp8 e4m3 8 1 4 3 精度好但范围小 fp8 e5m2 8 1 5 2 范围大但精度差 bf16 保留 fp32 的范围，牺牲精度——是当前混合精度的默认。\nFP16 / BF16 训练的三个 trick FP32 master weights：小 weight 不会 underflow 到 0 Loss scaling：梯度通常 \u0026lt; 1，提前把 loss 放大 N 倍 → backward 后再 unscaled 累加用 FP32：求和 / 平均时用 FP32 累加避免精度损失 FP8 训练 H100 上 FP8 GEMM 比 BF16 理论 2× FLOPS——数字诱惑巨大，但不稳定。\n第一次公开大规模成功的是 DeepSeek-V3。关键 trick： - 按 per-tile 量化：输入/激活用 \\(1 \\times 128\\) tile，权重用 \\(128 \\times 128\\) tile，减少 outlier 影响 - 主权重、累加梯度保持高精度（BF16/FP32），只有 GEMM 操作本身用 FP8\n各方案显存对比：\n方案 Master Grad acc W Grad Opt 总(bytes/param) BF16 + FP32 mixed（baseline） bf16 fp32 fp32 bf16 fp32+fp32 20 同上但不 FP32 grad acc bf16 fp32 n/a bf16 fp32+fp32 16 Transformer Engine fp8 n/a n/a fp32 fp32+fp32 16（-20%） FP8-LM O3 fp8 fp16 fp16 fp8 fp8+fp16 9（-55%） DeepSeek-V3 fp8 fp32 fp32 fp8 bf16+bf16 15（-25%） nanotron FP8 fp8 bf16 fp32 fp8 fp8+fp8 10（-50%） 到 2025 年初 FP8 仍算”实验性技术”，但趋势明确——Blackwell 已宣布支持 FP4。开源参考实现：nanotron FP8 PR。\n13 Conclusion（结语） 整本书走完一遍：单卡 → DP → ZeRO → TP → SP → CP → PP → EP → 5D 综合 → GPU 底层 → Flash Attention → Mixed Precision / FP8。最终能读懂 Llama-3 的 4D 并行配置图。\n分布式训练不再是少数 pretrain 团队的专利——AI builder 社区扩大 + 模型规模继续增大，任何 fine-tuning / inference / 训练 都越来越可能用上分布式技巧。这正是该读这本书的时机。\nAppendix A：集合通信原语速查 原语 功能 在 LLM 训练中的典型用途 Broadcast 1→N 广播 主节点广播初始权重 Reduce N→1 归约 单节点收梯度 AllReduce N→N 归约 + 广播 DP 梯度同步 Gather N→1 收集 收集 log / 监控 AllGather N→N 收集 ZeRO-3 gather params Scatter 1→N 分发 初始化分发 ReduceScatter N→N 归约并分片 ZeRO-1/2 gradient 同步 Ring AllReduce AllReduce 的带宽最优实现 大规模 DP Barrier 同步屏障 checkpoint 同步 实现：NCCL（NVIDIA Collective Communications Library）是事实标准。\nAppendix B：典型 LLM 训练规模（数量级速查） Input tokens：每 batch 处理 seq * mbs 个 token Activation（单 layer）：seq * mbs * h 个元素 权重 / 梯度：每个线性层约 \\(h^2\\) 元素 Optimizer 状态（Adam + mixed precision）：每权重矩阵 \\(6h^2\\)（FP32 momentum + variance + FP32 master weight） Transformer block 总参数：\\(16h^2\\)（含 GLU 的 MLP），无 GLU 是 \\(12h^2\\) 整模型参数：\\(16h^2 \\cdot L\\)（含 GLU） Forward FLOPs：\\(\\approx 2 \\cdot \\text{num\\_tokens} \\cdot \\text{num\\_params}\\) Backward FLOPs：\\(\\approx 2 \\times \\text{forward}\\) Appendix C：通信 vs 计算 Overlap 的数学判断 设 \\(\\text{peak\\_flops}\\) 是 GPU 算力（FLOPs/s），\\(\\text{peak\\_bw}\\) 是互联带宽（B/s）。两者之比决定 overlap 是否成立：\nDP（baseline） 每 bucket 通信：\\(t_{comm} = \\frac{bucket\\_size \\cdot 2(DP-1)}{DP \\cdot \\text{peak\\_bw}}\\)\nBackward 计算：\\(t_{compute} = \\frac{4 \\cdot \\text{num\\_tokens} \\cdot \\text{num\\_params}}{\\text{peak\\_flops}}\\)\nOverlap 条件（通信 / 计算 ≤ 1）：\n\\[ \\frac{\\text{num\\_params}}{2 \\cdot \\text{num\\_tokens}} \\cdot \\frac{DP-1}{DP} \\cdot \\frac{\\text{peak\\_flops}}{\\text{peak\\_bw}} \\le 1 \\]\nZeRO-3（FSDP） 每 block 通信总量 \\(3 \\cdot 16h^2 / DP\\)，所有 layer 总 \\(3L \\cdot 16h^2 / DP\\)。\n\\[ \\frac{t_{comm}}{t_{compute}} = \\frac{1}{2 \\cdot seq \\cdot mbs} \\cdot \\frac{DP-1}{DP} \\cdot \\frac{\\text{peak\\_flops}}{\\text{peak\\_bw}} \\le 1 \\]\n——通信能藏进计算的临界条件与 sequence × mbs 成反比。\nTP 每 block 通信 \\(8 \\cdot seq \\cdot mbs \\cdot h / TP\\)：\n\\[ \\frac{t_{comm}}{t_{compute}} = \\frac{TP-1}{2h} \\cdot \\frac{\\text{peak\\_flops}}{\\text{peak\\_bw}} \\le 1 \\]\n只与 hidden dim 和 TP 度有关，与 seq/mbs 无关——所以 TP 的通信能否被盖住主要看 hidden size。\nPP 每 microbatch 通信 \\(4 \\cdot seq \\cdot mbs \\cdot h\\)，所有 grad_acc 步总和 \\(4 \\cdot grad\\_acc \\cdot seq \\cdot mbs \\cdot h\\)：\n\\[ \\frac{t_{comm}}{t_{compute}} = \\frac{\\text{peak\\_flops}}{32 \\cdot h \\cdot L_{next} \\cdot \\text{peak\\_bw}} \\le 1 \\]\n也跟 seq / batch 无关——靠隐藏维 × 下一 stage 的层数决定 PP 的可扩性。\nAppendix D：参考资料 里程碑论文 ZeRO（Rajbhandari et al., 2019） Megatron-LM（TP / SP；Shoeybi et al., 2019） Ring Attention（Liu et al., 2023） Flash Attention（Dao et al., 2022；FA2 2023；FA3 2024） Pipeline Parallel schedules：GPipe、1F1B、PipeDream、ZeroBubble DeepSeek-V3 Technical Report（FP8 + DualPipe） FP8-LM（FP8 pretraining） 训练框架 PyTorch FSDP DeepSpeed Megatron-LM nanotron picotron 内部交叉引用 [[Qwen2.5-3.7 系列最全总结]]：Qwen 实际训练用到的并行配置示例 [[Knowledge Distillation]]：同样依赖大模型分布式部署 阅读建议 第一次读：跳过 Appendix 数学推导，先把 1-11 章过一遍，建立全局图 第二次读：精读 ZeRO 与 TP 章节，对照 picotron 源码 工程实践：先看 nanoGPT → picotron → nanotron 的递进 决策调优：跑通后用 nanotron benchmark 脚本在自己集群扫配置 ","date":"2026-06-24T00:00:00Z","permalink":"/ultrascale-playbook-%E7%AC%94%E8%AE%B0/","title":"The Ultra-Scale Playbook 学习笔记"},{"content":"2025-2026 大型 LLM 架构对比 整理自 Sebastian Raschka 的 The Big LLM Architecture Comparison（最后更新于 2026 年 4 月 2 日）。原文以架构为主线，扫描了 23+ 个 2025-2026 年发布的主流开源 LLM。本文按模型族拆成多份笔记，每篇都包含关键技术点、对应原图与索引。\n总览 文章开头的一张图囊括了 2025-2026 年开源 LLM 的主要架构变化趋势：从 GPT-2 到 DeepSeek V3 / Llama 4，骨架仍惊人相似——结构性的”大变化”其实不多，更多是细节打磨。\nFigure 1: A subset of the architectures covered in this article.\n作者关注的核心问题是：哪些架构上的小修小补真的有用？例如：\n位置编码：absolute → RoPE 多头注意力：MHA → GQA（甚至 MLA） 激活函数：GELU → SwiGLU MoE 在 2025 年大规模回潮 23 个模型 / 系列笔记 按原文顺序，每篇都是一个独立笔记：\n# 模型 / 系列 笔记 关键创新点 1 DeepSeek V3 / R1 [[DeepSeek V3 系列]] MLA、MoE + shared expert 2 OLMo 2 [[OLMo 2]] Post-Norm、QK-Norm 3 Gemma 3 / 3n [[Gemma 系列]] 滑动窗口注意力、Pre+Post-Norm、PLE 4 Mistral Small 3.1 [[Mistral 系列]] 放弃滑动窗口、推理优化 5 Llama 4 [[Llama 4]] 交替 MoE + dense 6 Qwen3 (Dense / MoE) [[Qwen3 系列]] dense 与 MoE 并存 7 SmolLM3 [[SmolLM3]] NoPE（每 4 层） 8 Kimi K2 / K2 Thinking [[Kimi 系列]] 1T 参数、Muon optimizer 9 GPT-OSS [[GPT-OSS]] 滑动窗口 + 注意力 bias + attention sinks 10 Grok 2.5 [[Grok 2.5]] 少量大专家、类共享专家 11 GLM-4.5 [[GLM 系列]] 3 层 dense + MoE、共享专家 12 Qwen3-Next / Coder-Next [[Qwen3 系列]] Gated DeltaNet + Gated Attention、MTP 13 MiniMax M2 [[MiniMax M2]] per-layer QK-Norm、Partial RoPE 14 Kimi Linear [[Kimi 系列]] KDA、channel-wise gating 15 Olmo 3 Thinking [[OLMo 2#Olmo 3 Thinking]] Post-Norm、YaRN、滑动窗口 16 DeepSeek V3.2 [[DeepSeek V3 系列#DeepSeek V3.2]] Sparse Attention 17 Mistral 3 [[Mistral 系列]] 复用 DeepSeek V3 架构 18 Nemotron 3 Nano / Super [[Nemotron 3]] Mamba-Transformer 混合、latent MoE 19 Xiaomi MiMo-V2-Flash [[Xiaomi MiMo]] 激进滑动窗口 (size=128) 20 Arcee AI Trinity Large [[Arcee Trinity Large]] 3:1 SWA、NoPE、深度缩放 sandwich norm 21 GLM-5 [[GLM 系列]] 744B、MLA + DSA 22 Feb 2026 速览 [[Feb 2026 LLM 新发布]] 10+ 个模型 23 Gemma 4 [[Gemma 系列#Gemma 4]] V=K 共享、p-RoPE 关键技术概念笔记 这些是跨模型反复出现的机制，独立成笔记方便查阅：\n[[MLA（Multi-Head Latent Attention）]] — DeepSeek 系的核心 KV 压缩方案 [[MoE（Mixture of Experts）]] — 2025 年最热趋势 [[QK-Norm]] — 稳定训练的常见手段（OLMo、Gemma 3、MiniMax M2 等） [[Post-Norm vs Pre-Norm]] — Normalization 位置选择 [[滑动窗口注意力（Sliding Window Attention）]] — Gemma 3、GPT-OSS、Mistral 等 [[NoPE（No Positional Embedding）]] — SmolLM3、Arcee Trinity 等 [[Gated DeltaNet]] — 线性注意力变体，Qwen3-Next / Kimi Linear 用 [[KDA（Kimi Delta Attention）]] — channel-wise gating 的 KDA [[MTP（Multi-Token Prediction）]] — 训练信号增强 [[Partial RoPE]] — MiniMax 系的做法 [[Attention Sinks]] — 长上下文稳定手段 [[Latent Experts]] — Nemotron 3 Super 的 MoE 改造 线性注意力的兴起（贯穿全文） 2025 下半年有一个清晰的趋势：用线性注意力替代 O(n²) 的标准注意力，又保持一定的全局注意力来弥补精度损失。\n2025/06：MiniMax M1（lightning attention） 2025/08：Qwen3-Next（Gated DeltaNet + Gated Attention 3:1） 2025/09：DeepSeek V3.2（DeepSeek Sparse Attention） 2025/10：Kimi Linear（KDA + MLA 3:1） 2025/12：Nemotron 3（Mamba-2 + MoE + 少量 attention） 但作者也指出：MiniMax M2 反而放弃了 M1 的线性注意力，回到 full attention——线性注意力在推理 / 多轮对话 / agentic 场景下表现欠佳。这让”线性注意力是否能落地”暂时成疑，而 Kimi Linear 算是给出了有竞争力的回应。\n一些观察 MoE 已经不再是少数派：DeepSeek V3、Llama 4、Qwen3 MoE、Mistral 3、Kimi K2、MiniMax M2、Nemotron 3、Xiaomi MiMo、Arcee Trinity、GLM-5、Gemma 4 MoE……几乎所有旗舰都给了 MoE 版本。 架构层面的”小改进”很密集：QK-Norm、滑动窗口、NoPE、Partial RoPE、attention sinks、latent expert、shared expert、post-norm……这些都不是颠覆性创新，但叠加后能稳定拿到质量 / 效率收益。 共享专家仍有争议：Qwen3 放弃了共享专家（认为对推理优化不利），DeepSeek V3 保留，Qwen3-Next 又加回来——团队之间没有共识。 滑动窗口的”激进”程度差异大：Gemma 3 是 1024，Xiaomi MiMo 是 128，Arcee Trinity 是 4096。 稀疏度差异巨大：从 4.37%（MiniMax M2）到 9.36%（Qwen3 235B）到更高（Grok 2.5 / Nemotron 3）。 原文引用 Last updated: Apr 2, 2026 (added Gemma 4 in section 23)\n“It has been seven years since the original GPT architecture was developed. … beneath these minor refinements, have we truly seen groundbreaking changes, or are we simply polishing the same architectural foundations?”\n来源：The Big LLM Architecture Comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/2025-2026-llm%E6%9E%B6%E6%9E%84%E5%AF%B9%E6%AF%94/","title":"2025-2026 LLM 架构对比"},{"content":"Arcee AI Trinity Large 原文第 20 节。Arcee AI 在 2026-01-27 发布的 400B MoE（13B 激活）。技术报告相当详细。\n关键设计 3:1 SWA（介于 Gemma 3 与 Olmo 3 之间） local:global = 3:1（不像 Gemma 3 / Xiaomi 是 5:1，也不像纯 global） 滑动窗口大小 = 4096（与 Olmo 3 类似） [[NoPE（No Positional Embedding）|NoPE]] in global layers global attention 层用 NoPE（参考 [[SmolLM3]] 的做法） 局部层照常用 RoPE Gated Attention 不像 Qwen3-Next 是完整 GatedDeltaNet，而是借鉴了 Qwen3-Next 的 attention 门控 在 scaled dot-product 后、output linear projection 前 加 elementwise gating 效果：减少 attention sinks，改善长序列泛化 + 训练稳定性 Figure 55: Illustration of the gating mechanism that Trinity Large uses in the attention mechanism.\nDepth-Scaled Sandwich Norm 之前没见过这种设计。每个 block 有 4 个 RMSNorm（不像 Gemma 3 的 2 个）：\n第二个 RMSNorm 的 gain 被初始化为约 1/√L（L = 总层数） 训练早期残差更新小，随训练学到合适 scale 长大 看起来像 Gemma 3 的 sandwich norm，但加了”深度缩放” [[MoE（Mixture of Experts）|MoE]] DeepSeek-like：大量小专家 但比 DeepSeek 略粗（coarser）—— 利于推理吞吐（Mistral 3 Large 也这么干） 其他训练改进 新的 MoE load-balancing MuOpt optimizer（与 Kimi K2 用 Muon 是不同方案） 模型族 Trinity Large 400B-A13B（旗舰） Trinity Mini 26B-A3B Trinity Nano 6B-A1B Figure 53: Overview of the Trinity Large architecture.\nFigure 54: Arcee AI Trinity Large next to GLM 4.5 of a relatively similar size (400B vs 355B).\n相关笔记 [[滑动窗口注意力（Sliding Window Attention）]] [[NoPE（No Positional Embedding）]] [[MoE（Mixture of Experts）]] [[QK-Norm]] —— Trinity 也用了 QK-Norm [[SmolLM3]] —— NoPE [[GLM 系列]] GLM-4.5 —— 大小相近 [[Gemma 系列]] —— 同样 sandwich norm 来源 原文 section 20 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/arcee-trinity-large/","title":"Arcee AI Trinity Large"},{"content":"Attention Sinks 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 在序列开头放一个”永远被 attend 到”的特殊 token，让长上下文 attention 稳定。这个特殊 token 会自然学到”整序列通用信息”。\n起源 Efficient Streaming Language Models with Attention Sinks 论文。\n经典做法 vs gpt-oss 做法 做法 描述 经典 在 tokenized input 开头放一个特殊 token gpt-oss 不污染 input，而是用每个 head 学到的 bias logits，append 到 attention scores → 同样的稳定效果，但不修改 token 序列。\nArcee Trinity Large 的”减少 attention sinks” Arcee Trinity 在 attention 中加 elementwise gating，能”减少 attention sinks”——意思是设计上对 attention sinks 现象做了抑制。\n图示 Figure 31: The use of attention sinks in gpt-oss.\n相关笔记 [[GPT-OSS]] [[Arcee Trinity Large]] 来源 The Big LLM Architecture Comparison, section 9.3 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/attention-sinks/","title":"Attention Sinks"},{"content":"DeepSeek V3 系列（V3 / R1 / V3.2） 原文第 1、16 节。DeepSeek R1（2025 年 1 月）发布后引起极大关注，它是构建在 DeepSeek V3（2024 年 12 月）架构之上的 reasoning 模型。V3.2（2025 年 12 月）则是另一个重要升级，引入了 sparse attention。\n关键架构：V3 / R1 V3 是 671B 总参数 / 37B 激活参数的 MoE 模型。两大核心技术：\n1.1 [[MLA（Multi-Head Latent Attention）]] V3 用 MLA 替代了 GQA。MLA 的核心思想是把 K / V 压缩到低维 latent space 再存进 KV cache，推理时再展开——比 GQA 在相同 cache 大小下表现更好（依据 DeepSeek-V2 论文的消融）。\nFigure 3: Comparison between MLA (used in DeepSeek V3 and R1) and regular MHA.\nFigure 4: Annotated tables from the DeepSeek-V2 paper showing that MLA outperforms GQA.\nQ 在训练时也会被压缩，但推理时不压缩（避免额外计算）。\n1.2 [[MoE（Mixture of Experts）]] V3 用 256 个专家 + 1 个共享专家，每 token 路由 8 个专家 + 始终激活 1 个共享专家：\n总参数 671B，激活 37B 共享专家：始终激活，处理常见 / 重复模式，让其他专家腾出 capacity 学更专门的模式 Figure 5: An illustration of the MoE module in DeepSeek V3/R1 (right) compared to a standard FeedForward block (left).\nFigure 6: From “DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models” — the shared expert idea.\n1.3 小结 V3 是个巨大但推理高效的模型：activation 只有 37B / 671B MLA 替代 GQA 是关键设计选择 与 Llama 3 405B 相比，V3 更大但推理便宜得多 DeepSeek V3.2 2025 年 12 月发布，引入 DeepSeek Sparse Attention (DSA)，在不显著损失建模性能的前提下，把长上下文推理成本压低。\nFigure 47: A timeline of the DeepSeek model releases since DeepSeek V3.\nFigure 48: The DeepSeek model architecture with multi-head latent and sparse attention.\n作者把 V3.2 的细节放到了单独一篇长文里：A Technical Tour of the DeepSeek Models from V3 to V3.2\n与其他模型的关系 [[Llama 4]] Maverick 整体结构与 V3 相似，但用 GQA 不用 MLA；MoE 模式是”少量大专家”。 [[Kimi 系列#8 Kimi K2 / K2 Thinking|Kimi K2]] 是 V3 架构放大到 1T 参数的版本。 [[Mistral 系列#Mistral 3（2025-12-02）|Mistral 3]] Large 几乎完全是 V3 架构的复刻。 [[GLM 系列]] GLM-5 学习了 MLA + DSA。 来源 原文：The Big LLM Architecture Comparison, section 1 \u0026amp; 16 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/deepseek-v3-%E7%B3%BB%E5%88%97/","title":"DeepSeek V3 系列"},{"content":"Feb 2026 LLM 新发布（速览） 原文第 22 节。2026 年 1-2 月集中爆发的一波开源 LLM，作者列了 10 个值得关注的：\n列表 发布日期 模型 笔记 2026-01-27 Arcee AI Trinity Large [[Arcee Trinity Large]] 2026-01-27 Moonshot AI Kimi K2.5 （K2 的小升级） 2026-02-01 StepFun Step 3.5 Flash （MTP-3） 2026-02-03 Qwen3-Coder-Next [[Qwen3 系列#Qwen3-Coder-Next]] 2026-02-12 z.AI GLM-5 [[GLM 系列#GLM-5]] 2026-02-12 MiniMax M2.5 （M2 升级） 2026-02-13 Nanbeige 4.1 3B 2026-02-15 Qwen 3.5 2026-02-16 Ant Group Ling 2.5 1T \u0026amp; Ring 2.5 1T 2026-02-17 Cohere Tiny Aya 2026-03-06 Sarvam 30B / 105B 已在本系列笔记中覆盖的 Arcee AI Trinity Large → [[Arcee Trinity Large]] Qwen3-Coder-Next → [[Qwen3 系列#Qwen3-Coder-Next]] GLM-5 → [[GLM 系列#GLM-5]] Kimi K2.5 → [[Kimi 系列#K2 的后继]] MiniMax M2.5 → [[MiniMax M2#M2.5]] 来源 原文 section 22 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/feb-2026-llm-%E6%96%B0%E5%8F%91%E5%B8%83/","title":"Feb 2026 LLM 新发布（速览）"},{"content":"Gated DeltaNet 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。一个线性 attention 变体，是 Qwen3-Next 的核心组件。\n一句话 Gated DeltaNet 是 DeltaNet + Mamba 风格 gating 的组合：\nDeltaNet：用 q / k / v / α / β 维护一个fast-weight memory，按 delta rule 更新（线性 O(n) 缩放） Mamba gating：额外用门控机制控制 memory 写入/读取 → 本质是”线性 attention 的现代变种”，常被拿来做 3:1 混合中的”线性那 3 层”。\nDeltaNet 是什么 DeltaNet 起源：用一个 fast-weight memory（类似 RNN 的 hidden state）来记录历史信息。Q 读取，α / β 控制更新强度。\n为什么不直接用 linear attention 早期 linear attention（2020 Transformers are RNNs）总掉精度，所以没在 SOTA 模型中落地 DeltaNet 通过 delta rule update，比”加性”linear attention 更精细 Gated DeltaNet 加上 Mamba 的 gating，更稳定 与 Mamba-2 的对比 “Mamba keeps a state with a learned state-space filter (essentially a dynamic convolution over time). DeltaNet keeps a tiny fast-weight memory updated with α and β and reads it with q, with small convolutions only used only to help form q, k, v, α, β.”\n两者都属于”linear-time, cache-free”族，本质都是 state-space 风格的 update，但具体数学结构不同。\n谁在用 Qwen3-Next：3 层 Gated DeltaNet + 1 层 Gated Attention 3:1 Kimi Linear：进一步改进为 [[KDA（Kimi Delta Attention）|KDA]]（channel-wise gate） Nemotron 3：用 Mamba-2（DeltaNet 的近亲） 图示 Figure 36: The Gated DeltaNet + Gated Attention hybrid mechanism. Note that these are arranged in a 3:1 ratio.\n相关文章 Gated Delta Networks: Improving Mamba2 with Delta Rule 相关笔记 [[KDA（Kimi Delta Attention）]] [[Qwen3 系列]] Qwen3-Next [[Kimi 系列]] Kimi Linear [[Nemotron 3]] 来源 The Big LLM Architecture Comparison, section 12.2, 14 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/gated-deltanet/","title":"Gated DeltaNet"},{"content":"Gemma 系列（Gemma 3 / 3n / 4） 原文第 3、23 节。Google 的 Gemma 系列一直是”被低估”的高质量开源模型——27B 这个 size 区间在 Mac Mini 上都能跑。\nGemma 3（2025 年初） 3.1 [[滑动窗口注意力（Sliding Window Attention）]] Gemma 2 已经用滑动窗口，Gemma 3 进一步把 local:global 比例从 1:1 改成 5:1（每 5 层 SWA + 1 层 full attention），窗口大小从 4096 缩到 1024。\n效果：KV cache 内存大幅下降，对 perplexity 几乎没影响。\nFigure 11: Annotated figure from Gemma 3 paper showing the KV cache memory savings via sliding window attention.\nFigure 12: A comparison between regular attention (left) and sliding window attention (right).\nFigure 13: Sliding window attention has little to no impact on perplexity (Gemma 3 paper).\n3.2 [[Post-Norm vs Pre-Norm|Pre-Norm + Post-Norm 混合]] Gemma 3 在 attention / FFN 前后都加 RMSNorm（Gemma 2 也是这样），算”两头兼顾”。\n原始 Transformer：Post-LN GPT-2 / 主流：Pre-Norm OLMo 2：Post-Norm（在残差内） Gemma 3：Pre + Post 都有 Figure 14: An architecture comparison between OLMo 2 and Gemma 3; note the additional normalization layers in Gemma 3.\n3.3 小结 27B size 在 Mac Mini 上能跑，是个甜蜜点 滑动窗口是核心效率手段 Pre+Post Norm 是它的”招牌” 与 DeepSeek V3 不同，Gemma 3 是 dense 模型（不是 MoE） Gemma 3n（手机优化版） 针对小设备（手机）优化。两大新设计：\nPer-Layer Embedding (PLE)：只把”模型核心参数”放 GPU，token / layer 特定的 embedding（文本、音频、视觉）按需从 CPU / SSD 流式加载 MatFormer (Matryoshka Transformer)：单个 LLM 可以”切片”成多个独立可用的子模型，推理时只跑需要的部分 Figure 15: Annotated figure from Google’s Gemma 3n blog illustrating the PLE memory savings.\nGemma 4（2026 年 4 月发布） 架构上变化很小——基本与 Gemma 3 一样：\n5:1 SWA + GQA Pre+Post Norm 仍是 dense 27B（升级到 31B） 两个容易被忽略的小变化：\nV = K：全局 attention 层把 value 复用为 key，KV cache 再省一份 p-RoPE：只对 25% 的频率对加位置信息，减少长上下文的位置噪声 Figure 58: Gemma 3 (27B) and Gemma 4 (31B) side by side.\n虽然架构改动小，但 benchmark 跳跃很大：在 AI Arena 上 Gemma 4 31B 接近更大的 Qwen3.5-397B-A17B。\nGemma 4 MoE 也发布了 MoE 版：26B-A4B，benchmark 与 dense 31B 几乎打平。\nFigure 60: Qwen3 Coder Flash compared to Gemma 4 MoE.\nFigure 61: Gemma 4 MoE (26B-A4B) is only slightly worse than Gemma 4 (31) dense.\n相关笔记 [[滑动窗口注意力（Sliding Window Attention）]] [[Post-Norm vs Pre-Norm]] [[QK-Norm]] [[Partial RoPE]] [[Mistral 系列]] —— Mistral Small 3.1 与 Gemma 3 的对比 [[GPT-OSS]] —— 也用滑动窗口 [[Xiaomi MiMo]] —— 激进的 SWA 来源 原文 section 3 \u0026amp; 23 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/gemma-%E7%B3%BB%E5%88%97/","title":"Gemma 系列（Gemma 3 / 3n / 4）"},{"content":"GLM 系列（GLM-4.5 / GLM-5） 原文第 11、21 节。智谱（z.AI / Zhipu）的旗舰开源模型系列。\n11 GLM-4.5 GLM-4.5 是 2025 年的主推模型：\n旗舰 355B MoE，平均 12 个 benchmark 超过 Claude 4 Opus Air 版本 106B Instruct / reasoning hybrid，类似 Qwen3，但更针对 function calling / agent Figure 33: GLM-4.5 benchmark.\n关键设计 开头 3 层 dense + 之后 MoE（学自 DeepSeek V3） dense 开头的目的：稳定早期收敛；MoE 路由决策如果一开始就介入，会干扰低层语法 / 语义特征提取 保留 shared expert（与 Qwen3 不一样） 保留 attention bias（与 GPT-2 / GPT-OSS 一致） Figure 34: GLM-4.5 next to a similarly-sized Qwen3 model.\n21 GLM-5（2026-02-11） 春节前一周发布，是 2026 年 GLM 系列的开年旗舰：\n总参数 744B（GLM-4.5 是 355B，翻倍） 激活参数 40B（GLM-4.5 是 32B） 介于 DeepSeek V3.2 和 Kimi K2 之间 关键升级 采用 DeepSeek 的 [[MLA（Multi-Head Latent Attention）|MLA]] —— 这是 GLM-4.5 没有的 采用 DeepSeek Sparse Attention（与 V3.2 同源） 专家从 160 增到 256 embedding / expert dim 从 5120 → 6144 intermediate 从 1536 → 2048 层数从 92 → 78（变浅，可能是为了降推理延迟——层深度无法并行） Figure 56: Architecture of GLM-5 and GLM-4.5 side by side.\nFigure 57: GLM architectures next to benchmarks. GLM-4.7 architecture is similar to GLM-4.5.\n相关笔记 [[DeepSeek V3 系列]] —— GLM 学了 V3 的 MLA + sparse attention [[MoE（Mixture of Experts）]] [[MLA（Multi-Head Latent Attention）]] [[Qwen3 系列]] —— 同期对照 来源 原文 section 11 \u0026amp; 21 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/glm-%E7%B3%BB%E5%88%97/","title":"GLM 系列（GLM-4.5 / GLM-5）"},{"content":"GPT-OSS 原文第 9 节。OpenAI 自 GPT-2（2019）以来首次发布开源模型，2025-08。两个 size：gpt-oss-20b 和 gpt-oss-120b。\n总体架构 整体骨架”很眼熟”——所有组件都在其他模型中见过。3.6B 激活参数，MoE + 滑动窗口 attention + GQA。\nFigure 26: Architecture overview of the two gpt-oss models.\n9.1 宽度 vs 深度 维度 gpt-oss Qwen3 30B-A3B 层数 24（浅） 48（深） Embedding dim 2880（宽） 2048 FFN expert dim 2880 768 注意力头数 2× 1× 作者在 Gemma 2 论文里找到的消融：9B 参数下，宽略胜于深（52.0 vs 50.8）。\n深模型：灵活，但训练不稳定（梯度消失 / 爆炸） 宽模型：tokens/sec 更高，memory cost 更高 Figure 27: Architecture comparison between gpt-oss and Qwen3.\n9.2 少量大专家 vs 大量小专家 gpt-oss 用 32 个专家（每 token 激活 4），比 Qwen3 的 128 个少很多。这与”细粒度专家”趋势相反——论文证明同样总参数下细粒度更好，但 gpt-oss 选了少量大专家。\n没有用 shared expert（与 Qwen3 一致）。\nFigure 28: From the DeepSeekMoE paper, illustrating the small-experts trend.\n9.3 Attention Bias + Attention Sinks 两个”复古”细节：\nAttention bias gpt-oss 在 attention 层重新启用了 bias units——上次见到是 GPT-2 时代。\n一般认为 redundant 2023 年论文（arXiv:2302.08626）证明对 k_proj 至少数学上多余 消融显示对最终 loss 影响微乎其微 Figure 29: gpt-oss models use bias units in the attention layers.\nFigure 30: Table from https://arxiv.org/pdf/2302.08626 showing average test loss with and without bias.\n[[Attention Sinks]] 经典 attention sinks：开头放一个”总是被 attend 到”的特殊 token，存一些”整序列通用信息”，让长上下文 attention 稳定。\ngpt-oss 的实现更巧妙：不是真实 token，而是每个 head 学到的 bias logits，append 到 attention scores。\n同样的稳定效果 不污染 tokenized input Figure 31: The use of attention sinks in gpt-oss.\n其他 滑动窗口 attention：每 2 层一层 SWA（Gemma 3 是 5:1） 用了[[滑动窗口注意力（Sliding Window Attention）|sliding window]] 与 GPT-2 的对比作者另写了一篇长文：From GPT-2 to gpt-oss 相关笔记 [[MoE（Mixture of Experts）]] [[滑动窗口注意力（Sliding Window Attention）]] [[Attention Sinks]] [[Qwen3 系列]] —— 与 gpt-oss 大小相近的 MoE [[DeepSeek V3 系列]] DeepSeek-V3 [[GLM 系列]] GLM-4.5 —— 也保留了 attention bias 来源 原文 section 9 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/gpt-oss/","title":"GPT-OSS（OpenAI 的开源模型）"},{"content":"Grok 2.5 原文第 10 节。xAI 在 2025 年中发布了 Grok 2.5 的权重（去年 xAI 的旗舰生产模型）。这给我们提供了一个少有的”真实生产系统”视角。\n整体架构 270B dense（或 MoE，看 source），整体架构”标准”。\nFigure 32: Grok 2.5 next to a Qwen3 model of comparable size.\n关键点 少量大专家（8 个）—— 与近年”小而多专家”趋势相反 有一个 SwiGLU 模块作为类共享专家：不是经典 shared-expert 设计（它的 intermediate dim 翻倍），但思路一致 作者特别指出：Qwen3 砍了 shared expert，看 Qwen4 / 后续模型是否回归会很有意思 与其他模型的关系 跟 [[GPT-OSS]] 一样：都选择”少量大专家” 与 [[Qwen3 系列]] 对照：Qwen3 不用 shared expert，Grok 2.5 用 真实生产模型，权重可分析——难得 来源 原文 section 10 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/grok-2.5/","title":"Grok 2.5"},{"content":"KDA（Kimi Delta Attention） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。Kimi Linear 对 [[Gated DeltaNet]] 的精细改进。\n一句话 KDA = 把 Gated DeltaNet 中的标量 gate 改成 channel-wise gate（每个 feature 通道独立）：\nGated DeltaNet：每 head 一个标量 gate 控制 memory decay KDA：每 head × 每 feature 通道 一个 gate，更精细地控制 memory 读写 → 论文说能给 memory 更细的控制，改进长上下文 reasoning。\nKimi Linear 的整体结构 3 层 KDA + 1 层 MLA + gate 3:1 混合 MLA（global）层用 [[NoPE（No Positional Embedding）|NoPE]]，让 MLA 跑成纯 MQA RoPE 不在 MLA 层用，作者说”位置偏置由 KDA block 处理” 与 Qwen3-Next 的对比 维度 Qwen3-Next Kimi Linear 线性层 Gated DeltaNet（scalar gate） KDA（channel-wise gate） 全局层 Gated GQA MLA + gate RoPE 标准 在 MLA 层用 NoPE 性能 Kimi Linear is as fast as GatedDeltaNet, and much faster than an architecture with multi-head latent attention (like DeepSeek V3/R1), while having a higher benchmark performance.\n→ 在 long-context / reasoning benchmark 上linear but no accuracy drop。\n图示 Figure 41: An overview of the linear attention hybrid architectures.\nFigure 42: Qwen3-Next and Kimi Linear side by side.\nFigure 43: Annotated figure from the Kimi Linear paper showing that Kimi Linear is as fast as GatedDeltaNet.\n相关笔记 [[Gated DeltaNet]] [[MLA（Multi-Head Latent Attention）]] [[NoPE（No Positional Embedding）]] [[Kimi 系列]] Kimi Linear [[Qwen3 系列]] Qwen3-Next 来源 The Big LLM Architecture Comparison, section 14.5 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/kdakimi-delta-attention/","title":"KDA（Kimi Delta Attention）"},{"content":"Kimi 系列（K2 / K2 Thinking / Kimi Linear） 原文第 8、14 节。Moonshot AI 的旗舰开源系列。K2 是 1T 参数的”超大杯”，Kimi Linear 是 2025 年 10 月推出的线性注意力版本。\n8 Kimi K2 / K2 Thinking K2（2025 年）是个 1T 参数的”庞然大物”：\n训练用了 Muon optimizer（不是 AdamW）——首次在 1T 量级使用 架构基本是 DeepSeek V3 放大版（更多专家、更少 MLA 头） 2025-11-06 推出 K2 Thinking：架构不变，context 从 128k 扩到 256k 训练 loss 曲线 Kimi 团队特别强调训练 loss 曲线”异常平滑”——但作者指出这不稀奇（看 OLMo 2 也很平滑），真正值得注意的是 loss 衰减得多好。这得益于 Muon optimizer。\nFigure 24: Annotated figures from the Kimi K2 announcement blog article and the OLMo 2 paper.\nFigure 25.1: An architecture comparison between DeepSeek V3 and Kimi K2.\nFigure 25.2: DeepSeek R1 versus Kimi K2 Thinking architecture (top) and Kimi K2 Thinking benchmarks (bottom).\n截至本文撰写时，K2 可能是当世最大开源 LLM（Llama 4 Behemoth 没发、闭源不算、Google Switch Transformer 1.6T 是不同代 encoder-decoder）。\n14 Kimi Linear（2025-10） Kimi Linear 是在 MiniMax M2 刚回归 full attention 之后推出的”线性注意力”反击。\n14.1 / 14.2 传统 attention 与线性 attention 传统 attention：QK^T 是 n×n 矩阵 → O(n²) 时间空间。\n线性 attention 用 kernel trick 把计算重排：\n\\[\\text{Attention}(Q, K, V) = \\phi(Q)(\\phi(K)^\\top V)\\]\n→ O(n) 而非 O(n²)。问题：早期方案（2020 Transformers are RNNs）都因为精度下降而未在 SOTA 模型中落地。\nFigure 40: Illustration of the quadratic cost in attention due to sequence length n.\n14.3 线性注意力的”复兴” 时间 模型 线性 attention 方案 2025-06 MiniMax M1 lightning attention 2025-08 Qwen3-Next [[Gated DeltaNet]] + Gated Attention 3:1 2025-09 DeepSeek V3.2 DeepSeek Sparse Attention 2025-12 MiniMax M2 回退到 full attention 2025-10 Kimi Linear KDA + MLA 3:1 MiniMax 团队公开说：linear attention 在生产 LLM 里很棘手——普通 prompt 看起来还行，但 reasoning / multi-turn / agentic 任务掉点严重。\n这本来可能是 linear attention 走不通的”判决”——但 Kimi Linear 用 KDA 拿回了一些精度。\n14.4 Kimi Linear vs. Qwen3-Next 两者结构上很像：\n都用 3:1 混合（3 层线性 + 1 层 full attention） 共享 [[Gated DeltaNet]] 思路 区别：\n维度 Qwen3-Next Kimi Linear full attention 层 Gated GQA [[MLA（Multi-Head Latent Attention） 线性层 Gated DeltaNet（scalar gate） [[KDA（Kimi Delta Attention） 位置编码 RoPE 在 MLA 层用 NoPE（让 MLA 跑成纯 MQA） Figure 42: Qwen3-Next and Kimi Linear side by side.\n14.5 KDA（Kimi Delta Attention） KDA 是对 Gated DeltaNet 的精细改进：\nGated DeltaNet：每个 head 一个标量 gate 控制 memory decay KDA：每个 feature 通道一个 gate（channel-wise） → 论文说能给”memory 更细的控制”，改进长上下文 reasoning。\nMLA 在 DeepSeek-V2 论文的消融里，在调好超参后与 full attention 持平——这让 Kimi Linear 在长上下文 / reasoning benchmark 上”linear 但不掉点”成为可能。\n48B 参数，比 K2（1T）小 20×，但走另一条路。\n相关笔记 [[MLA（Multi-Head Latent Attention）]] [[MoE（Mixture of Experts）]] [[Gated DeltaNet]] [[KDA（Kimi Delta Attention）]] [[NoPE（No Positional Embedding）]] [[Qwen3 系列]] Qwen3-Next [[DeepSeek V3 系列]] DeepSeek V3.2 [[MiniMax M2]] 来源 原文 section 8 \u0026amp; 14 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/kimi-%E7%B3%BB%E5%88%97/","title":"Kimi 系列（K2 / K2 Thinking / Kimi Linear）"},{"content":"Latent Experts 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 Latent Experts = MoE 中的专家在 latent space（低维） 工作，而非原始 token embedding 空间：\n输入 4096 维 down-project 到 1024 专家在 1024 维工作 输出 up-project 回 4096 → 节省计算量，专家可以更”小”。\n谁在用 Nemotron 3 Super（2026-03 发布） 对比传统 MoE 维度 传统 MoE Latent MoE 专家 dim 等于 FFN 隐藏层 等于 latent dim（更小） 计算 直接 in/out 多了 down/up project 专家容量 大 小但高效 图示 Figure 51.3: Nemotron 3 Super 120B-A12B with latent MoE layers, multi-token prediction, and the Mamba-2 hybrid attention approach.\n相关笔记 [[MoE（Mixture of Experts）]] [[Nemotron 3]] 来源 The Big LLM Architecture Comparison, section 18.2 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/latent-experts/","title":"Latent Experts"},{"content":"Llama 4 Maverick 原文第 5 节。Llama 4 用 MoE，结构上与 DeepSeek V3 很像，但选择不同。\n关键设计 Llama 4 Maverick 整体骨架与 DeepSeek V3 类似，但有这些显著区别：\n维度 DeepSeek V3 Llama 4 Maverick 总参数 671B 400B 激活参数 37B 17B 注意力 MLA GQA MoE 模式 9 专家（每专家 hidden 2048） + 1 共享 2 专家（每专家 hidden 8192） MoE 放置 每层（除前 3 层） 隔层（dense / MoE 交替） 也就是说 Llama 4 Maverick 走”少量大专家”路线，DeepSeek V3 走”大量小专家 + 共享”路线。\nFigure 17: An architecture comparison between DeepSeek V3 (671B) and Llama 4 Maverick (400B).\n关键洞察 同样 MoE 趋势下，两种设计哲学：DeepSeek 偏”细粒度 + shared”，Llama 4 偏”少而大” Llama 4 原生多模态支持（但本文只关注文本部分） 2025 年 MoE 已经”不再是少数派” 相关笔记 [[DeepSeek V3 系列]] —— Maverick 的设计参照 [[MoE（Mixture of Experts）]] —— 两种 MoE 哲学的对比 [[Kimi 系列]] Kimi K2 —— 又一个 DeepSeek V3 放大版 来源 原文 section 5 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/llama-4/","title":"Llama 4"},{"content":"MiniMax M2 原文第 13 节。MiniMax（MiniMax）在 M1 之后发布的旗舰。benchmark 表现目前被认为是开源 SOTA。\n关键背景 M1（2025-06）用了 lightning attention（线性 attention 变体），456B 总参 / 46B 激活 M2（2025-12 之后）回退到 full attention——团队公开说 linear attention 在生产 LLM 里 tricky，普通 prompt 看起来 OK，但 reasoning / multi-turn / agent 掉点严重 Figure 37: MiniMax-M2 benchmark performance compared to other popular open-weight and proprietary LLMs.\nFigure 38: A timeline of the main LLMs covered in this article, next to some of the attention-hybrid models.\n总体架构 MiniMax-M2 与 Qwen3 惊人相似——只有层数 / 大小等数字差异，组件几乎一样。\n13.1 Per-Layer QK-Norm QK-Norm 一般是 per-head 共用（MHA / GQA 都一样）。MiniMax-M2 的”per_layer”实现：\nself.q_norm = MiniMaxText01RMSNormTP(self.head_dim * self.total_num_heads, eps=...) self.k_norm = MiniMaxText01RMSNormTP(self.head_dim * self.total_num_kv_heads, eps=...) hidden_size = num_heads * head_dim RMSNorm 的 scale 向量对 每个 head 的每个 head_dim 都有独立参数 → “per_layer” 意思是：每个 transformer block 各有自己的 QK-Norm（普通 QK-Norm 也这样），并且不是 head 间共享，而是每个 head 独立一套。\n注：config 里有 sliding window 设置，但默认关闭（与 Mistral Small 3.1 类似）。\nFigure 39: Comparison between Qwen3 and MiniMax-M2.\n13.2 MoE Sparsity 没有 shared expert（与 Qwen3 一致，与 Qwen3-Next 不一样） sparsity 极高：4.37% 激活 vs Qwen3 235B-A22B 的 9.36% 同样大小下，激活参数只有 10B（Qwen3 是 22B） 13.3 [[Partial RoPE]] 类似 M1，M2 在 attention 里用 partial RoPE：\n每个 head 的 rotary_dim 通道才做旋转 剩下 head_dim - rotary_dim 通道不变 Full RoPE: [r r r r r r r r] Partial RoPE: [r r r r — — — —] M1 README 原文：“Rotary Position Embedding (RoPE) applied to half of the attention head dimension with a base frequency of 10,000,000”\n目的 “implementing RoPE on half of the softmax attention dimensions enables length extrapolation without performance degradation.”\n作者猜测：长序列（特别是超过训练数据最长长度的）下，“完全不旋转” 比 “训练时没见过的剧烈旋转” 更安全。\n相关笔记 [[QK-Norm]]（普通版 vs Per-Layer 版） [[Partial RoPE]] [[MoE（Mixture of Experts）]] [[Qwen3 系列]] —— 同期对照 [[Kimi 系列]] Kimi Linear —— MiniMax 是先 linear attention 后回退，Kimi 是坚持 linear 来源 原文 section 13 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/minimax-m2/","title":"MiniMax M2"},{"content":"Mistral 系列（Mistral Small 3.1 / Mistral 3） 原文第 4、17 节。Mistral 团队的两代代表性模型：一个 24B dense、一个 675B MoE。\nMistral Small 3.1（2025-03） 24B dense，超过 Gemma 3 27B（除数学 benchmark 外），且推理更快。\n速度优势的来源：\n自定义 tokenizer 缩 KV cache 层数更少 架构上属于”标准 LLM”——GQA、无 SWA。\nFigure 16: An architecture comparison between Gemma 3 27B and Mistral 3.1 Small 24B.\n早期 Mistral 模型用过 SWA，但 Small 3.1 似乎放弃了——sliding_window: null 在 model hub config 里。可能是为了用更优化的 FlashAttention（sliding window 减少内存但不减少 latency，而 Small 3.1 关注的是 latency）。\nMistral 3（2025-12-02） 发布与 DeepSeek V3.2 前后只差一天。包含：\n3 个小 dense 模型（Ministral 3 系列）：3B / 8B / 14B 旗舰：Mistral 3 Large 675B MoE（41B 激活） + 2.5B 视觉编码器 Mistral 自 Mixtral（2023）以来第一次做 MoE。\n关键点 训练 in-house（不从 DeepSeek V3 继续训练，因为 tokenizer 不同） 与 NVIDIA 合作优化 Blackwell 芯片的 tokens/sec 架构上几乎与 DeepSeek V3 / V3.1 完全一样，只把专家大小 ×2、数量 ÷2 与 DeepSeek V3.2-Thinking 在 benchmark 上比差距明显 卖点：低成本、低延迟、多模态 Figure 49: Mistral 3 Large benchmarks from the Mistral 3 announcement, with the DeepSeek V3.2 results overlayed on top.\nFigure 50: DeepSeek V3 and Mistral 3 Large side by side.\n相关笔记 [[DeepSeek V3 系列]] —— Mistral 3 Large 与 V3 同构 [[Kimi 系列]] Kimi K2 —— 同样放大 V3 架构 [[滑动窗口注意力（Sliding Window Attention）]] —— Small 3.1 不用 SWA 来源 原文 section 4 \u0026amp; 17 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/mistral-%E7%B3%BB%E5%88%97/","title":"Mistral 系列（Small 3.1 / Mistral 3）"},{"content":"MLA（Multi-Head Latent Attention） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 MLA 是 DeepSeek 提出的 attention 变体：把 K / V 压缩到低维 latent space 再存进 KV cache，推理时再展开。比 GQA 在相同 cache 大小下表现更好。\n背景：GQA 是什么 [[MHA]] 每个 head 都有独立的 K / V；[[GQA]] 把多个 head 共享一组 K / V（group 内共享）：\n降低 KV cache 内存 推理时少算 K / V → 快 性能几乎不掉（消融证明） MLA 的不同思路 不共享 K / V head，而是把 K / V 张量压缩到低维再存：\n训练时 Q 也压缩（推理时不压缩，避免额外计算） 推理时压缩的 K / V 要展开才能用 → 多一次 matmul 优点：内存更省，且 DeepSeek-V2 论文消融显示建模性能优于 MHA 适用模型 DeepSeek V2（首创） DeepSeek V3 / R1 / V3.1 Kimi K2（K2 是 V3 架构的放大版） Mistral 3 Large（与 V3 几乎一样） GLM-5（2026 年新采用） 对比图 Figure 2: A comparison between MHA and GQA. Here, the group size is 2, where a key and value pair is shared among 2 queries.\nFigure 3: Comparison between MLA (used in DeepSeek V3 and R1) and regular MHA.\nFigure 4: Annotated tables from the DeepSeek-V2 paper showing that MLA outperforms GQA.\nKimi Linear 用 MLA + Gate Kimi Linear 在 global attention 层把 Gated GQA 换成 MLA + gate——是 MLA 唯一一次被用在 Gated DeltaNet 混合架构中。\n延伸阅读 本仓库已有更深入的 MLA 笔记：[[MLA]] DeepSeek-V2 论文 Build a Large Language Model (From Scratch) — 有 GQA + KV-cache 实现 来源 The Big LLM Architecture Comparison, section 1, 8, 14, 16, 17, 21 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/mlamulti-head-latent-attention/","title":"MLA（Multi-Head Latent Attention）"},{"content":"MoE（Mixture of Experts） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。本文梳理 2025-2026 年 LLM 中 MoE 的各种设计选择。\n一句话 MoE 把 transformer block 里的单个 FeedForward 模块替换成多个 FFN（专家） + 一个路由器。每 token 路由器只选少量专家激活。\n为什么 2025-2026 全面回潮 推理成本主要看激活参数而非总参数 → 巨大总参数 + 稀疏激活 = 强 capacity + 低 inference FLOPs DeepSeek V3 出圈，证明了这条路在大模型上有效 之后几乎所有旗舰开源模型都给了 MoE 版本 基本对比图 Figure 5: An illustration of the MoE module in DeepSeek V3/R1 (right) compared to a standard FeedForward block (left).\n设计维度 维度 选项 典型例子 专家数 少量大 / 大量小 Grok 2.5 8 个 vs DeepSeek 256 个 是否 shared expert 有 / 无 DeepSeek V3 有，Qwen3 无，Qwen3-Next 又加回 放置 每层 / 隔层 DeepSeek V3 每层（除前 3），Llama 4 隔层 Expert placement latent space / 原 dim Nemotron 3 Super 用 latent experts Load balancing 各种 Arcee Trinity 提出新策略 Shared Expert 的争议 Qwen3 砍了 shared expert：Junyang Lin 公开说”没发现明显收益，且担心对推理优化的影响”。\nDeepSeek V3 / R1 保留：源于 DeepSpeedMoE 论文，证明能提升建模性能（因为通用模式被 shared 专家吸收，其他专家腾出 capacity 学更专门的东西）。\nQwen3-Next 又加了回来：作者之前在视频里把”高专家数 + 共享专家”列为未来方向。\nGrok 2.5：用一个 SwiGLU 模块当”类 shared expert”（intermediate dim 翻倍，不是经典方案）。\nMistral 3 Large、Nemotron 3 Nano、MiniMax M2、Xiaomi MiMo：shared expert 情况各异。\n少而大 vs 多而小 风格 代表 论点 少而大专家 Grok 2.5（8 个）、Llama 4 Maverick（2 个大） 老派做法；推理调度简单 多而小专家 DeepSeek V3（256 个）、Qwen3（128 个） DeepSeekMoE 论文证明在总参数相同时多小专家更好 Arcee Trinity：coarser 多专家 介于两者之间 利于推理吞吐 综述图 Figure 6: From “DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models” — the shared expert idea.\nFigure 28: From the DeepSeekMoE paper, illustrating the small-experts trend.\n相关笔记 本仓库已有基础 MoE 笔记：[[MoE]] [[DeepSeek V3 系列]] [[Llama 4]] [[Qwen3 系列]] [[Grok 2.5]] [[Nemotron 3]] 来源 The Big LLM Architecture Comparison, 全文 23 节 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/moemixture-of-experts/","title":"MoE（Mixture of Experts）"},{"content":"MTP（Multi-Token Prediction） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。一个训练阶段的多 token 预测技巧。\n一句话 传统 LLM 每个位置只预测下一个 token（next-token prediction）。MTP 在每个位置加几个小的 head（线性层），同时输出 t+1..t+k 的 logits，对所有偏移的 cross-entropy 求和（论文推荐 k=4）。\n训练 vs 推理 阶段 行为 训练 主 loss + MTP extra loss → 训练信号更丰富，训练速度↑ 推理（普通 MTP） 仍按 next-token 逐步生成 推理（speculative MTP） MTP head 作为 draft model 一次提议 k 个 token，主模型验证 → 推理加速 与 Speculative Decoding 的关系 MTP head 天然适合做 speculative decoding 的”内部 draft model”——不需要单独训一个 draft 模型。\n谁在用 DeepSeek V3 \u0026amp; V3.2：训练用 GLM-4.5 / GLM-5：训练用 Qwen3-Next：训练用，且专门针对多步推理优化 MiniMax M2：训练用 Xiaomi MiMo：训练用 Nemotron 3 Super：训练 + 推理都用（共享权重 MTP head 作为 native speculative decoder） 对比图 Figure 51.2: Multi-Token Prediction versus regular next token prediction.\n相关笔记 [[DeepSeek V3 系列]] [[GLM 系列]] [[Qwen3 系列]] [[MiniMax M2]] [[Xiaomi MiMo]] [[Nemotron 3]] 来源 The Big LLM Architecture Comparison, section 12.3, 18.2 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/mtpmulti-token-prediction/","title":"MTP（Multi-Token Prediction）"},{"content":"Nemotron 3（Nano / Super） 原文第 18 节。NVIDIA 在 2025-12 发布的 Nemotron 3 系列，开源权重 + 技术报告 + 数据集 + 训练代码（与 Olmo 3 透明度相当）。\n18.1 Nemotron 3 Nano（30B-A3B） 架构总览 Nemotron 3 Nano 是 Mamba-Transformer 混合 + MoE：\n52 层（13 macro blocks × Mamba-2 + MoE sub-blocks + 少量 GQA 层） MoE：128 专家，每 token 激活 1 shared + 6 routed 几乎不用 self-attention，只在少量层用 比 Qwen3-Next / Kimi Linear 更激进——attention 层比例极少。\nFigure 51.1: Outline of the Nemotron 3 Nano model, which is a Transformer-Mamba hybrid.\nMamba-2 是什么 概念上类似 [[Gated DeltaNet]] / [[KDA（Kimi Delta Attention）|KDA]]：\n维护一个 running hidden state 用 learned gates 把新输入混进 state 线性 O(n) 缩放（不像 attention O(n²)） “Beyond Standard LLMs” 是作者的另一篇专门讲 SSM 的文章。\n性能 “really good performance compared to pure transformer architectures of similar size, while achieving much higher tokens-per-second throughput”\n18.2 Nemotron 3 Super（120B，2026-03-11 发布） Super 版在 Nano 基础上做了两个主要改动：\n改动 1：[[MTP（Multi-Token Prediction）|MTP]] MTP 训练时同时预测多个未来 token：\n训练信号更丰富 → 质量 + 推理效率都受益 与一般 MTP 不同：Nemotron 3 Super 在推理时也用 MTP 共享权重的 MTP head 充当”内部 draft model”，做 native speculative decoding 不需要外部 draft model 作者建议改叫”shared-weight MTP for speculative decoding”，而非”MTP-3”。\nFigure 51.2: Multi-Token Prediction versus regular next token prediction.\n改动 2：Latent Experts 专家在 latent space 操作：\n输入 4096 维先 down-project 到 1024 专家在 1024 维上工作 输出 up-project 回 4096 Figure 51.3: Nemotron 3 Super 120B-A12B with latent MoE layers, multi-token prediction, and the Mamba-2 hybrid attention approach.\n性能 2× faster than Qwen3.5 122B-A10B NVFP4 版本 2.2× faster than GPT-OSS 120B benchmark 与 Qwen3.5 122B-A10B、GPT-OSS 120B 同档 Figure 51.4: Nemotron 3 Super benchmark comparison from the Hugging Face Hub page.\n相关笔记 [[MoE（Mixture of Experts）]] [[MTP（Multi-Token Prediction）]] [[Gated DeltaNet]] —— Mamba-2 的近亲 [[Qwen3 系列]] Qwen3-Next —— 同样走 linear attention 路线 来源 原文 section 18 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/nemotron-3/","title":"Nemotron 3（Nano / Super）"},{"content":"NoPE（No Positional Embedding） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 NoPE = 完全不加位置编码。没有绝对位置 embedding，也没有 RoPE。起源是 2023 年的 The Impact of Positional Encoding on Length Generalization in Transformers 论文。\n怎么 work 因果 mask 已经天然编码方向：位置 t 只能 attend 到 ≤ t 模型通过 gradient descent 可以学到位置信息，如果对 loss 有利 NoPE 论文额外发现：长度泛化能力更好——context 变长时性能下降更少 局限 原论文实验用 ~100M 参数小模型 + 小 context 大模型 + 长 context 下效果如何，不清楚 所以主流 LLM 都”谨慎”使用 NoPE——只在部分层用，或只在 global 层用 谁在用 模型 用法 SmolLM3 每 4 层用 NoPE，其余仍用 RoPE Arcee Trinity Large global attention 层用 NoPE Kimi Linear 在 MLA（global）层用 NoPE，让 MLA 跑成纯 MQA 对比图 Figure 22: Absolute positional embeddings (from the “Build a Large Language Model (From Scratch)” book).\nFigure 23: Annotated figure from the NoPE paper showing better length generalization with NoPE.\n相关笔记 [[Partial RoPE]] —— 另一种”减少 RoPE 强度”的方案 [[SmolLM3]] [[Arcee Trinity Large]] [[Kimi 系列]] Kimi Linear 来源 The Big LLM Architecture Comparison, section 7.1 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/nopeno-positional-embedding/","title":"NoPE（No Positional Embedding）"},{"content":"OLMo 2 \u0026amp; Olmo 3 Thinking 原文第 2、15 节。OLMo 系列是 Allen Institute for AI 发布的”全透明”开源模型——训练数据 / 训练代码 / 训练报告全公开。在 benchmark 上不算最顶尖，但是 LLM 复现的”模板”。\nOLMo 2 核心：Post-Norm + QK-Norm OLMo 2 整体沿用 GPT 骨架（仍是 MHA，不用 GQA / MLA），但有两点与主流不同的设计：\n2.1 [[Post-Norm vs Pre-Norm|Post-Norm 风格]] OLMo 2 的 RMSNorm 放在 attention / FFN 之后（Post-Norm），而不是主流的 Pre-Norm 位置。Norm 仍然在残差连接内部。\n原始 Transformer（2017）：Post-LN（norm 在残差外） GPT-2 之后大多数 LLM：Pre-Norm（norm 在 attention / FFN 前） OLMo 2：Post-Norm（norm 在 attention / FFN 后，仍在残差内） 这种放置是 OLMo 2 团队在实验中发现的稳定训练的手段。\nFigure 8: A comparison of Post-Norm, Pre-Norm, and OLMo 2’s flavor of Post-Norm.\n2.2 [[QK-Norm]] QK-Norm = 另一个 RMSNorm 层，放在 attention 模块内，对 q、k 应用 RoPE 之前做归一化。\n起源：2023 年的 Scaling Vision Transformers 论文 效果：与 Post-Norm 一起，显著稳定训练 后续广泛采用：Gemma 2/3、MiniMax M2 等 class GroupedQueryAttention(nn.Module): def __init__(self, d_in, num_heads, num_kv_groups, head_dim=None, qk_norm=False, ...): if qk_norm: self.q_norm = RMSNorm(head_dim, eps=1e-6) self.k_norm = RMSNorm(head_dim, eps=1e-6) ... def forward(self, x, mask, cos, sin): queries = self.W_query(x) keys = self.W_key(x) values = self.W_value(x) if self.q_norm: queries = self.q_norm(queries) if self.k_norm: keys = self.k_norm(keys) # apply RoPE queries = apply_rope(queries, cos, sin) keys = apply_rope(keys, cos, sin) ... 2.3 小结 Figure 9: A plot showing the training stability for Pre-Norm vs OLMo 2’s flavor of Post-Norm.\nFigure 10: An architecture comparison between Llama 3 and OLMo 2.\n注意：Figure 9 同时混入了 QK-Norm 的影响，所以 Post-Norm 单独贡献多少并不容易看出。\n3 个月后发布的 32B OLMo 2 改用了 GQA。\nOlmo 3 Thinking（2025-11-20 发布） Allen AI 在 2025 年 11 月发布了 Olmo 3 7B 和 32B（注意官方拼写改了，从 OLMo → Olmo），并加了 reasoning 变体。\n关键设计 继承 Post-Norm（沿用 OLMo 2 的经验，验证稳定性） 7B 仍用 MHA（与 OLMo 2 一致），但加了滑动窗口注意力来缩 KV cache（类似 Gemma 3） 32B 改用 GQA（与 Llama 3 / Qwen3 一致） YaRN 用于 context extension 到 64k（Qwen3 是可选 32k → 131k） 32B 架构缩放与 Qwen3 32B 比例相近（intermediate 从 5x 提到 5.4x） Figure 44: Olmo 3 7B and Qwen3 8B side by side.\nFigure 45: Olmo 3 32B and Qwen3 32B side by side.\n训练与代码 Olmo 3 提供了 from-scratch 实现 notebook，是学习 LLM 架构的好资源。\n相关笔记 [[QK-Norm]] [[Post-Norm vs Pre-Norm]] [[滑动窗口注意力（Sliding Window Attention）]] [[Qwen3 系列]] 来源 原文 section 2 \u0026amp; 15 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/olmo-2/","title":"OLMo 2 \u0026 Olmo 3"},{"content":"Partial RoPE 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 Partial RoPE = 只对每个 head 的部分通道应用 RoPE 旋转，剩下通道不变。\nFull RoPE: [r r r r r r r r] Partial RoPE: [r r r r — — — —] 每个 head 的 rotary_dim 通道做旋转 剩余 (head_dim - rotary_dim) 通道保持不变 起源与目的 M1 README 原文：“Rotary Position Embedding (RoPE) applied to half of the attention head dimension with a base frequency of 10,000,000”\nM1 论文：“implementing RoPE on half of the softmax attention dimensions enables length extrapolation without performance degradation.”\n作者猜测的 rationale：长序列（特别是比训练数据最长还长的）下，“完全不旋转” 比 “训练时没见过的剧烈旋转” 更安全。\n谁在用 MiniMax M1（首创） MiniMax M2 Gemma 4：p-RoPE，只对 25% 的频率对加位置信息（思想类似） 与 [[NoPE（No Positional Embedding）|NoPE]] 的对比 NoPE：完全不加位置信息 Partial RoPE：只对一部分通道加 两者都旨在”减少位置信息强度”，让长上下文更稳 相关笔记 [[NoPE（No Positional Embedding）]] [[MiniMax M2]] [[Gemma 系列]] Gemma 4 [[Qwen3 系列]] Qwen3-Next —— 也用 partial RoPE 来源 The Big LLM Architecture Comparison, section 13.3 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/partial-rope/","title":"Partial RoPE"},{"content":"Post-Norm vs Pre-Norm 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。汇总 2025 年 LLM 中 normalization 放置的各种设计。\n一句话 Normalization 放在 attention / FFN 之前还是之后，对训练稳定性影响很大。2025 年的 LLM 至少分 4 派。\n四种主要放置 Post-LN（原始 Transformer 2017）：norm 在残差外 Pre-Norm（GPT-2 起主流）：norm 在 attention / FFN 前 Post-Norm 在残差内（OLMo 2 风格）：norm 在 attention / FFN 后，但仍在残差内 Pre + Post 都有（Gemma 系列风格）：norm 在 attention / FFN 前后都有 对比图 Figure 8: A comparison of Post-Norm, Pre-Norm, and OLMo 2’s flavor of Post-Norm.\n各家选择 模型 放置 原始 Transformer Post-LN（norm 在残差外） GPT-2 / 大多数 LLM Pre-Norm OLMo 2 Post-Norm（在残差内） Gemma 2 / 3 / 4 Pre + Post 都有 Arcee Trinity Large Pre + Post + Depth-Scaled Sandwich（4 个 norm） 训练稳定性证据 2020 年 Xiong et al. 证明 Pre-LN 在初始化时梯度更 well-behaved Pre-LN 不需要 learning rate warm-up OLMo 2 团队的消融显示 Post-Norm + [[QK-Norm]] 组合能稳定 loss Figure 9: A plot showing the training stability for Pre-Norm vs OLMo 2’s flavor of Post-Norm.\nDepth-Scaled Sandwich Norm（Arcee Trinity） Arcee Trinity 4 个 RMSNorm，并且第 2 个 RMSNorm 的 gain 初始化为约 1/√L（L = 总层数）：\n训练早期残差更新小 随训练学到合适 scale 长大 “看起来像 Gemma 3 sandwich norm，但加了深度缩放” 相关笔记 [[QK-Norm]] —— 通常与 Post-Norm 一起出现 [[OLMo 2]] [[Gemma 系列]] [[Arcee Trinity Large]] 来源 The Big LLM Architecture Comparison, section 2.1, 3.2, 15, 20 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/post-norm-vs-pre-norm/","title":"Post-Norm vs Pre-Norm"},{"content":"QK-Norm 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 QK-Norm = 在 attention 模块内，对 q、k 各自加一层 RMSNorm，在 RoPE 之前 应用。起源是 2023 年的 Scaling Vision Transformers 论文。\n代码示例 class GroupedQueryAttention(nn.Module): def __init__(self, d_in, num_heads, num_kv_groups, head_dim=None, qk_norm=False, dtype=None): # ... if qk_norm: self.q_norm = RMSNorm(head_dim, eps=1e-6) self.k_norm = RMSNorm(head_dim, eps=1e-6) else: self.q_norm = self.k_norm = None def forward(self, x, mask, cos, sin): b, num_tokens, _ = x.shape queries = self.W_query(x) keys = self.W_key(x) values = self.W_value(x) # ... if self.q_norm: queries = self.q_norm(queries) if self.k_norm: keys = self.k_norm(keys) # apply RoPE queries = apply_rope(queries, cos, sin) keys = apply_rope(keys, cos, sin) # ... 作用 与 [[Post-Norm vs Pre-Norm|Post-Norm]] 一起 → 稳定训练 QK 的尺度被显式控制 → 防止 attention logits 爆炸 谁在用 OLMo 2（首次出现在主流 LLM） Gemma 2 / Gemma 3 MiniMax M2（Per-Layer QK-Norm——见下） Arcee Trinity Large GLM-4.5 Per-Layer QK-Norm（M2 变体） 普通 QK-Norm 是一组 q_norm / k_norm 在每层共享。M2 的实现更”细”：\nself.q_norm = MiniMaxText01RMSNormTP(self.head_dim * self.total_num_heads, eps=...) self.k_norm = MiniMaxText01RMSNormTP(self.head_dim * self.total_num_kv_heads, eps=...) hidden_size = num_heads × head_dim scale 向量对每个 head 的每个 head_dim 都有独立参数——不是 head 间共享 相关笔记 [[Post-Norm vs Pre-Norm]] [[OLMo 2]] [[Gemma 系列]] [[MiniMax M2]] [[Arcee Trinity Large]] 来源 The Big LLM Architecture Comparison, section 2.2 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/qk-norm/","title":"QK-Norm"},{"content":"Qwen3 系列 原文第 6、12 节。Qwen3 是 2025 年开源模型里”六边形战士”，3 个分支（dense、MoE、Next）覆盖了从 0.6B 到 80B 几乎所有 size 区间。\n6.1 Qwen3 Dense Qwen3 dense 有 0.6B / 1.7B / 4B / 8B / 14B / 32B。0.6B 可能是目前最小的”当代”开源 LLM，本地训练 / 推理都极其方便。\n与 Llama 3 1B 的对比 维度 Qwen3 0.6B Llama 3 1B 设计哲学 深而窄 浅而宽 层数 较多 较少 Hidden / Heads 较小 较大 Tokens/sec 较慢（深度并行度低） 较快 Figure 18: An architecture comparison between Qwen3 0.6B and Llama 3 1B; notice that Qwen3 is a deeper architecture with more layers, whereas Llama 3 is a wider architecture with more attention heads.\n6.2 Qwen3 MoE Qwen3 MoE 有 30B-A3B 和 235B-A22B 两种。\ndense 适合：fine-tuning、部署、跨硬件优化 MoE 适合：固定推理预算下的高 capacity 同时提供两种 = 满足不同 use case 与 DeepSeek V3 的对比（Qwen3 235B-A22B） Qwen3 235B-A22B 与 DeepSeek V3 惊人相似，唯一显著区别是 Qwen3 移除了 shared expert。\nJunyang Lin（Qwen3 开发成员）回复：当时没发现 shared expert 带来足够收益，且担心对推理优化的影响——没有”标准答案”。\nFigure 19: An architecture comparison between DeepSeek V3 and Qwen3 235B-A22B.\n12 Qwen3-Next（2025-09-11） 80B-A3B，Instruct + Thinking 两种变体。比 235B-A22B 小 3×，但专家数 4×，并重新引入了 shared expert（回应作者之前的”未来方向”预言）。\n12.1 专家数 vs 专家大小 高专家数 + 共享专家 = 论文中证明对训练稳定性 + 最终质量有益 这次 Qwen3 把这条路走到底 12.2 [[Gated DeltaNet]] + Gated Attention 混合 Qwen3-Next 的最大亮点：用 Gated DeltaNet（3 层）+ Gated Attention（1 层） 混合 3:1 比例，替代纯 attention。\n让 262k context 的 memory cost 大幅下降 DeltaNet = 线性 attention 变体，RNN 风格的 fast-weight delta rule Gated Attention = 标准 GQA + 输出 gate + zero-centered RMSNorm for QK + partial RoPE 1 层 full attention 保留：弥补 DeltaNet 精度不足（content-based retrieval 不如 full attention） Figure 35: The original Qwen3 (left) next to the Qwen3 Next (right).\nFigure 36: The Gated DeltaNet + Gated Attention hybrid mechanism. Note that these are arranged in a 3:1 ratio.\n12.3 [[MTP（Multi-Token Prediction）|MTP]] 每个位置预测 t+1..t+k 个 token（论文推荐 k=4）：\n训练时：额外的 cross-entropy signal，加速训练 推理时：extra heads 可用于 speculative decoding Qwen3-Next 的 MTP 还针对多步推理做了优化，spec decode 接受率更高 V3、V3.2、GLM-4.5、MiniMax M2 训练时也用 MTP（作者没在架构对比里展开讲）。\n12.4 Qwen3-Coder-Next（2026-02） 80B、3B 激活。SWE-Bench Pro 接近 Claude-Sonnet-4.5。\n架构与 Qwen3-Next 80B 完全一致——直接以 Qwen3-Next 为 base 继续训练。\n相关笔记 [[Gated DeltaNet]] [[MTP（Multi-Token Prediction）]] [[MoE（Mixture of Experts）]] [[Kimi 系列]] Kimi Linear —— 同样 3:1 混合 [[MiniMax M2]] —— 同样用 MTP 来源 原文 section 6 \u0026amp; 12 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/qwen3-%E7%B3%BB%E5%88%97/","title":"Qwen3 系列（Qwen3 / Qwen3-Next / Coder-Next）"},{"content":"SmolLM3 原文第 7 节。HuggingFace 的 3B 模型，介于 Qwen3 1.7B 和 4B 之间。\n总体定位 3B 参数 size：兼顾能力与本地可运行性 训练透明度：与 OLMo 类似，公开了大量训练细节 性能：在与 Qwen3 1.7B / 4B、Llama 3 3B、Gemma 3 4B 的对比中处于前列 Figure 20: Win rate of SmolLM3 vs Qwen3 1.7B/4B, Llama 3 3B, Gemma 3 4B.\n关键设计：每 4 层用 [[NoPE（No Positional Embedding）|NoPE]] SmolLM3 整体架构”看起来”很标准。唯一有意思的点是 NoPE（无位置编码）——只在每 4 层中”用” NoPE（即省略 RoPE）。\nNoPE 是什么 传统的 LLM 必须注入位置信息，因为 self-attention 把 token 视为无序集合。两种主流方案：\n绝对位置编码（早期 GPT）：加一层 embedding，把位置信息加到 token embedding 上 RoPE（当前主流）：旋转 Q / K 向量，编码相对位置 NoPE = 什么位置信息都不加。起源于 2023 年 The Impact of Positional Encoding on Length Generalization in Transformers 论文。\n为什么 NoPE 也能工作？ 因果 mask 天然编码了方向：位置 t 只能看到 ≤ t 的 token 模型通过 gradient descent 可以学到”位置”信息，如果对 loss 有利的话 NoPE 论文的额外发现：长度泛化能力更好 Figure 21: A side-by-side architecture comparison between Qwen3 4B and SmolLM3 3B.\nFigure 22: Absolute positional embeddings (from the “Build a Large Language Model (From Scratch)” book).\nFigure 23: Annotated figure from the NoPE paper showing better length generalization with NoPE.\n论文实验用的是 ~100M 参数的小模型，小 context；SmolLM3 团队显然认为不能完全照搬，所以只在每 4 层用 NoPE，其余仍用 RoPE。\n相关笔记 [[NoPE（No Positional Embedding）]] [[Arcee Trinity Large]] —— 在 global 层用 NoPE [[Kimi 系列]] Kimi Linear —— 在 MLA 层用 NoPE 来源 原文 section 7 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/smollm3/","title":"SmolLM3"},{"content":"Xiaomi MiMo-V2-Flash 原文第 19 节。小米 2025-12 发布的 309B MoE（15B 激活），benchmark 接近 DeepSeek V3.2，参数只有一半，推理更快。\n关键设计 极激进滑动窗口 5:1 SWA + global attention（与 Gemma 3 同款比例） 滑动窗口大小 = 128 —— 比 Gemma 3 的 1024 还小 8× 作者说这是已知的”最大 SWA 模型” Figure 52: Xiaomi MiMo-V2-Flash compared to DeepSeek V3.2.\n训练时用 [[MTP（Multi-Token Prediction）|MTP]] 与 DeepSeek V3、V3.2、GLM-4.5、MiniMax M2 类似，训练时启用 MTP 提供额外 signal。\n相关笔记 [[滑动窗口注意力（Sliding Window Attention）]] [[MoE（Mixture of Experts）]] [[MTP（Multi-Token Prediction）]] [[Gemma 系列]] Gemma 3 —— 同样 5:1 SWA [[DeepSeek V3 系列]] —— benchmark 对照 来源 原文 section 19 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/xiaomi-mimo/","title":"Xiaomi MiMo-V2-Flash"},{"content":"滑动窗口注意力（Sliding Window Attention, SWA） 来自 Sebastian Raschka 的 The Big LLM Architecture Comparison。\n一句话 把 attention 从”每个 token 都能 attend 所有 token”变成”每个 token 只能 attend 邻近 window 内的 token”——KV cache 内存大幅下降。\n起源 2020 年 LongFormer 论文首次提出。Gemma 2 是首个在主流开源 LLM 中使用 SWA 的。\n与 Global Attention 配合 实际使用是”局部 + 全局”混合：每隔若干层保留一层 full attention，让全局信息仍能流动。\n谁在用 + 配置 模型 local:global 比例 窗口大小 Gemma 2 1:1 4096 Gemma 3 5:1 1024 Gemma 4 5:1 1024（global 层 V = K） GPT-OSS 1:1 — Olmo 3 7B 5:1（参考 Gemma 3） — Arcee Trinity Large 3:1 4096 Xiaomi MiMo 5:1 128（极激进） Mistral Small 3.1 不使用 — MiniMax M2 默认关闭 — 效果 KV cache 内存大幅下降 对 perplexity 几乎无影响（Gemma 3 论文消融） 推断：SWA 减少 memory 但不减少 latency（所以 Mistral Small 3.1 放弃） 对比图 Figure 11: Annotated figure from Gemma 3 paper showing the KV cache memory savings via sliding window attention.\nFigure 12: A comparison between regular attention (left) and sliding window attention (right).\nFigure 13: Sliding window attention has little to no impact on perplexity (Gemma 3 paper).\n相关笔记 [[Gemma 系列]] [[GPT-OSS]] [[OLMo 2]] Olmo 3 [[Arcee Trinity Large]] [[Xiaomi MiMo]] [[Mistral 系列]] 来源 The Big LLM Architecture Comparison, section 3.1 链接：https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison\n","date":"2026-06-06T00:00:00Z","permalink":"/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%B3%A8%E6%84%8F%E5%8A%9Bsliding-window-attention/","title":"滑动窗口注意力（Sliding Window Attention）"},{"content":" 来源：Qwen2.5-3.7系列最全总结~（作者：自动学习机 / 丁师兄，知乎原文 https://zhuanlan.zhihu.com/p/2045665300545262204）\n注：Qwen 3.5 之后没有 technical report，只发了 blog，本文很多地方只能基于假设与论文缺省。\n下文按”架构 → 预训练 → 后训练”的主线，整理从 Qwen2.5 到 Qwen3.7 的演进。\n01 Qwen2.5 架构 延续 Qwen2 的 Transformer 解码器，关键组件：\nGQA（Grouped Query Attention）：高效利用 KV Cache SwiGLU 激活函数 RoPE 旋转位置编码 QKV 偏置（Qwen3 中被移除） RMSNorm 预归一化 MoE 模型把标准 FFN 替换为 MoE 层（多个 FFN 专家 + top-K 路由）。Tokenizer 沿用 Qwen tokenizer（BBPE），常规 token 词表 151,643，控制 token 由 3 扩展到 22（含 2 个工具 token）。\n预训练 数据：18T tokens。扩充了 Qwen2.5-Math / Qwen2.5-Coder 数据；用 Qwen2-72B-Instruct、Qwen2-Math-72B-Instruct 合成数学、编程、知识数据；用 Qwen2-Instruct 给数据打领域标签，电商/社交/娱乐欠采样，技术/科学/学术过采样；用专有 RM + Qwen2-Math-RM-72B 严格过滤。 Scaling Law：得到随模型规模 N 和数据量 D 变化的最优学习率 μ、最优 batch size B_opt 的规律；并用于预测 MoE vs Dense 的性能差异，给 MoE 超参提供指导。 两阶段预训练：先用 4K context 训练，再扩到长 context。除 Qwen2.5-Turbo 外，最终阶段把 context 从 4K 扩到 32K，ABF 把 RoPE 基频从 10,000 提到 1,000,000。Qwen2.5-Turbo 走渐进式 32K→64K→128K→256K，RoPE 基频 10,000,000，每阶段 40% 当前最大长度 + 60% 短序列。 推理加长：YARN + 双块注意力（DCA）让序列长度处理能力提升 4 倍。 去污染：用 n-gram 匹配排除测试集污染。规则：若测试序列 \\(s_e\\) 与训练序列 \\(s_t\\) 的 \\(\\text{LCS}\\) 长度 \\(|LCS(s_t, s_e)| \\geq 13\\) 且 \\(\\geq 0.6 \\times \\min(|s_t|, |s_e|)\\)，则把 \\(s_t\\) 从训练集中剔除。 后训练 数据：数百万高质量示例（长序列、数学、编码、指令跟随、结构化数据理解、逻辑推理、跨语言迁移、系统指令等）。用专门评论模型 + 多智能体协作评分系统筛选。\nSFT：100 万+ 示例，32K 序列上两轮微调，lr 从 \\(7\\times 10^{-6}\\) 降到 \\(7\\times 10^{-7}\\)，配正则化和梯度裁剪。两阶段：阶段一仅短指令（≤32K），与其它 Qwen2.5 模型一致；阶段二短指令 + 长指令（≤256K）混合，提升长上下文指令跟随能力。\nRL：两阶段 RL。\n离线 RL：DPO，150,000 训练对，1 轮训练，lr \\(7\\times 10^{-7}\\)，在线合并优化器；面向数学、编程、指令跟随、逻辑推理等”难评”任务。 在线 RL：GRPO。查询来自开源集 + 专有挑战集，响应在 Qwen 多检查点 + 不同温度下采样，偏好对由人工 + 自动标注混合。按响应分数方差排序处理查询（方差大优先）。每查询采样 8 次，全局 batch 2048，每回合 2048 样本。 评估：自动化为主，最小化人工干预，公开基准 + 内部技能数据集。\n02 Qwen3 架构 密集模型在 Qwen2.5 基础上：\n保留 GQA / SwiGLU / RoPE / RMSNorm + 预归一化 移除 QKV 偏置 引入 QK-Norm 稳定训练 MoE：细粒度专家分割，128 个专家，每个 token 激活 8 个；去掉了共享专家（与 Qwen2.5-MoE 不同）；用全局 batch 负载均衡损失促进专家专业化。Tokenizer 词表 151,669。\n预训练 数据规模翻倍（相对 Qwen2.5），语言覆盖 ×3。36T tokens / 119 种语言和方言。高质量长文合成：Qwen2.5-VL 识别 PDF → Qwen2.5 精炼；用 Qwen2.5 / 2.5-Math / 2.5-Coder 合成教科书、QA、指令、代码。多语言标注系统给 30T+ tokens 打”教育价值、学科、领域、安全性”等标注。 三阶段预训练： S1 通用：\u0026gt;30T tokens，4K 序列，覆盖 119 种语言 S2 推理：提 STEM / 编程 / 推理 / 合成数据比例，~5T 高质量 tokens，4K 序列，加速 lr 衰减 S3 长上下文：数百 B tokens，32K 序列；75% 长度 16K-32K，25% 长度 4K-16K；ABF 把 RoPE 基频从 10,000 提到 1,000,000；YARN + DCA 让推理阶段长度处理能力 ×4 Scaling Law：贯穿三阶段，给每个 dense / MoE 模型预测最优 lr 和 batch size 策略。 后训练 后训练流程围绕两个核心目标： 1. 思维控制：整合”非思考”和”思考”两种模式，让用户能灵活选是否推理，并通过 token 预算控制推理深度。 2. 强到弱蒸馏：优化轻量级模型后训练流程。\n整体后训练 pipeline 如下：\nQwen3 后训练 pipeline Long-CoT 冷启动 数据：数学 / 编程 / 逻辑推理 / 一般 STEM，每个问题配参考答案或代码测试用例。 两阶段过滤： 查询过滤：Qwen2.5-72B-Instruct 识别并删除”难验证”的查询（多子问题、要求一般文本生成、模型不用 CoT 就能答对的）。 响应过滤：用 QwQ-32B 对每个查询生成 N 个候选，QwQ-32B 持续做不对的由人工评估。 目标：建立基础推理模式，不过分强调即时推理性能。 推理强化学习 筛选 3,995 对”查询-验证器”样本：未在冷启动出现、冷启动模型能学、有挑战性、覆盖广。\n用 GRPO，要点： - 大 batch size + 多 rollout - 离线策略训练提样本效率 - 控制熵的稳定增长/保持稳定来平衡探索与利用\n思考模式融合 聊天模板：对思考/非思考样本，在用户查询或系统消息中分别引入 /think 和 /no think token。两种模式的模板示意：\nThinking Mode / Non-Thinking Mode 模板 对推理 RL 模型继续做 SFT 思考数据：阶段二模型对阶段一查询做拒绝采样生成 非思考数据：编程、数学、指令遵循、多语言、创意写作、QA、角色扮演等；用自动 checklist 评估质量，并提高翻译任务比例以增强低资源语言 思考预算控制（Thinking Budget） 当 \u0026lt;think\u0026gt; 长度达到用户设定的上限时，手动终止思考过程并插入：\n“由于用户设定的时间有限，我现在需要基于当前的思考内容直接给出解答。”\n模型基于截至该点的累积推理继续生成响应。该能力并非显式训练获得，而是思考模式融合的自然产物。\n通用 RL 奖励系统覆盖 20+ 任务，每种任务有定制评分标准，提升：指令遵循、格式遵循（响应 /think//no think、区分思考与响应的 token）、偏好对齐、Agent 能力、特定场景能力。\n三种奖励：基于规则 / 基于参考答案（Qwen2.5-72B-Instruct 评分）/ 无参考答案（人类偏好数据训练的 RM 打分）。\n强到弱知识蒸馏 两个阶段：\nOff-policy Distillation：教师模型在 /think 和 /no think 模式下分别生成响应，组合后作为蒸馏数据，目标是让轻量学生模型掌握基础推理能力 + 学会在两种模式间切换。 On-policy Distillation：学生模型在 /think 或 /no think 模式下生成响应，将学生 logits 与教师模型（Qwen3-32B 或 Qwen3-235B-A22B）logits 对齐，计算 KL 散度并微调。 有趣的结论 思考预算有效性：随思考预算增加，Qwen3 表现平稳、可扩展地提升：\nThinking Budget 效果 在线策略蒸馏有效性：知识蒸馏性能显著优于 RL，且仅需约 1/10 GPU 计算时间就让小模型超过 RL 训练的自己。\nMethod AIME’24 AIME’25 MATH500 LiveCodeBench v5 MMLU-Redux GPQA-Diamond GPU Hours Off-policy Distillation 55.0 (90.0) 42.8 (83.3) 92.4 42.0 86.4 55.6 - + Reinforcement Learning 67.6 (90.0) 55.5 (83.3) 94.8 52.9 86.9 61.3 17,920 + On-policy Distillation 74.4 (93.3) 65.5 (86.7) 97.0 60.3 88.3 63.3 1,800 蒸馏 vs RL 对比 思考模式融合 + 通用 RL 的副作用：知识 / STEM / 数学 / 编程任务没有显著改进；反而在 AIME’24、LiveCodeBench 等挑战任务上，思考模式性能两个阶段后有所下降。原因：模型被训练应对更广泛的通用任务，削弱了对复杂任务的专业能力——adaptive learning 存在弊端。\n具体退化数据（Stage 2 → Stage 3 → Stage 4）：\nStage 2/3/4 退化 03 Qwen3.5 核心：通过混合注意力架构（Transformer + 线性注意力 / 类 Gated Delta Network）+ MoE 稀疏机制，在降低 ~50% 计算成本的同时实现十万级 token 长上下文 + 高性能推理；原生多模态融合 + 强化 Agent 能力（90+ 分多步任务执行）。\n架构 混合注意力机制：把一部分计算复杂度降到 O(n)，类似 Gated Delta Network。\nQwen3.5 混合注意力 预训练 数据：更大规模、严格过滤的视觉-文本混合语料；加强数学（STEM）、推理和中英文数据 词表：从 15 万扩到 25 万，多数语言编解码效率提升 10%-60%，32K / 256K 解码吞吐分别飙升 8.6 倍 / 19.0 倍 Decode Throughput 对比 原生多模态融合：抛弃”图像/视频先转文本再处理”的旧模式，文本/图像/视频在模型早期直接融合 + 联合训练 后训练与基础设施 异步并行 RL 系统：把”数据生成”与”模型训练”分离。\nQwen3.5 异步并行 RL Qwen-Agent 与环境交互产生经验数据（Rollout）存 Buffer Trainer 同步从队列抓数据学习 Reward Service 实时打分 整体训练效率 提升 3-5 倍 混合精度与显存优化：大量使用 FP8，关键节点保留 BF16，显存占用减少约一半，速度提升 10%+。\n多模态计算重叠：智能调度把不同模态计算重叠，让 GPU 满负荷，多模态训练速度逼近纯文本。\n有趣的结论 真实环境 RL：偏向真实任务环境的 RL 让模型在不同任务上表现均衡，稳定性高。 小模型策略：9B 等中小规模模型在很多任务上已接近甚至超过上一代几十 B / 上百 B 模型。模型能力不再完全依赖参数规模，而越来越依赖架构设计和训练策略。 04 Qwen3.6 核心特性与能力跃升：\n超长上下文：默认支持 100 万 token 上下文窗口 更注重 Agent 能力：智能体编程领域飞跃，能从容应对前端网页开发、复杂代码仓库级问题求解、终端操作与自动化任务 长程规划：在多个高难度长程规划任务中取得最优成绩 原生多模态演进：视觉与视频理解大幅增强，结合推理 / Grounding / OCR 做复杂分析 05 Qwen3.7 优势：更优编程能力、更长程的工具调用与执行能力。\n架构（特指训练基础设施架构） 未提神经网络架构本身，重点是 RL Rollout 环境基础设施：\n解耦设计：把每个训练实例正交解耦为三个独立组件：Task / Harness / Verifier 组合式扩展：同一任务能以极低成本与不同类型/版本的框架、验证器自由重组 赋能跨框架 + 跨验证器的 RL 训练——迫使模型在多变框架配置下处理同源任务，学习泛化性的解题策略，而非依赖特定框架的捷径 预训练 完全没有提数据规模、配比或算力细节。\n后训练与 RL 后训练是 Qwen3.7 重点着墨的板块。\nQwen3.7 后训练 智能体训练环境大幅扩展（在 Qwen3.5 基础上）：训练环境质量与多样性进一步提升，评测基准均为训练时未见过的全新领域 跨框架 + 跨验证器 RL：依靠解耦架构，逼迫模型学通用解题策略，不走框架捷径 对抗”奖励作弊”的自进化体系：在 80+ 小时的 SWE 强化学习中，Qwen3.7-Max 被接入训练监控系统，自主回放轨迹、归纳作弊模式（如去 GitHub 偷看标准答案），自进化出 13 条规则，拦截 1,618 个作弊案例 长程时序复杂度强化：在”动态累积生存博弈框架”下扩展训练任务时序复杂度，让模型在数小时、涉及数千步决策的过程中保持策略一致性，克服长上下文的”记忆腐化”和”指令漂移”（该框架具体定义未公开） 参考 Qwen2.5-3.7系列最全总结~ - 微信公众号 Qwen2.5-3.7系列最全总结 - 知乎 ","date":"2026-06-05T00:00:00Z","permalink":"/qwen/","title":"Qwen2.5-3.7 系列最全总结"},{"content":"nano-vllm 全代码精读笔记 配套参考:博客 从零构建 nano-vllm:深入理解 vLLM 推理引擎(原博客正文需自行访问,本文以源码为主)\n适用对象:想从零理解 vLLM 底层结构的初学者。建议先读完本文,再去看 vLLM 主仓库。\nnano-vllm 是一个约 1200 行的精简 vLLM 实现,覆盖了 vLLM 最核心的工程思想:连续批处理、PagedAttention、Prefix Caching、FlashAttention、Tensor Parallelism、CUDA Graph。本文逐文件、逐函数地把代码讲透,目标是让一个不熟悉 vLLM 的人读完本文后,能直接去看 vLLM 主仓库并快速定位到对应模块。\n0. 怎么读本文 你想了解的内容 跳转章节 整体架构、模块关系 §1 §2 一个请求从进入到返回的完整流程 §3 Sequence(请求对象)怎么工作 §4 Scheduler 怎么调度、怎么抢占 §5 PagedAttention 和 Prefix Caching 怎么实现 §6 ModelRunner 怎么准备输入、怎么跑模型、CUDA Graph §7 Attention 怎么读 KV cache,FlashAttention 怎么用 §8 Qwen3 模型结构、张量并行怎么切 §9 §10 采样、tokenize、权重加载等周边 §11 §12 §13 这些组件和官方 vLLM 的对应关系 §14 推荐阅读顺序 §15 1. 项目定位和文件地图 nano-vllm/ ├── README.md ├── example.py # 最小使用样例 ├── bench.py # 性能基准 ├── nano_vllm_learning_notes.md # 仓库自带的精简笔记 ├── SUMMARY.md └── nanovllm/ ├── __init__.py # 暴露 LLM, SamplingParams ├── llm.py # LLM = LLMEngine(空壳) ├── config.py # Config 数据类 ├── sampling_params.py ├── engine/ │ ├── llm_engine.py # 主循环 │ ├── scheduler.py # 调度器(prefill/decode) │ ├── block_manager.py # Paged KV cache + Prefix cache │ ├── sequence.py # Sequence 请求对象 │ └── model_runner.py # GPU 执行 + TP 进程 + CUDA Graph ├── layers/ │ ├── attention.py # Attention + Triton store_kvcache_kernel │ ├── linear.py # 张量并行的 4 种线性层 │ ├── embed_head.py # VocabParallelEmbedding / ParallelLMHead │ ├── layernorm.py # RMSNorm(支持 fused residual) │ ├── rotary_embedding.py │ ├── activation.py # SiluAndMul │ └── sampler.py # 采样 ├── models/ │ └── qwen3.py # 完整 Qwen3 模型 └── utils/ ├── context.py # 全局 Context(用模块级单例) └── loader.py # safetensors 权重加载 代码体量(粗略):\n模块 行数 主要职责 llm_engine.py ~90 推理主循环 scheduler.py ~70 调度 + 抢占 block_manager.py ~115 分页 + 前缀缓存 sequence.py ~85 序列状态 model_runner.py ~250 TP 进程、KV cache 分配、CUDA Graph attention.py ~80 KV cache 写入 + FlashAttention 调用 linear.py ~175 4 种 TP 线性层 qwen3.py ~215 Qwen3 模型 其他 layers/utils ~250 周边 合计 ~1200 2. 整体架构:四层金字塔 ┌───────────────────────────────────┐ │ LLM / LLMEngine │ 用户 API │ (llm.generate → add_request) │ └─────────────┬─────────────────────┘ │ step() ┌─────────────▼─────────────────────┐ │ Scheduler │ 调度层 │ waiting/running 双队列 │ │ 优先 prefill,否则 decode │ └─────────────┬─────────────────────┘ │ 调 block_manager ┌─────────────▼─────────────────────┐ │ BlockManager │ 内存层 │ 物理 Block + block_table │ │ 引用计数 + xxhash 前缀缓存 │ └─────────────┬─────────────────────┘ │ 调 run ┌─────────────▼─────────────────────┐ │ ModelRunner │ 执行层 │ prepare_prefill / prepare_decode │ │ run_model(CUDA Graph) │ │ Qwen3ForCausalLM → Sampler │ └───────────────────────────────────┘ 核心数据流:\n用户 prompts → LLM.generate(...) → LLMEngine.add_request(prompt, sp) # tokenize + 建 Sequence → Scheduler.add(seq) # 进 waiting 队列 → loop step(): Scheduler.schedule() # 选 batch,返回 (seqs, is_prefill) ModelRunner.run(seqs, is_prefill) # 准备输入、跑模型、采样 Scheduler.postprocess() # append token,完成则 deallocate → outputs = [seq.completion_token_ids for finished seqs] → tokenizer.decode → 文本 3. 端到端流程:从 llm.generate 到出 token 下面用 example.py 里的最小例子走一遍整个调用链,所有 \u0026lt;\u0026lt; 标注的行都是 nano-vllm 中的实际代码位置。\n# example.py llm = LLM(path, enforce_eager=True, tensor_parallel_size=1) sp = SamplingParams(temperature=0.6, max_tokens=256) outputs = llm.generate(prompts, sp) 3.1 初始化阶段(LLMEngine.__init__) \u0026lt;\u0026lt;nanovllm/engine/llm_engine.py:17-34\u0026gt;\u0026gt;\nclass LLMEngine: def __init__(self, model, **kwargs): # 1. 构造 Config config = Config(model, **kwargs) # 2. 如果 TP \u0026gt; 1, 启动子进程 (rank 1..tp_size-1) for i in range(1, config.tensor_parallel_size): event = ctx.Event() p = ctx.Process(target=ModelRunner, args=(config, i, event)) p.start() # 3. 主进程上初始化 rank 0 的 ModelRunner self.model_runner = ModelRunner(config, 0, self.events) # 4. tokenizer self.tokenizer = AutoTokenizer.from_pretrained(config.model, use_fast=True) config.eos = self.tokenizer.eos_token_id # 5. scheduler self.scheduler = Scheduler(config) \u0026lt;\u0026lt;nanovllm/engine/llm_engine.py:4\u0026gt;\u0026gt; 中的 class LLM(LLMEngine): pass 说明 LLM 没有任何附加逻辑,纯粹是为了对齐 vLLM 的 API 命名。\nModelRunner.__init__ 内部会按顺序完成 5 件事(详见 §7):\n初始化分布式环境(dist.init_process_group(\"nccl\", ...))。 加载模型(Qwen3ForCausalLM(hf_config),然后 load_model)。 warmup 一次,统计显存峰值。 动态分配 KV cache(allocate_kv_cache)。 捕获 CUDA Graph(除非 enforce_eager=True)。 3.2 添加请求(LLMEngine.add_request) \u0026lt;\u0026lt;nanovllm/engine/llm_engine.py:42-46\u0026gt;\u0026gt;\ndef add_request(self, prompt, sampling_params): if isinstance(prompt, str): prompt = self.tokenizer.encode(prompt) seq = Sequence(prompt, sampling_params) # 构造 Sequence self.scheduler.add(seq) # 入 waiting 队列 3.3 主循环(LLMEngine.generate + step) \u0026lt;\u0026lt;nanovllm/engine/llm_engine.py:48-93\u0026gt;\u0026gt;\ndef step(self): seqs, is_prefill = self.scheduler.schedule() # ① 调度 token_ids = self.model_runner.call(\u0026quot;run\u0026quot;, seqs, is_prefill) # ② 跑模型 self.scheduler.postprocess(seqs, token_ids) # ③ 收尾 outputs = [(seq.seq_id, seq.completion_token_ids) for seq in seqs if seq.is_finished] return outputs, num_tokens def generate(self, prompts, sampling_params, use_tqdm=True): for prompt, sp in zip(prompts, sampling_params): self.add_request(prompt, sp) while not self.is_finished(): output, num_tokens = self.step() ... 主循环的关键点:\n每个 step 只调度一次,但调度的内容可能全是 prefill、全是 decode、或混合。 is_prefill 是这一批 step 的标志,prefill 批和 decode 批不会在同一步混合(这是 vLLM v0 的设计,nano-vllm 保留了这个限制)。 完成的序列在 step 内就被 outputs 抓出,postprocess 会负责释放它的 KV blocks。 num_tokens 在 prefill 时返回正数,在 decode 时返回负的序列数,用于在 tqdm 中显示吞吐。 4. Sequence:一个请求的”全生命周期” 文件 \u0026lt;\u0026lt;nanovllm/engine/sequence.py\u0026gt;\u0026gt;(85 行)定义了一个请求的所有状态。\nclass SequenceStatus(Enum): WAITING = auto() RUNNING = auto() FINISHED = auto() class Sequence: block_size = 256 counter = count() # 进程内全局递增,用于恢复输出顺序 def __init__(self, token_ids, sampling_params=SamplingParams()): self.seq_id = next(Sequence.counter) self.status = SequenceStatus.WAITING self.token_ids = copy(token_ids) # prompt + 已生成 self.last_token = token_ids[-1] # decode 时只用这个 self.num_tokens = len(self.token_ids) self.num_prompt_tokens = len(token_ids) self.num_cached_tokens = 0 # prefix cache 命中数 self.block_table = [] # 逻辑块 → 物理块 ID self.temperature = sampling_params.temperature self.max_tokens = sampling_params.max_tokens self.ignore_eos = sampling_params.ignore_eos 4.1 状态机 add_request WAITING ─────────────► RUNNING ────────────► FINISHED ▲ │ ▲ │ preempt(seq) │ EOS / max_tokens │ └──────────────────────┘ │ │ │ │ deallocate + appendleft │ └────────────────────────────────────────────┘ 状态变迁的代码位置: - WAITING → RUNNING:\u0026lt;\u0026lt;nanovllm/engine/scheduler.py:36\u0026gt;\u0026gt; seq.status = SequenceStatus.RUNNING - RUNNING → WAITING(抢占):\u0026lt;\u0026lt;nanovllm/engine/scheduler.py:60-63\u0026gt;\u0026gt; preempt() - RUNNING → FINISHED:\u0026lt;\u0026lt;nanovllm/engine/scheduler.py:69\u0026gt;\u0026gt; seq.status = SequenceStatus.FINISHED\n4.2 关键属性 属性 含义 seq_id 进程内单调递增 ID,用于最终按提交顺序输出 token_ids 完整 token 列表(prompt + 已生成) last_token decode 阶段只需要”上一步的输出 token” num_cached_tokens prefix cache 命中的 token 数,prefill 时不重算 block_table 该序列逻辑块 → 物理 KV block 的映射 num_blocks ceil(num_tokens / block_size) last_block_num_tokens 最后一个块已占用的 token 数 num_cached_blocks num_cached_tokens // block_size is_finished status == FINISHED prompt_token_ids / completion_token_ids 切片访问 4.3 block(i) 和 append_token def block(self, i): assert 0 \u0026lt;= i \u0026lt; self.num_blocks return self.token_ids[i*self.block_size: (i+1)*self.block_size] def append_token(self, token_id): self.token_ids.append(token_id) self.last_token = token_id self.num_tokens += 1 block(i) 是 BlockManager 计算 hash 时调用的:它从 token 列表中切出第 i 个逻辑块。Sequence 自己持有完整的 token 列表,BlockManager 不需要单独存 token 副本。\n4.4 __getstate__ / __setstate__:TP 跨进程传输 def __getstate__(self): return (self.num_tokens, self.num_prompt_tokens, self.num_cached_tokens, self.block_table, self.token_ids if self.num_completion_tokens == 0 else self.last_token) def __setstate__(self, state): self.num_tokens, self.num_prompt_tokens, self.num_cached_tokens, self.block_table = state[:-1] if self.num_completion_tokens == 0: self.token_ids = state[-1] else: self.last_token = state[-1] 这一段非常关键,是 vLLM TP 设计的精华:当 tensor_parallel_size \u0026gt; 1 时,主进程通过 pickle 把 Sequence 传到子进程(\u0026lt;\u0026lt;nanovllm/engine/model_runner.py:76-83\u0026gt;\u0026gt;)。已经完成 prefill 的序列不再需要完整 token 列表,只需要 last_token 一个值,这样可以极大减少 IPC 数据量。num_completion_tokens == 0 表示还在 prefill 阶段,要传完整 token_ids;否则只传 last_token。\n5. Scheduler:连续批处理的”调度官” 文件 \u0026lt;\u0026lt;nanovllm/engine/scheduler.py\u0026gt;\u0026gt;(70 行)。\nclass Scheduler: def __init__(self, config): self.max_num_seqs = config.max_num_seqs self.max_num_batched_tokens = config.max_num_batched_tokens self.eos = config.eos self.block_manager = BlockManager(config.num_kvcache_blocks, config.kvcache_block_size) self.waiting: deque[Sequence] = deque() self.running: deque[Sequence] = deque() 5.1 调度策略 def schedule(self) -\u0026gt; tuple[list[Sequence], bool]: # 阶段 1: 优先 prefill scheduled_seqs = [] while self.waiting and num_seqs \u0026lt; self.max_num_seqs: seq = self.waiting[0] if num_batched_tokens + len(seq) \u0026gt; self.max_num_batched_tokens \\ or not self.block_manager.can_allocate(seq): break self.block_manager.allocate(seq) num_batched_tokens += len(seq) - seq.num_cached_tokens seq.status = SequenceStatus.RUNNING self.waiting.popleft() self.running.append(seq) scheduled_seqs.append(seq) if scheduled_seqs: return scheduled_seqs, True # is_prefill=True # 阶段 2: decode while self.running and num_seqs \u0026lt; self.max_num_seqs: seq = self.running.popleft() while not self.block_manager.can_append(seq): if self.running: self.preempt(self.running.pop()) # 抢占别人 else: self.preempt(seq) # 抢占自己 break else: num_seqs += 1 self.block_manager.may_append(seq) scheduled_seqs.append(seq) self.running.extendleft(reversed(scheduled_seqs)) # 保持原顺序 return scheduled_seqs, False # is_prefill=False 关键设计点:\nprefill 优先:有 waiting 请求就优先做 prefill,不让新请求无限等待。 三重闸门:max_num_seqs(并发数)、max_num_batched_tokens(单步 token 预算)、can_allocate(显存闸门),任何一个不满足就停。 prefill token 预算:num_batched_tokens += len(seq) - seq.num_cached_tokens。prefix cache 命中的部分不计入预算,这是 vLLM 节省计算的关键。 decode 阶段抢占:从 running 队列尾部开始 pop 抢占(默认 LIFO 策略,后进先抢占,防止队头长序列一直被抢)。 抢占 = 退回 waiting + 释放所有 KV block:\u0026lt;\u0026lt;nanovllm/engine/scheduler.py:60-63\u0026gt;\u0026gt; def preempt(self, seq): seq.status = SequenceStatus.WAITING self.block_manager.deallocate(seq) # 释放全部 KV block self.waiting.appendleft(seq) # 放回 waiting 队头 为什么 appendleft? 抢占后该序列回到 waiting 队头,下次 schedule 会被优先考虑,降低首 token 时延。\n5.2 Postprocess def postprocess(self, seqs, token_ids): for seq, token_id in zip(seqs, token_ids): seq.append_token(token_id) if (not seq.ignore_eos and token_id == self.eos) \\ or seq.num_completion_tokens == seq.max_tokens: seq.status = SequenceStatus.FINISHED self.block_manager.deallocate(seq) self.running.remove(seq) 两个停止条件(OR): - 生成了 EOS token(且 ignore_eos=False) - 已生成 token 数 == max_tokens\n完成时立即释放 KV block(给其他 decode 序列腾位置)。\n6. BlockManager:PagedAttention + Prefix Caching 文件 \u0026lt;\u0026lt;nanovllm/engine/block_manager.py\u0026gt;\u0026gt;(115 行)。这是理解 vLLM 的”心脏”。\n6.1 Block 和 BlockManager 数据结构 class Block: def __init__(self, block_id): self.block_id = block_id self.ref_count = 0 # 共享计数 self.hash = -1 # -1 表示不在 prefix cache 中 self.token_ids = [] # 冗余存储,用于 hash 命中后校验 def update(self, hash, token_ids): ... def reset(self): # 被 _allocate_block 调用 self.ref_count = 1 self.hash = -1 self.token_ids = [] class BlockManager: def __init__(self, num_blocks, block_size): self.block_size = block_size self.blocks: list[Block] = [Block(i) for i in range(num_blocks)] self.hash_to_block_id: dict[int, int] = dict() # prefix cache 索引 self.free_block_ids: deque[int] = deque(range(num_blocks)) self.used_block_ids: set[int] = set() free_block_ids:空闲 block ID 队列(O(1) 分配)。 hash_to_block_id:前缀缓存的核心,key 是 xxhash,value 是 block_id。 blocks[i]:第 i 个 block 的元数据,实际 KV 数据存在 Attention 模块的 k_cache/v_cache 里(见 §8)。 6.2 哈希链:Prefix Caching 的精髓 @classmethod def compute_hash(cls, token_ids, prefix=-1): h = xxhash.xxh64() if prefix != -1: h.update(prefix.to_bytes(8, \u0026quot;little\u0026quot;)) # 关键: 链入前一块的 hash h.update(np.array(token_ids).tobytes()) # 再 hash 当前块 token return h.intdigest() 为什么必须链入前缀 hash? 看这个反例:\n序列 1: [A][B][C] 序列 2: [X][B][C] 如果不链前缀,两个 [B][C] 的 hash 相同,会被错误复用。但有了前缀 hash,序列 1 的第二个块 hash = hash([B], hash([A])),序列 2 的第二个块 hash = hash([B], hash([X])),完全不同。\n只有完整块才计算 hash:len(token_ids) == self.block_size 时才计算,否则 hash 设为 -1。这意味着最后一个未满块永远不在 prefix cache 中。\n6.3 allocate:分配 + 复用 prefix cache def allocate(self, seq): assert not seq.block_table h = -1 cache_miss = False for i in range(seq.num_blocks): token_ids = seq.block(i) h = self.compute_hash(token_ids, h) if len(token_ids) == self.block_size else -1 block_id = self.hash_to_block_id.get(h, -1) if block_id == -1 or self.blocks[block_id].token_ids != token_ids: cache_miss = True if cache_miss: block_id = self.free_block_ids[0] block = self._allocate_block(block_id) else: seq.num_cached_tokens += self.block_size if block_id in self.used_block_ids: # 已经被别人在用 block = self.blocks[block_id] block.ref_count += 1 else: # 在 prefix cache 但没人用 block = self._allocate_block(block_id) if h != -1: block.update(h, token_ids) self.hash_to_block_id[h] = block_id seq.block_table.append(block_id) 算法精髓:从前往后扫,一旦 cache miss,后续所有块都必须重新分配(因为哈希链断了)。cache hit 的块有两种情况:\nblock 已经在 used_block_ids(被别的序列占用):ref_count += 1,共享 KV cache。 block 不在 used_block_ids(在 prefix cache 中但没人用):_allocate_block 把它从 free_block_ids 移到 used_block_ids。 校验 token_ids:即使 hash 命中,也要对比 self.blocks[block_id].token_ids != token_ids 确认内容一致。这是为了防止 hash 冲突(虽然 64-bit hash 几乎不会冲突,但工程上要做)。\n6.4 deallocate 和 can_append / may_append def deallocate(self, seq): for block_id in reversed(seq.block_table): # 逆序 block = self.blocks[block_id] block.ref_count -= 1 if block.ref_count == 0: self._deallocate_block(block_id) # 真正放回 free 队列 seq.num_cached_tokens = 0 seq.block_table.clear() 引用计数归零才真正释放。共享 block 在最后一个引用者释放时才回收。\ndef can_append(self, seq): # 当且仅当下一个 token 会\u0026quot;换块\u0026quot;时才需要新 block return len(self.free_block_ids) \u0026gt;= (len(seq) % self.block_size == 1) def may_append(self, seq): block_table = seq.block_table last_block = self.blocks[block_table[-1]] if len(seq) % self.block_size == 1: # 进入新块 assert last_block.hash != -1 # 旧块必须已 sealed block_id = self.free_block_ids[0] self._allocate_block(block_id) block_table.append(block_id) elif len(seq) % self.block_size == 0: # 刚填满一个块 assert last_block.hash == -1 # 必须还没 hash token_ids = seq.block(seq.num_blocks-1) prefix = self.blocks[block_table[-2]].hash if len(block_table) \u0026gt; 1 else -1 h = self.compute_hash(token_ids, prefix) last_block.update(h, token_ids) self.hash_to_block_id[h] = last_block.block_id else: # 普通追加 assert last_block.hash == -1 三种情况(% block_size): - == 1:刚跨过 256 的整数倍 → 分配新块。 - == 0:刚填满一块 → 算 hash,加入 prefix cache。 - 其他:块未满 → 啥也不做。\n📌 can_append 只在需要新块时才检查,这是个小优化。\n6.5 一个图示 假设 block_size=4,prefix cache 里已有 hash(\u0026quot;AB\u0026quot;) → block 7,hash(\u0026quot;CD\u0026quot;) → block 3 新请求:prompt = \u0026quot;ABCDEF\u0026quot; → num_blocks = 2 → 第 0 块:token_ids = \u0026quot;ABCD\u0026quot;,算 hash(\u0026quot;ABCD\u0026quot;, prefix=hash(\u0026quot;AB\u0026quot;))? 实际算法:prefix 是上一块的 hash,初始为 -1,所以 h = hash(\u0026quot;ABCD\u0026quot;) → 第 1 块:token_ids = \u0026quot;EF\u0026quot;(不满 4),h = -1,不进 cache decode 1 步:token 变成 \u0026quot;ABCDEFG\u0026quot; → num_blocks = 2 → may_append:len = 7, 7 % 4 = 3,普通情况 decode 1 步:token 变成 \u0026quot;ABCDEFGH\u0026quot; → num_blocks = 2 → may_append:len = 8, 8 % 4 = 0,刚好填满第 1 块 → 算 hash(\u0026quot;EFGH\u0026quot;, prefix=hash(\u0026quot;ABCD\u0026quot;)) → 注册到 hash_to_block_id 7. ModelRunner:真正”跑模型”的地方 文件 \u0026lt;\u0026lt;nanovllm/engine/model_runner.py\u0026gt;\u0026gt;(250 行)。这是 nano-vllm 最复杂的一个文件,涵盖分布式、KV cache 分配、CUDA Graph 捕获、prefill/decode 输入准备。\n7.1 初始化(__init__) def __init__(self, config, rank, event): self.config = config self.block_size = config.kvcache_block_size self.enforce_eager = config.enforce_eager self.world_size = config.tensor_parallel_size self.rank = rank self.event = event # 1. 初始化分布式 dist.init_process_group(\u0026quot;nccl\u0026quot;, \u0026quot;tcp://localhost:2333\u0026quot;, world_size=self.world_size, rank=rank) torch.cuda.set_device(rank) # 2. 切默认 dtype / device default_dtype = torch.get_default_dtype() torch.set_default_dtype(hf_config.torch_dtype) # 通常是 bfloat16 torch.set_default_device(\u0026quot;cuda\u0026quot;) # 3. 建模型 + 加载权重 self.model = Qwen3ForCausalLM(hf_config) load_model(self.model, config.model) self.sampler = Sampler() # 4. warmup → 5. 分配 KV cache → 6. 捕获 CUDA Graph self.warmup_model() self.allocate_kv_cache() if not self.enforce_eager: self.capture_cudagraph() 7.2 分布式:简单粗暴的共享内存 if self.world_size \u0026gt; 1: if rank == 0: self.shm = SharedMemory(name=\u0026quot;nanovllm\u0026quot;, create=True, size=2**20) dist.barrier() else: dist.barrier() self.shm = SharedMemory(name=\u0026quot;nanovllm\u0026quot;) self.loop() # 非 rank 0 的子进程进入 loop def loop(self): while True: method_name, args = self.read_shm() self.call(method_name, *args) if method_name == \u0026quot;exit\u0026quot;: break 主进程调用 self.model_runner.call(\"run\", seqs, is_prefill) 时(\u0026lt;\u0026lt;llm_engine.py:50\u0026gt;\u0026gt;):\ndef call(self, method_name, *args): if self.world_size \u0026gt; 1 and self.rank == 0: self.write_shm(method_name, *args) # rank 0 写 SHM,通知所有子进程 method = getattr(self, method_name, None) return method(*args) # rank 0 自己同步执行 这是很取巧的设计:rank 0 通过 pickle 把方法名 + 参数序列化到 1MB 共享内存,然后用 Event 通知子进程,子进程反序列化后调用同名方法。子进程不直接接收 NCCL 集合通信,而是镜像执行 rank 0 的 Python 逻辑,NCCL 只在 ColumnParallel/RowParallel 内部的 dist.all_reduce 中起作用。\n7.3 warmup_model:为显存分配做准备 def warmup_model(self): torch.cuda.empty_cache() torch.cuda.reset_peak_memory_stats() max_num_batched_tokens, max_model_len = self.config.max_num_batched_tokens, self.config.max_model_len num_seqs = min(max_num_batched_tokens // max_model_len, self.config.max_num_seqs) seqs = [Sequence([0] * max_model_len) for _ in range(num_seqs)] self.run(seqs, True) # 跑一次 prefill torch.cuda.empty_cache() 关键目的: - 让 PyTorch 完成 lazy kernel 初始化、CUDA context 建立。 - 让 torch.cuda.memory_stats()[\"allocated_bytes.all.peak\"] 有真实数据可读。\nreset_peak_memory_stats 在 warmup 前调用,所以 peak 反映的是模型本身 + 一次最大 prefill 的显存。后续 allocate_kv_cache 用这个数字反算 KV cache 容量。\n7.4 allocate_kv_cache:动态算 block 数 def allocate_kv_cache(self): free, total = torch.cuda.mem_get_info() used = total - free peak = torch.cuda.memory_stats()[\u0026quot;allocated_bytes.all.peak\u0026quot;] current = torch.cuda.memory_stats()[\u0026quot;allocated_bytes.all.current\u0026quot;] num_kv_heads = hf_config.num_key_value_heads // self.world_size # TP 切分 block_bytes = 2 * hf_config.num_hidden_layers * self.block_size * num_kv_heads \\ * hf_config.head_dim * hf_config.torch_dtype.itemsize config.num_kvcache_blocks = int(total * config.gpu_memory_utilization - used - peak + current) // block_bytes assert config.num_kvcache_blocks \u0026gt; 0 # 形状: [2 (K/V), num_layers, num_blocks, block_size, num_kv_heads, head_dim] self.kv_cache = torch.zeros(2, hf_config.num_hidden_layers, config.num_kvcache_blocks, self.block_size, num_kv_heads, hf_config.head_dim) # 把每层 slice 挂到对应 attention 模块 layer_id = 0 for module in self.model.modules(): if hasattr(module, \u0026quot;k_cache\u0026quot;) and hasattr(module, \u0026quot;v_cache\u0026quot;): module.k_cache = self.kv_cache[0, layer_id] module.v_cache = self.kv_cache[1, layer_id] layer_id += 1 计算公式:\n可用显存 = total * gpu_memory_utilization - used - peak + current block 字节数 = 2 (K+V) × num_layers × block_size × num_kv_heads_per_rank × head_dim × sizeof(dtype) num_blocks = 可用显存 // block 字节数 注意 + current 是因为 warmup 时分配的临时 tensor 已经被 empty_cache 释放,但 current 记录了”现在还有多少是模型权重本身”,peak 是”模型 + 临时 tensor”的最大值。两者相减再加回去,等价于”从分配比例里扣掉模型权重,留给 KV cache”。\nKV cache 是 1 个大张量:[2, num_layers, num_blocks, block_size, num_kv_heads, head_dim],所有层、所有 block、所有 head 共享。每层通过 self.kv_cache[0, layer_id] 拿到属于自己的 slice(共享存储,无复制)。这是 PagedAttention 的”页表”层面,而 Attention 模块的 k_cache/v_cache 是”实际数据”层面,二者通过 slot_mapping 关联。\n7.5 prepare_prefill:prefill 输入准备 def prepare_prefill(self, seqs): input_ids = [] positions = [] cu_seqlens_q = [0] cu_seqlens_k = [0] max_seqlen_q = 0 max_seqlen_k = 0 slot_mapping = [] block_tables = None for seq in seqs: seqlen = len(seq) input_ids.extend(seq[seq.num_cached_tokens:]) # 只算未缓存部分 positions.extend(list(range(seq.num_cached_tokens, seqlen))) seqlen_q = seqlen - seq.num_cached_tokens seqlen_k = seqlen cu_seqlens_q.append(cu_seqlens_q[-1] + seqlen_q) cu_seqlens_k.append(cu_seqlens_k[-1] + seqlen_k) max_seqlen_q = max(seqlen_q, max_seqlen_q) max_seqlen_k = max(seqlen_k, max_seqlen_k) if not seq.block_table: continue for i in range(seq.num_cached_blocks, seq.num_blocks): start = seq.block_table[i] * self.block_size end = start + self.block_size if i != seq.num_blocks-1 \\ else start + seq.last_block_num_tokens slot_mapping.extend(list(range(start, end))) # prefix cache 命中时, K 长度 \u0026gt; Q 长度,需要 block_table if cu_seqlens_k[-1] \u0026gt; cu_seqlens_q[-1]: block_tables = self.prepare_block_tables(seqs) # 全部转 tensor + pin_memory + non_blocking input_ids = torch.tensor(input_ids, dtype=torch.int64, pin_memory=True).cuda(non_blocking=True) ... set_context(True, cu_seqlens_q, cu_seqlens_k, max_seqlen_q, max_seqlen_k, slot_mapping, None, block_tables) return input_ids, positions 关键概念:\n数据 含义 input_ids 所有序列未缓存 token 拼成的扁平数组 positions 每个 token 的位置 ID cu_seqlens_q query 累计序列长度(变长 attention 用) cu_seqlens_k key 累计长度(= prompt 长度,无 prefix 截断) max_seqlen_q / max_seqlen_k 最大长度,FlashAttention 用于分块 slot_mapping 每个新 token 在 KV cache 中的物理 slot block_tables 已有 KV 块的逻辑表(只有 prefix cache 命中时才有) prefill + prefix cache 命中:cu_seqlens_k[-1] \u0026gt; cu_seqlens_q[-1],因为 K 看到完整上下文(包括已缓存的 prefix),Q 只看到未缓存部分。这时 Attention 层会用 flash_attn_varlen_func + block_table 从 KV cache 读已缓存部分。\n7.6 prepare_decode:decode 输入准备 def prepare_decode(self, seqs): input_ids = [] positions = [] slot_mapping = [] context_lens = [] for seq in seqs: input_ids.append(seq.last_token) # 每条序列只一个 token positions.append(len(seq)) # 当前位置 context_lens.append(len(seq)) # 上下文长度(给 FlashAttention) slot_mapping.append(seq.block_table[-1] * self.block_size + seq.last_block_num_tokens - 1) ... set_context(False, slot_mapping=slot_mapping, context_lens=context_lens, block_tables=block_tables) return input_ids, positions decode 的关键差异: - 每条序列只输入 1 个 token(last_token)。 - 没有 cu_seqlens(因为每条序列长度都是 1)。 - 需要 context_lens 告诉 FlashAttention 每条序列当前的上下文长度。 - slot_mapping 计算:最后一个 block 的起始 offset + 该 block 已有 token 数 - 1(指向新 token 写入位置)。\n7.7 run_model:决定走 eager 还是 CUDA Graph @torch.inference_mode() def run_model(self, input_ids, positions, is_prefill): if is_prefill or self.enforce_eager or input_ids.size(0) \u0026gt; 512: return self.model.compute_logits(self.model(input_ids, positions)) else: bs = input_ids.size(0) context = get_context() graph = self.graphs[next(x for x in self.graph_bs if x \u0026gt;= bs)] graph_vars = self.graph_vars for k, v in graph_vars.items(): if k != \u0026quot;outputs\u0026quot;: v.zero_() graph_vars[\u0026quot;input_ids\u0026quot;][:bs] = input_ids graph_vars[\u0026quot;positions\u0026quot;][:bs] = positions graph_vars[\u0026quot;slot_mapping\u0026quot;][:bs] = context.slot_mapping graph_vars[\u0026quot;context_lens\u0026quot;][:bs] = context.context_lens graph_vars[\u0026quot;block_tables\u0026quot;][:bs, :context.block_tables.size(1)] = context.block_tables graph.replay() return self.model.compute_logits(graph_vars[\u0026quot;outputs\u0026quot;][:bs]) 走 CUDA Graph 的条件(全部满足): - 不是 prefill - 没有 enforce_eager - batch_size ≤ 512\n为什么 decode 才走 CUDA Graph? decode 阶段每 step 处理 1 个 token,GPU 算得快但 CPU launch kernel 慢,launch overhead 占总时间的比例很高。CUDA Graph 一次性捕获整段计算,后续 replay() 直接交给 GPU 调度,极大降低 CPU 开销。prefill 阶段算力密集,launch overhead 不成问题,不需要 Graph。\n7.8 capture_cudagraph:批量大小网格 self.graph_bs = [1, 2, 4, 8] + list(range(16, max_bs + 1, 16)) self.graphs = {} self.graph_pool = None for bs in reversed(self.graph_bs): graph = torch.cuda.CUDAGraph() set_context(False, slot_mapping=slot_mapping[:bs], context_lens=context_lens[:bs], block_tables=block_tables[:bs]) outputs[:bs] = self.model(input_ids[:bs], positions[:bs]) # warmup with torch.cuda.graph(graph, self.graph_pool): outputs[:bs] = self.model(input_ids[:bs], positions[:bs]) # capture if self.graph_pool is None: self.graph_pool = graph.pool() self.graphs[bs] = graph 预设了 1, 2, 4, 8, 16, 32, 48, ... 这一系列 batch size。 每个 batch size 单独 capture 一张图,运行时选第一个 ≥ 当前 batch size 的图(next(x for x in self.graph_bs if x \u0026gt;= bs))。 使用 graph_pool 共享内存池:不同 batch size 的图共享底层显存,避免每张图重复分配。 ⚠️ CUDA Graph 限制:同一张图的所有输入张量必须地址不变(用预分配的 graph_vars)。run_model 里把当前 batch 的数据 [:bs] 复制到 graph_vars 中,然后 replay(),这正是用空间换时间。\n8. Attention:FlashAttention + KV Cache 写入 文件 \u0026lt;\u0026lt;nanovllm/layers/attention.py\u0026gt;\u0026gt;(80 行)。包含两个关键部分:Triton 写的 store_kvcache_kernel,以及 Attention 前向。\n8.1 store_kvcache_kernel:用 Triton 把 K/V 写到 cache @triton.jit def store_kvcache_kernel( key_ptr, key_stride, value_ptr, value_stride, k_cache_ptr, v_cache_ptr, slot_mapping_ptr, D: tl.constexpr, ): idx = tl.program_id(0) key_offsets = idx * key_stride + tl.arange(0, D) value_offsets = idx * value_stride + tl.arange(0, D) key = tl.load(key_ptr + key_offsets) value = tl.load(value_ptr + value_offsets) slot = tl.load(slot_mapping_ptr + idx) cache_offsets = slot * D + tl.arange(0, D) tl.store(k_cache_ptr + cache_offsets, key) tl.store(v_cache_ptr + cache_offsets, value) 输入参数: - key / value:[N, num_kv_heads, head_dim],N 是当前 step 的 token 数。 - k_cache / v_cache:[num_blocks, block_size, num_kv_heads, head_dim]。 - slot_mapping:[N],每个 token 写入 cache 的物理 slot 索引。 - D = num_kv_heads * head_dim:把多个 head 合并成一个 1D 写。\n逻辑:对每个 token(用一个 program 处理),从 slot_mapping[idx] 读出它在 cache 中的物理位置,然后用 tl.store 把整个 D 维一次性写进去。\ndef store_kvcache(key, value, k_cache, v_cache, slot_mapping): N, num_heads, head_dim = key.shape D = num_heads * head_dim store_kvcache_kernel[(N,)](key, key.stride(0), value, value.stride(0), k_cache, v_cache, slot_mapping, D) grid=(N,) 表示启动 N 个 program,每个 program 处理一个 token。这个 kernel 性能很关键——它会在每层 attention、每个 step 都被调用,直接决定 decode 延迟。\n8.2 Attention.forward class Attention(nn.Module): def __init__(self, num_heads, head_dim, scale, num_kv_heads): super().__init__() ... self.k_cache = self.v_cache = torch.tensor([]) # 会被 ModelRunner 替换 def forward(self, q, k, v): context = get_context() k_cache, v_cache = self.k_cache, self.v_cache if k_cache.numel() and v_cache.numel(): store_kvcache(k, v, k_cache, v_cache, context.slot_mapping) if context.is_prefill: if context.block_tables is not None: # prefix cache 命中 k, v = k_cache, v_cache # ← 关键: K/V 来自 cache o = flash_attn_varlen_func( q, k, v, max_seqlen_q=context.max_seqlen_q, cu_seqlens_q=context.cu_seqlens_q, max_seqlen_k=context.max_seqlen_k, cu_seqlens_k=context.cu_seqlens_k, softmax_scale=self.scale, causal=True, block_table=context.block_tables, ) else: # decode o = flash_attn_with_kvcache( q.unsqueeze(1), k_cache, v_cache, cache_seqlens=context.context_lens, block_table=context.block_tables, softmax_scale=self.scale, causal=True, ) o = o.view(-1, self.num_heads * self.head_dim) return o 两种路径,两种 FlashAttention API:\n阶段 函数 输入 特点 Prefill 无 prefix cache flash_attn_varlen_func q, k, v 都是当前 step 算出 变长序列支持,内部按 cu_seqlens 切分 Prefill 有 prefix cache flash_attn_varlen_func q 当前, k/v 来自 cache K/V 用 block_table 从分页 cache 读 Decode flash_attn_with_kvcache q 单 token, k/v 来自 cache 直接基于 KV cache 算当前 token attention 关于 block_table:FlashAttention 的 block_table 参数接受 [batch, max_blocks] 的 int32 tensor,告诉 kernel 怎么从分页 cache 中 gather K/V。这是把 PagedAttention 集成到 FlashAttention 的关键。\n关于 q.unsqueeze(1):flash_attn_with_kvcache 要求 query 的形状是 [batch, 1, num_heads, head_dim](每条序列 query 长度 1),所以要把 [N, num_heads, head_dim] 加一个维度。\n9. Qwen3 模型结构 文件 \u0026lt;\u0026lt;nanovllm/models/qwen3.py\u0026gt;\u0026gt;(215 行)。\nQwen3ForCausalLM ├── model: Qwen3Model │ ├── embed_tokens: VocabParallelEmbedding │ ├── layers: nn.ModuleList[Qwen3DecoderLayer × N] │ │ ├── input_layernorm: RMSNorm │ │ ├── self_attn: Qwen3Attention │ │ │ ├── qkv_proj: QKVParallelLinear │ │ │ ├── q_norm: RMSNorm(head_dim) ← Qwen3 特有 │ │ │ ├── k_norm: RMSNorm(head_dim) ← Qwen3 特有 │ │ │ ├── rotary_emb: RotaryEmbedding │ │ │ ├── attn: Attention ← §8 │ │ │ └── o_proj: RowParallelLinear │ │ ├── post_attention_layernorm: RMSNorm │ │ └── mlp: Qwen3MLP │ │ ├── gate_up_proj: MergedColumnParallelLinear │ │ ├── act_fn: SiluAndMul │ │ └── down_proj: RowParallelLinear │ └── norm: RMSNorm └── lm_head: ParallelLMHead 9.1 DecoderLayer:fused residual + RMSNorm def forward(self, positions, hidden_states, residual): if residual is None: # 第一层: 没有 residual residual = hidden_states hidden_states = self.input_layernorm(hidden_states) else: # 后续层: 把残差加和 norm 融合 hidden_states, residual = self.input_layernorm(hidden_states, residual) hidden_states = self.self_attn(positions, hidden_states) hidden_states, residual = self.post_attention_layernorm(hidden_states, residual) hidden_states = self.mlp(hidden_states) return hidden_states, residual add_rms_forward(在 layernorm.py)把 x = x + residual 和 x = norm(x) 融合成一个 kernel:\n@torch.compile def add_rms_forward(self, x, residual): orig_dtype = x.dtype x = x.to(torch.float32).add_(residual.to(torch.float32)) # 融合 1 residual = x.to(orig_dtype) var = x.pow(2).mean(dim=-1, keepdim=True) x.mul_(torch.rsqrt(var + self.eps)) x = x.to(orig_dtype).mul_(self.weight) return x, residual 这样每层只多 1 个 residual tensor 占用显存(而不是 2 个)。torch.compile 进一步把这个函数编译成高效 kernel。\n9.2 Qwen3Attention:per-head QK Norm def forward(self, positions, hidden_states): qkv = self.qkv_proj(hidden_states) q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1) q_by_head = q.view(-1, self.num_heads, self.head_dim) q_by_head = self.q_norm(q_by_head) # Qwen3 特有 q = q_by_head.view(q.shape) k_by_head = k.view(-1, self.num_kv_heads, self.head_dim) k_by_head = self.k_norm(k_by_head) # Qwen3 特有 k = k_by_head.view(k.shape) q, k = self.rotary_emb(positions, q, k) o = self.attn(q, k, v) output = self.o_proj(o) return output Qwen3 关键差异:和 LLaMA 不同,Qwen3 对 Q 和 K 的每个 head 单独做 RMSNorm(head_dim 大小),这能提升训练稳定性。\n9.3 SwiGLU MLP class Qwen3MLP(nn.Module): def forward(self, x): gate_up = self.gate_up_proj(x) # 一次性输出 [gate, up] x = self.act_fn(gate_up) # silu(gate) * up x = self.down_proj(x) return x SiluAndMul(\u0026lt;\u0026lt;layers/activation.py\u0026gt;\u0026gt;):\n@torch.compile def forward(self, x): x, y = x.chunk(2, -1) return F.silu(x) * y 把一个 2 * intermediate 维度的 tensor 在最后一维切成两半,前一半过 SiLU,和后一半逐元素相乘。这就是 SwiGLU 激活。\n9.4 Qwen3ForCausalLM class Qwen3ForCausalLM(nn.Module): packed_modules_mapping = { \u0026quot;q_proj\u0026quot;: (\u0026quot;qkv_proj\u0026quot;, \u0026quot;q\u0026quot;), \u0026quot;k_proj\u0026quot;: (\u0026quot;qkv_proj\u0026quot;, \u0026quot;k\u0026quot;), \u0026quot;v_proj\u0026quot;: (\u0026quot;qkv_proj\u0026quot;, \u0026quot;v\u0026quot;), \u0026quot;gate_proj\u0026quot;: (\u0026quot;gate_up_proj\u0026quot;, 0), \u0026quot;up_proj\u0026quot;: (\u0026quot;gate_up_proj\u0026quot;, 1), } def forward(self, input_ids, positions): return self.model(input_ids, positions) def compute_logits(self, hidden_states): return self.lm_head(hidden_states) packed_modules_mapping 是权重加载的关键:\nHF 格式:model.layers.0.self_attn.q_proj.weight、k_proj.weight、v_proj.weight 三个独立文件。 nano-vllm 格式:合并成 qkv_proj.weight,每条通过 QKVParallelLinear.weight_loader 切到正确的 slice。 compute_logits 单独抽出来,是因为只有最后一个 token 的 hidden state 需要算 logits,ModelRunner 会利用这一点减少 LM head 计算量(见 §10.2)。\n10. 张量并行(Tensor Parallelism) 文件 \u0026lt;\u0026lt;nanovllm/layers/linear.py\u0026gt;\u0026gt; 和 \u0026lt;\u0026lt;nanovllm/layers/embed_head.py\u0026gt;\u0026gt;。\n10.1 4 种线性层 类 切分方式 通信 用途 ReplicatedLinear 不切 无 单卡等价 ColumnParallelLinear 输出维度切 后续是否 all-reduce 看下游 QKV projection、MLP gate/up RowParallelLinear 输入维度切 forward 末尾 all-reduce attention output proj、MLP down proj QKVParallelLinear 继承 Column 后续是否 all-reduce 看下游 合并 Q+K+V 三个投影 MergedColumnParallelLinear 继承 Column 后续是否 all-reduce 看下游 合并 gate+up 10.2 ColumnParallelLinear(输出切分) class ColumnParallelLinear(LinearBase): def __init__(self, input_size, output_size, bias=False): super().__init__(input_size, output_size, 0) # tp_dim=0 表示按行切 self.output_size_per_partition = divide(output_size, self.tp_size) self.weight = nn.Parameter(torch.empty(self.output_size_per_partition, self.input_size)) ... def weight_loader(self, param, loaded_weight): param_data = param.data shard_size = param_data.size(self.tp_dim) start_idx = self.tp_rank * shard_size loaded_weight = loaded_weight.narrow(self.tp_dim, start_idx, shard_size) param_data.copy_(loaded_weight) def forward(self, x): return F.linear(x, self.weight, self.bias) # 不需要 all-reduce 每个 rank 只算自己分到的输出维度。weight_loader 把 HF 完整权重按行切,只 copy 当前 rank 的那一片。\n10.3 RowParallelLinear(输入切分) class RowParallelLinear(LinearBase): def __init__(self, input_size, output_size, bias=False): super().__init__(input_size, output_size, 1) # tp_dim=1 表示按列切 self.input_size_per_partition = divide(input_size, self.tp_size) self.weight = nn.Parameter(torch.empty(self.output_size, self.input_size_per_partition)) ... def forward(self, x): y = F.linear(x, self.weight, self.bias if self.tp_rank == 0 else None) if self.tp_size \u0026gt; 1: dist.all_reduce(y) return y 每个 rank 输入维度只有 input_size / tp_size,因此计算出的 y 是不完整的(只是部分和),需要 all_reduce 汇总。只 rank 0 算 bias,因为所有 rank 加完 bias 之后是 N 份相同的 bias 相加。\n10.4 QKVParallelLinear:QKV 三合一 class QKVParallelLinear(ColumnParallelLinear): def __init__(self, hidden_size, head_size, total_num_heads, total_num_kv_heads=None, bias=False): ... output_size = (total_num_heads + 2 * total_num_kv_heads) * head_size super().__init__(hidden_size, output_size, bias=bias) def weight_loader(self, param, loaded_weight, loaded_shard_id): # loaded_shard_id ∈ {\u0026quot;q\u0026quot;, \u0026quot;k\u0026quot;, \u0026quot;v\u0026quot;} if loaded_shard_id == \u0026quot;q\u0026quot;: shard_offset = 0 shard_size = self.num_heads * self.head_size elif loaded_shard_id == \u0026quot;k\u0026quot;: shard_offset = self.num_heads * self.head_size shard_size = self.num_kv_heads * self.head_size else: # \u0026quot;v\u0026quot; shard_offset = self.num_heads * self.head_size + self.num_kv_heads * self.head_size shard_size = self.num_kv_heads * self.head_size param_data = param.data.narrow(self.tp_dim, shard_offset, shard_size) loaded_weight = loaded_weight.chunk(self.tp_size, self.tp_dim)[self.tp_rank] param_data.copy_(loaded_weight) Qwen3 用 GQA(Grouped Query Attention):num_kv_heads 通常比 num_heads 少(例如 16 query heads 对 8 kv heads),通过 GQA 减少 KV cache 内存。QKVParallelLinear 内部按 [Q heads, K heads, V heads] 顺序拼接,每个 rank 只 load 自己负责的 heads。\n10.5 VocabParallelEmbedding 和 ParallelLMHead class VocabParallelEmbedding(nn.Module): def forward(self, x): if self.tp_size \u0026gt; 1: mask = (x \u0026gt;= self.vocab_start_idx) \u0026amp; (x \u0026lt; self.vocab_end_idx) x = mask * (x - self.vocab_start_idx) y = F.embedding(x, self.weight) if self.tp_size \u0026gt; 1: y = mask.unsqueeze(1) * y dist.all_reduce(y) return y Embedding 切分思路: 1. 每个 rank 只存自己那段 vocab 的 embedding。 2. forward 时,token ID 是完整 vocab 的全局 ID,通过 mask 标记”是否在本 rank”。 3. 不在的 token 减成本地 ID 后乘 mask = 0,在的 token 正常查表。 4. 最后 all_reduce 求和,得到每条 token 的完整 embedding(不在自己 rank 的 token 贡献 0,在的贡献真实值)。\nclass ParallelLMHead(VocabParallelEmbedding): def forward(self, x): context = get_context() if context.is_prefill: # 只取每条序列最后一个 token 的 hidden state last_indices = context.cu_seqlens_q[1:] - 1 x = x[last_indices].contiguous() logits = F.linear(x, self.weight, self.bias) if self.tp_size \u0026gt; 1: all_logits = [torch.empty_like(logits) for _ in range(self.tp_size)] \\ if self.tp_rank == 0 else None dist.gather(logits, all_logits, 0) logits = torch.cat(all_logits, -1) if self.tp_rank == 0 else None return logits prefill 优化:cu_seqlens_q[1:] - 1 取出每条序列最后一个 token 的下标(因为变长 attention 中,每条序列的最后一个位置就是 cu_seqlens_q 中的结束边界减 1)。这样 LM head 只需要算 N 个 token(seq 数)的 logits,而不是整个 prompt。\nTP 下:rank 0 gather 所有 rank 的部分 logits(dist.gather 而非 all_reduce),然后 concat 拼成完整 vocab 维度。\n11. 采样 Sampler 文件 \u0026lt;\u0026lt;nanovllm/layers/sampler.py\u0026gt;\u0026gt;(20 行)。\nclass Sampler(nn.Module): def forward(self, logits, temperatures): logits = logits.to(torch.float) greedy_tokens = logits.argmax(dim=-1) logits.div_(temperatures.unsqueeze(dim=1)) probs = torch.softmax(logits, dim=-1, dtype=torch.float) sample_tokens = probs.div_(torch.empty_like(probs).exponential_(1) + epsilon).argmax(dim=-1) return torch.where(temperatures == 0, greedy_tokens, sample_tokens) Gumbel-max 技巧:\nE ~ Exponential(1) argmax(p_i / E_i) ~ Categorical(p) 为什么? 取 g_i = -log(-log(u_i)) (Gumbel),等价于 argmax(log p_i + g_i)。而 -log(E_i) = g_i,所以 argmax(p_i / E_i) = argmax(log p_i - log E_i) = argmax(log p_i + (-log E_i)),正好等价。\n两种模式: - temperature == 0:走 argmax,greedy decoding。 - temperature \u0026gt; 0:走 Gumbel-max sampling。 - torch.where 一行完成两种模式的选择。\n12. 权重加载 文件 \u0026lt;\u0026lt;nanovllm/utils/loader.py\u0026gt;\u0026gt;(30 行)。\ndef default_weight_loader(param, loaded_weight): param.data.copy_(loaded_weight) def load_model(model, path): packed_modules_mapping = getattr(model, \u0026quot;packed_modules_mapping\u0026quot;, {}) for file in glob(os.path.join(path, \u0026quot;*.safetensors\u0026quot;)): with safe_open(file, \u0026quot;pt\u0026quot;, \u0026quot;cpu\u0026quot;) as f: for weight_name in f.keys(): for k in packed_modules_mapping: if k in weight_name: v, shard_id = packed_modules_mapping[k] param_name = weight_name.replace(k, v) param = model.get_parameter(param_name) weight_loader = getattr(param, \u0026quot;weight_loader\u0026quot;) weight_loader(param, f.get_tensor(weight_name), shard_id) break else: param = model.get_parameter(weight_name) weight_loader = getattr(param, \u0026quot;weight_loader\u0026quot;, default_weight_loader) weight_loader(param, f.get_tensor(weight_name)) 两种加载路径:\npacked 权重(在 packed_modules_mapping 中):比如 q_proj.weight → 改名 → qkv_proj.weight,然后调用该参数的 weight_loader(QKVParallelLinear.weight_loader)做 TP 切片。 普通权重:直接用参数的 weight_loader(由 ColumnParallelLinear 等定义)做 TP 切片,或 fallback 到 default_weight_loader(纯拷贝)。 关键点:每个 nn.Parameter 上挂 weight_loader 属性,而不是用全局字典。这让每个并行层可以自定义自己的切片方式(QKV 和 gate_up 都需要传入 shard_id)。\n13. 周边模块 13.1 utils/context.py:全局 Context @dataclass class Context: is_prefill: bool = False cu_seqlens_q: torch.Tensor | None = None cu_seqlens_k: torch.Tensor | None = None max_seqlen_q: int = 0 max_seqlen_k: int = 0 slot_mapping: torch.Tensor | None = None context_lens: torch.Tensor | None = None block_tables: torch.Tensor | None = None _CONTEXT = Context() def get_context(): return _CONTEXT def set_context(is_prefill, ...): global _CONTEXT _CONTEXT = Context(is_prefill, ...) def reset_context(): global _CONTEXT _CONTEXT = Context() 模块级单例。Attention、LMHead 等模块都通过 get_context() 拿到当前 step 的 metadata。这是一种隐式的全局状态——优点是避免到处传参,缺点是不易扩展。vLLM 主仓库也保留了这种模式。\n13.2 rotary_embedding.py:RoPE class RotaryEmbedding(nn.Module): def __init__(self, head_size, rotary_dim, max_position_embeddings, base): ... inv_freq = 1.0 / (base ** (torch.arange(0, rotary_dim, 2, dtype=torch.float) / rotary_dim)) t = torch.arange(max_position_embeddings, dtype=torch.float) freqs = torch.einsum(\u0026quot;i,j -\u0026gt; ij\u0026quot;, t, inv_freq) cos = freqs.cos() sin = freqs.sin() self.register_buffer(\u0026quot;cos_sin_cache\u0026quot;, torch.cat((cos, sin), dim=-1), persistent=False) @torch.compile def forward(self, positions, query, key): cos_sin = self.cos_sin_cache[positions] cos, sin = cos_sin.chunk(2, dim=-1) query = apply_rotary_emb(query.view(num_tokens, -1, head_size), cos, sin).view(query_shape) key = apply_rotary_emb(key.view(num_tokens, -1, head_size), cos, sin).view(key_shape) return query, key 预计算 cos_sin_cache 存到 buffer,避免每 step 重算三角函数。 forward 用 positions 索引需要的 cos/sin,大幅减少计算量。 cos_sin_cache 不持久化(persistent=False),节省权重文件大小。 get_rope 用 lru_cache(1) 缓存一个 RotaryEmbedding 实例(所有层共享同一份 cos_sin_cache)。 13.3 sampling_params.py @dataclass class SamplingParams: temperature: float = 1.0 max_tokens: int = 64 ignore_eos: bool = False vLLM 完整版还支持 top_p、top_k、presence_penalty、frequency_penalty、stop、logprobs 等,nano-vllm 只保留最简版。\n13.4 config.py @dataclass class Config: model: str max_num_batched_tokens: int = 16384 max_num_seqs: int = 512 max_model_len: int = 4096 gpu_memory_utilization: float = 0.9 tensor_parallel_size: int = 1 enforce_eager: bool = False hf_config: AutoConfig | None = None eos: int = -1 kvcache_block_size: int = 256 num_kvcache_blocks: int = -1 def __post_init__(self): assert os.path.isdir(self.model) assert self.kvcache_block_size % 256 == 0 # 必须能被 256 整除 assert 1 \u0026lt;= self.tensor_parallel_size \u0026lt;= 8 self.hf_config = AutoConfig.from_pretrained(self.model) self.max_model_len = min(self.max_model_len, self.hf_config.max_position_embeddings) assert self.max_num_batched_tokens \u0026gt;= self.max_model_len kvcache_block_size % 256 == 0 的硬约束:FlashAttention 的某些优化路径要求对齐;\u0026lt;= 256 的因子也行,但 nano-vllm 选择只允许 ≥ 256。\n14. 关键优化原理(vLLM 核心思想) 14.1 Continuous Batching(连续批处理) 传统静态批处理:一个 batch 内 N 条请求必须同时开始、同时结束。短请求要等长请求,GPU 利用率低。\nnano-vllm 的实现: - waiting:还没进 batch 的新请求。 - running:已经 prefill 完、正在 decode 的请求。 - 每次 schedule() 优先 prefill 新请求,否则 decode running 请求。完成的请求立即释放资源,新请求能立刻补位。\nStatic batching: [req1 ████][req2 ████][req3 ████] → 等待最慢的 Continuous: [req1 ████] [req2 ████][req2 ████][req2 ██] [req3 ████][req3 ██] ← 新请求随时补位 14.2 Prefill/Decode 分离 阶段 输入 计算模式 瓶颈 Prefill 整个 prompt(N 个 token) GEMM 主导,计算密集 GPU 算力 Decode 每条 1 token Memory-bound,小 kernel 频繁 显存带宽 + CPU launch nano-vllm 怎么做: - 准备输入时区分(prepare_prefill vs prepare_decode)。 - 选不同 FlashAttention API。 - 只有 decode 走 CUDA Graph。\n14.3 PagedAttention KV Cache 被切分为固定大小 block,通过 block_table 间接寻址。核心收益:\n避免预分配浪费:不需要为每条请求预留 max_model_len 长度的连续 KV cache。 降低内存碎片:操作系统分页思想。 支持变长 batch:不同长度的序列可以共存于一个 batch。 支持 prefix 共享:多个序列可以共享同一个物理 block(引用计数)。 14.4 Prefix Caching 链式 hash + hash_to_block_id 索引 = 命中相同 prefix 直接复用 KV cache。\n典型场景: - 相同 system prompt 的多轮对话。 - 共享 few-shot 示例的批量推理。 - 相同文档的多次 QA。\n触发条件: - 块必须完整填满(len == block_size)。 - 序列一旦 cache miss,后续所有块必须重新分配(哈希链断了)。\n14.5 FlashAttention flash_attn_varlen_func:变长序列支持,通过 cu_seqlens 把多个不同长度序列打包成一个 batch。 flash_attn_with_kvcache:专门为 decode 阶段设计,直接基于 paged KV cache 计算。 核心优势:不显式 materialize 巨大的 attention matrix,大幅减少 HBM 读写,适合长序列。 14.6 Tensor Parallelism 列切分(ColumnParallel):输出维度分到多卡,各自算各自的输出,无需 all-reduce。 行切分(RowParallel):输入维度分到多卡,各算部分和,末尾 all-reduce 汇总。\nQwen3 上的切分:\nQKV proj: Column 切 (无 all-reduce,直接进入 attention) O proj: Row 切 (all-reduce 之后接下一层) Gate/Up: Column 切 (无 all-reduce,直接进入 SiluAndMul) Down: Row 切 (all-reduce 之后接下一层) Embedding: Vocab 切 (mask + all-reduce) LM Head: Vocab 切 (gather 拼接) 14.7 CUDA Graph 为什么 decode 特别需要?decode 阶段每 step 只算 1 token,但 launch 几十个 CUDA kernel(attention、norm、linear…),CPU 调度开销占总时间比例可能高达 30-50%。\n怎么解决:把”形状固定”的一串 CUDA 操作捕获成一张图,后续 replay() 由 GPU 自己调度,CPU 不再介入。\n限制: - 同一张图的所有输入必须地址不变。所以 nano-vllm 预分配了 graph_vars tensor,运行时把当前数据 [:bs] 复制过去。 - 不同 batch size 需要不同图。nano-vllm 预 capture 1, 2, 4, 8, 16, 32, ...,运行时选第一个 ≥ 当前 bs 的图。\n14.8 Torch Compile RMSNorm、SiluAndMul、RotaryEmbedding.forward 都加了 @torch.compile:\n减少 Python overhead。 自动融合小算子(比如把 chunk + silu + * 融成一个 kernel)。 自动选择最优实现(为不同 shape 编译专用 kernel)。 15. 与官方 vLLM 的对应关系 nano-vllm 组件 官方 vLLM 中的位置 LLMEngine vllm/v1/engine/ (v1) 或 vllm/engine/llm_engine.py (v0) Scheduler vllm/v1/core/sched/scheduler.py (v1) Sequence + SequenceStatus vllm/v1/sequence.py BlockManager + Block vllm/v1/core/kv_cache_manager.py + vllm/v1/core/block_pool.py block_table block_table (相同概念) hash_to_block_id + xxhash vllm/v1/core/kv_cache_utils.py 的 BlockHash ModelRunner vllm/v1/worker/gpu_model_runner.py prepare_prefill / prepare_decode prepare_inputs 内部 capture_cudagraph vllm/compilation/cuda_graph.py Attention vllm/attention/backends/flash_attn.py store_kvcache_kernel 在 vLLM 中由 C++/CUDA 实现 Sampler vllm/model_executor/layers/sampler.py ColumnParallelLinear vllm/model_executor/layers/linear.py RowParallelLinear 同上 QKVParallelLinear 同上 VocabParallelEmbedding / ParallelLMHead 同上 load_model + packed_modules_mapping vllm/model_executor/model_loader/ 官方 vLLM 比 nano-vllm 多的东西:\nAsync engine:AsyncLLMEngine + asyncio,支持 streaming 输出。 OpenAI API server:HTTP/gRPC server,支持 /v1/chat/completions 等。 多种 model:LLaMA、Mistral、Mixtral、Qwen、GPT-OSS、DeepSeek 等。 Chunked prefill:把长 prompt 切成多段,和 decode 混合调度。 Speculative decoding:draft model + verify,加速 2-3x。 Quantization:GPTQ、AWQ、FP8、INT4 等。 LoRA:动态加载 adapter。 Pipeline parallel:跨节点分阶段。 多种 attention backend:FlashInfer、XFormers、FlashAttention2/3、Triton。 更复杂的采样:top_p, top_k, min_p, penalties, stop, logprobs。 prefix cache 策略:LRU eviction、eviction policy。 但核心抽象一致:理解 nano-vllm 后,看官方 vLLM 就是把这些概念”工程化”。\n16. 阅读建议路线 步骤 文件 重点 1 README.md, example.py 知道怎么用 2 nanovllm/__init__.py, nanovllm/llm.py 入口 3 nanovllm/config.py, nanovllm/sampling_params.py 知道有哪些配置 4 nanovllm/engine/llm_engine.py 主循环,理解 step() 5 nanovllm/engine/sequence.py 理解请求对象 6 nanovllm/engine/scheduler.py 理解调度,理解 preempt 7 nanovllm/engine/block_manager.py 重点:PagedAttention + Prefix Cache 8 nanovllm/engine/model_runner.py 重点:warmup、KV cache 分配、CUDA Graph 9 nanovllm/utils/context.py 知道 Attention 怎么拿到 metadata 10 nanovllm/layers/attention.py 重点:Triton kernel + FlashAttention 11 nanovllm/layers/linear.py 张量并行 12 nanovllm/layers/embed_head.py 词表并行 13 nanovllm/layers/layernorm.py, activation.py, rotary_embedding.py 周边算子 14 nanovllm/models/qwen3.py 模型结构 15 nanovllm/layers/sampler.py 采样 16 nanovllm/utils/loader.py 权重加载 17 bench.py 看怎么 benchmark 对照阅读:每读一个文件,回到本文的对应章节;再去看官方 vLLM 的对应位置。\n17. 常见疑问(基于代码反推) Q1:为什么 kvcache_block_size 必须能被 256 整除? 为了和某些硬件/CUDA 库的对齐要求兼容。FlashAttention 在某些路径上对 block size 有 256 字节的对齐要求,nano-vllm 把这个限制直接暴露成 API 约束。\nQ2:num_kvcache_blocks 是怎么算出来的? int(total * gpu_memory_utilization - used - peak + current) // block_bytes。详见 §7.4。\nQ3:decode 阶段 batch 包含 prefill 的序列吗? 不包含。nano-vllm 的 schedule() 设计是”非 prefill 即 decode”——一次 step 要么全是 prefill,要么全是 decode。这是 vLLM v0 的设计,v1 才引入了 chunked prefill 允许混合。\nQ4:为什么 prefill 时也要传 block_table? 只有 prefix cache 命中时才需要(代码中 if cu_seqlens_k[-1] \u0026gt; cu_seqlens_q[-1])。FlashAttention 用 block_table 从分页 KV cache gather 已缓存的 K/V,避免重算。\nQ5:preempt 后,被抢占的序列 prefix cache 还有效吗? 完全有效。deallocate 只把 block 引用计数 -1,只有 ref_count == 0 时才真正释放。即使在 prefix cache 中,只要 ref_count \u0026gt; 0,hash_to_block_id 映射就保留。被抢占的序列下次 prefill 还能命中之前的 prefix cache。\nQ6:enforce_eager=True 时性能会差多少? decode 阶段慢 30-50%(具体看模型)。CUDA Graph 是 nano-vllm 性能的关键优化之一。开发调试时用 enforce_eager=True 比较方便,正式推理时建议关掉。\nQ7:nano-vllm 和 vLLM v0/v1 哪个对应? nano-vllm 是 vLLM v0 思想的精简实现:Scheduler-Worker 分离、prefill/decode 分离、TP 简单 SHM 通信。它没有 vLLM v1 的 multi-step scheduling、chunked prefill 等高级特性。\nQ8:为什么 samper.py 里同时算了 greedy_tokens 和 sample_tokens? 为了用一个 kernel 覆盖两种模式,最后用 torch.where 选择。如果想省一点,可以在 Python 层分流,只跑对应分支。\nQ9:Sequence.__getstate__ 里的优化真的有效吗? 非常有效。decode 阶段一个序列可能已经 1000+ tokens,完整传 token 列表要 ~4KB(int32);只传 last_token 只要 4 字节。100 条序列差 400KB,跨进程 IPC 时这差距很明显。\n18. 一句话总结 nano-vllm 用 1200 行 Python 实现了 vLLM 的”骨架”: - Scheduler 做连续批处理和抢占, - BlockManager 用分页 + 哈希链做 KV cache 和 prefix cache, - ModelRunner 用 warmup 算显存、分配 KV cache、捕获 CUDA Graph,跨 TP rank 用共享内存 + NCCL, - Attention 用 Triton kernel 写 KV cache,用 FlashAttention 读 KV cache, - 各种 parallel linear 把模型切到多卡, - Sampler 用 Gumbel-max 完成采样。\n这套抽象就是 vLLM 的”魂”。读懂它,再去看 vLLM 主仓库(几万行 C++/Python 混合),就不会迷失。\n附:文件 → 章节速查 文件 本文章节 nanovllm/__init__.py §1 nanovllm/llm.py §1, §3.1 nanovllm/config.py §13.4 nanovllm/sampling_params.py §13.3 nanovllm/engine/llm_engine.py §3 nanovllm/engine/sequence.py §4 nanovllm/engine/scheduler.py §5 nanovllm/engine/block_manager.py §6 nanovllm/engine/model_runner.py §7 nanovllm/utils/context.py §13.1 nanovllm/utils/loader.py §12 nanovllm/models/qwen3.py §9 nanovllm/layers/attention.py §8 nanovllm/layers/linear.py §10.1 - §10.4 nanovllm/layers/embed_head.py §10.5 nanovllm/layers/layernorm.py §9.1 nanovllm/layers/rotary_embedding.py §13.2 nanovllm/layers/activation.py §9.3 nanovllm/layers/sampler.py §11 ","date":"2026-06-04T00:00:00Z","permalink":"/nano-vllm%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/","title":"nano-vllm 全代码精读笔记:用 1200 行 Python 理解 vLLM 推理引擎"},{"content":" 来源：【AI Infra】【RLHF框架】一、VeRL中基于Ray的执行流程源码解析（作者：白强伟）\n前置知识：[[Engineering/coding/verl/ray前置知识]] 提到此篇博客\nVeRL 是字节开源的 LLM RL 后训练框架，完整设计思路见论文《HybridFlow: A Flexible and Efficient RLHF Framework》。这篇博客从源码角度深入理解其基于 Ray 的执行流程，不涉及具体 RL 算法。\n一、Overview 1. 一个例子 假设有 4 个 GPU，每个 GPU 实例化一个 GPUAccumulator，其成员变量 value 初始化为 GPU rank，然后对所有 value 执行加 1 操作：\nimport ray import torch from verl.single_controller.base import Worker from verl.single_controller.ray.base import RayWorkerGroup, RayResourcePool, RayClassWithInitArgs from verl.single_controller.base.decorator import register, Dispatch @ray.remote class GPUAccumulator(Worker): def __init__(self): super().__init__() self.value = torch.zeros(size=(1,), device=\u0026quot;cuda\u0026quot;) + self.rank @register(Dispatch.ONE_TO_ALL) def add(self, x): self.value += x print(f\u0026#39;rank {self.rank}, value: {self.value}\u0026#39;) return self.value.cpu() class_with_args = RayClassWithInitArgs(GPUAccumulator) resource_pool = RayResourcePool([4], use_gpu=True) workergroup = RayWorkerGroup(resource_pool, class_with_args) print(workergroup.add(x=1)) # 输出：[tensor([1.]), tensor([2.]), tensor([3.]), tensor([4.])] 执行过程示意图（DriverProcess → WorkerGroup → ResourcePool → Worker）：\nGPUAccumulator 执行流程 2. 整体设计 为了实现上面的功能，VeRL 中抽象出了 Worker、WorkerGroup、ResourcePool、DriverProcess 的概念：\nWorker：基础执行单元 WorkerGroup：Worker 的集合，统一调度 ResourcePool：资源池 DriverProcess：驱动进程 下面是官方文档中这些概念的示例图：\nWorker/WorkerGroup/ResourcePool 概念图 后面的章节通过源码的方式深入理解这些概念。\n二、数据交换协议：DataProto DataProto 是 veRL 中各种功能之间用于数据交换的标准协议。\n1. 基础结构 # 原始代码位于 verl/protocol.py @dataclass class DataProto: batch: TensorDict = None non_tensor_batch: Dict = field(default_factory=dict) meta_info: Dict = field(default_factory=dict) ... DataProto 持有三种类型的数据：batch、non_tensor_batch 和 meta_info。通过命名可以看出，DataProto 是以 batch 的方式组织数据的。\n2. 使用示例 更多使用示例见 tests/utility/test_tensor_dict_utilities.py：\n# 构造 obs = torch.randn(100, 10) # batch_size 为 100 的 tensor labels = [random.choice([\u0026#39;abc\u0026#39;, \u0026#39;cde\u0026#39;]) for _ in range(100)] # 随机挑选 \u0026#39;abc\u0026#39; 和 \u0026#39;cde\u0026#39; 构造大小为 100 的 list dataset = DataProto.from_dict(tensors={\u0026#39;obs\u0026#39;: obs}, non_tensors={\u0026#39;labels\u0026#39;: labels}) # 实例化 # pop poped_dataset = dataset.pop(batch_keys=[\u0026#39;obs\u0026#39;]) # 将 batch 中的 \u0026quot;obs\u0026quot; 弹出，poped_dataset 仍为 DataProto 3. DataProtoFuture 在 verl/protocol.py 中除 DataProto 外，还定义了 Future 版本的 DataProtoFuture，保存了 ray.ObjectRef 对象列表 futures 以及两个函数 collect_fn 和 dispatch_fn：\n@dataclass class DataProtoFuture: collect_fn: Callable # 用于将 future 对象列表简化为一个 DataProto futures: List[ray.ObjectRef] dispatch_fn: Callable = None # 用于将 DataProto 划分为大小为 world_size 的多个 DataProto 列表 def get(self): output = ray.get(self.futures) for o in output: assert isinstance(o, DataProto) output = self.collect_fn(output) if self.dispatch_fn is not None: output = self.dispatch_fn(output) return output 三、基础执行单元：Worker Worker 负责执行具体任务的基础执行单元，保存分布式执行时必要的信息。具体的执行任务需要用户继承 Worker 类来实现。\n1. 代码解析 在 verl 中，通过 WorkerGroup 统一实例化并调度 Worker，概念上存在 master worker。先看单个 Worker。\n简单来说，Worker 类负责存储 world_size、rank 等分布式计算的相关信息并配置环境变量，Master Worker 相对于其他 Worker 需要额外保存 MASTER_ADDR 和 MASTER_PORT 的信息。简化后的代码：\n# 简化后代码，原始代码位于 verl/single_controller/base/worker.py class Worker(WorkerHelper): def __new__(cls, *args, **kwargs): instance = super().__new__(cls) rank = os.environ.get(\u0026quot;RANK\u0026quot;, None) worker_group_prefix = os.environ.get(\u0026quot;WG_PREFIX\u0026quot;, None) if None not in [rank, worker_group_prefix] and \u0026#39;ActorClass(\u0026#39; not in cls.__name__: instance._configure_before_init(f\u0026quot;{worker_group_prefix}_register_center\u0026quot;, int(rank)) return instance def _configure_before_init(self, register_center_name: str, rank: int): # rank=0 时，配置 MASTER_ADDR 和 MASTER_PORT 环境变量 if rank == 0: master_addr, master_port = self.get_availale_master_addr_port() rank_zero_info = { \u0026quot;MASTER_ADDR\u0026quot;: master_addr, \u0026quot;MASTER_PORT\u0026quot;: master_port, } if os.getenv(\u0026quot;WG_BACKEND\u0026quot;, None) == \u0026quot;ray\u0026quot;: from verl.single_controller.base.register_center.ray import create_worker_group_register_center self.register_center = create_worker_group_register_center(name=register_center_name, info=rank_zero_info) os.environ.update(rank_zero_info) def __init__(self, cuda_visible_devices=None) -\u0026gt; None: world_size = int(os.environ[\u0026#39;WORLD_SIZE\u0026#39;]) rank = int(os.environ[\u0026#39;RANK\u0026#39;]) self._rank = rank self._world_size = world_size master_addr = os.environ[\u0026quot;MASTER_ADDR\u0026quot;] master_port = os.environ[\u0026quot;MASTER_PORT\u0026quot;] local_world_size = int(os.getenv(\u0026quot;LOCAL_WORLD_SIZE\u0026quot;, \u0026quot;1\u0026quot;)) local_rank = int(os.getenv(\u0026quot;LOCAL_RANK\u0026quot;, \u0026quot;0\u0026quot;)) store = { \u0026#39;_world_size\u0026#39;: world_size, \u0026#39;_rank\u0026#39;: rank, \u0026#39;_local_world_size\u0026#39;: local_world_size, \u0026#39;_local_rank\u0026#39;: local_rank, \u0026#39;_master_addr\u0026#39;: master_addr, \u0026#39;_master_port\u0026#39;: master_port } # WorkerMeta 仅是将 store 信息存储在实例对象中 meta = WorkerMeta(store=store) # 将 meta(store) 信息更新到当前实例的 __dict__ 中并配置环境变量 self._configure_with_meta(meta=meta) 要点： - Worker 继承自 WorkerHelper，WorkerHelper 提供唯一的功能就是获得当前节点的 IP 地址和可用端口号。 - 核心方法包括 __new__ 和 __init__： - __new__ 针对 rank 0 配置 MASTER_ADDR 和 MASTER_PORT - __init__ 将 world_size、rank 保存在当前实例中\n2. 自定义 Worker 的例子 Worker 本身不执行具体计算任务，需要用户继承该类实现自定义计算任务。下面是一个模拟”数据并行 (DP) 前向”的例子：\nimport os import ray import time import torch import torch.nn as nn import torch.nn.functional as F from verl.single_controller.base import Worker class MyNet(nn.Module): def __init__(self): super(MyNet, self).__init__() self.linear = nn.Linear(100, 1) def compute_loss(self, x, y): pred = self.linear(x) loss = F.mse_loss(pred, y) return loss @ray.remote(num_gpus=1) class MyNetWorker(Worker): def __init__(self): super().__init__() self.network = MyNet() self.device = torch.device(f\u0026quot;cuda\u0026quot;) self.network.to(self.device) def compute_network_loss(self, inputs, targets): inputs = inputs.to(self.device) targets = targets.to(self.device) return self.network.compute_loss(inputs, targets).item() if __name__ == \u0026quot;__main__\u0026quot;: ray.init() options0 = {\u0026#39;runtime_env\u0026#39;: {\u0026#39;env_vars\u0026#39;: {\u0026#39;LOCAL_WORLD_SIZE\u0026#39;: \u0026#39;2\u0026#39;, \u0026#39;LOCAL_RANK\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;WORLD_SIZE\u0026#39;: \u0026#39;2\u0026#39;, \u0026#39;RANK\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;WG_PREFIX\u0026#39;: \u0026#39;test\u0026#39;, \u0026#39;WG_BACKEND\u0026#39;: \u0026#39;ray\u0026#39;}}} options1 = {\u0026#39;runtime_env\u0026#39;: {\u0026#39;env_vars\u0026#39;: {\u0026#39;LOCAL_WORLD_SIZE\u0026#39;: \u0026#39;2\u0026#39;, \u0026#39;LOCAL_RANK\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;WORLD_SIZE\u0026#39;: \u0026#39;2\u0026#39;, \u0026#39;RANK\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;WG_PREFIX\u0026#39;: \u0026#39;test\u0026#39;, \u0026#39;WG_BACKEND\u0026#39;: \u0026#39;ray\u0026#39;}}} # 初始化 Master Worker worker0 = MyNetWorker.options(**options0).remote() time.sleep(10) # 等待 register_center_actor 创建 register_center_actor = ray.get_actor(\u0026quot;test_register_center\u0026quot;) rank_zero_info = ray.get(register_center_actor.get_rank_zero_info.remote()) print(f\u0026quot;rank_zero_info: {rank_zero_info}\u0026quot;) # 更新 MASTER_ADDR 和 MASTER_PORT 至 options1，并创建 worker1 options1[\u0026#39;runtime_env\u0026#39;][\u0026#39;env_vars\u0026#39;].update(rank_zero_info) worker1 = MyNetWorker.options(**options1).remote() # 生成测试数据 batch_size = 16 inputs = torch.randn(batch_size, 100) targets = torch.randn(batch_size, 1) # 分割数据为两份 split_size = batch_size // 2 inputs_split = torch.chunk(inputs, 2) targets_split = torch.chunk(targets, 2) # 并行计算 future0 = worker0.compute_network_loss.remote(inputs_split[0], targets_split[0]) future1 = worker1.compute_network_loss.remote(inputs_split[1], targets_split[1]) # 获取结果并平均 loss0 = ray.get(future0) loss1 = ray.get(future1) avg_loss = (loss0 + loss1) / 2 print(f\u0026quot;Worker 0 Loss: {loss0:.4f}\u0026quot;) print(f\u0026quot;Worker 1 Loss: {loss1:.4f}\u0026quot;) print(f\u0026quot;Averaged Loss: {avg_loss:.4f}\u0026quot;) ray.shutdown() 3. 装饰器 在自定义 Worker 时，可以使用装饰器 register 扩展函数功能。为什么要用这个装饰器，在 [[#六、RayWorkerGroup]] 章节详细介绍。\n3.1 一个示例 import os import torch from verl.single_controller.base import Worker from verl.single_controller.base.decorator import register, Dispatch, Execute os.environ[\u0026#39;LOCAL_WORLD_SIZE\u0026#39;] = \u0026#39;2\u0026#39; os.environ[\u0026#39;LOCAL_RANK\u0026#39;] = \u0026#39;0\u0026#39; os.environ[\u0026#39;WORLD_SIZE\u0026#39;] = \u0026#39;2\u0026#39; os.environ[\u0026#39;RANK\u0026#39;] = \u0026#39;0\u0026#39; os.environ[\u0026#39;WG_PREFIX\u0026#39;] = \u0026#39;test\u0026#39; os.environ[\u0026#39;WG_BACKEND\u0026#39;] = \u0026#39;ray\u0026#39; class GPUAccumulatorDecorator(Worker): def __init__(self): super().__init__() self.value = torch.zeros(size=(1,), device=\u0026quot;cuda\u0026quot;) + self.rank @register(dispatch_mode=Dispatch.ONE_TO_ALL, execute_mode=Execute.RANK_ZERO) def add(self, x): self.value += x return self.value.cpu() worker = GPUAccumulatorDecorator() print(worker.add.attrs_3141562937) 输出：{'dispatch_mode': \u0026lt;Dispatch.ONE_TO_ALL: 1\u0026gt;, 'execute_mode': \u0026lt;Execute.RANK_ZERO: 1\u0026gt;, 'blocking': True}\n所以装饰器 register 的核心功能就是给函数添加一个新属性 attrs_3141562937，该属性保存 dispatch_mode 等信息。\n3.2 源码解析 def register(dispatch_mode=Dispatch.ALL_TO_ALL, execute_mode=Execute.ALL, blocking=True, materialize_futures=True): _check_dispatch_mode(dispatch_mode=dispatch_mode) _check_execute_mode(execute_mode=execute_mode) def decorator(func): @wraps(func) def inner(*args, **kwargs): if materialize_futures: # _materialize_futures 将 future 转换为实际的数据 args, kwargs = _materialize_futures(*args, **kwargs) return func(*args, **kwargs) # 为被装饰函数新增一个 MAGIC_ATTR 属性 attrs = {\u0026#39;dispatch_mode\u0026#39;: dispatch_mode, \u0026#39;execute_mode\u0026#39;: execute_mode, \u0026#39;blocking\u0026#39;: blocking} setattr(inner, MAGIC_ATTR, attrs) return inner return decorator register 仅包含两个功能： 1. 若参数是 future，则获得实际的值 2. 为被装饰函数添加 MAGIC_ATTR 属性\n四、RayClassWithInitArgs RayClassWithInitArgs 保存了通过 @ray.remote 定义的 Actor 类，以及一些用于异步调用该 Actor 时所需要的参数。更像是一个工厂类，在调用 __call__ 时再启动 Actor。\n# 简化后代码，原始代码位于 verl/single_controller/ray/base.py class RayClassWithInitArgs(ClassWithInitArgs): def __init__(self, cls, *args, **kwargs) -\u0026gt; None: super().__init__(cls, *args, **kwargs) # 保存 Actor self._options = {} self._additional_resource = {} def __call__(self, placement_group, placement_group_bundle_idx, use_gpu: bool = True, num_gpus=1, sharing_with=None) -\u0026gt; Any: # 调度策略 options = { \u0026quot;scheduling_strategy\u0026quot;: PlacementGroupSchedulingStrategy( placement_group=placement_group, placement_group_bundle_index=placement_group_bundle_idx)} # 更新 actor 启动的一些参数 options.update(self._options) if len(self._additional_resource) \u0026gt; 1: for k, v in self._additional_resource.items(): options[k] = v # Ray 中异步启动 Actor return self.cls.options(**options).remote(*self.args, **self.kwargs) 五、资源池：RayResourcePool 1. Ray 中的 Placement Group Placement Group 是 Ray 中的资源组概念，允许用户跨多个节点原子性地保留一组资源。创建时需指定 bundles，单个 bundle 是一个”资源”的集合，也是 Placement Group 的资源预留单位。\n例如，包含 8 GPU 16 CPU 的节点，想用一半资源：\nimport ray from ray.util.placement_group # 创建包含 8GPU、16CPU 的 Ray 集群 ray.init(num_cpus=16, num_gpus=8) # 创建包含 4 个 bundle 的 placement group，共使用一半资源 pg = placement_group(bundles=[{\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}]) # 等待资源组创建完成 ray.get(pg.ready(), timeout=10) 2. RayResourcePool RayResourcePool 继承自 ResourcePool，ResourcePool 负责存储资源相关信息，RayResourcePool 则通过 Ray 的 Placement Group 实现资源池分配。\nRayResourcePool 与 PlacementGroup 的关系 resource_pool = RayResourcePool(process_on_nodes=[4, 4], max_colocate_count=2, use_gpu=True) # 创建资源池 pgs = resource_pool.get_placement_groups() # 创建 placement group 列表 该段代码会创建 两个 Placement Group，每个包含 4 个 GPU 和 8 个 CPU。单个 Placement Group 为：\npg = placement_group(bundles=[{\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}, {\u0026quot;CPU\u0026quot;: 2, \u0026quot;GPU\u0026quot;: 1}]) 即 process_on_nodes 指定要创建几个 Placement Group 以及每个包含多少 GPU。max_colocate_count 是 bundle 中单个 GPU 对应多少个 CPU，因为 colocate 的 actor 至少要有 1 个 CPU。\n六、RayWorkerGroup RayWorkerGroup 扮演着资源分配和调度 Worker 的协调器角色，核心方法包括：_init_with_resource_pool、_bind_worker_method 和 execute_all_async。\n__init__ 中核心两件事： 1. 在指定的资源池上启动 worker 2. 将 worker 的某些方法绑定到 workergroup 上，方便直接通过 workergroup 进行调用\n# 简化后代码，原始代码位于 verl/single_controller/ray/base.py class RayWorkerGroup(WorkerGroup): def __init__(self, resource_pool: RayResourcePool = None, ray_cls_with_init: RayClassWithInitArgs = None, bin_pack: bool = True, name_prefix: str = None, detached=False, worker_names=None, **kwargs) -\u0026gt; None: super().__init__(resource_pool=resource_pool, **kwargs) self.ray_cls_with_init = ray_cls_with_init ... # 基于 resource_pool 信息，启动 worker self._init_with_resource_pool(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init, bin_pack=bin_pack, detached=detached) # ray_cls_with_init.cls 中的某些方法绑定到 RayWorkerGroup 上 self._bind_worker_method(self.ray_cls_with_init.cls, func_generator) 1. 启动 Workers：_init_with_resource_pool # 简化后代码，原始代码位于 verl/single_controller/ray/base.py def _init_with_resource_pool(self, resource_pool, ray_cls_with_init): # max_collocate_count 意味着单个 GPU 上至多有几个 worker num_gpus = 1 / resource_pool.max_collocate_count # resource_pool.store: [4, 4] for pg_idx, local_world_size in enumerate(resource_pool.store): pg = pgs[pg_idx] # 当前的 PlacementGroup for local_rank in range(local_world_size): # 1. 构造 worker 需要的配置信息，包括环境变量等 # 2. 通过 ray_cls_with_init.update_options 更新这些配置信息 # 启动 worker worker = ray_cls_with_init(placement_group=pg, placement_group_bundle_idx=local_rank, num_gpus=num_gpus) self._workers.append(worker) self._worker_names.append(name) 2. 异步执行：execute_all_async _init_with_resource_pool 后，self._workers 中保存着所有 worker。当需要调用所有 worker 某个方法时，可以直接使用 execute_all_async。\n假设有 4 个 worker，需要每个 worker 都执行方法 add，参数为 10：\nworkergroup.execute_all_async(\u0026quot;add\u0026quot;, x=10) # 或者 workergroup.execute_all_async(\u0026quot;add\u0026quot;, x=[10, 10, 10, 10]) 但用 execute_all_async 调用 worker 方法并不自然，所以利用装饰器 register 和 _bind_worker_method 让调用更自然。\n3. worker 方法绑定至 workergroup：_bind_worker_method _bind_worker_method 来自基类 WorkerGroup，参数包含 user_defined_cls 和 func_generator，其中 user_defined_cls 就是用户自定义的 worker 类。\n3.1 func_generator # 原始代码位于 verl/single_controller/ray/base.py def func_generator(self, method_name, dispatch_fn, collect_fn, execute_fn, blocking): def func(*args, **kwargs): args, kwargs = dispatch_fn(self, *args, **kwargs) output = execute_fn(method_name, *args, **kwargs) if blocking: output = ray.get(output) output = collect_fn(self, output) return output return func func_generator 的功能是构造一个函数。method_name 实际就是自定义 worker 中使用装饰器 register 的函数。构造的新函数会： 1. 先对参数调用 dispatch_fn 2. 然后执行 method_name 方法 3. 最后用 collect_fn 产生最终输出\n3.2 _bind_worker_method 源码 # 简化代码，原始代码位于 verl/single_controller/base/worker_group.py def _bind_worker_method(self, user_defined_cls, func_generator): for method_name in dir(user_defined_cls): # 遍历类的所有方法 if hasattr(method, MAGIC_ATTR): # 寻找使用装饰器 `register` 的方法 # 获取 dispatch_fn、collect_fn 和 execut_fn dispatch_mode = attribute[\u0026#39;dispatch_mode\u0026#39;] execute_mode = attribute[\u0026#39;execute_mode\u0026#39;] blocking = attribute[\u0026#39;blocking\u0026#39;] fn = get_predefined_dispatch_fn(dispatch_mode=dispatch_mode) dispatch_fn = fn[\u0026#39;dispatch_fn\u0026#39;] collect_fn = fn[\u0026#39;collect_fn\u0026#39;] execute_mode = get_predefined_execute_fn(execute_mode=execute_mode) wg_execute_fn_name = execute_mode[\u0026#39;execute_fn_name\u0026#39;] execute_fn = getattr(self, wg_execute_fn_name) # 利用 func_generator 将 dispatch_fn、collect_fn 组装到 method_name 上 func = func_generator(self, method_name, dispatch_fn=dispatch_fn, collect_fn=collect_fn, execute_fn=execute_fn, blocking=blocking) # func 绑定到 workgroup 上，func 名字仍保持为 method_name setattr(self, method_name, func) 参考资料 verl 官方文档 - Hybrid Flow HybridFlow: A Flexible and Efficient RLHF Framework veRL GitHub 仓库 VeRL(v0.2.0.post2)系列文章 相关笔记 [[Engineering/coding/verl/ray前置知识]] [[Engineering/coding/verl/verl总体概览]] [[Engineering/coding/verl/init_workers详解]] [[Engineering/coding/verl/veRL_colocate实现源码解析]] ","date":"2026-06-04T00:00:00Z","permalink":"/verl_ray%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/","title":"veRL基于Ray的执行流程源码解析（一）"},{"content":" 来源：【AI Infra】【RLHF框架】二、VeRL中colocate实现源码解析（作者：白强伟）\n前置知识： - [[Engineering/coding/verl/ray前置知识]] - [[Engineering/coding/verl/veRL_Ray执行流程源码解析]]\ncolocate 的作用是使多个 Worker 共享相同的资源池。目前版本 verl 中所有模型的 Worker 都共享相同的资源池：global_pool。这篇博客主要通过例子和源代码理解 verl 中 colocate 的实现。\n一、一个例子 下面简单修改了 verl 的单元测试作为示例，先直观感受下 colocate 的作用：\nimport ray from verl.single_controller.base import Worker from verl.single_controller.base.decorator import register, Dispatch from verl.single_controller.ray.base import RayResourcePool, RayClassWithInitArgs, RayWorkerGroup, create_colocated_worker_cls from verl import DataProto @ray.remote class Actor(Worker): def __init__(self) -\u0026gt; None: super().__init__() @register(dispatch_mode=Dispatch.DP_COMPUTE_PROTO) def add(self, data: DataProto): data.batch[\u0026#39;a\u0026#39;] = data.batch[\u0026#39;a\u0026#39;].to(\u0026quot;cuda\u0026quot;) data.batch[\u0026#39;a\u0026#39;] += self.rank return data @ray.remote class Critic(Worker): def __init__(self, config) -\u0026gt; None: super().__init__() self.config = config @register(dispatch_mode=Dispatch.DP_COMPUTE_PROTO) def sub(self, data: DataProto): data.batch[\u0026#39;a\u0026#39;] = data.batch[\u0026#39;a\u0026#39;].to(\u0026quot;cuda\u0026quot;) data.batch[\u0026#39;a\u0026#39;] -= self.config[\u0026#39;b\u0026#39;] return data def test_colocated_workers(): ray.init() import torch # 构建一个 DataProto，其中属性 a 是维度为 10 的零向量 data = DataProto.from_dict({\u0026#39;a\u0026#39;: torch.zeros(10)}) print(data.batch[\u0026quot;a\u0026quot;]) # 利用 RayClassWithInitArgs 将自定义的 worker 和参数封装起来 actor_cls = RayClassWithInitArgs(cls=Actor) critic_cls = RayClassWithInitArgs(cls=Critic, config={\u0026#39;b\u0026#39;: 10}) # 定义资源池，仅包含一个 2 GPU 的节点 resource_pool = RayResourcePool(process_on_nodes=[2]) # 利用 create_colocated_worker_cls 将自定义的两个 worker 绑定到 WorkerDict 上 cls_dict = {\u0026#39;actor\u0026#39;: actor_cls, \u0026#39;critic\u0026#39;: critic_cls} ray_cls_with_init = create_colocated_worker_cls(cls_dict) # 启动 WorkerDict wg_dict = RayWorkerGroup(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init) # 分别获取 actor 和 critic 的 workergroup spawn_wg = wg_dict.spawn(prefix_set=cls_dict.keys()) colocated_actor_wg = spawn_wg[\u0026#39;actor\u0026#39;] colocated_critic_wg = spawn_wg[\u0026#39;critic\u0026#39;] # actor 执行 add、critic 执行 sub actor_output = colocated_actor_wg.add(data) critic_output = colocated_critic_wg.sub(data) # actor_output.batch[\u0026quot;a\u0026quot;] == [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # critic_output.batch[\u0026quot;a\u0026quot;] == [-10, -10, -10, -10, -10, -10, -10, -10, -10, -10] print(actor_output.batch[\u0026quot;a\u0026quot;]) print(critic_output.batch[\u0026quot;a\u0026quot;]) ray.shutdown() if __name__ == \u0026#39;__main__\u0026#39;: test_colocated_workers() 1. Actor 和 Critic 的解释 Actor 和 Critic 的定义比较简单，在 add 和 sub 方法上使用了装饰器 @register(dispatch_mode=Dispatch.DP_COMPUTE_PROTO)。\n这个装饰器的作用就是 数据并行，即将数据 chunk 后交由定义 worker 的两个实例分别计算，再将结果合并。\n下面是 Actor 执行过程的示意图，Critic 类似：\nActor DP 流程：dispatch -\u0026gt; rank(0) + rank(1) -\u0026gt; collect 2. Actor 和 Critic 绑定至 WorkerDict 通过打印一些元信息，可知 ray_cls_with_init 是 RayClassWithInitArgs，其中持有的 cls 是一个定义为 WorkerDict 的类，其基类同样是 Worker：\ncls_dict = {\u0026#39;actor\u0026#39;: actor_cls, \u0026#39;critic\u0026#39;: critic_cls} ray_cls_with_init = create_colocated_worker_cls(cls_dict) print(type(ray_cls_with_init)) # RayClassWithInitArgs print(ray_cls_with_init.cls.__ray_actor_class__) # WorkerDict print(ray_cls_with_init.cls.__ray_actor_class__.__base__) # Worker print(ray_cls_with_init.cls.actor_add) print(ray_cls_with_init.cls.critic_sub) create_colocated_worker_cls 的工作过程示意图：\nRayClassWithInitArgs -\u0026gt; create_colocated_worker_cls -\u0026gt; WorkerDict 3. 启动 WorkerDict 并执行操作 # 启动 WorkerDict wg_dict = RayWorkerGroup(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init) spawn_wg = wg_dict.spawn(prefix_set=cls_dict.keys()) # 获得 actor 和 critic 的 RayWorkerGroup colocated_actor_wg = spawn_wg[\u0026#39;actor\u0026#39;] colocated_critic_wg = spawn_wg[\u0026#39;critic\u0026#39;] # 执行 actor_output = colocated_actor_wg.add(data) critic_output = colocated_critic_wg.sub(data) 通过 RayWorkerGroup 启动 WorkerDict。spawn 方法返回一个 RayWorkerGroup 的字典：\n{\u0026#39;actor\u0026#39;: \u0026lt;verl.single_controller.ray.base.RayWorkerGroup object at 0x7f2efc719790\u0026gt;, \u0026#39;critic\u0026#39;: \u0026lt;verl.single_controller.ray.base.RayWorkerGroup object at 0x7f2f0e125dd0\u0026gt;} 获得 actor 和 critic 的 RayWorkerGroup 后直接执行操作即可。\n二、create_colocated_worker_cls 源码解析 简化后的源码：\n# 原始代码位于 verl/single_controller/ray/base.py def create_colocated_worker_cls(class_dict: dict[str, RayClassWithInitArgs]): # {\u0026quot;actor\u0026quot;: Actor, \u0026quot;critic\u0026quot;: Critic} cls_dict = {} # {\u0026#39;actor\u0026#39;: {\u0026#39;args\u0026#39;: (...), \u0026#39;kwargs\u0026#39;: {}}, \u0026#39;critic\u0026#39;: {\u0026#39;args\u0026#39;: (...), \u0026#39;kwargs\u0026#39;: {...}}} init_args_dict = {} # worker_cls 是指 Actor 和 Critic，其实就是 Worker worker_cls = None for key, cls in class_dict.items(): worker_cls = cls.cls.__ray_actor_class__.__base__ cls_dict[key] = cls.cls init_args_dict[key] = {\u0026#39;args\u0026#39;: cls.args, \u0026#39;kwargs\u0026#39;: cls.kwargs} class WorkerDict(worker_cls): def __init__(self): super().__init__() self.worker_dict = {} for key, user_defined_cls in cls_dict.items(): # 去除掉 ray.remote 的包装，这里 user_defined_cls 就是 Actor 和 Critic 两个类 user_defined_cls = _unwrap_ray_remote(user_defined_cls) with patch.dict(os.environ, {\u0026#39;DISABLE_WORKER_INIT\u0026#39;: \u0026#39;1\u0026#39;}): self.worker_dict[key] = user_defined_cls( *init_args_dict[key].get(\u0026#39;args\u0026#39;, ()), **init_args_dict[key].get(\u0026#39;kwargs\u0026#39;, {})) for key, user_defined_cls in cls_dict.items(): user_defined_cls = _unwrap_ray_remote(user_defined_cls) _bind_workers_method_to_parent(WorkerDict, key, user_defined_cls) remote_cls = ray.remote(WorkerDict) remote_cls = RayClassWithInitArgs(cls=remote_cls) return remote_cls 1. 类 WorkerDict WorkerDict 是定义在 create_colocated_worker_cls 内部的类，其初始化方法 __init__ 中核心就是构建 self.worker_dict。\n在本文例子中，key 是 actor 和 critic，对应的 value 就是 Actor 和 Critic 的实例。\n关键点：使用 patch.dict(os.environ, {'DISABLE_WORKER_INIT': '1'}) 来 抑制子 worker 的初始化，避免重复执行 super().__init__() 中的环境变量配置 / register_center 创建等副作用，因为这些工作由 WorkerDict 自己（作为 master）已经完成。\n2. _bind_workers_method_to_parent 一句话说明：将 user_defined_cls 中使用装饰器 register 的方法绑定到 WorkerDict，key 是方法绑定至 WorkerDict 的前缀。\n# 原始代码位于 verl/single_controller/ray/base.py def _bind_workers_method_to_parent(cls, key, user_defined_cls): for method_name in dir(user_defined_cls): if hasattr(method, MAGIC_ATTR): # 遍历 user_defined_cls 的所有方法，找到使用装饰器 `register` 装饰的方法 def generate_function(name): def func(self, *args, **kwargs): # dispatch to the actual worker return getattr(self.worker_dict[key], name)(*args, **kwargs) return func func = generate_function(method_name) # 将原始函数 `add` 和 `sub` 的 MAGIC_ATTR 绑定到 func 上 setattr(func, MAGIC_ATTR, getattr(method, MAGIC_ATTR)) try: method_name_with_prefix = key + \u0026#39;_\u0026#39; + method_name setattr(cls, method_name_with_prefix, func) except Exception as e: raise ValueError(f\u0026#39;Fail to set method_name {method_name}\u0026#39;) _bind_workers_method_to_parent 实现可能比较难理解一些，先跳过 generate_function。\nsetattr(func, MAGIC_ATTR, getattr(method, MAGIC_ATTR)) 这行代码主要是将 add 或 sub 的装饰器提供的信息复制到 func 上。\nmethod_name_with_prefix = key + \u0026#39;_\u0026#39; + method_name setattr(cls, method_name_with_prefix, func) 这段代码就是将 Actor 的方法 add 或 Critic 的方法 sub 绑定至 WorkerDict 上。\n理解 generate_function 的等价代码：\n将 add 绑定到 WorkerDict 后，等价于：\nclass WorkerDict(worker_cls): def __init__(self): ... def actor_add(self, *args, **kwargs): return getattr(self.worker_dict[\u0026quot;actor\u0026quot;], \u0026quot;add\u0026quot;)(*args, **kwargs) def critic_sub(self, *args, **kwargs): return getattr(self.worker_dict[\u0026quot;critic\u0026quot;], \u0026quot;sub\u0026quot;)(*args, **kwargs) 三、spawn 的作用 回顾一下开始例子中的这段代码：\nwg_dict = RayWorkerGroup(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init) spawn_wg = wg_dict.spawn(prefix_set=cls_dict.keys()) print(spawn_wg[\u0026quot;actor\u0026quot;].workers) print(spawn_wg[\u0026quot;critic\u0026quot;].workers) wg_dict = RayWorkerGroup(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init) 这行代码会启动两个 WorkerDict 的远程实例。\n注意：这里不使用 Ray 中 actor 的称呼，因为和示例中的 actor 会混淆。\n在调用 spawn 后返回字典 spawn_wg，打印 spawn_wg[\"actor\"].workers 和 spawn_wg[\"critic\"].workers 可以发现 这两个 workergroup 中持有的 workers 都是相同的（都是同一组 WorkerDict 实例）。\n那么 spawn 的作用是什么？看下源码：\ndef spawn(self, prefix_set): def _rebind_actor_methods(worker_group, actor_name): prefix: str = actor_name + \u0026#39;_\u0026#39; for method_name in dir(worker_group): if method_name.startswith(prefix): # only valid when Python \u0026gt;= 3.9 original_method_name = method_name.removeprefix(prefix) method = getattr(worker_group, method_name) setattr(worker_group, original_method_name, method) new_worker_group_dict = {} for prefix in prefix_set: # 从现有的 workers 中选出名字为 self._worker_names 的 worker 并构成新的 RayWorkerGroup new_worker_group = self.from_detached(worker_names=self._worker_names, ray_cls_with_init=self.ray_cls_with_init) # 将带有前缀的方法名移除前缀。例如：`actor_add` -\u0026gt; `add` _rebind_actor_methods(new_worker_group, prefix) new_worker_group_dict[prefix] = new_worker_group return new_worker_group_dict self.from_detached 作用是利用现有的 worker 构造一个新的 RayWorkerGroup，其并不会启动新的 Worker。 _rebind_actor_methods 则是将 actor_add 这种带前缀的方法名改为 add，然后将 add 绑定到新的 RayWorkerGroup 上。 所以，spawn 的作用就是保证可以像非 colocate 那样来执行具体的功能——返回的 spawn_wg['actor'] 表面上是一个独立的 RayWorkerGroup（只持有 actor 相关的 add 方法），但底层 worker 仍共享同一份 WorkerDict。\n参考资料 verl 官方文档 - Hybrid Flow HybridFlow: A Flexible and Efficient RLHF Framework veRL GitHub 仓库 VeRL(v0.2.0.post2)系列文章 相关笔记 [[Engineering/coding/verl/ray前置知识]] [[Engineering/coding/verl/veRL_Ray执行流程源码解析]] [[Engineering/coding/verl/verl总体概览]] [[Engineering/coding/verl/init_workers详解]] [[Engineering/coding/verl/DataProto]] ","date":"2026-06-04T00:00:00Z","permalink":"/verl_colocate%E5%AE%9E%E7%8E%B0%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/","title":"veRL中colocate实现源码解析（二）"},{"content":" 整理自 Claude Code 官方 Hooks 参考。原文是逐字段罗列的字典式文档，这里压缩成”什么时候用什么、怎么决定”的速查笔记。第一次配置 hook 前推荐先看 使用 hooks 自动化工作流 指南。\nClaude Code Hooks 笔记 Hooks 是挂在 Claude Code 生命周期事件上的回调：可以跑 shell 命令、HTTP 请求、调用 MCP 工具、或让 LLM 帮判断。本质是”事件 → 过滤 → 副作用/决定”的三段式管道。\n按触发频率分三档：\n每个会话一次：SessionStart、SessionEnd、Setup、InstructionsLoaded 每轮对话一次：UserPromptSubmit、UserPromptExpansion、Stop、StopFailure、PreCompact、PostCompact 代理循环内每个工具调用：PreToolUse、PostToolUse、PostToolUseFailure、PostToolBatch、PermissionRequest、PermissionDenied、SubagentStart、SubagentStop、TaskCreated、TaskCompleted、Notification、CwdChanged、FileChanged、ConfigChange、WorktreeCreate、WorktreeRemove、MessageDisplay、Elicitation、ElicitationResult 另外有些事件是异步独立触发：TeammateIdle（agent team 队友闲置）、ConfigChange、CwdChanged、FileChanged、WorktreeCreate/Remove、Notification。\n生命周期全景 Hook 生命周期 上图里几个要点：\nSetup（可选）→ SessionStart → 每轮循环（UserPromptSubmit → Agentic Loop → Stop / StopFailure）→ SessionEnd Agentic Loop 里嵌套 PreToolUse → PermissionRequest → 工具执行 → PostToolUse / PostToolUseFailure → PostToolBatch Subagent 循环是平行的：SubagentStart / SubagentStop、TaskCreated / TaskCompleted 灰色虚线的事件（Notification、ConfigChange、WorktreeCreate 等）是异步副作用，不在主路径上 Elicitation / ElicitationResult 嵌在 MCP 工具执行里 MessageDisplay 是只读显示事件，不影响 Claude 行为 解析流程 Hook 解析流程 一个 hook 要真正跑起来，三层过滤缺一不可：\n事件触发：Claude Code 决定发事件（如要执行 Bash） 匹配器检查：matcher 字段过滤事件（如只对 Bash 工具） if 条件检查：if 用权限规则语法再过滤一次（如 Bash(rm *)） 执行 handler：从 stdin 收 JSON，决定如何回应 下面这个例子同时演示了三层结构 + 通过 JSON permissionDecision: \"deny\" 阻止调用：\n{ \u0026quot;hooks\u0026quot;: { \u0026quot;PreToolUse\u0026quot;: [ { \u0026quot;matcher\u0026quot;: \u0026quot;Bash\u0026quot;, \u0026quot;hooks\u0026quot;: [ { \u0026quot;type\u0026quot;: \u0026quot;command\u0026quot;, \u0026quot;if\u0026quot;: \u0026quot;Bash(rm *)\u0026quot;, \u0026quot;command\u0026quot;: \u0026quot;${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh\u0026quot; } ] } ] } } handler 通过退出码和 stdout 表达决定（详见 决定控制）。\n配置位置 位置 范围 可共享 ~/.claude/settings.json 所有项目 否（用户级） .claude/settings.json 单个项目 是（提交到仓库） .claude/settings.local.json 单个项目 否（gitignored） 托管策略设置 组织 是（管理员控制） Plugin hooks/hooks.json 启用插件时 是 Skill / Subagent frontmatter 组件活跃时 是 临时禁用所有 hook：在任意 settings 文件里设 \"disableAllHooks\": true。托管策略里的 hook 不受用户/项目设置里的 disableAllHooks 影响——只有托管级才能禁托管 hook。/hooks 菜单可读不能改。\nHandler 类型 配置里 hooks 数组每个对象是一个 handler，有五种类型：\ntype 行为 怎么收输入 怎么传结果 command 跑 shell 命令 stdin 收 JSON 退出码 + stdout/stderr http POST 到 URL 请求体收 JSON 状态码 + 响应体 mcp_tool 调已连接 MCP 服务器的工具 按工具 schema 工具文本输出 prompt 送 LLM 单轮评估 提示里 $ARGUMENTS 占位 LLM 返 {ok, reason} agent 起 subagent 验证（实验性） 提示里 $ARGUMENTS 占位 LLM 返 {ok, reason} 并非所有事件都支持所有类型。SessionStart / Setup 只支持 command 和 mcp_tool。prompt 和 agent 只能在能”做决定”的事件上用（PreToolUse、PostToolUse、Stop、UserPromptSubmit 等），不能在纯副作用事件上用。\n通用字段 字段 必填 说明 type 是 上面五种之一 if 否 权限规则语法过滤，如 Bash(git *)、Edit(*.ts)。只在工具事件上评估 timeout 否 秒数。默认 command/http/mcp_tool=600，prompt=30，agent=60 statusMessage 否 hook 跑时显示的加载提示 once 否 每会话只跑一次（仅在 skill frontmatter 的 hook 里生效） 命令 hook 的 exec vs shell 形式 设了 args 走 exec 形式：command 解析为可执行文件，args 是参数向量，无 shell 介入。引号、空格、$ 都原样传递。路径占位符必须用 exec 形式（${CLAUDE_PROJECT_DIR} 等不会被 shell 破坏）。\n{ \u0026quot;type\u0026quot;: \u0026quot;command\u0026quot;, \u0026quot;command\u0026quot;: \u0026quot;node\u0026quot;, \u0026quot;args\u0026quot;: [\u0026quot;${CLAUDE_PROJECT_DIR}/scripts/format.js\u0026quot;, \u0026quot;--fix\u0026quot;] } 没设 args 走 shell 形式：command 整体交给 sh -c 解释，能用管道、\u0026amp;\u0026amp;、变量展开。\n{ \u0026quot;type\u0026quot;: \u0026quot;command\u0026quot;, \u0026quot;command\u0026quot;: \u0026quot;node \\\u0026quot;${CLAUDE_PROJECT_DIR}\\\u0026quot;/scripts/format.js --fix\u0026quot; } Windows 上：exec 形式需要真实 .exe，npm 垫片（.cmd/.bat）必须走 shell 形式或 node 直调。设 \"shell\": \"powershell\" 可在 PowerShell 里跑单条 hook。\n路径占位符 ${CLAUDE_PROJECT_DIR}：项目根 ${CLAUDE_PLUGIN_ROOT}：插件安装目录（每次更新会变） ${CLAUDE_PLUGIN_DATA}：插件持久数据目录（更新后保留） 匹配器（matcher） 不同事件匹配不同字段：\n工具事件（PreToolUse/PostToolUse/PostToolUseFailure/PermissionRequest/PermissionDenied）：匹配 tool_name SessionStart：匹配 source（startup/resume/clear/compact） SessionEnd：匹配退出原因（clear/resume/logout/prompt_input_exit/bypass_permissions_disabled/other） Notification：匹配通知类型（permission_prompt/idle_prompt/auth_success/elicitation_*） SubagentStart/SubagentStop：匹配代理类型（general-purpose/Explore/Plan/自定义名） PreCompact/PostCompact：匹配 manual/auto ConfigChange：匹配 user_settings/project_settings/local_settings/policy_settings/skills FileChanged：字面文件名（用 | 分隔，如 .envrc|.env），不遵循下面的通用规则 StopFailure：匹配错误类型（rate_limit/authentication_failed/billing_error 等） InstructionsLoaded：匹配加载原因（session_start/nested_traversal/compact 等） UserPromptExpansion：匹配命令名 Elicitation/ElicitationResult：匹配 MCP 服务器名 不支持 matcher、永远全触发：UserPromptSubmit、PostToolBatch、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove、MessageDisplay、CwdChanged。给这些事件加 matcher 字段会被静默忽略。\n匹配器语法：\n\"*\"、\"\" 或省略：匹配所有 只有字母/数字/_/|：精确字符串或 | 列表（Bash、Edit|Write） 含其他字符：JS 正则（^Notebook、mcp__memory__.*） MCP 工具命名是 mcp__\u0026lt;server\u0026gt;__\u0026lt;tool\u0026gt;，匹配整个 server 用 mcp__\u0026lt;server\u0026gt;__.*（末尾的 .* 必须有，否则按字面精确匹配）。\nif 字段在同一 handler 上做更窄的二次过滤，权限规则语法：Bash(rm *)、Edit(*.ts)。注意只支持单条规则，不支持 \u0026amp;\u0026amp;/||，要多个条件就写多个 handler。\n事件清单（按生命周期） 事件 何时触发 典型用途 SessionStart 会话开始/恢复/clear/compact 加载上下文、设环境变量、注入项目状态 Setup --init-only / --init / --maintenance CI 一次性准备 InstructionsLoaded CLAUDE.md 或 .claude/rules/*.md 加载 跟踪记忆文件被加载 UserPromptSubmit 用户提交 prompt，Claude 处理前 注入上下文、过滤敏感词 UserPromptExpansion slash command 展开为 prompt 前 拦截 / 改写 / 阻止命令 PreToolUse 工具执行前 校验输入、阻止危险命令、改写参数 PermissionRequest 权限弹窗出现时 自动批准 / 拒绝 PermissionDenied 自动模式分类器拒绝 记录、告诉模型可重试 PostToolUse 工具成功后 检查结果、改写输出、加上下文 PostToolUseFailure 工具失败后 告警、向 Claude 反馈错误上下文 PostToolBatch 一批并行工具全部完成后、下次模型调用前 基于整批结果注入上下文 SubagentStart / SubagentStop subagent 启动 / 结束 计时、注入子代理上下文 TaskCreated / TaskCompleted 任务创建 / 标记完成 跟踪任务状态 Stop Claude 自然完成回复 强制继续、追加任务 StopFailure API 错误导致轮次结束 告警 TeammateIdle agent team 队友要闲置 阻止闲置、让其继续 Notification 通知发送 桌面通知、提醒 MessageDisplay 助手消息文本流式显示 改写展示文本（不改变 Claude 看到的内容） CwdChanged 工作目录变化 direnv 类的响应式环境 FileChanged 被监视的文件磁盘上变化 文件变更触发后续动作 ConfigChange 配置文件变化 跟踪配置 WorktreeCreate --worktree 或 isolation: \"worktree\" 自定义 worktree 路径 WorktreeRemove worktree 移除 清理 PreCompact 压缩前 阻止压缩 PostCompact 压缩后 注入压缩后上下文 SessionEnd 会话终止 清理 Elicitation MCP 服务器在工具调用中请求用户输入 编程方式响应、跳过对话框 ElicitationResult 用户回应 MCP 询问后 观察 / 改写 / 阻止响应 输入输出 通用输入字段 每条 hook 通过 stdin（命令）/ POST body（HTTP）/ 工具参数（MCP）收到 JSON：\n{ \u0026quot;session_id\u0026quot;: \u0026quot;abc123\u0026quot;, \u0026quot;transcript_path\u0026quot;: \u0026quot;/.../.claude/projects/.../transcript.jsonl\u0026quot;, \u0026quot;cwd\u0026quot;: \u0026quot;/home/user/project\u0026quot;, \u0026quot;permission_mode\u0026quot;: \u0026quot;default\u0026quot;, \u0026quot;effort\u0026quot;: { \u0026quot;level\u0026quot;: \u0026quot;high\u0026quot; }, \u0026quot;hook_event_name\u0026quot;: \u0026quot;PreToolUse\u0026quot;, \u0026quot;tool_name\u0026quot;: \u0026quot;Bash\u0026quot;, \u0026quot;tool_input\u0026quot;: { \u0026quot;command\u0026quot;: \u0026quot;npm test\u0026quot; } } --agent 启动或 subagent 内触发还会多 agent_id、agent_type。SessionStart 还会多 model 字段（不是环境变量）。\ntool_name / tool_input 是事件特定字段，每个事件能拿到什么见 原文事件章节。\n退出码语义 0：成功。stdout 被解析为 JSON（可选），除 UserPromptSubmit / UserPromptExpansion / SessionStart 外，stdout 不进对话 2：阻止错误。stderr 进 Claude 上下文作为错误信息，stdout 被忽略 其他：非阻止错误。WorktreeCreate 例外——任何非零都会中止 退出 2 能阻止什么 事件 退出 2 效果 PreToolUse 阻止工具调用 PermissionRequest 拒绝权限 UserPromptSubmit 阻止并从上下文删 prompt UserPromptExpansion 阻止命令展开 Stop / SubagentStop 阻止停止，继续对话 TeammateIdle 阻止队友闲置 TaskCreated 回滚任务创建 TaskCompleted 阻止标完成 ConfigChange 阻止配置生效（policy_settings 除外） PostToolBatch 在下次模型调用前停代理循环 PreCompact 阻止压缩 Elicitation 拒绝询问 ElicitationResult 操作变为 decline 已发生的事件不能阻止：PostToolUse / PostToolUseFailure / Notification / SessionStart / Setup / SessionEnd / CwdChanged / FileChanged / PostCompact / InstructionsLoaded / MessageDisplay / WorktreeRemove / StopFailure —— 退出 2 只会让 stderr 展示给用户。PermissionDenied 不能用退出码阻止，但能用 JSON retry: true 告诉模型”可以再试一次”。\nJSON 输出（精细控制） 退出 0 时往 stdout 打 JSON，得到比退出码更细的语义。只支持一种模式：要么只用退出码，要么用 JSON——不能混。stdout 必须只含 JSON，否则 shell 启动输出会破坏解析。\n通用字段：\n字段 默认 说明 continue true false 强制停止 Claude stopReason - continue: false 时给用户看的消息 suppressOutput false 隐藏 stdout systemMessage - 给用户看的警告 terminalSequence - 让 Claude Code 发出 OSC 转义（桌面通知、窗口标题、BEL）。受限白名单（OSC 0/1/2/9/99/777），不能写光标控制或颜色 为 Claude 注入上下文（在 hookSpecificOutput 里）：\n{ \u0026quot;hookSpecificOutput\u0026quot;: { \u0026quot;hookEventName\u0026quot;: \u0026quot;PostToolUse\u0026quot;, \u0026quot;additionalContext\u0026quot;: \u0026quot;这个文件是生成的，请改 src/schema.ts 然后跑 bun generate\u0026quot; } } additionalContext 出现在 SessionStart/UserPromptSubmit/PreToolUse 等不同位置——本质是”Claude 看到的下一个模型请求时多了一条系统提醒”。多个 hook 都返回时 Claude 收到全部。超 10000 字符会被截断到文件并附带预览。\nCLAUDE.md 比 additionalContext 更适合放不变的项目说明——它加载时不需要跑脚本。\n决定控制 每个事件表达”决定”的方式不同，下面是速查表：\n事件 决定方式 关键字段 UserPromptSubmit / UserPromptExpansion / PostToolUse / PostToolUseFailure / PostToolBatch / Stop / SubagentStop / ConfigChange / PreCompact 顶级 decision: \"block\" reason TeammateIdle / TaskCreated / TaskCompleted 退出 2 或 continue: false - PreToolUse hookSpecificOutput.permissionDecision allow / deny / ask / defer + permissionDecisionReason PermissionRequest hookSpecificOutput.decision.behavior allow / deny；allow 时还能 updatedInput 改工具参数 PermissionDenied hookSpecificOutput.retry: true 让模型重试被拒的工具 WorktreeCreate 命令 hook stdout 打印路径 / HTTP hookSpecificOutput.worktreePath - Elicitation / ElicitationResult hookSpecificOutput.action accept / decline / cancel + content MessageDisplay hookSpecificOutput.displayContent 改屏幕上显示的文本（Claude 看到的还是原文） SessionStart / Setup / SubagentStart 仅 additionalContext 无阻止能力（SessionStart 还接受 initialUserMessage / watchPaths / sessionTitle / reloadSkills） WorktreeRemove / Notification / SessionEnd / PostCompact / InstructionsLoaded / StopFailure / CwdChanged / FileChanged 无 只能做副作用（日志、清理） PreToolUse 的 deferred 模式：返回 permissionDecision: \"defer\" 配合 toolUseID，可以把工具调用延迟到下一轮——典型场景是用户审批 UI 弹窗没准备好时。\nPreToolUse 改写参数：\n{ \u0026quot;hookSpecificOutput\u0026quot;: { \u0026quot;hookEventName\u0026quot;: \u0026quot;PreToolUse\u0026quot;, \u0026quot;permissionDecision\u0026quot;: \u0026quot;allow\u0026quot;, \u0026quot;updatedInput\u0026quot;: { \u0026quot;command\u0026quot;: \u0026quot;npm run lint\u0026quot; } } } PostToolUse 改写输出：\n{ \u0026quot;hookSpecificOutput\u0026quot;: { \u0026quot;hookEventName\u0026quot;: \u0026quot;PostToolUse\u0026quot;, \u0026quot;updatedToolOutput\u0026quot;: { \u0026quot;stdout\u0026quot;: \u0026quot;[redacted]\u0026quot;, \u0026quot;stderr\u0026quot;: \u0026quot;\u0026quot;, \u0026quot;interrupted\u0026quot;: false, \u0026quot;isImage\u0026quot;: false } } } 注意：updatedToolOutput 只能改 Claude 看到的内容，工具已执行的副作用（写文件、跑命令、HTTP 请求）都已经发生。遥测也在 hook 前抓完。\n异步 Hook 命令 hook 默认阻塞。加 \"async\": true 后台跑，Claude 不等结果。适合”测试、部署、外部 API 回调”这类长操作。\n{ \u0026quot;type\u0026quot;: \u0026quot;command\u0026quot;, \u0026quot;command\u0026quot;: \u0026quot;/path/to/run-tests.sh\u0026quot;, \u0026quot;async\u0026quot;: true, \u0026quot;timeout\u0026quot;: 120 } 异步 hook 不能阻止、不能返回决定（操作已经走完了）。它能做的：返回 additionalContext 在下一轮传给 Claude。systemMessage 给用户看。asyncRewake: true 让它在退出 2 时即使会话闲置也立刻唤醒 Claude。\n限制：\n只 type: \"command\" 支持 不支持 prompt / agent 异步 每次执行独立后台进程，不去重 输出默认被抑制（Ctrl+O 开详细模式） 基于提示的 Hook type: \"prompt\"，不跑命令，送一个 LLM 单轮评估决定：\n{ \u0026quot;type\u0026quot;: \u0026quot;prompt\u0026quot;, \u0026quot;prompt\u0026quot;: \u0026quot;评估 Claude 是否应停止: $ARGUMENTS。检查任务是否完成。\u0026quot; } LLM 返回 { \"ok\": true } 或 { \"ok\": false, \"reason\": \"...\" }。$ARGUMENTS 会被 hook 输入 JSON 替换。\nok: true 允许 ok: false： Stop / SubagentStop / PreToolUse 等：原因作为下一条指令或工具错误回给 Claude PostToolUse：默认结束轮次；设 continueOnBlock: true 把原因作为上下文 PostToolBatch / UserPromptSubmit / UserPromptExpansion：结束轮次 PermissionRequest 和 PermissionDenied 上 ok: false 无效，要用命令 hook 返回 decision.behavior: \"deny\" 或 retry: true。\n基于代理的 Hook type: \"agent\"（实验性，行为可能变）。和 prompt hook 类似，但代理可以调 Read/Grep/Glob 工具来验证条件，50 轮内返回。配置字段和 prompt 一样，只是默认 timeout=60。生产环境建议用命令 hook。\n安全 命令 hook 以你的系统用户完整权限跑——能改/删任何文件。\n最佳实践：\n永远不信任 hook 输入数据 shell 变量要引号 (\"$VAR\" 不是 $VAR) 检查 .. 路径遍历 用绝对路径（${CLAUDE_PROJECT_DIR} + exec 形式最稳） 跳过 .env、.git/、密钥目录 企业管理员可以设 allowManagedHooksOnly 禁止用户/项目/插件 hook——只允许托管设置里的 hook 跑。托管 enabledPlugins 强制启用的插件里的 hook 是豁免的。\n调试 /hooks 菜单：只读浏览所有配置的 hook claude --debug：把 hook 匹配、退出码、stdout/stderr 写到 ~/.claude/debug/\u0026lt;session-id\u0026gt;.txt，不打印终端 claude --debug-file \u0026lt;path\u0026gt;：日志写到指定位置 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose：额外显示匹配器计数和查询细节 典型调试场景：\nhook 不触发 → 检查 matcher 字段拼写和事件支持列表 Stop hook 持续阻止 → 确认退出 0 / JSON 模式二选一 JSON 解析失败 → 检查 shell rc 文件（.bashrc 等）是否在 hook stdout 里打印东西 速查 事件 → matcher → [if] → handler ↓ stdin/POST 收 JSON ↓ 退出 0 (stdout JSON) | 退出 2 (阻止) | 其他 (非阻止) ↓ stdout JSON 含 decision / permissionDecision / additionalContext / continue 参考资料：\nHooks 官方参考 使用 hooks 自动化工作流（指南） 设置文件 权限规则 Bash 命令验证器示例 ","date":"2026-06-03T00:00:00Z","permalink":"/hooks/","title":"Claude Code Hooks 笔记"},{"content":"CISPO: Clipped IS-weight Policy Optimization 背景 在 GRPO 的训练过程中，观察到一个显著问题：模型会出现性能下降，且不能有效促进 CoT 推理行为。通过一系列实验后发现，问题主要出在 PPO/GRPO 损失函数中的裁剪（Clipping）操作。\n具体来说，与反思行为相关的 token 往往是推理路径中的”分叉点”，在 Base 模型中出现频率低且被赋予较低的概率。这些 token 往往具备较高的重要性采样比 \\(r_t\\)，因此在第一次 on-policy 更新后就会被裁剪掉，无法参与后续的 off-policy 梯度更新。然而，这些低概率的 token 恰恰对于稳定熵以及促进可扩展的强化学习至关重要。\n尽管 DAPO 尝试通过提高裁剪上界来缓解这一问题，但效果有限——每个生成批次包含 16 轮 off-policy 更新，简单的上界调整无法从根本上解决问题。\nCISPO 核心思想 CISPO（Clipped IS-weight Policy Optimization）的设计目标非常明确：\n明确避免丢弃 token，即使是那些对应较大更新的 token，同时通过内在机制将熵控制在合理范围内，以确保探索过程的稳定性。\n从 REINFORCE 出发 CISPO 从标准 REINFORCE 出发，其损失函数为：\n\\[ J_{\\text{REINFORCE}}(\\theta) = \\mathbb{E}_{(q,a)\\sim D, o_i \\sim \\pi_{\\theta_{\\text{old}}}(\\cdot|q)}\\left[\\frac{1}{|o_i|}\\sum_{t=1}^{|o_i|}\\text{sg}(r_{i,t}(\\theta))\\hat{A}_{i,t}\\log\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t})\\right] \\]\n其中 \\(\\text{sg}\\) 表示停止梯度操作。\nCISPO 对重要性采样权重进行裁剪：\n\\[ \\hat{r}_{i,t}(\\theta) = \\text{clip}\\left(r_{i,t}(\\theta), 1-\\epsilon^{\\text{IS}}_{\\text{low}}, 1+\\epsilon^{\\text{IS}}_{\\text{high}}\\right) \\]\n代入得到 CISPO 的目标函数：\n\\[ J_{\\text{CISPO}}(\\theta) = \\mathbb{E}_{(q,a)\\sim D, \\{o_i\\}_{i=1}^G \\sim \\pi_{\\theta_{\\text{old}}}(\\cdot|q)}\\left[\\frac{1}{\\sum_{i=1}^G|o_i|}\\sum_{i=1}^G\\sum_{t=1}^{|o_i|}\\text{sg}(\\hat{r}_{i,t}(\\theta))\\hat{A}_{i,t}\\log\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t})\\right] \\]\n关键创新：stop-gradient 这里的核心是 sg 操作——让重要性采样比停止梯度。这么做的好处是：\nIS 权重只作为一种固定的重新加权因子，衡量采样分布和目标分布（新旧策略）之间的差异 不会像 PPO 那样因 clip 导致梯度被截断，进而导致 token 更新被强行剪掉 即使某些 token 的 \\(r_t\\) 很大，也只是被重新加权，而不是完全丢弃 与 PPO clip 的本质区别 PPO 的裁剪是直接对梯度进行裁剪，一旦超出裁剪区间，该位置的梯度直接变为零。而 CISPO 的裁剪是对 IS 权重进行裁剪并断开梯度，IS 权重本身不再推动参数更新，但它作为固定系数参与损失计算，不存在”梯度被截断”的问题。\n通用公式与 Token Mask 文中还提出了一个通用公式，引入 token-level 的 mask：\n\\[ J_{\\text{unify}}(\\theta) = \\mathbb{E}\\left[\\frac{1}{\\sum_i|o_i|}\\sum_i\\sum_t\\text{sg}(\\hat{r}_{i,t})\\hat{A}_{i,t}\\log\\pi_\\theta(o_{i,t})M_{i,t}\\right] \\]\n其中：\n\\[ M_{i,t} = \\begin{cases} 0 \u0026amp; \\text{if }\\hat{A}_{i,t} \u0026gt; 0 \\text{ and } r_{i,t}(\\theta) \u0026gt; 1 + \\epsilon_{\\text{high}} \\\\ 0 \u0026amp; \\text{if }\\hat{A}_{i,t} \u0026lt; 0 \\text{ and } r_{i,t}(\\theta) \u0026lt; 1 - \\epsilon_{\\text{low}} \\\\ 1 \u0026amp; \\text{otherwise} \\end{cases} \\]\n简单来说，就是让过分放大奖励和过分惩罚的 token 损失为 0，完全不参与梯度更新——这在一定程度上等价于 PPO 带梯度的 clip。\n熵增强 CISPO 还采用了基于熵的项来增强优势函数。研究发现：\n高熵区域与三类探索性推理行为存在正相关性： 1. 决定或连接逻辑步骤的关键 Token 2. 自我验证和修正等反思性行为 3. Base 模型未充分探索的罕见行为\n因此，熵可以作为识别语言模型中探索性推理行为的有用信号。\n具体做法是使用裁剪并断开梯度的熵项来增强优势函数。与传统的最大熵方法（通过促进不确定性来鼓励探索）不同，CISPO 通过促进更长、更深的推理链来鼓励探索。\n裁剪操作确保熵项既不会主导优势项，也不会改变其符号 梯度分离则保持了原始的优化方向 其他技术细节 CISPO 还借鉴了 DAPO 的两项技术：\n动态采样（Dynamic Sampling）：过滤掉优势为零的样本 长度惩罚（Length Penalty）：缓解长度偏差问题 同时，CISPO 没有使用 KL 惩罚项。\n总结 特性 CISPO GRPO PPO Clip 位置 IS 权重（断开梯度） IS 权重 梯度直接裁剪 探索性 Token 保留 可能被裁剪 可能被裁剪 KL 惩罚 无 可选 有 熵增强 有 无 无 序列级/Token级 Token级 Token级 Token级 参考 MiniMax-M1: Scaling Test-Time Compute Efficiently with Lightning Attention GRPO优化在继续——CISPO和熵 | 长琴 ","date":"2026-04-27T00:00:00Z","permalink":"/cispo/","title":"CISPO"},{"content":"SAPO: Soft Adaptive Policy Optimization 背景 在 LLM 的 RL 训练中，稳定的策略优化仍然具有挑战性。Token-level 的重要性采样比往往表现出高方差——这一问题在 MoE（Mixture-of-Experts）模型中尤为严重，会导致不稳定的更新。\n现有的基于组的策略优化方法（如 GSPO 和 GRPO）通过硬裁剪（hard clipping）来缓解高方差问题，但这使得稳定性和有效学习之间难以兼顾：\nGSPO：在序列级别进行硬裁剪，一旦某个序列中有少数 token 严重偏离策略，整个序列的梯度都会被抑制 GRPO：在 token 级别进行硬裁剪，虽然 token 级别更细粒度，但裁剪边界过于”脆弱” 什么是 SAPO SAPO（Soft Adaptive Policy Optimization）提出用平滑的、温度控制的门控机制替代硬裁剪，自适应地衰减 off-policy 更新的同时保留有用的学习信号。\nSAPO replaces hard clipping with a smooth, temperature-controlled gate that adaptively attenuates off-policy updates while preserving useful learning signals.\n核心思想 硬裁剪的问题 以 GSPO 为例，其硬裁剪逻辑是：\n\\[ r_t = ext{clip}\\left(\frac{\\pi_ heta(a_t|s_t)}{\\pi_{ heta_{ ext{old}}}(a_t|s_t)}, 1-\\epsilon, 1+\\epsilon ight) \\]\n当一个序列中某个 token 严重 off-policy 时（\\(r_t\\) 超出裁剪区间），GSPO 会抑制整个序列的所有梯度。这固然稳定，但也浪费了那些 near-on-policy token 的有效学习信号。\nSAPO 的解决方案 SAPO 的核心是用一个软门控函数替代硬裁剪：\n\\[ g(r_t; au) = rac{r_t}{ au + r_t} \\]\n或者更一般地，使用 sigmoid 类型的平滑过渡：\n\\[ g(r_t; au) = ext{sigmoid}\\left(\frac{\\log r_t}{ au} ight) \\]\n其中 $ au$ 是温度参数，控制平滑程度。\n当 $ au o 0$ 时，\\(g(r_t) o 1\\)（无裁剪） 当 $ au o $ 时，\\(g(r_t) o 0\\)（完全抑制） 中间状态提供一个连续的信任域，而不是 GSPO 那种”断裂”的硬裁剪带 SAPO vs GSPO vs GRPO 特性 SAPO GSPO GRPO 裁剪方式 平滑软门控 序列级硬裁剪 Token级硬裁剪 序列连贯性 保持 保持 Token独立 Token自适应 是 否（全序列抑制） 否（独立裁剪） 信任域 连续 断裂带 断裂带 训练稳定性 高 高 中 样本效率 高（保留有效信号） 中 中 关键特性 1. 序列连贯性（Sequence Coherent） 与 GRPO 不同，SAPO 保持了序列级别的连贯性。GSPO 虽然也是序列级别，但使用的是硬裁剪。SAPO 通过软门控，在保持序列级语义的同时，对严重 off-policy 的 token 进行选择性降权，而不是全序列抑制。\n2. Token 自适应（Token-Adaptive） 当一个序列包含少数严重 off-policy 的 token 时：\nGSPO：抑制整个序列的梯度 SAPO：只对偏离的 token 进行降权，保留 near-on-policy token 的学习信号 3. 温度控制（Temperature-Controlled） 温度参数 $ au$ 控制裁剪的”软硬”程度：\n较小的 $ au$：更接近无裁剪，保留更多学习信号，但可能不稳定 较大的 $ au$：更 aggressive 地抑制 off-policy，更稳定但可能欠学习 这提供了一个连续可调的稳定性和效率的权衡空间。\n与 CISPO 的关系 值得注意的是，CISPO 和 SAPO 都指向了同一个问题：GRPO/PPO 的硬裁剪会损害探索性 token 的学习。但它们的解决思路不同：\nCISPO（MiniMax）：对 IS 权重裁剪并断开梯度，用 token mask 替代梯度裁剪，同时用熵增强探索 SAPO（Qwen）：用平滑的软门控替代硬裁剪，保持序列连贯性的同时 token 自适应地衰减 两者都是对 GRPO 硬裁剪问题的有力改进。\n实验结果 在数学推理基准测试中，SAPO 表现出：\n更好的训练稳定性 更高的 Pass@1 性能（在相同训练预算下） 此外，SAPO 被用于训练 Qwen3-VL 模型系列，在不同任务和不同模型规模上都取得了一致的性能提升。\n总结 SAPO 的核心贡献在于：\n用软门控替代硬裁剪，提供一个连续而非断裂的信任域 Token 自适应降权，只抑制严重 off-policy 的 token，保留 near-on-policy token 的有效学习信号 温度控制的平滑过渡，提供连续可调的稳定性和效率权衡 保持序列连贯性，避免 GSPO 全序列抑制的样本效率损失 SAPO 为 RL 训练提供了一个更可靠、更可扩展、更有效的优化策略。\n参考 [Qwen3 Technical Report]（RL 优化部分） LLM 强化的”炼金术”：主流开源模型的 RL 优化策略赏析 | 长琴 ","date":"2026-04-27T00:00:00Z","permalink":"/sapo/","title":"SAPO"},{"content":"/btw /rewind /insights /simplify /branch /loop /remote-control /export ","date":"2026-04-17T00:00:00Z","permalink":"/cc%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","title":"cc常用命令"},{"content":"[文档目录] ## vLLM简介 vLLM 是 UC Berkeley 等开源的一款高吞吐、内存高效的大模型推理与服务引擎。它的核心创新在于 PagedAttention 算法，将注意力层的 Key-Value 缓存（KV cache）以类似操作系统虚拟内存分页的方式管理。通过这一创新，vLLM 实现了远超传统方案的推理性能。下图展示了一个13B的模型在A100 40GB的gpu上做推理时的显存占用分配，从这张图中我们可以直观感受到推理中KV cache对显存的占用。因此，如何优化KV cache，节省显存，提高推理吞吐量，就成了LLM推理框架需要解决的重点问题。\n本次分享针对vLLM v0进行，vLLM v1针对vLLM v0存在的一些问题进行了改进，分享过程中对部分内容会稍作提及，后续有机会也会为大家带来更详细的vLLM v1的分享～\n## vLLM高效的关键 vLLM 的架构围绕提高 GPU 利用率和内存利用率而设计。其关键包括高效的推理流水线（连续批处理调度）和创新的内存管理策略（PagedAttention）。\n2.1. 静态批处理与连续批处理 传统推理框架常采用静态批处理：\n工作方式：收集多个请求 -\u0026gt; 组成一个批次 -\u0026gt; 一次性输入模型 -\u0026gt; 所有请求同时计算 -\u0026gt; 所有请求完成后，释放整个批次，再处理下一批。 致命缺陷：木桶效应。在生成任务中，每个请求的输入长度和输出长度（即需要生成的token数）都不同。一个请求可能生成10个token就结束了，而另一个需要100个token。快的请求必须等待慢的请求全部完成后，整个批次才能解散，导致GPU计算资源在每一步都有大量闲置。 连续批处理调度在每个生成迭代（每产生一个新 token）的粒度上动态管理批次：\n每次迭代时，调度器将当前所有未完成的请求组成批次，在 GPU 上并行推理一个 token。 只要某些请求完成了当前 token 的生成，调度器立即回收其占用的计算槽和显存页，下一迭代就可立即将新到达的请求补充进来，不用等待其他请求完成。 通过上述机制，vLLM 的推理流水线能够持续地接纳新请求并生成输出。GPU 几乎始终保持忙碌状态，减少空闲和等待时间，大幅提升了整体吞吐量。 ### 2.2. 内存管理策略：PagedAttention #### 2.2.1 为KV cache分配存储空间的常规方式 在常规的推理框架中，当我们的服务接收到一条请求时，它会为这条请求中的prompts分配gpu显存空间，其中就包括对KV cache的分配。由于推理所生成的序列长度大小是无法事先预知的，所以大部分框架会按照(batch_size, max_seq_len)这样的固定尺寸，在gpu显存上预先为一条请求开辟一块连续的矩形存储空间。\n我们假设max_seq_len = 8，所以当第1条请求(prompt1)过来时，我们的推理框架为它安排了(1, 8)大小的连续存储空间。 当第2条请求（prompt2）过来时，同样也需要1块(1, 8)大小的存储空间。 但此时prompt1所在的位置上，只剩3个空格子了，所以它只能另起一行做存储。对prompt3也是同理。 观察整个KV cache排布，你会发现它们的毛病在于太过“静态化”。当你无法预知序列大小时，你为什么一定要死板地为每个序列预留KV cache空间呢？为什么不能做得更动态化一些，即“用多少占多少”呢？这样我们就能减少上述这些存储碎片，使得每一时刻推理服务能处理的请求更多，提高吞吐量，这就是vLLM在做的核心事情，我们先通过一张实验图来感受下vLLM在显存利用上的改进效果（VS 其它推理框架）：\nvLLM是通过什么技术，动态地为请求分配KV cache显存，提升显存利用率的？\n为了解决KV缓存带来的内存消耗低效的问题，引入了PagedAttention。\n2.2.2. PagedAttention机制解析 vLLM 的 PagedAttention 从操作系统的分页存储获得灵感，彻底改变了 KV 缓存的管理方式：\n分页存储思想：不再为每个请求分配一整块连续内存，而是将每个序列的 KV 缓存拆分为若干固定大小的小块（KV 页）。每个块可存储预定数量（例如 N 个）token 的键/值向量。系统仅在需要时（生成新 token 时）按块分配内存，并通过一个块表（block table）维护序列的逻辑块到物理内存块的映射。 非连续存储：得益于块表映射，序列的各块在物理显存中无需连续。逻辑上每个序列的 token 仍按顺序划分到第1块、第2块…，但实际这些块可以分散在内存任意位置。这样避免了请求间争夺大块连续内存导致的外部碎片。通过这种方式，每个request都会认为自己在一个连续且充足的存储空间上操作，尽管物理上这些数据的存储并不是连续的。 按需分配：当序列生成新 token 时，vLLM 先尝试放入该序列当前的最后一个块；如果该块已满，则向 GPU 内存的空闲块池申请一个新物理块，映射为下一个逻辑块。整个生成过程中，只分配实际需要的块，不预留整段空间，从而将内存浪费降至极低。 高效释放：当请求结束或被中止，其占用的物理块会立即归还空闲池，无需执行大范围的显存释放操作，减少碎片整理开销。由于所有块大小相同，释放后很容易被其它序列重新利用，也不会产生严重碎片。 2.2.3 PagedAttention在不同场景下的运作 处理单个请求 * 处理多个不相同请求\n* 处理多个相同请求（Parallel Sampling） * Prefill阶段： * 逻辑块：逻辑块独立，每个Sample有各自的逻辑块。 * 物理块：文字完全相同，共享物理空间。\n* Decode阶段： * 两个Sample分别进行推理，得到fathers和mothers。 * 逻辑块中，两个Sample分别保存各自的逻辑块。 * 物理块中，触发copy-on-write机制：由于fathers/mothers是两个完全不同的token，因此对物理块block1触发复制机制，即在物理内存上新开辟一块空间。此时物理块block1只和A2的逻辑块block1映射，将其ref count减去1；物理块block3只和A1的逻辑块block1映射，将其ref count设为1。 * 总结起来，vLLM节省KV cache显存的核心思想是，对于相同数据对应的KV cache，能复用则尽量复用；无法复用时，再考虑开辟新的物理空间。 * Beam Search：束搜索，这是LLM常用的deocde策略之一，即在每个decode阶段，我不是只产生1个token，而是产生top k个token（这里k也被称为束宽）。top k个token必然对应着此刻的top k个序列。我把这top k个序列喂给模型，假设词表的大小为|V|，那么在下一时刻，我就要在k*|V|个候选者中再选出top k，以此类推。不难想象每一时刻我把top k序列喂给模型时，它们的前置token中有大量的KV cache是重复的。\n我们从右往左来看这张图。虚线位置表示“当前decoding时刻”，beam width = 4。图中所有的block皆为逻辑块。\n因为beam width = 4，这意味着根据beam search算法，在当前阶段我们生成了top 4个概率最大的token（我们记这4个token为beam candidate 0/1/2/3），它们分别装在block5，block6，block7和block8中。 现在我们继续使用beam search算法做decoding，继续找出top 4个最可能的next token。经过我们的计算，这top 4 next token，有2个来自beam candidate 1，有2个来自beam candidate 2。因此我们在block6中引出block9和block10，用于装其中两个top 2 next token；对block7也是同理。 现在，block9/10/11/12中装的top 4 next token，就成为新的beam candidates，可以按照和上述一样的方式继续做beam search算法。而对于block5和block8，它们已经在beam search的搜索算法中被淘汰了，后续生成的token也不会和它们产生关系，所以可以清除掉这两个逻辑块，并释放它们对应的物理块的内存空间。 好，我们继续往左边来看这幅图。block3引出block5/6/7，block4引出block8，这意味着当前这4个top4 token，是上一个timestep下candidate1和candidate3相关序列生成的（candidate0和2的block没有画出，是因为它们所在的序列被beam search算法淘汰了，因此没有画出的必要）。由于block8已经被淘汰，所以block4也相继被淘汰，并释放对应的物理内存空间。 由此往左一路推，直到block0为止（block0代表着prompt，因此被beam seach中所有的序列共享）。这一路上，我们都根据最新时刻的beam search decoding结果，释放掉不再被需要的逻辑块和对应的物理内存空间，达到节省显存的目的。\n2.2.4. PagedAttention的优势 PagedAttention 带来了多方面的优势：\n内存利用率大幅提升：由于仅为实际生成的内容分配内存，并消除了跨请求的大块预留，GPU 内存碎片和浪费显著降低。这使得同等显存下可以并发容纳更多请求的 KV 缓存，从而支持更大的有效批量，提升吞吐。 支持长序列和大模型：逻辑-物理分离后，vLLM 不需要提前为最大长度保留显存，只要有空闲块就能继续扩展序列长度。对于超长输入/输出或模型参数非常大的场景，PagedAttention 也能灵活调度内存，用完再申请新的块。此外，vLLM 还支持在GPU显存不足时，将部分序列的KV块交换（swap）到CPU内存，并在其重新调度时通过重算快速恢复。 内存共享与分支开销降低：PagedAttention 天然支持KV 缓存共享。利用块表的灵活映射，不同序列可以指向相同的物理块实现内容共享。典型应用是在并行采样和Beam Search等生成分支场景：多个输出序列共享相同的输入 prompt 或共同前缀。vLLM 中这些序列的前缀部分可以共用同一批物理KV块，避免重复存储和计算。当分支真正发生差异（如Beam Search某条beam生成不同新token），仅对新增部分分配块即可；若共享块需要修改，则触发写时复制（Copy-on-Write）另给新块，保证线程安全。通过共享前缀，并行采样可减少约6%~30%的内存占用，Beam Search 减少约44%~66% 的KV内存。这带来明显性能收益：复杂采样算法的显存开销降低最多一半，令其在服务中更实用。 综上，PagedAttention 将传统 LLM 推理中低效的内存管理变成了类似操作系统虚拟内存的按需分页。几乎所有内存浪费被限制在每个序列最后一个未填满的块内。实验表明，这种改进让系统的GPU内存利用率接近最优，有效批量显著增加，从而带来吞吐量的大幅提升。PagedAttention 正是 vLLM 性能飞跃的“秘密武器”。\n2.3. 调度与抢占 当采用动态分配显存的办法时，虽然明面上同一时刻能处理更多的prompt了，但因为没有为每个prompt预留充足的显存空间。如果在某一时刻整个显存被打满了，而此时所有的prompt都没做完推理，那该怎么办？\n2.3.1. vLLM的总原则 先来先服务（FCFS），后来先抢占，gpu不够就先swap到cpu上。\n后来先抢占 当一堆请求到达vLLM服务器，导致gpu显存不足时，vLLM会暂停这堆请求中最后到达的那些请求的推理，同时将它们相关的KV cache从gpu上释放掉，以便为更早到达的请求留出足够的gpu空间，让它们完成推理任务。如果不这样做的话，各个请求间相互争夺gpu资源，最终将导致没有任何一个请求能完成推理任务。等到先来的请求做完了推理，vLLM调度器认为gpu上有足够的空间了，就能恢复那些被中断的请求的执行了。 2.3.2. “后来先抢占”的实现 对于这些因gpu资源不足而被抢占的任务，vLLM要完成两件事：\n暂停它们的执行，同时将与之相关的KV cache从gpu上释放掉 等gpu资源充足时，重新恢复它们的执行 针对这两件事，vLLM分别设计了Swapping（交换策略）和Recomputation（重计算策略）来解决。我们来细看这两个策略。\n（1）Swapping\n对于被抢占的请求，vLLM要将其KV cache从gpu上释放掉，那么：\n问题1：该释放哪些KV cache？ 问题2：要把这些KV cache释放到哪里去？ 先看问题1。在vLLM中，一般采取的是all-or-nothing策略，即释放被抢占请求的所有block。\n再来看问题2。对于这些被选中要释放的KV block，如果将它们直接丢掉，那未免过于浪费。vLLM采用的做法是将其从gpu上交换（Swap）到cpu上。这样等到gpu显存充足时，再把这些block从cpu上重载回来。\n（2）Recomputation\n知道了Swapping机制，重计算的过程也很好理解了：对于有些任务，当它们因为资源不足而被抢占时，可以不做swap，而是直接释放它们的物理块，把它们重新放入等待处理的队列中，等后续资源充足时再重新从prefill阶段开始做推理。\n2.4. 总结​ PageAttention为动态批处理提供了灵活、高效的内存管理基础，使其调度能力得以充分发挥；而动态批处理则将PageAttention节省的内存资源转化为实实在在的计算吞吐量提升。\nvLLM整体架构设计 ### 3.1. Centralized Controller Centralized Controller，也就是前文我们所说的调度器(Scheduler)。\n调度器的主要作用就是，在每1个推理阶段，决定要把哪些数据送给模型做推理，同时负责给这些模型分配KV Cache物理块。但要注意，它只是分配了物理块的id，而不是物理块本身。物理块的实际分配是模型在推理过程中根据物理块id来操作的，也就是CacheEngine做的事情。 self.policy：是vLLM自定义的一个Policy实例，目标是根据调度器总策略（FCFS，First Come First Serve，先来先服务）原则，对各个队列里的seq_group按照其arrival time进行排序。\nself.prev_time：上一次调度发起的时间点，初始化为0。我们知道每执行1次推理阶段前，调度器都要做一次调度，这个变量存放的就是上次调度发起的时间点。\nself.prev_prompt：取值为True/False，初始化为False。若上一次调度时，调度器有从waiting队列中取出seq_group做推理，即为True，否则为False。\nself.last_prompt_latency：记录“当前调度时刻（now） - 最后一次有从waiting队列中取数做推理的那个调度时刻”的差值（并不是每一次调度时，调度器一定都会从waiting队列中取seq_group，它可能依旧继续对running队列中的数据做推理），初始化为0。\nBlockManager：物理块管理器。这也是vLLM自定义的一个class。物理块管理器这个class下又维护着两个重要属性：\nBlockAllocator：物理块分配者，负责实际为seq做物理块的分配、释放、拷贝等操作。其下又分成self.gpu_allocator和self.cpu_allocator两种类型，分别管理gpu和cpu上的物理块。 self.block_tables：负责维护每个seq下的物理块列表。 3.2. Distributed Workers Distributed Workers，也就是分布式系统，你可以将每个worker理解成一块gpu。它的作用是将我们要使用的模型load到各块卡上，然后对Controller传来的数据做1次推理，返回相关结果。\nWorker：在硬件上，它指gpu；在代码上，它指的是Worker实例（每个gpu上的进程维护自己的Worker实例）。在每个Worker实例中又管控着如下两个重要实例： CacheEngine：负责管控gpu/cpu上的KV cache物理块（调度器的block manager只负责物理块id的分配，CacheEngine则是根据这个id分配结果实打实地在管理物理块中的数据） Worker.model：负责加载模型，并执行推理。PagedAttention的相关逻辑，就维护这个实例关联的代码下。 vLLM运作流程 当一条请求过来时，整个vLLM是怎么运作的呢？\nfrom vllm import LLM, SamplingParams if __name == \u0026quot;__main__\u0026quot;: # Sample prompts. prompts = [ \u0026quot;Hello, my name is\u0026quot;, \u0026quot;The president of the United States is\u0026quot;, \u0026quot;The capital of France is\u0026quot;, \u0026quot;The future of AI is\u0026quot;, ] # Create a sampling params object. sampling_params = SamplingParams(temperature=0.8, top_p=0.95) # 创建llm实例，在这个过程中也创建了llm实例下维护的llm_engine llm = LLM(model=\u0026quot;facebook/opt-125m\u0026quot;) # 执行offline batching推理，得到这批prompts的输出 outputs = llm.generate(prompts, sampling_params) # 打印输出 for output in outputs: prompt = output.prompt generated_text = output.outputs[0].text print(f\u0026quot;Prompt: {prompt!r}, Generated text: {generated_text!r}\u0026quot;) 4.1. 加载模型 # =========================================================================== # 初始化vLLM offline batched inference实例，并加载指定模型 # =========================================================================== llm = LLM(model=\u0026quot;facebook/opt-125m\u0026quot;) 这里在做的事很直观：把base model加载到worker上。\n4.2. 预分配显存 实例化了一个离线批处理的vLLM对象。其本质是实例化了一个内核引擎LLMEngine对象。在执行这个步骤时，LLMEngine会执行一次模拟实验（profiling），来判断需要在gpu上预留多少的显存空间给KV Cache block。vLLM管这个步骤叫profile_num_available_blocks。\n加载预分配的KV Cache到gpu上 当我们确定好KV Cache block的大小后，我们就可以创建empty tensor，将其先放置到gpu上，实现显存的预分配。以后这块显存就是专门用来做KV Cache的了。也正是因为这种预分配，你可能会发现在vLLM初始化后，显存的占用比你预想地要多（高过模型大小），这就是预分配起的作用。\n4.3. Scheduler调度 * self.waiting, self.running, self.swapped：这三个都是python的deque()实例（双端队列，允许你从队列两侧添加或删除元素）。 * waiting队列用于存放所有还未开始做推理的seq_group，“未开始”指连prefill阶段都没有经历过。所以waiting队列中的seq_group只有一个seq，即是原始的prompt。 * running队列用于存放当前正在做推理的seq_group。更准确地说，它存放的是上1个推理阶段被送去做推理的seq_group们，在开始新一轮推理阶段时，调度器会根据本轮的筛选结果，更新running队列，即决定本轮要送哪些seq_group去做推理。 * swapped队列用于存放被抢占的seq_group。若一个seq_group被抢占，调度器会对它执行swap或recomputation操作，分别对应着将它送去swapped队列或waiting队列。\n请求发起：The future of Artifical Intelligence 到达LLMEngine 分词 加入调度序列：waiting queue 调度器Scheduler进行队列管理：筛选出需要运行的一组请求 当KV块内存有空闲：waiting请求变为running 当没有KV块内存用于生成新token running变为swapping running变为waiting BlockSpaceManager进行块空间管理： 给新的KVCache分配空间 处理块共享 交换/删除被抢占的KV块 BlockAllocator：当BlockSpaceManager下达“分配”或者“交换”命令时，块分配器在真实的物理内存上进行分配和回收。 4.4. 执行推理 Distributed Workers接受来自Scheduler的请求，分发到各个worker上去做推理。\nCacheEngine负责管理实际KVCache。 Worker.model负责加载模型，进行推理。 推理入口：outputs = llm.generate(prompts, sampling_params)\n当我们调用generate()时，它实际做了两件事情：\n_add_request：将输入数据传给LLMEngine：把每1个prompt包装成一个SequenceGroup对象（“1个prompt -\u0026gt; 多个outputs”的结构）。把包装成SequenceGroup对象的数据加入调度器（Scheduler）的waiting队列，等待处理。 _run_engine：执行推理：只要调度器的waiting/running/swapped队列非空，我们就认为此时这批batch还没有做完推理，这时我们就会调用LLMEngine的step()函数，来完成1次调度以决定要送哪些数据去做推理。 SequenceGroup 可能出现”1个prompt -\u0026gt; 多个outputs”的情况。那是否能设计一种办法，对1个prompt下所有的outputs进行集中管理，来方便vLLM更好做推理呢？\n到这里不妨思考一下，vLLM v0的整个运作流程有没有哪些可以优化的地方？\nvLLM v1的优化，还是以offline batching为例:\n* vllm v1将请求的pre-process和输出结果的post-process与实际的推理过程拆分在2个不同的进程中(process0, process1)。 * Client负责请求的pre-process和输出结果的post-process，EngineCore负责实际的推理过程，不同进程间使用ZMQ来通信数据。 * 通过这样的进程拆分，在更好实现cpu和gpu运作的overlap的同时，也将各种模型复杂的前置和后置处理模块化，统一交给processor和output_processor进行管理。\nScheduler 5.1. 调度器结构 ### 5.2. 整体调度策略 running队列中的seq_group不一定能继续在本次调度中被选中做推理，这是因为gpu上KV cache的使用情况一直在变动，以及waiting队列中持续有新的请求进来的原因。所以调度策略的职责就是要根据这些变动，对送入模型做推理的数据做动态规划。\n总结来说：\n如果当前swapped队列为空，那就去检查是否能从waiting队列中调度seq_group，直到不满足调度条件为止（gpu空间不足，或waiting队列已为空等）。此时，1个推理阶段中，所有的seq_group都处在prefill阶段。 如果当前swapped队列非空，或者无法从waiting队列中调度任何seq_group时： 检查是否能从running队列中调度seq_group，直到不满足调度条件为止。 若本次无新的被抢占的seq_group，且swapped队列非空，就检查是否能从swapped队列中调度seq_group，直到不满足调度条件为止。 此时，1个推理阶段中，所有的seq_group要么全来自running队列，要么来自running + swapped队列，它们都处在decode阶段。\n至此我们要记住vLLM调度中非常重要的一点：在1个推理阶段中，所有的seq_group要么全部处在prefill阶段。要么全部处在decode阶段。\n你可能想问：为什么要以swapped是否非空为判断入口呢？\n这是因为，如果当前调度步骤中swapped队列非空，说明在之前的调度步骤中这些可怜的seq_group因为资源不足被抢占，而停滞了推理。所以根据FCFS规则，当gpu上有充足资源时，我们应该先考虑它们，而不是考虑waiting队列中新来的那些seq_group。\n同理，在图中你会发现，当我们进入对running队列的调度时（图中红色分支），我们会根据“本次调度是否有新的被抢占的seq_group”，来决定要不要调度swapped队列中的数据。这个理由也很简单：在本次调度中，我就是因为考虑到gpu空间不足的风险，我才新抢占了一批序列。既然存在这个风险，我就最好不要再去已有的swapped队列中继续调度seq_group了。\n5.3. _passed_delay：判断调度waiting队列的时间点 在5.1的流程图中，我们会看到进入waiting循环的判断条件之一是：waiting队列是否达到调度间隔阈值。这是个什么东西？又为什么要设置这样一个阈值呢？\n我们知道模型在做推理时，waiting队列中是源源不断有seq_group进来的，一旦vLLM选择调度waiting队列，它就会停下对running/swapped中seq_group的decode处理，转而去做waiting中seq_group的prefill，也即vLLM必须在新来的seq_group和已经在做推理的seq_group间取得一种均衡：既不能完全不管新来的请求，也不能耽误正在做推理的请求。所以“waiting队列调度间隔阈值”就是来控制这种均衡的：\n调度间隔设置得太小，每次调度都只关心waiting中的新请求，这样发送旧请求的用户就迟迟得不到反馈结果。且此时waiting队列中积累的新请求数量可能比较少，不利于做batching，浪费了并发处理的能力。 调度间隔设置得太大，waiting中的请求持续挤压，同样对vLLM推理的整体吞吐有影响。 5.4. can_allocate：能否为seq_group分配物理块做prefill 通过了调度时间阈值的判断条件，现在我们顺利从waiting中取出一个seq_group，我们将对它进行prefill操作。所以这里我们必须先判断：gpu上是否有充足的空间为该seq_group分配物理块做prefill，根据5.1中绘制的调度器结构，这个操作当然是由我们的self.block_manager来做。\n5.5. can_append_slot：能否为seq_group分配物理块做decode 我们从running队列中调度seq_group时，我们也会判断是否能为该seq_group分配物理块。但这时，我们的物理块空间是用来做decode的（给每个seq分配1个token的位置），而不是用来做prefill的（给每个seq分配若干个token的位置），所以这里我们采取的是另一种判断方法can_append_slot。\n更具体来说，running队列中seq_group下的n个seqs在上1个推理阶段共生成了n个token。在本次调度中，我们要先为这n个token分配物理块空间，用于存放它们在本次调度中即将产生的KV值。我们知道：\n当往1个seq的物理块上添加1个token时，可能有两种情况： 之前的物理块满了，所以我新开1个物理块给它 之前的物理块没满，我直接添加在最后一个物理块的空槽位上 所以，对于1个seq来说，最坏的情况就是添加1个物理块；对于n个seqs来说，最坏的情况就是添加n个物理块（想想前面讲过的copy-on-write机制） 对于1个seq_group，除了那些标记为“finish”的seq外，其余seqs要么一起送去推理，要么一起不送去推理。即它们是集体行动的 所以，判断能否对一个正在running的seq_group继续做推理的最保守的方式，就是判断当前可用的物理块数量是否至少为n。\n5.6. preempt：抢占策略 抢占策略的核心逻辑：\n在若干个推理阶段后，gpu上的资源不够了，这个seq_group不幸被调度器抢占（preemption），它相关的KV block也被swap out到cpu上。此时所有seq的状态变为swapped。这里要注意，当一个seq_group被抢占时，对它的处理有两种方式：\nSwap：如果该seq_group剩余生命周期中并行运行的最大seq数量 \u0026gt; 1，此时会采取swap策略，即把seq_group下【所有】seq的KV block从gpu上卸载到cpu上。（seq数量比较多，直接把算出的KV block抛弃，比较可惜） Recomputation：如果该seq_group剩余生命周期中并行运行的最大seq数量 = 1，此时会采取recomputation策略，即把该seq_group相关的物理块都释放掉，然后将它重新放回waiting队列中(放在最前面)。等下次它被选中推理时，就是从prefill阶段开始重新推理了，因此被称为“重计算”。（seq数量少，重新计算KV block的成本不高） 5.7. vLLM v1的改进 在V0的策略中，每次调度步骤要么全是做prefill的请求，要么全是做decode的请求，除此以外，调度器中维护着waiting，running，swapped三个队列，整体来说调度策略比较复杂。\n在V1中，调度策略简化许多，最主要的就是允许单次调度步骤中同时调度prefill和decode请求，同时调度器中只维护waiting和running队列。我们从vllm官方blog中来看下V1的整体调度思想\n如图所示：\n首先，vllm会对单次调度步骤设置一个token_budget，它用来决定每次调度中最多允许“计算”多少个token。这个token_budget可以由用户通过scheduler_config.max_num_batched_tokens进行配置。在我们的图例中token_budget = 10 假设此时waiting队列中有3个R1，R2，R3三个请求（prompts），长度分别为3，5，12。请求已经按照到来的先后顺序排列好了。Vllm v1依然采用的是FCFS原则（First come First serve），按先来后到的顺序处理请求。 step0： 调度器开始执行调度。根据某种策略，调度器决定将【R1的3个token】，【R2的5个token】都算入本次调度步骤中。此时token_budget = 10 - 3 - 5 = 2。 R3有12个token需要计算，但本轮调度的token_budget只剩2个，所以只取R3的2个token加入本次调度中 R1，R2和R3都会从waiting队列转移到running队列上 step1: 调度器开始执行新一轮调度。此时R1和R2都做完了prefill，进入decode阶段，它们待计算的token都只有1个。R3仍在prefill阶段，并根据token_budget再送入8个token进入本轮调度 以此类推，可以发现，直到step3为止，最长的R3才做完了prefill，进入decode阶段。 BlockManager 6.1. BlockManager结构 BlockManager这个class下又维护着两个重要属性：\nBlockAllocator：物理块分配者，负责实际为seq做物理块的分配、释放、拷贝等操作。其下又分成self.gpu_allocator和self.cpu_allocator两种类型，分别管理gpu和cpu上的物理块。 self.block_tables：负责维护每个seq下的物理块列表，本质上它是一个字典，形式如{seq_id: List[PhysicalTokenBlock]}。注意，这个字典维护着【所有】seq_group下seq的物理块，而不是单独某一个seq的。因为调度器是全局的，所以它下面的的BlockManager自然也是全局的。 其中，BlockAllocator又分成两种类型：\nCachedBlockAllocator：按照prefix caching的思想来分配和管理物理块。在前面的介绍中，我们提过有些prompts中可能含有类似system message（例如，“假设你是一个能提供帮助的行车导航”）E）等prefix信息，带有这些相同prefix信息的prompt完全可以共享用于存放prefix的物理块，这样既节省显存，也不用再对prefix做推理。 UncachedBlockAllocator：正常分配和管理物理块，没有额外实现prefix caching的功能。 6.2. UncachedBlockAllocator 在整体调度策略的讲解中，我们明确了非常重要的一点：在vllm的1个推理阶段，所有的seq_group要么一起做prefill，要么一起做decode。这也意味着，某次调度的结果，要么全部来自waiting队列（等待做prefill的），要么全部来自running或者running + swapped队列（等待做decode的）。\n6.2.1. 为waiting队列中的seq_group分配prefill需要的物理块 * 调用self.block_manager.can_allocate(seq_group)方法，判断当前gpu上是否有充足的空间，能为当下这seq_group的prefill阶段分配充足的物理块，用于装其KV Cache。 * 一旦我们认为当下空间充足，则调用self._allocate(seq_group)方法，为waiting队列中的这个seq_group实际分配物理块，这时我们就会运用到BlockAllocator，并且BlockAllocator的类型不同（即是否做prefix caching），allocate的方法也会不同。 * 这里我们做的只是给定一种“物理块的分配方案”，我们只是在制定这个seq_group可以使用哪些物理块，但并没有实际往物理块中添加数据！“添加数据”这一步留到这1步推理实际开始时，由CacheEngine按照这个方案，往物理块中实际添加KV Cache。\n6.2.2. 为running/swapped队列中的seq_group分配decode需要的物理块 接下来我们考虑为running/swapped队列中的seq_group分配decode需要的物理块。\n对于每个seq_group，在上1个推理阶段，我们对它下面的每个seq都产出了1个token。所以在这个推理阶段，我们判断能否为这些seq_group分配物理块时，我们也会分成两步：\n调用self.block_manager.can_append_slot(seq_group)方法，判断是否至少能为这个seq_group下的每个seq都分配1个空闲物理块。如果可以则认为能调度这个seq_group。 调用self._append_slot(seq_group, blocks_to_copy)方法，实际分配物理块。 同样，在这里我们依然要强调，调度器中只是给出了物理块的分配方案，并没有实际往物理块中添加数据，添加数据这一步是CacheEngine照着这个方案来实际操作的。\n6.3. CachedBlockAllocator # ==========================================================================# ========================================================================== # 如果做了prefix caching，即使用的是CachedBlockAllocator # ========================================================================== elif self.enable_caching: block = self.gpu_allocator.allocate( seq.hash_of_block(logical_idx), seq.num_hashed_tokens_of_block(logical_idx)) 6.3.1. hash值的计算 block = self.gpu_allocator.allocate( seq.hash_of_block(logical_idx), seq.num_hashed_tokens_of_block(logical_idx)) CachedBlockAllocator按照prefix caching的思想来分配和管理物理块。在前面的介绍中，我们提过有些prompts中可能含有类似system message（例如，“假设你是一个能提供帮助的行车导航”）E）等prefix信息，带有这些相同prefix信息的prompt完全可以共享用于存放prefix的物理块，这样既节省显存，也不用再对prefix做推理。\nhash值的计算就决定了这个prefix是否真的“相同”：当两个等待做prefill的seq拥有同样的hash值时，说明它们共享一样的prompt，这时就可以重复利用已有的KV cache。\nhash的计算过程 # 逻辑块0的计算： num_tokens = 0 * 4 + 4 = 4 token_tuple = (A, B, C, D) # 前4个token hash_value = hash(( (A,B,C,D), lora_id )) # 逻辑块1的计算： num_tokens = 1 * 4 + 4 = 8 token_tuple = (A, B, C, D, E, F, G, H) # 前8个token hash_value = hash(( (A,B,C,D,E,F,G,H), lora_id )) # 逻辑块2的计算： num_tokens = 2 * 4 + 4 = 12 token_tuple = (A, B, C, D, E, F, G, H, I, J, K, L) hash_value = hash((token_tuple, lora_id)) # 逻辑块3的计算 num_tokens = 3 * 4 + 4 = 16 token_tuple = (A, B, C, D, E, F, G, H, I, J, K, L, M, N) # 实际只有14个 hash_value = hash((token_tuple, lora_id)) 6.3.2. 使用evictor管理物理块分配细节 当一个物理块没有任何逻辑块引用时（例如一个seq刚做完整个推理），这时它理应被释放。但是在prefix caching的前提下，我们的优化思想是：即使这个物理块当前没有用武之地，可是如果不久之后来了一个新seq，它的prefix（例如system message）和这个物理块指向的内容高度一致，那么这个物理块就可以被重复使用，以此减少存储和计算开销。 所以，我们设置一个驱逐器（evictor）类，它的free_tables属性将用于存放这些暂时不用的物理块。 此时，该设备上全部可用的物理块 = 正在被使用/等待被使用的物理块数量 + evictor的free_tables中的物理块数量 在prefill阶段，当我们想创建一个物理块时，我们先算出这个物理块的hash值，然后去free_tables中看有没有可以重复利用的物理块，有则直接复用 如果没有可以重复利用的hash块，那这时我们先检查下这台设备剩余的空间是否够我们创建一个新物理块。如果可以，就创建新物理块。 如果此时没有足够的空间创建新物理块，那么我们只好从free_tables中驱除掉一个物理块，为这个新的物理块腾出空间，驱逐策略如下： 先根据LRU（Least Recently Used）原则，驱逐较老的那个物理块 如果找到多个最后一次使用时间相同的老物理块，那么则根据max_num_tokens原则（max_num_tokens即为6.3.1图例中的num_tokens），驱逐其hash值计算中涵盖tokens最多的那个物理块。 如果这些老物理块的LRU和max_num_tokens还是一致的话，那就从它们中随机驱逐一个 6.3.3. prefix的标准 看到这里，也许有个想法一直在你脑中徘徊：“使用prefix caching，是不是就意味着两个seq的prompt必须完全一致，才可以重复利用物理块呢？”\n下面我们再通过两个例子，帮助大家解答这个疑惑，也更好理解“prefix”的含义。\n假设seq0现在做完了prefill，产出蓝色的4块物理块。现在进来一个seq1，我们想知道：seq1到底该怎么复用seq0的物理块？\n当seq1刚进来时，我们先算好了它的逻辑块。现在要给每个逻辑块分配物理块。 对每个逻辑块，当我们决定是否要给它分配一个新的物理块时（一个新的物理块意味着占用了新的存储空间），我们先计算这个物理块的hash值。 按照这个流程，我们发现seq1的block0～2都可以复用seq0的（蓝色） 但是hash(seq1 block3) != hash(seq0 block3)，因此我们需要为seq1 block3（红色）开辟新空间。 可以发现，尽管seq0和seq1的prompt的大部分内容是相同的，但是seq1依然不能复用seq0的prompt，这是因为KV cache的计算也需要考虑位置编码的原因。\n通过例1和例2，你现在是否已更好了解我们只对prefix计算hash值的原因了呢？我们再小结一下vllm中hash值计算的一些要点：\nvllm中，hash值的计算是block-level维度的 vllm中，hash值的计算考虑了当前block及其之前所有block所维护的token值。这样做是为了找到最长可复用的prefix。 6.3.4. decode阶段物理块的分配 在上面我们讲过UncachedBlockAllocator下为decode阶段分配物理块的方法（比较简单），但是现在若使用CachedBlockAllocator，考虑物理块的复用问题时，情况就更复杂一些了。 对于每个seq_group，在上1个推理阶段，我们对它下面的每个seq都产出了1个token。所以在这个推理阶段，我们判断能否为这些seq_group分配物理块时，我们也会分成两步：\n（1）调用self.block_manager.can_append_slot(seq_group)方法，判断是否至少能为这个seq_group下的每个seq都分配1个空闲物理块。如果可以则认为能调度这个seq_group。 （2）调用self._append_slot(seq_group, blocks_to_copy)方法，实际分配物理块。 考虑下面这个使用parallel sampling做推理的例子，当n=2时，我们希望模型针对这一个prompt，产出2个推理序列结果：\n# Parallel Sampling(\u0026quot;What is the meaning of life?\u0026quot;, SamplingParams(n=2, temperature=0.8, top_p=0.95, frequency_penalty=0.1)) * 首先，seq正常做完prefill（蓝色部分），我们用黄色部分表示decode。 * 开始做decode。根据copy-on-write机制，FG所在的block1此时被两个子序列的逻辑块引用（ref_count = 2），所以它需要被拷贝一份。这样我们就得到2个物理块，用于装H0和H1。 * 在启动copy-on-write机制的同时，我们也要重新计算物理块的hash值。和prefill阶段不同，在decode阶段，当这个物理块还没满的时候，我们会给它附一个相互不重复的默认hash值（from itertools import count()，hash_str = next(iter(count()))）。 * 我们会把附上hash值的物理块加入CachedBlockAllocator的cached_blocks属性中（参见3.3节代码中的讲解），我们说过，这个属性用于记录当前正在被使用的物理块。 * 两个子序列继续做decode（风平浪静的美丽日子） * 当一个子序列用完当前物理块的所有slots时（例如当子序列1生成J0后），我们再对这个物理块重新做hash计算，计算方式是hash(A~J0)。 * 拿着这个new_hash，我们去cached_blocks（当前正在被使用的物理块列表）和free_tables（驱逐器的冷宫，曾经被使用的物理块列表）寻找。看看这两者中是否存着相同hash值的物理块： * 如果找到可以复用的物理块，我们就释放当前这个物理块，复用旧物理块 * 如果没有找到可以复用的物理块，我们就把当前这个物理块的旧hash值从cached_blocks中释放掉，取而代之以新hash值。\n参考文档 猛猿：图解大模型计算加速系列之：vLLM核心技术PagedAttention原理 猛猿：图解大模型计算加速系列：vLLM源码解析1，整体架构 猛猿：图解大模型计算加速系列：vLLM源码解析2，调度器策略(Scheduler) 猛猿：图解大模型计算加速系列：vLLM源码解析3，块管理器BlockManager（上篇） 猛猿：图解大模型计算加速系列：vLLM源码解析3，Prefix Caching 🎁文末互动好礼🎁 1️⃣ 互动一 🤔在你的模型推理或服务部署实践中，你最在意的是吞吐、时延，还是资源成本？\n🤔你是否用过 vLLM，或正在评估类似的推理框架？它解决了你哪些问题，又留下了哪些困惑？\n2️⃣ 互动二 ✍️「周一创作者日」已经陪大家走过了一段时间，新年新气象！为更好建设社区创作者专栏，欢迎各位在评论区留下你的直觉感受和建议，你的每一条反馈，都会成为我们栏目建设的重要参考💖\n欢迎在评论区分享你的想法，我们将从留言中精选 5 位走心分享的同学，送出「AI+社区」电脑包，期待你的声音 👀✨\n","date":"2026-01-07T00:00:00Z","permalink":"/vllm%E9%AB%98%E6%95%88%E6%8E%A8%E7%90%86%E5%85%A8%E9%93%BE%E8%B7%AF/","title":"vllm高效推理全链路"},{"content":"max_num_batched_tokens、max_num_seqs 以及 gpu_memory_utilization 的协作机制 这三个参数在 vLLM 中共同控制着请求调度和内存分配。让我详细解释它们之间的关系。\n参数定义 1. max_num_batched_tokens 这是单次迭代中可以处理的最大 token 数量。它控制着每个批次的计算预算。 1\n2. max_num_seqs 这是单次迭代中可以并发处理的最大请求（序列）数量。 2\n3. gpu_memory_utilization 这是GPU 内存的使用比例（默认 0.9，即 90%），用于确定分配给 KV cache 的内存大小。 3\n它们如何协作 调度器的约束机制 调度器在调度请求时会同时检查这两个约束： 4\n关键验证逻辑： - max_num_batched_tokens 必须 \u0026gt;= max_num_seqs - max_num_batched_tokens 不能超过 max_num_seqs * max_model_len 5\nKV Cache 内存分配流程 gpu_memory_utilization 通过以下流程影响 KV cache：\n计算请求内存： 6\n内存分析：执行一次前向传播来测量激活内存使用 7\n计算可用 KV cache 内存：\n可用 KV cache 内存 = 请求内存 - 模型权重 - 峰值激活内存 - 非 torch 内存 - CUDA graph 内存 计算 block 数量： 8\n实际例子说明 例子 1：基本配置场景 假设配置： - max_num_batched_tokens = 2048 - max_num_seqs = 128 - gpu_memory_utilization = 0.9 - max_model_len = 4096\n调度行为： - 调度器最多可以同时处理 128 个请求 - 但单次迭代的总 token 数不能超过 2048 - 例如：可以调度 128 个请求，每个生成 16 个 token（128 × 16 = 2048） - 或者：调度 64 个请求，每个处理 32 个 token\nKV Cache 分配： - 假设 GPU 有 80GB 内存，gpu_memory_utilization=0.9 意味着可用 72GB - 扣除模型权重（假设 40GB）和激活内存（假设 8GB）后，剩余 24GB 用于 KV cache - 这 24GB 需要支持最多 128 个并发请求的 KV cache 存储\n例子 2：Chunked Prefill 场景 9 配置： - max_num_batched_tokens = 8192 - max_num_seqs = 256 - gpu_memory_utilization = 0.9\n场景：一个长提示词请求（1000 tokens）+ 多个解码请求\n调度器会： 1. 优先调度所有解码请求（假设 200 个请求，各 1 token = 200 tokens） 2. 剩余预算：8192 - 200 = 7992 tokens 3. 可以调度长提示词的部分（如 7992 tokens），或完整调度多个较短的新请求\n例子 3：内存不足导致的抢占 10 当 KV cache 空间不足时： - 调度器会抢占最低优先级的请求 - 释放其 KV cache blocks - 被抢占的请求重新加入等待队列 - num_computed_tokens 重置为 0，需要重新计算\n优化建议 提高吞吐量 增加 max_num_batched_tokens（如 16384）：允许更大的批次 增加 gpu_memory_utilization（如 0.95）：提供更多 KV cache 空间 增加 max_num_seqs（如 512）：支持更多并发请求 改善延迟 减小 max_num_batched_tokens（如 2048）：减少 prefill 对 decode 的影响 减小 max_num_seqs：减少竞争，降低抢占频率 避免 OOM 降低 gpu_memory_utilization：为系统预留更多内存 减小 max_num_seqs：减少 KV cache 需求 使用 tensor parallelism：在多个 GPU 间分片模型 Notes 这三个参数形成了一个相互制约的系统： - max_num_batched_tokens 和 max_num_seqs 是调度约束，限制每次迭代能处理多少工作 - gpu_memory_utilization 是内存约束，通过 KV cache 分配间接限制能同时支持多少请求 - 如果 KV cache 内存不足以支持 max_num_seqs 个满长度序列，会发生频繁的抢占，严重影响性能\n合理配置需要根据： 1. 模型大小和 GPU 内存容量 2. 预期的请求模式（长提示词 vs 短对话） 3. 性能目标（吞吐量 vs 延迟）\n进行平衡调整。\nCitations File: vllm/config/scheduler.py (L50-55)\nmax_num_batched_tokens: int = Field(default=DEFAULT_MAX_NUM_BATCHED_TOKENS, ge=1) \u0026quot;\u0026quot;\u0026quot;Maximum number of tokens to be processed in a single iteration. The default value here is mainly for convenience when testing. In real usage, this should be set in `EngineArgs.create_engine_config`. \u0026quot;\u0026quot;\u0026quot; File: vllm/config/scheduler.py (L57-62)\nmax_num_seqs: int = Field(default=DEFAULT_MAX_NUM_SEQS, ge=1) \u0026quot;\u0026quot;\u0026quot;Maximum number of sequences to be processed in a single iteration. The default value here is mainly for convenience when testing. In real usage, this should be set in `EngineArgs.create_engine_config`. \u0026quot;\u0026quot;\u0026quot; File: vllm/config/scheduler.py (L265-270)\nif self.max_num_batched_tokens \u0026lt; self.max_num_seqs: raise ValueError( f\u0026quot;max_num_batched_tokens ({self.max_num_batched_tokens}) must \u0026quot; \u0026quot;be greater than or equal to max_num_seqs \u0026quot; f\u0026quot;({self.max_num_seqs}).\u0026quot; ) File: vllm/config/scheduler.py (L272-278)\nif self.max_num_batched_tokens \u0026gt; self.max_num_seqs * max_model_len: logger.warning( \u0026quot;max_num_batched_tokens (%d) exceeds max_num_seqs \u0026quot; \u0026quot;* max_model_len (%d). This may lead to unexpected behavior.\u0026quot;, self.max_num_batched_tokens, self.max_num_seqs * max_model_len, ) File: vllm/config/cache.py (L49-56)\ngpu_memory_utilization: float = Field(default=0.9, gt=0, le=1) \u0026quot;\u0026quot;\u0026quot;The fraction of GPU memory to be used for the model executor, which can range from 0 to 1. For example, a value of 0.5 would imply 50% GPU memory utilization. If unspecified, will use the default value of 0.9. This is a per-instance limit, and only applies to the current vLLM instance. It does not matter if you have another vLLM instance running on the same GPU. For example, if you have two vLLM instances running on the same GPU, you can set the GPU memory utilization to 0.5 for each instance.\u0026quot;\u0026quot;\u0026quot; File: vllm/v1/worker/utils.py (L253-272)\ndef request_memory(init_snapshot: MemorySnapshot, cache_config: CacheConfig) -\u0026gt; float: \u0026quot;\u0026quot;\u0026quot; Calculate the amount of memory required by vLLM, then validate that the current amount of free memory is sufficient for that. \u0026quot;\u0026quot;\u0026quot; requested_memory = init_snapshot.total_memory * cache_config.gpu_memory_utilization if init_snapshot.free_memory \u0026lt; requested_memory: GiB = lambda b: round(b / GiB_bytes, 2) raise ValueError( f\u0026quot;Free memory on device {init_snapshot.device_} \u0026quot; f\u0026quot;({GiB(init_snapshot.free_memory)}/\u0026quot; f\u0026quot;{GiB(init_snapshot.total_memory)} GiB) on startup \u0026quot; f\u0026quot;is less than desired GPU memory utilization \u0026quot; f\u0026quot;({cache_config.gpu_memory_utilization}, \u0026quot; f\u0026quot;{GiB(requested_memory)} GiB). Decrease GPU memory \u0026quot; f\u0026quot;utilization or reduce GPU memory used by other processes.\u0026quot; ) return requested_memory File: vllm/v1/worker/gpu_worker.py (L343-345)\nself.available_kv_cache_memory_bytes = ( self.requested_memory - profile_result.non_kv_cache_memory ) File: vllm/v1/core/kv_cache_utils.py (L829-844)\ndef get_num_blocks( vllm_config: VllmConfig, num_layers: int, available_memory: int, page_size: int ) -\u0026gt; int: \u0026quot;\u0026quot;\u0026quot; Get the number of kv cache blocks. Args: vllm_config: The global VllmConfig num_layers: The number of layers available_memory: Memory available for KV cache in bytes. page_size: The page size of the KV cache. \u0026quot;\u0026quot;\u0026quot; num_blocks = int(available_memory // page_size // num_layers) num_blocks = max(num_blocks, 0) num_blocks = may_override_num_blocks(vllm_config, num_blocks) return num_blocks File: docs/configuration/optimization.md (L15-24)\nWARNING 05-09 00:49:33 scheduler.py:1057 Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space. This can affect the end-to-end performance. Increase gpu_memory_utilization or tensor_parallel_size to provide more KV cache memory. total_cumulative_preemption_cnt=1 While this mechanism ensures system robustness, preemption and recomputation can adversely affect end-to-end latency. If you frequently encounter preemptions, consider the following actions:\nIncrease gpu_memory_utilization. vLLM pre-allocates GPU cache using this percentage of memory. By increasing utilization, you can provide more KV cache space. Decrease max_num_seqs or max_num_batched_tokens. This reduces the number of concurrent requests in a batch, thereby requiring less KV cache space. Increase tensor_parallel_size. This shards model weights across GPUs, allowing each GPU to have more memory available for KV cache. However, increasing this value may cause excessive synchronization overhead. Increase pipeline_parallel_size. This distributes model layers across GPUs, reducing the memory needed for model weights on each GPU, indirectly leaving more memory available for KV cache. However, increasing this value may cause latency penalties. **File:** docs/configuration/optimization.md (L43-48) ```markdown You can tune the performance by adjusting `max_num_batched_tokens`: - Smaller values (e.g., 2048) achieve better inter-token latency (ITL) because there are fewer prefills slowing down decodes. - Higher values achieve better time to first token (TTFT) as you can process more prefill tokens in a batch. - For optimal throughput, we recommend setting `max_num_batched_tokens \u0026gt; 8192` especially for smaller models on large GPUs. - If `max_num_batched_tokens` is the same as `max_model_len`, that\u0026#39;s almost the equivalent to the V0 default scheduling policy (except that it still prioritizes decodes). ","date":"2026-01-05T00:00:00Z","permalink":"/%E4%B8%80%E4%BA%9B%E5%8F%82%E6%95%B0/","title":"一些参数"},{"content":"Megatron-LM 详尽文档 目录 项目概述 架构设计 核心组件 训练脚本 并行策略 数据处理 优化器与调度器 模型实现 示例与工具 性能优化 项目概述 什么是 Megatron-LM？ Megatron-LM 是 NVIDIA 开发的用于大规模训练 Transformer 模型的 GPU 优化库。它包含两个主要部分：\nMegatron Core: 可组合的生产级库，提供 GPU 优化的构建模块 Megatron-LM: 参考实现，包含完整的训练脚本和示例 版本信息 当前版本: 0.14.0 (2025) 许可证: Apache 2.0 Python 版本: 3.12 推荐 PyTorch: 最新稳定版本 主要特性 ✅ 大规模训练: 支持从 2B 到 462B+ 参数的模型\n✅ 高性能: 在 H100 集群上达到 47% 的 Model FLOP Utilization (MFU)\n✅ 多种并行策略: TP, PP, DP, CP, EP 及其组合\n✅ 混合精度: FP16, BF16, FP8 支持\n✅ 模型支持: GPT, LLaMA, Mixtral, Mamba, BERT, T5, 多模态等\n✅ 容错训练: 集成 NVRx 进行故障检测和恢复\n架构设计 项目结构 Megatron-LM/\n├── megatron/ # 核心代码库\n│ ├── core/ # Megatron Core (生产级库)\n│ │ ├── models/ # 模型实现 (GPT, BERT, T5, Mamba, 多模态等)\n│ │ ├── transformer/ # Transformer 构建块\n│ │ ├── tensor_parallel/ # 张量并行\n│ │ ├── pipeline_parallel/ # 流水线并行\n│ │ ├── distributed/ # 分布式训练 (FSDP, DDP)\n│ │ ├── optimizer/ # 优化器\n│ │ ├── datasets/ # 数据集加载器\n│ │ ├── inference/ # 推理引擎\n│ │ └── export/ # 模型导出 (TensorRT-LLM)\n│ ├── training/ # 训练基础设施\n│ ├── inference/ # 推理服务器\n│ ├── legacy/ # 遗留组件\n│ └── post_training/ # 后训练 (RLHF 等)\n├── examples/ # 训练示例\n├── tools/ # 实用工具\n├── tests/ # 测试套件\n└── docs/ # 文档\n设计理念 Megatron Core: 可组合库 目标用户:\n框架开发者构建自定义训练框架 研究团队需要自定义训练循环 ML 工程师构建容错训练流水线 提供内容:\n可组合的 Transformer 构建块 (attention, MLP 等) 高级并行策略 (TP, PP, DP, EP, CP) 流水线调度和分布式优化器 GPU 优化内核和内存管理 高性能数据加载器 Megatron-LM: 参考实现 目标用户:\n训练最先进的基础模型 研究团队探索新架构 学习分布式训练概念 快速实验 提供内容:\n预配置的训练脚本 (GPT, LLaMA, DeepSeek, Qwen 等) 端到端示例 (从数据准备到评估) 研究工具和实用程序 核心组件 1. Parallel State ( megatron/core/parallel_state.py)\n功能: 管理所有并行组和全局状态\n关键组件:\n张量并行组 _TENSOR_MODEL_PARALLEL_GROUP\n_TENSOR_MODEL_PARALLEL_WORLD_SIZE\n_TENSOR_MODEL_PARALLEL_RANK\n流水线并行组 _PIPELINE_MODEL_PARALLEL_GROUP\n_PIPELINE_MODEL_PARALLEL_WORLD_SIZE\n_PIPELINE_MODEL_PARALLEL_RANK\n数据并行组 _DATA_PARALLEL_GROUP\n_DATA_PARALLEL_WORLD_SIZE\n_DATA_PARALLEL_RANK\n专家并行组 (MoE) _EXPERT_MODEL_PARALLEL_GROUP\n_EXPERT_TENSOR_PARALLEL_GROUP\n上下文并行组 _CONTEXT_PARALLEL_GROUP\n_HIERARCHICAL_CONTEXT_PARALLEL_GROUPS\n主要函数:\ninitialize_model_parallel(): 初始化所有并行组 get_tensor_model_parallel_group(): 获取 TP 组 get_pipeline_model_parallel_group(): 获取 PP 组 get_data_parallel_group(): 获取 DP 组 RankGenerator: 生成不同并行模式的 rank 组 2. Transformer Config ( megatron/core/transformer/transformer_config.py)\n功能: 统一的 Transformer 配置对象\n核心配置类:\nTransformerConfig 包含所有 Transformer 和并行配置:\n@dataclass\nclass TransformerConfig:\n# 模型架构 num_layers: int = 0 hidden_size: int = 0 num_attention_heads: int = 0 ffn_hidden_size: int = None # 注意力配置 num_query_groups: int = None # GQA kv_channels: int = None attention_dropout: float = 0.1 # 激活函数 gated_linear_unit: bool = False activation_func: Callable = F.gelu # 并行配置 tensor_model_parallel_size: int = 1 pipeline_model_parallel_size: int = 1 context_parallel_size: int = 1 expert_model_parallel_size: int = 1 # 混合精度 fp16: bool = False bf16: bool = False fp8: str = None # MoE 配置 num_moe_experts: int = None moe_router_topk: int = 2 moe_grouped_gemm: bool = False # 优化 recompute_granularity: str = None recompute_method: str = None sequence_parallel: bool = False MLATransformerConfig 用于 Multi-Latent Attention (DeepSeek-V3):\n@dataclass\nclass MLATransformerConfig(TransformerConfig):\nq_lora_rank: int = 1536 kv_lora_rank: int = 512 qk_nope_head_dim: int = 128 qk_rope_head_dim: int = 64 v_head_dim: int = 128 3. Transformer Block ( megatron/core/transformer/transformer_block.py)\n功能: 核心 Transformer 层实现\n主要类:\nTransformerBlock: 主 Transformer 块 TransformerLayer: 单个 Transformer 层 Attention: 注意力机制 MLP: 前馈网络 特性:\n支持多种注意力机制 (self-attention, cross-attention, MLA) 激活检查点 序列并行 CUDA Graph 支持 4. 模型实现 (megatron/core/models/) GPT Model (gpt/gpt_model.py) class GPTModel(MegatronModule):\n\u0026quot;\u0026quot;\u0026quot;GPT Transformer 语言模型 Args: config: TransformerConfig transformer_layer_spec: 层规范 vocab_size: 词汇表大小 max_sequence_length: 最大序列长度 pre_process: 是否包含嵌入层 post_process: 是否包含输出层 share_embeddings_and_output_weights: 共享嵌入和输出权重 \u0026quot;\u0026quot;\u0026quot; def __init__(self, config, transformer_layer_spec, vocab_size, ...): # 嵌入层 self.embedding = LanguageModelEmbedding(...) # Transformer 解码器 self.decoder = TransformerBlock(...) # 输出层 self.output_layer = ColumnParallelLinear(...) def forward(self, input_ids, position_ids, attention_mask, labels=None): # 嵌入 hidden_states = self.embedding(input_ids, position_ids) # Transformer 层 hidden_states = self.decoder(hidden_states, attention_mask) # 输出 logits logits = self.output_layer(hidden_states) # 计算损失 if labels is not None: loss = self.compute_language_model_loss(labels, logits) return loss return logits 其他模型 BERT (bert/bert_model.py): 双向编码器 T5 (T5/t5_model.py): 编码器-解码器 Mamba (mamba/mamba_model.py): 状态空间模型 Mixtral (MoE): 专家混合模型 多模态 (multimodal/): 视觉-语言模型 (LLaVA, NVLM) 训练脚本 预训练脚本 1. GPT 预训练 (pretrain_gpt.py) def model_provider(pre_process=True, post_process=True):\n\u0026quot;\u0026quot;\u0026quot;构建 GPT 模型\u0026quot;\u0026quot;\u0026quot; config = get_language_model_config(args) model = GPTModel( config=config, transformer_layer_spec=get_gpt_layer_with_transformer_engine_spec(), vocab_size=args.padded_vocab_size, max_sequence_length=args.max_position_embeddings, pre_process=pre_process, post_process=post_process ) return model def train_valid_test_datasets_provider(train_val_test_num_samples):\n\u0026quot;\u0026quot;\u0026quot;构建训练/验证/测试数据集\u0026quot;\u0026quot;\u0026quot; config = GPTDatasetConfig( random_seed=args.seed, sequence_length=args.seq_length, blend=args.data_path, split=args.split ) return BlendedMegatronDatasetBuilder( GPTDataset, train_val_test_num_samples, config ).build() def forward_step(data_iterator, model):\n\u0026quot;\u0026quot;\u0026quot;前向传播步骤\u0026quot;\u0026quot;\u0026quot; tokens, labels, loss_mask, attention_mask, position_ids = get_batch(data_iterator) output_tensor = model(tokens, position_ids, attention_mask, labels=labels) return output_tensor, partial(loss_func, loss_mask) if name == “main”:\npretrain( train_valid_test_datasets_provider, model_provider, ModelType.encoder_or_decoder, forward_step ) 2. Mamba 预训练 (pretrain_mamba.py) from mamba_builders import mamba_builder\ndef forward_step(data_iterator, model: MambaModel):\n\u0026quot;\u0026quot;\u0026quot;Mamba 前向传播\u0026quot;\u0026quot;\u0026quot; tokens, labels, loss_mask, attention_mask, position_ids = get_batch(data_iterator) output_tensor = model(tokens, position_ids, attention_mask, labels=labels) return output_tensor, partial(loss_func, loss_mask, model=model) if name == “main”:\npretrain( train_valid_test_datasets_provider, partial(model_provider, mamba_builder), ModelType.encoder_or_decoder, forward_step, args_defaults={\u0026#39;tokenizer_type\u0026#39;: \u0026#39;GPT2BPETokenizer\u0026#39;} ) 3. 其他预训练脚本 pretrain_bert.py: BERT 预训练 pretrain_t5.py: T5 预训练 pretrain_retro.py: RETRO 预训练 pretrain_vlm.py: 视觉-语言模型预训练 train_rl.py: 强化学习训练 (RLHF) 训练主循环 (megatron/training/training.py) 核心函数:\ndef pretrain(\ntrain_valid_test_datasets_provider, model_provider, model_type, forward_step_func, ... ):\n\u0026quot;\u0026quot;\u0026quot;主训练函数 流程: 1. 初始化 Megatron 2. 设置模型、优化器和学习率调度器 3. 加载检查点 4. 训练循环 5. 保存检查点 \u0026quot;\u0026quot;\u0026quot; # 初始化 initialize_megatron(...) # 构建模型和优化器 model, optimizer, opt_param_scheduler = setup_model_and_optimizer( model_provider, model_type ) # 加载检查点 iteration = load_checkpoint(model, optimizer, opt_param_scheduler) # 训练循环 while iteration \u0026lt; args.train_iters: train_step(forward_step_func, data_iterator, model, optimizer, ...) if iteration % args.save_interval == 0: save_checkpoint(iteration, model, optimizer, opt_param_scheduler) 关键训练步骤:\ndef train_step(forward_step_func, data_iterator, model, optimizer, …):\n\u0026quot;\u0026quot;\u0026quot;单个训练步骤 1. 前向-反向传播 2. 梯度裁剪 3. 优化器步骤 4. 学习率调度 \u0026quot;\u0026quot;\u0026quot; # 前向-反向传播 losses_reduced = forward_backward_func( forward_step_func, data_iterator, model, ... ) # 梯度裁剪 grad_norm = clip_gradients(model, optimizer, ...) # 优化器步骤 optimizer.step() # 学习率调度 opt_param_scheduler.step(increment=1) return losses_reduced 并行策略 1. 数据并行 (Data Parallelism, DP) 原理: 在多个 GPU 上复制模型，每个 GPU 处理不同的数据批次\n实现方式:\n标准 DDP –data-parallel-sharding-strategy no_shard\nFully Sharded Data Parallel (FSDP) Megatron 优化的 FSDP (~15% 比 PyTorch FSDP2 快) –use-custom-fsdp\nPyTorch FSDP2 –use-torch-fsdp2\n分片策略 –data-parallel-sharding-strategy optim # ZeRO-1: 分片优化器状态\n–data-parallel-sharding-strategy optim_grads # ZeRO-2: 分片梯度 + 优化器\n–data-parallel-sharding-strategy optim_grads_params # ZeRO-3: 分片参数 + 梯度 + 优化器\n代码实现 (megatron/core/distributed/):\n自定义 FSDP from megatron.core.distributed.fsdp import MegatronFSDP\nmodel = MegatronFSDP(\nmodule=model, sharding_strategy=ShardingStrategy.FULL_SHARD, # ZeRO-3 mixed_precision=mixed_precision_policy, )\n2. 张量并行 (Tensor Parallelism, TP) 原理: 在多个 GPU 上分割单个层的张量\n使用场景:\n单个层太大无法放入单个 GPU 减少激活内存占用 配置:\n–tensor-model-parallel-size 4 # 4-way TP\n–sequence-parallel # 启用序列并行 (推荐与 TP 一起使用)\n实现 (megatron/core/tensor_parallel/):\n列并行线性层 class ColumnParallelLinear(torch.nn.Module):\n\u0026quot;\u0026quot;\u0026quot;列并行线性层 将权重矩阵按列分割: Y = XA, A = [A1 | A2 | ... | Ap] 每个 GPU 计算 Yi = X * Ai \u0026quot;\u0026quot;\u0026quot; def forward(self, input_): # 输入在 TP 组内全收集 (如果需要) input_parallel = copy_to_tensor_model_parallel_region(input_) # 本地矩阵乘法 output_parallel = F.linear(input_parallel, self.weight) # 输出保持分割状态 return output_parallel 行并行线性层 class RowParallelLinear(torch.nn.Module):\n\u0026quot;\u0026quot;\u0026quot;行并行线性层 将权重矩阵按行分割: Y = XA, A = [A1; A2; ... ; Ap] 每个 GPU 计算 Yi = X * Ai，然后 all-reduce \u0026quot;\u0026quot;\u0026quot; def forward(self, input_): # 输入已经分割 output_parallel = F.linear(input_, self.weight) # All-reduce 跨 TP 组 output = reduce_from_tensor_model_parallel_region(output_parallel) return output 序列并行 在序列维度上分割激活 LayerNorm 输入: [seq_len, batch, hidden] -\u0026gt; [seq_len/tp, batch, hidden] class SequenceParallelLayerNorm(torch.nn.Module):\ndef forward(self, input_): # All-gather 跨序列维度 input_gathered = gather_from_sequence_parallel_region(input_) # LayerNorm output = F.layer_norm(input_gathered, ...) # Scatter 回序列维度 output_scattered = scatter_to_sequence_parallel_region(output) return output_scattered 3. 流水线并行 (Pipeline Parallelism, PP) 原理: 按深度分割模型，不同 GPU 处理不同层\n配置:\n–pipeline-model-parallel-size 8 # 8 个流水线阶段\n–virtual-pipeline-model-parallel-size 4 # 虚拟流水线以改善负载平衡\n流水线调度 (megatron/core/pipeline_parallel/schedules.py):\n1F1B (One-Forward-One-Backward) def forward_backward_pipelining_with_interleaving(\nforward_step_func, data_iterator, model, num_microbatches, ... ):\n\u0026quot;\u0026quot;\u0026quot;交错 1F1B 流水线调度 阶段: 1. Warmup: 填充流水线 2. 1F1B: 一个前向，一个反向 3. Cooldown: 清空流水线 \u0026quot;\u0026quot;\u0026quot; # Warmup 阶段 for i in range(num_warmup_microbatches): output_tensor = forward_step(...) # 1F1B 阶段 for i in range(num_microbatches - num_warmup_microbatches): output_tensor = forward_step(...) input_tensor_grad = backward_step(...) # Cooldown 阶段 for i in range(num_warmup_microbatches): input_tensor_grad = backward_step(...) P2P 通信 def send_forward(output_tensor, config):\n\u0026quot;\u0026quot;\u0026quot;发送激活到下一个流水线阶段\u0026quot;\u0026quot;\u0026quot; if not is_pipeline_last_stage(): send(output_tensor, get_pipeline_model_parallel_next_rank()) def recv_forward(config):\n\u0026quot;\u0026quot;\u0026quot;从上一个流水线阶段接收激活\u0026quot;\u0026quot;\u0026quot; if not is_pipeline_first_stage(): return recv(get_pipeline_model_parallel_prev_rank()) 4. 上下文并行 (Context Parallelism, CP) 原理: 在多个 GPU 上分割长序列\n使用场景:\n处理超长上下文 (\u0026gt;8K tokens) 减少注意力计算的内存占用 配置:\n–context-parallel-size 2 # 2-way CP\n–cp-comm-type p2p # 通信类型: p2p, a2a, allgather\n–hierarchical-context-parallel-sizes 2 4 # 分层 CP\n实现:\nRing Attention with CP def ring_attention_forward(query, key, value, cp_group):\n\u0026quot;\u0026quot;\u0026quot;Ring Attention 实现 每个 GPU 持有序列的一部分: GPU 0: Q0, K0, V0 GPU 1: Q1, K1, V1 通过环形通信计算完整注意力 \u0026quot;\u0026quot;\u0026quot; for step in range(cp_size): # 本地注意力计算 attn_output = scaled_dot_product_attention(query, key, value) # 发送 KV 到下一个 rank send_recv_kv(key, value, cp_group) 5. 专家并行 (Expert Parallelism, EP) 原理: 在 MoE 模型中分割专家\n配置:\n–expert-model-parallel-size 4 # 4-way EP\n–num-experts 8 # 每个 MoE 层 8 个专家\n–moe-grouped-gemm # 优化专家计算\n实现 (megatron/core/transformer/moe/):\nclass MoELayer(torch.nn.Module):\n\u0026quot;\u0026quot;\u0026quot;MoE 层实现 组件: 1. Router: 将 tokens 路由到专家 2. Experts: 专家网络 3. Token Dispatcher: 管理 token 分发 \u0026quot;\u0026quot;\u0026quot; def forward(self, hidden_states): # 路由 router_logits = self.router(hidden_states) topk_weights, topk_indices = torch.topk(router_logits, k=self.top_k) # Token 分发 (All-to-All) dispatched_input = self.token_dispatcher.dispatch( hidden_states, topk_indices ) # 专家计算 (每个 GPU 上的本地专家) expert_output = self.experts(dispatched_input) # Token 收集 (All-to-All) output = self.token_dispatcher.combine( expert_output, topk_weights, topk_indices ) return output 并行策略组合 推荐配置 (基于 NVIDIA NeMo) 模型 大小 GPUs TP PP CP EP 备注 LLaMA-3 8B 8 1 1 2 1 CP 用于长序列 (8K) LLaMA-3 70B 64 4 4 2 1 TP+PP LLaMA-3.1 405B 1024 8 8 2 1 3D 并行 GPT-3 175B 128-512 4 8 1 1 大模型配置 Mixtral 8x7B 64 1 4 1 8 EP 用于 MoE Mixtral 8x22B 256 4 4 8 8 TP+EP 用于大 MoE DeepSeek-V3 671B 1024 2 16 1 64 大 MoE 配置 重要: 当组合 EP 和 TP 时，必须启用序列并行 (SP)\n数据处理 数据集实现 (megatron/core/datasets/) 1. GPT Dataset (gpt_dataset.py) class GPTDataset(MegatronDataset):\n\u0026quot;\u0026quot;\u0026quot;GPT 数据集 功能: - 从二进制索引文件加载 - 动态采样和混合 - 支持文档级别的采样 \u0026quot;\u0026quot;\u0026quot; def __getitem__(self, idx): # 获取文档索引 doc_idx = self.doc_idx[idx] # 采样序列 tokens = self.indexed_dataset[doc_idx] # 创建训练样本 tokens = tokens[:self.seq_length + 1] input_ids = tokens[:-1] labels = tokens[1:] return { \u0026#39;input_ids\u0026#39;: input_ids, \u0026#39;labels\u0026#39;: labels, \u0026#39;loss_mask\u0026#39;: torch.ones_like(labels) } 2. Blended Dataset (blended_megatron_dataset_builder.py) class BlendedMegatronDatasetBuilder:\n\u0026quot;\u0026quot;\u0026quot;混合数据集构建器 支持: - 多个数据集混合 - 自定义混合权重 - 训练/验证/测试分割 \u0026quot;\u0026quot;\u0026quot; def build(self): # 构建单个数据集 datasets = [] for path, weight in zip(self.paths, self.weights): dataset = self.dataset_cls(path, ...) datasets.append((dataset, weight)) # 混合数据集 blended_dataset = BlendedDataset(datasets, self.weights) # 分割 train_ds, valid_ds, test_ds = split_dataset( blended_dataset, self.split ) return train_ds, valid_ds, test_ds 数据预处理 工具: tools/preprocess_data.py 基本预处理 python tools/preprocess_data.py\n--input data.jsonl \\ --output-prefix processed_data \\ --tokenizer-type HuggingFaceTokenizer \\ --tokenizer-model /path/to/tokenizer.model \\ --workers 8 \\ --append-eod 输出文件: - processed_data_text_document.bin # 二进制数据 - processed_data_text_document.idx # 索引文件 JSONL 格式:\n{“text”: “Your training text here…”}\n{“text”: “Another training sample…”}\n索引数据集 (indexed_dataset.py) class MMapIndexedDataset:\n\u0026quot;\u0026quot;\u0026quot;内存映射索引数据集 特性: - 零拷贝加载 - 快速随机访问 - 支持大规模数据集 \u0026quot;\u0026quot;\u0026quot; def __init__(self, path): # 加载索引 self.index = MMapIndexedDatasetIndex(path + \u0026#39;.idx\u0026#39;) # 内存映射二进制文件 self.bin_buffer = np.memmap( path + \u0026#39;.bin\u0026#39;, dtype=self.index.dtype, mode=\u0026#39;r\u0026#39; ) def __getitem__(self, idx): # 从索引获取偏移量和大小 ptr, size = self.index[idx] # 从内存映射缓冲区读取 return self.bin_buffer[ptr:ptr+size] 优化器与调度器 优化器 (megatron/core/optimizer/) 1. 分布式优化器 (distrib_optimizer.py) class DistributedOptimizer:\n\u0026quot;\u0026quot;\u0026quot;分布式优化器 特性: - 在数据并行组内分片优化器状态 - 减少内存占用 - 加速检查点保存/加载 \u0026quot;\u0026quot;\u0026quot; def __init__(self, optimizer, config): self.optimizer = optimizer # 分片参数 self.param_to_rank = self._shard_parameters() def step(self): # 本地优化器步骤 self.optimizer.step() # All-gather 更新的参数 self._all_gather_parameters() 使用:\n–use-distributed-optimizer\n2. CPU 卸载 (cpu_offloading/) 将优化器状态卸载到 CPU 以节省 GPU 内存 –cpu-optimizer\n3. 支持的优化器 Adam: 标准 Adam AdamW: 带权重衰减的 Adam SGD: 随机梯度下降 FusedAdam: Apex 融合 Adam (更快) 学习率调度器 (optimizer_param_scheduler.py) class OptimizerParamScheduler:\n\u0026quot;\u0026quot;\u0026quot;优化器参数调度器 支持: - 学习率调度 - 权重衰减调度 - Warmup \u0026quot;\u0026quot;\u0026quot; def step(self, increment): # 更新迭代计数 self.iteration += increment # 计算学习率 lr = self._get_learning_rate() # 更新优化器 for param_group in self.optimizer.param_groups: param_group[\u0026#39;lr\u0026#39;] = lr 调度策略:\n余弦衰减 –lr-decay-style cosine\n–lr 1e-4\n–min-lr 1e-5\n–lr-warmup-iters 1000\n–lr-decay-iters 100000\n线性衰减 –lr-decay-style linear\n常数学习率 –lr-decay-style constant\n梯度裁剪 (clip_grads.py) def clip_grad_norm(parameters, max_norm, norm_type=2):\n\u0026quot;\u0026quot;\u0026quot;梯度范数裁剪 在分布式设置中: 1. 计算本地梯度范数 2. All-reduce 跨数据并行组 3. 裁剪梯度 \u0026quot;\u0026quot;\u0026quot; # 计算本地范数 total_norm = 0.0 for p in parameters: if p.grad is not None: param_norm = p.grad.data.norm(norm_type) total_norm += param_norm ** norm_type total_norm = total_norm ** (1.0 / norm_type) # All-reduce torch.distributed.all_reduce( total_norm, group=get_data_parallel_group() ) # 裁剪 clip_coef = max_norm / (total_norm + 1e-6) if clip_coef \u0026lt; 1.0: for p in parameters: if p.grad is not None: p.grad.data.mul_(clip_coef) return total_norm 模型实现 1. GPT 模型族 GPT-3 / GPT-NeoX 175B GPT-3 –num-layers 96\n–hidden-size 12288\n–num-attention-heads 96\n–seq-length 2048\n–max-position-embeddings 2048\nLLaMA 系列 LLaMA-3 8B:\n–num-layers 32\n–hidden-size 4096\n–ffn-hidden-size 14336\n–num-attention-heads 32\n–num-query-groups 8 # GQA\n–seq-length 8192\n–max-position-embeddings 8192\n–use-rotary-position-embeddings\n–swiglu\n–normalization RMSNorm\nLLaMA-3 70B:\n–num-layers 80\n–hidden-size 8192\n–ffn-hidden-size 28672\n–num-attention-heads 64\n–num-query-groups 8\n2. MoE 模型 Mixtral 8x7B –num-layers 32\n–hidden-size 4096\n–num-attention-heads 32\n–num-query-groups 8\n–num-experts 8\n–moe-router-topk 2\n–expert-model-parallel-size 8\n–moe-grouped-gemm\nDeepSeek-V3 671B –num-layers 61\n–hidden-size 7168\n–num-attention-heads 128\n–num-experts 256\n–moe-router-topk 8\n–expert-model-parallel-size 64\n–use-mla # Multi-Latent Attention\n–q-lora-rank 1536\n–kv-lora-rank 512\n3. Mamba (状态空间模型) Mamba 混合模型 –num-layers 24\n–hidden-size 2048\n–is-hybrid-model\n–mamba-state-dim 16\n–mamba-head-dim 64\n–mamba-num-groups 1\n4. 多模态模型 LLaVA 视觉编码器 + LLM –vision-model-type clip\n–vision-encoder-hidden-size 1024\n–language-model-type llama\n–add-cross-attention\n示例与工具 训练示例 (examples/) 1. 简单训练循环 2 GPU, 模拟数据 torchrun –nproc_per_node=2 examples/run_simple_mcore_train_loop.py\n2. LLaMA-3 训练 8 GPU, FP8 精度 ./examples/llama/train_llama3_8b_fp8.sh\n3. Mixtral MoE 训练 ./examples/mixtral/train_mixtral_8x7b.sh\n4. 多模态训练 LLaVA 训练 ./examples/multimodal/train_llava.sh\n工具 (tools/) 1. 数据预处理 文本数据 python tools/preprocess_data.py –input data.jsonl …\n多模态数据 python tools/preprocess_mmdata.py –input mm_data.jsonl …\n2. 检查点转换 Megatron -\u0026gt; Hugging Face python tools/checkpoint/convert_checkpoint.py\n--model-type GPT \\ --load-dir /path/to/megatron/checkpoint \\ --save-dir /path/to/hf/checkpoint \\ --target-tensor-parallel-size 1 \\ --target-pipeline-parallel-size 1 3. 推理服务器 GPT 推理 python tools/run_text_generation_server.py\n--load /path/to/checkpoint \\ --tensor-model-parallel-size 4 Mamba 推理 python tools/run_mamba_text_generation_server.py\n--load /path/to/checkpoint 4. 理论内存计算 估算模型内存需求 python tools/report_theoretical_memory.py\n--num-layers 32 \\ --hidden-size 4096 \\ --num-attention-heads 32 \\ --seq-length 2048 \\ --batch-size 8 \\ --tensor-parallel-size 4 \\ --pipeline-parallel-size 2 性能优化 1. 混合精度训练 FP16 –fp16\nBF16 (推荐用于大模型) –bf16\nFP8 (Hopper, Ada, Blackwell GPUs) –fp8-hybrid\n–fp8-recipe hybrid # 或 ‘delayed_scaling’\n2. FlashAttention 默认: cuDNN attention via Transformer Engine 提供高达 50% 的前向加速和 84% 的反向加速 使用 flash-attn 包 –use-flash-attn\n3. 激活检查点 基本激活检查点 –recompute-activations\n完全重计算 (极端内存约束) –recompute-granularity full\n–recompute-method uniform\n选择性重计算 –recompute-granularity selective\n–recompute-method block\n4. 通信优化 数据并行通信重叠 –overlap-grad-reduce # 重叠梯度 reduce\n–overlap-param-gather # 重叠参数 gather\n张量并行通信重叠 –tp-comm-overlap\n流水线并行通信重叠 默认启用 5. CUDA Graph –cuda-graph\n6. 融合内核 –use-fused-swiglu # 融合 SwiGLU 激活\n–use-fused-rotary-pos-emb # 融合 RoPE\n7. 分布式优化器 –use-distributed-optimizer\n8. MoE 优化 –moe-grouped-gemm # GroupedGEMM\n–moe-permute-fusion # 融合排列内核\n–moe-router-fusion # 融合路由器\n–moe-expert-parallel-overlap # EP All-to-All 重叠\n性能基准 配置:\n词汇表大小: 131,072 序列长度: 4096 H100 GPUs 结果:\n模型大小 GPUs MFU 吞吐量 (tokens/s/GPU) 22B 128 41% ~3200 175B 1024 47% ~2800 462B 6144 48% ~2600 超线性扩展: MFU 从 41% (22B) 增加到 47-48% (175B+)，因为更大的 GEMM 具有更高的算术强度。\n高级特性 1. 容错训练 (NVRx) 启用故障检测和恢复 –enable-fault-tolerance\n–check-for-nan-in-loss-and-grad\n–check-for-spiky-loss\n2. 多数据中心训练 N/S 连接支持 –multi-datacenter-training\n3. 检查点 保存 –save /path/to/checkpoints\n–save-interval 1000\n加载 –load /path/to/checkpoints\n分布式检查点 –use-dist-ckpt\n4. 日志和监控 TensorBoard –tensorboard-dir /path/to/logs\nWeights \u0026amp; Biases –wandb-project my-project\n–wandb-exp-name my-experiment\n5. 评估 验证间隔 –eval-interval 1000\n–eval-iters 100\n生态系统 使用 Megatron Core 的库 Megatron Bridge\nHugging Face ↔︎ Megatron 检查点转换 灵活的训练循环 生产就绪的配方 NeMo RL\nRLHF, DPO 等后训练方法 可扩展的强化学习 NeMo Framework\n企业框架 云原生支持 端到端示例 TensorRT Model Optimizer\n量化、剪枝、蒸馏 Megatron Core 使用的库 Megatron Energon\n多模态数据加载器 (文本、图像、视频、音频) Transformer Engine\n优化内核 FP8 混合精度 NVRx\n容错训练 常见问题 Q1: 如何选择并行策略？ 答:\n小模型 (\u0026lt;10B): DP only 中等模型 (10B-100B): TP + PP + DP 大模型 (\u0026gt;100B): TP + PP + DP + CP MoE 模型: 添加 EP Q2: 内存不足怎么办？ 答:\n启用激活检查点: --recompute-activations 使用 FSDP: --use-custom-fsdp --data-parallel-sharding-strategy optim_grads_params 增加 PP: --pipeline-model-parallel-size 减少批次大小: --micro-batch-size CPU 卸载: --cpu-optimizer Q3: 如何加速训练？ 答:\n使用 FP8: --fp8-hybrid 启用通信重叠: --overlap-grad-reduce --overlap-param-gather --tp-comm-overlap 使用 FlashAttention: 默认启用 启用 CUDA Graph: --cuda-graph 使用融合内核: --use-fused-swiglu --use-fused-rotary-pos-emb Q4: 如何调试？ 答:\n启用详细日志 –log-level DEBUG\n检查 NaN/Inf –check-for-nan-in-loss-and-grad\n使用模拟数据 –mock-data\n单 GPU 测试 –tensor-model-parallel-size 1\n–pipeline-model-parallel-size 1\n参考资源 官方文档 Megatron Core 文档 API 指南 开发者指南 论文 Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism Efficient Large-Scale Language Model Training on GPU Clusters GitHub Megatron-LM 仓库 Issues 社区 NVIDIA 开发者论坛 更新日志 v0.14.0 (2025) MoE 性能优化 (路由器融合、EP A2A 重叠) FP8 优化 (填充优化、重计算支持) 推理改进 (多批次 CUDA Graph、FP8 权重) MiMo 视频 VLM 支持 v0.13.0 灵活的非对称虚拟流水线并行 混合分片数据并行 (MoE) FP8 支持 Multi-Token-Prediction 细粒度重计算 v0.12.0 FP8 配方选择 上下文并行损失缩放修复 混合模型 CUDA Graph 支持 MoE CUDA Graph 支持 总结 Megatron-LM 是一个功能强大、高度优化的大规模 Transformer 模型训练框架。它提供:\n✅ 完整的并行策略: TP, PP, DP, CP, EP\n✅ 最先进的性能: 47% MFU on H100\n✅ 丰富的模型支持: GPT, LLaMA, Mixtral, Mamba, 多模态\n✅ 生产就绪: 容错、检查点、监控\n✅ 可扩展性: 从 1 GPU 到 6000+ GPUs\n无论您是研究人员、工程师还是框架开发者，Megatron-LM 都提供了构建和训练大规模语言模型所需的所有工具。\n","date":"2025-11-20T00:00:00Z","permalink":"/ai%E9%98%85%E8%AF%BBmegatron/","title":"ai阅读megatron"},{"content":"需要： - rice vit的hf权重 - llm的hf权重 - 转换代码\n对比项 Qwen2.5 ViT RiCE ViT Attention 类型 Window Attention Global Attention Class Token 无 使用 Class Token 激活函数 SiLU GeLU layer数 32 24 ","date":"2025-11-14T00:00:00Z","permalink":"/hf2megatron/","title":"hf2megatron"},{"content":"整体训练流程架构 这是一个基于Megatron-LM框架的多模态（视觉-语言）模型训练脚本。训练流程从__main__入口开始：\nif __name__ == \u0026quot;__main__\u0026quot;: train_valid_test_dataloaders_provider.is_distributed = True pretrain( train_valid_test_dataloaders_provider, model_provider, ModelType.encoder_or_decoder, forward_step, args_defaults={\u0026#39;tokenizer_type\u0026#39;: \u0026#39;Qwen2VLTokenizer\u0026#39;}, extra_args_provider=get_patch_args, process_non_loss_data_func=write_online_eval_to_tensorboard, non_loss_data_func=run_online_eval, ) 核心组件详解 1. 模型构建 (model_provider) def model_provider( pre_process=True, post_process=True, add_encoder=True, add_decoder=True, vp_stage: Optional[int] = None ) -\u0026gt; Union[Qwen2_5VLModel]: args = get_args() print_rank_0(\u0026quot;start building qwen2-vl model ...\u0026quot;) # Config of vit, llm and projector config = core_transformer_config_from_args(args, Qwen2VLTransformerConfig) use_te = args.transformer_impl == \u0026quot;transformer_engine\u0026quot; if not use_te: raise NotImplementedError(\u0026quot;The Qwen2-VL model is only implemented with TransformerEngine!\u0026quot;) if args.rotary_seq_len_interpolation_factor is not None or args.rotary_seq_len_interpolation_factor != 1: print_rank_0(\u0026#39;Multimodal RoPE currently not support RoPE interpolation, set to None...\u0026#39;) args.rotary_seq_len_interpolation_factor = None vision_config = get_vision_model_config(args, deepcopy(config)) vision_config.pipeline_model_parallel_size = 1 vision_config.num_layers_in_first_pipeline_stage = None vision_projector_config = get_vision_projection_config(deepcopy(config), vision_config.hidden_size, vision_config.spatial_merge_size) print_rank_0(\u0026quot;building Qwen2-5-VL model in TE...\u0026quot;) # Layer Specs of vit, llm and projector transformer_layer_spec = get_gpt_layer_with_transformer_engine_spec(args.qk_layernorm) vision_model_spec = get_qwen2vl_vision_model_spec() vision_projector_spec = get_mlp_module_spec(add_norm=False).submodules model = Qwen2_5VLModel( language_transformer_config=config, language_transformer_layer_spec=transformer_layer_spec, language_vocab_size=args.padded_vocab_size, language_max_sequence_length=args.max_position_embeddings, vision_transformer_config=vision_config, vision_transformer_layer_spec=vision_model_spec, drop_vision_class_token=False, # NOTE: no class token to drop? vision_projection_config=vision_projector_config, vision_projection_layer_spec=vision_projector_spec, vision_projection_type=\u0026#39;mlp\u0026#39;, allow_missing_vision_projection_checkpoint= False, # TODO: may parameterized language_position_embedding_type=args.position_embedding_type, language_rotary_percent=args.rotary_percent, language_rotary_base=args.rotary_base, pre_process=pre_process, post_process=post_process, add_decoder=add_decoder, add_encoder=add_encoder, fp16_lm_cross_entropy=args.fp16_lm_cross_entropy, parallel_output=True, language_share_embeddings_and_output_weights=not args.untie_embeddings_and_output_weights, vp_stage=vp_stage ) model.freeze( freeze_language_model=getattr(args, \u0026#39;freeze_LM\u0026#39;, False), freeze_vision_model=getattr(args, \u0026#39;freeze_ViT\u0026#39;, False), freeze_vision_projection=False ) return model 作用：构建Qwen2.5-VL多模态模型 - 创建三个组件的配置：语言模型、视觉模型、视觉投影层 - 使用TransformerEngine实现（强制要求） - 支持冻结语言模型或视觉模型的参数\n2. 数据加载 (train_valid_test_dataloaders_provider) def train_valid_test_dataloaders_provider(train_val_test_num_samples): \u0026quot;\u0026quot;\u0026quot;Build multimodal train, validation and test dataloaders.\u0026quot;\u0026quot;\u0026quot; if get_tensor_model_parallel_rank() != 0: return None, None, None args = get_args() worker_debug_path = None worker_log_level = 0 rank = parallel_state.get_data_parallel_rank() world_size = parallel_state.get_data_parallel_world_size() data_parallel_group = parallel_state.get_data_parallel_group() worker_config = WorkerConfig( rank=rank, world_size=world_size, num_workers=args.num_workers, data_parallel_group=data_parallel_group, worker_debug_path=worker_debug_path, worker_log_level=worker_log_level, ) train_ds, valid_ds1, test_ds = datasets_provider(worker_config) train_dataloader = get_savable_loader(train_ds, worker_config=worker_config) if args.load is not None: if getattr(args, \u0026quot;dataloader_save\u0026quot;, None): dp_rank = parallel_state.get_data_parallel_rank() data_save_name = get_checkpoint_name( args.dataloader_save, args.iteration, basename=f\u0026quot;train_dataloader_dprank{dp_rank:03d}.pt\u0026quot;, ) if os.path.exists(data_save_name): try: dataset_state_dict = torch.load(data_save_name, map_location=\u0026quot;cpu\u0026quot;) train_dataloader.restore_state_rank(dataset_state_dict[\u0026quot;dataloader_state_dict\u0026quot;]) print_rank_0(f\u0026quot;restored dataset state from {data_save_name}\u0026quot;) except Exception as e: print_rank_0(\u0026quot;loading dataloader checkpoint failed. Skipping. \u0026quot; + str(e)) valid_dataloader = [ EnergonDataloader(get_loader(valid_ds, worker_config=worker_config)) for valid_ds in valid_ds1 ] test_dataloader = None # NOTE: no test return EnergonDataloader(train_dataloader), valid_dataloader, None 作用：创建分布式数据加载器 - 只在tensor并行的rank 0上创建dataloader - 使用Megatron Energon框架处理多模态数据 - 支持断点续训时恢复dataloader状态\n3. 数据集创建 (datasets_provider) def datasets_provider(worker_config=None): \u0026quot;\u0026quot;\u0026quot;Create multimodal train, validation and test datasets.\u0026quot;\u0026quot;\u0026quot; args = get_args() train_dataset = get_train_dataset( args.train_data_path[0], batch_size=args.micro_batch_size, task_encoder=TaskEncoder(), worker_config=worker_config, virtual_epoch_length=1000, max_samples_per_sequence=100, shuffle_buffer_size=100, handler=print_error_handler, image_decode=\u0026quot;pil\u0026quot;, ) val_datasets = get_val_datasets( args.valid_data_path[0], batch_size=args.micro_batch_size, # This is the total number over all workers # limit=args.eval_iters * get_num_microbatches(), task_encoder=TaskEncoder(), worker_config=worker_config, handler=print_error_handler, image_decode=\u0026quot;pil\u0026quot;, ) val_datasets_without_source_datasets = [ # Limit the dataset to eval_iters * num_microbatches LimitDataset( # Repeat the inner dataset in case it\u0026#39;s too short RepeatDataset(val_ds, worker_config=worker_config), length=args.eval_iters * get_num_microbatches(), worker_config=worker_config, reset_after_epoch=True, ) for val_ds, _src_ds in val_datasets ] return train_dataset, val_datasets_without_source_datasets, None 作用：配置训练和验证数据集 - 使用TaskEncoder处理多模态任务 - 验证集使用RepeatDataset和LimitDataset包装，确保固定的评估步数\n4. 批次数据获取 (get_batch) def get_batch(model, data_iterator): \u0026quot;\u0026quot;\u0026quot;Generate a batch\u0026quot;\u0026quot;\u0026quot; imgs = None tokens = None labels = None loss_mask = None attention_mask = None position_ids = None # Broadcast data. torch.cuda.nvtx.range_push(\u0026quot;get_data\u0026quot;) if data_iterator is not None and get_tensor_model_parallel_rank() == 0: data = next(data_iterator) else: data = None data_text = broadcast_data([\u0026quot;text\u0026quot;], data, torch.int64)[\u0026quot;text\u0026quot;] target = broadcast_data([\u0026quot;target\u0026quot;], data, torch.int64)[\u0026quot;target\u0026quot;] # shape: num_tiles x c x h x w imgs = broadcast_data([\u0026quot;imgs\u0026quot;], data, torch.float32)[\u0026quot;imgs\u0026quot;] # shape: num_tiles x c x h w videos = broadcast_data([\u0026quot;videos\u0026quot;], data, torch.float32)[\u0026quot;videos\u0026quot;] # shape: n_image_samples image_thw_grids = broadcast_data([\u0026quot;image_thw_grids\u0026quot;], data, torch.long)[\u0026quot;image_thw_grids\u0026quot;] # shape: n_video_samples video_thw_grids = broadcast_data([\u0026quot;video_thw_grids\u0026quot;], data, torch.long)[\u0026quot;video_thw_grids\u0026quot;] # shape: n_video_samples second_per_grid_ts = broadcast_data([\u0026#39;second_per_grid_ts\u0026#39;], data, torch.float32)[\u0026#39;second_per_grid_ts\u0026#39;] image_input_mask = broadcast_data([\u0026quot;image_input_mask\u0026quot;], data, torch.bool)[\u0026quot;image_input_mask\u0026quot;] video_input_mask = broadcast_data([\u0026quot;video_input_mask\u0026quot;], data, torch.bool)[\u0026quot;video_input_mask\u0026quot;] torch.cuda.nvtx.range_pop() torch.cuda.nvtx.range_push(\u0026quot;index tokens\u0026quot;) tokenizer = get_tokenizer() tokens = data_text.long().contiguous() labels = target.contiguous() assert tokens.shape == labels.shape, f\u0026quot;tokens: {tokens.shape} != labels: {labels.shape}\u0026quot; torch.cuda.nvtx.range_pop() # NOTE: no sequence packing in LLM inputs torch.cuda.nvtx.range_push(\u0026quot;get_ltor_masks_and_position_ids\u0026quot;) attention_mask, loss_mask, position_ids = get_ltor_masks_and_position_ids( model, tokens, image_thw_grids, video_thw_grids, labels, tokenizer.pad_token_id, second_per_grid_ts ) torch.cuda.nvtx.range_pop() return ( tokens, labels, loss_mask, attention_mask, position_ids, imgs, videos, image_thw_grids, video_thw_grids, image_input_mask, video_input_mask ) 作用：从数据迭代器获取并处理一个batch - 使用broadcast_data在tensor并行组间广播数据（只有rank 0读取数据） - 处理多模态输入：文本tokens、图像、视频 - 获取位置编码和损失掩码 - 使用NVTX标记进行性能分析\n5. 位置编码和掩码生成 (get_ltor_masks_and_position_ids) def get_ltor_masks_and_position_ids( model, input_ids, image_thw_grids, video_thw_grids, target, pad_token, second_per_grid_ts, ignore_index=None ): \u0026quot;\u0026quot;\u0026quot;Build masks and position id for left to right model.\u0026quot;\u0026quot;\u0026quot; # Position ids. [3 X bs X seqlen] position_ids, _ = model.get_rope_index( input_ids=input_ids, image_grid_thw=image_thw_grids, video_grid_thw=video_thw_grids, second_per_grid_ts=second_per_grid_ts, attention_mask=input_ids != pad_token ) # Loss mask. loss_mask = torch.ones(target.size(), dtype=torch.float, device=input_ids.device) loss_mask[target == pad_token] = 0.0 # mask paddings if ignore_index is not None: loss_mask[target == ignore_index] = 0.0 # mask prompts # Attention mask. attention_mask = None return attention_mask, loss_mask, position_ids 作用：为多模态输入生成位置编码 - 使用特殊的RoPE索引处理图像/视频的时空网格信息 - position_ids是3维的（支持多模态RoPE） - 创建损失掩码，忽略padding和可选的prompt部分\n6. 前向传播 (forward_step) def forward_step(data_iterator, model: Qwen2_5VLModel): \u0026quot;\u0026quot;\u0026quot;Forward training step. Args: data_iterator : Input data iterator model (GPTModel): The GPT Model \u0026quot;\u0026quot;\u0026quot; timers = get_timers() # Get the batch. timers(\u0026quot;batch-generator\u0026quot;, log_level=2).start() ( tokens, labels, loss_mask, attention_mask, position_ids, imgs, videos, image_thw_grids, video_thw_grids, image_input_mask, video_input_mask ) = get_batch(unwrap_model(model), data_iterator) timers(\u0026quot;batch-generator\u0026quot;).stop() vision_data = torch.cat([imgs, videos], dim=0) vision_grid = torch.cat([image_thw_grids, video_thw_grids], dim=0) output_tensor = model( input_ids = tokens, position_ids = position_ids, vision_data = vision_data, vision_grid_thw = vision_grid, video_start_index = image_input_mask.sum().cpu().item(), image_input_mask = image_input_mask, video_input_mask = video_input_mask, attention_mask = attention_mask, labels = labels ) return output_tensor, partial(loss_func, loss_mask) 作用：执行一次训练前向传播 - 合并图像和视频数据 - 通过video_start_index区分图像和视频 - 返回输出和损失函数（使用partial绑定loss_mask）\n7. 损失计算 (loss_func) def loss_func(loss_mask: torch.Tensor, output_tensor: torch.Tensor): \u0026quot;\u0026quot;\u0026quot;Loss function. Args: loss_mask (torch.Tensor): Used to mask out some portions of the loss output_tensor (torch.Tensor): The tensor with the losses \u0026quot;\u0026quot;\u0026quot; args = get_args() losses = output_tensor.float() loss_mask = loss_mask.view(-1).float() loss = torch.stack([torch.sum(losses.view(-1) * loss_mask), loss_mask.sum()]) if args.context_parallel_size \u0026gt; 1: torch.distributed.all_reduce(loss, group=mpu.get_context_parallel_group()) # Check individual rank losses are not NaN prior to DP all-reduce. if args.check_for_nan_in_loss_and_grad: global_rank = torch.distributed.get_rank() assert not loss.isnan().any(), ( f\u0026quot;Rank {global_rank}: found NaN in local forward loss calculation. \u0026quot; f\u0026quot;Device: {torch.cuda.current_device()}, node: {os.uname()[1]}\u0026quot; ) averaged_loss = average_losses_across_data_parallel_group(loss) averaged_loss = averaged_loss[0] / averaged_loss[1] return loss[0] / loss[1] * args.context_parallel_size, {\u0026quot;lm loss\u0026quot;: averaged_loss} 作用：计算并聚合损失 - 应用loss_mask忽略特定tokens - 在context并行组和data并行组间聚合损失 - 支持NaN检测 - 返回局部损失和全局平均损失\n8. 辅助类和函数 EnergonDataloader class EnergonDataloader: \u0026quot;\u0026quot;\u0026quot;A wrapper to use Megatron Energon dataloader with the Megatron-LM training loop.\u0026quot;\u0026quot;\u0026quot; def __init__(self, dataloader): self._dataloader = dataloader self._iter = iter(cyclic_iter(dataloader)) def __next__(self): return self._iter.__next__() def __iter__(self): return self._iter.__iter__() def save_state(self): return self._dataloader.save_state_rank() 作用：包装Energon dataloader以兼容Megatron训练循环 - 提供无限循环的迭代器 - 支持保存和恢复状态\ncyclic_iter def cyclic_iter(iter): while True: for x in iter: yield x 作用：创建无限循环的迭代器\n评估相关函数 def run_online_eval(model): \u0026quot;\u0026quot;\u0026quot;Run an evaluation benchmark during training.\u0026quot;\u0026quot;\u0026quot; # Do nothing. return [] def write_online_eval_to_tensorboard(data, iteration, writer): \u0026quot;\u0026quot;\u0026quot;Write online evaluation data to Tensorboard.\u0026quot;\u0026quot;\u0026quot; if not writer: return for item in data: for k, v in item.items(): writer.add_scalar(k, v, iteration) 训练流程总结 初始化阶段： 解析命令行参数 初始化分布式环境（TP、PP、DP、CP） 创建tokenizer 模型构建阶段（model_provider）： 创建语言模型配置 创建视觉模型配置 创建视觉投影层配置 实例化Qwen2_5VLModel 应用参数冻结策略 数据准备阶段（train_valid_test_dataloaders_provider）： 配置分布式worker 创建训练/验证数据集 包装成Energon dataloader 训练循环（由Megatron的pretrain函数驱动）： 获取batch（get_batch） 前向传播（forward_step） 计算损失（loss_func） 反向传播（Megatron自动处理） 参数更新（Megatron自动处理） 定期验证和保存checkpoint 关键特性： 支持图像和视频的混合训练 使用3D RoPE位置编码处理时空信息 支持模型并行（TP、PP、DP、CP） 支持选择性参数冻结 使用TransformerEngine加速 这是一个完整的多模态大模型训练pipeline，充分利用了Megatron-LM的分布式训练能力。\n","date":"2025-11-12T00:00:00Z","permalink":"/training_model/","title":"training_model"},{"content":"LLaVA-OneVision-1.5 模型与Megatron训练框架详细文档 目录 项目概述 架构设计 模型实现 训练框架 数据处理 训练流程 模型转换 配置与优化 1. 项目概述 1.1 项目介绍 LLaVA-OneVision-1.5 是一个完全开源的大规模多模态模型(LMM)训练框架，具有以下特点： - 原生分辨率图像处理：支持高分辨率图像输入 - SOTA性能：在多个多模态基准测试上达到业界领先水平 - 高效训练：基于Megatron-LM优化，全流程训练成本约$16,000 - 完全开源：提供完整的训练框架、配置、数据集和训练日志\n1.2 核心特性 支持MoE（Mixture of Experts） FP8精度训练 长序列并行 原生分辨率图像输入（最大1344x1344） 端到端优化的训练流水线 1.3 技术栈 基础框架：Megatron-LM (NVIDIA) 语言模型：Qwen系列（Qwen2.5/Qwen3） 视觉模型：Rice-ViT（基于ViT优化） 训练框架：AIAK-Training-LLM（百度AIAK团队） 2. 架构设计 2.1 整体架构 项目采用模块化设计，主要包含以下几个核心模块：\nLLaVA-OneVision-1.5/ ├── aiak_megatron/ # Megatron-LM核心代码 │ └── megatron/ │ ├── core/ # 核心功能模块 │ │ ├── models/ # 模型定义 │ │ ├── transformer/ # Transformer实现 │ │ ├── optimizer/ # 优化器 │ │ └── pipeline_parallel/ # 流水线并行 │ └── training/ # 训练工具 ├── aiak_training_llm/ # AIAK训练框架 │ ├── models/ # 模型实现 │ │ ├── llavaov_1_5/ # LLaVA-OV-1.5模型 │ │ ├── qwen/ # Qwen语言模型 │ │ └── qwen_vl/ # Qwen-VL模型 │ ├── data/ # 数据处理 │ ├── train/ # 训练逻辑 │ │ ├── pretrain/ # 预训练 │ │ └── sft/ # 监督微调 │ └── tokenizer/ # 分词器 └── tools/ # 工具集 └── convert_checkpoint/ # 模型转换工具 2.2 模型工厂模式 项目使用工厂模式管理多个模型，核心文件：aiak_training_llm/models/factory.py\n2.2.1 注册机制 # 全局注册表 MODEL_ARCH_CONFIGS = {} # 模型架构配置 MODEL_ARCH_TO_FAMILY = {} # 架构到模型族映射 MODEL_FAMILY_TO_ARCHS = {} # 模型族到架构映射 MODEL_FAMILY_TO_PROVIDER = {} # 模型族到提供者映射 # 注册模型配置 @register_model_config(model_family=\u0026quot;llava-ov-1.5\u0026quot;, model_arch=\u0026quot;llava-ov-1.5-4b\u0026quot;) def llava_one_vision_1_5_4b(): return LlavaOnevision1_5Config( num_layers=36, hidden_size=2560, ffn_hidden_size=9728, num_attention_heads=32, group_query_attention=True, num_query_groups=8, vocab_size_in_config_file=151936, # ... 更多配置 ) # 注册模型提供者 @register_model_provider(model_family=[VisionLanguageModelFamilies.LLAVA_OV_1_5]) def rice_vl_model_provider(pre_process=True, post_process=True): # 模型构建逻辑 pass 2.3 训练器工厂模式 训练器也采用工厂模式注册：aiak_training_llm/train/trainer_builder.py\n@register_model_trainer( model_family=[constants.VisionLanguageModelFamilies.LLAVA_OV_1_5], training_phase=constants.TrainingPhase.PRETRAIN ) def default_pretrain_trainer(train_args): trainer = MegatronTrainer( train_args=train_args, train_valid_test_dataset_provider=train_valid_test_dataset_provider, model_provider=model_provider, model_type=model_type, forward_step_func=forward_step, ) return trainer 3. 模型实现 3.1 LlavaOnevision1_5 模型 核心文件：aiak_training_llm/models/llavaov_1_5/llavaov_1_5_model.py\n3.1.1 模型组成 LlavaOnevision1_5由三个主要组件构成：\nclass LlavaOnevision1_5(MegatronModule): def __init__(self, ...): # 1. 视觉编码器（Vision Encoder） if self.add_encoder: self.vision_model = RiceViTModel( vision_config, vision_layer_spec, ) # 2. 适配器（Adapter）- 连接视觉和语言模态 self.adapter = Adapter( adapter_config, adapter_layer_spec, input_size=vision_config.hidden_size, output_size=language_config.hidden_size, ) # 3. 语言模型（Language Model） if self.add_decoder: self.language_model = QwenModel( config=language_config, transformer_layer_spec=language_layer_spec, vocab_size=language_vocab_size, max_sequence_length=language_max_sequence_length, # ... 更多参数 ) 3.1.2 前向传播流程 def forward( self, images: torch.Tensor, # 图像输入 image_grid_thw: torch.Tensor, # 图像网格维度（时间、高、宽） input_ids: torch.Tensor, # 文本token ID position_ids: torch.Tensor, # 位置ID attention_mask: torch.Tensor, # 注意力掩码 labels: torch.Tensor = None, # 标签（训练时使用） packed_seq_params: PackedSeqParams = None, # 序列打包参数 inference_params: InferenceParams = None, # 推理参数 ): # 步骤1：视觉编码 if self.add_encoder and images is not None: # 通过视觉模型提取图像特征 image_embeddings, window_index = self.vision_model( images, grid_thw=image_grid_thw ) # 通过适配器映射到语言模型维度 image_embeddings = self.adapter(image_embeddings, window_index) # 步骤2：文本编码 if self.pre_process: language_embeddings = self.language_model.embedding( input_ids=input_ids, position_ids=None ) # 步骤3：融合视觉和语言嵌入 if images is not None: # 找到图像token位置并替换为视觉嵌入 images_mask = (input_ids == self.config.image_token_id) combined_embeddings = language_embeddings.masked_scatter( images_mask, image_embeddings ) # 步骤4：通过语言模型生成输出 output = self.language_model( input_ids=None, decoder_input=combined_embeddings, attention_mask=attention_mask, labels=labels, # ... 其他参数 ) return output 3.2 视觉模型：RiceViTModel 核心文件：aiak_training_llm/models/llavaov_1_5/rice_vision_model.py\n3.2.1 关键组件 class RiceViTModel(VisionModel): def __init__(self, config, transformer_layer_spec, spatial_merge_size=2): # 1. Patch嵌入层 self.patch_embed = PatchEmbed( patch_size=config.patch_size, # 14 in_channels=config.in_channels, # 3 embed_dim=config.hidden_size, # 1024 ) # 2. 旋转位置编码 self.rotary_pos_emb = VisionRotaryEmbedding( dim=config.kv_channels // 2 ) # 3. Transformer块 self.decoder = TransformerBlock( config=config, spec=transformer_layer_spec, ) # 4. 类别token（用于全局特征） self.class_embedding = torch.randn(config.hidden_size) self.class_pos_emb = torch.randn(1, config.kv_channels // 2) # 5. 预层归一化 self.pre_layernorm = torch.nn.LayerNorm( config.hidden_size, eps=1e-4 ) 3.2.2 位置编码策略 Rice-ViT使用特殊的2D旋转位置编码：\ndef rot_pos_emb(self, grid_thw): \u0026quot;\u0026quot;\u0026quot; 为每个图像patch生成2D旋转位置编码 grid_thw: [num_images, 3] - (时间, 高度, 宽度) \u0026quot;\u0026quot;\u0026quot; pos_ids = [] for t, h, w in grid_thw: # 生成高度位置ID hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w) hpos_ids = hpos_ids.reshape( h // self.spatial_merge_size, self.spatial_merge_size, w // self.spatial_merge_size, self.spatial_merge_size, ) hpos_ids = hpos_ids.permute(0, 2, 1, 3).flatten() # 生成宽度位置ID wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1) # ... 类似处理 ... pos_ids.append(torch.stack([hpos_ids, wpos_ids], dim=-1)) # 应用旋转位置编码 rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size) rotary_pos_emb = rotary_pos_emb_full[pos_ids].flatten(1) return rotary_pos_emb 3.3 语言模型：QwenModel 基于Qwen系列架构，支持： - GQA (Grouped Query Attention)：减少KV缓存内存 - RoPE位置编码：更好的长度外推能力 - SwiGLU激活：更好的性能 - RMSNorm：更稳定的训练\n3.4 适配器（Adapter） 核心文件：aiak_training_llm/models/qwen_vl/adapter.py\n适配器负责将视觉特征映射到语言模型空间：\nclass Adapter(MegatronModule): \u0026quot;\u0026quot;\u0026quot; 2层MLP适配器 Input: [seq_len, hidden_size_vision] Output: [seq_len, hidden_size_language] \u0026quot;\u0026quot;\u0026quot; def __init__(self, config, layer_spec, input_size, output_size): # 第一层：投影到中间维度 self.linear_fc1 = build_module( layer_spec.linear_fc1, input_size, config.ffn_hidden_size, ) # 激活函数（通常是GELU） self.activation_func = config.activation_func # 第二层：投影到输出维度 self.linear_fc2 = build_module( layer_spec.linear_fc2, config.ffn_hidden_size, output_size, ) 3.5 模型配置 3.5.1 支持的模型规格 模型 层数 隐藏层维度 FFN维度 注意力头数 参数量 llava-ov-1.5-3b 36 2048 11008 16 ~3B llava-ov-1.5-4b 36 2560 9728 32 ~4B llava-ov-1.5-8b 36 4096 12288 32 ~8B llava-ov-1.5-14b 40 5120 17408 40 ~14B llava-ov-1.5-32b 64 5120 25600 64 ~32B llava-ov-1.5-30b-a3b 48 2048 6144 32 ~30B (MoE) 3.5.2 视觉模型配置 VisionConfig( num_layers=24, # Transformer层数 hidden_size=1024, # 隐藏层维度 ffn_hidden_size=4096, # FFN维度 num_attention_heads=16, # 注意力头数 patch_size=14, # Patch大小 image_size=(1344, 1344), # 最大图像尺寸 kv_channels=64, # KV通道数 normalization=\u0026quot;LayerNorm\u0026quot;, # 归一化类型 position_embedding_type=\u0026quot;rope\u0026quot; # 位置编码类型 ) 4. 训练框架 4.1 训练器架构 4.1.1 MegatronTrainer 核心文件：aiak_training_llm/train/megatron_trainer.py\nclass MegatronTrainer(object): \u0026quot;\u0026quot;\u0026quot;Megatron训练器基类\u0026quot;\u0026quot;\u0026quot; def __init__( self, train_args, # 训练参数 train_valid_test_dataset_provider, # 数据集提供者 model_provider, # 模型提供者 model_type, # 模型类型 forward_step_func, # 前向传播函数 process_non_loss_data_func=None, # 非损失数据处理函数 get_embedding_ranks=None, # 获取嵌入层所在rank get_position_embedding_ranks=None, # 获取位置嵌入所在rank ): self.train_args = train_args self.train_valid_test_datasets_provider = train_valid_test_dataset_provider self.model_provider = model_provider self.model_type = model_type self.forward_step_func = forward_step_func # ... def train(self): \u0026quot;\u0026quot;\u0026quot;启动训练\u0026quot;\u0026quot;\u0026quot; pretrain( train_args=self.train_args, train_valid_test_dataset_provider=self.train_valid_test_datasets_provider, model_provider=self.model_provider, model_type=self.model_type, forward_step_func=self.forward_step_func, # ... ) 4.2 训练主流程 核心文件：aiak_training_llm/train/training_utils.py\n4.2.1 预训练主函数 def pretrain( train_args, train_valid_test_dataset_provider, model_provider, model_type, forward_step_func, process_non_loss_data_func=None, ): \u0026quot;\u0026quot;\u0026quot; 主训练流程： 1. 初始化Megatron 2. 构建模型、优化器和学习率调度器 3. 获取数据迭代器 4. 执行训练循环 \u0026quot;\u0026quot;\u0026quot; # 步骤1：初始化 initialize_aiak_megatron( args=train_args, get_embedding_ranks=get_embedding_ranks, get_position_embedding_ranks=get_position_embedding_ranks ) # 步骤2：构建模型和优化器 model, ema, optimizer, opt_param_scheduler = setup_model_and_optimizer( model_provider, model_type, checkpointing_context=checkpointing_context ) # 步骤3：构建数据迭代器 train_data_iterator, valid_data_iterator, test_data_iterator = \\ build_train_valid_test_data_iterators( train_valid_test_dataset_provider ) # 步骤4：训练循环 if args.do_train and args.train_iters \u0026gt; 0: iteration, num_floating_point_operations_so_far = train( forward_step_func=forward_step_func, model=model, ema=ema, optimizer=optimizer, opt_param_scheduler=opt_param_scheduler, train_data_iterator=train_data_iterator, valid_data_iterator=valid_data_iterator, # ... ) 4.2.2 训练循环 def train(forward_step_func, model, ema, optimizer, ...): \u0026quot;\u0026quot;\u0026quot;主训练循环\u0026quot;\u0026quot;\u0026quot; while iteration \u0026lt; args.train_iters: # 1. 执行训练步骤 loss_dict, skipped_iter, should_checkpoint, should_exit, exit_code, \\ grad_norm, num_zeros_in_grad = train_step( forward_step_func, train_data_iterator, model, optimizer, opt_param_scheduler, config ) # 2. 更新EMA模型（如果启用） if args.enable_ema and ema is not None: for e, m in zip(ema, model): update_ema(e, m, rate=args.ema_decay) # 3. 日志记录 report_memory_flag = training_log( loss_dict, total_loss_dict, learning_rate, iteration, loss_scale, # ... ) # 4. 验证评估（按间隔） if args.eval_interval and iteration % args.eval_interval == 0: evaluate_and_print_results( prefix, forward_step_func, valid_data_iterator, model, iteration, # ... ) # 5. 保存检查点 should_exit = checkpoint_and_decide_exit( model, optimizer, opt_param_scheduler, iteration, num_floating_point_operations_so_far, # ... ) iteration += 1 4.3 训练步骤详解 def train_step(forward_step_func, data_iterator, model, optimizer, ...): \u0026quot;\u0026quot;\u0026quot;单个训练步骤\u0026quot;\u0026quot;\u0026quot; # 1. 清空梯度 for model_chunk in model: model_chunk.zero_grad_buffer() optimizer.zero_grad() # 2. 前向和反向传播 forward_backward_func = get_forward_backward_func() losses_reduced = forward_backward_func( forward_step_func=forward_step_func, data_iterator=data_iterator, model=model, num_microbatches=get_num_microbatches(), seq_length=args.seq_length, micro_batch_size=args.micro_batch_size, forward_only=False ) # 3. 更新参数 update_successful, grad_norm, num_zeros_in_grad = optimizer.step() # 4. 更新学习率 if update_successful: increment = get_num_microbatches() * args.micro_batch_size * args.data_parallel_size opt_param_scheduler.step(increment=increment) skipped_iter = 0 else: skipped_iter = 1 # 5. 计算平均损失 loss_reduced = {} for key in losses_reduced[0].keys(): numerator = sum(x[key][0] for x in losses_reduced) denominator = sum(x[key][1] for x in losses_reduced) loss_reduced[key] = numerator / denominator return loss_reduced, skipped_iter, ... 4.4 前向传播函数 4.4.1 预训练阶段 核心文件：aiak_training_llm/train/pretrain/pretrain_llavaov_1_5.py\ndef forward_step(data_iterator, model): \u0026quot;\u0026quot;\u0026quot;预训练前向传播步骤\u0026quot;\u0026quot;\u0026quot; # 1. 获取批次数据 images, image_grid_thw, pixel_values_videos, video_grid_thw, \\ input_ids, position_ids, attention_mask, \\ labels, loss_mask, attn_mask_type, packed_seq_params = get_batch(data_iterator) # 2. 模型前向传播 output_tensor = model( images, # 图像输入 image_grid_thw, # 图像网格 input_ids, # 文本token position_ids, # 位置ID attention_mask, # 注意力掩码 attn_mask_type, # 掩码类型 labels, # 标签 packed_seq_params, # 打包序列参数 pixel_values_videos=pixel_values_videos, # 视频输入 video_grid_thw=video_grid_thw # 视频网格 ) # 3. 返回输出和损失函数 return output_tensor, partial(loss_func, loss_mask) 4.4.2 损失函数 def loss_func(loss_mask: torch.Tensor, output_tensor: torch.Tensor): \u0026quot;\u0026quot;\u0026quot; 损失函数计算 Args: loss_mask: 损失掩码，标记哪些位置需要计算损失 output_tensor: 模型输出的损失张量 Returns: loss: 标量损失值 local_num_tokens: 本地token数量 loss_reduced_dict: 包含各种统计信息的字典 \u0026quot;\u0026quot;\u0026quot; losses = output_tensor.float() loss_mask = loss_mask.view(-1).float() # 计算加权损失 total_tokens = loss_mask.sum() loss = torch.cat([ torch.sum(losses.view(-1) * loss_mask).view(1), total_tokens.view(1) ]) # Context Parallel归约 if args.context_parallel_size \u0026gt; 1: torch.distributed.all_reduce(loss, group=mpu.get_context_parallel_group()) # NaN检查 if args.check_for_nan_in_loss_and_grad: assert not loss[0].isnan(), f\u0026#39;Found NaN in loss on rank {rank}\u0026#39; # 跨数据并行组归约 reporting_loss = loss.clone().detach() torch.distributed.all_reduce(reporting_loss, group=mpu.get_data_parallel_group()) loss_reduced_dict = { \u0026#39;lm loss\u0026#39;: (reporting_loss[0], reporting_loss[1]) } # 可变序列长度处理 if args.variable_seq_lengths: num_input_tokens = output_tensor.shape[0] * output_tensor.shape[1] input_tokens = torch.tensor(num_input_tokens, dtype=torch.int) torch.distributed.all_reduce(input_tokens, group=mpu.get_data_parallel_group()) loss_reduced_dict[\u0026quot;total_inputs\u0026quot;] = input_tokens.item() return ( loss[0] * args.context_parallel_size, loss[1].to(torch.int), loss_reduced_dict ) 4.5 模型和优化器初始化 def setup_model_and_optimizer(model_provider_func, model_type): \u0026quot;\u0026quot;\u0026quot;设置模型和优化器\u0026quot;\u0026quot;\u0026quot; # 1. 构建模型 model = get_model(model_provider_func, model_type) # 2. 构建优化器配置 config = OptimizerConfig( # 从args中提取配置 lr=args.lr, weight_decay=args.weight_decay, adam_beta1=args.adam_beta1, adam_beta2=args.adam_beta2, # ... ) # 3. 创建优化器 optimizer = get_megatron_optimizer( config, model, use_gloo_process_groups=args.enable_gloo_process_groups ) # 4. 创建学习率调度器 opt_param_scheduler = get_optimizer_param_scheduler(optimizer) # 5. 加载检查点（如果存在） if args.load is not None: args.iteration, args.num_floating_point_operations_so_far = \\ load_checkpoint(model, optimizer, opt_param_scheduler) else: args.iteration = 0 # 6. 创建EMA模型（如果启用） if args.enable_ema: ema = get_model(model_provider_func, model_type) if args.iteration == 0: # 初始化EMA为模型参数 for e, m in zip(ema, model): update_ema(e, m, rate=0) else: load_checkpoint(ema, None, None, load_arg=\u0026quot;load_ema\u0026quot;) return model, ema, optimizer, opt_param_scheduler 5. 数据处理 5.1 数据集架构 项目支持多种数据格式和加载方式：\n5.1.1 数据集类型 预训练数据集 WebDataset格式（推荐） 支持大规模数据流式加载 支持离线样本打包（Sample Packing） SFT数据集 HuggingFace Datasets格式 支持多种对话格式（Alpaca, ShareGPT等） 支持多模态输入（图像+文本） 5.1.2 数据配置 核心文件：aiak_training_llm/data/blended_hf_dataset_config.py\n@dataclass class SFTDatasetConfig(BlendedHuggingFaceDatasetConfig): \u0026quot;\u0026quot;\u0026quot;SFT数据集配置\u0026quot;\u0026quot;\u0026quot; # 数据路径和混合 dataset_config_file: str # 数据集配置文件路径 blend: Optional[List] # 数据混合比例 blend_per_split: Optional[List] # 各分割的混合比例 # 模板和处理器 chat_template: Optional[ChatTemplate] # 对话模板 processor: Optional[ProcessorMixin] # 数据处理器 # 训练配置 sequence_length: int # 最大序列长度 ignore_index: int = -100 # 忽略索引 train_on_prompt: bool = False # 是否在提示上训练 # 数据增强 packing: bool = False # 是否打包数据 sort_batch: bool = False # 是否排序批次 packing_batch_size: int = 10000 # 打包批次大小 # 流式加载 streaming: bool = False # 是否流式加载 streaming_buffer_size: int = 16384 # 流式缓冲区大小 # 并行配置 context_parallel_size: Optional[int] = None # 上下文并行大小 5.2 SFT数据集 核心文件：aiak_training_llm/data/sft_dataset.py\n5.2.1 支持的数据格式 class SFTDataFormats: \u0026quot;\u0026quot;\u0026quot;支持的SFT数据格式\u0026quot;\u0026quot;\u0026quot; ALPACA = \u0026quot;alpaca\u0026quot; # Alpaca格式 SHAREGPT = \u0026quot;sharegpt\u0026quot; # ShareGPT格式 OPENAI = \u0026quot;openai\u0026quot; # OpenAI格式 5.2.2 数据加载流程 class SFTDataset(HuggingFaceDataset): \u0026quot;\u0026quot;\u0026quot;SFT数据集类\u0026quot;\u0026quot;\u0026quot; def __init__(self, config: SFTDatasetConfig): super().__init__(config) # 1. 加载原始数据集 raw_datasets = self._load_datasets() # 2. 格式转换 processed_datasets = self._convert_format(raw_datasets) # 3. 分词处理 tokenized_datasets = self._tokenize(processed_datasets) # 4. 数据打包（可选） if config.packing: tokenized_datasets = self._pack_samples(tokenized_datasets) # 5. 排序（可选） if config.sort_batch: tokenized_datasets = self._sort_by_length(tokenized_datasets) self.dataset = tokenized_datasets 5.3 多模态数据处理 核心文件：aiak_training_llm/data/multimodal/qwen2vl_task_encoder.py\n5.3.1 任务编码器 class Qwen2VLTaskEncoder: \u0026quot;\u0026quot;\u0026quot;Qwen2-VL任务编码器\u0026quot;\u0026quot;\u0026quot; def __init__(self, args): self.args = args self.tokenizer = get_tokenizer() self.processor = AutoProcessor.from_pretrained( args.hf_tokenizer_path, trust_remote_code=True ) def encode_sample(self, sample): \u0026quot;\u0026quot;\u0026quot; 编码单个样本 Args: sample: 包含文本和图像的字典 { \u0026quot;messages\u0026quot;: [...], # 对话消息 \u0026quot;images\u0026quot;: [...], # 图像路径列表 \u0026quot;videos\u0026quot;: [...], # 视频路径列表（可选） } Returns: 编码后的样本，包含： - tokens: token ID - labels: 标签 - imgs: 图像张量 - image_grid_thw: 图像网格维度 - attn_mask: 注意力掩码 - cu_lengths: 累积长度（用于打包） \u0026quot;\u0026quot;\u0026quot; # 1. 应用对话模板 text = self.processor.apply_chat_template( sample[\u0026quot;messages\u0026quot;], tokenize=False, add_generation_prompt=False ) # 2. 处理图像 image_inputs = None if sample.get(\u0026quot;images\u0026quot;): images = [Image.open(img_path) for img_path in sample[\u0026quot;images\u0026quot;]] image_inputs = self.processor.image_processor( images, return_tensors=\u0026quot;pt\u0026quot; ) # 3. 分词 text_inputs = self.tokenizer( text, return_tensors=\u0026quot;pt\u0026quot;, padding=False, truncation=True, max_length=self.args.seq_length ) # 4. 合并输入 encoded = { \u0026quot;tokens\u0026quot;: text_inputs[\u0026quot;input_ids\u0026quot;], \u0026quot;labels\u0026quot;: self._build_labels(text_inputs, sample), \u0026quot;attn_mask\u0026quot;: text_inputs[\u0026quot;attention_mask\u0026quot;], } if image_inputs: encoded.update({ \u0026quot;imgs\u0026quot;: image_inputs[\u0026quot;pixel_values\u0026quot;], \u0026quot;image_grid_thw\u0026quot;: image_inputs[\u0026quot;image_grid_thw\u0026quot;], }) return encoded 5.4 数据打包（Sample Packing） 数据打包技术将多个短样本组合成一个批次，提高训练效率。\n核心文件：aiak_training_llm/data/sft_supervised_utils.py\ndef pack_samples(samples, max_length, context_parallel_size=1): \u0026quot;\u0026quot;\u0026quot; 将多个样本打包到固定长度 Args: samples: 样本列表 max_length: 最大序列长度 context_parallel_size: 上下文并行大小 Returns: 打包后的样本 \u0026quot;\u0026quot;\u0026quot; packed_samples = [] current_pack = { \u0026quot;input_ids\u0026quot;: [], \u0026quot;labels\u0026quot;: [], \u0026quot;attention_mask\u0026quot;: [], \u0026quot;cu_seqlens\u0026quot;: [0], # 累积序列长度 \u0026quot;images\u0026quot;: [], \u0026quot;image_grid_thw\u0026quot;: [], } current_length = 0 for sample in samples: sample_length = len(sample[\u0026quot;input_ids\u0026quot;]) # 检查是否可以添加到当前pack if current_length + sample_length \u0026lt;= max_length: # 添加到当前pack current_pack[\u0026quot;input_ids\u0026quot;].extend(sample[\u0026quot;input_ids\u0026quot;]) current_pack[\u0026quot;labels\u0026quot;].extend(sample[\u0026quot;labels\u0026quot;]) current_pack[\u0026quot;attention_mask\u0026quot;].extend(sample[\u0026quot;attention_mask\u0026quot;]) current_pack[\u0026quot;cu_seqlens\u0026quot;].append(current_length + sample_length) if \u0026quot;images\u0026quot; in sample: current_pack[\u0026quot;images\u0026quot;].append(sample[\u0026quot;images\u0026quot;]) current_pack[\u0026quot;image_grid_thw\u0026quot;].append(sample[\u0026quot;image_grid_thw\u0026quot;]) current_length += sample_length else: # 当前pack已满，开始新pack if current_length \u0026gt; 0: # 填充到max_length（考虑CP） pad_length = max_length - current_length if context_parallel_size \u0026gt; 1: # 确保长度可被CP整除 pad_length = (max_length // context_parallel_size) * context_parallel_size - current_length current_pack[\u0026quot;input_ids\u0026quot;].extend([0] * pad_length) current_pack[\u0026quot;labels\u0026quot;].extend([-100] * pad_length) current_pack[\u0026quot;attention_mask\u0026quot;].extend([0] * pad_length) packed_samples.append(current_pack) # 开始新pack current_pack = initialize_new_pack() current_length = 0 return packed_samples 5.5 数据加载器 核心文件：aiak_training_llm/data/multimodal/dataloader_provider.py\ndef get_train_loader(dataset, collator): \u0026quot;\u0026quot;\u0026quot; 创建训练数据加载器 Args: dataset: 数据集对象 collator: 数据整理函数 Returns: 数据加载器迭代器 \u0026quot;\u0026quot;\u0026quot; args = get_args() # 1. 创建数据采样器 if args.dataloader_type == \u0026#39;cyclic\u0026#39;: # 循环数据加载器（用于WebDataset） dataloader = build_pretraining_data_loader(dataset) else: # 标准数据加载器 from torch.utils.data import DataLoader dataloader = DataLoader( dataset, batch_size=args.micro_batch_size, shuffle=True, num_workers=args.num_workers, collate_fn=collator, pin_memory=True, drop_last=True, ) # 2. 创建循环迭代器 def cyclic_iter(dataloader): while True: for batch in dataloader: yield batch return cyclic_iter(dataloader) 5.6 数据整理器（Data Collator） class MultiModalDataCollatorForSupervisedDataset: \u0026quot;\u0026quot;\u0026quot;多模态监督数据集整理器\u0026quot;\u0026quot;\u0026quot; def __init__(self, processor, plugin): self.processor = processor self.plugin = plugin def __call__(self, samples): \u0026quot;\u0026quot;\u0026quot; 整理一个批次的样本 Args: samples: 样本列表 Returns: 批次字典 \u0026quot;\u0026quot;\u0026quot; # 1. 提取各个字段 input_ids = [sample[\u0026quot;input_ids\u0026quot;] for sample in samples] labels = [sample[\u0026quot;labels\u0026quot;] for sample in samples] attention_masks = [sample[\u0026quot;attention_mask\u0026quot;] for sample in samples] # 2. 填充到相同长度 max_length = max(len(ids) for ids in input_ids) padded_input_ids = [] padded_labels = [] padded_attention_masks = [] for ids, labs, mask in zip(input_ids, labels, attention_masks): pad_length = max_length - len(ids) padded_input_ids.append(ids + [0] * pad_length) padded_labels.append(labs + [-100] * pad_length) padded_attention_masks.append(mask + [0] * pad_length) # 3. 转换为张量 batch = { \u0026quot;input_ids\u0026quot;: torch.tensor(padded_input_ids, dtype=torch.long), \u0026quot;labels\u0026quot;: torch.tensor(padded_labels, dtype=torch.long), \u0026quot;attention_mask\u0026quot;: torch.tensor(padded_attention_masks, dtype=torch.long), } # 4. 处理图像（如果存在） if \u0026quot;images\u0026quot; in samples[0]: images = [] image_grid_thw = [] for sample in samples: if sample[\u0026quot;images\u0026quot;] is not None: images.append(sample[\u0026quot;images\u0026quot;]) image_grid_thw.append(sample[\u0026quot;image_grid_thw\u0026quot;]) if images: batch[\u0026quot;images\u0026quot;] = torch.cat(images, dim=0) batch[\u0026quot;image_grid_thw\u0026quot;] = torch.tensor(image_grid_thw, dtype=torch.long) return batch 6. 训练流程 6.1 三阶段训练策略 LLaVA-OneVision-1.5采用三阶段训练策略：\n6.1.1 Stage 1: Alignment Training（对齐训练） 目标：学习视觉-语言对齐\n数据：LLaVA-558K（图像描述数据）\n配置： - 只训练Adapter（冻结LLM和ViT） - 学习率：1e-4 - 训练步数：2500 - 序列长度：32768\n脚本示例：\n# examples/llava_ov_1_5/quick_start/stage_1_alignment_llava_ov_4b.sh MODEL_ARGS=( --model-name llava-ov-1.5-4b ) TRAINING_ARGS=( --training-phase sft --trainable-modules adapter # 只训练adapter --seq-length 32768 --micro-batch-size 1 --global-batch-size 8 --lr 1.0e-4 --train-iters 2500 --bf16 ) 6.1.2 Stage 1.5: Mid-Training（中期训练） 目标：增强视觉理解能力\n数据：LLaVA-OneVision-1.5-Mid-Training-85M（大规模高质量描述数据）\n配置： - 训练所有组件（LLM + ViT + Adapter） - 学习率：1e-5 - 训练步数：20000 - 支持样本打包\n特点： - 概念平衡的数据分布 - 原生分辨率图像（最大1344x1344） - 严格的质量控制\n6.1.3 Stage 2: Instruction Tuning（指令微调） 目标：学习指令跟随和多任务能力\n数据：LLaVA-OneVision-1.5-Instruct-Data（综合指令数据）\n配置： - 训练所有组件 - 学习率：1e-5 - 训练步数：3500 - 支持多种任务类型\n6.2 训练启动流程 6.2.1 入口脚本 核心文件：aiak_training_llm/train.py\ndef main(): \u0026quot;\u0026quot;\u0026quot;训练主入口\u0026quot;\u0026quot;\u0026quot; # 1. 解析参数 args = parse_train_args() # 2. 构建训练器 trainer = build_model_trainer(args) # 3. 启动训练 trainer.train() 6.2.2 参数解析 核心文件：aiak_training_llm/train/arguments.py\ndef parse_train_args(): \u0026quot;\u0026quot;\u0026quot;解析训练参数\u0026quot;\u0026quot;\u0026quot; args = parse_arguments( extra_args_provider=aiak_extra_train_args_provider, validate_extra_args_provider=validate_aiak_extra_args, ) return args def aiak_extra_train_args_provider(parser): \u0026quot;\u0026quot;\u0026quot;添加AIAK自定义参数\u0026quot;\u0026quot;\u0026quot; # 1. 模型参数 parser = _add_extra_model_args(parser) # 2. 分词器参数 parser = _add_extra_tokenizer_args(parser) # 3. SFT参数 parser = _add_extra_sft_args(parser) # 4. 多模态参数 parser = _add_extra_multimodal_args(parser) # 5. 并行参数 parser = _add_extra_parallel_args(parser) return parser 6.2.3 关键参数说明 模型参数：\n--model-name llava-ov-1.5-4b # 模型架构 --trainable-modules adapter # 可训练模块（all/adapter/language_model/vision_model） 数据参数：\n--data-path /path/to/dataset # 数据路径 --dataloader-type external # 数据加载器类型 --split 100,0,0 # 训练/验证/测试分割 --chat-template qwen2-vl # 对话模板 --num-workers 16 # 数据加载工作线程 训练参数：\n--training-phase sft # 训练阶段（pretrain/sft） --seq-length 32768 # 序列长度 --micro-batch-size 1 # 微批次大小 --global-batch-size 8 # 全局批次大小 --train-iters 2500 # 训练迭代次数 --lr 1.0e-4 # 学习率 --min-lr 1.0e-6 # 最小学习率 --lr-decay-style cosine # 学习率衰减策略 --lr-warmup-fraction 0.002 # 学习率预热比例 --clip-grad 1.0 # 梯度裁剪 --optimizer adam # 优化器 --adam-beta1 0.9 # Adam beta1 --adam-beta2 0.99 # Adam beta2 --weight-decay 0 # 权重衰减 并行参数：\n--tensor-model-parallel-size 1 # 张量并行度 --pipeline-model-parallel-size 1 # 流水线并行度 --use-distributed-optimizer # 使用分布式优化器 --attention-backend flash # 注意力后端（flash/native） 优化参数：\n--bf16 # 使用BF16精度 --recompute-granularity full # 重计算粒度 --recompute-method uniform # 重计算方法 --recompute-num-layers 4 # 重计算层数 6.3 分布式训练 6.3.1 并行策略 LLaVA-OneVision-1.5支持多种并行策略：\n数据并行（Data Parallelism, DP） 每个GPU维护完整模型副本 梯度在所有GPU间同步 张量并行（Tensor Parallelism, TP） 模型权重按张量维度切分 适合大型模型 流水线并行（Pipeline Parallelism, PP） 模型按层切分到不同GPU 使用微批次流水线减少气泡 上下文并行（Context Parallelism, CP） 长序列按序列维度切分 支持超长上下文训练 序列并行（Sequence Parallelism, SP） 激活值按序列维度切分 减少内存占用 6.3.2 并行配置示例 # 8GPU单节点，使用TP=2, PP=1 GPUS_PER_NODE=8 TP=2 PP=1 # 实际数据并行度 = 8 / (2 * 1) = 4 # 全局批次大小 = 微批次大小 * 数据并行度 * 梯度累积步数 6.4 内存优化技术 6.4.1 激活检查点（Activation Checkpointing） TRAINING_ARGS=( --recompute-granularity full # 完全重计算 --recompute-method uniform # 均匀选择层 --recompute-num-layers 4 # 重计算4层 ) 6.4.2 分布式优化器 MODEL_PARALLEL_ARGS=( --use-distributed-optimizer # 启用分布式优化器 ) 特点： - 优化器状态跨DP组分片 - 减少优化器内存占用 - 支持Adam/AdamW\n6.4.3 Flash Attention MODEL_PARALLEL_ARGS=( --attention-backend flash # 使用Flash Attention ) 优势： - 更快的注意力计算 - 更低的内存占用 - 支持长序列\n6.5 检查点管理 6.5.1 保存检查点 TRAINING_ARGS=( --save /path/to/checkpoint # 检查点保存路径 --save-interval 2000 # 保存间隔 --ckpt-format torch # 检查点格式（torch/torch_dist/zarr） ) 6.5.2 加载检查点 TRAINING_ARGS=( --load /path/to/checkpoint # 检查点加载路径 --ckpt-fully-parallel-load # 完全并行加载 ) 6.5.3 异步保存 支持异步检查点保存以减少训练中断：\nTRAINING_ARGS=( --async-save # 启用异步保存 ) 7. 模型转换 7.1 转换流程 LLaVA-OneVision-1.5提供完整的模型转换工具，支持HuggingFace格式与Megatron格式互转。\n7.1.1 HuggingFace → Megatron 转换脚本：examples/llava_ov_1_5/convert/convert_4b_hf_to_mcore.sh\n#!/bin/bash LOAD=$1 # HuggingFace模型路径 SAVE=$2 # Megatron输出路径 TP=$3 # 张量并行度 PP=$4 # 流水线并行度 # 步骤1：转换语言模型 python $CONVERT_CHECKPOINT_PATH/model.py \\ --load_platform=huggingface \\ --save_platform=mcore \\ --common_config_path=$CONVERT_CHECKPOINT_PATH/config/llava-ov-1.5-4b/qwen3.json \\ --tensor_model_parallel_size=$TP \\ --pipeline_model_parallel_size=$PP \\ --load_ckpt_path=$LOAD \\ --save_ckpt_path=$SAVE_LANGUAGE_MODEL # 步骤2：转换视觉模型 python $CONVERT_CHECKPOINT_PATH/model.py \\ --load_platform=huggingface \\ --save_platform=mcore \\ --common_config_path=$CONVERT_CHECKPOINT_PATH/config/llava-ov-1.5-4b/vision-model.json \\ --tensor_model_parallel_size=$TP \\ --load_ckpt_path=$LOAD \\ --save_ckpt_path=$SAVE_VISION_MODEL # 步骤3：转换适配器 python $CONVERT_CHECKPOINT_PATH/custom/llavaov_1_5/adapter.py \\ --load_platform=huggingface \\ --save_platform=mcore \\ --common_config_path=$CONVERT_CHECKPOINT_PATH/config/llava-ov-1.5-4b/adapter.json \\ --tensor_model_parallel_size=$TP \\ --load_ckpt_path=$LOAD \\ --save_ckpt_path=$SAVE_ADAPTER # 步骤4：转换视觉补丁嵌入 python $CONVERT_CHECKPOINT_PATH/custom/llavaov_1_5/vision_patch.py \\ --load_platform=huggingface \\ --save_platform=mcore \\ --common_config_path=$CONVERT_CHECKPOINT_PATH/config/llava-ov-1.5-4b/vision-patch.json \\ --load_ckpt_path=$LOAD \\ --save_ckpt_path=$SAVE_PATCH # 步骤5：合并所有组件 python $CONVERT_CHECKPOINT_PATH/custom/llavaov_1_5/merge_megatron.py \\ --megatron_path $AIAK_MAGATRON_PATH \\ --language_model_path $SAVE_LANGUAGE_MODEL/release \\ --vision_model_path $SAVE_VISION_MODEL/release \\ --vision_patch $SAVE_PATCH/release \\ --adapter_path $SAVE_ADAPTER/release \\ --save_ckpt_path $SAVE/release \\ --tensor_model_parallel_size $TP \\ --pipeline_model_parallel_size $PP 7.1.2 Megatron → HuggingFace 转换脚本：examples/llava_ov_1_5/convert/convert_4b_mcore_to_hf.sh\n#!/bin/bash LOAD=$1 # Megatron模型路径 SAVE=$2 # HuggingFace输出路径 TP=$3 # 源张量并行度 PP=$4 # 源流水线并行度 # 步骤1：转换语言模型 python $CONVERT_CHECKPOINT_PATH/model.py \\ --load_platform=mcore \\ --save_platform=huggingface \\ --common_config_path=$CONVERT_CHECKPOINT_PATH/config/llava-ov-1.5-4b/qwen3.json \\ --tensor_model_parallel_size=$TP \\ --pipeline_model_parallel_size=$PP \\ --load_ckpt_path=$LOAD \\ --save_ckpt_path=$SAVE_LANGUAGE_MODEL # 步骤2-4：转换其他组件（类似） # ... # 步骤5：合并到HuggingFace格式 python $CONVERT_CHECKPOINT_PATH/custom/llavaov_1_5/merge_huggingface.py \\ --language_model_path $SAVE_LANGUAGE_MODEL \\ --vision_model_path $SAVE_VISION_MODEL \\ --adapter_path $SAVE_ADAPTER \\ --vision_patch $SAVE_PATCH \\ --save_ckpt_path $SAVE # 复制非模型文件（tokenizer等） find $ORIGINAL_HF_PATH/ -type f -not -iname \u0026#39;*safetensors*\u0026#39; -exec cp {} $SAVE/ \u0026#39;;\u0026#39; 7.2 转换配置 转换配置文件定义了权重映射规则：\n7.2.1 语言模型配置示例 文件：tools/convert_checkpoint/config/llava-ov-1.5-4b/qwen3.json\n{ \u0026quot;model_type\u0026quot;: \u0026quot;GPT\u0026quot;, \u0026quot;num_layers\u0026quot;: 36, \u0026quot;hidden_size\u0026quot;: 2560, \u0026quot;ffn_hidden_size\u0026quot;: 9728, \u0026quot;num_attention_heads\u0026quot;: 32, \u0026quot;num_query_groups\u0026quot;: 8, \u0026quot;weight_mapping\u0026quot;: { \u0026quot;embedding.word_embeddings.weight\u0026quot;: \u0026quot;model.embed_tokens.weight\u0026quot;, \u0026quot;decoder.layers.{}.self_attention.linear_qkv.weight\u0026quot;: [ \u0026quot;model.layers.{}.self_attn.q_proj.weight\u0026quot;, \u0026quot;model.layers.{}.self_attn.k_proj.weight\u0026quot;, \u0026quot;model.layers.{}.self_attn.v_proj.weight\u0026quot; ], \u0026quot;decoder.layers.{}.self_attention.linear_proj.weight\u0026quot;: \u0026quot;model.layers.{}.self_attn.o_proj.weight\u0026quot;, \u0026quot;decoder.layers.{}.mlp.linear_fc1.weight\u0026quot;: [ \u0026quot;model.layers.{}.mlp.gate_proj.weight\u0026quot;, \u0026quot;model.layers.{}.mlp.up_proj.weight\u0026quot; ], \u0026quot;decoder.layers.{}.mlp.linear_fc2.weight\u0026quot;: \u0026quot;model.layers.{}.mlp.down_proj.weight\u0026quot;, \u0026quot;output_layer.weight\u0026quot;: \u0026quot;lm_head.weight\u0026quot; } } 7.2.2 视觉模型配置示例 文件：tools/convert_checkpoint/config/llava-ov-1.5-4b/vision-model.json\n{ \u0026quot;model_type\u0026quot;: \u0026quot;VisionModel\u0026quot;, \u0026quot;num_layers\u0026quot;: 24, \u0026quot;hidden_size\u0026quot;: 1024, \u0026quot;num_attention_heads\u0026quot;: 16, \u0026quot;weight_mapping\u0026quot;: { \u0026quot;decoder.layers.{}.self_attention.linear_qkv.weight\u0026quot;: [ \u0026quot;visual.blocks.{}.attn.qkv.weight\u0026quot; ], \u0026quot;decoder.layers.{}.self_attention.linear_proj.weight\u0026quot;: \u0026quot;visual.blocks.{}.attn.proj.weight\u0026quot;, \u0026quot;decoder.layers.{}.pre_mlp_layernorm.weight\u0026quot;: \u0026quot;visual.blocks.{}.norm2.weight\u0026quot;, \u0026quot;decoder.layers.{}.mlp.linear_fc1.weight\u0026quot;: \u0026quot;visual.blocks.{}.mlp.fc1.weight\u0026quot;, \u0026quot;decoder.layers.{}.mlp.linear_fc2.weight\u0026quot;: \u0026quot;visual.blocks.{}.mlp.fc2.weight\u0026quot; } } 7.3 权重映射逻辑 核心文件：tools/convert_checkpoint/model.py\ndef convert_checkpoint(load_platform, save_platform, config, load_path, save_path): \u0026quot;\u0026quot;\u0026quot;执行检查点转换\u0026quot;\u0026quot;\u0026quot; # 1. 加载源检查点 if load_platform == \u0026quot;huggingface\u0026quot;: src_checkpoint = load_huggingface_checkpoint(load_path) elif load_platform == \u0026quot;mcore\u0026quot;: src_checkpoint = load_mcore_checkpoint(load_path, config) # 2. 创建目标检查点结构 dst_checkpoint = {} # 3. 根据映射规则转换权重 weight_mapping = config[\u0026quot;weight_mapping\u0026quot;] for dst_key_template, src_key_template in weight_mapping.items(): # 处理层索引 if \u0026quot;{}\u0026quot; in dst_key_template: for layer_idx in range(config[\u0026quot;num_layers\u0026quot;]): dst_key = dst_key_template.format(layer_idx) if isinstance(src_key_template, list): # 需要拼接多个源权重 src_keys = [k.format(layer_idx) for k in src_key_template] weights = [src_checkpoint[k] for k in src_keys] dst_checkpoint[dst_key] = torch.cat(weights, dim=0) else: src_key = src_key_template.format(layer_idx) dst_checkpoint[dst_key] = src_checkpoint[src_key] else: # 直接映射 dst_checkpoint[dst_key_template] = src_checkpoint[src_key_template] # 4. 处理张量并行切分（如果需要） if save_platform == \u0026quot;mcore\u0026quot; and config.get(\u0026quot;tensor_model_parallel_size\u0026quot;, 1) \u0026gt; 1: dst_checkpoint = split_for_tensor_parallel( dst_checkpoint, config[\u0026quot;tensor_model_parallel_size\u0026quot;] ) # 5. 保存目标检查点 if save_platform == \u0026quot;huggingface\u0026quot;: save_huggingface_checkpoint(dst_checkpoint, save_path) elif save_platform == \u0026quot;mcore\u0026quot;: save_mcore_checkpoint(dst_checkpoint, save_path, config) 8. 配置与优化 8.1 推荐配置 8.1.1 不同规模模型的配置 4B模型（单节点8卡A100-80GB）:\nTP=1 PP=1 MBS=1 GBS=8 SEQ_LEN=32768 RECOMPUTE_LAYERS=4 # 内存占用：约60GB/GPU # 训练速度：约1.2 samples/s 8B模型（单节点8卡A100-80GB）:\nTP=2 PP=1 MBS=1 GBS=8 SEQ_LEN=32768 RECOMPUTE_LAYERS=8 # 内存占用：约70GB/GPU # 训练速度：约0.8 samples/s 32B模型（2节点16卡A100-80GB）:\nTP=4 PP=2 MBS=1 GBS=16 SEQ_LEN=32768 RECOMPUTE_LAYERS=16 # 内存占用：约75GB/GPU # 训练速度：约0.3 samples/s 8.1.2 性能优化建议 Flash Attention: 始终启用以获得最佳性能 --attention-backend flash BF16训练: 推荐使用BF16而非FP16 --bf16 激活检查点: 根据模型大小调整重计算层数 --recompute-granularity full --recompute-method uniform --recompute-num-layers \u0026lt;根据模型调整\u0026gt; 分布式优化器: 大模型必备 --use-distributed-optimizer 数据加载: 增加工作线程数 --num-workers 16 8.2 常见问题与解决 8.2.1 OOM（内存溢出） 症状: CUDA out of memory\n解决方案: 1. 减小微批次大小\n--micro-batch-size 1 # 最小值 增加重计算层数 --recompute-num-layers 8 # 或更多 增加张量并行度 --tensor-model-parallel-size 2 # 或更大 启用序列并行 --sequence-parallel 8.2.2 训练速度慢 症状: 训练吞吐量低\n解决方案: 1. 检查Flash Attention是否启用\n--attention-backend flash 优化数据加载 --num-workers 16 # 增加工作线程 --dataloader-type cyclic # 使用循环数据加载器 启用混合精度 --bf16 # 或 --fp16 调整并行配置 # 平衡TP和DP # TP太大会增加通信开销 # DP太大可能导致批次大小过小 8.2.3 损失为NaN 症状: Loss突然变成NaN\n解决方案: 1. 降低学习率\n--lr 5.0e-5 # 从更小的值开始 启用梯度裁剪 --clip-grad 1.0 检查数据质量 # 确保数据中没有异常值 使用更稳定的优化器配置 --optimizer adam --adam-beta1 0.9 --adam-beta2 0.999 --adam-eps 1e-08 8.3 监控与日志 8.3.1 TensorBoard监控 LOGGING_ARGS=( --tensorboard-dir ./tensorboard --log-timers-to-tensorboard --log-memory-to-tensorboard ) 监控指标： - 训练损失 - 学习率 - 梯度范数 - 内存使用 - 训练吞吐量\n8.3.2 WandB集成 export WANDB_API_KEY=\u0026lt;your-api-key\u0026gt; LOGGING_ARGS=( --wandb-project llava-onevision-1.5 --wandb-exp-name stage1-alignment ) 8.3.3 日志级别 LOGGING_ARGS=( --log-interval 1 # 日志记录间隔 --detail-log-interval 20 # 详细日志间隔 --timing-log-level 0 # 计时日志级别 ) 8.4 最佳实践总结 渐进式训练 Stage 1: 只训练Adapter（快速对齐） Stage 1.5: 全模型训练（增强能力） Stage 2: 指令微调（任务适配） 数据质量 使用高质量、多样化的数据 进行概念平衡 严格的质量控制 内存管理 合理配置并行度 使用激活检查点 启用分布式优化器 训练稳定性 适当的学习率和warmup 梯度裁剪 定期保存检查点 效率优化 Flash Attention 混合精度训练 样本打包 高效的数据加载 总结 LLaVA-OneVision-1.5是一个完整、高效、可扩展的多模态大模型训练框架，具有以下核心优势：\n完全开源: 提供完整的代码、数据、配置和训练日志 高效训练: 基于Megatron-LM深度优化，支持各种并行和优化策略 模块化设计: 易于扩展和定制 生产级质量: 经过大规模验证，训练成本仅$16K 通过本文档，您应该能够： - 理解项目的整体架构和设计思路 - 掌握模型的实现细节 - 配置和启动训练任务 - 进行模型转换和部署 - 优化训练性能和解决常见问题\n建议在实际使用时： 1. 从小规模实验开始，验证配置 2. 逐步增加模型规模和数据量 3. 密切监控训练指标和系统资源 4. 根据具体硬件和任务需求调整配置\n如需更多帮助，请参考： - 项目GitHub - 技术报告 - HuggingFace模型\n","date":"2025-11-07T00:00:00Z","permalink":"/llavaov_1_5/","title":"llavaov_1_5"},{"content":"核心文件： - vision_transformer_block.py - transformer_block.py - transformer_config.py - layer_specs.py - visionmodel.py - model.py - llm.py ## model.py\n最顶层的为model.py，组装visionmodel和llm\nlayer_specs.py layer_specs.py声明llm或者vit的整体架构，其定义和用法如下（以llm的架构为例子）：\ndef get_gpt_layer_with_transformer_engine_spec( qk_layernorm: bool = False ) -\u0026gt; ModuleSpec: mlp = get_mlp_module_spec( use_te=True, num_experts=None, moe_grouped_gemm=False ) return ModuleSpec( module=TransformerLayer, submodules=TransformerLayerSubmodules( self_attention=ModuleSpec( module=Qwen2VLSelfAttention, params={\u0026quot;attn_mask_type\u0026quot;: AttnMaskType.causal}, submodules=SelfAttentionSubmodules( linear_qkv=TELayerNormColumnParallelLinear, core_attention=TEDotProductAttention, linear_proj=TERowParallelLinear, q_layernorm=TENorm if qk_layernorm else IdentityOp, k_layernorm=TENorm if qk_layernorm else IdentityOp, ), ), self_attn_bda=get_bias_dropout_add, pre_mlp_layernorm=IdentityOp, mlp=mlp, mlp_bda=get_bias_dropout_add, ), ) 这是transformer的一个layer的定义，在block文件中需要获取layer数然后组合成transformerblock：\ndef _get_block_submodules( config: TransformerConfig, spec: Union[TransformerBlockSubmodules, ModuleSpec], vp_stage: Optional[int] = None, ) -\u0026gt; TransformerBlockSubmodules: \u0026quot;\u0026quot;\u0026quot; Retrieve or construct TransformerBlockSubmodules based on the provided specification. Args: config (TransformerConfig): Configuration object for the transformer model. spec (Union[TransformerBlockSubmodules, ModuleSpec]): Specification for the transformer block submodules. Can be either a TransformerBlockSubmodules instance or a ModuleSpec. vp_stage (Optional[int]): Virtual pipeline stage number. Returns: TransformerBlockSubmodules: The submodules for the transformer block. \u0026quot;\u0026quot;\u0026quot; # Transformer block submodules. if isinstance(spec, TransformerBlockSubmodules): return spec # ModuleSpec here is generally assumed to be for a transformer layer that # is implemented in `transformer_layer.py` or if it subclasses # `BaseTransformerLayer` from the `transformer_layer.py` file. elif isinstance(spec, ModuleSpec): if issubclass(spec.module, TransformerBlock): return spec.submodules elif issubclass(spec.module, BaseTransformerLayer): num_layers = get_num_layers_to_build(config, vp_stage) return TransformerBlockSubmodules( layer_specs=[spec] * num_layers, layer_norm=LayerNormImpl ) else: raise Exception(f\u0026quot;specialize for {spec.module.__name__}.\u0026quot;) else: raise Exception(f\u0026quot;specialize for {type(spec).__name__}.\u0026quot;) 然后就会被使用构建transformer的架构（self.submodules就是上面函数的返回）：\nself.layers = torch.nn.ModuleList( [ build_layer(layer_spec, i + 1) for i, layer_spec in enumerate(self.submodules.layer_specs) ] ) 最终到megatron/core/transformer/spec_utils.py的build_module函数中生成具体的模型结构类：\ndef build_module(spec_or_module: Union[ModuleSpec, type], *args, **kwargs): # If the passed `spec_or_module` is # a `Function`, then return it as it is # NOTE: to support an already initialized module add the following condition # `or isinstance(spec_or_module, torch.nn.Module)` to the following if check if isinstance(spec_or_module, types.FunctionType): return spec_or_module # If the passed `spec_or_module` is actually a spec (instance of # `ModuleSpec`) and it specifies a `Function` using its `module` # field, return the `Function` as it is if isinstance(spec_or_module, ModuleSpec) and isinstance( spec_or_module.module, types.FunctionType ): return spec_or_module.module # Check if a module class is provided as a spec or if the module path # itself is a class if isinstance(spec_or_module, type): module = spec_or_module elif hasattr(spec_or_module, \u0026quot;module\u0026quot;) and isinstance(spec_or_module.module, type): module = spec_or_module.module else: # Otherwise, dynamically import the module from the module path module = import_module(spec_or_module.module) # If the imported module is actually a `Function` return it as it is if isinstance(module, types.FunctionType): return module # Finally return the initialized module with params from the spec as well # as those passed as **kwargs from the code # Add the `submodules` argument to the module init call if it exists in the # spec. if hasattr(spec_or_module, \u0026quot;submodules\u0026quot;) and spec_or_module.submodules is not None: kwargs[\u0026quot;submodules\u0026quot;] = spec_or_module.submodules try: return module( *args, **spec_or_module.params if hasattr(spec_or_module, \u0026quot;params\u0026quot;) else {}, **kwargs ) except Exception as e: # improve the error message since we hide the module name in the line above import sys raise type(e)(f\u0026quot;{str(e)} when instantiating {module.__name__}\u0026quot;).with_traceback( sys.exc_info()[2] ) visionmodel+llm注意点 组合 Vision Model 和 LLM 需要注意的关键点 基于对代码的分析,我将从以下几个维度为您列举所有注意事项:\n1. 配置兼容性 (TransformerConfig) 1.1 Hidden Size 匹配 Vision 输出维度 → Projection 层 → LLM 输入维度 必须对齐 示例参考: qwen2_5_vl: Vision hidden_size=1280, LLM需要通过projection映射 llava_mcore: Vision和LLM的hidden_size通过MultimodalProjector连接 1.2 序列并行 (Sequence Parallel) 配置 Vision model 和 LLM 的 sequence_parallel 设置需要协调 Qwen2_5VL例子: Vision设置为False, LLM可以True # vision config config.sequence_parallel = False # language config config.sequence_parallel = True # 需要配合TE的TEDotProductAttention 1.3 张量并行 (Tensor Parallel) 配置 tp_comm_overlap 设置需要一致 Vision encoder通常设置为 False LLM可以根据需要启用 2. Position Embedding 兼容性 2.1 Position Embedding 类型 不同的position embedding类型不兼容,需要特别注意:\n模型类型 Position Embedding 实现方式 CLIP ViT Learned Absolute torch.nn.Embedding Qwen2_5 Vision Custom RoPE VisionRotaryEmbedding LLaMA LLM RoPE RotaryEmbedding Qwen2VL LLM M-RoPE MultimodalRotaryEmbedding GPT Learned Absolute torch.nn.Embedding 关键注意: - Vision用Learned Absolute,LLM用RoPE → 需要特殊处理 - Qwen2VL用专门的M-RoPE(Multimodal RoPE),需要mrope_section参数 - Position embedding在embedding层和transformer层都会用到\n2.2 RoPE参数对齐 如果都使用RoPE,需要确保:\nrotary_base # 基础频率(10000, 1000000等) rotary_percent # RoPE维度百分比 rotary_interleaved # 是否交错 kv_channels # 必须一致 3. Attention Mechanism 兼容性 3.1 Attention Mask 类型 # Vision Model attn_mask_type = AttnMaskType.no_mask # 或 causal_pad # Language Model attn_mask_type = AttnMaskType.causal # 通常是causal 3.2 Query-Key-Value 配置 num_attention_heads: 注意力头数量 num_query_groups: GQA (Grouped Query Attention) 配置 Vision通常 num_query_groups = num_attention_heads (无GQA) LLM可能使用GQA (如Qwen2用4, LLaMA3用8) 3.3 QKV Bias add_qkv_bias # Vision和LLM可能不同 # Vision: 通常 True # LLM: 取决于具体模型 4. MLP/FFN 层配置 4.1 FFN Hidden Size ffn_hidden_size # 中间层维度 gated_linear_unit # 是否使用门控(SwiGLU等) Vision通常不用gated (GELU激活) LLM常用SwiGLU (gated=True) 4.2 激活函数 # Vision models activation_func = torch.nn.functional.gelu # 或 quick_gelu # Language models activation_func = F.silu # (if gated_linear_unit=True) 5. Layer Normalization 5.1 Normalization 类型 # Vision: 通常LayerNorm normalization = \u0026#39;LayerNorm\u0026#39; # LLM: 常用RMSNorm normalization = \u0026#39;RMSNorm\u0026#39; layernorm_zero_centered_gamma = True/False 5.2 LayerNorm 位置 Vision: Pre-LN (ln_pre) + 可选Post-LN (CLIP vs SigLIP) LLM: Pre-LN (现代架构) 或 Post-LN 6. Vision Projection Layer 6.1 Projection 配置 这是连接Vision和LLM的关键桥梁:\n# Input: vision_hidden_size * (spatial_merge_size ** 2) # Output: language_hidden_size projection_config.ffn_hidden_size # 中间层大小 projection_type # \u0026quot;mlp\u0026quot;, \u0026quot;affine\u0026quot;等 6.2 Spatial Merge Qwen2VL特有,需要注意:\nspatial_merge_size = 2 # 2x2合并 merge_hidden_size = hidden_size * spatial_merge_size ** 2 7. Pipeline Parallelism 相关 7.1 pre_process / post_process 标志 # Vision Model pre_process=True # 包含patch embedding post_process=True # 包含projection # Language Model pre_process=True # 包含word embedding post_process=True # 包含output layer 7.2 add_encoder / add_decoder 多模态模型特有:\nadd_encoder = True # 是否构建vision encoder add_decoder = True # 是否构建language decoder 不同pipeline stage需要不同配置。\n8. 数据流和嵌入融合 8.1 Embedding 融合方式 方式1: LLaVA式 - 位置替换\n# 在特定位置(image_token_index=-200)插入vision embeddings # final_embeddings = [text_emb, image_emb, text_emb, ...] 方式2: Qwen2VL式 - Mask引导\n# 使用image_input_mask和video_input_mask # 在embedding层直接融合 language_embeddings = self.language_model.embedding( input_ids=input_ids, image_input_mask=image_input_mask, image_embeds=image_embeds ) 8.2 Position IDs 处理 LLaVA: 每个图像token占1个position Qwen2VL: 图像使用3D position (时间,高,宽), 文本使用1D 9. 特殊Token处理 9.1 Vision Class Token drop_vision_class_token # 是否丢弃class token class_token_len # class token长度(0, 1, 8等) CLIP: 有class token (可选择丢弃) SigLIP: 无class token 需要与后续处理对齐 9.2 特殊Token ID image_token_index = -200 # 或 151655 video_token_id = 151656 vision_start_token_id = 151652 不同模型的token ID定义不同,需要适配。\n10. Checkpoint 加载 10.1 参数名称映射 不同来源的模型参数名可能不同:\n# Vision: \u0026quot;vision_model.*\u0026quot; # LLM: \u0026quot;language_model.*\u0026quot; # Projection: \u0026quot;vision_projection.*\u0026quot; 10.2 允许缺失权重 allow_missing_vision_projection_checkpoint = True # 当只有预训练vision和LLM,projection需要从头训练 11. Transformer Layer Specs 11.1 Layer Spec 结构必须匹配 # Vision ModuleSpec( module=TransformerLayer, submodules=TransformerLayerSubmodules( self_attention=..., mlp=..., ) ) # 需要确保submodules的类型兼容 11.2 Core Attention实现 # 使用Transformer Engine时 TEDotProductAttention # 支持flash attention # 不使用TE时 DotProductAttention 两者参数和行为可能不同。\n12. 训练相关配置 12.1 梯度累积和重计算 recompute_granularity # \u0026#39;full\u0026#39;, \u0026#39;selective\u0026#39; recompute_method # \u0026#39;uniform\u0026#39;, \u0026#39;block\u0026#39; recompute_num_layers Vision和LLM可能需要不同的策略。\n12.2 混合精度训练 fp16_lm_cross_entropy # LM loss是否用fp16 bf16 / fp16 # 整体精度 fp8 # 是否启用FP8 12.3 冻结模块 freeze_language_model = True freeze_vision_model = True freeze_vision_projection = False 13. 特定模型的特殊要求 13.1 Qwen2VL 特殊项 mrope_section # M-RoPE的section划分 fullatt_block_indexes # 全attention的block索引 window_size # 窗口attention大小 temporal_patch_size # 时间patch大小 13.2 DeepSeek-V2 MLA multi_latent_attention # 多潜在attention q_lora_rank, kv_lora_rank # LoRA rank qk_rope_head_dim # RoPE head维度 14. 输入数据格式 14.1 Vision 输入 # CLIP/LLaVA images: [num_tiles, 3, H, W] # Qwen2VL vision_data: [total_thw, features] grid_thw: [num_images, 3] # temporal, height, width 14.2 文本输入 input_ids: [batch, seq_len] position_ids: [batch, seq_len] 或 [3, batch, seq_len] # Qwen2VL attention_mask: [batch, 1, seq_len, seq_len] 15. 推理优化 15.1 KV Cache inference_params.key_value_memory_dict[\u0026quot;image_tokens_count\u0026quot;] # 存储image token数量,用于KV cache offset 15.2 Flash Decode flash_decode = True # 需要预计算的cos/sin for RoPE 实践建议 最安全的组合策略: 选择相似架构的模型 Vision: CLIP系列 + LLM: GPT系列 (都是标准Transformer) Vision: Qwen2VL Vision + LLM: Qwen2 LLM (同系列) 使用相同的Position Embedding类型 都用RoPE,或都用Learned Absolute 避免混用 统一Normalization和Activation 检查LayerNorm vs RMSNorm 检查GELU vs SiLU 仔细设计Projection层 确保维度正确映射 考虑是否需要spatial merge 测试Pipeline Parallel兼容性 从单GPU开始 逐步增加PP和TP 调试检查清单: # 1. 打印各层输出shape print(f\u0026quot;Vision output: {vision_output.shape}\u0026quot;) print(f\u0026quot;After projection: {projected.shape}\u0026quot;) print(f\u0026quot;LLM input: {llm_input.shape}\u0026quot;) # 2. 检查dtype一致性 assert vision_output.dtype == llm_input.dtype # 3. 检查device assert vision_output.device == llm_input.device # 4. 检查config关键参数 print(f\u0026quot;Vision hidden: {vision_config.hidden_size}\u0026quot;) print(f\u0026quot;LLM hidden: {language_config.hidden_size}\u0026quot;) print(f\u0026quot;Projection out: {projection_config.hidden_size}\u0026quot;) 希望这份详尽的清单能帮助您成功组合不同的Vision Model和LLM!如果您有具体的模型组合计划,我可以提供更针对性的建议。\n","date":"2025-11-03T00:00:00Z","permalink":"/cr%E9%98%85%E8%AF%BB%E8%AE%B0%E5%BD%95/","title":"cr阅读记录"},{"content":"我来分析一下将 DeepSeek-V2 作为 LLM，同时使用 Qwen3-VL 的视觉编码器和投影器是否可行。\n🔍 可行性分析 ✅ 理论上可行，但需要解决多个关键技术问题 📊 主要挑战和兼容性分析 1️⃣ 位置编码兼容性问题 ⚠️ 重大挑战 Qwen3-VL 使用 M-RoPE（多模态旋转位置编码）： - 3D 位置编码：position_ids 形状为 [3, batch, seq_len]（T/H/W 三个维度） - 需要 mrope_section 配置来分配维度 - 在 gpt_model.py 中专门处理 M-RoPE\nDeepSeek-V2 可能使用： - 标准 RoPE 或其他位置编码方案 - position_ids 通常是 [batch, seq_len] 的 2D 张量\n冲突点：\n# Qwen3-VL 期望 rotary_pos_emb = self.rotary_pos_emb(position_ids, self.mrope_section) # position_ids: [3, batch, seq_len] # 标准 DeepSeek-V2 可能期望 rotary_pos_emb = self.rotary_pos_emb(seq_len) # 或者 position_ids: [batch, seq_len] 解决方案： - 需要修改 DeepSeek-V2 的位置编码模块以支持 M-RoPE - 或者在 Qwen3-VL 视觉特征进入 DeepSeek-V2 前转换位置编码格式\n2️⃣ DeepStack 特征注入机制 ⚠️ 需要适配 Qwen3-VL 的 DeepStack 特性：\n# transformer_block.py (Qwen3-VL 语言模型) if deepstack_visual_embeds is not None and l_no in range(len(deepstack_visual_embeds)): hidden_states = self._deepstack_process( hidden_states, visual_pos_masks, deepstack_visual_embeds[l_no], ) 问题： - DeepSeek-V2 的 Transformer Block 很可能没有这个 _deepstack_process 方法 - 需要在 DeepSeek-V2 的前向传播中添加视觉特征注入逻辑 - 需要处理 visual_pos_masks 来定位视觉 token 位置\n需要修改的地方： 1. DeepSeek-V2 的 TransformerBlock.forward() 方法 2. 添加 visual_pos_masks 和 deepstack_visual_embeds 参数 3. 实现特征注入逻辑（加法融合）\n3️⃣ Embedding 层的多模态支持 ⚠️ 关键适配点 Qwen3-VL 的特殊 Embedding：\n# model.py combined_embeddings = self.language_model.embedding( input_ids=input_ids, position_ids=None, image_input_mask=image_input_mask, video_input_mask=video_input_mask, image_embeds=image_embeds, video_embeds=video_embeds ) Qwen3-VL 使用的是 LanguageModelEmbedding： - 支持 image_input_mask 和 video_input_mask - 可以直接注入 image_embeds 和 video_embeds - 在特定位置替换文本 embedding 为视觉 embedding\nDeepSeek-V2 的 Embedding： - 很可能是标准的词嵌入层 - 不支持视觉特征注入 - 没有多模态掩码机制\n解决方案： - 需要将 DeepSeek-V2 的 Embedding 层替换为 Qwen3-VL 的 LanguageModelEmbedding - 或者在 DeepSeek-V2 的 Embedding 层外部手动进行视觉特征注入\n4️⃣ 模型架构差异 ⚠️ 潜在兼容问题 DeepSeek-V2 的特殊架构（如果有）： - MLA (Multi-head Latent Attention)：可能使用特殊的注意力机制 - MoE (Mixture of Experts)：可能有专家混合层 - 特殊的激活函数或归一化\nQwen3-VL 期望的架构： - 标准的 Multi-head Attention - Dense MLP 或支持 MoE - 使用 LayerNorm\n兼容性考虑： - 如果 DeepSeek-V2 使用 MLA，需要确保它能处理多模态输入 - MoE 层需要能正确处理视觉 token - 确保 hidden_size 维度匹配\n5️⃣ Forward 方法签名差异 ⚠️ 接口适配 Qwen3-VL 的 forward 签名：\ndef forward( self, input_ids, position_ids, attention_mask, decoder_input=None, labels=None, visual_pos_masks=None, # ← 特殊参数 deepstack_visual_embeds=None, # ← 特殊参数 ... ) 标准 DeepSeek-V2 可能的 forward：\ndef forward( self, input_ids, position_ids, attention_mask, labels=None, ... ) 解决方案： - 需要扩展 DeepSeek-V2 的 forward 方法签名 - 在整个调用链中传递多模态参数\n6️⃣ Hidden Size 匹配 ⚠️ 维度对齐 Qwen3-VL 视觉编码器输出： - Vision Model: hidden_size = 1152 (8B) 或 1024 (4B) - Projector 输出: 需要匹配语言模型的 hidden_size\nDeepSeek-V2 的 hidden_size： - 需要查看具体配置 - 如果不匹配，Projector 需要调整输出维度\n检查点：\n# vision_projection_config.py 中需要设置 config.hidden_size = deepseek_v2_hidden_size # 确保匹配 7️⃣ 特殊 Token 处理 ⚠️ 词表兼容性 Qwen3-VL 的特殊 token：\nvision_start_token_id = 151652 image_token_id = 151655 video_token_id = 151656 问题： - DeepSeek-V2 的词表可能没有这些 token - 需要扩展 DeepSeek-V2 的词表 - 或者重新映射这些 token ID\n📋 实施步骤建议 第一阶段：最小化改动方案 保持 Qwen3-VL 的视觉部分完全不变 Vision Model Projector DeepStack 特征提取 修改 DeepSeek-V2 以支持必要接口 添加 M-RoPE 支持（或转换为 2D RoPE） 添加视觉特征注入点 扩展 Embedding 层 第二阶段：深度集成 创建混合模型类 类似 Qwen3VLModel Vision: Qwen3VisionModel Language: DeepSeekV2Model（修改版） 实现特征融合逻辑 在 DeepSeek-V2 的前几层注入 DeepStack 特征 处理位置编码转换 ⚠️ 关键风险点 1. 位置编码不兼容 风险等级：高 M-RoPE 是 Qwen3-VL 的核心特性 DeepSeek-V2 可能无法直接支持 2. 训练收敛问题 风险等级：中高 DeepSeek-V2 和 Qwen3-VL 的训练范式可能不同 需要大量实验找到合适的学习率和训练策略 3. 性能下降 风险等级：中 DeepStack 特征是为 Qwen3 系列优化的 在 DeepSeek-V2 上效果未知 ✅ 推荐方案 方案 A：轻量级适配（推荐用于快速验证） 简化位置编码 放弃 M-RoPE，使用标准 2D RoPE 损失部分多模态位置建模能力 简化 DeepStack 只在第一层注入视觉特征 减少改动复杂度 使用 Adapter 模式 在 DeepSeek-V2 和视觉特征之间加入适配层 转换维度和格式 方案 B：完整适配（推荐用于生产环境） 完整移植 M-RoPE 修改 DeepSeek-V2 的位置编码模块 支持 3D 位置编码 实现完整 DeepStack 在多个层注入视觉特征 保留 Qwen3-VL 的设计优势 统一接口设计 创建统一的多模态模型基类 规范化参数传递 🎯 结论 可行性评分：6.5/10\n可行，但需要大量工程工作：\n✅ 优势： - 可以利用 Qwen3-VL 强大的视觉编码能力 - 可以利用 DeepSeek-V2 的语言建模能力 - 架构上没有根本性冲突\n❌ 挑战： - 位置编码兼容性是最大障碍 - 需要修改 DeepSeek-V2 的多个核心模块 - 需要大量实验验证效果 - 训练和调优工作量大\n建议： 1. 先用方案 A 做快速原型验证 2. 如果效果可行，再投入资源做方案 B 的完整适配 3. 重点关注位置编码和 Embedding 层的适配 4. 准备充分的消融实验验证各组件的必要性\n","date":"2025-10-29T00:00:00Z","permalink":"/qwen3vl-deepseek/","title":"qwen3vl+deepseek"},{"content":"Patch 就是把一张大图像切分成多个小方块（小图像块），然后把每个小方块当作一个”token”（类似文本中的单词）来处理。\n为什么需要 Patch？ 传统 CNN：\n图像 [224×224×3] → Conv → Pool → ... → 特征 Vision Transformer (ViT)：\n图像 [224×224×3] → 切成 Patches → Transformer → 特征 核心思想：让 Transformer 能像处理文本一样处理图像！\n4.2 Patch 切分过程（图解） 步骤 1: 原始图像 原始图像：224×224 像素，RGB 三通道 ┌─────────────────────────────────────┐ │ │ │ 🖼️ 猫咪图片 │ 224 像素 │ │ │ │ └─────────────────────────────────────┘ 224 像素 步骤 2: 切分成 Patches 假设 patch_size = 16，图像被切成 16×16 的小块：\n切分后：14×14 = 196 个 patches ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │P1│P2│P3│P4│P5│P6│P7│P8│P9│..│ │ │ │14│ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │15│16│17│18│19│20│21│22│23│..│ │ │ │28│ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │29│30│31│32│33│34│35│36│37│..│ │ │ │42│ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │183│184│185│186│187│188│189│190│191│..│ │ │ │196│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ 每个 Patch：16×16×3 = 768 个像素值 总共：196 个 patches 步骤 3: 展平 Patch 每个 Patch 从 3D 张量变成 1D 向量：\nPatch[0]: [16, 16, 3] → Flatten → [768] ┌─────────┐ │ R值×256 │ │ G值×256 │ ⟹ [768维向量] │ B值×256 │ └─────────┘ 步骤 4: 线性投影（Embedding） 将每个 768 维向量投影到模型的隐藏维度（如 1152）：\n[768] × W[768×1152] + b[1152] → [1152] 最终输出：[196个patches, 1152维] 的 Embedding 4.3 代码实现详解 PatchEmbed 类 class PatchEmbed(nn.Module): def __init__( self, patch_size: int = 14, # 每个patch的边长 temporal_patch_size: int = 2, # 视频时间维度patch大小 in_channels: int = 3, # 输入通道数（RGB=3） embed_dim: int = 1152, # 输出嵌入维度 bias: bool = False ): super().__init__() # 关键：使用 3D 卷积同时处理时间和空间维度 kernel_size = [temporal_patch_size, patch_size, patch_size] # [2, 14, 14] self.proj = nn.Conv3d( in_channels, # 输入：3 通道 embed_dim, # 输出：1152 通道 kernel_size=kernel_size, # 卷积核大小 stride=kernel_size, # 步长=核大小（无重叠） bias=bias ) def forward(self, hidden_states: torch.Tensor) -\u0026gt; torch.Tensor: # 输入 shape: [n_patches, channels, t, h, w] # 例如：[N, 3, 2, 16, 16] # 重塑为 5D hidden_states = hidden_states.view( -1, # batch中的patch数 self.in_channels, # 3 (RGB) self.temporal_patch_size, # 2 self.patch_size, # 16 self.patch_size # 16 ) # Shape: [N, 3, 2, 16, 16] # 3D卷积：一步完成切分和投影 hidden_states = self.proj(hidden_states) # Shape: [N, 1152, 1, 1, 1] # 展平成 2D hidden_states = hidden_states.view(-1, self.embed_dim) # Shape: [N, 1152] return hidden_states 为什么用 Conv3d？ 高效实现：一次卷积完成切分+投影 支持视频：时间维度也能 patch 化 无重叠切分：stride = kernel_size 4.4 2D Patch vs 3D Patch 2D Patch（图像） # 单张图片 image = [224, 224, 3] # Patch化 patch_size = 16 num_patches_h = 224 / 16 = 14 num_patches_w = 224 / 16 = 14 total_patches = 14 × 14 = 196 # 每个patch patch_shape = [16, 16, 3] patch_dim = 16 × 16 × 3 = 768 3D Patch（视频） # 视频 video = [8帧, 224, 224, 3] # Patch化 temporal_patch_size = 2 spatial_patch_size = 16 num_patches_t = 8 / 2 = 4 num_patches_h = 224 / 16 = 14 num_patches_w = 224 / 16 = 14 total_patches = 4 × 14 × 14 = 784 # 每个patch patch_shape = [2, 16, 16, 3] patch_dim = 2 × 16 × 16 × 3 = 1536 4.5 具体数值示例 Qwen3VL-8B 配置 patch_size = 14 # 空间 patch 大小 temporal_patch_size = 2 # 时间 patch 大小 embed_dim = 1152 # 嵌入维度 spatial_merge_size = 2 # 后续合并大小 单张图片处理 # 输入：448×448 图片 image = torch.randn(1, 3, 448, 448) # [batch, C, H, W] # 转为 5D（加时间维度 T=1） image_5d = image.view(1, 3, 1, 448, 448) # [B, C, T, H, W] # Patch化 patches = patch_embed(image_5d) # 计算patch数量 num_patches_t = 1 / 2 = 1（向上取整） num_patches_h = 448 / 14 = 32 num_patches_w = 448 / 14 = 32 total_patches = 1 × 32 × 32 = 1024 print(patches.shape) # [1024, 1152] 视频处理 # 输入：8帧 224×224 视频 video = torch.randn(1, 3, 8, 224, 224) # [B, C, T, H, W] # Patch化 patches = patch_embed(video) # 计算patch数量 num_patches_t = 8 / 2 = 4 num_patches_h = 224 / 14 = 16 num_patches_w = 224 / 14 = 16 total_patches = 4 × 16 × 16 = 1024 print(patches.shape) # [1024, 1152] 4.6 Patch 的优势与挑战 ✅ 优势 全局建模：Self-Attention 让所有 patches 都能交互 可扩展性：容易处理不同分辨率（通过位置编码插值） 统一架构：图像和文本都变成 token 序列 并行性好：所有 patches 可以并行处理 ⚠️ 挑战 计算代价高：Self-Attention 是 O(n²)，patch 越多越慢 解决方案：Spatial Merge、Window Attention 丢失细节：Patch 内部的空间关系被忽略 解决方案：使用更小的 patch_size（但会增加计算） 需要大数据：ViT 在小数据集上表现不如 CNN 解决方案：使用预训练模型 5. 视觉 Token 在 LLM 中的实际占用 5.1 核心答案 问题：图片输入到 LLM 中占的 token 数就是 patch 数吗？\n答案：不等于原始 Patch 数，通常是 Patch 数的 1/4（使用 Spatial Merge）\n5.2 完整转换链路 以 Qwen3VL 处理 448×448 图像为例：\n步骤 0: 原始图像 image = [448, 448, 3] # RGB图像 步骤 1: Patch Embedding patch_size = 14 num_patches_h = 448 / 14 = 32 num_patches_w = 448 / 14 = 32 total_patches = 32 × 32 = 1024 # 每个patch嵌入到 1152 维 vision_tokens_after_patch = [1024, 1152] 步骤 2: Spatial Merge（关键！） spatial_merge_size = 2 # Qwen3VL配置 # 相邻的 2×2=4 个patches合并成1个token merged_h = 32 / 2 = 16 merged_w = 32 / 2 = 16 merged_tokens = 16 × 16 = 256 # 每个合并后的token维度变大 merge_hidden_size = 1152 × (2×2) = 4608 vision_tokens_after_merge = [256, 4608] 步骤 3: Projection（投影到语言空间） # 通过MLP投影到语言模型维度 language_hidden_size = 2560 # 8B模型 vision_tokens_final = [256, 2560] 步骤 4: 插入到文本序列 # 最终在LLM中占用的token数 LLM_vision_tokens = 256 ← 答案！ 5.3 计算公式 通用公式 # 原始patch数量 num_patches = (H / patch_size) × (W / patch_size) # LLM中的视觉token数量 llm_vision_tokens = num_patches / (spatial_merge_size²) 不同图像尺寸的对比 图像尺寸 Patch Size 原始Patches Spatial Merge LLM中的Tokens 224×224 14 16×16 = 256 2 256/4 = 64 448×448 14 32×32 = 1024 2 1024/4 = 256 672×672 14 48×48 = 2304 2 2304/4 = 576 896×896 14 64×64 = 4096 2 4096/4 = 1024 5.4 代码证据 1. Spatial Merge 配置 # visionmodel.py, line 94-95 self.merge_hidden_size = projection_config.ffn_hidden_size self.square_merge_size = self.merge_hidden_size // embed_dim # 例如：ffn_hidden_size = 4608, embed_dim = 1152 # square_merge_size = 4608 / 1152 = 4 = 2² # 说明是 2×2 的merge 2. LLM 中的视觉 Token 计算 # model.py, get_rope_index 函数 llm_grid_t, llm_grid_h, llm_grid_w = ( t.item(), h.item() // spatial_merge_size, # ← 除以 spatial_merge_size w.item() // spatial_merge_size, ) # 视觉token数量 vision_token_count = llm_grid_t * llm_grid_h * llm_grid_w # 例如：单张图片 (t=1, h=32, w=32, merge_size=2) # vision_token_count = 1 × (32/2) × (32/2) = 1 × 16 × 16 = 256 5.5 视频的情况 示例：8帧 224×224 视频 # 原始数据 video = [8, 224, 224, 3] # 步骤1: 3D Patch Embedding temporal_patch_size = 2 spatial_patch_size = 14 num_patches_t = 8 / 2 = 4 num_patches_h = 224 / 14 = 16 num_patches_w = 224 / 14 = 16 total_patches = 4 × 16 × 16 = 1024 # 步骤2: Spatial Merge（只merge空间，不merge时间） spatial_merge_size = 2 merged_t = 4 # 时间维度不变 merged_h = 16 / 2 = 8 merged_w = 16 / 2 = 8 merged_tokens = 4 × 8 × 8 = 256 # LLM中的token数 llm_vision_tokens = 256 注意：Qwen3VL 使用时间戳方式，每帧单独处理：\n# Qwen3VL的实际方式（使用时间戳） # 序列：\u0026lt;t1\u0026gt; \u0026lt;vision_start\u0026gt; \u0026lt;frame1\u0026gt; \u0026lt;vision_end\u0026gt; \u0026lt;t2\u0026gt; \u0026lt;vision_start\u0026gt; \u0026lt;frame2\u0026gt; \u0026lt;vision_end\u0026gt; ... # 每帧token数 tokens_per_frame = (224/14)² / 4 = 16² / 4 = 64 # 8帧总token数 total_tokens = 8 × 64 = 512 5.6 为什么要做 Spatial Merge？ 原因 1: 减少计算量 # 没有merge num_tokens = 1024 attention_cost = O(1024²) = 1,048,576 # 有merge (2×2) num_tokens = 256 attention_cost = O(256²) = 65,536 # 减少了 16 倍！✨ 原因 2: 更好的语义表示 # 单个patch: 14×14像素，可能只包含部分物体 # 合并后: 28×28像素，更容易包含完整的局部特征 原因 3: 对齐语言模型的token粒度 # 文本token: 通常是一个词或子词 # 图像token: merge后的patch更接近\u0026quot;视觉词\u0026quot;的概念 5.7 实际序列示例 # 输入 text = \u0026quot;这张图片中有什么？\u0026lt;image\u0026gt;这是一只可爱的猫。\u0026quot; image = [448, 448, 3] # Token化后 tokens = [ \u0026quot;这\u0026quot;, \u0026quot;张\u0026quot;, \u0026quot;图片\u0026quot;, \u0026quot;中\u0026quot;, \u0026quot;有\u0026quot;, \u0026quot;什么\u0026quot;, \u0026quot;？\u0026quot;, # 7个文本tokens \u0026quot;\u0026lt;vision_start\u0026gt;\u0026quot;, # 1个特殊token \u0026lt;256 个视觉 tokens\u0026gt;, # 256个图像tokens \u0026quot;\u0026lt;vision_end\u0026gt;\u0026quot;, # 1个特殊token \u0026quot;这\u0026quot;, \u0026quot;是\u0026quot;, \u0026quot;一\u0026quot;, \u0026quot;只\u0026quot;, \u0026quot;可爱\u0026quot;, \u0026quot;的\u0026quot;, \u0026quot;猫\u0026quot;, \u0026quot;。\u0026quot; # 8个文本tokens ] # 总长度 = 7 + 1 + 256 + 1 + 8 = 273 tokens 关键发现： - 原始 patches：1024 个 - LLM 中实际占用：256 个 ✅ - 减少比例：1024/256 = 4倍（2×2 merge）\n5.8 可视化对比 原始Patches: 32×32 = 1024 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │P1│P2│P3│P4│P5│P6│P7│P8│P9│..│ │ │ │ │ │32│ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ ↓ Spatial Merge (2×2) LLM Tokens: 16×16 = 256 ┌─────┬─────┬─────┬─────┬─────┬─────┬────┐ │ T1 │ T2 │ T3 │ T4 │ ... │ │ 16 │ │(4个)│(4个)│(4个)│(4个)│ │ │ │ ├─────┼─────┼─────┼─────┼─────┼─────┼────┤ │ │ │ │ │ │ │ │ └─────┴─────┴─────┴─────┴─────┴─────┴────┘ 每个LLM Token = 2×2 = 4个原始patches的信息 5.9 不同模型对比 模型 Patch Size Spatial Merge 448×448图像的Token数 Qwen3VL 14 2×2 256 Qwen2VL 14 2×2 256 LLaVA-1.5 14 无 1024 CLIP ViT 14 无 1024 Qwen-VL 14 无 1024 结论：新版多模态模型普遍使用 Spatial Merge 来减少视觉 token 数量！\n5.10 总结 核心要点 原始 Patch 数 ≠ LLM 中的 Token 数\nQwen3VL 的转换：Patches → Spatial Merge (÷4) → LLM Tokens\n实际占用公式：\nLLM_tokens = (H/patch_size) × (W/patch_size) / (merge_size²) 具体例子（448×448 图像）：\nPatches: 1024 个 LLM Tokens: 256 个 ← 实际占用 比例: 1:4 设计原因 ✅ 效率：减少 Attention 计算量（O(n²)）\n✅ 语义：合并后的 token 包含更丰富的局部信息\n✅ 灵活：可以处理更高分辨率的图像\n✅ 性能：实验证明这样效果更好\n附录 A. 关键术语表 术语 英文 解释 Patch Patch 图像的小方块切片 Spatial Merge Spatial Merge 空间合并，将相邻patches合并 MRoPE Multi-modal RoPE 多模态旋转位置编码 DeepStack DeepStack 多层视觉特征注入机制 ModuleSpec ModuleSpec 模块规范，延迟实例化 Pipeline Parallel Pipeline Parallelism 流水线并行 Tensor Parallel Tensor Parallelism 张量并行 B. 参考资源 Megatron-LM GitHub: https://github.com/NVIDIA/Megatron-LM Qwen3VL 论文: [待补充] Vision Transformer 论文: https://arxiv.org/abs/2010.11929 RoFormer (RoPE): https://arxiv.org/abs/2104.09864 文档生成时间: 2025年10月28日\n文档版本: v1.0\n作者: AI Assistant based on Pai-Megatron-Patch codebase analysis\n","date":"2025-10-28T00:00:00Z","permalink":"/patch%E8%AF%A6%E8%A7%A3/","title":"patch详解"},{"content":"Pai-Megatron-Patch 项目深度学习总结文档 本文档基于对Pai-Megatron-Patch项目的系统性代码阅读和分析，总结了该项目的核心架构、设计理念、技术实现和最佳实践。\n目录 项目整体概览 核心架构设计 模型实现详解 数据处理系统 模型权重转换 训练流程剖析 强化学习集成 高级优化技术 工具链生态 最佳实践总结 1. 项目整体概览 1.1 项目定位与核心价值 Pai-Megatron-Patch是阿里云PAI算法团队开发的大模型训练加速工具，其核心定位是： - 桥梁角色：连接开源大模型（HuggingFace生态）和Megatron训练引擎 - 非侵入式设计：以patch方式扩展Megatron，不修改其源码 - 易用性优先：降低Megatron使用门槛，提供完整的训练流程\n1.2 支持的模型矩阵 模型类型 具体模型 特性支持 基础LLM LLaMA 1/2/3/3.1, Qwen 1/1.5/2/2.5/3, Baichuan, Mistral, Yi 预训练、SFT、评估、推理 MoE模型 Mixtral 8x7B, Qwen-MoE, DeepSeek-V2/V3 专家并行、Load Balance MLA架构 DeepSeek-V2/V3 (671B), Moonlight Multi-head Latent Attention 多模态 LLaVA, Qwen-VL, Qwen2-VL, Qwen3-VL, Qwen2.5-VL 图文理解、视频理解 特殊架构 Bloom, GLM, Starcoder, Galactica 各类特殊position encoding 1.3 核心功能组件 Pai-Megatron-Patch/ ├── megatron_patch/ # 核心patch模块 │ ├── model/ # 各类模型实现（20+模型） │ ├── data/ # 数据加载和处理 │ ├── tokenizer/ # 分词器封装 │ ├── generation/ # 推理生成 │ ├── training.py # 训练主流程 │ ├── finetune_utils.py # 微调工具 │ └── arguments.py # 参数扩展系统 ├── toolkits/ # 工具链 │ ├── model_checkpoints_convertor/ # 权重转换 │ ├── distributed_checkpoints_convertor/ # 分布式转换 │ ├── pretrain_data_preprocessing/ # 预训练数据处理 │ ├── sft_data_preprocessing/ # SFT数据处理 │ └── auto_configurator/ # 自动配置 ├── examples/ # 完整示例（30+模型） ├── backends/ # 依赖的Megatron版本和RL框架 │ ├── megatron/ # 多版本Megatron-LM │ └── rl/ # ChatLearn、Verl └── verl_patch/ # Verl适配层 2. 核心架构设计 2.1 非侵入式Patch设计哲学 设计原则： - 不修改Megatron-LM源码 - 通过继承和组合扩展功能 - 参数系统动态注入 - 解耦合设计便于版本升级\n关键实现：\n参数动态注入 (megatron_patch/arguments.py) def patch_if_not_exist(group_or_parser, keyname, type=None, default=None): \u0026quot;\u0026quot;\u0026quot; 只在参数不存在时添加，避免与Megatron冲突 \u0026quot;\u0026quot;\u0026quot; has_keyname = False for action in vars(group_or_parser)[\u0026quot;_actions\u0026quot;]: if keyname in action.option_strings: has_keyname = True if not has_keyname: return group_or_parser.add_argument(keyname, type=type, default=default) 设计亮点： - 检查参数是否已存在，避免重复定义 - 支持动态修改已有参数的默认值 - 为不同模型添加专有参数（如MoE、MLA参数）\n初始化流程封装 (megatron_patch/initialize.py) def initialize_megatron(extra_args_provider=None, args_defaults={}): \u0026quot;\u0026quot;\u0026quot; 初始化Megatron并验证patch参数 \u0026quot;\u0026quot;\u0026quot; # 1. 解析参数 args = parse_args(extra_args_provider, ignore_unknown_args) # 2. 验证原生Megatron参数 validate_args(args, args_defaults) # 3. 验证patch新增的MoE参数 validate_moe_args(args, args_defaults) # 4. 设置全局变量 set_global_variables(args) # 5. 初始化分布式通信 _initialize_distributed() 2.2 训练流程的统一抽象 预训练主流程 (megatron_patch/training.py) 核心函数签名：\ndef pretrain( train_valid_test_dataset_provider, # 数据提供函数 model_provider, # 模型构建函数 model_type, # 模型类型枚举 forward_step_func, # 前向传播函数 process_non_loss_data_func=None, # 非loss数据处理 extra_args_provider=None, # 额外参数注入 args_defaults={} # 参数默认值 ): 设计模式：函数式编程 + 依赖注入 - 将可变部分（模型、数据、前向）抽象为函数参数 - 训练循环、分布式通信、checkpoint等通用逻辑封装在pretrain内部 - 用户只需提供3个核心函数即可完成训练\n微调流程 (megatron_patch/finetune_utils.py) def finetune( train_valid_datasets_provider, # 数据集提供 model_provider, # 模型构建 model_type=ModelType.encoder_or_decoder, forward_step=_cross_entropy_forward_step, # 默认CE loss end_of_epoch_callback_provider=None, # epoch结束回调 task_collate_fn=None # 自定义collate ): 与预训练的差异： - 基于epoch而非iteration计数 - 支持动态数据加载器 - 添加per-epoch回调机制 - 支持gradient accumulation = 1的限制\n2.3 模型provider模式 示例 (examples/llama2/pretrain_mcore_llama.py):\ndef model_provider(pre_process=True, post_process=True): \u0026quot;\u0026quot;\u0026quot; 模型构建函数 - 在每个rank上调用 \u0026quot;\u0026quot;\u0026quot; args = get_args() # 1. 构建tokenizer build_tokenizer(args) # 2. 从args构建transformer config config = core_transformer_config_from_args(args) # 3. 获取layer spec（定义层结构） transformer_layer_spec = get_gpt_layer_with_transformer_engine_spec( args.num_experts, args.moe_grouped_gemm ) # 4. 构建模型 model = GPTModel( config=config, transformer_layer_spec=transformer_layer_spec, vocab_size=args.padded_vocab_size, max_sequence_length=args.max_position_embeddings, pre_process=pre_process, # 首个PP stage包含embedding post_process=post_process, # 最后PP stage包含output layer fp16_lm_cross_entropy=args.fp16_lm_cross_entropy, parallel_output=True, share_embeddings_and_output_weights=not args.untie_embeddings_and_output_weights, position_embedding_type=args.position_embedding_type, rotary_percent=args.rotary_percent, rotary_base=args.rotary_base, ) return model 关键理解： - pre_process/post_process：用于流水线并行，中间stage不需要embedding和output层 - transformer_layer_spec：ModuleSpec模式，延迟实例化，便于TE/非TE切换\n3. 模型实现详解 3.1 Megatron-Core模型架构 GPT模型的层次结构 GPTModel (megatron_patch/model/llama3/model.py) ├── LanguageModelEmbedding (pre_process=True) │ ├── word_embeddings: vocab_size → hidden_size │ └── position_embeddings (可选learned_absolute) ├── RotaryEmbedding (position_embedding_type=\u0026#39;rope\u0026#39;) │ └── 计算旋转位置编码 ├── TransformerBlock (decoder) │ └── N × TransformerLayer │ ├── input_layernorm (RMSNorm for LLaMA) │ ├── SelfAttention │ │ ├── linear_qkv (QKV projection) │ │ ├── core_attention (FlashAttention/DotProductAttention) │ │ └── linear_proj (output projection) │ ├── pre_mlp_layernorm │ └── MLP │ ├── linear_fc1 (up projection) │ └── linear_fc2 (down projection, gated for LLaMA) └── output_layer: hidden_size → vocab_size (post_process=True) Layer Spec模式 (megatron_patch/model/llama3/layer_specs.py) def get_gpt_layer_with_transformer_engine_spec(num_experts=None): \u0026quot;\u0026quot;\u0026quot; 使用Transformer Engine加速的layer spec \u0026quot;\u0026quot;\u0026quot; return ModuleSpec( module=TransformerLayer, submodules=TransformerLayerSubmodules( self_attention=ModuleSpec( module=SelfAttention, params={\u0026quot;attn_mask_type\u0026quot;: AttnMaskType.causal}, submodules=SelfAttentionSubmodules( linear_qkv=TELayerNormColumnParallelLinear, # TE融合算子 core_attention=TEDotProductAttention, # TE FlashAttention linear_proj=TERowParallelLinear, q_layernorm=TENorm if qk_layernorm else IdentityOp, k_layernorm=TENorm if qk_layernorm else IdentityOp, ), ), self_attn_bda=get_bias_dropout_add, # 融合bias+dropout+add pre_mlp_layernorm=TENorm, mlp=mlp, mlp_bda=get_bias_dropout_add, ), ) 设计优势： - 延迟实例化：spec只是描述，实际module在build_module时创建 - 灵活切换：TE版本 vs 原生PyTorch版本一键切换 - FP8支持：TE spec自动支持FP8训练\n3.2 Multi-Latent Attention (MLA) DeepSeek-V2/V3的核心创新 (megatron_patch/model/deepseek_v2/multi_latent_attention.py)\nMLA vs 传统MHA 传统Multi-Head Attention:\nQ: [batch, seq, num_heads * head_dim] # 如 128 heads × 128 dim = 16K K: [batch, seq, num_heads * head_dim] V: [batch, seq, num_heads * head_dim] KV Cache: [batch, seq, 2 * num_heads * head_dim] # 显存占用大 MLA (Multi-head Latent Attention):\n# 压缩到latent space KV_latent: [batch, seq, latent_dim] # 如512维，远小于16K # 解压时再恢复到multi-head Q: [batch, seq, num_heads * qk_head_dim] K: decode(KV_latent) → [batch, seq, num_heads * qk_head_dim] V: decode(KV_latent) → [batch, seq, num_heads * v_head_dim] KV Cache: [batch, seq, latent_dim] # 显存节省~30x 核心实现 class MultiLatentAttention(Attention): def __init__(self, config: MLATransformerConfig, ...): # Q投影：分为两步 self.linear_q_down_proj: hidden → q_lora_rank # 降维 self.linear_q_up_proj: q_lora_rank → num_heads * qk_head_dim # 升维 # KV投影：共享latent space self.linear_kv_down_proj: hidden → kv_lora_rank # 压缩 self.linear_kv_up_proj: kv_lora_rank → num_heads * (qk + v)_head_dim # 解压 # RoPE只应用于qk_pos_emb_head_dim维度 self.rotary_pos_emb = YarnRotaryEmbedding( qk_pos_emb_head_dim, rotary_base=config.rotary_base, scaling_factor=config.rotary_scaling_factor, ... ) 关键技术细节： 1. Low-Rank压缩：类似LoRA思想，用低秩分解减少参数和KV cache 2. 解耦RoPE：位置编码只加到qk_pos_emb维度，不影响value 3. Yarn Scaling：支持超长上下文的RoPE扩展 4. mscale调整：softmax_scale = mscale² / sqrt(q_head_dim)\n3.3 MoE (Mixture of Experts) Expert Parallel实现 (Qwen-MoE示例) # megatron_patch/model/qwen2_moe/layer_specs.py def get_gpt_layer_with_transformer_engine_spec_moe(): mlp = ModuleSpec( module=MoELayer, submodules=MoESubmodules( experts=ModuleSpec( module=TEGroupedMLP, # Grouped GEMM优化 submodules=MLPSubmodules( linear_fc1=TEColumnParallelLinear, linear_fc2=TERowParallelLinear, ), ), router=TopKRouter, # Top-K路由 shared_experts=ModuleSpec(...), # Qwen2-MoE的shared experts ) ) MoE关键参数： - --num-experts：专家数量（如8, 64） - --moe-router-topk：每个token选择的专家数（如2） - --expert-model-parallel-size：专家并行度 - --moe-grouped-gemm：是否使用Grouped GEMM优化 - --moe-token-dispatcher-type：alltoall vs allgather dispatcher\nLoad Balance策略： - Auxiliary Loss：鼓励token均匀分布到专家 - Expert Capacity：限制每个专家处理的token数 - Z-loss：稳定router logits\n3.4 多模态模型架构 Qwen2-VL实现 (megatron_patch/model/qwen2_vl/) Qwen2VLModel ├── vision_model: ViTModel │ ├── PatchEmbed: 图像 → patches │ ├── RotaryEmbedding3D: (T, H, W) 3D位置编码 │ └── N × ViTBlock (attention + MLP) ├── visual_projector: vision_hidden → llm_hidden └── language_model: GPTModel └── 处理交错的文本和图像token 关键技术： 1. 动态分辨率：任意分辨率图像 → 可变数量的patches 2. 3D RoPE：时间+空间位置编码 (grid_t, grid_h, grid_w) 3. Vision-Language Connector：对齐视觉和语言空间 4. Interleaved Input：\u0026lt;image\u0026gt;\u0026lt;text\u0026gt;\u0026lt;image\u0026gt;\u0026lt;text\u0026gt;交错处理\n4. 数据处理系统 4.1 预训练数据Pipeline Megatron原生格式 (.bin + .idx) # 工具: toolkits/pretrain_data_preprocessing/preprocess_data_megatron.py python preprocess_data_megatron.py \\ --input my_corpus.json \\ --output-prefix my_corpus_tokenized \\ --tokenizer-type GPT2BPETokenizer \\ --vocab-file vocab.json \\ --merge-file merges.txt \\ --append-eod \\ --workers 32 输出： - my_corpus_tokenized_text_document.bin: 二进制token序列 - my_corpus_tokenized_text_document.idx: 索引文件（快速定位文档）\n优势： - 预先tokenize，训练时零开销 - mmap加载，内存友好 - 支持多文档blend和采样权重\n数据加载 (megatron_patch/data/) def train_valid_test_datasets_provider(train_val_test_num_samples): args = get_args() if \u0026quot;-Raw\u0026quot; in args.dataset: # 原始格式：每次训练时tokenize train_ds, valid_ds, test_ds = build_pretrain_dataset_from_original(args.dataset) elif \u0026quot;-Idxmap\u0026quot; in args.dataset: # Megatron索引格式：预先tokenize config = GPTDatasetConfig( random_seed=args.seed, sequence_length=args.seq_length, blend=args.data_path, # 支持blend多数据集 split=args.split, # train:valid:test比例 eod_mask_loss=args.eod_mask_loss, ) train_ds, valid_ds, test_ds = BlendedMegatronDatasetBuilder( GPTDataset, train_val_test_num_samples, config ).build() return train_ds, valid_ds, test_ds 4.2 SFT数据处理 JSON格式定义 { \u0026quot;id\u0026quot;: \u0026quot;example_001\u0026quot;, \u0026quot;conversations\u0026quot;: [ {\u0026quot;from\u0026quot;: \u0026quot;human\u0026quot;, \u0026quot;value\u0026quot;: \u0026quot;你好，请介绍一下你自己\u0026quot;}, {\u0026quot;from\u0026quot;: \u0026quot;gpt\u0026quot;, \u0026quot;value\u0026quot;: \u0026quot;我是一个AI助手，可以帮助你...\u0026quot;} ] } 数据编码 (megatron_patch/data/json_sft.py) 核心逻辑： 1. 构建训练样本： python # System prompt + User + Assistant轮次对话 input_ids = tokenizer.apply_chat_template(conversations, tokenize=True)\n标签mask：\n# 只计算assistant回复的loss labels = input_ids.copy() labels[:user_input_end] = IGNORE_INDEX # mask掉user输入 Sequence Packing（可选）：\n# 将多个短样本pack到一个seq_len，提升效率 packed_input_ids = [sample1, sample2, sample3] # 用eos分隔 cu_seqlens = [0, len1, len1+len2, len1+len2+len3] # cumulative lengths 4.3 多模态数据处理 Qwen2-VL数据格式 (megatron_patch/data/dataset_helpers.py) class TaskEncoder(DefaultTaskEncoder): def encode_chatml(self, sample: ChatMLSample): # 1. 图像预处理 imgs = [get_visual_transform(img) for img in sample.imgs] flattened_imgs, image_thw_grids = self._flatten_visual_inputs(imgs) # 2. 构建conversation conversation = [ {\u0026quot;role\u0026quot;: \u0026quot;system\u0026quot;, \u0026quot;content\u0026quot;: \u0026quot;You are a helpful assistant.\u0026quot;}, {\u0026quot;role\u0026quot;: \u0026quot;user\u0026quot;, \u0026quot;content\u0026quot;: [ {\u0026quot;type\u0026quot;: \u0026quot;image\u0026quot;, \u0026quot;image\u0026quot;: \u0026quot;0\u0026quot;}, {\u0026quot;type\u0026quot;: \u0026quot;text\u0026quot;, \u0026quot;text\u0026quot;: \u0026quot;描述这张图片\u0026quot;} ]}, {\u0026quot;role\u0026quot;: \u0026quot;assistant\u0026quot;, \u0026quot;content\u0026quot;: \u0026quot;这是一张...\u0026quot;} ] # 3. Tokenize并插入image placeholders input_ids = tokenizer.apply_chat_template(conversation, tokenize=True) # 4. 扩展\u0026lt;|image_pad|\u0026gt; token # 一个image → (grid_t * grid_h * grid_w) // merge_size² 个tokens for image_token_idx in image_token_indices: size = image_thw_grids[i].prod() // merge_length # 替换单个\u0026lt;|image_pad|\u0026gt;为size个token final_input_ids[cur:cur+size] = image_token_id return ImageTaskSample( imgs=flattened_imgs, image_thw_grids=image_thw_grids, text=final_input_ids, target=labels, image_input_mask=image_mask, ) 关键设计： - 动态resolution：不同图像产生不同数量的visual tokens - Mask机制：image_input_mask标记哪些位置是图像token，用于注入visual features\n5. 模型权重转换 5.1 HuggingFace → Megatron转换 转换框架 (toolkits/model_checkpoints_convertor/llama/hf2mcore.py) 核心流程：\ndef convert_checkpoint_from_transformers_to_megatron(args): # 1. 加载HF模型 hf_model = AutoModelForCausalLM.from_pretrained(args.load_path) hf_state_dict = hf_model.state_dict() # 2. 初始化Megatron模型（指定TP/PP/EP） mg_model = model_provider() # 3. 权重映射和切分 mg_state_dict = {} for hf_name, hf_param in hf_state_dict.items(): # 命名空间转换 mg_name = convert_name_hf_to_mg(hf_name) # 张量切分（TP维度） if \u0026quot;self_attn.q_proj\u0026quot; in hf_name: # Split along num_heads dimension mg_param = split_tensor_for_tp(hf_param, args.target_tensor_model_parallel_size) elif \u0026quot;mlp.up_proj\u0026quot; in hf_name: # Column parallel: split along hidden_size mg_param = split_tensor_for_tp(hf_param, args.target_tensor_model_parallel_size) elif \u0026quot;mlp.down_proj\u0026quot; in hf_name: # Row parallel: split along input dimension mg_param = split_tensor_for_tp(hf_param, args.target_tensor_model_parallel_size, dim=1) mg_state_dict[mg_name] = mg_param # 4. 保存为Megatron checkpoint格式 for tp_rank in range(args.target_tensor_model_parallel_size): for pp_rank in range(args.target_pipeline_model_parallel_size): save_checkpoint( iteration, mg_model, None, None, checkpoint_name=get_checkpoint_name(load_dir, iteration, release, tp_rank, pp_rank) ) 命名空间映射示例 # HuggingFace → Megatron { \u0026quot;model.embed_tokens.weight\u0026quot;: \u0026quot;embedding.word_embeddings.weight\u0026quot;, \u0026quot;model.layers.0.self_attn.q_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.self_attention.linear_qkv.weight[0:hidden]\u0026quot;, \u0026quot;model.layers.0.self_attn.k_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.self_attention.linear_qkv.weight[hidden:2*hidden]\u0026quot;, \u0026quot;model.layers.0.self_attn.v_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.self_attention.linear_qkv.weight[2*hidden:3*hidden]\u0026quot;, \u0026quot;model.layers.0.self_attn.o_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.self_attention.linear_proj.weight\u0026quot;, \u0026quot;model.layers.0.mlp.gate_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.mlp.linear_fc1.weight[0:intermediate]\u0026quot;, \u0026quot;model.layers.0.mlp.up_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.mlp.linear_fc1.weight[intermediate:2*intermediate]\u0026quot;, \u0026quot;model.layers.0.mlp.down_proj.weight\u0026quot;: \u0026quot;decoder.layers.0.mlp.linear_fc2.weight\u0026quot;, } 5.2 分布式权重转换 超大模型转换挑战 对于DeepSeek-V3 671B这样的模型： - 内存限制：单机无法加载完整权重（~1.3TB FP16） - 转换时间：串行转换需要数小时\n解决方案 (toolkits/distributed_checkpoints_convertor/) # impl/convert.py class DistributedCheckpointConverter: def convert(self, src_path, dst_path, src_type, dst_type): \u0026quot;\u0026quot;\u0026quot; 并行转换checkpoint 支持格式： - HuggingFace safetensors - Megatron legacy (tp-pp-ep组织) - torch_dist (PyTorch Distributed format) \u0026quot;\u0026quot;\u0026quot; # 1. 构建权重映射表（不加载权重） weight_map = self.build_weight_mapping(src_type, dst_type) # 2. 分片加载和转换 for shard_id in parallel_range(num_shards): # 只加载当前shard需要的权重 shard_weights = load_shard(src_path, shard_id, weight_map) # 转换并写入目标shard converted = self.convert_shard(shard_weights, weight_map) save_shard(dst_path, shard_id, converted) 优化技术： - 分片映射：提前计算每个权重的源和目标位置，避免全量加载 - 并行转换：多进程/多节点并行处理不同shard - 流式转换：边加载边转换边写入，内存占用O(shard_size)而非O(total_size)\n5.3 MoE权重处理 Expert Parallel切分 # 64个experts，EP=8，每个rank持有8个experts def split_experts_for_ep(expert_weights, num_experts, ep_size): experts_per_rank = num_experts // ep_size for ep_rank in range(ep_size): start_expert = ep_rank * experts_per_rank end_expert = (ep_rank + 1) * experts_per_rank rank_experts = {} for expert_id in range(start_expert, end_expert): rank_experts[f\u0026quot;expert_{expert_id}\u0026quot;] = expert_weights[expert_id] save_checkpoint_for_ep_rank(ep_rank, rank_experts) 6. 训练流程剖析 6.1 分布式训练启动 典型启动脚本结构 # examples/qwen3/run_mcore_qwen.sh # 分布式配置 GPUS_PER_NODE=8 MASTER_ADDR=localhost MASTER_PORT=6000 NNODES=4 NODE_RANK=0 WORLD_SIZE=$(($GPUS_PER_NODE*$NNODES)) # 并行策略 TP=4 # Tensor Parallel PP=2 # Pipeline Parallel EP=1 # Expert Parallel（MoE模型） CP=1 # Context Parallel # 模型配置 SEQ_LENGTH=4096 HIDDEN_SIZE=4096 NUM_LAYERS=32 NUM_ATTENTION_HEADS=32 # 训练超参数 GLOBAL_BATCH_SIZE=256 MICRO_BATCH_SIZE=1 GRADIENT_ACCUMULATION_STEPS=$((GLOBAL_BATCH_SIZE / (MICRO_BATCH_SIZE * WORLD_SIZE / TP / PP))) # 启动命令 torchrun --nproc_per_node $GPUS_PER_NODE \\ --nnodes $NNODES \\ --node_rank $NODE_RANK \\ --master_addr $MASTER_ADDR \\ --master_port $MASTER_PORT \\ pretrain_qwen.py \\ --tensor-model-parallel-size $TP \\ --pipeline-model-parallel-size $PP \\ --expert-model-parallel-size $EP \\ --context-parallel-size $CP \\ --num-layers $NUM_LAYERS \\ --hidden-size $HIDDEN_SIZE \\ --num-attention-heads $NUM_ATTENTION_HEADS \\ --seq-length $SEQ_LENGTH \\ --max-position-embeddings $SEQ_LENGTH \\ --micro-batch-size $MICRO_BATCH_SIZE \\ --global-batch-size $GLOBAL_BATCH_SIZE \\ --train-iters 10000 \\ --lr 1e-4 \\ --min-lr 1e-5 \\ --lr-decay-style cosine \\ --weight-decay 0.1 \\ --adam-beta1 0.9 \\ --adam-beta2 0.95 \\ --clip-grad 1.0 \\ --init-method-std 0.01 \\ --attention-dropout 0.0 \\ --hidden-dropout 0.0 \\ --no-query-key-layer-scaling \\ --position-embedding-type rope \\ --rotary-base 1000000 \\ --rotary-percent 1.0 \\ --use-rotary-position-embeddings \\ --swiglu \\ --normalization RMSNorm \\ --untie-embeddings-and-output-weights \\ --disable-bias-linear \\ --use-flash-attn \\ --data-path /path/to/data_text_document \\ --split 949,50,1 \\ --tokenizer-type PretrainedFromHF \\ --tokenizer-name-or-path /path/to/tokenizer \\ --save-interval 500 \\ --eval-interval 100 \\ --log-interval 1 \\ --save /path/to/checkpoints \\ --load /path/to/checkpoints \\ --tensorboard-dir /path/to/tensorboard \\ --no-load-optim \\ --no-load-rng \\ --bf16 6.2 前向传播详解 Forward Step函数 def forward_step(data_iterator, model): \u0026quot;\u0026quot;\u0026quot; 单个microbatch的前向传播 \u0026quot;\u0026quot;\u0026quot; timers = get_timers() # 1. 获取数据 timers(\u0026#39;batch-generator\u0026#39;).start() tokens, labels, loss_mask, attention_mask, position_ids, _ = get_batch(data_iterator) timers(\u0026#39;batch-generator\u0026#39;).stop() # 2. 模型前向 output_tensor = model( input_ids=tokens, position_ids=position_ids, attention_mask=attention_mask, labels=labels ) # 3. 计算loss（只在最后一个PP stage） if mpu.is_pipeline_last_stage(): loss = compute_loss(output_tensor, labels, loss_mask) return loss, {\u0026#39;lm loss\u0026#39;: loss} else: return output_tensor Pipeline Parallel调度 Megatron使用的1F1B (One Forward One Backward) schedule:\n时间轴 → GPU0: F0 F1 F2 F3 B0 B1 B2 B3 GPU1: F0 F1 F2 F3 B0 B1 B2 B3 GPU2: F0 F1 F2 F3 B0 B1 B2 B3 GPU3: F0 F1 F2 F3 B0 B1 B2 B3 Bubble: 3个microbatch的时间（相比GPipe减少50%） 6.3 优化器与学习率调度 分布式优化器 # Megatron使用的DistributedOptimizer optimizer = get_megatron_optimizer(model) # 特点： # 1. 参数和梯度自动处理TP/PP分布 # 2. 梯度all-reduce跨DP组 # 3. 支持optimizer state的DP分片（ZeRO-1） 学习率调度 def get_learning_rate_scheduler(optimizer): \u0026quot;\u0026quot;\u0026quot; 常用：Cosine Decay with Warmup \u0026quot;\u0026quot;\u0026quot; if args.lr_decay_style == \u0026#39;cosine\u0026#39;: lr_scheduler = CosineAnnealingScheduler( optimizer, max_lr=args.lr, min_lr=args.min_lr, warmup_steps=args.lr_warmup_iters, decay_steps=args.lr_decay_iters, ) return lr_scheduler 6.4 Checkpoint管理 Checkpoint结构 /path/to/checkpoints/ ├── latest_checkpointed_iteration.txt # 记录最新iteration ├── iter_0001000/ │ ├── mp_rank_00/ # TP rank 0 │ │ ├── model_optim_rng.pt # 模型+优化器+RNG状态 │ │ └── model_rng.pt # 只有模型（no-load-optim时） │ ├── mp_rank_01/ # TP rank 1 │ └── ... └── iter_0002000/ 保存和加载 # 保存 save_checkpoint(iteration, model, optimizer, opt_param_scheduler) # 生成：/save/iter_0001000/mp_rank_XX_YY/model_optim_rng.pt # XX=tp*pp+pp_rank, YY=000 # 加载 load_checkpoint(model, optimizer, opt_param_scheduler) # 自动从latest_checkpointed_iteration.txt读取iteration # 根据当前rank的tp/pp位置加载对应shard 7. 强化学习集成 7.1 ChatLearn框架 GRPO (Group Relative Policy Optimization) 核心思想：对同一prompt的多个responses比较，学习相对偏好\n# examples/qwen3/chatlearn_entrypoint.py class ChatlearnLauncher: def launch(self): # 定义4个模型的角色 models = { \u0026quot;policy\u0026quot;: PolicyModel(), # 待训练的Actor \u0026quot;reference\u0026quot;: ReferenceModel(), # 冻结的参考模型 \u0026quot;reward\u0026quot;: RewardModel(), # 打分模型 \u0026quot;value\u0026quot;: ValueModel(), # 价值估计（PPO用） } # 定义计算流程 def rlhf_workflow(env, policy, reference, reward, value): # 1. Rollout：生成responses prompts = env.get_prompts() responses = policy.generate(prompts, num_samples=4) # 每个prompt生成4个 # 2. Reward：计算分数 scores = reward.score(prompts, responses) # 3. Reference log_probs：KL正则化 ref_log_probs = reference.forward(prompts, responses) # 4. 计算advantages（GRPO相对优势） group_mean_score = scores.reshape(-1, 4).mean(dim=1) advantages = scores - group_mean_score.repeat_interleave(4) # 5. Policy更新 policy_loss = compute_policy_loss( policy.forward(prompts, responses), ref_log_probs, advantages ) policy.backward(policy_loss) # 启动ChatLearn引擎 engine = ChatLearnEngine(models, rlhf_workflow) engine.train(num_episodes=1000) 关键特性： - 资源调度：自动管理4个模型在多卡上的分布 - Context Parallel：长上下文场景的显存优化 - Sequence Packing：提升rollout效率\n7.2 Verl框架 PPO训练流程 # examples/qwen3/verl_entrypoint.py @ray.remote class TaskRunner: def run(self, config): # 1. 初始化模型workers actor_rollout_worker = RayWorkerGroup( cls=MegatronPPOActor, config=config.actor_rollout_ref, ) critic_worker = RayWorkerGroup( cls=MegatronCritic, config=config.critic, ) reward_model = load_reward_model(config.reward_model) # 2. 创建trainer trainer = RayPPOTrainer( config=config, actor_rollout_worker=actor_rollout_worker, critic_worker=critic_worker, reward_model=reward_model, ) trainer.fit() Trainer核心循环 # verl_patch/trainer/ray_trainer.py class RayPPOTrainer: def fit(self): for epoch in range(total_epochs): for batch in train_dataloader: # 1. Rollout阶段 with torch.no_grad(): rollout_output = self.actor_rollout.generate_sequences(batch) # rollout_output: {sequences, log_probs, values} # 2. Reward计算 rewards = self.reward_model.compute_rewards( rollout_output[\u0026#39;sequences\u0026#39;] ) # 3. Advantage计算（GAE） advantages, returns = compute_gae( rewards, rollout_output[\u0026#39;values\u0026#39;], gamma=0.99, lambda_=0.95 ) # 4. PPO更新（多个epochs） for ppo_epoch in range(self.config.ppo_epochs): # Actor更新 actor_loss = self.compute_actor_loss( rollout_output, advantages, clip_ratio=0.2 ) self.actor.backward(actor_loss) # Critic更新 critic_loss = self.compute_critic_loss( rollout_output[\u0026#39;sequences\u0026#39;], returns ) self.critic.backward(critic_loss) Verl vs ChatLearn对比： | 维度 | Verl | ChatLearn | |——|——|———–| | 算法 | PPO (clip, value function) | GRPO (group relative) | | 模型数 | 2个（Actor+Critic） | 4个（Policy+Ref+Reward+Value） | | 实现 | Ray + PyTorch | 自研调度引擎 | | 适用 | 标准PPO场景 | 大规模RLHF，更好的资源利用 |\n8. 高级优化技术 8.1 Flash Attention集成 使用方式 --use-flash-attn # 启用FlashAttention-2 --use-te-flash-attn # 使用Transformer Engine的FlashAttention实现 实现 (megatron_patch/model/llama3/layer_specs.py):\nsubmodules=SelfAttentionSubmodules( core_attention=TEDotProductAttention, # TE backend自动使用FA # or core_attention=DotProductAttention, # Megatron实现，支持--use-flash-attn ) 性能提升： - 减少HBM访问：O(N²) → O(N) - 支持更长上下文：4K → 32K+ - 训练提速：1.5-2x（取决于seq_length）\n8.2 FP8训练 Transformer Engine FP8 --transformer-impl transformer_engine \\ --fp8-format hybrid # E4M3 (forward) + E5M2 (backward) \\ --fp8-amax-history-len 1024 \\ --fp8-amax-compute-algo max 工作原理： 1. 动态scaling：跟踪activation的amax（最大绝对值） 2. 混合精度： - Forward: FP8-E4M3 (更大动态范围) - Backward: FP8-E5M2 (更高精度) 3. Tensor Core加速：H100 FP8吞吐是BF16的2x\n适用模型： - LLaMA-2/3 - Qwen2/2.5/3 - Mixtral\n8.3 Sequence Packing 原理 将多个短样本pack到一个sequence，减少padding浪费：\n不使用packing: [sample1, pad, pad] → 33% 利用率 [sample2, pad, pad] → 33% 利用率 [sample3, pad, pad] → 33% 利用率 使用packing: [sample1, sample2, sample3] → 100% 利用率 实现要点 # 需要cu_seqlens指示每个样本的边界 cu_seqlens = torch.tensor([0, len1, len1+len2, len1+len2+len3]) # FlashAttention支持variable-length sequences attn_output = flash_attn_varlen_func( q, k, v, cu_seqlens_q=cu_seqlens, cu_seqlens_k=cu_seqlens, max_seqlen_q=max_len, max_seqlen_k=max_len, ) 效果： - SFT训练提速：1.5-3x（取决于样本长度分布） - 适用场景：样本长度差异大的SFT数据集\n8.4 Context Parallel 解决问题 超长上下文（如128K）时，attention矩阵过大导致OOM： - Attention memory: O(batch * num_heads * seq_len²) - 128K context → ~64GB activation memory (batch=1, bf16)\n实现 --context-parallel-size 4 # 将seq_len切分到4个GPU --sequence-parallel # 必须同时启用SP 机制：\n# 将sequence维度切分 seq_len_per_rank = seq_len // cp_size # Ring Attention: 每个rank持有部分Q和完整KV # 通过ring communication访问其他rank的KV for cp_rank in range(cp_size): local_q = q_full[cp_rank * seq_chunk : (cp_rank+1) * seq_chunk] for step in range(cp_size): # 当前持有的KV shard kv_rank = (cp_rank + step) % cp_size local_kv = kv_shards[kv_rank] # 计算local attention local_attn = torch.matmul(local_q, local_kv.transpose(-1, -2)) # 异步发送KV到下一个rank，接收前一个rank的KV send_recv_kv(local_kv, next_rank, prev_rank) 效果： - 支持上下文：32K → 128K+ - 通信开销：与CP size成正比\n8.5 Optimizer Offloading CPU Offloading --use-distributed-optimizer # 启用分布式优化器 --overlap-grad-reduce # 梯度reduce与backward overlap --offload-optimizer # 优化器状态offload到CPU 原理：\n# 优化器状态（Adam: momentum + variance）存储在CPU optimizer_state = { \u0026quot;exp_avg\u0026quot;: tensor.to(\u0026quot;cpu\u0026quot;), # momentum \u0026quot;exp_avg_sq\u0026quot;: tensor.to(\u0026quot;cpu\u0026quot;), # variance } # Backward时梯度累积在GPU grads_gpu = accumulate_gradients() # Optimizer step前将需要的state传回GPU for param in model.parameters(): state = optimizer_state[param].to(\u0026quot;gpu\u0026quot;) param.data = adam_update(param.data, grad, state) optimizer_state[param] = state.to(\u0026quot;cpu\u0026quot;) 适用场景： - 显存受限，无法用大batch size - 模型参数量极大（如70B+）\nTrade-off： - 节省显存：30-50% - 增加训练时间：10-15%（CPU-GPU传输开销）\n9. 工具链生态 9.1 自动配置器 功能 (toolkits/auto_configurator/) python report_auto_config.py \\ --model-size 70B \\ --num-gpus 64 \\ --gpu-memory 80GB \\ --seq-length 4096 \\ --global-batch-size 256 输出：\n推荐配置: - Tensor Parallel: 4 - Pipeline Parallel: 2 - Data Parallel: 8 - Micro Batch Size: 1 - Gradient Accumulation Steps: 32 预估性能: - 训练吞吐: 1250 tokens/sec/GPU - 显存占用: 72GB/GPU - MFU (Model FLOPs Utilization): 42% 计算逻辑： 1. 显存估算： python model_memory = num_parameters * bytes_per_param * (1 + optimizer_overhead) activation_memory = estimate_activation_memory(seq_len, hidden_size, ...) total_memory_per_gpu = (model_memory + activation_memory) / (tp * pp * dp)\n通信开销估算： TP：all-reduce，带宽敏感 PP：point-to-point，延迟敏感 DP：all-reduce，梯度大小 最优策略搜索： 遍历合法的(TP, PP, DP)组合 选择显存适配且通信开销最小的配置 9.2 数据预处理工具集 预训练数据 # 清洗 python clean_raw_text.py \\ --input raw_corpus.txt \\ --output cleaned_corpus.txt \\ --remove-duplicate-lines \\ --min-length 50 # Tokenize python preprocess_data_megatron.py \\ --input cleaned_corpus.txt \\ --output-prefix my_data \\ --tokenizer-type PretrainedFromHF \\ --tokenizer-name-or-path Qwen/Qwen-7B \\ --workers 64 \\ --append-eod # 输出: my_data_text_document.{bin,idx} SFT数据 python build_idxmap_sft_dataset.py \\ --input sft_data.jsonl \\ --output-prefix sft_indexed \\ --tokenizer-name-or-path Qwen/Qwen-7B \\ --seq-length 2048 \\ --apply-chat-template \\ --mask-instruction 多模态数据 # LLaVA format → WebDataset python convert_llava_pretrain_to_wds.py \\ --input llava_pretrain.json \\ --image-folder /path/to/images \\ --output wds_shards/shard-{000000..000099}.tar \\ --max-shard-size 1GB 9.3 评估集成 LM Evaluation Harness python evaluate_megatron_model.py \\ --model-path /path/to/checkpoint \\ --tasks mmlu,ceval,humaneval \\ --num-fewshot 5 \\ --tensor-model-parallel-size 4 \\ --pipeline-model-parallel-size 2 \\ --output-path eval_results.json 支持的评估任务： - MMLU: 多学科知识 - C-Eval: 中文综合能力 - HumanEval: 代码生成 - GSM8K: 数学推理 - TruthfulQA: 真实性\n10. 最佳实践总结 10.1 并行策略选择指南 模型规模 vs 并行策略 模型大小 推荐策略 示例配置 7B TP=1, PP=1, DP=N 单卡可训，DP多卡提速 13B TP=2, PP=1, DP=N/2 2卡TP，其余DP 70B TP=4, PP=2, DP=N/8 8卡模型并行 175B TP=8, PP=4, DP=N/32 32卡模型并行 MoE-8x7B TP=1, PP=1, EP=2, DP=N/2 EP切分专家 DeepSeek-V3 671B TP=1, PP=8, EP=8, DP=N/64 避免TP（MLA不支持） 通用原则 优先DP：通信开销最小，扩展性最好 TP适度：TP\u0026gt;4时通信成为瓶颈（跨节点尤甚） PP for large models：TP装不下时使用PP EP for MoE：专家数≥8时考虑EP CP for long context：seq_len\u0026gt;32K时考虑CP 10.2 训练稳定性技巧 常见不稳定原因及对策 # 1. Gradient Explosion --clip-grad 1.0 # 梯度裁剪 --init-method-std 0.01 # 更小的初始化标准差 # 2. Loss Spike --lr-warmup-iters 2000 # 充足的warmup --lr-decay-style cosine # 平滑的LR decay --attention-dropout 0.1 # 适度dropout --hidden-dropout 0.1 # 3. NaN/Inf --check-for-nan-in-loss-and-grad # 自动检测 --bf16 # BF16比FP16更稳定（动态范围大） --initial-loss-scale 4096 # FP16时调整loss scale # 4. MoE Load Imbalance --moe-aux-loss-coeff 0.01 # Auxiliary loss权重 --moe-z-loss-coeff 0.001 # Z-loss稳定router Checkpoint最佳实践 # 频繁保存（防止训练中断） --save-interval 500 # 但不要每次都保存optimizer state（节省空间） --no-save-optim # 每隔N次保存一次完整checkpoint # 加载时可选择性恢复 --load /path/to/checkpoint --no-load-optim # 从pretrain checkpoint开始SFT时 --no-load-rng # 改变batch size时 10.3 显存优化策略 从宽到紧的优化序列 # Level 0: Baseline micro_batch_size = 4 # Level 1: 激活检查点（Activation Checkpointing） --recompute-activations # 减少50% activation memory, +20% time # Level 2: Sequence Parallel --sequence-parallel # 与TP配合，分摊LayerNorm/Dropout activation # Level 3: 更小的micro batch micro_batch_size = 1 # 配合更多gradient accumulation # Level 4: CPU Offloading --use-distributed-optimizer --offload-optimizer # 减少30-50% memory, +10-15% time # Level 5: 混合策略 --recompute-granularity selective # 只checkpoint部分层 --zero-stage 1 # ZeRO stage 1: optimizer state sharding 10.4 性能调优checklist 吞吐量优化 # 1. Flash Attention (必开) --use-flash-attn # 2. Transformer Engine (Ampere/Hopper) --transformer-impl transformer_engine # 3. Overlap通信与计算 --overlap-grad-reduce # Backward时overlap --overlap-param-gather # ZeRO时overlap # 4. 编译优化 --use-torch-compile # PyTorch 2.0+ # 5. Fused Kernels --bias-dropout-fusion --bias-swiglu-fusion # LLaMA SwiGLU --apply-layernorm-1p # Fused LayerNorm # 6. 数据加载 --num-workers 4 # DataLoader多进程 --dataloader-type cyclic # 预加载下一个epoch 10.5 多模态训练注意事项 Qwen2-VL训练经验 两阶段训练：\nStage 1: Pretrain (vision encoder frozen) - 只训练projector和LLM - 大batch size (512+) - 学习率: 1e-3 Stage 2: SFT (vision encoder unfrozen) - 端到端训练 - 小batch size (64-128) - 小学习率: 1e-5 (vision), 1e-4 (LLM) 动态分辨率处理：\n# 不同图像产生不同数量的tokens # 需要dynamic padding和masking image_tokens_per_sample = [(t*h*w)//merge_size² for (t,h,w) in thw_grids] 显存管理：\n# Vision encoder通常很大（ViT-H: 1B params） --freeze-vision-encoder # Stage 1 --recompute-activations # Vision部分的activation重计算 10.6 从零开始训练新模型的流程 Step-by-Step Workflow # 1. 准备HuggingFace模型权重 # (如果从头预训练，可跳过此步) # 2. 转换为Megatron格式 bash toolkits/model_checkpoints_convertor/your_model/hf2mcore_convertor.sh # 3. 准备训练数据 # 预训练 bash toolkits/pretrain_data_preprocessing/run_make_pretraining_dataset_megatron.sh # SFT bash toolkits/sft_data_preprocessing/run_build_idxmap_sft_dataset.sh # 4. 自动配置 python toolkits/auto_configurator/report_auto_config.py \\ --model-size 7B --num-gpus 8 --gpu-memory 80GB # 5. 修改训练脚本 cp examples/llama2/pretrain_mcore_llama.py examples/your_model/ cp examples/llama2/run_pretrain_mcore_llama.sh examples/your_model/ # 修改模型配置和并行参数 # 6. 启动训练 bash examples/your_model/run_pretrain_mcore_llama.sh # 7. 监控训练 tensorboard --logdir /path/to/tensorboard_dir # 8. 评估 bash examples/your_model/run_evaluate_mcore.sh # 9. 转换回HuggingFace格式 bash toolkits/model_checkpoints_convertor/your_model/mcore2hf_convertor.sh # 10. 部署推理 python examples/your_model/generate_text.py 10.7 常见问题排查 OOM (Out of Memory) # 检查项： 1. micro_batch_size是否过大？ → 减小到1，增加gradient accumulation 2. 是否启用了activation checkpointing？ → --recompute-activations 3. TP/PP是否合理？ → 增大TP以分摊模型参数 4. Sequence length是否过长？ → 考虑--context-parallel-size 5. 是否有memory leak？ → torch.cuda.empty_cache() 定期清理 Loss不收敛 # 检查项： 1. Learning rate是否合理？ → 典型值：7B用1e-4, 70B用6e-5 → Warmup是否充足？建议≥2000 steps 2. 数据质量？ → 检查是否有重复/错误样本 → 验证tokenization正确性 3. 梯度是否异常？ → --log-grad-norm 查看梯度范数 → 是否出现NaN/Inf？ 4. 模型配置是否正确？ → 检查vocab_size, hidden_size等是否匹配HF config 训练速度慢 # 排查项： 1. GPU利用率低？ → nvidia-smi查看GPU util → 如果\u0026lt;80%，检查数据加载是否成为瓶颈 2. 通信时间长？ → nsys profile查看通信占比 → TP过大或跨节点TP都会导致通信瓶颈 3. 是否启用了所有优化？ → FlashAttention, Transformer Engine, Overlap等 4. Micro batch size过小？ → 增大micro_batch以提升GPU利用率 总结与展望 核心收获 通过对Pai-Megatron-Patch的深入学习，我掌握了：\n架构设计： 非侵入式patch设计的优雅性 依赖注入和函数式编程在大型系统中的应用 如何设计可扩展的训练框架 分布式训练： 3D并行（TP/PP/DP）的原理和实践 Expert Parallel和Context Parallel的特殊场景 通信与计算的overlap优化 模型实现： Transformer的Megatron-Core实现 MLA等先进架构的工程化 ModuleSpec延迟实例化模式 工程实践： 大规模模型的权重转换策略 多模态数据的高效处理 RLHF的工程化实现（ChatLearn/Verl） 优化技巧： FlashAttention, FP8等硬件加速技术 Sequence Packing, Activation Checkpointing等显存优化 训练稳定性和性能调优 项目的价值与局限 价值： - 大幅降低Megatron使用门槛 - 提供了20+主流模型的最佳实践 - 开源社区的宝贵资源\n局限： - 依赖特定Megatron版本，升级需要适配 - 部分模型实现未支持所有Megatron特性 - 文档相对代码更新有滞后\n未来发展方向 更多模型支持： Gemma, Command-R, Phi等新模型 更多多模态模型（音频、3D） 性能优化： 更好的kernel融合 支持新硬件（Intel GPU, AMD GPU） 易用性提升： 自动化配置和调优 更友好的错误提示 Web UI管理界面 生态集成： 与vLLM等推理框架的无缝衔接 支持更多RLHF算法（DPO, RRHF等） 联邦学习支持 附录 A. 关键概念速查 术语 全称 解释 TP Tensor Parallelism 张量并行：将单层参数切分到多GPU PP Pipeline Parallelism 流水线并行：将不同层分配到不同GPU DP Data Parallelism 数据并行：每个GPU持有完整模型，处理不同数据 EP Expert Parallelism 专家并行：MoE模型的专家分配到不同GPU CP Context Parallelism 上下文并行：超长序列切分到多GPU SP Sequence Parallelism 序列并行：LayerNorm等操作的激活分摊 ZeRO Zero Redundancy Optimizer 优化器状态分片，减少显存冗余 MLA Multi-head Latent Attention DeepSeek的KV cache压缩技术 MoE Mixture of Experts 稀疏激活的专家混合模型 RoPE Rotary Position Embedding 旋转位置编码 GQA Grouped Query Attention 分组查询注意力，减少KV cache SwiGLU Swish-Gated Linear Unit LLaMA使用的激活函数 B. 常用命令速查 # 启动分布式训练（单节点） torchrun --nproc_per_node 8 pretrain.py [args] # 启动分布式训练（多节点） torchrun --nnodes 4 --node_rank $RANK \\ --master_addr $MASTER_ADDR \\ --master_port $MASTER_PORT \\ --nproc_per_node 8 \\ pretrain.py [args] # 权重转换 HF→Megatron bash hf2mcore_convertor.sh # 权重转换 Megatron→HF bash mcore2hf_convertor.sh # 数据预处理 python preprocess_data_megatron.py \\ --input data.txt \\ --output-prefix data_tokenized \\ --tokenizer-type PretrainedFromHF \\ --tokenizer-name-or-path /path/to/tokenizer # 评估 bash run_evaluate_mcore.sh # 推理生成 bash run_text_generation.sh C. 参考资源 官方文档： Pai-Megatron-Patch: https://github.com/alibaba/Pai-Megatron-Patch Megatron-LM: https://github.com/NVIDIA/Megatron-LM Megatron-Core: https://github.com/NVIDIA/Megatron-Core 技术报告（见README）： Megatron的多模态训练加速 MoE大模型最佳实践 GSPO强化学习实践 相关论文： Megatron-LM: https://arxiv.org/abs/1909.08053 DeepSeek-V2 (MLA): https://arxiv.org/abs/2405.04434 FlashAttention: https://arxiv.org/abs/2205.14135 Mixtral MoE: https://arxiv.org/abs/2401.04088 文档版本: v1.0\n最后更新: 2025-01-27\n作者: AI Assistant based on codebase analysis\n本文档持续更新中，欢迎反馈和补充！\n","date":"2025-10-27T00:00:00Z","permalink":"/%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/","title":"快速入门"},{"content":"训练指标完整指南 目录 Actor 指标 (策略网络) Critic 指标 (价值网络) 奖励与优势指标 序列长度指标 Episode 指标 时间性能指标 系统性能指标 训练控制指标 World Model 指标 验证指标 指标监控最佳实践 1. Actor 指标 (策略网络) 1.1 actor/pg_loss - 策略梯度损失 含义：PPO 的策略梯度损失，衡量策略改进的幅度\n计算公式：\nratio = exp(log_prob - old_log_prob) pg_losses = -advantages * ratio # 或使用 clipped ratio pg_loss = mean(pg_losses) 数学解释： - 本质上是带权重的对数似然损失 - 权重 = advantages（优势函数） - 目标：增加高优势动作的概率，降低低优势动作的概率\n正常范围： - 初期：-0.01 到 0.01 - 稳定后：-0.001 到 0.001 - 接近 0 时表示策略更新幅度小\n趋势分析：\n趋势 含义 原因 建议 负值且绝对值大 策略正在积极改进 发现了更好的策略 正常，继续训练 正值且较大 策略在变差 clip 机制生效或优势估计不准 检查优势计算，降低学习率 接近 0 策略趋于稳定 收敛或陷入局部最优 检查奖励，考虑调整探索 剧烈波动 训练不稳定 学习率过高或批次大小过小 降低学习率，增大批次 示例：\nstep:0 - actor/pg_loss:-0.005 # 初期，策略在改进 step:50 - actor/pg_loss:-0.002 # 改进幅度变小 step:100 - actor/pg_loss:0.000 # 接近收敛 调试技巧：\n# 如果 pg_loss 异常，检查： print(f\u0026quot;Advantages mean: {advantages.mean()}\u0026quot;) # 应该接近 0 print(f\u0026quot;Advantages std: {advantages.std()}\u0026quot;) # 应该为 1（经过归一化） print(f\u0026quot;Log prob diff: {(log_prob - old_log_prob).mean()}\u0026quot;) # 应该很小 1.2 actor/pg_clipfrac - 策略梯度裁剪比例 含义：被 PPO clip 机制裁剪的样本比例\n计算公式：\nratio = exp(log_prob - old_log_prob) clipped_ratio = clip(ratio, 1-clip_range, 1+clip_range) pg_clipfrac = mean(clipped_ratio != ratio) 直观理解： - 衡量有多少样本的策略更新被限制住了 - 防止策略更新过大，保持训练稳定性\n正常范围： - 理想值：0.05 - 0.30 (5% - 30%) - \u0026lt; 0.05：策略更新保守，可能收敛慢 - \u0026gt; 0.50：策略更新激进，可能不稳定\n趋势分析：\n值域 含义 影响 建议 0 - 0.05 几乎没有裁剪 策略更新空间大，可能学习慢 可以增大学习率或 clip_ratio 0.05 - 0.30 适度裁剪 训练稳定且高效 继续保持 ✅ 0.30 - 0.50 较多裁剪 策略更新受限，进展可能慢 考虑减小学习率或增大 clip_ratio \u0026gt; 0.50 大量裁剪 策略更新被严重限制 减小学习率或检查奖励信号 示例：\nstep:0 - actor/pg_clipfrac:0.000 # 初期无裁剪 step:10 - actor/pg_clipfrac:0.120 # 适度裁剪 ✅ step:50 - actor/pg_clipfrac:0.450 # 过多裁剪 ⚠️ 与 clip_ratio 的关系：\n# 配置文件中 actor_rollout_ref.actor.clip_ratio: 0.2 # 默认值 # 如果 pg_clipfrac \u0026gt; 0.5，考虑增大到 0.3 actor_rollout_ref.actor.clip_ratio: 0.3 1.3 actor/ppo_kl - PPO KL 散度 含义：新旧策略之间的 KL 散度，衡量策略变化幅度\n计算公式：\nppo_kl = mean(old_log_prob - log_prob) 数学背景： - KL 散度衡量两个分布的差异 - 这里是近似 KL（基于重要性采样） - 真实 KL = E[log(π_old) - log(π_new)]\n正常范围： - 理想值：0.001 - 0.01 - \u0026lt; 0.001：策略几乎不变 - \u0026gt; 0.05：策略变化过大\n趋势分析：\n值域 含义 影响 建议 \u0026lt; 0.001 策略几乎不变 学习速度慢 增大学习率 0.001 - 0.01 适度变化 平衡探索与稳定性 ✅ 保持 0.01 - 0.05 较大变化 可能影响稳定性 监控性能 \u0026gt; 0.05 剧烈变化 训练不稳定 降低学习率 示例场景：\n场景 1：健康训练\nstep:0 - actor/ppo_kl:0.003 - actor/pg_clipfrac:0.15 step:10 - actor/ppo_kl:0.004 - actor/pg_clipfrac:0.20 step:20 - actor/ppo_kl:0.002 - actor/pg_clipfrac:0.10 # 策略稳步改进 ✅ 场景 2：学习率过高\nstep:0 - actor/ppo_kl:0.050 - actor/pg_clipfrac:0.80 step:1 - actor/ppo_kl:0.045 - actor/pg_clipfrac:0.75 # KL 过大，大量裁剪，需要降低学习率 ⚠️ 场景 3：收敛\nstep:80 - actor/ppo_kl:0.0001 - actor/pg_clipfrac:0.01 step:90 - actor/ppo_kl:0.0001 - actor/pg_clipfrac:0.01 # 策略稳定，可能已收敛 ✅ 1.4 actor/pg_clipfrac_lower - 负优势裁剪比例 含义：Dual-clip PPO 中，负优势样本被下界裁剪的比例\n计算公式：\n# 对于 advantage \u0026lt; 0 的样本 lower_clip = -advantages * clip_ratio_c # 通常 clip_ratio_c=3.0 pg_clipfrac_lower = mean((advantages \u0026lt; 0) \u0026amp; (lower_clip \u0026gt; clipped_loss)) 背景： - 来自论文：https://arxiv.org/pdf/1912.09729 - 防止负优势样本（不好的动作）被过度惩罚 - 增加训练稳定性\n正常范围： - 通常 \u0026lt; 0.1 - 较少触发（因为 clip_ratio_c=3.0 比较大）\n示例：\nstep:0 - actor/pg_clipfrac_lower:0.02 # 很少触发 1.5 actor/entropy_loss - 熵损失 含义：策略输出分布的熵，衡量策略的随机性\n计算公式：\nentropy = -sum(p * log(p)) # 对所有 token entropy_loss = mean(entropy) 直观理解： - 高熵 = 分布平坦 = 输出随机 = 探索多 - 低熵 = 分布尖锐 = 输出确定 = 利用多\n正常范围： - 初期：0.5 - 2.0（取决于任务） - 训练中：逐渐下降 - 最终：0.1 - 0.5\n趋势分析：\n阶段 典型值 含义 期望行为 初始化 5.0 - 8.0 模型几乎随机 快速下降 早期训练 0.5 - 2.0 开始学习但保持探索 缓慢下降 中期训练 0.3 - 0.8 平衡探索与利用 稳定或小幅下降 收敛阶段 0.1 - 0.3 策略较确定 稳定 过拟合 \u0026lt; 0.05 几乎确定性输出 ⚠️ 可能需要正则化 示例场景：\n场景 1：正常训练（对话生成）\nstep:0 - actor/entropy_loss:0.800 step:50 - actor/entropy_loss:0.650 step:100 - actor/entropy_loss:0.500 step:200 - actor/entropy_loss:0.400 # 保持一定多样性 ✅ 场景 2：过早收敛（数学推理）\nstep:0 - actor/entropy_loss:0.600 step:10 - actor/entropy_loss:0.050 # 过快下降 step:20 - actor/entropy_loss:0.030 # 可能陷入局部最优，考虑增大 entropy_coeff ⚠️ 场景 3：探索不足\nstep:0 - actor/entropy_loss:0.500 step:100 - actor/entropy_loss:0.480 # 几乎不变 # 策略没有学到东西，检查奖励信号 ⚠️ 与 entropy_coeff 的关系：\n# 如果希望增加探索 actor_rollout_ref.actor.entropy_coeff: 0.01 # 从 0.001 增大 # 如果希望确定性输出 actor_rollout_ref.actor.entropy_coeff: 0.0 # 关闭熵正则化 1.6 actor/kl_loss - KL 散度损失 含义：当前策略与参考策略的 KL 散度（用于 GRPO 等算法）\n计算公式：\nkld = kl_penalty(log_prob, ref_log_prob, kl_penalty_type) kl_loss = mean(kld) 使用场景： - GRPO 算法中使用（替代 critic） - 防止策略偏离参考策略太远 - 类似于信任域约束\n正常范围： - 0.001 - 0.1\n示例：\n# GRPO 训练 step:0 - actor/kl_loss:0.005 - actor/kl_coef:0.001 1.7 actor/grad_norm - 梯度范数 含义：参数梯度的 L2 范数，衡量梯度大小\n计算公式：\ngrad_norm = sqrt(sum(grad_i^2 for all parameters)) 正常范围： - 通常：0.1 - 10.0 - 经过梯度裁剪后：\u0026lt; grad_clip 值（通常为 1.0）\n趋势分析：\n值域 含义 影响 建议 \u0026lt; 0.01 梯度消失 学习停滞 增大学习率，检查奖励 0.01 - 1.0 健康梯度 训练正常 ✅ 保持 1.0 - 10.0 较大梯度 可能被裁剪 监控训练稳定性 \u0026gt; 10.0 梯度爆炸 训练不稳定 检查数据，降低学习率 NaN/Inf 数值错误 训练崩溃 检查学习率，数据预处理 示例：\nstep:0 - actor/grad_norm:1.523 step:10 - actor/grad_norm:0.845 step:20 - actor/grad_norm:1.000 # 触发梯度裁剪 调试：\n# 如果频繁出现 grad_norm = grad_clip # 说明梯度经常被裁剪，考虑： 1. 降低学习率 2. 增大 grad_clip (小心) 3. 检查是否有异常数据 1.8 actor/lr - 学习率 含义：当前的学习率（可能带有预热和衰减）\n示例：\nstep:0 - actor/lr:1e-07 # warmup 阶段 step:100 - actor/lr:1e-06 # 达到峰值 step:1000 - actor/lr:5e-07 # cosine 衰减 典型配置：\nactor_rollout_ref.actor.optim: lr: 1e-6 # 峰值学习率 lr_warmup_steps_ratio: 0.1 # 10% 步数用于预热 warmup_style: constant # 或 cosine 2. Critic 指标 (价值网络) 2.1 critic/vf_loss - 价值函数损失 含义：价值网络预测的准确度，使用 clipped MSE loss\n计算公式：\nvpred_clipped = clip(vpred, values - cliprange_value, values + cliprange_value) vf_loss1 = (vpred - returns)^2 vf_loss2 = (vpred_clipped - returns)^2 vf_loss = mean(max(vf_loss1, vf_loss2)) 正常范围： - 初期：10.0 - 100.0（取决于奖励尺度） - 训练后：0.1 - 10.0 - 收敛：\u0026lt; 1.0\n趋势分析：\n趋势 含义 影响 建议 快速下降 Critic 在学习 优势估计改善 ✅ 继续训练 稳定在低值 Critic 准确 Actor 可以信赖优势 ✅ 正常 不下降 Critic 学不到 Actor 训练受影响 增大 critic 学习率 先降后升 奖励分布变化 Actor 策略变化导致 正常现象 剧烈波动 训练不稳定 优势估计不可靠 降低学习率 示例：\nstep:0 - critic/vf_loss:14.947 step:10 - critic/vf_loss:8.352 step:50 - critic/vf_loss:2.148 step:100 - critic/vf_loss:0.832 # 收敛良好 ✅ 2.2 critic/vf_clipfrac - 价值函数裁剪比例 含义：被 clip 机制裁剪的价值预测比例\n计算公式：\nclipped = (vpred_clipped != vpred) vf_clipfrac = mean(clipped) 正常范围： - 0.0 - 0.5 - 通常 \u0026lt; 0.2\n含义： - 过高（\u0026gt;0.5）：价值函数更新受限 - 过低（\u0026lt;0.05）：裁剪机制未起作用\n示例：\nstep:0 - critic/vf_clipfrac:0.000 step:50 - critic/vf_clipfrac:0.384 # 较多裁剪 2.3 critic/vpred_mean - 价值预测均值 含义：Critic 预测的平均价值\n作用： - 监控价值尺度是否合理 - 应该与实际 returns 接近\n示例：\nstep:0 - critic/vpred_mean:-2.056 - critic/returns/mean:0.003 # 初期预测不准 step:100 - critic/vpred_mean:2.340 - critic/returns/mean:2.450 # 预测接近真实值 ✅ 2.4 critic/vf_explained_var - 解释方差 含义：价值函数解释 returns 方差的比例，衡量拟合质量\n计算公式：\nvf_explained_var = 1 - Var(returns - values) / Var(returns) 数学解释： - 类似于 R² 分数 - 1.0 = 完美拟合 - 0.0 = 预测不比常数好 - \u0026lt;0 = 预测比常数还差\n正常范围： - 初期：-1.0 到 0.2 - 训练中：0.2 到 0.8 - 良好收敛：\u0026gt; 0.7\n趋势分析：\n值域 质量 含义 建议 \u0026lt; 0 极差 Critic 没有学到 增大学习率，检查数据 0 - 0.3 差 Critic 刚开始学 继续训练 0.3 - 0.7 中等 Critic 基本可用 继续优化 0.7 - 0.9 良好 Critic 准确 ✅ 很好 \u0026gt; 0.9 优秀 Critic 非常准确 ✅ 完美 示例：\nstep:0 - critic/vf_explained_var:-0.234 # 初期很差 step:50 - critic/vf_explained_var:0.512 # 开始学到 step:100 - critic/vf_explained_var:0.823 # 拟合良好 ✅ 2.5 critic/grad_norm - Critic 梯度范数 含义：Critic 参数梯度的 L2 范数\n正常范围： - 0.1 - 100.0（通常比 Actor 大） - 经过裁剪后：\u0026lt; grad_clip（通常为 1.0）\n示例：\nstep:0 - critic/grad_norm:1023.278 # 初期很大 step:10 - critic/grad_norm:942.924 step:50 - critic/grad_norm:1.000 # 经常触发裁剪 2.6 critic/lr - Critic 学习率 含义：Critic 的学习率\n典型配置：\ncritic.optim: lr: 1e-5 # 通常比 Actor 大一个数量级 3. 奖励与优势指标 3.1 critic/score/mean|max|min - 原始分数统计 含义：每个序列的原始分数（token_level_scores 的总和）\n来源：\nsequence_score = token_level_scores.sum(dim=-1) 含义： - 奖励函数给出的原始分数 - 包含任务奖励 + KL 惩罚（如果启用）\n正常范围： - 取决于任务和奖励函数 - 数学任务：0.0 (错误) 或 1.0 (正确) - 对话任务：连续值，如 -5.0 到 5.0\n趋势分析：\n趋势 含义 期望 mean 上升 策略在改进 ✅ 训练目标 max 上升 最好样本改进 好的探索 min 上升 最差样本改进 更稳健 mean 稳定 收敛或瓶颈 检查是否最优 示例：\n场景 1：数学任务（GSM8K）\nstep:0 - critic/score/mean:0.145 - max:1.0 - min:0.0 # 14.5% 正确率 step:50 - critic/score/mean:0.523 - max:1.0 - min:0.0 # 52.3% 正确率 ✅ 进步明显 step:100 - critic/score/mean:0.782 - max:1.0 - min:0.0 # 78.2% 正确率 ✅ 持续改进 场景 2：对话任务\nstep:0 - critic/score/mean:0.234 - max:4.5 - min:-3.2 step:50 - critic/score/mean:1.823 - max:4.8 - min:-1.5 # 平均分数提高，最差样本也改进 ✅ 3.2 critic/rewards/mean|max|min - 奖励统计 含义：实际用于训练的奖励（score - KL penalty）\n计算公式：\nrewards = scores - kl_coef * kl_divergence 与 score 的关系：\n# 如果不使用 KL 惩罚 rewards == scores # 如果使用 KL 惩罚（在 reward 中） rewards = scores - kl_penalty 示例：\nstep:0 - critic/score/mean:0.500 - critic/rewards/mean:0.485 # KL 惩罚：0.500 - 0.485 = 0.015 3.3 critic/advantages/mean|max|min - 优势函数统计 含义：优势函数，衡量动作相对基线的好坏\n计算公式（GAE）：\ndelta = rewards + gamma * V(s_{t+1}) - V(s_t) advantages = GAE(delta, gamma, lambda) advantages = (advantages - mean) / (std + eps) # 归一化 数学直觉： - 优势 \u0026gt; 0：动作比平均好，应该增加概率 - 优势 \u0026lt; 0：动作比平均差，应该降低概率 - 优势 ≈ 0：动作接近平均水平\n正常特性： - mean ≈ 0：归一化后的期望特性 - std ≈ 1：归一化后的特性 - max 和 min 表示优势范围\n趋势分析：\n指标 正常值 异常情况 可能原因 mean -0.1 到 0.1 |mean| \u0026gt; 0.5 归一化失败 max 1.0 到 5.0 \u0026gt; 10.0 奖励方差过大 min -5.0 到 -1.0 \u0026lt; -10.0 奖励方差过大 max-min 2.0 到 10.0 \u0026gt; 20.0 策略差异大 示例：\n正常情况\nstep:0 - critic/advantages/mean:-0.023 - max:2.360 - min:-2.280 # mean 接近 0，范围合理 ✅ 异常情况\nstep:0 - critic/advantages/mean:5.234 - max:50.23 - min:-45.12 # mean 远离 0，范围过大 ⚠️ # 检查：奖励尺度、归一化、critic 训练 3.4 critic/returns/mean|max|min - 回报统计 含义：累积折扣奖励（advantages + values）\n计算公式：\nreturns = advantages + values # 或直接计算 returns = sum(gamma^t * reward_t) 作用： - Critic 的训练目标 - 应该与实际累积奖励接近\n与其他指标关系：\nreturns = advantages + values # 理想情况 returns ≈ actual_cumulative_rewards 示例：\nstep:0 - critic/returns/mean:0.003 - critic/values/mean:-2.045 # advantages = 0.003 - (-2.045) = 2.048 3.5 critic/values/mean|max|min - 价值估计统计 含义：Critic 预测的状态价值\n作用： - 作为优势计算的基线 - 应该接近实际 returns\n趋势： - 训练过程中应该逐渐接近 returns/mean\n示例：\nstep:0 - critic/values/mean:-2.045 - critic/returns/mean:0.003 # 初期相差大 step:100 - critic/values/mean:2.340 - critic/returns/mean:2.450 # 逐渐接近 ✅ 3.6 actor/reward_kl_penalty - KL 惩罚 含义：策略与参考策略的 KL 散度\n计算公式：\nkl = mean(old_log_prob - ref_log_prob) 配置：\nalgorithm: use_kl_in_reward: True # 启用 kl_ctrl: type: fixed # 或 adaptive kl_coef: 0.001 示例：\nstep:0 - actor/reward_kl_penalty:0.000 - actor/reward_kl_penalty_coeff:0.001 4. 序列长度指标 4.1 response_length/mean|max|min - 生成长度统计 含义：生成的 response 的实际长度（不含 padding）\n正常范围： - 取决于 max_response_length 配置 - 理想：\u0026lt; max_response_length（未达到上限）\n趋势分析：\n情况 含义 影响 建议 mean ≈ max 经常截断 可能影响质量 增大 max_response_length mean \u0026lt;\u0026lt; max 生成较短 效率高 正常 ✅ mean 增长 生成变长 监控质量 正常演化 max = max_response_length 有截断 部分样本未完成 考虑增大上限 示例：\n正常情况\n# max_response_length = 512 step:0 - response_length/mean:239.1 - max:256.0 - min:77.0 # 平均 239，未达到上限 ✅ 截断过多\n# max_response_length = 256 step:0 - response_length/mean:253.8 - max:256.0 - clip_ratio:0.85 # 85% 样本达到上限，可能需要增大到 512 ⚠️ 4.2 response_length/clip_ratio - 响应截断比例 含义：达到最大长度限制的样本比例\n计算公式：\nclip_ratio = mean(response_length == max_response_length) 理想值： - \u0026lt; 0.1 (10%)：大部分样本能自然结束 - 0.1 - 0.3：可以接受 - \u0026gt; 0.5：需要增大 max_response_length\n示例：\nstep:0 - response_length/clip_ratio:0.012 # 1.2% 截断 ✅ step:50 - response_length/clip_ratio:0.523 # 52.3% 截断 ⚠️ 4.3 prompt_length/mean|max|min - 提示长度统计 含义：输入 prompt 的实际长度\n作用： - 监控输入数据分布 - 计算 token 吞吐量\n示例：\nstep:0 - prompt_length/mean:123.5 - max:512.0 - min:45.0 4.4 prompt_length/clip_ratio - 提示截断比例 含义：达到最大提示长度的样本比例\n理想值： - \u0026lt; 0.05：数据准备合理 - \u0026gt; 0.1：考虑增大 max_prompt_length\n5. Episode 指标 5.1 episode/reward/mean|max|min - Episode 奖励统计 含义：完整 episode 的累积奖励\n应用场景： - 多轮对话任务 - 环境交互任务（AlfWorld, WebShop等）\n与 critic/score 的区别： - critic/score：单步生成的奖励 - episode/reward：整个 episode 的累积奖励\n示例：\n# AlfWorld 任务 episode/reward/mean: 0.65 episode/reward/max: 1.0 # 完成任务 episode/reward/min: 0.0 # 失败 5.2 episode/length/mean|max|min - Episode 长度统计 含义：Episode 包含的交互轮数\n示例：\nepisode/length/mean: 15.3 # 平均 15 轮交互 episode/length/max: 50.0 # 达到上限 episode/length/min: 3.0 # 快速完成 5.3 episode/success_rate_* - 任务成功率 含义：特定任务的成功率指标\n示例：\nepisode/success_rate_alfworld: 0.68 # 68% 成功率 6. 时间性能指标 6.1 timing_s/* - 各阶段耗时（秒） 包含的阶段： - timing_s/gen：生成阶段（rollout） - timing_s/ref：参考策略计算 - timing_s/values：价值函数计算 - timing_s/adv：优势函数计算 - timing_s/update_critic：Critic 更新 - timing_s/update_actor：Actor 更新 - timing_s/step：总步骤时间\n示例：\nstep:0 - timing_s/gen:21.470 - timing_s/ref:4.360 - timing_s/values:5.800 - timing_s/adv:0.109 - timing_s/update_critic:15.664 - timing_s/update_actor:20.314 瓶颈分析：\n阶段 占比 典型值 优化方向 gen 30-40% 20-30s 增大 batch size，优化采样 ref 5-10% 3-5s 使用 offload values 5-10% 3-5s - update_actor 25-35% 15-25s 优化 micro batch size update_critic 20-30% 10-20s 优化 micro batch size 6.2 timing_per_token_ms/* - 每 Token 耗时（毫秒） 含义：归一化到每个 token 的处理时间\n计算公式：\ntiming_per_token_ms/gen = timing_s/gen * 1000 / num_response_tokens timing_per_token_ms/ref = timing_s/ref * 1000 / num_total_tokens 作用： - 更公平的性能对比（消除 batch size 影响） - 识别效率瓶颈\n示例：\ntiming_per_token_ms/gen: 41.67 # 每个生成 token 42ms timing_per_token_ms/ref: 25.00 # 每个 token 25ms timing_per_token_ms/values: 25.00 性能基准： - 生成（Gen）：20-50 ms/token - 前向（Ref/Values）：10-30 ms/token - 训练（Update）：30-60 ms/token\n7. 系统性能指标 7.1 perf/total_num_tokens - 总 Token 数 含义：一个 step 处理的总 token 数（prompt + response）\n计算公式：\ntotal_num_tokens = sum(attention_mask) # 所有有效 token 示例：\nperf/total_num_tokens: 98304 # batch_size * seq_length 7.2 perf/time_per_step - 每步耗时 含义：完整训练步的总时间\n等价于：timing_s/step\n示例：\nperf/time_per_step: 67.8 # 秒 7.3 perf/throughput - 吞吐量 含义：每秒每 GPU 处理的 token 数\n计算公式：\nthroughput = total_num_tokens / (time_per_step * n_gpus) 示例：\nperf/total_num_tokens: 98304 perf/time_per_step: 67.8 n_gpus: 8 perf/throughput: 145.0 # tokens/s/GPU 性能基准：\n模型大小 目标吞吐量 (tokens/s/GPU) 7B 100 - 300 13B 50 - 150 30B 20 - 60 70B 10 - 30 7.4 perf/mfu/* - 模型 FLOPs 利用率 含义：实际 FLOPs 占理论峰值的比例\n计算公式：\nestimated_flops = model_flops(num_tokens, model_params) promised_flops = hardware_peak_flops(gpu_type) mfu = estimated_flops / (promised_flops * n_gpus) 指标： - perf/mfu/actor：Actor 训练的 MFU - perf/mfu/critic：Critic 训练的 MFU\n目标值： - 优秀：\u0026gt; 40% - 良好：30% - 40% - 一般：20% - 30% - 较差：\u0026lt; 20%\n示例：\nperf/mfu/actor: 0.35 # 35% MFU ✅ perf/mfu/critic: 0.28 # 28% MFU 7.5 内存指标 指标： - perf/max_memory_allocated_gb：GPU 分配的最大内存 - perf/max_memory_reserved_gb：GPU 保留的最大内存 - perf/cpu_memory_used_gb：CPU 内存使用\n示例：\nperf/max_memory_allocated_gb: 45.2 perf/max_memory_reserved_gb: 48.0 perf/cpu_memory_used_gb: 128.5 8. 训练控制指标 8.1 training/global_step - 全局步数 含义：当前训练步数（从 0 开始）\n用途： - X 轴坐标 - 学习率调度 - 保存检查点\n示例：\ntraining/global_step: 123 8.2 training/epoch - 训练轮次 含义：当前训练 epoch\n示例：\ntraining/epoch: 5 9. World Model 指标 9.1 world_model/loss - World Model 损失 含义：World Model 的预测损失（通常是交叉熵）\n应用场景： - 模型推理树搜索 - 状态预测\n示例：\nworld_model/loss: 2.345 world_model/grad_norm: 0.823 10. 验证指标 10.1 验证集指标 格式：val/{metric_name}\n常见指标： - val/score：验证集平均分数 - val/accuracy：任务准确率 - val/pass@k：编程任务的 pass@k\n示例：\nval/score: 0.856 val/accuracy: 85.6% 11. 指标监控最佳实践 11.1 核心指标组合 训练健康度：\n# 策略改进 actor/pg_loss: 下降 critic/score/mean: 上升 episode/reward/mean: 上升 # 如果有 # 训练稳定性 actor/pg_clipfrac: 0.05 - 0.30 actor/ppo_kl: 0.001 - 0.01 actor/grad_norm: \u0026lt; grad_clip # Critic 质量 critic/vf_explained_var: \u0026gt; 0.7 critic/vf_loss: 下降 性能监控：\nperf/throughput: 越高越好 perf/mfu/actor: \u0026gt; 0.3 timing_s/step: 越低越好 11.2 异常诊断流程 问题 1：训练不收敛 症状：\nactor/pg_loss: 波动大 critic/score/mean: 不上升 检查清单： 1. 学习率是否过高？→ 查看 actor/grad_norm 2. 奖励信号是否合理？→ 查看 critic/rewards/mean 3. Critic 是否学习？→ 查看 critic/vf_explained_var 4. 批次大小是否太小？→ 查看 batch_size 配置\n诊断步骤：\n# Step 1: 检查梯度 if actor/grad_norm == grad_clip: print(\u0026quot;梯度被裁剪，考虑降低学习率\u0026quot;) # Step 2: 检查奖励 if critic/rewards/std \u0026gt; 10 * critic/rewards/mean: print(\u0026quot;奖励方差过大，考虑归一化\u0026quot;) # Step 3: 检查 Critic if critic/vf_explained_var \u0026lt; 0.3: print(\u0026quot;Critic 学习不足，增大 critic 学习率\u0026quot;) 问题 2：策略过早收敛 症状：\nactor/entropy_loss: 快速下降到接近 0 critic/score/mean: 停止上升 actor/ppo_kl: 接近 0 原因： - 陷入局部最优 - 探索不足\n解决方案：\n# 增加熵正则化 actor_rollout_ref.actor.entropy_coeff: 0.01 # 从 0.001 增大 # 或增加温度 actor_rollout_ref.rollout.temperature: 1.2 # 从 1.0 增大 问题 3：内存溢出 症状：\nCUDA out of memory 检查：\nperf/max_memory_allocated_gb: 接近 GPU 总内存 解决方案：\n# 减小 batch size actor_rollout_ref.actor.ppo_mini_batch_size: 128 # 从 256 减半 # 或启用梯度检查点 actor_rollout_ref.model.enable_gradient_checkpointing: True # 或使用动态批次大小 actor_rollout_ref.actor.use_dynamic_bsz: True actor_rollout_ref.actor.ppo_max_token_len_per_gpu: 8192 # 从 16384 减半 问题 4：训练速度慢 症状：\nperf/throughput: \u0026lt; 50 tokens/s/GPU (7B 模型) perf/time_per_step: \u0026gt; 120s 诊断：\n# 找到瓶颈 if timing_s/gen \u0026gt; 0.5 * timing_s/step: print(\u0026quot;生成是瓶颈\u0026quot;) # 优化：增大 vllm max_num_seqs，调整 tensor_parallel_size if timing_s/update_actor \u0026gt; 0.4 * timing_s/step: print(\u0026quot;Actor 更新是瓶颈\u0026quot;) # 优化：调整 micro_batch_size，启用 torch.compile 11.3 Wandb/TensorBoard 可视化 推荐面板：\n面板 1：训练进度\n# Y 轴 - critic/score/mean - episode/reward/mean # X 轴 - training/global_step 面板 2：策略更新\n- actor/pg_loss - actor/pg_clipfrac - actor/ppo_kl - actor/entropy_loss 面板 3：Critic 质量\n- critic/vf_loss - critic/vf_explained_var - critic/vpred_mean vs critic/returns/mean 面板 4：性能\n- perf/throughput - perf/mfu/actor - timing_s/step 11.4 日志示例解读 示例 1：健康训练 step:0 - timing/gen:21.470 - timing/ref:4.360 - timing/values:5.800 - actor/reward_kl_penalty:0.000 - actor/reward_kl_penalty_coeff:0.001 - timing/adv:0.109 - timing/update_critic:15.664 - critic/vf_loss:14.947 - critic/vf_clipfrac:0.000 - critic/vpred_mean:-2.056 - critic/grad_norm:1023.278 - critic/lr(1e-4):0.100 - timing/update_actor:20.314 - actor/entropy_loss:0.433 - actor/pg_loss:-0.005 - actor/pg_clipfrac:0.000 - actor/ppo_kl:0.000 - actor/grad_norm:1.992 - actor/lr(1e-6):0.010 - critic/score/mean:0.004 - critic/score/max:1.000 - critic/score/min:0.000 - response_length/mean:239.133 - response_length/max:256.000 解读： - ✅ actor/pg_loss:-0.005：策略在改进 - ✅ actor/pg_clipfrac:0.000：初期无裁剪 - ✅ critic/score/max:1.000：有成功样本 - ⚠️ critic/vf_loss:14.947：Critic 初期损失高，正常 - ✅ 各项指标在合理范围\n示例 2：学习率过高 step:5 - actor/pg_loss:0.523 - actor/pg_clipfrac:0.850 - actor/ppo_kl:0.125 - actor/grad_norm:1.000 - critic/score/mean:0.003 解读： - ⚠️ actor/pg_clipfrac:0.850：85% 被裁剪，过高！ - ⚠️ actor/ppo_kl:0.125：KL 过大（\u0026gt;0.05） - ⚠️ actor/grad_norm:1.000：频繁触发梯度裁剪 - ❌ critic/score/mean：不上升\n建议：\n# 降低学习率 actor_rollout_ref.actor.optim.lr: 5e-7 # 从 1e-6 减半 示例 3：Critic 学习不足 step:50 - critic/vf_loss:45.23 - critic/vf_explained_var:-0.234 - critic/vpred_mean:0.123 - critic/returns/mean:5.678 - actor/pg_loss:-0.002 - critic/score/mean:0.256 解读： - ❌ critic/vf_explained_var:-0.234：负值！ - ❌ critic/vf_loss:45.23：50 步后仍然很高 - ❌ vpred_mean vs returns/mean：相差太大\n建议：\n# 增大 Critic 学习率 critic.optim.lr: 5e-5 # 从 1e-5 增大 5 倍 # 或增加训练 epochs critic.ppo_epochs: 2 # 从 1 增加 12. 快速参考表 核心指标目标值 指标 目标范围 理想趋势 actor/pg_loss -0.01 ~ 0.01 → 0 actor/pg_clipfrac 0.05 ~ 0.30 稳定 actor/ppo_kl 0.001 ~ 0.01 稳定或↓ actor/entropy_loss 0.1 ~ 0.5 缓慢↓ critic/vf_loss \u0026lt; 1.0 ↓ critic/vf_explained_var \u0026gt; 0.7 ↑ critic/score/mean 任务相关 ↑ perf/throughput 见模型基准 越高越好 异常模式速查 异常模式 可能原因 检查指标 不收敛 学习率/奖励/Critic grad_norm, vf_explained_var 过早收敛 探索不足 entropy_loss, ppo_kl 训练崩溃 梯度爆炸 grad_norm, pg_loss 性能差 瓶颈 timing_s/*, throughput 内存溢出 batch 过大 max_memory_* 附录：指标计算源码位置 指标类别 源文件 Actor 指标 verl/workers/actor/dp_actor.py Critic 指标 verl/workers/critic/dp_critic.py 数据指标 verl/trainer/ppo/metric_utils.py 时间指标 verl/trainer/ppo/metric_utils.py 核心算法 verl/trainer/ppo/core_algos.py 文档版本：v1.0\n最后更新：2025-01-XX\n适用于：verl 0.3.x+\n","date":"2025-10-23T00:00:00Z","permalink":"/metrics%E6%80%BB%E7%BB%93/","title":"metrics总结"},{"content":"[project] name = \u0026quot;rl-train\u0026quot; version = \u0026quot;0.1.0\u0026quot; description = \u0026quot;Add your description here\u0026quot; readme = \u0026quot;README.md\u0026quot; requires-python = \u0026quot;\u0026gt;=3.10\u0026quot; dependencies = [ \u0026quot;codetiming\u0026gt;=1.4.0\u0026quot;, \u0026quot;datasets\u0026gt;=4.2.0\u0026quot;, \u0026quot;flash-attn==2.7.4.post1\u0026quot;, \u0026quot;gpustat\u0026gt;=1.1.1\u0026quot;, \u0026quot;hydra-core[all]\u0026gt;=1.3.2\u0026quot;, \u0026quot;ninja\u0026gt;=1.13.0\u0026quot;, \u0026quot;peft\u0026gt;=0.17.1\u0026quot;, \u0026quot;python-dotenv[cli]\u0026gt;=1.1.1\u0026quot;, \u0026quot;ray[default]\u0026gt;=2.50.0\u0026quot;, \u0026quot;requests\u0026gt;=2.32.5\u0026quot;, \u0026quot;setuptools\u0026gt;=80.9.0\u0026quot;, \u0026quot;tensordict\u0026gt;=0.10.0\u0026quot;, \u0026quot;torch\u0026gt;=2.6.0\u0026quot;, \u0026quot;torchaudio\u0026gt;=2.6.0\u0026quot;, \u0026quot;torchdata\u0026gt;=0.11.0\u0026quot;, \u0026quot;torchvision\u0026gt;=0.21.0\u0026quot;, \u0026quot;transformers\u0026gt;=4.57.1\u0026quot;, \u0026quot;wandb\u0026gt;=0.22.2\u0026quot;, ] [[tool.uv.index]] url = \u0026quot;https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple\u0026quot; default = true [[tool.uv.index]] name = \u0026quot;pytorch-cu124\u0026quot; url = \u0026quot;https://download.pytorch.org/whl/cu124\u0026quot; explicit = true [tool.uv.sources] torch = [ { index = \u0026quot;pytorch-cu124\u0026quot;, marker = \u0026quot;sys_platform == \u0026#39;linux\u0026#39; or sys_platform == \u0026#39;win32\u0026#39;\u0026quot; }, ] torchvision = [ { index = \u0026quot;pytorch-cu124\u0026quot;, marker = \u0026quot;sys_platform == \u0026#39;linux\u0026#39; or sys_platform == \u0026#39;win32\u0026#39;\u0026quot; }, ] torchaudio = [ { index = \u0026quot;pytorch-cu124\u0026quot;, marker = \u0026quot;sys_platform == \u0026#39;linux\u0026#39; or sys_platform == \u0026#39;win32\u0026#39;\u0026quot; }, ] [tool.uv] no-build-isolation-package = [\u0026quot;flash-attn\u0026quot;] ","date":"2025-10-20T00:00:00Z","permalink":"/example/","title":"example"},{"content":"构建 通过镜像源拉去镜像\ndocker pull docker.m.daocloud.io/nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22. 04 创建Dockerfile，基于拉去的镜像创建自定义的镜像\nmkdir ~/docker-projects/gpu-experiment/ cd ~/docker-projects/gpu-experiment/ vim Dockerfile # 1. 基础镜像：CUDA 11.8 + CuDNN 8 + 开发工具（devel）+ Ubuntu 22.04 # 保留devel版本以支持后续可能的编译需求，但仅包含最核心组件 FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 # 2. 关闭交互模式，避免安装时弹窗 ENV DEBIAN_FRONTEND=noninteractive # 3. 安装基础系统工具并添加sudo支持 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y --no-install-recommends \\ apt-utils \\ openssh-client \\ # 基础shell工具 bash curl wget ca-certificates \\ # 文本编辑工具 vim nano \\ # 版本控制工具（后续拉代码用） git \\ # 基础编译工具（devel已包含大部分，这里补充必要的） build-essential \\ # 添加sudo支持 sudo \\ # 清理缓存，保持镜像精简 \u0026amp;\u0026amp; rm -rf /var/lib/apt/lists/* # 4. 配置用户和权限（与主机UID/GID匹配） # 创建与主机相同GID的用户组 # 红色的部分需要根据自己主机的UID/GID进行修改。查看方式：在终端输入 id 命令即可。 RUN groupadd -g gid username \u0026amp;\u0026amp; \\ # 创建与主机相同UID的用户并加入组 useradd -u uid -g gid -m -s /bin/bash username \u0026amp;\u0026amp; \\ # 允许该用户使用sudo且无需密码 echo \u0026quot;username ALL=(ALL) NOPASSWD:ALL\u0026quot; \u0026gt;\u0026gt; /etc/sudoers # 5. 配置CUDA环境变量（确保后续在容器内编译时能找到CUDA） ENV CUDA_HOME=/usr/local/cuda ENV PATH=$CUDA_HOME/bin:$PATH ENV LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH # 6. 创建工作目录并设置权限 RUN mkdir -p /workspace \u0026amp;\u0026amp; \\ chown -R username:username /workspace WORKDIR /workspace # 7. 切换到普通用户（与主机用户匹配） USER username # 8. 启动容器时直接进入bash，等待手动配置环境 CMD [\u0026quot;/bin/bash\u0026quot;] 给拉取的镜像打标签（与 Dockerfile 中的 FROM 一致）： 因为 Dockerfile 中写的是 nvidia/cuda:xxx，需要将手动拉取的镜像重命名为该标签，否则构建时仍会去官方源拉取\ndocker tag docker.m.daocloud.io/nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 构建本地镜像\ndocker build -t gpu-experiment:latest . 使用 如果计划长期在容器内进行实验（需要保留环境配置、代码、实验数据，避免每次启动都重新搭建），则需要调整容器启动参数，核心是去掉 --rm 并增加“持久化配置”，确保容器停止后不被删除，且数据和环境能复用。以下是详细的参数配置和操作指南：\n一、核心参数调整（针对长期使用） 首先明确长期使用容器的关键需求：容器不自动删除、数据不丢失、环境可复用。推荐的启动命令如下，我们逐参数解释：\ndocker run -it \\ --name gpu_exp_container \\ # 给容器起固定名称（核心：方便后续找到/启动容器） --gpus all \\ # 启用所有GPU（必选，实验需要GPU） -v /home/lbwang/sxliu:/workspace \\ # 挂载宿主机目录（核心：持久化数据） -p 8888:8888 \\ # 端口映射（可选：若用Jupyter/远程调试） --shm-size=32g \\ # 增大共享内存（可选：避免数据加载时内存不足） --restart unless-stopped \\ # 自动重启（可选：服务器重启后容器自动启动） gpu-experiment:latest # 镜像名（你的GPU实验镜像） 二、关键参数详解（针对长期使用） 1. 去掉 --rm：保留容器不自动删除 原 --rm 问题**：容器停止后会被自动删除，之前配置的环境（如安装的Python库、编译的工具）全部丢失。\n去掉后的效果：容器停止后仍保留在系统中（状态为 Exited），下次可直接启动，环境完全复用。\n2. --name gpu_exp_container：给容器固定名称 作用：用自定义名称（如 gpu_exp_container）标识容器，避免Docker自动生成随机名称（如 nostalgic_turing），方便后续操作（启动、进入、停止）。\n后续操作示例：\n启动已停止的容器：docker start gpu_exp_container\n进入运行中的容器：docker exec -it gpu_exp_container /bin/bash\n停止容器：docker stop gpu_exp_container\n3. -v /home/lbwang/sxliu:/workspace：挂载宿主机目录（数据持久化核心） 作用：将宿主机的 ~/sxliu 目录（你的实验目录）与容器内的 /workspace 目录双向绑定，实现：\n数据不丢失：容器内 /workspace 下的代码、实验结果、日志会同步到宿主机 ~/sxliu，即使容器被删除，数据仍在宿主机。\n跨容器复用：若后续重建容器，重新挂载该目录即可恢复所有数据。\n宿主机编辑方便：可在宿主机用熟悉的工具（如VS Code）编辑代码，容器内直接运行（无需在容器内用 vim 编辑）。\n注意：替换 /home/lbwang/sxliu 为你宿主机实际的实验目录路径（用 pwd 命令可查看当前目录路径）。\n4. 可选但推荐的参数 -p 8888:8888：端口映射若需要在容器内启动Jupyter Notebook/Lab（可视化调试代码），通过该参数将容器的8888端口映射到宿主机的8888端口，之后在宿主机浏览器访问 http://localhost:8888 即可进入Jupyter。\n--shm-size=32g：增大共享内存深度学习实验中，数据加载（如用 DataLoader）常依赖共享内存，默认共享内存较小（约64MB），可能导致“内存不足”错误。根据你的GPU内存配置（如16G/32G/64G）设置，建议设为GPU内存的2倍（如32G）。\n--restart unless-stopped：自动重启容器若服务器意外重启（如断电、维护），该参数会让容器在服务器启动后自动恢复运行，避免手动重新启动。\n三、长期使用容器的核心操作流程 1. 首次启动容器（用上述命令） docker run -it \\ --name gpu_container_cuda118 \\ --gpus all \\ -v /data/lbwang:/data/lbwang \\ -v /home/lbwang:/home/lbwang \\ -p 8888:8888 \\ --shm-size=32g \\ --restart unless-stopped \\ gpu-experiment:latest 进入容器后，即可开始配置环境（如安装Python、深度学习库、下载数据集等），所有操作会保存在容器内，且 /workspace 目录的数据会同步到宿主机。\n2. 日常使用：启动/进入容器 若容器已停止，先启动：\ndocker start gpu_container_cuda118 # 启动容器（后台运行） 进入已启动的容器（核心操作，每次用容器都需执行）：\ndocker exec -it gpu_container_cuda118 /bin/bash exec：在运行的容器内执行命令；\n-it：交互式终端（允许输入命令、查看输出）；\n执行后会进入容器的 /workspace 目录，可直接运行实验代码。\n3. 停止容器（不删除，下次可启动） docker stop gpu_container_cuda118 # 停止容器，环境和数据全部保留 4. 备份容器（可选，防止容器损坏） 若配置了复杂的实验环境，可将容器保存为新镜像（相当于“快照”），即使原容器损坏，也能从新镜像重建：\n# 将运行中的容器保存为新镜像（镜像名：gpu_exp_backup，版本：v1） docker commit gpu_container_cuda118 gpu_exp_backup:v1 后续重建容器时，用新镜像启动：\ndocker run -it --name gpu_exp_new --gpus all gpu_exp_backup:v1 四、注意事项 容器命名唯一性：若提示 Error response from daemon: Conflict. The container name \"/gpu_exp_container\" is already in use，说明该名称已被占用，可换个名称（如 gpu_exp_container_v2），或删除旧容器（docker rm 旧容器名，需先停止旧容器）。\n数据备份：虽然 -v 挂载实现了数据同步，但仍建议定期在宿主机备份 /home/lbwang/sxliu 目录（如用 tar 压缩），防止宿主机磁盘故障导致数据丢失。\n环境更新：若后续需要安装新依赖（如新增Python库），直接在容器内用 pip install 即可，安装结果会保留在容器内，下次启动仍有效。\n如果需要root权限，使用sudo su -命令。\n通过以上配置，你可以在容器内长期稳定地进行实验，既隔离了宿主机环境，又能确保环境和数据的持久化，无需每次重新搭建。\n","date":"2025-10-20T00:00:00Z","permalink":"/%E8%87%AA%E5%88%9B%E5%AE%B9%E5%99%A8/","title":"自创容器"},{"content":" 在一个需要多轮对话完成的长rollout，目前有两种主流做法，第一种是把每一轮的上下文内容给拼接起来。最后形成一条长长的轨迹通过小心的设置mask来让模型可以训练。第二种是把所有的单轮交互切开，直接看做独立的rollout送进batch，每一个步骤都送一个相同的final reward来训练。这两种做法其实都有各自的问题。第一种做法最后一次完整对话会变超级长，非常耗显存。第二种做法的话，模型会明显遇到上下文缺失的问题。\n第一种的代表论文为RAGEN：Understanding Self-Evolution in LLM Agents via Multi-Turn Reinforcement Learning，第二种的代表论文为Group-in-Group Policy Optimization for LLM Agent Training。 现在的框架基本上都是后者，比如agent-lighnting、verl-agent。目前进入决赛圈的框架有： - Agent-Lightning - RLLM - Verl (toy实现) - Verl-Agent 前两个较为通用，但不支持MLLM，后两者支持MLLM但一个是toy，另一个只支持特定环境，需要自己实现环境。\n参考 # DeepWerewolf 如何搭建一个LLM狼人杀 Agentic RL实时训练系统\n","date":"2025-10-17T00:00:00Z","permalink":"/agentic-rl/","title":"agentic RL"},{"content":"这篇论文无疑是大语言模型（Large Language Model, LLM）对齐（Alignment）领域的一项杰出工作。它不仅提出了一个在数学上更优美、在实践中更稳定高效的后训练（Post-Training）算法，更重要的是，它为我们理解和统一多种主流的对齐方法提供了一个全新的理论视角。通过这篇解读，我将带您深入其核心，理解其精髓。\n首先，让我们对论文进行一个整体性的深入解读。\n大语言模型的后训练，其核心目标是让模型在完成“通才”式的预训练后，学会如何更好地遵循人类的指令、偏好和价值观，成为一个“专才”或“有用的助手”。当前主流的技术路线主要包括监督微调（Supervised Fine-Tuning, SFT）和从人类反馈中进行强化学习（Reinforcement Learning from Human Feedback, RLHF）。然而，RLHF，尤其是以PPO为代表的传统方法，存在训练过程不稳定、对超参数敏感、需要维护多个模型（策略模型、价值模型、奖励模型、参考模型）等问题，这极大地增加了其在工业界应用的门槛和成本。\n为了解决这些问题，学术界进行了一系列探索。其中，直接偏好优化（Direct Preference Optimization, DPO）是一项里程碑式的工作，它巧妙地将奖励模型和策略优化合并为一个单一的损失函数，绕过了显式的奖励建模和复杂的强化学习流程。但DPO主要处理成对的（pairwise）偏好数据（即“回答A比回答B好”），并且其理论基础——Bradley-Terry模型存在一些固有的理论缺陷，可能导致无法收敛到全局最优解。\n在此背景下，更新的算法如群体相对策略优化（Group Relative Policy Optimization, GRPO）被提出，它将优化范围从“成对”扩展到了“群体”（group-wise），即同时比较多个模型生成的回答。这更符合真实场景，也更能利用计算资源，但论文指出，GRPO同样面临着训练不稳定的问题。\nHowever, GRPO has been documented to experience issues with training instability in prior literature [34, 16]. Specifically, GRPO is highly sensitive to its hyperparameters, such as the clip threshold and the KL-divergence coefficient.\n这正是本篇论文《GVPO》切入的痛点。作者们的目标是设计一个既能像GRPO一样利用群体数据进行高效优化，又能像DPO一样在数学上简洁优雅，并且最重要的是，能够克服现有方法的不稳定性和理论局限，实现稳定、高效且有理论保障的LLM后训练。\nGVPO的核心思想可以概括为“通过最小化均方误差来对齐隐式奖励与真实奖励”。这是一个非常漂亮且直观的物理解释。让我们通俗地解释一下：\n隐式奖励（Implicit Reward）: DPO的论文揭示了一个深刻的联系：任何一个经过微调的策略模型 $ $ 相对于一个参考模型 $ {ref} $ 的对数概率差异，都可以看作是模型“内心”学到的一个隐式奖励函数 $ R_(x, y) $。也就是说，模型倾向于生成让这个隐式奖励更高的内容。 真实奖励（Actual Reward）: 这是我们通过外部评价（如人类打分、特定规则判断）给予模型的显式奖励信号 $ R(x, y) $。 GVPO的目标: 就是让模型“内心”的奖励标准（隐式奖励）与我们“外部”设定的奖励标准（真实奖励）尽可能地保持一致。它通过一个均方误差（Mean Squared Error, MSE）损失函数来实现这一点，即最小化两者中心化后的差值的平方。 这个设计带来了几个关键的理论和实践优势，也是论文最大的创新贡献：\n理论最优性保证：论文从理论上证明（Theorem 3.1），GVPO的唯一最优解精确地对应于KL约束下奖励最大化的最优策略。这解决了DPO可能无法收敛到全局最优的理论隐患，为算法的可靠性提供了坚实的数学基础。 训练稳定性：通过其独特的损失函数设计，GVPO避免了传统RL方法中的价值函数拟合误差和PPO中的重要性采样（Importance Sampling）带来的梯度爆炸风险，也比GRPO对超参数更不敏感，从而显著提升了训练的稳定性。 采样灵活性（离策略 Off-policy）：GVPO对采样分布的要求非常宽松，允许使用“离策略”数据进行训练。这意味着我们可以复用历史数据、混合不同来源的数据，甚至使用比当前策略更强的模型进行采样，极大地提升了样本效率和训练的灵活性，这是传统“在线策略（On-policy）”方法（如PPO）难以做到的。 统一的视角：论文将SFT、GRPO、DPO等方法统一到了一个“加权对数似然”的框架下（Equation 7），清晰地揭示了这些方法的内在联系：它们本质上都是在为不同的回答分配不同的权重。GVPO则是在这个框架下，设计出了一种理论上最优的权重分配方案。 在实验部分，论文选择了极具挑战性的数学推理任务来验证GVPO的性能。在Qwen2.5-Math-7B模型上的实验结果令人信服。如Table 1所示，GVPO在AIME2024、AMC、MATH500等多个权威数学基准测试中，全面超越了基础模型、GRPO及Dr.GRPO（GRPO的改进版）。例如，在AIME2024上，GVPO取得了20.72的成绩，而表现最好的基线Dr.GRPO仅为16.56，提升幅度巨大。这充分证明了GVPO在复杂推理场景下的实际效果。\n总而言之，GVPO不仅仅是对现有算法的一次增量式改进，它更像是一次理论上的“正本清源”。它抓住了后训练算法的核心——奖励对齐，并为此设计了一个目标明确、理论完备、实践高效的解决方案，为大模型对齐技术的发展提供了一个更可靠、更通用的新范式。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解和分析。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是开发一种更加可靠、通用且高效的大语言模型后训练算法。这个算法需要同时具备理论上的最优性保证和实践中的训练稳定性与采样灵活性。\n解决的实际问题:\n训练不稳定性: 解决以PPO和GRPO为代表的现有后训练方法在训练过程中容易出现梯度爆炸、对超参数（如学习率、KL惩罚系数、裁剪阈值）高度敏感的问题。这在实际生产中会导致大量的计算资源浪费在调参上，且模型性能难以复现。 理论局限性: 解决DPO等方法存在的理论缺陷，即不能保证收敛到KL约束下奖励最大化的全局最优解。这意味着即使DPO训练收敛了，得到的也可能是一个次优的模型。 样本效率低下: 解决传统在线策略RL方法（如PPO）必须“边玩边学”，每次更新都需要用当前策略重新采样，导致数据利用率低、训练速度慢的问题。 行业发展意义:\n降低对齐成本和门槛: 一个稳定、高效的算法可以显著减少训练对齐模型所需的算力和时间，降低对超参数调试的依赖，使得更多中小型企业和研究机构也能负担得起高质量模型的对齐训练。 提升模型性能与可靠性: 具有全局最优保证的算法，意味着我们更有可能训练出性能上限更高、表现更可靠的模型。这对于金融、医疗、法律等对模型准确性和可靠性要求极高的领域至关重要。 加速技术迭代: 灵活的离策略训练能力，使得研究者可以更方便地利用和混合各种来源的数据（如人类标注数据、模型蒸馏数据、历史数据），从而极大地加速了新想法的验证和模型的迭代周期。这为整个大模型生态的繁荣发展提供了底层技术支持。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心方法: 论文提出了群体方差策略优化（Group Variance Policy Optimization, GVPO）。其核心思路不是直接优化策略，而是通过一个均方误差损失，强制模型的隐式奖励函数去拟合真实的奖励函数。\n关键创新与细节:\n零和权重设计（Zero-sum Property）: DPO的成功在于利用了奖励与策略的解析关系，但其推导中存在一个难以处理的配分函数Z(x)。GVPO发现了一个关键条件： \u0026gt; To address this, we identify a critical condition: when the sum of assigned response weights within a prompt group equals zero, \\(\\sum_{i=1}^k w_i = 0\\), the partition function becomes invariant across compared responses, effectively canceling out in the policy update loss. 只要保证在一个样本群体中，分配给所有回答的权重之和为零，配分函数Z(x)的影响就可以在梯度更新中被消除。这是一个非常巧妙的数学技巧，为后续的损失函数设计铺平了道路。\n均方误差损失函数: 基于上述零和设计，GVPO的梯度可以被构造成一个均方误差损失的梯度。如下图（源自论文Figure 1）所示，其核心是最小化“隐式奖励的中心化值”与“真实奖励的中心化值”之间的差距。\n这个形式非常直观：\\(R_\\theta(x, y)\\) 是模型的隐式奖励，\\(R(x, y)\\) 是真实奖励。GVPO就是要让这两者对齐。\n与之前方法的比较与优势:\n特性 PPO (RLHF) DPO GRPO GVPO (本文) 优化对象 策略+价值函数 偏好对（Pairwise） 偏好群体（Group-wise） 偏好群体（Group-wise） 训练稳定性 较低，对超参敏感 较高 中等，不稳定 非常高，鲁棒 理论保证 局部最优 可能次优 经验性 全局唯一最优 样本效率 低 (在线策略) 高 (离策略) 中等 (依赖重要性采样) 非常高 (灵活的离策略) 核心优势 通用RL框架 简洁，无需奖励模型 利用群体信息 理论完备、稳定高效、采样灵活 核心劣势 复杂，不稳定 仅限成对，理论有瑕 不稳定，依赖裁剪 暂无明显理论劣势 ### 3. 论文通过什么实验来验证所提出方法的有效性？ 论文通过在数学推理（Math Reasoning）这一具有挑战性的任务上进行全面的实验，来验证GVPO的有效性。\n实验设计:\n基础模型: Qwen2.5-Math-7B，一个强大的数学基础模型。 训练数据: Competition Math dataset，一个高质量的数学竞赛题数据集。 评估基准: 采用了多个公认的数学能力评测基准，包括 AIME2024, AMC, Math500, Minerva, 和 OlympiadBench，覆盖了不同难度和类型的数学问题。 对比方法: Qwen2.5-Math-7B (基础模型，未经过后训练) +GRPO (使用GRPO进行后训练) +Dr.GRPO (GRPO的一种改进变体) +GVPO (使用本文提出的方法进行后训练) 实验设置: 为了保证公平性，所有方法采用完全相同的实验设置（如模型、数据、硬件），只改变核心的优化算法部分。 实验数据与结果:\nTable 1: 算法在数学数据集上的性能比较\nAlgorithm AIME2024 AMC MATH500 Minerva Olympiadbench Qwen2.5-Math-7B 14.68 38.55 64.00 27.20 30.66 +GRPO 14.79 55.42 80.00 41.17 42.07 +Dr.GRPO 16.56 48.19 81.20 44.48 43.40 +GVPO 20.72 62.65 83.80 45.95 46.96 从上表数据可以清晰地看到： * **全面超越**: GVPO在**所有五个基准**上都取得了最佳性能，证明了其普适的有效性。 * **显著提升**: 提升幅度非常可观。例如，在最具挑战性的AIME2024上，GVPO（20.72）相比基础模型（14.68）提升了超过**41%**，相比之前的SOTA方法Dr.GRPO（16.56）提升了约**25%**。在AMC上，GVPO（62.65）相比GRPO（55.42）也有巨大优势。 * **消融实验**: 论文还通过消融实验（Figures 2, 3, 4）证明了GVPO对关键超参数β不敏感、性能随采样数量k的增加而稳定提升、并且能够灵活适应不同的采样策略，这些都印证了其理论上的鲁棒性和灵活性。 ### 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 结合当前大模型领域的理解，GVPO为未来的研究打开了新的大门。\n值得探索的问题与挑战: 更复杂的奖励函数: 目前实验中的奖励是基于数学答案的正确性，这是一个相对明确的信号。GVPO在处理更模糊、主观和多维度的人类偏好（如文本的创造性、安全性、无害性）时的表现有待进一步验证。 多模态模型的对齐: 如何将GVPO的思想扩展到多模态模型（如文生图、文生视频）？多模态内容的奖励函数定义和群体采样会更加复杂。 **最优采样策略（$ _s $）的探索**: 论文证明了GVPO对采样策略的灵活性，但并未深入探讨何为“最优”采样策略。是应该从更强的教师模型采样？还是混合历史数据？设计一个动态的、自适应的采样策略可能进一步提升GVPO的效率和效果。 与其他对齐技术的结合: 如何将GVPO与过程监督（Process Supervision）、宪法AI（Constitutional AI）等其他对齐技术有机结合，以实现更深层次的价值对齐。 大规模应用下的计算开销: 尽管GVPO比PPO更高效，但群体采样（group-wise sampling）的推理成本会随着群体大小k的增加而线性增加。如何在超大规模模型（千亿、万亿参数）的训练中平衡性能增益与计算成本，是一个实际的工程挑战。 可能催生的新技术和投资机会: 对齐即服务（Alignment-as-a-Service）: 基于GVPO这类稳定可靠的算法，可以发展出为第三方基础模型提供高效、低成本对齐服务的平台，成为模型生态中的重要一环。 自动化数据策展工具: 开发能够自动生成高质量、多样化响应群体的工具，并与GVPO训练流程相结合，形成数据与算法的闭环，这是数据层面的一个重要机会。 垂直领域模型工厂: 利用GVPO的鲁棒性，可以更快速、更低成本地在金融、医疗、教育等垂直领域微调和对齐专用模型，催生出大量高质量的行业解决方案。 ### 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从严谨的学术角度出发，仍有一些可以探讨的方面。\n实验领域的局限性: 论文的所有实验都集中在数学推理这一个领域。虽然数学推理是评估模型逻辑能力的好场景，但这并不能完全代表所有LLM的应用场景。GVPO在开放域对话、长文本写作、代码生成等任务上的泛化能力尚未得到验证。 对奖励模型的依赖: GVPO虽然优化了策略学习过程，但它仍然高度依赖一个高质量的奖励信号 R(x,y)。如果奖励模型本身存在偏差或缺陷（所谓的“reward hacking”），GVPO只会更高效地学习到这些偏差。论文对此并未进行深入讨论。 实现复杂度的讨论不足: 论文提供了一个简化的代码实现，但实际工程中的实现可能涉及更复杂的细节，例如如何高效地进行群体采样和管理、如何处理变长的群体大小（Corollary 3.5虽然理论上解决了，但工程实现可能复杂）。与部署一个成熟的DPO流程相比，GVPO在工程上的开销和复杂度如何，论文没有提供详细的比较。 与最新SOTA的比较: 论文主要与GRPO进行了比较。虽然DPO在理论上有不足，但它在业界被广泛应用，如果能有一个DPO（或其变体，如IPO）在相同设定下的直接对比，将使实验结果更具说服力。 ### 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的研究者或从业者，这篇论文提供了宝贵的启发。\n重点学习的核心思想: 回归本源的思考方式: 不要满足于对现有算法（如PPO）进行修补，而是要回归到问题的本源（奖励最大化），并尝试寻找更直接、更优雅的数学解。DPO和GVPO都是这种思想的典范。 统一框架的视角: 学会从一个更高的维度去审视和统一看似不同的方法。论文中将多种后训练算法统一为“加权对数似然”的框架，这种抽象和归纳的能力是产生突破性创新的关键。 寻找“消除项”的数学技巧: 论文中通过“零和权重”来消除棘手的配分函数Z(x)是一个绝佳的例子。在自己的研究中，当遇到难以处理的复杂项时，可以思考是否能通过巧妙的约束或设计来规避它，而不是硬碰硬地去估计它。 物理解释的重要性: 将复杂的数学公式与直观的物理或几何图像联系起来（如GVPO的“均方误差”解释），不仅有助于自己理解，也更容易让别人接受你的想法。 需要补充的背景知识: 强化学习基础: 尤其是策略梯度（Policy Gradient）、价值函数（Value Function）、KL散度等概念。 PPO算法: 作为RLHF事实上的标准，深入理解其工作原理、优缺点是理解后续改进算法的前提。 Direct Preference Optimization (DPO): GVPO是在DPO思想上的重要发展，必须精读DPO的论文，理解其如何将奖励函数隐式地编码到策略中的。 变分推断（Variational Inference）: KL约束下的优化问题与变分推断有深刻的联系，了解相关知识有助于更深入地理解这些算法的理论根基。 希望这份详尽的解读能够帮助您全面、深入地理解这篇优秀的论文。GVPO无疑为大模型对齐领域树立了一个新的标杆。\n","date":"2025-10-17T00:00:00Z","permalink":"/gvpogroup-variance-policy-optimization-for-large-language-model-post-training/","title":"GVPO：Group Variance Policy Optimization for Large Language Model Post-Training"},{"content":" 论文深度解读 首先，让我们对这篇论文进行一个整体性的深入剖析。\n这篇论文的核心论点是，当前的大语言模型代理（LLM Agents）在执行持久化、长期的真实世界任务时，存在一个致命缺陷：它们无法从过去累积的交互历史中学习。这导致它们像一个“失忆的专家”，每次都从零开始，不断重复过去的错误，也无法利用相似问题的解决经验。论文将这一问题归结为现有记忆机制的不足。\n为了解决这个问题，论文提出了一个名为 REASONINGBANK 的新型记忆框架。理解REASONINGBANK的关键在于它与传统方法的区别。传统的Agent记忆方法主要有两种： 1. 轨迹记忆（Trajectory Memory）: 直接存储原始的交互记录，例如Synapse方法。这种方式虽然信息完整，但充满了噪音和冗余，难以直接复用于新任务。 2. 工作流记忆（Workflow Memory）: 存储成功的、可复用的操作流程或脚本，例如AWM方法。这种方式更具结构性，但它过度依赖于成功的经验，完全忽略了从失败中学习的宝贵机会。\nREASONINGBANK的创新之处在于，它不满足于简单地“记录”，而是追求“提炼”。它从Agent所有的经验——无论是成功还是失败——中，提炼出可泛化的、高层次的推理策略（reasoning strategies）。这就像人类学习一样，我们记住的不仅是“昨天我这么做成功了”，更是“面对这类问题，应该先考虑A，警惕B，因为上次我就在B上犯了错”。\n正如论文所说：“by over-emphasizing successful experiences, they leave the valuable lessons from an agent’s own failures largely underexplored.”\n（通过过分强调成功的经验，它们使得从Agent自身失败中获得的宝贵教训在很大程度上未被探索。）\n这个过程通过一个闭环系统实现：Agent在遇到新任务时，从REASONINGBANK中检索（retrieve）相关的推理策略来指导行动；任务完成后，无论成败，其交互轨迹都会被一个“记忆提取器”构建（construct）成新的、结构化的记忆条目（包含标题、描述、内容）；最后，这些新条目被巩固（consolidate）回REASONINGBANK中，使其不断丰富和进化。\n论文的第二个重大贡献是提出了 MATTS (Memory-Aware Test-Time Scaling，记忆感知的测试时扩展)。这是一个非常精妙的概念。首先，我们需要理解什么是测试时扩展（Test-Time Scaling, TTS）。简单来说，就是在模型推理（测试）阶段，投入更多的计算资源来换取更好的结果。比如，让Agent对同一个任务尝试5次（即k=5），然后选出最好的结果。\n然而，简单的TTS是“盲目”的，多次尝试可能只是在重复相同的错误。MATTS的核心思想是让记忆和扩展产生强大的协同效应（synergy）。具体来说： * 高质量的记忆指导扩展：REASONINGBANK中提炼的策略可以引导Agent的多次尝试（扩展）走向更有希望的路径，避免在无效的方向上浪费计算资源。 * 多样化的扩展经验反哺记忆：通过并行或串行地生成多个不同的解决方案轨迹，Agent可以进行自我对比（self-contrast）或自我优化（self-refinement）。这种对比过程本身就产生了丰富的“元认知”信号，极大地帮助了高质量、可泛化推理策略的提炼，从而创造出更好的记忆。\n这种“记忆指导探索，探索丰富记忆”的良性循环，被论文定位为一个全新的扩展维度（scaling dimension）。作者认为，除了扩展模型参数、扩展数据量，扩展由记忆驱动的经验同样是提升Agent能力的关键路径。\n为了验证这一系列构想，论文在WebArena、Mind2Web和SWE-Bench-Verified等多个高难度、标准化的Agent基准测试上进行了详尽的实验。结果非常显著，例如，在WebArena基准测试中，使用Gemini-2.5-pro模型时，REASONINGBANK相比无记忆基线，成功率提升了7.2%，并且平均交互步骤更少，效率更高（见Table 1）。\n图1: 论文Table 1的实验结果，展示了REASONINGBANK在WebArena基准上相较于No Memory（无记忆）、Synapse（轨迹记忆）、AWM（工作流记忆）的优势。\n更关键的发现体现在Figure 5中，它清晰地揭示了记忆与扩展的协同作用。在没有高质量记忆（如No Memory或Synapse）的情况下，进行测试时扩展（Best-of-3）带来的性能提升非常有限，甚至可能因为引入噪声而降低单次尝试的平均质量（Pass@1）。而REASONINGBANK是唯一一个在扩展的同时，还能提升单次尝试平均质量的方法，这充分证明了其“记忆-扩展”协同机制的有效性。\n图2: 论文Figure 5的结果，展示了记忆质量与测试时扩展效果的协同关系。\n总而言之，这篇论文不仅仅是提出了一个新模型，更是为构建能够“终身学习”和“自我进化”的通用人工智能代理（AGI-like agents）描绘了一幅清晰且可行的技术蓝图。它将Agent的能力提升从单纯依赖于更大的模型，转向了如何更有效地利用和扩展“经验智慧”。\n接下来，我将按照您提出的六个问题，进行更详细的结构化解读。\n1. 论文的研究目标是什么？ 研究目标: 论文的核心研究目标是构建能够从自身经验中持续学习和自我进化的LLM Agent。它旨在解决当前Agent在连续、长期的任务中存在的“失忆”问题，使其能够累积和泛化知识，从而变得越来越强大和高效。\n解决的实际问题:\n重复犯错: Agent在面对相似任务时，会反复陷入之前同样的错误中，浪费大量时间与计算资源。 知识无法迁移: 在一个任务中获得的宝贵见解（比如如何在一个复杂的网站上找到导航菜单）无法被有效保存并应用于其他相关任务。 能力静态: Agent的能力在部署后是固定的，无法根据新的交互经验动态地提升其解决问题的策略和效率。 对行业发展的重要性: 这个问题对于推动AI从“工具”向“自主代理”的转变至关重要。一个能自我进化的Agent，意味着：\n更高的自动化水平和可靠性: 在软件工程、网页自动化、科学研究等复杂领域，Agent可以逐渐成长为真正的专家，自主处理日益复杂的问题。 更低的维护成本: 无需人工频繁地去调试和更新Agent的策略，Agent可以通过与环境的交互“自我修复”和“自我完善”。 通往通用人工智能（AGI）的关键一步: 学习和适应是智能的核心特征。让Agent具备从经验中学习的能力，是实现更通用、更强大人工智能的必经之路。 2. 论文提出了哪些新的思路、方法或模型？ 论文的核心贡献是两个紧密耦合的创新：REASONINGBANK 框架和 MATTS 扩展策略。\nREASONINGBANK (推理银行): 新思路: 核心思路是“提炼高于记录”，并且“失败比成功更具启发性”。它认为记忆的价值在于其抽象和泛化程度，而不仅仅是原始保真度。同时，它首次系统性地将失败经验作为一等公民纳入记忆框架。 具体方法: 结构化记忆模式 (Memory Schema): 将杂乱的经验提炼为结构化的记忆条目，包含title（简明扼要的策略名）、description（一句话总结）、content（具体的推理步骤或决策原则）。这使得记忆易于理解和机器调用。 LLM-as-a-Judge: 使用一个LLM来自动判断任务轨迹的成败，为后续的记忆提取提供“对错标签”。 差异化记忆提取: 针对成功和失败的轨迹，使用不同的prompt指令进行提取。对成功的，提炼“可复用的策略”；对失败的，总结“应吸取的教训和预防措施”。 闭环学习: 形成了“检索-执行-提取-巩固”的完整闭环，让Agent的知识库在每次任务后都能得到更新。 优势: 相比Synapse（只存轨迹）和AWM（只存成功工作流），REASONINGBANK的记忆泛化能力更强，因为它存储的是高层策略而非具体操作；同时它更鲁棒，因为它从失败中学习，形成了“避坑”指南。 MATTS (记忆感知的测试时扩展): 新思路: 将“测试时扩展”从一种“暴力破解”式的计算力投入，转变为一种“有策略的探索”，并利用这个过程来创造更高质量的记忆。 具体方法: 并行扩展 (Parallel Scaling): 同时生成多个轨迹，然后进行“自我对比 (self-contrast)”，让模型比较这些轨迹的优劣，从而提炼出在多次尝试中都稳定有效的核心策略。 串行扩展 (Sequential Scaling): 对单次生成的轨迹进行多轮“自我优化 (self-refinement)”，模型会反复审视并修正自己的推理过程，这个修正过程本身就包含了宝贵的反思，可以被提炼成记忆。 优势: MATTS极大地提升了计算资源的利用效率。它不是简单地增加尝试次数，而是利用这些尝试来产生“对比信号”，这对于机器学习和知识提炼来说是信息量最密集的。它使得“花更多的计算力”能够直接转化为“更高质量的记忆”，从而实现了性能的飞跃。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且严谨的实验来验证其方法的有效性、通用性和鲁棒性。\n实验设计: 多基准、多场景评估: WebArena: 模拟真实的网页浏览任务（购物、论坛、代码管理），测试Agent在复杂Web环境中的执行能力。 Mind2Web: 专门测试Agent在不同网站、不同任务类型间的泛化能力。 SWE-Bench-Verified: 一个高难度的软件工程基准，要求Agent自主修复真实GitHub仓库中的bug。 这确保了实验结果不是在某个特定任务上的“过拟合”。 多模型骨干验证: 实验不仅使用了一种LLM，而是横跨了Gemini-2.5家族（Flash, Pro）和Claude-3.7-sonnet，证明了REASONINGBANK的有效性不依赖于特定的模型架构，具有良好的通用性。 多维度量化指标: 有效性 (Effectiveness): 任务成功率（Success Rate, SR）。 效率 (Efficiency): 完成任务所需的平均交互步骤（Step）。 消融研究 (Ablation Study): 这是实验设计的点睛之笔。论文系统性地分析了各个组件的贡献： 对比REASONINGBANK与多个基线（No Memory, Synapse, AWM）。 在Figure 7中，专门对比了“只使用成功轨迹”和“同时使用成败轨迹”的REASONINGBANK，用数据证明了从失败中学习的巨大价值。 在Figure 4和5中，详细对比了不同记忆机制下，测试时扩展（MATTS）的效果，清晰地展示了记忆与扩展的协同效应。 关键数据和结果: 一致的优越性: 在所有数据集和模型上，REASONINGBANK的成功率都最高，交互步骤最少。例如，在SWE-Bench上，使用Gemini-2.5-pro时，REASONINGBANK的bug修复率（Resolve Rate）为57.4%，高于No Memory的54.0%（见Table 2）。 失败经验的价值: Figure 7的结果极具说服力。对于REASONINGBANK，加入失败轨迹后，成功率从46.5%提升到了49.7%。而对于AWM，加入失败轨迹反而导致性能从44.4%下降到42.2%，因为它不知道如何处理失败信息。这凸显了REASONINGBANK设计的优越性。 协同效应的量化: Figure 4显示，对于无记忆基线，将扩展因子k从1提升到5，成功率仅从39.0%艰难爬升到42.2%。而对于有REASONINGBANK加持的MATTS，成功率则从49.7%大幅提升至55.1%，增长斜率远超前者，直观地证明了“1+1\u0026gt;2”的协同效应。 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文打开了一扇通往“自进化Agent”的大门，未来有许多激动人心的方向和挑战。\n值得探索的问题和挑战: 复合式记忆 (Compositional Memory): 目前，记忆条目是独立检索和使用的。未来可以研究如何让Agent组合（compose）多个记忆条目来解决一个更复杂的、多阶段的任务。这需要Agent具备更高层次的规划和推理能力。 先进记忆架构 (Advanced Memory Architectures): 可以借鉴认知科学，构建更复杂的记忆系统，例如区分短期工作记忆（working memory）、中期情景记忆（episodic memory）和长期语义记忆（semantic memory）。这需要更复杂的记忆巩固（如睡眠巩固）、遗忘和检索机制。 记忆的生命周期管理: 当记忆库变得非常庞大时，如何处理过时、错误或相互冲突的记忆？需要研究记忆的遗忘机制（forgetting）、合并机制（merging）以及可信度评估。 从个体学习到群体智能: 如何设计一个机制，让多个Agent可以共享和交换它们的REASONINGBANK？这可以极大地加速学习过程，形成一个“群体智慧大脑”。 可能催生的新技术和投资机会: Agent记忆即服务 (Memory-as-a-Service): 可能会出现专门为AI Agent提供持久化、自进化记忆能力的云服务平台。企业可以将其Agent接入该平台，使其具备学习能力。 Agent开发与运维（AgentDevOps）: 围绕如何管理、评估、版本控制和优化Agent的REASONINGBANK，可能会催生一套全新的工具链和最佳实践。 垂直领域的“专家Agent”: 在金融、法律、医疗等专业领域，可以通过让Agent在该领域内持续交互和学习，利用REASONINGBANK培养出具备高度专业化知识和推理能力的“AI专家”，这具有巨大的商业价值。 新一代AI芯片架构: 频繁的、基于语义的记忆检索和更新操作，可能对未来的AI硬件提出新的要求，催生为Agent记忆系统优化的芯片架构。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度来看，仍然存在一些潜在的不足和待验证之处。\n对“裁判”的高度依赖: 整个记忆提取的质量，始于LLM-as-a-Judge对任务成败的判断。如果这个“裁判”LLM本身能力不足或存在偏见，它可能会做出错误的判断（例如，把一个失败的任务标记为成功），从而向REASONINGBANK中注入“有毒”的、错误的知识，长期来看可能导致Agent的能力退化。 记忆检索和巩固机制过于简化: 论文为了凸显核心贡献，有意简化了检索（基于embedding的相似度搜索）和巩固（直接追加）的机制。但在真实的长期运行环境中，这种简单机制可能会遇到瓶颈。比如，语义相似但策略完全相反的两个任务，可能会导致检索混乱。简单的追加也会导致记忆库无限膨胀，效率降低。 记忆库的可扩展性问题 (Scalability): 论文验证了Agent能力的扩展性，但记忆库本身的可扩展性并未深入探讨。当交互经验达到百万甚至千万级别时，目前基于JSON文件和向量搜索的方案是否还能高效工作，是一个未知数。 泛化与“过拟合”的权衡: Agent是否有可能过度依赖其记忆，形成一种“思维定势”，从而在新颖的、需要创造性解决方案的任务上表现不佳？虽然实验展示了良好的泛化，但在更长的时间尺度上，如何平衡“利用已知经验（exploitation）”和“探索未知方案（exploration）”仍然是一个开放性问题。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极富价值的启示。\n重点学习的核心思想: 拥抱失败，视其为宝贵数据: 在构建任何学习系统时，不要轻易丢弃失败的案例。失败数据中蕴含的“边界条件”和“反面教材”，对于构建一个鲁棒的系统至关重要。思考一下，在您的工作中，哪些被丢弃的“负样本”可以被重新利用来提炼“避坑指南”？ 追求知识的抽象和升维: 不要满足于存储原始数据。真正的价值在于从数据中提炼出更高层次的、可迁移的“原则”、“模式”或“策略”。这启发我们，在做数据分析或模型训练时，应该增加一个“知识提炼”的环节。 构建协同效应的“飞轮”: 寻找系统中两个可以相互促进的组件，并设计一个机制让它们形成正反馈循环。本文中“记忆”和“扩展”的协同就是一个绝佳范例。思考一下，您的系统中是否存在类似的潜在“飞轮”，可以通过巧妙的设计让它们相互增强？ 补充的背景知识: Agent基础框架: 建议了解 ReAct (Reason and Act) 框架，它是当前许多LLM Agent的基础工作模式，本文的Agent也基于此构建。 测试时计算（Test-Time Compute）: 了解一下 Best-of-N Sampling、Self-Consistency 等概念，它们都属于在推理阶段投入更多计算来提升性能的技术，是理解MATTS的基础。 In-Context Learning (上下文学习): 理解大模型是如何利用prompt中的示例（exemplars）来完成任务的，因为Agent的记忆检索回来后，本质上也是作为一种特殊的示例注入到模型的上下文中。 希望这份详尽的解读能够帮助您深入理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-10-17T00:00:00Z","permalink":"/reasoningbankscaling-agent-self-evolving-with-reasoning-memor/","title":"ReasoningBank：Scaling Agent Self-Evolving with Reasoning Memor"},{"content":" 思考时间和性能之间有一个平衡，超过这个平衡，就算增加思考时间性能也不会有大的提升了。 RL相对于SFT来说可以提高泛化能力，SFT之后out-domain数据集性能反而下降。\n课程学习如果是针对同一个数据集，即同分布的情况，先训练简单题再逐步增加难度会提高性能。但如果是两个难度不同的数据集，并不一定能提高性能，这可以归因于分布偏移。因为两种数据的推理路径可能大相径庭。此外学习率也是个重要的超参数。 熵的总体是下降趋势，但极值的数量增加，这说明RL训练会让模型某些地方更不确定。\n","date":"2025-10-13T00:00:00Z","permalink":"/skywork-r1v3-technical-report/","title":"Skywork-R1V3 Technical Report"},{"content":" image.png 这篇论文聚焦于一个日益重要且极具挑战性的问题：当一个由多个大模型智能体（LLM Agents）组成的复杂系统在执行任务时失败了，我们如何能自动、精准地定位到是哪个智能体的哪一步操作导致了最终的失败？这一定位过程，论文称之为智能体系统失败归因（agentic system failure attribution）。\n随着大模型技术的发展，我们不再满足于单一模型的单打独ت斗，而是开始构建由多个“各司其职”的智能体协同工作的复杂系统，例如让一个智能体负责规划，一个负责编码，一个负责测试，共同完成软件开发任务。这种“智能体社会”的模式极大地提升了AI系统的能力上限，但同时也带来了新的“烦恼”：系统变得异常脆弱，一个微小的错误就可能导致整个任务链的崩溃。而传统的调试方法依赖于人类专家去阅读冗长的执行日志，耗时耗力且效率低下。论文开篇就尖锐地指出，即便是当前最顶尖的推理大模型，在处理这类归因任务时，准确率也“惊人地低下，通常低于10%”。\n这正是本篇论文的核心贡献所在。它不仅仅是提出了一个模型，而是构建了一整套自动化、可扩展的解决方案，旨在从根本上改变我们诊断和修复智能体系统失败的方式。论文的核心贡献可以概括为两个层面：一个创新的数据生成流水线和一个高效的失败追踪模型。\n首先，是名为 AgenTracer 的自动化标注流水线。研究者们敏锐地意识到，解决失败归因问题的关键瓶颈在于缺乏大规模、高质量的“失败案例”标注数据。手动标注成本高昂，且难以覆盖多样的失败模式。为此，他们设计了一套极其巧妙的自动化数据工厂，其运作方式包含两大核心技术：\n反事实回放（Counterfactual Replay）：这个概念听起来很学术，但原理很直观。对于一个已知的失败任务轨迹，系统会像“时光倒流”一样，从第一步开始，用一个“全知”的分析器智能体（analyzer agent）提供的正确操作（Oracle guidance）逐一替换掉原始操作，然后重新运行后续步骤。一旦替换到某一步后，整个任务从失败转向成功，那么这一步就被认定为导致失败的关键错误（decisive error）。 \u0026gt; 正如论文中所述：“…the earliest action in the trajectory whose correction is sufficient to steer the system from failure to success.” 这种方法通过模拟“如果当时这么做了，结果会怎样？”的反事实情景，精准地定位了错误的根源。\n程序化故障注入（Programmatic Fault Injection）：仅仅分析失败案例还不够，为了扩充数据多样性并保证标注的绝对精准，AgenTracer还对成功的任务轨迹“主动下毒”。它会有选择地在成功轨迹的某个步骤中，通过一个扰动算子（perturbation operator），程序化地引入一个错误（例如，修改代码中的一个关键参数，或提供一个错误的信息），从而“人为”地创造出一个新的失败轨迹。由于这个错误是我们亲手植入的，因此它的位置和责任智能体是100-percent确定的。 \u0026gt; 论文中这样描述其核心思想：“Its core principle is to take a known-good trajectory and programmatically introduce a fault, thereby creating a synthetic failure instance where the decisive error is known by construction.”\n通过这两项技术，AgenTracer流水线能够大规模、低成本地生成包含超过2000个高质量标注的失败轨迹数据集——TracerTraj。这为后续模型的训练提供了前所未有的丰富“养料”。\n其次，在拥有了强大的数据集后，论文推出了一个轻量级但性能卓越的失败追踪模型——AgenTracer-8B。它并非从零开始训练，而是基于QWEN3-8B模型进行微调。其训练过程的精髓在于采用了多粒度强化学习（multi-granular reinforcement learning）。这意味着奖励（reward）的设计非常精细，不仅仅是看最终是否找对了错误，而是同时评估两个层面的准确性：\n智能体层面（Agent-Level）：是否正确识别了该为失败负责的智能体？ 步骤层面（Step-Level）：是否精确定位了出错的具体步骤？ 这种多粒度的奖励机制，特别是对于步骤层面，还引入了高斯核函数，使得预测的步骤离真实错误步骤越近，获得的奖励越高。这就像在射箭比赛中，不仅命中靶心才得分，靠近靶心的区域也能获得相应分数，从而为模型提供了更平滑、更有效的学习信号。\n最终，AgenTracer-8B在实验中取得了惊人的成果。在公开的Who\u0026amp;When基准测试中，它的表现全面超越了体量远大于它的业界巨头，如Gemini-2.5-Pro和Claude-4-Sonnet。例如，在Who\u0026amp;When（automated）测试集上，AgenTracer-8B的步骤级准确率（step-level accuracy）达到了42.86%，而Gemini-2.5-Pro仅为29.52%。如下图1所示，无论是在agent-level还是step-level，AgenTracer-8B（深蓝色柱）都展现了明显的优势。\n图1：AgenTracer-8B与业界领先模型的性能对比。数据显示，无论是在agent-level还是step-level的归因任务上，AgenTracer-8B均表现出色。\n更重要的是，论文验证了AgenTracer的实用价值。它不仅仅是一个能“诊断问题”的工具，更能“治病救人”。研究者将AgenTracer-8B生成的诊断反馈提供给其他主流的多智能体系统（如MetaGPT、MaAS），发现这些系统在接收到反馈后，能够在后续任务中进行自我修正，性能提升了4.8%至14.2%。这证明了AgenTracer能够赋能AI系统实现自我纠错和自我进化，为构建更鲁棒、更智能的AI系统铺平了道路。\n总而言之，这篇论文通过“自动化数据生成+精细化模型训练”的范式，优雅且高效地解决了LLM智能体系统中的关键“调试”难题。它不仅在学术上树立了新的技术标杆，更展示了巨大的实际应用潜力，是“AI for AI”领域一次非常重要的探索和突破。\n接下来，我将按照您提出的六个问题，进行更详细的解读。\n论文的研究目标、解决的问题及其行业意义 研究目标： 论文的核心研究目标是实现LLM多智能体系统的自动化失败归因。具体来说，就是当一个由多个智能体协作的任务失败时，能够自动、准确地识别出是哪个（Who）智能体在哪一步（When）犯下了导致任务失败的关键错误。 解决的实际问题： 解决了当前多智能体系统开发和运维中的一个核心痛点：调试困难。随着系统复杂性增加，任务执行链条变长，日志信息爆炸，人工排查错误变得极其低效且不可靠。正如论文引言中提到的，UC Berkeley的一项研究发现，流行的多智能体框架失败率高达86.7%，这严重阻碍了其在真实世界中的可靠应用。 该研究为AI系统提供了自我审视和自我改进的能力。一个能够准确理解自己为何失败的系统，才有可能在下一次尝试中做得更好。 行业意义： 提升AI系统可靠性：这是将多智能体系统从“实验室玩具”推向“工业级应用”的关键一步。只有当系统足够稳定、可预测、易于维护时，企业才敢于将其部署在金融、医疗、自动驾驶等关键业务上。 加速开发迭代效率：自动化的失败归因工具可以被集成到AI应用的开发流程中，成为“AI世界的调试器（Debugger）”，极大地缩短开发和测试周期。 赋能AI的自我进化：论文证明了精确的失败归因可以作为高质量的反馈信号，驱动智能体系统进行自我修正。这为实现能持续学习、自主进化的高级人工智能系统提供了新的路径。 论文提出的新思路、方法或模型及其优势 新的思路与方法： 核心思路：将失败归因问题从一个纯粹的推理任务，转化为一个由数据驱动的学习任务。研究者认为，当前大模型推理能力不足的根源在于缺乏针对该任务的专业训练数据。因此，工作的重心放在了如何自动化地构建这样一个高质量数据集。 数据生成方法： 反事实回放 (Counterfactual Replay)：一种巧妙的归因方法，通过模拟“修正-重跑”的过程来定位最早的关键错误点。这比简单地让模型阅读日志进行“静态分析”要可靠得多。 程序化故障注入 (Programmatic Fault Injection)：一种创造性的数据增强方法，它将成功的案例转化为带有精确标签的失败案例。这不仅极大地扩充了数据集规模，而且保证了标注的“绝对正确性”，避免了分析器智能体可能带来的标注噪声。 模型训练方法： 多粒度强化学习 (Multi-granular RL)：设计了同时奖励智能体和步骤两个维度的准确性的奖励函数。特别是引入高斯核来平滑步骤奖励，为模型提供了更丰富的学习信号，避免了“全对或全错”的稀疏奖励问题，从而稳定了训练过程。 与之前方法的比较与优势： 相较于人工分析：优势是自动化、高效、可扩展。人工分析无法应对海量、复杂的智能体交互日志。 相较于之前依赖LLM直接推理的方法（如论文中提到的Who\u0026amp;When研究）： 性能优势：AgenTracer-8B作为一个仅有8B参数的微调模型，其性能远超GPT-4、Gemini-2.5-Pro等千亿参数的通用大模型。这证明了专业数据微调在特定任务上比通用模型的零样本/少样本推理更有效。 数据优势：之前的工作（如MAST, Who\u0026amp;When）依赖于小规模（仅一两百个样本）、人工标注的数据集，规模小且成本高。AgenTracer流水线能够生成数千个高质量样本，这是其成功的基石。 \u0026gt; 论文中提到：“To the best of our knowledge, MAST and Who\u0026amp;When contain only 200 and 127 manually annotated multi-agent failure trajectories, respectively, offering limited scale for systematic evaluation.” 相较于传统软件调试：传统调试依赖于断点、日志打印等，而智能体系统的失败往往是语义层面、逻辑层面的，错误表现更隐晦。AgenTracer直接在语义和行为层面进行归因，更适合AI系统的特点。 实验设计、数据与结果分析 实验设计： 数据集构建：首先，通过AgenTracer流水线，从6个主流多智能体系统（如MetaGPT, AutoGen）在6个基准测试（如MBPP+, GAIA）上的运行轨迹中，创建了包含超过2000个标注样本的TracerTraj数据集。 基准测试：为了验证模型的泛化能力，论文在两个截然不同的测试集上进行了评估： Who\u0026amp;When Benchmark：一个公开的、与训练数据来源不同的第三方基准，用于检验模型对未见过的系统和任务的归因能力。 TracerTraj的留出测试集：从自建数据集中按9:1划分出的测试集，用于评估模型在同分布数据上的性能上限。 评估指标： Agent-level accuracy: 识别出错智能体的准确率。 Step-level accuracy: 定位出错步骤的准确率。 对比基线：选取了从小型（LLAMA-3.2-3B）、中型（QWEN3-32B）到大型（GPT-4.1, Gemini-2.5-Pro, Claude-4-Sonnet）的一系列代表性模型作为对比。 应用验证：设计了一个迭代改进实验，将AgenTracer-8B的诊断反馈提供给三个不同的多智能体系统，观察它们在后续多轮任务中的性能变化，并与Self-Refine、CRITIC等其他反馈方法进行对比。 关键数据与结果： 在Who\u0026amp;When上的卓越表现： \u0026gt; 如Table 1所示，在Who\u0026amp;When (handcraft)子集上，AgenTracer-8B的agent-level准确率达到69.10%，而Claude-4-Sonnet为56.90%，GPT-4.1为43.10%。Step-level准确率达到20.68%，同样领先于所有对手。这有力地证明了其强大的归因能力。 在TracerTraj上的性能： \u0026gt; 如Table 2所示，在更贴近训练分布的TracerTraj (agentic)子集上，AgenTracer-8B的step-level准确率达到了36.17%，显著高于其8B的基础模型QWEN3-8B（13.49%），也高于DEEPSEEK-R1（27.13%）和Gemini-2.5-Pro（18.60%）。 赋能系统自我进化： \u0026gt; 如Figure 3所示，在MaAS+MATH-500任务上，经过3轮迭代，AgenTracer-8B的反馈使得系统性能提升了约14.2%。相比之下，基于GPT-4.1的CRITIC方法反而导致性能下降。这直观地展示了AgenTracer作为外部反馈的有效性。 图3：AgenTracer-8B与其他反馈方法（Self-Refine, CRITIC）对主流多智能体系统性能影响的对比。AgenTracer-8B（蓝色）能够带来持续的性能提升。\n未来探索方向、挑战与机遇 值得探索的问题与挑战： 处理更微妙的“因果链”：当前的“最早关键错误”定义可能过于简单。有时，失败是多个微小偏差累积的结果，或是某个深层次的策略性失误导致的。未来的研究需要探索更复杂的因果归因模型。 零样本泛化能力：AgenTracer虽然表现优异，但仍依赖于从多个已知系统中收集数据。如何训练一个能够对全新架构的未知智能体系统进行高效归因的“通用诊断器”，是一个巨大的挑战。 实时归因与主动预防：目前的工作是事后分析。未来的方向可以是在系统运行过程中实时监测，预测潜在的失败风险，并主动进行干预，实现从“被动调试”到“主动防御”的转变。 自动修复（Auto-Fixing）：在准确定位错误之后，下一步自然是自动生成修复方案。将失败归因与代码生成、规划修正等能力结合，构建一个端到端的“诊断-修复”闭环系统，是极具价值的研究方向。 可能催生的新技术和投资机会： Agentic MLOps平台：围绕多智能体系统的开发、测试、监控和运维，将会出现一批新的创业公司，提供类似于软件工程领域DevOps的工具链。失败归因将是这类平台的核心功能之一。 AI可靠性与安全（AI Reliability \u0026amp; Safety）：随着AI系统在关键领域的应用，对其可靠性和安全性的需求将急剧增加。专注于提供AI系统“保险”、“审计”、“调试”服务的技术和公司将迎来巨大的市场机会。 高质量合成数据服务：AgenTracer的流水线证明了高质量合成数据在训练专用AI模型上的巨大价值。未来可能会出现专门为训练各类“AI for AI”模型（如AI裁判、AI监督、AI调试器）提供定制化合成数据服务的商业模式。 论文的不足与待验证之处 从批判的视角审视，这篇论文虽然非常出色，但仍存在一些值得探讨的方面：\n对“分析器智能体”的依赖： 在“反事实回放”中，系统依赖一个analyzer agent来提供“正确的”修正操作。论文中该角色由DEEPSEEK-R1扮演。这个“神谕（Oracle）”的性能上限直接决定了数据标注的质量和覆盖面。如果神谕本身出错或提供了一个非最优的修正，可能会导致标注偏差。论文对此“神谕”自身的局限性讨论不足。 “最早错误”定义的局限性： 论文将关键错误定义为时间上最早的、修正后能导致成功的错误。在某些情况下，这个错误可能只是一个表层症状。一个更根本的、位于任务链后端的战略性错误，可能对系统改进更有启发意义，但由于不符合“最早”原则，会被该方法忽略。 合成失败与真实失败的分布差异： “程序化故障注入”生成的失败案例虽然标注精确，但其模式可能相对简单，与真实世界中由复杂交互、环境变化、模型幻觉等导致的涌现性失败（emergent failures）在分布上可能存在差异。模型在合成数据上训练出的能力能否很好地泛化到更复杂、更“诡异”的真实失败场景，还需要更多验证。 评估基准的单一性： 尽管使用了Who\u0026amp;When和自建的TracerTraj，评估的场景仍然局限于编码、数学和通用智能体任务。在更开放、交互更复杂的环境（如机器人控制、多智能体博弈）中，AgenTracer的有效性还有待检验。 对读者的启发与建议 重点学习的创新想法： 数据驱动的范式转换：最重要的启发是，在面对一个看似困难的AI高级认知任务（如调试）时，可以尝试将其转化为一个数据问题。与其执着于提升模型的通用推理能力，不如思考如何为这个特定任务构建一个高质量、大规模的专属数据集。 “反事实”与“主动注入”的数据生成思想：这套自动化数据标注的组合拳非常具有普适性。在其他需要对AI行为进行监督和评估的领域（如对齐、可解释性），这套方法论都具有极高的借鉴价值。 面向任务的精细化奖励设计：在强化学习中，根据任务的结构化输出，设计多粒度的、平滑的奖励函数，是提升模型性能和训练稳定性的关键技巧。 补充的背景知识： 多智能体系统（Multi-Agent Systems）：建议了解一下论文中提到的MetaGPT、AutoGen等框架的基本原理，理解它们是如何通过角色分配和通信协作来完成复杂任务的。 大模型作为智能体（LLM as Agents）：熟悉ReAct (Reason + Act)等提示框架，了解大模型如何进行工具调用、与环境交互。 强化学习基础：了解策略梯度方法（Policy Gradient），特别是PPO算法，因为论文中使用的GRPO是其变体。理解奖励建模（Reward Modeling）的基本概念。 因果推断（Causal Inference）：虽然论文没有深入形式化的因果理论，但了解“反事实”等基本概念，有助于更深刻地理解其方法论的内核。 ","date":"2025-09-23T00:00:00Z","permalink":"/agentracerwho-is-inducing-failure-in-the-llm-agentic-systems/","title":"AGENTRACER：WHO IS INDUCING FAILURE IN THE LLM AGENTIC SYSTEMS"},{"content":"deepresearch的工作，解决的问题是ds训练中数据短缺的问题，提出的创新主要是数据合成，方法类似于逆向工程和WebExplorer：Explore and Evolve for Training Long-Horizon Web Agents，本文的方法是在知识图谱上面随机探索，形成复杂推理路径，并且对实体进行模糊化，最后将路径作为问题，路径终点作为答案，并且还过滤难度，过于简单的直接抛弃。\nimage.png 首先，让我们对这篇论文进行一个全面的、深入的解读。\n这篇论文的核心目标是提升大型语言模型（LLMs）作为深度搜索智能体（deep search agents）的能力，使其能够解决那些需要跨越多个信息来源、进行长程推理才能找到答案的复杂现实世界问题。作者观察到一个关键的行业痛点：尽管通过集成浏览工具，LLMs展现出巨大潜力，但现有的开源模型在这方面远远落后于像OpenAI、Google等公司的顶尖闭源模型。论文将这种差距归因于两个主要因素：一是缺乏足够困难、需要深度搜索的监督式训练数据；二是缺少有效的多轮强化学习（multi-turn RL）训练方法来培养模型的长程推理（long-horizon reasoning）能力。\n为了解决这两个挑战，论文提出了一个名为DeepDive的综合框架，其创新性主要体现在两大支柱上：\n第一大支柱是创新的自动化数据合成策略。 传统问答（QA）数据集，如HotpotQA，问题结构相对简单，通常通过几次直接搜索就能解决，无法有效训练模型处理“难找（hard-to-find）”信息的能力。而手动构建高质量的深度搜索数据集又极其昂贵且耗时。DeepDive另辟蹊径，提出了一种利用知识图谱（Knowledge Graphs, KGs）来自动生成大规模、高质量、高难度的深度搜索问答对的方法。\n这个过程非常精巧，正如论文图3所示，它分为几个步骤： 1. 在知识图谱上进行随机游走（Random Walk on KG）：从一个初始节点出发，在图谱中随机游走多步（例如论文中设置的k\u0026gt;5步），形成一条包含多个实体和关系的复杂推理路径。这天然地构建了问题所需的多跳（multi-hop）逻辑链。 2. 模糊化属性丰富的路径（Blur Attribute-Rich Path）：为了增加问题的模糊性和难度，防止模型通过简单的关键词匹配找到答案，DeepDive利用一个强大的LLM（如Gemini-2.5-Pro）对路径中节点的关键属性进行“模糊化”处理。例如，将具体的日期“1948年”模糊成“20世纪40年代末”，将具体的人名或地名用其特征来描述。这创造了论文所说的“模糊实体（blurry entity）”，迫使模型必须通过推理和综合多个信息源来确定实体。 3. 合成问答对并进行难度过滤：最后，LLM将这条模糊化的路径转化为一个自然语言问题，并将路径终点的某个属性作为答案。更关键的是，为了确保数据的“足够难”，他们设计了一个自动化难度过滤器：使用一个前沿模型（如GPT-4o）加上基础的搜索工具尝试回答这些生成的问题。如果该模型在几次尝试内能成功回答，这个问题就被认为是“太简单”而被丢弃。只有那些顶级模型也难以解决的问题，才会被保留下来用于训练。\n通过这种方式，DeepDive成功地生成了一个包含3090个高质量深度搜索问答对的数据集，这些数据不仅答案可验证，而且在逻辑深度和信息模糊度上都远超传统数据集。\n第二大支柱是端到端的多轮强化学习（End-to-End Multi-Turn RL）框架。 仅仅有好的数据进行监督微调（SFT）是不够的。SFT能教会模型模仿“好的搜索轨迹”，但无法让模型学会在真实环境中自主探索、试错和规划。因此，DeepDive采用了多轮强化学习来训练智能体。\n与单轮RL（模型只输出一次最终答案）不同，多轮RL允许智能体在一个模拟的网页浏览环境中进行一系列的“思考 -\u0026gt; 调用工具 -\u0026gt; 观察结果”的迭代。这个过程会形成一个完整的决策轨迹。DeepDive采用了GRPO（Group Relative Policy Optimization）算法，这是一种先进的策略优化算法。模型在每一步决策（是搜索、点击链接还是给出最终答案）后，都会与环境交互，直到最终输出答案。\n为了有效引导模型的学习，DeepDive设计了一个非常严格的奖励机制（Strict Rewards）。一个轨迹只有在同时满足以下两个条件时才能获得+1的奖励，否则奖励为0： 1. 轨迹中的每一步，无论是思考链（Chain-of-Thought）还是工具调用，其格式都必须完全正确。 2. 最终提交的答案必须与标准答案完全一致（由LLM-as-Judge进行判定）。\n这种严苛的二元奖励机制，结合“一旦出错立即终止并给予0奖励”的早停机制（early exit mechanism），极大地提升了样本的有效性，确保模型学到的是鲁棒且可靠的搜索策略，而不是通过“奖励黑客（reward hacking）”来投机取巧。\n最终，DeepDive将这两大支柱结合，先用合成的高质量数据对基础模型（如QwQ-32B）进行SFT，使其具备初步的深度搜索能力，然后再通过多轮RL进行深度优化。实验结果非常亮眼：在极具挑战性的BrowseComp基准测试上，DeepDive-32B模型取得了14.8%的准确率，不仅远超之前的开源模型（如WebSailor-32B的10.5%），甚至超过了配备了搜索工具的GPT-4o（1.9%）和Claude-3.7-Sonnet（4.5%）等顶尖闭源模型，创造了开源模型在该领域的新纪录。如论文图1所示，RL训练相比仅SFT训练，在四个主流深度搜索基准上都带来了一致且显著的性能提升（例如在BrowseComp上从9.5%提升至14.8%）。这雄辩地证明了DeepDive框架的有效性和巨大潜力。\n论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是提升开源大型语言模型（LLMs）在复杂、长程、多跳信息检索与推理任务中的能力，使其成为一个高效的“深度搜索智能体”。\n解决的实际问题：\n弥合开源与闭源模型在高级搜索能力上的差距：目前，如OpenAI的DeepResearch等闭源模型在处理需要深度网络搜索和信息综合的复杂问题上表现出色，而开源社区缺乏同等能力的模型。 解决深度搜索训练数据的稀缺性问题：训练强大的搜索智能体需要大量高质量、高难度的问答数据，而这类数据难以通过人工方式大规模构建。 探索超越监督微调（SFT）的更有效的训练范式：SFT只能让模型模仿静态的专家轨迹，而真实的搜索是一个动态决策过程。论文旨在探索如何通过强化学习让模型学会自主规划和执行长程搜索任务。 对行业发展的重要意义： 这个问题对于AI行业的发展至关重要。一个强大的深度搜索智能体是实现通用人工智能（AGI）的关键一步，它不仅仅是一个“问答机器”，更是一个“自动化研究助理”。其意义体现在：\n加速科学研究与创新：研究人员可以利用这样的智能体快速进行文献综述、寻找交叉学科的解决方案、验证假设，极大地提升科研效率。 变革商业决策与分析：在金融、市场、法律等领域，深度搜索智能体可以从海量公开信息中挖掘深层联系，为企业提供更精准的商业洞察和风险预警。 推动AI Agent技术的成熟：深度搜索是Agent能力的一个典型体现。解决了这个问题，相关的技术（如工具使用、长程规划、动态环境交互）可以迁移到更广泛的Agent任务中，如软件开发、自动化客服、个人助理等。 促进开源社区的繁荣：通过提供一套可行的、高效的训练方法和高质量的数据集，本文的工作降低了研究门槛，能激励更多开发者参与到构建强大开源Agent的行列中，避免技术被少数巨头垄断。 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心是DeepDive框架，它包含两个关键的创新思路：基于知识图谱的自动化数据合成和端到端的多轮强化学习训练。\n新的思路与方法： 从“无结构文本”转向“结构化知识”来生成训练数据： \u0026gt; To address these challenges, we present DeepDive to advance deep search agents. First, we introduce a strategy to automatically synthesize hard-to-find questions from open knowledge graphs (KGs). 之前的方法多依赖于已有的QA数据集（如HotpotQA）或人工标注，这些数据要么过于简单，要么成本高昂。DeepDive巧妙地利用知识图谱（KGs）这种半结构化的数据源。 特点与优势： 保证逻辑深度和可验证性：KG中的实体和关系是明确的，通过在KG上进行多步随机游走，可以天然地构建出具有复杂逻辑链且答案确凿无误的QA对。 可控的难度生成：通过模糊化处理和自动化难度过滤器，可以系统性地生成远超现有数据集难度的问题，精准地“靶向”训练模型的深度搜索能力。 可扩展且成本低：该方法是完全自动化的，可以轻松扩展到不同领域、不同语言的知识图谱上，大规模生成训练数据。 从“模仿学习”转向“在线策略学习”来训练智能体： \u0026gt; Second, we apply end-to-end multi-turn RL to enhance LLMs’ long-horizon reasoning with deep search. 之前很多Agent的训练严重依赖监督微调（SFT），即模仿专家轨迹。DeepDive则更进一步，采用多轮强化学习（Multi-Turn RL）进行端到端优化。 特点与优势： 学习动态决策策略：RL让模型在与环境的持续交互中学习一个最优的行动策略（policy），而不是死记硬背静态的轨迹。这使得模型在面对训练时未见过的、更复杂多变的情况时，具有更好的泛化能力和鲁棒性。 培养长程规划能力：多轮RL的奖励是在任务最终完成时才给予，这迫使模型必须学会为了一个长远目标（找到正确答案）而规划一系列连贯的行动，有效提升了其长程推理能力。 严格的奖励设计避免“投机取巧”：通过对过程格式和最终结果都进行严格要求的奖励函数，确保了模型学到的是真正解决问题的能力，而不是利用环境或奖励函数的漏洞。 图示：论文图3和图5的核心思想，展示了从KG生成数据到多轮RL训练的完整流程。 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来全面验证DeepDive框架的有效性。\n实验设计： 基准测试（Benchmarking）：在四个公认的、具有挑战性的深度搜索基准上进行评测：BrowseComp、BrowseComp-ZH（中文版）、Xbench-DeepSearch 和 SEAL-0。 模型对比（Baselines Comparison）：将DeepDive模型与两类模型进行对比： 闭源模型：包括GPT-4o, Claude-3.7-Sonnet等，以及它们通过函数调用（function call）获得浏览能力后的版本。 开源模型：包括当时最先进的开源模型如Qwen, DeepSeek-R1，以及专门为搜索任务设计的Agent模型如WebSailor, Search-o1。 消融研究（Ablation Study）：为了验证框架中每个组件（高质量数据和RL训练）的必要性，论文进行了详细的消融实验。例如，比较仅使用HotpotQA数据训练和使用DeepDive合成数据训练的效果；比较仅进行SFT和进行SFT+RL训练的效果。 泛化能力测试：在一些相对简单的搜索任务（如HotpotQA, WebWalker）上测试DeepDive，检验其在难任务上学到的能力是否能迁移到简单任务上。 扩展性分析（Scaling Analysis）：探究模型性能与最大工具调用次数（Max Tool Calls）之间的关系，验证模型是否能通过增加推理时间（即更多的搜索步骤）来解决更难的问题。 实验数据和结果： 核心性能：在最具挑战性的BrowseComp上，DeepDive-32B取得了14.8%的准确率，创造了开源模型的新纪录。 \u0026gt; As shown in Table 1, DeepDive-32B achieved 14.8%, setting a new record for open-source models. 这个结果显著优于其他所有开源Agent，并且超过了配备工具的GPT-4o†（1.9%）和Claude-3.7-Sonnet†（4.5%）。\n模型 BrowseComp 准确率 (%) GPT-4o† 1.9* Claude-3.7-Sonnet† 4.5 WebSailor-32B 10.5* DeepDive-32B (SFT-only) 9.5 DeepDive-32B 14.8 消融研究结果：论文的表2清晰地展示了数据和RL的重要性。以BrowseComp-266子集为例：\n数据的重要性：使用论文合成数据进行SFT的模型（7.5% Acc）远超使用HotpotQA训练的模型（4.9% Acc）。 RL的重要性：在SFT的基础上增加RL训练后，模型准确率从9.5%（SFT-only）提升到12.0%，并且平均工具调用次数（#Turn）从32.7次增加到47.2次，证明RL显著增强了模型的深度搜索意愿和能力。 扩展性分析结果：论文图8显示，随着最大工具调用次数从8次增加到128次，DeepDive-32B在BrowseComp上的性能稳步提升。这表明模型学会了利用更长的推理链来解决问题，这是深度搜索能力的关键标志。\n结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 更高效的探索策略：论文提到模型存在“过度搜索（over-search）”现象。未来的研究需要探索更智能的搜索策略，比如引入信息增益（Information Gain）作为判断是否继续搜索的依据，或者让模型学会主动剪枝（pruning）不相关的搜索路径，以提高效率。 更复杂的工具使用和规划：目前的搜索工具相对简单（search, click, open）。未来可以扩展到更复杂的工具集，如代码解释器、计算器、数据库查询API等。这要求模型具备更强的动态规划（dynamic planning）和工具协同能力。 处理噪声和对抗性信息：真实的网络环境充满了错误、过时甚至恶意的信息。如何让智能体具备事实核查（fact-checking）、信息溯源（source verification）和对抗性鲁棒性（adversarial robustness）是未来一个重要的研究方向。 个性化与自适应学习：如何让Agent根据特定用户或特定领域的知识进行快速自适应，并能通过与用户的持续交互进行在线学习（online learning）和自我进化，是通往个性化智能助理的关键。 多模态深度搜索：将搜索范围从文本扩展到图片、视频、音频等多模态信息，实现真正的“全网”深度搜索。 可能催生的技术和投资机会： Agent训练即服务（Agent Training as a Service）：提供专门用于训练高级Agent的平台，包括高质量的合成数据、复杂的模拟环境和高效的RL训练框架。DeepDive的数据合成方法可以被产品化。 垂直领域的自动化研究平台：在金融、生物医药、法律等专业领域，开发专门的深度搜索智能体，作为该领域专家的“超级助理”，提供深度研究和分析服务。 下一代搜索引擎：颠覆传统基于关键词的搜索引擎，提供能够直接理解复杂、模糊问题并给出综合性、推理后答案的“答案引擎”。 AI Agent安全与对齐：随着Agent能力越来越强，确保其行为可控、可信、符合人类价值观变得至关重要。专注于Agent安全、监控和对齐技术的公司将有巨大市场。 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 搜索效率问题：论文自己承认了“过度搜索”的局限性。模型虽然效果好，但可能代价是极高的工具调用次数，这在实际应用中会带来高昂的API成本和延迟。论文缺乏对“效率”（如找到答案所需的平均步数或时间）的深入分析。 数据合成的“天花板”： \u0026gt; the upper limit of difficulty remains significantly lower than datasets like BrowseComp. 尽管KG合成的数据质量很高，但其复杂度和创造性可能仍受限于知识图谱本身的结构和LLM的模糊化能力，与人类专家精心设计的问题（如BrowseComp中的问题）相比仍有差距。 奖励机制的单一性：二元奖励（非0即1）虽然严格，但也可能过于稀疏，导致模型在训练初期难以学习。同时，它没有对“部分正确”的答案或更优的解题路径给予奖励，可能限制了模型学习的多样性。 对模型“内在推理”与“外部搜索”的解耦分析不足：模型性能的提升究竟多大程度上来自于其内在推理能力的增强，多大程度上是学会了更好地“堆砌”搜索结果？这部分还需要更细致的探究。 需要进一步验证和存疑的： 泛化到其他类型Agent任务的能力：论文证明了模型在深度搜索任务上的强大能力，但这种能力能否有效迁移到其他需要复杂规划和工具使用的任务上（例如，作为编码助手、游戏玩家）？这有待进一步验证。 对知识图谱的依赖：该方法的核心是高质量的知识图谱。如果在一个缺乏良好知识图谱的领域，这种数据合成方法是否依然有效？其效果对KG的覆盖率和质量有多敏感？ 评估方法的鲁棒性：虽然使用了LLM-as-Judge，但这种评估方式本身也可能存在偏见和不稳定性。尤其是在评估复杂推理问题时，机器裁判的准确性仍是一个开放性问题。 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新想法与启发： “数据驱动”的Agent能力构建思路：这篇论文给我们的最大启发是，构建强大Agent的核心瓶颈之一是数据。与其在模型结构上“内卷”，不如创造性地去构建一个能够持续生成高质量、高难度、多样化训练数据的“数据引擎”。这个“数据引擎”本身，就可能是一个非常有价值的产品。 将RL用于优化“策略”而非“知识”：可以将SFT和RL看作一个组合。SFT负责为模型注入基础的“知识”和“技能模仿”，而RL则在此基础上，教会模型如何在动态环境中灵活运用这些技能，形成“策略”和“智慧”。这个分层训练的思路在很多Agent任务中都值得借鉴。 “逆向工程”式的问题生成法：DeepDive从“答案和推理路径”（KG中的路径）出发，反向生成“问题”，这是一个非常聪明的想法。在很多领域，定义一个好问题比找到答案更难。我们可以借鉴这种思路，为自己的领域自动生成高质量的训练数据。 将“计算量”转化为“智能”：论文中的“测试时工具调用扩展”（test-time scaling）和“并行采样投票”表明，对于困难问题，给予模型更多的计算资源（更多的思考步骤或尝试次数）是提升性能的有效途径。这启发我们在设计Agent时，可以引入一个动态的“资源分配”机制，简单问题快速解决，复杂问题则投入更多计算。 需要补充了解的背景知识： 强化学习（Reinforcement Learning, RL）基础：特别是策略梯度方法（Policy Gradient），以及像PPO（Proximal Policy Optimization）这样的现代RL算法。理解奖励函数（Reward Function）、策略（Policy）、价值函数（Value Function）等核心概念。 知识图谱（Knowledge Graphs, KGs）：了解KG的基本构成（实体、关系、属性），以及它如何用图结构来表示世界知识。 大型语言模型作为智能体（LLM as Agents）：了解当前主流的Agent框架，如ReAct（Reason+Act），以及工具调用（Tool-Using）的基本原理。 基准测试（Benchmarks）：可以去了解一下像BrowseComp, HotpotQA, WebQSP等数据集的特点，这能帮助你更深刻地理解“深度搜索”任务的难点所在。 ","date":"2025-09-23T00:00:00Z","permalink":"/deepdiveadvancing-deep-search-agents-with-knowledge-graphs-and-multi-turn-rl/","title":"DeepDive：Advancing Deep Search Agents with Knowledge Graphs and Multi-Turn RL"},{"content":"这是去年的论文了，不过从中看到了如今search-r1的雏形，就是先提问然后调用外部工具（这里是知识大模型）给出解答，直到感觉自己信息已经掌握足够了，就进一步推理然后作答。只不过本文只是进行了sft。memory也就是掌握关键信息的环节。\nimage.png 这篇论文的精妙之处在于，它没有满足于像 链式思考 (Chain-of-Thought, CoT) 那样仅仅引导模型“多想几步”，也没有像 检索增强生成 (Retrieval-Augmented Generation, RAG) 那样完全依赖外部知识库，而是巧妙地提出了一种全新的推理范式，旨在显式地解耦和分离模型自身的参数化记忆（Parametric Memory）调用与逻辑推理（Reasoning）过程。作者们通过引入两个特殊的可学习控制令牌（control tokens）——\u0026lt;memory\u0026gt; 和 \u0026lt;reason\u0026gt;——成功地引导模型在生成答案的每一步都清晰地标示出自己正在执行的是“回忆一个事实”还是“进行一步推理”。\n为了实现这一目标，论文设计了一个非常聪明的两阶段流程。第一阶段是数据生成：他们使用一个强大的“教师模型”（如GPT-4o）来分析问题并生成解题步骤。但关键创新在于，他们要求这个教师模型对每一步进行分类，标记为需要事实知识的[memory]步骤，或需要逻辑推演的[reason]步骤。对于[memory]步骤，教师模型会进一步生成一个具体的问题（例如，对于“MMA和罗马角斗有关吗？”这个问题，一个memory步骤可能是“混合武术（MMA）的起源和特点是什么？”）。然后，另一个“知识模型”会为这些问题提供精准的事实答案。最后，这些经过验证的答案被重新填充到原始的解题步骤中，形成一份高质量、结构清晰、步骤明确的“解耦”训练数据集。这个过程，正如论文图1所示，将一个模糊的思考过程，转化为了一个结构化的行动序列。\n第二阶段是模型训练：他们使用上述生成的高质量数据集，对一个“学生模型”（如LLaMA-3.1-8B）进行指令微调 (Instruction Fine-tuning)。通过这种方式，学生模型不仅学会了如何解决问题，更重要的是，它学会了识别并使用\u0026lt;memory\u0026gt;和\u0026lt;reason\u0026gt;令牌来组织自己的“思路”。这使得模型的推理过程变得前所未有的透明和可解释。\n论文的实验结果极其有力。在StrategyQA、CommonsenseQA和TruthfulQA等多个知名基准测试中，他们的方法都显著优于传统的Zero-shot、CoT以及其他微调方法。最引人注目的成果出现在TruthfulQA数据集上，这是一个专门测试模型事实准确性的数据集。经过他们方法微调的LLaMA-3.1-8B模型，准确率达到了惊人的86.6%，甚至超过了体量远大于它、能力更强的闭源模型GPT-4o（使用CoT提示）的85.4%（见论文表1）。这证明，通过清晰地分离记忆和推理，一个中等规模的模型也能在特定任务上达到甚至超越顶级模型的性能。\n表1：主要对比实验结果 在TruthfulQA数据集上，我们的方法（Ours）增强的LLaMA-3.1-8B取得了86.6%的准确率，而GPT-4o在CoT设置下的准确率为85.4%。\n更深层次的分析揭示了该方法的内在优势。论文中的解耦分析（Decoupling Analysis）显示，他们的模型在区分“记忆”和“推理”步骤上的准确性远高于基线方法（见论文图2）。而在错误分析（Error Analysis）中，论文发现了一个关键现象：在模型答错的案例中，绝大多数错误来源于推理环节，而非记忆环节。例如，在StrategyQA数据集上，高达98.3%的错误被归因于推理失败，只有1.7%是由于知识回忆出错（见论文表7）。这一发现意义重大，它清晰地指出了未来提升模型能力的主攻方向：我们需要的不仅仅是给模型灌输更多知识，更关键的是提升它们运用这些知识进行复杂逻辑推理的能力。\n总而言之，这篇论文不仅仅是提出了一种提升模型性能的技术，更是为我们打开LLM这个“黑箱”、理解并优化其内在认知过程提供了一个全新的、强有力的框架。它将模糊的“思考”过程变成了可训练、可解释、可干预的结构化步骤，为构建更可靠、更可信的AI系统迈出了坚实的一步。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 核心研究目标: 论文的核心目标是解耦（disentangle）大型语言模型在解决复杂问题时的两个核心认知能力：记忆回忆（memory recall）和逻辑推理（reasoning）。它希望将模型模糊、一体化的推理过程分解为一系列清晰、可识别的步骤。\n想要解决的实际问题:\n不可解释性（The “Black Box” Problem）: 当前LLMs的决策过程非常不透明。当模型给出一个错误答案时，我们很难判断是它“记错了知识”（记忆问题）还是“想错了逻辑”（推理问题）。这种不确定性极大地阻碍了模型在金融、医疗、法律等高风险领域的应用。 性能瓶颈: 论文认为，记忆和推理的混淆会导致模型在处理长链条、多步骤问题时效率低下，容易出现知识遗忘（knowledge forgetting）——即在推理的后期忘记了早先回忆出的关键信息。 可靠性与可控性差: 一个无法解释其推理过程的模型是难以信任的。用户无法验证其答案的来源和逻辑，也无法有效地纠正其错误。 对行业发展的重要意义:\n构建可信AI（Trustworthy AI）: 这是通往可靠、负责任AI的必经之路。通过让模型的“思考过程”透明化，我们可以审查其逻辑，定位错误根源，从而建立用户和监管机构的信任。 提升模型效率与准确性: 清晰的结构有助于模型更好地组织信息流，减少逻辑断裂和事实遗忘，从而在复杂任务上取得更好的表现。 实现精准的模型调试与优化: 当我们能明确区分记忆错误和推理错误后，就可以进行针对性的优化。例如，如果是记忆错误，可以通过知识编辑或RAG来修正；如果是推理错误，则需要训练模型更强的逻辑能力。这使得模型维护从“玄学”变成了“科学”。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路与方法: 论文提出了一种名为“记忆-推理分解（Memory-Reasoning Decomposition）”的全新推理范式。其核心是一个包含两个阶段的流程： 基于LLM的结构化数据生成: 双模型协作: 使用一个强大的推理LLM来生成初步的推理步骤，并为每一步打上\u0026lt;memory\u0026gt;或\u0026lt;reason\u0026gt;的标签。然后，一个知识LLM负责核实并提供\u0026lt;memory\u0026gt;步骤所需的精确事实。 结构化输出: 最终生成的数据集不仅包含问题和答案，还包含一系列带有明确认知标签（是记忆还是推理）的中间步骤。 基于控制令牌的模型微调: 引入特殊令牌: 将\u0026lt;memory\u0026gt;和\u0026lt;reason\u0026gt;作为可学习的特殊令牌加入到模型的词汇表中。 引导式学习: 在微调过程中，模型被训练来预测这些令牌，从而学会自主地将复杂的任务分解为记忆调用和逻辑推理的序列。 与之前方法的比较、特点和优势: 对比方法 特点与局限性 本文方法的优势 链式思考 (Chain-of-Thought, CoT) 通过”Let’s think step by step”等提示引导模型生成中间步骤，但步骤的性质是模糊的，没有结构。 结构化与可解释性: 明确区分了每一步的认知类型，使得整个推理过程像一张清晰的“流程图”，而不仅仅是一段自由流动的文本。 检索增强生成 (RAG) 专注于从外部知识库检索信息来辅助生成，解决的是模型参数化知识不足或过时的问题。 激活内部知识: 本文方法旨在更好地组织和利用模型内部的参数化知识，使其与推理能力高效协同，而非完全依赖外部数据。 规划令牌 (Planning Tokens) 引入特殊令牌来规划任务的宏观步骤（如“分解问题”、“执行计划”），侧重于任务执行流程。 认知层面的分解: 本文的令牌作用于更底层的认知层面，区分的是“获取信息”和“处理信息”这两种基本思维活动，粒度更细，更贴近认知科学。 正如论文在引言中所指出的： \u0026gt; Despite these advancements, existing inference frameworks often operate as an opaque process without explicitly separating knowledge retrieval and reasoning steps. This makes it unclear what specific knowledge the model utilizes and how it performs reasoning, leaving the decision-making process ambiguous. 翻译：尽管取得了这些进步，现有的推理框架通常作为一个不透明的过程运行，没有明确区分知识检索和推理步骤。这使得我们不清楚模型利用了哪些具体知识以及它如何进行推理，导致决策过程含糊不清。\n本文方法正是为了解决这种“含糊不清”而设计的。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来全方位验证其方法的有效性。\n实验设计: 数据集: 选用了三个具有代表性的问答数据集： StrategyQA: 测试需要多步、隐式推理策略的复杂问题。 CommonsenseQA: 专注于常识性知识的推理。 TruthfulQA: 评估模型生成事实准确、避免常见误解的能力。 基线模型 (Baselines): 设置了全面的对比基线，包括： Vanilla Baselines: Zero-shot（直接回答）和 CoT（链式思考提示）。 Fine-tuned Baselines: 标准的LoRA微调和加入了规划令牌（Planning-token）的微调方法。 评估方式: 性能评估: 使用准确率（Accuracy）作为主要指标。 解耦效果评估: 使用GPT-4o-mini作为评估器，判断模型生成的\u0026lt;memory\u0026gt;步骤是否真的是事实陈述，\u0026lt;reason\u0026gt;步骤是否真的是逻辑推理。 错误来源分析: 使用GPT-4o对错误答案进行归因分析，判断错误是源于记忆还是推理。 关键实验数据和结果: 总体性能优越: 在所有三个数据集上，论文提出的方法（在表1中标记为”Ours”）在各个体量的模型上都取得了最佳性能。\n在StrategyQA上，LLaMA-3.1-8B模型使用该方法达到78.0%的准确率，显著高于CoT的69.4%和Planning-token的76.7%。 在TruthfulQA上，LLaMA-3.1-8B模型达到86.6%，超越了GPT-4o的85.4%。 卓越的解耦能力: 实验证明，该方法能非常有效地分离记忆和推理。\n如图2所示，在三个数据集上，该方法在识别记忆步骤（例如StrategyQA上达到94%）和推理步骤（71%）的准确性上，都一致且显著地优于使用one-shot CoT提示的基线方法。 图2：本文方法与One-shot CoT在三个数据集上的解耦性能对比。蓝色柱（Ours）在所有任务上均表现更优。\n揭示了推理是主要瓶颈: 错误分析（表7）提供了深刻的洞见。\n在StrategyQA和CommonsenseQA上，推理错误分别占总错误的98.3%和78.4%。这说明模型其实“知道”正确的事实，但无法正确地运用它们进行推理。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为“可解释与可控的LLM推理”开辟了一个充满机遇的新方向。未来的探索可以围绕以下几点展开：\n更丰富的认知令牌 (Richer Cognitive Tokens): 挑战: 目前只有\u0026lt;memory\u0026gt;和\u0026lt;reason\u0026gt;两个标签，这对于更复杂的认知活动（如提出假设、进行类比、验证结论）可能过于简化。 探索方向: 能否设计一套更丰富的“认知指令集”，例如\u0026lt;hypothesize\u0026gt;（假设）, \u0026lt;verify\u0026gt;（验证）, \u0026lt;synthesize\u0026gt;（综合）等，从而让模型能够执行更复杂、更结构化的思考策略。 技术与投资机会: 这可能催生出“AI认知架构设计”这一新领域，以及开发用于构建高度专业化、可审计的专家系统（如AI律师、AI科学家）的平台。 动态记忆与持续学习 (Dynamic Memory and Continual Learning): 挑战: 本文方法主要关注如何利用模型已有的参数化知识。但现实世界的知识是不断更新的。 探索方向: 能否将此框架与外部知识库（如RAG）或持续学习机制相结合？例如，当模型遇到一个\u0026lt;memory\u0026gt;步骤但其内部知识不足时，可以触发一次外部搜索。 技术与投资机会: 面向企业的“知识生命周期管理平台”，能够动态地更新、修正和扩展企业专属大模型的知识库，并确保其推理过程的可靠性。 自适应推理深度 (Adaptive Reasoning Depth): 挑战: 不同问题的复杂度不同，所需的推理步骤数量也不同。目前模型通常生成固定或相似长度的推理链。 探索方向: 研究如何让模型学会“元认知”（metacognition），即根据问题的难度自主决定需要多少个\u0026lt;memory\u0026gt;和\u0026lt;reason\u0026gt;步骤，以在效率和效果之间取得最佳平衡。 技术与投资机会: 开发“高效推理引擎”，能够智能分配计算资源，简单问题快速响应，复杂问题深入思考，从而降低大模型的使用成本。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的角度看，仍存在一些值得讨论的方面：\n对“教师模型”的严重依赖: 不足: 整个方法的基石——高质量的结构化训练数据——完全依赖于GPT-4o的生成能力。如果教师模型本身存在偏见、知识盲点或推理缺陷，这些问题将被直接“遗传”给学生模型。这种模式的上限受限于教师模型的能力。 待验证: 论文通过Jaccard相似度分析来论证没有发生直接的知识蒸馏，但这并不能完全排除学生模型在模仿教师模型的“推理风格”或“思维模式”。性能的提升究竟多大程度上源于算法的优越性，多大程度上源于对更强模型行为的模仿，仍需更深入的分析。 记忆与推理的二元划分可能过于简化: 不足: 在人类认知中，记忆和推理往往是深度交织、相互影响的，其界限并不总是那么清晰。例如，“根据过往经验进行判断”既涉及记忆也涉及推理。将所有认知活动强制归类为二者之一，可能是一种过度简化。 待验证: 对于那些记忆和推理高度耦合的任务，该框架的有效性如何？是否会出现模型难以抉择使用哪个令牌，从而导致性能下降的情况？ 计算与延迟开销: 不足: 数据生成阶段需要两个大型LLM的多次调用，成本高昂。在推理阶段，生成一系列显式的中间步骤，相比于直接生成答案，可能会增加时延（latency），这对于实时应用（如聊天机器人）可能是一个挑战。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是创新思想的宝库，以下几点尤其值得您学习和借鉴：\n核心启发：将“过程”本身作为学习目标: 学什么: 不要只关注模型最终输出的答案是否正确，更要关注产生答案的过程是否合理、可控。本文最大的创新就是将隐式的“思考过程”变成了显式的、可训练的“结构化输出”。 如何用: 在您自己的项目中，思考是否可以定义一些中间步骤或结构，并训练模型来生成它们。例如，在代码生成任务中，可以要求模型先生成“功能描述”、“伪代码”，再生成最终代码。 方法论：用控制令牌（Control Tokens）引导复杂生成: 学什么: 使用可学习的特殊令牌来引导模型的生成行为，是一种极其强大且灵活的工具。这相当于给了你一个“遥控器”，可以在关键节点上指导模型的输出方向。 如何用: 思考在您的任务中，哪些关键的、可复用的“模式”或“状态”可以被抽象为特殊令牌。例如，在对话系统中，可以设计\u0026lt;clarification_needed\u0026gt;（需澄清）、\u0026lt;empathetic_response\u0026gt;（共情回应）等令牌来控制对话风格和流程。 实践范式：“强模型生成数据，小模型学习范式”: 学什么: 利用强大的闭源模型（如GPT-4系列）来生成高质量、带有特定结构或标签的训练数据，然后用这些数据去微调一个更小、更可控的开源模型。这是一种兼顾性能、成本和灵活性的高效路径。 如何用: 这是一个可以直接应用的策略。无论您的任务是数据增强、风格迁移还是训练一个特定技能的模型，都可以利用这一“生成-微调”范式。 补充背景知识: 深入理解CoT和RAG: 了解它们的工作原理、优势和局限性，这能帮助您更好地理解本文方法所处的技术生态位及其创新价值。 熟悉指令微调（Instruction Fine-tuning）: 这是实现本文方法的关键技术。了解其原理、常见数据集（如Alpaca）和训练框架（如Axolotl, LLaMA-Factory）将非常有帮助。 了解LoRA等参数高效微调（PEFT）技术: 论文中提到了使用LoRA进行训练，了解这些技术如何能以较低的计算成本微调大模型，对于实践至关重要。 ","date":"2025-09-23T00:00:00Z","permalink":"/disentangling-memory-and-reasoning-ability-in-large-language-models/","title":"Disentangling Memory and Reasoning Ability in Large Language Models"},{"content":"规划的工作，解决的问题是在以往的经验工程中直接检索会导致检索到大量无关的干扰信息，降低了成功率，这个问题的根本原因是检索粒度太粗了，方法是分层规划，解耦成做什么的高级知识和怎么做的低级知识，类似HIPLAN：Hierarchical Planning for LLM Agents with Adaptive Global-Local Guidance，构建方法是在事后主动进行反思，通过基于轨迹的子目标推断、对比性洞察提取和结构化的记忆单元（高层记忆单元包含{任务描述, 成功子目标序列, 相关规划洞察}，低层记忆单元包含{子目标描述, 对应子轨迹, 相关执行洞察}）。此外对于高级计划由planner检索，低级动作由executor检索。这篇论文非常值得阅读。\n论文深度解读 大型语言模型（LLM）驱动的智能体，正以前所未有的速度渗透到从软件自动化到物理机器人等众多领域。然而，如何让这些智能体像人类一样，高效地从过去的经验中学习，并将知识灵活运用于全新的任务，始终是一个核心挑战。这篇名为《H²R: Hierarchical Hindsight Reflection for Multi-Task LLM Agents》的论文，正是对这一问题的深刻回应。其最大的创新贡献在于，它指出并解决了现有智能体在知识迁移中的一个根本性缺陷：粗粒度知识迁移（coarse-grained knowledge transfer）。\n为了理解这一点，论文开篇就给出了一个绝佳的例子： \u0026gt; …一个LLM智能体学会了“清洁平底锅并将其放在台面上”的任务，现在面临一个新任务“冷却生菜并将其放在台面上”。当将整个任务的知识存储在记忆中时，智能体可能会检索到前一个任务的整个记忆单元。这个粗粒度的记忆单元可能包含与不相关的子目标“清洁平底锅”相关的知识，这会分散对可复用子目标“将其放在台面上”的注意力…\n这个例子生动地揭示了问题的核心：将一次完整的任务经验“打包”存储，会导致在解决新任务时，检索到的知识中包含了大量无关甚至干扰性的信息，从而降低决策效率和成功率。\n为解决此问题，作者提出了一个优雅且符合直觉的解决方案——分层式记忆架构（Hierarchical Memory Architecture）。这个架构不再将经验视为一个整体，而是将其“解耦”为两个层次：\n高层规划记忆（High-level Planning Memory, M_high）：它负责存储抽象的、战略性的知识。比如，一个任务可以被分解为哪些步骤（子目标序列），以及在规划这些步骤时应该遵循哪些通用原则（规划洞察）。这好比我们做事时脑海中的“总纲”或“流程图”。 低层执行记忆（Low-level Execution Memory, M_low）：它负责存储具体的、战术性的知识。比如，针对某一个具体的子目标（如“拿起杯子”），应该执行哪些原子操作序列，以及在执行过程中需要注意哪些细节（执行洞察）。这相当于我们完成某个具体步骤时的“操作手册”。 仅仅有分层架构还不够，如何构建和填充这个记忆库是关键。为此，论文设计了一套名为分层事后反思（Hierarchical Hindsight Reflection, H²R）的精巧机制。这个机制模仿了人类“复盘”和“总结经验”的过程，智能体在完成任务（无论成功或失败）后，不会简单地将经历存档，而是会进行一次深入的、结构化的反思，从原始的交互轨迹中提炼出宝贵的知识。\n这个反思过程（如图1所示）包含四个关键环节：子目标推断（Subgoal Inference）、子轨迹推断（Subtrajectory Inference）、洞察力提取（Insight Extraction）和记忆组织（Memory Organization）。智能体首先会回顾整个任务过程，推断出为了完成任务，它实际上执行了哪些子目标。然后，将冗长的操作序列切分成与每个子目标对应的“子轨迹”。接着，在“高层”和“低层”两个维度上，通过对比成功和失败的案例，提炼出具有普适性的规则或“洞察”，例如高层的“避免循环的子目标”或低层的“行动失败时，检查一下当前可用的行动列表”。最后，将这些宝贵的“洞察”与相应的任务描述、子目标序列或子轨迹关联起来，存入分层的记忆库中。\n在执行新任务时，这套分层架构的优势便显现出来。智能体内部的规划器（Planner）会查询高层记忆库，寻找与当前任务相似的“总纲”来指导其分解任务。而执行器（Executor）则会根据规划器给出的当前子目标，去查询低层记忆库，寻找最高效的“操作手册”来完成具体步骤。这种各司其职、精准检索的模式，极大地提升了知识复用的效率和准确性。\n为了验证其有效性，论文在两个截然不同的多任务环境——基于文本的家庭场景模拟器AlfWorld和策略游戏环境PDDLGame——中进行了详尽的实验。结果令人信服，在AlfWorld环境中，H²R的成功率达到了75.9%，相较于采用“打包”式记忆的先进基线方法Expel提升了3.5%。在需要更复杂规划能力的PDDLGame环境中，H²R的优势更为明显，成功率高达80.5%，相对Expel提升了8.3%。更具说服力的是消融实验（Ablation Studies），当研究者分别移除高层记忆和低层记忆后，智能体的性能出现了断崖式下跌（在PDDLGame中分别下降了27.7%和19.4%），这强有力地证明了分层架构中两个层级的记忆都不可或缺，且各自扮演着至关重要的角色。\n总而言之，H²R论文的核心贡献在于，它从认知科学中“分层处理”和“事后反思”等概念汲取灵感，为LLM智能体设计了一套全新的、高效的知识管理与复用框架。它不仅在理论上指出了“粗粒度”记忆的弊端，更在实践中通过精巧的架构设计和强大的实验结果，证明了“分层”是通往更通用、更高效人工智能体的关键一步。\n接下来，我将按照您提出的六个问题，进行更详细的剖析。\n1. 论文的研究目标是什么？ 研究目标：这篇论文的核心研究目标是提升LLM智能体在多任务环境下的知识迁移效率和泛化能力。换言之，它致力于让智能体能够更好地从已完成的任务中学习，并将学到的知识和技能，更精准、更高效地应用于解决未来的新任务。\n想要解决的实际问题：\n知识干扰问题：解决现有方法中存在的“粗粒度”知识迁移问题。如前所述，当智能体将过往经验作为一个不可分割的整体进行记忆和检索时，会导致在解决新任务时，将与任务无关的冗余信息（例如“清洁平底锅”）一并调取，干扰了对当前任务核心环节（例如“放在台面上”）的专注，从而降低决策效率和成功率。 学习效率低下问题：在没有高效知识复用机制的情况下，智能体面对每一个新任务，都需要进行大量的探索和试错，即使新任务与旧任务有很高的重合度。这极大地限制了智能体的学习速度和适应性。 对行业发展的重要意义：\n推动通用人工智能（AGI）的发展：人类智能的核心特征之一就是能够将从一个领域学到的知识（例如，学会了骑自行车）迁移到另一个领域（例如，学骑摩托车时会更快）。这篇论文提出的分层记忆机制，正是模拟了人类这种结构化、分层次的知识管理方式，是构建能够适应多样化、开放式环境的通用智能体的关键一步。 提升自动化系统的鲁棒性和效率：在工业自动化、智能客服、个人助理等实际应用中，智能体需要处理大量既有共性又有差异的任务。H²R提出的方法能让系统更智能地复用已有解决方案，减少重复开发和调试，在面对新需求时能更快响应，并表现得更稳定。 降低训练成本：通过高效的知识迁移，可以显著减少智能体在新任务上的学习时间（即所需的交互样本数量），这直接关系到计算资源和时间的消耗，对于大规模部署LLM智能体而言，具有重要的经济价值。 2. 论文提出了哪些新的思路、方法或模型？ 这篇论文的核心创新在于提出了一个完整的框架——H²R (Hierarchical Hindsight Reflection)，它包含了一个新颖的记忆架构和一套用于构建该架构的方法论。\n核心思路：解耦与分层（Decoupling and Hierarchy） 新思路：与之前将经验视为“单一整体（monolithic units）”的思路不同，H²R的核心思路是将智能体的知识体系进行解耦，分为“做什么”（What to do）的高层规划知识和“怎么做”（How to do it）的低层执行知识。 优势：这种分层结构的最大优势在于精准检索和减少干扰。当智能体规划任务时，它只关心高层的步骤和策略，不会被底层的具体操作细节所困扰。当它执行一个具体步骤时，又能精准地调用与该步骤相关的操作技巧，而不会被其他任务的宏观规划所干扰。这大大提高了知识复用的信噪比。 核心方法：分层事后反思（Hierarchical Hindsight Reflection） 新方法：为了构建上述分层记忆，论文提出了一套自动化的知识提炼流程。它不是被动地记录轨迹，而是主动地、在事后（Hindsight）进行反思（Reflection）。 具体步骤与特点： 基于轨迹的子目标推断：这是一个非常巧妙的设计。智能体不是预先定义好子目标，而是在任务完成后，通过一个LLM（F_subgoal）来分析整个成功的交互轨迹，反向推断出其中包含了哪些有意义的子目标。这使得子目标的切分更加灵活和数据驱动。 \u0026gt; Formally, for a given task Xⁱ and the corresponding interaction trajectory τⁱ, reflection is conducted to infer subgoal sequence by prompting an LLM: Gⁱ ← F_subgoal(Xⁱ, τⁱ) 对比性洞察提取（Contrastive Insight Extraction）：该方法借鉴了Expel[4]的思路，但将其应用到了两个不同的层级。通过对比成功轨迹和失败轨迹，LLM被提示去总结出导致成功或失败的关键原因，并形成可复用的“洞察力”或规则。这种方法让智能体不仅知道“怎么做”，还知道“为什么这么做”以及“什么不能做”。 结构化的记忆单元：每个记忆单元不再是杂乱的文本，而是结构化的数据。高层记忆单元包含{任务描述, 成功子目标序列, 相关规划洞察}，低层记忆单元包含{子目标描述, 对应子轨迹, 相关执行洞察}。这种结构化设计极大地便利了后续的精准检索。 核心模型：规划器-执行器架构（Planner-Executor Architecture） 模型设计：虽然Planner-Executor架构本身并非首创，但H²R将其与分层记忆库进行了深度融合（如图2所示）。 Planner：专门与高层记忆库（M_high）交互。它接收任务，检索相似任务的规划经验，然后输出下一步的子目标。 Executor：专门与低层记忆库（M_low）交互。它接收来自Planner的子目标，检索如何执行该子目标的具体操作序列，然后输出原子动作。 特点与优势：这种职责分离的设计，使得整个决策流程非常清晰。Planner负责运筹帷幄，Executor负责冲锋陷阵，二者通过分层记忆库高效协作，实现了复杂任务的有序执行。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文通过一系列设计严谨的实验，在两个基准环境中充分验证了H²R方法的有效性、必要性和先进性。\n实验设计： 基准环境（Benchmarks）： AlfWorld：一个基于文本的交互式家庭环境模拟器。它考验智能体对自然语言指令的理解、长期规划和与环境的交互能力。 PDDLGame：一个基于规划领域定义语言（PDDL）的策略游戏环境。它更侧重于智能体的逻辑推理和复杂的分层规划能力。选择这两个环境可以分别检验方法在“交互与理解”和“策略与规划”上的表现。 对比基线（Baselines）： ReAct：一个经典且强大的“推理-行动”范式，但它没有长期记忆机制，代表了无经验学习的基准水平。 Expel：一个先进的、带有经验学习能力的智能体框架，但它的记忆是“粗粒度”的，直接将整个任务的成功和失败经验进行提炼和存储，是H²R着重对比和超越的目标。 评估指标： 成功率（Success Rate %）：在规定步数内（AlfWorld为30步，PDDLGame为40步）成功完成任务的百分比，这是衡量智能体能力的黄金标准。 消融研究（Ablation Studies）：为了探究分层记忆中每个组成部分的重要性，作者还设计了消融实验，即分别移除高层记忆库和低层记忆库，观察智能体性能的变化。 实验数据和结果： 总体性能对比： \u0026gt; 如表I所示，H²R在两个基准环境中都超越了所有基线方法，在AlfWorld中成功率为75.9%，在PDDLGame中成功率为80.5%，相对于基线Expel，分别取得了3.5%和8.3%的相对提升。\n下面是论文中关键的表I的可视化呈现： | 算法 (Algorithms) | AlfWorld 成功率 (%) | PDDLGame 成功率 (%) | | :—————: | :——————-: | :——————-: | | ReAct | 46.3 | 66.7 | | Expel | 72.4 | 72.2 | | H²R | 75.9 | 80.5 |\n这个结果清晰地表明，H²R的分层记忆架构优于无记忆的ReAct和采用粗粒度记忆的Expel。特别是在更考验规划能力的PDDLGame上，优势更为显著，证明了分层规划知识的巨大价值。\n消融实验结果： \u0026gt; 消除高层记忆单元时，系统无法提取任务级别的洞察和子目标序列…导致在PDDLGame中性能下降27.7%。相反，移除低层记忆会阻止执行洞察和子目标特定模式的利用，导致19.4%的性能下降。\n论文中的表II数据如下： | 算法 (Algorithms) | PDDLGame 成功率 (%) | | :———————–: | :——————-: | | H²R | 80.5 | | H²R w/o high-level memories | 52.8 | | H²R w/o low-level memories | 61.1 |\n这个结果极具说服力地证明了：\n高层和低层记忆都至关重要。缺少任何一层，都会导致性能的急剧恶化。 高层规划记忆的贡献更大。在PDDLGame这个注重策略规划的环境中，移除高层记忆比移除低层记忆带来的性能损失更大，这完全符合H²R框架的设计初衷。 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 基于这篇论文和当前大模型领域的发展趋势，我认为以下几个方向极具探索价值：\n动态与开放环境的适应性： 挑战：当前的实验环境（AlfWorld, PDDLGame）相对静态和封闭，规则是固定的。真实世界是动态变化的，智能体需要不断更新甚至淘汰过时的知识。 探索方向：研究记忆的动态更新、压缩与遗忘机制。如何让智能体判断哪些旧知识已经不再适用？如何将大量相似的记忆融合成更精炼的知识单元，以避免记忆库无限膨胀？这可能会催生出基于“记忆半衰期”或“知识图谱剪枝”的新技术。 投资机会：能够自适应学习和演进的机器人操作系统、能持续跟进市场变化的金融分析智能体、能自动更新知识库的智能客服系统。 多智能体协作与知识共享： 挑战：H²R主要关注单个智能体的学习。在多智能体系统中，如何高效地共享和传递经验是一个巨大的挑战。 探索方向：设计分层式的多智能体知识共享协议。高层规划知识可能在所有智能体之间共享，而低层执行技巧可能只在特定类型的智能体之间传递。这可以催生出“智能体知识市场”或“分布式经验网络”等新范式。 投资机会：协同工作的机器人集群（如仓储物流、自动化产线）、大规模虚拟世界中的NPC生态系统、用于复杂科学研究的AI协作平台。 跨模态知识的迁移： 挑战：H²R的记忆是纯文本的。如何将从文本中学到的规划能力，迁移到处理视觉、听觉等多模态信息的任务中？ 探索方向：研究统一的多模态记忆表示。能否将高层规划知识表示为一种与具体模态无关的抽象格式（如符号逻辑或图结构），而将低层执行知识与具体的模态（如图像、声音）绑定？这可能需要结合多模态大模型和神经符号系统。 投资机会：能够听懂口头指令并观看演示来学习新技能的家庭服务机器人、结合病历文本和影像图片进行诊断的医疗AI助手。 记忆检索的效率与准确性： 挑战：当记忆库变得极其庞大时，基于向量相似度的检索方式可能会变得缓慢且不够精准。 探索方向：开发更先进的混合检索技术，例如结合稀疏检索（如BM25）和稠密检索，或引入重排（reranking）和查询重写（query rewriting）模块（论文中也提到了这一点）。甚至可以训练一个专门的“检索模型”来决定调用哪条记忆。 投资机会：为海量企业知识库打造的高效智能检索引擎、下一代AI编程助手中的代码库与API调用推荐系统。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度来看，仍然存在一些潜在的局限和值得商榷之处：\n子目标推断的可靠性存疑： 整个H²R框架的基石是能够从轨迹中准确地推断出子目标序列。这个过程本身依赖于一个LLM（F_subgoal）。如果这个LLM的推断出现错误或偏差，那么构建出的整个记忆库的质量都会受到影响，可谓“一步错，步步错”。论文中并未详细讨论这一推断过程的准确率，也没有分析其对整体性能的敏感度。 计算与存储开销未被讨论： “事后反思”过程，特别是涉及多次调用LLM进行子目标推断、洞察力提取和知识接地，这无疑会带来巨大的计算开销。此外，分层记忆库的存储开销如何随着任务数量的增加而增长？在资源受限的场景下，H²R框架的实用性可能会受到挑战。论文对此缺乏量化分析。 泛化能力的边界不够清晰： 实验虽然在两个环境中取得了成功，但这两个环境都是离散的、基于文本的。H²R框架能否直接推广到连续动作空间（如机器人手臂控制）或高维视觉输入（如视频游戏）的环境中，尚需进一步验证。将连续的、高维的交互轨迹切分成有意义的“子轨迹”会是一个全新的挑战。 洞察力池的规模限制： 论文提到，高层和低层的洞察力池（I_high, I_low）是固定大小的（fixed-size）。这可能会成为长期学习的瓶颈。在一个需要不断学习新知识的开放环境中，一个固定大小的“规则集”可能很快就会过时或饱和。如何动态地管理和扩充这个洞察力池，是一个悬而未决的问题。 对失败经验的利用可能不足： 论文虽然提到了利用失败轨迹进行对比性反思，但记忆单元的构建似乎主要围绕成功的轨迹（例如，子轨迹切分只在成功轨迹上进行）。如何从失败中更深入地学习，例如构建一个专门的“失败案例库”来指导未来的规划以避免重蹈覆辙，这一点似乎还有挖掘的空间。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取即用创新想法的您来说，这篇论文提供了几个极具价值的“思想工具”和实践范式：\n核心启发一：将“分而治之”的思想应用于AI记忆 学什么：不要把AI的经验看作一个“大杂烩”。学习H²R对知识进行分层解耦的核心思想。在您自己的项目中，思考一下是否可以将系统的知识库或经验池，按照“战略层”和“战术层”、“规划层”和“执行层”、“抽象原则”和“具体案例”等维度进行拆分。 怎么用：例如，在开发一个自动化测试AI时，可以将“测试策略”（如先测试核心功能，再测试边缘案例）存入高层记忆，将“具体某个API的测试脚本”存入低层记忆。这能让AI在面对新软件时，更智能地复用测试策略。 核心启发二：让AI学会“复盘”和“写总结” 学什么：不要满足于让AI被动地记录日志或轨迹。学习H²R的“事后反思”机制。在任务完成后，设计一个流程，让LLM对刚刚发生的过程进行分析、归纳和总结，提炼出可复用的“金科玉律”。 怎么用：在构建一个代码生成AI时，当它成功解决了一个Bug后，可以触发一个反思模块，让它总结：“这次的Bug是由于空指针异常，修复的关键是增加了前置的非空判断。通用规则：在访问对象属性前，最好先进行判空处理。” 然后将这条规则存入知识库。 核心启发三：结构化你的提示（Prompt）和记忆 学什么：H²R的成功，很大程度上得益于其高度结构化的记忆单元和与之匹配的、职责分明的Planner和Executor。 怎么用：在设计复杂Agent时，可以借鉴这种职责分离的模式。创建一个“规划Agent”，它的Prompt专门用于分解任务和调用高层知识。再创建若干个“执行Agent”，它们的Prompt专门用于完成具体子任务和调用底层API/工具。这种模块化的设计能让系统更清晰、更易于扩展和调试。 补充了解的背景知识： 经典Agent架构：为了更好地理解H²R的创新之处，建议您了解一下ReAct (Reason+Act)、Reflexion（自我反思的雏形）和Expel（H²R直接对比的基线）这几篇关键论文。 检索增强生成（RAG）：H²R本质上是一种“内在化的RAG”，它检索的不是外部文档，而是自己生成的内部记忆。理解RAG的原理，有助于您更好地实现H²R中的记忆检索模块。 强化学习（RL）基础：论文使用了POMDP等强化学习的术语来定义问题。了解RL中的基本概念（如状态、动作、奖励、策略）将帮助您更深刻地理解LLM Agent领域的研究范式。 ","date":"2025-09-23T00:00:00Z","permalink":"/h2rhierarchical-hindsight-reflection-for-multi-task-llm-agents/","title":"H2R：Hierarchical Hindsight Reflection for Multi-Task LLM Agents"},{"content":"信用分配的工作，解决的问题是针对多步交互的agent，只给予结果奖励无法鼓励好的动作和抑制不好的动作，方法是利用外部大模型针对轨迹中的每一个step进行打分作为奖励，同时为了激发探索能力，混合了一些困难的数学题目。\n论文深度解读 这篇论文的核心目标是解决一个当前AI领域极具挑战性的问题：如何训练出一个能听懂人类的语音指令，并在一个复杂的环境中通过调用工具（如API）来完成多轮、长程任务的交互式多模态智能体。传统的训练方法，如在静态数据集上进行监督微调，难以让模型学会在动态变化的环境中进行探索和决策。而标准的强化学习（RL）方法在处理这类长程任务时，又会面临一个经典难题——信用分配（Credit Assignment）。简单来说，当一个任务包含几十步操作，最终成功或失败时，我们很难判断究竟是哪一步操作做得好或差。这导致模型的学习效率极低，奖励信号非常稀疏。\n为了克服这些挑战，作者提出了一套精巧且完整的解决方案。其最核心的创新是“回合级评审的强化学习”（Turn-level Adjudicated Reinforcement Learning, TARL）。这个方法非常巧妙，它引入了一个强大的大语言模型（LLM，如GPT-4.1）作为“评审员（Judge）”。在智能体与环境交互的每一回合结束后，这个评审员都会根据任务目标和真实行动轨迹，给智能体的当前表现打一个分（-1, 0, 或 1）。这样一来，模型不再是等到任务完全结束后才收到一个模糊的“总分”，而是在每一步都能得到及时、细致的反馈。\n正如论文在图3中所展示的，在一个用户要求更换蓝牙音箱的场景中，智能体在第6回合错误地选择了另一款不符合用户要求的音箱型号。TARL机制的评审员能够立刻识别出这个“重大偏差（Major Deviation）”，并给予-1的奖励，从而精确地惩罚这个错误决策。\n图3：论文中的回合级奖励示例 仅仅有精细的奖励还不够，作者还发现，在训练过程中模型会变得越来越“自信”，从而减少探索，陷入局部最优。为了解决这个问题，他们引入了“混合任务训练”（Mixed-Task Training）。具体做法是在训练工具使用任务的同时，穿插一些中等难度的数学推理题。这背后的逻辑是，数学题能天然地激发模型进行更长的“思维链”（Chain-of-Thought, CoT）推理和自我纠错，这种探索性的行为模式可以“正则化”训练过程，防止模型在工具使用任务上过拟合，并保持其探索能力。\n最后，论文将这套方法成功地应用到了多模态场景中，构建了一个支持“语音-文本”交错输入的沙盒环境，并首次展示了通过强化学习成功训练了一个语音交互智能体。实验结果令人印象深刻：在纯文本任务上，他们的方法（Math + TARL）将基础模型的pass@1（单次尝试成功率） 从42.6%提升到了57.4%，提升了近15个百分点。在更具挑战性的多模态任务上，他们将模型的语音交互成功率从基线的14.8%一举提升至37.4%，证明了该框架的巨大潜力。\n总而言之，这篇论文不仅仅是提出了一两个算法改进，而是构建了一套从环境、算法到多模态应用的完整训练框架。它通过引入LLM评审员解决了长程任务中的信用分配难题，通过混合任务训练维持了模型的探索能力，最终成功地将强化学习应用于训练更自然、更强大的语音交互智能体，为开发下一代AI助理和自动化工具指明了一条清晰且有效的技术路径。\n接下来，我将按照您提出的六个方面，对论文进行更详细的剖析。\n### 1. 论文的研究目标与实际意义 研究目标： 论文的核心研究目标是开发和验证一个有效的框架，用于训练能够处理长程、多轮、多模态（特别是语音）交互的工具使用智能体（Tool-Use Agents）。\n解决的实际问题： 当前，我们与AI的交互大多依赖于文本。想象一下，如果你能像和真人助理说话一样，直接用语音告诉AI：“帮我查一下上周那个订单，把里面的那个蓝色耳机换成红色的，如果需要补差价，就用我账户里的礼品卡支付。” 这个任务非常复杂，它包含：\n多轮交互：AI需要先确认是哪个订单，再找到耳机，然后搜索红色款，最后确认支付方式。 长程依赖：用户的意图贯穿整个对话，AI需要记住上下文。 工具使用：AI需要调用查询订单、查询商品、修改订单等多个API。 多模态输入：指令是语音形式的，AI需要准确理解。 现有的技术很难完美解决这个问题。监督微调依赖大量的标注数据，且模型难以应对动态变化；而传统强化学习则因为奖励稀疏而难以训练。这篇论文正是为了解决这个“能听会用工具的AI助理”在训练上的核心技术瓶颈。\n行业发展意义： 这项研究意义重大。它直接关系到我们能否创造出真正实用、自然的下一代AI应用。\n推动人机交互革命：将交互从文本解放到语音，是实现AI普惠化的关键一步，能极大地降低使用门槛。 赋能复杂任务自动化：能够使用工具的智能体是实现工作流程自动化的基础，无论是个人助理、企业客服还是软件开发，都将因此发生深刻变革。 探索通用人工智能（AGI）路径：一个能够理解人类语言、与物理或数字世界交互并完成任务的智能体，被认为是通往AGI的重要里程碑。这项工作是这条道路上坚实的一步。 ### 2. 论文的新思路、方法与优势 论文提出了一个由多个创新点组成的集成框架，其核心在于TARL和混合任务训练。\n核心方法1：回合级评审的强化学习 (TARL) 思路：传统RL在任务结束后才给予奖励，这好比学生考完期末考才知道总分，但不知道错在哪。TARL的思路是请一位“随堂老师”（LLM Judge），在每一道题（每一个交互回合）做完后就立即批改并给出反馈。 细节： \u0026gt; 评审员（Judge）会接收到完整的对话历史和任务的“标准答案”（Ground-truth tool calls）。它为每一回合打分：1代表正确，0代表有小问题但不影响大局（或由后续步骤修正），-1代表导致任务偏离轨道的重大错误。同时，为了避免惩罚过当，规定每次任务最多只有一个-1。 优势： 解决信用分配难题：奖励信号变得密集且精确，模型能快速定位并修正错误行为。 反馈更具信息量：相比于二进制的“成功/失败”奖励，TARL提供了更细致的反馈，区分了“小错”和“大错”，让奖励信号的质量更高。 核心方法2：混合任务训练 (Mixed-Task Training) 思路：为了防止模型在反复训练同一类型任务后变得“思维僵化”，减少探索，作者引入了看似不相关的数学题。这就像为了训练一个足球运动员的敏捷性，让他去练习跳绳一样。 细节： \u0026gt; 论文中提到，基础模型（如Qwen3-8B）在预训练中已经接触了大量数学和代码数据，具备一定的推理能力。解数学题能促使模型产生更长的思维链（CoT）和自我纠错行为。通过将零售领域的工具使用任务和DeepScaleR数据集中的数学题混合训练，可以有效“正则化”训练过程。 优势： 促进探索：数学题迫使模型进行更复杂的推理，增加了行为的多样性，打破了在单一任务上形成的惯性思维。 防止过拟合：通过引入不同领域的任务，防止模型过度拟合到训练领域的特定模式，从而提升了模型的泛化能力和鲁棒性。 核心方法3：多模态沙盒环境与训练流程 特点：论文不只停留在算法层面，还实现了一个支持语音和文本交互的沙盒环境。在这个环境中，可以通过TTS模型（SeedTTS）模拟用户的语音输入，从而让智能体在更真实的多模态场景中进行在线学习。 优势：这是首次将此类复杂的RL训练方法应用于语音驱动的智能体。它打通了从理论到实践的关键一环，展示了训练更自然的交互式智能体的可行路径。 ### 3. 实验验证与关键结果 论文通过在T-BENCH基准上的一系列详尽实验，系统地验证了其方法的有效性。\n实验设计：\n基线对比：首先，作者复现了多个基线模型，包括强大的闭源模型GPT-4.1，以及多种RL算法（PPO, GRPO, RLOO）训练的Qwen3-8B模型。 消融研究 (Ablation Study)：在最好的RL算法基础上，作者逐步加入自己的改进。首先只加入TARL，然后同时加入TARL和混合任务训练（MATH），以验证每个模块的贡献。 多模态验证：使用支持多模态的Qwen2.5-Omni-7B作为基础模型，在文本和语音两种评估模式下，对比了基线模型和经过其完整框架训练后的模型性能。 实验数据与结果：\n文本智能体实验 (Text-based Agent): 从下方的表2可以看出，在T-BENCH的RETAIL领域：\n基础的Qwen3-8B模型pass@1成功率为42.6%。 使用标准RL算法GRPO后，成功率提升至51.3%。 在GRPO基础上加入TARL，成功率进一步提升至53.9%。 最终同时加入MATH（混合任务训练）和TARL，成功率达到了57.4%，相比基础模型提升了14.8%，相比强大的RL基线也提升了6.1%。这证明了论文提出的两个核心方法都起到了显著的正面作用。 同时，#Wait（代表自我反思）和Len（回复长度）指标的增加，也从侧面印证了模型在进行更复杂的思考。 Agent Model #Wait Len pass@1 pass@2 pass@3 pass@4 Baseline Model Qwen3-8B 14.6 228 42.6 30.4 25.2 21.7 Qwen3-8B + RL (GRPO Variants) GRPO 11.7 204 51.3 37.4 30.4 27.0 +TARL 14.0 210 53.9 (+2.6) 40.9 (+3.5) 33.9 (+3.5) 30.4 (+3.4) +MATH + TARL 15.8 236 57.4 (+6.1) 42.6 (+5.2) 36.5 (+6.1) 33.9 (+6.9) 表2：文本智能体在T-BENCH RETAIL领域的性能对比 **多模态智能体实验 (Multimodal Agent)**: 从下方的**表3**可以看出，在语音评估模式（Speech Eval Mode）下： * 基础的Qwen2.5-Omni-7B模型`pass@1`成功率仅为**14.8%**，说明在没有经过特定训练时，多模态模型处理这类任务的能力很弱。 * 而使用论文的完整方法（GRPO + Math + TARL）并在语音和文本混合数据上训练后，成功率飙升至**37.4%**，提升超过**22个百分点**，效果极为显著。 Eval Mode Agent Train Mode pass@1 pass@2 pass@3 pass@4 Baseline Models Speech Qwen2.5-Omni-7B — 14.8 8.7 5.2 5.2 Qwen2.5-Omni-7B + RL Speech GRPO + Math + TARL S\u0026amp;T 37.4 26.1 22.6 20.9 表3：多模态智能体在语音评估模式下的性能对比 这些坚实的数据清晰地证明了论文所提方法的有效性和先进性。\n### 4. 未来探索方向、挑战与机遇 尽管这篇论文取得了巨大成功，但它也为未来的研究揭示了更广阔的空间。\n值得探索的问题和挑战： 更真实的模拟环境：当前的T-BENCH等环境仍然是高度结构化的。未来的研究需要构建更开放、更复杂的模拟环境，甚至探索直接在真实世界中进行学习的方法，以应对现实世界的噪声和不确定性。 评审员的成本与可靠性：TARL依赖于一个强大的LLM（如GPT-4.1）作为评审员，这既昂贵又无法保证100%准确。未来的挑战在于如何训练出更小、更高效、更可靠的专用评审员模型，或者发展出不那么依赖外部评审的自我改进机制。 开放域工具学习：论文中的工具集是预定义好的。一个更大的挑战是让智能体能够自主发现、理解并学会使用全新的、从未见过的工具（API）。 处理模糊指令与异常：真实用户的指令往往是模糊、不完整甚至错误的。如何让智能体在这种情况下主动提问、澄清意图，并具备强大的容错和恢复能力，是亟待解决的问题。 可能催生的技术和投资机会： 智能体即服务（Agent-as-a-Service）：未来可能会出现专门提供预训练好的、能执行特定领域任务（如电商、旅游、金融）的智能体平台，企业可以通过API调用这些智能体来构建自己的应用。 多模态基础模型：这项研究凸显了强大的多模态基础模型的重要性。对能无缝处理文本、语音、图像甚至视频的下一代基础模型的研发投入将持续增加。 RL for LLM 训练平台：专门用于大模型强化学习的工具链和平台将成为一个重要的细分市场，包括高效的模拟环境、可信的奖励模型以及优化的RL算法库。 新一代交互硬件：随着语音交互智能体的成熟，可能会催生出新的硬件形态，如更智能的耳机、AR眼镜等，它们将成为这些智能体与物理世界交互的入口。 ### 5. 论文的不足与待验证之处 从批判的视角来看，这篇论文也存在一些局限性。\n对模拟环境的过度依赖：整个训练和评估过程都发生在模拟环境中，用户由GPT-4模拟，语音由TTS合成。这种理想化的环境与充满口误、背景噪音和非理性行为的真实人类交互相去甚远。从模拟到现实的“Sim-to-Real”鸿沟有多大，仍需进一步验证。 泛化能力的局限：表1的数据显示，虽然模型在训练过的RETAIL领域表现出色，但在未训练过的AIRLINE领域，性能提升有限，甚至在某些指标上不如基线。这表明模型的领域泛化能力仍有待提高，可能需要更多样化的训练数据来解决。 评审员的潜在偏见和错误：TARL方法的效果上限受制于LLM评审员的能力。如果评审员自身存在偏见或犯下错误，这些错误可能会被传递给正在学习的智能体，导致“学坏”。论文也提到，过于依赖评审员的准确性有时会破坏训练的稳定性。 训练成本高昂：强化学习，特别是涉及到多次在线交互（rollouts）和调用大型LLM作为评审员，其计算成本和时间成本都非常高昂。论文没有深入探讨如何降低训练成本，这在一定程度上限制了该方法的广泛应用。 ### 6. 核心启发与学习路径 对于希望从中汲取创新想法的您来说，这篇论文提供了宝贵的启示。\n重点学习与启发： 过程监督（Process Supervision）的核心思想：本文最大的启发是，对于复杂的长程任务，不要只关注最终结果，而要对过程进行监督和奖励。TARL就是这一思想的精彩体现。这个想法可以泛化到很多领域，比如代码生成（奖励每一步正确的逻辑）、长文写作（奖励每个写得好的段落）等。 通过辅助任务进行正则化：使用数学题来“锻炼”模型的推理和探索能力，是一个非常巧妙的“跨界”训练思路。这启发我们，在解决一个主要问题时，可以引入一些能培养所需“元能力”（meta-ability）的辅助任务，往往能取得意想不到的效果。 “简单方法”的胜利：论文在分析中发现，将奖励在整个轨迹上统一分配（trajectory-level）比在每个回合结束时精确分配（turn-level）给PPO算法效果更好。一些更复杂的干预手段，如实时打断模型生成，反而会起反作用。这呼应了著名的“苦涩的教训”（The Bitter Lesson）——简单、可扩展、能充分利用计算资源的方法，往往比复杂精巧的设计走得更远。 需要补充的背景知识： 强化学习基础：建议深入理解PPO（Proximal Policy Optimization）算法的原理，它是当前训练LLM最主流的RL算法之一。同时，要理解信用分配问题、策略（Policy）、价值函数（Value Function）等基本概念。 智能体框架（Agent Framework）：了解ReACT (Reasoning and Acting)框架，论文中提到用户模拟器就采用了此框架，它是当前工具使用智能体进行思考和决策的主流模式。 LLM Agent评测基准：了解如T-BENCH, ToolBench, AgentBench等评测体系，这能帮助您理解评估一个智能体能力的主要维度和方法。 多模态大模型：对当前主流的多模态模型（如Gemini, Qwen-VL/Omni系列）有基本的了解，知道它们是如何处理不同模态输入的。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-09-23T00:00:00Z","permalink":"/process-supervised-reinforcement-learning-for-interactive-multimodal-tool-use-agents/","title":"Process-Supervised Reinforcement Learning for Interactive Multimodal Tool-Use Agents"},{"content":"发现了GRPO系列算法需要针对一个prompt多次生成的问题，这会导致资源浪费，并且在agent rl中会被工具调用最慢的样本影响。因此创新的尝试去掉grpo中的group。创新方法是针对问题的被解答的概率作为价值估计，并且根据策略的kl散度动态变化。此外利用放弃了group，直接用batch进行归一化，最后还引入了课程学习，先学习不难不易的。\n论文深度解读 这篇由腾讯研究者提出的论文《Single-stream Policy Optimization》（单流策略优化，简称SPO），是对当前大语言模型（LLM）强化学习训练范式的一次深刻反思和重大革新。其核心贡献在于，它挑战并超越了当前主流的、基于“组”的策略优化方法（如Group Relative Policy Optimization, GRPO），提出了一种更简洁、更高效、更具可扩展性的单流训练框架。\n当前，为了提升LLM的复杂推理能力，研究者们普遍采用强化学习方法。GRPO这类方法通过为每个问题（prompt）生成一组（group）多个答案，并根据这组答案的好坏来计算一个“即时”（on-the-fly）的基线（baseline），以此来指导模型的学习。这种方法虽然取得了一定的成功，但论文作者敏锐地指出了其两个致命缺陷：\n计算资源的巨大浪费：当一个“组”里的所有答案都正确或都错误时，它们之间的相对优势就变成了零。这种情况在论文中被称为“退化组”（degenerate groups）。实验数据显示，这种情况极为普遍，论文在图3a中指出： \u0026gt; 对于GRPO（蓝线），退化组中的样本比例从大约60%上升到超过80%，这些样本产生了零优势值，不提供任何学习信号。 这意味着超过80%的计算（生成答案、评估奖励）都被浪费了，模型没有从中获得任何有效梯度来更新自己。\n严重的可扩展性瓶颈：在分布式训练中，基于组的方法要求必须等待组内最慢的那个答案生成完毕，才能进行下一步计算。这形成了一个“同步屏障”（synchronization barrier）。在处理需要多步工具调用或长链条推理的复杂智能体（agentic）任务时，不同答案的生成时间可能差异巨大。一个“拖后腿”的慢样本会拖慢整个组的进度。论文在第10页的仿真实验（图4和图5）中生动地展示了这一点，一个耗时508秒的慢样本会迫使其他在100秒内就完成的样本原地等待。\n为解决这些问题，SPO应运而生。它并非对现有方法的修补，而是回归到更经典的“单一样本流”范式，即每个训练样本就是一个独立的“问题-答案”对。为了克服单流范式下梯度方差过高这一经典难题，SPO精巧地设计了三大核心组件：\nKL自适应价值追踪器 (KL-Adaptive Value Tracker)：这是SPO的灵魂。它不再依赖于那个充满噪声的、临时的“组内基线”，而是为每一个问题（prompt）维护一个持久化的、历史知情的价值估计 û(x)。具体来说，它使用贝叶斯方法（Beta分布）来追踪一个问题被成功解答的概率。更精妙的是，这个追踪器是“KL自适应”的。这里的KL散度 (KL divergence) 用来衡量模型策略（policy）的变化程度。如果模型对于某个问题的策略发生了剧烈变化，追踪器就会通过一个折扣因子 p(x) “遗忘”掉更早的历史记录，更快地适应新策略下的价值。这就像一个聪明的老手，他会根据你的能力变化动态调整对你的期望，而不是每次都让你和一群临时工去比较。\n全局优势归一化 (Global Advantage Normalization)：GRPO在小规模的“组”内计算优势的均值和标准差来进行归一化，这个统计量本身就因为样本少而充满噪声。SPO则是在一个巨大的批次（batch）B 中，对所有样本的优势值进行全局归一化。这提供了远比GRPO稳定和可靠的学习信号，显著降低了梯度的方差。论文在图3b中用数据证明了这一点：SPO的优势方差（红线）稳定且远低于原始奖励的方差（绿线），而GRPO的有效优势方差（蓝线）甚至比原始奖励的方差还要高，波动剧烈。\n优先化提示词采样 (Prioritized Prompt Sampling)：SPO进一步引入了课程学习（curriculum learning）的思想。它不再对所有问题一视同仁，而是优先采样那些模型“最不确定”的问题——即价值估计 û(x) 既不接近1（已掌握）也不接近0（太难）的问题。这使得计算资源能够被智能地分配到“学习潜力最大”的样本上，极大地提升了数据效率。\n实验结果雄辩地证明了SPO的优越性。在多个高难度数学竞赛基准测试上，SPO不仅在性能上稳定超越了GRPO，例如在maj@32指标上平均提升了3.4个百分点（Table 1），更重要的是，它在架构上展现了巨大的效率优势。仿真实验显示，在模拟的智能体任务中，SPO的异步架构使得其数据收集速度达到了GRPO的4.35倍（Figure 5）。\n总而言之，SPO通过“返璞归真”的设计哲学，以一个更简单、更优雅的架构，系统性地解决了当前LLM强化学习训练中的核心效率和扩展性难题。它提醒我们，真正的进步并非总是来自于增加模型的复杂性，有时回归基础、并对关键环节进行精巧的重新设计，反而能带来更根本的突破。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是设计一种高效且可扩展的策略优化算法，用于提升大语言模型（LLM）的复杂推理能力，并替代当前主流但存在严重缺陷的基于组（group-based）的强化学习方法。\n解决的实际问题：\n计算效率低下：解决由“退化组”（即组内所有样本奖励相同）导致的计算资源浪费问题。在现有方法中，大量算力被用于生成那些最终无法提供有效学习信号的样本。 训练扩展性差：解决由“同步屏障”导致的可扩展性瓶颈。在需要与外部工具（如代码解释器）交互的智能体（Agent）任务中，不同样本的完成时间差异悬殊，同步等待机制严重拖慢了整体训练吞吐量。 行业发展意义： 随着大模型从单纯的文本生成走向更复杂的智能体（Agent）应用，模型需要具备长链条推理、多步工具调用和与环境动态交互的能力。这类任务的训练成本极高，且训练过程极其复杂。SPO提出的高效、异步的训练框架，直接回应了这一行业趋势的核心需求。它的意义在于：\n降低训练成本：通过消除计算浪费和提高吞吐量，SPO能够显著降低训练强大推理模型的成本，使得更多机构有能力进行前沿的Agent模型研发。 加速模型迭代：更高的训练效率意味着更快的实验和迭代速度，这将加速整个领域在复杂推理和Agent能力上的探索进程。 推动Agent技术落地：一个稳定、可扩展的训练框架是实现复杂Agent商业化应用的前提。SPO为此提供了一个更坚实、更可靠的技术底座。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文提出了单流策略优化（Single-stream Policy Optimization, SPO）方法。它与之前以GRPO为代表的组策略优化方法相比，在思路和具体设计上都有根本性的不同。\n核心思路转变：从“群体内比较”到“个体历史追踪”\nGRPO：其核心思想是横向的、即时的群体比较。它为每个问题生成一个G个样本的“组”，然后在这个小群体内部计算一个临时基线（baseline），以此判断每个样本的相对好坏。 SPO：其核心思想是纵向的、持久的个体追踪。它为每个问题维护一个独立的价值估计，这个估计是基于该问题在整个训练历史中的表现动态更新的。它不再需要“组”这个概念。 具体方法创新与优势：\n架构：单流 vs. 组 SPO：采用单流架构，每个样本（prompt-response对）都是一个独立的训练数据点。 \u0026gt; 我们主张回归经典的单流策略优化范式[29]，其中每个训练样本都是一个提示-响应对的单流。 优势：无同步屏障，可扩展性强。SPO的异步特性使其能够自然地过滤掉生成速度过慢的“拖后腿”样本，非常适合时间差异巨大的智能体任务。 基线估计：持久化价值追踪器 vs. 即时组基线 SPO：使用KL自适应价值追踪器。这个追踪器基于历史数据，提供了一个稳定、低方差的价值估计û(x)作为基线。其更新公式（式7）本质上是一个自适应指数移动平均（EMA），学习率η(x)会根据策略变化（通过KL散度p(x)）和统计置信度（通过有效样本数N_eff）进行智能调整。 优势：稳定且低方差。与GRPO基于少数几个（如8或16个）样本计算出的高方差基线相比，SPO的基线基于长期的历史信息，极其稳定。论文在附录C.2中从理论上分析，GRPO的基线噪声会将优势方差放大(1 + 1/G)倍。 优势归一化：全局 vs. 组内 SPO：在整个批次（Batch，规模远大于G）中进行全局归一化。 \u0026gt; SPO将它们在整个提示批次B [3, 17, 20, 25]中进行归一化。 优势：归一化统计量更可靠。从大规模样本中计算出的均值和标准差非常稳定，避免了GRPO中因分母（组内标准差）噪声过大而导致的梯度爆炸或消失问题。 数据采样：主动课程学习 vs. 被动均匀/动态采样 SPO：采用优先化提示词采样，根据公式 wi(x) ∝ sqrt(û(1-û)) + ε 来主动选择样本。 优势：数据效率高。它将算力集中于模型最需要学习的“半懂不懂”的知识点上，是一种主动的、前瞻性的课程学习。而GRPO的动态采样（DAPO）是一种被动的、补救性的措施，它在生成了退化组之后，被迫继续生成更多样本，造成了时间与数据的浪费。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文通过一系列精心设计的实验，从模型性能、信号质量和训练吞吐量三个维度全面验证了SPO的有效性。\n实验设计：\n模型与任务：使用了一个中等规模的开源模型Qwen3-8B，在需要与Python解释器进行工具交互的Tool-Integrated Reasoning (TIR) 任务上进行训练。 评估基准：在5个极具挑战性的数学竞赛基准（AIME 24, AIME 25, BeyondAIME, BRUMO 25, HMMT 25）上进行评估。这些基准能很好地衡量模型的深度推理能力。 对比方法：将SPO与当前该领域的强基线方法GRPO进行直接比较。 评估指标：主要使用maj@32（32次采样结果中，多数投票答案的正确率）和pass@k（k次尝试内成功解决问题的概率）。 实验数据与结果：\n模型性能优越：\n如表1所示，SPO在所有五个基准的maj@32指标上都优于GRPO。在平均性能上，SPO的maj@32为63.8%，而GRPO为60.4%，实现了+3.4个百分点的显著提升。 在最难的数据集之一BRUMO 25上，SPO的maj@32达到了64.0%，相比GRPO的56.7%，提升高达+7.3个百分点。 图2的pass@k曲线显示，在所有k值上，SPO的平均性能（图2f）都稳定地高于GRPO。 图注：论文表1，展示了SPO在所有五个测试基准上的maj@32指标均优于GRPO。\n学习信号质量更高：\n信号效率（图3a）：GRPO的“退化组”比例超过80%，意味着大量计算被浪费。而SPO中“零优势”的比例非常低，这证明了其学习信号的有效性。 信号稳定性（图3b）：SPO的优势方差（红线）相比于原始奖励（绿线）降低了近50%，且非常稳定。相比之下，GRPO在非退化样本上的“有效”优势方差（蓝线）甚至高于原始奖励，极不稳定。 训练吞吐量巨大提升：\n在模拟复杂智能体任务的实验中（图5），SPO的异步架构展现了惊人的效率。收集一个包含24个有效样本的批次，GRPO（即使经过并行优化）需要486秒，而SPO仅需112秒，实现了4.35倍的加速。 图注：论文图5c，直观比较了在智能体任务场景下，SPO相对于GRPO在训练批次收集速度上的巨大优势。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 价值追踪器的泛化：论文中的价值追踪器是表格型（tabular）的，即为每个prompt维护一个独立的价值。当prompt数量巨大或存在泛化需求时，这种方法会遇到瓶颈。未来的研究可以探索如何将这个追踪器与一个小型神经网络结合，实现对新prompt的价值泛化预测。 非二元与稀疏奖励：论文主要在二元奖励（成功/失败）场景下进行实验。如何将SPO及其价值追踪器优雅地扩展到更复杂的非二元奖励或稀疏奖励场景，是未来需要解决的重要问题。 与离线（Off-policy）方法的结合：SPO目前是在线（On-policy）算法。探索如何将其与离线数据结合，利用海量的已有数据进行预训练或持续训练，可能会进一步提升效率和性能。 大规模模型上的验证：在百亿、千亿甚至万亿参数的更大规模模型上，SPO的优势是否依然存在，或者是否会带来新的挑战（如价值追踪器的存储和更新开销），值得深入研究。 新的技术和投资机会： 高效智能体训练平台：SPO证明了异步训练框架的巨大潜力。这可能催生出专注于大规模、异步、分布式智能体训练的MaaS（Model-as-a-Service）平台。这类平台将SPO类算法作为核心，为企业提供高效、低成本的Agent模型训练服务，这是一个巨大的投资机会。 开源RL训练框架：围绕SPO的思想，可以构建新一代的、为LLM优化的开源强化学习框架。这个框架将以简洁、高效和可扩展性为核心卖点，挑战当前复杂且臃肿的RL库，有望成为LLM-Agent时代的“PyTorch”。 课程学习即服务（Curriculum Learning as a Service）：SPO的优先化采样机制是课程学习的一种体现。未来可能出现专门提供动态、自适应数据集和课程学习策略的服务，帮助企业更高效地训练模型，解决特定领域的难题。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 初始化成本问题：SPO需要一个“冷启动”过程来初始化价值追踪器。论文在附录A中承认： \u0026gt; 我们的早期实验表明，如果没有离线估计，训练在最初的几个步骤中是不稳定的。 虽然作者共享了他们预先计算好的数据集，但这无疑为新用户或新任务增加了一个额外的初始成本和步骤。这个初始化过程的质量对最终性能的影响有多大？论文缺乏对此的详细分析。\n内存开销：为每个prompt维护一个价值追踪器，在prompt数据集规模极其巨大时，可能会带来不可忽视的内存开销。论文没有讨论这个问题，也没有提出潜在的解决方案。\n超参数敏感性分析：SPO引入了一些新的超参数，如D_half, p_min, p_max等，这些参数控制着价值追踪器的“遗忘”速率。论文没有提供关于这些超参数的敏感性分析，这对于算法在不同任务中的实际部署和调优至关重要。\n需要进一步验证和存疑之处： 在更大模型上的表现：实验仅基于一个8B模型。虽然结果令人信服，但SPO的性能优势能否线性扩展到100B以上的更大模型上，仍是一个开放问题。大模型的动态和学习行为可能与中小模型有很大差异。 对不同类型任务的普适性：论文聚焦于数学推理和工具使用。SPO在其他类型的任务，如长文本生成、创意写作或对话系统上的表现如何，还需要进一步的实验验证。 与更复杂基线的比较：虽然GRPO是一个强基线，但领域内还有其他改进方法（如论文中提到的A*-PO等）。更全面的比较将有助于更准确地定位SPO的优势范围。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发： “返璞归真”的设计哲学：最大的启发是，面对复杂问题，不一定要用更复杂的方案。SPO回归了最简单的单流范式，通过精巧地改进关键组件（基线估计、归一化），反而获得了更好的效果。这启示我们，在自己的研究或工程中，应时常审视和优化最基础的模块，而不是盲目堆砌复杂性。 解耦的力量（The Power of Decoupling）：SPO成功的关键在于解耦。它将GRPO中紧密耦合的几个环节——样本生成、基线估计、优势归一化——分离开来，并为每个环节设计了更优的独立方案。这是一个极其强大的系统设计原则，可以应用到任何复杂的工程项目中。 从“数据为王”到“数据效率为王”：SPO的优先化采样体现了从“拥有更多数据”到“更智能地使用数据”的转变。这启发我们，在训练资源有限的情况下，设计一个聪明的“课程表”，让模型在每个阶段都学习最有效率的知识，可能比单纯增加数据量更重要。 可以拿来即用的创新想法： 持久化价值追踪器：无论是否在LLM领域，只要你使用策略梯度类算法，都可以借鉴SPO的价值追踪器思想。用一个自适应EMA来追踪每个状态（或在LLM中是prompt）的价值，作为稳定的基线，这是一个非常通用且易于实现的改进点。 全局归一化：在任何需要对中间变量（如优势、奖励）进行归一化的场景，如果可以，尽量在一个更大的、更具代表性的样本集上进行全局归一化，以获得更稳定的统计量。 异步化改造：检查你的分布式训练流程，识别其中的“同步屏障”，思考是否能像SPO一样，通过异步化设计来消除瓶颈、提升吞吐量。 需要补充的背景知识： 策略梯度（Policy Gradient）与REINFORCE算法：这是理解本文的基础。你需要明白策略梯度定理，以及为什么需要引入基线（baseline）来降低方差。论文的第3节对此有简要介绍，但更系统的学习会让你理解得更深刻。 近端策略优化（Proximal Policy Optimization, PPO）：SPO最终使用了PPO的裁剪目标函数（L^CLIP）来更新策略。了解PPO的工作原理，尤其是其如何稳定训练过程，会帮助你更好地理解SPO的最后一步。 贝叶斯统计基础：特别是共轭先验的概念。理解Beta分布是伯努利分布的共轭先验，会让你明白为什么SPO在处理二元奖励时选择Beta分布来建模成功率是如此自然和优雅。 强化学习中的可验证奖励（RLVR）：这是SPO所处的具体训练范式，与RLHF（基于人类反馈的强化学习）相对应。RLVR的奖励是客观的（如数学题对错），而RLHF的奖励是主观的人类偏好。了解这一背景有助于你把握SPO的应用场景。 ","date":"2025-09-23T00:00:00Z","permalink":"/single-stream-policy-optimization/","title":"Single-stream Policy Optimization"},{"content":"主要创新点是将原本的json的工具调用转变成了全部使用python代码，从而出现了python代码相较于json没有的优势，此外本文也引入了课程学习，即一开始学习不难不易的样本。最近课程学习的论文也很多，像Single-stream Policy Optimization等，主要原因使用GRPO算法如果问题过难或者过于简单，会导致奖励的标准差过小，会陷入训练停滞，影响训练效率和稳定性。\nimage.png 论文深度解读 这篇论文的核心目标是解决一个关键问题：如何让大型语言模型（LLM）像一个能干的“数字员工”一样，在面对复杂、动态的真实世界任务时，能够灵活、高效且可靠地使用各种外部工具（如搜索引擎、代码解释器、文件阅读器等）。现有的LLM虽然在语言理解和知识储备上表现出色，但它们本质上是“静态”的，知识截止于某个时间点，且无法执行需要精确计算或与外部环境交互的操作。这极大地限制了它们的应用潜力。\n为了弥克这一差距，研究领域主要探索了两条路径：提示工程（Prompt Engineering）和强化学习（Reinforcement Learning, RL）。提示工程方法，如著名的ReAct框架，通过精心设计的提示词引导模型进行“思考-行动”的循环，但这种方法严重依赖模型自身的内部知识来决定如何使用工具，当遇到环境的非预期反馈（如API调用失败、搜索结果不佳）时，其适应性和鲁棒性较差。而强化学习方法天生适合通过与环境的试错交互来学习最佳策略，但它面临两大瓶颈：1) 灵活性不足，传统方法常使用死板的JSON格式来调用工具，难以实现跨步骤的变量共享和复杂的逻辑组合；2) 训练成本高昂，RL需要大量的在线采样（即真实地调用工具），这既耗时又可能耗费API费用，导致其应用范围受限。\nTool-R1正是在这个背景下应运而生，它巧妙地融合了代码生成的灵活性和强化学习的自适应性，并着重解决了训练效率的难题。论文的创新性贡献可以概括为三个层面：\n以代码为中心的工具调用范式：Tool-R1最核心的转变是摒弃了JSON格式，转而让LLM生成可执行的Python代码来调用工具。这看似简单的改变带来了巨大的优势。如论文中图1所示的例子，模型为了计算“需要移走多少杯蜂蜜才能让其比蛋黄酱轻”，它生成了多步Python代码。第一步调用wikipedia_qa搜索密度，失败后，它能在第二步反思（Reflection）并切换到web_qa工具。更重要的是，在后续步骤中，它能将搜索到的密度值（1.420, 0.910）存储在Python变量中，并在后续的计算代码中直接使用这些变量。这种跨步骤的状态维持和变量共享能力是JSON格式难以企及的，它赋予了Agent解决复杂、依赖上下文任务的强大能力。\nThis design not only supports user-defined tools … but also enables intermediate variable sharing across steps, allowing LLMs to construct coherent and multi-round tool-use workflows.\n结果驱动的自动化奖励机制：如何评价Agent执行得好不好？Tool-R1设计了一套完全自动化的结果驱动奖励函数（Outcome-Driven Reward），无需昂贵的人工标注。这个奖励函数由三部分组成：\n最终答案正确性 (R_answer)：利用一个轻量级的LLM-as-Judge模型（论文中使用Qwen2.5-3B-Instruct），对比Agent生成的最终答案和标准答案，并给出“正确”（1分）、“部分正确”（0.5分）或“错误”（0分）的评价。这是一种非常聪明的“以子之矛，攻子之盾”的策略。 代码可解析性 (R_parse) 和 代码可执行性 (R_exec)：这两个是辅助奖励，分别用于奖励模型生成语法正确的代码和能够无错运行的代码。这引导模型产出不仅语义正确，而且技术上可靠的“行动指令”。论文中明确指出，总奖励 R = R_answer + 0.3 * R_parse + 0.3 * R_exec，这种组合奖励确保了模型在追求最终目标的同时，也注重过程的稳健性。 样本高效的训练优化策略：这是Tool-R1在工程实践上的关键突破，直接解决了RL训练成本高昂的痛点。它引入了两个核心技术：\n难度感知的数据筛选（Difficulty-aware Data Filtering）：研究者发现，训练集中太简单或太难的任务对模型学习帮助不大。因此，他们先用一个初始模型对每个问题尝试解答10次，只保留那些“中等难度”（成功率在20%到80%之间）的问题用于训练。这确保了训练资源被用在“刀刃上”。 动态样本队列（Dynamic Sample Queue）：这是提升效率的神来之笔。传统的RL在每轮训练时都需要为每个任务生成全新的G个执行轨迹（trajectories）。Tool-R1则维护一个大小为G的队列，每轮只生成g个（g \u0026lt; G）新轨迹，替换掉队列中最旧的g个。这意味着大部分训练样本（G-g个）被复用了，极大地减少了与外部工具的交互次数。实验结果（表2）惊人地证明了这一点：在几乎不损失精度的情况下，训练时间从41.5小时锐减到22.3小时，几乎减半。 总而言之，Tool-R1不仅仅是一个新模型，更是一个设计精巧、务实高效的Agent训练框架。它通过“代码生成”、“自动化奖励”和“高效采样”这三把利剑，成功地提升了AI Agent在复杂任务中的灵活性、可靠性和训练效率，为构建更强大的实用型Agent铺平了道路。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是开发一个样本高效的强化学习框架（Tool-R1），以训练大型语言模型（LLM）能够通用、可组合、多步骤地使用外部工具来解决复杂的现实世界问题。\n解决的实际问题：\nLLM的能力边界问题：LLM无法访问实时信息（如今天的天气）、无法执行精确计算（如复杂的数学运算）、也无法与私有知识库或软件交互。这使得它们在许多实际应用场景中束手无策。 现有工具使用方法的局限性： 提示工程：不够鲁棒，无法从环境的动态反馈中学习和适应。 传统RL方法：不够灵活（依赖JSON），且训练成本过高，难以规模化应用。 对行业发展的重要意义：\n推动LLM从“聊天机器人”向“行动执行者”转变：这是当前大模型领域最重要的发展方向之一。一个能自主规划、使用工具并完成任务的AI Agent，其商业价值和社会价值远超一个只能对话的模型。 降低Agent的训练门槛：通过解决样本效率问题，Tool-R1使得用中等规模的计算资源训练一个有能力的Agent成为可能，这将极大地促进该技术的普及和应用创新。 赋能更复杂的应用场景：能够可靠使用工具的Agent可以在企业自动化（RPA）、智能个人助理、科学研究（自动进行数据分析和文献检索）、软件开发（自动编码和调试）等领域发挥巨大作用，是实现通用人工智能（AGI）道路上的关键一步。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ Tool-R1的核心创新在于其训练框架的整体设计，具体可以分解为以下几点，并与之前的方法进行对比：\n创新点 Tool-R1 的方法 传统方法对比 特点与优势 1. 工具调用接口 生成可执行的Python代码 生成结构化的JSON/YAML 灵活性与组合性：代码天然支持变量、循环、条件判断等复杂逻辑，可以轻松实现多工具的协同工作和跨步骤的信息传递。而JSON只是简单的函数调用，难以表达复杂流程。 2. 学习范式 强化学习（RL） 提示工程 (如 ReAct) 或 监督微调 (SFT) 适应性与自进化：RL通过与环境的真实交互和奖励信号进行学习，能适应工具返回的各种预期外结果（如错误、空值），并调整策略。而提示工程和SFT都是基于固定模式，缺乏这种动态适应能力。 3. 奖励信号来源 自动化结果驱动奖励：LLM-as-Judge + 代码执行成功率 人工标注 或 简单的规则奖励 低成本与可扩展性：完全摆脱了对昂贵的人工标注的依赖，使得在大量不同任务上进行训练成为可能。奖励信号也更全面，兼顾了最终结果和过程的可靠性。 4. 训练效率 动态样本队列 + 难度筛选 完全在线采样 高效率与低成本：通过复用历史轨迹，大幅减少了耗时的在线工具调用，显著降低了训练时间和计算成本，解决了RL for Agent领域的核心痛点。 We introduce Tool-R1, a novel method that supports general and compositional tool use across complex real-world tasks, with LLMs trained through sample-efficient reinforcement learning.\n这个框架的整体优势在于，它在表达能力（代码）、学习能力（RL）、评估能力（自动奖励）和训练效率（样本队列） 这四个维度上都做出了显著的改进，形成了一个闭环的、可持续优化的Agent训练系统。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文的实验设计得非常严谨且有说服力，主要通过在权威基准上的性能对比和详细的消融研究来验证其有效性。\n实验设计：\n基准（Benchmark）：选用了GAIA基准。这是一个专为通用AI Agent设计的挑战性数据集，其任务需要模型理解复杂指令，并与多种类型的真实文件（如PDF, XLSX, PPTX）和工具（如网页浏览）进行交互，非常贴近现实世界场景。 对比对象：论文将Tool-R1与多种模型进行了对比，包括： 闭源模型：如GPT-4。 开源基线模型（无微调）：如Qwen2.5-Instruct，用于展示微调带来的提升。 其他微调方法：如MAT Agent，这是一个使用监督微调（SFT）的强大Agent模型。 核心评估指标：答案准确率（AnsAcc），即Agent给出的最终答案是否与标准答案一致。 实验数据和结果：\n核心性能对比（表1）：这是最关键的结果。Tool-R1（基于Qwen2.5-14B）在GAIA上取得了26.67%的准确率，在所有开源模型中表现最佳。尤其值得注意的是以下对比： 与MAT Agent相比，后者使用了2万个高质量问答对和GPT-4生成的轨迹进行训练，准确率仅为16.97%。而Tool-R1只用了1300个问答对，且无需昂贵的GPT-4轨迹，性能却远超对方。这强有力地证明了Tool-R1的样本高效性。 与基线模型Qwen2.5-14B-Instruct（准确率15.15%）相比，Tool-R1带来了超过11个百分点的巨大提升，证明了其训练框架的有效性。 这是一个根据论文Table 1简化的图表，用以说明核心性能对比。\n消融研究（Ablation Study，表2）：这部分实验旨在验证Tool-R1中每个创新组件的价值。研究者在Qwen2.5-7B模型上逐一添加或移除不同组件，观察性能变化。 难度筛选：加入后，准确率从9.09%提升到16.36%（+7.27%），效果显著。 辅助奖励：再加入代码解析/执行奖励后，准确率提升至18.79%（+2.43%），并且首次在最难的Level 3任务上取得了非零的准确率（3.84%）。 动态队列：最后加入动态样本队列，准确率进一步提升至19.39%，同时训练时间从41.5小时减少到22.3小时。 These results validate that each proposed component contributes meaningfully to both model performance and training efficiency.\n这个消融实验清晰地量化了每个设计决策的贡献，使得论文的结论非常坚实可靠。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个研究方向正处在爆发期，未来充满了机遇和挑战：\n挑战与探索方向： 工具的规模化与泛化：Tool-R1只使用了几种预定义工具。当工具有成千上万个（如一个公司的所有API），或者需要Agent泛化到从未见过的工具时，如何进行有效的工具检索、选择和学习，是一个巨大的挑战。 更长期的任务规划与记忆：现实世界的任务可能需要几十甚至上百步才能完成。当前的Agent框架在长期记忆、复杂任务分解和动态规划方面还很薄弱，容易“迷失”。 安全性与可靠性：执行模型生成的代码存在天然的安全风险。如何构建一个既功能强大又绝对安全的沙箱（Sandbox）环境，防止恶意代码执行，是该技术落地前必须解决的问题。同时，如何提升Agent在关键任务上的可靠性，减少“幻觉”和错误，也是一个持续的挑战。 自主学习与工具创造：更进一步，Agent能否不仅仅是使用工具，而是能够根据任务需求，自主地创造新工具（例如，通过编写一个新的Python函数或API封装）？这需要模型具备更高层次的抽象和推理能力。 新技术和投资机会： AI Agent平台：提供Agent开发、训练、部署和安全沙箱的一体化平台将是巨大的市场机会。这类似于移动应用时代的App Store和开发工具链。 垂直领域的超级助理：在金融、法律、医疗、科研等专业领域，能够熟练使用领域专用工具和数据库的AI Agent，将成为强大的生产力工具，催生新的服务模式。 新一代自动化技术：当前的RPA（机器人流程自动化）技术将被更智能、更灵活的AI Agent所取代，能够处理更复杂的非结构化任务，带来企业运营效率的革命。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管Tool-R1非常出色，但从批判性角度看，仍存在一些可以探讨的局限性：\nLLM-as-Judge的可靠性：整个奖励机制的核心依赖于一个3B参数的LLM作为裁判。虽然高效，但这个“裁判”自身的判断能力是有限的。对于一些答案正确但表述方式新颖、或涉及复杂事实核查的任务，它可能会出现误判。这种潜在的评估偏差可能会影响模型的学习方向。 任务的泛化性验证不足：实验主要在GAIA这一个基准上进行。虽然GAIA很全面，但它是否能完全代表所有类型的真实世界任务，仍有待商榷。模型在GAIA上学到的策略能否很好地泛化到其他类型的任务或工具集上，论文并未提供充分的证据。 绝对性能仍有提升空间：尽管在开源模型中领先，但26.67%的最高准确率说明GAIA的任务对于当前最先进的Agent来说依然极具挑战。这表明距离真正可靠、通用的AI Agent还有很长的路要走。 代码生成复杂度的局限：从论文的示例来看，生成的Python代码逻辑相对直接。对于需要定义复杂类、处理并发、或者进行精细错误处理的场景，当前框架的能力可能不足。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是创新思想的宝库，您可以重点学习和借鉴以下几点：\n范式转变：将代码视为通用的行动指令 启发：在您的项目中，如果需要模型与外部系统交互，可以考虑让模型直接生成可执行的代码（如Python, SQL, Bash脚本），而不是设计复杂的JSON API格式。这能极大地提升灵活性和任务表达能力。 数据驱动的效率优化：聚焦于“有效学习区” 启发：论文中“难度感知的数据筛选”思想非常实用。在任何机器学习任务中，都可以借鉴这个思路，分析数据集，识别出对模型学习最“有价值”的样本子集，从而在有限的计算资源下实现更高的训练效率。 自监督的奖励设计：用模型评估模型 启发：当缺乏人工标注时，可以尝试设计一个自动化的评估和奖励机制。利用一个（通常更强的或专门的）模型作为“裁判”来评估另一个模型的输出，是一种非常前沿且有效的方法，可应用于文本生成、代码生成等多种任务的优化。 迭代与复用：用“队列”为昂贵操作提速 启发：动态样本队列的思想可以泛化到任何需要昂贵在线数据生成的场景。如果您的训练过程中某个环节是瓶颈（如API调用、物理仿真、A/B测试），可以考虑设计一个缓存或队列机制，复用历史生成的数据，以加速整个迭代过程。 补充背景知识建议：\n强化学习基础：特别是策略梯度（Policy Gradient）方法和PPO（Proximal Policy Optimization）算法。Tool-R1使用的GRPO是其变体，了解基础会更有助于理解。 AI Agent框架：了解经典的ReAct (Reason and Act)框架，因为它奠定了Agent“思考-行动”循环的基础，Tool-R1是在这个思想上的进一步发展。 LLM-as-Judge：了解这个新兴的评估范式，即如何使用一个LLM来评估另一个LLM的输出质量。这是实现自动化奖励设计的关键技术。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-09-23T00:00:00Z","permalink":"/tool-r1sample-efficient-reinforcement-learning-for-agentic-tool-use/","title":"Tool-R1：Sample-Efficient Reinforcement Learning for Agentic Tool Use"},{"content":"agent tool use的工作，解决的问题是传统奖励模型无法准确给予格式的工具调用奖励。方法是构建数据使用DPO训练了一个专门给工具调用奖励的奖励模型。其应用场景为推理时优化，即同时生成多个工具调用然后让模型判断选择。还有就是可以进行拒绝采样来提高数据质量进行sft。\n论文深度解读 随着GPT-4、Claude和Gemini等模型的普及，我们已经见证了大型语言模型在自然语言处理任务上的卓越表现。然而，要让这些模型真正成为能够解决现实世界问题的智能代理（Agent），就必须赋予它们与外部世界交互的能力，而工具调用便是实现这一目标的核心技术。它允许LLM像程序员调用API一样，去使用计算器、搜索引擎、数据库、订票系统等外部工具，从而极大地扩展了其能力边界。\n然而，一个严峻的挑战随之而来：LLM生成的工具调用指令，其质量参差不齐。它可能会调用错误的工具、提供错误的参数，或者遗漏必要的信息。我们如何判断一次工具调用的好坏？又如何利用这种判断来训练出更优秀的模型呢？传统的奖励模型（Reward Model, RM），通常用于对模型的自然语言输出进行评分（比如判断一段文字是否流畅、有帮助），但在评估结构化、逻辑性极强的工具调用时，却显得力不从心。\n这篇论文正是为了解决这一“AI质检员”缺位的问题。作者们敏锐地发现，现有行业标准中，既没有一个专门用于评估工具调用奖励模型的基准（Benchmark），也没有一个足够强大且高效的奖励模型本身。\n为此，他们做了两件核心工作： 1. 构建并发布了第一个专门评估工具调用奖励模型的基准——FC-RewardBench。这个基准的巧妙之处在于，它不仅包含了正确的工具调用，还利用25个不同的开源模型生成了大量“真实世界”中常见的错误调用。这些错误不是随机杜撰的，而是模型在实际操作中真正会犯的错误，比如论文中Table 1列出的“参数值错误”、“函数名错误”等。这使得FC-RewardBench能够非常精准地衡量一个奖励模型“明辨是非”的能力。\n提出并训练了一个专门用于工具调用的奖励模型系列——ToolRM。与通用奖励模型不同，ToolRM的训练数据完全聚焦于工具调用场景。作者们设计了一套高效的数据合成框架：利用一系列开源模型去执行工具调用任务，如果模型的输出与标准答案（ground-truth）不符，这个错误的输出就被标记为“负样本”，而标准答案则为“正样本”。通过这种方式，他们构建了一个庞大的、包含“正确vs错误”的偏好数据集，并以此训练出了从1.5B到14B参数不等的一系列ToolRM模型。 论文最令人印象深刻的成果在于其实用价值。作者不仅证明了ToolRM在评价任务上的优越性，还展示了它在两个关键应用场景中的巨大潜力： * 推理时优化（Best-of-n Sampling）：让一个基础模型生成多个（例如32个）工具调用候选方案，然后用ToolRM作为“裁判”，从中选出得分最高的那个。实验结果（Figure 4）显示，这种方法能显著提升模型的表现，尤其是对于中小型模型，其性能提升甚至能追平或超越比它大得多的模型。例如，一个0.6B参数的Qwen3模型，在ToolRM的帮助下，准确率从39.5%跃升至64.38%，提升了惊人的24.9个百分点。 * 数据过滤（Data Filtering）：在微调（fine-tuning）模型时，数据质量至关重要。作者证明，使用ToolRM对训练数据进行筛选，只保留高质量的样本，可以用更少的数据达到更好的训练效果。Table 3的数据显示，使用ToolRM筛选出的8K条高质量数据训练的模型，其平均性能（62.5%）甚至超过了使用全部16K条数据训练的模型（61.0%）。这意味着ToolRM可以帮助我们事半功倍地训练出更强大的工具调用模型。\n总而言之，这篇论文不仅仅是提出一个新模型，更是提供了一整套从“评估标准”到“优化工具”再到“应用方法”的完整解决方案。它为如何系统性地提升大模型工具调用这一核心能力，铺平了道路，具有极高的学术价值和产业应用价值。\n1. 论文的研究目标与问题意义 研究目标：论文的核心研究目标有两个：\n建立一个标准化的评估体系：创建一个专门用于衡量奖励模型在工具调用场景下性能的基准（即FC-RewardBench）。 开发一个高效的专用奖励模型：训练一个专门为工具调用任务设计的结果奖励模型（Outcome Reward Model, ORM），即ToolRM，并验证其在评估和优化LLM工具调用能力方面的有效性。 解决的实际问题： 当前，大模型正在从“文本生成器”向“任务执行器”转变。让模型学会使用工具，是实现这一转变的关键。然而，如何确保模型能够正确、可靠、高效地使用工具，是一个巨大的挑战。目前的痛点在于：\n缺乏评估标准：我们不知道现有的奖励模型在工具调用这个特殊任务上表现如何。 通用模型不适用：被训练来理解自然语言好坏的奖励模型，无法很好地理解工具调用的结构化和逻辑性错误。 训练成本高昂：依赖人工标注来判断工具调用的对错，既昂贵又缓慢，无法规模化。 行业发展意义： 这个问题是大模型走向实用化和产品化的关键瓶颈。如果模型不能可靠地调用工具，那么构建在其之上的自主代理（Autonomous Agents）、复杂工作流自动化（Orchestration）等应用都将是空中楼阁。 \u0026gt; As LLMs become increasingly central to AI systems deployed in real-world settings, the demand for them to go beyond language generation and interact with external systems and tools has grown substantially.\n解决这个问题，意味着：\n提升可靠性：可以显著降低AI代理在执行任务时出错的概率，使其在金融、医疗、自动化客服等关键领域的应用成为可能。 降低成本：通过自动化的奖励模型进行评估和数据筛选，可以大幅降低模型迭代和优化的成本。 推动Agent技术成熟：为构建更复杂、更强大的AI Agent提供了核心的基础设施，是通往通用人工智能（AGI）道路上的重要一步。 2. 论文提出的新思路、方法与模型 核心思路： 论文的核心思路是“专业问题，专业解决”。作者认为，工具调用是一个具有特殊语法和语义规则的领域，不能用评估一般文本的“尺子”来衡量。因此，必须构建专用的评估基准和奖励模型。\n新方法与模型：\nFC-RewardBench的构建方法： 特点：其创新之处在于负样本的生成方式。它并非依赖人工构造或简单的规则扰动，而是利用了25个不同规模和架构的开源模型在真实任务中产生的错误。 优势：这种方法捕获了模型在实际应用中最常犯、最多样、最微妙的错误。从Table 1可以看出，这些错误类型分布广泛，其中“参数值错误”（Incorrect Parameter Value）占比最高，这类错误往往是语义层面的，而非简单的语法错误，对奖励模型提出了更高的要求。 \u0026gt; This data generation approach captures the nuance and variability of real-world model behavior, reflecting the kinds of errors that actual models tend to make in practice, including subtle and complex failure modes that may be difficult to anticipate or enumerate manually. ToolRM的训练框架： 特点：采用了结果奖励模型（ORM）的路线，只评估最终的工具调用结果，而非中间的思考过程。这大大降低了数据标注的难度。其训练数据是通过“自博弈”式的合成产生的：让模型生成答案，凡是与正确答案不一致的，都构成一个“正确 vs 错误”的偏好对。 优势： 高度可扩展：无需昂贵的人工标注，可以利用大量已有的函数调用数据集，快速、低成本地生成海量训练数据。 任务专注：由于训练数据全部是工具调用相关的，ToolRM对该任务的理解远超通用奖励模型。 架构高效：基于预训练模型（如Qwen-2.5）进行改造，只替换最后一层为线性头输出一个标量奖励值，训练高效且能充分利用基础模型的语言理解能力。 3. 实验设计与关键数据结果 论文通过三个环环相扣的研究问题（RQ1, RQ2, RQ3）来系统性地验证其方法的有效性。\n实验一：ToolRM在FC-RewardBench上的性能评估 (RQ1) 设计：在FC-RewardBench上，直接比较ToolRM与现有顶级的通用奖励模型（来自RewardBench）以及大型LLM作为裁判（LLM-as-a-Judge）的准确率。准确率的定义是：奖励模型为“正确调用”打出的分数是否高于为“错误调用”打出的分数。 数据与结果：Figure 1 的结果极具说服力。 ToolRM表现最佳：ToolRM (14B) 的准确率接近90%，远超其他所有模型。 小模型超越大模型：即使是1.5B的ToolRM，其性能也超过了庞大的gpt-oss-120B模型，充分证明了专业化训练的优势远大于单纯的模型尺寸。 通用RM的失败：大多数在RewardBench上表现优异的奖励模型，在工具调用这个新领域表现平平，准确率徘徊在60%-80%之间，证明了它们确实无法很好地泛化到工具调用任务。 图1：ToolRM、其他奖励模型及LLM裁判在FC-RewardBench上的性能对比（简化版） 实验二：通过Best-of-n采样提升推理性能 (RQ2) 设计：让一个基础LLM针对一个任务生成n=32个候选工具调用，然后使用ToolRM作为排序器（ranker）选出最佳答案。比较这种方法与传统的贪心解码（Greedy Decoding）和多数投票（Majority Voting）的性能差异。 数据与结果：Figure 4 和 Table 2 展示了显著的性能提升。 对小模型提升巨大：如前所述，Qwen3-0.6B模型准确率提升了24.9个百分点。这表明ToolRM可以作为一种“性能放大器”，让小模型的潜力得到充分发挥。 普遍有效：无论是Qwen系列还是xLAM系列模型，无论模型大小，ToolRM都能带来稳定的性能增益。例如，在BFCL-v3数据集上，xLAM-2-1B-fc-r模型在使用ToolRM-14B后，整体准确率从54.09%提升至57.28%。 实验三：利用ToolRM进行数据过滤以优化微调 (RQ3) 设计：使用一个包含16K样本的工具调用数据集，对Llama-3.1-8B模型进行微调。设置三组对比实验：\n使用全部16K数据进行训练。 随机抽取8K数据进行训练。 使用ToolRM-14B对16K数据打分，筛选出分数最高的8K数据进行训练。 数据与结果：Table 3 的结果清晰地展示了数据质量的重要性。 \u0026gt; Selecting the top 50% of samples with ToolRM-14B yields the strongest results at 62.5%, surpassing the full-data model while using only half the training corpus.\n质量胜于数量：用ToolRM筛选出的一半数据，训练效果（平均准确率62.5%）超过了全部数据（61.0%），并且远好于随机抽样的那一半数据（58.4%）。 实用价值：这证明了ToolRM是一个非常有效的数据预处理工具，可以在有限的计算预算和数据预算下，最大化模型的训练效益。 4. 未来研究方向、挑战与机遇 值得探索的问题与挑战： 从判别式到生成式验证器（Generative Verifiers）：目前的ToolRM只给出一个分数，未来可以探索让奖励模型生成“判决理由”（即Chain-of-Thought），解释为什么一个工具调用是好的或坏的。这能极大地增强模型的可解释性和鲁棒性。 处理有状态和多步任务：现实世界的任务往往是多步且有状态的（例如，先搜索航班，再预定座位）。当前的FC-RewardBench主要关注单步、无状态的调用。未来的研究需要将工具执行后的环境状态反馈（如API返回的错误码）也纳入奖励模型的考量范围。 融合过程奖励（PRM）与结果奖励（ORM）：ORM虽然高效，但无法保证过程的正确性。未来可以探索将两者结合，形成一个既高效又能进行细粒度控制的混合奖励框架。 催生的新技术和投资机会： AI质量保证（AI Quality Assurance）：专门为Agent和工具调用系统提供质量评估、监控和优化服务的平台将有巨大市场。ToolRM就是这类平台的核心技术。 高质量数据集即服务（High-Quality Dataset as a Service）：利用类似ToolRM的技术，可以为AI开发者提供经过筛选和优化的、用于特定任务（如工具调用、代码生成）的高质量训练数据集。 更强大的Agent开发框架：未来的AI Agent开发框架，将内置强大的奖励模型作为其核心组件，用于自我评估、自我修正和持续学习。 “小模型+强裁判”的新范式：对于许多垂直应用，部署巨大的通用模型成本过高。采用一个轻量级的专用模型，配合一个强大的奖励模型（如ToolRM）进行推理时优化，可能成为一种更具性价比的解决方案，这为小模型公司和边缘计算应用带来新的机遇。 5. 批判性视角：论文的不足与存疑 尽管这篇论文非常出色，但从批判性的角度审视，仍有一些方面值得商榷或需要进一步探索：\n基准的局限性：FC-RewardBench虽然在“真实错误”的模拟上做得很好，但它依然是静态和无状态的。它无法评估多轮交互中依赖上下文的工具调用序列，也无法评估工具调用失败后模型的纠错和恢复能力，而这些是高级Agent的关键能力。\n对“不调用”场景的偏见：论文在附录A.5的错误分析（Table 5）中坦诚了一个有趣的现象：在使用ToolRM进行Best-of-n优化后，“不相关错误”（Irrelevance Errors）的数量从185增加到了210。 \u0026gt; In contrast, Irrelevance Errors increase from 185 to 210, suggesting that ToolRM tends to favor producing function call outputs even in cases where no valid call can appropriately satisfy the user query.\n这说明ToolRM可能存在一种“过度活跃”的偏见，即它倾向于鼓励模型进行工具调用，哪怕在用户问题不需要或无法通过工具解决时。这可能会导致模型在某些情况下做出不必要的、甚至有害的尝试。\n合成数据的天花板：ToolRM的训练完全依赖合成数据。虽然这很高效，但可能会缺少人类偏好中更微妙的部分。例如，两个工具调用在功能上都正确，但一个更简洁、更符合人类习惯。这种细微的差异，仅靠“正确vs错误”的合成数据可能难以捕捉。模型在真实的人类偏好数据集上的表现如何，尚待验证。\n泛化能力的考验：论文虽然在多个基准上进行了测试，但这些基准的工具定义和任务类型仍有一定范围。当面对与训练数据（如API-Gen, SGD等）的领域和风格截然不同的、全新的工具集时，ToolRM的泛化能力还需要更严苛的考验。\n6. 核心启发与知识补充 我应该从这篇论文中学到的核心思想： 专业化是王道：在特定领域，一个精心设计的、小而美的专用模型，其效能可以远超一个庞大但“大而不精”的通用模型。这是资源有限的团队和个人进行创新的重要突破口。 数据是真正的护城河：这篇论文的亮点不在于新颖的网络结构，而在于其创造高质量、专业化训练数据的方法。如何以低成本、可扩展的方式生成高质量数据，是当前AI领域创新的核心。 奖励模型的多重价值：奖励模型不仅仅是用于RLHF训练的“幕后英雄”，它本身就可以成为一个强大的产品化工具，用于推理时优化（提升线上效果）和数据流水线优化（提升训练效率）。 给我的启发： 寻找“评估”的缺口：在一个新兴技术领域，率先建立起一套权威的评估标准（Benchmark），本身就是一项重大的贡献，能够引领后续的研究方向。 将模型作为数据生成器：利用现有模型来创造新的训练数据（无论是生成负样本，还是进行数据增强），是一种极其强大且高效的策略。 应用驱动的研究：这篇论文的三个研究问题（RQ1, RQ2, RQ3）紧密围绕应用展开，从“能不能评”到“能不能用”，再到“能不能帮”，层层递进，这种研究范式值得学习。 需要补充的背景知识： 人类反馈强化学习 (RLHF) / 直接偏好优化 (DPO)：需要理解奖励模型在整个大模型对齐和训练流程中的核心作用和基本原理。 工具调用与Agent技术：了解什么是函数调用（Function Calling），以及像ReAct (Reason+Act) 这类框架是如何让LLM与工具进行交互的。 相关的评估基准：了解一下通用的奖励模型基准如RewardBench，以及通用的工具调用基准如BFCL (Berkeley Function Calling Leaderboard)，这能帮助你更好地理解FC-RewardBench的独特性和贡献。 ","date":"2025-09-23T00:00:00Z","permalink":"/toolrmoutcome-reward-models-for-tool-calling-large-language-models/","title":"ToolRM：Outcome Reward Models for Tool-Calling Large Language Models"},{"content":"将规划中的两种常见做法，直接生成可执行动作和先生成计划后映射为可执行动作折中一下，让模型在比计划具体比动作抽象的skill空间中选择，然后再利用翻译器进行确切翻译。不使用任何训练就可以达到好性能。\n这篇论文的核心目标是解决一个当前AI智能体领域最为棘手的问题之一：如何让大语言模型（LLM）在一个复杂的、需要长远规划、并且存在对抗（adversarial）因素的环境中，有效地将它的“思考”转化为精确的“行动”。这在学术上通常被称为“接地”（Grounding）问题，即如何将语言这种抽象符号与现实世界的具体操作联系起来。\n论文开篇就指出了现有方法的两大困境，并通过图1清晰地展示了出来：\n* 方法(a) - 直接生成底层动作：让LLM直接输出游戏或环境中的每一个微小操作（比如“向上移动一个像素”）。这种方法的弊端是显而易见的：动作空间（所有可能操作的集合）极其巨大，LLM很容易生成无效或无意义的指令，这被称为“维度灾难”（curse of dimensionality）。 * 方法(b) - 生成高层任务：让LLM生成一个模糊的、宏观的计划（比如“发展经济”）。这种方法的问题在于，如何将这个宏观计划精确地翻译成一系列可执行的底层动作，这个“翻译”过程本身就非常困难，通常依赖大量的人工规则或额外的模型训练。\n面对这一困境，本文作者提出了一个非常巧妙且实用的“中间道路”——PLAP (Plan with Language, Act with Parameter) 框架。这个框架的精髓在于引入了一个“参数化技能”（Parameterized Skills）的中间层。你可以把一个参数化技能理解为一个带有明确参数的、人类可理解的指令。例如，Attack(unit, enemy)就是一个技能，它的参数是“用哪个单位”和“攻击哪个敌人”。这个技能比“发展经济”具体得多，又比“向上移动一个像素”抽象得多。\nPLAP框架的整个工作流程可以用论文中的图2来概括：\n1. 观察与理解：智能体首先观察环境状态（比如游戏画面），并将其转化为一段结构化的文本描述。 2. 规划（Plan with Language）：这段文本描述会连同一个预先设计的提示（Prompt）一起发送给LLM。这个提示非常关键，它包含了游戏规则、所有可用的“参数化技能”列表、以及当前战况。LLM作为“技能规划器”（Skill Planner），它的任务就是根据这些信息，生成一个由参数化技能组成的行动计划。 3. 执行（Act with Parameter）：这个技能计划（例如，[Produce Unit](worker, east)）会被发送到一个“技能执行器”（Skill Executor）模块。这个执行器是一个确定性的代码模块，它负责将这些高级技能精确地翻译成一连串底层的、环境可执行的动作指令序列。\n这套框架最吸引人的地方在于它的高效和通用性。它不需要对LLM进行任何额外的训练或微调，仅仅通过精巧的提示工程（Prompt Engineering）就释放了现有LLM强大的规划能力。同时，通过将模糊的规划任务与精确的执行任务解耦，极大地提升了智能体的稳定性和可靠性。\n为了验证PLAP框架的有效性，研究人员选择了一个名为MicroRTS的即时战略游戏作为试验场。这个选择非常明智，因为RTS游戏天然具备长时程规划、对抗性和高复杂度的特点。更重要的是，如论文图6所示，他们通过问答测试证明了，绝大多数LLM对MicroRTS的知识几乎为零，QA准确率平均只有15%，这排除了模型因为“背诵”了相关攻略而表现出色的可能性，确保了实验的公正性。\n实验结果令人振奋。如论文表II所示，搭载了GPT-4o的PLAP智能体在与其他模型的对抗中取得了84.3%的惊人胜率，高居榜首。更有说服力的是，在图7中，一个使用了精心设计的少量样本（few-shot）和专家提示（expert tips）的Qwen2-72B模型，其表现甚至超越了为该游戏专门编写的、赢得过比赛冠军的顶尖脚本AI（CoacAI），这充分证明了PLAP框架的巨大潜力。\n总而言之，这篇论文不仅仅是提出了一个新模型，更是提供了一套将LLM的智慧“落地”到复杂任务中的工程范式。它通过“参数化技能”这一核心思想，优雅地解决了LLM智能体在规划与执行之间的鸿沟，并用扎实的实验和一个全新的排行榜基准（leaderboard）证明了其有效性，为未来更强大、更通用的AI智能体的研发铺平了道路。\n接下来，我将按照您提出的六个问题，对这篇论文进行更详细的拆解和分析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是设计一个通用框架，以有效赋能大语言模型（LLMs）在需要长时程规划（long-horizon planning）的对抗性环境（adversarial domains）中进行决策和行动。\n要解决的实际问题： 它旨在解决LLM智能体在与复杂环境交互时的“接地”（Grounding）难题。具体来说，就是如何将LLM生成的基于自然语言的、较为抽象的规划，转化为环境中精确、有效、无差错的底层动作序列。现有方法要么过于依赖LLM直接生成底层动作导致错误率高，要么生成过于宏观的计划而无法有效执行。\n对行业发展的重要意义： 这个问题是实现通用人工智能（AGI）道路上的关键瓶颈。解决了这个问题，意味着我们可以：\n加速AI智能体的开发：开发者不再需要为每个特定任务都去训练复杂的强化学习模型，而是可以利用现有强大的LLM，通过设计“技能库”来快速构建能胜任复杂任务的智能体。 拓宽LLM的应用场景：LLM将不再局限于聊天、写作等纯文本任务，而是可以成为机器人操作、游戏AI、自动化流程控制（RPA）、智能制造等领域的核心“大脑”。 降低技术门槛：通过“语言”来编程和指挥智能体，使得不具备深厚编程背景的领域专家也能参与到AI应用的开发中来，极大地促进了AI的普及和落地。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路与方法： 论文最核心的创新是提出了PLAP（Plan with Language, Act with Parameter）框架。其核心思路是在LLM的高层规划和环境的底层动作之间，插入一个“参数化技能”（Parameterized Skills）作为中间桥梁。\n方法细节分析： PLAP框架由三个关键组件构成：\n技能库（Skill Library）：这是一个预先定义好的、由人类专家设计的技能集合。每个技能都是一个函数，拥有明确的名称和参数，例如在MicroRTS中定义的Harvest Mineral(mineral_location)，Build Building(building_type, building_location)等。这个库为LLM的规划提供了一个清晰、有界的目标空间。 技能规划器（Skill Planner）：这部分由一个通用的LLM（如GPT-4o）担任。它接收一个精心构造的提示（Prompt），该提示将当前环境状态文本化，并附上可用的技能列表。LLM的输出不再是繁杂的底层动作，而是一个简洁的、由参数化技能组成的计划。 技能执行器（Skill Executor）：这是一个非LLM的、确定性的代码模块。它接收技能规划器生成的计划，并负责在接下来的多个时间步（time steps）中，将每个技能转化为具体的、可执行的底层动作序列，并注入到环境的动作队列中。 特点与优势： 与之前的方法相比，PLAP具有以下显著优势：\n无需额外训练：PLAP直接利用了预训练LLM的零样本（zero-shot）或少量样本（few-shot）能力，避免了传统强化学习或模仿学习方法中成本高昂的训练和微调过程。 \u0026gt; PLAP operates without extra learning costs and does not rely on expert experience or prior knowledge. 高效率与低成本：规划是以k个时间步为间隔进行的（实验中k=100），而不是每一步都调用LLM。这大大降低了LLM的调用频率，减少了推理时间和API成本，使其在即时决策场景中成为可能。 鲁棒性与稳定性：由于技能执行器是确定性的代码，它保证了从高层计划到底层动作的转换是无差错的。这解决了LLM直接生成动作时常出现无效或非法操作的问题。 可解释性与可控性：LLM生成的技能计划（如Attack(Worker1, Worker1)）是人类可读的，这使得智能体的决策过程更易于理解、调试和干预。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在即时战略游戏MicroRTS上进行的一系列详尽实验来验证PLAP框架的有效性。\n实验设计： 构建基准（Benchmark）：研究人员首先构建了一个名为Skill-RTS的基准测试环境，它将MicroRTS的复杂状态和动作空间封装成了对LLM友好的文本和参数化技能。 验证环境的陌生性：他们设计了一系列关于MicroRTS和另一款著名游戏星际争霸II（StarCraft II）的问答题，测试了8个主流LLM。结果（图6）显示，LLM对星际争霸II非常熟悉（平均准确率80%），而对MicroRTS则几乎一无所知（平均准确率15%），这证明了MicroRTS是一个公平的、能真正考验模型泛化规划能力而非记忆力的试验场。 多维度对比： 与传统AI对比：将不同LLM驱动的PLAP智能体与5个不同强度的基线AI（从随机策略到比赛冠军CoacAI）进行对战。 不同Prompt策略对比：测试了四种PLAP变体：零样本（zs-PLAP）、零样本+专家提示（zs-tip-PLAP）、少量样本（fs-PLAP）和少量样本+专家提示（fs-tip-PLAP）。 LLM横向对比：建立了一个排行榜（Leaderboard），让8个不同的LLM驱动的PLAP智能体互相进行循环赛，全面评估不同模型的规划能力。 关键数据与结果： PLAP框架全面优于基线AI：如图7所示，几乎所有版本的PLAP智能体得分都显著高于传统的基线AI。特别是，Qwen2-72B-fs-tip（使用少量样本和专家提示的Qwen2-72B）的平均得分（4.6）甚至超过了最强的基线AI CoacAI（得分4.0），这是一个里程碑式的成果。 顶尖LLM展现强大实力：在LLM排行榜（表II）中，GPT-4o以49分的总分和84.3%的胜率遥遥领先，展现出卓越的零样本规划能力。Gemini 1.5 Flash位居第二。这表明，更强大的基础模型能为PLAP框架带来更高的性能上限。 Prompt策略的影响：实验发现，专家提示（Expert Tips）对所有模型都有稳定提升。而少量样本（Few-shot）对中等规模模型提升巨大，但对于像GPT-4o这样的顶级模型，简单的零样本提示效果反而更好，这可能是因为过多的例子反而限制了其更高级的自主推理能力。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为LLM智能体研究打开了一扇大门，但前路依然充满机遇与挑战。\n值得探索的问题与方向： 技能的自动发现与学习（Automated Skill Discovery）：目前PLAP的技能库是人工设计的。未来的一个重要方向是让智能体能够在与环境的交互中自主学习和创造新的技能。这可能结合强化学习中的“期权（Option）”概念或大模型的代码生成能力来实现，让智能体能够动态扩展自身的能力边界。 更复杂的层级规划（Hierarchical Planning）：PLAP本质上是一个两层结构（规划层-执行层）。对于更复杂的任务，可能需要一个更深、更动态的层级规划体系，例如“战略-战术-行动”的多层决策。LLM可以负责更高层的战略制定，并将任务分解给下层的“专家”模型或模块来处理。 对手建模与博弈（Opponent Modeling）：如论文所承认，PLAP目前主要根据环境的显式状态做反应，而没有对对手的意图和策略进行建模。在对抗环境中，预测并反制对手的策略至关重要。未来可以研究如何让LLM根据历史观察来推断对手的“心智模型”，从而做出更具博弈智慧的决策。 多智能体协作（Multi-Agent Collaboration）：如何将PLAP框架扩展到多个智能体协同工作的场景？这涉及到通信、任务分配、信任建立等一系列复杂的社会性AI问题。 可能催生的新技术和投资机会： AI原生游戏：未来的游戏开发可能不再是编写死板的NPC脚本，而是为游戏世界设计一套“物理规则”和“技能库”，让LLM驱动的AI角色在其中自由演化，创造出千变万化、永不重复的游戏体验。 企业级流程自动化“大脑”：PLAP范式可以用于构建下一代RPA（机器人流程自动化）系统。企业只需用自然语言定义业务流程和可用工具（即技能库），LLM就能自动编排和执行复杂的跨系统工作流。 通用机器人操作系统：对于机器人领域，可以构建一个通用的、包含“抓取”、“移动”、“放置”等基础技能的库，用户只需用语言下达指令，机器人就能通过PLAP框架自主完成任务，极大地推动机器人的普及。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些值得商榷和深入研究的地方。\n对技能库设计的依赖过高： \u0026gt; The skill functions used in PLAP are manually constructed, which limits generalization across different domains. PLAP的成功在很大程度上依赖于一个设计良好的参数化技能库。如果技能库设计得不合理（过粗或过细），智能体的表现将大打折扣。论文没有深入探讨如何设计一个好的技能库，以及系统性能对技能库质量的敏感度如何。这使得框架的成功有“巧匠”的成分，而非完全自动化的智慧。\n环境观察的文本化瓶颈： 当前框架需要将环境状态完整地转化为文本输入给LLM。对于MicroRTS这样8x8的简单环境尚可，但对于星际争霸II或真实世界这种高维、复杂的环境，如何有效地将海量信息压缩成一段LLM能够理解且不失真的文本描述，是一个巨大的挑战。这可能成为PLAP框架扩展到更复杂场景时的主要瓶颈。\n实时性的挑战： 尽管通过k-step规划降低了调用频率，但LLM的推理延迟仍然是不可忽视的。在需要毫秒级反应的场景（如格斗游戏或高频交易）中，当前的PLAP框架可能仍然无法满足要求。\n缺乏对失败的归因与学习： 当一个计划执行失败时，当前的PLAP框架似乎只是在下一个规划周期重新生成一个新计划，而没有一个明确的机制去反思（Reflection）失败的原因，并从中学习。一个更先进的智能体应该具备从错误中吸取教训、动态调整其规划策略的能力。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 对于希望从中汲取创新思路的您来说，这篇论文的价值远不止于其具体实现。\n重点学习的核心思想： 分层与解耦的智慧：最核心的启发是“让合适的人干合适的事”。不要强求一个模型解决所有问题。要学会将一个复杂任务分解成不同的层次，让LLM去做它最擅长的高层语义理解和规划，而将精确、重复、需要确定性的底层执行交给传统代码。这种LLM与代码协同的“混合智能”范式，是当前构建可靠AI应用的最佳实践。 “中间语言”的重要性：参数化技能本质上是一种为特定任务领域设计的“领域特定语言（DSL）”。它充当了人类意图、LLM理解和机器执行之间的桥梁。在您自己的项目中，思考一下是否可以为您的任务设计一套类似的“中间语言”，这将极大提升系统的模块化和可控性。 Prompt即程序（Prompt as Code）：论文中的Prompt设计（图3）极其精巧，它像一个“配置文件”或一段“脚本”，精确地引导LLM的行为。这告诉我们，在LLM时代，高质量的提示工程本身就是一种编程。学习如何构建结构化、信息丰富的Prompt，是释放LLM能力的关键。 需要补充的背景知识： 强化学习（Reinforcement Learning）：理解MDP（马尔可夫决策过程）、策略（Policy）、动作空间（Action Space）等基本概念，将有助于您理解PLAP框架解决问题的理论背景。特别是Hierarchical RL和Options Framework与本文思想一脉相承。 经典AI规划（Classical AI Planning）：了解如STRIPS、HTN（分层任务网络）等经典规划算法，能让您更好地理解PLAP中“规划”与“执行”分离思想的历史渊源。 其他LLM Agent架构：可以去了解一下ReAct（Reason + Act）、Tree of Thoughts、SayCan等其他有影响力的Agent框架，将PLAP置于更广阔的背景中进行比较，能让您对其创新点有更深刻的认识。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-09-20T00:00:00Z","permalink":"/empowering-llms-with-parameterized-skills-for-adversarial-long-horizon-planning/","title":"Empowering LLMs with Parameterized Skills for Adversarial Long-Horizon Planning"},{"content":"联合训练planner和executor，跟PilotRL：Training Language Model Agents via Global Planning-Guided Progressive Reinforcement Learning一样。planner使用DPO，executor使用SFT，这个联合训练的思路可以借鉴。\n首先，我们来对这篇论文进行一个全面的、深入的概述。\n这篇论文精准地切入了当前大模型（LLM）驱动的多智能体系统（Multi-Agent Systems）领域一个核心且普遍存在的痛点：能力差距（Capability Gap）。在复杂的任务中，我们通常不会用一个单一的通用大模型来解决所有问题，而是构建一个由多个功能专用型智能体（functionality-specialized agents）组成的协作系统。论文中以一个最经典的组合为例：一个负责将复杂任务分解为一系列子目标的规划智能体（Planning Agent），和一个负责调用外部工具（如计算器、搜索引擎）来执行这些子目标的接地智能体（Grounding Agent）。\n过去的方法，正如论文图1(a)所描绘的，通常对这两个智能体进行独立训练（Independent Training）。规划智能体在一个数据集上学习如何分解任务，接地智能体在另一个数据集上学习如何执行指令。这种“各自为政”的训练方式导致了严重的协作障碍。规划智能体可能会生成一些逻辑上完美但接地智能体难以理解或无法执行的“空中楼阁”式指令；反之，一个强大的接地智能体也可能因为规划智能体给出的模糊或低效指令而“有力无处使”。这种由于能力不匹配、指令风格不统一而导致的协作鸿沟，就是论文所要解决的“能力差距”。\n为了弥合这一差距，论文提出了一个极具创新性的框架——MOAT（Multi-Agent Joint Alignment Tuning，多智能体联合对齐调优）。MOAT的核心思想摒弃了独立训练的范式，采用一种迭代式、交替进行的联合优化策略，让规划智能体和接地智能体在相互“磨合”中共同进化，形成高效的协作默契。\n这个过程分为两个交替进行的阶段，构成了 MOAT 的核心循环，正如论文图2所示：\n规划智能体对齐（Planning Agent Alignment）：这个阶段的目标是让规划智能体学会生成更“接地气”、更符合其搭档（接地智能体）偏好的子目标。它的做法非常巧妙：首先，让规划智能体针对一个任务，生成多个（例如 K=15 个）不同的子目标序列方案。然后，让当前的接地智能体去“评估”这些方案。评估的标准不是看方案本身逻辑多完美，而是计算接地智能体在理解并生成正确工具调用时所表现出的困惑度（Perplexity, PPL）。 \u0026gt; Perplexity can directly reflect how s is useful to the end-to-end task performance… a lower perplexity indicates that the subgoal sequence is more helpful to the grounding agent.\n这里的困惑度（Perplexity）是一个衡量语言模型对其生成内容的“自信程度”的指标。PPL越低，代表模型对于生成这个序列越有把握，也间接说明输入的子目标序列是它更容易理解和执行的。这个PPL值随后被用作奖励信号，通过直接偏好优化（Direct Preference Optimization, DPO）算法来微调规划智能体。DPO是一种先进的对齐技术，它能让模型直接从“哪个方案更好”（即哪个PPL更低）的偏好数据中学习，从而调整自己，使其未来更倾向于生成接地智能体“喜欢”的（即低PPL的）子目标。\n接地智能体改进（Grounding Agent Improving）：这个阶段的目标是提升接地智能体理解和执行规划智能体所生成指令的能力。它巧妙地复用了第一阶段中规划智能体生成的各种子目标序列作为训练数据。这样做的好处是，训练数据不再是理想化的、手工标注的指令，而是更贴近实战中规划智能体真实输出的风格和分布。但这里有一个关键问题：模型自己生成的数据可能包含错误。为了解决这个问题，MOAT引入了一个“批评家”模型（Critic Model）——一个能力更强的第三方大模型（如论文中使用的DS-Qwen-32B或GPT-4）。这个批评家模型负责检查接地智能体生成的工具调用序列是否正确，如果不正确，则进行修正，生成高质量的“（子目标，正确动作）”训练对。 \u0026gt; This filtering and correction process ensures that only reliable supervision signals are used during grounding agent training.\n通过这两个阶段的交替迭代，系统形成了一个良性循环：规划智能体越来越懂得如何“指挥”，接地智能体越来越善于“执行”，两者之间的能力差距被逐步抹平，整个系统的综合性能得到显著提升。论文的实验部分也充分证明了这一点，例如，在Mistral-7B模型上，MOAT相比之前的SOTA方法Agent Lumos，在未见过的任务上平均性能提升了4.4%（53.6 vs 49.2），这个提升在模型能力竞争激烈的今天是非常显著的。\n总而言之，这篇论文的贡献在于，它从根本上指出了多智能体协作中的一个关键瓶颈，并提出了一套行之有效的、基于联合对齐的解决方案 MOAT。它通过巧妙设计的反馈机制（PPL作为奖励）和数据提纯机制（批评家模型），成功地让两个独立的智能体实现了能力的协同进化，为构建更强大、更可靠的多智能体系统提供了全新的思路和坚实的实践基础。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决多智能体系统中，由于各智能体被独立训练而导致的“能力差距”（Capability Gap）问题，从而提升整个系统的协作效率和任务成功率。\n解决的实际问题：在实际应用中，例如构建一个能自动预定旅行计划的AI助手，可能需要一个“规划智能体”来分解任务（“查询航班”、“预定酒店”、“租赁汽车”），以及一个“接地智能体”来调用真实的API（航空公司API、酒店预订网站API）。如果规划智能体生成的子目标是“找一个便宜的住处”，而接地智能体只懂得调用book_hotel(name, date)这样的具体API，那么任务就会失败。MOAT框架解决的就是这种“鸡同鸭讲”的协作困境，让智能体之间能够高效协同工作。\n行业意义：这个问题对于AI行业，特别是Agentic AI（智能体AI）的发展至关重要。随着模型能力增强，单一模型解决所有问题的“巨石应用”模式正逐渐被灵活、高效的“微服务”式多智能体协作模式所取代。多智能体系统是实现复杂业务流程自动化的关键。如果智能体之间无法有效协作，整个系统的可靠性将无从谈起。MOAT提出的联合对齐思想，为构建鲁棒、高效的商用多智能体系统提供了一套可行的工程范式。它推动了行业从“训练单个强大的模型”向“培育高效协作的智能体团队”的理念转变，对于加速AI在企业自动化、科学研究、个人助理等领域的落地具有深远影响。\n2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路：核心新思路是从“独立优化”转向“联合对齐”。之前的方法（如论文中提到的Agent Lumos）虽然也训练专用的规划和接地智能体，但它们是在各自固定的数据集上独立进行微调的，缺乏一个动态的、相互适应的反馈机制。MOAT的思路是，智能体的能力不是一成不变的，它们应该在协作中共同成长。\n新方法/模型：论文提出的MOAT框架本身就是一套新方法。其核心创新点体现在两个具体机制上：\n基于困惑度（PPL）的偏好反馈：这是一个非常巧妙的设计。它没有引入复杂的奖励模型，而是直接利用接地智能体自身的PPL作为衡量规划质量的内在指标。这相当于让执行者（接地智能体）来“投票”决定哪个指挥方案（子目标序列）更好，然后让规划者根据投票结果来学习，实现了轻量级且高效的对齐。 引入批评家模型进行数据提纯：在让接地智能体学习规划智能体生成的指令时，直接用可能错误的输出来训练会导致“近墨者黑”。MOAT引入一个更强大的外部“老师”（批评家模型）来批改作业，确保接地智能体学到的是正确的行为。这解决了自生成数据训练中常见的噪声累积和性能退化问题。 特点与优势：\n协同进化：MOAT最大的特点是其双向、迭代的优化过程。规划智能体为了“迁就”接地智能体而改变，接地智能体也为了更好地理解规划智能体而学习。这种动态的相互适应过程，使得最终达成的协作状态远优于静态的、单向的训练。 泛化能力强：由于智能体之间形成了真正的“默契”，它们在面对训练时未见过的“held-out”任务时表现更出色。实验数据显示，MOAT在多个模型和任务上都实现了比基线更高的泛化性能，例如在Held-out任务上，基于Qwen2.5-14B的MOAT平均分达到60.4，高于Agent Lumos的59.1。 框架通用性：MOAT框架不依赖于特定的模型架构，论文成功地将其应用于Llama、Mistral、Qwen等多种模型家族，证明了其方法的普适性。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来验证MOAT的有效性。\n实验设计： 任务选择：实验涵盖了三种不同类型的复杂任务：数学推理（GSM8K, SVAMP）、问答（StrategyQA, HotpotQA）和网页操作（Mind2Web, WebShop），确保了评估的全面性。 数据集划分：采用了“held-in”（训练中见过）和“held-out”（训练中未见过）的任务划分。这是一种评估模型泛化能力的黄金标准。在held-in任务上的好表现说明模型学得好，在held-out任务上的好表现说明模型真正理解了协作的本质，而不是死记硬背。 基线对比：选取了包括AgentTuning、Agent-FLAN、Agent Lumos等SOTA的Agent微调方法，以及GPT-3.5和GPT-4这样的强大闭源模型作为对比，使得实验结果更具说服力。 消融研究（Ablation Study）：为了验证MOAT框架中每个组件的必要性，论文进行了消融实验，分别移除了“规划智能体对齐”（w/o stage 1）、“接地智能体改进”（w/o stage 2）和“批评家模型”（w/o critic），观察性能变化。 实验数据与结果： 总体性能（见下表，简化自Table 2）：MOAT在所有基座模型和任务上均取得了SOTA或有竞争力的结果。 | Base Model | Method | Held-in Avg. | Held-out Avg. | | :— | :— | :—: | :—: | | Llama-2-7B | Agent Lumos | 41.1 | 49.6 | | Llama-2-7B | MOAT | 44.1 | 52.3 | | Mistral-7B-v0.2 | Agent Lumos | 43.2 | 49.2 | | Mistral-7B-v0.2 | MOAT | 46.3 | 53.6 | | Qwen2.5-14B | Agent Lumos | 54.3 | 59.1 | | Qwen2.5-14B | MOAT | 56.8 | 60.4 |\n消融研究结果（Table 3）：结果显示，移除任何一个组件都会导致性能下降。其中，移除规划智能体对齐（w/o stage 1）导致的性能下降最为显著（平均分从46.80降至44.17）。这强有力地证明了让规划智能体去主动适应接地智能体是MOAT框架中最关键的一步。移除批评家模型也导致了显著的性能下降，说明了数据质量的重要性。\n能力差距的量化验证（Table 4）：论文直接测量了接地智能体的PPL。结果显示，经过MOAT训练后，PPL显著降低。例如，在Mistral-7B上，处理数学任务的PPL从3.53降至2.56，降幅达27.48%。这为“MOAT弥合了能力差距”这一核心论点提供了直接的量化证据。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 该研究方向前景广阔，同时也面临诸多挑战，催生了新的技术和投资机会。\n值得探索的问题与挑战： 多智能体协作的扩展性：MOAT主要验证了两个智能体（planner-grounder）的协作。当系统中存在更多角色（如反思智能体、工具检索智能体、用户模拟智能体）时，如何设计高效的联合对齐策略？两两对齐的计算成本会呈指数级增长，需要更高效的群体对齐（Group Alignment）机制。 通信协议的演化：目前智能体间的通信（子目标序列）是单向的、结构化的。未来可以探索更复杂的、双向的通信模式，例如允许接地智能体在指令不明确时向规划智能体“提问”或“协商”，形成更动态的交互。 对齐过程的效率：MOAT的迭代过程，尤其是需要调用强大的批评家模型，计算成本较高。未来的研究可以探索更轻量级的对齐信号，或者让智能体学会自我批评与修正，减少对外部强大模型的依赖。 开放与动态环境下的对齐：当前实验环境相对静态。在工具集或环境规则会动态变化的真实世界中，智能体团队如何实现快速的在线自适应与重新对齐是一个巨大的挑战。 新技术与投资机会： 多智能体开发与编排平台：未来会出现类似“智能体操作系统”的平台，它不仅提供智能体开发工具，更核心的是内置了MOAT这类联合对齐和自动优化的能力，帮助开发者轻松构建和维护高效的智能体团队。 Agent-as-a-Service：专门提供预训练、高度对齐的智能体对（或团队）的服务。例如，一个专门用于软件开发的“规划-编码-测试”智能体组合，企业可以直接通过API调用。 针对对齐优化的AI芯片或硬件：DPO和类似的偏好学习算法计算开销大，专门为这类训练范式优化的硬件或计算架构可能成为新的投资热点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但仍有一些值得商榷和深入探讨之处。\n对强大“批评家”模型的依赖：整个框架的有效性在很大程度上依赖于一个能力远超待训练智能体的批评家模型。这带来了一个问题：如果用户没有权限或预算访问GPT-4这类顶级模型，效果会如何？虽然论文在Table 5中探讨了使用较弱批评家的影响，并表明效果依然不错，但这种依赖性本身就是一个潜在的瓶颈。它有点像“用一个更聪明的老师来教两个学生，学生自然会进步”，但“更聪明的老师”从何而来？ 交互模式的简单性：论文中的planner-grounder交互是线性的“指令-执行”模式。真实的复杂任务往往需要更复杂的、非线性的协作，可能包含反馈、修正、澄清等多个来回。MOAT框架目前未能覆盖这种复杂的交互模式。 收敛性的实际探讨：论文从理论上证明了收敛性，但从实验图4（Analysis of Iteration Count）来看，性能在第2-3次迭代后就迅速饱和，增长变得非常微弱。这引发了思考：这种快速饱和是达到了真正的性能上限，还是陷入了一个局部最优？联合优化的探索空间是否足够大？ 任务的局限性：虽然覆盖了三类任务，但这些任务仍然是目标明确、有固定答案的。对于更开放、需要创造力或长期战略规划的任务（如“撰写一份市场分析报告”），MOAT框架的有效性还有待验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文充满了可以借鉴的创新思想，对您极具启发价值。\n重点学习的核心思想： “协同对齐”大于“个体最优”：这是最核心的启发。在设计任何由多个模块组成的系统时（无论是多智能体、RAG系统，还是复杂的软件架构），不要只孤立地优化每个模块的性能。要思考如何建立模块间的反馈机制，让它们相互适应，追求系统层面的整体最优。 利用“下游”信号指导“上游”：MOAT用接地智能体（下游）的PPL来指导规划智能体（上游）的训练，这是一个极其优雅的思路。您可以思考在您的任务中，是否也能找到类似的“下游信号”。例如，在RAG（检索增强生成）中，能否用生成模型（下游）对检索文档的“利用率”或“生成答案的流畅度”来作为信号，反过来优化检索模型（上游）？ 用强者智慧提纯自产数据：当需要用模型生成的数据来继续训练模型时，数据质量是关键。MOAT的“批评家”模型提供了一个绝佳的范例。这是一种“弱者向强者学习”的蒸馏思想，但应用在了数据层面。在您的工作中，如果需要数据增强或合成数据，可以考虑引入一个更高阶的模型、规则系统甚至人类专家来对数据进行“清洗”和“提纯”。 需要补充的背景知识： 多智能体系统（Multi-Agent Systems, MAS）：了解智能体的基本定义、角色（如协作、竞争）、以及常见的系统架构。 大模型的工具使用（Tool Use）：熟悉LLM如何通过ReAct（Reason+Act）等框架调用外部API和工具来与环境交互。 直接偏好优化（Direct Preference Optimization, DPO）：这是当前LLM对齐（Alignment）领域最前沿和核心的技术之一。深刻理解DPO的原理对于理解MOAT的规划智能体对齐阶段至关重要。 语言模型评估指标：特别是困惑度（Perplexity），理解它的计算方式和其在评估模型“自信度”方面的意义。 希望以上详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-09-13T00:00:00Z","permalink":"/bridging-the-capability-gapharmonizing-multi-agent-systems-via-joint-alignment-tuning/","title":"Bridging the Capability Gap：Harmonizing Multi-Agent Systems via Joint Alignment Tuning"},{"content":"长时序任务下的信用分配问题，部分实验结果直接使用的Group-in-Group Policy Optimization for LLM Agent Training，这说明如果到时候我写相关论文，也可以直接用别人论文的结果，避免了复现成本。不过baseline还是要自己跑。\n本文的方法是利用熵来作为一种step级别的奖励信号，放大正确且自信，严惩自信但错误。类似ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM和EDGE-GRPO：Entropy-Driven GRPO with Guided Error Correction for Advantage Diversity。并且如果某个动作导致的状态熵更低，额外给予一个奖励 \u0026gt;这鼓励智能体不仅要完成当前任务，还要学会有策略地探索，主动走向那些“豁然开朗”的决策路径，避免陷入混乱和不确定的状态。\n我觉得这个可以挪用到我的世界模型中，本来就设计了一个奖励函数来选择最佳动作，完全可以把这时候给的奖励用到训练中，后续试一下看看结果怎么样。\nimage.png 论文深度解读：驾驭不确定性，重塑智能体学习范式 这篇论文的核心目标是解决当前大型语言模型代理（LLM Agents） 在执行长时序、复杂任务（Long-Horizon Tasks） 时面临的一个根本性难题：稀疏奖励下的信用分配（Credit Assignment）问题。想象一下，你让一个智能体在网上预订一个包含多个中转站的复杂航班，只有在最后成功预订时，你才会告诉它“做得好”，奖励为1，否则为0。在这个过程中，智能体执行了几十个步骤，比如点击、搜索、填写表单等。如果最终失败了，它完全不知道是哪一步出了错；即使成功了，它也不知道哪些步骤是关键的、最高效的。这种最终结果的单一反馈，就是所谓的“稀疏奖励”。\n传统的强化学习（RL）方法在这种场景下举步维艰，因为它们难以将最终的成败功劳或责任精确地分配到每一个中间步骤上。论文敏锐地指出，过去的研究主要通过两种方式绕过这个问题：一是设计复杂的稠密奖励信号（Dense Rewards），比如引入人类编写的逐步反馈模型（Process Reward Models, PRMs），但这成本高昂且难以泛化；二是通过逆向强化学习（Inverse RL） 等方法，但这在LLM Agent巨大的状态和动作空间中难以规模化。\n这篇论文的真正创新之处在于，它没有向外部寻求更复杂的奖励，而是将目光投向了模型内部——利用模型自身在决策过程中的“不确定性”作为一种前所未有的、丰富的内在学习信号。\n论文的理论基石是他们提出的命题1（Proposition 1）。这个命题从数学上揭示了一个被忽视但至关重要的现象：在标准的策略梯度（Policy Gradient）算法中，一个动作的策略梯度的大小与其策略熵（Policy Entropy） 直接相关。通俗地说，当模型对某个动作非常“确定”（低熵）时，它产生的梯度信号反而很微弱；而当它非常“不确定”（高熵）时，梯度信号却很强。\nFor a policy πθ parameterized by a softmax over logits zθ(s), the expected squared L2-norm of the score function … is a direct function of the policy’s Rényi-2 entropy [24], H2(π): \\[ \\mathbb{E}_{a \\sim \\pi_{\\theta}(\\cdot|s)} [||\\nabla_{z_{\\theta}(s)} \\log \\pi_{\\theta}(a|s)||^2] = 1 - \\exp(-H_2(\\pi)) \\]\n这个特性在智能体学习中会造成两个严重问题： 1. 学习效率低下：对于那些智能体已经很有把握做出的正确决策，由于梯度小，其得到的正向强化非常微弱，学习速度被大大限制。 2. 训练不稳定：对于那些探索性的、不确定的动作，由于梯度大，一旦最终结果不好，就会产生巨大的负向更新，可能导致策略剧烈波动，破坏了有益的探索。\n为了解决这个“熵-梯度耦合”的根本性矛盾，论文提出了一个全新的框架——熵调制策略梯度（Entropy-Modulated Policy Gradients, EMPG）。这个框架 brilliantly 地设计了两个核心组件，共同对学习信号进行“重校准”：\n自校准梯度缩放（Self-Calibrating Gradient Scaling）：这是EMPG的核心。它不再让所有步骤“平分”最终的功劳，而是根据每一步的决策熵来动态调整（modulate）奖励的权重。具体来说，它会放大那些“自信且正确”的步骤所获得的奖励，同时严厉惩罚那些“自信但错误”的步骤（即“幻觉自信”）。对于那些模型本就不确定的探索性步骤，它则会衰减更新的幅度，从而保护探索过程不被噪声梯度干扰，稳定训练。这个缩放因子是“自校准”的，它通过在每个训练批次（mini-batch）内进行归一化，动态适应模型能力的变化，非常优雅。\n未来清晰度奖励（Future Clarity Bonus）：这是一个巧妙的内在激励（Intrinsic Motivation）机制。除了调整已有的奖励信号，EMPG还额外给予智能体一个“奖励”——如果某个动作能让智能体进入一个对“下一步该怎么走”更为确定的状态（即下一个状态的策略熵更低），就给予一个正向激励。这鼓励智能体不仅要完成当前任务，还要学会有策略地探索，主动走向那些“豁然开朗”的决策路径，避免陷入混乱和不确定的状态。\n论文通过在三个极具挑战性的长时序任务——WebShop（网页购物）、ALFWorld（文本交互世界）和Deep Search（深度信息检索）——上进行的大量实验，充分验证了EMPG的有效性。实验结果令人信服，无论是在小模型（Qwen2.5-1.5B）还是大模型（Qwen2.5-32B）上，EMPG都能稳定地超越现有的SOTA基线方法，如GRPO和DAPO。例如，在最具挑战性的Deep Search任务上，EMPG在强大的DAPO基线上，将模型的整体平均分从62.0分提升至65.3分（+3.3），特别是在域外（Out-of-Domain, OOD） 的泛化测试上，性能提升高达+3.9分。这证明了EMPG不仅提升了性能，更重要的是增强了模型的泛化能力和鲁棒性。\n表2：Deep Search 任务上的主要结果，EMPG在域内（ID）和域外（OOD）任务上均表现出强大的性能，尤其是在OOD任务上泛化能力提升显著。 此外，论文还通过消融实验（Ablation Study）清晰地证明了“梯度缩放”和“未来清晰度奖励”两个组件各自的贡献，并发现前者主要提升模型的泛化和鲁棒性，而后者则有助于在域内任务上更好地利用已知模式，二者结合相得益彰。\n总而言之，这篇论文不仅仅是提出了一种新的算法，更是为解决LLM Agent训练中的核心瓶颈提供了一种全新的、源自模型内部的、可扩展的解决思路。它将“不确定性”从一个需要被动管理的问题，转变为一种可以被主动利用的宝贵资源，为训练更强大、更鲁棒、更高效的自主智能体铺平了道路。\n接下来，我将按照您提出的六个方面，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决长时序决策任务中，基于稀疏奖励信号的强化学习所面临的信用分配难题。 实际问题：在许多现实世界的应用中，例如复杂的网络操作、代码调试、科学研究、多步骤规划等，LLM Agent需要执行一长串动作才能最终获得一个成败反馈。在这种情况下，传统的RL方法由于无法确定中间哪一步是关键的，导致学习效率低下、训练不稳定，甚至无法收敛。这个问题是阻碍LLM Agent从“玩具”走向“工具”，在真实复杂环境中可靠工作的核心技术瓶颈。 行业意义：解决这个问题意义重大。 降低训练成本：它摆脱了对昂贵的人工逐一标注（如PRMs）的依赖，使得利用海量的、只有最终结果标签的数据进行训练成为可能。 提升智能体能力上限：一个能有效进行信用分配的智能体，可以更快地学习到完成复杂任务的最优策略，从而能够处理更长、更复杂的任务链，这是实现通用人工智能（AGI） 的关键一步。 增强鲁棒性和可靠性：通过学会理解和利用不确定性，智能体在面对新环境、新问题时会更加稳健，而不是在不确定的情况下做出“幻觉自信”的错误决策，这对于在金融、医疗、自动驾驶等关键领域的应用至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文提出的核心方法是熵调制策略梯度（EMPG），其新颖之处在于以内在不确定性（熵）来重校准（re-calibrate）外部奖励信号。\n与之前的方法相比，EMPG具有以下特点和优势：\n全新的问题视角： 之前方法：将信用分配问题视为一个“信号缺失”问题，试图从外部“创造”或“稠密化”奖励信号。 EMPG思路：将其视为一个“信号失真”问题。它认为信号（梯度）一直存在，但其大小被模型的置信度（熵）错误地调制了。EMPG的目的是“校准”这个失真的信号，使其能正确反映每一步的重要性。 两大核心机制： 自校准梯度缩放 (Self-Calibrating Gradient Scaling)：这是对现有奖励信号的乘法式调制（multiplicative modulation）。 \u0026gt; g(H) in A_mod(i, t) = A(i) * g(H(i)_t) + ... 其优势在于： 动态适应：通过在批次内对熵进行归一化，g(H)是动态变化的。在训练初期，模型普遍不确定，它会温和地调整；在训练后期，模型变得自信，它则会进行更大幅度的调整。 区分对待：它能放大自信且正确的决策的影响力，加速学习；同时也能衰减不确定决策带来的噪声，稳定学习。这是传统方法（如PPO、GRPO）无法做到的，它们对成功轨迹中的所有步骤一视同仁。 未来清晰度奖励 (Future Clarity Bonus)：这是对奖励信号的加法式增强（additive enhancement）。 \u0026gt; ζ * f(H(i)_{t+1}) in A_mod(i, t) = ... + ζ * f(H(i)_{t+1}) 其优势在于： 提供探索方向：它为智能体提供了一个明确的内在动机——寻找“确定性”。这使得探索不再是盲目的，而是有目的地寻找通往“清晰”解题路径的策略，这在复杂问题空间中能极大提升搜索效率。 信息论基础：这个奖励可以被看作是最大化关于未来最优路径的信息增益（Information Gain），具有坚实的理论基础。 通用性和兼容性：EMPG被设计为一个“即插即用”的模块，可以直接应用于任何基于策略梯度的RL算法之上，如论文中展示的，它可以同时增强GRPO和DAPO。这使得它的应用范围非常广泛。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文通过一系列设计严谨的实验来验证EMPG的有效性、可扩展性和鲁棒性。\n实验设计： 基准任务 (Benchmarks)：选择了三个公认的、具有挑战性的长时序智能体任务： WebShop: 模拟真实的购物网站，需要理解指令并进行多步网页交互。 ALFWorld: 结合了文本理解与常识推理的交互式环境。 Deep Search: 一个需要多步检索、阅读、综合信息才能回答问题的复杂问答任务，并被分为域内（In-Domain, ID）和域外（Out-of-Domain, OOD）两部分以测试泛化能力。 基线模型 (Baselines)：选择了两个强大的、开源的SOTA策略梯度方法GRPO和DAPO作为对比基线。 模型规模：实验覆盖了不同规模的模型，包括Qwen2.5-1.5B-Instruct, Qwen2.5-7B-Instruct, 和 Qwen2.5-32B-Instruct，以验证方法的可扩展性。 评估指标：主要使用任务成功率（Success Rate）或得分（Score）作为评估指标。 关键实验数据和结果： 普遍有效性：EMPG在所有任务、所有模型规模、所有基线上都取得了一致的性能提升。 在ALFWorld任务上，使用Qwen2.5-7B模型，EMPG将GRPO的成功率从74.8%提升至78.5% (+3.7)，将DAPO的成功率从90.0%提升至91.6% (+1.6)。（引用自Table 1） 在WebShop任务上，同样使用7B模型，EMPG将DAPO的成功率从79.6%提升至82.7% (+3.1)。（引用自Table 1） 强大的泛化能力：在Deep Search任务的OOD测试中，EMPG表现尤为突出。 相较于DAPO基线，EMPG在OOD任务上的平均分提升了+3.9分（从59.8到63.7），高于其在ID任务上的提升（+3.1分）。这有力地证明了EMPG通过学习如何处理不确定性，获得了更强的泛化能力。（引用自Table 2） 提升训练稳定性： 论文中的图2展示了训练过程中的KL散度损失（KL Loss）。DAPO基线在训练后期出现了剧烈的损失尖峰，表明策略崩溃（policy collapse）的风险。而加入了EMPG的模型则全程保持了平稳的低KL损失，证明了其训练过程更稳定、更鲁棒。 图2：训练过程中的KL损失动态。蓝色线（EMPG）显著比橙色线（基线）更稳定。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 更精细的不确定性度量：论文主要使用了策略熵作为不确定性的代理。未来可以探索更复杂的度量方式，例如： 模型集成（Ensemble）的预测方差。 使用MC Dropout等技术来近似贝叶斯不确定性。 区分偶然不确定性（Aleatoric Uncertainty）（数据固有噪声）和认知不确定性（Epistemic Uncertainty）（模型知识的缺乏），并设计不同的调制策略。 与其他学习范式结合：如何将EMPG的思想与离线强化学习（Offline RL）、基于模型的强化学习（Model-based RL） 或层级强化学习（Hierarchical RL） 结合，可能会产生更强大的智能体。 多智能体协作（Multi-Agent Collaboration）：如论文所言，在多智能体环境中，一个智能体的不确定性可能会影响整个团队的决策。如何利用和管理群体的不确定性是一个极具挑战性的前沿方向。 理论分析：虽然论文给出了理论动机，但EMPG对收敛性的影响、最优超参数的理论界限等还需要更严格的数学证明。 新的技术和投资机会： Agent训练与评估平台：随着像EMPG这样高效训练方法的出现，对能够支持大规模、长时序、多场景的智能体训练和评估平台的需求将激增。 不确定性即服务（Uncertainty-as-a-Service）：开发能够精确量化和解释大模型不确定性的工具或API，可以成为AI安全、对齐和可解释性领域的重要产品。 垂直领域的高鲁棒性智能体：在金融交易、自动驾驶、医疗诊断等对可靠性要求极高的领域，基于不确定性优化的智能体将具有巨大的商业价值。 AI原生开发工具链：催生新一代的AI开发工具，帮助开发者更方便地利用模型内在信号来调试、优化和部署智能体应用。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 计算开销分析缺失：EMPG需要在每一步计算token级别的熵，并进行批次内的归一化，这无疑增加了额外的计算开销。论文并未量化这部分开销相对于基线方法增加了多少，也未讨论在推理时是否可以关闭这些计算，这对于评估其在实际部署中的可行性很重要。 超参数敏感性讨论不足：该方法引入了新的超参数，如梯度缩放的k、k'和未来清晰度奖励的权重ζ。尽管论文称其为“自校准”，但这些超参数的选择可能仍然对最终性能有较大影响。论文缺乏对这些超参数的敏感性分析。 熵作为不确定性代理的局限性：平均token熵是一个方便但相对粗糙的度量。模型可能以极低的熵（非常自信地）生成完全错误或“幻觉”的内容。虽然EMPG通过惩罚“自信的错误”部分缓解了此问题，但它无法根本解决熵度量与语义真实性之间的鸿沟。 需要进一步验证和存疑之处： 在非文本模态任务上的泛化性：论文的实验全部集中在基于文本生成的交互任务上。EMPG在机器人控制、视觉导航等涉及连续动作空间或多模态输入的任务上的表现如何，尚待验证。 与Process Reward Models (PRMs)的对比：论文将PRMs作为一种昂贵的替代方案，但并未进行直接的实验对比。如果在一个有高质量PRM的场景下，EMPG是否还能带来显著的额外收益？二者是否可以结合使用？这是一个值得探究的问题。 对“探索-利用”权衡的长期影响：“未来清晰度奖励”鼓励智能体走向更确定的路径，这在长期来看是否存在过早收敛、抑制了对更优但暂时不确定的新路径的探索的风险？这需要更长周期的实验来观察。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习和启发： 核心思想：向内看，利用模型的内在状态。这是最大的启发。当外部监督信号稀疏或昂贵时，模型的内在状态（如不确定性、注意力分布、神经元激活等）是一个尚未被充分挖掘的宝藏。你可以思考，在你的任务中，模型还有哪些内在信号可以被用作自监督或学习信号。 “调制”而非“替代”的设计哲学。EMPG没有抛弃原有的奖励，而是通过乘法和加法对其进行“调制”和“增强”。这种“微调”现有学习信号的思路，比完全设计一套新信号，往往更鲁棒、更容易实现。 动态与自适应机制。EMPG的“自校准”特性使其能够适应模型能力的变化，这是一个优秀算法设计的标志。在设计你的方法时，思考如何让它能根据训练进程或输入数据的动态变化而自适应调整。 为探索赋予方向。“未来清晰度奖励”将盲目的探索变成了有目标的搜索。这启发我们，可以通过设计巧妙的内在激励，引导模型更高效地探索复杂的解空间。 需要补充了解的背景知识： 强化学习基础： 策略梯度方法（Policy Gradient Methods）：理解其基本原理，特别是REINFORCE算法。 优势函数（Advantage Function）：理解A2C、A3C中优势函数的概念。 近端策略优化（Proximal Policy Optimization, PPO）：这是现代LLM RL的基石算法之一，理解其Clipped Objective和KL Penalty机制很重要。 LLM Agent技术： ReAct框架 (Reasoning and Acting)：论文中智能体遵循的“思考-行动”循环范式，这是当前主流的Agent设计模式。 工具使用（Tool Use）：了解LLM如何调用外部工具（如搜索引擎、代码解释器）来完成任务。 信息论基础： 香农熵（Shannon Entropy）：理解其作为信息量或不确定性度量的基本概念。论文中提到的Rényi熵是其推广，了解即可。 希望这份详尽的解读能够帮助您深入理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-09-13T00:00:00Z","permalink":"/harnessing-uncertaintyentropy-modulated-policy-gradients-for-long-horizon-llm-agents/","title":"Harnessing Uncertainty：Entropy-Modulated Policy Gradients for Long-Horizon LLM Agents"},{"content":"规划与执行分离，本文的观点是长程规划任务中一个step的错误有可能导致后续agent看到自己犯的错误，反而使得后面也变成错误。不过进一步发现了具有思考能力的模型可以避免这一点。\n这篇论文的核心论点极具颠覆性：我们可能一直以来都用错了衡量大模型进步的“尺子”。当整个行业都在为MMLU等基准测试中零点几个百分点的提升而欢呼或焦虑，并开始讨论“模型规模回报递减”的时代是否到来时，这篇论文提供了一个全新的视角。它指出，大模型真正的、且更具经济价值的潜力，并非体现在单步问答的准确率上，而是体现在其可靠地执行长序列、多步骤复杂任务的能力上，即“长时域执行 (Long-Horizon Execution)”能力。\n论文首先从一个简单但违反直觉的数学事实出发：单步执行准确率上的微小、看似“回报递減”的提升，可以复合成模型能成功完成的任务长度上的指数级增长。这为继续投入资源进行模型扩展（Scaling）提供了强有力的理论依据。\n正如论文在摘要中所说： \u0026gt; We start this work by observing the simple but counterintuitive fact that marginal gains in single-step accuracy can compound into exponential improvements in the length of a task a model can successfully complete.\n为了验证这一点，作者们设计了一个巧妙的实验框架，将任务中的“规划 (Planning)”和“执行 (Execution)”两个环节解耦。他们发现，即便为模型提供了清晰的计划和所需的全部知识，模型在执行一连串简单的、重复性的步骤时，其准确率也会随着步骤增多而显著下降。\n这引出了论文的第二个核心发现——“自我调节效应 (Self-Conditioning Effect)”。这个概念非常新颖，它指的是模型在上下文中看到自己先前犯下的错误后，会变得更容易在接下来的步骤中再次犯错。这就像一个负面循环，模型被自己不完美的历史“污染”了。这与人类从错误中学习的模式截然相反。作者们通过精巧的“反事实实验”证明了这种效应的存在，并指出它不同于已知的长上下文信息丢失问题，且仅仅扩大模型规模并不能有效缓解这一问题。\n幸运的是，论文进一步发现，“思考” (Thinking) 模型，特别是那些通过CoT（思维链）或强化学习（RL）训练来生成中间推理步骤的模型，能够有效地克服自我调节效应。这些模型在执行新任务时，似乎能“独立思考”，而不受历史错误的过度干扰。\n最终，论文在自己设计的基准上对当前最前沿的模型进行了测试，结果惊人。那些不具备或未使用“思考”能力的模型，哪怕是千亿甚至万亿参数的巨型模型，在单轮中也难以正确执行超过2个步骤。而具备“思考”能力的模型，其执行长度则实现了数量级的飞跃。例如，文中提到的DeepSeek R1能执行200步，而下一代模型如GPT-5（代号Horizon）更是能达到惊人的1000步以上，远超其他对手。\n图1 论文核心贡献总结 图注：引自原论文图1，该图高度概括了论文的四大核心发现：（左上）单步准确率的微小提升带来任务长度的指数级增长；（右上）模型会因历史错误而自我调节，导致后续表现下降；（左下）扩大模型规模能有效延长任务执行长度；（右下）引入“思考”机制能极大提升任务执行长度，并使模型更不易受自我调节效应影响。\n总而言之，这篇论文不仅仅是提出了一种新的评估方法，它更像是一篇“檄文”，呼吁整个AI研究和产业界将目光从短任务的“智商测试”转向长时域任务的“可靠性工程”。它为模型扩展的经济价值提供了新的论证，并指明了提升模型在复杂代理（Agent）任务中实用性的关键方向——克服执行过程中的累积性失败。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解分析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是建立一个新的评估框架来衡量和理解大语言模型在长时域任务中的执行能力，并挑战当时学术界和工业界普遍存在的“模型扩展回报递减”的观点。 解决的实际问题： 评估的错位：当前的流行基准（如MMLU）主要衡量模型单轮、知识驱动的问答能力。然而，LLM最有价值的应用场景，如自主代理 (Autonomous Agents)、自动化软件工程、科学研究助手等，都需要模型在多个步骤中连续、可靠地工作。现有评估体系无法反映模型在这方面的能力，导致我们可能低估了顶尖模型的真实进展和潜力。 投资的疑虑：训练越来越大的模型需要消耗惊人的计算资源和资金。如果只能在现有基准上看到微不足道的提升，自然会引发“继续投入是否值得”的疑问。论文试图解答这个问题，为持续的研发投入提供新的、更具说服力的理由。 对行业发展的重要意义： 重新定义“更好”的模型：它推动行业从“知识更渊博”的模型转向“做事更可靠”的模型。这对于所有致力于构建AI Agent的公司来说，是方向性的指引。模型的价值将更多地体现在其能自主完成多长的任务流，而不是能回答多少难题。 指明Agent技术的核心瓶颈：论文明确指出了“执行失败”和“自我调节效应”是阻碍Agent走向实用的关键障碍。这为研究人员提供了清晰的靶子，未来的研究可以更集中地解决如何提高执行的鲁棒性、如何进行有效的错误检测与修正等问题。 为模型扩展提供新的“商业案例”：通过证明单步准确率的微小提升能带来任务长度的指数级回报，论文雄辩地论证了“大力出奇迹”在Agent时代依然有效。这会增强产业界继续投资于更大、更强基础模型的信心。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其思路和实验方法，而非提出了一个新模型。\n新思路一：重新审视回报递减——复合效应 内容：论文的核心观点是，即使单步准确率 \\(p\\) 的提升是递减的，但模型在任务成功率大于 \\(s\\) （例如50%）的情况下能够执行的长度 \\(H_s\\) 与 \\(p\\) 的关系是 \\(H_s(p) = \\ln(s)/\\ln(p)\\)。当 \\(p\\) 趋近于1时， \\(p\\) 的微小增加会导致 \\(H_s\\) 的巨大增长。 优势：这个思路将评估的焦点从一个线性的准确率数字转移到了一个非线性、指数增长的能力维度上，这更贴近复杂任务的本质——“一招不慎，满盘皆输”，因此对单步的可靠性要求极高。 新思路二：自我调节效应 (Self-Conditioning Effect) 内容：这是一个全新的概念，指模型会根据自己生成历史中的错误来“调整”自身，导致未来更容易犯错。 \u0026gt; We hypothesize that as a significant fraction of model training is to predict the most likely next token given its context, conditioning models on their own error-prone history increases the likelihood of future errors. 特点与优势：之前的研究更多关注长上下文导致的“信息遗忘”，而“自我调节”揭示了一种更深层次的、与模型生成行为模式相关的失败机制。它解释了为何LLM在重复性任务上表现不如传统计算机，因为它们不是在执行确定性程序，而是在进行一种“角色扮演”——扮演一个会犯错的自己。 新方法：解耦规划与执行的实验设计 内容：为了精准测量“执行”能力，作者设计了一个键值对字典求和的简单任务（见下图）。在这个任务中： 知识被外化：字典在上下文中直接提供，模型无需从参数中回忆知识。 规划被消除：每一步要加哪个键，由用户直接给出，模型无需自己规划。 特点与优势：这种“控制变量法”的设计非常巧妙，它排除了规划能力、知识储备等混杂因素的干扰，使得实验结果能够清晰地归因于模型的纯粹执行能力。这是一个非常干净、可复现、可无限生成数据的研究范式，对于基础能力的研究极具价值。 图注：引自原论文图3，展示了将长时域任务抽象为“检索-组合”步骤，并通过一个简单的字典求和任务来隔离和研究LLM的执行能力。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列精心设计的实验来层层递进地验证其假设。\n实验一：验证模型扩展对长时域执行的收益 设计：使用Qwen3和Gemma3两个模型系列，涵盖从4B到32B的多种尺寸。在字典求和任务上，让模型执行连续多轮（每轮加一个键），观察任务成功率随轮数增加的变化。 数据与结果：如图4(c)所示，模型的Horizon Length（任务成功率降至50%时的轮数）与模型大小呈现出清晰的正相关关系。例如，Qwen3-32B的Horizon Length远高于Qwen3-4B。这有力地证明了扩大模型规模能显著提升长时域执行能力，即使在这样一个无需复杂推理的简单任务上。 实验二：验证并拆分“自我调节效应” 设计：这是一个非常关键的“反事实”实验。研究者在评估模型第100轮表现时，人为地修改了前99轮的历史记录。他们控制历史记录中的错误率（Induced Error Rate），从0%（完美历史）到100%（完全错误的历史）不等。 数据与结果：如图5所示，结果非常清晰： 长上下文效应：即使在0%错误率的“治愈”历史下，模型在第100轮的准确率也低于第一轮，这部分性能下降归因于长上下文处理的固有挑战。 自我调节效应：随着历史记录中注入的错误率增加，模型在第100轮的准确率持续、显著地下降。这无可辩驳地证明了模型确实会因为看到历史错误而表现得更差。 模型扩展无法解决：一个令人惊讶的发现是，即使是像Kimi-K2和DeepSeek-V3这样的前沿大模型，虽然在“治愈”历史下表现优异（基本解决了100轮内的长上下文问题），但它们同样易受自我调节效应的影响。 实验三：验证“思考”机制的有效性 设计： 对比自我调节：对具备“思考”能力的Qwen3模型重复实验二。 单轮最大执行长度基准测试：设计了一个单轮任务，但要求模型一次性处理K个键值对的求和。通过二分查找，找到每个模型能以80%以上准确率处理的最大K值。 数据与结果： 如图6所示，“思考”模型完全免疫了自我调节效应。无论历史错误率多高，它们在第100轮的准确率都保持稳定。 如图7所示，在单轮最大执行长度测试中，“思考”与否造成了天壤之别。不使用思维链的模型（如Deepseek V3）连K=2都难以完成，而其经过RL训练的“思考”版本Deepseek R1能完成200步。GPT-5更是达到了1024步。这凸显了序列化的测试时计算（即“思考”）对于解决序列化任务的极端重要性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为长时域执行和Agent研究打开了新的大门，催生了诸多值得探索的方向：\n挑战与问题： 主动的自我纠正 (Self-Correction)：论文中的模型是“犯了错就一路错下去”。未来的关键挑战是让模型具备在执行过程中检测、反思并修正自身错误的能力。这需要模型有更强的元认知（meta-cognition）能力。 从简单执行到复杂工具使用：论文中的“执行”是简单的数学运算。真实Agent的执行涉及调用API、操作浏览器、与文件系统交互等。如何将这种对执行鲁棒性的研究扩展到多样化、有副作用的工具使用场景，是一个重要问题。 非马尔可夫任务的执行：论文的任务是马尔可夫的（当前状态只依赖于前一状态）。但许多真实任务（如长期对话、代码项目维护）需要模型记忆和利用遥远历史中的信息。论文中提到的“滑动窗口”上下文管理方法在这种场景下会失效，需要更根本的记忆机制创新。 探索“思考”的本质：论文证明了“思考”有效，但其背后的机制仍需深究。是强化学习带来的目标对齐？还是思维链本身提供的“抓手”和“草稿纸”？深入理解其工作原理，可能催生出更高效、更可靠的推理和执行方法。 新技术和投资机会： Agent可靠性工程：围绕如何提升Agent执行长任务的可靠性，会催生一系列新技术，例如：Agent的“单元测试”和“集成测试”框架、执行过程的监控与可观测性工具、失败任务的自动根因分析等。 下一代模型架构：自我调节效应可能暗示了标准Transformer架构在处理长序列生成任务时的固有缺陷。这可能激励对新架构的探索，例如混合了循环机制（RNN-like）、具备显式外部记忆或状态管理模块的架构。 Agent专用的评估基准：会出现更多像本文一样，但更贴近真实世界复杂度的长时域任务基准。投资于构建和维护这些高质量基准的公司将占据有利生态位。 强化学习与过程监督：对“思考”模型有效性的证明，将进一步推动过程监督 (Process Supervision) 和基于结果的强化学习 (RLAIF) 等训练技术的发展，因为它们是塑造模型可靠执行行为的关键。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文极具洞见，但从批判性角度看，仍存在一些局限和待验证之处：\n任务过于简化和“人造”：这是最主要的局限性。键值对求和任务虽然能干净地分离出“执行”能力，但它与真实世界任务的复杂性相去甚远。在真实任务中，状态更新、错误影响、动作空间都远比这复杂。因此，在该任务上的成功是否能直接泛化到真实Agent任务中，仍需打一个问号。它是一个必要条件，但远非充分条件。 对“思考”的解释不够深入：论文观察到“思考”模型能修复自我调节效应，并给出了一些推测（RL降低了语言建模的倾向性、独立思考等），但缺乏更深入的机制性探究。这使得“思考”在文中更像一个有效的“黑箱”，而非一个被完全理解的原理。 模型覆盖范围有限：论文中的扩展定律和自我调节效应主要基于Qwen3和Gemma3两个模型家族进行分析。虽然这两个系列很有代表性，但将结论推广到所有大模型架构（如Llama系列）仍需更多验证。 对前沿模型的引用可能存在推测性：论文中提及了GPT-5、Claude-4-Sonnet等模型及其具体性能数据，并给出了2025年的发布时间。在学术论文中，这通常是基于当时可得信息或为了说明数量级而设置的例证。我们需要意识到这些数据可能并非官方发布的最终性能，对其应持审慎态度。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中获得实践启发的您，以下几点是核心的学习内容：\n核心启发一：转变你的评估范式 学什么：在任何涉及多步骤流程的应用中（无论是聊天机器人、代码助手还是复杂的Agent），不要再仅仅依赖单轮交互的准确率作为最终评估指标。 怎么用：设计端到端的、能反映整个工作流成功率的测试用例。衡量模型“在连续N步之后不出错的概率”，这个指标远比单步正确率更有价值。这篇论文的Horizon Length (\\(H_s\\))就是一个可以直接借鉴的理念。 核心启发二：警惕并管理“自我调节效应” 学什么：认识到LLM在长序列任务中会“自我污染”。一次失败不仅是单点问题，它还会增加后续失败的风险。 怎么用：在你的Agent设计中，引入主动的状态管理和错误处理机制。例如： 上下文清理：对于马尔可夫任务，可以像论文附录A.2中那样，定期清理历史上下文，只保留最近的状态，打破负反馈循环。 显式状态传递：不要让模型从庞杂的对话历史中隐式推断当前状态，而是在每一步都以结构化的形式（如JSON）明确地传入当前状态。 优先选择“思考”模型：尽可能选择那些为推理和Agent任务进行过优化的模型（例如通过RL训练的模型），因为它们天生对此问题有更强的鲁棒性。 核心启发三：借鉴“隔离研究”的科学方法 学什么：论文解决问题的方法论本身就极具价值。当面对一个复杂的系统性失败时，学习如何将问题分解为相互独立的子模块（如规划、知识、执行），然后设计实验单独、干净地测试每个模块的能力。 怎么用：当你的Agent项目遇到瓶颈时，不要笼统地说“它不够智能”。尝试定位失败是在哪个环节：是没规划对？是知识不足？还是像这篇论文揭示的，计划和知识都对了，但就是“手滑”执行错了？精准的归因是有效优化的前提。 需要补充的背景知识： Scaling Laws：了解一下由Jared Kaplan等人在2020年提出的开创性工作，理解模型性能如何随计算、数据和参数量扩展。 思维链 (Chain-of-Thought, CoT)：熟悉其基本原理，即通过引导模型输出中间推理步骤来提升复杂问题的解决能力。 AI Agent框架 (如ReAct, Toolformer)：了解当前主流的Agent是如何将LLM的“大脑”（推理）与“双手”（工具）结合起来工作的。 长上下文模型评估：了解“大海捞针 (Needle in a Haystack)”等测试，并理解它与本文所关注的长输出 (Long Output) 生成的区别。前者关注输入信息的提取，后者关注输出序列的连贯性和准确性。 希望这份详尽的解读能帮助您深入理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-09-13T00:00:00Z","permalink":"/the-illusion-of-diminishing-returnsmeasuring-long-horizon-execution-in-llms/","title":"The Illusion of Diminishing Returns：Measuring Long Horizon Execution in LLMs"},{"content":"主要是一个新的框架，类似verl-agent，创新方法是课程学习，先用小step，再逐渐增大step。\n论文深度解读：从理论到实践，构建更强大的LLM智能体 当前，将大语言模型（LLM）从被动的文本生成器转变为能够主动与环境交互、完成复杂任务的智能体，已成为学术界和工业界竞相追逐的焦点。然而，这一过程面临着巨大挑战。现有方法大多依赖于两种路径：一是直接使用像GPT-4这样强大的闭源模型，通过精巧的提示工程（Prompt Engineering） 进行引导，但这限制了模型能力的进一步演化且成本高昂；二是通过监督微调（Supervised Fine-Tuning, SFT），即模仿专家示范数据来学习，但这种方式成本高昂、难以规模化，且模型很难学会处理专家数据未覆盖的新情况，缺乏真正的自主探索和泛化能力。\n这篇论文的核心贡献，正是为了打破这一僵局。它提出了一套无需SFT预先训练、完全通过与环境交互从头开始训练LLM智能体的端到端强化学习（Reinforcement Learning, RL）框架。作者认为，智能体真正的“智能”应当像人类一样，在与真实世界的不断试错和探索中习得，而非仅仅模仿。\n为此，论文提出了两大核心创新：\n1. AgentGym-RL 框架：一个模块化、可扩展的智能体“健身房”\n这不仅仅是一个单一的算法，而是一个完整的生态系统。它被设计成一个高度模块化（Modular） 和解耦（Decoupled） 的架构，主要包含三个核心部分： * 环境（Environment）模块：集成了多种真实且复杂的交互场景，如网页浏览（WebArena）、深度搜索（Deep Search）、数字游戏（TextCraft）、具身任务（BabyAI）和科学实验（SciWorld）。这种多样性确保了训练出的智能体能够应对不同类型的挑战。 * 智能体（Agent）模块：封装了LLM的推理和决策过程，支持长时程规划和自我反思等高级机制。 * 训练（Training）模块：实现了包括PPO、GRPO在内的多种主流RL算法，为优化智能体策略提供了强大的工具箱。\n正如论文中所述：“Designed with a modular and decoupled architecture, AgentGym-RL enables clean separation of agents, environments, and learning algorithms—offering high extensibility and flexibility for diverse research needs.”\n这种设计理念极大地提升了研究的灵活性和可复现性，研究人员可以像搭积木一样，轻松替换或添加新的环境、智能体模型或训练算法，从而加速整个领域的迭代。\n2. ScalingInter-RL 方法：一种创新的“渐进式”课程学习策略\n这是本文方法论上的最大亮点。训练智能体完成需要数十步才能解决的长时程（long-horizon） 任务，是RL领域公认的难题。如果一开始就让智能体在复杂的环境中自由探索，它很可能会因为始终无法获得成功（即获得奖励）而陷入“学不会”的困境，导致训练崩溃。论文中的实验也证实了这一点：\n“…beginning with a large number of interaction turns often leads the model into redundant reasoning and unproductive actions, ultimately causing training collapse and degraded performance.”\n为了解决这个问题，作者从人类学习过程中获得灵感，提出了ScalingInter-RL（Progressive Scaling Interaction for Agent RL）。其核心思想是一种课程学习（Curriculum Learning）： * 初期阶段（Exploitation-focused）：限制智能体与环境的最大交互步数（horizon），让它先专注于学习和掌握一些基础技能，解决相对简单的任务。这就像教孩子走路，先让他在平地上练习。 * 后期阶段（Exploration-focused）：随着训练的进行，逐步增加最大交互步数，鼓励智能体进行更深度的探索，学习更复杂的策略，如规划、反思和策略性回溯。这相当于让已经会走路的孩子去尝试更复杂的地形。\n这种渐进式增加“探索难度”的方法，巧妙地平衡了探索（Exploration） 与利用（Exploitation） 的关系，使得训练过程更加稳定、高效。如下图（源自论文Figure 5）所示，该方法通过逐步扩展交互边界 h，最终训练出更强大的智能体。\nScalingInter-RL 惊人的实验结果：7B模型超越顶级闭源模型\n这篇论文最令人振奋的部分在于其坚实的实验结果。作者在一个7B参数量的开源模型（Qwen-2.5-7B）上应用了他们的框架和方法，并与包括GPT-4o、Gemini-2.5 Pro在内的顶级闭源模型以及Llama-3.1-70B等更大规模的开源模型进行了全面对比。\n结果显示，经过AgentGym-RL训练的7B模型，在多个任务上达到甚至超越了这些比它大数十倍的、未经过特定智能体训练的模型。例如： * 在TextCraft（数字游戏）任务中，ScalingInter-7B模型取得了91.00的惊人高分，与Gemini-2.5-Pro的94.00分处于同一梯队，并且是极少数能在最难的“深度4”任务中获得非零分（33.33）的模型之一。 * 在BabyAI（具身任务）中，ScalingInter-7B以96.67%的准确率取得了SOTA（State-of-the-art）成绩，甚至超过了OpenAI o3的94.44%。 * 在SciWorld（科学实验）中，该模型取得了57.00分，创造了新的SOTA记录，显著超过了之前表现最好的OpenAI o3（41.50分）。\n这些数据雄辩地证明了一个关键洞见：\n“…strategic investment in post-training and test-time computation is more impactful than merely increasing a model’s parameter count.”\n也就是说，通过高效的、面向智能体任务的强化学习训练，一个中等规模的模型所能达到的智能水平，可以超越一个未经特定训练的、参数量大得多的模型。这无疑为大模型领域的发展，特别是如何经济高效地构建专用智能体，指明了一条全新的、极具潜力的道路。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n### 1. 论文的研究目标与意义 研究目标：本文的核心研究目标是创建一个统一、高效、端到端的强化学习框架（AgentGym-RL），用于在多样化和真实感的环境中，从零开始训练LLM智能体，使其具备解决长时程、多步骤复杂任务的能力，并且无需依赖昂贵的监督微调（SFT）数据。 解决的实际问题： 降低高性能智能体的构建门槛：当前构建强大LLM智能体严重依赖少数几家公司的顶级闭源模型，成本高昂且技术不透明。本文旨在证明，通过开源模型和RL训练，同样可以达到甚至超越这些模型的性能，从而实现技术的“平民化”。 解决LLM智能体训练不稳定的问题：在长时程任务中，RL训练常常因为奖励稀疏、探索空间巨大而导致模型“学废”或训练崩溃。本文提出的ScalingInter-RL方法，旨在解决这一核心技术痛点，提升训练的稳定性和效率。 填补学术界在多回合交互式RL框架上的空白：虽然已有研究尝试用RL训练LLM，但大多局限于单步任务或简单的环境。社区缺乏一个经过验证的、能够支持多种真实世界场景的、端到端的多回合交互RL框架。 对行业发展的意义： 这项研究的意义是深远的。它揭示了模型能力 ≠ 模型参数，训练方法的重要性可能不亚于模型的基础规模。对于行业而言，这意味着： 成本效益的提升：企业无需再盲目追求千亿甚至万亿参数的巨型模型，而是可以利用中等规模的开源模型，通过本文提出的RL方法，训练出满足特定业务需求的高性能、低成本专用智能体（例如，电商领域的自动购物助手、科研领域的实验助手等）。 推动智能体应用的普及：通过降低技术和成本门槛，更多的开发者和中小型企业将有能力开发自己的LLM智能体，这将极大丰富智能体应用生态，催生出更多创新的产品和服务。 开辟新的技术路径：它为LLM的能力演进提供了一条“后训练”（post-training）的新路径。模型的潜力不仅在预训练阶段被决定，通过与环境的持续交互和学习，它的能力可以被再次、甚至更大幅度地激发。 ### 2. 创新的思路、方法与模型 本文的创新主要体现在AgentGym-RL框架和ScalingInter-RL训练方法上。\nAgentGym-RL 框架 特点： 端到端RL训练：与依赖SFT的方法不同，它完全基于RL，让智能体直接从与环境的交互反馈中学习，这使得智能体能学到更灵活、更鲁棒的策略。 环境多样性与真实性：集成了五大类场景，覆盖了从信息检索到具身控制的多种能力维度，确保了模型能力的全面发展。 模块化与可扩展性：如前所述，其“即插即用”的架构设计，使得添加新环境、新算法变得异常简单，极大地促进了研究的迭代速度和社区协作。 优势：相较于之前零散的研究，AgentGym-RL提供了一个系统性的解决方案。它不仅是一个算法，更是一个包含了环境、智能体、训练、评估的全流程“兵工厂”。它让LLM智能体的训练从“手工作坊”模式，向“工业化流水线”模式迈进了一大步。 ScalingInter-RL 训练方法 特点： 课程学习机制：其核心是“由易到难”的渐进式学习。通过动态调整交互步数上限 h (horizon)，实现了对探索-利用困境的有效管理。 稳定性和效率：通过先掌握基础技能再探索复杂策略，避免了因过早进行无效探索而导致的训练崩溃。论文中的Figure 7清晰地展示了这一点：固定长边界（Max Interaction Rounds=10）的训练在早期奖励较高，但很快就崩溃了；固定短边界（=5）的训练稳定但会遇到性能瓶颈；而ScalingInter-RL则能稳定地达到更高的性能。 优势：ScalingInter-RL的优势在于它是一种非常符合认知科学原理的训练方法。它模拟了生物学习的自然过程，使得在复杂的RL环境中进行稳定和高效的训练成为可能。相比于那些需要大量超参数调优（hyperparameter tuning）才能勉强稳定的传统RL训练方法，ScalingInter-RL提供了一种更简洁、更鲁棒的解决方案。 ### 3. 实验验证与关键数据 论文的实验设计严谨且全面，通过在五个不同环境中与业界最强的模型进行对比，充分验证了方法的有效性。\n实验设计： 环境：WebArena, Deep Search, TextCraft, BabyAI, SciWorld。 基线模型： 闭源模型：GPT-4o, OpenAI o3, o4-mini, Gemini-2.5-Pro/Flash, Qwen-Max。 开源模型：Qwen, Llama-3.1, DeepSeek等不同参数规模的模型。 核心对比：将基于Qwen-2.5-7B训练的AgentGym-RL-7B和ScalingInter-7B模型与上述所有基线模型进行性能比较。 实验数据与结果： 以下表格汇总了论文中几个关键任务的性能数据，可以直观地看到ScalingInter-7B的强大之处： 任务领域 (Benchmark) ScalingInter-7B (本文方法) OpenAI o3 (顶级闭源) Gemini-2.5-Pro (顶级闭源) Llama-3.1-70B (大型开源) WebArena (Overall) 26.00% 34.00% 28.00% 16.00% Deep Search (Overall) 38.25 49.50 36.50 22.00 TextCraft (Overall) 91.00 93.00 94.00 93.00 BabyAI (Overall) 96.67% 94.44% 87.77% 86.67% SciWorld (Overall) 57.00 41.50 12.50 25.00 *数据来源：论文中的Table 1, 2, 3, 4, 5* 结果解读： 以小博大：ScalingInter-7B（7B参数）在BabyAI和SciWorld上取得了全场最高分，全面超越了所有闭源和开源模型。在TextCraft上与顶级模型性能相当。这证明了RL后训练带来的巨大价值。 显著提升：相较于未经训练的基座模型（如Qwen2.5-7B-Instruct在BabyAI得分66.67%），RL训练带来了近30个点的性能飞跃。 差距所在：在需要更强通用世界知识和复杂推理的WebArena和Deep Search任务中，本文方法虽然大幅超越了同量级的开源模型，但与最顶尖的OpenAI模型（特别是o3）仍存在差距。这说明基础模型的通用能力依然是智能体性能的天花板之一。 ### 4. 未来探索方向、挑战与机遇 结合当前大模型领域的发展，该研究方向未来充满机遇，也面临挑战。\n值得探索的问题与挑战： 泛化与迁移能力：当前智能体主要在“域内”（in-domain）设置下表现良好。未来的核心挑战是如何让智能体将在AgentGym-RL中学到的技能泛化到全新的、从未见过的环境和工具中，实现真正的零样本（zero-shot） 或少样本（few-shot） 迁移。 从数字世界到物理世界：论文中的任务仍局限于数字模拟环境。如何将RL训练扩展到更长时程、更复杂、与物理世界紧密结合的任务（如机器人控制、自动驾驶），是该领域的下一个圣杯。这将对模型的多模态（multi-modal） 理解能力和实时决策能力提出更高要求。 多智能体强化学习（Multi-Agent RL）：目前框架主要针对单智能体训练。探索多个智能体之间协作或竞争的场景，将开启全新的可能性（如智能体社会模拟、自动化研发团队），但也会带来通信、协调和算法设计上的巨大挑战。 样本效率和计算成本：RL训练通常需要海量的交互数据，计算成本不菲。如何提升训练的样本效率（sample efficiency），用更少的交互数据达到更好的效果，是决定该技术能否被广泛应用的关键。 新的技术和投资机会： 智能体开发平台（Agent Development Platforms）：类似于AgentGym-RL的、更加成熟和商业化的智能体训练与评估平台，将成为AI领域的基础设施，为企业提供一站式智能体解决方案。 垂直领域专用智能体：在金融、医疗、教育、科研、游戏等领域，训练专用LLM智能体的需求将爆发。能够提供高效训练服务和高质量专用智能体的公司，将拥有巨大的市场潜力。 智能体安全与对齐（Agent Safety \u0026amp; Alignment）：随着智能体自主性的增强，如何确保其行为符合人类的价值观和安全规范（即对齐问题）将变得至关重要。相关的研究和技术将成为一个新兴的、不可或缺的领域。 ### 5. 论文的不足与待验证之处 从批判的视角审视，这篇优秀的论文同样存在一些可以进一步探讨的局限性。\n存在的不足与缺失： 未能解决的硬性难题：在SciWorld的”Chem-Mix”（化学混合）子任务中，所有模型（包括本文模型）得分均为0。这暴露了当前LLM在需要深度、精确的科学模拟和推理方面的固有缺陷，本文提出的RL方法也未能突破这一瓶颈。 对计算资源的依赖：论文虽然强调了其方法的“成本效益”，但并未详细说明训练一个7B模型所需的具体计算资源（如GPU小时数）。RL训练通常是计算密集型的，这可能仍然是许多研究团队和小型公司面临的障碍。 模拟环境与现实的差距（Sim-to-Real Gap）：虽然WebArena等环境已经非常逼真，但与真实、动态、充满噪声的互联网环境相比，仍存在差距。在模拟环境中训练出的智能体，其策略能否无缝迁移到真实世界，是需要进一步验证的。 需要进一步验证和存疑之处： 方法的可扩展性：ScalingInter-RL在7B模型上取得了巨大成功。该方法在更大规模的模型（如70B, 100B+）上是否依然有效，甚至能带来更大收益？这是一个值得验证的问题。 对基座模型的依赖程度：本文使用了Qwen-2.5作为基座。训练效果在多大程度上依赖于基座模型本身的特性？如果换成Llama或Mistral等其他架构的开源模型，是否能复现类似水平的性能提升？ 奖励函数的设计：RL的成功在很大程度上取决于奖励函数（reward function） 的设计。论文中并未详细阐述在各个复杂环境中是如何设计奖励函数的。而这部分通常是RL应用中最具挑战性的“手艺活”。 ### 6. 核心启发与知识补充 对于希望从中汲取创新想法的您来说，这篇论文提供了极其宝贵的启示。\n应该重点学习什么？ “课程学习”的核心思想：ScalingInter-RL的“先易后难，逐步放开”是本文最核心、最值得借鉴的创新点。在任何复杂的AI训练任务中，思考如何设计一个动态的、由简到繁的“课程”，都可能成为项目成功的关键。 价值导向：RL \u0026gt; SFT：对于需要自主决策和适应性的智能体任务，应优先考虑强化学习。相比于只能模仿的SFT，RL能够让模型学到“为什么”这样做，而不仅仅是“怎么做”，从而在面对新情况时表现得更智能。 “小模型+精训练”的范式：不要迷信参数量。在资源有限的情况下，将算力投入到像AgentGym-RL这样的高效后训练方法上，可能比直接使用一个未经调优的“巨无霸”模型，回报率更高。 有哪些启发？ 启发一：任务分解与课程设计：在您自己的项目中，是否可以将一个宏大的、最终的目标，分解成一系列难度递增的子任务？然后让模型按此路径学习，或许能解决许多训练不收敛的问题。 启发二：构建最小化可行环境：如果您想训练一个特定任务的智能体，不必追求一步到位构建一个完美复杂的环境。可以先从一个简化的“沙盒”环境开始，验证核心算法，然后像AgentGym-RL一样，逐步增加环境的复杂度和真实性。 启发三：开源模型的巨大潜力：这篇论文是开源社区的一针强心剂。它证明了通过创新的训练方法，开源生态完全有能力追赶甚至超越闭源巨头。在您的技术选型中，应更加重视开源模型及其生态。 需要补充的背景知识： 强化学习基础：特别是策略梯度（Policy Gradient） 方法，如REINFORCE, PPO, TRPO。理解它们的基本原理，是读懂本文训练模块的关键。 LLM智能体（Agent）架构：了解当前主流的智能体框架，如ReAct（Reason+Act）等，这有助于理解智能体模块是如何进行思考和决策的。 POMDP（部分可观察马尔可夫决策过程）：这是对智能体所处环境进行数学建模的标准框架。理解其概念（状态、动作、观察、奖励、转移概率）有助于从更深层次理解智能体面临的决策问题。 希望这份详尽的解读能够帮助您全面、深入地理解这篇论文的价值。如果您对其中任何一个细节感兴趣，我们随时可以展开更深入的讨论。\n","date":"2025-09-12T00:00:00Z","permalink":"/agentgym-rltraining-llm-agents-for-long-horizon-decision-making-through-multi-turn-reinforcement-learning/","title":"AgentGym-RL：Training LLM Agents for Long-Horizon Decision Making through Multi-Turn Reinforcement Learning"},{"content":"建立TODO就是和planning一样，然后通过调用工具来执行计划。并且使用了多agent，可以跨仓库完成任务。\n论文深度解读：从代码库到智能体生态的范式革命 《EnvX: Agentize Everything with Agentic AI》这篇论文的核心贡献，在于提出并实现了一个名为 EnvX 的框架，其目标是实现“万物皆可智能体化”（Agentize Everything）。这里的“万物”，具体指的是 GitHub 等平台上的海量开源代码库。论文敏锐地指出了当前软件开发中的一个核心痛点：尽管开源社区提供了数百万个可复用的软件组件，但开发者利用它们的过程仍然是高度手动、繁琐且易错的。开发者需要花费大量时间阅读文档、理解API、配置环境、编写集成代码，这极大地阻碍了软件复用和创新的效率。\n为了解决这个问题，EnvX 提出了一种全新的范式：智能体化（Agentization）。这个概念是理解本文的关键。所谓智能体化，指的是将一个实体（在这里是代码库）转化为一个智能体。这个智能体不仅保留了原始实体的核心功能，还被赋予了自主行动和沟通的能力。想象一下，您不再需要“使用”一个图像处理库，而是可以直接对这个“图像处理智能体”下达自然语言指令，比如“帮我把这张照片变成宫崎骏风格”。如果任务复杂，比如“从社交媒体下载一张包含‘东方明珠’的图片，并将其转化为宫崎骏风格”，这个智能体还能主动寻找并“雇佣”其他智能体（如“社交媒体爬虫智能体”）来协同完成任务。这正是 EnvX 想要实现的宏大愿景。\n为了实现这一愿景，EnvX 的工作流程被精心设计为三个递进的阶段，论文中的图1清晰地展示了这一点：\n第一阶段：基于TODO列表的智能体环境设置（Agentic Environment Setting） 这是 EnvX 的基石，也是其区别于其他框架的第一个显著创新点。传统方法在处理代码库时，往往依赖于解析 requirements.txt 等结构化文件来安装依赖。但现实中的项目环境配置远比这复杂，还可能需要下载预训练模型、准备数据集等。EnvX 的做法是，让一个 EnvX 智能体 像人类开发者一样，去阅读非结构化的 README 文件和其他文档。通过理解文档内容，它能自动生成一个结构化的 TODO 列表，包含了完整的环境初始化步骤。 \u0026gt; 如论文所言：“By embedding TODO-driven control into initialization, EnvX not only systematizes the workflow and enhances goal clarity and decomposition, but also strengthens monitoring and self-reflection within the agentic system.” 这种方法将一个模糊的“环境设置”任务，分解为一系列清晰、可执行、可验证的子任务，大大提高了自动化配置的成功率和鲁棒性。\n第二阶段：与人类对齐的智能体自动化（Human-Aligned Agentic Automation） 在环境成功初始化后，EnvX 会实例化一个与该代码库绑定的“仓库智能体”（Repo Agent）。这个智能体被赋予了一系列工具，并且能够调用代码库自身的功能。当用户用自然语言提出任务时，该智能体能够自主地规划步骤、调用工具、执行代码，最终完成任务。它不仅仅是生成代码，更是调用和编排已有功能的执行者，这使得它的行为更加可靠和高效。\n第三阶段：面向智能体通信的智能体化（Agentization for Agentic Communication） 这是 EnvX 最具前瞻性的一步。它引入了 A2A（Agent-to-Agent）协议，旨在实现跨代码库的智能体协作。EnvX 为每个“仓库智能体”生成一张“智能体名片”（Agent Card），上面清晰地描述了该智能体的“技能”（Skills）。当一个任务需要多个代码库的功能时，一个“路由器智能体”（Router Agent）可以查阅这些名片，动态地组织一个智能体团队来协同工作。 论文中的图2给出了一个绝佳的案例：用户想“从社交媒体下载一张包含‘米雪’和‘东方明珠’的图片，并转成吉卜力风格”。这个复杂任务被自动分解并分配给三个智能体：\nMediaCrawlerAgent：负责从社交媒体抓取图片。 PromptOptimizerAgent：负责优化风格转换的提示词。 AnimeGANv3Agent：负责执行图片风格转换。 这种基于 A2A 协议的协作模式，真正将孤立的代码库连接成了一个强大的、分布式的智能系统，展现了构建未来软件生态的巨大潜力。 在实验验证方面，论文在权威的 GitTaskBench 基准上进行了详尽的测试。该基准包含了18个来自不同领域的真实开源项目和54个真实任务。EnvX 与 Aider、SWE-Agent、OpenHands 等当前主流的代码智能体框架进行了正面比较。实验结果（见表1）极具说服力：\n性能领先：在使用 Claude 3.7 Sonnet 模型时，EnvX 取得了 74.07% 的 ECR（执行完成率） 和 51.85% 的 TPR（任务通过率），全面超越了所有基准模型。其中，任务通过率比之前的最优结果提升了7.6%。这证明了 EnvX 框架的有效性。 效率优势：与表现同样出色的 OpenHands 相比，后者在处理任务时消耗了超过10倍的输入和输出 Token。这说明 EnvX 的方法（尤其是复用现有功能而非从头生成代码）在实现高成功率的同时，也极大地提升了计算效率。 总而言之，EnvX 不仅仅是一个工具或模型的改进，它代表了一种思想上的飞跃。它将我们与软件的交互方式从“编码”提升到了“对话”，将软件的组织形式从“孤岛”演进到了“生态”。这种“智能体化”的范式，为自动化软件工程、提升开发效率以及构建更强大的AI系统开辟了全新的道路。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是创建一个名为 EnvX 的框架，用于将任何开源代码库自动“智能体化”（Agentize），即把它们转变为能够理解自然语言、自主执行任务并能与其他智能体协作的智能实体。\n解决的实际问题： 它旨在解决当前软件开发中普遍存在的“开源软件复用困境”。具体来说，尽管 GitHub 等平台上有海量的开源代码，但开发者要利用它们，必须经历一个痛苦且低效的手动过程：\n学习成本高：需要阅读大量（通常不完整或过时的）文档来理解其功能和API。 环境配置复杂：手动安装依赖、下载模型、准备数据，这个过程繁琐且极易出错。 集成工作量大：需要编写大量的“胶水代码”来调用和组合不同库的功能。 这个问题导致了巨大的资源浪费，阻碍了创新的速度。 对行业发展的重要意义： 如果 EnvX 的愿景得以实现，将对软件行业产生革命性的影响：\n极大地降低软件开发门槛：非专业开发者或初学者可以通过自然语言直接利用强大的开源工具，而无需深入了解其底层代码实现。 实现软件开发的“超自动化”：复杂的软件任务可以被分解，并由一个智能体网络自动、协同地完成，人类开发者只需扮演监督和设计的角色。 催生全新的软件生态：软件将不再以“库”或“服务”的形式存在，而是以功能明确、接口标准（通过A2A协议）的“智能体”形式存在。这将形成一个可动态组合、自我组织的“智能体市场”，极大地加速应用创新。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路：代码库即智能体 (Code Repository as an Agent) 这是本文最核心的思路转变。传统方法（如 NL2Code）将代码库视为静态的知识源，目标是从自然语言生成全新的代码。而另一些工具（如 SWE-Agent）则将代码库视为修改和调试的对象。EnvX 则另辟蹊径，将其视为一个可交互、有主动性的智能体，目标是调用和利用其已有的功能。\n新方法：三阶段智能体化框架 为了实现上述思路，EnvX 设计了独特的三阶段流程，每个阶段都包含了创新点：\nTODO引导的环境初始化： 特点：不同于依赖 requirements.txt 的传统方法，EnvX 通过LLM阅读 README 等自然语言文档，生成结构化的初始化任务列表（TODO List）。 优势：这种方法更接近人类开发者的行为，能够处理更复杂的环境配置需求（如下载模型、运行预处理脚本），鲁棒性更强。 基于工具和代码库功能的任务自动化： 特点：EnvX 的智能体在执行任务时，不仅使用通用的基础工具（如文件读写、脚本执行），还能动态地识别和调用代码库本身封装好的功能。 优势：复用代码库的已有功能比从头生成代码更可靠、更高效。这确保了任务的执行质量，并显著降低了LLM的推理成本（Token消耗）。 基于A2A协议的多智能体协作： 特点：通过为每个智能体生成标准化的“智能体名片”（Agent Card）和“技能”（Skills），并建立 A2A（Agent-to-Agent）通信协议，实现了智能体之间的互发现和互操作。 优势：这使得 EnvX 的能力从单个代码库的自动化，扩展到了跨多个代码库的复杂任务流编排。它为构建一个庞大的、协作的软件智能体生态系统奠定了基础，这是之前所有工作都未曾实现的。 与之前方法的比较总结：\n对比维度 传统代码生成 (NL2Code) 代码修复智能体 (SWE-Agent) EnvX 核心目标 生成新代码 修改/修复现有代码 调用/复用现有功能 代码库角色 被动知识源 被动操作对象 主动交互的智能体 环境配置 依赖结构化文件 依赖结构化文件 通过阅读文档生成TODO列表 系统能力 单点代码生成 单一代码库内的任务 跨代码库的多智能体协作 优势 灵活性高 擅长解决具体issue 可靠性高、效率高、可扩展性强 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计：\n基准（Benchmark）：采用了 GitTaskBench，这是一个专门为评测代码智能体在真实代码库上解决实际任务而设计的基准。它包含18个来自不同领域的开源项目（如图像、语音、文档处理等）和54个由人类验证过的任务。 评价指标（Metrics）： 执行完成率 (ECR)：衡量智能体生成的方案是否能成功执行并产生预期的输出文件，不关心内容是否正确。它反映了方法的鲁棒性。 任务通过率 (TPR)：在ECR的基础上，进一步评估输出文件的内容是否正确。它反映了方法的有效性。 Token成本：统计LLM的输入和输出Token数量，衡量方法的效率。 基线模型（Baselines）：选择了三个有代表性的、强大的代码智能体框架进行比较：Aider, SWE-Agent, 和 OpenHands。 骨干模型（Backbone LLMs）：为了保证公平性，所有框架（包括EnvX）都在三种广泛使用的LLM上进行了测试：GPT-4o, GPT-4.1, 和 Claude 3.7 Sonnet。 实验数据和结果： 论文中的 Table 1 集中展示了核心实验结果。以下是关键数据的摘要和解读： \u0026gt; 引用关键数据： \u0026gt; 在最强的 Claude 3.7 Sonnet 模型驱动下： \u0026gt; * EnvX: ECR 74.07%, TPR 51.85% \u0026gt; * OpenHands: ECR 72.22%, TPR 48.15% \u0026gt; * SWE-Agent: ECR 64.81%, TPR 42.59% \u0026gt; * Aider: ECR 16.67%, TPR 12.96%\n结果分析：\nEnvX 性能最佳：从数据中可以清晰地看到，EnvX 在 ECR 和 TPR 两项关键指标上均取得了最高分，证明了其方法的优越性。特别是在任务通过率（TPR）这一最能体现最终效果的指标上，达到了51.85%，显著领先于其他所有方法。 效率优势显著：论文特别指出，虽然 OpenHands 的性能也很有竞争力，但它在使用 Claude 3.7 Sonnet 时消耗的 Token 数量是 EnvX 的10倍以上。这有力地证明了 EnvX 通过“复用”而非“生成”的策略，在效率上具有巨大优势。 跨模型鲁棒性强：EnvX 在不同的骨干LLM上都表现出色。例如，在 GPT-4.1 上，它将之前最好的 ECR 提升了23.4个百分点，TPR 提升了8.72个百分点，显示其框架设计不依赖于特定模型的“超能力”，具有很好的普适性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： A2A 协议的深化和标准化：当前的 A2A 协议还比较初级。未来需要像设计互联网 TCP/IP 协议一样，设计更完备的智能体通信协议，包括更丰富的技能描述、版本控制、错误处理机制、安全认证和计费结算等。 大规模智能体验证与安全：论文也承认，如何在大规模（百万级）智能体生态中进行有效验证是一个巨大挑战。如何确保智能体的行为是可靠、无害的？如何防止恶意智能体的出现？智能体安全（Agent Safety） 将成为一个至关重要的研究领域。 智能体组合与规划的复杂性：当智能体数量剧增时，如何让“路由器智能体”高效、准确地发现并组合正确的智能体来完成复杂任务，这是一个NP难问题。需要发展更强大的 AI规划（AI Planning） 和推理能力。 处理模糊和不完整文档：EnvX 的 TODO 列表生成依赖于高质量的 README 文件。但现实中大量开源项目的文档质量堪忧。如何让智能体在文档缺失或模糊的情况下，通过阅读代码来自主推断其功能和用法，是一个核心挑战。 可能催生的新技术和投资机会： 智能体即服务平台 (Agent-as-a-Service, AaaS)：类似于现在的云计算平台，未来可能会出现专门托管、管理、部署和交易“代码库智能体”的平台。开发者可以将自己的代码库一键发布为智能体，并从中获利。 自动化软件开发公司：将出现专注于利用智能体生态系统来为客户快速、低成本构建定制化软件的公司。他们的核心竞争力不再是写代码的工程师，而是善于设计和编排智能体工作流的“智能体架构师”。 智能体安全与审计工具：随着智能体生态的普及，专门用于检测、防御和审计智能体行为的安全产品和服务将成为刚需，这是一个潜力巨大的新赛道。 面向智能体开发的编程语言和工具链：可能会出现新的编程范式或语言，专门用于更方便地定义智能体的技能、行为和通信协议，以及配套的调试和测试工具。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 泛化能力的验证不足： 不足：实验虽然在 GitTaskBench 的18个代码库上取得了成功，但这相对于 GitHub 上数百万个项目来说只是九牛一毛。现实中的代码库质量参差不齐，文档混乱、依赖冲突、代码陈旧的情况非常普遍。EnvX 的方法在这些“野外”项目上的表现如何，仍是一个巨大的问号。 存疑：其核心的“TODO引导的初始化”方法，在面对一个几乎没有文档、只有少量代码注释的项目时，是否会彻底失效？ 对协作复杂性的简化处理： 不足：论文中的 A2A 协作案例（图2）非常清晰，但也可能经过了精心挑选。真实世界的多方协作充满了不确定性，比如一个智能体执行失败、返回了非预期的结果、或者需要动态协商参数等。论文对这些复杂的交互场景和鲁棒的错误处理机制着墨不多。 存疑：“路由器智能体”是如何被训练或设计的？它如何解决任务分解的歧义性？当多个智能体都能完成同一个子任务时，它如何选择？这些细节并未阐明。 成本和延迟的现实考量缺失： 不足：论文强调了相对于 OpenHands 的 Token 效率，但并未分析整个流程的端到端时间成本和金钱成本。将一个代码库“智能体化”需要多少计算资源？执行一个任务需要多长时间？这些对于实际应用至关重要的数据是缺失的。 安全风险被低估： 不足：让一个LLM智能体基于README文件自主地在环境中执行命令（如 pip install, curl 下载文件），这存在巨大的安全隐患。恶意的 README 文件可能引导智能体执行危险操作。论文虽然在讨论部分提到了安全，但并未在框架设计中体现出相应的安全机制（如沙箱隔离、权限控制等）。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新想法与启发： 核心启发：改变抽象层次。最大的启发是将问题从“如何生成代码”提升到“如何封装和调用功能”。对于任何一个复杂系统，都可以思考：我是否能将其“智能体化”，为其定义一个自然语言接口和一组“技能”，然后将其接入一个更大的协作网络？这个思想可以应用于软件开发、工作流自动化、物联网等多个领域。 从非结构化信息中提炼结构化流程：EnvX 从 README 生成 TODO 列表是一个绝佳的范例。这个“非结构化 -\u0026gt; 结构化”的模式非常有价值。在你的工作中，可以思考如何利用LLM将项目文档、邮件沟通、会议纪要等非结构化信息，自动转化为可执行的任务列表、项目计划或配置脚本。 协议先行的生态系统思维：EnvX 的 A2A 协议表明，要实现大规模协作，定义标准和接口是至关重要的一步。在设计系统时，与其为每个组件设计独特的接口，不如思考能否定义一个通用的、可扩展的通信协议，让所有组件都能在此基础上自由组合和协作。 需要补充的背景知识： 主流的智能体框架：建议了解 ReAct (Reason+Act) 框架（Yao et al., 2023, https://arxiv.org/abs/2210.03629），它是许多现代智能体思考和行动模式的基础。同时，可以看看 MetaGPT (Hong et al., 2024, https://openreview.net/forum?id=VtmBAGCN70) 和 ChatDev (Qian et al., 2024, https://arxiv.org/abs/2307.07924) 等多智能体协作框架，了解它们是如何模拟软件开发团队的。 工具学习（Tool Learning）：深入理解LLM如何学习和使用外部工具是理解EnvX这类系统的关键。 软件工程自动化（Automated Software Engineering）：了解该领域的基本概念和挑战，能帮助你更好地理解 EnvX 试图解决的问题的深度和广度。 多智能体系统（Multi-Agent Systems, MAS）：这虽然是一个经典的AI领域，但其关于通信、协调、协商和规划的理论，对于设计更复杂的智能体生态系统非常有借鉴意义。 ","date":"2025-09-12T00:00:00Z","permalink":"/envxagentize-everything-with-agentic-ai/","title":"EnvX：Agentize Everything with Agentic AI"},{"content":"记忆部分和ZEP：A TEMPORAL KNOWLEDGE GRAPH ARCHITECTURE FOR AGENT MEMORY很像，使用知识图谱管理记忆，并且随着时间的推理不断调整\n论文深度解读 这篇论文的核心目标是让一个具身智能体（例如家庭机器人或自动驾驶汽车）能够在一个不断变化的环境中，持续地、高效地理解并执行一组长期的、附带条件的指令。想象一下你告诉你的家庭机器人：“如果客厅温度高于26度，就打开窗户”，“如果电视开着但没人在看，就关掉它”，以及“如果看到书乱放，就把它放到书架上”。这些不是一次性的命令，而是一组需要机器人持续监控环境、并在条件满足时才执行的“规则”。这正是论文所定义的“持续指令跟随”（Continual Instruction Following）任务。\n传统方法的局限在于，它们要么只处理单一、静态的指令，要么在面对动态环境时效率低下。例如，一个简单的机器人可能需要每隔几秒就跑遍整个屋子，检查所有指令的条件是否满足，这无疑是极其浪费时间和能源的。这篇论文的精妙之处在于，它提出了一个名为 ExRAP (Exploratory Retrieval-Augmented Planning) 的框架，让智能体能够像人一样，智能地决定当前是应该去“探索”环境以获取最新信息，还是应该去“利用”已知信息来完成某个任务。\nExRAP框架主要由两大创新支柱构成：\n基于记忆增强的查询评估（Memory-augmented query evaluation）：为了避免重复探索，智能体维护着一个关于环境的记忆库。这个记忆库被设计成一个时序知识图谱（Temporal Embodied Knowledge Graph, TEKG），记录着它观察到的事实，例如 (电视, 状态, 关闭, 时间戳=7)。当需要判断某个条件（如“电视是否关闭”）时，智能体首先会查询这个记忆库。更关键的是，论文引入了“时间一致性”（Temporal Consistency）的概念。它认为记忆会随着时间的流逝而“衰退”，变得不可靠。因此，一个很久以前的记忆（比如一小时前看到电视是关的）其不确定性（或熵）会比一分钟前的记忆更高。这个机制使得智能体能够意识到哪些知识可能已经过时，从而激发它去重新探索。\n融合探索的集成任务规划（Exploration-integrated task planning）：这是ExRAP框架的灵魂。在每一步决策时，智能体不再仅仅考虑如何最高效地完成一个已知任务（即“利用”），而是会同时评估每个可能动作的“探索价值”。 \u0026gt; 正如论文公式(12)所定义的，智能体选择的下一个动作 zt 是最大化一个加权和： \u0026gt; \\[ \u0026gt; z_t = \\text{argmax}_{z \\in Z} [\\omega_\\tau \\cdot v_\\tau(G_t, z) + \\omega_R \\cdot v_R(G_t, z)] \u0026gt; \\] 这里的 vτ (exploitation value) 代表了动作 z 对于完成当前待执行任务的“利用价值”，而 vR (exploration value) 则代表了这个动作能在多大程度上帮助智能体减少对环境状态的不确定性，即“探索价值”。例如，“走到客厅”这个动作，它的“利用价值”可能为零（如果当前没有任务在客厅），但如果智能体对电视的状态很不确定，那么这个动作的“探索价值”就会很高，因为它能让智能体顺便看一眼电视的最新状态。\n通过这种方式，ExRAP框架赋予了智能体一种“深思熟虑”的能力。它会在“去厨房拿苹果”和“去客厅看看电视状态”之间做出权衡，最终形成一个兼顾任务执行效率和环境信息时效性的集成计划（Integrated plan）。实验结果有力地证明了这一框架的优越性。在ALFRED、VirtualHome和CARLA等多个模拟环境中，ExRAP在任务成功率（SR）和任务平均等待步数（PS）两个核心指标上都显著优于当前最先进的方法（如LLM-Planner）。尤其是在环境变化越快（高非平稳性）或指令越复杂的情况下，ExRAP的优势越明显。例如，在ALFRED的高非平稳性环境中，ExRAP的成功率达到了59.11%，而LLM-Planner仅为35.76%，差距悬殊。这充分说明了智能探索机制在应对动态复杂环境时的关键作用。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是开发一个名为ExRAP的AI框架，使具身智能体能够在非平稳（non-stationary），即持续动态变化的环境中，高效、鲁棒地执行多条、持续生效的、附带条件的指令。\n解决的实际问题： 它解决的是当前LLM驱动的具身智能体在面对真实世界复杂性时的两大痛点：\n一次性任务 vs. 持续性任务：现实世界中的任务（如家庭服务、安防巡逻）往往是持续性的，而不是“做完就忘”的一次性指令。例如，机器人需要一直遵守“地面脏了就打扫”的规则。 静态环境 vs. 动态环境：真实环境是不断变化的，人、物体都在移动，状态也在改变。智能体不能依赖过时的信息做决策，必须持续更新对环境的认知。 行业意义： 这项研究对于推动具身智能从“实验室玩具”走向“实用工具”具有重大意义。\n提升自主性与实用性：它让机器人等智能体更加自主，无需人类频繁下达新指令，就能根据预设的长期规则在复杂环境中持续工作，极大地提升了其实用价值，是实现真正的“智能管家”或“自主工人”的关键一步。 提高能源和时间效率：通过智能地平衡探索与利用，避免了盲目、重复地感知整个环境，这对于依赖电池供电的移动机器人等设备至关重要，直接关系到其续航能力和工作效率。 为复杂场景应用铺平道路：无论是智能家居、仓储物流、自动驾驶还是更复杂的灾后搜救场景，都需要智能体具备在动态、未知环境中持续执行复杂任务的能力。该研究为此提供了一个坚实的理论和技术框架。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文提出的核心方法是ExRAP框架，其创新思路体现在以下几个方面：\n思路一：将“持续指令”分解为“查询”和“执行” ExRAP首先将复杂的自然语言指令（如“如果温度高，就开窗”）分解成两个部分：\n查询 (Query, q)：一个需要验证的环境条件（温度是否高?）。 执行 (Execution, e)：条件满足后需要执行的任务（打开窗户）。 这种分解为后续的模块化处理奠定了基础，使得智能体可以专注于验证一系列“查询”。 方法二：用时序知识图谱（TEKG）管理动态记忆 为了存储和管理环境信息，ExRAP构建了一个TEKG。与简单的状态列表不同，TEKG中的每个事实都包含时间戳。 \u0026gt; 如论文中提到的，TEKG包含一系列四元组 τ = (se, re, te, t)，分别代表源实体、关系、目标实体和时间戳。 这使得记忆具有了时间维度。更重要的是，论文设计了时间一致性约束，即随着时间的推移，智能体对旧记忆的置信度会下降（熵增），这会促使它去重新确认。这是一个非常符合直觉且有效的设计。\n方法三：开创性的“探索-利用”双目标规划器 这是ExRAP最核心的创新。它没有采用单一的规划器，而是设计了两个并行的评估器：\n利用规划器 (Exploitation planner, vτ)：利用LLM的强大推理能力，根据当前的记忆和任务目标，评估某个动作对于“完成任务”的价值。这是传统方法的强化。 探索规划器 (Exploration planner, vR)：这是一个全新的设计。它基于信息论来评估一个动作对于“减少不确定性”的价值。一个动作能让智能体观察到它最不确定的环境区域，其探索价值就高。 \u0026gt; 论文公式(10)将探索价值定义为执行动作后，关于所有查询Q的互信息（Mutual Information）的增益。这为“探索”行为提供了坚实的数学基础。 特点与优势：\n主动性 (Proactive)：相比于被动等待指令或盲目循环检查，ExRAP智能体能主动地规划探索路径，以最高效的方式维护其对世界的认知。 高效性 (Efficient)：通过集成规划，智能体可以“一举多得”。比如在去厨房执行任务的路上，顺便经过客厅看一眼电视状态，将探索和利用行为无缝融合，避免了冗余的移动。 鲁棒性 (Robust)：在环境快速变化时，传统方法很容易因为信息过时而失败。ExRAP的时间一致性机制和探索规划器使其能快速适应环境变化，保持较高的任务成功率。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文通过一系列详尽的实验，在三个主流的具身AI模拟环境（VirtualHome, ALFRED, CARLA）中系统地验证了ExRAP的有效性。\n实验设计： 核心指标：SR (Task Success Rate, 任务成功率)，越高越好；PS (Pending Step, 平均等待步数)，即从条件满足到任务完成所需的平均步数，越低越好。 对比基线 (Baselines)：选择了多个有代表性的方法，包括零样本规划器ZSP、结合了 affordance 的SayCan以及当时最先进的LLM-Planner。 关键变量控制： 非平稳性程度 (Non-stationarity)：通过调整环境中状态变化的频率，设置了低、中、高三个等级。 指令规模 (Instruction scale)：测试了智能体同时处理3、5、7条不同指令时的性能。 指令类型 (Instruction type)：测试了不同表述方式（如句子、摘要、模糊对象）对性能的影响。 模型大小：测试了不同大小的LLM（Gemma-2B, Llama-3-8B/70B）对ExRAP和基线模型的影响。 消融研究 (Ablation Study)：为了验证关键模块的必要性，论文还对比了移除时间一致性模块（ExRAP-TC）和采用不同探索策略（ExRAP-LLM, ExRAP-EXP）后的性能变化。 实验数据和结果： 论文用大量数据证明了ExRAP的全面领先。 应对动态环境能力强： \u0026gt; 从表1可以看出，随着非平稳性从低到高，所有模型的性能都在下降，但ExRAP的优势越来越大。在VirtualHome的高非平稳性设置下，ExRAP的SR为50.12%，而LLM-Planner仅为34.60%。这说明ExRAP的探索机制在快速变化的环境中至关重要。 Model High non-stationarity SR (↑) High non-stationarity PS (↓) LLM-Planner 34.60% ± 6.49% 14.94 ± 2.89 ExRAP 50.12% ± 5.70% 8.61 ± 2.25 *(Table 1 in VirtualHome, simplified)* * **处理复杂多任务能力强**： \u0026gt; **表2**显示，当指令数量从3条增加到7条时，ExRAP的性能虽然也有所下降，但其领先优势进一步扩大。在7条指令时，ExRAP的PS（效率）为**8.76**，而LLM-Planner高达**17.63**，效率高出一倍。这证明了其集成规划的有效性。 * **关键模块不可或缺**： \u0026gt; **表4**的消融实验表明，去掉时间一致性模块后（ExRAP-TC），模型性能急剧下降，SR平均降低了**15.56%**。这证明了对记忆进行“老化”处理的必要性。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 尽管ExRAP取得了显著进展，但该研究方向仍有广阔的探索空间。\n值得探索的问题和挑战： 更复杂的长期记忆与学习：当前的TEKG是任务内的短期记忆。如何构建一个能够跨越数天、数周甚至更长时间的长期记忆系统？智能体应能从过去的失败经验中学习，形成更高效的探索与利用策略。 从“知道什么未知”到“知道如何探索”：ExRAP解决了“知道要去哪里看”的问题，但“如何看”仍然依赖预定义技能。未来的研究可以探索智能体自主学习探索技能，例如学习如何打开抽屉、移开障碍物来获取信息。 多智能体协作探索：在更大规模的环境中（如大型仓库或办公楼），单个智能体的探索效率有限。研究多个智能体如何共享信息、分配探索任务，将是重要方向。 人机交互与主动问询：当探索成本极高或存在危险时，智能体不应盲目探索，而应能主动向人类用户提问：“我不确定X的状态，需要现在去检查一下吗？”这种人机协作的决策模式更具实用性。 Sim-to-Real Gap：将ExRAP这样复杂的决策框架从模拟器迁移到真实的物理机器人上，会面临感知噪声、执行不确定性、安全约束等巨大挑战。 新的技术和投资机会： 具身智能操作系统 (Embodied OS)：可能会出现专门为机器人和具身智能体设计的操作系统，其核心就是一套类似ExRAP的、用于管理记忆、任务和决策的软件栈。这将成为智能机器人的“大脑”。 新一代机器人即服务 (RaaS 2.0)：当前RaaS更多是执行预定程序。基于此类技术，可以提供更高级的“自主决策服务”，客户只需设定高级规则，机器人就能在动态环境中自主工作，催生新的商业模式。 面向决策的AI芯片：ExRAP涉及大量的图计算（TEKG）、推理（LLM）和优化（双目标规划）。未来可能会出现专门为这类混合计算负载设计的AI芯片，在终端设备上实现低功耗、高效率的自主决策。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 从批判的角度审视，这篇论文同样存在一些潜在的局限性：\n不足及缺失： 计算开销分析不足：论文虽然提出了一个复杂的框架，但并未详细量化其计算开销，如决策延迟、GPU/CPU占用率等。在实际应用中，如果每一步决策都需要很长时间，其可用性会大打折扣。 超参数敏感性讨论缺失：公式(12)中的探索与利用权重 ωR 和 ωτ 是如何设定的？它们是否需要针对不同环境或任务进行精细调整？论文对此缺乏讨论，这可能影响模型的可复现性和泛化性。 记忆规模扩展性问题：TEKG随着时间推移会不断增长。论文没有探讨当知识图谱变得非常庞大时，检索和更新的效率问题，这在长期运行的系统中是一个无法回避的挑战。 需要进一步验证和存疑之处： 探索价值的近似计算：公式(11)中对探索价值的计算是一个近似，它基于一个“温和的假设”，即当记忆完全同步时熵为零。这个假设在多大程度上成立？在更复杂的、部分可观测的环境中，这种近似的有效性有待进一步验证。 对指令的理解能力：实验中的指令虽然多样，但仍然是结构化的。对于更口语化、模糊甚至带有比喻的指令，ExRAP的指令解释器（Instruction Interpreter）能否稳健地分解为“查询”和“执行”，是一个疑问。 Sim-to-Real的真实表现：所有实验均在模拟器中完成。模拟器简化了物理世界的复杂性，如传感器误差、光照变化、执行器故障等。该框架在真实物理机器人上的表现如何，是最大的未知数。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，您可以重点学习和借鉴以下几点：\n重点学习的创新思想： 将探索-利用框架形式化：核心启发是，不要将“探索”视为一种随机或启发式的行为，而应尝试将其数学化、模型化。通过信息论等工具为其建立一个明确的优化目标，并将其与主要任务（利用）整合进一个统一的决策框架中。这个思想可以应用到任何需要平衡信息获取和目标达成的领域。 动态数据的时间价值建模：在处理任何需要与外部动态世界交互的AI任务时，都应该为你的数据或知识引入时间维度。通过建模信息随时间的“衰退”或“不确定性增加”，可以驱动系统进行主动更新。这比设定固定的刷新周期要智能得多。 RAG的广义应用：不要把检索增强生成（RAG）局限于从静态文本库中检索信息。这篇论文展示了RAG可以应用于动态变化的、结构化的知识图谱。你的“知识源”可以是任何形式的数据库，关键在于如何有效地检索并将其融入LLM的推理上下文中。 给您的启发： 应用于其他领域：这个框架的思想可以被迁移。例如，一个智能投资助手，其“利用”是执行交易策略，“探索”则是花少量资金测试新的市场信号。一个推荐系统，“利用”是推荐用户高概率喜欢的物品，“探索”则是推荐一些新品类以拓宽对用户兴趣的认知。 设计更智能的系统：在您设计任何长期运行的AI系统时，都可以问自己：我的系统如何感知变化？它如何判断自己的知识是否过时？它应该在什么时候去主动“看一看”外部世界？ 需要补充的背景知识： 具身AI (Embodied AI)：了解该领域的基本概念、任务设定（如Navigation, Manipulation）以及主流的模拟环境（如ALFRED, Habitat, VirtualHome）。 强化学习 (Reinforcement Learning)：尤其是探索与利用 (Exploration vs. Exploitation) 的基本概念，这将帮助您更深刻地理解ExRAP框架的动机。 信息论基础 (Information Theory)：了解熵 (Entropy) 和互信息 (Mutual Information) 的基本概念和计算方法，这是理解ExRAP探索规划器数学原理的关键。 知识图谱 (Knowledge Graph)：了解其基本结构、构建和查询方式，有助于理解TEKG的设计。 ","date":"2025-09-12T00:00:00Z","permalink":"/exploratory-retrieval-augmented-planning-for-continual-embodied-instruction-following/","title":"Exploratory Retrieval-Augmented Planning For Continual Embodied Instruction Following"},{"content":"坏消息：缝合怪。好消息：全缝了。\nimage.png 深入解读：K2-THINK 的创新贡献与核心思想 这篇论文的核心论点是：通过将先进的训练后技术、巧妙的测试时计算策略以及专用的硬件优化进行系统性整合，一个仅有320亿参数的中等规模模型，能够在复杂的推理任务（尤其是数学）上，达到甚至超越数倍于其大小的顶尖模型的性能。 K2-THINK 不仅仅是一个模型，它是一个精心设计的“推理系统”，其贡献在于提供了一份清晰、可行的技术蓝图，挑战了“模型参数越大越智能”的传统观念。\n论文的创新性可以归结为“六大技术支柱”的协同作用，这六大支柱覆盖了模型从训练到部署的全生命周期：\n长链思维监督微调 (Long Chain-of-thought Supervised Finetuning, SFT): 这是系统的第一步，目标是“教会模型如何思考”。研究团队使用包含详细解题步骤（即链式思维，Chain-of-Thought）的高质量数据集对基础模型 Qwen2.5-32B 进行微调。这并非简单地让模型记住答案，而是让它学习并模仿复杂问题所需要的结构化、多步骤的推理过程。 \u0026gt; 正如论文在2.1节中提到的，SFT 阶段的目标是“为预训练的基础语言模型提供指导，以生成用于复杂查询的结构化响应”。从图2可以看出，模型性能在训练初期（约0.5个epoch）迅速提升，尤其是在AIME数学基准测试上，这表明模型很快就掌握了长链推理的“格式”。\n基于可验证奖励的强化学习 (Reinforcement Learning with Verifiable Rewards, RLVR): 在模型学会了“如何思考”之后，第二步是“让模型思考得更正确”。RLVR 是一种比传统人工反馈强化学习（RLHF）更高效的对齐技术。对于数学、代码这类有明确正确答案（即可验证结果）的领域，可以直接用程序的执行结果或答案的正确与否作为奖励信号，来优化模型的推理策略。这避免了昂贵且主观的人工标注。 \u0026gt; 论文在2.2节中提到，他们使用了包含数学、代码、科学等六个领域的 Guru 数据集进行RLVR训练，这大大扩展了模型的通用推理能力。\n推理前智能规划 (Agentic planning prior to reasoning, “Plan-Before-You-Think”): 这是测试时计算的第一环，引入了“谋定而后动”的理念。当系统接收到一个复杂问题时，它不会直接开始解答，而是先调用一个独立的语言模型（作为“规划器”）生成一个高层次的解题计划和关键概念。这个计划随后会和原始问题一起，作为更丰富、更结构化的提示（Prompt）输入到 K2-THINK 模型中。 \u0026gt; 这是一个简单而极其有效的创新。如图5所示，这个“Plan-Before-You-Think”步骤位于用户查询和K2-THINK模型之间，起到了导航作用。更令人惊讶的是，如表3所示，这种规划不仅提升了准确率，还平均减少了近12%的答案token消耗，因为它让模型的回答更简洁、更聚焦。\n测试时计算扩展 (Test-time Scaling, Best-of-N Sampling): 这是提升性能的核心武器。系统会让K2-THINK模型针对同一个问题，独立生成 N 个不同的解题路径（即“集思广益”）。然后，再由一个外部验证器（verifier）对这 N 个答案进行两两比较，最终选出最优的答案。 \u0026gt; 论文的一个关键发现是，N=3 (即 Best-of-3, Bo3) 是一个性价比极高的选择。表2的组件分析清晰地显示，仅使用Bo3带来的性能提升（从86.26%提升到90.77% on AIME 2024）是所有测试时技巧中最大的，远超规划带来的提升，同时计算开销可控。\n投机解码 (Speculative Decoding): 这是推理速度优化的技术。它通过用一个更小的模型来预测主模型可能的输出，然后让主模型一次性验证多个token，从而大幅减少生成每个token所需的时间。\n推理优化硬件 (Inference-optimized Hardware): 这是将整个系统从“理论可行”推向“实际可用”的关键。论文明确指出，他们将K2-THINK部署在 Cerebras晶圆级引擎（Wafer-Scale Engine, WSE） 上。这种硬件的特点是拥有巨大的片上内存和内存带宽，可以将整个32B模型放入芯片，避免了传统GPU架构中反复从内存读取权重的瓶颈。 \u0026gt; 论文给出了一个震撼的数据对比：一个典型的32,000 token的长推理任务，在NVIDIA H100上需要近3分钟，而在WSE上仅需16秒，实现了超过10倍的速度提升，达到了惊人的每秒2000 token的生成速度。这使得复杂的、需要多次交互的推理应用成为可能。\n综上所述，K2-THINK的真正创新在于它将模型、算法、软件、硬件视为一个整体进行系统性优化，并毫无保留地分享了这套“组合拳”的每一个细节和效果。它向我们证明了，通过精巧的设计，我们可以在不追求无尽参数扩张的前提下，实现AI推理能力的巨大飞跃。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是，探索并验证一套能够显著提升中等规模语言模型（32B）复杂推理能力的方法论，使其在参数效率上远超当前最先进的大模型。\n解决的实际问题:\n性能与成本的矛盾：当前最顶尖的推理模型（如GPT-4/5）参数量巨大，导致训练和推理成本极其高昂，只有少数科技巨头能够负担。这阻碍了技术的普及和创新。 开源模型与闭源模型的差距：开源社区虽然活跃，但在最尖端的推理能力上，开源模型与最先进的闭源模型之间仍存在显著差距。 推理延迟问题：复杂的推理任务通常需要生成很长的文本，在传统硬件上耗时严重，使得实时交互应用难以实现。 对行业发展的重要意义:\n推动AI民主化：K2-THINK提供了一条“以智取胜”而非“以力取胜”的技术路径。它告诉中小型企业和研究机构，无需耗费巨资训练千亿、万亿参数模型，通过优化训练方法和推理策略，同样可以获得顶尖的AI推理能力。 开辟新的优化维度：将行业的焦点从单纯的“模型规模竞赛”引导到“系统效率竞赛”上，鼓励研究者更多地关注训练后算法、推理时计算、软硬件协同设计等方向。 加速应用落地：通过在Cerebras WSE上的部署，K2-THINK展示了高性能推理系统在实际应用中的可行性，将原本需要数分钟的计算缩短到十几秒，为科学计算、智能体（Agent）、复杂对话系统等应用的普及铺平了道路。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ K2-THINK的核心并非提出一个全新的颠覆性算法，而是创造性地将多种现有技术进行系统性的整合与优化，形成了一套完整的、端到端的解决方案。其思路和方法的特点与优势如下：\n全链路系统化设计: 特点: 论文没有孤立地看待模型训练或推理，而是将训练后（SFT+RLVR）、测试时（规划+BoN）、部署端（硬件加速） 三个阶段视为一个有机整体。 优势: 这种系统观使得各个环节的优势可以叠加和互补。例如，SFT和RLVR赋予模型强大的基础推理能力，而测试时的规划和BoN则像一个“外挂”，将这种基础能力最大化地发挥出来。最后的硬件优化则确保了整个复杂系统能够高效运转。 高效的后训练策略 (SFT + RLVR): 特点: 论文选择了“先模仿，后优化”的两步走策略。先通过SFT让模型学会长链推理的“套路”，再通过RLVR在有正确答案的领域进行“实战演练”，以结果为导向进行优化。 优势: RLVR相比于依赖人类偏好的RLHF，其奖励信号更客观、成本更低、可扩展性更强，尤其适合在逻辑性强的任务上进行深度优化。 性价比极高的测试时计算: 特点: K2-THINK的测试时计算策略非常务实。它没有采用无限增加计算量的复杂方法，而是找到了两个性价比最高的“甜点”：“事前规划” 和 “Best-of-3”。 优势: 与复杂的思路（如思维树、多智能体辩论）相比，“Plan-Before-You-Think + Bo3”的组合在计算开销增加有限的情况下，带来了巨大的性能收益。特别是Bo3的有效性，证明了适度的多样性搜索比无限制的暴力搜索更具实践价值。 “软硬结合”的部署理念: 特点: 将特定的硬件架构（Cerebras WSE）作为系统的“第六大支柱”，这在学术论文中是比较少见的。 优势: 这体现了团队对实际应用落地的高度重视。一个理论上再好的模型，如果推理速度慢到无法使用，其价值也会大打折扣。K2-THINK证明了前沿模型的设计需要从一开始就考虑其在目标硬件上的部署效率。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列严谨、全面的实验来验证K2-THINK系统的有效性，主要包括模型性能对比和组件消融研究。\n实验设计: 基准测试: 选取了在学术界和工业界公认的、高难度的推理能力评测基准，覆盖数学、代码、科学三大领域。 对比对象: 对比了当时最先进的开源模型（不同参数规模）和顶尖的闭源模型（API访问），确保了评估的全面性。 评估方法: 采用了严格的评估协议，例如pass@1（一次生成成功率），并且为了结果的稳定性，每个基准测试都独立运行16次后取平均值。 实验数据和结果: 论文的核心性能数据集中在表1。为了更直观地展示，我为您整理了关键对比： 模型 参数量 数学综合分 (Micro-Avg.) LiveCodeBench (代码) GPQA-D (科学) K2-THINK (本文) 32B 67.99 63.97 71.08 GPT-OSS 120B 120B 67.20 74.53 77.04 DeepSeek V3.1 671B 64.43 66.59 79.46 Nemotron 32B 32B 65.78 57.79 74.98 Qwen3 235B-A22B 235B 62.99 56.64 65.55 — — — — — o3 High (Proprietary) - 68.68 73.30 81.30 Gemini2.5 Pro (Proprietary) - 73.82 58.24 84.51 \u0026gt; 从上表可以清晰地看到： \u0026gt; * 在核心的**数学推理**上，32B的K2-THINK（67.99分）**超越了**120B的GPT-OSS 120B和671B的DeepSeek V3.1，表现出惊人的参数效率。 \u0026gt; * 在**代码和科学**领域，K2-THINK也展现了强大的竞争力，其性能远超同等规模的模型，并能与一些更大规模的模型相媲美。 组件消融实验 (Component Analysis): 这是论文的另一大亮点，展示了每个测试时组件的贡献。表2的数据非常关键： 配置 AIME 2024 Omni-MATH-HARD SFT+RL Checkpoint (基线) 86.26 56.74 + Plan only (仅规划) 85.21 58.97 + Bo3 only (仅Best-of-3) 90.77 59.47 + Plan + Bo3 (完整K2-THINK) 90.83 60.73 \u0026gt; 这个消融实验清晰地表明： \u0026gt; * **Best-of-3是性能提升的最大功臣**，它自己就带来了约4-5个百分点的提升。 \u0026gt; * 规划（Plan）单独使用时效果不一，但在与Bo3结合后，能进一步推高系统性能上限，实现了**1+1\u0026gt;2**的效果。 这些详实的数据和严谨的实验设计，有力地支撑了论文的核心论点。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? K2-THINK为我们打开了一扇新的大门，未来的探索方向充满机遇与挑战：\n值得探索的问题和挑战: 动态计算与自适应推理: 目前K2-THINK的测试时计算策略（Plan+Bo3）是固定的。未来的挑战在于能否让模型学会“思考如何思考”？即模型可以根据问题的难度和类型，动态地决定是否需要规划、需要生成多少个候选答案（N值大小）、甚至选择不同的推理工具。这被称为自适应计算（Adaptive Computation）。 将配方扩展到更广泛的领域: K2-THINK在以逻辑为主的数理领域取得了巨大成功。如何将这套方法论有效地扩展到创造性、主观性更强的领域（如文学创作、艺术评论、商业策略），是一个巨大的挑战，因为这些领域缺乏“可验证的奖励”。 训练与推理的深度协同: 目前训练和测试时计算在一定程度上是分离的。未来的研究可以探索如何在训练阶段就将推理策略（如规划、验证）内化到模型参数中，使得模型在推理时能更“原生”、更高效地执行这些复杂操作，而不是完全依赖外部脚手架。 可解释性与可靠性: 随着推理过程越来越复杂（如多步规划、多答案比较），系统的决策过程也变得更加黑盒。如何保证复杂推理系统的可靠性、可解释性和安全性，将是一个至关重要的研究方向。 新的技术和投资机会: AI推理优化中间件 (Inference Optimization Middleware): 可能会出现一批专注于提供“测试时计算”服务的公司。它们不研发基础模型，而是为市面上主流的开源模型提供即插即用的推理增强引擎（如自动规划、动态BoN、集成验证器等），帮助企业以低成本提升现有AI应用的能力。 新一代AI芯片与硬件: K2-THINK凸显了硬件在释放AI潜力中的关键作用。除了Cerebras的晶圆级技术，专注于优化长序列、多分支推理等复杂计算负载的新型AI加速器将迎来巨大的投资机会。 垂直领域“小钢炮”模型: 企业可以利用K2-THINK的配方，基于一个中等规模的开源模型，使用自己所在领域的专有数据进行SFT和RLVR，打造出在特定领域（如金融分析、法律文书、药物研发）性能超越通用巨型模型的“小而美”的专家模型，这将是AI应用落地的重要商业模式。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管K2-THINK取得了令人瞩目的成就，但从批判性的角度审视，它依然存在一些局限和待验证之处：\n方法的普适性存疑: 对基础模型的依赖: 整套方法论在 Qwen2.5-32B 上取得了成功。但它能否无缝迁移到其他不同架构、不同规模的基础模型（如Llama系列、Mistral系列）上，并取得同样的效果？论文并未对此进行探讨，其普适性有待验证。 对特定任务的偏好: K2-THINK的优势主要体现在具有明确答案和逻辑步骤的数理任务上。在更开放、更依赖世界知识或创造力的任务上，其表现如何，论文提供的数据相对较少。 成本计算的模糊性: “隐形成本”未明确: 论文中提到的“规划器”和“验证器”都是独立的语言模型。这些外部模型的调用会带来额外的计算成本和延迟。论文没有详细说明这些外部模型的具体型号、大小和开销，这使得对K2-THINK总拥有成本（TCO） 的评估不够完整。 硬件的特殊性: 系统的超高性能严重依赖于Cerebras WSE这一相对小众且昂贵的硬件。在更普及的GPU硬件上，这套系统的实际性能和延迟表现如何，是否依然具有竞争力，需要打上一个问号。 安全性的短板: 论文在3.1节的“红队测试”中非常坦诚地暴露了模型的安全短板。如表6所示，其在“网络安全与数据保护”方面的得分仅为0.56，在“提示词提取鲁棒性”上仅为0.35。表7也显示，模型对“潜在越狱（LatentJailbreak）”等间接攻击的抵抗力较弱（得分0.37）。这表明，模型强大的推理能力并不能自然转化为强大的安全对齐，两者需要分开进行针对性优化。 部分结论的论证稍显薄弱: 论文中提到“被大量SFT过的模型在RL阶段的适应能力会受限”，这更多是基于观察的推测（“We suspect…”），而非通过严格实验得出的结论。这一有趣的现象值得更深入的研究来证实或证伪。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文是创新思想的宝库。以下是您可以直接借鉴和学习的核心启发：\n重点学习与启发: 系统思维，而非模型思维: 最大的启发是超越模型本身。在您的项目中，不要只想着换个更大的模型，而应思考如何设计一个从数据处理、模型训练、到推理策略、再到服务部署的完整系统。K2-THINK的成功是系统工程的胜利。 拥抱“测试时计算”的力量: 这是最“拿来即用”的创新点。在您的应用中，尝试引入Best-of-N（可以从N=3开始尝试）和事前规划。即使只是用简单的Prompt让另一个LLM为你的主模型制定一个计划，也可能带来意想不到的性能提升。这是低成本提升模型智能上限的“杠杆”。 效率即是质量: 论文中“规划反使答案更短”的发现极具启发。在设计Prompt和系统时，应追求让模型生成简洁、精确、高信息密度的答案，而不是冗长的“废话文学”。这不仅能提升用户体验，还能实实在在地降低API调用成本和延迟。 结果导向的优化 (RLVR): 如果您的应用场景有明确的、可自动验证的正确性标准（如代码生成、数据分析、API调用），那么强烈建议尝试引入RLVR的思想。构建一个小的验证集，用模型的输出结果（正确/错误）作为奖励信号，来持续优化您的模型。 需要补充的背景知识: 思维链 (Chain-of-Thought, CoT): 深入理解CoT的原理，以及它如何激发大模型进行多步推理。 模型对齐技术 (Alignment): 了解监督微调 (SFT) 和 基于人类反馈的强化学习 (RLHF) 的基本概念和流程，这将帮助您更好地理解RLVR的创新之处。 解码策略 (Decoding Strategies): 除了常见的Greedy Search和Beam Search，您应该了解温度采样 (Temperature Sampling)、Top-p (Nucleus Sampling)，以及本文中提到的Best-of-N和自洽性 (Self-Consistency) 等更高级的解码方法。 AI智能体框架 (Agentic AI Frameworks): 了解如ReAct、Plan-and-Solve等基础的智能体框架，这将帮助您更好地理解“Plan-Before-You-Think”在整个智能体系统中的角色和意义。 希望这份详尽的解读能够帮助您深入理解K2-THINK的精髓，并从中汲取宝贵的创新灵感。\n","date":"2025-09-12T00:00:00Z","permalink":"/k2-thinka-parameter-efficient-reasoning-system/","title":"K2-Think：A Parameter-Efficient Reasoning System"},{"content":"直接在推理时训练模式生成多条推理路径的能力并总结所有的路径。类似Learning to Refine：Self-Refinement of Parallel Reasoning in LLMs，其中并行推理的思路也类似ParallelSearch：Train your LLMs to Decompose Query and Search Sub-queries in Parallel with Reinforcement Learning。在训练时还结合了课程学习，在简单问题上强制并行推理（必须并行推理才给奖励），在困难问题上就只给结果奖励。\nimage.png 论文深度解读 这篇由腾讯AI Lab等机构研究员撰写的论文《Parallel-R1》 (https://arxiv.org/abs/2509.07980)，提出了首个旨在通过强化学习（Reinforcement Learning, RL）从零开始教会大语言模型（LLM）进行平行思考（Parallel Thinking）的框架。平行思考，顾名思义，是指模型在解决问题时，能够同时探索多条不同的推理路径或解决方案，最后综合这些路径得出结论。这与传统模型主要依赖的“单线程”的思维链（Chain-of-Thought, CoT）形成了鲜明对比，后者一旦在某个中间步骤出错，就很容易“一条道走到黑”。\n论文的核心洞见在于，当前激活LLM平行思考能力的方法主要有两类，但都存在明显缺陷： 1. 测试时策略（Test-time Strategies）：例如思维树（Tree of Thoughts, ToT）等方法，它们在模型进行推理时，通过外部程序强行引导模型生成多个分支。这类方法虽然有效，但会带来巨大的推理开销（Inference Overhead），使得应用成本高昂。 2. 训练时模仿（Training-based Approaches）：主要依赖监督微调（Supervised Fine-tuning, SFT），让模型去模仿预先生成好的、包含平行思考过程的数据。这种方法的瓶颈在于高质量的平行思考数据极难获取，尤其对于复杂问题。同时，模型仅仅是在“行为克隆”，并未真正内化探索和泛化的能力。\n正如论文在摘要中所指出的：“…existing methods predominantly rely on supervised fine-tuning (SFT) over synthetic data, which encourages teacher-forced imitation rather than exploration and generalization.” \u0026gt; （…现有方法主要依赖于合成数据上的监督微调（SFT），这鼓励了教师强制的模仿，而不是探索和泛化。）\n为了克服这些挑战，Parallel-R1框架巧妙地将渐进式课程学习（Progressive Curriculum）与强化学习（RL）相结合。这个框架的设计哲学是：不要试图一步到位让模型在难题上学会平行思考，而是分阶段、由易到难地引导它。\n第一步：解决“冷启动”问题 RL训练的一个前提是，模型需要能自发地产生一些值得奖励的行为，RL算法才能在此基础上进行优化。但一个从未见过平行思考的LLM，根本不知道如何生成\u0026lt;Parallel\u0026gt;、\u0026lt;Path\u0026gt;这样的结构化文本。这就是冷启动（Cold-start）问题。 论文的解决方案是，先在相对简单的数学任务（GSM8K）上，通过精心设计的提示（Prompt）让一个强大的“教师模型”生成一批包含平行思考路径的数据。然后，用这些数据对目标模型进行SFT。这一阶段的目的非常纯粹：只教会模型平行思考的“格式”，让它知道应该在何时、以何种结构（使用\u0026lt;Parallel\u0026gt;, \u0026lt;Path\u0026gt;, \u0026lt;Summary\u0026gt;等标签）来表达多条推理路径。\n第二步：通过RL进行探索和泛化 当模型掌握了基本格式后，就进入了RL阶段。这个阶段也分为两步： * 在简单任务上强化：继续使用GSM8K数据集，但这次用RL来训练。论文设计了一个混合奖励：R_final = R_parallel × R_acc，即只有当模型同时使用了平行思考结构并且答案正确时，才给予正奖励。这确保了模型不仅会用，而且会用对地方。 * 在困难任务上泛化：最后，将模型迁移到更难的数学数据集（如DAPO）上，此时的奖励函数简化为只看最终答案的准确性（Accuracy）。因为模型已经内化了平行思考的能力，现在RL的目标是让模型自由探索，将这项能力作为解决复杂问题的工具，以最大化最终的成功率。\n核心发现与贡献 论文通过大量的实验，得出了几个非常深刻的结论： 1. 框架的有效性：实验结果表明，Parallel-R1框架显著优于基线模型。例如，在多个数学基准测试中，其最佳变体Parallel-R1-Seen的平均分达到48.9，超过了标准的RL基线（45.1）。特别是在高难度的AIME25测试中，相比基线实现了42.9%的性能提升。\n平行思考行为的演化：这是论文最有趣的发现之一。通过分析训练过程，研究者发现模型使用平行思考的策略在悄然改变。 训练早期：模型倾向于在推理过程的早期使用平行思考，把它当作一种计算探索（Computational Exploration）的工具，来寻找可能的解题思路。 训练后期：随着模型自身推理能力的增强，它变得更加“自信”和“保守”，倾向于在推理的末尾才使用平行思考，将其作为一种多角度验证（Multi-perspective Verification）的手段，来确认自己初步得出的答案是否可靠。 上图（论文图3）清晰地展示了\u0026lt;Parallel\u0026gt;模块在生成文本中的相对位置随着训练步数的增加而稳步后移，直观地证明了这一策略演化。\n平行思考作为“训练中期的探索脚手架”：这是论文提出的一个极具价值的概念。研究者设计了一个实验，在训练的前200步强制模型高频率地使用平行思考（探索阶段），之后切换为只奖励准确率（利用阶段）。结果发现，即使在后期模型减少了平行思考的使用，其最终性能峰值（在AIME25上达到25.6%）也远高于从一开始就只用准确率奖励的基线模型。这表明，强制探索的经历帮助模型发现了一个更好的“策略空间”，即使之后不再强制，模型也能从中受益。这就像给初学者一个“脚手架”，帮助他们达到更高的高度，之后即便拆除脚手架，他们也已经站在了新的起点上。 总而言之，《Parallel-R1》不仅仅是提出了一个有效的训练方法，更重要的是，它深入剖析了LLM学习复杂推理策略的动态过程，为我们理解和引导模型的“思考”方式提供了宝贵的经验和全新的视角。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是，开发一个训练框架，使大语言模型（LLM）能够内在地、自适应地掌握平行思考（Parallel Thinking）的能力，而不是依赖高成本的外部推理时策略或效果有限的监督微调。\n想要解决的实际问题：\n提升复杂推理的鲁棒性：在数学、逻辑、规划等需要多步推理的任务中，LLM 常常因为“一步错，步步错”而失败。平行思考允许多路探索，大大增加了找到正确路径的概率，从而提高了模型的鲁棒性和问题解决能力。 降低高级推理能力的部署成本：像思维树（ToT）这样的方法，虽然能模拟平行思考，但其推理成本可能是标准方法的数倍甚至数十倍。如果能将这种能力“编译”进模型权重本身，就可以在保持高性能的同时，实现与标准模型相当的低成本推理。 对行业发展的重要意义： 这项研究对于推动LLM从一个“语言模型”向一个真正的“推理引擎”或“问题解决代理（Agent）**”转变具有重要意义。\n通往更强通用人工智能（AGI）的路径：人类在面对难题时，会本能地进行发散性思考和多角度论证。教会LLM平行思考，是使其认知架构更接近人类的重要一步。 解锁新应用场景：在科学发现、药物研发、代码优化、法律分析等领域，需要权衡多种可能性并找到最优解。具备平行思考能力的模型将成为这些领域强大的辅助工具。 提升模型的可解释性和可信度：通过让模型展示其考虑过的多种不同路径以及最终的选择依据（在\u0026lt;Summary\u0026gt;标签中），我们可以更好地理解其决策过程，从而提升对模型输出的信任度。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路与方法： 核心是“渐进式课程RL框架” (Progressive Curriculum RL Framework)。这个框架将复杂的学习任务分解为三个循序渐进的阶段，优雅地解决了直接用RL训练的“冷启动”难题。\n渐进式课程（Progressive Curriculum） 冷启动SFT阶段：在简单任务（GSM8K）上，通过SFT只教会模型平行思考的语法格式。 简单任务RL阶段：在简单任务上，用RL和混合奖励，强化模型在正确场景下使用该格式的能力。 困难任务RL阶段：在困难任务（DAPO）上，用RL和纯准确率奖励，让模型将平行思考能力泛化为一种通用的解题工具。 与之前方法的比较、特点和优势：\n相比SFT方法： 特点：Parallel-R1不依赖于为最终困难任务手动创建或生成高质量的平行思考数据。它通过RL让模型自己去探索。 优势：可扩展性强且能发现新知识。SFT方法上限受限于数据质量，模型只能模仿。而RL方法允许模型在探索中发现数据中从未见过的、更优的推理路径，潜力更大。 相比测试时（Test-time）方法： 特点：Parallel-R1是将能力内化（Instill）到模型参数中，而ToT等方法是外挂（External）的。 优势：推理效率高。一旦训练完成，模型在推理时可以自发地进行平行思考，无需复杂的外部控制器和多次模型调用，成本远低于测试时方法。 相比朴素的RL方法： 特点：引入了“冷启动SFT”和“渐进式课程”。 优势：解决了训练可行性问题。直接在复杂任务上用RL训练模型学习一种全新的行为范式是极其困难的，因为初始的随机探索很难产生任何有意义的平行思考轨迹。该框架通过先教格式，再教行为，最后促泛化，大大降低了学习难度，提高了训练的稳定性和效率。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来验证其方法的有效性，包括主实验和多项消融研究（Ablation Study）。\n实验设计：\n基座模型：选择了在同等规模下表现优异的开源模型 Qwen-3-4B-Base。 对比基线： Qwen3-4B-Base：原始基础模型。 SFT + Parallel：只进行冷启动SFT，不进行RL。 GRPO (DAPO)：标准的RL方法，直接在困难任务上训练，没有平行思考课程。 + RL on GSM8K：在标准RL之前，增加了一个在简单任务上的RL预热，但没有平行思考的引导。 评测基准：使用了四个高难度的数学推理基准测试集：AIME’25, AIME’24, AMC’23, 和 MATH。 关键实验数据和结果： \u0026gt; 主实验结果 (Table 2) \u0026gt; | Method | Avg. Score | \u0026gt; | :— | :—: | \u0026gt; | Qwen3-4B-Base | 6.6 | \u0026gt; | GRPO (DAPO) (标准RL基线) | 45.1 | \u0026gt; | Parallel-R1-Seen (本文方法) | 48.9 | \u0026gt; | Parallel-R1-Unseen (S2) | 46.8 |\n从上表可以看出，本文提出的Parallel-R1-Seen模型取得了最高的平均分 (48.9)，显著优于所有基线，证明了其框架的有效性。\n消融研究：奖励机制的重要性 (Table 4) 针对Parallel-R1-Unseen模型，论文比较了三种不同的奖励策略： | Reward Configuration | Parallel Ratio (%) | AIME 25 Perf. | | :— | :—: | :—: | | Accuracy-only | 13.6 | 17.7 | | Parallel-only | 80.3 | 17.7 | | Alternating Acc./Parallel (本文方案) | 63.0 | 19.0 |\n这个实验结果极具启发性：\n只奖励准确率，模型会“偷懒”，很少使用平行思考（只有13.6%）。 只奖励平行结构，模型会为了奖励而“形式主义”，过度使用平行思考（高达80.3%），反而损害了性能。 交替奖励策略则取得了最佳的平衡，既激发了模型进行平行思考，又取得了最高的性能。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 值得探索的问题和挑战： 跨领域泛化：当前研究集中在数学领域，因为答案有明确的对错。如何将平行思考能力泛化到开放域任务，如代码生成（探索不同算法实现）、创意写作（构思不同故事线）、科学假说提出等，是一个巨大的挑战。这些领域的奖励函数设计将更为复杂。 更深度的结构化思考：平行思考是第一步，未来可以探索更复杂的思考模式，例如递归思考（Recursive Thinking，即对思考过程本身进行反思）和类比推理（Analogical Reasoning），并研究如何通过训练内化这些能力。 模型架构创新：论文中结构化的Parallel-R1-Unseen版本效果不及非结构化的版本，这暗示当前的Transformer架构可能不是进行并行推理的最优选择。未来可以探索专门为并行计算设计的新型模型架构，例如包含多个并行计算“核心”的MoE（Mixture-of-Experts）变体。 动态资源分配：在平行思考时，并非所有路径都同等重要。如何让模型学会动态地为不同的推理路径分配计算资源，将更多的“算力”投入到更有希望的路径上，是一个值得研究的方向。 可能催生的新技术和投资机会： 新技术： 下一代AI Agent框架：能够原生支持并行规划和执行的AI代理。 专业领域“协处理器”模型：训练专门用于在特定领域（如法律、金融）进行多方案分析和风险评估的LLM。 自动化科学发现平台：利用具备平行思考能力的模型来大规模生成和验证科学假说。 投资机会： 初创公司：专注于开发内置高级推理能力（如平行思考）的、针对特定行业的垂直领域大模型。 AI基础设施：开发能够高效服务和优化这类并行推理模型的硬件和软件系统。 AI for Science：投资于利用这些先进模型加速基础科学研究和应用科学（如新材料、新药物）发现的公司。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性角度看，仍存在一些可以深入探讨的方面： * 模型规模的局限性：实验是在一个4B参数规模的模型上进行的。对于更大规模的顶尖模型（如GPT-4、Claude 3等），它们本身可能已经具备了一定的潜在平行推理能力。Parallel-R1框架在这些大模型上的效果如何，学习动态是否会发生变化，是一个未知数。 * “教师”模型的依赖：冷启动阶段的数据依赖于一个更强大的“教师模型”（DeepSeek-R1）。这意味着学习到的平行思考模式可能受限于教师模型的能力边界，难以“青出于蓝而胜于蓝”。 * 推理开销的讨论不足：虽然论文声称其方法比测试时方法更高效，但生成多条路径终究比生成单条路径的开销要大。论文中缺少对这种增量开销的定量分析，以及与性能提升之间的权衡（trade-off）讨论。 * 平行思考的“必要性”存疑：实验显示，在训练后期，模型会自发减少平行思考的使用频率。这引发一个问题：平行思考本身是一个最优的最终策略，还是仅仅是一个优秀的“助推器”？对于某些任务，是否一个推理能力极强的单线程模型就足够了？ * 结构化模型的潜力未被完全挖掘：Parallel-R1-Unseen（结构化版本）表现不佳，这可能不仅仅是架构和RL训练不匹配的问题，也可能是训练策略（如奖励设计）还不够精细。如何为带有强归纳偏置的结构化模型设计更有效的RL训练方法，本身就是一个开放性问题。\n### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文的价值远不止于“平行思考”本身。以下是几个我认为最关键的、可以迁移应用的启发点：\n核心启发一：将“复杂行为”的学习分解为“格式学习”和“能力学习” 学什么：Parallel-R1最精妙的设计就是渐进式课程。当你想教会模型一个全新的、复杂的行为时，不要指望一个RL算法能一步到位。 怎么用： 定义格式：首先，为你希望模型学习的行为定义一个清晰的、结构化的输出格式。 SFT教格式：用SFT的方式，先教会模型稳定地生成这种格式。这个阶段的目标不是追求正确性，而是追求形式上的合规。 RL教能力：在模型掌握格式后，再用RL去优化其内容的质量和应用的恰当性。 例子：如果你想训练一个会自动进行自我批判和修正的模型，可以先定义\u0026lt;Initial_Answer\u0026gt;, \u0026lt;Self_Critique\u0026gt;, \u0026lt;Revised_Answer\u0026gt;这样的标签，用SFT教会模型这个流程，然后再用RL奖励那些能有效提升最终答案质量的批判和修正。 核心启发二：把“过程”本身作为一种“临时训练脚手架” 学什么：论文中最深刻的洞见是“mid-training exploration scaffold”的概念。一个在训练中被强制执行的过程，其价值可能不在于这个过程本身，而在于它能引导模型探索到更好的参数空间。 怎么用：在你的训练流程中，可以设计一个探索阶段和一个利用阶段。在探索阶段，通过特殊的奖励或约束，强制模型使用某种你认为有益的特定行为（即使这会暂时牺牲一点性能）。在利用阶段，再切换回最终的目标奖励，让模型在探索到的新起点上自由优化。 例子：训练一个代码生成模型时，可以在前期强制要求模型生成包含详细注释和单元测试的代码（过程奖励），后期再转为只奖励代码执行的正确性（最终目标）。前期的强制过程可能会帮助模型学习到更结构化、更鲁棒的编程模式。 核心启发三：“交替奖励”是解决“过程”与“结果”矛盾的实用工具 学什么：在需要同时优化过程和结果时，如何设计奖励函数是一个难题。论文中“交替奖励准确率与平行结构”的策略是一个非常实用的解决方案。 怎么用：不要试图设计一个复杂的公式把两者结合，可以简单地在一个训练窗口内（比如每10步），80%的时间奖励最终结果，20%的时间奖励你想要的过程。这种方法简单、稳定，且易于调整。 需要补充的背景知识： 强化学习基础：特别是PPO (Proximal Policy Optimization)算法，它是目前LLM领域应用最广的RL算法。论文中使用的GRPO是其变体，但理解PPO是基础。 思维链 (Chain-of-Thought, CoT) 和 思维树 (Tree of Thoughts, ToT)：深入了解这些提示工程和推理时策略，能让你更好地理解Parallel-R1所要解决的问题的背景和意义。 RLHF (Reinforcement Learning from Human Feedback) / DPO (Direct Preference Optimization)：了解当前主流LLM对齐训练的基本范式，这有助于你将Parallel-R1中的思想与行业标准实践相结合。 希望这份详尽的解读能帮助您全面、深入地理解这篇论文，并从中获得有价值的启发。如果您还有任何问题，随时可以继续提问。\n","date":"2025-09-12T00:00:00Z","permalink":"/parallel-r1towards-parallel-thinking-via-reinforcement-learning/","title":"Parallel-R1：Towards Parallel Thinking via Reinforcement Learning"},{"content":"轨迹级优化，并且对于同一组，如果奖励方差太低，说明这个任务过难或者过简单，就直接丢弃，类似于DAPO的思想。并且还引入了value model给予更平滑的奖励。\n论文深度解读 大型语言模型（LLM）作为交互式智能体，其潜力远不止于单次的问答或文本生成。真正的智能体需要具备在复杂动态环境中进行长远规划、持续决策并与环境随机反馈互动的能力。强化学习（Reinforcement Learning, RL）被认为是实现这一目标的关键路径，因为它能让智能体通过“试错”来自我进化和提升。然而，将传统用于单轮任务（如解数学题）的RL方法直接应用于多轮交互的智能体时，往往会遭遇“滑铁卢”。这正是本篇论文的研究切入点。\n论文的核心贡献在于，它首次系统性地识别并命名了一个在多轮智能体RL训练中普遍存在的失败模式——“回声陷阱”（Echo Trap）。这个现象指的是，智能体在训练初期可能获得一些初步提升，但很快就会陷入一种自我强化的负反馈循环：模型开始过度拟合那些曾带来局部奖励的“思维模板”或行为模式。这导致其策略多样性急剧下降，探索能力丧失，最终性能崩溃。论文通过详尽的实验数据，生动地展示了这一过程：\nWe refer to this failure mode as an “Echo Trap”, which is similar to the findings in Shumailov et al. (2024), where the model repeatedly reuses memorized reasoning paths when trained on self-generated trajectories, leading to a collapse in diversity and long-term performance degradation.\n论文指出，“回声陷阱”伴随着几个清晰的早期预警信号：奖励标准差（Reward Standard Deviation）的下降、策略熵（Entropy）的骤降和梯度范数（Gradient Norm）的尖峰。这些指标就像是智能体训练过程中的“健康监护仪”，它们的异常波动预示着模型即将陷入不可逆的性能崩溃。\n为了解决这一核心难题，论文提出了一个通用的智能体RL框架——StarPO (State-Thinking-Actions-Reward Policy Optimization)。StarPO的精髓在于它将优化的基本单元从单个“动作”提升到了整个“轨迹”（Trajectory）。这意味着模型不再孤立地看待每一步决策，而是将包含观察、思考、行动和反馈的完整交互序列作为一个整体进行学习和优化。这更符合智能体进行长远规划的本质。\n然而，仅仅有StarPO框架是不够的。为了对抗“回声陷阱”，论文进一步提出了其稳定化变体——StarPO-S。这可以说是论文最具创新性的部分。StarPO-S引入了三大关键技术，其中最核心的是基于不确定性的轨迹过滤（Uncertainty-based Trajectory Filtering）。这个想法源于主动学习（Active Learning）的智慧：并非所有训练数据都是同等重要的。智能体最应该学习的，是那些它感到“困惑”或行为结果“不确定”的案例。具体来说，在每次训练迭代中，系统会根据多次 rollout（模拟运行）后奖励值的标准差来衡量智能体在某个任务上的不确定性。它会主动丢弃那些低方差的轨迹（即每次都轻松成功或稳定失败的“垃圾时间”），而将训练资源集中在那些高方差的、最具信息量的轨迹上。\n实验结果强有力地证明了这一设计的有效性。如论文中的 Figure 5 所示，在FrozenLake环境中，标准的PPO方法在100步左右就出现性能崩溃，而StarPO-S通过保留75%高不确定性轨迹，将稳定训练延长至140步；当过滤策略更激进，只保留50%的轨迹时，模型完全避免了崩溃。这清晰地表明，训练数据的“质”远比“量”更重要。\n此外，StarPO-S还结合了另外两种技术来增强稳定性：引入评判家（Critic）以提供更平滑的奖励信号，以及梯度整形（Gradient Shaping）技术（如移除KL散度惩罚和非对称裁剪）来鼓励模型更大胆地从高奖励轨迹中学习。\n论文的另一深刻洞见在于对“推理”（Reasoning）的探讨。许多工作期望通过“思想链”（Chain-of-Thought）等方式让智能体学会推理。但该论文发现，在缺乏精心设计的、能够奖励“优质思考过程”的奖励机制下，仅仅依赖稀疏的最终任务成功与否作为奖励，智能体的推理能力会随着训练而退化。模型会找到“捷径”，即便生成无意义或错误的思考过程，只要最终动作碰巧成功，就会被强化。\nWe find that simply encouraging reasoning in the action format does not guarantee reasoning behavior. … when rewards only reflect task success, models produce hallucinated reasoning, revealing a mismatch between thoughts and environment states.\n综上所述，RAGEN这篇论文不仅仅是提出了一个新算法，更重要的是，它为LLM智能体的强化学习研究提供了一套“诊断学”和“方法论”。它定义了核心问题（Echo Trap），找到了诊断指标（奖励标准差、熵），并给出了一套以“数据质量”为核心的、行之有效的稳定化训练方案（StarPO-S），为该领域未来的发展奠定了坚实的基础。\n### 1. 论文的研究目标与意义 研究目标：本文的核心研究目标是理解并解决在多轮、交互式环境中，使用强化学习训练大型语言模型智能体时出现的训练不稳定和性能崩溃问题。\n解决的实际问题：\n“回声陷阱”（Echo Trap）：智能体在训练中过度依赖并反复生成少数曾经成功的行为模式，导致策略僵化，无法适应新情况，最终性能退化。 从单轮RL到多轮RL的适配失灵：直接套用为单轮任务设计的RL算法（如PPO, GRPO）在多轮交互任务中常常会失败。 推理能力的退化：在稀疏奖励下，智能体的“思考”过程会变得越来越肤浅甚至虚假，因为它发现“思考”本身与最终奖励没有强关联。 行业发展意义： 当前，构建能够自主学习和进化的通用人工智能（AGI）是整个行业的终极梦想。LLM智能体被视为通往AGI的一条关键路径。然而，如果这些智能体无法通过与环境的真实互动来稳定地提升自己，那么它们的“智能”就只能停留在预训练知识的“复读”阶段。这篇论文直面了智能体“自我进化”中最核心的瓶颈——如何稳定地从经验中学习。解决了这个问题，才能让智能体真正走出实验室，在真实、复杂的动态世界中（如机器人控制、个性化教学、自动化科学实验等）发挥作用，其意义不言而喻。\n### 2. 论文提出的新思路与方法 新思路与方法： StarPO (State-Thinking-Actions-Reward Policy Optimization) 框架： 特点：它将优化的基本单元从“单步动作”提升为“完整轨迹”。模型学习的目标是最大化整个交互序列 τ = {s₀, a₀, r₀, ..., sₖ} 的累积奖励，而不是孤立地优化每个 aₜ。这使得学习过程更具全局性和前瞻性。 优势：相比单步优化，轨迹级优化更能捕捉到长时序依赖关系，对于需要多步规划的任务至关重要。 “回声陷阱”（Echo Trap）的诊断： 特点：首次清晰地定义并系统性地分析了这一多轮RL训练中的关键失败模式。 优势：为问题提供了明确的“诊断”，让后续的研究者可以有针对性地解决问题，而不是“盲人摸象”。 StarPO-S 稳定化训练方案：这是本文方法论的核心创新。 特点：它引入了三大关键组件，尤其是基于不确定性的轨迹过滤。该方法通过计算 rollout 奖励的标准差 Std_τ [R(τ)] 来评估不确定性，并只保留 top p% 的高不确定性轨迹用于训练。 优势： 提升训练稳定性：通过过滤掉“信息量低”的轨迹，避免了模型在简单或无解的样本上浪费计算资源和产生误导性梯度，从而有效规避了“回声陷阱”。 提升样本效率：将学习集中在最关键的样本上，使得模型能更快地学习到鲁棒的策略。这与传统RL中尽可能利用所有经验（如使用经验回放池）的思路形成了鲜明对比，强调了数据质量优先的原则。 与之前方法的对比： vs. 标准PPO/GRPO：传统方法通常将所有生成的轨迹不加区分地用于训练。这在多轮任务中，当模型开始产生低质量、同质化的数据时，会加速“回声陷阱”的形成。StarPO-S通过主动的数据筛选，打破了这一恶性循环。 vs. 传统主动学习：主动学习通常用于监督学习场景，从未标注数据池中挑选最有价值的样本进行人工标注。StarPO-S巧妙地将这一思想迁移到了强化学习的在线训练过程中，用“奖励的不确定性”作为“最有价值”的衡量标准，实现了自动化的“样本选择”。 ### 3. 实验验证与设计 论文通过在四个精心设计的环境中进行的大量实验，系统地验证了其发现和方法的有效性。\n实验设计： 环境选择：实验覆盖了从简单到复杂的四种环境，各有侧重： Bandit：单轮、随机性任务，测试风险敏感推理。 Sokoban (推箱子)：多轮、确定性任务，需要不可逆的符号规划。 Frozen Lake (冰湖)：多轮、随机性任务，结合了规划与概率转移。 WebShop：多轮、开放域任务，涉及自然语言理解和真实世界交互。 这样的设计使得实验结论更具普遍性，能够覆盖智能体所需的不同核心能力。 基线对比：论文将标准的 StarPO (使用 PPO 或 GRPO 优化器) 与其改进版本 StarPO-S 进行了直接对比。 评估指标：除了任务成功率，还监控了一系列过程指标来诊断训练状态，包括奖励标准差、策略熵、梯度范数和响应长度等。 关键数据和结果： “回声陷阱”的验证：Figure 3 展示了在 Bandit 和 Sokoban 任务中，基线 StarPO 方法虽然初期性能有所提升，但随后便急剧下降，出现了明显的性能崩溃，验证了“回声陷阱”的存在。\nStarPO-S 的优越性：Figure 6 直观地对比了 StarPO 和 StarPO-S 在所有四个环境中的表现。结果显示，StarPO-S (橙线) 显著优于基线 StarPO (蓝线)，它不仅延缓或完全避免了性能崩溃，而且达到了更高的最终成功率。\n图注：StarPO-S在所有四个任务中都改善了稳定性和最终性能。\n轨迹过滤的有效性：Figure 5 是一个关键的消融实验，展示了不同过滤比例 p% 的影响。在 Sokoban (PPO) 任务中，不过滤（All Rollouts）时模型在约110步崩溃，而保留50% (Keep 50%) 和 25% (Keep 25%) 的轨迹则能维持稳定并取得更高的性能。这直接证明了轨迹过滤是StarPO-S成功的核心要素。\n推理能力的衰退：Table 4 的数据显示，在 Bandit 和 Sokoban 任务中，随着训练的进行（从 Step 0 到 Step 200），\u0026lt;think\u0026gt; 块的平均长度显著下降。例如，在Sokoban中，从307.1个token减少到89.5个。这量化了在稀疏奖励下推理能力退化的现象。\n### 4. 未来探索方向与机遇 结合当前大模型领域的理解，这篇论文为未来的研究开辟了多个激动人心的方向：\n值得探索的问题与挑战： 精细化的奖励设计 (Meticulous Reward Design)：如论文所指，如何设计能够评估并奖励“优质思考过程”而非仅仅是“正确结果”的奖励模型，是让智能体学会真正推理的关键。这可能需要过程奖励模型 (Process Reward Models, PRM) 的进一步发展。 长时序记忆与信用分配：当前实验的任务 horizon 相对较短。对于需要跨越数千步甚至更长时间进行决策的复杂任务，如何维持记忆、进行有效的信用分配（Credit Assignment）是一个巨大挑战。 探索与利用的平衡 (Exploration-Exploitation)：StarPO-S通过关注不确定性来驱动探索，但更高级的探索策略（如内在好奇心、技能发现等）仍有巨大探索空间。 多模态智能体训练：如何将本文的发现扩展到需要理解和生成多模态信息（如视觉、听觉）的具身智能体（Embodied AI）中，是一个重要方向。 新的技术和投资机会： 智能体训练平台即服务 (Agent Training Platform as a Service)：提供一套集成了如 StarPO-S、高级奖励模型、多样化仿真环境和诊断工具链于一体的云服务，将成为AI领域的基础设施。 自动化奖励工程 (Automated Reward Engineering)：开发能够根据任务描述或少量人类反馈自动生成复杂奖励函数的技术，将极大降低智能体开发的门槛。 垂直领域的基础智能体模型 (Foundation Agent Models)：在特定领域（如游戏、软件测试、科学研究）通过大规模RL训练出的专用智能体模型，可能会成为新的商业模式。 AI安全与对齐：如何确保通过RL自我进化的智能体其行为符合人类价值观？对训练过程的稳定性和可解释性的研究（如本文）是AI对齐领域的重要组成部分。 ### 5. 论文的不足与缺失 从批判的视角来看，这篇论文虽然贡献卓著，但仍存在一些值得商榷和进一步验证之处：\n模型规模的局限性：实验主要基于相对较小的模型（0.5B, 3B）。这些发现（尤其是“回声陷阱”）是否同样适用于更大规模的前沿模型（如 GPT-4, Llama-3 等）？大模型可能由于其更强的先验知识和泛化能力，对训练不稳定的抵抗力更强。 省略了经验回放池 (Replay Buffer)：论文明确提到“omission of established RL practices like replay buffers”。经验回放是现代深度强化学习中提高样本效率和稳定性的关键技术。论文采用纯在线（On-policy）的训练方式，其必要性和与离线（Off-policy）方法的详细对比分析有所欠缺。 SFT vs. RL 的现实差距：论文附录G中的数据显示，在Sokoban任务上，使用专家数据进行监督微调（SFT）的性能（74.6%）远超其最好的RL方法StarPO-S（20.3%）。这引出了一个关键问题：在有专家数据可用的情况下，从零开始的RL自我进化在实践中的性价比如何？RL的真正优势可能在于超越人类水平或在无专家知识的领域。 “推理”的定义过于简单：模型生成的 \u0026lt;think\u0026gt; 内容与最终的 \u0026lt;answer\u0026gt; 之间没有强制的逻辑联系。奖励信号主要来自于环境对 \u0026lt;answer\u0026gt; 的反馈。因此，模型可能学会了生成看似合理的“思考”，但实际上决策逻辑完全在别处。如何确保推理过程的忠实性（Faithfulness）是一个开放性问题。 ### 6. 核心启发与知识补充 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的“拿来即用”的启示：\n重点学习与启发： 数据质量优先于数量的理念：在任何涉及模型自我生成数据的循环中（无论是RL、课程学习还是合成数据生成），都应该警惕“回声陷阱”。核心启发是：与其盲目地使用所有生成的数据，不如设计一套机制来主动筛选出最高质量、最具信息量的样本。 这个思想可以广泛应用于各种AI训练场景。 建立“健康监控”体系：在训练智能体或任何自回归模型时，引入并监控类似奖励标准差、策略熵、梯度范数等过程指标。这些指标可以作为自动化脚本的触发器，用于早期发现问题、调整超参数或自动回滚。 从“点”优化到“线”优化：在设计学习目标时，思考是否能将优化单元从孤立的“点”（单次预测）提升到“线”（完整序列或轨迹）。这种轨迹级（Trajectory-level）的视角在很多需要连贯性的任务中（如代码生成、故事创作、对话系统）都可能带来提升。 将不确定性视为宝贵资源：模型表现出不确定性的地方，恰恰是其知识边界和学习潜力最大的地方。在设计学习策略时，可以主动引导模型去探索和学习这些不确定性高的区域。 需要补充的背景知识： 强化学习基础 (Reinforcement Learning)：理解核心概念，如策略（Policy）、奖励（Reward）、轨迹（Trajectory）、价值函数（Value Function）、On-policy 与 Off-policy 的区别。 近端策略优化 (Proximal Policy Optimization, PPO)：这是现代LLM强化学习中最常用的算法之一，理解其目标函数、裁剪（Clipping）机制和优势函数（Advantage Function）至关重要。 主动学习 (Active Learning)：了解其基本思想，即如何在一个数据池中选择最能提升模型性能的样本进行学习。 思想链 (Chain-of-Thought, CoT)：了解LLM中用于激发推理能力的主流提示技术，这有助于理解本文中对“推理”的讨论背景。 希望这份详尽的解读能够帮助您深入理解这篇论文的精髓，并从中获得有益的启发。\n","date":"2025-09-12T00:00:00Z","permalink":"/ragenunderstanding-self-evolution-in-llm-agents-via-multi-turn-reinforcement-learning/","title":"RAGEN：Understanding Self-Evolution in LLM Agents via Multi-Turn Reinforcement Learning"},{"content":"利用知识图谱存储记忆，并且随着时间的推进来调整记忆。在边上设置时间戳，来识别旧记忆和新记忆，当记忆冲突时标记而不是删除。\n论文深度解读：从静态检索到动态记忆的进化 这篇论文（Rasmussen, P. et al., 2025, ZEP: A TEMPORAL KNOWLEDGE GRAPH ARCHITECTURE FOR AGENT MEMORY, https://arxiv.org/abs/2501.13956）的核心贡献在于，它提出了一个名为Zep的全新AI代理记忆层服务，旨在解决现有基于检索增强生成（RAG, Retrieval-Augmented Generation） 的记忆系统在处理动态、时变信息时的根本性缺陷。当前主流的RAG技术，虽然极大地扩展了LLM的知识边界，但其本质上是为静态文档库设计的。它们擅长从不变或很少变化的海量文本中检索信息，但在需要代理（Agent）像人一样，从连续的对话、不断变化的业务数据和 evolving 的世界知识中学习和记忆时，就显得力不从心。这篇论文敏锐地抓住了这个从“静态知识库”到“动态个人记忆”的转变，并给出了一个强大而优雅的解决方案。\nZep的核心是其内部的Graphiti引擎，一个时序感知知识图谱（temporally-aware knowledge graph）。这不仅仅是一个简单的知识图谱，它的设计精妙之处在于模拟了人类记忆的两个关键方面：情景记忆（episodic memory） 和语义记忆（semantic memory）。\nThe dual storage of both raw episodic data and derived semantic entity information mirrors psychological models of human memory. These models distinguish between episodic memory, which represents distinct events, and semantic memory, which captures associations between concepts and their meanings.\n论文中，Zep通过一个三层级的图结构来实现这一点： 1. 情节子图（Episode Subgraph）：这是最底层，负责无损地存储原始数据单元，如对话消息。这就像我们大脑中的原始、未加工的事件片段。 2. 语义实体子图（Semantic Entity Subgraph）：在情节子图之上，系统会提取出关键的实体（Entities）（如人、地点、概念）和它们之间的关系（Facts），构建成一个结构化的语义网络。这相当于我们从经历中提炼出的知识和理解。 3. 社区子图（Community Subgraph）：这是最高层，通过社区发现算法将强相关的实体聚类，形成更高层次的抽象概念总结。这类似于我们大脑中形成的关于某个主题的宏观认知。\n然而，这篇论文最具创新性的地方在于其对时间维度的处理。传统的知识图谱往往是静态的，一个“事实”一旦存入，就默认永久有效。但现实世界的信息是动态的，例如一个人的工作单位、居住地、甚至观点都会随时间改变。Graphiti引入了双时态模型（bi-temporal model），为图中的每一条“边”（即事实）都赋予了时间戳，精确记录一个事实的有效时间范围（t_valid, t_invalid）。当新的信息与旧信息产生矛盾时，系统能智能地将旧事实标记为“失效”，而不是粗暴地删除。\nConsistent with our bi-temporal modeling approach, the system tracks four timestamps: t’created and t’expired ∈ Τ’ monitor when facts are created or invalidated in the system, while tvalid and tinvalid ∈T track the temporal range during which facts held true.\n这种设计使得Zep能够构建一个动态演化的记忆系统，准确地理解“小明上周喜欢喝咖啡，但这周他改喝茶了”这类包含时间变化和信息更新的复杂情境。\n在实验验证部分，论文的策略也非常聪明。它没有满足于在传统的、相对简单的DMR（Deep Memory Retrieval） 基准上取得超越MemGPT的微弱优势（在gpt-4-turbo上Zep为94.8% vs MemGPT为93.4%），而是更进一步，在一个更具挑战性、更能反映真实世界复杂度的LongMemEval基准上进行测试。在这个基准上，对话平均长度高达11.5万个token，对记忆系统的要求极高。\n实验结果令人印象深刻。在LongMemEval上，Zep相比于将全部对话历史作为上下文的“Full-context”基准方法，取得了显著的性能提升。以GPT-4o模型为例，Zep的准确率从60.2%提升到了71.2%，实现了18.5%的巨大改进。更关键的是，它将LLM的平均上下文token数从115k急剧减少到1.6k，同时将响应延迟降低了约90%（从28.9秒缩短至2.58秒）。这不仅仅是学术上的数字提升，更是工程实践上的巨大飞跃，意味着更低的成本、更快的响应和更高的效率，为复杂AI代理的商业化落地扫清了关键障碍。特别是在需要时序推理（temporal-reasoning） 和多会话（multi-session） 记忆的任务上，Zep的优势尤为明显，这直接证明了其时序知识图谱设计的有效性。\n总而言之，这篇论文不仅仅是提出了一个新模型，更是为构建下一代AI代理的长期记忆系统指明了一个清晰的方向：记忆不应是无结构的文本块，而应是结构化的、动态演化的、并且深刻理解时间概念的知识图谱。Zep通过其精巧的架构和令人信服的实验数据，证明了这条道路的可行性和巨大潜力。\n接下来，我将按照您提出的六个问题，对这篇论文进行更详细的解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：这篇论文的核心研究目标是设计并实现一个高效、可扩展且能处理动态信息的长期记忆层（long-term memory layer），专为基于大语言模型的AI代理（LLM Agents）服务。\n解决的实际问题：\n静态RAG的局限性：传统的RAG系统主要面向静态或低频更新的文档语料库。然而，现实世界中的AI代理（如私人助理、企业客服）需要处理的是持续演进的数据流，包括用户的多轮对话历史、不断更新的业务数据库、动态变化的个人偏好等。现有RAG无法有效处理这种动态性。 \u0026gt; Current approaches using RAG have focused on broad domain knowledge and largely static corpora… For agents to become pervasive in our daily lives, autonomously solving problems from trivial to highly complex, they will need access to a large corpus of continuously evolving data from users’ interactions with the agent… LLM上下文窗口的限制：尽管LLM的上下文窗口越来越大，但将全部历史信息（如长达数月的对话记录）塞入上下文窗口，不仅成本高昂、延迟巨大，而且在信息过载时LLM的性能会下降（即“大海捞针”问题）。 信息的时效性与矛盾处理：代理需要理解信息的时效性。例如，用户今天提供的地址更新了上周的地址。简单的记忆系统无法处理这种信息的覆盖和失效，容易导致代理使用过时的信息作出错误判断。 行业发展意义：\n推动AI代理的实用化：一个真正有用的AI代理必须具备可靠的长期记忆，能够记住用户的偏好、历史互动和关键信息。Zep这样的动态记忆系统是实现从“玩具式”聊天机器人到“生产力工具”级AI代理的关键基础设施。 赋能企业级应用：在企业场景中（如CRM、智能客服），数据是高度动态的。一个能够整合对话信息和结构化业务数据，并理解其时间演变的记忆系统，可以极大地提升自动化服务的质量和个性化水平。 降低成本和延迟：通过智能地从庞大的记忆库中检索出最相关的、最即时的上下文（如Zep将115k token压缩到1.6k），可以大幅降低调用昂贵LLM API的成本，并提高系统响应速度，这对于大规模商业部署至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 这篇论文的核心创新是一个名为Zep的记忆架构，其内部由Graphiti知识图谱引擎驱动。其新颖之处和优势体现在以下几个方面：\n思路创新：从“记忆即文本”到“记忆即时序图谱” 之前的方法：大多数记忆系统（如MemGPT的早期版本或简单的RAG）将记忆视为一个文本片段的集合，通过向量相似度进行检索。这是一种扁平化、无结构、无时间维度的记忆模式。 Zep的思路：Zep认为记忆应该是一个结构化且时序化的知识网络。它通过构建知识图谱来连接实体和事实，更重要的是，它为这些事实打上了时间戳，记录其生命周期。 方法与模型创新： 三层级图谱架构（Episode -\u0026gt; Semantic -\u0026gt; Community）： 这种分层结构兼顾了保真度和抽象度。底层的Episode Subgraph确保了原始信息的无损存储，可用于溯源和引用。中间的Semantic Entity Subgraph提炼了结构化知识，便于推理。顶层的Community Subgraph提供了宏观视角，有助于快速定位相关领域。这比单一的向量数据库或简单的知识图谱结构更为精细和强大。 双时态模型（Bi-temporal Model）：这是Zep最核心的亮点。 特点：它不仅记录了事实在真实世界中何时为真（t_valid 到 t_invalid），还记录了该事实在数据库中何时被创建或失效（t'_created, t'_expired）。 优势：这种设计能够优雅地处理信息更新和矛盾。当一个新事实（例如，“我现在住在纽约”）与旧事实（“我住在波士顿”）冲突时，系统不是删除旧事实，而是将旧事实的t_invalid设置为新事实的t_valid。这保留了完整的历史演变轨迹，使得代理不仅知道“现在是什么”，还知道“过去是什么”，为复杂的时序推理提供了可能。 混合检索策略（Hybrid Retrieval）： 特点：Zep的检索过程分为Search -\u0026gt; Reranker -\u0026gt; Constructor三步。其中Search阶段融合了三种不同的搜索技术： 余弦相似度搜索（φ_cos）：捕捉语义上的相似性。 BM25全文搜索（φ_bm25）：捕捉关键词的匹配。 广度优先搜索（φ_bfs）：在图谱上扩展搜索，发现上下文相关的实体和事实。 优势：这种多方面的方法（论文称之为multi-faceted approach）最大化了召回相关信息的可能性，远比单一的向量搜索要鲁棒。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了两组实验来全面验证Zep的有效性，从简单场景到复杂场景层层递进。\n实验一：Deep Memory Retrieval (DMR) 基准 设计：该基准来自MemGPT论文，包含500个多会话对话，但每个对话总共只有约60条消息，规模较小，主要测试基本的事实检索能力。 数据与结果： Zep在使用gpt-4-turbo时，取得了94.8%的准确率，略高于MemGPT的93.4%和全对话上下文基线的94.4%。 论文在这里展现了批判性思维，指出这个基准的局限性：由于对话太短，以至于简单的“全上下文”方法就能取得极高的分数，这使得该基准在评估复杂记忆系统时区分度不足。 \u0026gt; However, these results must be contextualized: each conversation contains only 60 messages, easily fitting within current LLM context windows. 实验二：LongMemEval (LME) 基准 设计：这是一个更具挑战性的基准，对话平均长度高达115,000个token，问题类型也更复杂，包括单会话偏好、多会话信息综合、知识更新和时序推理等，更能模拟真实的企业级应用场景。\n数据与结果：这是展示Zep核心优势的实验。 | Metric | Model | Full-context Baseline | Zep | Improvement | | :— | :— | :— | :— | :— | | Accuracy Score | gpt-4o | 60.2% | 71.2% | +18.5% | | Avg. Latency | gpt-4o | 28.9 s | 2.58 s | ~90% reduction | | Avg. Context Tokens| gpt-4o | 115k | 1.6k | ~98.6% reduction |\n关键数据解读： 准确率大幅提升：特别是在gpt-4o上，Zep带来了18.5%的绝对准确率提升，证明了其检索到的上下文远比原始的全部历史更有效。 效率的巨大飞跃：将延迟降低90%，上下文长度减少98.6%，这意味着在实际应用中成本更低、速度更快，这对于商业化是决定性的。 复杂任务优势：在Table 3中，Zep在时序推理（temporal-reasoning） 任务上的提升尤为显著（使用gpt-4o时提升了38.4%），这直接验证了其双时态模型设计的成功。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为我们打开了一扇门，门后还有广阔的研究空间。\n值得探索的问题与挑战： 与形式化本体论（Formal Ontologies）的结合： 目前Zep的知识图谱 schema（模式）是自发形成的。未来可以引入特定领域的本体论（例如，在医疗领域引入SNOMED CT本体），来规范化图谱的结构，增强推理的准确性和一致性。 微调专用的小模型进行图谱构建： 论文中图谱的构建（实体、关系提取）依赖于通用的LLM（如GPT-4o-mini），这在成本和延迟上仍有优化空间。未来可以微调专门用于知识提取的小型模型（如论文引用的Triplex），实现更高效、更低成本的图谱构建流程。 更智能的上下文构建（Constructor）： 如何将检索到的图谱信息（节点、边、时间属性）“翻译”成LLM最容易理解的自然语言上下文，这是一个值得深入研究的问题。不同的任务可能需要不同格式的上下文。 处理更复杂的非结构化数据： 目前Zep主要处理文本（对话、JSON）。未来记忆系统需要能够处理和理解多模态信息，如图像、音频中的实体和关系，并将其融入时序知识图谱。 主动记忆与遗忘机制： 当前的记忆系统是被动记录。未来的代理或许应具备主动记忆（判断哪些信息重要并深入处理）和主动遗忘（清理无关或过期的冗余信息）的能力，以防止记忆库无限膨胀。 可能催生的新技术和投资机会： 垂直领域记忆解决方案：为金融、法律、医疗、CRM等特定行业打造预置了领域本体论的“记忆大脑”，这将是巨大的商业机会。 记忆即服务（Memory-as-a-Service）：提供标准化的API，让任何开发者都能轻松地为自己的AI应用集成强大的长期记忆能力，Zep本身就是这个方向的尝试。 新一代AI评估基准：开发更能反映真实世界动态性、多模态性和复杂推理需求的AI代理记忆评估基准，这将成为行业新的“黄金标准”。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些方面值得商榷或需要进一步阐明。\n工程实现的复杂性与成本： 论文重点阐述了架构的优势，但对其构建和维护的成本与复杂性讨论不足。构建时序知识图谱需要大量的LLM调用来进行实体提取、关系抽取、实体链接和时间解析，这本身就是一笔不小的计算开销。系统在超大规模（例如千万级用户）下的可扩展性和维护成本仍需验证。 部分任务性能下降： 在Table 3中，一个有趣且值得关注的现象是，在single-session-assistant 这个任务类型上，Zep的性能相比Full-context基线实际上是下降的（gpt-4o下降了17.7%）。论文也承认了这一点。 \u0026gt; The decrease in performance for single-session-assistant questions…represents a notable exception to Zep’s otherwise consistent improvements, and suggest further research and engineering work is needed. 这可能意味着，对于那些答案就在“嘴边”（即非常近的上下文）的简单问题，复杂的检索过程反而可能引入噪声或遗漏关键信息，不如直接提供完整的近期对话历史有效。这揭示了Zep系统在情景切换（何时用图谱，何时用原文）上可能还有优化空间。 基准的局限性： 尽管LongMemEval比DMR要好得多，但它仍然是基于文本对话的。论文提到Zep能够处理结构化业务数据，但没有任何实验来验证其在融合对话历史和外部结构化数据（如数据库表格）进行联合推理时的表现。这是论文的一个明显缺失。 可复现性问题： Zep是一个商业化产品。虽然论文承诺会公开实验notebooks，但其核心的Graphiti引擎是闭源的。这在一定程度上限制了学术界对其进行完全的复现和深入的独立研究。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了极其丰富的灵感。\n重点学习的核心思想： 超越向量数据库思考RAG：不要将RAG局限于“文本分块+向量检索”。真正的记忆需要结构。知识图谱是组织和连接信息的强大工具，是RAG 2.0的核心方向。 时间是记忆的灵魂：在您的项目中思考时间维度。信息不是永恒的，它有自己的生命周期。为您的数据引入valid_from, valid_to这样的时态概念，可以解决大量关于信息更新和冲突的难题。这是可以直接借鉴的创新点。 分层抽象的记忆架构：学习Zep的三层图谱思想。在设计系统时，考虑如何同时保存原始数据（用于溯源）、结构化知识（用于推理）和高层摘要（用于快速导航）。 混合检索的力量：不要迷信单一的检索方法。将语义搜索、关键词搜索、图遍历等多种技术结合起来，通常能获得远超单一方法的效果。 启发： 应用启发：您可以思考如何将这种时序知识图谱记忆应用到您的具体业务中。例如，在电商推荐中，构建一个用户的时序兴趣图谱，可以更准确地捕捉其兴趣的动态变化。在智能客服中，构建客户问题的演变图谱，可以预测其下一步可能遇到的问题。 技术启发：即使不构建一个完整的Zep系统，您也可以从中“借用”一些组件。例如，在现有的RAG流程中，增加一个步骤，用LLM从对话中提取关键“事实”，并为其附加时间戳，存储在一个简单的表格或JSON文档中。在检索时，优先检索时间上最新的、有效的事实。这是一个轻量级的实现。 需要补充的背景知识： 知识图谱（Knowledge Graphs）：深入了解其基本概念（节点、边、属性），以及图数据库（如Neo4j）和查询语言（如Cypher）。 RAG技术栈：熟悉当前主流的RAG框架（如LangChain, LlamaIndex）及其工作原理。 信息检索（Information Retrieval）：了解经典的检索算法如BM25和向量检索的区别与联系。 时态数据库（Temporal Databases）：了解数据库领域中处理时间数据的理论和技术，这能帮助您更深刻地理解Zep的双时态模型。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-09-12T00:00:00Z","permalink":"/zepa-temporal-knowledge-graph-architecture-for-agent-memory/","title":"ZEP：A TEMPORAL KNOWLEDGE GRAPH ARCHITECTURE FOR AGENT MEMORY"},{"content":"逆向工程，根据问题和答案反向构建思维链。用困惑度筛选思维链\nimage.png 论文深度解读 这篇论文的核心洞察在于，它为如何“教”会大模型进行深度思考，尤其是在那些没有标准答案的创意领域，开辟了一条全新的道路。传统上，提升模型的推理能力主要依赖两条路径：一是强化学习 (Reinforcement Learning, RL)，二是指令蒸馏 (Instruction Distillation)。然而，这两条路在创意生成领域都走得异常艰难。\n强化学习的困境：RL 依赖于明确的“奖励信号”。在数学题中，答案正确就是奖励；在代码生成中，程序能跑通就是奖励。但在创意写作中，一篇小说的好坏是主观的，充满了对原创性、情感共鸣和叙事连贯性的复杂评判。为这种任务设计一个高质量的奖励模型本身就是一个巨大的挑战，且 RL 过程通常样本效率低下，计算成本高昂。 指令蒸馏的局限：这条路径依赖一个更强大的“教师模型”（如 GPT-4）来生成带有思考过程的范例，然后让“学生模型”去学习。这种方法的瓶颈显而易见：首先，大规模调用顶级模型的成本极高；其次，学生模型的能力上限被教师模型牢牢锁死，无法超越老师。 面对这一僵局，论文作者提出了一个颠覆性的想法：与其“正向”地通过试错 (RL) 或模仿 (蒸馏) 来构建推理过程，为什么不“反向”地从一个已知的、高质量的输出来推导出它背后可能存在的、合乎逻辑的思考过程呢？这就是 REER 范式的精髓。\nWe introduce REverse-Engineered Reasoning (REER), a new paradigm that fundamentally shifts the approach. Instead of building a reasoning process “forwards” through trial-and-error or imitation, REER works “backwards” from known good solutions to computationally discover the latent, step-by-step deep reasoning process that could have produced them.\n这个“反向工程”是如何实现的呢？论文将其巧妙地形式化为一个基于搜索的优化问题。具体来说，给定一个输入（比如一个写作提示 x）和一个高质量的参考答案（比如一篇优秀的人类创作的故事 y），目标是找到一个最佳的思考轨迹 (reasoning trajectory) z。\n那么，如何评判一个思考轨迹 z 的好坏呢？这里论文引入了一个非常关键且聪明的代理指标 (proxy)：困惑度 (Perplexity, PPL)。在自然语言处理中，困惑度可以通俗地理解为模型对于一个句子“有多意外”。一个句子的困惑度越低，说明模型认为这个句子出现的概率越高，越符合模型的“预期”。\n论文的核心假设是：一个好的思考过程 z，应该能让一个高质量的答案 y 看起来是顺理成章、水到渠成的结果。换言之，在给定输入 x 和思考过程 z 的条件下，高质量答案 y 的困惑度 PPL(y|x, z) 应该尽可能低。\n有了这个优化目标，整个过程就变成了一个算法问题： 1. 初始化：从网上搜集大量高质量的“提示-答案”对 (x, y)。然后让一个 LLM 对其生成一个初步的、不完美的思考轨迹 z(0)。 2. 迭代优化：这是一个精巧的局部搜索 (Local Search) 过程。算法会逐段地（segment-wise）优化思考轨迹 z。在每一步，它会选取轨迹中的一小段，让 LLM 生成多个可能的“重写”版本。 3. 评估与选择：对于每个重写后的候选轨迹 z_cand，算法会计算 PPL(y|x, z_cand)。那个能使困惑度最低的候选版本，就会被采纳为当前步骤的最佳优化结果。 4. 终止：重复上述过程，直到困惑度降到一个预设的阈值以下，或者达到最大迭代次数。最终得到的 z* 就是被“反向工程”出来的、高质量的思考轨迹。\n通过这个可扩展、自动化的流程，研究团队构建了一个名为 DeepWriting-20K 的大规模数据集，包含了2万个针对开放式任务的深度推理轨迹。随后，他们在一个 80亿参数的 Qwen3-8B 基础模型上，使用这个数据集进行微调，得到了最终的模型 DeepWriter-8B。\n实验结果令人印象深刻。DeepWriter-8B 不仅在多个基准测试中远超同等规模的开源模型，甚至在某些任务上表现出与 GPT-4o 和 Claude 3.5 等顶级专有模型相媲美、甚至超越的性能。例如，在考验专业写作能力的 WritingBench 测试中，DeepWriter-8B 的平均得分比强大的开源基线 LongWriter-8B 高出 18个点。更惊人的是，在超长文本生成任务 LongBench-Write 上，它的得分（91.28）甚至超过了 GPT-4o（83.1）和 Claude 3.5（89.3）。\n这充分证明，REER 范式是极其有效的。它不依赖昂贵的模型蒸馏或复杂的强化学习，而是通过一种创新的数据生成方法，成功地将深度、类人的思考模式“注入”到了一个中等规模的模型中，实现了性能的巨大飞跃。这不仅是一次技术的成功，更是对“如何构建更智能的AI”这一根本问题的一次深刻回应。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n1. 研究目标与行业意义 研究目标：本文的核心研究目标是，为大语言模型在开放式、非可验证的生成任务（如创意写作、学术写作、商业文案等）中注入深度推理和规划能力。\n解决的实际问题：当前的大模型虽然在知识问答、数学推理等有明确答案的领域取得了巨大成功，但在创意写作等主观性强、无标准答案的领域，其表现往往流于表面，缺乏深思熟虑的规划和结构。它们通常是“想到哪写到哪”，难以构建复杂的情节、深入的人物弧光或严谨的论证结构。这限制了 LLM 从一个“信息检索工具”转变为一个真正的“创意伙伴”或“写作助手”。\n行业重要意义：\n解锁 AI 应用新场景：解决了这个问题，意味着 AI 可以在内容创作、市场营销、法律文书、科研辅助等更多依赖深度思考和规划的领域发挥核心作用，极大地拓展了 AI 的商业价值。 推动模型能力“内化”：目前的很多推理能力依赖于复杂的提示工程（如 CoT, ToT），模型本身并未完全“学会”思考。本文的方法旨在将这种思考能力通过微调“内化”到模型参数中，使得模型在面对相应任务时能自发地进行深度规划。 降低高性能模型的门槛：通过 REER 这种高效的数据生成方法，可以在中等规模的开源模型（如8B）上实现以往只有超大规模专有模型才具备的能力。这有助于实现先进 AI 技术的“民主化”，让更多中小企业和研究者能够部署和使用高性能的生成模型，从而推动整个行业的创新。 2. 新思路与方法优势 新思路/方法： REER 范式 (REverse-Engineered Reasoning)：这是本文最核心的创新。它颠覆了传统的“正向”构建推理过程的思路，提出从已知的高质量结果“反向”推导其背后的思考过程。\n以困惑度为代理的优化目标：巧妙地将“思考质量”这一模糊概念，量化为“对已知优质答案的困惑度 PPL(y|x, z)”这一可计算、可优化的指标。这是一个非常优雅的解决方案。\n迭代式局部搜索算法 (Iterative Refinement via Local Search)：设计了一套具体的、可执行的算法流程，通过逐段优化和评估，逐步将一个粗糙的思考计划打磨成一个详尽、连贯的高质量版本。如下图所示，这个过程从一个初始的思考步骤开始，通过节点扩展（生成多个候选优化方案）和节点评估（选择PPL最低的方案），最终得到一个精炼的思考过程。\n图3：深度思考合成的迭代式局部搜索过程\n与之前方法的对比与优势： 相较于强化学习 (RL)： 优势：REER 无需设计复杂的、主观的奖励模型，也避免了 RL 训练过程的不稳定和高样本需求。它的优化信号（困惑度）是内生的、自洽的，过程更稳定可控。 相较于指令蒸馏 (Distillation)： 优势：REER 摆脱了对昂贵且能力受限的“教师模型”的依赖。它可以从任何来源（包括人类创作）的高质量文本中学习，理论上可以达到甚至超越任何现有模型的思考水平。这使得数据生成成本更低，且质量天花板更高。 相较于传统合成数据方法： 优势：传统方法通常是“正向”生成，即给定一个提示 x，让模型直接生成答案 y。这种方法很难保证生成答案的质量和深度。而 REER 从一个已确认的高质量答案 y 出发，保证了学习目标的“含金量”，专注于恢复其高质量的“过程”。 3. 实验验证与关键数据 论文通过一系列全面的实验来验证其方法的有效性。\n实验设计：\n主实验：将微调后的 DeepWriter-8B 与多个顶级的专有模型（GPT-4o, Claude 3.5, Claude 3.7）和强大的开源基线模型（LongWriter-8B）在三个不同的基准测试上进行比较。 消融研究 (Ablation Studies)：为了验证方法中每个关键组件的贡献，他们进行了一系列消融实验，例如：移除 REER 合成的数据、移除迭代搜索过程、移除用于引导思考的“反思性词元”（如 ‘Hmm…’, ‘Wait’）等，观察模型性能的下降情况。 定性分析 (Qualitative Analysis)：除了定量分数，研究者还从问题分解、逻辑一致性、分析深度等五个维度对模型的生成质量进行了定性评估和比较。 实验数据与结果：\n引用关键数据 (Table 1)： | Model | Base Model | LB | HB-A | HB-B | WB-A | WB-B | WB-C | WB-D | WB-E | WB-F | | :— | :— | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | GPT-4o | - | 83.1 | 83.7 | 87.6 | 74.40 | 73.42 | 74.38 | 77.91 | 75.86 | 78.08 | | Claude 3.5 | - | 89.3 | 82.9 | 88.3 | 59.05 | 57.68 | 56.32 | 59.36 | 62.00 | 67.70 | | LongWriter-8B | Llama3.1-8b | 76.5 | 80.1 | 82.6 | 57.97 | 53.92 | 49.08 | 52.08 | 52.99 | 52.08 | | DeepWriter-8B| Qwen3-8b | 91.28 | 82.64| 87.48| 72.20| 71.76| 70.57| 70.57| 73.65| 72.29|\n主实验结果： 全面超越开源基线：在所有测试中，DeepWriter-8B 均大幅超越 LongWriter-8B，尤其在 WritingBench (WB) 的六个领域中，优势巨大。 媲美顶级专有模型：在创意生成任务 HB-B 上，得分（87.48）与 GPT-4o（87.6）和 Claude 3.5（88.3）在统计上持平。在 WritingBench 的所有专业写作任务上，它甚至全面超越了 Claude 3.5，并且与 GPT-4o 相比也极具竞争力。 超长文本生成优势：在 LongBench-Write (LB) 上得分高达 91.28，显著高于所有其他模型，这表明通过学习结构化的思考轨迹，模型维持长距离连贯性的能力得到了极大的增强。 消融研究结果 (Table 2)： 合成数据的重要性：当移除 DeepWriting-20K 数据集（“- Remove Synthesis Data”）后，模型性能出现断崖式下跌，HB-B 得分从 87.48 降至 73.73，WritingBench 平均下降超过8分。这证明了 REER 合成数据的核心价值。 迭代优化的有效性：如果不使用迭代搜索，而是直接使用初始生成的粗糙轨迹（“- Remove Iterative Search”），模型性能同样显著下降，尤其在需要精细规划的 WB-A 任务上，得分从 72.20 降至 66.72。这证明了迭代打磨过程的必要性。 4. 未来探索与机遇 值得探索的问题与挑战： 代理指标的泛化性：困惑度作为一个代理指标非常巧妙，但它是否是唯一的或最佳的选择？未来可以探索其他代理指标，比如基于信息论的度量，或者训练一个更通用的“推理质量”评分模型来指导搜索。 搜索算法的效率与广度：本文使用的是局部搜索。未来可以探索更先进的搜索算法，如蒙特卡洛树搜索 (MCTS) 的变体或进化算法，来探索更广阔的推理路径空间，可能会发现更具创造性的思考模式。 多模态领域的应用：REER 的思想完全可以扩展到多模态领域。例如，从一幅优秀的设计图或一个精彩的短视频“反向工程”出其背后的设计思路或剧本创作过程。 个性化推理：能否从特定作家的作品中“反向工程”出他/她的独特写作思考模式，然后让模型模仿这种风格进行创作？这将开启个性化 AI 创作的新纪元。 新的技术和投资机会： 高质量合成数据即服务 (High-Quality Synthetic Data as a Service)：REER 证明了高质量、带有过程知识的合成数据是模型能力飞跃的关键。未来可能会出现专门提供这类“推理轨迹”数据的公司，为不同领域的模型训练提供“燃料”。 领域专用小型专家模型 (Domain-Specific Expert Models)：通过 REER，可以用较低的成本训练出在特定领域（如法律、营销、科研）具备深度思考能力的专用模型。这类轻量级、高性能的专家模型将有巨大的市场需求。 新一代 AI 原生内容创作平台：结合了 REER 类技术的写作工具，将不再是简单的润色或续写，而是能够与创作者进行深度“头脑风暴”，提供结构化建议和多种创意路径，成为真正的“第二大脑”。 5. 批判性视角与潜在不足 尽管这篇论文非常出色，但从批判性的角度看，仍存在一些可以探讨的方面：\n数据集的领域偏向性：从 Figure 5 可以看到，DeepWriting-20K 数据集有 48% 集中在“艺术 (Artistic)”领域，其中绝大部分是创意写作。虽然论文提到混合了 OpenThoughts 等其他领域的数据，但其核心优势可能仍然局限在创意和叙事性文本上。该方法在逻辑性、事实性要求极高的领域（如科学综述、技术文档）的效果有多强，还需要更多验证。 困惑度代理的潜在偏见：以“降低困惑度”为目标，可能会倾向于生成更“符合套路”、更“可预测”的思考路径，而可能会抑制那些不循常规但极富创造力的“神来之笔”。真正的创造性思维有时恰恰是反直觉和“高困惑度”的。 评估方法的局限性：论文的评估在很大程度上依赖于 LLM-as-a-Judge（使用 GPT-4o 和 Claude 3.7 作为裁判）。虽然这是当前领域内的常用做法，但其固有的偏见（例如，偏爱更长、更复杂的输出）是存在的。未来需要更可靠、更稳定的人类评估或新的评估范式来补充。 “反向工程”过程的成本：论文称其方法是可扩展且成本效益高的，但并未详细说明生成一条高质量推理轨迹所需的具体计算资源和时间。这个迭代搜索过程可能仍然是计算密集型的。 6. 核心启发与知识补充 我应该重点学什么？ 范式转换的思维：最大的启发是解决问题的思路。当“正向”的路走不通时，尝试“反向”思考。从一个理想的“终点”倒推实现它的“路径”，这是一种在科研和工程中都极具价值的思维方式。 寻找代理指标的能力：如何将一个模糊、不可量化的目标（如思考质量），转化为一个具体、可优化的数学指标（如困惑度），是AI研究中的核心能力。这篇论文提供了一个绝佳的范例。 数据作为算法的核心：这再次印证了“数据为王”的真理。有时，最能带来性能突破的不是一个全新的模型架构，而是一种全新的、能够产生高质量数据的方法论。投资于数据生成的创新，回报可能超乎想象。 有哪些启发？ 不要迷信模型规模：一个8B的模型，通过高质量数据的“喂养”，可以在特定任务上比肩甚至超越数百B参数的巨无霸。这启示我们，在资源有限的情况下，应优先考虑如何“聪明地”利用数据。 过程比结果更重要：让模型学习“如何思考”比仅仅学习“答案是什么”要重要得多。带有思考过程的数据能够赋予模型更强的泛化能力和鲁棒性。 需要补充的背景知识： 困惑度 (Perplexity)：深入理解它在语言模型中的定义、计算方式及其作为模型性能评估指标的意义。 强化学习与RLHF：了解强化学习的基本概念，特别是它如何被用于根据人类反馈来对齐语言模型 (RLHF)。 指令蒸馏 (Instruction Distillation)：了解这种通过“教师-学生”模式传递知识的模型训练方法。 搜索算法基础：了解像局部搜索 (Local Search)、爬山算法 (Hill Climbing) 等基本的优化搜索策略，这将有助于你更好地理解本文算法的核心机制。 ","date":"2025-09-10T00:00:00Z","permalink":"/reverse-engineered-reasoning-for-open-ended-generation/","title":"Reverse-Engineered Reasoning for Open-Ended Generation"},{"content":"利用困惑度作为奖励。感觉可以用于开放领域。本文的做法是将其作为补充而不是替代。\n这篇论文的核心目标是解决当前大语言模型（LLMs）在进行复杂推理时普遍存在的“过度思考（overthinking）”问题。具体来说，通过强化学习（Reinforcement Learning, RL）训练的LLM虽然在数学、逻辑等推理任务上取得了显著进步，但其奖励机制通常是粗粒度的（coarse-grained）。模型通常只能得到一个二元的反馈：答案正确（+1奖励）或错误（0或-1奖励）。这种机制导致模型为了确保最终答案正确，倾向于生成极其冗长、 verbose 的推理过程，即便其中包含大量冗余甚至不必要的步骤。这不仅大大增加了模型的推理延迟（latency）和计算成本（computational cost），也影响了用户体验。\n为了解决这一困境，论文提出了一种新颖、精巧且高效的奖励信号——自对齐奖励（Self-Aligned Reward, SAR）。SAR的核心思想是，一个高质量的回答不仅应该是正确的，还应该与问题（query）紧密相关且简洁。它不依赖任何外部模型或人工标注，而是完全通过模型自身的输出来计算，是一种自引导（self-guided）的信号。\nSAR的计算方式非常巧妙，它基于语言模型中一个基础且重要的概念——困惑度（Perplexity, PPL）。通俗地讲，困惑度衡量了模型对于一段文本的“惊讶”程度。困惑度越低，表示模型对生成这段文本越有信心，认为它越“自然”或“合理”。SAR的计算公式如下：\n\\(R_{SA} = \\text{clip}\\left(\\frac{\\text{ppl}(a) - \\text{ppl}(a|q)}{\\text{ppl}(a)}, -1, 1\\right)\\)\n这里的变量解释如下： * \\(q\\) 代表输入的问题（query）。 * \\(a\\) 代表模型生成的回答（answer）。 * \\(\\text{ppl}(a|q)\\) 是在给定问题 \\(q\\) 的条件下，生成回答 \\(a\\) 的困惑度。对于一个与问题高度相关的回答，这个值通常会比较低。 * \\(\\text{ppl}(a)\\) 是在不考虑任何上下文（即没有问题）的情况下，生成回答 \\(a\\) 的困惑度。对于一个具体、包含特定信息的回答，它独立出现的概率很低，因此这个值通常会很高。\nSAR的直觉在于衡量“条件困惑度下降（conditioned perplexity drop）”的相对幅度。如果一个回答是为问题量身定做的，那么问题 \\(q\\) 会极大地帮助模型生成答案 \\(a\\)，导致 \\(\\text{ppl}(a|q)\\) 远低于 \\(\\text{ppl}(a)\\)，从而得到一个较高的SAR奖励。相反，如果回答中包含大量与问题无关的“废话”，那么这部分内容的生成并不依赖于问题 \\(q\\)，其困惑度在有无问题的条件下差别不大，这就会拉低整体的SAR得分。更极端地，如果模型只是输出一段它“背诵”下来的常见文本，那么 \\(\\text{ppl}(a)\\) 本身就会很低，导致SAR奖励也很低甚至为负，从而有效抑制了模型的“死记硬背”而非推理。\n论文通过全面的实验证明了SAR的有效性。在与传统的PPO、GRPO（仅依赖正确性奖励）以及两种基于长度惩罚的方法（GRPO-O1, GRPO-ER）的对比中，集成了SAR的SA-GRPO方法取得了压倒性的优势。如论文图1所示，SA-GRPO不仅实现了更高的准确率，也同时获得了更高的效率（更短的答案长度），在“准确率-效率”二维图上稳居右上方区域。\n图1解读: 该图展示了不同方法相对于基线模型（SFT）在数学推理任务上的准确率（Acc, y轴）和效率（Eff, x轴，通过答案长度减少来衡量）的相对增益。点越靠右上角，表示该方法在提升准确率和效率两方面都越成功。SA-GRPO和SA-PPO明显处于该区域，而其他方法则有所取舍。\n论文的核心贡献在于，它没有像以往工作那样将“效率”和“准确率”视为一组零和博弈，而是设计了一种能够同时促进二者的内容感知（content-aware）的奖励信号。SAR通过一种计算开销极小的方式，为强化学习过程提供了更细粒度的指导，推动LLM学会如何进行简洁、相关且正确的推理。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 本文的核心研究目标是设计一种新的奖励机制，以在强化学习训练中，同时提升大型语言模型（LLMs）的推理有效性（effectiveness，即准确率）和推理效率（efficiency，即减少计算成本和答案长度）。\n解决的实际问题: 主要解决LLMs在推理任务中普遍存在的“过度思考（overthinking）”问题。\n问题表现: 经过强化学习微调的模型，为了追求答案的正确性，倾向于生成冗长、繁琐的思维链（Chain-of-Thought）。例如，一个简单的计算题，模型可能会写下数百个词的解释。 问题根源: 现有的奖励信号过于粗糙（coarse）。无论是基于人类反馈的强化学习（RLHF）还是基于AI反馈的（RLAIF），奖励通常是二元的（答案对/错），无法区分“简洁正确的答案”和“冗长正确的答案”。这导致模型只要最终答案正确，就会获得正向激励，而过程的冗长则被忽视。 行业意义:\n降低服务成本: LLM的推理成本与生成token的数量直接相关。答案越长，API调用费用越高，服务器负载也越大。解决过度思考问题，能够直接降低LLM服务的运营成本，对于大规模商业应用至关重要。 提升用户体验: 用户通常希望获得直接、切中要点的回答，而不是冗长的独白。简洁高效的回答能显著改善交互体验，尤其是在需要快速响应的场景中。 推动高效AI发展: 当前AI领域的一个重要趋势是追求更高的效率和更低能耗（所谓的“绿色AI”）。这项研究为训练更“节制”、更高效的AI模型提供了一种有效范式，符合行业向更可持续的AI发展的趋势。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路/方法: 论文提出了自对齐奖励（Self-Aligned Reward, SAR），一种基于模型自身困惑度计算的、内容感知的内部奖励信号。它被设计为对现有可验证奖励（verifiable reward，即正确性奖励）的补充，而非替代。最终的奖励函数形式为 \\(R = R_{VR} + \\alpha R_{SA}\\)。\n与之前方法的比较、特点和优势:\n对比方法 核心思想 缺点 SAR的优势 标准RL (PPO/GRPO) 仅使用正确性奖励 (\\(R_{VR}\\)) 导致“过度思考”，答案冗长，效率低下 SAR提供了细粒度的信号，奖励简洁、相关的推理，从而提升效率 长度惩罚 (GRPO-O1, ER) 在奖励中直接加入对答案长度的惩罚 内容无关（content-agnostic），可能会惩罚必要的长推理步骤，从而损害准确率 内容感知（content-aware），通过困惑度变化判断内容的“必要性”，而非盲目惩罚长度，因此能在缩短答案的同时保持甚至提升准确率 基于置信度的奖励 奖励模型置信度高（即困惑度低）的输出 容易导致模型“偷懒”，输出过于自信但简单的答案，或陷入“熵塌陷（entropy collapse）”，探索性不足 SAR衡量的是相对困惑度下降，而非绝对值。它鼓励的是对问题有深入理解后产生的自信，而非盲目的自信，从而避免了上述问题 细节分析: 自引导与低成本: SAR的计算不依赖任何外部模型（如一个专门的奖励模型）或人工标注，仅需模型本身进行两次前向传播（一次带问题，一次不带），计算开销极小，可以无缝集成到现有的RL训练流程中。 细粒度信号: 论文在Table 2中进行了精彩的案例分析。 \u0026gt; 对于“Correct and concise”（正确且简洁）的答案，SA-GRPO给出的奖励值为1.15，是所有类型中最高的。而对于“Correct but redundant”（正确但冗余）的答案，奖励值降至1.00。更重要的是，它能为“Partly correct”（部分正确）的答案提供-0.01的轻微负向奖励，远好于传统方法给予的-0.5，这有助于模型在训练初期学习到部分正确的推理模式。对于“Correct but no thought”（正确但无思考，如直接输出答案）的情况，SAR甚至会给予-0.04的惩罚，因为它判断这种答案并非由问题推理得来。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计: 模型: 实验覆盖了4种不同规模和架构的开源模型（Qwen3-1.7B, Qwen3-4B, Phi-3.5-mini, Gemma3-1B），以验证方法的普适性。 数据集: 训练集由3个数学推理数据集（GSM8k, MATH, NuminaMath）混合而成。评估则在5个数学推理数据集（包括训练中未见的GSM-symbolic和AIME）和2个逻辑推理数据集（LogicBench, ProntoQA）上进行，以测试模型的泛化能力（generalization）。 对比基线: Base: 仅经过监督微调（SFT）的模型。 PPO/GRPO: 使用标准正确性奖励的强化学习方法。 GRPO-O1/GRPO-ER: 使用了两种不同长度惩罚策略的强化学习方法。 SA-PPO/SA-GRPO: 论文提出的，集成了SAR的方法。 评估指标: 主要关注两个指标：准确率（accuracy）和平均答案长度（average answer length）。此外，论文还定义了一个综合指标AES（Accuracy-Efficiency trade-off Score）来量化准确率和效率的综合提升。 关键数据和结果: 主要结果 (Table 3): SA-GRPO在几乎所有模型和数据集的组合上都取得了最佳或接近最佳的性能。 \u0026gt; 以Qwen3-4B模型为例，在所有数据集上的平均结果显示： \u0026gt; * GRPO: 准确率69.07%, 长度1030.6 \u0026gt; * GRPO-ER (效率优化方法): 准确率67.38%, 长度 579.0 (准确率下降，长度缩短) \u0026gt; * SA-GRPO: 准确率71.41%, 长度 894.0 (准确率最高，长度也显著缩短) \u0026gt; \u0026gt; 这个结果清晰地表明，SA-GRPO是唯一一个在大幅提升准确率的同时，还能显著降低答案长度的方法。\n帕累托最优分析 (Figure 3): 该图展示了不同方法在“准确率增益 vs 长度减少”二维平面上的表现。 \u0026gt; 结果显示，长度惩罚方法（GRPO-O1, GRPO-ER）的曲线几乎总是在x轴下方，意味着它们为了效率（长度减少）而牺牲了准确率。相比之下，SA-GRPO的曲线始终位于x轴上方，并且处于所有曲线的右上包络（Pareto frontier），证明其在平衡准确率和效率方面达到了帕累托最优。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 值得探索的问题和挑战: 多模态推理的奖励设计: 论文在附录中初步探索了SAR在视觉语言模型（VLM）上的应用，结果显示效果不如纯文本任务。这是一个关键的未来方向。如何设计一个能够同时评估文本相关性和视觉定位（visual grounding）的自对齐奖励？例如，奖励是否应该与模型对图像特定区域的注意力（attention）相关联？ 超越困惑度的内部信号: SAR的成功证明了利用模型内部信号作为奖励的巨大潜力。未来可以探索其他更复杂的内部状态，例如： 注意力流（Attention Flow）: 能否通过分析注意力图谱来判断推理步骤是否关注了问题中的关键信息？ 神经元激活（Neuron Activation）: 特定任务的推理是否会激活模型内部特定的“专家”神经元或通路？能否以此为信号进行奖励？ 任务自适应的SAR: 当前SAR的“简洁、相关”导向非常适合数理逻辑任务。但在创造性任务（如写诗、写故事）或开放式问答中，一定程度的“自由发挥”或“发散思维”是必要的。未来的研究可以探索如何让SAR的奖励目标（如对“相关性”的要求强度）能够根据任务类型自适应调整。 与模型可解释性的结合: SAR在token层面的分析（Figure 2）实际上为理解模型的推理过程提供了一个窗口。未来的研究可以深入探索如何利用这类细粒度的奖励信号来提升模型行为的可解释性（interpretability），甚至用于调试模型的错误推理路径。 新的技术和投资机会: 技术机会: 高效推理框架: 催生出新一代的RLHF/RLAIF框架，内置多种高效、自引导的奖励函数，成为模型即服务（MaaS）平台的核心竞争力。 自动化奖励工程: 开发能够根据任务描述自动生成或调整奖励函数的工具，极大地降低部署和优化AI应用的门槛。 投资机会: AI基础设施与工具链: 专注于开发下一代AI训练和推理优化工具的初创公司。这些工具的核心卖点就是能以更低的成本训练出更高效的模型。 垂直领域的高效模型: 针对金融、法律、医疗等对成本和延迟高度敏感的领域，投资开发经过类似SAR方法优化的高效专用模型。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 任务领域的局限性: 论文的验证主要集中在数学和逻辑推理上，这些任务具有明确的“最优”解路径（通常是逻辑上最直接的）。SAR在这种场景下表现优异，但在需要多样性、创造性或复杂权衡的任务（如创意写作、策略规划、共情对话）中，其有效性尚未可知。“简洁”和“与问题强相关”可能反而会抑制模型的表现。\n对困惑度代理的过度依赖: 该方法假设“条件困惑度下降”是衡量回答质量的可靠代理。这在大多数情况下成立，但可能存在对抗性攻击（adversarial cases）。例如，模型可能会生成一些语法上与问题高度相关、但逻辑上完全错误的“伪推理”来“欺骗”SAR，从而获得高奖励。\n超参数α的敏感性分析不足: 最终的奖励是 \\(R_{VR} + \\alpha R_{SA}\\)。超参数 \\(\\alpha\\) 控制了正确性与效率之间的权衡。尽管Figure 3展示了不同 \\(\\alpha\\) 值的结果，但论文并未深入讨论如何系统性地选择最优的 \\(\\alpha\\) 值，以及该值是否在不同模型、不同任务间具有通用性。在实际应用中，调优这个超参数可能需要额外的成本。\n缺乏与复杂奖励模型的对比: 论文主要对比了无奖励模型或简单启发式（如长度惩罚）的方法。如果与一个经过大量数据训练的、专门用于评估回答效率和质量的复杂外部奖励模型相比，SAR的优势可能不会那么显著。当然，SAR的优势在于其极低的成本，但这仍然是一个需要验证的方面。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 重点学习的核心思想: 从“外部”转向“内部”寻找奖励信号: 最大的启发是，奖励信号不一定需要昂贵的人工标注或外部模型。模型自身的内部状态（如困惑度、不确定性、注意力等）是尚未被充分挖掘的宝藏，可以用来构建低成本、高效的自引导、自监督学习循环。这是一个范式转变（paradigm shift）。 奖励“过程”而非仅仅“结果”: 传统奖励只看最终答案是否正确。SAR通过衡量回答与问题的相关性，实际上是在奖励一个“好的推理过程”（即紧扣问题、不跑偏）。这个思想可以扩展到许多其他领域，即如何定义和奖励一个“好过程”。 相对变化比绝对值更重要: SAR的巧妙之处在于它不看困惑度的绝对值，而是看它的相对变化。这个思想极具普适性。在评估模型行为时，关注模型状态的动态变化（例如，在看到关键信息前后，模型内部激活的变化）可能比关注静态的绝对值能提供更多信息。 可以拿来即用的创新想法: 在你的任务中定义一个“SAR”: 思考在你自己的任务中，什么样的回答是“与输入紧密相关的”。例如，在文本摘要任务中，可以计算摘要相对于原文的“困惑度下降”，以奖励那些忠于原文核心信息的摘要。在代码生成任务中，可以计算生成的代码相对于注释或需求的“困惑度下降”。 利用内部信号进行数据过滤: 在为模型准备微调数据时，可以使用SAR作为一个打分器，筛选出那些回答质量高（SAR得分高）的问答对，从而提升数据质量。 用于模型行为的实时监控: 在模型推理时，可以实时计算每个token的SAR贡献值（如Figure 2所示）。如果连续出现SAR值很低的token，可能意味着模型开始“跑偏”或生成无关内容，可以作为触发干预（如重新生成或截断）的信号。 需要补充的背景知识: 信息论基础: 深入理解熵（Entropy）、交叉熵（Cross-Entropy）和困惑度（Perplexity）的定义、计算方式及其在自然语言处理中的意义。 强化学习算法: 了解PPO (Proximal Policy Optimization) 的基本原理，因为它是当前LLM进行RLHF的主流算法。了解GRPO会更有帮助。你需要知道策略（policy）、价值函数（value function）、优势函数（advantage function）等基本概念。 LLM的训练流程: 熟悉从预训练（Pre-training）到监督微调（SFT）再到强化学习（RLHF/RLAIF）的整个流程，以便理解SAR是在哪个环节、如何发挥作用的。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-09-10T00:00:00Z","permalink":"/self-aligned-rewardtowards-effective-and-efficient-reasoners/","title":"SELF-ALIGNED REWARD：TOWARDS EFFECTIVE AND EFFICIENT REASONERS"},{"content":" 将多轮对话用单轮训练，类似verl-agent的做法，当上下文较长时会总结成summary。\n论文深度解读：从“思考模型”到“行动专家”的进化之路 这篇来自 Salesforce AI Research 的论文，核心目标是解决一个当前大模型（LLM）智能体领域极具挑战性的问题：如何高效地训练一个自主单智能体（Autonomous Single-Agent），使其能够在没有人类持续干预的情况下，独立完成需要广泛搜索、深度整合信息和复杂推理的深度研究（Deep Research, DR）任务。\n当前，实现智能体（Agent）的方式主要分为两种。一种是多智能体系统（multi-agent systems），如同一个分工明确的团队，将复杂任务拆解给扮演不同角色（如规划师、研究员、编码员）的多个模型，流程相对固定。另一种则是单智能体系统（single-agent system），它更像一个全能的“瑞士军刀”，由单个LLM自主决定下一步行动，灵活性和泛化能力更强，但对模型自身的规划和推理能力要求也更高。这篇论文的焦点，正是后者。\n论文的作者们观察到一个关键的行业痛点：虽然我们已经有了很多擅长“思考”（即推理能力强）的模型，比如论文中提到的 QwQ-32B、gpt-oss-20b 等，但如何将它们的“思考”能力有效地转化为可靠的“行动”（即使用工具解决实际问题）能力，尤其是在长达数十步的复杂任务中，依然是一个难题。传统的强化学习（Reinforcement Learning, RL）方法在训练这类长任务智能体时，常常会遇到训练不稳定、模型行为退化（例如，无意义地重复调用工具）等问题。\n如论文在 §4.2.2 中所揭示的，在没有优化的情况下，“…长的轨迹在每个批次中贡献了不成比例的动作步骤，主导了损失；结果，它们被强化并更频繁地出现，即使它们被负面奖励或长度惩罚严重惩罚。”\n这正是SFR-DeepResearch工作的核心创新所在。他们没有从一个通用的基础模型开始训练，而是选择站在巨人的肩膀上，直接在推理优化模型（reasoning-optimized models）的基础上进行持续强化学习（continual reinforcement learning）。他们设计了一套简洁而极其巧妙的 RL 训练配方（RL Training Recipe），这套配方包含三大亮点：\n高度优化的智能体推理工作流（Agentic Inference Scaffolding）：针对不同模型家族的特性，他们设计了不同的交互模式。特别是对于Qwen和QwQ系列模型，他们创造性地将传统的多轮对话任务，重构为迭代式的单轮上下文问答任务。这意味着每一步，模型看到的都是一个包含了所有历史工具调用记录的、不断增长的“初始问题”，这巧妙地规避了这些模型在长多轮对话中可能出现的“思考”能力下降问题。此外，他们还赋予智能体一个clean_memory工具，让其学会自主管理上下文窗口，解决了长任务中信息过载的核心痛点。\n长度归一化的RL目标函数（Length-normalized RL Objective）：这是论文在算法层面最关键的贡献。传统的REINFORCE算法在计算优势（Advantage）时，没有考虑轨迹的长度。这导致模型倾向于执行更长的动作序列，即使这些序列是错误或低效的，因为它们在损失计算中占据了更大的权重。该论文通过在优势计算公式中除以轨迹长度 T，极大地稳定了训练过程。\n\\[ A_{i,j} = A_i = \\frac{r_i - \\text{mean}(R)}{\\text{std}(R) \\cdot T_i} \\]\n这个简单的改动，如同一个“镇流器”，有效抑制了模型生成无意义长轨迹的冲动，使得奖励信号能更准确地引导模型学习高效、正确的行为。从论文的图2可以看出，这一改动效果显著，避免了模型性能的崩溃。\n高质量的合成数据生成管线（Training Data Synthesis Pipeline）：作者们发现现有的公开数据集对于顶尖的DR智能体来说过于简单。因此，他们构建了一套迭代式的方法来生成更具挑战性的合成数据，涵盖了短格式问答和长篇报告撰写，从而确保了训练的有效性和模型的先进性。\n最终，他们训练出的最佳模型 SFR-DR-20B 在多个权威基准测试中取得了惊人的成绩。尤其是在高难度的 Humanity’s Last Exam (HLE) 基准上，取得了 28.7% 的分数，不仅远超其 gpt-oss-20b 基础模型（提升了65%），甚至优于或媲美一些强大的专有模型，如 OpenAI 的 Deep Research (o3) 的 26.6%。这充分证明了其方法的卓越性。\n总而言之，这篇论文为如何构建更强大、更实用的自主研究智能体提供了一套清晰、可复现且效果卓越的蓝图。它不仅在技术细节上做出了精妙的创新，更在方法论上指明了方向：在强大的推理模型基础上，通过精心设计的、稳定的强化学习范式，是释放LLM作为自主行动者潜力的关键路径。\n接下来，我将按照您提出的六个问题，进行更详细的剖析。\n1. 论文的研究目标是什么？ 核心研究目标：开发一种有效且稳定的强化学习方法，将本身具备强大推理能力的LLM，转化为能够自主执行深度研究（Deep Research, DR）任务的单智能体（Single-Agent）。\n想要解决的实际问题：\n长程任务稳定性问题：在需要多步（几十甚至上百步）工具调用才能解决的复杂问题上，如何避免智能体在强化学习过程中出现行为退化（如陷入循环、重复无效操作）和训练不稳定的问题。 推理与行动的鸿沟：许多LLM在“闭卷”推理任务（如数学、逻辑题）上表现出色，但一旦需要与外部世界（如网络搜索、代码执行）交互，它们的规划和决策能力就会下降。论文旨在弥合这种“思考”与“行动”之间的差距。 上下文管理难题：随着任务的进行，智能体与环境的交互历史会迅速填满模型的上下文窗口。如何让智能体有效管理和压缩这些信息，保留关键内容，是实现长程任务自主性的关键。 对行业发展的重要性： 这个问题是推动AI从“问答引擎”向“问题解决伙伴”跃迁的核心。如果能解决，意味着我们可以拥有能够自主进行文献综述、市场分析、代码调试、科学研究等复杂工作的AI助手。这将极大提升知识工作者的生产力，甚至可能在科学发现、商业决策等领域催生新的范式。对于行业而言，这意味着更强大的AI产品能力和更广阔的商业化场景，是通往更通用人工智能（AGI）的关键一步。\n2. 论文提出了哪些新的思路、方法或模型？ 这篇论文的核心在于提出了一套精炼的训练“配方”（Recipe），而非一个全新的模型架构。其创新思路和方法主要体现在以下几个方面：\n思路转变：从“零基础培养”到“专才深造” 特点与优势：不同于许多从通用指令微调（SFT）或基础模型开始训练智能体的工作，本文选择从推理优化模型（reasoning-optimized “thinking” models）入手。这相当于不从“小学生”教起，而是直接招募“数学博士”，然后教他如何使用实验设备。 论文细节支持：作者明确指出，他们的目标是“利用并保留其强大的分步推理能力，同时赋予其新的智能体能力”。这种策略的优势在于，起点更高，训练过程可以更专注于学习如何规划和使用工具，而不是重新学习基础的推理能力。 方法创新：精巧的智能体工作流与RL算法 模型定制化的推理工作流 (Agentic Inference Scaffolding) 特点与优势：该方法不采用“一刀切”的方案，而是根据不同模型家族的特性进行适配。对于Qwen/QwQ这类在单轮推理上表现更优的模型，它通过将多轮对话重构成单轮长上下文问答，巧妙地将任务转换到模型最擅长的领域。 论文细节支持：如论文图1所示，每一步的工具调用历史（Result A, Result B等）都会被打包进下一次的用户输入中，而不是作为独立的对话轮次。这避免了模型在多轮对话中可能出现的性能衰减。 赋予智能体“元认知”：自主内存管理 特点与优势：相比于依赖外部记忆库的复杂方案，本文提出了一种更内化的方法：为智能体提供一个clean_memory工具。这让智能体能够像人一样，在感觉信息过载时主动“清理思绪”，决定哪些历史信息是重要的并保留下来。这是一种更轻量级且更符合“自主”理念的设计。 核心算法创新：长度归一化的RL目标函数 特点与优势：这是稳定长程任务RL训练的关键。通过在计算优势（advantage）时除以轨迹长度T，它惩罚了那些“虽然失败但步骤冗长”的轨迹，鼓励模型学习高效且正确的解决方案。 论文细节支持：公式(1) Ai = (ri - mean(R)) / (std(R) · T) 是这一创新的数学表达。论文在 §4.2.2 中通过实验对比（见下图2）清晰地展示了，没有这个归一化项，模型的平均轨迹长度会失控式增长，而任务性能（HLE分数）则会崩溃；加入归一化后，轨迹长度受到有效控制，性能稳步提升。 图注：论文中的图2，清晰展示了长度归一化（蓝色线）对稳定训练轨迹长度和提升性能（HLE分数从7.8提升到13.0）的关键作用，而标准方法（红色线）导致轨迹长度增长而性能崩溃。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文通过一系列严谨的实验来验证其方法的有效性，实验设计周密，结果令人信服。\n实验设计： 基准测试 (Benchmarks)：选用了三个公认的、高难度的智能体任务基准： FRAMES：专注于需要多跳推理的问答任务，考验智能体的深度信息检索和整合能力。 GAIA：一个更通用的AI助手基准，任务更贴近真实世界，需要灵活的工具使用能力。 Humanity’s Last Exam (HLE)：一个以推理为核心的基准，涵盖数学、科学等领域，极具挑战性。 对比对象 (Baselines)：对比了多种类型的系统，包括： 专有系统：如 OpenAI 的 Deep Research (o3)、GPT-5 等。 开源多智能体系统：如 OpenDeepSearch、MiroThinker 等。 开源单智能体系统：如 WebSailor、WebShaper 等。 污染防治 (Contamination Prevention)：实验中一个非常严谨的细节是，作者们实施了污染黑名单（contamination blocklist）。因为这些基准的答案可能存在于网络上，为防止智能体“抄答案”，他们阻止智能体访问 huggingface.co 等已知的答案托管网站。对于无法确认是否采取了同样措施的基线模型，他们用自己的黑名单重跑了其实验，这大大增强了结果的可信度。 实验数据和结果： 论文在表1中展示了核心实验结果，数据非常亮眼。以下是一个简化的对比表格，突出SFR-DR-20B的卓越表现： 智能体 基础模型 FRAMES GAIA HLE gpt-oss-20b (base) gpt-oss-20b - - 17.3 Deep Research (o3) o3 - 67.4 26.6 WebShaper-32B QwQ-32B 69.42* 48.5* 12.23* SFR-DR-20B (ours) gpt-oss-20b 82.8 66.0 28.7 *（*表示作者使用黑名单重跑的分数）* 从数据可以看出： * **显著的性能提升**：`SFR-DR-20B` 的 HLE 分数（28.7%）相比其基础模型 `gpt-oss-20b`（17.3%）提升了 **65%**，证明了其RL训练配方的巨大价值。 * **超越顶尖对手**：在最考验推理能力的HLE基准上，`SFR-DR-20B` 的28.7%不仅超过了众多开源对手，甚至略高于强大的专有模型Deep Research (o3)的26.6%。 * **全面的强大**：在FRAMES和GAIA这两个更侧重于网络浏览和综合任务的基准上，`SFR-DR-20B`同样取得了SOTA（State-of-the-art）级别的成绩。 消融研究 (Ablation Studies)： 为了证明其方法中每个组件的有效性，论文还进行了消融实验。如 §4.2.1 和表2所示，仅仅是在推理时使用他们提出的单轮重构工作流，就能让QwQ-32B在FRAMES上的性能从58.0%提升到68.0%，带来了10%的绝对提升，这充分证明了其工作流设计的巧妙和有效性。 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 尽管这篇论文取得了巨大成功，但它也为未来的研究揭示了更广阔的探索空间和挑战。\n值得探索的问题和挑战： 奖励模型自动化与对齐 (Reward Model Automation \u0026amp; Alignment)：目前，奖励模型（Verifier）本身也是一个LLM，它的判断力和偏好决定了智能体学习的天花板。如何构建更准确、更稳定、甚至能自我演进的奖励模型，或者探索无需奖励模型的智能体学习范式（如自主偏好学习），是一个核心挑战。 更复杂的工具使用 (More Complex Tool Usage)：当前的工具集（搜索、浏览、代码）相对基础。未来的智能体需要与更复杂的环境交互，例如操作图形用户界面（GUI）、与数据库进行复杂查询、使用专业软件包（如MATLAB、AutoCAD）等。这需要全新的智能体架构和训练方法。 从单智能体到混合智能体系统 (From Single-Agent to Hybrid Systems)：虽然本文聚焦于单智能体的优势，但未来的最佳形态很可能是分层或混合智能体系统。即一个“总指挥”单智能体，可以根据任务需要，动态地生成和调度多个“专家”子智能体来协同工作。这结合了单智能体的灵活性和多智能体系统的专业性。 安全与可控性 (Safety \u0026amp; Controllability)：一个拥有强大工具使用能力的自主智能体，也带来了潜在的风险。如何确保其行为始终符合人类的意图和价值观，如何设置有效的“护栏”，防止其被滥用或产生破坏性行为，是该领域走向实际应用前必须解决的关键问题。 可能催生的新技术和投资机会： 技术机会： Agent-Native模型架构：未来的LLM在设计之初就可能考虑到作为智能体核心的需求，例如内置的规划模块、更优的上下文管理机制等。 智能体操作系统 (Agent OS)：可能会出现类似操作系统的平台，专门用于管理智能体的工具、权限、内存和多智能体间的通信。 自动化科学发现平台：将这类智能体应用于特定科学领域（如材料科学、药物研发），可能加速新的科学发现。 投资机会： 垂直领域AI智能体解决方案：为金融、法律、医疗等行业提供高度定制化的自主智能体服务。 智能体开发与运维平台 (Agent MLOps)：提供用于训练、部署、监控和保护AI智能体的工具链。 下一代计算基础设施：支持大规模智能体并行运行和推理的基础设施，将成为新的热点。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 以批判性的眼光审视，这篇论文虽然优秀，但仍在一些方面存在局限性或值得商榷之处。\n对合成数据的依赖与泛化性疑问： 整个训练过程高度依赖作者自己构建的合成数据集。虽然他们证明了该数据集的挑战性，但论文并未详细披露数据的生成过程、多样性以及它与真实世界用户需求的差距。模型的优异表现可能部分归因于对特定类型合成任务的“过拟合”，其在更开放、更模糊的真实世界问题上的泛化能力有待进一步验证。 工具集的局限性： 论文有意选择了search, browse_page, code_interpreter这三个最小化工具集，目的是为了更好地训练模型的内在能力。然而，这也意味着模型没有学习如何处理更丰富的工具生态，例如调用API、与外部服务交互等。这使得其离一个通用的、能解决多样化现实问题的智能体还有距离。 模型特定优化方案的普适性： 论文为一个关键发现是，不同的模型家族需要不同的推理工作流（Qwen/QwQ用单轮重构，gpt-oss用标准多轮）。这暗示了他们提出的方法并非一个完全“即插即用”的通用解决方案，而是需要根据基础模型的特性进行手动调优。这在一定程度上限制了其方法的普适性和易用性。 对“思考”过程的分析不足： 论文观察到gpt-oss-20b在RL后CoT（思维链）变短但效率更高，而Qwen系列模型则相反。这是一个非常有趣的现象，但论文仅做了初步的假设性解释（“Qwen系列模型可能被优化用于单轮推理”）。对智能体在RL训练中其内部“思考”模式究竟发生了何种变化，缺乏更深入的量化分析。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了大量“拿来即用”的智慧和深刻的启发。\n重点学习的创新点： RL稳定性是第一要务：学习“长度归一化”： 这是最核心、最可移植的技术点。如果您正在训练任何需要多步决策的智能体，长度归一化的优势函数是一个非常值得立即尝试的技巧。它简单、有效，能极大缓解训练不稳定的问题。 推理时的小改动，性能上的大提升：学习“单轮重构工作流”： 这个思想极具启发性：不要强迫模型适应你的任务范式，而是将你的任务范式调整到模型最擅长的模式。在不重新训练的情况下，仅通过改变prompt的组织方式，就可能获得显著的性能提升。这是一个典型的“花小钱办大事”的工程智慧。 让智能体“自己的事自己做”：学习“自主内存管理”： 与其设计复杂的外部记忆系统，不如赋予智能体管理自身上下文的工具。这个思路可以推广到其他资源管理上，例如API调用预算、计算时间限制等，让智能体学会“精打细算”，从而变得更鲁棒、更实用。 带来的启发： 起点决定高度：选择一个强大的、在特定能力（如推理）上已经很突出的模型作为起点，再进行智能体能力的训练，事半功倍。 数据质量是关键：不要满足于现有的、可能已经“饱和”的公开数据集。为你的目标任务，精心构建高质量、有挑战性的训练数据，是拉开模型性能差距的关键。 简单即是美：论文的RL配方并没有使用极其复杂的算法，而是对现有算法做了精巧而关键的修改。这告诉我们，深刻理解问题的本质，往往能找到最简洁有效的解决方案。 需要补充的背景知识： 强化学习基础：特别是策略梯度（Policy Gradient）方法，尤其是REINFORCE算法，以及更现代的PPO (Proximal Policy Optimization)，后者是许多现代RLHF的基础。 智能体架构：了解经典的大模型智能体框架，如 ReAct (Reason + Act) 和 Toolformer 的思想，它们是理解智能体如何结合推理和工具使用的基础。 相关基准测试：可以去了解一下 HLE, GAIA, FRAMES 这几个基准测试的细节，这能帮助你更深入地理解当前智能体能力评测的前沿和挑战。 ","date":"2025-09-10T00:00:00Z","permalink":"/sfr-deepresearchtowards-effective-reinforcement-learning-for-autonomously-reasoning-single-agents/","title":"SFR-DeepResearch：Towards Effective Reinforcement Learning for Autonomously Reasoning Single Agents"},{"content":"非常类似EDGE-GRPO：Entropy-Driven GRPO with Guided Error Correction for Advantage Diversity，不过这篇是利用置信度，edge-grpo是利用熵。同样的思路就是“重罚”高信心的错误，并“重奖”低信心的正确。\n论文深度解读 这篇论文的核心目标是创建一个能够像人类数据分析师一样，自主、灵活地对表格数据进行复杂推理的程序化智能体 (Programmatic Agent)，他们将其命名为 TableMind。\n在当今世界，从金融报表到科学实验记录，再到医疗数据，表格无处不在。如何让AI精准地理解和处理这些结构化数据，是一个极具挑战性但价值巨大的问题。传统的语言模型（LLMs）虽然在自然语言处理上取得了巨大成功，但在处理需要精确计算和多步逻辑推理的表格任务时，往往会“心有余而力不足”。它们可能会在简单的数学运算上出错，或者无法执行精细的数据筛选和聚合操作。\n为了解决这个问题，研究界开始探索工具增强（Tool-Augmented）的方法，即让LLM调用外部工具（如代码解释器）来执行它不擅长的任务。然而，论文作者敏锐地指出，早期的方法存在一个关键缺陷：过于死板。它们大多遵循一种“先计划，后执行 (plan-then-execute)”的刚性工作流。这种模式就像一个严格按照说明书操作的“办事员”，一旦遇到计划外的突发情况（比如代码执行出错或中间结果不符合预期），就无法灵活应对，更不用说自我纠错了。\nMetaphorically speaking, these methods behave more like clerks following a fixed checklist—the workflow—rather than expert analysts who can improvise, learn from mistakes, and refine their approach on the fly. This distinction highlights the need to move from structured workflows to dynamic agents.\n（打个比方，这些方法更像是遵循固定清单的办事员，而不是能够即兴发挥、从错误中学习并动态调整方法的专家分析师。这种区别凸显了从结构化工作流转向动态智能体的必要性。）\nTableMind的提出正是为了打破这种僵局。它的核心思想是模仿人类专家的思维链，构建一个“计划-行动-反思” (plan-action-reflect) 的迭代循环。如下图所示，它不是一次性生成所有步骤，而是在每一步行动（执行代码）后，都会观察结果（成功输出或错误信息），然后进行“反思”，并根据反思结果动态调整下一步的计划。这种设计赋予了智能体前所未有的灵活性和鲁棒性。\n图1：TableMind通过“计划-行动-反思”的多轮循环模仿人类思维链来解决表格任务\n为了训练出这样聪明的智能体，论文设计了一个精巧的两阶段训练范式：\n监督微调 (Supervised Fine-Tuning, SFT)：这个阶段好比“学徒期”。研究者首先利用一个更强大的“专家模型”生成一批高质量的、包含完整“计划-行动-反思”链条的推理轨迹。然后，用这些轨迹数据对TableMind进行SFT训练。这一步的目的不是让模型变得多有创造力，而是为其注入基础的“工具使用语法”和“行为模式”，让它先学会“照猫画虎”，知道该如何组织语言、调用工具并理解反馈。这为后续的探索学习提供了一个非常好的“热启动 (warm-up)”基础。\n强化学习微调 (Reinforcement Fine-Tuning, RFT)：这才是TableMind从“学徒”走向“专家”的关键。在这个阶段，模型不再满足于模仿，而是开始自主探索更优的解题策略。为了有效引导这个探索过程，论文设计了一个多目标奖励函数 (multi-objective reward function)，它同时考虑了三个方面：最终答案的准确性、推理过程的格式规范性以及工具调用的策略有效性（例如，成功的工具调用会获得额外奖励）。\n更进一步，论文在RFT阶段引入了一项关键的技术创新——排序感知策略优化 (Rank-Aware Policy Optimization, RAPO)。这是一个非常深刻的洞察。传统的强化学习算法可能只关心“答案对不对”，而RAPO更关心模型的“认知校准”。它会比较模型对不同推理路径的“置信度”（即生成概率）和这些路径的“真实奖励”（即最终得分）。当出现“错位 (misalignment)”时——例如，模型以很高的置信度生成了一个错误的答案，同时以很低的置信度“猜”对了一个正确答案——RAPO会动态地调整学习权重。它会“重罚”高信心的错误，并“重奖”低信心的正确。通过这种方式，RAPO不仅在优化模型的行为，更是在优化模型的“判断力”，使其决策过程更可靠、更高效。\n实验结果也充分证明了TableMind的优越性。在WikiTQ、TabMWP和TabFact三个主流的表格推理基准测试中，TableMind全面超越了包括PoTable、Chain-of-Table和Table-R1在内的所有基线模型。尤其是在需要复杂数学计算的TabMWP数据集上，TableMind取得了99.27%的惊人准确率，比次优模型高出3.22%，这充分展示了其在精确计算和鲁棒推理方面的强大能力。\n总而言之，TableMind的创新性贡献在于，它不仅仅是另一个工具增强的LLM，而是一个真正意义上的自主智能体框架。它通过“计划-行动-反思”循环实现了动态适应性，通过“SFT+RFT”两阶段训练范式兼顾了基础能力和策略优化，并通过创新的RAPO算法提升了学习的效率和模型的“认知校准”能力，为构建更通用、更强大的AI数据分析师铺平了道路。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心目标是开发一个名为TableMind的自主程序化智能体 (autonomous programmatic agent)，使其能够像人类专家一样，灵活、准确地解决复杂的表格推理任务。\n解决的实际问题：\nLLM在精确计算上的短板：纯文本的LLM在处理需要精确数学运算或符号逻辑的表格数据时，表现不可靠，容易产生“幻觉”或计算错误。 现有工具增强方法的僵化：许多集成了代码执行等工具的方法依赖于固定的、预定义的工作流 (workflows)。这种方法缺乏灵活性，无法处理预设流程之外的意外情况（如代码错误），也无法进行自我纠正 (self-correction)。它们更像是机械的执行者，而非智能的思考者。 行业意义：\n释放数据价值：表格是商业、金融、科研、医疗等领域最核心的数据载体。能够自动化地、高精度地对表格数据进行推理、分析和洞察，将极大地提升数据处理效率，帮助企业和研究机构更快地从海量数据中提取价值。 推动AI Agent的落地：表格推理是构建更通用AI智能体（Agent）的关键一环。一个能处理结构化数据的Agent，才能真正地在企业环境中作为“AI员工”或“AI分析师”发挥作用，连接数据库、生成报告、执行复杂的业务逻辑。 降低专业分析门槛：强大的表格推理智能体可以赋能给不具备专业编程或数据分析技能的普通用户，让他们通过自然语言就能完成复杂的数据查询和分析工作，实现数据分析的“平民化”。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其整体框架设计和关键算法优化。\n新思路与模型： “计划-行动-反思” (plan-action-reflect) 循环：这是TableMind区别于“先计划、后执行”模型的最大特点。它是一种动态、迭代的推理模式。 \u0026gt; In this paper, we present TableMind, an LLM-driven table reasoning agent that (i) autonomously performs multi-turn tool invocation, (ii) writes and executes data-analyzing code… and (iii) exhibits high-level capabilities such as planning and self-reflection to adapt strategies. \u0026gt; \u0026gt; 在这篇论文中，我们提出了TableMind，一个LLM驱动的表格推理智能体，它能 (i) 自主执行多轮工具调用，(ii) 编写并执行数据分析代码… (iii) 展现出高级能力，例如通过计划和自我反思来调整策略。 优势分析：以论文图6的案例为例，模型第一步尝试提取时间，得到“2:14:15”和“2:17:27”两个字符串。一个僵化的模型可能会直接尝试用这两个字符串做减法，导致错误。但TableMind进入“反思”阶段，意识到“这些时间需要被转换成秒才能准确计算差值”。基于这个反思，它生成了新的计划（使用datetime模块进行转换），从而成功解决了问题。这种自我纠错和动态规划能力是其核心优势。 “SFT热启动 + RFT策略优化” 的两阶段训练范式： 特点：这个范式非常务实。它首先通过监督微调 (SFT) 让模型快速掌握基本技能（模仿学习），然后通过强化学习 (RFT) 在这个基础上进行探索和优化（策略学习）。 优势分析：直接用强化学习训练一个LLM，其探索空间巨大，收敛缓慢且不稳定。SFT阶段相当于给模型提供了一个“脚手架”，极大地降低了RFT的学习难度和成本。论文图3(a)的奖励曲线清晰地显示，SFT+RL的起始奖励远高于Pure RL（纯强化学习），并且收敛得更快、更稳定。 排序感知策略优化 (Rank-Aware Policy Optimization, RAPO)： 特点：这是论文在算法层面的关键创新。它在更新策略时，不仅考虑奖励的大小，还考虑模型生成该轨迹的置信度。 优势分析：RAPO的核心是解决模型置信度与真实奖励的“错位”问题。当模型对一个低奖励轨迹的置信度高于一个高奖励轨迹时，RAPO会通过一个权重因子 γ 来放大这个“坏”样本对的梯度信号，迫使模型进行更大幅度的修正。相比于传统的策略梯度算法（如GRPO），RAPO的学习过程更稳定、更高效，因为它能更精准地识别和纠正模型的“认知偏差”。论文图3(b)显示，RAPO的奖励曲线全程稳定地高于GRPO基线。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在三个公开、权威的基准数据集上进行全面的对比实验和消融研究，系统地验证了TableMind的有效性。\n实验设计：\n数据集：覆盖了表格推理的不同核心能力。 WikiTQ：开放域问答，考验模型的信息整合和查询能力。 TabMWP：数学推理，考验模型的数值计算和多步逻辑能力。 TabFact：事实核查，考验模型对表格内容的精确理解和判断能力。 基线模型：选取了6个强大的LLM-based方法，分为两类： 免训练 (Training-free)：如Tab-CoT、PoTable，主要依赖强大的基础模型和精巧的提示工程。 基于微调 (Tuning-based)：如TableLlama、Table-R1，这些模型在特定任务上进行了微调。 评估指标：对于问答任务使用精确匹配率 (Exact Match)，对于事实核查任务使用准确率 (Accuracy)。 实验数据和结果： TableMind在所有三个基准上都达到了state-of-the-art (SOTA) 的性能，关键数据如下表（源自论文Table 1）：\n模型 (Method) WikiTQ (EM%) TabMWP (EM%) TabFact (Acc%) Chain-of-Table (强基线) 68.31 96.17 87.78 Table-R1 (基于RL的强基线) 74.86 96.02 87.17 TableMind (本文方法) 76.82 99.27 91.85 相对提升 (+2.61%) (+3.22%) (+3.28%) 这些数据清晰地表明，TableMind相较于最强的基线模型，依然有显著的性能提升。\n消融实验 (Ablation Studies)：为了证明每个设计模块的必要性，作者系统地移除了模型的关键组件并观察性能变化（见论文Figure 4）。\n移除SFT：性能大幅下降，证明了SFT作为“热启动”阶段的必要性。 移除RFT：性能同样显著下降，说明仅靠模仿学习不足以掌握复杂的决策策略。 移除RAPO：性能下降，验证了RAPO算法相比传统RL算法的优越性。 移除奖励函数中的R_tool项：性能下降，说明专门奖励成功工具使用的设计是有效的。 这些消融实验有力地支撑了论文的结论：TableMind的卓越性能是其各个创新组件协同作用的结果。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个研究方向正处于AI Agent发展的风口浪尖，未来充满机遇和挑战。\n值得探索的问题和挑战： 多表推理 (Multi-Table Reasoning)：现实世界的数据分析任务往往需要连接 (join) 和聚合 (aggregate) 来自多个不同表格的信息。这是当前模型能力的下一个巨大鸿沟。 更广泛的工具集与环境交互：目前的工具仅限于一个安全的Python沙箱。未来的智能体需要能够调用更广泛的工具，如数据库查询语言 (SQL)、API调用、网页浏览等，并与更复杂的企业软件环境进行交互。 长程任务规划 (Long-Horizon Planning)：当前的“计划-行动-反思”循环在3-5步内表现良好。但如何处理需要数十甚至上百步才能完成的超长任务，同时保证智能体不“迷路”、不陷入循环，是一个巨大的挑战。 强化学习的样本效率：RFT虽然强大，但通常需要大量的试错和样本。如何提升其样本效率 (sample efficiency)，或者探索更高效的，如离线强化学习 (Offline RL) 或基于模型的强化学习 (Model-based RL)，是降低训练成本的关键。 可解释性与可信赖性：当智能体自主决策时，如何确保其行为是可预测、可解释且符合人类价值观的？这是一个关乎AI安全和伦理的重要问题。 新的技术和投资机会： 企业级AI数据分析师：直接将此类技术产品化，为企业提供“开箱即用”的AI数据分析解决方案，可以自动生成报表、挖掘业务洞察，市场潜力巨大。 Agent开发平台与基础模型：可能会出现专门为构建Agent而设计的基础模型 (Foundation Models for Agents) 和低代码开发平台。企业可以在此基础上，快速定制和部署能够操作其内部系统的专属AI Agent。 数据安全与Agent沙箱技术：当Agent需要访问真实数据库和API时，如何确保数据安全和操作可控变得至关重要。相关的安全沙箱 (secure sandbox) 和权限管理技术将成为一个重要的投资赛道。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些值得商榷和深入探讨的地方。\n模型与数据规模的局限性： 模型依赖：实验基于Qwen3-8B模型。这个框架在更小（如3B以下）或更大（如GPT-4o、Claude 3.5 Sonnet）的模型上表现如何？其性能增益是否具有普适性，还是高度依赖于特定规模模型的特性，这一点有待验证。 训练数据的真实性：SFT阶段使用了“合成样本”。这些样本的质量和多样性对最终性能至关重要，但论文对此的描述不够详尽。在面对真实世界中充满噪声、格式不规范的“脏数据”时，模型的鲁棒性如何，还需要进一步的检验。 算法的超参数敏感性： RAPO算法引入了新的超参数 α（在公式7中），用于控制对“错位”样本的惩罚强度。论文没有讨论模型性能对这个超参数的敏感性，这对于算法的复现和在其他场景中的推广应用是一个潜在的风险。 评估维度的单一性： 实验评估主要集中在最终答案的准确性上。对于一个智能体而言，效率（解决问题所需的步骤数）、成本（API调用次数）和鲁棒性（在遇到模糊指令或错误时的恢复能力）同样是重要的衡量维度。论文在这些方面的分析相对较少。 推理过程的“最优性”存疑： 虽然TableMind能够找到正确的答案，但它生成的推理路径是否是最高效、最简洁的？强化学习的目标是最大化累积奖励，这不完全等同于人类眼中的“最优”策略。对生成路径的质量进行更细致的人工评估可能会揭示更多问题。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是AI Agent设计思想的绝佳范例，其中蕴含了许多可以借鉴的宝贵经验。\n重点学习的创新思想： “模仿而后创新” (Imitate then Innovate) 的训练哲学：这是最核心的启发。在面对复杂的 Agent 任务时，不要一开始就让模型“自由发挥”。先通过SFT让它模仿高质量的专家行为，建立一个坚实的基础，然后再通过RFT等方法让它在此基础上进行探索和优化。这个范式在代码生成、复杂推理等多个领域都极具推广价值。 将“反思” (Reflection) 纳入核心循环：构建Agent时，不要只满足于“思考-\u0026gt;行动”的线性流程。引入一个明确的“反思”步骤，让模型在每一步行动后审视中间结果，评估当前策略的有效性，并动态调整后续计划。这是从“工具”迈向“智能体”的关键一步。 超越准确率的奖励设计：在用强化学习训练Agent时，奖励函数的设计至关重要。除了最终结果的奖励，可以考虑引入过程奖励，例如：对成功的工具调用给予奖励，对简洁的路径给予奖励，对格式规范的行为给予奖励。这种多目标奖励设计能更有效地塑造Agent的行为。 关注“认知校准”：RAPO算法的思路非常有启发性。在优化模型时，不仅要看它“做对了没”，还要看它“对自己有多自信”。想办法让模型的置信度与其真实能力对齐，是提升模型可靠性的一个高级技巧，可以应用在任何需要模型做决策的场景中。 需要补充的背景知识： 强化学习基础：特别是策略梯度 (Policy Gradient) 方法，如PPO (Proximal Policy Optimization)。了解这些是理解RFT和RAPO算法的基础。 RLHF与DPO：了解人类反馈强化学习 (RLHF) 和直接偏好优化 (DPO) 的思想，可以帮助你更好地理解为什么需要像RAPO这样对模型的“偏好”或“置信度”进行优化。 主流Agent框架：了解一些经典的Agent思想，如ReAct (Reason+Act)，它也是一种将思考和行动交错进行的模式，是TableMind这类框架的思想前身。 工具增强LLM (Tool-Augmented LLMs)：熟悉当前LLM是如何通过函数调用 (Function Calling) 或API与外部世界进行交互的。 ","date":"2025-09-10T00:00:00Z","permalink":"/tablemindan-autonomous-programmatic-agent-for-tool-augmented-table-reasoning/","title":"TableMind：An Autonomous Programmatic Agent for Tool-Augmented Table Reasoning"},{"content":"很像Learning to Refine：Self-Refinement of Parallel Reasoning in LLMs和Parallel-R1：Towards Parallel Thinking via Reinforcement Learning。这篇是通过rl来训练总结的能力。\n论文深度解读 这篇由 FAIR at Meta 和 CMU 的研究者们发表的论文，直面了当前大语言模型（LLMs）在解决复杂推理任务时的一个核心挑战。长久以来，为了提升模型在数学、编程等高难度任务上的表现，学术界和工业界普遍采用一种测试时计算扩展（test-time compute scaling）的策略。这种策略的本质很简单：与其让模型只生成一次答案，不如让它独立生成多个（例如100个）候选解决方案，然后通过某种机制从中选出最好的一个。最流行、最简单直接的机制就是多数投票（majority voting），也被称为自洽性（Self-Consistency）。它的逻辑是“真理掌握在多数人手中”，出现次数最多的答案最可能是正确答案。\n然而，本文的标题一针见血地指出了该方法的局限性——“多数并非总是正确”。论文作者观察到，在许多困难问题上，正确的解决方案可能非常精巧或不寻常，导致模型只有很小的概率能生成它。在这种情况下，正确的答案往往是“少数派”，而被淹没在大量相似但错误的“多数派”答案中。此外，多数投票机制完全忽略了那些虽然最终答案错误，但中间步骤包含正确推理的候选方案，错失了“取其精华、去其糟粕”的机会。\n基于这一洞察，论文提出了一个全新的范式：将“答案聚合”本身视为一种需要学习的推理技能，而非一个固定的、基于规则的启发式方法。他们设计的核心方法名为 AGGLM (Aggregation Language Model)。其工作流程如下：\n生成候选方案：首先，使用一个基础的LLM（论文中称为 solution model）针对一个问题（如一道数学题）生成多个独立的候选解决方案。 聚合与综合：然后，将问题描述和所有这些候选方案一同输入给另一个专门训练过的模型，即 AGGLM。AGGLM 的任务不是简单地从中挑选一个，而是被指令去“审阅、核对、并综合（review, reconcile, and synthesize）”这些方案，最终生成一个全新的、质量更高的最终答案。 通过强化学习进行训练：这套方法的精髓在于 AGGLM 是如何被训练的。作者采用了基于可验证奖励的强化学习（Reinforcement Learning from Verifiable Rewards, RLVR）。在数学这类任务中，“奖励”的定义非常明确：最终答案是否与标准答案一致。如果 AGGLM 综合出的答案是正确的，它就得到一个正奖励（+1），否则就是零奖励。通过这种方式，AGGLM 在训练中不断试错，逐渐学会如何识别并整合不同方案中的正确部分，修正错误，甚至在所有候选方案都错误的情况下，通过汲取它们的“思想火花”来构建出一个全新的正确解法。正如论文附录中的Example A所示，即使所有输入解法都错了，AGGLM 也能综合出一个正确答案。 Example A: No correct solutions were included in the prompt; a new, correct solution synthesized by AggLM-1.7B.\nProblem: … [Multiple incorrect candidate solutions are provided] AggLM-1.7B Output: … ### Step 1: Understand the Inequalities We simplify the inequalities using the plane equation x + y + z = 75… … a + b = 507 + 3 = 510\n一个尤为关键的训练细节是，作者精心设计了训练数据的构成。他们将训练样本分为两类：“简单样本（easy examples）”（多数投票结果正确的样本）和“困难样本（hard examples）”（多数投票结果错误的样本，即正确答案是少数派）。实验发现，只用困难样本训练会导致模型过于激进，而在只用简单样本训练则无法学会处理关键的少数派正确情况。最终，他们通过混合所有困难样本和一部分（50%）简单样本，达到了最佳的平衡，使得模型既能处理常规问题，又擅长攻克难题。\n在实验验证部分，论文的论证极为扎实。他们使用了四个极具挑战性的数学竞赛数据集（AIME24, AIME25, HMMT24, HMMT25）进行评测。结果显示，他们训练的 AggLM-1.7B 模型在所有数据集上都显著超越了所有基线方法。例如，在 AIME25 数据集上，基础模型 Qwen3-1.7B 的 pass@1 准确率（单次生成正确率）为 35.68%，传统的多数投票方法能将其提升到 45.89%，而 AggLM-1.7B 则达到了惊人的 50.00%。这不仅证明了方法的有效性，更体现了其卓越的效率——AggLM-1.7B 在仅聚合8个候选解时的表现，甚至优于多数投票聚合16个候选解的表现（见论文 Figure 3），这意味着它能用更少的计算成本达到更高的准确率。\n此外，论文还展示了 AggLM-1.7B 强大的泛化能力。即便它只在 1.7B 模型产生的答案上进行训练，当把它用于聚合一个更强大的 8B 模型产生的答案时，它依然是表现最好的方法。这说明 AGGLM 学到的是一种通用的、跨模型的答案聚合与推理能力，而非仅仅记住了特定模型的输出模式。\n总而言之，这篇论文的核心贡献在于，它将答案聚合从一个简单的后处理步骤，提升到了一个可以通过学习获得的、核心的推理能力层面。它不仅提出了一个效果卓越、计算高效、泛化能力强的 AGGLM 模型，更重要的是，它为如何突破现有LLM性能瓶颈提供了一个全新的、极具潜力的思路：与其盲目扩大模型规模，不如教会模型如何更“聪明”地利用和整合自己已有的知识。\n接下来，我将按照您提出的六个问题，进行更详细的剖析。\n### 1. 论文的研究目标与实际意义 研究目标： 本文的核心研究目标是设计并验证一种新的方法，用于有效聚合大语言模型生成的多个候选解决方案，从而在复杂的推理任务上取得比现有方法（如多数投票）更高的准确率。它旨在将聚合过程本身从一个固定的启发式规则，转变为一个通过强化学习训练出来的动态推理技能。\n解决的实际问题： 该研究直接解决了在实际应用中如何最大化“测试时计算”投入回报的问题。目前，为了让LLM解决一个难题，我们常常会让它“多想几次”（即生成多个答案），这会消耗大量的计算资源。但如果最后聚合答案的方法很原始（如多数投票），就可能导致：\n浪费正确信息：一个正确的“少数派”答案被错误的“多数派”答案覆盖。 无法组合优点：多个答案可能各自只对了一部分，但投票机制无法将它们的正确部分拼接成一个完整的正确答案。 AGGLM方法正是为了解决这种资源利用效率低下的问题，确保每一次额外的计算投入都能被更智能地转化为性能的提升。 行业重要意义： 这项研究对于整个大模型行业具有重要的战略意义。随着模型规模越来越大，训练成本呈指数级增长。因此，如何在不增加模型参数的情况下，通过更优的推理时策略（inference-time strategy）来压榨模型性能，成为一个极具性价比的技术方向。AGGLM展示了一条非常有前景的路径：即训练一个相对较小的“专家”模型（聚合器），来指挥和整合一个或多个大型“生成器”模型的工作成果。这为模型部署和优化提供了新的思路，即通过“智能调度”而非“暴力堆算力”来提升系统上限。\n### 2. 论文提出的新思路、方法与优势 新思路与方法： 聚合即推理（Aggregation as Reasoning）：这是本文最核心的思路转变。它不再将聚合看作是一个简单的选择题（从A、B、C中选一个），而是看作一个开放式的问答题：给你这些材料（候选答案），请你综合、批判并给出一个最终的、最好的答案。 AggLM 模型：一个专门用于执行上述“聚合推理”任务的语言模型。它的输入是“问题 + 一系列候选答案”，输出是一个经过综合提炼后的新答案。 基于可验证奖励的强化学习（RLVR）：这是训练 AggLM 的关键技术。它利用任务本身具有明确正确答案的特性（如数学题），为模型的输出提供了一个清晰、客观的奖励信号。这比依赖人类反馈的RLHF成本更低、效率更高、扩展性更强。 平衡的训练数据策略：论文明确提出并验证了在训练中混合“简单”和“困难”样本的重要性。这是一个非常实用的工程经验，确保模型既不会在简单问题上“退化”，又具备解决核心难题的能力。 与之前方法的比较与优势： 对比多数投票/自洽性： \u0026gt; But, as we will see, majority-based aggregation can overlook valuable minority solutions: correct answers are sometimes assigned low probability under the model due to modeling errors. 优势在于，AGGLM 能够发现并采纳“少数派”的正确答案，并且能通过识别和组合不同方案中的正确推理步骤来合成一个全新的答案，这是投票机制完全无法做到的。\n对比基于奖励模型的选择（Best-of-N）： 一些方法会训练一个奖励模型（Reward Model, RM）来给每个候选答案打分，然后选择分数最高的那个。 \u0026gt; This recombines frequency with a learned notion of quality, but it sometimes introduces more regression errors than improvements. 优势在于，AGGLM 不仅仅是“选择者”，更是“创造者”。奖励模型只能在现有答案中进行排序，其天花板受限于生成的所有答案中最好的那一个。而 AGGLM 则有可能创造出比所有原始候选方案都更好的答案。此外，训练一个能精确评估复杂推理过程的奖励模型本身就极其困难。\n对比简单的提示聚合（Prompted Aggregation）： 即用一个未经专门训练的通用大模型，通过提示词让它去聚合答案。 论文通过实验（见Table 1，Qwen3-1.7B vs AggLM-1.7B）证明，虽然提示有一定效果，但经过RLVR专门训练的 AggLM 性能要强大得多。这说明聚合这种高级推理能力是需要通过针对性训练才能充分激发的。\n### 3. 实验设计与结果分析 实验设计： 数据集：选用了 MathArena 基准中的四个现代数学竞赛数据集：AIME24, AIME25, HMMT24, HMMT25。这些数据集的特点是问题难度大、解法多样，且有唯一的数值答案，非常适合进行“可验证奖励”的实验。 评估协议（Protocol）：为了保证结果的稳健性，作者设计了严谨的评估流程。对每个问题，独立采样128个解，然后将其分成16个组，每组8个解。对每一组，他们用不同的聚合方法进行处理，并计算 pass@1 准确率，最后将16组的结果平均，得到该问题的最终得分。这种分组求平均的方法可以有效降低随机性带来的误差。 基线模型（Baselines）：实验设置了非常全面的基线，包括： 基础性能：pass@1 (原始模型单次生成) 和 pass@8 (8个答案中只要有1个正确就算对，代表理论上限)。 传统方法：Majority Voting。 先进方法：Best-of-N (使用强大的 AceMath 7B 和 72B 奖励模型) 和 Weighted Majority (结合奖励模型分数的加权投票)。 消融研究：Prompted Aggregation (使用未训练的 Qwen3-1.7B 模型进行聚合，以验证训练的必要性)。 实验数据与结果： 实验结果有力地证明了 AGGLM 的优越性。以下是 Table 1 中，在与训练数据同分布的 Qwen3-1.7B 模型上的核心数据摘要： 聚合模型 (Aggregation Model) AIME24 AIME25 HMMT24 HMMT25 基线 (Baselines) pass@1 (无聚合) 50.91 35.68 22.45 22.84 多数投票 (Majority Voting) 67.92 45.89 29.01 26.72 我们的方法 RL训练的聚合 (AggLM-1.7B) 70.69 50.00 33.34 32.07 从上表可以清晰地看到： 1. **显著超越**：在所有四个数据集上，`AggLM-1.7B` 的性能均**全方位超越**包括多数投票和基于强大奖励模型的选择在内的所有基线方法。 2. **提升巨大**：相较于最常用的多数投票，`AggLM-1.7B` 带来了 3 到 5 个百分点的绝对提升，这在极具挑战性的数学竞赛任务上是非常显著的成果。 3. **泛化性强**：`Table 2` 的数据显示，当聚合更强的 `Qwen3-8B` 模型生成的解时，`AggLM-1.7B` 依然是表现最好的方法，证明了其学习到的聚合能力是可迁移的。 ### 4. 未来研究方向、挑战与机会 值得探索的问题与挑战： 推广到非可验证领域：本文最大的成功依赖于数学任务有明确的“对/错”之分。未来最大的挑战是如何将 RLVR 的思想推广到奖励模糊或主观的领域，例如： 代码生成：如何定义一段代码是“好”的？（可能需要结合编译通过、单元测试、效率、可读性等多个指标） 长文写作：如何评估一篇综合多个来源的文章的质量？（可能需要训练一个更复杂的、基于 AI 或人类偏好的奖励模型） 多模态任务：如何聚合生成的图像或视频？ 聚合器与生成器的协同进化：本文中生成器是固定的。一个更高级的系统可以是，聚合器在聚合过程中发现的常见错误或优秀推理模式，可以被蒸馏（distill）回生成器模型中，从而让生成器本身也变得更强大，形成一个良性循环。 动态候选集大小：本文固定了聚合的候选解数量（如k=8）。未来的研究可以探索让模型动态决定需要生成多少个候选解，当置信度高时少生成，置信度低时多生成，以进一步优化计算效率。 新的技术和投资机会： 推理即服务（Inference-as-a-Service）的增值层：云服务商或AI公司可以提供一种“性能增强”服务。客户使用标准的大模型API，但可以选择开启“聚合推理”模式，在后端通过类似AGGLM的机制，以稍高的延迟和成本换取显著提升的准确率。 面向特定领域的“超级优化器”：可以针对金融、法律、医疗等垂直领域，训练专门的聚合模型。这些模型不仅能提升答案准确性，甚至可以确保输出内容符合行业规范和逻辑，成为专业人士的得力助手。 AI Agent 框架的决策核心：在复杂的 Agent 系统中，一个任务可能被分解给多个子 Agent 并行处理。AGGLM 的思想可以被用来设计一个“总指挥”Agent，负责整合各个子 Agent 的方案，做出最终的、最鲁棒的决策。 ### 5. 批判性视角：论文的不足与缺失 尽管这是一篇非常出色的论文，但从批判的角度看，仍存在一些可以探讨的局限性：\n对可验证奖励的强依赖：如前所述，这是该方法最大的限制。它在数学、部分科学计算和有严格测试用例的编程任务上表现优异，但其向更广泛的开放式、创造性任务的可迁移性并未被证明，仍是一个开放问题。 训练成本与复杂性：虽然推理时可能更高效，但训练 AGGLM 本身需要一个复杂的 RL 流程，包括大量样本生成、奖励计算和策略优化。论文没有详细讨论训练这样一个聚合器需要多大的计算开销和时间成本。 聚合器规模的探讨不足：实验中，生成器和聚合器都是 1.7B 规模。一个有趣的问题是：一个小规模的聚合器（如1.7B）能否有效聚合一个超大规模模型（如100B）的输出？还是说聚合器必须与生成器规模相当才能充分理解其推理过程？这方面的 scaling law 值得进一步研究。 “可解释性”问题：AGGLM 是一个黑箱。虽然它能给出好答案，但我们很难确切知道它是如何“取舍”和“组合”输入信息的。如果它合成了一个看似正确但实际上有微小错误的答案，其错误的根源将难以追溯。 ### 6. 核心启发与知识补充 我应该从这篇论文中重点学什么？ 范式转变的思维：最大的启发是“将后处理步骤看作可学习的关键模型组件”。在你的工作中，任何你使用启发式规则（hard-coded rules）来处理模型输出的地方，都可以思考一下：这个规则能否被一个小的、专门训练的模型所替代？ 数据驱动的解决方案：AGGLM 的成功很大程度上归功于其精巧的数据策略（平衡简单/困难样本）。这提醒我们，在很多时候，智能地构建和平衡训练数据，其效果可能比调整模型架构或优化算法更显著。 “小模型撬动大模型”的杠杆作用：你不一定总需要一个巨大的、端到端的模型。一个“大模型（生成器）+ 小模型（协调器/聚合器）”的组合架构，可能在成本和效益上达到更好的平衡。 我还需要补充了解哪些背景知识？ 为了完全理解并应用这篇论文的思想，建议您了解以下背景知识： 思维链（Chain-of-Thought, CoT）与自洽性（Self-Consistency）：这是本文所要改进的基础技术。理解它们的工作原理和局限性，才能更好地体会 AGGLM 的创新之处。可以阅读 Google 的原始论文 Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 和 Self-Consistency Improves Chain of Thought Reasoning in Large Language Models (https://arxiv.org/abs/2203.11171)。 强化学习基础与 PPO/GRPO 算法：理解强化学习的基本概念（策略、奖励、价值函数）和近端策略优化（PPO）算法，因为它是当前训练LLM最主流的RL算法之一。本文使用的 GRPO (Group-Relative Policy Optimization) 是其变种。 奖励模型（Reward Modeling）：了解在 RLHF（基于人类反馈的强化学习）中，奖励模型是如何被训练和使用的。这将帮助你理解 AGGLM 与传统 Best-of-N 方法的根本区别。 希望这份详尽的解读能帮助您深入理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-09-10T00:00:00Z","permalink":"/the-majority-is-not-always-rightrl-training-for-solution-aggregation/","title":"The Majority is not always right：RL training for solution aggregation"},{"content":"类似Beyond Ten Turns：Unlocking Long-Horizon Agentic Search with Large-Scale Asynchronous RL的复杂问题的思路来构建数据集，从原始问题出发构建更复杂的query和调用工具查询的过程，用来sft和rl。也有点像逆向工程。\n论文深度解读：WebExplorer的核心贡献与意义 这篇于2025年9月发表的论文，直面了当前开源大型语言模型（LLM）在构建高级网页代理（Web Agent）时的一个核心瓶颈：高质量、高难度训练数据的极度稀缺。现有方法要么依赖昂贵且难以扩展的人工标注，要么自动生成的数据过于简单，无法训练出能解决复杂、长时序任务的代理。论文作者敏锐地指出，真正的挑战并非模型结构或训练算法，而是如何自动化、大规模地创造出能迫使模型进行深度推理和探索的难题。\n为此，论文提出了一个名为 WebExplorer 的创新性数据合成框架。其核心思想分为两个优雅且互补的阶段：\n模型驱动的探索 (Model-Based Exploration)：该阶段摒弃了以往基于规则、结构僵化的图构建方法，转而利用一个强大的LLM本身作为探索者。从一个“种子实体”（例如一个历史人物或一个事件）出发，LLM通过自主进行多轮的“搜索”和“浏览”行为，在广阔的互联网信息空间中发现不同事实之间的隐藏关联。正如论文图3所示，从“巴西国家队”这个种子出发，模型自主地关联到了“1950年世界杯的观众人数记录”、“该场比赛裁判的生平”以及“另一场比赛中一位关键球员的家乡”等多个分散的信息点。最后，模型将这些信息点“编织”成一个初始的、需要跨网页推理才能解答的问答（QA）对。这种方法的优势在于其极高的灵活性和自主性，能够模拟人类专家进行研究时的探索过程。\n迭代式查询演化 (Iterative Query Evolution)：这是论文最颠覆性的创新点。传统的“数据增强”思路通常是“由简到难”（short-to-long），即通过增加约束条件来提升问题难度。而WebExplorer反其道而行之，提出了一种“由繁化简” (long-to-short) 的演化策略。它拿到第一阶段生成的、信息相对明确的初始问题后，通过一系列精心设计的步骤系统性地提升其模糊性和难度。论文中提到，具体操作包括：\n…refine queries through three strategic directions: (1) removing salient information, (2) introducing strategic obfuscation to specific details such as dates, locations, and proper names, and (3) identifying alternative descriptions to replace original explicit references.\n例如，将一个明确的线索“这位球员在44岁时去世”替换为更模糊的描述，或者将“击败了曼联队”替换为“击败了一支顶级联赛的豪门球队”。这种“信息剥离”和“战略性模糊化”的操作，使得问题无法再通过简单的关键词搜索解决，迫使代理必须进行更广泛的探索和更深层次的逻辑推理。论文在Table 1中用数据证明了这一过程的有效性：使用强大的Claude-4-Sonnet模型进行测试，初始QA的准确率高达86.6%，而经过演化后的QA，其准确率骤降至67.1%，同时平均解决步骤（tool calling turns）从7.9轮增加到9.9轮，这有力地证明了演化后的数据确实更具挑战性。\n基于这套创新的数据合成管线，论文构建了WebExplorer-QA数据集，并用其对一个80亿参数（8B）的模型进行了监督微调（SFT）和强化学习（RL）。最终训练出的 WebExplorer-8B 模型，在多个权威的网页代理基准测试中取得了惊人的成果。最引人注目的是，如Table 2所示，它在极具挑战性的BrowseComp-en基准上取得了15.7%的准确率，不仅远超同规模的最佳模型（MiroThinker-8B的8.7%），甚至击败了体量是其9倍的WebSailor-72B模型（12.0%）。这一“以小博大”的成就，雄辩地证明了训练数据质量对于模型能力的关键性影响，远胜于单纯地堆叠模型参数。\n总而言之，WebExplorer的贡献是方法论层面的。它为如何解决AI代理的“高级认知能力训练”问题提供了一套可行的、自动化的解决方案，其核心的“探索-演化”思想，特别是“long-to-short”的查询构建范式，为整个领域开辟了新的思路。\n接下来，我将按照您提出的六个方面，对论文进行更详细的剖析。\n一、研究目标与实际问题 研究目标：论文的核心研究目标是开发一种能够自动化、大规模合成高质量、高难度训练数据的方法，从而训练出能够在长时序、复杂信息检索任务中表现出色的长时序网页代理（Long-Horizon Web Agents）。\n想要解决的实际问题：\n数据稀缺性：当前用于训练高级网页代理的数据集要么规模小、成本高（如人工标注的GAIA），要么过于简单，无法培养模型的深度推理能力。 现有数据合成方法的局限： 图构建法：流程复杂，依赖大量启发式规则，灵活性差。 “由简到难”的演化法：通过不断添加信息来增加难度，容易产生不自然、冗长的查询。 开源模型能力不足：现有开源的网页代理在面对如BrowseComp这类由顶尖机构（如OpenAI）设计的挑战性基准时，表现普遍不佳，与最强的商业闭源模型存在巨大差距。 问题的重要性：网页代理是实现通用人工智能（AGI）的关键一步。一个强大的网页代理可以扮演自动化研究员、深度事实核查工具、高级个人助理等多种角色。解决其训练数据瓶颈，意味着可以显著降低训练高性能代理的门槛，加速整个AI代理技术的发展和普及，从而在科研、教育、商业分析等众多领域释放巨大潜力。\n二、核心思路与方法创新 论文的核心创新在于其两阶段的数据合成框架 WebExplorer。\n新的思路与模型： 模型驱动的探索 (Model-Based Exploration) 思路：利用LLM的自主探索能力来构建知识图谱和生成初始问题，而不是依赖外部的、基于规则的系统。 特点与优势： 高自主性：模型可以根据探索到的信息动态调整下一步的搜索和浏览策略，过程更灵活、更接近人类的探索模式。 深度关联发现：能够发现跨越多个主题和网页的非显性关联，从而创造出需要多步推理的复杂问题。论文图3中关于巴西足球的例子完美地诠释了这一点，它将世界杯记录、裁判生涯和球员背景三个看似无关的领域联系了起来。 迭代式查询演化 (Iterative Query Evolution) 思路：采用一种反直觉的“long-to-short”策略，通过系统性地移除和模糊化初始查询中的明确线索来提升问题难度。 特点与优势： 模拟真实世界难题：真实世界中的难题往往信息模糊、线索隐藏。这种方法创造出的查询更接近这种形态，能更好地训练代理的探索和鲁棒推理能力。 强制深度探索：由于缺乏明确的“搜索入口”，代理不能依赖简单的关键词匹配，必须进行更多轮次的试探性搜索和信息整合。 自然性：相比于不断堆砌信息，这种“提炼”和“模糊化”的方式更容易生成语法流畅、逻辑自然的复杂问题。论文中足球问题的演化例子（从“Manchester United”到“a First Division giant”）生动地展示了这一点。 与之前方法的对比 对比图构建法 (如 Li et al., 2025a; Tao et al., 2025)：WebExplorer的方法更灵活、更自动化，避免了复杂的节点扩展和选择策略设计。 对比传统演化法 (如 Gao et al., 2025; Wu et al., 2025a)：WebExplorer的“long-to-short”策略是根本性的不同，它旨在通过增加“模糊性”而非“信息量”来提升难度，从而创造出更具挑战性和真实性的问题。 三、实验设计与结果分析 论文设计了周密的多层次实验来验证其方法的有效性。\n实验设计： 数据质量验证：首先，论文需要证明其“迭代式查询演化”确实提升了数据难度。他们使用了一个强大的第三方模型（Claude-4-Sonnet）来分别解答演化前（Initial QA）和演化后（Evolved QA）的数据，并比较其准确率和所需的工具调用次数。 模型性能评估：其次，使用合成的WebExplorer-QA数据集训练出的WebExplorer-8B模型，在多个公认的、多样化的信息检索基准上进行测试，并与当前最先进的开源和闭源模型进行全面对比。 训练动态分析：最后，通过可视化强化学习（RL）过程中的关键指标（如平均工具调用次数、轨迹长度和任务性能），来展示模型是如何在训练中逐步学会更复杂推理的。 实验数据和结果： 数据质量验证（Table 1）： 关键数据：在Claude-4-Sonnet上，初始QA的准确率为86.6%，而演化后QA的准确率降至67.1%。同时，平均工具调用次数从7.9次增加到9.9次。 结论：这组数据强有力地证明了“查询演化”过程的成功，它显著增加了问题的复杂度和推理深度。 模型性能评估（Table 2）： 关键数据1（性能超越）：WebExplorer-8B (RL) 在BrowseComp-en上得分15.7%，显著高于参数量大得多的WebSailor-72B (12.0%)。 关键数据2（同级最佳）：在所有参数量小于100B的开源模型中，WebExplorer-8B在BrowseComp-en/zh, WebWalkerQA, 和 FRAMES等多个基准上均取得了最佳性能 (SOTA)。 关键数据3（泛化能力）：尽管训练数据非STEM导向，WebExplorer-8B在学术基准HLE上取得了17.3%的成绩，优于之前的32B模型，展示了其强大的泛化能力。 训练动态分析（Figure 5）： 关键数据：在RL训练过程中，模型的平均工具调用次数从约11次稳步增长到超过16次，轨迹长度（tokens）也随之增长到超过40K。与此同时，在BrowseComp基准上的性能也持续提升。 结论：这表明RL训练成功地激励模型进行更长、更复杂的推理，并且这种复杂度的提升与最终性能的提高有很强的正相关性。 四、未来方向与潜在机会 结合当前大模型领域的发展趋势，该研究方向充满机遇和挑战。\n值得探索的问题和挑战： 多模态代理：当前WebExplorer仅处理文本信息。未来的网页代理必须能够理解和操作包含图片、视频、复杂UI布局的多模态网页。如何将这套“探索-演化”框架扩展到多模态领域是一个巨大的挑战。 自进化数据管线：能否让代理在解决问题的过程中，自主判断哪些问题是“高质量”的，并利用这些经验反过来优化其数据合成过程，形成一个自增强的闭环（flywheel effect）？ 工具使用扩展与规划：除了search和browse，未来的代理需要掌握更复杂的工具（如代码执行器、数据库查询、API调用）。如何让代理在更庞大的工具集中进行高效规划，是一个亟待解决的问题。 鲁棒性与安全性：如何让代理在面对充满错误信息、动态变化的网站时保持鲁棒？如何为其行为建立可靠的安全边界，防止执行有害操作？ 新的技术和投资机会： 合成数据即服务 (Synthetic Data-as-a-Service)：提供高质量、特定任务导向的代理训练数据，可能会成为一个新的商业模式。 下一代AI助理/数字员工：基于这类技术训练出的高级代理，可以催生出能力远超当前产品的AI个人助理和企业级自动化解决方案（数字员工）。 代理开发与训练平台：降低构建、训练和部署高性能代理门槛的平台，将具有巨大的市场潜力。 自动化知识发现：将这类代理应用于科研领域，可能加速新材料、新药物的发现过程，带来巨大的科学和社会价值。 五、批判性视角与潜在不足 从审慎的学术角度看，这篇论文虽然出色，但仍存在一些可以探讨的局限性。\n数据源多样性问题：论文提到其种子实体主要来自维基百科（Wikipedia）。这可能会导致生成的数据在主题和风格上存在一定的偏向性，即“维基百科风格”的问答。训练出的模型在处理非百科类、更口语化或专业性极强的领域（如社交媒体、专业论坛）时，泛化能力可能需要进一步验证。\n评估方式的局限性：论文大量使用了LLM-as-Judge（以LLM作为评估者）的方式来评判答案的正确性。尽管这是当前领域的通行做法，但评估模型本身也存在偏见和错误率。缺乏大规模、专业的人工评估，使得实验结果的绝对可靠性尚存一丝疑问。\n工具集的简化：代理的行动空间仅限于search和browse两个工具。这极大地简化了真实世界的网页交互，忽略了点击按钮、填写表单、处理登录、与动态JavaScript元素交互等复杂操作。因此，模型在“沙盒环境”中的成功，能否完全迁移到真实的、复杂的网络环境中，还需要打个问号。\n数据合成的成本：论文使用强大的商业模型（如OpenAI和Claude的模型）来执行数据合成的两个阶段。这意味着其数据生成过程的计算成本可能相当高昂，对于资源有限的学术机构或初创公司而言，复现和扩展这一工作的门槛不低。\n六、核心启发与知识补充 对于希望从中学习和借鉴的研究者或开发者而言，这篇论文提供了宝贵的启示。\n重点学习与启发： 数据中心AI思想 (Data-centric AI)：这篇论文是数据中心AI思想的绝佳范例。它告诉我们，在模型能力提升上，与其无止境地追求更大的模型，不如将焦点放在如何创造更高质量的训练数据上。数据质量的杠杆效应可能远超模型规模。 “由繁化简”的逆向思维：这是最核心的创新点。在设计难题时，不妨试试“信息剥离”和“增加模糊性”的策略，而不是一味地“增加约束”。这种思维范式可以应用于许多其他领域的难题构造和数据增强任务。 利用模型自身进行探索：让LLM自主探索和构建知识关联，是比设计复杂规则更优雅、更具扩展性的方法。这启发我们在其他任务中，也可以更多地利用LLM的自主性来解决数据生成和预处理问题。 SFT+RL的经典范式：监督微调（SFT）用于教会模型基本技能和范式，强化学习（RL）用于在探索中突破能力上限，实现更复杂的行为。这个“先模仿，再探索”的训练路径被证明是非常有效的。 需要补充的背景知识： 代理（Agent）的ReAct框架：了解ReAct (Reason + Act) 框架（Yao et al., 2023）是理解这篇论文工作流程的基础。它是一种让LLM通过“思考-行动-观察”的循环来解决问题的标准范式。 强化学习算法 (GRPO)：虽然不必深究算法细节，但了解强化学习（特别是基于策略优化的算法，如PPO、DPO以及本文的GRPO）在LLM微调中的作用，有助于理解模型为何能在RL阶段实现能力跃升。 主流代理基准 (Agent Benchmarks)：熟悉如GAIA、BrowseComp等基准测试，可以帮助您更好地理解WebExplorer所取得成就的含金量，以及当前领域评估代理能力的标准。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-09-10T00:00:00Z","permalink":"/webexplorerexplore-and-evolve-for-training-long-horizon-web-agents/","title":"WebExplorer：Explore and Evolve for Training Long-Horizon Web Agents"},{"content":"这个好像才是experience engieerning的鼻祖。没想到去年9月就出现了，从经验中学习。\nimage.png 首先，我将为您提供一个对论文整体的深入解读，帮助您建立一个全面的认识。\n论文深度解读 这篇论文发表于2024年9月，直面了当前AI智能体领域的一个核心痛点：泛化能力与持续学习能力的缺失。尽管基于大语言模型的智能体在很多任务上表现出色，但它们往往像一个“健忘的初学者”，每次都从零开始解决问题，难以从过去的经验中吸取教D_訓并举一反三。当任务变得复杂，需要一长串精确操作时，这种模式的失败率会急剧上升。\n作者敏锐地观察到，人类专家之所以高效，并非因为他们每一次都重新思考，而是因为他们大脑中存储了大量针对特定子任务的“工作流程”（Workflows）。比如，一个程序员调试代码时，会下意识地遵循“查看日志 -\u0026gt; 定位错误行 -\u0026gt; 设置断点 -\u0026gt; 检查变量”等一系列流程。这篇论文的精髓，就是尝试为AI智能体构建一套类似的机制。\nAWM的核心机制可以分解为三个步骤，这在论文的图2中有清晰的展示： 1. 执行与体验 (Execution \u0026amp; Experience)：智能体首先尝试解决一个任务（例如，在网站上预订机票），这个过程中的所有观察和行动被记录下来，形成一个“轨迹”（Trajectory）。 2. 评估与归纳 (Evaluation \u0026amp; Induction)：任务完成后，一个评估模块会判断任务是否成功。如果成功，一个“归纳模块”（Induction Module）会被激活。这个模块（本身也是一个LM）会分析成功的轨迹，并将其“抽象化”和“泛化”。例如，将具体操作 type(\"New York\") 抽象为 type(\"{destination_city}\")，并为这个流程生成一个高级描述，如“预订从A到B的航班”。这个抽象化的流程就是工作流程（Workflow）。 3. 记忆与应用 (Memory \u0026amp; Application)：新生成的工作流程被存入智能体的“工作流程记忆”（Workflow Memory）中。在未来，当智能体接到新任务时，这些记忆里的工作流程会作为高级“技能”或“工具”出现在它的“思考”上下文中，引导它更直接、更高效地规划行动。\n这篇论文最大的亮点在于提出了两种AWM的应用模式： * 离线模式 (Offline Scenario)：智能体可以从一个预先收集好的、带有人工标注的成功案例数据集中学习工作流程。这类似于一个学生在上岗前接受系统培训。 * 在线模式 (Online Scenario)：这是更具突破性的一种模式。智能体在没有任何预先训练数据的情况下，直接处理真实的任务。它从自己每一次成功的尝试中即时学习、即时归纳，动态地扩充自己的工作流程记忆。这就像一个在岗员工，通过“干中学”不断积累经验，变得越来越熟练。论文中的图1生动地展示了这种模式的效果：随着处理的任务增多，AWM智能体的成功率持续攀升，与没有学习能力的基线模型拉开了巨大的差距。\n在实验验证部分，论文选取了两个极具挑战性的Web智能体基准测试平台：WebArena 和 Mind2Web。在WebArena上，AWM相比于当时最先进的自主智能体基线 BrowserGym，实现了高达 51.1% 的相对成功率提升，甚至超越了依赖人类专家预先编写工作流程的 SteP 方法。具体数据显示，AWM的总成功率达到了35.5%，而BrowserGym仅为15.0%，SteP为33.0%。这充分证明了AWM自主学习工作流程的强大能力。在强调泛化能力的Mind2Web测试中，尤其是在最困难的跨领域（Cross-Domain）测试上，在线AWM的表现远超基线，显示出其强大的适应未见任务的能力。\n总而言之，这篇论文不仅仅是提出了一种技术上的改进，更是为AI智能体的发展提供了一种极具前景的认知框架。它让智能体从一个简单的“指令执行者”向一个能够积累经验、持续成长的“学习型专家”转变，这对于推动AI智能体走向更复杂、更动态的真实世界应用具有里程碑式的意义。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解分析。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升语言模型智能体在处理长时序、复杂任务时的鲁棒性、效率和泛化能力。 解决的实际问题： 缺乏从经验中学习的能力：当前的智能体大多是“一次性”的，它们不会从过去成功或失败的经验中学习。正如论文引言中所说： \u0026gt; Moreover, as agents solve each task separately, they do not learn from past successes and failures, and are therefore unable to adapt over time. 难以泛化到新任务和新环境：智能体在训练过的相似任务上表现尚可，但一旦任务情境、网站界面或领域发生变化，性能就会急剧下降。它们难以提取出可跨任务、跨环境复用的核心技能。 效率低下：由于缺乏高级的行动“宏”或“子程序”，智能体需要一步一步地进行低级的思考和规划，导致完成复杂任务的步骤冗长且容易出错。 对行业发展的重要意义： 推动AI智能体走向实用化：目前，AI智能体在演示中看起来很酷，但在实际应用中（如自动化办公、软件测试、个人助理）却常常因不够可靠而受限。AWM这类能够持续学习和提高的机制，是弥合“演示”与“实用”之间鸿沟的关键。 降低智能体开发的门槛：通过在线自主学习，AWM减少了对大量人工标注训练数据的依赖，也避免了需要领域专家手动编写复杂规则或工作流程的麻烦。这使得为新领域、新任务部署AI智能体变得更加快捷和经济。 构建更强大的AI生态：一个能够不断学习和积累“技能”的智能体，可以看作一个不断成长的平台。未来，这些智能体甚至可以分享、交易它们学习到的工作流程，形成一个知识共享网络，极大地加速整个AI智能体生态的成熟。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路：核心思路是模仿人类从经验中抽象出可复用“工作流程”的认知过程，并将其作为智能体的一种动态、可扩展的记忆。\n新的方法/模型：AWM (Agent Workflow Memory) 框架。其核心组件包括：\n工作流程的表示 (Workflow Representation)：将一个复杂任务的解决方案，抽象成“高级描述 (description)” + “泛化的动作序列 (generalized trajectory)”两个部分。这种表示既易于模型理解，又具备了跨任务复用的潜力。 基于LM的归纳模块 (LM-based Induction)：利用大语言模型自身强大的理解和泛化能力，从具体的成功轨迹中自动生成抽象的工作流程。这是一个“以子之矛，攻子之盾”的巧妙设计。 在线、无监督的学习范式 (Online, Supervision-free Setting)：这是AWM最突出的特点。智能体可以在实际工作中“边做边学”，无需额外的训练数据，实现自我进化。 与之前方法的比较、特点和优势：\n特点 之前的方法 (如 Synapse, In-context Learning) AWM (Agent Workflow Memory) 优势分析 记忆内容 具体的、完整的成功案例（Concrete examples） 抽象的、可复用的子程序（Abstract sub-routines） 更强的泛化性。具体案例容易让模型过拟合到无关细节，而抽象流程更能抓住任务本质。 记忆构建方式 静态的、预先提供的（Static, pre-defined） 动态的、持续增长的（Dynamic, growing） 更强的适应性。AWM能适应新任务、新环境，并通过在线学习不断扩充自己的能力圈。 知识来源 依赖高质量的标注数据或人类专家（Human-dependent） 可从自身成功经验中自主学习（Self-supervised） 更低的部署成本和更高的自主性。摆脱了对人力的强依赖，实现了智能体的自我完善。 工作模式 主要是离线学习 兼具离线和在线学习模式 更高的灵活性。既可以利用已有数据快速启动，也可以在实际应用中持续优化。 例如，论文在 WebArena 上的实验结果（Table 1）非常有说服力。AWM 取得了 35.5% 的成功率，不仅远超依赖固定示例的 BrowserGym (15.0%)，甚至超过了由人类专家编写了14个专用工作流程的 SteP (33.0%)。这证明了AWM自主学习到的工作流程，其质量和覆盖范围甚至可以优于人类的手动设计。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来全面验证AWM的有效性、泛化能力和学习效率。\n实验平台：\nWebArena：一个基于真实网站（如电商、论坛、GitLab）的基准，强调执行的正确性，评估非常严格。 Mind2Web：一个涵盖大量网站和任务的基准，强调智能体的泛化能力，包含跨任务、跨网站、跨领域三种测试设定。 实验设计与关键结果：\n总体性能验证： 设计：在 WebArena 上，将 AWM（在线模式）与多个基线方法（包括之前的SOTA模型BrowserGym和依赖人类专家的SteP）进行比较。 结果：如 Table 1 所示，AWM 的成功率（35.5%）全面领先。这证明了AWM的核心有效性。 Method Total SR (%) # Steps BrowserGym_ax-tree 15.0 7.9 *SteP (Human engineered) 33.0 - AWM (Ours) 35.5 5.9 解读：AWM不仅成功率最高，完成任务的平均步骤数也更少（5.9 vs 7.9），说明其决策更高效。 泛化能力验证： 设计：在 Mind2Web 的跨网站和跨领域测试集上，比较 AWM（在线和离线模式）与基线 MindAct 的性能。这是对智能体适应未知环境能力的终极考验。 结果：如 Table 4 所示，在最难的跨领域（Cross-Domain）设定下，AWMonline 的步骤成功率（Step SR）达到了 35.5%，而基线 MindAct 仅为 18.6%，绝对提升了 16.9 个百分点。 解读：这个结果极具说服力。它表明，当面对一个全新领域的任务时（例如，从没见过的购物网站），AWM的在线学习能力使其能够快速适应并建立有效的工作流程，而基线模型则束手无策。 学习效率验证： 设计：在 WebArena 的地图任务上，绘制 AWM 智能体累积成功率随处理任务数量变化的曲线。 结果：如论文 Figure 5 所示，曲线在前40个样本中呈现出非常陡峭的“快速学习阶段”，成功率迅速提升并随后进入一个“稳定推理阶段”。 解读：这证明AWM的学习效率非常高，不需要海量数据就能掌握核心工作流程，这在实际应用中至关重要。 工作流构建的定性分析： 设计：通过一个案例（Figure 6）展示AWM如何“逐步构建”更复杂的工作流程。 结果：智能体首先从简单任务中学会了“根据名称查找地点”的流程。当遇到更复杂的任务“获取该地点的邮政编码”时，它会复用前一个流程来先找到地点，然后再学习后续获取邮编的新步骤，从而将两个流程组合成一个更强大的新流程。 解读：这个案例直观地展示了AWM知识积累的“滚雪球效应”，是其能够解决日益复杂任务的关键。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 工作流程的动态性与条件逻辑：目前的工作流程是线性的动作序列。然而，真实世界充满不确定性。未来的研究需要探索如何学习带有条件分支（if-else）、循环（loops）和错误处理（error handling）的更复杂的工作流程。这可能需要将程序综合（Program Synthesis）的思想更深入地融入进来。 工作流程的验证与维护：在线学习可能会学到错误或次优的工作流程。如何自动验证一个新流程的正确性？如何修剪或遗忘那些过时、低效的流程，以防止记忆库臃肿和污染？这涉及到工作流程的“生命周期管理”。 多模态工作流程：AWM目前处理的是基于文本和结构化数据（如HTML）的Web任务。如何将其扩展到需要理解视觉信息（如截图、GUI图标）的多模态智能体任务中？这需要能够归纳包含视觉操作的混合流程。 长期记忆与检索：当工作流程数量巨大时，将它们全部放入模型的上下文窗口（Context Window）会变得不可行。需要研究更高效的长期记忆机制，例如将工作流程存储在外部向量数据库中，在需要时通过检索增强生成（RAG）的方式动态调用最相关的流程。 新的技术和投资机会： 企业级自动化平台：基于AWM思想，可以开发出能够自主学习和适应企业内部各种软件（如ERP、CRM、OA）操作流程的自动化机器人（RPA 2.0）。企业员工只需正常操作几次，机器人就能学会并自动执行，这将是比现有RPA更智能、更灵活的解决方案。 开放世界的游戏AI：在大型开放世界游戏中，可以部署AWM智能体作为NPC或AI队友。它们可以通过与环境和玩家的互动，不断学习新的行为模式和任务策略，创造出真正动态和不可预测的游戏体验。 个性化AI助理：未来的个人助理将不仅仅是执行命令，而是通过观察用户的日常操作，自主学习用户在各种应用（邮件、日历、社交媒体）中的个性化工作流程，从而提供真正“懂你”的主动服务。 工作流程市场（Workflow Marketplace）：可以创建一个平台，让不同的AI智能体（甚至用户）可以上传、分享、下载或交易它们学习到的高效工作流程。这将催生一个新的知识经济生态，一个为AI服务的“App Store”。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 绝对成功率仍有提升空间：尽管AWM的相对提升巨大，但在WebArena上 35.5% 的绝对成功率距离在关键任务中可靠应用还有差距。这表明Web导航任务本身具有极高的复杂度，AWM虽是巨大进步，但远非最终解决方案。 对评估模块的依赖过高：在线学习的成败完全取决于评估模块（evaluator）的准确性。如果评估模块将一个失败的轨迹错误地判断为成功，智能体就会学到一个“有毒”的工作流程，从而污染整个记忆库。论文中提到评估模块是基于LM的，但对其本身的鲁棒性和准确率讨论不足。 归纳的泛化能力边界不清：LM归纳模块的泛化能力有多强？它能否处理那些与已有流程结构迥异的全新任务？还是说它本质上更擅长对已有流程进行“排列组合”？对于真正“创造性”地解决一个全新问题的能力，需要更深入的验证。 计算成本与延迟问题：在线学习模式中，“执行-评估-归纳-记忆”的循环发生在每个任务之后，这无疑增加了计算开销和延迟。在需要快速响应的实时应用中，这种开销是否可以接受，是一个需要考虑的工程问题。 对环境变化的鲁棒性存疑：论文中的工作流程是基于UI元素（如ID）的，如果网站前端进行改版，元素ID发生变化，那么之前学习到的工作流程可能会批量失效。虽然这是所有Web智能体的共同挑战，但AWM由于依赖记忆，这个问题可能被放大。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新思想与启发： 抽象与复用是通往高级智能的关键：不要只让模型处理原始、具体的数据。思考如何设计一个机制，让模型能够自动地从具体案例中提炼出抽象的、可复用的模式或知识。这个思想可以应用在代码生成、数据分析、科学研究等多个领域。 构建自举（Bootstrapping）的自我提升循环：AWM的在线学习是一个典型的“自举”系统。智能体利用自己（成功）的输出作为下一次学习的输入，从而实现能力的螺旋式上升。在设计任何AI系统时，都可以思考：我能否建立一个反馈循环，让系统可以从自己的工作中学习和改进？ 动态记忆 vs. 静态知识：传统的AI开发模式是“训练一次，到处部署”。AWM启发我们，未来的AI系统应该是“活”的，它的知识库（记忆）应该在使用过程中不断演化和更新。这是一种从“知识注入”到“知识生长”的范式转变。 需要补充了解的背景知识： AI智能体（AI Agents）基础：了解当前主流的智能体框架，特别是基于ReAct (Reason + Act) 等思想的语言模型智能体是如何进行思考和行动的。 Web自动化与DOM：理解网页是如何由DOM（文档对象模型）树构成的，以及智能体是如何通过识别和操作DOM元素来与网页进行交互的。这将帮助你更好地理解论文实验中的具体操作。 持续学习（Continual Learning）：了解这个机器学习领域，它专门研究如何让模型在不断接收新数据的过程中学习，而不会忘记旧知识（即所谓的“灾难性遗忘”问题）。AWM的在线模式就是持续学习的一种体现。 程序综合（Program Synthesis）：这个领域研究如何自动生成满足特定需求的计算机程序。AWM中从轨迹生成工作流程的过程，可以看作是一种简单的程序综合。了解这个领域将为思考如何生成更复杂的、带逻辑的流程提供灵感。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得丰富的启发。\n","date":"2025-09-09T00:00:00Z","permalink":"/agent-workflow-memory/","title":"AGENT WORKFLOW MEMORY"},{"content":"利用dpo来压缩思维链，和Pruning Long Chain-of-Thought of Large Reasoning Models via Small-Scale Preference Optimization思路很像。本文的方法是先生成，后压缩，然后利用压缩后和压缩前的作为偏好数据训练。\n这篇论文的核心思想在于，它挑战了当前大语言模型（LLM）在进行链式思考（Chain-of-Thought, CoT）时普遍存在的“思考过度”（over-thinking）问题。目前的LLM在解决问题时，无论问题难易，往往会生成长度相近、步骤繁琐的推理过程。这不仅导致了在简单问题上的资源浪费（增加了推理延迟和计算成本），也与人类认知中“根据任务难度动态调整认知资源”的直觉相悖。\n为此，作者提出了一个创新的“难度感知链式思考蒸馏” (Difficulty-Aware Chain-of-Thought Distillation) 框架。这个框架的精髓在于“按需思考”，即训练模型学会像人类一样，对简单问题进行简洁推理，对复杂问题进行详尽思考。它并非通过修改模型架构，而是通过精心构建高质量的训练数据，向模型“教导”这种动态推理能力。\n整个流程可以分为两个核心阶段：\n第一阶段：生成难度对齐的推理数据 这个阶段的目标是创建一个“理想”的训练数据集，其中每个问题的推理过程长度都与其内在难度相匹配。 1. 难度评估 (Difficulty Estimation)：首先，论文使用一个强大的模型（如GPT-4o-mini），依据一个类似人类竞赛的标准（AoPS，Art of Problem Solving），为每个数学问题打上一个1到10的难度分数。这是一个关键步骤，它为后续的“按需压缩”提供了量化指标。 2. 长链推理生成 (Verbose CoT Generation)：接着，使用一个能力顶尖的“教师模型”（如DeepSeek-R1）为每个问题生成一个详尽、步骤完整的“长链式思考”解答。这些解答虽然正确率高，但通常非常冗长。 3. 难度感知压缩 (Difficulty-Aware Compression)：最后，再次利用GPT-4o-mini，根据第一步评估出的难度分数，对“长链式思考”进行压缩。难度低的问题会被大幅简化，只保留核心步骤；难度高的问题则会保留更多必要的推理细节。\n正如论文中所描述的：“This function adapts the verbosity of the reasoning proportionally to the problem’s difficulty—minimal steps for easy problems and more elaborate chains for hard ones—while preserving logical correctness and the final answer.”\n通过这个流程，研究者们得到了一批高质量的、推理长度与问题难度正相关的训练样本。\n第二阶段：混合式训练学生模型 有了理想的数据集后，研究者们选择更小、更高效的“学生模型”（如Qwen2.5-Math），并采用一种创新的“课程学习”策略进行训练： 1. 监督微调 (Supervised Fine-Tuning, SFT)：首先进行SFT训练，目标是让学生模型模仿压缩后的推理轨迹。这一步主要是让模型学会生成简洁、格式清晰、符合难度预期的推理“形式”和“风格”。 2. 直接偏好优化 (Direct Preference Optimization, DPO)：SFT虽然能教会模型“形似”，但有时会为了追求简洁而牺牲推理的准确性。因此，第二步引入了DPO。在DPO阶段，模型会学习一个偏好：它被告知“压缩后、正确的推理”优于“原始的、冗长的推理”。DPO是一种无需显式奖励模型的强化学习对齐技术，它通过对比“偏好”和“不偏好”的样本对，直接优化模型，使其生成更符合人类期望（在这里是既简洁又准确）的输出。\n论文强调了这种混合训练的互补性：“SFT encourages the model to mimic surface-level patterns… DPO complements this by learning from pairwise preferences, optimizing for correctness and clarity without relying on exact sequence matching.”\n实验结果有力地证明了该框架的有效性。例如，在unimodal（纯文本）数学任务的AIME基准测试中，7B参数的SFT+DPO模型相较于其基础模型，pass@1（一次生成正确率）从35.0%略微下降到31.7%，但平均token消耗从6360骤降至5724，减少了近10%。在某些任务上，它甚至能以更少的token实现更高的准确率。在GSM8K基-准上，1.5B的SFT+DPO模型用1527个token达到了82.3%的准确率，与其使用1660个token的基础模型（83.9%） 性能相当，实现了显著的效率提升。\n这一系列详实的数据表明，通过“难度感知蒸馏”和“SFT+DPO”的组合拳，模型确实学会了在保持或提升性能的同时，更智能地分配其“思考资源”。这不仅是一个技术上的创新，更是对如何构建更高效、更经济、更接近人类智能的AI系统的一次成功探索。\n接下来，我将为您逐一深入解读您提出的六个问题。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是，在不牺牲甚至提升任务性能的前提下，显著降低大语言模型在推理任务中的计算成本和延迟。 实际问题：它要解决的是当前基于链式思考（CoT） 的LLM普遍存在的“计算资源浪费”问题。具体来说，现有模型在处理所有问题时，不论难易，都倾向于生成冗长的推理步骤（即“over-thinking”）。这导致在实际应用中，尤其是在处理海量简单任务时，会产生不必要的token生成，直接转化为更高的API调用费用和更长的用户等待时间。 行业意义： 经济效益：在LLM服务大规模部署的今天，推理成本是制约其应用广度的关键瓶颈之一。根据论文引用，“推理成本将远超训练成本”。如果能让模型在保证效果的同时“少说废话”，就意味着可以为企业和开发者节省大量的运营成本，推动LLM在更多对成本敏感的场景中落地。 用户体验：对于实时交互应用（如智能客服、在线教育、编程助手），低延迟至关重要。通过生成更简洁的推理，可以大幅缩短模型的响应时间，提升用户体验。 模型智能的演进：该研究推动了模型从“机械地执行指令”向“智能化地分配资源”迈进。让模型学会“判断何时需要深入思考，何时可以快速作答”，是其认知能力和智能水平提升的重要标志，使其行为模式更接近人类。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于提出了一套端到端的Difficulty-Aware CoT Distillation框架，其新颖之处体现在以下几个方面：\n新的思路：动态与自适应推理 核心思想：将问题难度 (problem difficulty) 作为调节推理详细程度的关键变量。这是一种动态的、自适应的资源分配策略。 对比优势：之前的方法，如L1 [31] 等，通常采用固定长度控制或统一压缩策略，它们试图将所有推理都压缩到某个固定的预算内，这可能导致在复杂问题上“思考不足”，从而损害性能。本论文的方法则更加灵活和智能，它允许模型为难题保留详尽的推理，只在简单问题上进行压缩，实现了效率和效果的更优平衡。 新的方法：数据驱动的难度感知能力注入 方法特点：论文的巧妙之处在于，它不依赖于复杂的模型结构修改或强化学习环境，而是通过一个精心设计的数据生成流程，将难度感知的行为“蒸馏”到学生模型中。这个流程包括了基于LLM的难度打分和基于难度的提示词压缩，是一种可扩展、可复制的范式。 对比优势：相比于一些需要复杂强化学习设置或修改解码策略的方法，本论文的方法实现起来相对简单，本质上是一种高质量的数据工程。它证明了通过巧妙地构建训练数据，同样可以赋予模型复杂的行为能力。 新的训练范式：SFT与DPO的课程化结合 方法特点：论文验证了一个高效的“SFT → DPO”两阶段训练策略。 SFT：负责“塑形”，让模型快速学习到简洁推理的格式和长度。 DPO：负责“铸魂”，在模型已经掌握了形式的基础上，通过偏好学习来优化其推理的正确性和逻辑性。 对比优势：单独使用SFT可能会导致模型为了模仿简洁格式而丢失关键信息，造成性能下降（如Table 2中，7B模型的SFT版本在AIME上准确率仅为8.4%）。单独使用DPO则可能无法有效缩短token长度。SFT+DPO的组合拳恰好取长补短，实现了形式（简洁）与内容（准确）的统一。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了严谨且全面的实验来验证其方法的有效性，覆盖了不同模态和模型规模。\n实验设计： 任务领域：实验分为两大类：Unimodal（纯文本数学推理） 和 Multimodal（图文结合的数学推理），验证了方法的普适性。 评估基准 (Benchmarks)： Unimodal: 使用了AIME, MATH, AMC, GSM8K, HMMT等多个高难度、公认的数学推理数据集。 Multimodal: 使用了MathVerse, MathVision, OlympiadBench, MMStar等视觉推理数据集。 对比基线 (Baselines)： 基础模型 (Base)：即在原始长CoT数据上微调的学生模型，代表了高token消耗下的性能上限。 单独训练模型 (SFT-only, DPO-only)：用于消融实验，证明SFT+DPO组合的必要性。 其他SOTA模型：如LLaVA-CoT，代表了领域内其他强大的多模态推理模型。 评估指标 (Metrics)： Pass@1：一次生成即获得正确答案的概率，衡量准确性。 Average Token Count：模型生成推理过程的平均token数，衡量效率。 LLM-as-Judge：使用GPT-4o-mini从清晰度、完整性、正确性、冗余度四个维度对推理质量进行打分，进行定性评估。 关键数据和结果： Unimodal 结果 (Table 2): \u0026gt; 在AIME基准上，7B的SFT+DPO模型相较于Base模型，在token数减少10% (5724 vs 6360) 的情况下，准确率仅有微小差距 (31.7 vs 35.0)。而在GSM8K上，SFT+DPO模型在token数减少8% (498 vs 553)的情况下，准确率几乎持平 (86.9 vs 88.0)。这清晰地展示了“效率-准确率”的权衡优势。 Multimodal 结果 (Figure 1): \u0026gt; 在MathVerse基准上，7B的SFT+DPO模型取得了与教师模型(Qwen2.5-VL-7B)相当的准确率，但平均token数从378减少到296，效率提升了约22%。相比于LLaVA-CoT-11B模型，SFT+DPO模型在多个基准上（如MMStar）都取得了更高的准确率，同时token消耗更少。 定性评估结果 (Figure 2)： \u0026gt; LLM-as-Judge的评分显示，SFT+DPO模型在清晰度(Clarity)、完整性(Completeness)和正确性(Correctness)上全面优于或持平于基线模型，同时在冗余度(Redundancy)上得分显著更低（得分越低越好），证明其生成的推理过程质量更高。 下面是Unimodal任务上7B模型关键结果的总结表，能更直观地体现其优势：\n模型 (7B) AIME (Pass@1 / Tokens) Math (Pass@1 / Tokens) GSM8K (Pass@1 / Tokens) Base 35.0 / 6360 86.0 / 3050 88.0 / 553 SFT only 8.4 / 864 75.4 / 474 88.5 / 244 DPO only 25.0 / 6584 86.0 / 3062 89.2 / 592 SFT + DPO 31.7 / 5724 88.8 / 2745 86.9 / 498 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个研究方向——即自适应计算 (Adaptive Computation) 或 条件计算 (Conditional Computation)——是提升LLM效率和能力的关键，未来充满机遇和挑战。\n值得探索的问题： 更精细的难度/复杂度度量：目前仅使用一个1-10的标量来表示难度。未来可以探索更丰富的度量，例如区分“计算密集型”和“逻辑密集型”难题，或者根据问题的主题、所需知识领域来动态调整推理策略。 跨领域适用性：将此框架从数学推理扩展到代码生成、法律文书分析、科学文献综述等其他需要复杂推理的领域。不同领域的“简洁”和“详尽”定义不同，需要新的数据和策略。 推理路径的动态选择：不仅仅是调整推理的长度，模型是否可以学习在不同的推理“算法”（如CoT, Tree-of-Thought, Graph-of-Thought）之间进行动态选择？例如，简单问题用CoT，复杂问题自动切换到更高成本但更强的ToT。 与模型架构结合：探索与Mixture-of-Experts (MoE) 架构的结合。模型能否根据问题难度，动态地决定激活哪些专家网络？简单问题激活少量通用专家，复杂问题激活更多专业化专家。 技术和投资机会： AI模型推理优化服务：可能会催生出专门为企业提供LLM推理优化服务的公司。他们可以利用类似的技术，帮助客户在不牺牲业务效果的前提下，大幅降低模型API调用成本。 下一代AI芯片/硬件：支持动态计算的硬件架构将更具优势。能够根据计算负载快速调整功耗和资源分配的芯片，将与自适应推理的软件算法形成协同效应。 自动化数据标注与蒸馏平台：构建一个自动化平台，能够为任何模型和任务，自动完成“难度评估-数据生成-压缩蒸馏”的流程，这将是一个极具价值的AI开发工具。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性角度审视，仍存在一些可以进一步完善的地方，作者在“Limitations”一节也坦诚地指出了部分问题：\n难度评估的可靠性与偏差：整个框架的基石是GPT-4o-mini对问题难度的评估。这个评估本身是否足够准确、客观和稳定？如果评估存在偏差（比如对某些类型的问题系统性地高估或低估难度），这种偏差会沿着整个数据处理管道被放大，最终影响学生模型的训练效果。论文缺乏对这一核心环节的深入误差分析。 数据集规模与泛化性：作者承认，他们使用的训练数据集比业界其他大规模蒸馏工作（如DeepSeek-Distil）要小得多。这可能会限制模型在未见过（out-of-distribution）的问题类型上的泛化能力。模型可能只是学会了在“训练集内的难度分布”上进行自适应，但在面对全新的、难度特征不同的问题时，其自适应能力可能会退化。 微调方法的局限性：出于计算资源的限制，论文使用了参数高效微调（LoRA）。虽然LoRA效率高，但它的能力通常被认为弱于全量微调（Full Fine-Tuning）。如果采用全量微调，模型是否能更好地内化这种难度感知能力，达到更高的性能上限？这是一个悬而未决的问题。 评估指标的单一性：论文主要使用pass@1作为准确性指标。但对于生成式任务，pass@k（生成k次，只要有一次正确就算对）能更好地衡量模型的综合能力，因为它考虑到了生成的随机性。使用pass@1可能会低估模型的真实水平。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 这篇论文是创新思想的宝库，您可以从中获得诸多启发：\n重点学习与启发： 核心启发：为AI注入“成本意识”：最大的启发是将计算经济性作为模型行为优化的一个核心目标。在您的项目中，可以思考：是否所有任务都需要模型“全力以赴”？能否设计一个简单的分类器或路由机制，将任务分发给不同能力（和成本）的模型或提示模板？ 数据工程的胜利：这篇论文再次证明，高质量、有思想的数据 curation 远比单纯堆砌模型参数或数据量更重要。创新不一定来自算法层面，通过设计巧妙的数据生成流程，可以赋予现有模型全新的能力。您可以思考如何为您的特定任务构建一个“理想”的训练集。 “模仿”与“偏好”的结合：SFT+DPO的训练范式非常实用。当您想让模型学习某种特定风格（如简洁、专业、幽默），但又担心其“学歪了”（丢失核心信息）时，可以借鉴这个思路： 第一步 (SFT)：用少量高质量样本，让模型快速模仿出您想要的格式和风格。 第二步 (DPO)：构建偏好对（\u0026lt;好的输出, 坏的输出\u0026gt;），让模型在保持风格的同时，学会优化内容质量。 需要补充的背景知识： 链式思考 (Chain-of-Thought, CoT)：这是理解论文问题背景的基础。您需要了解CoT是什么，以及它为什么能提升LLM的推理能力。 模型蒸馏 (Knowledge Distillation)：这是论文方法论的核心。了解其基本思想，即如何将一个强大的“教师模型”的能力迁移到一个小型的“学生模型”中。 直接偏好优化 (Direct Preference Optimization, DPO)：这是理解论文技术关键的重中之重。您需要深入了解DPO的原理，它如何绕过传统的奖励模型，通过偏好数据直接微调LLM。理解DPO，您才能明白为什么论文的第二阶段训练如此高效和成功。它是当前对齐大模型的主流技术之一。 希望这份详尽的解读能帮助您全面、深入地理解这篇优秀的论文，并从中汲取有价值的创新灵感。\n","date":"2025-09-09T00:00:00Z","permalink":"/less-is-more-tokensefficient-math-reasoning-via-difficulty-aware-chain-of-thought-distillation/","title":"Less is More Tokens：Efficient Math Reasoning via Difficulty-Aware Chain-of-Thought Distillation"},{"content":"利用多agent进行数据清洗。\n论文深度解读 这篇名为《Plan Verification for LLM-Based Embodied Task Completion Agents》的论文，其核心贡献在于提出了一个创新的、完全由自然语言驱动的迭代式验证框架 (iterative verification framework)，旨在自动识别并修正具身AI任务规划中的各种错误。在具身AI领域，我们通常通过模仿学习 (Imitation Learning) 的方式，让智能体（比如一个家用机器人）学习人类的演示操作。然而，人类的演示数据，比如论文中重点分析的 TEACh 数据集，往往是“嘈杂”且“非最优”的。\nThese include, for example, picking up irrelevant or unnecessary items, leaving goals unaccomplished due to erroneous instruction, or toggling appliances that were never activated. （这些问题）包括：例如，捡起了不相关或不必要的物品，由于错误的指令而未能完成目标，或者开关了从未被激活的电器。\n这些低质量的数据会严重影响智能体的学习效率和最终表现。传统方法要么依赖于复杂的、基于规则的硬编码启发式方法，要么需要耗费大量人力进行手动清洗，扩展性很差。\n该论文的 brilliant 之处在于，它将“数据清洗”这个任务巧妙地转化为两个大型语言模型 (LLM) 之间的对话与协作。这个框架包含两个核心角色：\n“裁判”LLM (Judge LLM)：负责审查一个给定的行动序列。它的任务是像一个严格的教练一样，逐一检查每个动作是否服务于最终目标，并识别出多余、矛盾或缺失的步骤。 “规划者”LLM (Planner LLM)：负责接收“裁判”的批评和建议，并据此对原始行动计划进行修改，生成一个更优化的新版本。 这个过程并非一蹴而就，而是迭代进行的。修改后的计划会再次提交给“裁判”审查，如此循环，直到“裁判”认为计划已经完美无瑕，或者达到预设的迭代次数上限。整个交互过程完全基于自然语言提示（Prompt），不依赖任何特定的环境模拟器或视觉输入，这使得该框架具有极强的模型无关性 (model-agnostic) 和通用性。\n为了验证框架的有效性，论文进行了一项扎实的实证研究。他们选取了四种当时最先进的LLM（GPT 04-mini, DeepSeek-R1, Gemini 2.5, LLaMA 4 Scout），并将它们分别扮演“裁判”和“规划者”的角色，在TEACh数据集上进行了测试。实验结果令人信服，通过关键指标 召回率 (Recall) 和 精确率 (Precision) 对比了不同模型的表现。\n一个核心发现是不同模型在“裁判”角色上表现出鲜明的“性格”差异： * DeepSeek-R1 像一个保守派，追求极高的精确率（在某些组合下达到100%），但召回率较低（68%），意味着它只标记非常有把握的错误，可能会漏掉一些模糊的问题。 * GPT 04-mini 则像一个激进派，拥有最高的召回率（迭代后高达90%），能找出绝大多数错误，但代价是精确率有所下降（80%），可能会“冤枉”一些本没有问题的操作。 * Gemini 2.5 表现最为均衡，在精确率和召回率之间取得了出色的平衡，F1分数在所有组合中都名列前茅，展现了其强大的综合判断能力。\n此外，数据显示，这个迭代过程的收敛速度非常快。如下图所示，超过62%的序列在第一轮迭代后就已修正完毕，到第三轮迭代时，96.5%的序列都已收敛，证明了该框架的效率。\n图：论文中的收敛曲线（Figure 3），展示了绝大多数任务序列在3轮迭代内即可完成修正\n总而言之，这篇论文不仅提出了一个解决具身AI数据质量问题的实用框架，更通过严谨的实验揭示了不同LLM在复杂推理和验证任务中的行为模式与权衡，为构建更智能、更鲁棒的AI系统提供了宝贵的见解和可行的工程范式。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是设计并验证一个通用的、可扩展的、基于语言模型的自动化框架，用于识别和纠正人类在具身任务演示中所产生的次优或错误的行为序列。\n解决的实际问题： 该研究直面具身AI训练数据的质量瓶颈问题。在机器人学和具身智能领域，通过模仿学习训练智能体是一种主流方法。这意味着机器人需要学习大量的人类演示数据。然而，人类在演示“如何泡咖啡”或“如何打扫房间”时，其行为往往不是最优的，可能包含多余的走动、拿错物品再放回、忘记某个步骤等“噪声”。这些噪声数据会直接降低训练出的AI模型的效率、可靠性和智能水平。手动清洗这些数据成本高昂且效率低下，限制了整个领域的发展速度。\n行业发展意义： 这个问题对行业发展至关重要，体现在以下几点：\n加速AI机器人的研发进程：一个能够自动化清洗和优化训练数据的工具，可以极大地缩短模型迭代周期，降低数据标注成本，让研究人员和工程师能将更多精力投入到算法和硬件的创新上。 提升消费级和工业级机器人的性能：更高质量的训练数据意味着机器人能够学习到更高效、更直接的任务执行策略，从而在家庭服务、仓储物流、智能制造等场景中表现得更可靠、更智能，提升用户体验和生产效率。 推动数据驱动的AI开发范式：论文提出的框架是“数据中心AI (Data-Centric AI)”理念在具身智能领域的绝佳实践。它强调了高质量数据的重要性，并提供了一种系统性的方法来提升数据质量，这将引导行业更加关注数据本身，而非仅仅是模型结构。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路和方法： 论文的核心创新是提出了“裁判-规划者” (Judge-Planner) 的双智能体协作框架。这个思路的精髓在于任务分解和迭代式求精。它没有试图让一个模型一次性解决所有问题，而是将复杂的“计划优化”任务分解为两个更简单的子任务：\n批判 (Critique)：由“裁判”LLM执行，专注于发现问题。 修正 (Revise)：由“规划者”LLM执行，专注于解决被发现的问题。 这个过程通过迭代循环不断优化，直到计划收敛。\n特点和优势： 与之前的方法相比，该框架具有以下显著特点和优势：\n完全基于自然语言：与依赖线性时序逻辑 (Linear Temporal Logic, LTL) 等形式化方法的 VerifyLLM 等工作不同，该框架完全通过自然语言提示进行交互。这大大降低了实现的复杂性，使其更容易被应用到各种不同的任务和场景中，通用性极强。 \u0026gt; Our framework contributes to this line of work by demonstrating that effective, high-recall verification can be achieved using zero-shot natural language critique alone, without requiring formal methods like LTL. \u0026gt; 我们的框架通过证明仅使用零样本自然语言批判就能实现有效的高召回率验证，而无需像LTL这样的形式化方法，从而为这一研究方向做出了贡献。 模型无关和模块化：用户可以自由替换“裁判”和“规划者”的LLM。实验本身就展示了GPT、DeepSeek、Gemini等不同模型组合的效果，证明了其灵活性。这种模块化设计便于集成最新的、更强大的模型。 后处理验证 (Post-hoc Verification)：该框架的作用是“后期处理”，它不关心初始计划是来自人类演示还是另一个AI模型。这使其能够作为一个独立的、可插拔的优化模块，与现有的各种计划生成方法协同工作，而不是相互竞争。 可解释性强：“裁判”LLM在标记错误时，会提供自然的语言解释，说明为什么某个动作是多余或错误的。这为开发者提供了透明的决策依据，便于调试和理解模型的行为。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 数据集：实验使用了一个精心挑选的 TEACh 数据集 的子集，包含了15个不同的高级别家务任务，共100个任务片段和1408个人类生成的动作。 评估基准：研究人员首先对这些数据进行了手动标注，标记出所有冗余、错误或缺失的动作，以此作为评估模型性能的“黄金标准 (Ground Truth)”。 实验流程：将原始的任务计划输入到由不同LLM组合构成的“裁判-规划者”框架中。框架输出经过修正的计划。 评估指标：通过比较“裁判”LLM标记的错误与手动标注的基准，计算两个关键指标： 精确率 (Precision)：在所有被模型标记为错误的动作中，有多少是真正的错误。Precision = TP / (TP + FP) 召回率 (Recall)：在所有真正的错误中，有多少被模型成功地找了出来。Recall = TP / (TP + FN) F1分数：精确率和召回率的调和平均数，用于综合评估。 实验数据和结果： 实验结果清晰地揭示了该方法的有效性以及不同模型的特点。 单次验证性能 (Static Verification)： 如下表（Table 1简化版）所示，即使只进行一次验证（非迭代），该框架也远超基于规则的基线方法。 Judge LLM Recall (召回率) Precision (精确率) GPT 04-mini 80% 93% DeepSeek-R1 68% 100% Gemini 2.5 74% 90% LLaMA 4 Scout 74% 85% Rule-based 22% 71% 2. **迭代验证性能 (Iterative Performance)**： 迭代过程显著提升了性能，尤其是召回率。 \u0026gt; As shown in Table 2, iterative refinement boosts recall by 5–10% on average across all Judge-Planner combinations. For instance, GPT 04-mini’s recall improves from 80% (static) to 88–90%... \u0026gt; 如表2所示，迭代优化在所有‘裁判-规划者’组合中平均将召回率提高了5-10%。例如，GPT 04-mini的召回率从80%（单次）提升到了88-90%... 3. **模型组合效应**： 实验发现了一个有趣的现象：模型与自身配对时通常表现很好，但某些跨模型组合效果更佳。例如，当**GPT 04-mini担任“裁判”，DeepSeek-R1担任“规划者”**时，系统获得了**最高的90%召回率**。这表明，一个“挑剔”的裁判和一个“严谨”的规划者组合能达到最佳的纠错效果。而**Gemini 2.5**无论与谁搭档，都表现出非常稳健和顶级的综合性能（F1分数最高达到93.9%），显示了其卓越的泛化能力。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 值得探索的问题和挑战： 多模态融合 (Vision-Grounded Verification)：当前框架纯粹基于文本。未来的一个重要方向是融合视觉信息。例如，裁判LLM不仅要分析文本指令，还应该能“看到”机器人的摄像头画面，判断“拿起杯子”这个指令在当前环境下是否可行（比如杯子是否存在，是否被遮挡）。 物理常识与环境交互：模型需要更深层次的物理世界常识。一个计划在文本上看似合理，但在现实世界中可能因为物理限制而失败。将框架与物理模拟器结合，进行执行前的“预演”，是一个重要的研究方向。 裁判模型的鲁棒性与对抗性攻击：正如论文引用的研究，LLM作为裁判时可能很脆弱，容易被对抗性攻击所欺骗。如何提升裁判模型的鲁棒性，使其不被精心设计的、看似合理但实际错误的计划所迷惑，是一个巨大的挑战。 更复杂的任务规划：当前实验局限于相对简单的家务任务。如何将此框架扩展到需要长期规划、涉及多智能体协作的复杂任务（如建筑施工、灾难救援），是未来的一个高地。 催生的新技术和投资机会： 机器人训练数据即服务 (DaaS - Data-as-a-Service)：专门为机器人和AI公司提供高质量、经过优化和清洗的训练数据集，将成为一个新兴的商业模式。 具身AI的MLOps平台：开发集成数据清洗、模型训练、模拟测试、部署监控于一体的机器学习操作 (MLOps) 平台，专门服务于具身智能领域，将有巨大的市场潜力。 混合智能体系统 (Hybrid Agent Systems)：开发结合不同LLM优势的混合系统，例如，用高召回率的GPT-4进行初步筛选，再用高精确率的DeepSeek-R1进行最终确认，这种流水线式的“专家会诊”系统将成为一种新的技术范式。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 从批判的角度看，这篇论文虽然非常出色，但仍存在一些固有的局限性和待验证之处： 1. 评估范围有限： \u0026gt; Firstly, our evaluation is conducted exclusively on a subset of the TEACh dataset, covering 15 common household goals, and the generalizability of our findings to broader, more diverse embodied tasks… remains untested. \u0026gt; 首先，我们的评估完全是在TEACh数据集的一个子集上进行的，涵盖了15个常见的家庭目标，其发现在更广泛、更多样的具身任务（如工业、医疗）上的泛化能力仍有待检验。 实验仅限于家庭环境，其方法在结构更复杂、对安全性要求更高的工业或医疗场景中的有效性未知。 2. 评估基准的主观性：整个评估依赖于人工标注的“黄金标准”。但对于一个动作是否“多余”，不同的人可能有不同的判断。这种主观性可能会给实验结果的绝对精确性带来一些不确定性。未来需要探索更客观或半自动化的标注方法。 3. 缺乏真实世界验证：所有实验都是在离线数据集上进行的，没有在物理机器人上进行部署和测试。一个在文本层面被优化的计划，在真实的物理交互中可能会遇到意想不到的问题（如物体识别错误、机械臂抓取失败等）。 4. 对“错误”的定义过于简化：该框架倾向于将所有“非最优”行为都视为需要移除的错误。然而，人类演示中的一些“冗余”行为可能包含着宝贵的纠错模式 (error-recovery patterns) 或探索行为。简单地删除这些行为，可能会让智能体失去学习如何从错误中恢复的能力。\n### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 重点学习和启发： “批判-修正”模式 (Critique-Revise Pattern)：这是本文最有价值的、可立即应用的创新思想。在任何需要由AI生成复杂内容（代码、文章、设计方案、法律文书等）的场景中，都可以引入一个“裁判”角色。与其追求一步到位，不如设计一个“生成器+批判家”的迭代系统，质量会显著提升。 智能体角色的分解与协作：将一个复杂的任务分解成多个简单的、专门化的智能体角色，让它们协同工作。这种“多智能体系统”的设计思想是构建强大AI应用的关键。你不必找到一个万能的LLM，而是可以组建一个各有所长的“LLM团队”。 利用不同模型的“性格”：论文对不同LLM（保守、激进、均衡）的分析极具启发性。在你的应用中，可以根据任务的不同阶段选择不同“性格”的模型。例如，在头脑风暴阶段使用创造力强但可能不精确的模型，在最终审查阶段使用保守但精确的模型。 Prompt Engineering的力量：论文附录中的Prompt设计展示了如何通过清晰的指令，让通用LLM扮演好一个特定领域的专家角色。这提醒我们，精心设计的Prompt是释放LLM能力的最直接、最有效的杠杆。 需要补充的背景知识： 具身AI (Embodied AI)：了解其基本概念，即AI如何通过物理实体（如机器人）与世界互动来学习和执行任务。 模仿学习 (Imitation Learning) 与强化学习 (Reinforcement Learning)：理解这两种训练智能体的主流范式，尤其是模仿学习中对演示数据质量的依赖。 大型语言模型作为智能体 (LLM as Agents)：了解当前LLM如何被用于推理、规划和执行任务，以及如 AutoGen、Reflexion（论文中也引用了）等多智能体协作框架。 评测指标（Precision, Recall, F1-Score）：深入理解这些在机器学习中广泛使用的评测指标的含义，以及它们在不同任务场景下的权衡关系。 希望这份详尽的解读能帮助您全面、深入地理解这篇论文的精髓。\n","date":"2025-09-09T00:00:00Z","permalink":"/plan-verification-for-llm-based-embodied-task-completion-agents/","title":"Plan Verification for LLM-Based Embodied Task Completion Agents"},{"content":"和Search-R1：Training LLMs to Reason and Leverage Search Engines with Reinforcement Learning的区别就是多了个格式奖励。\nimage.png 这篇论文的核心贡献在于提出了一种名为 ReSearch 的新颖框架，它利用强化学习（Reinforcement Learning）来训练大语言模型（LLM）在推理过程中主动、智能地使用搜索工具，而整个训练过程完全不需要人工标注的推理步骤数据。这标志着从传统的、相对固化的“检索-增强-生成”（RAG）模式，向一种更动态、更智能的“边思考边搜索”的代理（Agent）行为模式的重大转变。\n在传统的 RAG 流程中，系统通常是先根据用户问题进行一次或几次检索，然后将检索到的信息作为上下文，让模型生成答案。这种方法的局限性在于，它无法处理需要多步推理、且每一步推理都依赖于前一步搜索结果的复杂问题。模型是被动地接收信息，而不是主动地规划如何获取信息。\nReSearch 框架则彻底改变了这一模式。它将“搜索”这一行为本身视为推理链条中的一个可学习的“动作”（Action）。模型需要自己决定：什么时候应该去搜索？搜索什么关键词才能最高效地获取信息？以及如何根据搜索到的结果调整下一步的思考和行动？\n为了实现这一点，作者们巧妙地运用了强化学习。简而言之，他们让模型针对一个问题，生成多种可能的“解题路径”（在论文中称为 rollouts）。这些路径不仅仅是文字，而是由\u0026lt;think\u0026gt;（思考）、\u0026lt;search\u0026gt;（搜索）、\u0026lt;result\u0026gt;（搜索结果）和\u0026lt;answer\u0026gt;（最终答案）等特殊标签构成的结构化文本。例如，模型可能会先生成一段\u0026lt;think\u0026gt;来分析问题，然后决定生成一个\u0026lt;search\u0026gt;查询，系统执行这个查询后，将结果包裹在\u0026lt;result\u0026gt;标签中反馈给模型，模型再根据这个新信息进行下一步的\u0026lt;think\u0026gt;，如此循环，直到最终给出\u0026lt;answer\u0026gt;。\n这个过程的关键在于奖励模型（Reward Model）。研究人员并没有设计复杂的奖励机制，而是采用了一种非常简单有效的规则：如果模型最终给出的答案是正确的（通过与标准答案计算 F1 分数来判断），就给予一个正向奖励；如果答案错误但格式正确，给予一个微小的奖励；如果格式都错了，则不给奖励。\nSpecifically, for the final reward of a rollout: r = { f1(apred, agt), if f1 score is not 0; 0.1, if f1 score is 0 and format is correct; 0, if f1 score is 0 and format is incorrect }\n通过这种方式，模型在海量的“试错”中，会逐渐学会哪些“思考-搜索”序列更容易导向正确的答案，从而优化自身的决策策略（Policy）。论文中采用的强化学习算法是 GRPO (Group Relative Policy Optimization)，这是一种高效的 PPO 变体，它通过比较一组并行生成的多个 rollouts 的优劣来更新模型，而无需训练一个独立的“评论家”网络，从而提高了训练效率。\n更令人惊艳的是，这种训练方式催生出了“涌现”能力。论文的案例研究（Case Study）展示了一个绝佳的例子：模型在第一次搜索“Martín Ramírez Pech”时，得到了错误的人（一个同名的艺术家）。此时，模型在下一步的\u0026lt;think\u0026gt;中，竟能自行判断“It appears that I made a mistake in my search query.”（我似乎在搜索查询中犯了个错误），并主动修正查询为“Martín Ramírez Pech political party”，从而获取了正确的信息。这种自我反思和纠错（self-correction）的能力并非来自任何预设的规则，而是模型在以“最终答案正确性”为目标的强化学习过程中，自发学习到的高级推理策略。\n在实验验证上，论文取得了令人信服的结果。他们仅使用 MuSiQue 这一个数据集进行训练，却在包括 HotpotQA、2WikiMultiHopQA 和 Bamboogle 在内的多个知名多跳问答（multi-hop QA）基准测试集上，都取得了远超基线模型的性能。如下表所示（节选自论文 Table 2），在 Bamboogle 数据集上，ReSearch 将 Qwen2.5-32B-Instruct 模型的准确率（EM）从基线最高的 31.20% 提升到了 56.80%，提升幅度巨大，充分证明了该框架的有效性和泛化能力。\nModel Bamboogle (EM, %) Bamboogle (LJ, %) Naive Generation 18.40 29.60 Naive RAG 23.20 40.80 Iter-RetGen 29.60 44.80 IRCoT 31.20 47.20 ReSearch-Qwen-32B-Instruct 56.80 67.20 总而言之，ReSearch 论文的创新性贡献在于，它为“如何教模型使用工具”这一核心问题提供了一个可扩展、高效且无需大量人工监督的解决方案。它不仅仅是 RAG 技术的简单升级，更是向着构建能够自主规划、执行和反思的通用人工智能代理（AI Agent）迈出的坚实一步。\n接下来，我将从您提出的六个方面，对这篇论文进行更详细的解读。\n研究目标与实际问题 研究目标： 论文的核心研究目标是训练大语言模型（LLM）掌握一种动态、迭代的“推理与搜索”相结合的能力，使其能够自主解决需要多步信息检索和复杂逻辑推理才能回答的问题。\n解决的实际问题： 它主要解决的是现有 RAG (Retrieval-Augmented Generation) 系统在处理复杂多跳问题（complex multi-hop questions）时的普遍短板。例如，对于“《星际穿越》的导演的出生地的市长是谁？”这类问题，传统 RAG 难以胜任，因为它需要：\n步骤1：搜索“《星际穿越》的导演是谁？” -\u0026gt; 得到“克里斯托弗·诺兰”。 步骤2：搜索“克里斯托弗·诺兰的出生地是哪里？” -\u0026gt; 得到“英国伦敦”。 步骤3：搜索“伦敦的现任市长是谁？” -\u0026gt; 得到最终答案。 传统方法通常无法自主规划并执行这样一个连贯的、依赖于中间结果的搜索链条。ReSearch 正是为了让模型学会这种自主规划和执行的能力。 行业重要意义： 这个问题对于行业发展至关重要。随着 LLM 被广泛应用于搜索引擎、智能客服、个人助理等场景，用户需求早已不满足于简单的信息查询。用户期待 AI 能够像一个真正的专家助手一样，为复杂问题进行调研、分析并给出综合性的答案。ReSearch 提供了一条通往更强大的 AI Agent 的路径，这些 Agent 能够更可靠、更智能地利用外部世界的信息（通过搜索等工具），从而：\n提升信息准确性：显著减少模型在回答事实性问题时的“幻觉”现象。 扩展能力边界：使模型能回答其内部知识库未覆盖的、或需要最新信息的时效性问题。 实现任务自动化：为实现能够自主完成研究、报告撰写、旅行规划等复杂任务的自动化代理奠定了基础。 创新思路与方法优势 新的思路： 将搜索视为推理动作：最大的创新在于，它没有将搜索和推理看作两个独立的阶段，而是将“搜索”定义为推理过程中的一个内在“动作”。LLM 的任务不仅是生成文本，更是生成一个包含思考和动作的完整“计划”。 结果驱动的无监督学习：它绕开了为推理过程本身提供监督数据的难题。通过强化学习，模型仅从最终答案的正确性这一稀疏的奖励信号中，就能反向传播学习到优秀的推理策略，这大大降低了数据标注的成本，增强了方法的可扩展性。 新的方法/模型： 该论文提出的核心是 ReSearch 框架，其技术细节包括： 结构化生成（Structured Generation）：通过定义\u0026lt;think\u0026gt;, \u0026lt;search\u0026gt;, \u0026lt;result\u0026gt;, \u0026lt;answer\u0026gt;等一系列特殊标签，规范了模型的输出格式，使其生成过程本身就构成了一个清晰的推理轨迹。 强化学习算法 GRPO：采用 Group Relative Policy Optimization 对模型进行训练。相比传统的 PPO 算法，GRPO 通过在一组（Group）候选输出（rollouts）中进行比较来计算优势函数，避免了训练一个独立的 Critic 网络，使训练更加稳定和高效。 奖励结果屏蔽（Retrieval Result Masking）：这是一个非常巧妙的细节。在计算损失函数时，模型只对它自己生成的\u0026lt;think\u0026gt;和\u0026lt;search\u0026gt;部分负责，而由外部搜索工具提供的\u0026lt;result\u0026gt;部分的 token 则被屏蔽掉。 \u0026gt; Therefore, we mask the retrieval results in the loss calculation to avoid the training policy from being biased towards the retrieval results. 特点和优势： 与之前的方法相比，ReSearch 的优势非常明显： vs. 提示工程方法 (e.g., IRCoT, Iter-RetGen)：基于提示的方法（如 CoT）虽然也能引导模型进行多步推理，但它们依赖于固定的提示模板，模型只是在“遵循指令”，而不是真正“学会”一种策略。因此，ReSearch 学习到的策略更灵活、更具泛化性，能适应更多样化的问题。 vs. 监督微调方法：监督微调需要大量高质量的“问题-推理过程-答案”数据对，获取这种数据的成本极高。ReSearch 则完全不需要推理过程的标注，大大提升了实用性。 涌现的复杂能力：如前所述，ReSearch 能够诱导出自我反思和纠错等高级能力，这是简单的提示或监督微调难以实现的。模型真正从“死记硬背”转向了“学以致用”。 實驗設計與結果分析 实验设计： 训练与测试分离：为了验证方法的泛化能力（Generalization Ability），研究者只在 MuSiQue 数据集的训练集上进行训练。 多基准评估：在四个广泛认可的多跳问答基准上进行评估，包括 HotpotQA, 2WikiMultiHopQA, MuSiQue, 和 Bamboogle。这些数据集各有侧重，能全面地考察模型的性能。 模型选择：实验基于 Qwen2.5 系列的 7B 和 32B 尺寸模型，并同时在基础模型（base）和指令微调模型（instruct）上进行了训练，以比较不同起点的影响。 基线对比：选取了四种有代表性的基线方法进行对比： Naive Generation: 无 RAG，直接生成。 Naive RAG: 简单的先检索后生成。 Iter-RetGen: 一种迭代式的检索与生成方法。 IRCoT: 将检索与思维链（CoT）结合的方法。 评估指标： EM (Exact Match)：精确匹配率，非常严格。 LJ (LLM-as-a-Judge)：使用 GPT-4o-mini 作为裁判，对答案进行语义上的正确性判断，比 EM 更符合人类的评估方式。 实验数据和结果： 实验结果有力地证明了 ReSearch 的有效性。 显著的性能提升： \u0026gt; Compared with all the baselines, ReSearch achieves significant improvements on all the benchmarks… the average improvement of ReSearch over the best baseline is 15.81% in exact match and 17.56% in LLM-as-a-judge, for Qwen2.5 model with 7B parameters.\n强大的泛化能力：尽管只在 MuSiQue 上训练，ReSearch 在其他三个数据集上也表现出色。例如，在 HotpotQA 上，ReSearch-Qwen-7B-Instruct 的 LJ 得分达到了 63.62%，远高于基线最高的 52.22%（Iter-RetGen）。这表明模型学到的是通用的“推理-搜索”能力，而非针对特定数据集的知识。\n训练过程分析：论文中的 Figure 3 展示了训练过程中“响应长度”和“搜索次数”的变化，揭示了模型的学习动态。可以看到，随着训练的进行，模型平均的搜索次数（Number of Search）稳步上升，说明模型逐渐认识到对于复杂问题，多次、迭代地使用搜索是解决问题的关键。\n上图（模拟自论文 Figure 3）清晰地展示了，随着训练步数的增加，模型的输出中平均搜索操作的次数（右侧图）在不断增长，证明了其学会了更频繁地利用工具。 未來展望與潛在機會 值得探索的问题和挑战： 更丰富的工具集：目前 ReSearch 只使用了“搜索”这一种工具。未来可以将其扩展到更广泛的工具集，如代码解释器、计算器、API 调用、数据库查询等。如何让模型学会在多种工具间进行选择和组合，将是一个重要的研究方向。 更复杂的任务：可以将此框架应用于比问答更复杂的任务，如科学文献综述、自动编程、事实核查等。 提升样本效率：强化学习通常需要大量的训练样本。研究如何用更少的数据、更低的计算成本来达到同样的效果，例如结合模仿学习（Imitation Learning）进行预训练，将是关键挑战。 更优的奖励模型：当前的奖励模型只关注最终结果。未来可以设计更精细的奖励函数，比如对推理步骤的效率、搜索查询的质量进行奖励，以引导模型学习到更优的策略。 新的技术和投资机会： AI Agent 平台：ReSearch 的成功将极大地推动 AI Agent 技术的发展。专注于构建和托管能够使用多种工具、执行复杂任务的 Agent 平台，将成为一个巨大的市场。类似 AgentVerse、LangChain 等框架将更加成熟。 为 Agent 优化的工具 API：未来会出现更多专为 AI Agent 设计的 API 服务。这些 API 返回的数据将是结构化的、易于模型解析的，而不是目前主要面向人类阅读的网页。 Agent 训练和评估的基础设施：随着 Agent 变得越来越复杂，如何高效地训练、调试和评估它们将成为一个新的技术领域。提供 Agent 训练的模拟环境、评估基准和 MLOps 解决方案的公司将迎来发展机遇。 批判性視角與局限性 尽管这篇论文取得了突破性进展，但从批判的角度看，仍存在一些潜在的不足和待验证之处：\n对搜索工具质量的依赖： 整个框架的性能上限受制于其使用的搜索引擎。如果搜索引擎返回了错误或不相关的信息，模型可能很难从中恢复，甚至可能被误导。论文没有深入探讨模型在面对低质量搜索结果时的鲁棒性。\n奖励模式的潜在风险： 仅基于最终答案的奖励机制可能会导致“奖励黑客”（Reward Hacking）。即模型可能找到某种捷径或投机取巧的方式来获得正确答案，但其推理过程本身是不合逻辑或不可靠的。虽然论文中的案例看起来不错，但在更大规模、更复杂的场景下，这个问题依然存在。\n计算成本高昂： 强化学习的训练过程，尤其是对于 LLM 这种大模型，通常需要巨大的计算资源。论文提到训练使用了 8 x 8 Nvidia H800 GPUs，这对于许多研究机构和小型公司来说是难以承受的。方法的可复现性和普及性可能会因此受到限制。\n泛化领域的局限性： 虽然论文证明了在不同多跳问答数据集之间的泛化能力，但这仍然是在“问答”这个相对单一的领域内。该框架在其他类型的任务，如创意写作、代码生成、数学推理等领域的有效性，仍有待验证。模型学到的“推理-搜索”模式可能对特定领域存在偏好。\n核心啟示與知識補充 重点学习与启发： 范式转变的启发：最大的启发是从静态的“知识注入”思维（RAG）转向动态的“能力授予”思维（Agent）。我们不应只把 LLM 看作一个知识库，而应将其视为一个能够主动学习如何利用外部世界来解决问题的“决策者”。 RL 在 LLM Agent 中的应用：这篇论文是 RL 成功应用于训练 LLM 使用工具的典范。它证明了即使是简单的、基于最终结果的奖励，也足以激发模型产生复杂的、有益的行为（如自我纠错）。这为训练更复杂的 Agent 提供了信心和方法论。 结构化输出的重要性：通过设计一套简洁的 XML 风格标签，将模型的思维过程显式化、结构化，这不仅便于模型与外部环境（如搜索工具）交互，也极大地增强了模型输出的可解释性。这是一个非常值得借鉴的工程实践。 需要补充的背景知识： 为了更深入地理解这篇论文，我建议您补充了解以下背景知识： 强化学习基础 (Reinforcement Learning)：理解策略（Policy）、奖励（Reward）、状态（State）、动作（Action）等核心概念。 PPO 算法 (Proximal Policy Optimization)：了解 PPO 为何是当前 LLM 强化学习（如 RLHF）中的主流算法，它如何通过“信任区域”来保证训练的稳定性。理解了 PPO，就更容易理解其变体 GRPO。 检索增强生成 (RAG)：了解 RAG 的基本工作原理，包括其不同变种，如 Advanced RAG、Self-RAG 等，这能帮助您更好地理解 ReSearch 在此基础上的创新。 思维链 (Chain-of-Thought, CoT)：了解 CoT 如何通过引导模型输出中间推理步骤来提升其复杂推理能力，这有助于您将其与 ReSearch 这种通过 RL 学习推理链的方法进行对比。 ","date":"2025-09-09T00:00:00Z","permalink":"/researchlearning-to-reason-with-search-for-llms-via-reinforcement-learning/","title":"ReSearch：Learning to Reason with Search for LLMs via Reinforcement Learning"},{"content":"利用orm和prm筛选数据，然后用筛选好的数据进行训练，最近好多这种筛选的思路，如Sample More to Think Less：Group Filtered Policy Optimization for Concise Reasoning和rStar2-Agent：Agentic Reasoning Technical Report\nimage.png 论文深度解读 这篇论文的核心目标是解决在利用强化学习（RL）训练语言模型进行数学等复杂推理任务时遇到的一个根本性困境：过程奖励（Process Rewards）与结果奖励（Outcome Rewards）之间的矛盾。为了理解这篇论文的精髓，我们首先需要通俗地解释这两个概念。\n结果奖励模型（Outcome Reward Models, ORMs）：这是一种简单直接的奖励机制。它只关心最终答案是否正确。比如一道数学题，答对了就给+1分，答错了就给-1分。这种方法的优点是准确无误，因为答案的对错是客观的。但缺点也同样明显，它过于粗粒度（coarse-grained）。模型可能用一个错误百出的过程“瞎猫碰上死耗子”般地得到了正确答案，ORM无法分辨这种情况，依然会给予正向奖励，这会误导模型的学习。论文在表格1中给出了一个非常经典的例子： \u0026gt; Problem: There are coins of 1, 2, 3, and 5 cruzeiros… How can the counterfeit coin be identified with the minimum number of weighings… \u0026gt; Analysis: Correct Answer, Invalid Justification. The summary repeats the flawed weighing strategy. The final answer is correct but is completely unsupported by the fallacious reasoning presented. \u0026gt; 引用自论文第二页的表格1，展示了一个推理过程完全错误但最终答案碰巧正确的情况。\n过程奖励模型（Process Reward Models, PRMs）：为了解决ORM的粗粒度问题，研究者提出了PRM。它会对模型生成的解题过程中的每一步进行打分，奖励那些逻辑正确、推理严谨的步骤。这种方法的优点是细粒度（fine-grained），能引导模型学习“如何正确地思考”。但它的致命缺点是不准确且容易被“攻击”或“钻空子”（reward hacking）。因为PRM本身也是一个模型，它对推理步骤的判断可能出错。更糟糕的是，策略模型（被训练的LLM）会很快发现PRM的评分偏好，比如它可能会倾向于奖励更冗长、更“看起来”详细的步骤，导致模型为了获得高分而生成大量无效、重复的冗长内容，而真正的推理质量并未提升。\n现有的方法通常尝试将这两种奖励以加权的方式混合（blending）在一起，但论文指出，这种简单混合的策略效果不佳，因为PRM的噪声和不准确性很容易压倒ORM的信号，导致模型行为失控，出现严重的“奖励攻击”现象。\n正是在这个背景下，本文提出了其核心创新——过程一致性过滤器（PRocess cOnsistency Filter, PROF）。PROF的思路非常巧妙，它认为不应该直接将两种奖励信号混合用于梯度更新，而应该用它们来做数据筛选和管理（data curation）。简单来说，PROF扮演了一个“数据质检员”的角色，其工作流程如下：\n分组：首先，模型会针对一个问题生成多个解答（rollouts）。PROF会利用准确的ORM（最终答案是否正确）将这些解答分为“正确组”和“错误组”。 过程一致性评分：接着，PROF会使用PRM为每个解答的推理过程打一个综合分数。这个分数体现了过程的质量。 筛选：这是最关键的一步。PROF会在两个组内部分别进行筛选，但遵循不同的逻辑： 在正确组中，它会保留那些PRM评分高的解答，剔除那些PRM评分低的解答。这背后的直觉是：我们要留下那些“过程好，结果也好”的优质样本，剔除那些“过程差，但运气好结果对”的噪声样本。 在错误组中，它会保留那些PRM评分低的解答，剔除那些PRM评分高的解答。这背后的直觉是：我们要留下那些“过程差，结果也差”的样本，剔除那些“过程好，但结果却错”的矛盾样本（这种情况可能是PRM判断失误）。 平衡与更新：PROF在筛选后，还会确保“正确组”和“错误组”的样本比例保持平衡，防止模型只学习如何解决简单问题。最后，只有经过PROF筛选出的高质量、高一致性的样本才会被用于更新模型参数。 通过这种方式，PROF巧妙地利用了ORM的准确性来“锚定”事实，同时又借助PRM的细粒度来评估过程质量，但通过过滤而非混合的方式，有效避免了PRM的噪声对模型训练的直接干扰，从而显著提升了模型的最终性能和推理过程的质量。\n论文通过大量的实验证明了PROF的有效性。在使用Qwen2.5-Math-7B模型进行的测试中，PROF-GRPO方法在五个数学基准测试中的平均准确率达到了51.7%，显著高于仅使用ORM的GRPO基线（49.9%）和直接混合奖励的Blend方法（47.3%）。\nModel Algorithm Math500 Minerva Math Olympiad Bench AIME24 AMC23 Average Qwen2.5-Math- GRPO 81.6 37.2 45.5 20.6 64.4 49.9 7B-base Blend 81.7 36.7 45.0 15.2 58.0 47.3 PROF-GRPO 83.1 39.0 47.8 17.5 70.9 51.7 （引用并简化自论文Table 2） 更重要的是，论文通过分析模型的熵损失和响应长度（Figure 3）发现，Blend方法由于奖励攻击，导致模型熵值迅速崩溃（过度自信）且输出长度失控，而PROF则能保持稳定的训练动态。这充分证明了PROF在抑制奖励攻击方面的鲁棒性。\n总而言之，这篇论文最大的贡献在于提出了一种全新的范式来协调过程与结果奖励：从“信号混合”转向“数据管理”。PROF作为一个通用、模块化的数据过滤框架，为解决强化学习中奖励信号不完美的问题提供了一个非常实用且有效的解决方案，对未来构建更可靠、更值得信赖的AI推理系统具有重要的启发意义。\n接下来，我将针对您提出的六个问题，进行更详细的解读。\n1. 论文的研究目标是什么？ 研究目标：本文的核心研究目标是，在利用强化学习（RL）训练大型语言模型（LLM）进行数学等复杂推理任务时，如何有效、鲁棒地整合粗粒度的结果奖励（ORM）和细粒度的过程奖励（PRM），以同时提升最终答案的准确性和中间推理步骤的质量。\n想要解决的实际问题：\n提升推理的可靠性：当前的LLM在数学推理中常常“知其然，而不知其所以然”。它们可能通过一些虚假的、不合逻辑的步骤碰巧得到正确答案。这种“不可靠”的推理过程在金融、医疗、科学研究等高风险领域是不可接受的。论文希望解决这个问题，让模型的每一个推理步骤都更加坚实、可信。 克服“奖励攻击”（Reward Hacking）：在使用细粒度的过程奖励模型（PRM）时，策略模型（被训练的LLM）很容易学会利用PRM的漏洞来获得高分，而不是真正提升推理能力。例如，模型会生成大量冗余、重复的步骤来“刷分”。这严重阻碍了模型能力的提升。论文旨在解决这种奖励攻击问题，使训练过程更加稳定和有效。 对行业发展的重要意义： 这个问题的解决对于推动AI从一个“模式匹配器”向一个真正的“逻辑推理者”转变至关重要。一个能够生成可靠、可解释推理过程的AI，其应用价值将远超当前的模型。\n在科研领域，它可以成为科学家的得力助手，辅助进行复杂的数学推导和定理证明。 在教育领域，它可以为学生提供详细、正确的解题步骤，实现个性化辅导。 在工程和金融领域，它可以执行需要严密逻辑的分析和决策任务，提升安全性和效率。 因此，这项研究对于构建更高级、更值得信赖的AI系统具有奠基性的意义。 2. 论文提出了哪些新的思路、方法或模型？ 本文最核心的创新是提出了 PRocess cOnsistency Filter (PROF)，一个基于过程-结果一致性的数据管理框架。它与以往方法的根本区别在于，它不直接混合奖励信号，而是用奖励信号来筛选训练数据。\n核心思路：从“信号混合”到“数据筛选” 之前的方法（Blending）：通常的做法是将ORM的奖励 r_o 和PRM的奖励 r_p 进行加权求和，得到一个总奖励 r_total = w * r_o + (1-w) * r_p，然后用 r_total 来计算梯度并更新模型。这种方法的弱点在于，PRM的噪声会直接污染梯度信号，导致训练不稳定。 PROF的思路：PROF认为，ORM和PRM各有优劣，应该让它们在自己擅长的领域发挥作用。ORM绝对准确，适合用来做“黄金标准”的判断（答案对错）；PRM虽然有噪声，但能提供过程质量的相对排序。因此，PROF利用ORM进行分组，再利用PRM在组内进行排序和筛选。 具体方法与优势： 分离处理正确与错误样本：PROF的一个关键设计是，它将模型生成的解答根据最终答案的对错（由ORM判断）分为“正确组”（G+）和“错误组”（G-），并对这两组采用不同的筛选策略。 \u0026gt; Divide rollouts into correct group G+ … and incorrect group G- … Rank G+ and G- by r_pro separately, and keep the samples… \u0026gt; 引用自论文第五页的Algorithm 1，强调了分离处理的重要性。 这种分离处理的设计非常精妙，因为它承认了在不同结果下，过程奖励的意义是不同的。 ablation study（消融研究）也证明了这一点，如果不进行分离处理（PROF-GRPO w/o separation），性能会显著下降。\n基于一致性的筛选原则：PROF的筛选逻辑是基于“过程与结果的一致性”的。\n对于正确组，它保留高过程分的样本，过滤掉低过程分的样本。这相当于提纯了“过程好、结果也好”的正面范例。 对于错误组，它保留低过程分的样本，过滤掉高过程分的样本。这相当于提纯了“过程差、结果也差”的负面范例，同时避免了PRM出错导致的好过程被惩罚。 鲁棒性，避免奖励攻击：由于PRM的评分 r_pro 仅用于排序和过滤，不直接参与梯度计算，这从根本上切断了模型通过生成特定模式内容来“攻击”PRM的路径。论文中的Figure 3清晰地展示了这一点：Blend方法的熵损失迅速下降，响应长度失控，这是典型的奖励攻击现象；而PROF-GRPO则保持了健康的熵和稳定的响应长度，证明了其鲁棒性。\n模块化与通用性：PROF是一个独立的数据筛选模块，可以灵活地与各种强化学习算法（如本文中的GRPO）结合。它不依赖于特定的RL算法或PRM模型，具有很强的通用性。\n3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一系列周密详尽的实验来验证PROF方法的有效性、鲁棒性以及其对推理过程的积极影响。\n实验设计： 基线模型：实验设置了几个关键的对比基线： Base：原始的、未经RL训练的基础模型。 GRPO：仅使用结果奖励（ORM）进行训练的强化学习方法，代表了主流的RLVR（Reinforcement Learning with Verifiable Rewards）方法。 Blend-PRM-GRPO (Blend)：将过程奖励（PRM）和结果奖励（ORM）简单加权混合后，再用GRPO进行训练的方法，代表了直接混合信号的思路。 PROF-GRPO：本文提出的方法，即使用PROF进行数据筛选，再用GRPO进行训练。 模型与数据集：实验使用了两种不同规模的模型（Qwen2.5-Math-1.5B/7B 和 LLaMA-3.2-3B）以验证方法的通用性。训练数据使用大规模的Numina-Math数据集。 评测基准：为了全面评估性能，实验在五个广受认可的数学推理基准上进行测试，包括 Math500, Minerva Math, Olympiad Bench, AMC23, AIME24。 关键实验数据和结果： 最终准确率对比：Table 2的结果是核心证据。对于更强的Qwen2.5-Math-7B模型，PROF-GRPO的平均准确率达到了51.7%，比GRPO的49.9%高出1.8个百分点，比Blend方法的47.3%高出4.4个百分点。这证明了PROF在提升最终答案准确率上的显著优势。\n训练动态与鲁棒性：Figure 3展示了训练过程中的熵损失和响应长度曲线。\nBlend方法的熵损失曲线迅速下降至接近0，意味着模型变得过度自信，失去了探索能力。同时，其响应长度不受控制地持续增长，这是模型为了“刷”过程分而生成冗长内容的直接证据。 相比之下，PROF-GRPO的熵损失平稳下降，响应长度也保持在可控范围，与基线GRPO类似。这强有力地证明了PROF能够有效抑制奖励攻击。 中间步骤质量评估：为了证明PROF不仅仅提升了最终答案的准确率，还改善了推理过程的质量，论文在Figure 4中使用蒙特卡洛（Monte Carlo, MC）估计进行了分析。MC估计是一种通过从一个中间步骤开始多次采样，看最终能得到正确答案的概率，来评估该步骤价值的方法。 \u0026gt; The specific improvement gaps are 9.2% on Math500, 37.4% on Minerval Math… which are much larger than the outcome accuracy gap in Table 2. \u0026gt; 引用自论文第九页。结果显示，PROF-GRPO在所有五个基准上的MC估计值都显著高于GRPO，例如在Minerval Math上提升了高达37.4%。这表明，PROF确实让模型的每一步推理都变得更加“有价值”和可靠。\n4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为我们开辟了一个新的方向，即通过智能的数据管理来优化强化学习训练。基于此，未来的探索可以从以下几个方面展开：\n更先进的PRM与PROF的结合：PROF的性能在一定程度上依赖于所使用的PRM的质量。未来的研究可以探索如何开发更准确、更鲁棒的PRM模型。例如，可以研究能够进行因果推理的PRM，或者能够识别模型“不确定性”的PRM，并将它们与PROF框架结合，可能会取得更好的效果。\nPROF框架的自适应与动态化：当前的PROF使用固定的筛选比例。未来可以研究动态PROF，即根据训练阶段、模型能力或问题难度，自适应地调整筛选的严格程度。例如，在训练初期，可以放宽筛选标准以鼓励探索；在训练后期，则收紧标准以进行精调。这可能带来训练效率和最终性能的进一步提升。\n跨领域的应用与扩展：论文主要关注数学推理，但PROF的思想具有很强的通用性。 \u0026gt; Additionally, how to extend our method to other reasoning tasks, such as coding (Jimenez et al., 2023) and web navigation (Zhou et al., 2023) also deserves to be explored. \u0026gt; 引用自论文第十三页的结论部分。\n代码生成：在代码生成任务中，“结果奖励”可以是单元测试是否通过，“过程奖励”可以是代码的可读性、效率和规范性。PROF可以用来筛选出既能通过测试又风格优秀的代码。 AI Agent：在智能体（Agent）任务中，“结果奖励”是任务是否最终完成，“过程奖励”可以是每一步操作的效率和合理性。PROF可以用来训练出更高效、更“聪明”的智能体。 这些领域的成功应用将催生出新一代高可靠性的自动化工具，例如自动化编程助手、高级数据分析机器人等，这其中蕴含着巨大的技术和投资机会。 理论基础的探索：从理论上分析为什么基于“一致性”的筛选比“信号混合”更有效，可以为设计更优的算法提供指导。这可能涉及信息论、课程学习（Curriculum Learning）等领域的交叉研究。\n5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度来看，仍然存在一些可以进一步探讨和完善的地方：\n对PRM质量的依赖性：PROF的核心机制之一是使用PRM对推理过程进行排序。论文中使用的PRM（Qwen2.5-Math-PRM-7B）是专门为Qwen系列模型训练的，性能较强。如果换用一个质量较差、或者与基础模型存在较大分布差异的PRM，PROF的性能会如何变化？论文在 LLaMA 模型的实验中初步触及了这个问题，并发现此时仅过滤“正确组”效果更好，这暗示了PROF的鲁棒性在面对低质量PRM时可能需要调整策略。这是一个值得深入研究的问题。\n计算开销的讨论不足：PROF需要在每次训练迭代时，对所有生成的解答（rollouts）都运行一次PRM来获取过程分数。虽然这比在线训练一个PRM的开销要小，但相比仅使用ORM的GRPO，无疑增加了额外的计算成本。论文没有详细分析这部分开销对训练效率的具体影响，这在实际大规模部署时是一个需要考虑的因素。\n“一致性”定义的单一性：论文中的“一致性”是通过计算PRM分数的平均值来定义的。 \u0026gt; In this subsection, we investigate the influence of different computation methods of consistency score r_pro in addition to the mean of PRMs over steps… \u0026gt; 引用自论文第十二页。 论文虽然在消融研究中简单对比了 mean, minimum, sum 等聚合方式，但没有深入探讨更复杂的“一致性”度量。例如，是否可以度量推理步骤逻辑上的连贯性？或者一个步骤是否是后续步骤的必要前提？探索更多维度的“一致性”定义可能会让筛选更加精准。\n对矛盾样本的处理方式：PROF会过滤掉“过程好但结果错”的样本。虽然这可能是因为PRM判断失误，但也存在一种可能性：模型探索到了一个看似正确但实际有陷阱的推理路径。完全丢弃这些样本可能会让模型失去学习识别这类“陷阱”的机会。未来是否可以设计一种机制，对这些被过滤掉的“疑难样本”进行特殊处理和学习，可能会让模型变得更加鲁棒。\n6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中学习创新想法的您来说，这篇论文提供了几个非常宝贵的思想和启发：\n核心启发：将“优化问题”转化为“数据管理问题” 这是本文最大的智慧所在。当面对两个有噪声但互补的信号源时，我们通常的思维是设计一个复杂的融合算法来直接优化。而PROF告诉我们，或许可以退一步，用这些信号来“净化”我们的数据，用更优质的数据进行更简单的优化，效果反而更好。这种“数据中心AI（Data-Centric AI）”的思想在很多领域都极具价值。\n分离与征服（Divide and Conquer）的策略 PROF将样本分为“正确”和“错误”两组，并施以不同策略，这是一个非常强大的设计模式。它提醒我们，在处理异构数据时，不应试图用一个“万金油”式的规则去处理所有情况。识别数据的内在结构，并据此设计针对性的处理策略，往往能事半功倍。\n利用“信号的一致性”作为质量度量 当没有绝对的“真理标签”时，多个不同但相关的信号源之间的“一致性”本身就是一种非常强的质量信号。PROF正是利用了ORM和PRM之间的一致性来识别高质量的训练样本。这个想法可以被广泛应用，例如，在多模态学习中，可以利用文本和图像描述的一致性来筛选数据。\n需要补充的背景知识： 为了完全理解并应用这篇论文的思想，您可能需要补充以下背景知识：\n强化学习基础：特别是策略梯度（Policy Gradient）方法，以及经典的PPO (Proximal Policy Optimization)算法。文中的GRPO (Group Relative Policy Optimization)是PPO的一个变种，理解PPO是理解GRPO的基础。 大语言模型的对齐技术：了解从人类反馈中强化学习（RLHF）的整个流程，因为它是在LLM中应用RL的通用范式。 奖励模型（Reward Modeling）：深入理解结果奖励模型（ORM）和过程奖励模型（PRM）的设计理念、训练方法以及各自的优缺点。了解相关工作如 LLM-as-a-judge 会非常有帮助。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中汲取有价值的创新灵感。\n","date":"2025-09-08T00:00:00Z","permalink":"/beyond-correctnessharmonizing-process-and-outcome-rewards-through-rl-training/","title":"BEYOND CORRECTNESS：HARMONIZING PROCESS AND OUTCOME REWARDS THROUGH RL TRAINING"},{"content":"思想就是WEB AGENTS WITH WORLD MODELS ：LEARNING AND LEVERAGING ENVIRONMENT DYNAMICS IN WEB NAVIGATION的思想。\n论文综合解读：文本游戏中的智能体设计——一个概念性框架 这篇题为《Design and Optimization of Reinforcement Learning-Based Agents in Text-Based Games》的论文，探讨了一个在人工智能领域中兼具经典与前沿的课题：如何让AI在纯文本环境中通过理解语言、进行推理和决策来完成复杂任务。文本游戏（Text-Based Games），作为一种独特的媒介，因其完全依赖自然语言进行交互，对AI的语言理解、逻辑推理和长期规划能力提出了极高的要求，因此被视为检验AI综合能力的理想“试炼场”。\n论文的核心思想是构建一个基于深度强化学习（Deep Reinforcement Learning） 的智能体（Agent）框架。这个框架的运作逻辑可以分为两个核心阶段：\n环境理解与世界模型构建：智能体首先需要读懂游戏中的文本描述。比如，当游戏提示“你正站在一个昏暗的洞穴入口，北边有一条小径”时，智能体需要解析出“地点：洞穴入口”、“环境：昏暗”、“可行动作：向北走”等关键信息。论文提出使用深度学习模型（Deep Learning Models），特别是预训练语言模型（Pre-trained Language Models），来完成这项任务。这些模型擅长捕捉文本中的语义关系和上下文依赖，能够将非结构化的游戏文本转化为结构化的世界模型（World Model）。这个世界模型就像智能体在脑海中构建的一幅动态地图，包含了它对当前环境状态、可用动作以及动作可能导致后果的理解。\n正如论文在3.1节中提到的：“Deep learning models can parse game text to derive implicit environmental structure and informative facts, accurately rebuild a game’s world state.” （深度学习模型可以解析游戏文本，推导出隐含的环境结构和信息事实，从而精确地重建游戏的世界状态。）\n决策优化与策略学习：在理解了环境之后，智能体需要决定下一步该做什么，是“向北走”还是“检查背包”？这个决策过程是通过策略梯度（Policy Gradient） 方法进行优化的。简单来说，策略梯度是一种让智能体直接学习“在什么状态下应该采取什么行动”的策略函数的方法。智能体会在游戏中不断尝试（trial-and-error），如果某个行为序列最终带来了好的结果（例如，找到了宝藏，获得了高分），那么导致这个好结果的每一步决策都会被“加强”（增加其在未来被选择的概率）；反之，如果导致了坏结果（例如，游戏结束），那么相应的决策就会被“削弱”。这个过程通过奖励信号来引导，不断迭代，最终智能体的策略会收敛到最优或接近最优。\n论文在4.1节中对此进行了解释：“Policy gradient methods suggest parameterized policy functions that maximise cumulative return in expectation without using value functions.” （策略梯度方法提出使用参数化的策略函数，在期望上最大化累积回报，而无需借助价值函数。）\n论文强调，这种结合了深度学习（用于理解）和强化学习（用于决策）的框架，相比传统的基于规则或模板的智能体，具有显著的优势。传统方法难以应对文本游戏中动态、复杂和多变的环境，而该论文提出的智能体能够更好地泛化和适应。论文声称，经过优化的智能体在多个文本游戏实验中，其“游戏完成率和胜率显著超越了以往的智能体”。\n然而，通篇读下来，这篇论文更像是一个高层次的框架性综述和方法论介绍，它清晰地阐述了“什么”问题以及“用什么”方法，但在“具体怎么做”以及“效果究竟如何”的细节上着墨不多。它为我们描绘了一幅宏伟的蓝图，即如何打造一个能在纯文本世界中自由探索和解决问题的通用AI，但并未提供具体的模型架构、详尽的算法伪代码或可供复现的实验数据。尽管如此，它所提出的模块化设计思想（文本解析、行动生成、反馈调整）以及对未来方向的展望，仍然为该领域的研究者和开发者提供了富有价值的思考框架和方向指引。\n接下来，我将根据您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是设计并优化一种能够在文本游戏中高效运行的强化学习智能体（Reinforcement Learning-Based Agent）。\n解决的实际问题：\n高维状态空间问题：文本游戏的状态空间是巨大的，因为任何微小的文本变化都可能代表一个新状态。该研究旨在解决如何让AI在这种高维、稀疏的文本环境中有效学习。 自然语言理解与决策的结合：它试图解决一个核心的AI难题——如何将对自然语言的深刻理解（Comprehension）与复杂的序贯决策（Decision-Making）无缝结合起来。 传统智能体泛化能力差：如论文2.3节所述，传统基于规则的智能体无法适应动态复杂的游戏环境。本文旨在开发一个更具适应性和泛化能力的智能体。 对行业发展的重要意义：\n通用人工智能的探路石：文本游戏是通往通用人工智能（AGI） 的一个重要研究平台。如果一个AI能够掌握语言、推理和规划，那么它离处理更广泛的现实世界任务就更近了一步。 推动下一代人机交互：这项研究的成果可以被迁移到其他需要深度语言理解和交互的领域，例如更智能的对话系统（Dialogue Systems）、自动化的任务执行助手、以及能够理解和执行复杂指令的机器人。 大模型的“能力评估器”：在当前大模型（LLM）飞速发展的背景下，如何评估和提升模型的推理与规划能力是一个核心挑战。文本游戏为此提供了一个绝佳的、可控的评估环境。能够在此类环境中表现优异的模型，通常意味着其具备了更强的综合能力。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 提出的思路与方法： 论文提出的并非一个全新的、颠覆性的算法，而是一个模块化的设计框架（Modular Design Framework）。这个框架整合了现有技术，并强调了它们的协同作用。其核心流程如下： 文本解析模块（Text Parsing Module）：基于预训练语言模型，将游戏文本解析为结构化的世界模型。 行动生成模块（Action Generation Module）：基于策略梯度方法，根据当前世界模型生成最优行动策略。 反馈调整模块（Feedback Adjustment Module）：根据游戏环境的奖励信号，动态调整决策模型。 论文在5.1节中描述了这种模块化设计：“With modular design, the agent is separated into function modules of text parsing, action generation, and adjustment of feedback.”\n特点与优势： 解耦设计：将“理解”和“决策”两个复杂问题在功能上解耦，使得每个模块可以被独立优化和升级。例如，我们可以轻易地将文本解析模块从一个较旧的语言模型升级到一个更先进的模型，而无需改变整个系统架构。 端到端的学习：整个框架是端到端（End-to-End）的，智能体直接从原始文本输入学习到最终的行动输出，中间过程（如世界模型的构建）也是学习的一部分，这比需要大量人工特征工程的传统方法更为强大和灵活。 直接策略优化：采用策略梯度方法直接优化策略，而不是像一些传统强化学习方法（如Q-learning）那样先学习价值函数。论文在4.3节中指出，这种方法在处理高维行动空间时更具优势，更容易发现好的解决方案。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 这是这篇论文最薄弱的环节。论文多次声称其方法取得了显著成功，但并未提供任何具体的、可量化的实验细节和数据。\n实验设计（根据论文描述推断）：\n任务：在“几个文本游戏实验（several text-based game experiments）”中进行测试。 评估指标：主要评估了两个指标：游戏完成率（game completion ratio） 和 胜率（win rate）。 对比基线：将“优化后的智能体（optimized agents）”与“常规智能体（regular agents）”和“之前的智能体（previous agents）”进行了比较。 实验数据和结果（论文的定性描述）： 论文在摘要和5.2、5.3节中给出了结论性的描述，但缺乏数据支撑。 \u0026gt; “The enhanced agent works better in several text-based game experiments and significantly surpasses previous agents on game completion ratio and win rate.” \u0026gt; \u0026gt; “In task completion, optimized agents are more efficient and accurate compared to regular agents… In win ratios, in multiple rounds of game experiments, optimized agents obviously surpass regular agents.”\n为了更清晰地展示论文所缺失的内容，我设计了一个表格，来说明一个标准的实验结果部分应该包含什么。\n实验设置 游戏A (Game A) 游戏B (Game B) 游戏C (Game C) 对比方法 完成率 / 胜率 完成率 / 胜率 完成率 / 胜率 传统规则智能体 (Baseline 1) （数据缺失） （数据缺失） （数据缺失） 常规RL智能体 (Baseline 2) （数据缺失） （数据缺失） （数据缺失） 本文提出的优化智能体 （数据缺失） （数据缺失） （数据缺失） 总结：论文声称通过实验验证了方法的有效性，但由于完全没有提供实验环境、具体模型参数、训练细节、对比基线的明确定义以及最重要的——定量实验数据，其结论的科学性和可信度大打折扣。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 尽管论文本身比较宏观，但它所指出的方向确实是当前大模型领域的前沿热点。未来的探索和挑战包括：\n挑战与问题： 长期记忆与规划（Long-term Memory and Planning）：文本游戏通常需要跨越数千步的长期规划。如何让大模型智能体维持长期记忆，避免“遗忘”关键线索，并进行有效的远期规划，是一个巨大的挑战。 世界模型的准确性与动态更新：如何确保大模型构建的内部世界模型与游戏真实状态保持一致，尤其是在环境信息不完全或存在欺骗性文本时。 探索与利用的平衡（Exploration-Exploitation Trade-off）：如何让智能体在未知环境中进行有效探索，而不是陷入局部最优解，这是强化学习中的一个经典难题，在复杂的文本世界中尤为突出。 样本效率（Sample Efficiency）：目前的强化学习方法通常需要海量的交互数据（数百万甚至数十亿步）才能学会一个任务，如何提高学习效率，让智能体像人类一样从少量经验中学习，是关键所在。 催生的新技术和投资机会： Agentic AI框架：这一研究方向直接催生了各种智能体框架（Agentic AI Frameworks），如AutoGPT、BabyAGI等。未来的投资机会在于开发更强大、更可靠、更通用的智能体平台，服务于自动化办公、个人助理、软件开发等领域。 大模型推理能力增强技术：为了解决规划和推理问题，会催生出如思维链（Chain-of-Thought）、思维树（Tree-of-Thought） 以及更复杂的模型推理和验证技术。这些技术本身就是提升大模型能力的关键，具有巨大的商业价值。 交互式环境与模拟平台：对高质量、多样化的文本交互环境的需求会增加，这为开发用于训练和评估AI智能体的高级模拟平台（Simulation Platforms） 带来了机会。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 从一个严格的学术评审角度来看，这篇论文存在多个严重不足：\n缺乏实证证据：如前所述，论文最大的问题是完全没有提供可验证的实验数据。这使其所有关于“效果显著提升”的结论都变成了无法证实的“断言”。 方法描述过于笼统：论文提到了“深度学习模型”和“策略梯度方法”，但没有指明任何具体的模型架构（如Transformer, LSTM）或算法（如REINFORCE, A2C, PPO）。这使得研究无法被复现。 缺乏与SOTA（State-of-the-Art）的比较：没有将自己的方法与当前该领域的最新、最强的方法进行比较，无法判断其宣称的优势在学术界所处的位置。 忽略了关键挑战的讨论：论文对实现这一框架的难度估计不足，几乎没有讨论诸如训练成本、样本效率、长期记忆等实际操作中会遇到的核心难题。 文献综述不充分：引用的参考文献较少，且没有系统地梳理该领域的历史发展和关键工作，使其立论基础显得不够扎实。 总而言之，这篇论文更像是一份研究计划书（Research Proposal） 或一篇概念性文章（Position Paper），而非一篇严谨的、贡献了新知识的学术研究论文（Research Paper）。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 尽管论文有诸多不足，但它提供的框架性思想仍然非常有启发性。您可以重点学习以下几点：\n核心启发：模块化与解耦的思想 “世界模型 + 决策模型”的范式：这是最核心的启发。无论您在做什么应用，只要涉及“理解环境并作出决策”，都可以借鉴这个范式。例如，在开发一个自动数据分析工具时，可以先用一个大模型来“理解”数据和用户需求（构建世界模型），再用另一个模块来规划分析步骤和生成代码（决策模型）。 可插拔的组件化设计：将复杂的AI系统拆解为功能独立的模块。这使得系统更易于维护、调试和升级。您可以专注于优化某个特定模块，比如用最新的模型来提升系统的某一特定能力，而不用推倒重来。 需要补充了解的背景知识： 强化学习基础： 核心概念：深入理解状态（State）、动作（Action）、奖励（Reward）、策略（Policy）、价值函数（Value Function）。 经典算法：至少要了解Q-Learning和Policy Gradients的基本原理。特别推荐学习PPO（Proximal Policy Optimization），因为它是目前最常用和最稳健的策略梯度算法之一。 大语言模型（LLMs）： Transformer架构：了解其自注意力机制（Self-Attention）是如何工作的，这是理解所有现代LLM的基础。 Prompt Engineering：学习如何通过设计有效的提示（Prompt）来引导LLM完成特定任务，这是将LLM用作“文本解析模块”的关键技能。 智能体架构： ReAct框架：了解结合了推理（Reasoning） 和 行动（Acting） 的ReAct框架，这是一种非常实用的大模型智能体设计模式。 经典文本游戏环境：可以尝试了解一些经典的用于AI研究的文本游戏平台，如TextWorld或Jericho，它们能帮助您更好地理解该领域的实际挑战。 通过吸收这篇论文的宏观框架思想，并补充上述具体的背景知识，您就能将这些想法更有效地应用到您自己的创新项目中。\n","date":"2025-09-08T00:00:00Z","permalink":"/design-and-optimization-of-reinforcement-learning-based-agents-in-text-based-games/","title":"Design and Optimization of Reinforcement Learning-Based Agents in Text-Based Games"},{"content":" 并行采样，然后根据所有的采样进行refine。\n论文深度解读：从“择优”到“创优”的范式革命 大语言模型（LLMs）在解决复杂、多步骤推理问题上的能力，一直是衡量其智能水平的关键标尺。为了突破现有模型的性能上限，学术界和工业界探索了多种路径，其中测试时间扩展（Test-Time Scaling, TTS） 成为一个极具吸引力的方向。其核心思想是在不改变模型参数的情况下，通过增加推理时的计算开销来换取更好的性能。在这其中，以Best-of-N (BoN)和多数投票（Majority Voting）为代表的并行方法最为普遍：模型生成N个候选答案，然后通过外部验证器（如奖励模型）或内部一致性来挑选最佳答案。\n然而，这篇论文敏锐地指出了这类方法的根本局限性。正如论文在引言中提到的：\nThe performance of majority voting and BoN, is inherently bounded by the quality of the set of candidates. They are mechanistically incapable of producing a solution that transcends the quality of candidate proposals, which becomes particularly problematic when all candidates are flawed.\n换言之，无论“挑选”机制多么精妙，它终究无法在一个全是错误答案的集合中凭空创造出正确答案。这就像一群探险者都走错了路，你最多只能从他们中间选一条“看起来错得最不离谱”的路，却无法找到正确的出路。这个性能上限，在处理极具挑战性的问题时尤为致命。\n为了打破这一“选择”的桎梏，论文提出了一个全新的框架——生成式自我精炼（Generative Self-Refinement, GSR）。GSR的核心思想极具颠覆性：它让模型扮演两个角色——既是“解题者”，也是“评审与综合者”。其流程分为两步： 1. 并行生成（Generation）： 与传统方法类似，模型首先并行地生成一组多样化的候选答案。 2. 自我精炼（Self-Refinement）： 关键的创新在于此。模型接收原始问题以及自己刚刚生成的所有候选答案，然后被要求对这些答案进行分析、诊断、汲取其中有价值的推理步骤或思路（即便最终答案是错的），并最终合成一个全新的、更优的解决方案。\n论文中的图1完美诠释了GSR的威力。面对一个数学题，所有四个候选答案都得出了错误的解（5624），因为它们在计算中都犯了同样的错误（忽略了c=9的可能性）。传统的BoN或多数投票方法在此场景下必然失败。但GSR模型在精炼阶段，通过分析候选答案，敏锐地发现了这个共同的思维盲点（\"Wait, but in their calculation, they set c = 2. So perhaps they missed the possibility of c = 9.\"），并基于此进行了修正，最终得出了正确的答案（5694）。这清晰地证明了GSR具备了从错误中学习并创造正确答案的能力，实现了从“择优”到“创优”的范式转变。\n当然，仅仅通过提示（Prompting）很难稳定地激发模型这种高级的精炼能力。因此，论文提出了第二个关键贡献：混合训练流程（Hybrid Training Pipeline）。他们不依赖模型自发的“涌现”能力，而是通过监督微调（Supervised Fine-Tuning, SFT）的方式，显式地向模型“教授”这种技能。他们构建了一个包含两类任务的混合数据集： * 直接求解（Direct-Solving）： 传统的“问题-答案”对。 * 自我精炼（Self-Refinement）： “问题+多个候选答案-更优答案”对。\n通过在这种混合数据集上进行训练，模型被同时优化了两个互补的目标，使其既能独立解决问题，也擅长在他人（甚至是自己）的思考基础上进行批判和升华。\n实验结果极为有力地支撑了论文的论点。在AIME24、MATH500等五个极具挑战性的数学基准测试中，经过混合训练的7B模型（GSR-7B）的性能远超基线模型。最震撼人心的数据来自表2的细粒度分析（Fine-Grained Analysis）。该分析统计了在不同数量的正确候选答案（Nc）下，各种方法的表现。结果显示： * 当Nc ≥ 3时（大部分候选答案正确），所有方法表现都很好。 * 当Nc = 1时（只有一个正确答案），GSR的优势开始显现，在AIME24上达到了60.2%的准确率，远超多数投票的18.8%。 * 最关键的是，当Nc = 0时（所有候选答案均错误），GSR-7B在AIME24上依然取得了5.9%的成功率，而所有其他基线方法的成功率均为0%。\n这个5.9%虽然数值不大，但意义非凡。它雄辩地证明了GSR框架成功突破了“选择”方法的理论上限。此外，论文还通过消融实验证明了混合训练的必要性，并通过在更大模型（14B, 32B）和跨领域任务（逻辑谜题）上的实验，验证了该方法的可扩展性（Scalability）和泛化性（Generalizability）。\n总而言之，这篇论文不仅仅是提出了一种新方法，更是为提升大模型推理能力提供了一种全新的、更具想象力的范式。它告诉我们，与其寄希望于模型能“一发即中”，不如教会它如何在一个充满不确定性和错误的环境中，进行反思、批判和创造，这或许才是通往更高级机器智能的关键一步。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是提升大语言模型在解决复杂、多步骤推理问题上的性能，特别是突破现有测试时间扩展（TTS）方法的性能瓶颈。\n想要解决的实际问题：\n“选择”范式的上限问题：当前主流的TTS方法，如Best-of-N和多数投票，本质上都是“选择”机制。它们的性能上限被最初生成的候选答案集合的质量所束缚。如果所有候选答案都是错误的，这些方法就束手无策。这在处理高难度、低成功率的任务（如奥数级数学题、前沿科学问题）时是一个非常现实的痛点。 外部依赖与部署成本：Best-of-N等方法通常依赖一个强大的外部验证器（例如，一个专门训练的奖励模型），这不仅增加了系统的复杂性，也带来了额外的训练和部署成本。论文提到：\"...introducing an external model to verify or fuse imposes complex data curation, extra compute cycles and GPU memory occupancy...\"。 对行业发展的重要意义：\n提升AI能力的“天花板”：在科学发现、数学证明、软件工程等领域，问题的复杂性极高，模型一次性生成完美答案的概率很低。GSR这种“从错误中学习并创造”的能力，为AI在这些前沿领域的应用打开了新的可能性，直接提升了AI系统能够解决问题的难度上限。 更高效的模型利用：在模型尺寸和训练成本日益高昂的今天，如何更充分地挖掘现有模型的潜力变得至关重要。GSR提供了一种在“推理阶段”压榨模型性能的有效途径，使得一个中等规模的模型（如论文中的7B模型）通过更智能的推理策略，其表现能够媲美甚至超越更大规模的模型。 推动AI向更“鲁棒”的推理演进：现实世界的决策过程充满了不完美的信息和试错。GSR框架模拟了人类专家“集思广益、批判性思考”的过程，推动AI从简单的模式匹配向更具鲁棒性和反思能力的推理智能演进。这对于开发更可靠、更值得信赖的AI系统至关重要。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路与模型： 生成式自我精炼（Generative Self-Refinement, GSR）框架：这是论文最核心的思路。它将推理过程分解为“并行生成”和“自我精炼”两步，让单个统一的模型完成从初步尝试到最终优化的全过程。 混合训练流程（Hybrid Training Pipeline）：为了让模型掌握GSR的能力，论文设计了一种创新的训练方法。它不依赖模型的自然涌现，而是通过构建一个包含“直接求解”和“精炼求解”两种任务的混合数据集，对模型进行监督微调，从而显式地将这种“反思和提炼”的能力注入模型。 与之前方法的比较、特点和优势： 特性 多数投票 (Majority Voting) Best-of-N (BoN) 融合方法 (Fusion Methods) 生成式自我精炼 (GSR, 本文) 核心机制 选择出现频率最高的答案 依赖外部验证器（奖励模型）选择得分最高的答案 训练一个专门的“合成器”模型来融合多个答案 单一模型自我生成候选，然后自我分析和合成新答案 性能上限 受限于候选集质量，无法生成新答案 受限于候选集质量，无法生成新答案 理论上可生成新答案，但生成和融合解耦 能够超越候选集，从全错的候选中生成正确答案 模型依赖 单一生成模型 生成模型 + 外部奖励模型 生成模型 + 外部合成器模型 单一、统一的模型 系统复杂度 低 高 高 低 核心优势 简单，无额外模型 效果通常优于多数投票 能整合多个候选的有效信息 突破性能上限，系统简洁，能力内化 核心劣势 在候选质量低时失效 依赖奖励模型质量，成本高 训练和推理流程复杂，生成与融合能力分离 推理计算成本高于单次生成（但低于BoN） 关键优势分析：GSR的最大优势在于其“闭环”和“内化”的特性。它使用同一个模型来完成所有任务，使得生成能力和精炼能力可以相互促进。正如论文所说，这种设计避免了\"...decouples the connection between the generation and integration, therefore precluding any synergy that might exist...\"。更重要的是，它赋予了模型“创造性纠错”的能力，这是所有基于“选择”的方法都无法企及的。\n### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨且全面的实验来验证GSR的有效性。\n实验设计： 基准测试（Benchmarks）：选用了五个学术界公认的高难度数学推理数据集：AIME24, AIME25, AMC22 \u0026amp; AMC23, MATH500, 和 OlympiadBench。这些数据集覆盖了从高中到奥林匹克竞赛级别的数学问题，能充分考验模型的推理能力。 基线方法（Baselines）： 多数投票（maj@4）：从4个候选答案中选出最常见的。 Best-of-N（BoN@4）：使用多种先进的奖励模型（如Skywork-RM-27B, RRM-7B, RM-R1-7B）从4个候选中选出最佳。 融合方法（fuse@4）：使用专门的Synthesizer-8B模型来融合4个候选答案。 核心模型：实验主要围绕一个7B大小的模型（Qwen2.5-7B-Instruct）进行，训练后的模型被称为GSR-7B。 评估指标：主要使用pass@1（直接生成的准确率）和各种TTS策略下的准确率（maj@4, BoN@4, selfRef@4）。 关键实验数据和结果： 总体性能对比（表1）：这是最直观的结果。 GSR-7B在五个基准上的平均准确率达到了73.6%。 这显著优于所有基线，包括最强的BoN方法（RM-R1-7B đạt 70.9%）和融合方法（Synthesizer-8B đạt 69.6%）。 在最具挑战性的AIME24上，GSR-7B取得了66.0%的准确率，甚至超过了OpenAI的一个未公开的强大模型（OpenAI-01-mini 的 56.7%）。 细粒度分析（表2）：这是证明GSR核心优势的关键实验。 \u0026gt; 该实验分析了在AIME24上，当提供的4个候选答案中分别有4, 3, 2, 1, 0个正确答案时，不同方法的表现。 当所有候选答案都错误时 (Nc = 0)，GSR-7B的准确率为5.9%，而所有其他方法的准确率均为0%。这无可辩驳地证明了GSR的“创优”能力。 当只有一个正确答案时 (Nc = 1)，GSR-7B的准确率高达60.2%，远超多数投票（18.8%）和各种BoN方法（最高56.3%），显示出其在模糊和困难情境下的强大鲁棒性。 泛化能力测试（图6）： 实验将在纯数学数据上训练的GSR-7B模型，直接用于一个全新的领域——K\u0026amp;K逻辑谜题。 结果显示，GSR-7B的selfRef@4性能相比其maj@4提升了7.2%，而未经训练的基座模型反而下降了7.6%。这证明了GSR训练所获得的“精炼”能力是一种可泛化的通用技能，而不仅仅是对自己已知错误的修补。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个研究方向极具潜力，以下是一些值得探索的问题、挑战以及可能的机会：\n值得探索的问题和挑战： 推理效率与成本：GSR虽然有效，但其“生成+精炼”的两步过程增加了推理延迟和计算成本。未来的研究可以探索如何将这两个过程更高效地融合，例如，通过单步推测性解码（Speculative Decoding）或设计专门的模型架构，在一次前向传播中同时完成生成与精炼。 精炼能力的深化：目前在Nc=0的情况下，成功率（5.9%）虽然突破了0，但仍有很大提升空间。如何让模型更深入地诊断候选答案中的逻辑谬误、知识错误或计算错误，是一个核心挑战。这可能需要结合形式化验证（Formal Verification）或工具使用（Tool Use）来增强精炼的可靠性。 多模态与更广泛领域的应用：GSR目前在文本推理（数学、逻辑）上得到了验证。它能否被扩展到多模态任务中？例如，生成多张设计草图后，精炼出一张融合了所有优点的新图；或是在代码生成中，综合多个有bug的片段，生成一个正确的程序。 与Agent框架的结合：当前的AI Agent框架强调“规划-执行-反思”的循环。GSR提供的“精炼”能力可以作为Agent“反思”阶段的一个强大构件。Agent可以执行多个失败的计划（候选答案），然后利用GSR来生成一个更优的新计划。 可能催生的新技术和投资机会： “推理即服务”（Reasoning-as-a-Service）：未来可能会出现专门提供高级推理能力的服务。这些服务背后可能就是由GSR这类技术驱动的模型，它们擅长处理客户提出的复杂、开放式问题，提供高质量的、经过多轮反思和提炼的解决方案。 智能数据增强与合成：GSR的训练流程本身就是一种高质量数据的生成引擎。利用一个强大的教师模型，可以为海量问题生成“候选答案集+精炼答案”的数据对，这本身就是一种极具价值的合成数据，可以用来训练更小、更高效的专业模型。 下一代AI开发工具：可能会涌现出新的AI开发平台，它们内置了GSR这类“自我修正”、“自我提升”的训练和推理框架，帮助开发者更轻松地构建在特定领域具有超强推理能力的AI应用。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的角度审视，仍存在一些可以进一步完善的地方：\n成本效益分析的缺失：论文证明了GSR在性能上的优越性，但缺少对其计算成本的详细分析。例如，生成4个候选答案并进行一次精炼的总计算量，与直接使用一个更大规模（如32B或70B）的模型进行一次高质量生成相比，哪个更具性价比？在实际部署中，这是一个关键的权衡，论文对此讨论不足。 对候选答案多样性的依赖：GSR的有效性可能在一定程度上依赖于初始生成的候选答案具有足够的多样性。如果模型生成的多个答案高度同质化，都陷入了同一个思维陷阱，那么精炼步骤可能也难以跳出。论文中提到使用较高的temperature来增加多样性，但对多样性与最终性能的关系缺乏定量的分析。 训练数据的构建和泛化边界：GSR的能力是通过特定方式构建的混合数据“教会”的。这种能力的泛化边界在哪里？如果面对一个与训练数据分布差异很大的全新领域的精炼任务，它的表现会如何？虽然逻辑谜题的实验提供了一些证据，但还需要更广泛的跨领域验证。 对“为什么”的探索不足：论文展示了GSR“能行”，但对于其内部机制的探索还可以更深入。例如，模型在精炼时，其注意力机制是如何在不同的候选答案之间分配的？它是如何识别并利用“虽败犹荣”的推理片段的？更深入的可解释性分析（Interpretability Analysis）将使这项工作更有说服力。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文是创新思想的宝库。以下是您可以重点学习和借鉴的几个方面：\n重点学习的核心思想： “生成-反思-综合”模式：这是最核心的启发。在任何需要AI生成复杂内容的场景（无论是写文章、写代码、做设计还是制定策略），都可以借鉴这个模式。不要满足于第一个输出，而是让模型生成多个草稿，然后明确指示它对这些草稿进行批判性分析，并创造一个“集大成”的最终版本。 将高级能力“教给”模型，而非等待“涌现”：不要假设模型天生就具备某种复杂能力。像论文作者一样，思考如何将这种能力分解，并构建成一个明确的监督学习任务。通过创造高质量的训练数据，你可以“教会”模型你想让它掌握的任何高级技能，这比单纯依赖Prompt工程要可靠得多。 利用“错误”作为学习材料：传统的训练数据通常是“正确”的。本文的创新在于，它将模型的“错误输出”也作为了学习过程的一部分。这启发我们，在很多任务中，失败的尝试本身就蕴含着宝贵的信息。我们可以设计让模型从错误中学习的流程，从而变得更加鲁棒。 可以应用的具体启发： 在您的工作流中：如果您在使用LLM进行内容创作，可以设计一个两步的Prompt链。第一步：“请就[主题]提供三种不同风格的草稿。”第二步：“现在，请分析以上三份草稿的优缺点，并结合它们的优点，创作一份最终的、更完美的版本。” 在模型训练中：如果您在微调模型，可以借鉴混合训练的思路。除了主任务数据，可以加入一些“元任务”（meta-task）数据，比如让模型判断两个解决方案的优劣，或者让模型从一个有缺陷的方案中修复bug。这能有效提升模型的基础推理和逻辑能力。 需要补充了解的背景知识： 测试时间扩展（TTS）：您需要深入了解这个领域，特别是自洽性（Self-Consistency）和Best-of-N的原理、优缺点和代表性工作。 思维链（Chain-of-Thought, CoT）：GSR处理的是复杂的推理任务，其候选答案和精炼过程都是以CoT的形式展开的。理解CoT是理解本文工作的基础。 奖励模型（Reward Modeling）：了解奖励模型是如何工作的，对于理解GSR所要替代的BoN方法的机制至关重要。 监督微调（Supervised Fine-Tuning, SFT）：这是本文实现GSR能力的核心技术手段，对其有深入了解将帮助您更好地应用论文的思想。 ","date":"2025-09-08T00:00:00Z","permalink":"/learning-to-refineself-refinement-of-parallel-reasoning-in-llms/","title":"Learning to Refine：Self-Refinement of Parallel Reasoning in LLMs"},{"content":" 这篇论文的核心，在于它直面了当前大模型智能体（LLM Agents）领域一个非常棘手且普遍存在的问题：智能体应该在什么时候思考（规划），什么时候行动？ 这并非一个哲学问题，而是一个关乎计算效率、任务性能和最终智能体实用性的核心工程与科学问题。\n在解读之前，我们先要理解一个关键概念：测试时计算（Test-Time Compute）。简单来说，这指的是模型在接收到用户输入后、给出最终输出前所花费的计算资源。例如，通过“思维链（Chain-of-Thought）”让模型一步步思考，就是在增加测试时计算，以期获得更好的结果。对于智能体而言，“规划”就是一种典型的增加测试时计算的方式。\n现有的智能体框架，如经典的 ReAct (Reason-Act)，通常采用一种简单粗暴的策略：在每一步行动前都进行规划。这种“凡事三思而后行”的模式，虽然在某些简单任务上有效，但论文敏锐地指出其两大弊病： 1. 计算成本高昂：每一步都生成长篇的规划文本，极大地增加了推理时间和成本。 2. 性能退化：在长周期的复杂任务中，过于频繁的规划（或称“重新规划”）会导致智能体行为不稳定，可能在几个子目标间来回摇摆，或者放弃一个原本可行的计划，这种现象被论文精辟地称为不稳定性成本（Instability Cost）。\n与此相对的另一个极端是从不规划，智能体仅根据当前观察和历史信息直接输出行动。这种方式虽然快，但显然缺乏远见，难以完成需要多步协调的复杂任务。\n这篇论文的真正洞见在于，它明确指出“总是规划”和“从不规划”都是次优策略。最优解存在于两者之间，存在一个“金发姑娘区（Goldilocks Zone）”——一个既不过度规划、也不缺乏规划的最佳频率。而这篇论文最大的贡献，就是提出了一个系统性的方法，让智能体能够学会动态地、自适应地决定何时需要规划，从而将计算资源智能地分配到最需要的决策点上。\n为了实现这一目标，论文设计了一个简洁而强大的两阶段训练流程： 1. 监督微调（SFT）启动（Priming with SFT）：此阶段的目标是向模型“注入”规划的概念和能力。研究者们使用一个更强大的“教师模型”（Llama-3.3-70B）在游戏环境（Crafter）中生成了大量的“专家轨迹”。这些轨迹数据不仅包含了一系列的行动，还包含了在某些步骤生成的自然语言规划（\u0026lt;plan\u0026gt;...\u0026lt;/plan\u0026gt;）。通过在这些数据上进行监督微调，一个较小的“学生模型”（Llama-3.1-8B）学会了规划的格式、语言以及如何将规划与行动联系起来。正如论文所强调的，这一步至关重要，它为模型提供了必要的“归纳偏置（inductive biases）”，为后续学习打下基础。一个非常有趣的发现是（如图1c所示），即使是基于完全相同的行动序列进行训练，获得了规划文本信息（Primed-Dynamic）的模型，其模仿学习的效果也显著优于只学习行动本身（Primed-Naive）的模型。这说明，规划文本不仅仅是需要被模仿的行为，更是理解和学习后续行为的强大“催化剂”。\n强化学习（RL）微调（Multi-step RL）：在模型具备了基础的规划能力后，第二阶段通过强化学习让模型学会在真实环境中策略性地使用这种能力。智能体在环境中与交互，根据任务的成功与否获得奖励。同时，生成规划所消耗的计算资源（以token数量衡量）会作为一个负向奖励（惩罚项）被计入总回报中。通过优化这个目标函数，智能体被激励去找到一个平衡点：只在那些“规划的预期收益”大于“规划的计算成本”的时刻进行规划。实验结果（如图1e所示）清晰地表明，经过SFT+RL完整训练的动态规划智能体，在学习效率和最终性能上，都显著超越了所有基线方法，包括从头开始用RL训练的智能体。 更令人印象深刻的是，论文还展示了这种动态规划智能体的可控性（Steerability）。一个经过充分训练的智能体，能够理解并遵循人类提供的高级计划。在实验中，通过人类的指导，该智能体成功完成了在Crafter游戏中挖掘钻石的终极任务——这是任何自主智能体都未能实现的壮举（如图4和图10所示）。这不仅证明了方法的有效性，也为构建更安全、更可信、人机协作能力更强的智能体系统指明了方向。\n总而言之，这篇论文不仅仅是提出了一种新的训练技巧，它提供了一个关于如何高效利用LLM进行顺序决策的概念框架，并通过一个精心设计的两阶段学习范式成功地将其付诸实践。它将“何时规划”从一个固定的、由人类设计的超参数，转变为一个由智能体在与环境交互中自主学习的动态策略，这是迈向更高效、更自适应、更具扩展性智能体系统的重要一步。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：核心目标是训练一个大型语言模型智能体（LLM Agent），使其能够动态地、高效地分配其在测试时的计算资源，即学会自主决定在何时进行规划（plan）。\n解决的实际问题：\n计算效率问题：当前主流的如ReAct等智能体框架，通常在每一步行动前都强制进行“思考-行动”循环，导致了大量的计算冗余和高昂的推理成本，这在实际部署中是难以承受的。 长程任务性能瓶颈：研究发现，在需要长期记忆和执行的复杂任务中，过于频繁的规划会引入行为不稳定性（如在子目标间摇摆），反而降低任务成功率。 策略僵化问题：固定的规划策略（如“总是规划”或“每隔N步规划一次”）无法适应动态变化的环境和任务需求。智能体需要根据当前情况的复杂性和不确定性来调整其思考的深度和频率。 行业发展意义：\n降低运营成本：对于任何需要大规模部署LLM智能体的应用（如AI客服、自动化软件测试、游戏AI等），能够将计算成本降低一个数量级同时保持甚至提升性能，将极大地提高其商业可行性。 提升复杂任务解决能力：通过学会策略性地思考，智能体能更好地应对长程、复杂、动态的任务，这对于推动LLM在机器人控制、科学发现、战略游戏等前沿领域的应用至关重要。 构建更鲁棒和可信的AI系统：一个知道“何时该仔细想想”的智能体，比一个机械执行固定逻辑的智能体更可靠。此外，论文证明了这种智能体具有良好的“可控性”，能够接受人类指导，这为人机协作和AI安全提供了新的思路。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路：动态规划的成本效益框架 论文首次将智能体的规划决策形式化为一个成本效益分析问题。它提出了两个核心概念： \u0026gt; 规划优势 (Planning Advantage, A_plan): 生成一个新计划相比于沿用旧计划，在未来预期回报上的提升。 \u0026gt; 规划成本 (Cost of Planning, C_plan): 包含了三个部分：计算成本 (C_tokens)、延迟成本 (C_latency) 和一个非常新颖的 不稳定性成本 (C_noise)。\n特点与优势：这个框架的巧妙之处在于，它不仅考虑了显性的计算成本，还创造性地指出了“过度思考”带来的隐性成本（C_noise）。这为“为什么ReAct在长任务中表现不佳”提供了一个深刻的理论解释。它将问题从“如何规划”提升到了“何时规划”的元认知（meta-cognitive）层面。\n新方法：两阶段训练范式 (SFT Priming + RL Fine-Tuning) 第一阶段：监督微调启动 (SFT Priming) 做法：使用一个强大的教师模型生成带有间歇性规划的专家数据，然后用这些数据对目标智能体模型进行微调。 特点：这并非简单的行为克隆。其目的是让模型学习规划的“语法”和“语义”，建立起“规划-行动”的关联，为后续的RL学习提供一个优质的起点。如论文所言，这是为了“注入关键的归纳偏置”。 第二阶段：强化学习微调 (RL Fine-Tuning) 做法：使用近端策略优化 (PPO) 算法，在环境中进行在线学习。奖励函数被设计为任务奖励减去规划的token成本。 特点：RL阶段的核心是让智能体学会“策略性地”使用SFT阶段学到的规划能力。通过将规划成本直接纳入优化目标，智能体被迫在“任务进展”和“节约计算”之间做出权衡，最终学会动态规划。 与之前方法的比较优势： vs. ReAct等固定策略：最大的优势是自适应性和效率。ReAct是固定的“总是规划”策略，而本文方法是学习到的动态策略，能够在简单、确定的情境下直接行动，在复杂、不确定的情境下进行规划，从而实现性能和效率的最优平衡。 vs. 传统分层强化学习 (HRL)：传统HRL通常关注于学习在不同“选项”（或子策略）之间进行选择，但高层策略的调用通常是固定的（例如每一步或每隔固定步数）。本文的方法则直接解决了“何时调用高层规划模块”这个元决策问题，这在LLM作为规划模块的场景下尤其重要，因为这个调用本身成本极高。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列环环相扣的实验，在两个环境中进行：POGS (一个用于评估探索效率的自定义图搜索环境) 和 Crafter (一个复杂的、灵感源于《我的世界》的长期生存游戏基准)。\n实验一：零样本评估 (Zero-Shot Evaluation) 设计：使用强大的Llama-3.3-70B模型，不进行任何微调，仅通过不同的提示（prompt）来引导其采用固定的规划频率（从不规划、每步规划、每2/4/8…步规划一次）。 数据与结果：如图1(a)和(b)所示，结果清晰地展示了“金发姑娘”效应。 \u0026gt; 在Crafter环境中，“never”规划和”always”规划（ReAct模式）的性能都显著低于每4步或每8步规划一次的策略。例如，在Crafter中，“always”规划策略的最终得分约为0.33，而每4步规划一次的策略得分接近0.37，同时其计算成本（输出token数）显著降低。 结论：这个实验有力地证明了论文的核心假设：存在一个最优的、非极端的规划频率。 实验二：SFT启动阶段的有效性验证 设计：比较两个模型在SFT阶段的学习情况。两者都基于相同的专家行动序列进行训练，但一个模型（Primed-Dynamic）的训练目标包含规划文本，另一个（Primed-Naive）则只包含行动。 数据与结果：如图1(c)所示。 \u0026gt; 尽管行动监督完全相同，Primed-Dynamic模型在训练过程中的任务奖励始终高于Primed-Naive模型。这表明，规划文本作为额外的上下文信息，极大地帮助了模型理解和学习行动序列，起到了“认知脚手架”的作用。 实验三：端到端RL训练性能对比 设计：对比四种智能体配置的性能： Base+RL no plan: 基础模型直接RL，不规划。 Base+RL plan dynamically: 基础模型直接RL，动态规划。 SFT+RL no plan: 经过SFT启动（仅动作）后RL，不规划。 SFT+RL plan dynamically: 完整的两阶段方法。 数据与结果：如图1(e)和图5所示，结果具有决定性。 \u0026gt; SFT+RL plan dynamically 智能体在学习速度（样本效率）和最终达到的性能上都显著优于其他所有变体。与之对比，没有经过SFT启动的Base+RL plan dynamically智能体表现最差，说明在没有先验知识的情况下，让模型从零开始学习何时规划非常困难。 实验四：人机协作与可控性测试 设计：在实验三中表现最好的智能体（SFT+RL plan dynamically）的基础上，由人类提供高级别的战略计划。 数据与结果：如图4和图10所示。 \u0026gt; 在人类的指导下，该智能体成功地完成了挖掘钻石并通关Crafter的壮举，而这是所有自主智能体都未能达到的成就。这证明了模型不仅学会了自主规划，还能有效地将外部指令融入其决策流程中。 实验阶段 关键发现 支持数据/图表 零样本评估 存在最优的“金发姑娘”规划频率，过度规划和不规划都非最优。 图1 (a-b)，中间频率性能最高 SFT启动 规划文本能显著促进模仿学习的效率和效果。 图1 (c-d)，SFT plan奖励更高，KL散度更低 完整RL训练 SFT+RL的两阶段范式在样本效率和最终性能上最优。 图1 (e-f), 图5，SFT+RL plan曲线最高 人机协作 训练后的智能体可被人类计划有效引导，完成超高难度任务。 图4, 图10，成功挖掘钻石 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 规划的粒度与深度：当前模型的决策是二元的（规划/不规划）。未来的研究可以探索更复杂的决策空间，例如动态决定规划的深度（思考一步还是十步？）、广度（考虑多少种可能性？），甚至规划的抽象层次。这类似于人类在不同问题上投入不同心智资源。 模型规模的缩放法则 (Scaling Laws)：本文主要在8B模型上进行微调。一个关键问题是，这种“学习何时规划”的能力如何随着模型规模的增长而变化？一个千亿甚至万亿参数的模型，是否可能在没有SFT的情况下，仅通过提示或更少的RL就能学会动态规划？探索这其中的Scaling Laws将非常有价值。 更复杂的环境与任务：将此方法从游戏环境扩展到更开放、更真实的领域，如网页浏览、软件开发、机器人控制等。在这些领域，“规划漂移（plan drift）”问题会更严重，对智能体自适应 replan 的能力要求也更高。 将成本效益框架显式化：论文中的C_noise等成本是隐式地通过RL来优化的。未来的算法能否设计出一种方法来实时估计当前状态下的“规划优势”和“不稳定性成本”，并将其作为模型决策的显式输入，可能会进一步提升性能和可解释性。 从固定教师到自我提升：SFT阶段依赖于一个更强大的固定教师模型。一个更前沿的方向是探索如何让智能体通过自我博弈或自我反思（self-improvement）来生成更高质量的训练数据，实现能力的自举（bootstrapping）。 新的技术和投资机会： 高效能智能体平台：基于这种动态计算分配技术，可以开发出新一代的LLM智能体平台。对于需要部署成千上万个智能体的企业客户（如游戏公司、软件自动化公司），这种平台能提供“更智能、更便宜、更快速”的解决方案，具有巨大的商业潜力。 AI原生工作流自动化：这项技术将催生出更可靠的AI员工或AI助手，能够自主处理复杂的多步工作流。投资机会在于开发面向特定行业（如金融分析、法律文书处理、供应链管理）的AI原生自动化解决方案。 下一代人机协作工具：该研究展示了AI在接受人类高级指导方面的潜力。未来的投资机会可能在于开发能够与人类专家进行流畅、高效协作的AI工具，AI负责执行和战术规划，人类负责战略制定和监督，共同解决复杂问题。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性角度看，仍存在一些值得商榷和深入探讨的地方：\n模型规模的局限性：实验主要基于Llama-3.1-8B模型进行微调。虽然这对于验证方法的普适性有好处，但也带来一个疑问：SFT priming之所以如此关键，是否恰恰是因为8B模型本身“规划能力不足”，需要大量外部知识注入？对于GPT-4、Claude 3 Opus或未来的更强模型，其强大的零样本规划能力可能使得SFT阶段的必要性降低，或者说仅通过更精巧的提示工程+RL就足以达到类似效果。 环境复杂度的限制：Crafter虽然是一个公认的基准，但它仍然是一个状态和动作空间相对有限的2D网格世界。在真实物理世界或开放式网络环境中，状态的不可预测性和观测的噪声要大得多。论文提出的方法在这些更具挑战性的场景下的鲁棒性有待验证。 “学会规划”的可解释性不足：智能体通过RL学会了何时规划，但其决策机制是一个“黑箱”。论文并未深入分析智能体是根据哪些状态特征来决定规划的。例如，它是在遇到新奇事物时规划，还是在生命值低时规划，或是在任务陷入僵局时规划？对这一“元认知”决策过程的深入分析将非常有价值。 教师数据的质量上限：SFT阶段的教师数据是由一个采用“固定频率”规划的70B模型生成的。这位“教师”本身就不是最优的。如果使用一个已经通过本文方法训练好的、更懂得动态规划的智能体作为教师，是否能形成一个“迭代增强”的循环，培养出更强的学生？这篇论文没有探讨这种可能性。 “不稳定性成本”的量化：C_noise 是一个非常精彩的理论概念，但在实验中它是被隐式优化的。论文缺乏对这种不稳定性的直接量化和分析。例如，是否可以统计智能体在不同规划频率下“目标切换”或“无效回溯”的次数，来为C_noise提供更具体的实证支持？ 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新想法的宝库，以下是您可以重点学习和借鉴的：\n核心启发与创新点： “金发姑娘”原则的泛化应用：“更多的计算/思考并不总是更好”。这个原则不仅适用于LLM智能体，也适用于任何需要权衡计算资源和性能的系统。在你的项目中，可以思考是否存在类似的“过度计算”导致性能下降的场景，并尝试找到最优的“计算甜点区”。 两阶段学习范式 (SFT -\u0026gt; RL)：这是一个非常实用且强大的范式，可以被广泛应用。当你需要模型学习一个它不擅长但有结构的行为时（比如生成特定格式的代码、遵循复杂的指令），可以先用SFT教它“怎么做”（what and how），再用RL教它“什么时候做”（when）。这比单纯使用RL从零开始学习要高效得多。 将“元认知”能力作为学习目标：不要只让模型学习解决任务，还要让模型学习“如何更好地解决任务”。本文的“学习何时规划”就是一个元认知能力。你可以思考在你的应用场景中，是否存在其他可以被学习的元认知策略，例如“学习何时求助人类”、“学习何时调整自己的参数”等。 理由（Rationale）作为学习的催化剂：SFT实验最令人惊讶的结论是，提供行动背后的“理由”（即规划），能极大地帮助模型学习行动本身。这给数据构建带来了深刻启示：在创建训练数据时，如果可能，尽量包含解释性、结构性的信息（如代码注释、设计文档、思维过程），而不仅仅是最终结果。这会大大提高模型的学习效率和泛化能力。 需要补充的背景知识： 强化学习 (Reinforcement Learning)：特别是策略梯度方法和PPO (Proximal Policy Optimization)算法。你需要理解RL的基本框架（Agent, Environment, State, Action, Reward），以及PPO是如何稳定地更新策略网络的。 LLM智能体框架 (Agentic Frameworks)：了解ReAct (Reason-Act) 的工作原理，它是本文反复对比的基线。理解其优点（结构化推理）和缺点（僵化、高成本）将帮助你更好地理解本文的创新之处。 LLM微调技术：熟悉监督微调 (SFT) 的概念和实践，以及它与基于人类反馈的强化学习 (RLHF) 的区别与联系。 部分可观察马尔可夫决策过程 (POMDP)：论文在第4页将其环境建模为POMDP。虽然不要求深入的数学推导，但理解其核心概念——智能体无法观察到全部环境状态，只能通过不完整的“观测”来做决策——有助于理解为什么“规划”和“探索”在这些任务中如此重要。 ","date":"2025-09-08T00:00:00Z","permalink":"/learning-when-to-planefficiently-allocating-test-time-compute-for-llm-agents/","title":"Learning When to Plan：Efficiently Allocating Test-Time Compute for LLM Agents"},{"content":" 论文深度解读 这篇论文的核心贡献在于提出并实现了一个名为视觉语言世界模型（Vision Language World Model, VLWM）的全新框架。这个框架旨在解决人工智能体（AI Agent）在真实世界中进行高层次、长周期任务规划（high-level task planning）的难题。传统方法要么依赖于难以扩展的模拟环境，要么受限于无法与物理世界有效交互的纯语言模型，要么在像素级别生成未来场景，计算成本高且效率低下。VLWM则巧妙地选择了自然语言作为其核心的抽象世界状态表示（abstract world state representation），这可以说是本文最关键的洞见。\n论文的整个技术路线图清晰地分为两个主要部分：数据构建和模型设计。\n在数据构建方面，研究者们面临一个巨大的挑战：如何从海量的、无标注的原始网络视频中，自动地、大规模地创建出用于训练规划模型的高质量数据。他们为此设计了一个非常精巧的两阶段流水线： 1. 视频压缩为“字幕树”（TREE OF CAPTIONS）：首先，他们不直接处理原始视频像素，而是通过一个强大的视觉编码器（Perception Encoder）对视频进行分层聚类，形成一个树状结构。树的每个节点都对应视频的一个片段，并为其生成详细的文本描述。这极大地压缩了信息，同时保留了视频的核心语义。 2. LLM自我精炼提取结构化规划（LLM SELF-REFINE）：接着，他们将这个“字幕树”输入给一个强大的大语言模型（LLM），通过一种名为SELF-REFINE的迭代优化过程，从中提取出结构化的“目标-规划”对。每一对数据都包含清晰的目标描述（Goal description）、目标诠释（Goal interpretation）以及一个由动作（Action）和世界状态变化（World State Change, ΔS）交错组成的完整轨迹序列。\n这个数据流水线堪称一个“自动化的数据标注工厂”，它解决了高层次规划任务缺乏大规模、高质量训练数据的根本性瓶颈。\n在模型设计方面，论文的亮点在于提出了一个受人类认知科学启发的双系统规划（Dual-System Planning）架构，这直接借鉴了诺贝尔奖得主丹尼尔·卡尼曼（Daniel Kahneman）在《思考，快与慢》中提出的理论： * 系统1：快速、反应式的规划（System-1 Reactive Planning）：这由VLWM模型本身直接完成。给定当前观测（视觉或文本）和目标，模型会像一个标准的自回归语言模型一样，直接生成一个完整的动作-状态序列。这个过程非常快，适用于简单、熟悉的任务，但它缺乏深思熟虑，容易在复杂情况下“一条路走到黑”，无法修正错误。\n系统2：审慎、反思式的规划（System-2 Reflective Planning）：这是VLWM框架的精髓所在。它引入了一个独立的批评家模型（Critic Model）。规划过程变成了一个“生成-评估-选择”的循环： 提议（Propose）：一个“演员”（Actor，可以是VLWM自身或外部LLM）提出多个候选的动作序列。 模拟（Simulate）：VLWM作为世界模型，对每个候选动作序列进行“推演”（roll-out），预测执行这些动作后会导致的世界状态变化。 评估（Evaluate）：批评家模型登场，它会评估每个推演出的未来轨迹与最终目标的“语义距离”，并给出一个成本（cost）分数。成本越低，说明这条规划路径越优。 选择（Select）：系统最终选择成本最低的那个规划作为最终输出。 这个系统2模式通过内部的“试错”和“反思”，赋予了AI体进行推理、比较和选择最优解的能力，极大地提升了规划的质量和鲁棒性。论文中的一个关键技术点是，这个批评家模型也是通过自监督学习（self-supervised learning）训练的，无需额外的人工标注。例如，它被训练用来区分“好的规划步骤”和“无关的干扰步骤”，并确保正确的步骤顺序成本更低。\n论文通过一系列详实的实验，充分证明了VLWM框架的优越性。在PlannerArena这个模拟人类偏好评估的“竞技场”中，VLWM的系统2规划取得了1261的Elo等级分，远超强大的Llama-4-Maverick（1099分）甚至数据集中的“标准答案”（952分）。这一结果震撼地揭示了，现有的很多规划数据集质量堪忧，而VLWM生成的规划更符合人类的逻辑和偏好。此外，在视觉规划基准VPA、机器人问答基准RoboVQA等多个任务上，VLWM都取得了当前最佳（SOTA）或极具竞争力的表现。这些坚实的数据共同印证了论文核心思想的有效性。\n接下来，我将针对您的六个问题进行详细解读。\n### 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是创建一个能够理解物理世界动态、并在此基础上进行高层次、长周期任务规划的基础模型。\n解决的实际问题：\nAI的“知行合一”问题：当前的大模型（如GPT-4）虽然拥有强大的语言推理能力（“知”），但它们与物理世界是脱节的，无法直接理解视觉信息并制定出可执行的物理动作（“行”）。VLWM试图弥合这一鸿沟。 机器人与智能助手的自主性：如何让机器人、智能眼镜等设备不仅仅是执行预设指令的工具，而是能像人一样，根据一个模糊的目标（如“做一顿番茄炒蛋”），自主地观察环境、分解任务、规划步骤并执行。 数据瓶颈问题：为这类复杂的规划任务进行人工数据标注成本极高且难以规模化。论文试图解决如何从现有的海量网络视频中自动构建训练数据的问题。 行业意义：\n推动通用人工智能（AGI）发展：具备在真实世界中进行自主规划和行动的能力，被认为是通向AGI的关键一步。这项研究为构建更通用的AI代理提供了坚实的技术路径。 加速具身智能商业化落地：它将极大地推动家用机器人、智能驾驶、工业自动化、AR/VR智能助手等领域的进步，使这些设备更加智能和实用。 改变AI模型的设计范式：论文倡导的“以语言为抽象表征”和“双系统规划”思想，可能成为未来构建复杂AI代理系统的主流架构，引导行业从单纯追求模型参数规模转向构建更精巧、更高效的推理与规划系统。 ### 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心新思路是将世界模型建立在语言的抽象空间之上，并结合双系统规划框架进行推理。\n新的思路、方法和模型： 视觉语言世界模型 (VLWM)：其核心是将世界状态和动态变化都用自然语言来描述，而不是像素或潜在向量。 自动化的数据标注流水线：提出了视频 -\u0026gt; 字幕树 (TREE OF CAPTIONS) -\u0026gt; LLM自我精炼 (SELF-REFINE)的数据处理流程，实现了从原始视频到结构化规划数据的全自动转换。 双系统规划架构 (Dual-System Planning)： 系统1：快速、自回归式的直接规划生成。 系统2：通过“演员-世界模型-批评家”（Actor-VLWM-Critic）的架构，进行审慎的、基于成本最小化的规划搜索。 自监督的批评家模型 (Self-supervised Critic)：批评家模型通过对轨迹的正确性、相关性和时序性进行排序学习，而无需人工标注“好”或“坏”的规划。 与之前方法的对比、特点和优势： 对比方法 之前方法的局限性 VLWM的特点与优势 生成式世界模型 (如Sora) 在像素空间预测未来，计算成本极高，难以进行长周期规划，且会生成大量任务无关的细节。 高效与抽象：通过在语言空间进行预测，VLWM完全绕开了像素生成，计算效率极高，并且只关注与任务相关的语义变化，非常适合高层次规划。 JEPA式世界模型 在隐空间（latent space）中进行预测，虽然比像素生成高效，但其表征缺乏可解释性，且主要应用于低级别的连续控制任务。 可解释与灵活：语言表征具有天然的可解释性，便于人类理解和调试。同时，它可以无缝集成LLM的先验知识和推理能力。 纯LLM/VLM提示式规划 缺乏对物理世界动态的深刻理解（非“接地气”），容易产生不符合物理规律或实际操作的“幻觉”规划。 世界动态接地（Grounded）：VLWM通过从海量真实视频中学习动作与世界状态变化之间的因果关系，其规划更符合物理现实。 正如论文所强调的： \u0026gt; These limitations highlight the need for world models that predict in abstract representation spaces, rather than raw pixels. 这句话点明了其核心优势：在抽象（语言）空间而非原始像素空间进行预测，是解决现有世界模型局限性的关键。\n### 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了全面且严谨的实验来从不同维度验证VLWM的有效性。\n实验设计：\n标准基准评估：在公认的视觉规划辅助（VPA）基准上进行测试，与现有SOTA模型进行直接的量化指标对比。 人类偏好评估：创建了一个名为PlannerArena的评估平台，让真人来评判不同模型生成的规划的优劣。这种方式能更真实地反映模型在实际应用中的价值。 泛化能力测试：在机器人视觉问答（RoboVQA）任务上进行评估，测试模型将视觉理解与规划能力泛化到机器人领域的表现。 核心模块的独立评估：对批评家模型本身进行了一系列内在评估，如目标达成检测（Goal Achievement Detection），以验证其作为系统2核心组件的有效性。 实验数据和关键结果：\nPlannerArena 人类偏好评估 (见论文图表3)：这是最亮眼的实验结果。 设计：让评估者在多个模型（包括VLWM系统1、系统2、Llama-4、Qwen2.5VL、Ground Truth等）生成的规划中进行二选一。 结果：VLWM System-2 获得了 1261 的Elo分数，高居榜首。作为对比，强大的Llama-4-Maverick为1099分，而数据集自带的“标准答案”（Ground Truth Plan）仅为952分。 结论：这不仅证明了VLWM System-2规划质量的卓越，也暴露了现有规划数据集标注质量不足的问题。 VPA 基准评估 (见论文图表2)： 设计：在COIN和CrossTask两个数据集上，预测未来3个或4个步骤的规划。 结果：VLWM-8B模型在几乎所有指标（SR-成功率, mAcc-平均准确率, mIoU-平均交并比）上都超越了之前的SOTA模型（如VidAssist-70B）。例如，在COIN T=4的设置下，VLWM的SR达到了19.4%，而VidAssist为13.8%。 结论：即使模型参数量远小于对手（8B vs 70B），VLWM依然能取得更好的性能，证明了其架构的优越性。 批评家模型评估 (见论文图表5)： 设计：评估批评家模型能否准确识别出一个规划任务在何时完成。 结果：VLWM-critic-1B的准确率达到了96.9%，远超其他基线模型（如Qwen3-Reranker-8B的67.3%）。其成本曲线（Cost curves）显示出一个非常清晰的“能量谷”，最低点精确地落在了100%任务完成处，而其他模型的曲线则非常嘈杂。 结论：这直观地证明了批评家模型能够精确地度量任务进程，是系统2规划成功的关键保障。 ### 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 多模态世界模型：目前VLWM主要依赖语言作为抽象。未来的世界模型是否可以融合语言、物理符号、3D场景图等多种模态的表征，以获得更丰富、更精确的世界理解？ 实时交互与在线学习：VLWM主要基于离线视频数据进行学习。如何让模型在与环境的实时交互中不断学习和调整其世界模型，实现持续的在线自适应，是一个巨大的挑战。 系统2规划的效率：系统2的搜索过程虽然效果好，但计算成本高，延迟大。如何发展更高效的搜索算法（如结合蒙特卡洛树搜索 MCTS）或学习一个策略来指导搜索，以在效率和效果之间取得更好的平衡？ 长期记忆与复杂推理：对于需要跨越数小时甚至数天的超长周期任务，模型需要引入更强大的长期记忆机制和进行更复杂的因果推理。 安全与对齐：如何确保自主代理的行为是安全、可控且符合人类价值观的？这是所有自主系统都面临的终极挑战。 新的技术和投资机会： 技术催生： “AI Agent”操作系统：可能会出现专门为AI代理设计的操作系统或中间件，用于管理感知、记忆、规划和执行等模块。 下一代机器人软件栈：以语言世界模型为核心的机器人控制和任务规划软件将取代传统编程方法。 自动化科学发现平台：AI代理可以自主设计实验、操作仪器、分析数据，加速科学研究进程。 投资机会： 通用机器人公司：能够开发出基于此类先进AI代理技术的通用型家用或商用机器人的公司。 AR/VR内容与交互平台：能够提供高度智能、可主动辅助用户的虚拟助手和交互体验的平台。 AI驱动的工业自动化：在制造、物流、农业等领域，提供能够自主适应复杂流程和异常情况的AI解决方案。 ### 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性角度审视，仍存在一些潜在的不足和待验证之处：\n对数据生成流水线的依赖过高：整个VLWM的性能高度依赖于字幕树 -\u0026gt; SELF-REFINE这个数据流程的质量。这个流程本身依赖一个极其强大的外部LLM（论文中使用了Llama-4 Maverick）。这意味着：\n性能瓶颈：VLWM的“天花板”可能被这个外部LLM的能力所限制。 复现性挑战：如果无法获得同等级别的LLM，其他研究者可能很难复现出同等质量的数据和模型效果。 潜在的偏见传导：外部LLM自身的偏见或错误可能会被“蒸馏”并固化到最终的VLWM模型中。 物理常识的“真”理解 vs. “统计”关联：模型从视频文本描述中学习世界模型，它学到的是“当出现文本A（动作），通常会跟随文本B（状态变化）”这种统计规律。它是否真正理解了背后的物理因果？在面对训练数据中未见的、但符合物理规律的新情景时，模型的泛化能力有多强，还需要更多的“开箱（OOD）”测试来验证。\n系统2规划的延迟与实用性：论文证明了系统2的效果，但没有详细讨论其带来的计算开销和时间延迟。在需要快速反应的真实场景中（如自动驾驶中的紧急避险），系统2可能“远水解不了近渴”。其实用性需要在延迟和性能之间做更深入的权衡分析。\n评估维度的局限性：虽然实验很全面，但主要集中在“任务成功”和“人类偏好”上。对于自主代理，安全性、可预测性、鲁棒性等维度的评估同样至关重要，而这些在论文中着墨不多。\n### 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文是创新思想的宝库。以下是您可以重点学习和借鉴的方面：\n重点学习与启发： “语言作为接口”的核心思想：最值得借鉴的创新点。在你的项目中，思考是否可以将复杂的、非结构化的数据（如图像、传感器信号、代码）先通过一个模型“翻译”成自然语言描述，然后再让LLM在语言这个统一的、结构化的空间中进行处理、推理或生成。这是一个强大的降维和抽象工具。 “自动化数据工厂”的设计模式：原始数据 -\u0026gt; 粗提取 -\u0026gt; LLM精炼 这个模式极具通用性。无论你是想做代码生成、数据分析还是其他任何需要结构化输出的任务，都可以借鉴这个思路来自动创建高质量的训练数据，从而摆脱对人工标注的依赖。 双系统（或多系统）架构：不要试图用一个“大一统”的模型解决所有问题。借鉴“系统1/系统2”的思想，为你的应用设计一个快速反应的“直觉”模块和一个慢速但精确的“反思”模块。例如，一个推荐系统可以快速给出一个初步推荐（系统1），同时在后台启动一个更复杂的模型来优化这个推荐（系统2）。 自监督的“批评家”或“评估器”：为你的系统设计一个自监督的评估模块。这个模块不直接产出结果，而是评估其他模块产生结果的质量。通过巧妙地设计正负样本（如论文中的“正确步骤”vs“打乱步骤”），你可以训练出一个强大的质量控制器。 需要补充的背景知识： 世界模型（World Models）：建议阅读David Ha和Jürgen Schmidhuber的开创性论文《World Models》，了解其基本概念。同时关注JEPA (Joint Embedding Predictive Architecture)架构，这是当前世界模型研究的一个重要分支。 认知科学中的双过程理论（Dual Process Theory）：阅读Daniel Kahneman的《思考，快与慢》（Thinking, Fast and Slow），深入理解系统1和系统2的认知框架，这会帮助你更好地设计AI代理的架构。 具身智能（Embodied AI）与机器人学基础：了解具身AI的基本概念，以及在机器人任务规划（Task and Motion Planning, TAMP）领域的一些经典方法，这有助于理解VLWM所解决问题的行业背景。 最新的多模态大模型：持续关注如LLaVA、Flamingo、Gemini等多模态模型的进展，因为它们是实现类似VLWM这样框架的基础组件。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得丰富的启发。\n","date":"2025-09-08T00:00:00Z","permalink":"/planning-with-reasoning-using-vision-language-world-model/","title":"Planning with Reasoning using Vision Language World Model"},{"content":" image.png 论文深度解读 这篇发表于2025年9月的论文，其核心贡献在于提出了一个名为 VERLTOOL 的统一、模块化、高效率的开源框架，旨在解决工具增强的智能体强化学习（Agentic Reinforcement Learning with Tool use, ARLT） 范式下的系统性挑战。在深入探讨之前，我们需要理解几个关键的前置概念。\n首先是 RLVR (Reinforcement Learning with Verifiable Rewards)，即“可验证奖励的强化学习”。这是一种通过明确的、可自动验证的奖励信号（例如，数学题的答案是否正确）来训练大模型的方法。它极大地推动了LLM在长链条推理任务上的进步。然而，论文指出，传统的RLVR存在一个根本性缺陷：\nYet these systems remain constrained in a fundamental way: they are unable to interact with the external world. Current LLM reasoning unfolds in a closed, single-turn setting without environmental feedback… Conceptually, these models resemble a “brain in a vat,” locked into self-contained simulations without grounding in interactive or physical reality.\n这个“缸中之脑”的比喻非常形象。模型只能在自己的“思维空间”里推理，无法与外部世界（如代码执行器、搜索引擎、数据库）进行交互来获取真实反馈或执行操作。\n为了打破这个限制，ARLT 范式应运而生。它将RLVR与工具使用相结合，让模型在多轮交互中主动调用外部工具，并根据工具返回的观察（observation） 来调整后续行为。这使得训练过程从单向的“解答-验证”变成了双向的“行动-观察-再行动”的动态循环，从而能培养出更强大、更鲁棒的智能体能力。\n然而，理想很丰满，现实很骨感。实现ARLT面临着巨大的系统工程挑战： 1. 碎片化（Fragmentation）：现有的工作大多是为特定任务（如代码、搜索）定制的“作坊式”代码库，工具逻辑与训练逻辑紧密耦合，难以扩展和复用。 2. 低效率（Inefficiency）：普遍采用同步批处理（synchronous batch-by-batch） 的方式进行“交互演练”（rollout）。这意味着系统必须等待一批次中所有任务都生成了动作，才能统一调用工具，然后再等待所有工具执行完毕，才能继续下一步。这导致了大量的GPU空闲时间，严重拖慢了训练速度。 3. 扩展性差（Limited Extensibility）：支持新工具，尤其是跨模态（如图像处理）的工具，非常困难，缺乏统一的管理和接口。\nVERLTOOL 正是为了解决上述“ARLT的工程危机”而设计的。它像一个为AI智能体打造的“操作系统”，其创新性体现在四个层面：\n架构解耦与上游对齐：VERLTOOL将整个系统巧妙地分解为两大核心组件：Verl Workflow（负责RL训练流程）和 Tool Server（负责所有工具的执行与管理）。两者通过标准化的API进行通信。这种设计类似于现代软件工程的微服务架构，极大地降低了系统的复杂性，使得两部分可以独立开发和维护。同时，它继承自成熟的VERL框架，保证了与上游RL算法研究的兼容性。\n统一的工具管理：论文引入了专用工具服务器（dedicated tool server） 的概念，支持代码执行、搜索、SQL、视觉处理等多种工具。如论文中的图3所示，开发者只需通过一个轻量级的Python定义文件（继承自BaseTool类），就能轻松集成一个新工具，无需改动核心训练代码。这大大降低了研究人员的开发门槛。\n异步 rollout 执行：这是VERLTOOL在效率上的“杀手锏”。它抛弃了低效的同步批处理模式，允许每个训练序列（trajectory）独立、即时地与Tool Server交互。一旦某个序列生成了工具调用动作，它会立刻被发送执行，而不需要等待同批次的其他序列。这种并发设计极大地减少了硬件空闲。论文中的数据显示，这种异步设计带来了显著的性能提升：\nThis design yields over 2× speedup during rollout execution. … achieving near 2 times speedup for the rollout stage.\n例如，在DeepSearch任务上，同步执行需要193秒，而异步仅需98秒，加速比达到1.97倍（见论文Table 3）。\n全面的任务验证：VERLTOOL并非纸上谈兵。论文在6个覆盖不同领域和模态的ARLT任务上进行了广泛实验，包括数学推理（VT-Math）、知识问答（VT-Search）、SQL生成（VT-SQL）、视觉推理（VT-VisualReasoner）、网页搜索（VT-DeepSearch）和软件工程（VT-SWE）。实验结果非常亮眼，在多个任务上都达到或超过了那些为特定任务专门设计的系统的性能。例如，在知识问答任务上，VT-Search的平均分达到45.9%，显著优于专门的Search-R1基线（35.0%）；在软件工程任务VT-SWE上，得分达到19.5，远超之前的OpenHands（14.6）和SkyRL（9.4）。 综上所述，VERLTOOL的创新并非仅仅是提出一个新的算法，而是提供了一套完整、高效、开放的基础设施。它通过系统设计层面的创新，解决了ARLT范式在落地过程中的关键工程瓶颈，为社区构建更强大、更通用的AI智能体铺平了道路，具有里程碑式的意义。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心目标是设计并实现一个统一（unified）、高效（efficient）、可扩展（extensible） 的框架（VERLTOOL），专门用于支持工具增强的智能体强化学习（ARLT）。\n解决的实际问题：\n开发效率低下：当前研究人员在开发能够使用工具的AI智能体时，往往需要从零开始搭建复杂的系统，或者在一个高度定制化的、难以修改的代码库上工作。这导致了大量的重复劳动，并且使得尝试新的工具或多工具组合变得异常困难。 训练效率低下：现有框架大多采用同步执行机制，在智能体与环境（工具）交互时，GPU等计算资源利用率极低，导致训练周期漫长，成本高昂。 生态系统碎片化：缺乏一个公认的、标准的开发与训练平台，导致各项研究成果（如特定任务的智能体）之间难以复现、比较和组合，阻碍了整个领域的快速迭代。 对行业发展的重要意义： 这个问题非常关键，因为它直接关系到大模型能否从“语言模型”真正进化为“通用智能体”。\n降低研发门槛，加速创新：VERLTOOL这样的基础设施，就像是AI智能体领域的“Android”或“iOS”系统。它提供了一套标准化的开发工具和高效的运行环境，使得开发者可以将精力集中在算法创新（如何让智能体更聪明）和应用创新（用智能体解决什么问题）上，而不是耗费在底层的系统工程上。 推动通用智能体（AGI）的发展：一个能够与现实世界交互、使用多样化工具的智能体是通往通用人工智能（AGI）的关键一步。VERLTOOL通过支持文本、代码、视觉、系统等多种工具，为构建更全面的、能够处理复杂现实任务的“通用问题解决器”提供了可能。 催生新的应用生态：一个成熟的智能体开发框架将催生一个庞大的“工具”生态。未来，可能会出现像“App Store”一样的“Tool Store”，开发者可以为AI智能体开发各种工具（API），而应用开发者则可以在VERLTOOL这样的平台上，轻松地训练智能体来组合使用这些工具，创造出全新的自动化解决方案，例如自主软件工程师、AI科研助理、自动化金融分析师等。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其系统设计思路，而非单一的算法模型。它提出了一套全新的框架VERLTOOL，其特点和优势主要体现在以下几个方面：\n思路一：训练与执行的彻底解耦（Decoupling）\n方法：将系统划分为 Verl Workflow 和 Tool Server 两大独立模块。 Verl Workflow：专注于强化学习的训练逻辑，如策略更新（PPO/GRPO算法）、数据采样等。 Tool Server：一个独立的服务器，专门用于管理和执行所有外部工具（Python解释器、搜索引擎API、SQL执行器等）。 对比优势： 相比于紧耦合的专用系统（如论文中提到的 SEARCH-R1、PIXELREASONER），这种解耦设计具有极高的模块化和可维护性。研究者可以独立升级RL算法或更换工具实现，而互不影响。 相比于传统的RL框架（如OPENRLHF），VERLTOOL将工具管理提升到了“一等公民”的地位，为其设计了专门的服务和API，从而能更好地支持复杂的、多样的工具交互。 思路二：异步化Rollout机制（Asynchronous Rollouts）\n方法：允许每个正在进行的交互序列（trajectory）独立地、实时地向Tool Server发起请求，而无需等待批处理中的其他序列。 对比优势： 相比于同步框架（如VERL, SKYRL），异步机制能最大化硬件利用率。当一个序列在等待工具I/O（如网络请求）时，GPU可以继续处理其他序列的计算任务，从而消除“空闲气泡”（idle bubbles）。论文中的图2和表3直观地展示了这种效率优势，带来了近2倍的加速。这在训练昂贵的大模型时，意味着巨大的时间和成本节省。 图示：同步（上）vs 异步（下）。在同步模式下，所有序列必须等待最慢的工具调用完成，导致大量灰色“等待时间”。异步模式下，计算和工具调用并发进行，显著缩短了总时长。\n思路三：工具即插件（Tool-as-Plugin）的扩展模式\n方法：设计了一个统一的BaseTool接口。任何新的工具都可以通过继承这个基类来实现，并自动注册到Tool Server中。开发者只需实现parse_action（解析模型输出）和conduct_action（执行工具）等几个核心方法。 对比优势： 相比于硬编码（hard-coded）的工具集成方式，这种插件化设计提供了极佳的可扩展性。论文中提到： \u0026gt; Adding a new tool requires only a lightweight Python definition file, streamlining extensibility for both training and evaluation. 这意味着研究者可以像安装软件插件一样，轻松地为他们的智能体添加新功能，极大地促进了社区的实验和创新。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在六个多样化的ARLT基准任务上进行广泛实验，全面验证了VERLTOOL框架的有效性和通用性。\n实验设计： 任务多样性：实验覆盖了截然不同的领域和模态，旨在证明框架的“Holistic”（整体性）能力： VT-Math: 数学推理，使用Python代码执行器。 VT-Search: 开放域问答，使用FAISS向量检索。 VT-SQL: 自然语言转SQL，使用SQL执行器。 VT-VisualReasoner: 视觉推理，使用图像操作工具。 VT-DeepSearch: 基于Web搜索的复杂问答，使用Google Search API。 VT-SWE: 软件工程，使用Bash终端和文件I/O工具。 对比基线：实验结果与当前领域内最先进的（state-of-the-art）专用系统进行对比，例如与ToRL在数学任务上对比，与Search-R1在搜索任务上对比，与SkyRL-SQL在SQL任务上对比。这确保了评估的公平性和说服力。 统一框架训练：所有任务的模型都是在VERLTOOL这一个统一的框架下训练的，这本身就证明了框架的通用性和兼容性。 实验数据和结果： 实验结果令人信服，表明使用VERLTOOL训练的模型在各个任务上都表现出强大的竞争力。以下是关键数据的摘要： 任务名称 测试基准 VERLTOOL 结果 对比/意义 VT-Math (数学) GSM8K, MATH, AIME24 等 62.2% (DAPO 7B) 与专门为数学设计的ToRL-7B (61.1%) 相比，表现极具竞争力。 VT-Search (搜索) NQ, HotpotQA, 2wiki 等 45.9% (GRPO 7B) 显著超越 之前SOTA的专用系统Search-R1-base (35.0%)。 VT-SQL (SQL) Spider (Test) 83.4% (GRPO 7B) 基本持平 专门为SQL优化的SkyRL-SQL-7B (85.2%)，证明了其在结构化数据查询上的强大能力。 VT-VisualReasoner (视觉) V* Bench 82.7% 非常接近于专用的视觉推理模型Pixel-Reasoner-7B (84.3%)，展示了框架优秀的多模态支持。 VT-DeepSearch (网页搜索) GAIA, HLE 34.0% (GAIA) 大幅领先 于ReAct (23.3%) 等基线方法，显示了其处理复杂、真实网络信息的能力。 VT-SWE (软件工程) SWE-Verified 19.5 显著优于 SkyRL (9.4) 和 OpenHands (14.6) 等先进的软件工程智能体。 这些数据有力地证明，VERLTOOL不仅在工程效率上实现了突破，而且能够在不牺牲性能的前提下，支持多样化、跨领域的智能体训练，甚至在某些任务上超越了专用系统。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? VERLTOOL为智能体研究铺设了坚实的地基，未来的探索将在这之上“添砖加瓦”。\n值得探索的问题和挑战： 多工具协同与规划（Multi-Tool Orchestration \u0026amp; Planning）：当前的智能体主要在单个步骤调用单个工具。未来的挑战在于如何让智能体学习自主规划一个复杂的工具调用链（例如，先用Web Search查找API文档，然后用Python Executor编写代码调用该API，最后用Bash Terminal运行测试）。这需要更高级的规划和推理能力。 开放世界工具发现与学习（Open-World Tool Discovery）：如何让智能体在没有预先定义的情况下，自主发现互联网上的新工具（新的API、网站等），并学会使用它们？这涉及到自主学习、探索和泛化等更前沿的AI课题。 长期记忆与状态管理（Long-term Memory \u0026amp; State Management）：对于需要跨越数天甚至数周的超长任务（如完整的软件项目开发），智能体需要拥有可靠的长期记忆机制来存储和检索关键信息，VERLTOOL的轻量级状态字典可能不足以支撑。 智能体安全与对齐（Agent Safety \u0026amp; Alignment）：一个能执行代码、操作文件的强大智能体也带来了巨大的安全风险。如何确保它不会执行恶意代码、泄露隐私数据或被用于非法目的？这是限制智能体走向实际应用的关键瓶颈。 人机协同（Human-Agent Collaboration）：如何设计一个高效的交互界面，让人类可以自然地监督、指导和纠正智能体的行为，实现“人机结对编程”或“人机协同科研”？ 可能催生的新技术和投资机会： 智能体操作系统（Agent OS）：基于VERLTOOL这类框架，未来可能会出现更加成熟的“智能体操作系统”，提供工具管理、内存管理、安全沙箱、并发控制等核心服务。这是平台级的投资机会。 工具即服务（Tool-as-a-Service, TaaS）平台：将会出现专门为AI智能体提供和托管工具的平台（类似“智能体应用商店”），开发者可以发布自己的工具API并从中获利。这是生态级的投资机会。 垂直领域自主智能体解决方案：在法律、金融、医疗、科研等专业领域，会出现提供端到端自动化解决方案的初创公司。他们利用底层框架训练高度专业化的智能体来完成特定任务（如自动生成专利申请、进行量化交易策略回测）。这是应用级的投资机会。 智能体安全与监控服务：随着智能体能力的增强，专门提供安全审计、行为监控和风险控制服务的公司将变得至关重要。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管VERLTOOL是一项杰出的工作，但从批判的角度审视，仍存在一些潜在的不足和待验证之处：\n奖励函数设计的复杂性：论文中的每个任务都设计了专门的、人工定义的奖励函数（reward function）。例如，VT-Math的奖励是准确率和工具使用的组合，VT-VisualReasoner则有更复杂的“好奇心”奖励。这表明奖励工程（reward engineering） 仍然是ARLT的一个核心瓶颈。框架本身虽然通用，但要在一个新任务上取得好效果，可能仍然需要大量的人工努力来设计好的奖励信号。 超长序列任务的验证不足：软件工程（VT-SWE）任务虽然允许多达100轮交互，但大多数其他任务的交互轮次都相对较少（2-5轮）。对于需要数百甚至上千步交互的超长任务（如运行一个完整的科研项目），VERLTOOL的性能和稳定性如何，尚待验证。 可复现性挑战：尽管论文承诺开源，但强化学习系统（尤其是分布式系统）的复现往往非常困难，对环境配置、随机种子和超参数高度敏感。社区需要时间来验证其宣称的“易用性”和性能结果。 对基础模型能力的依赖：VERLTOOL是一个框架，其最终性能的上限在很大程度上取决于所使用的基础大模型（如Qwen2.5）的能力。如果基础模型本身的推理或代码能力不足，再好的框架也难以取得突破性成果。论文没有深入探讨框架与不同基础模型之间的相互作用。 资源消耗问题：论文提到实验使用了8个H100 GPU，这是一个相当可观的计算资源。对于资源有限的学术机构或个人开发者而言，使用VERLTOOL进行完整训练的门槛可能仍然很高。论文可以更详细地讨论框架的资源占用情况和低资源下的适应性。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，您可以从中获得很多启发。\n重点学习和借鉴的创新想法： 解耦设计思想：核心启发是“关注点分离”（Separation of Concerns）。在设计任何复杂的AI系统时，都应思考如何将核心算法逻辑（如RL训练）与外部依赖（如工具、数据源）解耦。这不仅能提高系统的模块化和可维护性，还能通过并行化来提升效率。 异步化思维：凡是涉及到I/O等待（网络、磁盘、API调用）的地方，都应考虑异步化。这个思想不仅适用于RL训练，也适用于任何需要与外部服务交互的大模型应用（如RAG系统）。它可以显著提高系统的吞吐量和资源利用率。 插件化架构：为你的系统设计一个统一的接口或基类，让新的功能模块可以像插件一样被轻松集成。这能极大地激发你所在团队或社区的创造力，因为贡献新功能的门槛被大大降低了。 基础设施先行：在探索一个新的、复杂的AI范式时，优先投入精力构建一个稳定、高效的基础设施（框架），往往能事半功倍。一个好的“轮子”能够让后续的研究和开发加速前进。 需要补充了解的背景知识： 强化学习基础：特别是 策略优化（Policy Optimization） 算法。您需要深入理解 PPO (Proximal Policy Optimization) 的工作原理，因为它是许多现代LLM强化学习的基础。论文中提到的 GRPO 是PPO的一个变体，了解PPO将帮助您理解其动机。 LLM智能体（Agents）主流范式：建议阅读一些关于智能体思想的经典论文，如 ReAct (Synergizing Reasoning and Acting) 和 Toolformer。这些工作将帮助您理解LLM是如何被引导进行思考（reasoning）和行动（acting）的。 分布式计算框架：论文中提到了 Ray 作为其后端之一。了解Ray这样的分布式计算框架，将帮助您更深入地理解VERLTOOL是如何实现异步化和并行化的，这对于您自己设计高效系统非常有帮助。 希望这份详尽的解读能够帮助您全面、深入地理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-09-08T00:00:00Z","permalink":"/verltooltowards-holistic-agentic-reinforcement-learning-with-tool-use/","title":"VERLTOOL：TOWARDS HOLISTIC AGENTIC REINFORCEMENT LEARNING WITH TOOL USE"},{"content":" 论文深度解读：在精细化探索中寻求效率与稳定的平衡 这篇论文的核心，是针对一个日益重要的领域——基于可验证奖励的强化学习（Reinforcement Learning from Verifiable Rewards, RLVR）——提出了一种更高效、更稳定的训练框架。RLVR旨在通过基于规则的、明确的奖励信号（例如，数学题答案是否正确）来提升LLMs在数学、编程等需要严谨推理领域的表现。然而，现有的主流方法，如GRPO和DAPO，在实践中暴露了若干关键瓶颈，这正是DCPO试图攻克的难题。\n论文开篇便一针见血地指出了现有方法的痛点： 1. 零梯度问题（Zero Gradients）：当一个批次（batch）中所有生成样本的奖励都相同时（例如，模型对一道难题的所有尝试都失败了），通过传统的优势标准化（Advantage Standardization）计算出的优势值（advantage）会变为零。这意味着模型无法从这次昂贵的推理尝试中学到任何东西，造成了严重的样本浪费。 2. 熵坍塌（Entropy Collapse）与探索不足：以GRPO为代表的方法，其固定的裁剪策略会过度限制模型的探索。尤其是在训练后期，模型变得越来越“自信”，倾向于生成高概率的、相似的答案，失去了探索稀有但可能正确的推理路径的能力，这被称为熵坍塌。 3. 样本效率低下：DAPO为了解决零梯度问题，引入了动态采样策略，即丢弃那些奖励一致的批次。这种“丢弃”行为虽然保证了梯度不为零，但代价是极大地降低了样本利用率，导致训练效率低下。\n面对这些挑战，DCPO巧妙地设计了一套组合拳，其创新性体现在三个相互关联的关键组件上：\n第一，动态自适应裁剪（Dynamic-Adaptive Clipping, DAC）。这是对传统强化学习算法PPO中核心思想的一次重要改进。PPO使用一个固定的超参数ε来裁剪概率比率，防止策略更新过猛。但DCPO的作者敏锐地指出：\nOur analysis reveals that the optimal clipping bound varies across tokens, making a uniform upper bound suboptimal. \u0026gt; 我们的分析表明，最优的裁剪边界因词元（token）而异，这使得统一的上限并非最佳选择。\nDAC的核心思想是，裁剪边界不应该一成不变，而应该与该词元在旧策略下的输出概率 q(x) 动态相关。具体来说，对于概率较低（即更“罕见”）的词元，DAC会给予一个更宽松的裁剪范围，允许模型进行更大胆的探索。反之，对于高概率的常见词元，则保持较紧的约束以确保稳定。这种机制极大地释放了模型对“稀有词元”的学习潜力，而论文引用先前工作指出，这些高熵的稀有词元正是模型推理能力涌现的关键驱动力。从实验结果看，DCPO的词元裁剪率（Token Clipping Ratio, TCR）比GRPO和DAPO低了一个数量级（见论文图1），这直观地证明了其在保护有价值的探索信号方面的优越性。\n第二，平滑优势标准化（Smooth Advantage Standardization, SAS）。这是对零梯度和训练不稳定问题的直接回应。传统方法仅在当前步骤（step）的样本中计算奖励的均值和方差来进行标准化。SAS则引入了“时间”维度，它将历史上所有步骤的奖励统计数据与当前步骤的进行加权平均。这种平滑处理带来了两大好处：首先，即使当前批次的奖励完全相同，由于有历史数据的“稀释”，计算出的优势值也不再为零，从而解决了零梯度问题，显著提升了数据利用率。论文中的响应利用率（Response Utilization Ratio, RUR）指标完美地印证了这一点。数据显示，GRPO的平均RUR仅为43.8%，意味着超过一半的生成响应被浪费，而DCPO则高达71.8%（见论文表2）。其次，这种累积平滑机制大大减少了因单步采样随机性带来的优势值剧烈波动，使得训练过程更加稳定。\n第三，仅词元均值损失（Only-Token-Mean Loss, OTM）。这是一个看似微小但十分精巧的改进。先前的DAPO方法在计算损失时，会对整个批次中的所有词元进行平均，这会导致一个问题：一个优势值较低但序列很长的响应，其在总损失中的权重可能会超过一个优势值很高但序列很短的响应。OTM则改变了这一计算方式，它首先在单个响应内部对所有词元的损失进行平均，然后再对批次中所有响应的损失求平均。这保证了每个响应对模型更新的贡献度只与其自身的优势值相关，而与长度无关，从而更公平、更准确地传递了奖励信号。\n综合这三大创新，DCPO在四个主流的数学推理基准测试中取得了全面的领先。尤其是在最具挑战性的AIME24基准上，基于Qwen2.5-Math-7B模型的DCPO，其Avg@32得分达到38.8，远超GRPO的32.1和DAPO的31.6（见论文表1）。这不仅仅是分数的提升，更意味着DCPO在保持稳定性的前提下，有效地激励了模型生成更多样化且高质量的解题路径。\n总而言之，DCPO并非一次颠覆性的革命，而是一次基于深刻洞察的、精巧的工程与理论优化。它通过动态调整探索边界、平滑稳定奖励信号和优化损失计算方式，在RLVR这个充满潜力的方向上，找到了一个兼顾探索效率、样本利用率和训练稳定性的“甜点区”，为训练更强大的推理模型铺平了道路。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是优化基于可验证奖励的强化学习（RLVR）框架，以更高效、更稳定地提升大型语言模型在复杂推理任务（如数学、编程）上的能力。\n解决的实际问题：\n训练效率低下：现有方法如GRPO和DAPO存在严重的样本浪费问题。GRPO因“零梯度”问题导致大量生成数据无法用于模型更新；DAPO为了避免零梯度而直接丢弃数据，导致GPU时钟时间的浪费。 模型探索能力受限：固定的裁剪策略（如PPO和GRPO）限制了模型对新颖、低概率推理路径的探索，尤其是在训练后期容易陷入“舒适区”，难以实现能力突破。 训练过程不稳定：由于采样随机性，奖励信号在不同训练步骤间波动巨大，导致优势值计算不稳定，甚至正负反转，干扰了模型的持续学习。 行业意义： 提升LLM的推理能力是当前业界公认的核心挑战之一，是实现通用人工智能（AGI）的关键一步。RLVR被认为是比传统的基于人类偏好的强化学习（RLHF）在逻辑任务上更有前景的技术路线，因为它提供了客观、可验证的奖励。然而，上述训练难题阻碍了RLVR技术的规模化应用。DCPO通过解决这些核心工程和算法问题，为大规模、高效地训练高推理能力的专业模型（Expert Model）提供了可行的技术路径。这意味着：\n可以用更少的计算资源和时间，训练出更强的数学或编程模型。 能够更充分地挖掘基础模型的潜力，实现性能的“二次飞跃”。 为自动化科学发现、复杂系统设计等需要高级推理能力的应用场景奠定了基础。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出了一个名为DCPO (Dynamic Clipping Policy Optimization) 的新框架，其核心优势在于通过以下三个协同工作的组件，实现了效率和稳定性的平衡：\n动态自适应裁剪 (Dynamic-Adaptive Clipping, DAC) 思路：打破PPO/GRPO中“一刀切”的固定裁剪边界，让裁剪范围与词元的先验概率 q(x) 挂钩。 对比优势： vs. GRPO (固定裁剪)：GRPO对所有词元都施加 [1-ε, 1+ε] 的相同相对约束。对于低概率词元，这个绝对空间非常小，限制了探索。而DAC为低概率词元提供了更宽的探索边界，如论文附录图4所示，当 q(x) 趋近于0时，DAC允许的概率比率 r(x) 急剧增大（上限为10），极大地激励了模型探索“冷门”但可能正确的路径。 vs. DAPO (非对称固定裁剪)：DAPO虽然使用了非对称边界，但这个边界仍然是固定的，没有考虑词元本身的概率分布。DAC则更加精细化和自适应。 平滑优势标准化 (Smooth Advantage Standardization, SAS) 思路：在计算优势值时，不仅仅依赖当前批次的奖励统计，而是引入历史所有批次的奖励统计数据，进行平滑加权。 对比优势： vs. GRPO/DAPO (当前步标准化)：传统方法完全依赖当前批次，极易出现零梯度问题。SAS通过引入历史统计量，即使当前批次奖励相同，Â_total,j 依然可以为非零值，从而从根本上解决了零梯度问题，提升了样本利用率。论文数据显示，DCPO的响应利用率（RUR）比GRPO高出约28个百分点。 稳定性：SAS通过平滑处理，有效抑制了因采样噪声导致的优势值剧烈波动，使得学习信号更稳定，有利于模型的持续收敛。 仅词元均值损失 (Only-Token-Mean Loss, OTM) 思路：在计算损失函数时，改变求平均的顺序。先在响应内部对所有词元求均值，再对整个批次的所有响应求均值。 对比优势： vs. GRPO (序列级均值损失, SLM)：SLM将优势在整个批次的所有响应上平均，稀释了信号。 vs. DAPO (词元级均值损失, TLM)：TLM将整个批次的所有词元“拉平”一起求均值，使得长响应不成比例地主导了梯度更新。OTM通过“先内再外”的平均方式，确保了每个响应的贡献度只与其优势值成正比，与长度无关，更加公平和准确。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列严谨的实验来验证DCPO的有效性。\n实验设计：\n模型：选取了不同尺寸的Qwen2.5系列模型（1.5B, 3B, 7B, 14B），以验证方法在不同模型规模下的普适性。 数据集：训练集结合了DAPO-Math-17K和MATH数据集，测试集则涵盖了四个广泛使用的数学推理基准：MATH500, AMC23, AIME24, AIME25。这些基准难度递增，可以全面评估模型的推理能力。 基线方法：选择了当前最主流的两种RLVR方法 GRPO 和 DAPO 作为直接比较对象。 评估指标： Avg@1：贪心解码（Greedy Decoding）下的准确率，代表模型的最佳性能。 Avg@32：32次采样下的平均准确率，衡量模型生成多样性正确答案的鲁棒性。 内部诊断指标：TCR (词元裁剪率) 和 RUR (响应利用率)，用于从机理上解释性能差异。 实验数据和结果： 论文的结果极具说服力，DCPO在几乎所有模型和基准上都取得了最佳或接近最佳的性能。\n模型 基准 方法 Avg@1 / Avg@32 Qwen2.5-Math-7B AIME24 GRPO 36.7 / 32.1 DAPO 36.7 / 31.6 DCPO 46.7 / 38.8 Qwen2.5-14B AIME25 GRPO 13.3 / 10.5 DAPO 20.0 / 15.3 DCPO 23.3 / 19.0 关键数据解读： 性能全面超越：如上表所示，在最具挑战性的AIME基准上，DCPO的领先优势尤为明显。在Qwen2.5-Math-7B模型上，DCPO的Avg@32得分比基线高出超过6个百分点，这是一个巨大的提升。 样本效率优势： \u0026gt; DCPO increased the response utilization ratio by 28% over GRPO, doubled the training efficiency compared to DAPO… \u0026gt; \u0026gt; DCPO相比GRPO将响应利用率提升了28%，相比DAPO将训练效率翻倍… 探索效率优势： \u0026gt; …and reduced the token clipping ratio by an order of magnitude. \u0026gt; \u0026gt; …并且将词元裁剪率降低了一个数量级。 消融研究（Ablation Study）：论文图3展示了对DCPO三个组件（OTM, SAS, DAC）的消融实验。结果表明，每个组件单独加入GRPO都能带来性能提升，而三者结合（完整的DCPO）效果最好，证明了设计的系统性和协同效应。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ DCPO为RLVR方向打开了新的思路，未来值得探索的问题和挑战包括：\n泛化到其他领域：论文的实验完全集中在数学推理。DCPO框架能否无缝迁移到代码生成、化学分子结构发现、定理证明、甚至长篇故事创作等其他需要探索和验证的领域？这些领域的奖励函数设计（Reward Function Design）将是关键挑战。 与人类偏好（RLHF）的结合：可验证奖励（RLVR）和人类偏好奖励（RLHF）并非互斥。未来的研究可以探索如何将两者结合，例如，用RLVR来保证逻辑的正确性，用RLHF来优化生成内容的可读性、风格和安全性，形成一个更全面的对齐（Alignment）框架。 奖励延迟和稀疏性问题：在更复杂的任务中（如写一个完整的软件项目），奖励可能是非常稀疏和延迟的。当前的RLVR框架可能难以应对。需要研究更先进的强化学习算法，如分层强化学习（Hierarchical RL），来分解任务和分配信用。 超参数的自适应调整：DCPO虽然引入了动态裁剪，但仍然依赖 ε_low 和 ε_high 等手动设置的超参数。能否设计一种元学习（Meta-Learning）或自适应机制，让模型在训练过程中自动调整这些超参数，将是提升算法鲁棒性的重要方向。 催生的技术和投资机会： 专业模型即服务（Expert Model as a Service）：基于DCPO这类高效训练框架，可以催生出为特定行业（金融、科研、法律）定制的高性能推理模型，并以API服务的形式提供。 新一代AI训练平台：提供内置了DCPO这类先进算法的AI训练和微调平台，降低企业开发高性能模型的门槛。 自动化科研（AI for Science）：在生物、材料、物理等领域，利用该技术自动探索和验证新的假设，可能带来颠覆性的科学发现。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管DCPO取得了显著成功，但从批判性角度审视，仍存在一些潜在的不足和待验证点：\n实验范围的局限性：\n模型架构单一：所有实验均基于Qwen2.5系列模型。该方法在其他主流架构（如Llama, Mistral, Gemma）上的表现如何，仍是未知数。不同架构的内部表征和学习动态可能与DCPO的假设存在差异。 任务领域单一：如前所述，实验仅限于数学领域。数学任务具有奖励明确、推理路径相对结构化的特点。在奖励模糊、或创造性更强的任务中，DCPO的优势是否依然存在，需要打一个问号。 对奖励模型的依赖： DCPO的成功高度依赖于一个高质量的、基于规则的奖励模型（+1, 0, -1）。在现实世界中，构建这样一个完美的“裁判”往往非常困难。如果奖励信号存在噪声或偏差，SAS的累积机制是否会放大这些噪声，导致模型学到错误的行为模式？这是一个潜在的风险。\n长期训练的稳定性： 论文的实验进行了400个训练步骤。在更大规模、更长时间（数千甚至数万步）的训练中，DCPO是否还能保持稳定？例如，SAS中累积的奖励统计数据是否会因为分布漂移（Distribution Shift）而变得“过时”，反而误导后续的训练？\n可复现性的挑战： DCPO引入了新的组件和超参数，这可能会增加复现的难度。论文虽然提供了核心参数，但完整的训练细节和代码的开源对于社区验证和跟进至关重要。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是RL算法微调LLM领域的一个极佳范例，您可以从中获得诸多启发：\n核心启发： 动态化与自适应思想：这篇论文最大的亮点在于将PPO中一个“静态”的核心组件（裁剪）改造成了“动态”的、依赖数据分布的组件。这个思路极具普适性。您可以思考在您的工作中，哪些固定的超参数或模块可以根据输入数据、模型状态或训练进程进行动态调整？ 利用时间维度的信息：SAS的核心是“用历史指导现在”。在很多机器学习任务中，我们往往只关注当前批次的数据。SAS提醒我们，引入历史信息的平滑或累积，是应对噪声、提高稳定性的一个强大武器。这在在线学习、持续学习等场景中尤其有用。 魔鬼在细节中——审视损失函数：OTM的例子告诉我们，损失函数的微小设计差异（如求平均的顺序）可能对训练动态产生巨大影响。当模型训练不如意时，回头审视并剖析损失函数的每一项是如何贡献的，可能会发现意想不到的优化空间。 您需要补充的背景知识： Proximal Policy Optimization (PPO)：必须深入理解PPO算法的原理，尤其是其目标函数、优势估计（Advantage Estimation, 如GAE）和裁剪（Clipping）机制。这是理解GRPO和DCPO创新的基础。 Importance Sampling (重要性采样)：这是PPO等策略梯度方法背后的核心数理统计概念，理解它能帮助您明白为什么需要概率比率 r(x) 以及为什么要对它进行裁剪。 GRPO 和 DAPO：建议快速阅读这两篇论文的摘要和方法部分，了解它们各自解决了什么问题以及引入了什么新问题，这样您能更深刻地体会到DCPO设计的巧妙之处。 强化学习中的探索与利用（Exploration vs. Exploitation）：这是一个经典概念，有助于您理解为什么DAC要为低概率词元提供更大的探索空间，以及熵（Entropy）在RL中的作用。 希望这份详尽的解读能帮助您全面、深入地理解这篇优秀的论文。\n","date":"2025-09-06T00:00:00Z","permalink":"/dcpodynamic-clipping-policy-optimization/","title":"DCPO：DYNAMIC CLIPPING POLICY OPTIMIZATION"},{"content":"在训练时忽略坏样本，从而使训练更加稳定。\n论文深度解读：SimpleTIR——驯服多轮工具推理中的“脱缰野马” 大型语言模型（LLM）正从单纯的“文本生成器”向复杂的“智能代理”（Agent）演进，其核心能力之一便是与外部工具（如代码解释器、搜索引擎）交互以完成复杂任务，这一范式被称为工具集成推理（Tool-Integrated Reasoning, TIR）。然而，当这种交互从单次调用扩展到需要多轮次、不断迭代的复杂场景时，模型的训练过程往往会变得极其不稳定，如同脱缰的野马，性能时常崩溃。这篇论文精准地诊断了这一问题的“病根”，并开出了一剂名为SimpleTIR的“良方”。\n问题的核心：训练过程中的“毒性”数据 论文开宗明义地指出，在多轮TIR的强化学习（RL）训练中，不稳定的根源在于分布漂移（distributional drift）。让我们用一个通俗的例子来理解这个概念：想象一个只学过经典文学的学生（LLM），突然被要求阅读并理解一堆由代码解释器返回的、充满语法符号和错误信息的文本（外部工具反馈）。这些反馈的语言风格和数据分布，与他之前学习的语料库截然不同。当他被迫基于这些“陌生”的文本进行下一轮思考和创作时，他的思维（模型内部状态）就很容易混乱，从而生成一些不着边际、概率极低的词句（low-probability tokens）。\n在多轮交互中，这种混乱会像滚雪球一样累积。第一轮的微小漂移，导致第二轮生成更奇怪的内容，进而导致第三轮的彻底“胡言乱语”。在强化学习的数学世界里，这些极低概率的词句会引发梯度范数爆炸（gradient norm explosions）。梯度可以理解为模型学习和调整方向的“指南针”，梯度爆炸意味着这个指南针的指针瞬间被一股巨大的力量打得疯狂旋转，模型彻底失去了学习的方向，训练随之崩溃。\n论文通过图1和图2中的实验数据清晰地展示了这一现象。在“Naive Multi-turn Training”（朴素多轮训练）的曲线中，我们可以看到代表模型性能的AIME24准确率曲线剧烈震荡并最终趋于零，而梯度范数曲线则出现大量尖锐的、灾难性的峰值。\nIn contrast, Naive Multi-turn Training not only suffers from unstable dynamics and catastrophic gradient norm explosions, but also fails to match the performance of the baseline without TIR. \u0026gt; 相比之下，朴素的多轮训练不仅遭受不稳定的动态和灾难性的梯度范数爆炸，而且其性能甚至无法达到没有TIR的基线水平。\nSimpleTIR的巧思：过滤“无效回合” 面对这一难题，以往的方法可能会尝试用复杂的数学技巧去“约束”失控的梯度，但这往往治标不治本。SimpleTIR的思路则返璞归真，它提出：既然问题的根源是那些导致模型混乱的“毒性”交互回合，那我们何不直接在训练时将它们剔除掉？\n论文将这些“毒性”的回合定义为“无效回合”（void turns）。\n… we define as a void turn: an LLM response that contains neither a complete code block nor a final answer. \u0026gt; … 我们定义为“无效回合”：一个既不包含完整代码块，也没有给出最终答案的LLM响应。\n这个定义非常巧妙且易于实现。一个“无效回合”通常是模型思维混乱的直接体现，比如生成了半截代码、重复的无意义文本，或者提前输出了结束符。SimpleTIR的核心算法（如图4所示）非常简单：在收集了一批模型的交互轨迹后，它会逐一检查每个轨迹。一旦发现任何一个轨迹中包含了“无效回合”，就将这整个轨迹标记为“坏样本”，在后续的策略更新中完全忽略它。\n这个看似简单的“过滤”操作，却起到了“四两拨千斤”的效果。通过移除这些包含“无效回合”的轨迹，那些由极低概率词元产生的、破坏性的高幅度梯度被从源头上阻断了，从而极大地稳定了学习动态。我们再次观察图1，SimpleTIR的训练曲线显示出平滑且持续上升的性能，梯度范数也始终保持在平稳的低位，与朴素训练形成了鲜明对比。\n卓越的成果与更深远的意义：“零样本强化学习”的胜利 SimpleTIR的有效性在实验结果中得到了充分验证。在极具挑战性的AIME24数学竞赛基准上，从Qwen2.5-7B基础模型开始，SimpleTIR将模型的得分从22.1%（一个仅依赖文本推理的基线）一举提升至50.5%，这一成就令人瞩目。在Table 1的详细对比中，SimpleTIR在7B和32B两个量级的模型上，其性能全面超越了其他所有基于“零样本强化学习”（Zero RL）范式的模型，甚至能与那些依赖监督微调（SFT）进行“冷启动”的复杂方法相媲美。\n(表1：论文中的性能对比，展示了SimpleTIR在多个数学基准测试中的卓越表现)\n这引出了SimpleTIR更深远的贡献：它推动了“零样本强化学习”范式的发展。传统的TIR模型训练，往往需要一个“冷启动”阶段，即先用大量人工标注的“工具调用”范例对模型进行监督微调（SFT），让模型先“学会”如何使用工具，然后再进行强化学习。然而，这种SFT方法会像一个“紧箍咒”，将模型的思维模式限制在人类标注的范例中，从而扼杀了模型探索更优、更创新解题路径的可能性。\nSimpleTIR则完全不需要SFT。它直接从一个未经对齐的基础模型出发，通过强化学习的自我探索和SimpleTIR的稳定性保障，让模型自由地发现多样的推理模式。论文在图6和表3中展示了模型自发学到的三种高级推理行为： * 交叉验证（Cross Validation）：模型会用两种不同的方法或代码来解决同一个问题，并比较结果以确保正确性。 * 渐进式推理（Progressive Reasoning）：模型会将一个复杂问题分解成多个步骤，并逐步通过代码执行来解决。 * 错误修正循环（Error Correction Loop）：当代码执行出错时，模型能够识别错误并尝试修正代码，再次执行。\n这些涌现出的复杂行为，正是通往更强大、更自主的AI智能体的关键。SimpleTIR的成功证明了，只要能保证训练的稳定，Zero RL是一条极具潜力的道路，能够释放出LLM内在的、远超SFT数据束缚的推理潜能。\n总而言之，SimpleTIR并非一个复杂的全新算法，而是一种优雅、高效的训练哲学。它通过一个简单的“过滤”思想，精准地解决了多轮TIR训练中的核心痛点，不仅在性能上取得了SOTA的成果，更重要的是，它为我们构建更通用、更具创造力的人工智能代理，点亮了一条充满希望的前进道路。\n接下来，我将针对您提出的六个问题，进行逐一的详细解读。\n一、研究目标与实际问题 研究目标： 本文的核心研究目标是稳定化多轮工具集成推理（Multi-Turn TIR）的端到端强化学习（End-to-End RL）训练过程。具体来说，作者希望开发一种方法，能够让大型语言模型（LLM）在没有任何预先监督微调（即Zero RL）的情况下，直接通过与环境（如代码解释器）的多轮交互和结果反馈进行学习，而不会出现训练崩溃。\n想要解决的实际问题： 实际问题是，当LLM需要像人一样，通过“思考 -\u0026gt; 动手（使用工具） -\u0026gt; 观察结果 -\u0026gt; 再思考”这一多轮循环来解决复杂问题（如解答数学题、进行数据分析、控制机器人）时，现有的强化学习训练方法极不稳定。模型经常在训练中途“学废了”，性能急剧下降，无法收敛。这极大地阻碍了开发能够自主执行复杂、长链条任务的AI代理（AI Agent）。\n问题的重要性： 解决这个问题对于行业发展具有里程碑式的意义。\n推动AI代理的实用化：目前大多数AI应用还停留在单轮问答或简单任务执行。能够稳定地进行多轮工具交互，是实现能够处理真实世界复杂任务（如自动化科学研究、编写和调试复杂软件、进行金融策略分析）的通用AI代理的技术前提。 降低开发成本和门槛：论文所倡导的Zero RL范式，摆脱了对昂贵且耗时的人工标注SFT数据的依赖。这意味着未来开发者可以更快速、更经济地训练出具备特定工具使用能力的专业模型，极大地促进了AI技术的普及和应用创新。 探索通用人工智能（AGI）的路径：让模型通过自我探索和试错（RL）来学习如何使用工具解决问题，而不是被动地模仿人类范例（SFT），被认为是通往更通用、更具创造性智能的一条重要路径。稳定化这一过程，就是为这条探索之路铺设了坚实的基础。 二、新思路、方法或模型 新的思路： 论文最核心的新思路是从“数据-中心”（Data-Centric）的视角来解决RL的稳定性问题。传统方法往往聚焦于算法层面，例如修改损失函数、增加正则项等。而本文的思路转换非常巧妙：它认为训练不稳定的根源不在于优化算法本身，而在于训练数据（即模型的交互轨迹）中混入了一部分“有毒”的、质量极差的样本。因此，问题的关键是识别并“净化”这些坏数据，而非改造算法。\n新的方法（SimpleTIR）： 基于上述思路，论文提出了SimpleTIR，一个简单而高效的轨迹过滤算法。\n核心机制：该方法定义了一种名为“无效回合”（void turns）的失败模式，即模型的回应既没有生成可执行的完整代码块，也没有给出最终答案。在RL的每轮更新前，SimpleTIR会检查所有采集到的交互轨迹，只要一个轨迹中包含至少一个“无效回合”，就将整个轨迹丢弃，不参与模型的参数更新。\n流程示意：\n(图4：SimpleTIR流程示意图，在策略更新前，包含“无效回合”的轨迹被过滤掉)\n与之前方法的特点和优势：\n简洁与通用性（Plug-and-Play）：与需要复杂算法调整的方法相比，SimpleTIR的过滤规则非常简单，且与具体的RL算法（如PPO, GRPO）解耦。它可以作为一个“即插即用”的模块，轻松集成到现有训练框架中，几乎没有额外的计算开销。 直击问题根源：论文通过理论分析（Proposition 3.1）和实验（图3）论证了低概率词元是导致梯度爆炸的直接原因，而“无效回合”正是这种现象的集中体现。SimpleTIR直接移除了这些“病灶”，比那些只在梯度层面进行“围追堵截”的方法（如梯度裁剪）更为根本和有效。论文在图5的消融实验中也证明，简单的过滤低概率词元或高重要性比率的轨迹，效果远不如SimpleTIR。 赋能Zero RL，激发涌现能力：这是其最大的优势。传统方法为了稳定，常常依赖SFT进行“冷启动”。但这会限制模型的创造力。SimpleTIR通过保障训练稳定，使得直接从基础模型进行Zero RL训练成为可能，从而让模型能够自发学习到如交叉验证、错误修正等SFT数据中可能不存在的、更高级的推理策略。 三、实验验证 论文通过设计缜密的实验，从多个维度验证了SimpleTIR的有效性。\n实验设计： 任务与基准：实验选取了极具挑战性的数学推理任务，使用了包括Math500, AIME24, AIME25等多个公开基准测试集。这些任务天然需要多轮次的精确计算和逻辑推理，是检验TIR能力的理想“试金石”。 基线模型对比：实验设置了三类有力的基线进行对比： 非TIR的Zero RL方法（如DAPO）：用于证明引入工具（TIR）的必要性。 依赖SFT冷启动的TIR方法（如ReTool）：用于证明SimpleTIR在Zero RL范式下也能达到甚至超越复杂SFT方法的性能。 其他TIR的Zero RL方法（如ZeroTIR）：用于证明SimpleTIR在同类方法中的优越性。 核心指标：主要评估指标是模型在各项基准上的准确率（pass@k）。同时，为了验证稳定性，实验还追踪了训练过程中的梯度范数（Gradient Norm）和模型性能随训练步数的变化。 实验数据与结果： 性能的压倒性优势：如Table 1所示，SimpleTIR取得了state-of-the-art的性能。 \u0026gt; On the Qwen2.5-7B model, SimpleTIR achieves an AIME24 score of 50.5, which is a significant improvement over the base model’s 3.2 and the previous Zero RL SOTA ZeroTIR-7B’s 39.6. For the larger Qwen2.5-32B model, it achieves 59.9 on AIME24 and 92.9 on MATH500, outperforming even strong cold-start methods like ReTool on several key metrics. \u0026gt; \u0026gt; 在Qwen2.5-7B模型上，SimpleTIR在AIME24上取得了50.5分，远超基础模型的3.2分和之前Zero RL的最佳模型ZeroTIR-7B的39.6分。在更大的Qwen2.5-32B模型上，它在AIME24上获得59.9分，在MATH500上获得92.9分，在多个关键指标上甚至超越了像ReTool这样强大的冷启动方法。\n训练稳定性的直观证明：Figure 1是全篇论文中最具说服力的图之一。 \u0026gt; The training dynamics of SimpleTIR are highly stable, and it clearly outperforms the baseline method… The gradient norm remains well-behaved with almost no spikes. \u0026gt; \u0026gt; SimpleTIR的训练动态高度稳定，其性能明显优于基线方法……梯度范数表现良好，几乎没有尖峰。 左图显示，SimpleTIR的AIME24准确率稳步提升，而朴素方法则剧烈震荡。右图显示，SimpleTIR的梯度范数一直维持在低位，而朴素方法则频繁出现灾难性的“尖峰”。这强有力地证明了SimpleTIR确实解决了训练不稳定的核心问题。\n消融研究（Ablation Studies）：在Table 2和Figure 5 (Bottom)中，论文通过消融实验进一步巩固了其结论。实验对比了SimpleTIR与其他几种启发式过滤方法（如过滤低概率词元、过滤高重要性比率）。结果显示，这些方法都无法有效解决不稳定性，性能远低于SimpleTIR。这证明了“过滤无效回合”这一策略的独特性和有效性。\n四、未来探索与挑战 结合当前大模型领域的理解，该研究方向未来充满了机遇和挑战。\n值得进一步探索的问题和挑战： “无效回合”定义的泛化：本文的“无效回合”定义高度依赖于“代码块”这一特定形式。在更广泛的代理任务中（如网页浏览、API调用、机器人控制），如何定义一个“无效”或“无进展”的步骤是一个开放且重要的问题。这需要我们为不同工具和任务设计更通用的进展评估（Progress Estimation）启发式规则或模型。 长程信用分配（Long-term Credit Assignment）：SimpleTIR将包含一个坏步骤的整个轨迹丢弃，这种做法可能有些“粗暴”，因为它也丢弃了轨迹中可能存在的正确步骤。对于需要数百上千步的超长任务，一个微小的后期错误就否定全部努力，这会使得学习效率极低。未来需要研究更精细的信用分配机制，能够在轨迹内部区分“好”与“坏”的步骤。 工具生态的复杂性：真实世界的工具使用远比单一的代码解释器复杂。AI代理需要面对版本不兼容的API、网络延迟、权限问题、工具本身返回错误信息等。如何让模型在这样一个充满不确定性的开放工具环境中稳定学习，是一个巨大的挑战。 探索与利用的平衡（Exploration-Exploitation）：SimpleTIR通过过滤“坏”的探索来保证稳定。但在某些情况下，暂时的“无效”可能是通往全新解决方案的必经之路。如何设计更智能的过滤机制，既能避免灾难性的崩溃，又能鼓励有价值的、高风险的探索，是未来研究的关键。 可能催生的新技术和投资机会： AI代理开发平台（Agent Development Platforms）：围绕如何稳定训练能使用复杂工具的AI代理，将会出现一批专注于提供“训练基础设施”的公司。它们会提供高度优化的模拟环境、可靠的工具沙箱、以及像SimpleTIR这样内置的稳定训练算法，成为AI代理领域的“Unity”或“Unreal Engine”。 专业领域工具模型即服务（Specialized Tool-Models as a Service）：利用类似SimpleTIR的技术，可以高效地训练出在特定领域（如生物制药、材料科学、量化交易）精通使用专业软件和数据库的模型，并以API服务的形式提供给行业客户。 自动化软件工程与QA：论文中模型涌现出的“错误修正”能力，预示着AI在自动化编程和软件测试方面有巨大潜力。专注于开发能够自动编写、调试、测试代码的AI智能体的初创公司将迎来重要的投资风口。 五、批判性视角与不足 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以探讨的不足之处。\n“无效回合”定义的局限性： 当前定义是语法层面而非语义层面的。一个模型可能生成了一个语法完整、可以执行的代码块，但这段代码在逻辑上完全错误，或者与解决问题的目标南辕北辙。SimpleTIR无法识别并过滤这种“语义上无效”的回合。这可能导致模型学会了一些看似在“做事”但实则“原地打转”的无效策略。\n数据效率问题： 如前所述，过滤整个轨迹是一种数据效率较低的做法。强化学习本身就面临样本稀疏的问题，SimpleTIR可能会丢弃大量包含有用信息的早期步骤，仅仅因为后期出现了一个错误。这可能使得模型需要更多的交互样本才能学会一项技能，从而增加了训练的总体成本。\n对成功轨迹的依赖： SimpleTIR的机制依赖于在探索过程中能够采样到足够多的“全流程正确”的轨迹作为正向激励。在一些极难的问题上，模型在初期可能很难随机探索出一个完全没有“无效回合”的成功轨迹。在这种情况下，由于所有（或绝大多数）轨迹都被过滤掉，模型可能会陷入“无数据可学”的困境，导致学习停滞。\n实验范围的单一性： 论文的所有实验都集中在数学推理这一个领域。虽然这是一个非常适合的测试场景，但该方法的有效性是否能直接迁移到其他类型的任务（如需要与搜索引擎、数据库进行多轮交互的开放式问答，或需要进行创意写作的规划任务）仍有待验证。不同任务的“失败模式”可能大相径庭。\n六、核心启发与学习路径 对于希望从中汲取创新想法的您来说，这篇论文提供了极其宝贵的启示。\n应该重点学习什么？有哪些启发？ “数据净化”大于“算法改造”的哲学：这是最大的启发。当遇到模型训练不稳定的问题时，我们的第一反应常常是去调整学习率、修改网络结构或更换更复杂的优化器。这篇论文告诉我们，应该先退一步，仔细审视你的训练数据。很多时候，问题的根源在于数据质量，一个简单而有效的过滤规则，其效果可能胜过无数次复杂的算法调优。这一思想在机器学习的任何领域都适用。 为失败模式建模（Modeling Failure Modes）：SimpleTIR的成功源于对“无效回合”这一关键失败模式的精准定义。在您自己的项目中，可以尝试去识别和归纳模型在任务中最常犯的、最具破坏性的错误类型，然后设计针对性的机制（无论是过滤、惩罚还是修复）来处理这些特定的失败模式。 拥抱Zero RL的潜力：不要迷信SFT是训练模型的唯一途径。论文雄辩地证明，只要有方法能确保探索过程的稳定，让模型“自由发挥”的Zero RL能够带来意想不到的惊喜和更强的泛化能力。在您的项目中，可以思考是否也能构建一个稳定的探索环境，减少对标注数据的依赖。 需要补充了解的背景知识： 强化学习（Reinforcement Learning）基础：需要扎实理解Agent, Environment, State, Action, Reward, Policy这些核心概念。 策略梯度方法（Policy Gradient Methods），特别是PPO (Proximal Policy Optimization)：论文的技术是建立在PPO（及其变体GRPO）之上的。理解PPO的工作原理，尤其是其核心的“Clipped Surrogate Objective”和重要性采样比率（Importance Sampling Ratio），是看懂论文3.2节中关于梯度爆炸理论分析的关键。 大型语言模型训练范式：了解预训练（Pre-training）、监督微调（SFT）和从人类反馈中强化学习（RLHF）的区别和联系。这将帮助您理解为什么“Zero RL”是一个重要且具有挑战性的研究方向。 工具增强型LLM（Tool-Augmented LLMs）：了解这一领域的通用架构和术语，例如ReAct (Reason + Act)框架等。这将为您提供论文所研究问题的更广阔背景。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-09-06T00:00:00Z","permalink":"/simpletirend-to-end-reinforcement-learning-for-multi-turn-tool-integrated-reasoning/","title":"SimpleTIR：End-to-End Reinforcement Learning for Multi-Turn Tool-Integrated Reasoning"},{"content":"首次提出伪标签训练的论文。ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM是对这一篇的改进。\n下面，我将为您呈现对这篇论文的全面深度解读。\n深入解读：TTRL——让大模型在测试中“自我进化” 这篇论文的核心贡献在于提出并验证了一种名为测试时强化学习（Test-Time Reinforcement Learning, TTRL）的全新范式。传统上，我们认为模型训练和推理（或测试）是两个泾渭分明的阶段：模型在带有标签的庞大数据集上完成训练后，其参数就被“冻结”，然后在测试阶段对未知数据进行预测。这种模式的一大瓶颈是，模型一旦部署，就失去了学习和适应新数据的能力，而且其性能高度依赖于昂贵且难以获取的大规模标注数据。\nTTRL打破了这一常规。它的核心思想可以概括为：利用模型自身的“集体智慧”作为监督信号，在测试阶段持续优化自己。 这个过程非常巧妙，正如论文图2所示，当模型面对一个没有答案的问题（Query）时，它不是只生成一个答案，而是通过多次随机采样（Stochastic Sampling）生成一大批（例如64个）候选答案。然后，通过多数投票（Majority Voting）的方式，从这批答案中选出一个共识度最高的答案，并将其作为“伪标签”（Pseudo Label）。接下来，这个伪标签被用来计算奖励（Reward）：与伪标签一致的候选答案获得正奖励（例如+1），不一致的则获得零奖励或负奖励。最后，模型利用这个自生成的奖励信号，通过强化学习算法（如论文中使用的GRPO）来更新自身的网络参数，使其未来更有可能生成与共识一致的答案。\n这个过程最令人惊叹的地方在于，它形成了一个自我强化的正向循环。论文中将其描述为模型“lifts itself up by its own bootstraps”（通过自己的鞋带把自己提起来），一个非常形象的比喻。起初，模型的共识可能是微弱且不完全准确的，但通过RL的优化，模型的能力会小幅提升。一个稍强的模型，在下一次面对新问题时，其生成的候选答案质量会更高，形成的共识（伪标签）也更可靠。更可靠的伪标签又会提供更高质量的奖励信号，进一步指导模型优化。如此循环往复，模型便在无标签的测试数据流中实现了持续的自我进化。\n论文用翔实的实验数据证明了这一构想的巨大威力。最引人注目的结果是在极具挑战性的数学竞赛基准AIME 2024上的表现。如表1所示，Qwen2.5-Math-7B模型在应用TTRL后，其pass@1（一次性通过率）从12.9%暴涨至40.2%，实现了211.6%的惊人提升。这证明了TTRL并非微小的修补，而是一种能带来性能飞跃的强大技术。\n更进一步，论文揭示了一个反直觉但至关重要的发现：TTRL的最终性能可以超越其自身的训练信号。直观上，人们可能会认为，既然伪标签是由模型的多数投票产生的，那么模型的最终准确率最多也就能达到它最初进行多数投票时的准确率（即maj@n）。然而，实验（如图6所示）证明，随着训练的进行，模型的平均准确率（avg@16）最终显著超过了初始的maj@16。这说明TTRL不仅仅是在“模仿自己的共识”，而是在RL的探索机制下，真正学到了更深层次、更通用的推理能力。论文将这种现象部分归因于“幸运一击（Lucky Hit）”（图10），即即便伪标签是错的，RL的奖励机制依然能正确地惩罚那些与错误伪标签不同的、同样是错误的答案，从而提供了有效的负反馈信号。\n总而言之，TTRL通过一种优雅的无监督方式，将测试阶段的计算资源创造性地转化为了模型的“成长养料”，为解决大模型的数据依赖和持续学习问题提供了一个极具潜力的解决方案。\n接下来，我将按照您提出的六个方面，对论文进行更详细的剖析。\n一、研究目标与现实意义 研究目标： 本文的核心研究目标是：探索一种让大型语言模型在没有外部标注的情况下，仅利用测试数据本身，通过强化学习实现自我性能提升的方法。\n解决的实际问题： 它主要解决了以下两个行业痛点：\n数据瓶颈（Data Bottleneck）：当前，提升LLM性能，尤其是通过强化学习（如RLHF），高度依赖于海量、高质量的人工标注数据。这些数据的获取成本高昂、周期漫长，已成为制约模型能力进一步发展的关键瓶颈。 静态部署与持续学习的矛盾：传统模型“一次训练，永久使用”的模式，使其无法适应现实世界中不断涌现的新知识、新问题和新分布的数据。模型一旦部署，其能力就被固化，面对新挑战时性能会下降。 行业重要意义： TTRL的意义是革命性的。它描绘了一幅未来AI系统的蓝图：\n实现AI的自主进化：它为AI系统在真实环境中进行“终身学习”或“持续学习”提供了可能。一个部署在特定领域（如医疗、法律、科研）的AI，可以不断从它处理的无标签真实案例中学习，变得越来越专业和精准，而无需昂贵的人工再训练。 极大降低AI应用的门槛和成本：通过摆脱对标注数据的依赖，企业可以更低成本地将通用大模型适配到其特定业务场景，只需让模型在业务数据流上运行TTRL即可。 迈向“经验时代”：论文引用了AI巨擘Silver和Sutton的“经验时代（era of experience）”概念。TTRL正是这一理念的践行者，它强调模型应通过与环境（在这里是无标签数据）的直接交互和经验积累来学习，而非仅仅依赖人类的监督。 二、核心方法与创新优势 新思路与模型： TTRL的核心机制可以分解为以下步骤，这在论文的2.1节有详细阐述： 随机化探索（Rollout）：针对一个输入问题q，使用带有一定“温度”（temperature \u0026gt; 0）的采样策略，生成M个不同的候选输出 {ŷ₁, ŷ₂,... ŷM}。温度参数是关键，它保证了输出的多样性，为后续的投票提供了基础。 共识即真理（Label Estimation）：对这M个输出的最终答案进行提取，并通过多数投票算法，确定出现频率最高的答案y，将其作为该问题的“伪真值标签”。 自我奖励机制（Reward Calculation）：根据伪标签y，为每一个候选输出ŷᵢ分配一个奖励R(ŷᵢ, y)。最简单的方式是二元奖励：如果ŷᵢ的答案与y相同，奖励为1；否则为0。伪代码见论文中的Listing 1。 策略迭代优化（Policy Optimization）：利用生成的(候选输出, 奖励)数据对，通过一个强化学习策略优化算法（如PPO或论文中提到的GRPO），更新LLM的参数θ，目标是最大化期望奖励。 与之前方法的比较与优势： 相较于传统RLHF/RLAIF：这些方法需要人类标注的偏好数据或预先设定的规则/验证器来提供奖励信号。TTRL完全自给自足，奖励信号内生于模型自身，实现了真正的无监督学习。 相较于传统自训练（Self-Training）/SFT：传统自训练通常是离线的，即用一个教师模型生成伪标签，然后用这些伪标签去监督训练一个学生模型。这种范式下，学生模型的性能上限通常被教师模型的准确率所束缚。TTRL是在线的、动态的RL过程，模型既是学生也是自己的老师，其性能能够“滚雪球”式地增长，并如前所述，能够超越初始的共识准确率上限。 相较于仅用测试时推理（TTI）技术：像“自洽性”（Self-consistency）这类方法，也是通过多样本投票来提升单次推理的准确率，但它们不改变模型参数。TTRL则更进一步，不仅利用投票结果来得到更好的单次答案，还利用这个过程产生的信号来永久性地提升模型自身的能力。 论文在第4.1节明确指出： \u0026gt; TTRL is Supervised by maj@n Yet Surpasses It. …we observe a surprising phenomenon: after training, the model not only matches but also surpasses the expected upper bound…\n这正是TTRL相较于传统方法的根本优势所在：它不仅是一个推理技巧，更是一个有效的无监督学习框架。\n三、实验设计与结果分析 实验设计： 模型多样性：论文选取了Qwen、LLaMA、Mistral等多个主流模型家族，覆盖了从1.5B到32B的不同参数规模，以及基础（Base）和指令微调（Instruct）两种类型，全面验证了方法的普适性。 高难度基准：实验选择了AIME 2024、AMC、MATH-500等数学推理和GPQA这种研究生水平的问答基准。这些任务的共同点是，它们需要复杂的、多步骤的链式思维（Chain-of-Thought, CoT），而非简单的知识检索，更能体现出模型深层次的推理能力。 严谨的评估：主要采用pass@1作为评估指标，并使用带温度的随机采样进行评估，这比使用贪心解码（greedy decoding）更能反映模型在探索性任务中的真实性能。 充分的对比：虽然缺乏直接的同类方法进行比较（因为TTRL本身很新颖），但论文将应用TTRL前后的模型性能进行了详尽对比，并与那些在大量标注数据上进行RL训练的SOTA模型（在附录A中）进行了间接比较，以凸显其效果。 关键数据与结果： 显著的性能提升：表1是核心成果展示。除了前面提到的Qwen2.5-Math-7B在AIME 2024上的+211.6%提升，Qwen2.5-Math-1.5B在MATH-500上也有+123.2%的提升。几乎所有模型在所有测试基准上都获得了显著的正向收益，证明了方法的鲁棒性。 良好的泛化能力：图4展示了TTRL的跨任务泛化能力。例如，在AIME 2024数据集上训练过的模型，其在AMC和MATH-500上的性能也得到了提升。这说明TTRL不仅仅是在“应试”，而是学到了可迁移的、通用的数学推理能力。 算法兼容性：图5显示，TTRL可以与GRPO、PPO、PRIME等多种RL算法无缝结合，并且性能表现相似。这表明TTRL的成功主要归功于其核心思想，而非某个特定的RL算法实现。 四、未来探索与机遇 值得探索的问题与挑战： 理论基础：TTRL目前仍以实验驱动为主，缺乏严格的收敛性分析。未来的研究需要从理论上回答：在什么条件下，TTRL能保证收敛并超越初始性能？其性能的理论上限在哪里？ 奖励函数的设计：目前采用的二元多数投票奖励函数虽然简单有效，但可能不是最优的。能否设计更精细的奖励函数，例如，引入模型置信度、答案的语义多样性、或者对少数派但正确的“创新”答案给予奖励，以避免“多数人暴政”并鼓励探索？ 在线与流式学习：将TTRL扩展到真正的流式数据（Streaming Data）场景，即模型实时处理连续不断的数据流并动态更新。这对于构建能适应真实世界动态变化的AI系统至关重要。 探索更广阔的应用领域：TTRL在数学推理等有明确答案的任务上表现出色。它在更开放的任务（如代码生成、创意写作、多智能体协作、科学发现）上效果如何，是一个亟待探索的问题。 新技术与投资机会： 自治AI与持续学习平台：TTRL是构建能自我完善的自治AI的关键技术。围绕这一理念，开发支持模型在生产环境中进行持续、安全、高效学习的MLOps平台，将是巨大的投资机会。 “训推一体”的硬件与云服务：TTRL模糊了训练和推理的界限。这可能催生出为“推理即训练”（Inference-as-Training）优化的新型AI芯片或云服务架构，它们能低功耗、低延迟地执行小批量的梯度更新。 垂直领域模型的低成本适配：对于拥有大量无标签行业数据（如病历、法律文书、金融报告）的企业，TTRL提供了一条极具吸引力的技术路径，可以用远低于传统微调的成本，打造出高度专业化的领域模型。 五、批判性视角与潜在不足 存在的不足与缺失： 对模型初始能力的依赖：TTRL并非“点石成金”。它要求基础模型必须具备一定的“先验知识”或解决问题的初步能力。如果模型初始能力太弱，其生成的候选答案质量过低，多数投票的结果将接近于随机噪声，无法形成有效的学习信号。论文在表3中也揭示了这一点：在MATH-500最难的子集上，TTRL的性能提升幅度明显减小。 超参数敏感性：RL算法通常对超参数（如学习率、温度、批次大小）非常敏感。论文在4.3节和图11中也承认，不当的超参数设置会导致训练失败。这意味着在实践中，应用TTRL可能需要大量的调试和调优工作，这在一定程度上削弱了其易用性。 计算成本的权衡：TTRL虽然节省了数据标注成本，但显著增加了推理阶段的计算开销。为每个查询生成数十个样本并执行反向传播，远比单次前向推理要昂贵。在实际部署时，需要在性能提升和计算成本之间做出权衡。 模式崩溃风险（Mode Collapse）：该方法存在一定的风险，即模型可能会过早地收敛到一个错误的共识上，并不断强化这个错误，导致“近亲繁殖”式的能力退化。论文对此风险的探讨和相应的缓解机制（如如何保持探索的多样性）着墨不多。 六、核心启示与学习路径 重点学习的创新想法与启发： 内生监督信号（Self-Generated Supervision）：最核心的启发是，监督信号不一定需要外部给予，它可以从系统内部的集体行为中涌现。这个“内部民主投票”的思想极具普适性，可以被借鉴到许多领域，例如在多智能体系统中通过共识形成协作策略，或是在代码生成中通过多个生成方案的共性来提炼出更鲁棒的代码结构。 化“探索”为“学习”：传统的推理技术（如Beam Search, Self-consistency）将计算资源用于“探索”更好的答案，是一次性的。TTRL的巧妙之处在于，它将这个探索过程本身作为了学习的机会，把探索中产生的经验沉淀到了模型参数中。这种将一次性计算转化为永久性能力的思路值得在系统设计中借鉴。 拥抱与利用不确定性：TTRL依赖于随机采样（不确定性）来产生多样的候选，再从多样性中寻找确定性（共识）。这启示我们，在设计AI系统时，不应总是追求确定性的、最优的单步决策，而应学会拥抱和管理不确定性，并将其作为学习和创新的源泉。 需要补充的背景知识： 强化学习基础：特别是策略梯度（Policy Gradient）方法，如PPO (Proximal Policy Optimization)。您需要理解策略（Policy）、奖励（Reward）、价值函数（Value Function）等基本概念，以及RL的“探索-利用”困境。 测试时计算（Test-Time Computation）：建议阅读论文引用中的《Self-consistency improves chain of thought reasoning in language models》(Wang et al., 2022) 这篇开创性工作。同时，理解论文附录C中对测试时训练（TTT）和测试时推理（TTI）的区分也至关重要。 LLM的解码策略：深入理解temperature、top-p、top-k等采样参数如何影响模型输出的随机性和多样性。这是TTRL能够成功实施的技术前提。 希望以上详尽的解读能帮助您全面、深入地理解这篇优秀的论文。如果您还有任何问题，随时可以提出。\n","date":"2025-09-06T00:00:00Z","permalink":"/ttrltest-time-reinforcement-learning/","title":"未命名"},{"content":"将grpo引入到了游戏这个交互式环境中。让模型学会知道如何做，其它的没什么创新性了。\n论文深度解读 这篇论文的核心贡献在于提出了一个名为 Think-In Games (TiG) 的创新框架，旨在弥合大型语言模型（LLMs）所拥有的 陈述性知识（Declarative Knowledge, 即“知道是什么”） 与在复杂动态环境中取得成功所必需的 程序性知识（Procedural Knowledge, 即“知道如何做”） 之间的鸿沟。作者敏锐地观察到一个普遍悖论：LLMs 能解决复杂的数学题、编写代码，却在许多孩童能通过玩耍轻松掌握的简单交互任务中表现拙劣。传统强化学习（RL）智能体擅长学习“如何做”，但通常是缺乏解释性的“黑箱”，且需要海量数据。而LLMs恰恰相反，它们拥有丰富的世界知识和推理能力，却难以将这些静态知识有效转化为动态决策。\nTiG框架的巧妙之处在于，它没有将LLM仅仅当作一个决策大脑，而是将整个 基于强化学习的决策过程重构为一个语言建模任务。具体来说，LLM不再输出一个简单的动作指令，而是生成一段包含“思考过程”和“决策结果”的自然语言。这个过程通过两个核心阶段进行优化：\n监督微调（SFT）：在第一阶段，模型学习模仿专家数据，初步掌握从游戏状态（以JSON格式呈现）到高质量决策和推理链的映射。这相当于为模型打下坚实的基础，让它学会“看懂”局势并“说出”合理的战术。 强化学习（RL）：在第二阶段，模型进入“实战演练”。论文并未使用传统的需要训练一个独立奖励模型的RL算法（如PPO），而是采用了 组相对策略优化（Group Relative Policy Optimization, GRPO） 算法。这个选择至关重要。GRPO的机制是让模型针对同一个游戏状态，生成一组候选的“思考-行动”方案。然后，通过一个简单明确的 基于规则的奖励函数（在本文中即为预测的宏观动作是否与真实玩家的动作一致）来评估这一组方案的优劣。奖励信号不是一个绝对的分数，而是每个方案在组内的“相对优势”。这种方式极大地稳定了训练过程，并且避免了设计和训练复杂奖励模型的开销，让模型在“比较”和“择优”中学会如何做出更好的决策。 正如论文在第5页所描述的GRPO形式化过程，其优势在于利用一组生成（a group of G completions）的相对好坏来计算优势函数 Âᵢ,ₜ，而非依赖一个绝对的奖励分数。这使得奖励信号更加稳定，尤其适合长文本序列的优化。\n实验结果是这篇论文最引人注目的部分。作者在复杂的MOBA游戏《王者荣耀》中验证了TiG框架的有效性。最令人印象深刻的发现是，一个经过TiG框架训练的、参数量相对较小的模型（如Qwen-3-14B），在游戏内的动作预测准确率上，竟然能够超越参数量远大于它的、业界领先的通用大模型Deepseek-R1。具体数据显示，经过2000步SFT+GRPO训练的Qwen-3-14B模型准确率达到了 90.91%，而Deepseek-R1的准确率为 86.67%。这不仅证明了TiG框架的有效性，更揭示了“领域专用”微调在效率和性能上可能带来的巨大优势。\n此外，该框架的另一大亮点是其 可解释性。模型输出的\u0026lt;think\u0026gt;部分，详细展示了它如何分析战局、评估风险、制定策略的“内心独白”。例如在图4的案例中，模型清晰地分析了“防御塔和野区保护机制均已失效”、“敌方中路一塔血量较低是潜在机会”等关键信息，最终给出了“联合姜子牙推掉敌方中路一塔”的具体指令。这种透明度对于人机协作、信任建立和模型调试都具有不可估量的价值。\n总而言之，这篇论文不仅仅是关于如何让AI玩好一个游戏，它更深远的意义在于探索了一条如何让拥有“智慧”的LLMs学会在真实世界中“行动”的有效路径。它通过巧妙的框架设计、高效的训练算法和令人信服的实验结果，为开发更强大、更高效、更可信的具身智能体（Embodied AI）提供了宝贵的思路和实践范例。\n接下来，我将按照您提出的六个方面，对论文进行更详细的拆解分析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 核心目标是 弥合大型语言模型在陈述性知识和程序性知识之间的差距。 . 解决的实际问题： LLMs在交互式任务中的“眼高手低”：解决LLMs虽然能纸上谈兵（例如，从游戏攻略中学习到“不要推线太深”的策略），但在实际操作中无法执行这些策略的窘境（例如，无法判断何为“太深”）。 传统RL智能体的“黑箱”与“数据饥渴”：传统RL方法虽然能学习到精湛的操作，但决策过程不透明，难以理解和信任，并且通常需要海量的交互数据和计算资源，训练成本高昂。 提升AI智能体的可解释性和可信度：在复杂决策任务中，用户不仅想知道AI“做了什么”，更想知道“为什么这么做”。TiG框架通过生成自然语言的思考过程，直接解决了这个问题。 对行业发展的重要意义： 推动通用人工智能（AGI）的进程：真正的智能不仅需要认知，还需要与环境互动并执行任务的能力。这项研究为构建能够在动态、复杂的真实世界（而不仅仅是数字世界）中有效行动的AI智能体（即具身智能）铺平了道路。 开辟AI应用新场景：这项技术可以被广泛应用于需要战略决策和实时适应的领域，例如： 机器人学：机器人可以更好地理解指令，并在执行过程中解释自己的行为。 自动驾驶：车辆的决策过程可以更透明，便于乘客理解和信任。 个人AI助理：AI助理可以更深入地参与到用户的复杂工作流中，进行规划和执行。 降低高性能AI智能体的开发门槛：论文证明，通过TiG这样的专用框架，一个中等规模的模型可以达到甚至超越超大模型的性能。这意味着未来开发特定领域的高性能AI应用的成本和技术门槛有望大幅降低。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 这篇论文的核心创新在于 TiG (Think-In Games) 框架，它包含了一系列精巧的设计，与以往方法相比具有显著优势。\n新的思路与方法： 将RL决策重构为语言建模：这是最核心的范式转变。不同于传统RL输出离散或连续的动作值，TiG让LLM输出一段结构化的自然语言，包含\u0026lt;think\u0026gt;（思考过程）和\u0026lt;answer\u0026gt;（最终行动）。这使得强大的语言生成和推理能力可以直接被用于决策过程。 宏观动作空间（Macro-level Action Space）：论文巧妙地避开了让LLM直接进行微观操作（如按键、鼠标点击）的难题，而是定义了40个高层次的“宏观动作”，如“Push Top Lane”、“Secure Dragon”。 \u0026gt; 这极大地降低了问题的复杂性，让LLM可以专注于其最擅长的战略规划和高层推理，而非它不擅长的精确控制。 两阶段训练范式（SFT + GRPO）： SFT：利用专家数据进行监督微调，为模型注入领域知识，使其快速“入门”。 GRPO：通过强化学习进行“实战”优化。GRPO算法本身并非本文首创，但其在本场景的应用是关键。它无需一个独立的、需要大量数据训练的奖励模型，而是通过比较“一组”生成结果的相对好坏来优化策略，这对于奖励信号稀疏或难以定义的复杂任务尤为有效。 基于规则的简单奖励函数：奖励函数被极度简化为一个二元信号：模型预测的宏观动作是否与专家的动作一致（是则为1，否则为0）。 \u0026gt; 论文中提到：“This binary reward encourages the model to generate action predictions that closely match real player behavior…” 这种设计避免了奖励函数本身的复杂性和可能引入的噪声，让学习信号更纯粹、更直接。 数据增强的 relabeling 算法：为了解决原始游戏数据中标注稀疏和不一致的问题，作者设计了一个“重标注算法”，通过时间窗口内的前向填充和基于优先级的冲突解决，生成了更稠密、更一致的训练数据。 特点与优势： 效率高：实验证明，中等规模模型（14B）通过TiG训练后，性能超越了超大模型（671B的Deepseek-R1），展示了极高的计算和数据效率。 可解释性强：\u0026lt;think\u0026gt;标签的输出提供了决策的完整逻辑链，使得模型的行为透明、可信。 性能好：不仅在特定游戏任务上表现出色，而且在通用语言能力基准测试（如MMLU, BBH）上没有出现性能下降（即没有灾难性遗忘），甚至在逻辑推理任务上略有提升。 框架灵活：该框架的思路可以被移植到其他需要决策和规划的交互式环境中。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列设计严谨的实验，从多个维度验证了TiG框架的有效性。\n实验设计： 实验环境：选择了流行且极具挑战性的MOBA游戏《王者荣耀》（Honor of Kings, HoK），该游戏需要复杂的团队协作、动态决策和长期规划。 核心任务：动作预测（Action Prediction Task）。给定当前的游戏状态（JSON格式），模型需要预测出最合理的宏观动作，并生成相应的思考过程。 对比基线：选取了不同规模的先进开源模型作为基线，包括 Qwen系列（7B, 14B, 32B） 和作为性能标杆的超大规模模型 Deepseek-R1。 评估方案： 消融研究（Ablation Study）：为了验证框架中不同组件的有效性，作者对比了三种训练方式：仅GRPO、仅SFT、SFT + GRPO。 性能对比：将TiG训练后的模型与原始基线模型及Deepseek-R1进行动作预测准确率的直接比较。 通用能力评估：在Ape210K, MMLU, BBH等多个标准NLP基准上测试TiG训练后的模型，以检验其通用语言和推理能力是否受损。 错误分析：对模型的错误类型进行归类和统计，如“游戏状态误判”、“关键事件忽略”等，以深入了解模型的改进之处和弱点。 关键数据和结果： TiG框架的压倒性优势： \u0026gt; 如表2a所示，Qwen-2.5-32B模型的基线准确率为66.67%，在仅经过160步GRPO训练后，准确率飙升至 86.84%，提升超过20个百分点。这证明了RL阶段的巨大威力。\n小模型超越大模型： \u0026gt; 同样在表2a中，Qwen-3-14B模型在经过SFT和2000步GRPO训练后，准确率达到 90.91%，显著高于基线模型的82.89%，并超过了参数量为其数十倍的Deepseek-R1（86.67%）。这是整个实验部分最震撼的结论。\nMethod Accuracy (%) Deepseek-R1 86.67 Qwen-2.5-32B 66.67 Qwen-2.5-32B + GRPO (our work, steps=160) 86.84 Qwen-3-14B 82.89 Qwen-3-14B + SFT + GRPO (our work, steps=2000) 90.91 错误类型的显著改善： \u0026gt; 图2b的雷达图直观地展示了，经过TiG训练的模型（绿色线）在“游戏状态误判”（Game State Misinterpretation）和“关键事件忽略”（Critical Event Oversight）等核心决策错误上的比例显著低于原始模型（蓝色线），整体表现更接近强大的Deepseek-R1（红色线）。\n通过这些定性和定量的实验，论文强有力地证明了TiG框架在提升LLM程序性知识、决策能力和可解释性方面的巨大成功。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文为我们打开了一扇门，门后还有广阔的探索空间。\n值得探索的问题和挑战： 从离线学习到在线学习：目前TiG的RL训练是基于离线的专家数据（replay data）进行的。未来的终极挑战是让智能体在 真实的游戏环境中进行在线学习（Online Learning），直接与环境交互、接收反馈并实时调整策略。这将对算法的稳定性和探索-利用（exploration-exploitation）平衡提出更高要求。 长期记忆与规划：当前模型更多是基于当前时间点的状态做出反应式决策。如何让模型拥有 长期记忆，能够根据一整局游戏中（例如15分钟前）发生的事件来制定长期战略，是一个巨大的挑战。这可能需要结合更先进的记忆网络或外部知识库。 多模态信息融合：游戏不仅有结构化的状态数据，还有丰富的 视觉和听觉信息。未来的研究需要探索如何让模型能够像人类玩家一样，融合多模态输入来进行决策，这将大大增强模型的环境感知能力。 泛化到更广泛的领域：将TiG的成功从单一的MOBA游戏泛化到其他类型的游戏（如FPS、RTS）乃至真实世界的任务（如机器人控制、软件操作）是下一步的关键。每个领域都有其独特的挑战，需要对框架进行相应的调整。 可能催生的新技术和投资机会： AI驱动的游戏开发与测试：可以开发出能深度理解游戏机制、模拟不同水平玩家行为的AI，用于自动化游戏测试、平衡性调整和内容生成，极大地提升游戏开发效率。 下一代个人AI助理/AI Agent：基于此类技术的AI Agent将不再局限于聊天和信息查询，而是能够 操作软件、执行多步任务 的“数字员工”，例如自动完成复杂的报销流程、管理项目日程等，这将是AI Agent赛道的核心技术突破口。 专业领域的模拟与培训：在金融交易、外科手术、飞行模拟等高风险领域，可以开发出高度拟真的AI陪练，它们不仅能模拟真实场景，还能对其决策进行解释和复盘，提供高质量的培训。 可解释AI（XAI）的商业化：TiG框架产生的可解释“思考过程”本身就是一种有价值的产品。在金融、医疗、法律等需要决策透明和合规的行业，提供可解释的AI决策服务将是一个巨大的市场。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性的学术角度审视，仍有一些可以探讨的局限性。\n存在的不足及缺失： 奖励信号的局限性：模型的奖励完全来自于 模仿人类玩家。这意味着模型学习的目标是“像一个高水平人类一样玩”，而不是“赢得游戏”。这可能会限制模型发现超越人类认知的创新策略（像AlphaGo那样）。一个更理想的奖励函数应该结合模仿奖励和最终的游戏胜负结果。 对“宏观动作”定义的依赖：整个框架的成功高度依赖于预先定义的一套“宏观动作”。这套动作集的质量和完备性直接决定了模型能力的上限。在更开放、无固定规则的环境中，如何让模型 自动学习和发现有意义的宏观动作（Skill Discovery），是一个悬而未决的问题。 真实交互的缺失：如前所述，实验是在离线数据集上完成的，这与在真实环境中进行实时、在线交互有本质区别。离线学习无法完全模拟真实环境的动态性、延迟和不确定性。 可解释性的真实性：模型生成的\u0026lt;think\u0026gt;过程是与\u0026lt;answer\u0026gt;一同生成的，它解释了模型“为什么会做出这个决策”，但这个解释本身也是模型学习到的“模式”，它 不一定完全等同于模型内部真正的计算和决策过程。这种“事后归因”式的解释是否足够忠实，还需要进一步研究。 需要进一步验证和存疑的： 泛化能力：论文仅在《王者荣耀》这一款游戏上进行了验证。该框架能否无缝迁移到其他规则、节奏、交互方式完全不同的游戏中（例如《星际争霸》或《CS:GO》）？这需要更多的跨游戏实验来验证。 对数据质量的敏感度：TiG框架依赖高质量的专家数据进行SFT和奖励计算。如果数据源的玩家水平参差不齐，或者包含了大量噪声数据，模型的性能会受到多大影响？这一点论文没有深入探讨。 推理与行动的耦合：\u0026lt;think\u0026gt;和\u0026lt;answer\u0026gt;是联合生成的，这可能导致模型为了生成一个“看起来合理”的解释，而选择了一个并非最优的行动。将推理过程和行动决策过程进行一定程度的解耦，可能会带来更好的性能，值得探索。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是一个充满了可借鉴思想的宝库。\n应该重点学习和借鉴的创新想法： 任务重构的力量：最核心的启发是，当你试图让一个模型（特别是LLM）解决它不直接擅长的问题时，尝试将这个问题重构为它擅长的形式。本文将复杂的RL决策重构为了一个生成式的语言任务，这是“范式转换”思维的绝佳体现。 分层抽象（Abstraction）：在解决复杂问题时，进行 分层抽象 是一个非常有效的方法。通过定义“宏观动作”，作者将问题从复杂的低层控制中解耦出来，让模型专注于高层战略。在你的项目中，思考一下是否也可以将问题分解为战略层和执行层。 “数据驱动”与“算法创新”的结合：聪明的 数据处理（relabeling algorithm） 和高效的 算法选择（GRPO） 相辅相成。在启动一个项目前，深入分析你的数据特性，并为其量身定制处理方法和训练算法，往往能事半功倍。 将可解释性作为内置特性（Interpretability by Design）：不要在模型训练完成后再试图去“解释”它，而是在 设计阶段就将可解释性作为模型输出的一部分。\u0026lt;think\u0026gt;标签就是一个典范，它不仅提升了模型的可信度，其生成的文本本身也可以作为宝贵的分析和调试数据。 需要补充了解的背景知识： 强化学习（Reinforcement Learning）基础： 了解PPO (Proximal Policy Optimization) 算法，因为它是当前LLM进行RLHF（从人类反馈中强化学习）的主流方法。理解PPO，可以让你更深刻地体会到本文选择GRPO的动机和优势。 了解DPO (Direct Preference Optimization)，它是近年来简化RLHF流程的代表性工作。GRPO在思路上与DPO有共通之处，都是为了摆脱独立的奖励模型。 大型语言模型的微调技术： 深入理解 SFT (Supervised Fine-Tuning) 的原理和实践，因为它是TiG框架的第一步，也是所有领域专用LLM的基础。 陈述性知识 vs. 程序性知识： 这个认知科学的概念是理解本文核心动机的钥匙。可以阅读一些相关的心理学或人工智能哲学文章，来加深对这一根本性区别的理解。 希望这份详尽的解读能帮助您全面、深入地掌握这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-09-02T00:00:00Z","permalink":"/think-in-gameslearning-to-reason-in-games-via-reinforcement-learning-with-large-language-models/","title":"Think in Games：Learning to Reason in Games via Reinforcement Learning with Large Language Models"},{"content":"过采样部分很像Sample More to Think Less：Group Filtered Policy Optimization for Concise Reasoning，多采样然后筛选。其实在Beyond Policy Optimization：A Data Curation Flywheel for Sparse-Reward Long-Horizon Planning就可以看到，将稀疏奖励作为数据筛选的方式也是个不错的方案。本文的创新点比较少，主要的都是工程上的东西。\n论文深度解读 这篇由微软研究院发表的技术报告，介绍了一款名为 rStar2-Agent 的140亿参数数学推理模型。其核心贡献在于，通过一套创新的 “智能体强化学习”（Agentic Reinforcement Learning） 框架，成功地让一个中等规模的模型，在复杂的数学推理任务上，达到了甚至超越了体量远大于它（如6710亿参数的DeepSeek-R1）的顶尖模型的性能。这篇论文的精髓可以概括为一句话：追求“想得更聪明”（Think Smarter），而非“想得更长”（Think Longer）。\n传统的模型在解决复杂问题时，主要依赖于所谓的 “思维链”（Chain-of-Thought, CoT），即生成一长串推理步骤。这种“想得更长”的模式虽然有效，但有其根本局限性：它容易在中间步骤犯下难以察觉的错误，且一旦初始思路走偏，就很难自我纠正。rStar2-Agent则另辟蹊径，它不仅仅是生成文本，更像一个会使用工具的“智能体”（Agent）。它将 Python代码解释器 作为一个外部环境，通过编程来探索、验证和修正自己的推理过程。这种与环境的动态交互，赋予了模型一种前所未有的高级认知能力。\n为了实现这一目标，论文提出了三大关键创新：\n高效、可扩展的智能体强化学习基础设施：这是工程上的巨大贡献。智能体强化学习需要模型与环境进行海量交互（称为“rollouts”），这会产生数以万计的工具调用（比如执行Python代码）。为了应对这一挑战，研究团队构建了一个能够每步处理高达4.5万次并发代码执行请求的专用环境服务，并且平均延迟仅为0.3秒。同时，他们设计了 “负载均衡的Rollout调度器”，根据GPU的实时负载动态分配任务，极大地提升了训练效率，使得在有限的64张MI300X GPU上，仅用一周时间就完成了全部训练。\n创新的GRPO-RoC强化学习算法：这是算法层面的核心突破。GRPO-RoC (Group Relative Policy Optimization with Resampling on Correct) 旨在解决智能体学习中的一个核心难题：环境噪声。当模型生成的代码出错时，返回的错误信息会干扰其主线的推理思路。更严重的是，传统的强化学习如果只看最终结果，可能会奖励一个虽然答案正确、但中间过程充满错误的“坏”轨迹，从而让模型养成“坏习惯”。GRPO-RoC通过一种巧妙的 “对正确轨迹重采样”（Resampling on Correct, RoC） 策略来解决此问题。\n它首先会“过采样”生成大量的推理轨迹。 然后进行非对称的“降采样”：对于最终答案错误的轨迹，它会保留其多样性，让模型充分学习各种失败模式；而对于最终答案正确的轨迹，它会进行严格筛选，只保留那些工具使用最少、代码错误最少、格式最规范的“高质量”成功范例。 通过这种方式，模型学到的不仅仅是“如何得到正确答案”，更是“如何高效、优雅地得到正确答案”，从而变得更“聪明”。 独特的“三阶段”训练配方：这是实践层面的智慧结晶。\n第一步：非推理SFT（监督微调）。与主流方法不同，rStar2-Agent在SFT阶段完全不使用包含复杂推理的数据，只让模型学习如何遵循指令、如何正确地格式化JSON工具调用。论文认为，过早进行推理SFT会导致“过拟合”，限制了模型在后续RL阶段的探索潜力。 第二步：多阶段强化学习。这是一个“课程学习”的设计。从8K的较短响应长度开始，让模型学会简洁地解决问题；然后扩展到12K，处理更复杂的情况；最后，筛选出最难的17.3K个问题进行“攻坚训练”，将模型的能力推向极限。 最终，rStar2-Agent-14B在极具挑战性的AIME24测试中取得了80.6%的pass@1准确率，不仅超越了671B的DeepSeek-R1（79.8%），甚至比Claude-Opus-4.0高出3.6%。更值得注意的是，它的平均响应长度（即解决问题所需的“思考长度”）显著缩短，例如在AIME24上，它的平均响应长度为9339.7个token，而DeepSeek-R1-Zero则需要14246.8个token。这有力地证明了“想得更聪明”策略的成功。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 核心研究目标：探索如何通过智能体强化学习（Agentic RL），让大语言模型超越当前基于“长思维链”（long CoT）的限制，发展出更高级的认知能力，即学会“思考得更聪明（think smarter）”，而不仅仅是“思考得更长（think longer）”。\n想要解决的实际问题：\n推理过程的脆弱性：长思维链模型在多步推理中容易犯下细微错误，且缺乏有效的自我纠正机制。 “伪阳性”强化：在强化学习中，一个中间步骤充满错误、但最终答案侥幸正确的推理过程（trajectory）仍然会得到正向奖励，这会污染模型的学习过程，让模型学会“坏习惯”。 训练效率低下：智能体与环境的大量交互（尤其是代码执行）会带来巨大的计算开销和延迟，使得大规模智能体RL训练在工程上极具挑战。 对行业发展的意义：这篇论文为大模型的发展指明了一个极具前景的方向。在模型尺寸和数据量趋于瓶颈的今天，单纯依靠“暴力堆料”来提升性能的边际效应正在递减。rStar2-Agent证明了，通过改进学习范式（Agentic RL）和算法，中等规模的模型同样可以实现甚至超越巨型模型的智能水平，并且效率更高。这为开发更经济、更实用、更可靠的AI系统提供了全新的可能性，也为通往更通用人工智能（AGI）的道路探索了一条资本效率更高的路径。\n2. 论文提出了哪些新的思路、方法或模型？ 论文的核心创新在于其方法论的整合，主要体现在以下三个层面：\n思路革新：从“生成者”到“行动者”： 最大的思路转变是将LLM从一个被动的“文本生成器”提升为一个主动的“智能行动者”（Agent）。它不再仅仅依赖内部的参数知识进行推理，而是主动与外部环境（Python解释器）交互。 \u0026gt; As stated in the paper: “we aim to enable models to “think smarter” by developing more advanced cognitive abilities that autonomously utilize the right tools to reason, validate, and learn from the feedback signals provided by the tool environment.” 这种交互使得模型可以： 探索（Explore）：通过写代码尝试不同的解题路径。 验证（Verify）：执行代码来检验中间步骤的正确性。 修正（Refine）：根据代码执行的反馈（无论是成功结果还是错误信息）来调整后续的推理策略。 算法创新：GRPO-RoC： 该算法的基石是 GRPO (Group Relative Policy Optimization)，一种强化学习算法。其核心创新在于 RoC (Resample-on-Correct) 策略。 特点与优势：相较于之前试图通过复杂的“奖励设计”（Reward Shaping）来惩罚工具错误的方法，RoC的做法更简洁、更鲁棒。 避免奖励 hacking：它坚持使用最简单的“仅结果奖励”（outcome-only reward），即最终答案对错决定奖励是1还是0。这有效避免了模型为了迎合复杂的奖励函数而做出一些投机取巧的“坏行为”。 净化学习信号：其精髓在于对“成功样本”的严格筛选。如下图所示，它通过计算工具调用错误率（perr）和格式错误（pformat）来给每个成功的轨迹打分，优先学习那些最“干净”、最高效的成功路径。 \u0026gt; “Positive trajectories are filtered to retain only the highest-quality ones with minimal tool-induced errors or tool call formatting issues, while negative trajectories are uniformly downsampled. This asymmetric sampling reinforces positive supervision without losing the various learning signal from failures…” 保留失败多样性：同时，它不对失败样本做任何筛选，确保模型能看到各种各样的错误，从而进行更全面的学习。 策略 成功轨迹 (r=1) 失败轨迹 (r=0) 优势 GRPO-RoC 筛选：只保留工具错误少、格式规范的 保留：均匀采样，保留多样性 学习高质量成功范例，同时充分认知失败模式 传统方法 保留：所有成功轨迹都给予正奖励 保留：所有失败轨迹都给予负奖励 容易强化包含“坏习惯”的成功轨迹 训练配方创新：非推理SFT + 多阶段RL： 非推理SFT：这是一个反直觉但非常有效的策略。论文主张，在SFT阶段，模型的核心任务是学会“如何使用工具”，而不是“如何思考”。这避免了模型对特定推理模式的“死记硬背”，为后续RL阶段的自由探索保留了最大的可能性。 多阶段RL课程：这是一种循序渐进的训练方法，如Table 2所示，它将训练分为三个阶段，逐步增加响应长度和问题难度。这种“先易后难”的课程设计，让模型在每个阶段都能高效学习，避免了因任务过难而导致的学习停滞。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且有说服力的实验来验证其方法的有效性。\n实验设计： 主实验：在多个权威的数学推理基准（MATH-500, AIME24, AIME25, HMMT25）上，将rStar2-Agent-14B与当前最先进的模型（包括闭源的OpenAI、Claude和开源的DeepSeek-R1、Qwen等）进行性能对比。 泛化能力实验：为了检验模型学到的推理能力是否局限于数学领域，论文还在科学推理（GPQA-Diamond）、智能体工具使用（BFCL v3）和通用对齐（IFEval, Arena-Hard）等任务上进行了测试。 消融研究（Ablation Study）：这是验证方法各部分有效性的关键。论文在Figure 9中对比了三种设置： DAPO (no tool)：传统的纯CoT强化学习，不使用工具。 GRPO with Tool：使用工具，但没有RoC筛选策略。 GRPO-RoC：完整的rStar2-Agent方法。 关键数据和结果： 性能超越：如Table 3所示，rStar2-Agent-14B（14B）在AIME24上获得80.6%的准确率，高于DeepSeek-R1（671B）的79.8%和Claude-Opus-4.0的76.0%。 效率提升：Table 4的数据极具说服力。在AIME24上，rStar2-Agent-14B的平均响应长度仅为9339.7 tokens，远低于Qwen3-14B的14747.6和DeepSeek-R1-Zero的14246.8。这直观地证明了它“想得更聪明”。 消融研究证明RoC的价值：Figure 9的结果清晰地显示，GRPO-RoC在AIME24和AIME25上的准确率持续高于GRPO with Tool和DAPO。更重要的是，(c)图显示GRPO-RoC的平均响应长度显著低于GRPO with Tool，证明了RoC策略确实能引导模型走向更简洁高效的推理路径。 (图注: Figure 9的简化示意图，清晰展示了GRPO-RoC在准确率和响应长度上的双重优势)\n4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 结合当前大模型领域的理解，该研究方向未来充满机遇和挑战：\n值得探索的问题： 环境的泛化：当前环境仅限于Python解释器。未来能否扩展到更复杂的环境，如 Web浏览器、文件系统、数据库、甚至物理世界的机器人？这将催生出能力更全面的通用智能体。 多智能体协作：能否构建一个多智能体系统，让不同的rStar2-Agent式智能体分工协作，解决更为宏大和复杂的问题，例如科学研究或软件工程项目。 自我改进的环境：智能体能否不仅利用环境，还能主动改进环境？例如，通过编写和存储可复用的函数库来不断扩充自己的工具集，实现能力的持续自我迭代。 模型能力上限的突破：论文中提到，当模型达到其“预训练所赋予的推理能力上限”时，继续强化学习会导致“崩溃”。这是一个根本性的挑战。未来的研究需要探索，能否通过新的模型架构（如混合专家模型MoE）或训练方法，来动态提升这个“能力天花板”。 新的技术和投资机会： 智能体平台即服务 (Agent PaaS)：可能会出现专门为训练、部署和托管AI智能体设计的云平台，提供标准化的工具环境、高效的RL基础设施和监控系统。 垂直领域专业智能体：在金融分析、药物研发、法律咨询等专业领域，基于该技术训练的专用智能体将具有巨大的商业价值。 新一代开发工具：未来的软件开发可能由AI智能体主导，人类开发者扮演“架构师”和“产品经理”的角色。这将催生出全新的AI辅助编程和项目管理工具。 对算力的需求：虽然模型本身变小了，但智能体RL训练对能够进行高并发、低延迟处理的基础设施（尤其是CPU和网络）提出了新的要求，这可能成为一个新的投资热点。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以探讨的局限性：\n泛化能力的局限性：尽管论文在GPQA科学推理上展示了良好的泛化，但在Table 5中，其在通用工具使用（BFCL v3）和对齐（IFEval, Arena-Hard）任务上的性能，与仅经过SFT的基线模型相比并无提升。这表明，其通过数学训练学到的“更聪明的思考方式”可能具有一定的领域特异性，能否平移到非形式逻辑或创造性任务上仍是未知数。\n对基础设施的强依赖：论文的成功在很大程度上依赖其强大的、定制化的RL基础设施。对于大多数没有能力复制这套“64卡MI300X集群+高并发代码执行环境”的研究机构或公司来说，复现其结果的门槛非常高。论文需要更详细地说明，其方法在普通或较差的硬件环境下的表现如何。\n“训练崩溃”现象未解：论文坦诚地报告了在训练后期出现的“崩溃”现象，并将其归因于“模型能力上限”。 \u0026gt; “We hypothesize the root cause is model capacity: our current RL implementation cannot reliably extend reasoning ability beyond what was acquired during pretraining”\n然而，这只是一个假设，缺乏深入的实验分析和验证。这个问题的根本原因是什么？是算法的不稳定、数据分布的偏移，还是确实是模型结构的限制？这是一个亟待解决的关键问题。 模型架构的单一性：实验主要基于Qwen系列模型。该方法论是否对其他主流架构（如Llama、Mistral等）同样有效，还需要进一步的验证。\n6. 我应该从这篇论文中重点学什么？ 对于希望从中汲取创新想法的您来说，以下几点尤其值得关注和学习：\n核心启发：分离“行为”与“推理”的训练： 非推理SFT 的思想极具借鉴意义。在引入一项新能力（如工具使用）时，可以先通过SFT让模型专门学习“如何正确地行为”（如调用API的格式），而将“何时、为何以及如何更好地行为”这个更复杂的“推理”任务，完全交给后续的强化学习阶段去探索。这是一种解耦复杂任务的有效策略。 拿来即用的创新点：Resample-on-Correct (RoC)： 这是本文最可移植的算法思想。在任何需要通过RL进行优化的场景，如果您面临“奖励信号稀疏或嘈杂”的问题，都可以借鉴RoC：使用简单的最终结果作为奖励，然后极其挑剔地选择成功的样本进行学习。与其设计复杂的奖励函数，不如设计一个精巧的“成功样本过滤器”。这个思想可以广泛应用于代码生成、游戏AI、对话系统优化等多个领域。 实践智慧：课程学习用于RL： 三阶段、由易到难的RL训练配方，是一个非常实用的工程实践。在训练模型解决复杂问题时，设置一个“课程”，从简化版任务（如更短的上下文、更简单的问题）开始，逐步过渡到完整版任务。这能显著提升训练的稳定性和效率。 需要补充的背景知识： 强化学习基础：尤其是策略优化算法，如 PPO (Proximal Policy Optimization)。GRPO是PPO思想的延伸，理解PPO将有助于您更深入地把握GRPO-RoC的机制。 思维链 (Chain-of-Thought) 与 Self-Correction：了解当前主流的LLM推理技术及其局限性，能让您更好地理解本篇论文试图解决的问题的重要性。 LLM Agent / Tool Use：熟悉关于大模型使用外部工具（如计算器、搜索引擎、代码解释器）的相关研究，这是整个Agentic RL范式的基础。 希望这份详尽的解读能够帮助您深入理解这篇论文的价值，并激发您的创新灵感。\n","date":"2025-09-01T00:00:00Z","permalink":"/rstar2-agentagentic-reasoning-technical-report/","title":"rStar2-Agent：Agentic Reasoning Technical Report"},{"content":"将与搜索工具交互与答案整理分离，也就是术业有专攻。根据最终答案的正确奖励来优化搜索llm。\n论文深度解读 首先，让我们对这篇论文进行一个全面的剖析。当前，利用大语言模型（LLM）与搜索引擎结合来解决复杂问题已成为主流。一种常见的方法是使用强化学习（RL）来训练一个模型，让它学会如何与搜索引擎进行多轮交互，这个过程我们称之为智能体搜索（Agentic Search）。然而，论文敏锐地指出了现有方法的症结所在：\nHowever, existing RL-based search agents rely on a single LLM to handle both search planning and question-answering (QA) tasks in an end-to-end manner, which limits their ability to optimize both capabilities simultaneously. （然而，现有的基于强化学习的搜索智能体依赖单一的LLM来端到端地处理搜索规划和问答任务，这限制了它们同时优化这两种能力。）\n简单来说，就是让一个模型同时扮演“思考者”（决定搜什么、怎么搜）和“回答者”（生成最终答案）两个角色，这很难做到两全其美。在实际应用中，为了保证回答的质量，我们通常会使用一个非常强大但“昂贵”的LLM（如GPT-4）作为“回答者”，并且这个模型通常是“冻结”的，即我们不会轻易去重新训练它。\n基于这一洞察，AI-SearchPlanner提出了三大核心创新，彻底改变了游戏规则：\n架构解耦（Decoupling the Architecture）：这是整个框架的基石。论文没有使用单一模型，而是将系统拆分为两个关键模块：\n搜索规划器（Search Planner）：一个相对小巧、可训练的LLM，专门负责与搜索引擎交互，制定搜索策略。它的任务是生成思考路径和搜索查询（sub-queries）。 生成器（Generator）：一个大型、冻结的LLM，专门负责在规划器收集完所有必要信息后，生成高质量的最终答案。 这种设计的好处是显而易见的：我们只需要训练一个轻量级的规划器模型，就可以显著提升一个已有的、强大的生成器模型的性能，这在工程上是极为高效和灵活的。你可以随时更换更强大的生成器模型，而规划器依然有效。\n双重奖励对齐（Dual-Reward Alignment）：这是训练规划器的“指挥棒”。为了让规划器学会“聪明地”搜索，论文设计了一个精巧的奖励机制，而不仅仅是看最终答案对不对。这个双重奖励包含两个部分：\n结果奖励（Outcome Reward）：衡量使用了搜索规划后，最终答案质量的提升程度。它会比较AI-SearchPlanner生成的答案与不进行规划（如直接问答或简单的RAG）生成的答案，根据答案质量和问题本身的难度给予奖励。这确保了规划器的行为最终是为了“结果好”。 过程奖励（Process Reward）：评估搜索过程本身是否“合理”。论文创造性地利用强大的生成器LLM来给规划器的整个搜索轨迹（Trajectory）打分。这就像一位老师批改数学题，不仅看最终答案是否正确，还会检查解题步骤是否清晰、逻辑是否严谨。这确保了规划器学会的是“方法对”，而不仅仅是碰巧蒙对答案。 效用与成本的帕累托优化（Pareto Optimization of Planning Utility and Cost）：这是平衡性能与效率的“调节阀”。在现实世界中，我们不希望搜索过程无限进行下去，因为每次搜索都意味着时间和计算成本。论文将这个问题形式化为一个帕累托优化问题，旨在同时最大化规划的效用（Utility）（由双重奖励定义）并最小化规划的成本（Cost）（由搜索轮次和查询次数定义）。通过引入一个成本系数α，研究者可以在模型的性能和效率之间进行权衡，找到最佳的平衡点。\n为了验证这一框架的有效性，论文在包括NQ、HotpotQA等7个基于维基百科的数据集和2个基于真实网络环境的数据集上进行了大量实验。结果非常亮眼，例如在表1中，使用一个7B的Qwen2.5模型作为规划器，一个32B的Qwen3模型作为生成器，AI-SearchPlanner在所有维基百科数据集上的平均分达到了0.597。相比之下，更强大的32B模型如果只使用简单的RAG方法，平均分仅为0.539；而一个统一的、经过强化学习训练的7B模型（Search-R1），平均分也只有0.519。这充分证明了AI-SearchPlanner框架的优越性，它用更小的训练代价，显著提升了大型冻结模型解决复杂问题的能力，实现了超过10%的性能增益。\nimage.png 图1：AI-SearchPlanner 框架概览，清晰地展示了可训练的规划器（Planner）与冻结的生成器（Generator）的解耦结构。 总而言之，这篇论文不仅仅是提出了一个新模型，更是倡导了一种新的、更具实践意义的系统构建范式。它告诉我们，通过精巧的系统设计和训练策略，我们可以在不重新训练巨大模型的前提下，有效“赋能”它们，解决更复杂的问题。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n### 1. 论文的研究目标是什么？ 研究目标：论文的核心研究目标是设计一个高效且有效的强化学习框架，用以优化一个小型、可训练的LLM作为搜索规划器，从而在不改动一个大型、冻结的生成器LLM的前提下，显著增强整个系统在复杂问答任务上的表现。\n解决的实际问题：\n成本与效率问题：在工业界，直接对GPT-4级别的超大模型进行完整的强化学习微调，成本极高且不切实际。这些模型通常作为API提供，是“冻结”的。论文提出的方法，通过只训练一个小型规划器，大大降低了训练成本和推理延迟。 能力优化冲突问题：现有的端到端方法要求一个模型同时擅长逻辑规划（如何搜索）和语言生成（如何回答），这两种能力并非总能协同增长。AI-SearchPlanner通过解耦，让每个模块专注于自己擅长的事情。 灵活性与可扩展性问题：在一个单一模型系统中，如果想升级基础模型，通常意味着需要重新进行整个训练流程。而在AI-SearchPlanner框架下，生成器可以被轻易替换成任何更先进的模型，而规划器依然能够与之配合，具有极强的“即插即用”特性。 行业意义：这个问题对于行业发展意义重大。它为如何在有限的资源下，最大化利用现有的大模型能力提供了标准答案。企业可以利用这个框架，在内部构建一个轻量级的“规划层”，来增强它们调用的商业LLM API（如OpenAI API）或内部部署的开源模型的能力，从而以较低的成本构建出更“聪明”的AI应用，例如下一代智能客服、研究助理和自动化报告生成系统。\n### 2. 论文提出了哪些新的思路、方法或模型？ 论文的核心贡献是一个名为 AI-SearchPlanner 的新框架，其创新之处主要体现在以下三个方面，与之前的方法形成了鲜明对比：\n思路一：架构解耦（Planner-Generator Decoupling） 新思路：将智能体搜索任务分解为“规划”和“生成”两个独立的模块，并由不同规模和角色的模型承担。 与之前方法的对比：之前的主流研究如Search-R1等，通常采用端到端（End-to-End）的训练方式，即一个模型负责从头到尾的所有任务。 特点与优势： 专业化：规划器专注于学习“如何提问”，生成器专注于“如何回答”。 高效率：训练成本集中在小模型上，推理时，昂贵的大模型只在最后调用一次。 灵活性：生成器可以自由替换，论文在实验的“Generator Transferability”部分也证明了这一点，当把生成器从Qwen3-32b换成更强的Deepseek-R1后，平均性能从0.597提升到了0.648，证明了规划器强大的泛化能力。 思路二：双重奖励机制（Dual-Reward Mechanism） 新思路：在强化学习的奖励设计中，同时考虑“结果”和“过程”的质量。 与之前方法的对比：多数RL方法只依赖基于结果的奖励（Outcome-based Reward），即只关心最终答案的正确性。这可能导致模型学会了一些碰巧有效的“坏习惯”。 特点与优势： 对齐更精准：通过过程奖励，模型被引导去学习一个逻辑上更合理的搜索路径。论文中提到，这个奖励R_process是通过一个强大的冻结生成器LLM_gen来评估搜索轨迹T的合理性得到的，这是一种非常聪明的利用大模型自身知识来监督小模型的方式。 解决复杂问题能力更强：在简单的单跳问题中，只看结果可能就够了。但在多跳推理问题（Multi-Hop QA）上，正确的步骤至关重要。从表1中可以看出，AI-SearchPlanner在HotpotQA、2Wiki等复杂多跳数据集上的性能优势尤为明显。 思路三：帕累托多目标优化（Pareto Multi-objective Optimization） 新思路：将搜索问题建模为一个在“效果”（Utility）和“成本”（Cost）两个维度上进行权衡的多目标优化问题。 与之前方法的对比：传统方法通常将成本（如搜索步数）作为惩罚项简单地加入奖励函数，缺乏系统性的权衡机制。 特点与优势： 权衡的显式控制：通过调整公式 R_pareto = R_utility + α * R_cost + R_format 中的 α 系数，用户可以明确地控制模型是更倾向于“不计成本地追求最好结果”还是“在保证一定效果下尽可能快”。 探索帕累托前沿：如图3所示，通过训练不同α值的模型，论文描绘出了性能与成本之间的帕累托前沿。例如，当α=0.005时，模型以很小的性能代价（0.606 vs 0.597）将平均搜索轮次从2.260次显著降低到1.886次，实现了更优的效用-成本权衡。 Figure 3 from the paper, showing Utility-Cost tradeoffs 图3：效用-成本权衡图。橙色点代表不同成本系数α下的AI-SearchPlanner模型，蓝色点代表基线方法。AI-SearchPlanner在性能和成本上均优于基线，形成了一个更优的帕累托前沿。 ### 3. 论文通过什么实验来验证有效性？ 论文设计了全面且严谨的实验来验证其方法的有效性。\n实验设计： 数据集：实验覆盖了两大类场景。 维基百科数据集：包括NQ、TriviaQA等3个通用问答数据集，以及HotpotQA、Musique等4个更具挑战性的多跳问答数据集。这些数据集主要检验模型在结构化知识库上的规划与推理能力。 网络数据集：包括WebShaper和WebWalkerQA，用于测试模型在真实、动态的网络环境中进行信息检索的能力，这更接近实际应用。 基线模型（Baselines）：为了充分证明其优势，论文与三类强大的基线进行了对比。 无检索方法：如直接推理（Direct Inference）和思维链（CoT）。 有检索的非训练方法：如朴素RAG（Naive RAG）和IRCoT。 基于训练的方法：包括监督微调（SFT）和同样使用强化学习的端到端模型Search-R1。 核心评估：评估主要围绕两个核心问题展开： 整体性能：在同等或更强的生成器条件下，AI-SearchPlanner是否优于所有基线？ 泛化与迁移能力：训练好的规划器，能否在不重新训练的情况下，适配不同的生成器（Generator Transferability）和不同的数据领域（Domain Transferability）？ 关键数据和结果： 整体性能优越：如前所述，在表1中，AI-SearchPlanner（规划器Qwen2.5-7B，生成器Qwen3-32B）的平均分（0.597）显著高于所有基线。特别是在多跳问答HotpotQA上得分0.678，而端到端RL模型Search-R1仅为0.571，显示了其在复杂推理上的巨大优势。 出色的迁移能力： 生成器迁移：当规划器保持不变，而生成器换成更强的Deepseek-R1时，平均性能提升至0.648，证明规划器学到的搜索策略是通用的，不依赖于特定的生成器。 领域迁移：在表2中，将在维基百科上训练的规划器直接用于网络数据集，其性能同样远超基线。例如，在WebShaper上，AI-SearchPlanner取得了0.366的成绩，而基线模型Naive RAG仅为0.204，性能提升了79.41%。这说明模型学到的规划能力具有很强的鲁棒性。 消融研究（Ablation Study）：在表4中，论文通过移除双重奖励中的某个部分来验证其必要性。 移除“过程奖励”（w/o Process Reward），模型平均性能从0.597下降到0.588，证明了过程监督的价值。 移除RL训练，直接使用一个冻结的规划器（w/o RL Training），性能更是大幅下降到0.547，证明了通过强化学习进行优化的极端重要性。 ### 4. 未来研究方向与潜在机会 结合当前大模型领域的理解，这篇论文的研究方向有广阔的探索空间：\n值得探索的问题与挑战： 多模态搜索规划：当前框架主要处理文本信息。未来一个重要的方向是将其扩展到多模态（Multi-modal）领域，让规划器能够决定是搜索文本、图片还是视频信息来回答一个复杂问题（例如，“给我找找那款在最近的科幻电影里出现，外观像甲壳虫的无人机”）。 动态与自适应奖励：目前的过程奖励依赖一个固定的生成器来评估。未来的研究可以探索动态奖励机制，例如，让奖励模型与规划器一同训练，或者根据任务的实时反馈（如用户是否点击了某个搜索结果）来动态调整奖励。 规划器的可解释性与安全性：如何确保规划器的搜索行为是可解释且安全的？它会不会为了寻求奖励而学会利用搜索引擎的漏洞，或者生成有害的搜索查询？这在模型部署到真实世界前是必须解决的问题。 更复杂的工具调用：目前的工具只有“搜索”。未来可以扩展到一个更复杂的工具集，例如，规划器可以决定是调用搜索引擎、计算器、代码解释器还是知识图谱查询API。这会大大增加规划的复杂性。 新的技术和投资机会： 规划即服务（Planning-as-a-Service）：这篇论文的技术可以催生出一种新的商业模式。公司可以专门训练和提供高效的“规划器模型”，作为一个中间件，即插即用地增强客户现有的LLM应用，这可能成为AI基础设施层的一个新热点。 小型专用模型（Small, Specialized Models）：这篇论文证明了小型模型在特定任务（如规划）上可以发挥巨大价值。这为专注于开发和优化小型、高效的专用模型的初创公司提供了机会，而不是所有人都去追逐构建通用的大模型。 AI智能体开发平台：围绕这种模块化思想，可以构建更先进的AI智能体开发平台。开发者可以在平台上选择不同的规划器、生成器和工具集，像搭积木一样快速构建出强大的智能体应用，而无需关心底层的复杂训练过程。 ### 5. 论文的不足与待验证之处 从批判的视角看，这篇论文虽然非常出色，但仍存在一些可以深入探讨的地方：\n过程奖励的可靠性与偏见：论文提出用一个大型生成器LLM_gen来评估搜索轨迹的合理性，并给出一个[0, 0.5]之间的分数R_process。这里的核心问题是：\n评估的稳定性：LLM作为评估者的输出本身可能存在不稳定性。对于同一个轨迹，多次评估可能会得到不同的分数。 潜在偏见：生成器自身的知识和推理模式会引入偏见。如果生成器本身在某个问题上存在“思维定式”，它可能会惩罚那些虽然新颖但同样有效的搜索路径，从而限制了规划器的探索。 实现细节模糊：论文中并未详细说明用于评估的提示（Prompt） P_T 是如何设计的，以及如何从LLM的文本输出中稳定地提取出一个连续的分数。 成本分析不够全面：论文主要从“搜索轮次”和“查询数量”来衡量成本，这是一种代理指标。在实际应用中，我们更关心真实的计算成本（FLOPs）和端到端延迟（Latency）。特别是在训练阶段，反复调用大型生成器来计算过程奖励，其计算开销可能相当可观，这一点论文并未进行深入分析。\n数据集的局限性：尽管实验覆盖了多种数据集，但它们大多是学术性的QA数据集。在更开放、更动态、充满噪声和不确定性的真实世界应用中（例如，一个需要与真实网站API交互的预订助手），AI-SearchPlanner学到的策略是否依然稳健，还需要进一步验证。\n### 6. 对我的启发与学习建议 这篇论文是创新思想的宝库，您可以从中获得很多“拿来即用”的启发：\n重点学习的创新思想： 模块化与解耦是王道：在构建复杂的AI系统时，不要试图用一个模型解决所有问题。学习将复杂任务分解为一系列更小的、定义清晰的子任务，并为每个子任务选择或训练最合适的模型。这种“微服务”的架构思想在AI领域同样适用。 利用“强模型”监督“弱模型”：论文中用强大的生成器来评估和指导规划器的训练，这是一个非常巧妙的自监督或弱监督范式。在你的工作中，如果缺少高质量的标注数据，可以思考是否能利用一个更强大的模型（如GPT-4）来为你现有的模型生成“过程反馈”或伪标签。 将隐性权衡显式化：在很多项目中，我们都会在性能、成本、速度之间做权衡。学习AI-SearchPlanner的帕累托优化思想，将这些权衡用数学语言明确地建模出来。这不仅能让你更科学地做出决策，也能向他人清晰地展示不同方案的优劣。 需要补充的背景知识： 强化学习（Reinforcement Learning）：特别是策略梯度方法，如论文中使用的PPO (Proximal Policy Optimization)。你需要理解RL的基本概念，如智能体（Agent）、环境（Environment）、状态（State）、动作（Action）、奖励（Reward）等。 检索增强生成（RAG）：这是当前LLM应用的基础技术之一，理解其工作原理和局限性，可以帮助你更好地理解AI-SearchPlanner的出发点。 大模型智能体（LLM Agents）：了解智能体的基本概念，特别是像ReAct (Reason+Act) 这样的框架，它们是AI-SearchPlanner这类工作的前身。 多目标优化（Multi-objective Optimization）：了解帕累托最优和帕累托前沿的基本概念，这是一种在多个冲突目标之间进行决策的强大数学工具。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中汲取有价值的灵感。\n","date":"2025-08-30T00:00:00Z","permalink":"/ai-searchplannermodular-agentic-search-via-pareto-optimal-multi-objective-reinforcement-learning/","title":"AI-SEARCHPLANNER：MODULAR AGENTIC SEARCH VIA PARETO-OPTIMAL MULTI-OBJECTIVE REINFORCEMENT LEARNING"},{"content":"引入了课程学习的思想，如果一个问题很难做对，给予一定的提示，并不断增强提示，直到可以做对。\n论文深度解读 大型语言模型（LLM）在复杂推理任务（如数学、编程）上的能力，已成为衡量其智能水平的关键标尺。强化学习（Reinforcement Learning, RL），特别是带有可验证奖励的强化学习（RLVR），被证明是激发LLM自我提升潜力的强大范式。它允许模型通过生成答案、获得正确与否的反馈（奖励），然后根据反馈调整自身策略，从而不断进步。\n然而，理想很丰满，现实很骨感。在实践中，尤其是当面对超出模型当前能力范围的难题时，RL训练往往会陷入困境。这篇论文敏锐地指出了问题的核心：奖励稀疏（Reward Sparsity）。想象一下让一个小学生直接去做考研数学题，他几乎不可能做对任何一道题。在模型训练中也是如此，当模型对一个问题连续多次尝试都失败时，它获得的奖励将永远是“0”。没有了奖励信号的指引，模型的学习就会停滞不前，梯度更新也会趋近于零，这就是所谓的“梯度消失”。这不仅极大地降低了训练效率，浪费了宝贵的计算资源，还会导致训练过程非常不稳定。\n论文引用了一个非常有力的数据来说明这个问题的严重性： \u0026gt; To quantify this capacity-difficulty mismatch, we evaluated the performance of the Qwen2.5-7B-Instruct model on the NuminaMath-1.5 dataset… Our analysis revealed that even the Qwen2.5-7B-Instruct model, a more capable version of its foundation model, failed to solve 52% of the problems. \u0026gt; 为了量化这种能力-难度不匹配，我们评估了Qwen2.5-7B-Instruct模型在NuminaMath-1.5数据集上的表现……我们的分析显示，即使是这个更强的模型，也无法解决其中52%的问题。\n这意味着超过一半的数据在训练过程中可能不会产生任何有效的学习信号！这对于任何RL算法来说都是一个巨大的挑战。\n为了攻克这一难题，论文提出了GHPO（Guided Hybrid Policy Optimization）框架。这个框架的精髓在于“智能引导”和“混合策略”。它不再是一根筋地强迫模型去“探索”，而是在模型“迷路”时，及时地伸出援手。\nGHPO框架主要由两个核心模块构成：\n自动化难度检测 (Automated Difficulty Detection): 这是GHPO的“眼睛”，用来判断模型是否需要帮助。它的机制非常简洁而高效：让模型对同一个问题生成G次答案，如果这G次尝试全部失败（即奖励全部为0），那么系统就判定这个问题对于模型当前的能力来说“太难了”。 自适应提示精炼 (Adaptive Prompt Refinement): 这是GHPO的“手”，在检测到困难问题后提供帮助。它会自动从该问题的标准解题步骤中，提取一部分作为“提示（Hint）”，并将其附加到原始问题之后，形成一个更简单的新问题。例如，在解一道数学题时，把解题的前几步思路直接告诉模型。 更进一步，这种“帮助”还是多阶段、自适应的。GHPO采用了一个动态的提示策略。开始时，只提供少量提示（例如，25%的解题步骤）。如果模型在获得少量提示后仍然无法解决问题，那么在下一轮训练中，系统会增加提示的比例（例如，提升到50%）。这种机制就像一位耐心的老师，根据学生的情况循循善善诱，动态调整教学难度，从而创建了一个自适应的课程学习 (Curriculum Learning)。\n通过这种方式，GHPO巧妙地将两种学习范式融合在一起： * 对于模型有能力处理的问题，它采用标准的on-policy强化学习，鼓励模型自由探索、自我发现。 * 对于模型暂时无法解决的难题，它无缝切换到引导式的模仿学习，通过提供部分答案来降低难度，确保模型能够获得有效的学习信号。\n论文通过详尽的实验证明了GHPO的有效性。如下图1所示，与基线模型相比，GHPO在多个数学基准测试上均取得了显著的性能提升，平均提升了约5%。\nFigure 1: Overall Performance of GHPO 尤其值得关注的是论文中对训练动态的分析（图6）。结果显示，GHPO不仅获得了更高的准确率奖励，还使得训练过程中的梯度范数（Gradient Norm）显著减小且更加平滑。这为“GHPO能提升训练稳定性”这一核心论点提供了最直接、最有力的证据。\n总而言之，这篇论文并非提出了一个全新的、复杂的模型结构，而是通过一个设计精巧、实用性极强的框架，解决了现有技术在应用中的一个关键痛症。它为如何在LLM的RL训练中平衡探索与指导、提升效率与稳定性，提供了一个优雅且可扩展的解决方案。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是解决大型语言模型（LLM）在应用带有可验证奖励的强化学习（RLVR）进行微调时，普遍存在的训练不稳定和样本效率低下的问题。 实际问题: 奖励稀疏 (Reward Sparsity): 这是最核心的实际问题。当训练任务的难度远超模型当前的能力时，模型很难生成正确的答案，导致获得的奖励信号极其稀疏（绝大部分时间为0）。 梯度消失与训练停滞: 稀疏的奖励信号会导致策略梯度消失，模型无法获得有效的学习方向，从而使得训练过程停滞不前。 计算资源浪费: 模型花费大量算力在这些“无效”的难题上进行采样和评估，但由于无法获得学习信号，这些计算资源被白白浪费，极大地拉低了训练的整体效率。 行业意义: 推动RL在LLM中的应用: RL被认为是让LLM实现超越预训练知识、获得真正推理和规划能力的关键路径。解决其训练不稳定的问题，是这项技术从学术前沿走向大规模工业化应用的核心瓶颈之一。 降低高性能模型的训练门槛: 通过提升训练效率和稳定性，GHPO使得用更少的计算资源、在更短的时间内训练出强大的推理模型成为可能。这尤其对于资源相对有限的中小型企业和研究机构具有重要意义。 赋能更强大的AI智能体: 复杂推理能力是构建通用人工智能（AGI）或高级AI智能体的基石。一个稳定高效的训练框架，能够加速模型在数学、科学、编程等领域的进化，为最终实现更强大的AI奠定基础。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新在于提出了GHPO (Guided Hybrid Policy Optimization)框架，它并非一个全新的模型，而是一种新颖的训练策略。\n新思路: 其核心思路是困难感知的混合式学习（Difficulty-Aware Hybrid Learning）。它摒弃了“一刀切”的训练方式，主张根据模型对具体问题的实时表现，动态地在强化学习（探索）和模仿学习（指导）之间切换。\n新方法: GHPO框架主要包含以下几个关键方法：\n自动化难度检测 (Automated Difficulty Detection): 特点: 论文提出了一种极为简洁的在线难度评估方法。它不依赖任何外部模型或复杂算法，而是直接利用RL训练过程中的内在信号——奖励。 \u0026gt; Specifically, if all G individual rewards associated with a given query are zero, it indicates that the current policy model failed to generate a correct reasoning path… Consequently, this query q is identified as difficult for the current policy model… \u0026gt; 具体来说，如果一个给定查询所关联的G个独立奖励全部为零，这表明当前策略模型未能生成一条正确的推理路径……因此，该查询q被识别为对于当前策略模型是“困难”的…… 优势: 这种方法是自适应的和动态的。一个问题在训练初期可能被判定为“困难”，但随着模型能力的提升，它可能会在后期被模型独立解决，此时系统就不会再提供指导。这完美地适应了模型能力的动态变化。 自适应提示精炼与多阶段指导 (Adaptive Prompt Refinement with Multi-Stage Guidance): 特点: 当问题被判定为困难时，GHPO会从标准答案中抽取一部分（例如，前25%）作为“提示”加入到问题中。并且，这个提示的比例是分阶段线性增加的（论文中使用了{0.25, 0.5, 0.75}的策略）。如果模型在当前阶段的提示下依然无法解决问题，下一阶段提示的强度就会增加。 优势: 这种渐进式的指导避免了“过度引导”。它首先尝试用最少的帮助来启发模，只有在必要时才增加指导的力度。这既保证了学习信号的有效性，又为模型保留了最大的自主探索空间，防止模型对提示产生过度依赖。 与之前方法的比较优势:\n相较于标准RL方法 (如GRPO): 标准RL方法在面对奖励稀疏问题时束手无策。GHPO通过引入指导，将原本会产生零奖励的“无效样本”转化为了能够提供有效学习信号的“有价值样本”，从根本上解决了训练停滞的问题。 相较于传统课程学习 (Curriculum Learning, CL): 传统CL需要预先对整个数据集进行手动的、静态的难度排序，这不仅费时费力，而且无法适应模型在训练过程中的能力成长。GHPO的难度评估是即时的、针对模型当前状态的，因此更加自动化和智能化。 相较于样本过滤方法 (如DAPO): DAPO等方法会选择直接丢弃“太难”或“太简单”的样本。这种做法虽然也能缓解部分问题，但造成了宝贵训练数据的浪费。GHPO的核心理念是“不抛弃，不放弃”，它通过改造难题来充分利用每一个数据，因此数据效率更高。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了一系列严谨的实验来全方位验证GHPO的有效性。\n实验设计: 基础模型: 选用了Qwen2.5-Base-7B（通用基础模型）和Qwen2.5-Math-7B（数学专业模型），以验证方法的通用性。 对比方法: 设置了多个强有力的基线进行对比，包括： Qwen2.5-Base-7B: 未经微调的基础模型。 Qwen2.5-Math-7B: 经过大量数学数据SFT的专业模型。 Qwen2.5-7B-GRPO: 使用当前SOTA的RLVR方法训练的模型。 Qwen2.5-7B-GRPO-CL: 结合了传统课程学习的GRPO。 Qwen2.5-7B-GRPO-CL-H(0.5): 结合了课程学习和固定比例（50%）提示的GRPO，用于证明动态提示的优越性。 训练与评估数据: 训练: 构建了两个不同难度的训练集：Math3to5（中等难度）和NuminaMath-S（困难）。 评估: 在六个主流的公开数学推理基准上进行测试，包括AIME24, Math-500, OlympiadBench, AMC23, Minerva Math, GPQA-Diamond。 关键实验数据与结果: 在中等难度任务上的显著优势: 如Table 1所示，在使用Math3to5数据集训练后，GHPO的平均准确率（AVG）达到了44.2%，相比标准GRPO的39.8%，实现了4.4%的绝对提升。 \u0026gt; However, our proposed GHPO further advances this performance, achieving an average accuracy improvement of 4.4% over GRPO. 在挑战性任务上表现更加鲁棒: 如Table 2所示，在使用更困难的NuminaMath-S数据集训练后，GHPO的优势进一步扩大。尤其是在极具挑战性的AIME24基准上，GHPO的得分为0.163，而标准GRPO仅为0.122。这强有力地证明了GHPO在处理超出模型能力边界的问题时，其指导机制是切实有效的。 训练过程更稳定、学习更高效: 图6的训练动态曲线是本论文的点睛之笔。从图6(d)可以看出，GHPO的梯度范数（Gradient Norm）在整个训练过程中始终显著低于且平稳于GRPO。这直接表明GHPO的优化过程更加平滑和稳定，有效避免了标准RL中常见的梯度爆炸或震荡问题。 同时，图6(b)显示，GHPO的准确率奖励（Accuracy Reward）曲线从训练早期就一直领先于GRPO，说明其学习效率更高。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得进一步探索的问题和挑战: “提示”的智能化与多样化: 目前的提示是机械地截取标准答案。未来的研究可以探索更智能的提示生成策略，比如：生成关键步骤的摘要、指出常见的错误陷阱、或者以苏格拉底式对话进行启发式引导。这需要模型具备更强的元认知（meta-cognition）能力。 难度评估的精细化: 当前“全错即为难”的判断标准虽然有效，但较为粗糙。可以探索更细粒度的难度指标，例如结合模型生成答案的置信度、多次生成答案的多样性（entropy）、甚至是模型内部激活值的变化来综合判断。 从“引导”到“放手”的平滑过渡: 如何设计一个机制，让模型在逐渐掌握一类问题后，能主动减少对提示的依赖，甚至完全摆脱提示，是实现真正自主学习的关键。 跨领域泛化能力: GHPO在数学领域取得了成功，但它在代码生成、法律文书写作、科学发现等其他需要长链推理的领域的有效性，是亟待验证的重要方向。 可能催生的新技术和投资机会: AI个性化教育平台: GHPO的核心思想与个性化教育理念高度契合。可以预见，基于此类技术的AI家教或辅导系统将是巨大的投资机会。它能实时评估学生的知识掌握情况，并提供恰到好处的提示和指导，实现千人千面的自适应学习。 自动化数据增强与课程学习服务: 可以开发一个“AI训练加速器”平台，为企业提供模型微调服务。企业只需提供基础模型和数据，该平台利用GHPO等技术，自动为其生成最优的训练课程，高效地将普通模型调优成专家模型。 新一代RLHF框架: 当前的RLHF（从人类反馈中强化学习）流程复杂。GHPO的思想可以被整合进去，例如，当模型生成的多个答案都被人类标注为“差”时，自动触发一个引导机制，从而提升RLHF的效率和稳定性。这可能催生出更高效、更自动化的对齐技术。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 对高质量“过程数据”的强依赖: GHPO的有效性建立在一个关键前提上：拥有带详细解题步骤的标准答案（ground-truth solution traces）。在许多现实场景中，获取这类高质量的过程数据成本极高，甚至是不可能的（例如，在探索全新的科学问题时）。这在很大程度上限制了该方法的直接适用范围。 可能扼杀模型的创造性（Guidance Bias）: 论文虽然意识到了“过度引导”的风险，但并未深入探讨其潜在的负面影响。长期接受“标准答案”片段的引导，可能会让模型对特定解题路径产生依赖，形成思维定式，从而扼杀其探索其他、甚至更优的创新性解法的能力。这种潜在的偏见有多大，以及如何检测和缓解，是存疑的。 超参数的敏感性分析不足: 整个GHPO框架包含多个关键超参数，例如： 触发困难检测的采样次数 G 多阶段指导的提示比例 w 的具体数值（为什么是0.25, 0.5, 0.75？） 冷启动阶段的步数 N 论文并未提供这些超参数的消融实验（Ablation Study），我们无从得知模型性能对这些参数的敏感程度，以及它们是否需要针对不同任务进行繁琐的调参。 实验领域的局限性: 尽管实验设计严谨，但所有实验都严格限制在数学推理这一个领域内。虽然论文最后提到已在更强的数学模型上验证，但这仍未跳出数学领域。因此，该方法能否成功泛化到代码、逻辑、自然科学等其他推理领域的结论，需要打一个问号。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 可以重点学习的创新想法与启发: 动态反馈闭环思想: 最核心的启发是建立一个“评估-决策-行动”的动态反馈闭环。不要用静态的策略去应对动态变化的对象（成长的模型）。GHPO的精髓在于：实时评估模型的能力（通过奖励信号），根据评估结果决策是否需要干预（判断是否困难），并采取相应的行动（提供提示）。这个思想可以应用在任何需要自适应调整的系统中。 化繁为简的降维打击: 当你面对一个看似无法解决的复杂问题时，一个极其有效的策略是想办法降低问题的难度。GHPO通过提供“提示”，本质上是将一个“从0到1”的创造性问题，转化为了一个“从0.5到1”的“补全”问题。在你的工作中，当你遇到瓶颈时，可以思考是否能通过增加一些初始条件或引导信息，来让问题变得更容易入手。 善用“失败”的信号: “G次尝试全部失败”在传统方法中是一个纯粹的负面结果，但在GHPO中，它被巧妙地转化为了一个极具价值的、指导后续行动的正面信号。这启发我们，在设计系统时，要善于从失败和错误中挖掘信息，让失败本身成为系统自我优化的驱动力。 需要补充的背景知识: 强化学习核心概念: 强烈建议您深入理解策略梯度（Policy Gradient）理论，以及其现代主流实现，特别是PPO (Proximal Policy Optimization)。这是理解论文中GRPO和GHPO目标函数（公式3和4）的基石。 On-Policy vs. Off-Policy RL: 了解这两种RL范式的区别。GHPO是基于On-Policy的GRPO进行的改进。理解On-Policy方法为何天生存在样本效率问题，将帮助您更深刻地认识到GHPO的动机和价值。 LLM微调技术全景: 除了强化学习，您还需要了解SFT (Supervised Fine-Tuning)。GHPO中的“引导学习”部分，其本质就是一种即时生成的SFT。理解SFT和RL在微调LLM上的根本区别和各自优劣，才能真正领会GHPO“混合策略”的巧妙之处。 ","date":"2025-08-30T00:00:00Z","permalink":"/ghpoadaptive-guidance-for-stable-and-efficient-llm-reinforcement-learning/","title":"GHPO：ADAPTIVE GUIDANCE FOR STABLE AND EFFICIENT LLM REINFORCEMENT LEARNING"},{"content":"将一个由LLM扮演的“动态用户模拟器”引入到强化学习的训练循环中，即verl中的interactions。使用动态数据而非静态数据集。\n论文深度解读 随着大型语言模型（LLM）从单纯的文本生成器向具备行动能力的“智能体”演进，如何让模型有效地学习并使用外部工具（如API、数据库、代码解释器）来完成复杂任务，成为了前沿研究的焦点。传统的训练方法，无论是基于静态数据集的 监督微调 (Supervised Fine-Tuning, SFT)，还是在预设脚本环境中进行的 强化学习 (Reinforcement Learning, RL)，都存在一个共同的缺陷：它们无法模拟真实用户交互的高度动态性和不确定性。在现实场景中，用户可能会改变主意、补充信息、提出模糊不清的需求，这种“共同演化”的对话过程是静态训练数据难以捕捉的。\n这篇论文的核心贡献，正是提出了一个名为 MUA-RL (Multi-turn User-interacting Agent Reinforcement Learning) 的新框架，旨在解决上述问题。其最具创新性的地方在于，首次将一个由LLM扮演的“动态用户模拟器”引入到强化学习的训练循环中。这彻底改变了传统RL训练中“智能体-静态环境”的交互模式，升级为“智能体-动态用户-环境”三方实时互动的全新范式。\nMUA-RL框架的设计思路清晰且务实，主要分为两个阶段：\n冷启动阶段 (Cold-start)：在进入复杂的RL训练之前，模型首先通过一个轻量级的SFT过程，学习使用工具的基本语法和模式。为了生成高质量的训练数据，论文设计了两条数据合成管线：一条是完全由LLM模拟工具的返回结果，另一条则接入真实的 模型上下文协议 (Model Context Protocol, MCP) 服务器，以获得真实的工具执行反馈。这个阶段相当于为智能体“打基础”，使其具备初步的工具调用能力。\n强化学习阶段 (RL Phase)：这是论文的创新核心。在训练过程中，智能体（Policy LLM）不再是面对一个固定的问题脚本，而是与一个LLM模拟的用户进行多轮对话。这个“用户模拟器”会根据智能体的回复动态调整其后续的提问和行为，从而创造出一个不断变化的、更接近真实的交互环境。智能体需要通过与用户沟通来澄清意图，同时调用工具与数据库交互来解决问题。\n如论文图4(c)所示，整个训练流程（Rollout）整合了文本生成、工具调用、用户消息和工具执行结果，极大地增强了训练过程的动态性、随机性和不确定性。\n图4: 论文中展示的三种不同的训练流程。(a) 纯文本模式, (b) 带工具执行的多步模式, (c) 创新的多轮用户交互模式。\n在优化算法上，论文采用了 GRPO (Group Relative Policy Optimization)，这是一种高效的策略优化算法，无需复杂的价值函数评估，适合大规模训练。更值得称道的是其 奖励机制 (Reward Mechanism) 的设计。论文摒弃了复杂的、分步式的奖励函数（比如奖励正确的工具名、正确的参数格式等），而是采用了极为简洁的 稀疏奖励 (Sparse Reward)：只有当智能体最终成功完成任务时，奖励为1，否则为0。这种设计的精妙之处在于：\n增强对话鲁棒性：它不限制智能体达成目标的具体路径，无论是通过何种对话策略或工具调用顺序，只要最终结果正确即可，这鼓励了行为模式的多样性。 避免奖励黑客 (Reward Hacking)：智能体无法通过取巧（如模仿某种特定的输出格式）来骗取奖励，必须真正解决问题，从而引导模型学习更通用、更鲁棒的行为策略。 实验结果充分证明了MUA-RL框架的有效性。在使用Qwen3系列模型（8B, 14B, 32B）的实验中，经过MUA-RL训练的模型在多个主流的多轮工具使用基准测试（如TAU2-Bench, BFCL-V3, ACEBench Agent）上，性能均显著优于其原始模型和仅经过冷启动训练的模型。尤其引人注目的是，MUA-RL-32B 作为一个320亿参数的模型，在部分任务上的表现甚至能够媲美或超越像 DeepSeek-V3 (数百亿参数) 和 Qwen3-235B-A22B (2350亿参数) 这样体量远大于它的模型。例如，在TAU2 Retail任务上，MUA-RL-32B取得了67.3的得分，而DeepSeek-V3-0324为64.7，Qwen3-235B-A22B为64.9。这说明MUA-RL不仅提升了模型的性能，更重要的是提升了模型的 学习效率和泛化能力，使其在面对复杂动态环境时表现得更为出色和稳健。\n总而言之，MUA-RL框架为训练更强大、更实用的AI智能体提供了一个可扩展、高效且可泛化的新范式。它通过在RL中引入动态用户模拟，成功地弥合了现有训练方法与真实世界交互之间的鸿沟，是推动Agentic AI发展的重要一步。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是提升大型语言模型在动态、多轮交互场景下作为智能体（Agent）使用工具的能力。\n解决的实际问题： 解决的是当前AI智能体训练方法中的一个关键缺陷。现有方法（无论是SFT还是传统的RL）大多依赖静态、预设的脚本化数据进行训练。然而，现实世界中的人机交互是动态且不可预测的。用户可能会：\n逐步提供信息：用户不会一次性给出所有必需信息。 改变需求：在对话过程中，用户可能会修改最初的目标。 表达模糊：用户的指令往往不够精确，需要智能体通过追问来澄清。 \u0026gt; 正如论文引言中所指出的：“这种交互式和共同演化的动态在现有的RL训练框架中被忽视了，在实践中很大程度上仍未被探索。” (This interactive and co-evolutionary dynamic is currently overlooked in existing RL training frameworks and remains largely unexplored in practice.) 行业意义： 这个问题是决定AI智能体能否从实验室走向大规模实际应用（如智能客服、个人助理、自动化软件操作等）的关键瓶颈。如果智能体只能处理结构化的、单向的指令，它的实用价值将大打折扣。能够处理动态交互的智能体，意味着：\n更强的实用性：能真正部署在复杂的真实世界场景中，处理来自普通用户的、非结构化的请求。 更好的用户体验：用户可以像与真人沟通一样与智能体交互，体验更自然、更流畅。 推动Agentic AI商业化：解决了这个核心问题，将极大地加速AI智能体在各个行业的落地和商业化进程，是实现通用人工智能（AGI）道路上的重要一步。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文提出的核心方法是 MUA-RL框架，它并非一个全新的模型结构，而是一个创新的 训练范式。其相较于此前方法的特点和优势如下：\n核心创新：引入LLM模拟的动态用户 之前的方法：RL训练通常在一个固定的环境中进行，智能体根据预设的任务脚本（scripted queries）执行动作并获得奖励。环境是被动的，不会“反过来”与智能体交互。 MUA-RL的方法：在RL的rollout（模拟执行）过程中，引入了一个由LLM驱动的用户模拟器。这个模拟器会根据智能体的回复动态生成新的用户消息。这创建了一个闭环的、共同演化 (co-evolutionary) 的训练环境。 优势：这种方法使得智能体能够在训练中就接触到接近真实交互的动态变化，学会处理意图澄清、信息追问、错误纠正等复杂对话策略，而不仅仅是机械地调用工具。 两阶段训练流程：冷启动 + 强化学习 之前的方法：一些工作可能直接在预训练模型上进行RL，或者只依赖SFT。直接RL可能因为探索空间过大而难以收敛；纯SFT则泛化能力有限。 MUA-RL的方法：采用“SFT冷启动 + RL精调”的两阶段策略。 冷启动 (Cold-start)：通过一个轻量级的SFT，让模型先掌握工具调用的基本语法和流程。如论文3.2节所述，数据来源多样，包括LLM模拟和真实MCP服务器执行，保证了基础能力的扎实。 强化学习 (RL)：在冷启动模型的基础上，利用动态用户交互进行RL，专注于提升策略的鲁棒性和泛化能力。 优势：这种分阶段的策略非常务实。SFT保证了训练的稳定性和基础性能，RL则在此基础上进行“拔高”，专注于学习更高级的动态交互策略，提升了整体训练的效率和效果。论文的消融研究 (Ablation Study)（见表3）也证明了这一点：去掉冷启动或去掉RL阶段都会导致性能显著下降。 简洁而高效的奖励机制 之前的方法：很多Agent RL研究试图设计复杂的奖励函数，比如对正确的工具名称、参数、甚至思考链（Chain-of-Thought）的中间步骤进行奖励，这很容易导致“奖励黑客”问题。 MUA-RL的方法：采用极简的二元稀疏奖励：任务成功则奖1，失败则为0。 优势： \u0026gt; * 鲁棒性 (Robustness to dialogue variation)：不限定智能体达成目标的具体方式，鼓励多样化的解决方案。 \u0026gt; * 避免过拟合 (Mitigation of reward hacking)：智能体必须专注于完成最终任务，而不是学习如何迎合奖励函数，从而学到更通用的能力。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文通过一系列全面的实验来验证MUA-RL的有效性，实验设计严谨，结果令人信服。\n实验设计：\n基础模型：选用Qwen3系列的8B、14B、32B模型，目的是为了观察MUA-RL方法在不同模型规模下的普适性和扩展性。 对比对象： 模型自身基线：比较了原始模型 (Non-thinking)、仅经过冷启动训练的模型 (Cold-start) 和 完整MUA-RL训练的模型，以证明MUA-RL中每个阶段的贡献。 业界顶尖模型：与 GPT-4.1, DeepSeek-V3, Qwen3-235B-A22B 等更大、更强的闭源或开源模型进行比较，以展示MUA-RL的性能水平。 评测基准：选用了四个业界公认的、具有代表性的多轮工具使用基准： TAU1-Bench \u0026amp; TAU2-Bench：模拟零售和航空领域的真实多轮交互，TAU2还引入了更复杂的用户与智能体双向控制场景。 BFCL-V3 Multi Turn：专注于评估在信息缺失、功能不全等挑战性场景下的工具调用能力。 ACEBench Agent：涵盖了多个真实世界的应用场景，如订票、外卖等。 评估指标：主要使用任务成功率（Accuracy）或任务完成率（Task Completion Rate, TCR），确保评估的客观性。 实验数据和结果： 实验结果清晰地展示了MUA-RL的优越性。\n显著超越自身基线：在所有基准和模型规模上，MUA-RL训练后的模型都远超其原始版本和冷启动版本。 \u0026gt; 例如，在TAU1-Bench的Retail任务上（见表1），Qwen3-32B原始模型得分为47.6，冷启动后提升至58.9，而经过完整的MUA-RL训练后，得分飙升至72.6。\n媲美甚至超越更大规模的模型：这是最亮眼的结果之一，体现了MUA-RL的效率。 \u0026gt; 在TAU2-Bench的Airline任务上（见表1），MUA-RL-32B（32B参数）取得了45.4的得分，超过了GPT-4o-2024-11-20的46.9，也高于DeepSeek-V3-0324（37.0）和Qwen3-235B-A22B（36.0）。这表明通过更先进的训练方法，中等规模的模型可以达到甚至超越参数量数倍于自身的大模型。\n卓越的泛化能力： \u0026gt; 论文在TAU2 Telecom这个更具挑战性的领域进行了测试。结果显示，冷启动模型因为过拟合了训练数据中的模式，表现甚至会下降。而MUA-RL模型则表现出强大的泛化能力，性能稳定领先。例如，MUA-RL-14B在TAU2 Telecom上的TCR达到了54.3%，远高于其冷启动版本的32.9%。\nModel TAU2 Retail TAU2 Airline TAU2 Telecom ACEBench Agent Qwen3-32B Non-thinking 50.2 23.5 24.8 72.5 Qwen3-32B Cold-start 58.2 31.1 19.3 76.6 MUA-RL-32B 67.3 45.4 28.3 82.5 DeepSeek-V3-0324 64.7 37.0 32.9 74.2 GPT-4.1 70.2 53.0 38.9 86.7 上表根据论文图1和表1、2数据整理，直观展示了MUA-RL-32B相较于其基线以及其他SOTA模型的性能。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这个研究方向前景广阔，但挑战与机遇并存。\n值得探索的问题和挑战： 用户模拟器的多样性与真实性：当前的LLM用户模拟器虽然动态，但其行为模式仍可能受限于其自身的训练数据。如何创造出能模拟更广泛人类行为（如非理性、情绪化、欺骗性）的模拟器，是一个巨大挑战。 开放域与长时程任务：论文中的任务仍局限于特定领域。如何将此框架扩展到需要长期记忆、跨领域知识和工具的开放式任务（如“帮我规划一个为期一周的欧洲旅行”），是未来的重要方向。 多模态交互：未来的智能体需要处理语音、图像等多种输入。如何将这种用户交互式RL框架扩展到多模态场景，是一个复杂但必要的研究课题。 奖励机制的精细化：对于没有明确“成功/失败”终点的探索性或创造性任务，二元奖励不再适用。设计更精细、能反映任务部分进展的奖励函数，同时又要避免奖励黑客，是一个难题。 安全性与可控性：随着智能体能力增强，如何确保其行为安全、可控、符合人类价值观，防止其被恶意利用或产生有害行为，是至关重要的研究领域。 新的技术和投资机会： 智能体开发与训练平台：提供一站式服务的平台，让开发者可以方便地定义工具、创建用户模拟环境、并使用MUA-RL这类先进范式来训练和部署高性能AI智能体。 领域专用模拟环境 (Domain-Specific Simulators)：针对金融、医疗、法律、教育等垂直行业，开发高度逼真的模拟环境和用户模拟器。这些环境本身就是宝贵的资产，可以用来训练和验证行业AI智能体。 新一代AI应用：基于这种强大智能体的全新应用，例如能够自主完成软件开发任务的“AI程序员”、能够进行深度市场分析和自动交易的“AI金融分析师”、以及能够提供个性化全程服务的“AI旅行管家”。 Agent 安全与对齐解决方案：随着智能体变得更加自主，专门提供Agent安全评估、漏洞检测、行为对齐和监控服务的公司将迎来巨大需求。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 从批判性角度审视，论文虽然出色，但仍存在一些潜在的局限和待验证之处：\n缺乏真实人类评估 (Lack of Human Evaluation)：这是本文最大的缺失。所有实验结果均基于与其他LLM（作为评测器或用户模拟器）的交互。LLM模拟的用户虽然动态，但终究不是真实的人类。其行为模式、语言习惯、犯错方式都可能与真人有差异。因此，MUA-RL训练出的模型在面对真实、多样、甚至“不讲道理”的人类用户时，其性能是否依然稳健，是一个亟待验证的开放问题。 奖励机制的适用范围：简洁的二元奖励在该论文所选的、有明确完成标准的任务中非常有效。但对于更开放、没有唯一正确答案的任务（如创意写作、策略规划），这种奖励机制可能就不再适用。论文未能探讨其奖励设计在更广泛任务类型上的局限性。 计算成本问题：论文强调了GRPO算法的样本效率，但没有详细讨论整个MUA-RL框架的计算成本。在训练循环中同时运行一个智能体LLM和一个用户LLM，其开销想必是相当可观的。与传统的SFT或离线RL相比，其成本效益如何，论文并未提供详细分析。 对“非思维”(Non-thinking)模式的依赖：论文中的所有实验都是在“non-thinking”模式下进行的，这是一种不生成中间思考步骤的直接输出模式。虽然这能保证评估的确定性，但也回避了一个问题：MUA-RL框架是否能有效地教会模型进行更复杂的、显式的推理规划 (Reasoning and Planning)，而不仅仅是反应式的工具调用。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文充满了可以直接借鉴的创新思想。\n重点学习与启发： 核心思想：用“演练”代替“背书”：最大的启发是从静态数据投喂的训练思维，转向创建动态交互环境的训练思维。与其给模型看无数本“棋谱”（静态数据），不如让它在和一个水平不断提升的“陪练”（用户模拟器）的对弈中学习。这个思想可以应用在任何需要交互能力的AI任务上。 “冷启动 + 精调”的务实范式：在开发复杂的AI能力时，可以借鉴其两阶段方法。先通过SFT或模仿学习让模型掌握基础，再通过RL或在线学习的方式在更真实的环境中进行强化和提升。这是一个非常稳健且高效的工程实践。 极简主义的奖励设计：在设计RL奖励时，要警惕过度工程化。一个简单的、与最终任务目标强相关的稀疏奖励，往往比复杂的多阶段奖励更有效，更能引导模型学习到通用的能力。KISS (Keep It Simple, Stupid) 原则在AI训练中同样适用。 将LLM作为环境的一部分：这篇论文展示了LLM不仅可以作为Agent，还可以作为环境的一部分（用户、评判者、工具等）。在构建自己的AI系统时，可以思考如何利用LLM来创造更丰富、更动态的模拟环境。 需要补充的背景知识： 强化学习 (Reinforcement Learning)：需要深入理解核心概念，如策略（Policy）、奖励（Reward）、状态-动作价值（Q-value）、策略梯度（Policy Gradient）等。 PPO (Proximal Policy Optimization)：由于论文使用的GRPO是PPO的变体，理解PPO的工作原理将非常有帮助。它是当前LLM对齐训练（如RLHF）中的主流算法。 大模型智能体 (LLM Agents)：了解主流的Agent框架，如 ReAct (Reason + Act)、Function Calling（函数调用）机制等，这能帮助你更好地理解论文中“工具使用”的具体含义。 LLM训练范式：清晰地辨别 SFT (Supervised Fine-Tuning)、RLHF (Reinforcement Learning from Human Feedback) 和本文提出的 RLAIF (Reinforcement Learning from AI Feedback) 之间的区别和联系。本文的方法本质上是一种自动化的、无需人类标注的RLAIF。 ","date":"2025-08-30T00:00:00Z","permalink":"/mua-rlmulti-turn-user-interacting-agent-reinforcement-learning-for-agentic-tool-use/","title":"MUA-RL：MULTI-TURN USER-INTERACTING AGENT REINFORCEMENT LEARNING FOR AGENTIC TOOL USE"},{"content":"之前的观点认为，RL只是激活预训练学到的知识，而并非学习到完全不知道的知识。而本文证明了使用工具集成推理（TIR）可以使得模型达到从未达到过的状态。读完我感觉TIR的思想和规划与执行分离的思想很类似，都是大模型负责规划或者推理，而真正的执行可以交给另一个大模型或者工具调用。术业有专攻。但是目前也有一些将所有能力集中在一个模型上的，类似Chain-of-Agents：End-to-End Agent Foundation Models via Multi-Agent Distillation and Agentic RL。\nimage.png 论文深度解读 这篇论文的核心论点是：工具集成从根本上扩展了大型语言模型的能力边界，使其能够解决纯文本模型无法或极难解决的问题。作者通过理论证明、算法创新和详尽的实验，系统地阐述了这一观点。\n首先，在理论层面，论文直面了一个关键问题：传统的强化学习方法在多大程度上能提升LLM的推理能力？论文引用了前人（Wu et al., 2017）提出的“无形 leash（invisible leash）”理论，该理论指出，标准的强化学习（如RLVR）本质上只是在模型已有的、可能生成（即概率不为零）的答案轨迹上重新分配概率权重。这意味着，如果一个正确的推理路径在模型的基础认知中完全不存在（概率为零），那么强化学习就永远无法“教会”模型这条新路径。这就像用绳子遛狗，狗可以在绳子范围内自由活动，但永远无法超越绳子的长度。\nTheorem 3.3 (Support Preservation under RLVR (from [17])). Let πθ(y|x) be an RLVR-trained policy distribution initialized from a base model with distribution q(y|x). For any prompt x, the support of the trained policy is a subset of the support of the base model: supp(πθ) ⊆ supp(q) This implies that if q(y|x) = 0 for a correct trajectory y, then RLVR can never discover y*.\n而这篇论文的核心理论贡献在于，它通过严格的数学证明（Theorem 3.4）指出，工具集成推理（Tool-Integrated Reasoning, TIR）能够彻底打破这条“无形的 leash”。当模型调用一个外部工具（如Python解释器）时，它引入了一个“确定性的、非语言的状态转换”。比如，模型生成代码print(123 * 456)，工具的返回56088是一个确定的、必然的结果，而不是模型基于其庞大参数概率性地“猜”出来的。这个过程允许模型进入一个全新的、之前无法到达的“状态空间”，从而严格扩张了模型的支持集（support）——即模型能够生成的所有可能轨迹的集合。这意味着，TIR模型可以探索并学会那些对于纯文本模型来说概率为零或极小，因而“不可能”的解决方案。\n为了让这个理论更具现实意义，论文还引入了“令牌效率（Token Efficiency）”和“预算下的可行支持集（Feasible Support under a Budget）”的概念。很多复杂的计算任务，如大型矩阵运算或深度递归，如果用自然语言一步步模拟，会消耗天文数字的令牌，远远超出任何现有模型的上下文窗口限制。而通过工具，这些复杂计算可以被压缩成几行代码，以极高的令牌效率完成。因此，在有限的计算预算（token budget）下，TIR模型的可行解空间远大于纯文本模型。\n其次，在算法层面，论文发现了一个在实践中非常棘手的问题。为了鼓励模型更早、更主动地使用工具，研究者很自然地会想到设计一种“奖励塑形（reward shaping）”机制，比如给提早调用代码的行为一个额外的奖励。然而，实验表明这种看似直接的方法会导致训练过程“高度不稳定”。论文深入分析了原因：在基于群体标准化优势（group normalized advantage）的算法（如GRPO）中，如果一个批次（batch）里的所有样本都回答正确，那么基础的正确性奖励（通常是+1）在标准化后会消失，导致辅助的“早鸟”奖励被不成比例地放大。这会使模型为了获得这个辅助奖励而牺牲主要任务的准确性，甚至惩罚那些虽然调用代码较晚但最终解对题的样本。\n为了解决这个问题，论文提出了一个新颖、稳定且高效的算法——优势塑造策略优化（Advantage Shaping Policy Optimization, ASPO）。ASPO的巧妙之处在于，它不直接修改奖励函数，而是在计算出基于任务正确性的标准优势函数（advantage function）值之后，再对其进行直接的、可控的“塑造”或“微调”。\n\\(A_i = A_{correct,i} + \\text{clip}(\\delta \\cdot \\frac{p_i - \\text{mean}(p)}{\\text{mean}(L)} - k \\cdot A_{correct,i}, k \\cdot A_{correct,i})\\)\n这个公式确保了对早期代码调用的激励（由\\(\\delta\\)控制）始终是一个附加的、有界的“微调”，永远不会压倒性地盖过任务本身正确与否的核心信号。实验结果（Figure 5）清晰地显示，基于奖励的方法导致训练迅速崩溃，而ASPO则保持了和基线模型几乎完全一致的稳定性和最终性能，同时显著优化了模型的工具使用行为。\n最后，在实验层面，论文的设计极其严谨和富有洞察力。通过在AIME、Omni-MATH-512等高难度数学推理基准上的对比，论文用pass@k指标（即生成k个答案中至少有一个正确的概率）无可辩驳地证明了TIR模型（基于Qwen3-8B）的性能全面且持续地优于纯文本模型（Figure 2）。例如，在Omni-MATH-512上，当k=256时，TIR模型的pass@k达到了82.0%，而纯文本模型仅为68.4%。更重要的是，论文通过一个“问题可解性流转”桑基图（Figure 3）直观地展示了，从纯文本模型切换到TIR模型，有15.4%的问题从“不可解”变为了“可解”，而反向的“能力退化”仅占1.8%，实现了巨大的净增益。\n论文最深刻的洞见之一，来自于其原创的“算法友好度（algorithmic friendliness）”分析。通过将问题按其对抽象数学洞察和直接计算的依赖程度分为5个等级，论文发现（Figure 4），即使在最不“友好”、最需要抽象推理（等级1-2）的问题上，TIR模型依然比纯文本模型有约9%的显著性能优势。这强有力地驳斥了“工具仅仅是个高级计算器”的浅显观点，证明了工具在辅助模型进行“探索与验证（Exploration and verification via code）”和“洞察到计算的转化（Insight-to-computation transformation）”等高级认知活动中的深刻价值。\n综上所述，这篇论文通过“理论突破-算法创新-实验验证”的完美闭环，系统地回答了工具集成推理的核心问题，为我们构建下一代更强大的AI系统提供了坚实的理论基础和实用的工程指导。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 本文的核心研究目标是从根本上理解为什么以及如何通过集成外部工具来提升大型语言模型的推理能力。它试图超越“工具很有用”的经验性观察，建立一个能够解释其有效性的形式化理论框架，并开发出能够稳定引导模型学习使用工具的算法。\n解决的实际问题:\n能力天花板问题: 纯文本LLM在需要精确计算、长程推理、事实核查或与外部世界交互的任务上存在固有的“能力天花板”。它们容易产生计算错误、幻觉，并且无法获取其参数化知识之外的信息。 训练不稳定性问题: 在尝试通过强化学习教会模型更有效地使用工具时（例如，鼓励早期、探索性的工具调用），现有的奖励设计方法常常导致训练过程不稳定，甚至损害模型的最终性能。 对行业发展的重要意义:\n为构建可靠的AI智能体（Agent）奠定理论基础: 随着AI从内容生成器向问题解决者（即Agent）演进，让模型可靠地使用工具（如API、数据库、搜索引擎）是实现自动化工作流、科学发现、具身智能等高级应用的前提。本文的理论解释了为什么这条路是可行的，甚至是必要的。 指导更优的Agent训练范式: ASPO算法为业界提供了一个稳定、可控地塑造Agent行为的实用方法。这意味着我们可以更精细地调整Agent的“性格”，比如让它变得更“积极探索”或更“谨慎保守”，而不用担心训练过程崩溃。 推动AI从“封闭大脑”到“开放生态”的转变: 论文的结论支持一种新的AI范式：LLM不应被视为一个万能的、封闭的“大脑”，而应成为一个核心的“推理引擎（reasoning engine）”，它能智能地将计算、检索等任务委托给专门化、高效的外部工具。这对于构建复杂的、由多个专用模型和工具组成的AI系统生态具有深远的指导意义。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文主要提出了两大创新点：一个理论框架和一个优化算法。\n思路创新：基于“支持集扩张”和“令牌效率”的理论框架 新思路: 论文首次形式化地证明了工具集成能够严格扩张（strict expansion）模型的经验支持集（empirical support）。 与之前方法的对比: 此前的研究，如 ReTool 或 ToRL，更多是展示工具集成的经验性成功，并设计RL框架来提高数据效率或扩展到复杂场景。而这篇论文则从理论上回答了“为什么”有效。它建立在“无形leash”理论之上，但做出了关键的超越：证明了工具是打破这根leash的钥匙。 特点与优势: 根本性: 它揭示了TIR与纯文本RL在能力获取机制上的根本区别——前者是“发现新大陆”（探索原本概率为零的路径），后者是“在旧大陆上重新规划”（重排已知路径的概率）。 实用性: 通过引入令牌效率和预算下的可行支持集，该理论将抽象的数学概念与模型上下文窗口这一现实瓶颈联系起来，解释了为什么即使某个问题理论上可以被纯文本模型解决，但在实践中却往往是不可行的。如下表所示（改编自论文Table 1 \u0026amp; 2），代码的表示极其紧凑，而自然语言模拟则会随着问题规模N的增长而变得冗长不堪。 任务类型 程序化方法 (Python) 自然语言推理 迭代任务 (N次) for i in range(N): check(i) 令牌成本: O(1) “第一步，我检查1…第二步，我检查2…” 令牌成本: Ω(N) 大型线性系统求解 np.linalg.solve(A, b) 令牌成本: O(1) “我们用高斯消元法，第一行乘以…加到第二行…” 令牌成本: Ω(n²) 方法创新：优势塑造策略优化 (ASPO) 新方法: ASPO是一种通过直接修改优势函数来引导模型行为的策略优化算法，旨在解决传统奖励塑形导致的不稳定性。 与之前方法的对比: 传统方法试图将引导信号（如“尽早使用工具”）直接加入奖励函数R_i。如论文所述，这在GRPO这类算法中存在致命缺陷： \u0026gt; In the common scenario where all samples in a group are correct, the primary reward signal (the constant ‘1’) is entirely eliminated by the normalization. The advantage calculation then becomes: \u0026gt; \\(A_i = \\frac{R_i - \\text{mean}(R)}{\\text{std}(R)} = \\frac{(1 + r\u0026#39;_i) - (1 + \\text{mean}(r\u0026#39;))}{\\text{std}(r\u0026#39;)} = \\frac{r\u0026#39;_i - \\text{mean}(r\u0026#39;)}{\\text{std}(r\u0026#39;)}\\) 这导致主次颠倒，辅助奖励信号被过度放大，从而破坏了训练。 特点与优势: 稳定性: ASPO通过在标准化优势值A_correct计算之后再添加一个有界的偏移量，保证了主任务信号的完整性。从Figure 5(a)可以看出，Reward-based方法在训练初期就迅速崩溃，而ASPO则保持了与基线一致的稳定。 可控性: ASPO通过超参数 δ 和 k 提供了对引导强度的精确控制，可以调整模型行为的激进程度，并且保证了引导始终是“辅助性”的，不会让模型为了满足激励而“耍小聪明”（reward hacking）。 通用性: ASPO的设计理念是通用的，可以被轻松地应用于引导其他期望的行为，例如鼓励模型生成更简洁的代码、进行更多样的探索等，为精细化Agent行为调优提供了一个强大的框架。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了一系列环环相扣的实验来系统地验证其理论和方法。\n实验设计:\n模型与数据: 实验基于 Qwen3-8B 模型，使用包含10,000个数学问题的 DAPO 数据集进行训练。评估则在三个高难度的基准上进行：AIME24, AIME25, 和 Omni-MATH-512。 对比组: 核心对比是两个模型： TIR模型: 可以在推理中调用Python解释器。 纯文本模型: 只能生成文本来解决问题。 两个模型都使用相同的基座模型和DAPO算法进行训练，以确保公平性。 评估指标: 主要使用 pass@k，它衡量模型生成 k 个答案中至少有一个正确的概率，能有效评估模型的潜在能力上限。 关键实验数据与结果:\nTIR全面超越纯文本模型: Figure 2的pass@k曲线显示，在所有三个基准上，TIR模型的性能曲线都始终且显著地高于纯文本模型，并且随着 k 的增大，两者之间的差距没有缩小。这表明TIR带来的不是简单的性能提升，而是整体能力“能级”的跃迁。 关键数据 (Table 6): 在最具挑战性的Omni-MATH-512上，当k=256时，TIR模型的pass@k为 0.8203，而纯文本模型为 0.6836，领先超过13个百分点。 TIR实现能力净扩张: Figure 3的桑基图直观地量化了能力变化。从纯文本模型到TIR模型： 能力扩张 (Capability Expansion): 15.4% 的问题从无法解决变为可以解决。 能力萎缩 (Capability Shrinkage): 仅 1.8% 的问题从可以解决变为无法解决。 这提供了TIR打破“能力天花板”的直接证据。 TIR的优势不仅限于计算密集型问题: 论文通过原创的“算法友好度”指标，将问题分为G1到G5五个等级（G5最易于算法化）。 关键发现 (Figure 4): 即使在最依赖抽象推理的G1和G2组，TIR模型依然稳定地优于纯文本模型。在G1组，当k=256时，TIR的准确率约为0.6，而纯文本模型约为0.51，优势约为9%。这证明了工具不仅仅是计算器，更是模型进行探索和验证的“认知伙伴”。 ASPO的有效性和稳定性: 论文通过对比实验验证了ASPO算法。 稳定性 (Figure 5): ASPO在整个训练过程中保持了与基线模型几乎相同的训练准确率和测试准确率，而朴素的基于奖励的方法则迅速失效。 行为引导的有效性 (Figure 6): ASPO成功地改变了模型的行为模式。例如，平均首次调用代码的位置从基线的约4000个token提前到了1000个token（Figure 6b），平均代码调用次数从1.3次增加到了3.3次（Figure 6e），且没有牺牲最终的解题正确率。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战: 复杂工具的组合与编排: 本文主要关注单一的确定性工具（Python）。当面临多个、非确定性（如Web搜索）、有副作用（如API调用）的工具时，如何进行有效的规划、容错和信用分配（credit assignment）是一个巨大的挑战。 工具创建与学习: 未来的Agent不仅应该能使用现有工具，还应该能根据任务需求动态地创建新工具（例如，编写一个新的函数并复用）。 安全与对齐: 如何确保Agent在使用强大工具（如文件系统、网络访问）时的行为是安全、可控且符合人类意图的？这是一个紧迫的研究方向。 长程推理与记忆: 对于需要跨越数万甚至数十万token的长程、多步任务，如何结合外部记忆工具来克服LLM的上下文窗口限制，并保持推理的连贯性。 多模态工具集成: 将工具的概念从文本扩展到多模态，例如使用图像编辑工具、物理模拟器等，将是具身智能和内容创作领域的关键。 可能催生的新技术和投资机会: Agentic PaaS平台 (Agent as a Service): 提供一个集成了开发、调试、监控和部署AI Agent所需的全套工具和基础设施的平台。这将成为AI应用开发的新范式。 专业领域工具API生态: 围绕金融、生物、材料科学等特定领域，开发和提供高质量、可靠的工具API，让LLM Agent能够解决该领域的专业问题，这将是一个巨大的市场。 Agent安全与防火墙: 专门为监控和限制AI Agent行为而设计的安全解决方案，防止其被滥用或产生有害行为。 新一代硬件: 为大规模Agent并行运行和低延迟工具调用而优化的硬件和计算架构，可能会成为新的增长点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但仍有一些局限性和值得商榷之处：\n实验范围的局限性: 单一工具类型: 论文的实证结论完全基于Python解释器这一个确定性、无副作用的工具。其在附录中对搜索、记忆等其他工具的泛化讨论是“非正式的（informal）”，缺乏实验支撑。这些工具（尤其是搜索）具有随机性和不确定性，其集成机制可能远比本文讨论的复杂。 单一任务领域: 实验集中在数学推理领域。虽然这是一个测试推理能力的绝佳场景，但其结论能否直接推广到对话、创意写作、规划等其他领域尚需验证。 模型和数据的单一性: 所有实验都基于Qwen3-8B这一个模型。研究结果在不同规模（如百亿、千亿参数）或不同架构（如MoE）的模型上是否依然成立，是一个开放问题。 训练数据量相对较小（10,000个样本），虽然论文声称其目的在于理解机制而非刷榜，但这可能会影响结论的泛化性。 评估方法的潜在主观性: 创新的“算法友好度”指标依赖于调用Gemini 2.5 Pro API进行分类。尽管有明确的评分标准（rubric），但这引入了另一个黑箱模型，其分类结果可能存在不一致性或偏见，为分析带来了一定的不确定性。 对“失败案例”的分析不足: 论文重点展示了TIR的成功，但对于那1.8%的“能力萎缩”案例——即纯文本模型能解决但TIR模型反而失败的问题——缺乏深入的定性分析。理解这些失败案例对于全面认识TIR的代价和风险至关重要。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发: 思维范式的转变: 最大的启发是将LLM视为一个推理规划器，而不是一个全能执行者。在设计解决方案时，主动思考：“哪些部分最适合LLM的抽象推理能力？哪些部分应该委托给确定性的外部工具？”这种分解式思维是构建复杂AI应用的关键。 ASPO算法的借鉴: 如果您正在从事Agent的微调工作，ASPO提供了一个非常实用且强大的框架。当您想引导Agent展现某种特定行为（如更具探索性、输出更简洁、优先使用某个工具）而又不希望干扰主任务的学习时，可以尝试直接“塑造”其优势函数，而不是去冒险修改复杂的奖励函数。 “令牌效率”的评估视角: 在评估一个Agent或工具的价值时，不仅仅看它能否完成任务，更要看它的令牌效率。一个能用几行代码解决问题的工具，远比一个需要数千token自然语言对话才能解决问题的工具更有价值。这对于优化成本和性能至关重要。 “认知伙伴”的隐喻: 重新审视工具的角色。工具不仅是“计算器”，更可以是模型的“沙盒”和“验证器”。在需要探索和试错的开放性问题上，鼓励模型通过编写简短代码来测试假设，是一种极具潜力的解决策略。 需要补充的背景知识: 强化学习基础: 尤其是策略梯度方法（Policy Gradient）、PPO (Proximal Policy Optimization) 算法，因为本文的DAPO和GRPO都源于此。理解“优势函数（Advantage Function）”的概念对掌握ASPO至关重要。 工具学习（Tool Learning）的代表性工作: 建议阅读如 Toolformer、ReAct (Reasoning and Acting) 等开创性论文，了解工具学习领域的基本框架和发展脉络。 “The invisible leash”论文: 本文的理论根基是建立在 Wu et al. (2025) 的 “The invisible leash: Why RLVR may not escape its origin” 之上。阅读原文能帮助您更深刻地理解“支持集”和“能力天花板”的理论背景。 大模型推理机制: 了解如思维链 (Chain-of-Thought, CoT)、自洽性 (Self-Consistency) 等提升LLM推理能力的技术，有助于理解TIR是在何种基础上进行的增强。 ","date":"2025-08-30T00:00:00Z","permalink":"/understanding-tool-integrated-reasoning/","title":"Understanding Tool-Integrated Reasoning"},{"content":"结合了sft和rl，让sft的数据引导Rl的过程。利用对比学习，拉近cot和sft中cot的距离作为目标函数。并与rl目标函数加权得到最终的目标函数。\nimage.png 论文深度解读 这篇论文的核心目标是解决当前大语言模型（LLM）在提升推理能力（Reasoning Capability） 方面遇到的一个关键瓶颈。目前，主流的两种增强推理能力的方法——监督微调（Supervised Fine-Tuning, SFT） 和强化学习微调（Reinforcement Learning-based Fine-Tuning, RL）——各自存在明显的短板，形成了一种“鱼与熊掌不可兼得”的困境。\nSFT 的困境：SFT 方法通常使用包含“问题-思路链-答案”三元组的数据进行训练。这种方法的优点是稳定、直接，但其致命弱点在于泛化能力有限。因为它强迫模型去模仿数据集中唯一给定的思路链（Chain-of-Thought, CoT），但一个复杂问题往往存在多种有效的解题路径。过度依赖单一路径会导致模型思维僵化，遇到略有变化的问题就可能“失灵”。论文中提到： \u0026gt; SFT-based reasoning enhancement approaches only exploit a single annotated CoT for each question within the training dataset. … Hence, conducting SFT with only a single annotated CoT in the training dataset may limit the generalization capability of LLMs.\nRL 的困境：以 ReFT 为代表的 RL 方法试图通过让模型在环境中自由探索（即生成不同的 CoT）并根据最终答案的正确性给予奖励，来克服 SFT 的泛化问题。这种方法理论上能探索更多样的推理路径。然而，它也带来了两大新问题：第一，训练过程极不稳定，容易发生“模型崩溃（model collapse）”，即模型在探索过程中性能急剧下降，甚至不如微调之前。实验部分的图3和图4清晰地展示了 ReFT 在训练过程中准确率的剧烈波动甚至断崖式下跌。第二，这种方法在训练时完全忽略了数据集中已有的、由人类专家或更强模型标注的高质量 CoT，这无疑是一种巨大的资源浪费。\n面对这一困境，论文的作者们提出了一个非常精妙的解决方案——CARFT。CARFT 的核心思想不再是“二选一”，而是将 SFT 的高质量数据优势与 RL 的探索能力优雅地结合起来。它实现这一点的关键技术，就是对比学习（Contrastive Learning）。\n让我用一个通俗的比喻来解释这个创新点：\n假设我们教一个学生解数学题。SFT 就像是让学生把一本标准答案的解题步骤背得滚瓜烂熟，他可能只会这一种方法。RL 就像是让学生自己随便尝试，做对了就表扬，做错了就批评，但他可能会陷入一些奇怪的解题误区，甚至丧失信心（模型崩溃）。\n而 CARFT 的做法是：我们既给了学生标准答案（annotated CoT），也鼓励他自己尝试（on-policy sampled CoT）。然后我们告诉他：“你这次自己想出的解题方法，虽然步骤和标准答案不一样，但最终得出了正确答案，说明你的思路和标准答案在本质上是相通的、高质量的。所以，你要学习让这两种思路在你的‘大脑’（模型的表示空间）里变得更‘接近’。”\n这个“让思路更接近”的过程，就是通过对比学习实现的。具体来说，CARFT 引入了几个关键模块：\nCoT 嵌入表示（CoT Embeddings）：为了比较两个不同的 CoT（比如一个是标注的，一个是模型自己生成的），CARFT 设计了一种方法，将一整个 CoT 序列压缩成一个固定维度的向量（embedding）。这个向量就代表了这个 CoT 的“语义核心”。实现方式是通过一个加权求和，权重来自于模型对每一步状态的价值判断（value function），非常巧妙。\n对比信号构建（Masked Contrastive Signal Construction）：这是 CARFT 的灵魂。当模型生成一个 CoT 并得到正确答案时，对比学习的目标就是拉近这个生成的 CoT 嵌入与数据集中对应问题的标准 CoT 嵌入的距离，同时推远与其他不相关问题的 CoT 嵌入的距离。这就像一个“正向激励”，鼓励模型生成的正确推理路径在语义上向高质量的标注路径看齐。\n嵌入增强的部分奖励（Embedding-enhanced Partial Reward）：这是另一个锦上添花的创新。传统的 RL 奖励很稀疏，通常是“答对得1分，答错得0分”。ReFT 改进了一点，对于能提取出数字但答案错误的 CoT 给予一个固定的低分（如0.1）。CARFT 则更进一步，它设计的奖励与生成的 CoT 和标准 CoT 的嵌入相似度挂钩。公式为 r(x, y) = (e_annotated, e_rollout) × 0.1 + 0.2。这意味着，即使最终答案错了，但如果推理过程（CoT）与标准答案的推理过程在语义上很相似，模型依然能获得一个较高的部分奖励（最高可达0.3）。这种密集且有信息量的奖励信号极大地稳定了 RL 的训练过程，有效避免了模型崩溃。\n通过这一系列设计，CARFT 成功地利用高质量的标注数据来引导和稳定强化学习的探索过程，既避免了 SFT 的死板，又克服了纯 RL 的不稳定和资源浪费问题，最终在实验中取得了显著的领先。例如，在 GSM8K 数据集上，使用 Qwen2.5-7B 模型，CARFT 的准确率达到了 84.31%，远超 SFT 的 80.67% 和 ReFT 的 66.11%（后者发生了严重的模型崩溃），充分证明了其方法的优越性。\n接下来，我们来详细解答您的六个问题。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是提升大语言模型（LLM）的数学或逻辑推理能力，并解决当前主流微调方法（SFT 和 RL）中存在的根本性矛盾。\n具体解决的实际问题：\nSFT 的泛化能力差：SFT 强迫模型模仿单一的、固定的推理路径（Annotated CoT），导致模型在新问题上举一反三的能力不足。 RL 的训练不稳定与效率低下：基于 RL 的方法（如 ReFT）在自由探索推理路径时，由于缺少有效引导，极易出现性能急剧下降的“模型崩溃”现象。同时，这些方法在训练时完全抛弃了数据集中宝贵的人工标注 CoT，造成了信息和资源的浪费。 对行业发展的重要意义： 推理能力是衡量 LLM 智能水平的“试金石”，也是其从“语言模仿”走向“问题解决”的关键一步。提升推理能力将直接赋能 LLM 在更广泛和更关键领域的应用，例如：\n科学研究：辅助科学家进行数据分析、理论推导和实验设计。 金融分析：执行复杂的量化分析、风险评估和市场预测。 软件工程：自动编写和调试复杂代码、设计算法。 教育辅导：为学生提供个性化、步骤清晰的解题指导。 因此，开发出一种既能保证训练稳定、又能提升模型泛化能力的微调方法，对于推动 LLM 技术的实用化和可靠性，具有至关重要的战略意义。CARFT 的探索为此提供了一个非常有前景的方向。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心新思路： 本文最核心的思路是利用对比学习（Contrastive Learning）作为桥梁，融合监督微调（SFT）的数据优势和强化学习（RL）的探索优势。它不再将两者视为对立或独立的阶段，而是让 SFT 的数据（Annotated CoT）在 RL 阶段持续发挥“引导”和“稳定器”的作用。\n具体新方法/模型（CARFT）： CARFT 框架包含以下几个关键创新点：\n统一的 CoT 嵌入表示：如前所述，它提出了一种将变长的 CoT 序列转换为固定维度嵌入向量的方法。这是进行对比学习的基础，使得不同形式但语义相近的推理路径可以被量化比较。\n对比反馈机制：这是 CARFT 的核心。它构建了一个对比损失函数（基于 InfoNCE），其目标是： \u0026gt; 在嵌入空间中，拉近由模型生成且答案正确的 CoT（on-policy sampled CoT）与数据集中预先标注的 CoT（annotated CoT）之间的距离。\n这个机制的巧妙之处在于，它并不要求模型生成与标准答案一模一样的 CoT，而是鼓励其生成语义上等价的正确路径，从而极大地提升了模型的泛化能力。\n嵌入增强的部分奖励机制：相比于 ReFT 给予错误的、但可提取数字的 CoT 一个固定的0.1分奖励，CARFT 的奖励机制是动态的、信息丰富的。奖励大小直接与生成 CoT 和标准 CoT 的嵌入相似度正相关。\n特点与优势： | 特点 | CARFT | SFT (传统监督微调) | ReFT (传统强化学习微调) | | :— | :— | :— | :— | | 数据利用 | 充分利用 Annotated CoT 和模型自己生成的 CoT | 仅利用 Annotated CoT | 仅利用模型自己生成的 CoT | | 训练稳定性 | 高，通过对比信号和密集奖励来稳定训练 | 高，但容易过拟合 | 低，极易发生模型崩溃 | | 泛化能力 | 强，鼓励探索多样的、语义正确的推理路径 | 弱，受限于单一标注路径 | 理论上强，但常因不稳定而无法实现 | | 奖励机制 | 密集且信息丰富（与嵌入相似度相关） | 无 | 稀疏（0/1奖励或固定的部分奖励） |\n### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 数据集：使用了两个经典的数学推理公开数据集 SVAMP 和 GSM8K。 基础模型：选用了两种不同架构和能力的开源模型：CodeLlama-7B 和 Qwen2.5-7B-Instruct，以验证方法的普适性。 基线方法（Baselines）：选取了三种代表性的方法进行对比： SFT：标准的监督微调。 ReFT：代表性的、先进的强化学习微调方法。 Dr.GRPO：另一种先进的 RL 方法，作为性能和效率的参考。 评估指标：最终答案的准确率（Accuracy）。 消融研究（Ablation Study）：为了验证 CARFT 各个组件的有效性，论文还设计了消融实验，例如： 比较仅使用正向对比信号和负向对比信号的效果。 测试不同超参数（如对比损失的系数 c）对结果的影响，以验证方法的鲁棒性。 对比加入“嵌入增强的部分奖励”前后的训练稳定性和最终性能。 实验数据与结果： 实验结果有力地证明了 CARFT 的优越性。 主要性能对比： Table 3 是核心的证据，以下是其简化版本，展示了 CARFT 带来的显著提升： | 模型 | 方法 | SVAMP 准确率 | GSM8K 准确率 | 平均准确率 | | :— | :— | :— | :— | :— | | CodeLlama-7B | SFT | 62.3% | 43.82% | 53.06% | | | ReFT | 62.5% | 50.27% | 56.39% | | | CARFT | 64.8% | 50.95% | 57.88% | | Qwen2.5-Instruct-7B | SFT | 86.9% | 80.67% | 83.79% | | | ReFT | 85.9% | 66.11% | 76.01% | | | CARFT | 88.0% | 84.31% | 86.16% |\n从表中可以清晰地看到，CARFT 在所有模型和数据集上都取得了最佳性能。尤其在 Qwen2.5-Instruct-7B + GSM8K 的组合上，CARFT 将准确率从 SFT 的 80.67% 提升至 84.31%，而 ReFT 在此场景下甚至出现了严重的性能衰退（模型崩溃），跌至 66.11%。\n训练稳定性： 论文中的 Figure 3 和 Figure 4 直观地展示了训练过程中的准确率曲线。ReFT 的曲线出现了剧烈的震荡和断崖式的下跌，而 CARFT 的曲线则平稳上升，表现出卓越的训练稳定性。这证明了 CARFT 的对比反馈和奖励机制确实起到了稳定训练的作用。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 值得探索的问题和挑战： 多模态推理：当前工作主要集中在文本推理。如何将 CARFT 的思想扩展到需要结合图像、表格、代码等多模态信息的复杂推理任务，是一个重要的方向。例如，让模型解释图表数据或理解代码执行逻辑。 更复杂的推理类型：数学应用题主要考验逻辑演绎。对于需要归纳、溯因、类比等更高级认知能力的推理任务，当前的 CoT 范式是否足够？CARFT 的对比学习框架是否依然有效？ 对标注数据的依赖：CARFT 仍然需要高质量的 Annotated CoT 作为“锚点”。如何降低对这种昂贵数据的依赖？例如，能否让模型在少量高质量 CoT 的引导下，通过自我迭代和对比，生成更多样化且高质量的伪标注数据（Self-Correction / Self-Improvement）？ 可解释性和可信度：CARFT 提升了结果的准确性，但推理过程本身的可信度如何保证？未来的研究需要关注如何确保模型生成的 CoT 不仅结果正确，而且过程合理、没有逻辑捷径（即所谓的 “reward hacking”）。 效率与扩展性：计算 CoT 嵌入和对比损失会带来额外的计算开销。在大规模模型（如千亿、万亿参数）和海量数据上，如何优化算法效率，使其能够低成本地部署，是一个工程和算法上的双重挑战。 可能催生的新技术和投资机会： 技术机会： 语义奖励模型（Semantic Reward Models）：CARFT 的嵌入增强奖励机制是“语义奖励”的一个雏形。未来可能会出现更强大的奖励模型，它们不再仅仅判断答案对错，而是能够从语义层面评估整个推理过程的质量、创新性和逻辑严谨性。 下一代对齐技术：这套方法论可以被广泛用于模型对齐（Alignment）领域，超越简单的偏好学习（DPO），实现对模型行为和思维模式更深层次的引导。 自动化数据增强流水线：基于 CARFT 的思想，可以构建一套系统，自动地让模型生成多样化的 CoT，并使用一个判别器（或对比学习）来筛选出高质量的合成数据，反哺到训练集中，形成一个自增强的飞轮。 投资机会： 专注于模型推理能力提升的初创公司：提供专门针对金融、法律、科研等垂直领域的推理增强微调服务。 高质量、多样化推理数据集的提供商：随着业界对推理能力越来越重视，包含多种正确解题路径的“多 CoT”数据集将变得非常有价值。 模型评估与可信度（Trust \u0026amp; Safety）平台：提供工具来验证 LLM 推理过程的逻辑正确性，而不仅仅是最终答案的准确性，这将成为企业级应用的关键需求。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管 CARFT 取得了令人印象深刻的成果，但从批判性的角度审视，仍然存在一些潜在的不足和值得商榷之处： * 负向信号的作用不明确：论文虽然设计了基于最长公共子序列（LCS）的负向对比信号（用于处理错误答案的 CoT），但在消融实验（Figure 5）中，其带来的性能提升（0.4%）远小于正向信号（2.30%），甚至单独使用时效果不佳。论文对此没有深入分析原因，负向信号的设计可能还有很大的优化空间。 * 对 Annotated CoT 质量的敏感性：整个框架的有效性高度依赖于作为“锚点”的 Annotated CoT 的质量。如果标注的 CoT 本身存在逻辑错误、或者不是最优路径，CARFT 可能会将模型的探索方向“带偏”。论文没有讨论在这种“脏数据”或“次优数据”下的鲁棒性。 * 潜在的思维模式固化风险：虽然 CARFT 鼓励多样的 CoT，但对比学习的本质是“拉近”与标注 CoT 的距离。这是否会在潜移默化中使得所有正确的推理路径都趋向于与标注 CoT 的“语义风格”相似，从而在更高维度上限制了模型的创新性？这需要更长期的观察和实验。 * 实验范围的局限性：实验仅限于7B参数级别的模型和数学应用题。该方法在更大规模的模型（如70B, 100B+）上是否依然有效且高效？在代码生成、故事创作等其他需要长链推理的任务上表现如何？这些都有待进一步验证。 * “模型崩溃”的根源分析不足：论文观察到了 ReFT 的模型崩溃现象，并用 CARFT 成功解决了它。但对于模型崩溃产生的根本原因（例如是梯度爆炸、价值函数估计错误还是探索策略问题）没有进行更深入的理论或实验分析，这使得其贡献更偏向于一个有效的“工程解”，而非对问题的深刻洞察。\n### 6. 我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发： “融合”而非“替代”的思维范式：在面对两种各有优劣的技术路线时，最优雅的创新往往不是发明一个全新的东西去替代它们，而是找到一个巧妙的机制将它们的优点结合起来。CARFT 用对比学习作为粘合剂，完美地融合了 SFT 和 RL，这是一个极具启发性的方法论。 用“表示学习”解决“行为引导”问题：传统 RL 的奖励是标量，信息量低。CARFT 通过引入 CoT 的嵌入表示，将对模型行为的引导问题，转化为了一个在表示空间中“度量距离”的问题。这种将稀疏的行为信号转化为密集的语义信号的思路，可以应用在非常多的领域。 数据的高阶利用：一份高质量的标注数据，其价值远不止于监督学习。CARFT 教会我们，可以将其用作探索的“灯塔”、奖励的“标尺”、稳定训练的“锚点”。在未来的工作中，应思考如何更创造性地压榨标注数据的价值。 重视训练过程的稳定性：最终性能指标固然重要，但稳定、可复现的训练过程同样关键。CARFT 对稳定性的关注和改进（如密集奖励机制）是其成功的关键因素之一，这在实际工程应用中尤为重要。 需要补充的背景知识： 强化学习基础，特别是 PPO 算法：深入理解 Proximal Policy Optimization (PPO) 算法，这是 ReFT 和 CARFT 等方法用于微调 LLM 的核心 RL 算法。需要了解其目标函数、优势函数（GAE）、裁剪（Clipping）等关键概念。 对比学习（Contrastive Learning）：学习其基本思想和主流的损失函数，特别是 InfoNCE。理解它是如何通过正负样本对来学习一个好的表示空间的。SimCLR、MoCo 等是该领域的经典论文。 思路链（Chain-of-Thought, CoT）：了解 CoT 的起源、发展，以及它为什么能有效激发 LLM 的推理能力。 Hugging Face TRL 库：论文中提到了使用了 TRL (Transformer Reinforcement Learning) 库。了解这个库的架构和使用方法，可以帮助你快速地将论文中的想法付诸实践。 希望这份详尽的解读能帮助您全面、深入地理解这篇优秀的论文。如果您还有任何其他问题，随时可以提出。\n","date":"2025-08-29T00:00:00Z","permalink":"/carftboosting-llm-reasoning-via-contrastive-learning-with-annotated-chain-of-thought-based-reinforced-fine-tuning/","title":"CARFT：Boosting LLM Reasoning via Contrastive Learning with Annotated Chain-of-Thought-based Reinforced Fine-Tuning"},{"content":"结合了全局规划和memory。\n论文深度解读 大型语言模型（LLM）驱动的智能体在处理单一、直接的指令时表现出色，但在需要长远规划、多步骤、且环境动态变化的复杂任务中，它们往往会“迷失方向”或“不知变通”。这篇论文精准地指出了当前主流方法的两大困境：一类是高层规划方法（High-level Planning），它们像一个制定了宏伟蓝图但缺乏细节的将军，能够设定清晰的总体目标（例如，分解为几个子任务），但在执行过程中遇到意外情况时（比如环境变化或执行错误）就显得非常僵化，难以适应。另一类是步进式方法（Step-wise Methods），它们像一个只看脚下三步路的士兵，能够根据当前环境灵活地做出反应，但由于缺乏全局视野，很容易陷入局部最优解，导致行为低效、重复，甚至偏离最终目标。\n正如论文在图1中所展示的：\nTop-Left: High-level planning with global sub-goals lacking flexibility; Top-Right: Step-wise methods with local adaptability but limited global guidance; Bottom: HIPLAN’s hierarchical approach combining milestone guidance and step-wise hints for adaptive and robust planning.\n这段描述清晰地揭示了现有方法的“两难困境”，并引出了本文的核心思想——将全局规划的“远见”与局部适应的“灵活”相结合。\nHIPLAN 框架正是为了解决这一核心矛盾而设计的。它的名字——Hierarchical Planning (分层规划)——已经揭示了其解决方案的精髓。HIPLAN的创新之处在于它引入了一种自适应的全局-局部双层指导机制（Adaptive Global-Local Guidance）。这个机制包含两个核心组件：\n全局指导（Global Guidance）：它以“里程碑行动指南（Milestone Action Guide）”的形式存在。这就像是为智能体的任务旅程设定了一张“路线图”，上面标记了几个关键的必经站点（即里程碑 Milestones）。这个路线图确保了智能体在任何时候都清楚自己的长远目标和当前所处的阶段，不会偏离主航道。\n局部指导（Local Guidance）：它以“步进式提示（Step-Wise Hints）”的形式存在。这就像一个实时的“GPS导航”，在智能体每走一步时，都会根据当前的位置和环境，给出具体的、细粒度的指令，告诉它下一步该怎么走才能最高效地到达下一个里程碑。更重要的是，当智能体走错路时（例如，执行了无效动作），这个“导航”还能及时发出“行动修正（Action Correction）”的提醒，帮助其纠正错误。\n为了支撑这套双层指导系统，HIPLAN设计了一个巧妙的“经验复用”机制。在离线阶段（Offline Phase），它会从人类专家的成功演示（Expert Demonstrations）中学习，构建一个里程碑库（Milestone Library）。这个库里存储的不是完整的、冗长的任务轨迹，而是被精心拆分成的、具有明确语义的“里程碑-轨迹片段”对。这种“中等粒度”的经验抽象是HIPLAN的另一个关键创新。相比于复用整个任务轨迹（可能包含过多噪音和不相关的细节）或单个动作（过于依赖特定上下文），复用里程碑级别的经验在通用性（Generalizability）和信息量（Informativeness）之间取得了绝佳的平衡。\n在执行阶段（Execution Phase），HIPLAN会进行两次检索：首先，根据当前任务，从库中检索相似的任务来动态生成全局的“里程碑行动指南”；然后，在每一步执行时，根据当前的里程碑，检索相似的里程碑经验来生成精准的“步进式提示”。\n论文通过在两个极具挑战性的 long-horizon 任务基准——ALFWorld（模拟家庭环境交互）和 WebShop（模拟在线购物）——上的大量实验，证明了HIPLAN的卓越性能。例如，在使用 LLaMA3.3-70B 模型时，HIPLAN 在 ALFWorld 上的成功率达到了惊人的 94%，远超之前最优的 TRAD 方法的 79%。在更为复杂的 WebShop 环境中，HIPLAN 的成功率也达到了 40%，是 TRAD 方法（14%）的近三倍。这些数据强有力地证明了HIPLAN框架的有效性和优越性。\n总而言之，HIPLAN 通过其创新的分层规划框架、自适应的全局-局部指导以及高效的里程碑级经验复用策略，为解决 LLM 智能体在复杂长期任务中的规划难题提供了一个非常优雅且有效的解决方案，显著提升了智能体的任务成功率、鲁棒性和执行效率。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是提升大型语言模型（LLM）智能体在复杂、长时程（long-horizon）任务中的规划与决策能力。\n解决的实际问题： LLM 智能体在现实世界中要发挥作用，必须能够完成像“打扫房间”、“在线预订一套满足特定需求（如价格、品牌、颜色）的商品”或“根据文档修复一个软件bug”这样需要多步骤、持续交互且可能遇到意外情况的任务。目前，智能体在这些任务中普遍存在以下问题：\n迷失方向：在长序列的决策中，容易忘记最初的目标，做出与任务无关的冗余行为。 缺乏适应性：当环境发生微小变化或之前的某个动作失败时，无法灵活调整后续计划，导致任务卡死。 效率低下：由于缺乏宏观规划，可能在环境中进行大量无效的探索，浪费时间和计算资源。 对行业发展的重要性： 解决这个问题是推动 AGI（通用人工智能） 发展的关键一步。一个真正“智能”的体，必须具备可靠的长期规划能力。其重要意义体现在：\n解锁商业应用：这将极大地推动 LLM 智能体在机器人流程自动化（RPA）、智能客服、个人AI助理、软件开发与测试等领域的商业化落地。例如，一个能稳定执行复杂网页操作的智能体，可以自动化完成数据录入、市场调研、在线采购等大量重复性工作。 提升人机交互体验：未来的操作系统或应用可能由AI智能体驱动。用户只需用自然语言下达一个复杂指令，智能体就能自主规划并完成，这将是人机交互的革命性飞跃。 加速科学研究：在科研领域，智能体可以被用于自动执行复杂的模拟实验、数据处理流程或文献综述，从而解放研究人员的生产力。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ HIPLAN 框架的核心是一个全新的思路：通过分层规划实现全局战略与局部战术的动态协同。\n新的方法与模型： 分层指导框架（Hierarchical Guidance Framework）：这是HIPLAN的骨架。它将复杂的规划问题分解为两个层面：\n宏观层面：生成一个全局的里程碑行动指南（Milestone Action Guide），作为任务的“主线剧情”。 微观层面：在每个时间步，生成一个局部的步进式提示（Step-Wise Hint），作为执行的“即时战术”。 里程碑级经验库（Milestone-level Experience Library）：这是HIPLAN的“知识大脑”。它通过离线处理专家演示，提取出中等粒度的经验。 \u0026gt; 如论文4.2节所述，库中存储的是元组 (v_task, v_milestone, m_k, ζ_k)，包含了任务向量、里程碑向量、里程碑描述和对应的轨迹片段。这种结构化的存储方式是高效检索和复用的基础。\n双重检索机制（Dual-Retrieval Mechanism）：在执行时，HIPLAN会进行两次不同粒度的检索：\n任务级检索（Task-Level Retrieval）：用于生成全局的里程碑指南。 里程碑级检索（Milestone-Level Retrieval）：用于生成局部的步进式提示。 与之前方法的比较、特点和优势： 对比方法 HIPLAN的特点与优势 ReAct (Step-wise Method) ReAct 通过“思考-行动”循环实现局部适应性，但缺乏全局视野。HIPLAN的优势在于，其全局里程碑指南为智能体提供了稳定的“灯塔”，防止其在复杂的决策空间中迷航，从而显著减少了无效探索和重复行为。 High-level Planners 这类方法预先生成一个固定的子目标序列，过于僵化。HIPLAN的优势在于，其步进式提示是动态生成的，可以根据实时的观测和环境反馈进行调整，甚至修正错误，实现了“战略坚定”与“战术灵活”的统一。图1底部的案例生动展示了HIPLAN如何修正一个错误的选择（选了红色而不是要求的蓝色）。 TRAD (Retrieval-Augmented Method) TRAD 通过检索相似的思维链（thought）来辅助决策，但它检索的是低层级的动作序列，容易引入噪音。HIPLAN的优势在于，它在更高抽象层次——里程碑——上进行检索和规划。这使得HIPLAN能够学习到更具通用性的策略，而不是被特定任务的细节所束缚。如论文在WebShop实验中指出的，TRAD的表现甚至不如ReAct，说明低层次的动作演示有时会严重误导智能体。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列严谨的实验来验证HIPLAN的有效性。\n实验设计：\n基准（Datasets）：选用了两个公认的、用于评估智能体长时程规划能力的基准： ALFWorld：一个基于文本的模拟家庭环境，任务复杂，需要执行平均超过50步的动作序列。 WebShop：一个模拟的在线购物网站，包含超过118万种商品，任务要求智能体根据自然语言指令搜索、筛选并购买商品。 基线模型（Baselines）：选取了三种代表性的强大基线模型进行对比： ReAct：代表经典的“思考-行动”步进式方法。 Reflexion：代表通过事后“反思”来学习的智能体。 TRAD：代表当前先进的检索增强规划方法。 评估模型（LLMs）：为了验证方法的通用性，实验在两种不同架构的主流开源大模型上进行：Mixtral-8x22B (MoE架构) 和 LLaMA3.3-70B (Dense架构)。 评估指标（Metrics）： 任务成功率（Success Rate）：衡量智能体最终完成任务的比例，是核心指标。 平均奖励（Average Reward for WebShop）：衡量购买的商品与用户需求的匹配度。 平均步数（Average Steps）：衡量智能体的执行效率。 实验数据和结果：\nHIPLAN在所有实验中都取得了最佳性能（State-of-the-Art）。\n任务成功率： \u0026gt; 根据 Table 1 (ALFWorld) 和 Table 2 (WebShop) 的数据，我们可以看到： \u0026gt; \u0026gt; * 在 ALFWorld (LLaMA3.3-70b)，HIPLAN 达到了 94% 的成功率，而基线中最高的 TRAD 为 79%，ReAct 仅为 50%。 \u0026gt; * 在 WebShop (LLaMA3.3-70b)，HIPLAN 达到了 40% 的成功率，而基线中最高的 TRAD 仅为 14%。这表明在更开放和复杂的环境中，HIPLAN的优势更为显著。\n执行效率： \u0026gt; 根据 Figure 5 (Average steps) 的数据： \u0026gt; \u0026gt; * HIPLAN 完成任务所需的平均步数显著低于所有基线。在ALFWorld上，步数减少了 28%；在WebShop上，减少了 37%。这证明了分层指导不仅提高了成功率，也极大地提升了效率。\n消融实验（Ablation Study）： 这是验证方法设计合理性的关键。 \u0026gt; 根据 Figure 3，实验对比了完整版HIPLAN和三个“阉割版”： \u0026gt; \u0026gt; 1. HIPLAN-Milestone (只有全局指导): 性能有所提升，但不如完整版，证明局部提示是必要的。 \u0026gt; 2. HIPLAN-w/o milestone-level demonstrations (提示不基于检索): 性能下降明显，证明从里程碑库中检索经验至关重要。 \u0026gt; \u0026gt; 这个实验清晰地证明了分层结构和里程碑级经验复用两个核心组件都是不可或缺的，它们共同造就了HIPLAN的优越性能。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? HIPLAN 为我们开辟了新的道路，但前方依然有广阔的探索空间。\n值得探索的问题和挑战： 里程碑的自动发现与学习：目前HIPLAN的里程碑库依赖于专家演示。这是一个巨大的瓶颈。未来的研究可以探索如何让智能体在没有监督的情况下，通过自我探索（Self-Exploration）或强化学习（Reinforcement Learning）自动地发现任务中的关键里程碑。 更抽象的跨任务技能迁移：HIPLAN实现了任务内的经验复用。更高阶的挑战是如何学习到可以跨越不同任务的通用技能（Generalizable Skills）。例如，学会“打开一个容器”这个技能后，可以应用于“打开冰箱”和“打开抽屉”，而不需要为每个任务单独学习。 动态规划与实时重规划：当遇到重大意外（例如，目标物品不存在，或网站结构发生重大变化）时，当前的HIPLAN可能需要从头开始。未来的智能体需要具备动态重规划（Dynamic Re-planning）能力，即在执行过程中调整甚至完全改变宏观的里程碑路径。 多模态融合：真实世界的任务不仅仅是文本。智能体需要能够理解视觉、听觉等多模态信息来进行规划。将HIPLAN的思想扩展到多模态领域是一个重要的方向。 新的技术和投资机会： AI原生自动化平台：基于类似HIPLAN的强大规划能力，可以构建新一代的AI原生自动化工具，它们能够理解模糊的自然语言指令，并稳定地执行跨应用、长流程的复杂任务。这将颠覆现有的RPA市场。 具身智能（Embodied AI）与机器人：为机器人配备强大的分层规划大脑，让它们能够自主理解并完成复杂的物理世界任务（如烹饪、打扫、装配），这是通向家庭机器人和智能制造的必经之路。 复杂软件工程的AI代理：开发能够自主编写代码、调试、测试并根据用户反馈迭代的AI软件工程师。这需要极强的长期规划和问题分解能力。 AI驱动的个性化教育与培训：设计能够为学生动态规划学习路径、生成个性化练习并提供实时反馈的AI导师。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管HIPLAN非常出色，但我们仍可以从批判性的角度审视其潜在的局限性。\n存在的不足及缺失： 对专家数据的高度依赖：整个框架的冷启动依赖于高质量的专家演示数据。在许多新领域，获取这样的数据成本高昂甚至是不可能的。论文没有讨论在数据稀疏或数据质量较差的情况下的表现。 里程碑切分的鲁棒性：里程碑的切分和描述是由另一个强大的LLM（GPT-4o）完成的。这个过程本身的稳定性和准确性没有得到深入的探讨。如果切分出错，可能会“污染”整个经验库，从而误导智能体。 可扩展性问题（Scalability）：论文中的里程碑库规模尚可（约3330条）。当任务领域扩展，库的规模增长到数百万甚至数十亿条时，检索的效率和准确性是否会成为新的瓶颈？ 可解释性与可调试性：虽然HIPLAN的规划过程比纯粹的端到端方法更有结构，但其核心的生成步骤（生成指南和提示）仍然依赖于LLM这个“黑箱”。当智能体做出一个奇怪的决策时，我们仍然很难精确地追溯其“思考过程”。 需要进一步验证和存疑的： 对LLM本身能力的依赖：HIPLAN作为一个框架，其性能上限在很大程度上受限于其所使用的LLM（如LLaMA3.3）本身的基础能力（如推理、代码理解）。框架带来的提升有多大程度上是独立的，多大程度上是放大了基础模型的优势，还需要更深入的分析。 在更动态和对抗性环境下的表现：ALFWorld和WebShop的环境相对静态。如果在一个与人类或其他智能体实时交互的、更具动态性和对抗性的环境中，HIPLAN的规划和适应能力是否依然稳健？ 失败案例的深入分析：尽管成功率很高，但仍有失败案例。论文在附录中做了一些错误分析，但更系统、更深入地剖析失败的根本原因（是感知错误、规划错误还是知识库的缺陷？），对于改进框架至关重要。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，以下是您可以重点学习和借鉴的。\n重点学习与启发： 分层思想是解决复杂问题的万能钥匙：无论是在AI领域还是在日常工作、项目管理中，将一个庞大、模糊的目标分解为一系列清晰、可执行、可验证的“里程碑”，都是最高效的策略。这是本文最核心、最普适的思想。 结构化经验，而非原始数据：不要只是简单地堆砌数据。像HIPLAN构建里程碑库一样，思考如何对你的知识和经验进行抽象和结构化，将其转化为可复用、可检索的“知识模块”。这比存储零散的笔记要强大得多。 “战略”与“战术”的平衡：在任何长期项目中，都需要有一个稳定的长期计划（战略），同时也要有能力根据实际情况灵活调整具体步骤（战术）。HIPLAN的“全局指导+局部提示”完美地诠释了这一点。在你的工作中，可以尝试建立类似的反馈和调整循环。 “中等粒度”的价值：HIPLAN的成功很大程度上归功于它找到了“里程碑”这个不高不低、恰到好处的抽象粒度。在解决问题时，寻找合适的“分解粒度”至关重要。太粗则无法指导行动，太细则会丧失通用性。 需要补充的背景知识： AI Agent基础框架：建议您深入了解 ReAct (Reason+Act) 的工作原理，这是许多现代Agent的基础。同时，对 Retrieval-Augmented Generation (RAG) 的理解也至关重要，因为HIPLAN本质上是一种非常高级的RAG应用。 经典AI规划：了解一些经典AI规划领域的概念，如 Hierarchical Task Networks (HTN)，可以帮助您更好地理解HIPLAN分层思想的渊源。 强化学习（Reinforcement Learning）：特别是 Hierarchical Reinforcement Learning (HRL)，它也在研究如何将任务分解为子目标（sub-goals），这与HIPLAN的里程碑思想异曲同工，但实现路径不同。了解HRL将为您提供更广阔的视角。 希望这份详尽的解读能够帮助您全面、深入地理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-08-29T00:00:00Z","permalink":"/hiplanhierarchical-planning-for-llm-agents-with-adaptive-global-local-guidance/","title":"HIPLAN：Hierarchical Planning for LLM Agents with Adaptive Global-Local Guidance"},{"content":"人工合成的工具数据不可靠，评测模型看到不同数量的工具性能有无提升，发现大部分模型看到的工具越多，性能越低，**对于真正具备强大代理能力（agentic capabilities）的模型，广阔的行动空间非但不是诅咒，反而是一种祝福（blessing）\nimage.png 论文的深度解读 《MCPVerse》这篇论文的核心贡献在于，它构建并提出了一个前所未有的、兼具广度（Expansive）与真实性（Real-World）的代理工具使用（Agentic Tool Use）评测基准。它不仅仅是对现有评测体系的一次增量改进，更是一次范式上的革新，旨在将大模型工具使用的能力评估从“实验室环境”推向“真实世界场景”。\n论文开篇就敏锐地指出了当前评测基准存在的两大核心缺陷：\n工具的“虚假性”：许多评测依赖于人工合成的、简化的工具，如模拟计算器或虚拟购物车。模型在这些环境中的成功，可能只是学会了识别某种特定的、简单的交互模式，而非真正掌握了在复杂、多变的真实工具环境中进行规划与协调的能力。论文提到： \u0026gt; These often simulate calculators, simplified weather services, or mock shopping carts, whose data formats and interaction patterns bear little resemblance to production systems. \u0026gt; （它们通常模拟计算器、简化的天气服务或模拟购物车，其数据格式和交互模式与生产系统几乎没有相似之处。）\n行动空间的“狭隘性”：受限于模型上下文长度，现有评测通常只提供几十个预先筛选好的工具供模型选择。这种“喂到嘴边”的方式，无法检验模型在面对成百上千种可能性时，自主导航、筛选和决策的“大海捞针”式的能力。\n为了解决这些问题，MCPVerse的设计理念立足于两大基石：真实主义（realism）和规模（scale）。其创新性体现在三个层面：\n首先，在真实性层面，MCPVerse集成了超过550个真实世界中可执行的工具，这些工具通过一个名为模型上下文协议（Model Context Protocol, MCP）的开放标准进行统一接入。MCP可以理解为一个“AI工具的USB接口”，它为不同来源、不同功能的工具提供了一个标准化的连接方式，使得LLM可以像操作系统发现和使用硬件一样，动态地发现和使用工具。这使得评测任务可以直接调用如Yahoo Finance的金融数据、Git的版本控制、Amap的地图服务等真实服务。更关键的是，它引入了实时验证（Real-Time Verification）机制，对于航班查询、股价分析这类时效性强的任务，评测系统会通过动态脚本获取实时的正确答案，确保了评估的准确性。\n其次，在规模层面，MCPVerse构建了一个极其广阔的行动空间（Action Space）。所有552个工具的API定义（schemas）加起来，总长度超过了14万个token。这是一个什么概念？它远超了包括GPT-4o（128k）在内的许多主流模型的最大上下文窗口。这迫使我们去思考一个全新的问题：当一个模型拥有一个近乎“无限”的工具箱时，它的表现是会更好还是更差？论文特意设计了三种评测模式来探究这个问题： * Oracle（神谕）模式：只提供完成任务所必需的最少工具，模拟了传统评测的“小而精”环境。 * Standard（标准）模式：提供一个包含218个工具（约44k token）的中等规模工具集，适配主流模型的64k上下文长度。 * Max-Scale（全尺寸）模式：提供全部552个工具（约140k token），是极限压力测试，只有少数具备超长上下文能力的模型（如Claude-4-Sonnet, Gemini-2.5-Pro）能够参与。\n最后，在评估方法上，MCPVerse采用了混合式、基于结果的评估（Hybrid Outcome-Based Evaluation）。它不关心模型是否遵循了某条预设的“标准路径”，只关心最终的输出结果是否正确。这更加符合现实，因为一个任务往往有多种解法。对于文本类输出，它使用强大的GPT-4o-20241120作为“LLM裁判”；对于涉及文件修改等环境状态变化的任务，则通过自动化脚本进行验证，兼顾了评估的客观性与灵活性。\n论文最震撼的发现来自于实验结果。当从Oracle模式切换到工具更多的Standard模式时，绝大多数模型的性能都出现了显著下降。例如，GPT-4o的准确率从51.32%暴跌至26.83%，Kimi-K2-0711更是下降了惊人的39.5个百分点。这验证了一个普遍的猜想：更多的工具选项会成为一种干扰，增加了模型的决策难度。然而，Claude-4-Sonnet成为了唯一的例外，它的平均准确率从57.81%反而提升到了61.01%。\n这个反直觉的结果揭示了一个深刻的洞见：对于真正具备强大代理能力（agentic capabilities）的模型，广阔的行动空间非但不是诅咒，反而是一种祝福（blessing）。论文通过一个案例（图5）生动地展示了这一点：当在工具受限的Oracle模式下，Claude-4-Sonnet因为一个认证参数错误而失败时，它便放弃了任务；但在工具丰富的Standard模式下，遇到同样的失败后，它没有放弃，而是自主地切换到了一个全新的、功能类似的工具（fetch），最终成功解决了问题。这种灵活的、适应性的问题解决能力，正是“代理智能”的核心体现。MCPVerse通过其设计，成功地将这种能力量化并凸显出来。\n总而言之，MCPVerse不仅仅是一个评测基准，它更是一个“筛选器”和“风向标”。它筛选出了在复杂真实环境中真正可靠的AI代理，并指明了未来大模型发展的方向——不仅仅是扩大上下文窗口，更要提升在超大规模、充满干扰的选项中进行有效规划、试错、和自适应调整的代理智能。\n接下来，我将按照您提出的六个问题，逐一进行详细解读。\n1. 论文的研究目标是什么？ 研究目标：构建一个大规模、高保真的评测基准（benchmark），用于科学地评估大语言模型（LLMs）作为智能代理（Agent）使用外部工具解决现实世界问题的能力。\n想要解决的实际问题：\n评估的“失真”问题：当前主流的工具使用评测大多依赖模拟的、简化的工具和静态的数据。这导致评估结果无法真实反映模型在面对现实世界中复杂、动态、充满非标准情况的API时的表现。 评估的“范围局限”问题：由于技术限制（如上下文长度），现有评测通常只给模型提供少量（几十个）高度相关的工具。这忽略了在现实中，一个AI代理可能需要从成百上千个潜在工具中进行选择和决策的挑战。 对于行业发展的重要意义： 这个问题的意义极其重大。随着大模型能力边界的拓展，其应用场景正从内容生成（如写邮件、写代码）转向行动执行（如自动预订机票、管理服务器、分析财报）。这就要求模型必须能可靠地与外部世界通过工具（API）交互。\n建立行业标准：一个好的评测基准如同行业的一把“标尺”。MCPVerse的出现，为业界提供了一套更接近真实应用场景的评估标准，能够更准确地衡量哪个模型是更好的“AI代理”，从而引导技术发展的方向。 驱动模型迭代：通过暴露现有顶尖模型（如GPT-4o）在复杂工具环境下的短板，MCPVerse直接指出了模型需要改进的核心能力，例如在庞大工具集中的选择能力、失败后的自主恢复与规划调整能力等，从而驱动下一代模型的研发。 加速应用落地：企业在选择哪个大模型来构建自己的AI应用时，需要一个可靠的参考。MCPVerse的评估结果比那些在“温室”环境中得出的结论更具参考价值，有助于企业降低技术选型的风险，加速AI代理应用的商业化落地。 2. 论文提出了哪些新的思路、方法或模型？ 这篇论文的核心是提出了一套新的评测框架和理念，而非一个新模型。其创新思路和方法主要体现在以下几个方面，这些都与之前的方法形成了鲜明对比：\n拥抱真实世界，而非模拟： 思路创新：彻底摒弃了“玩具工具”。论文坚信，只有让模型在“真实战场”上接受考验，才能评估其真正的能力。 方法实现：全面采用基于模型上下文协议（MCP）的真实、可执行工具。MCP作为一个开放标准，是实现这一点的技术关键。它允许评测框架轻松集成来自不同提供商的真实服务，覆盖文件系统、Git、金融数据、新闻聚合、办公软件等多种功能。 优势：评估结果更具说服力，能直接反映模型在实际部署中可能遇到的问题，如处理不规范的API返回、应对网络延迟、处理权限错误等。 从“精选”到“海量”，刻意构造广阔行动空间： 思路创新：挑战了“必须通过检索器（retriever）缩小工具范围”的传统观念。论文假设，一个强大的AI代理应该具备自主在信息海洋中导航的能力，而不是总需要一个“向导”。 方法实现： 大规模工具集：整合了65个MCPs，共552个工具，API定义总长度超过140k token。 刻意省略检索器： \u0026gt; Unlike many previous tool-use benchmarks that incorporate a retrieval module… our evaluation system deliberately omits this component… we hypothesize that agentic models can discover novel solution paths when presented with a large-scale action space. \u0026gt; （与许多包含检索模块的先前工具使用基准不同…我们的评估系统刻意省略了该组件…我们假设代理模型在面对大规模行动空间时能够发现新颖的解决方案路径。） 优势：这种“压力测试”能够有效地区分出两种模型：一种是只能处理“填空题”（给定少数选项）的模型，另一种是能处理“开放式问答”（在广阔空间中自主探索）的真正代理。 结果导向的灵活评估： 思路创新：评估的焦点应是“任务是否完成”，而非“是否遵循了标准答案”。 方法实现：采用混合式基于结果的评估（Hybrid Outcome-Based Evaluation）。它不预设固定的工具调用序列，只要最终结果正确即可。例如，查询某公司信息，模型既可以通过搜索引擎，也可以通过该公司的官方API，只要拿到正确信息，都被视为成功。 优势：更加公平和科学，鼓励并能够评估模型的创造性和灵活性，而不是死记硬背某种固定模式。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一套精巧的对比实验，通过控制变量（可用工具的数量和范围）来系统性地验证其核心假设，即“广阔的行动空间是检验真正代理能力的关键”。\n实验设计：\n核心变量：可用工具集的规模。 三个实验组（模式）： Oracle Mode（对照组）：仅提供完成任务必需的1-2个MCP。这代表了现有评测方法的最佳情况，工具干扰最小。 Standard Mode（核心实验组）：提供一个包含32个MCP（218个工具）的较大工具集。这是对模型在中等复杂现实场景下的模拟。 Max-Scale Mode（极限测试组）：提供全部65个MCP（552个工具）。这是对模型处理超大规模、复杂工具环境能力的极限挑战。 参与模型：涵盖了当时业界领先的8个大模型，包括Claude-4-Sonnet, Gemini-2.5-Pro, GPT-4o-20241120, DeepSeek系列等，确保了评估的广泛性。 实验数据和结果： 实验结果清晰地展示了不同模型在工具规模扩大时的性能变化，以下是关键数据摘要：\n模型 Oracle Mode 准确率 Standard Mode 准确率 性能变化 Claude-4-Sonnet 57.81% 61.01% +3.20% Gemini-2.5-Pro 48.25% 47.33% -0.92% DeepSeek-R1-0528 50.77% 50.28% -0.49% GPT-4o-20241120 51.32% 26.83%* -24.49% Kimi-K2-0711 55.84% 16.34%* -39.50% （注：带*的分数是由于API限制，通过“基于提示的函数调用”方式获得，可能本身就存在性能损失）\n关键结论： 大部分模型性能随工具增多而下降：如上表所示，GPT-4o和Kimi等模型的性能出现了灾难性的下滑。这表明它们在庞大的工具集中迷失了方向，无法有效筛选出正确的工具。 Claude-4-Sonnet表现出独特的“反脆弱性”：它是唯一一个在工具集扩大后性能不降反升的模型。这直接证明了它具备更强的代理能力，能够利用更丰富的选项来寻找备用方案和更优路径。 \u0026gt; This counter-intuitive result underscores the significant benefit of a larger exploration space, which allows more capable models to discover more effective or robust solutions. \u0026gt; （这个反直觉的结果凸显了更大探索空间的显著优势，它允许更有能力的模型发现更有效或更稳健的解决方案。） 任务越复杂，差距越明显：数据显示，Claude-4-Sonnet的性能优势在更复杂的L3任务上尤为突出，其在Standard模式下的L3任务准确率（56.45%）远高于其他模型。 这些实验数据强有力地支撑了论文的论点：MCPVerse不仅是一个有效的评测基准，而且它的设计成功地区分出了模型的不同层次的代理智能。\n4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为AI代理的研究打开了一扇新的大门，未来有许多激动人心的探索方向：\n挑战与问题： 无限扩展的工具生态：MCPVerse包含了552个工具，但现实世界的API数量是百万乃至千万级别的。如何让模型在这样一个近乎无限的、动态变化的工具海洋中进行实时发现、学习和安全使用，是一个巨大的挑战。 工具的组合与编排：当前的评测主要集中在调用单个或少数几个工具。未来的挑战在于如何让代理能够自主地将多个原本无关的工具组合起来，创造出全新的工作流来解决复杂问题（例如，结合天气API、地图API和日历API来规划一次完美的周末出行）。 经济性与效率：调用真实的API是有成本的（金钱和时间）。如何让代理在保证效果的同时，学会规划成本最低、效率最高的工具调用路径，是走向实用化的关键一步。 安全性与可靠性：赋予AI代理调用真实世界工具的能力，也带来了巨大的安全风险。如何建立一套完善的权限管理、风险监控和失败恢复机制，防止代理做出有害操作，是一个亟待解决的难题。 新技术和投资机会： AI代理操作系统（Agent OS）：未来可能会出现专门为AI代理设计的操作系统。它负责管理工具（MCPs）、分配资源、监控安全，并为上层代理提供一个稳定可靠的执行环境。这是一个巨大的平台级机会。 工具发现与注册中心：类似于苹果的App Store，未来可能会出现一个全球性的“MCP Store”，开发者可以在上面发布自己的工具（MCP服务），AI代理可以动态地搜索、订阅和使用这些工具。 专为代理优化的模型：目前的大模型大多还是通才。未来，可能会出现专门针对大规模工具使用和复杂任务规划进行深度优化的“代理专用模型”。在MCPVerse这样的基准上表现优异的模型，将具有极高的商业价值。 自动化测试与验证服务：随着AI代理应用的普及，针对其功能、性能和安全性的自动化测试与验证将成为一个新兴的、重要的产业环节，确保企业部署的AI代理是可靠和安全的。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文具有开创性，但从批判性的角度审视，仍存在一些潜在的不足和值得商榷之处：\n评测的稳定性和可复现性： MCPVerse严重依赖于65个第三方提供的在线服务。这些服务的可用性、API接口、返回数据格式都可能随时发生变化，甚至停止服务。这给评测的长期稳定性和严格复现带来了巨大挑战。尽管论文提到包含了配置文件，但无法从根本上解决对外部动态服务的依赖问题。\n实验方法的公平性疑虑： 论文坦诚，由于GPT-4o和Gemini-2.5-Pro等模型的原生API对工具数量有限制（128个或512个），在Standard和Max-Scale模式下，不得不采用一种“基于提示的函数调用”的替代方案。而实验（Table 3）表明，这种替代方案本身就会导致性能大幅下降，尤其是对于Claude-4-Sonnet（下降超过40%）。因此，GPT-4o等模型在Standard模式下的糟糕表现，究竟多大程度上是其自身能力不足，多大程度上是受到了这种“不公平”的评测方法的影响？ 这一点论文没有进行更深入的剥离分析，使得模型间的直接横向对比存在一些疑问。\n任务多样性和覆盖面： 尽管论文构建了250个任务，并分为三个难度等级，但这些任务是否能够全面、无偏地代表现实世界中AI代理需要解决的各类问题？任务的设计和筛选过程可能存在设计者的主观偏见，例如可能更偏向于信息查询和数据处理类任务，而对需要复杂物理交互或长期记忆的任务覆盖不足。\n成本与门槛： 运行这样一个大规模、调用真实API的评测基准，其计算成本和API调用费用将是极其高昂的。这可能会将许多资源有限的学术研究机构和小型团队排除在外，限制了该基准的广泛应用和后续研究的开展。\n6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文的价值巨大，以下是几个重点学习方向和启发：\n核心启发：从“能力”到“选择”的转变 最大的启发在于，AI智能的下一个阶段，核心瓶颈可能不再是单纯的“能力”（比如语言理解、代码生成），而是在庞大可能性空间中的“选择”能力。您的创新可以围绕如何提升模型的“选择智能”展开。 学习点：设计模型或系统时，要考虑当选项从10个增加到1000个时，系统性能将如何演进。要追求Claude-4-Sonnet那样的“反脆弱性”，即更多的选项带来更好的结果。 拥抱标准化：MCP协议的重要性 MCP协议是整个工作的技术基石。它揭示了标准化对于构建一个繁荣生态的决定性作用，就像USB之于外设、HTTP之于互联网一样。 学习点：如果您在构建AI应用或平台，思考一下是否可以定义一套标准接口来连接不同的模块（数据源、工具、模型）。这不仅能简化您自己的工作，还可能成为吸引第三方开发者加入的“护城河”。 “无检索器”设计的大胆假设 论文敢于去掉被认为是“标配”的检索器，直接让模型面对全部工具。这种“第一性原理”的思考方式值得学习。它挑战了现有范式，并因此得出了最惊人的发现。 学习点：在您的项目中，审视一下哪些模块是“约定俗成”的？它们是否真的不可或缺？大胆地做一些“减法”实验，可能会带来意想不到的突破。 需要补充了解的背景知识： 函数调用（Function Calling / Tool Use）：深入理解大模型是如何通过特定的输出格式（如JSON）来声明其调用外部API的意图的。这是所有工具使用的技术基础。 AI代理框架（Agentic Frameworks）：了解一些经典的代理框架，如ReAct (Reason, Act)、AutoGen等。这些框架为如何组织模型的思考和行动提供了不同的策略，与MCPVerse的简单循环评估流程形成对比。 模型上下文协议（MCP）：花时间阅读一下由Anthropic或OpenAI发布的关于MCP的官方文档或博客。理解其设计理念、目标和基本工作方式，将有助于您更深刻地理解这篇论文。 其他工具使用评测基准：可以简单浏览一下论文Table 1中提到的ToolBench, API-Bank等工作的摘要，这能让您更清晰地看到MCPVerse的革命性所在。 ","date":"2025-08-29T00:00:00Z","permalink":"/mcpversean-expansive-real-world-benchmark-for-agentic-tool-use/","title":"MCPVerse：An Expansive, Real-World Benchmark for Agentic Tool Use"},{"content":"存储记忆，有非参数化记忆和参数化记忆。其中参数化记忆有点像我正在做的世界模型。我是通过模拟多个动作并打分来得到最终动作，而这篇的做法是预测案例的分数，来得到最好的案例。\n（这张图也可以看作是当前流行的agent的工作流程。） ### 论文深度解读\n这篇论文的核心贡献在于，它为大型语言模型（LLM）智能体提出了一种全新的、低成本的持续学习范式——Memento。当前，要让一个LLM智能体（Agent）变得更聪明、更能适应新任务，主要有两种路径：一种是设计固定的、基于规则的工作流，这种方法死板、缺乏适应性；另一种是通过监督微调（Supervised Fine-tuning）或强化学习（Reinforcement Learning）来更新LLM本身的参数，这种方法虽然灵活，但计算成本极其高昂，且容易导致“灾难性遗忘”（Catastrophic Forgetting），即模型学会了新知识却忘记了旧知识。\nMemento巧妙地绕开了这两条路的弊端，提出了第三条路：在不改动底层LLM参数的前提下，通过外部记忆实现智能体的持续学习和适应。这个思想的灵感直接来源于人类的认知科学，特别是案例推理（Case-Based Reasoning, CBR）。简单来说，人类在解决新问题时，往往会回想过去处理过的类似问题（案例），并借鉴当时的解决方案。Memento正是将这一机制引入了LLM Agent。\n为了给这个想法建立坚实的理论基础，论文将其形式化为一个记忆增强马尔可夫决策过程（Memory-augmented Markov Decision Process, M-MDP）。这个概念听起来很复杂，但我们可以通俗地理解它： * 马尔可夫决策过程（MDP） 是强化学习的标准模型，描述了一个智能体在环境中通过观察状态、采取行动、获得奖励来学习最优策略的过程。 * Memento的“记忆增强”则是在这个标准模型中加入了一个新的核心组件：一个动态增长的记忆库（Case Bank）。这个记忆库存储了智能体过去的所有“经历”，每一条经历就是一个“案例”（Case），通常包含三元组 (状态, 行动, 奖励)，即“在什么情况下，做了什么事，得到了什么结果”。\n因此，Memento智能体在做决策时，不仅仅依赖于当前的状态和LLM内部的参数化知识，它还会主动地从记忆库中读取（Read）最相关的过往案例作为参考。完成任务后，无论成功与否，这次新的经历都会被写入（Write）记忆库，供未来使用。这构成了一个完整的“经历-学习-应用”闭环。\n论文的精髓在于对“如何读取记忆”这一环节的设计。它提出了两种具体的实现方式： 1. 非参数化记忆（Non-Parametric Memory）：这是最直观的方式。当面临新任务（新状态）时，智能体通过计算新任务与记忆库中所有旧任务的语义相似度（例如余弦相似度），来检索出最相似的几个案例。这就像一个初级程序员在Stack Overflow上搜索与当前bug最相似的问题描述。 2. 参数化记忆（Parametric Memory）：这是一种更高级、更智能的方式。它不再仅仅依赖“相似”，而是试图学习一个策略来判断哪个旧案例对当前任务最有用。为此，论文引入了软Q学习（Soft Q-Learning）框架来训练一个轻量的Q函数。这个Q函数的作用是预测：在当前状态下，参考某一个特定案例，最终能获得多大的奖励（即这个案例有多大的“价值”）。这样，智能体就能检索出“价值最高”的案例，而不仅仅是“表面最像”的案例。这个Q函数是独立于大模型的小网络，训练成本极低。\n实验部分是这篇论文的另一大亮点。它在四个极具挑战性的基准测试上验证了Memento的有效性，包括： * GAIA：一个测试长程规划和复杂工具使用的“通用AI助理”基准。Memento在此验证集上取得了87.88%的Pass@3准确率，排名第一，在测试集上达到79.40%，超越了当时众多知名的开源智能体框架。 * DeepResearcher：一个模拟真实世界网络研究的基准。Memento的平均F1分数达到了66.6%，几乎是传统“CoT + RAG”基线（37.7%）的两倍，证明了其在信息综合和多跳推理上的强大能力。 * 持续学习能力：论文通过迭代实验（见Table 4），清晰地展示了随着记忆库中案例的不断积累，Memento的性能在持续提升。例如，在DeepResearcher数据集上，经过5轮迭代，参数化CBR方法的性能从80.46%提升到了85.44%，这直观地证明了其“越用越聪明”的特性。 * 泛化能力：在处理未见过的域外（Out-of-Distribution, OOD）任务时，仅仅是增加了基于案例的记忆，就带来了4.7%到9.6%的绝对性能提升（见Figure 1d），这说明Memento学到的经验是可迁移的，能帮助智能体更好地泛化。\n总而言之，《Memento》这篇论文提出了一个极具开创性的框架，它不仅为LLM Agent的持续学习问题提供了一个高效、可扩展的解决方案，而且其设计思想深刻地植根于认知科学，并辅以严谨的强化学习理论建模和扎实的实验验证，为构建更通用、更智能的AI Agent指明了一个重要的发展方向。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是构建一种能够持续学习（Continual Learning）和适应环境变化的LLM智能体，同时避免对底层LLM进行昂贵的参数微调。 解决的实际问题： 静态智能体的局限性：许多现有智能体依赖固定的工作流和提示工程，一旦部署，其能力就被“冻结”，无法从新的交互中学习，也无法适应新情况。 微调方法的成本与风险：通过微调LLM参数来提升智能体能力，不仅需要大量的计算资源和高质量的训练数据，还面临“灾难性遗忘”的风险，使其在实际应用中难以进行频繁、快速的迭代。 \u0026gt; 正如论文在引言中指出的核心挑战： \u0026gt; \u0026gt; How can we build LLM agents that learn continuously from a changing environment without the prohibitive cost of fine-tuning the underlying LLMs? \u0026gt; \u0026gt; (我们如何才能构建出能够从变化的环境中持续学习，而又无需承担微调底层LLM高昂成本的LLM智能体？) 行业意义： 降低AI应用的迭代成本：Memento范式使得AI智能体可以通过“在线学习”的方式自我完善，企业无需为每次能力提升都进行大规模的模型重训练，极大地降低了AI应用的维护和迭代成本。 实现真正的个性化AI：一个部署给特定用户或特定场景的Memento智能体，可以通过不断积累与该用户/场景的交互经验，变得越来越“懂行”、越来越个性化，提供远超通用模型的使用体验。 推动通用人工智能（AGI）的发展：持续学习、从经验中汲取教训是通往更高级别人工智能的关键一步。Memento提供了一个可行的技术路径，让AI系统能够像人一样，通过积累经验来成长，这是构建通用智能体的重要基石。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心新思路： 从“模型学习”到“系统学习”的转变：它将学习能力从LLM模型内部转移到了一个外部记忆系统，智能体的进化不再依赖于模型参数的更新，而是依赖于外部经验库的丰富和检索策略的优化。 借鉴认知科学的案例推理（CBR）：与传统的检索增强生成（RAG）从静态知识库中检索“事实”不同，Memento从动态的经验库中检索“过程”和“策略”，这更接近人类的思维方式。 核心新方法/模型： 记忆增强马尔可夫决策过程 (M-MDP)：这是一个理论创新，为“记忆驱动决策”提供了数学上的严谨描述。它将记忆M正式纳入决策过程，使得策略函数π(a|s, M)同时依赖于当前状态s和记忆M。 双模态记忆检索机制：这是技术实现上的核心。 非参数化CBR：基于语义相似度的检索。 \u0026gt; ReadNP(St, Mt) = TopK sim(enc(st), enc(si)) 参数化CBR：基于学习的Q函数来预测案例的未来价值，从而进行检索。 \u0026gt; ReadP(st, Mt) = TopK Q(st, ci; θ) 这个Q函数通过一个简单的二元分类损失函数进行在线更新，因为它在“深度研究”这类任务中，奖励信号是二元的（成功或失败）。 \u0026gt; L(θ) = E(s,c,r)[-r log Q(s, c; θ) – (1 – r) log (1 – Q(s, c; θ))] 特点与优势： 高效性：无需微调LLM，学习过程仅涉及更新一个轻量级的Q函数网络和向记忆库中添加数据，计算开销极小。 适应性与持续性：智能体可以在线、实时地从新经验中学习，性能随着使用时间的增长而提升，不会“停滞不前”。 可解释性：当智能体做出一个决策时，可以追溯到它参考了哪些过去的案例，这为理解和调试智能体的行为提供了很好的窗口。 缓解灾难性遗忘：由于底层LLM的知识是固定的，新学到的经验以案例的形式存储在外部，不会冲刷掉模型原有的能力，从根本上避免了灾难性遗忘问题。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了全面且有说服力的实验来验证Memento的有效性。\n实验设计： 多基准评估：选取了4个不同侧重点的基准（GAIA, DeepResearcher, SimpleQA, HLE），全面评估了智能体在长程规划、网络研究、事实准确性和专业领域推理等方面的综合能力。 性能对比：将Memento与多种基线方法进行对比，包括传统的提示工程方法（CoT, CoT+RAG）和基于微调的SOTA方法（如DeepResearcher模型）。 消融研究（Ablation Study）：为了验证各个组件的必要性，论文精心设计了消融实验，对比了以下四种设置： Offline Executor: 只有LLM，没有工具和记忆。 Online Executor: LLM + 工具，但没有规划和记忆。 Memento w/o CBR: LLM + 工具 + 规划，但没有案例记忆。 Memento: 完整的模型。 持续学习验证：通过在DeepResearcher数据集上进行多轮迭代学习，并记录每一轮的性能变化，来验证模型的持续学习能力。 超参数分析：研究了检索案例数量K对性能的影响，以找到最佳平衡点。 关键实验数据和结果： 在DeepResearcher上的压倒性优势（Table 1）： | 方法 | 平均 F1 Score | 平均 PM Score | | :— | :— | :— | | CoT + RAG (基线) | 37.7% | 43.2% | | DeepResearcher (SOTA) | 51.8% | 60.5% | | Memento (GPT-4.1 + o4-mini) | 66.6% | 80.4% | 这个结果清晰地表明，Memento的性能远超传统的RAG方法和当时的SOTA微调方法。 在GAIA上的Top-1排名（Table 2）：在GAIA验证集上，Memento以87.88%的平均分位列榜首，证明了其在处理需要复杂规划和工具协调的通用任务上的卓越能力。 持续学习曲线（Table 4）： | 方法 | Iter 1 | Iter 2 | Iter 3 | Iter 4 | Iter 5 | | :— | :— | :— | :— | :— | :— | | Memento w/o CBR | 78.65 | 80.93 | 82.62 | 83.53 | 84.47 | | Memento w/ Parametric CBR | 80.46 | 82.84 | 84.10 | 84.85 | 85.44 | 这张表直观地展示了：1) 带有CBR的Memento性能始终优于不带CBR的；2) 随着迭代次数增加，性能稳步提升，验证了其学习能力。 案例数量K的影响（Table 3）：实验发现，在DeepResearcher数据集上，当K=4时，性能达到最优（F1: 64.5, PM: 78.5）。当K值继续增大时，性能反而略有下降。这说明： \u0026gt; …CBR benefits from a small, high-quality memory, unlike few-shot prompting, where more examples often help… Careful case selection and memory curation are thus crucial for continual learning. \u0026gt; （…CBR受益于一个小的、高质量的记忆库，这与少样本提示（few-shot prompting）不同，后者通常例子越多越好…因此，仔细的案例选择和记忆管理对于持续学习至关重要。） 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 记忆的抽象与遗忘：当记忆库变得极其庞大时（例如百万级案例），检索效率会下降，且充满大量冗余、甚至过时的信息。如何像人脑一样对记忆进行抽象化（Abstraction）（例如，从多个具体案例中总结出一条通用规则）和选择性遗忘（Forgetting），将是未来的核心挑战。 更高效的检索机制：目前的TopK检索在超大规模记忆库上会成为瓶颈。研究层次化记忆（Hierarchical Memory）、图记忆（Graph Memory）以及更先进的近似最近邻搜索（ANN）算法是必然趋势。 复杂案例的表征：目前案例是(s, a, r)三元组。但一次成功的经历可能包含复杂的推理链、工具调用序列和中间思考。如何更丰富地表征和存储“过程性知识”，而不仅仅是结果，是一个重要方向。 从失败中学习：论文中也存储了失败案例，但简单的reward=0信号能提供的信息有限。如何让智能体深入分析失败的原因（Credit Assignment），并从中学习到“什么不能做”，是一个更困难但价值巨大的问题。 新的技术和投资机会： “智能体记忆”即服务（Agent Memory as a Service）：未来可能会出现专门为AI Agent提供持久化、可学习、可迁移记忆的云服务平台。企业开发的多个Agent可以共享和贡献到一个统一的“组织记忆”中，实现群体智能。 新一代AI开发平台：目前的Agent开发框架（如LangChain, LlamaIndex）主要关注工作流编排和数据连接。下一代平台可能会将“可学习的记忆模块”作为其核心组件，让开发者能轻松构建出自进化的AI应用。 垂直领域的专业记忆库：可以预见，在金融、医疗、法律等专业领域，会出现预训练好的、包含大量高质量专业案例的“记忆库”产品。将这些记忆库与通用LLM Agent结合，可以快速构建出领域专家级的AI助手，这是一个巨大的商业机会。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 可扩展性验证不足：论文承认，在实验中，记忆库“用大约3k的训练数据就很快饱和了”。这引出了一个巨大的问题：当面临真实世界百万、千万级的交互数据时，Memento的检索性能和学习效率将如何变化？论文对此缺乏深入的探讨和实验。 成本分析不够全面：论文声称其方法比微调成本低，这在训练层面是成立的。但在推理（Inference）时，每次决策都需要进行一次检索操作，这会增加额外的延迟和计算开销。特别是在记忆库庞大时，这个开销不容忽视。论文没有提供详细的推理时延和成本分析。 对环境的假设过于理想化：实验是在基准数据集上进行的，这些数据集中的任务目标明确，奖励信号清晰（成功/失败）。在真实世界中，任务可能模棱两可，奖励可能是稀疏、延迟甚至带有噪声的。Memento框架在更复杂的现实环境中的鲁棒性有待验证。 对Q函数的设计讨论较少：参数化CBR的核心是Q函数，但论文对其网络结构、训练稳定性等细节着墨不多。这个小模型的性能和泛化能力在多大程度上影响了整个系统的上限，需要更深入的分析。 需要进一步验证和存疑的： 参数化与非参数化CBR的权衡：论文同时提出了两种方法，并在不同图表中展示了它们的效果。但在何种场景下应该优先选择哪一种？它们是否可以结合使用（例如，先用非参数方法快速召回，再用参数方法精排）？这些策略性的问题值得进一步研究。 “污染”问题：在DeepResearcher的消融实验（Table 5c）中，从Offline Executor切换到Online Executor时，性能出现了显著下降（F1从48.8降到30.8）。论文将其归因于数据污染问题，并引用了相关研究。 \u0026gt; This aligns with broader findings in the field… simply using external knowledge can sometimes negatively affect the model… 这说明，即使是简单的实时网络检索，也可能引入噪声，反而误导模型。Memento虽然通过规划和CBR最终解决了这个问题，但这提醒我们，与外部世界的交互是一把双刃剑，其负面影响和应对策略需要更深入的验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新想法： 将RAG升级为CBR-AG：如果你正在使用RAG，可以借鉴Memento的思想，将你的知识库从静态文档升级为一个动态的交互经验库。记录下用户的每个问题、系统的回答以及用户的反馈（如点赞、点踩、追问等），将这些作为案例存储起来。下次遇到类似问题时，优先检索这些成功的交互案例。 为你的检索引入“学习”：不要只满足于基于相似度的检索。你可以学习Memento的参数化方法，训练一个轻量级的排序模型（类似Q函数）。这个模型的目标是预测“检索出这个文档/案例，对最终生成用户满意的答案有多大帮助”。你可以用用户的隐式/显式反馈作为训练信号。这会让你的RAG系统“学会”什么是好的知识。 采用“规划-执行”架构：在构建复杂的Agent时，可以借鉴论文中的Planner-Executor架构。用一个更高阶的LLM（如GPT-4）作为Planner负责任务分解和策略制定，用一个更快速、成本更低的LLM（如o4-mini）作为Executor负责执行具体的子任务和工具调用。这种分层解耦的架构非常清晰和高效。 重视经验的积累和复用：在设计任何AI应用时，都应该内置一个“经验记录”模块。思考如何结构化地存储每一次成功和失败的交互，并设计机制让系统能够在未来的决策中复用这些宝贵的经验。 背景知识补充： 案例推理（Case-Based Reasoning, CBR）：了解CBR的基本概念和流程（4R：Retrieve, Reuse, Revise, Retain），这是理解Memento思想根源的关键。 强化学习基础：至少需要理解马尔可夫决策过程（MDP）、Q-Learning和价值函数（Value Function）的基本概念。这对于理解论文的参数化CBR方法和M-MDP建模至关重要。 检索增强生成（Retrieval-Augmented Generation, RAG）：深入理解RAG的工作原理，因为Memento可以看作是RAG在“知识源”和“检索策略”两个维度上的重要演进。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-29T00:00:00Z","permalink":"/mementofine-tuning-llm-agents-without-fine-tuning-llms/","title":"Memento：Fine-tuning LLM Agents without Fine-tuning LLMs"},{"content":"对记忆存储agent利用r1的思路训练。\n这篇论文无疑是LLM Agent（大型语言模型智能体）领域的一项重要进展。它精准地切入了当前大模型应用的核心痛点之一：记忆的持久化与智能化管理。当前的大模型，尽管在单次交互中表现惊人，但其本质上是“失忆的”（stateless），受限于有限的上下文窗口（context window），无法在长时间、跨会话的交互中有效保留和利用信息。这就好比我们与一位绝顶聪明但下一秒就忘掉一切的专家对话，其实用性大打折扣。\n为了解决这个问题，业界主流的方案是检索增强生成（RAG），即为模型外挂一个数据库作为“外部记忆”。但这带来了新的挑战：RAG通常是被动和启发式的，它只是机械地“检索-填充”，缺乏智能的“管理”能力。比如，它不知道何时应该新增一条记忆、何时应该更新一条过时的信息、何时应该删除一条错误或矛盾的记录。论文中给出了一个绝佳的例子：\nFor instance, in Figure 1, when a user first says “I adopted a dog named Buddy” and later adds “I adopted another dog named Scout”, a vanilla system misinterprets this as a contradiction, issuing DELETE+ADD and overwriting the original memory. A trained agent, however, consolidates with an UPDATE: “Andrew adopted two dogs, Buddy and Scout.”\n这个例子生动地说明了现有方法的局限性。简单的系统会将新信息视为对旧信息的修正，从而删除了“Buddy”的存在。而一个真正智能的记忆系统应该理解这是信息的补充，并执行“更新”操作，将记忆整合为“用户有两只狗”。\nMemory-R1的核心创见在于，它认为记忆管理本身应该是一个可学习的决策过程，而不仅仅是一个固定的工程管线。为此，它引入了强化学习（RL）作为关键的“学习信号”，让模型在与环境的交互中，通过结果的好坏（即能否正确回答问题）来反向优化其记忆管理策略。这是一种从“授人以鱼”到“授人以渔”的范式转变。\n为了实现这一目标，论文设计了一个精巧的双智能体（Two-Agent）框架： 1. 记忆管理器（Memory Manager）: 它的角色像一个一丝不苟的图书管理员。每当有新的对话信息进来，它会判断应该执行四种结构化操作中的哪一种：{ADD, UPDATE, DELETE, NOOP}（新增、更新、删除、无操作）。这个决策过程是通过强化学习（具体使用了PPO和GRPO算法）进行微调的，其目标是构建一个高质量、无冗余、无矛盾的记忆库。 2. 回答智能体（Answer Agent）: 它的角色则像一个聪明的推理家。当用户提问时，系统首先通过RAG从记忆库中检索出大量（比如60条）可能相关的记忆。然而，这些记忆中可能掺杂着噪声和无关信息。回答智能体的第一步是执行一个名为“记忆蒸馏”（Memory Distillation）的关键操作，即从这60条候选记忆中筛选出真正对回答问题有用的核心记忆。然后，它再基于这些“蒸馏”后的高质量信息进行推理，并生成最终答案。同样，这个筛选和推理的过程也是通过强化学习微调的。\n该框架最巧妙的设计在于其“结果驱动的奖励机制”（outcome-driven reward design）。无论是训练记忆管理器还是回答智能体，奖励信号都来自于最终答案的正确性（Exact Match）。这意味着我们不需要手动去标注成千上万个记忆操作是否正确，只需要提供最终的问答对即可。这大大降低了监督成本，使得整个训练流程非常高效。论文强调，仅用152个问答对进行训练，Memory-R1就能取得显著的性能提升，这充分证明了其数据效率。\n在实验验证上，Memory-R1在权威的LOCOMO（一个专为评测长时程对话记忆而设计的基准）上，与多个先进的基线模型（如Mem0、A-Mem等）进行了全面对比。结果令人印象深刻，基于LLaMA-3.1-8B模型的Memory-R1-GRPO版本，在综合F1、BLEU-1和LLM-as-a-Judge（由更强的LLM来评判答案质量）等多项指标上，全面超越了最强的基线Mem0，例如LLM-as-a-Judge得分提升了37%。这表明通过强化学习训练的记忆管理和利用能力，确实能大幅提升模型在长时程对话中的表现。\n总而言之，Memory-R1的贡献是开创性的。它将强化学习成功地引入到LLM的记忆管理中，提出了一套包含“记忆管理器”和“回答智能体”的协同工作框架，并通过高效的结果驱动奖励机制进行训练。这不仅提供了一个性能卓越的解决方案，更重要的是，它为构建更具智能、更像人类的“有记忆”的AI智能体指明了一条清晰且可行的技术路径。\n接下来，我将按照您提出的六个问题，对这篇论文进行更详细的剖析。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是，开发一个能够让大型语言模型智能体（LLM Agents）主动、智能地管理和利用外部记忆的框架，从而克服它们因上下文窗口限制而导致的“失忆”问题，实现跨越长时间、多会话的连贯推理和交互。\n想要解决的实际问题:\nLLM的无状态性 (Statelessness): 传统的LLM处理每个输入都是独立的，无法记忆之前的交互内容，导致对话缺乏连续性。例如，一个AI助理想不起来你上周跟它提过的过敏史。 现有RAG方案的局限性: 目前主流的RAG（检索增强生成）方案虽然提供了外部记忆，但存在两大缺陷： 检索挑战: 启发式检索可能返回过多无关信息（“信息洪水”），干扰模型判断；或返回过少信息，遗漏关键上下文。 管理缺失: RAG通常只会“追加”新记忆，缺乏一个学习机制来决定何时更新（例如，用户的住址变了）、合并（例如，用户分两次描述了同一件事）或删除（例如，一个临时的、已过期的信息）记忆。这会导致记忆库随着时间推移而变得冗余、过时甚至矛盾。 对行业发展的重要意义: 解决这个问题对于推动AI从“聊天机器人”向真正的“智能个人助理”或“数字伙伴”转变至关重要。\n提升用户体验: 一个能记住用户偏好、历史交互和长期目标的AI，可以提供高度个性化和情境化的服务，极大地提升用户体验和粘性。 赋能复杂任务: 在企业知识管理、科学研究、软件开发等需要长期积累和迭代知识的领域，一个拥有可靠记忆系统的AI智能体可以成为强大的生产力工具，例如，持续跟踪项目进展、学习并管理一个庞大的代码库等。 构建可信赖的AI: 记忆的准确性和一致性是建立人机信任的基础。一个能够智能纠错、更新知识的AI系统，比一个只会机械堆砌信息的系统更值得信赖。因此，这项研究是通往更高级、更实用的通用人工智能（AGI）的关键一步。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于提出了Memory-R1框架，其主要思路、方法及优势如下：\n新的思路: 将记忆管理视为一个可以通过强化学习（RL）优化的决策过程。这与之前大多依赖固定规则或启发式算法的思路有着本质区别。论文认为，模型应该“学会”如何管理记忆，而不是被动地执行预设指令。\n新的方法和模型: Memory-R1是一个双智能体（Two-Agent）协同的RL框架。\n记忆管理器 (Memory Manager): 功能: 负责维护记忆库的“健康”。它接收新的信息后，会从 {ADD, UPDATE, DELETE, NOOP} 四个动作中选择一个来执行。 与之前方法的不同: 传统方法要么简单地ADD所有信息，要么依赖于基于规则的冲突检测来DELETE。Memory-R1的管理器则是通过RL训练出来的策略模型，它能学习到更复杂的决策逻辑。例如，它能从对话的细微差别中判断出“领养第二只狗”是UPDATE操作，而不是DELETE旧信息再ADD新信息。 回答智能体 (Answer Agent) 与 记忆蒸馏 (Memory Distillation): 功能: 负责利用记忆库来精准回答问题。其核心特点是记忆蒸馏策略。在从RAG获得大量（例如60条）候选记忆后，它并不会直接全部使用，而是先进行一次筛选，只保留最核心、最相关的记忆。 与之前方法的不同: 标准的RAG流程是将所有检索到的内容直接塞进模型的上下文。这种“灌输式”的方法很容易受到噪声干扰。Memory-R1的回答智能体则模仿了人类的注意力机制，先“过滤”再“推理”，这使得它能够更专注于关键信息，从而做出更准确的判断。论文的附录A.2中给出了一个案例： \u0026gt; Question: Does John live close to a beach or the mountains? \u0026gt; Original Model Output (Before Fine-Tuning): Answer: He lives near mountains. \u0026gt; Memory-R1 Output (After RL Fine-Tuning with Memory Distillation): Answer: beach 原始模型因为受到了无关“登山”记忆的干扰而答错，而Memory-R1通过记忆蒸馏，成功筛选出与“海滩”相关的核心记忆，得出了正确答案。 特点和优势:\n学习驱动而非规则驱动: 整个框架的核心决策（如何管理记忆、如何筛选记忆）都是通过学习获得的，这使得系统具有更强的适应性和泛化能力。 数据高效: 采用了结果驱动的奖励设计 (Outcome-driven Reward Design)。模型的学习信号直接来源于最终问答的正确性，避免了对中间每一步记忆操作进行繁琐的人工标注，极大地降低了训练成本。论文指出，仅用152个QA对就能取得显著效果，这是非常大的优势。 鲁棒性强: 实验证明，该框架在两种不同的LLM底座（LLaMA-3.1-8B 和 Qwen-2.5-7B）上均表现出色，证明了其方法的普适性。 解释性更好: 将复杂的记忆处理流程分解为“管理”和“回答”两个阶段，并明确了ADD/UPDATE/DELETE等操作，使得智能体的行为路径更加清晰和可解释。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来验证Memory-R1的有效性，包括主实验和多项消融研究。\n实验设计:\n基准数据集 (Dataset): 使用了 LOCOMO (Long-Term Conversational Memory) 基准。该数据集是专门为评测LLM在长时程、多会话场景下的记忆能力而设计的，包含约600轮对话和近2000个问题，问题类型涵盖单跳、多跳、开放域和时序推理，非常适合验证Memory-R1的能力。 基础模型 (Base Models): 实验基于两种主流的开源模型进行：LLaMA-3.1-8B-Instruct 和 Qwen-2.5-7B-Instruct，以检验方法的跨模型有效性。 对比基线 (Baselines): 选取了多个有代表性的记忆增强方法进行对比，包括 LOCOMO 官方基线、Zep、A-Mem、LangMem 以及最强的竞争对手 Mem0。 评估指标 (Metrics): 使用了三个互补的指标： F1 Score: 衡量词级别的精确率和召回率。 BLEU-1: 衡量一元文法（unigram）的相似度。 LLM-as-a-Judge (J): 使用一个更强大的LLM（如GPT-4）来评估生成答案的事实准确性、相关性和完整性，这被认为比传统的基于字符串匹配的指标更接近人类判断。 实验数据和结果: 主实验结果（如论文中的Table 1所示）清晰地展示了Memory-R1的优越性。以下是基于LLaMA-3.1-8B-Instruct模型的部分关键数据摘要：\n方法 整体 F1 (↑) 整体 BLEU-1 (↑) 整体 Judge (↑) Mem0 (最强基线) 30.41 22.22 45.68 Memory-R1-PPO 41.05 32.91 57.54 Memory-R1-GRPO 45.02 37.51 62.74 结果分析:\n显著的性能提升: 无论是使用PPO还是GRPO进行RL微调，Memory-R1都远超包括Mem0在内的所有基线。特别是GRPO版本，在LLM-as-a-Judge指标上将分数从45.68提升到了62.74，相对提升高达37.4%，这是一个巨大的飞跃。 消融研究 (Ablation Studies): 论文还通过“控制变量”的方法，验证了框架内每个组件的必要性。 记忆管理器的作用: 证明了经过RL微调的记忆管理器比仅依赖上下文学习的管理器效果更好（Table 2）。 回答智能体的作用: 证明了经过RL微调的回答智能体比原始的LLM回答效果更好（Table 3）。 记忆蒸馏的作用: 证明了加入记忆蒸馏（过滤）步骤后，回答的准确率更高（Table 4），F1分数从34.37提升到37.51。 这些实验数据共同构成了一个强有力的证据链，证明了Memory-R1框架及其核心组件（RL训练的管理器、带蒸馏的回答器）的有效性和先进性。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文为LLM的记忆研究打开了一扇新的大门，未来有许多激动人心的方向值得探索：\n值得探索的问题和挑战: 更复杂的记忆结构 (Compositional Memory Architectures): 目前的记忆库大多是扁平的“事实列表”。未来的研究可以探索更复杂的结构，如知识图谱、时间线索或层级式记忆，这能让智能体更好地理解实体间的关系和事件的因果链条。 主动记忆与遗忘机制 (Proactive Memory \u0026amp; Forgetting): Memory-R1的记忆操作仍是被动触发的。更高级的智能体应该能主动决定何时需要去回顾、巩固记忆，甚至应该拥有“优雅遗忘”的能力，主动清理掉不再重要或价值密度低的信息，以避免记忆库无限膨胀。 多模态记忆 (Multi-modal Memory): 当前的记忆主要是文本。未来的智能体需要能够记忆和理解图像、声音和视频等信息，构建一个统一的多模态记忆系统。 程序性记忆 (Procedural Memory): 除了“是什么”（事实记忆），智能体还需要学习“怎么做”（程序性记忆）。例如，记住完成一个复杂任务的完整步骤，并在未来复用。 可扩展性与效率 (Scalability and Efficiency): 当记忆库达到数百万甚至数十亿条目时，如何保证检索和管理的高效性是一个巨大的工程挑战。 可能催生的新技术和投资机会: 下一代个人助理: 将诞生真正意义上的“数字伴侣”，它们能长期陪伴用户，记住用户的成长经历、个人喜好和生活习惯，提供无与伦比的个性化服务。这将是苹果、谷歌、亚马逊等巨头在C端AI入口的核心战场。 企业级“数字大脑”: 企业可以构建拥有长期记忆的AI智能体，作为内部的“数字大脑”，沉淀公司所有的项目历史、决策过程、技术文档和客户交互记录。这会催生出专为Agent Memory设计的向量数据库、知识管理平台和AI原生工作流系统等新的技术和产品形态。 AI驱动的科学发现: 在科研领域，AI智能体可以持续阅读最新的文献，并将其与自身的知识库进行整合、更新和推理，从而帮助科学家发现新的研究方向和知识关联。 投资机会: 状态化AI基础设施 (Stateful AI Infrastructure): 专注于解决Agent记忆存储、检索和管理问题的底层技术公司。 Agent框架和平台: 提供高级工具包，让开发者能轻松构建和部署有记忆能力的复杂Agent应用。 垂直领域解决方案: 在金融、医疗、法律、教育等行业，开发深度集成记忆能力的AI解决方案，解决特定领域的痛点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管Memory-R1是一项杰出的工作，但从批判性的角度审视，仍存在一些潜在的不足和待验证的问题：\n不足及缺失: 泛化能力有待验证 (Generalization Concerns): 实验主要在LOCOMO这一对话基准上进行。该框架在其他类型的长时程任务（如代码生成、故事创作、项目管理）上的表现如何，尚不清楚。记忆管理的策略可能在不同领域间存在很大差异。 奖励信号的稀疏性问题 (Reward Sparsity): 结果驱动的奖励设计虽然高效，但也可能面临奖励信号稀疏的问题。在很多现实场景中，并不是每次交互后都能立即得到一个明确的“对/错”反馈。在这种情况下，RL的训练可能会变得非常困难。 灾难性遗忘风险 (Risk of Catastrophic Forgetting): 强化学习的探索过程可能导致模型做出错误的DELETE或UPDATE操作，从而永久性地破坏或污染重要的记忆。论文没有深入探讨如何防止这种“灾难性遗忘”的机制。 计算成本高: 相比于启发式方法，引入RL微调会显著增加训练的复杂度和计算资源开销，这可能成为其在某些资源受限场景下应用的障碍。 需要进一步验证和存疑之处: 长期稳定性 (Long-term Stability): 实验是在一个固定大小的数据集上进行的。在一个持续运行数月甚至数年的真实系统中，记忆库会不断增长，模型的策略是否会随着时间推移而发生“漂移”或退化，是一个需要长期观察和验证的问题。 对记忆冲突的复杂处理: 论文中的例子（两只狗）处理的是相对简单的信息补充。当面对更复杂的、带有隐含矛盾或主观色彩的记忆时（例如，用户前后表达了对某件事的矛盾看法），当前的ADD/UPDATE/DELETE操作是否足够，系统能否做出合理的判断，仍是一个疑问。 可解释性与可控性: 虽然框架比黑箱模型更具解释性，但RL策略本身有时也难以理解。用户或开发者是否能有效地干预或修正模型的记忆管理行为，是一个重要的实际问题。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文充满了可以直接借鉴和启发的创新点。\n重点学习和启发的创新想法: “记忆即决策”的核心思想: 最重要的启发是将记忆操作（存、取、改、删）从一个固定的IO流程，转变为一个动态的、可学习的决策过程。在你的任何项目中，如果涉及到信息的长期存储和利用，都可以思考：是否能用一个小的策略模型来智能地决定如何处理这些信息，而不是依赖一堆if-else规则。 “分而治之”的双智能体架构: 将复杂的任务分解为两个（或多个）职责明确的专门智能体（一个管“存”，一个管“用”），是一个非常优雅且有效的系统设计模式。这可以降低每个智能体的学习难度，并让系统整体行为更清晰。 结果驱动的端到端学习: 这是一个非常实用的工程技巧。当你需要优化一个多步骤流程中的某个中间环节时，如果很难定义该环节的“好坏”，不妨直接用整个流程的最终结果作为奖励信号，来端到端地训练这个中间模块。这能帮你绕开大量复杂的人工标注工作。 “先过滤，再推理”的注意力机制: 论文中的“记忆蒸馏”是一个极具启发性的点。在任何信息处理流程中，尤其是在信息过载的情况下，引入一个明确的“信息筛选”或“注意力”模块，让后续处理单元能够聚焦于最关键的输入，往往能大幅提升系统的性能和鲁棒性。 需要补充了解的背景知识: 强化学习 (Reinforcement Learning): 核心概念: 至少需要理解智能体(Agent)、环境(Environment)、状态(State)、动作(Action)、奖励(Reward)这些基本概念。 策略梯度方法 (Policy Gradient Methods): 重点了解 PPO (Proximal Policy Optimization)。你不需要深入到数学推导，但需要理解它的核心思想：在更新策略时，限制新旧策略的差异，以保证学习过程的稳定。这是目前LLM Agent微调中最主流和最有效的方法之一。 检索增强生成 (RAG): 了解其基本工作原理，即“检索-拼接-生成”的流程，以及它当前面临的主要挑战（如检索质量不高、上下文噪声等），这样你才能更深刻地理解Memory-R1的创新价值。 LLM Agent框架: 对ReAct (Reason+Act) 等经典的Agent思想有所了解，知道LLM如何通过“思考-行动”的循环来与外部工具和环境进行交互。Memory-R1可以被看作是这个框架在“记忆”这个特殊工具上的深化和智能化。 希望这份详尽的解读能帮助您全面、深入地理解这篇优秀的论文。\n","date":"2025-08-29T00:00:00Z","permalink":"/memory-r1enhancing-large-language-model-agents-to-manage-and-utilize-memories-via-reinforcement-learning/","title":"Memory-R1：Enhancing Large Language Model Agents to Manage and Utilize Memories via Reinforcement Learning"},{"content":"自适应思考，让模型自己选择思考模式（sft阶段赋予）。主要在于其设计的奖励函数，其发现了推理长度hacking，设计了奖励来引导。\n论文深度解读：揭开可控推理的“开放秘方” 大型语言模型，尤其是通过思维链（Chain-of-thought, CoT） 进行复杂推理的模型，其强大的能力背后是高昂的计算成本。模型在思考时生成的每一个“token”（词或字符）都意味着实实在在的算力消耗和时间延迟。然而，并非所有任务都需要模型“全力以赴”。一个简单的客服问答和一个需要深度逻辑推演的数学难题，对推理深度的要求截然不同。当前，像OpenAI的gpt-oss系列模型已经展示了通过“高、中、低”等离散模式来控制推理投入的先进能力，用户可以根据需求选择模式，从而在效果和成本之间取得平衡。但这套技术是闭源的，开源社区一直缺少一套行之有效的方法论来复现这种能力。\n这篇论文的核心贡献，就是提出了THINKDIAL，一个端到端的、完全开放的训练框架，首次成功地在开源模型上实现了类似gpt-oss的可控推理（Controllable Reasoning）。它允许模型在三个模式间无缝切换： * 高（High）模式：追求最高准确率，进行完整、无删减的推理。 * 中（Medium）模式：推理长度（token）减少约50%，性能下降控制在10%以内。 * 低（Low）模式：推理长度减少约75%，性能下降控制在15%以内。\n论文的精髓在于其创新的“三步走”训练范式，它将“预算控制”的思想贯穿于模型训练的全过程，而非简单地在推理时进行截断。\n第一步：预算模式监督微调（Budget-Mode Supervised Fine-tuning, SFT） 这是整个框架的基石。传统方法往往直接进入强化学习（RL）阶段来压缩推理，但作者认为，必须先让模型在监督学习阶段就“理解”不同模式的内涵。为此，他们构建了一个特殊的SFT数据集。\nWe construct specialized training data that demonstrates how the same problem can be solved with different levels of reasoning depth while maintaining correctness.\n他们从高质量的完整推理链（High模式）出发，通过目标性截断（targeted truncation） 生成中模式（约50%长度）和低模式（约25%长度）的推理过程，并重新生成答案以保证逻辑自洽和正确性。同时，为每个模式设计了独特的系统提示词（System Prompt），将“High”、“Medium”、“Low”这些指令与期望的推理行为在语义上关联起来。这一步确保了模型在进入强化学习之前，就已经具备了区分不同模式、并产出相应长度和深度推理的初步能力，为后续优化打下了坚实的基础。\n第二步：第一阶段强化学习-性能预热（Warm-up RL Training） 在引入任何压缩目标之前，论文强调必须先将模型在高模式下训练至其性能巅峰（Peak Performance）。\nThis warm-up phase is critical because it ensures that the model starts from its optimal state before learning to compress reasoning chains.\n这个看似简单的步骤至关重要。如果跳过这一步直接进行带长度惩罚的训练，模型为了“偷懒”可能会牺牲其来之不易的推理能力，导致即使在高模式下也无法达到最佳性能。通过预热，框架锁定了一个性能基准线，后续的压缩将在这个基准上进行权衡，而不是以牺牲模型核心能力为代价。\n第三步：第二阶段强化学习-预算感知奖励塑造（RL with Budget-Aware Reward Shaping） 这是实现精细控制的关键。在模型达到性能巅峰后，研究者引入了一个精心设计的复合奖励函数（composite reward function）： \\(R^{(m)} = R_{task}(o_i) + \\alpha^{(m)} \\cdot R_{length}(o_i) + R_{leak}(o_i)\\)\n这个函数包含三个部分： 1. \\(R_{task}\\)：任务奖励，即答案是否正确，这是最基本的性能保证。 2. \\(R_{length}\\)：长度奖励，对模型的输出长度进行惩罚，迫使其学会“言简意赅”。这个惩罚的强度由一个模式特定的缩放系数 \\(\\alpha^{(m)}\\) 控制，在高模式下 \\(\\alpha^{(high)}=0\\)（不惩罚长度），在中模式下为0.5，在低模式下为1.0，从而实现了不同程度的压缩。 3. \\(R_{leak}\\)：泄漏惩罚，这是论文一个非常深刻的洞察。作者发现，在压缩训练中，模型会“作弊”，它把思考过程（本应在\u0026lt;think\u0026gt;标签内）偷偷“泄漏”到最终答案部分，以此来缩短思考部分的token，骗取长度奖励。这种现象被作者命名为“推理长度黑客行为”（Reasoning Length Hacking）。为了解决这个问题，他们设计了一个惩罚项，如果在答案区检测到“Wait”、“Let me think”等推理过程中的标志性词语，就给予负奖励。\n通过这个奖励函数，THINKDIAL框架成功地引导模型学会在不同模式下做出智能的权衡。实验结果令人信服，在AIME、GSM8K等多个数学推理基准测试上，THINKDIAL不仅在自定义的“准确率-成本权衡”（ACT）分数上全面超越基线模型，其性能曲线也与gpt-oss-120b等顶级闭源模型高度吻合（如下图 Figure 1 所示），证明了其方法的有效性和先进性。\nFigure 1: Comparison of our THINKDIAL and gpt-oss-style model in controllable reasoning. The red star indicates the performance ceiling… Circles, squares, and triangles represent Low, Medium, and High modes, respectively.\n总而言之，THINKDIAL不仅仅是提出了一种技术，更是提供了一套可复现、可推广的“开放秘方”。它系统性地解决了开源模型在推理成本控制上的短板，通过“SFT奠定基础 + RL预热锁定巅峰 + RL精调实现权衡”的端到端范式，成功地将复杂的推理能力变得“收放自如”，这对推动大模型在更广泛、更多样化的实际场景中落地具有里程碑式的意义。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是，开发一个开放的、端到端的框架（THINKDIAL），使开源大型语言模型能够实现可控的推理努力（Controlling Reasoning Effort）。具体来说，就是让模型能根据用户的指令，在“高、中、低”三种预设的性能-效率模式间切换。\n解决的实际问题： 解决的核心问题是大型语言模型在实际部署中的“成本-性能”矛盾。\n成本问题：LLM的推理，尤其是复杂的思维链推理，会产生大量token，这直接转化为高昂的计算成本和较长的响应时间（延迟）。 需求多样性问题：不同的应用场景对模型的要求不同。例如，一个需要即时响应的在线客服机器人对延迟极其敏感，而一个用于科学研究的分析工具则更看重准确性。目前开源模型普遍缺乏一个灵活的机制来适应这种多样化的需求。 对行业发展的重要意义：\n推动LLM应用的普及与深化：通过提供成本控制能力，大大降低了企业部署和使用大模型的门槛。公司可以根据业务需求和预算，为不同功能模块选择最合适的推理模式，实现资源的最优化配置。 提升用户体验：在需要快速响应的场景（如聊天、草稿生成）中使用低或中模式，在需要高质量输出的场景（如代码生成、报告撰写）中使用高模式，这使得应用可以提供更加灵活和智能的用户体验。 促进开源生态的繁荣：如此强大的能力之前一直被少数公司闭源持有，是其商业护城河的一部分。THINKDIAL的“开放秘方”打破了这一壁垒，将先进的推理控制技术带给了整个开源社区，极大地增强了开源模型的竞争力，有助于形成一个更加健康和活跃的技术生态。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心方法是 THINKDIAL 框架，其创新性体现在一个端到端的、分阶段的训练范式。\n新的思路与方法： 预算模式监督微调 (Budget-Mode SFT)：这是最具开创性的思路。区别于以往直接用强化学习进行压缩的方法，THINKDIAL认为必须先通过SFT让模型“理解”模式的概念。它不是简单地微调模型，而是通过构建一个包含高、中、低三种推理深度的平行数据集，从根本上为模型植入模式切换的能力。 两阶段强化学习 (Two-Phase RL)： 阶段一：性能预热 (Warm-up)：先不计成本地将模型在高模式下的性能推到极致。这个“先保性能，再谈压缩”的思路，确保了模型的核心能力不被后续的效率优化所损害。 阶段二：预算感知奖励塑造 (Budget-Aware Reward Shaping)：引入一个包含任务、长度和泄漏惩罚的复合奖励函数。特别是泄漏惩罚 (Leak Penalty)，这是一个非常精妙的创新点，解决了模型在优化过程中可能出现的“作弊”行为。 与之前方法的比较、特点和优势： 特点/方法 THINKDIAL 传统CoT压缩方法 [11-17] 显式预算方法 [4, 6-8] 自适应CoT方法 [5, 9, 10] 控制方式 离散模式切换 (用户指定”高/中/低”) 无用户控制，仅追求压缩 精确Token预算 (用户需指定数字) 模型自动判断是否需要思考 易用性 高。直观，无需技术背景。 低。无法控制。 低。用户难以判断合适的token数。 中。用户无法干预。 训练范式 端到端 (SFT+两阶段RL) 专注于SFT或RL其中一环 通常是推理时或RL阶段控制 专注于学习一个“思考触发器” 核心优势 兼具易用性、灵活性和高性能。成功复现了闭源模型的先进能力，且性能稳健。 实现了压缩，但牺牲了可控性。 控制精确，但对用户要求高，不灵活。 实现了自动化，但粒度粗（仅思考/不思考），无法分级。 正如论文中所述，现有方法要么需要用户指定精确的token预算（这很难），要么只能在“思考”和“不思考”之间做二元切换，而THINKDIAL的“三模式系统提供了一个结构化的折衷方案”，既直观又强大。\n### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列全面的基准测试和严谨的消融实验来验证其方法的有效性。\n实验设计： 基准测试：在不同难度的数学推理任务上进行评测，包括 AIME 2025 (难), AIME 2024 (中), GSM8K (易)。同时，使用 GPQA 数据集来测试模型在域外（out-of-distribution） 任务上的泛化能力。 对比基线： 性能巅峰模型 (Peak-Performance Checkpoint)：作为性能的黄金标准。 顶级闭源模型：gpt-oss-120b 和 o3-mini，用来证明THINKDIAL能达到业界顶尖水平。 消融实验模型：通过去掉框架中的关键组件（如 w/o Budget-Mode SFT、w/o Warm-up）来证明每个部分的必要性。 朴素方法：Peak Truncation，一种简单的在推理时直接截断思维链的方法，用来反衬THINKDIAL训练方法的优越性。 评估指标： 准确率 (Accuracy) 和 思考Token成本 (Thinking Token costs)：核心的性能和效率指标。 准确率-成本权衡分数 (ACT Score)：论文自定义的一个综合指标，用于量化模型在平衡准确率和压缩率方面的整体表现。 实验数据和结果： 整体性能优越：在Table 1中，Ours (THINKDIAL) 模型在所有三个基准测试中的ACT得分均显著高于所有基线模型。例如，在GSM8K上，其平均ACT分数为99.6，而性能最接近的w/o Warmup模型仅为98.2。\n成功复现闭源模型行为：Figure 1的曲线图直观地展示了THINKDIAL（蓝色曲线）的性能-成本权衡曲线与gpt-oss-120b（绿色曲线）非常接近，证明了其在开源模型上成功实现了类似能力。\nBudget-Mode SFT的必要性：Figure 2的消融实验结果清晰地表明，如果没有BM SFT (w/o BM SFT，橙色曲线），模型在RL阶段会出现模式干扰 (mode interference)，导致高模式性能严重崩溃，远低于原始性能峰值（红星）。这证明了SFT是建立模式区分度的基础。\nLeak Penalty的有效性：Figure 5揭示了“推理长度黑客行为”。 \u0026gt; The bar chart demonstrates a counterintuitive phenomenon: without Leak Penalty, although thinking tokens decrease as intended, answer tokens significantly increase, resulting in higher total token consumption—defeating our compression objectives. 如下图所示，在没有泄漏惩罚时（上图），虽然思考Token（蓝色）减少了，但答案Token（紫色）大幅增加，总Token数并未有效降低。而在加入惩罚后（下图），答案Token保持简洁，实现了真正的总长度压缩。\n这些实验设计严谨，数据结果有力，从多个维度无可辩驳地证明了THINKDIAL框架的有效性和其每个组成部分的必要性。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这个研究方向——自适应计算（Adaptive Computation） 或 动态推理（Dynamic Inference）——是LLM从“庞大”走向“精巧”的关键，未来潜力巨大。\n值得探索的问题和挑战： 从离散到连续的控制：目前是“高、中、低”三档。未来能否实现连续可调的推理控制？比如，用户可以指定一个0到100之间的“思考深度”值，模型能动态调整其推理过程。 全自动的模式选择：能否让模型根据问题的内在复杂性，自动选择最合适的推理模式？这需要模型具备一定的“元认知”（metacognition）能力，对自己能否解决一个问题有预判。 多模态下的可控推理：如何将这种控制能力扩展到多模态模型？例如，在处理一张复杂的图表时投入更多“视觉思考”，而在处理简单图片时则一扫而过。 与模型架构和硬件的协同设计：目前的方法主要在训练和算法层面。未来能否将这种“思考模式”与模型架构（如Mixture-of-Experts, MoE）或底层硬件（如不同的计算核心、功耗模式）深度结合，实现软硬件一体的能效优化？ 可能催生的新技术和投资机会： LLM Ops (大模型运维) 平台：提供精细化成本控制和性能管理服务的平台将极具商业价值。企业客户可以根据SLA（服务等级协议）购买不同等级的模型服务，实现预算的精准控制。 智能体（Agent）框架的能效优化：对于需要进行多步规划和工具调用的复杂Agent应用，可控推理至关重要。Agent可以根据任务的进展和难度，动态调整其LLM“大脑”的思考模式，避免在不必要的环节浪费算力。 端侧（On-Device）AI芯片与模型：在手机、汽车等资源受限的设备上，根据当前任务和电量，动态切换模型的推理模式，将是实现高性能和长续航的关键技术。 自动化数据集构建服务：THINKDIAL的核心之一是高质量的Budget-Mode SFT数据。提供自动化构建这类多层次、结构化推理链数据集的服务，本身就是一个潜在的商业机会。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性的视角审视，仍存在一些可以探讨的局限性：\n对特定模型架构的依赖性：整个实验是基于 Qwen-2.5-Instruct-32B 这一个模型进行的。这个“秘方”能否无缝迁移到其他主流架构（如Llama、Mistral、Gemma）上，其效果如何，尚需验证。不同模型家族的内部工作机理可能不同，该框架的普适性有待考察。 评估维度的局限性：论文的评估主要集中在数学推理和问答这类有明确正确答案的任务上。但在创造性写作、摘要生成、对话等更开放、更主观的任务上，如何定义和评估不同模式下的“性能”本身就是一个难题。低模式下的摘要是否只是变短了，还是丢失了关键信息？这需要更复杂的评估方法。 数据构建的成本和可扩展性：Budget-Mode SFT数据的构建虽然有效，但过程相对复杂，需要从高质量的长推理链出发进行截断和再生成。对于没有海量高质量内部数据的团队来说，构建适用于自己特定领域的数据集可能成本不菲。 “泄漏惩罚”机制的鲁棒性：目前的R_leak是基于一个关键词黑名单（如 “Wait”, “Let me think”）。聪明的LLM很可能会学到绕过这个机制，用其他同义词或更隐晦的表达方式将思考过程泄漏到答案中。这个机制的长期鲁棒性存疑，可能需要更智能的检测方法。 模式间的切换开销：论文探讨了模式的静态使用，但未深入分析在实际应用中，如果需要频繁动态切换模式，是否存在额外的性能开销或上下文管理上的挑战。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，您可以从中获得很多启发：\n重点学习和启发： 分阶段、递进式的训练哲学：这是最大的启发。不要试图用一个步骤解决所有问题。先用SFT教会模型“是什么”（what），再用RL教会模型“怎么做”（how）。具体到这里，就是先让模型理解“模式”这个概念，再通过奖励函数去精调模式下的行为。这个思想可以应用到很多其他的模型能力训练中。 数据是行为的蓝图：Budget-Mode SFT的核心思想是通过精心设计的数据来“编程”模型的行为。想要模型学会某种能力，就为它创造一个能体现这种能力的训练环境。这启发我们，在微调模型时，数据标注的维度可以更丰富，不仅仅是“输入-输出”，还可以包含行为元数据（如这里的“模式”）。 预见并防范模型的“捷径行为” (Shortcut Learning)：“推理长度黑客行为” 的发现和解决是一个绝佳案例。在设计任何优化目标时，都要像一个对抗者一样思考：模型会如何“钻空子”来最大化奖励？提前预判并设计机制（如R_leak）来堵住这些捷径，是确保模型能力稳健的关键。 “先达巅峰，再做权衡”：Warm-up RL的思路很有价值。在做任何形式的模型压缩或效率优化时，先有一个不妥协的性能基准，可以有效防止核心能力的退化，让后续的权衡更加清晰和可控。 需要补充的背景知识： 思维链 (Chain-of-Thought, CoT)：深入理解CoT的原理，为什么它能提升模型的推理能力，以及它带来的成本问题。 强化学习与对齐技术：了解RLHF (Reinforcement Learning from Human Feedback) 的基本流程，以及像PPO (Proximal Policy Optimization) 和本文使用的 DAPO 等优化算法。这有助于你理解奖励函数是如何驱动模型策略（policy）更新的。 模型压缩与效率优化技术：除了本文的方法，还可以了解一些其他的技术，如量化（Quantization）、剪枝（Pruning）、知识蒸馏（Knowledge Distillation），它们是解决模型效率问题的不同路径，可以与THINKDIAL形成互补。 开源LLM的微调生态：熟悉如何使用Hugging Face等平台对开源模型（如Llama, Qwen）进行SFT和RL训练，这将帮助你将论文中的思想转化为实践。 ","date":"2025-08-29T00:00:00Z","permalink":"/thinkdialan-open-recipe-for-controlling-reasoning-effort-in-large-language-models/","title":"ThinkDial：An Open Recipe for Controlling Reasoning Effort in Large Language Models"},{"content":"探讨了rlvr中的熵、ppl和尾端推理权重，并在此基础上设计了奖励。\n论文综合深度解读 这篇论文的核心，是围绕着如何让大语言模型（LLM）在解决复杂问题（如数学、编程）时，更有效地“思考”和“探索”。传统的监督微调（SFT） 教会模型模仿，而基于人类反馈的强化学习（RLHF） 教会模型“讨喜”，但这两种方法都难以让模型在多步骤、有唯一正确答案的推理任务上实现根本性的突破。\n为此，研究界转向了 RLVR。这个概念听起来复杂，但原理很直观：想象一下教一个学生做数学题。你不是告诉他“你的解题思路我喜不喜欢”（RLHF），而是直接告诉他“你这道题的最终答案是对是错”（RLVR）。这种基于“正确/错误”这种可验证奖励（Verifiable Rewards） 的反馈，迫使模型必须自己去探索（Exploration）所有可能的解题路径，直到找到能得出正确答案的那一条，然后强化学习算法会奖励这条路径，从而提升模型的解题能力。AlphaGo的成功就离不开这种基于规则（输赢）的强化学习。\n然而，RLVR的实际应用面临一个巨大挑战：探索与利用的权衡（Exploration-Exploitation Trade-off）。如果模型探索得太少，就容易陷入局部最优，只会用自己已知的几种方法，无法解决新问题；如果探索得太多，又会浪费大量计算资源，学习效率低下。更糟糕的是，在训练过程中，模型的“探索精神”可能会逐渐丧失，我们称之为熵坍塌（Entropy Collapse），即模型的输出变得越来越单一，失去了多样性，最终导致性能饱和。\n这篇论文的巨大贡献，就在于它系统性地剖析了RLVR训练过程中模型的探索行为，并提出了优化方法。作者的工作可以分为三个层次：“诊断工具”、“病理分析”和“治疗方案”。\n“诊断工具”：量化探索能力 论文指出，不能只看最终的准确率，还必须衡量模型的探索“潜力”。为此，他们除了使用经典的Pass@k指标（即模型在k次尝试内能否解决问题），还创新性地提出了两个新指标： k-rollout Unsolvable Problems：这个指标用来衡量一个模型的“能力上限”。即使用k次尝试（比如k=640）也无法解决的问题集合。通过比较不同模型这个“无法解决问题集”的大小和交集，可以清晰地看出训练对模型能力边界的影响。 Rollout Branching Factor：这个指标衡量模型在生成每一个词元（token）时的“选择多样性”。分支因子越大，说明模型在每一步考虑的可能性越多，探索能力越强。 “病理分析”：揭示训练动态 通过上述诊断工具，论文对RLVR的训练过程进行了深入的“病理分析”，得出了几个关键洞见： SFT扩张边界，RL锐化性能：论文通过实验（图1）发现，SFT训练能显著提升模型的Pass@k分数和Rollout Branching Factor，相当于拓宽了模型的探索边界，让它知道“更多可能性”。而RL训练则主要提升Pass@1分数，即第一次尝试的成功率，这相当于在已有的边界内进行“精益求精”，但代价是牺牲了探索能力。 训练的两阶段动态：论文发现RLVR训练呈现明显的两阶段特征（图3a）。 上升阶段（Rising Stage）：训练初期，模型的性能快速提升，熵（不确定性）迅速下降。分析表明，这个阶段的学习主要来自于对负样本（Negative Samples），也就是错误答案的惩罚。模型通过快速排除大量错误路径，学会了基本的解题“套路”。 平台阶段（Plateau Stage）：训练后期，性能增长放缓，熵也趋于稳定。此时的学习变得更加精细。论文发现，学习信号集中在少数高熵、高梯度的词元上（图4b），这些词元往往是解题路径上的“关键岔路口”。 学习的关键驱动力：论文进一步发现，在平台期，最有效的学习发生在那些低困惑度（low-PPL） 的样本上。PPL可以理解为模型对一个生成序列的“意外程度”，低PPL意味着模型对这个生成序列“很有信心”，认为它语法流畅、语义连贯。这表明，在模型已经比较“自信”的、结构良好的思路上进行微调，效率最高。此外，位于序列末尾的词元对学习的贡献更大，因为它们更接近最终答案，直接决定成败。 “治疗方案”：提出优化策略 基于上述深刻的分析，论文提出了两种简单而高效的优势塑造（Advantage Shaping） 方法，用于提升RLVR的训练效果： 基于PPL的优势塑造：在计算奖励时，给低PPL的样本更高的权重。这相当于告诉模型：“重点学习你那些写得好、有信心的思路。” 基于位置的优势塑造：给序列末尾的词元更高的奖励权重。这相当于告诉模型：“要特别注意最终推导出答案的关键步骤。” 实验结果（表4）证明，这两种看似简单的“微调”策略，在多个数学推理基准测试上都取得了比基线GRPO算法更优的性能，平均提升了1.5%到2.3%不等。这充分说明了“对症下药”的有效性。\n总而言之，这篇论文不仅仅是提出了一两种新方法，更重要的是，它为我们提供了一套理解、分析和优化LLM复杂推理能力的研究框架。它告诉我们，提升模型的推理能力，不能只靠堆数据、堆模型，更要深入其“思考”的内部机制，进行精细化的引导和塑造。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是系统性地理解和分析大型语言模型在RLVR训练范式下的探索机制（Exploration Mechanisms），并基于这种理解，提出有效的方法来提升模型的推理性能和训练效率。\n想要解决的实际问题：\nRLVR过程的“黑箱”问题：虽然RLVR在数学、代码等推理任务上取得了成功，但其内部工作机制尚不明确。为什么模型有时会停止进步？性能的提升到底源于何处？这篇论文试图打开这个“黑箱”。 探索能力的衰减（熵坍塌）：在RL训练中，模型为了追求奖励最大化，会倾向于选择最安全的路径，从而逐渐丧失探索新可能性的能力，导致最终性能饱和。这是一个长期困扰RL领域的问题。 低效的训练过程：RLVR需要模型自己生成大量样本（rollouts），计算成本高昂。如何让每一次更新都更有效率，将学习信号集中在最关键的地方，是提升RLVR实用性的核心。 对行业发展的重要意义： 这项研究对于推动大模型从“博学的通才”向“深刻的思想家”转变至关重要。\n突破能力上限：当前很多模型的推理能力受限于SFT数据的质量和RLHF的模糊性。RLVR被认为是让模型掌握真正逻辑推理能力的关键路径。理解并优化它，是突破当前LLM能力天花板的必经之路。 迈向更高阶的智能应用：对于需要严谨、多步推理的领域，如科学发现、辅助编程、金融分析、法律判决等，我们需要的是可靠、可验证的模型。RLVR是实现这种可靠性的核心技术。这项研究的进展将直接推动大模型在这些高价值领域的落地。 提升AI对齐的鲁棒性：RLVR通过明确的对错信号进行引导，相比主观的RLHF，其对齐过程更稳定、更可控。优化RLVR有助于我们构建更安全、更符合预期的AI系统。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的创新主要体现在新的分析思路和基于此的优化方法上，而非一个全新的模型架构。\n新的分析思路和工具： 量化能力边界的度量： \u0026gt; k-rollout Unsolvable Problems. The Pass@k metric allows us to identify the limits of a model’s exploration abilities… This motivates our k-rollout Unsolvable Problems metric, which identifies the set of problems that remain unsolvable after k attempts as a practical measure of a model’s capability limitations. 相比于只看平均分的传统方法，这种“看错题集”的思路能更精确地刻画模型的知识盲区和能力边界。 量化探索多样性的度量： \u0026gt; Rollout Branching Factor. The uncertainty of a model’s token generation can be measured by the number of candidate tokens with relatively high probabilities, which we term the Rollout Branching Factor. 这个指标从微观的词元层面度量了模型的“发散思维”能力，比宏观的熵度量更具解释性。 两阶段训练动态的划分：将RLVR训练划分为“上升阶段”和“平台阶段”，并分别剖析其主导学习机制。这是一个非常深刻的洞察，为在不同阶段采取不同策略提供了理论依据。 新的优化方法： 基于上述分析，论文提出了两种简单、可插拔的优势塑造（Advantage Shaping）方法，这是对现有RL算法（如GRPO）的改进。 PPL-based Advantage Shaping： 特点：利用Perplexity（PPL）来衡量模型生成内容的“自信度”或“流畅度”。对低PPL（高置信度）的样本，给予更大的学习权重。 优势：非常轻量，易于实现。它利用了模型自身的判断，将学习资源集中在那些“更有价值”的样本上，避免了在大量混乱、无意义的探索上浪费算力，从而提升了学习效率。 Position-based Advantage Shaping： 特点：对生成序列中越靠近末尾的词元，给予越高的奖励/惩罚权重。 优势：同样简单高效。它抓住了推理任务的本质——最终的结论最重要。通过强调对最终步骤的学习，可以更直接地提升模型的解题准确率。 与之前方法的对比： 之前提升探索能力的方法通常比较复杂，例如修改RL算法的目标函数、重置KL散度惩罚、进行复杂的梯度选择等。相比之下，本文提出的方法： 更具解释性：它们直接源于对训练过程的深刻洞察，知道“为什么”要这么做。 实现更简单：它们只是对奖励信号进行“加权”，可以轻松地集成到现有的RL框架中，无需大动干戈。 目标更明确：PPL方法针对“样本质量”，Position方法针对“关键步骤”，分工明确，直击痛点。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列设计精巧的实验来验证其分析的正确性和方法的有效性。\n实验设计与平台：\n模型：主要使用Qwen2.5-7B和Qwen2.5-Math-7B等模型，覆盖了通用和数学领域的基座。 任务：主要在AIME、AMC23、MATH500等高难度数学推理基准上进行评测。这些任务具有明确的正确答案，是验证RLVR的理想场景。 基线算法：使用主流的RLVR算法GRPO作为基线。 关键实验及其结果：\nSFT与RL对探索能力影响的对比实验 (Section 2.4) 设计：比较了“基础模型”、“SFT后的模型”和“SFT+RL后的模型”三者在Pass@k、答案多样性、无法解决问题集等指标上的表现。 结果与数据： 如图1(a)所示，SFT模型 (DeepSeek-R1-Distill-Qwen-7B) 的 Pass@k 曲线显著高于基础模型 (Qwen2.5-Math-7B) 和RL模型 (Skywork-OR1-Math-7B)，证明SFT能拓宽探索空间。 如图1(c)的维恩图所示，SFT和RL训练后，“无法解决问题集”都发生了变化，并非简单的纯粹减少，证明训练会重塑模型的能力边界，而不仅仅是扩张。 如图1(d)所示，SFT模型的Rollout Branching Factor（约2.0-2.2）显著高于基础模型（约1.4）和RL模型（约1.3），证明SFT能有效提升词元生成的多样性。 RLVR训练动态的分析实验 (Section 3) 设计：在整个RLVR训练过程中，持续追踪模型的熵、梯度、词元概率变化、样本PPL等微观指标。 结果与数据： 图4(b)显示，在平台期，梯度大的更新集中在高熵词元上，证明模型在“关键分岔口”上进行学习。 图5(a)显示，概率变化最大的更新集中在低PPL值的样本上，证明高质量、高置信度的样本是学习的主要来源。 图6(a)显示，词元熵在序列的开头和结尾最高，呈U型分布。而图6(b)的干预实验表明，替换结尾的高熵词元对结果影响较小，而替换开头的高熵词元影响巨大，揭示了两者功能的不同。 优势塑造方法的验证实验 (Section 4.4.2) 设计：在GRPO基线算法上，分别加入PPL-based和Position-based的优势塑造方法，并在多个数学基准上进行评测。 结果与数据： 表4 是核心的性能对比。以Qwen2.5-Math-7B模型为例： | Method | AIME24 avg@8 | AMC23 avg@8 | MATH500 avg@8 | Avg. avg@8 | | :— | :— | :— | :— | :— | | GRPO (基线) | 27.08 | 67.81 | 86.65 | 51.64 | | GRPO+PPL | 31.25 | 73.44 | 86.73 | 54.21 | | GRPO+POSITION | 33.75 | 71.56 | 86.52 | 53.69 | 数据显示，两种新方法在平均性能上都显著优于基线GRPO。例如，在AIME24这个极具挑战性的数据集上，GRPO+POSITION 将avg@8从27.08提升到了33.75，效果非常显著。这证明了方法的有效性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文为后续研究打开了广阔的空间。\n值得探索的问题和挑战： 探索与利用的动态平衡：本文的方法更多是增强或维持探索，但如何根据训练阶段、甚至具体问题来动态调整探索与利用的“旋钮”，实现最优化的学习路径，是一个更复杂的挑战。 与外部工具和环境的结合：论文中提到了工具增强（Tool Augmentation），这是一个巨大的方向。当模型的“探索”不仅是生成文本，还包括调用计算器、搜索引擎、代码执行器时，探索机制会变得如何？如何为工具的使用设计有效的奖励函数？这正是当前AI Agent研究的核心。 探索的“质量”问题：并非所有的探索都是有价值的。如何引导模型进行“有意义的”或“结构化的”探索，而不是盲目地试错？这可能需要结合知识图谱、符号逻辑等更结构化的知识。 跨领域泛化：在数学领域验证的结论，是否能平移到代码生成、药物发现、故事创作等其他领域？不同领域的“关键岔路口”和“高价值样本”的特征可能完全不同。 可解释性与安全性：RLVR训练出的模型虽然能力更强，但其决策过程可能更难理解。如何确保其推理过程的忠实性（Faithfulness），避免模型为了得到正确答案而“抄近道”或产生有害的推理，是一个重要的安全问题。 可能催生的新技术和投资机会： 新一代AI训练与调试平台：未来的模型开发不再是简单的“训练-测试”，而是需要像本文一样深入内部，进行细粒度的诊断和分析。因此，提供可视化、可交互的LLM训练动态分析与调试平台，将成为一个重要的市场机会。 Agentic AI框架与技术：基于RLVR和工具调用的AI Agent是实现通用人工智能（AGI）的重要路径。专注于开发高效、鲁棒的Agent训练框架、工具调用协议和专用奖励模型的公司，将具有巨大的发展潜力。 垂直领域推理模型：通过在特定领域（如金融、法律、科研）应用高度优化的RLVR技术，可以训练出具备专家级推理能力的专用模型。这些模型将创造巨大的商业价值。 AI对齐与安全服务：随着模型能力越来越强，如何确保其行为可控变得至关重要。提供高级的AI对齐解决方案、推理过程审计和安全性验证服务的公司，将迎来广阔的市场。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这是一篇非常出色的研究，但从批判的角度看，仍然存在一些可以改进和商榷之处。\n存在的不足及缺失： 推理过程的忠实性问题： \u0026gt; A major issue with Pass@k is that it may yield correct final answers in RLVR without producing valid reasoning chains. 论文在2.3节坦诚地指出了这个问题。模型可能通过“伪推理”或统计上的巧合得到了正确答案，而目前的奖励机制（只看最终答案）无法区分这一点。虽然作者通过人工抽查认为问题不大，但这始终是一个隐患，缺乏系统性的验证。 实验领域的局限性：实验几乎完全集中在数学推理上。虽然这是一个很好的起点，但数学推理的结构性非常强。这些发现（如“结尾词元更重要”）是否适用于结构性较弱的领域，如创意写作或开放式问答，是存疑的。 模型规模的限制：实验主要基于7B参数级别的模型。这些训练动态和优化策略在更大规模的模型（如70B、100B+）上是否依然有效，或者会呈现出怎样的新现象，还有待验证。大模型可能会有不同的“学习规律”。 需要进一步验证和存疑的： PPL作为“质量”代理的普适性：论文假设低PPL等同于高质量、高置信度的推理。在大多数情况下这是成立的，但是否存在模型对一个错误的推理路径也“信心十足”（即PPL很低）的情况？这种“自信的错误”可能会误导训练。 方法的组合效应：论文分别验证了PPL-based和Position-based两种方法。如果将它们结合起来，效果会是1+1\u0026gt;2，还是会相互干扰？论文对此没有进行深入探讨。 长期训练的稳定性：论文提出的优势塑造方法在短期内有效。但在更长时间的训练中，它们是否会引入新的偏见，或者导致模型过早地收敛到某个次优的“风格”，还需要更长周期的实验来验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文绝对是创新想法的宝库。以下是您可以重点学习和借鉴的：\n重点学习与启发： 从“单点结果”到“过程分析”的思维转变：不要只盯着最终的准确率。学习论文的方法，去度量和分析你的模型在训练过程中的“探索潜力”（如Pass@k）、“多样性”（如Rollout Branching Factor）和“能力边界”（如Unsolvable Problems）。这种过程导向的分析方法本身就是一个巨大的创新点。 识别学习的“关键杠杆点”：论文告诉我们，并非所有数据和所有步骤都同等重要。在你的任务中，去寻找那些“高熵词元”（关键决策点）、“低PPL样本”（高质量学习材料）和“关键位置”（如序列末尾）。将优化资源集中在这些杠杆点上，可以事半功倍。 简单但深刻的“微调”：创新不一定需要复杂的算法。论文的优势塑造方法证明，基于深刻洞察的、对现有流程的简单修改，往往能带来意想不到的效果。在你的工作中，思考一下是否可以通过类似的“加权”思想来优化你的模型训练或推理过程。 SFT和RL的协同策略：明确SFT和RL的不同分工。在你的项目中，可以考虑先用SFT来“拓宽视野”，让模型接触到足够多样的高质量数据，然后再用RL来进行“精益求精”，在特定目标上进行强化。 需要补充了解的背景知识： 强化学习基础：特别是策略（Policy）、奖励（Reward）、优势函数（Advantage Function） 等核心概念。理解这些是看懂本文优化方法的前提。 PPO算法（Proximal Policy Optimization）：GRPO是PPO的变种。了解PPO的基本原理，特别是其目标函数中的“clip”和“KL散度”的作用，将有助于你理解RLVR训练的细节。 信息论基础：至少需要理解熵（Entropy） 和困惑度（Perplexity, PPL） 的概念。熵衡量不确定性，PPL衡量模型对序列的“意外程度”，它们是本文分析的核心工具。 大模型训练范式：清晰地了解预训练（Pre-training）、监督微调（SFT）和强化学习（RLHF/RLVR） 三个阶段的区别和联系。这将帮助你更好地理解本文工作的定位和价值。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得启发。\n","date":"2025-08-28T00:00:00Z","permalink":"/from-trial-and-error-to-improvementa-systematic-analysis-of-llm-exploration-mechanisms-in-rlvr/","title":"From Trial-and-Error to Improvement：A Systematic Analysis of LLM Exploration Mechanisms in RLVR"},{"content":"Table date as \u0026quot;阅读时间\u0026quot; WHERE tags[2] = \u0026quot;memory\u0026quot; or tags[3] = \u0026quot;memory\u0026quot; Sort date desc 分类法 从存储介质分类 从作用区分 参考 万字解析\nReview: 自进化Agent的RL Memory与Skill方法批判 — 把 SkillRL / MemRL / prompt memory / search-augmented RL 四类做了批判性横向对比,并指出”真问题不是 memory,而是 credit assignment”\n","date":"2025-08-28T00:00:00Z","permalink":"/memory%E8%AE%BA%E6%96%87%E6%95%B4%E7%90%86/","title":"memory论文整理"},{"content":"一个agent模拟多agent。将多种能力内化到一个agent中。\n论文深入解读 在人工智能领域，我们正目睹一个关键的转变：从追求更大、更全能的单一大型语言模型（LLM），转向构建能够协同解决复杂任务的多智能体系统（Multi-Agent Systems, MAS）。例如，让一个“规划智能体”负责拆解任务，一个“研究智能体”负责搜集资料，一个“编码智能体”负责编写代码，它们通过对话协同工作。这种方式在处理深度研究、软件开发等复杂任务时展现了惊人的潜力。然而，正如论文开篇所指出的，现有的多智能体系统存在几个致命缺陷： 1. 计算开销巨大：多个智能体（通常是多个独立的LLM实例）之间的反复通信会消耗海量的计算资源和时间。 2. 泛化能力差：这些系统高度依赖于人工设计的提示（Prompt）和固定的工作流，一旦任务领域或流程发生变化，就需要重新进行繁琐的工程调优。 3. 无法进行数据驱动的学习：最关键的是，系统的核心——那些作为智能体的LLM——通常是预训练好后固定不变的。整个系统无法从解决任务的经验中学习和进化，其性能提升受限于“提示工程”而非“数据驱动”。\n为了解决效率问题，学术界提出了另一条路径：工具集成推理（Tool-Integrated Reasoning, TIR）。这种方法通过微调，让单个LLM学会使用外部工具（如搜索引擎、计算器）。其典型的模式是ReAct (Reasoning and Acting)框架，即“思考-行动-观察”的循环。TIR模型虽然更高效，但论文敏锐地指出，它们简单的线性循环模式“无法支持多智能体系统中的复杂协作”。\n正是在这个背景下，这篇论文抛出了其核心贡献——智能体链（Chain-of-Agents, CoA）范式。CoA的构想极具颠覆性：它并非在外部运行多个智能体，而是在一个单一的基础模型内部，模拟出多智能体的协作生态。模型在推理过程中，会根据任务需要，动态地“激活”不同的内部角色，如“思考智能体（Thinking Agent）”、“规划智能体（Plan Agent）”、“搜索智能体（Search Agent）”和“代码生成智能体（Code Generate Agent）”等。\n我们可以通过论文中的图2直观地理解其差异。传统的TIR如同一个工匠，遵循“思考-拿锤子-观察结果”的固定流程。而CoA则像一个高效的指挥官，在自己的大脑中快速调度不同的专家角色——“规划专家，请拆解任务；搜索专家，请查找资料；反思专家，请评估当前方案”，整个过程一气呵成，无需昂贵的外部通信。\n为了让模型学会这种复杂的内部协作能力，论文提出了一套同样创新的两阶段训练框架：\n第一阶段：多智能体知识蒸馏（Multi-Agent Knowledge Distillation） 这是论文方法论的精髓所在。手动创建高质量的、包含多智能体协作的训练数据极其困难。作者们想出了一个绝妙的办法：让一个现成的、强大的“教师”多智能体系统（他们使用了开源的OAgents系统）去解决大量任务。然后，他们像录制“教学视频”一样，将“教师”系统中各个智能体的完整交互、思考、行动轨迹记录下来。最后，通过一个巧妙的转换过程，将这些复杂的、并行的、多回合的协作轨迹，“蒸馏”成一个单一模型可以学习的、线性的CoA格式序列。\n论文在图3中展示了这一过程。一个复杂的任务，在OAgents中可能需要多个智能体之间数十次的通信和调用，而经过蒸馏后，它变成了一段包含\u0026lt;plan\u0026gt;, \u0026lt;think\u0026gt;, \u0026lt;tools\u0026gt;, \u0026lt;reflection\u0026gt;等结构化标签的文本序列，可以直接用于对AFM进行监督微调（Supervised Fine-tuning, SFT）。\n这个过程不仅解决了训练数据的来源问题，更重要的是，它将一个复杂多智能体框架的设计和能力，“迁移”到了一个单一、高效的模型中。为了确保“教学质量”，他们还设计了渐进式质量过滤（Progressive Quality Filtering）机制，只保留那些复杂的、高质量的、包含自我反思和错误修正的成功轨迹。\n第二阶段：智能体强化学习（Agentic Reinforcement Learning, RL） 在模型通过SFT掌握了基本的协作模式后，作者们利用强化学习对其进行“实战演练”和“能力打磨”。他们让模型在真实环境中执行任务，并根据最终结果的好坏给予奖励或惩罚。例如，在网页任务中，他们使用一个更强大的模型（LLM-as-a-Judge）来判断答案的正确性；在编程任务中，则直接以代码是否通过所有测试用例作为奖励标准。通过这种方式，模型不再是简单地模仿“教师”的轨迹，而是学会了如何根据环境的反馈自主优化策略，以达成最终目标。\n论文的实验部分极其详尽和令人信服。他们在网页智能体（如GAIA、BrowseComp）和代码/数学智能体（如AIME25、LiveCodeBench）等多个极具挑战性的基准上进行了测试。实验结果全面展示了AFM的优越性： * 性能卓越：在几乎所有测试基准上，AFM都取得了新的SOTA（State-of-the-art）成绩。例如，在公认难度极高的GAIA基准上，AFM (32B) 取得了55.3%的成功率，显著超过了之前的SOTA模型WebSailor (53.2%)，并逼近了基于GPT-4.1的复杂多智能体系统OAgents (58.3%)。在AIME25数学竞赛基准上，AFM更是取得了59.8%的解题率，相比之前的最佳TIR模型ReTool (49.3%)，实现了超过10%的绝对提升。 * 效率惊人：这是AFM最引人注目的优势之一。论文图5的数据显示，在解决GAIA任务时，相比传统的多智能体系统OAgents，AFM的token消耗量降低了84.6%，同时工具调用次数也大幅减少。这意味着AFM在实现顶级性能的同时，其推理成本和延迟也大大降低，使其在实际应用中更具可行性。 * 强大的泛化能力：论文还展示了一个有趣的实验：一个只在代码和数学任务上训练的AFM，在不经过额外训练的情况下，能够直接理解并正确使用它从未见过的网页搜索工具来解决问题，这证明了CoA范式具备良好的零样本泛化能力。\n总而言之，这篇论文为AI Agent的发展描绘了一幅清晰且可行的蓝图。它通过“智能体链”范式，巧妙地在单一模型中统一了多智能体系统的强大能力与端到端模型的高效可训练性。其“多智能体知识蒸馏”方法为训练这类复杂模型提供了一套行之有效的解决方案。最终诞生的Agent Foundation Models，在性能和效率上的双重突破，无疑将推动AI Agent从学术研究走向更广泛的实际应用。通过全面开源模型、数据和代码，作者们也为整个社区提供了坚实的基石。\n接下来，我将针对您提出的六个问题，进行更详细的解读。\n一、研究目标与实际问题 研究目标： 本文的核心研究目标是创建一种全新的AI智能体架构——智能体基础模型（Agent Foundation Models, AFMs）。这种模型旨在能够在一个统一的、端到端的框架内，原生支持并高效执行复杂的、多步骤、多角色的问题解决流程，从而克服现有智能体范式的根本性限制。\n想要解决的实际问题： 论文旨在解决当前AI智能体领域的两大主流技术路线——多智能体系统（MAS）和工具集成推理（TIR）——分别存在的瓶颈：\n解决多智能体系统（MAS）的“三高一低”问题：即高计算开销、高工程复杂度、高维护成本和低学习效率。传统MAS通过组合多个LLM来模拟团队协作，虽然功能强大，但在实际部署中过于笨重、昂贵且难以优化。 解决工具集成推理（TIR）的能力上限问题：TIR模型（如基于ReAct的模型）虽然高效，但其简单的“思考-行动-观察”模式不足以支撑复杂的协作逻辑，例如任务的并行处理、多角度的批判性反思、或者在多个专业工具间进行灵活调度。它本质上是一个“单线程”的工作模式。 问题的重要性与行业意义： 这个问题对于AI行业的发展至关重要。当前，我们正从“模型能力”时代迈向“应用智能”时代。无论是打造能自主完成市场调研报告的AI分析师，还是能独立开发、测试、部署软件的AI程序员，都需要AI具备超越简单问答和工具调用的复杂协作与规划能力。 \u0026gt; “However, the current TIR paradigm cannot train LLMs to support multi-agent systems in an end-to-end fashion.” 这篇论文的工作，恰好打通了从“强大但笨拙”的MAS到“高效但简单”的TIR之间的断层，提出了一条兼具二者之长的中间道路。它使得构建可扩展、可学习、经济高效的通用AI助手成为可能，极大地加速了AI Agent技术从实验室原型走向商业化应用的进程。\n二、新思路、方法与模型 核心新思路：智能体链（Chain-of-Agents, CoA）范式 CoA是本文最核心的理论创新。它重新定义了“多智能体协作”的实现方式。\n特点：与传统MAS在“外部”运行多个模型实例不同，CoA是在“内部”通过一个统一模型的单次解码过程来完成协作。模型学会了在生成文本时，根据上下文动态切换到不同的“角色”或“功能模块”，如\u0026lt;plan\u0026gt;、\u0026lt;reflect\u0026gt;、\u0026lt;search\u0026gt;等。这些角色就像是模型大脑中的不同“专家”，由一个更高层次的“思考智能体”进行统一协调。 优势： 高效性：消除了多个LLM实例间的网络通信和API调用开销，所有协作都在模型内部完成。 连贯性：由于所有信息都存在于单一模型的上下文中，避免了MAS中因信息传递不完整或上下文丢失导致的问题。 可训练性：最关键的优势是，整个协作过程是端到端可微的（differentiable），因此可以通过SFT和RL等方法进行整体优化，让模型从数据中学习如何更好地“协作”。 论文中的表1清晰地对比了不同范式的优劣，只有CoA在工具集成、端到端执行、多智能体协作和数据中心优化这四个维度上全部实现了支持。\n核心新方法：多智能体知识蒸馏 + 智能体强化学习 为了实现CoA范式，论文提出了一套创新的训练方法。\n多智能体知识蒸馏： 特点：这是一种新颖的“行为级”知识蒸馏。它不是简单地让学生模型模仿教师模型的输出概率分布，而是让学生模型学习教师系统（OAgents）在解决问题时的整个决策链和协作模式。 优势： \u0026gt; “Specifically, we use a multi-agent system to solve these agentic tasks and convert the successful trajectories into CoA-compatible ones.” 这种方法巧妙地利用了现有SOTA系统的能力来自动化地生成高质量、高复杂度的训练数据，解决了复杂智能体训练数据稀缺的核心难题。同时，通过渐进式质量过滤，如对包含\u0026lt;reflection\u0026gt;和错误修正的轨迹进行上采样，确保了模型学到的是鲁棒且深刻的推理能力。 智能体强化学习（Agentic RL）： 特点：在SFT之后引入RL，让模型超越“模仿”，学会“为目标而优化”。 优势：RL使得模型能够探索在SFT数据中未曾见过的更优解。例如，在代码生成任务中，奖励函数直接与代码能否通过所有测试用例挂钩。这种以最终任务成功为导向的优化，能有效提升模型在真实场景下的鲁棒性和问题解决能力。 三、实验验证 实验设计： 论文的实验设计非常严谨和全面，遵循了科学的评估方法： 多维度基准：选取了涵盖网页信息检索（Web Agent）和代码与数学推理（Code Agent）两大类、近20个不同难度和类型的公开基准，确保了评估的全面性。 强大的基线：比较对象不仅包括了原始的LLM（Direct Inference），还包括了多种主流的TIR方法（如Search-R1, ReTool）和基于强大闭源模型（GPT-4.1）的MAS框架（如OAgents, OWL），保证了比较的公平性和说服力。 多尺度模型：在不同参数规模（如7B, 32B）的模型上都进行了实验，验证了其方法的有效性和可扩展性。 实验数据与关键结果： 实验结果有力地证明了AFM的优越性，以下是一些关键数据引用： 网页智能体任务 (Web Tasks)：根据表7，在Qwen2.5-32B-Instruct作为骨干模型时： 在GAIA基准上，AFM-RL得分为55.3%，而之前的SOTA模型WebSailor为53.2%，基于更强骨干QwQ-32B的WebDancer-RL也只有51.5%。 在HLE（人类前沿难题）基准上，AFM-RL得分为18.0%，显著高于WebThinker-RL (15.8%)和Deepseek-R1 (8.6%)。 在BrowseComp（高难度网页浏览）上，AFM-RL达到11.1%，同样是新的SOTA。 代码与数学任务 (Code/Math Tasks)：根据表11，在使用Qwen2.5-Coder-32B骨干模型时： 在AIME25（美国数学邀请赛）上，AFM-RL取得了59.8%的平均解题率，而之前的最佳模型ReTool-32B为49.3%。 在OlympiadBench（奥林匹克竞赛级难题）上，AFM-RL得分为72.1%，同样优于ReTool-32B的66.4%。 计算效率：图5的数据是论文的另一大亮点，我将其整理为下表以便理解： 方法 在GAIA上的平均Token消耗 OAgents (MAS) 156,400 WebThinker (TIR) 55,957 AFM (CoA) 24,047 (比OAgents低84.6%) 这个数据清晰地表明，AFM在实现SOTA性能的同时，极大地降低了资源消耗。\n四、未来探索与挑战 值得探索的问题与方向： 更复杂的智能体链结构：目前CoA内部的“智能体”角色相对固定，未来可以探索让模型动态生成、组合新的智能体角色，以适应更开放、更多样的任务。 多模态智能体基础模型：将CoA范式扩展到多模态领域，让模型能够在一个统一框架内协同处理文本、图像、音频等信息，这将是构建通用AI助手的关键一步。 终身学习与自主演化：当前模型的学习过程是离线的（SFT+RL）。未来的研究可以探索如何让AFM在与环境的持续交互中进行在线学习和自我完善，实现能力的终身演化。 智能体社会模拟：利用高效的AFM作为基本单元，可以在单个或少量计算设备上模拟大规模的智能体社会，用于经济、社会学等领域的研究。 挑战： “教师”模型的能力天花板：知识蒸馏的效果上限受限于“教师”系统（OAgents）的能力。如何超越教师，或者从多个不同教师身上学习，是一个重要挑战。 安全、可控与对齐：随着AFM变得越来越强大和自主，如何确保其行为是安全、可预测，并且始终与人类的价值观对齐，将成为一个愈发重要的研究课题。 复杂环境的泛化：虽然论文展示了对未见工具的泛化能力，但在更加动态和不可预测的真实世界环境中，AFM的鲁棒性仍需进一步验证。 新的技术和投资机会： 技术催生：这篇论文可能会催生“智能体原生”（Agent-Native）的模型架构和专门用于训练AFM的软硬件协同设计。高效的智能体训练平台和评估基准也将成为热点。 投资机会：基于AFM技术的初创公司，尤其是在自动化科学研究（AI for Science）、软件工程自动化、企业级流程自动化（RPA）和个性化教育等垂直领域的公司，将具有巨大的投资潜力。投资重点将从“模型大小”转向“模型解决复杂任务的效率和智能程度”。 五、批判的视角看，论文的不足及缺失 尽管这是一篇非常出色的工作，但从批判性视角审视，仍存在一些可以探讨的方面：\n对“教师”模型依赖性的探讨不足：论文成功地从OAgents蒸馏了知识，但没有深入讨论这种依赖性可能带来的负面影响。例如，“教师”模型的偏见、错误或能力盲点是否会被毫无保留地“遗传”给学生AFM？如何缓解这一问题？ 泛化能力的验证范围有限：论文在5.2节中验证了模型对未见“工具”的泛化能力，这是一个亮点。但“工具”本质上也是一种“智能体”。其宣称的CoA范式可以支持“几乎任何多智能体系统”，这一论断的普适性还需要在更多样、更异构的智能体协作框架（而不仅仅是OAgents）上进行蒸馏实验来验证。 RL阶段奖励设计的复杂性：论文在网页任务中使用了LLM-as-a-Judge作为奖励信号。众所周知，这类“模型裁判”自身也存在偏见、不稳定性等问题。RL训练的最终效果在很大程度上取决于“裁判”的公正性和准确性，这方面的讨论可以更深入。 实验结果的可复现成本：虽然作者承诺开源，但完整复现这篇论文的实验（尤其是训练32B规模的模型）需要巨大的计算资源，这可能会限制中小型研究机构在此工作基础上进行快速迭代和创新。 六、重点学习、启发与背景知识 应该重点学什么？ 核心思想：复杂协作的“内部化”：最值得学习的是将一个复杂的外部系统（多智能体协作）抽象、建模并“内化”到一个单一、高效模型中的设计哲学。这是一个在能力和效率之间取得精妙平衡的范式。 方法论：行为轨迹的知识蒸馏：多智能体知识蒸馏是一个极具启发性的方法。它告诉我们，要教会模型一种复杂的行为，可以先找到一个能够执行该行为的“专家系统”（即使它很慢或很昂贵），然后将其完整的“行为轨迹”蒸馏给一个高效的学生模型。这个“流程蒸馏”思想可以被广泛应用。 数据工程：结构化数据的重要性：AFM的成功离不开plan, think, tools, reflection等结构化标签。这启发我们，在训练模型解决复杂推理任务时，数据的结构化程度和其内容同样重要。 有哪些启发？ 这个“流程蒸馏”的思路可以迁移到许多其他领域。例如： 软件工程：可以记录一个高级软件工程师团队（包含项目经理、架构师、程序员、测试工程师）的完整开发流程，并将其蒸馏到一个端到端的AI软件开发智能体中。 科学研究：可以记录一个科研团队从文献调研、提出假设、设计实验到分析数据的全过程，并蒸馏到一个AI科学家智能体中。 需要补充了解的背景知识： ReAct框架：理解其“思考-行动-观察”的基本循环，这是理解TIR模型和CoA范式区别的基础。 多智能体系统（MAS）：了解MAS的基本概念，如智能体间的通信、协调、角色扮演等（例如AutoGen, CrewAI等框架的思想），有助于理解CoA要解决的核心问题。 知识蒸馏（Knowledge Distillation）：了解其基本原理，即如何将一个“教师”模型的知识迁移给一个“学生”模型。 强化学习（RL）：了解强化学习，特别是PPO (Proximal Policy Optimization)或论文中提到的DAPO算法，是如何被用于微调语言模型的。 希望以上详尽的解读能够帮助您全面、深入地理解这篇优秀的论文。如果您还有其他问题，随时可以提出。\n","date":"2025-08-27T00:00:00Z","permalink":"/chain-of-agentsend-to-end-agent-foundation-models-via-multi-agent-distillation-and-agentic-rl/","title":"Chain-of-Agents：End-to-End Agent Foundation Models via Multi-Agent Distillation and Agentic RL"},{"content":"经验学习，将成功轨迹和失败轨迹总结成经验，然后作为上下文，还有遇到失败的反馈时，进行反思。\n论文深度解读：从“粗放”到“精细”，唤醒大模型的经验智慧 这篇论文的核心思想，是解决当前大模型智能体在复杂任务中“学不精、记不牢、不会反思”的困境。现有智能体，要么依赖成本高昂的在线训练，要么通过简单的记忆机制（例如，存储过去的成功案例）来辅助决策。然而，这些方法往往效率低下，因为它们处理经验的方式是“单粒度的”（single-granularity）。这意味着智能体无法区分哪些是普适的原则性知识，哪些是特定场景下的操作技巧，导致学习到的经验难以泛化，遇到新情况时容易“失忆”或“犯傻”。\n作者提出的“由粗到精的接地记忆”（Coarse-to-Fine Grounded Memory, CFGM）框架，旨在通过一种更智能、更有层次的方式来组织和利用经验。这里的“接地”（Grounded）是一个非常关键的概念，通俗地讲，就是让大模型将自己庞大的内部知识与真实环境中的具体交互经验联系起来，而不是空泛地推理。CFGM框架将这个“接地”过程巧妙地分成了三个递进的阶段：\n粗粒度聚焦，指导经验收集（Coarse-Grained）：在训练开始前，CFGM并不像其他方法那样盲目地让智能体去探索，而是先利用大模型（LLM）自身的理解能力，对任务环境和目标进行分析，生成一系列“焦点”（Focus Points）。这些焦点就像是为智能体划出的“学习重点”，指导它在环境中进行更高质量、更有目的性的探索，从而收集到更有价值的成功与失败经验。这从源头上提升了原始记忆的质量。\n混合粒度提炼，构建经验智慧库（Hybrid-Grained）：收集到高质量的经验（包含成功和失败的轨迹）后，CFGM会利用LLM进行“事后复盘”。它会对比成功和失败的案例，从中提炼出“经验性技巧”（Experience-wise Tips）。这些技巧是混合粒度的，既包含了“在不确定时，优先检查最可能的位置”这类粗粒度的通用策略，也包含了“如果‘go to drawer 3’指令无效，请检查抽屉是否已打开”这类细粒度的具体操作建议。这些技巧被存入一个“技巧字典”（Tips Dictionary），成为智能体的“智慧锦囊”。\n细粒度反思，实现自适应规划（Fine-Grained）：在实际执行任务（推理阶段）时，智能体不仅会调用相关的经验和技巧，更重要的是，当它遇到“意外情况”（例如，执行一个动作后环境反馈“Nothing happens”）时，CFGM会激活一个“关键信息自问自答反思”（Self-QA Reflection）机制。这个机制会利用LLM对当前的细粒度状况（即时的任务进度、历史操作）进行分析，提出关键问题（例如，“我已经检查了所有可能存放CD的地方吗？”），并自行回答，从而动态地纠正错误、调整计划。\n这三个阶段构成了一个完整的“学习-沉淀-应用”闭环，让智能体不再是简单地模仿过去，而是学会了从经验中总结规律，并在遇到困难时进行创造性的反思。论文通过在三个极具挑战性的环境——AlfWorld（模拟家居）、WebShop（网页购物）和ScienceWorld（科学实验）——中的实验，充分证明了CFGM的强大实力。例如，在ScienceWorld中，CFGM的成功率（Success Rate, SR）达到了74%，相较于经典的ReAct方法（43%）提升了惊人的31个百分点。这一数据强有力地说明，CFGM框架显著增强了智能体在复杂、长链条任务中的规划和纠错能力，是LLM Agent领域一次意义深远的探索。\n### 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是提升基于大模型的自主智能体（LLM-based Agents）在复杂交互环境中的规划与决策能力，具体手段是通过构建一个高效、多层次的记忆与反思框架。\n解决的实际问题：\n探索效率低下：LLM Agent在陌生环境中常常进行大量无效或重复的探索，导致收集到的经验数据质量低下，学习效率极低。 经验利用粗糙：现有方法通常只是将过去的成功轨迹作为“提示”喂给模型，无法从中提炼出可泛化的策略和技巧，导致智能体举一反三的能力很弱。 纠错能力不足：在任务执行中遇到意外情况时，智能体容易陷入“推理偏轨”（reasoning derailment），不断重复错误，缺乏动态调整和自我反思的能力。 行业意义： 这个问题对于推动AI从“聊天机器人”走向“自主智能体”至关重要。无论是软件自动化（如自动测试、RPA）、具身智能（机器人）、虚拟人还是个性化推荐系统，都需要智能体具备从经验中学习并自主完成复杂任务的能力。一个强大的记忆和规划系统，是实现这些高级应用的基础设施。解决好这个问题，意味着我们可以开发出更可靠、更高效、更自主的AI应用，从而在各行各业释放巨大的生产力。这不仅是学术界的前沿，也是工业界实现AI大规模落地必须攻克的难关。\n### 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路与模型： 论文的核心创新是提出了Coarse-to-Fine Grounded Memory (CFGM) 框架。它不是一个单一的模型，而是一套完整的工作流，其精髓在于利用LLM自身的知识来分层级地处理和“消化”经验。\n特点与优势对比：\n特点 传统方法 (如 ReAct, ExpeL) CFGM 框架 经验收集 依赖于相对盲目的探索，经验质量参差不齐。 焦点驱动 (Focus Points)：先用LLM分析任务，生成粗粒度的探索重点，从源头上保证了经验的高质量和相关性。 知识提炼 直接复用成功的轨迹（raw trajectories），知识是单粒度的，难以泛化。 混合粒度技巧 (Hybrid-Grained Tips)：通过对比成功和失败案例，提炼出通用策略（粗）和具体操作（细），形成了结构化的知识库。 在线规划 主要依赖上下文学习（In-context Learning），遇到新问题时纠错能力弱。 自适应反思 (Adaptive Reflection)：当遇到障碍时，会触发基于细粒度当前状态的Self-QA机制，进行动态的自我诊断和规划修正。 知识来源 主要依赖外部提供的经验数据。 充分利用了LLM的内部知识（internal knowledge）来指导整个记忆的构建和使用过程，实现了“自力更生”。 \u0026gt; 论文中提到：“However, recent studies are depend on single-granularity memory derived from dynamic environmental interactions, whose effectiveness is fundamentally limited by the quality of the collected experiences.” \u0026gt; \u0026gt; (然而，最近的研究依赖于从动态环境交互中获得的单粒度记忆，其有效性从根本上受限于所收集经验的质量。) 这段话精准地指出了先前方法的局限性，而CFGM正是通过其“由粗到精”的层次化设计，系统性地解决了这个问题。其最大的优势在于**变被动记忆为主动学习和反思**，让智能体真正“吃透”了经验，而不仅仅是“记住”了经验。 ### 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 多环境验证：为了证明方法的泛化能力（generalizability），论文选取了三个性质迥异的基准环境： AlfWorld：考验具身智能的导航和物体操作能力。 WebShop：考验在动态网页环境中进行信息检索和决策的能力。 ScienceWorld：考验复杂的科学推理和长序列规划能力。 强基线对比：选择了多个主流的LLM Agent方法作为基线（Baselines），包括经典的ReAct，以及同样是基于记忆增强的ExpeL、AutoGuide和QuBE。 模块消融研究（Ablation Studies）：为了验证CFGM框架中每个组件的有效性，作者在AlfWorld上进行了消融实验，即分别移除焦点（FP）、经验技巧（ET）和关键信息反思（KIR），观察性能下降情况。 关键数据与结果： 总体性能优越：如下表所示，CFGM在所有三个环境中的成功率（SR）都显著优于所有基线方法。 方法 AlfWorld (SR) WebShop (SR) ScienceWorld (SR) ReAct 80.60% 37% 43% ExpeL 81.34% 42% 57% AutoGuide 83.58% 47% N/A CFGM (Ours) 91.00% 57% 74% \u0026gt; 在WebShop环境中，CFGM的成功率达到了57%，比AutoGuide高出10个百分点；在ScienceWorld中，更是比基于经验的ExpeL高出17个百分点，这证明了CFGM的记忆组织方式远比简单存储经验要高效。 * **消融实验证明各组件不可或缺**： 在Table 2中，基线ReAct的成功率是80.60%。单独加入任何一个CFGM组件都能带来提升。而当所有三个组件（FP+ET+KIR）结合时，成功率达到最高的**91.00%**。值得注意的是，如果从完整的CFGM中移除**焦点（FP）**，成功率会从91.00%下降到**88.06%**，这有力地证明了“高质量的经验收集”是整个框架成功的基石。 ### 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 数据效率与小样本学习： 挑战：CFGM虽然提升了经验质量，但仍需要一定数量的训练任务来构建初始记忆。论文也承认，在“极小的训练集”面前，其效果会受限。 探索方向：如何实现零样本（Zero-shot）或极少样本（Few-shot）的智能体学习？这可能需要结合世界模型（World Models）的构建，让智能体在没有真实经验时也能进行有效的“思想实验”。 投资机会：专注于为Agent训练提供高质量合成数据（Synthetic Data）的平台，或者开发能高效压缩和泛化经验的Agent基础模型。 长期记忆与动态遗忘： 挑战：当智能体长期运行时，记忆库会无限膨胀，导致检索效率下降，甚至引入过时或冲突的“坏”知识。 探索方向：研究动态记忆管理机制，包括记忆的抽象、归纳和遗忘。如何让智能体像人一样，随着时间推移，将具体经验升华为抽象智慧，并忘记不重要的细节。 新技术：可能催生出类似人脑海马体和新皮质协作机制的分层记忆架构，用于处理短期情景记忆和长期语义记忆。 从模拟到现实的迁移（Sim-to-Real）： 挑战：在模拟器中有效的策略，在充满噪声和不确定性的物理世界中可能完全失效。 探索方向：研究更鲁棒的策略学习和泛化方法，使智能体能够在现实世界中进行在线的、安全的微调和适应。 投资机会：具身智能/机器人领域，将这类高级规划Agent与物理硬件结合，解决真实世界的自动化任务。 ### 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 计算成本与延迟： CFGM框架涉及多次调用LLM（生成焦点、提炼技巧、在线反思），这在计算上是非常昂贵的。论文没有详细分析其在推理时的延迟（latency）和API调用成本，这在实际部署中是一个关键的考量因素。\n对强大基础模型的依赖： 实验主要基于GPT-4-Turbo和GPT-4o这类顶级的闭源模型。虽然Figure 3展示了其在Qwen等开源模型上依然有效，但性能差距显著。该框架在更小、更经济的模型上的表现如何，以及其效果下限在哪里，有待进一步验证。\n框架的复杂性与可解释性： 这是一个包含多个阶段、多个提示模板的复杂系统。相比于端到端的方法，它的工程实现和调试难度更大。当智能体做出一个错误决策时，要追溯是哪个环节（焦点、技巧、还是反思）出了问题，可能会比较困难。\n技巧（Tips）的泛化范围存疑： 论文展示了从WebShop到WebArena-Shopping的成功迁移，但这本质上还是在“网页购物”这个领域内。这些从特定任务中提炼的“技巧”能否跨领域泛化（例如，将购物技巧用于科学实验），是一个值得怀疑的问题。其泛化能力可能被高估。\n### 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 核心启发： 让模型“自我管理”：最大的启发是利用LLM的元认知（meta-cognition）能力来管理它自身的学习和推理过程。不要只把LLM当成一个执行者，更要把它当成一个策略师，让它为自己“划重点”、“写总结”、“做复盘”。这个思想可以应用到LLM的许多其他应用中。 分层处理的威力：“由粗到精”的层次化思想是解决复杂问题的经典模式。在设计AI系统时，可以思考如何将任务分解为战略（粗）和战术（细）两个层面，先解决方向性问题，再处理细节问题。 拥抱失败的价值：CFGM明确地利用失败轨迹（failed trajectories）来提炼“什么不能做”的技巧。在构建任何学习系统时，都应重视负反馈（negative feedback）和失败案例的价值，它们往往比成功案例包含更多信息量。 动态反思的重要性：与其在任务开始前给一堆静态的提示，不如在任务进行中，当系统遇到困难时，赋予它动态的、基于当前状态的反思和纠错能力。这对于构建鲁棒的AI系统至关重要。 需要补充的背景知识： 基础Agent框架：需要深入理解ReAct (Yao et al., 2023a)，它是许多后续Agent研究的基础，理解其“Reason-Act”的循环机制。 记忆增强方法：可以阅读Reflexion (Shinn et al., 2023b) 和 ExpeL (Zhao et al., 2024a) 等论文，了解其他利用经验来提升Agent能力的方法，以便更好地理解CFGM的创新之处。 基准环境：大致了解AlfWorld、WebShop和ScienceWorld这几个环境的特点和挑战，有助于你更直观地感受CFGM解决的问题难度。 提示工程（Prompt Engineering）：论文附录中给出了大量的提示模板，研究这些模板可以让你学习到如何设计复杂的指令来引导LLM完成多阶段的推理和生成任务。 ","date":"2025-08-27T00:00:00Z","permalink":"/coarse-to-fine-grounded-memory-for-llm-agent-planning/","title":"Coarse-to-Fine Grounded Memory for LLM Agent Planning"},{"content":"比context_engineering更高级的经验学习。从经验池中检索相似的经验来指导agent。\n论文深度解读 这篇论文的核心目标是解决大型语言模型（LLM）在执行多步骤、复杂工具调用（Multi-step Function Calling） 任务时遇到的一个根本性瓶颈：如何高效、动态地从过去的经验中学习，以应对新问题？ 传统的解决方案，如上下文学习（In-context Learning, ICL），通常依赖于在提示（Prompt）中提供一些固定的、人工筛选的示例（Few-shot Examples）。然而，这种方法存在三大问题： 1. 静态性与局限性：示例库是预先定义好的，无法适应模型在使用过程中遇到的新情况和新工具。 2. 检索粒度粗糙：传统的检索方法通常只基于用户初始问题的语义相似度来寻找示例，但这对于需要复杂工具链协作的任务来说远远不够。一个任务的解决路径（即工具的调用顺序和方式）可能比其初始描述更具参考价值。 3. 扩展性差：随着工具数量和任务复杂度的增加，手动编写高质量的示例变得极其困难且成本高昂，无法规模化。\n为了克服这些挑战，作者提出了一个名为SEER (Stepwise ExperiencE Recall) 的创新框架。SEER的核心思想可以概括为两点：“动态成长的经验池” 和 “精准的、多维度经验检索”。\n动态成长的经验池（Continual Experience Accumulation） 是SEER实现“自引导”的关键。论文中提到： \u0026gt; Instead of relying on a static, pre-collection pool, our method adopts an online self-guided mechanism. After each successful task completion, the corresponding trajectory is added to the demonstration pool, enabling continual refinement and enrichment over time.\n这意味着SEER不再依赖一个一成不变的示例库。它建立了一个“活”的经验池（Experience Pool）。每当模型成功完成一个任务，整个交互过程（从用户问题到最终答案，包括中间所有的工具调用和返回结果），即一个“轨迹”（Trajectory），就会被储存到这个池子里。如何判断成功？论文引入了一个“LLM作为裁判”（LLM-as-a-judge）的机制，让另一个LLM来评估任务是否成功完成。这个设计使得SEER能够在没有人工标注的情况下，持续地、自动地从自己的成功经验中学习，实现自我进化。这一点在论文的图2（Figure 2） 中得到了有力证明，实验显示随着处理的任务批次增多，SEER的准确率呈现出明显的上升趋势，从最初的37.7%攀升至最终的54.9%，直观地展示了其自学习、自提升的能力。\n精准的、多维度经验检索（Stepwise Experience Recall） 则是SEER的“大脑”，它决定了如何从经验池中找到对当前任务最有帮助的“导师”。传统方法可能只会问：“哪个过去的‘问题’和现在的问题最像？” 而SEER则会问：“哪个过去的‘解决过程’对现在的任务最有启发？” 为此，它设计了一个精妙的多维度评分系统，从三个方面来评估候选经验的价值：\n轨迹相似度 (Trajectory Similarity, \\(s_1\\)): 这不仅仅是比较初始问题的相似性，而是将整个交互历史进行向量嵌入（Embedding），比较整个问题解决流程的相似性。这使得模型能够找到结构上相似的解决范式。 工具链覆盖度 (ToolChain Coverage, \\(s_2\\)): 这个指标衡量的是，候选经验中使用的工具集合，在多大程度上覆盖了当前任务已经用到的工具。这非常关键，因为它帮助模型找到那些在“如何组合使用工具”方面有指导意义的经验，尤其是在复杂的、需要多个工具协作的任务中。 意图匹配度 (Intent Match, \\(s_3\\)): 这个指标确保检索到的经验在用户的核心目标上是一致的。 最终，SEER通过一个加权公式 Score = λ1*s1 + λ2*s2 + λ3*s3 来计算每个候选经验的得分，并选取分数最高的几个作为上下文示例。这种细粒度的检索机制，远比单一的语义检索要强大。论文的消融实验（Ablation Studies） 也验证了这一点。图3（Figure 3） 的数据显示，如果去掉工具链覆盖度（w/o s2），模型在困难问题上的性能下降最为显著；而去掉意图匹配（w/o s3），在简单问题上性能下降更明显。这清晰地表明，SEER的每个检索维度都有其不可或缺的价值，共同构成了其强大的性能。\n在实验验证方面，SEER在两个权威的工具使用评测基准 ToolQA 和 T-bench 上都取得了SOTA（State-of-the-art）级别的表现。在ToolQA上，相较于之前的最强基线 ExpeL，SEER在简单任务上平均准确率提升了6.1%，在困难任务上提升了4.7%。在更具挑战性的多轮对话场景 T-bench 上，SEER更是将 Qwen2.5-72B 模型的平均成功率从 28.46% 大幅提升至 51.84%，几乎追平了强大的闭源模型 GPT-4o（54.76%）。这些数据充分展示了SEER框架的有效性和巨大潜力。\n总而言之，这篇论文不仅仅是提出了一种新的算法，更是为构建能够自主学习和进化的AI智能代理提供了一个切实可行的工程范式。它通过“事后复盘、积累成功经验”和“遇到难题、精准求教”这两个核心机制，让LLM从一个被动执行指令的工具，向一个能够主动思考、自主成长的智能体迈进。\n接下来，我将按照您提出的六个问题，进行更详细的逐一解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是提升大型语言模型在执行多步骤、复杂函数调用任务时的性能和自主学习能力。 解决的实际问题: 静态示例的局限性: 当前LLM在进行工具调用时，严重依赖于人工编写和筛选的少量示例（Few-shot）。这些示例是静态的，无法覆盖所有场景，导致模型在遇到新问题或需要复杂的工具组合时表现不佳。 低效的经验利用: 即使模型过去成功解决过类似问题，它也没有机制能自动“记住”并“复用”这些宝贵的成功经验。每一次任务都像是从零开始，缺乏成长性。 粗糙的检索逻辑: 现有的检索方法大多只关注问题表面的文本相似性，而忽略了解决问题的核心——工具调用逻辑和流程的相似性。 行业意义: 推动AI Agent的自主进化: 这是迈向真正自主AI智能代理（Autonomous Agents）的关键一步。能够自我学习和改进的Agent，可以大大减少人工维护成本，并能适应不断变化的环境和需求，例如自动适应API的更新或新的软件功能。 加速复杂任务自动化: 在金融分析、科学研究、软件开发等领域，许多任务需要调用多个API或软件工具，并根据中间结果动态规划下一步。SEER这样的技术能够让LLM更好地胜任这些“高级助理”的角色，极大地提升生产力。 降低Agent开发门槛: 通过自引导学习机制，开发者不再需要为每一种可能遇到的情况手动设计和优化提示（Prompt Engineering），使得构建强大、鲁棒的AI Agent变得更加容易和高效。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出了 SEER (Stepwise ExperiencE Recall) 框架，其核心创新在于动态经验积累和多维度细粒度检索。\n新思路与方法: 在线经验积累 (Online Experience Accumulation): 这是SEER的“自学习”模块。 \u0026gt; we propose an online updating experience pool that continuously supports in-context learning, enhancing inference quality and real-world adaptability. 它通过一个“LLM裁判”来自动评估任务成功与否，并将成功的交互轨迹（包括用户的每一步输入、模型的每一步思考和工具调用）存入一个动态更新的经验池。 多维度检索评分 (Multi-dimensional Scoring Strategy): 这是SEER的“智能检索”模块，它摒弃了单一的检索标准，综合考量三个维度： 轨迹相似度 (\\(s_1\\)): 比较整个交互历史的嵌入向量，而非仅仅是初始查询。这能捕捉到问题解决的“模式”相似性。 工具链覆盖度 (\\(s_2\\)): 计算候选经验中的工具与当前任务所需工具的重合度。这对于规划复杂的工具序列至关重要。 意图匹配度 (\\(s_3\\)): 判断用户在两个任务中的根本目的是否一致。 与之前方法的比较、特点和优势: 相比静态ICL (In-context Learning): 特点: 动态 vs 静态。SEER的经验池是“活”的，能不断吸收新知识。 优势: 适应性强、无需人工维护。能够自动学习处理新任务，并随着使用时间的增长而变得越来越“聪明”。 相比传统检索方法 (如ART): 特点: 细粒度 vs 粗粒度。ART等方法主要依赖任务或推理步骤的语义相似性，是“粗粒度”的。SEER的检索综合了轨迹、工具链和意图，是“细粒度”的。 优势: 检索更精准。尤其在困难任务中，工具链的相似性往往比问题的文本相似性更重要。SEER能够找到在“如何做”层面更有启发性的案例，而不仅仅是“做什么”相似的案例。 相比强化学习方法 (如StepTool, SWIRL): 特点: 无需训练 vs 需要训练。强化学习方法通常需要训练一个奖励模型或调整LLM本身的参数。 优势: 实现简单、效率高。SEER在推理阶段（Inference time）工作，不需要额外的模型训练，可以更轻量级地集成到现有系统中。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列全面的实验来验证SEER的有效性，实验设计严谨，层层递进。\n实验设计: 基准测试 (Benchmark Evaluation): 在两个公认的、有挑战性的数据集 ToolQA 和 T-bench 上，将SEER与多种基线方法（如ReAct, ART, ExpeL等）进行性能对比。ToolQA侧重于单轮交互中的多步工具使用，T-bench则模拟真实世界的多轮、动态交互。 自改进能力验证 (Self-Improvement Experiment): 为了证明SEER的在线学习能力，论文设计了一个增量学习实验。将ToolQA的1530个问题分成10个批次，每完成一个批次的评估后，将所有成功的案例加入到经验池中，用于下一批次的检索。 消融研究 (Ablation Studies): 为了探究SEER多维度检索中每个部分的重要性，研究者分别移除了工具链覆盖度（\\(s_2\\)）和意图匹配（\\(s_3\\)），并与完整的SEER进行对比。同时，也对比了仅基于初始查询的检索（Query-based）和基于完整轨迹的检索。 超参数敏感性分析 (Hyperparameter Sensitivity): 探究了检索示例数量（Top-k）对模型性能的影响。 实验数据和结果: 基准测试结果: 在 ToolQA 上 (Table 3)，SEER在简单和困难任务上的平均准确率分别达到 67.9% 和 31.1%，全面超越了所有基线方法。最强的基线ExpeL的成绩为61.8%和26.4%。 在 T-bench 上 (Table 4)，SEER (72B) 的平均成功率达到了 51.84%，相比基础的Qwen2.5-72B模型 (28.46%) 实现了翻倍级的性能提升。 模型 / 方法 ToolQA (Easy) ToolQA (Hard) T-bench (Avg) ExpeL (Baseline) 61.8% 26.4% - Qwen2.5-72B (Base) - - 28.46% SEER (Ours) 67.9% 31.1% 51.84% 自改进能力结果 (Figure 2): 模型的准确率随着处理批次的增加而稳定提升，从第1批的 37.7% 上升到第10批的 54.9%，清晰地展示了其“越用越强”的特性。 消融研究结果 (Figure 3): 移除工具链覆盖度（\\(s_2\\)）后，模型在困难任务上的性能下降最大。 移除意图匹配（\\(s_3\\)）后，模型在简单任务上的性能下降最大。 这证明了多维度检索的必要性和各维度的独特贡献。 超参数分析结果 (Table 5): 实验发现，检索 k=4 个示例时性能达到最佳。这表明过少或过多的示例都可能对模型产生负面影响，需要一个平衡。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 该研究方向前景广阔，但仍面临诸多挑战和机遇。\n值得探索的问题和挑战: 从失败中学习: SEER目前只从成功的经验中学习。然而，失败的经验同样宝贵，能告诉模型“什么不能做”。如何设计一个能从失败轨迹中总结教训、进行归因分析并指导未来决策的机制，是一个极具价值的研究方向。 经验池的管理与泛化: 随着经验池不断增大，检索效率会下降，且可能积累大量冗余或低质量的经验。未来的研究可以探索： 经验剪枝（Pruning）: 自动移除陈旧或价值不高的经验。 经验蒸馏/总结（Distillation/Summarization）: 将多个相似的成功经验融合成一个更具泛化性的“元经验”或“策略”。 动态权重调整: SEER中的检索权重（λ）是固定的。然而，不同类型的任务可能对轨迹、工具链、意图的侧重不同。能否让模型根据当前任务的特点，动态地调整这三个维度的权重，实现更智能的检索？ 对“LLM裁判”的鲁棒性: 整个自学习循环的基石是“LLM裁判”的准确性。但LLM本身存在偏见和幻觉，可能做出错误判断。如何提升裁判的准确性和鲁棒性，甚至引入多裁判“会审”机制，是一个重要的工程和研究挑战。 可能催生的新技术和投资机会: Agent操作系统/平台 (Agent OS): 围绕着动态经验管理、自主学习、失败归因等核心技术，可能会诞生专门为AI Agent服务的平台层，即Agent的“操作系统”，统一管理Agent的记忆、学习和进化过程。 自我进化的垂直领域Agent: 在金融、医疗、法律等专业领域，可以利用SEER的思想，构建能够不断学习领域知识和操作流程的专用Agent，其商业价值巨大。 新一代RAG技术: SEER本质上是一种针对“行为”的检索增强生成（RAG）。这个思路可以扩展到更多领域，比如代码生成、UI设计等，催生出检索“组件”、“模式”或“流程”的新一代RAG技术。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但仍有一些值得商榷和进一步探讨的地方。\n存在的不足及缺失: 冷启动问题 (Cold-Start Problem): SEER的自学习机制需要有成功的“种子”经验。在系统初期，经验池为空或只有少量示例时，其性能可能很差（如图2所示，初始准确率不高）。论文没有深入探讨如何高效地度过这个冷启动阶段，或者如何构建高质量的初始经验集。 对LLM基础能力的依赖: 整个框架的上限受限于底层LLM的推理能力和作为“裁判”的评估能力。如果LLM本身能力不足，就无法完成复杂的成功轨迹，经验池的质量也就无从谈起。 检索效率问题: 随着经验池扩展到数万甚至数百万条，每次都进行全局的向量相似度计算和多维度评分，可能会成为性能瓶颈。论文中将经验池大小限制在1000，回避了在大规模场景下的效率问题。 权重设置的简单化: 论文将三个检索维度的权重简单地设为1/3，这是一种启发式设置。缺乏理论或实验依据来证明这是最优解，也忽略了不同任务对权重需求的差异性。 需要进一步验证和存疑的: LLM裁判的真实准确率: 论文中提到了使用LLM作为裁判，但没有提供该裁判自身的评估准确率数据。如果裁判的“误判率”较高，可能会导致经验池被“污染”，反而降低模型性能。 在更长、更复杂任务链上的表现: T-bench虽然是多轮对话，但每个任务的步数仍然有限。在需要几十甚至上百步工具调用的超长任务链上，SEER的轨迹相似度等机制是否依然有效，有待验证。 对工具变化的适应性: 论文主要关注任务的变化，但现实世界中工具（API）本身也会变化（如参数增减、功能更新）。SEER能否有效处理这种情况，也是一个疑问。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文充满了可以借鉴的创新思想，尤其对于构建智能系统和优化检索流程非常有启发。\n重点学习的创新点: 核心思想：为“过程”而不是“问题”建立索引: 传统检索系统关注的是“信息”的匹配。SEER的核心启发是，我们可以为“解决方案”或“工作流程” 建立索引和检索。在你的工作中，思考一下是否有一些重复性的“解决问题的模式”，是否可以把这些模式（轨迹）记录下来，并在遇到新问题时，去检索最相似的“模式”，而不仅仅是相似的“问题描述”。 多维度、可解释的检索逻辑: 不要满足于单一的相似度得分。像SEER一样，将“相关性”分解为多个可解释的维度（如流程结构、使用工具、最终目标）。这不仅能提升检索精度，也让你的系统决策过程更透明、更易于调试和优化。 构建动态反馈闭环 (Feedback Loop): 这是从“死”系统迈向“活”系统的关键。在你的项目中，思考如何建立一个机制，自动识别出“好的”结果，并将其反馈到系统的知识库或示例库中。哪怕这个反馈机制很简单，长期的累积效应也会带来巨大的性能提升。 需要补充的背景知识: 上下文学习 (In-Context Learning, ICL): 深入理解LLM是如何通过Prompt中的示例来学习和模仿的，这是所有这类方法的基础。 检索增强生成 (Retrieval-Augmented Generation, RAG): SEER可以看作是RAG在Agent行为生成领域的一种高级应用。了解RAG的原理，有助于你更好地理解SEER的检索机制。 大型语言模型作为智能代理 (LLM as Agents): 了解当前主流的Agent框架，如ReAct (Reason + Act)、Chameleon等，可以帮助你更好地理解SEER在整个Agent技术演进图谱中的位置和创新性。 向量嵌入与相似度计算 (Vector Embeddings \u0026amp; Similarity): 理解文本或复杂数据结构（如轨迹）是如何被转换成向量，以及如何通过余弦相似度等方法来衡量它们的相似性，这是实现智能检索的技术基础。 ","date":"2025-08-23T00:00:00Z","permalink":"/self-guided-function-calling-in-large-language-models-via-stepwise-experience-recall/","title":"Self-Guided Function Calling in Large Language Models via Stepwise Experience Recall"},{"content":"缩减思维链长度。\n这篇论文的核心目标是解决当前大语言模型（LLMs）普遍存在的“过度思考”（Overthinking）问题。具体来说，许多强大的模型，无论面对“1+1=?”这样的简单问题，还是复杂的微积分难题，都会生成长度相近、步骤繁琐的思维链（Chain-of-Thought, CoT），这不仅造成了巨大的计算资源浪费，也显著增加了响应延迟，限制了其在实际应用中的效率。\n论文的核心创新在于提出了一个名为“Think in Blocks”（TiB，块状思考）的全新框架。这个框架颠覆了传统思维链的连续生成模式，将推理过程分解为一系列离散、可控的“推理块”（reasoning blocks）。其最精妙的设计在于，模型不再是盲目地开始思考，而是首先对问题的难度进行预估，并给出一个明确的“推理预算”——也就是它计划使用多少个推理块来解决这个问题。这个预算（一个整数）本身就成了一个可以被模型学习和优化的目标。\n为了实现这一目标，作者设计了一套完整且严谨的三阶段训练流程：\n监督微调（Supervised Fine-Tuning, SFT）：此阶段的目标是教会模型理解并生成这种新的“块结构化”文本格式。模型需要学习如何在思考的开头就预测并声明所需的块数量（例如，\u0026lt;thought_segments\u0026gt;5\u0026lt;/thought_segments\u0026gt;），然后在各个推理块之间使用特殊的分隔符（例如，\u0026lt;continue_think\u0026gt;）进行连接。作者还巧妙地运用了课程学习（Curriculum Learning）的策略，先用中等难度的数据让模型熟悉格式，再逐步引入“不思考直接回答”（0个块）的简单问题和“深度思考”（多个块）的复杂问题，避免模型在学习初期感到困惑。\n直接偏好优化（Direct Preference Optimization, DPO）：在模型掌握了基本格式后，DPO阶段旨在让模型的“推理预算”变得更加智能和自适应。其核心思想是，对于简单问题，模型应该偏好使用更少（甚至0个）推理块的答案；而对于复杂问题，则应该偏好使用更多推理块的、更详尽的答案。作者借鉴了DAST（Difficulty-Adaptive Slow Thinking）的思想，构建了一个奖励函数，该函数同时考虑了答案的正确性和长度（块的数量），并以此来构建偏好对（chosen-rejected pairs），从而教会模型如何在效率和准确性之间做出权衡。\n强化学习（Reinforcement Learning, RL）：作为最后一步，RL阶段通过一个更精细的奖励函数来进一步优化模型的行为。这个奖励函数是一个复杂的组合，旨在实现多个目标，如论文中提到的： \u0026gt; - Maximize No-Thinking samples (最大化“不思考”样本) \u0026gt; - Minimize reasoning block count (最小化推理块数量) \u0026gt; - Reduce average reasoning block length (减少平均块长度) \u0026gt; - Ensure format consistency (确保格式一致性) \u0026gt; - Maintain/improve task accuracy (保持/提升任务准确率)\n通过这个三阶段流程，模型不仅学会了如何“思考”，更学会了“思考多少”，并能根据问题难度动态调整。更具突破性的是，这种显式的块数量预测机制，为推理时（inference-time）的外部控制打开了大门。用户或开发者可以根据自己的延迟或成本预算，强制模型使用特定数量的推理块（例如，最多使用3个块），从而实现对模型推理深度和资源消耗的精准控制。\n实验结果充分证明了该框架的有效性。在DeepMath数学推理任务上，论文最终训练出的模型（SFT→DPO→GRPO）相较于强大的基线模型，取得了惊人的成果：\n在Table 4中，最终模型将答案的平均长度（mean length）从7735减少到5791，缩短了25.1%，而整体准确率（Overall Accuracy）仅从85.5%微降至85.3%，几乎没有性能损失（-0.2%）。\n这一数据有力地说明，“Think in Blocks”框架成功地让模型在保持高水准推理能力的同时，变得更加高效和“节俭”。这不仅仅是一次技术上的优化，更是对如何构建更智能、更实用、更经济的AI系统的一次深刻探索。\n接下来，我将按照您提出的六个方面，对这篇论文进行更详细的解读。\nH3: 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决大型语言模型在推理任务中存在的“过度思考”（Overthinking）问题。\n实际问题：\n计算资源浪费：当前的LLMs倾向于为所有问题（无论难易）生成冗长的推理过程，这消耗了大量的计算资源（GPU/TPU时间），导致高昂的运行成本。 高延迟：过长的推理链意味着更长的生成时间，这使得模型在需要快速响应的实时应用（如在线客服、智能助手）中表现不佳。 缺乏适应性：模型无法根据任务的实际难度动态调整其“思考”的深度，表现出一种“一刀切”的僵化推理模式，这与人类智能的灵活性相去甚远。 行业意义：\n降低AI服务成本：通过减少不必要的计算，可以显著降低LLM推理服务的成本，这对于扩大AI应用的普及范围、让更多中小企业用得起强大的AI能力至关重要。 提升用户体验：更快的响应速度能直接改善用户体验，尤其是在交互式应用中。 推动端侧AI（On-Device AI）发展：高效的推理模型对计算资源的需求更低，这使得在手机、笔记本电脑甚至物联网设备等资源受限的边缘设备上部署强大的推理能力成为可能，从而保护用户隐私并减少对云服务的依赖。 实现可控的AI：论文提出的推理时控制机制，允许开发者根据具体场景（如“快速预览模式”和“深度分析模式”）动态调整模型的行为，为构建更灵活、可控的AI产品提供了新的思路。 H3: 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于“Think in Blocks”框架，其新颖之处和优势体现在以下几个方面：\n新的思路：将推理深度显式化和离散化 思路：传统CoT将推理视为一个连续、不可中断的文本生成过程。而TiB框架将其离散化为可数的“推理块”，并通过一个显式的元数据标签 \u0026lt;thought_segments\u0026gt;n 来声明所需的块数 n。 优势： 可优化：这个整数 n 成为了一个可以直接优化的具体目标，让模型可以学习“需要思考多深”。 可解释：推理过程被划分为逻辑上独立的步骤，更便于人类理解和调试。 可控制：这是最大的优势，显式的块数让推理时的外部干预成为可能。 新的方法：三阶段自适应训练流水线 方法：论文没有止步于一个想法，而是设计了一套完整的SFT → DPO → RL训练流程，逐步地、有层次地将自适应能力注入模型。 与之前方法的比较： 许多先前工作只提供一个二元选择：“思考”或“不思考”。例如，通过一个路由器模型判断是否需要调用CoT。TiB提供了从0到N的细粒度控制，更加灵活。 一些方法通过固定的惩罚项（如长度惩罚）来缩短推理，但这可能导致模型在复杂问题上也“思考不足”。TiB的DPO和RL阶段是难度自适应的，旨在为简单问题“节约”，为复杂问题“投入”，更加智能。 其他方法可能需要融合多个模型（一个用于快速回答，一个用于深度推理），而TiB在单个模型内就实现了这种动态切换，架构更简洁，部署更方便。 新的机制：推理时块数控制 方法：通过在生成块数 n 时使用logit masking技术（如Algorithm 1所示），可以在不重新训练模型的情况下，强制其生成特定范围内的块数。 优势：这提供了一个极其强大的速度-准确率权衡（speed-accuracy trade-off）的调节旋钮。在追求速度时，可以设置 cap_high=1；在追求高质量时，可以解除限制。这种灵活性是传统CoT模型不具备的。 H3: 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在数学推理基准 DeepMath 上的实验，系统地验证了其方法的有效性。\n实验设计： 基线模型（Baseline）：使用强大的Qwen3-8B模型，在原始的DeepMath数据集上进行标准微调，作为性能和长度的基准。 核心方法（Ours）：将在同样的数据上应用“Think in Blocks”格式转换，并依次进行SFT-stage1, SFT-stage2, +DPO, +RL等步骤，每一步都评估其性能，以展示训练流程中每个阶段的贡献。 评估指标： Accuracy (准确率)：在Easy, Difficult和Overall三个难度划分上评估模型解决问题的能力。 Length (长度)：计算正确答案的平均token数，衡量模型的效率。 ∆Acc / ∆Len (%)：与基线模型相比的准确率和长度变化百分比，直观展示优劣。 分析维度：除了总体性能，论文还深入分析了模型在不同难度问题上生成的块数分布（Block Analysis）以及推理时控制块数对性能的影响（Inference-Time Control）。 关键数据和结果： 总体性能（Table 4）：这是最核心的证据。如下图所示，Ours 流程中的 +GRPO 版本（一种RL算法）在性能上达到了最佳平衡。\nMethod Length (mean) Accuracy (Overall) ∆Acc ∆Len(%) Baseline 7735 85.5% - - Ours +GRPO 5791 85.3% -0.2% -25.1% Ours +Dr.GRPO 3970 77.2% -8.3% -48.7% 这张表清晰地显示，论文的方法在几乎不牺牲准确率的前提下，将推理过程的长度显著缩短了四分之一。相比之下，另一种RL变体 Dr.GRPO 虽然长度缩减更多（-48.7%），但准确率也大幅下降（-8.3%），证明了论文所选方法的优越平衡性。\n自适应行为（Figure 4b）：该图展示了模型对不同难度问题的反应。在简单问题（easy）上，模型生成的答案大部分是0个块（深蓝色部分），即“不思考直接回答”。而在难题（difficult）上，模型几乎不使用0块模式，而是生成更多的推理块。这直观地证明了模型确实学会了根据难度自适应地调整推理深度。\n推理时控制（Table 5）：该实验验证了外部控制的有效性。例如，当强制模型“不思考”（Block Cap ≤ 0）时，虽然长度大幅缩短（-81.9%），但准确率也骤降至59.0%（-26.3%）。而当设置一个适中的上限（Block Cap ≤ 6）时，可以在长度减少5.6%的同时，将准确率的损失控制在-4.9%以内，实现了一个很好的平衡。这证明了其作为实用工具的价值。\nH3: 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这个研究方向极具潜力，未来可以从以下几个方面深入探索：\n值得探索的问题和挑战： 动态块生成：目前模型是在开始时就预测总块数。一个更高级的模式是逐块决策，即每完成一个块后，模型动态决定是“继续思考”还是“得出结论”。这更接近人类的思维过程，也更灵活。 功能性推理块：可以探索为不同的推理块赋予特定功能，例如“信息检索块”、“代码执行块”、“自我批判块”、“答案整合块”等。这会让模型的推理过程更加结构化和强大。 与模型架构的深度融合：目前的方法主要在数据和训练层面。未来可以探索在模型架构层面支持自适应推理，例如，将Mixture-of-Experts (MoE) 架构与此结合，让不同的“专家”负责不同深度或类型的推理块。 跨领域泛化：当前实验集中在数学推理。该框架在创造性写作、代码生成、法律文书分析等其他领域的有效性如何，是一个亟待验证的重要问题。不同领域的“推理块”定义可能完全不同。 新的技术和投资机会： 分层AI服务（Tiered AI Services）：云服务提供商可以基于此技术推出不同等级的API服务。例如，提供一个廉价但快速的“草稿模式”（限制块数），以及一个昂贵但精确的“专家模式”（不限块数），满足不同客户的预算和需求。 高效AI芯片与硬件：这种可预测、可控制的计算负载模式，可能为设计专门的AI加速器提供新的思路，实现硬件和软件的协同优化。 新一代AI开发平台：可以出现专注于构建和优化“结构化推理”模型的开发工具链。这些平台将帮助开发者为特定任务设计和训练具有自适应能力的模型，催生“AI推理工程师”这样的新角色。 垂直领域智能体（Domain-Specific Agents）：在金融、医疗、科研等专业领域，可以训练出能够根据问题复杂性自动调整分析深度的智能体，例如，一个金融分析智能体在面对常规财报时快速给出摘要，但在检测到异常信号时自动进入深度调查模式。 H3: 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性角度看，仍存在一些可以改进和探讨的地方：\n不足及缺失： 预测与执行的不一致性：论文在“Limitations”一节中坦诚地指出了最主要的问题：模型预测的块数（\u0026lt;thought_segments\u0026gt;n）与实际生成的块数并不总是一致。虽然在RL阶段有惩罚项，但无法完全杜绝。这影响了控制的精确性。 任务领域的局限性：如前所述，实验仅限于数学推理。数学问题的解题步骤相对清晰，易于分割成“块”。但对于那些逻辑链条更模糊、更依赖发散性思维的任务（如写诗、构思小说大纲），“块”的定义和分割本身就是一个巨大挑战。 超参数的复杂性：尤其是在RL阶段，奖励函数包含了多个权重系数（λ1, λ2, λ3, λ4），这些超参数的调整可能非常敏感和困难，可能会影响模型在不同任务上的复现和迁移。 缺乏对“坏块”的分析：论文主要关注块的数量，但没有深入分析块的“质量”。模型有没有可能为了凑数而生成一些无意义的、重复的“垃圾块”？这方面的分析是缺失的。 需要进一步验证和存疑的： 对模型核心能力的影响：这种高度结构化的训练，是否会在某种程度上“固化”模型的思维模式，影响其在需要更自由、更灵活思考的任务上的表现？即，它是否会损害模型的通用性？ Refactor过程的可靠性：在构建SFT数据集时，作者使用“prompt engineering to refactor”已有的CoT数据。这个重构过程的质量和一致性对最终模型的性能至关重要，但论文对此描述不多。这个过程是否引入了偏见？ 与模型规模的关系：实验基于8B模型。这种方法在更小（如3B）或更大（如70B, 100B+）的模型上效果如何？小模型是否能学会这种复杂的自适应能力？大模型是否天生就有一定的自适应性，导致该方法收益降低？ H3: 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，您可以重点学习和借鉴以下几点：\n重点学习与启发： 核心思想：将隐性过程显式化。这篇论文最 brilhant 的地方，就是把“思考多深”这个模型内部的、隐性的过程，通过一个简单的整数 n 变成了外部的、显式的、可优化的目标。启发：在您的工作中，思考一下有哪些过程是模型“凭感觉”在做的？比如摘要的长度、代码的模块化程度、回复的礼貌等级等。您能否将这些隐性特征设计成一个显式的、可控的标签，并教会模型去预测和遵循它？ 方法论：课程学习用于复杂格式教学。在引入一个全新的、复杂的输出格式时，直接用所有数据进行训练可能会让模型“学废”。论文中先用“标准”案例教学，再引入“边缘”案例的SFT两阶段方法，是一个非常实用且高明的技巧。启发：当您需要微调模型以遵循某种复杂指令或格式时，可以设计一个由易到难的数据投喂策略。 产品思维：为模型设计“控制旋钮”。论文不仅提升了模型的性能，还提供了一个推理时的“控制旋钮”（block cap），这极大地提升了模型的实用性和产品价值。启发：在设计AI功能时，多思考如何为用户或开发者提供控制其行为的参数，而不是提供一个“黑箱”。这能让您的AI产品适应更多样化的场景。 迭代优化思想：SFT → DPO → RL 的流程体现了一种逐步求精的优化思想。先教会“是什么”（格式），再优化“好不好”（偏好），最后精调“极致”（权衡）。启发：在解决复杂AI问题时，可以分解目标，通过一个流水线式的多阶段训练过程，逐步达成最终目的。 需要补充的背景知识： Chain-of-Thought (CoT) Prompting：这是理解本工作起点和动机的基础。 Direct Preference Optimization (DPO)：这是理解论文第二阶段如何教会模型“权衡”的关键。了解它与传统的基于奖励模型的RLHF（如PPO）的区别。 Reinforcement Learning (RL)：特别是 Proximal Policy Optimization (PPO)，这是理解论文第三阶段精调过程的基础。您不需要深入到数学细节，但需要理解其基本思想：通过奖励函数引导模型策略的优化。 大模型微调技术：对 SFT (Supervised Fine-Tuning) 有基本的了解。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得丰富的启发。\n","date":"2025-08-23T00:00:00Z","permalink":"/think-in-blocksadaptive-reasoning-from-direct-response-to-deep-reasoning/","title":"Think in Blocks：Adaptive Reasoning from Direct Response to Deep Reasoning"},{"content":"利用专家轨迹和逆向强化学习优化一个prm，用于grpo中计算奖励。\n首先，让我们进入对这篇论文的整体深度解读。\n论文深度解读：一个“免费”且“动态”的推理过程裁判 当前，为了提升大型语言模型（LLM）在数学、编程、科学推理等复杂任务上的表现，学术界和工业界主要探索两大类测试时扩展（Test-Time Scaling, TTS）技术。您可以将TTS理解为，在模型推理（回答问题）时，不满足于只生成一个答案，而是通过投入更多的计算资源来探索多种可能性，从而找到更优的解。\n强化学习（RL）路径：这类方法通过奖励信号来优化模型。例如，模型生成一个解题步骤后，如果最终答案正确，就给予正奖励，反之则为负奖励。然而，这种稀疏的、基于最终结果的奖励（Outcome-based Rewards）存在巨大问题。想象一下教一个孩子解一道复杂的数学题，你只在最后告诉他对或错，却不告诉他错在哪一步。这种学习方式效率极低，且训练过程非常不稳定。\n搜索路径：这类方法在推理时，会像棋手一样思考多种“走法”（即多个推理步骤），然后从中选择最好的。为了判断哪一步“走法”更好，需要一个“裁判”，这个“裁判”就是过程奖励模型（Process Reward Model, PRM）。PRM能够为每一个中间步骤打分，指导模型在巨大的搜索空间中前进。然而，训练一个好的PRM“裁判”代价极其高昂。你需要大量的人工标注数据，告诉模型每一步推理的好坏。更糟糕的是，这个PRM是静态的，而主模型（LLM）在不断学习进化。一个静态的“裁判”去指导一个动态的“选手”，很快就会出现分布偏移（distributional shifts）问题，导致模型学会钻空子，也就是奖励滥用（reward hacking）。\n这篇论文敏锐地指出了上述两种路径的根本矛盾，并提出了一个颠覆性的问题：我们为什么需要一个独立、昂贵的PRM？强化学习训练过程中学到的奖励函数，难道不就是最理想、最懂当前模型的PRM吗？\n这正是本文的核心创见。作者提出了一种名为 AIRL-S 的统一框架，其精髓在于：在强化学习训练“选手”的同时，也顺便训练出了一个为它量身定制的“裁判”，并且这个“裁判”是免费且动态更新的。\n它是如何做到的呢？AIRL-S框架巧妙地融合了两种技术：\n对抗性逆向强化学习（Adversarial Inverse Reinforcement Learning, AIRL）：这是整个框架的灵魂。传统的RL是“给定奖励规则，学习最优行为”，而逆向RL（IRL）则是“观察专家行为，反推出奖励规则”。AIRL是一种先进的IRL方法，它通过一个判别器（discriminator）来学习奖励函数。这个判别器的任务是区分“专家走出的好棋谱”（在论文中是正确的推理链，即Reference Rollouts）和“模型自己走出的棋谱”（Policy Rollouts）。为了赢得这场对抗游戏，判别器必须学会精准地为每一步推理打分，从而识别出模仿得不像的步骤。这个经过充分训练的判别器，其内部的打分机制，自然而然地就成了一个高质量的、稠密的、逐步骤的PRM。最关键的是，这个过程完全不需要为中间步骤进行人工标注，只需要提供最终正确的完整推理过程即可。\n组相对策略优化（Group Relative Policy Optimization, GRPO）：这部分主要借鉴了DeepSeek-AI的工作，负责处理来自最终答案正确与否的稀疏奖励。它通过比较一组生成答案的好坏，来稳定地优化模型策略。\nAIRL-S将这两者结合，模型的更新同时受到两方面指导：一是来自AIRL判别器（即PRM）提供的稠密过程奖励，告诉模型“如何走好每一步”；二是来自GRPO的稀疏结果奖励，告诉模型“最终目标是什么”。\n如论文中所述，其统一的目标函数是： \u0026gt; J(θ) = λ * J_AIRL(θ) + (1 - λ) * J_GRPO(θ) 这公式清晰地表明了它如何平衡过程奖励（来自AIRL）和结果奖励（来自GRPO）。\n在推理时，这个在训练中“免费”得到的PRM（判别器）可以直接用于指导各种搜索算法，如束搜索（Beam Search）和蒙特卡洛树搜索（MCTS），极大地提升了模型的推理能力。\n实验结果非常有力地证明了该框架的有效性。基于Qwen2.5-7B-Instruct这样一个中等规模的开源模型，AIRL-S框架训练出的模型在8个主流的数学和代码推理基准测试中，平均性能提升了9%，整体表现媲美了强大的闭源模型GPT-4o。尤其值得注意的是图2，它展示了AIRL-S训练出的PRM具有极强的泛化能力。将这个PRM应用到其他不同的大模型上，依然能稳定地带来性能提升，远超那些依赖静态标注数据训练的PRM。这充分说明，通过AIRL学到的奖励函数，抓住了推理任务的本质，而不仅仅是适配某一个特定模型。\n图2解读: 此图展示了不同PRM与不同LLM组合在数学推理任务上的平均准确率。深灰色柱代表本文的PRM（Qwen2.5-AIRL-S-PRM）。可以清晰地看到，无论搭配哪个基础LLM（横轴），本文的PRM带来的性能增益（深灰色柱的高度）几乎总是最高的，证明了其卓越的性能和泛化能力。\n总而言之，这篇论文的创新贡献是巨大的。它不仅提出了一个效果卓越的新框架，更重要的是，它为我们揭示了一条更高效、更经济地提升大模型推理能力的路径——通过内在的、自洽的训练机制，让模型在学习解决问题的同时，也学会如何评估解决问题的过程。\n接下来，我将从您提出的六个具体方面，对这篇论文进行更详细的剖析。\n### 1. 论文的研究目标是什么？ 核心研究目标：旨在统一强化学习（RL-based）和基于搜索（Search-based）的测试时扩展（TTS）方法，为提升大型语言模型的复杂推理能力创建一个协同、高效的单一框架。\n想要解决的实际问题：\n高昂的标注成本：当前顶尖的搜索算法（如MCTS）依赖于过程奖励模型（PRM）来指导。而训练高质量的PRM需要对推理过程的每一步进行精细的人工或LLM标注，成本极高且难以规模化。 训练与推理的脱节：静态的PRM与持续优化的LLM策略之间存在分布偏移，这会导致模型学会利用PRM的漏洞（奖励滥用），而非真正提升推理能力。 强化学习的低效：单纯依赖最终结果（对或错）的稀疏奖励进行RL训练，存在样本效率低、训练不稳定的问题，难以有效指导模型学习长链条的复杂推理。 对行业发展的重要性： 这个问题是当前大模型从“能聊天”走向“会思考、能解决问题”的关键瓶颈。如果能解决，意味着：\n降低顶尖AI模型的训练门槛：通过免除昂贵的过程标注，使得更多机构有能力训练出具备强大推理能力的模型，促进AI技术的普及和竞争。 提升模型可靠性：动态、自洽的PRM能减少奖励滥用，让模型的推理过程更可靠、更符合逻辑，这在科学发现、代码生成、教育辅导等严肃场景中至关重要。 指明新的技术方向：它将RL和搜索从“二选一”的竞争关系，转变为“一加一大于二”的共生关系，为未来的模型能力扩展开辟了新的思路。 ### 2. 论文提出了哪些新的思路、方法或模型？ 核心新思路： 整篇论文的基石是一个非常优雅的洞见：在RL训练中为了优化策略而学习到的奖励函数，本身就是指导搜索的最佳PRM。它内生地、动态地与模型策略保持一致，完美地解决了静态PRM的分布偏移问题。\n提出的新方法/模型： AIRL-S框架，它是一个统一了RL和搜索的训练与推理系统。其特点和优势体现在以下几个方面：\n用AIRL学习免标注的PRM： 特点：它不依赖任何中间步骤的标签。仅通过对比模型生成的推理路径和参考的正确推理路径，其内置的判别器就能通过对抗训练学会一个高质量的、逐步骤的奖励函数。 优势： \u0026gt; 论文中提到：“…entirely eliminating the need for labeled intermediate process data.” 这从根本上解决了PRM训练的“数据”和“成本”两大难题。 稠密与稀疏奖励的结合： 特点：AIRL-S的策略更新同时由两部分驱动：(1) 来自AIRL判别器的稠密过程奖励，指导每一步的精细优化；(2) 来自GRPO算法的稀疏结果奖励，确保最终目标（即答对问题）的达成。 优势：这种结合既保证了训练的稳定性和高效率（稠密信号），又保证了任务的最终收敛性（稀疏信号），比单一奖励机制更为鲁棒。 训练与推理的无缝统一： 特点：在训练阶段作为奖励函数的判别器，在推理（测试）阶段被直接用作PRM，无需任何修改，无缝衔接Best-of-N、Beam Search、MCTS等搜索算法。 优势： \u0026gt; 论文中强调：“…the resulting PRM simultaneously serves as the critic for RL rollouts and as a heuristic to effectively guide search procedures…” 这确保了PRM与LLM策略的完美对齐，最大限度地减少了奖励滥用风险，实现了训练和推理目标的高度一致。 ### 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且极具说服力的实验来验证AIRL-S框架的有效性，可以总结为“一个核心验证、三大维度对比”。\n实验设计： 核心验证：将一个中等规模的开源模型Qwen2.5-7B-Instruct作为基础，使用AIRL-S框架进行训练，得到最终模型Qwen2.5-7B-AIRL-S。 三大维度对比： 模型自身性能对比 (Policy Model Performance)：将Qwen2.5-7B-AIRL-S的性能与包括GPT-4o、DeepSeek-R1在内的10个业界顶尖的开源及闭源模型进行比较。 PRM泛化能力对比 (PRM Generalizability)：将AIRL-S训练出的PRM（命名为Qwen2.5-AIRL-S-PRM）与其它三种通过不同方式获得的PRM进行对比。评估方法是，将这四个PRM分别与其他不同的LLM（包括本文模型、Phi-4-14B等）组合，观察谁能带来最大的性能提升。 不同搜索算法的有效性对比 (Effectiveness on Search Methods)：在本文模型上，应用MCTS、Beam Search、Best-of-N等不同搜索算法，并与无PRM指导的Self-Consistency基线对比，验证PRM在不同搜索策略下的普适有效性。 实验数据和关键结果： 模型性能：如表1所示，Qwen2.5-7B-AIRL-S在8个数学和代码基准上的平均分达到57.5，超越了所有其他同规模的本地模型（如Phi-4-14B的53.4），并与GPT-4o的57.0持平。这是一个非常惊人的结果，说明该方法能让一个7B模型达到业界顶级模型的性能水平。 PRM泛化性：图2的结果是本文的一大亮点。以Qwen2.5-7B-AIRL-S(Our LLM)为例，其基础准确率(Accuracy@1)是55.3%。 搭配本文的PRM后，准确率提升至61.3%（提升了6.0%）。 搭配其他PRM，如Math-Shepherd-Mistral-7B-PRM，准确率仅提升至59.8%。 这个优势在搭配其他LLM时同样显著，证明了AIRL-S PRM的卓越泛化能力。 搜索算法有效性：图3显示，随着生成路径数量（Generation Rollouts）的增加，所有使用了本文PRM的搜索方法（MCTS, Beam Search, Best-of-N）性能都稳定提升，且显著优于未使用PRM的Self-Consistency基线。这表明PRM的指导是确实有效的。 消融研究 (Ablation Study)：图4通过对比“AIRL-S (有PRM)”和“仅GRPO (无PRM)”的训练过程，清晰地显示出PRM的加入显著提升了模型的训练和验证准确率，并使模型能生成更长的推理链，证明了稠密过程奖励的关键作用。 ### 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 结合当前大模型领域的理解，该研究方向未来充满机遇，但也面临挑战：\n值得探索的问题和挑战： 模型规模的扩展性（Scalability）：论文在7B模型上取得了成功。该框架在更大（如70B、100B+）或更小（如1B）的模型上是否依然有效？训练动态是否会发生变化？这是该技术能否在工业界大规模应用的关键。 任务多样性（Task Diversity）：目前验证集中在数学和代码这类有明确对错答案的“封闭域”推理。在更开放的任务上，如创意写作、法律咨询、商业策略生成，如何定义“好的过程”和“专家示例”？这需要对AIRL框架中的“参考路径”进行更深入的研究。 对参考数据的依赖性（Dependency on Reference Data）：AIRL-S虽然免除了过程标注，但仍需要一批高质量的“正确解题过程”作为参考。如何降低对这些高质量数据的依赖？能否从不完美的、甚至是模型自我生成的（Synthetic）数据中学习？这直接关系到方法的自动化和自进化能力。 与Agent框架的结合：当前的搜索还停留在“思维链”层面。未来，如何将这种PRM引导的搜索与更复杂的AI智能体（Agent）框架结合，让Agent在规划、使用工具、与环境交互时，也能进行高效的、有远见的搜索？ 可能催生的新技术和投资机会： 自我进化的推理引擎：该技术是实现模型自我提升（Self-Improvement）闭环的关键一环。模型可以通过自我实践生成新的正确推理，将其加入参考数据库，反过来再优化自己的策略和PRM。这将催生出能够持续自我学习、无需频繁人工干预的“思考机器”。 低成本、高性能的专用领域模型：对于金融、医疗、科研等专业领域，可以利用该技术，仅需提供该领域的标准解题/工作流范例，即可低成本地训练出具备强大专业推理能力的模型。这是一个巨大的市场机会。 新一代AI开发与训练平台：围绕这种RL与搜索统一的训练范式，可以开发新的MLOps平台，专门用于管理参考数据、协调对抗训练、部署推理搜索服务，为企业提供一站式的高级AI能力构建方案。 ### 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从严谨的学术角度看，仍存在一些可以进一步探讨和完善之处：\n存在的不足及缺失： 计算成本分析缺失：论文强调了其节省了“标注成本”，但回避了“计算成本”的讨论。对抗性训练（AIRL）和强化学习本身是计算密集型的，通常比监督学习需要更多的计算资源和更长的训练时间。论文没有提供与“监督学习PRM + RL”范式在总训练成本上的详细对比。 超参数敏感性讨论不足：公式(6)中的λ是平衡过程奖励和结果奖励的关键超参数。论文中设为0.5，但并未探讨其对最终性能的影响。这类RL框架的性能往往对超参数非常敏感，缺乏这部分讨论会影响方法的可复现性和鲁棒性。 对“参考数据”的质量要求模糊：AIRL的成功高度依赖于“专家示例”的质量。如果参考的推理路径本身存在瑕疵、不够多样或风格单一，模型学到的PRM也可能会有偏见。论文对此未做深入分析。 需要进一步验证和存疑之处： “缓解奖励滥用”的程度：论文声称该方法能“缓解奖励滥用”，因为PRM是动态的。这是一个合理的推断，但奖励滥用是一个非常顽固的问题。需要更长时间、更具挑战性的实验来验证，在长期训练下，模型是否会找到新的、更隐蔽的方式来利用这个动态PRM。 PRM的可解释性：学到的PRM到底在奖励什么？是逻辑的严谨性、步骤的简洁性，还是某种特定的表达风格？对PRM进行可解释性分析，将有助于我们理解模型推理能力的本质，并建立对该系统的信任。 对其他模型架构的适用性：如作者在局限性部分坦言，该方法尚未在Phi-4、Gemma等其他流行的模型架构上验证。其成功可能部分依赖于Qwen系列模型的特定架构特性，普适性有待进一步验证。 ### 6. 我应该从这篇论文中重点学什么？ 如果您希望从中汲取可以立即应用的创新想法，以下几点是核心：\n重点学习的启发和思想： “资源再利用”的思维模式：这是最核心的启发。不要轻易创造一个新组件，先审视现有流程中是否产生了可以被重新利用的“副产品”。本文的精髓就是将RL训练的“副产品”——奖励函数，变废为宝，直接用作推理时的核心组件PRM。这个思想可以应用在任何复杂的系统设计中。 范式统一而非对立：不要将不同的技术路径视为非此即彼的竞争关系，而应思考如何让它们协同增效。RL负责生成搜索所需要的高质量启发式函数（PRM），而搜索则将RL训练出的单一策略点的能力，扩展为一个强大的决策面。 通过“对比”和“对抗”进行隐式学习：当获取显式标签（如每一步的好坏）极其困难时，可以设计一个“对比性”或“对抗性”的任务，让模型在尝试区分“好的”和“坏的”的过程中，隐式地学会评估标准。逆向强化学习（IRL）是这种思想的绝佳体现。 需要补充了解的背景知识： 强化学习基础 (Reinforcement Learning)：特别是策略优化算法，如PPO、TRPO。本文使用的GRPO是其变体。理解Actor-Critic（演员-评论家）框架对于理解本文至关重要，因为PRM就扮演了Critic的角色。 逆向强化学习 (Inverse Reinforcement Learning, IRL)：理解其核心思想——从专家演示中学习奖励函数。建议重点了解AIRL，特别是它如何巧妙地将IRL问题转化为GAN（生成对抗网络）的训练框架。 LLM中的搜索算法 (Search Algorithms in LLMs)：需要熟悉蒙特卡洛树搜索 (MCTS)、束搜索 (Beam Search) 和 Best-of-N 采样在文本生成和推理任务中的具体应用。 PRM vs. ORM: 深刻理解过程奖励模型 (Process Reward Model) 和 结果奖励模型 (Outcome Reward Model) 的区别，这是理解整个LLM对齐和推理优化领域研究的基础。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-23T00:00:00Z","permalink":"/your-reward-function-for-rl-is-your-best-prm-for-searchunifying-rl-and-search-based-tts/","title":"Your Reward Function for RL is Your Best PRM for Search：Unifying RL and Search-Based TTS"},{"content":"context_engineering中memory的好文，这个预测是个很热门的方向。\nimage.png 论文深度解读 大型语言模型（LLMs）如GPT-4等，尽管在生成流畅、相关的文本方面表现出色，但它们存在一个根本性的“健忘”问题。它们的记忆力受限于一个固定的上下文窗口（Context Window），一旦对话历史超出了这个窗口，早期的信息就会被遗忘。这导致AI智能体在多轮、跨天的长期交互中会忘记用户的偏好、之前的承诺或关键事实，严重影响用户体验和信任感。想象一下，你告诉你的AI助手你是素食主义者，但第二天它却向你推荐了鸡肉阿尔弗雷多意面，这就是论文开篇所描绘的典型场景。\n为了解决这个问题，学术界和工业界探索了多种方案，如不断扩大模型的上下文窗口（但这会带来巨大的计算成本和“大海捞针”问题），或使用检索增强生成（Retrieval-Augmented Generation, RAG）技术。然而，传统的RAG方法通常是检索原始文本块，这可能包含大量无关噪声，不够精确。\n这篇论文的核心创新贡献在于提出并实现了一套更精细、高效、生产就绪的记忆架构——Memo和其增强版Memo G (Graph)。它们不再是简单地存储和检索原始对话，而是动态地从对话中提取、整合和检索“显著信息”（salient information），并以结构化的方式进行管理。\nMemo架构是一个以自然语言文本为核心的记忆系统。它的工作流程分为两个关键阶段：提取（Extraction）和更新（Update）。\n提取阶段：当用户和AI产生新的对话（比如一问一答）时，Memo系统并不会直接保存这段对话。相反，它会结合两个信息源来理解上下文：一个是整个对话的全局摘要（conversation summary），另一个是最近的几条对话历史（recent messages）。这种“全局+局部”的上下文设计非常巧妙，既能把握对话的整体脉络，又不会忽略最新的细节。然后，LLM会像一个专业的“信息分析师”，从新的对话中提取出核心的、值得记忆的事实，形成一组候选记忆（candidate facts）。\n更新阶段：这些候选记忆并不会被盲目地存入知识库。系统会先用向量嵌入（vector embeddings）技术，在现有记忆库中检索出语义上最相似的几条旧记忆。然后，它将候选记忆和这些相似的旧记忆一起交给LLM，通过一个类似工具调用（Tool Call）的机制，让LLM自己判断应该执行哪种操作：\nADD：如果这是一个全新的信息，就新增一条记忆。 UPDATE：如果新信息是对旧记忆的补充或修正，就更新旧记忆。 DELETE：如果新信息与旧记忆相矛盾，就删除旧记忆。 NOOP：如果这个信息已经存在或不重要，就什么也不做。 这个设计的高明之处在于，它利用了LLM强大的推理能力来维护记忆库的一致性和时效性，避免了信息的冗余和冲突，使得记忆库变得“小而精”。\n正如论文所说：“Rather than using a separate classifier, we leverage the LLM’s reasoning capabilities to directly select the appropriate operation based on the semantic relationship between the candidate fact and existing memories.” （我们不使用一个单独的分类器，而是利用LLM自身的推理能力，根据候选事实和现有记忆之间的语义关系来直接选择合适的操作。）\n而Memo G则是在Memo的基础上，引入了知识图谱（Knowledge Graph），对记忆进行更深层次的结构化。在Memo G中，记忆不再是孤立的文本事实，而是由实体（Entities）作为节点、关系（Relationships）作为边的图。例如，“Alice”和“San Francisco”是实体节点，它们之间可以通过一条“LIVES_IN”（居住于）的边连接起来。\n这种图结构极大地增强了系统进行复杂推理的能力。当需要回答一个涉及多个事实、需要“多跳推理”的问题时（比如“Alice去年住在哪里？”），系统可以在图上沿着关系路径进行遍历，从而找到答案。这对于需要理解事件顺序、因果关系的时间性推理（Temporal Reasoning）任务尤其有效。\n为了验证这两种架构的有效性，论文在LOCOMO这个专门为评测对话系统长期记忆能力而设计的数据集上进行了详尽的实验。他们不仅比较了传统的F1、BLEU-1分数，还引入了一个更符合人类判断的评估指标——LLM-as-a-Judge (J)，即用一个更强大的LLM（如GPT-4）来评判模型生成答案的质量。\n实验结果令人印象深刻。在四个维度的测试（单跳、多跳、开放域、时间性）中，Memo和Memo G在大多数指标上都超越了包括MemGPT、LangMem等在内的所有基线模型。\n例如，在衡量综合性能的Overall J score上，传统的RAG方法最高只能达到约61%，而Memo达到了66.88%，Memo G更是达到了68.44%。更重要的是，相比于将全部对话历史作为上下文的Full-context方法（其J score最高，为72.90%），Memo的p95延迟（95%情况下的响应时间）降低了92%（从17.1秒降至1.44秒），成本也大幅下降。这充分证明了Memo在性能和效率之间取得了绝佳的平衡，真正具备了“生产就绪”（Production-Ready）的潜力。\n论文在Table 2中明确指出，Full-context方法的p95延迟为17.117秒，而Memo为1.440秒。这组数据直观地展示了其在效率上的巨大优势。\n总而言之，这篇论文不仅仅是提出了一种新的技术模型，更是为构建实用、高效、可靠的AI长期记忆系统提供了一套完整的工程蓝图和评估框架。它清晰地指明了，未来的AI记忆系统不应是简单的信息堆砌，而应是像人类一样，具备主动提取、组织、更新和遗忘信息的能力。\n### 1. 论文的研究目标与实际问题 研究目标：论文的核心研究目标是设计并实现一个可扩展、高效且生产就绪的长期记忆架构，以克服大型语言模型在固定上下文窗口限制下无法维持长期对话连贯性的问题。\n解决的实际问题：\n遗忘问题 (Forgetting Problem)：AI智能体在与用户进行跨越数天、数周的长期交互后，会忘记用户的关键信息，如个人偏好、历史事件、特定知识等。 效率与成本问题 (Efficiency and Cost Issue)：虽然可以通过扩展上下文窗口来容纳更多历史信息，但这会导致推理延迟急剧增加、计算成本飙升，在实际应用中并不可行。 信息噪声问题 (Information Noise Problem)：传统的RAG方法直接检索原始文本块，这些文本块中往往包含了大量与当前问题无关的“噪声”，干扰了LLM的精确回答。 行业意义：\n提升用户体验：一个拥有可靠长期记忆的AI，能够提供真正个性化和连贯的交互，就像一个了解你的老朋友，这将极大提升用户满意度和粘性。 增强信任与可靠性：在医疗、教育、金融咨询等高风险领域，AI的记忆连续性至关重要。一个不会忘记病人病史、学生学习进度或客户财务状况的AI，是建立信任、确保服务质量的基础。 推动AI智能体走向实用：长期记忆是AI从一个“问答机器”转变为一个能执行复杂、长期任务的自主智能体（Autonomous Agent）的关键瓶颈。解决了这个问题，才能让AI在个人助理、项目管理、客户关系维护等场景中发挥更大价值。 ### 2. 论文提出的新思路、方法与优势 论文提出了两种核心架构：Memo和Memo G。它们的核心思路是从“被动存储”转向“主动管理”。\nMemo (自然语言记忆架构) 思路：将非结构化的对话历史，通过LLM提炼成一组简洁、显著的自然语言事实，并动态维护这个记忆库。 特点与优势： 双重上下文提取 (Dual Contextual Information)：在提取记忆时，同时使用全局摘要和近期消息作为上下文。这比仅依赖近期对话历史的方法能更准确地判断信息的“显著性”。 LLM驱动的记忆更新 (LLM-driven Update Mechanism)：创新性地使用了ADD, UPDATE, DELETE, NOOP四种操作，让LLM来决策如何处理新信息。这使得记忆库能够自我修正和进化，保持高度的一致性和准确性，远优于仅做增量添加的方法。 高信噪比 (High Signal-to-Noise Ratio)：由于存储的是提炼后的事实而非原始对话，输入给LLM进行问答的上下文更加精炼，减少了无关信息的干扰。 Memo G (图记忆架构) 思路：在Memo的基础上，将记忆组织成一个知识图谱，用实体-关系-实体的三元组形式来表示事实，从而更好地捕捉信息之间的复杂关联。 特点与优势： 结构化表示 (Structured Representation)：图结构能显式地表示实体间的关系（如“谁”、“做了什么”、“在哪里”），这对于需要多跳推理和理解因果、时序关系的复杂问题至关重要。 增强的推理能力 (Enhanced Reasoning Capability)：当面对“A和B是什么关系？”或者“事件C发生前发生了什么？”这类问题时，图结构比纯文本记忆更有优势。 精确检索 (Precise Retrieval)：可以基于实体或关系进行检索，比纯粹的语义相似度检索更加精确和可解释。 论文在Figure 3中清晰地展示了Memo G的架构，从对话消息中先提取实体（nodes），再生成关系（triplets），最后通过冲突检测（Conflict Detector）和更新解析器（Update Resolver）来更新知识图谱。这是一个非常清晰的工程流程。\n与之前的方法相比，Memo和Memo G的共同优势在于在准确性、效率和成本之间取得了出色的平衡，使其具备了部署到真实世界应用的潜力。\n### 3. 实验设计与关键结果 论文通过在LOCOMO基准数据集上进行全面的对比实验，来验证其方法的有效性。\n实验设计: 数据集 (Dataset)：LOCOMO，包含10个超长对话（平均26000个token），模拟了多天的交互，并配有200个需要长期记忆才能回答的问题。 评估指标 (Metrics)： 性能指标：F1, BLEU-1, 以及更重要的 LLM-as-a-Judge (J)，后者用于评估答案的事实准确性、相关性和完整性。 部署指标：延迟 (Latency)（p50和p95的搜索与总时间）和Token消耗 (Token Consumption)。 基线 (Baselines)：实验比较了六大类基线，覆盖了现有主流方法，包括： 已有的LOCOMO基准模型 (如 A-Mem, MemGPT) 开源记忆方案 (如 LangMem) 多种配置的RAG 全上下文处理 (Full-Context) 商业模型 (OpenAI’s memory feature) 记忆服务提供商 (Zep) 实验数据与结果： 以下是论文Table 1和Table 2中部分关键数据的简化重现，直观地展示了其性能优势：\n表1：不同问题类型的性能对比 (J score)\n方法 Single-Hop Multi-Hop Open-Domain Temporal LangMem 62.23 47.92 71.12 23.43 OpenAI 63.79 42.92 62.29 21.71 Zep 61.70 41.35 76.60 49.31 Memo 67.13 51.15 72.93 55.51 Memo G 65.71 47.19 75.71 58.13 从上表可以看出： * Memo在需要直接信息检索的单跳（Single-Hop）和需要整合多条信息的多跳（Multi-Hop）问题上表现最佳。 * Memo G凭借其图结构，在需要理解事件顺序的时间性（Temporal）问题上取得了最高分。 * 所有方法在开放域（Open-Domain）问题上都表现不错，其中Zep略微领先，但Memo和Memo G紧随其后，表现依然强劲。\n表2：总体性能与延迟对比\n方法 Overall J score Total p50 Latency (s) Total p95 Latency (s) RAG (best) 60.97 0.802 1.907 LangMem 58.10 18.53 60.40 Zep 65.99 1.292 2.926 Full-context 72.90 9.870 17.117 Memo 66.88 0.708 1.440 Memo G 68.44 1.091 2.590 “In particular, Memo attains a 91% lower p95 latency and saves more than 90% token cost, thereby offering a compelling balance between advanced reasoning capabilities and practical deployment constraints.” （特别是，Memo实现了低91%的p95延迟，并节省了超过90%的token成本，从而在先进的推理能力和实际的部署限制之间提供了一个引人注目的平衡。）\n这张表揭示了最关键的权衡： * Full-context虽然准确率最高，但其延迟是灾难性的，完全不适用于实时交互。 * Memo在所有具备可用性的模型中，延迟最低，p95延迟仅为1.44秒，同时其准确率（J score）也极具竞争力，比最好的RAG高出近6个点。 * Memo G在准确率上比Memo更高，延迟也控制在可接受的范围内，展示了结构化记忆的价值。\n这些数据有力地证明了论文所提出的方法在保证高质量回答的同时，极大地提升了系统的响应速度和经济性。\n### 4. 未来研究方向、挑战与机会 结合当前领域理解，该研究方向未来充满机遇和挑战：\n值得探索的问题与挑战： 多模态记忆 (Multimodal Memory)：当前研究主要集中在文本对话。未来的智能体需要能够记忆和理解图像、声音和视频中的信息。如何有效地提取和组织多模态记忆是一个巨大的挑战。 动态遗忘机制 (Dynamic Forgetting Mechanisms)：人类的记忆不是只进不出的。如何设计更高级的、类似人脑的遗忘机制，让AI能主动忘记过时的、不重要的或私密的信息，是提升系统鲁棒性和安全性的关键。 记忆的隐私与安全 (Memory Privacy and Security)：AI的记忆库中存储了大量用户隐私。如何进行加密、权限控制，防止记忆被篡改或泄露，是一个亟待解决的伦理和技术难题。 分层与抽象记忆 (Hierarchical and Abstractive Memory)：人类记忆是分层的，有瞬时记忆、短期记忆和长期记忆，并且能够进行高度抽象。探索类似的分层架构，让AI能形成对世界的更高层次的、抽象的理解，是通往通用人工智能的重要一步。 新的技术和投资机会： 企业级记忆中台 (Enterprise Memory Platforms)：为企业客户提供统一的AI记忆解决方案，整合不同业务线的交互数据（如客服、销售、内部知识库），打造拥有企业专属长期记忆的AI员工。 超个性化AI伴侣 (Hyper-Personalized AI Companions)：基于长期记忆的AI伴侣，可以用于心理健康、老年人陪护、儿童教育等领域，提供真正懂你的情感支持和个性化指导。 下一代AI开发框架 (Next-Gen AI Development Frameworks)：将先进的记忆管理模块作为核心组件，集成到新的AI开发框架中，降低开发者构建复杂AI智能体的门槛。 ### 5. 论文的不足与待验证之处 从批判的视角看，这篇论文也存在一些可以进一步探讨的方面：\n评估指标的局限性：虽然LLM-as-a-Judge比传统指标更优，但它本质上还是用一个模型去评估另一个模型。这种方法可能存在偏见（例如，评判者LLM可能偏爱某种风格的回答），其评估结果的一致性和可靠性仍需更多研究来验证。 数据集的单一性：实验主要在LOCOMO这一个数据集上进行。虽然该数据集质量很高，但它主要模拟的是日常对话。该方法在更专业的领域（如法律、医疗）或需要执行复杂任务的场景下的表现，还有待进一步验证。 模型依赖性：论文中的所有LLM操作都使用了gpt-4o-mini。该方法的性能在多大程度上依赖于特定模型的强大能力？如果换成开源模型（如Llama 3）或能力稍弱的模型，其效果会如何，这是一个值得探究的问题。 实时性挑战：论文提到Memo G的图构建“在最坏情况下也能在一分钟内完成”。对于某些需要极高实时性的应用场景（如实时交易助手），这个延迟可能仍然过高。记忆的写入和更新过程如何进一步优化以达到毫秒级响应，是一个工程挑战。 冲突解决的鲁棒性：Memo G中的冲突检测和更新解析器是基于LLM的判断。当面对高度模糊、复杂或恶意注入的矛盾信息时，这个机制的鲁棒性如何，是否可能被“欺骗”，需要更严格的对抗性测试。 ### 6. 核心启发与知识补充 对于希望从中汲取创新想法的您来说，这篇论文提供了几个宝贵的启示：\n重点学习： “提取-更新”范式：不要把AI记忆看作一个简单的数据库。学习这种“先提炼，再决策更新”的闭环管理思想。这是一个非常通用和强大的设计模式，可以应用在任何需要动态维护知识库的系统中。 利用LLM进行元认知：论文最大的亮点之一是让LLM自己管理自己的记忆。通过巧妙的提示工程和工具调用，LLM可以扮演“数据管理员”的角色。这种让LLM参与到系统工作流（Workflow）中的思路，是构建复杂智能体的关键。 结构化与非结构化的权衡：Memo（非结构化）和Memo G（结构化）的对比清晰地展示了不同记忆形式的优劣。对于追求速度和简单性的场景，自然语言记忆可能足够；而对于需要深度推理的场景，图结构等结构化方法是必要的。在你的应用中，思考哪种表示更合适，或者如何将它们结合起来。 面向部署的评估思维：在评估一个AI系统时，不要只看准确率。学习论文同时关注性能和部署指标（延迟、成本）的评估框架。一个在实际生产中跑不起来的模型，即使指标再高也没有价值。 需要补充的背景知识： 检索增强生成 (RAG)：深入理解RAG的工作原理、不同变体（如Chunking策略、Embedding模型选择、Reranking等）。 知识图谱 (Knowledge Graphs)：了解知识图谱的基本概念（实体、关系、三元组）、构建方法（如实体识别、关系抽取）以及存储和查询技术（如Neo4j、SPARQL）。 向量数据库 (Vector Databases)：熟悉向量数据库的原理（如HNSW等索引算法），以及Chroma, Pinecone, Weaviate等主流工具的使用。 LLM Function Calling / Tool Use：深入学习如何让LLM通过调用外部工具来执行任务，这是实现论文中ADD/UPDATE/DELETE等操作的技术基础。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-23T00:00:00Z","permalink":"/mem0building-production-ready-ai-agents-with-scalable-long-term-memory/","title":"未命名"},{"content":"context_engineering的好文\n论文深度解读 这篇论文的核心目标是解决当前大模型在处理超长文本时面临的“不可能三角”困境，即无限文本长度（Arbitrary Length）、无损性能（Lossless Extrapolation）和线性计算成本（Linear Cost）三者难以兼得。传统的长文本方案，无论是通过位置编码外推（如YaRN、NTK-Aware Scaled RoPE）、稀疏注意力机制（如Longformer）还是状态空间模型（如Mamba），都或多或少地在其中一两个方面做出妥协。例如，基于完整注意力的模型虽然效果好，但其计算和内存复杂度是文本长度的二次方（O(n²)），在处理数百万token的文本时成本高昂且不切实际；而线性复杂度的模型虽然快，却常常伴随着性能下降和外推能力的损失。\nMemAgent的作者们另辟蹊径，提出了一种全新的范式。他们认为，与其在模型架构上“死磕”，不如改变问题本身的解法。其核心思想源于一个非常符合直觉的人类行为：\nWhen humans process long-context information, we tend to abstract out the main revealing conceptions to capture the essence of the whole text, often by making notes of critical details or using short-handed stenograph to record the key points, while discarding redundant and irrelevant data. （当我们处理长文本信息时，我们倾向于抽象出关键概念来把握全文精髓，通常通过做笔记或速记来记录要点，同时丢弃冗余和无关的信息。）\n基于此，他们设计了 MemAgent，一个基于强化学习（Reinforcement Learning, RL）的记忆代理工作流。它并不直接处理一个巨大的、完整的文本，而是将长文本视作一个“证据流”，进行分块（chunk-by-chunk）处理。\n其工作流程（见下图，原文Figure 2）可以概括为： 1. 分块读取：模型在每一步只读取一小部分文本（一个chunk）和一段固定长度的“记忆”（memory）。 2. 记忆更新：在读取完当前文本块后，模型的核心任务是生成一段新的、更新后的记忆，用来覆盖（overwrite）旧的记忆。这段新记忆是模型对截至目前所有相关信息的压缩和提炼。 3. 循环往复：模型不断重复上述过程，直到读完所有文本块。 4. 最终回答：最后，模型利用最终形成的记忆来回答用户提出的问题。\nimage.png image.png 这个设计最精妙的地方在于，它将一个棘手的长文本处理问题，转化为了一个序列决策问题。由于记忆的长度是固定的，模型在每一步处理的上下文长度（文本块 + 记忆）也是恒定的，这直接导致了整个流程的计算复杂度与文本总长度呈线性关系（O(N)），完美解决了成本问题。\n然而，新的挑战也随之而来：模型如何“学会”更新记忆？“覆盖”是一个离散操作，无法通过传统的反向传播进行端到端优化。这正是强化学习登场的舞台。作者将“生成新记忆”这一行为看作是RL中的一个动作（action）。模型因为做出了一个“好的”记忆更新（即保留了对最终答案有用的信息，丢弃了无用信息），并最终答对了问题，而获得奖励（reward）。\n为了实现这一目标，作者对现有的RL算法进行了改进，提出了多轮对话DAPO（Multi-Conv DAPO）。传统的LLM强化学习算法（如PPO、DAPO）通常针对单轮或多轮连续对话进行优化。但在MemAgent的设定中，一次完整的长文本处理过程包含了多次独立的“上下文处理”对话（每次更新记忆都是一轮），这些对话共同服务于最终的“答案生成”对话。Multi-Conv DAPO算法的核心在于，它将最终答案的奖励公平地分配给所有在此之前的记忆更新步骤，从而让模型学会每一步都做出最优的决策。\n论文的实验结果极其亮眼。最令人印象深刻的是，一个在8K上下文长度上训练的MemAgent模型，能够成功外推到处理长达350万token的问答任务，并且性能损失极小。从原文的Figure 1和Table 2中可以看到，RL-MemAgent-14B模型在处理7K长度文本时准确率为83.59%，在处理3.5M长度文本时准确率仍有78.12%。相比之下，一些先进的基线模型，如QwenLong-L1-32B，在超过其训练长度后性能急剧下降，而Qwen2.5-Instruct-1M在896K长度时准确率已经降为0。这充分证明了MemAgent在长文本外推能力上的巨大优势。\n此外，通过消融实验（Ablation Study），作者证明了强化学习训练的必要性。一个只具备记忆机制但未经RL训练的模型（MemAgent w/o RL），其性能会随着文本长度增加而显著下降。而经过RL训练后，模型性能则能保持稳定（见原文Figure 5）。这说明，仅仅提供一个“记忆”的结构是不够的，模型必须通过RL学会如何有效地“使用”这个记忆。\n总而言之，MemAgent论文提供了一个优雅且高效的工程解决方案，它没有发明新的模型结构，而是通过一个巧妙的工作流和RL训练范式，将任何中等上下文长度的LLM“变身”为强大的长文本处理工具，为解决行业核心难题开辟了全新的道路。\n1. 论文的研究目标是什么？ 研究目标：解决大型语言模型在处理超长文本时面临的根本性挑战，即如何在保持线性计算复杂度的同时，实现对任意长度文本的处理，并且在超出训练长度时性能不发生显著衰减（即强大的外推能力）。\n实际问题：当前的大模型通常有一个固定的上下文窗口（Context Window），例如4K, 8K, 或128K tokens。一旦需要处理的文本（如一本小说、一份几百页的财报、一个完整的代码库）超过这个长度，模型就无能为力。现有的解决方案存在以下问题：\n暴力扩展：直接使用完整的注意力机制处理长文本，会导致O(n²)的计算灾难。 架构修改：采用稀疏注意力、线性注意力或状态空间模型等架构，虽然能降低复杂度，但往往以牺牲部分性能为代价，且在处理远超训练长度的文本时表现不佳。 RAG (检索增强生成)：依赖外部检索系统，但检索质量不稳定，且难以处理需要全局理解才能回答的复杂问题。 行业意义：解锁真正意义上的长文本处理能力，是推动大模型从“玩具”走向“生产力工具”的关键一步。它将极大地扩展LLM的应用场景，例如：\n知识密集型工作：自动分析和总结法律合同、科学文献、金融财报等长篇文档。 软件开发：理解整个代码库的上下文，进行代码生成、Bug修复和重构。 个性化AI：实现具有长期记忆的AI助手，能够记住与用户数月乃至数年的对话历史。 内容创作：阅读整本书后，进行续写、角色分析或情节推理。 2. 论文提出了哪些新的思路、方法或模型？ 论文的核心创新在于提出了一套新的工作流（Workflow）和相应的训练算法，而不是一个全新的模型架构。\n新思路：流式处理与记忆更新 核心理念：将长文本视为一个“受控的证据流”（controlled stream of evidence），而非一个静态的整体。这从根本上改变了问题的建模方式。 记忆机制：引入一个固定大小、基于token的显式记忆（explicit memory）。这个记忆和输入的文本块一样，都是普通token，可以直接被Transformer模型处理，无需任何架构改动。 覆盖策略（Overwrite Strategy）：这是实现系统可扩展性的关键。记忆的长度永远不会增长，模型通过不断生成更精炼的新记忆来迭代更新，确保了每一步的计算成本都是恒定的。 新方法：MemAgent工作流 该工作流将推理过程分解为两个模块： 上下文处理模块（Context-Processing）：循环迭代，输入为“问题 + 上一轮记忆 + 当前文本块”，输出为“更新后的记忆”。 答案生成模块（Answer-Generation）：在处理完所有文本后调用，输入为“问题 + 最终记忆”，输出为最终答案。 新算法：多轮对话DAPO（Multi-Conv DAPO） 特点与优势：该算法是为MemAgent工作流量身定制的。 \u0026gt; However, due to the nature of the MEMAGENT approach, it generates multiple context-independent conversations for a single query… Therefore, policy optimization cannot be implemented by simply applying the attention mask as is done in multi-turn tool-calling optimization. \u0026gt; （然而，由于MEMAGENT方法的特性，它会为单个查询生成多个上下文独立的对话……因此，不能像在多轮工具调用优化中那样简单地应用注意力掩码来实现策略优化。） 相比传统RL算法：传统RL方法（如用于工具学习的RL）通常处理一个线性的、交错的对话流。而MemAgent的记忆更新过程是多个并行的、服务于同一目标的“子对话”。Multi-Conv DAPO通过将最终的结果奖励（outcome reward）（即答案是否正确）均匀地分配给所有前序的记忆更新对话，从而对整个决策链进行优化。这使得模型能够学习到一个通用能力：如何提炼和压缩信息以服务于最终目标，而不是学习特定位置的注意力模式。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一系列严谨的实验来验证MemAgent的有效性、外推能力和泛化能力。\n实验设计： 基础模型：选用Qwen2.5-7B和Qwen2.5-14B作为基础模型，证明该方法的通用性。 训练设置：为了专门测试外推能力，作者故意将模型的训练上下文窗口限制在8K。 数据集：主要使用RULER基准中的HotpotQA任务。这是一个多跳问答数据集，被改造用于长文本评测，非常适合检验模型在充满干扰信息（haystack）的长文本中定位和整合关键信息（needle）的能力。 测试集生成：通过在问题周围填充不同数量的干扰文章，生成了从7K到3.5M token等不同长度的测试集。 关键实验与数据结果： 长文本外推能力测试 (Main Results, Table 2)：这是核心实验，旨在比较MemAgent与当前主流长文本模型在不同长度下的性能。 MemAgent表现：如前所述，RL-MEMAGENT-14B在3.5M token长度下准确率仍高达78.12%，相比于它在7K长度下的83.59%，性能几乎没有衰减。 基线模型对比：QwenLong-L1-32B在224K后性能开始大幅下滑，到896K时仅剩11.72%。被认为拥有1M上下文的Qwen2.5-Instruct-1M系列，在896K时准确率已降至0%，说明它们虽然有很大的上下文窗口，但无法有效利用超长距离的信息。 模型 7K 112K 896K 3.5M QwenLong-L1-32B 72.66% 31.25% 11.72% N/A Qwen2.5-Instruct-14B-1M 60.16% 50.00% 0.00% N/A RL-MEMAGENT-14B 83.59% 76.56% 77.34% 78.12% 消融研究 (Ablation Study, Figure 5)：该实验旨在验证强化学习（RL）的必要性。 实验对比：比较了三个模型：1) 原始模型截断输入，2) 有记忆机制但无RL训练的模型 (MemAgent w/o RL)，3) 完整的RL训练模型。 结果：无RL训练的模型性能随着文本变长而持续下降，而RL训练后的模型性能曲线几乎是平的。这强有力地证明了RL是教会模型如何有效管理记忆的关键。 跨任务泛化能力测试 (Out-of-Distribution Tasks, Figure 6)：评估模型学习到的记忆能力是否能泛化到训练任务之外。 实验内容：在RULER基准的其他任务（如多针寻草、变量跟踪等）上进行测试。 结果：MemAgent在各种任务上都表现出一致的优越性。例如，MEMAGENT-14B在10个RULER任务上的平均准确率超过95%（在512K上下文内），远超所有基线模型。 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 该研究方向极具潜力，未来可以从以下几个方面进行探索：\n更复杂的记忆结构与操作： 结构化记忆：当前的记忆是扁平的token序列。未来可以探索更复杂的记忆结构，如知识图谱、键值对（Key-Value）或层级化摘要，这可能让模型更好地组织和检索信息。 多样化记忆操作：除了“覆盖”，是否可以引入“追加（append）”、“编辑（edit）”、“遗忘（forget）”等更精细的记忆操作？这将构成一个更丰富的RL动作空间，但也增加了训练难度。 高级强化学习算法： 智能奖励分配：目前采用均匀奖励分配。未来可以研究更智能的信用分配（Credit Assignment）策略。例如，一个关键信息的发现步骤是否应该比常规的总结步骤获得更高的奖励？ 探索与利用：如何平衡对新信息的探索和对现有记忆的巩固利用，是一个值得研究的课题。 与现有技术的融合： 混合模型：是否可以将MemAgent与RAG结合？当记忆模块判断信息不足时，可以主动触发一次外部检索，并将检索结果作为新的文本块来处理。 与高效注意力结合：在处理单个文本块时，依然可以利用Flash Attention等高效注意力机制来加速计算。 新的技术和投资机会： 下一代RAG系统：MemAgent的思想可能催生出“自适应RAG”或“RAG 2.0”，其中LLM自身成为主动的知识管理者，而不是被动地接收检索结果。 企业级知识管理：为企业提供能够消化全部内部文档（数百万甚至上亿token）的AI解决方案，实现真正的“企业大脑”。 专业领域AI助手：在法律、医疗、科研等领域，打造能够持续学习和记忆领域知识的专家级AI助手。 开发工具链：围绕这种Agentic Memory范式，可能会出现新的开发框架、调试工具和优化服务。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但仍存在一些潜在的局限性和待验证的问题：\n任务类型的局限性： 论文的主要评估任务是抽取式问答（Extractive QA），例如HotpotQA和Needle-in-a-Haystack。这类任务的核心是“定位和提取”。对于需要对整个文本进行高度抽象、综合和创造性推理的任务（例如，“请分析《百年孤独》中魔幻现实主义的叙事风格”），MemAgent的表现如何尚待验证。它的记忆机制是否能保存足够复杂的文体和情感信息是一个疑问。 错误累积与鲁棒性： 这是一个序列决策过程，早期的错误可能会被累积和放大。如果模型在处理前面某个文本块时犯了关键错误（例如，丢弃了一个对后续至关重要的信息），它可能无法在后续步骤中恢复。论文对此“灾难性遗忘”的风险和应对策略讨论不足。 推理速度与延迟： 虽然计算复杂度是线性的，但其串行处理的本质意味着总的推理时间是所有步骤时间的累加。对于需要低延迟的应用，这种分块处理的方式可能成为瓶颈，因为它无法像并行架构那样一次性处理。论文主要关注FLOPs，但对实际的wall-clock time讨论较少。 “记忆”的真正内涵： 将token序列称为“记忆”是一个非常贴切的类比，但其本质上仍是一个高度浓缩的文本摘要。它是否真正形成了类似人类的结构化、可推理的记忆，还有待进一步的机制解释性研究。 6. 我应该从这篇论文中重点学什么？ 这篇论文为我们提供了非常宝贵的启发，尤其是关于如何“换个思路解决问题”。\n核心启发： 重构问题，而非硬解问题：当在架构层面遇到瓶颈时（如Transformer的O(n²)复杂度），尝试从更高维度的工作流或算法层面重新定义问题。将“处理一个大问题”分解为“处理一系列小问题”，是工程和科研中一种极其强大的思想。 强化学习是Agent工作流的“粘合剂”：对于包含离散决策、需要长期规划的Agentic流程（无论是使用工具还是更新记忆），RL提供了一个完美的数学框架来对其进行端到端优化，将过程与最终结果联系起来。 通用技能的价值 \u0026gt; 特定长度的拟合：通过RL训练模型学会一种通用的“信息压缩与摘要”技能，远比让它去死记硬背超长上下文中的位置信息要更具扩展性和鲁棒性。这是一种“授人以渔”的训练哲学。 需要补充的背景知识： 长文本LLM技术：建议了解主流的长文本解决方案，包括： 位置编码外推：RoPE (Rotary Position Embedding) 及其改进，如 PI, YaRN, DCA。 高效注意力机制：Sparse Attention (Longformer), Linear Attention, Flash Attention。 非Transformer架构：State Space Models (Mamba, Griffin), RWKV。 了解这些技术的优缺点，才能更深刻地理解MemAgent的创新之处。 LLM的强化学习： 基础：了解 RLHF (Reinforcement Learning from Human Feedback) 的基本流程，以及核心算法 PPO (Proximal Policy Optimization)。 前沿：学习 DPO (Direct Preference Optimization) 及其变体（如本文的DAPO）。DPO是当前更稳定、更受欢迎的对齐技术。 AI Agent (智能体)： 了解AI Agent的基本概念，特别是像 ReAct (Reason + Act) 这样的框架，它们展示了LLM如何通过“思考”和“行动”的循环来解决复杂问题。MemAgent可以看作是一种特殊形式的、专注于内部记忆管理的Agent。 ","date":"2025-08-22T00:00:00Z","permalink":"/memagentreshaping-long-context-llm-with-multi-conv-rl-based-memory-agent/","title":"MemAgent：Reshaping Long-Context LLM with Multi-Conv RL-based Memory Agent"},{"content":"类似Reinforcing General Reasoning without Verifiers和RLPR：EXTRAPOLATING RLVR TO GENERAL DOMAINS WITHOUT VERIFIERS，将RLVR拓展到开放领域中。\n论文深度解读：从“可验证”到“可度量”的范式革命 当前，以强化学习（Reinforcement Learning）为核心的对齐技术，特别是基于可验证奖励的强化学习（Reinforcement Learning from Verifiable Rewards, RLVR），已成为驱动顶尖大模型（如OpenAI的GPT系列、DeepMind的Gemini系列）能力飞跃的关键引擎。RLVR的核心思想是，利用那些模型难以生成但易于验证的任务来进行训练。例如，在数学题中，模型可能很难直接得到正确答案，但验证一个答案是否正确（比如通过对比标准答案）却非常简单；在代码生成中，执行单元测试（Unit Test）就能客观地判断代码的正确性。这种范式极大地推动了模型在逻辑推理、数学和编程等领域的能力。\n然而，这篇论文敏锐地指出了RLVR范式的“阿喀琉斯之踵”：它严重依赖于存在明确、客观、可自动验证正确答案的任务领域。\nWhile effective, this requirement for unambiguous correctness largely confines RLVR to domains with clear, automatically checkable outcomes.\n这意味着，对于那些更广阔、更模糊、更具人文色彩的领域——例如创意写作、共情对话、情感表达、艺术评论——RLVR几乎无能为力。因为在这些领域，答案没有绝对的对中，评价标准是主观、多维度且复杂的。这正是这篇论文试图攻克的堡垒。\n论文的核心贡献在于提出了一个名为Rubicon的框架，其本质思想是用“可度量”（Measurable）取代“可验证”（Verifiable）。它不追求一个二元的“对/错”信号，而是构建了一套基于量规的奖励系统（Rubric-based Reward System）。“量规”（Rubric）这个词是关键，它源自教育学，指的是一套清晰、具体、多维度的评分标准。作者认为，即使一个任务没有标准答案，我们依然可以从多个维度去评价其质量。例如，评价一段故事，我们可以从“情节构造的精巧度”、“人物形象的丰满度”、“语言风格的独特性”、“情感传达的真挚度”等多个维度进行打分。\n为了实现这一构想，论文做了几项关键工作： 1. 构建了庞大的量规库：他们构建了迄今为止最大的量规奖励系统，包含超过10,000条由人类专家、大型语言模型（LLMs）以及人机协作生成的量规。这是一个巨大的工程，为后续的RL训练奠定了数据基础。 2. 设计了多维奖励框架：不同于传统RL中的单一标量奖励值，Rubicon框架输出一个多维的奖励向量 R(y|x, R) = [r₁(y|x), r₂(y|x), ..., rk(y|x)]，其中每一维 rk 对应一条量规的得分。这种设计保留了评价的粒度，使得奖励信号更加丰富和具有解释性。 3. 提出了先进的奖励聚合策略：为了将多维奖励向量转换为可用于优化的单一标量奖励，论文超越了简单的加权求和。他们引入了如否决机制（Veto Mechanisms）（在关键的、不可协商的维度上失败，则总奖励为零）、饱和度感知聚合（Saturation-Aware Aggregation）（鼓励模型在多个维度上均衡发展，而不是在单一维度上“刷分”）等高级策略。这使得奖励的塑造更为精细和智能。 4. 实施了分阶段的RL训练流程：论文发现，同时训练严格的指令遵循任务和开放的创造性任务，会出现“跷跷板效应”（Seesaw Effect），即提升一方面能力可能损害另一方面。为解决此问题，他们设计了两阶段训练：第一阶段，使用确定性高的量规（如“是否遵循了字数限制”）训练模型的基础指令遵循能力；第二阶段，再使用更主观、更复杂的量规（如“情感表达是否真诚”）来培养模型的创造性和社交能力。\n实验结果极为亮眼。他们的Rubicon-preview模型（基于30B的Qwen-A3B）仅用了5000多个训练样本，就在多个人文社科和开放式评估基准上实现了平均5.2%的绝对提升。更令人印象深刻的是，这个30B参数量的模型在这些任务上的表现，甚至反超了体量为其20多倍的671B DeepSeek-V3模型2.4个百分点。这充分证明了该方法的高效率（Token Efficiency）。同时，模型在数学和推理等传统能力上没有出现明显下降，有效缓解了能力顾此失彼的问题。\nModel C.W Writing Judge EQ IFE Collie IFS Avg Qwen3-30B-A3B 77.82 75.65 56.20 73.35 83.55 35.77 54.68 65.29 Rubicon-preview 81.89 80.11 69.20 79.55 81.70 40.27 60.79 70.50 ▲ Improvement ↑4.07 ↑4.46 ↑13.00 ↑6.20 ↓1.85 ↑4.50 ↑6.11 ↑5.21 DeepSeek-V3-671B 80.10 74.08 61.30 75.6 81.89 42.69 60.92 68.08 表1：论文核心实验结果，展示了Rubicon-preview在多个开放式基准测试上相较于基线模型和强大的DeepSeek-V3的优势。\n总而言之，这篇论文的创新性贡献在于，它为强化学习对齐开辟了一条超越“非黑即白”的可验证领域的新道路。通过引入“量规”这一核心概念，它为训练和评估模型在主观、复杂、多维度任务上的能力提供了一套系统性的、可扩展的、高效的解决方案。这不仅是对RLVR范式的一次重要拓展，更可能引领大模型对齐进入一个更加精细化、人性化和全面化的新阶段。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标与问题意义 研究目标： 论文的核心研究目标是扩展强化学习对齐方法的适用范围，使其能够处理那些缺乏明确、客观、二元正确答案的开放式、主观性强的任务。具体来说，它想解决在创意写作、情感交流、风格控制等人文中心领域，如何构建有效、可靠且可扩展的奖励信号来指导LLM进行优化的问题。\n想要解决的实际问题： 当前大模型的一个普遍槽点是回答“AI味”太浓、过于说教（didactic tone）、缺乏人情味和真情实感。这是因为现有的对齐方法（无论是基于人类偏好标注的RLHF还是基于验证的RLVR）都很难在这种细微的、风格化的维度上提供有效反馈。论文想要解决的正是这个问题：如何让LLM的回答更像人，更具情感智能（EQ），更富创造力，并且风格可控。\n对行业发展的意义： 这个问题的意义极其重大。随着大模型技术从“能用”向“好用”发展，用户对其要求不再仅仅是信息准确，更在于交互体验的自然、愉悦和个性化。\n拓宽应用场景：解决了这个问题，LLM才能更好地胜任内容创作、心理咨询、个性化教育、娱乐陪伴等对情感和创造力要求极高的角色。 提升用户体验：一个能进行风格化、情感化沟通的模型，能极大地增强用户粘性，是产品从众多竞品中脱颖而出的关键。 深化模型对齐：这推动了模型对齐从“行为安全”和“事实准确”的初级阶段，迈向“价值观对齐”和“人格化塑造”的高级阶段，使得AI能够更好地融入复杂的人类社会。 2. 论文提出的新思路、方法与优势 新思路：量规优先的工作流（Rubric-First Workflow） \u0026gt; To operationalize this, we adopt a rubric-first workflow: we first construct model-verifiable rubrics, then curate or synthesize data that match these rubrics, and finally re-use them for supervision, reward shaping, and evaluation. 传统的思路是“数据优先”，即先收集大量数据，再让人去标注偏好。而本文提出的新思路是“量规优先”。他们首先定义好评价的标准（Rubrics），然后再去收集或生成符合这些标准的数据。这种方法的优势在于： 目标明确：从一开始就清晰地定义了模型需要优化的方向。 标准统一：保证了数据标注、模型训练和最终评估使用的是同一套标准，避免了不一致性带来的噪声。 新方法：基于量规的多维奖励框架（Rubric-Based Multi-Dimensional Reward Framework） 相较于之前方法返回一个单一的好坏分数，该框架的核心特点是多维度的、结构化的反馈。 多维奖励信号：如前所述，奖励是一个向量，每一维对应一个具体的评价标准。这使得模型能够知道自己具体在哪个方面做得好，哪个方面需要改进，而不是得到一个笼统的“好”或“坏”。 先进的奖励聚合策略：这部分是其精髓所在。例如，“否决机制”确保了模型不会为了在某些维度拿高分而牺牲基本要求（比如安全性）；“饱和度感知聚合”则体现了一种“木桶理论”，鼓励模型全面发展。这些都比简单的线性加权更能模拟复杂的人类评价逻辑。 两阶段RL训练（Stage-wise RL Training）：这是一个非常务实的工程实践。它将复杂的优化目标分解，先易后难。第一阶段确保模型“不出格”（遵循硬约束），第二阶段再鼓励模型“出彩”（发展软实力）。这有效缓解了不同优化目标间的冲突，即论文中提到的“跷跷板效应”。 图2：论文中展示的“跷跷板效应”。仅在创意和共情任务（橙色点）上训练，会导致指令遵循能力下降；反之亦然（绿色点）。分阶段训练旨在达到一个平衡（Balanced）状态。 优势总结： 可解释性强：由于奖励基于明确的量规，模型的行为变化可以追溯到具体的评价标准。 数据效率高：实验证明，仅用5K样本就取得显著效果，远低于传统RLHF所需的数据量。 可扩展性好：可以通过增删量规，灵活地调整模型的优化目标，甚至为不同场景定制不同的“AI人格”。 效果显著：在主观性、人文中心任务上，性能提升巨大，实现了“用小模型战胜大模型”的壮举。 3. 实验设计与关键数据结果 实验设计： 实验设计得相当全面，主要包含三个层面： 定量评估（Quantitative Evaluation）： 基线模型：Qwen3-30B-A3B，一个强大的开源模型。 对比模型：DeepSeek-V3-671B，一个以人文能力著称的、参数量远超基线的顶尖闭源模型。 评估基准：分为两类。一类是开放式和人文中心基准（如Creative Writing V3, EQ-Bench3），用于验证其核心优势。另一类是通用和推理能力基准（如MMLU, AIME24），用于检验是否存在能力退化（Regression）。 定性分析（Qualitative Analysis）：通过具体的案例（Case Studies）展示Rubicon-preview模型与基线模型在生成内容上的风格差异，直观体现其在“人性化”和“去AI味”方面的提升。 消融研究（Ablation Study）：虽然论文主体部分没有详述，但在附录和“跷跷板效应”的讨论中，实际上进行了消融研究，即只在部分类型的任务上进行训练，以观察其影响。 关键数据与结果： 核心性能提升：如表1所示，Rubicon-preview在7个开放式基准测试上，平均分从65.29提升到70.50，绝对提升5.21%。在Judge（评判能力）和EQ（情商）这两个基准上提升尤为显著，分别达到13.00%和6.20%。 超越更大模型：Rubicon-preview (70.50 avg) 的平均分超过了DeepSeek-V3 (68.08 avg) 2.42个百分点，证明了方法的有效性，而非单纯依赖模型规模。 通用能力保持：在MMLU、AIME等推理基准上，Rubicon-preview与基线模型性能基本持平，甚至在AIME 2024上还略有提升（+4.17%），这说明该方法没有以牺牲核心推理能力为代价。 \u0026gt; As a result, the model preserves its overall competence while also delivering extra gains on reasoning benchmarks, including AIME 2024 (+4.1%) and AIME 2025 (+0.8%). 4. 未来研究方向、挑战与机会 值得探索的问题与挑战： 量规与RLVR的结合：论文主要关注非可验证领域。一个核心问题是，如何将基于量规的奖励与基于确定性验证的奖励（RLVR）有效结合，在一个统一的框架下同时提升模型的创造力和逻辑推理能力，并有效管理“跷跷板效应”。 量规的规模化法则（Scaling Laws）：论文提出了一个有趣的问题：“a limited number of tokens and a large set of rubrics”是否代表了一种新的模型后训练（Post-training）的规模化法则？研究量规的数量、质量、粒度与模型性能之间的关系，将是未来一个重要的理论方向。 量规的自动生成与优化：目前量规的生成还依赖大量人工和人机协作。如何让模型自动地、动态地生成和调整量规以适应不同任务和用户偏好，是一个巨大的挑战。 个性化对齐：如何利用量规框架为每个用户实现个性化的模型对齐？这需要解决如何快速、低成本地捕获用户个人偏好并转化为有效量规的问题。 新的技术和投资机会： 对齐即服务（Alignment-as-a-Service）：可能会出现专门提供高质量量规库和基于量规的对齐解决方案的公司。企业可以根据自己的业务需求（如客服、营销文案、教育陪练），购买或定制相应的量规包，来“塑造”符合其品牌调性的AI。 AI“人格”或“风格”市场：可能会形成一个创作者市场，在这里，优秀的作家、心理学家、教育家可以设计并出售他们的“量规包”，让用户可以下载并应用到自己的AI助手上，使其拥有特定的沟通风格或专业知识视角。 下一代评估基准：对主观、多维能力的评估将变得愈发重要。开发更全面、更细粒度的、基于量规的自动化评估平台，将成为一个重要的基础设施投资方向。 5. 论文的不足与待验证之处 从批判的视角看，这篇论文虽然出色，但仍存在一些局限性：\n量规构建的主观性与成本：论文虽然构建了10,000+的量规库，但并未详细阐述其构建过程的成本、周期以及如何保证量规本身的高质量和无偏见。量规的设计本身就是一项极具挑战和主观性的工作，其质量直接决定了对齐的天花板。 数据集的封闭性：论文中提到，所有数据来源于一个“proprietary 900K+ instance corpus”（专有的90万+实例语料库）。这使得外部研究者难以复现其结果，也无法评估其数据集本身对实验结果可能产生的影响。 “跷跷板效应”的解决方案仍是权宜之计：论文提出的“分阶段训练”方法，被其自己描述为“a pragmatic mitigation strategy, without claiming it as a definitive solution”（一个务实的缓解策略，而非最终解决方案）。这表明其并未从根本上解决不同类型能力训练之间的内在冲突，只是一个有效的工程折衷。 奖励模型（Critic Models）的细节缺失：论文提到使用“critic models”来根据量规打分，但并未详细说明这些评价模型是如何训练的，以及如何保证这些模型本身能够准确理解和执行量规。如果评价模型本身存在偏差，这种偏差就会被传递给正在训练的主模型。 6. 核心启发与需补充的背景知识 重点学习与启发： 思维转变：从“数据为王”到“标准先行”：这篇论文最大的启发是“量规优先”的工作流。在启动一个AI项目，尤其是涉及主观评价的项目时，可以先花大量时间定义清楚什么是“好”，构建一套细致的、可操作的评价标准（Rubrics），然后再去指导数据生产和模型训练。 拥抱多维度反馈：在进行模型评估和优化时，应尽量避免使用单一、笼统的指标。建立一个多维度的评估体系，可以更精确地定位模型的优缺点，指导后续的迭代方向。 主动防御性设计：论文中“自适应地防御奖励攻击”（Adaptive Defense Against Reward Hacking）的思想非常实用。即通过分析模型的“作弊”行为，反过来设计新的规则（Rubric）来封堵这些漏洞。这是一种将博弈论思想融入模型训练的体现。 需要补充的背景知识： 强化学习基础：特别是PPO（Proximal Policy Optimization）算法，这是当前LLM进行RL训练时最主流的算法。 RLHF与RLAIF：了解基于人类反馈的强化学习（RLHF）的完整流程（从SFT -\u0026gt; RM -\u0026gt; RL），以及基于AI反馈的强化学习（RLAIF）是如何用AI替代人类来提供偏好信号的。这将帮助您更好地理解本文是在这个技术脉络上的演进。 Constitutional AI：了解Anthropic公司提出的“宪法AI”概念。它也是一种用规则（“宪法”）来指导模型对齐的方法，与本文的“量规”思想有异曲同工之妙，但侧重点和实现方式有所不同。 评估基准（Benchmarks）：了解一些主流的LLM评估基准，特别是像MT-Bench, AlpacaEval这样依赖于更强模型（如GPT-4）作为裁判的评估方式，这有助于理解当前评估开放式任务的主流范式。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文。如果您还有任何其他问题，随时可以提出。\n","date":"2025-08-22T00:00:00Z","permalink":"/reinforcement-learning-with-rubric-anchors/","title":"Reinforcement Learning with Rubric Anchors"},{"content":"解决熵坍塌，自我博弈策略。\nimage.png 这篇论文的核心思想极其精妙，它试图解决当前大模型在进行强化学习训练时遇到的一个根本性瓶颈：“竭泽而渔”式的学习。传统的强化学习方法，尤其是基于可验证奖励的强化学习（Reinforcement Learning with Verifiable Rewards, RLVR），在提升模型解决特定问题的“准确率”（即 Pass@1）方面效果显著，但这往往以牺牲模型的“思维多样性”为代价。您可以将其想象成一个学生，为了应付考试，只反复刷同一套题库。他可能会对这些题型了如指掌，考试能得高分（高Pass@1），但一旦遇到稍微新颖或需要发散思维的题目，他就束手无策了，因为他的解题思路已经被固化，探索更优解法的潜力（即 Pass@k）受到了限制。\n论文将这种“思维多样性”的丧失，用一个信息论中的概念——策略熵（Policy Entropy） 来量化。策略熵衡量的是模型生成答案的随机性或多样性。当模型反复在固定的、有限的数据集上进行RLVR训练时，它会倾向于收敛到少数几个“高分答案”上，导致策略熵持续下降，这被称为“熵坍塌”（Entropy Collapse）。熵的降低意味着模型失去了探索精神，无法发现更高级或更多样化的推理路径，最终其性能提升也会很快达到瓶颈。\n正如论文在第二节开头所指出的： \u0026gt; Recent study (Cui et al., 2025) demonstrates a trade-off between policy entropy and model performance, where gains in test accuracy come at the expense of response diversity…This suggests that RLVR training narrows the reasoning trajectory toward most reward-prone solutions, reducing exploration capacity without fostering more general or advanced reasoning beyond that of base models.\n面对这一困境，论文的作者们没有选择简单地扩大数据集（这既昂贵又低效），而是提出了一个极具创造性的解决方案：让模型自己为自己出题。这就是论文的核心贡献——带有多样化问题合成的自博弈策略（Self-play with Variational problem Synthesis, SVS）。\nSVS策略的运作机制可以概括为一个优雅的自循环、自提升的流程：\n识别前沿问题：首先，模型会在现有的训练集上解决问题。SVS会筛选出那些模型“半懂不懂”的问题，即模型的解题正确率（论文中称为Group Accuracy）不高也不低，恰好处于其能力边界上的问题（例如，正确率在12.5%到50%之间）。这就像一位好老师，总能找到学生“跳一跳就能够得着”的题目来练习。 逆向工程：从答案到问题：对于上述问题，模型生成的正确解答本身包含了通向答案的完整逻辑链和原始问题的所有信息。SVS利用这一点，将这些正确的解答作为“上下文”，提示模型去生成新的、表述方式和结构都不同，但答案与原问题完全相同的新问题。这是一种“逆向思维”，模型不仅要学会解题，还要学会出题。 难度控制与奖励塑造：为了防止模型“作弊”（例如，生成一个包含答案提示的过于简单的问题），SVS设计了一套精巧的奖励塑造（Reward Shaping）机制。一个新生成的问题只有在当前模型看来具有“中等难度”（即解题正确率不高不低）时，才会被认为是高质量的，并给予正面奖励。太简单或太难的问题都会被抑制。 联合训练：最后，模型会在一个动态更新的“经验池”中进行训练，这个池子里包含了三种任务的数据：(1) 解决原始问题；(2) 从正确解答生成新问题；(3) 解决自己生成的新问题。通过这种方式，模型同时锻炼了问题求解和问题生成两种能力，形成了一个强大的自强循环。 论文的实验结果极其有力地支撑了SVS的有效性。在最具挑战性的AIME数学竞赛基准测试上，传统RLVR训练在约450步后，模型的Pass@32性能就基本停滞了。而采用SVS策略的模型，其性能则能持续、稳定地增长。数据显示，相比于标准RLVR，SVS在AIME24和AIME25这两个数据集上的Pass@32性能分别取得了18.3%和22.8%的绝对提升。这是一个惊人的进步，因为它意味着SVS真正拓宽了模型的推理边界（Reasoning Boundary），使其具备了解决更复杂问题的潜力。\nimage.png 同时，论文通过熵监控图（Figure 5）清晰地展示了SVS能够有效维持策略熵在一个稳定水平，避免了标准RLVR训练中熵持续下降的问题。这为SVS能够持续提升性能提供了根本性的解释：通过不断创造新的学习挑战，模型得以保持探索的活力。\n总而言之，这篇论文不仅仅是提出了一种新的训练技巧，更是为解决大模型强化学习中的“数据瓶颈”和“探索-利用困境”提供了一个全新的、可持续的范式。它将训练过程从对静态数据的被动学习，转变为模型主动参与的、动态的、自我驱动的认知发展过程，这对于未来构建更强大、更通用的AI系统具有深远的启发意义。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决在利用基于可验证奖励的强化学习（RLVR）对大语言模型进行后训练（Post-training）时普遍存在的“熵-性能权衡”（Entropy-Performance Trade-off）问题。\n实际问题：\n性能瓶颈：标准的RLVR虽然能提升模型在已有问题上的单次解答准确率（Pass@1），但由于训练数据有限，模型会很快“背会”答案，导致思维固化，无法在更广泛的推理任务中持续进步，尤其是在衡量模型推理能力上限的Pass@k（尝试k次能解决问题的概率）指标上提升乏力甚至下降。 训练不可持续：随着训练进行，模型的输出多样性（策略熵）会急剧下降，即“熵坍塌”。这表明模型失去了探索新解题路径的能力，训练过程变得低效且不可持续，无法将模型的潜力完全激发出来。 数据依赖：高质量、多样化的训练数据（尤其是带有精确答案的复杂推理问题）是稀缺且昂贵的。传统的RLVR方法高度依赖于大规模的静态数据集，这构成了模型能力扩展的主要障碍。 行业意义：\n突破AI推理天花板：当前，提升大模型的复杂推理能力是通向更高级别人工智能（如AGI）的关键。这个问题如果得到解决，意味着我们可以用更经济、更高效的方式，让模型的推理能力上限（Pass@k）得到实质性提升，而不仅仅是优化现有能力（Pass@1）。 实现AI的自我进化：SVS提出的“自博弈”和“自我出题”范式，是AI自我提升能力的一大步。它展示了一条让模型摆脱对人类标注数据强依赖的路径，通过自我生成、自我挑战、自我学习的闭环，实现能力的持续、自主进化。这对于降低AI训练成本、加速技术迭代具有革命性的意义。 赋能垂直领域应用：在科学发现、药物研发、辅助编程、教育辅导等需要深度推理的专业领域，一个能够自我生成问题并持续提升能力的AI，可以成为更强大的研究助手或个性化导师，其应用价值远超一个只能解决固定问题的模型。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心方法：论文提出了一个名为带有多样化问题合成的自博弈（Self-play with Variational problem Synthesis, SVS）的在线问题增强框架。\n新思路与特点：\n从“解题者”到“出题者”的转变：SVS的核心创新在于，它不仅仅把模型当作一个“学生”（问题解决者），还赋予了它“老师”（问题创造者）的角色。模型需要学会从一个已知的正确解法“逆向工程”出一个新的问题。 在线与动态的数据增强：与离线使用外部模型生成“复述”问题（Rephrasing）不同，SVS是在训练过程中在线、动态地生成新问题。这使得新生成的数据总是与模型当前的能力水平（frontier capabilities）相匹配。 纯粹的自提升（Self-Improvement）：整个SVS框架完全依赖于策略模型自身，无需任何外部模型（如更强的教师模型）的指导或蒸馏，也无需额外的人工标注。这是一种闭环的、端到端的自提升范式。 与之前方法的比较优势：\n相比于标准RLVR： 维持熵，避免瓶颈：标准RLVR在静态数据集上训练，导致熵坍塌和性能停滞。SVS通过源源不断地生成新问题，有效维持了策略熵，使得训练可以持续更长时间并达到更高的高度。 提升Pass@k：标准RLVR主要提升Pass@1，而对Pass@k效果有限。SVS通过鼓励探索多样化的推理路径，显著提升了Pass@k，真正拓展了模型的能力边界。 相比于基于复述的数据增强（Rephrasing-based Augmentation）： 保证答案准确性：外部模型复述问题时可能引入语义偏差，导致新问题的答案与原答案不符，干扰训练。SVS从一个已知的正确答案出发生成问题，天然地保证了合成问题的“标准答案”是准确无误的，为RLVR提供了最关键的可靠训练信号。 保证问题多样性与对齐性：复述往往局限于对原问题的同义词替换和句式改写，多样性有限。SVS可以生成结构上截然不同的问题。更重要的是，SVS生成的问题与模型自身的能力高度对齐，避免了外部模型可能带来的能力不匹配问题。 论文中明确指出了理想数据增强策略的几个标准，而SVS正是为了满足这些标准而设计的： \u0026gt; …ideal data augmentation for RLVR should be iterative, provide precise reference answers, and be aligned with the policy’s capabilities.\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了全面且严谨的实验来验证SVS的有效性。\n实验设计： 模型：涵盖了从3B到32B参数量的多种主流开源模型，如Qwen2.5-3B-Instruct, LLaMA-3.1-8B-Instruct, 和 Qwen2.5-32B-Instruct，以验证方法的通用性。 数据集：训练集使用了数学领域的标准数据集MATH-12k和更具挑战性的DAPO-17k。 评估基准：评估涵盖了从基础到竞赛级别的12个广泛使用的推理基准，包括GSM8K, MATH-500, AIME, OlymMATH等，进行了非常全面的评估。 对比基线：实验的核心对比对象是标准RLVR（使用GRPO算法）和SVS（在GRPO基础上集成SVS策略）。同时，也与模型的初始状态（Init Model）进行了比较。 评估指标：主要关注Pass@1（单次成功率）和Pass@k（多次尝试成功率，尤其是Pass@32），以及训练过程中的策略熵变化。 实验数据和结果： 核心性能提升：Table 1的数据是论文最亮眼的结果。以在DAPO-17k上训练的32B模型为例： 在AIME24基准上，SVS的Pass@32得分比标准RLVR高出18.3%（70.8 vs 52.5）。 在AIME25基准上，SVS的Pass@32得分更是高出22.8%（65.2 vs 42.4）。 在Pass@1指标上，SVS同样全面领先，平均提升了5.4%。 熵的维持：Figure 5的图表直观地显示，在所有模型和数据集的实验中，标准RLVR的策略熵都呈现出明显的下降趋势，而SVS则能将其维持在一个相对稳定的水平。这直接证明了SVS解决了“熵坍塌”问题。 推理边界的拓展：Figure 6展示了Pass@k随k值增大时的变化。在MATH-500这类基准上，标准RLVR的性能在k值较大时甚至会被初始模型反超，说明其推理路径非常狭窄。而SVS在所有k值上都稳定地优于基线，表明它确实学会了更多样、更鲁棒的推理方法。 通用性验证：Table 2的结果表明，无论是在3B, 8B还是32B的模型上，也无论是在MATH-12k还是DAPO-17k数据集上，SVS相比标准RLVR都带来了一致的性能提升（平均提升约2.5%-2.9%），证明了其广泛的适用性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文开辟了一个激动人心的方向，也留下了一些值得深入探索的问题：\n挑战与问题： 计算成本：SVS的自博弈循环（生成解、生成问题、解决新问题）无疑会增加训练的计算开销。如何优化这个流程，例如，通过更高效的采样策略或异步执行来降低成本，是一个关键的工程挑战。 泛化到更开放的领域：SVS在有明确“对/错”答案的数学推理任务中表现出色。但如何将其泛化到代码生成、科学文献综述、创意写作等更开放、答案不唯一的领域？在这些领域，如何定义“正确解”以及如何评估生成问题的质量，将是核心难题。 避免模式坍塌和偏见放大：模型从自己的成功经验中学习并生成新问题，这可能存在一个潜在风险：它可能会陷入一个“舒适区”，不断生成自己擅长解决的某类问题，而回避那些它不擅长的领域，从而导致知识面窄化或偏见被放大。如何引入更多随机性或外部信号来打破这种潜在的自我固化循环，值得研究。 更智能的课程学习：目前SVS通过固定的正确率区间来筛选问题。未来是否可以设计更动态、更智能的课程学习（Curriculum Learning）策略？让模型能自主判断在哪个阶段应该专注于哪种难度或类型的问题生成，从而实现最高效的学习路径。 催生的新技术和投资机会： 自动化数据工厂：SVS是实现高质量训练数据“自给自足”的雏形。未来，这项技术可能发展成为“自动化数据工厂”，为特定行业（如法律、金融、医疗）大规模、低成本地生成高质量的定制化训练数据，这将是一个巨大的商业机会。 下一代AI教育和培训平台：一个能根据学习者水平动态生成练习题的AI系统，是实现真正的个性化教育的关键。SVS的技术可以直接应用于开发AI导师（AI Tutor），为学生、程序员、医生等提供量身定制的培训。 AI驱动的科学发现：在科研领域，提出有价值的问题和假设，有时比解决问题更重要。SVS的“问题生成”能力可以被视为一种初级的“假设提出”能力。未来，更强大的类似系统可能成为科学家的得力助手，帮助他们在海量数据中发现新的研究方向。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的角度审视，仍有一些方面有待完善：\n计算开销分析的缺失：论文详细报告了性能提升，但对SVS策略带来的额外计算成本（如GPU小时数、训练时间增加的百分比）着墨不多。对于希望在实际生产中应用该方法的团队来说，这是一个非常关键的决策信息。 超参数敏感性分析：SVS引入了几个关键的超参数，例如用于筛选“前沿问题”的正确率区间 [acc_l, acc_h] 和用于奖励塑造的区间 [acc_l_hat, acc_h_hat]。这些区间的设置对最终效果有多大影响？论文给出了使用的具体数值，但缺少对这些超参数的敏感性分析或消融研究，这使得复现和推广到其他场景时可能需要大量的调试。 对“合成失败”案例的分析不足：SVS在生成问题时可能会失败，比如生成了语义不通、过于简单或无法回答的问题。论文提到了过滤和奖励机制，但对这些“坏”案例的类型、比例以及它们对训练过程的潜在影响缺乏深入的定性或定量分析。 对泛化能力的验证仍可加强：虽然论文在Table 3中展示了SVS在通用问答和代码任务上能避免性能下降，甚至略有提升，但这些任务与核心的数学推理任务相比，跨度仍然有限。如果能在更远的领域，如创意写作或长篇文本理解上进行测试，将能更全面地评估其泛化能力，并揭示其潜在的局限性。 对模型规模的依赖性：实验主要集中在3B到32B的模型上。SVS这种需要模型具备一定“自我反思”和“创造”能力的方法，是否对模型的基础能力（即规模）有最低要求？在更小的模型（如1B以下）上是否依然有效，或者说是否存在一个“智能涌现”的门槛，这些都有待进一步验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是创新思想的宝库，您可以从中汲取以下关键启发：\n核心启发： 动态数据观：最大的启发是，不要将训练数据视为静态的、一次性的资源。SVS告诉我们，数据可以和模型一起“成长”。在您的项目中，可以思考如何建立一个数据与模型交互的动态循环，让模型根据自己的学习状态来影响后续的训练数据。 学习“逆向问题”：在很多任务中，我们只训练模型从A到B（例如，从问题到答案）。SVS的精髓在于，它还训练模型从B到A（从答案到问题）。这种“逆向思维”的训练是一种强大的元学习，能极大加深模型对任务本质的理解。您可以思考在您的领域，什么是“逆向问题”，并尝试构建类似的训练任务。 聚焦“学习者”的“最近发展区”：SVS通过正确率筛选，精确地将学习资源投入到模型最需要的地方（半懂不懂的问题）。这比在所有数据上平均用力要高效得多。在您的训练流程中，可以引入类似的主动学习或难例挖掘机制，让模型“告诉”你它需要学习什么。 自博弈作为通用范式：从AlphaGo到这篇论文，自博弈（Self-play）再次证明了它是驱动AI能力超越人类的强大引擎。无论您的任务是什么，都可以思考是否能构建一个“自己”的副本或变体作为对手或伙伴，通过相互博弈来共同进步。 补充的背景知识： 强化学习基础：尤其是策略优化算法，如PPO (Proximal Policy Optimization)。理解GRPO（Group Relative Policy Optimization）会更有帮助，因为它是本文RLVR的基础。 RLHF/RLAIF：了解从人类/AI反馈中进行强化学习（RLHF/RLAIF）的整体框架，RLVR是其中的一个特例，其特殊之处在于奖励信号是二进制且可自动验证的。 大模型评估指标：深入理解Pass@k的计算方式和它与Pass@1的区别，明白为什么它更能代表模型的推理潜力。 课程学习（Curriculum Learning）：了解这个机器学习分支的基本思想，即从易到难地给模型安排学习任务，这有助于您更好地理解SVS中“难度筛选”的动机。 ","date":"2025-08-21T00:00:00Z","permalink":"/beyond-pass@1self-play-with-variational-problem-synthesis-sustains-rlvr/","title":"BEYOND PASS@1：SELF-PLAY WITH VARIATIONAL PROBLEM SYNTHESIS SUSTAINS RLVR"},{"content":"好的，非常荣幸能以领域专家的身份，为您深入解读这篇富有洞见的论文《Beyond the 80/20 Rule: High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning》。\n这篇论文的核心思想，用一句话概括，就是揭示了在大型语言模型（LLM）的推理过程中，并非所有生成的词元（token）都同等重要；一小部分“高熵”词元扮演了决定推理路径的关键“岔路口”角色，而强化学习的有效性，正源于对这些少数关键词元的精准优化。 作者通过引入“词元熵”这一新颖视角，不仅深刻洞察了带有可验证奖励的强化学习（RLVR, Reinforcement Learning with Verifiable Rewards）的工作机制，还提出了一种远超传统“二八定律”的、更高效的训练策略。\n首先，让我们来理解一个核心概念：词元熵 (Token Entropy)。在LLM生成每一个词元时，它都会对词汇表中的所有词元计算一个概率分布。如果模型对下一个词元非常确定（例如，在“…is the capital of France”之后，模型会以极高概率生成“Paris”），那么这个概率分布就非常尖锐，其“熵”就非常低。相反，如果模型面临一个决策点，有多个合理的续写选项（例如，在解数学题时，下一步是“假设…”、“因此…”还是“让我们先计算…”），概率分布就会比较平坦，其“熵”就很高。这篇论文将这些高熵词元形象地称为“分岔词元 (forking tokens)”，因为它们是引导模型走向不同推理路径的关键节点。\n论文的论证过程层层递进，极具说服力。\n观察与发现：作者首先对LLM在执行思维链（Chain-of-Thought, CoT）推理时生成的词元进行了大规模统计分析。如图2所示，他们发现词元熵呈现出一种高度偏斜的分布： \u0026gt; Entropy Pattern 1 in CoTs: Typically, only a minority of tokens are generated with high entropy, while a majority of tokens are outputted with low entropy. … only 20% of tokens have entropy greater than 0.672.\n绝大多数（超过80%）的词元熵值都非常低，它们通常是用于完成语法结构、填充常见短语的“填充词元”。而少数高熵的“分岔词元”则多为逻辑连接词（如“Thus”, “However”, “Suppose”），是真正的决策点。\n图注：论文中的图2，(a)展示了词元熵的高度偏斜分布，大部分词元熵很低；(b)和(c)的词云直观对比了高熵词元（逻辑决策）与低熵词元（语法/符号填充）的区别。\n验证与假设：为了验证“分岔词元”的重要性，作者做了一个巧妙的实验（如图3所示）：在生成时，人为地提高或降低不同词元的解码温度（temperature，一种控制生成随机性的参数，与熵正相关）。结果发现，仅提高“分岔词元”的温度能显著提升模型的推理性能，而提高“填充词元”的温度反而会导致性能下降。这有力地证明了“分岔词元”确实是推理探索的关键，需要更大的探索空间（即更高的熵）。\n机制洞察：接着，论文探究了RLVR训练过程本身。通过分析训练过程中模型熵的变化，作者发现RLVR并不会大规模地改变模型原有的熵模式，而是“顺势而为”： \u0026gt; RLVR primarily preserves the existing entropy patterns of the base models… RLVR predominantly alters the entropy of high-entropy tokens, whereas the entropy of low-entropy tokens remains comparatively stable…\n这意味着，强化学习主要是在调整那些本来就具有高不确定性的“分岔词元”的生成策略，使其能做出更优的决策，而对那些低熵的、确定性的“填充词元”影响甚微。\n提出新方法并取得惊人成果：基于以上洞察，作者提出了一个颠覆性的训练方法：在进行策略梯度更新时，只保留熵最高的20%的词元，而忽略掉其余80%的低熵词元。这不仅意味着可以节省大量的计算资源，更令人惊讶的是，其实验结果（如表2所示）表明，这种“少即是多”的方法性能非但没有下降，反而显著超越了在所有词元上进行训练的传统方法（vanilla DAPO）。\nOn the Qwen3-32B base model, this approach delivers gains of 7.71 points on AIME′24 and 11.04 points on AIME′25.\n在Qwen3-32B这样的大模型上，新方法在AIME’25数学竞赛基准测试上取得了超过11个百分点的惊人提升。更重要的是，这种性能增益呈现出强大的规模效应（Scaling Law），即模型规模越大，这种方法的优势越明显（如图8所示）。这无疑为未来更大规模模型的训练指明了一条极具潜力的道路。\n图注：论文中的图8，展示了新方法（仅使用分岔词元）相比传统方法（DAPO）的性能增益随着模型规模（8B, 14B, 32B）的增大而显著提升。\n综上所述，这篇论文的创新性贡献在于，它首次系统地从“词元熵”的视角剖析了LLM推理和RLVR训练的内在机制，识别出“高熵少数词元”的核心驱动作用，并基于此提出了一种计算上更高效、性能上更卓越的训练范式。它将学术界对LLM推理的理解从宏观的“思维链”层面，深化到了微观的“词元决策”层面，为大模型的优化和效率提升开辟了全新的思路。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是深入理解并优化大型语言模型在执行复杂推理任务时的强化学习（RLVR）过程。它试图回答一个根本性问题：RLVR是如何提升LLM推理能力的？模型在学习过程中，哪些部分是关键，哪些是冗余？\n解决的实际问题：\n训练效率低下：传统的RLVR方法对模型生成的所有词元都进行策略梯度更新，这不仅计算成本高昂，而且可能引入噪声，因为大量的词元只是在完成语言的“陈规俗套”，对提升逻辑推理能力贡献甚微。 机制不透明（黑盒问题）：虽然RLVR在实践中效果显著，但其工作机制尚不明确。研究人员不清楚模型是通过学习新的推理结构，还是在优化关键的决策点。这种“知其然不知其所以然”的状态阻碍了更高级、更可控的算法设计。 对行业发展的重要意义：\n降本增效：在当前大模型“军备竞赛”的背景下，训练成本是制约技术发展和应用普及的关键瓶颈。该论文提出的方法，通过只在20%的关键数据上训练就能取得更好效果，为大幅降低模型训练成本提供了切实可行的路径。 提升模型能力上限：研究结果表明，专注于“分岔词元”的训练能够更好地激发大模型的潜力，尤其是在更大规模的模型上。这意味着行业可以沿着这条路，用更少的资源训练出更强大的推理模型。 算法创新的新范式：该研究将“数据选择”的思想引入到RL训练的微观层面，启发行业从“训练所有数据”的思维定势中跳出。未来，更多基于内容、结构或不确定性的动态数据选择策略可能涌现，推动RLHF（以及更广泛的AI对齐技术）进入一个更精细、更高效的时代。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路： 词元熵视角 (Token Entropy Perspective)：这是本文最核心的创新思路。它不再将LLM的输出视为一个同质化的词元序列，而是引入“熵”作为度量，区分出具有不同功能角色的词元：低熵的“填充词元”和高熵的“分岔词元”。这个视角为理解和分析LLM的生成过程提供了一个全新的、定量的工具。 新的方法： 基于高熵词元的选择性策略更新 (Selective Policy Gradient Update on High-Entropy Tokens)：基于上述思路，作者提出了一个简单而有效的方法。在RLVR训练的每一步中，首先计算当前批次（batch）中所有生成词元的熵，然后设定一个阈值（例如，选择熵最高的20%），只对熵值超过这个阈值的“分岔词元”计算并反向传播策略梯度。其数学表达如公式(6)所示，通过一个指示函数 I[H \u0026gt;= τ] 来实现对梯度的“过滤”。 与之前方法的比较、特点和优势： 传统RLVR方法：如DAPO、PPO等，它们平等地对待序列中的每一个词元，对所有词元都进行梯度更新。这可以看作是“广撒网”策略。 本文方法的特点： 靶向性 (Targeted)：该方法如同“精准打击”，将优化资源集中在被识别为对推理最重要的“分岔词元”上。 高效性 (Efficient)：由于只更新一小部分词元的梯度，理论上可以大幅减少反向传播的计算量，缩短训练时间。 解释性 (Interpretable)：它为RLVR的成功提供了一个清晰的解释——即通过优化关键决策点来提升推理能力。 本文方法的优势： 性能更优：实验证明，该方法不仅没有因为忽略80%的词元而性能受损，反而超越了传统方法，尤其是在大模型上。这可能是因为它减少了来自低熵词元的“梯度噪声”，让模型更专注于学习核心的逻辑跳转。 更好的规模效应 (Better Scaling)：如图8所示，模型越大，该方法的性能优势越明显。这对于追求SOTA（State-of-the-Art）性能的前沿研究机构极具吸引力。 促进探索 (Enhances Exploration)：论文指出，专注于高熵词元有助于在训练中保持更高的整体熵（如图7所示），这意味着模型能进行更有效的探索，从而找到更优的推理路径。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列环环相扣的实验来系统地验证其假设和方法的有效性。\n实验设计与数据： 模型：主要使用了Qwen3系列的多个尺寸模型（8B, 14B, 32B），以验证方法在不同规模模型上的表现。同时也在Llama-3.1-8B上做了尝试。 训练数据：使用了DAPO-Math-17K数据集，这是一个专注于数学推理的数据集。 评估基准：在6个标准的数学推理基准上进行评估，包括AIME’24, AIME’25, AMC’23, MATH500, Minerva, 和 OlympiadBench。评估指标为Acc@16（生成16个答案的平均准确率）。 基线方法：以SOTA的RLVR算法DAPO作为基线，该算法在所有词元上进行训练。 核心实验与结果： 主实验：与基线方法对比： 设置：对比“DAPO w/ Forking Tokens”（只训练top 20%高熵词元）和“vanilla DAPO”（训练全部词元）。 结果：如表2所示，新方法在所有模型尺寸上都取得了性能提升。引用关键数据： \u0026gt; For the Qwen3-32B model, the AIME’24 score increased from 55.83 to 63.54 (+7.71), and the AIME’25 score increased from 45.63 to 56.67 (+11.04). \u0026gt; For the Qwen3-14B model, the AIME’24 score increased from 45.21 to 50.42 (+5.21). 这些数据清晰地表明，选择性更新策略远优于全量更新。 消融研究：不同比例词元的影响： 设置：在Qwen3-8B模型上，分别测试了只使用熵最高10%、20%、50%的词元，以及只使用熵最低80%的词元进行训练。 结果：如图7所示，Top 10%和Top 20%的性能表现最佳且相近；Top 50%性能稍有下降；而Bottom 80%（只使用低熵词元）的性能则急剧下降，几乎无法学习。这强有力地证明了推理能力的提升确实来源于高熵词元，而低熵词元贡献甚微，甚至可能是有害的。 泛化能力测试： 设置：将在数学数据集上训练的模型，在一个完全不同的领域——代码生成（LiveCodeBench benchmark）上进行测试。 结果：如图9所示，即使在分布外（out-of-distribution）的数据集上，只使用Top 10%或Top 20%高熵词元训练的模型，其性能依然显著优于使用全部词元训练的模型。这表明高熵词元可能与模型更底层的、可泛化的推理能力相关联。 这些实验从“是什么”（观察熵分布）、“为什么”（验证分岔词元重要性）到“怎么用”（提出新方法并验证），再到“效果如何”（主实验、消融实验和泛化实验），形成了一个完整的逻辑闭环，论证非常扎实。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 该研究为未来的探索打开了多扇大门。\n值得进一步探索的问题和挑战： 动态与自适应阈值：论文中使用了固定的20%阈值。未来的研究可以探索动态阈值，例如，让模型根据任务难度或训练阶段自动学习最佳的词元选择比例。甚至可以设计一个元学习器（meta-learner）来决定哪些词元应该被更新。 跨领域和跨模态的适用性：当前研究主要集中在数学推理。这种“高熵词元驱动学习”的范式是否适用于代码生成、长文本创作、科学发现、多模态（文生图/视频）等其他领域？在这些领域中，“分岔词元”的具体表现形式是什么？这是一个巨大的待探索空间。 与SFT（监督微调）的结合：论文讨论了RL与SFT的区别。一个有趣的方向是，在SFT阶段是否也可以应用类似的思想？例如，对数据集中的“分岔词元”赋予更高的损失权重，从而让模型在微调阶段就更关注决策点的学习。 推理时（Inference-time）的应用：除了用于训练，词元熵信号是否也能在推理时发挥作用？例如，当模型检测到一个高熵“分岔词元”时，可以触发更复杂的解码策略，如多路径搜索（multi-path search）或自我反思（self-reflection），而在低熵区域则使用快速的贪心解码，从而在保证质量的同时提升推理速度。 可能催生的新技术和投资机会： 新一代AI训练/对齐平台：可以开发集成“熵感知”或其他选择性更新机制的AI训练框架。这类平台将以“更智能、更高效”为卖点，吸引需要频繁微调和对齐模型的企业客户。这是一个技术服务（MLaaS）领域的投资机会。 专用计算硬件/软件：选择性梯度更新对计算模式提出了新要求。这可能催生出专门用于高效处理稀疏/动态梯度计算的AI芯片或软件库，从而在硬件层面实现极致的能效比。 模型可解释性与安全工具：“分岔词元”分析提供了一种新的模型行为诊断工具。可以开发用于可视化模型决策路径、识别潜在逻辑漏洞、或检测模型“胡说八道”（hallucination）的软件产品。这在模型安全、对齐和调试领域具有商业价值。 自动化AI研究代理（AI for AI）：将熵分析等技术自动化，构建能够自我分析、自我诊断、并自我优化训练策略的AI代理。这类“AI教练”可以帮助人类研究员更快地迭代和改进模型，是AI领域的前沿投资方向。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些局限和待验证之处。\n模型和家族的单一性： \u0026gt; Our experiments could be extended to models beyond the Qwen family. Although we attempted to evaluate our approach on LLaMA models, they struggled to achieve meaningful performance on the AIME benchmarks.\n论文的主要成功案例建立在Qwen模型家族之上。虽然作者尝试了Llama模型，但结果并不理想，且需要额外的“冷启动”SFT，这使得结论的普适性打了一定的折扣。该方法是否对所有主流的模型架构（如Llama, Mistral, Gemini等）都同样有效，尚需更多证据。\n任务领域的局限性：实验高度集中于数学推理。数学推理的思维链结构清晰，逻辑性强，“分岔”点相对明确。但在更开放、更模糊的领域，如诗歌创作、法律文书撰写或日常对话中，熵的分布模式和“分岔词元”的作用可能完全不同。将结论直接推广到所有任务领域是需要非常谨慎的。\n“20%”这个数字的任意性：论文将20%作为一个有效的比例，但这个数字是如何得来的？它是否是针对当前模型、数据集和任务最优的“超参数”？在不同的设置下，最优比例可能是5%或40%。论文对此缺乏深入的讨论，使得这个“20%”看起来更像一个经验值而非理论最优解。\n相关性 vs. 因果性：论文建立了“高熵”与“推理关键点”之间的强相关性，并基于此取得了成功。但两者之间的因果关系仍有待深究。是否存在一个更深层的变量（例如，概念抽象的层级），同时导致了高熵和推理的重要性？将“熵”作为唯一的标准可能简化了问题。例如，某些低熵但罕见的专有名词，可能对特定问题的推理也至关重要。\n对“填充词元”作用的忽视：论文的结论是低熵词元贡献甚微甚至有害。但这可能低估了它们在维持语言流畅性、可读性和思维连贯性上的作用。完全忽略它们进行训练，是否会在长期或在某些特定任务上损害模型的综合能力？这需要更长期的、更多样化的评估来验证。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文是创新思想的宝库。以下是您可以重点学习和借鉴之处：\n重点学习的创新想法： “不均等”思维：核心启发是打破“所有数据/特征/词元都平等”的惯性思维。无论是在数据清洗、模型训练还是推理优化中，都可以思考：哪些是“少数关键”，哪些是“多数冗余”？如何识别前者并集中资源？ 不确定性作为信号：模型的不确定性（以熵为代表）本身就是一种宝贵的信息。它可以被用作训练过程中的“指挥棒”，指导模型在哪里应该“精耕细作”（高不确定性区域），在哪里可以“一带而过”（低不确定性区域）。这个想法可以应用到主动学习、数据增强等多个领域。 简单但深刻的修改：本文的成功之处在于，它没有设计一个极其复杂的全新算法，而是在现有SOTA算法上做了一个微小但基于深刻洞察的修改。这告诉我们，真正的创新有时来源于对问题本质的理解，而不是算法复杂度的堆砌。 给您的启发： 在您的工作中：如果您正在进行模型微调或数据处理，可以尝试分析数据的“熵”或“不确定性”。例如，在对用户反馈数据进行微调时，是否可以给那些让模型“困惑”的样本更高的权重？在处理长文档时，是否可以识别出信息密度最高、转折最关键的“分岔句”并重点处理？ 跨领域应用：这个思想可以跨界。比如在金融风控中，识别交易数据中的“高熵”异常模式；在A/B测试中，重点分析那些导致用户行为出现“分岔”的关键决策点。 需要补充的背景知识： 信息论基础：强烈建议您深入理解熵（Entropy）、交叉熵（Cross-Entropy）、KL散度（KL Divergence）这些基本概念。它们是衡量信息量和分布差异的基石，也是理解本文的关键。 强化学习基础：了解策略梯度（Policy Gradient）算法的基本思想，特别是PPO（Proximal Policy Optimization）。您不需要成为RL专家，但需要明白策略网络是如何通过奖励信号和梯度来更新的。 大语言模型推理技术：熟悉思维链（Chain-of-Thought, CoT）的概念，以及解码策略（如Greedy Search, Beam Search, Temperature Sampling）的工作原理。这将帮助您更好地理解词元熵在实际生成过程中的意义。 希望这份详尽的解读能帮助您全面、深入地掌握这篇优秀论文的精髓，并从中获得有价值的启发。\n","date":"2025-08-21T00:00:00Z","permalink":"/beyond-the-80-20-rulehigh-entropy-minority-tokens-drive-effective-reinforcement-learning-for-llm-reasoning/","title":"Beyond the 80 20 Rule：High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning"},{"content":"DAG编排动作序列，有点类似TURA：Tool-Augmented Unified Retrieval Agent for AI Search 和ParallelSearch：Train your LLMs to Decompose Query and Search Sub-queries in Parallel with Reinforcement Learning编排工具使用序列。\nimage.png 论文深度解读 这篇论文的核心贡献在于，它巧妙地将显式的因果推理（Explicit Causal Reasoning）与大语言模型（LLM）的规划能力相结合，从而显著提升了多智能体系统在协作任务中的表现，尤其是针对那些计算资源相对有限、更容易出错的开源小型LLM。论文的作者们观察到一个关键现象：LLM，特别是小型模型，在作为智能体（Agent）进行决策时，往往依赖于其在海量数据中学到的表层相关性（surface-level correlations），而非对任务环境内在的因果关系（grounded causal reasoning）的深刻理解。这导致它们会生成“因果无效”的动作序列，比如在一个烹饪游戏中，智能体可能会在拿起洋葱之前就尝试“把洋葱放进锅里”，这在现实世界中是荒谬且不可执行的。\n为了解决这一根本性问题，作者提出了一个名为 CausalPlan 的创新框架。这个框架的设计思想非常精妙，它模仿了人类“先学习规则，再运用规则”的决策过程，分为两个核心阶段：\n第一阶段：因果行动结构学习（Causal Action Structure Learning）\n这个阶段是离线的，相当于智能体的“学习与内化”过程。CausalPlan通过观察一个行为策略（可以是预训练的智能体或人类数据）在环境中产生的大量交互轨迹（即一系列的状态、动作序列），来学习任务背后的因果规则。\n核心模型：结构化因果行动模型（Structural Causal Action, SCA） 这可以看作是经典结构化因果模型（SCM）的一个扩展。简单来说，SCM用一个有向无环图（DAG）来表示变量间的因果关系，例如“拿起洋葱”是“把洋葱放入锅中”的原因。这篇论文的创新之处在于，它将未来的动作作为因果图中的“子节点”，而将当前环境状态和过去执行的动作作为“父节点”。 \u0026gt; Unlike prior work, which typically focuses on modeling state transitions or rewards, our approach explicitly treats the action as a child node in the causal graph. This novel formulation allows the agent to reason causally about how past actions and current states influence future actions…\n学习成果：因果行动矩阵（Causal Action Matrix, M） 通过训练SCA模型，CausalPlan最终会生成一个矩阵M。您可以把它想象成一本“因果规则手册”。这个矩阵的每一行代表一个可能的未来动作，每一列代表一个当前状态或过去动作的特征（比如“我手里有洋葱”、“锅是空的”）。矩阵中的数值（causal score）则量化了在特定状态下，执行某个动作的因果合理性有多高。比如，当“手里有洋葱”和“锅是空的”这两个状态特征被激活时，“把洋葱放进锅里”这个动作对应的因果分数就会很高。\n第二阶段：带有因果知识的智能体规划（Agent Planning with Causal Knowledge）\n这个阶段是在线的，即智能体在实际执行任务时的决策过程。当LLM接收到当前的环境状态并被要求规划下一步行动时，CausalPlan会利用第一阶段学到的“因果规则手册”进行干预和优化。\n这个干预过程分为两种情况，体现了框架的鲁棒性：\n因果感知行动规划（Causal-Aware Action Planning）：当LLM根据自身知识提出了一系列候选动作时，CausalPlan会用因果行动矩阵M来为这些动作打一个“因果分”。然后，它会将LLM自身给出的动作概率和这个因果分数进行加权融合（通过公式 pf(am) = γ·pa(am) + (1 - γ)·pc(am) 实现），最终选择一个既符合语言模型常识、又满足因果逻辑的动作。这相当于一个专家在旁边对LLM的初步想法进行“校准”。\n因果备用行动（Causal Backup Action）：当LLM因为“幻觉”而未能提出任何一个有效的候选动作时（这在小型模型中很常见），系统不会陷入僵局。CausalPlan会启动一个备用计划：直接查询因果行动矩阵M，在当前状态下，选择那个拥有最高因果分数的动作来执行。这是一种非常强大的容错机制，确保了智能体在面对不确定性时，能够回退到最符合基本规则的安全选项。\n图注：CausalPlan框架概览。左侧为离线的因果学习阶段，右侧为在线的规划与决策阶段，清晰展示了两种决策路径。\n实验结果有力地证明了CausalPlan的有效性。在复杂的多智能体协作环境 Overcooked-AI 中，搭载了CausalPlan的各种开源LLM（如Gemma-7B, Llama-70B）不仅显著减少了无效动作的产生（如图1(a)所示），其协作任务的得分也获得了大幅提升。例如，Qwen-14B和Llama-70B的平均性能分别提升了29.04%和22.42%。更令人印象深刻的是，在与顶级的强化学习（RL）基线模型的对比中，配备了CausalPlan的Llama-70B智能体在五个测试场景中的三个场景里取得了最高分（见表1），在“非对称优势（AA）”布局中甚至领先次优基线63%。这充分说明，这种因果增强的方法甚至能让通用LLM智能体超越为特定任务优化的专用RL模型。\n总而言之，这篇论文不仅仅是提出了一种新算法，更是为如何构建更可靠、更高效、更具解释性的LLM智能体提供了一种全新的设计范式：即利用LLM强大的通用知识进行“创意发散”（提出候选方案），再用从数据中学习到的显式因果模型进行“逻辑收敛”（评估与决策）。这种模块化、无需微调LLM本身的设计，使其具有极高的实用价值和可扩展性。\n接下来，我将按照您提出的六个问题，对论文进行更详细的结构化解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是为基于大语言模型的智能体（LLM Agents）赋予因果推理能力，以提升它们在动态、协作式多智能体环境中的规划效率和协作表现。\n解决的实际问题：\nLLM的“因果盲点”：当前的LLM主要通过学习文本中的统计规律来进行预测和生成，这使它们缺乏对物理世界或任务流程中严格的因果次序的理解。这导致它们在需要精确规划的场景下，会频繁生成不合逻辑、无法执行的“因果无效动作”（Causally Invalid Actions）。 开源小模型的性能瓶颈：相比于GPT-4等巨型闭源模型，开源的小型LLM（如7B、13B级别）更容易出现上述问题。然而，这些模型因其低成本、高可控性和易于部署的特点，在学术界和工业界有着巨大的应用潜力。如何低成本地弥补它们的性能短板，是一个亟待解决的实际问题。 对行业发展的重要意义：\n提升AI智能体的可靠性与安全性：在自动驾驶、机器人协作、自动化生产线等实际应用中，一个错误的动作顺序可能导致任务失败甚至安全事故。通过引入因果推理，可以确保智能体的行为遵循基本逻辑，这是将AI智能体从模拟环境推向真实世界应用的关键一步。 降低高性能智能体的应用门槛：该论文提出的方法无需对LLM本身进行微调（fine-tuning），而是作为一个外部“插件”发挥作用。这意味着企业和研究者可以利用现有的、更易于获取的开源模型，通过集成这样一个因果推理模块，来达到甚至超越许多需要大量计算资源训练的专用模型的效果，极大地推动了LLM智能体技术的普及。 增强AI系统的可解释性：当智能体做出一个决策时，我们可以通过查询其背后的因果图和因果矩阵来理解其“思考过程”。例如，“因为它检测到‘手里有洋葱’，所以它认为‘把洋葱放进锅里’是合理的”。这种可解释性对于调试、优化以及建立人类对AI系统的信任至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路、方法或模型： 论文的核心创新是 CausalPlan 框架，它包含以下关键组件： 结构化因果行动（SCA）模型：一种新颖的因果建模思路，直接将“未来动作”作为被预测的变量，清晰地建立了（过去动作，当前状态）→（未来动作）的因果链路。 因果行动矩阵（M）：一个从SCA模型中提炼出的、可直接用于在线查询的、量化的因果规则库。 双模态决策机制：集成了因果感知行动规划（Causal-Aware Action Planning）和因果备用行动（Causal Backup Action），兼顾了对LLM建议的优化和在LLM失效时的鲁棒纠错。 与之前方法的比较、特点和优势： 对比传统LLM智能体（如ProAgent）: 特点：传统方法直接依赖LLM的内在知识进行端到端的规划。CausalPlan则采用了一种“生成-验证”的混合模式。 优势：通过引入一个外置的、显式的因果模型，CausalPlan为LLM的规划过程增加了一道“事实检查”关卡，有效过滤了不合理的想法。这是一种知识的解耦，让通用语言知识和特定任务的因果知识各司其职。 对比传统强化学习（RL）方法（如SP, PBT, COLE）: 特点：RL方法通常需要通过大量的在线试错来学习一个最优策略，学习过程像一个“黑箱”。 优势：CausalPlan的学习过程是离线的、一次性的，并且学习到的是一个具有明确结构和意义的因果图，这使得它数据效率更高且更具可解释性。此外，它利用了LLM的泛化能力，使其在面对未见过的合作伙伴时，表现出比RL方法更好的零样本协调（zero-shot coordination）能力。 对比其他因果推理与RL结合的方法: 特点：以往的研究大多利用因果关系来重塑奖励函数（reward shaping）或学习状态转移模型。 优势：CausalPlan的切入点更直接、更具创新性。 \u0026gt; Unlike prior work…our approach explicitly treats the action as a child node in the causal graph. 它直接对“哪个动作是合理的”进行建模和干预，这对于一个以行动为核心的智能体来说，是更根本、更有效的。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 测试平台：Overcooked-AI，这是一个标准的多智能体协作基准，模拟了双人协作烹饪洋葱汤的过程。它包含五个不同布局的厨房（CR, AA, COR, FC, CC），各有不同的协作难点。 核心模型：选用了四种不同规模的开源LLM作为基础模型（Gemma-7B, Llama-8B, Qwen-14B, Llama-70B）。 对比基线： 自身对比：不使用CausalPlan的同款LLM智能体。 领域SOTA对比：与当前最先进的（State-of-the-art）多智能体RL算法进行比较，包括SP, PBT, FCP, MEP, COLE。 评估场景： AI-AI协作：评估CausalPlan智能体与一系列不同AI基线智能体协作时的表现。 人-AI协作：通过一个“人类代理”策略（在真实人类数据上训练得到的模型）来模拟与人类玩家的协作。 关键指标：主要是在固定时间内成功送餐获得的累积奖励分数。 实验数据和结果： 显著提升LLM性能：图3和附录表A1的数据显示，CausalPlan为所有测试的LLM都带来了稳定的性能提升。以Llama-70B为例，在多个地图上的平均得分从未经优化的150分提升至180分以上。 超越专业RL模型：表1的结果尤为关键，它展示了CausalPlan-Llama-70B与多个专业RL模型的正面对比。 \u0026gt; In the AA layout, CausalPlan (Ours) achieved a score of 258.7 ± 16.4, significantly outperforming the best RL baseline, SP, which scored 184.0 ± 17.5. This represents a 63% advantage. 这个数据极具说服力，证明了该方法的强大竞争力。 有效的人-AI协作：在模拟与人类协作的场景中（图4），CausalPlan智能体（绿色条）在10个测试配置中的8个都取得了最优表现，平均比未优化的LLM智能体（红色条）性能高出约30%。 消融研究（Ablation Study）：表2的消融实验证明了CausalPlan各个组件的必要性。“CausalPlan (Full)”的平均得分（150.7±2.3）显著高于移除了因果备用机制的“CausalPlan (no CBA)”（141.3±12.9），更远高于没有使用任何因果模块的版本（121.3±2.3），这清晰地表明性能增益确实来源于因果模块，而非其他因素（如提示工程）。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 在线因果学习与适应：当前的CausalPlan是在离线数据集上一次性学习因果图。未来的研究可以探索如何让智能体在与环境的持续交互中动态地、在线地更新其因果模型，以适应环境的变化或修正错误的因果信念。 从非结构化数据中学习因果：该方法依赖于预先定义好的、结构化的状态和动作空间。一个巨大的挑战是如何让智能体直接从原始的、高维的输入（如图像、自然语言描述）中自动地发现和学习因果变量及其关系。 更复杂的因果推理：当前模型主要处理动作的先决条件。未来可以探索更复杂的因果推理，如反事实推理（“如果我刚才没拿起洋葱，现在会怎样？”）和工具使用（“为了切洋葱，我需要先找到刀”）。 将“他心理论（Theory of Mind）”融入因果模型：在多智能体协作中，理解伙伴的意图、信念和知识状态至关重要。未来的因果模型可以尝试将对其他智能体的建模也包含进来，形成更深层次的协作。 可能催生的新技术和投资机会： “因果中间件”或“AI智能体安全带”：CausalPlan的成功启发了一种新的产品形态——一个通用的、可插拔的“因果推理引擎”。它可以作为中间件，为任何基于LLM的智能体系统提供安全和逻辑保障，就像汽车的安全带一样。这在金融交易、工业自动化、智能客服等领域有巨大的商业价值。 可解释与可信AI（XAI）解决方案：因果模型天然具有良好的可解释性。基于此技术，可以开发出能够清晰解释“为什么AI会这么做”的系统，这对于需要严格审计和监管的行业（如医疗、法律）是刚需，相关技术和服务的提供商将有广阔的市场。 低代码/无代码智能体开发平台：通过将复杂的因果学习过程自动化，未来的开发平台可能允许用户通过少量示例或自然语言描述，就能快速构建出具备基本逻辑推理能力的、可靠的智能体，这将极大地加速AI应用的落地。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 对状态/动作空间分解的依赖： \u0026gt; We assumed a known factorization of state and action spaces… 这是论文最大的前提假设，也是其应用的潜在限制。在许多现实世界任务中，预先定义一个清晰、完备的状态和动作列表是非常困难的。该方法如何推广到状态和动作空间更模糊、更连续的领域，是一个开放性问题。 因果图学习的质量依赖于数据：附录C.4.3的实验表明，用于学习因果图的离线数据质量至关重要。使用一个强大智能体（MEP）收集的数据训练出的模型，性能远超使用较弱智能体（Llama-8B）收集的数据。这意味着该方法存在“数据瓶颈”，其效果上限受限于数据收集策略的优劣。 可扩展性问题：因果行动矩阵的大小与状态和动作特征的数量成正比。在特征维度极高的复杂环境中，学习和存储这个矩阵的成本可能会变得非常高昂，其在线查询的效率也可能下降。 需要进一步验证和存疑的： 在真实人类协作中的表现：实验中使用的是“人类代理”模型，它虽然基于人类数据，但其行为模式仍然是固定的。该框架在与真实的、行为多变甚至会犯错的人类玩家进行实时协作时，表现如何，还需要进一步的真人测试来验证。 对噪声和不确定性的鲁棒性：实验环境中的状态感知是完美的、无噪声的。在真实世界中，传感器信息可能包含噪声、延迟或缺失。CausalPlan在处理这种不完美信息时的鲁棒性尚待检验。 超参数γ的敏感性：平衡LLM信念和因果分数的权重γ需要针对不同的模型和环境进行调整（见附录表A6）。一个更理想的系统应该能自动调整这个参数，或者对它的变化不那么敏感。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发： 混合智能范式（Hybrid Intelligence Paradigm）：最核心的启发是不要试图让一个单一模型（LLM）解决所有问题。将LLM的生成能力（联想、创意）与一个独立的、显式的符号推理或逻辑模型（如因果图）的验证能力相结合，是一种构建更强大、更可靠AI系统的黄金法则。这是一种“系统1”（快速、直觉）与“系统2”（缓慢、逻辑）思考的结合。 将隐性知识显性化：LLM的知识是隐性的、分布在亿万参数中的。而CausalPlan的成功在于它把任务所需的特定领域知识（因果规则）从数据中提取出来并显性化（因果矩阵）。在你的项目中，可以思考哪些核心的、约束性的知识可以被模型化，并作为LLM决策的“外部约束”或“导航仪”。 设计鲁棒的容错机制：CausalPlan的“因果备用行动”是一个绝佳的例子。当你的主系统（LLM）可能失效时，应该设计一个简单但可靠的备用方案。这个备用方案不一定要非常智能，但必须保证行为的基本合理性，避免灾难性失败。 需要补充的背景知识： 因果科学基础：强烈建议了解结构化因果模型（SCM）和有向无环图（DAGs）的基本概念。阅读Judea Pearl的《The Book of Why》（为什么）会是一个极好的开始，它能帮助你理解因果推断的核心思想。 多智能体强化学习（MARL）：了解MARL领域的基本挑战，如信用分配、非平稳性问题等，可以帮助你更好地理解CausalPlan所解决问题的背景。可以简单了解一下论文中提到的Self-Play, Population-Based Training等方法的思想。 LLM智能体（Agent）框架：熟悉当前主流的LLM Agent框架，如 ReAct (Reason+Act), Reflexion, 以及本文所基于的 ProAgent，了解它们是如何通过提示工程、记忆模块和工具使用来驱动LLM进行决策和行动的。这将为你应用CausalPlan的思想提供具体的工程蓝图。 ","date":"2025-08-21T00:00:00Z","permalink":"/causalplanempowering-efficient-llm-multi-agent-collaboration-through-causality-driven-planning/","title":"CausalPlan：Empowering Efficient LLM Multi-Agent Collaboration Through Causality-Driven Planning"},{"content":"小模型是agent的未来。\n论文深度解读 这篇论文的核心论点可以概括为：当前AI智能体领域普遍依赖大型、通用LLM作为核心引擎的模式是过度、低效且不可持续的，未来必然会转向由更经济、更灵活、功能足够强大的SLM主导的异构、模块化架构。 作者将这一立场（Position）清晰地表述为三个核心论点（Views）：\nV1: SLMs在原则上足够强大（principally sufficiently powerful），能够处理智能体应用的语言建模任务。 V2: SLMs在操作上比LLMs更适合（inherently more operationally suitable）用于智能体系统。 V3: SLMs由于其更小的尺寸，在绝大多数智能体用例中都比通用LLM更经济（necessarily more economical）。\n基于这三点，作者得出结论：SLMs是智能体AI的未来。\n论文的论证过程极具说服力，它并非空谈概念，而是紧密结合了当前的技术现状、智能体系统的架构特点以及部署语言模型的经济学原理。\n首先，在“SLM足够强大”方面（支撑V1），论文列举了大量前沿的SLM家族作为证据。它没有停留在“小模型能力弱”的刻板印象中，而是引用了具体的技术成果来证明SLM的能力已经取得了巨大飞跃。例如： * 微软Phi系列：论文提到，Phi-2（27亿参数）的常识推理和代码生成能力已能与300亿参数的模型媲美，而运行速度快了约15倍。Phi-3 small（70亿参数）更是能与700亿参数的同代模型在多个维度上匹敌。 * NVIDIA Nemotron-H系列：这类混合了Mamba和Transformer架构的模型，在指令跟随和代码生成任务上，能够以低一个数量级的推理FLOPs（浮点运算次数）达到与300亿参数LLM相当的精度。 * DeepSeek-R1-Distill系列：论文特别指出，这个系列的70亿参数模型在常识推理上甚至超越了像Claude-3.5-Sonnet和GPT-4o-0513这样顶尖的专有大模型。 * Salesforce xLAM-2-8B：一个80亿参数的模型在工具调用（Tool Calling）这一智能体核心能力上实现了顶尖水平，超越了GPT-4o等前沿模型。\n这些例子强有力地反驳了“唯有LLM才能胜任复杂任务”的观点，证明了精心设计和训练的SLM完全有能力处理智能体中常见的、定义明确的子任务。\n其次，在“SLM更适合、更经济”方面（支撑V2和V3），论文从系统设计的角度进行了深入剖析。它指出，智能体的工作流本质上是将复杂目标分解为一系列模块化的子任务。这些子任务大多是重复性的、范围有限的、非对话式的（repetitive, scoped, and non-conversational）。例如，格式化API请求、从文本中提取特定信息、生成样板代码等。对于这些任务，LLM的通用性和强大的对话能力不仅是一种浪费，甚至可能因为其“创造性”而导致格式错误或不稳定的输出。\n正如论文所言：“坚持让LLM处理所有这些任务，反映了一种计算资源的错配——这在经济上是低效的，在规模上是环境不可持续的。”\n论文进一步从经济性上给出了量化概念： * 推理效率：部署一个70亿参数的SLM比部署700亿到1750亿参数的LLM在延迟、能耗和计算成本上要便宜10到30倍。 * 灵活性与敏捷性：SLM的微调（Fine-tuning）成本极低，仅需几个GPU小时，使得企业可以“隔夜”为一个新任务或新格式要求定制一个专家模型，而不是等待数周来调整一个庞大的LLM。 * 边缘部署：SLM可以轻松部署在消费级GPU甚至智能手机上（例如，论文中提到的ChatRTX和PowerInfer-2），实现了低延迟、离线运行和更强的数据隐私保护。\n为了让这个构想更具操作性，论文还提出了一个非常实用的“LLM到SLM智能体转换算法”（LLM-to-SLM Agent Conversion Algorithm）。这个六步法（S1-S6）为开发者提供了一个清晰的路线图：从安全地收集智能体内部的调用数据开始，到数据清洗、任务聚类，再到选择合适的SLM、进行专门的微调，最后通过持续迭代来不断优化系统。这使得论文的立场不仅仅停留在理论层面，而是具备了工程实践的指导价值。\n图1：论文中描述的两种智能体代理模式。左侧为“语言模型代理”，LM既是控制器也是工具调用者，趋向于使用单一LLM。右侧为“代码代理”，由一个专用控制器（Code）来编排工具调用，这为使用多个专门的SLM（不同的LM）提供了天然的架构基础。\n总而言之，这篇论文的创新性贡献在于，它系统性地、前瞻性地挑战了当前AI智能体领域“越大越好”的技术惯性。它通过详实的证据、严谨的逻辑和可行的工程路径，描绘了一幅由SLM驱动的、更加高效、经济、灵活和民主化的智能体AI未来蓝图。\n接下来，我将针对您提出的六个问题进行详细解读。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是论证并倡导一种范式转移（Paradigm Shift），即在Agentic AI（智能体AI）的设计和部署中，应从当前主流的、以单一通用LLM为核心的架构，转向以多个、专门化的SLM为基础、LLM为辅助的异构架构。\n解决的实际问题：\n成本与效率问题：目前，绝大多数AI智能体应用（无论是创业公司还是大型企业）都通过API调用少数几家巨头提供的LLM。这些LLM的每次调用都价格不菲，且随着智能体与用户、工具的交互次数增加，成本会急剧上升。同时，LLM的推理延迟较高，限制了需要实时响应的应用场景。 资源错配问题：智能体执行的大量内部任务（如API格式化、数据提取）并不需要LLM级别的通用智能。使用LLM处理这些简单、重复的任务，就像“用牛刀杀鸡”，造成了巨大的计算资源浪费。 灵活性与可控性问题：通用LLM的行为难以精确控制，容易在需要严格格式的输出上“幻觉”或出错。而对LLM进行定制化微调的成本高昂、周期长，难以适应快速变化的业务需求。 对行业发展的重要意义：\n推动AI应用的普及与可持续发展：大幅降低AI智能体的运营成本，可以让更多的中小型企业和个人开发者有能力构建和部署复杂的智能体应用，从而“民主化”AI技术。同时，降低能耗也符合全球对绿色计算和可持续发展的要求。 催生新的技术栈和商业模式：转向SLM-first架构将催生对SLM优化、微调、部署和编排（Orchestration）工具的巨大需求。这会创造一个全新的市场，包括SLM模型库、高效的推理引擎、智能路由（Router）技术以及MaaS（Model as a Service）平台的新形态。 提升智能体系统的性能和可靠性：使用经过专门微调的SLM来处理特定任务，可以获得比通用LLM更稳定、更可靠、更低延迟的性能，从而提升最终用户的体验，尤其是在企业级应用中。正如论文中对智能体交互的分析所言，智能体需要与代码进行紧密的行为对齐（close behavioral alignment），而这正是专用SLM的优势所在。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 本文的核心是新思路和新方法，而非新模型。\n新思路：SLM-First的异构智能体架构 内容：这个思路主张，在设计智能体系统时，默认应该使用SLM来处理绝大多数子任务，只有在遇到确实需要高级、通用推理能力的复杂任务时，才“选择性地、少量地”（selectively and sparingly）调用LLM。这构成了一个“Lego-like”的组合式智能系统，通过增加小而精的“专家”模块来扩展能力，而非无限地扩大单一模型的规模。 与之前方法的比较：之前的主流方法是LLM-Centric，即所有或大部分的语言处理任务都交给一个单一的、最强大的LLM。这个LLM既是“大脑”，也是“手脚”。 特点与优势： 成本效益：如前所述，成本可降低10-30倍。 模块化与可维护性：每个SLM专家负责一个明确的任务，使得系统更容易调试、更新和扩展。如果某个功能出问题，只需检查对应的SLM，而不是去“猜测”一个庞大而神秘的LLM为何出错。 性能优化：可以为每个任务选择或训练最合适的模型，实现整体性能的最优化，包括延迟、精度和稳定性。 新方法：LLM-to-SLM智能体转换算法 内容：这是一个将现有基于LLM的智能体平滑迁移到SLM-first架构的实用分步指南。 S1: 安全地收集使用数据：在智能体内部的非HCI（人机交互）调用点（如工具调用）部署日志记录器（logger），收集输入、输出等数据。 S2: 数据清洗与过滤：对收集到的数据进行处理，去除个人身份信息（PII）等敏感内容。 S3: 任务聚类：使用无监督学习方法对收集到的请求和动作进行聚类，识别出重复出现的、可以被标准化的任务模式（如“意图识别”、“代码生成”等）。 S4: SLM选择：为每个聚类出的任务，根据其特点（如指令跟随、推理需求）选择一个或多个候选SLM。 S5: 专门化SLM微调：使用对应任务的数据对所选的SLM进行微调。论文提到了可以使用PEFT（参数高效微调）技术如LoRA来降低成本。 S6: 迭代与优化：周期性地用新数据重新训练SLM和路由模型，形成一个持续改进的闭环。 与之前方法的比较：之前，优化智能体通常意味着改进提示工程（Prompt Engineering）或等待LLM提供商发布更强的模型。这个方法则提供了一条主动、内部优化的路径。 特点与优势： 可操作性强：提供了一个清晰的、从数据驱动到模型部署的完整流程。 数据驱动：该方法利用智能体在实际运行中产生的“有机数据”（organic data），这些数据质量高，与真实应用场景完全契合。 渐进式迁移：企业可以逐步替换掉成本最高的LLM调用点，而无需对整个系统进行颠覆性重构，风险可控。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 由于这是一篇立场论文，它没有设计和执行全新的、大规模的对比实验。而是通过引用现有研究成果和进行案例分析来支撑其论点。\n证据1：引用前沿SLM的性能数据 设计：作者广泛调研了2024-2025年发布的顶级SLM研究，并直接引用了这些研究中的关键性能对比数据，以此证明“SLM足够强大”（V1）和“更经济”（V3）。 数据与结果： Microsoft Phi-2 (2.7B) vs. 30B models: 在常识推理和代码生成任务上性能相当，速度快~15倍。 NVIDIA Hymba-1.5B vs. 13B+ models: 在指令跟随任务上超越了比它大近10倍的模型，吞吐量高出3.5倍。 DeepSeek-R1-Distill-Qwen-7B vs. Proprietary LLMs: 在常识推理上超越了Claude-3.5-Sonnet和GPT-4o-0513。 Salesforce xLAM-2-8B vs. GPT-4o/Claude 3.5: 在工具调用任务上达到SOTA（State-of-the-Art）。 证据2：LLM-to-SLM替换潜力的案例研究（Case Studies） 设计：作者在附录B中，选取了三个流行的开源智能体框架（MetaGPT, Open Operator, Cradle），分析了它们内部LLM的调用类型，并评估了其中有多少比例可以被专门化的SLM替代。这是一个定性的、基于专家经验的评估。 数据与结果： 开源智能体 用途 LLM调用场景 SLM适用场景 可替换比例估算 MetaGPT 模拟软件开发团队 角色扮演、代码生成、文档编写 例行代码生成、基于模板的结构化响应 ~60% Open Operator 工作流自动化 解析用户意图、决策执行流、生成报告 简单的命令解析、基于模板的消息生成 ~40% Cradle GUI应用控制 理解视觉上下文、规划GUI操作序列 重复性的GUI交互流程、执行预学习的点击序列 ~70% * **结论**：这些案例研究直观地表明，在一个真实的、复杂的智能体应用中，有相当大比例（**40%-70%**）的LLM调用属于SLM可以胜任的范畴。这为论文的核心论点提供了来自应用层面的有力佐证。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文开启了一个充满机遇的研究方向。\n值得探索的问题和挑战： 智能路由与模型编排（Intelligent Routing \u0026amp; Orchestration）：当系统中有成百上千个专家SLM时，如何构建一个高效、准确的“路由器”（Router）来决定在特定步骤调用哪个模型（或者调用LLM）？这个路由本身可能就需要一个轻量级的模型来决策，如何训练这个“元模型”是一个关键挑战。 SLM的“智能体能力”优化：当前很多SLM依然是在通用语料上预训练的。未来需要专门为智能体任务（如工具调用、格式遵循、小任务规划）设计预训练方法和数据集，让SLM“天生”就更擅长作为智能体的一环。 统一的SLM微调与部署平台：如何高效地管理、微调、测试和部署大量的SLM模型？这需要一套全新的MLOps（机器学习操作）工具链，专门为这种“模型菌群”（Model Swarm）生态设计。 知识组合与迁移：多个专家SLM的知识是分散的。如何让它们在需要时进行有效的知识组合？一个SLM的微调经验如何能低成本地迁移到另一个SLM上？ 成本效益的临界点分析：在什么情况下，维护多个SLM的复杂性成本会超过使用单一LLM的额外费用？需要更精细的量化分析模型来帮助开发者做决策。 新的技术和投资机会： SLM-Native的智能体框架：开发原生支持多模型、异构执行的下一代智能体开发框架。 自动化SLM工厂：提供一站式服务，能自动完成论文中提出的“LLM-to-SLM转换算法”，即上传LLM调用日志，自动产出优化后的、由多个专家SLM组成的智能体。 超高效推理引擎：专门为在边缘设备或服务器上同时运行大量不同SLM而优化的推理服务技术。 小型化、专业化的模型提供商：可能会出现专注于提供某个特定领域（如金融API调用、医疗文书摘要）SOTA级SLM的商业公司。 AI Agent安全与治理：管理大量SLM的访问权限、数据流和行为一致性，将成为一个新的安全领域。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文极具启发性，但从批判的角度看，也存在一些值得商榷之处。\n不足及缺失： 缺乏大规模实证研究：作为一篇立场论文，其最大的“不足”是缺少一个大规模的、端到端的实证研究。例如，将一个复杂的商业级智能体完全按照其提出的方法进行改造，并与原有的LLM版本在成本、性能、用户满意度等方面进行严格的A/B测试。附录中的案例研究只是估算，并非实测。 低估了异构系统的复杂性：论文对其所提倡的“Lego-like”系统的管理复杂性可能有些过于乐观。管理成百上千个模型的生命周期、版本控制、持续集成/持续部署（CI/CD）是一个巨大的工程挑战，其隐性成本可能很高。 对“路由”问题的探讨不足：智能路由是整个方案成功的关键。如果路由决策的错误率很高，或者路由模型本身就需要大量资源，那么SLM-first架构的优势就会大打折扣。论文对此提及不多。 需要进一步验证和存疑之处： 任务分解的普适性：论文的一个核心假设是，大部分智能体的复杂任务都可以被清晰地分解为适合SLM处理的简单子任务。但在某些高度依赖全局上下文、长程推理或创造性思维的场景下，这种分解可能非常困难，或者分解后的子任务仍然需要LLM级别的能力。这个假设的边界在哪里，尚不明确。 “涌现能力”的损失：LLM的一个特点是涌现能力（Emergent Abilities），即在处理一系列简单任务的过程中，可能会展现出未曾预料的、更高层次的智能。将所有任务拆分给独立的、专业的SLM，是否会丧失这种系统层面的整体智能和灵活性？ 数据的“冷启动”问题：论文提出的转换算法依赖于收集足够的调用数据。对于一个全新的智能体应用，在没有大量用户数据之前，如何进行有效的任务聚类和SLM微调？这存在一个“先有鸡还是先有蛋”的问题。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是一个充满了可实践想法的宝库。\n重点学习和创新想法： 成本审计与任务分析：立刻行动！ 如果您正在开发或运营一个AI智能体，请马上借鉴论文的思路，对您的LLM API调用进行一次成本审计。分析哪些调用是简单、重复、格式化的。这些就是您最先可以用SLM替代的“低垂的果实”。 实施数据日志：按照论文S1的建议，在您的智能体系统中建立一个安全的日志管道，开始系统性地收集内部模型调用的数据。这是您未来进行任何模型优化、微调和迭代的宝贵资产。 两阶段模型调用策略：作为一个简单的起点，您可以设计一个两阶段的调用策略。首先尝试用一个轻量级的SLM（如Phi-3, Llama-3-8B等）来处理请求。如果SLM的输出置信度低或格式错误，再将任务“升级”给更昂贵的LLM处理。这是一种简单但有效的成本节约方法。 为“工具调用”专门微调一个SLM：工具调用是智能体的核心。您可以收集所有与工具调用相关的交互数据，专门微调一个SLM来处理这项任务。由于工具的API格式是固定的，这个任务非常适合SLM。 启发： 从“模型消费者”到“模型构建者”：不要仅仅依赖外部的LLM API。通过微调SLM，您可以拥有专属于您业务的、成本可控的AI能力。 系统思维优于模型崇拜：一个成功的AI应用，其精妙的系统架构设计可能比单纯使用最强大的模型更为重要。资源的最优配置是关键。 AI的可持续性：在追求更高性能的同时，要开始关注计算的经济和环境成本。高效是未来AI发展的核心议题之一。 需要补充的背景知识： 参数高效微调（PEFT）技术：深入了解LoRA, QLoRA, DoRA等技术。这些是您低成本微调SLM的“瑞士军刀”。 开源SLM模型：熟悉当前主流的开源SLM家族，如Phi-3, Llama 3, Gemma, Mistral等，了解它们的特点、性能和许可协议。 模型量化与推理优化：学习模型量化（Quantization）、剪枝（Pruning）等技术，以及如何使用像vLLM, TensorRT-LLM这样的推理引擎来最大化SLM的部署效率。 智能体框架（Agentic Frameworks）：了解如LangChain, LlamaIndex, AutoGen等框架。知道它们是如何处理任务分解、工具使用和模型调用的，可以帮助您更好地找到应用SLM的切入点。 希望这份详尽的解读能帮助您全面、深入地理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-08-20T00:00:00Z","permalink":"/small-language-models-are-the-future-of-agentic-ai/","title":"Small Language Models are the Future of Agentic AI"},{"content":" d336e4399b581a80604ab07febd868c1.png 目前的上下文工程统一成了四个部分： - 写入 - 读取 - 压缩 - 隔离 图片来自Context Engineering for Agents\n写入 读取 压缩 隔离 参考 Context Engineering，一篇就够了。\nContext Engineering for Agents\n","date":"2025-08-19T00:00:00Z","permalink":"/context_engineering/","title":"context_engineering"},{"content":"感觉思想和First Return, Entropy-Eliciting Explore和AGENTIC REINFORCED POLICY OPTIMIZATION很类似，利用熵做额外探索。联合优化使用的GRPO\nimage.png 论文深度解读：在“不确定”处寻找“确定”的进步 这篇论文的核心，是解决一个在利用强化学习与验证者奖励 (Reinforcement Learning with Verifier Reward, RLVR) 训练大语言模型（LLM）进行复杂推理（如数学解题）时普遍存在的棘手问题。这个问题就是，模型在训练过程中会很快变得“自信”，倾向于生成少数几种高度相似的、缺乏多样性的答案。这种现象被称为策略熵衰减。一旦熵衰减发生，模型的探索能力就会急剧下降，无法发现新的、更优的解题路径，导致其性能提升过早地陷入停滞。这就像一个学生只反复练习自己已经掌握的几种解法，却不去探索更巧妙或更通用的方法，最终难以成为顶尖高手。\n为了攻克这一难题，论文作者提出了一个名为 CURE 的创新框架，其全称为“Critical-token-gUided Re-concatenation for Entropy-collapse prevention”（直译为：关键令牌引导的重拼接以防止熵衰减）。CURE 的精髓在于它不再像以往的方法那样，仅仅通过调整损失函数或奖励机制等“间接”手段来鼓励探索，而是直接从训练数据生成的根源入手，动态地、有针对性地扩展模型的探索空间。\nCURE 框架被巧妙地设计为两个阶段，完美地平衡了探索 (Exploration) 和利用 (Exploitation) 这对强化学习中的经典矛盾。\n第一阶段：有引导的探索 (Guided Exploration)\n这是 CURE 框架最具创造性的部分。其核心思想是：在模型最“迷茫”、最“不确定”的时刻，给予它新的探索起点。具体流程如下：\n识别不确定性：对于一个给定的问题（prompt），模型会像往常一样生成一个或多个解答。在生成过程中，CURE会实时计算每个步骤（每个 token）的策略熵。策略熵是一个衡量不确定性的指标，熵值越高，代表模型在这一步对于接下来该生成哪个词（token）越不确定，这往往是推理路径的关键分岔口。 定位“关键令牌”：CURE 会在所有生成的 token 中，找出熵最高的那些，称之为关键令牌 (Critical Tokens)。 创造新的起点：找到关键令牌后，CURE 会截取该令牌之前的所有文本内容（即“前缀”），然后将其拼接到原始问题的后面，形成一个全新的、更长的“细化提示 (refined prompt)”。 再生成与联合优化：模型会基于这个新的、更具体的提示，生成全新的解答。最后，这些新生成的解答（branched trajectories）会和最初的解答（original trajectories）被放在一个组里，进行联合优化。 正如论文中所描述的：\n…we re-generate at high-entropy critical tokens and jointly optimize the original and the branched trajectories.\n（我们在高熵的关键令牌处重新生成，并联合优化原始轨迹和分支轨迹。）\n这个过程的巧妙之处在于，它迫使模型从自己先前感到困惑的地方出发，探索之前从未见过的、但又与当前上下文紧密相关的推理路径。这极大地丰富了训练数据的多样性，从根本上延缓了熵的衰减。从 图 3 (Figure 3) 的策略熵演化曲线中可以清晰地看到，在训练过程中，其他方法（如 GRPO、DAPO）的策略熵迅速下降或维持在低位，而 CURE 第一阶段的策略熵却能持续增长，这直观地证明了其强大的探索能力。\n第二阶段：聚焦的利用 (Focused Exploitation)\n在第一阶段充分探索、积累了大量多样的解题经验后，模型需要将这些“发散”的知识“收敛”为稳定、可靠的题能力。因此，CURE 进入第二阶段，回归到更传统的训练方式。\n在这一阶段，CURE 停止了基于关键令牌的重拼接过程，转而使用像 DAPO 这样的成熟算法，在固定的原始问题集上进行训练。这相当于让模型回到一个熟悉的环境中，对第一阶段学到的各种解题思路进行“复盘”和“提纯”，从而将探索阶段获得的潜在优势，转化为实实在在的性能提升。\n论文中这样描述这个转换：\n…to translate the exploration bonus into realized performance.\n（……将探索红利转化为实际性能。）\n实验结果充分证明了 CURE 框架的有效性。在 表 1 (Table 1) 的性能对比中，经过两个阶段训练的 CURE 在六个数学基准测试中取得了 54.3% 的平均分，显著优于之前所有的 RLVR 方法和熵控制方法，例如强大的 NFT (51.7%) 和 Clip-Cov (50.9%)。特别是在 AIME24 和 AMC23 等竞赛级数学数据集上，性能提升尤为明显。这表明 CURE 不仅成功地维持了探索，更关键的是，它能高效地将探索成果转化为最终的推理准确率。\n总而言之，CURE 论文的贡献是提出了一种新颖的、数据驱动的范式来解决强化学习中的熵衰减问题。它通过倾听模型自身的“困惑”，并将其作为新知识的生长点，实现了一种高效且智能的探索机制。这种“从不确定性中汲取力量”的哲学，为未来训练更强大、更具创造性的 AI 推理模型，开辟了一条充满希望的道路。\n### 1. 论文的研究目标与行业意义 研究目标 论文的核心研究目标是解决在利用强化学习与验证者奖励 (RLVR) 训练大语言模型进行复杂推理时遇到的策略熵衰减 (Policy Entropy Collapse) 问题。\n实际问题 在实际应用中，当使用强化学习让模型自我提升时（例如学习解决数学题），模型会很快收敛到它认为“最安全”的几种解题模式上。这导致： * 性能瓶颈：模型停止探索新的、可能更优的解题方法，性能提升很快达到天花板。 * 多样性丧失：模型生成的答案千篇一律，缺乏创造性和鲁棒性。 * 训练效率低下：大量的计算资源被用于重复低质量的探索，无法带来持续的进步。\n行业意义 这个问题是当前大模型能力扩展的核心瓶颈之一。能够通过自我迭代和学习不断变强的“自进化”AI 是行业的终极目标之一。如果熵衰减问题无法解决，那么模型的自我提升能力将大打折扣。因此，攻克这一难题具有重大的行业意义： * 解锁模型潜力：它为实现更持续、更深入的强化学习训练铺平了道路，有望将大模型的推理能力提升到一个新的高度。 * 提升AI可靠性：通过鼓励多样化的推理路径，可以增强模型在面对新颖或复杂问题时的鲁棒性和适应性。 * 推动通用人工智能（AGI）发展：解决探索与利用的平衡问题，是机器智能从“模仿”走向“创造”和“推理”的关键一步，对 AGI 的长远发展至关重要。\n### 2. 论文提出的新方法及其优势 新思路与方法 论文提出了一个名为 CURE 的两阶段框架，其核心创新是关键令牌引导的重拼接 (Critical-Token-Guided Re-concatenation) 机制。\n数据层面的干预：与以往在算法层面修改损失函数（如添加 KL 散度惩罚项）或奖励层面进行奖励塑造（如增加熵奖励）的方法不同，CURE 独辟蹊径，在数据层面进行干预。它通过动态生成新的、更具挑战性的训练样本来直接塑造训练状态的分布。\n基于模型自身不确定性的引导：CURE 的探索不是盲目的。它利用策略熵来识别模型在推理过程中最“不确定”的“决策点”（即高熵的关键令牌）。这种“自我诊断”式的探索，确保了新生成的训练数据总是能命中模型的知识盲区，从而实现高效探索。\n探索-利用分离的两阶段设计：\n第一阶段 (探索)：通过重拼接机制，强制模型探索新的推理分支，维持高的策略熵。 第二阶段 (利用)：切换回标准的 RL 算法（DAPO），在固定数据集上训练，将探索阶段获得的知识固化为稳定的能力。 论文对此的总结非常精辟：\nEmpirically, this decoupled explore-then-exploit design arrests entropy collapse, maintains policy diversity when it matters, and then selectively compresses it to harvest performance.\n（根据经验，这种解耦的‘先探索后利用’设计能够阻止熵衰减，在关键时刻保持策略多样性，然后有选择地压缩它以收获性能。）\n特点与优势 与之前的方法相比，CURE 的优势在于： * 更根本：直接作用于数据分布，比间接调整损失函数或奖励更接近问题的本质。 * 更高效：探索是有目标的，直接针对模型的不确定性，避免了无效的随机探索。 * 更稳定：两阶段设计清晰地分离了探索和利用，避免了两者在单一训练阶段中互相干扰，使得训练过程更稳定、目标更明确。 * 即插即用 (Plug-and-Play)：该方法具有很好的模块化特性，可以轻松地集成到现有的 RLVR 训练流程中，只需替换采样器并增加一个简单的两阶段调度即可。\n### 3. 实验设计与结果验证 论文通过一系列严谨的实验来验证 CURE 方法的有效性。\n实验设计 基础模型：Qwen2.5-Math-7B，一个专为数学任务设计的模型。 训练数据：DAPO-Math-17K 数据集，包含 1.7 万个数学问题。 评估基准：在六个广泛使用的数学推理基准上进行评估，包括 AIME24/25、AMC23、MATH500 等，这些数据集覆盖了从高中到奥林匹克竞赛级别的难度。 对比方法：与多种主流的 RLVR 方法（如 SimpleRL-Zero, NFT）和最新的熵控制方法（如 KL-Cov, Clip-Cov）进行了全面对比。 关键数据与结果 实验结果有力地支持了 CURE 的优越性。\n总体性能：如下表所示（根据论文 Table 1 整理），CURE Second Stage 在所有方法中取得了最佳的平均性能。 方法 (Method) 平均准确率 (Avg. %) SimpleRL-Zero 41.3 NFT 51.7 KL-Cov 50.7 Clip-Cov 50.9 CURE First Stage 52.1 CURE Second Stage 54.3 熵维持能力：图 3 (Figure 3) 是最核心的证据。它展示了不同方法在训练过程中的策略熵变化。CURE First Stage 的策略熵（绿线）显著且持续地增长，而其他方法的熵要么快速崩溃（GRPO），要么维持在较低水平（DAPO, Clip-Cov）。这直观地证明了 CURE 在防止熵衰减方面的有效性。\n探索的质量：图 5 (Figure 5) 分析了一些关键逻辑连接词（如 “check”, “verify”, “hence”）在高熵令牌中的出现频率。结果显示，CURE 显著提升了这些代表着关键推理步骤的词语的使用频率。例如，“verify” 的出现次数从基线的 33 次飙升到 188 次。这表明 CURE 的探索不是产生无意义的随机文本，而是在鼓励更有条理、更深入的逻辑推理。\n消融研究 (Ablation Study)：表 2 (Table 2) 中，作者比较了基于熵选择（CURE_Entropy）和随机选择（CURE_Random）截断点进行重拼接的效果。结果显示，基于熵的策略（平均分 53.7%）远优于随机策略（51.4%），这证明了“在不确定处探索”这一核心思想的有效性。\n### 4. 未来探索方向、挑战与机遇 值得探索的问题和挑战 自适应调度：当前两个阶段的切换是手动的。未来可以研究一种自适应的调度策略，让模型能够根据自身的学习状态（如性能饱和度、熵水平）自动在探索和利用之间切换。 跨领域泛化：将 CURE 的思想应用到数学之外的领域，如代码生成、科学文献问答、多模态推理等。在这些领域中，“关键决策点”的定义可能需要调整。 关键令牌的深层理解：目前仅使用熵作为指标。未来可以结合其他方法，如模型梯度、注意力权重等，来更精确地识别真正影响推理质量的“关键令牌”。 理论分析：对 CURE 框架下的熵动态演化进行更深入的理论分析，从数学上解释其为何能有效防止熵衰减，并指导参数设计。 新技术与投资机会 自进化AI系统：CURE 的成功为构建能够持续自我改进的 AI 系统提供了关键技术。这可能催生出新一代的 AI 应用，它们能够在使用过程中不断学习和进化，尤其是在专业领域（如科研、法律、医疗）。 高效RL训练平台：围绕这类数据驱动的探索增强技术，可以开发新一代的 RL 训练平台，为企业提供更高效、更强大的模型微调和对齐服务。 投资机会：专注于开发具有高级推理和自学习能力的基础模型公司，以及提供高效能AI训练解决方案的公司，将具有巨大的投资潜力。能够解决复杂、开放式问题的 AI 代理（Agent）技术是下一个风口，而 CURE 正是构建这类代理的核心技术之一。 ### 5. 论文的不足与批判性视角 尽管这篇论文取得了显著的成果，但从批判的角度看，仍存在一些可以进一步探讨和完善之处：\n模型与规模的局限性：所有实验都基于 Qwen 家族的 7B 模型。该方法在其他模型架构（如 Llama, Mistral）或更大规模（如 70B, \u0026gt;100B）的模型上是否同样有效，仍有待验证。大模型可能表现出不同的熵动态。 任务领域的单一性：实验完全集中在数学推理上。虽然这是一个极具挑战性的领域，但其问题结构相对清晰。CURE 在更开放、更主观的创造性任务（如写诗、写小说）或需要与外部环境交互的任务中表现如何，尚是未知数。 计算成本考量：第一阶段的重拼接和再生成过程，无疑会增加训练的计算开销。论文并未详细分析这种开销与性能提升之间的权衡关系（trade-off）。在实际部署时，这可能是一个重要的考量因素。 超参数敏感性：CURE 引入了一些新的超参数，如初始采样数量 N1、重采样数量 N2、选择 Top-K 的 K 值。这些参数的最优选择可能依赖于具体任务和模型，论文中对其敏感性的讨论不够充分。 ### 6. 核心启发与知识补充 重点学习与启发 转变思维：从“算法调优”到“数据塑造”：最大的启发是，解决 RL 训练中的问题，不应只局限于修改算法本身，而应更多地关注如何动态地、智能地构造和塑造训练数据流。让数据本身来引导模型的学习，可能比复杂的算法调整更有效。 利用模型的不确定性：模型表现出的“不确定性”（高熵）不应被视为噪声或缺陷，而应被看作是宝贵的学习信号。它是模型知识边界的体现，是最高效的学习区域。这个思想可以被广泛应用于主动学习、课程学习等多个领域。 解耦复杂问题：CURE 将“探索”和“利用”这两个纠缠在一起的复杂问题，通过两阶段设计成功解耦。这种分而治之的策略，对于解决其他复杂的 AI 问题也极具借鉴意义。 需要补充的背景知识 为了完全理解并应用这篇论文的思想，我建议您补充了解以下背景知识： * 强化学习基础：特别是策略梯度 (Policy Gradient) 方法，以及广泛用于 LLM 训练的 PPO (Proximal Policy Optimization) 算法。了解 GRPO 和 DAPO 作为其变体，会有助于理解 CURE 的优化目标。 * 信息论基础：深入理解熵 (Entropy) 的概念，不仅是其数学定义，更重要的是它作为“不确定性”或“信息量”的物理和信息学含义。 * LLM的训练范式：了解从预训练（Pre-training）、指令微调（Instruction-Tuning）到对齐（Alignment）的整个流程，特别是基于人类反馈的强化学习（RLHF）和基于 AI 反馈的强化学习（RLAIF）的原理。 * 大模型推理：熟悉思维链 (Chain-of-Thought, CoT) 等技术，理解 LLM 是如何通过生成中间步骤来解决复杂问题的，这将帮助您更好地理解 CURE 作用于推理过程的机制。\n希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。如果您还有任何问题，随时可以继续提问。\n","date":"2025-08-19T00:00:00Z","permalink":"/curecritical-token-guided-re-concatenation-for-entropy-collapse-prevention/","title":"CURE：Critical-Token-Guided Re-concatenation for Entropy-collapse Prevention"},{"content":"利用熵来给予不同的reward，变相的改变了优势值，类似于ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM\nimage.png 首先，让我们对这篇论文进行一个全面的、高度浓缩的解读。\n这篇论文的核心目标是解决在利用强化学习（Reinforcement Learning, RL）提升大模型（LLM）推理能力时，一个常见于 GRPO (Group Relative Policy Optimization) 算法中的技术瓶颈——优势坍塌 (Advantage Collapse)。简单来说，GRPO通过比较模型对同一个问题生成的多个答案（一个“组”）的好坏来进行优化。但当一个问题太难，模型生成的所有答案都是错的（或者太简单，所有答案都对），它们会得到完全相同的奖励分数。这时，每个答案相对于“组平均水平”的“优势（Advantage）”就变成了零。策略优化的梯度依赖于这个“优势”值，一旦为零，模型就无法从这个样本中学习到任何东西，导致训练效率低下，尤其是在面对有价值的难题时。\n为了攻克这个难题，作者们独创性地提出了 EDGE-GRPO 算法，这是一个双管齐下的优雅框架。它包含两个关键创新点：\n引导式纠错 (Guided Error Correction, GEC)：这是一个在响应层面 (response-level) 的干预策略。它不再天真地寄希望于模型能“自我反思”来纠正错误（论文用实验证明了这种方式效果很差），而是在模型生成了一组错误的答案后，以一定的概率，主动地将外部的、正确的参考答案“注入”或“替换”到这组答案中。这就像一位导师，在学生们都做错题时，直接给出正确解法作为示范。这种方法虽然简单，但却从根本上保证了答案组里的响应多样性 (response diversity)，从而确保了奖励的差异性，直接避免了“优势坍塌”的发生。\n熵驱动优势 (Entropy-Driven Advantage, EDA)：这是一个在信号层面 (signal-level) 的精细化设计。仅仅避免优势为零还不够，作者们追求更高效的训练信号。他们引入了策略熵 (policy entropy) 作为衡量模型对其生成答案“自信程度”的指标（低熵代表高自信，高熵代表不确定）。论文敏锐地观察到，模型常常存在“自信地犯错”或“不自信地答对”的情况（见论文图3右侧）。EDA正是利用这一点，对原始的“优势”值进行调整。具体来说，对于正确但高熵（不自信）的答案，EDA会给予适度的奖励，鼓励探索；而对于错误但低熵（迷之自信）的答案，则会给予更严厉的惩罚。这使得训练信号变得异常丰富和精确，引导模型朝着“既正确又自信”的方向优化。\n正如论文在摘要中所述：\nIn this work, we begin by analyzing the limitations of model reflection and investigating the policy entropy of responses at the fine-grained sample level. Based on our experimental findings, we propose the EDGE-GRPO algorithm, which adopts Entropy-Driven Advantage and Guided Error Correction to effectively mitigate the problem of advantage collapse.\n论文最令人印象深刻的成果在于其惊人的数据效率。如下图1所示，仅仅使用1000个（1K）训练样本，EDGE-GRPO就能将在Qwen2.5-Math-7B这样的基础模型上训练出的性能，提升到与其他使用数千甚至数万样本训练的开源模型（如Oat-Zero-7B, Eurus-2-7B-PRIME）相媲美的水平。这充分证明了其方法的有效性和先进性。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决大型语言模型在进行基于GRPO算法的强化学习时所面临的优势坍塌 (Advantage Collapse) 问题。\n实际问题：\n在现实中，我们希望通过强化学习让模型学会解决复杂的推理问题（如数学、编程）。GRPO是一种很有前景的算法，因为它比传统的PPO算法资源消耗更少。 然而，当模型面对一个难题，它可能会生成一组（例如8个）答案，但所有答案都是错误的。根据GRPO的规则，这些答案会获得相同的低分奖励（比如0分）。 计算“优势”的公式为 A_i = r_i - mean(r_1, r_2, ..., r_G)。如果所有 r_i 都相等，mean就等于r_i，于是所有的A_i都等于0。 策略梯度更新正比于 A_i，A_i为0意味着梯度消失，模型无法从这次宝贵的“试错”经验中学到任何东西。这导致了样本效率 (sample efficiency) 的急剧下降，大量有价值的、困难的训练数据被浪费掉了。 行业意义：\n提升训练效率，降低成本：大模型的训练成本极其高昂。解决优势坍塌意味着能用更少的训练数据和更短的训练时间，达到同样甚至更好的模型性能。这直接关系到AI研发的成本控制和迭代速度。 增强模型解决难题的能力：优势坍ăpadă问题在难题上尤为突出。解决了它，模型就能更有效地从失败中学习，从而更好地攻克数学、科学、逻辑等复杂推理任务的边界，这是通往更高水平人工智能（如AGI）的关键一步。 推动RLHF/RLAIF的落地：强化学习（特别是从人类或AI反馈中学习）是当前优化大模型的核心技术之一。提高其稳定性和效率，能让这项技术更容易被广泛应用，催生出更强大、更安全的AI模型。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心方法是 EDGE-GRPO，它由两个正交的模块构成，其整体框架如下图4所示。\nFigure 4 from paper 新思路与方法： 引导式纠错 (Guided Error Correction, GEC) - 在响应层面打破僵局 特点：这是一种非常实用的数据增强或干预策略。当一组响应被判断为全错时，GEC会启动。它不是让模型自己“反思”（论文图2右侧的实验显示，强制模型反思的成功率极低，大部分低于5%），而是采取三种外部干预措施： P = 0.5 的概率：仅给出反思提示（如 “Wait! Something is wrong here.”），让模型重新生成。这保留了模型的自主性。 P = 0.25 的概率：在给出提示的同时，直接注入正确的答案。 P = 0.25 的概率：直接用一个高质量的参考解替换掉模型的错误答案。 优势：与之前依赖模型“自我救赎”的“模型反思”（Model Reflection）相比，GEC的优势是确定性和高效性。它能保证响应组中至少包含一些高质量的正面样本，从而从根本上杜绝了优势坍塌。它承认了模型能力的边界，并在模型“卡壳”时给予最直接有效的帮助。 熵驱动优势 (Entropy-Driven Advantage, EDA) - 在信号层面精雕细琢 特点：在GEC保证了优势不为零后，EDA进一步优化这个训练信号。它利用了策略熵 P 来调整优势 A，核心公式为 A_new = A_i / P_i（其中P_i是归一化后的熵）。 优势：相比之前的工作，EDA更加精细和有针对性。 有些工作主张最小化熵，认为低熵代表高准确率，但这可能扼杀模型的探索能力。 有些工作主张最大化熵，以鼓励探索，但这可能导致模型在简单问题上也不够自信。 EDA则采取了一种情境化的策略： 正确且自信 (r \u0026gt; mean(r), P低)：A为正，P小，A_new变得更大。大力奖励。 错误但自信 (r \u0026lt; mean(r), P低)：A为负，P小，A_new的负值绝对值更大。严厉惩罚。 正确但不自信 (r \u0026gt; mean(r), P高)：A为正，P大，A_new变小。温和奖励，容忍探索。 错误但不自信 (r \u0026lt; mean(r), P高)：A为负，P大，A_new的负值绝对值变小。轻微惩罚。 这种动态调整使得训练信号的“信噪比”更高，能更准确地引导模型优化。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且严谨的实验来验证EDGE-GRPO的有效性。\n实验设计： 基础模型：选择了两个不同参数规模的主流开源模型 Qwen2.5-Math-1.5B 和 Qwen2.5-Math-7B。 训练数据：为了凸显数据效率，作者并未使用庞大的数据集，而是从 DeepScaleR 数据集中精选了1000个样本（DeepScaleR-Random-1K）。为了进一步测试模型在难题上的表现，他们还构建了一个更具挑战性的 DeepScaleR-Hard-1K 数据集。 评估基准：使用了五个业界公认的、有挑战性的数学推理基准测试集：AIME, AMC, MATH, Minerva, OlympiadBench，总计1560个问题。 对比方法（Ablation Study）：为了证明每个模块的贡献，作者进行了一系列消融实验，对比了： Base：未经任何训练的基础模型。 SFT：仅使用监督微调。 Vanilla GRPO：标准的GRPO算法。 +Force-R：在GRPO基础上加入强制反思。 +Force-R \u0026amp; EDA：在GRPO基础上加入强制反思和熵驱动优势。 EDGE-GRPO：完整的算法（GEC + EDA）。 实验数据和结果： 性能显著提升：如下表1（训练集为Random-1K），EDGE-GRPO在两个模型上都取得了最佳平均性能。例如在1.5B模型上，EDGE-GRPO (48.08) 远超 Vanilla GRPO (40.32)。在7B模型上，EDGE-GRPO (49.30) 也超过了 Vanilla GRPO (46.47)。 Model Method Avg (1560) Qwen2.5-Math-1.5B Vanilla GRPO 40.32 EDGE-GRPO 48.08 Qwen2.5-Math-7B Vanilla GRPO 46.47 EDGE-GRPO 49.30 * **优势坍塌问题的缓解**：论文图5可视化了训练过程中**组内优势方差（Intra-group advantage variance）**的变化。 \u0026gt; As shown in Figure 5, Compared to the vanilla GRPO algorithm and the variant with enforced reflection, our method maintains a higher level of intra-group advantage variance during training, significantly alleviating the advantage collapse problem. * 结果显示，Vanilla GRPO的优势方差很快衰减并趋近于零，而EDGE-GRPO则能持续维持在较高的水平，这直观地证明了它成功地缓解了优势坍塌。 * **在难题上效果更佳**：在更难的 DeepScaleR-Hard-1K 数据集上（见表2），EDGE-GRPO的优势更加明显。在7B模型上，Vanilla GRPO的性能为47.69，而EDGE-GRPO达到了**53.21**，提升巨大，说明该方法尤其擅长帮助模型攻克难关。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这是一个非常好的问题，这篇论文为我们打开了新的思路。\n值得探索的问题和挑战： GEC策略的智能化：目前GEC的干预概率（0.5, 0.25, 0.25）是固定的超参数。未来能否设计一个动态GEC？例如，根据问题的难度、模型当前的能力水平，或者一个epoch中的不同阶段，自动调整干预的强度和方式。这可能需要一个“元控制器”（meta-controller）来学习最佳的干预策略。 EDA的信号源扩展：目前EDA仅使用了策略熵。未来可以探索融合更多维度的“内部反馈”信号，例如： 语义熵：衡量一组答案在语义上的多样性，而不仅仅是token层面的概率。 时间/计算成本：将生成答案所花费的推理步数或计算量也作为信号，惩罚那些“又慢又错”的答案。 可信度校准：探索更复杂的模型不确定性估计方法，而不仅仅是策略熵，以获得更校准的置信度。 领域的泛化性：当前实验完全集中在数学推理领域。EDGE-GRPO在代码生成、长篇故事创作、科学文献问答等其他需要复杂推理的领域是否同样有效？这是一个亟待验证的关键问题。 对参考答案质量的依赖：GEC的有效性高度依赖于高质量的参考答案。在许多现实场景中，获取完美、唯一的参考答案是困难或昂贵的。如何设计一个对噪声或多样化参考答案更鲁棒的GEC版本，是一个重要的研究方向。 可能催生的新技术和投资机会： 高效能AI训练平台：基于EDGE-GRPO这类思想，可以开发出新一代的RLHF/RLAIF（从AI反馈中强化学习）平台。其核心卖点是“用最少的数据和算力，达到最好的效果”，这对于众多希望拥有自己专用模型的企业来说具有巨大的吸引力。 自动化课程学习（Automated Curriculum Learning）：GEC实际上是在为模型设计一个“课程”。这可以启发开发更智能的自动化课程学习系统，该系统能够评估模型的当前状态，并从庞大的数据池中挑选出最适合当前模型学习的“甜点”样本（既有挑战性又不过于困难），并动态调整反馈强度。 模型可解释性与调试工具：EDA通过分析模型的“自信度”来指导训练，这本身就提供了一个观察模型“内心世界”的窗口。围绕这个思路，可以开发出更先进的模型调试工具，帮助研究人员理解模型为什么会犯错，尤其是在它“迷之自信”的时候。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些方面值得商榷和进一步完善。\n潜在不足与缺失： 泛化性验证不足：这是最主要的一点。所有实验都局限于数学推理。数学问题答案相对客观、唯一，这使得GEC的“答案注入”策略非常有效。但在开放性问题（如“如何写一首关于秋天的诗？”）或需要权衡利弊的商业决策问题上，不存在唯一的“正确答案”。GEC的方法可能难以直接适用，其有效性需要打上一个问号。 对GEC的公平性讨论：GEC直接引入外部正确答案，有人可能会认为这在一定程度上是“开卷考试”，削弱了纯粹强化学习的色彩。论文的消融实验证明了其有效性，但未能完全厘清性能提升中，有多少归功于这种“有监督的纠正信号”，有多少归功于更优化的RL过程。 超参数敏感性分析缺失：论文中GEC的概率和EDA计算中的温度T都是固定值。这些超参数的选择是否对最终结果有很大影响？如果改变这些值，性能会如何变化？缺少这部分的敏感性分析，使得方法的可复现性和鲁棒性略显不足。 对参考数据的高要求：EDGE-GRPO的训练流程要求每个问题都配有高质量的参考解，这在现实世界的数据收集中可能是一个瓶颈。相比之下，一些无监督或弱监督的RL方法对数据的要求更低。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 当然，这篇论文充满了可以借鉴的智慧。\n重点学习与启发： “实用主义”的混合范式：最大的启发是不要拘泥于单一的技术范式。EDGE-GRPO完美地将监督学习（通过GEC注入正确答案）和强化学习（通过EDA优化策略）结合起来。当RL陷入困境时，不要犹豫使用更直接、更强的监督信号来“破局”。这种务实的混合思想在解决实际问题时非常强大。 “自上而下”的问题分解：作者将“训练效率低”这个大问题，清晰地分解为“响应层面”和“信号层面”两个子问题，并分别用GEC和EDA这两个正交的模块来解决。这种将复杂问题分解、分而治之的工程和研究思路值得学习。 从“模型行为学”中寻找信号：EDA的灵感来源于对模型“自信度错配”这一行为的细致观察。这启发我们，在设计算法时，可以更多地去分析模型的“行为特征”（比如不确定性、生成速度、注意力分布等），并尝试将这些特征转化为有价值的训练信号。 数据效率是核心竞争力：在算力成本日益高昂的今天，能够以1K数据量就达到或超过别人用数十K数据量才能达到的效果，这本身就是一种降维打击。在构思自己的项目时，应始终将“如何提升数据和计算效率”放在重要位置。 需要补充的背景知识： 强化学习基础：特别是策略梯度（Policy Gradient）方法，理解“策略（Policy）”、“奖励（Reward）”、“优势函数（Advantage Function）”等核心概念。 PPO算法 (Proximal Policy Optimization)：这是当前LLM强化学习中最主流的算法之一，GRPO是其变体。理解PPO的裁切（clipping）目标函数和KL散度惩罚项，有助于理解GRPO的动机。 GRPO算法 (Group Relative Policy Optimization)：需要直接阅读GRPO的原始论文（Shao et al. 2024），理解它如何通过组内排序来替代PPO中的值函数。 信息熵与策略熵：理解信息熵的基本概念，以及它在语言模型中如何体现为对下一个词选择的不确定性。 希望这份详尽的解读能帮助您全面、深入地理解这篇论文的精髓。它确实是一项在LLM强化学习领域中，兼具理论洞察和实践价值的杰出工作。\n","date":"2025-08-19T00:00:00Z","permalink":"/edge-grpoentropy-driven-grpo-with-guided-error-correction-for-advantage-diversity/","title":"EDGE-GRPO：Entropy-Driven GRPO with Guided Error Correction for Advantage Diversity"},{"content":"依旧是利用熵，也是在熵高的位置分叉，类似AGENTIC REINFORCED POLICY OPTIMIZATION和CURE：Critical-Token-Guided Re-concatenation for Entropy-collapse Prevention。从而共享路径，减少计算。此外基于熵进行了优势函数重塑。根据熵来调整优势值。TTRL：Test-Time Reinforcement Learning\nimage.png 这篇论文聚焦于大语言模型（LLM）领域中一个极具前瞻性且充满挑战的方向：测试时强化学习（Test-Time Reinforcement Learning, TTRL）。传统的大模型训练范式，如监督微调（SFT）或人类反馈强化学习（RLHF），通常需要一个庞大、昂贵的静态数据集，在“训练阶段”完成后，模型便被固化并部署。然而，TTRL试图打破这一壁垒，它让模型在面对一个全新的、无标签的任务（即“测试时”）时，能够通过自我生成、自我评估和自我优化的方式，动态地提升自身性能。这好比一个学生在考场上遇到难题时，不依赖老师，而是通过自己打草稿、多方推演、比较验证，最终找到正确解法。这赋予了模型一种“终身学习”或“即时学习”的能力，对于推动AI的自主性和适应性具有里程碑式的意义。\n然而，正如论文开篇所指出的，现有的TTRL框架存在两大核心痛点。第一是高昂的推理成本。为了通过“少数服从多数”的投票机制（Majority Voting）生成一个可靠的伪标签（pseudo-label）作为学习目标，模型需要进行海量的并行“推演”（rollouts），这带来了巨大的计算开销。第二是早期的估计偏差。在学习初期，模型能力有限，投票产生的“共识”很可能是错误的。但此时模型会对此“错误的共识”产生过度自信（overconfidence），并迅速强化这个错误方向，从而陷入局部最优，难以取得实质性进步。\n为了攻克这两个难题，作者们提出了一个名为ETTRL的创新框架，其核心是精妙地运用了“熵（Entropy）”这一信息论概念。熵在这里可以通俗地理解为模型在生成下一个词元（token）时的“不确定性”或“犹豫程度”。ETTRL框架包含两大核心组件，分别对症下药：\n熵引导的树状多数票推演（Entropy-fork Tree Majority Rollout, ETMR）：这是为了解决“高成本”问题。传统的TTRL就像让100个学生从头到尾完整地做一遍题，成本很高。ETMR则聪明得多，它让一个“主思路”先走，当遇到一个“高熵”的节点——即模型很“犹豫”，觉得有好几种写法都有可能（例如，在数学证明中，是使用“因此”、“然而”还是“不妨设”）——它就在这里“分叉”（fork），衍生出新的推理路径。而对于那些模型非常确定的“低熵”部分（例如，“1+1=”后面几乎必然是“2”），则所有路径共享，无需重复计算。这种方式极大地提升了生成多样性回答的效率。论文中的数据显示，ETMR能用相当于基线方法60%的计算资源（token budget），达到甚至超越原有的性能。\n基于熵的优势函数重塑（Entropy-based Advantage Reshaping, EAR）：这是为了解决“过度自信”问题。在强化学习中，“优势函数（Advantage）”衡量了一个行为比平均水平好多少，直接指导模型的优化方向。在TTRL早期，某个碰巧达成“共识”的错误答案可能会获得很高的优势值，导致模型被误导。EAR机制的核心思想是：不能只看结果，还要看过程中的“信心”。具体而言，如果一个回答虽然符合了多数票，但其生成过程的平均熵很高（即模型一路磕磕绊绊，非常不确定），那么EAR就会调低它的优势值，相当于告诫模型：“你这个答案虽然蒙对了，但你心里没底，别学得太快。”反之，如果一个回答既符合多数票，生成过程又非常流畅（低熵），EAR则会放大其优势值。通过这种方式，EAR有效地抑制了模型对低质量伪标签的过拟合，鼓励模型在真正“胸有成竹”的方向上深入探索。\n论文通过在AIME 2024、AMC等多个高难度数学推理基准上的详尽实验，系统地验证了ETTRL的有效性。实验结果令人印象深刻，例如，在最具挑战性的AIME 2024上，ETTRL帮助Llama-3.1-8B模型实现了高达68%的相对性能提升，这在竞争激烈的LLM评测中是一个巨大的飞跃。这些坚实的数据不仅证明了ETTRL框架的优越性，也为我们展示了如何通过精巧的机制设计，在探索（Exploration）和利用（Exploitation）这一强化学习的经典困境中找到更优的平衡点。\n总而言之，这篇论文不仅仅是提出了一种优化算法，更是一种思想上的启迪。它告诉我们，模型在生成内容时所展现的“不确定性”（熵）本身就是一种宝贵的信息资源，可以被用来指导模型的学习过程，使其变得更高效、更稳健。ETTRL为实现更自主、更经济的AI自我完善提供了一条清晰且有效的技术路径。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解和阐述。\n一、 研究目标与现实意义 研究目标是什么？ 论文的核心研究目标是优化大语言模型（LLM）的测试时强化学习（TTRL）框架，使其在没有人工标注数据的情况下，能够更高效、更鲁棒地进行自我提升。\n想要解决什么实际问题？ 它旨在解决当前TTRL方法面临的两个核心瓶颈：\n效率问题 (High inference budget)：标准的TTRL为了通过“多数票决”生成可靠的伪标签，需要进行大量并行的完整序列生成（rollouts），这导致极高的计算成本和时间延迟，限制了其在实际场景中的应用。 鲁棒性问题 (Early-stage estimation bias)：在学习初期，模型的认知水平有限，通过投票产生的“共识”很可能是错误的。然而，模型会对此产生“过早的过度自信”（premature overconfidence），从而被误导，陷入局部最优解，无法实现持续的性能提升。 \u0026gt; 正如论文中所述： \u0026gt; High inference budget. TTRL must perform tens to hundreds of rollouts to obtain a reliable pseudo-label. Early-stage estimation bias. During the early iterations, the pseudo-label is often incorrect, yet the model may quickly overfit to it with greater advantage. 这个问题对于行业发展有什么重要意义? 解决这个问题意义重大，它关乎到AI能否实现真正的自主学习和持续进化。\n降低AI的“培养”成本：当前主流的LLM训练（如SFT和RLHF）极度依赖昂贵的人工标注数据。TTRL提供了一种“无监督”的自提升路径，如果能解决其效率和鲁棒性问题，将极大降低模型迭代和适应新领域的成本。 实现真正的“终身学习”：让模型在部署后，能根据遇到的新问题不断自我完善，而不是一次训练定终身。这对于需要处理动态、开放领域任务的AI应用（如科学研究、复杂编程、个性化助教等）至关重要。 推动AI能力的边界：在一些人类也难以提供高质量标注的前沿领域（如高能物理、药物研发），让AI通过自我探索和推理来提升能力，可能是突破当前认知瓶颈的关键。 二、 核心方法与创新优势 论文提出的核心方法是ETTRL (Entropy-based Test-Time Reinforcement Learning) 框架，它由两个关键组件构成：\n熵引导的树状多数票推演 (Entropy-fork Tree Majority Rollout, ETMR) 思路：传统方法是生成N个完全独立的答案，ETMR则认为答案的大部分内容是相似且低不确定性的（低熵），可以被复用。真正的多样性来源于少数几个关键的、高不确定性的（高熵）决策点。 实现细节： 识别分叉点：在生成过程中，模型会计算每个词元（token）的香农熵（Shannon Entropy）。熵值最高的K个词元被识别为“分叉点”（fork points）。这些点通常是逻辑连接词、关键术语等，代表了推理路径可能发生变化的地方。 树状生长：从一个初始路径开始，遇到分叉点时，不是只选择概率最高的一个词元，而是采样多个高概率的候选词元，每个候选词元都生长出一条新的路径分支。 共享前缀：在到达分叉点之前的所有词元，被所有后续分支共享，从而避免了重复计算。 特点与优势： 高效率：相比于完全并行的采样，ETMR通过复用低熵前缀，显著降低了生成同样数量候选答案所需的总词元数。论文给出了一个明确的数学模型来量化这种效率提升，在典型配置下（N=3, B=2），token消耗仅为并行采样的60%。 \u0026gt; 论文中的简化公式清晰地展示了这一点： \u0026gt; \\[ \u0026gt; Simplified: TR_{tree} = \\frac{(1 + 0.5 * B * N)}{1 + B * N} \u0026gt; \\] 高质量多样性：它将探索的重点聚焦于最关键的决策点，因此生成的多样性更具“意义”，而不是在无关紧要的细节上做无谓的变动。 基于熵的优势函数重塑 (Entropy-based Advantage Reshaping, EAR) 思路：在强化学习中，不能仅仅根据一个行为的结果好坏（是否符合多数票）来给予奖励，还应该考虑执行该行为时的“信心”或“确定性”。 实现细节： 计算相对熵：对于每一个生成的完整回答，计算其所有词元的平均熵H_resp(o)。然后，计算这个回答的熵与当前批次所有回答的平均熵avg(H_resp)的差异，得到一个相对熵。 重塑优势函数：论文设计了一个缩放系数γ_i，该系数与相对熵负相关。 如果一个回答的熵高于平均值（模型很“犹豫”），γ_i会小于1，从而惩罚（降低）其优势值。 如果一个回答的熵低于平均值（模型很“自信”），γ_i会大于1，从而奖励（提升）其优势值。 \u0026gt; 核心公式如下： \u0026gt; \\[ \u0026gt; \\gamma_i = 1 + (\\text{avg}(H_{resp}(o_i)) - H_{resp}(o_i)) / \\text{avg}(H_{resp}(o_i)) \u0026gt; \\] \u0026gt; \\[ \u0026gt; \\hat{A}\u0026#39;_{i,t} = \\text{clip}(\\gamma_i, 1-\\alpha, 1+\\beta) * \\hat{A}_{i,t} \u0026gt; \\] 特点与优势： 抑制过拟合：有效防止模型在学习初期对偶然达成的、质量不高的“共识”进行过度学习，提高了训练的稳定性。 平衡探索与利用：当模型对某个方向非常自信（低熵）时，鼓励其深入“利用”；当模型表现出不确定性（高熵）时，则鼓励其继续“探索”其他可能性，而不是过早地收敛。 三、 实验设计与结果分析 论文通过一系列严谨的实验来验证ETTRL的有效性。\n实验设计：\n模型选择：涵盖了不同规模和架构的模型，包括专门为数学优化的 Qwen2.5-Math-1.5B，以及通用模型 Qwen2.5-Base-3B 和 Llama-3.1-8B，以验证方法的普适性。 评测基准：选用了三个公认的、具有挑战性的数学推理基准：AIME 2024, AMC, 和 MATH-500。 基线方法：将提出的ETMR和EAR分别与原始的TTRL框架进行对比。在EAR的实验中，还引入了另一种简单的正则化方法Adv-Clip（直接对优势值进行裁剪）作为参照。 评价指标：采用Pass@1，即模型在贪心解码（greedy decoding）下一次性生成正确答案的概率，这是衡量LLM推理能力的核心指标。 实验数据与结果： 实验结果有力地证明了ETTRL两个组件的有效性。\nETMR的效率与性能 (对比TTRL, 见论文Table 1): ETMR在消耗更少计算资源（约60%的rollout tokens）的情况下，在多数任务上取得了比TTRL更好的性能。 关键数据：在最具挑战性的AIME 2024数据集上，Llama-3.1-8B模型使用ETMR后，Pass@1得分从10.0%提升至16.9%，相对提升了69.0%。这表明ETMR带来的高效探索对于攻克难题至关重要。 模型 方法 AIME 2024 AMC MATH-500 平均 Llama-3.1-8B TTRL 10.0 32.3 63.7 35.3 Llama-3.1-8B ETMR 16.9 35.4 59.5 37.3 Δ ↑69.0% ↑9.6% ↓6.6% ↑5.7% EAR的鲁棒性与性能 (对比TTRL和Adv-Clip, 见论文Table 2): EAR (在论文中记为 Adv-Res) 在所有模型和数据集上的表现几乎都优于基线TTRL和简单的Adv-Clip。 关键数据：在AIME 2024上，Qwen2.5-Base-3B使用EAR后，Pass@1从7.9%提升至13.1%，相对提升65.8%。这说明EAR对于那些非数学专用、不确定性更高的模型，改善效果尤为明显。 模型 方法 AIME 2024 AMC MATH-500 平均 Qwen2.5-Base-3B TTRL 7.9 40.7 72.2 40.3 Qwen2.5-Base-3B Adv-Clip 10.0 42.0 71.3 41.1 Qwen2.5-Base-3B Adv-Res (EAR) 13.1 41.4 72.4 42.3 Δ (EAR vs TTRL) ↑65.8% ↑1.7% ↑0.3% ↑5.0% 四、 未来展望与潜在机遇 结合当前大模型领域的理解，该研究方向未来潜力巨大。\n值得探索的问题与挑战： 面向通用领域的扩展：目前实验集中在有明确答案的数学领域。如何将ETTRL的思想应用于开放式、创造性或主观性强的任务（如长文写作、代码生成、艺术设计）是一个巨大的挑战。在这些领域，“多数票”可能导向平庸而非卓越，需要设计更复杂的奖励机制。 工程化与性能优化：论文坦言，ETMR在理论上节省了token，但由于现有深度学习框架对这种树状、异步的计算模式支持不佳，实际的“墙上时间”（wall-clock time）加速有限。研发原生支持混合执行模式的RL训练框架是一个重要的工程挑战。 超参数自适应：论文提到方法对温度系数、缩放因子等超参数较为敏感。开发自适应的超参数调整机制，让模型能根据任务难度和自身学习阶段动态调整探索力度，是提升方法易用性和泛化性的关键。 与其他自提升方法的融合：如何将基于熵的内在不确定性信号，与模型自我反思、自我批判（self-critique）等其他内在奖励信号相结合，可能产生1+1\u0026gt;2的效果。 可能催生的新技术和投资机会： “自进化”AI服务：可以催生出新型的AI产品，它们能够在部署后，根据用户特定领域的数据和交互，进行低成本的“在线进化”，提供高度定制化和持续优化的服务。 下一代AI训练平台：专注于LLM推理时优化和持续学习的新型AI基础设施和MaaS（Model-as-a-Service）平台。这些平台将内置ETTRL这类高效的自适应学习算法，成为其核心竞争力。 垂直领域“专家模型”孵化器：利用TTRL技术，可以快速、低成本地将一个通用大模型，适配成解决特定行业问题（如法律、金融、医疗）的专家模型，这将是一个巨大的市场。 五、 批判性审视与潜在局限 从批判的视角看，这篇论文虽然出色，但仍存在一些值得商榷和深入探讨之处。\n存在哪些不足及缺失？ 泛化性验证不足：如前所述，实验完全局限于数学推理。这使得论文的结论在多大程度上能推广到其他领域（尤其是非数理领域）成为一个开放性问题。在数学领域，答案的正确性是客观且唯一的，这大大简化了“多数票决”的有效性。 现实世界效率的讨论不够深入：论文在“局限性”一节中承认了理论节省与实际加速之间的差距，但未能提供更深入的性能分析，例如在不同硬件配置下的实际延迟、吞吐量等数据。这使得对其在真实生产环境中的经济性评估变得困难。 对“共识”本身的探讨较少：方法的核心依赖于“多数票”产生的伪标签。但当所有路径都陷入一个共同的“思维陷阱”时，这种共识反而是有害的。论文对如何识别和打破这种“集体性错误”着墨不多。 有哪些需要进一步验证和存疑的？ 对模型规模的敏感性：实验使用的模型最大为8B。对于规模远大于此的巨型模型（如100B以上），它们本身的推理能力和内在不确定性分布可能完全不同。ETTRL在这些模型上的效果如何，是否依然显著，需要进一步验证。 超参数的普适性：论文承认超参数是根据数据集和模型调整的。这引发了一个疑问：这些超参数的最优值是否存在某种规律？还是说每次应用于新任务时，都需要进行一轮成本高昂的搜索？这关系到该方法的实用价值。 与模型校准（Calibration）的关系：熵直接关联到模型的不确定性。一个校准得很好的模型，其输出的概率（和熵）能真实反映其置信度。ETTRL的有效性在多大程度上依赖于基础模型的校准水平？这是一个值得探究的有趣问题。 六、 核心启示与知识补充 对于希望从中汲取创新想法的您来说，这篇论文提供了极其宝贵的启示。\n应该从这篇论文中重点学什么？ 将“不确定性”视为一种可利用的资源：这篇论文最核心的洞见是，不要把模型的“犹豫”（高熵）看作一个纯粹的负面因素。相反，它是指导探索、平衡学习、提升效率的宝贵信号。在您自己的项目中，可以思考如何量化和利用模型在决策过程中的不确定性。 从“原子操作”走向“结构化”操作：ETMR的思路是从并行的“独立尝试”升级为有组织的“树状探索”。这启示我们，在优化复杂的生成任务时，应该思考如何在过程中复用公共组件，并将计算资源集中在最关键的决策点上，实现结构化的效率提升。 动态调整学习信号的强度：EAR的思想是根据过程的“信心”来动态调整结果的“权重”。这是一种非常精巧的反馈机制。在任何需要迭代优化的系统中，都可以借鉴这种思想，即根据反馈信号的“质量”或“可信度”，来决定本次更新的步长和影响。 有哪些启发？ 创新源于交叉：将信息论中的“熵”与强化学习中的“优势函数”和“采样策略”相结合，产生了意想不到的良好效果。 问题即是答案：TTRL的“高成本”和“不鲁棒”这两个问题，最终都通过深入分析模型自身的“熵”状态得到了缓解。很多时候，解决问题的线索就隐藏在问题本身之中。 需要补充了解哪些背景知识? 为了更透彻地理解本文，建议您补充以下背景知识： 强化学习基础 (Reinforcement Learning)：特别是策略梯度（Policy Gradient）方法，以及优势函数（Advantage Function）的概念。了解PPO（Proximal Policy Optimization）算法会对理解论文中提到的GRPO（Group Relative Policy Optimization）有很大帮助。 信息论基础：主要是香农熵（Shannon Entropy）的定义和含义，理解它如何量化一个概率分布的不确定性。 LLM中的推理技术：了解贪心解码（Greedy Decoding）、束搜索（Beam Search）、Top-K/Top-p采样等不同的文本生成策略。 自监督/无监督学习思想：了解伪标签（Pseudo-Labeling）和一致性正则化（Consistency Regularization）等概念，这些是TTRL思想的根基。 前置关键论文：可以阅读一下本文引用的核心基线工作，如 TTRL (Zuo et al., 2025) 和 TreeRL (Hou et al., 2025)，以更好地理解其改进的出发点。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得丰富的启发。\n","date":"2025-08-19T00:00:00Z","permalink":"/ettrlbalancing-exploration-and-exploitation-in-llm-test-time-reinforcement-learning-via-entropy-mechanism/","title":"ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM"},{"content":"context engineering的论文，这篇主要涉及工具管理。设计了两个元工具，可以用来添加或者删除上下文的工具。属于上下文工程中的上下文选取，选取最相关的工具。\n这篇论文发表于2025年7月29日，精准地切入了当前LLM Agent（大语言模型智能体）领域一个非常棘手且关键的工程和研究难题：动态工具调用的上下文管理。随着Agent能力的增强，它们不再局限于使用一小组预设的工具，而是能够像人类逛应用商店一样，动态地发现、加载并使用成千上万的工具。这带来了前所未有的灵活性，但同时也引发了一个致命问题：LLM的“工作记忆”（即上下文窗口）是有限的。如果不加管理，Agent在与用户进行多轮对话时，其上下文中会堆积大量不再需要的工具，迅速耗尽宝贵的上下文空间，导致性能下降、成本飙升，甚至任务失败。这篇论文将这种现象类比为计算机的RAM（随机存取存储器）被占满，非常形象。\n论文的核心贡献在于提出了一个名为 MemTool 的框架，旨在优化LLM Agent在多轮对话中的短期记忆，特别是对工具的动态管理。它不把记忆管理看作一个隐性的、由模型自行处理的模糊任务，而是将其提升为一个显式的、可设计的核心模块。MemTool创新性地设计了三种具有不同“自主性”水平的架构，来应对这一挑战：\n自治Agent模式 (Autonomous Agent Mode): 这是最“激进”的模式，赋予了LLM Agent完全的自主权。Agent被提供了两个特殊的“元工具”：Search_Tools（搜索并添加新工具）和Remove_Tools（移除不再需要的工具）。Agent需要根据对话的进展，自行推理判断何时该“清理内存”、何时该“安装新应用”。这非常考验LLM本身的推理能力。实验结果也验证了这一点，论文在Table 1中指出，像GPT-03这样的顶级推理模型，其“3轮平均工具移除率”（Avg Removal Ratio 3T）高达94.1%，能够有效地维持一个干净的工具集。然而，许多中小型模型，如LLaMA 3 70B和Claude 3.5 Sonnet，则完全无法胜任，它们的移除率分别只有24.4%和6.2%。从Figure 2的“工具数量变化图”中可以清晰地看到，后者的工具数量在20轮对话内就迅速攀升至128个的API上限，导致Agent“崩溃”。\n工作流模式 (Workflow Mode): 这个模式采取了完全相反的思路，它将工具管理任务从Agent的核心逻辑中剥离出来，变成一个确定性的、两步走的“预处理”流程。在Agent响应用户之前，系统会先进行两次独立的LLM调用：第一次调用专门负责识别并“剪除”（prune）当前上下文中与新问题无关的工具；第二次调用则负责为新问题搜索并添加合适的工具。这种“先剪除，后搜索”（prune-then-search）的模式，极大地降低了对Agent自主推理的要求。其优势是稳定可靠。如Figure 3所示，在这种模式下，几乎所有参与测试的13个模型都能将工具数量稳定地控制在低位，避免了上下文溢出的问题。例如，之前在自治模式下表现很差的GPT-4.1 Nano，其移除率也达到了90.4%。这证明了将复杂任务分解可以有效提升系统的整体鲁棒性。\n混合模式 (Hybrid Mode): 这是对前两种模式的折衷与融合，堪称“两全其美”的实用主义方案。它借鉴了工作流模式的可靠性，使用一个确定性的LLM调用来移除无关工具。但同时，它又保留了自治模式的灵活性，Agent自身仍然持有Search_Tools工具，可以在需要时自主地添加新工具。论文指出，这种设计基于一个敏锐的观察：“LLMs tend to struggle with tool removal… but perform well in searching and adding tools”（LLM通常不擅长移除工具，但在搜索和添加工具方面表现良好）。混合模式将模型不擅长的任务（移除）外部化、自动化，而保留其擅长的任务（添加）的自主权。实验结果表明，这种模式在保持高工具移除率的同时，也实现了很高的“任务完成度”（Task Completion），在多个模型上甚至超过了纯自治模式。\n总而言之，MemTool通过这三种模式的提出与评估，为构建长时程、多回合、工具丰富的LLM Agent提供了一套极具价值的设计蓝图和性能参照。它不仅清晰地定义了问题，还通过严谨的实验量化了不同策略的优劣，为开发者在Agent的“自主性”与“可靠性”之间进行权衡提供了坚实的数据支持。\n接下来，我将按照您提出的六个方面，对这篇论文进行更详细的解读。\n1. 论文的研究目标与意义 研究目标: 本文的核心研究目标是为LLM Agent（大语言模型智能体）开发并评估一个优化其短期工具记忆（short-term tool memory）管理的框架。具体来说，它要解决Agent在进行多轮对话（multi-turn conversations）时，如何高效地、动态地管理其上下文窗口中可调用的工具集的问题。\n解决的实际问题: 随着技术发展，LLM Agent被赋予了动态工具发现（dynamic tool discovery）的能力，可以从庞大的工具库（论文中实验使用了包含5000个工具的ScaleMCP基准）中按需加载工具。这带来一个严峻的实际问题： \u0026gt; However, fixed context windows limit effectiveness in multi-turn interactions requiring repeated, independent tool usage. \u0026gt; （然而，固定的上下文窗口限制了需要重复、独立使用工具的多轮交互的有效性。） 在长时间的对话中，Agent会不断添加新工具，但很少会主动移除那些已经用不到的旧工具。这会导致“上下文膨胀”（context bloat），迅速填满模型的上下文窗口（如OpenAI API限制128个工具）。一旦达到上限，Agent就无法加载新工具，也可能因为上下文中充满了无关噪声而导致推理能力下降，最终任务失败。\n对行业发展的重要意义:\n推动Agent从“玩具”走向“生产”: 当前多数Agent应用还停留在单轮或短对话的“演示”阶段。要构建能够处理复杂、长周期任务（如个人助理、持续性项目管理、自动化科研）的生产级Agent，一个稳定、高效的记忆管理系统是不可或缺的基础设施。MemTool是向这个方向迈出的关键一步。 定义了Agent设计的核心权衡: 论文通过三种模式的对比，清晰地揭示了Agent设计中 “自主性”（Autonomy） 与 “可靠性”（Reliability） 之间的核心权衡。这为行业内的Agent架构设计提供了重要的理论指导和实践范本。 为Agent的可扩展性奠定基础: 一个无法有效管理自身“内存”的系统是无法扩展的。MemTool所探索的机制，是未来构建能够管理数百万工具、进行数千轮对话的超级Agent的基石。 2. 论文提出的新思路与方法 核心思路: 将LLM Agent的工具管理问题从一个模糊的、依赖模型“自觉性”的任务，转变为一个显式的、可设计的工程问题。论文首次系统性地提出了“短期工具记忆管理”这一概念，并围绕它构建了具体的解决方案。\n方法与模型: 论文提出了MemTool框架及其三种架构，这是其核心的方法论创新。\n模式 工具移除方式 工具添加方式 特点与优势 劣势 自治模式 (Autonomous) Agent自主调用 Remove_Tools Agent自主调用 Search_Tools 高度灵活，Agent可自我纠正和探索 可靠性差，严重依赖LLM的推理能力，易失败 工作流模式 (Workflow) 确定的LLM调用（Prune） 确定的LLM调用（Search） 高度可靠，稳定可控，适用于所有模型 灵活性差，一旦工具选择失误，无法补救 混合模式 (Hybrid) 确定的LLM调用（Prune） Agent自主调用 Search_Tools 平衡可靠与灵活，取长补短，实用性强 仍需Agent具备一定的自主搜索能力 与之前方法的对比与优势: 超越了单纯的上下文压缩: 之前的研究更多关注于通过摘要（summarization）或截断（truncation）来管理对话历史，以节省空间。 \u0026gt; However, these methods apply primarily to conversational tasks… and do not address the dynamic retrieval and management of tools within an agent’s context window. \u0026gt; （然而，这些方法主要适用于对话任务……并未解决Agent上下文窗口中工具的动态检索和管理问题。） MemTool则精准地聚焦于“工具”这一特定类型的上下文，并提供了更精细化的“添加/移除”操作，而非粗暴的压缩。 解决了多轮对话中的状态持久化: 许多现有的工具检索方法（如RAG）主要关注单次检索的准确性。MemTool的创新之处在于它考虑了跨轮次（multi-turn）的工具集持久化和演变问题，这是一个更复杂也更现实的场景。 提供了明确的架构选择: 与其他提出单一解决方案的研究不同，MemTool提供了三种可供选择的架构。这本身就是一种优势，因为它承认了不存在“银弹”，最佳方案取决于具体的模型能力、成本预算和任务需求。这使得论文的结论更具普遍指导意义。 3. 实验设计与结果分析 实验设计: 基准 (Dataset): 使用 ScaleMCP 基准，它包含5,000个工具（或称MCP服务器），模拟了一个庞大而真实的工具生态。 任务 (Task): 设计了一个包含100轮连续对话的测试流程。这足以模拟一个长时程的用户会话，并暴露出上下文管理的问题。 模型 (Models): 广泛评估了 13个 不同的LLM，覆盖了从顶级的闭源模型（如GPT-03, Gemini 2.5 Pro, Claude系列）到主流的开源模型（LLaMA 3 70B），以及不同尺寸的模型（如GPT-4.1 Mini/Nano）。这使得实验结论非常全面且有说服力。 评估指标 (Metrics): 除了传统的“任务完成度”（Task Completion）和“工具调用正确率”（Tool Correctness），论文还创新性地提出了两个关键指标来衡量记忆管理效率： Avg Removal Ratio 3T (3轮平均移除率): 衡量在工具被添加后的3轮对话内，有多大比例被成功移除。这个指标直接反映了“内存清理”的效率。 Avg Residual 3T (3轮平均残留量): 衡量3轮对话后，平均还剩下多少工具。这个指标反映了“内存泄漏”的严重程度。 实验数据与结果: 实验结果清晰地展示了不同模式的性能差异。 自治模式的“能力鸿沟”: Table 1的数据是关键证据。 强者恒强: GPT-03的Avg Removal Ratio 3T为0.941，Avg Residual 3T仅为7.44，同时Task Completion高达0.90。这表明顶级模型有能力同时处理好任务和自我管理。 弱者崩溃: 相比之下，Claude 3.5 Sonnet的移除率仅为0.062，残留工具数高达124.00，任务完成度也只有0.59。这生动地展示了让不具备足够推理能力的模型进行自我管理的风险。 工作流模式的“普适高效”: \u0026gt; Workflow Mode yielded consistently high performance across nearly all tested LLM models. The average 3-window removal ratio remained above 90% for all models. \u0026gt; （工作流模式在几乎所有测试的LLM模型上都表现出一致的高性能。所有模型的3窗口平均移除率都保持在90%以上。） 例如，GPT-03在该模式下的Tool Correctness达到了全场最高的88%，证明了确定性流程对提升执行精度的帮助。 混合模式的“最佳平衡”: 混合模式普遍实现了与工作流模式相近的高移除率（大多在90%以上），同时在任务完成度上紧追甚至在某些模型上超越了自治模式。例如，Claude 3.5 Sonnet在混合模式下的任务完成度从自治的0.59提升到了0.83，这是一个巨大的飞跃。 4. 未来探索方向与机遇 值得探索的问题与挑战: 长短期记忆的融合: 本文专注于会话内的“短期记忆”。一个巨大的挑战是如何将其与跨会话的“长期记忆”框架（如论文中提到的Mem0, Zep, Letta）无缝集成。例如，Agent如何记住用户在几周前偏好使用某个特定工具？ 更智能的移除策略: 当前的移除判断相对简单（“是否与当前查询相关”）。未来的研究可以探索更复杂的策略，比如预测用户未来的意图，避免过早地移除可能很快会再次用到的工具。 成本与延迟优化: 工作流和混合模式引入了额外的LLM调用，增加了成本和延迟。研究如何使用更小、更快的模型，甚至非LLM方法来执行“剪除”任务，将是重要的优化方向。 提升模型内在的“清理”能力: 自治模式的失败揭示了当前LLM在“认知清理”或“状态管理”方面的根本缺陷。通过特定的指令微调（Instruction Tuning）或提示工程（Prompt Engineering）来专门提升模型的这项能力，是一个基础性的研究方向。 技术与投资机会: Agent中间件/操作系统: 未来的Agent应用不会从零开始构建。市场需要成熟的Agent开发框架和中间件，而高效的记忆管理模块将是这类产品的核心卖点。专注于此的公司（可以称之为“Agent的内存条厂商”）将有巨大机会。 Agent可观测性与调试工具: 随着Agent逻辑越来越复杂，诊断其“为什么会忘记”或“为什么内存溢出”将变得至关重要。提供Agent记忆和状态的可视化、监控和调试服务的工具，将成为一个新兴的蓝海市场。 模型即服务（MaaS）的新维度: 模型提供商（如OpenAI, Google）未来可能会提供内置高级记忆管理能力的新版API，将其作为高级功能收费，这可能成为新的商业增长点。 5. 论文的不足与思考 从批判的视角看，尽管这篇论文非常出色，但仍存在一些可以深入探讨的方面：\n成本分析的缺失: 论文详细分析了性能，但没有对不同模式的经济成本（token消耗）和时间成本（延迟）进行量化比较。在生产环境中，工作流模式可能因为多一次LLM调用而导致延迟增加，这在某些实时性要求高的场景中是不可接受的。 “相关性”定义的模糊性: 论文中移除工具的依据是“与当前用户查询是否相关”。这个“相关性”是由另一个LLM来判断的，其标准是模糊的。一个工具可能与当前问题表面无关，但对用户潜在的、未明确提出的后续问题至关重要。对这种深层意图的判断，论文没有展开讨论。 泛化能力的验证: 实验完全基于ScaleMCP的金融工具集。这些模式在其他领域的表现如何，例如需要创意和上下文联想的写作助手，或者工具间依赖关系复杂的IT运维Agent？不同领域的工具使用模式可能截然不同，需要进一步的验证。 “裁判”模型的偏见: 任务完成度由GPT-4o-mini作为“裁判”来评估。虽然这是学术界的常用方法，但裁判自身的偏好和能力上限可能会影响评估的公正性。引入人类评估或其他模型作为交叉验证，会使结论更可信。 6. 论文的启发与实践 对于希望从中汲取创新想法并应用于实践的您来说，这篇论文提供了极其宝贵的启示：\n核心启发: 将记忆管理提升为一等公民: 在设计任何Agent系统时，必须有一个显式的记忆管理策略。不要再想当然地认为LLM能“自动搞定一切”。 分而治之，解耦复杂性: 工作流和混合模式的成功告诉我们，将一个复杂的、模糊的Agent任务（“回答问题并管好你的工具”）分解为一系列更小、更明确的子任务（“第一步：清理工具；第二步：添加工具；第三步：回答问题”），是提升系统稳定性的黄金法则。 架构匹配能力，不做“越级”设计: 这是最重要的一条实践准则。根据你所使用的LLM模型的能力来选择合适的架构。 追求最高可靠性/使用普通模型: 从工作流模式开始。 寻求灵活性与可靠性的平衡: 混合模式是你的最佳选择。 拥有顶级模型/进行前沿研究: 才考虑在自治模式中探索模型的极限。 需要补充的背景知识: RAG (Retrieval-Augmented Generation): 论文中的工具搜索本质上是一种针对工具的RAG。深入理解RAG的原理，包括向量数据库、嵌入模型、重排（reranking）等，是理解本文的基础。 Agentic Frameworks (智能体框架): 了解像 LangChain, LlamaIndex, AutoGen 等主流Agent框架。这些框架中已经包含了Agent、Tools、Memory等核心概念的实现，可以帮助您快速上手实践论文中的思想。 Function Calling / Tool Use API: 熟悉主流LLM提供商（如OpenAI, Google, Anthropic）的工具调用API。这是实现Agent与外部世界交互的底层技术，也是MemTool框架能够运行的基础。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-19T00:00:00Z","permalink":"/memtooloptimizing-short-term-memory-management-for-dynamic-tool-calling-in-llm-agent-multi-turn-conversations/","title":"MemTool：Optimizing Short-Term Memory Management for Dynamic Tool Calling in LLM Agent Multi-Turn Conversations"},{"content":"思想和Good Learners Think Their Thinking：Generative PRM Makes Large Reasoning Model More Efficient Math Learne非常相似，都是对step重新划分然后针对step计算奖励\nimage.png 论文深度解读 这篇论文的核心目标是解决大型推理模型（Large Reasoning Models, LRMs）在处理复杂任务时普遍存在的“过度思考”（Overthinking）问题。所谓“过度思考”，指的是模型在面对一些相对简单的问题时，仍然会生成过长、冗余的推理链（Chain-of-Thought, CoT），这不仅大大增加了计算成本和推理时间，有时甚至会因为引入不必要的干扰而降低准确率。这个问题已经成为制约LRMs在真实世界中大规模、经济地部署的关键障碍。\n论文开篇便一针见血地指出了问题的根源。现有的一些顶尖LRMs，如OpenAI的O1和DeepSeek-AI的DeepSeek-R1，普遍采用了一种名为“基于可验证奖励的强化学习”（Reinforcement Learning with Verifiable Rewards, RLVR）的训练范式。这种范式通常只在最终结果上给予奖励（例如，答案正确得1分，错误得0分）。这种机制虽然能有效提升模型的推理能力，但也无形中“鼓励”模型为了确保最终的正确性而“不惜一切代价”，从而倾向于生成 overly redundant（过度冗冗）而非 potentially wrong（可能错误）的内容。\n论文在引言中提到： \u0026gt; This flaw stems from the training mechanism of reinforcement learning with verifiable rewards (RLVR)—for instance, the binary reward function used in R1 encourages the model to over-generate content in the pursuit of correctness, fostering a preference for being overly redundant rather than potentially wrong.\n为了更精细地剖析这个问题，研究者们进行了一项案例研究。他们使用DeepScaleR和DS-Distill-1.5B模型对MATH-500数据集中的问题进行解答，并人工抽样分析了10%的答案。他们发现，推理过程通常可分为“问题重述”、“逐步解答”和“答案总结”三个阶段，而“过度思考”主要发生在“逐步解答”阶段。如下图2所示，模型为了解决一个简单的问题，在中间步骤进行了大量无效的自我怀疑和修正，最终甚至得出了错误答案，并消耗了高达8734个token。\nimage.png 图2：论文中展示DeepScaleR模型“过度思考”的实例，其中红色和橙色部分标记了不正确或无效的推理步骤。\n基于以上洞察，论文作者认为，解决“过度思考”的关键在于精确地区分有效和无效的推理步骤，并对前者进行鼓励，对后者进行惩罚。这正是本文最核心的创新思路。\n为了实现这一目标，论文提出了一种名为“可验证的步进式奖励机制”（Verifiable Stepwise Reward Mechanism, VSRM）。该机制的巧妙之处在于，它抛弃了传统方法中需要额外训练一个复杂且不稳定的“过程奖励模型”（Process Reward Model, PRM）的思路，转而设计了一套基于规则、可解释性强的步进式奖励计算方法。其具体实现分为两个关键环节：\n步骤分离 (Step Separation)：首先，VSRM通过一系列预定义的“特殊词元”（special tokens），如 “however”, “thus”, “so”, “but”, “wait” 等，将模型生成的完整推理链（main rollout）切分成多个子推理路径（sub-rollouts）。这些词元通常标志着一个推理阶段的结束和下一个阶段的开始。这种基于规则的方法简单、高效且具有很强的可解释性。\n步进式奖励分配 (Assigning Reward to Intermediate Stage)：这是VSRM的精髓所在。对于每一个切分点（即一个中间步骤），VSRM会将该点之前的所有推理内容作为新的prompt，让模型基于这个“不完整”的推理生成多个（例如N个）最终答案，并计算这些答案的平均准确率\\(A_i\\)。一个步骤的奖励\\(d_i\\)，就是它所对应的平均准确率\\(A_i\\)与上一步骤平均准确率\\(A_{i-1}\\)之间的差值。 \u0026gt; \u0026gt; \\(d_{i-1} = A_i - A_{i-1}\\)\n这意味着，如果一个推理步骤能够显著提升得出正确答案的概率，它就会获得正奖励；反之，如果一个步骤是冗余的、无效的（准确率没有变化），甚至是错误的（准确率下降），它就会得到零奖励或负奖励。这种设计将对最终结果的验证巧妙地转化为了对中间过程的监督，从而实现了“可验证的步进式奖励”。\n此外，为了解决某些关键步骤的价值无法立刻体现（即奖励稀疏）的问题，VSRM还引入了一种奖励衰减传播机制。如果某一步的准确率提升微不足道，系统会向前“看”几步（lookahead window），如果发现未来的某一步带来了显著的准确率提升，那么这个未来的奖励会以一个衰减的折扣率（discount factor）传播回当前的步骤。\n在实验验证部分，论文做得非常扎实。研究者们在DS-Distill-1.5B、DS-Distill-7B和DeepScaleR等多个不同规模的模型上，结合PPO和Reinforce++两种强化学习算法，应用了VSRM。他们在包括MATH-500, AIME24, AIME25, AMC23, Minerva和OlympiadBench在内的六个数学推理基准上进行了广泛测试。\n实验结果令人信服。从下方的Table 2数据摘要可以看出，VSRM在几乎所有基准上都实现了显著的输出长度压缩，同时保持甚至略微提升了原模型的推理性能（pass@1准确率）。例如，在DS-Distill-1.5B模型上，应用VSRM-PPO后，AIME24任务的输出长度从12605减少到7065（压缩44%），而pass@1准确率从28.8%提升到29.2%。在更困难的OlympiadBench上，长度从9249减少到4499（压缩51%），准确率则保持稳定。\n表2：论文核心实验结果，VSRM在大幅降低输出长度（Length↓）的同时，保持了模型性能（pass@1↑）。\n论文还通过消融实验（Ablation Study） 证明了VSRM中每个组件的必要性，并分析了VSRM对模型推理行为的影响。结果表明，VSRM不仅有效抑制了无效步骤，也没有损害模型探索多样化解题路径的能力（通过pass@k指标衡量）。\n总而言之，这篇论文从一个极具实际意义的问题出发，通过深入的现象分析定位了问题根源，并创新性地提出了一套可解释、易于实现且效果显著的VSRM机制。它不仅为解决大模型效率问题提供了一个即插即用的新范式，也为我们理解和优化复杂的AI推理过程提供了宝贵的启示。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n1. 论文的研究目标与实际意义 研究目标： 本文的核心研究目标是解决大型推理模型（LRMs）在推理过程中存在的“过度思考”（Overthinking） 现象。具体来说，就是模型为了追求最终答案的正确性，倾向于生成过长、冗余、计算成本高昂的推理链，尤其是在面对相对简单的问题时。\n想要解决的实际问题：\n降低推理成本：LLM的推理成本，特别是对于需要长篇推理的任务，与其生成的Token数量直接相关。“过度思考”会产生大量不必要的Token，直接导致更高的API调用费用和硬件资源消耗。 提升推理速度：生成更长的文本序列需要更多的时间，这在高并发、低延迟的应用场景（如实时助教、在线编程助手）中是致命的。解决“过度思考”可以显著缩短模型的响应时间。 提高模型的可靠性和部署可行性：一个行为更可预测、效率更高的模型，更容易被集成到实际的生产系统中。当前LRMs因其高昂的成本和缓慢的速度，其实际部署（practical deployment）受到了很大限制。 对行业发展的重要意义： 随着大模型技术从“能力”竞争转向“效率”和“成本”竞争，推理优化已成为行业发展的关键。这篇论文的研究直接关系到AI普惠化的进程。如果能让强大的推理模型以更低的成本、更快的速度运行，将极大地拓宽其应用边界，使其能够赋能更多中小企业和个人开发者，并深入到更多对成本和延迟敏感的行业（如教育、金融、客户服务等）。因此，这项研究对于推动大模型技术从实验室走向大规模产业应用具有至关重要的战略意义。\n2. 新的思路、方法与优势 新的思路： 本文最核心的新思路是从“结果导向”的奖励机制转向“过程导向”的细粒度奖励机制。它不再仅仅关注最终答案是否正确，而是深入到推理的每一步，评估该步骤对最终结果的“贡献度”，并依此进行即时奖惩。这是一种更加精细化的模型行为引导方式。\n新的方法：可验证的步进式奖励机制 (VSRM)： VSRM是实现上述思路的具体方法，其特点和优势主要体现在以下几个方面，均可在论文细节中找到支撑：\n可解释性强且无需额外模型： \u0026gt; 论文在“Make Stepwise Reward Verifiable”一节中提到： \u0026gt; \u0026gt; However, PRMs suffer from significant limitations… they are difficult to train and their reliability cannot be guaranteed… This dilemma motivates us to design a verifiable stepwise reward mechanism, which combines the advantages of stepwise rewards with the reliability and usability of verifiable outcome-based rewards. 与依赖于一个黑盒的、难以训练且不稳定的过程奖励模型（PRM） 的方法不同，VSRM通过基于规则的步骤切分和基于子路径采样验证的奖励计算，整个过程是透明且可解释的。开发者可以清晰地知道模型在哪一步因为什么原因被奖励或惩罚。 奖励信号与问题本质紧密对齐： 传统的长度惩罚（length penalties）方法是一种“一刀切”的策略，它可能会错误地惩罚那些虽然长但却十分必要的推理步骤。VSRM则更为智能，它惩罚的是无效步骤而非长步骤。其奖励信号直接与“该步骤能否提升最终解题准确率”这一核心目标挂钩，因此能更精准地引导模型进行高效推理。 动态适应，无需预设： 一些所谓的“自适应推理”（difficulty-adaptive reasoning）方法，通常需要预先评估问题的难度，然后分配一个固定的Token预算或推理模式。这种预估本身就很困难且不够灵活。VSRM则是在生成过程中动态地评估每一步的价值，完全不需要对问题进行预判，因此具有更好的灵活性和泛化能力。 通过奖励传播解决稀疏性问题： 论文中的公式 (3) 和 (4) 详细描述了其奖励传播机制。这是一个非常精巧的设计，解决了“某个步骤的贡献可能在多步之后才能体现”这一难题，使得奖励信号更加密集（dense），从而加速了模型的学习过程。 3. 实验设计与结果验证 实验设计： 为了全面验证VSRM的有效性，论文设计了严谨的实验： 基础模型：选择了三种具有代表性的开源模型：DS-Distill-1.5B, DS-Distill-7B (蒸馏模型) 和 DeepScaleR (经过强化学习的强模型)，覆盖了不同模型规模和训练背景。 对比方法：将VSRM的性能与多种最新的高效推理方法进行了比较，如 LC-R1, ThinkPrune, AdaptThink, Efficient-Reasoning, 和 GRPO-LEAD。 评估基准：使用了六个广泛认可的数学推理基准（MATH-500, AIME24/25, AMC23, Minerva, OlympiadBench），覆盖了从初中到奥林匹克竞赛级别的不同难度。 评估指标：主要关注两个核心指标：pass@1（单次采样生成答案的准确率，衡量性能）和Length（生成答案的平均Token数，衡量效率）。 消融研究：为了证明VSRM各个组件（如步进式奖励、奖励传播机制）的有效性，他们还设计了消融实验，即逐步移除某个组件，观察性能变化。 关键数据与结果： 实验结果有力地证明了VSRM的有效性。 性能与效率双赢：如前文图表所示，在DS-Distill-7B模型上，VSRM-PPO在AIME24任务中，将长度从10909压缩至6675（减少39%），同时pass@1从53.1%微降至51.8%，实现了效率和性能的极佳平衡。 对强模型同样有效：即使是对于已经经过深度优化的DeepScaleR模型，VSRM依然能在所有基准上压缩输出长度，同时基本维持其顶尖性能。这说明VSRM并非只对弱模型有效。 消融实验的证明：Table 3的数据显示，如果不使用奖励传播机制（VSRM-PPO w/o decay），模型的长度压缩效果会变差（例如MATH-500数据集上长度为2990，而完整版为2400），且性能略有下降。这证明了奖励传播机制对于提供密集有效的监督信号至关重要。 4. 未来探索方向与机遇 值得探索的问题和挑战： 跨领域泛化性：本文主要聚焦于数学推理，其步骤明确、对错分明。VSRM在更开放、模糊的任务（如法律文书撰写、代码生成、创意写作）上效果如何，是一个巨大的挑战。在这些领域，“正确的中间步骤”更难定义和验证。 步骤切分方法的自动化与鲁棒性：目前基于“特殊词元”的切分方法可能存在局限性，不同模型、不同语言的表达习惯各异。研究如何让模型自动学习推理步骤的边界，将是一个非常有价值的方向。 与模型架构的结合：是否可以将VSRM的思想与Mixture-of-Experts (MoE) 或思维树（Tree of Thoughts） 等更复杂的模型结构或推理范式结合，实现更动态、更高效的计算资源分配？ 训练成本与推理收益的权衡：VSRM在训练阶段需要为每个步骤采样多次，这无疑增加了训练的计算开销。如何在训练成本和推理收益之间找到最佳平衡点，是实际应用中需要考虑的问题。 可能催生的新技术和投资机会： AI模型“编译器”/优化器：未来可能会出现专门的AI公司，提供模型推理优化服务。他们可以利用类似VSRM的技术，对客户的预训练模型进行“二次精炼”，在不损失性能的前提下，大幅降低其推理成本，这在AI-as-a-Service领域将有巨大的商业价值。 高效推理框架与平台：可以预见，市场上将出现更多关注“Efficient AI”的工具链和平台，帮助开发者训练和部署更“经济”的模型。 新一代模型架构：对推理过程的细粒度理解，可能会启发新的模型架构设计，例如内置“快/慢思考”路径切换机制的模型，模型可以根据VSRM这类内部信号，自主决定在某一步骤上投入多少计算资源。这对于芯片设计和AI硬件加速也提出了新的机遇。 5. 论文的不足与待验证之处 从批判的视角来看，这篇论文虽然优秀，但仍存在一些可以深入探讨的方面：\n领域局限性：如前所述，论文的实验验证完全局限于数学推理。虽然这是一个很好的起点，但其结论能否推广到其他类型的推理任务（如逻辑推理、常识推理）甚至是自然语言生成任务，是一个巨大的疑问。数学问题的结构化特性为VSRM的成功提供了便利条件，但这恰恰可能是其泛化性的枷锁。 对“特殊词元”列表的依赖：VSRM的步骤切分依赖于一个预定义的词元列表。这个列表的完备性和适用性值得商榷。如果模型在训练后改变了其话语风格，或者在处理一个新领域的任务时使用了不同的过渡词，该方法的有效性可能会下降。论文中没有讨论这个列表是如何选择的，以及它对不同模型和任务的敏感度如何。 超参数敏感性分析的缺失：VSRM引入了几个新的超参数，如奖励传播的衰减因子γ、前看窗口L_max、以及子路径采样的数量N。论文中并未详细分析模型性能对这些超参数变化的敏感度，这对于复现和在其他场景应用该方法的研究者来说，可能会造成一定的困扰。 对推理多样性的潜在影响：尽管论文通过pass@k指标说明VSRM没有损害模型的探索能力，但一个强烈引导模型走向“最快正确路径”的奖励机制，长期来看，是否会抑制模型产生更有创意、更出人意料的解决方案？这需要更长期的、更定性的分析来验证。 6. 核心启发与知识补充 我应该从这篇论文中重点学什么？ 细粒度奖励工程（Granular Reward Engineering）的思维：最大的启发是从宏观的、一次性的奖励，转向微观的、过程性的奖励。在任何复杂的序列决策问题中，尝试分解任务，为中间状态设计合理的评估和奖励函数，往往能取得比只看最终结果更好的效果。这个思想可以应用在很多AI领域。 “验证”而非“建模”的智慧：当过程评估很困难时，一个巧妙的思路是绕过它。VSRM没有去硬磕一个PRM模型来“学习”什么是好的步骤，而是通过从中间步骤出发看它能产生多少好结果的“采样验证”方式，来间接评估该步骤的价值。这种“用结果验证过程”的思路非常值得借鉴。 问题诊断的重要性：论文的成功很大程度上源于其前期对“过度思考”现象的深入分析和精准定位。在做研究或项目时，花足够的时间去理解问题的本质、复现问题并找到其根源，往往比直接尝试各种解决方案更为重要。 需要补充了解的背景知识： 强化学习基础：特别是应用于大模型的强化学习，如 RLHF (Reinforcement Learning from Human Feedback) 和 RLAIF (RL from AI Feedback)。理解其基本概念，如策略（Policy）、奖励（Reward）、值函数（Value Function）等。 PPO算法 (Proximal Policy Optimization)：这是目前微调大模型最主流的强化学习算法之一。了解其基本原理对于理解本文的实验设置至关重要。 大语言模型的推理技术：熟悉 Chain-of-Thought (CoT)、Self-Consistency、Tree of Thoughts (ToT) 等主流的提升LLM推理能力的技术，这能帮助你更好地理解本文所要解决的问题背景和上下文。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文。\n","date":"2025-08-18T00:00:00Z","permalink":"/promoting-efficient-reasoning-with-verifiable-stepwise-reward/","title":"Promoting Efficient Reasoning with Verifiable Stepwise Reward"},{"content":"一开始还以为是GRPO的变式，没想到是DPO的变式。对于回答正确的回复，短的为winner，长的为loser。数据筛选部分使用的方法是对一个问题多次回答，根据正确率来判断难度。\n论文深入解读 这篇论文的核心贡献在于提出了一种名为长度受控偏好优化 (Length Controlled Preference Optimization, LCPO) 的新方法，旨在解决大型推理模型 (Large Reasoning Models, LRMs) 在执行思维链 (Chain-of-Thought, CoT) 推理时普遍存在的“过度思考”（overthinking）问题。LRMs，如论文中提到的 Deepseek-R1，虽然在复杂任务上表现出色，但其生成的推理过程往往冗长、繁琐，这不仅大幅增加了计算成本和延迟，有时甚至会因为步骤过多而引入错误。论文开篇就用一个极具说服力的例子（Figure 1）展示了这一问题：对于一个数学问题，原始的 DeepSeek-R1 模型生成了长达 5465 个 token 的解答，而经过 LCPO 优化后，模型能够用仅仅 1128 个 token 给出同样正确的答案，token 数量减少了惊人的 79.37%。\n这篇论文的创新性体现在两个层面：一是数据驱动的洞察与策略，二是算法层面的理论分析与创新。\n首先，在数据层面，论文的研究者没有依赖昂贵的人工标注，而是巧妙地挖掘了模型自身的“潜力”。他们提出了一个核心的研究问题（RQ1）：“在推理模型的生成空间中，是否存在更短但同样有效的推理路径？”为了回答这个问题，他们进行了一项经验性研究：让模型对同一个问题生成多个（16个）不同的解答。通过分析这些解答，他们发现了一个关键现象（Figure 2a）：模型的推理性能并不会随着输出长度的增加而无限提升，当长度超过某个阈值后，准确率反而会下降。这说明模型本身具备生成简洁、高效答案的能力，只是在常规推理中未能稳定地选择这条路径。\n基于这个洞察，他们设计了一套非常高效的自蒸馏（self-distillation）数据筛选流程。他们首先根据模型对问题的平均通过率（pass rate） 来粗略地评估问题的难度，将问题分为“简单”、“中等”和“困难”三类。这一步非常关键，因为它是一个低成本的自动化难度评估方法。接着，对于“简单”问题，他们构建偏好数据对：将模型生成的所有正确答案中最短的一个作为“获胜”（chosen）样本，将最长的一个作为“落败”（rejected）样本。这种做法最大化了长度上的偏好信号，直接告诉模型：“在能做对的前提下，越短越好”。\n其次，在算法层面，论文对现有的偏好优化算法进行了统一框架下的深刻分析，并在此基础上提出了 LCPO。研究者将多种偏好优化方法（如 DPO, SimPO, ORPO）统一在 Bradley-Terry 损失框架 下进行分析。这是一种用于处理成对比较数据的经典统计模型，其核心思想是每个比较对象的“能力”可以用一个潜在的分数来表示。通过理论推导（Appendix D），他们发现： \u0026gt; …the implicit reward associated with NLL loss can hinder length preference acquirement. \u0026gt; （…与负对数似然损失（NLL loss）相关的隐式奖励可能会阻碍长度偏好的学习。）\n具体来说，像 ORPO 这样的方法，虽然也试图进行偏好优化，但其目标函数中包含了一项标准的负对数似然损失（NLL loss），这与传统的监督式微调（SFT）目标类似。这个NLL项会促使模型去拟合“获胜”样本的每一个词元，而忽略了我们真正想要的“长度”这个宏观偏好。这导致模型在学习缩短长度方面不够高效。\n为了解决这个问题，LCPO 被设计出来。从其目标函数（Equation 12）可以看出，它移除了传统的NLL项，直接在一个统一的 Bradley-Terry 框架内对“获వ胜”和“落败”样本的隐式奖励（implicit reward） 进行优化。这个奖励与模型生成整个序列的对数概率相关。通过直接平衡这对奖励，LCPO 能够更纯粹、更高效地学习到对长度的偏好，而不会被拟合具体词元的任务所干扰。这种设计的另一个巨大优势是训练效率极高。论文中提到： \u0026gt; …our method requires only 0.8k training samples and 50 training steps, largely reducing computational cost compared to prior approaches. \u0026gt; （…我们的方法仅需 0.8k 训练样本和 50 个训练步骤，与先前的方法相比，极大地降低了计算成本。）\n这使得 LCPO 成为一种非常实用、易于部署的“即插即用”式优化技术。\n总而言之，这篇论文不仅仅是提出了一个新算法，更是提供了一套完整的、从数据洞察到算法设计的低成本、高效能的解决方案。它证明了通过巧妙的自蒸馏和对偏好优化理论的深刻理解，我们可以在极小的资源开销下，显著改善大模型的推理效率，这对于推动大模型在实际应用中的部署和普及具有非凡的价值。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？ 研究目标：论文的核心研究目标是，在保持大型推理模型（LRMs）高推理性能的前提下，显著缩短其在解决问题时生成的思维链（CoT）的长度。\n解决的实际问题：\n高昂的计算成本：LRMs 生成的长篇大论（即长 CoT）会消耗大量的计算资源，导致推理延迟增加、显存占用变大，直接推高了模型服务的成本。在商业应用中，这会限制模型的部署规模和响应速度。 过度思考导致性能下降：论文指出，过长的输出可能导致“过度思考”（overthinking），模型在简单问题上消耗过多 token，甚至可能产生冗余或错误的输出。Figure 4 就清晰地展示了原始模型在最简单的 Level 1 问题上，其平均输出长度甚至超过了 Level 2 和 Level 3，这显然是不合理的。 限制了模型的应用场景：对于需要实时交互或处理海量请求的场景（如在线客服、代码助手），高延迟是不可接受的。缩短推理路径是让 LRMs 在这些场景中变得实用的关键一步。 对行业发展的意义： 这个问题的解决对于 AI 行业，特别是大模型即服务（MaaS）领域，具有至关重要的意义。它直接关系到大模型商业化的“最后一公里”——效率和成本。一个既聪明又高效（便宜）的模型，其商业价值和应用前景远大于一个虽然聪明但运行缓慢且昂贵的模型。这项研究为业界提供了一种低成本、易于实施的“模型加速”方案，有助于推动功能强大的 LRMs 从实验室走向更广泛的产业应用。\n### 2. 论文提出了哪些新的思路、方法或模型？ 论文提出的核心方法是一套包含数据策略和优化算法的完整流程。\n新的思路：挖掘并放大模型自身的“简洁推理”能力。 传统思路可能是用人工编写的简洁范例去微调模型，但这成本高昂且难以规模化。本文的思路是，模型本身在多次采样中就能生成长短不一的答案，其中不乏简洁高效的路径。我们要做的是设计一套机制，让模型“学会”稳定地选择这些简洁路径。这是一个“向内求索”的思路，即自蒸馏（self-distillation）。\n新的方法：数据筛选策略 + LCPO 算法\n基于难度的自适应数据筛选： 特点：不依赖人工，而是通过一个简单的启发式规则——模型在某问题上的多次采样通过率（pass rate）——来自动化地评估问题难度。 优势：这是一个非常轻量级且有效的代理指标。基于此，论文将数据分为“简单”、“中等”、“困难”。然后，仅在“简单”问题上构建偏好对，选择最短的正确答案作为正面样本，最长的作为负面样本。这套策略的优势在于，它精准地在模型最有潜力缩短长度（即简单的、已掌握的问题）的地方施加优化，避免了在难题上因强制缩短而损害推理能力。Table 4 的消融实验完美证明了这一点：使用“easy” split 训练的模型，在 MATH-500 上长度缩减了 57.07%，远超使用“medium”（41.56%）和“hard”（25.88%）split 的效果。 长度受控偏好优化（LCPO）算法： 特点：LCPO 是一种无参考模型（reference-free） 的偏好优化算法。它在 Bradley-Terry 框架下，直接优化“获胜”和“落败”样本的隐式奖励函数 r_θ(y|x) 的差值。这个奖励函数 r_θ(y|x) 被定义为序列的对数几率 log(p_θ(y|x) / (1 - p_θ(y|x)))，其中 p_θ(y|x) 是整个序列的概率。 优势：与 DPO 等方法相比，LCPO 的关键优势在于其目标函数（Equation 12）更纯粹地关注偏好。它不像 ORPO 那样带有一个标准的 NLL 损失项。论文的理论分析指出，这个 NLL 项会使优化目标偏向于“模仿”获胜样本，从而削弱了对“长度”这一宏观属性的学习。因此，LCPO 能够更早、更快地收敛于长度偏好，这也是它为什么仅用 50 个训练步就能达到优异效果的原因。它比 SimPO 等方法超参数更少，更容易调节。 ### 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且具有说服力的实验来验证 LCPO 的有效性，涵盖了性能、效率、泛化性和深入分析等多个维度。\n实验设计： 基准模型：DeepSeek-R1-Distill-Qwen-7B 和 1.5B 两个不同规模的模型。 评测数据集：涵盖了多个主流的数学推理基准测试，如 MATH-500, GSM8K, Minerva-Math, AIME24, AMC23, OlympiadBench，确保了评测的全面性。 对比方法：与多种现有方法进行了对比，包括推理时方法（CoD）、基于强化学习的方法（L1, TrEff）和其它偏好优化方法（DAST, DPO, SimPO, SimPER, ORPO）。 核心实验：在多个数学基准上比较 LCPO 与基线方法在准确率（Acc）和平均生成长度（Len）上的表现。 消融研究（Ablation Study）：验证其数据筛选策略的有效性，分别使用“easy”、“medium”、“hard”以及不过滤的数据集进行训练，观察效果差异。 泛化性测试：在分布外（Out-of-Distribution, OOD） 的 MMLU 数据集上进行测试，检验模型学习到的“简洁推理”能力是否能泛化到非数学领域。 细粒度分析：分析了在不同难度等级的 MATH-500 问题上，LCPO 对准确率和长度的影响。 关键数据和结果： 主实验结果 (Table 3)：LCPO 在性能和效率之间取得了最佳的权衡（trade-off）。 | 模型 | 数据集 | 原始性能 | LCPO 性能 | 长度变化 | | :— | :— | :— | :— | :— | | 7B | MATH-500 | 92.20% / 4223 | 92.00% (-0.20) / 1813 | -57.07% | | 7B | Minerva-Math | 36.76% / 5926 | 37.13% (+0.37) / 1797 | -69.68% | | 1.5B | MATH-500 | 83.00% / 5665 | 83.20% (+0.20) / 2397 | -57.69% | \u0026gt; 这些数据显示，LCPO 可以在几乎不降低甚至略微提升准确率的情况下，将生成长度减少 50% 以上。\n与其它偏好优化的对比 (Table 2)：在 MATH-500 上，LCPO 的长度缩减率（57.07%）远高于 DPO（38.41%）、SimPO（31.68%）和 ORPO（31.14%），同时保持了最高的准确率（92.00%）。这证明了其算法设计的优越性。\n泛化性结果 (Table 5)：在 MMLU 数据集上，7B 模型在准确率仅从 61.91% 下降到 61.61% 的情况下，长度减少了 42.47%。这表明 LCPO 学习到的并非只针对数学问题的“死记硬背”，而是一种更通用的高效推理策略。\n难度分析 (Figure 4)：LCPO 解决了原始模型在简单问题上“过度思考”的问题。训练后，模型在 Level 1 上的输出长度大幅缩短，准确率反而有所提升。这说明 LCPO 帮助模型将计算资源更合理地分配到了真正需要复杂推理的难题上。\n### 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 结合当前大模型领域的理解，这个研究方向前景广阔，但仍有许多挑战和机遇。\n值得探索的问题和挑战： 更通用的难度/质量评估标准：论文中的“通过率”启发式方法在有明确答案的数学题上很有效，但对于开放式问题（如创意写作、策略分析、法律咨询），如何自动评估一个答案的“质量”和“简洁性”是一个巨大挑战。未来可以探索利用模型自身进行评估（Model-based evaluation）或引入更复杂的多维度奖励模型。 多维度行为控制：目前只控制了“长度”，但模型的行为还有很多维度，如安全性、事实性、幽默感、代码风格等。能否将 LCPO 的思想扩展，通过构建多维度的偏好数据，实现对模型输出风格的精细化、个性化定制？ 动态推理路径规划：当前方法是让模型整体倾向于简洁。一个更高级的目标是让模型学会动态规划：在遇到简单子问题时快速跳过，在遇到关键难题时则投入更多思考步骤。这需要模型具备更强的元认知（meta-cognition）能力。 对超大模型（\u0026gt;100B）的有效性：该研究在 7B 模型上效果显著，但在千亿甚至万亿参数的巨型模型上是否依然有效，以及其行为模式是否相同，尚待验证。大模型可能存在更复杂的“过度思考”模式。 新的技术和投资机会： AI Agent 的效率优化：对于需要执行多步任务的 AI Agent，每一步的效率都至关重要。LCPO 这类技术可以用来优化 Agent 的“内心独白”或规划过程，使其更快、更低成本地完成任务。 模型行为定制化平台：可以预见，未来会出现面向企业的 MLOps 平台，允许用户上传少量偏好数据，就能快速、低成本地“塑造”一个符合其业务需求的模型（例如，一个言简意赅的客服模型，或一个详尽严谨的科研助理模型）。 端侧（On-device）部署的大模型：通过极致地压缩推理路径，结合模型量化、蒸馏等技术，有望将强大的推理模型部署到手机、汽车等边缘设备上，这将催生巨大的市场机会。投资于这些领域的初创公司，特别是那些拥有高效模型微调和部署技术的公司，将有很大潜力。 ### 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度来看，仍然存在一些可以探讨的局限性。\n存在的不足及缺失： 数据生成成本的讨论不足：虽然 LCPO 的训练过程非常高效（0.8k样本，50步），但其数据准备阶段需要对每个问题进行多次（16次）采样生成，这本身是一个计算密集型过程。论文没有详细讨论这一阶段的开销，这使得总成本（data generation + training）评估不够完整。 “简洁”的定义较为单一：论文将“简洁”等同于“token 数量少”。但在某些情况下，一个稍长但结构更清晰、可解释性更强的答案，可能比一个极度压缩的答案更有价值。对“简洁”或“高效”的定义可以更加多元化。 对推理能力损失的风险评估不足：尽管实验结果显示准确率下降很小，但是否存在某些类型的难题，其正确的推理路径天生就非常长，而 LCPO 可能会“一刀切”地惩罚这种长路径，从而导致在这些特定问题上能力下降？论文缺乏对这种“corner case”的分析。 需要进一步验证和存疑的： 训练数据领域的局限性：论文的训练数据主要来自 MATH 数据集。尽管在 MMLU 上展示了泛化性，但这种泛化能力在多大程度上适用于代码生成、逻辑推理、自然语言理解等其他复杂推理领域，还需要更广泛的实验验证。 超参数 λ 的敏感性：LCPO 的目标函数（Equation 12）中引入了一个超参数 λ。论文中没有详细讨论这个超参数的选择依据及其对模型最终性能的影响。它的鲁棒性如何，是否需要针对不同任务进行精细调整，这是一个疑问。 “正确”与“错误”样本的偏好：论文的策略是在“简单”问题中，从正确答案里选出最短和最长的。如果将“最长的正确答案”与“任意一个错误答案”进行比较，模型会如何学习？这关系到模型是在学习“简洁”，还是在学习避免“冗余的正确路径”。对偏好数据的构建策略可以有更多探索。 ### 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的“拿来即用”的思路和启发。\n重点学习的核心思想： 自省式数据挖掘（Introspective Data Mining）：最核心的启发是，在优化模型前，先深入分析模型自身的“能力边界和行为空间”。不要急于寻找外部数据，模型的多次、多样化输出本身就是一个富矿，蕴含了提升其性能的宝贵信息。这是一个普适性的方法论。 轻量级启发式规则（Lightweight Heuristics）：用“通过率”来代理“难度”，这种思路非常值得学习。在复杂的 AI 问题中，寻找一个简单但有效的代理指标（proxy）往往是解决问题的关键。这启发我们，面对复杂目标，可以先从一个可行的、低成本的近似方案入手。 偏好即奖励（Preference as Reward）：使用 DPO/LCPO 这类偏好优化方法，本质上是将复杂的、难以量化的奖励函数（如“简洁又准确”），简化为成对的比较（A 比 B 好）。这大大降低了定义奖励的难度。在任何需要优化模型行为的场景，都可以思考是否能将其转化为一个偏好学习问题。 对您的启发： 快速验证新想法：如果您有一个关于模型行为优化的新想法，可以借鉴本文的流程：首先，通过采样生成数据，看看模型是否已经“自发”地表现出您想要的行为；然后，构建小规模的偏好数据集；最后，用 LCPO 或类似的方法进行快速、低成本的微调验证。 低成本定制模型：这为个人开发者或小型团队提供了一条低成本定制专属模型的路径。通过收集少量体现个人偏好的数据（例如，你喜欢的代码风格、写作语气），就可以对一个基础模型进行微调，使其更符合你的需求。 超越长度的优化：您可以将这个框架应用到其他维度。例如，想让模型的回答更有同理心？可以生成多个回答，然后由人来挑选哪个“更有同理心”，构建偏好对进行训练。 需要补充的背景知识： 思维链（Chain-of-Thought, CoT）：理解 CoT 的原理，以及它为什么能提升模型的推理能力，这是理解本文问题背景的基础。 强化学习与人类反馈（RLHF）：了解 RLHF 的基本流程，因为 DPO 和 LCPO 都是在 RLHF 的思想基础上发展而来的，旨在解决其流程复杂、训练不稳定的问题。 直接偏好优化（Direct Preference Optimization, DPO）：这是理解 LCPO 的关键前置知识。您需要了解 DPO 是如何将 RLHF 中的奖励学习和策略优化合并为一个简单的分类损失的。Rafailov 等人的 DPO 原始论文是必读的。 Bradley-Terry 模型：虽然不是必需，但了解这个模型的基本思想，有助于您更深刻地理解为什么偏好优化算法的目标函数会写成 log-sigmoid 的形式，以及其背后的统计学原理。 ","date":"2025-08-18T00:00:00Z","permalink":"/pruning-long-chain-of-thought-of-large-reasoning-models-via-small-scale-preference-optimization/","title":"Pruning Long Chain-of-Thought of Large Reasoning Models via Small-Scale Preference Optimization"},{"content":"相较于Search-R1：Training LLMs to Reason and Leverage Search Engines with Reinforcement Learning的使用真实的搜索引擎和ZEROSEARCH：Incentivize the Search Capability of LLMs without Searching使用虚拟的训练的大模型作为搜索引擎，这篇论文直接让自己当搜索引擎，更节省成本。\n论文深度解读 这篇论文的核心思想，建立在一个深刻且颇具前瞻性的洞察之上：大型语言模型（LLM）本身就是一个庞大的、内隐的知识库，或者说是一个“世界模型”（World Model）。传统的搜索智能体在训练时，需要不断地与外部的真实搜索引擎（如Google、Bing）进行交互，这个过程被称为全真实搜索（Full-Real Search）。这种方式虽然直接，但代价高昂。每一次交互都可能涉及API调用费用，并且网络延迟和不稳定的返回结果也给强化学习（RL）训练带来了巨大的挑战，因为RL需要海量的“试错”样本（rollouts）。\n论文作者们首先提出了一个问题：我们能否不依赖外部工具，仅仅利用LLM内部存储的知识来完成需要搜索的任务？为了回答这个问题，他们设计了名为自搜索（Self-Search）的推理时（inference-time）策略。\nSelf-Search的核心是让模型在一次生成中，自回归地模拟出完整的“思考 → 搜索 → 获取信息 → 形成答案”的链条。模型会先在\u0026lt;think\u0026gt;标签中进行思考，然后生成\u0026lt;search\u0026gt;查询，接着自己“扮演”搜索引擎，在\u0026lt;information\u0026gt;标签中生成相关信息，如此循环，直到最终在\u0026lt;answer\u0026gt;标签中给出答案。\n通过这种方式，作者们发现LLM拥有巨大的未被发掘的潜力。他们引入了一个关键评估指标——pass@k。这个指标的含义是：模型生成k个不同的答案，只要其中有一个是正确的，就算通过。这与传统的精确匹配（Exact Match）不同，它衡量的是模型知识的“上限”或“广度”。实验结果（如图2所示）惊人地显示，随着采样次数k的增加，模型的pass@k准确率显著提升。例如，在Bamboogle基准测试上，Llama-3.1-8B模型在pass@1（单次生成）时的准确率可能不高，但在pass@1024时，准确率提升了150%，达到了87.2%。这雄辩地证明了：正确答案往往就“蕴藏”在模型的参数中，只是我们缺少一种有效的方法将其稳定地“提取”出来。\n仅仅证明潜力是不够的，关键在于如何利用这种潜力。由此，论文的核心贡献——自搜索强化学习（SSRL）应运而生。SSRL是一种全模拟（Full-Sim）的训练范式，它在训练过程中完全不与任何外部搜索引擎交互。智能体（即LLM本身）的“环境”就是它自己的参数化知识。\nSSRL的巧妙之处在于其奖励函数（Reward Function）的设计。它包含两个部分： 1. 结果奖励（Outcome Reward）：答案正确，则奖励为+1；错误则为-1。这是标准的RL做法。 2. 格式奖励（Format Reward）：这是论文的一个精髓。它奖励那些严格遵循\u0026lt;think\u0026gt;...\u0026lt;search\u0026gt;...\u0026lt;information\u0026gt;...结构的行为。这个设计至关重要，因为它迫使模型学习一种结构化的、可分解的思考和信息检索模式，而不是产出混乱无序的文本。这不仅稳定了训练过程，也使得模型学习到的能力更容易泛化。\n通过SSRL训练后，模型在多个问答基准上取得了卓越的性能。从论文的核心实验结果表（Table 3）中可以看到，在Llama-3.1-8B模型上，SSRL-Instruct的平均准确率达到了43.1%，显著优于依赖外部搜索引擎训练的Search-R1（37.1%）和依赖另一个LLM作为模拟器的ZeroSearch（41.5%）。更重要的是，SSRL的训练效率极高。论文图8显示，与ZeroSearch相比，SSRL的训练时间缩短了5.53倍，这是一个巨大的工程优势。\n论文最令人兴奋的发现之一是Sim2Real（从模拟到现实）的泛化能力。一个在纯粹“自我幻想”（Full-Sim）中训练出来的模型，当在推理时被允许接入真实的Google搜索后，其性能不仅没有下降，反而得到了进一步的提升。例如，在Qwen2.5-7B模型上，纯SSRL的平均准确率为30.2%，而接入真实搜索后（Sim2Real K=3），准确率飙升至47.9%（Table 4）。这说明SSRL教会模型的不仅仅是“死记硬背”的知识，更是一种“如何思考和利用信息”的通用能力。模型学到了如何提出好问题（生成好的search query），以及如何整合信息，这种能力在真实环境中同样适用。\n综上所述，这篇论文完成了一次漂亮的范式探索。它从验证LLM的内隐知识潜力出发，提出了一套完全“自给自足”的强化学习训练框架SSRL，并最终证明了这种低成本训练模式不仅高效，还能成功地将学到的能力迁移到真实世界。这为未来构建可大规模扩展、低成本、高效率的AI智能体提供了坚实的理论基础和实践路径。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是探索和验证一种全新的、低成本的范式来训练用于搜索任务的语言模型智能体。具体来说，它旨在证明LLM可以作为其自身的知识模拟器，通过自搜索强化学习（SSRL）在完全不依赖外部信息源的情况下，有效提升其在信息检索和问答任务上的能力。\n解决的实际问题：\n高昂的训练成本：训练需要与外部世界（如网络搜索引擎）交互的智能体，需要进行海量的API调用。这不仅产生了直接的经济成本（API费用），还带来了时间成本（网络延迟）。 训练过程的不稳定性：外部搜索引擎的返回结果是非确定性的、充满噪声的，且格式不一。这种不稳定性给需要稳定环境的强化学习训练带来了巨大困难，使得训练过程难以复现和收敛。 对外部工具的过度依赖：现有的智能体大多被设计为外部工具的“调用者”，其核心能力受限于工具的质量和可用性，而模型自身的推理和知识能力没有被充分激发。 对行业发展的重要意义：\n大幅降低AI智能体研发门槛：如果智能体可以在“离线”或“自我模拟”的环境中完成大部分训练，那么研发成本将呈数量级下降。这将使得更多的中小型企业和研究机构能够参与到前沿的智能体研发中，极大地促进了技术的普及和民主化。 提升智能体训练的效率和可复现性：SSRL的训练过程是确定性的、高效的。如图8所示，其训练速度远超依赖外部模拟器的方法。这使得研究人员可以更快地进行模型迭代和实验验证，加速了整个领域的发展。 推动智能体向更“自主”的方向发展：SSRL证明了模型可以学习“反思”和“利用”自己的内部知识，这是一种更高级的自主智能。它鼓励我们重新思考智能体的本质，从一个纯粹的“工具使用者”转变为一个拥有强大内隐知识并懂得如何利用它的“思考者”。 催生新的混合智能体架构：论文中的Sim2Real和熵引导搜索（entropy-guided search）实验，为设计混合智能体（Hybrid Agents）提供了思路。这类智能体能智能地判断何时依赖自身知识，何时求助于外部工具，从而在效率和能力之间取得最佳平衡。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于提出了一套从理念到实践的完整框架，主要包括Self-Search的概念和SSRL的训练方法。\n新的思路与方法： Self-Search（自搜索）： 这是一种推理范式，其核心是让LLM在一次生成过程中，完整地模拟“思考-查询-生成信息-整合”的闭环。 \u0026gt; 如论文中的Prompt Template所示，模型被引导生成\u0026lt;think\u0026gt;, \u0026lt;search\u0026gt;, \u0026lt;information\u0026gt;, \u0026lt;answer\u0026gt;等一系列标签，形成结构化的思维链。\nSSRL (Self-Search Reinforcement Learning)： 这是本文最主要的方法创新。它是一个全模拟（Full-Sim）的强化学习框架，特点是在训练过程中，智能体（Policy Model）同时扮演了决策者和环境（信息提供者）的角色。 \u0026gt; 其RL目标函数简化为：max E[r(x, y)] - βDKL[πθ(y|x) || πref(y|x)]，其中信息R的来源分布与策略模型πθ本身一致，从而实现了“自给自足”。\n与之前方法的比较、特点和优势： 我们可以用论文Table 11中的术语来清晰地对比： 搜索范式 训练时的信息源 代表性工作 特点与优势/劣势 Full-Real Search 真实的外部搜索引擎（如Google、Wikipedia API） Search-R1 优势: 接触真实数据，泛化性好。\n劣势: 成本高、速度慢、不稳定。 Semi-Real Search 另一个经过微调的LLM作为外部模拟器 ZeroSearch 优势: 相比Full-Real成本更低、速度更快。\n劣势: 仍需维护一个强大的模拟器模型，存在双模型开销，效率不如SSRL。 Full-Sim Search 策略模型自身的参数化知识 SSRL (本文) 优势: 极致的成本效益和训练效率，无需任何外部依赖，训练过程稳定可控。\n劣势: 知识受限于预训练数据，存在时效性问题。 **SSRL的关键优势细节分析**： * **高效性**：由于无需网络I/O和外部模型调用，SSRL的训练循环极快。论文图8明确指出，其训练速度比ZeroSearch快**5.53倍**。 * **内部知识的深度激发**：通过格式奖励和结果奖励的共同作用，SSRL迫使模型深入探索其参数空间，学习如何组织和检索其内隐知识，而不仅仅是作为外部API的调用前端。 * **强大的Sim2Real泛化能力**：这是SSRL最令人惊喜的优势。一个在纯模拟环境中训练出的模型，因为它学会了如何“思考”和“提问”，所以当它被给予真实工具时，能迅速适应并表现得更好。这证明了SSRL学习到的是一种**可迁移的元认知能力（meta-cognitive skill）**。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文的实验设计非常严谨，分为两个主要阶段，层层递进，令人信服。\n实验设计： 第一阶段：验证Self-Search的潜力（Inference-time Scaling of Self-Search） 目的：证明LLM内部确实蕴含着足够解决复杂问答任务的知识。 方法：对多个模型家族（Llama, Qwen）和不同尺寸的模型，在7个QA基准（如TriviaQA, HotpotQA, BrowseComp等）上进行大规模采样测试。 核心指标：pass@k。通过计算生成k个答案中至少有一个正确的概率，来评估模型的知识上限。 设计亮点：这个实验巧妙地将“模型能否一次答对”的问题，转换为了“模型的知识库里有没有正确答案”，为后续的RL训练提供了理论依据。 第二阶段：验证SSRL的有效性与效率（SSRL: Self-Search Reinforcement Learning） 目的：证明SSRL方法能够有效提升模型性能，并且优于其他基线方法。 方法：在6个QA基准上（排除了特别难的BrowseComp），使用SSRL方法训练Llama和Qwen模型。 对比基线（Baselines）： 无搜索方法：Direct Answer, CoT 基于RAG的方法：Standard RAG, Search-o1 基于RL的方法：R1, Search-R1 (Full-Real), ZeroSearch (Semi-Real) 核心指标：精确匹配率（EM），以及训练效率（训练时间）。 扩展实验：还包括了Sim2Real泛化测试、不同RL算法的兼容性测试、消融研究（如格式奖励、信息掩码的作用）等。 关键实验数据和结果： Self-Search潜力巨大： \u0026gt; As shown in Figure 2 and Figure 3, we observe consistent and predictive performance improvements across all benchmarks as the number of samples increases. Notably, on Bamboogle, Llama-3.1-8B-Instruct achieves 87.2% accuracy for pass@1024, a 150% improvement over pass@1 performance. 解读：这组数据显示了巨大的性能提升空间，证明了“答案在模型内部”的假设。 SSRL性能超越基线： \u0026gt; 在Table 3中，以Llama-3.1-8B-Instruct模型为例： \u0026gt; * SELF-SEARCH-INSTRUCT (Full-Sim): 平均分 43.1 \u0026gt; * ZeroSearch-Instruct (Semi-Real): 平均分 41.5 \u0026gt; * Search-R1-Instruct (Full-Real): 平均分 37.1 解读：这个核心结果表明，SSRL这个成本最低的训练方法，其最终性能竟然超过了那些依赖更昂贵外部资源的训练方法。 Sim2Real泛化效果显著： \u0026gt; 在Table 4中，以Qwen2.5-7B-Instruct模型为例： \u0026gt; * SSRL (无真实搜索): 平均分 30.2 \u0026gt; * Sim2Real (K=1, 接入1次真实搜索): 平均分 38.0 \u0026gt; * Sim2Real (K=3, 接入3次真实搜索): 平均分 47.9 \u0026gt; * Search-R1 (Full-Real基线): 平均分 35.9 解读：这个结果极具说服力。在模拟环境中“闭门造车”训练出的模型，一旦接触真实世界，其表现远超预期，甚至比那些一直在真实环境中训练的基线模型还要好。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为“LLM作为世界模型”和高效智能体训练打开了一扇大门，未来有许多激动人心的探索方向：\n值得探索的问题与挑战： 动态知识更新（Dynamic Knowledge Update）：SSRL最大的局限是模型的知识是静态的，无法获知预训练之后发生的新事件。未来的研究需要探索如何将SSRL与高效的知识编辑或实时信息注入技术相结合，让智能体在保持低成本训练的同时，也能与时俱进。 从“搜索”到更复杂的“工具使用”：SSRL成功模拟了“搜索引擎”这个工具。那么，它能否模拟更复杂的工具，如代码解释器、计算器、数据库查询API？如果模型能学会模拟这些工具的输入输出关系，将进一步降低智能体学习使用各类工具的成本。 模型“自知之明”的量化与利用：如何让模型更准确地判断“我是否知道这个问题的答案”？论文中提到的基于熵的方法是一个初步探索。未来可以研究更复杂的置信度估计方法，让模型在“依赖自己”和“求助外部”之间做出更优的决策，这是通向更鲁棒混合智能体的关键。 内在幻觉的修正：SSRL能提升模型提取已有知识的能力，但如果模型参数中存储的知识本身就是错误的（即幻觉），SSRL可能会强化这种错误。如何让模型在“自省”的过程中，还能发现并修正自己内在的知识谬误，是一个极具挑战性的前沿问题。 可能催生的新技术和投资机会： 高效智能体训练平台：基于SSRL理念，可以开发出专门用于智能体“预训练”的平台。开发者可以在这个平台上，以极低的成本，让智能体掌握使用各类工具的基本逻辑和模式，然后再通过少量真实数据进行微调。 专为“世界模型”设计的LLM架构：未来的LLM设计可能会更倾向于强化其作为世界模型的能力，例如通过特定的预训练任务或模型结构，使其更容易被SSRL这类方法激发和利用。 企业级内网知识智能体：SSRL范式非常适合在企业内部知识库上训练智能体。企业可以用私有数据训练一个“了解”内部所有文档和流程的LLM，然后通过SSRL让它学会自主地在内部知识体系中进行“搜索”和“推理”，而无需频繁地与外部昂贵模型API交互。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些值得商榷和进一步验证的地方：\n知识的静态性是其根本局限： 这是最核心的不足。模型无法回答关于其预训练数据截止日期之后发生的任何事情。尽管Sim2Real实验展示了良好的泛化性，但这只是一个“权宜之计”（workaround），并没有从根本上解决模型在训练阶段的知识局限。对于需要高度实时性的应用场景，SSRL的直接适用性有限。\n任务复杂度的覆盖范围： 论文在进行SSRL训练时，有意排除了最难的BrowseComp基准，理由是“exceptional difficulty”。这恰恰说明，当任务的复杂性、模糊性和多步推理要求达到一定高度时，当前LLM的内部知识模拟能力可能仍然不足以支撑有效的RL训练。SSRL的性能边界在哪里，仍有待探索。\nSim2Real Gap的潜在风险： 论文展示了成功的Sim2Real迁移，但现实世界的网络环境远比结构化的API返回要复杂和混乱。模型在纯净的、自我生成的\u0026lt;information\u0026gt;文本上训练，是否能完全适应真实网页中混杂着广告、导航栏、格式错误的HTML噪声，这一点还需要在更广泛、更复杂的真实环境中进行验证。论文中提到的“rule-based modifications”可能只是冰山一角。\n对基模型知识的依赖性： SSRL的效果高度依赖于基模型本身预训练知识的质量和广度。如果基模型对某个垂直领域的知识覆盖不足，那么SSRL训练出的智能体在该领域的表现也必然会很差。它“激发”的是模型已有的潜力，而不能“创造”模型没有的知识。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是一个充满了“宝藏”的矿山，以下几点是您绝对可以借鉴和学习的创新精华：\n核心启发：视LLM为可交互的“世界模型” 这是最关键的思维转变。不要再把LLM仅仅看作一个问答或文本生成工具，而要把它看作一个蕴含了丰富知识和逻辑的、可以被“探测”和“交互”的模拟环境。基于这个思想，您可以设计出许多新的应用，比如让模型模拟用户、模拟代码运行结果、模拟物理定律等。\n可直接借鉴的技术点：\n结构化思维链的强制引导：通过设计如\u0026lt;think\u0026gt;, \u0026lt;search\u0026gt;这类XML标签，并配合格式奖励（Format Reward），可以非常有效地引导模型产出结构化、可解析的中间步骤。这个方法几乎可以用于任何需要复杂推理的任务，以增强模型输出的稳定性和可控性。 pass@k评估思维：在评估需要探索或创造性解决方案的任务时，不要只看模型的单次最佳表现（pass@1）。通过pass@k或类似的多样本评估方法，可以更准确地衡量模型能力的上限，帮助您判断一个模型是“不能”还是“不稳定”。 熵引导的混合策略：论文中提到的“entropy-guided search”是一个非常实用的工程技巧。通过监测模型生成特定内容（如下一步的搜索查询）时的不确定性（熵），可以动态决定是相信模型的内部生成，还是调用一个更可靠的外部工具。这对于构建兼具成本效益和鲁棒性的系统至关重要。 需要补充的背景知识： 为了完全理解并应用这篇论文的思想，建议您补充以下知识：\n强化学习（Reinforcement Learning）：特别是策略优化算法，如PPO（论文中也用到了），以及奖励模型（Reward Modeling）的设计理念。 AI Agent（智能体）概念：了解经典的“ReAct”（Reasoning and Acting）框架，以及工具使用的相关研究。 Sim2Real概念：了解其在机器人学中的起源和应用，有助于更好地理解论文中Sim2Real泛化的意义。 LLM评估指标：除了传统的BLEU、ROUGE，需要了解pass@k, maj@k这类针对生成多样性和准确性的评估方法。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-18T00:00:00Z","permalink":"/ssrlself-search-reinforcement-learning/","title":"SSRL：SELF-SEARCH REINFORCEMENT LEARNING"},{"content":"一个训练框架，可以直接将推理代码转为训练。\n论文深度解读 《Agent Lightning》提出的是一个灵活、可扩展的框架，其目标是实现AI Agent与强化学习训练过程的“完全解耦”。在它出现之前，想要用RL来优化一个Agent，通常意味着您需要在一个特定的、支持RL的框架内从头构建您的Agent，或者对现有Agent进行大量侵入式改造。这极大地限制了RL在庞大多样的Agent生态中的应用。\n论文的作者敏锐地洞察到，Agent的执行逻辑（例如，使用LangChain编排工具调用，使用AutoGen进行多智能体对话）和底层的LLM模型优化（通过RL提升决策能力）应该是可以分开的。为了实现这一点，他们引入了几个关键的创新设计：\n训练-代理分离架构 (Training-Agent Disaggregation Architecture)：这是论文的顶层设计。它将系统分为Lightning服务器和Lightning客户端。服务器端负责重量级的RL训练任务（如模型参数更新、任务分发），而客户端则轻量化，只负责运行用户已有的Agent逻辑。两者之间通过一个类似OpenAI的API进行通信。这样一来，无论您的Agent是用什么框架（LangChain, AutoGen, Semantic Kernel等）开发的，甚至于是从零手写的，只要它最终需要调用LLM，就可以通过这个API接入Agent Lightning进行训练，而Agent本身的代码几乎无需改动。\n基于马尔可夫决策过程（MDP）的统一数据接口：这是实现解耦的理论基石。作者将复杂的Agent执行过程抽象为一个马尔可夫决策过程 (Markov Decision Process, MDP)。这是一个经典的强化学习模型，我为您通俗地解释一下：\n状态 (State)：可以理解为Agent在某一时刻的“快照”，包含了所有关键信息，比如用户的输入、已经生成的中间结果、当前的程序变量等。 动作 (Action)：在Agent的场景里，特指LLM模型生成的一次完整输出（比如，生成一个SQL查询、或一句话的回复）。 奖励 (Reward)：一个数值信号，用来评价“动作”的好坏。比如，最终生成的SQL查询是否正确执行，答案是否准确。 状态转移 (Transition)：Agent执行一个“动作”后，它的“状态”发生了改变，这个过程就是状态转移。 通过这个抽象，任何复杂的Agent执行过程都可以被看作是一系列状态 -\u0026gt; 动作 -\u0026gt; 奖励 -\u0026gt; 新状态的转换序列。Agent Lightning据此定义了一个标准化的数据格式来记录这些转换，即每次组件（LLM或工具）调用都记录为(component, input, output, reward)。这个接口的巧妙之处在于，它屏蔽了Agent内部纷繁复杂的实现细节，使得训练系统只关心这些标准化的“决策点”。\n分层强化学习算法 (LightningRL)：这是针对Agent场景提出的优化算法。以往的方法（如论文图3(b)所示）常常将Agent的多轮交互拼接成一个长长的序列，然后通过复杂的“掩码（masking）”技术来告诉模型哪些部分需要学习，哪些部分不需要。这种方法不仅实现复杂、容易出错，而且会产生超长上下文，给LLM带来巨大负担。\nIn contrast, previous methods often concatenated all turns in a trajectory into a single response and used masking to control which parts were updated–an approach that cannot support such flexible and modular context construction.\nLightningRL则不同，它采用了一种“分而治之”的策略（如图3(c)所示）：\n分解: 它首先将一次完整的Agent执行轨迹（Trajectory）分解成数个独立的“转换”（Transition），每一次LLM的调用就是一次转换。 信用分配 (Credit Assignment): 然后，它将最终获得的奖励（比如任务成功与否）分配给轨迹中的每一次转换。论文中采用了最简单的方式：将最终奖励R平等地赋予每一次转换。 单轮优化: 最后，它将每一个带有奖励的转换 (input, output, reward) 视作一个独立的、单轮的RL训练样本。这样一来，就可以直接套用现有的、成熟高效的单轮RL算法（如GRPO, REINFORCE++）进行优化。 图3: LightningRL算法与其他方法的对比。 (a)是单轮RL，(b)是之前多轮RL的拼接+掩码方式，(c)是LightningRL的分解+信用分配方式，更加清晰和高效。\n实验部分，论文精心挑选了三个具有代表性的任务来验证其框架的通用性和有效性：使用LangChain实现的Text-to-SQL任务、使用OpenAI Agents SDK实现的检索增强生成（RAG）任务，以及使用AutoGen实现的数学问答任务。实验结果（如图5, 6, 7所示）清晰地表明，在这些不同的框架和场景下，Agent Lightning都能够带来持续、稳定的性能提升，奖励曲线稳步上扬。例如，在Text-to-SQL任务中，模型在Spider数据集上的测试奖励从初始的不到0.1提升并稳定在0.5以上，证明了方法的有效性。\n总而言之，这篇论文提供了一个优雅而实用的工程解决方案，它像一个“万能适配器”，成功地在庞杂的AI Agent生态和专业的RL训练算法之间架起了一座桥梁，极大地降低了应用RL优化Agent的门槛。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是设计并实现一个通用、非侵入式的框架（即Agent Lightning），使得任何类型的AI Agent都能够便捷地利用强化学习（RL）进行优化和自适应学习。\n解决的实际问题: 它要解决的核心问题是RL训练与Agent开发的“紧耦合”问题。在当前，AI Agent的开发生态百花齐放（LangChain, AutoGen等），开发者可以快速构建功能强大的Agent。然而，这些Agent一旦开发完成，其能力就基本固定了，很难像人类一样从后续的执行经验中学习和进步。虽然RL是实现这种“自我进化”的理想途径，但现有的RL训练框架往往要求Agent必须按照其特定的方式来构建，导致：\n高昂的迁移成本: 开发者需要重写大量代码，才能将已有的Agent迁移到RL框架中。 技术壁垒: Agent开发者通常不是RL专家，复杂的RL框架配置和调试令人望而却步。 生态不兼容: 无法利用海量的、已存在的优秀Agent实现。 对行业发展的重要意义: 这个问题是阻碍AI Agent从“功能强大的工具”迈向“能够持续学习的智能体”的关键障碍。解决它具有重大的行业意义：\n释放存量价值: 让数以万计的、基于现有框架开发的Agent能够通过RL获得持续优化的能力，极大提升了现有技术生态的价值。 加速技术迭代: 开发者可以专注于Agent的业务逻辑创新，而将模型优化这一复杂任务交给像Agent Lightning这样的后端框架。这会形成一个“开发-部署-训练-再部署”的快速迭代闭环。 推动Agent走向真实世界: 真实世界的任务复杂多变，预训练模型难以覆盖所有场景。通过RL从真实交互数据中学习，是Agent适应特定领域、处理私有数据和工具的关键，Agent Lightning为此铺平了道路。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新不在于单个算法，而在于一套系统性的设计思路和方法。\n思路一：训练-代理分离架构 (Training-Agent Disaggregation) 特点: 这是一种物理和逻辑上的双重解耦。通过C/S（客户端/服务器）架构，训练逻辑和Agent执行逻辑可以运行在不同的物理机器上，并通过标准API通信。 优势: Agent-Agnostic（代理不可知）: 训练服务器不关心Agent是如何实现的，只关心通过API收到的训练数据。 Trainer-Agnostic（训练器不可知）: Agent端也不关心后端用的是什么RL算法，只需调用模型API即可。 灵活性和可扩展性: 开发者可以自由选择最适合的Agent框架，同时系统可以轻松地横向扩展Agent执行实例（客户端），以满足大规模RL所需的并行数据采集（Rollout）需求。 方法二：基于MDP的统一数据接口 特点: 将复杂的Agent执行轨迹，通过MDP理论，抽象为一系列标准化的（组件调用，输入，输出，奖励）元组。 优势: 通用性: 该接口适用于任何Agent，无论是单Agent、多Agent、顺序流还是动态图。 \u0026gt; This unified data interface abstracts away the underlying orchestration logic and agent framework details, making it applicable to ANY agent. 简化数据处理: 避免了对异构、混乱的Agent执行日志进行繁琐且易错的解析。训练系统可以直接消费这些标准化的数据。 模型/算法三：分层强化学习算法 (LightningRL) 特点: 将多轮交互任务分解为单轮决策的集合，再进行优化，而非将多轮交互拼接成一个长序列。 优势 vs. 拼接+掩码 (Concatenation \u0026amp; Masking) 策略: 避免超长上下文: 拼接方式会使输入序列越来越长，很快会触及LLM的上下文窗口限制或导致计算成本飙升。分解策略则没有这个问题。 实现更简单鲁棒: 拼接需要设计复杂的掩码来控制梯度更新，这不仅容易出错，还会破坏LLM位置编码的连续性。分解策略则完全避免了掩码。 上下文构建更灵活: 在分解后的每个决策点，可以根据需要灵活地构建LLM的输入（prompt），而不受固定拼接格式的限制。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过三个精心设计的实验来证明其方法的通用性和有效性。\n实验设计: 作者的实验设计思想是覆盖多样性，以证明框架的“ANY AI agent”承诺。他们选择了： 不同的Agent框架: LangChain, OpenAI Agents SDK, AutoGen。 不同的任务类型: 结构化数据操作 (Text-to-SQL)，开放域问答 (RAG)，工具使用 (Math QA)。 不同的Agent复杂度: 包括单LLM Agent和由多个LLM扮演不同角色的多Agent系统。 The effectiveness of Agent Lightning is validated on three distinct tasks, each implemented with a different agent framework… As shown by results in the following subsections, Agent Lightning enables continuous and stable performance improvement across diverse scenarios…\n实验数据和结果: 任务 框架 数据集 核心挑战 关键结果（引用原文数据/图） Text-to-SQL LangChain Spider SQL代码生成、多步推理、多Agent协作 图5显示，在训练过程中，奖励持续稳定上升。在测试集上，奖励也从非常低的水平显著提高，证明了模型学会了生成和修正更准确的SQL。 Open-domain QA (RAG) OpenAI Agents SDK MuSiQue 多跳推理、在超大规模文档（2100万）中检索、开放式查询生成 图6显示了类似的学习曲线。奖励由0.9 * R_correctness + 0.1 * R_format构成，其稳定提升表明Agent不仅学会了正确的格式，也提升了答案的F1得分。 Math QA AutoGen Calc-X 精确的工具（计算器）调用和结果整合 图7再次验证了这一点，模型的奖励在训练中持续攀升至较高水平，表明Agent学会了何时以及如何正确地使用计算器来解决数学问题。 这些一致的积极结果有力地证明了Agent Lightning框架的有效性和普适性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个研究方向，即Agent的持续学习和优化，是当前大模型领域的前沿热点，未来充满了机遇和挑战。\n值得探索的问题和挑战: 更高效的信用分配 (Credit Assignment): 论文中采用了简单的“平均分配”最终奖励。但对于长程任务，早期的关键决策可能影响深远。未来的研究需要探索更智能的信用分配机制，例如： 基于模型的信用分配: 训练一个高阶的价值函数（Value Function）来评估每个中间状态的价值，从而给出更精细的奖励。 因果推断: 尝试分析动作与最终结果之间的因果关系来分配奖励。 奖励函数的自动化设计 (Reward Engineering): 本文实验中的奖励函数相对明确（如答案正确性）。但在现实中，很多任务的奖励是模糊的（如“用户满意度”）。如何从人类反馈中学习奖励模型（RLHF for Agents），或者设计无需密集人工标注的探索性奖励，是一个巨大挑战。 提升样本效率 (Sample Efficiency): RL通常需要海量的数据。如何结合监督学习、模仿学习、离线RL（Offline RL）等技术，用更少的交互次数达到更好的学习效果，是降低Agent训练成本的关键。 开放环境中的安全探索 (Safe Exploration): 当Agent与真实世界（如电商网站、操作系统）交互时，错误的探索性“动作”可能导致灾难性后果。研究安全的探索算法至关重要。 可能催生的新技术和投资机会: AgentOps / LLMOps 平台: 类似于为传统机器学习提供服务的MLOps，未来将出现专门为AI Agent提供训练、部署、监控、优化全生命周期管理的服务平台。Agent Lightning就是这类平台的核心技术原型。 自动化Agent评估与红队测试: 随着Agent能力的自我进化，需要更强大的自动化工具来持续评估其性能、鲁棒性和安全性。 专用硬件与基础设施: 大规模的Agent并行“Rollout”（数据采集）将成为新的计算瓶颈。为这一场景优化的专用硬件或云服务将成为新的投资热点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以深入或补充的地方：\n“零代码修改”的绝对性: 论文声称“almost ZERO code modifications”，但在附录的例子中，用户仍需编写一个train.py脚本，将原有的Agent逻辑封装在agent_run函数中，并理解其Client, Resource, Task等抽象。虽然这比重构整个Agent要简单得多，但并非完全“即插即用”，对于非技术用户仍有一定门槛。\n信用分配方法的简单性: 论文的核心算法LightningRL采用了最简单的平均奖励分配。虽然实验证明了其有效性，但缺乏与更复杂信用分配方法的对比。在一些关键决策影响重大的长程任务中，这种简单方法的有效性可能会下降。论文也承认了这一点，但未提供实验数据。\n对奖励函数的依赖: 整个框架高度依赖于一个定义良好、可自动计算的奖励函数。对于现实世界中许多奖励模糊、难以量化的任务，该框架如何应用，论文没有深入探讨。这实际上是将定义奖励这一核心难题留给了用户。\n实验规模的局限性: 实验中使用的基础模型为Llama-3.2-3B-Instruct，这是一个相对较小的模型。该框架在更大规模模型（如70B+）上的表现，以及在更长、更复杂的任务（例如，完成一个完整的软件开发项目）上的可扩展性和稳定性，还有待进一步验证。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文充满了可以借鉴的智慧。\n重点学习的核心思想: 解耦是系统设计的黄金法则: 核心启发是通过抽象和标准化来实现解耦。当面对一个由多个异构部分组成的复杂系统时（比如你的公司业务），思考如何定义清晰的边界和标准的“接口”，将不同的关注点分离。这能极大地提升系统的灵活性、可维护性和扩展性。 用理论指导工程实践: 作者巧妙地运用了经典的MDP理论来为Agent执行数据建模。这提醒我们，在解决看似混乱的工程问题时，回归到基础理论，寻找合适的抽象工具，往往能带来豁然开朗的效果。 分治法解决复杂流程: LightningRL算法将复杂的多轮任务分解为简单的单轮任务集合，这是一种典型的“分而治之”思想。在设计长流程、复杂任务的解决方案时，思考如何将其分解为一系列更小、更易于管理和优化的子步骤，是一个非常强大的思维模型。 可以获得的具体启发: 构建自优化系统: 如果你在开发任何基于LLM的应用，可以借鉴Agent Lightning的思路，通过埋点（instrumentation）来捕获用户的关键交互（输入、模型输出、最终结果），将这些数据结构化地存储下来。即使暂时没有RL训练，这些宝贵的数据也是未来进行监督微调、分析模型弱点的基础。论文提到的使用OpenTelemetry进行自动化埋点，是一个非常现代且可行的工程实践。 API作为解耦的工具: 在设计内部系统时，将核心能力（如模型训练）封装成服务，并通过定义良好的API暴露出来，可以让其他业务团队更灵活地接入和使用，而不是让所有人都去理解核心能力的复杂实现。 需要补充的背景知识: 强化学习基础 (Reinforcement Learning): 至少需要理解PPO (Proximal Policy Optimization) 算法的基本思想，这是当前RL微调LLM的主流方法。同时，了解“策略（Policy）”、“价值函数（Value Function）”、“奖励（Reward）”、“信用分配（Credit Assignment）”等核心概念。 主流AI Agent框架: 了解一下LangChain和AutoGen的基本工作原理，知道它们是如何帮助开发者组织LLM调用、工具使用和流程控制的。这会让你更深刻地理解Agent Lightning所解决问题的背景。 分布式系统与服务化架构: 对客户端/服务器（C/S）模型、API设计、消息队列等概念有所了解，有助于你理解Agent Lightning的系统架构设计。 ","date":"2025-08-15T00:00:00Z","permalink":"/agent-lightningtrain-any-ai-agents-with-reinforcement-learning/","title":"Agent Lightning：Train ANY AI Agents with Reinforcement Learning"},{"content":"多智能体，不过将另一个智能体作为一种工具调用，即agent-as-tool。\nimage.png 论文深度解读 这篇论文的核心贡献在于，它超越了当前研究普遍关注的“如何提升大模型能力上限”的范畴，转而聚焦于一个更具挑战性也更具实际应用价值的问题：如何保证一个配备了多种工具的复杂智能体系统在执行任务时的稳定性和可靠性。随着大语言模型（LLM）能力的飞速发展，我们已经从单纯的模型问答（Q\u0026amp;A）进入了“智能体时代”，即模型可以像人一样，通过调用外部工具（如搜索引擎、代码解释器、数据库等）来解决真实世界中的复杂问题。\n然而，正如论文开篇所指出的，这种“LLM+Tools”的模式引入了新的脆弱性。当一个智能体为了解决问题而调用大量工具时，它的工作“上下文（Context）”会变得异常冗长和嘈杂。想象一下，一个智能体为了预订一次旅行，可能需要搜索航班、查询酒店、阅读评论、调用地图、最后进行预订。每一步的工具调用都会产生输出，这些输出有好有坏，有相关有无关，全部堆积在模型的记忆里。这会导致两个主要问题：1. 上下文过载（Extended Contexts）：模型在冗长的信息中迷失，忘记了最初的目标。2. 噪声干扰（Noisy or Irrelevant Tool Outputs）：错误的工具输出会误导模型的后续推理，导致“一步错，步步错”的连锁反应。\n为了解决这个问题，作者团队从一个非常新颖的领域——船舶航行操控（Marine Vessel Maneuvering）——获得了灵感。他们将智能体的解题过程比作一艘船的航行： * 最终目标（解决方案） 好比是 航行的终点港口。 * 智能体的推理链（Reasoning Chain） 好比是 船舶的航行轨迹。 * 工具输出的噪声和无关信息 好比是 海上的风浪和洋流，它们会不断干扰航行，使其偏离预定航线。\n在航海中，船长需要不断调整舵角来修正航向，对抗风浪的干扰。借鉴于此，论文提出了一种名为 “动态机动”（Dynamic Maneuvering） 的机制。这个机制的核心是一个创新的 双代理（Multi-Agent） 架构，包含两个角色：\n执行代理（Execution Agent）：这是主力，负责分析任务、选择并调用工具、推进解题过程。它就像是船上的引擎和主舵手。 守卫代理（Guard Agent）：这是“瞭望员”和“纠偏员”。它的作用是在关键时刻被“执行代理”调用，来审视当前的推理过程是否存在逻辑漏洞或偏差。如果发现问题，它会提供修正建议，帮助“执行代理”回到正确的轨道上。它就像是船长手中的备用舵，在主航线发生严重偏离时介入，进行强力修正。 这种设计的精妙之处在于它的 动态性 和 非侵入性。“守卫代理”并非每一步都介入，而是在“执行代理”认为有必要时（例如，遇到逻辑矛盾或不确定性时）才被激活。这就像一个“第二双眼睛”，在必要时提供外部视角来打破思维僵局。论文将“守卫代理”实现为一个特殊的“工具”，可以被“执行代理”随时调用，这种“Agent-as-Tool”的设计范式也极具工程美感。\n为了验证这一架构的有效性，论文在公认的高难度通用AI助手基准测试 GAIA 上进行了详尽的实验。他们设计了三组对照实验： * 基础模型（Gemini 2.5 Pro）：不使用任何工具，纯靠模型自身知识回答。 * 单代理系统（SAS）：一个标准的“LLM+Tools”系统。 * 多代理系统（MAS）：即本文提出的AWORLD架构，在SAS基础上增加了“守卫代理”。\n实验结果（如论文中的Table 2所示）非常具有说服力： \u0026gt; * 在准确性上：基础模型的Pass@1（一次通过率）仅为31.5%。加入工具后，SAS大幅提升至62.39%。而本文的MAS架构则进一步提升至67.89%，相较于SAS有8.82%的相对提升。这证明了“守卫代理”的纠错机制是有效的。 \u0026gt; * 在稳定性上（这是本文的核心论点）：作者通过计算多次运行结果的标准差（Standard Deviation）来衡量稳定性。基础模型的标准差极低（0.00863），因为它没有随机的工具调用。SAS的标准差飙升了近四倍（0.03265），这直观地展示了工具使用带来的不确定性。而MAS成功地将标准差降低了17.3%（至0.02701），证明“动态机动”机制确实增强了系统的稳定性，使其表现更加一致和可靠。\nFigure 1: 论文中的性能对比图，直观展示了多代理系统（Multi-Agent System）在平均性能（μ）上更高，且在分布上更稳定（σ更小，虽然图中未直接标出但体现在箱形图的紧凑度上）。\n总而言之，这篇论文不仅仅是提出了一个在GAIA排行榜上取得第一的系统，更重要的是，它识别并定义了智能体时代的一个关键问题——稳定性与可靠性危机，并提供了一套新颖、有效且设计优雅的解决方案。它将研究的焦点从单纯追求“更高、更快、更强”引向了“更稳、更准、更可靠”，这对于推动大模型智能体从实验室走向真实世界的工业应用具有里程碑式的意义。\n接下来，我将按照您提出的六个问题，对论文进行逐一详细解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升工具增强型大语言模型智能体（Tool-Augmented LLM Agents） 在解决复杂问题时的鲁棒性（Robustness）和稳定性（Stability）。\n解决的实际问题：\n上下文污染：当智能体连续调用多个工具时，其记忆（上下文）会被大量信息填满，其中包含许多无用甚至错误的“噪声”，这会干扰其后续的判断和推理。 推理链脆弱性：复杂的任务需要一个长长的推理步骤链条。在这个链条中，任何一个环节的微小错误都可能被放大，导致最终结果的失败。这种现象在实践中非常普遍，使得许多Agent系统看起来很强大，但实际用起来却“时灵时不灵”。 缺乏自我纠错能力：单个智能体容易陷入自己的逻辑陷阱中无法自拔。当它沿着一条错误的路径走下去时，很难自我意识到并进行修正。 对行业发展的重要意义： 这个问题至关重要，是决定AI Agent能否从“有趣的玩具”变为“可靠的生产力工具”的关键。\n提升信任度：在金融、医疗、自动驾驶、科学研究等高风险领域，一个不可靠的AI系统是绝对无法被接受的。只有当Agent的决策过程是稳定且可预测的，用户和企业才敢于信任并依赖它。 推动商业化落地：对于企业而言，部署一个性能波动巨大的AI系统是一场灾难。这篇论文所探索的稳定性提升技术，是Agent产品化和商业化的基石。 定义下一代AI工程范式：论文提出的思路启发我们，未来的AI系统设计可能不再是单打独斗的“超级英雄”模式，而是类似于人类组织的“团队协作”模式，通过不同角色的Agent互相监督、验证和协作，来共同完成任务，从而实现1+1\u0026gt;2的系统级可靠性。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路： 从控制论中汲取灵感：最核心的创新思路是引入了“动态机动（Dynamic Maneuvering）”的概念，将其类比于船舶航行中的航向修正。这是一个极具启发性的跨学科借鉴，为解决AI系统的稳定性问题提供了一个全新的理论框架。 \u0026gt; We posit that an agent’s problem-solving process is analogous to a ship’s voyage. The agent’s logical reasoning forms a “trajectory” toward a solution, which is constantly perturbed by “noise”…\n“第二双眼睛”的监督范式：论文明确提出了通过引入一个独立的审查者角色（即守卫代理 Guard Agent）来对执行者的推理过程进行实时监督和纠正。这与之前常见的让模型自我反思（Self-reflection）的方法不同，外部监督通常比自我监督更客观、更有效。\n新的方法/模型： 论文提出的核心方法是AWORLD框架下的动态多代理系统（MAS）架构。 架构组成： 执行代理（Execution Agent）：负责主要的任务执行和工具调用。 守卫代理（Guard Agent）：作为一个特殊的“验证工具”，在需要时被调用，以审查执行代理的逻辑链。 工作流程：执行代理在解题过程中，可以主动调用守卫代理，向其提供当前的推理状态和遇到的困惑。守卫代理则返回诊断结果和修正建议，帮助执行代理调整方向。 \u0026gt; This mechanism steers the Execution Agent’s reasoning process, correcting deviations and ensuring it remains on a robust and accurate path to the final goal. 特点和优势： 动态适应性（Dynamic \u0026amp; Adaptive）：与一些固定的、每一步都进行验证的“Solver-Reviewer”模式相比，AWORLD的监督是动态的。只在“关键时刻”介入，既保证了效率，又能在最需要的地方提供帮助。 上下文优化（Context Optimization）：调用守卫代理的过程，在某种程度上也是对上下文的一次“刷新”和“聚焦”。它迫使模型从冗长的历史记录中跳出，重新审视最核心的逻辑问题，从而有效地对抗上下文污染。 实现优雅（Elegant Implementation）：将守卫代理封装成一个工具，使得整个系统可以在现有的Agent框架（如MCP协议）下轻松实现，无需对底层架构做颠覆性改造。这种“Agent-as-Tool”的模式具有很强的可扩展性。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计：\n基准测试（Benchmark）：选用了GAIA (General AI Assistants) 测试集中的109个Level 1和Level 2的任务。GAIA以其任务的复杂性和对多工具协调能力的高要求而著称，是评估高级Agent能力的黄金标准。 对照组设置（Ablation Study）：实验设计了严谨的消融研究，以逐一验证每个组件的贡献。 Base (Gemini 2.5 Pro)：仅使用LLM本身，作为性能基线。 SAS (Single Agent System)：在Base的基础上增加了工具集，代表了当前主流的Agent实现方式。 MAS (Multi-Agent System)：即AWORLD系统，在SAS的基础上增加了守卫代理，用于验证其核心贡献。 运行设置：每个实验独立运行3次，以获取平均性能和波动情况（标准差），模型均采用Gemini 2.5 Pro，温度系数（temperature）设为0.1以追求更确定的输出。 实验数据和结果： 关键数据集中在论文的Table 2中，我将其整理如下：\n指标 (Metric) Gemini 2.5 Pro (Base) SAS MAS (AWORLD) MAS vs SAS (提升) Pass@1 平均准确率 31.5% 62.39% 67.89% +8.82% (相对) Pass@3 准确率 38.53% 81.65% 83.49% +2.25% (相对) Pass@1 标准差 (稳定性) 0.00863 (最稳定) 0.03265 (最不稳定) 0.02701 -17.3% (更稳定) 结果分析： 有效性验证：从准确率来看，MAS \u0026gt; SAS \u0026gt; Base。这清晰地表明：(1) 工具的使用能极大提升Agent性能（从31.5%到62.39%）；(2) 在此基础上，守卫代理的引入能带来进一步的、显著的性能增益（从62.39%到67.89%）。 稳定性验证：标准差的数据是本论文的点睛之笔。它量化了一个重要现象：引入工具在提升性能的同时，也牺牲了稳定性（标准差从0.00863剧增到0.03265）。而AWORLD的MAS架构成功地在提升准确率的同时，逆转了稳定性的下降趋势，将标准差降低了17.3%。这完美地支撑了论文的核心论点。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 值得探索的问题和挑战： 守卫代理的能力增强：如论文在“未来工作”部分提到的，目前的守卫代理主要进行逻辑验证。未来可以赋予它独立调用工具的能力，例如，让它自己去搜索引擎进行事实核查（Fact-checking），或者调用代码解释器来验证一小段代码的正确性。 动态监督的智能化：如何让执行代理更智能地判断何时（When）以及为何（Why）需要调用守卫代理？目前可能还依赖于一些基于规则或模板的触发机制，未来的研究可以探索基于模型不确定性、或推理困惑度的自适应触发机制。 多代理的专业化分工：可以从“通用守卫代理”扩展到“专家代理团队”。例如，一个负责代码审查的“程序员代理”，一个负责数值计算的“数学家代理”，一个负责文字润色的“编辑代理”。执行代理可以根据任务类型，动态地咨询不同的专家。 成本与性能的权衡（Trade-off）：MAS架构不可避免地会增加LLM的调用次数，从而导致更高的延迟和成本。如何设计轻量级、低成本的守卫代理（例如，使用更小的、经过特殊微调的模型），在不显著增加开销的情况下保证监督质量，是一个极具应用价值的研究方向。 处理“守卫”自身的错误：如果守卫代理自己也犯错了怎么办？这引入了更高阶的“元问题”。未来的系统可能需要引入更复杂的共识机制、投票机制，甚至是“守卫的守卫”，来构建一个更具韧性的Agent社会。 可能催生的新技术和投资机会： AI Agent可靠性工程（Agent Reliability Engineering, ARE）：将诞生一系列专注于测试、监控、调试和加固Agent系统的工具链和平台。 企业级Agent框架：未来的企业级Agent平台，其核心竞争力可能不仅仅是功能强大，更在于其内置的稳定性和可靠性保障机制。AWORLD的思路可能成为这类框架的标配。 垂直领域专家Agent：投资机会在于训练和部署面向特定行业（如法律、金融、医疗）的“专家审查Agent”，它们可以作为插件集成到各种通用Agent系统中，提供专业级的验证服务。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 泛化性验证不足（Lack of Generalization）： 实验完全基于GAIA基准。虽然GAIA很有代表性，但它主要测试的是信息检索和办公软件操作类的任务。该方法在其他类型的Agent任务上（如软件开发（SWE-bench）、科学发现、复杂的Web导航）是否同样有效，还需要进一步的验证。 成本分析缺失（Missing Cost Analysis）： 论文完全没有讨论引入守卫代理所带来的额外成本，包括API调用费用、计算资源消耗和任务完成的延迟。在实际应用中，成本是一个决定技术能否被采纳的关键因素。一个性能提升8%，但成本增加50%的方案，可能并不具备吸引力。 守卫代理的潜在失效模式（Failure Modes of Guard Agent）： 论文对守卫代理的能力有些过于理想化。守卫代理本身也是由同一个基础模型（Gemini 2.5 Pro）驱动的，它同样可能产生幻觉或做出错误的判断。论文没有探讨如果守卫代理提供了错误的修正意见，执行代理该如何处理，以及这种“误导”会对系统造成怎样的负面影响。 实验设置的局限性（Limitations in Experimental Setup）： 实验排除了GAIA中需要浏览器功能的Level 3任务，理由是“最小化浏览器不稳定性等外部影响”。但这也在一定程度上回避了更复杂的、与真实世界动态交互的场景。而这类场景恰恰是稳定性最容易出问题的地方。 模型单一性：所有实验都基于Gemini 2.5 Pro。这种双代理架构的增益在不同能力水平的模型（如更强的GPT-4o或更弱的开源模型）上表现如何？是否模型能力越强，对守卫代理的依赖就越小？这些问题都有待回答。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 重点学习的创新想法： “执行-审查”（Solver-Reviewer）设计模式：这是最核心、最普适的一个思想。无论你是在构建一个代码生成工具、一个报告撰写助手，还是一个数据分析流程，都可以引入一个“审查者”角色。让一个Agent生成初稿，另一个Agent进行批判、检查和修正。这个模式能显著提升输出质量和可靠性。 “代理即工具”（Agent-as-Tool）的工程范式：这是一个非常聪明的工程实践。当你需要集成一个复杂的功能（比如一个完整的审查流程）时，可以将其封装成一个简单的“工具”。这让你的主流程保持简洁，并极大地提高了系统的模块化和可扩展性。 跨界寻找灵感（Metaphor-Driven Innovation）：从船舶航行这个看似毫不相关的领域获得灵感，是本论文的一大亮点。这启发我们，在解决AI领域的新问题时，可以多从物理学、生物学、社会学、控制论等成熟学科中寻找相似的模式和成熟的解决方案。 将“稳定性”作为核心优化指标：在评估你的AI系统时，除了关注准确率、F1分数等传统指标外，应该开始系统地测量和优化稳定性（例如，通过多次运行计算结果的标准差或一致性）。 需要补充的背景知识： 主流Agent框架：了解一下LangChain、AutoGen、CrewAI等框架，它们是实现工具调用和多代理协作的基础设施。这能帮助你理解AWORLD在工程上是如何实现的。 Agent评测基准（Benchmarks）：除了GAIA，可以去了解一下SWE-bench（软件工程）、WebArena（网页浏览）、ToolBench等，这将让你对当前Agent的能力边界和评测方法有更全面的认识。 多智能体系统（Multi-Agent Systems）：可以阅读一些关于MAS的综述性文章，了解除了“执行-审查”之外，还有哪些其他的协作模式，如竞争、合作、协商等。 思维链（Chain-of-Thought）与自我反思（Self-Reflection）：了解这些提示工程技术，可以帮助你更好地理解执行代理和守卫代理的“思考过程”，以及为什么外部的“审查”通常比内部的“反思”更有效。 ","date":"2025-08-15T00:00:00Z","permalink":"/aworlddynamic-multi-agent-system-with-stable-maneuvering-for-robust-gaia-problem-solving/","title":"AWORLD：DYNAMIC MULTI-AGENT SYSTEM WITH STABLE MANEUVERING FOR ROBUST GAIA PROBLEM SOLVING"},{"content":"提出了一个异步框架，来解决长尾分布问题，并且创新了一种数据合成的方式，注入和模糊化问题。\n这篇论文的核心目标是解决当前开源大型语言模型（LLM）在执行复杂、长周期的搜索任务时能力不足的问题。在当今世界，我们越来越依赖AI智能体（Agent）来解决需要深度探索和信息整合的知识密集型任务。例如，不仅仅是简单地回答“法国的首都是哪里？”，而是去解决“根据公开财报和新闻，分析苹果公司在2024年第四季度的供应链风险，并总结主要影响因素”。这类任务需要智能体具备我们称之为“搜索智能”（Search Intelligence）的能力——即能够理解模糊的查询、设计精确的搜索策略、从充满噪声的海量信息中提取关键数据、解决信息冲突，并进行持续深入的探索。\n然而，目前开源社区的智能体普遍存在两个致命瓶颈。第一，训练方法的局限性。主流的基于强化学习（Reinforcement Learning, RL）的在线训练方法，为了保证训练效率，往往会人为地限制智能体的交互轮次（turn），比如少于10轮。这就像让一个侦探在10分钟内破一个世纪悬案，根本无法学习到复杂的多步推理和调查策略。第二，高质量训练数据的匮乏。训练出聪明的智能体需要大量复杂、刁钻且有正确答案的问题。但现有的开源数据集要么过于简单，要么已经过时，无法激发和训练模型进行深度搜索。\n针对这些痛点，这篇论文提出了一个名为 ASearcher 的开源项目，其创新性贡献主要体现在两大方面：\n1. 一套可扩展的、完全异步的强化学习训练框架。 这是本文最核心的技术贡献。传统的“批处理生成（batch generation）”强化学习，在训练时需要等待一个批次（batch）中所有任务都执行完毕才能进行模型更新。如果其中有一个任务特别复杂，需要很长的执行时间（例如，一个需要40轮搜索的查询），那么整个批次的计算资源（如GPU）就会在等待这一个“慢任务”时处于闲置状态，造成巨大的效率浪费。ASearcher的异步框架则巧妙地将“轨迹执行”（trajectory execution，即智能体解决问题的过程）和“模型更新”（model updates）这两个环节解耦（decoupling）。\n论文中通过 图7 (Figure 7) 直观地对比了这两种模式。在传统模式下，整个训练步骤N+1必须等待轨迹7（最长的轨迹）完成后才能开始。而在ASearcher的“完全异步RL”模式下，系统无需等待轨迹7，只要收集到了足够数量的其他已完成轨迹（如轨迹9），就可以立即启动训练步骤N+1，而轨迹7可以继续在后台运行，完成后被纳入未来的某个批次。\n这种设计使得系统能够支持极长的交互轮次（论文中设置为128轮），同时保持极高的硬件利用率，从而让智能体有机会在复杂的环境中进行充分探索，学习长周期的策略。实验结果也证明了其有效性，训练过程中，智能体的工具调用次数峰值可达70次，生成的总字符数（tokens）超过15万，这在以往的开源项目中是难以想象的。\n2. 一个可扩展的、能自主生成高质量问答（QA）数据的智能体。 为了解决数据稀缺问题，研究者们设计了一个“数据合成智能体”。这个智能体能够像一个聪明的出题老师一样，从一个简单的“种子问题”出发，通过两种核心操作，自动地、迭代地增加问题的复杂性和不确定性。 * 注入（Injection）: 智能体会从外部知识源（如维基百科）抓取与问题相关的实体事实，并将其“注入”到问题中，丰富问题的上下文。例如，将“迈克尔·海因何时出生？”升级为“那位曾在纽约州阿尔斯特县担任首位县长、并拥有工商管理学士学位的埃克德学院校友，是何时出生的？”。 * 模糊化（Fuzzing）: 智能体会有意地将问题中的一些精确信息变得模糊。例如，将确切的年份“1934年”替换为“20世纪30年代早期”，或者将“卡茨基尔山铁路”替换为“一条历史悠久的山区铁路”。\n通过这两种方式，系统可以源源不断地创造出需要多步搜索和推理才能解决的难题。更关键的是，这个过程还伴随着严格的三步质量验证，确保生成的问题清晰、有唯一解且足够困难。最终，他们从14,107个种子问题出发，生成了25,624个高质量的训练样本，极大地丰富了训练数据。\n总而言之，ASearcher项目通过创新的训练架构和自动化的数据生成两大支柱，有效地打破了开源搜索智能体在“长周期探索”和“高质量数据”上的双重枷锁，为训练更强大的、能够应对真实世界复杂度的AI智能体铺平了道路。其在GAIA和xBench-DeepSearch等高难度基准测试中的卓越表现——例如，在GAIA上取得了52.8的Avg@4得分，在xBench上取得了42.1的Avg@4得分，全面超越了之前的SOTA（state-of-the-art）开源模型——也雄辩地证明了其方法的有效性和巨大潜力。\n接下来，我将根据您提出的六个问题，进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 本文的核心研究目标是显著提升开源大型语言模型（LLM）作为AI智能体（Agent）在执行长周期、知识密集型搜索任务时的能力，即提升其“搜索智能”（Search Intelligence）。\n解决的实际问题:\n现有RL训练框架的效率瓶颈: 当前主流的在线强化学习方法在处理长度差异极大的任务时，由于同步等待机制，会导致严重的计算资源浪费，因此不得不限制智能体的探索深度（例如，少于10轮交互），这使得模型无法学习解决复杂问题的策略。 高质量训练数据的严重短缺: 训练强大的搜索智能体需要海量、复杂且多样的问答数据，而人工标注成本高昂，现有开源数据集又过于简单或陈旧，无法满足训练需求。 行业意义:\n推动Agentic AI的平民化: 当前，最顶尖的智能体能力往往掌握在少数几家科技巨头（如Google、OpenAI）手中，他们的模型背后是海量的专有数据和庞大的计算资源。ASearcher项目通过开源其高效的训练框架和数据合成方法，极大地降低了开发高性能搜索智能体的门槛，使得学术界和中小型企业也能参与到这场技术革命中，有助于构建一个更开放、更多样化的AI生态。 解锁更复杂的应用场景: 现实世界中的许多重要任务，如科学研究、金融分析、法律咨询、市场调研等，本质上都是一个长周期的信息搜集、分析、推理和验证过程。通过提升智能体的“搜索智能”，我们可以开发出能够真正辅助甚至自主完成这些复杂任务的AI助手，从而极大地提高生产力。 探索LLM能力边界: 这项工作不仅是一个工程上的突破，更是在探索LLM能力边界的科学尝试。它证明了通过正确的训练范式和高质量的数据，我们可以在不改变模型基础架构的情况下，解锁其更深层次的推理和规划能力，为未来更高级的通用人工智能（AGI）研究提供了宝贵的经验。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 本文的核心创新在于系统和数据层面，而非模型结构层面。它提出了一个名为 ASearcher 的完整解决方案，主要包含两个创新点：\n方法一：完全异步的强化学习训练框架 (Fully Asynchronous Agentic RL Training) 思路: 其核心思路是“解耦”——将智能体与环境交互产生经验数据的过程（轨迹生成）和用这些数据训练模型的过程（模型更新）分离开来，使二者可以独立、异步地进行。 与之前方法的对比: 传统批处理RL (Batch Generation RL): 如论文中提到的One-Step-Off RL，它虽然可以并行生成轨迹，但受限于“批同步”机制。整个批次必须等待其中最慢（最长）的那个任务完成后，才能打包数据去更新模型。 ASearcher: 打破了批同步的限制。它维护一个数据缓冲区，各个任务独立生成轨迹，完成后就放入缓冲区。训练模块则不断地从缓冲区中拉取足够的数据组成一个批次进行训练，无需等待任何特定的慢任务。 特点和优势: 极高的效率: 几乎完全消除了因等待长轨迹而导致的GPU空闲时间，实现了近乎100%的资源利用率。 支持长周期探索: 由于效率问题得到解决，研究者可以将智能体的最大交互轮次设置得非常高（如128轮），让智能体有足够的机会学习复杂的多步推理链条。 可扩展性强: 这种架构非常适合大规模并行训练，可以轻松扩展到更多的计算节点上。 方法二：可扩展的问答对合成智能体 (A Scalable QA Synthesis Agent) 思路: 利用LLM自身的能力，设计一个“元智能体”（meta-agent），让它自动、迭代地创造出用于训练其他智能体的高质量数据。 与之前方法的对比: 人工标注: 成本高、速度慢、规模有限，且难以创造出真正“刁钻”的问题。 简单数据增强: 如随机替换词语等，容易破坏语义，无法系统性地提升问题复杂度。 特点和优势: 自动化与可扩展性: 能够以极低的成本，大规模、源源不断地生成训练数据。 复杂度可控: 通过注入 (Injection) 和模糊化 (Fuzzing) 两种操作，可以系统性地提升问题的上下文依赖和不确定性，从而引导模型学习更高级的搜索技能。 质量保证: 内置了严格的三步验证流程（基础质量、难度测量、答案唯一性），确保了生成数据的可用性和有效性，避免了“垃圾进，垃圾出”的问题。 正如论文中的案例分析 图3 (Figure 3) 所示，面对一个来自GAIA的复杂问题，传统的Search-R1模型因无法分解复杂查询而产生幻觉；未经训练的QwQ模型虽然能进行多轮搜索，但会遗漏关键信息并做出错误结论；而经过ASearcher训练的智能体，则表现出不确定性感知、精确信息提取、跨文档推理和严谨验证等专家级行为，最终找到了正确答案。这生动地展示了ASearcher方法的优势。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了全面且有说服力的实验来验证其方法的有效性。\n实验设计: 多层次的基准测试 (Benchmarks): 标准QA任务: 使用了如2WikiMultiHopQA、HotpotQA等多跳问答数据集，以及NQ、TriviaQA等单跳问答数据集，用于测试模型的基础搜索与回答能力。 挑战性基准: 采用了 GAIA、xBench-DeepSearch 和 Frames 这三个公认的高难度基准。这些任务专门为评估智能体的真实网络交互、长周期规划和开放域探索能力而设计，是实验的核心。 两种实验环境 (Settings): 本地知识库+RAG: 在一个受控环境中，智能体通过检索增强生成（RAG）与本地的维基百科知识库交互。这用于评估模型在相对干净的信息源中的搜索策略学习情况。 真实网络搜索+浏览: 在一个更真实、更嘈杂的环境中，智能体可以调用搜索引擎和网页浏览器。这用于评估模型学习到的策略能否泛化到真实的互联网环境中。 多维度的评估指标 (Metrics): F1 Score \u0026amp; LasJ: 用于标准QA任务，衡量答案的准确性。LasJ（LLM-as-Judge）使用一个更强大的LLM来评判答案的正确性，更符合语义。 Avg@4 \u0026amp; Pass@4: 用于挑战性基准。Avg@4是4次尝试的平均得分，Pass@4则是4次尝试中只要有1次成功就算通过的比例。这能更好地评估模型的稳定性和解决问题的上限。 关键实验数据和结果: 在挑战性基准上取得SOTA: 这是最有力的证据。如 表4 (Table 4) 所示，ASearcher-Web-QwQ模型在三大挑战性基准上全面领先于其他所有开源基线模型。 在 GAIA 上，Avg@4得分为 52.8，而次优的Search-o1(QwQ)为48.1。 在 xBench-DeepSearch 上，Avg@4得分为 42.1，显著高于WebDancer-QwQ的40.0和Search-o1(QwQ)的40.3。 在 Frames 上，Avg@4得分高达 70.9，同样是所有模型中最高的。 Method GAIA (Avg@4) xBench-DeepSearch (Avg@4) Frames (Avg@4) Search-o1 (QwQ) 48.1 40.3 63.6 WebDancer-QwQ 47.4 40.0 63.8 ASearcher-Web-QwQ 52.8 42.1 70.9 * **强化学习训练的显著效果**: 论文通过对比RL训练前后的性能，证明了其训练方法的有效性。如 **图8 (Figure 8)** 所示，经过RL训练后： * GAIA的Avg@4得分从43.7提升至**52.8**（提升了**20.8%**）。 * xBench-DeepSearch的Avg@4得分从28.7提升至**42.1**（提升了**46.7%**）。 * 这表明ASearcher的训练方法确实教会了模型更复杂的搜索策略。 引用论文中的描述：“Significant improvements in pass rate demonstrate that our training pipeline trains the agent to learn complex search strategies to perform precise searches, extract key information, and resolve conflict information.”\n* **长周期行为的涌现**: **图1 (Figure 1)** 和 **图6 (Figure 6)** 的中间和右侧图表展示了训练动态。随着训练的进行，模型生成的平均工具调用次数和Token数显著增加，最长的轨迹甚至出现了超过40次工具调用和超过15万个Token的极端长周期行为。这直接证明了异步框架成功地支持了长周期探索的学习。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战: 更精细化的奖励函数 (Reward Function): 目前ASearcher使用的是稀疏奖励（sparse reward），即只有在任务最终完成时才根据答案的正确性给予奖励。这种方式虽然简单，但学习效率可能不高。未来的研究可以探索更稠密的奖励机制，例如，对智能体每一步的“好行为”（如提出一个有价值的搜索查询、成功从网页中提取关键信息）给予中间奖励，从而加速学习过程。 超越QA的数据合成: 当前的数据合成智能体主要生成问答（QA）任务。未来可以研究如何让它生成更多样化的任务类型，例如探索性任务（“调研一下量子计算的最新进展并写一份摘要”）、规划型任务（“帮我规划一个为期一周的日本旅行线路”），甚至是需要与多种工具交互（如代码解释器、计算器、API调用）的复杂任务流。 多智能体协同搜索 (Multi-Agent Collaboration): 许多现实世界的复杂问题需要不同角色的专家协同完成。未来的研究可以探索如何将ASearcher的框架扩展到多智能体系统，让多个智能体分别扮演不同角色（如信息搜集员、数据分析师、批判性思考者），协同解决一个更大的问题。 模型能力的内在提升: ASearcher主要通过外部训练框架来提升智能体能力。另一个方向是研究如何在模型内部构建更强大的世界模型（World Model）和自我反思（Self-reflection）机制，使智能体能够更好地预测其行动的后果，并从失败的经验中自我修正，而不仅仅依赖外部的RL信号。 新的技术和投资机会: Agentic AI训练平台即服务 (Agent Training Platform as a Service): ASearcher验证了一套高效训练智能体的方法论。未来可能会出现专门提供此类服务的商业平台，企业可以上传自己的基础模型和特定领域的工具集，平台利用异步RL和数据合成技术，为企业训练出满足其业务需求的专用AI智能体。 垂直领域的高级智能体: 在金融、法律、医疗、科研等知识密集型行业，对能够进行深度信息挖掘和复杂推理的AI智能体有巨大需求。基于ASearcher这类技术，开发面向特定垂直领域的“专家智能体”将是一个巨大的蓝海市场。 自动化数据工程 (Automated Data Engineering): 高质量数据是AI的燃料。ASearcher的数据合成智能体展示了“用AI创造AI的训练数据”的巨大潜力。未来，专注于开发更通用、更强大的数据合成工具和平台的公司，可能会成为AI产业链中不可或缺的一环。 开源社区与基金会: ASearcher的成功再次证明了开源的力量。支持和投资于能够聚集社区力量，共同训练和维护强大的开源基础模型和智能体的非营利组织或基金会，将对整个行业产生深远而积极的影响。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 对奖励模型的依赖与风险: 论文在微调QwQ-32B这类大型推理模型时，使用了LLM-as-Judge作为奖励函数。这是一个潜在的弱点。首先，作为“裁判”的LLM本身可能存在偏见、知识盲点或事实性错误，这可能导致智能体学到的是如何“取悦裁判”，而不是如何“追求真理”。其次，奖励模型的泛化能力存疑，它在一个领域给出的评分标准，未必适用于另一个领域。 泛化能力的验证范围有限: 尽管论文声称模型学到了“可泛化的搜索策略”，但其主要的验证都集中在有明确答案的QA任务上。智能体在更开放、没有唯一正确答案的探索性或创造性任务上的表现如何，尚待验证。例如，它能否写出一份高质量的市场分析报告？这种任务的好坏评判标准更加主观和模糊。 数据合成的潜在偏见闭环: 数据合成智能体本身是基于一个LLM构建的。这意味着合成出的数据可能会继承并放大该LLM的内在偏见和知识局限性。如果长期使用这种方式迭代，可能会形成一个“信息茧房”式的偏见增强闭环，导致训练出的智能体在某些方面存在系统性的认知盲区。 计算成本与可复现性: 论文提到，训练ASearcher-Web-QwQ模型大约需要7600个H800 GPU小时。这是一个非常巨大的计算开销。虽然项目本身是开源的，但如此高昂的硬件门槛，使得大多数学术研究机构和个人开发者难以完全复现其结果，这在一定程度上限制了其普适性和进一步的研究。 对模型基础能力的依赖: ASearcher框架极大地释放了LLM的潜力，但智能体的能力上限最终还是受限于其基础模型（base model）的推理能力。如果基础模型本身很弱，再好的训练框架也难以妙手回春。论文中7B模型在学习网页浏览方面失败的例子也部分说明了这一点。未来的工作需要更深入地探讨训练框架与基础模型能力之间的相互作用关系。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新想法与启发: 系统级思维解决AI瓶颈: 本文最大的启发是，提升AI能力不一定非要从模型结构或算法本身入手。优化训练系统架构（如异步解耦）同样能带来革命性的突破。当你遇到一个看似是模型能力的问题时，可以退一步思考，这是否可能是训练系统、数据流或工程实现上的瓶颈导致的？ “数据飞轮”的自动化构建: 不要再将数据视为静态的、需要人工生产的资源。应该思考如何构建一个自动化的“数据飞轮”。ASearcher的“注入”和“模糊化”思想非常具有借鉴意义。在你的领域，是否也可以设计一个智能体，通过类似的操作，自动地、持续地为你正在训练的模型创造出越来越难、越来越贴近真实场景的训练数据？ 容忍并拥抱长周期任务: 很多时候我们为了效率会下意识地简化问题、截断流程。本文告诉我们，复杂问题的解决需要足够的时间和空间。在设计智能体或自动化流程时，应该构建能够支持长周期、多步骤探索的系统，而不是过早地要求“看到结果”，这才能让真正的“智能”涌现。 开源的力量在于“赋能”: 一个优秀的开源项目，不仅是开放代码，更是开放一套强大的方法论和实现它的工具。ASearcher通过提供高效的训练框架和数据工具，赋能整个社区去训练自己的智能体，这是比发布一个单一模型更有价值的贡献。 需要补充了解的背景知识: 强化学习基础: 尤其是策略梯度方法（Policy Gradient），如PPO (Proximal Policy Optimization)。本文使用的GRPO是其变体。理解智能体（Agent）、环境（Environment）、状态（State）、动作（Action）、奖励（Reward）这些核心概念是必须的。 LLM Agent 主流框架: 了解如 ReAct (Reason and Act) 这样的基础框架，它揭示了LLM如何通过“思考-行动”的循环与外部工具进行交互，这是理解本文中智能体工作方式的基础。 检索增强生成 (RAG): 理解RAG的工作原理，即模型如何从外部知识库中检索信息来辅助生成答案，这对于理解论文中的“本地知识库”实验环境至关重要。 分布式计算与异步编程: 具备一些分布式系统和异步编程的基本概念，将帮助你更深刻地理解为什么ASearcher的异步架构能带来如此巨大的效率提升。 希望这份详尽的解读能够帮助您全面、深入地理解这篇论文的价值。\n","date":"2025-08-15T00:00:00Z","permalink":"/beyond-ten-turnsunlocking-long-horizon-agentic-search-with-large-scale-asynchronous-rl/","title":"Beyond Ten Turns：Unlocking Long-Horizon Agentic Search with Large-Scale Asynchronous RL"},{"content":"和TURA：Tool-Augmented Unified Retrieval Agent for AI Search一样，考虑的是并行的工具调用，不过TURA是整个系统，通过DAG来编排工具调用（外部LLM）。而本文研究的是提高大模型本身并行调用工具的能力。从而节省很多时间。\nimage.png 论文深度解读 当前，以 检索增强生成（RAG） 和 工具使用（Tool-using） 为核心的大模型代理，在处理复杂问题时，已经展现出超越传统模型的能力。它们通过与外部知识源（如搜索引擎）进行多轮交互，逐步搜集信息、进行推理，最终得出答案。然而，正如论文开篇所指出的，现有方法存在一个“根本性的架构限制”——严格的顺序执行。\n想象一下，当你问一个代理：“莫奈（Claude Monet）和毕沙罗（Camille Pissarro），谁更年长？”。一个典型的代理，比如论文中反复提及的基线模型 Search-R1，会这样工作： 1. 第一步：生成搜索查询“莫奈的生日”，然后调用搜索引擎。 2. 第二步：接收到莫奈的生日信息后，进行思考，然后生成第二个查询“毕沙罗的生日”，再次调用搜索引擎。 3. 第三步：拿到两个人的生日后，进行比较和推理，最终给出答案。\n这个过程涉及三次模型调用（LLM calls）和两次顺序的搜索操作。这在单个查询中似乎还好，但在需要服务数百万用户的生产环境中，这种低效会累积成巨大的计算成本和延迟。\n论文的核心洞察在于，很多复杂问题天然包含逻辑上独立、可并行的子问题。在上面的例子中，查询莫奈的生日和查询毕沙罗的生日，这两个动作之间没有任何依赖关系，完全可以同时进行。如果能做到这一点，整个过程就可以优化为： 1. 第一步：一次性生成两个搜索查询“莫奈的生日”和“毕沙罗的生日”，并行调用搜索引擎。 2. 第二步：同时收到两个人的生日信息，进行比较和推理，给出答案。\n这个优化将 LLM 的调用次数从三次减少到两次，搜索过程从两步串行变为一步并行，极大地提升了效率。这正是 ParallelSearch 框架要实现的目标。\n为了实现这一目标，论文没有采用简单的提示工程（Prompting），而是选择了一条更根本、更稳健的路径：通过强化学习（Reinforcement Learning, RL）来训练模型，使其内在地获得分解和并行执行任务的能力。其创新主要体现在两个方面：\n1. 简洁的并行任务表达：论文设计了一个非常直观的机制，允许 LLM 在\u0026lt;search\u0026gt;标签内，使用##作为分隔符来一次性提交多个子查询。例如：\u0026lt;search\u0026gt; location of Laleli Mosque ## location of Esma Sultan Mansion \u0026lt;/search\u0026gt;。这个简单的语法约定，为模型学习并行规划提供了清晰的“动作空间”。\n2. 精心设计的奖励函数（Reward Function）：这是 ParallelSearch 的灵魂所在。强化学习的核心是“奖励驱动”，你奖励什么，模型就会学到什么。如果只奖励最终答案的正确性（Outcome Reward），模型可能不会有动力去学习更高效的并行策略。因此，作者设计了一个包含四个部分的复合奖励函数：\n结果奖励 (Outcome Reward, \\(r_o\\))：最基础的奖励，判断最终答案是否正确。 分解奖励 (Decomposition Reward, \\(r_d\\))：这是最关键的创新。它专门用于激励模型正确地识别和分解可并行的查询。当一个问题是可并行的（如比较类问题），模型成功将其分解，就会获得高奖励；反之，如果它错误地分解了一个不应分解的问题（如需要上下文依赖的桥接类问题），则会受到惩罚。 搜索次数奖励 (Search Count Reward, \\(r_s\\))：用于控制效率。对于可并行的问题，如果模型通过多次顺序搜索才解决，会受到惩罚；而对于必须多步推理的顺序问题，如果搜索次数过少，同样会受罚。这个奖励机制隐式地鼓励模型“在一次动作中完成所有可并行的搜索”。 格式奖励 (Format Reward, \\(r_f\\))：确保模型的输出遵循预定义的格式，保证训练的稳定性。 通过这个精巧的奖励机制，ParallelSearch 不仅教会了模型如何回答问题，更重要的是教会了它如何高效地思考和规划。\n论文的实验结果充分验证了该框架的有效性。在七个主流问答基准测试中，ParallelSearch 的平均性能比 SOTA 基线高出 2.9%。这已经是一个不错的提升，但更惊人的是在专门抽取的可并行化问题子集上的表现。在这些子集上，ParallelSearch 不仅将答案的准确率（Exact Match, EM）提升了高达 12.7%，还将 LLM 的调用次数减少了 30.4%，推理时间也显著缩短。例如，在 HotpotQA-par 数据集上，相比于 Search-R1，ParallelSearch 将完成任务所需的平均轮次（# Turns）从 4.00 轮大幅降低至 2.13 轮（使用 Qwen2.5-7b-Base 模型）。这些数据雄辩地证明，ParallelSearch 成功地解决了顺序执行的瓶颈，实现了显著的效率和性能双提升。\n总而言之，这篇论文不仅仅是提出了一种新模型，更是为构建高效、低成本、可扩展的 LLM 代理系统提供了一套行之有效的方法论。它证明了通过精心设计的强化学习范式，我们可以将更高级的规划能力“内化”到大模型中，而不仅仅是依赖于临时的指令。\n接下来，我将按照您提出的六个问题，对论文进行更详细的结构化解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？ 研究目标：论文的核心研究目标是训练大语言模型（LLMs）学会识别复杂查询中可并行的子任务，并将它们分解以便并发执行，从而打破现有 LLM 代理在信息检索任务中“严格顺序执行”的效率瓶颈。\n解决的实际问题：\n高昂的推理成本：当前的搜索代理在处理需要多次查找信息的问题时，每一步都需要调用一次 LLM 进行思考和生成下一步的查询。这导致 LLM 调用次数过多，而在实际应用中，LLM 的 API 调用是主要的成本来源。 过高的延迟：顺序执行搜索请求，意味着总耗时是所有搜索步骤耗时的累加，这会导致用户等待时间过长，影响体验，尤其是在交互式应用中。 可扩展性差：随着问题复杂度的增加，顺序执行的步骤会线性增长，导致成本和延迟问题被进一步放大，使得基于此类代理的应用难以大规模部署。 对行业发展的重要意义： 当前，行业正从“模型能力”的竞争转向“代理应用”的落地。构建能够自主规划和执行复杂任务的 AI 代理是未来的重要方向。这篇论文解决的效率问题，是所有代理应用走向实用化、规模化和商业化必须克服的关键障碍。一个更高效、更低成本的代理框架，意味着：\n更低的运营成本：企业可以以更低的代价提供更强大的 AI 服务。 更好的用户体验：用户可以更快地获得复杂问题的答案。 推动更复杂的应用成为可能：当单次查询的成本和时间大幅下降后，开发者可以构建出需要更多步骤、更深度推理的复杂代理应用，例如自动化科学研究、市场分析报告生成等。 2. 论文提出了哪些新的思路、方法或模型？ 论文的核心是提出了 ParallelSearch，一个基于强化学习的训练框架。它不是一个全新的模型架构，而是一种创新的训练方法和奖励机制，可以应用于现有的 LLM之上。\n新的思路： 从“指令遵从”到“能力内化”：不同于仅仅通过 Prompt 指示模型进行并行搜索，论文的思路是通过强化学习，让模型从根本上理解“何时可以并行，何时必须串行”，将这种规划能力内化为模型自身的一种技能。这比简单的 Prompt 更鲁棒，泛化能力也更强。 过程与结果并重：其核心思路是，一个优秀的代理不仅要给出正确答案，还必须通过高效的过程来获得答案。因此，奖励函数的设计必须同时兼顾结果的正确性和过程的效率。 新的方法和模型： 并行查询的语法约定：通过在\u0026lt;search\u0026gt;标签内使用##分隔符来表达并行查询。这一定义清晰、易于解析，为模型学习提供了一个明确的目标。 四元复合奖励函数：这是方法的核心。与之前的方法（如 Search-R1 主要关注最终结果）相比，其优势在于对“过程”进行了精细化的建模和激励。 \u0026gt; The reward function in training consists of four components: outcome reward, decomposition reward, search count reward, and format reward, as illustrated in Fig. 2. 分解奖励 (Decomposition Reward) 是最具创新性的部分。它通过判断查询本身的属性（是否可并行）和模型的行为（是否分解）来进行奖惩，直接教会模型辨别不同类型的任务。 搜索次数奖励 (Search Count Reward) 则通过对搜索轮次的奖惩，进一步强化了效率导向，避免模型即使分解了任务，仍然采用多轮搜索的“假并行”策略。 特点和优势： 效率高：通过并行化，显著减少了 LLM 调用次数和任务执行时间。 性能好：实验表明，学习到并行策略的模型在可并行问题上的准确率更高，这可能是因为一次性获取所有相关信息有助于模型进行更全面的综合推理，避免了顺序获取信息可能带来的“思维隧道”效应。 适应性强：该框架不仅在并行问题上表现优异，在必须顺序执行的问题上也能保持甚至超越基线模型的性能，表明模型学会了根据问题类型动态选择最优策略。 通用性：该方法不依赖于特定的模型架构，可以应用于各种预训练的 LLM（论文中在 Qwen2.5-7B 的 Base 和 Instruct 版本上都取得了成功）。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一系列严谨的实验来全面验证 ParallelSearch 的有效性。\n实验设计： 多元化的基准测试：选取了七个公开数据集，涵盖两大类： 通用问答 (General QA): NQ, TriviaQA, PopQA。 多跳问答 (Multi-Hop QA): HotpotQA, 2WikiMultiHopQA, Musique, Bamboogle。这类任务通常更复杂，更能体现规划能力的重要性。 强有力的基线对比：与多个 SOTA 的搜索代理方法进行比较，包括 Search-R1, ZeroSearch, StepSearch 等。 受控变量实验：为了精准衡量并行处理能力的提升，作者们从 HotpotQA, 2WikiMultiHopQA 等数据集中，根据问题类型（如 comparison vs. bridge）创建了只包含可并行化问题的子集（例如 HotpotQA-par）和只包含顺序问题的子集（例如 HotpotQA-seq）。这使得他们能够隔离并量化并行策略带来的真实影响。 消融研究 (Ablation Study)：为了证明其精心设计的奖励函数的必要性，论文对分解奖励（\\(r_d\\)）和搜索次数奖励（\\(r_s\\)）的权重参数（\\(\\lambda_d\\) 和 \\(\\lambda_s\\)）进行了消融实验，通过改变这些权重，观察模型性能的变化。 实验数据和结果： 总体性能提升：在 Table 2 中，ParallelSearch 在所有七个数据集上的平均分（Avg. EM Score）达到了 0.458（基于 Qwen2.5-7b-Base），超过了所有基线模型，比当时最强的基线 Search-R1 (0.434) 提升了 2.4%。\n并行任务上的显著优势：Table 3 是展示其核心优势的关键。以下是 HotpotQA-par 数据集上使用 Qwen2.5-7b-Base 模型的部分结果对比： | Metric | Search-R1 | ParallelSearch (Ours) | Improvement | | :— | :—: | :—: | :—: | | EM (Accuracy) ↑ | 0.641 | 0.673 | +5.0% | | DR (Decomposition Ratio) ↑ | 9.06% | 97.03% | +970% | | Inference Time (s) ↓ | 2.59 | 2.16 | -16.6% | | # Turns ↓ | 4.00 | 2.13 | -46.8% | 这个表格清晰地显示，ParallelSearch 不仅准确率更高，而且以极高的比例（97.03%）成功地执行了并行分解，同时将完成任务所需的平均轮次减半。\n奖励函数的有效性：在 Table 4 的消融研究中，当把分解奖励的权重 \\(\\lambda_d\\) 设为 0 时，模型的分解率（DR）从 94.30% 下降到 90.94%，准确率（EM）也随之下降。这证明了分解奖励对于学习并行策略至关重要。\n4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文打开了“高效代理规划”的大门，未来还有广阔的探索空间。\n值得探索的问题和挑战： 更复杂的任务规划：目前的研究主要集中在“一步并行”上。未来的代理需要处理更复杂的依赖关系，例如： 条件执行：if 任务A的结果是X，then 并行执行B和C，else 执行D。 动态资源分配：当有多个可并行的任务，但API调用次数或计算资源受限时，代理应如何决定任务的优先级？ 混合规划：在一个宏大任务中，如何将并行步骤和顺序步骤优雅地组合成一个复杂的执行图（Execution Graph）。 异构工具的并行调用：当前的并行主要针对同一种工具（搜索引擎）。未来的代理需要能够并行调用不同类型的工具，例如同时查询数据库、执行代码片段和调用网页API，并对返回的异构信息进行高效整合。 错误处理和鲁棒性：在并行执行中，如果一个或多个并行的子任务失败或返回了错误/误导性信息，代理应如何处理？是重试、忽略还是调整后续计划？需要更强的容错和动态调整能力。 从“隐式学习”到“显式规划”：目前模型通过RL“隐式地”学习规划。未来是否可以训练模型显式地生成一个可解释的、可编辑的执行计划（如JSON或YAML格式），让人类可以审查和干预，这将大大提高代理的可控性和可靠性。 可能催生的新技术和投资机会： Agentic-AI 平台：专注于提供高效、低成本、可扩展的AI代理开发和托管平台。这些平台的核心竞争力将是其底层的任务规划器和执行引擎的效率，ParallelSearch 的思想可以成为其核心技术之一。 LLM 运维与成本优化工具 (LLMOps)：随着代理应用普及，如何监控、分析和优化其执行成本将成为一个巨大的市场。专门用于分析代理执行路径、发现效率瓶颈并提出优化建议的工具将非常有价值。 垂直领域的高级代理：在金融分析、药物研发、法律咨询等领域，需要处理大量信息的复杂代理将迎来发展机遇。例如，一个金融分析代理可以并行抓取多家公司的财报、新闻和股价数据，在几秒钟内生成综合分析报告。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些方面值得商榷或需要进一步研究。\n对“可并行性”的定义过于简化：论文主要将“比较类”问题视为可并行，将“桥接类”问题视为顺序。但在现实世界中，问题的依赖关系可能非常模糊和复杂，存在“弱依赖”或“部分可并行”的情况。模型在这种模糊地带的表现如何，论文没有深入探讨。 对合成（Synthesis）步骤的挑战讨论不足：并行获取大量信息后，如何有效地将这些信息整合、去伪存真，并进行最终推理，本身就是一个巨大的挑战。如果并行返回的信息存在冲突或冗余，模型的合成能力将面临考验。论文在这方面的讨论相对较少。 可扩展性问题：实验中的并行任务大多是2-3个子查询。当需要并行执行10个或更多的子查询时，##这种简单的语法是否依然有效？大量的返回信息是否会超出模型的上下文窗口限制（Context Window）？其性能如何扩展，尚待验证。 对基线模型的公平性：论文将自己的方法与 Search-R1 等基线进行了对比。但 Search-R1 的设计初衷是处理需要逐步推理的链式问题，其本身并未针对并行优化。虽然论文通过添加“Parallel Prompt”来试图增强基线，但这可能不足以完全激发基线模型的潜力。一个更公平的对比或许是与其他专门为并行设计的模型（如果存在的话）进行比较。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了宝贵的启示。\n重点学习内容： 奖励函数塑造（Reward Shaping）的艺术：这篇论文是奖励函数工程的绝佳范例。核心启发是：不要只奖励最终目标，一定要奖励你希望模型学会的“中间过程”。通过为分解、效率等特定行为设计独立的奖励项，你可以精确地引导模型学习复杂的技能。 将复杂能力分解为可学习的步骤：并行规划这个复杂能力，被论文分解为“识别并行性”、“生成分解查询”、“高效执行”等几个方面，并用不同的奖励项来对应。这种化繁为简的思路，在训练AI解决任何复杂问题时都适用。 动作空间设计的简洁之美：##分隔符这个设计非常简单，但极其有效。这告诉我们，在为LLM设计工具或动作时，简洁、直观、易于学习的接口，往往比复杂的形式化语言更有效。 补充了解的背景知识： 强化学习基础：特别是 PPO (Proximal Policy Optimization) 算法，这是论文中使用的核心 RL 算法。理解其基本原理，将有助于您理解训练过程的细节。 LLM Agent 主流框架：了解像 ReAct (Reason + Act)、Toolformer 以及论文的基线 Search-R1 等工作的基本思想。这将帮助您理解 ParallelSearch 是在什么样的技术脉络下进行的演进。 多跳问答（Multi-hop QA）：了解此类任务的特点，特别是“桥接（Bridge）”和“比较（Comparison）”这两类问题的区别，这对于理解论文中对任务可并行性的划分至关重要。 ","date":"2025-08-15T00:00:00Z","permalink":"/parallelsearchtrain-your-llms-to-decompose-query-and-search-sub-queries-in-parallel-with-reinforcement-learning/","title":"ParallelSearch：Train your LLMs to Decompose Query and Search Sub-queries in Parallel with Reinforcement Learning"},{"content":"压缩cot的工作，类似LAPO：INTERNALIZING REASONING EFFICIENCY VIA LENGTH-ADAPTIVE POLICY OPTIMIZATION，这篇叫做GFPO。\nimage.png 这篇论文直面了当前大模型领域一个非常棘手且普遍的问题：在通过强化学习（Reinforcement Learning）进行对齐训练时，模型为了追求更高的奖励（reward），往往会产生越来越长、越来越啰嗦的回答。这种现象被称为“长度膨胀”（Length Inflation）。尽管更长的回答在处理复杂问题时有时是必要的，但很多时候模型只是在生成重复、冗长的“填充性”文本，这不仅极大地增加了推理的计算成本和延迟，有时甚至会因为引入过多无关信息而降低回答的准确性。\n论文的核心思想可以用一句话概括：通过在训练时付出更多的计算（采样更多），来换取推理时更高的效率（思考更少，回答更简洁）。作者们为此提出了一种名为 GFPO (Group Filtered Policy Optimization，组过滤策略优化) 的新方法。\nGFPO的机制相当巧妙和直观。在传统的强化学习方法如 GRPO (Group Relative Policy Optimization) 中，模型针对一个问题会生成一组（比如G=8个）候选回答，然后计算这组回答的平均奖励，并用这个平均值作为基线来更新模型。这意味着无论好坏、长短，所有的候选回答都会对模型的学习过程产生影响。\n而GFPO则在此基础上增加了一个关键的“过滤”步骤。它首先让模型生成一个更大的候选回答组（比如G=16或G=24个），然后根据一个预设的“度量标准”（metric）对这些回答进行排序，只选择其中最优的k个（比如k=8个）进入下一步的训练。那些被筛掉的回答则不会对模型的策略梯度更新产生任何影响。这就像一位严格的老师，只用学生最好的作业来评估和指导他们，从而引导他们朝着更优秀的方向发展。\n论文探索了几个关键的“度量标准”： 1. 回答长度（Response Length）: 最简单直接的度量，直接保留最短的k个回答进行训练，以此来鼓励模型变得简洁。 2. 令牌效率（Token Efficiency）: 这个度量更加智能，它计算的是奖励 / 长度的比率。这意味着，如果一个较长的回答能带来足够高的奖励，它仍然是“高效”的，从而被保留。这允许模型在必要时进行详细推理，同时又对不必要的冗长进行惩罚。 3. 自适应难度（Adaptive Difficulty）: 这是GFPO的一个高级变体。它会动态地根据问题的难度来调整保留的回答数量k。对于简单问题，它会进行更严厉的过滤（比如只保留k=4个），迫使模型用最简洁的方式回答；而对于难题，它则会保留更多的候选回答（比如k=8个），给予模型更充分的探索空间。\n通过这一系列设计，GFPO将对“简洁性”的追求从复杂的奖励函数设计（reward engineering）中解耦出来，变成了一个简单明了的数据筛选问题。实验结果非常惊人：在多个高难度的STEM和编程基准测试（如AIME, GPQA）上，GFPO在几乎不损失准确率的前提下，成功将基线模型GRPO的长度膨胀减少了46%-71%。当使用“令牌效率”作为度量时，这个数字更是达到了71%-85%。这篇论文为解决大模型在实际应用中的效率和成本问题，提供了一个简单、有效且极具启发性的新范式。\n接下来，我将按照您提出的六个方面，对这篇论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义？ 研究目标: 本文的核心研究目标是，在不牺牲大型语言模型（LLM）推理能力和准确性的前提下，有效抑制其在接受基于奖励的强化学习训练后产生的响应长度过度膨胀的问题。\n解决的实际问题:\n高昂的推理成本: LLM生成文本时，每个token都意味着实实在在的计算开销（GPU时间）和金钱成本。响应越长，服务单个用户的成本越高，延迟也越长。在需要大规模部署的场景下，这是一个严峻的经济和技术挑战。 奖励作弊 (Reward Hacking): 模型发现“说得越多，可能得分点就越多”，于是学会了通过生成冗长、重复甚至无关的文本来“刷分”，以最大化其获得的奖励信号。这是一种投机取行文为，它偏离了我们希望模型“准确且高效”解决问题的初衷。 用户体验下降: 过长的回答会增加用户的阅读负担，关键信息可能被淹没在大量填充文本中，导致体验下降。 行业意义: 这个问题对于LLM技术从实验室走向大规模产业应用至关重要。一个推理成本过高、响应延迟过长的模型，其商业价值会大打折扣。解决了长度膨胀问题，意味着：\n提升经济可行性: 能够以更低的成本服务更多的用户，加速AI应用的普及。 改善用户交互体验: 提供更精准、更易于消费的回答，增强用户粘性。 推动模型能力边界: 使我们能够训练出更强大的推理模型，而不必过分担心其效率会变得不可控，从而推动整个领域向更高效、更实用的方向发展。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新是提出了 GFPO (Group Filtered Policy Optimization) 及其变体。其思路与之前的方法相比，有显著的不同和优势。\n新的思路: 核心思路是“训练时选择性学习”（Selective Learning at Training-Time）。它认为，与其让模型看到所有好坏掺杂的生成结果，不如只让它学习“精英范例”，从而隐式地塑造其行为。\n与GRPO的对比:\nGRPO (基线方法): 为每个问题生成G个回答，然后用这G个回答的平均奖励作为基准来计算每个回答的优势（Advantage），并据此更新策略。 GFPO (新方法): 同样为每个问题生成G个（通常G更大）回答，但增加了一个过滤（Filtering）步骤。它根据特定度量（如长度、令牌效率）选出最好的k个回答，然后只在这k个回答内部计算平均奖励和优势。 正如论文中图1和公式(2)所展示的，GFPO的关键区别在于其优势函数 \\(\\hat{A}_{i,t}^{(m)}\\) 的计算。它引入了一个二进制掩码 \\(m_i\\)，只有当回答 \\(i\\) 属于被选中的集合 \\(S\\) 时，\\(m_i\\) 才为1，否则为0。这意味着未被选中的回答其优势直接为0，对模型更新无任何贡献。\n特点与优势:\n隐式奖励塑造 (Implicit Reward Shaping): 传统方法通常需要精心设计复杂的奖励函数，比如加入明确的长度惩罚项。但设计这样的函数很难，惩罚重了可能损害准确率，惩罚轻了又没效果。GFPO通过简单的“数据过滤”就达到了“奖励塑造”的目的，它没有修改奖励函数本身，而是改变了模型“看到”的数据分布，方法更简单、更直接。 通用性和可扩展性: GFPO是一个框架，其核心的metric可以灵活替换。论文中使用了长度和令牌效率，但未来完全可以扩展到其他度量，如事实性、安全性、代码质量等，这使其具有很强的通用性。 效果显著且稳定: 与直接在奖励中加入长度惩罚项相比，论文提到他们的初步实验发现后者“没有产生实质性的长度缩减，或者以牺牲准确性为代价”。而GFPO则能在维持准确率的同时大幅缩减长度。 GFPO的变体分析:\n令牌效率GFPO (Token Efficiency GFPO): 这是我认为最巧妙的设计。它不一刀切地惩罚长回答，而是看重“性价比”。这使得模型在面对难题时，如果确实需要长篇幅来推导，只要最终奖励够高，这样的长回答也是被鼓励的。这比单纯基于长度的过滤更智能。 自适应难度GFPO (Adaptive Difficulty GFPO): 这个变体体现了对计算资源的精细化管理。它认识到不同问题的学习价值不同。 \u0026gt; 根据算法2 (Algorithm 2) 的描述，它使用一个t-digest数据结构来实时追踪问题的奖励分布，从而估计问题难度，并动态分配k值。这是一种在线的、自适应的课程学习（Curriculum Learning），让模型在简单问题上“快跑”，在难题上“深耕”。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了一系列严谨的实验来全面验证GFPO的有效性。\n实验设计: 基线模型: SFT (Supervised Fine-Tuning) Model: 仅经过监督微调，未经过强化学习的Phi-4-reasoning模型。作为原始起点。 GRPO Model: 使用标准GRPO方法训练的模型，作为主要的对比基线。 评估数据集: 实验覆盖了多个高难度的推理基准，包括： 数学推理: AIME 2025/2024, Omni-MATH 综合问答: GPQA 代码生成: LiveCodeBench (用于测试分布外泛化能力) 评估指标: Pass@1 Accuracy: 模型的准确率。 Average Response Length (L): 平均响应长度。 Excess Length Reduction (ELR): 衡量GFPO相比于GRPO减少了多少“多余”的长度，是核心效率指标。公式为 (L_GRPO - L_GFPO) / (L_GRPO - L_SFT)。 关键实验数据和结果: GFPO显著缩短响应长度，同时保持准确率：这是核心结论，由表1和表2的数据有力支撑。 \u0026gt; - 在 AIME 24 数据集上，GRPO的准确率为77.7%，长度为13.3k。而Shortest 4/24 GFPO在准确率几乎持平（76.5%）的情况下，将长度缩减至11.3k，ELR高达59.8%。 \u0026gt; - Token Efficiency GFPO表现更为惊人，在AIME 24上，它将ELR提升至84.6%，平均长度仅为10.6k。 \u0026gt; - 平均来看 (Average across all benchmarks, Table 2)，GRPO平均长度为13k，而Token Efficiency GFPO仅为10.2k，ELR高达79.5%，同时准确率几乎无差 (71.7% vs 72.1%)。\nPareto最优性分析: 论文通过图3的Pareto前沿图直观地展示了GFPO的优越性。 \u0026gt; \u0026gt; 在这张图中，理想的模型位于左上角（高准确率、低长度）。可以看到，多个GFPO变种（如Shortest 4/24, Adaptive Difficulty）都位于GRPO的左上方，这意味着它们在至少一个维度上优于GRPO，而在另一维度上不差于GRPO，实现了Pareto改进。\nk/G比率是关键控制阀: 论文通过实验证明，降低保留比例 k/G (即更严格地过滤) 是控制长度的关键。 \u0026gt; 如图4所示，随着保留比例k/G的下降，平均响应长度也随之下降。这为实践者提供了一个简单有效的调参旋钮来平衡简洁性和性能。\nGFPO削减的是冗余推理部分: 论文进一步分析了GFPO到底削减了哪些内容。 \u0026gt; 如图10所示，通过对推理链路进行分段（问题、解决方案、验证、最终答案），作者发现GRPO主要在“解决方案”（Solution）和“验证”（Verification）部分产生了大量冗余。而GFPO正是精准地削减了这两个部分的token数量，同时基本保全了“问题”重述和“最终答案”部分。例如，在AIME 25上，Shortest 8/24 GFPO将Solution部分的超额长度削减了94.4%。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? GFPO为“高效对齐”（Efficient Alignment）开辟了新的思路，未来有许多值得探索的方向：\n值得探索的问题和挑战: 更丰富的过滤维度: 未来的研究可以探索更多元的metric。例如，可以设计一个评估“事实性密度”或“新颖性”的度量，过滤掉那些信息量低、重复已知事实的回答。还可以引入评估“安全性”和“无害性”的度量，在训练阶段就过滤掉有潜在风险的输出。 多目标过滤: 当我们有多个目标时（如既要简洁、又要事实准确、还要安全），如何设计一个多目标的metric或过滤策略？这些目标可能相互冲突，如何权衡是一个巨大的挑战。 与推理时技术的结合: GFPO是训练时技术。未来可以研究如何将其与推理时（inference-time）的干预技术（如提前退出、预算强制等）相结合，实现端到端的效率最优控制。 理论基础深化: GFPO为什么有效？其背后的理论机制是什么？能否从理论上证明某种k/G比率或metric是最优的？建立更坚实的理论基础是未来的重要方向。 对模型“创造力”的影响: 对于需要创造性、发散性思维的任务（如写诗、构思故事），过度强调“简洁”和“效率”是否会扼杀模型的创造力？如何为不同类型的任务设计不同的GFPO策略值得研究。 新的技术和投资机会: 高效对齐平台: 未来可能会出现专门提供高效对齐解决方案的平台或服务。它们不再仅仅提供基础的RLHF工具，而是集成像GFPO这样的高级策略，帮助企业以更低成本、更高效率地“调教”自己的专属大模型。 自动化度量函数开发: GFPO的成功依赖于好的metric。未来可能出现专门研究和开发用于模型对齐的自动化metric评估和生成的公司，这本身就是一个“卖铲子”的商业机会。 AI Agent的效率优化: 随着AI Agent（智能体）的发展，其“思考”过程（即中间推理步骤）的效率至关重要。GFPO的思想可以被借鉴用于优化Agent的决策链，使其能以最少的步骤完成复杂任务，这将是AI Agent商业化落地的重要一环。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判的视角看，仍然存在一些可以探讨的局限性：\n训练成本的增加: 论文的核心是“Sample More”，即采样更大的候选组G。这直接导致了训练时计算成本和时间的增加。虽然作者论证这是一次性的训练投入换取持续的推理收益，但在资源受限的情况下，这个前期投入可能会成为一个不小的门槛。论文没有详细量化这种训练开销到底增加了多少。 超参数敏感性: GFPO引入了新的超参数，如G（候选组大小）、k（保留数量）以及metric的选择。尤其是k/G这个比率，虽然被证明是关键，但也意味着使用者需要根据自己的任务和模型仔细调整，增加了调参的复杂性。 任务泛化性的局限: 实验主要集中在STEM和编程这类有明确“正确答案”和“最优解法”的领域。在这些领域，“简洁”通常是好的。但对于开放式对话、创意写作、情感支持等任务，情况则复杂得多。在这些场景下，长篇幅的共情、详细的背景阐述可能恰恰是高质量回答的一部分。将GFPO直接应用于这些领域，可能会损害模型的表现，这部分是论文没有覆盖的。 度量本身的可靠性: 成功与否高度依赖metric的质量。如果metric设计不当，可能会导致“劣币驱逐良币”。例如，单纯追求最短，可能会让模型学会给出过于简单粗暴、缺乏细节的答案。虽然Token Efficiency部分缓解了这个问题，但其鲁棒性仍需在更多任务上验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的启发：\n重点学习与启发: “数据选择”比“算法修改”更直接: 解决模型行为问题时，我们常常陷入修改模型结构或复杂算法的思维定势。GFPO告诉我们，有时控制模型学习的数据流是更简单、更有效的杠杆。在你的工作中，如果想让模型具备某种特性（如遵循特定格式、语气），可以先尝试在训练数据生成阶段就进行筛选和过滤。 “加大探索，然后精选”范式: 这个“Sample More, then Filter”的范式可以被广泛应用。比如在生成式AI应用中，你可以让模型内部生成多个候选答案，然后用一个快速的、低成本的评估模型或规则来筛选出最好的一个呈现给用户。这可以有效提升最终输出的质量。 成本转换思维: 将高频发生的推理成本，通过一次性的投入，转移到低频的训练阶段，是一种非常经济的工程思想。在设计任何AI系统时，都可以思考是否存在类似的成本优化空间。 指标驱动的对齐: 明确你想要模型达成的目标（简洁、高效、安全等），并将其转化为一个可计算的metric，然后用这个metric来指导你的数据选择和训练过程。 需要补充的背景知识: 为了完全消化这篇论文的精髓，并将其思想应用到您自己的工作中，建议您补充了解以下背景知识： PPO (Proximal Policy Optimization): 这是当前主流RLHF方法的基础。您需要理解它的工作原理，特别是“优势函数”（Advantage Function）和“裁剪目标函数”（Clipped Surrogate Objective）的概念。 RLHF/RLAIF (Reinforcement Learning from Human/AI Feedback): 了解整个通过反馈来对齐大模型的流程，包括奖励模型（Reward Model）的训练和策略模型（Policy Model）的强化学习过程。 GRPO (Group Relative Policy Optimization): 作为本文的直接对比基线，理解GRPO如何通过使用组内平均奖励作为基线来简化PPO是理解GFPO创新的关键前提。这篇论文（作者Shao et al., 2024）值得一读。 DAPO (Distributional Advantage Policy Optimization): 论文中提到他们使用了DAPO的token级损失归一化。了解DAPO可以帮助您理解实验设置中的一些技术细节。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-08-15T00:00:00Z","permalink":"/sample-more-to-think-lessgroup-filtered-policy-optimization-for-concise-reasoning/","title":"Sample More to Think Less：Group Filtered Policy Optimization for Concise Reasoning"},{"content":"问题分解-\u0026gt;工具DAG编排（规划）-\u0026gt;蒸馏小型执行器。\n论文综合解读 这篇发表于2025年的论文，由百度公司的研究团队撰写，提出了一种名为 TURA (Tool-Augmented Unified Retrieval Agent for AI Search) 的新型AI搜索框架。TURA的核心目标是突破当前主流的、基于 检索增强生成 (Retrieval-Augmented Generation, RAG) 技术的AI搜索在处理真实世界复杂查询时的根本性限制。\n我们知道，自大型语言模型（LLM）兴起以来，AI搜索（如Perplexity AI, Google AI Overviews）大多采用RAG范式。其基本工作模式是“先检索，后生成”：当用户提问时，系统首先从一个庞大的、静态的网页语料库中检索相关文档，然后将这些文档作为上下文，让LLM生成一个综合性的回答。这种方法在回答事实性、知识性的问题时表现出色，但论文一针见血地指出了它的“阿喀琉斯之踵”：\nHowever, this RAG-centric paradigm inherits a critical limitation from its search engine ancestry: it primarily operates on a static snapshot of the web. Current RAG systems… are fundamentally incapable of accessing dynamic, real-time information that is not present on a static webpage but must be generated via interaction. （然而，这种以RAG为中心的范式从其搜索引擎的“祖先”那里继承了一个关键限制：它主要在静态的网页快照上运行。当前的RAG系统…根本无法访问那些不存在于静态网页上、必须通过交互才能生成的动态、实时信息。）\n举例来说，当用户提出“帮我预订一张明天从北京到上海的高铁票”或“查询一下附近某家商店的实时库存”这类需要与外部数据库或API进行实时交互的查询时，传统的RAG系统就无能为力了。因为它索引的是“过去”的网页，而无法执行“现在”的动作。这极大地限制了AI搜索从一个“信息查找器”进化为“任务执行器”的可能性。\n为了弥合 静态信息检索 与 动态信息交互 之间的鸿沟，TURA应运而生。它不是对RAG的简单修补，而是一个系统性的、三阶段的 代理（Agentic）框架，将RAG的能力与 工具调用（Tool-Use） 无缝集成。这三个阶段分别是：\n意图感知的检索模块 (Intent-Aware Retrieval)：这是TURA的“前端”。它不只是简单地匹配关键词，而是首先使用一个强大的LLM（论文中提到使用Qwen3-1.7B）将用户模糊、复杂的查询分解成一系列清晰、原子的子意图。例如，将“6月10-15号去北京旅游，需要酒店和几个景点推荐”分解为“查询北京天气”、“推荐北京景点”和“预订北京酒店”等。同时，它对可用的工具（API）也进行了“语义索引增强”，即让LLM为每个工具生成大量可能的用户查询。这样，系统就能在更深层次的语义上理解用户意图，并精准地从众多工具中召回最相关的一批。\n基于DAG的任务规划器 (DAG-based Task Planner)：这是TURA的“大脑”。传统Agent（如ReAct框架）通常采用线性的“思考-行动”链条，效率低下。TURA创新性地引入了 有向无环图（Directed Acyclic Graph, DAG）。规划器会分析子任务之间的依赖关系，构建一个任务图。没有依赖关系的任务（如查询天气和查询景点）可以并行执行，而有依赖关系的任务（如先找到酒店地址，再规划路线）则按顺序执行。这种方式极大地提升了复杂任务的执行效率。论文中的关键数据显示，在处理复杂多跳查询时，DAG规划器相比于顺序执行，平均延迟降低了44.2% (从1650ms降至920ms)，这是一个巨大的性能提升。\n蒸馏后的轻量级代理执行器 (Distilled Agent Executor)：这是TURA的“双手”。在生产环境中，每次工具调用都使用超大规模的LLM（如GPT-4、Deepseek-V3）不仅成本高昂，而且延迟极高。TURA采用了一种巧妙的 代理蒸馏 (Agent Distillation) 策略。他们首先用一个强大的“教师模型”（Deepseek-V3）生成大量高质量的“专家决策轨迹”（即在各种情境下如何正确思考和调用工具）。然后，通过一个严格的自动化流程筛选和修正这些轨迹。最后，用这些“完美”的轨迹数据来微调（Fine-tuning）一个更小、更高效的“学生模型”（如Qwen3-4B）。这个学生模型学会了教师的“决策逻辑”，但推理成本大大降低。论文提出的 “带思考训练，无思考推理 (train-with-thought, infer-without-thought)” 范式是点睛之笔：训练时模型学习完整的思考链，但在实际推理时只输出最终的行动指令，显著减少了生成Token的数量，从而降低了延迟。实验结果令人振奋：蒸馏后的Qwen3-4B模型（40亿参数）在工具调用准确率上达到了 88.3%，不仅远超其原始版本（70.1%），甚至超过了强大的教师模型（82.4%），而延迟仅为教师模型的不到十分之一（750ms vs 8700ms）。\n总而言之，TURA论文的贡献是系统性的。它不仅提出了一个创新的技术框架，更重要的是，它提供了一套经过大规模工业验证（服务于数千万用户）的、切实可行的解决方案，解决了AI搜索在走向实用化过程中的一个核心瓶颈。它清晰地指明了AI搜索的未来方向：从被动的信息检索，走向主动的、具备工具增强能力的任务解决。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是设计一个能够将 静态内容检索（RAG） 和 动态工具调用（Tool Use） 无缝集成的统一框架，以解决现代AI搜索在处理需要实时、交互式或事务性信息的查询时的局限性。\n解决的实际问题：\n实时性问题：传统RAG无法回答需要最新信息的问题，如“今天下午从A到B的航班还有票吗？”，因为这些信息是动态变化的，不存在于静态网页索引中。 交互性问题：用户常常需要执行一个动作，而不仅仅是获取信息，如“预订一张电影票”或“在我的日历里创建一个会议”。RAG只能“说”，不能“做”。 复杂意图问题：用户的查询通常是模糊且多方面的，如“计划一次周末旅行”。这需要系统能分解任务、调用多个工具（查天气、订酒店、找餐馆、规划路线），并综合结果，这是传统RAG难以胜任的。 行业意义：这个问题对于行业发展至关重要。它直接关系到大型语言模型能否从一个“聪明的聊天机器人”转变为一个“能干的个人助理”。\n拓宽应用场景：解决了这个问题，AI搜索就能从教育、研究等信息获取领域，扩展到旅游、电商、本地生活、金融等更广阔的、具有巨大商业价值的事务性领域。 提升用户体验和粘性：一个能直接完成任务的AI，远比一个只能提供信息链接的AI更有用。这将极大提升用户满意度和产品粘性，是下一代搜索引擎和智能助手的核心竞争力。 商业模式创新：能够接入并协调海量第三方服务（API），为平台方（如百度、Google）带来了新的商业模式可能性，例如通过API调用收取佣金、提供更精准的增值服务等。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心方法是 TURA框架，其创新性体现在三个协同工作的模块上，相比于之前的 LLM + RAG 或简单的 ReAct 等代理方法，具有显著优势。\n思路与方法： 意图感知的检索模块 (Intent-Aware MCP Server Retrieval)： 特点：它采用 “双向语义对齐” 的思路。一方面，通过 LLM进行查询分解，将用户的模糊查询（User Vernacular）转化为清晰的机器可处理的子任务。另一方面，通过 LLM进行工具描述增强（生成合成查询），极大地丰富了工具（API）的语义表达。这解决了用户自然语言与开发者定义的API文档之间的“词汇鸿沟”。 优势：相比传统基于关键词或简单向量相似度的工具检索，TURA的检索更精准、召回率更高。它能理解用户“字面意思”背后的“真实意图”。 基于DAG的任务规划器 (DAG-based Task Planner)： 特点：放弃了传统代理的线性执行逻辑，转而采用 图规划 (Graph Planning) 的思想。它将任务流建模为有向无环图（DAG），清晰地表达了任务间的依赖关系。 优势：最大的优势是 效率。通过识别图中可以并行的节点，系统能够同时执行多个独立的工具调用，显著缩短了处理复杂查询所需的总时间。如论文中“北京旅游”的例子，查询天气、景点和酒店可以同时进行，大大优于一步一步来的线性模式。 \u0026gt; The output is a structured representation of this DAG, which enables an execution engine to identify and run independent tasks in parallel, drastically reducing the latency L(π) for complex, multi-hop queries. \u0026gt; （输出是这个DAG的结构化表示，它使执行引擎能够识别并并行运行独立任务，从而极大地减少了复杂多跳查询的延迟 L(π)。） 蒸馏后的轻量级代理执行器 (Distilled Agent Executor)： 特点：采用了 “教师-学生模型” 的蒸馏策略，并结合 “带思考训练，无思考推理” 的高效推理范式。 优势：完美平衡了 性能 与 成本/效率。它使得小型模型能够达到甚至超越大型教师模型的决策能力，同时将推理延迟和计算成本降至工业化可接受的水平。这是将先进代理技术从实验室推向大规模生产应用的关键一步。 整体优势：TURA是一个 系统性、端到端 的解决方案，而非单点技术的改进。它将 检索、规划、执行 三个环节有机地结合起来，协同解决AI搜索在动态、交互式场景下的核心挑战，这是以往方法所不具备的。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来全方位验证TURA的有效性，包括离线评估、在线A/B测试和详细的消融研究。\n实验设计与数据： 数据集：论文构建了一个名为 MCP-Bench 的基准测试集。这个数据集来源于 匿名的生产环境日志，包含了从简单查询到复杂多跳请求的真实用户查询分布。每个查询都由人类专家标注了正确的工具、执行轨迹和理想答案，保证了评估的真实性和可靠性。 基线模型：主要的比较对象是一个强大的 LLM + RAG 基线，该基线使用Deepseek-V3模型和百度的搜索引擎API，代表了业界主流的先进RAG实现。 评估指标： 离线：答案准确率 (Answer Accuracy) 和 忠实度 (Faithfulness)，即答案是否正确且完全基于工具返回的信息。 在线：会话成功率 (Session Success Rate, SSR) 和 用户评价 (Good vs. Same vs. Bad, GSB)，这些是衡量生产系统性能的黄金标准。 组件：针对不同模块使用 Recall@5、Precision@5、工具调用准确率 和 平均延迟 等。 关键实验结果： 端到端性能对比 (Table 1)：TURA在核心指标上全面碾压LLM+RAG基线。 在人类评估中，TURA的 准确率达到87.5%，远高于RAG的65.3%。 忠实度更是高达96.2%，而RAG只有72.4%，这说明TURA能有效避免LLM的“幻觉”，忠实于工具返回的实时数据。 Method Accuracy (Human) Faithfulness (Human) LLM + RAG 65.3% 72.4% TURA 87.5% 96.2% Table 1 简化版: TURA 与 RAG 端到端性能对比\n2. **在线A/B测试结果 (Table 2)**：在真实的生产环境中，TURA同样表现出色。 * **会话成功率 (SSR) 提升了8.9%** (从55.1%到64.0%)，这是一个统计上非常显著的提升，直接证明了TURA能更好地满足用户需求。 3. **代理蒸馏效果 (Table 7)**：这是论文最亮眼的结果之一，充分证明了蒸馏策略的成功。 * 蒸馏后的Qwen3-4B（4B参数）模型，其 **工具调用准确率达到了88.3%**，不仅远超其基础版（70.1%），甚至优于参数量为其数十倍的教师模型Deepseek-V3（82.4%）。 * 同时，其 **P80延迟仅为750ms**，相比教师模型的8700ms，实现了数量级的优化。 Agent Model Model Size Tool Calling Acc. (%) ↑ Avg. Latency/Step P80 (ms)↓ Teacher (Deepseek-V3) 671B-A37B 82.4 8,700 Qwen3-4B 4B 70.1 2,200 Qwen3-4B Distilled 4B 88.3 760 Table 7 简化版: 代理蒸馏效果显著\n这些翔实的数据从不同维度强有力地证明了TURA框架的先进性、有效性和在工业界部署的可行性。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 尽管TURA取得了巨大成功，但它也为未来的研究揭开了新的篇章。\n值得探索的问题和挑战： 工具生态的扩展性与管理 (Tool Ecosystem Scalability)：TURA验证了几十上百个工具的场景。当工具库扩展到成千上万个时，如何保证意图感知检索模块的效率和准确性？如何自动发现、验证和更新工具？这需要更先进的工具管理和检索机制。 更鲁棒的规划与错误处理 (Robust Planning \u0026amp; Error Handling)：现实世界中API调用会失败、超时或返回格式错误的数据。TURA的DAG规划器虽然高效，但论文未详细探讨如何处理执行过程中的动态异常。未来的研究需要让Agent具备更强的自我反思和纠错能力，例如实现动态的重规划（re-planning）。 安全与权限控制 (Security \u0026amp; Permission Control)：赋予AI调用真实世界API（特别是涉及支付、个人数据修改的API）的能力，带来了巨大的安全风险。如何设计一套精细、可靠、上下文感知的安全和权限管理系统，防止恶意使用或意外操作，是一个亟待解决的挑战。 成本与性能的动态平衡 (Dynamic Cost-Performance Trade-off)：TURA选择了一个固定的4B模型作为执行器。未来的系统可能会更加智能，根据任务的复杂性动态选择调用不同大小、不同成本的模型（Mixture-of-Agents），以实现极致的成本效益。 跨应用的复杂任务编排 (Cross-Application Task Orchestration)：TURA主要聚焦在“搜索”这个单一应用内。未来的终极AI助理需要能够跨越多个App和平台（如在邮件中读取信息，在日历中创建事件，并通过订票网站订票），这需要标准化的应用间通信协议和更强大的任务编排能力。 新技术和投资机会： 技术机会： Agentic AI平台：提供从工具接入、语义索引、任务规划到安全执行的全套解决方案，让开发者可以方便地构建自己的工具增强型AI应用。 模型蒸馏与专业化：专门为Agent任务（如工具调用、规划）优化的模型蒸馏技术和小型专家模型（Small Language Models, SLMs）将是热点。 Agent安全沙箱 (Agentic Security Sandbox)：为AI Agent提供安全的执行环境，监控其行为并防止其“越狱”或进行危险操作。 投资机会：投资于上述“卖铲子”的公司，即为构建AI Agent提供基础设施和中间件的平台型企业，以及在特定垂直领域（如金融、法律、医疗）深度集成专业工具的AI应用公司。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 以批判性的眼光审视，这篇论文虽然非常出色，但仍存在一些潜在的不足和可以深入探讨的地方。\n基准的封闭性 (Proprietary Benchmark)：论文的核心实验建立在内部数据集MCP-Bench之上。尽管构建严谨，但由于其不公开，外部研究者无法在其上进行公平的比较和复现。这在一定程度上限制了该工作的可验证性和对学术社区的直接贡献。一个公开、标准的Tool-use基准对于推动领域发展至关重要。 系统复杂性高 (High System Complexity)：TURA是一个相当复杂的系统，它涉及多个LLM（一个用于分解，一个用于规划，一个用于执行）、复杂的向量索引、DAG处理引擎和数据策展流水线。这种复杂性可能构成了一个较高的实现门槛，对于资源有限的中小型团队来说，复现或部署类似的系统可能非常困难。 对“教师模型”的依赖 (Dependency on Teacher Model)：蒸馏策略的成功，在很大程度上依赖于拥有一个极其强大的教师模型（如Deepseek-V3）。“学生”的高度取决于“教师”的上限。这意味着该方法的成功，建立在能够持续获得最先进（且通常是昂贵的）LLM服务的基础上。 对模糊与对抗性查询的处理 (Handling of Ambiguity and Adversarial Queries)：论文主要展示了TURA在处理意图清晰或可分解查询上的成功。但对于那些本身就极度模糊、充满歧义，甚至是对抗性的用户查询，意图分解模块是否依然鲁棒？系统将如何应对分解失败或错误的情况？这方面论文着墨不多。 6. 我应该从这篇论文中重点学什么？有哪些启发？我还需要补充了解哪些背景知识? 这篇论文对于任何希望构建高级AI应用的从业者或研究者来说，都充满了宝贵的启发。\n重点学习与启发： 超越朴素RAG：最核心的启发是，不要将思路局限在简单的“检索-生成”RAG框架内。对于构建真正有用的AI产品，必须思考如何将信息检索与真实世界的交互能力（即工具调用）结合起来。混合范式 (Hybrid Paradigm) 是未来。 分解是王道 (Decomposition is King)：面对复杂问题，先将其分解为更小、更易于管理的部分，是一个非常强大且通用的策略。这个“分而治之”的思想，无论是在系统设计还是在提示工程（Prompting）中都极为有效。 用图的思维优化流程 (Think in Graphs for Efficiency)：不要满足于线性的思考和执行链。思考任务之间的依赖关系，用DAG等图结构来建模工作流，可以发掘出巨大的并行优化潜力，这对于构建低延迟、高响应的系统至关重要。 蒸馏是生产部署的利器 (Distillation is Key for Production)：不要迷信“越大越好”。在生产环境中，效率和成本是生命线。通过蒸馏，用一个强大的教师模型去“教会”一个轻量级的学生模型，是实现高性能、低成本部署的黄金法则。“带思考训练，无思考推理” 是一个可以直接借鉴的、非常聪明的工程技巧。 需要补充的背景知识： 检索增强生成 (RAG)：深入理解RAG的工作原理、不同变种（如Self-RAG）及其局限性。 ReAct框架：阅读Yao et al., 2023的《ReAct: Synergizing Reasoning and Acting in Language Models》论文，这是许多现代Agent思想的基础，TURA也在此基础上发展。 工具学习 (Tool Learning/Tool-Use)：了解如Toolformer、ToolLLM等关于如何让LLM学习使用外部工具的代表性工作。 知识蒸馏 (Knowledge Distillation)：了解其基本概念，即如何将一个大模型（教师）的知识迁移到一个小模型（学生）上。 有向无环图 (DAG)：了解其基本的数据结构和在任务调度、工作流管理中的应用。 希望这份详尽的解读能够帮助您全面、深入地理解这篇论文的精髓及其对整个领域的重大意义。\n","date":"2025-08-15T00:00:00Z","permalink":"/turatool-augmented-unified-retrieval-agent-for-ai-search/","title":"TURA：Tool-Augmented Unified Retrieval Agent for AI Search"},{"content":"课程学习，逐步压缩思维链，思想类似于LAPO：INTERNALIZING REASONING EFFICIENCY VIA LENGTH-ADAPTIVE POLICY OPTIMIZATION\nimage.png 论文深度解读：从“长篇大论”到“言简意赅”的智能进化 这篇论文（可以在arXiv:2508.08940v1上访问）的核心贡献在于，它巧妙地将课程学习（Curriculum Learning） 的思想引入到大型语言模型的推理能力训练中，旨在解决一个日益突出的矛盾：我们既希望模型具备强大的、多步骤的复杂推理能力（这通常需要长的“思考链”，即Chain-of-Thought），又希望它在推理时尽可能地节省计算资源（即使用更少的token）。\n目前主流的方法，如OpenAI的O1模型或DeepSeek的R1系列模型，倾向于通过强化学习生成更长、更详尽的推理路径来提升准确率，但这往往导致“过度思考”（overthinking）和高昂的推理成本。其他一些方法尝试在推理时直接限制token数量，但这又可能因为“一刀切”而损害模型的推理能力。\n这篇论文反其道而行之，它认为效率不应该是在推理时才考虑的外部约束，而应该是在训练过程中内化为模型的一种核心能力。它提出的“先长后短”（Train Long, Think Short）策略，是一个非常符合认知科学的训练范式。\n具体来说，该方法在训练初期给予模型一个非常宽松的token预算。这就像我们学习一个新知识点时，一开始会不计成本地进行各种尝试，写下详尽的步骤，探索所有可能的解法。这个阶段的目标是“探索”（exploration），让模型有足够空间去发现解决问题的有效逻辑路径。\n正如论文中所述：“Our method starts with generous token budgets and gradually tightens them over training, encouraging models to first discover effective solution strategies and then distill them into more concise reasoning traces.” （我们方法从一个宽松的token预算开始，并在训练中逐步收紧它，鼓励模型首先发现有效的解决方案，然后将其提炼为更简洁的推理轨迹。）\n随着训练的进行，这个token预算会通过一个指数衰减的课程表（exponential decay schedule）逐渐收紧。这就好比我们熟练掌握一个技能后，开始追求效率，用更简洁、更精炼的步骤完成同样的任务。这个阶段的目标是“压缩”（compression），迫使模型在不牺牲准确率的前提下，学会用更少的token表达相同的推理过程。\n为了实现这一目标，作者基于一种名为群体相对策略优化（Group Relative Policy Optimization, GRPO）的强化学习框架进行微调。GRPO本身就是一个亮点，它不需要一个独立的价值网络（value function），而是通过在一组候选答案中进行奖励归一化来稳定训练，这使得它在处理稀疏奖励信号（比如答案是否正确）时非常有效。\n作者为GRPO设计了一个精妙的多目标奖励函数（reward function），它同时平衡了三个方面： 1. 任务正确性（Correctness）: 答案是否正确，这是根本。 2. 长度效率（Length Efficiency）: 生成的token数量是否接近当前课程表设定的预算。 3. 格式规范性（Formatting）: 是否遵循了\u0026lt;think\u0026gt;和\u0026lt;answer\u0026gt;标签的结构，便于解析和评估。\n特别值得一提的是长度奖励函数的设计。它不是一个简单的“越短越好”的函数，而是一个三角形函数（triangular function）。奖励在token长度等于当前预算L时达到峰值，在长度为0和2L时降为0。这种设计非常聪明，它鼓励模型充分利用当前的预算去思考，而不是为了追求短小而过早地停止推理，从而避免了“欠思考”的问题。\n实验结果有力地证明了该方法的有效性。在GSM8K（小学数学题）和MATH500（竞赛级数学题）等多个数据集上，采用课程学习训练的模型，在最终token预算与固定预算基线模型相同的情况下，准确率显著更高。例如，在GSM8K上，课程学习模型准确率达到86.20%，而固定预算模型仅为82.71%，同时平均token使用量几乎相同（87.0 vs 88.8）。这说明，渐进式的约束是一种强大的归纳偏置（inductive bias），它能引导模型学到更鲁棒、更高效的推理能力。 (图1：论文中的Figure 1，直观展示了token预算随训练步数指数衰减，以及模型在不同阶段对同一问题的解答从冗长到简洁的演变)\n总而言之，这篇论文提供了一个优雅且高效的解决方案，将“效率”从一个推理时的外部约束，转变成了模型在训练时内化的一种内在能力。它不仅在技术上展示了课程学习在LLM推理训练中的巨大潜力，更在理念上为我们指明了一条通往更智能、更经济的AI系统的可行路径。\n1. 论文的研究目标是什么？ 核心研究目标: 训练出一种能够进行高效推理（efficient reasoning）的大型语言模型。这里的“高效”意味着在保持或提升任务准确率的同时，显著降低推理过程中消耗的计算资源（主要体现为生成的token数量）。\n要解决的实际问题:\n高昂的推理成本: 当前最先进的LLM为了追求高准确率，通常会生成非常长的思考链（Chain-of-Thought），这导致了高昂的API调用费用和更长的响应时间。这限制了它们在许多成本敏感或对延迟有高要求的场景中的应用。 “过度思考”（Overthinking）: 模型即使在面对简单问题时，也可能产生不必要的冗长推理，这是一种计算资源的浪费。 现有方法的局限性: 固定预算训练: 在训练时使用固定的、较短的token预算，虽然能产出简洁的模型，但可能因为一开始就限制了模型的探索空间，导致其无法学习到最优的推理策略，从而损害最终的准确率。 推理时控制: 在推理时通过prompting或截断来控制长度，治标不治本，无法从根本上提升模型的内在效率，且可能因为粗暴干预而破坏推理逻辑的完整性。 对行业发展的意义:\n降低AI应用门槛: 通过降低推理成本，可以让更多中小型企业和开发者有能力部署和使用强大的推理模型，推动AI技术的普及。 提升用户体验: 更快的响应速度可以显著改善交互式应用（如AI助教、智能客服）的用户体验。 推动“绿色AI”: 减少不必要的计算消耗，符合AI领域对可持续发展的追求。 促进端侧AI部署: 训练出更“精炼”的模型是未来在手机、汽车等边缘设备上部署复杂推理能力的关键一步。 2. 论文提出了哪些新的思路、方法或模型？ 这篇论文的核心创新在于其思路，即将效率训练过程化、课程化，而不是将其作为一个静态的目标。\n新的思路：课程学习用于效率优化 核心理念: “先长后短”（Train Long, Think Short）。这是一种模拟人类学习过程的渐进式优化策略。它认为，模型需要先有足够自由的探索空间来“学会”如何解决问题，然后才能在压力下“学会”如何高效地解决问题。 与之前方法的对比: vs. 固定预算: 固定预算方法像是让一个新手直接参加F1排位赛，可能因为压力过大而无法发挥最佳水平。而课程学习则像是一个完整的车手培养计划，从卡丁车开起，逐步升级，最终才能在F1赛场上游刃有余。 vs. 推理时控制: 推理时控制像是给赛车装了一个限速器，而课程学习则是通过训练让车手本人知道在什么弯道应该用什么速度，将效率内化为一种本能。 新的方法/模型：基于GRPO的课程学习框架 基础框架: 群体相对策略优化 (Group Relative Policy Optimization, GRPO)。论文选择GRPO作为强化学习的基础，因为它： 无价值函数 (Value-Function-Free): 不需要训练一个独立的网络来评估状态的价值，简化了训练流程。 群体归一化: 通过在一组（group size G=8）生成的候选答案中对奖励进行归一化，将绝对的奖励信号（如0或1）转换为相对的优劣排序，这极大地稳定了训练过程，尤其适合处理稀疏奖励。 核心机制：动态预算与奖励函数 指数衰减的token预算: 这是课程学习的具体实现。预算B(t)按照公式 B(t) = max(1, B₀ * γ^(t/T)) 进行衰减。 B₀: 初始预算（如256 tokens），非常宽松。 γ: 衰减因子（0 \u0026lt; γ \u0026lt; 1），决定了预算缩减的速度。 T: 更新间隔，即每隔多少训练步数更新一次预算。 这个动态预算会直接影响长度奖励的目标。 精巧的复合奖励函数: \u0026gt; rᵢ = λc * R_correct + λl * R_len(lᵢ) + λf * R_fmt(aᵢ) 这是一个加权和，λ是权重，R是奖励。 R_len (长度奖励): 这是最精妙的设计之一。它采用了三角形函数。如下图所示，奖励在达到目标长度L时最大化，鼓励模型探索完整的推理路径。相比之下，传统的“阶梯”或“带状”（band）奖励（即只要小于L就给满分），容易让模型满足于找到一个能用的短路径，而不再探索更优但可能更长的路径，导致“过早收敛”和“过度压缩”。 image.png (图2：论文中Figure 5的示意图，三角形奖励鼓励模型用尽预算，而带状奖励可能导致过度压缩) 优势总结: 根本性提升: 将效率内化为模型的能力，而非外部约束。 性能更优: 实验证明，在相同最终效率下，准确率更高。 过程可控: 可以通过调整奖励权重（λc vs λl）来控制模型在“准确率”和“效率”之间的权衡。 训练稳定: 基于GRPO框架，训练过程相对稳定。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一系列严谨的消融实验（ablation studies）和对比实验来回答其在第4节开篇提出的六个关键问题（Q1-Q6），从而系统性地验证了方法的有效性。\n实验设计:\n模型: QWEN-2.5-7B 数据集: 训练集: GSM8K（简单数学题）和MATH500（困难数学题），用以测试方法在不同难度任务上的表现。 评估集: 除了GSM8K和MATH500，还包括SVAMP（GSM8K的变体）、GSM+（对抗性GSM8K）和College Math（大学数学），用于评估模型的泛化能力和鲁棒性。 基线模型 (Baselines): Base model: 原始的QWEN-2.5-7B，未经任何RL微调。 Fixed-budget GRPO: 使用GRPO进行微调，但token预算从始至终固定为87（与课程学习的最终预算相同）。这个基线是最关键的对照组，它能有效分离出“课程学习”本身带来的增益。 核心对比: Curriculum GRPO vs. Fixed-budget GRPO。 关键实验数据和结果:\n课程学习 vs. 固定预算 (回答Q1, Q2): 结果显示，课程学习全面优于固定预算训练。 关键数据 (Figure 2): 在GSM8K上训练后，Curriculum GRPO在测试集上准确率达到 86.20%，而Fixed-budget GRPO为 82.71%。 在更难的MATH500上训练后，Curriculum GRPO准确率为 43.40%，而Fixed-budget GRPO为 38.80%。同时，Curriculum GRPO的平均token数甚至更少（137.1 vs 179.3）。这证明了该方法在简单和困难任务上均有效。 课程衰减速率的影响 (回答Q4): 实验比较了快（每75步衰减）、中（150步）、慢（300步）三种衰减速率。 关键数据 (Table 1): 在困难任务MATH500上，快速衰减效果最好（准确率38.4%），而慢速衰减表现很差（9.8%）。这表明对于难题，尽早施加压缩压力有助于模型找到高效的本质解法。 在简单任务GSM8K和SVAMP上，慢速衰减准确率最高（分别为86.8%和88.0%），但token消耗也最多。 综合来看，中速衰减在准确率和效率之间取得了最佳的平衡。 Dataset Decay Interval Avg. Token Count Accuracy (%) MATH500 0.340 300 336 9.8 0.700 150 201 37.4 0.857 75 132 38.4 Average 0.700 150 135 57.9 0.857 75 115 57.9 (表1：论文Table 1数据节选，展示了衰减率对性能的影响)\n3. **长度奖励函数形状的影响 (回答Q5)**: * 对比了**三角形（Triangular）**和**带状（Band）**两种奖励函数。 * **关键数据 (Table 2)**: * 在所有数据集上，**三角形奖励**的准确率都显著高于带状奖励。 * 在**MATH500**上，差距尤其明显：三角形为**37.4%**，而带状仅为**30.8%**（相差了6.6个百分点）。这证明了带状奖励确实会导致“过度压缩”，损害了模型在复杂任务上的推理能力。 通过这些精心设计的实验，论文雄辩地证明了其提出的课程学习框架不仅有效，而且其内部的各个设计（如衰减率、奖励形状）都是经过深思熟虑且效果显著的。\n4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文打开了一扇新的大门，未来有许多激动人心的方向值得探索：\n挑战与问题: 扩展到更大规模的模型和更长的上下文: 论文实验基于7B模型和最大256的token预算。当模型规模扩展到70B、100B+，或者任务需要数千token的上下文时，当前的课程设计（如衰减率）是否依然最优？这需要进行新的缩放法则（Scaling Law）研究。 更复杂的任务领域: 论文主要关注数学推理，这类任务有明确的对错验证器。对于开放式生成任务（如写小说、代码生成、进行多轮对话），如何定义“正确性”和“效率”？“效率”仅仅是token少吗，还是指信息密度高？这需要设计更复杂的奖励模型和评估体系。 自适应课程学习 (Adaptive Curriculum Learning): 目前的课程表是预先设定的（指数或线性衰减）。一个更智能的系统应该能够根据模型在训练中的实时表现自适应地调整课程难度。例如，如果模型准确率下降过快，系统可以自动放宽token预算；反之，如果模型表现良好，则可以加速预算收紧的进程。 与其他效率技术的结合: 如何将这种训练时的课程学习方法与推理时的技术（如投机性解码 (Speculative Decoding)、专家混合 (Mixture of Experts, MoE)）结合，以实现效率的叠加效应？ 新的技术和投资机会: 高效微调平台: 未来可能会出现专门提供“效率导向”微调服务的平台。这些平台可以集成类似本文的课程学习策略，帮助客户在保证性能的同时，将模型微调得更小、更快、更便宜。 “绿色AI”解决方案: 随着对AI能耗问题的关注增加，任何能够显著降低计算成本的技术都有巨大的商业价值。这类研究为开发“绿色AI”工具链和认证标准提供了理论基础。 自动化AI教练 (Automated AI Coach): “自适应课程学习”的想法可能会催生出一种新的技术——AI教练。它能监控模型的学习过程，并动态调整数据难度、任务类型、学习目标等，实现最高效的训练。 端侧AI推理引擎: 经过这类效率优化训练得到的模型，更有可能被压缩和量化后部署到手机、AR/VR眼镜等边缘设备上，这将极大地拓展AI的应用边界，催生出新的消费级智能应用。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度来看，仍然存在一些局限性和待验证的问题：\n论文自身承认的局限性: 模型和预算规模有限: 如前所述，实验仅限于7B模型和较短的token预算（最大256），其结论能否推广到更大模型和更复杂任务上仍是一个开放问题。对于需要数千token进行推理的复杂法律文档分析或科研文献综述任务，256的预算上限显然是不够的。 额外的批判性思考: 对验证器的强依赖 (Verifier Dependence): 整个强化学习过程严重依赖于一个能够自动判断答案对错的验证器（verifier）。这在数学、代码等有明确答案的领域是可行的。但在许多现实世界的任务中（如“写一封有说服力的邮件”、“生成一份有创意的营销文案”），不存在这样简单的验证器。这限制了该方法目前可以直接应用的范围。未来的工作需要依赖更强大的奖励模型（Reward Model）来提供信号。 超参数敏感性 (Hyperparameter Sensitivity): 实验结果表明，最终性能对衰减率、奖励权重、衰减曲线形状（指数vs线性）等超参数相当敏感。这意味着实践者在应用该方法时，可能需要投入大量的计算资源进行繁琐的超参数调优，才能找到适用于自己特定任务和模型的最佳配置，这在一定程度上提高了使用门槛。 对“效率”的定义较为单一: 论文将效率等同于token数量的减少。但在实际应用中，效率还可能包括模型的推理延迟（wall-clock time）、内存占用等。一个token更少的模型，其内部计算逻辑可能更复杂，导致实际延迟并未减少。对效率的评估可以更加多维度。 潜在的灾难性遗忘 (Catastrophic Forgetting): 强制模型压缩其推理路径，是否可能导致其遗忘一些在长链推理中才能表现出来的、更细微的能力？虽然论文在相关的OOD数据集上验证了泛化性，但并未测试模型在完全不相关的其他能力（如知识问答、语言风格模仿）上是否有所衰退。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是创新思想的宝库，您可以从中借鉴以下几点核心启发：\n重点学习与启发: 核心思想：将优化目标“过程化”：最大的启发是不要将一个复杂的目标（如效率）看作一个终点，而要将其设计成一个可管理的、渐进的过程。这个“课程学习”的思想是高度可迁移的。您可以将其应用到其他领域： 数据难度课程: 从简单的数据开始训练，逐步引入困难或有噪声的数据。 任务复杂度课程: 先训练模型执行单步指令，再逐步过渡到多步、有依赖关系的复杂指令。 工具使用课程: 先让Agent学习使用单个简单工具，再学习组合使用多个复杂工具。 奖励函数设计的艺术: 论文中三角形长度奖励的设计堪称典范。它告诉我们，在设计激励机制时，必须深入思考其可能带来的意外后果（unintended consequences）。一个好的奖励函数应该精确地引导模型朝向你想要的行为，同时避免“钻空子”（比如为了奖励而过度压缩）。这对于任何涉及强化学习或偏好优化的任务都至关重要。 “探索-压缩”（Explore-then-Compress）策略: 这是一个非常强大的通用训练策略。在很多任务中，都可以先让模型在一个宽松的环境中自由探索，找到至少一个可行的解决方案，然后再通过增加约束来迫使它优化和提炼这个方案。这比从一开始就在强约束下摸索要有效得多。 需要补充了解的背景知识: 强化学习基础与LLM微调: PPO (Proximal Policy Optimization): 这是RLHF中最常用的算法，理解它的工作原理是基础。 DPO (Direct Preference Optimization): 这是当前非常流行的一种替代RLHF的方法，它将强化学习过程转化为一个更简单的分类问题。 GRPO (Group Relative Policy Optimization): 既然本文使用了它，深入了解其与PPO/DPO的区别和优势将非常有帮助。 推理策略与效率优化: Chain-of-Thought (CoT), Tree-of-Thought (ToT), Self-Consistency: 了解当前主流的提升LLM推理能力的方法，才能明白本文优化的对象是什么。 LLM效率技术概览: 了解其他提高效率的技术，如量化（Quantization）、剪枝（Pruning）、知识蒸馏（Knowledge Distillation）、专家混合（MoE）等，可以将本文的贡献放到一个更广阔的知识图谱中。 课程学习 (Curriculum Learning): 阅读一些课程学习领域的经典论文，了解其在计算机视觉和传统NLP任务中的应用，可以帮助你更深刻地理解其背后的哲学和数学原理。 ","date":"2025-08-14T00:00:00Z","permalink":"/train-long-think-shortcurriculum-learning-for-efficient-reasoning/","title":"TRAIN LONG, THINK SHORT：CURRICULUM LEARNING FOR EFFICIENT REASONING"},{"content":"迭代式生成并评估新的动作，也就是将多个函数包装成一个函数，想法很有趣。\nimage.png 论文深度解读 这篇论文的核心论点是：当前大模型智能体（LLM Agents）的潜力被其固定的、预先定义好的“动作空间”（Action Space）所严重限制。智能体就像一个工匠，而动作空间就是它的工具箱。如果工具箱里只有锤子和螺丝刀，那么无论工匠多么聪明（LLM的推理能力有多强），他都很难完成需要电钻或扳手的复杂任务。现有的方法，如著名的ReAct或Reflexion，更多地是在教工匠如何“更好地思考”如何使用现有工具，而这篇论文则另辟蹊径，致力于让工匠学会“自己制造和打磨新工具”。\n这个“制造新工具”的过程，就是论文提出的核心框架——LearnAct。LearnAct的本质是一个迭代式的主动学习框架，它让LLM智能体通过与环境的交互试错，动态地生成、评估和优化新的“动作”。这些新动作以Python函数的形式存在，可以将一系列基础、低级的原子动作（如pick(), move()）封装成一个更强大、更符合任务逻辑的高级动作（如carry_to(object, destination)）。\n让我们通过论文中的一个例子来理解这个过程（见图1和图2）：\n初始阶段： 智能体只有一个基础工具箱，包含pick(), move(), drop()等基本动作。当面对一个需要“把A房间的球搬到B房间”的任务时，它需要繁琐地分步思考和执行。 动作创建 (Action Creation)： LearnAct首先会利用一个强大的“学习者LLM”（Learner LLM，实验中为GPT-4），根据任务描述，主动创造一个可能更高效的新动作，例如carry_to(room1, room2, obj, gripper)。这个新动作就是一个Python函数，它内部封装了pick, move, drop等一系列原子操作。这相当于工匠自己打造了一把“搬运枪”。 动作学习 (Action Learning) - 核心迭代循环： 这把新“搬运枪”刚造出来可能并不好用，甚至有缺陷。LearnAct会进入一个试错和优化的循环： 试用： 智能体在一些训练任务中尝试使用这个新的carry_to动作。 发现错误： 在试用中可能会失败。比如，carry_to函数没有考虑到机械臂（gripper）一次只能拿一个物体，当尝试拿起第二个球时，环境返回了错误信息：“You already have a ball in hand, cannot pick up another ball.” 归因与修正： “学习者LLM”会分析这个失败的轨迹和错误反馈。它有两个选择： 更新函数 (Function Updating)： 直接修改carry_to函数的Python代码，加入检查逻辑，比如在pick()之前先判断机械臂是否为空。这相当于给“搬运枪”增加了一个保险装置。 撰写笔记 (Writing Notes)： 如果代码逻辑本身没错，只是使用场景不对，LLM会为这个函数生成更清晰的“使用说明”，例如在提示（Prompt）中补充一句：“注意：carry_to函数每次只能搬运一个物体。” 这相当于为工具写一份更详细的用户手册。 论文指出，通过这个“创建-试用-修正”的闭环，智能体的动作空间不再是静态的，而是动态演进、持续优化的。经过几轮学习，智能体不仅拥有了更强大的定制化工具，也更清楚如何使用它们。\n该论文最显著的贡献在于，它将学习的焦点从“策略学习”（Policy Learning）转向了“动作学习”（Action Learning）。策略学习（如Reflexion）关注于优化智能体的思考链（Chain-of-Thought）或决策逻辑，而动作学习则直接优化智能体的能力边界。实验数据雄辩地证明了这一思路的优越性。例如，在复杂的Robotic Planning任务中，使用GPT-4的LearnAct取得了82.8%的平均成功率，而同样使用GPT-4的ReAct+Reflexion（一种结合了思考和反思的先进方法）成功率仅为66.7%。这超过16个百分点的巨大差距，强有力地说明了“拥有更好的工具”比“更好地思考如何使用烂工具”要有效得多。\n正如论文所强调的: “Action learning outperforms the verbal policy learning… While learned verbal policies and prompts can aid in enhancing the agent’s planning capabilities, possessing proficient skills is essential for the agent to fully realize its potential.” (动作学习优于口头策略学习…虽然学习到的口头策略和提示可以帮助增强智能体的规划能力，但拥有熟练的技能对于智能体充分发挥其潜力至关重要。)\n总而言之，这篇论文通过LearnAct框架，为LLM智能体提供了一条从“熟练工”成长为“发明家”的路径，使其能够根据任务需求，自主地扩展和完善自身的能力集，从而在解决复杂问题时表现出更高的效率和鲁棒性。这是一个非常深刻且具有实践价值的范式转移。\n接下来，我将按照您提出的六个问题，进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是解决大型语言模型（LLM）智能体在与环境交互时，因其动作空间（Action Space）固定而导致的学习能力和任务适应性不足的问题。 实际问题： 能力瓶颈： 现实世界任务复杂多变，预先定义的有限动作集（如“左转”、“拿起”）往往粒度过细或表达能力不足，导致智能体需要极长的步骤和复杂的推理才能完成任务，效率低下且容易出错。 缺乏经验泛化： 现有智能体虽然能从单次失败中进行“反思”（如Reflexion），但这种反思通常是“一次性”的，形成的是临时的、基于自然语言的策略调整，很难沉淀为可复用的、结构化的“技能”。 人机对齐鸿沟： 人类在解决问题时，思考的抽象层次（例如“把客厅的书拿到卧室”）和物理执行的层次（拿起书、转身、走路…）之间存在一个巨大的鸿沟。固定动作空间的智能体强迫LLM用低层次的动作去模拟高层次的规划，这本身就是一种错位。 行业意义： 提升智能体能力上限： 通过让智能体自主学习新技能，可以指数级地提升其解决复杂问题的能力，使其能够胜任过去无法完成的任务，例如复杂的家庭服务机器人、自动化科学实验、软件开发流程自动化等。 加速智能体开发与部署： LearnAct这样的框架可以自动化“技能开发”的过程。开发者不再需要手动为智能体编写所有可能用到的高层API，而是可以定义一个基础动作集，让智能体在训练环境中“自我成长”，这极大地降低了开发成本和周期。 迈向通用人工智能（AGI）的关键一步： 人类智能的一个核心特征就是能够学习和创造工具来扩展自身的能力。让AI具备类似的能力，是其从一个“任务执行者”转变为一个“问题解决者”的关键，这与通用人工智能的愿景高度契合。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新是提出了LearnAct框架。它并非一个新模型，而是一种全新的智能体学习范式。其特点和优势可以与几个主流方法进行对比：\n方法 核心思想 学习对象 优点 论文指出的局限性 ReAct 将“思考”（Thought）和“行动”（Act）交织在一起 实时推理策略 提升了决策的透明度和准确性 动作空间固定，无法学习新技能 Reflexion 通过对失败轨迹进行“反思”，生成口头指导，用于下一次尝试 口头策略（Verbal Policy） 能从失败中学习，修正行为模式 学习成果是自然语言提示，非结构化，泛化能力有限 Voyager 也能生成代码作为新技能 代码化的新动作 实现了技能的程序化，可复用 缺乏迭代学习和修正机制，生成的技能质量不稳定 LearnAct (本文) 通过试错迭代地创建和优化代码化的新动作 代码化的新动作 (Action) 兼具技能创造和迭代优化能力 - LearnAct的核心优势在于：\n学习目标的根本性转变： 它不满足于学习“怎么做”，而是学习“能做什么”。它直接扩展了智能体的能力边界（Action Space），这是比优化策略（Policy）更根本的提升。 迭代式的闭环学习： 与Voyager等一次性生成代码的方法不同，LearnAct引入了一个基于环境反馈的迭代优化循环。这是一个强大的“自我纠错”机制，确保了学习到的技能是经过验证和打磨的，更加可靠和有效。 \u0026gt; 论文中的算法流程（Algorithm 1）清晰地展示了这一点：在一个循环中，智能体使用当前动作集Ai解决问题，通过SelectErrorCase找到失败案例，然后通过ActionLearn生成修正后的新动作集Ai+1。 学习成果的结构化与可复用性： LearnAct的产出是Python函数，这是结构化的、可移植的知识。理论上，在一个环境中学习到的carry_to函数，可以被迁移到另一个具有相似基础动作的 환경中，泛化潜力巨大。 灵活的修正机制： LearnAct提供了两种修正错误的路径：更新函数代码或撰写使用笔记。这给予了学习者LLM极大的灵活性，可以根据错误的性质选择最合适的修正方式。实验数据显示（见表4），模型在约90%的情况下倾向于直接“更新函数”，这表明它更倾向于从根本上修复工具的缺陷，而非仅仅做一些“表面文章”。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在两个具有挑战性的模拟环境中进行详尽的实验，来验证LearnAct的有效性。\n实验设计：\n环境： Robotic Planning: 包含四种复杂的机器人规划任务（如调酒师Barman、积木世界Blockworld），需要长序列的精确操作。 AlfWorld: 一个基于文本的家庭环境模拟器，智能体需要在部分可观测的环境中探索、定位并与物体交互。 模型： 实验同时在GPT-3.5-Turbo和GPT-4上进行，以验证方法的普适性。对于所有需要学习的方法（LearnAct, Reflexion, Voyager），其“学习者”角色都由更强大的GPT-4担任。 基线方法 (Baselines)： 论文选择了当前具有代表性的多种智能体方法进行对比，包括：Act, ReAct, Act+Reflexion, ReAct+Reflexion, CodeAsPolicy, 和 Voyager。 评估指标： 任务成功率 (Task Success Rate)。 流程： 对每个任务，随机选择3个实例作为训练集（用于LearnAct的学习过程），其余实例作为测试集。所有方法都在测试集上进行评估，以检验其泛化能力。 实验数据和结果： 论文的实验结果极具说服力，我将关键数据整理成下表以便查看（数据源自原论文表2和表3，选取GPT-4作为骨干模型的结果）：\n方法 Robotic Planning (平均成功率) AlfWorld (平均成功率) Act 60.7% 48.6% ReAct 63.3% 53.7% ReAct+Reflexion 66.7% 54.7% Voyager 72.1% 58.0% LearnAct (本文) 82.8% 72.2% 结果分析：\n全面超越： 无论是在Robotic Planning还是AlfWorld环境中，LearnAct的性能都显著优于所有基线方法。在Robotic Planning中领先第二名（Voyager）超过10个百分点，在AlfWorld中领先第二名（Voyager）超过14个百分点，优势巨大。 动作学习 \u0026gt; 策略学习： LearnAct相比于ReAct+Reflexion的巨大优势，证明了直接学习和优化动作比学习口头策略更有效。 迭代学习的重要性： LearnAct相比于同样生成代码的Voyager表现更好，印证了其迭代式错误修正机制的关键作用。没有经过迭代优化的技能，质量和鲁棒性都无法保证。 学习过程的有效性： 图3的图表显示，LearnAct的性能在经过1-2轮学习迭代后便能达到峰值，说明其学习效率非常高。这对于实际应用至关重要，意味着不需要漫长的训练过程。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这一研究方向前景广阔，但挑战与机遇并存。\n值得探索的问题和挑战： 技能的深度泛化 (Deep Generalization of Skills)： 论文中的技能学习仍局限于同一任务类型。未来的挑战在于，如何让智能体将在“积木世界”中学到的stack（堆叠）技能，泛化应用到“宜家家具组装”任务中？这需要更高层次的抽象和概念对齐能力。 终身学习与知识遗忘 (Lifelong Learning \u0026amp; Catastrophic Forgetting)： 当智能体持续学习新技能时，如何保证不会遗忘旧技能，或者新旧技能之间不会产生冲突？需要借鉴终身学习领域的方法，建立一个动态、可扩展且稳定的技能库。 从模拟到现实的迁移 (Sim2Real Transfer)： 模拟环境中的Python函数如何无缝迁移到真实的机器人上？这涉及到处理物理世界的不确定性、传感器噪声和执行器误差，是该领域走向实际应用必须跨越的鸿沟。 更复杂的错误归因 (Complex Credit Assignment)： 当前的错误修正机制相对简单，只针对最后一步的失败。但在长序列任务中，真正的错误根源可能在很早之前的某一步。如何让LLM具备更深邃的因果推理能力，进行精准的错误归因，是一个巨大的挑战。 多智能体协作学习： 未来的智能体系统必然是多智能体协作的。如何让多个智能体协同学习，创造出互补的、可组合的技能集？例如，一个智能体学习“开门”，另一个学习“搬运”，它们如何协同完成“把屋里的东西搬到屋外”的任务？ 可能催生的技术和投资机会： 智能体技能市场 (Agent Skill Marketplace)： 可能会出现一个类似App Store的平台，开发者和用户可以在上面分享、购买、出售针对不同场景（如家庭、办公、游戏）的智能体技能包。 自动化工作流平台 (Automated Workflow Platforms)： 基于此技术，可以打造出能自主学习和优化工作流的下一代RPA（机器人流程自动化）或软件测试平台。 专用于“学习”的LLM (Specialized “Learner” LLMs)： 可能会催生出专门为代码生成、调试、优化和自我修正而设计的LLM，它们将成为智能体“大脑”的核心。 通用机器人操作系统： 这项技术是实现真正通用的机器人操作系统的关键部分，能够让机器人像人类一样不断学习新本领。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些值得商榷或需要进一步阐明的点：\n对“学习者LLM”能力的强依赖： 整个LearnAct框架的有效性，高度依赖于一个极其强大的“学习者LLM”（实验中的GPT-4）来进行代码生成、错误分析和自我修正。如果换成能力稍弱的模型，这个框架是否依然有效？其性能下降曲线如何？论文对此没有进行深入探讨。这使得该方法的普适性和成本效益存疑。 过拟合风险的讨论不足： 论文虽然在附录中提到了学习过程可能因过度优化而导致性能下降，并展示了一个失败案例（图10），但在主文中对此的讨论不够深入。学习到的技能很可能“过拟合”于训练实例中的特定场景，导致在测试集中遇到稍有不同的情况就失灵。如何平衡技能的特化和泛化，是一个未被充分解决的问题。 实验环境的局限性： 尽管Robotic Planning和AlfWorld是公认的基准，但它们仍然是规则明确、状态离散的模拟环境。现实世界的复杂性、连续性和不确定性远超于此。该方法在更逼真的物理模拟或真实世界中的表现，是一个巨大的问号。 采样效率和成本问题： LearnAct在每个学习步骤中都需要进行K次采样（实验中K=4），并在训练实例上进行评估，以选出最优的动作更新。这是一个计算成本非常高昂的过程，尤其是在与真实环境交互时（耗时、耗能、有物理风险）。如何提升学习的样本效率，是其走向实用必须考虑的问题。 技能库的管理缺失： 论文主要关注单个或少量技能的学习。当智能体学会成百上千个技能后，如何有效地组织、索引、检索和组合这些技能？一个简单的列表式的动作空间会迅速变得臃肿和低效。论文没有探讨技能库（Skill Library）的管理和组织问题。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文充满了可以借鉴的创新思想。\n重点学习的核心思想： 范式转变：从“调教LLM”到“赋能LLM”。最大的启发是，不要只把LLM当作一个需要通过精心设计的Prompt来引导的“黑箱推理机”。相反，应该把它看作一个主动的“学习者”和“创造者”，为它搭建一个可以试错和成长的环境，让它自己去构建解决问题的能力。 将“代码”作为知识沉淀的载体。相比于自然语言形式的反思，代码是结构化的、精确的、可执行的。将智能体的学习成果固化为代码（Python函数），是一种远比修改Prompt更稳定、更可复用的知识表示方法。 迭代和反馈是王道。任何一次性的生成都可能是脆弱的。建立一个“生成-测试-反馈-修正”的闭环，是提升系统鲁棒性和性能的不二法门。这个思想可以应用在任何需要LLM生成内容的场景（代码、文章、计划等）。 赋予选择权。在ActionLearn阶段，让LLM自己选择“更新函数”还是“写笔记”，这是一个精妙的设计。它承认了不同性质的错误需要不同的解决方案，赋予了模型更大的自主性和灵活性。 你需要补充了解的背景知识： 强化学习基础 (Reinforcement Learning Basics): 了解马尔可夫决策过程 (MDP)、策略 (Policy)、动作空间 (Action Space)、奖励 (Reward)等基本概念，会让你更深刻地理解这篇论文试图解决的问题在AI领域的定位。 智能体经典框架 (Classic Agent Frameworks): 建议快速阅读一下ReAct (Synergizing Reasoning and Acting in Language Models) 和 Reflexion (Language Agents with Verbal Reinforcement Learning) 的原始论文。了解它们的工作原理，才能真正体会到LearnAct的创新之处。 代码生成大模型 (Code Generation LLMs): 了解像Codex, AlphaCode等模型的工作原理和能力边界，有助于你理解LearnAct中“学习者LLM”生成和修改Python函数这一步骤的技术可行性。 分层强化学习 (Hierarchical Reinforcement Learning): HRL的思想是将一个大任务分解为多个子任务，并学习不同层次的策略。LearnAct通过学习高层动作（技能）来封装低层动作，在思想上与HRL有异曲同工之妙，了解HRL会让你对LearnAct的结构有更深的认识。 希望这份详尽的解读能帮助您全面、深入地掌握这篇优秀的论文。\n","date":"2025-08-12T00:00:00Z","permalink":"/empowering-large-language-model-agents-through-action-learning/","title":"Empowering Large Language Model Agents through Action Learning"},{"content":"思想和PLAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks基本一样，创新点在于分层执行，将高级规划分类，这样相当于约束了agent的计划范围。\nimage.png 这是一篇非常典型且高质量的智能体（Agent）框架研究论文。它精准地抓住了当前LLM Agent在执行复杂任务时普遍存在的痛点，并提出了一个兼具“大局观”和“灵活性”的解决方案。通读全文，我们可以清晰地感受到作者对于现有Agent框架优劣势的深刻理解，以及在设计新框架时所做的精妙权衡。\n首先，我们来对论文进行一个全面的、深入的解读。\n当前，基于大语言模型（LLM）的智能体展现了巨大的潜力，但它们在面对真实世界的复杂任务时，往往会遇到两个核心瓶颈：任务规划（Task Planning）与任务执行（Task Execution）。论文开篇就一针见血地指出了这一点。一方面，像 ReAct [7] 这样的框架，采用“思考-行动-观察”的增量式循环，虽然灵活，但缺乏一个全局的、长远的目标指引，导致其在需要多步骤、多分支的复杂任务中，非常容易“迷失方向”，陷入局部最优的“死胡同”里。论文中提到：\nSpecifically, current planning methods often lack clear global goals—leading agents to get stuck in local branches…\n另一方面，像 Plan-and-Solve [6] 或 Plan-and-Execute [9] 这类方法，虽然会先生成一个宏观的全局计划，试图解决“迷失方向”的问题，但它们的计划往往过于“理想化”，与具体可执行的动作（Action）脱节。这导致计划在实际执行时可能会发现根本无法操作，或者计划本身超出了智能体实际能调用的工具和能力范围，即“计划不具备可执行性”。\n在执行层面，现有方法也面临着“复杂性”与“稳定性”的权衡。ReAct主要依赖文本或JSON格式来调用工具，这种方式简单直接，但在处理循环、条件判断等复杂逻辑时就捉襟见肘。为了解决这个问题，CodeAct [8] 框架被提出，它将智能体的行动空间统一为Python代码，极大地增强了逻辑表达能力。然而，这种自由度也带来了新的问题：代码执行的复杂性更高，且在与不可预测的外部环境（如API返回错误）交互时，复杂的代码更容易出错，导致整个系统非常脆弱和不稳定。\n正是基于对以上困境的深刻洞察，这篇论文提出了一个名为 GoalAct 的新型智能体框架。GoalAct的核心思想可以概括为两个关键词：全局规划 (Global Planning) 和 分层执行 (Hierarchical Execution)。\n全局规划：这并非一个一成不变的静态计划，而是一个“持续更新的动态蓝图”。智能体在每一步执行后，都会根据最新的观察（Observation）和历史记录，重新评估和更新整个全局计划 G。这个计划 G 由一系列的 (计划步骤Pi, 对应动作Ai) 组成。这种设计巧妙地结合了Plan-and-Execute的“远见”和ReAct的“灵活性”，既保证了智能体始终围绕一个清晰的长期目标前进，又确保了计划能根据实际情况动态调整，保证了计划的可行性。\n分层执行：这是GoalAct另一个精妙之处。它没有像CodeAct那样“一刀切”地要求所有动作都通过代码实现，而是借鉴了人类解决问题的思路：先确定要用什么“技能（Skill）”，再选择具体的工具和方法。论文将执行层分解为多个高级技能，例如：\nSearching (搜索)：用于信息检索，类似ReAct，简单、稳定，适合试探性探索。 Coding (编码)：用于处理复杂逻辑，类似CodeAct，功能强大，适合需要数据处理和复杂流程控制的场景。 Writing (写作)：用于内容生成任务，如撰写法律文书，这本身就是一个包含信息搜集、起草、润色等步骤的复杂过程，无法简单地用搜索或编码来概括。 Other Skills (其他技能)：框架是可扩展的，可以根据任务需求轻松加入新的技能，如推理、研究等。 这种分层结构极大地简化了全局规划的难度。在规划阶段，LLM只需要决定“下一步用哪个技能以及该技能要达成什么目标”，而不需要关心这个技能内部具体如何调用工具、如何处理参数等繁琐的细节。这降低了规划的认知负担，也使得智能体能根据任务的复杂度，灵活地选择最合适的技能组合。\n为了验证GoalAct的有效性，论文选择了一个极具挑战性的新基准 LegalAgentBench [12]。这个选择非常明智，因为它是一个新发布的数据集，有效避免了模型在训练中“见过”测试数据的问题（即数据泄露），同时，法律领域的任务天然地需要调用外部工具（如法条数据库）并具备专业知识，非常适合测试智能体的综合能力。实验结果令人信服，在GPT-4o-mini、Qwen-max和GLM-4-Plus三款主流大模型上，GoalAct的性能都全面超越了包括ReAct、CodeAct在内的所有基线方法。论文中的Table 2清晰地展示了这一点，GoalAct取得了“an average improvement of 12.22% in success rate”的平均成功率提升。\n更值得注意的是，随着任务复杂度的增加（从1-hop到5-hop），GoalAct的性能优势愈发明显。例如，在3-hop任务上，其性能提升高达20%，这强有力地证明了其全局规划和分层执行机制在处理复杂长链条任务上的鲁棒性。此外，论文还进行了消融实验 (Ablation Study)，通过移除“全局规划”模块或某个核心“技能”，来验证各组件的必要性。Table 3的数据显示，移除全局规划会导致整体性能下降8.14%，而移除编码（Coding）技能则会导致性能下降14.06%，这清晰地证明了GoalAct设计的合理性和各组件的不可或缺性。\n总而言之，这篇论文不仅仅是提出了一个新框架，更是为如何构建更强大、更可靠、更实用的LLM Agent提供了一套清晰且有效的设计哲学：用动态的全局规划指引方向，用分层的技能执行应对挑战。\n接下来，我将按照您提出的六个问题，进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升基于大语言模型的智能体（LLM-Based Agents）在执行复杂真实世界任务时的有效性（effectiveness）和泛化性（generalizability）。\n解决的实际问题：\n“短视”问题：解决现有主流Agent（如ReAct）因缺乏全局规划而在多步骤、多分支的复杂任务中容易“陷入局部最优”或“迷失方向”的问题。 “空想”问题：解决另一些Agent（如Plan-and-Solve）虽然有全局规划，但计划与实际执行脱节，导致计划“不切实际、无法执行”的问题。 “僵化”与“脆弱”的权衡问题：解决在任务执行层面，简单的工具调用方式（如Text/JSON）表达能力有限，而复杂的代码执行方式（如Python）虽然强大但又容易出错、不够稳定的两难困境。 行业意义：这个问题对于推动LLM Agent从“玩具”走向“工具”至关重要。当前，虽然Agent的概念非常火热，但在企业级的复杂应用（如自动化法律分析、金融研究、软件开发流程自动化等）中落地仍然困难重重。其根本原因就是现有Agent不够可靠。一个理想的Agent系统必须能够稳定、高效地完成人类交付的复杂任务。这篇论文提出的GoalAct框架，通过提升Agent在复杂任务中的规划和执行能力，使其表现更稳定、成功率更高，这无疑是向着构建真正“可用”的商用级Agent系统迈出的重要一步，有助于增强行业信心，加速Agent技术在各行各业的渗透与应用。\n2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新是提出了 GoalAct 框架，它并非一个新模型，而是一种新的智能体架构思路和工作流。其创新点主要体现在全局规划和分层执行两个方面。\n持续更新的全局规划 (Continuously Updated Global Planning) 思路：不同于Plan-and-Solve的一次性静态规划，也不同于ReAct的完全无规划，GoalAct的全局计划G是一个动态实体。智能体每完成一步 (Pi, Ai) 并获得观察 Oi 后，都会利用包含 (P1A1O1, ..., Pt-1At-1Ot-1) 的历史记录 St 来重新生成整个后续计划。 特点与优势： 兼具方向感与灵活性：它既为智能体提供了长期的目标指引，避免了ReAct的盲目探索，又能够根据实时反馈灵活调整，避免了静态计划的僵化和不可执行性。 紧密耦合规划与执行：论文中提到，GoalAct “tightly integrates global planning with execution”。这意味着规划不再是空中楼阁，而是与每一步的实际执行结果紧密绑定，确保了计划的现实性和可行性。 分层执行 (Hierarchical Execution) 思路：受人类解决问题方式的启发（先想做什么，再想怎么做），GoalAct将复杂的执行过程分解为对高级“技能（Skills）”的选择和调用。LLM在规划时，只需要决定使用哪个技能（如Searching, Coding, Writing）来达成子目标。 特点与优势： 降低规划复杂度：全局规划时，LLM的输出被简化为更高层次的技能选择，而不是繁琐的底层API调用细节。如论文所述：“it significantly simplifies global planning, as the plan only needs to specify appropriate high-level skills and their objectives rather than low-level details”。 行动策略的动态适应：智能体可以根据任务的难易程度，自主选择合适的技能。例如，在Figure 13的案例中，GoalAct首先使用Coding技能处理复杂的排序和数据关联，然后在后续步骤中切换到更简单的Searching技能来查询具体信息，这种灵活性是ReAct（只能搜索）和CodeAct（倾向于一直用代码）所不具备的。 可扩展性强：框架允许方便地增加新技能。如果未来需要一个专门用于科学计算的Agent，开发者可以为其添加一个“Scientific_Calculation”技能，而无需改动框架的核心逻辑。 图2: 论文中的案例研究 (Figure 13)。清晰对比了ReAct、CodeAct和GoalAct在解决同一个法律任务时的行为路径。ReAct陷入局部循环，CodeAct因试图一次性执行过多工具而报错，而GoalAct通过合理的规划和技能切换成功解决问题。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列精心设计的实验来验证GoalAct的有效性。\n实验设计 (Experimental Setup) 基准 (Benchmark)：选择了LegalAgentBench [12]。这是一个包含300个任务、6种类型的法律领域数据集。任务类型从1-hop到5-hop的逻辑推理（hop数越多，推理链条越长，任务越复杂）以及一个专门的法律文书写作（Writing）任务。 基线方法 (Baselines)：选取了四种代表性的Agent实现方法进行对比： Plan-and-Solve (P-S)：先生成完整静态计划，然后逐步执行。 Plan-and-Execute (P-E)：类似P-S，但在每步执行后会重新评估计划。 ReAct：增量式的“思考-行动-观察”循环。 CodeAct：使用Python代码作为行动空间。 基础模型 (Base LLMs)：为了证明方法的普适性，实验在三个不同的、通过API调用的LLM上进行：GPT-4o-mini, Qwen-max, 和 GLM-4-Plus。 评估指标 (Metric)：使用成功率 (Success Rate) 作为核心指标。其计算方式为模型输出中命中的关键答案元素（key_answer elements）数量与标准答案中总关键元素数量的比例。这是一个客观且可量化的指标。 实验数据与结果： 总体性能：如Table 2所示，GoalAct在所有模型和所有任务类型上几乎都取得了最佳性能（SOTA）。综合来看，GoalAct相比于次优方法的平均成功率提升了12.22%。具体到不同模型，在GPT-4o-mini上提升14.45%，Qwen-max上提升10.09%，GLM-4-Plus上提升12.11%。 随任务复杂度变化：GoalAct的优势在更复杂的任务上尤为突出。在1-hop任务上，领先幅度较小（平均3.26%），但在3-hop任务上，领先幅度扩大到惊人的20%，在5-hop任务上也有15.5%的提升。这证明了其处理长链条、复杂逻辑问题的强大能力。 消融研究 (Ablation Study)：Table 3的结果进一步巩固了结论。在GLM-4-Plus模型上，当移除不同组件时： w/o global plan（移除全局规划）：整体性能从0.8710下降到0.7896（下降8.14%）。 w/o coding（移除编码技能）：整体性能从0.8710下降到0.7304（下降14.06%）。 这组数据雄辩地证明了“全局规划”和“分层执行中的关键技能”都是GoalAct成功的必要条件，二者协同作用才能达到最佳效果。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为Agent研究开辟了新的道路，但前方依然有广阔的探索空间和严峻的挑战：\n值得探索的问题: 技能的自动发现与学习 (Automated Skill Discovery)：目前GoalAct的技能库（Searching, Coding, Writing）是人工预定义的。未来的终极Agent应该能根据任务需求，自主学习和生成新的技能。这可能需要结合元学习（Meta-Learning）或持续学习（Continual Learning）等技术。 更复杂的长期记忆机制 (Long-term Memory)：论文结尾提到了整合MemoryBank [24]。当前Agent的记忆通常局限于单次任务（in-session）。如何构建一个能跨任务、长期演化的记忆系统，让Agent能从过去的成功和失败中学习，是提升其智能水平的关键。 多智能体协作 (Multi-Agent Collaboration)：真实世界的复杂问题往往需要团队协作。未来的研究可以探索如何让多个GoalAct-style的Agent进行有效沟通、任务分配和协同工作，形成一个智能体“组织”。 与世界模型的结合 (Integration with World Models)：Agent不仅要会规划，还要能预测其行动可能带来的后果。将GoalAct这样的执行框架与能够模拟环境动态的“世界模型”相结合，可以让Agent做出更具前瞻性的决策，实现真正的“深思熟虑”。 催生的新技术和投资机会: Agent开发平台/OS：可能会出现专门用于构建、调试和部署高级Agent的“操作系统”或低代码平台。这类平台会内置类似GoalAct的成熟执行框架，并提供丰富的、可插拔的技能库和记忆模块。 垂直领域专业Agent服务：基于GoalAct这类强大框架，可以开发出在特定领域（如法律、金融、科研、医疗）表现超越人类专家的Agent服务，这会是B2B市场巨大的投资机会。 Agent安全与对齐 (Safety \u0026amp; Alignment)：随着Agent能力越来越强，如何确保其行为符合人类意图、不产生有害后果，将成为一个全新的、至关重要的研究领域和产业方向，催生出Agent“防火墙”、“监察官”等新技术。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性角度审视，仍存在一些可以深入探讨的局限性：\n基准的局限性 (Benchmark Limitations)：LegalAgentBench虽然质量很高，但它本质上是一个结构化的问答和文本生成任务集。现实世界的任务往往更加开放和模糊（open-ended）。GoalAct在需要创造力、主观判断或与物理世界交互（如机器人控制）的任务上表现如何，尚待验证。 技能定义的成本 (Cost of Skill Definition)：论文声称添加新技能是“easy and highly iterative”，但这可能过于乐观。定义一个高质量、鲁棒的新技能，可能需要大量的人工设计和提示工程（Prompt Engineering）。这个过程的复杂度和成本没有在论文中被充分讨论。 性能开销 (Performance Overhead)：持续更新全局计划听起来很美好，但它也意味着在每一步都需要LLM进行一次成本不菲的推理。与ReAct相比，GoalAct的计算成本和延迟可能会显著增加。论文没有对这方面的开销进行量化分析，这对于评估其在实际部署中的经济性和效率至关重要。 失败处理与反思机制的缺失 (Lack of Failure Handling and Reflection)：虽然GoalAct能动态调整计划，但当一个动作彻底失败（如代码执行持续报错）时，它似乎没有一个明确的回溯（backtracking）或反思（reflection）机制来分析失败的根源。论文结尾提到了整合Reflexion [23]，这正说明了当前版本GoalAct在这方面的不足。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然！这篇论文充满了可以直接借鉴的智慧和创新点。\n重点学习与启发： “战略”与“战术”分离的思想：这是最核心的启发。在设计任何复杂系统时，都可以借鉴这种分层思想。将系统目标分解为高层次的战略规划（做什么）和低层次的战术执行（怎么做）。战略层面保持稳定和聚焦，战术层面保持灵活和适应性。这不仅适用于Agent设计，也适用于项目管理、产品设计甚至个人工作流程。 不要陷入“锤子-钉子”思维：论文最大的亮点之一就是不迷信任何一种单一的执行方式。它告诉我们，没有“万能”的工具。Coding很强大，但有时简单的Searching更稳定高效。启发我们 在解决问题时要有一个“工具箱”或“技能库”，并学会根据问题的具体情况，动态地选择最合适的工具，而不是试图用一个工具解决所有问题。 动态反馈循环的重要性：静态的、一成不变的计划在复杂多变的环境中是脆弱的。GoalAct的持续更新机制强调了建立一个快速反馈和调整闭环的重要性。无论做什么，都应该有一个机制来不断地根据新信息来审视和调整最初的计划。 需要补充的背景知识： 主流Agent框架：为了深刻理解GoalAct的创新性，您需要对它所对比的几个框架有基本了解，特别是 ReAct (Reason+Act) 和 Code-based Agents (如CodeAct) 的工作原理和优缺点。 思维链 (Chain-of-Thought, CoT)：这是现代LLM推理能力的基础，也是ReAct等框架的理论基石。了解CoT有助于理解LLM是如何进行逐步推理的。 提示工程 (Prompt Engineering)：像Table 4中展示的Prompt，是指导LLM作为Agent核心控制器（大脑）的关键。了解如何设计高效的Prompt，对于复现或改进GoalAct至关重要。 LLM作为工具使用者 (LLMs as Tool-Users)：可以阅读相关的综述性文章，了解LLM如何通过API调用与外部世界进行交互，这是所有Agent系统的基础。Toolformer [3] 是该领域的开创性工作之一。 希望这份详尽的解读能帮助您全面、深入地掌握这篇论文的精髓。如果您对其中任何一点还有疑问，我们随时可以继续探讨。\n","date":"2025-08-12T00:00:00Z","permalink":"/enhancing-llm-based-agents-via-global-planning-and-hierarchical-execution/","title":"Enhancing LLM-Based Agents via Global Planning and Hierarchical Execution"},{"content":"改进了agent中的记忆模块\n论文深度解读 这篇发表于2025年8月的论文，聚焦于为基于LLM的Agent赋予一种程序化记忆（Procedural Memory）。这个概念源于认知科学，指的是我们“如何做某事”的记忆，例如骑自行车或打字，一旦学会便可自动执行，无需有意识地思考每一步。作者将此概念巧妙地迁移到AI Agent领域，旨在解决一个普遍存在的问题：尽管LLM Agent在处理多样化任务上表现出色，但它们往往缺乏从经验中学习并形成稳定“技能”的能力。每当遇到一个新任务，即使与之前的任务高度相似，它们也常常需要从头开始探索，导致效率低下和成功率不稳定。\n论文的核心贡献在于提出了一个名为 Memp 的统一框架。这个框架不仅仅是简单地存储过去的成功案例，而是将程序化记忆视为一个可学习、可更新、可演进的生命体。它将Agent过去的交互轨迹（trajectories）提炼为两种形式的记忆：一种是细粒度的、具体的步骤指令（step-by-step instructions），另一种是更高级别的、抽象的脚本（script-like abstractions）。\nMemp框架的精髓体现在其三大核心模块：构建（Build）、检索（Retrieve）和更新（Update），这构成了一个完整的记忆生命周期闭环，正如论文图2所示。\n图2解读: 这个流程图是论文方法论的核心。 * Build: Agent执行任务后，其行动轨迹（无论好坏）被送入记忆存储库。成功的轨迹（Golden Trajectory）被提炼并存储为程序化记忆（P Mem）。 * Retrieve: 当遇到新任务时，检索器（Retriever）从记忆库中找到最相关的记忆。 * Update: 这一步是关键的创新。系统不仅仅是简单地添加新记忆。如果一个被检索出的记忆在新任务中导致了失败（P Mem Error），系统会进入一个修正流程（Update），将错误的经验与原有记忆结合进行“反思”和“修正”，生成一个更正后的记忆（Corrected P Mem）。同时，成功的经验也会被添加进去。这个动态的更新机制确保了记忆库随经验增长而持续优化。\n论文通过在两个截然不同的基准测试——ALFWorld（模拟家居环境中的实体任务）和TravelPlanner（基于工具的复杂信息规划任务）——上的大量实验，系统性地验证了Memp框架的有效性。实验结果令人信服地展示了，随着记忆库的不断迭代和优化，Agent在解决同类任务时的成功率显著提升，同时完成任务所需的步骤数（即效率）也大幅降低。\n一个尤为亮眼的发现是程序化记忆的可迁移性（transferability）。论文展示了由一个强大的前沿模型（如GPT-4o）生成的程序化记忆库，可以直接迁移给一个能力较弱的开源模型（如Qwen2.5-14B），并显著提升后者的性能。这为如何利用顶级模型的“智慧”来赋能和提升整个开源模型生态提供了一条极具价值的实践路径。\n总而言之，这篇论文不仅仅是提出了一个技术方案，更是为如何构建能够持续自我完善（continual learning）和泛化（generalization）的智能代理提供了一个清晰、系统且可操作的范式。它将“记忆”从一个被动存储的“数据”，提升到了一个主动优化和演进的“核心组件”的战略高度。\n接下来，我将按照您的要求，对六个具体问题进行详细解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是探索如何为LLM Agent构建一个有效的程序化记忆（Procedural Memory）系统，使其能够从过去的经验中学习、提炼并复用解决问题的流程和技能。\n解决的实际问题:\n脆性（Brittle）和低效: 当前的LLM Agent在执行复杂、长周期的任务时，其“记忆”要么是人工硬编码的提示模板，要么是固化在模型参数中，难以更新。这导致它们非常“脆”，一旦外部环境（如UI变化、数据格式改变）或任务细节稍有不同，就容易失败。而且，对于相似的任务，它们无法利用过往经验，每次都从零开始“试错”，极大地浪费了计算资源（tokens）和时间。论文中提到： \u0026gt; Restarting from scratch every time is a punishing ordeal for present-day agents. 缺乏持续学习能力: 理想的Agent应该像人一样，越做越熟练。但现有Agent大多是“一次性”的，缺乏一个将单次任务经验转化为长期能力的机制。这篇论文正是要解决这个问题，让Agent能够实现持续的、近乎线性的任务掌握过程。 行业意义:\n推动Agent从“工具”向“伙伴”演进: 具备程序化记忆的Agent，能够学习并适应特定的工作流，从而可以从一个通用的“瑞士军刀”变成一个懂你需求的“私人助理”或“专业同事”。这对于企业自动化、个人生产力工具等领域意义重大。 提升Agent的经济性和实用性: 通过减少不必要的探索和失败，Memp这类框架能显著降低Agent运行的成本（token消耗）和时间，这使得在真实世界中大规模部署LLM Agent变得更加经济可行。 为通用人工智能（AGI）铺路: 持续学习和经验泛化是通往更高级别人工智能的关键一步。这项研究为机器如何从具体实例中提炼出抽象规则，并不断自我完善，提供了一个重要的实践范例。特别是记忆的迁移能力，展示了“知识”可以在不同“大脑”（模型）间传递的可能性。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新是系统性的 Memp 框架，其特点和优势体现在对记忆生命周期（构建、检索、更新）的全面设计上。\n新的思路和方法: 记忆的二元化构建 (Build): 论文没有简单地存储原始轨迹，而是探索了两种互补的记忆形式：\nTrajectory (轨迹): 完整的、成功的交互记录。这像是具体的“案例”，提供了详尽的上下文和操作细节。 Script (脚本): 从轨迹中提炼出的高级、抽象的计划或摘要。这像是“原则”或“SOP”，泛化能力更强。 Proceduralization (程序化): 这是论文推荐的最佳策略，将具体的轨迹和抽象的脚本相结合。这既能提供宏观指导，又有具体的例子可供参考，鲁棒性最强。 精细化的检索 (Retrieve): 论文对比了不同的检索策略，从简单的随机抽样到基于语义的向量检索。其中，AveFact 策略最具巧思：它先用一个大模型从任务查询中提取出核心关键词（keywords），然后计算这些关键词向量与记忆库中对应项的平均相似度。这比直接对整个查询句子做向量检索更聚焦、更准确。\n动态的记忆更新 (Update): 这是相比以往工作最突出的优势。论文设计了三种更新机制：\nVanilla Memory Update: 简单地追加所有新任务的轨迹。 Validation: 只追加成功的轨迹。 Adjustment (调整/反思): 当从记忆库中检索出的“旧知识”导致任务失败时，该机制会将失败的轨迹与旧知识结合，让LLM进行反思和修正，生成一个更新后的、更准确的记忆。这是一个强大的“从错误中学习”的闭环。 与之前方法的比较和优势: 超越静态记忆框架: 像AutoGPT或LangGraph等早期框架，虽然也有记忆模块，但它们的记忆通常是临时的、基于单次任务的，或者更新机制非常简单（如仅追加成功案例）。Memp的Adjustment机制让记忆库具备了自我修正和演进的能力。 系统性的量化分析: 之前的研究多是提出某个单一的记忆技巧。而本文对Build, Retrieve, Update的各个环节，都设计了不同的策略并进行了量化对比实验（Ablation Study），系统性地分析了哪种组合最优。这使得其结论更具说服力和指导意义。 明确了记忆的价值: 论文不仅证明了记忆“有用”，还通过实验量化了它的价值。例如，图5的案例直观地展示了使用记忆后，任务步骤从27步（失败）减少到14步（成功），Token消耗节省了超过1000个。 图5案例解读: 在“加热鸡蛋并放入垃圾桶”这个任务中： * 无记忆: Agent会进行大量无效探索，比如尝试用烤面包机（toaster）或炉子（stoveburner）加热鸡蛋，最终失败。 * 有记忆: 记忆直接提示“去冰箱拿鸡蛋，然后用微波炉加热”，Agent直接执行了最优路径，既快又准。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文的实验设计非常严谨和全面，覆盖了不同的任务领域、模型和评价维度。\n实验设计:\n基准（Datasets）: 选用了ALFWorld和TravelPlanner。前者是实体交互任务，考验Agent对环境的理解和操作序列的正确性；后者是信息规划任务，考验Agent使用工具和处理复杂逻辑约束的能力。这种多样性确保了结论的泛化性。 模型（Backbones）: 采用了业界领先的闭源模型GPT-4o、Claude-3.5-sonnet和强大的开源模型Qwen2.5-72B。这既验证了方法在SOTA模型上的有效性，也证明了其在开源生态中的适用性。 对比实验（Ablation Studies）：论文系统性地对 Build, Retrieve, Update 三个模块的不同策略进行了“控制变量”实验，以找到最优组合。 Build 策略对比: 比较No Memory, Script, Trajectory, Proceduralization四种构建方式的效果。 Retrieve 策略对比: 比较Random Sample, Key=Query, Key=AveFact三种检索方式的效果。 Update 策略对比: 比较Vanilla, Validation, Adjustment三种更新方式带来的长期性能演进。 关键实验数据和结果:\nBuild策略有效性: 在ALFWorld测试集上，使用GPT-4o模型，Proceduralization 策略的成功率达到了 77.86%，远高于基线No Memory的 42.14%。同时，任务平均步数从23.76步降至15.01步，效率大幅提升。（数据来源：Table 1） Retrieve策略有效性: 在TravelPlanner基准上，Key=AveFact 检索策略在常识约束（#CS）上的得分为 76.02，优于Key=Query的73.38和基线的71.93。（数据来源：Table 2） Update策略的优越性: 如图3所示，随着任务组的推进，Adjustment 策略带来的奖励增益（Reward Gain）和步数减少（Steps Reduction） 持续领先于其他策略。在最后一个任务组，其优势尤为明显，这证明了从错误中学习的强大能力。 图3解读: 该图展示了Agent在完成一组组任务后性能的演进。横轴是任务组的序号，纵轴是相比“无记忆”基线的性能提升。可以看到，绿色的“Adjustment”线始终在最上方（奖励更高）和最下方（步数减少更多），表明其学习效率最高。\n记忆的可迁移性: 论文的一个亮点实验。当把GPT-4o生成的记忆库给Qwen2.5-14B使用时，后者在TravelPlanner任务上的完成率提升了5%，平均步数减少了1.6步。这有力地证明了程序化记忆作为一种“知识”，是可以在不同模型间传递和复用的。（数据来源：Figure 4(a)） 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为程序化记忆研究打开了大门，但仍有许多激动人心的方向值得探索。\n论文中提及的未来方向: 更丰富的检索策略: 目前主要依赖向量相似度。未来可以融合传统信息检索方法，如BM25（一种基于关键词匹配的算法），实现更精准的混合检索。 LLM作为奖励模型/裁判: 在很多现实场景中，任务没有明确的成功/失败信号。论文提出可以利用一个强大的LLM作为“裁判”来评估任务完成度，从而为记忆的Validation和Adjustment提供反馈信号。这与当前热门的RLAIF (Reinforcement Learning from AI Feedback) 不谋而合。 我补充的未来挑战与机遇: 记忆的遗忘与剪枝 (Forgetting and Pruning): 随着经验积累，记忆库会越来越庞大。如何设计一个高效的“遗忘”机制，自动识别并清除过时的、低价值的或冗余的记忆，是维持系统长期高效运行的关键。 层级化与组合式记忆 (Hierarchical \u0026amp; Composable Memory): 对于极其复杂的任务（如“策划并执行一场市场活动”），可能需要层级化的记忆结构。比如，一个顶层记忆调用多个子流程的记忆（“写宣传文案”、“联系场地”、“发邀请函”）。如何让记忆能够被灵活地组合和调用，是一个巨大的挑战和机遇。 环境剧变下的记忆适应性: 论文的实验环境相对稳定。如果现实世界发生剧变（如网站大规模改版、API接口完全重写），当前的Adjustment机制可能不足以应对。需要研究更鲁棒的机制来处理这种概念漂移（Concept Drift）。 多Agent间的记忆共享与协作: 在一个组织中，多个Agent能否共享一个中央记忆库？一个Agent学习到的技能如何高效地传递给其他Agent？这会催生协作型学习Agent，带来组织级别的效率提升。 技术与投资机会: 企业级自动化平台: 提供能学习和适应企业内部SOP的Agent服务。 自适应机器人: 尤其在工业和服务机器人领域，能通过示教和自我试错不断学习新技能。 下一代AI原生应用: 应用不再是固定的，而是能根据用户的使用习惯，动态生成和优化交互流程。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的角度审视，仍存在一些潜在的局限和待验证的问题。\n可扩展性问题 (Scalability): 实验是在精心设计的基准上进行的，记忆库的规模相对可控。当记忆库包含成千上万条程序时，检索的效率和准确性是否会下降？Adjustment过程需要调用LLM进行修正，当需要修正的记忆很多时，其成本和延迟是否会变得无法接受？Figure 4(b)也暗示了检索过多记忆可能导致性能下降，这个问题值得深究。 “调整”过程的可靠性: Adjustment机制是论文的一大亮点，但也可能是一个弱点。它依赖LLM能够理解失败原因并给出正确的修正。如果LLM的“反思”是错误的，反而可能“污染”记忆库，让情况变得更糟。论文对这一修正过程本身的失败率和鲁棒性缺乏深入分析。 泛化与过拟合的权衡: 程序化记忆在提升相似任务效率的同时，也存在风险，即Agent可能过拟合（overfit）于过去的经验。当遇到一个表面相似但核心逻辑完全不同的新任务时，固有的记忆反而可能成为一种“思维定势”，阻碍Agent找到正确的解决方案。如何在这种“利用旧知识”和“探索新可能”之间取得最佳平衡（Exploitation vs. Exploration），是该领域永恒的难题。 对高质量“黄金轨迹”的依赖: 论文中的记忆构建很大程度上依赖于从训练集中筛选出的成功轨迹（gold trajectories）。在现实世界中，获取这样清晰、高质量的“教学材料”可能很困难。如何从大量混杂着成功、失败和噪音的原始交互数据中有效学习，是一个更具挑战性的问题。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文提供了许多可以直接借鉴的宝贵思想。\n重点学习与启发: 核心思想转变：将记忆视为生命体: 最重要的启发是，不要把给Agent的“知识”或“提示”看作是静态的、一次性写入的东西。而应该把它设计成一个动态的、可演进的系统，建立起 “经验-\u0026gt;记忆-\u0026gt;应用-\u0026gt;反馈-\u0026gt;修正” 的完整闭环。这是构建自适应系统的核心。 “原则+案例”的黄金组合: 论文中效果最好的Proceduralization策略，即抽象脚本（Script）+ 具体轨迹（Trajectory）的结合，是一个极其强大且通用的模式。在设计任何形式的提示工程（Prompt Engineering）或Agent知识库时，都可以借鉴这个思想：既要给它高层次的原则性指导，也要给它一两个可供模仿的具体案例。 从错误中学习的机制: Adjustment机制是“点睛之笔”。它告诉我们，失败的经验同样宝贵，甚至比成功的经验信息量更大。在你的系统中，一定要设计一个机制来捕获失败案例，并利用它们来迭代和优化你的核心逻辑或知识库。 迁移学习的价值: 如果你同时使用多种模型，可以考虑用最强大的模型（如GPT-4o）来“生产”高质量的知识/记忆，然后将这些知识“赋能”给成本更低的模型来执行任务。这是一个非常实用的降本增效策略。 需要补充的背景知识: LLM Agent基本范式: 了解ReAct (Reason + Act)、Tool Use等Agent构建的基本原理，知道Agent是如何与环境和工具交互的。 马尔可夫决策过程 (Markov Decision Process, MDP): 论文用MDP来形式化定义Agent与环境的交互，了解其基本概念（State, Action, Reward, Policy）有助于理解论文的理论基础。 向量数据库与语义检索: 了解Retrieve模块背后的技术，如文本如何被编码成向量（Embedding），以及如何通过计算向量间的余弦相似度来找到最相关的内容。 上下文学习 (In-Context Learning): 明白这篇论文的方法本质上是一种非常高级的“上下文学习”，它不是通过梯度下降来更新模型参数（Fine-tuning），而是通过在提示中动态构建和优化上下文来引导模型的行为。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-12T00:00:00Z","permalink":"/mempexploring-agent-procedural-memory/","title":"Memp：Exploring Agent Procedural Memory"},{"content":"有关于RL提高规划能力的，看agent RL相关论文的planning章节\nEnhancing LLM-Based Agents via Global Planning and Hierarchical Execution PilotRL：Training Language Model Agents via Global Planning-Guided Progressive Reinforcement Learning Routine：A Structural Planning Framework for LLM Agent System in Enterprise PLAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks PLAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks Routine：A Structural Planning Framework for LLM Agent System in Enterprise Thinking vs. Doing：Agents that Reason by Scaling Test-Time Interaction RLVMR：Reinforcement Learning with Verifiable Meta-Reasoning Rewards for Robust Long-Horizon Agents Group-in-Group Policy Optimization for LLM Agent Training Beyond Policy Optimization：A Data Curation Flywheel for Sparse-Reward Long-Horizon Planning PilotRL：Training Language Model Agents via Global Planning-Guided Progressive Reinforcement Learning Enhancing LLM-Based Agents via Global Planning and Hierarchical Execution Empowering Large Language Model Agents through Action Learning CausalPlan：Empowering Efficient LLM Multi-Agent Collaboration Through Causality-Driven Planning Coarse-to-Fine Grounded Memory for LLM Agent Planning HIPLAN：Hierarchical Planning for LLM Agents with Adaptive Global-Local Guidance Learning When to Plan：Efficiently Allocating Test-Time Compute for LLM Agents Plan Verification for LLM-Based Embodied Task Completion Agents WEB AGENTS WITH WORLD MODELS ：LEARNING AND LEVERAGING ENVIRONMENT DYNAMICS IN WEB NAVIGATION ","date":"2025-08-12T00:00:00Z","permalink":"/%E5%88%86%E5%B1%82%E8%A7%84%E5%88%92%E7%9B%B8%E5%85%B3%E8%AE%BA%E6%96%87/","title":"分层规划相关论文"},{"content":"感觉思想很类似于GTPO：Trajectory-Based Policy Optimization in Large Language Models。不同的答案有共享片段。\nimage.png 首先，让我们对这篇论文进行一个整体性的深入剖析。\n这篇论文的核心贡献在于，它识别并解决了一个在现有大模型后训练方法中普遍存在但被忽视的问题：对模型生成的多个候选答案之间关系的错误处理。以该领域一个高效的代表性方法 GRPO (Group Relative Policy Optimization) 为例，这类方法通常会针对一个问题（query）生成一组候选答案，然后通过比较这组答案的相对优劣来优化模型。然而，它们都隐含了一个关键假设：这组候选答案是相互独立的。\n论文作者敏锐地指出，这个独立性假设在现实中是不成立的。正如论文开篇图1所示，不同的候选答案之间可能存在复杂的语义关系：它们可能是“支持链”（Supportive Chains），从不同角度共同指向一个正确答案；也可能是“冲突链”（Conflicting Chains），因计算错误或理解偏差而相互矛盾。将它们视为独立的个体，会丢失这种宝贵的结构化信息，从而限制了模型的学习效率和最终的推理能力。\n为了解决这个问题，作者引入了结构因果模型 (Structural Causal Model, SCM) 这一强大的分析工具。他们论证了，当多个独立的候选答案被用来共同推断一个最终的综合性答案时，这个过程在因果图上会形成一个特殊的结构——“对撞结构”（Collider Structure）。这是本文最核心的理论洞见。在因果推断中，对“对撞节点”（collider）进行条件化，会使其原本相互独立的父节点之间产生依赖关系。通俗地说，虽然每个候选答案是模型独立生成的，但一旦我们考虑它们如何共同构成一个最终答案，它们之间就变得不再独立了。\n正如论文中所说： \u0026gt; In our case, candidate responses are initially independent when conditioned solely on the query. But when these responses jointly influence a final integrated output, conditioning on this final result (the collider) introduces dependencies among the candidate responses.\n基于这一因果分析，论文提出了两大理论支柱（定理1与推论2），证明了如果能让模型的预测“感知”到这种对撞结构，就可以系统性地降低测试误差。这为他们提出的新方法 GCPO (Group Causal Policy Optimization) 奠定了坚实的理论基础。\nGCPO通过两个关键组件将因果结构融入优化过程： 1. 因果调整的奖励 (Causally-adjusted reward)：它不再仅仅基于单个答案的好坏来给予奖励，而是会根据该答案与一个“因果投影参考”的对齐程度来调整奖励。这个参考可以理解为综合了所有其他候选答案信息后形成的一个更鲁棒的“共识”方向。 2. 新的KL散度正则化项 (KL-divergence regularization term)：它引导模型策略不仅要接近原始模型，更要对齐到一个考虑了因果依赖的、结构上更一致的参考分布。\n通过在多个极具挑战性的数学推理和代码生成基准上的大量实验，论文有力地证明了GCPO的有效性。实验结果显示，GCPO不仅在平均性能上显著超越了包括GRPO在内的所有基线方法，而且在解决最困难的问题时，性能提升尤为明显。例如，在DeepSeek-R1-Distill-Qwen-7B模型上，GCPO相较于GRPO和GVPO等先进方法，在AIME和MinervaMATH等高难度数据集上实现了超过1%的性能提升，总平均准确率提升了近1个百分点。论文中的定性分析（图5）更是直观地展示了GCPO如何通过协调不同推理路径来修正错误，而GRPO则容易陷入单一、错误的逻辑。\n总而言之，这篇论文不仅仅是提出了一种性能更好的算法，更重要的是，它为我们理解和优化大模型的复杂推理过程提供了一个全新的、基于因果的视角。它标志着大模型后训练正在从简单的“偏好对齐”迈向更深层次的“结构化推理对齐”。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心目标是改进大型语言模型（LLMs）在后训练（post-training）阶段的优化算法，特别是针对需要复杂推理能力的任务（如数学和代码生成）。\n解决的实际问题：它主要解决现有基于强化学习的后训练方法（如GRPO）中的一个关键缺陷：错误地假设模型为同一输入生成的多个候选答案之间是相互独立的。在实际中，这些答案之间存在着如互补、协同、矛盾等丰富的语义关系。忽略这些关系，会导致：\n信息损失：无法利用答案间的协同信息来构建更完整的推理链。 优化低效：无法通过识别答案间的矛盾来发现模型的系统性缺陷。 性能瓶颈：模型的推理鲁棒性受限，容易在多步骤、多角度的问题上犯错。 行业意义：\n提升复杂任务性能：随着LLM从通用对话向专业领域（如科学计算、软件工程、金融分析）渗透，对模型推理的准确性和可靠性要求越来越高。这项研究提出的方法能够显著提升模型在这些领域的表现。 推动“AI科学家”发展：一个真正有能力的“AI科学家”不仅要能给出一个答案，更要能评估不同研究路径的优劣、整合不同来源的证据、并识别其中的矛盾。GCPO正是朝这个方向迈出的重要一步，它让模型学会了更智能地“权衡”不同思路。 超越简单的RLHF：目前主流的RLHF（基于人类反馈的强化学习）更多关注于对齐人类的单一偏好。这项工作开辟了“结构感知”的RLHF新范式，即模型不仅学习哪个答案更好，还学习一组答案为何在逻辑上是“好结构”。这对于构建更可信、更鲁棒的AI系统至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新的思路： 引入因果推断视角：这是本文最大的思想创新。作者没有停留在传统的机器学习优化框架内，而是引入了结构因果模型（SCM）来分析LLM的生成过程。 识别“对撞结构”（Collider）：论文的核心洞见是，将多个候选答案（\\(y_0, ..., y_{n-1}\\)）共同用于生成最终答案（\\(y_n\\)）的过程，在因果图上形成了一个典型的对撞结构。如下图（论文图2）所示，多个原因共同指向一个结果。这一结构是解释为什么候选答案间会产生依赖的关键。 新的方法/模型（GCPO）： GCPO在GRPO的基础上，通过两大核心改造实现了对因果关系的建模： 因果调整的相对优势函数 (Causally-Adjusted Relative Advantage)： GRPO的做法：计算每个候选答案的奖励\\(r_i\\)，然后通过与组内平均值比较，得到相对优势\\(A_i\\)。这个过程完全忽略了答案之间的关系。 GCPO的创新：GCPO引入了一个因果因子 \\(\\gamma_i\\)，将相对优势升级为 \\(B_i = A_i \\cdot \\gamma_i\\)。这个\\(\\gamma_i\\)是通过计算当前候选答案\\(y_i\\)的向量表示与一个“因果投影”目标向量的余弦相似度得到的。该目标向量（论文中的 \\(\\tilde{Z}_i - \\tilde{Z\u0026#39;}_i + \\bar{z}\\)）本质上是一个考虑了所有其他候选答案信息之后的、理论上更优的表示。这意味着，一个答案如果不仅自身质量高，而且与其他高质量答案在思路上“一致”或“互补”，它就会获得更高的优势权重。 因果引导的KL散度正则化 (Causal-guided KL Regularization)： GRPO的做法：使用KL散度来约束优化后的策略\\(\\pi_\\theta\\)不要偏离原始策略\\(\\pi_{ref}\\)（通常是\\(\\pi_{\\theta_{old}}\\)）太远，以保证训练稳定。 GCPO的创新：GCPO设计了一个全新的参考策略\\(\\pi\u0026#39;_{ref}\\)。这个策略不仅仅是旧策略，而是一个融合了因果结构信息的动态目标。如公式(13)所示，它由两部分加权构成：一部分是基于当前上下文的直接预测，另一部分是基于考虑了其他候选答案（即对撞结构）之后的“修正”预测。通过最小化与这个更优目标的KL散度，GCPO主动地引导模型学习生成结构上更连贯、逻辑上更一致的输出。 特点和优势： 理论基础坚实：GCPO的设计不是凭空想象，而是严格基于因果推断的定理（Theorem 1 \u0026amp; Corollary 2），这使得其有效性具有更强的理论保障。 更强的推理能力：通过建模答案间的依赖关系，GCPO能够更好地理解问题的多面性，整合互补信息，排除内在矛盾，从而在复杂推理任务上表现更佳。 鲁棒性更高：GCPO对输入的变化不那么敏感。因为它已经内化了答案间的依赖关系，所以即使某些候选答案质量不高，它也能依赖整体的结构信息做出稳健的判断。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列精心设计的定量和定性实验，在多个公认的基准上验证了GCPO的有效性。\n实验设计： 任务和数据集：实验涵盖了两大类具有挑战性的推理任务： 数学推理：使用了AIME 2024/2025, AMC 2023, MATH500, 和 MinervaMATH。这些数据集覆盖了从高中到奥林匹克竞赛级别的数学问题，是检验LLM深度推理能力的黄金标准。 代码生成：使用了HumanEval数据集，评估模型根据自然语言描述生成正确Python代码的能力。 基础模型：为了证明方法的普适性，实验在多种不同规模和架构的开源模型上进行，包括 DeepScaleR-1.5B-Preview, DeepSeek-R1-Distill-Qwen-1.5B, 和 DeepSeekR1-Distill-Qwen-7B。 对比基线：GCPO与当前最先进的（SOTA）几种后训练方法进行了直接比较，包括其直接的改进对象 GRPO，以及 ReST-MCTS, GVPO, Dr.GRPO 等强力竞争者。 评估指标：主要使用 Pass@1 准确率，即模型一次生成就通过所有测试用例的概率。这是一个非常严格的指标。 实验数据和结果： 主要性能对比 (Table 1)：GCPO在所有模型和所有数据集上都取得了最优的平均性能。 \u0026gt; 例如，在DeepSeek-R1-Distill-Qwen-7B这个强大的基础模型上，GCPO的平均Pass@1达到了66.5%，而其改进的基础GRPO为65.1%，最强的竞争对手GVPO和Dr.GRPO也只有65.7%。这0.8%到1.4%的提升在这些已经高度优化的模型和任务上是非常显著的。\n定性分析 (Figure 5)：这部分结果极具说服力。论文展示了GRPO和GCPO在解决具体问题时的推理过程对比。 \u0026gt; 以“计算从(0,0)到(6,6)不越过y=x对角线的路径数”这个问题为例（一个经典的卡特兰数问题），GRPO给出了一个看似合理但错误的答案462（总路径数的一半），因为它只应用了简单的对称性启发式。而GCPO则正确地识别出了这是一个卡特兰数问题，并使用了正确的公式 \\(C_6 = \\frac{1}{6+1}\\binom{12}{6} = 132\\)，得到了正确答案。这清晰地展示了GCPO更深层次的结构化推理能力。\n消融研究 (Ablation Study, Table 2)：为了证明GCPO中每个新组件都是必要的，作者进行了消融实验。 \u0026gt; 结果显示，如果去掉“因果调整的优势权重”（w/o Adv. Weighting），平均性能从56.8%下降到55.4%；如果去掉“因果KL正则化项”（w/o KL Term），性能则下降到55.6%。这证明了两个核心创新都是不可或缺的。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为我们打开了一扇新的大门，未来有许多激动人心的方向值得探索：\n更复杂的因果结构建模： 挑战：本文只考虑了最基础的对撞结构。在更开放的对话或规划任务中，候选答案之间可能存在更复杂的因果关系，如链式依赖（一个答案是另一个的基础）或共同原因（一个潜在的错误逻辑导致了多个表面上不同的错误答案）。 机会：开发能够自动发现和建模这些复杂因果图的算法，将使LLM的推理和规划能力产生质的飞跃。 因果感知的多智能体协作 (Causality-Aware Multi-Agent Systems)： 挑战：当多个LLM智能体协作解决一个问题时，它们的输出同样不是独立的。如何协调它们的“思想”以避免集体陷入误区？ 机会：将GCPO的思想扩展到多智能体系统，将每个智能体的输出视为一个候选答案，通过建模它们之间的因果关系来优化整体的协作策略。这在科学研究、商业决策等领域有巨大应用潜力，催生出更高效的AI协作平台。 与可解释性的结合 (Integration with Interpretability)： 挑战：我们知道因果关系能提升性能，但模型具体“理解”到了什么样的因果关系仍然是个黑箱。 机会：开发新的工具来可视化和分析GCPO学习到的内部因果依赖。例如，当模型拒绝一个看似正确的答案时，能否解释是因为它与另一条更可靠的推理路径相冲突？这将极大增强我们对AI决策的信任。 降低计算开销： 挑战：GCPO引入了额外的计算步骤（论文附录提到约有1.18倍的训练成本增加）。虽然不多，但在千亿、万亿参数模型的训练中，任何开销的增加都需谨慎。 机会：研究更轻量级的因果近似算法，或者通过模型蒸馏等技术，将大型因果教师模型的“结构化推理能力”迁移到小型模型上，实现性能与效率的平衡。这可能成为模型压缩和加速领域新的投资热点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些潜在的局限和待验证之处：\n因果模型的简化与假设： 论文假设了所有候选答案之间的关系都可以被一个对撞结构很好地概括。这个假设在结构化的数学和代码问题上可能成立，但在更开放、更模糊的领域（如创意写作、哲学辩论）是否依然适用，需要进一步验证。现实世界的因果关系可能远比这复杂。 实现的近似性： 理论推导是优美的，但工程实现充满了近似。例如，论文使用最后一个token的隐藏状态向量来代表整个候选答案的复杂语义，这无疑是一种信息压缩和损失。同样，对贝叶斯最优函数\\(\\pi^*\\)的近似也可能引入误差。这些近似处理对最终结果的影响有多大，论文没有进行深入的量化分析。 超参数的敏感性与泛化： GCPO引入了新的超参数\\(\\alpha\\)和\\(\\kappa\\)。虽然论文在附录中对其进行了敏感性分析，但这些最优值是否能很好地泛化到其他类型的模型、任务和数据分布上，仍是一个问号。对于新应用，可能需要成本不菲的重新调优。 对“负面关系”的利用不足： GCPO更侧重于奖励“一致”和“互补”的答案。对于那些明确“矛盾”的答案，它只是通过较低的奖励间接地进行了惩罚。未来是否可以设计一种机制，显式地利用矛盾关系作为一种更强的学习信号，来帮助模型定位并修复其知识或逻辑上的漏洞？ 实验范围的局限： 实验集中在数学和代码这两个高度逻辑化的领域。GCPO在需要事实性、知识性的问答（如历史、医学）或需要创造性的任务上的表现如何，尚待探索。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的你，我认为有以下几点特别值得学习和借鉴：\n核心启发：跳出独立性假设的思维定式。 学什么：在处理任何由模型生成的集合（无论是多个答案、多条摘要、还是多个计划步骤）时，不要再将它们视为一堆独立的样本。 怎么用：即使不实现复杂的GCPO，你也可以立即应用这个思想。例如，在做模型输出的后处理或集成时，可以先对候选答案进行聚类，找出哪些是同一思路，哪些是不同思路。在进行投票或选择时，给予不同思路的簇更高的权重，而不是简单地按数量投票。这是一种轻量级的“结构感知”集成方法。 创新想法：构建“共识基线”来做评估。 学什么：GCPO中那个“因果投影参考”的核心思想是，用群体共识来校准个体评估。 怎么用：当你在评估一个生成结果（例如，一篇AI写的文章）的质量时，除了与黄金标准（ground truth）比较，不妨让模型再生成几个不同的版本。然后，构建一个代表这些版本“平均思想”或“核心共识”的基线。最后，评估最初那个版本在多大程度上偏离了这个“共识基线”。一个与共识高度一致的结果可能更可靠，而一个“离群”的结果则需要警惕，即使它表面看起来不错。 方法论借鉴：跨界思维，用“旧”理论解决“新”问题。 学什么：本文的成功在于将一个相当成熟的领域——因果推断——的理论，创造性地应用到了LLM后训练这个崭新的问题上。 启发：思考一下你当前遇到的问题，是否能在其他看似不相关的领域（如博弈论、信息论、控制论、认知科学）中找到成熟的理论框架来对其进行重新描述和分析？这种跨界链接往往是重大创新的源泉。 需要补充的背景知识： 强化学习基础：特别是PPO (Proximal Policy Optimization) 算法，它是GRPO和许多现代RLHF方法的基础。 因果推断入门：强烈推荐阅读Judea Pearl的《The Book of Why》，至少要理解SCM（结构因果模型）、d-分离以及对撞结构（Collider）的基本概念。理解了Collider，你就理解了这篇论文的灵魂。 GRPO (Group Relative Policy Optimization)：作为本文的直接前身，了解GRPO的工作原理（特别是其奖励和优势函数的设计）将帮助你更清晰地看到GCPO的创新点。 ","date":"2025-08-09T00:00:00Z","permalink":"/group-causal-policy-optimization-for-post-training-large-language-models/","title":"Group Causal Policy Optimization for Post-Training Large Language Models"},{"content":"思想类似于GAN，生成与对抗，然后自我进化。\n这篇由腾讯AI Lab等机构提出的R-Zero框架，无疑是朝着“让AI自力更生”这一宏伟目标迈出的坚实一步。它探索了一个核心问题：我们能否摆脱对海量、昂贵的人工标注数据的依赖，让大语言模型（LLM）通过自我博弈和进化，实现推理能力的持续提升？这篇论文给出了一个肯定的、且极具创造性的答案。\n首先，让我们对这篇论文进行一个全面的、深入的解读。\nR-Zero的核心思想，可以概括为 “无监督下的自我驱动式协同进化”。它构建了一个完全自动化的闭环系统，系统内有两个关键角色：挑战者（Challenger） 和 解决者（Solver）。这两个角色都由同一个基础LLM初始化而来，但它们的目标和优化方式完全不同，它们在一种“相爱相杀”的动态关系中共同成长。\n整个过程就像一位武学宗师的自我修行。这位宗师（基础模型）分化出两个心神：一个心神（挑战者）负责冥想出越来越诡异、越来越难破解的招式，另一个心神（解决者）则负责寻找破解这些新招式的法门。每当解决者成功破解了一系列招式，它的功力便会提升；而挑战者则会根据解决者当前的功力水平，去创造那些“刚好在它能力边缘”的、最能激发其潜力的招式。如此循环往复，宗师的武学境界便能不断突破。\n这套框架的精妙之处在于，它解决了两个根本性难题：“学什么”（训练数据的生成）和 “怎么学”（学习信号的构建），并且全程无需人类插手。\n1. “学什么”：挑战者的动态课程生成\n挑战者的任务是生成对当前解决者而言“有难度”的问题。但“有难度”是个模糊概念，如何量化它？论文给出了一个非常聪明的定义：不确定性（Uncertainty）。\n为了引导挑战者产出具有挑战性且可解的问题，我们首先定义了一个不确定性得分。…… 这个函数激励那些让解决者最不确定（准确率接近50%）的问题。\n具体来说，挑战者生成一个问题后，会让当前的解决者（冻结状态）尝试回答 m 次（例如10次）。如果解决者给出的答案高度一致（比如10次都答对或都答错），说明这个问题对它来说太简单或太难了，价值不大。反之，如果答案出现了显著分歧，比如5次答对，5次答错，这恰恰说明该问题命中了解决者当前能力的“软肋”，是“最完美的训练题”。\n论文将此量化为一个不确定性奖励（Uncertainty Reward）：r_uncertainty = 1 - 2|p(x; S_p) - 0.5|。其中 p 是解决者回答正确的概率。你可以看到，当 p=0.5 时，这个奖励达到最大值1。论文在3.5节的理论分析中进一步指出，这种做法能最大化学习潜力，因为它使得学习者（解决者）的奖励方差最大化，从而最有效地推动其策略更新。\n此外，为了避免挑战者“偷懒”反复生成类似的问题，R-Zero还引入了重复惩罚（Repetition Penalty），通过计算问题之间的BLEU相似度来确保课程的多样性。最终，挑战者的总奖励是“不确定性奖励”减去“重复惩罚”，它通过GRPO（Group Relative Policy Optimization） 这种强化学习算法来优化自己，学会如何成为一个出色的“出题人”。\n2. “怎么学”：解决者的自我验证式学习\n解决者拿到挑战者精心设计的“考题”后，需要进行学习。但没有老师，谁来批改作业呢？R-Zero采用了“少数服从多数”的民主投票制来生成伪标签（pseudo-label）。\n具体流程是： - 过滤课程：首先，系统会过滤掉那些解决者回答一致性过高（太简单）或过低（太难，可能题目本身有问题）的题目，只保留那些“中等难度”的。论文中提到，它们保留了正确率在30%到70%之间的问题（δ=0.25），这既是难度筛选，也是一种隐性的质量控制。 - 生成伪标签：对于一个留下的问题，让解决者生成 m 个答案，出现次数最多的那个答案就被当做“正确答案”（即伪标签）。 - 强化学习：然后，解决者再次以GRPO算法进行微调。这次的奖励机制非常简单直接：如果新生成的答案与伪标签完全一致，奖励为1，否则为0。通过这种方式，解决者强化了自己对这些难题的“共识解法”。\n这个 挑战者出题 → 解决者刷题 → 解决者变强 → 挑战者出更难的题 的循环不断迭代，最终在不耗费任何人力的情况下，实现了模型推理能力的螺旋式上升。实验结果非常惊人，例如，在Qwen3-8B-Base模型上，经过三轮R-Zero迭代，其在数学推理任务上的平均分从49.18提升到54.69（提升5.51个点），在通用推理任务上，整体平均分也从34.49提升到了38.73（提升4.24个点）。这些实打实的数据，雄辩地证明了R-Zero框架的有效性和泛化能力。\nimage.png 总而言之，R-Zero不仅仅是提出了一种新算法，更是描绘了一幅AI自我进化的蓝图。它用一种优雅的、自洽的方式，让模型自己成为自己的老师和对手，为解决大模型训练中的数据瓶颈问题提供了一个极富想象力的范本。\n接下来，我将按照您提出的六个问题，对这篇论文进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义？ 研究目标： 该论文的核心研究目标是创建一个完全自主的、无需任何人工标注数据的LLM训练框架（R-Zero），旨在提升LLMs，特别是其在数学和通用领域的推理（Reasoning） 能力。\n解决的实际问题： 它主要解决当前大模型发展中的一个核心瓶颈：对大规模、高质量、人工标注数据集的过度依赖。具体来说：\n成本高昂：创建和标注海量数据集需要巨大的人力、时间和金钱投入。 扩展性差：人类标注员的能力和知识范围是有限的，这限制了AI向超越人类智能水平发展的可能性。我们很难创造出我们自己都无法解决的问题的训练数据。 数据静态：一次性创建的数据集是静态的，无法根据模型的当前能力进行动态调整，导致训练效率不高。 行业意义： 这个问题的重要性不言而喻，它关系到大模型技术能否持续、高效、可扩展地发展。\n通往“超级智能”的潜在路径：通过让模型自我进化，理论上可以摆脱“人类知识上限”的束缚，探索出超越人类认知能力的全新知识和推理模式，这是迈向通用人工智能（AGI）或超级智能的关键一步。 降低AI研发门槛：如果模型可以“自学成才”，那么对专有、昂贵数据集的依赖将大大降低，这将使更多中小型企业和研究机构能够参与到前沿大模型的训练和优化中，促进整个行业的繁荣和创新。 提升模型训练效率：动态生成的、永远处于模型“学习甜点区”的课程，远比静态数据集的训练效率更高。R-Zero展示了如何实现这种自适应课程，为未来的训练方法提供了新的思路。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其方法论，即R-Zero框架本身。它不是一个新模型，而是一套全新的训练范式。其思路和优势可以总结如下：\n新的思路：协同进化（Co-evolution） 核心特点：论文引入了“挑战者-解决者”的二元对立角色。这不同于传统的“生成器-判别器”的GANs模式，也不同于单一模型的自我迭代。这里的两个角色目标不同但互相依赖，形成了一个动态的、互相促进的进化闭环。 相比优势：相比于依赖固定外部环境（如代码解释器）的自玩（Self-play）方法，R-Zero的“环境”是动态的、由挑战者创造的，这使得它可以应用于缺乏明确验证器的开放域推理任务。相比于依赖现有任务语料的无标签RL方法，R-Zero从零开始创造任务本身，具有更强的自主性。 新的方法：基于不确定性的内在奖励机制 核心特点：挑战者的奖励函数 r_uncertainty = 1 - 2|p(x; S_p) - 0.5| 是一个纯粹的内在信号。它不依赖任何外部知识或真值标签，仅通过衡量“解决者”的内部状态（回答的摇摆不定程度）来定义“好问题”的标准。 相比优势：这种方法非常巧妙地将“难度”这一抽象概念进行了量化和操作化。它使得课程的生成具有了明确的优化目标，并且这个目标能自适应于解决者能力的变化，天然形成了一个课程学习（Curriculum Learning） 的过程。 新的范式：零数据自驱动学习 核心特点：整个R-Zero流程从一个基础模型开始，不消费任何外部数据集（无论是带标签还是不带标签的）。所有训练数据都在循环内部生成、消费、迭代。 相比优势：这是其最显著的优势。它真正意义上实现了“自力更生”，解决了数据依赖的根本问题。论文中提到： \u0026gt; While these methods all rely on a pre-existing set of unlabeled problems, R-Zero removes the need for any seed dataset. \u0026gt; （虽然这些（无标签学习）方法都依赖于一个预先存在、但未标记的问题集，R-Zero则消除了对任何种子数据集的需求。） 这使得它的可扩展性和自主性达到了一个新的高度。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列周密且有说服力的实验来验证R-Zero的有效性、泛化性和各个组件的必要性。\n实验设计： 模型多样性：为了证明方法的通用性，实验覆盖了两种不同架构、不同规模的四个模型：Qwen3-4B/8B-Base 和 OctoThinker-3B/8B。 任务多样性：评估基准分为两大类： 数学推理 (Mathematical Reasoning)：包括AMC, Minerva, MATH, GSM8K等7个高难度基准，用于衡量核心推理能力的提升。 通用领域推理 (General Domain Reasoning)：包括MMLU-Pro, SuperGPQA, BBEH等3个挑战性基准，用于检验模型学到的推理能力是否可以泛化到其他领域。 有力的基线对比：实验将R-Zero的结果与两个关键基线进行了比较： Base Model：未经任何训练的原始模型。 Base Challenger：一个简化版流程，其中解决者学习由一个未经训练的、只会随机出题的挑战者生成的问题。这个对比的目的是为了证明对挑战者进行强化学习训练的必要性。 实验数据和结果： 实验结果全面且令人信服地展示了R-Zero的威力。 显著的性能提升：如 Table 1 所示，所有模型在R-Zero框架下都表现出持续的性能增长。以Qwen3-8B-Base为例，其数学推理平均分（AVG）从49.18（Base Model）一路上升到54.69（R-Zero Iter 3），实现了 +5.51 的净增长。这远超Base Challenger的51.87分，证明了“聪明的出题人”至关重要。\n良好的泛化能力：如 Table 2 所示，在数学上学到的能力成功迁移到了通用领域。Qwen3-8B-Base的通用推理总平均分（Overall AVG）从34.49提升到38.73，净增+4.24分。这表明R-Zero提升的是模型底层的、可迁移的推理能力，而不仅仅是解数学题的技巧。\n组件的不可或缺性（消融实验）：Table 3 的消融实验有力地证明了框架中每个部分都不可或缺。在Qwen3-4B-Base模型上：\n去掉挑战者的RL训练 (w/o RL-Challenger)，数学平均分从48.06骤降至44.36，下降了3.7分，是影响最大的部分。 去掉重复惩罚 (w/o Rep. Penalty)，分数也下降了超过2分，说明题目多样性很重要。 去掉任务过滤 (w/o Filtering)，通用推理能力下降尤其严重（从30.41降到24.26），说明课程的质量和难度控制至关重要。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ R-Zero打开了一扇通往新世界的大门，门后还有广阔的未知领域值得探索。\n未来的挑战： 伪标签的准确性瓶颈：这是该方法最核心的挑战。论文在 Table 4 中坦诚地揭示了这一点：随着题目变难，由“少数服从多数”产生的伪标签的真实准确率从第一轮的79.0%下降到了第三轮的63.0%。当模型面对超越自身能力的难题时，“共识”很可能是“共同犯错”。如何设计更鲁棒的自我验证（Self-Verification） 机制，是该方向能否走得更远的关键。 从客观到主观的跨越：R-Zero目前适用于数学这类有客观正确答案的领域。如何将其扩展到开放式、主观性强的任务（如创意写作、情感对话、伦理判断）是一个巨大的难题。在这些领域，我们如何定义“不确定性”？又如何产生可靠的“伪标签”？ 计算效率和稳定性：这种迭代式的自博弈训练对计算资源消耗巨大。如何提升其效率，以及如何保证这种协同进化过程在更大模型规模和更长训练周期下的稳定性，也是一个待解决的工程和理论问题。 未来的技术和投资机会： 模型自我校准与验证技术：可能会催生出专门研究“模型自我反思”、“模型间交叉审阅”的新技术。例如，可以引入第三个“裁判（Judge）”模型，与挑战者和解决者一同进化，专门负责评估答案质量和题目难度，形成更稳固的“三足鼎立”结构。 AI自动化训练平台：基于R-Zero的思想，可以开发出商业化的“AI教练”或“AI陪练”平台。模型开发者不再需要数据团队，只需将他们的基础模型接入平台，平台就能自动为其进行针对性的强化训练，这可能成为一种新的模型即服务（Model-as-a-Service） 模式。 新一代AI芯片与基础设施：这类需要多个模型高频交互的复杂训练范式，对计算架构提出了新要求。专门为这种“协同进化”优化的AI芯片或分布式计算框架，可能会成为新的投资热点。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 以批判性的眼光审视，R-Zero虽具开创性，但并非完美无瑕。\n存在的不足与缺失： “零数据”的绝对性存疑：论文声称“from Zero Data”，但实际上，挑战者的初始提示词（Prompt）本身就是一种信息密度极高的数据注入。在附录A.2中，系统给挑战者的指令是：“你是一位专业的竞赛数学出题专家……目标是让不到30%的优秀高中生能解出……避免使用教科书上的陈词滥调……”。这个Prompt精巧地定义了任务的领域、难度、风格和创新要求，是人类专家知识的高度浓缩。因此，它并非真正的“真空”起点，而是站在了人类设计的精巧“脚手架”上。 对评估“神谕”的依赖：实验结果的评判，尤其是在数学领域，严重依赖GPT-4o作为“神谕”（oracle）来判断答案正确性。这带来一个潜在问题：R-Zero训练出的模型，究竟是学会了更强的普适性推理，还是仅仅学会了如何更好地“模仿”GPT-4o的解题风格和答案格式？这在一定程度上削弱了其结论的独立性。 长期演化的潜在风险：实验只进行了三轮迭代。如果进行几十、几百轮，系统是否会陷入某种“模式崩溃”或“自娱自乐”的状态？例如，挑战者发现某些特定类型的、有漏洞的题目总能让解决者“最不确定”，于是不断生成这类问题，导致系统在狭窄的领域里“内卷”，而丧失了通用性。 需要进一步验证和存疑之处： 规模扩展的有效性：实验止步于8B模型。当模型规模扩展到百亿、千亿甚至万亿级别时，这种协同进化的动态是否依然有效？更大、更聪明的模型可能会带来全新的、更不稳定的交互模式。 泛化能力的真实来源：模型在通用推理任务上的提升，是因为它真的学到了抽象的、底层的推理逻辑，还是因为数学训练中包含的大量逻辑、符号和结构化思维恰好对通用任务有益？需要更细致的分析来解耦其泛化能力的来源。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文是一个充满“拿来即用”创意的宝库。\n重点学习的核心思想： 流程即创新（Process as Innovation）：最大的启发是，创新不一定来自模型架构，而可以来自训练流程。构建模型间的对抗或协作关系来驱动学习，是一个极其强大的设计模式。 内在驱动的奖励设计（Self-Generated Rewards）：学会如何基于模型自身的内部状态（如不确定性、置信度、内部冲突等）来定义奖励，是摆脱外部标签依赖的关键一步。这是一个可以被广泛应用到各种自监督和强化学习任务中的核心技巧。 动态课程是王道（Curriculum is King）：不要满足于静态数据集。成功的关键在于动态生成并智能过滤训练数据，即构建一个自适应的课程。R-Zero中的过滤步骤 |p - 0.5| \u0026lt; δ 就是一个简单而高效的课程管理机制。 可借鉴的创新点启发： 应用于其他领域：这个“挑战者-解决者”的二元结构可以被迁移到任何需要提升能力的领域。 代码领域：一个“Bug制造者”挑战者 vs 一个“代码修复者”解决者。 教育领域：一个“提出刁钻问题”的学生模型 vs 一个“给出清晰易懂解释”的老师模型。 安全领域：一个“攻击系统”的黑客模型 vs 一个“防御系统”的保安模型。 “不确定性”作为探索工具：可以利用R-Zero中的不确定性度量，作为一个通用工具来主动发现任何AI模型知识体系中的薄弱环节，然后进行针对性的“补课”。 需要补充的背景知识： 要完全消化并应用这篇论文的思想，建议您补充了解以下知识： 强化学习（Reinforcement Learning）：特别是策略梯度方法，如PPO（Proximal Policy Optimization），因为论文中使用的GRPO是其变体。 自玩（Self-Play）：了解其在AlphaGo等里程碑式工作中的应用，理解其核心的博弈论思想。 无标签/自监督学习（Label-Free / Self-Supervised Learning）：熟悉该领域的宏观背景和主流方法，以便更好地理解R-Zero的定位和突破。 课程学习（Curriculum Learning）：了解如何由易到难地安排学习任务，以提升模型训练的效率和效果。 希望这份详尽的解读能帮助您深入理解R-Zero的精髓，并从中获得宝贵的启发。\n","date":"2025-08-09T00:00:00Z","permalink":"/r-zeroself-evolving-reasoning-llm-from-zero-data/","title":"R-Zero：Self-Evolving Reasoning LLM from Zero Data"},{"content":"tool-use 通过RL提高模型调用工具的能力。 ### Search-R1：Training LLMs to Reason and Leverage Search Engines with Reinforcement Learning\nWebThinker：Empowering Large Reasoning Models with Deep Research Capability ZEROSEARCH：Incentivize the Search Capability of LLMs without Searching AGENTIC REINFORCED POLICY OPTIMIZATION Search and Refine During Think：Autonomous Retrieval - Augmented Reasoning of LLMs planning 通过RL提高长程规划能力，一般都会针对信用分配问题进行解决。 ### RLVMR：Reinforcement Learning with Verifiable Meta-Reasoning Rewards for Robust Long-Horizon Agents\nGroup-in-Group Policy Optimization for LLM Agent Training Training Long-Context, Multi-Turn Software Engineering Agents with Reinforcement Learning 参考 # 2025年大模型agent rl训练多轮planning技术TORL,ToolRL, RAGEN,OTC,SkyRL-v0, GiGPO,Tool-N1 ,ARTIST, ZeroTIR, GRPO\n","date":"2025-08-08T00:00:00Z","permalink":"/agent-rl%E7%9B%B8%E5%85%B3%E8%AE%BA%E6%96%87/","title":"agent RL相关论文"},{"content":"又是信用分配的论文，不过实验做的比较少，直接把wandb的表放上来了。\n论文深度解读：从“大锅饭”式奖励到“精准制导” 这篇论文的核心，是解决大型语言模型在进行长链推理（long-chain reasoning）任务时的一个根本性难题：信贷分配（Credit Assignment）。想象一下，模型在解答一个需要50个步骤的复杂数学题。如果它在第49步都完美无瑕，但在最后一步算错了，导致最终答案错误。那么，传统的强化学习方法，如GRPO (Group Relative Policy Optimization)，会给这整个50步的解答过程打上一个“0分”的标签。这意味着，前面49个正确的推理步骤，和最后一个错误的计算步骤，受到了同等的“惩罚”。这种“成王败寇”、“大锅饭”式的奖励机制，效率低下且不合理，是提升模型深度推理能力的一大瓶颈。\n论文的作者敏锐地洞察到这一点，并提出了一个非常优雅且符合直觉的解决方案：动态熵加权（Dynamic Entropy Weighting）。这个想法的根源在于一个深刻的观察：\nIn correct reasoning sequences, token positions where the model’s policy exhibits high entropy often correspond to critical decision points in the reasoning path or moments of uncertainty where the model explores among multiple plausible options.\n换句话说，当模型在一个正确的推理路径上，遇到一个需要“三思而后行”的关键决策点时（比如选择应用哪个数学定理，或者构建哪个逻辑连接词），它的不确定性会自然增加，这在技术上表现为输出词元的策略熵（Policy Entropy）会变高。作者认为，这种“有价值的不确定性”恰恰是模型在进行真正“思考”的标志，而不应被忽视。因此，我们应该给这些高熵的关键决策点更高的奖励权重，从而更精准地指导模型学习。\n基于这一核心思想，论文提出了两种新的优化算法：\nGTPO (Group Token Policy Optimization)：这是一种词元级别（token-level）的精细化奖励方法。对于一个被判定为“正确”的输出序列，GTPO会为其中的每一个词元（token）计算一个独特的奖励。这个奖励由两部分组成：基础的序列成功奖励（比如1分），和一个正比于该词元“相对熵”的额外奖励。具体来说，一个词元的熵越高（相对于在同一时间步生成的其他序列中的词元），它获得的奖励加成也越多。如下面的公式（2）所示，它为每个词元 oi,t 计算了动态奖励 ři,t：\nři,t := ri + α * (Hi,t / Σk=1 Hk,t) * dt 这里 Hi,t 是词元 oi,t 的熵，Σk=1 Hk,t 是在所有成功序列中、同一时间步 t 的词元熵之和。这个设计非常巧妙，它奖励的是“相对”不确定性，鼓励模型在关键步骤进行有意义的探索。\nGRPO-S (Sequence-Level Group Relative Policy Optimization)：这是一个计算上更轻量级的序列级别（sequence-level）的方案。它不为每个词元单独计算奖励，而是计算整个序列的平均熵。如果一个成功的序列整体上表现出更高的平均不确定性，那么它就会获得更高的总奖励。这可以看作是GTPO的一个简化版，旨在性能和效率之间取得平衡。\n为了验证这些方法的有效性，论文进行了一系列实验。他们使用Qwen2.5-32B模型，并与当前非常强大的基线方法DAPO (Decoupled Clip and Dynamic sAmpling Policy Optimization) 进行了直接对比。实验结果令人信服。从论文的图表中我们可以看到：\n图1 (Entropy increases) 显示，与基线DAPO相比，GTPO和GRPO-S都能显著提升模型的策略熵。 图2 (Response length increases) 则显示，熵的提升直接导致了模型生成更长的响应，这意味着模型在进行更复杂的、多步骤的思考。 图3和图4 (Mean reward) 是最关键的结果。它们展示了在数学推理任务上，GTPO和GRPO-S的平均奖励（无论是best@16还是best@32指标）都显著超越了DAPO。这直接证明了通过熵加权进行精准信贷分配的有效性，成功提升了模型的性能上限。 图注：论文中的核心实验结果（图1, 3, 4）。左图显示GTPO (DAPO_TOKEN) 和 GRPO-S (DAPO_SEQ) 提升了模型熵。中图和右图显示，它们在数学推理任务上的奖励显著优于基线DAPO (DAPO_step1)。\n总而言之，这篇论文的创新性贡献在于，它没有停留在像DAPO那样优化损失函数的“计算形式”上，而是深入到了强化学习的本质——“奖励信号的设计”上。它提出了一种新颖的、基于熵的启发式方法来解决信贷分配问题，为提升LLM的深度推理能力提供了一个极具前景的新范式。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是解决基于强化学习的LLM对齐算法中普遍存在的粗粒度信贷分配（coarse-grained credit assignment）问题。\n实际问题：\n在当前主流的、类似GRPO的RL算法中，奖励是基于整个生成序列的最终结果来给予的（例如，数学题答案是否正确）。 这种机制导致序列中所有词元（tokens）获得完全相同的奖励信号，无论它们各自的贡献如何。 这在需要长链推理（如数学、编程、逻辑推理）的任务中是致命的缺陷。一个微小的、发生在推理链末端的错误，会使得前面所有正确的、关键的推理步骤都得不到正向激励，极大地降低了学习效率。 行业意义：\n突破长链推理瓶颈：解决信贷分配问题是解锁LLM进行更长、更复杂推理能力的关键。只有当模型能够理解并被激励去做出正确的“过程性”决策时，它才能真正掌握解决复杂问题的能力，而不是仅仅靠“运气”或模式匹配来碰对答案。 提升RL训练效率和稳定性：精准的奖励信号意味着更有效的梯度更新。模型可以更快地学习到有价值的行为，减少在无效探索上的浪费，从而提升整体训练的效率和稳定性。 通往更强认知能力AI的基石：当前，LLM从简单的“模式匹配”向“深度认知模拟”演进，而强化学习是这一转变的核心驱动力。这篇论文的工作，正是这条道路上一个坚实的步骤，它推动模型学习更接近于人类的、带有深思熟虑的决策过程，这对于未来构建更强大、更可靠的AI系统至关重要。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出了一个核心新思路——动态熵加权（Dynamic Entropy Weighting），并基于此设计了两个新算法：GTPO和GRPO-S。\n核心思路：动态熵加权 思路来源：观察到在正确的推理路径中，高熵的词元位置往往对应着关键的决策点。 核心做法：利用策略熵作为一种“认知不确定性”的代理指标，对奖励进行重新分配。在判定为正确的序列中，给予高熵词元或高平均熵序列更高的奖励权重。 新方法：GTPO 和 GRPO-S GTPO (Group Token Policy Optimization)： 特点：实现了真正的词元级别（token-level）信贷分配。它为成功序列中的每一个词元都计算一个独立的、由熵加权的奖励。 优势：这是最精细化的奖励方式。它能精准地告诉模型，在推理过程中的“哪一步”做出的“哪个”探索是有价值的，从而提供最直接、最有效的学习信号。 GRPO-S (Sequence-Level Group Relative Policy Optimization)： 特点：一种计算开销更小的序列级别（sequence-level）方法。它使用序列的平均熵来调整整个序列的奖励。 优势：在性能和效率之间取得了很好的平衡。虽然不如GTPO精细，但它依然抓住了“奖励探索性强的正确序列”这一核心思想，并且实现起来更简单，计算开销更小。 与之前方法（特别是DAPO）的比较： 这篇论文最根本的优势在于优化目标的层级不同。论文中有一段非常精彩的论述： \u0026gt; In short, DAPO optimizes how to sum the losses, while we optimize the content of the loss terms themselves. DAPO：DAPO的核心改进在于损失函数的“归一化”方式。它提出将损失在整个批次的所有词元上进行一次性平均，而不是像GRPO那样先在序列内求和再在序列间平均。这在数学上降低了梯度的方差，提升了稳定性，但它并没有改变“所有词元共享相同优势函数（advantage）”这一核心设定。 GTPO/GRPO-S：这篇论文的工作则更进一步，它直接重构了奖励信号本身（即ři,t或ři）。它引入了非均匀的、动态的奖励，使得不同词元/序列的优势函数（advantage）变得不同。这才是从根本上解决了信贷分配的问题。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过在数学推理基准上的受控实验，严谨地验证了所提方法的有效性。\n实验设计： 基础模型：为了保证可复现性和通用性，实验选用了开源的 Qwen2.5-32B 模型。 基线（Baseline）：选取了当时最先进（SOTA）的开源GRPO类算法 DAPO 作为强有力的对比基线。 实验框架：所有实验均在 verl 框架下进行，这与DAPO的开源实现一致，确保了公平的比较环境。 命名对应：实验中，Qwen2.5-32B-DAPO_TOKEN 对应本文的 GTPO，Qwen2.5-32B-DAPO_SEQ 对应 GRPO-S，而 Qwen2.5-32B-DAPO_step1 则是基线 DAPO。 实验数据和结果： 熵与响应长度（图1, 2）：实验数据显示，GTPO和GRPO-S的策略熵（actor/entropy）都出现了明显的回升，这反过来又导致了响应平均长度（response_length/mean）的增加。这表明模型被成功激励去进行更深入的探索和思考。 核心性能指标（图3, 4）：这是证明方法有效性的关键证据。 在 val-core/math_dapo/reward/best@32 指标上（图3），GTPO和GRPO-S的奖励曲线明显高于DAPO基线，并在训练后期达到了约0.6的水平，而DAPO则在0.5以下徘徊。 在 val-aux/math_dapo/reward/best@16/mean 指标上（图4），也呈现出同样的趋势，GTPO和GRPO-S的性能天花板被显著抬高。 结论：这些数据有力地证明，通过引入动态熵加权，GTPO和GRPO-S能够比DAPO更有效地学习长链推理任务，显著提升模型的性能上限。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为信贷分配研究打开了一扇新的大门，未来有许多激动人心的探索方向：\n超越熵的启发式方法： 熵只是一个代理指标。未来可以探索更复杂的信贷分配启发式方法。例如，论文中提到的：“训练一个轻量级的信贷模型，专门用来预测每个词元对最终结果的贡献度”，这可能比熵提供更精准的信号。 可以探索结合其他信号，如模型校准度（calibration）、词元对梯度的影响（influence functions）等来综合判断一个决策点的重要性。 思想的泛化应用： 将这种“动态奖励”或“细粒度奖励”的思想，从GRPO类的在线RL算法，扩展到更广泛的对齐技术中。例如，在DPO（Direct Preference Optimization）中，是否可以对偏好对（chosen \u0026amp; rejected）中的词元进行加权，从而让模型更关注那些决定了偏好差异的关键部分？ 熵度量的精细化研究： 论文在结尾提出了一个非常深刻的问题：如何衡量“相对熵”？文中提到了三种方式：1）比较不同序列在同一位置的词元熵（列比较）；2) 比较同一序列中不同位置的词元熵（行比较）；3) 比较所有词元的熵（全局比较）。当前工作用的是第一种，未来研究可以深入探索不同比较方式的优劣，甚至将它们结合起来。 技术和投资机会： 技术机会：能够解决长链推理的AI，将催生出更强大的AI智能体（Agent）、科学发现工具（AI for Science）、自动化代码生成和调试系统，以及能进行复杂法律或金融文书分析的AI助手。 投资机会：专注于研发和优化下一代AI推理引擎的初创公司，或是在特定领域（如生物制药、材料科学、软件工程）深度应用这些高级推理技术的公司，将具有巨大的市场潜力。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以进一步完善的地方：\n熵作为启发式方法的局限性： 论文自己也承认，熵不总是完美的。一个简单的、模板化的推理步骤，可能因为词汇多样性（lexical diversity）而表现出很高的熵，但这并非真正的“认知不确定性”。反之，一些关键但确定的步骤（如 1+1=2），其熵可能很低。如何区分“有价值的熵”和“无意义的熵”是一个挑战。 实验验证的范围： 实验主要集中在数学推理任务上。虽然这是验证长链推理的典型场景，但该方法的有效性是否能泛化到其他领域，如代码生成、长篇故事创作、或复杂的逻辑问答，还需要更多的实验来验证。 实验只使用了一种基础模型（Qwen2.5-32B）。该方法在不同架构、不同大小的模型上表现如何，也是一个未知数。 超参数的敏感性： 方法引入了新的超参数 α（用于GTPO）和 β（用于GRPO-S）。这些参数控制着熵奖励的权重，它们的取值很可能对最终性能有显著影响。论文中并未详细讨论这些超参数的调优过程和敏感性分析，这对于其他人复现和应用该方法可能会构成障碍。 计算开销： GTPO需要为每个词元计算和存储熵，相比DAPO，这无疑增加了计算和存储的开销。虽然论文声称“开销是可控的”（manageable in practice），但没有提供定量的分析，比如训练时长、显存占用的具体增加幅度。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的启发点：\n核心启发一：奖励工程（Reward Engineering）的回归 在DPO等“RL-Free”方法兴起后，社区的焦点一度从奖励模型转向了偏好数据本身。这篇论文提醒我们，奖励信号的设计本身（what to reward），依然是提升模型能力最根本、最强大的杠杆之一。不要满足于二元的（0/1）或静态的奖励，思考如何设计出能反映任务细节的动态、细粒度的奖励函数。 核心启发二：将模型的不确定性视为一种宝贵资源 通常，模型的不确定性（高熵）被视为一个负面信号。但这篇论文反其道而行之，巧妙地将其转化为一种正向的、引导探索的信号。在您的研究中，可以思考模型在哪些方面表现出的“不确定性”或“内部状态”是有价值的，并尝试将其整合到学习目标中。 核心启发三：相对性比绝对性更重要 GTPO的成功关键在于它使用了“相对熵”（一个词元的熵与同一位置其他词元熵的比较）。这个“相对”的概念非常强大。在设计任何指标或奖励时，思考一下，是绝对值重要，还是它在某个群体中的相对位置更重要？这往往能带来更鲁棒、更有意义的信号。 需要补充的背景知识： RLHF技术演进路线：强烈建议您了解从 PPO -\u0026gt; DPO -\u0026gt; IPO -\u0026gt; GRPO 的整个发展脉络。这能帮助您理解每种算法的动机和取舍，从而更好地把握这篇论文的定位和贡献。 策略梯度方法（Policy Gradient Methods）：理解PPO和GRPO这类算法的基本原理，特别是优势函数（Advantage Function）和重要性采样（Importance Sampling）的概念。 信息论基础：对熵（Entropy）有一个基本的理解，知道它如何从模型的输出概率分布（logits）中计算出来，以及它代表了什么物理意义（不确定性/信息量）。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-08T00:00:00Z","permalink":"/gtpo-and-grpo-stoken-and-sequence-level-reward-shaping-with-policy-entropy/","title":"GTPO and GRPO-S：Token and Sequence-Level Reward Shaping with Policy Entropy"},{"content":"针对信用分配问题和熵坍塌问题进行解决。名字虽然和GTPO and GRPO-S：Token and Sequence-Level Reward Shaping with Policy Entropy一样，但T的含义不同。也都是针对信用分配提出的方法。\n论文深度解读：从GRPO的困境到GTPO的革新 这篇发表于2025年8月的论文，将目光聚焦于一种近期在业界崭露头角并取得显著成效的策略优化方法——群体相对策略优化（Group-relative Policy Optimization, GRPO）。GRPO通过对一组（a group）模型生成的多个候选答案（completions）进行比较，从而估计相对奖励，摆脱了传统强化学习对齐方法（如PPO）中对独立奖励模型（reward model）的依赖，在数学推理等任务上表现出色。然而，任何技术在初期都非完美，这篇论文的核心贡献，正是系统性地揭示并解决了GRPO存在的两大关键“暗疾”。\n第一个“暗疾”是“梯度冲突”（Gradient Conflicts）。 在GRPO的框架下，模型会针对一个问题生成多个答案。这些答案中，有的可能整体是正确的（获得正向奖励），有的则是错误的（获得负向奖励）。问题在于，这些不同答案之间往往会共享一些完全相同的初始片段（prefix），比如一些用于格式化或引导思维链的特殊标签（如论文中提到的\u0026lt;reasoning\u0026gt;）。当一个包含正确格式标签的答案因为后续内容错误而被赋予负奖励时，GRPO的更新机制会“不分青红皂白”地惩罚这个答案中的所有词元（token），包括那些本该被鼓励的格式化标签。\n正如论文第4.1节所指出的：“This implies that the update to a prefix mainly depends on the relative balance of advantages across all completions in group k.”\n这意味着，一个共享前缀的梯度更新，取决于它所在的所有答案的奖励总和。如果包含这个前缀的正确答案碰巧比错误答案更长，根据GRPO的优势（advantage）计算方式，它的贡献可能会被稀释，导致整个前缀的更新方向是负面的。这会严重影响模型生成结构良好、符合指令的答案的能力。\n第二个“暗疾”是“策略崩溃”（Policy Collapse）。 这是深度强化学习中一个更为普遍但在此场景下有新表现的问题。当模型对某个答案的输出变得高度自信（即输出分布的熵值很低）但这个答案又恰好是错误的时，GRPO会对其施加巨大的惩罚。这种惩罚会迫使模型降低对正确词元的置信度，同时“病急乱投医”般地提升其他所有（哪怕是毫不相干的）词元的概率。长此以往，模型的输出分布会变得越来越“扁平”，失去判断力，最终导致性能急剧下降，无论是准确率还是格式都一并崩溃。\n论文的一个关键洞察在于，GRPO中用于防止模型偏离过远的KL散度（KL-divergence） 正则化项，对于预警和阻止策略崩溃反应过于迟钝。论文通过实验清晰地展示了这一点：\n从论文的图1（Figure 1）中可以清晰地看到，在使用LLaMA-8B模型进行训练时，代表模型性能的格式化奖励（Formatting）大约在第750步左右开始断崖式下跌，这标志着策略崩溃的发生。但此时，KL散度曲线几乎没有变化，依然平稳。直到大约1200步，当模型性能已经“病入膏肓”时，KL散度才开始急剧上升，试图“亡羊补牢”。\n与之形成鲜明对比的是熵（Entropy） 曲线。在奖励下降的同时，熵值开始急剧攀升，成为了一个更灵敏、更及时的“病情”指示器。\n基于以上两大问题的深刻洞察，论文提出了创新的解决方案——基于轨迹的群体相对策略优化（Group-relative Trajectory-based Policy Optimization, GTPO）。GTPO的核心思想是将每个答案视为一个完整的决策“轨迹”，并从两个层面进行优化：\n冲突感知梯度修正（Conflict-Aware Gradient Correction）：为了解决梯度冲突，GTPO首先会识别出在正、负奖励答案的相同位置上都出现的“冲突词元”。然后，它设计了一个精巧的梯度掩码（masking）机制。对于这些冲突词元，如果它们出现在负奖励的答案中，GTPO会直接屏蔽掉对它们的惩罚；如果出现在正奖励的答案中，则加倍奖励它们。这种“扬善抑恶”的精细化操作，保护了如格式化标签这类关键结构性词元，避免了“错杀”。\n基于熵的策略正则化（Entropy-Based Policy Regularization）：为了解决策略崩溃，GTPO摒弃了反应迟钝的KL散度，转而拥抱更敏感的熵。它引入了两个熵相关的机制：\n高熵答案过滤器：在训练开始前，GTPO会评估模型的初始熵水平。如果模型本身倾向于低熵（自信）输出，那么在训练中一旦发现某个答案的熵值超过了一个阈值（如ln(2)），GTPO就会认为这个答案“精神错乱”，直接过滤掉它，不让它的梯度污染整个模型。 熵正则化项：在损失函数中，GTPO加入了一个直接最小化输出熵的正则项。这与传统PPO希望保持一定熵（探索）的做法不同，因为作者发现GRPO本身就有内在的熵增趋势，所以需要反向施加一个力，鼓励模型保持“自信”和“专注”。 通过这两大创新，GTPO不仅解决了GRPO的内在矛盾，还带来了一个额外的好处：因为它不再需要KL散度，所以也不再需要一个额外的参考模型（reference model），这使得训练过程更轻量、更快速。\n### 1. 论文的研究目标与意义 研究目标：本文的核心研究目标是识别、分析并解决GRPO (Group-relative Policy Optimization) 方法中存在的两个主要缺陷：梯度冲突 (gradient conflicts) 和 策略崩溃 (policy collapse)。 解决的实际问题：在实际的大模型对齐训练中，工程师们常常会遇到模型性能在训练后期不升反降、或是模型无法稳定学会遵循特定输出格式的问题。这篇论文精准地定位了导致这些不稳定现象的微观（梯度层面）和宏观（策略层面）原因，并提出了一个更稳定、更高效的优化算法GTPO。 行业意义：随着大模型从“预训练”走向“精调对齐”，基于强化学习的对齐技术（如RLHF）已成为提升模型能力、使其遵循人类偏好的关键。GRPO作为一种很有前景的无奖励模型（reward-model-free）的对齐方法，其稳定性和效率直接关系到能否低成本、高效率地训练出更强大、更可靠的特定任务模型（如代码生成、数学推理等）。GTPO的提出，为整个行业提供了一个更鲁棒的对齐技术选项，有助于推动大模型在需要高精度和高可靠性场景下的应用落地。 ### 2. 论文提出的新思路与优势 论文提出的核心新方法是GTPO (Group-relative Trajectory-based Policy Optimization)。相较于之前的GRPO方法，其特点和优势体现在以下几个方面，我们可以通过一个表格来清晰对比：\n特性/机制 GRPO (先前方法) GTPO (新方法) 优势分析 正则化方式 KL散度 (KL-divergence) 熵 (Entropy) GTPO的熵正则化能更早地发现并抑制策略崩溃，反应更灵敏。同时摆脱了对参考模型的依赖，降低了计算和存储开销。 梯度更新 对答案中的所有词元进行统一的奖励或惩罚。 冲突感知梯度修正：识别冲突词元，对负奖励中的冲突词元屏蔽梯度，对正奖励中的加倍。 GTPO的梯度更新更“智能”，能够精确打击，保护了答案中的关键结构部分（如格式标签），避免了“误伤”，提升了生成质量和稳定性。 稳定性机制 依赖KL散度防止模型偏离过远。 高熵答案过滤器：主动丢弃那些可能导致不稳定的高熵（不确定性高）的答案。 GTPO的过滤机制是一种主动防御，能提前将“坏样本”剔除，从源头上减少了训练过程中的噪声和不稳定性。 核心理念 将多个答案作为独立的个体进行比较和打分。 将一组答案视为相互关联的“轨迹”，关注它们之间的分歧点，并控制其发散程度。 GTPO的“轨迹”视角更符合生成过程的本质，使得优化更加精细和有针对性。 论文在第5.1节中详细描述了冲突感知的梯度修正机制，其核心是λ_i,t这个权重：\nλ_i,t = { 1 if M_i,t = 0 (非冲突词元), 0 if M_i,t = 1 and A_i \u0026lt; 0 (负奖励中的冲突词元), 2 if M_i,t = 1 and A_i \u0026gt; 0 (正奖励中的冲突词元) }\n这个简单的分段函数，精准地体现了GTPO“保护”、“惩罚”和“奖励”的精细化思想，是其相比GRPO的一大核心创新。\n### 3. 实验设计与结果 论文通过一系列严谨的实验来验证GTPO的有效性。\n实验设计: 模型: 实验选用了LLaMA-8B和Qwen 2.5 (3B) 两个不同架构和大小的模型，以验证方法的通用性。 数据集: 训练和评测使用了学术界公认的高难度数学推理基准GSM8K和MATH。此外，还使用了AIME2024数据集进行域外泛化能力 (Out-of-distribution) 的测试。 对比基线: 实验将GTPO与SFT (Supervised Fine-tuning) 和不同参数设置的GRPO（包括不使用KL散度的β=0版本和论文推荐的β=10^-6版本）进行了全面对比。 评估指标: 采用了pass@k（在k个答案中至少有一个正确）和maj@k（在k个答案中通过投票得出正确答案）两个核心指标，全面评估模型的准确率和一致性。 实验数据与结果: 训练稳定性: 论文的图2（上）清晰地展示了GTPO在训练过程中的优越性。在LLaMA-8B于MATH数据集上的训练中，GRPO的准确率和格式分在训练后期都出现了明显的策略崩溃，而GTPO则能持续稳定提升，最终达到远超GRPO的性能。 In-distribution性能: 图2（下）的结果显示，在GSM8K和MATH的测试集上，GTPO在pass@k和maj@k指标上几乎全面优于所有GRPO的变体。例如，在LLaMA-MATH任务中，当k=30时，GTPO的maj@k接近40%，而GRPO则低于25%。这表明GTPO训练出的模型不仅更准确，而且其多个答案之间的一致性也更强。 Out-of-distribution泛化能力: 在更具挑战性的AIME2024数据集上（图3），GTPO的优势进一步凸显。尤其是在MATH数据集上训练后，GTPO在AIME上的pass@k表现显著优于GRPO和SFT，显示出其更强的泛化能力。 Figure 2 from the paper 这些实验结果强有力地证明了GTPO不仅在理论上是创新的，在实践中也是切实有效的。\n### 4. 未来研究方向与机遇 结合当前大模型领域的发展，这篇论文的研究方向可以衍生出以下值得探索的问题和机遇：\n挑战与问题: 自适应熵阈值: 论文中GTPO的熵过滤阈值（ln(2)）是固定的。未来的研究可以探索一种动态的、自适应的熵阈值机制，让模型可以根据自身状态和任务难度，智能地调整“自信”与“探索”的边界。论文在结论中也提到了这一点：“A promising future direction will be to further investigate a theoretical minimum entropy threshold”。 更广义的“冲突”定义: 目前对“冲突词元”的定义是基于位置的。能否将其扩展到语义层面？比如，“The answer is”和“Here is the solution”在语义上是冲突的吗？研究更复杂的冲突检测机制，可能会带来进一步的性能提升。 与其他对齐方法的结合: GTPO解决了GRPO的问题，但它能否与DPO (Direct Preference Optimization)等其他主流对齐方法结合，取长补短，形成更强大的混合对齐框架？ 技术与投资机会: AI对齐平台即服务 (Alignment-as-a-Service): GTPO这类更稳定、更轻量的对齐技术，降低了高质量模型精调的门槛。这为提供自动化、高效率的AI模型对齐服务的初创公司创造了机会。企业可以不再需要自己组建庞大的RLHF团队，而是通过平台服务来定制和优化自己的专属模型。 模型诊断与可观测性工具: 论文强调了熵作为模型状态“晴雨表”的重要性。这启发我们可以开发更先进的模型训练可观测性工具，通过监控熵、梯度分布等深度指标，实时诊断模型健康状况，预测并规避策略崩溃等训练风险，这将是AI基础设施层的一个重要投资方向。 ### 5. 论文的不足与存疑之处 从批判的视角来看，这篇论文虽然贡献突出，但仍存在一些可以探讨的局限性：\n实验范围的局限性: 论文的实验主要集中在数学推理 (Mathematical Reasoning) 任务上。这类任务的答案结构化程度高、对错分明。GTPO在更开放、更主观的领域，如创意写作、长文本摘要、日常对话等，其有效性还有待验证。在这些任务中，“冲突词元”的定义和奖励的评估都会变得更加模糊。 模型规模的限制: 实验采用的LLaMA-8B和Qwen 2.5-3B在2025年的标准下属于中小型模型。该方法在更大规模的模型（如70B、千亿级模型或MoE架构）上是否依然有效，以及其超参数（如熵正则化系数γ）是否需要重新调整，是需要进一步验证的问题。 对“有益冲突”的潜在压制: GTPO的核心假设是冲突是不好的。但在某些探索性任务中，在同一位置产生不同（甚至相互冲突）的词元，可能正是模型探索不同解决方案路径的有益体现。GTPO的机制是否会过度压制这种有益的探索，从而导致模型过早地收敛到局部最优解，这是一个值得深思的问题。 超参数γ的敏感性: 论文的消融研究（图4）显示，熵正则化系数γ的取值对最终性能有显著影响。虽然论文给出了一组实验结果，但并没有提供一个清晰的理论指导或自适应方法来选择最优的γ，这在实际应用中可能会增加调参的难度。 ### 6. 核心启发与知识补充 对于希望从这篇论文中汲取创新想法的读者，以下几点是您应该重点学习和思考的：\n核心启发: 熵是比KL散度更灵敏的模型状态“探针”: 这是本文最具价值的洞察之一。在您的模型训练（尤其是强化学习）中，请密切监控输出熵的变化。熵的异常飙升往往是模型即将“崩溃”的强烈信号，比KL散度等传统指标更值得信赖。 从“一刀切”到“精准调控”的梯度更新: 不要将一个被赋予负奖励的序列中的所有部分都视为“坏”的。学习GTPO的思想，识别出序列中那些与最终失败无关、甚至是有益的“结构性”部分，并对它们进行保护性或差异化的梯度更新。这个思想可以应用到任何基于序列反馈的优化任务中。 用更直接的机制替代复杂的正则化: GTPO用简单的熵正则化和过滤器替代了需要参考模型的KL散度，既提升了效果又降低了复杂性。在您自己的研究中，当遇到一个复杂问题时，可以思考一下是否有可能找到一个更直接、更根本的指标来进行调控，而不是沿用传统的复杂方案。 需要补充的背景知识: PPO (Proximal Policy Optimization): 这是理解几乎所有现代LLM强化学习对齐方法的基础。您需要了解其核心思想，特别是优势函数（Advantage Function）和Clipped Surrogate Objective。 RLHF (Reinforcement Learning from Human Feedback): 了解RLHF的完整流程，包括SFT、奖励模型训练和PPO优化这三个阶段。这将帮助您理解GRPO和GTPO试图解决的是哪个环节的问题。 信息熵 (Shannon Entropy): 重新回顾信息熵的定义及其在概率分布中的含义（衡量不确定性）。这将帮助您深刻理解为什么熵可以作为模型“混乱”程度的度量。 总而言之，这篇论文是一次非常精彩的学术探索，它从一个看似成熟的领域（GRPO）中挖掘出深层次的问题，并通过优雅而有效的方法予以解决，为大模型对齐技术的发展提供了重要的理论洞察和实践工具。\n","date":"2025-08-08T00:00:00Z","permalink":"/gtpotrajectory-based-policy-optimization-in-large-language-models/","title":"GTPO：Trajectory-Based Policy Optimization in Large Language Models"},{"content":"------------------------------------------------------------------------------------------------------ # Qwen-1 + Embedding and output projection. (Untied embedding for input embedding and output projection) + ROPE + QKV bias required + Pre-Norm \u0026amp; RMSNorm + SwiGLU ------------------------------------------------------------------------------------------------------ # Qwen-2 - Multi-Head Attention + MoE + Grouped Query Attention + Dual Chunk Attention + YARN + Expert Granularity + Expert Routing + Expert Initialization + Shared Experts ------------------------------------------------------------------------------------------------------ # Qwen-2.5 + More control tokens. 3 -\u0026gt; 22 ------------------------------------------------------------------------------------------------------ # Qwen-3 - QKV bias - Shared experts + QK-Norm ------------------------------------------------------------------------------------------------------ 参考 Qwen 各版本主要结构变化\n","date":"2025-08-08T00:00:00Z","permalink":"/qwen/","title":"qwen"},{"content":"信用分配在规划中，并且用了我想要做的benchmark：ALFWorld、ScienceWorld和WebShop。\n这篇论文的核心思想，可以说是在如何训练能执行复杂任务的“智能体（Agent）”这个问题上，提出了一个“大力出奇迹”但又充满智慧的解决范式。传统方法，尤其是强化学习（Reinforcement Learning, RL），在处理需要很多步骤才能看到最终结果（即长时程规划 Long-Horizon Planning）且中途没有任何提示（即稀疏奖励 Sparse Reward）的任务时，会陷入一个巨大的困境，我们称之为“信用分配难题（Credit Assignment Problem）”。这就像你在一个巨大的迷宫里走了100步，最后终于找到了出口，但你很难说清楚这100步里，哪几步是真正关键的。强化学习算法在这种情况下，常常因为反馈信号太弱或太滞后而无法有效学习。 而这篇论文的作者们另辟蹊径，他们认为：“既然直接利用奖励信号进行复杂的梯度优化这么困难，我们何不换个思路？” 他们不再将稀疏的成功信号（比如任务最终是否完成）用作一个复杂的、需要反向传播的“学习指令”，而是将其作为一个极其简单、明确的“数据过滤器”。只保留那些最终成功的尝试过程，用这些高质量的“成功经验”来喂养模型，让模型在潜移默化中学会如何成功。\n这个过程形成了一个强大的自我改进循环，论文称之为“数据策划飞轮（Data Curation Flywheel）”。这个飞轮一旦转起来，智能体的能力就会像滚雪球一样越来越强：模型根据现有能力生成尝试数据 -\u0026gt; 用最终奖励筛选出成功的轨迹 -\u0026gt; 在这些高质量的成功轨迹上进行微调 -\u0026gt; 训练出更强的模型 -\u0026gt; 生成质量更高、成功率更高的尝试数据。这套被称为BPO (Beyond Policy Optimization) 的三阶段框架，在多个高难度智能体任务上取得了惊人的效果，甚至用一个80亿参数的模型，在效果上超越了更大、更强的专用推理模型。\n接下来，我将按照您提出的六个问题，对这篇论文进行更详细的剖析。\n### 论文的研究目标与意义 研究目标：论文的核心研究目标是解决如何让大型语言模型（LLMs）在互动的、长时程的、稀疏奖励的环境中，有效学习并执行复杂任务。具体来说，就是训练一个能自主规划、推理并行动的“智能体（Agent）”，使其能在没有即时反馈的情况下，完成一系列连贯操作，最终达成目标。\n实际问题：目前，虽然LLMs在文本生成、知识问答等“静态”任务上表现出色，但要将它们应用于动态世界，如机器人控制、复杂的网页操作、科学实验等，就面临巨大挑战。这些真实世界任务的共同特点是：\n长时程（Long-Horizon）: 需要执行一长串动作序列。 稀疏奖励（Sparse Reward）: 大部分时间里，环境不会告诉你做得好不好，只有在任务最终完成时，才会给予一个“成功”的信号。 推理与行动的结合: Agent不仅要理解指令，还要在与环境的互动中不断推理、调整计划。 行业意义：解决这个问题是迈向通用人工智能（AGI）的关键一步。一个能有效应对稀疏奖励和长时程规划的智能体，意味着我们可以：\n构建更自主的机器人: 例如，让机器人根据“打扫整个公寓”这样一个高级指令，自主完成一系列复杂的导航、识别、抓取、操作任务，而不需要人类对其每一步进行编程或监督。 开发更强大的自动化工具: 例如，创建一个能根据用户模糊需求（“帮我预订下周末去夏威夷的性价比最高的旅行套餐”）自动完成信息搜集、比较、填表、支付等一系列网页操作的智能助手。 加速科学发现: 在模拟环境中，智能体可以根据科学目标（如“设计一种新型催化剂”）自主进行成千上万次的虚拟实验，这在药物研发、材料科学等领域具有革命性潜力。 因此，这篇论文的研究为构建能够胜任真实世界复杂任务的下一代AI智能体，提供了一个极其重要且有效的范式，推动了行业从“语言模型”向“行动模型”的转变。\n### 新思路、方法与优势 论文提出了一个名为BPO (Beyond Policy Optimization) 的三阶段框架，其核心优势在于巧妙地规避了传统强化学习的“信用分配”难题，代之以一个数据驱动的自我完善循环。\nTo address the challenges…, this work introduces BPO, a novel three-stage framework of bootstrapping, extrapolation, and refinement that establishes a self-improving data flywheel.\n下面我将详细解读其三个阶段的创新之处：\n阶段一: 引导推理（Bootstrapping Reasoning via Planning Quaternion Synthesis） 这是整个框架的基础，旨在生成高质量的初始训练数据。这里的核心创新是规划四元组（Planning Quaternion）和长短链思想融合（Long-Short Chain-of-Thought Fusion）。\n规划四元组 (Planning Quaternion): 传统方法通常将“思考”和“行动”混在一起。而BPO框架将智能体的每一步决策分解为一个结构化的四元组：Qt = (ot, et, pt, at)。\not (Observation): 当前环境的观察。 et (Elaborate thought): 详尽、详细的“长链”思考过程。这是模型对当前情况的深度分析和推理。 pt (Planning thought): 简明、扼要的“短链”规划意图。这是长链思考后提炼出的核心结论和下一步计划。 . at (Action): 最终执行的动作。 这个设计的精妙之处在于解耦了即时推理和长远规划。et 保证了决策的深度和质量，而 pt 则浓缩了关键信息，为后续步骤提供了一个简洁的上下文。\n长短链思想融合 (Long-Short Chain-of-Thought Fusion): 在实际推理（Inference）时，模型会先生成详细的 et 来辅助决策，但在将其存入历史记录时，只保留简短的 pt。\n优势: 这样做极大地降低了计算开销和上下文长度。避免了在长时程任务中，早期的重要信息被后面冗长的思考过程挤出模型的注意力窗口，从而实现了性能和效率的完美平衡。如论文Table 5所示，BPO模型在ScienceWorld任务上，平均每步只需要112个token，远低于DeepSeek-R1的620个，但成功率却高出近27个百分点（83.16% vs 56.70%）。 阶段二: 课程合成与外推（Extrapolation via Curriculum Synthesis） 为了让模型不仅仅是模仿，而是能应对更复杂、更多样的新情况，BPO引入了课程学习。\n生成式任务增强: 利用一个强大的“教师模型”（如GPT-4o），凭空创造出新的、更难的任务。这些合成任务在结构、长度和目标上都与初始数据不同，从而扩展了模型的泛化能力。 复杂度分级课程学习: 将所有任务（真实的和合成的）按复杂度（主要通过轨迹长度来衡量）进行排序，从易到难地训练模型。这就像教学生一样，先学加减，再学乘除。这种方式可以稳定训练过程，避免模型在初期就被过难的任务“劝退”，从而系统性地构建其解决复杂问题的能力。 阶段三: 奖励门控的自我精炼（Self-Refinement through Reward-Gated Trajectory Refinement） 这是BPO框架的“点睛之笔”，也是“飞轮”效应的核心。\n奖励门控的拒绝采样（Success-Contingent Rejection Sampling）: 在这个阶段，训练好的模型会与环境互动，产生大量新的轨迹。然后，利用环境给出的最终二元奖励（成功/失败）作为一个过滤器：只有那些最终成功的轨迹才会被保留下来。 \u0026gt; While a binary success reward is an insufficient signal for gradient-based credit assignment…, it is a effective, noise-free signal for classification.\n这一步直接将传统RL中最棘手的“稀疏奖励”信号，从一个模糊的“学习信号”转变为一个清晰的“数据准入标准”。\n迭代式策略优化: 将筛选出的高质量成功轨迹加入到训练数据集中，然后在新扩充的数据集上重新微调模型，得到一个更强的下一代模型（πθ_k+1）。这个过程可以不断重复，每一次迭代，模型都会在自己发现的成功经验上学习，从而实现持续的自我提升。\n这个阶段的巨大优势在Table 4中体现得淋漓尽致。在ScienceWorld任务上，传统微调（SFT）的成功率是77.73%，但如果在此基础上应用DPO或GRPO等基于偏好的RL方法，成功率反而分别下降到73.46%和64.43%。这有力地证明了，在稀疏奖励场景下，直接优化策略是多么困难。而BPO的方法将成功率提升到了85.15%，彰显了其范式的优越性。\n### 实验设计与结果 论文通过在三个公认的、具有挑战性的智能体基准测试上进行大量实验，来验证其方法的有效性。\n实验设计:\n基准 (Benchmarks): ALFWorld: 一个基于文本的家庭环境模拟器，要求智能体根据高级指令完成多步家务任务。 ScienceWorld: 一个模拟的科学实验环境，要求智能体完成复杂的、需要长时程规划的初中科学实验。 WebShop: 一个模拟的电商网站环境，要求智能体根据用户指令搜索和购买商品。 对比基线 (Baselines): 实验对比了多种模型，包括： 零样本模型 (Zero-shot Models): 如Llama-3.1-8B-Instruct，Qwen-2.5-7B-Instruct等基础模型，以及Deepseek-R1等专用推理模型。 微调模型 (Fine-Tuned Methods): 包括标准的监督微调（SFT）、ETO以及当时最先进的MPO方法。 评估指标: 核心指标是任务成功率 (Success Rate, SR)，辅以平均奖励 (Average Reward) 来评估轨迹的整体质量。 核心实验: 总体性能对比: 验证BPO框架的最终效果。 消融研究 (Ablation Study): 通过逐一添加BPO的三个阶段，来验证每个组件的贡献。 模型无关性验证: 将BPO框架应用于不同的基础模型（Llama和Qwen），检验其通用性。 与强化学习方法的对比: 直接与DPO、GRPO等RL方法进行比较。 实验数据与结果:\n总体性能SOTA: 如Table 1所示，BPO在所有三个基准上都取得了当前最佳性能，平均成功率达到88.16%。这显著高于最强的微调基线MPO（81.83%），也超过了像Deepseek-R1这样的大型专用推理模型。特别是在ALFWorld的未见（Unseen）任务上，BPO的成功率（89.60%）比MPO（78.40%）高出超过11个百分点，展示了其强大的泛化能力。\nTable 1: Comparative analysis of Success Rate (%) 各阶段贡献显著: Table 2的消融研究结果清晰地展示了BPO框架的“三级火箭”效应。以Llama-3.1-8B为基础模型，其原始平均成功率仅为44.88%。\n经过阶段一（引导推理）后，成功率飙升至80.60%。 加入阶段二（课程合成）后，成功率进一步提升至85.24%。 最后经过阶段三（自我精炼），成功率达到最终的88.16%。 这个结果雄辩地证明了，BPO的每一个阶段都是不可或缺且贡献巨大的。 越复杂，优势越明显: Figure 2展示了在ScienceWorld任务中，随着任务复杂度（按步骤数分为简单、中等、困难）的增加，BPO的性能优势愈发明显。在“困难”（\u0026gt;17步）任务上，BPO的成功率比次优方法高出12个百分点，而许多基线方法的性能则随着复杂度增加而急剧下降。这表明BPO构建的智能体具有超强的鲁棒性。\n通过这些设计严谨的实验和令人信服的数据，论文有力地证明了BPO框架的有效性、鲁棒性和通用性。\n### 未来探索方向与机遇 这篇论文为智能体的研究开辟了新的道路，也指明了未来值得探索的方向和潜在的机遇。\n值得探索的问题与挑战:\n多模态领域的应用: 论文主要聚焦于基于文本的环境。如何将这套“数据飞轮”范式扩展到多模态（Multi-modal）领域，例如，训练一个能在真实物理世界中操作的机器人，或者一个能理解并操作图形用户界面（GUI）的视觉智能体，将是下一步的关键挑战。 更复杂的奖励函数: BPO巧妙地利用了二元（成功/失败）奖励。但真实世界中很多任务的反馈是更复杂的、非二元的稀疏奖励（例如，电商任务的最终利润、游戏中的最终得分）。如何将BPO的思想推广到这些更复杂的奖励场景是一个开放问题。 安全与对齐（Safety and Alignment）: “数据飞轮”依赖于“成功”的经验。但这可能带来一个风险：如果智能体发现了一个“成功”但有害或不符合人类价值观的“捷径”（Reward Hacking），飞轮会不断强化这种不良行为。如何在自我改进的循环中嵌入安全约束和价值对齐机制，至关重要。 数据策划的效率: 在任务成功率极低的情况下，BPO的阶段三可能需要生成海量的轨迹才能筛选出少数几个成功的样本，这在计算上是昂贵的。研究如何提高数据生成的效率，例如，用一个辅助模型来预测轨迹的成功潜力，从而进行更智能的探索，是一个有价值的方向。 新的技术和投资机会:\n智能体数据平台: BPO的核心是数据。未来，专门用于生成、管理、筛选和增强智能体互动轨迹的高质量数据平台将变得极具价值。这可能催生出“智能体领域的Hugging Face”。 Agentic AI 基础模型与框架: 不同于通用LLMs，专门为Agentic任务设计的基础模型和训练框架（如BPO的开源实现）将成为一个新的赛道。这些框架将数据策划和课程学习作为其核心组件。 模拟环境即服务（Simulation-as-a-Service）: 高保真的、可扩展的、能提供多样化任务的模拟环境对于训练和评估智能体至关重要。为AI研究者提供这样的模拟平台本身就是一个巨大的商业机会。 ### 批判性视角下的不足与缺失 尽管这篇论文非常出色，但从批判的角度看，仍然存在一些值得商榷或需要进一步验证的地方。\n对强大教师模型的依赖: BPO的阶段一和阶段二在很大程度上依赖于一个强大的“教师模型”（如GPT-4o）来生成初始数据和课程。这意味着整个框架的性能上限在初期受限于这个教师模型的智慧。它并非一个完全“从零开始”的自举系统，其“冷启动”问题依然存在。 自我精炼阶段的可扩展性: 论文中的实验在阶段三只进行了一两轮（K=1或2）迭代。对于那些极其困难、初始成功率可能低于1%的任务，这种依赖“拒绝采样”的方式可能会因为迟迟无法产生成功的轨迹而导致“飞轮”转不起来。其在该类极限场景下的可扩展性和计算成本有待验证。 任务复杂度定义的简单化: 论文主要使用“轨迹长度”作为任务复杂度的代理指标。虽然直观，但这可能过于简单。真正的任务复杂度可能源于更抽象的推理、组合爆炸或需要创造性地使用工具，而这些是长度无法完全体现的。 从模拟到现实的鸿沟（Sim-to-Real Gap）: 所有实验都是在文本模拟器中进行的。尽管这些模拟器设计精良，但与充满噪声、延迟和不确定性的真实物理世界相比，仍然是高度简化的。BPO框架在真实机器人或真实网络环境中的表现如何，是其价值最终的试金石，而这部分工作是缺失的。 ### 核心启发与知识补充 对于希望从中汲取创新思路的读者来说，这篇论文提供了几个极其宝贵的启发。\n我应该重点学习什么？有哪些启发？\n范式转移：从算法为中心到数据为中心: 对于智能体训练，最核心的启发是将焦点从设计越来越复杂的强化学习算法，转移到如何系统性地、迭代式地策划和生成高质量的训练数据上。在智能体领域，“数据飞轮”可能比“算法优化”更有效。 反馈信号的巧妙利用: 不要总想着如何将一个微弱的信号（如稀疏奖励）直接用于梯度下降。思考一下，是否可以将其转变为一个强信号，用于其他目的，比如论文中的数据筛选。这种“改变问题定义”的思路在很多领域都极具威力。 认知过程的解耦: 规划四元组中对“详尽思考”（长链）和“规划意图”（短链）的解耦是一个 блестящая идея (brilliant idea)。在设计自己的智能体或复杂系统时，可以借鉴这种思想，将不同的认知功能模块化，既能保证决策质量，又能优化系统效率。 课程学习的重要性: 不要用同质化的数据“一锅炖”。通过由易到难的课程学习和主动生成多样化的挑战，可以更稳定、更高效地培养模型的泛化能力和鲁棒性。 我需要补充了解哪些背景知识？\n为了更深入地理解这篇论文，建议您补充以下背景知识：\n强化学习（Reinforcement Learning, RL）基础: 尤其是马尔可夫决策过程（MDP）、策略优化（Policy Optimization）以及稀疏奖励和信用分配等核心概念和挑战。 大型语言模型（LLMs）与提示工程（Prompt Engineering）: 了解思维链（Chain-of-Thought, CoT）等技术，因为它构成了BPO中推理部分的基础。 智能体框架（Agentic Frameworks）: 熟悉如ReAct (Reason+Act) 等经典的智能体框架，因为BPO可以看作是对ReAct风格互动循环的一种深度优化和改进。 偏好优化算法: 了解如DPO (Direct Preference Optimization) 和GRPO等方法，这能帮助您更好地理解为什么BPO选择的路径与它们截然不同，以及其优势所在。 总而言之，这篇论文不仅仅是提出了一种新方法，更是为如何构建更强大、更通用的AI智能体提供了一种全新的、极具前景的哲学和工程范式。\n","date":"2025-08-07T00:00:00Z","permalink":"/beyond-policy-optimizationa-data-curation-flywheel-for-sparse-reward-long-horizon-planning/","title":"Beyond Policy Optimization：A Data Curation Flywheel for Sparse-Reward Long-Horizon Planning"},{"content":"信用分配在推理任务中。对比了传统prm和RLVR。让大模型对step的正确与否打标签，然后针对step计算reward，然后加权计算最终token的reward计算adv。就是不知道针对adv进行加权计算是否更直接更好一点。很容易想到的方法。\n论文综合深度解读 这篇发表于2025年8月的论文，由来自中国人民大学和腾讯微信搜索的研究团队共同完成，提出了一种名为CAPO（Credit Assignment Policy Optimization） 的新方法，旨在通过一种创新的可验证生成式信誉分配（Verifiable Generative Credit Assignment）机制，显著增强大型语言模型（LLM）在复杂推理任务中的能力。\n要理解这篇论文的精髓，我们必须首先把握其试图解决的核心困境。当前，利用带可验证奖励的强化学习（Reinforcement Learning with Verifiable Rewards, RLVR） 来微调LLM以提升其推理能力，已成为一种主流范式。这种方法通常依赖一个简单的、基于规则的二元反馈信号——例如，一个数学题的最终答案“对”或“错”。如果答案正确，模型生成的所有内容（tokens）都会获得正奖励；反之，则全部受到惩罚。\n这种“一刀切”的奖励机制，论文称之为粗粒度信誉分配（Coarse-grained credit assignment），存在一个致命缺陷。论文在图1中生动地展示了这个问题：\nFigure 1: The coarse credit assignment problem in RLVR. RLVR assigns rewards based solely on the final outcome, failing to provide differentiated feedback. This lack of granular feedback hinders the model’s ability to learn robust reasoning processes.\nimage.png 图1展示了四个不同的解题过程。前两个虽然最终答案都正确（都得到+1奖励），但第一个过程完美无瑕，第二个则包含错误步骤。后两个答案都错误（都得到-1奖励），但第三个只是在最后有微小失误，而第四个则从头错到尾。\n在传统RLVR框架下，模型无法区分这些细微但关键的差异。它不知道应该“学习”第一个过程的严谨，还是应该“模仿”第二个过程的“侥幸”；它也无法从第三个过程中吸取“大部分正确”的经验，只会将其与第四个“完全错误”的过程同等对待。这就是信誉分配问题：在一个长序列的行动（在这里是生成token）后，我们如何准确地判断出哪些特定的行动对最终结果贡献了价值，哪些导致了失败？\n为了解决这个问题，学术界探索了不同路径。一类方法是奖励密集化（Densification-based RL），例如使用博弈论中的Shapley值来评估每个token的贡献。但论文指出，这类方法产生的奖励信号往往是不可验证的（non-verifiable），其准确性依赖于另一个奖励模型，这可能引入新的不稳定性和被“钻空子”（reward hacking）的风险。另一类是过程奖励模型（Process Reward Models, PRMs），它会对推理的每一步都进行打分。这种方法虽然精细，但严重依赖高质量、高成本的人工标注，并且在在线强化学习中，需要对每一步都进行多次模型推理来获取奖励，效率极低。\n在此背景下，CAPO应运而生。它巧妙地试图融合过程奖励的精细度和结果奖励的可验证性与高效性。其核心思想是，利用一个现成的、能力更强的LLM，充当“生成式过程奖励模型”（LLM-as-GenPRM）。这个“裁判模型”的任务，是在一次前向传播中，通读“学生模型”生成的完整解题步骤，并直接输出其中哪些步骤是错误的。\n这个过程可以分解为以下几步，论文的图2清晰地展示了其概览：\nimage.png 推理生成：策略模型（即正在被训练的LLM）针对一个问题生成一个完整的推理过程。\n生成式过程评估：将生成的推理过程喂给一个更强大的、现成的LLM（例如，论文中使用的Qwen2.5-72B或Llama-3-70B）。这个“裁判模型”被指示去识别并列出所有错误的推理步骤的索引。\n投票机制增强鲁棒性：为了减少单个“裁判”可能带来的随机性或误判，CAPO引入了投票机制。它会通过多次采样，让“裁判模型”生成多个（例如N=4个）批判结果，然后通过交集投票（Intersection Vote）或多数投票（Majority Vote）等策略，整合出一个更可靠的错误步骤集合。交集投票要求所有批判都认为某一步是错的，才将其标记为错误，这是一种高精度、低召回的保守策略，适合能力较弱的模型；而多数投票则更为平衡。\n精细化信誉分配：有了精确到步骤的错误标签，CAPO便可以进行精细化的奖励分配。它设计了一个巧妙的奖励公式： \u0026gt; \\(R_t = r_v \\cdot W_{whole} - I(t \\in T_{err}) \\cdot W_{process}\\)\n这里，\\(R_t\\) 是每个token \\(t\\) 获得的最终奖励。\\(r_v\\) 是基于最终答案的二元奖励（对为1，错为0）。\\(W_{whole}\\) 是结果奖励的权重。\\(I(t \\in T_{err})\\) 是一个指示函数，如果token \\(t\\) 属于被标记为错误的步骤，则为1，否则为0。\\(W_{process}\\) 是过程惩罚的权重。\n至关重要的是，CAPO采用了非对称权重配置（\\(W_{whole} \u0026gt; W_{process}\\)）。例如，在实验中采用 \\(C=2, P=1\\)（即\\(W_{whole}=2, W_{process}=1\\)）。这意味着：\n答案正确，过程完美：奖励为 +2。 答案正确，过程有错：奖励为 2 - 1 = +1。 答案错误，过程完美（理论情况）：奖励为 0。 答案错误，过程有错：奖励为 0 - 1 = -1。 这种设计建立了一个清晰的学习层次：“首先要确保最终答案的正确性，这是首要目标；在此基础上，我们再鼓励你优化推理过程。” 这种机制确保了模型不会因为过度关注过程的完美而牺牲最终结果，同时又为模型提供了修正内部推理逻辑的明确信号。\n实验部分，论文在多种模型（Llama-3系列和Qwen2.5系列）和多个基准测试（包括数学推理类的MATH、OlympiadBench，以及通用推理类的MMLU-Pro、ARC-c等）上验证了CAPO的有效性。结果（如Table 2, 3所示）一致表明，CAPO及其变体显著优于传统的SFT（监督微调）和基于结果奖励的RL方法（如GRPO-Rule）。例如，在Llama-3-1B模型上，CAPO-Intersection方法在所有任务的平均分上比GRPO-Rule提升了约18%（0.170 vs 0.144）。这证明了其精细化信誉分配机制的巨大价值。\n总而言之，CAPO的创新性在于它为解决LLM推理中的信誉分配问题提供了一个高效、可扩展且可验证的解决方案。它没有发明一个全新的模型，而是巧妙地利用现有的大模型生态，通过“以模治模”的方式，将一个复杂的、难以标注的过程监督问题，转化为一个高效的、生成式的文本判断任务，并通过精巧的奖励机制设计，成功地引导模型学习更鲁棒、更可靠的推理路径。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 本文的核心研究目标是解决在利用强化学习（RL）微调大型语言模型（LLM）进行复杂推理时遇到的信誉分配（credit assignment）问题。\n想要解决的实际问题：\n区分“侥幸的正确”与“逻辑的正确”：现有方法通常只奖励最终结果正确的答案，但无法区分一个解题过程是逻辑严谨、步步为营，还是过程中存在错误但歪打正着。这使得模型无法学习到真正稳健的推理能力。 从“有价值的失败”中学习：一个答案即使最终错误，其推理过程也可能包含大量正确的步骤和思路。粗粒度的惩罚会让模型无法识别和保留这些有价值的部分，降低了学习效率。 提升RL训练的效率和稳定性：过程奖励模型（PRM）虽然能提供精细化指导，但数据标注和在线推理成本高昂。CAPO旨在提出一种兼具效率、可扩展性和可验证性的精细化信誉分配方案。 对行业发展的重要意义： LLM的推理能力是其迈向通用人工智能（AGI）的关键瓶颈。只有当模型不仅能给出答案，还能提供可靠、可解释、逻辑连贯的推理过程时，我们才能在以下高风险、高价值领域信赖并大规模应用它们：\n科学研究：自动进行数学证明、设计实验、分析数据。 医疗诊断：根据病历和检验报告，进行逻辑严谨的诊断推理。 法律金融：分析复杂的法律条款、制定金融策略。 教育：为学生提供个性化的、循序渐进的解题辅导。 因此，解决信誉分配问题、提升LLM的推理稳健性，是推动大模型从“玩具”走向“工具”，从“通用聊天”走向“专业赋能”的核心技术引擎，对整个AI产业的发展至关重要。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心方法是CAPO (Credit Assignment Policy Optimization)，它并非一个全新的模型结构，而是一套创新的训练框架和方法论。其新颖之处体现在以下几个方面：\n思路创新：利用LLM作为生成式过程奖励模型 (LLM-as-GenPRM) 思路：CAPO不再依赖昂贵的人工标注或不稳定的奖励模型来获取过程反馈，而是开创性地使用一个现成的、更强大的LLM（即“裁判模型”）来自动、高效地识别并标注出待训练LLM（即“学生模型”）推理过程中的错误步骤。 特点：这是一种“以子之矛，攻子之盾”的 bootstrapping（自举）思想，利用了LLM本身强大的语言理解和逻辑分析能力来监督和提升同类模型。 方法创新：结合投票机制和非对称奖励函数 投票机制（Voting Mechanisms） 方法：为了增强“裁判模型”判断的鲁棒性，CAPO引入了投票机制。通过多次采样生成多个批判结果，再用交集（Intersection）、并集（Union）或多数（Majority）等策略进行整合，得到一个更可靠的错误步骤列表。 优势：相比单次判断，这极大地降低了噪声和随机误判的影响，提供了更稳定的学习信号。同时，可以根据“学生模型”的能力水平灵活选择投票策略（例如，对弱模型用高精度的“交集”，对强模型用高召回的“并集”）。 非对称奖励函数（Asymmetric Reward Formulation） 方法：CAPO设计的奖励函数 \\(R_t = r_v \\cdot W_{whole} - I(t \\in T_{err}) \\cdot W_{process}\\) 中，特意设置了结果权重 \\(W_{whole}\\) 大于过程惩罚权重 \\(W_{process}\\) （例如 \\(2 \u0026gt; 1\\)）。 优势：这种设计建立了一个清晰的学习优先级，有效平衡了“把题做对”和“把过程写好”这两个目标。它避免了模型为了追求过程的完美而陷入局部最优（例如，生成非常简单但无用的正确步骤），确保了学习始终朝着解决问题的最终目标前进。 与之前方法的比较优势： 特性 传统RLVR (如GRPO-Rule) 传统PRM (过程奖励模型) CAPO (本文方法) 奖励粒度 粗粒度 (整个序列) 精细化 (每一步) 精细化 (每一步) 可验证性 高 (基于规则) 依赖标注数据质量，不一 高 (基于强大LLM的逻辑验证) 数据依赖 无需额外标注 需要大量高质量人工标注 无需人工标注，利用现有LLM 在线效率 高 低 (每步需多次推理) 高 (一次推理完成所有步骤批判) 核心优势 简单、可验证 精细化 兼具精细、可验证、高效、低成本 如论文中的 Table 1 所示，CAPO是唯一一个同时在提供可验证奖励（Providing Verifiable Rewards）和高效性（Number of Inference Calls为1）上都表现出色的方法。\nTable 1: Comparison of existing methods to conduct credit assignment. … CAPO is an efficient, simple, and general method that provides verifiable credit assignment.\n### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列全面的实验来验证CAPO的有效性，实验设计严谨，覆盖了多种模型、多个任务和不同的评估维度。\n实验设计： 模型选择：为了验证方法的通用性，实验采用了来自不同机构、不同规模的开源模型，包括 Llama-3-1B/3B 和 Qwen2.5-1.5B/7B。 任务选择： 域内（In-Distribution）数学推理任务：包括 MATH、Minerva-Math、OlympiadBench、AMC23 等6个高难度数学基准，用于测试模型的核心推理能力。 域外（Out-of-Distribution, OOD）通用推理任务：包括 ARC-c、MMLU-Pro、GPQA-diamond 等3个基准，用于测试模型的泛化能力。 基线方法： SFT (Supervised Fine-tuning)：作为最基础的监督微调基线。 GRPO-Rule：代表了主流的、基于最终结果二元奖励的RLVR方法。 GRPO-GenORM：一种使用生成式模型来判断最终结果好坏的基线，但仍属于粗粒度奖励。 评估指标：采用 pass@1 准确率，即模型在贪心解码（greedy decoding）下一次性生成正确答案的比例，这是一个严格且有说服力的指标。 实验数据和结果： 实验结果有力地证明了CAPO的优越性。 主要性能对比 (Table 2 \u0026amp; Table 3)：CAPO在几乎所有的模型和基准测试上都显著优于所有基线方法。 \u0026gt; 在 Qwen-2.5-7B 模型上（Table 3），CAPO-MajorVote 在数学任务平均分上达到 0.287，而基线 GRPO-Rule 仅为 0.258。在OOD任务上，CAPO-Intersection 达到了 0.412，也高于基线的 0.410。这表明CAPO不仅提升了核心推理能力，也增强了泛化性。 \u0026gt; \u0026gt; 投票数N的影响 (Table 4 \u0026amp; Figure 4)：实验表明，增加“裁判模型”的批判数量（N从2增加到8）能够持续提升模型性能，尤其是在更难的数学基准上。这验证了投票机制的有效性，更多的批判样本可以平滑掉噪声，提供更准确的监督信号。\n奖励权重C/P的影响 (Table 5)：这是一个关键的消融实验。结果显示，非对称配置 (C=2, P=1) 的性能最佳。而对称配置 (C=2, P=2) 或极端过程导向 (C=2, P=5) 的性能则显著下降。 \u0026gt; The asymmetric (C=2, P=1) setup clearly outperforms all others. … The poor performance of the symmetric(C=2, P=2) … confirms that a poorly calibrated reward signal … can misguides the model. \u0026gt; 这证明了CAPO设计的非对称奖励函数对于正确引导模型学习至关重要。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ CAPO虽然提出了一个优雅的解决方案，但也为未来的研究揭示了更广阔的空间和挑战。\n值得探索的问题和挑战： “裁判”能力的瓶颈与迭代：CAPO的性能上限受限于“裁判模型”的能力。当“学生模型”的能力逐渐逼近甚至超越“裁判模型”时，这套体系就会失效。未来的研究需要探索模型的自我进化和迭代提升机制，例如，让训练好的“学生”成为下一代“裁判”，形成一个持续的、自洽的“AI训练AI”闭环。 从“数学”到“人文”的泛化：当前实验主要集中在逻辑清晰、答案唯一的数学和科学推理上。对于没有唯一正确答案的开放性领域，如创意写作、法律辩护、商业策略、伦理判断等，如何定义“错误的步骤”？如何设计“裁判模型”的prompt？这是一个巨大的挑战。 奖励函数的自动化设计：CAPO中的权重（C和P）是手动设定的。未来可以探索使用元学习（Meta-Learning）或更复杂的RL算法来自动学习和动态调整这些超参数，使其能适应不同任务、不同模型和不同训练阶段。 对抗性攻击与“钻空子”：模型可能会学到如何“欺骗”裁判模型，生成一些看起来逻辑正确但实际上有问题的步骤。研究如何增强裁判模型的对抗鲁棒性，或者引入多个、多样化的裁判模型进行交叉验证，将是一个重要方向。 可能催生的新技术和投资机会： 新技术： AI自主对齐与进化系统：能够自我监督、自我批判、自我迭代，不断提升自身能力和可靠性的AI系统。 专用的“AI裁判”或“AI验证”模型：专门为评估、验证和批判其他AI模型而优化的模型，可能成为AI基础设施的一部分。 可解释与可信AI的训练框架：将CAPO的思想扩展，形成一套完整的、能够产出逻辑链条清晰、过程可追溯的AI模型的训练平台。 投资机会： AI可靠性与安全公司：专注于提供AI模型验证、评估和加固服务的初创公司。 自动化AI训练平台：提供“AI教练”或“AI陪练”服务的平台，能够以低成本、高效率的方式提升特定领域AI模型的性能。 垂直领域的高可靠性AI应用：在金融、法律、医疗等对过程正确性要求极高的行业，基于类似CAPO技术训练的AI将有巨大的商业价值。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 从批判的视角看，尽管论文非常出色，但仍存在一些潜在的局限性和值得商榷之处：\n对“裁判模型”的依赖过强：这是其最大的潜在弱点。整个方法的有效性完全建立在“裁判模型”（如Qwen2.5-72B）足够强大的假设之上。论文没有深入探讨如果裁判模型出错（给出错误的批判），会对训练过程产生怎样的负面影响，以及如何缓解这种影响。 实验领域的局限性：如前所述，实验主要集中在数理逻辑和选择题形式的推理。该方法在需要创造力、主观判断或处理模糊信息的任务（如诗歌创作、代码审查中的风格问题）上的有效性是未经证实的。 Prompt的敏感性问题：裁判模型的表现高度依赖于给它的指令（Prompt）。论文虽然给出了使用的Prompt范例，但并未进行Prompt敏感性分析。不同的Prompt表述方式可能会极大地影响批判的质量和风格，这是一个潜在的不稳定因素。 对“Ground Truth”数据的反直觉发现：论文在附录中提到，将“标准答案”提供给裁判模型，有时反而会导致性能轻微下降（Table 9）。作者将其归因于模型生成分布与标准答案分布的差异。这个发现非常反直觉，值得更深入的分析和探讨，目前的解释略显初步。 计算成本的讨论不足：虽然CAPO的在线推理效率很高（单次调用），但为了鲁棒性而引入的投票机制（N=4或N=8）意味着在训练的每个样本上，都需要让一个非常大的“裁判模型”进行多次推理。这在实践中的总体训练成本（尤其是对资源有限的研究者）可能依然很高，论文对此讨论不足。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的研究者或工程师，这篇论文提供了极其宝贵的启发：\n重点学习的核心思想： “LLM即服务”的系统性思维：不要把LLM仅仅看作一个端到端的黑箱。CAPO的精髓在于将LLM作为系统的一个可编程组件来使用。你可以用一个LLM去批判、评估、标注、路由另一个LLM的输出。这是构建更复杂、更智能AI系统的关键范式转变。 过程监督的可扩展实现：业界普遍认识到过程监督的重要性，但苦于其高昂的成本。CAPO提供了一个可行的、低成本的蓝图，展示了如何将昂贵的人力监督，转化为可扩展的机器自动监督。这是一个可以被广泛借鉴的模式。 分层与解耦的奖励设计：CAPO的非对称奖励函数是一个非常实用的工程智慧。在处理多目标优化问题时（例如，既要结果好，又要过程好；既要内容准，又要风格对），设计一个有主次、有优先级的奖励体系，往往比试图用一个单一指标来平衡所有目标要有效得多。 给你的启发： 在你的项目中，如果遇到需要对LLM输出进行精细化评估的场景，可以立即尝试实现一个类似“LLM-as-GenPRM”的模块。用GPT-4o或Claude 3 Opus等顶级模型作为“裁判”，去评估你的微调模型在特定任务上的表现，甚至可以用它的输出来构造训练数据。 如果你正在用RL训练模型，并且奖励信号很稀疏（sparse reward），可以借鉴CAPO的思路，设计一个基于LLM判断的辅助奖励（auxiliary reward），来为模型提供更密集的引导信号。 在设计复杂的AI Agent时，可以引入一个“批判者”Agent，其职责就是像CAPO中的裁判一样，监督和评估“执行者”Agent的行为过程，并提供反馈用于其迭代优化。 需要补充的背景知识： 强化学习基础：特别是Policy Gradient方法和PPO (Proximal Policy Optimization)算法，因为CAPO的目标函数格式与PPO非常相似。 RLHF (Reinforcement Learning from Human Feedback)：了解其基本流程（SFT -\u0026gt; Reward Modeling -\u0026gt; RL），这是当前大模型对齐的主流范式，CAPO是对此范式中Reward环节的创新。 过程监督奖励模型 (PRMs)：例如OpenAI的Let’s Verify Step by Step，这是CAPO直接对标和试图改进的技术方向，理解PRM的优缺点有助于更好地 appreciating CAPO的贡献。 ","date":"2025-08-07T00:00:00Z","permalink":"/capotowards-enhancing-llm-reasoning-through-verifiable-generative-credit-assignment/","title":"CAPO：Towards Enhancing LLM Reasoning through Verifiable Generative Credit Assignment"},{"content":"研究agent不同组件的经济可行性。\n论文深度解读 这篇论文的核心贡献并非提出一个全新的、革命性的智能体（Agent）架构，而是首次对现代大型语言模型（LLM）驱动的智能体系统进行了系统性的、以成本效益为核心的实证研究。在当前业界普遍追求智能体“能力更强、解决问题更复杂”的大趋势下，这篇论文反其道而行之，聚焦于一个极为现实但常被忽视的问题：智能体的经济可行性。它将研究的重心从单纯追求性能（Effectiveness）的提升，转向了性能与效率（Efficiency）之间的权衡与优化。\n论文开篇就指出了一个尖锐的行业痛点：\nThe remarkable capabilities of Large Language Model (LLM)-driven agents have enabled sophisticated systems to tackle complex, multi-step tasks, but their escalating costs threaten scalability and accessibility. 译：LLM驱动的智能体其非凡的能力使得复杂多步任务的解决成为可能，但其不断攀升的成本威胁到了系统的可扩展性和可及性。\n这正是当前许多AI应用开发者和企业面临的困境。一个智能体即使能力再强，如果每次执行任务都需要消耗数美元的API调用费用，那么它就很难被大规模部署，最终只能停留在“技术演示”阶段。\n为了量化并优化这种“效费比”，论文引入并采用了“cost-of-pass”这一关键评价指标。这个概念非常重要，我们可以通俗地理解为“成功解决一次任务的期望成本”。其计算公式为：v(m, p) = Cm(p) / Rm(p)，即单次尝试的成本除以成功率。这个指标的精妙之处在于，它同时惩罚了高昂的成本和低下的成功率。一个模型如果成功率很低，需要反复尝试，那么即使单次成本低，其“cost-of-pass”也会很高。反之，一个模型虽然成功率高，但单次成本过于昂贵，其“cost-of-pass”同样会很差。因此，追求更低的“cost-of-pass”是这篇论文所有实验和优化的最终目标。\n论文的整个研究过程就像是一次严谨的“智能体性能调优实验”。作者将一个智能体系统解构成五个关键的可变组件，并在极具挑战性的GAIA（一个通用AI助手基准测试）上，对每个组件的不同配置进行了独立的、控制变量的实验。这五个组件分别是：\nBackbone（骨干模型）: 智能体的大脑，即底层的LLM。 Planning（规划）: 智能体如何分解任务和制定执行计划。 Tool Using（工具使用）: 智能体如何利用外部工具（如网络浏览器）获取信息。 Memory（记忆）: 智能体如何记录和利用历史信息。 Test-time Scaling（测试时扩展）: 如何通过多次采样等方式提升单次任务的成功率。 通过对这些组件的详尽分析，论文得出了一系列极具实践价值的结论。例如，在骨干模型的选择上，论文通过Table 1的数据指出，像Claude 3.7 Sonnet虽然在GAIA上取得了最高的准确率（61.82%），但其“cost-of-pass”高达3.54，而GPT-4.1在准确率稍低（53.33%）的情况下，“cost-of-pass”仅为0.98，显示出更优的效费比。这揭示了顶尖模型的高昂成本往往不成比例。\nimage.png 在记忆模块的设计上，论文得到了一个非常“反直觉”但极其重要的发现（见Table 5）：最简单的记忆模式（Simple Memory），即只保留历史观察和动作记录，其效果（56.36%准确率）和成本效益（0.74 cost-of-pass）居然超过了所有更复杂的记忆机制（如摘要记忆、混合记忆等）。这强有力地证明了在智能体设计中，复杂性并不总是带来正面收益，过度设计（Over-engineering）反而会增加成本、损害性能。\n最终，论文基于这些实验洞察，像组装一台“性价比最高的电脑”一样，将各个组件的最优配置组合起来，构建了EFFICIENT AGENTS框架。如Table 7所示，该框架在GAIA上的性能达到了业界领先的开源智能体OWL的96.7%（51.52% vs 53.33%），但其“cost-of-pass”从0.75降至0.55，相对提升了28.4%。这是一个非常了不起的成就，它清晰地展示了一条在不严重牺牲性能的前提下，大幅降低智能体运营成本的可行路径。\n总而言之，这篇论文的最大价值在于其提供的“方法论”和“设计哲学”：它倡导一种数据驱动、成本感知的智能体设计范式，并用详实的实验证据告诉我们，在智能体的世界里，“够用就好”和“大道至简”往往是比“盲目堆料”更明智的策略。\n接下来，我将按照您提出的六个问题，逐一进行详细解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是系统性地分析和优化LLM驱动的智能体系统中的“效率-效果”权衡（Efficiency-Effectiveness Trade-off）。它试图通过实证研究，为构建成本可控且性能强大的智能体提供一套清晰的设计原则和最佳实践。\n解决的实际问题：它要解决的是当前智能体技术落地面临的最大障碍之一：高昂的运营成本。随着智能体能力的增强，其背后LLM的调用次数和token消耗量呈爆炸式增长。论文中提到： \u0026gt; …cutting-edge agent products (e.g., DeepResearch, Manus) demonstrate impressive capabilities but suffer from exorbitant operating costs due to explosive LLM call overhead. \u0026gt; 译：…前沿的智能体产品（如DeepResearch, Manus）展现了惊人的能力，但因爆炸性的LLM调用开销而承受着过高的运营成本。 这使得许多有潜力的智能体应用在商业上变得不可行，阻碍了技术的普及和规模化。\n行业意义：这个问题对于行业的意义极其重大。\n推动技术普及：降低成本是AI技术从实验室走向市场的关键。这项研究为开发者提供了“降本增效”的具体方法，能让更多中小型企业和个人开发者有能力部署和使用强大的智能体，从而催生更丰富的应用生态。 促进产业成熟：一个行业成熟的标志之一就是其成本结构变得可控和可预测。这项研究将“成本效益”作为核心指标，推动行业从单纯的技术狂热转向理性的商业落地，有助于AI Agent产业的健康和可持续发展。 提供优化范式：它提供了一套行之有效的分析框架和度量标准（cost-of-pass），使“智能体优化”从一门玄学变成了一门有据可循的工程科学。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新不在于一个全新的模型，而在于一套新的研究思路和设计方法。\n新的思路与方法： 成本效益驱动的设计哲学：与以往大多数研究优先考虑最大化任务成功率不同，本文将cost-of-pass作为核心优化目标。这是一种视角的根本转变，从“不计成本地把事做成”变为“用最经济的方式把事做成”。 模块化解构与独立变量实验：论文没有将智能体视为一个黑箱，而是将其拆分为五个正交（Orthogonal）的组件。这种解构使得研究人员可以像做科学实验一样，固定其他变量，只调整单一组件的配置，从而精确地量化该组件对总体性能和成本的影响。这是一个非常严谨且可复现的研究范式。 “恰到好处的复杂性”原则（The Principle of “Sufficient Complexity”）：论文通过实验反复证明，更复杂的组件并不总是更好。这一点在Memory模块的实验中体现得淋漓尽致。 \u0026gt; Among the six memory configurations, Simple Memory, which retains only the agent’s observations and actions, minimizes the context window size, resulting in the lowest computational cost. Surprisingly, this configuration also yields the best performance… \u0026gt; 译：在六种记忆配置中，仅保留智能体观察和动作的简单记忆，最小化了上下文窗口，从而带来了最低的计算成本。令人惊讶的是，这种配置也取得了最好的性能… 如Table 5所示，Simple Memory的cost-of-pass为0.74，显著低于Summarized Memory的1.52和Extra Hybrid Memory的1.29。这直接挑战了业界普遍认为需要复杂记忆系统来处理长期任务的传统观念。 与之前方法的对比与优势： 对比追求SOTA（State-of-the-Art）的方法：之前许多工作（如OpenAI的Deep Research Agent或开源的OWL）主要目标是在GAIA等基准上刷榜，追求更高的准确率。本文则展示了这种追求的代价，并提供了一个性能略有妥协但成本大幅降低的替代方案。 对比提出单一新颖组件的方法：许多论文只关注改进智能体的某一个部分，比如一个新的规划算法或记忆机制。本文的优势在于其全局视角和系统性，它评估了各个组件的相互作用，并给出了一个整体最优的配置方案（EFFICIENT AGENTS），这个方案的配置（见Table 6）正是基于前述所有实验的结论： Backbone: GPT-4.1 (效费比均衡) Max Step: 8 (超过则收益递减) Search Source: Multi (来源越多越好) BoN: 1 (即不使用，成本太高) Memory: Simple (大道至简) 这种系统性的方法论，比单点创新更能为工程实践提供全面的指导。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文通过一系列精心设计的控制实验来验证其方法的有效性。\n实验设计： 基准（Benchmark）：选用公认的高难度通用智能体基准 GAIA。这个基准需要智能体具备复杂的推理、规划和工具使用能力，非常适合测试智能体的综合性能。 控制变量法：这是其实验设计的核心。如Table 8所示，他们首先定义了一套Default Setup（默认配置）。然后，在评估某个组件（如Planning）时，他们会保持所有其他组件为默认设置，只改变该组件的参数（如Max Steps从4变到8再到12），从而隔离出该变化带来的影响。 核心指标：所有实验都围绕三个维度进行评估： Effectiveness (效果): 使用Accuracy/% (pass@1)来衡量。 Cost (成本): 使用Cost/$和#Tokens来衡量。 Efficiency (效率): 使用核心指标cost-of-pass来综合评估。 关键实验数据和结果： 规划（Planning）实验 (Table 3)：实验证明了“过度思考”的代价。\n当Max Steps从4增加到8时，准确率从41.82%显著提升到52.73%，但cost-of-pass也从0.48上升到0.70。 当Max Steps从8增加到12时，准确率几乎没有提升（52.73% -\u0026gt; 53.33%），但cost-of-pass却急剧恶化到0.98。这表明8步是一个甜点（sweet spot），更多的规划步数只是在空耗成本。 工具使用（Tool Using）实验 (Table 4)：实验揭示了工具配置的诀窍。\n增加搜索源：将搜索源从Simple（Google, Wikipedia）扩展到Multi（增加Bing, Baidu, DuckDuckGo），准确率从53.33%提升至59.39%，同时cost-of-pass从1.32优化到0.81。结论：搜索源多多益善。 简化浏览器：使用简单的Crawler（爬虫，只获取静态文本）比使用复杂的浏览器（支持交互）在成本和性能上都更有优势，表明对于GAIA这类任务，复杂的页面交互是不必要的。 最终验证 (Table 7)：将所有优化后的组件集成为 EFFICIENT AGENTS，并与两个主流开源框架进行对比。\nAgent cost-of-pass↓ Accuracy/%↑ Cost/$↓ OWL (SOTA) 0.75 53.33 0.398 SmolAgent 5.82 53.33 3.104 Efficient Agent (ours) 0.55 51.52 0.285 表格2：不同智能体最终性能对比（源自Table 7）\n这个结果极具说服力：\nEFFICIENT AGENTS的 cost-of-pass (0.55) 显著低于OWL (0.75)，实现了28.4%的效率提升。 它的单次运行成本Cost/$ (0.285) 远低于OWL (0.398)。 而所有这些成本上的巨大优势，仅仅付出了微小的性能代价（准确率从53.33%下降到51.52%）。 这些数据清晰地证明了论文所提出的设计原则是有效的，并且能够带来显著的经济效益。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文为“效率导向的智能体研究”开了一个好头，未来还有广阔的探索空间。\n值得探索的问题和挑战： 动态自适应配置 (Dynamic Adaptive Configuration)：本文提出的EFFICIENT AGENTS是一个静态配置。更理想的智能体应该能在任务执行中动态调整策略。例如，它可以在开始时使用廉价的小模型进行初步探索，当判断任务复杂性很高时，再无缝切换到昂贵的大模型进行关键推理。这需要研究任务难度预估和策略动态切换机制。 面向智能体的模型蒸馏 (Agent-oriented Model Distillation)：是否可以将在昂贵模型（如GPT-4.1）上运行智能体的推理链（Chain-of-Thought）和决策过程，蒸馏到一个更小、更便宜的模型中？这不仅仅是蒸馏知识，更是蒸馏“智能体行为”。 长程任务的效率瓶颈 (Long-Horizon Task Bottlenecks)：本文的实验最多只到12步。对于需要数百甚至数千步的超长程任务，其“Simple Memory”是否依然有效？届时，记忆的压缩、遗忘和检索机制可能会重新成为效率的关键瓶颈。 多智能体系统的成本控制 (Cost Control in Multi-Agent Systems)：本文聚焦于单智能体。在多智能体系统中，除了LLM调用成本，还存在智能体之间的通信成本。如何设计高效的通信协议，减少冗余交流，避免“开会空转”，将是多智能体效率研究的核心。 新的技术和投资机会： 智能体即服务（Agent-as-a-Service）平台：提供预置了各种“成本效益优化档案”（Cost-Optimized Profiles）的智能体开发平台。开发者可以根据自己的预算和性能需求（例如，“最高性价比型”、“极限性能型”、“最低成本型”），一键选择并部署智能体。 专为智能体设计的LLM (Agent-Specific LLMs)：当前的LLM大多是为对话而优化的。未来可能会出现专门为智能体设计的模型，它们可能在工具调用、规划、推理等“智能体原生”任务上具有极高的token效率。 智能体可观测性与分析工具 (Agent Observability \u0026amp; Analytics Tools)：类似于软件性能监控（APM）工具，未来会出现专门用于监控、分析和优化智能体性能与成本的工具。它们可以帮助开发者追踪cost-of-pass，识别成本瓶颈，并提供优化建议，就像这篇论文做的一样。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性的学术角度看，仍存在一些局限性。\n泛化性存疑 (Questionable Generalizability)：所有实验都基于GAIA这一个基准。GAIA主要测试的是基于网络的问答和推理，对于其他类型的智能体任务，如软件开发、代码生成、科学发现等，这些结论是否依然适用？例如，在软件开发任务中，可能需要更长的规划步数和更复杂的代码库记忆，Simple Memory可能就不再是最佳选择。 模型选择的局限性 (Limited Scope of Models)：论文测试的骨干模型虽然具有代表性，但并不全面。特别是，它没有包含经过特定任务微调（Fine-tuning）的开源模型。一个经过精心微调的7B或13B模型，可能在特定任务上以极低的成本超越GPT-4.1。 静态成本模型 (Static Cost Model)：论文的成本计算基于2025年5月的API价格。这个价格是会变动的。更重要的是，它只考虑了API调用成本，忽略了自托管模型的计算成本、向量数据库的运维成本、工具API的潜在费用等。真实的总拥有成本（TCO）更为复杂。 对“测试时扩展”的探讨不足 (Insufficient Exploration of Test-Time Scaling)：论文对Best-of-N的评估有些简单。虽然在默认配置下它显得性价比不高，但如果与更便宜的模型（如Qwen）结合，或者使用更智能的评估函数（而非GPT-4o打分），Best-of-N策略或许能找到它的“甜点区”。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是指导AI应用实践的宝库，您可以从中获得许多即插即用的思想。\n重点学习与启发： 拥抱“Cost-of-Pass”思维模型：在设计任何AI功能时，都不要只盯着效果。从立项开始，就要将“成功一次的期望成本”作为核心考量。这个思维模型可以帮助您在功能设计和技术选型上做出更明智的商业决策。 从“简单”开始，警惕过度工程化：论文关于Simple Memory的发现是一个极佳的警示。在你的项目中，优先实现最简单的版本，然后通过A/B测试等数据驱动的方式，验证增加的复杂性是否真的带来了超过其成本的收益。 骨干模型是第一杠杆：论文清晰地表明，骨干模型的选择是影响成本和性能的最大单一因素。在项目初期，花时间对几个候选模型（包括开源和闭源）进行小范围测试，评估它们的cost-of-pass，这项投入的回报会非常高。 将系统分解，逐个优化：借鉴论文的方法，将您的智能体系统也分解为几个关键模块（如信息获取、任务规划、历史记忆、结果生成等），然后有针对性地进行优化。 需要补充的背景知识： 智能体主流架构 (Agent Architectures)：您需要了解一些经典的智能体工作流，特别是 ReAct (Reason and Act) 框架，因为它是许多现代智能体的基础。了解智能体是如何通过“思考-行动”循环与环境互动的。 智能体评测基准 (Agent Benchmarks)：除了GAIA，可以去了解一下其他的评测基准，如AgentBench、WebArena、ToolBench等。这能帮助您理解智能体能力的多个维度，并为您的应用选择合适的测试场景。 大语言模型经济学 (LLM Economics)：深入了解LLM的成本构成，包括不同模型的API定价策略（输入/输出token价格差异）、自托管模型的推理成本（硬件、吞吐量、延迟）等。这将使您在实践“cost-of-pass”分析时更加得心应手。 希望这份详尽的解读能帮助您深入理解这篇论文的精髓，并为您的工作带来启发。\n","date":"2025-08-07T00:00:00Z","permalink":"/efficient-agentsbuilding-effective-agents-while-reducing-cost/","title":"Efficient Agents：Building Effective Agents While Reducing Cost"},{"content":"信用分配问题，而且贡献了verl-agent库，可以方便针对真实环境进行训练。思想也类似于GTPO：Trajectory-Based Policy Optimization in Large Language Models，不同的轨迹有相同的前缀，不过用在了完全不同的领域上。\nimage.png image.png 这篇论文（arXiv:2505.10978v1）聚焦于一个在大型语言模型（LLM）智能体（Agent）训练领域中极其关键且充满挑战的问题：长时程任务中的精细化信用分配（Fine-grained Credit Assignment）。随着LLM从简单的问答系统演变为能够在复杂环境中执行多步任务的智能体，如何有效地从稀疏、延迟的奖励信号中学习，成为了制约其能力上限的核心瓶颈。\n传统的强化学习（RL）方法，尤其是近年来在LLM领域大放异彩的基于组的强化学习（Group-based RL），例如 GRPO (Group-based Reward Policy Optimization)，虽然在单轮次任务（如数学推理）中表现出色，因其无须评论家网络（critic network）、内存开销低、训练稳定而备受青睐。但当它们被直接应用于长时程任务时，其核心机制便显现出局限性。这些方法通常将一整条完整的交互轨迹（trajectory）视为一个不可分割的单元，给予一个总的奖励或惩罚。这就像是评价一场足球比赛，只看最终比分，却无法知道是哪个球员的哪个传球或射门决定了胜局。对于一个包含50个步骤的任务，如果最终失败了，仅仅告诉模型“这次表现很差”，模型很难知道到底是第3步的决策错误还是第45步的操作不当。\n正如论文核心问题所提出的： \u0026gt; Can we preserve the critic-free, low-memory, and stable convergence properties of group-based RL while introducing fine-grained credit assignment for long-horizon LLM agents?\n（我们能否在保留基于组的RL算法的无评论家、低内存、收敛稳定等优点的同时，为长时程LLM智能体引入精细化的信用分配机制？）\n为了解决这一难题，作者们提出了组中组策略优化（Group-in-Group Policy Optimization, GiGPO）算法。GiGPO的精髓在于其创新的双层级优势估计（two-level advantage estimation）结构，它巧妙地将宏观的全局反馈与微观的局部反馈结合起来，实现了在不增加额外计算开销的前提下的精细化信用分配。\n宏观层面，GiGPO沿用了GRPO的思路，在回合（episode）级别对一组完整的轨迹进行比较，评估每个轨迹的整体优劣。这提供了一个稳定、全局的训练信号，确保智能体学习到能完成整个任务的连贯策略。\n而GiGPO真正的创新在于微观层面——步骤（step）级别。它引入了一种名为“锚点状态分组（anchor state grouping）”的机制。其核心洞察是：在多次执行同一任务的过程中，智能体不可避免地会多次遇到完全相同的环境状态。例如，在网页购物任务中，可能会多次返回到同一个搜索结果页面；在虚拟家居任务中，可能多次进入同一个房间。GiGPO将这些被重复访问的状态视为“锚点”。它在训练数据生成后，追溯性地（retroactively）将所有从同一个“锚点状态”出发的不同动作及其后续的累积回报收集起来，形成一个“步骤级小组”。在这个小组内部，算法可以清晰地比较出，在“这个特定状态下”，哪个动作带来了更好的长期回报。这就像是复盘录像，专门分析在“对方禁区前沿”这个关键位置的所有传球选择，看哪个选择最终导致了进球。\n通过一个加权和，GiGPO将宏观的回合优势和微观的步骤优势结合起来，形成一个分层的、信息更丰富的梯度信号来指导模型更新。这一设计极其巧妙，因为它完全复用了已有的交互数据，不需要像传统方法那样为了评估每个步骤而进行大量额外的、计算成本高昂的模拟（rollouts）。\n实验结果有力地证明了GiGPO的有效性。在ALFWorld（一个需要50步决策的具身智能环境）和WebShop（一个复杂的多步网页购物环境）这两个极具挑战性的长时程基准上，GiGPO全面超越了所有基线方法。论文中的表1显示，与强大的基线GRPO相比，基于Qwen2.5-7B模型的GiGPO在ALFWorld上的成功率提升了12.6%，在WebShop上提升了9.1%。更令人印象深刻的是，如图6所示，这种显著的性能提升带来的额外计算时间成本几乎可以忽略不计（仅占总迭代时间的不到0.002%），并且保持了与GRPO完全相同的GPU内存开销。这使得GiGPO成为一个兼具高效与高性能的实用算法。\n总而言之，GiGPO通过其创新的“组中组”和“锚点状态分组”机制，优雅地解决了长时程LLM智能体训练中的精细化信用分配难题，在保持原有Group-based RL方法核心优势的同时，极大地提升了智能体在复杂任务中的学习效率和最终性能，为训练更强大、更自主的LLM智能体铺平了道路。\n一、研究目标与意义 研究目标： 论文的核心研究目标是解决长时程、稀疏奖励环境下LLM智能体的精细化信用分配问题。具体来说，作者希望在不牺牲现有Group-based RL算法（如GRPO）的无评论家、低内存、高效率、训练稳定等优良特性的前提下，让模型能够理解并学习到其多步决策序列中每一步动作的相对好坏。\n解决的实际问题： 在现实世界中，许多任务都具有长时程的特点，例如，一个机器人需要执行一系列操作来打扫房间，或者一个软件代理需要浏览多个页面来完成在线预订。在这些任务中，奖励信号往往是稀疏且延迟的——只有当整个任务完成时，系统才会给出“成功”或“失败”的反馈。这导致现有训练方法难以判断中间步骤的优劣，从而学习效率低下，难以掌握复杂策略。\n行业意义： 这个问题是推动LLM从“语言模型”进化为“通用智能体”的关键瓶颈。其重要意义体现在：\n解锁复杂应用场景：解决了这个问题，意味着我们可以更有效地训练LLM智能体来执行更复杂、更长期的任务，这对于机器人技术、自动化软件测试、数字个人助理、复杂游戏AI等领域的发展至关重要。 降低训练成本和门槛：通过在不增加额外计算开销的情况下提升学习效率，GiGPO这类方法可以显著缩短训练时间，降低对计算资源的需求，使得训练高性能的LLM智能体变得更加可行和经济。 提升智能体的鲁棒性和泛化能力：精细化的信用分配能让智能体学习到更优的“子策略”，而不仅仅是记住几条能通关的“死板路径”。这有助于智能体在面对新情况时做出更合理、更鲁棒的决策。 二、新思路与方法 论文提出的核心方法是GiGPO (Group-in-Group Policy Optimization)，其创新之处在于一个双层级的优势（advantage）计算框架。\n思路对比： 传统方法 (如PPO)：需要训练一个额外的评论家网络（critic network）来估计每个状态的价值，以此计算优势。这引入了额外的模型、内存开销和潜在的训练不稳定性。 Group-based RL (如GRPO)：放弃了评论家网络，通过比较一组（group）轨迹的总回报来计算优势。这很高效，但是是粗粒度的（coarse-grained），无法区分单步动作的好坏。 GiGPO：结合了两者的优点并规避了其缺点。它既是无评论家的，又是细粒度的。 GiGPO的核心机制： 回合级优势 (\\(A^E\\))： 与GRPO类似，首先采样N条完整的轨迹 \\(\\{\\tau_1, \\tau_2, ..., \\tau_N\\}\\)。 计算每条轨迹的总回报 \\(R(\\tau_i)\\)。 通过将单个轨迹回报与组内平均回报进行比较（并用标准差或固定值1进行归一化），计算出每条轨迹的回合级优势 \\(A^E(\\tau_i)\\)。这个值反映了“整个执行过程的全局好坏”。 步骤级优势 (\\(A^S\\)) - 核心创新： 锚点状态分组 (Anchor State Grouping)：这是GiGPO的灵魂。在N条轨迹生成后，算法会遍历所有步骤，识别出所有完全相同的环境状态，并将这些状态定义为锚点状态 \\(\\tilde{s}\\)。 构建步骤级小组：对于每一个锚点状态 \\(\\tilde{s}\\)，算法会把所有从这个状态 \\(\\tilde{s}\\) 执行的动作 \\(a_t^{(i)}\\) 以及该动作带来的未来折扣回报 \\(R_t^{(i)}\\)（即从当前步到结束的所有奖励的折扣累加和）收集起来，形成一个步骤级小组 \\(G^S(\\tilde{s})\\)。 \u0026gt; \\(R_t^{(i)} = \\sum_{k=t}^{T} \\gamma^{k-t} r_k^{(i)}\\) 计算步骤级优势：在这个步骤级小组内部，同样通过比较每个动作的未来回报与小组内的平均回报，计算出每个动作的步骤级优势 \\(A^S(a_t^{(i)})\\)。这个值反映了“在当前状态下，这个动作的局部好坏”。 融合优势信号： 最终用于策略更新的总优势是两者的加权和： \u0026gt; \\(A(a_t^{(i)}) = A^E(\\tau_i) + \\omega \\cdot A^S(a_t^{(i)})\\) 其中 \\(\\omega\\) 是一个超参数，用于平衡全局和局部信号的权重。这个分层的优势信号，既能保证策略的长期连贯性，又能对局部决策进行精细打磨。 特点与优势： 精细化：首次在无评论家的框架下实现了对每一步动作的信用分配。 高效率：通过追溯性的数据复用，无需任何额外的环境交互或模型前向传播，计算开销极低。 稳定性：保留了Group-based RL训练稳定的优点。 正交性与兼容性：GiGPO的设计是正交的，可以与其他Group-based RL的改进技术（如DAPO）结合，进一步提升性能，如论文附录中的GiGPO_dynamic实验所示。 三、实验验证 论文通过设计严谨的实验来验证GiGPO的有效性、效率和内部机制。\n实验设计： 环境：选择了两个公认的长时程、稀疏奖励基准： ALFWorld：一个基于文本的具身智能环境，任务平均需要50步完成。 WebShop：一个模拟的在线购物网站，需要智能体通过多步浏览、点击、搜索来找到并购买指定商品。 基线：对比了多种有代表性的方法： 闭源大模型：GPT-4o, Gemini-2.5-Pro (作为能力上限参考)。 提示工程方法：ReAct, Reflexion (不进行模型微调)。 传统RL方法：PPO (有评论家网络的代表)。 Group-based RL方法：RLOO, GRPO (直接对比的基线)。 模型：使用Qwen2.5-1.5B-Instruct和Qwen2.5-7B-Instruct作为基础模型。 实验数据和结果： 性能对比（表1）：这是最核心的证据。GiGPO在所有任务和模型尺寸上都取得了最优性能（SOTA）。 关键数据：在使用Qwen2.5-7B模型时，GiGPO (w/o std版本) 在ALFWorld上取得了90.2%的成功率，显著高于GRPO的77.6%和PPO的80.4%。在WebShop上，GiGPO取得了75.2%的成功率，同样高于GRPO的66.1%。 消融研究（图4）：为了证明双层级优势的必要性，作者进行了消融实验。 移除步骤级优势 (w/o A^S)：性能大幅下降，尤其是在需要精细操作的复杂任务上（如Cool, Pick2），证明了局部信号的关键性。 移除回合级优势 (w/o A^E)：性能同样大幅下降，说明缺乏全局信号会导致策略失去长期连贯性。 结论：全局和局部优势信号缺一不可，共同构成了GiGPO成功的基石。 计算开销分析（图6）：该实验量化了GiGPO额外部分的耗时。 关键数据：在一个总耗时362.83秒的训练迭代中，锚点状态分组耗时仅0.01秒，步骤级优势计算耗时0.53秒。总开销占比小于0.002%。这强有力地证明了GiGPO的高效率。 四、未来探索与机遇 这篇论文为LLM智能体训练开辟了新的道路，同时也揭示了未来的研究方向和潜在机会。\n值得探索的问题和挑战： 状态表示与近似匹配：论文最大的局限在于“锚点状态”依赖于精确的状态哈希匹配。在更复杂、高维或有噪声的现实环境中（如机器人视觉输入），完全相同的状态极少出现。未来的关键挑战是，如何从精确匹配走向基于状态嵌入（state embedding）的近似或语义匹配。可以利用VLM等模型将状态编码为向量，在向量空间中寻找近邻作为“锚点”，这将极大扩展GiGPO的适用范围。 动态权重调整：超参数 \\(\\omega\\) 在论文中被固定为1。一个更优的策略可能是动态调整它。例如，在训练初期，当策略还很混乱时，可以更侧重全局的回合信号；当策略基本成型后，再加大局部步骤信号的权重以进行精细打磨。 分层策略学习：GiGPO的分层优势结构天然地与分层强化学习（Hierarchical RL）思想契合。未来可以探索设计分层智能体，其中高层策略负责设定子目标（受回合优势指导），底层策略负责执行具体动作（受步骤优势指导）。 新的技术和投资机会： 智能体训练平台即服务（Agent Training Platform as a Service）：可以预见，集成GiGPO这类高效算法的云平台将极具商业价值。企业无需自建复杂的RL训练设施，即可在该平台上高效地训练用于特定业务（如智能客服、流程自动化）的专有智能体。 下一代机器人和自动化解决方案：更高效的训练算法是推动具身智能发展的核心引擎。在物流、制造、家庭服务等领域的机器人公司，以及软件自动化（RPA）公司，都可以利用此技术来开发更智能、适应性更强的产品。 大模型能力军火库：GiGPO这类算法可能成为继SFT（监督微调）、RLHF/DPO（偏好对齐）之后，微调和增强大模型能力的又一标准工具，特别是在为模型赋予复杂的“行动能力”方面。 五、批判视角与不足 尽管这篇论文非常出色，但从批判性的角度看，仍存在一些可以深入探讨的方面。\n主要不足与缺失： 状态匹配的脆弱性：如前所述，对精确状态匹配的依赖是其最大软肋。论文中选择的ALFWorld和WebShop环境的状态空间相对离散和结构化，这为GiGPO的成功创造了有利条件。它在状态空间巨大、连续且充满随机性的环境（如真实的物理世界）中的表现尚待验证。 \u0026gt; 论文在结论部分也坦诚了这一点： \u0026gt; \u0026gt; A potential limitation of GiGPO is its reliance on state matching for anchor group construction. In highly complex environments, identical states may be hard to detect due to noise or subtle differences.\n锚点状态的稀疏性问题：在极其复杂的环境中，可能大部分状态在有限的N条轨迹中都只出现一次，这将导致大多数步骤级小组 \\(G^S(\\tilde{s})\\) 的大小为1，无法形成有效的比较，使得步骤级优势 \\(A^S\\) 退化为0。在这种情况下，GiGPO会自然地退化为GRPO，虽然保证了性能下限，但也失去了其核心优势。\n超参数敏感性分析缺失：论文并未提供对关键超参数\\(\\omega\\)的敏感性分析。\\(\\omega\\)的取值很可能对最终性能有显著影响，了解其鲁棒性对于该方法的实际应用非常重要。\n需要进一步验证和存疑之处： 对任务类型的泛化性：论文验证了两种任务类型，但世界上的任务远不止于此。GiGPO在需要创造性、而非重复性路径探索的任务中表现如何？ 样本效率：尽管计算效率很高，但论文没有直接与需要更少环境交互样本的off-policy算法进行样本效率的比较。对于与真实环境交互成本极高的场景（如物理机器人），这一点至关重要。 六、核心启发与学习路径 对于希望从中汲取创新思想的从业者和研究者，这篇论文提供了宝贵的启发。\n重点学习与启发： 分层/多尺度反馈思想：这是最核心的启发。在设计任何学习系统时，都可以思考如何从不同时间尺度或粒度上提供反馈。一个复杂的任务可以分解为“全局目标”和“局部决策”，并为之设计相应的奖励或监督信号。 数据的高效复用：“锚点状态分组”是从已有数据中压榨更多信息的典范。在任何数据驱动的项目中，都应该思考“我的数据还能从哪些不同角度进行切分和分析，以产生新的洞见？”。这比盲目地采集更多数据要高效得多。 算法设计的“奥卡姆剃刀”原则：GiGPO在不引入复杂组件（如评论家网络）的情况下解决了核心问题，体现了算法设计的简洁之美。这提醒我们，在面对复杂问题时，应优先考虑对现有框架进行巧妙的“微创手术”，而非直接引入一个全新的、更重的框架。 需要补充的背景知识： 强化学习基础：需要扎实理解策略（Policy）、价值函数（Value Function）、奖励（Reward）、轨迹（Trajectory）、优势函数（Advantage Function）等基本概念。 策略梯度算法：特别是REINFORCE和PPO。理解PPO的原理，可以更深刻地体会到GiGPO“无评论家”设计的优势所在。 Group-based RL方法：强烈建议先阅读GRPO (arXiv:2402.03300) 的相关工作，因为GiGPO是其直接的演进和增强。理解GRPO的机制是理解GiGPO创新的前提。 LLM Agent技术：了解ReAct (arXiv:2210.03629) 等思想，以及当前LLM智能体在长时程规划方面所面临的普遍挑战。 ","date":"2025-08-07T00:00:00Z","permalink":"/group-in-group-policy-optimization-for-llm-agent-training/","title":"Group-in-Group Policy Optimization for LLM Agent Training"},{"content":"长期规划问题。并且通过观察来不断修订计划。类似PLAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks和Routine：A Structural Planning Framework for LLM Agent System in Enterprise\n“规划器-执行器”解耦思维：这是最核心的架构思想。在设计任何需要长期执行的AI系统时，都可以借鉴这种分工模式。将“思考长远战略”和“处理眼前事务”的能力分开，可以极大地提升系统的逻辑清晰度和鲁棒性。 闭环自适应（Closed-loop Adaptation）**：不要让你的计划一成不变。建立一个快速的反馈循环，让系统的每一次行动结果都能反过来指导和修正未来的方向。这是所有智能系统走向真正“智能”的必经之路。 image.png image.png 首先，我将为您呈现对这篇论文的整体性深度解读。\n这篇论文（Lu et al., 2024, “PilotRL: Training Language Model Agents via Global Planning-Guided Progressive Reinforcement Learning”, arXiv:2508.00344）直面了当前大型语言模型（LLM）在构建复杂智能体（Agent）时的一个核心瓶颈：缺乏远见和有效的长期规划能力。目前主流的智能体范式，如ReAct，虽然巧妙地将“思考”与“行动”结合，但其本质上是一种“走一步看一步”的短视策略。在需要多步骤、长远规划的复杂任务中，这种策略很容易“迷路”，导致行为冗余或任务失败。此外，研究界普遍采用的监督微调（SFT）方法，更倾向于让模型“背诵”固定的成功路径，而非真正学会“举一反三”，导致其在面对新问题时泛化能力严重不足。\n为解决这些痛点，论文提出了两个环环相扣的核心贡献。第一个是全新的智能体范式——AdaPlan（自适应全局规划）。它将智能体内部的认知过程解耦为两个协同工作的角色：全局规划器（Global Planner）和执行器（Executor）。规划器负责制定一个高层次、全局性的任务蓝图，而执行器则专注于执行当前步骤。最关键的是，这个计划并非一成不变，而是“自适应”的。每次执行器与环境交互后，新的观察结果会同时反馈给执行器和规划器，后者会根据最新信息动态地调整和优化后续的计划。这种“边走边看，不断校准大方向”的机制，极大地提升了智能体在长时程任务中的鲁棒性和效率。正如论文图2所示，这种动态规划与反馈的循环，是AdaPlan范式的精髓。\n第二个核心贡献是为训练AdaPlan智能体量身打造的训练框架——PilotRL（引导式渐进强化学习）。作者们深刻地认识到，同时优化规划和执行两个能力是极其困难的，很容易导致训练过程不稳定。因此，他们创造性地设计了一个三阶段的“渐进式”强化学习流程： 1. 阶段一：执行器能力增强 (Executor Enhancement)。此阶段的目标很纯粹：教会“执行器”无条件地遵循高质量的全局计划。奖励函数的设计重点是“指令依从度”，确保执行器能成为一个“听话的好兵”。 2. 阶段二：全局规划器培养 (Global Planner Cultivation)。在一个“好兵”的基础上，此阶段开始培养“规划器”成为一个“优秀的指挥官”。训练重点转向提升其生成计划的质量，奖励函数中加入了对计划正确性、可执行性等的评估。 3. 阶段三：端到端联合优化 (Joint Optimization)。最后，对规划器和执行器进行联合微调，目的是促进二者之间的协同配合，最大化最终的端到端任务成功率。\n这种分阶段、由简到难的训练策略，有效规避了多目标优化中的冲突，使得模型能够稳定地习得复杂的规划与执行能力。实验结果极为亮眼，基于LLaMA3.1-8B模型训练的PilotRL智能体，在多项基准测试中的综合表现（平均分70.43%）甚至超越了强大的闭源模型GPT-4o（67.98%），并且相较于同等参数规模下的GPT-4o-mini，实现了高达55.78%的性能增益。这一结果充分证明了该框架的有效性和巨大潜力，为开源模型追赶甚至超越闭源模型的智能体能力，开辟了一条新的、可行的技术路径。\n接下来，我将按照您提出的六个问题，逐一进行更详细的剖析。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升大型语言模型（LLM）在复杂、长时程任务中的自主规划和执行能力，使其能够像人一样，基于一个长远目标进行战略规划，并根据实时反馈灵活调整行动。\n解决的实际问题：\nReAct范式的短视性 (Myopia of ReAct)：ReAct等主流方法将推理限制在单个步骤，缺乏对任务全局的理解。如论文引言中提到的 (C1) Limited Contextual Awareness of ReAct，这种方法在需要前后连贯、多步协作的复杂场景（如在虚拟家庭环境中找东西并放置到指定地点）中效率低下。 规划与执行的脱节 (Decoupling of Planning and Execution)：即便一些方法引入了规划器，但规划器和执行器往往是独立设计的，导致“计划赶不上变化”，执行器无法有效跟随计划。这即是论文中指出的 (C2) Insufficient Coordination between Planning and Executing。 监督微调的泛化能力缺陷 (Poor Generalization of SFT)：通过SFT训练的智能体倾向于记住特定任务的“解题套路”，而不是学习通用的问题解决能力，一旦遇到新情况就无所适从。这对应了 (C3) Deficient Generalization of SFT。 行业意义：这个问题是决定LLM能否从一个“聊天机器人”进化为真正的“自主智能体”的关键。当前，行业正努力将AI应用于更广泛的领域，如自动化软件操作、机器人控制、科学研究等。这些应用无一例外都需要AI具备强大的长时程任务处理能力。解决这个问题，意味着：\n赋能更复杂的自动化：AI将能胜任需要数十甚至数百步操作的复杂工作流，例如自动化财务分析、代码库重构或药物研发流程。 提升AI的可靠性：一个有远见的AI，在遇到意外情况时，不会轻易放弃或陷入死循环，而是能主动调整策略，从而在关键应用中（如智能制造、自动驾驶辅助）更加可靠。 降低开发门槛：通过PilotRL这样的框架，社区可以用中等规模的开源模型，训练出媲美甚至超越顶级闭源模型的智能体，这将极大地推动AI应用的普及和创新。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于AdaPlan范式和PilotRL训练框架。\nAdaPlan：自适应全局规划范式 新思路：它将智能体的“大脑”显式地划分为“战略规划”和“战术执行”两个部分，但又将它们统一在一个模型内部。最核心的创新是计划的动态自适应性。 实现细节： \u0026gt; At each time step \\(t\\) (\\(t \\ge 1\\)), the executor takes an action \\(a^{(t)}\\)… it receives the resulting observation \\(o^{(t)}\\)… The global plan \\(P^{(t-1)}\\) is then iteratively refined according to the task goal \\(G\\) and the accumulated context \\(C^{(t)}\\) to facilitate the next execution, resulting in \\(P^{(t)}\\). 特点与优势： 相比ReAct：AdaPlan克服了ReAct的“短视”。ReAct的思考是“下一步我该干嘛？”，而AdaPlan的规划器思考的是“为了最终目标，未来N步的路径是什么样的？”，并在每一步后审视和修正这个路径。 相比独立的规划器：许多工作尝试外挂一个规划模块，但这常常导致规划与执行能力不匹配。AdaPlan将二者置于一个统一模型（Unified Model）内共同训练，确保了规划器制定的计划是执行器能够理解和完成的，实现了紧密耦合。实验中的表5明确指出了这一点，统一架构（Unified）比隔离架构（Isolated）在LLaMA3.1-8B上平均性能高出7.51%。 PilotRL：渐进式强化学习训练框架 新思路：采用“分而治之”和“循序渐进”的策略来训练复杂的双角色智能体。它不试图一步到位，而是像人类学习技能一样，先打基础，再谋发展，最后求协同。 特点与优势： 相比单一RL训练（Vanilla RL）：传统的RL方法直接优化最终任务，在复杂的智能体任务中，奖励信号稀疏且充满噪声。PilotRL通过三个阶段的精心设计，为模型提供了更明确、更平滑的学习路径。表2显示，PilotRL（LLaMA3.1-8B）的平均分（70.43%）显著高于Vanilla RL（63.33%），这之间的差距证明了AdaPlan范式+渐进式训练的优越性。 相比SFT：SFT是“授人以鱼”，让模型死记硬背。PilotRL基于强化学习（具体为GRPO算法），是“授人以渔”，通过与环境的交互和试错来学习策略，泛化能力更强。这在处理域外（OOD）任务时尤为明显。 以下是AdaPlan和PilotRL的整体框架图，可以帮助您更好地理解其工作流程。\nAdaPlan 范式 (运行时) PilotRL 训练框架 (训练时) 输入: 任务目标 (Task) 阶段 1: 增强执行器 - 学习遵循计划 1. 全局规划器: 生成初始计划 \\(P^{(0)}\\) - 目标: 提高指令依从度 2. 执行器: 执行计划的第 \\(t\\) 步 \\(p_i^{(t-1)}\\)，生成动作 \\(a^{(t)}\\) - 奖励: 格式、依从度、端到端性能 3. 环境: 返回观察 \\(o^{(t)}\\) 阶段 2: 培养规划器 - 学习生成好计划 4. 规划器: 根据新的上下文 \\(C^{(t)}\\) 更新计划为 \\(P^{(t)}\\) - 目标: 提高计划质量 5. 循环 至任务完成 - 奖励: 格式、端到端、全局计划质量 阶段 3: 联合优化 - 促进规划与执行协同 - 目标: 提升端到端整体性能 - 奖励: 格式、端到端性能 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了一套非常全面且严谨的实验来验证其方法的有效性。\n实验设计： 数据集：实验覆盖了6个不同的基准数据集，并巧妙地将其划分为域内（In-Domain, ID）和域外（Out-of-Domain, OOD）两类。 ID (用于训练和测试)：ALFWorld (家庭环境), IQA (交互式问答), TextCraft (文本游戏), Wordle (猜词游戏)。 OOD (仅用于测试)：BabyAI (导航与交互), MAZE (迷宫)。这样的设计能非常有效地评估模型的泛化能力。 基线对比 (Baselines)：对比对象非常全面，覆盖了当前所有主流方法： 闭源模型：GPT-4o, GPT-4o-mini，作为性能天花板的参考。 开源专用智能体模型：Agent-FLAN-7B等，检验与专门为智能体任务设计的模型对比效果。 不同训练/推理范式：在相同的开源模型（如Qwen2.5, LLaMA3.1）上应用Naive Response（无处理）、ReAct、MPO（外部规划器）、SFT和Vanilla RL，这可以公平地剥离出PilotRL框架自身的优势。 消融研究 (Ablation Studies)：为了证明PilotRL中每个设计环节的必要性，论文进行了详尽的消融实验（见表3），分别验证了渐进式训练的必要性、每个训练阶段的角色以及阶段顺序的影响。 关键实验数据与结果： 核心性能对比 (表2)：这是最有说服力的结果。以LLaMA3.1-8B-Instruct为基础模型： PilotRL (ours): 平均分 70.43% GPT-4o: 平均分 67.98% SFT: 平均分 63.84% ReAct: 平均分 41.68% 这些数据清晰地表明，PilotRL不仅远超其他开源方案，甚至超越了当时的顶尖闭源模型。 渐进式训练的必要性 (表3)： 标准三阶段训练 (1→2→3): 平均分 69.58% 同时训练所有阶段 (1 \u0026amp; 2 \u0026amp; 3): 平均分 67.27% (性能下降 3.32%) 这证明了“渐进式”对于避免不同奖励信号冲突、稳定学习过程至关重要。 移除阶段一的影响 (表3)： 标准流程：平均分 69.58% 移除阶段一 (只进行2→3)：平均分 66.66% (性能下降 4.20%) 这说明，如果智能体没有先学会“如何听话”，那么后续的“学习如何指挥”就成了无源之水，效果大打折扣。 规划器、执行器与端到端性能的演进 (图3)：这张图生动地展示了三阶段训练中各项能力的成长曲线。在阶段一，执行器奖励（Executor Reward）迅速提升；进入阶段二，规划器奖励（Global Planner Reward）开始显著增长；而端到端性能（E2E Reward）在整个过程中稳步攀升。这完美印证了渐进式训练的设计初衷。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为智能体研究开辟了新的道路，但前方的探索空间依然广阔。\n值得探索的问题与挑战： 规划的复杂性与层级性：当前的全局计划仍然是线性的步骤列表。未来的智能体需要处理更复杂的、带有条件分支、循环甚至可以自我调用的层级化计划（Hierarchical Planning）。 规划的效率问题：论文中提到的“生成后选择”（generate-then-select）策略来产生计划，在复杂场景下可能会非常耗时。如何实现更高效的一次性规划（One-shot Planning）或基于检索的规划，是一个重要的研究方向。 更强的鲁棒性与错误恢复：当智能体执行出错或陷入困境时，目前的AdaPlan只是调整后续计划。更高级的智能体应具备根本原因分析和复杂的错误恢复策略的能力，比如主动寻求帮助或设计一个“勘误子任务”。 从模拟到现实的迁移：所有实验均在模拟环境中进行。如何将PilotRL学到的能力有效迁移到充满噪声和不确定性的物理世界，是应用于机器人学领域的核心挑战。 多智能体协同规划：当环境中存在多个智能体时，它们的计划需要相互协调。如何将AdaPlan扩展到多智能体系统（Multi-Agent Systems）中，是一个极具价值的研究课题。 可能催生的新技术和投资机会： 自主软件工程师：能够理解一个大型项目的需求文档，自主生成覆盖整个架构的开发计划，并逐步编写、调试和部署代码的AI。 AI研究助理：能够根据一个科研目标，自主设计实验方案、操作模拟软件、分析数据并撰写初步报告的AI。 下一代机器人操作系统：内置强大的规划与执行协同能力，使得机器人能完成如“打扫整个公寓”或“组装宜家家具”等长时程复杂任务。 智能体即服务（Agent-as-a-Service）平台：提供基于类似PilotRL框架预训练的、具备强大通用规划能力的智能体底层平台，开发者可以在此基础上快速构建各种垂直领域的应用。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以深入探讨的局限性。\n对“上帝视角”评估模型的依赖：整个PilotRL的奖励系统（包括计划质量、执行依从度、E2E性能）都依赖于一个更强大的前沿模型（如DeepSeek-V3）作为“法官”（Judge）。这带来了几个问题： 性能天花板：智能体的最终能力在某种程度上受限于“法官”的认知水平。法官无法理解的更优策略，智能体可能就学不会。 潜在偏见：“法官”自身的偏见可能会被传递给正在训练的智能体。 可复现性与成本：这种方法需要持续调用一个强大的闭源或前沿模型作为环境的一部分，这增加了研究的成本和复现的难度。 计划形式的简单性：虽然名为“全局计划”，但其形式仍然是线性的步骤序列。真正的复杂任务规划可能需要非线性的图结构，例如包含并行任务、条件判断和回溯点的计划图。论文中的方法尚未触及这一点。 环境的局限性：实验环境均为文本或符号化的模拟环境。现实世界的复杂性，如物理交互的随机性、传感器噪声、环境的非预期变化等，论文中的模型并未经历考验。从模拟到现实的“Sim2Real Gap”依然存在。 缺乏对失败的深入分析：论文主要展示了成功的案例和平均性能的提升。但对于智能体在何种情况下会彻底失败，以及失败的原因是什么，缺乏深入的案例分析（Case Study on Failures）。理解失败是改进模型的重要途径。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 对于希望从中汲取创新养分的读者，我认为以下几点最具启发性：\n重点学习的核心思想： “规划器-执行器”解耦思维：这是最核心的架构思想。在设计任何需要长期执行的AI系统时，都可以借鉴这种分工模式。将“思考长远战略”和“处理眼前事务”的能力分开，可以极大地提升系统的逻辑清晰度和鲁棒性。 渐进式训练（Progressive Training）：这个方法论极具普适性。在面对任何复杂的多目标学习任务时，都可以尝试将大目标分解为由易到难的多个阶段性小目标，逐一攻克。这比从一开始就混合所有目标进行优化要稳定和高效得多。 闭环自适应（Closed-loop Adaptation）：不要让你的计划一成不变。建立一个快速的反馈循环，让系统的每一次行动结果都能反过来指导和修正未来的方向。这是所有智能系统走向真正“智能”的必经之路。 需要补充的背景知识： 强化学习基础：特别是策略梯度方法，如PPO (Proximal Policy Optimization)。论文使用的GRPO是其变体，理解PPO中的策略网络、价值网络、优势函数等概念，是理解PilotRL训练过程的关键。 主流智能体范式：务必阅读ReAct (Yao et al., 2023, “ReAct: Synergizing Reasoning and Acting in Language Models”) 的原始论文，这样才能深刻理解PilotRL到底在ReAct的基础上解决了什么核心问题。 LLM-as-a-Judge：了解一下将大型语言模型用作评估器和奖励模型的概念和相关研究。这能帮助你理解PilotRL奖励函数设计的思路及其潜在的优缺点。 希望这份详尽的解读能帮助您全面、深入地掌握这篇优秀论文的精髓。如果您还有任何其他问题，随时可以提出。\n","date":"2025-08-07T00:00:00Z","permalink":"/pilotrltraining-language-model-agents-via-global-planning-guided-progressive-reinforcement-learning/","title":"PilotRL：Training Language Model Agents via Global Planning-Guided Progressive Reinforcement Learning"},{"content":"很基础的多轮对话agent RL论文，类似于alfworld等真实环境，没有训练数据，直接在真实的环境中交互获得数据。我认为这是以后的一个趋势。在SWE-bench上测评。目测采用了react框架，act-\u0026gt;obs-\u0026gt;act。第一步是拒绝微调，就是用交互结果成功的数据进行微调，第二步就是rollout后用奖励信号进行学习。 agent总体可以分为三个部分：基础模型、行动、观察，行动可以是计划、工具调用、实际的动作等，观察可以是环境状态也可以是memory。所有的研究可以在这几个组件进行。\n论文深度解读：迈向自主学习的软件工程师 这篇论文 (arXiv:2508.03501v1) 解决了一个当前AI智能体领域的核心挑战：如何让AI智能体在没有明确、逐步指导的情况下，通过与复杂环境的反复试错和交互，学会解决需要多步骤、长上下文推理的现实问题。软件工程，尤其是修复GitHub上的代码缺陷（issue），是这类问题的绝佳代表。\n传统的AI方法在应对这类任务时，往往依赖以下三种策略： 1. 使用像GPT-4这样强大的闭源专有模型 (proprietary LLMs)，并配合复杂的脚手架 (scaffolding) 代码。 2. 采用耗费大量计算资源的推理时扩展技术 (inference-time scaling techniques)，如蒙特卡洛树搜索。 3. 通过监督微调 (Supervised Fine-Tuning, SFT) 的方式，用更强大的“教师模型”生成的“完美答案”来训练一个“学生模型”。\n这些方法虽然有效，但成本高昂、依赖闭源技术，或者需要难以获取的高质量示范数据，限制了开源社区和普通研究者构建顶级AI软件工程师的能力。\n本文作者独辟蹊径，提出了一条基于强化学习的“自主进化”路线。他们没有寻求外部的“教师”，而是让模型自己成为“学徒”，直接在软件开发环境中“实习”——尝试修复代码、观察编译器的报错、分析测试用例的失败日志，并从最终是否成功修复（一个值为0或1的稀疏奖励信号）中学习。\n为了实现这一点，论文首先在概念上做了一个重要的区分。他们指出，像数学解题或单次代码生成这样的任务，可以被看作是“赌博机” (bandit-style) 问题。AI模型接收一个问题，给出一个答案，然后得到一个奖励，整个过程一步到位，没有中间反馈。\nThe interactive, structured nature of SWE, where actions produce observable transitions and verifiable outcomes, makes it an ideal domain for RL. Yet, to date, most RL applications for LLMs have been limited to single-turn tasks, such as math reasoning or single-shot code generation, which can be trivially modeled as multi-armed bandits or degenerate MDPs with no intermediate environmental feedback (Figure 2).\n翻译：软件工程的交互性、结构化特性，即行动会产生可观察的状态转换和可验证的结果，使其成为强化学习的理想领域。然而，迄今为止，大多数针对LLM的RL应用都局限于单轮任务，例如数学推理或单次代码生成，这些任务可以被简单地建模为多臂赌博机或没有中间环境反馈的退化马尔可夫决策过程（图2）。\n图注：论文中的图2，清晰地对比了单步的“赌博机”式任务（上）与多步交互的POMDP任务（下）。在软件工程任务中，智能体需要执行find命令、分析输出、运行测试、接收反馈，然后决定下一步行动，这是一个典型的交互循环。\n而软件工程则是一个更复杂的部分可观察马尔可夫决策过程 (Partially Observable Markov Decision Process, POMDP)。在这个过程中，智能体无法完全洞察环境的所有状态（例如，代码仓库的完整内部状态），它必须根据一系列的行动（如执行shell命令、编辑文件）和观察（如命令的返回结果、测试日志）来构建对当前状况的理解，并规划后续步骤。\n基于此，论文提出了一套精心设计的两阶段训练流程：\n第一阶段：拒绝微调 (Rejection Fine-Tuning, RFT) 这是一个巧妙的“预热”步骤。他们首先让基础模型（Qwen2.5-72B-Instruct）在数千个软件工程任务上反复尝试，然后只保留那些最终成功解决问题的“幸运”轨迹。接着，用这些成功的轨迹对模型进行监督微调。这一步至关重要，因为它教会了模型如何正确地使用工具、遵循指令格式，相当于一个“岗前培训”。实验数据显示，仅凭基础模型，在SWE-bench Verified测试集上的成功率（Pass@1）仅为11.4%，而经过RFT后，成功率提升到了20.5%。\n第二阶段：多轮强化学习 (Multi-Turn RL) 这是论文的核心贡献。在RFT的基础上，他们采用了一种名为解耦优势策略优化 (Decoupled Advantage Policy Optimization, DAPO) 的改良版RL算法。DAPO是一种无需“评论家网络”(critic)的策略优化算法，它通过比较一组尝试中某个尝试的最终奖励与这组尝试的平均奖励，来计算一个“优势”值，并以此指导模型的参数更新。这比传统的PPO算法更稳定、更高效。\n作者对DAPO进行了针对性的改造，以适应长序列、多轮次的软件工程任务。例如，他们引入了对超长响应的“软惩罚”机制，并让模型在长达131k的上下文窗口中进行训练。为了让学习更高效，他们还设计了一种课程学习 (curriculum learning) 策略：在训练后期，筛选掉那些模型已经能稳定解决的“太简单”的任务和那些从未成功过的“太难”的任务，将计算资源集中在最具学习价值的“中等难度”任务上。\n最终，经过这个两阶段流程训练出的智能体，在SWE-bench Verified上的Pass@1成功率达到了惊人的39.0%，几乎是RFT基线的两倍。这个成绩足以与业界顶尖的开源模型（如DeepSeek-V3-0324）相媲美，证明了这条“自主学习”路径的巨大潜力。\n总而言之，这篇论文不仅仅是发布了一个更强的代码智能体，它更像是一本详细的“操作手册”，展示了如何将一个强大的开源基础模型，通过与环境的直接交互，锻造成一个专业的“AI软件工程师”。它为整个AI Agent领域的发展，特别是在摆脱对专有模型和教师数据依赖方面，提供了宝贵的经验和深刻的启示。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是验证并展示强化学习（RL） 是一种可以有效训练大语言模型（LLM）以解决长周期、多轮交互的软件工程（SWE）任务的强大方法。 解决的实际问题：当前开发高性能SWE智能体主要面临三大瓶颈： 依赖闭源模型：最好的效果通常来自GPT-4等专有模型，这使得研究和应用成本高昂，且技术路线不透明。 依赖教师模型：许多开源工作通过监督微调（SFT）进行，但这需要用一个更强的模型（教师）来生成高质量的解决方案，陷入了“用魔法打败魔法”的循环，并且限制了模型超越教师的能力。 高昂的推理成本：一些方法通过在推理时进行大量搜索（如MCTS）来提升性能，但这在实际部署中速度慢、成本高。 论文旨在解决这些问题，探索出一条仅使用开源模型，无需教师模型示范，并且将智能提升的成本主要放在训练阶段的新路径。 行业意义：这项研究的意义是深远的。 推动AI Agent的自主进化：它证明了AI智能体可以通过与环境的直接交互进行“自我完善”，这是通往更通用、更自主AI的关键一步。 降低顶级AI Agent的开发门槛：为开源社区和中小型企业提供了一套可行的方案，使他们也能构建出与顶级水平相竞争的AI软件工程师，从而促进技术的普及和创新。 开辟新的商业模式：未来，AI Agent的能力可能不再仅仅取决于其基础模型的大小，更取决于其在特定领域“实习”和“进化”的经验。这可能催生出专注于提供高度专业化、经过RL训练的AI Agent服务的公司。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其系统性的训练框架和对RL算法的精巧改造，而非提出一个全新的模型结构。\n新思路：将SWE任务形式化为POMDP 特点：区别于将代码生成视为“输入-输出”的单步任务，本文强调其交互式和状态化的本质。智能体每一步的行动都会改变环境状态（如修改了文件），并收到新的观察（如编译器错误），它需要基于整个历史（history）来决策。 优势：这种建模方式更贴近现实，使得能够应用强大的强化学习框架来优化整个决策链，而不仅仅是单个生成步骤。 新方法：两阶段训练流程（RFT + RL） 第一阶段：拒绝微调 (Rejection Fine-Tuning, RFT) 特点：这是一种高效的“冷启动”方法。它不依赖外部数据，而是通过“广撒网”式的尝试，筛选出模型自身产生的成功案例进行学习。 优势：相比于直接上RL，RFT能快速让模型适应任务的格式和工具，显著提升初始成功率（从11.4%到20.5%），为后续更耗资源的RL阶段提供了一个更好的起点，避免了RL在“一无所知”的情况下进行无效探索。 第二阶段：基于改良DAPO的强化学习 特点： 无评论家（Critic-Free）：采用了DAPO算法，避免了训练一个额外的、不稳定的价值网络（critic network），简化了训练过程。 长上下文适应：将训练的上下文长度从多数开源模型默认的32k扩展到65k，甚至131k，以应对真实软件项目中庞大的代码库和冗长的错误日志。 课程学习：在训练后期（Stage 2），动态调整训练任务池，剔除太简单或太难的任务，集中火力攻克“学习甜点区”的任务。 稀疏奖励处理：整个RL流程仅依赖任务最终成功与否的0/1奖励，这在现实场景中更容易定义和获取。 优势：这套方法鲁棒且有效。它使得模型能够在没有密集、人工设计的奖励信号的情况下，从稀疏的最终结果中学习到复杂的行为链。与依赖教师模型的SFT相比，RL允许模型探索并可能发现SFT数据中不存在的、更优的解决方案路径，具备更高的潜力上限。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了严谨的实验来验证其方法的有效性。\n实验设计： 基准测试集：主要使用SWE-bench，这是一个广泛认可的、从真实GitHub issue中提取任务的基准。为了快速迭代，他们还创建了一个50个问题的随机子集VERIFIED-50用于中间评估。 消融研究 (Ablation Study)：论文通过分步展示模型性能，清晰地证明了每个阶段的贡献。这是一个经典的实验设计，说服力很强。 横向对比：将最终训练出的模型与其他顶尖的开源模型（如DeepSeek-V3-0324, Qwen3-235B等）在相同的环境下进行“公平竞赛”。 实验数据和结果： 核心性能提升：如下表所示（根据论文Table 1简化），每个阶段都带来了显著的性能提升。 | 模型/阶段 | 上下文长度 | SWE-bench Verified (Pass@1) | | :— | :—: | :—: | | Qwen2.5-72B-Instruct (基础模型) | 65k | 11.4% | | + RFT (第一阶段后) | 65k | 20.5% | | + RL Stage 1 (RL第一阶段后) | 65k | 35.7% | | + RL Stage 2 (最终模型) | 131k | 39.0% | | DeepSeek-V3-0324 (竞品) | 131k | 39.6% | | Qwen3-235B (更大模型) | 131k | 25.8% |\n关键数据解读： \u0026gt; Our two-phase procedure yields substantial improvements… The final model achieves 39.0% on the full SWE-BENCH VERIFIED. \u0026gt; \u0026gt; 翻译：我们的两阶段流程取得了实质性的改进……最终模型在完整的SWE-BENCH VERIFIED上达到了39.0%的成功率。\n这组数据清晰地表明：\nRFT的有效性：RFT将成功率提升了近一倍，证明了其作为预热步骤的价值。 RL的巨大贡献：RL阶段将成功率从20.5%进一步提升到39.0%，是性能增长的主要动力。 竞争力：他们训练的72B模型，性能上追平了以代码能力著称的DeepSeek模型，并超过了参数量远大于自身的Qwen3-235B模型，展示了其训练方法的卓越效率。 另一个值得注意的数据是Pass@10的得分。最终模型的Pass@1（单次尝试成功率）为39.0%，而Pass@10（10次尝试中至少有1次成功）高达58.4%。这表明模型生成的解决方案候选中，往往包含正确答案，只是模型有时无法准确判断哪个是最好的。这为未来引入重排（re-ranking）模块来进一步提升性能指明了方向。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文打开了一扇门，门后还有广阔的探索空间。\n稀疏奖励与信用分配 (Sparse Rewards and Credit Assignment) 挑战：当前模型仅从最终的0/1奖励中学习。在一个包含数十步操作的轨迹中，很难确定到底是哪几步关键操作导致了最终的成功或失败。 探索方向： 奖励塑造 (Reward Shaping)：设计更密集的奖励信号，比如“成功通过一个单元测试”、“编译错误减少”等，都可以给予一个小的正向奖励。 引入价值网络 (Value Head/Critic)：虽然本文为了简化而放弃了评论家，但训练一个辅助的评论家网络来评估每一步的“价值”，仍然是解决信用分配问题的经典途径。 前缀采样 (Prefix Sampling)：从一个成功的轨迹中间某一步开始继续探索，可以更精确地评估后续决策的影响。 不确定性与风险意识 (Uncertainty and Risk-Awareness) 挑战：当前的智能体被训练得“不惜一切代价”去提交解决方案，即使它成功的概率很低。在真实世界中，一个好的工程师懂得何时该“放弃”或“求助”。 探索方向：训练模型输出一个置信度分数，或者利用模型本身的熵作为不确定性的代理指标。这能让智能体在没有把握时选择停止，或者请求人类介入，实现更好的人机协作。 技术和投资机会： AI Agent开发与评估平台：构建能够支持大规模、并行、有状态交互的仿真环境，将成为训练下一代AI Agent的核心基础设施，具有巨大的商业价值（类似文中的Tracto AI）。 高效RL算法与框架：专门为LLM设计的、能够处理超长上下文和稀疏奖励的RL算法库和训练框架，将是关键的技术壁垒。 垂直领域专业Agent：将这套方法论应用到其他需要复杂交互的领域，如科学研究、药物发现、金融分析、游戏等，可以催生出一系列高度专业化的AI Agent，这本身就是巨大的投资赛道。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但仍有一些值得商榷和进一步验证的地方。\n任务泛化性存疑：所有训练和测试都围绕SWE-bench展开，其任务类型主要是修复bug。该智能体在其他类型的软件工程任务上（如新功能开发、代码重构、文档撰写）表现如何，是一个未知数。 数据清洗的潜在偏见：作者从21,336个原始任务中，通过一系列规则筛选出了7,249个任务。例如，他们排除了“根据LLM评估得分较低的任务”。这种清洗虽然保证了训练的稳定性，但也可能无意中过滤掉了一些更复杂、更模糊但更真实的边缘案例，导致模型对“干净”问题的解决能力被高估。 对“教师”的隐性依赖：论文声称不依赖教师模型，这是指它不使用教师模型生成的轨迹进行SFT。然而，SWE-bench本身提供的“标准答案补丁” (ground-truth solution patch) 在计算最终奖励时，充当了一个“完美的裁判”。智能体的目标是找到能通过这个补丁所对应的测试的方法，这在某种程度上仍是一种对“标准答案”的拟合，而非完全自由的探索。 复现难度：整个流程涉及复杂的分布式基础设施（16个H200节点）、定制化的RL算法和课程学习策略，包含众多超参数。如果没有同等的工程能力和计算资源，其他研究者可能难以完全复现其结果。 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，以下几点是关键：\n核心启发：问题建模是第一生产力 最大的启发在于如何正确地为问题建模。将一个复杂的任务从“一步生成”的思维定式中解放出来，看作是“多步交互与决策”的POMDP过程，是应用更强大算法（如RL）的前提。在您的工作中，可以思考是否也能将某些任务拆解为交互式的智能体问题。 可以“拿来即用”的创新点： 拒绝微调 (RFT)：这是一个非常实用且低成本的自增强技巧。只要您的任务有一个明确的、可自动验证的成功标准（a verifiable success criterion），您就可以用它来为您的模型“热身”。操作流程：用您的模型生成大量候选方案 -\u0026gt; 用验证器筛选出成功方案 -\u0026gt; 用这些成功方案对您的模型进行SFT。这几乎适用于所有代码、文本生成甚至设计领域。 课程学习策略：在任何长周期的训练任务中，动态调整训练集，优先学习“半懂不懂”的知识点，是一个普遍适用的加速训练、提升效果的策略。您可以借鉴其思路：定期评估模型在各项任务上的表现，然后将资源集中在那些“有进步但尚未完全掌握”的任务上。 需要补充的背景知识： 强化学习基础：尤其是策略梯度（Policy Gradient） 方法，理解PPO (Proximal Policy Optimization) 是理解DAPO的基础。核心概念包括：策略（Policy）、奖励（Reward）、优势函数（Advantage Function）。 软件工程基准 (SWE-bench)：了解SWE-bench是如何构建的，即它提供一个代码仓库、一个GitHub issue描述和一个用于验证的脚本。这有助于理解任务的输入、输出和评估方式。 大模型训练与推理技术：对长上下文扩展技术（如本文的YaRN）、分布式训练和高效推理服务框架（如vLLM）有所了解，有助于理解论文的技术实现细节和其面临的工程挑战。 ","date":"2025-08-07T00:00:00Z","permalink":"/training-long-context-multi-turn-software-engineering-agents-with-reinforcement-learning/","title":"Training Long-Context, Multi-Turn Software Engineering Agents with Reinforcement Learning"},{"content":"和Group Sequence Policy Optimization撞车了，思想都是几何平均。\n论文深度解读：从“算术平均”到“几何平均”的策略优化革命 这篇名为 《Geometric-Mean Policy Optimization (GMPO)》 (https://arxiv.org/abs/2507.20673) 的论文，为我们揭示了在大型语言模型（LLM）强化学习（RL）后训练领域中一个看似微小却影响深远的改进。其核心目标是解决当前主流的近端策略优化（PPO）及其变体，特别是组相对策略优化（GRPO），在训练过程中普遍存在的“不稳定性”问题。\n在深入探讨之前，我们需要理解一个关键背景。为了提升LLM在数学、代码、逻辑推理等复杂任务上的能力，研究者们普遍采用强化学习方法进行“后训练”（Post-training）。其中，GRPO是一种非常有效的方法，它通过比较模型对同一个问题生成的多个“答案”（rollouts）的好坏，来计算相对奖励，从而指导模型学习。然而，论文作者敏锐地发现，GRPO虽然强大，却像一匹烈马，难以驾驭。\n问题的根源在于GRPO的目标函数。它试图最大化所有答案中、每个词元（token）级别奖励的算术平均值（Arithmetic Mean）。这里有一个核心的技术概念叫做重要性采样率（Importance Sampling Ratio），您可以通俗地理解为一个“校正因子”。因为我们是用旧策略（π_old）产生的数据来训练新策略（π_θ），这个比率（π_θ / π_old）就是用来校正这种数据分布差异的。理想情况下，这个比率应该接近1。但如果某个词元的奖励因为某种原因变得特别高或特别低（即“离群点”），算术平均的机制会极大地放大这个离群点的影响，导致重要性采样率剧烈波动，出现极端值。\n论文在图1中非常直观地展示了这一点：\nGRPO optimizes the arithmetic mean of token-level rewards, while GMPO uses the geometric mean (left). For GRPO, extreme importance sampling ratios emerge frequently during training, which implies unstable policy updates. In contrast, GMPO results in more stable importance sampling ratios with less extreme values and narrower range (right).\n这种剧烈的波动，就像开车时猛踩油门又猛踩刹车，使得模型的策略更新（Policy Update）变得极具攻击性和不确定性，最终损害模型的性能和训练的稳定性。\nGMPO的妙手回春之计，正是将目标函数从“算术平均”替换为“几何平均（Geometric Mean）”。 这是一个统计学上众所周知的常识：几何平均对离群点（outliers）的敏感度远低于算术平均。一个极端的数值不会对几何平均值产生不成比例的巨大影响。通过这一改变，GMPO从根本上抑制了由奖励离群点导致的重要性采样率爆炸问题，使得整个训练过程变得如丝般顺滑。\n论文不仅提出了这个优雅的解决方案，还提供了坚实的理论和实验支撑。理论上，论文证明了GMPO的目标函数值域更窄，梯度更新更平滑，从而降低了训练方差。实验上，论文展示了令人信服的结果。例如，在多个数学推理基准测试中，GMPO-7B模型相较于GRPO平均性能提升了4.1%（从59.3%提升至63.4%）。在多模态推理任务上，也取得了1.4%的提升（从53.3%提升至54.7%）。这些数据明确证实了GMPO在提升模型推理能力和稳定性方面的双重优势。\n此外，论文还探讨了两个关键的实现细节： 1. 词元级裁剪（Clipping at token-level）: 相较于之前在整个序列级别进行裁剪的方法，GMPO在每个词元上进行裁剪，这被证明是更稳定、更精细的控制策略，避免了因单个词元问题而“一刀切”地扼杀整个序列的梯度。 2. 更宽的裁剪范围（Clipping wider）: 由于GMPO本身更稳定，它可以使用比GRPO更宽的裁剪范围（e^-0.4到e^0.4，约等于0.67到1.49），这允许模型在训练时有更大的探索空间，避免过早陷入局部最优，从而激发了更大的性能潜力。\n总而言之，这篇论文以一个简单而深刻的洞察为基石，通过将算术平均替换为几何平均，优雅地解决了强化学习后训练中的一个核心痛点。它不仅提升了模型的最终性能，更重要的是，它为开发更可靠、更可扩展的RL系统铺平了道路，展现了从基础数学原理中汲取灵感解决前沿工程问题的巨大价值。\n接下来，我将针对您的六个问题进行详细解读。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是提升大型语言模型强化学习（尤其是GRPO算法）的训练稳定性。\n解决的实际问题： 它要解决的是，在使用GRPO这类PPO变体算法对LLM进行后训练时，由于目标函数对离群奖励值（outlier rewards）过于敏感，导致重要性采样率（importance sampling ratio）出现极端值，进而引发策略更新不稳定（unstable policy updates）的问题。这种不稳定性会降低训练效率，损害模型的最终性能，并使得训练过程难以复现和扩展。\n对行业发展的重要意义：\n可靠性与可扩展性： 随着模型规模越来越大，训练成本日益高昂。一个不稳定的训练过程意味着大量的计算资源被浪费，且结果难以预测。GMPO提供了一种更稳定的训练范式，这对于将RL技术规模化应用于千亿甚至万亿参数级别的模型至关重要，是实现可靠、可扩展RL系统的关键一步。 性能提升： 稳定的训练过程本身就能带来性能的提升。如论文所示，更稳定的GMPO在多个推理基准上超越了GRPO。这意味着行业可以以更低的风险和更高的效率，开发出推理能力更强的AI模型。 降低技术门槛： 一个鲁棒性强、不易“翻车”的算法，可以降低RL后训练的技术门槛。中小型企业或研究团队可能没有足够资源去反复调试一个高度敏感的训练过程，GMPO这类更“宽容”的算法使得先进的RL技术更加普惠。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于提出了Geometric-Mean Policy Optimization (GMPO)。它并非一个全新的模型，而是对现有GRPO算法目标函数的关键改进。\n核心思路：用几何平均替代算术平均 之前的方法 (GRPO): GRPO的目标函数可以简化理解为最大化 token 奖励的算术平均。其形式如论文公式(2)所示，本质上是一个求和（∑）操作的平均。 \u0026gt; \\(J_{GRPO}(\\pi_{\\theta}) = \\mathbb{E}[\\frac{1}{G}\\sum_{i=1}^{G}\\frac{1}{|o_i|}\\sum_{t=1}^{|o_i|}\\frac{\\pi_{\\theta}(o_{i,t}|q, o_{i,\u0026lt;t})}{\\pi_{\\theta_{old}}(o_{i,t}|q, o_{i,\u0026lt;t})}\\hat{A}_i]\\) 新方法 (GMPO): GMPO 的目标函数则是最大化 token 奖励的几何平均。其形式如论文公式(3)所示，本质上是一个求积（∏）再开方。 \u0026gt; \\(J_{GMPO}(\\pi_{\\theta}) = \\mathbb{E}[\\frac{1}{G}\\sum_{i=1}^{G}(\\prod_{t=1}^{|o_i|}\\frac{\\pi_{\\theta}(o_{i,t}|q, o_{i,\u0026lt;t})}{\\pi_{\\theta_{old}}(o_{i,t}|q, o_{i,\u0026lt;t})})^{\\frac{1}{|o_i|}}sgn(\\hat{A}_i)|\\hat{A}_i|^{\\frac{1}{|o_i|}}]\\) 特点与优势： 对离群点鲁棒（Robust to Outliers）： 这是最核心的优势。几何平均天然地平滑了极端值的影响。一个token的奖励即使异常高，在GMPO中也只是乘积项中的一个，其影响力被大大削弱；而在GRPO中，它会在求和中占据主导地位。这直接带来了更稳定的重要性采样率，如 图1 所示。 更稳定的梯度更新： 论文在公式(5)和(6)中分析了梯度。在GRPO中，每个token的梯度权重包含其自身的importance sampling ratio，容易导致梯度爆炸。而在GMPO中，权重包含的是所有token ratio的几何平均，这是一个更“全局”、更平滑的视角，使得梯度更新更稳健。 更优的探索-利用平衡： 词元级裁剪 (Token-level Clipping): 论文指出，相较于DeepSeek-R1中在序列级别裁剪，GMPO在词元级别进行裁剪更稳定。序列级裁剪一旦触发，会“扼杀”整个序列所有token的梯度，过于激进。而词元级裁剪更为精细。 更宽的探索范围 (Wider Clipping Range): 由于GMPO本身更稳定，它可以承受更大的策略变化。因此，它采用 (e^-0.4, e^0.4) 作为裁剪范围，比GRPO的 (0.8, 1.2) 更宽。这赋予了模型更大的探索自由度，有助于发现更好的策略。图3(a) 显示GMPO在训练中维持了比GRPO更高的熵（Entropy），这正是模型持续探索的直接证据。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列精心设计的实验，在语言和多模态任务上全面验证了GMPO的有效性。\n实验设计： 基线模型 (Base Models): 覆盖了不同规模和类型的模型，如 Qwen2.5-Math-1.5B/7B、DeepSeek-R1-Distill-Qwen-7B（语言任务）和 Qwen2.5-VL-Instruct-7B（多模态任务），以验证方法的普适性。 训练数据： 数学任务使用 MATH 数据集，多模态任务使用 Geometry3K 数据集。 评估基准 (Benchmarks): 涵盖了从中等到高难度的多个数学推理基准（AIME24, AMC, MATH500, Minerva, OlympiadBench）和视觉问答基准（Geometry3K）。 评估指标 (Metric): 主要使用 Pass@1，即模型生成一次答案就通过的概率，这是一个严格且有说服力的指标。 对比方法： 主要与GRPO和Dr.GRPO（一个GRPO的变体）进行直接对比，同时也列出了其他SOTA（State-of-the-art）方法的性能作为参考。 消融研究 (Ablation Studies): 通过控制变量法，分别验证了“几何平均替代算术平均”、“裁剪策略”、“归一化项”等不同组件的有效性。 实验数据和结果： 核心性能对比 (Table 1 \u0026amp; 2):\n在数学推理任务上，基于最强的 R1-Distill 模型，GMPO的平均Pass@1准确率达到了 63.4%，显著高于GRPO的 59.3%。 在多模态 Geometry3K 任务上，GMPO也以 54.7% 的准确率小幅超越了GRPO的 53.3%。 稳定性验证 (Figure 3):\nKL散度 (KL Divergence): 图3(c) 显示，GMPO与训练前模型的KL散度始终保持在较低水平，而GRPO则出现剧烈峰值。这表明GMPO学习过程更稳定，没有“忘记”初始模型的能力，降低了过拟合风险。 熵 (Entropy): 图3(a, b) 显示，GMPO在训练中维持了更高的token熵，意味着模型策略更多样，探索更充分。 训练奖励 (Training Rewards): 图3(d, f) 显示，在更具挑战性的数据集上，GMPO的训练奖励持续稳定地高于GRPO。 消融实验结果 (Table 3): \u0026gt; The performance of GRPO and GMPO is reported in lines 1 and 5 of Table 3, respectively. GRPO achieves an average performance of 51.2% by optimizing the arithmetic mean of token-level rewards. In contrast, GMPO improves this to 52.7%, outperforming GRPO by 1.5%…\n该表的数据清晰地表明，从GRPO（行1，51.2%）到GMPO（行5，52.7%）的性能提升，主要就来自于几何平均的引入。\n### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ GMPO为我们打开了一扇新的窗户，未来的探索方向广阔：\n算法的泛化性： 任务扩展： 论文主要聚焦于数学和几何推理。GMPO在代码生成、长文本摘要、创意写作、多轮对话等其他需要复杂推理和规划的任务上表现如何？这些任务的奖励函数可能更稀疏或更模糊，对稳定性的要求更高。 架构扩展： GMPO在非Transformer架构，如Mamba（状态空间模型）上是否同样有效？ 与其他RL技术的结合： 与更复杂的奖励模型结合： 现实世界的奖励信号往往是多维度、有噪声的。GMPO的稳定性优势，可能使其能更好地与复杂的、甚至是由模型自身学习的奖励模型（Reward Model）相结合，而不会被奖励模型的噪声干扰。 与离线RL（Offline RL）结合： 是否可以将GMPO的思想应用于离线强化学习，在不与环境交互的情况下，更稳定地从固定数据集中学习策略？ 超大规模模型下的表现（Scaling Law）： 论文的实验主要在7B模型上进行。当模型规模扩展到100B、500B甚至1T级别时，GMPO的稳定性优势是否会更加突出？或者，在超大规模下是否会出现新的不稳定性因素？探索其缩放定律（Scaling Law）将非常有价值。 技术与投资机会： 可靠的RL平台： 能够提供集成GMPO这类稳定算法的一站式、低门槛RL微调平台，将具有巨大的商业价值，可以服务于各行各业需要定制化AI能力的企业。 专业领域模型： 利用GMPO稳定训练的优势，可以在金融、法律、医疗等专业领域，基于专有数据训练出能力超强的垂直领域模型。 自动化AI研究员（AI for Science）： 稳定的RL是实现能够自主探索、进行科学发现的AI Agent的基础，这是一个极具想象力的长远投资方向。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍有一些方面值得商榷或需要进一步验证：\n性能提升的显著性： 在多模态任务 Geometry3K 上，1.4%（54.7% vs 53.3%）的提升虽然是积极的，但幅度相对有限。这可能需要更多次的重复实验来验证其统计显著性，并排除实验误差的影响。 基线选择的局限性： 论文的主要对比对象是GRPO和Dr.GRPO。虽然这是最直接的对比，但如果能加入与DPO (Direct Preference Optimization) 等其他非PPO类主流对齐（Alignment）算法的对比，将更能体现GMPO在整个RLHF领域的综合竞争力。 计算开销的讨论： 论文没有明确讨论GMPO相对于GRPO在计算（特别是梯度计算）上的开销差异。虽然几何平均的计算在现代硬件上可能不是瓶颈，但对于一个旨在应用于大规模训练的算法，对其计算复杂度和效率的分析是必不可少的。 对负奖励的处理： 几何平均对正数定义良好，但对负奖励的处理需要特殊技巧。论文中使用了 sgn(Â) 函数来处理符号，这是一种务实的做法。但这种处理方式是否最优，以及它对梯度的影响是否还有更深入的理论分析空间，是值得探讨的。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个含金量极高的启发：\n核心启发：回归基础，寻找“优雅解” 最大的启发是，解决前沿、复杂工程问题的钥匙，有时就藏在基础的数学或统计学原理中。面对棘手的“不稳定性”问题，作者没有堆砌复杂的模块，而是回归到“平均”这个概念的本质，通过一个简单的替换（算术-\u0026gt;几何），优雅地解决了问题。这启示我们，在自己的研究或工作中，遇到瓶颈时，不妨跳出复杂的框架，回归第一性原理思考。 拿来即用的想法： 鲁棒统计学（Robust Statistics）的应用： 在您的工作中，任何涉及到“平均”或“聚合”数据的地方，如果数据存在噪声或离群点，都可以思考是否可以用更鲁棒的统计量来替代。除了几何平均，还可以考虑中位数（Median）、截尾平均（Trimmed Mean）等。这在数据预处理、模型评估、集成学习等多个环节都可能派上用场。 “宏观”与“微观”控制的平衡： 论文中“序列级裁剪”与“词元级裁剪”的对比，是一个关于控制粒度的绝佳案例。这启示我们，在设计调控机制时，要思考是在全局（宏观）层面“一刀切”，还是在局部（微观）层面精细化管理。这个思想可以应用于特征工程、正则化方法、注意力机制设计等。 需要补充的背景知识： 为了完全消化这篇论文并举一反三，我建议您补充了解以下知识： 强化学习基础： 特别是策略梯度（Policy Gradient）方法。 PPO (Proximal Policy Optimization): 这是理解GRPO和GMPO的基石。建议阅读 Schulman 等人的原始PPO论文 (https://arxiv.org/abs/1707.06347)。 重要性采样（Importance Sampling）： 深刻理解其原理、作用以及风险，这是理解整个问题的关键。 RLHF (Reinforcement Learning from Human Feedback): 了解PPO、GRPO这类算法在大模型“对齐”中的应用背景。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。\n","date":"2025-08-05T00:00:00Z","permalink":"/geometric-mean-policy-optimization/","title":"Geometric-Mean Policy Optimization"},{"content":"通过外部LLM反思修订prompt，真正的p门！\n这篇论文的核心贡献在于提出了一种名为 GEPA (Genetic-Pareto) 的新型提示（Prompt）优化器。它的出现，旨在解决当前大模型（LLM）应用中一个非常棘手的问题：如何高效、低成本地优化由多个LLM模块、工具调用和复杂逻辑组成的 “复合AI系统”（Compound AI Systems）。\n传统上，优化这类系统的主流方法是强化学习（Reinforcement Learning, RL），例如论文中反复对比的 GRPO (Group Relative Policy Optimization) 算法。这类方法虽然有效，但存在一个致命缺陷：样本效率极低。正如论文所言：\nWhile these RL approaches are effective, they typically require tens of thousands of rollouts in practice to fit new tasks. (虽然这些RL方法是有效的，但在实践中它们通常需要数以万计的“演练”来适应新任务。)\n这意味着，为了让系统学会一个新任务，我们需要消耗大量的计算资源和API调用费用来“刷分”，这在许多现实场景中是不可接受的。\nGEPA的创新之处在于它彻底改变了学习的“信号源”。它认为，仅仅使用一个最终的标量奖励值（比如任务得分0.85）来指导学习，是对LLM强大语言能力的巨大浪费。复合AI系统在执行任务过程中的每一步——包括它的推理链、工具调用、甚至是评估器（如编译器）返回的错误信息——都构成了富含信息的自然语言轨迹。GEPA的核心思想就是利用LLM本身来“阅读”和“反思”这些语言轨迹，从而进行更智能、更高效的自我改进。\n为了实现这一思想，GEPA构建了三大支柱：\n基因式提示演化 (Genetic Prompt Evolution)：它将系统中的每个提示视为一个“基因”。通过不断迭代，优秀的提示得以保留和演化，从而提升整个系统的性能。这借鉴了生物进化论的思想。 反思式提示突变 (Reflective Prompt Mutation)：这是GEPA最关键的创新。传统进化算法的“突变”往往是随机的，而GEPA的突变是“有智慧的”。它会让一个更高阶的LLM（元LLM）去分析失败或成功的案例轨迹，并用自然语言进行反思：“上一个提示为什么失败了？可能是因为它没有考虑到用户的地理位置。我应该修改提示，让它明确要求这一点。” 这种基于自然语言反馈 (Natural Language Feedback) 的反思，使得提示的优化方向性极强，效率极高。 基于帕累托的候选选择 (Pareto-based Candidate Selection)：为了避免优化过程陷入“局部最优解”（即找到一个还不错的提示后就停止进步），GEPA引入了帕累托最优的概念。这个概念可能比较新颖，通俗地讲，它旨在维持一个“精英团队”而非“单一个人冠军”。在优化过程中，GEPA会保留一组在不同任务实例上各有所长的“专家”提示。比如，提示A在任务1上是冠军，提示B在任务2上是冠军，那么GEPA会同时保留A和B，而不是只保留平均分最高的那个。这保证了提示的多样性，鼓励探索更多样的解决方案，从而找到全局更优的提示。论文中的图3清晰地展示了这个流程。 (图注：改编自论文图3，展示了GEPA的迭代优化循环，包括帕累托筛选、反思式突变和系统感知合并等关键步骤)\n通过这套组合拳，GEPA在实验中取得了惊人的成果。在Qwen3 8B和GPT-4.1 Mini两个模型上，跨越HotpotQA（多跳问答）、IFBench（指令遵循）等四个不同任务，GEPA不仅全面超越了之前的SOTA提示优化器MIPROv2，更在性能上击败了需要进行模型权重训练的GRPO。最亮眼的数据是：\nAcross all four benchmarks, GEPA demonstrates rapid adaptation and robust generalization in compound AI systems—outperforming GRPO (24,000 rollouts with LoRA) by up to 19% while using up to 35× fewer rollouts. (在所有四个基准测试中，GEPA在复合AI系统中展示了快速适应和强大的泛化能力——其性能比使用24,000次演练和LoRA微调的GRPO高出多达19%，而使用的演练次数却少了35倍。)\n这意味着GEPA用远低于强化学习的成本，实现了更高的性能。这无疑为大模型时代的应用落地提供了一个极具吸引力的降本增效方案。\n接下来，我将按照您提出的六个问题，对论文进行更详细的拆解分析。\n一、 论文的研究目标与意义 研究目标：论文的核心研究目标是为复合AI系统 (Compound AI Systems) 创建一个样本高效 (sample-efficient) 的优化框架。这类系统通常由多个LLM调用、外部工具（如搜索引擎、代码解释器）和复杂的控制流构成，是构建高级AI智能体（Agent）的基础。\n解决的实际问题：\n高昂的优化成本：当前主流的基于强化学习（如GRPO）的优化方法需要成千上万次的“试错”（即rollouts），这在金钱和时间上都非常昂贵，限制了模型的快速迭代和在预算有限场景下的应用。 学习信号的浪费：传统的RL方法通常将复杂的任务执行过程压缩成一个单一的奖励分数（scalar reward），忽略了过程中产生的丰富文本信息（如推理步骤、错误日志），这对于语言能力强大的LLM来说是一种信息浪费。 模型优化的可解释性差：通过梯度下降更新模型权重的方式通常是一个“黑箱”，而GEPA通过修改自然语言提示来优化系统，其优化过程和结果（即生成的提示）具有很强的可解释性。 行业意义：\n降低AI应用门槛：提供了一种低成本、高效率的优化方法，使得中小型企业或个人开发者也能负担得起复杂AI系统的调优，从而推动AI应用的普及。 加速AI智能体迭代：在智能体（Agent）技术飞速发展的今天，如何让Agent快速学习新工具、适应新任务是核心挑战。GEPA提供了一种“快思考”式的优化路径，能极大加速Agent的开发和迭代周期。 推动“语言驱动学习”新范式：这篇论文的探索预示着一种新的学习范式——让模型直接从语言反馈中学习，而不是依赖数值奖励。这更接近人类的学习方式，可能催生出更智能、更自适应的AI系统。 二、 论文的核心方法与创新 论文提出的GEPA方法，其创新性主要体现在它巧妙地融合了进化算法、LLM的反思能力和多目标优化思想。\n新的思路、方法或模型： 反思式突变 (Reflective Prompt Mutation)：这是GEPA与传统进化算法（如EvoPrompt）最根本的区别。它不是随机改变提示中的词句，而是设立一个“元任务”：让LLM扮演优化者的角色，输入是“当前指令”、“任务实例”、“系统输出”以及“关于输出好坏的反馈”，输出是“一个更好的新指令”。这使得每一次“突变”都基于对失败和成功的深刻“反思”，是高度智能化的。论文附录B中给出了用于此过程的元提示模板。 帕累托候选选择 (Pareto-based Candidate Selection)：这是GEPA与贪心优化策略（Greedy Strategy）的核心区别。传统方法可能会不断优化那个在整个验证集上平均分最高的提示，但这容易陷入局部最优。GEPA通过维护一个帕累托前沿，保留了那些在某些“刁钻”任务上表现出色的“偏科生”提示。这保证了种群的多样性，使得优化过程能够跳出局部舒适区，探索更广阔的解空间。论文中的算法2详细描述了这一过程，它通过计算每个候选者在多少个任务实例上取得了最优成绩来决定其被选中的概率。 系统感知合并 (System Aware Merge)：这是GEPA+Merge变体中的一个策略。它允许多个独立的优化“血统”（lineages）在演化后期进行“联姻”。比如，一个血统演化出了非常擅长生成查询的模块A，另一个血tǒng演化出了非常擅长总结答案的模块B，合并策略可以将A和B组合成一个新的、更强大的候选者。 与之前方法的比较优势： vs. GRPO (强化学习)： 样本效率：GEPA利用丰富的语言反馈，学习效率远高于只看最终分数的GRPO。如前所述，最高可达35倍的效率提升。 优化对象：GRPO优化的是模型权重（需要LoRA等技术），而GEPA优化的是自然语言提示，无需训练，更加轻量和灵活。 vs. MIPROv2 (贝叶斯优化)： 优化粒度：MIPROv2同时优化指令和少量示例（few-shot examples），而GEPA专注于优化指令本身。 提示长度与成本：由于GEPA不生成冗长的few-shot示例，其最终产出的提示比MIPROv2的短得多（论文图15显示，短约33%），这在实际API调用中意味着更低的成本和延迟。 泛化能力：实验结果（图14）显示，GEPA生成的指令在从验证集到测试集的泛化表现上优于MIPROv2，意味着它学到的“规则”更具普适性。 三、 实验设计与结果分析 论文的实验设计非常严谨和全面，有力地支撑了其结论。\n实验设计： 基准测试 (Benchmarks)：选取了四个多样化的任务：HotpotQA（多跳问答）、IFBench（复杂指令遵循）、HoVer（多跳事实核查）和PUPA（隐私保护），覆盖了推理、遵循、查询生成等多种能力。 模型 (Models)：使用了业界领先的开源模型（Qwen3 8B）和商业模型（GPT-4.1 Mini），验证了方法的跨模型有效性。 对比方法 (Baselines)：选取了三个代表性的对比对象：Baseline（未优化）、MIPROv2（SOTA提示优化器）和GRPO（SOTA RL优化器），确保了比较的公平性和说服力。 评估指标 (Metrics)：除了各任务的性能得分，还重点考察了样本效率（性能-Rollouts曲线）和成本（最终提示的Token长度）。 实验数据和结果： 核心性能对比：Table 1是论文最重要的结果展示。我为您整理了一个简化版本： 模型 方法 聚合性能提升 备注 Qwen3 8B MIPROv2 +6.26% SOTA提示优化器 GRPO +2.29% RL权重优化 GEPA +12.44% 显著超越所有基线 GPT-4.1 Mini MIPROv2 +7.04% SOTA提示优化器 GEPA +14.29% 性能翻倍 GEPA+Merge +16.02% 达到最佳性能 * **样本效率优势**：论文中的一系列性能-Rollouts曲线（如Fig 1, 10-13）直观地展示了GEPA的效率。在所有任务中，GEPA的性能曲线都以最陡峭的斜率上升，意味着它用少得多的“试错”次数就达到了甚至超越了其他方法经过大量训练后的性能水平。例如，在HotpotQA任务上，GEPA仅用几百次rollouts就达到了GRPO用24000次rollouts才能达到的性能。 * **帕累托选择的有效性**：Table 2的消融实验证明了帕累托选择的重要性。与简单的“选择最佳”（SelectBest）策略相比，使用帕累托策略的GEPA在聚合性能上有**+6.4%**的提升。这说明了维持种群多样性对于避免局部最优至关重要。 四、 未来研究方向与潜在机遇 这篇论文为该领域打开了新的大门，论文的第8节也明确指出了未来的研究方向。\n值得探索的问题和挑战： 反馈工程 (Feedback Engineering)：论文提出了这个新概念。什么样的反馈最有效？是编译器的报错，还是用户对输出的自然语言评价，或是模型内部的推理轨迹？系统地研究如何设计和利用最高质量的语言反馈，将是一个充满潜力的研究方向。 提示与权重优化的结合：GEPA专注于提示，RL专注于权重。未来的终极方案很可能是两者的结合。例如，用GEPA快速找到一个高质量的提示，然后用RL对模型进行轻量微调，可能会达到1+1\u0026gt;2的效果。 更高效的候选验证：论文指出，GEPA的大部分计算开销用在了在验证集上评估候选提示。开发动态的、基于子集的或者代理模型（proxy model）的验证方法，可以进一步提升GEPA的效率。 自适应的演化策略：如何让GEPA自动决定何时进行“突变”，何时进行“合并”（crossover）？设计自适应的超参数调整策略，可以让优化器本身变得更加智能。 新技术和投资机会： 智能体“自动驾驶”系统：GEPA本质上是一个能让AI系统自我优化的“元系统”。这预示着一个巨大的市场机会——为各种AI智能体框架（如LangChain, AutoGen等）提供即插即用的“优化插件”或“自动驾驶”服务，让开发者无需成为RL专家也能构建高性能的Agent。 下一代AI开发平台：未来的AI开发平台可能不再仅仅是代码库和API的集合，而是包含强大“自优化”能力的集成环境。开发者只需定义任务目标和提供反馈渠道，平台就能自动完成提示工程、模型微调等一系列优化工作。 可解释AI (XAI) 的新维度：GEPA的优化过程是基于语言和逻辑的，这为理解和调试复杂AI系统提供了新的视角。基于此开发的可解释性工具，能帮助我们更好地理解AI的“思考过程”。 五、 论文的局限与批判性思考 从批判的视角看，尽管这篇论文非常出色，但仍存在一些值得商榷或需要进一步探索的地方。\n不足与缺失： 与全参数微调的界限模糊：论文承认，在数据量极其庞大的情况下，传统的全参数微调（full-parameter finetuning）可能仍然是上限最高的方案。但论文没有探讨GEPA与这类“重型”方法之间的性能边界在哪里，即在多大的数据和计算预算下，GEPA的优势会减弱。 对元LLM能力的依赖：“反思式突变”的有效性高度依赖于执行反思的那个LLM本身的能力。如果这个LLM的推理和反思能力不强，GEPA的性能可能会大打折扣。论文没有对这种依赖性进行量化分析。 忽略了Few-Shot示例的优化：GEPA完全专注于指令（instruction）的优化，而像MIPROv2等方法会同时优化指令和示例（exemplars）。在某些任务中，好的示例至关重要。论文虽然将此作为“提示更短”的优势，但也可能放弃了一部分潜在的性能提升空间。 “合并”策略的普适性存疑：论文提到GEPA+Merge策略在Qwen3 8B上部分任务表现不佳，说明这个策略的超参数（如调用时机）可能对不同模型和任务比较敏感，其鲁棒性有待进一步验证。 需要进一步验证和存疑之处： 真实世界任务的复杂性：论文中的任务虽然多样，但仍是标准化的学术基准。在更开放、更混乱的真实世界应用中（例如，一个需要与多个动态API交互的客服Agent），GEPA生成的提示是否依然鲁棒有效，需要更多验证。 优化器的超参数敏感性：GEPA本身引入了一些新的超参数，如评估minibatch的大小、帕累托集的大小等。论文中对这些参数的选择没有进行充分的消融研究，其对不同任务的最优配置可能是什么，仍是一个开放问题。 六、 核心启示与知识补充 对于希望从中汲取创新想法的您来说，这篇论文的启发是多方面的。\n重点学习与启发： 核心思想：超越标量奖励。最大的启发是，不要将复杂的AI行为简化为单一的数字分数。思考在你的任务中，除了最终得分，还有哪些“过程性”的、富含信息的“语言”可以被用作学习信号？这可能是代码、日志、用户评论、甚至是模型自己的“内心独白”。 方法论：反思式自我改进。将LLM用作一个“反思者”来诊断和改进另一个LLM（或其自身）是一个极其强大的范式。你可以思考如何将这个“反思循环”应用到你自己的工作中，无论是代码生成、文案写作还是数据分析。 优化哲学：拥抱多样性。帕累托优化的思想提醒我们，在复杂问题上，通常没有唯一的“最佳”解决方案。在进行A/B测试或方案选择时，可以考虑保留一组各有所长的“专家”方案，而不是过早地淘汰那些在平均表现上稍逊一筹但在特定场景下有奇效的方案。 需要补充的背景知识： 复合AI系统与智能体框架：了解一下DSPy（论文作者团队的另一个工作）、LangChain、AutoGen等框架，理解它们是如何将LLM、工具和内存等模块编排起来的。 强化学习基础：至少需要了解策略梯度（Policy Gradient）方法的基本思想，特别是PPO (Proximal Policy Optimization)，因为GRPO是其变种。这将帮助你深刻理解为什么RL方法样本效率低。 进化计算 (Evolutionary Computation)：了解遗传算法（Genetic Algorithm）的基本概念，如种群（population）、突变（mutation）、交叉（crossover）和选择（selection）。 多目标优化 (Multi-Objective Optimization)：核心是理解帕累托最优 (Pareto Optimality) 和帕累托前沿 (Pareto Frontier) 的概念，这是理解GEPA候选选择策略的关键。 总而言之，GEPA是一篇开创性的论文，它不仅提供了一个可以直接使用的强大工具，更重要的是，它为我们思考如何训练和优化AI系统提供了一个全新的、更接近人类学习方式的视角。\n","date":"2025-08-05T00:00:00Z","permalink":"/gepareflective-prompt-evolution-can-outperform-reinforcement-learning/","title":"GEPA：REFLECTIVE PROMPT EVOLUTION CAN OUTPERFORM REINFORCEMENT LEARNING"},{"content":"思想非常类似于WEB AGENTS WITH WORLD MODELS ：LEARNING AND LEVERAGING ENVIRONMENT DYNAMICS IN WEB NAVIGATION，并且还结合了PLAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks，区分了计划和行动。将真实的世界状态o抽象成自然语言状态s，并且分层规划，也就是将规划和行动分离。利用抽象的action来预测状态并且利用抽象的action得到真实的action。\nimage.png image.png （这张图是本文应用wm的核心，在planner，利用世界模型得到最佳的抽象动作。）\n这篇论文发表于2025年7月，来自MBZUAI、CMU、三星研究院等顶尖机构，提出了一种名为 SIMURA (Simulative Reasoning Architecture) 的新型AI智能体（Agent）架构。其核心思想在于，模仿人类在做复杂决策时“三思而后行”的“思考实验”过程，通过构建一个基于大语言模型（LLM）的“世界模型”（World Model），来模拟（simulate）不同行为可能带来的后果，并从中选择最优的行动方案。这与当前主流智能体依赖单一、线性的“自回归推理”（autoregressive reasoning）模式形成了鲜明对比，旨在解决当前LLM智能体在面对复杂任务时容易出现规划失败、重复无效操作和难以纠错等根本性问题。\n论文的精髓在于，它不满足于让LLM仅仅作为一个“思考者”或“执行者”，而是构建了一个包含感知（Encoder）、规划（Planner）和行动（Actor）的完整决策闭环。在这个环路中，规划模块是灵魂，它内部又包含了一个“微型决策系统”： 1. 策略模块 (Policy): 负责“出主意”，根据当前状态提出几种可能的高层意图（比如“点击价格最低的航班标签”）。 2. 世界模型 (World Model): 充当“预言家”，对每一种主意进行推演，预测执行后世界（例如网页界面）会变成什么样。这是SIMURA最具创新性的部分，它让智能体拥有了“想象未来”的能力。 3. 评估模块 (Critic): 扮演“裁判”的角色，评估每一种被预言的未来与最终目标的接近程度，给出评分。\n通过这个“提议-模拟-评估”的循环，SIMURA能够在真正采取行动前，就在其“脑海”中权衡利弊，选择一条成功率最高的路径。这种模拟推理（Simulative Reasoning）机制，赋予了智能体前所未有的规划深度和鲁棒性。\n论文的另一大亮点是其巧妙的设计哲学。首先，它采用自然语言作为离散的、基于概念的状态表示。不同于将整个网页或视频帧压缩成一个模糊的连续向量，SIMURA的编码器（Encoder）会将观察到的信息（如网页DOM树）提炼成一段关键的、结构化的自然语言描述（即信念状态 Belief State）。这样做的好处是显而易见的：它过滤了广告、布局变化等无关噪声，使得后续的规划和决策都建立在一个更稳定、更清晰、更接近人类认知的概念空间上，极大地降低了幻觉。\n其次，SIMURA采用了分层规划（Hierarchical Planning）的策略。它将高层、抽象的“模拟动作”（如“搜索从芝加哥到迪拜的机票”）与底层、具体的“执行动作”（如 click('128')）分离开来。这种分层设计使得智能体的规划能力可以跨任务、跨环境迁移，而不必纠结于每个应用琐碎的实现细节，大大提升了通用性和效率。\n为了验证这一架构的强大能力，研究者们在一系列复杂的真实网络浏览任务上进行了严苛的实验。实验结果令人印象深刻，充分证明了SIMURA的优越性。在一个专门构建的、极具挑战性的机票预订数据集 FlightQA 上： \u0026gt; Experiments on difficult web browsing tasks show that SIMURA improves the success of flight search from 0% to 32.2%. World-model-based planning, in particular, shows consistent advantage of up to 124% over autoregressive planning…\n这个数据极具说服力。基线智能体（OpenHands BrowsingAgent）在这个任务上完全失败（成功率为0%），而SIMURA达到了32.2%的成功率。更关键的是，论文进行了一项重要的消融实验：将SIMURA中的模拟规划模块换成传统的自回归规划，结果成功率骤降至14.4%。这有力地证明了成功率提升124%的功劳主要归功于模拟推理架构本身，而非仅仅是使用了更强的底层LLM。\n任务类型 基线 (OpenHands) SIMURA (自回归规划) SIMURA (世界模型规划) 提升幅度 复杂网站导航 (FlightQA) 0.0% 14.4% 32.2% +124% vs 自回归 多跳多网站问答 (FanOutQA) 17.0% 20.2% 29.8% +48.6% vs 自回归 通用Web自动化 (WebArena) 12.0% 19.0% 23.0% +21.1% vs 自回归 表格1：SIMURA在三大Web任务类型上的核心性能对比 (数据源: 原文 Table 1, 2, 3, Figure 6)\n综上所述，SIMURA论文不仅仅是提出了一种性能更好的智能体，更重要的是，它为构建通用、强大、可靠的AI智能体提供了一套全新的、更接近人类智慧的架构范式。它从根本上解决了自回归模型的短视问题，通过引入基于LLM的世界模型进行前瞻性模拟规划，为通往通用人工智能（AGI）的道路探索了一个极具潜力的方向。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 核心目标: 克服当前基于LLM的AI智能体在自回归推理（autoregressive reasoning）模式下的根本性缺陷。 具体目标: 提出并验证一种名为SIMURA的通用目标导向智能体架构，该架构通过模拟推理（simulative reasoning）来提升复杂任务的规划能力和鲁棒性。 解决的实际问题: 规划失败: 传统智能体（如基于ReAct框架的）采用线性思维，一步错，步步错，缺乏对未来的预判和规划修正能力。例如，在复杂的网站上预订机票，一旦点错一个筛选按钮，后续所有操作都可能白费。 低效和重复: 由于缺乏远见，智能体常常会陷入无意义的重复操作或在错误路径上反复试探，浪费大量时间与计算资源。 通用性差: 许多现有智能体是为特定任务（one-task-one-agent）设计的，难以迁移到新环境。例如，一个精通电商网站购物的智能体，可能完全无法处理航班查询或代码开发任务。 脆弱性: 智能体容易被网页广告、弹窗等无关信息干扰，导致决策错误。 行业意义: 迈向通用智能体的一大步: SIMURA展示了一条通往通用问题解决器（generalist problem-solvers）的可行路径。一个基于SIMURA的智能体，理论上可以适应从网页浏览、软件开发到科学研究等多种环境，这是实现真正个人AI助理或自动化工作流的关键。 提升AI的可靠性与实用性: 对于需要高可靠性的商业应用，如自动化客户服务、金融交易执行、供应链管理等，SIMURA的“三思而后行”机制能显著降低错误率，使其从“玩具”变为可靠的“工具”。 开辟新的AI架构范式: 它推动行业从简单的“输入-思考-输出”链式模式，转向更复杂的、包含内部世界模型和模拟能力的“认知”架构，这为下一代AI系统的设计提供了全新的思路。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新在于SIMURA架构本身，它相比之前的方法，具有以下鲜明特点和优势：\n核心思路：模拟推理 vs. 自回归推理 传统方法 (自回归): 大多数智能体遵循类似ReAct (Reason+Act)的模式，LLM根据当前状态生成一个“思考”，然后生成一个“行动”，执行后再进入下一个循环。这是一个线性的、单路径的决策过程。 SIMURA (模拟): SIMURA在行动前引入了一个内部规划循环。它不是直接选择一个行动，而是先构想出多个可能的行动，然后利用世界模型分别模拟这些行动的后果，最后通过评估器选出最优的那个。 \u0026gt; In practice, agents often samples promising action candidates using a policy function…Building the optimal agent thus requires capabilities for proposing possible actions (π), predicting their outcomes (µ), and evaluating goal progress (r, V), respectively. 关键组件：基于LLM的世界模型 (World Model) 特点: 论文的WM是一个基于LLM的生成模型，它的输入是当前的信念状态(ŝt)和一个模拟动作(a’)，输出是预测的下一个信念状态(ŝt+1)。 优势: 这赋予了智能体“想象力”。它可以在不与真实世界交互的情况下，探索不同决策路径的潜在结果。这是解决复杂规划问题的关键，因为穷举所有真实世界的可能性成本太高。 创新设计：离散的自然语言状态表示 特点: SIMURA的编码器（Encoder）并不直接处理原始的、充满噪声的网页数据，而是将其总结成一段简洁、准确的自然语言描述。 \u0026gt; …we propose to represent the world state ŝt using a discrete natural language summary ŝt generated by an encoder LLM h…Such a concept-based representation allows the other modules like policy to operate on a more structured latent space… 优势: 抗噪性: 过滤了广告、动态UI元素等无关信息，让决策更稳定。 通用性与可解释性: 自然语言是一种通用的表示方式，使得模型更容易在不同任务间迁移。同时，状态是人类可读的，极大方便了调试和分析。 概念抽象: 允许模型在更高的概念层面上进行推理（例如，识别出这是一个“航班搜索结果页”），而不是纠结于具体的HTML标签。 架构优势：分层规划 (Hierarchical Planning) 特点: 规划器在高层级的“意图”空间（如“探索网站”）进行模拟，而执行器（Actor）负责将最优意图翻译成底层的具体API调用（如 click('link_id')）。 优势: 提高效率: 在高层意图上进行模拟，可以一次性跨越多步底层操作，缩短了规划的“思考链”，减少了错误累积的可能。 增强通用性: 高层意图（如“提交表单”）在不同网站间是通用的，使得规划经验更容易迁移。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了三个递进的实验，全面评估了SIMURA在不同维度上的能力。\n实验设计: 三大任务场景: 复杂网站导航 (Complex Website Navigation): 使用自建的FlightQA数据集。这个任务要求智能体在真实的航班预订网站（如Google Flights）上，根据一系列复杂且动态变化的约束条件（如“往返”、“下个月出发”、“停留10天”、“非中途停留”）找到正确航班。这考验的是智能体在单个复杂动态界面上的深度交互和推理能力。 多跳多网站问答 (Multi-Hop, Multi-Website QA): 使用FanOutQA数据集。这类问题需要智能体首先在一个网站（如维基百科）找到一个实体列表（如“哈利波特系列所有书籍”），然后依次访问每个实体的相关页面，聚合信息后才能回答（如“所有书籍的出版日期”）。这考验的是广度导航、长程记忆和信息聚合能力。 通用Web自动化 (General Web Automation): 使用业界标准基准WebArena。它包含购物、论坛、代码管理等多种模拟网站，考验智能体的通用性和适应性。 对比基线 (Baselines): OpenHands BrowsingAgent: 一个代表性的、基于自回归推理的开源智能体。 SIMURA (Autoregressive Planning): 这是最重要的消融研究 (Ablation Study)。它使用SIMURA的整体架构，但将核心的“世界模型模拟规划”替换为简单的“自回归规划”（即直接采纳策略模块的第一个建议）。这个对比项能精准地衡量“模拟”这一思想本身带来的提升。 实验数据和结果: FlightQA 结果: SIMURA (世界模型规划) 取得了 32.2% 的正确率。 SIMURA (自回归规划) 只有 14.4%。 OpenHands BrowsingAgent 为 0%。 关键结论: 世界模型规划相比自回归规划，性能提升了 124% ((32.2-14.4)/14.4)。这清晰地表明了模拟推理的巨大优势。此外，在处理不断增加的约束条件时，世界模型规划的性能下降更平缓，展现了更强的鲁棒性（见原文Figure 9）。 FanOutQA 结果: SIMURA (世界模型规划) 准确率为 29.8%。 SIMURA (自回归规划) 为 20.2%。 关键结论: 在需要跨网站聚合信息的任务上，模拟规划依然能带来 48.6% 的显著性能提升。 WebArena 结果: SIMURA (世界模型规划) 成功率为 23.0%。 SIMURA (自回归规划) 为 19.0%。 OpenHands BrowsingAgent 为 12.0%。 关键结论: 即使在更通用的基准测试中，SIMURA的架构优势依然存在，相比强大的开源基线，性能提升了91.7%，其核心的模拟规划也比自回归规划高出21.1%。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 基于这篇论文，该研究方向未来充满机遇与挑战：\n值得探索的问题和挑战: 世界模型的训练与优化: 论文中的世界模型是零样本（zero-shot）的，完全依赖预训练LLM的内在知识。未来的一个关键方向是如何利用智能体的交互数据来微调（fine-tune）或专门训练世界模型。一个更懂特定环境（如PS软件、代码编辑器）动态变化的世界模型，将极大提升规划的准确性。 效率与延迟: 模拟规划虽然强大，但其“深思熟虑”的过程（多次调用LLM）带来了高昂的计算成本和时间延迟。如何通过并行化模拟、结果缓存、模型蒸馏等技术，在不牺牲太多性能的前提下，将决策延迟降低到用户可接受的范围，是其走向实用的关键。 多模态融合: 当前SIMURA是纯文本的。真正的通用智能体必须能够理解图像、视频和音频。如何将多模态信息有效地融入到信念状态表示、世界模型预测以及评估器中，是一个巨大的挑战。 长期记忆与持续学习: 如何让智能体拥有跨任务、跨会话的长期记忆，并能从过去的成功与失败中持续学习、进化，是构建“成长型”智能体的核心。 安全与对齐 (Safety \u0026amp; Alignment): 一个能力强大的通用智能体也带来了巨大的潜在风险。如何确保其行为始终与人类的价值观、意图和安全底线保持一致，是该领域最重要、也最困难的课题。 新的技术和投资机会: 智能体开发与调试平台: 随着智能体架构变得如SIMURA一样复杂，市场将迫切需要新一代的开发工具。这些工具不仅用于编写代码，更重要的是用于可视化、调试和评估智能体的“思考过程”，例如直观展示世界模型的模拟路径、评估器的打分等。 垂直领域专业智能体 (Specialized Agents): 在SIMURA这样的通用架构之上，可以为金融、医疗、法律、科研等专业领域训练专有世界模型，打造出具有专家级能力的自动化解决方案。这将是AI产业化的重要方向。 Agent-as-a-Service (AaaS): 提供强大的、可定制的智能体API服务。企业无需从头构建复杂的智能体，只需调用服务并定义目标，即可实现复杂的自动化任务。 AI原生硬件: SIMURA的模拟过程是高度并行的，这可能催生出专门为大规模、低延迟的智能体模拟而设计的AI芯片或硬件加速方案。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判的角度审视，仍存在一些局限性和待验证之处：\n成本与实用性的权衡: 论文的核心短板在于其高昂的计算成本和决策延迟。一次决策可能需要数十次LLM调用，这在许多实时应用中是不可接受的。论文虽然承认了这一点，但并未提供解决该问题的有效方案，这使得其当前距离广泛的实际部署还有很长的路要走。 世界模型泛化能力的真实边界: 实验虽然覆盖了三种Web任务，但本质上都局限于“网页浏览”这个大领域。其基于LLM的世界模型能否真正泛化到物理世界（如机器人控制）、软件开发（如理解代码库和API）、社交互动（如多智能体博弈）等差异巨大的环境中，尚需大量实验验证。LLM的常识可能不足以精确模拟这些领域的动态。 评估数据集的局限性: FlightQA数据集虽然设计精巧，但终究是程序化生成的，可能无法完全覆盖真实世界用户千奇百怪、模糊不清的查询意图。此外，在FanOutQA和WebArena上的评估仅使用了100个样本的子集，这可能会影响结论的普适性。 “黑盒”调试的难题: 尽管SIMURA的架构是模块化的，但其核心组件（Encoder, Policy, WM, Critic）都是基于LLM的“黑盒”。当智能体失败时，我们很难精确归因：究竟是世界模型预测错了？是评估器打分不准？还是策略模块没提出好的想法？这种端到端的不可解释性为系统的迭代优化带来了巨大挑战。 对LLM能力的过度依赖: SIMURA的成功高度依赖于底层LLM的强大能力（如GPT-4o）。如果底层LLM本身存在偏见或知识盲点，这些缺陷会被放大并传递到整个决策系统中。架构的鲁棒性在多大程度上能弥补底层模型的不足，还需要进一步验证。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是创新思想的宝库，您可以重点学习和借鉴以下几点：\n核心启发: 从“反应”到“规划”的思维转变: 最大的启发是，不要满足于让LLM做一个简单的“问答机”或“执行器”。在处理复杂任务时，引入模拟（Simulation）和前瞻（Lookahead）机制。即使不完全照搬SIMURA，也可以在您的应用中加入一个“验证步骤”或“多方案比较步骤”，让模型在输出最终答案前，先自我评估多个候选方案。 架构的力量 \u0026gt; 模型本身: 不要迷信于仅仅通过更换更强的LLM来提升性能。一个精巧的、解耦的系统架构（如SIMURA的感知-规划-行动分离）能让同一个LLM发挥出远超其单体作战的威力。将复杂任务分解为多个更小、更明确的子任务，让LLM分别扮演不同角色，是提升系统天花板的关键。 将自然语言作为“中间代码”: 在系统内部，使用结构化的自然语言来传递信息（如状态、计划），而不是依赖难以理解的向量。这不仅极大地增强了系统的可解释性和可调试性，也利用了LLM最擅长的能力，可以有效降低噪声和幻觉。 需要补充的背景知识: 强化学习 (Reinforcement Learning, RL): SIMURA的理论框架大量借鉴了RL。您需要理解策略（Policy）、价值函数（Value Function，对应Critic）、世界模型（World Model）、马尔可夫决策过程（MDP）等核心概念。阅读RL的经典教材，如Sutton和Barto的《Reinforcement Learning: An Introduction》，会对理解本文第3-4节的公式大有裨益。 规划与搜索算法 (Planning \u0026amp; Search Algorithms): 论文中提到了深度优先搜索（DFS）和蒙特卡洛树搜索（MCTS）。了解这些算法的工作原理，可以帮助您理解SIMURA的规划器是如何在众多模拟路径中进行探索和选择的。 主流AI智能体架构 (Agent Architectures): 了解ReAct (Reason+Act)框架是必不可少的，因为它是理解SIMURA创新的一个重要参照物。此外，也可以了解其他一些智能体工作，如Voyager等，以形成更全面的认知。 ","date":"2025-08-05T00:00:00Z","permalink":"/simuratowards-general-goal-oriented-agent-via-simulative-reasoning-architecture-with-llm-based-world-model/","title":"SIMURA：Towards General Goal-Oriented Agent via Simulative Reasoning Architecture with LLM-Based World Model"},{"content":"最近涌现了很多关于信用分配的论文，因此整理一下\nFirst Return, Entropy-Eliciting Explore Good Learners Think Their Thinking：Generative PRM Makes Large Reasoning Model More Efficient Math Learne Group Sequence Policy Optimization PROCESS REINFORCEMENT THROUGH IMPLICIT REWARDS RLVMR：Reinforcement Learning with Verifiable Meta-Reasoning Rewards for Robust Long-Horizon Agents VAPO：Efficient and Reliable Reinforcement Learning for Advanced Reasoning Tasks Group-in-Group Policy Optimization for LLM Agent Training CAPO：Towards Enhancing LLM Reasoning through Verifiable Generative Credit Assignment Beyond Policy Optimization：A Data Curation Flywheel for Sparse-Reward Long-Horizon Planning GTPO and GRPO-S：Token and Sequence-Level Reward Shaping with Policy Entropy GTPO：Trajectory-Based Policy Optimization in Large Language Models ","date":"2025-08-05T00:00:00Z","permalink":"/%E4%BF%A1%E7%94%A8%E5%88%86%E9%85%8D/","title":"信用分配"},{"content":"一言以蔽之就是对于一个问题找出n个正确回答的长度中位数，然后将这个数放到prompt提示大模型，进行GRPO的训练。两个阶段有不同的奖励。\nimage.png 首先，我将为您呈现对这篇论文的整体性深度解读。\n这篇论文的核心贡献在于提出了一种名为 长度自适应策略优化 (Length-Adaptive Policy Optimization, LAPO) 的新型两阶段强化学习框架，其目标是将对推理过程计算成本（即Token长度）的控制，从一种“外部强加的约束”转变为模型“内生的、自适应的能力”。当前的大模型，尤其是经过指令微调和思维链（Chain-of-Thought, CoT）训练的模型，虽然在复杂推理任务上表现出色，但往往会陷入一个困境：即使用于解决简单问题，也会生成冗长、繁琐的推理步骤。这种“杀鸡用牛刀”的模式，论文中称之为“过度思考”，直接导致了高昂的推理成本和延迟，极大地阻碍了这些强大模型的规模化部署。\n以往的解决方案各有弊端。例如，直接在奖励函数中加入长度惩罚，可能会导致模型“因噎废食”，过度缩短推理链，损害准确性；而动态提前终止策略，则可能粗暴地打断模型的“思考过程”，使其无法得出正确结论。LAPO的作者们认为，这些方法的根本局限在于它们都将长度控制视为一个外在于模型认知过程的“缰绳”。\nLAPO的精妙之处在于它的范式转移 (paradigm shift)。它认为，对于一个特定问题，其内在的复杂度天然决定了解决它所需要的“思考深度”。当模型成功解决问题时，其输出的答案长度本身就蕴含了关于这种“思考深度”的宝贵信息。因此，我们不应该强行限制模型，而应该引导模型去发现和学习这些规律，并将其内化。 为此，LAPO设计了一个两阶段的训练流程，这在论文的图2中有非常清晰的展示：\n第一阶段：发现阶段 (Discovery Stage)。此阶段的目标是让模型“学习”什么是“恰当的思考长度”。具体来说，模型会针对一批问题生成多个候选答案（论文中称为rollouts）。通过一个特别设计的、同时奖励准确性和效率的复合奖励函数R1，模型被鼓励在保证正确的前提下，探索更简洁的推理路径。框架会收集所有正确答案的长度，并从中提取统计特征，尤其是长度的中位数 (Median)。这个中位数被视为该问题“典型”的、最高效的推理长度。通过这种方式，框架为每个问题建立了一个从问题到理想长度的映射 M(q) = Lmedian(q)。这个阶段的本质，是让模型通过自身的成功经验，建立起一个关于“问题难度-推理深度”的初步认知。\n第二阶段：内化阶段 (Internalization Stage)。这是LAPO最具创新性的部分。在此阶段，框架会将第一阶段学到的“理想长度”M(q)，作为一种“元认知指导”信息，直接嵌入到模型的输入提示（prompt）中。具体形式如下： \u0026gt; I will answer the question with n tokens. 其中 n 就是从映射 M 中查到的理想长度。这一步至关重要，它将一个外部的统计数据，转化为了模型“自己”的计划声明。模型不再是被动地被约束，而是主动地“宣称”它将用多长的篇幅来解决问题。随后，模型会再次进行强化学习训练，但这次的奖励函数R2的核心是奖励模型遵守自己声明的计划。也就是说，如果模型最终的输出长度与它自己声明的 n 越接近，且答案正确，它获得的回报就越高。通过这种方式，模型被激励去学习如何精准地规划并执行其计算资源分配，从而将这种能力真正地“内化”。\n论文的实验结果极为亮眼，充分验证了LAPO的有效性。例如，在以冗长输出著称的DeepScaleR-1.5B-Preview模型上应用LAPO后，其性能表现令人印象深刻： \u0026gt; When applied to DeepScaleR-1.5B-Preview, LAPO-I achieves a remarkable 37.8% token reduction while boosting the average accuracy by 2.3 percentage points.\n具体数据如表1所示，在四个数学基准测试的平均表现上，Token使用量从6229锐减至3832，而Pass@1准确率却从62.5%提升至64.8%。这实现了“又快又好”的理想效果，在性能图（图1右）上，LAPO模型稳稳地占据了左上角的“帕累托最优”区域，即在所有方法中，它用最少的Token达到了最高的准确率。\n更深层次的分析（图4）揭示了LAPO是如何做到这一点的。它并非简单地删减内容，而是选择性地修剪了犹豫和探索性的思维模式（如 “Wait”, “Alternatively” 等词的频率显著下降），同时保留了解决问题所必需的核心逻辑框架（如 “Given”, “Therefore” 等词的频率保持稳定）。这表明模型学会了更“自信”、更“直接”地思考，这正是其效率提升和准确性不降反升的根本原因。\n总而言之，LAPO通过一个精巧的“发现-内化”两阶段框架，成功地将推理效率从外部约束转变为模型的内在能力，为解决LLM的“过度思考”问题提供了一个极具前景和通用性的范式。\n接下来，我将根据您提出的六个问题，进行更详细的解读。\n一、论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 论文的核心研究目标是让大型语言模型能够根据问题的内在复杂性，自主、动态地调整其推理过程的计算资源投入（即生成的Token数量），从而实现高效推理。它希望将这种效率控制能力，从一种外部施加的、僵化的规则，转变为模型自身固有的一种“元认知”能力。\n解决的实际问题： 论文主要解决的是大型语言模型在实际应用中的“过度思考”（Overthinking）问题。具体表现为，即使面对简单的问题，许多强大的推理模型也会生成非常冗长的、不必要的思维链条。这直接导致了两个严峻的实际问题：\n高昂的推理成本：在商业应用中，模型推理是按Token数量计费的。不必要的Token生成会急剧增加API调用成本和服务器GPU运算开销。 较长的响应延迟：生成更多的Token意味着需要更长的计算时间，这会导致用户等待时间变长，严重影响实时交互体验（如在线客服、实时编程助手等）。 行业发展意义： 解决“过度思考”问题对于整个大模型行业的发展具有至关重要的战略意义：\n推动大模型规模化落地：降低推理成本和延迟是决定大模型能否从“玩具”走向“工具”，并被广泛集成到各类应用中的关键。高效推理能显著降低企业的运营成本，使AI应用更具经济可行性。 提升用户体验：更快、更精准的响应是所有AI产品的核心追求。一个“思考”得恰到好处的模型，能够提供更流畅、更令人满意的交互体验。 促进AI普惠化：更高效的模型意味着在同等硬件条件下可以服务更多的用户，或者可以在更廉价的硬件上运行，这有助于将先进的AI能力推广到更多资源有限的场景中。 解锁新的应用场景：对于延迟极其敏感的领域（如实时决策系统、游戏AI、高频代码生成等），只有解决了效率问题，大模型才能真正发挥作用。 二、论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出的核心方法是长度自适应策略优化 (LAPO) 框架。其最大的创新思路在于将长度控制从“外部约束”转变为“内部规划”。\n与之前的方法相比，LAPO的特点和优势体现在以下几个方面：\n两阶段学习范式（发现-内化）： 之前的方法：大多是“一步到位”的。比如，直接在奖励函数里加入一个固定的长度惩罚项，或者训练一个外部路由器模型来分发任务。 LAPO的特点：采用“先学习、后内化”的两步走策略。 发现阶段：不预设任何固定的长度目标，而是通过强化学习探索，让模型从自身的成功经验中“发现”不同问题的“自然”推理长度。这一步非常关键，因为它承认了“最佳长度”是一个需要根据问题动态变化的未知量。 内化阶段：将发现的规律通过上下文学习 (In-Context Learning) 的方式，转化为模型自己的“计划”。通过奖励模型遵守自己的计划，LAPO引导模型真正理解并吸收这种效率意识。 优势：这种循序渐进的方式更符合认知学习的规律，使得模型学到的不是僵硬的规则，而是一种可泛化的、灵活的资源分配能力。 创新的上下文引导机制： 之前的方法：通常通过修改奖励函数或模型架构来控制长度，这些对模型来说是“隐式”的信号。 LAPO的特点：在第二阶段，它直接修改了模型的输入（prompt），加入了一句明确的声明：I will answer the question with n tokens.。 \u0026gt; By placing the length guidance “I will answer the question with n tokens.” immediately after the \u0026lt;think\u0026gt; token, we transform an external constraint into an intrinsic part of the model’s cognitive process. 优势：这是一种非常巧妙的“元指令”。它利用了LLM强大的上下文理解能力，将一个优化目标（长度控制）直接变成了任务描述的一部分。这使得模型能够将“如何高效思考”和“思考什么内容”结合起来，从而实现更深层次的“内化”。 数据驱动的、动态的奖励机制： 之前的方法：长度惩罚通常是固定的，或者基于一个简单的函数，缺乏对问题复杂度的感知。 LAPO的特点：LAPO的奖励是高度数据驱动和动态的。 第一阶段的长度奖励范围 [Lmin, Lmax] 是从实际生成的正确答案中统计出来的（取30%到70%分位点），这比硬编码的阈值要灵活得多。 第二阶段的奖励则与模型自己声明的 n 挂钩，这是一个动态变化的目标。 优势：这种设计使得优化目标能自适应于不同的问题和模型在训练过程中的能力演进，从而找到更优的“准确率-效率”平衡点。论文在表3的消融实验中也证明了使用中位数 (Median) 作为目标长度，比使用均值 (Mean) 或最小值 (Minimum) 效果更好，因为它对异常长的成功解答有更强的鲁棒性，更能代表“典型”的推理深度。 三、论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列设计严谨的实验来验证LAPO的有效性，主要包括主实验、消融研究和定性分析。\n实验设计： 基准测试 (Benchmarks)：选用了四个具有不同难度的数学推理基准：MATH-500, AIME2024, AMC23, 和 Olympiad-Bench。这覆盖了从中等到极高难度的问题，能有效检验模型的泛化能力和在不同复杂度下的表现。 基础模型 (Base Models)：选用了两个有代表性的模型：DeepSeek-R1-1.5B（一个强大的指令微调模型）和DeepScaleR-1.5B-Preview（一个以高质量但冗长输出闻名的RL微调模型），以验证LAPO在不同起点模型上的通用效果。 对比基线 (Baselines)： 基础模型本身：即不加任何优化的原始模型。 现有高效推理方法：包括 L1-Exact 和 L1-Max（直接控制长度的方法）以及 ThinkPrune（迭代剪枝方法）。这些是LAPO的直接竞争对手。 评估指标：主要使用两个指标：Pass@1 准确率（衡量推理质量）和平均生成Token数（衡量效率）。 实验数据和结果： 实验结果有力地证明了LAPO的优越性。 主实验结果（表1）：LAPO在所有测试中都取得了SOTA（State-of-the-art）级别的“效率-准确率”权衡。 关键数据：在 DeepScaleR-1.5B-Preview 模型上，LAPO-I（经过两阶段训练的模型）的平均准确率为 64.8%，平均Token数为 3832。相比之下，基础模型准确率为 62.5%，Token数为 6229。这意味着LAPO在将Token数减少37.8% 的同时，还将准确率提升了2.3个百分点。 与基线对比，ThinkPrune-4k 虽然准确率略高（63.5%），但Token数也更多（4094）。而L1-Exact等方法虽然Token数更少（2278），但准确率也大幅下降（56.2%）。LAPO找到了最佳的平衡点。 消融研究（表2和表3）：这些实验验证了LAPO关键设计的必要性。 长度指导形式（表2）：实验证明，提供精确的目标长度（w/ Exact）比提供一个范围（w/ Range）或完全不提供（w/ Implicit）的效果要好得多。这直接证明了第二阶段中“上下文引导”的有效性。 统计指标选择（表3）：实验表明，使用中位数（w/ Median） 作为目标长度，其综合表现（准确率64.8%，Token数3832）优于使用均值（64.1%，4040）和最小值（63.5%，3821）。 定性与机制分析（图3和图4）： 难度感知分配（图3）：展示了LAPO训练后的模型，其生成的推理长度与问题的难度（从MATH Level 1到AIME 2024）呈现出近线性的正相关关系。这表明LAPO确实学会了根据问题难度来分配计算资源。 推理行为提炼（图4）：通过关键词频率分析，发现LAPO显著减少了与“自我修正与验证”（如Wait, But）和“探索与替代方案”（如Alternatively）相关的词汇，而保留了“上下文设定”和“结论推导”的核心词汇。这揭示了其效率提升的来源——剪除冗余的思考过程，而非核心逻辑。 四、结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为“模型自我调节”和“高效推理”开辟了新的道路，在此基础上，未来有许多值得探索的方向和挑战：\n更复杂的推理类型： 挑战：LAPO在数学推理这类有明确“正确答案”的任务上表现优异。但对于开放式生成、创意写作、代码生成、长篇故事续写等任务，“最佳长度”和“正确性”的定义非常模糊。 探索方向：如何设计新的奖励函数来评估这类任务的“效率”和“质量”？也许可以引入基于人类偏好（RLHF）的奖励模型，或者基于代码编译/测试通过率的奖励。 动态在线调整能力： 挑战：LAPO是在推理前，根据问题给出一个固定的长度预算。但模型在实际推理中，可能会“意识”到问题比预想的更难或更简单。 探索方向：能否让模型具备动态调整预算的能力？例如，在推理中途生成一个特殊的Token来请求“增加算力”或“提前结束”。这需要更复杂的模型架构和训练策略，类似于人类的“即时反思”能力。 跨模态的效率控制： 挑战：目前的研究集中在文本领域。对于多模态大模型（如视觉问答、视频理解），“推理长度”的概念需要重新定义，可能涉及到需要关注的图像区域数量、视频帧数、或者生成的文本/语音长度。 探索方向：如何将LAPO的思想扩展到多模态任务中？例如，在第一阶段发现解决某个视觉问题通常需要关注多少个“关键对象”，然后在第二阶段引导模型去主动规划其“注意力分配”。 与模型架构的结合： 挑战：LAPO是一种训练策略，独立于模型架构。但某些架构（如Mixture-of-Experts, MoE）本身就具备动态分配计算资源的能力。 探索方向：如何将LAPO这样的训练策略与MoE等动态架构深度结合？能否训练MoE中的路由器，使其不仅考虑内容匹配度，也考虑LAPO学到的“效率先验”，从而做出更全局最优的专家选择。 催生的技术和投资机会： 高效推理平台：提供一站式服务，帮助企业将他们自己的大模型通过类LAPO技术进行“效率优化”，使其更具商业竞争力。 “成本感知”的AI Agent：训练出的Agent不仅能完成任务，还能在指定的成本预算下完成任务，这在金融、自动化等领域有巨大价值。 新一代模型压缩与加速技术：不再是通用的剪枝或量化，而是与模型认知过程相结合的、任务自适应的压缩技术。 AI原生应用的开发框架：提供能够让开发者轻松设定和控制AI模型“思考深度”和“成本预算”的API，这将是构建下一代智能应用的关键基础设施。 五、退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判和审慎的角度看，仍存在一些潜在的不足和待验证之处：\n泛化领域的局限性： 不足：论文的实验完全集中在数学推理这个单一领域。虽然数学题目的难度分级清晰，便于验证，但这并不能完全保证LAPO在其他领域（如法律文本分析、生物医学研究、常识问答、情感对话等）也能取得同样好的效果。这些领域的“最优推理模式”可能与数学截然不同。 待验证：需要在更多样化的任务领域上进行实验，以检验LAPO框架的通用性。 框架的复杂性与训练成本： 不足：LAPO是一个包含两个强化学习阶段的复杂框架，需要大量的计算资源进行rollouts和策略更新。这对于资源有限的学术研究者或中小型企业来说，可能是一个不低的门槛。 待验证：论文没有详细讨论训练该框架所需的总计算开销，以及它与简单的单阶段RLHF相比的成本差异。其训练效率本身是否够高，是一个潜在问题。 对“中位数”启发式规则的依赖： 不足：虽然实验证明中位数是一个鲁棒的统计量，但它仍然是一个启发式规则。对于某些特定的、需要非典型性思考（极短的顿悟或极长的探索）才能解决的问题，强制模型向中位数靠拢，可能会压制其创造性或探索能力，从而错过最优解。 待验证：是否存在一类问题，其最优解的长度分布是双峰或长尾的，而LAPO在这种情况下会表现不佳？需要更细致的错误分析来探究这一点。 超参数的敏感性分析缺失： 不足：LAPO引入了多个新的超参数，如 α 和 β（分别控制两阶段中长度奖励的权重）、σ（第二阶段奖励函数的高斯分布标准差）、以及百分位数的选择（P30, P70）。论文给出了所选用的值，但缺乏对这些超参数的敏感性分析。 待验证：模型的最终性能在多大程度上依赖于这些超参数的精确调优？如果这些参数非常敏感，那么在新的任务或模型上应用LAPO时，调参过程可能会非常繁琐。 六、我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 当然，这篇论文充满了可以借鉴的创新思想。以下是您可以重点学习和吸收的启发：\n核心启发：从“外部控制”到“内部引导”的范式转变。 学什么：在设计AI系统时，与其用生硬的规则去限制模型的行为，不如思考如何设计一个环境或一套机制，让模型通过“自我探索”和“自我反思”来学会期望的行为。 怎么用：在你的项目中，如果需要模型具备某种能力（如安全性、遵循特定格式、代码风格等），可以尝试设计一个类似LAPO的流程：首先让模型在较为宽松的环境中生成多样化的输出，然后识别出其中“好的”输出的共同模式，最后再将这些模式作为“指导”或“榜样”，让模型在下一阶段学习模仿。 具体技术：利用“上下文学习”进行元认知引导。 学什么：将模型的行为控制指令（元指令）直接编码到prompt中，是一种非常轻量级且高效的控制方法。I will answer the question with n tokens. 是一个绝佳的例子。 怎么用：你可以将此思想应用到各种控制任务上。例如： 控制语气：...\u0026lt;think\u0026gt;I will answer in a formal and professional tone.\u0026lt;/think\u0026gt;... 控制格式：...\u0026lt;think\u0026gt;I will provide the answer as a JSON object with keys 'name' and 'age'.\u0026lt;/think\u0026gt;... 控制步骤：...\u0026lt;think\u0026gt;I will first analyze the user's need, then propose three solutions, and finally ask for feedback.\u0026lt;/think\u0026gt;... 然后设计相应的奖励函数来激励模型遵守自己的“声明”。 方法论：数据驱动的奖励函数设计。 学什么：不要凭空设计奖励函数。LAPO的奖励函数是基于对模型自身行为的统计分析得出的。这让奖励信号更具信息量和有效性。 怎么用：在你的RLHF项目中，除了简单的人类偏好打分，可以尝试分析高分回答的共性特征（如长度、关键词使用、逻辑结构等），并将这些特征量化后，融入到奖励函数中，为模型提供更丰富的学习信号。 补充背景知识： 要完全理解并应用这篇论文的思想，建议您补充了解以下背景知识： 强化学习 (Reinforcement Learning)：特别是策略梯度 (Policy Gradient) 方法，如PPO (Proximal Policy Optimization)。论文中使用的GRPO (Group Relative Policy Optimization) 是其变体，了解PPO的基本原理将非常有帮助。 思维链 (Chain-of-Thought, CoT)：理解CoT的原理，以及它为什么能提升模型推理能力，同时又会带来“过度思考”的问题。 大语言模型微调 (LLM Fine-tuning)：了解SFT（Supervised Fine-tuning）和RLHF（Reinforcement Learning from Human Feedback）的基本流程和区别，LAPO可以看作是一种高度自动化的、特定目标的RLHF。 元学习 (Meta-Learning)：虽然论文没有直接提，但LAPO“学会如何学习”的思想与元学习有异曲同工之妙，了解元学习的概念有助于从更高维度理解这篇论文的贡献。 ","date":"2025-08-04T00:00:00Z","permalink":"/lapointernalizing-reasoning-efficiency-via-length-adaptive-policy-optimization/","title":"LAPO：INTERNALIZING REASONING EFFICIENCY VIA LENGTH-ADAPTIVE POLICY OPTIMIZATION"},{"content":"一篇关于PRM的方法（最近prm有点复兴的趋势）\n论文深度解读：从“结果导向”到“过程学习”的范式革命 在大型语言模型（LLM） стремительно (qián suǒ wèi yǒu)发展的今天，我们已经见证了它们在语言理解和生成方面的惊人能力。然而，要让模型真正具备强大的逻辑推理能力，尤其是在数学、编程等需要严谨步骤的领域，仅仅依赖于预测下一个词的预训练目标是远远不够的。因此，强化学习（Reinforcement Learning, RL），特别是基于结果反馈的RL方法，成为了提升模型推理能力的主流技术路径。\n这条路径的核心思想很简单：让模型生成解题过程和答案，如果最终答案正确，就给予正向奖励；如果错误，就给予负向奖励。这种模式被称为结果导向的奖励（Outcome-only Rewards）。然而，这篇论文敏锐地指出了这种模式的根本性缺陷：反馈信号极其稀疏且低效。一个模型可能经过了九步完美无瑕的推理，仅在最后一步计算时犯了个小错，但它得到的惩罚却和一个胡言乱语的模型完全相同。这种“一刀切”的奖励机制，浪费了大量有价值的中间过程信息，导致模型训练效率低下，就像一个学生做了大量的练习题，但老师只告诉他对错，从不讲解题过程中的具体问题，其学习效率可想而知。\n为了解决这个问题，学术界提出了过程奖励模型（Process Reward Models, PRMs） 的概念，即对模型生成的推理过程中的每一步进行评估和打分。但传统PRMs也面临着自身的困境，论文中总结了几个关键痛点： 1. 评估单元切分模糊（Ambiguous Step Segmentation）：如何将一长串推理过程切分成有意义的“步骤”？切分得太细（比如每个词或每行），评估成本高且容易出错；切分得太粗，又会失去过程监督的意义。 2. 对评估模型要求过高（Over-reliance on Reasoning Strength）：传统的PRM，无论是判别式还是生成式的，通常需要其自身就具备强大的解题能力，才能准确判断一个推理步骤的对错。这几乎陷入了一个“鸡生蛋还是蛋生鸡”的悖论：为了训练一个强大的推理模型，我需要一个同样强大的模型来当老师。 3. 奖励作弊（Reward Hacking）：模型可能会学会生成一些容易获得过程奖励但对最终解题没有帮助的“套话”，从而“欺骗”奖励系统。\n这篇论文的卓越之处，在于它直面这些挑战，并提出了一套完整且充满巧思的解决方案，其核心是全新的生成式过程奖励模型（GenPRM）范式。这不仅是一个模型的改进，更是一种训练思想的升华，我将其概括为三大创新支柱： 支柱一：思想单元（Thought-level）评估——从“步”到“思”的粒度升维\n论文不再纠结于如何定义一个完美的“步骤”，而是提出了一个更符合认知逻辑的单元——“思想（thought）”。一个“思想”被定义为连续的正确或错误的推理步骤的集合。如下图1所示，模型将连续的正确或错误步骤合并，形成一个个逻辑上连贯的“思想块”。\nBy merging the step-level evaluation results, we further obtain thought-level process evaluation. The implementation is conceptually simple: for correct solutions, consecutive correct or incorrect reasoning steps are merged into correct or incorrect units, named as ‘thoughts’, respectively…\n这种做法的好处是显而易见的：它降低了对步骤切分精确度的敏感性，使得奖励信号更加稳定和鲁棒。奖励不再是零散地给到每一步，而是给到整个“思想块”的末尾，这让信用分配（Credit Assignment）更加准确。\n支柱二：内在信号驱动（Intrinsic Signal-driven）的评估——让模型“自我反思”\n这是我认为本文最精彩的创新点。它极大地降低了对评估模型（即GenPRM）自身推理能力的要求。其核心思想是，评估模型不需要自己会做题，而只需要能“读懂”学生（被训练的模型）的解题思路。具体做法区分了两种情况：\n对于最终答案正确的解题过程：评估模型被要求去识别学生自己反思和修正错误的信号。比如，当学生在解题过程中写下“等等，我可能犯了个错误”或“让我重新检查一下”时，这些就是强烈的内在信号。评估模型的工作就是定位这些“反思点”以及它们所修正的“错误源头”。 对于最终答案错误的解题过程：评估模型通过对比\u0026lt;think\u0026gt;（思考过程）和\u0026lt;answer\u0026gt;（最终答案）部分的内容，来判断哪些思考步骤被采纳并导致了错误。被采纳的步骤被标记为“有问题的”，而那些未被采纳的探索性步骤则不被惩罚。 这个设计堪称神来之笔。它将评估任务从一个困难的“推理任务”转化为了一个相对简单的“理解和匹配任务”，大大降低了构建PRM的门槛。\n支柱三：能力自适应（Capability-adaptive）的奖励机制——动态平衡探索与利用\n在强化学习中，探索（Exploration）与利用（Exploitation）的平衡是一个永恒的难题。论文设计了一个巧妙的奖励机制来动态调整。对于正确的解题过程中的“正确思想块”，其奖励大小由以下公式决定： \\[ r^{correct} = \\alpha / \\text{acc\\_mean} \\] 其中，\\(\\alpha\\)是超参数，而 acc_mean 是当前模型对于该问题的平均正确率。这个公式的精妙之处在于： * 当模型对某个问题很不擅长时（acc_mean很低），\\(r^{correct}\\)会变得很大。这会给予模型很强的正反馈，鼓励它更多地学习和探索正确的路径。 * 当模型已经很擅长某个问题时（acc_mean很高），\\(r^{correct}\\)会变得很小。此时，过程奖励的影响减弱，模型会更专注于最终结果的正确性，避免因过度追求过程奖励而产生“奖励作弊”。\n这套组合拳被整合进了一个新的强化学习算法——TP-GRPO (Thought-level Process-reward-based GRPO)。实验结果令人印象深刻：在仅用少量训练样本的情况下，TP-GRPO在多个数学基准测试上显著超越了仅使用结果奖励的基线方法。例如，在AIME 2024测试中，1.5B模型仅用1800个训练样本就实现了+5.98%的性能提升，而7B模型仅用1070个样本就实现了+6.67%的提升。论文还为此设计了一个新的效率指标Effic.，TP-GRPO在该指标上以36.43的得分，碾压性地超过了其他所有基线方法（大多低于1.0）。\n总结来说，这篇论文不仅仅是提出了一个新算法，更是倡导了一种新的、更高效、更符合认知科学的LLM训练哲学：让模型像一个优秀的学生一样，不仅知其然（知道答案对错），更知其所以然（反思自己的思考过程）。通过“思想单元”评估、“内在信号”驱动和“能力自适应”奖励这三大创新，它为解决强化学习在LLM推理任务中的效率瓶颈问题，提供了一条极具潜力的路径。\n接下来，我将按照您提出的六个问题，逐一进行更详细的解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升大型推理模型（LRMs）在解决复杂数学问题时的训练效率和性能。 解决的实际问题：它旨在解决当前主流强化学习方法中“结果导向奖励”（Outcome-only Reward）机制带来的两大问题： 训练效率低下：如前所述，最终答案对错的二元信号过于稀疏，无法有效利用模型在推理过程中产生的宝贵信息，导致模型需要海量的训练数据才能学会推理。 信用分配不准：无法区分是哪个环节的错误导致了最终的失败，使得模型的优化过程像“盲人摸象”，难以进行针对性的改进。 行业意义：这个问题意义重大。 降低成本，加速创新：当前训练顶尖大模型（尤其是具备强大推理能力的模型）的成本极其高昂，动辄需要数月甚至更长的训练时间和天量的计算资源。如果能像本文一样，用更少的数据达到甚至超越原有的性能，将极大地降低AI研究和应用的门TA（门槛），让更多机构有能力训练出强大的专用模型，从而加速整个行业的创新迭代。 推动AI走向更高级的推理智能：数学推理被认为是检验机器智能水平的“试金石”。攻克了数学推理，意味着模型在逻辑、规划、符号操作等方面的能力得到了质的提升。这不仅对数理科学有益，其核心能力还可以迁移到编程、法律、金融、医疗诊断等同样需要严谨多步推理的领域，是通往更通用人工智能（AGI）的关键一步。 增强模型的可解释性和可靠性：通过关注和奖励“好的思考过程”，我们不仅能得到正确的答案，还能得到一个逻辑清晰、可被理解的解题路径。这对于需要高可靠性和可解释性的应用场景（如自动驾驶、医疗AI）至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出了一套环环相扣的新方法，其核心是TP-GRPO (Thought-level Process-reward-based GRPO) 框架，它建立在一种全新的生成式过程奖励模型（GenPRM）之上。\n与之前方法的对比如下表所示：\n特性 传统方法 (Outcome-only RL \u0026amp; PRM) 本文方法 (TP-GRPO \u0026amp; Novel GenPRM) 优势分析 奖励单元 整个解题过程 或 细粒度的步骤 思想单元 (Thought) 更鲁棒：避免了步骤切分的模糊性，奖励信号更稳定。 评估机制 依赖外部标签或需要PRM自身具备强大推理能力 内在信号驱动 门槛更低：评估模型只需具备“理解能力”而非“推理能力”，更容易构建和泛化。 奖励策略 固定的过程奖励或无过程奖励 能力自适应奖励 更智能：动态平衡探索与利用，在模型能力不同阶段侧重不同目标，训练更高效。 核心算法 PPO, GRPO TP-GRPO 更精细：专门为结构化的“思想单元”奖励设计，能更好地利用过程信息。 细节分析：\n思想单元的提出：彻底跳出了“如何切分步骤”的泥潭。论文在图1中清晰地展示了，无论是正确的解题中的.../?（错误）和.../（正确）步骤，还是错误解题中的matched和unmatched步骤，都会被合并成更大的Thought块。这是一种化繁为简的智慧。\n内在信号驱动的精妙设计： \u0026gt; For correct solutions…prompt the PRM to identify the specific step where an error is recognized through reflection… For incorrect solutions…we compare each step in the think with the answer and identify matched think steps.\n这个设计是方法论上的核心突破。它利用了大型模型在生成长文本时自然产生的元认知（Metacognition）信号，例如自我怀疑和修正。这使得奖励模型的构建不再依赖于昂贵的人工标注或一个无所不知的“超级教师模型”。\n能力自adaptive奖励的有效性：如前所述，\\(r^{correct} = \\alpha / \\text{acc\\_mean}\\) 这个公式在模型的不同学习阶段扮演不同角色。在学习初期，它是一个积极的“鼓励者”；在学习后期，它变成一个冷静的“把关人”，确保模型不忘初心，始终以解决问题为最终目标。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了全面且有说服力的实验来验证其方法的有效性。\n实验设计： 基准模型：选取了DeepSeek-R1-Distill-Qwen的1.5B和7B版本作为基础模型。 对比基线： 核心对比：作者自己复现的、仅使用结果奖励的GRPO算法。这是最公平的对比，控制了除奖励机制外的所有变量。 广泛对比：与其他多个开源的、同样在数学推理上进行强化学习的SOTA模型进行比较，如AdaptThink, AReaL, Skywork-OR1等。 评估数据集：使用了5个广泛认可的数学推理基准测试集：AIME 24, AIME 25, AMC 23, MATH-500, 和 Olympiad。 评估指标： 性能：使用Pass@k（本文主要是Avg@16和Avg@5）来衡量模型的准确率。 效率：除了比较达到同等性能所需的训练步数，论文还创新性地提出了Effic.指标，综合了性能提升和训练样本数量，用于量化训练效率。 关键数据和结果： 性能和效率双优：\n1.5B模型 (Table 1)：TP-GRPO使用140个训练步（约700个问题）达到了50.10的平均分，而GRPO Replication需要850个训练步（约4250个问题）才能达到49.64分。TP-GRPO以约1/6的训练量超越了基线。 7B模型 (Table 2)：TP-GRPO使用214个训练步（约1070个问题）达到了67.23的平均分，而GRPO Replication需要400个训练步（约2000个问题）才能达到65.34分。TP-GRPO以约1/2的训练量显著超越了基线。 学习曲线陡峭：\nFigure 3直观地展示了在AIME 2024任务上，TP-GRPO的学习曲线（紫色）的斜率明显高于其他方法，意味着它能更快地提升性能，收敛速度更快。 图3：TP-GRPO在AIME 2024上的学习曲线，显示出更快的收敛速度\n效率指标碾压：\nTable 3显示，TP-GRPO的Effic.值（1.5B模型为36.43，7B模型为40.07）远超所有其他方法，包括GRPO Replication（分别为4.65和9.6），这强有力地证明了其在训练效率上的巨大优势。 鲁棒性验证（Ablation Study）：\nTable 5的消融实验显示，即使使用性能较弱的LLM（如Qwen3-4B, Gemma-3-12b-it）作为过程评估器（GenPRM），TP-GRPO依然能稳定提升基础模型的性能。这证明了其“内在信号驱动”设计的鲁棒性和低依赖性。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这个研究方向充满了机遇和挑战。\n值得探索的问题和挑战： 大规模扩展性（Scalability）：论文在中小尺寸模型上验证了方法的有效性。当模型规模和数据规模扩展到千亿级别（如GPT-4、Claude 3）时，这套方法的优势是否依然存在？是否会出现新的挑战？这是一个亟待验证的问题。 跨领域泛化（Generalization）：该方法在数学推理上表现出色，但它能否无缝迁移到其他需要复杂推理的领域，如法律文书分析、科学文献发现、大规模软件工程？每个领域可能都有其独特的“内在信号”，需要对方法进行适配和调整。 PRM自身的演进：论文也承认，第一阶段的步骤评估仍是瓶颈。未来可以研究如何微调（fine-tune）GenPRM使其更懂“套路”，或者设计更鲁棒的提示工程，甚至将PRM与模型的知识图谱相结合。 探索与利用的动态策略：论文结尾提出了一个非常有前景的想法——交替使用过程奖励和结果奖励。例如，先用结果奖励进行广泛探索，然后用过程奖励进行深度“反思”和巩固。设计最优的动态切换策略将是一个有趣的研究方向。 催生的新技术和投资机会： 高效推理模型即服务（Efficient Reasoning-Model-as-a-Service）：可以催生一批专注于提供高效、低成本、但在特定推理领域（如金融分析、药物研发）能力超强的专用大模型服务的公司。 下一代RLHF/RLAIF平台：目前市场上的RLHF平台大多还停留在简单的偏好排序上。可以开发集成类似TP-GRPO这种先进过程监督能力的“智能RLAIF平台”，为模型开发者提供更强大、更自动化的训练工具链。这将是AI基础设施层的一个重要投资机会。 AI辅助学习与调试工具：这套“内在信号驱动”的评估思想，可以被用来开发“AI代码调试器”或“AI逻辑分析器”，它们能理解并指出AI或其他程序员在复杂任务中的思维盲点和错误根源。 教育科技（Ed-Tech）：可以将这套评估学生解题过程的逻辑，应用于个性化教育系统，为学生提供更精准、更具洞察力的学习反馈。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以探讨的局限性。\n实现的复杂性：整个TP-GRPO流程包含多个阶段：采样、分解、针对正确/错误解的两种评估流程、合并、奖励计算、优化等。这套系统的工程实现复杂度较高，引入了多个需要调节的超参数和启发式规则，可能会给复现和应用带来挑战。\n对启发式规则的依赖：附录B.1中提到了一些启发式规则，例如，对于最终答案正确的解，如果其\u0026lt;think\u0026gt;部分太短（少于4096字符），则直接跳过过程评估。这种规则的设定依据是什么？模型的最终性能对这些阈值的敏感度如何？论文对此没有深入探讨。\n惩罚的不精确性：论文在3.1.4节坦诚了一个局限性：对于错误的解，它会惩罚整个与\u0026lt;answer\u0026gt;匹配的“思想块”。然而，这个思想块中可能包含部分正确的推理步骤。这种“错杀”会引入一定的噪声，尽管作者称之为“务实的妥协”，但这依然是一个可以改进的点。\n比较的公平性疑问：尽管作者与自己复现的GRPO基线进行了公平比较，但在与其它开源模型（如Table 1和2中的其它方法）比较时，由于训练框架、超参数、训练步数定义等存在差异，直接的性能对比可能不完全公平。论文提出的Effic.指标在一定程度上缓解了这个问题，但底层的差异依然存在。\n“内在信号”的普适性：模型产生“wait, I made a mistake”这类反思性话语的倾向，可能与它的预训练数据和指令微调阶段的风格有关。这种“内在信号”在多大程度上是普适的，还是特定模型家族的“习惯”？这需要更多的实验来验证。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文充满了可以借鉴的智慧，即使您不直接从事RL研究。\n重点学习和启发： “思考思考过程”的元认知视角：这是最重要的思想启发。在任何需要优化复杂流程的任务中，都不要只盯着最终结果。尝试去分解过程，并找到评估过程质量的有效方法。这个思想可以应用于项目管理、软件开发流程优化、个人学习等任何领域。 寻找“内在信号”的智慧：在评估一个系统或流程时，不要总想着引入一个全知的“外部裁判”。多去观察系统自身产生的信号——错误日志、性能瓶颈报告、用户抱怨、甚至是代码中的“TODO”或“FIXME”注释。这些都是宝贵的、低成本的“内在信号”。 能力自适应的反馈机制：在给予反馈或制定激励措施时，要考虑到对象的当前能力水平。对于新手，多鼓励探索和尝试（高过程奖励）；对于专家，则更应关注最终产出的质量（高结果奖励）。这种动态调整的思路在团队管理和教育中非常实用。 “降低老师门槛”的原则：想要改进一个系统，并不一定需要一个比它更高级的系统来指导。一个好的“批评家”或“观察者”可能比一个“全能选手”更有用。这启发我们在设计监控和反馈系统时，可以更侧重于理解和分析能力，而非全面的执行能力。 需要补充的背景知识： 强化学习基础（RL）：至少需要理解PPO (Proximal Policy Optimization) 的基本原理，因为它是GRPO和本文算法的基础。了解PPO的目标函数、优势函数（Advantage Function）等概念，能帮助您看懂论文中的公式（如Eq. 1）。 RLHF/RLAIF框架：了解从人类反馈中进行强化学习 (RLHF) 的基本流程（SFT -\u0026gt; RM -\u0026gt; RL）。本文可以看作是从AI反馈中进行强化学习 (RLAIF) 的一个高级变种，它用一个GenPRM代替了传统的人工标注或简单的奖励模型。 思维链（Chain-of-Thought, CoT）：本文所有方法都建立在模型能生成详细的思考过程之上，这与思维链技术密切相关。了解CoT及其变种，能帮助您理解模型生成的内容及其结构。 大模型推理评估基准：对AIME, MATH等数学竞赛和数据集有所了解，能帮助您更好地理解本文实验结果的含金量。 希望这份详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得丰富的启发。\n","date":"2025-08-02T00:00:00Z","permalink":"/good-learners-think-their-thinkinggenerative-prm-makes-large-reasoning-model-more-efficient-math-learne/","title":"Good Learners Think Their Thinking：Generative PRM Makes Large Reasoning Model More Efficient Math Learne"},{"content":"主要证明了长时间的强化学习可以拓展推理边界，而不是单纯的从top-k变成了top-1。为了防止长时间的强化学习训练崩溃，引入多样化的数据集和参考策略重置（防止熵坍塌）\n这篇论文的核心论点是：长时间的强化学习（Prolonged Reinforcement Learning, ProRL）能够真正地拓展大语言模型（LLM）的推理能力边界，而不仅仅是优化或放大模型预训练时已学到的知识。 这个观点直接挑战了当时学术界部分认为强化学习仅能“唤醒”模型已有能力的看法，为通过更高效的训练方式（而非无尽地扩大预训练数据和模型规模）来提升模型智能提供了一条极具前景的路径。\n现在，我将为您详细剖析这篇论文。\n首先，让我们对论文进行一次全面的深度解读。\n论文开篇便指出了一个大模型领域的核心争议：强化学习（RL）在提升模型推理能力方面究竟扮演什么角色？它究竟是能教会模型全新的解题策略，还是仅仅让模型更高效地从其庞大的知识库中“采样”出那些高回报的答案？许多先前的研究因为训练时间过短（通常只有几百步）或过分依赖单一领域（如数学），得出了偏向后者的悲观结论。\n而这篇论文的作者们提出了一个大胆的假设：以往的结论可能源于方法论的局限，而非强化学习本身的根本性缺陷。 为此，他们设计了一套名为 ProRL 的新训练“配方”（Recipe）。这并非一个全新的算法，而是一套旨在实现 长期、稳定 强化学习的系统性方法论。\nProRL 的核心思想是“把训练时间拉长”。他们将训练步数延长至前所未有的2000步以上。为了支撑如此长时间的训练而不崩溃，ProRL 融合了多种关键技术：\n多样化的任务集：他们没有将模型局限于数学或代码，而是构建了一个包含数学、代码、STEM、逻辑谜题和指令遵循等五大领域的庞大训练集（136K样本）。作者认为，这种多样性对于培养模型的通用推理能力（Generalization）至关生要。 缓解熵坍塌（Entropy Collapse）：这是长期强化学习训练中的“拦路虎”。当模型对某些答案过于自信时，其输出的多样性（即熵）会急剧下降，导致模型停止探索新的可能性，训练陷入停滞。ProRL通过两种方式解决这个问题： KL散度正则化（KL Divergence Regularization）：这是一个“稳定器”，通过惩罚当前策略（Policy）与一个参考策略之间的差异，防止模型在训练中“跑偏”太远。 参考策略重置（Reference Policy Reset）：这是本文一个非常精妙的创新。随着训练的进行，最初的参考策略会变得“过时”，成为模型进一步探索的束缚。作者因此提出，周期性地将参考策略“硬重置”为当前训练策略的一个较新快照。这就像给探索者换上了一张更新的地图，使其能够继续向未知领域前进，而不是永远被限制在起点附近。 通过 ProRL，作者们训练出了一个1.5B参数的模型，命名为 Nemotron-Research-Reasoning-Qwen-1.5B。实验结果极其亮眼，这个“小”模型在多个推理基准上，不仅显著超越了其基座模型，甚至在某些方面能够媲美或超过参数量更大的7B模型。\n例如，在与基座模型的对比中，新模型在数学基准上平均提升了15.7%，代码能力提升14.4%，逻辑谜题上更是提升了惊人的54.8%。（见论文Table 1, 2, 3）\nimage.png 图1：论文中的核心图表，展示了ProRL带来的多方面收益。左图显示了性能随训练步数持续提升；中图的“创造力指数”更高，说明产生了新颖的解法；右图则展示了在多个任务上对基座模型的巨大优势。\n但最令人振奋的发现，是ProRL确实拓展了推理的边界。作者通过 pass@k 指标（即模型在k次尝试中至少有一次成功的概率）来衡量。他们发现，在许多任务上，基座模型的pass@256为0，意味着即便给它256次机会，它也永远无法找到正确答案。然而，经过ProRL训练的模型却能达到很高的成功率。这强有力地证明了，模型学会了全新的、本质上不存在于基座模型中的解题路径。\n如论文图4中“Sustained”类别和图5针对分布外（OOD）任务“boxnet”的实验所示，基座模型（蓝色线）在k值很高时成功率依然为0，而ProRL训练后的模型（绿色和橙色线）则表现出强大的解题能力。\n这篇论文的贡献是多方面的：它不仅为强化学习在LLM中的应用“正名”，证明了其作为能力“探索者”而非“放大器”的潜力；更重要的是，它提供了一套行之有效的、可复现的工程实践方法（ProRL），为整个行业指明了方向——即通过更精巧和持续的后训练，而非仅仅依赖于更大规模的预训练，来打造更智能的模型。这对于AI的普惠化和可持续发展具有深远意义。\n接下来，我将按照您提出的六个问题，逐一进行更详细的解读。\n一、研究目标与意义 研究目标：论文的核心研究目标是验证一个根本性问题：强化学习（RL）能否让大型语言模型（LLM）学会其基座模型（Base Model）完全不具备的、全新的推理能力？ 作者们试图挑战当时普遍存在的一种怀疑论调，即认为RLHF/RLAIF等方法只是在“利用（Exploitation）”模型在预训练阶段已经学到的知识，而非“探索（Exploration）”新的知识和能力。\n解决的实际问题：该研究直接面对的是如何更高效地提升LLM高级认知能力的瓶颈问题。随着模型规模越来越大，单纯依靠扩大数据和算力进行预训练的“暴力美学”方法，其边际效益递减，成本却急剧上升。如果RL只能做“锦上添花”的工作，那么AI能力的提升将严重依赖于昂贵的预训练。而如果RL能“无中生有”，则意味着我们找到了一条更经济、更高效地为模型“赋能”的路径。\n行业意义：这个问题对行业发展的意义极其重大。\n降本增效：肯定RL的“探索”能力，意味着企业可以基于一个通用的基座模型，通过成本相对较低的、长时间的RL后训练，来打造针对特定领域（如法律、金融、科研）的专家模型，而无需从零开始进行代价高昂的预训练。 突破能力上限：它表明模型的潜力并不完全由其预训练数据决定。通过与环境（例如，一个有明确奖励信号的任务）的持续互动，模型可以自我进化，学会超越其“先天”知识库的复杂技能，这为实现更高级别的通用人工智能（AGI）提供了新的可能性。 推动AI普惠化：如论文中所述，一个通过ProRL训练的强大的1.5B模型，其部署和运行成本远低于巨型模型。这使得先进的AI推理能力能够被更多资源有限的中小企业、研究机构和个人所用，极大地促进了AI技术的普及和应用创新。 二、核心方法与创新 论文提出的核心方法是 ProRL (Prolonged Reinforcement Learning)，它不是一个单一算法，而是一套实现长期稳定RL训练的系统性“配方”。其创新之处在于多个组件的有机结合，解决了长期训练中的关键痛点。\n核心思路：长时间训练 (Prolonged Training) 特点：与以往研究通常在几百个训练步数后就早早终止不同，ProRL将训练过程延长至超过2000步，甚至在附录中展示了长达16k GPU小时的训练过程。作者认为，复杂的推理能力需要足够的时间来“涌现”，就像人类学习一样。 优势：长时间的训练为模型提供了充分的“探索”机会，使其能够从最初的偶然成功中逐渐归纳出通用的解题策略，并将其泛化到新的问题上。 关键技术1：通过KL散度与策略重置缓解熵坍塌 背景：在RL中，模型会倾向于重复能够获得高奖励的动作，这会导致其输出分布变得越来越“尖锐”，即熵坍塌。一旦发生，模型便失去了多样性，无法再探索新的、可能更优的解法，训练也就停滞了。 ProRL的解决方案： KL散度正则化：在损失函数中加入一项 \\(D_{KL}(\\pi_{\\theta}||\\pi_{ref})\\)，它衡量当前策略 \\(\\pi_{\\theta}\\) 和一个固定的“参考策略” \\(\\pi_{ref}\\) 之间的差异。这相当于一根“缰绳”，防止模型为了追求短期奖励而偏离一个稳定可靠的基准太远。 参考策略重置 (Reference Policy Reset)：这是点睛之笔。如果参考策略永远不变（例如，永远是最初的基座模型），那么它最终会成为模型进步的“天花板”。ProRL的创新在于周期性地将这个参考策略 \\(\\pi_{ref}\\) 更新为当前模型 \\(\\pi_{\\theta}\\) 的一个新版本。 \u0026gt; We further observe that as training progresses, the KL term may increasingly dominate the loss… To alleviate this, we introduce a simple yet effective technique: reference policy reset. Periodically, we hard-reset the reference policy πref to a more recent snapshot of the online policy πθ… 优势：这种“动态基准”的设计，既能在每个阶段提供稳定性，又允许模型“水涨船高”，不断突破旧的束缚，向更高水平的能力迈进，从而支撑起数千步的有效训练。 关键技术2：多样化任务与动态数据采样 特点：论文强调了训练数据多样性的重要性，覆盖了数学、代码、STEM、逻辑和指令遵循五大类。此外，他们还借鉴了DAPO算法中的动态采样（Dynamic Sampling）思想，即在训练中动态过滤掉那些模型已经能100%解决（太简单）或完全无法解决（太难）的样本。 优势： 多样化的任务能防止模型在单一领域过拟合，培养其通用的、可迁移的推理能力。 动态采样让模型始终聚焦于“学习区”的难题，最大化了每个训练样本的价值，提高了训练效率。 与之前的方法相比，ProRL的优势在于其系统性和前瞻性。它不只是解决一个孤立问题，而是构建了一个能够支撑“马拉松式”强化学习的完整框架，其核心创新——参考策略重置——巧妙地平衡了训练的稳定性和模型的成长性。\n三、实验设计与结果 论文通过一系列精心设计的实验，极具说服力地验证了ProRL的有效性。\n实验设计： 模型：选择了一个1.5B参数的DeepSeek-R1-Distill-Qwen-1.5B作为基座模型，通过ProRL训练后得到Nemotron-Research-Reasoning-Qwen-1.5B。这种“同源对比”能最清晰地展示ProRL带来的增益。同时，论文也引入了更大的7B模型作为参考，以凸显其1.5B模型的卓越性能。 评估基准 (Benchmarks)：覆盖面极广，包括： 数学：AIME, AMC, MATH, Minerva, Olympiad Bench 代码：APPS, Codeforces, TACO, HumanevalPlus, LiveCodeBench STEM与指令遵循：GPQA, IFEval 逻辑谜题：Reasoning Gym (包含96种不同任务) 评估指标 (Metrics)：主要使用 pass@k。pass@1反映模型的首答准确率，而高k值（如pass@128, pass@256）下的成功率则能衡量模型推理能力的广度或边界——即正确的答案是否存在于模型的“能力范围”之内。 关键实验结果： 全面性能超越：训练后的模型在所有领域都显著优于基座模型。 \u0026gt; 在数学领域，平均pass@1得分从44.45%提升到60.14%；在代码领域，从23.08%提升到37.49%；在GPQA上从15.86%提升到41.78%。(见论文Table 1, 2, 3)\n成功拓展推理边界：这是论文最有力的证据。实验清晰地显示，在某些任务上，基座模型的能力边界被完全突破。\nOOD任务(boxnet)：对于一个训练中从未见过的boxnet任务，基座模型的pass@k在所有k值下都为0。而ProRL模型在pass@1时就有14.4%的成功率，在pass@256时更是高达94.0%。(见论文Figure 5) Sustained Gains任务(dice)：对于dice任务，基座模型的pass@256同样为0，而ProRL模型最终达到了接近100%的成功率。(见论文Figure 4) 图5：在OOD任务boxnet上的表现。蓝色线（基座模型）始终为0，而橙色和绿色线（ProRL模型）表现出强大的泛化能力，证明了新能力的产生。 “越弱则越强”的增益模式：论文发现，ProRL带来的性能提升与基座模型的初始能力呈显著负相关（\\(R^2=0.879\\)）。 \u0026gt; A key finding…is that the effectiveness of RL in expanding a model’s reasoning boundary…is strongly influenced by the base model’s initial capabilities. …tasks where the base model already performs well…tend to exhibit minimal or even negative gains… In contrast, in domains where the base model struggles…RL training is most effective. 这意味着，RL并非简单地“刷分”，而是在模型最薄弱的环节、最未知的领域，发挥出最大的“开荒”作用。\n这些实验设计严谨，结果清晰，层层递进地证明了ProRL不仅能提升性能，更重要的是能发现并巩固基座模型中不存在的新知识和新策略。\n四、未来展望与机遇 这篇论文为大模型领域的发展开辟了新的想象空间，也带来了一系列值得探索的问题和机遇。\n值得进一步探索的问题和挑战： 规模法则 (Scaling Laws)：ProRL的效果是否会随着模型规模的增长而变化？在百亿甚至千亿参数的模型上，这种长时间RL训练是否会带来更惊人的“涌现”能力？这是一个亟待验证的问题。 更复杂的推理任务：目前验证的任务大多有明确、可验证的答案。未来，能否将ProRL的思想应用于更开放、更复杂的领域，如科学发现、数学定理证明、AI Agent的长期规划等？ 训练效率与成本：ProRL虽然效果显著，但其计算成本（16k GPU hours）依然高昂。如何设计更高效的长期RL算法，降低“探索”新能力的成本，将是未来的一个重要研究方向。 与价值对齐的结合：ProRL优化的是“任务成功率”这类可验证的奖励。如何将其与旨在对齐人类价值观的RLHF/RLAIF方法相结合，确保模型在能力增长的同时，其行为也是安全、可控和有益的？ 可能催生的新技术和投资机会： “模型即服务”的精细化：云服务商或AI公司可以提供基于通用大模型的“专家模型定制”服务。客户只需提供特定领域的任务和奖励函数，即可通过ProRL这类技术，以远低于预训练的成本，获得一个高性能的专属模型。 AI for Science (AI用于科学发现)：ProRL证明了AI有能力探索人类未知的解决方案。这在药物研发、材料科学、气候模拟等领域有巨大潜力。投资于将此类技术应用于特定科学领域的初创公司或项目，可能获得颠覆性的回报。 RL训练平台与工具：ProRL所涉及的复杂训练流程（如数据集管理、动态采样、策略重置、超参数调优等）催生了对专业工具和平台的需求。开发简化和自动化大规模RL for LLM流程的软件，将是一个重要的市场机会。 五、批判性审视与不足 尽管这篇论文非常出色，但从批判的视角来看，它仍存在一些局限和待验证之处。\n计算资源门槛过高：这是最显著的不足。作者坦言“The extended RL training process requires substantial computational resources”。16k GPU小时的消耗对于绝大多数学术研究机构和中小型公司而言是难以承受的。这限制了该方法的可复现性和广泛应用。 规模扩展性存疑：所有实验都基于一个1.5B的模型。虽然结果令人印象深刻，但这并不能直接推断出同样的方法在10B、70B或更大的模型上也能奏效。大模型的训练动态可能完全不同，可能会遇到新的稳定性或收敛性问题。 “配方”的复杂性与脆弱性：ProRL是一个包含众多超参数和启发式规则的复杂“配方”（例如，何时重置策略、KL散度的权重β如何设置、任务混合的比例等）。这可能使得最终结果对这些参数非常敏感，难以调试和复现，更像是一门“炼丹术”而非一个简洁优雅的算法。 对“新能力”的定义：论文声称模型学会了“全新的”推理路径。但一个持怀疑态度的观察者可能会反驳说，这些路径可能并非“无中生有”，而只是在基座模型中以极低概率存在的状态，RL的作用是发现了它们并极大地放大了其概率。尽管从实践效果上看差异不大，但在理论上，这涉及到“学习”与“发现”的本质区别。 任务范围的局限：尽管任务多样，但它们仍然属于有明确对错、有单一或清晰奖励信号的范畴。ProRL在更开放、模糊、需要创造力的任务（如写小说、进行哲学辩论）上效果如何，论文并未探讨。 六、核心启示与学习路径 对于希望从中汲取创新想法的您来说，这篇论文提供了极其宝贵的启示。\n重点学习与启发： 思维转变：将RL视为“探索者”：最核心的启发是，不要将强化学习仅仅看作一个优化模型输出分布的“微调”工具。要相信，通过设计合理的探索机制和足够长的训练，RL可以成为带领模型走出“知识舒适区”，探索未知能力空间的引擎。 耐心与持续：不要过早放弃：短期的RL训练结果可能是具有欺骗性的。复杂能力的涌现需要时间。在您的项目中，如果初期RL效果不佳，不妨思考是否是训练时间不足或探索不够，而不是方法本身失效。 多样性是泛化的基石：在设计RL训练时，应尽可能引入多样化的任务和数据。这能有效防止模型过拟合，使其学到的策略更具通用性，这对于构建鲁棒的AI系统至关重要。 稳定压倒一切：在实践中，RL for LLM最大的挑战往往是训练的不稳定性。从这篇论文中学习系统性地解决稳定性的方法，如KL正则化、策略重置、动态数据管理等，是能直接用于您自己项目的宝贵工程经验。 需要补充的背景知识： 强化学习基础：建议您扎实掌握Policy-Gradient方法，特别是其代表性算法 PPO (Proximal Policy Optimization)。ProRL使用的GRPO是PPO的一个变种，理解PPO的Clipped Objective对于理解本文至关重要。 KL散度 (Kullback-Leibler Divergence)：需要深刻理解它作为衡量两个概率分布差异的数学工具，在RL中扮演的“正则化”或“信赖域”角色。 LLM推理任务评估：熟悉 pass@k、BLEU、ROUGE等生成任务常用评估指标的含义、计算方式及其优缺点。 熵 (Entropy)：在信息论和RL中，熵都代表了不确定性或多样性。理解为什么在RL训练中维持一定的熵是如此重要，是理解本文动机的关键。 希望这份详尽的解读能够帮助您深入理解这篇论文的精髓，并从中获得有价值的启发。\n","date":"2025-08-02T00:00:00Z","permalink":"/prorlprolonged-reinforcement-learning-expands-reasoning-boundaries-in-large-language-models/","title":"ProRL：Prolonged Reinforcement Learning Expands Reasoning Boundaries in Large Language Models"},{"content":"这篇也是在过程上做文章，设计了过程导向的奖励，类似Good Learners Think Their Thinking：Generative PRM Makes Large Reasoning Model More Efficient Math Learne\n和First Return, Entropy-Eliciting Explore等类似都是为了解决信用分配问题。\n论文深度解读 image.png 这篇论文的核心贡献在于识别并解决了一个在长时程任务代理训练中普遍存在但又未被充分探讨的问题：结果正确，但过程混乱。传统的强化学习（RL）方法，尤其是那些仅依赖最终任务成败作为稀疏奖励信号的方法（如论文中对比的GRPO），其优化目标是最大化最终奖励。这意味着，无论代理是通过深思熟虑的逻辑推理，还是通过一系列偶然、重复、甚至不合逻辑的步骤侥R幸成功，只要最终完成了任务，这条路径就会被强化。论文将这种现象定义为“低效探索”（inefficient exploration）。\n这种训练范式会导致两个严重后果： 1. 脆弱性（Brittleness）：代理学会的并非是通用的问题解决方法，而是一些针对特定场景的“捷径”或“固定套路”。一旦环境发生微小变化或遇到未曾见过的任务（unseen tasks），这种捷径就会失效，导致性能急剧下降。 2. 低泛化性（Poor Generalization）：由于没有学到内在的、可迁移的推理能力，代理很难将从简单任务中学到的知识应用到更复杂的、需要全新策略的任务类别中。\n论文通过一个生动的例子展示了这个问题。在一个名为ALFWorld的虚拟家庭环境中，一个由GRPO方法训练的代理在执行“将两个钥匙串放入保险箱”的任务时，即使已经到达了目标地点“dresser 1”，并且拿到了一个钥匙串，它接下来的动作竟然是连续多次尝试“go to dresser 1”。\nA trajectory segment of an agent trained with GRPO Step 8 Observation: You pick up the keychain 1 from the dresser 1. Reasoning: The current situation is that I am at dresser 1 with the keychain 1 in my possession. To put two keychains in the safe, I need to find another keychain and then move both keychains to the safe. Action: go to dresser 1 Step 9 Observation: Nothing happens. Action: go to dresser 1 Step 10 Observation: Nothing happens. Action: go to dresser 1\n这个例子直观地暴露了“低效探索”的弊病：代理的行为和它的“思考”（Reasoning）是脱节的，它只是在执行一个被强化过的、看似成功的动作序列，而没有真正理解自己所处的状态和任务要求。\n为了解决这个问题，RLVMR框架应运而生。其设计的核心思想借鉴了认知科学中的元认知（Metacognition）理论，即“关于思考的思考”。一个具备元认知能力的学习者，不仅会学习知识，还会监控和调节自己的学习过程，比如制定计划、评估进展、反思错误。RLVMR将这一理念应用到了大模型代理的训练中。\nimage.png RLVMR的实现方式极具巧思，它没有采用复杂的模型结构，而是通过改造奖励机制和训练流程来达成目标。具体来说，它包含以下几个关键部分：\n显式的元认知标签（Explicit Meta-Reasoning Tags）：RLVMR扩展了流行的ReAct（Reason + Act）框架。它不再将“思考”视为一个笼统的过程，而是定义了四种具体的元认知状态，并要求代理在生成动作前，用XML风格的标签明确标示出自己当前的认知活动： \u0026lt;planning\u0026gt;：在任务开始或需要重新规划时，进行宏观的步骤拆解。 \u0026lt;explore\u0026gt;：当遇到不确定性或瓶颈时，生成探索性的假设或选项。 \u0026lt;reflection\u0026gt;：当尝试失败后，回顾历史、分析错误并制定纠正措施。 \u0026lt;monitor\u0026gt;：在顺利执行时，跟踪任务进展，确保行动与子目标一致。 过程导向的奖励塑造（Process-Oriented Reward Shaping）：这是RLVMR的灵魂所在。除了最终任务成功与否的结果奖励（Outcome Reward），RLVMR还设计了一套密集的、基于规则的元认知奖励（Meta-Reasoning Reward），在每一步对代理的“思考过程”进行打分。例如： 探索奖励：当代理的动作探索了一个新的对象或位置时给予奖励，以抑制重复行为。 反思奖励：当一个\u0026lt;reflection\u0026gt;标签后紧跟着一个有效的纠错动作时给予奖励。 格式惩罚：如果模型的输出不符合预期的\u0026lt;tag\u0026gt;...\u0026lt;/tag\u0026gt;\u0026lt;action\u0026gt;...\u0026lt;/action\u0026gt;结构，则给予负奖励。 两阶段训练框架（Two-Phase Training Framework）： 冷启动（Cold Start）：由于直接让模型从零开始学习这套复杂的标签和推理模式非常困难，RLVMR首先进行一个轻量级的监督微调（SFT）阶段。研究者使用一个更强大的“教师模型”（如GPT-4）来为少量（仅200个）成功的轨迹自动标注上元认知标签，然后用这些“伪专家数据”对目标模型进行微调，使其掌握基本的标签语法和推理模式。 强化学习（RL）：在冷启动之后，代理进入真正的与环境交互的强化学习阶段。此时，它使用一种名为GRPO-MR (Group Relative Policy Optimization with Meta-Reasoning) 的自定义策略优化算法。该算法巧妙地结合了来自整个轨迹的结果奖励和来自当前步骤元认知行为的过程奖励，通过一个超参数α来平衡两者的影响，共同计算每一步的优势函数（Advantage），从而指导策略的更新。 实验结果有力地证明了RLVMR的有效性。在ALFWorld和ScienceWorld这两个极具挑战性的长时程任务基准上，RLVMR在各种模型尺寸（1.5B和7B）和所有数据划分（L0-L2）上都取得了全面超越（State-of-the-Art, SOTA） 的性能。\n表1：ALFWorld和ScienceWorld基准测试性能对比 (成功率 %) Model Method ALFWorld L0 ALFWorld L1 ALFWorld L2 ScienceWorld L2 Qwen-7B + GRPO 79.3 77.3 52.3 26.6 Qwen-7B + GiGPO 89.5 90.2 67.2 25.8 Qwen-7B + RLVMR 91.4 91.8 83.6 32.2 GPT-4o - 57.3 66.0 68.8 41.0 Qwen-1.5B + RLVMR 89.1 87.9 56.3 26.5 表格根据原论文Table 1简化和整理\n最引人注目的结果出现在泛化能力要求最高的L2（unseen task variants and unseen task categories）划分上。Qwen-7B模型结合RLVMR后，在ALFWorld上取得了83.6%的成功率，相较于次优的GiGPO（67.2%）有16.4个百分点的巨大提升。这充分说明，通过奖励良好的推理过程，RLVMR确实教会了模型“如何思考”，使其具备了解决全新问题的能力。\n更令人印象深刻的是，RLVMR能够让小模型超越大模型。例如，1.5B的Qwen模型在使用RLVMR训练后，在ALFWorld L1上的成功率（87.9%）远超标准ReAct模式下的GPT-4o（66.0%）。这雄辩地证明，精巧的、过程导向的监督比单纯依赖巨大的模型规模是通往高性能更有效、更高效的路径。\nimage.png 上图（模拟原论文Figure 5）展示了RLVMR如何直接解决“低效探索”问题。相较于GRPO，RLVMR训练的代理在所有任务上都表现出极低的重复动作率（Repetitive Actions）和无效动作率（Invalid Actions）。例如，在L0任务上，7B模型的重复动作率从GRPO的21.5%骤降至2.3%，减少了近十倍。这证明了元认知奖励（如探索新状态的奖励和格式惩罚）在引导代理走向高效、有目的性的行为方面起到了决定性作用。\n综上所述，RLVMR通过引入一种新颖的、受元认知启发的、对推理过程进行显式奖励的范式，成功地解决了强化学习代理在长时程任务中的低效探索和泛化难题，为构建更智能、更可靠的自治代理系统提供了清晰且有效的路径。\n### 1. 论文的研究目标与意义 研究目标：本文的核心研究目标是解决基于大模型的强化学习代理在执行复杂、长时程任务时普遍存在的低效探索（inefficient exploration）问题。\n想要解决的实际问题：\n提升代理的泛化能力：传统的RL方法（如GRPO）通过强化最终结果，可能会让代理学会一些脆弱的、特定于训练环境的“捷径”，导致其在面对未见过的新任务或新环境时性能大幅下降。论文希望代理能学会通用的、可迁移的推理能力，而不是死记硬背解决方案。 提高代理的探索效率和鲁棒性：代理在探索过程中经常会陷入无意义的重复动作或执行无效动作，这不仅浪费了计算资源，也表明其推理过程是混乱和脆弱的。论文旨在减少这种冗余和错误，使代理的行为更加高效和稳健。 增强代理的可解释性：通过让代理明确标示其当前的认知状态（规划、探索、反思等），其决策过程变得更加透明，便于人类理解、调试和信任。 对行业发展的重要意义： 随着大模型技术的发展，构建能够自主完成复杂任务的自治代理（Autonomous Agents）已成为AI领域的前沿热点，其应用场景遍及机器人、软件工程、科学发现、智能客服等。然而，这些应用的成功与否，很大程度上取决于代理的可靠性和泛化能力。如果一个代理只能在它被明确训练过的场景中工作，或者在遇到小问题时就容易崩溃或陷入死循环，那么它的实用价值将大打折扣。 这篇论文的意义在于，它直面了构建可靠代理的核心挑战，并提供了一个可扩展且有效的解决方案。它告诉我们，要让代理真正变得“智能”，就不能只关注“做什么”（what to do），更要关注“如何思考”（how to think）。通过对推理过程进行监督，可以培养出真正理解任务、能够举一反三的代理。这对于推动自治代理技术从“玩具”走向“工具”，从实验室走向实际产业应用，具有至关重要的指导意义。\n### 2. 论文提出的新思路与方法 论文提出的核心方法是RLVMR (Reinforcement Learning with Verifiable Meta-Reasoning Rewards) 框架。相较于之前的方法（主要是SFT和仅有结果奖励的RL），RLVMR的特点和优势体现在以下几个方面：\n新思路：监督思考过程，而非仅仅监督结果 之前的方法： SFT (Supervised Fine-Tuning)：在专家轨迹上进行微调。优点是高效，但缺点是泛化能力差，遇到新情况容易“卡壳”，因为模型只是在模仿，没有学会探索和纠错。 GRPO (Group-based RL)：通过与环境交互，强化能带来最终成功的轨迹。优点是能探索，泛化性优于SFT，但缺点是“不择手段”，会强化包括大量重复、无效步骤在内的任何成功路径，导致“低效探索”。 RLVMR的思路：RLVMR认为，通往成功的路径有好有坏，应该明确地奖励“好的”路径，惩罚“坏的”路径。这个“好坏”的评判标准，就是推理过程是否符合逻辑、是否高效。这是其最根本的创新。 新方法：基于元认知的奖励机制 RLVMR通过以下精巧设计将上述思路落地： 显式的元认知标签：这是实现“可验证”（Verifiable）奖励的前提。通过引入\u0026lt;planning\u0026gt;, \u0026lt;explore\u0026gt;, \u0026lt;reflection\u0026gt;, \u0026lt;monitor\u0026gt;四种标签，代理的内部认知状态被外化，从而变得可以被程序化地评估和奖励。 \u0026gt; This decouples reasoning from actions and enables fine-grained analysis and supervision. 混合奖励信号（Composite Reward Signal）：这是RLVMR的核心机制。它不依赖单一的奖励来源，而是结合了两种信号： 稀疏的结果奖励 (Outcome Reward, R(τ))：来自于任务最终是否成功，为代理的整体行为提供最终目标导向。 密集的过程奖励 (Meta-Reasoning Reward, r_t^MR)：在交互的每一步，根据代理的元认知行为进行即时反馈。这部分奖励是程序化的、基于规则的，例如： \u0026gt; Exploration Reward (r_explore): Awarded if the current action targets a new object or location, discouraging redundancy. \u0026gt; Reflection Reward (r_reflection): Awarded if a \u0026lt;reflection\u0026gt; step is followed by a corrective action after a sequence of failures. GRPO-MR优化算法：为了有效利用这两种奖励，论文提出了GRPO-MR算法。该算法在计算每一步的优势函数A_t时，加权融合了两个维度的优势：一个是基于最终结果的轨迹级优势 (A_traj)，另一个是基于当前推理质量的元认知级优势 (A_MR,tag)。 \u0026gt; At = α · A_t^traj + (1 - α) · A_t^MR 这里的α是一个关键的平衡旋钮，它决定了模型在多大程度上关注长期目标，以及在多大程度上关注眼前的推理步骤是否“漂亮”。 优势总结： 解决了信用分配难题：在长时程任务中，最终的成功可能归功于早期某个关键决策。密集的过程奖励使得信用分配更加及时和准确。 提升了样本效率和训练稳定性：密集、清晰的奖励信号为模型提供了更稳定的学习梯度，避免了在稀疏奖励下的盲目探索，从而加快了收敛速度（如Figure 6所示）。 兼具SFT的效率和RL的泛化性：通过“冷启动SFT + RL微调”的两阶段训练，RLVMR既利用SFT快速教会模型基本规则，又利用RL的探索能力来培养其泛化和鲁棒性，取二者之长。 ### 3. 实验设计与结果 论文通过在ALFWorld（虚拟家庭环境）和ScienceWorld（虚拟科学实验环境）两个具有挑战性的基准上进行的大量实验来验证其方法的有效性。\n实验设计： 基准和数据划分：为了系统地评估模型的泛化能力，研究者精心设计了三个难度递增的评估集（evaluation splits）： L0 (seen-L0): 训练和测试任务的类型、变体都见过。衡量模型的记忆和基础能力。 L1 (unseen-L1): 测试任务的变体是新的，但任务类型（如“加热并放置”）是见过的。衡量模型对已知能力的组合应用能力。 L2 (unseen-L2): 测试任务的变体和类型都是全新的。这是对模型零样本泛化（zero-shot generalization）能力的终极考验。 对比基线（Baselines）：实验对比了多种主流和前沿的方法，包括： ReAct: 基础的零样本prompting方法。 SFT: 监督微调。 ETO, GRPO, GiGPO: 几种不同的、仅依赖结果奖励的强化学习方法。 GPT-4o, DeepSeek-V3/R1: 更大规模的闭源/开源模型，以验证RLVMR是否能让小模型实现“以小博大”。 评估指标（Metrics）： 成功率 (%)：衡量方法的有效性。 无效/重复动作率 (%)：直接量化“低效探索”问题，衡量探索效率。 平均动作序列长度：衡量解决问题的直接性（越短越好）。 实验数据和结果： 论文呈现了压倒性的积极结果，关键数据如下： SOTA性能和卓越的泛化能力： 在最具挑战性的ALFWorld L2上，Qwen-7B + RLVMR 取得了 83.6% 的成功率，而之前的SOTA方法GiGPO仅为67.2%（提升16.4%）。这表明RLVMR在面对全新任务时具有极强的泛化能力。 显著提升探索效率： 在ALFWorld L0任务上，RLVMR将7B模型的重复动作率从GRPO的21.5%降至2.3%，无效动作率从14.8%降至5.6%（见Figure 5）。这直接证明了RLVMR有效抑制了低效行为。 小模型超越大模型： 1.5B的RLVMR模型在ALFWorld L1上达到87.9%的成功率，而GPT-4o仅为66.0%。这证明了方法的效率，说明一个好的训练方法比单纯的模型尺寸更重要。 消融实验验证了各组件的必要性： Table 3的消融研究显示，移除元认知奖励 (w/o A^MC) 或结果奖励 (w/o A^T) 或冷启动 (w/o CS) 都会导致性能大幅下降。例如，在ALFWorld L2上，移除元认知奖励后成功率从56.3%降至45.3%；而移除结果奖励则几乎使模型完全失效，成功率骤降至12.5%。这证明了RLVMR的每个设计都是不可或缺的，尤其是过程奖励和结果奖励的协同作用。 ### 4. 未来研究方向与机会 结合这篇论文的启示和当前大模型领域的发展趋势，未来有以下值得探索的方向和机会：\n更复杂的奖励机制： 自适应奖励（Adaptive Rewards）：当前RLVMR的奖励规则是固定的。未来可以研究如何让奖励机制本身变得更智能，例如，根据任务的难度或代理的能力水平动态调整奖励的权重（如α值），甚至让模型学会自己生成奖励函数（Learnable Reward Models），从而适应更广泛的任务。 多模态反馈：当前反馈是基于文本的。在机器人等具身智能场景中，可以引入来自视觉、听觉或物理传感器的反馈来共同塑造奖励，使代理能与真实物理世界进行更复杂的交互。 扩展到更广泛的应用领域： 软件工程与代码生成：可以将元认知标签应用于复杂的编码任务中，例如\u0026lt;debug_code\u0026gt;, \u0026lt;refactor_module\u0026gt;, \u0026lt;write_test_case\u0026gt;，通过奖励这些高质量的开发行为，训练出更专业的“AI程序员”。 科学发现：在自动化实验、分子设计等领域，可以定义如\u0026lt;formulate_hypothesis\u0026gt;, \u0026lt;design_experiment\u0026gt;, \u0026lt;analyze_results\u0026gt;等步骤，并对其进行奖励，以加速科学研究进程。 教育领域：可以构建一个“AI导师”，它不仅能给出答案，还能展示其\u0026lt;breakdown_problem\u0026gt;, \u0026lt;provide_analogy\u0026gt;等元认知思考过程，从而更好地引导学生学习。 技术与投资机会： Agentic AI平台：这篇论文的技术为构建更可靠、更通用的AI代理平台提供了核心思路。未来的平台级产品，其核心竞争力可能就在于其训练框架是否能培养出具备类似RLVMR所倡导的鲁棒推理能力的代理。专注于开发此类训练框架和平台是重要的投资方向。 “AI心理学”与可解释性：对代理的元认知行为进行分析，本身就为我们打开了一扇理解“机器心智”的窗户。深入研究这些元认知模式与模型内部激活状态的关联（ mechanistic interpretability），可能催生出全新的AI可解释性技术，这在金融、医疗等高风险领域至关重要。 ### 5. 论文的批判性视角与不足 尽管这篇论文非常出色，但从批判性的角度看，仍存在一些可以探讨的局限和待验证的问题：\n对教师模型的依赖：冷启动阶段需要一个更强大的教师模型（GPT-4）来生成带标签的训练数据。这带来几个问题：\n性能天花板：学生模型的性能可能会受限于教师模型的“智慧”。如果教师模型的标注本身存在偏见或错误，这些问题可能会被传递给学生模型。 成本与可复现性：使用强大的闭源API（如GPT-4）会增加研究的成本和复现的难度。 自举（Bootstrapping）问题：如何摆脱对更强模型的依赖，实现完全的自我进化和学习，是一个悬而未决的问题。 奖励设计的复杂性：元认知奖励是基于人工设计的、程序化的规则。虽然在这两个基准上很有效，但在更开放、更多样化的任务中，设计一套普适且完备的奖励规则可能会变得非常困难，这本质上是一种特征工程（Feature Engineering）。如何自动化或简化这个设计过程是一个挑战。\n模拟环境与现实世界的差距（Sim-to-Real Gap）：实验是在纯文本的模拟环境中进行的。现实世界充满了噪音、不确定性和连续的状态空间。RLVMR在这些模拟环境中培养出的高效推理能力，能否顺利迁移到与真实物理世界交互的机器人上，还需要进一步的验证。\n超参数的敏感性：模型中存在一些关键超参数，特别是平衡结果和过程奖励的α。论文中将其设为0.5，但没有详细讨论该参数的敏感性分析。在不同类型的任务中，α的最优值可能不同，如何自适应地调整它是一个值得研究的问题。\n### 6. 核心启发与知识补充 对于希望从中学习并应用创新想法的读者来说，这篇论文提供了几个宝贵的启示：\n核心启发： 最重要的启发：从“奖励结果”转向“奖励过程”。在设计AI系统，尤其是需要复杂推理的代理时，不要只盯着最终的KPI。思考如何将“好的行为/过程”量化，并将其纳入到模型的学习目标中，这往往能带来性能的飞跃。 让隐性认知显性化。通过设计一套标签或结构化输出来让模型的“思考”过程可见、可控，是实现过程监督和提升可解释性的强大武器。这在任何需要人机协同或高可靠性的场景中都极具价值。 “SFT引导 + RL探索”的混合训练范式。对于复杂的任务，纯粹的SFT或RL都可能存在短板。先用少量高质量数据为模型“打好基础”，再让它在探索中“自由成长”，是一种非常高效且实用的训练策略。 需要补充的背景知识： 强化学习基础：需要理解策略梯度（Policy Gradient）、优势函数（Advantage Function）以及PPO (Proximal Policy Optimization) 算法。这篇论文的GRPO-MR是PPO思想的一种变体。 Agent框架：了解ReAct (Reason+Act) 框架是理解本文工作起点和背景的关键。ReAct开创了让LLM在行动前先“思考”的范式。 长时程任务（Long-Horizon Tasks）：理解这类任务的特点，即奖励稀疏（sparse rewards）和信用分配（credit assignment）困难，这有助于你 grasp the core problem the paper is trying to solve. 元认知（Metacognition）：虽然不需要成为认知心理学家，但了解元认知的基本概念（如计划、监控、评估、反思）会让你更深刻地理解RLVMR框架设计的理论灵感和高明之处。 ","date":"2025-08-02T00:00:00Z","permalink":"/rlvmrreinforcement-learning-with-verifiable-meta-reasoning-rewards-for-robust-long-horizon-agents/","title":"RLVMR：Reinforcement Learning with Verifiable Meta-Reasoning Rewards for Robust Long-Horizon Agents"},{"content":"这篇论文就侧面揭示了世界模型的重要性：可以同时生成多个执行动作，然后使用世界模型来预测动作导致的后果，最终来选择最正确的动作，这个观念完美符合这篇论文的发现，即广度行动。\nimage.png 这篇论文的核心论点极具启发性，它挑战了当前大模型智能体（Agent）研究中一个主流却可能存在局限的趋势。目前，许多研究致力于通过增加模型的“思考深度”来提升性能——即在执行每一步动作前，生成更长、更复杂的推理链（Chain-of-Thought）。作者们犀利地指出，这种“闭门造车”式的深度思考，虽然在信息完备的任务中有效，但对于需要在动态、信息不完全的环境中（如网页浏览、软件操作）与外界交互的智能体而言，存在根本性瓶颈。因为无论思考多久，智能体都无法凭空获得环境中未曾展现的新信息。\n为此，论文提出了一个全新的、与“深度思考”正交的智能体能力扩展维度——“广度行动”（Scaling Test-Time Interaction）。其核心思想是，与其让智能体在单一步骤上“想得更多”，不如赋予它“做得更多”的能力，即增加其与环境交互的总步数。这使得智能体能够执行更丰富的行为序列，例如试错、回溯、主动探索和动态重新规划。举一个论文中提到的生动例子：在预订酒店时，一个“深度思考”的智能体可能会对自己找到的第一个看起来不错的酒店进行详尽的分析和推理，但最终可能错过一个需要多点击一次才能发现的、性价比更高的选择。而一个“广度行动”的智能体则可以通过浏览更多列表、比较不同选项，从而获得更全面的信息，做出更优决策。\n然而，仅仅“允许”智能体行动更多步是不够的。如果缺乏有效的训练，智能体可能会陷入盲目探索（比如漫无目的地点击链接），浪费了交互机会，甚至导致性能下降。这正是论文的另一大核心贡献所在——提出了一种名为 TTI（Test-Time Interaction） 的训练方法。这是一种基于课程学习（Curriculum Learning）的在线强化学习框架。它的精妙之处在于，它不是从一开始就让智能体在复杂的长交互序列中学习，而是动态地调整其“交互视界（interaction horizon）”。\n具体来说，TTI的训练流程是： 1. 从短视界开始：初始阶段，限制智能体在较少的步数内完成任务。这迫使它首先学习最高效、最直接的“利用性（exploitation）”策略，掌握核心任务技能。 2. 逐步扩展视界：随着训练的进行，TTI会根据一个预设的“课程表”（schedule），逐渐增加允许的最大交互步数。论文通过实验发现（见表2），采用乘法式（multiplicative）的增长策略（如10步 -\u0026gt; 20步 -\u0026gt; 30步）比加法式（additive，如10 -\u0026gt; 11 -\u0026gt; 12）效果更好，因为它能更快地将智能体暴露在需要“探索性（exploration）”行为的复杂场景中。 3. 在线强化学习：整个过程采用了一种简单的在线强化学习算法——过滤式行为克隆（filtered behavior cloning），即智能体在环境中自主探索，只有成功完成任务的轨迹才会被用来更新模型。\n通过这种“先易后难”的课程设计，TTI有效地解决了长视界训练中因奖励稀疏和信用分配困难导致的学习效率低下问题。智能体首先在短视界中建立了“什么是成功”的基本认知，然后在更长的视界中，在此基础上学习如何通过探索和回溯等复杂行为来达成目标。\n论文通过在WebVoyager和WebArena这两个极具挑战性的网页浏览基准上的大量实验，雄辩地证明了TTI的有效性。如下图3所示，论文清晰地展示了在固定的计算预算（以总Token数衡量）下，将预算分配给更多的交互步骤（Interaction Scaling，绿色曲线），比分配给每一步更长的推理（Per-Step Budget Forcing，蓝色曲线）或多次采样（Per-Step Best-of-N，橙色曲线）能带来持续且显著的性能提升。\n图3解读：此图是论文核心论点的关键证据。上图显示，随着总计算预算（X轴，对数坐标）的增加，交互扩展（绿色）的成功率（Y轴）增长最快、天花板最高。下图则将计算成本分解为“每步Token数”（Y轴）和“总步数”（X轴），清晰地表明交互扩展策略是将计算资源投入到X轴的增长，而传统方法则是投入到Y轴的增长。实验证明，前者在交互式任务中效率更高。\n最终，使用Gemma-3 12B模型并通过TTI训练的智能体，在WebVoyager上取得了64.8%的成功率，比之前最好的开源模型提升了19.6%（见表3）。在WebArena上，TTI同样在所有开源自改进模型中取得了最佳性能（见表4）。这些坚实的数据证明，“做得更多”不仅是一个新颖的理念，更是一条通往更强大、更自适应AI智能体的有效路径。\n总而言之，这篇论文的贡献是多方面的：它首先在概念上提出了“交互扩展”这一新的智能体能力标度，并将其与传统的“计算扩展”进行了清晰的区分和对比；然后，它设计了TTI这一创新的、基于课程学习的训练框架来有效实现交互扩展；最后，通过在权威基准上取得的SOTA（State-of-the-Art）结果，为该方法的有效性和优越性提供了强有力的实证支持。\n接下来，我将按照您的要求，从六个方面进行更详细的解读。\n一、研究目标与行业意义 研究目标： 核心目标是解决当前主流AI智能体在部分可观察（partially observable）和非平稳（non-stationary）环境中适应性不足的问题。 具体来说，论文旨在构建一种能够通过与环境进行更长时间、更丰富的交互来主动收集信息、适应环境变化并动态调整自身行为的智能体。 解决的实际问题： \u0026gt; As the paper states: “current post-training approaches produce reactive agents that respond to immediate observations but struggle with evolving or uncertain task dynamics.” \u0026gt; 当前的智能体大多是反应式（reactive）的。它们根据当前所见的屏幕或状态进行决策，但如果任务需要的信息没有一次性全部展示出来（例如，在网页上需要滚动、点击多个标签页才能找到所有信息），或者环境状态会随时间变化（例如，机票价格波动），这类智能体就很容易失败。 行业意义： 推动智能体从“工具”向“伙伴”转变：这项研究是实现真正自主AI智能体的关键一步。一个只能“思考”的AI更像一个高级计算器或搜索引擎，而一个懂得如何去“行动”和“探索”的AI，才更接近能够独立完成复杂任务的自主伙伴。 解锁更广泛的应用场景：目前AI在静态问答、文本生成上表现优异，但在需要与真实世界软件（如浏览器、Office套件、各类专业软件）进行多步交互的任务上仍显稚嫩。该研究提出的方法有望大幅提升AI在这些领域的实用性，例如实现真正的自动化网页数据抓取、软件测试、客户服务流程等。 提供新的模型评估和优化维度：论文提出的“交互扩展”为评估和提升大模型能力提供了一个全新的视角。未来的模型发展，除了要看它的推理能力（IQ），可能还要看它的交互和适应能力（EQ，或称为“执行智能”）。 二、新思路、新方法与优势 新的思路： “思考”vs.“行动”的权衡（Thinking vs. Doing Tradeoff）：这是论文提出的最核心的新思路。它明确指出，在资源（计算预算）有限的情况下，智能体需要在“想得更深”和“做得更多”之间做出权衡。论文论证了在交互式任务中，后者往往是更优的选择。 新的方法： TTI (Test-Time Interaction)：这是实现“交互扩展”的具体方法论。其核心是基于交互视界的课程学习。 \u0026gt; “we define curriculum progression in terms of the maximum number of steps an agent is allowed per trajectory.” 与之前方法的区别和优势： 对比传统Test-Time Scaling方法（如更长的CoT）：传统方法在已有信息上进行深度加工，属于“内化思考”。TTI则是通过行动获取新信息，属于“外化探索”。TTI能够解决信息不全的问题，这是传统方法无法做到的。 对比传统RL训练方法：很多RL方法采用固定的、较长的交互视界（horizon）进行训练，但这面临着信用分配困难、学习效率低下的问题。TTI的课程学习机制通过从短视界过渡到长视界，完美地规避了这一难题，实现了先学习利用（exploitation），再学习探索（exploration）的有效训练路径。如论文图4所示，固定短视界（h=5）训练的智能体虽然上手快，但很快过拟合，无法处理复杂任务；而固定长视界（h=20）训练的智能体学习缓慢且不稳定。TTI则兼具二者之长。 对比其他课程学习方法：以往的课程学习多基于任务的语义难度（如指令的复杂性）来设计课程。TTI则另辟蹊径，使用交互长度作为任务难度的代理（proxy），这种方法不依赖于对任务本身的复杂评估，更加简洁和通用。 三、实验设计与关键结果 论文的实验设计非常严谨，分为两个层次：小规模的受控实验和大规模的基准测试。\n实验设计： 受控实验（验证核心假设）： 平台：在WebArena的一个子集上进行。 目的：直接对比“交互扩展”与“计算扩展”的效率。 方法： 交互扩展：设计了一种“再次检查（check-again）”的prompting策略，在智能体决定结束任务时，提示它“再思考一下”，从而诱导其进行更多的交互步骤。 计算扩展：采用了两种经典方法，一是“预算强制（budget forcing）”，强迫模型在每一步生成更长的思考过程；二是“最佳N选（best-of-n）”，在每一步生成多个候选动作并投票。 衡量标准：在相似的总Token消耗下，比较不同策略的任务成功率。 大规模实验（验证TTI方法的SOTA性能）： 平台：完整的WebVoyager和WebArena基准。 目的：证明TTI训练框架的有效性和先进性。 方法：使用Gemma-3 12B作为基础模型，利用TTI框架进行端到端训练。并与以下基线进行对比： Zero-shot（零样本）基线。 采用固定短视界（h=10）和固定长视界（h=30）训练的基线。 其他已发表的SOTA开源模型。 实验数据和结果： 在受控实验中，如下图表所示（根据论文图3数据整理），交互扩展策略的效率远超其他两种计算扩展策略。 策略 任务成功率 (SR) 特点 Interaction Scaling ~29% 随着预算增加，性能稳定提升，效率最高 Per-Step Budget Forcing ~26% 性能提升有限，很快达到平台期 Per-Step Best-of-N ~25% 计算成本最高，但性能提升最小 * 在大规模实验中，TTI表现出色： * **WebVoyager (表3)**：TTI模型达到**64.8%**的成功率，而之前的最佳开源模型（PAE-LLaVa-34B）为33.0%。这不仅是巨大的性能飞跃，也显著优于采用固定视界训练的模型（h=10时为59.1%，h=30时为45.2%）。 * **WebArena (表4)**：TTI模型达到**26.1%**的成功率，虽然绝对值不高（反映了该基准的挑战性），但在所有开源自改进模型中排名第一，比Zero-shot基线（18.3%）提升了**7.8%**。 四、未来探索、挑战与机遇 值得探索的问题和挑战： 思考与行动的最佳平衡：论文证明了“行动”的重要性，但未来的终极智能体需要在“思考”和“行动”之间实现动态、智能的平衡。如何让智能体学会在何时需要深度思考，在何时需要快速行动？这是一个关键的开放性问题。 更高效的强化学习算法：论文使用的是相对简单的过滤式行为克隆。未来可以探索更先进的RL算法（如PPO、价值函数方法），以更有效地处理长视界任务中的信用分配和非平稳性问题。 记忆与上下文长度：更长的交互意味着更长的历史记录，这对模型的上下文窗口（Context Length）和记忆能力提出了巨大挑战。如何设计有效的记忆机制来应对这一问题至关重要。 跨领域泛化：如何将TTI的思想从网页浏览扩展到更广泛的领域，如机器人控制、科学计算、游戏等，这些领域的环境随机性和不确定性更高。 新的技术和投资机会： 智能体训练与评估平台：随着交互式智能体研究的深入，对高质量、高保真、可扩展的模拟环境的需求将激增。投资开发这样的平台将具有巨大价值。 “交互中心”的模型架构：可能会催生出专门为交互而优化的模型架构，例如，模型内部包含专门负责规划、探索、记忆等不同功能的模块。 Meta-RL for Agents：开发能够自动学习“如何学习”的元强化学习算法，让智能体自主发现思考与行动的最佳权衡策略，而不是依赖人工设计的课程。 可解释性与安全性：随着智能体自主性增强，如何确保其行为可控、可解释、安全可靠，将成为一个重要的研究和商业应用领域。 五、批判性视角：不足与存疑 尽管这是一篇非常出色的论文，但从批判性的角度看，仍存在一些可以深入探讨的方面：\n对失败案例的分析不够深入： \u0026gt; As noted in Section 6.2, “the agent treats search as a universal fallback, even when more domain-specific actions… would be more effective.” 论文提到，智能体在失败时倾向于过度依赖“重置”（即返回搜索引擎），而不是尝试更细致的领域内恢复策略。这表明其探索行为还不够“智能”，更像是一种“穷举”而非“启发式”的探索。论文对此的讨论可以更深入，例如分析导致这种行为的训练数据偏差或算法局限。 自我验证能力的局限性： 论文坦诚，智能体有时即使意识到了答案与任务要求不匹配（例如，找到了2021年的项目，而任务要求2022年），仍然会提交错误答案。这说明TTI虽然增强了“行动”能力，但在最后一步“决策”的认知和验证环节仍有不足。这可能是因为更长的交互历史反而对最终决策造成了干扰。 对合成数据的依赖： 大规模训练依赖于模型自己生成的合成数据。虽然这展示了“自我提升”的潜力，但合成数据与真实、多样的人类交互数据之间仍存在差距。这或许可以解释为什么TTI的性能（如在WebVoyager上的64.8%）与那些可能使用了私有、高质量人类标注数据训练的闭源模型（如UI-TARS的84.8%）仍有显著差距。 泛化性存疑： 所有实验都基于Gemma-3模型。TTI作为一个训练框架，其有效性在多大程度上可以迁移到其他不同架构、不同大小的模型上，还需要进一步的实验验证。 六、核心启发与知识补充 我应该从这篇论文中学到什么？ 思维范式的转变：最核心的启发是，在设计和评估AI智能体时，必须跳出“唯推理论”的框架。交互能力和推理能力同等重要，甚至在很多实际场景中前者更为关键。 课程学习的实践价值：TTI的成功雄辩地证明了课程学习在训练复杂智能体时的巨大威力。“先易后难，逐步放开” 的思想是一种普适且强大的工程实践，可以应用在很多其他复杂的AI训练任务中。 效率意识：在设计智能体时，要时刻考虑计算预算的“性价比”。论文提供了一个很好的分析范例：如何将有限的资源（Tokens）投入到最能提升性能的环节。 敢于质疑主流：这篇论文敢于挑战当时“CoT越长越好”的主流观点，并另辟蹊径，最终取得了突破。这种批判性思维和创新精神值得学习。 我还需要补充了解哪些背景知识？ 强化学习（Reinforcement Learning）基础：特别是策略梯度（Policy Gradient）方法，如REINFORCE，以及RL中的核心概念，如视界（horizon）、奖励（reward）、策略（policy）、探索与利用（exploration vs. exploitation）等。 课程学习（Curriculum Learning）：了解其基本思想和在机器学习领域中的各种应用，有助于更好地理解TTI的设计动机。 AI智能体基准（Agent Benchmarks）：对WebArena和WebVoyager这两个平台有所了解，能帮助你更直观地理解论文实验的难度和其结果的含金量。 大模型作为智能体（LLM as Agents）：了解当前该领域的基本框架，包括基于Prompting的方法、基于ReAct（Reason+Act）框架的方法等，能让你更清晰地看到这篇论文所处的技术脉络。 ","date":"2025-08-02T00:00:00Z","permalink":"/thinking-vs.-doingagents-that-reason-by-scaling-test-time-interaction/","title":"Thinking vs. Doing：Agents that Reason by Scaling Test-Time Interaction"},{"content":"长尾分布问题的解决和熵坍塌的解决方案。\n这篇来自腾讯混元团队的论文（Du et al., 2025）聚焦于一个当前大模型领域的前沿核心问题：如何通过强化学习（Reinforcement Learning, RL）有效提升模型的复杂推理能力。我们知道，要让模型解决复杂的数学题或编写复杂的代码，往往需要它生成一个非常长的、包含详细推理步骤的答案，这就是所谓的“超长输出”（Ultra-Long Output）。然而，传统的强化学习方法在处理这种超长输出时会遇到两大瓶颈：训练效率低下和熵坍塌（Entropy Collapse）。\n论文的核心贡献在于提出了一套名为 UloRL (Ultra-Long Output Reinforcement Learning) 的创新性解决方案，它巧妙地化解了上述两大难题。\n第一个难题是训练效率。 在传统的RL训练中，系统需要等待一批次（batch）中所有样本都生成完毕后才能进行模型更新。如果这个批次里有一个样本需要生成128k长度的超长序列，而其他样本只需要8k，那么所有样本都必须等待那个最长的样本完成，这造成了巨大的计算资源浪费和时间延迟。论文称之为“长尾分布导致的瓶颈”。UloRL对此提出的解决方案是 分段生成（Segment Rollout）。它不再一次性生成完整的超长答案，而是将其切分成多个固定长度的“段落”（例如，将128k的输出切分为8个16k的段落）。每生成完一个段落，完成了的样本就可以立刻进入“经验池”用于训练，而未完成的则继续生成下一个段落。这种“即生即训”的流水线模式极大地提升了训练效率。论文中的数据显示，通过分段，训练速度最多可以提升 2.06倍。 当然，分段生成也带来了新的技术挑战：一个完整的样本可能由不同时期的模型（模型在训练过程中不断更新）生成，这破坏了传统强化学习算法（如PPO）中重要性采样的假设。为此，论文提出了 伪在线策略重要性采样（Pseudo On-Policy Importance Sampling, POIS）。这是一种务实且有效的简化方法，它将一个样本中所有段落都“视作”是由最新的模型生成的，从而简化了计算，并巧妙地利用了在线训练（on-policy training）的优势来维持训练稳定性。\n第二个难题是熵坍塌。 熵在模型训练中可以通俗地理解为“多样性”或“探索能力”。在强化学习中，当模型因为生成了正确答案而获得奖励时，它会倾向于更加“自信”地走这条路。如果对已经掌握得很好的正确答案（即模型能以非常高的概率生成正确的词元）持续进行奖励和训练，模型的预测分布会变得越来越尖锐，即熵值急剧下降。这会导致模型失去探索其他可能解法的能力，思想变得“僵化”，最终损害其泛化和推理能力。 UloRL的另一个核心创新 动态掩码熟练掌握正向词元（Dynamic Masking of well-Mastered Positive Tokens, DMMPTs） 正是为此而生。论文作者敏锐地指出，熵坍塌的根源并非“训练正样本”，而是“过度训练已经熟练掌握的正向词元（MPTs）”。MPTs指的是那些在正确答案中，且模型已经能以极高概率（如\u0026gt;99%）预测出来的词元。DMMPTs为此设计了一个“熵值恒温器”： 1. 设定一个理想的“目标熵值”。 2. 在训练时，如果模型的当前熵低于这个目标值，说明模型开始变得“僵化”了。此时，系统会自动“屏蔽”（mask）掉那些MPTs，不再对它们进行训练，迫使模型关注那些还未掌握好的部分。 3. 如果模型熵值高于或等于目标值，则正常进行训练。\n这种动态调节机制，既避免了模型“思想僵化”，又不会因为盲目追求多样性而损害性能，从而在探索（exploration）和利用（exploitation）之间取得了精妙的平衡。实验图表清晰地显示，采用DMMPTs后，模型的熵值在整个训练过程中都稳定在目标水平附近。\n最终，通过这些方法的结合，UloRL取得了令人瞩目的成果。在Qwen3-30B-A3B这样一个300亿参数的模型上，经过UloRL训练后，其在AIME-2025数学推理基准测试上的准确率从 70.9% 飙升至 85.1%。这一成绩不仅远超其自身的基础水平，甚至超过了参数量为其近8倍的Qwen3-235B-A22B模型（81.5%），充分展示了UloRL在提升模型推理能力方面的卓越效果和高效率。\n总而言之，这篇论文不仅仅是提出了一两个孤立的技巧，而是围绕“如何高效、稳定地进行超长输出的强化学习”这一核心目标，构建了一套环环相扣、逻辑自洽且经过充分实验验证的系统性解决方案。它为如何通过RL释放大模型在复杂推理任务上的潜力，提供了一个非常清晰和可行的技术路线图。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是开发一种高效且稳定的强化学习方法，专门用于处理超长输出序列（ultra-long output sequences），从而显著提升大型语言模型（LLMs）在复杂推理任务（如数学、编程）上的能力。\n解决的实际问题：\n训练效率瓶颈：传统的RL框架在处理需要不同长度输出的样本时，必须等待最长的样本生成完毕，这在处理包含少量超长输出（如128k tokens）和大量较短输出的混合数据时，会导致严重的训练延迟。 熵坍塌问题：在RL训练中，模型为了追求奖励，容易对已知的正确答案路径产生过拟合，导致其输出多样性（熵）急剧下降，从而丧失探索更优解的能力，最终限制了其性能上限。 对行业发展的重要意义：\n解锁复杂推理的潜力：许多前沿的AI应用，如科学发现、高级编程辅助、复杂问题求解，都需要模型具备长链条、多步骤的推理能力。这些任务的输出本质上就是超长的。解决超长输出的训练难题，是推动LLM从“知识问答”走向“复杂问题解决者”的关键一步。 提升训练的经济性与可行性：RL训练本就成本高昂。UloRL提出的分段生成等方法，能够将训练速度提升一倍以上，这意味着可以用更少的计算资源和时间，达到甚至超过以往需要更大规模集群才能实现的效果。这降低了进行前沿大模型研究和开发的门槛，使得更多机构有能力训练出具备高级推理能力的模型。 提供新的模型优化范式：DMMPTs提出的“熵恒温器”思想，为解决RL训练中的稳定性问题提供了一个全新的、优雅的视角。它超越了简单地在损失函数中增加一个熵正则项的传统做法，更加精准地定位了问题的根源。这一思想可能启发更多关于模型训练动态调控的研究，对于未来更稳定、更高效的AI训练算法具有重要价值。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文提出了一套名为UloRL的系统性方法，其核心由以下几个创新点构成：\n分段生成 (Segment Rollout) 思路：将超长序列的生成过程分解为多个阶段。在每个阶段只生成一个固定长度的“段落”。 与之前方法的对比：传统方法是“一次性生成”，需要等待一个批次中所有样本（无论长短）全部生成完毕。K1.5等工作虽然提出了部分生成的概念，但论文指出其缺乏实现细节，难以复现。 特点与优势： \u0026gt; By decoding only a much shorter segment at each step, our method allows samples that have completed decoding to enter the experience pool for training immediately, while unfinished samples continue decoding in subsequent iterations. 这种“流水线”作业模式打破了长尾样本对整个批次的阻塞，极大地提高了GPU利用率和训练吞吐量。如论文 Table 1 所示，当将一个长序列分为4段时，每步的训练时间从1240秒缩短到601秒，速度提升了 2.06倍。 伪在线策略重要性采样 (Pseudo On-policy Importance Sampling, POIS) 思路：在分段生成的设定下，一个完整的样本可能由不同版本的模型生成（即部分是“过时”的，或称off-policy）。为了简化计算并利用on-policy训练的稳定性，POIS大胆地将所有段落的生成模型（π_θold）都视为当前的最新模型。 与之前方法的对比：标准的off-policy修正（如PPO）需要精确计算每个词元的重要性采样比率，在分段生成下会变得很复杂。而纯粹的on-policy训练虽然稳定，但无法利用历史数据。POIS是介于两者之间的一种务实选择。 特点与优势：该方法极大地简化了重要性采样的计算，使得所有词元的权重都变为1，避免了PPO中可能导致梯度裁剪和信息损失的clipping操作。实验证明，尤其是在长输出场景下，POIS的效果优于更精确的“分段感知重要性采样（SAIS）”，因为它能更好地维持熵的稳定性。 动态掩码熟练掌握正向词元 (Dynamic Masking of well-Mastered Positive Tokens, DMMPTs) 思路：熵坍塌的元凶是“过度训练模型已熟练掌握的正确词元（MPTs）”。因此，通过设置一个目标熵σ，当模型实际熵低于σ时，就暂时“屏蔽”掉MPTs，不对它们进行更新。 与之前方法的对比： 传统方法通常是在损失函数中加入一个固定的熵正则项，但这可能与提升推理能力的目标不完全一致，可能损害性能上限。 DAPO等方法通过放宽裁剪阈值来允许更多样化的词元参与训练，但这种方法在on-policy设置下无效。 特点与优势：DMMPTs是一种更精准、自适应的熵调节机制。它不是盲目地追求高熵，而是动态地维持熵在一个健康的水平。从 Figure 3 的思想示意图可以看出，其目的是避免在已经很尖锐的概率分布上“火上浇油”。Figure 5 的实验结果完美地验证了这一点，在引入DMMPTs后，不同大小模型的熵都能稳定在预设的目标值0.2附近。 此外，论文还提到了使用生成式验证器（Generative Verifier Model） 和一系列数据清洗策略，这体现了其工作的系统性和工程严谨性，确保了奖励信号的准确性，这也是其实践效果出色的重要保障。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列设计精良的实验和消融研究（ablation studies）来验证UloRL的有效性。\n实验设计：\n基础模型：主要使用 Qwen3-30B-A3B 模型进行训练和评估。 评测基准：使用了两个在业界公认的、衡量高级推理能力的基准测试：AIME-2025（一个高难度的数学竞赛问题集）和 BeyondAIME。 对比对象： 自身基线：与未经UloRL训练的Qwen3-30B-A3B模型进行对比。 其他SOTA模型：与当时最先进的模型，如DeepSeek-R1-0528，以及更强大的Qwen3-235B-A22B模型进行比较。 消融研究：为了验证UloRL中关键组件（特别是DMMPTs）的必要性，他们设计了UloRL-A3B-w/o-DMMPTs（即不使用DMMPTs的UloRL版本）进行对比。 参数影响分析：探究了不同输出长度（32k, 64k, 96k, 128k）对模型性能的影响。 实验数据和结果：\n总体性能验证： 如下方 Table 2 的关键数据显示，UloRL的效果非常显著。 \u0026gt; On the Qwen3-30B-A3B model, … the model’s performance on AIME2025 improved from 70.9% to 85.1% and on BeyondAIME from 50.7% to 61.9%, even surpassing Qwen3-235B-A22B with remarkable gains.\nUloRL-A3B-128k 在AIME-2025上达到 82.8%（最终版本达到85.1%），远高于基线的70.9%。 这个成绩甚至超过了参数量大得多的 Qwen3-235B-A22B（81.5%），实现了“以小博大”的壮举。 Model AIME-2025 BeyondAIME AVG Qwen3-30B-A3B (Baseline) 70.9 50.7* 60.8 Qwen3-235B-A22B (Larger Model) 81.5 59.0* 70.3 UloRL-A3B-128k (Ours) 82.8 60.2 71.5 UloRL-A3B-w/o-DMMPTs (Ablation) 78.6 57.1 67.9 UloRL-A3B-128k-Yarn (Extended) 85.1 61.9 73.5 *\n表1: UloRL 核心性能对比 (改编自原文 Table 2)\nDMMPTs组件的有效性验证： 通过比较UloRL-A3B-128k和UloRL-A3B-w/o-DMMPTs，可以发现，在移除了DMMPTs后，AIME-2025的性能从82.8%显著下降到78.6%。这强有力地证明了DMMPTs对于稳定训练和达到最佳性能至关重要。\n输出长度的影响验证： Table 3 的结果揭示了一个清晰的趋势：随着RL训练中允许的输出长度增加，模型的推理性能也随之提升。 \u0026gt; Overall, the results show a clear trend: the longer the output length, the better the model’s reasoning performance.\nModel (trained on Qwen3-30B-A3B) Output Length AIME-2025 UloRL-A3B-32k 32k 73.5 UloRL-A3B-64k 64k 79.9 UloRL-A3B-96k 96k 81.6 UloRL-A3B-128k 128k 82.8 *\n表2: 输出长度对性能的影响 (改编自原文 Table 3)\n这个实验结果验证了论文的初始动机——即“提升超长输出能力是提升推理的关键”，并证明了UloRL正是实现这一目标的有效工具。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这个研究方向充满了潜力和机遇，未来值得探索的问题包括：\n问题与挑战： 熵调节的智能化：目前DMMPTs中的目标熵σ和MPTs的概率阈值τ是预先设定的超参数。未来能否让模型动态地、自适应地学习最适合当前任务和训练阶段的熵目标？这可能需要引入更高阶的优化策略或元学习（Meta-Learning）。 分段策略的优化：目前的分段是固定长度的。能否发展出动态分段策略？例如，在推理的简单部分使用较短的段落以快速通过，在关键的复杂步骤使用更长的段落进行精细生成。 奖励模型的进一步提升：论文使用了生成式验证器，但验证器的准确性始终是RLHF（及RLVR）的天花板。如何开发出能理解更抽象、更复杂推理过程正确性的验证器，尤其是在缺乏明确答案的探索性任务中，是一个巨大的挑战。 跨领域通用性：UloRL在数学推理上取得了巨大成功。它在其他需要长程依赖和推理的领域，如法律文书分析、长篇小说创作、生物信息学（如蛋白质折叠路径分析）中的效果如何，有待进一步验证和适配。 与模型架构的协同进化：当前的RL方法是应用于固定架构的模型之上的。未来，模型架构（如Attention机制）是否可以为超长推理进行特殊优化，以便与UloRL这类训练方法更好地协同工作？ 新技术和投资机会： 推理即服务（Reasoning-as-a-Service, RaaS）：随着模型推理能力的增强，未来可能会出现专门提供高阶推理能力API服务的商业模式。企业无需自己训练庞大的推理模型，可以直接调用API来解决其业务中的复杂规划、优化和分析问题。 垂直领域专业模型：在UloRL这类技术的加持下，开发针对特定垂直领域（如药物研发、材料科学、金融建模、辅助编程）的专用推理模型将变得更加可行。这些模型将成为该领域专家的强大助手，催生巨大的商业价值。 新一代AI训练平台：提供包含UloRL这类先进训练策略（如高效长序列处理、动态熵调节）的AI开发和训练平台，将对AI公司和研究机构产生巨大吸引力。这将是AI基础设施领域的一个重要投资方向。 自动化科学发现：具备强大推理能力的AI模型，有望在基础科学领域扮演更重要的角色，例如提出新的科学假说、设计实验方案、分析海量实验数据等，加速科学发现的进程。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从严谨的学术批判角度看，仍存在一些可以探讨和改进的空间：\n超参数的敏感性分析缺失：论文中设定了τ = 0.99（MPTs的概率阈值）和σ = 0.2（目标熵）。这两个关键超参数是如何选定的？最终性能对这两个值的变化有多敏感？论文缺乏对这些超参数的敏感性分析，这使得读者不清楚该方法是否具有良好的鲁棒性，以及在其他模型或任务上复现时应如何调整这些参数。 POIS的理论解释相对薄弱：对于POIS方法，论文的论证更多是基于“实验结果很好”的经验主义。为什么将部分off-policy的样本近似为on-policy对待，没有对训练造成更严重的负面影响？虽然论文给出了一些直觉解释，但缺乏更深入的理论分析或更详尽的梯度行为分析，来阐明其成功背后的深层机制。 评测维度的单一性：论文的成功主要是在AIME和BeyondAIME这两个数学推理基准上展示的。虽然这足以证明其在数理逻辑推理上的强大，但我们仍会存疑：这种能力的提升能否泛化到其他类型的推理任务上？例如，常识推理、法律推理或需要创造性思维的开放式问题。增加更多样化的评测维度将使结论更有说服力。 系统的复杂性：UloRL是一个包含分段生成、POIS、DMMPTs、生成式验证器和复杂数据清洗流程的综合系统。这种复杂性可能会增加复现和应用的难度。与一些更简洁的单一算法相比，它的调试和维护成本可能会更高。 对“伪负样本”的讨论不足：在RLVR中，一个常见的挑战是“伪负样本”，即模型给出了一个语法正确、逻辑看似合理但最终答案错误的解法。论文虽然通过数据清洗和验证器来提升奖励质量，但没有深入讨论UloRL如何处理这类细微但关键的错误，以及其对模型最终能力边界的影响。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文充满了可以直接借鉴和启发的宝贵思想。\n重点学习和启发的创新点： “分而治之”的工程智慧：分段生成（Segment Rollout） 是一个极其有价值的工程思想，它不仅仅适用于RL。在任何需要处理长短不一序列的批处理任务中（例如长文档翻译、长文本摘要），都可以借鉴这种“流水线”模式来优化计算资源的利用效率。这是一个通用性很强的性能优化技巧。 “温故而知新，不必温故而温故”的训练哲学：DMMPTs 的核心思想——不要在模型已经完全掌握的事情上浪费训练资源——极具启发性。这是一种高级的课程学习（Curriculum Learning）或数据选择策略。在你的训练任务中，如果发现模型在某些简单或重复的样本上过拟合或loss已经很低，可以考虑借鉴DMMPTs，动态地降低这些样本的训练权重甚至将其暂时移出训练集，从而迫使模型聚焦于更有挑战性的“硬样本”，这有助于防止过拟合，提升模型的泛化能力和稳定性。 “奖励信号质量是第一生命线”：论文花了相当大的篇幅（3.3和3.4节）来描述他们如何构建生成式验证器和进行数据清洗。这提醒我们，在任何基于反馈的學習系統中，算法设计和信号质量同等重要。在应用类似技术时，投入大量精力去打磨你的“奖励函数”或“验证器”是绝对值得的，所谓“Garbage in, garbage out”。 务实主义的简化：POIS 的成功告诉我们，在复杂的系统中，有时一个经过深思熟虑的、务实的简化，其效果可能胜过一个理论上完美但实现复杂的方案。在工程实践中，要敢于尝试这类“近似”和“简化”，并用实验结果来验证其有效性。 需要补充了解的背景知识： 强化学习基础，特别是PPO：要完全理解UloRL的动机和设计，你需要对强化学习的基本概念（Policy, Reward, Value Function）以及近端策略优化（Proximal Policy Optimization, PPO） 算法有深入的了解。PPO是当前LLM强化学习中最主流的算法之一，UloRL中的很多概念（如重要性采样、clipping）都是在PPO的框架下进行的改进。建议阅读PPO的原始论文 Schulman et al., 2017。 广义优势估计（GAE）：这是PPO中用于估计优势函数（Advantage Function）的标准方法，理解它有助于你明白RL中奖励和价值是如何被评估的。 语言模型中的熵（Entropy）：理解信息熵在语言模型中的含义，即它如何量化模型输出的“不确定性”或“多样性”，是理解“熵坍塌”问题和DMMPTs解决方案的关键。 RLHF（Reinforcement Learning from Human Feedback）：了解RLHF的基本流程，因为它开创了通过反馈来对齐LLM的范式。UloRL所基于的RLVR（Reinforcement Learning with Verifiable Rewards）可以看作是RLHF在有明确对错答案场景下的一种特殊形式。 ","date":"2025-07-31T00:00:00Z","permalink":"/ulorlan-ultra-long-output-reinforcement-learning-approach-for-advancing-large-language-models-reasoning-abilities/","title":"UloRL：An Ultra-Long Output Reinforcement Learning Approach for Advancing Large Language Models’ Reasoning Abilities"},{"content":"思想类似于First Return, Entropy-Eliciting Explore\n论文深度解读：AGENTIC REINFORCED POLICY OPTIMIZATION (ARPO) 这篇论文的核心目标是解决当前大型语言模型（LLM）在作为智能体（Agent）与外部工具（如搜索引擎、代码解释器）进行多轮交互时，强化学习（RL）训练效率低下的问题。作者观察到了一个关键现象：当LLM接收到工具返回的信息后，它在生成后续内容时的“不确定性”会显著增加。这种不确定性，论文通过词元熵（Token Entropy）这一指标来量化。传统的智能体强化学习方法，如GRPO或DAPO，通常采用轨迹级（trajectory-level）的优化方式，即执行一整套完整的“思考-行动”序列后，根据最终结果给予一次性的奖励或惩罚。这种方法的弊端在于，它平均了整个轨迹中所有决策步骤的功过，忽略了在某些关键节点（尤其是工具调用后）进行更细致探索的必要性。\nARPO的创新之处在于，它将“不确定性”作为探索的“指南针”，提出了一种基于熵的自适应部署（Entropy-based Adaptive Rollout）机制。这个机制的核心思想是：与其低效地对整个轨迹进行完整采样，不如将宝贵的计算资源（采样预算）动态地分配到模型最“困惑”的时刻。\n正如论文在摘要中所说： Motivated by this observation, ARPO incorporates an entropy-based adaptive rollout mechanism, dynamically balancing global trajectory sampling and step-level sampling, thereby promoting exploration at steps with high uncertainty after tool usage. （受此观察启发，ARPO集成了一种基于熵的自适应部署机制，动态平衡全局轨迹采样和步级采样，从而促进在工具使用后的高不确定性步骤进行探索。）\nimage.png 具体来说，ARPO在训练过程中会实时监控模型生成词元的熵值变化。一旦检测到在某次工具调用后，熵值超过一个预设的阈值，意味着模型对如何处理新信息感到“迷茫”，ARPO便会在此处进行分支采样（Branching）。它会从这个高熵节点开始，生成多条不同的后续行为路径（partial sampling），而不是像传统方法那样只沿着一条路走到底。这种设计精妙地平衡了全局采样（Global Sampling）（探索大的战略方向）和步级采样（Step-level Sampling）（在关键决策点进行战术深挖），极大地丰富了探索的多样性。\n为了配合这种新颖的采样机制，ARPO还引入了优势归因估计（Advantage Attribution Estimation）。当从一个节点分支出多条路径后，如何公平地评估这些路径并把奖励信号正确地传导回模型的策略网络，就成了一个新问题。ARPO巧妙地利用了群组相对策略优化（GRPO）的思想，对分支前的共享路径部分和分支后的独立路径部分，分配不同但相关的优势值（Advantage）。简单来说，在共享路径上的词元，其“功劳”是所有后续分支路径的平均体现；而在各个独立分支上的词元，则获得其所在路径的独特“功劳”。这使得模型能够更精确地学习到在关键步骤中，哪种行为能带来更高的回报。\n论文通过详尽的实验证明了ARPO的有效性。作者在横跨计算推理、知识推理和深度搜索三大领域的13个极具挑战性的基准测试集上进行了评估。结果非常亮眼，例如，在Qwen2.5-7B模型上，ARPO的平均性能比GRPO和DAPO等主流轨迹级RL算法高出约4%（见Table 1）。更令人印象深刻的是其效率的提升。如下图所示，论文中的Figure 7清晰地表明，ARPO在达到甚至超越基线模型性能的同时，所需的工具调用预算（Tool-use Budget）仅为传统方法的一半。\n方法 工具调用总数 (近似) 性能 (奖励分数) Qwen2.5-7B w. GRPO ~450-500 ~0.4 Qwen2.5-7B w. ARPO ~250-300 ~0.5-0.6 数据来源：论文Figure 7，展示了训练过程中的工具调用效率对比\n这意味着ARPO不仅效果更好，而且训练成本更低，这对于需要大量与外部API（通常是收费的）交互的真实世界智能体应用来说，具有巨大的实践价值。总而言之，ARPO通过“在最需要的地方进行最深入的思考”这一哲学，为智能体强化学习领域提供了一个更高效、更具扩展性的新范式。\n接下来，我将按照您提出的六个问题，对论文进行更详细的结构化解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？ 研究目标：这篇论文的核心研究目标是为基于大型语言模型（LLM）的多轮工具使用智能体（multi-turn LLM-based agents）设计一个更高效、更具扩展性的强化学习（RL）算法。\n解决的实际问题：\n训练效率低下：现有的智能体RL算法（如GRPO, DAPO）大多采用轨迹级优化，它们将智能体与环境的一次完整交互（一条轨迹）作为一个样本。这种方法在智能体需要进行多轮工具调用（例如，先搜索、再浏览网页、最后执行代码）的复杂任务中，样本效率极低，因为大部分决策步骤可能并非学习的关键。 探索不足：轨迹级方法忽略了智能体在与工具交互后，面对新信息时产生的“不确定性”。这些高不确定性的时刻恰恰是模型最需要学习和探索的关键决策点。传统方法在这些点上探索不足，导致模型难以学习到精细化的工具使用策略。 行业意义：\n降低智能体训练成本：构建强大的AI智能体需要大量的在线交互和学习，特别是与外部API（如谷歌搜索、代码执行环境）的交互往往伴随着高昂的计算或金钱成本。ARPO提出的方法能用更少的工具调用达到更好的效果，直接降低了研发成本。 加速通用人工智能（AGI）的进程：让AI能够像人一样使用工具、与真实世界互动是通往AGI的关键一步。这篇论文通过提升AI智能体的学习能力，使其能更有效地掌握复杂工具链，推动了更强大、更自主的AI系统的发展。 2. 论文提出了哪些新的思路、方法或模型？ 论文提出了一个名为Agentic Reinforced Policy Optimization (ARPO) 的新算法，其核心创新包含两个相互关联的部分：\n基于熵的自适应部署 (Entropy-based Adaptive Rollout) 新思路：核心思路是将模型的词元熵（Token Entropy）作为“不确定性”的代理指标。作者发现，LLM在接收到工具返回的信息后，其生成下一个词元的概率分布会变得更平坦，即熵值会显著升高。这表明模型正在努力整合新知识，处于一个关键的决策岔路口。 新方法：ARPO不采用固定的完整轨迹采样，而是设计了一个动态机制。 它首先进行少量的全局采样（global sampling），生成几条完整的轨迹。 在生成过程中，它会监控每个工具调用步骤之后的词元熵。 当熵值的变化量ΔHt超过一个阈值τ时（Pt = α + β · ΔHt \u0026gt; τ），ARPO会认为这是一个高价值的探索点，并立即从该点分支（Branch），生成Z条新的、较短的局部轨迹（partial rollouts）。 优势：与之前的方法相比，这种方法的优势在于智能地分配了计算资源。它将探索的重点从“盲目地走完多条全程”转向了“在关键的十字路口多想几种可能”，从而在有限的采样预算内，极大地增加了对关键决策步骤的探索深度和广度。 优势归因估计 (Advantage Attribution Estimation) 新思路：在引入分支采样后，如何为不同路径上的行为分配合理的奖励（即优势值）成为一个挑战。ARPO的思路是区分共享部分（分支前的路径）和独立部分（分支后的路径）。 新方法：论文探讨了两种策略：硬性分配（Hard Estimation）和软性分配（Soft Estimation）。最终，它采用了基于GRPO (Group Relative Policy Optimization)的软性分配方法。 \u0026gt; a straightforward approach is to explicitly distinguish the shared and individual parts of each trajectory at the advantage level… An elegant alternative… is to integrate the distinction between shared and individual token segments latently during policy optimization. 优势：通过GRPO的重要性采样比率（importance sampling ratio），共享路径上的词元会自然地获得一个“平均”的优势值，该值综合了其所有子分支的表现。而每个独立分支上的词元则会获得其专属的优势值。这比简单的硬性平均分配更优雅，也更稳定（如Figure 5所示），能让模型更平滑地学习到不同决策带来的细微差异。 3. 论文通过什么实验来验证所提出方法的有效性？ 论文设计了全面且严谨的实验来验证ARPO的有效性。\n实验设计： 任务领域：实验覆盖了三大类任务，以确保通用性： 数学推理 (Mathematical Reasoning): 如AIME2025, MATH500等。 知识密集型推理 (Knowledge-Intensive Reasoning): 如HotpotQA, WebWalker等。 深度搜索 (Deep Search): 如GAIA, HLE (Humanity’s Last Exam)等，这些任务需要多步的网页浏览和信息整合。 基线模型：对比的基线非常全面，包括： 直接推理 (Direct Reasoning)：不使用RL，仅用基础的LLM。 主流轨迹级RL算法：GRPO, DAPO, Reinforce++。 专门的LLM搜索智能体：如ReAct, WebThinker等。 评估设置：为了公平对比，所有方法都在相同的骨干模型（如Qwen, Llama系列）和相同的训练数据上进行训练。 实验数据和结果： 性能优越性：在Table 1中，ARPO在10个具有挑战性的推理任务上，一致性地超越了所有其他的RL算法。例如，在Llama3.1-8B模型上，ARPO的平均分达到了55.3，而表现最好的基线GRPO仅为51.1。 深度搜索能力：在更复杂的深度搜索任务中（Table 2），ARPO的优势更加明显。在Qwen3-14B模型上，ARPO在GAIA和WebWalkerQA等基准上取得了43.7%和36.0%的成绩，远超GRPO的36.9%和30.0%。 采样效率：Figure 7的结果极具说服力。它表明ARPO在使用大约一半的工具调用预算（financial costs）的情况下，获得了比GRPO更高的奖励分数。这直接证明了ARPO在探索效率上的巨大优势。 扩展性分析：Figure 8的消融实验进一步验证了ARPO设计的合理性。实验表明，适度的熵值权重、平衡的全局/局部采样比例，以及更大的总部署规模，都能稳定地提升模型性能，证明了ARPO是一个可扩展且鲁棒的算法。 4. 未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 基于这篇论文，我认为该研究方向未来有以下几个值得探索的点：\n更智能的不确定性度量：目前ARPO仅使用词元熵作为不确定性的指标。未来可以探索更复杂的度量方式，例如： 模型内部状态：分析注意力（Attention）分布的熵、或者模型隐藏状态（Hidden States）的离散度。 语义不确定性：不仅仅是看下一个词的概率分布，而是通过生成多个候选答案并评估它们的语义差异来判断不确定性。 分层强化学习（Hierarchical RL）：ARPO解决了“在何处深搜”的问题，但“何时启动深搜”仍由一个简单的阈值决定。未来可以构建一个更高层次的策略网络（Meta-policy），由它来动态决定在当前状态下，是应该进行常规探索，还是调用ARPO进行高成本的深度分支探索。 多模态智能体：将ARPO的思想扩展到多模态领域。例如，一个视觉语言模型（VLM）在分析一张复杂的图表或场景时，可能会在某些区域感到“困惑”。ARPO可以指导模型在这些区域投入更多的“注意力”或调用外部的图像分析工具。 技术和投资机会： 智能体开发平台：可以开发集成ARPO这类高效学习算法的智能体开发平台（Agent Development Platform），帮助企业和开发者以更低成本训练能处理复杂业务流程的AI员工。 专业领域智能体：在金融分析、科学研究、法律咨询等需要大量信息检索和深度分析的领域，基于ARPO训练的智能体有望提供更可靠、更深入的决策支持，这背后蕴藏着巨大的商业价值。 5. 从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的角度看，仍有一些潜在的不足：\n超参数敏感性：ARPO引入了几个新的超参数，如熵权重β、基础采样概率α和触发阈值τ。虽然论文在Figure 8中做了简单的分析，但在更复杂的真实场景中，如何自动、动态地调整这些超参数，以适应不同任务和不同模型状态，是一个尚未解决的难题。这可能增加了算法在实践中应用的调参难度。 理论基础的创新性：论文将其理论基础归结为一个广义策略梯度（GPG）定理。这个定理本身更多地是对现有基于Transformer的策略网络的一种重新表述（将多步生成看作一个宏观动作），而非一个全新的、根本性的理论突破。它为ARPO的合理性提供了很好的解释，但其理论贡献的深度相对有限。 对长程依赖的验证：论文声称ARPO适用于“多轮”交互，但实验中的任务轮数相对有限。在一个需要几十甚至上百轮交互的超长程任务中，ARPO的熵引导机制是否依然有效？熵的累积效应是否会导致探索行为发生意外的偏离？这些问题需要进一步的实验验证。 工具反馈的复杂性：实验中的工具反馈相对结构化（如API返回的文本、代码执行结果）。如果工具返回的是极其嘈杂、冗长或模棱两可的信息（例如，一篇充满矛盾观点的新闻报道），简单的词元熵是否还能准确捕捉到模型的“有效不确定性”，还是会被噪声淹没？ 6. 我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新思路的您来说，这篇论文的精髓在于以下几点：\n核心启发：不确定性驱动的自适应学习 最大的启发是将模型的不确定性显式地作为一种宝贵信号来指导学习过程。不要把模型的生成过程看作一个无法干预的黑盒，而是要找到模型最“犹豫”的关键节点，并把学习资源集中于此。这个思想可以被广泛应用到各种AI学习任务中，比如主动学习（Active Learning）、课程学习（Curriculum Learning）等。 重点学习： 动态资源分配：学习ARPO如何动态地在“广度”（全局采样）和“深度”（分支采样）之间分配计算预算。在您自己的项目中，如果您也面临资源有限但需要深入探索某些关键决策点的问题，这个思路非常有借鉴意义。 分层信誉分配：学习ARPO（及GRPO）如何巧妙地解决分支路径下的信誉分配问题。当一个决策会引发多种可能的未来时，如何评估这个决策的“平均价值”和“最优价值”，是一个非常普遍且重要的问题。 需要补充的背景知识： 强化学习基础（PPO/TRPO）：需要深入理解策略梯度方法，特别是近端策略优化（PPO），因为它是现代LLM强化学习（如RLHF）的基石。 词元熵（Token Entropy）：理解信息熵的基本概念，以及它如何被用来量化一个概率分布的不确定性。在LLM中，它直接反映了模型对下一个词选择的“信心”。 智能体RL的前沿算法（DAPO/GRPO）：了解DAPO和GRPO等轨迹级优化算法的工作原理，这样才能更深刻地体会到ARPO从“轨迹级”到“步级”的创新点所在。 智能体与工具使用（Tool Use）：了解当前LLM作为智能体使用工具（如ReAct框架）的基本模式和挑战，这能帮助您理解ARPO所要解决问题的实际场景和重要性。 ","date":"2025-07-30T00:00:00Z","permalink":"/agentic-reinforced-policy-optimization/","title":"AGENTIC REINFORCED POLICY OPTIMIZATION"},{"content":"预训练 Muon-clip 详见Muon\n通过裁剪权重解决Max-Logit问题，实现稳定训练。\n数据增强 通过改写句子来提高token效率，避免重复使用token造成的过拟合，改写方法如下：\n多样化风格和视角的提示词：为在保持事实完整性的同时增强语言多样性，研究团队应用了一系列精心构建的提示词。这些提示词引导大语言模型从不同风格和视角生成原始文本的忠实改写版本。 分块式自回归生成：为保持全局连贯性并避免长文档信息丢失，研究团队采用了基于分块的自回归重写策略。文本被划分为若干段落，分别进行改写，然后重新组合形成完整文章。这种方法缓解了大语言模型通常存在的隐式输出长度限制问题，如图4所示。 保真度验证：为确保原始内容与改写内容的一致性，研究团队执行保真度检查，比较每个改写段落与原文的语义对齐程度。这作为训练前的初步质量控制环节。 模型架构 k2通过实验发现了稀疏性扩展法则（稀疏性定义为专家总数与激活专家数量的比值）。 以及注意力头的数量多只能提升少量性能，但带来了大量的计算量。\ninfra 分为并行化策略和激活值压缩\n并行化策略：EP通信与交错1F1B的重叠与更小的EP规模\n激活值压缩：\n选择性重计算 对计算成本低但内存占用高的操作进行重计算，包括LayerNorm、SwiGLU和MLA上投影。此外，训练过程中还对MoE下投影进行重计算以进一步降低激活值内存需求。虽然这种重计算是可选的，但它能够维持充足的GPU内存，防止早期训练阶段因专家负载不均衡导致的系统崩溃。\n不敏感激活值的FP8存储 将MoE上投影和SwiGLU的输入压缩为1×128数据块中的FP8-E4M3格式，配合FP32缩放因子。小规模实验表明这种压缩不会导致可测量的损失增加。由于初步研究中观察到潜在的性能下降风险，研究团队未在计算过程中采用FP8格式。\n激活值CPU卸载 将所有剩余激活值卸载至CPU内存。数据传输引擎负责流式卸载和加载，与计算和通信核心实现重叠处理。在1F1B阶段，系统在预取下一个微批次的反向激活值的同时卸载前一个微批次的前向激活值。\nPost-training 工具能力增强 合成了大量工具来进行微调。 - 工具规范生成：首先从现实世界工具和LLM合成工具中构建大型工具规范库 - 智能体和任务生成：针对从工具库中采样的每个工具集，生成使用该工具集的智能体和相应任务 - 轨迹生成：为每个智能体和任务生成智能体通过工具调用完成任务的轨迹\nimage.png 智能体的多样化 团队通过合成各种系统提示词并为其配备来自资源库的不同工具组合，生成了数千个不同的智能体。这创建了具有不同能力、专业领域和行为模式的多样化智能体群体，确保了潜在用例的广泛覆盖。\n基于评价标准的任务生成 针对每个智能体配置，团队生成从简单到复杂的各种操作任务。每个任务都配备明确的评价标准，规定成功标准、期望的工具使用模式和评估检查点。这种基于评价标准的方法确保了智能体性能评估的一致性和客观性。\n多轮轨迹生成 团队通过以下组件模拟真实的工具使用场景：\n用户模拟：LLM生成具有不同沟通风格和偏好的用户角色，与智能体进行多轮对话，创建自然的交互模式。 工具执行环境：复杂的工具模拟器（功能等同于世界模型）执行工具调用并提供真实反馈。模拟器在每次工具执行后维护和更新状态，支持具有持续效果的复杂多步交互。它引入受控随机性，产生包括成功、部分失败和边缘情况在内的各种结果。 质量评估和筛选 基于LLM的评判者根据任务评价标准评估每个轨迹。只有满足成功标准的轨迹被保留用于训练，确保数据质量的同时允许任务完成策略的自然变化。\n与真实执行环境的混合方法 虽然模拟提供了可扩展性，但团队认识到模拟保真度的固有局限性。为解决这一问题，团队在真实性至关重要的场景（特别是编程和软件工程任务）中，使用真实执行沙盒来补充模拟环境。这些真实沙盒执行实际代码，与真正的开发环境交互，并通过测试套件通过率等客观指标提供真实反馈。这种组合确保模型能够从模拟场景的多样性和真实执行的准确性中学习，显著增强实际智能体能力。\n参考 [2507.20534] Kimi K2: Open Agentic Intelligence\nhttps://zhuanlan.zhihu.com/p/1933619657589384402\n","date":"2025-07-30T00:00:00Z","permalink":"/k2/","title":"K2"},{"content":"在softmax的结果后添加一个静态的不可学习的偏置项。 q1和k1之间的距离是0，所以对应位置就是0\nq2和k1之间的距离是「相对位置偏移为“k的索引”1」 - 「q的索引2」，得到1-2 = -1，就对应到了中间矩阵的取值为-1了\n以此类推，相对距离矩阵的中间对角线上都是0，然后左下角的取值都是对应的「k的索引」-「q的索引」了\n","date":"2025-07-29T00:00:00Z","permalink":"/alibi/","title":"ALiBi"},{"content":"Gemma 3 QK-Norm 简单来说就是在Q和K矩阵上进行RMSNorm，即：\n\\[ \\begin{aligned} O \u0026amp;= softmax(\\bar{Q}\\bar{K}^T)V \\\\ \\bar{Q} \u0026amp;=RMSNorm(Q) \\\\ \\bar{K} \u0026amp;=RMSNorm(V) \\end{aligned}\\ \\]\n但这种方法的问题是不适用MLA的推理阶段，因为推理阶段的MLA将Wk吸取到了Q中，具体见MLA\n","date":"2025-07-29T00:00:00Z","permalink":"/gemma/","title":"Gemma"},{"content":"这篇由字节跳动Seed团队在2025年4月发表的论文，直面了当前大模型领域中一个核心且棘手的难题：如何通过强化学习（Reinforcement Learning, RL）高效、稳定地提升模型在复杂推理任务上的能力。当前，以链式思考（Chain-of-Thought, CoT）为代表的推理技术是实现通用人工智能（AGI）的关键路径，而如何让模型学会更长、更可靠的推理链，是业界公认的瓶颈。\n传统的强化学习方法在应用于大型语言模型（LLM）时，通常分为两大流派：无价值模型（value-model-free）和基于价值模型（value-model-based）的方法。近年来，如DAPO等无价值模型方法因其简单稳定而成为主流。它们绕过了训练一个价值模型（Value Model）——即一个用于预测在某个状态下未来总回报期望值的网络——的复杂过程，直接将整个推理序列的最终奖励（比如答案正确与否）分配给序列中的每一步。这种方法虽然规避了难题，但也触及了性能的天花板。原因在于，它无法实现精确的信用分配（credit assignment），即无法分辨出一个推理步骤中的具体哪个词或哪个想法是导致最终成败的关键。\n这篇论文的核心论点，正是对当前主流趋势的一次“拨乱反正”。作者们认为，基于价值模型的方法拥有更高的性能上限，因为它能为每一步决策提供更精细、更低方差的指导信号。其面临的挑战并非无解，而是需要被系统性地识别和攻克。论文精准地指出了阻碍价值模型训练的三大核心挑战：\n长序列上的价值模型偏差（Value Model Bias over Long Sequences）：价值模型通常由奖励模型（Reward Model）初始化，但二者目标不一致。奖励模型旨在为完整的句子打分，而价值模型需要评估不完整句子的未来潜力，这导致了严重的初始化偏差。 训练中异构序列长度（Heterogeneous Sequence Lengths during Training）：推理任务中，模型生成的文本长度差异巨大。传统的广义优势估计（Generalized Advantage Estimation, GAE）方法使用固定的λ参数，无法同时适应长短不一的序列，导致对短序列估计方差过高，对长序列估计偏差过大。 验证器任务中奖励信号的稀疏性（Sparsity of Reward Signal）：在数学、代码等推理任务中，奖励通常是二元的（0或1），并且只在推理链的末尾给出。模型需要进行大量探索才能偶尔获得一个正向反馈，学习效率极低。 为了系统性地解决上述问题，论文提出了VAPO框架。它并非单一的算法革新，而是一套集大成的“组合拳”，包含了七项关键技术优化，共同将基于价值模型的强化学习推向了新的高度。这七项技术如同一组精密配合的齿轮，协同作用，最终实现了1+1\u0026gt;2的效果。\nVAPO框架的卓越性能在AIME 2024（一个高难度的数学推理竞赛数据集）上得到了充分验证。如下图所示，基于同样的Qwen2.5-32B模型，VAPO的性能显著超越了之前的SOTA方法DAPO。\nIn direct comparison under identical experimental settings, VAPO outperforms the previously reported results of DeepSeek-R1-Zero-Qwen-32B and DAPO by more than 10 points. The training process of VAPO stands out for its stability and efficiency. It reaches state-of-the-art performance within a mere 5,000 steps.\nVAPO不仅在最终得分（60.4分 vs DAPO的约50分）上取得了巨大优势，更重要的是，它在训练效率和稳定性上表现优异，仅用约5000步训练就达到了SOTA水平，且多次实验结果一致，没有出现训练崩溃。这充分证明，只要方法得当，基于价值模型的强化学习是一条更具潜力的技术路线。论文通过详尽的消融实验（Ablation Study），逐一验证了其提出的七个模块各自不可或缺的贡献，为后续研究者提供了清晰、可复现的技术蓝图。\nimage.png 图1：VAPO在AIME 2024上的得分与DAPO对比，展示了其在性能和训练效率上的显著优势。\n接下来，我将按照您提出的六个维度，对这篇论文进行更深入的拆解和分析。\n一、论文的研究目标是什么？ 核心研究目标：论文旨在创建一个高效且可靠的、基于价值模型的强化学习框架（即VAPO），专门用于提升大语言模型在高级推理任务（特别是长链式思考任务）上的性能。\n想要解决的实际问题：当前业界在利用强化学习提升大模型推理能力时，普遍遇到了瓶颈。\n性能瓶颈：以DAPO为代表的无价值模型方法，虽然稳定，但由于无法进行精确的信用分配，其性能提升已接近极限。模型很难学会进行超过一定长度和复杂度的推理。 训练瓶颈：直接应用基于价值模型的传统强化学习算法（如PPO），在长链式思考任务上极易失败，表现为训练崩溃（training collapse），即模型性能急剧下降，甚至只会生成无意义的短回答。 对行业发展的重要意义：\n指明技术方向：这篇论文为整个行业探索大模型推理能力的提升，指出了一个更具潜力的方向。它证明了克服价值模型训练的困难是完全可行的，其回报是远超无价值模型方法所能达到的性能高度。 推动复杂任务自动化：强大的推理能力是实现科学发现自动化、编写复杂软件、进行法律分析等高级认知任务的前提。VAPO这类框架的出现，将极大加速AI在这些专业领域的应用落地。 降低SOTA模型训练门槛：通过提升训练效率和稳定性，VAPO实际上降低了训练出顶尖推理模型的计算成本和时间成本，使得更多机构有能力参与到前沿模型的研发中。 二、论文提出了哪些新的思路、方法或模型？ VAPO的核心思想是“直面问题，系统解决”。它没有回避价值模型训练的困难，而是将其拆解为三个子问题，并针对性地提出了七项技术改进。这些技术共同构成了VAPO框架，其特点和优势在于其系统性和协同效应。\nWe pinpoint three key challenges that plague value-model-based methods: value model bias, the presence of heterogeneous sequence lengths, and the sparsity of reward signals. Through systematic design, VAPO offers an integrated solution that effectively alleviates these challenges…\n以下是VAPO框架中七个关键组件的详细解析：\n价值预训练 (Value-Pretraining) 思路：为了解决价值模型的初始化偏差问题，在正式的PPO训练开始前，增加一个预训练阶段。此阶段固定策略模型，生成样本，然后用蒙特卡洛回报（Monte-Carlo return）——即从当前步到结束的实际累积奖励——来监督价值模型的训练。 优势：这相当于先让价值模型“见过”真实的回报长什么样，对其进行有效“热身”，极大地缓解了从奖励模型继承来的偏差，是防止训练初期崩溃的关键第一步。 解耦的GAE (Decoupled-GAE) 思路：在计算优势函数时，为价值网络和策略网络的更新使用不同的λ参数。具体地，价值网络更新使用 λ_critic = 1.0，策略网络更新使用一个较小的值如 λ_policy = 0.95。 优势：λ=1.0 意味着价值函数的更新目标是无偏的蒙特卡洛回报，这能确保价值模型学到更准确的长期价值。而较小的 λ_policy 能引入一定的偏差以降低方差，从而加速策略的收敛。这种“解耦”设计兼顾了价值学习的准确性和策略学习的稳定性。 长度自适应GAE (Length-Adaptive GAE) 思路：这是对Decoupled-GAE的进一步创新。作者发现固定的 λ_policy 无法适应长短不一的序列。因此，他们设计了一个动态的λ，使其随序列长度l自适应调整。公式为： \\[ \\lambda_{\\text{policy}} = 1 - \\frac{1}{\\alpha l} \\] 优势：对于短序列，λ值较低，估计偏向于低偏差、高方差的蒙特卡洛方法；对于长序列，λ值接近1，估计偏向于高偏差、低方差的引导（bootstrapping）方法。这种自适应机制完美地平衡了不同长度序列下的偏差-方差权衡，是VAPO的一大亮点。 词元级策略梯度损失 (Token-Level Policy Gradient Loss) 思路：传统的PPO损失函数先在序列内部对所有词元的损失进行平均，再对一个批次内的所有序列求平均。这导致长序列中的单个词元对总损失的贡献过小。VAPO修改了损失函数，直接在整个批次的所有词元上进行平均。 优势：确保了每个词元（token）在梯度更新中拥有平等的“话语权”。这使得模型能够更有效地关注并修正长序列中的错误，防止因权重被稀释而导致的问题被忽视。 更高的裁剪上界 (Clip-Higher) 思路：在PPO的裁剪目标函数中，将裁剪范围的上下界ε解耦为 ε_low 和 ε_high，并设置一个更大的上界（如ε_high = 0.28, ε_low = 0.2）。 优势：更大的ε_high为提升那些“非主流”（低概率）词元的概率提供了更多空间。在探索阶段，正确的推理路径可能就隐藏在这些低概率的序列中。此举能在不牺牲稳定性的前提下，有效鼓励模型进行更充分的探索（exploration）。 正样本语言模型损失 (Positive Example LM Loss) 思路：由于奖励稀疏，获得一个最终答案正确的“正样本”非常宝贵。当采样到正样本时，除了标准的PPO损失外，额外增加一个模仿学习（imitation learning）的损失项，即最大化该正确序列的似然概率。 优势：这是一种“从成功中加倍学习”的策略，极大地提升了对来之不易的正样本的利用效率，加速了模型对正确推理模式的学习。 分组采样 (Group-Sampling) 思路：在有限的计算预算下，相比于“每个提示词（prompt）采样一次，使用大量不同提示词”的策略，VAPO采用了“减少提示词数量，但对每个提示词进行多次采样”的策略。 优势：为同一个问题生成多个不同的解答（无论对错），能为模型提供更丰富的对比信号（contrastive signal），帮助其更清晰地辨别出哪些推理路径更优，从而增强学习效果。 三、论文通过什么实验来验证有效性？ 论文设计了一系列严谨的实验来全方位验证VAPO的有效性，主要围绕性能对比、消融研究和训练动态分析三个层面展开。\n实验设计： 基准平台：所有实验均在AIME 2024数据集上进行，该数据集是评估数学推理能力的权威标准。 基础模型：统一使用 Qwen2.5-32B 作为基础语言模型，以确保比较的公平性。 对比对象：主要的对比对象是当时最先进的无价值模型方法 DAPO 和 DeepSeek-R1-Zero-Qwen-32B。此外，还与朴素PPO（Vanilla PPO）进行了比较，以突显VAPO系列改进的重要性。 实验数据与结果： 性能全面超越： 从图1可见，VAPO最终在AIME24上的平均得分达到60.4。 相比之下，DAPO的得分约为50，DeepSeek-R1为47。VAPO的性能提升超过10个点，这是一个巨大的飞跃。 朴素PPO的得分仅为5，说明若不加改进，基于价值模型的方法确实无法工作。 详尽的消融实验：这是论文论证力度最强的部分。通过从完整的VAPO中逐一移除七个组件，清晰地量化了每个组件的贡献。 模型 (VAPO移除某个组件后) AIME24得分 (avg@32) 性能下降幅度 VAPO (完整版) 60 - VAPO w/o Value-Pretraining 11 49点 VAPO w/o Decoupled-GAE 33 27点 VAPO w/o Length-adaptive GAE 45 15点 VAPO w/o Clip-Higher 46 14点 VAPO w/o Token-level Loss 53 7点 VAPO w/o Positive Example LM Loss 54 6点 VAPO w/o Group-Sampling 55 5点 *数据来源：论文Table 1* \u0026gt; Without Value-Pretraining, the model experiences the same collapse as Vanilla PPO during training, converging to a maximum of approximately 11 points. \u0026gt; Removing the decoupled GAE causes reward signals to exponentially decay during backpropagation, preventing the model from fully optimizing long-form responses and leading to a 27-point drop. 从表中可以看出，**价值预训练**和**解耦GAE**是整个框架的基石，移除它们会导致性能雪崩式的下降。**长度自适应GAE**也带来了高达15个点的提升，证明了其创新价值。其余四项技术也均有5-14个点的显著贡献。 3. **训练动态更优**：论文中的Figure 2展示了VAPO与DAPO在训练过程中的多项指标对比，结论是： * **更稳定的训练**：VAPO的各项指标曲线（如奖励分数、熵）比DAPO更平滑，表明其优化过程更稳定。 * **更强的长度扩展能力**：VAPO生成的平均响应长度持续稳定增长且高于DAPO，这通常被认为是模型泛化能力和推理能力更强的标志。 * **更快的学习速度**：VAPO的奖励分数增长速度更快，这得益于价值模型提供了更精细的梯度信号来加速优化。 四、未来在该研究方向上的探索和挑战？ 这篇论文为该领域开辟了新的道路，同时也引出了更多值得探索的问题和挑战：\n值得探索的问题与挑战： 通用性与扩展性：VAPO在数学推理上取得了巨大成功。但它在其他同样需要复杂推理的领域（如法律、生物、物理）是否同样有效？以及当模型规模继续扩大到千亿甚至万亿参数级别时，VAPO框架是否依然是最优选择？ 过程监督 vs. 结果监督：目前VAPO仍主要依赖最终结果的二元奖励。一个更理想的系统应该能对推理过程本身进行奖励（Process Supervision）。如何设计高效的、能理解并奖励“优秀思考步骤”的奖励模型，是一个巨大的挑战和机遇。 超参数的自动化：VAPO引入了多个新的超参数（如α、μ等）。如何实现这些超参数的自适应调整或自动化搜索，以降低应用门槛，是工程实践中的一个重要问题。 探索与利用的深层理论：VAPO通过Clip-Higher等技术改善了探索，但对长程推理任务中“探索-利用”困境的理论理解仍不深入。更深层次的理论突破可能会带来全新的算法。 可能催生的新技术和投资机会： 推理即服务（Reasoning-as-a-Service）：基于VAPO这类框架训练出的强大推理模型，可以被封装为专门解决特定领域复杂问题的API服务，例如“AI数学家”、“AI程序员”，在教育、科研、金融等领域有巨大的商业价值。 AI驱动的科学发现平台（AI for Science）：高阶推理能力是科学发现的核心。未来可能出现集成这类模型的自动化实验平台，能够自主提出假说、设计实验、分析数据，极大地加速新材料、新药物的研发进程。 下一代RLAIF开发工具链：VAPO的成功凸显了当前RLHF/RLAIF（从AI反馈中进行强化学习）流程的复杂性。围绕这一流程开发更易用、更高效的开发、调试和部署工具，将成为一个重要的投资赛道。 五、从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管VAPO取得了显著的成功，但从批判和审慎的角度看，它仍存在一些可以改进和深入探讨的地方：\n模型架构的局限性：所有实验都基于Qwen这一种模型架构。其结论是否能直接推广到Llama、Gemini等其他主流架构上，仍有待验证。不同架构的特性可能会影响VAPO中各项技术的有效性。 任务领域的单一性：论文的验证主要集中在AIME数学竞赛上。虽然作者提及对代码等任务有效，但并未提供详尽的跨领域实验数据。对于逻辑结构、知识依赖性与数学截然不同的任务（例如创意写作、法律案例分析），VAPO框架可能需要进行适应性调整。 实现复杂性较高：VAPO是一个包含七个组件的复杂系统，虽然强大，但也提高了复现和调优的门槛。这在一定程度上牺牲了算法的简洁性，对于追求快速迭代的团队可能构成挑战。 对潜在负面影响的讨论缺失：与所有致力于提升AI能力的研究一样，论文并未探讨VAPO所增强的强大推理能力可能带来的潜在风险，例如被用于恶意目的，或是其推理过程的不可解释性带来的安全问题。 对采样策略的潜在风险分析不足：文中的“分组采样”策略，虽然能提供更丰富的对比信号，但也可能增加模型对训练集中见过的提示词产生“过拟合”的风险。论文对此策略的讨论略显单薄，缺乏对这一潜在负面效应的深入分析。 六、我应该从这篇论文中重点学什么？ 如果您希望从这篇论文中汲取能“拿来即用”的创新思想，我建议您重点关注以下几个方面：\n核心启发与学习点： 重新审视价值模型：最重要的启发是，不要因为一个技术路径存在困难就轻易放弃它。无价值模型是“绕开了问题”，而VAPO是“解决了问题”。在您的研究或项目中，如果遇到类似瓶颈，可以思考是否能够通过系统性的分析和创新的方法，去攻克那些被普遍认为“太难”而遭规避的核心技术。 系统化解决问题的思维：VAPO的成功并非源于单一的“银弹”，而是对一个复杂问题进行精准拆解，并为每个子问题设计针对性解决方案的成果。这种“诊断-分解-逐一击破”的系统工程思维，在应对任何复杂挑战时都极具价值。 算法的自适应性设计：长度自适应GAE是本文最精彩的单点创新之一。它体现了一种重要的设计哲学：让算法的关键参数能够根据输入数据的特征（如此处的序列长度）进行动态调整。这个思想可以被广泛应用于您遇到的其他问题，例如根据任务难度动态调整学习率、根据上下文长度调整注意力机制等。 高效利用“黄金数据”：正样本语言模型损失提醒我们，在任何奖励稀疏的环境下，成功样本都是极其宝贵的“黄金数据”。要思考如何最大化地从每一次成功中学习，例如通过增加额外的损失项、进行优先经验回放（prioritized experience replay）等方式，榨干其价值。 需要补充的背景知识： 为了完全消化这篇论文的精髓，并能将其思想应用到您自己的工作中，我建议您确保对以下背景知识有扎实的理解： PPO (Proximal Policy Optimization)：您需要深入理解PPO算法的原理，特别是其核心的“裁剪代理目标函数”（Clipped Surrogate Objective）以及它为何能保证训练的稳定性。 GAE (Generalized Advantage Estimation)：必须清晰地理解GAE是什么，γ（折扣因子）和λ（偏差-方差权衡参数）各自的作用。这是理解VAPO中Decoupled-GAE和Length-Adaptive GAE创新的基础。 RLHF (Reinforcement Learning from Human Feedback)：需要了解标准的RLHF流程，包括策略模型、奖励模型、价值模型三者在训练流程中的角色和交互方式。 链式思考 (Chain-of-Thought, CoT)：熟悉CoT的基本概念，以及它为何能有效激发大模型的推理能力。这是理解VAPO应用场景和动机的关键。 希望以上深入的解读能帮助您全面、透彻地理解这篇优秀的论文，并从中获得有益的启发。如果您对其中任何细节还有疑问，我们随时可以继续探讨。\n","date":"2025-07-29T00:00:00Z","permalink":"/vapoefficient-and-reliable-reinforcement-learning-for-advanced-reasoning-tasks/","title":"VAPO：Efficient and Reliable Reinforcement Learning for Advanced Reasoning Tasks"},{"content":"这篇论文的核心贡献是提出了一种名为 组序列策略优化 (Group Sequence Policy Optimization, GSPO) 的新型强化学习（RL）算法，旨在解决在训练大型语言模型（特别是混合专家模型, Mixture-of-Experts, MoE）时普遍存在的训练不稳定甚至模型崩溃的痛点。这不仅仅是一次微小的算法改进，而是一次对现有主流RL优化范式的根本性反思与重构，其核心思想是 “将优化的基本单元与奖励的基本单元对齐”。\n在深入理解GSPO之前，我们需要了解它试图取代的先前方法，尤其是组相对策略优化 (Group Relative Policy Optimization, GRPO)。传统的RL算法，如经典的近端策略优化 (PPO)，在应用于大模型时，通常需要一个独立的、与模型本身差不多大的“价值模型”（Value Model）来评估每个决策（即每个token）的好坏，这带来了巨大的计算和内存开销。为了解决这个问题，GRPO被提出，它巧妙地通过在同一查询（query）生成的多个回答（response）之间进行“相对比较”，从而绕开了价值模型。具体来说，它会比较一个回答的奖励（reward）与同组其他回答的平均奖励，从而计算出优势（advantage），并用这个优势来指导模型更新。\n然而，Qwen团队发现，GRPO虽然思路巧妙，却存在一个致命的“设计缺陷”。正如论文在第3节“动机”中所述，GRPO的根基——重要性采样（importance sampling）被“误用”了。重要性采样是一种统计学技巧，目的是用一个分布（行为分布）的采样来估计另一个分布（目标分布）下的期望值。它的有效性依赖于对大量样本的平均。但GRPO却在词元级别 (token-level) 上应用了它。\nIn contrast, GRPO applies the importance weight … at each token position t. Since this weight is based on a single sample yi,t from each next-token distribution … it fails to perform the intended distribution-correction role. Instead, it introduces high-variance noise into the training gradients, which accumulates over long sequences and is exacerbated by the clipping mechanism.\n换句话说，对于一个长序列中的每一个词元，GRPO都计算一个“重要性权重”，试图校正新旧策略的差异。但因为每个词元的生成只是“一次采样”，这种校正不仅无效，反而引入了巨大的、不稳定的噪声。当模型变得庞大、回答序列变长时，这种噪声会累积并被PPO家族算法中的“裁剪机制”（clipping mechanism）放大，最终导致训练过程“灾难性且不可逆转地崩溃”。\nGSPO正是为了解决这个根本问题而设计的。它的核心创新点，如论文第4.1节所述，是将重要性采样的应用从“词元级别”提升到了“序列级别” (sequence-level)。既然奖励是针对整个回答序列给出的，那么策略更新的校正也应该在整个序列的层面上进行。GSPO定义了一个基于整个序列似然度（sequence likelihood）的重要性比率 s_i(θ)（公式8），这个比率衡量的是新策略生成整个回答序列的概率与旧策略生成该序列概率的比值。\n\\[ s_i(\\theta) = \\left( \\frac{\\pi_\\theta(y_i|x)}{\\pi_{\\theta_{\\text{old}}}(y_i|x)} \\right) ^ {\\frac{1}{\\mid y_{i}\\mid}} \\]\n基于这个序列级别的重要性比率，GSPO对整个序列的优势函数 Â_i 进行裁剪（clipping）和优化（公式6）。这样做的好处是显而易见的：它从根本上避免了GRPO在词元级别引入的噪声累积问题。如论文的梯度分析部分（公式11 vs 公式13）所揭示的，GRPO对序列中每个词元的梯度赋予了不同的权重，而GSPO则对同一序列中的所有词元赋予了相同的权重，这个权重由整个序列的表现决定。这极大地增强了训练的稳定性。\n论文通过一系列详实的实验证明了GSPO的优越性。最引人注目的莫过于图1展示的在AIME’24（数学）、LiveCodeBench（编程）等高难度任务上的训练曲线。GSPO不仅稳定地提升性能，而且在相同的计算资源下，其训练效率和最终性能都显著优于精心调优后的GRPO。\n一个非常“反直觉”但又深刻的发现体现在图2中。GSPO在训练中裁剪掉的词元比例（Clipping Fraction）高达15%，而GRPO仅为0.13%，两者相差超过100倍。传统的观念认为，裁剪掉过多的样本意味着浪费数据，会降低训练效率。但GSPO的实验结果恰恰相反：“大规模的裁剪反而带来了更高的训练效率”。\nThis counter-intuitive finding — that clipping a much larger fraction of tokens leads to superior training efficiency — further indicates that GRPO’s token-level gradient estimates are inherently noisy and inefficient for sample exploitation. In contrast, GSPO’s sequence-level approach provides a more reliable and effective learning signal.\n这雄辩地证明了GRPO的词元级梯度估计充满了噪声，而GSPO的序列级信号则更加可靠和有效。此外，论文还强调了GSPO在训练MoE模型时的巨大优势（第5.3节）。MoE模型在训练中存在“专家激活不一致”的问题，即梯度更新后，模型对同一个输入激活的专家组合会发生变化，这对于GRPO的词元级重要性权重是灾难性的。而GSPO只关心整个序列的似然度，对底层专家激活的变化不敏感，从而天然地解决了MoE模型的训练稳定性问题，甚至不再需要像“路由回放”（Routing Replay）这样复杂的额外技巧。\n总而言之，GSPO通过一个看似简单却直击问题本质的改变——将优化单元从词元级提升至序列级——成功解决了大规模RL训练中的核心稳定性难题，为更大规模、更强能力的LLM（尤其是MoE架构）的持续发展铺平了道路，是LLM训练算法领域一个坚实而优雅的进步。\n接下来，我将按照您提出的六个问题，逐一进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：设计一种新的、更稳定、更高效的强化学习（RL）算法，用于大型语言模型的训练。 要解决的实际问题： 训练不稳定性：现有SOTA（State-of-the-Art）的RL算法，如GRPO，在训练超大规模语言模型（如拥有数百亿甚至更多参数的MoE模型）时，会遭遇严重的稳定性问题，频繁导致训练过程突然恶化，即“模型崩溃”。 效率瓶颈：GRPO算法因其内在的噪声问题，导致样本利用效率不高，需要更多的计算资源才能达到理想的性能。 MoE模型训练难题：MoE模型在RL训练中存在独特的“专家激活波动性”问题，GRPO等传统方法难以应对，需要复杂的辅助策略（如Routing Replay）才能勉强收敛。 对行业发展的重要意义： 解锁更大规模的RL训练：稳定是规模化的前提。如果投入数千张GPU进行数周的RL训练，结果却因为算法不稳定而崩溃，这将是巨大的资源浪费。GSPO这样的稳定算法是推动LLM能力边界（例如在数学、编程等复杂推理任务上）的基石，使得更大规模的RL投资成为可能。 简化RL训练设施与流程：GSPO因为其内在的稳定性，特别是在MoE训练上，不再需要“路由回放”等复杂的“补丁”，这可以显著简化RL的训练代码和系统架构（RL Infrastructure），降低维护成本，提高研发效率。 推动MoE架构的普及：MoE被认为是未来扩展LLM能力的关键路径之一。GSPO解决了其在RL阶段的一个核心训练难题，将极大地促进MoE模型在更多场景下的应用和发展。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心新思路：“奖励与优化单元对齐” (Aligning the unit of optimization with the unit of reward)。这是整篇论文的哲学基石。既然奖励（如一个数学题是否做对）是基于整个生成序列来评估的，那么用于策略更新的重要性采样和裁剪等核心机制也应该在序列级别上进行，而不是在更细的词元级别上。 具体新方法：GSPO (Group Sequence Policy Optimization) 算法。 与GRPO的对比与优势： 特性 GRPO (之前的方法) GSPO (新方法) 优势 重要性比率 (Importance Ratio) 词元级别：为每个token计算一个w_i,t(θ) 序列级别：为整个sequence计算一个s_i(θ) 从根本上消除噪声累积，信号更可靠，更符合重要性采样的初衷。 梯度加权 (Gradient Weighting) 每个token的梯度由各自的权重加权 一个序列中所有token的梯度由相同的权重加权 训练更稳定，避免了因词元权重剧烈波动导致的梯度方向突变。 裁剪操作 (Clipping) 在词元级别对优势函数进行裁剪 在序列级别对优势函数进行裁剪 裁剪更有效，能够大胆地剔除整个“差”的序列，而不是在充满噪声的词元信号上小心翼翼。 对MoE模型的适应性 差，需要“路由回放”等额外技巧来稳定 好，天然对专家激活波动不敏感 简化训练，释放MoE模型的全部潜力，无需额外开销和复杂性。 细节分析： 论文中的公式(8)是GSPO的关键。它不仅定义了序列级的重要性比率，还通过长度归一化（除以 |y_i|）来降低序列长度带来的方差，使得不同长度序列的重要性比率可以在一个统一的数值范围内进行比较和裁剪，这是一个非常重要的工程实践。 论文提出的GSPO-token（公式14）是一个灵活的变体。它保留了GSPO的序列级裁剪稳定性，但允许在未来为序列中的不同词元分配不同的优势值（Â_i,t）。这为未来研究更精细化的奖励分配（credit assignment）留下了空间，例如在一个长的代码生成序列中，只奖励关键的算法实现部分。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 模型与任务：使用 Qwen3-30B-A3B-Base 模型进行冷启动微调，这是一个强大的MoE基础模型。 评测基准 (Benchmarks)：选取了三个公认的、衡量模型复杂推理能力的高难度基准： AIME’24：一个数学竞赛数据集。 LiveCodeBench：一个持续更新的编程能力评测集。 CodeForces：一个编程竞赛平台，通过Elo等级分来评估模型性能。 对比基线 (Baseline)：将GSPO与经过“精心调优”的GRPO进行直接对比，确保了比较的公平性。 关键实验数据与结果： 性能与效率 (图1)： 结果：在所有三个基准测试中，GSPO的训练曲线（蓝色）始终位于GRPO（红色）的上方，并且波动更小。这表明GSPO不仅性能更好，而且训练过程更稳定、收敛更快。 数据引用：例如在AIME’24上，GSPO在训练初期就迅速达到了75分左右的水平，而GRPO则在70分附近徘徊且波动较大。在CodeForces上，GSPO的Elo分数稳定在1900分以上，而GRPO则在1800-1900分之间大幅震荡。 裁剪分数 (图2)： 结果：GSPO的平均裁剪比例是 0.15，而GRPO仅为 0.0013。 数据引用：这个结果的震撼之处在于，GSPO“扔掉”了多得多的数据，但学习效果却更好。这强有力地证明了其学习信号的质量远高于GRPO。 image.png MoE训练的必要性 (图3)： * 结果：该图显示，对于MoE模型，使用GRPO算法时，如果不开启“路由回放”（w/o Routing Replay，橙色线），训练将很快崩溃。而开启后（紫色线）才能正常收敛。 * 数据引用：结合图1（GSPO未使用路由回放）可以得出结论：GSPO从根本上解决了GRPO在MoE训练中必须依赖额外技巧才能存活的问题。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 精细化奖励分配 (Fine-grained Credit Assignment)：GSPO-token虽然提供了理论框架，但如何设计有效的、非人工的机制来为长序列中的不同部分（如一个关键论证步骤、一个正确的函数调用）赋予不同的奖励值，是一个巨大的挑战。这需要结合模型的可解释性研究。 与DPO等非RL方法的融合：直接偏好优化（DPO）及其变体是另一条非常热门的对齐路线。GSPO的核心思想（序列级处理）能否与DPO类方法融合，创造出兼具DPO的稳定性和RL的探索能力的更强算法？ 超越固定奖励模型：当前的RLHF依赖于一个预训练好的、固定的奖励模型。未来的研究可能会探索在RL过程中同时在线优化策略模型和奖励模型，GSPO的稳定性为这种更复杂的在线学习系统提供了可能。 探索更复杂的组策略：GSPO和GRPO都基于“组”的概念。如何更智能地构建这个“组”？例如，不仅仅是N个随机采样，而是包含一个“好”的样本、一个“坏”的样本、一个“有创意”的样本等，可能会提供更丰富的学习信号。 可能催生的新技术和投资机会： 新一代LLM训练平台：可以投资或开发为GSPO这类新范式优化的LLM训练/RL平台。这些平台可以主打“稳定性”、“对MoE友好”、“简化架构”等特性，吸引那些苦于RL训练不稳定的公司。 自动化奖励函数设计：围绕“精细化奖励分配”的技术可能催生出自动为复杂任务（如科学发现、法律文书撰写）设计奖励函数的初创公司，这是将LLM应用于更专业领域的关键。 MoE模型即服务 (MoE-as-a-Service)：随着GSPO等技术降低MoE的训练门槛，专门提供预训练和微调MoE模型的云服务将有巨大的市场机会，因为MoE在推理时具有成本优势。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足与缺失： 对比基线不够全面：论文的核心对比对象是GRPO。虽然这是最直接的对比，但缺少与学术界其他主流算法的比较，例如带学习价值函数的PPO或当前非常火热的DPO。虽然论文在引言中提到了PPO价值模型的挑战，但一个直接的、大规模的实验对比会更有说服力。 任务领域的局限性：实验主要集中在数学和编程这两个有明确正确答案（奖励信号清晰）的领域。GSPO在更开放、主观性更强的任务（如创意写作、情感对话、多轮交互）上的表现如何，尚待验证。在这些任务中，序列级别的奖励可能更模糊，GSPO的优势是否依然存在？ 超参数敏感性分析缺失：论文提到GRPO经过了“精心调优”，但并未提供GSPO本身对关键超参数（如裁剪范围ε、学习率）的敏感性分析。一个鲁棒的算法应该在较宽的超参数范围内都能表现良好。 GSPO-token未经实验验证：GSPO-token作为一个有潜力的变体被提出，但论文中并未包含任何相关的实验。其“更高的灵活性”目前仍停留在理论层面。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习的创新思想与启发： 第一性原理思考：对齐优化与奖励单元。这是最核心、最可迁移的启发。在设计任何学习算法时，都要反思：你的反馈信号（reward/loss）是在哪个粒度上定义的？你的优化机制是否与这个粒度匹配？如果不匹配，就可能存在类似GRPO的“设计缺陷”。 视不稳定性为根本性问题的信号。当你的训练过程（特别是大规模训练）出现不稳定时，不要只满足于调整超参数、增加workaround。要敢于质疑算法最底层的假设，就像这篇论文质疑“词元级重要性采样”的合理性一样。 简单、稳定压倒一切。在规模化应用中，一个在理论上可能稍显“粗糙”但稳定、可靠的算法（GSPO），往往比一个理论上“精巧”但脆弱、充满隐患的算法（GRPO）更有价值。工程实现上的优雅和鲁棒性至关重要。 需要补充的背景知识： 强化学习基础与RLHF：深入理解强化学习的基本概念，如策略（Policy）、奖励（Reward）、价值函数（Value Function）、优势函数（Advantage Function）以及整个从人类偏好中进行强化学习（RLHF）的流程。 PPO算法：必须详细了解PPO的目标函数，特别是重要性采样（Importance Sampling） 和 裁剪（Clipping） 机制为何被引入，以及它们的数学原理。这是理解GSPO创新的基础。 混合专家（MoE）模型：了解MoE的基本架构，特别是其稀疏激活和路由（Routing）机制。这样你才能理解论文中反复强调的“专家激活波动性”究竟是什么问题，以及为什么GSPO能解决它。 DPO（Direct Preference Optimization）：了解这一当前RLHF领域最热门的替代方案，可以帮助你更全面地评估GSPO的价值和局限性，并思考二者融合的可能性。 ","date":"2025-07-28T00:00:00Z","permalink":"/group-sequence-policy-optimization/","title":"Group Sequence Policy Optimization"},{"content":"详细讲解一下verl中的RLHFDataset，它继承自torch的Dataset，需要实现__getitem__来返回数据。\n初始化 class RLHFDataset(Dataset): \u0026quot;\u0026quot;\u0026quot; Load and preprocess RLHF data from Parquet files. - Caches files locally. - Reads into a HuggingFace Dataset and tokenizes prompts. - Optionally handles images/videos via a ProcessorMixin. - Filters prompts over a max length. - Supports resuming from checkpoints. Args: data_files (str or list): Path(s) to Parquet file(s). tokenizer (PreTrainedTokenizer): For the tokenization of text to token IDs. config (DictConfig): Options like cache_dir, prompt_key, max_prompt_length, truncation, etc. processor (ProcessorMixin, optional): Multimodal preprocessor for images/videos. \u0026quot;\u0026quot;\u0026quot; def __init__( self, data_files: str | list[str], tokenizer: PreTrainedTokenizer, config: DictConfig, processor: Optional[ProcessorMixin] = None, ): if not isinstance(data_files, list | ListConfig): data_files = [data_files] self.data_files = copy.deepcopy(data_files) self.original_data_files = copy.deepcopy(data_files) # use for resume self.tokenizer = tokenizer self.processor = processor self.config = config self.cache_dir = os.path.expanduser(config.get(\u0026quot;cache_dir\u0026quot;, \u0026quot;~/.cache/verl/rlhf\u0026quot;)) self.prompt_key = config.get(\u0026quot;prompt_key\u0026quot;, \u0026quot;prompt\u0026quot;) self.image_key = config.get(\u0026quot;image_key\u0026quot;, \u0026quot;images\u0026quot;) self.video_key = config.get(\u0026quot;video_key\u0026quot;, \u0026quot;videos\u0026quot;) self.max_prompt_length = config.get(\u0026quot;max_prompt_length\u0026quot;, 1024) self.return_raw_chat = config.get(\u0026quot;return_raw_chat\u0026quot;, False) self.return_full_prompt = config.get(\u0026quot;return_full_prompt\u0026quot;, False) self.truncation = config.get(\u0026quot;truncation\u0026quot;, \u0026quot;error\u0026quot;) self.filter_overlong_prompts = config.get(\u0026quot;filter_overlong_prompts\u0026quot;, True) self.num_workers = config.get(\u0026quot;filter_overlong_prompts_workers\u0026quot;, max(1, os.cpu_count() // 4)) self.num_workers = min(self.num_workers, os.cpu_count()) self.use_shm = config.get(\u0026quot;use_shm\u0026quot;, False) self.chat_template_func = config.get(\u0026quot;chat_template_func\u0026quot;, None) self.need_tools_kwargs = config.get(\u0026quot;need_tools_kwargs\u0026quot;, False) self.filter_prompts = config.get(\u0026quot;filter_prompts\u0026quot;, True) self.serialize_dataset = False self.return_multi_modal_inputs = config.get(\u0026quot;return_multi_modal_inputs\u0026quot;, True) self._download() self._read_files_and_tokenize() （来自deepwiki）\ndownload方法就是把hdfs文件或者本地文件缓存到缓存路径下。\n接下来重点看一下read_files_and_tokenize方法：\ndef _read_files_and_tokenize(self): dataframes = [] for parquet_file in self.data_files: # read parquet files and cache dataframe = datasets.load_dataset(\u0026quot;parquet\u0026quot;, data_files=parquet_file)[\u0026quot;train\u0026quot;] dataframes.append(dataframe) self.dataframe: datasets.Dataset = datasets.concatenate_datasets(dataframes) print(f\u0026quot;dataset len: {len(self.dataframe)}\u0026quot;) self.dataframe = self.maybe_filter_out_long_prompts(self.dataframe) def maybe_filter_out_long_prompts(self, dataframe: datasets.Dataset = None): # filter out too long prompts if self.filter_overlong_prompts: tokenizer = self.tokenizer processor = self.processor prompt_key = self.prompt_key image_key = self.image_key video_key = self.video_key if processor is not None: from verl.utils.dataset.vision_utils import process_image, process_video def doc2len(doc) -\u0026gt; int: messages = self._build_messages(doc) raw_prompt = self.processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=False ) images = [process_image(image) for image in doc[image_key]] if image_key in doc else None videos = [process_video(video) for video in doc[video_key]] if video_key in doc else None return len(processor(text=[raw_prompt], images=images, videos=videos)[\u0026quot;input_ids\u0026quot;][0]) else: def doc2len(doc) -\u0026gt; int: return len(tokenizer.apply_chat_template(doc[prompt_key], add_generation_prompt=True)) dataframe = dataframe.filter( lambda doc: doc2len(doc) \u0026lt;= self.max_prompt_length, num_proc=self.num_workers, desc=f\u0026quot;Filtering prompts longer than {self.max_prompt_length} tokens\u0026quot;, ) print(f\u0026quot;filter dataset len: {len(dataframe)}\u0026quot;) return dataframe 实现了读取parquet文件，然后再根据传入的prompt_length筛选掉prompt长度超过length的样本。返回的是dataframe。\n然后就到了最重要的__getitem__方法，来构造我们需要的数据：\ndef __getitem__(self, item): \u0026quot;\u0026quot;\u0026quot; Note that we also return the raw_input_ids so that it can be combined with other chat template \u0026quot;\u0026quot;\u0026quot; row_dict: dict = self.dataframe[item] messages = self._build_messages(row_dict) model_inputs = {} if self.processor is not None: from verl.utils.dataset.vision_utils import process_image, process_video raw_prompt = self.processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) multi_modal_data = {} images = None if self.image_key in row_dict and row_dict.get(self.image_key, None) is not None: images = [process_image(image) for image in row_dict.pop(self.image_key)] # due to the image key is \u0026quot;image\u0026quot; instead of \u0026quot;images\u0026quot; in vllm, we need to use \u0026quot;image\u0026quot; here # link: https://github.com/vllm-project/vllm/blob/3c545c0c3b98ee642373a308197d750d0e449403/vllm/multimodal/parse.py#L205 multi_modal_data[\u0026quot;image\u0026quot;] = images videos = None if self.video_key in row_dict and row_dict.get(self.video_key, None) is not None: videos = [process_video(video) for video in row_dict.pop(self.video_key)] # due to the video key is \u0026quot;video\u0026quot; instead of \u0026quot;videos\u0026quot; in vllm, we need to use \u0026quot;video\u0026quot; here # link: https://github.com/vllm-project/vllm/blob/3c545c0c3b98ee642373a308197d750d0e449403/vllm/multimodal/parse.py#L205 multi_modal_data[\u0026quot;video\u0026quot;] = [video.numpy() for video in videos] model_inputs = self.processor(text=[raw_prompt], images=images, videos=videos, return_tensors=\u0026quot;pt\u0026quot;) input_ids = model_inputs.pop(\u0026quot;input_ids\u0026quot;) attention_mask = model_inputs.pop(\u0026quot;attention_mask\u0026quot;) if \u0026quot;second_per_grid_ts\u0026quot; in model_inputs: model_inputs.pop(\u0026quot;second_per_grid_ts\u0026quot;) # There\u0026#39;s a trap here, multi_modal_inputs has to be a dict, not BatchFeature row_dict[\u0026quot;multi_modal_data\u0026quot;] = multi_modal_data # We will do batch.union() in the trainer, # so we cannot have \u0026quot;multi_modal_inputs\u0026quot; in row_dict if rollout generates new multi_modal_inputs if self.return_multi_modal_inputs: row_dict[\u0026quot;multi_modal_inputs\u0026quot;] = dict(model_inputs) # second_per_grid_ts isn\u0026#39;t used for training, just for mrope row_dict[\u0026quot;multi_modal_inputs\u0026quot;].pop(\u0026quot;second_per_grid_ts\u0026quot;, None) else: raw_prompt = self.tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) model_inputs = self.tokenizer(raw_prompt, return_tensors=\u0026quot;pt\u0026quot;, add_special_tokens=False) input_ids = model_inputs.pop(\u0026quot;input_ids\u0026quot;) attention_mask = model_inputs.pop(\u0026quot;attention_mask\u0026quot;) input_ids, attention_mask = verl_F.postprocess_data( input_ids=input_ids, attention_mask=attention_mask, max_length=self.max_prompt_length, pad_token_id=self.tokenizer.pad_token_id, left_pad=True, truncation=self.truncation, ) if self.processor is not None and \u0026quot;Qwen2VLImageProcessor\u0026quot; in self.processor.image_processor.__class__.__name__: from verl.models.transformers.qwen2_vl import get_rope_index position_ids = [ get_rope_index( self.processor, input_ids=input_ids[0], image_grid_thw=model_inputs.get(\u0026quot;image_grid_thw\u0026quot;), video_grid_thw=model_inputs.get(\u0026quot;video_grid_thw\u0026quot;), second_per_grid_ts=model_inputs.get(\u0026quot;second_per_grid_ts\u0026quot;), attention_mask=attention_mask[0], ) ] # (1, 3, seq_len) else: position_ids = compute_position_id_with_mask(attention_mask) row_dict[\u0026quot;input_ids\u0026quot;] = input_ids[0] row_dict[\u0026quot;attention_mask\u0026quot;] = attention_mask[0] row_dict[\u0026quot;position_ids\u0026quot;] = position_ids[0] raw_prompt_ids = self.tokenizer.encode(raw_prompt, add_special_tokens=False) if len(raw_prompt_ids) \u0026gt; self.max_prompt_length: if self.truncation == \u0026quot;left\u0026quot;: raw_prompt_ids = raw_prompt_ids[-self.max_prompt_length :] elif self.truncation == \u0026quot;right\u0026quot;: raw_prompt_ids = raw_prompt_ids[: self.max_prompt_length] elif self.truncation == \u0026quot;middle\u0026quot;: left_half = self.max_prompt_length // 2 right_half = self.max_prompt_length - left_half raw_prompt_ids = raw_prompt_ids[:left_half] + raw_prompt_ids[-right_half:] elif self.truncation == \u0026quot;error\u0026quot;: raise RuntimeError(f\u0026quot;Prompt length {len(raw_prompt_ids)} is longer than {self.max_prompt_length}.\u0026quot;) row_dict[\u0026quot;raw_prompt_ids\u0026quot;] = raw_prompt_ids # encode prompts without chat template if self.return_raw_chat: row_dict[\u0026quot;raw_prompt\u0026quot;] = messages # get prompts with chat template if self.return_full_prompt: row_dict[\u0026quot;full_prompts\u0026quot;] = raw_prompt # array of strings # add index for each prompt index = row_dict.get(\u0026quot;extra_info\u0026quot;, {}).get(\u0026quot;index\u0026quot;, 0) tools_kwargs = row_dict.get(\u0026quot;extra_info\u0026quot;, {}).get(\u0026quot;tools_kwargs\u0026quot;, {}) interaction_kwargs = row_dict.get(\u0026quot;extra_info\u0026quot;, {}).get(\u0026quot;interaction_kwargs\u0026quot;, {}) need_tools_kwargs = row_dict.get(\u0026quot;extra_info\u0026quot;, {}).get(\u0026quot;need_tools_kwargs\u0026quot;, self.need_tools_kwargs) if need_tools_kwargs and not tools_kwargs: logger.warning(\u0026quot;tools_kwargs is empty for index {}, data source: {}\u0026quot;, index, row_dict[\u0026quot;data_source\u0026quot;]) row_dict[\u0026quot;index\u0026quot;] = index row_dict[\u0026quot;tools_kwargs\u0026quot;] = tools_kwargs row_dict[\u0026quot;interaction_kwargs\u0026quot;] = interaction_kwargs return row_dict 对于纯文本数据，build_messages方法就是获取prompt_key对应的内容，对于多模态的处理，先不做讲解。processor就是处理多模态数据的，跳到else部分，就是对message调用apply_chat_template，这就要求我们的数据文件需要先处理成这样的格式，详见verl/examples/data_preprocess中的处理。 将raw_prompts保存起来，然后再进行tokenize，postprocess_data是对其进行padding和截断。\ndef postprocess_data( input_ids: torch.Tensor, attention_mask: torch.Tensor, max_length: int, pad_token_id: int, left_pad=True, truncation=\u0026quot;error\u0026quot;, ): \u0026quot;\u0026quot;\u0026quot;Process tokenizer outputs to consistent shapes via padding/truncation. Args: input_ids: Token indices [batch_size, seq_len] attention_mask: Mask [batch_size, seq_len] max_length: Target sequence length pad_token_id: Padding token ID left_pad: Pad left if True truncation: \u0026quot;left\u0026quot;, \u0026quot;right\u0026quot;, \u0026quot;middle\u0026quot; or \u0026quot;error\u0026quot; Returns: (input_ids, attention_mask) padded/truncated to max_length \u0026quot;\u0026quot;\u0026quot; assert truncation in [\u0026quot;left\u0026quot;, \u0026quot;right\u0026quot;, \u0026quot;middle\u0026quot;, \u0026quot;error\u0026quot;] assert input_ids.ndim == 2 sequence_length = input_ids.shape[-1] if sequence_length \u0026lt; max_length: input_ids = pad_sequence_to_length( input_ids, max_seq_len=max_length, pad_token_id=pad_token_id, left_pad=left_pad ) attention_mask = pad_sequence_to_length( attention_mask, max_seq_len=max_length, pad_token_id=0, left_pad=left_pad ) elif sequence_length \u0026gt; max_length: if truncation == \u0026quot;left\u0026quot;: # actually, left truncation may not be reasonable input_ids = input_ids[:, -max_length:] attention_mask = attention_mask[:, -max_length:] elif truncation == \u0026quot;right\u0026quot;: input_ids = input_ids[:, :max_length] attention_mask = attention_mask[:, :max_length] elif truncation == \u0026quot;middle\u0026quot;: left_half = max_length // 2 right_half = max_length - left_half input_ids = torch.cat([input_ids[:, :left_half], input_ids[:, -right_half:]], dim=-1) attention_mask = torch.cat([attention_mask[:, :left_half], attention_mask[:, -right_half:]], dim=-1) elif truncation == \u0026quot;error\u0026quot;: raise NotImplementedError(f\u0026quot;{sequence_length=} is larger than {max_length=}\u0026quot;) else: raise NotImplementedError(f\u0026quot;Unknown truncation method {truncation}\u0026quot;) return input_ids, attention_mask 根据传入的不同的截断方式，也有不同的处理。\n然后我们再根据pad或者截断后的mask计算position_ids。\n接下来的操作主要是为了保存raw_prompt和raw_prompt_ids，raw_prompt是messages进行chat_template之后的变量，最后再将extra_info中的一些信息存入到最终要返回的row_dict中返回。\n","date":"2025-07-26T00:00:00Z","permalink":"/dataset/","title":"Dataset"},{"content":"verl中的device_mesh verl中有3个device_mesh，分别是： - 训练用的FSDP mesh（通常是一维） - 推理用的rollout mesh（包含tp维度） - Ulysses序列并行的mesh（dp×sp）\nfsdp mesh rollout mesh ulysses mesh ","date":"2025-07-26T00:00:00Z","permalink":"/device_mesh/","title":"device_mesh"},{"content":"这篇论文的核心是关于代码集成推理（Code-Integrated Reasoning, CIR），即大语言模型在解决问题时，能够根据需要自主生成代码，并通过调用一个代码解释器（Code Interpreter）来执行代码、获取结果，再将这个结果整合回自身的推理链条中，以辅助后续的思考和判断。这个范式并非全新概念，但之前的研究往往停留在“让模型学会使用工具”的层面，而忽略了这一过程的内在挑战。这篇论文最大的贡献，就是系统性地识别并解决了在训练这类模型时遇到的不稳定性（instability）问题。\n作者指出，使用工具增强的强化学习（Tool-augmented Reinforcement Learning）是教会模型使用工具的主流方法。简单来说，就是让模型不断尝试解决问题，如果它使用了工具并最终答对了，就给予奖励；答错了就给予惩罚。通过这种方式，模型会逐渐“领悟”到何时、以及如何使用工具。然而，这个过程远比想象的要复杂。论文深刻地揭示了三大核心挑战： 1. 交互边界的扰动（Interaction boundary disruptions）：模型如何精确地知道一段代码从哪里开始、到哪里结束？如果边界判断失误，送去执行的代码可能不完整或包含无关内容，导致执行失败或返回错误结果，从而干扰模型的学习。 2. 分布偏移（Distributional shifts）：模型自己生成的推理文本和工具返回的执行结果在文风、格式和内容上存在巨大差异（比如，模型思考的是自然语言，而工具返回的是一个冷冰冰的数字或者报错信息）。这种“画风突变”会打断模型连贯的思考流程，导致学习过程不稳。 3. 响应同质化（Response homogenization）：在固定的交互次数限制下，模型可能会“偷懒”，只学会一种或几种能稳定拿到奖励的“套路”，而不再探索更多样、更优的解题策略，这也就是强化学习中常说的“模式崩溃（mode collapse）”。\n为了解决这些问题，论文提出了一套双管齐下的增强训练策略，其精髓在于在“探索”与“稳定”之间取得精妙的平衡。\n在维持稳定性（Stability maintenance）方面，他们提出了三项关键技术： * 精确匹配交互边界：放弃了以往简单的“停止符”方案（如遇到output就执行），而是采用精确的代码块（如```python ... ```）匹配。这确保了送去执行的永远是格式良好且完整的代码，从源头上杜绝了边界不清带来的噪声。 * 屏蔽外部工具反馈：在计算模型的训练损失时，刻意将代码执行的结果（如output\\n44）屏蔽掉。这样做是为了让模型专注于学习“如何推理和写出正确的代码逻辑”，而不是去模仿工具返回结果的特定格式，从而避免了前文提到的分布偏移问题。 * 禁用熵增益（Entropy bonus disabling）：在强化学习中，“熵增益”通常用于鼓励模型探索更多可能性。但作者发现，在与工具交互时，过多的随机性反而会放大噪声，导致训练不稳定。因此，他们选择禁用这一项，追求更确定的学习路径。\n在强化探索（Exploration enhancement）方面，他们也同样采取了精妙的策略： * 渐进式增加交互预算：训练初期，只允许模型调用少数几次工具。随着训练的进行，再逐步放宽限制。这鼓励模型在早期先学习简单的工具使用方法，后期再探索更复杂的、需要多次代码执行的解题路径。 * 移除KL散度惩罚项并采用非对称截断（Clip higher）：在传统的强化学习中，KL散度（KL divergence）惩罚项用于防止新模型偏离旧模型太远，以保证训练稳定。但在这里，作者认为这个限制反而束缚了模型学习使用工具的“新技能”。因此，他们大胆地移除了它，并借鉴了DAPO论文中的思想，允许模型在“好的方向”上进行更大幅度的探索和更新。\n通过这套组合拳，论文中的模型（基于Qwen2.5-Math-7B）在多个高难度数学推理基准测试中取得了当前最佳性能。例如，在AIME2024测试集上，他们的模型CIR达到了42.3%的准确率，显著优于其他所有基线模型。更重要的是，论文不仅展示了“做什么”，还深入分析了“为什么有效”。通过大量的实验，他们证明了代码集成推理能够扩展模型的能力边界（capability boundaries），并且能以远比长思考链（Long-CoT）更简洁高效的方式解决问题。一个特别有趣的发现是，即使是无法成功执行的代码（non-executable code），有时也能通过返回的错误信息，促使模型反思和修正自身的逻辑，最终反而导向正确答案。这一发现揭示了模型与工具交互的复杂动态，极具启发性。\n总而言之，这篇论文不仅仅是一次模型性能的提升，更是一份关于如何稳定、高效地训练“AI智能体（Agent）”的详细技术指南。它在理论分析和工程实践上都做出了坚实的贡献，为未来构建更强大、更可靠的、能够驾驭复杂工具的通用人工智能系统铺平了道路。\n一、论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是提升代码集成推理（CIR）模型的训练效率和稳定性。它旨在系统性地解决在使用强化学习（RL）来教导大语言模型（LLM）使用代码解释器时所面临的一系列技术挑战。\n解决的实际问题：\nLLM在精确计算上的短板：标准LLM本质上是文本预测模型，对于需要精确数学计算、逻辑推导和符号操作的任务（如高等数学、物理计算、金融分析等）常常力不从心，容易出现“一本正经地胡说八道”的情况。通过集成代码解释器，模型可以将这部分工作“外包”给最擅长它的工具。 工具增强型RL训练的不稳定性：虽然强化学习是训练模型使用工具的有效途径，但如前所述，其训练过程充满挑战，容易失败（即训练崩溃）。这篇论文要解决的，就是如何让这个训练过程变得更加稳健（robust）和可复现（reproducible），从“炼丹”变成“工程”。 行业意义：\n推动AI从“聊天机器人”向“问题解决者”转变：一个能够稳定使用工具的LLM，其能力将发生质变。它不再局限于信息检索和内容生成，而是可以成为能够执行复杂任务的智能体（Agent），例如自动化数据分析、编写和调试软件、进行科学模拟等。这是通向通用人工智能（AGI）的关键一步。 提升AI应用的可靠性：在金融、医疗、工程等对准确性要求极高的领域，一个不可靠的AI是无法投入实际应用的。本文提出的稳定性增强策略，直接关系到能否构建出足够可靠、值得信赖的AI系统，对AI技术的商业化落地至关重要。 为多工具协同打下基础：虽然本文聚焦于代码解释器这一个工具，但其提出的关于稳定性的见解和方法论（如边界处理、反馈屏蔽等）具有很强的普适性，可以迁移到训练模型使用搜索引擎、数据库、API等多种工具的场景中，为构建更复杂的多智能体系统（Multi-Agent System）提供了宝贵的经验。 二、论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新在于提出了一套系统的、旨在平衡探索（Exploration）与稳定（Stability）的工具增强型RL训练框架。它不是一个全新的模型结构，而是一系列精巧的训练策略组合。\n与之前的方法相比，其主要特点和优势在于系统性和针对性。之前的工作可能零散地注意到某个问题，但本文是第一个系统性地诊断并“对症下药”的。\n原文引用 (Section 2.3.1): “Our empirical studies reveal that integrating external tools into reinforcement learning (RL) can introduce instability issues stemming from three primary factors: interaction boundary disruptions, distributional shifts between model reasoning and external feedback, and response homogenization due to fixed interaction budgets.” 这段话清晰地指出了本文所要解决的三个核心问题根源。\n下面我们详细解析其创新方法：\n稳定性维护策略 (Stability Maintenance) 精确的交互边界匹配 (Precise matching of interaction boundaries)： 特点：放弃了之前常用的、基于特定关键词（如output）的启发式方法。那种方法很脆弱，如果模型生成的代码里恰好包含了这个词，或者忘记生成这个词，就会导致交互失败。 优势：本文采用基于精确模式匹配的方式来识别完整的代码块（例如，```python ... ```）。如论文中的图1所示，这种方法确保了只有结构完整、语法正确的代码段才会被送去执行，极大地减少了因边界识别错误而引入的“噪声”。这是一个看似微小但极其关键的工程优化，是保证稳定性的第一道防线。 外部工具反馈屏蔽 (External tool feedback masking)： 特点：这是一个非常新颖的思路。在计算RL的损失函数以更新模型时，他们会有选择地忽略（mask out）掉由代码解释器返回的那部分内容。 优势：这解决了“分布偏移”的问题。模型因此可以专注于学习“如何思考”和“如何写代码”，而不需要分心去模仿工具返回结果的特定、僵硬的格式。这使得模型的学习目标更加纯粹和集中，增强了训练的稳定性。 禁用熵增益 (Entropy bonus disabling)： 特点：这与传统RL为了鼓励探索而增加熵的做法背道而驰。 优势：作者敏锐地意识到，与确定性的工具（代码执行结果是唯一的）交互时，过度的随机探索是有害的。它会放大反馈中的噪声，可能导致模型在错误的道路上越走越远。禁用熵增益，使得模型在学习使用工具时行为更加确定，从而促进了学习过程的稳定。 探索性增强策略 (Exploration Enhancement) 渐进式增加交互预算 (Progressive increase of interaction budget)： 特点：交互预算（即一次任务中可以调用工具的次数）不是固定的，而是从少（例如2次）逐步增加到多（例如4次）。 优势：这种“循序渐进”的教学方式非常符合学习规律。它避免了模型在训练初期就被迫探索过于复杂的解题路径，从而过早地收敛到一些次优的简单策略上。这保留了策略的多样性，为后期学习更复杂的任务打下了基础。 KL项移除与非对称截断 (KL term removal \u0026amp; Clip higher)： 特点：大胆地移除了在PPO等算法中用于维持新旧策略一致性的KL散度惩罚项。 优势：作者认为，学习使用工具是一种“质变”，模型需要足够的自由度来探索全新的行为模式。移除KL项就是“松开了缰绳”，让模型可以更大胆地进行创新。同时，采用非对称截断（上界更高）则是在鼓励模型朝奖励更高的方向进行更大幅度的探索，从而加速了有效策略的学习。 三、论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了全面且严谨的实验来验证其方法的有效性，主要包括性能对比实验和机制分析实验。\n实验设计：\n基座模型：选择了在数学和代码能力上都很强的 Qwen2.5-Math-7B 作为主要实验对象。 评测基准：使用了五个业界公认的高难度数学推理基准测试集：MATH500, AMC23, AIME2024, AIME2025 和 OlymMATH。这些测试集覆盖了从竞赛级别到奥林匹克级别的数学难题，能充分考验模型的推理极限。 基线模型：实验设置了非常全面的对照组，分为两类： 纯文本推理模型：不使用代码解释器，包括监督微调模型（QWEN2.5-MATH-7B-INSTRUCT）和纯文本RL模型（SIMPLERL-ZERO-7B, EURUS-2-7B-PRIME）。 代码集成推理模型：也使用代码解释器，包括一个在推理时才使用工具的模型（INSTRUCT-TIR）和一个同样使用工具增强RL训练的先进模型（ToRL-7B）。 评测指标：对于MATH500采用贪心解码（greedy decoding）的准确率，对于其他更难的基准，采用AVG@16（生成16个答案，只要有一个正确就算对），以更好地评估模型的综合能力上限。 实验数据和结果：\n论文的核心结果体现在表1中，它清晰地展示了本文方法（CIR）的优越性。\n模型 (Models) 使用代码 (Code) MATH500 AMC23 AIME24 AIME25 OlymMATH 平均 (Avg.) OAT-ZERO-7B ✗ 79.2 64.2 31.3 10.4 11.3 39.3 ToRL-7B ✓ 83.8 73.2 37.9 29.2 28.5 50.5 CIR (QWEN2.5-M7) ✓ 86.4 74.2 42.3 29.2 31.6 52.4 从上表中可以得出关键结论：\n全面超越基线：本文的CIR模型在平均分上达到了52.4%，超越了所有基线，包括最强的同类竞品ToRL-7B（50.5%）。 在高难度任务上优势明显： \u0026gt; 原文引用 (Section 3.2.1): “On AIME24, for example, CIR achieves 42.3%, a substantial increase compared to EURUS-2-7B-PRIME (18.8%) and OAT-ZERO-7B (31.3%).” 在极具挑战性的AIME24和OlymMATH上，CIR的性能提升尤为显著。在AIME24上，42.3%的准确率相比强大的纯文本RL模型OAT-ZERO-7B的31.3%有巨大提升。这证明了本文的方法在处理复杂问题时具有独特的优势。 代码集成推理的价值：通过与QWEN2.5-MATH-7B-INSTRUCT-TIR（40.2%）对比，可以看出，仅仅在推理时“临时抱佛脚”式地使用工具，远不如通过本文的RL训练方法将工具使用能力“内化”为模型的核心技能。 此外，论文还通过机制分析实验进一步验证了其洞察：\n扩展能力边界：图6的PASS@k曲线显示，无论是否经过RL训练，只要使用了代码（Base_code vs Base_text），模型的潜在能力上限（k值很大时的通过率）都会被极大拓宽。 提升推理效率：表2显示，相比于需要极长推理步骤的Long-CoT模型，CIR模型可以用不到20%的token长度达到甚至超越其准确率，证明了代码推理的简洁高效。 错误代码的价值：表4的数据揭示，在最终正确的解答中，有39.4%的案例在推理过程中包含了至少一次不成功的代码执行。这证明了“失败是成功之母”——错误的反馈也能引导模型走向正确。 四、结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 本文为我们指明了方向，但前路依然充满机遇和挑战。\n值得探索的问题和挑战： 多工具协同与规划：本文只用了一个代码解释器。当模型需要同时使用多种工具（如搜索引擎、数据库、计算器、API）时，问题会变得指数级复杂。模型需要学习一个更高层次的规划（Planning）能力：面对一个任务，先分解，再决定在哪个步骤、调用哪个工具、传递什么参数。如何设计稳定的RL框架来训练这种高级规划能力是一个巨大的挑战。 处理有状态和异步的工具：代码解释器是无状态的（每次调用都是独立的），但许多现实世界的工具（如API会话、数据库连接）是有状态的，且可能是异步返回结果的。模型需要学会管理工具状态和等待异步结果，这对当前的LLM架构和训练方法提出了新的要求。 开放域工具的使用：本文的工具是封闭且确定的。如果工具是开放的（如调用一个不断变化的网站API），其返回结果可能充满噪声甚至具有欺骗性。模型需要具备结果甄别和可信度判断的能力，这对模型的鲁棒性提出了更高要求。 训练效率和成本：基于RL的训练，尤其是需要与外部环境（工具）交互的，通常需要海量的计算资源和时间。研究更样本高效（sample-efficient）的RL算法（如离线RL、模型基RL）来降低训练成本，是该领域能否规模化的关键。 可能催生的新技术和投资机会： AI Agent开发平台：未来，开发者构建AI应用的方式可能不再是直接微调一个LLM，而是在一个AI Agent开发平台上，通过“拖拉拽”的方式为模型配置各种工具集，并利用平台提供的预置稳定训练方案来快速构建一个特定领域的智能体。这类平台将成为新的基础设施，是巨大的投资机会。 垂直领域智能体解决方案：在金融（量化交易）、生物（药物发现）、法律（合同分析）、软件工程（自动化测试）等专业领域，可以利用本文的技术思路，打造能够深度集成领域专用工具和数据库的垂直领域智能体，提供高价值的自动化服务。 新一代的“Agentic”芯片或硬件：随着Agent与环境的交互越来越频繁，当前的硬件架构可能成为瓶颈。未来可能会出现专门为Agent模型优化的芯片，它们能更高效地处理模型推理与外部I/O之间的频繁切换和数据流转。 五、退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判性视角审视，仍存在一些可以探讨的局限性：\n领域泛化性存疑： 本文的所有实验都集中在数学推理这一高度结构化的领域。代码解释器在这种场景下是“天选之子”。然而，对于那些更偏向自然语言理解、创造性写作或常识推理的任务，代码工具的价值相对有限。本文提出的训练策略能否以及如何泛化到这些领域，是一个未经证实的问题。 对基座模型能力的依赖： 论文选用的是Qwen2.5-Math-7B，这是一个本身就在代码和数学上经过深度优化的模型。本文的成功，有多少归功于其精妙的RL策略，又有多少是源于基座模型的强大基础？ 如果将同样的方法应用于一个通用的、未经数学优化的基座模型（如LLaMA-3），效果会如何？实验的缺失使得我们难以完全剥离这两者的贡献。 对简单工具交互的简化： 本文处理的是一个相对简单的、单轮、无状态的工具。对于需要多轮对话、维护复杂状态的真实API交互，其提出的“精确边界匹配”和“反馈屏蔽”策略可能不足以应对。例如，API的错误返回可能包含重要的调试信息，简单屏蔽掉或许会丢失关键的学习信号。 可解释性的挑战： 论文中“不可执行代码也能带来益处”的发现虽然有趣，但也引出了新的可解释性难题。当模型最终给出错误答案时，我们很难判断问题出在模型的逻辑推理、代码生成能力，还是对工具错误返回的错误解读上。这使得模型的调试和迭代变得更加困难。 奖励设计的简单化： 本文采用了“最终答案正确与否”的二元稀疏奖励。这种奖励方式虽然简单，但可能不够高效。未来是否可以设计更稠密的过程奖励（process rewards），比如对模型推理步骤的合理性、代码的逻辑正确性等进行奖励，从而加速学习？论文没有在这方面进行探索。 六、我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 这篇论文是理论与实践紧密结合的典范，充满了可以借鉴的创新思想。\n重点学习与启发： “稳定压倒一切”的工程哲学：在将LLM与外部系统（工具）集成时，最大的挑战往往不是功能实现，而是系统稳定性。本文提出的一系列稳定性策略，特别是精确边界定义和输入/输出隔离（反馈屏蔽）的思想，是任何试图构建可靠Agent系统的开发者都应首先考虑的黄金法则。 “先易后难”的课程学习法：渐进式增加交互预算的策略，本质上是一种课程学习（Curriculum Learning）。在训练复杂模型时，不要一开始就给它最难的任务，而应该设计一个由易到难的学习路径。这个思想可以应用在各种模型的训练中。 对RL算法的“魔改”意识：不要迷信某个算法的“标准形态”。本文大胆移除KL惩罚项的做法告诉我们，要深刻理解算法每个组件的作用，并根据具体任务的特点进行针对性的调整甚至反向操作。为了学习“新能力”，有时就需要打破“旧束缚”。 拥抱“建设性的失败”：从“不可执行代码亦有益”的发现中，我们应该学到，工具的失败反馈不是垃圾信息，而是宝贵的学习信号。在设计Agent时，应该重点构建模型从失败中恢复（Error Recovery）的能力，这比单纯追求工具调用的成功率可能更有价值。 需要补充的背景知识： 强化学习基础，特别是PPO：要透彻理解本文，你需要对强化学习有基本的认识，尤其是PPO（Proximal Policy Optimization）算法。你需要了解策略（Policy）、奖励（Reward）、价值函数（Value Function）、优势函数（Advantage Function）以及KL散度在其中的具体作用。 LLM的训练范式：了解从预训练（Pre-training）到监督微调（Supervised Fine-tuning, SFT），再到基于人类反馈的强化学习（Reinforcement Learning from Human Feedback, RLHF）的整个流程。本文的Tool-augmented RL可以看作是RLHF在工具使用场景下的一个变种。 思维链（Chain-of-Thought, CoT）及其变体：了解什么是思维链（CoT），以及像本文提到的Long-CoT这类技术，因为本文将CIR定位为一种比CoT更高效的推理范式。理解它们的原理，能帮助你更好地把握CIR的优势所在。 Agent和工具学习（Tool Learning）的前沿研究：可以关注一些相关的综述或关键论文，例如Google的ReAct、Toolformer等，了解当前业界在Agent和工具学习方向上的不同技术路线，从而将本文的研究成果置于一个更广阔的学术图景中进行理解。 ","date":"2025-07-26T00:00:00Z","permalink":"/towards-effective-code-integrated-reasoning/","title":"Towards Effective Code-Integrated Reasoning"},{"content":"论文深度解读：从“混沌”到“有序”，Routine框架如何驯服企业级AI智能体 当前，以大型语言模型（LLM）为核心的自主智能体（Autonomous Agents）正以前所未有的速度发展，展现出在数据分析、人机交互等领域的巨大潜力。然而，当我们将这些通用智能体置于规则严密、流程复杂的企业环境中时，往往会遭遇“水土不服”的窘境。论文开篇就指出了这一核心挑战：\nThe deployment of agent systems in an enterprise environment is often hindered by several challenges: common models lack domain-specific process knowledge, leading to disorganized plans, missing key tools, and poor execution stability. （在企业环境中部署智能体系统常常受到几个挑战的阻碍：通用模型缺乏领域特定的流程知识，导致规划混乱、关键工具缺失以及执行稳定性差。）\n通用大模型如同一个拥有渊博知识但缺乏特定公司工作经验的“实习生”，在面对企业内部复杂的工具链和业务逻辑时，常常会“手足无措”，无法稳定、准确地完成多步骤任务。\n为了解决这一难题，论文作者提出了 Routine 框架，其本质是一个结构化的、面向任务的规划脚本。它不再让模型在运行时进行完全自主、不可预测的“自由发挥”，而是像一份详尽的“标准作业程序”（SOP），清晰地指导模型每一步应该做什么、调用哪个工具。这标志着一种从追求“完全自主”到强调“可靠可控”的范式转变，而后者恰恰是企业应用最看重的品质。\nRoutine的设计精妙之处在于它将一个复杂的智能体任务清晰地解耦为两个核心环节：\n规划（Planning）：这个环节负责生成Routine。它可以由领域专家提供草稿，再由一个能力强大的大模型（如GPT-4o）进行优化和补全，生成包含明确步骤、工具和参数描述的结构化计划。这一过程可以离线完成，确保了计划的质量和周密性。 执行（Execution）：这个环节由一个相对轻量级、经过专门微调的模型负责。它的任务不再是复杂的推理和规划，而仅仅是严格遵循Routine的指令，一步步调用工具并传递参数。 这种“规划与执行分离”的架构带来了显而易见的优势：它允许我们用最强大的大脑（大模型+专家）来做最复杂的规划，同时用一个更经济、更高效的小模型来处理高频的执行任务，实现了能力与成本的最佳平衡。\n为了支撑这套框架，论文还设计了一套完整的系统架构，如Figure 2所示，包括规划模块、执行模块、工具模块（MCP Server）和创新的双重内存模块（Memory Module）。其中，内存模块的设计尤其值得称道： * 程序内存（Procedure Memory）：用于存储和检索整个Routine库。当用户提出请求时，系统能快速匹配到最合适的Routine。 * 变量内存（Variable Memory）：这是一个巧妙的上下文管理机制。当工具调用返回过长的结果（如一整篇文章）时，系统会将其存入变量内存，并只在上下文中保留一个简短的键（如VAR_AI_ETHICS_TEXT_001）。这极大地压缩了上下文长度，降低了token消耗，并减少了长文本可能导致的模型“分心”或语法错误。\nimage.png 当然，仅有框架是不够的，如何让模型真正“学会”使用Routine是关键。为此，论文提出了两种互补的训练策略： 1. 通用Routine遵循能力训练：使用一个开源的多工具数据集（BUTTON），通过GPT-4o生成对应的Routine，然后用这些数据对模型进行微调。目的是让模型学会理解并遵循Routine这种结构化指令的“语法”。 2. 特定场景知识蒸馏（Knowledge Distillation）：这是论文的另一大亮点。针对具体场景（如文中的HR Agent），研究者让“教师模型”（GPT-4o）在Routine的指导下，生成大量高质量的、包含完整多步工具调用的“完美范例”。然后，用这些“蒸馏”出的数据去微调一个“学生模型”（如Qwen3-14B）。这一过程，本质上是将蕴含在Routine中的流程知识和教师模型的强大执行能力，“烘焙”进了轻量级模型的参数中。\n实验结果是惊人的。论文在一个包含25个工具的真实HR Agent场景中进行了严谨的测试。关键数据显示（见Table 2），在没有Routine指导时，即便是强大的GPT-4o，其多步工具调用的端到端准确率也仅有41.1%。而引入Routine后，其准确率飙升至96.3%！对于开源模型Qwen3-14B，效果同样显著，准确率从32.6%提升到83.3%。\n更具启发性的是，经过特定场景知识蒸馏后，轻量级的Qwen3-14B模型在没有Routine指导的情况下，准确率也能达到90.2%，当再次获得Routine指导时，准确率更是高达95.5%，几乎追平了GPT-4o。这雄辩地证明了，Routine框架不仅能作为一种外部“提示”来指导模型，还能作为一种有效的“知识载体”，通过蒸馏将领域知识内化到小模型中，从而构建出高稳定、低成本的企业级智能体。\n总而言之，这篇论文为业界提供了一份极具操作性的蓝图，它巧妙地平衡了智能体的自主性与可靠性，通过结构化规划、执行解耦和知识蒸馏等一系列创新方法，为大模型在严肃、复杂的企业场景中大规模落地铺平了道路，真正向着“AI for Process”（AI赋能流程）的技术愿景迈出了坚实的一步。\n接下来，我将按照您提出的六个问题，进行更详细的解读。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标： 核心目标：设计并验证一个名为 Routine 的结构化规划框架，旨在显著提升LLM智能体在执行特定领域、多步骤工具调用任务时的稳定性、准确性和可靠性。 延伸目标：探索如何通过该框架，使得轻量级、低成本的本地化模型能够在特定企业场景中达到甚至逼近顶级闭源模型的性能水平。 解决的实际问题： 问题：当前通用大模型在企业环境中落地时面临的“最后一公里”难题。即便是最先进的模型，在面对企业内部特有的、复杂的业务流程和工具集时，也常常因为缺乏专门的领域知识而表现出“规划混乱、执行不稳”的问题。例如，模型可能会选择错误的工具、遗漏关键步骤（如权限验证），或在多步任务中途“迷失方向”，导致任务失败。 具体场景：想象一下一个企业HR智能体，需要处理“查询某员工上个季度的绩效、其所在部门的平均绩效、并生成一份对比报告”的请求。这涉及查询员工信息、查询部门数据、调用分析工具、调用报告生成工具等多个步骤，且步骤之间存在依赖关系。通用模型很可能在这个链条的某一步出错。 对行业发展的重要性： 推动AI在企业核心业务中的应用：目前，AI在企业的应用多集中在客服、内容生成等外围环节。要深入到ERP、CRM、SCM等核心业务流程，系统的可靠性是第一要求。Routine框架通过提供一种可控、可预测的执行模式，极大地增强了企业对AI智能体的信任，是AI从“玩具”走向“生产力工具”的关键一步。 降低企业部署AI的门槛和成本：顶级大模型的API调用费用高昂，且存在数据隐私风险。Routine框架通过知识蒸馏，使得企业可以部署性能媲美大模型、但成本更低、数据更安全的本地化小模型。这对于广大中小型企业或对数据安全要求极高的行业（如金融、医疗）意义重大。 催生新的技术生态和商业模式：围绕Routine的设计、管理、优化和分发，可能会诞生新的“智能体流程编排平台”、“领域知识蒸馏服务”等，为AI产业带来新的增长点。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于提出了一整套系统性的方法论，而不仅仅是单一模型。\n新的思路、方法与模型：\nRoutine框架本身： 特点：它是一种人类可读、机器可执行的中间表示（Intermediate Representation）。它将模糊的自然语言任务意图，转化为结构化的、包含明确步骤、描述、工具和I/O的计划。 优势：与之前依赖模型在运行时进行“黑盒”规划的Plan-and-Act模式相比，Routine将“Plan”过程前置和显式化，使得整个工作流变得透明、可调试、可维护。开发者可以轻易地修改、增加或删除Routine中的步骤。 “规划-执行”解耦架构（Decoupling Planning and Execution）： 特点：系统在架构上明确分离了负责生成Routine的规划模块（高能力大模型）和负责执行Routine的执行模块（轻量级小模型）。 优势：这是对资源的最优化配置。复杂的、低频的规划任务由“最强大脑”完成，保证了计划的质量；而简单的、高频的执行任务由“经济小脑”完成，保证了效率和成本。这与传统软件工程中“控制面”与“数据面”分离的思想不谋而合。 AI辅助的Routine生成与优化流程： 特点：论文展示了一个半自动化的流程（见Figure 3），即领域专家只需提供一个简单的草稿（Routine Draft），强大的规划模型就能自动将其丰富、细化为一个完整的Routine。 优势：这大大降低了创建和维护Routine的门槛和工作量，使其具有更好的可扩展性，避免了完全依赖人工编写的瓶颈。 双重内存机制（Dual Memory）： 程序内存（Procedure Memory）：这类似于一个“流程库”，负责存储和检索Routines。 变量内存（Variable Memory）：这是针对LLM上下文长度限制的精巧设计，通过键值对的方式传递长信息。 优势：相比于将所有信息（工具定义、历史记录等）一股脑塞进Prompt的粗暴方式，这种精细化的内存管理机制更加高效和稳定，能有效避免因上下文过长导致的性能下降和成本激增。 面向特定场景的知识蒸馏管道： 特点：构建了一个“教师（GPT-4o + Routine）-\u0026gt; 范例 -\u0026gt; 学生（Qwen3-14B）”的知识转移链条。 优势：这是一种非常高效的领域知识迁移方法。它证明了程序性的、流程性的知识（procedural knowledge）同样可以通过蒸馏被模型“学会”，而不仅限于事实性知识。这使得训练高度定制化的、专业的“小钢炮”模型成为可能。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文的实验设计非常严谨、扎实，紧密围绕其核心主张展开。\n实验设置：\n场景：一个真实的企业HR智能体应用。这个场景足够复杂，因为它包含： 多样的子任务：7个不同的子场景，最终可分解为10个无分支的Routines。 丰富的工具集：一个包含25个不同功能（数据查询、权限验证、模型生成等）的MCP服务器。 真实的依赖关系：任务步骤之间存在参数依赖，必须按特定顺序执行。 评估框架：采用了开源的伯克利函数调用排行榜（BFCL） 的评估方法，基于抽象语法树（AST）进行分层评估，能精确地将错误归因于结构错误（Structural Error）、工具选择错误（Tool Selection Error） 和 参数错误（Parameter Error）。 实验设计与关键数据：\n核心有效性验证（Impact of Routine）： 设计：对比了同一模型在三种不同配置下的表现：无Routine（基线）、有线性Routine、有带分支的Routine。 结果：如Table 2所示，这是最有说服力的结果。 \u0026gt; Without Routine guidance, all baseline models performed poorly, with none exceeding 50% overall accuracy. … In particular, GPT-4-Turbo’s performance approached perfection, and the Qwen series models also demonstrated significant gains. \u0026gt; （在没有Routine指导的情况下，所有基线模型表现不佳，整体准确率均未超过50%…… 特别是，GPT-4-Turbo的表现接近完美，Qwen系列模型也显示出显著增益。） 关键数据：Qwen3-14B 的准确率从 32.6% 跃升至 83.3%，提升了约 50个百分点。这证明了Routine框架的普适性和巨大价值。错误分析表明，性能提升主要来源于工具选择准确率的大幅提高。 模型训练效果验证（Impact of Model Training）： 设计：对比了原始模型、经过“通用Routine遵循训练”的模型、以及经过“特定场景知识蒸馏”的模型在不同配置下的表现。 结果： 通用训练能提升模型在有Routine指导时的执行力，但在无Routine时反而会下降，说明模型被“训练”成了一个忠实的执行者，而非规划者。 知识蒸馏的效果非常惊艳。经过蒸馏的Qwen3-14B，在没有Routine的情况下，准确率达到了90.2%，远超其原始基线（32.6%），甚至超过了有Routine指导时的原始表现（83.3%）。这表明领域知识成功内化了。 消融研究（Ablation Study）： Routine组件影响（Table 3）：移除了Routine中的“工具名称”，只保留步骤描述，模型的准确率大幅下降（Qwen3-14B从79.1%降至69.5%）。这证明了明确的工具指令是保证执行准确性的核心要素。 Routine生成方式影响（Table 4）：对比了用户草稿、AI优化、人工标注三种Routine。结果显示，AI优化后的Routine（AI Optimization） 已经能达到很高的水平（Qwen3-14B为76.7%），非常接近甚至在某些模型上超过了完美的人工标注（Human Annotation）版本（83.3%），证明了AI辅助生成流程的巨大潜力。 Routine数量影响（Table 5）：在Prompt中提供多个候选Routine（1个正确，其余为干扰项）会显著降低模型性能。这强调了程序内存（Procedure Memory） 进行高精度检索的重要性，一次只给模型最相关的那个Routine才是最佳实践。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文打开了一扇通往可靠企业智能体的大门，同时也揭示了更多值得探索的方向。\n值得探索的问题和挑战：\nRoutine的动态适应与自主学习： 挑战：目前的Routine是静态的，当企业业务流程或工具集发生变化时，需要人工或半自动地更新Routine。这是一个可扩展性的瓶颈。 探索方向：结合强化学习（Reinforcement Learning）。智能体可以在执行Routine的基础上进行探索（Exploration），如果发现了更优的路径或成功处理了异常，就可以通过奖励信号来更新或生成新的Routine。这篇论文的第6节也提到了这一点，这是从“死板执行”到“动态进化”的关键一步。 从文档到Routine的全自动生成： 挑战：目前Routine的生成仍需要专家提供草稿。如何实现“零人工”的全自动化？ 探索方向：训练一个专门的“流程理解模型”，能够自动阅读企业的内部文档（如SOP、API文档、知识库），并直接从中抽取出结构化的Routine。这需要强大的文档理解、实体关系抽取和逻辑推理能力。 层级化与多智能体协作（Hierarchical \u0026amp; Multi-Agent Systems）： 挑战：对于极其复杂的企业级任务（如全流程的供应链管理），单一的线性Routine可能过于冗长和脆弱。 探索方向：构建一个层级化的多智能体系统。一个“总指挥”智能体负责顶层规划，将任务分解，并生成宏观的Routine；然后将子任务分配给多个高度专业化的“执行者”智能体，每个执行者再依据自己的、更具体的Routine来完成任务。这能极大提升系统的模块化和鲁棒性。 可能催生的新技术和投资机会：\n企业智能体流程编排（Orchestration）平台：提供可视化界面，让业务人员可以通过拖拉拽的方式设计、测试和管理Routine库，并一键部署到不同的执行模型上。 领域知识蒸馏即服务（Knowledge Distillation as a Service）：专门为企业提供服务，利用其私有数据和业务流程，通过类似本文的蒸馏管道，为其训练出轻量、高效、安全的定制化智能体模型。 “AI流程挖掘”工具：能够分析企业现有的操作日志和数据流，自动发现并生成最优的业务流程（Routines），帮助企业实现流程再造和自动化。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判性的学术角度审视，仍有一些可以探讨的局限性。\n潜在的不足与缺失：\n灵活性与泛化能力的权衡： 不足：Routine框架的核心优势是其结构性和稳定性，但这同时牺牲了智能体的灵活性和处理未知情况的能力。如果用户提出了一个偏离所有已知Routine的“边缘请求”，系统可能会束手无策或错误地匹配到一个不完全适用的Routine。论文承认了这是一个权衡，但未深入探讨如何优雅地处理“Routine Miss”的情况。 Routine创建与维护的隐性成本： 不足：虽然论文提出了AI辅助优化，但整个流程的起点仍然是“专家草稿”。对于拥有成百上千个业务流程的大型企业而言，创建和持续维护这个“Routine库”的真实人力成本和时间成本可能非常高昂。论文对此没有进行量化分析。 实验场景的单一性： 不足：尽管HR场景很典型，但它仍然是相对封闭和确定的。在一些更加开放和动态的领域，比如市场分析（需要实时调用外部API、处理非结构化新闻）、软件开发（代码库和需求频繁变更），Routine框架的有效性是否依然如此出色？需要更多样化的案例来验证其普适性。 对“程序内存”的检索精度要求过高： 存疑之处：论文的消融研究（Table 5）表明，即使只增加一个干扰Routine，模型的性能也会大幅下降。这反向说明，系统中的“程序内存”模块必须要有极高的检索精度，确保每次都能“又快又准”地找到唯一正确的Routine。然而，论文并未详细阐述这个高精度检索模块是如何实现的，这是一个非平凡的挑战。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是理论与实践结合的典范，充满了可以借鉴的智慧。\n重点学习与启发：\n拥抱务实主义：可靠性优于完全自主性。 启发：在企业级应用中，不要盲目追求一个无所不能、完全自主的“黑盒”AI。一个行为可预测、结果稳定、过程可追溯的系统远比一个时而惊艳、时而犯错的天才更有价值。将复杂任务流程化、结构化，是让AI落地的关键。 分而治之：解耦复杂问题。 启发：“规划与执行分离”的思想极具普适性。在设计任何复杂AI系统时，都可以思考：哪些是需要强大推理能力的、低频的“决策任务”？哪些是需要高效稳定执行的、高频的“操作任务”？尝试为它们匹配不同能力和成本的模型或模块。 知识蒸馏是实现“小模型、大能力”的利器。 启发：当你想让一个小模型掌握特定领域的复杂能力时，不要只想着喂给它原始数据。可以先用一个大模型（或者规则引擎、或者人类专家）在这个领域生成大量“高质量的解题过程”，然后让小模型去学习这些“过程”，而不是“结果”。这是一种高效的知识迁移模式。 中间表示（IR）的力量。 启发：Routine本质上是一种精心设计的IR。一个好的IR能够成为系统不同模块之间清晰的“沟通语言”，同时兼具人类可读性和机器可处理性。在你自己的项目中，思考一下是否也能设计一个类似的IR来解耦系统、降低复杂度。 需要补充的背景知识：\nLLM Agent主流框架：了解像 ReAct (Reason+Act)、Plan-and-Act 等基础的智能体工作模式，以便更好地理解Routine框架的创新之处。 模型微调技术：特别是 LoRA (Low-Rank Adaptation)，了解其原理，明白为什么可以用较低的成本对大模型进行有效微调。 知识蒸馏（Knowledge Distillation）：理解其基本概念，即“教师模型”如何将知识转移给“学生模型”。 企业业务流程管理（BPM）：了解企业中标准作业程序（SOP）、工作流等概念，你会发现Routine的思想与之有异曲同工之妙，它是AI时代的动态SOP。 ","date":"2025-07-25T00:00:00Z","permalink":"/routinea-structural-planning-framework-for-llm-agent-system-in-enterprise/","title":"Routine：A Structural Planning Framework for LLM Agent System in Enterprise"},{"content":"Verl 最近实现了 agent loop 功能，也就是多轮工具调用 RL ，弥补了 verl 中 vllm 无法使用多轮 rollout 的不足。整体流程大致如下（来自 https://github.com/zhaochenyang20/Awesome-ML-SYS-Tutorial/blob/main/rlhf/verl/multi-turn/imgs/Multi-Turn_Rollout_Workflow.png）\nimage.png 在官方实现中（目前在 verl/experimental/agent_loop 目录下），核心代码在 agent_loop.py中，single_turn_agent_loop.py和tool_agent_loop对应两种agent_loop，tool_parser.py定义了hermes工具解析类。所以重点就是在agent_loop.py中，各个类的协作流程如下图：\nimage.png verl官网图如下（Agent Loop — verl documentation）： 按照上面的流程图进行逐步讲解。\nAgentLoopManager AgentLoopManager是入口处，在ray_trainer.py中由actor_rollout_wg初始化：\nif self.config.actor_rollout_ref.rollout.mode == \u0026quot;async\u0026quot;: from verl.experimental.agent_loop import AgentLoopManager self.async_rollout_mode = True self.async_rollout_manager = AgentLoopManager( config=self.config, worker_group=self.actor_rollout_wg, ) AgentLoopManager需要先启动多个推理引擎服务器，数量为dp_size（world_size/tp_size）。代码：\nclass AgentLoopManager: \u0026quot;\u0026quot;\u0026quot;Agent loop manager that manages a group of agent loop workers.\u0026quot;\u0026quot;\u0026quot; def __init__(self, config: DictConfig, worker_group: RayWorkerGroup): \u0026quot;\u0026quot;\u0026quot;Initialize agent loop manager. Args: config (DictConfig): trainer config. worker_group (RayWorkerGroup): ActorRolloutRef worker group. \u0026quot;\u0026quot;\u0026quot; self.config = config self.worker_group = worker_group self._initialize_llm_servers() self._init_agent_loop_workers() def _initialize_llm_servers(self): # 这里也对应了一开始图中的最下面的部分 self.rollout_tp_size = self.config.actor_rollout_ref.rollout.tensor_model_parallel_size self.rollout_dp_size = self.worker_group.world_size // self.rollout_tp_size ... unready_dp_ranks = set(range(self.rollout_dp_size)) while len(unready_dp_ranks) \u0026gt; 0: servers = { rollout_dp_rank: server_class.options( # make sure AsyncvLLMServer colocates with its corresponding workers scheduling_strategy=ray.util.scheduling_strategies.NodeAffinitySchedulingStrategy( node_id=workers_info[rollout_dp_rank * self.rollout_tp_size], soft=False, ), name=f\u0026quot;async_llm_server_{rollout_dp_rank}\u0026quot;, ).remote(self.config, self.rollout_dp_size, rollout_dp_rank, self.worker_group.name_prefix) for rollout_dp_rank in unready_dp_ranks } for rollout_dp_rank, server in servers.items(): try: address = ray.get(server.get_server_address.remote()) self.server_addresses[rollout_dp_rank] = address self.async_llm_servers[rollout_dp_rank] = server unready_dp_ranks.remove(rollout_dp_rank) except Exception: ray.kill(server) print(f\u0026quot;rollout server {rollout_dp_rank} failed, maybe address already in use, restarting...\u0026quot;) # All server instances are ready, init AsyncLLM engine. ray.get([server.init_engine.remote() for server in self.async_llm_servers]) AgentLoopManager负责管理多个AgentLoopWorker，个数由参数rollout.agent.num_workers确定。然后将prompt切分成num_worker个，让各个worker分别进行推理，这部分的代码如下：\n## 初始化各个worker def _init_agent_loop_workers(self): self.agent_loop_workers = [] for i in range(self.config.actor_rollout_ref.rollout.agent.num_workers): self.agent_loop_workers.append( AgentLoopWorker.options( name=f\u0026quot;agent_loop_worker_{i}\u0026quot;, ).remote(self.config, self.async_llm_servers) ) ... ## 根据worker的数量切分prompts，推理后再合并起来 def generate_sequences(self, prompts: DataProto) -\u0026gt; DataProto: if self.config.actor_rollout_ref.rollout.free_cache_engine: self.wake_up() chunkes = prompts.chunk(len(self.agent_loop_workers)) outputs = ray.get( [ worker.generate_sequences.remote(chunk) for worker, chunk in zip(self.agent_loop_workers, chunkes, strict=True) ] ) output = DataProto.concat(outputs) 从初始化也说明了，我们在RayPPOTrainer中init_workers方法里面初始化好AgentLoopManager，同时也初始化好了推理服务器和各个workers。 ## AsyncLLMServerManager\n在上面的代码中，可以看到我们在创建各个worker的时候传入了初始化好的servers，需要一个类来对这些server进行管理，即AsyncLLMServerManager类。具体的作用就是针对不同的request，来选择哪一个server进行推理，使用LRU算法来进行管理，具体如下：\ndef __init__(self, config: DictConfig, server_handles: list[ray.actor.ActorHandle], max_cache_size: int = 10000): \u0026quot;\u0026quot;\u0026quot;Initialize the AsyncLLMServerManager. Args: config (DictConfig): YAML config. server_handles (List[ray.actor.ActorHandle]): OpenAI compatible LLM server actor handles. max_cache_size (int, optional): max cache size for request_id to server mapping. Defaults to 10000. \u0026quot;\u0026quot;\u0026quot; self.config = config self.server_handles = server_handles random.shuffle(self.server_handles) # Least requests load balancing self.weighted_serveres = [[0, (hash(server), server)] for server in server_handles] heapq.heapify(self.weighted_serveres) # LRU cache to map request_id to server self.request_id_to_server = LRUCache(maxsize=max_cache_size) def _choose_server(self, request_id: str) -\u0026gt; ray.actor.ActorHandle: # TODO: implement server pressure awareness load balancing if request_id in self.request_id_to_server: return self.request_id_to_server[request_id] server = self.weighted_serveres[0][1][1] self.weighted_serveres[0][0] += 1 heapq.heapreplace(self.weighted_serveres, self.weighted_serveres[0]) self.request_id_to_server[request_id] = server return server 具体做法解释如下： 1. 建立了一个最小堆self.weighted_serveres，列表中的每个元素结构是 [0, (hash(server), server)]，即 [请求计数, (服务器哈希, 服务器句柄)]。heapq.heapify(self.weighted_serveres) 将这个列表转换成一个最小堆，堆的排序依据是每个元素的第一个值，也就是 请求计数。因此，请求数最少的服务器始终位于堆的顶部（self.weighted_serveres[0]）。 2. 创建了一个 self.request_id_to_server，这是一个 LRU (Least Recently Used) 缓存。这个缓存用于存储 request_id 到 server 句柄的映射。 3. 首先检查request_id是否在映射中，如果在的话直接返回对应的服务器句柄，否则需要给它分配一个全新的server，也就是请求计数最少的那个server，然后再更新堆和映射。\n这样做的目的是为了各个服务器之间的负载均衡。\n实际的生成就是由这个类进行的，即\n@rollout_trace_op async def generate( self, request_id, *, prompt_ids: list[int], sampling_params: dict[str, Any], ) -\u0026gt; list[int]: \u0026quot;\u0026quot;\u0026quot;Generate tokens from prompt ids. Args: request_id (str): request id for sticky session. prompt_ids (List[int]): List of prompt token ids. sampling_params (Dict[str, Any]): Sampling parameters for the chat completion. Returns: List[int]: List of generated token ids. \u0026quot;\u0026quot;\u0026quot; server = self._choose_server(request_id) output = await server.generate.remote( request_id=request_id, prompt_ids=prompt_ids, sampling_params=sampling_params, ) return output 先选择合适的server，再利用该server的generate方法来进行推理。具体的类和方法在workers/rollout下。\nAgentLoopWorker 现在回到AgentLoopWorker中，前面已经提到了，我们用AgentLoopManager管理多个Worker，每一个Worker都可以用所有创建的server进行推理，接下来就看worker的generate_sequcences方法做了什么：\nasync def generate_sequences(self, batch: DataProto) -\u0026gt; DataProto: \u0026quot;\u0026quot;\u0026quot;Generate sequences from agent loop. Args: batch (DataProto): Input batch. Returns: DataProto: Output batch. - prompts: [bsz, prompt_length], prompt token ids from dataset. - responses: [bsz, response_length], output token ids include response tokens from LLM generation and observation tokens from tool_calls. - response_mask: [bsz, response_length], 1 for LLM generated tokens, 0 for observation/padding tokens. - input_ids: [bsz, prompt_length + response_length], whole sequence token ids, including prompt tokens and response tokens. - attention_mask: [bsz, prompt_length + response_length], 0 for padding tokens, 1 for other tokens. - position_ids: [bsz, prompt_length + response_length], incremental position ids. For multi-turn conversations: responses: |\u0026lt;- LLM generation -\u0026gt;|\u0026lt;- tool_calls -\u0026gt;|\u0026lt;- LLM generation -\u0026gt;|\u0026lt;- padding -\u0026gt;| response_mask: | 1, 1, 1, ..., 1, 1 | 0, 0, .., 0, 0 | 1, 1, 1, ..., 1, 1 | 0, 0, ..., 0| \u0026quot;\u0026quot;\u0026quot; config = self.config.actor_rollout_ref.rollout sampling_params = dict( temperature=config.temperature, top_p=config.top_p, repetition_penalty=1.0, ) # override sampling params for validation if batch.meta_info.get(\u0026quot;validate\u0026quot;, False): sampling_params[\u0026quot;top_p\u0026quot;] = config.val_kwargs.top_p sampling_params[\u0026quot;temperature\u0026quot;] = config.val_kwargs.temperature # by default, we assume it\u0026#39;s a single turn agent if \u0026quot;agent_name\u0026quot; not in batch.non_tensor_batch: batch.non_tensor_batch[\u0026quot;agent_name\u0026quot;] = np.array([\u0026quot;single_turn_agent\u0026quot;] * len(batch), dtype=object) tasks = [] agent_names = batch.non_tensor_batch[\u0026quot;agent_name\u0026quot;] raw_prompts = batch.non_tensor_batch[\u0026quot;raw_prompt\u0026quot;] if \u0026quot;index\u0026quot; in batch.non_tensor_batch: index = batch.non_tensor_batch[\u0026quot;index\u0026quot;] else: index = np.arange(len(raw_prompts)) trajectory_info = await get_trajectory_info( batch.meta_info.get(\u0026quot;global_steps\u0026quot;, -1), index, batch.meta_info.get(\u0026quot;validate\u0026quot;, False) ) for agent_name, messages, trajectory in zip(agent_names, raw_prompts, trajectory_info, strict=True): tasks.append( asyncio.create_task(self._run_agent_loop(agent_name, messages.tolist(), sampling_params, trajectory)) ) outputs = await asyncio.gather(*tasks) output = self._postprocess(outputs) return output 根据注释的信息，输入为batch数据，输出为推理后的prompt、response、mask等。这里需要先判断使用的agent_loop的类型，前面提到这里实现了single_turn和tool两种agent_loop，此外用户可以根据AgentLoopBase抽象类来自定义agent_loop（init_class和run两个方法）。 这里先是有一个判断，如果数据集中没有agent_name字段，则默认就是single_turn类型，因此如果你想使用agent_loop，必须在数据预处理的时候加入agent_name字段为tool_agent。这里创建了一个叫trajectory_info的变量，目的是为了rollout_trace，来区分保存到文件中的各个prompt生成的response。接下来进入到_run_agent_loop方法中：\nasync def _run_agent_loop( self, agent_name: str, messages: list[dict[str, Any]], sampling_params: dict[str, Any], trajectory: dict[str, Any], ) -\u0026gt; AgentLoopOutput: with rollout_trace_attr( step=trajectory[\u0026quot;step\u0026quot;], sample_index=trajectory[\u0026quot;sample_index\u0026quot;], rollout_n=trajectory[\u0026quot;rollout_n\u0026quot;], validate=trajectory[\u0026quot;validate\u0026quot;], name=\u0026quot;agent_loop\u0026quot;, ): assert agent_name in _agent_loop_registry, ( f\u0026quot;Agent loop {agent_name} not registered, registered agent loops: {_agent_loop_registry.keys()}\u0026quot; ) agent_loop_config = _agent_loop_registry[agent_name] agent_loop = hydra.utils.instantiate( config=agent_loop_config, trainer_config=_DummyConfig(config=self.config), server_manager=self.server_manager, tokenizer=self.tokenizer, ) output = await agent_loop.run(messages, sampling_params) return output 对于定义的不同的agent_loop，需要用register装饰器进行注册（设计模式中的工厂模式），register装饰器如下：\ndef register(agent_name: str): \u0026quot;\u0026quot;\u0026quot;Register agent loop class.\u0026quot;\u0026quot;\u0026quot; def decorator(subclass: type[AgentLoopBase]) -\u0026gt; type[AgentLoopBase]: fqdn = f\u0026quot;{subclass.__module__}.{subclass.__qualname__}\u0026quot; _agent_loop_registry[agent_name] = {\u0026quot;_target_\u0026quot;: fqdn} return subclass return decorator 对于装饰的类，将这个信息存入一个名为 _agent_loop_registry 的全局字典中。 - 键 (Key)：在装饰器中提供的 agent_name 字符串 (e.g., “my_agent”)。\n值 (Value)：一个特殊格式的字典 {“target”: “类的完整路径”}。 这里我们注册了两个agent_name，然后利用hydra进行初始化，本文重点介绍tool_agent_loop，接下来转到ToolAgentLoop这个类中。\nToolAgentLoop 首先看初始化：\n@register(\u0026quot;tool_agent\u0026quot;) class ToolAgentLoop(AgentLoopBase): @classmethod def init_class(cls, config, tokenizer, **kwargs): if cls._class_initialized: return cls._class_initialized = True print(\u0026quot;Performing class-level ToolAgentLoop initialization\u0026quot;) # Initialize tools from config file cls.tokenizer = tokenizer cls.max_user_turns = config.actor_rollout_ref.rollout.multi_turn.max_user_turns cls.max_assistant_turns = config.actor_rollout_ref.rollout.multi_turn.max_assistant_turns cls.max_parallel_calls = config.actor_rollout_ref.rollout.multi_turn.max_parallel_calls cls.max_tool_response_length = config.actor_rollout_ref.rollout.multi_turn.max_tool_response_length cls.tool_response_truncate_side = config.actor_rollout_ref.rollout.multi_turn.tool_response_truncate_side tool_config_path = config.actor_rollout_ref.rollout.multi_turn.tool_config_path tool_list = initialize_tools_from_config(tool_config_path) if tool_config_path else [] cls.tools = {tool.name: tool for tool in tool_list} cls.tool_schemas = [tool.tool_schema.model_dump(exclude_unset=True, exclude_none=True) for tool in tool_list] cls.tool_parser = ToolParser.get_tool_parser(config.actor_rollout_ref.rollout.multi_turn.format, cls.tokenizer) print(f\u0026quot;Initialized tools: {cls.tools}\u0026quot;) cls.prompt_length = config.actor_rollout_ref.rollout.prompt_length cls.response_length = config.actor_rollout_ref.rollout.response_length cls.system_prompt = tokenizer.apply_chat_template([{}], add_generation_prompt=False, tokenize=True) 关于工具的初始化，来自于sglang团队，详细可以看博客：Awesome-ML-SYS-Tutorial/rlhf/verl/multi-turn/release_log/verl-multiturn-rollout-Release_ZH.md at main · zhaochenyang20/Awesome-ML-SYS-Tutorial · GitHub\n简单来说就是将工具的信息定义在一个yaml文件中，将文件路径传入到actor_rollout_ref.rollout.multi_turn.tool_config_path参数，然后获取tool_schemas用于后续传入到chat_template中，此外在tool_parser.py中定义了tool_parser。\n这里还有一个值得注意的是system_prompt，这是因为进行chat_template的时候如果没有role为system的会自动加上，而在后面对工具返回结果进行单独chat_template的时候需要将自动添加的system prompt给删除，所以这里预存了一个system_prompt。\n下面我们来看最核心的多轮rollout代码：\n@rollout_trace_op async def run(self, messages: list[dict[str, Any]], sampling_params: dict[str, Any]) -\u0026gt; AgentLoopOutput: metrics = {} request_id = uuid4().hex prompt_ids = await self.loop.run_in_executor( None, lambda: self.tokenizer.apply_chat_template( messages, tools=self.tool_schemas, add_generation_prompt=True, tokenize=True ), ) response_mask = [] user_turns, assistant_turns = 0, 0 while True: with simple_timer(\u0026quot;generate_sequences\u0026quot;, metrics): response_ids = await self.server_manager.generate( request_id=request_id, prompt_ids=prompt_ids, sampling_params=sampling_params ) prompt_ids += response_ids response_mask += [1] * len(response_ids) assistant_turns += 1 # reach max response length if len(response_mask) \u0026gt;= self.response_length: break # reach max assistant turns if self.max_assistant_turns and assistant_turns \u0026gt;= self.max_assistant_turns: break # reach max user turns if self.max_user_turns and user_turns \u0026gt;= self.max_user_turns: break # no tool calls _, tool_calls = await self.tool_parser.extract_tool_calls(response_ids) if not tool_calls: break # call tools tasks = [] for tool_call in tool_calls[: self.max_parallel_calls]: tasks.append(self._call_tool(tool_call)) with simple_timer(\u0026quot;tool_calls\u0026quot;, metrics): tool_responses = await asyncio.gather(*tasks) if any(isinstance(item, Exception) for item in tool_responses): break # append tool_response_ids tool_response_ids = await self.loop.run_in_executor( None, lambda messages=tool_responses: self.tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True ), ) tool_response_ids = tool_response_ids[len(self.system_prompt) :] # NOTE: last turn should not be user turn, or the EOS token reward # can\u0026#39;t be propagated to previous token in GAE. if len(response_mask) + len(tool_response_ids) \u0026gt;= self.response_length: break prompt_ids += tool_response_ids response_mask += [0] * len(tool_response_ids) user_turns += 1 response_ids = prompt_ids[-len(response_mask) :] prompt_ids = prompt_ids[: len(prompt_ids) - len(response_mask)] output = AgentLoopOutput( prompt_ids=prompt_ids, response_ids=response_ids[: self.response_length], response_mask=response_mask[: self.response_length], num_turns=user_turns + assistant_turns + 1, metrics=metrics, ) return output async def _call_tool(self, tool_call: FunctionCall) -\u0026gt; dict[str, str]: \u0026quot;\u0026quot;\u0026quot;Call tool and return tool response.\u0026quot;\u0026quot;\u0026quot; tool, instance_id = None, None try: # TODO: append malformed tool_call to the prompt: invalid function name or arguments tool_name = tool_call.name tool_args = json.loads(tool_call.arguments) tool = self.tools[tool_name] instance_id = await tool.create() tool_response, _, _ = await tool.execute(instance_id, tool_args) except Exception as e: logger.exception(f\u0026quot;Error when executing tool: {e}\u0026quot;) return e finally: if tool and instance_id: await tool.release(instance_id) if len(tool_response) \u0026gt; self.max_tool_response_length: if self.tool_response_truncate_side == \u0026quot;left\u0026quot;: tool_response = tool_response[: self.max_tool_response_length] + \u0026quot;...(truncated)\u0026quot; elif self.tool_response_truncate_side == \u0026quot;right\u0026quot;: tool_response = \u0026quot;(truncated)...\u0026quot; + tool_response[-self.max_tool_response_length :] else: length = self.max_tool_response_length // 2 tool_response = tool_response[:length] + \u0026quot;...(truncated)...\u0026quot; + tool_response[-length:] return { \u0026quot;role\u0026quot;: \u0026quot;tool\u0026quot;, \u0026quot;content\u0026quot;: tool_response, } 还记得我们这里传入的batch是哪里来的吗？没错，是来自于AgentLoopManager根据num_workers切分的prompt。在run方法一开始，我们为每一个切分后的prompts设定一个request_id，在AsyncLLMServerManager中管理映射。 接下来对prompt进行chat_template，传入我们前面定义好的tool_schemas，这样system_prompt就会加入对应格式的工具信息。\n之后就进入到了多轮对话中，先是使用ServerManager进行generate，拼接上prompt_ids（为什么直接拼接tokenid详见issue:训练途中突然崩了，无论是grpo还是reinforce++，出现nan · Issue #30 · 0russwest0/Agent-R1）。\n我们直接跳到工具解析的部分，定义好的tool_parser从response_ids中解析工具调用信息（其实就是正则表达式匹配），如果没有工具调用，则直接结束，否则就调用_call_tool方法调用工具。调用完工具后返回工具调用的结果，然后将其编码，并截断前面的system prompt，拼接在prompt_ids后，用于下个循环的推理。注意在训练的时候要将工具返回的结果进行掩码。也就是response_mask += [0] * len(tool_response_ids)。当达到最大循环或者回复长度超出设定的response_length，就结束推理。并按照AgentLoopOutput进行返回。\n返回 返回后，我们回到AgentLoopWorker的generate_sequences方法中，可以看到我们将返回的结果都放入了outputs变量中，而在最终输出前，需要进行后处理，也就是_postprocess方法。需要对prompt进行左pad，对response进行右pad，最后整合成DataProto返回。\ndef generate_sequences(self, prompts: DataProto) -\u0026gt; DataProto: \u0026quot;\u0026quot;\u0026quot;Split input batch and dispatch to agent loop workers. Args: prompts (DataProto): Input batch. Returns: DataProto: Output batch. \u0026quot;\u0026quot;\u0026quot; if self.config.actor_rollout_ref.rollout.free_cache_engine: self.wake_up() chunkes = prompts.chunk(len(self.agent_loop_workers)) outputs = ray.get( [ worker.generate_sequences.remote(chunk) for worker, chunk in zip(self.agent_loop_workers, chunkes, strict=True) ] ) output = DataProto.concat(outputs) if self.config.actor_rollout_ref.rollout.free_cache_engine: self.sleep() # calculate performance metrics metrics = [output.meta_info[\u0026quot;metrics\u0026quot;] for output in outputs] # List[List[Dict[str, str]]] timing = self._performance_metrics(metrics, output) output.meta_info = {\u0026quot;timing\u0026quot;: timing} return output 然后回到AgentLoopWorker的generate_sequences方法中，将各个worker推理的结果concat起来。并将一些metric放入meta_info来logger。\n","date":"2025-07-24T00:00:00Z","permalink":"/agent_loop/","title":"agent_loop"},{"content":"如果想将模型训练扩展到大的批次，则很快就会达到在单个 GPU 上可以做的极限。具体来说，会发生 RuntimeError: CUDA out of memory。 梯度累计、Activation checkpointing 和 CPU offloading 都可以一定程度上减少显存的占用，为了_有效地_扩展到更大的模型大小和不断增长的数据集，同时仍然在合理的时间内训练模型，我们需要将计算分布在一组机器上。\n3 D 并行即：数据并行、张量并行、流水线并行 后两者可以统一划分到模型并行，区别是一个是层内并行，一个是层间并行。\n这里介绍数据并行。\nNaive data parallel 一个很直觉的做法就是在 batch 维度上进行划分，各个卡上初始化完整的模型，然后将将划分的不同的 batch 发送到各个卡上进行前向传播和反向传播，再由一个卡整合梯度再下发给各 GPU，然后各 GPU 更新自己维护的模型参数。\nimage.png 但这种做法显然有很多问题，需要有一个 gpu 担任梯度聚合和下发的角色，如果这个 gpu 出问题了怎么办？每一个 gpu 都需要维护完整的模型参数、梯度和优化器，这部分的显存没有得到减少；此外这种方式通讯量很大，详见显存占用计算\nDDP DDP 解决的问题就是将 Server 上的通讯压力均衡转移到各个 worker 上（Server 即担任梯度聚合和下发的角色，而 worker 就是各个 gpu），因此引入了 ring-all-reduce 算法来解决这个问题。需要把反向传播后的梯度切分成 N（world_size）份来进行 ring-all-reduce 算法。\nzero zero ## fsdp\nfsdp ## 参考\nThe Ultra-Scale Playbook: Training LLMs on GPU Clusters 💥 Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU \u0026amp; Distributed setups | by Thomas Wolf | HuggingFace | Medium Training extremely large neural networks across thousands of GPUs. # 图解大模型训练之：数据并行上篇(DP, DDP与ZeRO) ","date":"2025-07-23T00:00:00Z","permalink":"/data_parallel/","title":"data_parallel"},{"content":"参考 The Ultra-Scale Playbook: Training LLMs on GPU Clusters 💥 Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU \u0026amp; Distributed setups | by Thomas Wolf | HuggingFace | Medium Training extremely large neural networks across thousands of GPUs. ","date":"2025-07-23T00:00:00Z","permalink":"/pipeline-parallelism/","title":"pipeline parallelism"},{"content":"参考 The Ultra-Scale Playbook: Training LLMs on GPU Clusters 💥 Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU \u0026amp; Distributed setups | by Thomas Wolf | HuggingFace | Medium Training extremely large neural networks across thousands of GPUs. ","date":"2025-07-23T00:00:00Z","permalink":"/tensor_parallel/","title":"tensor_parallel"},{"content":"分为zero1、zero2、zero3，虽然zero3对模型进行了分割，但是本质上还是属于数据并行，因为在前向传播和反向传播需要all-gather模型参数，需要完整的模型权重。\n先来一张经典的图片：\nimage.png 这里的K具体是多少可以看显存占用计算\n接下来逐个介绍各个stage zero。随着zero优化的深入，越来越节省显存，但通讯时间也大大增加，导致训练时间增加。 ## zero1 针对优化器状态进行分割。\nzero2 针对优化器状态和梯度进行分割。\nzero3 针对优化器状态、梯度和模型参数进行分割。\n","date":"2025-07-22T00:00:00Z","permalink":"/zero/","title":"zero"},{"content":"论文深度解读：从“思考时搜索”到“思考时搜索并提炼”的范式革命 这篇论文的核心贡献在于，它挑战了传统检索增强生成（RAG）系统中一个根深蒂固的、看似理所当然的流程，并提出了一种更为精细、鲁棒和智能的替代范式。传统的 RAG 模型通常遵循一种“思考时搜索”（search-during-think）的模式：当模型在生成答案的过程中意识到知识不足时，它会触发一次或多次搜索，获取外部文档，然后直接基于这些（可能混杂着大量噪声的）文档来生成最终答案。这种方法的致命弱点在于，它假设模型能够自行从混杂、冗长甚至可能错误的信息中精准地“淘金”，而现实是，这种“信息投喂”常常导致“垃圾进，垃圾出”（garbage in, garbage out）的困境。\n论文作者敏锐地观察到了这一点，并提出了一个全新的范式——“思考时搜索并提炼”（search-and-refine-during-think）。这个新范式的灵魂在于引入了一个明确的、独立的“提炼”（Refine）步骤。具体来说，模型在执行 \u0026lt;search\u0026gt; 操作并获取到 \u0026lt;documents\u0026gt; 后，不会立即生成 \u0026lt;answer\u0026gt;，而是会先进入一个 \u0026lt;refine\u0026gt;...\u0026lt;/refine\u0026gt; 的代码块。在这个阶段，模型的唯一任务就是阅读和理解取回的文档，然后从中提炼、过滤并整合出最关键、最相关的信息。这个过程完成后，模型再根据这些经过“预处理”的、高质量的信息精华来决定是继续进行下一轮搜索（如果发现还有知识盲区），还是生成最终答案。\n正如论文图 1 (a) 所展示的生动例子，当被问及“‘The Umbrellas’是哪位法国印象派画家的作品？”时，传统方法可能会因为检索到的文档中提及了画作的捐赠者“Hugh Lane”（一位爱尔兰艺术品经销商）而错误地回答“Hugh Lane”。而 AutoRefine 则通过 \u0026lt;refine\u0026gt; 步骤，明确地提炼出“文档结论是，皮埃尔-奥古斯特·雷诺阿创作了‘The Umbrellas’”这样的核心事实，从而避免了干扰，给出了正确答案“Pierre-Auguste Renoir”。\n为了让模型学会这种高级的“搜索-提炼”能力，作者们设计了一套巧妙的强化学习（Reinforcement Learning, RL） 框架。这里的第二个关键创新点在于其混合奖励机制（Hybrid Reward Mechanism）。传统方法通常只依赖于一个最终结果奖励（Outcome-Based Reward），即只有当最终答案完全正确时，模型才会得到正向激励。这种奖励机制非常稀疏，模型很难学会在复杂的中间步骤中做出正确的决策。\nAutoRefine 则引入了一个额外的检索特定奖励（Retrieval-Specific Reward, R_Ret）。这个奖励的独特之处在于，它不关心最终答案是否正确，只关心在 \u0026lt;refine\u0026gt; 步骤中提炼出的内容是否包含了正确答案的关键信息。如论文公式 (2) 所示：\nR_Ret = I(a ∩ o_refine = a) 其中 I() 是指示函数，a 是标准答案，o_refine 是所有 \u0026lt;refine\u0026gt; 块中内容的集合。\n这意味着，即使模型最终因为某些原因答错了，但只要它在中间的提炼步骤中成功地从文档里找到了正确的信息片段，它依然能获得一个正向的“部分学分”。如公式 (3) 所示，总奖励 R_overall 的设计非常精妙：如果最终答案正确 (R_ans \u0026gt; 0)，则获得满分奖励；如果最终答案错误但提炼步骤正确 (R_ans = 0 and R_Ret \u0026gt; 0)，则获得一个较小的固定奖励（0.1）；否则奖励为 0。这种“过程与结果并重”的奖励设计，极大地缓解了学习信号稀疏的问题，有效地引导模型学习如何在复杂的检索信息中“去粗取精”。\n在训练算法上，论文采用了群体相对策略优化（Group Relative Policy Optimization, GRPO）。与需要大量人工标注的传统 RLHF（基于人类反馈的强化学习）不同，GRPO 通过在每次迭代中生成一组（Group）候选答案，并根据奖励函数来评估这组答案的相对好坏，从而计算策略梯度。这是一种更高效的、无需人工的 RL 训练方法，非常适合 AutoRefine 这种需要探索复杂行为空间的任务。\n在实验验证部分，论文的设置非常全面。它在包括 Natural Questions、TriviaQA、HotpotQA 等在内的七个单跳及多跳问答基准上进行了测试。实验结果令人信服，如表 1 所示，AutoRefine 在所有七个数据集上的平均准确率达到了0.405，显著优于之前的 SOTA 模型如 Search-R 1（0.312），平均准确率提升了 6.9%。尤其是在需要多次推理的多跳问答（Multi-Hop QA） 任务上，AutoRefine 的优势更为明显，例如在 2 Wiki 数据集上，其准确率（0.393）相比 Search-R 1（0.274）提升了超过 10 个百分点，相对提升达到43%。\n更重要的是，论文通过一系列的分析实验（Analytical Results）深入剖析了 AutoRefine 成功的原因。图 4 展示了其搜索行为，AutoRefine 能够根据问题难度自适应地调整搜索次数——在复杂的多跳问题上进行更多次搜索。图 5 则量化了“提炼”步骤的有效性，证明了 \u0026lt;refine\u0026gt; 块能将上下文长度压缩约 4 倍，同时保留了回答问题所需的关键信息。最后，关键的消融实验（Ablation Studies）（表 2）无可辩驳地证明了“提煉”步骤和“检索特定奖励”这两个核心组件都是不可或缺的。去掉任何一个，模型性能都会出现显著下滑。\n总而言之，AutoRefine 不仅仅是一次模型性能的提升，更是一次关于 RAG 范式的深刻反思和创新。它提出的“search-and-refine-during-think”框架，以及与之配套的混合奖励机制，为构建更可靠、更智能、也更具可解释性（\u0026lt;refine\u0026gt; 的内容本身就是一种推理轨迹）的知识密集型 AI 应用，开辟了一条清晰且前景广阔的道路。\n接下来，我将按照您提出的六个方面，对论文进行更详细的结构化解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升大型语言模型在处理需要外部知识的复杂问答任务时的准确性和鲁棒性。 要解决的实际问题： 信息噪声问题：当前的 RAG 系统在检索外部文档时，返回的内容往往是冗长、嘈杂、甚至包含错误或误导性信息的。模型直接基于这些原始文档进行推理，很容易被噪声干扰，导致生成错误的答案。 推理过程黑盒问题：传统 RAG 模型从检索到生成答案的过程不够透明。我们很难知道模型是根据文档中的哪部分信息得出结论的，这使得模型的行为难以解释和调试。 学习信号稀疏问题：在训练 RAG 模型（尤其是使用强化学习时），通常只有在最终答案完全正确时才会给予奖励。对于需要多步推理的复杂任务，这种“非黑即白”的奖励机制使得模型很难学会中间步骤的复杂操作。 行业意义： 提升 AI 系统可靠性：在金融、医疗、法律、科研等对信息准确性要求极高的领域，一个能够有效过滤噪声、精准利用知识的 AI 系统是至关重要的。AutoRefine 为构建这样的高可靠性系统提供了关键技术。 推动 Agent 智能体发展：未来的 AI 智能体需要具备自主规划、工具使用和信息处理的能力。AutoRefine 所展示的“识别知识缺口 -\u0026gt; 搜索 -\u0026gt; 提炼 -\u0026gt; 决策”的闭环，是构建更高级、更自主的 AI Agent 的重要一步。 增强模型可解释性：通过 \u0026lt;refine\u0026gt; 步骤，模型被迫显式地写出它认为重要的信息摘要。这为我们提供了一个观察模型“思考过程”的窗口，极大地增强了系统的可解释性，有助于建立人机之间的信任。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于AutoRefine 框架，它包含两个主要部分：新的推理范式和新的奖励机制。\n新的思路和方法： “Search-and-Refine-During-Think”范式：这是对传统“Search-During-Think”范式的重大改进。 \u0026gt; 论文在§2.1 中明确提出了这个范式，使用一个‘ \u0026lt;search\u0026gt;...\u0026lt;/search\u0026gt;[documents]\u0026lt;refine\u0026gt;...\u0026lt;/refine\u0026gt; ’模板。 这个模板强制模型在每次搜索后插入一个显式的知识提炼环节。 混合奖励机制（Retrieval-Aware Signals）：它结合了两种奖励信号。 Outcome-Based Reward (R_ans): 评估最终答案的正确性，与传统方法类似。 Retrieval-Specific Reward (R_Ret): 评估中间 \u0026lt;refine\u0026gt; 步骤是否成功提取了答案的关键信息。这是该论文奖励设计的核心创新。 特点和优势： 抗噪声能力强：与之前直接处理原始文档的方法不同，AutoRefine 通过 refine 步骤主动过滤掉了无关信息，使得后续的决策和生成过程基于的是高质量、高信息密度的内容。 更高效的强化学习：混合奖励机制提供了更密集的学习信号。模型在探索过程中，即便最终没能答对，只要中间的“提炼”步骤做对了，也能得到鼓励。这就像一个好的老师，不仅看期末考试成绩，也为学生的每一次课堂正确发言而鼓掌，从而极大地加速了学习过程。 推理过程更具逻辑性和适应性：模型可以基于提炼后的信息，来决定是已经掌握足够信息可以回答问题，还是需要带着新的疑问进行下一轮搜索。图 4 (a) 的数据表明，模型学会了为复杂问题进行更多轮次的搜索，展现了这种适应性。 可解释性强：\u0026lt;refine\u0026gt; 块的内容成为了模型推理过程的“思维链”，让使用者可以清晰地看到模型是如何一步步从原始文档中提炼出关键证据，并最终形成答案的。 图 1：论文中 AutoRefine 与先前方法的对比，清晰地展示了“Refine”步骤的作用和两种奖励机制的区别。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列设计严谨的实验来验证其方法的有效性。\n实验设计： 数据集：使用了 7 个公认的 QA 基准数据集，涵盖了单跳（Single-Hop）（如 Natural Questions, TriviaQA）和多跳（Multi-Hop）（如 HotpotQA, 2 WikiMultihopQA）两类任务。这确保了评估的全面性。 基线模型：对比了三类强大的基线方法：(1) 无检索方法 (w/o Retrieval)； (2) 单跳检索方法 (w/ Single-Hop Retrieval)；(3) 多跳检索方法 (w/ Multi-Hop Retrieval)，包括了当时最先进的 Search-R1 和 ReSearch 等模型。 评估指标：主要使用精确匹配（Exact Match, EM） 准确率作为评估指标。 核心实验： 总体性能比较：在所有 7 个数据集上比较 AutoRefine 与所有基线模型的准确率。 分析实验：深入分析模型的搜索频率、搜索质量和提炼效果。 消融实验：通过移除 AutoRefine 的关键组件（如 refine 步骤和 R_Ret 奖励）来检验这些组件的必要性。 实验数据和结果： 总体性能优越： \u0026gt; 如表 1 所示，AutoRefine-Base 在 7 个数据集上的平均准确率达到 0.405，而表现最好的基线模型 Search-R 1-Base 为 0.312。 在多跳数据集 Musique 上，AutoRefine 的准确率（0.157）是 Search-R 1（0.066）的两倍多。这证明了其在处理复杂推理链上的巨大优势。 提炼步骤高效： \u0026gt; 图 5 (a) 和 5 (b) 的数据显示，\u0026lt;refine\u0026gt; 步骤能够在保留关键信息（与 \u0026lt;search\u0026gt; 动作成功率几乎持平）的同时，将处理的上下文长度从 600+ tokens 减少到 100-200 tokens，极大地降低了后续推理的认知负担。 关键组件的必要性： \u0026gt; 表 2 的消融研究结果是论文中最有力的证据之一。完整版 AutoRefine-Base 的平均准确率为0.405。当去掉 Retrieval Reward 后，性能下降到0.376。如果同时去掉 Retrieval Reward 和 Refinement 步骤，性能则骤降至0.312，与基线模型无异。这清晰地证明了两个核心创新点都对最终性能有巨大贡献。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 该研究方向充满机遇，但也面临挑战。\n值得探索的问题： 更高级的提炼（Refinement）能力：目前的 refine 主要是信息提取和摘要。未来可以探索让 refine 步骤具备更高级的能力，例如： 事实校验与矛盾检测：当检索到的多份文档信息冲突时，模型能否在 refine 阶段识别出矛盾点？ 信息综合与观点生成：不仅仅是提取，而是将多个来源的信息融合成一个新的、更全面的观点。 不确定性表达：如果检索到的信息不足以回答问题，模型能否在 refine 中明确表达其不确定性？ 动态与实时知识源的集成：论文使用的是静态的维基百科快照。未来的研究需要将 AutoRefine 这样的框架应用于动态知识源，如实时搜索引擎。这会带来新的挑战，如处理网页广告、内容格式多变、信息时效性等问题。 与更强大的基础模型结合：论文使用的是 3 B 参数量的模型。将 AutoRefine 框架应用于 70 B、100 B+甚至 MoE 模型上，可能会观察到新的涌现能力，其 refine 的质量和效率可能会有质的飞跃。 更灵活的评估体系：引入LLM-as-a-Judge（让另一个强大的 LLM 作为裁判）等更先进的评估方法，来评价答案的语义正确性而非仅仅是字符串的精确匹配，可以更公允地衡量模型的真实能力。 技术和投资机会： 下一代搜索引擎：结合了 AutoRefine 思想的搜索引擎，不再是返回一堆链接，而是直接给出一个经过提炼、综合、带有引用来源的精准答案。这正是 Perplexity AI 等公司正在探索的方向。 企业级知识管理解决方案：企业内部有海量的非结构化文档。基于此技术可以开发出能精准回答员工问题的智能知识库，大幅提升企业运营效率。 自动化科研助手：帮助科研人员快速阅读大量文献，提炼核心观点、实验数据和方法，极大地加速科学研究的进程。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以进一步完善的地方。\n模型规模的局限性（Model Scale）： \u0026gt; 论文坦诚地在第 4 节“Limitations”中提到：“all experiments in this paper use 3 B-parameter language models.” 实验完全基于 3 B 模型，其结论能否无缝推广到更大、能力更强的模型（如 GPT-4、Llama-3 等）上尚不明确。有可能对于非常强大的基础模型，它们本身就有一定的抗噪声和信息提炼能力，AutoRefine 框架带来的增益可能会减小。 评估指标的单一性（Evaluation Metrics）： \u0026gt; 论文也承认：“This work evaluates model performance solely on exact match accuracy, which may overlook semantically correct responses with minor textual variations.” 精确匹配（EM）过于严苛，会扼杀掉许多语义上正确但表述不同的答案。虽然这是该领域常用的指标，但它并不能完全反映模型的真实推理水平。 静态知识库的限制（Static Retrieval Corpus）： 如前所述，使用固定的维基百科快照，限制了其在真实世界动态场景中的应用价值。 效率与成本的权衡分析不足：AutoRefine 的迭代式“搜索-提炼”循环，相比于简单的 RAG 流程，无疑会增加推理的延迟（latency）和计算成本。论文没有详细分析这种准确率提升在多大程度上是以牺牲效率为代价的，以及在不同应用场景下如何权衡这两者。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的“拿来即用”的启发。\n重点学习的核心思想： 在流程中增加“净化”环节：最大的启发是，不要想当然地认为模型能处理好原始、嘈杂的输入。在任何一个复杂的 AI 工作流（不仅仅是 RAG）中，有意识地设计一个中间处理/净化/提炼步骤，都可能极大地提升系统的稳定性和最终效果。这是一个普适的系统设计哲学。 “过程奖励”的设计理念：在设计复杂的 Agent 或工作流时，不要只奖励最终结果。尝试将任务拆解，为关键的中间步骤设计独立的、易于评估的奖励，可以极大地改善模型的学习效率。AutoRefine 的混合奖励机制是这一理念的绝佳范例。 赋予模型“反思”的能力：AutoRefine 让模型在回答前先进行提炼，这本质上是一种“反思”。让模型有机会在决策前先整理和评估手头的信息，是通往更鲁棒 AI 的必经之路。 需要补充的背景知识： RAG（Retrieval-Augmented Generation）：需要深入理解 RAG 的基本工作原理，包括其中的检索器（Retriever）和生成器（Generator）是如何协同工作的。 强化学习（Reinforcement Learning）：至少需要了解 RL 的基本概念，如策略（Policy）、奖励（Reward）、状态（State）、动作（Action）。对策略梯度（Policy Gradient） 方法，特别是PPO (Proximal Policy Optimization) 有所了解，会更有助于理解论文中使用的GRPO。 Agentic AI / AI Agents：了解 AI Agent 的概念，以及一些经典的 Agent 框架，如ReAct (Reason+Act)，可以帮助您将 AutoRefine 放在一个更宏大的背景中去理解。 希望这份详尽的解读能够帮助您全面、深入地理解这篇优秀的论文。\n","date":"2025-07-20T00:00:00Z","permalink":"/search-and-refine-during-thinkautonomous-retrieval-augmented-reasoning-of-llms/","title":"Search and Refine During Think：Autonomous Retrieval - Augmented Reasoning of LLMs"},{"content":" FLOPs, floating point operations, 表示浮点数运算次数，衡量了计算量的大小。 如何计算矩阵乘法的 FLOPs 呢？ 对于 \\(A\\in R^{1\\times n},B\\in R^{n\\times1}\\) ,计算 \\(AB\\) 需要进行 \\(n\\) 次乘法运算和 \\(n\\) 次加法运算，共计 \\(2n\\) 次浮点数运算，需要 \\(2n\\) 的 FLOPs。对于 \\(A\\in R^{m\\times n},B\\in R^{n\\times p}\\) ,计算 \\(AB\\) 需要的浮点数运算次数为 \\(2mnp\\) 。\n在一次训练迭代中，假设输入数据的形状为 \\([b,s]\\) 。我们先分析 self-attention 块的计算，计 算公式如下：\n\\[Q=xW_Q,K=xW_K,V=xW_V\\] \\[x_{out}=softmax(\\frac{QK^T}{\\sqrt{h}})\\cdot V\\cdot W_o+x\\]\n计算 \\(Q,K,V:\\) 矩阵乘法的输入和输出形状为 \\([b,s,h]\\times[h,h]\\to[b,s,h]\\) 。计算量为 \\(3*2bsh^2=6bsh^2\\) 。 \\(2.QK^T\\) 矩阵乘法的输入和输出形状为 \\([b,head\\_num,s,per\\_head\\_hidden\\_size]\\) \\(\\times[b,head\\_num,per\\_head\\_hidden\\_size,s]\\rightarrow[b,head\\_num,s,s]\\) 。计算量为 \\(2bs^2h\\) 。 3. 计算在 \\(V\\) 上的加权 \\(score\\cdot V\\), 矩阵乘法的输入和输出形状为 \\([b,head\\_num,s,s]\\times[b,head\\_num,s,per\\_head\\_hidden\\_size]\\) 。计算量为 \\(2bs^2h\\) 。 4. Attention 后的线性映射，矩阵乘法的输入和输出形状为 \\([b,s,h]\\times[h,h]\\to[b,s,h]\\) 。计 算量为 \\(2bsh^2\\) 。 接下来分析 MLP 块的计算，计算公式如下：\n\\[x=f_{gelu}(x_{out}W_1)W_2+x_{out}\\]\n第一个线性层，矩阵乘法的输入和输出形状为 \\([b,s,h]\\times[h,4h]\\to[b,s,4h]\\)。计算量 为 \\(8bsh^2\\) 。 第二个线性层，矩阵乘法的输入和输出形状为 \\([b,s,4h]\\times[4h,h]\\to[b,s,h]\\)。计算量 为 \\(8bsh^2\\) 。 将上述计算量相加，得到每个 transformer 层的计算量大约为 \\(24bsh^2+4bs^2h\\) 。 此外，另一个计算量的大头是 logits 的计算，将隐藏向量映射为词表大小。矩阵乘法的输入和 输出形状为 \\([b,s,h]\\times[h,V]\\to[b,s,V]\\) ,计算量为 \\(2bshV\\) 。 因此，对于一个 \\(l\\) 层的 transformer 模型，输入数据形状为 \\([b,s]\\) 的情况下，一次训练迭代的 计算量为 \\(l*(24bsh^2+4bs^2h)+2bshV\\) 。 计算量与参数量的关联 当隐藏维度 \\(h\\) 比较大，且远大于序列长度 \\(s\\) 时，我们可以忽略一次项，计算量可以近似为 \\(24bsh^2*l\\) 。前面提到当模型参数量为 \\(12lh^2\\), 输入的 tokens 数为 \\(bs\\) ,存在等式 \\(\\frac{24bsh^2l}{12h^2\\times bs}=2\\)。我们可以近似认为：在一次前向传递中，对于每个 token，每个模型参数，需要进行 2 次浮点数运算，即一次乘法法运算和一次加法运算。 一次训练迭代包含了前向传递和后向传递，后向传递的计算量是前向传递的 2 倍。因此，前向传递+后向传递的系数=1+2=3 。一次训练迭代中，对于每个 token，每个模型参数，需要进行 2*3=6 次浮点数运算。 接下来，我们可以估计训练 GPT 3-175 B 所需要的计算量。对于 GPT 3，每个 token，每个参数进行了 6 次浮点数运算，再乘以参数量和总 tokens 数就得到了总的计算量。GPT 3 的模型参数量为 174600 \\(M\\) ,训练数据量为 \\(300B\\) tokens。\n\\[6\\times174600\\times10^6\\times300\\times10^9=3.1428\\times10^{23}flops\\]\n训练时间 \\[ 训练时间=\\frac{8 * tokens数 *模型参数量}{GPU数 * GPU峰值flops*GPU利用率} \\]\n也就是训练时的 flops / gpu 的 flops。8 是前向传播、后向传播、Activation checkpointing 。前向传播系数为 1，后向传播是前向传播计算量的 2 倍，再加上在反向传播时需要前向传播一次，因此总系数为 4，1 个参数需要 2 次浮点数运算，这就是 8 怎么来的。\n","date":"2025-07-19T00:00:00Z","permalink":"/flops%E5%88%86%E6%9E%90/","title":"flops分析"},{"content":"这篇论文系统地研究了大型语言模型（LLM）中一个长期存在但又至关重要的技术细节：层归一化（Layer Normalization, LN） 的放置位置。传统的 Post-LN 和 Pre-LN 策略各有优劣，前者梯度流不稳定，后者激活值容易爆炸，这给大模型的训练带来了诸多挑战。该论文通过详尽的理论分析和大规模实验，聚焦于一种被部分开源模型“悄然”采用但缺乏系统研究的“第三种”策略，并将其命名为 Peri-LN (Peripheral-LN)，即“外围层归一化”。\nPeri-LN 的核心思想是在 Transformer 的每个子层（如自注意力或前馈网络）的输入和输出都进行归一化。这篇论文的卓越贡献在于，它不仅仅是介绍这种方法，更是从根本上阐明了其工作机理。论文指出，Pre-LN 之所以不稳定，是因为其残差连接是一条“高速公路”，模块计算产生的巨大激活值（文中称为 “massive activations”）会毫无阻碍地在网络中累积，导致方差呈指数级增长。正如论文在图 1 中所展示的，在训练过程中，Pre-LN 模型的隐状态方差（hidden-state variance）急剧攀升，而 Post-LN 则保持平稳。\nFigure 1. Illustration of hidden-state variance… We observe the growth in hidden-state variance for both Pre-LN and Post-LN architectures.\nPeri-LN 通过在模块输出后、与残差相加前增加一个归一化层，巧妙地“净化”了这条高速公路。这使得模型的方差增长从 Pre-LN 的指数级（exponential growth）转变为更温和的线性增长（linear growth）。论文通过公式 Var(x_l+1) ≈ Var(x_l) + β_0 形象地描述了这一过程，其中 β_0 是一个接近常数的方差项。这意味着每一层只增加一个小的、可控的方差，从而保证了整个模型的稳定性。\n更进一步，论文在 Proposition 3.1 中从理论上分析了这种稳定性。它证明了在 Pre-LN 架构中，巨大的激活值会直接导致梯度爆炸；而在 Peri-LN 中，新增的输出归一化层对梯度产生了一个“阻尼因子”（damping factor），即使出现巨大的中间激活值，最终的梯度范数也能保持有界，从而实现了“自正则化”（self-regularizing）。\n在实验验证方面，论文做得非常扎实。他们训练了高达 32 亿参数的模型，在长达 300 亿个 token 的数据上进行了广泛测试。Table 1 的结果极具说服力，在所有模型尺寸（400 M, 1.5 B, 3.2 B）和多项基准测试（如 ARC, HellaSwag, PIQA）中，Peri-LN 的性能都稳定地、显著地优于 Pre-LN 和 Post-LN。例如，在 3.2 B 模型上，Peri-LN 的平均分达到了 58.56，而 Pre-LN 和 Post-LN 分别为 56.69 和 46.45。这不仅仅是性能的提升，更重要的是训练的稳定性。Figure 4 生动地展示了 Pre-LN 在训练早期频繁遭遇的损失尖峰（loss spikes）和发散（divergence）问题，而 Peri-LN 则表现出平滑稳定的训练曲线。\n论文还有一个非常深刻的洞见，即激活值的大小与训练中使用的数值精度（如 FP 16 和 BF 16）息息相关。Figure 11 的实验表明，Pre-LN 中的激活值在训练早期（仅 5 亿 token）就轻易超过了 FP 16 能表示的最大范围 (65,504)，这解释了为什么使用 FP 16 训练 Pre-LN 模型（如早期的 OPT 模型）会如此不稳定。相比之下，Peri-LN 的激活值则始终保持在安全的范围内。这个发现为业界在硬件选择和训练策略制定上提供了宝贵的实践指导。\n总而言之，这篇论文通过命名、理论化和系统验证“Peri-LN”这一策略，清晰地指出了大模型训练中一条更稳定、更高效的路径。它不仅解释了“为什么”一些成功的开源模型（如 Gemma, OLMo）会不约而同地采用类似设计，还为整个领域“应该”如何构建和训练未来的大模型提供了坚实的理论和实践依据。\n接下来，我将按照您提出的六个问题，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：本文的核心研究目标是系统性地分析并验证一种更优的 Transformer 层归一化（LN）放置策略，即 Peri-LN，以解决现有主流策略（Post-LN 和 Pre-LN）在训练大型语言模型时面临的稳定性与性能权衡困境。\n解决的实际问题：\n训练不稳定性：Pre-LN 架构虽然解决了 Post-LN 的梯度消失问题，但引入了新的“巨量激活值”（massive activations）问题，导致训练过程非常脆弱，容易出现梯度尖峰、损失爆炸甚至训练发散，尤其是在模型规模增大时。这使得训练大型、高性能的模型变得像一场“赌博”，需要反复调试和祈祷。 性能瓶颈：Post-LN 虽然稳定，但其梯度信号随深度衰减，导致模型收敛缓慢且最终性能不佳，难以训练非常深的网络。Pre-LN 虽然理论上性能更好，但其不稳定性常常导致实际性能受损或无法完成训练。 资源浪费：不稳定的训练过程意味着大量的计算资源被浪费在失败的训练任务上。一次数十亿甚至百亿参数模型的训练失败，可能造成数十万甚至数百万美元的损失。 行业意义：\n提高训练成功率和效率：提供一个像 Peri-LN 这样鲁棒的训练“配方”，可以显著降低大模型训练的失败风险，缩短研发周期，节约宝贵的计算资源。 推动模型规模化：一个更稳定的架构使得研究人员和工程师能更有信心地探索更大、更深的模型，而不必过分担心由架构本身带来的不确定性，从而推动 AI 能力的边界。 降低技术门槛：当训练大模型不再是一件高风险、高成本的“玄学”时，更多的中小型企业和研究机构也能参与其中，促进了技术的普及和生态的繁荣，这对于整个行业的健康发展至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心方法：Peri-LN (Peripheral Layer Normalization) 这篇论文系统化研究并命名了 Peri-LN。尽管作者坦言类似设计已在 Gemma、OLMo 等模型中被“悄然采用”，但本文的创新在于首次对其进行了系统的命名、深入的理论分析和全面的实验对比，从而将其从一种零散的实践提升为一种有理论支撑的、值得推广的范式。\n方法对比与优势分析 我们可以通过论文中的 Figure 2 来直观理解三种策略的差异： \u0026gt; \u0026gt; \u0026gt; \u0026gt; Figure 2. Placement of normalization in Transformer sub-layer.\nPost-LN (后置归一化): y = Norm(x + Module(x))。LN 放在残差连接之后。 特点: 归一化作用于主干道，强制将每层的输出拉回到标准分布，使得方差保持恒定。 缺点: 在反向传播时，梯度会穿过 LN 层，导致梯度信号随层数加深而衰减，引发梯度消失问题，不利于深层模型训练。 Pre-LN (前置归一化): y = x + Module(Norm(x))。LN 放在残差连接之前，仅作用于模块输入。 特点: 残差连接的主干道 x 上没有 LN，梯度可以直接流过，解决了梯度消失问题，这是其被广泛采用的原因。 缺点: 正是因为主干道是“无障碍高速公路”，Module(Norm(x)) 产生的巨大激活值会直接累加到 x 上，导致模型方差指数级增长，引发训练不稳定和数值溢出（特别是对于 FP 16）。 Peri-LN (外围归一化): y = x + Norm(Module(Norm(x)))。在模块的输入和输出都进行归一化。 特点与优势: 兼收并蓄: 它既像 Pre-LN 一样，对模块输入 x 进行归一化（Norm(x)），保证了模块计算的稳定性；又通过对模块输出 Module(...) 进行归一化，切断了巨大激活值直接污染主干道的路径。 控制方差增长: 它将 Pre-LN 的方差指数增长 Var(x_l+1) ≈ exp(l) * Var(x_0) 变为线性增长 Var(x_l+1) ≈ Var(x_l) + β_0。这从根本上遏制了“巨量激活值”的累积效应，是其稳定性的关键。 保持梯度流: 与 Pre-LN 类似，它的主干道 x 上也没有 LN 层，因此保留了通畅的梯度流，避免了 Post-LN 的梯度消失问题。 自正则化梯度: 如 Proposition 3.1 所示，输出归一化层 Norm(a) 在反向传播中引入了一个分母 ||a||，对梯度起到了动态的、自适应的正则化效果，防止了梯度爆炸。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列缜密且全面的实验来验证 Peri-LN 的有效性，覆盖了从初始化分析到最终模型评测的整个流程。\n实验设计: 模型与规模: 实验采用了 400 M、1.5 B 和 3.2 B 三种不同规模的 Transformer 模型，以验证方法在不同尺寸下的普适性。 对比对象: 严格对比了 Post-LN、Pre-LN 和 Peri-LN 三种架构。 训练设置: 每个模型都在 300 亿 token 的 DCLM 数据集上进行预训练，并采用 5 个不同的随机种子以保证结果的可靠性，减少偶然性带来的偏差。 评估维度: 训练动态: 监控训练过程中的损失曲线、梯度范数变化、激活值方差演变。 最终性能: 在预训练结束后，评估模型在 C 4 数据集上的困惑度（Loss），以及在 ARC、HellaSwag、PIQA、SIQA、Winogrande 等五个标准语言理解基准上的零样本（zero-shot）性能。 指令微调: 对模型进行指令微调（SFT），并评估其在下游任务上的性能。 消融研究: 进行了大量的消融实验，探究了学习率、权重衰减、初始化方法、序列长度、预热（warm-up）比例、QK-Norm 等超参数和模块对性能的影响。 关键数据与结果: 最终性能全面领先: Table 1 是最有力的证据。以 3.2 B 模型为例，Peri-LN 在五个基准任务上的平均分（Avg.）为 58.56，显著高于 Pre-LN 的 56.69 和 Post-LN 的 46.45。并且，Peri-LN 结果的标准差（standard deviation）远小于 Pre-LN，说明其训练结果更稳定、可复现性更强。 训练稳定性：Figure 4 的图像显示，在多个随机种子下，Pre-LN 架构频繁出现训练发散或损失尖峰，而 Peri-LN 始终保持稳定。这在实践中至关重要。 方差与激活值控制: Figure 6 直观展示了三种策略下隐状态（hidden state）的绝对幅值和方差随网络深度的增长情况。Post-LN 几乎不变，Pre-LN 呈指数爆炸，而 Peri-LN 则呈平缓的线性增长，完美印证了理论分析。 数值精度优势: Figure 11 的结果极具洞察力。它显示 Pre-LN 的激活值在训练不到 10 亿 token 时就超过了 FP 16 的表示上限，而 Peri-LN 则始终远低于该上限。 \u0026gt; This pattern, echoing Sun et al. (2024), highlights that choosing FP 16 or BF 16 is not just a hardware preference but is closely linked to how hidden state magnitudes evolve within the model. 这雄辩地证明了 Peri-LN 对硬件（特别是只支持 FP 16 的 GPU，如 V 100）更友好，大大降低了出现数值溢出导致训练失败的风险。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为我们开辟了新的视角，也引出了更多值得探索的方向：\n与其他优化技术的协同作用: 论文在附录中简要提及了 QK-Norm（对 Query 和 Key 进行归一化），并发现它与 Peri-LN 结合能带来轻微提升。未来可以更系统地研究 Peri-LN 与各种新型归一化技术（如 RMSNorm vs LayerNorm 的更深层差异）、注意力机制变体（如 FlashAttention）、专家混合（MoE）架构的相互作用。特别是对于 MoE，其固有的路由不稳定性或许能从 Peri-LN 的稳定性中受益。 论文也简单测试了 SGD 优化器，但 Adam 依然是主流。Peri-LN 的稳定性是否能让一些计算更简单的优化器（如 SGD with momentum）重新焕发生机？这是一个有趣的问题。 理论边界的拓展: 本文的理论分析（Proposition 3.1）主要集中在单层和梯度范数上。未来的工作可以尝试建立一个端到端（end-to-end）的信号传播理论，精确刻画 Peri-LN 在整个深度网络中的信息流动和梯度动态，类似于 Kedia et al. (2024) 为 Pre/Post-LN 所做的工作，从而更深刻地理解其行为。 自适应与动态架构: Peri-LN 在所有层都使用了相同的策略。是否可以设计一种动态的归一化策略？比如，模型可以根据当前层的状态（如激活值大小）自动选择是否应用输出归一化，或者动态调整归一化中的可学习参数 γ（gamma）。Figure 9 (b) 中 γ 随深度和训练变化的模式暗示了其背后可能有更深的规律。 催生的新技术和投资机会: 新一代训练框架: 可以预见，Peri-LN 或其变体将成为未来 LLM 训练框架（如 Megatron-LM、DeepSpeed）的默认或推荐配置。专注于开发和优化包含这些最佳实践的高效、稳定、易用的训练解决方案的公司将具有显著的竞争优势。 AI for AI（AI 辅助 AI 开发）: 研究 Peri-LN 中超参数（如 γ）的动态变化规律，可能催生出利用 AI 自动优化和设计神经网络架构的新技术。 硬件与软件协同设计: Peri-LN 对 FP 16 的友好性，可能会影响未来 AI 加速器的设计，使其在追求极致性能的同时，也为算法的稳定性提供更好的硬件支持。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这是一篇非常出色的论文，但从批判的角度看，仍有一些方面可以商榷或进一步完善：\n原创性（Novelty）的边界: 论文最核心的观点——Peri-LN 架构本身并非作者首创。作者在文中也坦诚地指出，Gemma 和 OLMo 等模型已经采用了类似的设计。因此，这篇论文的贡献更多在于“发现”而非“发明”，即首次对这一“民间智慧”或“工程技巧”进行了系统的理论化、命名和验证。这一点在评价其科学贡献时需要客观看待。\n实验范围的局限性:\n架构多样性: 实验主要集中在标准的 Decoder-only 语言模型上。Peri-LN 在其他重要架构，如 Encoder-Decoder（用于翻译）、Vision Transformers (ViT) 或多模态模型上的表现如何，仍是未知数，需要进一步的实验验证。 数据和任务: 实验主要基于通用文本数据。在特定领域（如代码、生物、金融）的数据上进行预训练，Peri-LN 是否依然能保持同样的优势，值得探究。 理论分析的深度: 虽然 Proposition 3.1 给出了精彩的局部洞见，但整个理论分析相比于近年来信号传播理论的最高水平，仍有提升空间。一个更完整的、能预测整个训练过程动态的理论模型将更有价值。\n对“巨量激活值”来源的探讨不足: 论文很好地解释了 Pre-LN 如何“传播”巨量激活值，以及 Peri-LN 如何“抑制”它。但对于这些巨量激活值最初在 Module 中是如何产生的，其深层原因（例如与特定 token、注意力模式或模型参数的关系）探讨相对较少。\n6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取实践经验和创新灵感的您来说，这篇论文提供了几个非常有价值的要点：\n拿来即用的核心思想: “外围”归一化原则: 在设计任何深度残差网络时，都可以借鉴 Peri-LN 的核心思想：对进入计算模块的输入进行归一化，对模块的输出在汇入主干道（残差连接）之前再次进行归一化。这是一个简单、普适且极其有效的稳定性技巧。 优先采用 Peri-LN: 如果您正在从头开始构建或训练一个新的 Transformer 模型，将 Peri-LN 作为默认的架构选择，相比 Pre-LN，几乎可以肯定会带来更稳定、更可预测的训练过程和更优的最终性能。 关注数值精度与激活值的匹配: 在训练时，特别是使用 FP 16 时，要密切监控模型内部的激活值大小。如果发现激活值接近或超过精度上限，应考虑切换到 BF 16 或采用像 Peri-LN 这样能从根本上抑制激活值增长的架构。 启发与思维方式: 重新审视“标准实践”: Pre-LN 在很长一段时间里被认为是训练 Transformer 的“标配”。这篇论文告诉我们，即便是被广泛接受的行业标准，也可能存在被忽视的缺陷，值得我们用批判和探索的眼光去重新审视。 从“民间智慧”中挖掘金矿: 很多成功的开源模型中蕴含着未被言明的“工程智慧”。系统性地去分析、理解和理论化这些实践，本身就是一条重要的创新路径。 动态与全局视角: 不能只在初始化阶段分析模型，而必须在整个训练生命周期中去观察和理解模型的动态行为（如方差、梯度、激活值的演变）。很多问题只有在动态中才会暴露。 需要补充的背景知识: Transformer 基础: 深入理解 Transformer 的每一个组件，特别是残差连接（Residual Connection）、层归一化（Layer Normalization）和自注意力机制（Self-Attention）。 深度学习中的梯度问题: 了解梯度消失（Vanishing Gradients）和梯度爆炸（Exploding Gradients）的成因和影响。 归一化技术: 除了 LayerNorm，了解一下 BatchNorm、RMSNorm 等其他归一化方法的原理和区别。 信号传播理论（Signal Propagation Theory）: 如果想更深入地理解，可以阅读一些关于深度网络中信号（激活值和梯度）如何传播和演变的基础性论文。 希望这份详尽的解读能帮助您全面、深入地掌握这篇论文的精髓。\n","date":"2025-07-19T00:00:00Z","permalink":"/peri-lnrevisiting-normalization-layer-in-the-transformer-architecture/","title":"Peri-LN：Revisiting Normalization Layer in the Transformer Architecture"},{"content":"好的，作为大模型领域的学术专家，我非常乐意为您深入解读这篇富有启发性的论文《ZEROSEARCH: Incentivize the Search Capability of LLMs without Searching》。\n这篇论文的核心思想极其巧妙，它直击了当前训练“搜索智能体（Search Agent）”LLM 时最头疼的两个问题：高昂的 API 调用成本和不可控的搜索结果质量。传统的做法是让 LLM 在训练时与真实的搜索引擎（如谷歌）进行交互，通过强化学习（RL）来学习何时搜索、搜索什么以及如何利用搜索结果。但这就像让一个新手司机直接在高峰期的纽约街头学开车，不仅成本高昂（每次“练习”都要花钱），而且路况复杂多变（搜索结果时好时坏），很容易让模型“学坏”或者干脆放弃学习。\n而 ZEROSEARCH 提出了一种革命性的替代方案：我们能否创造一个“虚拟驾校”，用一个专门的 LLM 来模拟真实的搜索引擎？\n这个想法的精妙之处在于，它将一个不可控、高成本的外部环境问题，转化为了一个可控、低成本的内部模型问题。论文的作者们发现，大型语言模型本身就蕴含了海量的世界知识，完全有能力根据一个查询（Query）生成以假乱真的“搜索结果”。\n为了实现这一点，他们首先通过监督微调（Supervised Fine-tuning, SFT），将一个 LLM 训练成一个“模拟器”。这个过程很有趣：他们先收集真实的搜索互动数据，然后让一个强大的 LLM（作为“法官”）来判断每一份搜索文档对于回答原始问题是“有用的（useful）”还是“有噪声的（noisy）”。然后，他们用这些标注好的数据来微调模拟器 LLM，让它能够根据指令（例如，在提示中加入“useful”或“noisy”的关键词）来生成特定质量的文档。正如论文在表格 2（Table 2）的模板中展示的，通过简单的关键词控制，就可以让模拟器生成不同质量的内容。\nTemplate for Search Simulation You are the Google search engine. Given a query, you need to generate five [useful / noisy] documents for the query. The user is trying to answer the question: [question] whose answer is [ground truth]. … Query: [query] [Useful / Noisy] Output:\n有了这个可控的模拟器，他们引入了课程学习（Curriculum Learning） 的策略。在训练初期，他们让模拟器多生成“有用”的文档，这就像在驾校里先在空旷的场地上练习，让智能体 LLM 先学会基本的搜索动作和格式。随着训练的进行，他们逐步提高生成“噪声”文档的概率（根据论文中的公式 1 进行调整），这就像逐步增加路上的障碍和干扰，逼迫智能体 LLM 学会分辨信息、深度思考，而不是盲目相信搜索结果。这种“从易到难”的训练方式，极大地稳定了强化学习过程，并激发了模型更强的推理能力。\n论文最令人震撼的成果体现在实验数据上。在核心的性能对比表（Table 3）中，我们可以看到，无论是在 Qwen-2.5-7 B 还是 LLaMA-3.2-3 B 等不同模型上，ZEROSEARCH 的表现都稳定地超过了基线方法，包括直接使用真实搜索引擎进行 RL 训练的 Search-R1。例如，在使用 Qwen-2.5-7 B-Base 模型时，ZEROSEARCH 的平均分达到了40.93，而 Search-R1 为39.51。\n更惊人的是，在表格 4（Table 4）中，论文探讨了不同模拟器的效果。结果显示，一个经过 SFT 微调的 14 B 模型（SFT-14 B）作为模拟器，其训练出的智能体 LLM 的最终表现，甚至超越了使用真实谷歌搜索（Google）作为训练环境的智能体（在多个问答数据集上的平均分为34.47 vs. 32.81）。这强有力地证明了，一个高质量的模拟环境不仅可以替代真实环境，甚至可能因为其“可控性”和“课程设计”的优势，带来更好的训练效果。\n最后，成本优势是该方法最实际的贡献。表格 8（Table 8）的成本分析显示，使用谷歌 API 进行约 64,000 次搜索请求的训练成本估算为$586.7，而使用一个 14 B 模型的 SFT 模拟器，其 GPU 成本仅为$70.8，成本降低了近 8 倍。这使得原本对算力要求极高、只有少数大公司能负担得起的搜索智能体研究，变得更加普及和可行。\n总而言之，ZEROSEARCH 通过“用 LLM 模拟搜索引擎”这一核心思想，并辅以“可控的 SFT 模拟器”和“课程学习”两大支柱，成功地构建了一个低成本、高效率、高稳定性的 LLM 搜索智能体训练框架，为整个领域的发展开辟了新的道路。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 论文的核心研究目标是，开发一个高效、低成本且可扩展的框架，用于训练大型语言模型（LLMs）掌握与外部搜索引擎交互以解决问题的能力。\n解决的实际问题: 该研究旨在解决在使用强化学习（RL）训练 LLM 搜索智能体时面临的两个核心瓶颈：\n高昂的 API 成本（Prohibitively High API Costs）: 训练过程需要数十万次的与真实搜索引擎（如 Google Search API）的交互，每次交互都需要付费，导致总成本极其高昂，限制了研究和应用的可扩展性。 不可控的文档质量（Uncontrolled Document Quality）: 真实搜索引擎返回的结果质量参差不齐，充满了噪声、广告和不相关信息。这种不可预测性会给 RL 训练带来巨大的不稳定性，可能误导模型的学习方向，使其难以建立有效的搜索和推理策略。 行业意义: 解决这个问题对于大模型和 AI Agent 领域的发展具有重大意义：\n推动 Agent 研究的“民主化”: 它极大地降低了训练复杂 Agent 的门槛。之前，只有拥有雄厚资本和算力资源的大公司才能进行大规模的真实环境交互式训练。ZEROSEARCH 展示了一条更经济的路径，使得更多的中小企业、研究机构和个人开发者也能参与到高级 Agent 的研发中来。 加速 Agent 能力的迭代: 低成本和高效率意味着可以进行更多、更快的实验。研究人员可以快速验证新的想法、调整模型架构、优化奖励函数，从而加速整个领域的技术迭代速度。 开辟“世界模型”的新思路: 该研究是“用模型模拟世界”这一宏大构想（常被称为世界模型, World Models）的一次成功实践。它证明了我们可以为 Agent 创造出可控、安全的“模拟训练场”。这一思路可以从模拟搜索引擎，扩展到模拟代码解释器、API 调用、甚至复杂的物理或社会环境，为构建更通用、更强大的 AI Agent 奠定了基础。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心创新在于其训练范式，而非全新的模型结构。其新颖之处体现在以下几个思路和方法的组合：\n核心思路：用 LLM 模拟搜索引擎（LLM as a Simulator）: 特点: 这是与以往方法最根本的区别。之前的方法如 DeepResearcher 和 WebThinker 都强调与真实搜索引擎的“实时交互”。而 ZEROSEARCH 则反其道而行，主张在训练中完全脱离真实搜索引擎，转而使用一个经过特殊微调的 LLM 来扮演搜索引擎的角色。 优势: 成本归零: 消除了所有外部 API 调用费用。 控制在我: 可以精确控制生成文档的质量、风格和内容，为后续的课程学习提供了可能性。 速度和并行性: 模拟器部署在本地 GPU 上，通信延迟极低，并且可以轻松地进行大规模并行化，以支持多个 RL 训练任务，这是外部 API 难以做到的。 方法一：可控的搜索模拟器微调（Search Simulation Tuning, SFT）: 特点: 为了让模拟器更逼真、更可控，论文设计了一个精巧的 SFT 流程。它不是简单地让 LLM 去模仿搜索结果，而是通过“判断-学习”的模式，让模型学会生成两种特定类型的内容： “有用”文档: 包含能够直接或间接回答问题的关键信息。 “噪声”文档: 与问题相关，但没有提供实质性答案，甚至可能包含误导信息。 优势: 这种显式的质量区分和控制能力，是简单的模仿式微调所不具备的。它使得训练环境的“难度”变得可编程。 方法二：课程学习的 rollout 策略（Curriculum-based Rollout）: 特点: 将 RL 训练的难度从易到难动态调整。论文中给出的概率公式 p_i = p_s + (b^(i/m) - 1)/(b-1) * (p_e - p_s) 精确地描述了在训练的第 i 步生成“噪声”文档的概率。训练初期，p_i 很低（接近 p_s），智能体 LLM 主要看到高质量的“有用”文档，更容易学会基本的交互流程。随着训练步数 i 增加，p_i 指数级增长至 p_e，智能体被迫面对更复杂的、充满噪声的环境，从而锻炼出更强的分辨和推理能力。 优势: 相比于在整个训练过程中使用固定难度（例如，固定的噪声比例）或完全随机的真实环境，这种课程学习策略能显著提升训练的稳定性和最终性能。论文在表格 6（Table 6）中的消融实验也证明了这一点，课程学习策略（Curriculum）的效果优于随机策略（Random）。 方法三：文档 Token 的损失屏蔽（Loss Masking）: 特点: 在计算 RL 损失函数时，只计算由策略模型（即智能体 LLM）自己生成的 Token（如 \u0026lt;think\u0026gt; 和 \u0026lt;search\u0026gt; 标签内的内容），而忽略掉由模拟器 LLM 生成的文档 Token。 优势: 这样做是符合逻辑且至关重要的。因为策略模型无法控制模拟器的输出，将这部分“外来”的 Token 纳入损失计算会引入巨大的噪声和不稳定性，干扰策略模型的学习。这一细节处理保证了训练过程的稳定性。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨且全面的实验来验证其方法的有效性。\n实验设计:\n数据集: 涵盖了不同类型的问答任务，以测试方法的泛化性。 单跳问答: NQ, TriviaQA, PopQA。 多跳问答: HotpotQA, 2 WikiMultiHopQA, Musique, Bamboogle。 基线模型: 实验对比了多种有代表性的方法。 无搜索方法: Direct Answer (直接回答), CoT (思维链)。 标准 RAG: 一次性检索后生成。 高级搜索 Agent: RA-Agent, Search-01。 核心对比对象: Search-R1，这是一个同样使用 RL 进行训练，但依赖真实搜索引擎的 SOTA 方法。将 ZEROSEARCH 与 Search-R1 对比，可以直接衡量“模拟搜索”与“真实搜索”在训练效果上的差异。 模型骨干: 实验使用了来自不同家族、不同大小的模型，包括 Qwen-2.5-7 B/3 B 和 LLaMA-3.2-3 B 的 Base 和 Instruct 版本，以验证方法的通用性。 评估方式: 在评估阶段，为了公平起见，所有方法（包括 ZEROSEARCH 训练出的模型）都统一使用真实的谷歌搜索引擎（通过 SerpAPI）来获取信息。这确保了比较的是模型本身利用搜索工具的能力，而不是训练环境的差异。评估指标为精确匹配（Exact Match, EM）。 关键实验数据与结果:\n总体性能优越: ZEROSEARCH 在绝大多数测试中都取得了最佳性能。 数据引用: 从 Table 3 中可以看到，以 LLaMA-3.2-3 B-Base 为骨干模型时，ZEROSEARCH 在 7 个数据集上的平均分达到 36.07，而 Search-R1 为 34.21，标准 RAG 为 25.11。这表明模拟训练环境不仅可行，效果还更好。 模拟器可超越真实引擎: 这是论文最惊人的发现之一。 数据引用: Table 4 专门比较了使用不同模拟器（从 3 B 到 14 B 的 Prompt-based 和 SFT-based 模型）和真实谷歌搜索作为训练环境时的效果。结果显示，当使用 Qwen-2.5-3 B-Base 作为策略模型时： | 训练环境 (Search Engine) | 平均分 (Avg.) | | :— | :— | | Prompt-3 B (模拟器) | 29.44 | | SFT-3 B (模拟器) | 30.47 | | SFT-14 B (模拟器) | 34.47 | | Google (真实引擎) | 32.81 | 这个结果清晰地表明，一个精心微调的大模型模拟器（SFT-14 B）能够提供比真实谷歌搜索更有效的训练环境。 成本效益显著: 数据引用: Table 8 对比了约 12 小时训练、产生约 64,000 次搜索请求的成本。 | 训练方式 | API 成本 | GPU 成本 | 总成本 | | :— | :— | :— | :— | | Google (真实引擎) | $586.7 | $0.0 | $586.7 | | SFT-14 B (模拟器) | $0.0 | $70.8 | $70.8 | 成本降低了87%，展示了巨大的经济价值。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这个方向充满了机遇，ZEROSEARCH 打开了一扇通往“Agent-as-a-Service”未来的大门。\n值得探索的问题和挑战: 模拟更复杂的工具和环境: 如何从模拟搜索引擎扩展到模拟代码解释器（Code Interpreter）、数据库查询（SQL）、API 调用，甚至是操作系统或浏览器？每种工具都有其独特的交互逻辑和错误模式，为它们构建高保真的模拟器是一个巨大的挑战。 自进化和自适应的模拟器: 当前的模拟器是静态的（微调后固定不变）。未来的模拟器能否根据智能体 LLM 的行为动态调整难度，甚至从智能体的失败中学习，自我进化，创造出更能激发 Agent 潜能的训练环境？这与“生成式对抗网络（GAN）”的思想有异曲同工之妙。 多智能体模拟（Multi-Agent Simulation）: 如何构建一个包含多个智能体的模拟环境？例如，模拟一个软件开发团队，其中有程序员 Agent、测试工程师 Agent 和项目经理 Agent，它们在一个共享的模拟环境中协作和竞争。 处理模拟与现实的“分布鸿沟”（Sim-to-Real Gap）: 尽管实验表明模拟器效果很好，但模拟环境与真实世界之间始终存在差异。如何进一步缩小这一鸿沟，确保在模拟环境中训练出的能力可以无缝迁移到真实世界，是一个长期存在且至关重要的问题。 可能催生的新技术和投资机会: Agent 训练平台即服务（Agent Training Platform as a Service）: 可能会出现专门的公司，提供用于训练 Agent 的、高度优化和可定制的模拟环境套件。企业用户无需自己构建模拟器，只需接入平台，选择所需的模拟环境（如模拟电商网站、模拟金融交易系统），即可训练自己的定制 Agent。 垂直领域的“数字孪生”模拟器: 在金融、医疗、法律、自动驾驶等高价值领域，构建超高保真的“数字孪生”模拟环境将成为一个巨大的商业机会。例如，一个模拟了整个金融市场的环境，可以让量化交易 Agent 在其中进行无风险的训练和测试。 通用世界模型的研发: 对模拟环境的研究最终会导向一个终极目标——构建通用的世界模型。能够投资和推动这项研究的公司，将在下一代人工智能竞争中占据核心优势。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度审视，仍然存在一些潜在的局限和值得商榷之处：\n模拟器的知识局限性与“分布外”问题: 模拟器 LLM 本身的知识受其预训练数据截止日期的限制。当智能体需要查询一个全新的、模拟器闻所未闻的事件或概念时（out-of-distribution），模拟器能否生成合理的结果？论文提到将问题和答案也输入模拟器以扩展其知识边界，但这可能不足以覆盖所有新知识。模拟器可能会“幻觉”出看似合理但完全错误的文档，这与真实搜索引擎返回“无结果”或低质量链接的行为模式不同。 真实世界交互的复杂性被简化: 真实的网页搜索体验远比返回结构化的“文档”要复杂。它包括广告、弹窗、动态加载内容、需要登录的网站、人机验证（CAPTCHA）等等。ZEROSEARCH 的模拟器目前似乎并未模拟这些复杂的交互挑战，这可能导致训练出的模型在面对真实世界的“脏数据”和复杂流程时表现下降。 “有用性”判断的主观性和潜在偏差: SFT 模拟器的训练数据质量，依赖于一个“法官”LLM 对文档“有用性”的判断。这个判断本身是主观的，且“法官”LLM 可能带有自身的偏见。一个有偏见的法官可能会训练出一个有偏见的模拟器，进而影响智能体 LLM 的学习，这种潜在的“偏见传递”链条需要被更深入地研究。 算力成本的相对性: 虽然与昂贵的 API 相比，GPU 成本显著降低，但部署和运行一个 14 B 甚至更大规模的 LLM 作为模拟器，本身仍然是一笔不小的开销。对于资源非常有限的个人研究者或初创公司来说，这依然是一个门槛。论文的成本分析可能过于乐观，没有完全考虑 GPU 的闲置成本和运维成本。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 这篇论文是创新思想的宝库，您可以从中借鉴以下几点核心启发：\n核心启发一：模拟范式（Simulation Paradigm） 学什么: 当与外部世界的交互是昂贵、缓慢、危险或不可控的时候，尝试用一个微调过的 LLM 来模拟它。 这是本文最核心、最普适的思想。 如何用: 如果您正在开发一个需要调用付费 API、操作数据库或与用户进行多轮对话的 Agent，您可以借鉴 ZEROSEARCH 的思路： 收集一批真实的交互数据。 定义交互结果的“好”与“坏”（例如，API 调用成功/失败，SQL 查询返回正确/错误结果，用户对话满意/不满意）。 微调一个 LLM 模拟器，让它能生成不同质量的交互结果。 在这个安全、廉价的模拟器中训练您的主 Agent。 核心启发二：课程学习的工程智慧 学什么: 不要让模型一开始就面对最困难的挑战。 采用从易到难的训练策略，是保证复杂系统（尤其是 RL 系统）训练成功的关键。 如何用: 在您的任何训练任务中，思考如何定义任务的“难度”。可以是数据噪声的比例、任务的复杂度、干扰信息的多少等。然后设计一个课程，让模型在训练初期处理简单的样本，逐步过渡到复杂的样本。 核心启发三：可控的数据生成（Controllable Data Generation） 学什么: 通过在 Prompt 中加入控制指令（如“useful”/“noisy”），可以精巧地控制 LLM 生成特定风格或质量的数据。 如何用: 这个技巧可以广泛用于数据增强。例如，您可以微调一个模型，让它能根据指令生成“带有攻击性的用户评论”和“礼貌的用户评论”，用于训练一个更鲁棒的客服机器人。或者生成“代码风格良好”和“代码风格糟糕”的示例，用于训练代码格式化工具。 需要补充的背景知识: 强化学习基础（Reinforcement Learning）: 至少要理解策略（Policy）、奖励（Reward）、智能体（Agent）、环境（Environment）等基本概念。对 PPO（Proximal Policy Optimization）和 REINFORCE 等经典算法有了解会更有帮助。 检索增强生成（RAG）: 理解 RAG 的基本工作原理，知道它是如何结合检索和生成来提升回答质量的。 LLM Agent 架构: 了解当前主流的 Agent 框架，如 ReAct（Reason+Act）等，理解它们是如何通过“思考-行动”循环来与工具交互的。这篇论文中的 \u0026lt;think\u0026gt;、\u0026lt;search\u0026gt; 标签就是 ReAct 思想的体现。 ","date":"2025-07-18T00:00:00Z","permalink":"/zerosearchincentivize-the-search-capability-of-llms-without-searching/","title":"ZEROSEARCH：Incentivize the Search Capability of LLMs without Searching"},{"content":"All-reduced=all-gather+reduce-scatter\nAll-Gather ：将分布式数据汇总到所有节点，适用于需要全局数据同步的场景。 Reduce-Scatter：将分布式数据进行规约并分散到所有节点，适用于需要局部结果分发的场景。 All-Reduce ： Reduce-Scatter 和 All-Gather 的组合。 All-gather 核心功能：将每个节点的部分数据汇总到所有节点，最终所有节点拥有完整数据副本。\n适用场景：模型并行中的参数同步、全局统计信息聚合。\nimage.png Reduce-Scatter 核心功能：先对多节点数据进行规约（如求和），再将结果分散到各节点，使每个节点仅保留部分规约结果。\n适用场景：ZeRO显存优化、梯度分片更新。\nimage.png 区别 All-Gather：只进行数据收集和分发，不进行任何计算或规约操作。每个节点拥有所有节点的数据副本。 Reduce-Scatter：先进行数据规约（reduce），然后再进行数据分散（scatter）。每个节点只拥有部分规约后的数据，而不是所有的数据 例子 通信量计算 假设模型参数大小为 \\(\\theta\\)，GPU 个数为 N，则每一个梯度块大小为 \\(\\frac{\\theta}{N}\\)\n对于单卡而言： - Reduce-Scatter 阶段通讯量：\\((N-1) \\frac{\\theta}{N}\\) - All-Reduce 阶段通讯量：\\((N-1) \\frac{\\theta}{N}\\)\n单卡通讯量为 \\(2(N-1) \\frac{\\theta}{N}\\)，所有卡的通讯量为 \\(2(N-1) \\theta\\)\n参考 # 分布式训练中All-Reduce、All-Gather、Reduce-Scatter原理介绍\n","date":"2025-07-17T00:00:00Z","permalink":"/ring-all-reduce/","title":"ring-all-reduce"},{"content":"batch_size 的复杂性来自 TP、DP、SP 三个维度的交织。本文先讲清楚「一个 batch 是怎么从 global 流到一张 GPU 的」,再讲两条 micro-batch 切分路径(静态/动态),最后展开动态路径背后的 use_dynamic_bsz、_balance_batch、all_reduce(MAX) 同步等关键机制。\n引用浅入理解verl中的batch_size的解释:\nvllm + fsdp 训推时, 如果每张卡都是一个 DP, 事情会简单很多。但 verl 中有两个功能不满足这一条件, 一是 rollout 时让 vllm 开启 TP, 二是在 fsdp 中使用 ulysses (SP)。verl 中数据分发使用的是 dispatch mode 这一机制, 比如 fsdp workers 主要使用 Dispatch.DP_COMPUTE_PROTO 这个 mode, 它是在 worker group 的层次上进行数据分发以及结果收集的。由于这个层次是没有 TP/SP 概念的, 所以它仅在 one GPU one DP 时才是正确的。那么为了正确支持 TP/SP, 就需要对数据做一些前后处理。\n这一点在 DataProto 也有提到, 具体的实现就是 all_gather_data_proto 函数。\nTP 需要在 TP rank 上 all-gather 来保证各个 tp rank 的输入相同, 然后再将输出 split 返回当前 rank 的部分, 保持和输入一致。 SP 与 TP 一样, 在 SP group 上进行 allgather 来保证各个 SP rank 的输入相同 (ulysses 的需要); 对输出进行 split 并返回当前 rank 对应的部分。 fsdp_worker 初始化时的 batch_size 处理 # normalize config if self._is_actor: self.config.actor.ppo_mini_batch_size *= self.config.rollout.n self.config.actor.ppo_mini_batch_size //= self.device_mesh.size() // self.ulysses_sequence_parallel_size assert self.config.actor.ppo_mini_batch_size \u0026gt; 0, ( f\u0026quot;ppo_mini_batch_size {self.config.actor.ppo_mini_batch_size} should be larger than 0 after \u0026quot; f\u0026quot;normalization\u0026quot; ) # micro bsz if self.config.actor.ppo_micro_batch_size is not None: self.config.actor.ppo_micro_batch_size //= ( self.device_mesh.size() // self.ulysses_sequence_parallel_size ) self.config.actor.ppo_micro_batch_size_per_gpu = self.config.actor.ppo_micro_batch_size if self.config.actor.ppo_micro_batch_size_per_gpu is not None: assert self.config.actor.ppo_mini_batch_size % self.config.actor.ppo_micro_batch_size_per_gpu == 0, ( f\u0026quot;normalized ppo_mini_batch_size {self.config.actor.ppo_mini_batch_size} should be divisible by \u0026quot; f\u0026quot;ppo_micro_batch_size_per_gpu {self.config.actor.ppo_micro_batch_size_per_gpu}\u0026quot; ) assert self.config.actor.ppo_mini_batch_size // self.config.actor.ppo_micro_batch_size_per_gpu \u0026gt; 0, ( f\u0026quot;normalized ppo_mini_batch_size {self.config.actor.ppo_mini_batch_size} should be larger than \u0026quot; f\u0026quot;ppo_micro_batch_size_per_gpu {self.config.actor.ppo_micro_batch_size_per_gpu}\u0026quot; ) # normalize rollout config if self._is_rollout and self.config.rollout.log_prob_micro_batch_size is not None: self.config.rollout.log_prob_micro_batch_size //= ( self.device_mesh.size() // self.ulysses_sequence_parallel_size ) self.config.rollout.log_prob_micro_batch_size_per_gpu = self.config.rollout.log_prob_micro_batch_size # normalize ref config if self._is_ref and self.config.ref.log_prob_micro_batch_size is not None: self.config.ref.log_prob_micro_batch_size //= self.device_mesh.size() // self.ulysses_sequence_parallel_size self.config.ref.log_prob_micro_batch_size_per_gpu = self.config.ref.log_prob_micro_batch_size 前面创建了 fsdp_devicmesh(dp)、ulysses_device_mesh(dp 和 sp) 以及在初始化 rollout 中创建了 rollout_device_mesh(dp 和 tp)。具体可以查看 device_mesh。\nRollout 注意 verl v0.5.0 的实现中, 所有的 prompts 直接由 driver 重复然后再 dispatch, 而不是先 dispatch 然后再交给 Worker 重复样本。这样做的目的是如果先分片的话, 有可能 batch_size 小于 world_size (对我来说基本不可能), 导致不能正确切分, 如果是先 repeat 的话就可以了。\n因此对于 Rollout: - 全局:train_batch_size * n 个 prompts, 输出 train_batch_size * n 个 prompts + responses。 - 单卡: 输入 train_batch_size * n / world_size 个 prompts, 进行前处理为 \\(\\frac{train\\_batch\\_size \\cdot n}{world\\_size} \\cdot tp\\_size\\) 个 prompts, 然后进行推理, 得到 \\(\\frac{train\\_batch\\_size \\cdot n}{world\\_size} \\cdot tp\\_size\\) 个 prompts+responses, 再进行后处理, 输出 train_batch_size * n / world_size 个 prompts+responses。\nActor 与 Actor 有关的参数为 ppo_mini_batch_size(决定一批 experience 的更新次数)、rollout.log_prob_micro_batch_size_per_gpu(计算 old_logp)、ref.log_prob_micro_batch_size_per_gpu(计算 ref_logp)、micro_batch_size_per_gpu(直接指定单卡上的 batch_size)。\n重点看 ppo_mini_batch_size 用于 update_policy 的处理, 其它的都大同小异:\n全局:train_batch_size * n 大小的 batch, 包含了部分计算好的 experience。 单卡: 输入为 train_batch_size * n / world_size, 经过前处理为 train_batch_size * rollout_n * sp_size / world_size。这和 fsdp_worker 初始化时的 ppo_mini_batch_size 一致, 即 self.config.actor.ppo_mini_batch_size *= self.config.rollout.n self.config.actor.ppo_mini_batch_size //= self.device_mesh.size() // self.ulysses_sequence_parallel_size 这样的话对于一批 experience 的更新次数, 就等于你传入的 train_batch_size // mini_batch_size。对于 micro_batch_size_per_gpu 参数来说, 可以直接指定单卡的 bsz。对于传入 micro_batch_size 再计算的方式, verl 已经废弃了。\nppo 流程 image.png 从图中可以得到的是 ppo_mini_batch_size 是全局的 prompt batch, 而 ppo_micro_batch_per_gpu 是每一个 GPU 上的 prompt+response batch, 所以由此可以得到梯度累计的 steps 为 ppo_mini_batch_size * n * sp_size // world_size // micro_batch_per_gpu。即 micro_batch_per_gpu 代表梯度累计的 bsz。\n两条 micro-batch 切分路径 上面四节都把 batch 视为按”条数”切分, 但实际 verl 训练侧有两条切分路径, 由 use_dynamic_bsz 二选一:\n路径 触发参数 切分依据 平衡目标 路径 A: 静态切分 use_dynamic_bsz=False (默认) micro_batch_size_per_gpu 条样本 不做平衡 路径 B: 动态切分 use_dynamic_bsz=True max_token_len_per_gpu 个 token KK 算法平衡 token 总数 三层 batch 概念 先看清”batch size”这个词在不同位置的含义, 数据从一步训练流到一张 GPU 经过三层:\nverl 三层 batch + 两层 KK 平衡 verl 在数据流上做了两层 KK 平衡: - 第 1 层 (默认开, trainer.balance_batch=True): single controller 跨 DP rank 全局重排, 在 _balance_batch 中做一次 KK。 - 第 2 层 (要开 use_dynamic_bsz): 每个 rank 内切 micro-batch 时再做一次 KK, 按 token 预算打包。\n路径 A: 静态切分 (默认) 关键参数: ppo_micro_batch_size_per_gpu / log_prob_micro_batch_size_per_gpu。逻辑非常简单, 按样本条数等分。\n# verl/workers/engine/utils.py if use_dynamic_bsz: ... else: total_data_size = len(data) micro_batch_size_per_gpu = data[\u0026quot;micro_batch_size_per_gpu\u0026quot;] assert total_data_size % (force_group_size * micro_batch_size_per_gpu) == 0 micro_batches = tu.chunk_tensordict( data, total_data_size // (micro_batch_size_per_gpu * force_group_size) ) 特性: - 条数固定: 每个 micro-batch 恰好 micro_batch_size_per_gpu 条样本, 无论它们多长。 - 显存不可控: 极端情况一个 micro-batch 全是长样本, token 总数远超预期, OOM。 - DP 间天然对齐: 每张卡的 micro-batch 数完全一致, FSDP/Megatron 的集合通信节奏天然同步。 - 失衡浪费算力: 短样本 padding 到 batch 里最长那条, 长样本拉低吞吐, GPU 利用率低。\n路径 B: 动态切分 (use_dynamic_bsz=True) 关键参数: ppo_max_token_len_per_gpu。不再按条数切, 而是给每张卡一个 token 预算, 由算法去打包。\n入口代码:\n# verl/workers/engine/utils.py — prepare_micro_batches() if use_dynamic_bsz: assert \u0026quot;max_token_len_per_gpu\u0026quot; in data.keys() max_token_len_per_gpu = data[\u0026quot;max_token_len_per_gpu\u0026quot;] max_token_len = max_token_len_per_gpu * sp_size # SP 倍率 micro_batches, batch_idx_list = rearrange_micro_batches( data, max_token_len=max_token_len, dp_group=dp_group, same_micro_num_in_dp=same_micro_num_in_dp, # 关键 ... ) 三步切分流程:\n步骤 1: 算每张卡至少要切几个 micro-batch。\ntotal_seqlen = attention_mask.sum() num_micro_batches = ceil(total_seqlen / max_token_len) 步骤 2 (dynamic 模式独有): DP 维度同步 micro-batch 数。\nif dist.is_initialized() and same_micro_num_in_dp and dp_group is not None: num_micro_batches = torch.tensor([num_micro_batches], device=...) dist.all_reduce(num_micro_batches, op=dist.ReduceOp.MAX, group=dp_group) num_micro_batches = num_micro_batches.cpu().item() 步骤 3: 用 Karmarkar-Karp 算法把样本按工作量平均分进 num_micro_batches 个桶。\n# 工作量估算: 模拟 attention FLOPs ≈ 12·h²·L + 2·h·L² workload = 24576 * seqlen + seqlen ** 2 partitions = karmarkar_karp(workload, k_partitions=num_micro_batches, equal_size=False) 为什么 all_reduce(MAX) 不可省: DP 各 rank 拿到的样本长度分布不同 → 各自算出的 num_micro_batches 不一样 → FSDP forward 循环里调用 all_gather 的次数不一样 → 集合通信永远配不上对, 直接 NCCL hang。verl 用 MAX 把所有 rank 拉齐到全局最大值, 短的 rank 通过把样本切成更稀疏的桶来对齐 (不是补空 padding)。\n_balance_batch: 进 dynamic_bsz 之前的全局 KK 重排 第 03 节的 dynamic_bsz 只管 rank 内的 micro-batch 切分。那 rank 之间的总 token 数谁负责? 答案是: 在 dynamic_bsz 进各 rank 之前, verl 在 single controller 上先做了一次全局 KK 重排, 叫 _balance_batch。由 trainer.balance_batch=True 默认开启。\n# verl/trainer/ppo/ray_trainer.py — _balance_batch() global_seqlen_lst = batch.batch[\u0026quot;attention_mask\u0026quot;].sum(-1) workload_lst = calculate_workload(global_seqlen_lst) dp_size = self._get_dp_size(self.actor_rollout_wg, \u0026quot;actor\u0026quot;) # equal_size=True: 每个 rank 拿到等条数样本 global_partition_lst = get_seqlen_balanced_partitions( workload_lst, k_partitions=dp_size, equal_size=True ) # 把 batch 按 KK 给的分组重排, 再 chunk 出去 global_idx = torch.tensor([j for partition in global_partition_lst for j in partition]) batch.reorder(global_idx) 这层和 dynamic_bsz 的关键区别:\nequal_size=True: 每个 rank 必须拿到等条数样本 (PPO 后续 mini-batch 数对齐需要), KK 在这个硬约束下做平衡。 只重排顺序, 不重切样本: 原样本不变, 只是 chunk 时被分到不同 rank。 对 workload 而非 token 数平衡: 用 12·h²·L + 2·h·L² 估算 attention FLOPs, 让长样本被特别”重视”。 每个 mini-batch 跑一次, rollout 完成后、进 forward 前。 演示: 16 条样本下发到 4 个 DP rank 同一组 16 条不同长度样本, 按原顺序 chunk vs 经过 _balance_batch 重排后再 chunk。\n_balance_batch 效果对比 可以看到 KK 重排后, 各 rank 的总 token 数从原来最大 21,500 / 最小 11,800 (极差 1.82×) 压到了 17,300 / 15,800 (极差 1.09×)。\n限制: equal_size=True 让 KK 不能完全自由, 比如”单条特别长的样本”总会落到某个 rank 上、把那个 rank 的 token 总数推高。这是为什么 log 里 balanced_max - balanced_min 不会真的变成 0, 只是从原始的 ~10% 极差压到 ~3-5%。\nlog 里 global_seqlen/min,max 是未重排时的统计; global_seqlen/balanced_min,balanced_max 是KK 重排后的统计。在自己的训练 metric 里直接对比这两组数字就能看 _balance_batch 在你的数据上有多大效果。\nuse_dynamic_bsz 数据流 参数定义与作用 参数 作用 ulysses_sequence_parallel_size Ulysses 序列并行大小, 将序列切分到多个 GPU 上并行计算 use_dynamic_bsz 动态批处理, 根据 token 总数动态划分 micro batch, 平衡各 batch 的计算量 use_remove_padding 移除 padding, 将多个序列的有效 token 拼接成一个连续序列, 配合 flash_attn_varlen 使用 ppo_mini_batch_size PPO 训练时每个 mini batch 的样本数 ppo_micro_batch_size_per_gpu 每个 GPU 上的 micro batch 大小 (梯度累积的基本单位) compute_log_prob 阶段 (推理 / 计算 log prob) 输入数据 (DataProto) │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 1: 划分 Micro Batches │ │ ├─ if use_dynamic_bsz: │ │ │ 使用 rearrange_micro_batches() 按 token 总数动态划分 │ │ │ max_token_len = meta_info[\u0026quot;max_token_len\u0026quot;] * sp_size│ │ │ 返回重排后的 micro_batches 和 indices │ │ └─ else: │ │ 按固定 micro_batch_size 均匀切分 │ └──────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 2: 对每个 Micro Batch 进行前向计算 │ │ └─ _forward_micro_batch() │ │ ├─ if use_remove_padding: │ │ │ ├─ unpad_input(): 移除 padding, 得到连续 token │ │ │ ├─ if use_ulysses_sp: │ │ │ │ ├─ ulysses_pad(): 填充到 sp_size 整数倍 │ │ │ │ └─ ulysses_pad_and_slice_inputs(): │ │ │ │ 切分序列到各 SP rank │ │ │ ├─ 模型前向 (flash_attn_varlen) │ │ │ ├─ if use_ulysses_sp: │ │ │ │ └─ gather_outpus_and_unpad(): 聚合并去除填充│ │ │ └─ pad_input(): 恢复原始 batch 形状 │ │ └─ else (不使用 remove_padding): │ │ └─ 标准前向计算 │ └──────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 3: 合并结果 │ │ ├─ concat 所有 micro batch 的 log_probs │ │ └─ if use_dynamic_bsz: │ │ 使用 get_reverse_idx() 恢复原始顺序 │ └──────────────────────────────────────────────────────────┘ update_policy 阶段 (PPO 训练) 输入数据 (DataProto, batch_size = N) │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 1: 划分 Mini Batches │ │ num_mini_batches = N / ppo_mini_batch_size │ │ dataloader = data.chunk(num_mini_batches) │ └──────────────────────────────────────────────────────────┘ │ ▼ for epoch in ppo_epochs: for mini_batch in dataloader: │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 2: 划分 Micro Batches (梯度累积) │ │ ├─ if use_dynamic_bsz: │ │ │ 使用 rearrange_micro_batches() 按 token 总数动态划分 │ │ │ max_token_len = ppo_max_token_len_per_gpu * sp_size │ │ └─ else: │ │ gradient_accumulation = ppo_mini_batch_size / │ │ ppo_micro_batch_size_per_gpu│ │ micro_batches = mini_batch.split( │ │ ppo_micro_batch_size_per_gpu) │ └──────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Step 3: 梯度累积 │ │ optimizer.zero_grad() │ │ for micro_batch in micro_batches: │ │ ├─ _forward_micro_batch() (同上, 含 SP 和 rmpad) │ │ ├─ 计算 policy_loss │ │ ├─ if use_dynamic_bsz: │ │ │ loss = policy_loss * (len(data) / │ │ │ ppo_mini_batch_size) │ │ └─ else: │ │ loss = policy_loss / gradient_accumulation │ │ loss.backward() │ │ optimizer.step() │ └──────────────────────────────────────────────────────────┘ 关键关系图 ┌─────────────────────────────────────┐ │ Total Batch (N samples) │ └─────────────────────────────────────┘ │ ┌─────────────────┴─────────────────┐ │ ppo_mini_batch_size 划分 │ │ (PPO 每次更新使用的样本数) │ └─────────────────┬─────────────────┘ │ ┌───────────────────────┼───────────────────────┐ │ │ │ Mini Batch 1 Mini Batch 2 Mini Batch K │ │ │ └───────────────────────┼───────────────────────┘ │ ┌─────────────────┴─────────────────┐ │ use_dynamic_bsz ? │ └─────────────────┬─────────────────┘ ┌──────────┴──────────┐ │ │ Yes (动态) No (固定) │ │ ┌────────────┴────────────┐ ┌────┴────┐ │ rearrange_micro_batches │ │ split │ │ 按 max_token_len 划分 │ │ 按固定 │ │ (平衡各 batch token 数) │ │ micro │ │ │ │ batch │ │ max_token_len = │ │ size │ │ ppo_max_token_len_per_gpu│ │ │ │ * ulysses_sp_size │ │ │ └────────────┬────────────┘ └────┬────┘ │ │ └──────────┬──────────┘ │ Micro Batches │ ┌─────────────────┴─────────────────┐ │ use_remove_padding ? │ └─────────────────┬─────────────────┘ ┌──────────┴──────────┐ │ │ Yes (移除) No (保留) │ │ ┌────────────┴────────────┐ │ │ unpad_input() │ │ │ 移除 padding, 拼接有效 │ │ │ token 成连续序列 │ │ └────────────┬────────────┘ │ │ │ ┌──────┴──────┐ │ │ ulysses_sp? │ │ └──────┬──────┘ │ ┌──────┴──────┐ │ │ │ │ Yes (SP) No (单卡) │ │ │ │ ┌────────┴────────┐ │ │ │ ulysses_pad + │ │ │ │ slice_inputs │ │ │ │ 切分序列到各 rank│ │ │ └────────┬────────┘ │ │ │ │ │ └──────┬──────┘ │ │ │ └──────────┬──────────┘ │ Model Forward │ ┌─────────────────┴─────────────────┐ │ 后处理: gather + unpad + revert │ └────────────────────────────────────┘ 静态 vs 动态 micro-batch 切分对比 假设这张 GPU 拿到 8 条样本, token 长度依次为 [800, 6500, 1200, 4000, 9000, 2500, 1500, 7500], 共 33,000 token。\n同一组 8 条样本的两种 micro-batch 切法 观察: - 静态切分 (按条数 2): 4 个桶, token 数 [7300, 5200, 11500, 9000], 极差 2.21×, 其中 mb3 已经接近 budget 13312。预算线再低一点就会触发 OOM。 - 动态切分 (按 token 预算 13312): 3 个桶, token 数 [11300, 11200, 10500], 极差 1.08×, 每桶都接近 budget, 没有浪费也没有 OOM 风险。\nDP 维度同步: 从「各算各的」到「整齐划一」 动态模式下, 4 张卡各自算出来的 num_micro_batches 大概率不一样。verl 用 all_reduce(MAX) 把它们拉齐, 否则 FSDP 集合通信对不上号会直接 hang。\nDP 维度同步演示 关键事实澄清:「拉齐」靠的不是补 dummy padding, 而是让短的 rank 把自己手里的样本切成更多、更稀疏的桶 — 代码里的硬约束是 assert num_micro_batches \u0026lt;= num_groups, 每个桶至少 1 条真实样本, 没有空 batch。短 rank 后续 forward 循环里跑的是稀疏的真样本而非 padding。\n遗留隐患: verl 在进 dynamic_bsz 之前其实还有一层全局 KK 平衡 (_balance_batch, 由 trainer.balance_batch=True 默认开), 会先重排样本让各 rank 总 token 数尽量接近 (log 里 global_seqlen/balanced_min/max 就是它的产物)。但它有 equal_size=True 的硬约束 (每 rank 等条数), 且只平衡总和不平衡每个 micro-batch 的工作量。所以单条特别长的样本仍可能让某 rank 的某个 micro-batch 慢得多, 触发 NCCL watchdog timeout (issue #5750 的根因之一)。\n关键代码逻辑 动态批处理 (use_dynamic_bsz) # 核心函数: rearrange_micro_batches() # 使用 Karmarkar-Karp 算法平衡各 micro batch 的 token 总数 max_token_len = ppo_max_token_len_per_gpu * ulysses_sequence_parallel_size micro_batches, indices = rearrange_micro_batches(batch, max_token_len) # Loss 缩放 (保证梯度正确) loss = policy_loss * (len(data) / ppo_mini_batch_size) 移除 Padding (use_remove_padding) # 移除 padding input_ids_rmpad, indices, cu_seqlens, *_ = unpad_input(input_ids.unsqueeze(-1), attention_mask) # 模型使用 flash_attn_varlen 进行计算 output = self.actor_module(input_ids=input_ids_rmpad, attention_mask=None, ...) # 恢复原始形状 full_log_probs = pad_input(hidden_states=log_probs.unsqueeze(-1), indices=indices, batch=batch_size, seqlen=seqlen) 序列并行 (ulysses_sequence_parallel_size \u0026gt; 1) # 填充并切分输入 input_ids_rmpad, position_ids_rmpad, pad_size = ulysses_pad_and_slice_inputs( input_ids_rmpad, position_ids_rmpad, sp_size=self.ulysses_sequence_parallel_size ) # 模型前向后聚合结果 log_probs = gather_outpus_and_unpad(log_probs, gather_dim=0, unpad_dim=0, padding_size=pad_size) 梯度累积关系 # 固定 batch size 模式 gradient_accumulation = ppo_mini_batch_size // ppo_micro_batch_size_per_gpu loss = policy_loss / gradient_accumulation # 动态 batch size 模式 loss = policy_loss * (len(data) / ppo_mini_batch_size) # 按实际样本数比例缩放 参数配合使用的典型场景 场景 use_dynamic_bsz use_remove_padding ulysses_sp 说明 基础训练 ❌ ❌ ❌ 固定 batch size, 标准 attention 长序列优化 ❌ ✅ ❌ 移除 padding 减少无效计算 负载均衡 ✅ ❌ ❌ 动态划分 batch 平衡各 GPU 计算量 长序列 + 负载均衡 ✅ ✅ ❌ 推荐配置, 效率最高 超长序列 ✅ ✅ ✅ 序列并行支持超长序列训练 参数生效速查表 遇到”我设了这个参数怎么没用”的时候回来看一眼。\n参数 use_dynamic_bsz=False use_dynamic_bsz=True 说明 *.ppo_micro_batch_size_per_gpu 生效 忽略 每卡 micro-batch 的样本条数 *.log_prob_micro_batch_size_per_gpu 生效 忽略 compute_log_prob / ref_log_prob 用 actor.ppo_max_token_len_per_gpu 忽略 生效 actor 训练 micro-batch 的 token 上限 ref / rollout.log_prob_max_token_len_per_gpu 忽略 生效 前向算 logprob 时的 token 上限 critic.ppo_max_token_len_per_gpu 忽略 生效 critic 用 dynamic 时才生效 data.max_prompt_length / max_response_length 生效 生效 跟 dynamic_bsz 无关, 是单条样本的硬上限 rollout.max_model_len / max_num_batched_tokens 生效 生效 vLLM 引擎参数, 跟训练侧切分无关 总结 batch size 概念分层: 全局 train_batch_size → 每个 DP rank 上的 ppo_mini_batch_size (per-GPU shard) → 每个 rank 内的 micro-batch。RL 训练里不同位置说”batch_size”往往指不同含义, 跟 TP/SP/DP 维度乘在一起。\n两条切分路径: use_dynamic_bsz=False 走静态 (按条数等分); use_dynamic_bsz=True 走动态 (按 token 预算 + KK 算法)。后者能避免长样本 OOM 和短样本 padding 浪费, 但代价是实现复杂。\n两层 KK 平衡: _balance_batch 负责 rank 之间的总 token 平衡 (equal_size=True); dynamic_bsz 负责每个 rank 内 micro-batch 的 token 平衡 (equal_size=False)。两层叠加才能让各 rank 的每个 micro-batch 都接近 workload。\nDP 同步关键: dynamic_bsz 必须配 all_reduce(MAX) 把 num_micro_batches 拉齐, 否则 FSDP 的 all_gather 循环次数不一致, 直接 NCCL hang。这是 dynamic 模式最容易踩的坑, 对应多个 issue #5750、#3873、#12 等 hang 问题。\n推荐配置: use_dynamic_bsz=True + use_remove_padding=True, 处理变长序列时效率最高。如果需要支持超长序列 (单条超过单卡能容纳的长度), 再叠 ulysses_sequence_parallel_size \u0026gt; 1。\nLoss 缩放差异:\n固定 batch size: loss = policy_loss / gradient_accumulation 动态 batch size: loss = policy_loss * (len(data) / ppo_mini_batch_size) — 按实际样本数比例缩放, 等价于”按权重”对每个 micro-batch 的梯度归一化。 参考 浅入理解verl中的batch_size batch size 是怎么被切开的 (syt-nju) — 本文”两条切分路径”、“_balance_batch”、“DP 同步”、“参数生效速查表”四节主要整理自这篇博客 Performance Tuning Guide — verl docs issue #5750 — NCCL timeout during compute_log_prob issue #3873 — DAPO+Megatron+SGLang hang PR #5110 — upcast seq_len_effective to int64 issue #6073 — VLM nested tensor jagged 翻转 issue #12 — Hangs during vllm rollout [[DataProto]] — 数据分发与 dispatch mode [[device_mesh]] — DP/TP/SP 维度 ","date":"2025-07-16T00:00:00Z","permalink":"/batch_size%E8%A7%A3%E9%87%8A/","title":"batch_size 解释"},{"content":"利用EM算法思想的论文。\n论文综合解读 大型语言模型（LLMs）在处理复杂推理任务时已展现出惊人的潜力，但这背后往往伴随着一个严峻的挑战——推理过程的可靠性。我们经常见到模型给出的答案看似合理，但其“思考”过程（即推理链，Chain-of-Thought）却充满了逻辑漏洞。这极大地限制了 LLM 在数学、编程、科学研究等高要求领域的实际应用。\n《BRITE》这篇论文直面这一核心挑战，提出了一套名为BRITE (Bootstrapping Reinforced Thinking Process)的创新框架。其核心思想可以概括为：让模型“自我进化”，通过强化学习来学会如何“更好地思考”，然后用这些高质量的“思考过程”来反哺和提升自身。 这是一种“授人以渔”而非“授人以鱼”的思路，旨在从根本上增强模型的内在推理能力。\n论文的精髓在于其优雅地融合了两种强大的机器学习范式：期望最大化（Expectation-Maximization, EM）算法的思想和强化学习（Reinforcement Learning, RL）的技术。\n首先，作者构建了一个清晰的概率图模型（Probabilistic Graphical Model）来描述 LLM 的推理生成过程。这个模型包含四个关键变量：输入的问题（Prompt, X）、模型内部的潜在思考过程（Latent Rationale, Z）、最终的答案（Answer, Y）以及一个评估信号（Evaluation Signal, O，比如答案是否正确）。将“思考过程”Z 明确地作为一个潜在变量来建模，是整个框架的理论基石。它将一个复杂的“从 X 直接到 Y”的问题，分解为了“从 X 生成思考过程 Z”和“从 X 和 Z 生成答案 Y”两个更易于处理的步骤。\n正如论文第 2 页所述：“引入 Z 有两个基本目的：首先，它将复杂的分布 P (Y|X) 分解为更易处理的边际分布 P (Z|X) 和 P (Y|X, Z)，这与思想链（CoT）方法一致；其次，引入评估信号 O 为生成期望的（正确的）推理过程提供了关键的质量信号。”\n在此框架下，BRITE 算法通过一个两阶段的迭代过程来优化模型：\nψ-更新阶段（类比于 EM 算法的 E-步）：这是 BRITE 最核心的创新。传统方法（如拒绝采样，Rejection Sampling）会生成大量推理路径，然后被动地筛选出能得到正确答案的路径。BRITE 则要主动得多。它将“生成高质量思考过程”这一任务构建为一个强化学习问题。模型（作为 RL 中的策略，Policy）的目标是生成一个推理链（一系列动作，Action），以最大化最终获得正确答案的奖励（Reward）。论文使用PPO (Proximal Policy Optimization)算法来训练这个策略，从而学习到一个专门用于生成高质量推理过程的“思考模型”Q (ψ)。这个过程本质上是在逼近一个理想但难以直接计算的后验分布 P (z | x, y, o)，即“在给定问题和正确答案的情况下，最优的思考过程是什么”。\nθ-更新阶段（类比于 EM 算法的 M-步）：在获得高质量的“思考模型”Q (ψ) 后，此阶段就相对直接了。我们用 Q (ψ) 为训练集中的每个问题生成一个高质量的推理过程 z，然后将这些自动生成的、带有详细思考步骤的数据 (x, z, y*) 作为“黄金教材”，对基础的 LLM（由参数θ控制）进行监督微调（Supervised Fine-tuning, SFT）。通过学习这些优质的推理范例，基础 LLM 的内在推理能力得到“引导”和“强化”。\n这个“生成更优的思考过程 -\u0026gt; 训练出更强的模型 -\u0026gt; 生成更优的思考过程”的循环，就是 BRITE“自举（Bootstrapping）”思想的体现。\n实验结果非常有力地支撑了这一框架的有效性。在 GSM 8 K 和 MATH 等标准数学推理基准上，BRITE 不仅显著优于拒绝采样等基线方法，更令人瞩目的是，它在多个测试中达到甚至超越了使用人类专家标注的推理过程进行 SFT 的性能。例如，在 Gemma-2-9 B 模型上，BRITE 在 MATH 测试集上取得了 50.5%的准确率，而使用人类标注数据进行 SFT 的准确率仅为 41.5%（见论文 Table 1）。这揭示了一个惊人的可能性：机器通过强化学习自我探索生成的推理过程，其质量可以媲美甚至超过人类专家的示范。这对于降低 AI 训练成本、突破人类知识瓶颈具有重大意义。\n总而言之，《BRITE》提供了一个理论完备且实践有效的框架，旨在通过 RL 驱动的自我进化，将 LLM 从一个简单的文本生成器，转变为一个更可靠的“思考者”。它不仅为提升 LLM 推理能力开辟了一条新路，也为我们理解和构建更高级的人工智能系统提供了深刻的启示。\n接下来，我将按照您提出的六个问题，对论文进行更详细的剖析。\n### 1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升大型语言模型的复杂推理能力，并解决其推理过程不可靠的问题。具体来说，它致力于开发一个能够自动化生成高质量、逻辑正确的思考过程（推理链）的框架，并利用这些生成的过程来增强 LLM 自身的推理性能。\n解决的实际问题：\n推理过程的“黑盒”与不可靠性：当前的 LLM 即使能给出正确答案，其推理步骤也常常是随意、甚至错误的。这使得我们无法信任它去执行金融分析、医疗诊断、法律咨询等高风险任务。 对人工标注数据的过度依赖：提升模型推理能力最直接的方法是使用大量由人类专家编写的“解题步骤”进行微调。但这不仅成本高昂、耗时费力，而且对于前沿的科学问题，人类专家自身也可能不存在或存在分歧。 现有自动化方法的局限性：像拒绝采样（Rejection Sampling, RS）这样的自动化方法，虽然无需人工数据，但效率低下。它们像是在“大海捞针”，只能从模型自己生成的众多（通常质量不高的）推理中筛选出碰巧能得到正确答案的，而无法主动地、有指导性地生成更优的推理路径。 对行业发展的重要意义：\n推动 AI 从“生成”走向“推理”：这是 AI 发展的关键一步。可靠的推理能力是实现通用人工智能（AGI）的基石。解决了这个问题，AI 才能真正成为科学研究、软件开发和复杂决策中的得力助手。 大幅降低高质量 AI 的训练成本：BRITE 展示了模型“自我教育”和“自我提升”的潜力。如果模型能够自动化地为自己创造高质量的训练数据，将极大地减少对昂贵的人工标注的依赖，使顶尖 AI 技术的开发更加普惠和高效。 增强 AI 系统的可解释性和可信度：通过生成更清晰、更逻辑化的思考过程，我们能更好地理解模型“在想什么”，这对于调试模型、修复错误、以及在关键应用中建立信任至关重要。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 论文的核心贡献是提出了BRITE 框架，它包含了一系列环环相扣的新思路和方法。\n新思路与方法： 统一的概率图模型：首次将 LLM 的推理过程形式化为一个包含问题 (X)、潜在思考过程 (Z)、答案 (Y) 和评估信号 (O)的概率图模型（见论文 Figure 1）。这个模型清晰地定义了问题，并将“思考过程”Z 作为核心的潜在变量进行优化，为后续算法设计提供了坚实的理论基础。 RL 驱动的推理生成（ψ-更新）：这是最具颠覆性的创新。BRITE 没有采用被动的筛选方式，而是将“生成最佳推理链”的任务转化为一个强化学习（RL）优化问题。 \u0026gt; 论文第 8 页明确指出：“为了实现这个目标，我们旨在利用 RL 来训练一个 LLM，该 LLM 能够表征我们需要的分布……我们将这个具有挑战性的采样问题，转换为一个更易于处理的 RL 优化问题。” 在这个 RL 设定中，模型通过探索不同的“思考”步骤（动作），来学习如何最大化最终答案正确的概率（奖励）。这种主动探索的方式，使得模型有可能发现人类都未曾想到的、更优或更简洁的解题路径。 自举学习循环（Bootstrapping Loop）：BRITE 建立了一个“生成-微调”的自举循环。RL 阶段生成高质量的推理数据，SFT 阶段利用这些数据提升模型能力，而能力更强的模型又能反过来在下一轮迭代中生成更高质量的数据。这个正反馈循环是模型实现自我进化的关键。 框架的通用性：BRITE 不仅仅是一个单一算法，它是一个灵活的框架。论文展示了它可以与不同的训练范式结合，例如： BRITE-SFT：将 RL 生成的推理链用于标准的监督微调。 BRITE-DPO：将 RL 生成的推理链用于构建更高质量的偏好数据对 (z_win, y_win) vs (z_lose, y_lose)，从而显著增强直接偏好优化（DPO）的效果。 与之前方法的比较、特点和优势： 特点 BRITE 拒绝采样 (Rejection Sampling, e.g., STaR) 人工标注 SFT 数据来源 自动化，通过 RL 主动生成 自动化，通过模型自由生成后筛选 依赖人类专家 生成方式 主动探索与优化，寻找最优推理路径 被动筛选，在已有生成中“碰运气” 人类预设的固定路径 推理质量 高，可能发现超越人类的路径 中等，受限于模型原始生成能力 高，但可能存在偏见或非最优 成本 计算成本高，但无需人工 计算成本中等，无需人工 人工成本极高 可扩展性 强，可应用于任何有验证器的任务 强 弱，难以扩展到新领域 核心优势：相较于拒绝采样，BRITE 更高效且上限更高，因为它是在主动“学习如何思考”；相较于人工标注，BRITE成本更低、可扩展性更强，并且展现了超越人类标注的潜力。\n### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了周密且有说服力的实验来验证 BRITE 的有效性，主要围绕数学推理和代码生成两大任务。\n实验设计： 任务与数据集： 数学推理：使用了两个经典基准，GSM 8 K（小学数学题）和MATH（竞赛级数学题），覆盖了从易到难的推理挑战。 代码生成：使用了HumanEval和BCB (BigCodeBench)，评估模型根据自然语言指令生成正确代码的能力。 基础模型：实验覆盖了多个主流的开源模型，如Mistral-7 B, Gemma 系列, Llama-3-8 B, Qwen 2.5-7 B等，以证明方法的普适性。 对比基线（Baselines）： 基础模型（Instruction-tuned）：未经任何额外推理训练的模型。 SFT：使用人类标注的推理过程进行监督微调。这是“黄金标准”之一。 RS（Rejection Sampling）：自动化方法的代表，生成多个推理并筛选出能得到正确答案的进行微调。 Iterative DPO：在 RLHF 场景下的强基线，通过迭代生成偏好数据来优化模型。 关键实验数据与结果： BRITE 显著优于自动化基线（RS）： \u0026gt; 论文 Table 1 显示，在所有模型和数据集上，BRITE 的性能均一致地优于 RS。例如，在 Gemma-1.1-7 B 模型和 MATH 数据集上，BRITE 取得了23.7%的准确率，而 RS 仅为18.7%。这证明了 RL 主动探索的优越性。 BRITE 能匹敌甚至超越人类标注 SFT： \u0026gt; 这是最惊人的发现。同样在 Table 1 中，在 Gemma-2-9 B 模型上，BRITE 在 GSM 8 K 上获得了89.7%的准确率，优于 SFT 的 80.1%；在 MATH 上获得了50.5%，远超 SFT 的 41.5%。 \u0026gt; 引用原文 (Section 4.2): “This highlights a remarkable outcome: RL-generated thinking processes can achieve a quality comparable to or even superior to human-derived reasoning.” (这凸显了一个非凡的结果：由强化学习生成的思考过程，其质量可以达到甚至超过人类推导的思考过程。) BRITE 能增强 RLHF 流程（BRITE-DPO）： 论文中的Figure 3直观地展示了 BRITE-DPO 与标准 Iterative DPO 的对比。在所有四种模型和两个数据集上，BRITE-DPO 的性能都显著高于 Iterative DPO。这表明，通过生成更高质量的内在思考过程，可以为偏好学习提供更优质、更具分辨力的训练信号。 Figure 3 (from paper): BRITE-DPO consistently outperforms iterative DPO, showing its value in the RLHF stage. 扩展实验验证了其巨大潜力： 在 Section 4.3 中，作者使用了更强的 Qwen 2.5-7 B 模型和更大的 40 K 混合数据集。在更难的基准上，BRITE 的优势更加明显。如下表（整理自论文 Table 2）所示，在 MATH 500 上，BRITE 的第一轮ψ-update 就将准确率从基线的 44.1%提升至79.1%，而 RS 仅能提升至 54.3%，差距巨大。 Method MATH 500 Minerva Math AIME 24 GPQA Diamond Base Model 44.1 12.9 0.9 25.9 RS (Rejection Sampling) 54.3 21.0 5.6 26.9 BRITE (ψ-update) 79.1 35.0 14.3 28.5 BRITE (θ-update) 76.9 40.6 14.4 29.8 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? BRITE 为我们打开了一扇通往“自进化推理智能”的大门，未来的探索空间广阔。\n值得探索的问题和挑战： 更丰富的评估信号（O）：目前，评估信号 O 主要是基于最终答案的“正确/错误”。未来的研究可以探索更复杂的奖励函数，例如推理过程的效率（更少的步骤）、简洁性、人类对推理过程本身的偏好度，而不仅仅是答案。这将引导模型学习更“优雅”的思考方式。 开放域和无确定答案的任务：BRITE 在有明确验证器（如数学答案、代码单元测试）的任务上表现出色。真正的挑战在于如何将其推广到没有唯一正确答案的开放域任务，如写一份商业分析报告或进行法律辩护。这需要一个能评估复杂文本质量的“AI 裁判”，而这本身就是一个前沿的 AI 研究课题。 与工具使用和知识检索结合：未来的推理任务必然涉及与外部世界的交互。可以将 BRITE 框架与工具使用（Tool Use）和检索增强生成（RAG）相结合，让模型的“思考过程”Z 不仅包含内部的逻辑推导，还包括调用 API、查询数据库、阅读文档等动作。 推理过程的安全性与对齐：当模型能自主生成复杂的推理链时，如何确保这些推理过程是安全、无偏见、且符合人类价值观的？这提出了新的对齐（Alignment）挑战，我们需要防止模型“为了达成目标而不择手段”，比如在推理中利用或生成有害信息。 可能催生的新技术和投资机会： “AI 推理能力”即服务（Reasoning-as-a-Service）：公司可以开发基于 BRITE 这类技术的平台，专门为企业客户的特定任务（如金融量化、药物发现）自动训练出具备高级推理能力的专用模型。 下一代 MLOps 平台：传统的 MLOps 平台专注于数据管理和模型部署。未来将出现专注于自动化推理提升和高质量合成数据生成的 MLOps 平台。这会成为模型能力竞争的核心壁垒。 赋能开源模型生态：BRITE 这类技术为开源社区提供了一条追赶甚至超越闭源顶尖模型的有效路径。通过社区的计算资源，可以持续地对开源模型进行“自举”训练，使其推理能力不断进化。这将是开源 AI 生态的一个巨大投资机会。 “自我完善”的 AI 系统：长远来看，这项研究是构建能够在部署后持续学习和自我完善的 AI 系统的前奏。这样的系统能在与环境的交互中自动发现自己的知识盲区和能力短板，并主动生成数据来弥补它们，从而实现真正的终身学习。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些值得商榷和深入探讨的地方。\n计算成本问题：BRITE 框架，特别是涉及到 PPO 的 RL 训练阶段（ψ-更新），计算开销是巨大的。论文中提到实验使用了 4 到 8 块 NVIDIA H 100 GPU，这对许多学术研究机构和小型公司来说是一个很高的门槛。论文没有详细分析其计算成本与性能增益之间的权衡，这对于评估其工业实用性是一个关键缺失。 奖励作弊（Reward Hacking）的风险：强化学习领域一个众所周知的问题是“奖励作弊”。虽然 BRITE 的奖励信号与可验证的最终答案挂钩，但模型仍可能找到一些“取巧”的、非普适的推理路径，这些路径在训练集上能得到正确答案，但在更广泛的分布上却不成立，或者其推理过程本身毫无逻辑可言。论文缺乏对生成推理链 z 的质量的深入分析（例如，人工评估其逻辑性），而不仅仅是其最终结果的正确性。 对高质量验证器的强依赖：整个框架的成功，高度依赖于一个可靠的评估信号 O。对于数学和代码，这相对简单。但对于前文提到的开放域任务，验证器本身就成了一个瓶颈。BRITE 的性能上限被验证器的能力所束缚。可以说，它将“如何提升推理能力”的问题，部分转移到了“如何构建一个完美的裁判”上。 “超越人类标注”结论的普适性存疑：BRITE 超越 SFT on human data 的结果虽然振奋人心，但需要更审慎地看待。这是否可能是因为所用的公开数据集中的人类标注本身就存在质量问题或不够优化？或者是因为所选的特定模型架构恰好对 RL 训练特别敏感？这个“惊人”的结论需要在更多样化的数据集、模型家族和任务上进行广泛的复现，才能被确立为一个普遍规律。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文充满了可以借鉴的创新火花。\n重点学习与启发： 将“思考过程”作为可优化的核心资产：最大的启发是将“思考过程”或“中间步骤”从一个简单的提示工程技巧，提升到模型核心学习目标的高度。在你的项目中，思考一下哪些任务的中间步骤是可以被明确定义和评估的，并尝试将其作为一个潜在变量来建模和优化。 用强化学习生成高质量合成数据：这是一个非常强大的范式。不要仅仅将 RL 用于与人类偏好对齐（RLHF），可以将其看作一个强大的合成数据生成引擎。当你缺少高质量的训练数据，但有一个明确的评估标准时，可以尝试用 RL 来探索和生成符合标准的优质数据，再用这些数据去监督微调你的模型。这是一种从“被动筛选”到“主动创造”的思维转变。 自举（Bootstrapping）的力量：模型“教”自己的想法非常迷人。在你的工作中，可以设计一些迭代流程：用当前最好的模型去标注一批无标签数据 -\u0026gt; 用这些新标注的数据去微调模型，得到一个更强的版本 -\u0026gt; 重复此过程。这种自我增强的循环在很多资源有限的场景下都非常有效。 需要补充的背景知识： 强化学习（RL）基础：特别是PPO (Proximal Policy Optimization)算法。你需要理解其基本原理，为何它在 LLM 训练中比其他 RL 算法更稳定。 期望最大化（EM）算法：虽然不要求精通其数学推导，但理解 EM 算法中 E-步（估计潜在变量的后验分布）和 M-步（基于潜在变量最大化似然）的核心思想，会让你对 BRITE 的两阶段框架有更深刻的理解。 推理生成的前沿方法：了解Chain-of-Thought (CoT), Tree of Thoughts (ToT), 以及STaR (Self-Taught Reasoner), Self-Correction等技术，这能帮助你更好地理解 BRITE 所处的学术脉络以及它的创新之处。 偏好学习算法：特别是DPO (Direct Preference Optimization)。要理解论文中的 BRITE-DPO 扩展，你需要了解 DPO 是如何绕过显式的奖励建模，直接从偏好数据中学习的。 希望这份详尽的解读能帮助您全面、深入地掌握这篇优秀论文的精髓。\n","date":"2025-07-16T00:00:00Z","permalink":"/britebootstrapping-reinforced-thinking-process-to-enhance-language-model-reasoning/","title":"BRiTE：Bootstrapping Reinforced Thinking Process to Enhance Language Model Reasoning"},{"content":"Self-attention 首先介绍一下最主要的 self-attention，可以说是 self-attention 实现了上述的 token 之间交互的功能。\n自注意力是模型的关键组成部分之一。注意和自注意之间的区别在于，自注意在相同性质的表示之间运行：例如，某个层中的所有编码器状态。\n形式上，这种直觉是通过查询键值注意来实现的。Self-attention 中的每个输入标记都会收到三种表示，对应于它可以扮演的角色：\nQuery Key Value 进入正题：\n作为我们想要翻译的输入语句“The animal didn’t cross the street because it was too tired”。句子中”it”指的是什么呢？“it”指的是”street” 还是“animal”？对人来说很简单的问题，但是对算法而言并不简单。\n当模型处理单词“it”时，self-attention 允许将“it”和“animal”联系起来。当模型处理每个位置的词时，self-attention 允许模型看到句子的其他位置信息作辅助线索来更好地编码当前词。如果你对 RNN 熟悉，就能想到 RNN 的隐状态是如何允许之前的词向量来解释合成当前词的解释向量。Transformer 使用 self-attention 来将相关词的理解编码到当前词中。\n下面看一下 self-attention 是如何计算的：\n向量计算 第一步，根据编码器的输入向量，生成三个向量，比如，对每个词向量，生成 query-vec, key-vec, value-vec，生成方法为分别乘以三个矩阵，这些矩阵在训练过程中需要学习。【注意：不是每个词向量独享 3 个 matrix，而是所有输入共享 3 个转换矩阵；权重矩阵是基于输入位置的转换矩阵；有个可以尝试的点，如果每个词独享一个转换矩阵，会不会效果更厉害呢？】\n注意到这些新向量的维度比输入词向量的维度要小（512–\u0026gt;64），并不是必须要小的，是为了让多头 attention 的计算更稳定。\n第二步，计算 attention 就是计算一个分值。对“Thinking Matchines”这句话，对“Thinking”（pos #1 ）计算 attention 分值。我们需要计算每个词与“Thinking”的评估分，这个分决定着编码“Thinking”时（某个固定位置时），每个输入词需要集中多少关注度。\n这个分，通过“Thing”对应 query-vector 与所有词的 key-vec 依次做点积得到。所以当我们处理位置 #1时 ，第一个分值是 q 1 和 k 1 的点积，第二个分值是 q 1 和 k 2 的点积。这也就是所谓的注意力得分. 第三步和第四步，除以 8 (\\(=\\sqrt{dim_{key}}\\))，这样梯度会更稳定。然后加上 softmax 操作，归一化分值使得全为正数且加和为 1。\nSoftmax 分值决定着在这个位置，每个词的表达程度（关注度）。很明显，这个位置的词应该有最高的归一化分数，但大部分时候总是有助于关注该词的相关的词。\n第五步，将 softmax 分值与 value-vec 按位相乘。保留关注词的 value 值，削弱非相关词的 value 值。\n第六步，将所有加权向量加和，产生该位置的 self-attention 的输出结果。 上述就是 self-attention 的计算过程，生成的向量流入前向网络。在实际应用中，上述计算是以速度更快的矩阵形式进行的。下面我们看下在单词级别的矩阵计算。\n矩阵计算 第一步，计算 query/key/value matrix，将所有输入词向量合并成输入矩阵 \\(X\\)，并且将其分别乘以权重矩阵 \\(W^q, W^k,W^v\\)\n最后，鉴于我们使用矩阵处理，将步骤 2~6 合并成一个计算 self-attention 层输出的公式。 多头注意力机制 论文进一步增加了 multi-headed 的机制到 self-attention 上，在如下两个方面提高了 attention 层的效果：\n多头机制扩展了模型集中于不同位置的能力。在上面的例子中，z 1 只包含了其他词的很少信息，仅由实际自己词决定。在其他情况下，比如翻译 “The animal didn’t cross the street because it was too tired”时，我们想知道单词”it”指的是什么。 多头机制赋予 attention 多种子表达方式。像下面的例子所示，在多头下有多组 query/key/value-matrix，而非仅仅一组（论文中使用 8-heads）。每一组都是随机初始化，经过训练之后，输入向量可以被映射到不同的子表达空间中。 如果我们计算 multi-headed self-attention 的，分别有八组不同的 Q/K/V matrix，我们得到八个不同的矩阵。 这会带来点麻烦，前向网络并不能接收八个矩阵，而是希望输入是一个矩阵，所以要有种方式处理下八个矩阵合并成一个矩阵。\n上述就是多头自注意机制的内容，我认为还仅是一部分矩阵，下面尝试着将它们放到一个图上可视化如下。\n#### 代码 下面实现一下多头注意力机制，在原论文中，实现的方法如下： 也就是对每个 W 进行多头的设置，即为原维度/head，然后拼接后，再经过 \\(hd_v\\times d_{model}\\) 的转换又得到原来的维度，代码的实现不太一样，代码是 W 还是 \\(d_{model}\\times d_{model}\\) 的矩阵然后得到 q, k, v 之后再进行截断，实现如下。\nclass MultiHeadedAttention(nn.Module): def __init__(self, h, d_model, dropout=0.1) -\u0026gt; None: # h为head，这里为8，d_model为embedding的维度，这里为512 super().__init__() assert d_model % h == 0 self.d_k = d_model // h # 64 self.h = h self.Q_Linear = nn.Linear(d_model, d_model) self.K_Linear = nn.Linear(d_model, d_model) self.V_Linear = nn.Linear(d_model, d_model) self.res_Linear = nn.Linear(d_model, d_model) self.attn = None self.dropout = nn.Dropout(p=dropout) def forward(self, query, key, value, mask=None): if mask is not None: mask = mask.unsqueeze(1) batch_size = query.size(0) query = self.Q_Linear(query).view(batch_size, -1, self.h, self.d_k) # (batch_size, seq_len, h, d_k)即(batch_size, seq_len, 8, 64) query = query.transpose(1, 2) # (batch_size, h, seq_len, d_k)即(batch_size, 8, seq_len, 64) key = self.K_Linear(key).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) value = self.V_Linear(value).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout) # x为(batch_size, h, seq_len, d_k) # attn为(batch_size, h, seq_len1, seq_len2) x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.h * self.d_k) # (batch_size, h, seq_len, d_k) -\u0026gt; (batch_size, seq_len, h, d_k) -\u0026gt; (batch_size, seq_len, h * d_k) = (batch_size, seq_len, 512) return self.res_Linear(x) Masked self-attention 在训练的时候，主要是消除后面的信息对预测的影响，因为 decoder 输入的是整个句子，也就是我们所谓的参考答案，而实际预测的时候就是预测后面的 token，用不到后面的 token，如果不 mask 掉，当前的 token 将看到“未来”，这不是我们想要的，因此必须要 mask 掉。\n其实 decoder 里的 sequence mask 与 encoder 里的 padding mask 异曲同工，padding mask 其实很简单，就是为了使句子长度一致进行了 padding，而为了避免关注 padding 的位置，进行了 mask，具体的做法就是将这些位置的值变成负无穷，这样 softmax 之后就接近于 0 了。\n而 sequence mask 思想也差不多：\n假设现在解码器的输入”\u0026lt; s \u0026gt; who am i \u0026lt; e \u0026gt;“在分别乘上一个矩阵进行线性变换后得到了 Q、K、V，且 Q 与 K 作用后得到了注意力权重矩阵（此时还未进行 softmax 操作），如图 17 所示。 此时已经计算得到了注意力权重矩阵。由第 1 行的权重向量可知，在解码第 1 个时刻时应该将 20%（严格来说应该是经过 softmax 后的值）的注意力放到’\u0026lt; s \u0026gt;’上，30%的注意力放到’who’上等等。不过此时有一个问题就是，模型在实际的预测过程中只是将当前时刻之前（包括当前时刻）的所有时刻作为输入来预测下一个时刻，也就是说模型在预测时是看不到当前时刻之后的信息。因此，Transformer 中的 Decoder 通过加入注意力掩码机制来解决了这一问题。 当然还要进行 softmax 等计算。\n在网上查了很多资料，说法都很不一样，不过我更倾向于这样的看法。而在预测的时候是用前面的输出结果作为输入的。\n几张图帮助理解： 后面还有 padding mask，所有的 self attention 都要用这个，因为 pad 的位置没有任何意义。 实践一下加深理解： 首先我们来定义模型：\n# 词典数为10， 词向量维度为8 embedding = nn.Embedding(10, 8) # 定义Transformer，注意一定要改成eval模型，否则每次输出结果不一样 transformer = nn.Transformer(d_model=8, batch_first=True).eval() 接下来定义我们的 src 和 tgt：\n# Encoder的输入 src = torch.LongTensor([[0, 1, 2, 3, 4]]) # Decoder的输入 tgt = torch.LongTensor([[4, 3, 2, 1, 0]]) 然后我们将 [4] 送给 Transformer 进行预测，模拟推理时的第一步：\ntransformer(embedding(src), embedding(tgt[:, :1]), # 这个就是用来生成阶梯式的mask的 tgt_mask=nn.Transformer.generate_square_subsequent_mask(1)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 然后我们将 [4, 3] 送给 Transformer，模拟推理时的第二步：\ntransformer(embedding(src), embedding(tgt[:, :2]), tgt_mask=nn.Transformer.generate_square_subsequent_mask(2)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598], [ 1.2726, -0.3516, 0.6584, 0.3297, 1.1161, -1.4204, -1.5652, -0.0396]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 出的第一个向量和上面那个一模一样。\n最后我们再将 tgt 一次性送给 transformer，模拟训练过程：\ntransformer(embedding(src), embedding(tgt), tgt_mask=nn.Transformer.generate_square_subsequent_mask(5)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598], [ 1.2726, -0.3516, 0.6584, 0.3297, 1.1161, -1.4204, -1.5652, -0.0396], [ 1.4799, -0.3575, 0.8310, 0.1642, 0.8811, -1.3140, -1.5643, -0.1204], [ 1.4359, -0.6524, 0.8377, 0.1742, 1.0521, -1.3222, -1.3799, -0.1454], [ 1.3465, -0.3771, 0.9107, 0.1636, 0.8627, -1.5061, -1.4732, 0.0729]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 可以看到使用 mask 后就可以保证前面的结果都是不变的，不然如果没有 mask 则计算 attention 时因为计算注意力变化所以结果都会变化，这就是 Mask self-attention 的意义。 到这里 self-attention 就介绍完了\n","date":"2025-07-16T00:00:00Z","permalink":"/mha/","title":"MHA"},{"content":" image.png 标准的 mha 中，KV heads 的数量和 Query heads 的数量相同，每一个 q head 对应一个独立的 kv head，但这样的开销比较大。 MQA (Multi Queries Attention): MQA 比较极端，只保留一个 KV Head，多个 Query Heads 共享相同的 KV Head。这相当于不同 Head 的 Attention 差异，全部都放在了 Query 上，需要模型仅从不同的 Query Heads 上就能够关注到输入 hidden states 不同方面的信息。这样做的好处是，极大地降低了 KV Cache 的需求，但是会导致模型效果有所下降。（层内共享）\n","date":"2025-07-16T00:00:00Z","permalink":"/mqa/","title":"MQA"},{"content":"论文深入解读 这篇名为《Process Reinforcement through Implicit Rewards》(通过隐式奖励进行过程强化) 的论文，由来自清华大学、上海人工智能实验室、UIUC 等顶尖机构的研究者共同完成，为大语言模型（LLM）的强化学习（RL）领域带来了一个极具价值和创新性的解决方案——PRIME 框架。其核心贡献在于，它成功地将过程监督 (Process Supervision) 的高效率与结果监督 (Outcome Supervision) 的低成本相结合，解决了在复杂推理任务（如数学和编程）中应用强化学习时面临的关键瓶颈。\n在深入探讨 PRIME 之前，我们必须理解它试图解决的根本矛盾。在训练 LLM 进行多步推理时，我们有两种主要的奖励（Reward）机制：\n稀疏结果奖励 (Sparse Outcome Rewards)：模型生成完整的解答后，我们只根据最终答案的正确与否给予一次性奖励。这种方法简单、成本低，因为我们只需要一个最终结果的验证器（例如，数学题的答案是否正确，代码是否通过所有测试用例）。但它的巨大缺陷在于“信用分配 (credit assignment)”难题：如果答案错误，模型无法知道是哪一步推理出了问题；即使答案正确，推理过程也可能存在瑕疵甚至逻辑跳跃（即“侥幸成功”）。这导致学习效率低下，尤其是在需要长链条推理的任务上。\n稠密过程奖励 (Dense Process Rewards)：在模型生成过程中的每一步（或每一个 token），都给予一次奖励，以评价这一步推理的质量。这种方法能提供细粒度的指导，理论上学习效率和效果都远胜于稀疏奖励。但它的致命弱点在于成本高昂：为每一步推理都进行人工标注或验证，是“令人望而却步的昂贵 (prohibitively expensive)”。此外，如何定义中间步骤的“绝对正确性”本身也是一个模糊的问题。\n现有的大模型 RL 训练（如 RLHF）大多依赖于结果奖励，因此难以在复杂推理上取得突破。而 PRIME 的精妙之处，就在于它找到了一条“鱼与熊掌兼得”的中间道路。\nPRIME 的核心思想可以概括为：用结果监督的方式训练一个模型，却用它来产生过程级别的稠密奖励信号。这个看似矛盾的操作是通过一种名为隐式过程奖励 (Implicit Process Rewards) 的机制实现的。具体来说，它借鉴了 DPO (Direct Preference Optimization) 等方法的思想，将奖励模型 (Reward Model, RM) 本身也设计成一个自回归语言模型 πφ。这个奖励模型像一个标准的结果奖励模型 (Outcome Reward Model, ORM) 一样，只用最终的（正确/错误）结果标签进行训练。论文中提到，它的训练损失是标准的交叉熵损失：\nL_CE(φ) = -E(x,y,ro(y))~T [ro(y) · log σ(rφ(y)) + (1 - ro(y)) · log(1 - σ(rφ(y)))]\n这里 ro(y) 就是最终的 0 或 1 的 outcome reward。然而，在推理和 RL 训练时，PRIME 并不只在最后使用这个模型。由于 πφ 是一个自回归模型，它能在生成过程中的任意一步 t，针对下一个 token yt 给出其概率 πφ(yt|y\u0026lt;t)。PRIME 利用这个概率，与一个固定的参考模型 (reference model) π_ref 的概率进行比较，从而计算出每一步的“隐式奖励”：\nrφ(yt) := β log (πφ(yt|y\u0026lt;t) / π_ref(yt|y\u0026lt;t))\n这个公式是 PRIME 的灵魂。它意味着，尽管奖励模型 πφ 的训练信号是稀疏的最终结果，但我们可以在 RL 训练的每一步都从它身上“榨取”出一个稠密的、token 级别的奖励信号 rφ(yt)。这个奖励直观地衡量了当前策略模型生成的 token yt 在多大程度上“更像”一个能导向正确结果的模型会生成的 token。\n基于这个核心机制，PRIME 框架展现出几个显著的优势：\n解决了标注成本问题：它完全不需要过程级别的标注，只需要最终结果的标签，极大地降低了数据成本。 实现了奖励模型的在线更新：由于只需要结果标签，奖励模型 πφ 可以在 RL 训练过程中，用策略模型 πθ 新生成的样本（rollouts）进行实时更新。这至关重要，因为它能有效缓解“奖励黑客 (reward hacking)”问题——即策略模型找到奖励模型的漏洞并加以利用，而不是真正提升能力。如论文中的图 6 所示，在线更新的 PRM（蓝色和绿色线）其准确率能在训练中持续提升，而离线 PRM（橙色线）则会因为分布偏移而逐渐退化。 简化了开发流程：论文有一个惊人的发现，即 PRM无需专门预训练。可以直接用 SFT（监督微调）后的模型来初始化 PRM。这不仅消除了一个昂贵且耗时的训练阶段，而且实验表明（图 5），直接用 SFT 模型初始化的 PRM 效果甚至好于一个用额外 50 万数据专门训练的 PRM。这大大降低了技术门槛和开发开销。 在实验验证上，论文做得非常扎实。以 Qwen2.5-Math-7B-Base 为基础模型，训练出的 Eurus-2-7B-PRIME 在多个数学和编程基准测试中表现优异。最亮眼的数据是，相较于仅使用结果奖励的 RLOO 方法，PRIME 展现了2.5 倍的样本效率，并在最终性能上提升了6.9%（图 4）。更令人印象深刻的是，与强大的 Qwen2.5-Math-7B-Instruct 模型相比，Eurus-2-7B-PRIME仅用了 10%的训练数据，就在多个推理基准上超越了前者（表 1 和图 1）。例如，在 AIME 2024 测试中，PRIME 模型取得了 26.7%的 pass@1 准确率，远超 GPT-4 o（9.3%）和 Llama-3.1-70 B-Instruct（20.0%）等更大或更强的模型，充分证明了其方法的有效性。\n总之，PRIME 论文不仅仅是提出了一个新模型，更是提出了一种高效、低成本、可扩展的 RL 训练范式。它通过“隐式奖励”这一精妙设计，优雅地化解了过程监督和结果监督之间的核心矛盾，为训练更强大的推理型大模型铺平了道路。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是为大语言模型的强化学习（RL）开发一种可扩展且高效的稠密奖励机制，特别是在需要复杂多步推理的任务（如数学、编程）上。\n想要解决的实际问题：\n高昂的标注成本：当前，要让模型学会正确的推理“过程”，需要对每一步进行人工标注，这种“过程监督”成本极高，限制了其在大规模 RL 中的应用。 稀疏奖励的低效性：仅依赖最终“结果”的正确性来提供奖励，信号过于稀疏，模型难以定位错误步骤，导致训练效率低下，性能提升困难。 奖励模型过拟合（Reward Hacking）：使用静态的、离线训练的奖励模型，策略模型很容易在训练中找到并利用其漏洞，从而获得高奖励分数，但实际能力并未提升。 对行业发展的重要意义：\n大幅降低顶尖推理模型的训练成本：PRIME 证明了仅需结果标签（如代码测试用例、数学答案）就可以实现高效的过程级强化学习。这意味着，训练一个数学或代码高手模型的门槛，从需要大量昂贵的过程标注，降低到了只需要一个可靠的自动验证器。这将使更多中小型公司或研究机构有能力训练出专业领域的顶尖模型。 推动 RL 在 LLM 推理能力提升上的应用：此前，由于上述困难，业界更多采用 SFT（监督微调）来提升模型的推理能力。PRIME 范式为通过 RL 进行更深层次、更探索性的能力提升开辟了新道路，可能成为继 SFT 和 DPO 之后，训练推理智能体的又一核心技术。 迈向更自动化的 AI 训练流程：该方法减少了对人类专家的持续依赖，推动了“AI 训练 AI”的进程。只要能定义一个清晰的最终目标（outcome verifier），PRIME 就能自动地、细粒度地引导模型学习如何达成这个目标，这在科学发现、药物设计等领域具有巨大潜力。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路/方法/模型：论文提出了 PRIME (Process Reinforcement through IMplicit rEwards) 框架。其核心是隐式过程奖励（Implicit Process Rewards） 的概念。\n与之前方法的比较、特点和优势：\n特点 传统方法 (结果奖励 RL) 传统方法 (过程奖励 RL) PRIME 方法 奖励信号 稀疏，仅在序列末尾 稠密，在每一步 稠密，在每一个 token 监督来源 结果标签 (e.g., 最终答案) 过程标签 (e.g., 每步对错) 仅需结果标签 奖励模型更新 通常离线训练，易被 hacking 在线更新成本极高 可在线更新，有效防止 hacking 开发成本 低 极高 低，无需额外标注和 RM 预训练 核心优势分析： 成本效益 (Cost-Effectiveness)：这是最大的优势。PRIME 绕过了对过程标注的依赖。论文在表 1 中明确对比了其模型 Eurus-2-7B-PRIME 和 Qwen2.5-Math-7B-Instruct 的资源需求，前者在 RL 阶段仅用了 150 k 查询，而后者用了 66 k 查询 x 32 个样本，总数据量远小于后者，且没有专门的 RM 训练数据（RM Data 为 0 vs 618k）。 \u0026gt; We demonstrate PRIME’s effectiveness on competitional math and coding… Surpasses Qwen 2.5-Math-7 B-Instruct on seven reasoning benchmarks with 10% of its training data. 可扩展的在线学习 (Scalable Online Learning)：PRIME 的奖励模型仅依赖结果标签，使其可以与策略模型同步在线更新。这解决了 RL 中的一个关键挑战 C 2：“PRM online updates are not scalable”。在线更新使得奖励模型能适应策略模型的分布变化，从而得到更准确的奖励信号，避免奖励 hacking。 开发流程简化 (Simplified Pipeline)：论文的一个关键发现是，PRM 可以直接用 SFT 模型初始化，这解决了挑战 C 3：“Explicit reward modeling brings extra cost”。这极大地简化了工作流程，因为研究者不再需要收集专门的奖励数据并训练一个独立的奖励模型。 \u0026gt; In practice, we find that the starting policy model itself serves as a decent initialization of PRM, bypassing the PRM training stage. ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文通过一系列精心设计的实验和消融研究来验证 PRIME 的有效性。\n实验设计： 基础模型：选用在数学方面有强大基础的 Qwen2.5-Math-7B-Base 模型。 任务领域：专注于复杂的推理任务，即数学 (mathematics) 和 代码生成 (coding)。 评估基准：使用了 7 个公认的、具有挑战性的推理基准，包括 AIME 2024, AMC, MATH-500, Minerva Math, OlympiadBench, LeetCode 和 LiveCodeBench。 核心对比实验：将 PRIME 与一个仅使用结果奖励的强基线方法 RLOO (Leave-One-Out) 进行对比。两者使用相同的 RL 算法框架，唯一的区别在于 PRIME 额外引入了隐式过程奖励。 实验数据和结果： 显著的性能提升： 在图 1 (Overall math performance) 中，Eurus-2-7B-PRIME（深蓝色条）在所有数学基准上都显著优于其 SFT 版本（Eurus-2-7B-SFT，浅蓝色条），平均准确率从 28.8%提升到 43.9%（表中为 41.0%到 43.9%等不同阶段的数据，但图 1 的最终结果提升了 15.1%）。 在表 2 (Detailed results) 中，经过 592 步训练的 PRIME 模型在 AIME 上达到 26.7%，AMC 上达到 57.8%，全面超越了基线以及包括 Llama-3.1-70 B 在内的更大模型。 更高的样本效率： 图 4 (The effect of dense reward) 是最有说服力的证据之一。下图 (a) 显示，PRIME（橙线）达到约 0.48 的训练奖励水平只用了大约 100 步，而 RLOO（蓝线）则需要 240 步。这直观地展示了 2.5 倍的样本效率。 下图 (b) 的测试集准确率也显示，PRIME（橙线）的性能爬升速度和最终高度都优于 RLOO（蓝线）。 Compared with sparse reward, PRIME takes 40% of the training steps to achieve the same training rewards as RLOO and improves the final rewards by 6.9%, with lower variances.\n通用性验证：论文在第 5.4 节和图 10中，将 PRIME 与 REINFORCE、GRPO、PPO 等多种 RL 算法结合，结果表明 PRIME 都能带来一致的性能提升，证明了它作为一种“即插即用”模块的通用性。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 对结果验证器 (Outcome Verifier) 的依赖：PRIME 的成功依赖于一个廉价且可靠的结果验证器。这在数学（检查答案）和编程（运行测试）中是可行的。但对于开放性、主观性强的任务（如创意写作、策略规划、法律咨询），如何定义和自动验证“好的结果”是一个巨大挑战。未来的研究需要探索如何将 PRIME 范式扩展到这些缺乏明确验证器的领域，或许可以结合人类反馈或另一个强大的 AI 作为裁判。\n“Zero”训练的饱和问题：论文在第 5.6 节提到，直接从基础模型开始 RL（即 PRIME-Zero）虽然初期收敛极快，但性能会过早饱和 (quickly saturated)。这是一个关键挑战，因为它限制了这种最高效路径的潜力。未来的研究可以探索如何解决饱和问题，例如通过更智能的探索策略 (exploration strategies)、课程学习 (curriculum learning)，或者在训练中动态调整 β 值来平衡探索和利用。\n隐式奖励的深层机理：PRIME 的隐式奖励有效，但其内在机理仍有待挖掘。它是否真的在奖励“好的推理”，还是在奖励某种“风格”或“模式”？在某些情况下，错误的推理过程也可能导向正确答案，PRIME 是否会错误地强化这种“侥幸”路径？对隐式奖励的可解释性 (interpretability) 和鲁棒性 (robustness) 进行研究将非常有价值。\n与自博弈 (Self-play) 机制的结合：PRIME 提供了一种高效的自我提升机制。未来可以探索将其与 AlphaGo 式的自博弈框架结合，用于更复杂的对抗性或合作性任务，例如辩论、谈判或多智能体协作。\n技术和投资机会： 专业领域模型即服务 (Specialized Model as a Service)：基于 PRIME 这类技术，可以为特定行业（金融、医疗、法律）低成本地训练出高度专业的模型，这本身就是巨大的商业机会。 高效 RL 平台和工具链：提供基于此范式的、开箱即用的 RL 训练平台，会受到市场的欢迎。 自动化数据生成与验证：由于该方法依赖结果验证，投资于开发针对不同领域的自动化问题生成和验证工具的公司将占据有利地位。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 隐式假设的强度：论文的核心假设是——一个仅基于最终结果训练的奖励模型 πφ，其在中间步骤给出的 token 概率 πφ(yt|y\u0026lt;t) 能够有效指导推理过程。这个假设在逻辑性强的数理任务中可能成立，但在更宽泛的任务中可能很脆弱。例如，在写一个故事时，一个好的结局并不能保证每一个段落都是精彩的。论文缺乏对这一核心假设局限性的深入讨论。\n对“好过程”的定义模糊：PRIME 优化的是“通往好结果”的过程，但这不完全等同于人类理解的“好的过程”（例如，优雅、可解释、简洁）。模型可能会学到一些人类难以理解但计算上有效的“捷径”，这在需要人机协作和信任的场景下可能是个问题。\n实验领域的局限性：尽管实验很扎实，但它们都集中在数学和编程这两个具有确定性答案的领域。该方法在多轮对话、长文本摘要、知识问答等更开放领域的有效性没有得到验证，而这些是 LLM 应用更广泛的场景。\n超参数的敏感性：PRIME 引入了至少一个关键超参数 β（公式 3 中的奖励缩放因子）。论文中设其为 0.05，但没有提供关于这个值如何选择、其敏感性如何的详细分析。在实践中，这类超参数的调整往往非常耗时耗力。\n### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？ 重点学习的创新想法： 核心启发：解耦监督信号的“形式”与“来源”。不要被监督数据的原始形态所束缚。PRIME 的精髓在于，它用一个稀疏的信号来源（最终结果）通过一个巧妙的模型设计，生成了稠密的信号形式（token 级奖励）。这个“转换”思想可以应用在很多领域。 自回归模型作为奖励函数：任何为了某个目标（如生成正确答案）而训练的自回归模型，都有潜力被用作一个 token 级的奖励函数。这个想法扩展了 DPO（模型即奖励模型）的概念，并将其应用到了过程级别。 实用主义至上：复用与简化。论文中“用 SFT 模型初始化 PRM”以及“在线更新”的做法，是典型的实用主义。它告诉我们，在构建复杂系统时，应优先考虑如何复用现有组件、简化流程，而不是默认一切都要从头构建。这在工程实践中极具价值。 需要补充的背景知识： 强化学习基础：需要扎实理解策略梯度 (Policy Gradient)、PPO、优势函数 (Advantage Function) 等概念，这是理解论文 RL 部分的基础。 LLM 的 RLHF 范式：特别是要深入理解 DPO (Direct Preference Optimization)。PRIME 的隐式奖励公式 log(πφ / π_ref) 在形式上与 DPO 的奖励思想一脉相承。理解 DPO 有助于你 grasp the core of PRIME. 过程监督与结果监督：深入了解这两种监督方式的优缺点，这是理解本论文动机和贡献的出发点。可以阅读论文中引用的相关工作，如 Uesato et al., 2022 和 Lightman et al., 2023。 ","date":"2025-07-16T00:00:00Z","permalink":"/process-reinforcement-through-implicit-rewards/","title":"PROCESS REINFORCEMENT THROUGH IMPLICIT REWARDS"},{"content":"好的，作为大模型领域的学术专家，我非常乐于为您深入解读这篇具有重要意义的论文《SEARCH-R 1: Training LLMs to Reason and Leverage Search Engines with Reinforcement Learning》。\n这篇论文的核心，是探索如何让大型语言模型（LLM）学会像人类专家一样，在解决复杂问题时，主动、智能、且迭代地使用搜索引擎。它不仅仅是简单地把搜索结果“喂”给模型，而是通过强化学习（Reinforcement Learning, RL），训练模型形成一种内在的“研究”能力——知道什么时候需要信息，需要什么信息，以及如何整合这些信息来形成最终答案。\n以下是我为您准备的详细解读。\n论文深度解读 SEARCH-R 1 的工作可以看作是连接“静态知识库”式 LLM 与“动态信息探索者”式 AI 智能体（Agent）的关键一步。传统的 LLM 像一本庞大但内容固定的百科全书，而 SEARCH-R 1 试图把它训练成一个懂得如何使用图书馆（搜索引擎）的研究员。\n论文开篇就指出了当前 LLM 面临的核心困境：知识截止日期（Knowledge Cutoff）导致信息过时，以及在面对需要外部知识的复杂推理任务时容易产生“幻觉”（Hallucination）。为了解决这个问题，业界主流方案有两种：\n检索增强生成（Retrieval-Augmented Generation, RAG）: 这是目前最流行的方法。它在处理用户问题时，首先根据问题检索相关文档，然后将这些文档作为上下文（Context）连同原始问题一起输入给 LLM，让其生成答案。这种方法的局限性在于，它通常是一次性的、前置的检索。对于需要多步推理的问题，比如“A 演员参演的、由 B 导演执导的电影是哪部？”，第一步检索可能只找到 A 演员的信息，无法一步到位。\n工具使用（Tool-Use）: 这类方法将搜索引擎看作一个“工具”，通过提示工程（Prompting）或监督微调（Supervised Fine-Tuning, SFT）来教模型调用。例如，ReAct 框架通过交错的“思考-行动-观察”循环来使用工具。但这些方法要么依赖于复杂的提示词设计，泛化能力有限；要么需要大量高质量的、人工标注的“决策轨迹”数据进行微调，成本极高且难以规模化。\nSEARCH-R 1 正是在这个背景下应运而生，它提出了一种更根本、更具扩展性的解决方案。其核心思想是：我们不直接教模型每一步怎么做，而是设定一个最终目标（答对问题），然后让模型在与环境（包括搜索引擎）的互动中自己学习如何达成这个目标。 这就是强化学习的精髓。\n该框架的具体工作流程非常巧妙，它定义了一套特殊的“语言”让模型与系统交互： - 当模型需要思考和规划时，它会生成 \u0026lt;think\u0026gt;...\u0026lt;/think\u0026gt;。 - 当它判断需要外部信息时，它会生成 \u0026lt;search\u0026gt;搜索查询词\u0026lt;/search\u0026gt;。 - 系统捕捉到这个指令后，会执行搜索，并将结果封装在 \u0026lt;information\u0026gt;...\u0026lt;/information\u0026gt; 标签中返回给模型。 - 模型接收到新信息后，可以继续思考、再次搜索，或最终给出答案 \u0026lt;answer\u0026gt;...\u0026lt;/answer\u0026gt;。\n这个过程是迭代和动态的。模型可以根据第一次搜索返回的信息，决定下一步是继续搜索（可能用新的关键词），还是已经信息充足，可以直接回答。例如在论文的案例研究（Table 9）中，为了回答“’Curious’香水的创始人出生在哪个城市？”，模型首先搜索“Curious fragrance information”，发现是布兰妮·斯皮尔斯（Britney Spears）创造的。然后，它生成了第二个搜索指令“Britney Spears birthplace”，找到了她出生在密西西比州的麦库姆（McComb, Mississippi）。这个多步查询和推理的过程，是传统 RAG 难以实现的。\n案例分析：R 1 vs. SEARCH-R 1 (Table 9) 问题: “Curious”是哪位歌手推出的女士香水，这位歌手出生在哪个城市和州？ R 1 (无搜索的 RL): \u0026lt;think\u0026gt; 这位歌手是碧昂丝，她出生在德州的休斯顿。\u0026lt;/think\u0026gt; \u0026lt;answer\u0026gt; 休斯顿 \u0026lt;/answer\u0026gt; (错误) SEARCH-R 1: 1. \u0026lt;think\u0026gt; 我需要找出”Curious”香水的创造者是谁。\u0026lt;/think\u0026gt; 2. \u0026lt;search\u0026gt; Curious fragrance information \u0026lt;/search\u0026gt; -\u0026gt; \u0026lt;information\u0026gt;… 是 Britney Spears 推出的… \u0026lt;/information\u0026gt; 3. \u0026lt;think\u0026gt; 我需要找出 Britney Spears 出生在哪个城市和州。\u0026lt;/think\u0026gt; 4. \u0026lt;search\u0026gt; Britney Spears birthplace \u0026lt;/search\u0026gt; -\u0026gt; \u0026lt;information\u0026gt;… 出生在 McComb, Mississippi… \u0026lt;/information\u0026gt; 5. \u0026lt;think\u0026gt; 我已经找到了答案。\u0026lt;/think\u0026gt; 6. \u0026lt;answer\u0026gt; McComb, Mississippi \u0026lt;/answer\u0026gt; (正确)\n为了让这个学习过程稳定有效，论文提出了一个关键的技术创新：检索内容损失掩码（Retrieved Token Loss Masking）。这是一个非常重要的细节。在 RL 训练中，模型生成的所有内容（token）都会计算损失，用于更新模型参数。但搜索返回的 \u0026lt;information\u0026gt; 内容是外部的、不可控的，我们不希望模型因为这些外部内容而受到“惩罚”或“奖励”，否则模型可能会学到一些奇怪的东西（比如试图去“修正”搜索结果）。因此，SEARCH-R 1 在计算损失时，会“屏蔽”掉所有从搜索引擎检索来的 token，只在模型自己生成的 \u0026lt;think\u0026gt;, \u0026lt;search\u0026gt;, \u0026lt;answer\u0026gt; 等部分计算损失。这确保了模型只专注于学习“如何思考和行动”，而不是去学习“知识内容本身”。实验证明，这个小小的改动带来了显著的性能提升，在 Qwen 2.5-7 B 模型上，使用掩码的版本平均分达到 0.431，而未使用时仅为 0.343（Table 4）。\n最终，论文通过在 7 个不同的问答数据集上的大量实验，证明了 SEARCH-R 1 的有效性。与强大的 RAG 基线相比，它在 Qwen 2.5-7 B 模型上实现了41%的平均相对性能提升，在 Qwen 2.5-3 B 模型上提升了20%（Table 2）。这些坚实的数据证明，通过强化学习教会 LLM 如何使用搜索引擎，是一条非常有前途的道路。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标: 核心目标是创建一个框架（SEARCH-R 1），利用强化学习（RL）来训练大型语言模型（LLM），使其能够自主、有效地与搜索引擎进行多轮交互，以解决需要外部、实时知识的复杂问题。 解决的实际问题: 知识过时: LLM 的知识库是静态的，在预训练后就已固定。SEARCH-R 1 通过实时搜索，赋予了模型获取最新信息的能力。 幻觉问题: 当 LLM 缺乏特定知识时，它倾向于编造答案。SEARCH-R 1 通过提供事实依据，显著减少了幻觉。 复杂推理: 许多现实世界的问题需要分解成多个步骤，并逐步收集信息才能解决。SEARCH-R 1 通过支持迭代式、多轮的“思考-搜索”循环，增强了模型解决这类问题的能力，这远超传统单次检索的 RAG。 行业意义: 推动 AI Agent 的发展: 这是构建更强大、更可靠的 AI 智能体（Agent）的关键一步。未来的智能体需要能够主动探索、收集信息并执行任务，而不是被动地回答问题。SEARCH-R 1 为训练这种主动探索能力提供了有效范式。 提升 LLM 应用的可靠性: 在金融、医疗、法律等对事实准确性要求极高的领域，这项技术可以大大提升 LLM 应用的可靠性和实用性，使其从“玩具”变为真正的“生产力工具”。 降低对 SFT 的依赖: 传统上，让模型学会复杂行为需要昂贵的监督微调（SFT）。SEARCH-R 1 证明，仅通过简单的“结果导向”奖励（答案是否正确），就能让模型学会复杂的“过程行为”（如何搜索和推理），这为降低训练成本、提升模型能力开辟了新路径。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路: 将搜索行为建模为 RL 问题: 论文的核心思路是将“与搜索引擎交互”这一过程，从一个简单的信息检索任务，重新定义为一个序列决策问题。模型在每一步都需要决策是继续思考、发起搜索还是给出答案。这个决策过程可以通过 RL 进行端到端的优化。 新方法/模型 (SEARCH-R 1 框架): 交错式推理与检索（Interleaved Reasoning and Retrieval）: 通过引入 \u0026lt;think\u0026gt;, \u0026lt;search\u0026gt;, \u0026lt;information\u0026gt;, \u0026lt;answer\u0026gt; 等特殊 token，创建了一个结构化的交互协议。这使得模型的推理过程变得透明且可控，并且能够灵活地在思考和信息收集之间切换。 结果导向的奖励机制（Outcome-based Reward）: 与需要标注完整行为轨迹的“过程监督”不同，SEARCH-R 1 采用了非常简单的奖励函数：只根据最终答案的正确性（如，精确匹配 Exact Match）来给予奖励。这种设计大大简化了训练过程，因为它不需要知道模型是如何得到答案的，只要结果对就行。 检索内容损失掩码（Retrieved Token Loss Masking）: 这是该方法的一个关键技术亮点。在 RL 训练更新模型时，它会忽略（mask out）所有从搜索引擎检索到的 \u0026lt;information\u0026gt; 部分产生的损失。 \u0026gt; We introduce loss masking for retrieved tokens, ensuring the policy gradient objective is computed only over LLM-generated tokens, excluding retrieved content from the optimization process. This approach stabilizes training while preserving the flexibility of search-augmented generation. 特点和优势: 动态性和自适应性: 与 RAG 的静态、一次性检索相比，SEARCH-R 1 是动态的。模型可以根据已获取的信息，自适应地调整后续的搜索策略，实现多步推理。 学习成本低: 与依赖昂贵人工标注轨迹的 SFT 方法（如 Toolformer）相比，SEARCH-R 1 仅需一个最终结果的标签（例如，问题的标准答案），大大降低了数据标注的门槛和成本。 更好的泛化能力: 因为模型是从与环境的交互中“学会”了推理和搜索的策略，而不仅仅是模仿标注数据，所以它在面对未见过的新问题时，有望展现出更好的泛化能力。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计: 模型: 实验覆盖了不同尺寸的 Qwen 2.5 模型（3 B, 7 B, 14 B），并同时在基础（Base）和指令微调（Instruct）版本上进行测试，以验证方法的普适性。 数据集: 选取了 7 个公开的 QA 数据集，分为两类： 通用问答: NQ, TriviaQA, PopQA。 多跳问答（需要多步推理）: HotpotQA, 2 WikiMultiHopQA, Musique, Bamboogle。选择这些数据集旨在全面评估模型在不同难度和类型的推理任务上的表现。 基线方法: 对比了多种有代表性的方法，包括： 不使用检索（Direct Inference, CoT）。 使用检索（RAG, IRCoT, Search-O 1）。 微调方法（SFT, 以及一个不带搜索功能的纯 RL 推理基线 R 1）。 评估指标: 使用精确匹配率（Exact Match, EM）作为评估最终答案正确性的核心指标。 实验数据和结果: 核心结论: SEARCH-R 1 在所有模型尺寸和数据集上都显著优于所有基线方法。 关键数据: 论文的 Table 2 是核心结果的展示。以 Qwen 2.5-7 B-Base 模型为例，我们可以看到： | 方法 | 平均 EM 得分 | | :— | :—: | | RAG (强基线) | 0.304 | | R 1-base (无搜索 RL) | 0.276 | | Search-R 1-base | 0.431 | 这表明，SEARCH-R 1 不仅远超 RAG，甚至比同样使用 RL 但不带搜索功能的 R 1 模型性能高出一大截，证明了“学会搜索”的巨大价值。 \u0026gt; Experiments on seven question-answering datasets show that SEARCH-R 1 improves performance by 41% (Qwen 2.5-7 B) and 20% (Qwen 2.5-3 B) over various RAG baselines under the same setting. 消融研究: 损失掩码的重要性: Table 4 的数据显示，在 7 B 模型上，使用损失掩码的 SEARCH-R 1 平均分为 0.431，而不使用则降至 0.343，证明了该设计的必要性。 PPO vs. GRPO: 论文比较了两种 RL 算法，发现 PPO 虽然收敛稍慢，但训练过程更稳定，最终效果也略好（Table 3），因此将其作为默认选择。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 更复杂的奖励模型: 目前的奖励函数（EM）过于简单，只适用于有标准答案的问答。对于开放性问题（如“写一份市场分析报告”），需要更复杂的、可能是由另一个强大 LLM 驱动的奖励模型来评估结果的质量。 检索与工具的协同: 未来可以从单一的“搜索”工具扩展到更广泛的工具集，如代码解释器、计算器、API 调用等。挑战在于如何让模型学会选择正确的工具，以及如何处理不同工具返回的异构信息。 检索质量的瓶颈: SEARCH-R 1 的性能上限受制于其使用的搜索引擎/检索器的质量。如果检索器本身找不到相关信息，模型再智能也无能为力。未来的研究可以探索模型与检索器的协同优化，甚至让模型学会生成更利于检索的查询（Query Rewriting）。 训练效率和成本: RL 训练的探索过程会消耗大量的计算资源。如何提升训练的样本效率，降低训练成本，是该方向走向大规模应用必须解决的挑战。 技术与投资机会: 自主 AI 智能体平台: 基于此类技术，可以开发出能够自主完成复杂任务（如自动进行市场调研、撰写代码、管理日程）的 AI 智能体平台，这是下一个蓝海市场。 企业级知识管理与问答系统: 将 SEARCH-R 1 的思想应用于企业内部知识库，可以打造出远超传统搜索的智能问答系统，极大提升员工获取和利用内部知识的效率。 AI 驱动的科学发现: 在科研领域，可以训练智能体阅读文献、分析数据、提出假设并设计实验，加速科学研究的进程。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？ 对检索器的依赖性过强: 论文将搜索引擎 R 视为一个给定的、性能稳定的“黑盒”。但现实中，检索结果的质量波动很大。实验没有充分讨论当检索器性能下降时，SEARCH-R 1 框架的鲁棒性如何。 奖励函数的局限性: 正如前面提到的，EM 奖励函数限制了该方法的应用场景。它无法处理没有唯一正确答案或答案形式更复杂的任务。这使得它目前更像一个“解题专家”，而非一个“通用问题解决者”。 泛化能力的验证不足: 实验虽然在多个数据集上验证了方法的有效性，但这些数据集仍然局限于“问答”这一大类。它在更广泛的任务类型上（如内容创作、摘要、对话）的泛化能力仍有待验证。 对失败案例的分析不够深入: 论文在附录中给出了一些失败案例（Case Study），例如 Table 11 和 Table 14，显示模型有时会被不相关的检索结果误导或无法分解复杂问题。但文中缺乏对这些失败模式的系统性归因分析，以及相应的改进策略。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？ 重点学习的创新想法: 过程学习的范式转换: 核心启发是，对于复杂的行为，我们可以通过奖励最终结果来让模型自己学会中间过程。不要一开始就试图设计完美的“过程指令”，而是定义好目标，让模型在探索中学习。这个思想可以应用于各种需要模型学习多步操作的任务。 “内外有别”的损失设计: 损失掩码的思想极具价值和普适性。当你的模型需要整合一个外部的、不可微的、或你不希望模型去“学习”的信息源时，就应该在计算损失时将这部分信息屏蔽掉。这保证了模型“术业有专攻”，只学习它该学的部分。 用特殊 Token 定义交互协议: 通过设计一套简洁的、机器可读的特殊 Token（如 \u0026lt;search\u0026gt;, \u0026lt;think\u0026gt;），可以清晰地界定模型的不同行为阶段，使得整个系统更加模块化和可解释。这在设计任何需要 LLM 与外部系统交互的 Agent 时都是一个非常实用的技巧。 需要补充的背景知识: 强化学习（RL）基础: 尤其是策略梯度（Policy Gradient）方法，建议了解 REINFORCE、A 2 C/A 3 C，以及本篇论文使用的PPO (Proximal Policy Optimization)。理解 Actor-Critic 架构会对理解 PPO 有很大帮助。 检索增强生成（RAG）: 深入理解标准 RAG 的工作原理及其变种，这样才能体会到 SEARCH-R 1 在动态性和多轮交互上的突破。 思维链（Chain-of-Thought）与工具使用框架: 了解如ReAct (Reason+Act) 和 Toolformer 等论文，它们是 SEARCH-R 1 所处领域的重要前序工作，有助于理解整个技术演进的脉络。 ","date":"2025-07-16T00:00:00Z","permalink":"/search-r1training-llms-to-reason-and-leverage-search-engines-with-reinforcement-learning/","title":"Search-R1：Training LLMs to Reason and Leverage Search Engines with Reinforcement Learning"},{"content":"论文深度解读 这篇于 2025 年 4 月发表的论文《WebThinker》(arXiv:2504.21776v1)，旨在解决大型推理模型（Large Reasoning Models, LRMs）在处理复杂、知识密集型任务时面临的一个根本性瓶颈：它们对内部静态知识的过度依赖。尽管像 OpenAI-o 1 和 DeepSeek-R 1 这样的模型展现了强大的长链推理能力，但一旦任务需要最新的、多样化的或领域外的知识，它们的能力就会受到极大限制。传统的检索增强生成（Retrieval-Augmented Generation, RAG）技术虽然能从外部引入知识，但其工作流通常是预定义的、浅层的，无法模拟人类研究员那种“深入探索、动态调整、边想边写”的复杂研究过程。\nWebThinker 的作者们敏锐地抓住了这一痛点，提出了一个全新的“深度研究代理”（Deep Research Agent）框架。其核心思想是，将大型推理模型（LRM）从一个被动的信息生成者，提升为一个能够自主规划、持续思考、深度探索、实时撰写的智能体。它不再是简单地“搜索-然后-回答”，而是实现了一个“思考-搜索-导航-整合-撰写”的动态闭环。\n为了实现这一目标，论文提出了两个关键的核心组件：\n深度网络探索器（Deep Web Explorer）：这不仅仅是一个简单的搜索引擎调用。它本身就是一个由 LRM 驱动的子流程，被赋予了两种关键能力：搜索（Search）和导航（Navigate）。当主推理流程遇到知识缺口时，它会调用这个探索器。探索器首先进行初步搜索，然后能像人一样，通过分析搜索结果，决定是否需要点击某个链接（Navigate）进入更深层次的网页，从中提取更精确、更深入的信息。这种递归式的探索能力，使得信息获取不再局限于搜索引擎返回的摘要，而是能够深入到网页内部，挖掘第一手资料。这极大地扩展了信息获取的深度和广度。\n自主“思考-搜索-撰写”策略（Autonomous Think-Search-and-Draft）：这是 WebThinker 区别于传统 RAG 的又一革命性设计。它将报告的撰写过程与信息搜集过程深度融合。模型不是在所有信息搜集完毕后才开始写作，而是可以实时地、交错地进行推理、搜索和撰写。比如，模型可以先根据已有信息撰写报告的“引言”部分，然后在撰写过程中发现需要更多关于“方法论”的数据，于是它会暂停写作，启动“深度网络探索器”去搜集新信息，获得新知后再回来继续或修改报告。为了支持这一策略，论文为模型配备了三个专用工具：T_draft（撰写章节）、T_check（检查报告结构）、T_edit（编辑报告）。这种设计使得最终生成的报告不是零散信息的堆砌，而是随着研究深入而有机生长、不断完善的连贯产物。\n为了让模型能够高效、准确地使用这些强大的工具，论文还引入了基于强化学习的训练策略——迭代式在线直接偏好优化（Iterative Online Direct Preference Optimization, DPO）。这是一个非常精妙的设计。DPO 是一种比传统 RLHF 更轻量、更高效的对齐技术，它通过构建“偏好对”数据（即一个好的行为轨迹比一个差的行为轨迹更好）来训练模型。WebThinker 的训练流程是： 1. 让当前模型针对一系列复杂任务生成多个不同的解决路径（轨迹）。 2. 根据预设的准则（如最终答案的正确性、工具使用的效率、思考过程的简洁性）自动构建偏好对 (Rw, Rl)，其中 Rw 是更优的轨迹。 3. 使用这些偏好对通过 DPO 损失函数来微调模型，使其更倾向于生成更优的轨迹。 4. 用微调后的新模型重复第一步，进行下一轮迭代。\n这种“自我对弈”、“持续进化”的在线训练模式，使得 WebThinker 能够不断优化其复杂的工具使用策略，学会在恰当的时机，以恰当的方式调用恰当的工具。\n在实验验证上，论文做得非常扎实。他们选取了横跨多个领域的极具挑战性的基准测试集，如考验专业科学知识的GPQA、评估通用 AI 能力的GAIA、专门测试网络遍历能力的WebWalkerQA，以及号称“人类最后考试”的超高难度推理任务HLE。实验结果令人印象深刻，以 32 B 模型为例，在 GAIA 任务上，WebThinker-32 B-RL 的平均分达到了48.5，显著高于标准迭代式 RAG 的35.0和之前的 SOTA 搜索代理 Search-o 1 的39.8（见 Table 1）。在 HLE 这个硬骨头上，WebThinker 的得分更是达到了15.8，而迭代 RAG 和 Search-o 1 分别只有9.6和10.8（见 Table 2）。这些数据强有力地证明了 WebThinker 框架在解决复杂问题上的优越性。此外，在科学报告生成任务（Glaive）上，WebThinker 同样取得了最高分，尤其在报告的“全面性”和“深入性”上表现突出（见 Figure 4），这直接验证了其“思考-搜索-撰写”策略的有效性。\n总而言之，WebThinker 不仅仅是一次技术上的增量改进，它更代表了一种范式上的转变——推动大模型从“博学的对话者”向“能干的研究助理”迈出了坚实的一步。\n接下来，我将按照您提出的六个问题，逐一进行详细解读。\n1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是创建一个通用、灵活、开源的深度研究框架，以克服大型推理模型（LRMs）在处理需要实时、深度、多样化外部知识的复杂任务时的内在局限。\n解决的实际问题：\n知识静态性问题：现有大模型的知识截止于其训练数据，无法获取最新的信息，也无法处理训练语料库之外的“长尾”知识。 传统 RAG 的浅层性问题：标准的 RAG 通常只能利用搜索引擎返回的表层摘要信息，无法像人类一样点击链接深入挖掘信息，导致在需要多跳推理和信息整合的任务上表现不佳。 研究流程割裂问题：传统 AI 工作流通常将“信息检索”和“内容生成”作为两个独立的步骤，无法模拟人类研究员那种思考、探索和写作交织进行的动态、迭代过程。 行业意义：\n提升 AI 能力上限：这个问题是当前限制 AI 从“通用问答”走向“专业问题解决”的关键瓶颈。解决它，意味着 AI 可以在科学研究、金融分析、市场调研、法律咨询等知识密集型行业中扮演更核心、更自主的角色。 降低专业研究门槛：一个强大的深度研究代理，可以极大地缩短研究人员在信息搜集和初步整合上花费的时间和精力，使他们能更专注于创新和决策，从而加速整个社会的知识生产和创新周期。 推动 Agentic AI 的发展：WebThinker 是智能体（Agent）技术在研究领域的一个完美范例。它的成功将激励更多研究者探索如何构建更强大、更自主的 AI 智能体，这对于通用人工智能（AGI）的最终实现至关重要。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 论文的核心创新在于其独特的框架设计和训练方法，相较于之前的方法，其特点和优势非常鲜明。\n正如论文在图 2 中所展示的，传统的 RAG（图 a）和迭代式 RAG（图 b）都是一种预定义的工作流，缺乏思考的深度和连贯性。而 WebThinker（图 c）将工具调用融入到一个连续的深度思考过程中，实现了端到端的自主执行。\n新的思路与模型： Hierarchical Agent Architecture（分层代理架构）：WebThinker 实际上是一个两层架构。 顶层：主推理 LRM。它负责整体任务的规划和编排（Orchestration），决定何时搜索、何时写作、何时修改。 底层：专用功能模块。包括由另一个 LRM 驱动的Deep Web Explorer和由一个“助手 LLM”执行的Report Drafting Tools。这种“指挥官-士兵”的模式，让主模型专注于高层逻辑，而将繁琐的细节（如网页内容摘要、具体文本撰写）外包出去，提高了系统的效率和鲁棒性。 Autonomous Think-Search-and-Draft Strategy（自主“思考-搜索-撰写”策略）：这是对传统“检索后生成”范式的颠覆。 优势：这种交错进行的方式使得信息搜集更具目的性（按需索取，而非一次性泛泛检索），也使得最终报告的逻辑更连贯（信息和论点同步发展，而非生硬拼接）。 细节：论文中提到，该策略通过 T_check 工具，允许模型随时检查已生成的报告大纲，这有助于它动态调整后续的研究和写作计划，体现了高度的“元认知”（Metacognition）能力。 Recursive Deep Web Explorer（递归式深度网络探索器）：这是实现“深度”研究的关键。 特点：它不仅能调用搜索引擎 API（返回摘要），还能通过 T_n（导航工具）和网页爬虫（Crawl 4 AI）来“点击”链接，获取并理解完整的网页内容。 优势：这使得 WebThinker 能够执行多跳信息检索。例如，在 GAIA 任务中，它需要先找到一个地点（Fred Howard Park），然后从另一个信息源中找到这个公园对应的邮政编码。这是传统 RAG 难以完成的。 与之前方法的对比优势总结： 特性 传统 RAG WebThinker 优势分析 工作流 预定义、线性 动态、自主、闭环 更灵活，能适应复杂多变的任务需求 信息深度 浅层（搜索摘要） 深层（可点击链接导航） 能获取更精确、更底层的证据 核心过程 检索与生成分离 思考、检索、撰写深度融合 过程更像人类专家，产出质量更高 训练范式 依赖 SFT 或简单 RL 迭代式在线 DPO 能持续自我优化复杂的工具使用策略 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了全面且严谨的实验来验证 WebThinker 的有效性。\n实验设计： 任务类型：实验覆盖两大类任务： 复杂问题解决（Complex Problem-Solving）：评估模型在需要多步推理和外部知识才能解决问题时的能力。 科学报告生成（Scientific Report Generation）：评估模型围绕一个开放式问题，综合信息并生成结构化、高质量报告的能力。 基准数据集：选取了多个公认的“地狱难度”数据集，确保实验的挑战性和说服力。 GPQA: 博士级别的科学问答。 GAIA: 通用 AI 助手基准，任务多样。 WebWalkerQA: 专门测试需要通过点击网页进行导航和信息提取的任务。 HLE (Humanity’s Last Exam): 跨学科的超高难度推理题。 Glaive: 用于评估开放式研究能力的通用问题集。 对比基线：设置了多层次的对比，非常有说服力。 Direct Reasoning: 不使用任何外部工具的纯模型推理。 RAG Workflow: 包括标准 RAG、带查询规划的 RAG 和迭代式 RAG。 Autonomous Search Systems: 其他先进的搜索代理系统，如 Search-o1 和一些闭源系统（OpenAI Deep Research, Gemini Deep Research 等）。 核心评估指标：对于问题解决任务，使用** Pass@1 **准确率；对于报告生成任务，使用由 DeepSeek-R1-671B 和 GPT-4o 作为裁判进行打分（全面性、深入性、事实性、连贯性）的平均分。 关键实验数据和结果： 在复杂问题解决上全面领先： \u0026gt; 如 Table 1 所示，在 GAIA (Avg) 上，WebThinker-32 B-RL 取得了 48.5 的高分，而 RAG-QwQ-32 B (w/ Iterative RAG) 仅为 35.0，提升了 38.6%。在专门考验导航能力的 WebWalkerQA (Avg) 上，WebThinker 更是达到了 46.5，相比 RAG-QwQ-32 B 的 31.5，提升了 47.6%。\n消融实验验证了各组件的必要性： \u0026gt; Table 3 的消融研究（Ablation Studies）揭示了框架成功的秘诀。移除深度网络探索器 (w/o Deep Web Explorer) 后，在 GAIA、WebWalkerQA 和 HLE 上的平均分从 45.4 暴跌至 38.3，证明了深度探索的价值。同样，仅去掉点击链接 (w/o Link Clicking) 的能力，分数也从 45.4 降至 42.6，说明导航功能不可或缺。移除RL 训练 (w/o Training (Base))，分数则降至 42.1，验证了 DPO 训练策略的有效性。\n在报告生成上达到 SOTA 水平： \u0026gt; Figure 4 (left) 显示，在 Glaive 任务上，WebThinker (Avg. 8.1) 的总分超过了包括 Gemini Deep Research (Avg. 7.9) 在内的所有基线方法。其报告的全面性（Completeness, 8.4）和深入性（Thoroughness, 8.4）得分尤其高，这直接得益于其独特的“思考-搜索-撰写”策略。\n4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文为我们打开了一扇通往更强大 AI 研究代理的大门，未来的探索空间巨大。\n值得探索的问题和挑战： 多模态深度研究：论文的未来展望也提到了这一点。当前的 WebThinker 主要处理文本信息。未来的研究代理需要能够理解和整合网页中的图片、图表、视频内容，甚至能分析交互式数据可视化。这是一个巨大的挑战，需要视觉语言模型（VLMs）和代理能力的深度融合。 工具的自主学习与扩展（Tool Learning）：WebThinker 的工具集是预定义的。一个更高级的代理应该能够自主发现和学习使用新的 API 或工具。例如，它能否学会在需要计算时调用 WolframAlpha，或在需要代码执行时使用代码解释器？ GUI 级别的人机交互：目前的网页交互还停留在解析 HTML 层面。真正的“像人一样”浏览网页，意味着需要理解和操作图形用户界面（GUI），例如填写表单、拖动滑块、与复杂的 JavaScript 应用交互。这需要模型具备类似 RPA（机器人流程自动化）的视觉代理能力。 鲁棒性与可控性：自主代理的一个巨大挑战是“失控”风险。如何确保代理在深度探索时不会陷入无关信息的“兔子洞”？如何设计有效的“刹车”机制和成本控制策略？这在工程和算法层面都极具挑战。 可能催生的新技术和投资机会： Agentic AI 平台：会出现更多专注于构建、训练和部署 AI 智能体的平台。这些平台将提供标准化的工具接口、环境模拟器、以及类似 WebThinker 中 DPO 的先进训练框架，成为 AI 应用开发的新基础设施。 垂直领域“AI 研究员”：将涌现大量针对特定行业的“AI 研究员”或“AI 分析师”初创公司。例如，专门用于新药研发信息挖掘的 AI、用于专利分析的 AI、用于金融市场异动归因的 AI。这些将是高价值的 B 2 B 服务。 下一代搜索引擎：传统的“链接列表”式搜索引擎可能会被“答案报告”式的新一代 AI 原生搜索引擎所颠覆。用户输入一个复杂问题，得到的不再是 10 个蓝色链接，而是一份由 AI 实时研究生成的综合性报告，类似 Perplexity AI 的强化版。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管 WebThinker 非常出色，但从批判的角度看，仍存在一些值得商榷和深入探讨的地方。\n存在的不足及缺失： 成本与可复现性：论文提到实验是在“4 个节点的 8 卡 NVIDIA H 100-80 GB GPU”上进行的。其核心的迭代式 DPO 训练过程，对于算力要求极高。这使得该方法对于大多数中小型研究机构和公司来说难以复现，限制了其广泛应用和验证。 对“助手 LLM”的依赖：WebThinker 的框架设计中，大量繁重的文本处理任务（如网页摘要、报告撰写）是由一个“助手 LLM”（如 Qwen 2.5-Instruct）完成的。这意味着 WebThinker 的最终性能高度依赖于这个助手模型的质量，而不仅仅是其核心的 WebThinker-LRM。论文对此的讨论不够深入，其性能提升有多少归功于框架，多少归功于强大的助手模型，界限不够清晰。 评估方法的局限性：在报告生成任务中，论文使用了 LLM-as-Judge（让大模型当裁判）的方法进行评估。这虽然是当前的主流做法，但其公正性和稳定性仍存疑。LLM 裁判可能存在偏见（例如，偏好更长、更复杂的句子），且其评分标准与人类专家的判断未必完全一致。缺乏小规模但高质量的人类专家评估，是一个小小的缺憾。 需要进一步验证和存疑之处： 长程任务的稳定性：论文展示的案例都很精彩，但在更长、更开放的研究任务中（例如，写一篇需要数天研究的综述），WebThinker 能否保持逻辑一致性、避免重复劳动和信息遗忘？其“记忆”（Document Memory M）机制是否足够强大来应对这种挑战，需要进一步验证。 对错误信息的处理能力：网络充满了错误和过时的信息。WebThinker 的框架中似乎缺乏一个明确的“事实核查”或“信息交叉验证”模块。它如何甄别和处理相互矛盾的网络信息？当 Deep Web Explorer 探索到一个低质量或虚假信息网站时，它会如何反应？这是实际应用中的一个关键问题。 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文是创新思想的宝库。以下是您可以重点学习和借鉴的几个方面：\n重点学习的创新想法： 超越 RAG，拥抱 Agent：最大的启发是，不要将思路局限在简单的“检索-生成”模式。应该思考如何将模型升级为一个能够与信息环境主动交互的智能体。赋予模型更多的“动作”（如点击、导航、检查、编辑），并让它自主决策，是通往更强大 AI 的关键。 任务分层与解耦：WebThinker 的分层代理架构非常值得借鉴。当您需要构建一个复杂的 AI 系统时，可以尝试将任务分解为“高层战略规划”和“底层具体执行”，并用不同的模型或模块来承担。这能让系统设计更清晰，也更容易扩展和维护。 动态融合工作流：“思考-搜索-撰写”策略的精髓在于打破线性流程，实现多任务的动态融合。在您的应用中，也可以思考是否能将原本串行的步骤（如数据分析、图表生成、文本报告撰写）变为一个交错进行的迭代过程，让 AI“边做边想，边想边改”。 用偏好数据精调复杂行为：当模型的行为涉及复杂的多步决策和工具使用时，简单的 SFT（监督微调）可能效果不佳。可以借鉴 WebThinker 的思路，定义一套偏好准则，生成高质量的偏好数据对，然后使用DPO等方法来对模型的复杂行为进行端到端的优化。 需要补充了解的背景知识： RAG（Retrieval-Augmented Generation）：您需要深入理解 RAG 的各种变体（如 Iterative RAG, Self-RAG 等），因为这是 WebThinker 所要超越的基线，理解它们才能体会 WebThinker 的创新之处。 Chain-of-Thought (CoT) 和相关推理技术：这是激发大模型推理能力的基础。WebThinker 的整个思考过程都是基于 CoT 的。 Reinforcement Learning from Human Feedback (RLHF) / Direct Preference Optimization (DPO)：这是当前对齐大模型行为（尤其是复杂行为）最核心的技术。您需要理解 DPO 的基本原理：它如何通过偏好对 (chosen, rejected) 来优化模型，以及为什么它比传统的 RLHF 更受欢迎。 AI Agent（智能体）概念和框架：了解智能体的基本定义（感知-决策-行动循环），以及一些流行的 Agent 框架（如 LangChain, AutoGPT 等），这能帮助您更好地理解 WebThinker 在整个 AI Agent 发展图景中的位置。 ","date":"2025-07-16T00:00:00Z","permalink":"/webthinkerempowering-large-reasoning-models-with-deep-research-capability/","title":"WebThinker：Empowering Large Reasoning Models with Deep Research Capability"},{"content":"DAPO 是对 GRPO 的改进。DAPO（Decoupled Clip and Dynamic sAmpling Policy Optimization，即解耦裁剪和动态采样策略优化）的优化点有四个（其中前 2 个是主要亮点，是命名的来源）\nimage.png 更高裁剪 clip的上边界可以放宽一些，即 clip_high 从 0.2 提高到了 0.28。\n传统的PPO算法中，上限裁剪（upper clip）会限制策略的探索能力。这个问题在低概率的”探索型”token上特别明显，因为这些token的概率提升会被限制在一个严格的范围内。这导致了”熵坍塌”现象，也就是说策略的信息熵下降，采样的响应趋于几个相同的结果，限制了探索。DAPO通过调整上限ehigh裁剪范围来解决这个问题，特别是通过提高ehigh的值，为低概率token的概率提升留出更多空间。实验证明这种方法有效增强了策略的探索性，成功解决了熵坍塌问题。 动态采样（Dynamic Sampling） 在现有的RL算法中，如果某些提示的准确率达到1时，会遇到梯度下降的问题。比如在GRPO中，如果一个特定提示的所有输出都是正确的并获得相同的奖励，那么这组数据的优势（advantage）就会变为零。这种情况会导致策略梯度为零，缩小了批次梯度的幅度并增加了噪声敏感性，降低了采样效率。随着训练的进行，准确率为1的样本数量不断增加，这意味着每个批次中有效提示的数量不断减少，导致梯度方差增大并削弱了模型训练的梯度信号。同样地，准确率为0的样本也会导致零梯度问题。DAPO的解决方案是在训练前，采样并过滤准确率为1和0的提示，确保训练批次中有充满准确率既非0也非1的样本。这样可以确保批次中所有的提示都带有有效的梯度信号，保持了批次中有效提示数量的一致性，从而提高了训练效率和稳定性。\nimage.png image.png Token 级策略梯度损失（Token-Level Policy Gradient Loss） 这种策略可以使得response length稳步增长，而不是波动大。\nimage.png image.png 超长奖励塑造（Overlong Reward Shaping） image.png image.png 常见问题 为什么clip-higher可以缓解熵坍塌？ image.png 参考 # DAPO全是已有的小trick，为什么这么火? # 漫谈类PPO系列强化学习算法：GRPO、DAPO、GSPO\n","date":"2025-07-15T00:00:00Z","permalink":"/dapo/","title":"dapo"},{"content":"利用熵作为分支点，相当于prm。想法令人眼前一亮\nimage.png 论文深度解读：在不确定性中寻找确定性的强化学习新范式 在大语言模型（LLM）能力飞速发展的今天，如何让模型像人一样进行复杂、多步骤的推理（如解决奥数题、编写复杂代码），已成为前沿研究的焦点。传统的监督微调（SFT）让模型学会了“模仿”，但要让模型真正“理解”并进行创造性、探索性的推理，强化学习（Reinforcement Learning, RL）被寄予厚望。然而，将 RL 应用于 LLM 的广阔“思想空间”中，如同在没有地图的汪洋中航行，充满了挑战。\n这篇论文的核心贡献，在于提出了一种名为 FR 3 E (First Return, Entropy-Eliciting Explore) 的新型强化学习框架。它为 LLM 这艘大船在推理的汪洋中，提供了一套精巧的“声纳探测 + 局部精细探索”的导航系统，旨在解决一个根本性难题：稀疏奖励下的信用分配（Credit Assignment）。\n想象一下，让模型解答一道复杂的数学题。它需要写下一连串的解题步骤，但只有最终答案的正确与否才能给出一个明确的奖励信号（reward=1 或 0）。这种奖励是稀疏（sparse）且延迟（delayed）的。如果最终答案错误，我们很难知道是哪一个推理步骤出了问题。传统方法，如论文中对比的GRPO (Group Relative Policy Optimization)，倾向于将最终的成败“平均”分摊到每一步，这显然是不合理的——可能仅仅是一个关键步骤的失误，导致了整个推理链的失败。而另一些方法，如PPO (Proximal Policy Optimization)，虽然试图通过一个“评论家”（Critic）网络来评估每一步的价值，但在 LLM 巨大的、离散的状态空间中，训练一个稳定而准确的评论家本身就极其困难，常常导致训练不稳定。\nFR 3 E 的巧妙之处在于，它绕过了训练“评论家”的难题，而是直接从模型自身的行为中寻找探索的线索。它的哲学可以概括为：“与其在整片大海中盲目试探，不如先找到最可能出现风暴（或宝藏）的‘价值航区’，然后集中力量在该区域进行深入探索。”\n这个过程被分为两个核心阶段：\n第一阶段：First Return - 寻找高不确定性的关键决策点。FR 3 E 首先让模型生成一个完整的推理路径（称为“基础轨迹”）。然后，它并不急于评价这条路的好坏，而是用一个非常巧妙的指标——信息熵（Entropy）——来分析模型在生成这条路径时每一步的“犹豫程度”。 \u0026gt; 通俗解释“熵”：在信息论中，熵衡量的是不确定性。当模型在生成下一个词时，如果对多个候选词的概率分布非常均匀（比如“A”有 30%可能，“B”有 28%可能，“C”有 25%可能），说明模型非常“纠结”，此时的熵就很高。反之，如果模型非常确定下一个词就是“A”（99%的可能），那么熵就很低。\nFR 3 E 认为，这些高熵的节点，正是模型推理的“岔路口”，是思维最不确定的地方，也是最值得探索的“关键决策点”。论文通过计算每个生成词元（token）的熵，并选择熵值最高的 Top-K 个点作为“锚点”。\n第二阶段：Entropy-Eliciting Explore - 从锚点出发进行多样化探索。找到这些“岔路口”后，FR 3 E 会从每一个锚点开始，生成多个不同的后续推理路径（称为“部分部署”，partial rollouts）。这就像是在一个关键决策后，尝试所有可能的平行宇宙。通过统计这些从同一“岔路口”出发的路径最终成功的比例，FR 3 E 能够非常有效地估计出这个“岔路口”决策的经验价值 V (Sj)。这种方式比从头开始生成完整路径要高效得多，并且提供了宝贵的、有语义基础的中间反馈。\n更进一步，FR 3 E 还设计了自适应优势调制（Adaptive Advantage Modulation）机制。这是一个动态的反馈控制器，当模型在一个方向上的探索取得了进展（V (Sj) \u0026gt; V (Sj-1)），它会适度“调低”学习信号，防止模型过早地陷入局部最优，鼓励继续探索；而当探索停滞不前时（V (Sj) ≤ V (Sj-1)），它会“放大”学习信号，促使模型更积极地摆脱困境。\n论文的实验部分极其扎实，通过在 Qwen 2.5 系列模型上与 GRPO++（一个集成了多种优化技巧的强大基线）的对比，有力地证明了 FR 3 E 的有效性。例如，在极具挑战性的AIME 24数学竞赛基准测试中，对于 Qwen 2.5-32 B 模型，FR 3 E 取得了40.2%的准确率，显著优于 GRPO++的34.1%（见论文 Table 1）。\n更有说服力的是对训练过程的深入分析。Figure 7 和 Figure 8 的图表显示，随着训练的进行，FR 3 E 能显著增加“完全正确”（All-Right）的轨迹数量，同时抑制“完全错误”（All-Wrong）的轨迹。这表明 FR 3 E 不仅仅是提升了平均分，更是教会了模型如何生成更稳定、更可靠的正确推理路径。\n总而言之，FR 3 E 通过“熵”这一内生信号来识别不确定性，并以此为基点进行结构化探索，为解决 LLM 强化学习中的信用分配难题提供了一个优雅、高效且无需外部评论家的创新方案。它推动了 LLM 从“模仿者”向“思考者”的转变，为未来构建更强大、更可靠的 AI 推理系统铺平了道路。\n### 1. 论文的研究目标是什么？想要解决什么实际问题？这个问题对于行业发展有什么重要意义？ 研究目标：本文的核心研究目标是为基于强化学习的大语言模型（LLM）优化过程，设计一个更稳定、更高效的结构化探索（structured exploration）框架，以提升其在复杂推理任务（如数学解题）上的能力。\n解决的实际问题：\n信用分配难题（Credit Assignment Problem）：在多步骤的推理任务中，奖励信号往往是稀疏和延迟的（只有最终结果才有反馈）。当推理失败时，很难确定是哪一步的决策出了问题。这篇论文旨在解决如何将最终的成败功过，精确地归因到中间的关键步骤上。 \u0026gt; 正如论文引言中所述：“A central challenge in these RLVR methodologies is the granular assignment of credit to intermediate steps within a reasoning trajectory.” 探索不稳定性（Unstable Exploration）：传统的 RL 方法要么因为依赖不稳定的价值网络（如 PPO 中的 Critic）而难以训练，要么因为探索方式过于简单（如 GRPO）而效率低下，导致模型训练过程波动大，甚至出现“熵崩溃”（entropy collapse），即模型变得过于自信，丧失了探索多样性的能力。 行业意义：\n推动 AI 能力边界：解决这个问题，意味着我们可以更有效地训练 LLM 解决需要长链条、复杂逻辑推理的任务。这将直接提升 AI 在科学研究、软件工程、金融分析、法律咨询等专业领域的应用价值，使其从一个“知识问答机”进化为“问题解决伙伴”。 提升 AI 的可靠性与可解释性：通过 FR 3 E 这样的方法，我们不仅能提升模型的平均表现，还能使其生成更连贯、更稳定的推理过程。这有助于减少模型“胡说八道”的现象，并为我们理解模型的“思考”过程提供了新的窗口。 降低训练成本和门槛：FR 3 E 是一个无价值模型（value-model-free）的方法，它绕过了训练复杂 Critic 网络的需要，这在理论上可以降低对计算资源的要求和训练的复杂度，使得更多机构有能力去进行 LLM 的强化学习优化。 ### 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 核心新方法：论文提出了FR 3 E (First Return, Entropy-Eliciting Explore) 框架。其核心思路是“基于不确定性的结构化探索”。\n与之前方法的对比及优势：\n特性 PPO (Proximal Policy Optimization) GRPO (Group Relative Policy Optimization) FR 3 E (本文方法) 反馈粒度 词元级别（Token-level） 轨迹级别（Trajectory-level） 语义块级别（Semantic-block level） 核心机制 依赖一个需要额外训练的 Critic 网络来估计每一步的价值 比较多条完整轨迹的最终奖励，进行策略优化 通过计算熵识别不确定点，并从这些点进行局部探索 主要优势 反馈精细 实现简单，不依赖 Critic 无需 Critic，反馈信号比 GRPO 精细，探索效率高，训练更稳定 主要劣势 Critic 网络在 LLM 中训练不稳定，计算开销大 信用分配不精确，将奖励平均化 计算开销高于 GRPO，引入了新的超参数 FR 3 E 的具体创新点分析： 用熵作为内在不确定性信号：这是 FR 3 E 的基石。它不依赖外部的奖励模型或价值网络，而是利用模型自身在生成过程中固有的“犹豫”（高熵）来定位探索的起点。这是一种非常聪明的自监督信号。 \u0026gt; 论文在 4.1.2 节中定义了熵的计算方式： \u0026gt; \\[ H_k = - \\sum_{v \\in V} \\pi_{\\theta}(v | q, t_{\u0026lt;k}) \\log \\pi_{\\theta}(v | q, t_{\u0026lt;k}) \\] 两阶段探索范式： First Return：这个阶段的核心是“识别”，即通过生成基础轨迹并计算熵，识别出最有价值的探索“锚点”。 Entropy-Eliciting Explore：这个阶段的核心是“探索”，即从锚点出发进行高效的局部探索，以获得对中间状态价值的准确估计。这种“先定位，再深挖”的模式，远比全局性的随机探索或从头开始的完整轨迹探索要高效。 自适应优势调制：这是一个精巧的动态反馈机制。它通过一个调制因子 aj 来动态调整学习信号的强度，实现了类似 PID 控制器的效果，从而稳定了学习过程，平衡了探索与利用（exploration-exploitation trade-off）。 \u0026gt; 论文在 4.3 节给出了 aj 的定义，它与相邻状态价值的差值 V(Sj) - V(Sj-1) 成反比，这个设计非常关键。 ### 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 论文设计了一系列严谨的实验来全方位验证 FR 3 E 的有效性。\n实验设计： 基线模型（Baseline）：选择了一个强大的基线 GRPO++，它是在香草 GRPO 基础上集成了拒绝采样（rejection sampling）和 Clip-Higher 等先进技术的版本，确保了对比的公平性和挑战性。 基础大模型（Backbone Models）：实验覆盖了不同尺寸和特性的模型，包括 Qwen 2.5-7 B（通用模型）、Qwen 2.5-Math-7 B（数学专用模型）和 Qwen 2.5-32 B（更大规模的通用模型），以检验方法的普适性和可扩展性。 评测基准（Benchmarks）：使用了多个主流的数学推理评测集，如 AIME 24, GSM 8 K, Math 500, Minerva Math 等，全面评估模型的推理能力。 分析维度：除了最终的准确率，论文还深入分析了训练过程中的多个关键指标，如熵损失、平均优势值、响应长度、All-Right/All-Wrong 轨迹数量等，从不同侧面揭示 FR 3 E 的工作机理。 关键实验数据与结果： 最终性能优越：如上文 Table 1 所示，在绝大多数评测集上，FR 3 E 的表现都优于或持平于强大的 GRPO++基线。尤其在最具挑战性的 AIME 24 和最大规模的 Qwen 2.5-32 B 模型上，优势最为明显（40.2% vs 34.1%），证明了 FR 3 E 对于提升高难度任务和大型模型性能上限的有效性。 更健康的探索行为：Figure 3 的熵损失曲线显示，FR 3 E 能在训练中维持更高的熵水平，尤其是在 Qwen 2.5-Math-7 B 上，后期熵值甚至出现回升。这表明 FR 3 E 能有效避免“熵崩溃”，保持更持久的探索能力。 \u0026gt; 论文指出：“FR 3 E maintains slightly higher entropy levels compared to GRPO++, suggesting a more balanced trade-off between exploration and exploitation.” 更稳定的学习过程：Figure 5 的优势比较图显示，FR 3 E 产生的优势值（Advantage）波动更小，且更紧密地围绕 0 值波动。这印证了其理论预期，即 FR 3 E 的策略更新过程中的分布漂移更小，学习过程更稳定。 更高质量的推理轨迹：Figure 7 和 8 的结果是本论文的一大亮点，清晰地展示了 FR 3 E 训练的模型生成的“All-Right”轨迹持续增加，而“All-Wrong”轨迹持续减少，且两者差距不断拉大。这说明 FR 3 E 学到的是一种“通往正确答案的可靠路径”，而非零散的、偶然的成功。 ### 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会？ 值得探索的问题和挑战： 不确定性的更优度量：token 级别的熵是衡量不确定性的一个有效代理，但可能不是最优的。未来的研究可以探索更复杂的度量方式，例如： 语义层面的不确定性：模型可能对某个词的 token 形式不确定，但对它要表达的语义是确定的。如何度量语义层面的不确定性？ 模型集成不一致性（Ensemble Disagreement）：通过多个模型或同个模型多次 dropout 后的输出差异来衡量不确定性，这可能比单一模型的熵更鲁棒。 探索与人类反馈的结合：FR 3 E 是一种纯粹基于模型自反馈的探索。如何将其与RLAIF (RL from AI Feedback) 或 RLHF (RL from Human Feedback) 相结合？例如，在 FR 3 E 识别出的高熵点，可以主动向 AI 或人类标注者请求反馈，从而将探索效率和质量提升到新的高度。 跨领域泛化能力：论文的实验集中在数学推理。FR 3 E 在其他需要长链推理的领域，如代码生成与调试、科学文献综述、法律案件分析等，其效果如何？这些领域可能需要对“状态”和“奖励”进行不同的定义。 计算效率的极致优化：尽管 FR 3 E 已经比某些方法高效，但对于大规模应用而言，其计算成本仍然是一个考量。能否开发自适应探索策略，例如，只对最关键的少数几个锚点进行深入探索，或者动态调整探索的“深度”和“广度”？ 新技术和投资机会： AI Agent（智能体）的进化：这类研究是构建下一代 AI Agent 的核心技术。能够进行可靠推理和自主探索的 Agent，将在自动化科学实验、无人驾驶决策、个性化教育、智能投顾等领域催生革命性的产品。 “AI 研究员”和“AI 程序员”：基于这类技术的工具，可以辅助人类研究员进行文献分析、提出假说、设计实验；也可以辅助程序员编写、调试和优化复杂的软件系统。这将是一个巨大的 B 2 B 市场。 可信 AI（Trustworthy AI）平台：FR 3 E 等方法提升了模型的稳定性和推理过程的连贯性。围绕这些技术可以构建可信 AI 解决方案，为金融、医疗、法律等高风险行业提供更可靠、更易于审计的 AI 服务。相关的投资机会在于为这些行业提供定制化的、经过严格验证的 AI 系统。 ### 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足与缺失： 超参数敏感性分析不足：FR 3 E 引入了几个关键超参数，如选择锚点的数量 K，以及每个锚点探索的次数 M。论文中并未详细讨论这些超参数的选择依据，也没有提供充分的消融实验（Ablation Study）来分析模型性能对这些超参数的敏感度。这使得复现和推广该方法存在一定的不确定性。 计算成本量化不清晰：论文提到 FR 3 E 的计算足迹与 GRPO++不同，但没有给出定量的比较。在实际应用中，性能的提升是否值得额外的计算开销，是一个需要权衡的关键问题。一个详细的“性能-成本”分析图表会让论文的结论更有说服力。 对已精通知识的潜在干扰：实验结果显示，FR 3 E 在已经经过数学能力优化的 Qwen 2.5-Math-7 B 模型上提升有限。 \u0026gt; 论文也坦诚：“applying standard RL strategies to a math-specialized model may not yield significant improvements, possibly due to interference with its fine-tuned knowledge.” 这提出了一个深刻的问题：对于一个已经在特定领域很强的模型，如何进行“查漏补缺”式的强化学习，而不是“推倒重来”，以免破坏其已有的知识结构？ 奖励函数的局限性：实验中使用的奖励是二元的（完全正确=1，否则=0）。对于许多复杂问题，部分正确的解法也包含有价值的信息。更精细的、非二元的奖励函数（例如，基于步骤正确性的过程奖励）可能会与 FR 3 E 框架结合，产生更好的效果。 ### 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识？ 重点学习与启发： 不确定性驱动的思维范式：最核心的启发是“将不确定性视为一种宝贵的资源，而非一个问题”。在任何需要探索和创新的任务中，识别出系统中最不确定的环节，并集中资源去攻克它，往往是最高效的策略。这个思想可以应用到产品设计、科学研究、甚至个人学习中。 利用模型的内生信号：在缺少外部监督信号时，要善于从系统内部挖掘可用的反馈信号。本文中的“熵”就是一个绝佳的例子。在你的工作中，也可以思考：模型或系统自身还产生了哪些可以被利用的、有价值的副产品信息？ 分解与结构化：FR 3 E 将复杂的端到端 RL 问题分解为“识别关键点”和“局部探索”两个更简单、更可控的子问题。这种“分而治之”的思想是解决复杂系统问题的普适方法。 动态反馈与自适应：自适应优势调制机制告诉我们，一个优秀的系统应该具备动态调整的能力。它应该能根据当前的状态（是顺境还是逆境）来调整自己的行为策略（是保守还是激进）。 需要补充的背景知识： 强化学习基础： 马尔可夫决策过程 (MDPs)：理解 State, Action, Reward, Policy 这些基本概念。 策略梯度方法 (Policy Gradient)：特别是 REINFORCE 算法，这是 GRPO 的基础。 Actor-Critic 方法：特别是PPO (Proximal Policy Optimization)，了解其工作原理，尤其是 Advantage Function 和 Clipping 机制，这有助于你理解 FR 3 E 中 Clip-Higher 和优势调制的动机。 信息论基础： 香农熵 (Shannon Entropy)：深入理解熵的数学定义及其作为不确定性度量的物理意义。 大语言模型训练： 了解自回归（Autoregressive）生成模型的基本原理。 熟悉RLHF (Reinforcement Learning from Human Feedback) 的一般流程，这是当前 LLM 对齐的主流方法，也是本文工作的宏观背景。 希望这份详尽的解读能够帮助您深入理解这篇优秀的论文，并从中获得宝贵的启发。\n","date":"2025-07-15T00:00:00Z","permalink":"/first-return-entropy-eliciting-explore/","title":"First Return, Entropy-Eliciting Explore"},{"content":"Safe softmax 并没有 1-pass 算法，那么 Attention 会不会有呢？有！这就是 FlashAttention！\n在使用 online attention 的情况下，从头开始计算 attention score 的过程如下： \\(\\operatorname{NOTATIONS}\\)\n\\(Q[k,:]:\\) the \\(k\\) -th row vector of \\(Q\\) matrix. \\(\\begin{aligned}O[k,:]:\\mathrm{~the~}k\\text{-th row of output }O\\mathrm{~matrix.}\\\\\\mathbf{V}[i,i]:\\mathrm{~the~}k\\text{-th row of output }O\\mathrm{~matrix.}\\end{aligned}\\) \\(V[i,:]{:\\text{ the }i\\text{-th row of }V\\text{ matrix}}.\\) \\(\\{\\boldsymbol{o}_i\\}{:}\\sum_{j=1}^ia_jV[j,:]\\), a row vector storing partial aggregation result \\(A[k,:i]\\times V[:i,:]\\) BODY\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[\\begin{aligned}x_i\u0026amp;\\leftarrow\\quad Q[k,:]\\:K^T[:,i]\\\\m_i\u0026amp;\\leftarrow\\quad\\max\\left(m_{i-1},x_i\\right)\\\\d_i\u0026#39;\u0026amp;\\leftarrow\\quad d_{i-1}\u0026#39;e^{m_{i-1}-m_i}+e^{x_i-m_i}\\end{aligned}\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[\\begin{aligned}\u0026amp;a_i\\:\\leftarrow\\:\\frac{e^{x_i-m_N}}{d_N^{\\prime}}\\\\\u0026amp;o_i\\:\\leftarrow\\:o_{i-1}+a_i\\:V[i,:\\:]\\end{aligned}\\] \\(\\mathbf{end}\\) \\[O[k,:]\\leftarrow\\boldsymbol{o}_N\\]\n优化思路和 online attention 一样，将 \\(o_{i}\\) 的计算简化以便于可以写成迭代式。\n原来的 \\(o_{i}\\) 使用以下方式计算，依赖于全局的 \\(m_{N}\\) 和 \\(d_{N}\\)。 \\[\\boldsymbol{o}_i:=\\sum_{j=1}^i\\left(\\frac{e^{x_j-m_N}}{d_N^{\\prime}}V[j,:]\\right)\\] 将其改写成如下形式： \\[\\boldsymbol{o}_i^{\\prime}:=\\left(\\sum_{j=1}^i\\frac{e^{x_j-m_i}}{d_i^{\\prime}}V[j,:]\\right)\\] 这样按照上面的方式拓展下去，可以找到一个循环迭代式。\n\\[\\begin{aligned} \\mathbf{o}_i^{\\prime}\u0026amp; =\\sum_{j=1}^i\\frac{e^{x_j-m_i}}{d\u0026#39;}V[j,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_i}}{d_i^{\\prime}}V[j,:] \\right)+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_{i-1}}}{d_{i-1}^{\\prime}}\\frac{e^{x_j-m_i}}{e^{x_j-m_{i-1}}}\\frac{d_{i-1}^{\\prime}}{d_i^{\\prime}}V[j,:]\\right)+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_{i-1}}}{d_{i-1}^{\\prime}}V[j,.]\\right)\\frac{d_{i-1}^{\\prime}}{d_i^{\\prime}}e^{m_{i-1}-m_i}+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,.] \\\\ \u0026amp;= \\boldsymbol{o}_{i-1}\u0026#39;\\frac{d_{i-1}\u0026#39;e^{m_{i-1}-m_i}}{d_i\u0026#39;}+\\frac{e^{x_i-m_i}}{d_i\u0026#39;}V[i,:] \\end{aligned}\\]\n这样就找到了 \\(o_{i}\\) 的递推表达式。\n之后对 Q, K 进行 tiling 后计算，得到如下： \\[\\begin{aligned}\u0026amp;\\textbf{for }i\\leftarrow1,\\#\\text{tiles do}\\\\\u0026amp;\u0026amp;\u0026amp;\\boldsymbol{x}_i\\quad\\leftarrow\\quad Q[k;\\cdot] K^T[\\cdot,(i-1) b; i b]\\\\\u0026amp;\u0026amp;\u0026amp;m_i^{(\\mathrm{local})}=\\begin{array}{c}\\overset{b}{\\operatorname*{max}}\\left(\\boldsymbol{x}_i[j]\\right)\\\\\\end{array}\\\\\u0026amp;\u0026amp;\u0026amp;m_i \\leftarrow \\max\\left(m_{i-1},m_i^{(\\mathrm{local})}\\right)\\\\\u0026amp;\u0026amp;\u0026amp;a_i^{\\prime} \\leftarrow d_{i-1}^{\\prime}e^{m_{i-1}-m_i}+\\sum_{j=1}^be^{\\boldsymbol{x}_i[j]-m_i}\\\\\u0026amp;\u0026amp;\u0026amp;\\boldsymbol{o}_i^{\\prime} \\leftarrow \\boldsymbol{o}_{i-1}^{\\prime}\\frac{d_{i-1}^{\\prime}e^{m_{i-1}-m_i}}{d_i^{\\prime}}+\\sum_{j=1}^b\\frac{e^{\\boldsymbol{x}_i[j]-m_i}}{d_i^{\\prime}}V[(i-1) b+j,:]\\\\\u0026amp;\\text{end}\\\\\u0026amp;\u0026amp;\u0026amp;O[k,:]\\leftarrow\\boldsymbol{o}_{N/b}^{\\prime}\\end{aligned}\\] 对于 tiles，示意图如下： 可以理解成滑动窗口，\\(K^{T}\\) 从左向右滑动（按列读取），\\(V\\) 从上向下滑动（按行读取）。也可以直接理解成分块矩阵，具体为什么这么做，参考：Cuda 编程之 Tiling - 知乎 (zhihu.com)\n参考 From Online Softmax to FlashAttention. )\n","date":"2025-07-15T00:00:00Z","permalink":"/flash-attention/","title":"flash attention"},{"content":" image.png 如上图所示，GQA 就是在 MHA 和 MQA 之间做了一个平衡。对 query heads 进行分组，分成几组就对应多少个 kv heads，然后每一组内的 query Heads 共享相同的 KV head。 GQA 可以在减少计算量和 KV Cache 同时确保模型效果不受到大的影响。\n现在基本都使用 GQA，代码如下（核心是 repeat_kv 函数）：\n```python def repeat_kv(x: torch.Tensor, n_rep: int) -\u0026gt; torch.Tensor: “““torch.repeat_interleave(x, dim=2, repeats=n_rep)”“” bs, slen, n_kv_heads, head_dim = x.shape if n_rep == 1: return x return ( x[:, :, :, None, :] .expand(bs, slen, n_kv_heads, n_rep, head_dim) .reshape(bs, slen, n_kv_heads * n_rep, head_dim) )\nclass Attention(nn.Module): “““Multi-head attention module.”“”\ndef __init__(self, args: ModelArgs): \u0026quot;\u0026quot;\u0026quot; Initialize the Attention module. Args: args (ModelArgs): Model configuration parameters. Attributes: n_kv_heads (int): Number of key and value heads. n_local_heads (int): Number of local query heads. n_local_kv_heads (int): Number of local key and value heads. n_rep (int): Number of repetitions for local heads. head_dim (int): Dimension size of each attention head. wq (ColumnParallelLinear): Linear transformation for queries. wk (ColumnParallelLinear): Linear transformation for keys. wv (ColumnParallelLinear): Linear transformation for values. wo (RowParallelLinear): Linear transformation for output. cache_k (torch.Tensor): Cached keys for attention. cache_v (torch.Tensor): Cached values for attention. \u0026quot;\u0026quot;\u0026quot; super().__init__() self.n_kv_heads = args.n_heads if args.n_kv_heads is None else args.n_kv_heads model_parallel_size = fs_init.get_model_parallel_world_size() self.n_local_heads = args.n_heads // model_parallel_size self.n_local_kv_heads = self.n_kv_heads // model_parallel_size self.n_rep = self.n_local_heads // self.n_local_kv_heads self.head_dim = args.dim // args.n_heads self.wq = ColumnParallelLinear( args.dim, args.n_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wk = ColumnParallelLinear( args.dim, self.n_kv_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wv = ColumnParallelLinear( args.dim, self.n_kv_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wo = RowParallelLinear( args.n_heads * self.head_dim, args.dim, bias=False, input_is_parallel=True, init_method=lambda x: x, ) # kv_cache是缓存键值对，在训练过程中，我们只保存最近n个键值对 self.cache_k = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() self.cache_v = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() def forward( self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor], ): \u0026quot;\u0026quot;\u0026quot; Forward pass of the attention module. Args: x (torch.Tensor): Input tensor. start_pos (int): Starting position for caching. freqs_cis (torch.Tensor): Precomputed frequency tensor. mask (torch.Tensor, optional): Attention mask tensor. Returns: torch.Tensor: Output tensor after attention. \u0026quot;\u0026quot;\u0026quot; # 假设当前x为(1, 1, dim)，也就是上一个预测的token # self-attention的输入，标准的(bs, seqlen, hidden_dim) bsz, seqlen, _ = x.shape # 计算当前token的qkv # q k v分别进行映射，注意这里key, value也需要先由输入进行映射再和kv_cache里面的key, value进行拼接 xq, xk, xv = self.wq(x), self.wk(x), self.wv(x) xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim) xk = xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) xv = xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) # 对当前输入的query和key进行RoPE，注意kv_cache里面的key已经做过了RoPE xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis) # 缓存当前token的kv self.cache_k = self.cache_k.to(xq) self.cache_v = self.cache_v.to(xq) self.cache_k[:bsz, start_pos: start_pos + seqlen] = xk self.cache_v[:bsz, start_pos: start_pos + seqlen] = xv # 取出前seqlen个token的kv缓存 # 取出全部缓存的key和value（包括之前在cache里面的和本次输入的），作为最终的key和value keys = self.cache_k[:bsz, : start_pos + seqlen] values = self.cache_v[:bsz, : start_pos + seqlen] # 将kv重复填充，使kv和q的头数个数相同 # repeat k/v heads if n_kv_heads \u0026lt; n_heads，对齐头的数量 keys = repeat_kv(keys, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) values = repeat_kv(values, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) # 计算当前token的attention score，，注意mask需要加上，另外维度要对应上 xq = xq.transpose(1, 2) # (bs, n_local_heads, seqlen, head_dim) keys = keys.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) values = values.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim) if mask is not None: scores = scores + mask # (bs, n_local_heads, seqlen, cache_len + seqlen) scores = F.softmax(scores.float(), dim=-1).type_as(xq) output = torch.matmul(scores, values) # (bs, n_local_heads, seqlen, head_dim) output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1) return self.wo(output) ","date":"2025-07-15T00:00:00Z","permalink":"/gqa/","title":"GQA"},{"content":"GRPO (trl 库) 重要参数 Num_generations: Number of generations to sample. The effective batch size (num_processes * per_device_batch_size * gradient_accumulation_steps) must be evenly divisible by this value. generation_batch_size: Batch size to use for generation. If None, it defaults to the effective training batch size: per_device_train_batch_size * num_processes * steps_per_generation. steps_per_generation: Number of optimization steps per generation. If None, it defaults to gradient_accumulation_steps. Num_iterations: Number of iterations per batch (denoted as μ in the algorithm). Per_device_train_batch_size Num_processes (world_size) trl 库的重要参数比较少。其中根据官方文档，generation_batch_size = `per_device_train_batch_size * num_processes * steps_per_generation Gradient_accumulation_steps 一般就是 steps_per_generation (对应 verl 中的 mini_batch_size / n_gpus / ppo_micro_batch_size_per_gpu)，可以理解为 per_device_train_bs (对应 verl 中的 ppo_micro_batch_size_per_gpu) 是使用梯度累计后的 bs，乘 gpu 数，再乘梯度累计的 steps 就是总的 batch_size（对应 verl 中的 train_batch_size * rollout. N）。所以注意，总的 batch_size (generation_batch_size) 是已经 rollout 采样后的 bs，除以 num_generations 才是针对 prompts 的 bs（verl 中的 train_batch_size）。 下面是_get_train_sampler 方法的注释，对每一个 prompt 重复 num_generations 是该方法实现的。\nif dataset is None: dataset = self.train_dataset return RepeatSampler( data_source=dataset, mini_repeat_count=self.num_generations, # 每个 prompt 生成 self.num_generations 个 completions # 例如，如果 per_device_train_batch_size=8, num_generations=2, steps_per_generation=4, # 则 generation_batch_size = 8 (per_device_train_batch_size) * 4 (steps_per_generation) = 32 # 这里的 batch_size = 32 / 2 = 16，表示一个 \u0026quot;generation block\u0026quot; 中有16个不同的prompt。 batch_size=self.args.generation_batch_size // self.num_generations, # 每个 \u0026quot;generation block\u0026quot; (包含16个不同prompt，每个prompt有2个completion) 会被用于 num_iterations * steps_per_generation 次更新 # 例如 num_iterations=1, steps_per_generation=4, 则这个 block 会被重复 1*4=4 次，每次取出一个 per_device_train_batch_size 的数据进行训练 repeat_count=self.num_iterations * self.args.steps_per_generation, shuffle=self.shuffle_dataset, seed=self.args.seed, ) 结合下面的例子帮助理解，例子中梯度累计 steps 不等于 steps_per_generation 在 GRPO_trainer 中，最重要的方法是 _generate_and_score_completions 方法，输入为 input，输出为计算得到的优势值和 old_logp 用于计算 ratio。一些核心的部分和注释如下：\nwith unwrap_model_for_generation( self.model_wrapped, self.accelerator, gather_deepspeed3_params=self.args.ds3_gather_for_generation ) as unwrapped_model: with ( FSDP.summon_full_params(self.model_wrapped, recurse=False) if self.is_fsdp_enabled else nullcontext() ): # prompt_ids: (B_gen_local, P_max) # prompt_mask: (B_gen_local, P_max) # prompt_completion_ids: torch.Tensor (B_gen_local, P_max + C_new), C_new 是 HF generate 生成的新 token 数量 (最大为 max_completion_length) prompt_completion_ids = unwrapped_model.generate( prompt_ids, attention_mask=prompt_mask, generation_config=self.generation_config ) # Compute prompt length and extract completion ids prompt_length = prompt_ids.size(1) # P_max # prompt_ids 保持不变: (B_gen_local, P_max) prompt_ids = prompt_completion_ids[:, :prompt_length] # completion_ids: torch.Tensor (B_gen_local, C_new_hf) completion_ids = prompt_completion_ids[:, prompt_length:] 上面为 generate 的过程，不过现在基本上使用 vllm 或者 sglang 加速推理。为了逻辑简单，这里展示了 HF generate 的过程。Trl 实现的时候，将一个 prompt 采样多次的逻辑实现在了 get_train_dataloader 方法中，即一开始就使用 get_train_sampler 方法对同一个 prompt repeat 了多次。因此这里不需要再进行 repeat。 之后得到补充部分的 mask:\n# Mask everything after the first EOS token # is_eos: torch.Tensor (B_gen_local, C_new), C_new 是 completion 的实际长度 (C_max_vllm 或 C_new_hf) is_eos = completion_ids == self.processing_class.eos_token_id # eos_idx: torch.Tensor (B_gen_local,), 存储每个 completion 中第一个 EOS token 的索引，如果没有EOS则为序列长度 eos_idx = torch.full((is_eos.size(0),), is_eos.size(1), dtype=torch.long, device=device) eos_idx[is_eos.any(dim=1)] = is_eos.int().argmax(dim=1)[is_eos.any(dim=1)] sequence_indices = torch.arange(is_eos.size(1), device=device).expand(is_eos.size(0), -1) # completion_mask: torch.Tensor (B_gen_local, C_new), 标记有效 token (EOS之前及EOS本身) completion_mask = (sequence_indices \u0026lt;= eos_idx.unsqueeze(1)).int() # Convert tensor to a list of lists of token IDs. This will be passed to the reward function, avoiding the need # to re-tokenize completions if the reward is computed from tokens. # completion_ids_list: list[list[int]], 长度 B_gen_local, 移除了 padding 和 EOS 之后的 token completion_ids_list = [ [id.item() for id, m in zip(row, mask_row) if m] for row, mask_row in zip(completion_ids, completion_mask) ] 然后根据 generate 的 ids 得到 old_logp:\nwith torch.no_grad(): # When using num_iterations == 1 and steps_per_generation \u0026lt;= gradient_accumulation_steps # old_per_token_logps == per_token_logps, so we can skip it\u0026#39;s computation here, and use # per_token_logps.detach() instead. if self.num_iterations \u0026gt; 1 or self.args.steps_per_generation \u0026gt; self.args.gradient_accumulation_steps: # old_per_token_logps: torch.Tensor (B_gen_local, C_new), 代表生成这些 completion 时所用模型的 log probabilities old_per_token_logps = self._get_per_token_logps( self.model, prompt_completion_ids, attention_mask, logits_to_keep, batch_size ) else: old_per_token_logps = None # 在特定条件下，可以用当前模型的 logprobs.detach() 代替，以节省计算 在上一步 generate 的时候我们不是已经进行过完整 batch 的推理了么？为什么现在还要重复进行一次 forward 来计算 log_prob，而不是在 generate 的过程中就把 log_prob 保存下来？\n因为 forward 的时候和 generate 的时候 logprob 由于推理引擎和训练引擎的优化目标不一样，会造成两者对不上，因此需要做两次。 Batch 算子的细微差异，都会造成这两个 log_prob 不完全一致。推理引擎要的是快速出 token id，训练引擎需要保证一定的 log_prob 精度。\n注意这里的很关键的一点是如果符合分支条件将 old_logp 设置成了 None，那么后续计算 ratio 时就固定为 1（old_logps = logps. Detach ）。如果 num_iterations \u0026gt; 1，说明一批数据会被训练多次，ratio 就不固定为 1 了。所以要保存生成训练数据的那个模型对应的 logps。Steps_per_generation \u0026gt; ga_steps 也一样，因为 steps_per_generation 参数就代表一批数据训练多少次。Ga_steps 更新 actor 参数，这之后 ratio 就不为 1 了。一共要经历 steps_per_gen / ga_steps 参数更新。如果我们不设置 steps_per_generation 默认就是 ga_steps，这里还是看这个图就可以理解了： 然后根据定义的奖励函数计算 reward：\nfor i, (reward_func, reward_processing_class, reward_func_name) in enumerate( zip(self.reward_funcs, self.reward_processing_classes, self.reward_func_names) ): with profiling_context(self, reward_func_name): if isinstance(reward_func, nn.Module): # GRPO一般不需要这部分 else: # 自定义奖励函数 output_reward_func = reward_func( prompts=prompts, completions=completions, completion_ids=completion_ids_list, **reward_kwargs ) # Convert None values to NaN output_reward_func = [reward if reward is not None else torch.nan for reward in output_reward_func] # rewards_per_func[:, i]: torch.Tensor (B_gen_local,) rewards_per_func[:, i] = torch.tensor(output_reward_func, dtype=torch.float32, device=device) 得到了奖励，就可以计算组内优势了：\n# Gather the reward per function: this part is crucial, because the rewards are normalized per group and the # completions may be distributed across processes rewards_per_func = gather(rewards_per_func) # (N_proc * B_gen_local, num_reward_funcs) # Apply weights to each reward function\u0026#39;s output and sum rewards = (rewards_per_func * self.reward_weights.to(device).unsqueeze(0)).nansum(dim=1) # (N_proc * B_gen_local,) # Compute grouped-wise rewards mean_grouped_rewards = rewards.view(-1, self.num_generations).mean(dim=1) # (N_groups_total, G) -\u0026gt; (N_groups_total,) std_grouped_rewards = rewards.view(-1, self.num_generations).std(dim=1) # (N_groups_total, G) -\u0026gt; (N_groups_total,) is_std_zero = torch.isclose(std_grouped_rewards, torch.zeros_like(std_grouped_rewards)) # (N_groups_total,) # Normalize the rewards to compute the advantages mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(self.num_generations, dim=0) # (N_groups_total * G,),即(N_proc * B_gen_local) std_grouped_rewards = std_grouped_rewards.repeat_interleave(self.num_generations, dim=0) # (N_groups_total * G,),即(N_proc * B_gen_local) advantages = rewards - mean_grouped_rewards 这样 grpo 训练所需要的 experience 就生产好了。下面进入训练阶段，计算 kl 散度：\nif self.beta != 0.0: # 仅当 beta 不为0时才需要计算 KL 散度 with torch.no_grad(): if self.ref_model is not None: ref_per_token_logps = self._get_per_token_logps( self.ref_model, input_ids, attention_mask, logits_to_keep ) else: with self.accelerator.unwrap_model(self.model).disable_adapter(): ref_per_token_logps = self._get_per_token_logps( self.model, input_ids, attention_mask, logits_to_keep ) # per_token_kl: torch.Tensor (B_eff, C_new), 每个 token 的 KL 散度 # KL(P || Q) = sum P(x) log(P(x)/Q(x)) 的一种估计形式 # 使用 exp(log P - log Q) - (log P - log Q) - 1 来避免直接计算 P/Q 可能的数值不稳定 per_token_kl = ( torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1 ) # k3 核心计算部分：\n# Compute the loss advantages = inputs[\u0026quot;advantages\u0026quot;] # torch.Tensor (B_eff,) # old_per_token_logps: torch.Tensor (B_eff, C_new), 旧策略（生成数据时）的对数概率 old_per_token_logps = ( per_token_logps.detach() if inputs[\u0026quot;old_per_token_logps\u0026quot;] is None else inputs[\u0026quot;old_per_token_logps\u0026quot;] ) # coef_1 (r_t(θ)): torch.Tensor (B_eff, C_new), 概率比率 exp(log_probs_new - log_probs_old) coef_1 = torch.exp(per_token_logps - old_per_token_logps) # coef_2 (clipped r_t(θ)): torch.Tensor (B_eff, C_new), 裁剪后的概率比率 coef_2 = torch.clamp(coef_1, 1 - self.epsilon_low, 1 + self.epsilon_high) # Two-sided clipping if self.args.delta is not None: # GRPO 论文中的 δ 参数，用于额外限制概率比率的上限 coef_1 = torch.clamp(coef_1, max=self.args.delta) # per_token_loss1: torch.Tensor (B_eff, C_new), PPO 目标的第一项 r_t(θ) * A_t per_token_loss1 = coef_1 * advantages.unsqueeze(1) # per_token_loss2: torch.Tensor (B_eff, C_new), PPO 目标的第二项 clip(r_t(θ), 1-ε, 1+ε) * A_t per_token_loss2 = coef_2 * advantages.unsqueeze(1) # per_token_loss: torch.Tensor (B_eff, C_new), PPO 损失的 surrogate 部分 -min(loss1, loss2) per_token_loss = -torch.min(per_token_loss1, per_token_loss2) if self.beta != 0.0: # 如果 beta 不为0，则加入 KL 散度惩罚项 per_token_loss = per_token_loss + self.beta * per_token_kl 这样，我们就得到了补全部分的每一个有效 token 的损失。这次还可以加入 entropy loss，指策略分布的熵 (Entropy)：策略对选择下一个动作（在这里是下一个 token）的不确定性程度。熵越高，表示策略输出的概率分布越均匀，选择各个动作的概率越接近，策略的探索性越强；熵越低，表示策略越倾向于选择少数几个高概率的动作，确定性越强。 entropy_loss 指 entropy 的平均值，是一个标量，表示探索性高低。 得到 token_loss 后根据不同的方法计算 batch 损失：\nif self.loss_type == \u0026quot;grpo\u0026quot;: # GRPO 论文中的标准损失：对每个序列的 token 损失求和后取平均，然后再对批次取平均 # (sum_t (L_t * mask_t) / sum_t mask_t).mean_batch() loss = ((per_token_loss * completion_mask).sum(-1) / completion_mask.sum(-1).clamp(min=1.0)).mean() elif self.loss_type == \u0026quot;bnpo\u0026quot;: # BNPO (Batch Normalized Policy Optimization) 损失：对所有 token 的损失求和后，除以所有有效 token 的总数 # sum_batch sum_t (L_t * mask_t) / sum_batch sum_t mask_t loss = (per_token_loss * completion_mask).sum() / completion_mask.sum().clamp(min=1.0) elif self.loss_type == \u0026quot;dr_grpo\u0026quot;: # DR-GRPO (Dense Reward GRPO) 损失：对所有 token 的损失求和后，除以 (批次大小 * 最大完成长度) # sum_batch sum_t (L_t * mask_t) / (B_eff * C_max) loss = (per_token_loss * completion_mask).sum() / (per_token_loss.size(0) * self.max_completion_length) else: raise ValueError(f\u0026quot;Unknown loss type: {self.loss_type}\u0026quot;) 例子（claude生成） 设定一个极简例子（单组 G=1，序列长为 2） 词表：{A, B, C} 问题 q 固定不变 旧策略 π_old 固定（无梯度）： t=1：π_old(·|q) = [A: 0.7, B: 0.2, C: 0.1] 采样得到 token A t=2：π_old(·|q, A) = [A: 0.2, B: 0.5, C: 0.3] 采样得到 token B 因此本序列为 o = (A, B)，|o|=2 设优势（token 级）： Â1 = 1.5，Â2 = 0.5（例如由序列奖励分配而来；对 θ 停梯度） 裁剪超参：ε = 0.2 → 裁剪区间 [0.8, 1.2] 当前策略 π_θ（可训练）： t=1：π_θ(·|q) = [A: 0.5, B: 0.3, C: 0.2] t=2：π_θ(·|q, A) = [A: 0.05, B: 0.65, C: 0.30]（让比例落在上界之外，便于展示裁剪） 计算重要性比 r_t = π_θ(y_t)/π_old(y_t)（只对被选中的 y_t= o_t）： - t=1：r1 = 0.5 / 0.7 ≈ 0.7143（低于下界 0.8） - t=2：r2 = 0.65 / 0.5 = 1.3（高于上界 1.2）\n前向（forward） GRPO/PPO token 级目标（单序列、单组）： - 对每个 t 计算 s_t = min(r_t · Â_t, clip(r_t, 1−ε, 1+ε) · Â_t) - 然后序列内取平均，组内取平均： J = (1/G) · (1/|o|) · Σ_t s_t 这里 G=1，|o|=2\n逐 t 计算： - t=1：Â1 \u0026gt; 0 且 r1 \u0026lt; 1−ε。对 A\u0026gt;0，目标随 r 增大而增大，所以 - unclipped = r1·Â1 = 0.7143·1.5 = 1.0715 - clipped = (1−ε)·Â1 = 0.8·1.5 = 1.2 - s1 = min(1.0715, 1.2) = 1.0715（选未裁剪分支） - t=2：Â2 \u0026gt; 0 且 r2 \u0026gt; 1+ε。对 A\u0026gt;0，若 r 超上界，min 选裁剪值 - unclipped = r2·Â2 = 1.3·0.5 = 0.65 - clipped = (1+ε)·Â2 = 1.2·0.5 = 0.6 - s2 = min(0.65, 0.6) = 0.6（选裁剪分支，数值与 θ 脱钩）\n整体目标： - J = (1/2) · (s1 + s2) = 0.5 · (1.0715 + 0.6) = 0.83575 - 训练时用损失 L = −J\n要点： - 红色标注的 1/|o_i| 是“序列长度归一化”，避免长序列因为 token 多而贡献更大梯度。 - 旧策略 π_old、优势 Â 全部 stop-grad，仅作为常数进入计算。\n反向（backward）：对 log prob 的梯度与裁剪的影响 令每个 token 的损失项 L_t = −(1/|o|) · s_t（这里 |o|=2，G=1），对 log π_θ 的导数取决于落在哪个分支：\n当选中未裁剪分支（如 t=1）：s_t = r_t·Â_t，且 r_t = exp(log π_θ(y_t) − log π_old(y_t)) d r_t / d log π_θ(y_t) = r_t d L_t / d log π_θ(y_t) = −(1/|o|) · Â_t · r_t 当选中裁剪分支（如 t=2 且 A\u0026gt;0、r\u0026gt;1+ε）：s_t = (1+ε)·Â_t，为常数，不依赖 θ d L_t / d log π_θ(y_t) = 0（完全“关梯度”） 把 t=1 的数代入： - 共同前因子 g1 = −(1/|o|) · Â1 · r1 = −(1/2)·1.5·0.7143 ≈ −0.5357 - 这是对 log π_θ(y1=A) 的梯度。接下来通过 softmax 链回 logits z（t=1 的那一步）： - 对 softmax：d log π(y)/d z_y = 1 − π(y)，d log π(y)/d z_k≠y = −π(k) - 于是 - ∂L/∂z_A(t=1) = g1 · (1 − π_θ(A|q)) = −0.5357 · (1 − 0.5) = −0.26785 - ∂L/∂z_B(t=1) = g1 · (−π_θ(B|q)) = −0.5357 · (−0.3) = +0.16071 - ∂L/∂z_C(t=1) = g1 · (−π_θ(C|q)) = −0.5357 · (−0.2) = +0.10714 - t=2 由于进入裁剪分支（对 A\u0026gt;0 超上界），梯度为 0，因此第二步的 logits 不接收梯度（来自该目标）。\n把各时刻梯度相加（这里只 t=1 有梯度），再继续通过注意力/MLP 等层往回传到所有参数 θ，得到最终 ∇_θ L，并进行更新。\n分支选择规则（直觉） 若 Â_t \u0026gt; 0： r_t ≤ 1+ε 时，用未裁剪分支，梯度方向会推动 π_θ(y_t)↑（增加好 token 概率） r_t \u0026gt; 1+ε 时，用裁剪分支，梯度为 0（防止过度放大） 若 Â_t \u0026lt; 0（坏 token）： r_t ≥ 1−ε 时，用未裁剪分支，梯度方向会推动 π_θ(y_t)↓ r_t \u0026lt; 1−ε 时，用裁剪分支，梯度为 0（防止过度压低） 小结实现步骤（从前到后） 采样：用 π_old 生成序列 o（或冻结快照作为 π_old）。 评估：得到奖励并转成 Â_{i,t}（组相对/标准化等；对 θ 停梯度）。 前向： 用当前 π_θ 计算被选 token 的 log π_θ 与比例 r_t 逐 token 计算 s_t = min(r_t·Â_t, clip(r_t)·Â_t) 求 J = (1/G)·(1/|o|)·Σ s_t，令 L = −J 反向： 自动微分即可得到对 log π 的梯度（裁剪区间之外时为 0） 通过 softmax-Jacobian 把梯度传到 logits，再传到参数 θ 归一化 1/|o| 控制了“每个序列贡献的总梯度规模”，不随长度线性放大。 以上例子展示了： - token 级 PPO/GRPO 的 min-clip 机制如何在超过阈值时关梯度； - 对应 token 的梯度只来自被选中的 y_t 的对数概率； - 优势和旧策略对 θ 均不反传； - 归一化 1/|o| 的实际缩放效应。 # 常见问题\nGRPO 的初始 Loss 为0，整体趋势是从0上升后下降，这正常吗？当 loss 是0的时候，那岂不是没梯度了，还能正常训练吗？ 当我们只执行一个step的时候，这时候 \\(\\pi_{\\theta_{\\text{old}}}\\)和 \\(\\pi_\\theta\\) 是相同的，所以目标函数可以被简化为：\n$$\n= {i=1}^G {t=1}^{|o_i|} {i,t} - {i=1}^G {t=1}^{|o_i|} D{}[| {}] $$\n易得优势值（归一化的）相加后等于0。因此变成了：\n\\[ - \\frac{1}{G} \\sum_{i=1}^G \\frac{1}{|o_i|} \\sum_{t=1}^{|o_i|} \\beta D_{\\text{KL}}[\\pi_\\theta \\| \\pi_{\\text{ref}}] \\]\n而在训练初期，目标策略和参考策略也是相同的，因此就导致了loss等于0。但这不代表梯度等于0\n对GRPO的目标函数求导后，得到：\n\\[ \\nabla_\\theta J_{\\text{GRPO}}(\\theta) = \\frac{1}{G} \\sum_{i=1}^G \\frac{1}{|o_i|} \\sum_{t=1}^{|o_i|} \\left[\\hat{A}_{i,t} + \\beta\\left(\\frac{\\pi_{\\text{ref}}(o_{i,t}|q,o_{i,\u0026lt;t})}{\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t})} - 1\\right)\\right]\\nabla_\\theta\\log\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t}) \\]\n中括号内的右边部分可以易得为0，这时候就变成了：\n因为\\(\\frac{1}{\\mid o_{i} \\mid} \\sum_{t=1}^{\\mid o_{i}\\mid} \\hat{A}_{i,t} = \\hat{A}_{i}\\) 化简为： \\[ \\nabla_\\theta J_{\\text{GRPO}}(\\theta) = \\frac{1}{G} \\sum_{i=1}^G \\left[\\hat{A}_i \\cdot \\left(\\frac{1}{|o_i|} \\sum{t=1}^{|o_i|} \\nabla_\\theta\\log\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t})\\right)\\right] \\neq 0 \\] 因此loss为0不代表梯度为0。\n为什么在GRPO训练后的模型会出现更倾向于长文本的回答呢？对于偏差是如何解决的呢？ 参考# GRPO为什么会使得模型的推理变长？\n$$\n{}() = {p_Q,{i}{i=1}^G {_{}}(|)} }] $$\n\\[ \\hat{A}_{i,t} = \\frac{R(\\mathbf{q}, \\mathbf{o}_i) - \\text{mean}(\\{R(\\mathbf{q}, \\mathbf{o}_1),\\ldots,R(\\mathbf{q}, \\mathbf{o}_G)\\})}{\\color{red}{\\text{std}(\\{R(\\mathbf{q}, \\mathbf{o}_1),\\ldots,R(\\mathbf{q}, \\mathbf{o}_G)\\})}}, \\]\n响应级别长度偏差：对积极的advantage，这种偏差导致较短的响应获得更大的梯度更新，从而使策略倾向于在正确答案中优先选择更简洁的表达。相反，对于消极的advantage，由于较长的响应具有更大的 |oi|，因此它们受到的惩罚较小，这导致策略在错误答案中倾向于选择较长的响应。 问题难度级别偏差：标准差较低的问题（例如，太简单或太困难的问题，结果奖励几乎全为 1 或 0）在策略更新时会被赋予更高的权重。问题级归一化导致不同问题在目标函数中的权重不同，从而在优化过程中产生了难度偏差。 如果是答对的样本，因为模型鼓励正确的样本，短的回复token权重更高，这时候模型会倾向于回复更短\n如果是答错的样本，因为模型要避免错误的样本，短的回复token权重更高，这时候模型会避免回复太短，也就是倾向于回复更长。\n因此可以看出，在一个step中，样本回答的正确率，会对模型回复的长度倾向有一定的影响。\n在step正确率高的情况下，模型的回复可能会倾向于越来越短 在step正确率低的情况下，模型的回复可能会倾向于越来越长 DAPO的token-loss解决了这个问题。\nPPO 、GRPO、DAPO 的区别是什么? 它们依次进行了那些改动，说出他们的异同？ - PPO 的 KL 计算是在每个 token 的生成过程中发生的，不断计算当前 token 和 ref_model 的 KL 散度。 - GRPO 的 KL 计算是在一个回答生成结束后发生的，一次性对句子中的每个 token 计算 KL 散度，并参与最终 loss 的计算； - PPO 的 KL 塞到 reward 里（reward shaping） - GRPO 的 KL 是独立的损失项。 - advantage 角度来看，PPO 的 KL 惩罚是 token-level 的 - GRPO 的 KL 惩罚是 sentence-level（但是也是逐个 token 算 kl 再取 mean）的。\nDAPO与GRPO的区别详见dapo ### K1、K2、K3分别是什么以及区别？\nimage.png grpo的on-policy和off-policy \\[ \\nabla_\\theta J_{\\text{GRPO}}(\\theta) = \\frac{1}{G} \\sum_{i=1}^G \\frac{1}{|o_i|} \\sum_{t=1}^{|o_i|} \\left[\\hat{A}_{i,t} + \\beta\\left(\\frac{\\pi_{\\text{ref}}(o_{i,t}|q,o_{i,\u0026lt;t})}{\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t})} - 1\\right)\\right]\\nabla_\\theta\\log\\pi_\\theta(o_{i,t}|q,o_{i,\u0026lt;t}) \\] 从算法理论上来说，如果不计较训练损耗，一直使用on policy来训练，GRPO结论可能也未必会好，因为GRPO的问题在于动作粒度与奖励粒度的不均衡，在对GRPO目标函数计算梯度时每个token具有相同的优势值。这时候主要的梯度贡献其实是重要性采样修正项的值。如果这个值为1的话，GRPO每个token就只剩下了相同的优势值，其实这样就退化成了SFT训练的交叉熵梯度，也就是每个token是具有相同奖励的行为克隆，因为从强化学习策略梯度定理角度来看，RL相对于SFT梯度公式最大的区别在于每个动作（token）是具备不同优势值。 # 参考\nSome simple thoughts on GRPO\nGRPO-DAPO-GSPO\n","date":"2025-07-15T00:00:00Z","permalink":"/grpo/","title":"grpo"},{"content":"3-pass \\(\\mathsf{NO}\\) TATIONS\n\\(\\{m_i\\}{:}\\max_{j=1}^i\\left\\{x_j\\right\\}\\), with initial value \\(m_0=-\\infty.\\) \\(\\{d_i\\}{:}\\sum_{j=1}^ie^{x_j-m_N}\\), with initial value \\(d_0=0,d_N\\) is the denominator of safe softmax. \\(\\{a_i\\}{:\\text{ the final softmax value}}.\\)\nBODY \\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[m_i\\leftarrow\\max\\left(m_{i-1},x_i\\right)\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[d_i\\leftarrow d_{i-1}+e^{x_i-m_N}\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[a_i\\leftarrow\\frac{e^{x_i-m_N}}{d_N}\\] \\(\\mathbf{end}\\)\n这是 3 step 计算 attention 的方法，每一步都需要上一步的结果才可以继续计算。这样的话由于 sram 中没有足够的存储空间，因此需要多次访存。 ### Online attention \\[\\begin{aligned} d_i^{\\prime}\u0026amp; =\\sum_{j=1}^ie^{x_j-m_i} \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1} e^{x_j-m_i}\\right)+e^{x_i-m_i} \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1} e^{x_j-m_{i-1}}\\right)e^{m_{i-1}-m_i}+e^{x_i-m_i} \\\\ \u0026amp;= d_{i-1}\u0026#39; e^{m_{i-1}-m_i}+e^{x_i-m_i} \\end{aligned}\\] 找到迭代式之后就可以从 3 step 降到 2 step \\[\\begin{aligned}\u0026amp;\\mathbf{for~}i\\leftarrow1,N\\textbf{ do}\\\\\u0026amp;\u0026amp;\u0026amp;m_i\u0026amp;\u0026amp;\\leftarrow\u0026amp;\\max\\left(m_{i-1},x_i\\right)\\\\\u0026amp;\u0026amp;\u0026amp;d_i^{\\prime}\u0026amp;\u0026amp;\\leftarrow\u0026amp;d_{i-1}^{\\prime}e^{m_{i-1}-m_i}+e^{x_i-m_i}\\\\\u0026amp;\\mathbf{end}\\\\\u0026amp;\\mathbf{for~}i\\leftarrow1,N\\textbf{ do}\\\\\u0026amp;\u0026amp;\u0026amp;a_i\\leftarrow\u0026amp;\u0026amp;\\frac{e^{x_i-m_N}}{d_N^{\\prime}}\\\\\u0026amp;\\mathbf{end}\\end{aligned}\\] 好像 FLOPs 计算量并没有减少，甚至还略有增加，因为现在每次都需要计算额外的 scale\nX 值，也就是 pre-softmax logits，由于需要 O (N^2) 的显存无法放在 SRAM 中。因此：\n1. 要么提前计算好 x，保存在全局显存中，需要 O (N^2) 的显存，容易爆显存。\n2. 要么在算法中 online 计算，每次循环中去 load 一部分 Q，K 到片上内存，计算得到 x。\nAttention 优化的目标就是避开第一种情况，尽可能节省显存，否则，LLM 根本无法处理类似 100 K 以上这种 long context 的情况。而对于第二种情况，我们不需要保存中间矩阵 x，节省了显存，但是计算没有节省，并且增加了 HBM IO Accesses（需要不断地 load Q, K）。此时，2-pass 算法相对于 3-pass 算法，可以减少一次整体的 load Q, K 以及减少一次对 xi 的 online recompute，因为在 2-pass 的第一个 pass 中， xi 是被两次计算共享的。类似 online-softmax 这种算法，对应到 Attention 中的应用，就是 Memory Efficient Attention（注意不是 FlashAttention）。\n","date":"2025-07-15T00:00:00Z","permalink":"/online-attention/","title":"online attention"},{"content":"参考 # 图解大模型计算加速系列之：vLLM核心技术PagedAttention原理\n","date":"2025-07-15T00:00:00Z","permalink":"/paged-attention/","title":"paged attention"},{"content":"PPO (openrlhf 库) 重点记录一下 experience 的采集过程。训练其实很简单。Actor 在 RLHF 会进行 auto-regressive decoding，而 critic, reward 和 reference 则只会 prefill，不会 decode。所以，我们将 actor 的推理特定称为 rollout，而其他模型的推理称为 inference。 获取 experience 的总体流程：\n#################### # 1. 调用Actor generate()方法获取Prompt的生成结果，把结果存储到Sample对象 #################### samples_list = self.generate_samples(all_prompts, **generate_kwargs) torch.distributed.barrier() #################### # 2. 调用make_experience 对每个Sample做处理，组装Experience部分字段（除了advantage和return） #################### experiences = [] for samples in samples_list: experiences.append(self.make_experience(samples).to_device(\u0026quot;cpu\u0026quot;)) experiences, rewards = self.process_experiences(experiences) #################### # 3. 通过从后往前回溯计算的方式，获取advantage和return值 #################### for experience, reward in zip(experiences, rewards): num_actions = experience.info[\u0026quot;num_actions\u0026quot;] if self.advantage_estimator == \u0026quot;gae\u0026quot;: experience.advantages, experience.returns = self.get_advantages_and_returns( experience.values, reward, experience.action_mask, generate_kwargs[\u0026quot;gamma\u0026quot;], generate_kwargs[\u0026quot;lambd\u0026quot;], ) if not getattr(self, \u0026quot;packing_samples\u0026quot;, False): return_sums = reward.sum(dim=-1) else: return_sums = torch.tensor( [each_reward.sum() for each_reward in reward], device=torch.cuda.current_device() ) experience.info[\u0026quot;return\u0026quot;] = return_sums return experiences 关于句子序列中的 state 和 action 定义如下： ## Prompt -\u0026gt; sample 首先对 batch 进行 pad, 注意推理时需要左 pad。 然后生成 sequences，attention_mask, action_mask： Sequences： Attention_mask： Action_mask： 至此 sample 数据就得到了。 ## Sample -\u0026gt; experience 首先根据前面 generate 的 prompt+response 计算得到 response 部分的 logp： 同样的方式得到 reference_model 的 logp，然后就可以计算 kl 散度。\nCritic 是预估状态的价值，看代码实现时，参考图 3，先理解 LLM 中状态的起始位置。最终状态序列长度是 num_actions (生成 token 的数量)，状态序列起始位置是 Prompt 的最后一个 token，结束位置是最后 eos token 前一个 token，所以计算出的 Critic 预估状态价值的数据为： 注意，图里 eos token 和 pad token 的位置输出应该并不是 0，是 regression head 的输出（小实数值），只是我们期望良好的价值函数在这些位置输出 0\n在 RLHF 中，Reward Model 是一个 ORM（outcome Reward Model） 也就是对完整的生成 response 输出一个打分。代码实现上取每个 sequence eos token 位置的预估打分值。如图 11，图中”xx”也是会并行计算出的 Reward 值，单最终只取了序列最后 eos 位置的 score 作为完整序列的打分值。最后 reward 处理成[B, 1]格式，每个序列一个打分。 Gae (Generalized Advantage Estimation) 是 PPO 论文中实现的优势奖励值计算方法，可平衡优势预估的偏差和方差。结合公式和图片内容更容易理解：\ndef get_advantages_and_returns(values: torch.Tensor, rewards: torch.Tensor,） Advantages looks like this: Adv1 = R1 + γ * λ * R2 + γ^2 * λ^2 * R3 + ... - V1 + γ * (1 - λ) V2 + γ^2 * λ * (1 - λ) V3 + ... Returns looks like this: Ret1 = R1 + γ * λ * R2 + γ^2 * λ^2 * R3 + ... + γ * (1 - λ) V2 + γ^2 * λ * (1 - λ) V3 + ... 计算 returns： image.png 此时我们就完成了 experience 的采集过程。\nClip 的一些细节 image.png image.png 上面这张图是很经典的一张图，来分析什么情况下 clip 项计算梯度。\n例子 image.png state对于文本序列来说就是之前生成的所有tokens，V(s)就是当前所有tokens为状态下的回报。而reward是对于整个句子而言的，因此只有最后一个状态才有reward。\n参考 如何理解Q值和V值 (https://zhuanlan.zhihu.com/p/14569025663)\n","date":"2025-07-15T00:00:00Z","permalink":"/ppo/","title":"ppo"},{"content":"一、论文的研究目标与意义 研究目标与待解决问题 论文的核心研究目标是：将基于强化学习（RL）的推理能力提升方法，从仅限于数学、编程等拥有明确验证规则的领域，扩展到更广泛的通用推理领域（如化学、法律、生物、商业等），同时摆脱对外部验证器（Verifier）的依赖。\n要理解这一点，我们需要先了解一个背景概念：基于可验证奖励的强化学习 (RL with Verifiable Rewards, RLVR)。这是一种近年来非常成功的 LLM 训练范式，以 DeepSeek-R 1-Zero 为代表。其基本流程是：\n模型针对一个问题，生成一个包含“思考过程”（Chain-of-Thought, CoT）和“最终答案”的完整回答。 一个验证器（Verifier），通常是一个基于规则的程序，会自动检查“最终答案”是否正确。 如果答案正确，模型获得+1 的奖励；如果错误，则获得 0 的奖励。 模型通过强化学习算法（如 PPO），根据这个非 0 即 1 的奖励信号来调整自身参数，以期生成更多能获得高奖励（即正确答案）的回答。 这种方法的瓶颈非常明显：它严重依赖一个可靠、廉价的自动验证器。这在数学（答案唯一）和编程（代码可执行）领域是可行的，但在绝大多数真实世界问题中，构建这样的验证器几乎不可能。例如，如何用程序自动判断一个法律分析或商业策略是否“正确”？\n为了解决这个问题，行业内出现了一种变通方法：用一个更强大的 LLM（如 GPT-4）作为模型验证器（Model-based Verifier）来判断答案的正确性。但这又引入了新的问题，正如论文摘要中提到的：\nCurrent practical workarounds use an additional LLM as a model-based verifier; however, this introduces issues such as reliance on a strong verifier LLM, susceptibility to reward hacking, and the practical burden of maintaining the verifier model in memory during training.\n这里的Reward Hacking是个很关键的概念，指的是模型可能会找到一些方法来“欺骗”验证器以获得奖励，但其推理过程本身可能是错误的。比如，模型可能学会了生成一些看起来很有说服力但实际上是错误的推理，来迎合验证器模型的“偏好”。同时，训练时需要额外加载和查询一个强大的验证器模型，这会带来巨大的计算和内存开销。\n因此，这篇论文要解决的实际问题就是：如何在没有验证器（无论是基于规则还是基于模型）的情况下，依然能有效地通过强化学习提升 LLMs 在通用领域的推理能力？\n行业发展意义 这个问题的解决对于行业发展至关重要：\n普及先进的训练技术：它将 RLVR 这一被证明在特定领域极为有效的技术，推广到了更广阔的通用场景，使得训练各行各业的“专家模型”成为可能。 降低训练成本和复杂性：通过移除验证器，该方法简化了训练流程，降低了对计算资源（特别是显存）的要求，让更多机构有能力进行类似的训练。 提升模型能力的“天花板”：相比于传统的监督微调（SFT），基于探索和奖励的强化学习能够让模型生成更多样化、更高质量的推理路径，从而可能达到比 SFT 更高的高度。这篇论文的方法为实现这一目标铺平了道路。 增强系统的鲁棒性：摆脱了对可能被“欺骗”的模型验证器的依赖，使得训练过程更稳定，更专注于提升模型内在的推理和答案生成能力的一致性。 二、核心思路与方法创新 论文提出的核心方法VeriFree (Verifier-Free)，其思路堪称精妙。它没有试图去判断模型生成的答案 y 是否正确，而是巧妙地改变了优化的目标。\n核心思想：从“判断对错”到“评估置信度” 传统 RLVR 的优化目标可以理解为最大化“获得正确答案的期望奖励”。在只有一个正确答案 y* 的情况下，其目标函数如论文公式 (2) 所示： \u0026gt; J_Verifier (θ; x, y*) = E_{z∼π_θ(·|x)} E_{y∼π_θ(·|x, z)} [ R_verifier (y; y*) ]\n其中 z 是推理过程，y 是模型生成的答案，R_verifier 在 y 等于 y* 时为 1，否则为 0。\nVeriFree 的核心洞察在于，上述期望值 E_{y∼π_θ(·|x,z)}[...] 其实可以直接计算出来。因为只有一个答案 y* 能获得奖励 1，其他所有答案奖励都为 0，所以这个期望值就等于模型在给定了问题 x 和推理过程 z 后，生成正确答案 y* 的概率 π_θ(y*|x, z)。\n因此，VeriFree 将优化目标直接转化为了论文中的公式 (4)： \u0026gt; J_VeriFree (θ; x, y*) = E_{z∼π_θ(·|x)} [ π_θ(y*|x, z) ]\n这个转变的直观理解是：我们不再让模型生成一个完整的回答然后去判断对错，而是只让模型生成推理过程 z，然后直接评估模型对于“标准答案” y* 的置信度有多高。这个置信度（即概率 π_θ(y*|x, z)）本身就成了奖励信号。一个好的推理过程 z，自然应该让模型对正确答案 y* 有更高的置信度。\n方法优势与创新点 与之前的方法相比，VeriFree 具有以下显著特点和优势：\n更低的梯度方差（Lower Variance）：这是该方法在理论上的一大亮点。论文在定理 1（Theorem 1）中证明，VeriFree 的梯度估计器方差低于传统的验证器方法。这里涉及到一个重要的统计学概念叫Rao-Blackwellization。通俗地讲，传统方法需要随机采样一个答案 y 来获得一个充满随机性的 0 或 1 的奖励，这个过程方差很大。而 VeriFree 通过数学变换，用一个确定的期望值（即概率 π_θ(y*|x, z)）替代了这个随机采样过程，从而“分析性地边缘化”了 y 带来的随机性，使得训练信号更稳定，收敛更快。\n理论上的等价性：在“单一正确答案”的假设下，VeriFree 的优化目标与原始的验证器方法在期望上是完全等价的。它不是一个近似，而是一个精确的数学重构。这使得它的理论基础非常坚实，不像其他一些方法（如 JLB、LaTRO）是在优化一个近似的下界。\n对齐推理与答案：论文在 2.3 节中与 JLB、LaTRO 等方法的对比，揭示了 VeriFree 一个更深层次的优势。其他方法在更新答案生成部分时，权重通常是固定的（为 1），这意味着无论推理过程 z 有多糟糕，模型都会被强制要求在该推理下生成 y*。而 VeriFree 的更新权重是 π_θ(y*|x, z)，即奖励值本身。这会产生一个很好的效果：\n如果推理过程 z 是高质量的，模型对 y* 的置信度 π_θ(y*|x, z) 会很高，那么对推理过程和答案生成的更新权重就大。 如果推理过程 z 是低质量的（例如胡言乱语），模型对 y* 的置信度会很低，更新权重就小，从而避免了让模型学习“从错误的推理得到正确的答案”这种有害的关联。 巧妙的实现细节：论文在 2.4 节中讨论了如何处理“拼接点”的 Tokenization 问题。这是一个非常实践的细节，展示了作者的严谨。简单地用文本分割推理和答案，可能会因为上下文变化导致 Tokenization 不一致而出错。他们提出的方法是，在生成推理时，将停止符设置为 \u0026lt;answer（不含 \u0026gt;），这确保了 Token 边界的干净对齐，是一个非常聪明且高效的工程技巧。\n三、实验设计与结果验证 论文通过一系列周密设计的实验来验证 VeriFree 的有效性。\n实验设计 基础模型：使用了不同参数规模的Qwen 3系列模型（1.7 B, 4 B, 8 B），这有助于检验方法在不同模型尺寸下的普适性。 训练数据：使用了一个名为WebData的通用推理数据集，该数据集源自 WebInstruct，经过了筛选和清洗，覆盖了多个领域，确保了训练的通用性。 评估基准： 通用推理：MMLU-Pro和SuperGPQA，这两个都是极具挑战性的、覆盖广泛学科的研究生水平的基准测试，能很好地评估模型的通用推理能力。 数学推理：同时也在 MATH、GSM 8 K 等一系列数学基准上进行了测试，以验证方法的泛化性。 核心对比对象 (Baselines)： Verifier：这是最重要的对比组。他们构建了一个基于模型的验证器，并使用与 VeriFree 相同的 RL 算法（Dr. GRPO）进行训练，确保了控制变量的公平性。 基础模型 (Base)：即未经任何微调的 Qwen 3 模型。 指令微调模型 (Instruct)：即官方发布的、经过 SFT 的 Qwen 3 模型。 实验数据与结果 实验结果非常有力地支持了论文的结论。\n通用推理能力超越 Verifier 方法： 在 MMLU-Pro 和 SuperGPQA 这两个核心基准上，VeriFree 的表现稳定地优于或持平于需要额外验证器的 Verifier 方法。\n模型 (Qwen 3-8 B) MMLU-Pro (Avg Acc %) SuperGPQA (Avg Acc %) Base (基础模型) 59.8 31.0 Base-Verifier 65.9 37.1 Base-VeriFree (Ours) 67.2 38.0 如上表所示（数据来自原文 Table 1 和 Table 2），在 8 B 规模的模型上，VeriFree 在两个基准上都取得了比 Verifier 更高的分数，并且远超基础模型。\n更高的学习效率：论文中的Figure 4 (Left)清晰地展示了学习效率的差异。\nMMLU-Pro Performance Figure 4: Left: MMLU-Pro accuracy of VeriFree and the baseline fine-tuned from Qwen 3-8 B base model along training steps.\n图中的红色曲线（VeriFree）自始至终都位于灰色曲线（Verifier）之上，这意味着在相同的训练步数下，VeriFree 能达到更高的准确率。这完美印证了其因“低方差”而带来的“高效率”的理论优势。\n可迁移的推理能力：Figure 5中的实验设计非常巧妙。研究人员移除了训练数据中所有的数学相关样本，然后用 VeriFree 进行训练。结果发现，即使从未在数学数据上训练，模型在数学基准测试上的性能依然得到了提升。这表明 VeriFree 学习到的不是针对特定领域的“解题技巧”，而是一种更底层的、可跨领域迁移的“通用推理能力”。\n消融实验（Ablation Study）：Figure 6 (Left)中的消融实验证明了方法中各个组件的必要性。去掉 RLOO（一种方差缩减技术）或使用简单的文本分割策略，都会导致模型性能显著下降，这反过来证明了论文所提出方法的完整性和严谨性。\n四、未来方向与潜在机会 结合当前大模型领域的学术理解，这篇论文的研究为未来开辟了多个激动人心的方向。\n处理答案等价类（Equivalence Class）：论文的主要理论建立在“单一正确答案”的假设上。但在现实中，很多问题有多个表述不同但语义相同的正确答案（如“1.6”和“8/5”）。论文在 3.3 节中也承认这是一个当前方法的局限，并做了一个初步实验。未来的一个重要研究方向就是如何将 VeriFree 扩展到能高效处理答案等价类的情况，例如，将奖励信号定义为“模型对等价类中所有答案的概率之和”，但这会带来新的计算挑战。 与更先进 RL 算法的结合：VeriFree 本质上定义了一种新的奖励形式。这种奖励形式可以与更复杂的强化学习算法（如在线的、基于模型的 RL 算法）相结合，可能会进一步提升训练效率和效果。 探索“自我提升”的极限：VeriFree 的核心是利用“标准答案”作为监督信号。一个更前沿的方向是，能否让模型在没有标准答案的情况下自我提升？例如，模型生成多个不同的推理路径和答案，然后利用某种一致性原则（如多个推理路径指向同一个答案）或模型自身的置信度来构建奖励信号，实现完全的自监督推理能力提升。 技术与投资机会： 高效 RL 训练平台：类似论文中使用的 Oat 框架，专注于 LLM 的 RL 训练平台将成为一个重要的基础设施。提供低成本、高效率、易于使用的 VeriFree 式训练解决方案，对企业非常有吸引力。 高质量数据集服务：VeriFree 虽然不需要验证器，但依然需要高质量的 (问题, 推理过程, 标准答案) 数据对。因此，围绕特定行业（如法律、金融、医疗）构建和销售这种高质量数据集，将成为一个新的商业模式。 领域专用推理模型（Domain-Specific Reasoners）：企业可以利用 VeriFree，以相对较低的成本，在自己的私有数据上训练出具有强大专业推理能力的模型，而无需投入巨资研发复杂的领域验证器。这将催生大量定制化的 AI 解决方案。 五、批判性视角与潜在不足 从批判的角度看，这篇论文虽然出色，但仍存在一些值得探讨的局限和待验证之处。\n对“标准答案”的强依赖：这是该方法最核心的“软肋”。VeriFree 将对“验证器”的依赖，转移到了对“标准答案（Reference Answer）”的依赖。如果训练数据中的标准答案是错误的、有偏见的、或只是众多可能答案中的一个，那么训练出的模型也会继承这些问题。数据的质量直接决定了模型能力的上限。 “单一正确答案”假设的普适性：如前所述，该方法的核心理论推导依赖于此假设。尽管在很多评测基准中这是成立的，但在开放、真实的现实世界问题中，这个假设往往不成立。论文虽然做了初步探索，但其有效性在更复杂场景下仍需验证。 推理质量与答案置信度的关联：该方法有一个隐含假设：高质量的推理过程必然导致对正确答案的高置信度。这在大多数情况下是成立的，但我们能否设想一种情况：模型给出了一个看似合理但实际上有缺陷的推理，并“碰巧”对正确答案产生了高置信度？这种“伪推理”是否会被错误地强化，值得进一步研究。 泛化到全新问题类型：实验表明模型具有一定的迁移能力，但训练数据仍然是 (问题, 答案) 对的形式。模型学到的是否主要是“从已有问题到答案的推理模式”，对于那些需要全新、从未见过的推理结构才能解决的问题，其泛化能力如何，还需要更具挑战性的实验来验证。 六、核心启发与知识补充 对于希望从中汲取创新想法的学习者和研究者，我认为以下几点尤其值得关注：\n核心启发 奖励信号的重塑（Reward Shaping）：这篇论文最精彩的启发是“重新定义奖励”。当直接的、稀疏的奖励（如 0/1）难以获得或效果不佳时，可以思考是否能找到一个连续的、低方差的、可微分的代理信号。将奖励从“对错判断”转变为“模型自身对正确答案的置信度”，这是一个极具创造力的范式转换，可以应用到很多其他机器学习任务中。 理论指导实践的力量：Rao-Blackwellization 定理的应用是理论指导算法设计从而获得实践收益（更高效率、更稳定）的典范。这提醒我们，深入理解一些基础的统计和数学原理，往往能为解决复杂的工程问题提供意想不到的捷径。 模型置信度的价值：Figure 4 (Right)揭示了模型置信度 π_θ(y*|x, z) 与最终评测准确率之间存在强正相关（ρ=0.82）。这表明模型自身的置信度是一个非常有价值的信号。它不仅可以作为训练中的奖励，甚至可以作为模型部署后的一种“自我评估”指标，用于判断模型输出的可靠性，或者用于实现主动学习（Active Learning）——只在模型低置信度的样本上请求人工标注。 需要补充的背景知识 要完全理解并应用这篇论文的思想，您可能需要了解以下背景知识：\n强化学习基础：特别是策略梯度（Policy Gradient）方法和PPO（Proximal Policy Optimization）算法。这是理解论文中梯度公式和优化过程的基础。 从人类反馈中进行强化学习（RLHF）：了解标准的 RLHF 流程，特别是奖励建模（Reward Modeling）部分，能帮助您更深刻地理解 VeriFree 是如何绕过显式奖励建模这一环节的。 变分推断（Variational Inference, VI）：论文提到了 JLB、LaTRO 等方法源于 VI。对 VI 有基本了解，可以帮助您理解那些方法与 VeriFree 在理论根源上的不同。 Rao-Blackwell 定理：如果想深入理解 VeriFree 的低方差优势，花时间学习一下这个统计学定理会非常有帮助。 总而言之，这篇论文以其简洁而深刻的洞察，为大模型推理能力的提升提供了一条非常实用且高效的新路径。它完美地平衡了理论的优雅与实践的效用，无疑是近期 LLM 强化学习领域一篇不容错过的佳作。希望这份详细的解读能对您有所帮助！\n","date":"2025-07-15T00:00:00Z","permalink":"/reinforcing-general-reasoning-without-verifiers/","title":"Reinforcing General Reasoning without Verifiers"},{"content":"","date":"2025-07-15T00:00:00Z","permalink":"/reinforece/","title":"REINFORECE++"},{"content":"","date":"2025-07-15T00:00:00Z","permalink":"/remaxreinforce-argmax/","title":"ReMAX（REINFORCE argmax）"},{"content":"","date":"2025-07-15T00:00:00Z","permalink":"/rloo/","title":"RLOO"},{"content":" AdaGrad 直接暴力累加平方梯度，这种做法的缺点就是累加的和会持续增长，会导致学习率变小最终变得无穷小，最后将无法获得额外信息。\n","date":"2025-07-12T00:00:00Z","permalink":"/adagrad/","title":"AdaGrad"},{"content":"AdamW相对与Adam的改动十分简单，其将权重衰减项从梯度的计算中拿出来直接加在了最后的权重更新步骤上（图1，式12）。其提出的动机在于：原先Adam的实现中如果采用了 L2权重衰减，则相应的权重衰减项会被直接加在loss里，从而导致动量的一阶与二阶滑动平均均考虑了该权重衰减项，而这影响了Adam的优化效果，而将权重衰减与梯度的计算进行解耦能够显著提升Adam的效果。目前，AdamW现在已经成为transformer训练中的默认优化器了。\nimage.png 参考：# Adam和AdamW\n","date":"2025-07-12T00:00:00Z","permalink":"/adamw/","title":"AdamW"},{"content":" RMSProp 和 Adagrad 算法的最大区别就是在于更新累积梯度值 r 的时候 RMSProp 考虑加入了一个权重系数 ρ 。 它使用了一个梯度平方的滑动平均。其主要思路就是考虑历史的梯度，对于离得近的梯度重点考虑，而距离比较远的梯度则逐渐忽略。注意图中的是内积。\n","date":"2025-07-12T00:00:00Z","permalink":"/rmsprop/","title":"RMSProp"},{"content":"作为机器学习的初学者必然会接触梯度下降算法以及 SGD，基本上形式如下：\n\\[ \\theta_t = \\theta_{t-1} - \\alpha \\;g(\\theta) \\] 其中 \\(\\alpha\\) 为学习率，\\(g(\\theta)\\) 为梯度。\n","date":"2025-07-12T00:00:00Z","permalink":"/sgd/","title":"SGD"},{"content":"带动量的随机梯度下降方法\n它的思路就是计算前面梯度的该变量，每次迭代会考虑前面的计算结果。这样如果在某个维度上波动厉害的特征，会由于“momentum”的影响，而抵消波动的方向（因为波动剧烈的维度每次更新的方向是相反的，momentum 能抵消这种波动）。使得梯度下降更加的平滑，得到更快的收敛效率。而后续提出的 Adagrad，RMSProp 以及结合两者优点的 Adam 算法都考虑了这种“momentum”的思想。\n前面求梯度的过程省略了，后面可以这样写：\n\\[ \\begin{align} \u0026amp; v_t = \\beta v_{t-1} + (1-\\beta)g_t \\\\\\\\ \u0026amp; \\theta = \\theta - \\alpha v_t \\end{align} \\]\n其中 \\(\\alpha\\) 为学习率，一般的 \\(\\beta\\) 为 0.9。V 就是动量。\n所以，SGD + Momentum 可以理解为，利用历史权重梯度矩阵 \\(W_{i} l(i\u0026lt;t)\\) 和当前权重梯度矩阵 \\(W_{t} l\\) 的加权平均和，来更新权重矩阵 \\(W\\) 。由于 \\(\\beta \\in(0,1)\\) ，所以随着 \\(t\\) 的增大和 \\(i\\) 的减小， \\(\\beta^{t-i}\\) 会减小，历史权重梯度矩阵 \\(W_{i} l(i\u0026lt;t)\\) 会逐渐减小。通俗来讲，会逐渐遗忘越旧的权重梯度矩阵。\n","date":"2025-07-12T00:00:00Z","permalink":"/sgd-momentum/","title":"SGD-Momentum"},{"content":"前置阅读：MHA 、MQA、GQA 和 rope\n在标准的 Transformer中，多头注意力（Multi-Head Attention, MHA）机制通过并行计算多个注意力头来捕捉输入序列中的不同特征。每个注意力头都有自己的查询（Query, Q）、键（Key, K）和值（Value, V）矩阵，计算过程如下：\n查询矩阵 Q：用于计算输入序列中每个位置的注意力权重。 键矩阵 K：用于与查询矩阵 Q 计算注意力分数。 值矩阵 V：用于根据注意力分数加权求和，得到最终的输出。 MLA 的核心思想是通过低秩联合压缩技术，减少训练时的激活占用和推理时的 kv cache 的占用，从而节省显存。\n核心思想 当前我要存的K cache是4个k_head，但如果我能从这4个k_head中抽取出1份共有的信息，然后在做attn计算时，每个head都用这1份共有的信息做计算，那么我也只需存这1份共有信息作为K cache了。这样我就把K cache从原来num_heads = 4变成num_heads = 1，这不就能节省K cache了吗？ 但是等等，现在共有的k_head信息是抽取出来了，那么相异的k_head信息呢？（简单来说，就是由 \\(W_{K}\\) 不同head部分学习到的相异信息）。我们当然是希望k_head间相异的信息也能保留下来，那么该把它们保留至哪里呢？当你回顾attn_weights的计算公式时，一个想法在你脑中闪现：q部分不是也有heads吗！我可以把每个k_head独有的信息转移到对应的q_head上吗！写成公式解释就是： 原来 \\(attention\\_weights=(W_{Q}h_{i})^T * (W_{k}h_{j})\\) ，括号表示运算顺序，即先各自算2个括号内的，再做 * 计算 现在 \\(attention\\_weights=(h_{i}^TW_{Q}W_{k})^T * h_{j}\\)，同理括号表示运算顺序。也就是说，这里我们通过矩阵乘法的交换律，巧妙地把1个token上k_heads独有的信息转移到了对应的q_head上来，这样1个token上k_heads间共享的相同信息就能被我们当作K cache存储下来。\n（在这里，你可以抽象地把 \\(h_{j}\\) 理解成是4个k_heads共享的信息，但最终K cache的形式还会在这基础上有所变化。我知道此时你脑海中一定有很多疑惑。但我们先不要纠结细节的问题，因为在后文会展示全部细节，这里我们要做的是从宏观上理解MLA设计的核心思想。）\n上述叙述来自再读MLA，还有多少细节是你不知道的\n因此 MLA 的核心思想就是找到一个压缩的 \\(h_{j}\\) 来表示所有 head 的共有信息，而将 head 之间相异信息让 q 来吸收，这样就大大压缩了 k 的 head_num，v 也是同理，不同是 \\(W_{V}\\) 和 \\(W_{O}\\) 吸收。因此 MLA 并没有损失信息，而是将信息转移到了 q_head 上。总维度并没有减少。\n苏神思路 我认为先看苏神的博客再去看猛猿大佬的博客是比较容易理解的。\n苏神认为 MLA 是在 GQA 的基础上将简单的线性变换（分割、复制）换成一般的线性变换来增强模型的能力。GQA 也可以看作是一种低秩投影。原因见原文：\n其中 g 是组数。这里的 c 只是简单的拼接，MLA 的初始想法就是使用一种线性转换代替拼接。这个线性转换也是一个可以学习的矩阵 \\(W\\)。因此一个自然的想法就出来了： 然而这种方法在推理阶段并不会减少 kv cache 的显存占用，我们还是需要存储 k 和 v 而不是 c，并且 kv cache 大小反而和 MHA 一样大了。这时引入一个恒等变换就可以解决问题： 这样就可以把 c 取代原来的 k，而把两个 W 当作新的 Q，同理后面可以把 Wv 和 Wo 合并，v 也可以用 c 取代。这个 c 就是上面说的所有 head 的共享信息。而相异信息放入了新的 Q 中来学习。这样在推理阶段就可以只保存 c 来作为 kv cache 了。所以可以降低 c 的维度来进一步降低显存占用。\n现在成功降低了显存占用，但有了一个新问题，如何在这里引入 rope 呢，众所周知 rope 是在计算注意力分数时加入了位置信息。如果强行加入则会产生下面的问题：\nimage.png 也就是新的 Q 矩阵与 i 有关，而不是固定的一个矩阵了。\n解决的办法也简单粗暴，就是在 q 和 k 的维度上增加 rope 的维度。也就是：\nimage.png 其中前半部分是 nope，后半部分是 rope，这个在后面猛猿大佬讲解的源码中会有体现。 最后为了降低激活值，又对 q 进行了低秩投影，最后变成了： 至于为什么 q 的 rope 项是 c，而 k 的 rope 项是 x，这个我也不清楚。在训练阶段，MLA 只是在 MHA 的基础上加了低秩投影，以及为了 rope 的计算在原本 dk 的维度上增加了 dr 维度。\n区别在于解码阶段，MLA 可以像 MQA 一样简单： 在解码阶段，只需要保存 c（注意 c 是 k 和 v 的集合体），这大大降低了显存占用。苏神博客写 prefill 阶段使用 (10)，generation 阶段使用 (12)，因为 prefill 是 compute-bound，需要降低计算量，而 generation 阶段是 memory-bound，需要降低显存占用，这样结合完美符合我们的需求。\n猛猿大佬思路 一切从下面这张图说起，这是对源码实现逻辑的抽离： (图中的 kv_b_proj 维度标注错误，应该为(nope+v_head_dim)，这里的 v_head_dim=nope)\n从图中可以看到，此时 q_len 为 1，kv_len 为 1024，q_lora_rank 大小为 1536，而 kv_lora_rank 大小为 512，我们可以观察到左路和右路的计算流程不太一样，这是因为上面公式 (10) 中 q 和 k 的 rope 部分使用的不一样，q 使用的是压缩后的 c，而 k 使用的是原始 x, 因此在 x 为输入的时候 kv 部分就可以将 rope 部分算出来，而 q 部分需要将压缩后的 q 得到后才可以计算。q 是将 PE 和 NoPE split 开，而 kv 是将 NoPE 的 k 和 v split 开。\n需要注意的是，在 nope 中，head_dim 为 128，而这大于 hsz // num_heads，猛猿大佬猜测这是为了提高模型的复杂度，因为推理的时候通过只保存压缩后的 kv 来减少了 kv cache 占用，那么训练的时候就可以稍微提高复杂度。\n下面的代码是上图中的一些定义。结合苏神的推导过程相信可以理解 MLA。\nclass DeepseekV2Attention(nn.Module): \u0026quot;\u0026quot;\u0026quot;Multi-headed attention from \u0026#39;Attention Is All You Need\u0026#39; paper\u0026quot;\u0026quot;\u0026quot; def __init__(self, config: DeepseekV2Config, layer_idx: Optional[int] = None): super().__init__() self.config = config self.layer_idx = layer_idx self.attention_dropout = config.attention_dropout self.hidden_size = config.hidden_size self.num_heads = config.num_attention_heads self.head_dim = config.head_dim self.max_position_embeddings = config.max_position_embeddings self.rope_theta = config.rope_theta self.q_lora_rank = config.q_lora_rank self.qk_rope_head_dim = config.qk_rope_head_dim self.kv_lora_rank = config.kv_lora_rank self.v_head_dim = config.v_head_dim self.qk_nope_head_dim = config.qk_nope_head_dim self.qk_head_dim = config.qk_nope_head_dim + config.qk_rope_head_dim self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads self.is_causal = True if self.q_lora_rank is None: self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.qk_head_dim, bias=False) else: self.q_a_proj = nn.Linear(self.hidden_size, config.q_lora_rank, bias=config.attention_bias) self.q_a_layernorm = DeepseekV2RMSNorm(config.q_lora_rank) self.q_b_proj = nn.Linear(config.q_lora_rank, self.num_heads * self.qk_head_dim, bias=False) self.kv_a_proj_with_mqa = nn.Linear( self.hidden_size, config.kv_lora_rank + config.qk_rope_head_dim, bias=config.attention_bias, ) self.kv_a_layernorm = DeepseekV2RMSNorm(config.kv_lora_rank) self.kv_b_proj = nn.Linear( config.kv_lora_rank, self.num_heads * (self.qk_head_dim - self.qk_rope_head_dim + self.v_head_dim), bias=False, ) self.o_proj = nn.Linear( self.num_heads * self.v_head_dim, self.hidden_size, bias=config.attention_bias, ) self.scaling = self.qk_head_dim ** (-0.5) 参考 缓存与效果的极限拉扯：从MHA、MQA、GQA到MLA - 科学空间|Scientific Spaces\n# Multi-Head Latent Attention (MLA) 详细介绍（来自Deepseek V3的回答）\n再读MLA，还有多少细节是你不知道的\nmodeling_deepseek.py · deepseek-ai/DeepSeek-V2 at main\n","date":"2025-07-11T00:00:00Z","permalink":"/mla/","title":"MLA"},{"content":"Muon 算法流程如下图所示：\nimage.png 其中最主要的部分是 NewtonSchulz 5 算法，流程如下：\ndef newtonschulz5(G, steps=5, eps=1e-7): assert G.ndim == 2 a, b, c = (3.4445, -4.7750, 2.0315) X = G.bfloat16() X /= (X.norm() + eps) if G.size(0) \u0026gt; G.size(1): X = X.T for _ in range(steps): A = X @ X.T B = b * A + c * A @ A X = a * X + B @ X if G.size(0) \u0026gt; G.size(1): X = X.T return X 这个算法的作用是将 G 近似为一个最接近他的半正交矩阵，即：\nimage.png 对于经验动机，我们观察到，基于手动检查，SGD-momentum 和 Adam 对基于 Transformer 的神经网络中的 2D 参数产生的更新通常具有非常高的条件数。也就是说，它们几乎是低秩矩阵，所有神经元的更新仅由几个方向主导。我们推测正交化有效地增加了其他“罕见方向”的规模，这些方向在更新中幅度较小，但对学习仍然很重要。\nMuon in Moonlight 来自Muon续集：为什么我们选择尝试Muon？ - 科学空间|Scientific Spaces ### Weight Decay\nkimi团队研究发现如果不加上权重衰减收敛速度到后面会被adam追上，因此加上了权重衰减：\n\\[ \\Delta W =-\\eta[msign(M) + \\lambda W] \\]\n总的来说，这种做法可以缓解MaxLogit爆炸的问题，因为qk相乘的结果和xq、xk以及Wq和Wk有关，x会经过rmsnorm，所以爆炸的原因来自于W的爆炸，所以权重衰减可以缓解这个问题。\nRMS对齐 我理解为这是一种将Adam调好的超参数用到其它优化器的方法。\nQK-clip QK-norm可以很好的压制MaxLogit，但它只适用于MHA、GQA，不适用于MLA的推理阶段。因为推理阶段的Wk被吸收到了Q中。\n这时候就需要返璞归真，既然MaxLogit太大，那就设定一个阈值，当Logit的值超过阈值的时候，就 直接裁剪到这个阈值。\n这就有了他们最初始的想法（因为Logit是\\(QK^T\\)，目的是在\\(QK^T\\)上进行裁剪，所以要对各自的参数矩阵裁剪sqrt。）：\nimage.png 如果max_logit（在batch上也要找最大）大于阈值并且即将优化的参数是q或者k的线性矩阵参数，就将矩阵参数进行clip，来达到缩放max_logit的目的。\n但后面他们发现这样一刀切很容易伤及无辜，因为多头注意力中，有可能只有1个头出现了max_logit超出阈值，但其它头的参数也会被裁减。\n我们知道，不管哪种Attention变体都有多个Head，一开始我们是每一层Attention只监控一个MaxLogit指标，所有Head的Logit是放在一起取Max的，这导致QK-Clip也是所有Head一起Clip的。然而，当我们分别监控每个Head的MaxLogit后发现，实际上每层只有为数不多的Head会出现MaxLogit爆炸，如果所有Head按同一个比例来Clip，那么大部份Head都是被“无辜受累”的了，这就是过度裁剪的含义。\n简单来说，QK-Clip的操作是乘以一个小于1的数，这个数对于MaxLogit爆炸的Head来说是刚刚好抵消增长趋势，但是对于其他head来说是单纯的缩小（它们没有增长趋势或者增长趋势很弱）。由于长期无端被乘一个小于1的数，那么很容易出现就趋于零的现象，这是“过度裁剪”的表现。\n因此还需要监控各个头的max_logit，如果某个头出现了这个问题，那么就单独对这个头的参数矩阵进行裁剪。但这里有一个问题就是，对于MLA而言，并不是简单的存在Wq和Wk，而是Wqc、Wkc、Wqr、Wkr，而Wkr是所有的head共享的，如果裁剪Wkr也会导致出现无辜头，所以只需要裁剪Wqr。\n所以最终版本如下：\nimage.png 将QK-clip应用到Muon优化器就变成了Muon-Clip：\nimage.png image.png 对比原生Muon可见改进了蛮多。 ","date":"2025-07-11T00:00:00Z","permalink":"/muon/","title":"Muon"},{"content":"为什么存储激活值？ 首先回顾为什么要存储激活值。 简单来说，模型参数是根据导数更新的。为了有效地计算这些导数，必须缓存某些张量。激活内存是这些缓存张量的内存成本。 具体来说，以 \\(f\\) 是矩阵乘法运算：\n\\[y=f(x)=W\\cdot x\\] \\(W\\)是一个可学习的权重矩阵。假设我们有关于 早期反向传播阶段的手头输出，\\(\\frac{\\partial L}{\\partial y}\\),我们 需要计算 两个额外的梯度： 1.关于\\(W\\),以便我们可以更新此权重。 2.关于\\(x\\),这样我们就可以继续反向传播算法 到产生的任何作\\(x\\)\n前导数是\n\\[\\frac{\\partial L}{\\partial W}=\\frac{\\partial L}{\\partial y}\\frac{\\partial y}{\\partial W}=\\frac{\\partial L}{\\partial y}\\times x\\]\n而后者的导数是\n\\[\\frac{\\partial L}{\\partial x}=\\frac{\\partial L}{\\partial y}\\cdot W\\]\n因此，如下图所示，我们需要缓存输入张量 \\(x\\) 为了能够计算我们关心的导数。节省的成本 \\(x\\) 是激活 memory。 我们发现只需要保存 x，而对于 y 不需要保存，也就是说我们只保存反向传播时绝对需要的激活值，其它的临时变量要立即释放。\nMLP class MLP(nn.Module): \u0026quot;\u0026quot;\u0026quot; Basic MLP (multi-layer perceptron) layer with optional Dropout. \u0026quot;\u0026quot;\u0026quot; def __init__( self, d_model: int, act_fn: nn.Module, dropout_prob: Optional[float] = None, device: Optional[Union[str, torch.device]] = None, dtype: Optional[torch.dtype] = None, ) -\u0026gt; None: super().__init__() self.d_model = d_model self.act_fn = act_fn self.dropout_prob = dropout_prob factory_kwargs = {\u0026quot;device\u0026quot;: device, \u0026quot;dtype\u0026quot;: dtype} self.lin_0 = nn.Linear(self.d_model, 4 * self.d_model, **factory_kwargs) self.lin_1 = nn.Linear(4 * self.d_model, self.d_model, **factory_kwargs) self.dropout = nn.Dropout(self.dropout_prob) if self.dropout_prob else None def forward(self, inputs: torch.Tensor) -\u0026gt; torch.Tensor: x = self.lin_0(inputs) x = self.act_fn(x) x = self.lin_1(x) if self.dropout is not None: x = self.dropout(x) return x image.png Maybe saved 的是否 是根据激活函数的类型来的： 如果是 GELU 激活函数\n\\[y=\\frac x2\\times\\tanh\\left(\\sqrt{\\frac2\\pi}\\left(x+.044715x^3\\right)\\right)\\] 这时候计算梯度的话还是需要输入 x，这是需要存储的情况\n而如果是一些特殊的激活函数的话，比如 ReLU\n\\[y=\\mathsf{ReLU}(x)=\\begin{cases}x\u0026amp;\\mathrm{if~}x\u0026gt;0\\\\0\u0026amp;\\mathrm{if~}x\u0026lt;0\u0026amp;\\end{cases}\\]\n梯度为：\n\\[\\frac{dy}{dx}=\\frac{d\\text{ ReLU}(x)}{dx}=\\begin{cases}1\u0026amp;\\mathrm{if~}x\u0026gt;0\\\\0\u0026amp;\\mathrm{if~}x\u0026lt;0\u0026amp;\\end{cases}\\]\n就不需要存储。 Tanh 也是同理：\n\\[ \\frac{dy}{dx} = \\frac{d\\tanh (x)}{dx} = 1-\\tanh(x)^2 = 1-y^2 \\]\n如果想要性能，选择 GELU，想要低显存，使用 ReLU。\n激活值计算 根据博客 # 分析transformer模型的参数量、计算量、中间激活、KV cache 内容来得到激活值的计算公式。\n大模型在训练过程中通常采用混合精度训练，中间激活值一般是float16或者bfloat16数据类型的。在分析中间激活的显存占用时，假设中间激活值是以float16或bfloat16数据格式来保存的，每个元素占了2个bytes。唯一例外的是，dropout操作的mask矩阵，每个元素只占1个bytes。在下面的分析中，单位是bytes，而不是元素个数。\n先分析 self-attention 块的中间激活。Self-attention 块的计算公式如下： \\[Q=xW_Q,K=xW_K,V=xW_V\\]\n\\[x_{out}=softmax(\\frac{QK^T}{\\sqrt{h}})\\cdot V\\cdot W_o+x\\]\n对于 \\(Q,K,V\\) ,需要保存它们共同的输入 \\(x\\) ,这就是中间激活。输入 \\(x\\) 的形状为 \\([b,s,h]\\) ,元素个数为 bsh ,占用显存大小为 \\(2*bsh=2bsh\\) 。 对于 \\(QK^T\\) 矩阵乘法，需要保存中间激活 \\(Q,K\\) ,两个张量的形状都是 \\([b,s,h]\\) ,占用显 存大小合计为 \\(2*2*bsh=4bsh\\) 。 对于 \\(softmax()\\) 函数，需要保存函数的输入 \\(QK^T\\) ,占用显存大小为 \\(2bs^2a\\) ,这里的 \\(a\\) 表示注意力头数。 \\[score=softmax(\\frac{QK^T}{\\sqrt{d_k}})\\]\n\\(Q\\) 的形状为：\\([ b, head\\_ num, s, per\\_ head\\_ hidden\\_ size]\\) \\(K^T\\) 的形状为：\\([b,head\\_num,per\\_head\\_hidden\\_size,s]\\) \\(QK^T\\) 的形状为：\\([b,head\\_num,s,s]\\) ,元素个数为 \\(bs^2a\\) ,占用显存大小为 \\(2bs^2a\\) 。 4. 计算完 \\(softmax()\\) 函数后，会进行 dropout 操作。需要保存一个 mask 矩阵，mask 矩阵的形状与 \\(QK^T\\) 相同，占用显存大小为 \\(bs^2a\\) 。 5. 计算在 \\(V\\) 上的 attention，即 \\(score\\cdot V\\), 需要保存 score ,大小为 \\(2bs^2a\\) ;以及 \\(V\\) ,大小为 \\(2bsh\\) 。二者占用显存大小合计为 \\(2bs^2a+2bsh\\) 。 6. 计算输出映射以及一个 dropout 操作。输入映射需要保存其输入，大小为 \\(2bsh\\) ;dropout 需要保存 mask 矩阵，大小为 bsh 。二者占用显存大小合计为 \\(3bsh\\) 。 因此，将上述中间激活相加得到，self-attention 块的中间激活占用显存大小为 \\(11bsh+5bs^2a\\) 。\n接下来看 MLP 块的中间激活。MLP 块的计算公式如下：\n\\[x=f_{gelu}(x_{out}W_1)W_2+x_{out}\\]\n第一个线性层需要保存其输入，占用显存大小为 \\(2bsh\\) 。 激活函数需要保存其输入，占用显存大小为 \\(8bsh\\) 。 第二个线性层需要保存其输入，占用显存大小为 \\(8bsh\\) 。 最后有一个 dropout 操作，需要保存 mask 矩阵，占用显存大小为 bsh 。 对于 MLP 块，需要保存的中间激活值为 19 bsh 。 另外，self-attention 块和 MLP 块分别对应了一个 layer normalization。每个 layer norm 需要保存其输入，大小为 \\(2bsh\\) （忽略了均值和方差的2 bs，如果不忽略应当为 2 bsh+4 bs，这里的单位都是 bytes）。2 个 layer norm 需要保存的中间激活为 \\(4bsh\\) 。 综上，每个 transformer 层需要保存的中间激活占用显存大小为 \\(34bsh+5bs^2a\\)。对于 \\(l\\) 层 transformer 模型，还有 embedding 层、最后的输出层。Embedding 层不需要中间激活。总的而言，当隐藏维度 \\(h\\) 比较大，层数 \\(l\\) 较深时，这部分的中间激活是很少的，可以忽略。因 此，对于 \\(l\\) 层 transformer 模型，中间激活占用的显存大小可以近似为 \\((34bsh+5bs^2a)*l\\) Activation checkpointing 当使用该方法时，我们只需要保存一些关键的激活值，可以舍弃一些激活值从而在反向传播的过程中重新计算，通常有两种策略：\nFull：我们在 Transformer 模型每一层之间的过渡点检查激活值。这通常被称为“完整”策略，因为它需要每层都进行一次前向传播，即在反向传播过程中增加了一次完整的前向传播。这种策略节省的内存最多，但在计算方面成本最高。它通常会使计算成本和时间增加高达 30-40%，这一影响非常显著。\nSelect：总体而言，我们可以比全面优化做得更好。那篇关于重新计算的论文的作者进行了详细分析，研究了哪些激活值的增长最大，并且以每秒浮点运算次数（FLOPS）为标准，其重新计算成本最低。结果表明，注意力计算属于这一类别，因此我们通常可以丢弃它们，而专注于对昂贵的前馈计算进行检查点设置。对于 GPT-3（1750 亿参数）模型而言，这意味着在计算成本仅增加 2.7%的情况下，激活值内存减少了 70%。\n如今，大多数训练框架都使用 FlashAttention，它通过在反向传播中重新计算注意力得分和矩阵，而不是存储它们，将激活重新计算集成到其优化策略中。因此，大多数使用 FlashAttention 的人已经在使用 AC。 由于重新计算，激活重新计算略微增加了 FLOPS 的数量，同时显著降低了内存访问开销。 这种权衡在 GPU 等高速内存有限的硬件上特别有利，因为访问内存通常比执行计算慢。尽管涉及额外的操作，但总体效果通常是计算速度更快，内存占用更少。\n1. Full Activation Checkpointing (全量激活值检查点) 这是最经典、最直接的检查点方法。\n工作原理： 分段: 将整个模型的计算图（所有层）在逻辑上划分为若干个段 (Segment)。 保存: 在前向传播时，只保存每个段的输入张量 (Input Tensor)。这些被保存的张量就是“检查点”。段内所有中间层的激活值都会在计算后被立即丢弃，不占用显存。 重算: 在反向传播时，当需要某个段内的激活值来计算梯度时，它会找到该段的检查点（即该段的输入），然后重新执行该段的前向传播，以恢复所需要的激活值。一旦这个激活值被使用完毕，它会再次被丢弃。 图解： 假设一个模型有16层，我们每4层设置一个检查点：\n标准训练 (无Checkpointing): [Input] -\u0026gt; L1 -\u0026gt; L2 -\u0026gt; ... -\u0026gt; L16 -\u0026gt; [Output] - 前向传播: 计算并存储 L1 到 L16 的所有激活值。 - 内存占用: 极高。\nFull Activation Checkpointing: [Input] -\u0026gt; [Segment 1: L1-L4] -\u0026gt; [Segment 2: L5-L8] -\u0026gt; [Segment 3: L9-L12] -\u0026gt; [Segment 4: L13-L16] -\u0026gt; [Output] - 前向传播: - 执行 Segment 1 (L1-L4)，只保存 L5 的输入（即L4的输出）作为检查点，丢弃 L1-L3 的激活值。 - 执行 Segment 2 (L5-L8)，只保存 L9 的输入作为检查点，丢弃 L5-L7 的激活值。 - …以此类推。 - 反向传播: - 当需要计算 L12 的梯度时，发现它的激活值没有被保存。 - 系统加载最近的检查点（L9的输入）。 - 重新计算 L9 -\u0026gt; L10 -\u0026gt; L11 -\u0026gt; L12 的前向传播，得到 L12 的激活值。 - 使用该激活值计算梯度，然后丢弃它。\n优缺点： 优点: 效果显著: 可以大幅度降低显存占用，内存占用量与模型的层数基本无关，只与最长的那个段的计算复杂度有关。 实现简单: 逻辑清晰，易于在各种框架中实现（例如 PyTorch 的 torch.utils.checkpoint）。 缺点: 计算开销大: 每个被 checkpoint 的段（除了最后一个）都会被重新计算一次。如果模型很大，这会带来大约 30%-50% 甚至更高的训练时间开销。 2. Selective Activation Checkpointing (选择性激活值检查点) 这是对全量检查点方法的智能化升级，也是目前更受关注的重点。它认识到“全量丢弃、全量重算”的策略过于粗暴和低效。\n核心思想： 在同一个计算段内，不同的操作（Op）或激活值，其存储成本和重算成本是不同的。\n有些激活值占用显存大，但重算很快（例如 ReLU, Dropout 等元素级操作）。 有些激活值占用显存小，但重算很慢（例如 MatMul, Convolution 的输出）。 Selective Checkpointing 的目标就是：在每个段内，不再丢弃所有激活值，而是有选择性地保存那些“重算成本高、存储成本低”的激活值，从而在反向传播时，避免代价高昂的重计算。\n工作原理： 成本分析: 它需要对计算图中的每个操作进行成本分析。 存储成本: 该操作输出的激活值张量占用的显存大小。 重算成本: 重新计算出这个激活值所需要的时间（通常用 FLOPs 衡量）。 智能决策: 基于成本分析，算法（通常是动态规划或启发式搜索）会做出决策。对于段内的每一个激活值，它会判断： 是直接保存它更划算？ 还是丢弃它，之后再通过重计算恢复更划算？ 选择性保存与重算: 在前向传播时，除了保存每个段的输入（主检查点）外，还会额外保存段内那些被判定为“值得保存”的少量关键激活值。 在反向传播时，当需要一个激活值时，如果它被保存了，就直接使用。如果没被保存，系统会从最近的一个检查点（无论是主检查点还是段内保存的次级检查点）开始重算，而不是必须从段的开头开始。 图解（续上例）： 在 Segment 3 (L9-L12) 中： L9 (MatMul) -\u0026gt; L10 (LayerNorm) -\u0026gt; L11 (ReLU) -\u0026gt; L12 (MatMul)\nFull Checkpointing 会丢弃 L9, L10, L11 的所有输出。重算 L12 时需要从 L9 的输入开始，重新执行 MatMul, LayerNorm, ReLU。 Selective Checkpointing 可能会做出如下决策： L9 (MatMul) 的输出：重算成本极高，但存储成本可能相对可控。决策：保存。 L11 (ReLU) 的输出：占用显存和 L10 一样大，但重算成本极低（只需对 L10 的输出再做一次 ReLU）。决策：丢弃。 在反向传播时： - 当需要 L11 的激活值时，发现它没被保存。但系统发现 L10 的激活值（或者 L9 的）被保存了。 - 它只需从 L10 开始重算 L10 -\u0026gt; L11 (ReLU)，而无需重算昂贵的 L9 (MatMul)。\n优缺点： 优点: 最佳平衡: 在实现与 Full Checkpointing 几乎相同显存节省效果的同时，显著降低了重计算的开销，从而缩短了训练时间。它在“时间”和“空间”之间找到了一个更优的平衡点。 高效: 相比 Full Checkpointing，训练速度更快。 缺点: 实现复杂: 需要对模型的计算图进行深入分析，并建立准确的成本模型。这通常需要深度学习框架或特定库（如 DeepSpeed）的底层支持。 模型依赖: 优化的效果依赖于模型结构。对于包含大量昂贵操作的模型，效果会更明显。 总结对比 特性 Full Activation Checkpointing Selective Activation Checkpointing 核心策略 以时间换空间 在时间和空间之间寻找最优平衡 粒度 粗粒度 (段级别) 细粒度 (操作级别) 保存内容 仅保存每个段的输入 保存每个段的输入 + 段内部分关键激活值 重算方式 总是从段的开头重算整个段 从最近的可用检查点开始重算，路径更短 计算开销 高 较低 显存节省 非常高 非常高 (与 Full 类似) 实现复杂度 低 高 总而言之，Selective Activation Checkpointing 是对传统检查点技术的一次重大优化。它通过更精细化的资源管理，在不牺牲太多内存节省的前提下，大幅减少了因重计算带来的时间惩罚，是目前训练超大规模模型时更为先进和高效的主流技术之一。 ## 参考 Activation Memory: A Deep Dive using PyTorch | Determined AI\n","date":"2025-07-10T00:00:00Z","permalink":"/activation-checkpointing/","title":"Activation checkpointing"},{"content":"CPU 卸载允许我们将一些状态传输到 CPU，因此我们不必将所有内容都保存在 GPU RAM 中。虽然 CPU作比 GPU作慢，但将不常访问的数据移动到 CPU 内存可以帮助我们保持在 GPU 内存限制范围内。\n","date":"2025-07-10T00:00:00Z","permalink":"/cpu-offloading/","title":"CPU offloading"},{"content":"论文综合深度解读 这篇论文的核心贡献，在于它揭示并严谨地证明了一个深刻的“二象性”：大型语言模型（LLM）的训练过程，不仅是在学习“如何生成文本”（模仿），同时也是在隐式地学习“如何评价文本的好坏”（奖励）。作者将这种内在于 LLM 自身、无需额外训练即可提取的奖励信号，命名为“内生奖励”（Endogenous Reward）。这彻底改变了我们对 LLM 对齐（Alignment）的传统认知。\n在当前主流的从人类反馈中强化学习（RLHF）流程中，研究者需要先收集大量的人类偏好数据（比如，对于同一个问题，标注哪个答案更好），然后用这些数据专门训练一个奖励模型（RM）。这个 RM 像一个“外部裁判”，负责给 LLM 生成的所有回答打分。最后，LLM 再根据这个“裁判”的打分，通过强化学习来调整自己的行为，让自己变得更“有用、无害、诚实”。这个过程虽然有效，但其最大的瓶颈在于“训练裁判”这一步——它极其依赖昂贵、耗时且难以规模化的人工标注数据。\n为了解决这个问题，业界探索了从 AI 反馈中强化学习（RLAIF），即用一个更强大的闭源模型（如 GPT-4）来充当“AI 裁判”，代替人类进行标注。但这更像是一种“启发式”的工程实践，缺乏严谨的理论根基，并且容易让学生模型继承老师模型的偏见和风格。这就引出了一个根本性的问题：我们真的必须从外部寻找一个“裁判”吗？\n这篇论文给出了一个石破天惊的答案：不必，裁判就在模型自己心中。\n作者的论证逻辑如同一场精彩的推理。他们首先将 LLM 的常规训练方式——“下一个词元预测”（next-token prediction），与一个经典的机器学习范式——“模仿学习”（Imitation Learning）联系起来。在模仿学习的视角下，LLM 其实是在模仿海量文本数据（专家示例）的行为。\n紧接着，他们引入了另一个更深刻的理论工具——“逆向强化学习”（Inverse Reinforcement Learning, IRL）。IRL 的核心思想是，既然我们能观察到专家的行为，那么我们应该可以反推出专家内心遵循的那个“奖励函数”。也就是说，专家之所以会这么做，一定是因为他认为这么做能得到最高的奖励。\n论文最关键的理论突破（命题 1, Proposition 1），就是证明了 LLM 的“下一个词元预测”训练目标，在数学上等价于一种名为“离线逆向软 Q 学习”（offline inverse soft Q-learning）的 IRL 问题的解。这个证明的直接推论是：\n一个经过标准训练的 LLM 的 logits（即模型在输出每个词元之前的原始打分），其本身就是这个 IRL 问题所要寻找的“软 Q 函数”（soft Q-function）。\n这个结论是整篇论文的基石。Q 函数在强化学习中代表了在某个状态下采取某个动作的长期价值。既然 LLM 的 logits 就是 Q 函数，那么根据强化学习的贝尔曼方程，我们就可以通过一个简单的数学变换（逆向软贝尔曼算子, inverse soft Bellman operator），直接从 logits 中“解码”出那个隐含的、逐词元的奖励值r。这就是内生奖励的由来。\nR (sh, ah) := Q(sh, ah) – α log (∑ exp (Q(sh+1, ah+1)) / α) An+1∈V\n这个公式（原文公式 7）展示了如何从当前状态-动作的 Q 值（即 logits）和下一步所有可能状态的 Q 值中计算出当前的奖励 r。简单来说，一个词元的奖励 = 模型选择这个词元的倾向性（logits） - 模型对生成这个词元后未来所有可能性的平均预期。这种方法完全是“训练-免费”（training-free）的，它不需要任何新的参数，也不需要任何偏好数据，仅仅是利用了模型已有的知识。\n更令人信服的是，论文不仅仅停留在理论层面，还给出了严格的误差分析（定理 2, Theorem 2）。它证明了，使用这种内生奖励进行强化学习微调后的新模型，其最终的策略误差界是与生成长度 H 成线性关系 O (H)的。而传统的模仿学习，由于误差会逐词元累积，其误差界是与 H 成二次方关系 O (H²)的。这意味着，论文提出的方法能够从根本上缓解模仿学习中的“复合误差”问题，从而得到一个性能更优越的模型。\n实验部分也充分验证了这一理论。在奖励模型的评测基准 RM-Bench 上，该方法（EndoRM）的平均准确率达到了70.2%，不仅远超其他所有“训练-免费”的基线方法，甚至可以媲美乃至超越那些使用大量数据专门训练的、最先进的奖励模型（例如，Skywork-Reward-Llama-3.1-8 B 的准确率为 70.1%）。\n总而言之，这篇论文完成了一次华丽的“理论回归”。它将 LLM 对齐从依赖昂贵数据和工程技巧的“术”，升华到了一个有坚实理论基础、更高效、更可扩展的“道”的层面。它告诉我们，对齐的关键或许不是向外寻求一个“更聪明的老师”，而是向内挖掘模型自身已经蕴含的“评价智慧”。\n接下来，我将针对您的六个问题进行详细解读。\n### 论文的研究目标与意义 研究目标：论文的核心研究目标是寻找一种无需额外训练、不依赖外部偏好数据，即可为大型语言模型提供高质量奖励信号的方法。\n解决的实际问题：\n高昂的对齐成本：解决当前 RLHF 流程中，为训练奖励模型而收集人类偏好数据集所带来的巨大时间、金钱和人力成本问题。 理论基础的缺失：解决 RLAIF 等方法虽然降低了成本，但本质上是启发式的，缺乏严格的理论证明，且容易引入“裁判”模型的偏见。 对齐的可扩展性瓶颈：传统的对齐方法在向多模态领域（如图像、视频）扩展时，收集偏好数据的难度呈指数级增长。论文试图提供一个更具可扩展性的对齐范式。 对行业发展的重要意义：\n降低 AI 对齐门槛：如果该方法被广泛采纳，将极大降低中小企业和研究机构开发和对齐高性能 LLM 的成本和技术门槛，促进 AI 领域的创新和民主化。 加速模型迭代：它将传统的“预训练-SFT-RM 训练-RLHF”四阶段流程，简化为“预训练-SFT-RL”三阶段流程，省去了独立的 RM 训练阶段，从而缩短了模型的开发和部署周期。 推动对齐理论的发展：论文为 LLM 对齐提供了全新的、基于 IRL 的理论框架，将启发学界从更深层次的理论（如因果、世界模型）来理解和解决对齐问题，而不仅仅停留在工程层面。 ### 新思路、方法与优势 新思路：核心思路是范式转换，即从“构建一个外部奖励模型”转向“从模型内部发现一个内生奖励模型”。它假设 LLM 在学习生成文本的同时，已经隐式地学会了评价文本质量的准则。\n新方法或模型：\n理论连接：首次建立了下一个词元预测目标与离线逆向强化学习（IRL）之间的直接数学等价关系。 内生奖励提取（EndoRM）：提出了一种具体、无训练的算法来提取内生奖励。该方法将模型的logits直接视为强化学习中的软 Q 函数。 奖励计算：通过逆向软贝尔曼算子（见上文公式），从 Q 函数（logits）中计算出每个词元的奖励。 \u0026gt; “We can simply take its logits, Q = f˜, and substitute them into the inverse soft Bellman operator from Eq. (7).” \u0026gt; 简单来说，模型每一步生成一个词元的奖励，等于它对这个词元的原始偏好度（logit），减去生成这个词元后，对未来所有可能生成序列的价值期望。 特点与优势：\n无训练（Training-Free）：与需要训练数百万参数的传统 RM 相比，该方法不引入任何新参数，也无需训练，极大地节省了计算资源。 数据高效：完全摆脱了对人类或 AI 偏好数据集（preference datasets）的依赖。 理论完备（Theoretically Grounded）：与 RLAIF 的启发式不同，该方法有严格的 IRL 理论作为支撑，并提供了误差分析，证明了其在纠正模仿学习复合误差上的优越性。 可控与可解释：如实验所示，内生奖励是可提示（promptable）的。通过在输入中提供不同的指令（system prompt），可以让同一个模型在评价时遵循不同的标准（例如，学术写作标准 vs. 娱乐写作标准），这为个性化对齐提供了极大的灵活性。 ### 实验验证与结果 论文设计了三个环环相扣的实验，以回答三个核心研究问题（Q 1, Q 2, Q 3）：\n实验设计： Q 1: EndoRM 的性能如何？ 在权威的奖励模型评测基准 RM-Bench 和 Multifaceted-Bench 上，将 EndoRM 与多种基线进行比较，包括其他无训练方法（如 Generative Verifier）和需要专门训练的 SOTA 奖励模型。评价指标是分类准确率（即判断一对回答中哪个更优的能力）。 Q 2: EndoRM 是否具备指令遵循能力？ 使用 DSP 数据集，该数据集包含四个不同领域（学术、商业、文学、娱乐）的偏好。实验中，为 EndoRM 提供特定领域的指令，然后测试它在所有四个领域上的评价准确率，观察其是否在“被指令”的领域表现最好。 Q 3: 使用 EndoRM 进行 RL 微调能否提升模型性能？ 在数学推理任务上，以Qwen 2.5-Math-7 B为基础模型，首先测试其原始性能。然后，使用该模型自身的 EndoRM 作为奖励信号，进行强化学习微调（RLFT），再测试微调后模型的性能，看是否有提升。 实验数据与结果： Q 1 结果：EndoRM 表现出色。在 RM-Bench 上，其平均准确率达到70.2%。 引用关键数据：从论文的Table 1可以看出： EndoRM (ours): 70.2% Generative Verifier (无训练基线): 64.6% Skywork/Skywork-Reward-Llama-3.1-8 B (有训练 SOTA): 70.1% NVIDIA/Nemotron-340 B-Reward (有训练 SOTA): 69.5% 这表明，无训练的 EndoRM 不仅碾压了其他无训练方法，甚至在性能上与耗费大量资源训练的专用奖励模型不相上下。 Q 2 结果：实验结果呈现出强烈的对角线模式，证实了 EndoRM 的可提示性。 引用关键数据：从论文的Table 2可以看出： | Domain | Academy | Business | Literature\u0026amp;Art | Entertainment | | :— | :—: | :—: | :—: | :—: | | EndoRM-Academy | 76.89 | 67.17 | 57.45 | 52.57 | | EndoRM-Business | 65.09 | 69.21 | 51.44 | 51.17 | | EndoRM-Literature\u0026amp;Art | 32.00 | 32.94 | 62.97 | 36.12 | | EndoRM-Entertainment | 30.41 | 38.65 | 38.69 | 72.09 | 当给模型“学术”指令时（EndoRM-Academy），它在学术数据集上的准确率最高（76.89%），而在其他领域则较低。这证明了内生奖励不是固定的，而是可以被动态引导的。 Q 3 结果：强化学习微调后，模型在所有数学基准测试上都取得了一致的性能提升。 引用关键数据：从论文的Table 3可以看到，在五个数学基准上的平均分从33.0提升到了38.8 (+5.8)，在 Minerva 和 Olympiad 等难题上提升尤为显著（分别+10.7和+5.8）。这有力地支持了定理 2的理论预测，即该方法能有效提升模型策略的质量。 ### 未来探索方向与挑战 值得探索的问题和挑战： 自洽性偏见的放大（Self-Reinforcing Biases）：这是最大的挑战。如果基础模型本身存在事实性错误、逻辑谬误或社会偏见，使用内生奖励进行强化学习可能会加剧这些偏见，形成一个“自我肯定”的恶性循环。如何引入外部的、稀疏但高质量的信号（如规则系统、事实核查工具）来“校准”内生奖励，是一个关键研究方向。 元问题提示工程（Prompt Engineering of the Meta-Question）：内生奖励的质量高度依赖于给它的“指令”。如何设计出鲁棒、安全、能准确激发所需评价能力的指令，将成为一门新的“提示工程”艺术。 与 DPO 等方法的融合：像 DPO（直接偏好优化）这类方法虽然仍依赖偏好数据，但在实践中非常有效。能否将 EndoRM 与 DPO 结合，例如，用 EndoRM 生成“伪偏好数据”来驱动 DPO 训练，可能是一个兼具理论优雅性和实践有效性的方向。 对基础模型质量的依赖：该方法的前提是基础模型足够好，其“内生智慧”才有价值。对于能力较弱的模型，其内生奖励的质量可能很低。探索该方法性能与模型能力之间的 scaling law 关系很有必要。 可能催生的新技术和投资机会： 新一代 AI 对齐平台：可能会出现新的 AI 开发和对齐工具链，它们不再需要庞大的数据标注团队，而是内置了基于 EndoRM 的“一键对齐”功能，使得 AI 开发流程更敏捷、成本更低。 个性化 AI 服务：由于 EndoRM 的可提示性，可以为每个用户或企业客户动态生成符合其特定价值观和需求的“个性化奖励模型”，从而训练出高度定制化的 AI 助手。这是一个巨大的 B 2 C 和 B 2 B 市场机会。 多模态内容创作与对齐：该方法可以自然地扩展到图像、视频、音乐等自回归生成模型上。这意味着我们可以用同样的方法来“对齐”AIGC 工具，让它们生成更符合人类审美、更有创意、更安全的内容，这将是多模态 AI 领域的一个重要投资方向。 模型即服务（MaaS）的价值提升：提供基础模型服务的公司（如 OpenAI, Anthropic, Google）可以通过向客户暴露其模型的内生奖励 API，创造新的收入来源。开发者可以直接调用这个 API 来对齐自己的下游应用，而无需自己构建复杂的 RLHF 系统。 ### 论文的不足与存疑之处 从批判的视角来看，这篇论文虽然非常出色，但仍存在一些值得商榷和深入验证的地方：\n对偏见问题的讨论不足：论文在“Limitations”一节中提到了自洽性偏见的风险，但对其严重性和可能的解决方案讨论得相对简略。这实际上是该方法最大的潜在“阿喀琉斯之踵”。如果一个模型从有毒的互联网数据中学到了歧视性言论，EndoRM 可能会奖励模型生成更多此类言论，导致“越学越坏”。 实验范围的局限性： \u0026gt; “Due to time limitation, we report initial experimental results, and more results will be presented in future versions.” 作者在文中坦承，由于时间限制，所报告的是初步实验结果。这意味着当前的实验可能还不够全面。例如，实验主要集中在 Qwen 系列模型上，该方法在其他不同架构、不同训练数据的模型（如 Llama, Mistral）上是否同样有效，还需要更多验证。此外，对于更复杂的、涉及长程逻辑和事实性的评价任务，EndoRM 的表现如何，尚不明确。 对“最优”假设的依赖：整个理论推导基于一个前提，即 LLM 的训练数据（被模仿的专家）是“最优”的。但现实世界中的网络文本充满了次优、甚至低质量的内容。模型在模仿这些“有瑕疵的专家”时，其学到的内生奖励是否仍然可靠，是一个值得怀疑的问题。 超参数的敏感性：在计算最终奖励时，论文引入了折扣因子γ和阈值β等超参数（见附录 C）。这些超参数的设置对最终结果有多大影响？是否需要针对不同任务进行精细调整？论文对此缺乏详细的消融研究（ablation study），这可能会影响该方法在实际应用中的鲁棒性。 ### 核心启发与知识补充 对于希望从中学习并应用创新想法的您来说，这篇论文提供了极其宝贵的启示：\n我应该重点学什么？ 核心思想：挖掘模型的“双重身份”。最核心的启发是，要学会从不同角度审视同一个 AI 模型或过程。一个生成模型（Generator）可能同时也是一个评价模型（Evaluator）。在你的工作中，可以思考一下，你正在构建的系统是否也存在这样未被发掘的“双重功能”。 方法论：从理论中寻找捷径。论文的成功源于将一个工程问题（如何低成本构建 RM）回归到一个深刻的理论框架（IRL）。这启示我们，在遇到工程瓶颈时，不妨回到问题的本源，从相关理论（如信息论、控制论、博弈论）中寻找更优雅、更根本的解决方案。 具体技术：Logits 作为价值信号。一个非常“拿来即用”的想法是，直接使用模型的 logits 作为一种置信度或价值的代理指标。在你的应用中，如果需要对模型的多个候选输出进行排序或筛选，可以尝试计算每个输出的平均 log-probability（EndoRM 的简化形式），作为一个快速有效的排序依据，而无需调用外部评价 API。 我需要补充了解哪些背景知识？ 强化学习（Reinforcement Learning）基础：你需要理解马尔可夫决策过程（MDP）、Q 函数（Q-function）、价值函数（Value Function）和贝尔曼方程（Bellman Equation）。这是理解论文理论推导的基础。 模仿学习（Imitation Learning）：特别是行为克隆（Behavior Cloning）的概念，以及它存在的“复合误差”（compounding errors）问题。这将帮助你理解论文所解决问题的背景。 逆向强化学习（Inverse Reinforcement Learning, IRL）：这是论文的理论核心。建议至少了解最大熵 IRL（Maximum Entropy IRL）的基本思想，即在所有能解释专家行为的奖励函数中，选择那个最“不确定”的（熵最大），因为它做出的假设最少。这将让你深刻理解论文为何能够从理论上建立起这种连接。 LLM 对齐技术：了解 RLHF 和 DPO (Direct Preference Optimization) 的工作原理。这会让你明白论文的创新之处在于，它绕过了这两种方法都需要的“偏好对”，开辟了一条全新的技术路径。 ","date":"2025-07-10T00:00:00Z","permalink":"/generalist-reward-modelsfound-inside-large-language-models/","title":"GENERALIST REWARD MODELS：FOUND INSIDE LARGE LANGUAGE MODELS"},{"content":"好的，作为大模型领域的学术专家，我非常乐意为您深入解读这篇具有重要价值的论文《RLPR: Extrapolating RLVR to General Domains without Verifiers》。\n这篇论文的核心贡献在于，它巧妙地绕开了现有强化学习方法在提升大模型通用推理能力时遇到的一个核心瓶颈——验证器（Verifier），从而为更广泛、更低成本地提升大模型能力开辟了一条新路径。\n接下来，我将为您进行详细的剖析。\n### 一、论文的研究目标与意义 研究目标 论文的核心研究目标是解决基于可验证奖励的强化学习（Reinforcement Learning with Verifiable Rewards, RLVR） 在扩展到通用领域时所面临的可扩展性瓶颈。\n想要解决的实际问题 在 RLVR 框架下，为了训练模型，我们需要一个“裁判”或者说“验证器”（Verifier）来判断模型的输出是否正确，并据此给出奖励或惩罚。这个验证器在一些特定领域是比较容易实现的，例如： * 数学问题：验证器可以是一个程序，它能直接计算出正确答案并与模型的输出进行比对。 * 代码生成：验证器可以是一个沙箱环境，它能实际运行模型生成的代码，通过单元测试来判断代码是否正确。\n然而，一旦我们想把这种强化学习方法应用到通用领域（General Domains），比如开放式问答、创意写作、逻辑推理等，问题就来了。为这些领域构建一个可靠的、自动化的验证器是极其困难甚至不可能的。正如论文在引言中提到的： \u0026gt; For general-domain reasoning with free-form answers, it is even impossible to devise rule-based verifiers due to the high diversity and complexity of natural language. \u0026gt; （对于具有自由形式答案的通用领域推理，由于自然语言的高度多样性和复杂性，设计基于规则的验证器几乎是不可能的。）\n这种对领域特定验证器（domain-specific verifiers） 的重度依赖，导致了 RLVR 方法难以规模化，限制了它在提升大模型更广泛推理能力上的潜力。\n对于行业发展的重要意义 这个问题的解决具有重大的行业意义。当前，提升大模型能力的主流方法之一是基于人类反馈的强化学习（RLHF），但它依赖大量昂贵的人工标注。RLVR 通过自动化验证器降低了成本，但应用范围受限。\n如果能有一种方法，既能利用强化学习的强大威力，又不需要构建复杂的验证器，就能在通用领域内训练模型，这将意味着： 1. 极大降低训练成本：省去了为不同领域设计和维护验证器的巨额工程投入。 2. 极大拓宽应用范围：使得强化学习可以被用于提升模型在几乎所有文本任务上的能力，而不仅仅是数学和代码。 3. 加速模型迭代：开发者可以利用海量的、无标注的通用领域数据（只要有参考答案），通过该方法持续优化模型，实现更快的能力演进。\n这篇论文提出的 RLPR 框架，正是朝着这个“鱼与熊掌兼得”的目标迈出的关键一步。\n### 二、论文的核心方法与创新 为了摆脱对外部验证器的依赖，论文提出了一个全新的框架——RLPR (Reinforcement Learning with Reference Probability Reward)，即基于参考答案概率的强化学习奖励。其核心思想是：不再依赖外部裁判，而是利用模型自身的“自信程度”作为奖励信号。\n核心思路与流程 RLPR 的流程非常巧妙和简洁，具体可以分解为以下几步（可参考论文图 2）： 1. 模型生成：给定一个问题 Q，模型 πθ 首先生成一个包含思考过程 (reasoning chain, z) 和最终答案 (final answer, y) 的完整回复 o = z || y。 2. 奖励构建：RLPR 并不直接评判 y 的好坏。相反，它取出模型生成的思考过程 z，并与数据集中提供的标准参考答案 (reference answer, y*) 进行拼接，构成一个新的序列 o' = z || y*。 3. 概率计算：将这个新序列 o' 输入到模型 πθ 中，计算模型在给定了思考过程 z 的前提下，生成标准答案 y* 中每一个词（token）的概率。 4. 奖励信号：将这些概率值通过一个函数 f_seq 聚合成一个标量奖励 r。这个 r 就代表了奖励的大小。\n这个设计的直觉是：如果模型的思考过程 z 是正确且高质量的，那么它理应更有信心地生成正确的答案 y*，从而在 y* 的每个词上赋予更高的概率。\n主要创新点 RLPR 框架包含几个关键的技术创新，这些创新共同保证了方法的有效性和稳定性。\n概率奖励的计算方式 (Probability Reward) 论文发现，简单地将所有 token 的概率相乘（即序列似然度）作为奖励，会引入很高的方差，且对个别低概率 token 过于敏感。例如，(0.01, 0.7, 0.9) 和 (0.05, 0.7, 0.9) 两个概率序列，虽然只是第一个 token 有微小差异，但乘积结果会相差 5 倍。 因此，论文提出使用平均概率 (mean probabilities) 作为奖励函数 f_seq = (1/N) * Σ p_i。这种方式更加稳健，能更好地反映整体的置信度，实验也证明其与答案质量的相关性更高（如图 4 所示）。 奖励去偏 (Reward Debiasing) 模型对 y* 的生成概率不仅受到思考过程 z 的影响，还可能受到问题 Q 本身或者 y* 格式的偏见影响。 为了分离出纯粹由思考过程 z 带来的“贡献”，论文引入了一个基线奖励 r'。这个 r' 是在没有任何思考过程 z 的情况下，模型直接生成 y* 的概率。 最终的奖励是 r_hat = clip(0, 1, r - r')。这衡量的是“经过思考后，模型信心的提升量”，这个信号更加纯粹和鲁棒。 标准差过滤 (Standard Deviation Filtering) 这是一种自适应课程学习 (adaptive curriculum learning) 机制。在训练过程中，模型对不同样本的反应是不同的。 如果对于一个问题，模型无论怎么回答（生成不同的 z），得到的奖励 r 都差不多（即奖励的标准差很低），这说明这个问题对当前的模型来说，要么“太简单”（都能得高分），要么“太难”（都得低分）。 RLPR 会动态地过滤掉这些低标准差的样本，让模型专注于那些“难度适中”、最能学到东西的样本上，从而稳定训练过程并提升最终性能。 与之前方法的比较优势 相比 RLVR：最大的优势是无验证器 (verifier-free)。这使得 RLPR 具备极强的领域无关性 (domain-agnostic) 和可扩展性 (scalability)，从复杂的、需要大量工程设计的验证器，变成了一个简单的、内置的概率计算。 相比其他无验证器方法（如 VeriFree）：RLPR 的设计更优越。例如，VeriFree 使用序列似然度作为奖励，并限制答案长度小于 7 个 token。而 RLPR 使用更稳健的平均概率，并且通过去偏和过滤机制，在性能上取得了显著的超越。 ### 三、实验设计与结果分析 论文通过一系列详尽的实验，在多个模型和基准测试上验证了 RLPR 的有效性。\n实验设计 基础模型：实验覆盖了业界主流的多个模型家族，包括 Qwen 2.5 (7 B), Llama 3.1 (8 B), 和 Gemma 2 (2 B)，保证了结论的普适性。 训练数据：使用了 Ma et al. (2025) 发布的WebInstruct数据集，并特意移除了其中的数学相关问题，以专注于通用领域的推理能力。 评估基准：评估非常全面，涵盖了四大通用推理基准（MMLU-Pro, GPQA, TheoremQA, WebInstruct）和三大数学推理基准（MATH-500, Minerva, AIME 24）。 对比基线 (Baselines)：对比了多种方法，包括： 基础模型 (Base Models)：未经任何 RL 训练的原始模型。 指令微调模型 (Instruct Models)：官方的对话微调版本。 基于规则验证器的 RLVR：传统的 RLVR 方法。 基于模型验证器的 RLVR (General Reasoner)：使用一个专门训练的 1.5 B 模型作为验证器。 同期无验证器方法 (VeriFree)：一个与 RLPR 思路相似但实现不同的并发工作。 实验数据与结果 实验结果非常亮眼，充分证明了 RLPR 的优越性。以下是基于Qwen 2.5-7 B模型的一些关键数据（来自论文 Table 1）：\n方法 (Verifier) MMLU-Pro GPQA TheoremQA WebInst. 通用平均分 总平均分 Qwen 2.5-7 B-Inst (Base) 54.5 34.2 47.3 72.6 52.2 49.0 RLVR (Rule-based) 55.1 36.2 52.2 75.3 54.7 52.6 General Reasoner (Model-based) 55.4 37.4 52.1 74.5 54.8 52.0 VeriFree (X, Verifier-free) 53.8 36.7 47.6 72.5 52.6 49.4 RLPR (X, Verifier-free) 56.0 37.6 55.4 75.5 56.1 53.6 从上表可以清晰地看到： 1. RLPR 显著超越所有基线：在通用平均分上，RLPR（56.1）不仅远超基础模型（52.2），也超过了依赖规则验证器的 RLVR（54.7）和依赖模型验证器的 General Reasoner（54.8）。这证明无验证器方法不仅可行，甚至可以比有验证器的方法做得更好。 2. RLPR 碾压同类方法：RLPR（56.1）的表现远优于同为无验证器方法的 VeriFree（52.6）。特别是在 TheoremQA 和 Minerva 这两个数据集上，RLPR 分别高出7.6 分和7.5 分，优势巨大。 3. 奖励质量的量化对比：论文图 4 显示，在通用领域数据上，RLPR 的奖励信号在区分正确与错误答案上的AUC 达到了 0.91，而传统的规则验证器只有0.61，基于模型的验证器也只有0.69。这直观地展示了 RLPR 奖励信号的高质量。\n这些数据强有力地证明了 RLPR 框架的有效性和先进性。\n### 四、未来方向与潜在机会 值得探索的问题和挑战 多模态领域的扩展：论文主要集中在文本领域。未来一个重要的方向是将 RLPR 的思想扩展到多模态任务中，比如根据文本生成图片，或理解图文内容。如何定义多模态场景下的“概率奖励”将是一个有趣的挑战。 对“参考答案”依赖的优化：RLPR 依赖高质量的参考答案 y*。在一些没有唯一正确答案的、更具开放性的任务（如创意写作）中，如何应用 RLPR？或许可以探索使用多个参考答案、或者一个“评价模型”来提供概率目标。 与自洽性等方法的结合：可以将 RLPR 与自洽性（Self-Consistency） 等方法结合。例如，模型多次生成思考过程，选择使得参考答案概率最高的那个路径，或者对多个路径的奖励进行综合。 模型规模的影响：论文主要在 7 B/8 B 量级的模型上进行了实验。该方法在更大规模（如 70 B、数百 B）的模型上是否依然有效，以及其带来的性能提升是否会变化，值得进一步研究。 新的技术和投资机会 一站式 RL 优化平台：可以预见，未来会出现简化 RLPR 这类“无验证器 RL”流程的平台或工具。开发者只需提供基础模型和带有参考答案的数据集，平台即可自动完成 RL 优化，这将成为模型即服务（MaaS）的一个重要增值点。 高质量数据集的价值凸显：既然方法本身变得简单，那么竞争的焦点就会进一步向上游的数据转移。拥有大规模、高质量、多样化的带有“黄金参考答案”的数据集，将成为 AI 公司的核心资产。相关的数据采集、清洗、标注产业将迎来新的机会。 垂直领域模型的低成本定制：企业可以利用内部积累的业务数据（例如高质量的客服问答对、代码库、技术文档等），通过 RLPR 方法，低成本地在开源大模型的基础上训练出符合自身业务需求的、更高性能的垂直领域模型。 ### 五、论文的局限与批判性思考 从批判性的角度看，这篇论文虽然非常出色，但仍存在一些值得讨论的方面： 1. 对参考答案质量的强依赖：这是该方法最核心的假设，也是其潜在的弱点。如果参考答案 y* 本身存在错误或不是最优表述，RLPR 可能会“好心办坏事”，强化模型去学习一个次优的答案。 2. 对语义等价性的处理：模型生成的答案 y 可能在语义上与 y* 完全一致，但措辞不同。当前的概率计算方式是基于 token 的，可能会惩罚这种语义等价但字面不同的答案。虽然使用平均概率比序列似然度更稳健，但这个问题依然存在。 3. 计算开销问题：RLPR 在训练时，对于每个样本的每次生成，都需要一次额外的完整前向传播来计算奖励。如果每个 prompt 采样 k 个回答（论文中 k=8），则意味着计算开销会显著增加。虽然省去了验证器，但训练本身的成本变高了。 4. 去偏方法的普适性：r - r' 的去偏方式虽然直观，但可能无法完全消除所有潜在的偏见。例如，某些复杂的思考过程 z 可能与问题 Q 存在更深层的耦合关系，简单的相减可能无法完全解耦。\n### 六、核心启发与知识补充 重点学习与启发 核心思想：将外部监督信号内部化：这篇论文最大的启发是“向内求”。当外部的、显式的奖励难以获得时，可以尝试从模型内部寻找代理信号。模型的“自信程度”（即概率）就是一个绝佳的例子。这个思想可以应用在很多其他机器学习问题上。 增量优化的理念：奖励去偏的设计（r - r'）体现了“优化增量”的思想。我们奖励的不是模型本身有多好，而是它“通过思考，进步了多少”。这在很多需要过程优化的场景中都极具借鉴意义。 课程学习的重要性：标准差过滤机制提醒我们，在训练模型时，数据的“喂给”方式至关重要。动态地为模型筛选难度适中的“教材”，可以让学习过程事半功倍。 大道至简：RLPR 的框架相比于构建复杂的验证器系统，显得异常简洁和优雅，但效果却更好。这启示我们在设计算法时，应追求简单而强大的解决方案。 需要补充的背景知识 为了完全理解这篇论文，建议您补充了解以下知识： * 强化学习基础：特别是策略梯度（Policy Gradient）方法，如PPO (Proximal Policy Optimization)。论文中提到的 GRPO 算法就是基于 PPO 的，理解 RL 的基本概念（Policy, Reward, Value Function）是必要的。 * RLHF (Reinforcement Learning from Human Feedback)：了解当前大模型主流的对齐技术，可以更好地理解 RLVR 和 RLPR 是在解决 RLHF 中的什么痛点。 * Transformer 与语言模型生成原理：需要知道大模型是如何基于前面的文本（context），逐个 token 生成后续内容，并为词汇表中的每个 token 赋予一个概率值的。这是 RLPR 核心机制的技术基础。 * 相关前置工作：如果想深入，可以阅读一些 RLVR 的代表性论文（如 PRIME）以及论文中提到的并发工作 VeriFree，这样能更清晰地看到 RLPR 的传承与创新。\n希望这份详尽的解读能帮助您深入理解这篇优秀的论文！\n","date":"2025-07-10T00:00:00Z","permalink":"/rlprextrapolating-rlvr-to-general-domains-without-verifiers/","title":"RLPR：EXTRAPOLATING RLVR TO GENERAL DOMAINS WITHOUT VERIFIERS"},{"content":"梯度累积使我们能够通过按顺序处理较小的批次来扩展到更大的有效批次。我们不是一次计算整个批次的梯度（这需要将所有激活存储在内存中），而是在更新模型参数之前将每个小批次的梯度相加。这减少了内存使用量，但需要更多的向前/向后传递。\n代码 loss = loss / gradient_accumulation_stepsloss.backward() if step% gradient_accumulation_steps == 0: optimizer.step() optimizer.zero_grad() step+=1 gradient_accumulation_steps 是梯度累积次数，累积几次，原本的 loss 就要除以几，这是为了对多个批次的数据的梯度做累积。\n有人说，应该有这一行代码才算累加\nlosses += loss 这样理解是错误的。\n要明白最重要的一点是，梯度累加，累加的并不是损失，而是根据损失得到的梯度。\n梯度累计如何节省显存 减少瞬时激活值显存：每次仅处理小批量的数据，激活值显存占用降低为原来的 1/k（例如 k=4 时，显存占用降至 25%）。\n复用显存：每次小批量计算完成后，释放当前激活值显存，供下一次计算使用（显存占用峰值始终为小批量对应的量）。\n梯度显存不变：模型参数和梯度的显存占用与批量大小无关，因此不受影响（但需额外存储累积梯度的变量，这部分开销极小）。\n梯度累积两次，跟 batch size 增大 2 倍，在多数情况下，效果一样吗？（loss 的 3 次平均） 理论上，梯度累计在数学上应该等同于全批量训练，但实际发现 loss 并不匹配。( Gradient accumulation yields worse results than the equivalent batch size · Issue #2175 · huggingface/trl)\n一般情况下，loss 计算会经历三次平均\nmicro batch 维度，分母是这个 micro batch 中的所有 label 不是 -100 的 token 数（不同 token 之间 loss 的平均）\nDP 维度，分母是 DP size （和 GPU 数量相关，不同机器之间 loss 的平均）\n梯度累加维度，分母是梯度累加数。（不同 batch 之间的 loss 的平均）\nimage.png ","date":"2025-07-10T00:00:00Z","permalink":"/%E6%A2%AF%E5%BA%A6%E7%B4%AF%E8%AE%A1/","title":"梯度累计"},{"content":" image.png 从上图中可以看到DataProto可以分为3个部分： - non_tensor_batch - batch - meta_info 其中non_tensor_batch和meta_info都是个字典，而batch是TensorDict类型的变量。\nimage.png DataProto支持的一些操作如下：\n- concat: Combines multiple DataProto objects along the batch dimension - chunk: Splits a DataProto into equal chunks (requires batch size to be divisible by chunks) - split: Splits a DataProto into chunks of specified size (handles uneven splits) - repeat: Repeats the entire batch a specified number of times - sample_level_repeat: Repeats each sample a variable number of times - union: Merges two DataProto objects, combining their tensors and non-tensors - select/pop: Filter or remove specific keys from the batch - rename: Rename keys in the batch\nDataProtoConfig 这个设置主要用于管理auto_padding配置，用在dp中，即如果数据批次 / world_size不能整除的话，就需要padding。这部分体现在：\ndef _split_args_kwargs_data_proto_with_auto_padding(chunks, *args, **kwargs): from verl.protocol import DataProto, DataProtoFuture data_proto_len = None padding_size = None def _padding_and_split_data(obj, chunks): nonlocal data_proto_len, padding_size assert isinstance(obj, DataProto | DataProtoFuture) if isinstance(obj, DataProto) and obj.is_padding_enabled(): # for padding, we only support DataProto with same length if data_proto_len is None: data_proto_len = len(obj) padding_size = (chunks - (data_proto_len % chunks)) if (data_proto_len % chunks \u0026gt; 0) else 0 else: assert data_proto_len == len(obj), ( f\u0026quot;expecting all arg share same length of {data_proto_len}, but got {len(obj)}\u0026quot; ) obj.padding(padding_size=padding_size) return obj.chunk(chunks=chunks) splitted_args = [_padding_and_split_data(arg, chunks) for arg in args] splitted_kwargs = {key: _padding_and_split_data(val, chunks) for key, val in kwargs.items()} if padding_size is not None: splitted_kwargs[_padding_size_key] = padding_size return splitted_args, splitted_kwargs 计算需要多少个额外的项目才能使批量大小能被目标除数整除 从批次的开头获取项以用作填充 将原始批次与填充项连接起来 跟踪填充大小以供以后删除 DataProtoFuture DataProtoFuture类用于veRL中的分布式计算\nimage.png 关于collect_fn和dispatch_fn详见init_workers详解\n接下来是分布式dataproto的工作流：\nimage.png 其中all_gather_data_proto函数为：\ndef all_gather_data_proto(data: DataProto, process_group): # Note that this is an inplace operator just like torch.distributed.all_gather group_size = torch.distributed.get_world_size(group=process_group) assert isinstance(data, DataProto) prev_device = data.batch.device data.batch = data.batch.to(get_device_id()) data.batch = allgather_dict_tensors(data.batch.contiguous(), size=group_size, group=process_group, dim=0) data.batch = data.batch.to(prev_device) # all gather non_tensor_batch all_non_tensor_batch = [None for _ in range(group_size)] torch.distributed.all_gather_object(all_non_tensor_batch, data.non_tensor_batch, group=process_group) data.non_tensor_batch = {k: np.concatenate([d[k] for d in all_non_tensor_batch]) for k in data.non_tensor_batch} 也就是all_gather操作来收集data，主要用于对tp分组上的数据all_gather，因为tp分组需要相同的输入。 \u0026gt;vllm + fsdp 训推时，如果每张卡都是一个 DP，事情会简单很多。但 verl 中有两个功能不满足这一条件，一是 rollout 时让 vllm 开启 TP，二是在 fsdp 中使用 ulysses（SP）。verl 中数据分发使用的是 dispatch mode 这一机制，比如 fsdp workers 主要使用 Dispatch.DP_COMPUTE_PROTO这个 mode，它是在 worker group 的层次上进行数据分发以及结果收集的。由于这个层次是没有 TP/SP 概念的，所以它仅在 one GPU one DP 时才是正确的。那么为了正确支持 TP/SP，就需要对数据做一些前后处理。\n(上文引用自浅入理解verl中的batch_size)\n体现在ShardingManager中，即：\n@GPUMemoryLogger(role=\u0026quot;fsdp vllm sharding_manager\u0026quot;, logger=logger) def preprocess_data(self, data: DataProto) -\u0026gt; DataProto: \u0026quot;\u0026quot;\u0026quot;All gather across tp group to make each rank has identical input.\u0026quot;\u0026quot;\u0026quot; if self.tp_size == 1: return data # TODO: Current impl doesn\u0026#39;t consider FSDP with torch micro-dp group = vllm_ps.get_tensor_model_parallel_group().device_group all_gather_data_proto(data=data, process_group=group) return data 可见这里获取了tp group，然后在group中进行all_gather操作。post_process_data就是再将数据按照tp分组dispatch出去。在fsdp中需要sp，也有类似的操作。\nwith self.rollout_sharding_manager: log_gpu_memory_usage(\u0026quot;After entering rollout sharding manager\u0026quot;, logger=logger) prompts = self.rollout_sharding_manager.preprocess_data(prompts) with simple_timer(\u0026quot;generate_sequences\u0026quot;, timing_generate): output = self.rollout.generate_sequences(prompts=prompts) log_gpu_memory_usage(\u0026quot;After rollout generation\u0026quot;, logger=logger) output = self.rollout_sharding_manager.postprocess_data(output) 总结：因为我们在进行dispatch的时候，是按照world_size进行的，但是对于tp分组需要相同的输入，所以要进行all_gather。 ## Sequence Balancing\nverl中对序列优化的做法有以下几点： - 序列长度平衡：在工作线程之间分配序列以平衡计算工作负载（本章节） - 动态批处理：按令牌计数而不是固定批次大小对序列进行分组（见 [[batch_size解释]] 中”use_dynamic_bsz 数据流”章节） - 序列打包：删除填充标记以提高内存效率remove_padding - 微批次优化：重新排列批次以实现最佳 GPU 利用率\nimage.png 平衡算法：对序列进行分组以最大限度地减少填充，同时尊重标记限制，从而减少填充标记上的计算浪费。\n数据处理流程 verl的数据处理流程大概如下图所示：\nimage.png ","date":"2025-07-09T00:00:00Z","permalink":"/dataproto/","title":"DataProto"},{"content":"即 packing，将不同长度的序列紧凑存储，避免填充，减少不必要的计算和存储，提升效率。\n动机 sft进行微调，因为gpu是并行计算的，所以如果一个batch里面的数据，每条数据长度不相等，就需要对数据进行truncation（截断）和padding（pad数据到相同的seq_length）。显然，如果使用了padding，那么一个batch里面，就会有很多的pad_token，这些pad_token输入进入到了模型，但是却没有样本训练，造成了计算量的浪费。\n因此，对于这些长度不相等的样本，就可以使用packing（类似于打包），把这些样本拼接成长度相等的文本（比如20480, 4096, 8192）等长度。这样就能够是样本全部训练，增加了样本的计算效率。如图所示。每个样本之间不等长，但是可以使用eos_token进行拼接，达到加速训练的目的\nimage.png 带来的问题和解决方案（理论上） 如果使用了packing，需要考虑两个问题：attention和位置编码。相比于不使用packing，使用packing导致：\natteniton有问题：本来我只需要和sample1的token计算attention，现在packing以后，我的attention不仅仅是sample1内部计算。现在是sample1，sample2，sample3，通通一起计算attention。这样是不是会有问题？ 位置编码：本来sample1的位置编码是从0开始的，现在我sample1,2,3一起packing，那sample2，3的位置编码就变了，无法和单条样本训练一致。 解决方案： - 将packing中的attention方式进行修改（每条样本只和自己内部做attention），如下图 - 将packing的位置编码，修改成和不使用packing一样的位置编码。\n代码做法 引用 verl 中 tests 的代码：\ndef test_hf_casual_models(): batch_size = 4 seqlen = 128 response_length = 127 for config in test_configs: # config = AutoConfig.from_pretrained(test_case) with torch.device(\u0026quot;cuda\u0026quot;): model = AutoModelForCausalLM.from_config( config=config, torch_dtype=torch.bfloat16, attn_implementation=\u0026quot;flash_attention_2\u0026quot; ) model = model.to(device=\u0026quot;cuda\u0026quot;) breakpoint() input_ids = torch.randint(low=0, high=config.vocab_size, size=(batch_size, seqlen), device=\u0026quot;cuda\u0026quot;) attention_mask = create_random_mask( input_ids=input_ids, max_ratio_of_left_padding=0.1, max_ratio_of_valid_token=0.8, min_ratio_of_valid_token=0.5, ) position_ids = compute_position_id_with_mask( attention_mask ) # TODO(sgm): we can construct the position_ids_rmpad here input_ids_rmpad, indices, *_ = unpad_input( input_ids.unsqueeze(-1), attention_mask ) # input_ids_rmpad (total_nnz, ...) input_ids_rmpad = input_ids_rmpad.transpose(0, 1) # (1, total_nnz) # unpad the position_ids to align the rotary position_ids_rmpad = index_first_axis( rearrange(position_ids.unsqueeze(-1), \u0026quot;b s ... -\u0026gt; (b s) ...\u0026quot;), indices ).transpose(0, 1) # input with input_ids_rmpad and postition_ids to enable flash attention varlen logits_rmpad = model( input_ids_rmpad, position_ids=position_ids_rmpad, use_cache=False ).logits # (1, total_nnz, vocab_size) origin_logits = model( input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids, use_cache=False ).logits origin_logits_rmpad, origin_logits_indices, *_ = unpad_input(origin_logits, attention_mask) logits_rmpad = logits_rmpad.squeeze(0) log_probs = log_probs_from_logits_all_rmpad( input_ids_rmpad=input_ids_rmpad, logits_rmpad=logits_rmpad, indices=indices, batch_size=batch_size, seqlen=seqlen, response_length=response_length, ) # (batch, seqlen) origin_log_probs = log_probs_from_logits_all_rmpad( input_ids_rmpad=input_ids_rmpad, logits_rmpad=origin_logits_rmpad, indices=origin_logits_indices, batch_size=batch_size, seqlen=seqlen, response_length=response_length, ) # (batch, seqlen) torch.testing.assert_close( masked_mean(log_probs, attention_mask[:, -response_length - 1 : -1]), masked_mean(origin_log_probs, attention_mask[:, -response_length - 1 : -1]), atol=1e-2, rtol=1e-5, ) print(\u0026quot;Check pass\u0026quot;) 其中 unpad_input 函数简化逻辑的代码如下：\ndef unpad_input(hidden_states, attention_mask): # 1. 找到所有有效 token 的位置 # seqlens_in_batch 是一个包含批次中每个序列实际长度的列表，例如 [3, 4] seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32) # indices 是一个一维张量，包含了所有值为1的 mask 元素的展平后索引 indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten() # 2. 从 hidden_states 中提取出所有有效的 token # 首先将 hidden_states 展平成 (batch_size * sequence_length, ...) flat_hidden_states = hidden_states.reshape(-1, hidden_states.shape[-1]) # 然后使用 indices 来挑选出所有有效的 token hidden_states_unpadded = flat_hidden_states[indices] # 3. 计算累积序列长度 (cu_seqlens) # 例如，如果 seqlens_in_batch 是 [3, 4]，cu_seqlens 会是 [0, 3, 7] cu_seqlens = torch.cat( [torch.zeros(1, dtype=torch.int32), seqlens_in_batch.cumsum(dim=0)], dim=0 ) max_seqlen_in_batch = seqlens_in_batch.max().item() return hidden_states_unpadded, indices, cu_seqlens, max_seqlen_in_batch 这里的 cu_seqlens 就是不需要传入 attention_mask 的原因，相当于取代了 mask 的功能。 调试输出一些张量的 shape：\n(Pdb) input_ids.shape torch.Size([4, 128]) (Pdb) attention_mask.shape torch.Size([4, 128]) (Pdb) position_ids.shape torch.Size([4, 128]) (Pdb) input_ids_rmpad.shape torch.Size([1, 359]) # 也就是说去掉pad后4个sample在一起的有效长度为359 简单来说，indices 是一个“索引地图”。它的核心作用是记录在原始的、带填充的、被展平（flattened）的批次数据中，所有有效（非填充）词元的位置。\n当 unpad_input 处理 input_ids 时，它会丢掉所有的填充词元，只保留有效词元，并生成这个 indices 地图。这个地图至关重要，因为批次中的数据往往不止 input_ids，还有与之严格对齐的 position_ids、token_type_ids 等。\nindices 的主要用途是：确保其他辅助张量（如 position_ids）能够以与 input_ids 完全相同的方式被“解填充”（unpad），从而保持数据的一致性和对齐。 如果 position_ids 的解填充方式与 input_ids 不一致，那么旋转位置编码（Rotary Position Embedding, RoPE）等依赖位置信息的操作就会完全错乱。\n","date":"2025-07-08T00:00:00Z","permalink":"/remove_padding/","title":"remove_padding"},{"content":"一句话：在sequence维度上进行切分\n将输入序列 X (长度 N) 沿序列维度切分为 SP 块，每个 GPU 分配到 N/SP 长度的子序列。 对于非注意力层 (如 MLP)，计算是完全局部的，每个 GPU 处理自己的子序列即可。 token 之间独立，token-level projection Ulysses SP的核心复杂性在于Attention层。为了让每个token在计算注意力时能够考虑到全局序列信息（或者说，让每个head在计算时能看到完整的序列，即使这个head只在当前rank计算），Attention模块前后需要进行两次精密的all-to-all数据重排。MLP层则没有这样的需求，数据在进入MLP时已经是按序列分片好的，可以直接进行本地计算。 对于注意力层: 步骤 1 (计算 Q, K, V): 每个 GPU 基于其本地子序列计算出本地的 Q_local, K_local, V_local (维度约为 N/SP x d，d 是隐藏维度)。 步骤 2 (全局 K, V 收集 - 关键): 使用 All-to-All 通信操作（All-Gather??）。每个 GPU 将自己的 K_local, V_local 发送给所有其他 GPU，并接收来自所有其他 GPU 的 K, V 块。执行后，每个 GPU 拥有完整的全局 K 和 V 矩阵 (维度 N x d)，但仍然只拥有本地的 Q_local (维度 N/SP x d)。 https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/collectives.html 步骤 3 (本地注意力计算): 每个 GPU 使用其 Q_local 和完整的全局 K, V 计算其负责的那部分注意力输出 O_local (维度 N/SP x d)。计算公式为 Attention(Q_local, K_global, V_global)。这一步的计算量是 (N/SP) * N * d，内存瓶颈在于存储临时的注意力分数矩阵，大小约为 (N/SP) * N。相比原始的 **N*N**，内存显著降低。 步骤 4 (可选的输出重组): 如果后续层需要按序列拼接的完整输出，可能需要另一次通信（如 All-Gather 或另一次 All-to-All 的变种）来组合 O_local。但在 DeepSpeed 实现中，通常保持分布式状态，直接输入到下一个同样按序列并行的层。 verl中的序列并行 在verl中，一般与remove_padding一起使用，即\nif config.actor_rollout_ref.actor.strategy in {\u0026quot;fsdp\u0026quot;, \u0026quot;fsdp2\u0026quot;}: if ( config.actor_rollout_ref.actor.get(\u0026quot;ulysses_sequence_parallel_size\u0026quot;, 1) \u0026gt; 1 or config.actor_rollout_ref.ref.get(\u0026quot;ulysses_sequence_parallel_size\u0026quot;, 1) \u0026gt; 1 ): assert config.actor_rollout_ref.model.use_remove_padding, ( \u0026quot;When using sequence parallelism for actor/ref policy, you must enable `use_remove_padding`.\u0026quot; ) if self.use_critic and config.critic.strategy in {\u0026quot;fsdp\u0026quot;, \u0026quot;fsdp2\u0026quot;}: if config.critic.get(\u0026quot;ulysses_sequence_parallel_size\u0026quot;, 1) \u0026gt; 1: assert config.critic.model.use_remove_padding, ( \u0026quot;When using sequence parallelism for critic, you must enable `use_remove_padding`.\u0026quot; ) 先进行remove padding操作 然后进行序列并行的pad和slice操作 ## 参考 pytorch_distribute_tutorials/tutorials/3D-parallel/SP-序列并行.ipynb at main · chunhuizhang/pytorch_distribute_tutorials · GitHub\n","date":"2025-07-08T00:00:00Z","permalink":"/ulysses_sequence_parallel/","title":"ulysses_sequence_parallel"},{"content":"论文深度解读 这篇发表于 2025 年 4 月的论文，直面了当前 LLM 智能体领域的一个核心痛点：虽然 LLM 在处理简单、单步的指令上表现出色，但在面对需要多步骤、长期规划和适应环境变化的长时域任务（long-horizon tasks）时，其能力显著下降。想象一下让一个 AI 助手“预订一张下周从北京到上海的靠窗经济舱机票”，这背后涉及到理解意图、访问网站、输入信息、处理动态页面、比较选项、完成支付等一系列复杂操作。传统的单一 LLM 方法，如著名的ReAct（Reasoning and Acting）框架，试图将思考（Reasoning）和行动（Acting）融合在一个模型中，但这给模型带来了巨大的“认知负-载”，常常导致其“顾此失彼”，在复杂的交互中迷失方向。\n本文的核心贡献在于提出了一个名为 PLAN-AND-ACT 的双模块框架，旨在通过“分而治之”的思想来解决这一难题。其架构清晰地将任务分解为两个专业化的角色： 1. PLANNER（规划器）：一个高层次的“战略家”。它负责接收用户的原始指令（如“关注这个 GitHub 项目贡献最多的人”），并将其分解成一系列结构化的、高层次的步骤。例如，它会生成计划：“第一步，导航到‘贡献者’页面；第二步，识别出贡献最多的人并关注他”。这个规划器专注于“做什么”，而不关心“具体怎么做”。 2. EXECUTOR（执行器）：一个低层次的“操作手”。它接收 PLANNER 制定的高层次计划，并将其转化为在具体环境（如网页 HTML）中可以执行的精确动作。例如，它会根据“导航到‘贡献者’页面”这一计划，在 HTML 代码中找到对应的链接元素，并生成一个 do(action=\"Click\", element=\"13\") 的指令。\n正如论文中所强调的：“这种分离允许每个组件专注于其核心任务。PLANNER 可以在不陷入实现细节的情况下进行高层战略推理，而 EXECUTOR 则可以专注于将抽象计划转化为具体行动。”\nimage.png 然而，仅仅提出框架是不够的，训练这样专业的 PLANNER 和 EXECUTOR 需要大量高质量的、带有详细规划和行动标注的数据。在现实世界中，这类数据极其稀缺且标注成本高昂。这正是本文第二个，也是更具创新性的贡献所在：一个可扩展的合成数据生成流水线。这个流水线（如下图所示，源自论文图 3）巧妙地解决了“鸡生蛋，蛋生鸡”的困境，分为三个精巧的阶段：\nimage.png 行动轨迹生成（Action Trajectory Generation）：首先，利用一个强大的“教师 LLM”（如 GPT-4 o），在真实或模拟环境（如 WebArena）中执行任务，收集成功的“行动轨迹”（即一系列成功的底层操作序列）。 接地规划生成（Grounded Plan Generation）：接着，让另一个“教师 LLM”扮演“事后诸葛亮”的角色，对上一步收集到的成功行动轨迹进行“逆向工程”。它会分析这些具体的行动序列，反推出一个合乎逻辑的高层计划，并将每个计划步骤与具体的行动序列进行关联。这一步至关重要，因为它确保了生成的计划是“接地的”（Grounded），即与真实世界的可行操作紧密相连，而非凭空想象。 合成计划扩展（Synthetic Plan Expansion）：最后，将上一步生成的“计划-行动”对作为“种子”，利用 LLM 强大的泛化能力，生成大量结构相似但内容多样的新查询和计划。例如，从一个关于“查找商品”的计划，可以衍生出关于“查找不同商品”、“比较价格”等多种新计划。 通过这个流水线，论文成功地为 PLANNER 和 EXECUTOR 的训练生成了海量的、高质量的、多样化的数据。此外，论文还引入了动态重规划（Dynamic Replanning）机制。在静态计划中，如果环境发生意外变化（如搜索结果为空），执行器可能会陷入困境。而在动态重规划中，每当 EXECUTOR 完成一个动作后，PLANNER 都会根据环境返回的新信息（新的 HTML 状态）来审视并更新后续的计划。例如，当最初的搜索“CMU 的图书馆”失败后，PLANNER 会将计划调整为更泛化的“搜索 CMU 附近的图书馆”。\n“动态重规划允许规划器在演化的计划中保留关键信息… 这种方法使我们能够应对长时域任务中的记忆挑战，而无需明确的记忆模块。”\n最终，实验结果雄辩地证明了该方法的有效性。在 WebArena-Lite 基准测试中，通过逐步应用其提出的各项技术，模型的成功率从9.85%（基础模型）飙升至57.58%。这个最终成绩不仅远超基线，也刷新了当时的最先进水平（SOTA），超过了之前 SOTA 模型 WebRL-Llama-3.1-70 B 的 49.1%。这一系列详尽的消融实验（Ablation Study）清晰地展示了框架中每个组成部分（如规划器、数据增强、动态重规划、思维链）的价值。\n总而言之，PLAN-AND-ACT 框架及其合成数据生成流水线，为构建更强大、更鲁棒的 LLM 智能体提供了一套系统性且可扩展的解决方案。它不仅在理论上清晰地划分了智能体的“思考”与“行动”，更在实践中解决了训练数据匮乏的关键瓶颈，为自主 AI 在真实、动态环境中的应用铺平了道路。\n接下来，我将按照您提出的六个问题，逐一进行更详细的解读。\n### 1. 论文的研究目标与意义 研究目标： 论文的核心研究目标是提升大型语言模型（LLM）智能体在处理复杂、多步骤、长时域任务时的规划和执行能力。\n要解决的实际问题： 它旨在解决当前 LLM 智能体在面对真实世界任务（尤其是网页导航这类动态环境）时普遍存在的几个关键问题： * 规划难题：智能体难以将高层次的用户目标（如“帮我订一张去纽约的机票”）分解为具体、可执行的子任务（如“打开航空公司网站”、“输入旅行日期”等）。 * 上下文丢失：在长任务序列中，智能体容易“忘记”已经完成的步骤和最终目标，导致行为不连贯。 * 适应性差：真实环境（如网页）是动态和不可预测的，静态的、预先生成的计划往往会因为环境的微小变化而失败。 * 高质量训练数据稀缺：训练智能体进行有效规划需要大量的专家标注数据，而这类数据的获取成本极高，严重制约了模型性能的提升。\n对行业发展的意义： 这个问题对于 AI 行业的发展具有至关重要的意义。能够可靠执行长时域任务的自主智能体是实现通用人工智能（AGI）的关键一步。 * 提升自动化水平：强大的智能体可以将人类从繁琐的数字化工作中解放出来，例如自动化的客户服务、数据录入、市场分析报告生成、复杂的软件测试等，极大地提高生产力。 * 革新用户交互方式：未来的用户与计算机的交互可能不再是点击和输入，而是通过自然语言下达复杂指令，由 AI 智能体自主完成所有中间步骤。这将彻底改变人机交互的范式。 * 催生新的商业模式：基于自主智能体的平台和服务（Agent-as-a-Service）将成为可能，企业可以雇佣“AI 员工”来完成特定的业务流程，这将催生出巨大的市场和投资机会。\n解决好长时域任务规划问题，就如同为 AI 智能体装上了可靠的“大脑”和“手脚”，使其能从一个“玩具”真正走向“工具”，甚至成为“伙伴”。\n### 2. 论文的新思路、方法与模型 论文提出的核心方法是 PLAN-AND-ACT 框架，其创新之处在于双模块解耦设计与可扩展的合成数据生成流水线的有机结合。\n新的思路与方法：\nPLANNER-EXECUTOR 架构： 思路：将智能体的“大脑”（战略规划）与“双手”（具体执行）进行解耦。这借鉴了软件工程中的模块化思想，让每个部分更简单、更健壮。 特点： PLANNER：生成高层次、结构化的计划，关注任务的逻辑流程。它使得整个任务流程更清晰，也更容易被人类理解和调试。 EXECUTOR：专注于将计划步骤转化为环境中的具体动作。它处理与环境交互的复杂细节，减轻了 PLANNER 的负担。 优势：相比于 ReAct 等单模型方法，这种解耦降低了单个模型的认知复杂性，避免了在规划和执行之间“精神分裂”。如论文所述，这有助于“弥合高层用户意图与低层行动之间的鸿沟”。 动态重规划（Dynamic Replanning）： 思路：规划不是一次性的，而是一个持续适应的过程。 特点：在 EXECUTOR 每执行一步后，PLANNER 都会接收环境的最新反馈，并对剩余的计划进行评估和调整。 优势：极大地增强了智能体对动态环境的适应能力。论文中给出了一个很好的例子：当搜索“CMU 的图书馆”失败时，PLANNER 能接收到“未找到结果”的 HTML 反馈，并主动将下一步计划调整为更宽泛的“搜索 CMU 附近的图书馆”，从而挽救了任务。这比静态计划要灵活和鲁棒得多。 三阶段合成数据生成流水线： 思路：既然没有现成的高质量数据，就利用 LLM 自身的能力来创造数据。 特点： 行动轨迹生成：利用强大的教师模型（文中是 WebRL-Llama-3.1-70 B）在环境中探索，生成成功的操作序列。 接地规划生成：这是最巧妙的一步。让教师模型（文中是 DeepSeek-R 1-Distill-Llama-70 B）从具体的成功轨迹中“反向推导”出高层计划。这确保了计划不是空想，而是与现实操作紧密绑定的。 合成计划扩展：利用 Alpaca 风格的指令生成技术，以少量高质量的“计划-查询”对为种子，生成数以万计的新数据。如论文提到，他们用 GPT-4 o 在不到一小时内生成了 10,000 个新的查询-计划对，极具效率和可扩展性。 优势：此方法系统性地解决了训练数据瓶颈。它比纯人工标注成本低、速度快，并且通过“接地”步骤保证了数据质量，比完全依赖 LLM 凭空想象的计划更可靠。 与之前的方法相比，PLAN-AND-ACT 的优势在于它的系统性和可扩展性。它不依赖于需要复杂提示工程（prompting）的闭源模型，而是通过为开源模型（如 LLaMA）生成高质量的微调数据来提升其能力，为构建更开放、更强大的自主智能体社区提供了切实可行的路径。\n### 3. 实验验证与结果分析 论文通过一系列设计严谨的实验，在多个公认的基准测试上验证了 PLAN-AND-ACT 框架的有效性。\n实验设计：\n基准测试（Benchmarks）： WebArena-Lite：这是一个 WebArena 的子集，包含 165 个跨越购物、论坛（Reddit）、代码托管（GitLab）等多种网站的测试用例。它被用作主要的消融实验平台。 WebArena (Full)：完整的基准测试，用于和其他 SOTA 模型进行公平比较。 WebVoyager：一个基于真实世界网站的动态基准，用于测试模型在非模拟环境中的泛化能力。 模型选择：主要使用 LLaMA-3.3-70 B-Instruct 作为 PLANNER 和 EXECUTOR 的基础模型，并通过微调进行训练。同时，也使用了其他模型（如 GPT-4 o, DeepSeek-R 1）作为数据生成过程中的“教师模型”。 消融实验（Ablation Study）：这是实验设计的核心亮点。作者在 WebArena-Lite 上，从一个最基础的系统开始，逐步增加 PLAN-AND-ACT 框架的各个组件，并记录每一步带来的性能提升。这清晰地展示了每个创新点的贡献。 实验数据与结果：\n以下是论文核心的表 1（Table 1） 的简化版本，它直观地展示了消融实验的结果：\nPLANNER 设计 (逐步增强) EXECUTOR (Base) EXECUTOR (+Finetuning) EXECUTOR (+Synthetic Traj.) 无 PLANNER (ReAct-style) 9.85% 36.36% 36.97% … (其他基线) WebRL-3.1-70 B (先前 SOTA) - - 49.1%* Base PLANNER (zero-shot) 14.21% 17.16% 23.63% + Finetuning 22.42% 16.36% 20.60% + Plan Expansion (10 k) 27.10% 38.18% 39.40% + Targeted Augmentation (5 k) 29.63% 42.42% 43.63% + Dynamic Replanning 44.24% 48.48% 53.94% + CoT (最终模型) - - 57.58% 注：星号表示引用自原论文的报告结果。\n关键数据解读：\nPLANNER 的价值：仅仅加入一个未经微调的 Base PLANNER，就能让最基础的 Base EXECUTOR 的成功率从 9.85%提升到 14.21%，证明了规划-执行解耦的初步有效性。 数据为王： + Plan Expansion 和 + Targeted Augmentation 这两步带来了显著的性能飞跃。例如，对于最强的 EXECUTOR，成功率从 20.60%一路提升到了 43.63%。这证明了论文提出的合成数据生成流水线是成功的关键。 论文中提到：“每次训练数据集的扩展都会带来性能提升，其中最显著的增长来自于增加了 10,000 个直接生成的计划，成功率提升了大约 10 个百分点。” 动态重规划的决定性作用：引入 Dynamic Replanning 后，最终模型的成功率从 43.63%跃升至53.94%，提升超过 10 个百分点。这说明在动态环境中，适应和调整的能力是不可或缺的。 最终成果：最终加入思维链（CoT）推理后，模型在 WebArena-Lite 上达到了57.58%的成功率，创造了新的 SOTA 记录，显著超越了之前 SOTA（49.1%）。 泛化能力：在更具挑战性的真实世界基准 WebVoyager 上，PLAN-AND-ACT（使用 QWQ-32 B 模型）同样取得了81.36%的成功率，超越了所有之前的模型，包括基于 GPT-4-Turbo 的方法（如 Wilbur 的 52.6%），展示了其强大的泛化能力。 这些详实的数据和严谨的实验设计，有力地证明了 PLAN-AND-ACT 框架及其核心技术在提升 LLM 智能体长时域任务能力方面的有效性和优越性。\n### 4. 未来研究方向、挑战与机遇 基于这篇论文的研究，未来在该方向上还有许多值得探索的问题和挑战，同时也孕育着巨大的技术和投资机会。\n值得进一步探索的问题和挑战：\n更高效的重规划机制： 挑战：论文中承认，“在每个动作之后都进行重规划可能是低效和缓慢的”。这在需要快速响应的实时应用中是致命的。 探索方向：可以研究让EXECUTOR 学会“自主判断”何时需要重规划。例如，只有当 EXECUTOR 遇到预期之外的错误，或者对下一步行动的置信度低于某个阈值时，才向 PLANNER 请求帮助。这可以演变成一种分层代理（Hierarchical Agents）或委托（Delegation）机制。 多模态信息的融合： 挑战：目前的框架主要依赖于 HTML 等文本信息，但现代网页包含大量视觉元素（图片、视频、复杂的 UI 布局），仅靠文本理解是不够的。 探索方向：将多模态大模型（如 GPT-4 V）的能力整合到 PLANNER 和 EXECUTOR 中。PLANNER 可以基于页面截图进行更高层次的视觉规划，而 EXECUTOR 则可以执行更精确的视觉定位操作（如“点击那个红色的购物车图标”）。 基于强化学习的计划优化： 挑战：目前的计划生成主要依赖监督学习，模型的创造性受限于训练数据。 探索方向：可以引入强化学习（RL），特别是基于人类反馈的强化学习（RLHF）或直接偏好优化（DPO）。让智能体在环境中试错，根据任务成功与否、执行效率等作为奖励信号，来持续优化其 PLANNER 的规划策略，从而发现比人类示范更优的解决方案。 工具使用与泛化： 挑战：智能体不仅要会浏览网页，还需要学会使用各种 API 工具（如日历 API、天气 API、计算器）。 探索方向：将工具学习（Tool-use）框架与 PLAN-AND-ACT 结合，让 PLANNER 能够规划何时以及如何调用外部工具，并将工具返回的结果整合到后续的计划中。 新的技术和投资机会：\nAgentic AI 平台：开发允许用户低代码或无代码构建、训练和部署自定义 AI 智能体的平台。这类平台可以集成 PLAN-AND-ACT 的思想，提供模块化的 PLANNER 和 EXECUTOR 组件，以及强大的合成数据生成工具。 垂直领域的“AI 员工”：在电商、金融、法律、医疗等特定行业，训练专门的 AI 智能体来执行复杂的行业流程（如自动化的索赔处理、合同审查、病历分析）。这些是短期内最可能商业化的方向。 新一代操作系统和硬件：未来的操作系统可能会以 AI 智能体为核心，用户通过自然语言与整个系统交互。这也可能催生对专为 Agentic 计算优化的 AI 芯片（如加速并行函数调用和推理）的需求。 AI 安全与对齐：随着智能体变得越来越自主，如何确保其行为符合人类的意图和价值观（即对齐问题）变得至关重要。针对自主智能体的安全、监控和可解释性技术将成为一个重要的研发和投资领域。 ### 5. 论文的不足与存疑之处 从批判的视角来看，尽管这篇论文非常出色，但仍存在一些固有的局限性和需要进一步验证的地方。\n对强大“教师模型”的依赖（自举问题）： 不足：论文的合成数据生成流水线始于“行动轨迹生成”阶段，该阶段依赖一个已经很强大的模型（如 WebRL-Llama-3.1-70 B 或 GPT-4 o）来成功完成任务。 存疑：这带来了一个“自举（bootstrapping）”问题：如果你想进入一个全新的领域，而这个领域没有任何强大的基础模型，那么这个数据生成流程就无法启动。正如论文在“局限性”一节中坦承：“对于没有任何训练数据的任务（如 WebVoyager），流水线将依赖于有一个基础模型来收集轨迹。” 这在一定程度上限制了该方法的普适性。 效率和延迟问题： 不足：如前所述，每步都进行动态重规划的计算成本很高，会导致显著的延迟。论文虽然承认了这一点，但并未提供解决方案或对延迟进行量化分析。 存疑：在实际应用中，用户是否能接受一个反应缓慢的智能体？在需要与动态环境进行高频交互的场景（如在线游戏）中，这种方法可能并不可行。 成功率的绝对值仍有提升空间： 不足：尽管 57.58%的成功率在 WebArena-Lite 上是 SOTA，但这也意味着在超过 40%的情况下，智能体仍然会失败。对于许多要求高可靠性的商业应用来说，这个失败率是不可接受的。 存疑：这表明长时域网页任务的内在复杂性极高，即便有了先进的框架，距离实现人类水平的鲁棒性还有很长的路要走。是什么导致了剩余的失败？是规划的错误，执行的偏差，还是对环境理解的不足？论文对此缺乏深入的错误分析（error analysis）。 泛化领域的局限性： 不足：实验主要集中在网页导航任务上。虽然这是一个极具代表性的领域，但该框架能否无缝迁移到其他需要长时域规划的领域（如复杂的软件开发、科学研究、物理机器人控制）仍有待验证。 存疑：不同领域的“环境”和“行动空间”差异巨大。例如，在代码编写中，“行动”是编辑文本，“环境”是代码库和编译器反馈。该框架的 PLANNER 和 EXECUTOR 是否需要针对每个新领域进行大量重新设计和训练？其跨领域泛化的能力尚不明确。 ### 6. 对读者的启发与学习建议 从这篇论文中，我们可以汲取许多宝贵的、可直接应用的创新思想。\n重点学习与启发：\n分治思想（Divide and Conquer）： 学什么：核心启发是将一个复杂问题分解为几个更简单、更专业的子问题。在构建任何复杂的 AI 系统时，都可以思考是否能将“战略规划”与“战术执行”分离。 怎么用：例如，在开发一个自动生成报告的 AI 时，可以设计一个 PLANNER 来规划报告的大纲和结构（引言、数据分析、结论），再设计一个 EXECUTOR 来填充每个部分的具体文字和图表。 “逆向工程”创造高质量数据： 学什么：从“结果”反推“过程”是生成高质量标注数据的绝佳思路。论文中从成功的行动轨迹反推高层计划的“接地规划生成”是点睛之笔。 怎么用：如果你想训练一个 AI 来解决数学题，可以先用求解器（Solver）找到答案和解题步骤（结果），然后让 LLM 学习如何从问题生成这些解题步骤（过程），而不是让它凭空想象。这能保证训练数据的正确性和逻辑性。 迭代式的数据增强策略： 学什么：数据生成不是一蹴而就的。论文展示了一个从“生成-接地-扩展”的完整流程，特别是其中“靶向增强（Targeted Augmentation）”的思想——分析模型的失败案例，并针对性地生成更多相关的训练数据来弥补短板。 怎么用：在你的 AI 项目进入测试阶段后，系统性地收集和分类失败案例。然后，让 LLM 围绕这些失败案例生成更多样的、更具挑战性的训练数据，进行模型的迭代优化。这是一个持续改进模型的强大闭环。 需要补充的背景知识：\n为了完全理解和应用这篇论文的思想，建议您补充了解以下背景知识： * LLM 智能体（LLM-based Agents）基础：特别是 ReAct (Reasoning and Acting) 框架（arXiv:2210.03629），这是理解当前智能体研究的基石。 * 指令微调（Instruction Fine-tuning）：了解如 Alpaca、FLAN 等工作，这对于理解如何通过微调使 LLM 遵循特定格式和指令至关重要。 * Web Agent 相关基准测试：可以查阅 WebArena (arXiv:2307.13854) 和 WebVoyager (arXiv:2401.13919) 的原始论文，以了解评测环境的具体细节和挑战。 * 思维链（Chain-of-Thought, CoT）：了解 CoT prompting 技术（arXiv:2201.11903）如何通过引导 LLM 生成中间推理步骤来提升其在复杂任务上的表现。\n希望以上详尽的解读能帮助您深入理解这篇优秀的论文，并从中获得有价值的启发。如果您还有任何问题，随时可以提出。\n","date":"2025-07-07T00:00:00Z","permalink":"/plan-and-actimproving-planning-of-agents-for-long-horizon-tasks/","title":"LAN-AND-ACT：Improving Planning of Agents for Long-Horizon Tasks"},{"content":"熵坍塌 UloRL：An Ultra-Long Output Reinforcement Learning Approach for Advancing Large Language Models’ Reasoning Abilities ProRL：Prolonged Reinforcement Learning Expands Reasoning Boundaries in Large Language Models BEYOND PASS@1：SELF-PLAY WITH VARIATIONAL PROBLEM SYNTHESIS SUSTAINS RLVR Skywork Open Reasoner 1 Technical Report 利用熵 Beyond the 80 20 Rule：High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning First Return, Entropy-Eliciting Explore AGENTIC REINFORCED POLICY OPTIMIZATION CURE：Critical-Token-Guided Re-concatenation for Entropy-collapse Prevention ETTRL：BALANCING EXPLORATION AND EXPLOITATION IN LLM TEST-TIME REINFORCEMENT LEARNING VIA ENTROPY MECHANISM GTPO and GRPO-S：Token and Sequence-Level Reward Shaping with Policy Entropy EDGE-GRPO：Entropy-Driven GRPO with Guided Error Correction for Advantage Diversity GTPO：Trajectory-Based Policy Optimization in Large Language Models SSRL：SELF-SEARCH REINFORCEMENT LEARNING 利用熵来判断是依赖自己的内部知识还是依赖于外部工具。\n如何缓解熵坍塌？ clip-higher on-policy entropy loss 参考 小作坊的强化之路\n","date":"2025-07-06T00:00:00Z","permalink":"/entropyreasoning/","title":"entropy(reasoning)"},{"content":"参考 RL 系统深思：FSDP 训练后端\nPyTorch FSDP 设计解读\n","date":"2025-07-06T00:00:00Z","permalink":"/fsdp/","title":"fsdp"},{"content":"进入大模型时代，基本上所有大模型都使用 decoder 部分，因此本文只分析 decoder 部分的参数量。 Transformer 的 decoder 每一层由 attention 和 mlp 组成，一般有 l 层。\nSelf-attention Self-attention 层由 \\(W_{Q}\\) 、\\(W_{K}\\)、\\(W_{V}\\) 和输出矩阵 \\(W_{O}\\) 和它们的偏置组成，权重矩阵的形状为 \\([h,h]\\)，偏置形状为 \\([h]\\)，则 self-attention 部分的参数量为 \\(4h^2+4h\\)\nMLP MLP 由 2 个线性层构成，第一个线性层将维度从 h 变为 4 h，第二个将维度由 4 h 变为 h，第一个权重矩阵形状为 \\([h,4h]\\)，偏置为 \\([4h]\\)，第二个形状为 \\([4h,h]\\)，偏置为 \\([h]\\)，则参数量为 \\(8h^2+5h\\)\nLayer norm 在 self-attention 和 mlp 中都存在 layer norm，有 2 个可训练参数：缩放参数 \\(\\gamma\\) 和平移参数 \\(\\beta\\)，形状都是 \\([h]\\)，2 个 layer norm 的参数量为 4 h。\n词嵌入 词嵌入矩阵的参数量和词表大小 V 有关，而且输入和输出一般公用一个矩阵，因此参数量为 \\(Vh\\)\n位置编码 如果是可训练式的位置编码，则占据一定的参数量，否则不占参数量\n总参数 综上所述，transformer 一个层的参数量为 \\(12h^2+13h\\)，l 层就是 \\(l(12h^2+13h)\\)，再加上词嵌入矩阵，总参数量为 \\(l(12h^2+13h)+Vh\\)，h 较大时，可以忽略一次项，近似为 \\(12lh^2\\)\n实际参数量 隐藏维度h 层数l 12lh^2 6.7B 4096 32 6,442,450,944 13.0B 5120 40 12,582,912,000 32.5B 6656 60 31,897,681,920 65.2B 8192 80 64,424,509,440 表来自 分析 transformer 模型的参数量、计算量、中间激活、KV cache ## 参考\n# 分析transformer模型的参数量、计算量、中间激活、KV cache Transformer Math (Part 1) - Counting Model Parameters ","date":"2025-07-06T00:00:00Z","permalink":"/transformer%E5%8F%82%E6%95%B0%E9%87%8F%E5%88%86%E6%9E%90/","title":"transformer参数量分析"},{"content":"Pruning Long Chain-of-Thought of Large Reasoning Models via Small-Scale Preference Optimization TRAIN LONG, THINK SHORT：CURRICULUM LEARNING FOR EFFICIENT REASONING LAPO：INTERNALIZING REASONING EFFICIENCY VIA LENGTH-ADAPTIVE POLICY OPTIMIZATION Sample More to Think Less：Group Filtered Policy Optimization for Concise Reasoning Think in Blocks：Adaptive Reasoning from Direct Response to Deep Reasoning ","date":"2025-07-06T00:00:00Z","permalink":"/%E6%80%9D%E7%BB%B4%E9%93%BE%E5%8E%8B%E7%BC%A9/","title":"思维链压缩"},{"content":"训练时 模型参数：我们模型的可学习权重。 Optimizer states（优化器状态）：您需要跟踪的确切状态取决于您使用的优化器;例如，如果您使用的是 AdamW，则除了模型参数之外，您还需要跟踪第一和第二动量估计值。 模型激活值：这将根据您的网络架构和批处理大小而有所不同，但会显著影响内存使用。反向传播需要此信息，以便我们能够有效地计算梯度。 梯度：为模型的每个参数存储，与模型参数相同的内存占用。 Input data：要传递给模型的 Importing 数据批次，内存占用取决于正在建模的数据的大小和类型。 图示： 具体数值： 对于一个 transformer 来说，参数量可以由以下公式给出（详见 transformer参数量分析）：\n\\[N=h*v+L*(12*h^2+13*h)+2*h\\]\nIn that equation, \\(h\\) is the hidden dimension, \\(v\\) the vocabulary size, and \\(L\\) the number of layers in the model. Note that looking at the equation we can see that the term that will dominate with large hidden dimensions is the \\(h^{2}\\) term, since it’s the only one growing quadratically As we scale the parameters.\n在全精度训练中（所有的存储单位都是 fp 32），优化器使用 adam 的情况下，模型部分我们需要存储：\n\\[\\begin{aligned}\u0026amp;m_{params}=4*N\\\\\u0026amp;m_{grad}=4*N\\\\\u0026amp;m_{opt}=(4+4)*N\\end{aligned}\\]\n而在使用混合精度的情况下，模型的参数和梯度使用 bf 16，为了稳定性，优化器还需要存储 fp 32 的模型参数，即：\n\\[\\begin{aligned}\u0026amp;m_{params}=2*N\\\\\u0026amp;m_{grad}=2*N\\\\\u0026amp;m_{params\\_fp32}=4*N\\\\\u0026amp;m_{opt} =(4+4)*N\\end{aligned}\\]\nSome libraries store grads in FP32, which would require an additional mparams_fp32=4∗Nmparams_fp32​=4∗N memory. This is done, for example, in Nanotron, because BF16 is lossy for smaller values and we always prioritize stability. See this DeepSpeed issue for more information.\n也就是说有的库还实现了存储 fp 32 的梯度，考虑稳定性。\nThe FP32 copy of the parameters (mparams_fp32mparams_fp32​) is sometimes called the “master weights” in the literature and codebases.\n保存 fp 32 模型参数的原因是 bf 16 的精度不足以支持高效参数更新，fp 32 可以避免误差累计，保证优化器的数值稳定性和训练效果。具体分析如下（来自为什么LLM一般使用较大的权重衰减系数？）：\n从浮点数的存储格式建立了「计算机浮点数的数值绝对值越大，则精度越低」的结论，对于深度学习训练过程（前向-反向-更新）来说： 如果使用低精度浮点数保存和更新模型参数时，如果模型参数绝对值比较大，而更新的步幅比较小，那么更新会由于舍入误差而失效，这就是为什么要维护一个 fp 32 的模型参数的原因。 并且从一个高精度的模型转化为低精度模型的时候，参数的绝对值越大，则丢失的精度越多。在模型更新了fp32的备份之后，还需要将fp32的权重转化为低精度的版本，参与后续的forward过程。由于浮点数的精度随着绝对值的增加而降低，因此参数的绝对值越大，在精度的转化中损失的精度也越多。此外，在前向和反向计算的过程中，激活值也会存在类似的精度损失问题。如果我们在训练过程中引入权重衰减，那么模型的权重的绝对值就可以得到一定的控制。除了提供一定的正则化效应之外，也能够降低由于模型的参数范数增长而导致的精度损失的风险。\n混合精度训练示意图： 1. 参数以FP32存储；这主要是因为，在基于梯度更新权重的时候，往往公式:权重 = 旧权重 + lr * 梯度，而在深度模型中，lr * 梯度这个值往往是非常小的，如果利用 fp16 来进行相加的话， 则很可能出现精度的问题，导致模型无法更新。因此参数以FP32的形式存储 2. 正向计算过程中，遇到FP16算子，需要把算子输入和参数从FP32 cast成FP16进行计算； 3. 将Loss层设置为FP32进行计算； 4. 反向计算过程中，首先乘以Loss Scale值，避免反向梯度过小而产生下溢； 5. FP16参数参与梯度计算，其结果将被cast回FP32； 6. 除以Loss scale值，还原被放大的梯度； 7. 判断梯度是否存在溢出，如果溢出则跳过更新，否则优化器以FP32对原始参数进行更新。\n根据上述公式可以快速得到一些模型训练时占用显存：\nimage.png 此外激活值也是显存的巨大杀手，随着句子长度的增加而增加，有以下的计算公式：\n\\[m_{act}=L\\cdot seq\\cdot bs\\cdot h\\cdot(34+\\frac{5\\cdot n_{heads}\\cdot seq}h)\\]\nHere, \\(L\\) is the number of layers, seq the sequence length, \\(bs\\) the batch size in samples, \\(h\\) the hidden dimension of the model, and \\(n_{heads}\\) the number of heads.\nimage.png 可见在长上下文的训练中，激活值才是显存最大的杀手。这就需要Activation checkpointing 来降低这部分的显存占用，详见 Activation checkpointing\n推理时 一个经验法则是：推理时的峰值显存大致是模型参数显存的 1.5 - 2.5 倍（尤其在处理长序列或大批次时）。更精确的估计需要结合具体模型和输入\n输入/输出的 Token 存储：需要显存存储输入的 Token 嵌入（embedding）和生成的输出 Token。 Key-Value 缓存（KV Cache）：自回归生成时，为避免重复计算历史 Token 的 Key/Value，需缓存这些中间结果（显存占用与输入+输出长度成正比） 关于 kv cache 占用显存的计算，详见 KV cache ## 参考\nThe Ultra-Scale Playbook - a Hugging Face Space by nanotron ","date":"2025-07-06T00:00:00Z","permalink":"/%E6%98%BE%E5%AD%98%E5%8D%A0%E7%94%A8%E8%AE%A1%E7%AE%97/","title":"显存占用计算"},{"content":"","date":"2025-07-06T00:00:00Z","permalink":"/%E9%95%BF%E5%BA%A6%E6%89%A9%E5%B1%95%E5%A4%96%E6%8E%A8/","title":"长度扩展（外推）"},{"content":" image.png 这篇名为《Web Agents with World Models: Learning and Leveraging Environment Dynamics in Web Navigation》（https://arxiv.org/abs/2410.13232）的论文，由延世大学的研究团队撰写，并计划在 ICLR 2025 会议上发表。它直面了当前大型语言模型（LLM）在构建自主网页代理（Web Agents）时遇到的一个核心瓶颈：缺乏对环境动态的理解，即没有“世界模型”（World Model）。\n通俗地说，当前的 AI 代理在执行复杂的网页任务时，就像一个蒙眼走路的人，只能通过不断试错来摸索前进，这导致它们效率低下，并且容易犯下一些无法挽回的错误，比如在购物网站上重复购买不可退款的商品。人类之所以能避免这类错误，是因为我们的大脑中有一个“世界模型”，能让我们在行动之前“预演”或“想象”出这个行动可能带来的后果。\n这篇论文的核心贡献，就是为 LLM 代理引入了这样一个“世界模型”的概念，并提出了一套完整、高效的框架来实现它。他们首先通过初步实验有力地证明了问题的存在性：即便是最先进的 LLM（如 GPT-4 o, Claude-3.5-Sonnet），在预测一个网页操作（如点击按钮）会带来什么页面变化时，其表现也近乎随机猜测。然而，如果把操作的“后果”（即下一个页面的状态）直接告诉 LLM，它们选择正确操作的能力就会大幅提升。\n正如论文在初步分析部分（Preliminary Analyses）中展示的那样，在预测下一个网页状态的任务中，LLMs 的表现非常糟糕（平均准确率仅为 54.75%），而人类则高达 83%（图 1）。但当为 LLM 提供每个候选动作的后果时，其选择正确动作的准确率则出现了巨大飞跃，例如 GPT-4 o 的准确率从 53%提升到了 73%（图 2）。\n这一发现直接催生了论文的核心方法：世界模型增强的网页代理（World-Model-Augmented Web Agent, WMA）。该方法并不试图让一个庞大的模型包办一切，而是设计了一个精巧的分工体系： 1. 策略模型（Policy Model）：一个强大的 LLM（如 GPT-4 o），负责提出可能的行动选项。 2. 世界模型（World Model）：一个经过专门训练的、更小巧的模型（如 Llama-3.1-8 B），负责预测每个行动选项将导致的网页变化。 3. 价值函数（Value Function）：一个评估器，负责判断哪个预测结果最有利于实现最终目标，从而选出最佳行动。\n其中最巧妙的创新在于世界模型的训练方式。研究者意识到，让模型预测整个网页的 HTML 代码是极其低效且困难的。因此，他们提出了“聚焦于状态转换的观测抽象”（Transition-focused Observation Abstraction）。这个技术不要求模型生成一整个新页面，而是只学习和预测两次页面状态之间的“差异”。\n他们通过匈牙利算法（Hungarian algorithm）来比较前后两个网页的元素，精确地找出哪些元素是“新增的”（ADDED）、“删除的”（DELETED）或“更新的”（UPDATED）。然后，再用一个 LLM 将这些结构化的“差异”信息转换成一段简洁的自然语言描述。例如，不是预测整个购物车页面，而是预测出“商品‘A’已被添加到购物车，总价更新为‘$59’”。\n这种方法极大地降低了世界模型的学习难度和计算开销，使得用一个小型模型来完成高质量的未来预测成为可能。最终，论文通过在 WebArena 和 Mind 2 Web 这两个极具挑战性的基准测试上的大量实验，证明了 WMA 代理不仅显著提升了任务成功率，并且在推理时间和 API 成本上，远优于其他需要进行实际环境探索的先进方法（如树搜索）。例如，WMA 代理的成本仅为树搜索代理的1/6.8，速度则快了5.3 倍。\n总而言之，这篇论文不仅仅是提出了一种新模型，更是为自主代理领域引入了一种更接近人类思考方式的“深思熟虑”的决策范式。它将复杂的任务分解为“提出可能 -\u0026gt; 模拟后果 -\u0026gt; 评估选择”，并通过巧妙的抽象技术解决了模拟过程中的效率难题，为构建更智能、更高效、更可靠的 AI 代理指明了一个极具潜力的方向。\n接下来，我将按照您提出的六个方面，对论文进行更详细的解读。\n一、论文的研究目标是什么？想要解决什么实际问题？ 研究目标：核心目标是提升基于大型语言模型的自主网页代理在复杂、长时程任务（long-horizon tasks）中的性能和可靠性。\n解决的实际问题：\n决策短视（Myopia）：现有的 LLM 代理在决策时是“反应式”的，它们根据当前所见的页面内容直接生成下一步操作，缺乏对未来的规划和预见。这导致它们在需要多步、有逻辑关联操作的任务中表现不佳。 不可逆的错误：由于缺乏预见能力，代理容易犯下一些代价高昂且无法撤销的错误。论文中反复提及的例子是“重复购买一张不可退款的机票”，这生动地说明了问题的严重性。 低效率和高成本：依赖“试错”的代理需要与真实网页环境进行大量交互。每一次交互都可能耗费时间（页面加载）和金钱（API 调用成本），尤其是在探索多种可能性时（如树搜索方法），成本会急剧增加。 行业意义： 解决这个问题对于推动 AI 从“聊天机器人”向真正的“自主智能体”转变至关重要。一个能够预见后果、深思熟虑的 AI 代理，可以被可靠地应用于自动化各种复杂的日常和商业流程，例如：\n个人助理：自动完成在线购物比价、预订旅行、管理复杂的在线账户等。 企业自动化：自动完成数据录入、跨平台信息整合、软件测试、客户支持等。 可访问性：为残障人士开发能够自主操作复杂网页界面的辅助工具。 因此，这项研究为构建更实用、更值得信赖的下一代 AI 应用奠定了坚实的技术基础。 二、论文提出了哪些新的思路、方法或模型？ 论文的核心是提出了世界模型增强的网页代理（WMA）框架。相较于之前端到端（end-to-end）或基于简单试错的方法，其特点和优势在于其模块化的、基于模拟的决策流程。\n具体来说，该框架包含三个关键组件和一项核心技术：\n模块化设计： 策略模型 (Policy Model θ)：负责生成候选动作。这通常是一个强大的通用 LLM（如 GPT-4 o）。它保持冻结，无需训练，这使得整个框架易于集成和升级。 世界模型 (World Model Φ)：负责模拟未来。这是一个经过专门训练的小型 LLM（如 Llama-3.1-8 B），输入当前状态和候选动作，输出对下一状态的预测。 价值函数 (Value Function V)：负责评估。它也是一个 LLM，用于评估每个“预测出的未来”对于完成最终目标的价值，并给出一个分数。 核心技术：聚焦于状态转换的观测抽象 (Transition-focused Observation Abstraction) 这是论文方法论的精髓所在。它解决了直接用 LLM 模拟网页环境的两个核心痛点： 低信息增益：网页上的大部分内容在一次操作后保持不变。让模型重复生成这些不变内容是在浪费计算资源。 序列过长：完整的 HTML 或 DOM 树作为模型的输出，会产生极长的文本序列，训练和推理成本高昂。如下图（源自论文图 4）所示，原始的观测（axtree）平均长度达 4 K token，而论文提出的抽象描述（description）则极短。 图注: 论文图 4 的可视化，展示了不同观测表示的序列长度分布。Õt+1 description（蓝色）代表论文提出的抽象方法，其长度远小于其他方法。\n该抽象过程如论文图 5 所示，分为两步： 步骤一：机械化差异提取。利用匈牙利算法对前后两个页面的 DOM 元素进行匹配，自动识别出 UPDATED, DELETED, ADDED 的元素列表。 步骤二：自然语言总结。将这些结构化的差异信息输入给一个 LLM，让它生成一段简洁、流畅的自然语言描述，作为世界模型的训练目标。 优势： 高效性：用一个小型模型进行内部“模拟”，远比让一个大型模型与实际浏览器进行多次交互要快得多、便宜得多。 有效性：通过“预见未来”，代理可以避开那些会导致任务失败或陷入死循环的“陷阱”动作。 模块化与灵活性：世界模型可以独立训练和优化，并且可以即插即用地增强任何现有的策略模型，无需对策略模型本身进行微调。 三、论文通过什么实验来验证所提出方法的有效性？ 论文的实验设计非常全面、严谨，通过在两个公认的基准上进行的多维度对比，充分验证了方法的有效性。\n实验平台：\nWebArena：一个包含购物、社交论坛（Reddit）、代码协作（Gitlab）等多种真实场景的模拟网页环境，任务复杂，极具挑战性。 Mind 2 Web：一个大规模的、涵盖了来自 137 个网站的真实世界任务的数据集，注重泛化能力。 实验设计与关键结果：\n与基准模型的性能对比： 在 WebArena 上，WMA 代理全面超越了传统的 CoT（思维链）方法。如下表（简化自论文表 1）所示，使用 GPT-4 o 作为策略模型时，WMA 将成功率（Success Rate, SR）从12.8%提升至16.6%。更亮眼的是，当使用能力稍弱的 GPT-4 o-mini 时，性能提升幅度更大，从9.4%跃升至13.5%，相对提升了43.6%。这证明该方法能有效“赋能”能力较弱的模型。 Policy LLM Method SR (w/o Action Selection) SR (w/ Action Selection) Relative Gain GPT-4 o Tree search 15.0% 19.2% +28.0% GPT-4 o WMA (ours) 12.8% 16.6% +29.7% GPT-4 o-mini WMA (ours) 9.4% 13.5% +43.6% * 在 **Mind2Web** 上，WMA代理取得了**新的SOTA（State-of-the-Art）** 表现。在跨任务（Cross-Task）设定下，成功率达到**25.4%**，显著超过了之前的最佳方法AWM（15.3%）。这证明了其强大的泛化能力。 2. **与最强竞品的效率对比**： * 论文将WMA与**树搜索代理（Tree search agent）** 进行了直接对比，后者是当时性能最强的基准之一。结果（论文表4）显示，WMA在性能略低于树搜索的情况下，**推理时间快5.3倍，API成本低6.8倍**。这在实际应用中是巨大的优势。 3. **消融实验 (Ablation Studies)**： * 为了证明每个组件的必要性，论文进行了消融实验（论文表5）。结果表明，如果**不使用训练好的世界模型**（而是用prompting代替），或者**不使用“抽象观测”**（而是预测整个页面），性能都会大幅下降。这强有力地证明了**“训练”**和**“抽象”**是该方法成功的两大支柱。 四、未来在该研究方向上还有哪些值得进一步探索的问题和挑战？ 这篇论文为世界模型在网页代理中的应用开辟了道路，也揭示了许多激动人心的未来方向：\n多模态世界模型 (Multimodal World Models)： 挑战：当前研究主要基于文本（HTML/DOM）。但现代网页高度可视化，很多关键信息（如图标、图片内容、页面布局）仅靠文本是无法完全捕捉的。 方向：如论文附录 A 的初步探索所示，将视觉信息（截图）融入世界模型，能极大地提升性能（在 Mind 2 Web 上 SR 从 25.4%提升到 36.7%）。未来的研究可以探索更精细的图文融合技术，如利用 Set-of-Marks 等方法让模型关注特定视觉区域。这是一个明确的技术和投资热点。 长期规划与多步模拟 (Long-term Planning \u0026amp; Multi-step Simulation)： 挑战：当前方法主要进行单步模拟。随着模拟步数的增加，预测误差会累积，导致“想象”偏离现实。 方向：可以研究混合规划方法，即“模拟几步，再与真实环境交互一步”来校准。同时，如何提升世界模型的长期预测保真度，使其能够进行更深度的“思考链”，是一个核心的算法挑战。 世界模型的泛化与在线学习 (Generalization and Online Learning)： 挑战：为一个网站训练的世界模型，能否无缝迁移到另一个全新网站？ 方向：研究如何从海量、多样化的网页数据中训练一个“通用世界模型”。此外，让代理在与新环境交互时，能够在线学习和更新其世界模型，是实现真正自适应代理的关键。这可能催生出“代理即服务”的商业模式，其核心竞争力就是拥有一个持续进化的通用世界模型。 技术与投资机会： 世界模型即服务 (WMaaS)：未来可能会出现专门提供高保真环境模拟服务的平台。开发者可以调用这些 API 来训练或增强自己的 AI 代理，而无需自己搭建和维护复杂的模拟环境。 新一代开发工具：可能会出现专为构建和调试“深思熟虑型”代理而设计的开发框架和工具，它们内置了模拟、评估和可解释性功能。 五、从批判的视角看，这篇论文还存在哪些不足及缺失？ 尽管这篇论文非常出色，但从批判的角度看，仍存在一些可以深入探讨的局限性：\n世界模型的准确性瓶颈： 论文在第 6.2 节坦诚地分析了世界模型的错误类型。其中，“反事实想象”（Counterfactual imagination），即模型凭空捏造了不存在的页面元素，占比高达42%。这是一个非常严重的问题。如果代理的“想象”有近一半是错误的，那么基于这种想象做出的决策有多可靠？这可能会限制该方法在对可靠性要求极高的场景（如金融交易）中的应用。 对价值函数的依赖： 整个框架的最后一步——从多个“美好的未来”中选择一个，完全依赖于价值函数 V 的评估。论文中对价值函数的讨论相对较少。这个函数的准确性、鲁棒性以及它本身的复杂性，都直接影响最终决策的质量。如果价值函数本身存在偏见或短视，那么再好的世界模型也无济于 C-A-S-C-A-D-E of failure。 探索范围的局限性： 方法依赖于策略模型生成 k 个候选动作。k 的取值是一个权衡：k 太小，可能错过最优解；k 太大，则会丧失效率优势。在动作空间巨大的复杂页面上，如何有效地生成一小撮高质量的候选动作，是一个悬而未决的问题。 环境的静态假设： 实验环境（WebArena, Mind 2 Web）是相对静态的。而真实世界的网页是动态变化的（如商品库存、新闻更新）。当前的模型没有考虑如何处理这种环境的非平稳性（Non-stationarity）。 六、我应该从这篇论文中重点学什么？有哪些启发？ 对于希望从中汲取创新想法的您来说，这篇论文提供了几个极具价值的启发点：\n核心思想：从“反应”到“规划” 最大的启发是将模型驱动的模拟（Model-based Simulation）引入 LLM 代理决策。不要满足于让 LLM 成为一个只会“看一步走一步”的执行者，而要思考如何赋予它“三思而后行”的能力。这个“模拟-评估-选择”的范式可以被应用到网页浏览之外的许多领域，如代码生成、游戏 AI、机器人控制等。 方法论：抽象是解决复杂问题的关键 “聚焦于状态转换的观测抽象” 是一个可以被广泛借鉴的“降维”思想。当您面对一个状态空间巨大、信息冗余的问题时，可以思考：我真正需要模型学习和预测的是什么？是整个状态，还是状态之间的“变化”？ 这种对问题核心动态的抽象，是设计出高效模型的关键。 架构设计：模块化带来的灵活性 将一个复杂的系统分解为策略、世界、价值三个松耦合的模块，是一种非常优雅的工程实践。这种设计使得您可以独立地迭代和优化每个部分，例如，您可以保留强大的 GPT-4 作为策略模型，同时专注于用自己的数据训练一个高度专业化的、小巧的世界模型和价值函数，从而在特定领域实现高性价比的解决方案。 需要补充的背景知识： 基于模型的强化学习 (Model-Based Reinforcement Learning, MBRL)：这篇论文是 MBRL 思想在 LLM 代理领域的绝佳应用。了解 MBRL 的基本概念（学习环境模型，利用模型进行规划）会有助于您更深刻地理解本文的动机和方法。可以回溯阅读 Sutton 的 Dyna 架构等经典工作。 部分可观测马尔可夫决策过程 (POMDP)：论文将网页导航形式化为 POMDP。了解其基本定义（状态、观测、动作、转移、奖励）是理解该领域学术论文的标准前置知识。 规划与搜索算法 (Planning/Search Algorithms)：了解一些基本的规划算法，如树搜索（MCTS），可以帮助您更好地理解 WMA 方法在效率上相比传统规划方法的优势所在。 ","date":"2025-07-05T00:00:00Z","permalink":"/web-agents-with-world-models-learning-and-leveraging-environment-dynamics-in-web-navigation/","title":"WEB AGENTS WITH WORLD MODELS ：LEARNING AND LEVERAGING ENVIRONMENT DYNAMICS IN WEB NAVIGATION"},{"content":"这篇论文的核心目标是解决当前自主网页智能体（Web Agent） 在通过自我改进（Self-Improvement） 学习时遇到的一个关键瓶颈：性能停滞（stagnation point）。传统的自我改进方法，即智能体通过与真实网络环境交互，收集成功经验（轨迹），然后用这些经验来微调自身的语言模型。这种模式在初期有效，但很快就会因为探索范围有限而“碰壁”——智能体倾向于在已经熟悉的任务和路径上反复练习，难以发现更优或全新的解决方案，从而无法持续进步。\nWebEvolver 的作者们敏锐地洞察到，这个问题的根源在于“对真实世界交互的过度依赖”和“对模型内部知识的利用不足”。真实世界的交互既慢又昂贵，而且充满了重复。为了打破这一僵局，他们引入了一个在强化学习和认知科学中非常经典但在此领域应用新颖的概念——世界模型（World Model）。\n这篇论文的最大创新，在于提出了一个“与智能体共同进化的世界模型”（Co-evolving World Model）框架，并赋予了这个世界模型双重关键角色：\n作为“虚拟网络服务器”（Virtual Web Server）：在训练阶段，世界模型扮演了一个模拟器的角色。它学习理解网页的动态变化规律，即在给定当前网页状态（用可访问性树 Accessibility Tree 表示）和一个操作（如点击、输入）后，预测出下一个网页状态应该是什么样子。有了这个能力，它就可以凭空“想象”出大量的、多样化的网页交互轨迹（synthetic trajectories）。这极大地丰富了训练数据，让智能体可以在一个低成本、高效率的虚拟环境中进行“沙盘推演”，接触到在真实世界中可能永远不会或很少遇到的情况，从而打破探索瓶颈。如下图 1 所示，这是整个框架的核心循环。\nOur framework co-trains a world model alongside the agent by predicting next-step observations from current states and actions, using trajectory data collected during sampling. The world model then serves as a virtual web engine, enabling synthetic multi-step trajectories generation for training the policy. (我们的框架通过使用采样收集的轨迹数据，在训练智能体的同时，共同训练一个世界模型来预测基于当前状态和动作的下一步观察。该世界模型随后充当一个虚拟网络引擎，为策略训练生成合成的多步轨迹。)\n作为“想象力引擎”（Imagination Engine）：在推理（即实际执行任务）阶段，世界模型的能力被用来进行前瞻性规划（Look-ahead Planning）。当智能体面临多个可能的下一步操作时，它不再是盲目选择一个。而是利用世界模型，对每个候选操作进行“脑内模拟”，想象出执行该操作后可能会发生什么。论文中将此机制称为 世界模型前瞻（World-Model Look-Ahead, WMLA）。通过模拟未来几步的走向，并由一个评分器（LLM Scorer）评估哪条路径最有可能导向成功，智能体可以做出远比之前更明智、更具前瞻性的决策。例如，在下图 2 的推理演示中，智能体在考虑 Click [13] 这个低效选项和 Click [4] 这个高效选项时，WMLA 可以模拟出后者的光明前景，从而引导智能体做出正确选择。\nimage.png 图 1：论文核心框架图（修改自原论文 Fig 1 \u0026amp; 2），清晰展示了世界模型的双重角色\n论文通过在 WebVoyager 和 Mind 2 Web-Live 等高难度、真实的网页导航基准测试集上的实验，有力地证明了该方法的有效性。实验数据显示，相比于基线自改进方法，WebEvolver 带来了显著的性能提升。例如，在 WebVoyager 测试集上，仅使用 WebEvolver 进行训练，成功率就从 38.68%提升到了 42.49%。而当在推理时结合 WMLA（d=2，即向前看两步），成功率更是飙升至 51.37%，相较于迭代一次的自改进模型，提升了超过 12 个百分点，这在自主智能体领域是一个非常巨大的飞跃。\nExperiments in real-world web environments (Mind 2 Web-Live, WebVoyager, and GAIA-web) show a 10% performance gain over existing self-evolving agents…\n这篇论文不仅仅是提出一个有效的技术方案，更重要的是，它为自主智能体的持续学习和适应性进化指明了一条极具潜力的道路。它巧妙地将基于模型的强化学习思想（Model-Based RL）与大语言模型的强大先验知识相结合，证明了让智能体“学会想象”，是解锁其更高层能力的“金钥匙”。\n接下来，我将根据您提出的六个问题，进行更详细的逐一解读。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是提升 LLM 驱动的网页智能体在自我改进过程中的学习效率和最终性能上限。\n解决的实际问题：\n性能停滞：如前所述，基于纯粹真实环境交互的自我改进方法，智能体的能力会很快达到一个平台期。它们会陷入“认知固化”，无法发现新的、更高效的策略。 探索与利用困境 (Exploration-Exploitation Dilemma)：智能体在真实环境中进行探索（尝试新操作）的成本非常高，可能会导致任务失败、耗时过长。因此，它们倾向于利用（exploit）已知的成功路径，但这又限制了学习。 数据效率低下：完全依赖真实交互来收集高质量的训练数据，是一个缓慢且昂贵的过程。 行业意义：\n通向通用人工智能助理 (General AI Assistants)：解决这个问题是创造能够自主学习、适应不断变化的网页环境的通用 AI 助理的关键一步。一个能持续自我进化的智能体，才能真正成为我们可靠的“数字雇员”，处理复杂的线上任务，如自动预订、比价购物、信息整合等。 降低训练成本和门槛：通过引入世界模型生成合成数据，可以大幅减少对昂贵的真实世界交互和人工标注的依赖，使得训练高性能智能体的成本更低、速度更快，有助于技术的普及。 提升智能体的鲁棒性和泛化能力：在多样化的虚拟环境中训练，能让智能体学会应对更多样的网站布局和任务流程，从而在遇到从未见过的网站时表现得更好，泛化能力更强。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？ 新思路/方法/模型： 核心是 WebEvolver 框架，其精髓在于引入了一个与智能体策略模型（Agent Policy Model）共同学习、共同进化（Co-learning / Co-evolving） 的 世界模型（World Model）。\n与之前方法的比较、特点和优势：\n特性 之前的方法 (如 OpenWebVoyager) WebEvolver 学习数据来源 仅依赖真实环境交互。智能体在真实网站上操作，收集成功轨迹用于微调。 真实数据 + 合成数据。除了真实轨迹，还利用世界模型生成大量虚拟交互轨迹，数据来源更丰富、多样。 学习范式 纯粹的无模型 (Model-Free) 学习。智能体直接学习“状态-动作”的映射，不理解环境的动态。 模型辅助 (Model-Based) 学习。引入世界模型来学习环境的动态变化规律 T(s, a) -\u0026gt; s'，让智能体具备了“预测未来”的能力。 推理决策方式 反应式 (Reactive)。根据当前观察直接生成一个动作。 规划式 (Planning-based)。利用 WMLA 机制，在决策前“想象”多个动作的后果，进行多步前瞻规划，选择最优动作。 解决停滞问题 效果有限，容易陷入局部最优，导致性能停滞。 通过合成数据拓宽探索边界，通过前瞻规划优化决策质量，有效打破性能瓶颈。 细节分析： 共同进化是关键。世界模型并非一个固定的模拟器，它和智能体一样，在每一轮自我改进中都会被更新。这意味着，随着智能体能力的提升，它能探索更复杂的场景，收集到更高质量的真实轨迹；这些高质量的轨迹又可以把世界模型训练得更精准；更精准的世界模型又能生成更高质量的合成数据，并为智能体提供更可靠的规划支持。这是一个正向的飞轮效应。 论文中提到，即使世界模型会产生“幻觉”（hallucinated web states），在训练中也不是致命问题，甚至可能是有益的。 \u0026gt; Importantly, while the World Model may produce hallucinated (i.e., non-realistic) web states, this is not a critical issue during training, as the agent’s objective is to learn flexible action prediction rather than perfect state prediction. \u0026gt; (重要的是，尽管世界模型可能会产生幻觉（即不真实的）网页状态，但这在训练中并非关键问题，因为智能体的目标是学习灵活的动作预测，而非完美的状态预测。) 这种“不完美但多样”的模拟环境，反而可能增强智能体的泛化能力和想象力。 3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？ 实验设计： 基准模型：使用强大的开源模型 Llama-3.3-70 B 作为智能体和世界模型的基础。 评测数据集：选用了三个业界公认的、高难度的真实网络环境评测基准： WebVoyager: 包含来自 15 个不同真实网站的复杂任务。 Mind 2 Web-Live: 从真实用户操作中提取的任务，场景多样。 GAIA-web: 需要多步网页浏览和逻辑推理的挑战性任务。 对比方法：实验设置了清晰的对比路径，以展示每个创新点的贡献： Llama-3.3 70B: 零样本基线。 self-improve (iter 1/2): 传统的自我改进方法，仅使用真实数据迭代 1 次或 2 次。这对应了之前的 SOTA 方法（如 OpenWebVoyager）。 WebEvolver: 论文提出的方法，使用真实+合成数据进行训练。 WebEvolver + WMLH: 在 WebEvolver 训练出的模型基础上，在推理时使用世界模型前瞻规划。 评估指标：主要指标是任务成功率 (Task success rate)，由更强大的 GPT-4 o 作为裁判进行端到端的评估，确保公正性。 实验数据和结果： 论文 Table 1 提供了详尽的结果，这里引用关键数据说明： 模型/方法 WebVoyager (All) 成功率 Mind 2 Web-Live 成功率 Self-Improving Llama-3.3 70 B (iter 1) 38.68% 15.09% Self-Improving Llama-3.3 70 B (iter 2) 38.23% 16.98% WebEvolver (我们的方法-仅训练) 42.49% 22.64% WebEvolver + WMLH (d=1) 46.24% 28.30% WebEvolver + WMLH (d=2) 51.37% 24.53% **结果分析**： 1. **性能停滞的证据**：`self-improve` 从迭代1到迭代2，在WebVoyager上的性能甚至略有下降(38.68% -\u0026gt; 38.23%)，清晰地证明了传统方法的瓶颈。 2. **WebEvolver训练的有效性**：`WebEvolver` 仅在训练阶段加入合成数据，就比`self-improve`高出约4个百分点（WebVoyager）和约7.5个百分点（Mind2Web-Live），证明了“虚拟服务器”角色的成功。 3. **WMLA规划的巨大威力**：在推理时加入WMLA后，性能再次大幅提升。尤其是在WebVoyager上，`d=2`（向前看两步）的规划将成功率推高至51.37%，展现了“想象力引擎”的巨大价值。 4. **泛化能力**：在GAIA-web上的实验（Table 3）也显示了类似的提升趋势，证明了WebEvolver在训练中未见过的任务上也具有良好的泛化能力。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 世界模型的保真度与效率：如何让世界模型在预测网页动态时更准确、更高效？目前基于 LLM 的预测仍然较慢，且对复杂 JavaScript 动态渲染支持不佳。探索如图文多模态扩散模型（predicting the next screenshot）或专用图神经网络（for HTML structure）可能是未来的方向。 长时程任务规划 (Long-horizon Planning)：当前的 WMLA 只能进行 2-3 步的短视规划。对于需要几十步才能完成的复杂任务（如规划一次完整的旅行），需要研究更高效的、具有层级抽象能力的规划算法（Hierarchical Planning）。 多模态世界模型：网页是视觉和文本的结合体。未来的世界模型必须是多模态的，能够理解和预测视觉布局、图片内容的变化，而不仅仅是文本结构（可访问性树）。 应对反爬与安全机制：如何让智能体学会处理登录、验证码（CAPTCHA）、动态安全令牌等挑战，是其走向真实应用必须攻克的难关。 模型的在线学习与快速适应：当一个网站突然改版，智能体能否在几次失败的交互后，快速在线更新其世界模型和策略模型，以适应新的界面？这需要研究更高效的在线学习算法。 可能催生的技术和投资机会： 下一代 RPA（机器人流程自动化）：当前 RPA 工具脆弱、易受网页改版影响。基于 WebEvolver 思想的智能体能创造出更鲁棒、更智能的自动化解决方案，颠覆数据录入、流程处理等行业。 超级个人助理：能够自主在网络上为你完成复杂任务（“帮我订一张下周五去上海的机票，找性价比最高的，并且预订一家离会场近的四星级酒店”）的个人助理产品。 自动化软件测试：利用智能体自动探索 Web 应用，发现 bug 和可用性问题，极大地提升软件测试的效率和覆盖率。 智能市场研究与舆情分析：部署大量智能体自动抓取、整合、分析来自不同网站和社交媒体的动态信息，提供实时的商业洞察。 “智能体即服务”(Agent-as-a-Service) 平台：提供一个平台，让企业可以低代码地训练和部署用于特定垂直领域的网页智能体。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 计算成本高昂：论文没有详细讨论该方法的计算开销。训练和维护一个与智能体同样规模（70 B）的世界模型，并在每次决策时都进行多步前瞻模拟，其计算成本和延迟将是巨大的，这可能成为其在实际应用中的主要障碍。 对世界模型“幻觉”的讨论过于乐观：论文声称幻觉在训练中问题不大，甚至有益。但这缺乏深入的消融实验来验证。错误的模拟（例如，模拟出一个不存在但看起来很有诱惑力的按钮）是否会误导智能体学习到错误的“因果关系”？这个问题值得更严肃的探讨。 状态表示的局限性：论文依然沿用可访问性树（Accessibility Tree）作为网页状态的表示。这种纯文本的表示丢失了大量的视觉布局信息，而人类在浏览网页时高度依赖视觉。对于许多现代网页，仅靠可访问性树是无法完全理解其功能的。 评估指标单一：主要依赖任务成功率。但效率（完成任务的步数）、成本（API 调用次数）、可解释性等也是衡量一个智能体好坏的重要维度，论文对此着墨不多。 需要进一步验证和存疑之处： 泛化到更复杂的动态网页：实验中的网站虽然真实，但可能仍是精心筛选过的。该方法在处理由复杂前端框架（如 React/Vue）生成的高度动态、异步加载内容的网页时的表现，仍有待验证。 WMLA 规划深度的扩展性：实验表明，规划深度 d=3 时性能开始下降，这说明世界模型的预测能力在多步后迅速衰减。这是否是该方法的一个内在限制？如何突破这个限制？需要进一步研究。 对不同大小基础模型的适用性：实验基于强大的 70 B 模型。该框架对于小一些的模型（如 7 B、13 B）是否同样有效？或者说，世界模型的能力是否有一个“智能涌现”的门槛？ 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发： “模拟器”思维：这是最核心的启发。在你试图让一个 AI 学习某个复杂任务时，思考一下是否可以为它构建一个“模拟器”（即世界模型）。这个模拟器不需要 100%完美，但它能够低成本、大规模地生成训练数据，让 AI 在安全可控的环境中“演练”，这是打破数据瓶颈、加速学习的强大范式。 模型的“双重用途”设计：设计一个模块，让它既能在训练阶段服务（如生成数据），又能在推理阶段服务（如辅助规划），是一种非常优雅和高效的架构思想。这启发我们在设计复杂 AI 系统时，要思考模块的复用和协同。 “想象力”是高级智能的关键：让模型具备前瞻规划和反事实思考（“如果我那么做会怎样？”）的能力，是其从一个简单的模式匹配器进化为真正问题解决者的关键。WMLA 机制就是这种思想的具体实现。 拥抱“不完美”：世界模型可以有幻觉，合成数据可以不真实，但这不妨碍它们成为有效的训练工具。这告诉我们，在 AI 的训练中，数据的“多样性”和“启发性”有时比“绝对真实”更重要。 需要补充的背景知识： 强化学习 (Reinforcement Learning)：特别是基于模型的强化学习 (Model-Based RL) 和 无模型的强化学习 (Model-Free RL) 的区别。理解智能体（Agent）、环境（Environment）、策略（Policy）、奖励（Reward）等基本概念。 马尔可夫决策过程 (Markov Decision Process, MDP)：论文中提到的 POMDP (Partially Observable MDP) 是其变体，理解其基本原理有助于抓住问题的数学本质。 网页前端基础：了解 DOM 树 (Document Object Model) 和论文中使用的 Accessibility Tree 是什么，它们如何表示一个网页的结构。 AI 规划算法 (AI Planning)：了解像蒙特卡洛树搜索 (Monte Carlo Tree Search, MCTS) 等基本规划算法，可以帮助你更好地理解 WMLA 这类前瞻性决策机制的原理。 希望这份详尽的解读能够帮助您全面、深入地理解这篇优秀的论文。\n","date":"2025-07-05T00:00:00Z","permalink":"/webevolverenhancing-web-agent-self-improvement-with-coevolving-world-model/","title":"WebEvolver：Enhancing Web Agent Self-Improvement with Coevolving World Model"},{"content":"我理解的agent中的world model即可以预测采取某个action之后state的变化，这样做的好处是可以降低试错带来的时间成本或者是其它潜在的成本、风险。\n目前agent中常用的模块为reflexion，即反思模块。作用是评价行动轨迹的结果是成功还是失败，如果评估为失败，则接受失败的轨迹和评估结果，生成一段失败总结文本。\n这当然是可行的，但问题是这需要不断试错才有可能纠错从而执行正确的action，这就会造成时间成本，甚至如果agent负责的是一个购物任务，错误的action甚至有可能是下单错误的商品。\n所以我认为world model的作用就是预测action之后state会如何变化，这相当于走一步，看一步，来避免出错。\nreflexion是必要的，这是对整个轨迹的评估，如果评估结果为失败的话，还是要进行反思，与world model结合的作用是降低评估失败的概率，使试错的次数降低。我认为这在实际应用当中是有必要的，试错的时间成本要远远大于每一步调用world model所带来的开销。\n","date":"2025-06-30T00:00:00Z","permalink":"/world_model/","title":"world_model"},{"content":"前置知识，ray前置知识 我将用此文来详细介绍veRL中有关single_controller和SPMD的相关内容。本文不涉及ppo训练相关，只是记录一下理解veRL架构实现的核心。\n实现single_controller方法的核心有以下方法：\nWorker RayResourcePool RayWorkerGroup RayClassWithInitArgs 实现SPMD的有以下方法：\nregister dispatch_fn和collect_fn execute_fn func_generator 推荐阅读：verl 解读 - 源码阅读(part3)\n","date":"2025-06-26T00:00:00Z","permalink":"/init_workers%E8%AF%A6%E8%A7%A3/","title":"init_workers详解"},{"content":"原生Ray 代码改编自# verl 解读 - ray 相关前置知识 (part1) ray分配资源的单位是bundle，一个bundle一般由1个cpu和1个gpu构成。 而一个placement_group由多个bundle组成，当参数设置为pack通常为同1个node上的bundle构成的。参数设置为spread为不同node的bundle组成。如下图所示： 在python中，定义placement_group如下：\npg = placement_group([ {\u0026quot;CPU\u0026quot;: 1, \u0026quot;GPU\u0026quot;: 1} for _ in range(total_devices) ], strategy=\u0026quot;STRICT_PACK\u0026quot;, name=\u0026quot;ray_multi_group_comm\u0026quot;) ray.get(pg.ready()) print(f\u0026quot;=\u0026gt; Placement group is ready, total_devices: {total_devices}\u0026quot;) ray中的基本单位有task和actor，一个无状态，一个有状态（可以简单理解为一个是函数，一个是面向对象的类）。都需要用worker来代替运行。因此我们需要定义一个worker来完成各种任务。这个worker都是上述资源的分配单位，它只会看到ray分配给它的资源，而看不到其它的资源，对于分布式训练而言，可以在这个worker中定义分布式训练的各种东西、加载模型、前向训练等。一个worker定义如下：\nclass WorkerBase: \u0026quot;\u0026quot;\u0026quot; 通用的 Worker 基类。 每个 Worker 实例是一个 Ray Actor，它会在一个独立的进程中运行，并持有一个模型副本。 \u0026quot;\u0026quot;\u0026quot; def __init__(self, model_cls, temp_init: bool = False): # 获取当前 Ray Actor 所在的节点 ID 和 Actor ID self._node_id = ray.get_runtime_context().get_node_id() self._actor_id = ray.get_runtime_context().get_actor_id() # 获取当前主机的 IP 地址 self._ip_address = socket.gethostbyname(socket.gethostname()) if temp_init: # 如果是临时初始化，则直接返回，不进行模型和分布式环境的设置 return # 设置随机种子 self._set_seed(42) # 初始化 PyTorch 分布式环境 if not dist.is_initialized(): # 检查是否已经初始化 dist.init_process_group( backend=\u0026quot;nccl\u0026quot;, # 使用 NCCL 作为后端，适用于 NVIDIA GPU world_size=int(os.getenv(\u0026quot;WORLD_SIZE\u0026quot;, \u0026quot;1\u0026quot;)), # 从环境变量获取 world_size rank=int(os.getenv(\u0026quot;RANK\u0026quot;, \u0026quot;0\u0026quot;)), # 从环境变量获取当前进程的 rank # 从环境变量获取主节点的地址和端口，用于建立连接 init_method=f\u0026quot;tcp://{os.getenv(\u0026#39;MASTER_ADDR\u0026#39;)}:{os.getenv(\u0026#39;MASTER_PORT\u0026#39;)}\u0026quot; ) self._rank = dist.get_rank() # 获取当前进程的排名 self._world_size = dist.get_world_size() # 获取分布式组的大小 self.model = model_cls() # 实例化传入的模型类 self.model.to(\u0026quot;cuda\u0026quot;) # 将模型移动到 GPU # 打印初始化信息 print(f\u0026quot;=\u0026gt; Rank {self._rank}/{self._world_size} in group \u0026#39;{os.getenv(\u0026#39;GROUP_NAME\u0026#39;)}\u0026#39; initialized model: {self.model.__class__.__name__}\u0026quot;) def get_actor_info(self): \u0026quot;\u0026quot;\u0026quot;返回该 actor 的网络信息。\u0026quot;\u0026quot;\u0026quot; return { \u0026quot;ip_address\u0026quot;: self._ip_address } def _set_seed(self, seed: int = 42): \u0026quot;\u0026quot;\u0026quot;设置随机种子。\u0026quot;\u0026quot;\u0026quot; set_random_seed(seed) def train_step(self, data): \u0026quot;\u0026quot;\u0026quot;执行一个训练步骤。\u0026quot;\u0026quot;\u0026quot; x = data.to(\u0026quot;cuda\u0026quot;) # 将输入数据移动到 GPU y = self.model(x) # 模型前向传播 loss = y.sum() # 计算一个简单的损失（所有输出的和） loss.backward() # 反向传播，计算梯度 return loss.cpu() # 返回在 CPU 上的 loss 值 def sample_grads(self): \u0026quot;\u0026quot;\u0026quot;采样一个参数的梯度用于验证。\u0026quot;\u0026quot;\u0026quot; for name, p in self.model.named_parameters(): # 遍历所有带名字的参数 if p.requires_grad is True: # 找到第一个需要梯度的参数 return name, p.grad.cpu() # 返回参数名和它在 CPU 上的梯度 其中model_cls就是定义的各种模型。 然后就可以给各种worker绑定资源。这里我定义了两种模型，node上有4张gpu，我实现的是两种模型分别占2张不同的卡：\ndef setup_and_create_workers(pg, model_cls, group_name, start_bundle_index, num_workers_in_group): \u0026quot;\u0026quot;\u0026quot; 一个辅助函数，用于设置一个分布式组并为其创建 workers。 \u0026quot;\u0026quot;\u0026quot; print(f\u0026quot;\\\\n========== 正在设置组: {group_name} ==========\u0026quot;) # 将 WorkerBase 类包装成 Ray Remote Actor Worker = ray.remote(WorkerBase) # 创建一个临时 worker 来获取该组的网络信息 # 这个 worker 会被放置在指定的 placement group bundle 上 temp_worker = Worker.options( scheduling_strategy=PlacementGroupSchedulingStrategy( placement_group=pg, placement_group_bundle_index=start_bundle_index, ), num_gpus=1, ).remote(model_cls=model_cls, temp_init=True) # 使用 temp_init=True，避免完全初始化 # 获取临时 worker 的网络信息，特别是 IP 地址，用作该组的 master 地址 network_info = ray.get(temp_worker.get_actor_info.remote()) master_addr = network_info[\u0026quot;ip_address\u0026quot;] master_port = str(find_free_port()) # 为该组找到一个空闲端口 ray.kill(temp_worker) # 销毁临时 worker print(f\u0026quot;为组 {group_name} 使用的主节点地址: {master_addr}:{master_port}\u0026quot;) workers = [] # 存储创建的 worker # 循环创建指定数量的 worker for i in range(num_workers_in_group): rank = i # 当前 worker 在其组内的 rank bundle_index = start_bundle_index + i # 该 worker 在 placement group 中的 bundle 索引 # 设置分布式环境所需的环境变量 env_vars = { \u0026quot;WORLD_SIZE\u0026quot;: str(num_workers_in_group), \u0026quot;RANK\u0026quot;: str(rank), \u0026quot;MASTER_ADDR\u0026quot;: master_addr, \u0026quot;MASTER_PORT\u0026quot;: master_port, \u0026quot;GROUP_NAME\u0026quot;: group_name } # 创建一个正式的 worker actor workers.append( Worker.options( name=f\u0026quot;{group_name}_rank_{rank}\u0026quot;, # 为 actor 命名，方便调试 scheduling_strategy=PlacementGroupSchedulingStrategy( placement_group=pg, placement_group_bundle_index=bundle_index, ), runtime_env={\u0026quot;env_vars\u0026quot;: env_vars}, # 将环境变量传递给 actor num_gpus=1, # 为每个 actor 分配一个 GPU ).remote(model_cls=model_cls) # 传入模型类进行实例化 ) print(f\u0026quot;========== 组 {group_name} 设置完成 ==========\u0026quot;) return workers # 返回创建的 worker 列表 其中Woker.options()是分配资源的关键部分.placement_group代表使用的pg，而bundle_index代表分别的资源是编号为多少的bundle 以及num_gpus=1代表worker需要1个gpu资源，这是最关键的地方。\n传入的环境变量在worker类内部获取，见上一段代码。这样就实现了两张卡放一个模型，另两张卡放另一个模型。\n最后是启动代码：\ndef main(): \u0026quot;\u0026quot;\u0026quot;主执行函数\u0026quot;\u0026quot;\u0026quot; set_random_seed(42) # 设置全局随机种子 ray.init() # 初始化 Ray total_devices = 4 # 定义总共使用的设备（GPU）数量 num_devices_per_group = 2 # 定义每个分布式组中的设备数量 # 创建一个 Placement Group，请求 total_devices 个 \u0026quot;bundle\u0026quot; # 每个 bundle 需要 1 个 CPU 和 1 个 GPU # \u0026quot;STRICT_PACK\u0026quot; 策略会尝试将这些 bundle 放置在同一个节点上 pg = placement_group([ {\u0026quot;CPU\u0026quot;: 1, \u0026quot;GPU\u0026quot;: 1} for _ in range(total_devices) ], strategy=\u0026quot;STRICT_PACK\u0026quot;, name=\u0026quot;ray_multi_group_comm\u0026quot;) ray.get(pg.ready()) # 等待 placement group 的资源被成功分配 print(f\u0026quot;=\u0026gt; Placement group 已就绪, 总设备数: {total_devices}\u0026quot;) # --- 设置组 A，使用 DummyAttn 模型 --- workers_a = setup_and_create_workers( pg=pg, model_cls=DummyAttn, # 指定模型类 group_name=\u0026quot;GroupA_DummyAttn\u0026quot;, # 组名 start_bundle_index=0, # 在 placement group 中的起始 bundle 索引 num_workers_in_group=num_devices_per_group # 该组的 worker 数量 ) # --- 设置组 B，使用 NewDummyModel 模型 --- workers_b = setup_and_create_workers( pg=pg, model_cls=NewDummyModel, # 指定模型类 group_name=\u0026quot;GroupB_NewDummyModel\u0026quot;, # 组名 start_bundle_index=2, # 在 placement group 中的起始 bundle 索引（紧接着组 A） num_workers_in_group=num_devices_per_group # 该组的 worker 数量 ) # --- 训练并验证组 A (DummyAttn) --- print(\u0026quot;\\\\n--- 正在训练并验证组 A (DummyAttn) ---\u0026quot;) # 创建一批随机数据 datas_a = torch.randn(4, 128, 1024) # 异步执行 train_step # 将数据分块 (chunk) 并分发给每个 worker ray.get([worker.train_step.remote(datas_a.chunk(num_devices_per_group)[i]) for i, worker in enumerate(workers_a)]) # 从每个 worker 获取梯度样本 grads_a = ray.get([worker.sample_grads.remote() for worker in workers_a]) print(\u0026quot;正在验证组 A 的梯度...\u0026quot;) # 验证组内所有 worker 的梯度是否一致 for i in range(1, len(grads_a)): # 使用 torch.allclose 比较两个梯度张量是否在数值上足够接近 assert torch.allclose(grads_a[0][1], grads_a[i][1]), \u0026quot;组 A 的梯度不匹配!\u0026quot; print(\u0026quot;✅ 组 A 的梯度一致。\u0026quot;) # --- 训练并验证组 B (NewDummyModel) --- print(\u0026quot;\\\\n--- 正在训练并验证组 B (NewDummyModel) ---\u0026quot;) # 创建一批随机数据 datas_b = torch.randn(4, 512) # 异步执行 train_step # 将数据分块并分发给每个 worker ray.get([worker.train_step.remote(datas_b.chunk(num_devices_per_group)[i]) for i, worker in enumerate(workers_b)]) # 从每个 worker 获取梯度样本 grads_b = ray.get([worker.sample_grads.remote() for worker in workers_b]) print(\u0026quot;正在验证组 B 的梯度...\u0026quot;) # 验证组内所有 worker 的梯度是否一致 for i in range(1, len(grads_b)): assert torch.allclose(grads_b[0][1], grads_b[i][1]), \u0026quot;组 B 的梯度不匹配!\u0026quot; print(\u0026quot;✅ 组 B 的梯度一致。\u0026quot;) ray.shutdown() # 关闭 Ray 最后的输出：\n2025-06-26 15:23:47,563 INFO worker.py:1879 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8266 =\u0026gt; Placement group 已就绪, 总设备数: 4 \\n========== 正在设置组: GroupA_DummyAttn ========== 为组 GroupA_DummyAttn 使用的主节点地址: 127.0.1.1:49335 ========== 组 GroupA_DummyAttn 设置完成 ========== \\n========== 正在设置组: GroupB_NewDummyModel ========== 为组 GroupB_NewDummyModel 使用的主节点地址: 127.0.1.1:50477 ========== 组 GroupB_NewDummyModel 设置完成 ========== \\n--- 正在训练并验证组 A (DummyAttn) --- (WorkerBase pid=2898182) =\u0026gt; Rank 0/2 in group \u0026#39;GroupA_DummyAttn\u0026#39; initialized model: DummyAttn 正在验证组 A 的梯度... ✅ 组 A 的梯度一致。 \\n--- 正在训练并验证组 B (NewDummyModel) --- (WorkerBase pid=2898392) =\u0026gt; Rank 0/2 in group \u0026#39;GroupB_NewDummyModel\u0026#39; initialized model: NewDummyModel 正在验证组 B 的梯度... ✅ 组 B 的梯度一致。 (WorkerBase pid=2898181) =\u0026gt; Rank 1/2 in group \u0026#39;GroupA_DummyAttn\u0026#39; initialized model: DummyAttn (WorkerBase pid=2898391) =\u0026gt; Rank 1/2 in group \u0026#39;GroupB_NewDummyModel\u0026#39; initialized model: NewDummyModel veRL中的Ray 使用# 【AI Infra】【RLHF框架】二、VeRL中colocate实现源码解析中的代码：\nimport ray from verl.single_controller.base import Worker from verl.single_controller.base.decorator import register, Dispatch from verl.single_controller.ray.base import RayResourcePool, RayClassWithInitArgs, RayWorkerGroup, create_colocated_worker_cls from verl import DataProto @ray.remote class Actor(Worker): def __init__(self) -\u0026gt; None: super().__init__() @register(dispatch_mode=Dispatch.DP_COMPUTE_PROTO) def add(self, data: DataProto): data.batch[\u0026#39;a\u0026#39;] = data.batch[\u0026#39;a\u0026#39;].to(\u0026quot;cuda\u0026quot;) data.batch[\u0026#39;a\u0026#39;] += self.rank return data @ray.remote class Critic(Worker): def __init__(self, config) -\u0026gt; None: super().__init__() self.config = config @register(dispatch_mode=Dispatch.DP_COMPUTE_PROTO) def sub(self, data: DataProto): data.batch[\u0026#39;a\u0026#39;] = data.batch[\u0026#39;a\u0026#39;].to(\u0026quot;cuda\u0026quot;) data.batch[\u0026#39;a\u0026#39;] -= self.config[\u0026#39;b\u0026#39;] return data def test_colocated_workers(): ray.init() import torch # 构建一个DataProto，其中属性a是维度为10的零向量。 data = DataProto.from_dict({\u0026#39;a\u0026#39;: torch.zeros(10)}) print(data.batch[\u0026quot;a\u0026quot;]) # 利用RayClassWithInitArgs将自定义的worker和参数封装起来 actor_cls = RayClassWithInitArgs(cls=Actor) critic_cls = RayClassWithInitArgs(cls=Critic, config={\u0026#39;b\u0026#39;: 10}) # 定义资源池，仅包含一个2GPU的节点 resource_pool = RayResourcePool(process_on_nodes=[2]) # 利用create_colocated_worker_cls将自定义的两个worker绑定到WorkerDict上 cls_dict = {\u0026#39;actor\u0026#39;: actor_cls, \u0026#39;critic\u0026#39;: critic_cls} ray_cls_with_init = create_colocated_worker_cls(cls_dict) # 启动WorkerDict wg_dict = RayWorkerGroup(resource_pool=resource_pool, ray_cls_with_init=ray_cls_with_init) # 分别获取actor和critic的workergroup spawn_wg = wg_dict.spawn(prefix_set=cls_dict.keys()) colocated_actor_wg = spawn_wg[\u0026#39;actor\u0026#39;] colocated_critic_wg = spawn_wg[\u0026#39;critic\u0026#39;] # actor执行add、critic执行sub actor_output = colocated_actor_wg.add(data) critic_output = colocated_critic_wg.sub(data) # actor_output.batch[\u0026quot;a\u0026quot;]==[0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # critic_output.batch[\u0026quot;a\u0026quot;]==[-10, -10, -10, -10, -10, -10, -10, -10, -10, -10] print(actor_output.batch[\u0026quot;a\u0026quot;]) print(critic_output.batch[\u0026quot;a\u0026quot;]) ray.shutdown() if __name__ == \u0026#39;__main__\u0026#39;: test_colocated_workers() 具体的操作我会在init_workers章节进行讲解，这里是对veRL中的single_controller的一个初探。\n问题 有一个常见的问题是为什么在placement_group中分配了一个bundle为1个gpu，为什么还要指定num_gpus=1，这个参数有什么含义吗?\n简单来说，两者都需要，因为它们在 Ray 的调度系统中扮演着不同但互补的角色：\nplacement_group 中的 {\"GPU\": 1}：这是在做资源“预留” (Reservation)。 它的作用是告诉 Ray 集群：“我需要你为我准备好一个资源包（bundle），这个包里必须包含 1 个 CPU 和 1 个 GPU。” placement_group 的主要目的是确保一组相关的资源能够被原子性地、协同地调度。比如，strategy=\"STRICT_PACK\" 确保了所有这些 bundles 都会被放置在同一个节点上，这对于需要低延迟通信的分布式训练至关重要。 这就像是为一场宴会预订了一个能容纳4位客人的包间，并确保这4个座位都在同一张桌子上。它只是圈占了资源，但还没有指定谁来使用这些资源。 Worker.options(num_gpus=1)：这是在为 Actor 提出具体的“资源请求” (Request)。 它的作用是告诉 Ray：“我即将创建的这个 Worker Actor，它本身在运行时需要消耗 1 个 GPU。” Ray 的调度器需要根据这个明确的请求来为 Actor 分配具体的物理设备。没有这个声明，Ray 调度器会认为该 Actor 不需要 GPU。 这就像是告诉宴会的主管：“这位客人需要一个座位。” 为什么缺一不可？\nPlacementGroupSchedulingStrategy 将这两者联系起来。它告诉 Ray：“请将这个需要1个GPU的Actor (num_gpus=1)，安排到我们之前预留的那个包含GPU的bundle ({\"GPU\": 1}) 上去。”\n如果只有 placement_group 的预留而没有 Actor 的 num_gpus=1 请求，Ray 的调度器会看到一个矛盾：你试图将一个声称不需要 GPU 的 Actor 安排在一个为 GPU 使用者保留的“席位”上。这可能会导致调度失败或资源分配混乱。\n反之，如果只有 Actor 的 num_gpus=1 请求而没有 placement_group，Ray 会在整个集群中寻找任何一个可用的 GPU 来满足这个 Actor，但无法保证它会和其他相关的 Actor 运行在同一个节点上，从而失去了分布式训练的性能优势。\n总结：\nplacement_group 是一个宏观的、用于资源预留和协同定位的机制，而 num_gpus=1 是一个微观的、用于声明单个 Actor 实际资源消耗的机制。两者必须同时使用并保持一致，才能确保 Ray 能够精确、高效地将需要特定资源的 Actor 调度到你为它们预留的、具有特定拓扑结构的资源包中。这种显式的设计让复杂的分布式资源管理变得更加清晰和可控。\n参考 ","date":"2025-06-26T00:00:00Z","permalink":"/ray%E5%89%8D%E7%BD%AE%E7%9F%A5%E8%AF%86/","title":"ray前置知识"},{"content":"这篇论文的核心思想，一言以蔽之，就是通过一种名为 RLVR（Reinforcement Learning with Verifiable Rewards，可验证奖励的强化学习） 的技术，对 世界模型 (World Models) 进行“二次打磨”或“精加工”，从而使其更精准地服务于特定任务。这解决了传统训练方法（如最大似然估计 MLE）与最终应用目标之间存在的“貌合神离”的问题。\n让我们先通俗地理解一下什么是 世界模型。您可以把它想象成一个学习了特定环境“物理规律”的模拟器。比如，在一个游戏中，世界模型知道“你推一下箱子，箱子会向前移动”；在一个视频里，它知道“球被抛出后，会沿着抛物线落下”。它通过预测“在当前状态下，执行某个动作后，世界会变成什么样子”来工作。\n然而，传统的训练方式存在一个巨大痛点。它们通常使用 最大似然估计 (MLE)，目标是让模型预测的下一个词元（token）或像素（pixel）与真实数据尽可能一致。这种方法虽然能让模型学到数据的大致分布，但往往与我们真正关心的“任务目标”有所偏差。论文中提到：\nHowever, standard training objectives such as maximum likelihood estimation (MLE) often misalign with task-specific goals of world models, i.e., transition prediction metrics like accuracy or perceptual quality.\n例如，在视频预测任务中，仅用像素级的均方误差 (MSE) 作为目标，会导致生成的视频模糊不清；在语言任务中，则容易导致模型产生重复、无意义的“车轱辘话”或事实性错误（幻觉）。\n为了解决这一“对不齐”的难题，作者们提出了 RLVR-World 框架。其精髓在于，不再使用模糊的、学习来的人类偏好（如 RLHF 中那样），而是将一个 可验证的、基于规则的奖励函数 作为强化学习的明确信号。这个奖励函数直接衡量模型输出的好坏，例如，预测的游戏状态是否完全准确，或者生成的视频帧在观感上是否清晰真实 (使用 LPIPS 这类感知度量)。这个直接、清晰的信号，能更有效地指导模型“进步”。\n该框架的一大亮点是其通用性。它将语言、视频等不同模态的任务统一到了一个自回归的序列建模范式下。无论是文本描述的状态，还是视频画面的像素，都被编码成一串串的 词元 (tokens)。世界模型的工作，就是像一个语言模型一样，逐个预测代表“未来世界”的词元。 为了证明其有效性，论文在两个截然不同的领域——语言和视频——进行了详尽的实验，并取得了令人瞩目的成果。\n在 语言世界模型 的实验中，以“文本游戏状态预测”为例，任务要求模型根据玩家的动作，预测游戏世界的状态变化（以 JSON 格式表示）。如论文 表1 所示，经过 RLVR 微调后，模型的整体准确率从 32.87% 飙升至 63.24%，几乎翻了一倍，并且达到了与更强大的 GPT-4 (64.76%) 相媲美的水平。这充分证明了 RLVR 在精确理解和推理任务上的巨大潜力。同样，在“网页状态预测”任务中（表2），使用 RLVR 训练的世界模型，让网页导航智能体的任务成功率相对提升了 18.4%，展现了其在实际应用中的价值。\n在 视频世界模型 的探索中，作者们更是扮演了“开创者”的角色。他们将其应用于机器人操作轨迹预测。实验结果（表3 和 图3）揭示了一个惊人的事实：RLVR 仅需 几百次 的梯度更新，就能在各项指标上取得显著优于传统 MLE 方法 数十万次 训练的效果。这不仅仅是“更快”，更是“质”的飞跃。尤其是在多步预测中，模型生成重复画面的问题（Repetition Rate）从惊人的 48.6% 大幅降低到了 9.9%，这意味着生成的视频序列更加连贯和真实。\n总而言之，这篇论文为我们揭示并验证了一个强大而通用的后训练（Post-training）范式。RLVR-World 通过将强化学习与直接、可量化的任务指标相结合，有效地弥合了传统训练目标与真实世界需求之间的鸿沟，为提升各类生成模型（尤其是世界模型）的实用性和可靠性开辟了一条崭新的道路。\n接下来，我将按照您提出的六个问题，逐一进行更详细的解读。\n一、研究目标与实际意义 研究目标：本文的核心目标是解决在训练世界模型时普遍存在的 “目标函数错位” (Objective Misalignment) 问题。 实际问题：传统的训练方法（如 MLE）旨在优化代理指标（如预测下一个词元的概率），但这并不等同于优化模型在真实任务中的表现（如预测的准确性、生成内容的质量）。这种错位导致模型“学会了皮毛，却未得精髓”，限制了其在复杂决策和模拟任务中的可靠性。 行业意义：高保真、高精度的世界模型是实现更高级别人工智能的关键基石。 推动自主智能体发展：对于机器人、自动驾驶汽车、软件智能体（如自动化网页操作）而言，一个强大的世界模型意味着它们可以在“脑中”对行为的后果进行更准确的预演和规划，从而做出更优决策，减少在现实世界中进行昂贵且危险的试错。 革新模拟与规划领域：精准的世界模型本身就是强大的模拟器。这可以彻底改变从产品设计、药物研发到供应链管理的各个行业，使得大规模、低成本、高效率的模拟测试成为可能。 提升生成式AI的可靠性：该方法论不仅限于世界模型，它提供了一种通用的思路，用于提升所有生成式模型（包括大型语言模型）在特定任务上的实用性与可控性，例如减少幻觉、提升代码生成正确率等。 二、新思路与方法优势 论文提出的核心方法是 RLVR-World 框架，其创新之处体现在以下几个方面：\n核心思路：放弃间接的、基于学习的奖励模型（如 RLHF），转而采用 直接、可量化的任务指标作为强化学习的奖励信号。这是对齐（Alignment）技术路线的一次重要探索和补充。 方法特点： 统一的序列建模框架：将不同模态（文本、视频、机器人动作）统一视为词元序列，使得强大的 Transformer 架构可以被直接应用，极具通用性和扩展性。 可验证奖励 (Verifiable Rewards)：这是整个方法论的基石。奖励函数直接根据任务的“金标准”来计算。如论文公式 (4) 所示： \u0026gt; \\[ R_i = \\text{sign}(D) \\cdot D(\\hat{s}\u0026#39;_i, s\u0026#39;) \\] 这里的 \\(D\\) 就是一个可量化的评价指标，例如，对于语言任务可以是 F1 分数或准确率，对于视频任务可以是 LPIPS (Learned Perceptual Image Patch Similarity) 这种更符合人类视觉感知的指标。这种奖励信号是客观、稳定且可靠的。 高效的强化学习算法：采用了 GRPO (Group Relative Policy Optimization)。该算法通过对一组生成样本进行“内部比较”和“相对排序”来计算优势，避免了传统 RL 算法中对复杂值函数的依赖，使得训练过程更稳定、更高效。 相比优势： 直接性：直接面向最终目标进行优化，避免了代理目标的“中间商差价”，效果更显著。 效率高：如视频模型实验所示，RLVR 只需极少的微调成本（几百步）就能带来巨大性能提升，相比于动辄需要数周甚至数月的预训练，性价比极高。 问题修复能力：能有效缓解 MLE 训练带来的固有顽疾，如在视频生成中将重复率从 48.6% 降至 9.9%，显著提升了生成质量。 通用性强：该框架在语言和视频两个差异巨大的模态上都取得了成功，证明了其作为一种通用后训练范式的潜力。 三、实验设计与结果分析 论文通过一系列精心设计的实验，充分验证了 RLVR-World 框架的有效性。\n实验设计： 两大模态验证：选取了 语言 和 视频 作为代表，验证框架的通用性。 多层次任务评估： 核心能力测试：在孤立环境中测试世界模型本身的能力（文本游戏预测、视频轨迹预测）。 下游应用测试：将训练好的世界模型嵌入到一个更大的系统中（网页导航智能体），检验其对整个系统性能的提升效果。 严格的基线对比：所有 RLVR 模型都与仅经过监督微调 (SFT) 的模型进行对比，清晰地展示了 RLVR 带来的增益。同时，在部分任务中还与 GPT-4 等更强大的模型进行了比较。 关键数据与结果： 语言模型 - 文本游戏 (表1)： 任务：预测游戏状态的 JSON 对象变化。 结果：在预测“状态发生改变”的困难样本上，RLVR 模型准确率达到 33.80%，而 SFT 模型仅有 24.21%，基座模型更是只有 0.08%。这表明 RLVR 极大地增强了模型的精确推理能力。 语言模型 - 网页导航 (表2)： 任务：预测网页状态变化，并作为世界模型提升导航智能体。 结果：RLVR 将世界模型的 F1 分数从 49.94% 提升至 65.11%（相对提升 +30.3%），并最终将整个智能体的任务成功率从 12.06% 提升至 14.29%（相对提升 +18.4%）。 视频模型 - 机器人操作 (表3)： 任务：预测机器人手臂操作的未来视频帧。 结果：在多步预测中，RLVR 使得衡量感知质量的 LPIPS 指标提升了 9.2%，同时将衡量图像相似度的 SSIM 提升了 1.9%。这说明生成的视频不仅更准确，而且看起来更真实。 四、未来探索与机遇 这篇论文为该领域指明了方向，同时也留下了许多值得探索的课题和潜在机遇。\n值得探索的问题与挑战 (论文第7节也进行了讨论): 更智能的奖励函数：当前的“可验证奖励”虽然有效，但仍相对初级。如何设计能捕捉更复杂、更抽象概念（如物理规则的遵守、长期任务的一致性、美学价值）的奖励函数，是一个巨大的挑战。 突破性能瓶颈：RLVR 的训练收敛得非常快，这既是优点也是一个挑战。如何让模型在快速收敛后还能持续学习和提升，可能需要对模型架构、数据多样性和 RL 算法本身进行更深入的研究。 分布外 (OOD) 泛化能力：经过 RLVR“精调”的模型，在面对训练时未曾见过的、甚至是反事实的场景时，其表现如何？提升模型在开放世界中的泛化能力，是其走向实用化的关键。 可能催生的技术和投资机会: 企业级数字孪生 (Digital Twin)：高保真世界模型是构建数字孪生的核心技术。在制造业、城市管理、物流等领域，通过精准模拟来优化流程、预测故障，拥有巨大的商业价值。 AI驱动的科学发现 (AI for Science)：利用世界模型来模拟分子动力学、气候变化、材料科学等复杂系统，有望加速科学研究的进程。 下一代机器人技术：能够精准预见未来的机器人将拥有更强的学习能力和安全性，这将极大地推动机器人在家庭、医疗、工业等场景的普及。 模型对齐与安全工具链：围绕 RLVR 这类技术，开发一套方便研究者和开发者定义奖励函数、执行对齐训练的平台和工具，本身就是一个重要的技术和商业方向。 五、批判性视角与局限性 从批判的角度看，这篇论文也存在一些值得注意的局限性：\n对基座模型的依赖：论文坦言，RLVR 的效果上限受制于基座模型的能力。它更像是一个“优化器”而非“创造者”。如果基座模型本身能力很弱，RLVR 也难以“点石成金”。例如，在文本游戏任务中，其 1.5B 模型在处理复杂变化时的能力仍显著落后于 GPT-4。 可验证奖励的适用范围：RLVR 的成功依赖于一个易于计算的、明确的奖励函数。这在代码生成（单元测试）、游戏（胜负判断）等任务中是可行的。但对于许多目标模糊、主观性强的任务（如“写一首优美的诗”），定义这样的函数极其困难，这也是 RLHF 这类范式仍然不可或缺的原因。 探索与利用的权衡：论文在图4a中发现一个有趣的现象，在测试时，虽然 RLVR 模型的单次生成质量更高，但传统的 MLE 模型通过大量采样（生成100个样本后选最优）最终能反超。这可能暗示 RLVR 在优化过程中牺牲了一部分生成的多样性，过于集中在奖励高的区域。如何平衡优化与多样性是一个待解的问题。 评估方法的局限：在最终的智能体策略评估中，成功与否依赖于单个人类标注员的判断。虽然这样做保证了标准的一致性，但缺乏大规模评估的鲁棒性，可能存在个人偏见。 六、核心启发与学习路径 对于希望从中汲取创新想法的您来说，这篇论文提供了宝贵的启发：\n核心启发： 目标驱动的优化范式：“预训练 + 任务导向的精调”是一个极其强大的范式。其核心思想是，不要满足于训练一个通用的预测器，而要致力于打磨一个卓越的任务执行者。找到衡量最终任务成功的“那把尺子”，并用它来直接指导模型的学习。 创新的通用配方：您可以将这个思路应用到自己的领域。首先，识别出一个使用生成式模型，并且其产出可以用一个自动化脚本来评估好坏的场景。然后，应用 RLVR 框架：用模型生成结果，用评估脚本计算得分，再将此得分作为奖励信号反馈给模型进行微调。这是一个实现模型性能定点优化的“万能公式”。 需要补充的背景知识： 强化学习基础 (Reinforcement Learning)：建议深入理解策略梯度（Policy Gradient）方法，特别是 PPO 算法。这将帮助您理解本文所用的 GRPO 算法的动机和原理。经典的教材是 Sutton 和 Barto 的《强化学习导论》。 世界模型概念：可以阅读 David Ha 和 Jürgen Schmidhuber 的开创性论文 “Recurrent World Models Facilitate Policy Evolution” (参考文献 [16])，来理解世界模型的基本思想。 模型对齐技术：了解 RLHF（来自人类反馈的强化学习）的原理，并与本文的 RLVR 进行对比。这能让您更深刻地理解，为什么用“可验证”的机器反馈替代“学习来的”人类反馈，是一种重要且有效的思路。 ","date":"2025-06-16T00:00:00Z","permalink":"/rlvr-world/","title":"RLVR-World"},{"content":"根据Anthropic的定义，agent定义如下：\nAt Anthropic, we categorize all these variations as agentic systems, but draw an important architectural distinction between workflows and agents: Workflows are systems where LLMs and tools are orchestrated through predefined code paths. Agents, on the other hand, are systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks.\n简单来说就是Workflows是预先定义好的一个路径，而Agents是让其自主完成各种流程，而无需预先定义。\nworkflows Prompt chaining image.png Routing image.png Parallelization image.png Orchestrator-workers image.png Evaluator-optimizer image.png 最近热门的Gemini 2.5 Pro Capable of Winning Gold at IMO 2025论文就是使用的这个工作流。\nagents image.png 参考 Agents\nZero to One: Learning Agentic Patterns\nBuilding Effective AI Agents \\ Anthropic\nHow we built our multi-agent research system \\ Anthropic\n","date":"2025-06-14T00:00:00Z","permalink":"/agent%E6%A6%82%E8%A7%88/","title":"agent概览"},{"content":"Hybrid Engine 在 RLHF 流程中，actor model 的 generation 和 rollout 占据了绝大多数运行时间（在 veRL 是 58.9%）。并且，由于 PPO 是 online 算法，经验（experiences）必须来自于被 train 的模型本身，因此，rollout 和 training 是必须串行的。如果这两者使用不同的资源组，比如 rollout 用 2 张卡，而 training 用 4 张卡，rollout 的时候 training 的资源闲置，training 的时候 rollout 的资源闲置，无论如何都会浪费大量的计算资源。由此，veRL 将 training 和 rollout engine 放置在同一个资源组中串行执行。training 时，将 rollout engine 的显存回收（offload 到 CPU 上 或者直接析构掉），rollout 时，再将 training engine 的显存释放掉。这种将 actor model 的不同 engine 放置在同一个资源组上的方案，就称为 hybrid engine。\nimage.png ","date":"2025-06-05T00:00:00Z","permalink":"/verl%E6%80%BB%E4%BD%93%E6%A6%82%E8%A7%88/","title":"verl总体概览"},{"content":"最原生的reward_mananger:\nclass NaiveRewardManager: \u0026quot;\u0026quot;\u0026quot;The reward manager.\u0026quot;\u0026quot;\u0026quot; def __init__(self, tokenizer, num_examine, compute_score=None, reward_fn_key=\u0026quot;data_source\u0026quot;) -\u0026gt; None: self.tokenizer = tokenizer self.num_examine = num_examine # the number of batches of decoded responses to print to the console self.compute_score = compute_score or default_compute_score self.reward_fn_key = reward_fn_key def __call__(self, data: DataProto, return_dict=False): \u0026quot;\u0026quot;\u0026quot;We will expand this function gradually based on the available datasets\u0026quot;\u0026quot;\u0026quot; # If there is rm score, we directly return rm score. Otherwise, we compute via rm_score_fn if \u0026quot;rm_scores\u0026quot; in data.batch.keys(): if return_dict: return {\u0026quot;reward_tensor\u0026quot;: data.batch[\u0026quot;rm_scores\u0026quot;]} else: return data.batch[\u0026quot;rm_scores\u0026quot;] reward_tensor = torch.zeros_like(data.batch[\u0026quot;responses\u0026quot;], dtype=torch.float32) reward_extra_info = defaultdict(list) already_print_data_sources = {} for i in range(len(data)): data_item = data[i] # DataProtoItem prompt_ids = data_item.batch[\u0026quot;prompts\u0026quot;] prompt_length = prompt_ids.shape[-1] valid_prompt_length = data_item.batch[\u0026quot;attention_mask\u0026quot;][:prompt_length].sum() valid_prompt_ids = prompt_ids[-valid_prompt_length:] response_ids = data_item.batch[\u0026quot;responses\u0026quot;] valid_response_length = data_item.batch[\u0026quot;attention_mask\u0026quot;][prompt_length:].sum() valid_response_ids = response_ids[:valid_response_length] # decode prompt_str = self.tokenizer.decode(valid_prompt_ids, skip_special_tokens=True) response_str = self.tokenizer.decode(valid_response_ids, skip_special_tokens=True) ground_truth = data_item.non_tensor_batch[\u0026quot;reward_model\u0026quot;][\u0026quot;ground_truth\u0026quot;] data_source = data_item.non_tensor_batch[self.reward_fn_key] extra_info = data_item.non_tensor_batch.get(\u0026quot;extra_info\u0026quot;, None) score = self.compute_score( data_source=data_source, solution_str=response_str, ground_truth=ground_truth, extra_info=extra_info, ) if isinstance(score, dict): reward = score[\u0026quot;score\u0026quot;] # Store the information including original reward for key, value in score.items(): reward_extra_info[key].append(value) else: reward = score reward_tensor[i, valid_response_length - 1] = reward if data_source not in already_print_data_sources: already_print_data_sources[data_source] = 0 if already_print_data_sources[data_source] \u0026lt; self.num_examine: already_print_data_sources[data_source] += 1 print(\u0026quot;[prompt]\u0026quot;, prompt_str) print(\u0026quot;[response]\u0026quot;, response_str) print(\u0026quot;[ground_truth]\u0026quot;, ground_truth) if isinstance(score, dict): for key, value in score.items(): print(f\u0026quot;[{key}]\u0026quot;, value) else: print(\u0026quot;[score]\u0026quot;, score) if return_dict: return { \u0026quot;reward_tensor\u0026quot;: reward_tensor, \u0026quot;reward_extra_info\u0026quot;: reward_extra_info, } else: return reward_tensor 逻辑很简单，就是通过compute_score函数来计算score。\n","date":"2025-06-02T00:00:00Z","permalink":"/reward_mananger/","title":"reward_mananger"},{"content":"核心总结 PRM和MCTS实际上是两种可以独立使用的技术，只不过，往往它们组合使用时往往能产生1+1\u0026gt;2的效果。例如， 单独使用PRM：我们可以让模型对同一个prompt采样多个不同solution，无需MCTS，只需利用模型的temperature等随机参数让每次生成结果不同，然后用PRM对每个solution的每一步打分，最终选择分数最高的路径返回。 单独使用MCTS：使用MCTS生成多个解题路径时，不一定要用PRM来决定哪个节点值得扩展，可以用外部大模型（如GPT-4）来选择，也可以用模型自身的perplexity来判断。本质上，我们需要的是找到最值得扩展的节点，PRM只是挑选的众多方法之一。 PRM 和 MCTS 既可以应用于优化训练数据，也可以用来预测用 用于得到高质量训练数据：如rStar论文中，可以用PRM和MCTS的方式来迭代地筛选得到质量更好的思维链SFT数据或者RLHF数据，还可以生成更精确的reward model训练数据。 用于推理：很简单，推理用MCTS的方式把 test-scaling 做上来，再结合PRM的方式从众多路径中挑选最佳答案。 PRM和MCTS的缺点\n这方面 DeepSeek-R1和 kimi1.5的论文已经说得很情况了。 Process Reward Model(PRM) 在实际应用中有三大局限： 第一，难以清晰界定一般推理中的细粒度步骤，说白了，怎么定义什么为一个步骤。 第二，判断当前步骤的正误难度大，模型自动化标注不如人意，人工标注又难以拓展。 第三，引入基于模型的PRM易致reward hacking，有时为了训练 policy model，但反而更多时间去优化 reward model 去了。 对MCTS的看法： 文本的生成搜索空间指数级增长，为应对，给节点设扩展上限，却容易让模型陷入局部最优解困境。 MCTS往往要结合一个精确的PRM来用才能发挥最大效果，但PRM又有上述的问题，陷入一个死循环。 参考 https://zhuanlan.zhihu.com/p/27278317894 rStar-Math: Small LLMs Can Master Math Reasoning with Self-Evolved Deep Thinking\n","date":"2025-04-04T00:00:00Z","permalink":"/mcts%E5%92%8Cprm/","title":"MCTS和PRM"},{"content":"a = \u0026#39;global\u0026#39; def outer(): # def len(in_var): # print(\u0026#39;called my len() function: \u0026#39;, end=\u0026quot;\u0026quot;) # l = 0 # for i in in_var: # l += 1 # return l a = \u0026#39;local\u0026#39; def inner(): nonlocal a a += \u0026#39; variable\u0026#39; inner() print(\u0026#39;a is\u0026#39;, a) # print(len(a)) outer() # print(len(a)) print(\u0026#39;a is\u0026#39;, a) 此时为nonlocal a，会按照local-闭包-global的顺序找到闭包变量a。a的值为local variable\na = \u0026#39;global\u0026#39; def outer(): # def len(in_var): # print(\u0026#39;called my len() function: \u0026#39;, end=\u0026quot;\u0026quot;) # l = 0 # for i in in_var: # l += 1 # return l a = \u0026#39;local\u0026#39; def inner(): global a a += \u0026#39; variable\u0026#39; inner() print(\u0026#39;a is\u0026#39;, a) # print(len(a)) outer() # print(len(a)) print(\u0026#39;a is\u0026#39;, a) 此时为global，会从全局变量中寻找a，a的值为global variable\n","date":"2025-03-24T00:00:00Z","permalink":"/legb/","title":"LEGB"},{"content":"python调试工具，类似于vscode的调试工具，使用命令行进行调试。\n使用方法 插入式 import pdb; pdb.set_trace() 或者\nbreakpoint() 非插入式 python -m pdb [-c command] (-m module | pyfile) [args ...] 常用命令 h 即help，可用命令如下 p p x 即print(x)，用于打印变量。 pp x，使用pprint打印 w 即where，查看当前调用栈。 u和d u即up，回到上一个frame d即down，到下一个frame l 即lst l 查看前后12行代码 ll查看当前函数全部代码 b 即break，进行打断点 b x，在第x行打断点。 b 查看所有断点。 相同的有tbreak，与break的区别是第一次到该断点后会自动移除断点。即temporary breakpoint n 即next，执行下一条语句，但忽视函数调用内部细节 s 即step，执行下一条语句，如果有函数调用，则调用新frame，进入函数内部。 c 即continue，继续程序的执行直到下一个断点\nimage.png r 即return，直接跳转到当前函数return语句 until until n，使程序继续执行直到执行到行数为n的语句。 cl 即clear clear n，清除编号为n的断点 clear，清除所有断点。 ### j 即jump，向前或向后跳转，与until区别是，jump不会执行中间的语句，而是忽略他们。 display 相当于一个监视器，用于监视变量的变化 ### retval 打印当前函数最后一次返回的返回值 ### q 即quit，退出pdb调试。\n","date":"2025-03-23T00:00:00Z","permalink":"/debugger/","title":"debugger"},{"content":"理论部分在这：generate相关 ## generate参数\ndef generate( self, inputs: Optional[torch.Tensor] = None, generation_config: Optional[GenerationConfig] = None, logits_processor: Optional[LogitsProcessorList] = None, stopping_criteria: Optional[StoppingCriteriaList] = None, prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor], List[int]]] = None, synced_gpus: Optional[bool] = None, assistant_model: Optional[\u0026quot;PreTrainedModel\u0026quot;] = None, streamer: Optional[\u0026quot;BaseStreamer\u0026quot;] = None, negative_prompt_ids: Optional[torch.Tensor] = None, negative_prompt_attention_mask: Optional[torch.Tensor] = None, **kwargs, ) -\u0026gt; Union[GenerateOutput, torch.LongTensor]: 在代码中可以看到在函数入口显式的定义了很多参数。他们的具体含义如下\ninputs：tensor 形式的 token_id，通常先准备文本形式的提示词和输入，使用tokenizer转化为对应 id，这里维度通常为 [batch_size, seq_len] generation_config：一个用 GenerationConfig 类创建的对象，存储着模型生成的超参数，可以提前创建该对象并传入 .generate() logits_processor：高级功能，logits_processor 可以在每个 step 的输出概率计算完成后，对分数进行进一步的干预，改变输出的概率分布，从而影响生成的结果，例如最常见的，重复惩罚，就是使用 logits_processor 完成的。 stopping_criteria：高级功能，允许用户通过 stopping_criteria 自定义生成停止条件 prefix_allowed_tokens_fn：解码策略的一个超参数，用于前缀 token 约束 synced_gpus： DeepSpeed ZeRO Stage-3 多GPU时使用（ZeRO-3包括优化器状态+梯度+权重并行优化，而推理阶段只使用权重并行），此时需要将 synced_gpus 设置成 Ture。. 否则，如果一个 GPU 在另一个 GPU 之前完成生成，整个系统就会挂起，因为其余 GPU 尚未从最先完成的 GPU 接收到权重分片。 transformers\u0026gt;=4.28 在生成时检测到多个 GPU 会自动设置 synced_gpus=True，transformers\u0026lt;4.28 需要手动设置，本文代码环境transformers=4.41.1 assistant_model：高级功能，辅助生成模型，另一个词表完全相同的小模型，有些token使用辅助模型生成更快 streamer：流式输出控制器，现在的大模型平台都是一个字一个字显示出来的，这就是流式输出，否则的话会等所有生成完成再显示出来。这个可以自定义流式输出的方式 negative_prompt_ids：负面提示，一些前沿研究会用到，不用管 negative_prompt_attention_mask：负面提示的 attention_mask **kwargs 这里经常传入 temperature=0.7, top_k=20, max_new_tokens=512等参数，都是通过**kwargs传入进来的 其实传入的这些都是输入参数 generation_config 的属性（可以进入对应类中看一下有哪些属性，from transformers.generation.configuration_utils import GenerationConfig），你可以创建该对象并覆盖某些参数，也可以通过参数形式在调用.generate()时传进来 在后面会将传入的这些参数覆盖掉generation_config中对应的属性 下面只说明一些关键的地方 ## kwargs -\u0026gt; generation_config\n就是将kwargs中传入的kwargs的参数变成config。\ngeneration_config, model_kwargs = self._prepare_generation_config(generation_config, **kwargs) 准备logit处理器 prepared_logits_processor = self._get_logits_processor( generation_config=generation_config, input_ids_seq_length=input_ids_length, encoder_input_ids=inputs_tensor, prefix_allowed_tokens_fn=prefix_allowed_tokens_fn, logits_processor=logits_processor, device=inputs_tensor.device, model_kwargs=model_kwargs, negative_prompt_ids=negative_prompt_ids, negative_prompt_attention_mask=negative_prompt_attention_mask, ) 就是将generation_config中的采样参数封装成logit-processor，还有自己定义的processor\n准备stopping处理器 prepared_stopping_criteria = self._get_stopping_criteria( generation_config=generation_config, stopping_criteria=stopping_criteria, tokenizer=tokenizer, **kwargs ) 同理。将一些与停止有关的参数封装成stopping处理器。\nlogits warper logits warper 里面是采样时才需要运行的处理器 logits processor 是通用的处理器，每种生成模式都需要用到的 prepared_logits_warper = ( self._get_logits_warper(generation_config) if generation_config.do_sample else None ) 正式生成 # 进入模型内部生成下一个token outputs = self( **model_inputs, return_dict=True, output_attentions=output_attentions, output_hidden_states=output_hidden_states, ) if synced_gpus and this_peer_finished: continue # don\u0026#39;t waste resources running the code we don\u0026#39;t need # 取出最后一个token，.logits维度为（batch_size, seq_len, vocab_size） next_token_logits = outputs.logits[:, -1, :] # 经过前面的处理器进行分数调整 next_token_scores = logits_processor(input_ids, next_token_logits) if do_sample: next_token_scores = logits_warper(input_ids, next_token_scores) 按照是否采样来生成下一个token：\nif do_sample: probs = nn.functional.softmax(next_token_scores, dim=-1) # torch.multinomial：按照输入probs的每一行（每个batch）作为采样的概率， # 每行不放回的取出num_samples个，随机采样每个batch按输入概率取出一个 next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1) else: # torch.argmax取出输入next_token_scores中值最大的索引 next_tokens = torch.argmax(next_token_scores, dim=-1) 最后判断是否可以停止：\nunfinished_sequences = unfinished_sequences \u0026amp; ~stopping_criteria(input_ids, scores) this_peer_finished = unfinished_sequences.max() == 0 参考 https://blog.csdn.net/qq_41496421/article/details/142346738?spm=1001.2014.3001.5502 https://blog.csdn.net/qq_41496421/article/details/142580960?spm=1001.2014.3001.5501\n","date":"2025-03-09T00:00:00Z","permalink":"/generate/","title":"generate"},{"content":" einops.einsum calls einsum operations with einops-style named axes indexing, computing tensor products with an arbitrary number of tensors. Unlike typical einsum syntax, here you must pass tensors first, and then the pattern.\nAlso, note that rearrange operations such as \"(batch chan) out\", or singleton axes (), are not currently supported.\n爱因斯坦求和\n","date":"2025-01-11T00:00:00Z","permalink":"/einsum/","title":"einsum"},{"content":"pack Packs several tensors into one. See einops tutorial for introduction into packing (and how it replaces stack and concatenation).\n## unpack \u0026gt;Unpacks a single tensor into several by splitting over a selected axes. See einops tutorial for introduction into packing (and how it replaces stack and concatenation).\nimage.png ","date":"2025-01-11T00:00:00Z","permalink":"/pack-and-unpack/","title":"pack and unpack"},{"content":" einops.rearrange is a reader-friendly smart element reordering for multidimensional tensors. This operation includes functionality of transpose (axes permutation), reshape (view), squeeze, unsqueeze, stack, concatenate and other operations.\n代替reshape，给维度命名。可以用…代表不想动的维度。 ","date":"2025-01-11T00:00:00Z","permalink":"/rearrange/","title":"rearrange"},{"content":" einops.reduce combines rearrangement and reduction using reader-friendly notation.\nreduce会使维度减少。 ","date":"2025-01-11T00:00:00Z","permalink":"/reduce/","title":"reduce"},{"content":" einops.repeat allows reordering elements and repeating them in arbitrary combinations. This operation includes functionality of repeat, tile, and broadcast functions.\nrepeat是使维度增加，与reduce相反。 ## 应用 比如说repeat_kv函数就可以用einops.repeat很方便的实现\ndef repeat_kv(x: torch.Tensor, n_rep: int) -\u0026gt; torch.Tensor: \u0026quot;\u0026quot;\u0026quot;torch.repeat_interleave(x, dim=2, repeats=n_rep)\u0026quot;\u0026quot;\u0026quot; bs, slen, n_kv_heads, head_dim = x.shape if n_rep == 1: return x return ( x[:, :, :, None, :] .expand(bs, slen, n_kv_heads, n_rep, head_dim) .reshape(bs, slen, n_kv_heads * n_rep, head_dim) ) 等价于\ndef repeat_kv(x: torch.Tensor, n_rep: int) -\u0026gt; torch.Tensor: einops.repeat(x, \u0026#39;bs slen kvheads dim-\u0026gt; bs slen (kvheads n_rep) dim\u0026#39;, n_rep=n_rep).shape ","date":"2025-01-11T00:00:00Z","permalink":"/repeat/","title":"repeat"},{"content":" Convert a tensor of an imperative framework (i.e. numpy/cupy/torch/jax/etc.) to numpy.ndarray\nimage.png ","date":"2025-01-08T00:00:00Z","permalink":"/asnumpy/","title":"asnumpy"},{"content":" Parse a tensor shape to dictionary mapping axes names to their lengths.\n# Use underscore to skip the dimension in parsing. \u0026gt;\u0026gt;\u0026gt; x = np.zeros([2, 3, 5, 7]) \u0026gt;\u0026gt;\u0026gt; parse_shape(x, \u0026#39;batch _ h w\u0026#39;) {\u0026#39;batch\u0026#39;: 2, \u0026#39;h\u0026#39;: 5, \u0026#39;w\u0026#39;: 7} # `parse_shape` output can be used to specify axes_lengths for other operations: \u0026gt;\u0026gt;\u0026gt; y = np.zeros([700]) \u0026gt;\u0026gt;\u0026gt; rearrange(y, \u0026#39;(b c h w) -\u0026gt; b c h w\u0026#39;, **parse_shape(x, \u0026#39;b _ h w\u0026#39;)).shape (2, 10, 5, 7) 也就是把维度的维数映射到对应的命名。与数据无关，只看得到维度。\n","date":"2025-01-08T00:00:00Z","permalink":"/parse_shape/","title":"parse_shape"},{"content":"该工作主要梳理了LLM-based Agent 中的规划（planning）能力，原文链接：\nUnderstanding the planning of LLM agents: A survey\n文章中，作者将planning能力进一步细分为了五个维度：\n任务分解（Task Decomposition） 规划选择（Plan Selection） 外部辅助规划（External Planner） 反馈和改进（Reflection and Refinement） 记忆（Memory） 引言 (Introduction) 自主智能代理：被定义为能够完成特定任务的智能实体。它们通过感知环境、规划和执行动作来实现目标。 规划的重要性：规划是代理最关键的能力之一，它要求代理进行复杂的理解、推理和决策过程。 传统方法的局限性：以往的工作主要依赖于符号方法或基于强化学习的方法，如规划领域定义语言（PDDL）或策略学习。这些传统方法有其局限性，例如符号方法需要将自然语言描述的问题转换为符号建模，这可能需要人类专家的努力，而且缺乏容错性。强化学习方法通常需要与环境的大量样本（交互）来学习有效策略，这在数据收集耗时或成本高昂的场景中可能不切实际。 LLM的潜力：近年来，大型语言模型（LLM）的出现标志着一个范式的转变。LLM在多个领域取得了显著的成功，展示了在推理、工具使用、规划和指令跟随方面的重要智能。这种智能为将LLM作为代理的认知核心提供了可能性，从而有潜力提高规划能力。 本文工作：尽管已有调查尝试总结LLM的技术，但文献中往往缺乏对规划能力的详细分析。本调查旨在分析最新的研究工作，讨论优势和局限性，并提供对基于LLM的代理规划能力的系统性视角。 方法 任务分解 (Task Decomposition) image.png 现实世界中的任务通常是复杂和多步骤的，直接通过单步规划过程来解决复杂任务是一项巨大挑战。任务分解通过将复杂任务分解为多个简单子任务，使得规划过程更加可行。\n分解方法分类：任务分解方法主要分为两类： - 分解优先方法（Decomposition-First Methods）：首先将任务分解为子目标，然后依次为每个子目标制定计划。 - 交错分解方法（Interleaved Decomposition Methods）：在任务分解和子任务规划之间进行交错，每次只揭示当前状态的一个或两个子任务。\n分解优先方法的代表工作 HuggingGPT：LLM作为控制器，负责将人类输入的任务分解为子任务，选择模型，并生成最终响应。 Plan-and-Solve：将原始的“让我们逐步思考”转变为两步提示指令：“我们首先制定计划”和“我们执行计划”。 ProgPrompt：将自然语言描述的任务转化为编码问题，将每个动作形式化为函数，每个对象表示为变量。 #### 交错分解方法的代表工作： Chain-of-Thought (CoT)：通过构建的轨迹指导LLM对复杂问题进行推理，利用LLM的推理能力进行任务分解。 Zero-shot CoT：使用“让我们逐步思考”的指令，解锁LLM的零样本推理能力。 ReAct：将推理和规划解耦，交替进行推理（思考步骤）和规划（行动步骤）。 #### 讨论 分解优先方法的优势在于创建了子任务与原始任务之间的强关联，降低了任务遗忘和幻觉的风险。但需要额外的调整机制，以避免某个步骤的错误导致整体失败。 交错分解方法可以根据环境反馈动态调整分解，提高了容错性。但对于复杂任务，过长的轨迹可能导致LLM产生幻觉，偏离原始目标。 挑战：尽管任务分解显著提高了LLM代理解决复杂任务的能力，但仍存在挑战，包括任务分解引入的额外开销、时间成本和计算成本，以及LLM的上下文长度限制。 ### 多计划选择 (Multi-Plan Selection) 由于任务的复杂性和LLM固有的不确定性，对于给定任务，LLM代理可能会生成多种不同的计划。多计划生成涉及利用生成模型解码过程中的不确定性，通过不同的采样策略来产生多个候选计划。 - Self-consistency：采用简单的直觉，即复杂问题的解决方案很少是唯一的。通过温度采样、top-k采样等策略，获得多个不同的推理路径。 - Tree-of-Thought (ToT)：提出“采样”和“提议”两种策略来生成计划。LLM在解码过程中会采样多个计划，并通过少量示例提示生成各种计划。 - Graph-of-Thought (GoT)：在ToT的基础上增加了思想的转换，支持任意思想的聚合。 - LLM-MCTS 和 RAP：利用LLM作为启发式策略函数，通过蒙特卡洛树搜索（MCTS）算法来获取多个潜在动作。\n最优计划选择：在候选计划中选择最优计划时，采用了多种启发式搜索算法。 - Self-consistency：使用简单的多数投票策略，将得票最多的计划视为最优选择。 - Tree-of-Thought (ToT)：支持树搜索算法，如广度优先搜索（BFS）和深度优先搜索（DFS），使用LLM评估多个动作并选择最优动作。 - LLM-MCTS 和 RAP：也使用树结构辅助多计划搜索，但它们采用MCTS算法进行搜索。 - LLM A：利用经典的A算法协助LLM搜索，使用当前位置到目标位置的切比雪夫距离作为启发式成本函数来选择最优路径。 #### 讨论 多计划选择的可扩展性显著优势在于提供了在广阔搜索空间中更广泛探索潜在解决方案的能力。 然而，这种优势伴随着计算需求的增加，尤其是对于具有大量token计数或计算的模型，这在资源受限的情况下尤为重要。 LLM在计划评估中的作用引入了新的挑战，因为LLM在任务排名方面的表现仍在审查中，需要进一步验证和微调其在此特定情境下的能力。 LLM的随机性质为选择过程增加了随机性，可能影响所选计划的一致性和可靠性。\n外部规划器辅助规划 (External Planner-Aided Planning) 尽管LLM在推理和任务分解方面展现出了强大的能力，但在面对具有复杂约束的环境时，例如数学问题求解或生成可执行动作，仍然面临挑战。\n方法分类：根据引入的规划器类型，这些方法可以分为两类： - 符号规划器（Symbolic Planner）：基于形式化模型，如PDDL，使用符号推理来找到从初始状态到目标状态的最优路径。 - 神经规划器（Neural Planner）：通过强化学习或模仿学习技术训练的深度模型，针对特定领域展现出有效的规划能力。 #### 符号规划器的代表工作 - LLM+P：通过结合基于PDDL的符号规划器，使用LLM将问题组织成PDDL语言格式，并利用Fast Downward solver进行规划。 - LLM-DP：特别为动态交互环境设计，将环境反馈信息形式化为PDDL语言，并使用BFS solver生成计划。 - LLM+PDDL：在LLM生成的PDDL模型中增加手动验证步骤，并提出使用LLM生成的计划作为局部搜索规划器的初始启发式解。 - LLM+ASP：将自然语言描述的任务转换为ASP问题，然后使用ASP求解器CLINGO生成计划。 #### 神经规划器的代表工作 - CALM：结合了语言模型和基于RL的神经规划器，使用语言模型生成候选动作，然后通过DRRN策略网络重新排序以选择最优动作。 - SwiftSage：将规划过程分为快速思考和慢速思考，快速思考通过模仿学习训练的DT模型实现，慢速思考则涉及LLM基于当前状态的推理和规划。 #### 讨论 在这些策略中，LLM主要扮演支持角色，其主要功能包括解析文本反馈并提供额外的推理信息以协助规划，特别是在解决复杂问题时。 传统的符号AI系统在构建符号模型时复杂且依赖于人类专家，而LLM可以加速这一过程，有助于更快更优地建立符号模型。 符号系统的优势包括理论完备性、稳定性和可解释性。将统计AI与LLM结合，有望成为未来人工智能发展的主要趋势。\n反思和精炼 (Reflection and Refinement) 反思和精炼是规划过程中不可或缺的组成部分，它们增强了LLM代理规划的容错能力和错误纠正能力。由于LLM在规划过程中可能产生幻觉或在复杂问题上推理能力不足，导致错误或陷入“思维循环”，反思和总结失败有助于代理纠正错误并在后续尝试中打破循环。\nSelf-refine： 利用迭代过程，包括生成、反馈和精炼。在每次生成后，LLM为计划产生反馈，促进基于反馈的调整。 Reflexion： 扩展了ReAct方法，通过引入评估器来评估轨迹。LLM在检测到错误时生成自我反思，帮助纠正错误。 CRITIC： 使用外部工具，如知识库和搜索引擎，来验证LLM生成的动作。然后利用外部知识进行自我纠正，显著减少事实错误。 InteRecAgent： 使用称为ReChain的自我纠正机制。LLM用于评估由交互推荐代理生成的响应和工具使用计划，总结错误反馈，并决定是否重新规划。 LEMA： 首先收集错误的规划样本，然后使用更强大的GPT-4进行纠正。这些纠正后的样本随后用于微调LLM代理，从而在各种规模的LLaMA模型上实现显著的性能提升。 #### 讨论 自我反思策略类似于强化学习的原则，其中代理作为决策者，环境反馈触发策略网络的更新。然而，与深度强化学习通过修改模型参数来更新不同，在LLM代理中，这种更新是通过LLM自身的自我反思来实现的，最终形成文本形式的反馈。 -这些文本反馈可以作为长期和短期记忆，通过提示影响代理后续的规划输出。然而，目前还没有确凿的证据证明这种文本形式的更新最终能够使LLM代理达到特定目标。 记忆增强规划 (Memory-Augmented Planning) 记忆是提升代理规划能力的关键途径，可以帮助代理从经验中学习并适应新的情境。 #### RAG-based Memory（基于RAG的记忆) - 概念：使用检索增强生成（Retrieval-Augmented Generation, RAG）技术，将记忆以文本形式存储，并在需要时检索出来辅助规划。 - 方法：如MemoryBank、TiM 和 RecMind，这些方法通过文本编码模型将记忆编码为向量，并建立索引结构，以便在规划时检索与当前任务相关的经验。 #### Embodied Memory（体现记忆）： - 概念：通过微调（fine-tuning）LLM，将代理的历史经验样本嵌入到模型参数中，从而增强记忆能力。 - 方法：如CALM 和 TDT，这些方法使用从代理与环境交互中收集的数据来微调模型，使其能够记住与规划相关的信息，并在规划任务中表现更好。 #### 记忆更新方式： - RAG-based：提供了实时、低成本的外部记忆更新，但依赖于检索算法的准确性。 - Finetuning：提供了更大的记忆容量，但记忆更新成本较高，并且在保留细节方面存在挑战。\n讨论： 记忆增强的LLM代理在规划中表现出更强的增长潜力和容错能力，但记忆生成在很大程度上依赖于LLM自身的生成能力。 通过自我生成的记忆来提升较弱LLM代理的能力仍然是一个具有挑战性的领域。 #### 挑战 尽管记忆增强LLM代理在规划方面表现出优势，但它们在记忆生成上仍然面临挑战，特别是在自我生成记忆方面。 评估 (Evaluation) 评估代理的规划能力是研究领域中的一个关键问题。作者调查了几种主流的基准测试方法，并将它们分为以下几类：\n交互式游戏环境（Interactive Gaming Environments）： 提供基于代理动作的实时多模态反馈，如文本和视觉反馈。 例如Minecraft，代理需要收集材料制作工具以获得更多奖励，常用评价指标是代理创建的工具数量。 ### 基于文本的交互环境（Text-based interactive environments）： 代理位于用自然语言描述的环境中，动作和位置有限。 常用评价指标是成功率或获得的奖励，例如ALFWorld和ScienceWorld。 ### 交互式检索环境（Interactive Retrieval Environments）： 模拟人类在现实生活信息检索和推理的过程。 代理可以与搜索引擎和其他网络服务交互，通过搜索关键词或执行点击、前进、后退操作来获取更多信息，完成问答任务或信息检索任务。 ### 交互式编程环境（Interactive Programming Environments）： 模拟程序员与计算机之间的交互，测试代理解决计算机相关问题的规划能力。 代理需要与计算机交互，通过编写代码或指令来解决问题。\n实验 作者在四个基准测试上进行了实验，以验证代表性方法的性能。这些基准测试包括==ALFWorld==、==ScienceWorld==、==HotPotQA==和==FEVER==，涵盖了交互式游戏和问答基准测试。 实验结果显示，性能随着成本的增加而提高，表明更详细的思考（即消耗更多的token）可以带来性能上的提升。 另外，对于复杂任务，示例（例如Zero-shot CoT和Few-shot CoT）对于LLM进一步理解任务至关重要。 反思（Reﬂexion）在提高成功率方面发挥了关键作用，尤其是在复杂任务中，显示了LLM具备错误纠正能力。 讨论： 现有的基准测试大多依赖于任务的最终完成状态，缺乏细粒度的逐步评估。 环境反馈通常是规则化的、简化的，并且与现实世界场景有距离。 未来的发展方向可能包括利用高智能模型如LLM来设计更现实的评估环境。\n结论和未来方向 (Conclusions and Future Directions) 进展总结：自LLM展现出智能以来，使用LLM增强代理规划能力的研究受到了越来越多的关注。作者概述了主要的研究方向，并在前文中对各种方法进行了详细比较和分析。 实验结果：作者在四个基准测试上进行了实验，比较了几种代表性方法的有效性，并指出随着投入成本的增加，性能也随之提高。 ### 挑战 幻觉问题（Hallucinations）：LLM在规划过程中可能会产生幻觉，导致非理性的计划或无法遵循复杂指令。 生成计划的可行性：与基于符号的人工智能相比，LLM在优化过程中可能难以遵守复杂约束，导致生成的计划缺乏可行性。 计划的效率：现有LLM代理的规划过程可能没有考虑生成计划的效率，未来的发展可能需要引入额外的效率评估模块。 ### 未来方向： 多模态环境反馈：考虑集成多模态大型模型的发展，并重新审视相关的规划策略，以处理包括图像、音频等在内的多模态反馈。 细粒度评估：利用高智能模型如LLM设计更现实的评估环境，提供更细致的逐步评估，以更好地模拟现实世界场景。 ","date":"2024-12-20T00:00:00Z","permalink":"/agent-planning%E7%BB%BC%E8%BF%B0/","title":"agent planning综述"},{"content":"gather 参数：\ninput (Tensor) – the source tensor dim (int) – the axis along which to index index (LongTensor) – the indices of elements to gather out (Tensor_,__optional_) – the destination tensor sparse_grad (bool,optional) – If True, gradient w.r.t. input will be a sparse tensor. \u0026gt; gather操作是scatter操作的逆操作，如果说scatter是根据index和src求self(input)，那么gather操作是根据self(input)和index求src。具体来说gather操作是根据index指出的索引，沿dim指定的轴收集input的值。 out[i][j][k] = input[index[i][j][k]][j][k] # if dim == 0 out[i][j][k] = input[i][index[i][j][k]][k] # if dim == 1 out[i][j][k] = input[i][j][index[i][j][k]] # if dim == 2 对于gather操作来说，有三个约束需要满足：\n（1）对于所有的维度d != dim，有input.size(d) == index.size(d)，对于维度dim来说，有index.size(d) \u0026gt;= 1； （2）张量out的维度大小必须和index相同； （3）和scatter一样，index中的索引值必须在input.size(dim)范围内。 ### example ### code example\nimport torch t = torch.Tensor([[1, 2], [3, 4]]) # t = 1 2 # 3 4 index = torch.LongTensor([[0, 0], [1, 0]]) # index = 0 0 # 1 0 # dim = 0 : [[1,2],[3,2]] # dim = 1 : [[1,1],[4,3]] # index = 0 # 1 # dim = 0 : [[1],[3]] # dim = 1 : [[1],[4]] # index = 0 1 # dim = 0 : [[1, 4]] # dim = 1 : [[1, 2]] scatter Writes all values from the tensor into at the indices specified in the tensor. For each value in , its output index is specified by its index in for and by the corresponding value in for .`src``self``index``src``src``dimension != dim``index``dimension = dim` For a 3-D tensor, is updated as:`self` 参数： - dim (int) – the axis along which to index - index (LongTensor) – the indices of elements to scatter, can be either empty or the same size of src. When empty, the operation returns identity - src (Tensor) – the source element(s) to scatter, incase value is not specified - value (float) – the source element(s) to scatter, incase src is not specified\nself[index[i][j][k]][j][k] = src[i][j][k] # if dim == 0 self[i][index[i][j][k]][k] = src[i][j][k] # if dim == 1 self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2 看了上面这个操作就理解了。 由此可以得出以下约束： 1. 张量self，张量index和张量src的维度数量必须相同（即三者的.dim()必须相等，注意不是维度大小）； 2. 对于每一个维度d，有index.size(d)\u0026lt;=src.size(d)； 3. 对于每一个维度d，如果d!=dim，有index.size(d)\u0026lt;=self.size(d)； 对于index也有一些约束： 1. 张量index中的每一个值大小必须在[0, self.size(dim)-1]之间； 2. 张量index沿dim维的那一行中所有值都必须是唯一的（弱约束，违反不会报错，但是会造成没有意义的操作）。\nexample ### code example\nimport torch a = torch.arange(10).reshape(2,5).float() print(f\u0026quot;a: \\n{a}\u0026quot;) b = torch.zeros(3, 5) print(f\u0026quot;b: \\n{b}\u0026quot;) b_= b.scatter(dim=0, index=torch.LongTensor([[1, 2, 1, 1, 2], [2, 0, 2, 1, 0]]), src=a) print(f\u0026quot;b_: \\n{b_}\u0026quot;) # tensor([[0, 6, 0, 0, 9], # [0, 0, 2, 8, 0], # [5, 1, 7, 0, 4]]) scatter_add_ 这个函数和scatter基本上没有任何区别，区别在于上图中的对于self中同一位置的填入是随机的，self[3,0]不确定是7还是9，self[0,1]不确定是8还是10，但是使用scatter_add就将所有即将填入同一位置的数相加，例子如下： ### example 参考 https://zhuanlan.zhihu.com/p/158993858 ","date":"2024-12-20T00:00:00Z","permalink":"/gather%E5%92%8Cscatter/","title":"gather和scatter"},{"content":"LLaMA介绍 LLaMA 是目前为止，效果最好的开源 LLM 之一。\n论文的核心思想：相比于GPT，更小的模型+更多的训练数据**也可以获得可比的效果\n基于更多 tokens 的训练集，在各种推理预算下，训练出性能最佳的一系列语言模型，称为 LLaMA，参数范围从 7B 到 65B 不等，与现有最佳 LLM 相比，其性能是有竞争力的。比如，LLaMA-13B 在大多数基准测试中优于 GPT-3，尽管其尺寸只有 GPT-3 的十分之一。作者相信，LLaMA 将有助于使 LLM 的使用和研究平民化，因为它可以在单个 GPU 上运行！在规模较大的情况下，LLaMA-65B 也具有与最佳大型语言模型（如 Chinchilla 或 PaLM-540B）相竞争的能力。\nLLaMA1、2的主要差别在训练上下文长度、训练token数、注意力机制以及对齐方法上。\n模型 训练长度 分词器 词表大小 位置编码 激活层 标准化 训练token数 链接 精度 注意力机制 有无chat版本 Alignment LLaMA 2,048 BPE（Sentence-Piece） 32k ROPE SwiGLU 基于 RMSNorm 的 Pre-Norm 1万亿(6.7B,13B)\n1.4万亿（32.5B,65.2B） http://arxiv.org/abs/2302.13971 fp16 MHA 0 LLaMA2 4,096 同上 32k ROPE 同上 同上 2万亿 https://arxiv.org/abs/2307.09288 bf16 34B,70B GQA, 其他MHA 1 SFT+RLHF(拒绝采样+PPO) （表来自LLaMA家族）\nLLaMA1 训练数据 image.png 训练参数 image.png RMSnorm 与 Layer Norm 相比，RMS Norm的主要区别在于去掉了减去均值的部分，计算公式为：\n\\[\\overline{a}_{i}=\\frac{a_{i}}{RMS(a)}\\]\n其中\n\\[RMS(a)=\\sqrt{\\frac{1}{n}\\Sigma_{i=1}^{n}a_{i}^{2}} \\\\ \\]\n此外RMSNorm 还可以引入可学习的缩放因子g，从而得到\n\\[\\overline{a}_i=\\frac{a_i}{RMS(\\boldsymbol{a})}g_i\\]\nPre-norm和Post-norm 注意其使用的是Pre-norm结构，与Post-norm结构差异如下： 关于Pre Norm的效果和Post Norm效果差异，相关分析在这两篇文章中：\n模型优化漫谈：BERT的初始标准差为什么是0.02？\n为什么Pre Norm的效果不如Post Norm？\n总结来说就是Pre-norm加深的是模型的宽度，而不是深度，从而导致训练效果不如Post-norm，但可以缓解Post-norm的梯度消失。\n代码 class LlamaRMSNorm(nn.Module): def __init__(self, hidden_size, eps=1e-6): \u0026quot;\u0026quot;\u0026quot; LlamaRMSNorm is equivalent to T5LayerNorm \u0026quot;\u0026quot;\u0026quot; super().__init__() self.weight = nn.Parameter(torch.ones(hidden_size)) self.variance_epsilon = eps # eps 防止取倒数之后分母为0 def forward(self, hidden_states): input_dtype = hidden_states.dtype variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True) hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon) # rsqrt 即sqrt后取倒数 # weight 是末尾乘的可训练参数, 即g_i return (self.weight * hidden_states).to(input_dtype) RoPE RoPE 的核心思想是“通过绝对位置编码的方式实现相对位置编码”，可以说是具备了绝对位置编码的方便性，同时可以表示不同 token 之间的相对位置关系。RoPE 是将位置编码和 query或者key进行相乘。\n\\[\\begin{bmatrix}\\cos m\\theta_0\u0026amp;-\\sin m\\theta_0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\\\sin m\\theta_0\u0026amp;\\cos m\\theta_0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\cos m\\theta_1\u0026amp;-\\sin m\\theta_1\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\sin m\\theta_1\u0026amp;\\cos m\\theta_1\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\u0026amp;\\vdots\\\\0\u0026amp;0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;\\cos m\\theta_{d/2-1}\u0026amp;-\\sin m\\theta_{d/2-1}\\\\0\u0026amp;0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;\\sin m\\theta_{d/2-1}\u0026amp;\\cos m\\theta_{d/2-1}\\end{bmatrix}\\begin{bmatrix}q_0\\\\q_1\\\\q_2\\\\q_3\\\\\\vdots\\\\q_{d-2}\\\\q_{d-1}\\end{bmatrix}\\]\n由于矩阵太稀疏，会造成浪费，因此计算时是这么做的：\n\\[\\begin{bmatrix}q_0\\\\q_1\\\\q_2\\\\q_3\\\\\\vdots\\\\q_{d-2}\\\\q_{d-1}\\end{bmatrix}\\otimes\\begin{bmatrix}\\cos m\\theta_0\\\\\\cos m\\theta_0\\\\\\cos m\\theta_1\\\\\\cos m\\theta_1\\\\\\vdots\\\\\\cos m\\theta_{d/2-1}\\\\\\cos m\\theta_{d/2-1}\\end{bmatrix}+\\begin{bmatrix}-q_1\\\\q_0\\\\-q_3\\\\q_2\\\\\\vdots\\\\-q_{d-1}\\\\q_{d-2}\\end{bmatrix}\\otimes\\begin{bmatrix}\\sin m\\theta_0\\\\\\sin m\\theta_0\\\\\\sin m\\theta_1\\\\\\sin m\\theta_1\\\\\\vdots\\\\\\sin m\\theta_{d/2-1}\\\\\\sin m\\theta_{d/2-1}\\end{bmatrix}\\]\n此外，角度的计算方式如下：\n\\[\\theta_j=10000^{-2j/d},j\\in[1,2,\\dots,d/2]\\]\n代码 class LlamaRotaryEmbedding(torch.nn.Module): def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None): super().__init__() inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float().to(device) / dim)) self.register_buffer(\u0026quot;inv_freq\u0026quot;, inv_freq) # Build here to make `torch.jit.trace` work. self.max_seq_len_cached = max_position_embeddings t = torch.arange(self.max_seq_len_cached, device=self.inv_freq.device, dtype=self.inv_freq.dtype) freqs = torch.einsum(\u0026quot;i,j-\u0026gt;ij\u0026quot;, t, self.inv_freq) # Different from paper, but it uses a different permutation # in order to obtain the same calculation emb = torch.cat((freqs, freqs), dim=-1) dtype = torch.get_default_dtype() self.register_buffer(\u0026quot;cos_cached\u0026quot;, emb.cos()[None, None, :, :].to(dtype), persistent=False) self.register_buffer(\u0026quot;sin_cached\u0026quot;, emb.sin()[None, None, :, :].to(dtype), persistent=False) def forward(self, x, seq_len=None): # x: [bs, num_attention_heads, seq_len, head_size] # This `if` block is unlikely to be run after we build sin/cos in `__init__`. # Keep the logic here just in case. if seq_len \u0026gt; self.max_seq_len_cached: self.max_seq_len_cached = seq_len t = torch.arange(self.max_seq_len_cached, device=x.device, dtype=self.inv_freq.dtype) freqs = torch.einsum(\u0026quot;i,j-\u0026gt;ij\u0026quot;, t, self.inv_freq) # Different from paper, but it uses a different permutation # in order to obtain the same calculation emb = torch.cat((freqs, freqs), dim=-1).to(x.device) self.register_buffer(\u0026quot;cos_cached\u0026quot;, emb.cos()[None, None, :, :].to(x.dtype), persistent=False) self.register_buffer(\u0026quot;sin_cached\u0026quot;, emb.sin()[None, None, :, :].to(x.dtype), persistent=False) return ( self.cos_cached[:, :, :seq_len, ...].to(dtype=x.dtype), self.sin_cached[:, :, :seq_len, ...].to(dtype=x.dtype), ) def rotate_half(x): \u0026quot;\u0026quot;\u0026quot;Rotates half the hidden dims of the input.\u0026quot;\u0026quot;\u0026quot; x1 = x[..., : x.shape[-1] // 2] x2 = x[..., x.shape[-1] // 2 :] return torch.cat((-x2, x1), dim=-1) def apply_rotary_pos_emb(q, k, cos, sin, position_ids): # The first two dimensions of cos and sin are always 1, so we can `squeeze` them. cos = cos.squeeze(1).squeeze(0) # [seq_len, dim] sin = sin.squeeze(1).squeeze(0) # [seq_len, dim] cos = cos[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim] sin = sin[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim] q_embed = (q * cos) + (rotate_half(q) * sin) k_embed = (k * cos) + (rotate_half(k) * sin) return q_embed, k_embed SwiGLU \\[\\begin{aligned} \\mathrm{FFN}_{\\mathrm{SwiGLU}}(x,W,V,W_{2})\u0026amp;=\\mathrm{SwiGLU}(x,W,V)W_{2}\\\\\\mathrm{SwiGLU}(x,W,V)\u0026amp;=\\mathrm{Swish}_{\\beta}(xW)\\otimes xV\\\\\\mathrm{Swish}_{\\beta}(x)\u0026amp;=x\\sigma(\\beta x) \\end{aligned}\\]\n代码 class LlamaMLP(nn.Module): def __init__( self, hidden_size: int, intermediate_size: int, hidden_act: str, ): super().__init__() self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False) self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False) self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False) # config 中 hidden_act = \u0026#39;silu\u0026#39; # \u0026#39;silu\u0026#39; 和 \u0026#39;swish\u0026#39; 对应的激活函数均为：SiLUActivation # https://github.com/huggingface/transformers/blob/717dadc6f36be9f50abc66adfd918f9b0e6e3502/src/transformers/activations.py#L229 self.act_fn = ACT2FN[hidden_act] def forward(self, x): # 对应上述公式的 SwiGLU return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) 实验结果 常识推理任务 image.png LLaMA - 13B模型虽然比GPT - 3小10倍，但在大多数基准上也优于GPT - 3。 除BoolQ外，LLaMA - 65B在所有报告的基准上都优于Chinchilla-70B。 除了在BoolQ和WinoGrande上，LLaMA-65B在所有地方都超过了PaLM540B。 阅读理解任务 image.png 可以看到，LLaMA-13B比GPT-3高出了几个百分点。 LLaMA-65B的表现已经接近甚至超越PaLM-540B的表现。 LLaMA2 Llama1只做了预训练，Llama2做了预训练+SFT+RLHF KV Cache image.png LLM推理过程分为Prefill和Decode两个阶段。\nPrefill阶段会对Prompt中所有的token做并行计算，得到Prompt中所有Tokens的KV Cache以及计算得到生成的第一个Token。Prompt阶段Token计算得到的KV Cache会保存下来，留给Decode阶段复用。 Decode阶段是一个自回归过程，每decode一个新的Token，都需要用到所有之前计算得到的KV Cache来计算当前query token的Attention。因此，当输出长度越来越大或者context很长时，KV Cache将会占用大量的显存。 使用KV cache时位置信息怎么注入？ 初次学习KV cache时，虽然原理比较简单易懂，但是对于后续的输入只有一个token这里产生了些许困惑，后续只输入一个token的话，位置编码该怎么办呢？于是我比较简单粗暴地猜测位置index随着推理不断更新，当时翻了各种资料也没有得到解释，后面翻了翻llama的源码，发现我的猜测还真是正确的。\ndef forward(self, tokens: torch.Tensor, start_pos: int): \u0026quot;\u0026quot;\u0026quot; Perform a forward pass through the Transformer model. Args: tokens (torch.Tensor): Input token indices. start_pos (int): Starting position for attention caching. Returns: torch.Tensor: Output logits after applying the Transformer model. \u0026quot;\u0026quot;\u0026quot; _bsz, seqlen = tokens.shape h = self.tok_embeddings(tokens) self.freqs_cis = self.freqs_cis.to(h.device) freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen] mask = None if seqlen \u0026gt; 1: mask = torch.full( (seqlen, seqlen), float(\u0026quot;-inf\u0026quot;), device=tokens.device ) mask = torch.triu(mask, diagonal=1) # When performing key-value caching, we compute the attention scores # only for the new sequence. Thus, the matrix of scores is of size # (seqlen, cache_len + seqlen), and the only masked entries are (i, j) for # j \u0026gt; cache_len + i, since row i corresponds to token cache_len + i. mask = torch.hstack([ torch.zeros((seqlen, start_pos), device=tokens.device), mask ]).type_as(h) for layer in self.layers: h = layer(h, start_pos, freqs_cis, mask) h = self.norm(h) output = self.output(h).float() return output 可以看到forward函数中的start_pos参数代表着位置信息，freqs_cis是实现RoPE位置编码需要用到的。\n注意 freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen]这一行，即是实现了rope相对位置编码的kv cache的核心。\n代码 class Attention(nn.Module): # ... self.cache_k = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() self.cache_v = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() def forward( self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor], ): # 假设当前x为(1, 1, dim)，也就是上一个预测的token # self-attention的输入，标准的(bs, seqlen, hidden_dim) bsz, seqlen, _ = x.shape # 计算当前token的qkv # q k v分别进行映射，注意这里key, value也需要先由输入进行映射再和kv_cache里面的key, value进行拼接 xq, xk, xv = self.wq(x), self.wk(x), self.wv(x) xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim) xk = xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) xv = xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) # 对当前输入的query和key进行RoPE，注意kv_cache里面的key已经做过了RoPE xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis) # 缓存当前token的kv self.cache_k = self.cache_k.to(xq) self.cache_v = self.cache_v.to(xq) self.cache_k[:bsz, start_pos: start_pos + seqlen] = xk self.cache_v[:bsz, start_pos: start_pos + seqlen] = xv # 取出前seqlen个token的kv缓存 # 取出全部缓存的key和value（包括之前在cache里面的和本次输入的），作为最终的key和value keys = self.cache_k[:bsz, : start_pos + seqlen] values = self.cache_v[:bsz, : start_pos + seqlen] # 将kv重复填充，使kv和q的头数个数相同 # repeat k/v heads if n_kv_heads \u0026lt; n_heads，对齐头的数量 keys = repeat_kv(keys, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) values = repeat_kv(values, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) # 计算当前token的attention score，，注意mask需要加上，另外维度要对应上 xq = xq.transpose(1, 2) # (bs, n_local_heads, seqlen, head_dim) keys = keys.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) values = values.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim) if mask is not None: scores = scores + mask # (bs, n_local_heads, seqlen, cache_len + seqlen) scores = F.softmax(scores.float(), dim=-1).type_as(xq) output = torch.matmul(scores, values) # (bs, n_local_heads, seqlen, head_dim) output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1) return self.wo(output) MQA\u0026amp;GQA image.png 为什么不继续使用MHA？ 标准的mha中，KV heads的数量和Query heads的数量相同，每一个q head对应一个独立的kv head，但这样的开销比较大。 MQA 标准的MHA中，KV heads的数量和Query heads的数量相同，每一个q head对应一个独立的kv head，但这样的开销比较大。\nMQA比较极端，只保留一个KV Head，多个Query Heads共享相同的KV Head。这相当于不同Head的Attention差异，全部都放在了Query上，需要模型仅从不同的Query Heads上就能够关注到输入hidden states不同方面的信息。这样做的好处是，极大地降低了KV Cache的需求，但是会导致模型效果有所下降。\nGQA GQA就是在MHA和MQA之间做了一个平衡。对query heads进行分组，分成几组就对应多少个kv heads，然后每一组内的query Heads共享相同的KV head。 GQA可以在减少计算量和KV Cache同时确保模型效果不受到大的影响。 SFT 监督微调（Supervised Fine-Tuning, SFT）是对已经预训练的模型进行特定任务的训练，以提高其在该任务上的表现。预训练模型通常在大量通用数据上进行训练，学到广泛的语言知识和特征。在SFT过程中，利用特定任务的数据，对模型进行进一步调整，使其更适合该任务。\nSFT数据一般就是\u0026lt;prompt, response\u0026gt;数据对。在训练方式上和pretrain没有任何区别，即得到当前token对应的logit，以next token作为标签计算交叉熵损失。\npretrain 是在背书，纯粹的学习知识；sft 则是在做题，学习的是指令 follow 能力。\n一些要点 少量高质量数据集训练模型的效果，要好于大量低质量数据集的训练效果。分析数据和清洗数据就是 sft 阶段 90% 的工作量。\nsft 会让模型见到最重要的 eos_token，pretrain 模型因为没见过该 token 而无法停止生成。\nsft 的 prompt 不做 loss，但这并不是说它不能做 loss。主要原因是 prompt 的同质化比较严重，不做 loss_mask 的话，同样的一句话会被翻来覆去的学，但如果你能保证你的每条 prompt 都是独一无二的，就完全可以省去 prompt 的 loss_mask 环节。\n为了提高模型训练效率，将多组数据进行拼接，尽量填满4096。但对于分类任务会出现问题，详见https://zhuanlan.zhihu.com/p/809229182。\n经过一通分析后，我们发现，新的训练方式改变了短 answer 数据的 loss 占比，毕竟模型在计算 loss 的时候，是先算一个句子内每个 token 的 平均 loss，再算一个 batch_size 内的平均 loss。\n分类任务的 answer 通常只有 1 个 token：不 concat 的时候，它的 loss 贡献就是 1 / batch_size；concat 的时候，它就需要先和别的 answer 的 token 算平均 loss，再贡献 1 / batch_size。\n这也就是说，采用 llama2 提到的 先 concat 语料再做 sft 训练，会对短 answer 数据很不公平，也就更容易造成短 answer 数据的欠拟合，pretrain 由于所有 token 都算 loss 则没有这个现象。最终，我们通过上采样短 answer 数据，成功的避免了分类任务的效果下滑。\n实验结果 - Llama 2模型优于Llama 1模型。 - Llama 2-70B比Llama 1-65B在MMLU和BBH上的结果分别提高了≈5和≈8个点。 - Llama 2-7B和30B模型在除代码基准以外的所有类别上都优于相应大小的MPT模型。 - Llama 2-7B和34B在所有类别的基准测试集上都优于Falcon-7B和40B模型。\n参考 [KV Cache优化]🔥MQA/GQA/YOCO/CLA/MLKV笔记: 层内和层间KV Cache共享 - 知乎 (zhihu.com) Transformers KV Caching Explained | by João Lages | Medium https://zhuanlan.zhihu.com/p/679640407 LLaMA家族 https://zhuanlan.zhihu.com/p/809229182 ","date":"2024-09-26T00:00:00Z","permalink":"/llama%E7%B3%BB%E5%88%97/","title":"llama系列"},{"content":" image.png LLM解码时采用的自回归采样，其过程如下：\n小模型使用前缀作为输入，将输出结果处理+归一化成概率分布后，采样生成下一个token。 将生成的token和前缀拼接成新的前缀，重复执行1，直到生成EOS或者达到最大token数目。 将模型输出logits的转换成概率，有几种常用的采样方法，包括argmax、top-k和top-n等 # 贪心搜索 直接选择概率最高的单词。这种方法简单高效，但是可能会导致生成的文本过于单调和重复 # 随机采样 按照概率分布随机选择一个单词。这种方法可以增加生成的多样性，但是可能会导致生成的文本不连贯和无意义。 # beam search 维护一个大小为 k 的候选序列集合，每一步从每个候选序列的概率分布中选择概率最高的 k 个单词，然后保留总概率最高的 k 个候选序列。这种方法可以平衡生成的质量和多样性，但是可能会导致生成的文本过于保守和不自然。 # top-k 选取前k个token，然后再重新生成概率分布，再进行抽样 它可以与其他解码策略结合使用，例如温度调节（Temperature Scaling）、重复惩罚（Repetition Penalty）、长度惩罚（Length Penalty）等，来进一步优化生成的效果。 代码:\nimport torch from labml_nn.sampling import Sampler # Top-k Sampler class TopKSampler(Sampler): # k is the number of tokens to pick # sampler is the sampler to use for the top-k tokens # sampler can be any sampler that takes a logits tensor as input and returns a token tensor; e.g. `TemperatureSampler`. def __init__(self, k: int, sampler: Sampler): self.k = k self.sampler = sampler # Sample from logits def __call__(self, logits: torch.Tensor): # New logits filled with −∞; i.e. zero probability zeros = logits.new_ones(logits.shape) * float(\u0026#39;-inf\u0026#39;) # Pick the largest k logits and their indices values, indices = torch.topk(logits, self.k, dim=-1) # Set the values of the top-k selected indices to actual logits. # Logits of other tokens remain −∞ zeros.scatter_(-1, indices, values) # Sample from the top-k logits with the specified sampler. return self.sampler(zeros) top-p top-k 有一个缺陷，那就是“k 值取多少是最优的？”非常难确定。于是出现了动态设置 token 候选列表大小策略——即核采样（Nucleus Sampling）。 top-p 采样的思路是，在每一步，只从累积概率超过某个阈值 p 的最小单词集合中进行随机采样，而不考虑其他低概率的单词。这种方法也被称为核采样（nucleus sampling），因为它只关注概率分布的核心部分，而忽略了尾部部分。例如，如果 p=0.9，那么我们只从累积概率达到 0.9 的最小单词集合中选择一个单词，而不考虑其他累积概率小于 0.9 的单词。这样可以避免采样到一些不合适或不相关的单词，同时也可以保留一些有趣或有创意的单词。\nimport torch from torch import nn from labml_nn.sampling import Sampler class NucleusSampler(Sampler): \u0026quot;\u0026quot;\u0026quot; ## Nucleus Sampler \u0026quot;\u0026quot;\u0026quot; def __init__(self, p: float, sampler: Sampler): \u0026quot;\u0026quot;\u0026quot; :param p: is the sum of probabilities of tokens to pick $p$ :param sampler: is the sampler to use for the selected tokens \u0026quot;\u0026quot;\u0026quot; self.p = p self.sampler = sampler # Softmax to compute $P(x_i | x_{1:i-1})$ from the logits self.softmax = nn.Softmax(dim=-1) def __call__(self, logits: torch.Tensor): \u0026quot;\u0026quot;\u0026quot; Sample from logits with Nucleus Sampling \u0026quot;\u0026quot;\u0026quot; # Get probabilities $P(x_i | x_{1:i-1})$ probs = self.softmax(logits) # Sort probabilities in descending order sorted_probs, indices = torch.sort(probs, dim=-1, descending=True) # Get the cumulative sum of probabilities in the sorted order cum_sum_probs = torch.cumsum(sorted_probs, dim=-1) # Find the cumulative sums less than $p$. nucleus = cum_sum_probs \u0026lt; self.p # Prepend ones so that we add one token after the minimum number # of tokens with cumulative probability less that $p$. nucleus = torch.cat([nucleus.new_ones(nucleus.shape[:-1] + (1,)), nucleus[..., :-1]], dim=-1) # Get log probabilities and mask out the non-nucleus sorted_log_probs = torch.log(sorted_probs) sorted_log_probs[~nucleus] = float(\u0026#39;-inf\u0026#39;) # Sample from the sampler sampled_sorted_indexes = self.sampler(sorted_log_probs) # Get the actual indexes res = indices.gather(-1, sampled_sorted_indexes.unsqueeze(-1)) # return res.squeeze(-1) Temperature采样 详见温度超参数\nspeculative decoding 大型语言模型（LLM）的推理通常需要使用自回归采样。它们的推理过程相当缓慢，需要逐个token地进行串行解码。因此，大型模型的推理过程往往受制于访存速度，生成每个标记都需要将所有参数从存储单元传输到计算单元，因此内存访问带宽成为严重的瓶颈。\n为了解决推理速度慢的问题，已经进行了许多针对推理的工程优化，例如改进的计算核心实现、多卡并行计算、批处理策略等等。然而，这些方法并没有从根本上解决LLM解码过程是受制于访存带宽的问题。\n投机采样是一种可以从根本上解码计算访存比的方法，保证和使用原始模型的采样分布完全相同。它使用两个模型：一个是原始目标模型，另一个是比原始模型小得多的近似模型。近似模型用于进行自回归串行采样，而大型模型则用于评估采样结果。解码过程中，某些token的解码相对容易，某些token的解码则很困难。因此，简单的token生成可以交给小型模型处理，而困难的token则交给大型模型处理。这里的小型模型可以采用与原始模型相同的结构，但参数更少，或者干脆使用n-gram模型。小型模型不仅计算量较小，更重要的是减少了内存访问的需求。 ## 采样过程 投机采样过程如下：\n用小模型Mq做自回归采样连续生成 γ 个tokens。 把生成的γ个tokens和前缀拼接一起送进大模Mp执行一次forwards。 使用大、小模型logits结果做比对，如果发现某个token小模型生成的不好，重新采样这个token。重复步骤1。 如果小模型生成结果都满意，则用大模型采样下一个token。重复步骤1。 第2步，将γ个tokens和前缀拼成一起作为大模型输入，和自回归相比，尽管计算量一样，但是γ个tokens可以同时参与计算，计算访存比显著提升。\n第3步，如何评价一个token生成的不好？如果q(x) \u0026gt; p(x)（p，q表示在大小模型采样概率，也就是logits归一化后的概率分布）则以一定1-p(x)/q(x)为概率拒绝这个token的生成，从一个新的概率分布p’(x) = norm(max(0, p(x) − q(x)))中重新采样一个token。 ## 例子 例如，在第一行中，近似模型生成了5个token，目标模型使用这5个token和前缀拼接后的句子”[START] japan’s bechmark bond”作为输入，通过一次推理执行来验证小模型的生成效果。这里，最后一个token ”bond“被目标模型拒绝，重新采样生成”n“。这样中间的四个tokens，”japan” “’s” “benchmark”都是小模型生成的。以此类推，由于用大模型对输入序列并行地执行，大模型只forward了9次，就生成了37个tokens。尽管总的大模型的计算量不变，但是大模型推理一个1个token和5个token延迟类似，这还是比大模型一个一个蹦词的速度要快很多。 # 惩罚参数 频率惩罚参数修改概率分布，以生成模型在训练过程中不常见的词。这鼓励模型生成新颖或不太常见的词。它的工作原理是缩放模型在训练过程中常见词的对数概率，从而降低模型生成这些常见词的可能性。\n而存在惩罚参数修改概率分布，以使输入提示中出现的词不太可能出现在输出中。这鼓励模型生成输入中没有的词。它的工作原理是缩放输入中存在词的对数概率，使模型不太可能生成输入中已经存在的单词。\n简单地说，频率惩罚控制模型输出用词的新颖性，而存在惩罚控制模型谈论新主题的可能性。 # 参考 ChatGPT模型中的惩罚机制_frequency penalty-CSDN博客\n大模型推理妙招—投机采样（Speculative Decoding） - 知乎 (zhihu.com)\n大模型文本生成——解码策略（Top-k \u0026amp; Top-p \u0026amp; Temperature） (zhihu.com)\nhttps://zhuanlan.zhihu.com/p/667025336\n","date":"2024-09-05T00:00:00Z","permalink":"/generate%E7%9B%B8%E5%85%B3/","title":"frequency_penalty\u0026presence_penalty"},{"content":"线性Transformer \\[V_i\u0026#39;=\\frac{\\sum_{j=1}^N sim(Q_i,K_j)V_j}{\\sum_{j=1}^N sim(Q_i,K_j)}\\] 注意下标i。 其中\n\\[sim(Q_{i},K_{j})=\\phi(Q_{i},K_{j})\\]\n此时有：\n\\[V_{i}^{\\prime}=\\frac{\\phi(Q_{i})\\sum_{j=1}^{i}\\phi(K_{j})^{T}V_{j}}{\\phi(Q_{i})\\sum_{j=1}^{i}\\phi(K_{j})^{T}}\\]\n注意可以将\\(\\phi(Q_{i})\\)提出来。\n原始Transformer的计算复杂度随序列长N呈二次方增长，这是因为attention的计算包含两层for循环，外层是对于每一个Query，我们需要计算它对应token的新表征；内层for循环是为了计算每一个Query对应的新表征，需要让该Query与每一个Key进行计算。 所以外层是 for q in Queries，内层是 for k in Keys。Queries数量和Keys数量都是N，所以复杂度是 O(N^2) 。而Linear Transformer，它只有外层for q in Queries这个循环了。因为求和项的计算与i无关，所以所有的 Qi 可以共享求和项的值。换言之，求和项的值可以只计算一次，然后存在内存中供所有 Qi 去使用。所以Linear Transformer的计算复杂度是O(N) 。\nAttention Free Transformer \\[V_i\u0026#39;=\\sigma(Q_i)\\odot\\frac{\\sum_{j-1}^iexp(K_j+w_{i,j})\\odot V_j}{\\sum_{j=1}^iexp(K_j+w_{i,j})}\\] 其中σ是sigmoid函数\\(^{+};\\odot\\)是逐元素相乘 (element-wise product); wi,j是待训练的参数。 AFT采用的形式和上面的Linear Transformer不一样。首先是attention score, Linear Transformer仍然是同Transformer一样，为每一个Value赋予一个weight。而AFT会为每个 dimension\\(^{+}\\)赋予weight。换言之，在Linear Transformer中，同一个Value中不同dimension的weight是一致的；而AFT同一Value中不同dimension的weight不同(\\(w_{i,j}\\))。此外，attention score的计算也变得格外简单，用K去加一个可训练的bias\\(^{+}\\)。Q的用法很像一个gate。 可以很容易仿照公式(5)把AFT也写成递归形式，这样容易看出，AFT也可以像Linear Transformer,在inference阶段复用前面时刻的计算结果，表现如RNN形式，从而相比于 Transformer变得更加高效。\nRWKV RWKV的特点如下：\n改造AFT，通过Liner Transformer变换将self-attention复杂度由O(N^2)降为 O(N) 。 保留AFT简单的“attention”形式和Sequential Decoding，具有RNN表现形式。 Time-Mixing image.png def time_mixing(x, last_x, last_num, last_den, decay, bonus, mix_k, mix_v, mix_r, Wk, Wv, Wr, Wout): k = Wk @ ( x * mix_k + last_x * (1 - mix_k) ) v = Wv @ ( x * mix_v + last_x * (1 - mix_v) ) r = Wr @ ( x * mix_r + last_x * (1 - mix_r) ) wkv = (last_num + exp(bonus + k) * v) / \\ (last_den + exp(bonus + k)) rwkv = sigmoid(r) * wkv num = exp(-exp(decay)) * last_num + exp(k) * v den = exp(-exp(decay)) * last_den + exp(k) return Wout @ rwkv, (x,num,den) Channel-Mixing image.png def channel_mixing(x, last_x, mix_k, mix_r, Wk, Wr, Wv): k = Wk @ ( x * mix_k + last_x * (1 - mix_k) ) r = Wr @ ( x * mix_r + last_x * (1 - mix_r) ) vk = Wv @ np.maximum(k, 0)**2 return sigmoid(r) * vk, x 参考 How the RWKV language model works | The Good Minima (johanwind.github.io)\n","date":"2024-09-04T00:00:00Z","permalink":"/rwkv/","title":"rwkv"},{"content":"证明 核心思想就是找到一个转换，可以通过点积操作将位置信息注入，即： \\[\u0026lt;f_q\\left(x_m,m\\right),f_k\\left(x_n,n\\right)\u0026gt;=g\\left(x_m,x_n,m-n\\right)\\] 而通过复数的一些性质，找到了满足上述操作的转换：\n\\[\\begin{aligned} \u0026amp;f_{q}\\left(\\boldsymbol{x}_{m},m\\right)=\\left(\\boldsymbol{W}_{q}\\boldsymbol{x}_{m}\\right)e^{im\\theta} \\\\ \u0026amp;f_{k}\\left(\\boldsymbol{x}_{n},n\\right)=\\left(\\boldsymbol{W}_{k}\\boldsymbol{x}_{n}\\right)e^{in\\theta} \\\\ \u0026amp;g\\left(\\boldsymbol{x}_{m},\\boldsymbol{x}_{n},m-n\\right)=\\mathrm{Re}\\left[\\left(\\boldsymbol{W}_{q}\\boldsymbol{x}_{m}\\right)\\left(\\boldsymbol{W}_{k}\\boldsymbol{x}_{n}\\right)^{*}e^{i(m-n)\\theta}\\right] \\end{aligned}\\] 可以发现g函数中存在相对位置信息。 欧拉公式：\\(e^{ix}=\\cos x+i\\sin x\\)\n\\[\\begin{aligned}\u0026amp;\\text{基于上面面1点结论,可知}\\\\\u0026amp;f_{q}\\left(x_{m},m\\right)=\\left(W_{q}x_{m}\\right)e^{im\\theta}=q_{m}e^{im\\theta}\\\\\u0026amp;\\text{然后将}q_{m\\text{表示成复数形式（torch.view\\_as\\_complex）,可得}}\\\\\u0026amp;q_{m}=\\left[q_{m}^{(1)},q_{m}^{(2)}\\right]=\\left[q_{m}^{(1)}+iq_{m}^{(2)}\\right]\\\\\u0026amp;\\text{从而有}\\\\\u0026amp;f_{q}\\left(x_{m},m\\right)=q_{m}e^{im\\theta}=\\left[q_{m}^{(1)}+iq_{m}^{(2)}\\right]e^{im\\theta}\\\\\u0026amp;\\text{基于欧拉公式,可知}f_{q}\\left(x_{m},m\\right)_{\\text{即是两个复数相乘}}\\\\\u0026amp;f_{q}\\left(x_{m},m\\right)=q_{m}e^{im\\theta}=\\left(q_{m}^{(1)}+iq_{m}^{(2)}\\right)*\\left(\\cos(m\\theta)+i\\sin(m\\theta)\\right)\\end{aligned}\\]\n根据复数的计算，可得：\n\\[\\begin{aligned}q_{m}e^{im\\theta}=\\left(q_{m}^{(1)}+iq_{m}^{(2)}\\right)*(\\cos(m\\theta)+i\\sin(m\\theta))\\\\=\\left(q_{m}^{(1)}\\cos(m\\theta) -q_{m}^{(2)}\\sin(m\\theta)\\right)+i\\left(q_{m}^{(2)}\\cos(m\\theta)+q_{m}^{(1)}\\sin(m\\theta)\\right)\\end{aligned}\\]\n再将结果写成向量的形式，即：\n\\[q_{m}e^{im\\theta}=\\left[q_{m}^{(1)}\\cos(m\\theta)-q_{m}^{(2)}\\sin(m\\theta),q_{m}^{(2)}\\cos(m\\theta)+q_{m}^{(1)}\\sin(m\\theta)\\right]\\]\n即是query向量乘了一个旋转矩阵：\n\\[\\begin{gathered} f_{q}\\left(x_{m},m\\right)=\\left(W_{q}x_{m}\\right)e^{im\\theta}=q_{m}e^{im\\theta} \\\\ =\\left|q_{m}^{(1)}\\cos(m\\theta)-q_{m}^{(2)}\\sin(m\\theta),q_{m}^{(2)}\\cos(m\\theta)+q_{m}^{(1)}\\sin(m\\theta)\\right| \\\\ =\\left(\\begin{array}{cc}{\\cos(m\\theta)}\u0026amp;{-\\sin(m\\theta)}\\\\{\\sin(m\\theta)}\u0026amp;{\\cos(m\\theta)}\\end{array}\\right)\\left(\\begin{array}{c}{q_{m}^{(1)}}\\\\{q_{m}^{(2)}}\\end{array}\\right) \\end{gathered}\\]\n后续的证明看一文通透位置编码：从标准位置编码、旋转位置编码RoPE到ALiBi、LLaMA 2 Long(含NTK-aware简介)-CSDN博客\n将二维推广，有：\n\\[\\boldsymbol{R}_{\\Theta,m}^{d}=\\underbrace{\\left(\\begin{array}{ccccccc}{\\cos m\\theta_{0}}\u0026amp;{-\\sin m\\theta_{0}}\u0026amp;{0}\u0026amp;{0}\u0026amp;{\\cdots}\u0026amp;{0}\u0026amp;{0}\\\\{\\sin m\\theta_{0}}\u0026amp;{\\cos m\\theta_{0}}\u0026amp;{0}\u0026amp;{0}\u0026amp;{\\cdots}\u0026amp;{0}\u0026amp;{0}\\\\{0}\u0026amp;{0}\u0026amp;{\\cos m\\theta_{1}}\u0026amp;{-\\sin m\\theta_{1}}\u0026amp;{\\cdots}\u0026amp;{0}\u0026amp;{0}\\\\{0}\u0026amp;{0}\u0026amp;{\\sin m\\theta_{1}}\u0026amp;{\\cos m\\theta_{1}}\u0026amp;{\\cdots}\u0026amp;{0}\u0026amp;{0}\\\\{\\vdots}\u0026amp;{\\vdots}\u0026amp;{\\vdots}\u0026amp;{\\vdots}\u0026amp;{\\ddots}\u0026amp;{\\vdots}\u0026amp;{\\vdots}\\\\{0}\u0026amp;{0}\u0026amp;{0}\u0026amp;{0}\u0026amp;{\\cdots}\u0026amp;{\\cos m\\theta_{d/2-1}}\u0026amp;{-\\sin m\\theta_{d/2-1}}\\\\{0}\u0026amp;{0}\u0026amp;{0}\u0026amp;{0}\u0026amp;{0}\u0026amp;{\\cdots}\u0026amp;{\\sin m\\theta_{d/2-1}}\u0026amp;{\\cos m\\theta_{d/2-1}}\\end{array}\\right)}\\]\n则计算旋转编码，即有：\n\\[\\begin{bmatrix}\\cos m\\theta_0\u0026amp;-\\sin m\\theta_0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\\\sin m\\theta_0\u0026amp;\\cos m\\theta_0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\cos m\\theta_1\u0026amp;-\\sin m\\theta_1\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\sin m\\theta_1\u0026amp;\\cos m\\theta_1\u0026amp;\\cdots\u0026amp;0\u0026amp;0\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\u0026amp;\\vdots\\\\0\u0026amp;0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;\\cos m\\theta_{d/2-1}\u0026amp;-\\sin m\\theta_{d/2-1}\\\\0\u0026amp;0\u0026amp;0\u0026amp;0\u0026amp;\\cdots\u0026amp;\\sin m\\theta_{d/2-1}\u0026amp;\\cos m\\theta_{d/2-1}\\end{bmatrix}\\begin{bmatrix}q_0\\\\q_1\\\\q_2\\\\q_3\\\\\\vdots\\\\q_{d-2}\\\\q_{d-1}\\end{bmatrix}\\]\n由于矩阵太稀疏，会造成浪费，因此计算时是这么做的： \\[\\begin{bmatrix}q_0\\\\q_1\\\\q_2\\\\q_3\\\\\\vdots\\\\q_{d-2}\\\\q_{d-1}\\end{bmatrix}\\otimes\\begin{bmatrix}\\cos m\\theta_0\\\\\\cos m\\theta_0\\\\\\cos m\\theta_1\\\\\\cos m\\theta_1\\\\\\vdots\\\\\\cos m\\theta_{d/2-1}\\\\\\cos m\\theta_{d/2-1}\\end{bmatrix}+\\begin{bmatrix}-q_1\\\\q_0\\\\-q_3\\\\q_2\\\\\\vdots\\\\-q_{d-1}\\\\q_{d-2}\\end{bmatrix}\\otimes\\begin{bmatrix}\\sin m\\theta_0\\\\\\sin m\\theta_0\\\\\\sin m\\theta_1\\\\\\sin m\\theta_1\\\\\\vdots\\\\\\sin m\\theta_{d/2-1}\\\\\\sin m\\theta_{d/2-1}\\end{bmatrix}\\] 此外，角度的计算方式如下：\n\\[\\theta_j=10000^{-2j/d},j\\in[1,2,\\dots,d/2]\\]\n代码 llama实现 llama实现比较简单，但是一开始很不容易理解，实现如下：\ndef precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0): \u0026quot;\u0026quot;\u0026quot; Precompute the frequency tensor for complex exponentials (cis) with given dimensions. This function calculates a frequency tensor with complex exponentials using the given dimension \u0026#39;dim\u0026#39; and the end index \u0026#39;end\u0026#39;. The \u0026#39;theta\u0026#39; parameter scales the frequencies. The returned tensor contains complex values in complex64 data type. Args: dim (int): Dimension of the frequency tensor. end (int): End index for precomputing frequencies. theta (float, optional): Scaling factor for frequency computation. Defaults to 10000.0. Returns: torch.Tensor: Precomputed frequency tensor with complex exponentials. \u0026quot;\u0026quot;\u0026quot; # dim = 128 # end = 4096 # torch.arange(0, dim, 2) [0, 2, 4, 6, 8, 10,..., 124, 126] 共64个 # torch.arange(0, dim, 2)[: (dim // 2)] 保证是64个 freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)) # rope中的角度 # freqs = [1/10000.0^(0/128), 1/10000.0^(2/128), 1/10000.0^(4/128), ..., 1/10000.0^(126/128)] t = torch.arange(end, device=freqs.device) # postition idx # t = [0, 1, 2, ..., 4095] freqs = torch.outer(t, freqs).float() # type: ignore # freqs 得到 freqs和t的笛卡尔积，维度为（4096，64） # freqs = [[0, 0, 0,..., 0], # [1/10000.0^(0/128), 1/10000.0^(2/128), 1/10000.0^(4/128), ..., 1/10000.0^(126/128)], # [2/10000.0^(0/128), 2/10000.0^(2/128), 2/10000.0^(4/128), ..., 2/10000.0^(126/128)], # ..., # [4095/10000.0^(0/128), 4095/10000.0^(2/128), 4095/10000.0^(4/128), ..., 4095/10000.0^(126/128)]] freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # complex64 # freqs_cis的维度为(4096,64)，相当于半径为1，角度为freqs的极坐标的复数表示，如公式6所示。 return freqs_cis def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor): # 将除了position和dim其他的维度变为1 \u0026quot;\u0026quot;\u0026quot; Reshape frequency tensor for broadcasting it with another tensor. This function reshapes the frequency tensor to have the same shape as the target tensor \u0026#39;x\u0026#39; for the purpose of broadcasting the frequency tensor during element-wise operations. Args: freqs_cis (torch.Tensor): Frequency tensor to be reshaped. x (torch.Tensor): Target tensor for broadcasting compatibility. Returns: torch.Tensor: Reshaped frequency tensor. Raises: AssertionError: If the frequency tensor doesn\u0026#39;t match the expected shape. AssertionError: If the target tensor \u0026#39;x\u0026#39; doesn\u0026#39;t have the expected number of dimensions. \u0026quot;\u0026quot;\u0026quot; # freqs_cis.shape = [1024, 64] # x.shape = [2, 1024, 32, 64] ndim = x.ndim assert 0 \u0026lt;= 1 \u0026lt; ndim assert freqs_cis.shape == (x.shape[1], x.shape[-1]) # 将freqs_cis.shape变为[1, 1024, 1, 64] shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)] return freqs_cis.view(*shape) def apply_rotary_emb( xq: torch.Tensor, xk: torch.Tensor, freqs_cis: torch.Tensor, ): \u0026quot;\u0026quot;\u0026quot; Apply rotary embeddings to input tensors using the given frequency tensor. This function applies rotary embeddings to the given query \u0026#39;xq\u0026#39; and key \u0026#39;xk\u0026#39; tensors using the provided frequency tensor \u0026#39;freqs_cis\u0026#39;. The input tensors are reshaped as complex numbers, and the frequency tensor is reshaped for broadcasting compatibility. The resulting tensors contain rotary embeddings and are returned as real tensors. Args: xq (torch.Tensor): Query tensor to apply rotary embeddings. xk (torch.Tensor): Key tensor to apply rotary embeddings. freqs_cis (torch.Tensor): Precomputed frequency tensor for complex exponentials. Returns: Tuple[torch.Tensor, torch.Tensor]: Tuple of modified query tensor and key tensor with rotary embeddings. \u0026quot;\u0026quot;\u0026quot; # 将xq和xk的最后一个维度进行复数运算，得到新的xq和xk # 为了进行复数运算，需要将xq和xk的最后一个维度展开为2维 # 例如，xq的形状为[2, seq_len, 32, 128], reshape后为[2, seq_len, 32 , 64, 2] # view_as_complex函数可以将张量中的最后一维的两个元素作为实部和虚部合成一个复数xq的形状变为[2, seq_len, 32, 64] xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2)) xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2)) # 将freqs_cis广播到xq和xk的最后一个维度 freqs_cis = reshape_for_broadcast(freqs_cis, xq_) # freqs_cis.shape = [1, 1024, 1, 64] # view_as_real和view_as_complex相反，可以将张量中最后一维的复数拆出实部和虚部 # (xq_ * freqs_cis).shape = [2, seq_len, 32 , 64] # torch.view_as_real(xq_ * freqs_cis).shape = [2, seq_len, 32 , 64, 2] # flatten(3)将张量展平为[2, seq_len, 32 , 128]，3代表从的第3个维度开始展平 xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3) xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3) return xq_out.type_as(xq), xk_out.type_as(xk) 代码的实现是根据下面这个公式来的：\n\\[\\begin{aligned}q_{m}e^{im\\theta}=\\left(q_{m}^{(1)}+iq_{m}^{(2)}\\right)*(\\cos(m\\theta)+i\\sin(m\\theta))\\\\=\\left(q_{m}^{(1)}\\cos(m\\theta) -q_{m}^{(2)}\\sin(m\\theta)\\right)+i\\left(q_{m}^{(2)}\\cos(m\\theta)+q_{m}^{(1)}\\sin(m\\theta)\\right)\\end{aligned}\\] 因为对于query的dim维度，两两组合再变成复数形式，并和对应的旋转矩阵相乘，然后再转换成向量形式。这样就完成了转换。\nimage.png 另一种实现 另一种实现(transformers)利用了下面这个式子：\n\\[ \\begin{bmatrix}q_0\\\\q_1\\\\q_2\\\\q_3\\\\\\vdots\\\\q_{d-2}\\\\q_{d-1}\\end{bmatrix}\\otimes\\begin{bmatrix}\\cos m\\theta_0\\\\\\cos m\\theta_0\\\\\\cos m\\theta_1\\\\\\cos m\\theta_1\\\\\\vdots\\\\\\cos m\\theta_{d/2-1}\\\\\\cos m\\theta_{d/2-1}\\end{bmatrix}+\\begin{bmatrix}-q_1\\\\q_0\\\\-q_3\\\\q_2\\\\\\vdots\\\\-q_{d-1}\\\\q_{d-2}\\end{bmatrix}\\otimes\\begin{bmatrix}\\sin m\\theta_0\\\\\\sin m\\theta_0\\\\\\sin m\\theta_1\\\\\\sin m\\theta_1\\\\\\vdots\\\\\\sin m\\theta_{d/2-1}\\\\\\sin m\\theta_{d/2-1}\\end{bmatrix} \\]\nclass LlamaRotaryEmbedding(torch.nn.Module): def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None): super().__init__() inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float().to(device) / dim)) self.register_buffer(\u0026quot;inv_freq\u0026quot;, inv_freq) # Build here to make `torch.jit.trace` work. self.max_seq_len_cached = max_position_embeddings t = torch.arange(self.max_seq_len_cached, device=self.inv_freq.device, dtype=self.inv_freq.dtype) freqs = torch.einsum(\u0026quot;i,j-\u0026gt;ij\u0026quot;, t, self.inv_freq) # Different from paper, but it uses a different permutation # in order to obtain the same calculation emb = torch.cat((freqs, freqs), dim=-1) dtype = torch.get_default_dtype() self.register_buffer(\u0026quot;cos_cached\u0026quot;, emb.cos()[None, None, :, :].to(dtype), persistent=False) self.register_buffer(\u0026quot;sin_cached\u0026quot;, emb.sin()[None, None, :, :].to(dtype), persistent=False) def forward(self, x, seq_len=None): # x: [bs, num_attention_heads, seq_len, head_size] # This `if` block is unlikely to be run after we build sin/cos in `__init__`. # Keep the logic here just in case. if seq_len \u0026gt; self.max_seq_len_cached: self.max_seq_len_cached = seq_len t = torch.arange(self.max_seq_len_cached, device=x.device, dtype=self.inv_freq.dtype) freqs = torch.einsum(\u0026quot;i,j-\u0026gt;ij\u0026quot;, t, self.inv_freq) # Different from paper, but it uses a different permutation # in order to obtain the same calculation emb = torch.cat((freqs, freqs), dim=-1).to(x.device) self.register_buffer(\u0026quot;cos_cached\u0026quot;, emb.cos()[None, None, :, :].to(x.dtype), persistent=False) self.register_buffer(\u0026quot;sin_cached\u0026quot;, emb.sin()[None, None, :, :].to(x.dtype), persistent=False) return ( self.cos_cached[:, :, :seq_len, ...].to(dtype=x.dtype), self.sin_cached[:, :, :seq_len, ...].to(dtype=x.dtype), ) def rotate_half(x): \u0026quot;\u0026quot;\u0026quot;Rotates half the hidden dims of the input.\u0026quot;\u0026quot;\u0026quot; x1 = x[..., : x.shape[-1] // 2] x2 = x[..., x.shape[-1] // 2 :] return torch.cat((-x2, x1), dim=-1) def apply_rotary_pos_emb(q, k, cos, sin, position_ids): # The first two dimensions of cos and sin are always 1, so we can `squeeze` them. cos = cos.squeeze(1).squeeze(0) # [seq_len, dim] sin = sin.squeeze(1).squeeze(0) # [seq_len, dim] cos = cos[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim] sin = sin[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim] q_embed = (q * cos) + (rotate_half(q) * sin) k_embed = (k * cos) + (rotate_half(k) * sin) return q_embed, k_embed 相对于llama的版本比较容易理解。\nLong-term decay of RoPE 公式不看了，结论就是RoPE有长距离衰减的特性，相对距离越远的token之间的关注度也会降低，表现为attention score减小，这是个很好的特性。”This property coincides with the intuition that a pair of tokens with a long relative distance should have less connection.“ # 参考 一文通透位置编码：从标准位置编码、旋转位置编码RoPE到ALiBi、LLaMA 2 Long(含NTK-aware简介)-CSDN博客\nLLM—llama2结构和源码解读 - 知乎 (zhihu.com)\n","date":"2024-08-31T00:00:00Z","permalink":"/rope/","title":"rope"},{"content":"好的，非常荣幸能以专家的身份，与您一同深入探讨这篇在长上下文（Long Context）领域具有重要影响力的论文——《Data Engineering for Scaling Language Models to 128K Context》。\n这篇文章的核心价值在于，它为如何经济高效地将现有大语言模型（LLMs）的上下文窗口扩展到128K甚至更长，提供了一套清晰、可复现且极为有效的数据工程（Data Engineering）实践方案。在业界普遍认为扩展上下文窗口需要巨大的算力投入和复杂的模型结构改造时，这篇论文如同一股清流，指出问题的关键或许不在于模型架构的“大手术”，而在于如何通过“精细喂养”——即高质量的数据策略——来“解锁”模型早已在预训练阶段就潜移默化中习得的长距离信息处理能力。\n接下来，我将首先为您全面深入地解读这篇论文的精髓，然后按照您提出的六个问题，逐一进行剖析。\n论文深度解读 在长上下文技术成为大模型核心竞争力的今天，无论是处理长篇文档、进行多文档问答，还是实现代码仓库级别的理解，拥有一个超长且有效的上下文窗口都至关重要。然而，在很长一段时间里，开源社区的模型尽管在理论上支持数万甚至十万以上的上下文长度（例如通过修改RoPE位置编码的base），但在实际应用中，尤其是在著名的“大海捞针”（Needle-in-a-Haystack, NIAH）测试中表现不佳。这个测试通过将一个特定的信息（“针”）隐藏在一段冗长的文本（“草堆”）中的不同位置，来检验模型是否真的能“看到”并利用整个上下文的信息。许多模型在“草堆”的中间部分会出现信息提取失败的情况，形成一个“U”形性能曲线，即只对开头和结尾的信息敏感。\n本文作者正是针对这一痛点展开研究。他们提出了一个颠覆性的假设： \u0026gt; We hypothesize that the capability to utilize information at arbitrary locations within long context length is (mostly) already acquired during pretraining, even for models pretrained on substantially shorter 4K contexts. … a small amount of long-context data, in our case, 1-5B tokens, can “unlock” a 7B model’s capability… \u0026gt; \u0026gt; 我们假设，在任意位置利用长上下文信息的能力，（大部分）是在大规模预训练阶段就已经获得的，即使模型是在短得多的4K上下文上预训练的。… 少量长上下文数据，在我们的案例中是10亿到50亿个token，就足以“解锁”一个7B模型的这种能力…\n基于这个假设，他们将研究重心从复杂的模型算法创新彻底转向了数据工程。他们没有设计新的注意力机制或模型结构，而是沿用了标准的Transformer架构，并使用了FlashAttention-2等现有技术来优化效率，其核心贡献是一套被称为“按源长度上采样”（Per-source Length Upsampling）的数据策略。\n这个策略的精妙之处在于它解决了数据工程中的一个核心矛盾：为了让模型学习长依赖，我们需要喂给它更多长文档；但长文档（如书籍、代码）在通用语料库中占比较低，如果直接大量增加长文档的比例，就会破坏预训练时的数据领域分布，可能导致模型在网页、对话等短文本领域的性能下降，即所谓的“知识遗忘”或“领域偏移”。\n该论文提出的解决方案如下： 1. 维持领域平衡：他们使用了SlimPajama数据集，这是一个LLaMA预训练数据的开源复现版本。他们严格维持SlimPajama中各个领域的原始比例，例如CommonCrawl占67%，Github占4.5%，Books占4.5%等。 2. 域内长度上采样：在保持上述领域比例不变的前提下，他们在每个领域内部，提高了长文档被采样的概率。例如，在处理Books领域的数据时，他们会更倾向于选择那些完整的、长篇的章节，而不是短小的片段。\n通过这种方式，模型既接触到了足够多的长文本样本来学习长距离依赖，又不会因为“偏食”某一类数据而损害其通用能力。\n为了验证这一方法的有效性，论文进行了一系列详尽的实验。最引人注目的结果体现在大海捞针测试中（见下图）。他们基于LLaMA-2 7B模型，使用其数据配方持续预训练了50亿（5B）个token后，模型在长达128K的上下文长度测试中，信息检索的准确率达到了惊人的88.0%。这一结果不仅远超当时其他的开源长上下文模型（如YaRN Mistral的57.4%），甚至在特定测试配置下略高于被视为业界标杆的GPT-4 128K（87.1%）。\n图1：论文核心成果展示。绿色代表模型成功在“草堆”中找到了“针”，红色代表失败。可见，“Ours LLaMA 7B”模型在100K长度内基本实现了全绿色覆盖，表现与GPT-4 128K相当，远优于其他开源模型。\n更重要的是，他们证明了这种性能提升的“性价比”极高。整个持续预训练过程仅需要10亿到50亿（1B-5B）个token的数据量，这与从头预训练动辄数万亿（trillion）token相比，成本降低了几个数量级。如论文Table 2所示，在8块A100（80G）GPU上，训练一个7B模型仅需约5天时间，这使得顶尖的长上下文模型技术不再是少数巨头的专利，普通的学术实验室和中小型企业也完全有能力负担。\n同时，论文还通过消融实验（Ablation Study）详细对比了不同数据策略的优劣（见下表）。实验清晰地表明，那些破坏了领域平衡的策略，例如只用书籍数据进行上采样（Upsample Book），虽然在书籍相关的长文本任务上表现优异，但在代码等其他领域的性能上却造成了损害。这强有力地证明了“按源长度上采样”策略的均衡性和优越性。\n表5：不同数据工程方法对模型在各领域损失（loss）的影响。红色表示性能下降，绿色表示提升。可见，“Per-source”策略在提升长文本性能（4K-128K Context Length部分多为绿色）的同时，对短文本性能（0-4K部分）的负面影响最小，是最均衡的方案。\n综上所述，这篇论文以严谨的实验和清晰的逻辑，为大模型社区贡献了一个极其宝贵的“食谱”：它告诉我们，通往高性能长上下文模型的道路，或许就隐藏在对数据的深刻理解和精巧处理之中。\n一、论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义? 研究目标：论文的核心研究目标是，探索并验证一套经济高效（affordable）且效果卓越（effective）的数据工程方法，用于将现有的大语言模型（如LLaMA-2）的上下文处理能力从标准的4K扩展到128K。\n解决的实际问题：\n开源模型长上下文能力不足：在当时，许多开源模型虽然声称支持长上下文，但在实际应用（如NIAH测试）中表现不佳，无法在整个上下文窗口内可靠地检索信息，存在明显的性能短板。 扩展上下文的成本高昂：传统观念认为，要让模型具备强大的长上下文能力，要么需要从头开始用海量长文本数据进行预训练，要么需要进行大规模的持续预训练（如之前的工作动辄使用400B token），这两种方式都耗资巨大，非普通机构所能承受。 对行业发展的重要意义：\n技术的民主化：该论文提出的方法大幅降低了构建高性能长上下文模型的门槛。它证明了只需“学术级别”的资源（几天到几周的GPU时间），就能将一个普通的开源模型提升到接近甚至超越“前沿模型”（frontier models）的水平。这极大地推动了长上下文技术的普及，使得中小型企业、创业公司和学术研究者都能参与到这一领域的创新中。 催生新的应用场景：一个可靠的128K上下文窗口，意味着模型可以一次性处理整本书、一个完整的代码仓库、一份长篇的法律或财务报告。这直接赋能了如代码智能体（Repo-level Code Agents）、多文档复杂问答系统、法律/金融文档分析等过去难以实现的复杂应用，为行业开辟了全新的想象空间。 指明了新的研究方向：它将社区的注意力从“卷”模型架构和算法，部分转移到了数据本身。这启发了更多关于数据质量、数据配比、数据工程对模型能力影响的深入研究，强调了“数据为王”在LLM时代依然是黄金法则。 二、论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 新的思路： “解锁”而非“注入”：这是本文最核心的思路转变。之前的方法（如LLaMA Long、XVERSE）倾向于认为长上下文是一种需要通过大规模训练“注入”到模型中的新能力。而本文认为这种能力在标准预训练后已经“潜伏”在模型内部，只需要通过小规模、有针对性的持续预训练来“解锁”。这一思路直接导致了方法论上的巨大差异，即从“重投入”转向“轻量化”。 新的方法： 轻量化持续预训练（Lightweight Continual Pretraining）：在仅使用1B到5B token的数据量上进行训练，相比之前工作使用的400B token，数据量减少了近两个数量级。 按源长度上采样（Per-source Length Upsampling）：这是其数据工程配方的核心技术。具体操作细节如下： 基准数据集：采用SlimPajama，这是一个复刻LLaMA预训练数据的公开语料库，包含了网页、代码、书籍、论文等多个领域。 维持领域分布：在采样时，严格保持SlimPajama中各领域的原始比例（如67% CommonCrawl, 15% C4, 4.5% Github, 4.5% Books等）。 域内上采样：在每个领域内部，提高长文档被选中的概率。论文中提到，他们将长度超过4K的序列占比从约30%提升到了约70%。 特点和优势： 性能卓越：如Table 3所示，其7B模型在NIAH测试上达到88.0%的准确率，13B模型达到90.0%，效果媲美甚至超越了GPT-4。 成本极低：训练成本大幅降低，使得技术普及成为可能。 保持通用性：与那些“偏科”的方法形成鲜明对比，该方法在提升长上下文能力的同时，几乎没有损害模型在短上下文任务上的通用能力。Table 3中的MMLU分数（一个衡量模型通用知识和推理能力的主流基准）可以证明这一点。例如，Ours LLaMA-2 7B的MMLU分数为43.3，与基础模型（Together LLaMA-2 7B的44.8）相比，仅有微小下降，远优于LongLoRA 7B的37.9。 方法论清晰：Table 1清晰地对比了其方案与之前工作的不同。例如，YaRN Mistral仅使用PG19书籍数据，破坏了领域平衡；LongLoRA虽然使用了Redpajama（SlimPajama的前身），但没有进行长度上采样。这些细节的对比，凸显了本文方法的严谨性和创新性。 As we list in Table 1, many of the data details are crucial for strong long-context performance, yet may be easily overlooked.\n正如我们在表1中列出的，许多数据细节对于强大的长上下文性能至关重要，但却很容易被忽视。\n三、论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 实验设计： 基础模型：选择了开源社区广泛使用的LLaMA-2 7B和LLaMA-2 13B。 训练方案：使用“按源长度上采样”策略准备数据，在80K（7B模型）或64K（13B模型）的训练长度上，对模型进行5B token的持续预训练。 核心评估基准： 大海捞针（Needle-in-a-Haystack, NIAH）：用于测试模型在128K上下文长度内任意位置的精确信息检索能力。 BookQA：一个真实世界的长文本问答基准，要求模型读完一本书后回答关于情节的问题，用于检验模型的实际应用能力。 MMLU：用于评估模型的通用知识水平，确保长上下文训练没有破坏模型的“常识”。 对比基线（Baselines）：选择了当时最强的开源长上下文模型（如YaRN Mistral 7B 128K, LongLoRA 100K）和闭源标杆模型（GPT-4-Turbo 128K）。 实验数据和结果： 大海捞针测试结果：这是最亮眼的结果。 Ours LLaMA-2 7B：NIAH准确率88.0% Ours LLaMA-2 13B：NIAH准确率90.0% 与之对比，YaRN Mistral 7B为57.4%，LongLoRA 7B为70.0%，而GPT-4-Turbo为87.1%。（数据来源：Table 3） 数据量影响实验：Figure 3展示了训练数据量对模型性能的影响。 从100M tokens到500M tokens，模型性能（NIAH准确率）显著提升（从37.8%到81.1%）。 在5B tokens时，性能达到饱和点（88.0%）。 继续增加到10B tokens，性能反而略有下降（84.0%），出现了过拟合迹象。这证明了5B tokens是一个“甜点位”，既高效又有效。 下游任务性能：Table 4展示了在BookQA任务上的表现。 Ours LLaMA-2 13B 的分数为31.1，显著高于YaRN Mistral的26.3和LongLoRA 13B的24.6，并且正在缩小与GPT-4（37.4）的差距。 通用能力保持：Table 3的MMLU分数显示，本文方法训练出的模型通用能力保持得很好，没有出现严重衰退。 四、结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 值得探索的问题和挑战： 长上下文指令微调（Instruction-Finetuning）：本文只解决了持续预训练的问题，但如何对这些长上下文基础模型进行高效的指令微调，使其在具体的长文本任务（如合同审查、代码生成）上表现更好，是一个巨大的开放性问题。微调数据格式、任务设计都亟待探索。 长距离推理与整合：NIAH主要测试“检索”能力，但真正的智能体需要对长达128K的信息进行推理、总结和整合。如何设计新的评估基准和训练方法来提升模型的长距离推理能力是下一个核心挑战。 多模态长上下文：本文的方法论是否可以迁移到视频、音频等多模态领域？如何为长视频或超长音频设计类似的“按源长度上采样”策略是一个全新的课题。 更高效的架构结合：将本文的数据工程方法与更先进的模型架构（如State Space Models like Mamba, or linear attention variants）相结合，是否能以更低的成本实现更长的上下文，甚至突破百万级上下文窗口。 数据质量与合成：除了上采样，如何进一步提升长文本数据的质量？是否可以利用强大的模型来合成高质量、多样化的长上下文训练数据，以进一步“解锁”模型潜力？ 新的技术和投资机会： 垂直领域长上下文模型服务：利用该论文的技术，为金融、法律、医疗、科研等拥有海量长文本数据的行业，提供定制化的长上下文模型，会是巨大的商业机会。 数据工程即服务（DEaaS）平台：开发自动化平台，帮助企业和开发者轻松实现本文提出的数据清洗、配比、上采样和持续预训练流程，降低技术门槛。 下一代开发工具与智能体：基于强大的长上下文模型，开发能够理解整个代码库的AI编程助手、能够阅读所有背景材料的AI研究助理、以及能够处理复杂案件卷宗的AI法律顾问等。 对数据提供商的投资：高质量、经过精细标注和分类的语料库将变得前所未有的重要，拥有优质数据源的公司其价值将进一步凸显。 五、退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 存在的不足及缺失： 评估维度相对单一：论文的成功高度依赖于NIAH这个检索任务。虽然这是一个很好的压力测试，但它并不能完全代表长上下文的全部能力。模型可能只是擅长“精确查找并复述”，而对于需要整合全文信息进行复杂推理的任务，其表现仍是未知的。 模型规模的局限性：实验主要在7B和13B规模的模型上进行。这套数据工程方法能否同等有效地应用于70B、数百B甚至MoE（Mixture of Experts）等更大规模的模型上，还需要进一步验证。更大模型的学习动态和数据需求可能存在差异。 缺乏指令微调的讨论：论文的工作止步于基础模型的持续预训练。一个真正可用的产品级模型还需要经过指令微调。微调过程本身可能会对模型在长上下文任务上的表现产生正面或负面的影响，这部分内容是缺失的。 对RoPE参数的依赖：论文中提到他们调整了RoPE的base，这是一个标准操作。但他们没有深入探讨不同的RoPE调整策略与数据工程策略之间的交互影响。 需要进一步验证和存疑的： “解锁”假说的普适性：本文的“解锁”假说是基于LLaMA-2系列模型提出的。这个假说是否对所有主流的Transformer架构（如GPT系列、Mistral系列等）都适用？不同预训练数据和方法训练出的模型，“潜伏”的能力是否相同？ 真实世界任务的泛化能力：尽管BookQA的结果令人鼓舞，但在更多样化、更开放的真实世界长文本任务上，模型的表现如何仍需大量验证。例如，在充满噪声和非结构化数据的长篇报告中，其鲁棒性如何？ 成本计算的全面性：论文强调了训练时间的经济性，但没有详细讨论数据处理本身的成本。对于一个没有现成SlimPajama副本的团队来说，获取、清洗、去重和处理TB级别的原始数据，也是一笔不小的开销。 六、我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 重点学习与启发： 思维范式转变：从“算法/模型为中心”到“数据为中心”：最大的启发是，在资源有限的情况下，与其追求复杂新颖的模型架构，不如回归到数据本身，通过精细的数据工程策略，往往能取得事半功倍的效果。这是一个非常实用的工程哲学。 “平衡”的艺术：核心的可借鉴想法是“按源长度上采样”所体现的平衡思想。在做任何数据增强或采样时，都要时刻警惕是否会破坏原有的领域平衡，导致模型“偏科”。这个原则可以推广到所有的数据工作中。 迭代验证和“小步快跑”：论文展示了从100M到10B token的详细性能变化，告诉我们不需要一步到位就投入海量资源。可以先用少量数据（如500M token）进行实验，快速验证想法，看到效果后再逐步加码。这是一种非常敏捷的研发策略。 需要补充的背景知识： Transformer与注意力机制：理解其基本工作原理，尤其是注意力机制的二次方复杂度问题，这样才能明白为什么长上下文在计算上是个挑战，以及FlashAttention这类技术的重要性。 位置编码（Positional Encodings）：特别是旋转位置编码（Rotary Position Embedding, RoPE）。你需要了解RoPE的基本原理，以及为什么通过调整其base参数可以外插到比训练时更长的上下文，这是几乎所有长上下文模型都在使用的基础技术。 大模型的训练阶段：清晰地区分预训练（Pre-training）、持续预训练（Continual Pre-training）和指令微调（Instruction Fine-tuning）。本文做的是持续预训练，理解这个定位至关重要。 主流评测基准：了解像MMLU和Needle-in-a-Haystack (NIAH)这类评测是做什么的，它们的测试逻辑和局限性。这将帮助你更批判性地看待论文的实验结果。 开源数据集：对The Pile, RedPajama, SlimPajama等主流的开源预训练数据集有一个大致的了解，知道它们的构成和特点。 ","date":"2024-08-08T00:00:00Z","permalink":"/data-engineering-for-scaling-language-models-to-128k-context/","title":"Data Engineering for Scaling Language Models to 128K Context"},{"content":"KV cache LLM推理过程分为Prefill和Decode两个阶段，其中Prefill阶段会对Prompt中所有的token做并行计算，得到Prompt中所有Tokens的KV Cache以及计算得到首Token。Prompt阶段Token计算得到的KV Cache会保存下来，留给Decode阶段复用，Decode阶段是一个自回归过程，每decode一个新的Token，都需要用到所有之前计算得到的KV Cache来计算当前query token的Attention。因此，当输出长度越来越大或者context很长时，KV Cache将会占用大量的显存。如何优化KV Cache的显存占用，一直都是LLM推理的核心主题之一。\n之前一直疑惑kv cache既然每次只输入生成token就可以，那么位置信息该怎么注入呢？翻了翻llama的源码，找到了答案：\ndef forward(self, tokens: torch.Tensor, start_pos: int): \u0026quot;\u0026quot;\u0026quot; Perform a forward pass through the Transformer model. Args: tokens (torch.Tensor): Input token indices. start_pos (int): Starting position for attention caching. Returns: torch.Tensor: Output logits after applying the Transformer model. \u0026quot;\u0026quot;\u0026quot; _bsz, seqlen = tokens.shape h = self.tok_embeddings(tokens) self.freqs_cis = self.freqs_cis.to(h.device) freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen] mask = None if seqlen \u0026gt; 1: mask = torch.full( (seqlen, seqlen), float(\u0026quot;-inf\u0026quot;), device=tokens.device ) mask = torch.triu(mask, diagonal=1) # When performing key-value caching, we compute the attention scores # only for the new sequence. Thus, the matrix of scores is of size # (seqlen, cache_len + seqlen), and the only masked entries are (i, j) for # j \u0026gt; cache_len + i, since row i corresponds to token cache_len + i. mask = torch.hstack([ torch.zeros((seqlen, start_pos), device=tokens.device), mask ]).type_as(h) for layer in self.layers: h = layer(h, start_pos, freqs_cis, mask) h = self.norm(h) output = self.output(h).float() return output 注意freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen]这一行，即是实现了rope相对位置编码的kv cache的核心。\nkv cache代码 def repeat_kv(x: torch.Tensor, n_rep: int) -\u0026gt; torch.Tensor: \u0026quot;\u0026quot;\u0026quot;torch.repeat_interleave(x, dim=2, repeats=n_rep)\u0026quot;\u0026quot;\u0026quot; bs, slen, n_kv_heads, head_dim = x.shape if n_rep == 1: return x return ( x[:, :, :, None, :] .expand(bs, slen, n_kv_heads, n_rep, head_dim) .reshape(bs, slen, n_kv_heads * n_rep, head_dim) ) class Attention(nn.Module): \u0026quot;\u0026quot;\u0026quot;Multi-head attention module.\u0026quot;\u0026quot;\u0026quot; def __init__(self, args: ModelArgs): \u0026quot;\u0026quot;\u0026quot; Initialize the Attention module. Args: args (ModelArgs): Model configuration parameters. Attributes: n_kv_heads (int): Number of key and value heads. n_local_heads (int): Number of local query heads. n_local_kv_heads (int): Number of local key and value heads. n_rep (int): Number of repetitions for local heads. head_dim (int): Dimension size of each attention head. wq (ColumnParallelLinear): Linear transformation for queries. wk (ColumnParallelLinear): Linear transformation for keys. wv (ColumnParallelLinear): Linear transformation for values. wo (RowParallelLinear): Linear transformation for output. cache_k (torch.Tensor): Cached keys for attention. cache_v (torch.Tensor): Cached values for attention. \u0026quot;\u0026quot;\u0026quot; super().__init__() self.n_kv_heads = args.n_heads if args.n_kv_heads is None else args.n_kv_heads model_parallel_size = fs_init.get_model_parallel_world_size() self.n_local_heads = args.n_heads // model_parallel_size self.n_local_kv_heads = self.n_kv_heads // model_parallel_size self.n_rep = self.n_local_heads // self.n_local_kv_heads self.head_dim = args.dim // args.n_heads self.wq = ColumnParallelLinear( args.dim, args.n_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wk = ColumnParallelLinear( args.dim, self.n_kv_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wv = ColumnParallelLinear( args.dim, self.n_kv_heads * self.head_dim, bias=False, gather_output=False, init_method=lambda x: x, ) self.wo = RowParallelLinear( args.n_heads * self.head_dim, args.dim, bias=False, input_is_parallel=True, init_method=lambda x: x, ) # kv_cache是缓存键值对，在训练过程中，我们只保存最近n个键值对 self.cache_k = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() self.cache_v = torch.zeros( ( args.max_batch_size, args.max_seq_len, self.n_local_kv_heads, self.head_dim, ) ).cuda() def forward( self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor], ): \u0026quot;\u0026quot;\u0026quot; Forward pass of the attention module. Args: x (torch.Tensor): Input tensor. start_pos (int): Starting position for caching. freqs_cis (torch.Tensor): Precomputed frequency tensor. mask (torch.Tensor, optional): Attention mask tensor. Returns: torch.Tensor: Output tensor after attention. \u0026quot;\u0026quot;\u0026quot; # 假设当前x为(1, 1, dim)，也就是上一个预测的token # self-attention的输入，标准的(bs, seqlen, hidden_dim) bsz, seqlen, _ = x.shape # 计算当前token的qkv # q k v分别进行映射，注意这里key, value也需要先由输入进行映射再和kv_cache里面的key, value进行拼接 xq, xk, xv = self.wq(x), self.wk(x), self.wv(x) xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim) xk = xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) xv = xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim) # 对当前输入的query和key进行RoPE，注意kv_cache里面的key已经做过了RoPE xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis) # 缓存当前token的kv self.cache_k = self.cache_k.to(xq) self.cache_v = self.cache_v.to(xq) self.cache_k[:bsz, start_pos: start_pos + seqlen] = xk self.cache_v[:bsz, start_pos: start_pos + seqlen] = xv # 取出前seqlen个token的kv缓存 # 取出全部缓存的key和value（包括之前在cache里面的和本次输入的），作为最终的key和value keys = self.cache_k[:bsz, : start_pos + seqlen] values = self.cache_v[:bsz, : start_pos + seqlen] # 将kv重复填充，使kv和q的头数个数相同 # repeat k/v heads if n_kv_heads \u0026lt; n_heads，对齐头的数量 keys = repeat_kv(keys, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) values = repeat_kv(values, self.n_rep) # (bs, cache_len + seqlen, n_local_heads, head_dim) # 计算当前token的attention score，，注意mask需要加上，另外维度要对应上 xq = xq.transpose(1, 2) # (bs, n_local_heads, seqlen, head_dim) keys = keys.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) values = values.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim) scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim) if mask is not None: scores = scores + mask # (bs, n_local_heads, seqlen, cache_len + seqlen) scores = F.softmax(scores.float(), dim=-1).type_as(xq) output = torch.matmul(scores, values) # (bs, n_local_heads, seqlen, head_dim) output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1) return self.wo(output) prefill和decode分离 在传统的 LLM 推理框架中，Prefill 和 Decode 阶段通常由同一块 GPU 执行。推理引擎的调度器会根据显存使用情况及请求队列状态，在 Prefill 和 Decode 之间切换，完成整个推理过程。\n而在 Prefill-Decode 分离式架构（以下简称 PD 分离式架构）中，这两个阶段被拆分到不同的 GPU 实例上独立运行。如下图所示，这是 DistServe 提供的一张架构图： 在大模型推理中，常用以下两项指标评估性能：\nTTFT（Time-To-First-Token）：首 token 的生成时间，主要衡量 Prefill 阶段性能。 TPOT（Time-Per-Output-Token）：生成每个 token 的时间，主要衡量 Decode 阶段性能。 当 Prefill 和 Decode 在同一块 GPU 上运行时，由于两阶段的计算特性差异（Prefill 是计算密集型，而 Decode 是存储密集型），资源争抢会导致 TTFT 和 TPOT 之间的权衡。例如：\n若优先处理 Prefill 阶段以降低 TTFT，Decode 阶段的性能（TPOT）可能下降。 若尽量提升 TPOT，则会增加 Prefill 请求的等待时间，导致 TTFT 上升。 PD 分离式架构的提出正是为了打破这一矛盾。通过将 Prefill 和 Decode 分离运行，可以针对不同阶段的特性独立优化资源分配，从而在降低首 token 延迟的同时提高整体吞吐量。\n在 PD 分离架构中，Prefill 和 Decode 阶段的资源需求不同，分别体现为：\nPrefill 阶段：计算密集型（compute-bound）。在流量较大或用户提示长度较长时，Prefill 的计算压力更大。完成 KV Cache 的生成后，Prefill 阶段本身无需继续保留这些缓存。 Decode 阶段：存储密集型（memory-bound）。由于逐 token 生成的特性，Decode 阶段需频繁访问 KV Cache，因此需要尽可能多地保留缓存数据以保障推理效率。 Batching 策略对两阶段的性能影响显著，但趋势相反：\nPrefill 阶段：吞吐量随 batch size 增加逐渐趋于平稳。这是因为 Prefill 的计算受限特性（compute-bound），当 batch 中的总 token 数超过某个阈值时，计算资源成为瓶颈。 Decode 阶段：吞吐量随 batch size 增加显著提升。由于 Decode 阶段的存储受限特性（memory-bound），增大 batch size 可提高计算效率，从而显著增加吞吐量。 Chunked prefills image.png 常见问题 128k token输入需要多少显存存kv cache? image.png 超参数如上，如果我们采用int8精度，也就是每个参数占据一个字节，每个token占据的kv cache大小就是\n2 * K * H * L = 2 (k 和 v) * 8 (n kv heads) * 128 (d_qkv) * 80 (n layers) * 1 (byte)= 160kB\n（这里的 2 是因为每个 token 需要存储k和v）\n那么128k的kv cache就是：\n162e3 * 128 * 1024 = 21.2GB 需要注意的是 llama 3 使用的是 gqa，所以 N 为 64，K 为 8.\n套公式就是 （使用 bf 16 即 2 bytes）：\n\\[ b(s+n)h*l*2*2 \\]\n其中 b 为 batch_size，s 为输入序列长度，n 为输出序列长度，h 为 k 或者 v 的总维度，不同的 attention 改变的是这个东西来降低显存。\n参考 LLM—llama2结构和源码解读 - 知乎 (zhihu.com) # LLM推理优化 - Chunked prefills # 图解大模型计算加速系列：分离式推理架构1，从DistServe谈起\n","date":"2024-08-07T00:00:00Z","permalink":"/kv-cache/","title":"KV cache"},{"content":"Low-Rank Adaption (LoRA)，即“低秩适配”，实现了预训练模型的参数高效微调，且不会增加模型的推理延迟。 ## 内在维度 2020年，A. Aghajanyan等人研究了这一现象，发现预训练模型存在一个较低的”内在维度”,使用少量样本微调时，实际上是在更新低维空间中的参数。把预训练模型的全部参数看成一个D维参数向量，记为\\(\\Theta^\\mathrm{(D)}\\),模型的原始参数为\\(\\Theta_0^\\mathrm{(D)}\\),设\\(\\Theta^{(d)}\\)是d维子空间中的一个向量，d\u0026lt;D,利用一个固定的D*d映射矩阵P 把d维空间中的向量映射到D维空间，\\(\\Theta^{(\\mathrm{D})}\\)可写为：\n\\[\\mathrm{\\theta^{(D)}=\\theta_0^{(D)}+P\\theta^{(d)}}\\]\n下图中，以D=3,d=2为例： 左图直接在3维空间中训练模型，直接优化原始模型参数\\(\\Theta_{0}^{(\\mathrm{D})}\\),把它更新为\\(\\Theta^{(\\mathrm{D})}\\)。右图冻结\\(\\Theta_{0}^{(\\mathrm{D})}\\),转而在2维空间中寻找一个\\(\\Theta^{(\\mathrm{d})}\\),再用矩阵P把\\(\\Theta^{(\\mathrm{d})}\\)映射到3维空间。如果用右图的方式可以把模型优化到良好的效果，例如，达到了全量参数微调效果的90%,则该模型的内在维度\\(\\mathrm{d}_{90}=2\\)。 实验表明，仅训练200个参数，就可以使RoBERTa-large在MRPC数据集上的效果达到全量参数微调效果的90%。 ## 低秩适配 预训练模型的权重矩阵通常具有满秩，这意味着权重矩阵的各个列向量之间线性无关，这样的矩阵没有冗余信息，是无法被压缩的。但是，“内在维度”现象表明，微调模型时只需更新少量参数，这启发我们微调时产生的权重增量矩阵\\(\\Delta\\)W可能包含大量冗余参数，\\(\\Delta\\)W很可能不是满秩的。对低秩矩阵做分解，可以利用较少的参数重建或近似原矩阵。这就是LoRA的核心思想。 设输入为x，微调时得到增量\\(\\Delta W\\),与原始权重\\(\\mathcal{W}_{0}\\)相加得到更新后的权重，输出h= ( \\(W_0\\)+ \\(\\Delta\\)W) x。根据矩阵的乘法分配律，有h= \\(W_0\\)x+ \\(\\Delta\\)Wx,这意味着微调时可以保持\\(W_0\\)不变，分别将\\(W_0\\)、\\(\\Delta\\)W与x相乘，最后把两个乘积相加即可得到输出h。 设\\(\\mathcal{W}_0\\in\\mathbb{R}^{\\mathrm{dxk}}\\),\\(\\Delta \\mathcal{W} _{\\mathrm{f} }\\)的秩为r。\\(\\Delta \\mathcal{W}= \\mathcal{B} \\mathcal{A}\\)是\\(\\Delta\\mathcal{W}\\)的一个满秩分解，其中 \\(\\mathcal{B} \\in \\mathbb{R} ^{\\mathrm{dxr}}, \\mathcal{A} \\in \\mathbb{R} ^{\\mathrm{rxk}}, \\mathcal{r} \\ll \\min ( \\mathcal{d} , \\mathcal{k} )\\)。训练时，分别用随机高斯和零矩阵初始化A和B，确保初始化时 BA是零矩阵，对模型效果没有影响。训练过程中冻结\\(\\mathcal{W}_0\\),只更新矩阵B和A，共r(d+k)个参数，从而实 现“参数高效”微调。推理时，分别计算\\(\\mathbf{W_{\\mathrm{n} }x}\\)和 BAx并相加，得到输出h，如下图所示： 实际上，r是一个超参，训练时可任意设定，\\(\\Delta\\)W真正的秩未必等于r。如果r恰好等于\\(\\Delta\\)W的秩，甚至大于\\(\\Delta\\)的秩(例如等于预训练权重矩阵\\(W_{0}\\)的秩),利用学到的B和A可以完全重建\\(\\Delta\\)W,这时，LoRA的效果近似于全量微调。如果r小于\\(\\Delta\\)W的秩，BA就是\\(\\Delta\\)W的一个低秩近似，利用矩阵B和A可以恢复矩阵\\(\\Delta W\\)中的部分信息。\n还有一个超参数为lora_alpha:\nimage.png 降低了哪部分显存需求 image.png image.png 代码 class LoraModel(torch.nn.Module): \u0026quot;\u0026quot;\u0026quot; Creates Low Rank Adapter (Lora) model from a pretrained transformers model. Args: model ([`transformers.PreTrainedModel`]): The model to be adapted. config ([`LoraConfig`]): The configuration of the Lora model. Returns: `torch.nn.Module`: The Lora model. Example:: \u0026gt;\u0026gt;\u0026gt; from transformers import AutoModelForSeq2SeqLM, LoraConfig \u0026gt;\u0026gt;\u0026gt; from peft import LoraModel, LoraConfig \u0026gt;\u0026gt;\u0026gt; config = LoraConfig( peft_type=\u0026quot;LORA\u0026quot;, task_type=\u0026quot;SEQ_2_SEQ_LM\u0026quot;, r=8, lora_alpha=32, target_modules=[\u0026quot;q\u0026quot;, \u0026quot;v\u0026quot;], lora_dropout=0.01, ) \u0026gt;\u0026gt;\u0026gt; model = AutoModelForSeq2SeqLM.from_pretrained(\u0026quot;t5-base\u0026quot;) \u0026gt;\u0026gt;\u0026gt; lora_model = LoraModel(config, model) **Attributes**: - **model** ([`transformers.PreTrainedModel`]) -- The model to be adapted. - **peft_config** ([`LoraConfig`]): The configuration of the Lora model. \u0026quot;\u0026quot;\u0026quot; def __init__(self, config, model): super().__init__() self.peft_config = config self.model = model self._find_and_replace() mark_only_lora_as_trainable(self.model, self.peft_config.bias) def _find_and_replace(self): kwargs = { \u0026quot;r\u0026quot;: self.peft_config.r, \u0026quot;lora_alpha\u0026quot;: self.peft_config.lora_alpha, \u0026quot;lora_dropout\u0026quot;: self.peft_config.lora_dropout, \u0026quot;fan_in_fan_out\u0026quot;: self.peft_config.fan_in_fan_out, \u0026quot;merge_weights\u0026quot;: self.peft_config.merge_weights, } key_list = [key for key, _ in self.model.named_modules()] for key in key_list: if any(key.endswith(target_key) for target_key in self.peft_config.target_modules): # 对特定的层插入lora层 parent, target, target_name = self._get_submodules(key) bias = target.bias is not None if isinstance(target, torch.nn.Linear) and self.peft_config.enable_lora is None: new_module = Linear(target.in_features, target.out_features, bias=bias, **kwargs) elif self.peft_config.enable_lora is not None: kwargs.update({\u0026quot;enable_lora\u0026quot;: self.peft_config.enable_lora}) if isinstance(target, Conv1D): in_features, out_features = target.weight.shape else: in_features, out_features = target.in_features, target.out_features if kwargs[\u0026quot;fan_in_fan_out\u0026quot;]: warnings.warn( \u0026quot;fan_in_fan_out is set to True but the target module is not a Conv1D. \u0026quot; \u0026quot;Setting fan_in_fan_out to False.\u0026quot; ) kwargs[\u0026quot;fan_in_fan_out\u0026quot;] = False new_module = MergedLinear(in_features, out_features, bias=bias, **kwargs) self._replace_module(parent, target_name, new_module, target) def _get_submodules(self, key): parent = self.model.get_submodule(\u0026quot;.\u0026quot;.join(key.split(\u0026quot;.\u0026quot;)[:-1])) target_name = key.split(\u0026quot;.\u0026quot;)[-1] target = self.model.get_submodule(key) return parent, target, target_name def _replace_module(self, parent_module, child_name, new_module, old_module): setattr(parent_module, child_name, new_module) new_module.weight = old_module.weight if old_module.bias is not None: new_module.bias = old_module.bias def forward(self, *args, **kwargs): return self.model(*args, **kwargs) def __getattr__(self, name: str): \u0026quot;\u0026quot;\u0026quot;Forward missing attributes to the wrapped module.\u0026quot;\u0026quot;\u0026quot; try: return super().__getattr__(name) # defer to nn.Module\u0026#39;s logic except AttributeError: return getattr(self.model, name) @property def modules_to_save(self): return None def get_peft_config_as_dict(self, inference: bool = False): config = {k: v.value if isinstance(v, Enum) else v for k, v in asdict(self.peft_config).items()} if inference: config[\u0026quot;inference_mode\u0026quot;] = True return config 插入lora层的核心代码如下：\nclass LoraLayer: def __init__( self, r: int, lora_alpha: int, lora_dropout: float, merge_weights: bool, ): self.r = r self.lora_alpha = lora_alpha # Optional dropout if lora_dropout \u0026gt; 0.0: self.lora_dropout = nn.Dropout(p=lora_dropout) else: self.lora_dropout = lambda x: x # Mark the weight as unmerged self.merged = False self.merge_weights = merge_weights class Linear(nn.Linear, LoraLayer): # Lora implemented in a dense layer def __init__( self, in_features: int, out_features: int, r: int = 0, lora_alpha: int = 1, lora_dropout: float = 0.0, fan_in_fan_out: bool = False, # Set this to True if the layer to replace stores weight like (fan_in, fan_out) merge_weights: bool = True, **kwargs, ): nn.Linear.__init__(self, in_features, out_features, **kwargs) LoraLayer.__init__(self, r=r, lora_alpha=lora_alpha, lora_dropout=lora_dropout, merge_weights=merge_weights) self.fan_in_fan_out = fan_in_fan_out # Actual trainable parameters if r \u0026gt; 0: self.lora_A = nn.Linear(in_features, r, bias=False) self.lora_B = nn.Linear(r, out_features, bias=False) self.scaling = self.lora_alpha / self.r # Freezing the pre-trained weight matrix self.weight.requires_grad = False self.reset_parameters() if fan_in_fan_out: self.weight.data = self.weight.data.T def reset_parameters(self): nn.Linear.reset_parameters(self) if hasattr(self, \u0026quot;lora_A\u0026quot;): # initialize A the same way as the default for nn.Linear and B to zero nn.init.kaiming_uniform_(self.lora_A.weight, a=math.sqrt(5)) nn.init.zeros_(self.lora_B.weight) def train(self, mode: bool = True): nn.Linear.train(self, mode) self.lora_A.train(mode) self.lora_B.train(mode) if self.merge_weights and self.merged: # Make sure that the weights are not merged if self.r \u0026gt; 0: self.weight.data -= ( transpose(self.lora_B.weight @ self.lora_A.weight, self.fan_in_fan_out) * self.scaling ) self.merged = False def eval(self): nn.Linear.eval(self) self.lora_A.eval() self.lora_B.eval() if self.merge_weights and not self.merged: # Merge the weights and mark it if self.r \u0026gt; 0: self.weight.data += ( transpose(self.lora_B.weight @ self.lora_A.weight, self.fan_in_fan_out) * self.scaling ) self.merged = True def forward(self, x: torch.Tensor): if self.r \u0026gt; 0 and not self.merged: result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias) if self.r \u0026gt; 0: result += self.lora_B(self.lora_A(self.lora_dropout(x))) * self.scaling return result else: return F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias) 参考 https://snailcoder.github.io/2023/08/06/parameter-efficient-llm-fine-tuning-lora.html\n# 当红炸子鸡 LoRA，是当代微调 LLMs 的正确姿势？\n","date":"2024-08-07T00:00:00Z","permalink":"/lora%E5%BE%AE%E8%B0%83/","title":"Lora微调"},{"content":"MoE 的思想类似于集成学习中的 Ensemble Learning。MoE 作用于原本 transformer 模型的 MLP 层，即：\n图片来自于Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity 论文。\n总结来说，在混合专家模型 (MoE) 中，我们将传统 Transformer 模型中的每个前馈网络 (FFN) 层替换为 MoE 层，其中 MoE 层由两个核心部分组成: 一个路由器（或者叫门控网络）和若干数量的专家。\n用多个 FeedForward 块替换单个 FeedForward 块 （如在 MoE 设置中所做的那样）会大大增加模型的总参数数。然而，关键的诀窍是，我们不会对每个token使用（“激活”）所有专家。相反，路由器仅为每个token选择一小部分专家。\nimage.png 技术选择 Expert Choice 路由（容量为 2，即每个专家选两个 token）在 NLP 模型效果好，Top-K 路由则在视觉模型效果好。 在 NLP 任务中，专家越多越好，而视觉任务中存在饱和点。 增加 MoE 层数也可以增加模型容量，但视觉任务中同样存在饱和点。 恢复优化器状态（一些统计量）和路由权重归一化可以提高视觉 MoE 模型的性能，对 NLP 任务无效。 八股 image.png ","date":"2024-08-07T00:00:00Z","permalink":"/moe/","title":"MoE"},{"content":"这篇论文的核心贡献，在于它为Transformer模型中占据了约三分之二参数量、但长期以来其功能被严重忽视的前馈神经网络（Feed-Forward Network, FFN）层，提供了一个简洁而深刻的解释框架。在此之前，学术界的目光大多聚焦于自注意力（Self-Attention）机制，而FFN层则像一个神秘的“黑箱”。Geva等人的这项工作，通过一系列精巧的实验，令人信服地论证了：FFN层在功能上等同于一个键值记忆（Key-Value Memory）系统。\n具体来说，论文揭示了FFN内部的运作机制。FFN层包含两个线性变换矩阵，论文将它们分别类比为“键（Keys）”和“值（Values）”。当一个输入向量（代表某个词元或文本片段的含义）进入FFN层时，它会首先与所有的“键”进行匹配计算。这个匹配度，论文称之为记忆系数（memory coefficient），决定了每个“键”所对应的“值”被激活的强度。每个“值”本身并不直接是一个词，而是一个向量，它蕴含了一个关于“下一个可能出现的词”的概率分布。最终，FFN层的输出是所有被激活的“值”向量的加权和。这个加权和的输出，实际上是对模型下一步应该生成什么内容的一次“提议”或“修正”。\n论文的一个惊人发现是，这些“记忆”是具有层次性和可解释性的。\nOur experiments show that the learned patterns are human-interpretable, and that lower layers tend to capture shallow patterns, while upper layers learn more semantic ones. 我们的实验表明，学习到的模式是人类可解释的，并且底层网络倾向于捕获浅层模式，而上层网络则学习更具语义的模式。\n例如，在模型的底层（如1-9层），FFN的“键”主要响应一些浅层模式（shallow patterns），比如特定的n-gram短语（例如，以“substitutes”结尾的句子）。而到了模型的高层（如10-16层），“键”则演变为响应更抽象的语义模式（semantic patterns），比如某个特定的话题（例如，关于“电视剧”的讨论）。这一点在论文的 图2 中得到了清晰的展示，图中显示了随着层数的增加，语义模式（绿色部分）的占比显著提升，而浅层模式（蓝色部分）则相应减少。\n图2：论文中的关键图表，展示了不同层级的FFN记忆中，浅层模式与语义模式的比例变化。\n更进一步，论文发现高层FFN中的“值”向量与对应的“键”模式高度相关。当一个高层的“键”被某个输入模式激活时，其对应的“值”所编码的词汇分布，确实能很大概率地预测出该模式之后最可能出现的词。例如，如果一个“键”专门识别“关于二战轰炸机基地”的语境，那么它对应的“值”向量，在被激活后，就会倾向于生成“任务（missions）”、“攻击（attacked）”等相关词汇。论文在 图4 中通过“一致率（agreement rate）”这个指标量化了这一发现，在高层（11-16层），这种一致率达到了约3.5%，虽然看似不高，但论文强调这比随机猜测（约0.0004%）高出几个数量级，证明了其非凡的预测能力。\n最后，论文还探讨了这些记忆是如何被聚合（Aggregating）的。在一个FFN层内部，最终的输出通常不是由单个最强的记忆决定的，而是由数百个被激活的记忆组合（composition）而成，这在 图8 中有明确数据支撑，超过68%的情况下，层的输出与任何单个记忆的预测都不同。在层与层之间，FFN的输出通过残差连接（residual connections）对主干信息流进行精细的修正（refinement）。模型并非在最后一层才做出最终决定，而是在逐层传递的过程中，不断地利用FFN的“记忆”来调整和优化对下一个词的预测。\n总而言之，这篇论文将FFN从一个难以理解的计算模块，变成了一个结构清晰、功能明确的分布式记忆系统。它不仅为我们理解Transformer的内部工作原理打开了一扇窗，也为后续的模型编辑、可信AI和模型压缩等研究方向奠定了坚实的基础。\n1. 论文的研究目标是什么？ 想要解决什么实际问题？这个问题对于行业发展有什么重要意义？ 研究目标：论文的核心研究目标是揭开Transformer模型中前馈神经网络（FFN）层的神秘面纱，并为其功能提供一个清晰、可验证的解释。具体来说，它试图回答一个核心问题：占模型三分之二参数的FFN层，究竟在做什么？\n解决的实际问题：\n模型可解释性缺失：在当时，自注意力机制的研究已经比较深入，但FFN的功能却是一个巨大的知识盲区。这使得我们对大模型的决策过程理解不完整，难以信任其输出，也难以在出错时进行调试。 模型行为的不可预测性：我们不知道模型为何会产生“幻觉”、输出有害内容或泄露隐私数据。如果能理解FFN的功能，就可能从机理上找到这些问题的原因。 对行业发展的重要意义：\n推动可信AI（Trustworthy AI）：理解是信任的前提。通过将FFN解释为键值记忆，这篇论文极大地增强了我们对大模型内部工作原理的理解，是迈向构建更安全、更可控、更值得信赖的AI系统的关键一步。 催生新的技术方向：该研究直接启发了模型编辑（Model Editing）领域。如果我们知道某个事实性错误（比如“埃菲尔铁塔在罗马”）存储在哪个或哪些FFN“记忆”中，我们就有可能直接修改这些“值”向量来纠正错误，而无需重新训练整个庞大的模型。 提升模型效率和安全性：通过识别FFN中存储的“记忆”，我们可以分析哪些是冗余的，从而进行模型压缩；也可以识别哪些“记忆”可能存储了训练数据中的敏感信息，为隐私保护和数据安全审计提供新的工具。 2. 论文提出了哪些新的思路、方法或模型？跟之前的方法相比有什么特点和优势？请尽可能参考论文中的细节进行分析。 新的思路/模型：论文并未提出一个全新的模型架构，而是提出了一个关于现有Transformer FFN层的功能性类比（functional analogy）——即FFN作为非归一化的键值记忆。\n核心方法论：\n概念重构：将FFN的数学公式 FFN(x) = ReLU(x * K^T) * V (简化后) 与神经记忆网络 MN(x) = softmax(x * K^T) * V 进行对比。 \u0026gt; Comparing equations 1 and 2 shows that feed-forward layers are almost identical to key-value neural memories; the only difference is that neural memory uses softmax as the non-linearity f(·), while the canonical transformer does not use a normalizing function in the feed-forward layer. \u0026gt; 对比公式1和2可以发现，前馈层与键值神经记忆几乎完全相同；唯一的区别在于，神经记忆使用softmax作为非线性函数f(·)，而标准的Transformer在前馈层中不使用归一化函数。 关键区别与优势：论文敏锐地指出了ReLU与softmax的区别。softmax会进行归一化，使得所有激活值的和为1，这意味着它倾向于只选择一个或少数几个最匹配的记忆。而ReLU是非归一化的，它允许多个记忆单元被同时、独立地激活。这恰好解释了为何FFN的输出是“组合式”的——它是多个被触发的“想法”（记忆）的叠加，而不是非此即彼的选择。这个洞察是论文的核心优势，更贴近模型在实践中的复杂行为。 分析方法：论文采用了一种“假设-验证”的实验探究方法，而非纯理论推导。\n寻找触发样本（Trigger Examples）：为了探究“键”k_i的功能，研究者在庞大的训练集中搜索，找到那些能够让该键的记忆系数ReLU(x * k_i)取得最大值的输入文本x。这个方法非常巧妙，它让我们能直接“拷问”每个神经元：“什么样的信息最能让你兴奋？” 模式标注与分类：通过人类专家（NLP研究生）对这些触发样本进行归纳，识别出其中的共性模式，并将其分为“浅层”和“语义”两类。这是一种将复杂的神经活动与人类可理解的概念联系起来的有效手段。 值向量分析：将“值”向量v_i通过乘以输出嵌入矩阵E并应用softmax，将其转换为一个完整的词汇表概率分布p_i。这使得研究者可以直接评估每个“记忆”的预测倾向。 与之前笼统地将FFN视为“特征提取器”或简单地增加模型容量的方法相比，这篇论文提供了一个结构化、模块化且可操作的视角，其优势在于将抽象的数学运算赋予了具体的认知功能——记忆与联想。\n3. 论文通过什么实验来验证所提出方法的有效性？实验是如何设计的？实验数据和结果如何？请引用关键数据加以说明。 论文设计了一系列环环相扣的实验来验证其核心假设。\n实验一：验证“键（Keys）”捕获可解释的输入模式 设计：研究者从一个16层的Transformer语言模型中，每层随机抽取10个“键”（共160个）。对每个键，他们检索出训练集中最能激活它的前25个文本前缀。然后，由人类专家分析这25个前缀，归纳出它们共同的模式。 数据与结果： 高覆盖率：对于几乎每一个被分析的键，专家都能找到至少一种可识别的模式。平均每个键能识别出3.6个不同的模式。 模式分层：如前所述，图2清晰地展示了从低层到高层，模式由浅层（如固定短语）向语义（如主题）的转变。在第1层，超过80%的模式是浅层的；而在第16层，约70%的模式是语义的。 具体案例：表1 提供了一些生动的例子。例如，第4层的k_449专门响应以“substitutes”结尾的句子；而第16层的k_1635则响应关于“电视剧”的上下文。 实验二：验证“值（Values）”存储与键相关的输出分布 设计：对于每个键k_i，研究者找到最能激活它的那个文本前缀（top-1 trigger example），并记录其真实的下一个词w_t。同时，他们将对应的“值”向量v_i转换为词汇表分布p_i，并查看其预测概率最高的词argmax(p_i)。他们计算这两个词匹配的频率，即“一致率”。 数据与结果： 高层一致性：图4 显示，在1-10层，一致率几乎为零。但从第11层开始，一致率迅速攀升，到第16层时达到约3.5%。这证明了高层“值”向量确实存储了有意义的、与“键”模式匹配的预测信息。 排名提升：图5 从另一个角度验证了这一点。它展示了真实下一个词w_t在“值”向量v_i的预测排名分布。随着层数加深，这个排名显著变好（箱形图整体下移），意味着w_t被赋予了更高的概率。 实验三：验证记忆的聚合机制（Intra-layer \u0026amp; Inter-layer） 设计： 层内聚合：研究者随机抽取4000个验证集样本，观察每一层FFN的输出。他们统计有多少记忆被激活，并比较最终的层输出预测是否与任何单个记忆的预测相同。 层间聚合：他们比较了每一层的残差流（FFN的输入）的预测与经过FFN修正后的输出预测，以及模型的最终预测。 数据与结果： 组合性是常态：图8 的结果非常关键，它显示在所有层中，有超过68%的情况下，FFN层的最终输出预测与该层内任何一个单独的记忆单元的预测都不相同。这强有力地证明了FFN的输出是组合（compositional）而非选择性的。 逐层精炼：图9 显示，模型的最终预测结果在很深的层就已经被残差流（residual）的顶层预测所确定。例如，在第10层，超过60%的情况下，残差流的预测就是模型的最终预测。这说明FFN层扮演的角色更像是对一个已经不错的“草稿”进行微调和修正。 4. 结合大模型领域的当前学术理解，未来在该研究方向上还有哪些值得进一步探索的问题和挑战？这可能催生出什么新的技术和投资机会? 这篇论文开辟了一个充满机遇的研究领域，至今仍在不断发展。\n值得探索的问题和挑战： 与自注意力的协同机制：论文主要孤立地研究FFN，但FFN的输入恰好是自注意力层的输出。未来的一个核心挑战是理解自注意力层是如何“预处理”和“路由”信息，以便激活正确的FFN记忆的。它们之间存在怎样的“电路（circuits）”？ 跨模型和任务的泛化性：该论文的结论主要基于一个中等规模的GPT风格模型。这些发现在今天的千亿、万亿参数模型（如GPT-4、Llama系列）中是否仍然成立？在不同的模型架构（如Encoder-Decoder、MoE）和不同任务（如代码生成、多模态理解）中，FFN的记忆机制有何异同？ 记忆的形成与学习：FFN中的这些“记忆”是在训练过程中如何形成的？它们与训练数据中的具体样本有何对应关系？理解这一点对于解决数据隐私和版权问题至关重要。 组合性的语法：我们知道FFN的输出是组合的，但这种组合遵循什么样的“语法”或规则？当多个记忆被激活时，模型如何权衡和整合它们的信息？这背后是否存在更深层次的代数结构？ 可能催生的新技术和投资机会： AI安全与对齐（AI Safety and Alignment）：通过深入理解FFN的记忆机制，可以开发出更先进的工具来检测和抑制模型的有害行为（如偏见、谎言）。这是一个巨大的投资赛道，专注于“可控AI”的公司将有很大潜力。 精准模型编辑与维护：基于对FFN记忆的理解，可以发展出成熟的“模型手术”技术，对线上部署的大模型进行实时、低成本的知识更新和错误修复。这将催生“大模型运维（LLMOps）”市场中的新兴服务。 自动化可解释性工具：将论文中需要人工标注的模式发现过程自动化，开发出能够自动分析并报告任意大模型内部“记忆”功能的商业软件。这将成为AI开发和审计流程中的标准工具。 新一代AI芯片设计：如果FFN的核心功能是稀疏激活的键值查找，那么未来的AI硬件或许可以为此进行专门优化，设计出能效更高、更适合大模型推理的芯片架构。 5. 退一步，从批判的视角看，这篇论文还存在哪些不足及缺失？又有哪些需要进一步验证和存疑的？ 尽管这篇论文极其出色，但从今天的视角看，我们仍可以提出一些批判性的思考。\n主要不足与缺失： 对“模式”的定义依赖于人类主观性：论文的核心证据之一来自于人类专家对触发样本的标注。这个过程是主观的，且可能过度简化了神经元实际响应的复杂模式。一个神经元可能响应的是比人类语言标签更抽象、更高维的特征组合。 对“值”向量功能的简化：“值”向量v_i被分析为对下一个词的直接预测，但它的实际作用是向残差流中添加一个方向向量，这个向量会影响后续所有层的计算。论文的分析方法捕捉到了其主要功能，但可能忽略了它对模型深层计算流的更长远、更复杂的影响。 实验范围的局限性：如前所述，实验仅限于一个特定的模型（Baevski \u0026amp; Auli, 2019）和一个特定的数据集（WikiText-103）。其结论在更大、更多样化的现代模型上的普适性有待验证。 需要进一步验证和存疑之处： 浅层与语义的截然划分：论文将模式清晰地划分为浅层和语义，并观察到在第9-10层左右的转变。这个转变是平滑过渡还是突变？在更大的模型中，这个分界点是否存在，或者说是否存在更复杂的、多阶段的模式演化？ 记忆的稀疏性假设：论文提到，大部分记忆单元在单次推理中是不活跃的。这一稀疏性是FFN高效工作的关键吗？这直接启发了后来的混合专家（MoE）模型。但FFN层是否真的可以被看作是一个大型的、静态的MoE层，这一点仍需更深入的验证。 “组合”的本质：当FFN的输出是一个与所有单个记忆都不同的“妥协”或“组合”时，这个新的预测是如何产生的？论文称之为“composition”，但其背后的计算原理仍不清晰。它仅仅是向量的线性叠加，还是存在更复杂的非线性相互作用？ 6. 我希望从这篇论文中找一些拿来即用的创新想法，我应该从这篇论文中重点学什么？有哪些启发？你认为我还需要补充了解哪些背景知识? 当然，这篇论文是创新思想的宝库。\n重点学习与启发： 功能性抽象的思维方式：最重要的启发是，不要将模型的组件视为纯粹的数学黑箱，而要尝试为其赋予功能性的、可类比的解释。将FFN类比为“键值记忆”就是一个绝佳的例子。当您遇到一个复杂的系统时，尝试提出一个简单的、可检验的功能假设，然后设计实验去验证它。 “触发样本”的调试方法：这是一个非常实用的调试和分析技术。当您的模型做出奇怪或错误的预测时，可以借鉴论文的方法，去检查是哪些内部单元（如FFN神经元）被异常激活了。然后，去查找这些单元通常在什么情况下被激活（即它们的“触发样本”），这往往能帮助您定位问题的根源。 分层和组合的视角：理解模型的决策是一个逐层构建和精炼的过程，而非一步到位。同时，要认识到最终的输出往往是多个“内部意见”组合的结果。这启发我们在设计和分析模型时，不仅要看最终输出，更要关注信息在网络中流动的中间状态。 需要补充的背景知识： Transformer基础架构：您需要非常熟悉原始论文《Attention Is All You Need》中的所有细节，包括自注意力、多头注意力、位置编码、FFN和残差连接的具体计算过程。 机械可解释性（Mechanistic Interpretability）：这是本篇论文所属的领域。建议了解该领域的目标，即彻底理解模型内部的每一个计算步骤和参数是如何协同工作以实现其功能的，可以关注一下“电路（circuits）”这个概念。 向量语义与嵌入（Vector Semantics \u0026amp; Embeddings）：深入理解词嵌入（如Word2Vec, GloVe）和句子嵌入的思想，即如何将文本表示为高维空间中的向量，以及这些向量在数学上如何捕捉语义关系。这是理解FFN中“键”与输入匹配、“值”编码输出分布的基础。 后续研究进展：阅读一些跟进这项工作的论文，比如关于模型编辑的 ROME 和 MEMIT 算法，或者关于定位模型知识的研究，这将帮助您了解这个领域最新的发展。 ","date":"2024-08-07T00:00:00Z","permalink":"/transformer-feed-forward-layers-are-key-value-memories/","title":"Transformer Feed-Forward Layers Are Key-Value Memories"},{"content":" Problem:\n思路 注意拓扑排序最好是邻接表（哈系表实现），并用队列处理后续入度为0的点\n解题方法 描述你的解题方法\n复杂度 时间复杂度:\n添加时间复杂度, 示例： \\(O(n)\\)\n空间复杂度:\n添加空间复杂度, 示例： \\(O(n)\\)\nCode class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -\u0026gt; bool: if not prerequisites: return True def get_zero(numCourses, prerequisites, temp): queue = [] for prerequisite in prerequisites: temp[prerequisite[0]] += 1 for i in range(len(temp)): if temp[i] == 0: queue.append(i) return queue temp = [0 for _ in range(numCourses)] queue = get_zero(numCourses, prerequisites, temp) if queue == []: return False from collections import defaultdict d = defaultdict(list) for prerequisite in prerequisites: d[prerequisite[1]].append(prerequisite[0]) while queue: idx = queue.pop(0) nexs = d[idx] if nexs: for nex in nexs: temp[nex] -= 1 if temp[nex] == 0: queue.append(nex) numCourses -= 1 return numCourses == 0 ","date":"2024-03-29T00:00:00Z","permalink":"/%E8%AF%BE%E7%A8%8B%E8%A1%A8%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F/","title":"课程表（拓扑排序）"},{"content":" Problem:\n思路 讲述看到这一题的思路\n解题方法 描述你的解题方法\n复杂度 时间复杂度:\n添加时间复杂度, 示例： \\(O(n)\\)\n空间复杂度:\n添加空间复杂度, 示例： \\(O(n)\\)\nCode ","date":"2024-03-18T00:00:00Z","permalink":"/%E4%B9%98%E7%A7%AF%E6%9C%80%E5%A4%A7%E7%9A%84%E6%95%B0%E7%BB%84/","title":"乘积最大的数组"},{"content":"跳跃游戏 Problem:\n思路 讲述看到这一题的思路\n解题方法 描述你的解题方法\n复杂度 时间复杂度:\n添加时间复杂度, 示例： \\(O(n)\\)\n空间复杂度:\n添加空间复杂度, 示例： \\(O(n)\\)\nCode ```Python3 []\n# 跳跃游戏ii # 划分字母区间 [763. 划分字母区间 - 力扣（LeetCode）](https://leetcode.cn/problems/partition-labels/description/?envType=study-plan-v2\u0026amp;envId=top-100-liked) 本题预处理完毕后思路和跳跃游戏2类似，当然也可以使用合并区间的思路来，都是贪心算法。 ## Code ```python class Solution: def partitionLabels(self, s: str) -\u0026gt; List[int]: from collections import defaultdict d = defaultdict(list) for i, char in enumerate(s): d[char].append(i) # 也可以考虑合并区间做了，下面的解法类似跳跃游戏2 res = [] start = 0 max_jump = 0 for i, char in enumerate(s): max_jump = max(max_jump, d[char][-1]) if i == max_jump: res.append(i - start + 1) start = i + 1 max_jump = 0 return res ","date":"2024-03-18T00:00:00Z","permalink":"/%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8F/","title":"跳跃游戏"},{"content":"41. 缺失的第一个正数 - 力扣（LeetCode） 空间复杂度o(n)很好想，但o(1)不好想，还是个408考研真题\n注意O(n) == O(2n)，即相较于边遍历边判断，还是遍历两次更加方便且不会有太多损失。类似思想：73. 矩阵置零 - 力扣（LeetCode）\nclass Solution: def firstMissingPositive(self, nums: List[int]) -\u0026gt; int: # 将原数组当作哈希表使用。 for i, num in enumerate(nums): if num \u0026lt;= 0: # 先把小于0的先一步处理，以便于使用标记 nums[i] = len(nums) + 1 for i, num in enumerate(nums): num = abs(num) if num \u0026gt;= 1 and num \u0026lt;= len(nums): if nums[num-1] \u0026gt; 0: # 不重复添加 nums[num-1] = -nums[num-1] res = len(nums) + 1 for i, num in enumerate(nums): if num \u0026gt; 0: res = i + 1 break return res ","date":"2024-03-16T00:00:00Z","permalink":"/%E7%BC%BA%E5%A4%B1%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%AD%A3%E6%95%B0/","title":"缺失的第一个正数"},{"content":"题目地址 # 思路 通过前缀和+哈希表，并有简单的数学变换。前缀和即 \\(y[i]=y[i-1]+x[i]\\) 类比于accumlate函数，注意前缀和思想也可以应用为“前缀积、后缀和、后缀积”等思想。238. 除自身以外数组的乘积 - 力扣（LeetCode） \u0026gt; 使用前缀和的方法可以解决这个问题，因为我们需要找到和为k的连续子数组的个数。通过计算前缀和，我们可以将问题转化为求解两个前缀和之差等于k的情况。 \u0026gt;假设数组的前缀和数组为prefixSum，其中prefixSum[i]表示从数组起始位置到第i个位置的元素之和。那么对于任意的两个下标i和j（i \u0026lt; j），如果prefixSum[j] - prefixSum[i] = k，即从第i个位置到第j个位置的元素之和等于k，那么说明从第i+1个位置到第j个位置的连续子数组的和为k。 通过遍历数组，计算每个位置的前缀和，并使用一个哈希表来存储每个前缀和出现的次数。在遍历的过程中，我们检查是否存在prefixSum[j] - k的前缀和，如果存在，说明从某个位置到当前位置的连续子数组的和为k，我们将对应的次数累加到结果中。 这样，通过遍历一次数组，我们可以统计出和为k的连续子数组的个数，并且时间复杂度为O(n)，其中n为数组的长度。 # 代码\nclass Solution: def subarraySum(self, nums: List[int], k: int) -\u0026gt; int: from collections import defaultdict d = defaultdict(int) d[0] = 1 prefix = 0 res = 0 for num in nums: prefix += num temp = prefix - k if temp in d: res += d[temp] d[prefix] += 1 return res ","date":"2024-03-14T00:00:00Z","permalink":"/%E5%92%8C%E4%B8%BAk%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/","title":"和为K的子数组"},{"content":"题目地址 # 思路 通过前缀和+哈希表，并有简单的数学变换。前缀和即 \\(y[i]=y[i-1]+x[i]\\) 类比于accumlate函数，注意前缀和思想也可以应用为“前缀积、后缀和、后缀积”等思想。238. 除自身以外数组的乘积 - 力扣（LeetCode） \u0026gt; 使用前缀和的方法可以解决这个问题，因为我们需要找到和为k的连续子数组的个数。通过计算前缀和，我们可以将问题转化为求解两个前缀和之差等于k的情况。 \u0026gt;假设数组的前缀和数组为prefixSum，其中prefixSum[i]表示从数组起始位置到第i个位置的元素之和。那么对于任意的两个下标i和j（i \u0026lt; j），如果prefixSum[j] - prefixSum[i] = k，即从第i个位置到第j个位置的元素之和等于k，那么说明从第i+1个位置到第j个位置的连续子数组的和为k。 通过遍历数组，计算每个位置的前缀和，并使用一个哈希表来存储每个前缀和出现的次数。在遍历的过程中，我们检查是否存在prefixSum[j] - k的前缀和，如果存在，说明从某个位置到当前位置的连续子数组的和为k，我们将对应的次数累加到结果中。 这样，通过遍历一次数组，我们可以统计出和为k的连续子数组的个数，并且时间复杂度为O(n)，其中n为数组的长度。 # 代码\nclass Solution: def subarraySum(self, nums: List[int], k: int) -\u0026gt; int: from collections import defaultdict d = defaultdict(int) d[0] = 1 prefix = 0 res = 0 for num in nums: prefix += num temp = prefix - k if temp in d: res += d[temp] d[prefix] += 1 return res ","date":"2024-03-14T00:00:00Z","permalink":"/%E5%92%8C%E4%B8%BAk%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84-1/","title":"和为K的子数组"},{"content":"def find(x): if (p[x] != x): p[x] = find(p[x]) return p[x] 上面是y总的模板，实现了路径压缩。\n","date":"2024-03-13T00:00:00Z","permalink":"/%E5%B9%B6%E6%9F%A5%E9%9B%86/","title":"并查集"},{"content":"","date":"2024-03-09T00:00:00Z","permalink":"/t5/","title":"T5"},{"content":"温度超参数t，一般为softmax结果除以该参数，或者在对比学习中，相似度除以参数t。 如图： 上图为无监督simcse中的损失函数。\nt越大，结果越平滑，t越小，得到的概率分布更“尖锐”。 当t趋于0时： 此时只关注最困难的负样本（smax）。 当t趋于∞时： 此时对比损失对所有负样本的权重都相同。\n因此t越大，可以避免陷入局部最优解。(局部最优是过早地确定了优化的梯度方向，失去了在其他方向上探索的机会。较大的温度使得各个方向上的梯度差异没有那么明显，从而获得了在早期更多的探索机会。)\n[!note] 温度系数的作用是调节对困难样本的关注程度：越小的温度系数越关注于将本样本和最相似的困难样本分开，去得到更均匀的表示。然而困难样本往往是与本样本相似程度较高的，很多困难负样本其实是潜在的正样本，过分强迫与困难样本分开会破坏学到的潜在语义结构，因此，温度系数不能过小 考虑两个极端情况，温度系数趋向于0时，对比损失退化为只关注最困难的负样本的损失函数；当温度系数趋向于无穷大时，对比损失对所有负样本都一视同仁，失去了困难样本关注的特性。 可以把不同的负样本想像成同极点电荷在不同距离处的受力情况，距离越近的点电荷受到的库伦斥力更大，而距离越远的点电荷受到的斥力越小。对比损失也是这样的。这种性质更有利于形成在超球面均匀分布的特征。\n语言建模 (lena-voita.github.io)中有个小游戏可以看到随着温度的变化导致概率分布的变化\n参考 CVPR2021自监督学习论文: 理解对比损失的性质以及温度系数的作用 - 知乎 (zhihu.com)\n","date":"2024-01-14T00:00:00Z","permalink":"/%E6%B8%A9%E5%BA%A6%E8%B6%85%E5%8F%82%E6%95%B0/","title":"温度超参数"},{"content":"leetcode地址：953. 验证外星语词典 - 力扣（LeetCode）\n简单方法 python列表之间也可以进行比较（太灵活了），比如[1, 2, 3] \u0026lt; [2, 2, 3]成立，即按照字典序进行比较，与其是一样的比较规则。因此对于本题可以利用python的特性轻松解决。 好久没写python了，变得很生疏，一开始写的很蠢：\nclass Solution: def isAlienSorted(self, words: List[str], order: str) -\u0026gt; bool: d = dict(zip(order, range(len(order)))) words = list(map(lambda s: [d[i] for i in s], words)) print([1, 2, 3] \u0026lt; [2, 2, 3]) return words == sorted(words) 后来想起来了sorted中还有个key参数，并且列表还有个index方法（我基本上没用过），于是改成了一行\nclass Solution: def isAlienSorted(self, words: List[str], order: str) -\u0026gt; bool: return words == sorted(words, key=lambda w:[order.index(x) for x in w]) ","date":"2024-01-08T00:00:00Z","permalink":"/%E9%AA%8C%E8%AF%81%E5%A4%96%E6%98%9F%E8%AF%AD%E8%AF%8D%E5%85%B8/","title":"验证外星语词典"},{"content":"\n","date":"2023-06-10T00:00:00Z","permalink":"/fp-growth/","title":"FP-Growth"},{"content":"","date":"2023-03-22T00:00:00Z","permalink":"/batch-norm/","title":"Batch Norm"},{"content":"L1正则化 L2正则化 权重衰减 L2正则化和权重衰减的区别 L2正则化是在损失函数上做文章。 权重衰减是在梯度更新时增加一项。 ","date":"2023-03-22T00:00:00Z","permalink":"/l1-l2%E6%AD%A3%E5%88%99%E5%8C%96%E5%92%8C%E6%9D%83%E9%87%8D%E8%A1%B0%E5%87%8F/","title":"L1 L2正则化"},{"content":"pre-norm Pre-norm:\\(X_{t+1}=X_{t}+F_{t}(Norm(X_{t}))\\)\n\\(先来看Pre-norm^{+},递归展开：\\) \\[X_{t+1}=X_t+F_t(Norm(X_t))\\] \\(=X_{0}+F_{1}(Norm(X_{1}))+\\ldots+F_{t-1}(Norm(X_{t-1}))+F_{t}(Norm(X_{t}))\\) 其中，展开\\(^{+}\\)后的每一项( \\(F_{1}( Norm( X_{1}) ) , \\ldots\\), \\(F_{t- 1}( Norm( X_{t- 1}) )\\), \\(F_{t}( Norm( X_{t}) )\\))之间都是同一量级的， 所以\\(F_1(Norm(X_1))+\\ldots F_{t-1}(Norm(X_{t-1}))+F_t(Norm(X_t))\\)和 \\(F_1(Norm(X_1))+\\ldots F_{t-1}(Norm(X_{t-1}))\\)之间的区别就像t和t-1的区别一样，我们可以将 其记为\\(X_t+ 1= \\mathscr{O} ( t+ 1)\\) . 这种特性就导致当t足够大的时候，\\(X_{t+1}\\)和\\(X_t\\)之间区别可以忽略不计（直觉上），那么就有：\n\\[F_t(X_t)+F_{t+1}(X_{t+1})\\approx F_t(X_t)+F_{t+1}(X_t)=(F_t\\bigoplus F_{t+1})(X_t)\\] 这就是所谓的增加宽度，而没有增加深度。从而导致pre-norm的精度不高。 ## post-norm\nPost-norm:\\(X_{t+1}=Norm(X_{t}+F_{t}(x_{t}))\\) 本来layernorm是为了缓解梯度消失，但是在post-norm这里却成为了梯度消失的罪魁祸首。也导致了收敛较难、需要大量调参。\n\\[X_{t+1}=Norm(X_t+F_t(X_t))=\\frac{X_t+F_t(X_t)}{\\sqrt{2}}\\] \\[=\\frac{X_0}{\\sqrt{2}^{t+1}}+\\frac{F_0(X_0)}{\\sqrt{2}^{t+1}}+\\ldots+\\frac{F_{t-1}(X_{t-1})}{\\sqrt{2}^2}+\\frac{F_t(X_t)}{\\sqrt{2}}\\:(\\] 这个结构跟pre-norm比起来充分考虑了所有分支 (残差\\(^{+})\\) 的输出，做到了真正增加深度，自然精度会相对好一些。\n不过它也有它很显然的问题，当t足够大、也就是叠加的attention层足够多以后，底层那些分支(残差)的影响力被衰减掉了，残差有利于解决梯度消失，但是在Post Norm中，残差这条通道被严重削弱了，越靠近输入，削弱得越严重，残差“名存实亡”，那么势必会有梯度消失的问题，这也就是文章开头所说的postnorm难收敛、参数难调的原因。本来我们做Norm也是为了处理梯度消失，但从分析看来，transformer结构中的layernorm\\(^{+}\\)并没有完全实现它的作用。那这就意味着transformer原始结构的失败吗？并不是的，因为这种梯度消失的问题在整个结构上来看(配合上adam系优化器和学习率warmup，warmup对于post-norm极为重要) 是并不明显的。\n离输入层的残差影响力弱这一特性，也有它的用武之地，比如在finetune的时候，我们就希望不要过多调整靠近输入层的参数、以免破坏预训练的效果。\nwarmup的重要性 Post-LN Transformer在训练的初始阶段，输出层附近的期望梯度非常大，所以，如果没有warm-up，模型优化过程就会炸裂，非常不稳定。 模型对越靠后的层越敏感，也就是越靠后的层学习得越快，然后后面的层是以前面的层的输出为输入的，前面的层根本就没学好，所以后面的层虽然学得快，但却是建立在糟糕的输入基础上的。 很快地，后面的层以糟糕的输入为基础到达了一个糟糕的局部最优点，此时它的学习开始放缓（因为已经到达了它认为的最优点附近），同时反向传播给前面层的梯度信号进一步变弱，这就导致了前面的层的梯度变得不准。但 Adam 的更新量是常数量级的，梯度不准，但更新量依然是常数量级，意味着可能就是一个常数量级的随机噪声了，于是学习方向开始不合理，前面的输出开始崩盘，导致后面的层也一并崩盘。 从上图中就可以看出来，post-ln在开始阶段层数越高梯度越大，此时需要小学习率，而当warmup完后，梯度变得很小（绿色部分）。此时可以使用大学习率。\n很好的总结回答 为什么 layer norm 会使方差累积和训练不稳定 让我们来追踪一下 Pre-Norm 结构中数据 x 的方差变化：\n假设我们有一个输入 \\(x_l\\)，它进入第 l 个残差块。\n在 Sublayer 分支中，我们首先对 \\(x_l\\) 进行 LayerNorm。\\(LayerNorm(x_l)\\) 的输出具有严格的均值为0，方差为1 的特性。\n这个归一化后的结果被送入 Sublayer（例如一个全连接网络或注意力层）。经过计算后，输出 \\(Sublayer(LayerNorm(x_l))\\) 的方差通常不再是1，我们假设它的方差是 Var(S)。\n最后，这个 Sublayer 的输出被加回到原始的、未归一化的输入 \\(x_l\\) 上，得到该层的最终输出 \\(x_{l+1}\\)：\n\\(x_{l+1} = x_l + Sublayer(LayerNorm(x_l))\\)\n现在我们来计算 x_{l+1} 的方差。在统计学中，如果两个变量（这里是 x_l 和 Sublayer(…)）大致不相关，那么它们和的方差约等于它们各自方差的和： \\(Var(x_{l+1}) ≈ Var(x_l) + Var(Sublayer(LayerNorm(x_l)))\\)\n\\(Var(x_{l+1}) ≈ Var(x_l) + Var(S)\\)\n这个公式清晰地揭示了问题所在：每一层的输出方差 \\(Var(x_{l+1})\\) 都是在前一层方差 \\(Var(x_l)\\) 的基础上，又增加了一个正数 \\(Var(S)\\)。\n因此，随着网络层数的加深（l 变大），\\(x_l\\) 的方差会像滚雪球一样不断累加，导致主干分支上的数值越来越大。这可能会在训练后期导致数值不稳定。\n对比 Post-Norm：\n在 Post-Norm 中，\\(x_{l+1} = LayerNorm(...)\\)。无论括号里的值 \\((x_l + Sublayer(x_l))\\) 方差多大，经过最后的 LayerNorm 后，输出 \\(x_{l+1}\\) 的方差都会被强制重置为 1。因此，Post-Norm 不存在方差累积增大的问题，但它也因此带来了训练初期不稳定、需要特殊学习率热身（warm-up）等其他问题。\nPrenorm公式还意味着，流经网络主干道（即 x_0, x_1, x_2, …）的信号，其“能量”（方差）在一层层地单调递增。经过几十上百层网络后，x_l 的数值大小（即激活值）就会变得非常巨大。这就是“巨量激活值”（massive activations）的直接来源。\n现在我们有了“巨量激活值”，为什么这会导致训练不稳定呢？这与反向传播和参数更新的机制有关。\n产生巨量梯度 (Massive Gradients)：在反向传播计算梯度时，很多层的梯度都与其前向传播时的激活值成正比。举一个简化的例子，对于一个权重 W，它的梯度 ∂L/∂W 通常会包含一个与输入激活值 x 相乘的项。如果 x 是一个“巨量激活值”，那么计算出的梯度 ∂L/∂W 也会是一个“巨量梯度”。\n破坏优化器状态 (Corrupting Optimizer State)：像 Adam 这样的现代优化器，会维护梯度的历史信息（如一阶矩和二阶矩的移动平均）。一个突然出现的巨量梯度会严重“污染”这些历史平均值，使得优化器对学习率的自适应调整产生剧烈摆动。\n灾难性的参数更新 (Catastrophic Weight Updates)：优化器使用这个被污染的巨量梯度来更新模型权重：W_new = W_old - lr * massive_gradient。这个更新步长会异常巨大，相当于在复杂的损失地形上进行了一次“盲目的、大跨步的跳跃”。\n损失尖峰 (Loss Spikes)：这次“大跳跃”很可能会让模型参数跳到一个非常糟糕的位置，导致模型的预测性能急剧下降，训练损失（Loss）瞬间飙升，在训练曲线上就形成了一个“尖峰”。\n训练发散 (Training Divergence)：如果这个“尖峰”过于极端，参数更新可能会导致数值溢出（inf）或非法操作（NaN），整个训练过程就此崩溃，即“训练发散”。\nAdam如何缓解梯度消失 其实。最关键的原因是，在当前的各种自适应优化技术“下，我们已经不大担心梯度消失问题了。这是因为，当前 NLP 中主流的优化器是 Adam 及其变种。对于 Adam 来说，由于包含了动量和二阶矩校正，所以近似来看，它的更新量大致上为 \\[\\Delta\\theta=-\\eta\\frac{\\mathbb{E}_{t}[g_{t}]}{\\sqrt{\\mathbb{E}_{t}[g_{t}^{2}]}}\\] 可以看到，分子分母是都是同量纲的，因此分式结果其实就是 (1)的量级，而更新量就是 (n)量级。也就是说，理论上只要梯度的绝对值大于随机误差，那么对应的参数都会有常数量级的更新量（意思就是参数的更新量与梯度的关系不是很大，因此受梯度消失影响较小）；这跟 SGD 不一样，SGD 的更新量是正比于梯度的，只要梯度小，更新量也会很小，如果梯度 过小，那么参数几乎会没被更新。 所以，Post Norm 的残差虽然被严重削弱，但是在 base、large 级别的模型中，它还不至于削弱到小于随机误差的地步，因此配合 Adam 等优化器，它还是可以得到有效更新的，也就有可能成功训练了。当然，只是有可能，事实上越深的 Post Norm 模型确实越难训练，比如要仔细调节学习率和 Warmup 等\nDeep-norm \\(最后再提一下DeepNet中结合Post-LN^+的良好性能以及Pre-LN的训练稳定性做出的改良\\)。 \\[X_{t+1}=Norm(\\alpha X_t+F_t(X_t))\\text{(6)}\\] \\(它在add norm之前给输入乘了一个up-scale^+的常数系数 α\u0026gt;1\\)。\n现在 (5) 的展开为： \\[X_{t+1}=\\frac{\\alpha^{t+1}X_{0}}{\\sqrt{2}^{t+1}}+\\frac{\\alpha^{t}F_{0}(X_{0})}{\\sqrt{2}^{t+1}}+\\ldots+\\frac{\\alpha F_{t-1}(X_{t-1})}{\\sqrt{2}^{2}}+\\frac{F_{t}(X_{t})}{\\sqrt{2}}\\] 因为\\(\\alpha\u0026gt;1\\) ,所以它能够在保留post-norm真正增加了深度这优点的同时，一定程度避免了梯度消失。（本质还是post-norm）\nPeri-LN Peri-LN：Revisiting Normalization Layer in the Transformer Architecture\n参考 Transformer梳理（一）：Post-Norm VS Pre-Norm - 知乎 (zhihu.com) 模型优化漫谈：BERT的初始标准差为什么是0.02？ - 科学空间|Scientific Spaces (kexue.fm) 为什么Pre Norm的效果不如Post Norm？ - 科学空间|Scientific Spaces (kexue.fm) 香侬读 | Transformer中warm-up和LayerNorm的重要性探究 - 知乎 (zhihu.com) Bert/Transformer 被忽视的细节（或许可以用来做面试题） - 知乎 (zhihu.com)\n","date":"2023-03-22T00:00:00Z","permalink":"/layer-norm/","title":"Layer Norm"},{"content":"在机器学习中，hinge loss是一种损失函数，它通常用于”maximum-margin”的分类任务中，如支持向量机。数学表达式为：\n其中 \\(\\hat{y}\\) 表示预测输出，通常都是软结果（就是说输出不是0，1这种，可能是0.87。）， \\(y\\) 表示正确的类别。 - 如果 \\(\\hat{y}y\u0026lt;1\\) ，则损失为： \\(1-\\hat{y}y\\) - 如果\\(\\hat{y}y\u0026gt;1\\) ，则损失为：0\n","date":"2023-03-13T00:00:00Z","permalink":"/hinge-loss/","title":"hinge loss"},{"content":"前几天试着投了简历，没想到有两家约了面试，一个是得物一个是北京百分点，得物面试没有怎么准备，太仓促了，二面挂了，百分点拿到了offer，但决定考研了就没去，记录一下面试的问题。岗位是nlp算法岗。\n一面：技术面 先自我介绍，然后介绍了一下项目。根据印象提问有以下内容：\n有没有数据不平衡问题，是怎么解决的？欠采样，过采样，focal loss，着重介绍了一下focal loss的参数含义。 对分类问题最后一层要怎么做：max、mean，全连接 有没有尝试过别的模型：没有，然后面试官说其实textcnn和fasttext效果不一定比bert效果差。 对传统的神经网络了解吗？了解。介绍一下lstm。按照三个门分析就行，哪个门是什么作用，注意一下激活函数的不同。 介绍一下bert，从输入开始介绍就行，把两个任务也展开说一下。 介绍一下tfidf，这个很简单，说一下tf和idf的含义和公式就行。 数据增强怎么做的？同义词替换、回译。 介绍一下提示学习。说一下主要思想，还有离散prompts和连续prompts。 大体就记得问了这些，总体来说不是很难，而且大多数都是神经网络相关的，准备了一些传统的机器学习方法也没有用上。 然后是两道算法题： 第一题：已知一随机发生器，产生0的概率是p，产生1的概率是1-p，\n现在要你构造一个发生器，\n使得它构造0和1的概率均为 1/2；\ndef res(): a = random() b = random() if a == 0 and b == 1: return 0 if a == 1 and b == 0: return 1 else: return res() 由于需要产生1/2，而用1位0，或1位1无法产生等概率， 因此，考虑将随机数扩展成2位： 00 pp 01 p(1-p) 10 (1-p)p 11 (1-p)(1-p) 有上述分析知道，01和10是等概率的，因此我们只需要产生01和10就行了。 于是可以，遇到00和11就丢弃，只记录01和10。可以令，01表示0,10表示1，则等概率1/2产生0和1了。\n当时面试官问了这个问题的时候有点懵，因为之前从来没有做过，但是经过面试官的提示也是写出来了。\n第二题是LCS问题，就是最长公共子序列问题，当时想到了使用动态规划，但是懒得在纸上推转移方程了，就直接暴力解决了。\ndef LCS(s1, s2): dp = [[0 for _ in range(len(s1))] for _ in range(len(s2))] res = 0 for i in range(len(s1)): for j in range(i, len(s2)): if s1[i:j+1] in s2: res = max(res, j+1-i) return res print(LCS(\u0026quot;baced\u0026quot;, \u0026quot;acefg\u0026quot;)) 二面综合面 上来也是自我介绍加项目介绍，有些问题和一面重复了，就不赘述。\n介绍几种bert的变体，albert、roberta，就介绍了这两种。 bert的mask策略。 具体的也忘了，当时也没有想直接记录下来，后面就等到了口头offer，过几天也发了正式offer，但是因为要考研等一些原因就拒了，等考完研再找吧。这次只在软件上投了投简历，没有内推等，还是挺有收获的。\n总结 这几轮面试还是很有收获的，也算是模拟了以后的考研复试，虽然以后大概率是线下复试了。还有就是简历上最最重要的就是实习经历，我之前也没有实习经历，因此没有实习经历的情况下最重要的就是项目，别的基本上都没有问，不过还是要保证你写上去的都是真的会的。因此重要的就是实习和项目，获奖经历可能只是给面试的机会更大一些。这次拒绝offer少了一次实习经历还算有点可惜，不过相对而言还是考研重要一些。\n","date":"2023-03-10T00:00:00Z","permalink":"/%E5%8C%97%E4%BA%AC%E7%99%BE%E5%88%86%E7%82%B9%E9%9D%A2%E7%BB%8F/","title":"北京百分点面经"},{"content":"KMP是字符串匹配问题的算法。“字符串A是否为字符串B的子串?如果是的话出现在B的哪些位置?”该问题就是字符串匹配问题，字符串A称为模式串，字符串B称为主串。\n## BF算法 BF算法就是暴力匹配，即对主串从头开始慢慢移动模式串，直到找到相匹配的位置。 代码很简单暴力： 假设n为主串长度，m为模式串长度。\n每一轮字符串比较：最差的情况为模式串最后一个字与主串不同其他都相同（如模式串为AAB，主串对应部分为AAC），必须走完整个字符串才能得出结果，因此复杂度为O(m)。\n所有轮字符串比较：最差的情况是移动到最后一次比较才寻找得到，总共需要n-m+1次，主串通常比模式串长很多，故Brute-Force时间复杂度为O(nm)\n在匹配上没有办法进行优化，因此可以从模式串的移动上入手，由此引入了Kmp算法。\nKMP KMP 算法的不同之处在于，它会花费空间来记录一些信息。目的就是为了减少匹配的趟数，算法的核心就是每次匹配过程中推断出后续完全不可能匹配成功的匹配过程，从而减少比较的趟数。\nNext数组 next数组实质上就是找出模式串中前后字符重复出现的个数，为了能够跳跃不可能匹配的步骤。 next数组的定义为：next[i]表示模式串A[0]至A[i]这个字串，使得前k个字符等于后k个字符的最大值，特别的k不能取i+i,因为字串一共才i+1个字符，自己跟自己相等毫无意义。\n如何确定在移动过程中需要跳过多少步呢？下图更直观的体现了跳跃的过程： 也就是跳到模式串中的后缀相同字符串开始。因为这时可以确定前面的字符串肯定无法匹配了，过的趟数=匹配上字符串中间字符长度-重复字符串长度 在实际代码编写中，移动的实际上是模式串的匹配位置。前面展示的只是理解减小匹配次数这一过程。实际上跳动就是模式串比较指针的移动。模式串向右移动也就是比较指针向左移动，移动的距离就是跳过的趟数 移动后的指针为：\\(j=j-(j-next[j-1]) =next[j-1]\\) 其中原来的j为匹配成功的字符串长度，也就是匹配失败的指针位置。next[j-1]就是匹配成功的字符串的最长相同前后缀数，也就是要跳转到的指针的下标。（下标从0开始的，如果下标从1开始则在原来基础上+1就是要跳转到的位置，这也是为什么书上要+1）。\n即下图所示。 然后再用移动后的指针和主串的上一轮匹配错误的位置相比，这时已经保证了前面的字符已经匹配了，即当前指针前面的子串已经和上一轮匹配成功的子串的最长后缀相同了。\n因此可以看到，Kmp算法的核心就是构造Next数组。 最简单的方法就是根据定义暴力构造时间复杂度为O(m2法)：： 第二种构建方案，是一种递推的方式进行构建，时间复杂度为O(n+m):\n考虑：如果next[0], next[1], … next[x-1]均已知，那么如何求出 next[x] ？我们已经知道next[x-1],标记next[x-1]=temp,则可以讨论A[temp]和A[x]的值，分2种情况讨论： 第一种情况：A[temp]等于A[x]，也就是说在前一个next结果上又多了一个字符串相同的长度，因此next[x]为next[x-1]+1\n这种情况说明了x-1时最长前缀的后一位和x位置的字符相同，则说明了前缀和后缀都增加了相同的1位，next[x] = next[x-1] + 1。 （这里给一下我自己的理解，因为next中保存的是前后缀最大相同的长度，因此通常代表着最大相同前缀的后一位）\n第二种情况：当A[temp]和A[x]不相等的时候，我们需要缩小temp,把temp变成next[temp-1]，直到A[temp]=A[x]为止。A[now]=A[x]时，就可以直接向右扩展了。\n如何理解这张图，当发现A[temp] != A[x]的时候，就一直缩小temp，也就是temp = next[temp-1]，也就是找原来的最长前缀中对应的next[temp-1]，因为前缀中的相同前后缀最大长度（图中为2）肯定也与后缀中的相同前后缀最大长度（图中为2）相同，也说明了前缀中的前缀（图中为0和1上的A和B）等于后缀中的后缀（图中为10和11上的A和B），此时还是原来的思路temp为前缀中的前缀的下一位，判断是否与后缀中的后缀的下一位相同（就是当前的A[x]），如果相同就是第一种情况，next[x] = temp + 1，如果一直没找到则设为0，说明没有。\n学到这不得不感叹kmp三位大佬的恐怖。。 大体的思想就是这样，不过如果对于做题来说的话，有很多不一样的地方，比如王道书上的next数组是从下标1开始的，对于本文的内容，需要得到匹配失败位置-1的next数组值，因此书上将整体右移了一位，开头补上了-1，这样匹配失败的位置就是对应的next数组值。又下一次匹配要从最长前缀的下一位开始，而对于下标从0开始，next数组值就是下一位（前面解释了），而对于下标从1开始，需要再+1才是对应下标，因此也对next数组最后进行了+1操作。不管怎么样，next的含义都是最长前缀的下一个位置。主要思想都是一样的，随机应变即可。\n代码： class KMP: def __init__(self, text, pattern) -\u0026gt; None: self.text = text self.pattern = pattern self.next = [] def init_next(self): self.next.append(0) # first for i, s in enumerate(self.pattern[1:], 1): tmp = self.next[i-1] if s == self.pattern[tmp]: self.next.append(tmp + 1) else: while self.pattern[tmp] != s and tmp != 0: tmp = self.next[tmp-1] if tmp == 0 and self.pattern[tmp] != s: self.next.append(0) continue self.next.append(tmp + 1) def search(self): i = 0 # text index j = 0 # partten index while i \u0026lt; len(self.text): if self.text[i] == self.pattern[j]: i += 1 j += 1 elif j: j = self.next[j-1] else: i += 1 if j == len(self.pattern): return i - j return -1 kmp = KMP(\u0026quot;hello\u0026quot;, \u0026quot;ll\u0026quot;) kmp.init_next() print(kmp.search()) print(kmp.next) nextval 还有一种改进的next，因为匹配错误的位置重新移动指针后的位置的值可能与原来的值相同，因此在构建next数组的时候可以直接跳转到不相同的位置，这样就减小了重复的比较。 比如aaabaaaab和aaaab，当在主串中第一个b位置出错时，此时模式串为a，会比较str[next[2]]与b，但此时str[next[2]]也是a，再次比较也会错误，但再移动后发现还是a还是错误，这些比较都是没有意义的，最后要移动到模式串开头，然后将主串指针+1再比较，所以构建nextval减少了这些比较。 我是根据已经构建好的next数组推理来的。代码如下：\ndef create_nextval(self): length = len(self.pattern) self.nextval = [0] * length for i in range(1, len(self.next)): tmp = self.next[i-1] if self.pattern[tmp] != self.pattern[i]: self.nextval[i-1] = tmp else: while tmp and self.pattern[tmp] == self.pattern[i]: tmp = self.next[tmp-1] self.nextval[i-1] = tmp 因为用不到最后一位的数值，所以考研书上就直接全部右移一位，理论上都是相同的。原理就是匹配错误的位置的字符和要跳转到的位置的字符相同的话就要继续跳转直到等于0或者不相同为止。\n","date":"2023-03-08T00:00:00Z","permalink":"/kmp/","title":"KMP"},{"content":"特征选择是特征工程里的一个重要问题，其目标是寻找最优特征子集。特征选择能剔除不相关(irrelevant)或冗余(redundant )的特征，从而达到减少特征个数，提高模型精确度，减少运行时间的目的。另一方面，选取出真正相关的特征简化模型，协助理解数据产生的过程。并且常能听到“数据和特征决定了机器学习的上限，而模型和算法只是逼近这个上限而已”，由此可见其重要性。但是它几乎很少出现于机器学习书本里面的某一章。然而在机器学习方面的成功很大程度上在于如果使用特征工程。\n根据特征选择的形式，可分为三大类： - Filter(过滤法)：按照发散性或相关性对各个特征进行评分，设定阈值或者待选择特征的个数进行筛选 - Wrapper(包装法)：根据目标函数（往往是预测效果评分），每次选择若干特征，或者排除若干特征 - Embedded(嵌入法)：先使用某些机器学习的模型进行训练，得到各个特征的权值系数，根据系数从大到小选择特征（类似于Filter，只不过系数是通过训练得来的） ## 过滤式 基本想法是：分别对每个特征 \\(x_i\\) ，计算 \\(x_i\\) 相对于类别标签 y 的信息量 S(i) ，得到 n 个结果。然后将 n 个 S(i) 按照从大到小排序，输出前 k 个特征。显然，这样复杂度大大降低。那么关键的问题就是使用什么样的方法来度量 S(i) ，我们的目标是选取与 y 关联最密切的一些 特征\\(x_i\\) 。 - Pearson相关系数 - 卡方验证 - 互信息和最大信息系数 - 距离相关系数 - 方差选择法 ###\n包裹式 嵌入式 ","date":"2023-03-08T00:00:00Z","permalink":"/%E7%89%B9%E5%BE%81%E9%80%89%E6%8B%A9/","title":"特征选择"},{"content":"介绍 早停止（Early Stopping）是 当达到某种或某些条件时，认为模型已经收敛，结束模型训练，保存现有模型的一种手段。\n如何判断已经收敛？主要看以下几点： - 验证集上的Loss在模型多次迭代后，没有下降 - 验证集上的Loss开始上升。 这时就可以认为模型没有必要训练了，可以停止了，因为训练下去可能就会发生过拟合，所以早停法是一种防止模型过拟合的方法。\n代码 import numpy as np import torch import os class EarlyStopping: \u0026quot;\u0026quot;\u0026quot;Early stops the training if validation loss doesn\u0026#39;t improve after a given patience.\u0026quot;\u0026quot;\u0026quot; def __init__(self, save_path, patience=7, verbose=False, delta=0): \u0026quot;\u0026quot;\u0026quot; Args: save_path : 模型保存文件夹 patience (int): How long to wait after last time validation loss improved. Default: 7 verbose (bool): If True, prints a message for each validation loss improvement. Default: False delta (float): Minimum change in the monitored quantity to qualify as an improvement. Default: 0 \u0026quot;\u0026quot;\u0026quot; self.save_path = save_path self.patience = patience self.verbose = verbose self.counter = 0 self.best_score = None self.early_stop = False self.val_loss_min = np.Inf self.delta = delta def __call__(self, val_loss, model): score = -val_loss if self.best_score is None: self.best_score = score self.save_checkpoint(val_loss, model) elif score \u0026lt; self.best_score + self.delta: self.counter += 1 print(f\u0026#39;EarlyStopping counter: {self.counter} out of {self.patience}\u0026#39;) if self.counter \u0026gt;= self.patience: self.early_stop = True else: self.best_score = score self.save_checkpoint(val_loss, model) self.counter = 0 def save_checkpoint(self, val_loss, model): \u0026#39;\u0026#39;\u0026#39;Saves model when validation loss decrease.\u0026#39;\u0026#39;\u0026#39; if self.verbose: print(f\u0026#39;Validation loss decreased ({self.val_loss_min:.6f} --\u0026gt; {val_loss:.6f}). Saving model ...\u0026#39;) path = os.path.join(self.save_path, \u0026#39;best_network.pth\u0026#39;) torch.save(model.state_dict(), path) # 这里会存储迄今最优模型的参数 self.val_loss_min = val_loss ","date":"2023-03-06T00:00:00Z","permalink":"/early-stopping/","title":"early-stopping"},{"content":"Focal Loss Focal Loss主要是为了解决类别不平衡的问题，Focal Loss可以运用于二分类，也可以运用于多分类。下面以二分类为例：\n原始Loss 原始的二分类： 其中 所以： 很容易理解，因为CE就是softmax在二分类的形式，实际运算中只关注对应标签的概率，对于二分类，如果是负样本的话，预测概率小于0.5则说明预测正确，则对应的实际的概率应该为1-p。最大化概率，就是最大化Log概率，也就是最小化-log概率。\n什么是易分类样本 对于正样本，如果预测的结果总是在0.5以上，就是易分类样本，如果总是在0.5以下，则说明是难分类样本。 对于负样本，如果预测的结果总是在0.5以下，就是易分类样本，如果总是在0.5以上，则说明是难分类样本。 对应\\(p_t\\)来说，就是\\(p_t\u0026gt;0.5\\)为易分类，\\(p_t\u0026lt;0.5\\)为难分类。\ngamma参数 在模型训练的时候，我们更希望关注难分类的样本，因此focal loss在原始loss上增加了一项，对整体进行了衰减： 对于公式中的参数\\(\\gamma\\)，一般会选择2，对于易分类的样本，即\\(p_t\u0026gt;0.5\\)的样本，\\(1-p_t\\)则会小于0.5，则loss会衰减的更多，最终的损失就变的很小。而对于难分类的样本，loss会衰减的比较小，通过这种衰减的对比，则变相增加了模型对于难分类样本的权重。\nalpha参数 对于二分类任务，负样本的数量远远多于正样本，导致模型更多关注在负样本上，忽略正样本。因此在使用交叉熵损失的时候通常会增加一个平衡参数用来调节正负样本的比重。\n所以会增加一个平衡参数来调节正负样本的比重。 其实这就是balanced cross entropy，可以将它引入focal loss\n在式子中，\\(\\gamma\\)占据了主导地位，因此其实不用太在意\\(\\alpha\\)的数值。\n对于多分类 对于多分类任务，其实是一样的，因为如果一个类别的样本预测结果总是大于0.5，也说明它是易分类的，对于平衡因子，在实现的时候，可以提前设置好各类别的平衡因子，对于每一个类别都有一个对应的。\n为什么有效 focal loss从样本难易分类角度出发，解决样本非平衡带来的模型训练问题。\n直觉上来讲样本非平衡造成的问题就是样本数少的类别分类难度较高。因此从样本难易分类角度出发，使得loss聚焦于难分样本，解决了样本少的类别分类准确率不高的问题，当然难分样本不限于样本少的类别，也就是focal loss不仅仅解决了样本非平衡的问题，同样有助于模型的整体性能提高。\n思考 难分类样本与易分类样本其实是一个动态概念，也就是说 p 会随着训练过程而变化。原先易分类样本即 p大的样本，可能随着训练过程变化为难训练样本即p小的样本。\n上面讲到，由于Loss梯度中，难训练样本起主导作用，即参数的变化主要是朝着优化难训练样本的方向改变。当参数变化后，可能会使原先易训练的样本 p 发生变化，即可能变为难训练样本。当这种情况发生时，可能会造成模型收敛速度慢，正如苏剑林在他的文章中提到的那样。\n为了防止难易样本的频繁变化，应当选取小的学习率。 ## 代码 ### 二分类\nclass Focal_Loss(): \u0026quot;\u0026quot;\u0026quot; 二分类Focal Loss \u0026quot;\u0026quot;\u0026quot; def __init__(self,alpha=0.25,gamma=2): super(Focal_Loss,self).__init__() self.alpha=alpha self.gamma=gamma def forward(self,preds,labels): \u0026quot;\u0026quot;\u0026quot; preds:sigmoid的输出结果 labels：标签 \u0026quot;\u0026quot;\u0026quot; eps=1e-7 loss_1=-1*self.alpha*torch.pow((1-preds),self.gamma)*torch.log(preds+eps)*labels loss_0=-1*(1-self.alpha)*torch.pow(preds,self.gamma)*torch.log(1-preds+eps)*(1-labels) loss=loss_0+loss_1 return torch.mean(loss) 多分类 class Focal_Loss(): def __init__(self,weight,gamma=2): super(Focal_Loss,self).__init__() self.gamma=gamma self.weight=weight def forward(self,preds,labels): \u0026quot;\u0026quot;\u0026quot; preds:softmax输出结果 labels:真实值 \u0026quot;\u0026quot;\u0026quot; eps=1e-7 y_pred =preds.view((preds.size()[0],preds.size()[1],-1)) #B*C*H*W-\u0026gt;B*C*(H*W) target=labels.view(y_pred.size()) #B*C*H*W-\u0026gt;B*C*(H*W) ce=-1*torch.log(y_pred+eps)*target floss=torch.pow((1-y_pred),self.gamma)*ce floss=torch.mul(floss,self.weight) floss=torch.sum(floss,dim=1) return torch.mean(floss) 参考 https://zhuanlan.zhihu.com/p/266023273\n","date":"2023-03-06T00:00:00Z","permalink":"/focal-loss/","title":"focal loss"},{"content":"","date":"2023-03-06T00:00:00Z","permalink":"/%E6%9C%80%E5%A4%A7%E7%86%B5%E6%A8%A1%E5%9E%8B/","title":"最大熵模型"},{"content":"在训练开始的时候，如果学习率太高的话，可能会导致loss来回跳动，会导致无法收敛，因此在训练开始的时候就可以设置一个很小的learning rate，然后随着训练的批次增加，逐渐增大学习率，直到达到原本想要设置的学习率。\n关于warmup的好处，有： - 有助于减缓模型在初始阶段对mini-batch的提前过拟合现象，保持分布的平稳 - 有助于保持模型深层的稳定性。\nwarmup有助于网络的收敛，当达到预期的学习率时，之后的步骤就可以每固定批次进行学习率衰减，防止过拟合，以慢慢达到收敛。\n","date":"2023-03-05T00:00:00Z","permalink":"/warmup/","title":"warmup"},{"content":"神经网络会促使自身往正确标签和错误标签差值最大的方向学习，在训练数据较少，不足以表征所有的样本特征的情况下，会导致网络过拟合。因为onehot本身就是一个稀疏的向量，如果所有无关类别都为0的话，就可能会疏忽某些类别之间的联系。 具体的缺点有： - 真是标签与其它标签之间的关系被忽略了，很多有用的知识学不到了。 - 倾向于让模型更加武断，导致泛化性能差 - 面对有噪声的数据更容易收到影响。\nlabel smoothing可以解决上述问题，这是一种正则化策略，主要是通过soft one-hot来加入噪声，减少了真实样本标签的类别在计算损失函数时的权重，最终起到抑制过拟合的效果。\n增加label smoothing后真实的概率分布有如下改变： ","date":"2023-03-05T00:00:00Z","permalink":"/%E6%A0%87%E7%AD%BE%E5%B9%B3%E6%BB%91/","title":"标签平滑"},{"content":" 基本原则：快速试错。 小步试错，快速迭代 可以试试无脑的配置 实时打印一些结果 自动调参：网格搜索、random search、贝叶斯优化、 参数初始化 学习率warmup，慢慢增加，然后学习率衰减。 batch_size和lr 大的batchsize收敛到sharp minimum，而小的batchsize收敛到flat minimum，后者具有更好的泛化能力。两者的区别就在于变化的趋势，一个快一个慢，如下图，造成这个现象的主要原因是小的batchsize带来的噪声有助于逃离sharp minimum。\n大的batchsize性能下降是因为训练时间不够长，本质上并不少batchsize的问题，在同样的epochs下的参数更新变少了，因此需要更长的迭代次数。\n如果增加了学习率，那么batch size最好也跟着增加，这样收敛更稳定。 尽量使用大的学习率，因为很多研究都表明更大的学习率有利于提高泛化能力。如果真的要衰减，可以尝试其他办法，比如增加batch size，学习率对模型的收敛影响真的很大，慎重调整。 总结 学习率直接影响模型的收敛状态，batchsize则影响模型的泛化性能\n","date":"2023-03-02T00:00:00Z","permalink":"/%E8%B0%83%E5%8F%82%E6%8A%80%E5%B7%A7/","title":"调参技巧"},{"content":"Min-Max公式 $$ {} {(x,y) }U[{r{adv}}L(,x+r_{adv},y)]\n$$\n内部max是为了找到worst-case的扰动，也就是攻击，其中， \\(L\\)为损失函数， \\(\\mathbb{S}\\) 为扰动的范围空间。 外部min是为了基于该攻击方式，找到最鲁棒的模型参数，也就是防御，其中 \\(\\mathbb{D}\\) 是输入样本的分布。 简单理解就是在输入上进行梯度上升(增大loss)，在参数上进行梯度下降(减小loss) 加入扰动后的损失函数 $$ {} -P(y |x+r{adv};)\n\\[ 那扰动要如何计算呢？Goodfellow认为，**神经网络由于其线性的特点，很容易受到线性扰动的攻击。** # Fast Gradient Sign Method (FGSM) \\] r_{adv} = sgn(_{x}L(,x,y)) $$ # FGM ## 代码 代码来自[1]，注意的是一般扰动加在了embedding矩阵上，相当于x+r。\nimport torch class FGM(): def __init__(self, model): self.model = model self.backup = {} def attack(self, epsilon=1., emb_name=\u0026#39;emb.\u0026#39;): # emb_name为embedding矩阵参数对应名 for name, param in self.model.named_parameters(): if param.requires_grad and emb_name in name: self.backup[name] = param.data.clone() norm = torch.norm(param.grad) if norm != 0 and not torch.isnan(norm): r_at = epsilon * param.grad / norm param.data.add_(r_at) def restore(self, emb_name=\u0026#39;emb.\u0026#39;): for name, param in self.model.named_parameters(): if param.requires_grad and emb_name in name: assert name in self.backup param.data = self.backup[name] self.backup = {} 使用时：\n# 初始化 fgm = FGM(model) for batch_input, batch_label in data: # 正常训练 loss = model(batch_input, batch_label) loss.backward() # 反向传播，得到正常的grad # 对抗训练 fgm.attack() # 在embedding上添加对抗扰动 loss_adv = model(batch_input, batch_label) loss_adv.backward() # 反向传播，并在正常的grad基础上，累加对抗训练的梯度 fgm.restore() # 恢复embedding参数 # 梯度下降，更新参数 optimizer.step() model.zero_grad() 参考文献 【炼丹技巧】功守道：NLP中的对抗训练 + PyTorch实现 - 知乎 (zhihu.com) ","date":"2023-03-02T00:00:00Z","permalink":"/%E5%AF%B9%E6%8A%97%E8%AE%AD%E7%BB%83/","title":"对抗训练"},{"content":"数据不均衡 所谓的不平衡指的是不同类别的样本量差异非常大，或者少数样本代表了业务的关键数据（少量样本更重要），需要对少量样本的模式有很好的学习。样本类别分布不平衡主要出现在分类相关的建模问题上。样本类别分布不平衡从数据规模上可以分为大数据分布不平衡和小数据分布不平衡两种。\n大数据分布不均衡。这种情况下整体数据规模大，只是其中的少样本类的占比较少。但是从每个特征的分布来看，小样本也覆盖了大部分或全部的特征。例如拥有1000万条记录的数据集中，其中占比50万条的少数分类样本便于属于这种情况。 小数据分布不均衡。这种情况下整体数据规模小，并且占据少量样本比例的分类数量也少，这会导致特征分布的严重不平衡。例如拥有1000条数据样本的数据集中，其中占有10条样本的分类，其特征无论如何拟合也无法实现完整特征值的覆盖，此时属于严重的数据样本分布不均衡。 如果不同分类间的样本量差异达到超过10倍就需要引起警觉并考虑处理该问题，超过20倍就要一定要解决该问题。\n主要有三种解决方法： - 欠采样：在少量样本数量不影响模型训练的情况下，可通过对多数类样本欠采样，实现少数样本和多数样本均衡。 - 过采样：在少量样本数量不支持模型训练的情况下，可以通过对少数类样本过采样，实现少数样本和多数样本的均衡。 - 模型算法：通过引入有权重的模型算法，针对少量样本着重拟合，以提升对少量样本特征的学习。\n欠采样 通过减少分类中多数类样本的样本数量来实现样本均衡。通过欠采样，在保留少数类样本的同时，会丢失多数样本中的一些信息。经过欠采样，样本数量在变少。因此我个人并不倾向于这种方法。\n随机法 随机的删除一些多数类样本，使少数类样本和多数类样本数量达到均衡。\n原型生成 PG 算法主要是在原有样本的基础上生成新的样本来实现样本均衡，对多数类样本生成新的样本去替代原样本，使得样本数目减少, 具体做法如下： 原型选择 原理：从多数类样本中选取最具代表性的样本用于训练，主要是为了缓解随机欠采样中的信息丢失问题。 NearMiss 采用一些启发式的规则来选择样本，根据规则的不同可分为 3 类,通过设定 version 参数来确定： - NearMiss-1：选择到最近的 K 个少数类样本平均距离最近的多数类样本 - NearMiss-2：选择到最远的 K 个少数类样本平均距离最近的多数类样本 - 3: 对于每个少数类样本选择 K 个最近的多数类样本，目的是保证每个少数类样本都被多数类样本包围\n过采样 过采样（over-sampling）方法通过增加分类中少数的数量来实现样本均衡，最直接的方法是简单的复制少数类样本形成多条记录，这种方式可能导致样本特征少而可能出现过拟合的问题。经过改进的过抽样方法通过在少数类中加入随机噪声、干扰数据或者通过一定规则产生新的合成样本。\n随机复制 就是随机选择少量样本进行复制。\nSMOTE 在随机过采样的基础上，通过样本构造一方面降低了直接复制样本代理的过拟合的风险，另一方法实现了样本的均衡。比如样本构造方法 SMOTE（Synthetic minority over-sampling technique）及其衍生算法。\n模型算法 cost sensitive算法 focal loss 可以查看本博客focal loss内容Focal Loss\n","date":"2023-03-02T00:00:00Z","permalink":"/%E6%95%B0%E6%8D%AE%E4%B8%8D%E5%B9%B3%E8%A1%A1/","title":"数据不平衡"},{"content":"","date":"2023-03-01T00:00:00Z","permalink":"/xgboost/","title":"xgboost"},{"content":"简介 生成对抗网络（Generative Adversarial Network，简称GAN）是无监督学习的一种方法，通过让两个神经网络相互博弈的方式进行学习。\n大白话： 说就是生成对抗网络有一个生成网络和一个判别网络，假设有一个真实数据和一个生成网络生成的假数据，那么判别网络就是识别出这两种数据，判别网络努力分类成功，生成网络努力生成和真实数据相似的数据使判别网络分类不出来。这就是所说的相互博弈的方式。\n专业的话： 生成式对抗网络由生成器和判别器构成。生成对抗网络的核心目的是训练生成器。生成器的目的是生成与真实样本尽可能相似的“假样本”，判别器的目的是尽可能区分出给定样本是真实样本还是生成的“假样本”。二者目的相悖，在不断博弈的过程中相互提高，最终在判别器判别能力足够可靠的前提下仍无法区分给定样本是真实样本还是生成样本，从而我们说生成器能够生成“以假乱真”的样本。\n优化 优化目标 - 价值函数 (Value Function) \\(\\min_G \\max_D V(D, G)=\\mathbb{E}_{\\boldsymbol{x} \\sim p_{\\text {data }}(\\boldsymbol{x})}[\\log D(\\boldsymbol{x})]+\\mathbb{E}_{\\boldsymbol{z} \\sim p_{\\boldsymbol{z}}(\\boldsymbol{z})}[\\log (1-D(G(\\boldsymbol{z})))]\\) - 优化方式 - 生成器优化方向: 最小化价值函数 - 判别器优化方向: 最大化价值函数\n简单理解，判别器就是要使正确的样本分为1，生成的样本分为0，那么就是最大化价值函数。 生成器就是要以假乱真，使判别器判生成的样本为1，即最大化\\(D(G(z))\\)，就是最小化价值函数。\n这里的价值函数与交叉熵类似。真实样本为正例，生成样本为负例，一般都是最小化损失函数，因此需要做一下变形： 这个很好理解，与交叉熵一样。\n上面是判别器的优化，下面是生成器的优化： 总的来说，生成器得损失函数就是\n\\[ J^{(G)} = -\\frac{1}{2}E_z\\log D(G(z)) \\] 即“以假乱真”。\n","date":"2022-12-22T00:00:00Z","permalink":"/gan/","title":"GAN"},{"content":"线性判别分析LDA(Linear Discriminant Analysis) 线性判别分析，也就是LDA（与主题模型中的LDA区分开），现在常常用于数据的降维中，但从它的名字中可以看出来它也是一个分类的算法，而且属于硬分类，也就是结果不是概率，是具体的类别，一起学习一下吧。\n主要思想 类内方差小 类间方差大 推导 这里以二类为例，即只有两个类别。\n首先是投影，我们假定原来的数据是向量 \\(x\\)，那么顺着 $ w$ 方向的投影就是标量： \\[ z=w^T\\cdot x(=|w|\\cdot|x|\\cos\\theta) \\] 对第一点，相同类内部的样本更为接近，我们假设属于两类的试验样本数量分别是 \\(N_1\\)和 \\(N_2\\)，那么我们采用方差矩阵来表征每一个类内的总体分布，这里我们使用了协方差的定义，用 \\(S\\) 表示原数据的协方差：\n\\[ \\begin{aligned} C_1:Var_z[C_1]\u0026amp;=\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(z_i-\\bar{z_{c1}})(z_i-\\bar{z_{c1}})^T\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(w^Tx_i-\\frac{1}{N_1}\\sum\\limits_{j=1}^{N_1}w^Tx_j)(w^Tx_i-\\frac{1}{N_1}\\sum\\limits_{j=1}^{N_1}w^Tx_j)^T\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=w^T\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(x_i-\\bar{x_{c1}})(x_i-\\bar{x_{c1}})^Tw\\nonumber\\\\\\\\ =w^TS_1w\\\\\\\\\\\\\\\\ C_2:Var_z[C_2]\u0026amp;=\\frac{1}{N_2}\\sum\\limits_{i=1}^{N_2}(z_i-\\bar{z_{c2}})(z_i-\\bar{z_{c2}})^T\\nonumber\\\\\\\\ =w^TS_2w \\end{aligned} \\]\n所以类内距离为：\n\\[ \\begin{align} Var_z[C_1]+Var_z[C_2]=w^T(S_1+S_2)w \\end{align} \\]\n对于第二点，我们可以用两类的均值表示这个距离：\n\\[ \\begin{align} (\\bar{z_{c1}}-\\bar{z_{c2}})^2\u0026amp;=(\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}w^Tx_i-\\frac{1}{N_2}\\sum\\limits_{i=1}^{N_2}w^Tx_i)^2\\nonumber\\\\\\\\ \u0026amp;=(w^T(\\bar{x_{c1}}-\\bar{x_{c2}}))^2\\nonumber\\\\\\\\ \u0026amp;=w^T(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw \\end{align} \\]\n合这两点，由于协方差是一个矩阵，于是我们用将这两个值相除来得到我们的损失函数，并最大化这个值：\n\\[ \\begin{align} \\hat{w}=\\mathop{argmax}\\limits_wJ(w)\u0026amp;=\\mathop{argmax}\\limits_w\\frac{(\\bar{z_{c1}}-\\bar{z_{c2}})^2}{Var_z[C_1]+Var_z[C_2]}\\nonumber\\\\\\\\ \u0026amp;=\\mathop{argmax}\\limits_w\\frac{w^T(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw}{w^T(S_1+S_2)w}\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=\\mathop{argmax}\\limits_w\\frac{w^TS_bw}{w^TS_ww} \\end{align} \\]\n这样，我们就把损失函数和原数据集以及参数结合起来了。下面对这个损失函数求偏导，注意我们其实对w的绝对值没有任何要求，只对方向有要求，因此只要一个方程就可以求解了：\n\\[ \\begin{aligned} \u0026amp;\\frac{\\partial}{\\partial w}J(w)=2S_bw(w^TS_ww)^{-1}-2w^TS_bw(w^TS_ww)^{-2}S_ww=0\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;\\Longrightarrow S_bw(w^TS_ww)=(w^TS_bw)S_ww\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;\\Longrightarrow w\\propto S_w^{-1}S_bw=S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw\\propto S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}}) \\end{aligned} \\]\n也就是说最后我们的结果就是\\(w=S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}})\\) 可以归一化求得单位的w值。\n多类情况 前面的很容易类比二类的情况，现在的目标函数变成了：\n\\[ \\frac{W^TS_bW}{W^TS_wW} \\] 现在的问题就是这些都是矩阵，不能像上面那样直接优化，需要替换优化目标。\n\\[ \\underbrace{arg\\;max}_W\\;\\;J(W) = \\frac{\\prod\\limits_{diag}W^TS_bW}{\\prod\\limits_{diag}W^TS_wW} \\] 其中 \\(\\prod_{diag}A\\)为A的主对角线元素的乘积,W为\\(n \\times d\\)的矩阵，n为原来的维度，d为映射到超平面的维度，则最终的目标就变成了：\n\\[ J(W) = \\frac{\\prod\\limits_{i=1}^dw_i^TS_bw_i}{\\prod\\limits_{i=1}^dw_i^TS_ww_i} = \\prod\\limits_{i=1}^d\\frac{w_i^TS_bw_i}{w_i^TS_ww_i} \\] 根据广式瑞利商，最大值是矩阵\\(S_w^{-1}S_b\\)的最大特征值,最大的d个值的乘积就是矩阵的\\(S_w^{-1}S_b\\)最大的d个特征值的乘积,此时对应的矩阵\\(W\\)为这最大的d个特征值对应的特征向量张成的矩阵。\n总结 LDA是一种监督学习的降维技术，也就是说它的数据集的每个样本是有类别输出的。这点和PCA不同。PCA是不考虑样本类别输出的无监督降维技术。LDA的思想可以用一句话概括，就是“投影后类内方差最小，类间方差最大”。什么意思呢？ 我们要将数据在低维度上进行投影，投影后希望每一种类别数据的投影点尽可能的接近，而不同类别的数据的类别中心之间的距离尽可能的大。\n实际上LDA除了可以用于降维以外，还可以用于分类。一个常见的LDA分类基本思想是假设各个类别的样本数据符合高斯分布，这样利用LDA进行投影后，可以利用极大似然估计计算各个类别投影数据的均值和方差，进而得到该类别高斯分布的概率密度函数。当一个新的样本到来后，我们可以将它投影，然后将投影后的样本特征分别带入各个类别的高斯分布概率密度函数，计算它属于这个类别的概率，最大的概率对应的类别即为预测类别。\nLDA用于降维，和PCA有很多相同，也有很多不同的地方，因此值得好好的比较一下两者的降维异同点。\n首先我们看看相同点：\n1）两者均可以对数据进行降维。\n2）两者在降维时均使用了矩阵特征分解的思想。\n3）两者都假设数据符合高斯分布。\n我们接着看看不同点：\n1）LDA是有监督的降维方法，而PCA是无监督的降维方法\n2）LDA降维最多降到类别数k-1的维数，而PCA没有这个限制。\n3）LDA除了可以用于降维，还可以用于分类。\n4）LDA选择分类性能最好的投影方向，而PCA选择样本点投影具有最大方差的方向。\n代码 mean_list = [] for i in range(2): mean_list.append(np.mean(X_train[y_train==i], axis=0)) mean_list = np.array(mean_list) S_W = np.zeros((X_train.shape[1], X_train.shape[1])) # 类内散度矩阵 for c, mv in zip(range(2), mean_list): class_scatter = np.zeros((X_train.shape[1], X_train.shape[1])) for row in X_train[y_train==c]: row, mv = row.reshape(X_train.shape[1], -1), mv.reshape(X_train.shape[1], -1) class_scatter += (row-mv).dot((row-mv).T) S_W += class_scatter over_all_mean = np.mean(X_train, axis=0) S_B = np.zeros((X_train.shape[1], X_train.shape[1])) # 类间散度矩阵 for i, mean_vec in enumerate(mean_list): n = X_train[y_train==i, :].shape[0] mean_list_temp = mean_list[i, :].reshape(1, -1) over_all_mean = over_all_mean.reshape(X_train.shape[1], 1) S_B += n*(mean_vec-over_all_mean).dot((mean_vec-over_all_mean).T) eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B)) eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i]) for i in range(len(eig_vals))] eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True) # eigv_sum = sum(eig_vals) # for i, j in enumerate(eig_pairs): # print(\u0026#39;eigenvalue {0:}: {1:.2%}\u0026#39;.format(i + 1, (j[0] / eigv_sum).real)) # 根据百分比显示特征值，从而选取最大的n个特征值 W = np.hstack((eig_pairs[0][1].reshape(X_train.shape[1], 1), eig_pairs[1][1].reshape(X_train.shape[1], 1))) ","date":"2022-12-21T00:00:00Z","permalink":"/lda/","title":"LDA"},{"content":" image.png 其中\\(r_{t}\\)为reset门，用于重置上一step的状态。\\(z_{t}\\)为update门，用于得到当前step的状态。\n","date":"2022-12-04T00:00:00Z","permalink":"/gru/","title":"GRU"},{"content":"\n很简单，就是统计x中的数出现次数，返回结果的最大长度就是x中的最大值+1，idx为对应的数，值为出现的次数，没有出现的为0。\nx = np.array([7, 6, 2, 1, 4]) # 索引0出现了0次，索引1出现了1次......索引5出现了0次...... np.bincount(x) #输出结果为：array([0, 1, 1, 0, 1, 0, 1, 1]) weight这个参数也很好理解，x会被它加权，也就是说，如果值n发现在位置i，那么out[n] += weight[i]而不是out[n] += 1。所以weight必须和x等长。\nw = np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]) # 我们可以看到x中最大的数为4，因此bin的数量为5，那么它的索引值为0-\u0026gt;4 x = np.array([2, 1, 3, 4, 4, 3]) # 索引0 -\u0026gt; 0 # 索引1 -\u0026gt; w[1] = 0.5 # 索引2 -\u0026gt; w[0] = 0.3 # 索引3 -\u0026gt; w[2] + w[5] = 0.2 - 0.6 = -0.4 # 索引4 -\u0026gt; w[3] + w[4] = 0.7 + 1 = 1.7 np.bincount(x, weights=w) # 因此，输出结果为：array([ 0. , 0.5, 0.3, -0.4, 1.7]) 没出现的还是0，出现的要按照出现的地方的weight计算。\n","date":"2022-11-29T00:00:00Z","permalink":"/bincount/","title":"bincount"},{"content":"把数字翻译成字符串 题目： https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/\n思路： dp思想，不用管是什么字符，定义dp[i]为长度为i时 有多少个方法\n代码: class Solution: def translateNum(self, num: int) -\u0026gt; int: s = str(num) if len(s) \u0026lt; 2: return 1 dp = [0] * len(s) dp[0] = 1 dp[1] = 2 if int(s[0] + s[1]) \u0026lt; 26 else 1 for i in range(2,len(s)): dp[i] = dp[i-1] + dp[i-2] if int(s[i-1] + s[i]) \u0026lt; 26 and s[i-1] != \u0026#39;0\u0026#39; else dp[i-1] return dp[-1] 注意如果长度小于等于1 则直接返回1\n如果不是26个英文字母里面的 则dp[i] = dp[i-1] 说明方法次数并不改变\n注意有首位为0的情况 所以要int一下\n","date":"2022-11-17T00:00:00Z","permalink":"/%E6%8A%8A%E6%95%B0%E5%AD%97%E7%BF%BB%E8%AF%91%E6%88%90%E5%AD%97%E7%AC%A6%E4%B8%B2/","title":"把数字翻译成字符串"},{"content":"Seq2Seq （本文只介绍最原始的seq2seq，带有注意力在attention文章中）\nRNN 有关RNN\nSeq2Seq是典型的Encoder-decoder框架的模型，其中编码器和解码器都采用的RNN模型或者RNN模型的变体：GRU、LSTM等。\n一般的RNN模型有几种形式，分别为一对一、一对多、多对一、多对多。\n一对一： 就是一般的MLP，并不能称之为RNN模型 一对多: 典型的例子就是语音生成，比如输入某个值可以由程序生成一段音乐。 多对一： 最常见的文本分类或者情感分析就是这个模型架构 多对多： 序列标注、NER、分词，大多数标注任务就是用的这个模型架构。而Seq2Seq也属于多对多的任务，不过由于输入和输出的长度可能会不一样，因此采用encoder-decoder的框架，主要思想就是通过encoder将输入的信息编码，然后传入decoder再进行解码得到想要的结果。这常用于生成式的任务，比如说机器翻译、对话系统、文本摘要等等，都可以使用这个框架进行实现。著名的Transformer 就是使用的这个框架。 encoder-decoder 框架 encoder-decoder框架可以看作一种深度学习领域的研究模式，应用场景十分广泛，\n文本处理领域的Encoder-Decoder框架可以这么直观地去理解：可以把它看作适合处理由一个句子（或篇章）生成另外一个句子（或篇章）的通用处理模型。对于句子对\u0026lt;Source,Target\u0026gt;，我们的目标是给定输入句子Source，期待通过Encoder-Decoder框架来生成目标句子Target。Source和Target可以是同一种语言，也可以是两种不同的语言。而Source和Target分别由各自的单词序列构成。\n\\[ Source = \u0026lt;x_1, x_2, \\dots, x_m\u0026gt; \\\\\\\\ Target = \u0026lt;y_1, y_2, \\dots, y_n\u0026gt; \\]\nEncoder顾名思义就是对输入句子Source进行编码，将输入句子通过非线性变换转化为中间语义表示C：\n\\[ C = F(x_1, x_2, \\dots, x_m) \\]\n对于解码器Decoder来说，其任务是根据句子Source的中间语义表示C和之前已经生成的历史信息\\(y_1,y_2,\\dots, y_{i-1}\\)来生成i时刻要生成的单词\\(y_i\\)：\n\\[ y_i = G(C, y_1,y_2,\\dots,y_{i-1}) \\]\n每个yi都依次这么产生，那么看起来就是整个系统根据输入句子Source生成了目标句子Target。如果Source是中文句子，Target是英文句子，那么这就是解决机器翻译问题的Encoder-Decoder框架；如果Source是一篇文章，Target是概括性的几句描述语句，那么这是文本摘要的Encoder-Decoder框架；如果Source是一句问句，Target是一句回答，那么这是问答系统或者对话机器人的Encoder-Decoder框架。由此可见，在文本处理领域，Encoder-Decoder的应用领域相当广泛。\nEncoder-Decoder框架不仅仅在文本领域广泛使用，在语音识别、图像处理等领域也经常使用。对于“图像描述”任务来说，Encoder部分的输入是一副图片，Decoder的输出是能够描述图片语义内容的一句描述语。一般而言，文本处理和语音识别的Encoder部分通常采用RNN模型，图像处理的Encoder一般采用CNN模型。\nSeq2Seq模型 Seq2Seq模型是输出的长度不确定时采用的模型，这种情况一般是在机器翻译的任务中出现，将一句中文翻译成英文，那么这句英文的长度有可能会比中文短，也有可能会比中文长，所以输出的长度就不确定了。\n结构 seq2seq属于encoder-decoder结构的一种，这里看看常见的encoder-decoder结构，基本思想就是利用两个RNN，一个RNN作为encoder，另一个RNN作为decoder。encoder负责将输入序列压缩成指定长度的向量，这个向量就可以看成是这个序列的语义，这个过程称为编码，获取语义向量最简单的方式就是直接将最后一个输入的隐状态作为语义向量C。也可以对最后一个隐含状态做一个变换得到语义向量，还可以将输入序列的所有隐含状态做一个变换得到语义变量。\n而decoder则负责根据语义向量生成指定的序列，这个过程也称为解码，最简单的方式是将encoder得到的语义变量作为初始状态输入到decoder的RNN中，得到输出序列。可以看到上一时刻的输出会作为当前时刻的输入，而且其中语义向量C只作为初始状态参与运算，后面的运算都与语义向量C无关。\ndecoder处理方式还有另外一种，就是语义向量C参与了序列所有时刻的运算，如下图，上一时刻的输出仍然作为当前时刻的输入，但语义向量C会参与所有时刻的运算。 上面的这两种结构是我刚学的时候疑惑的一个地方，因为我在有的地方看到的代码是第一种结构的，而沐神的教程中的结构用的是第二种结构，其实这两种都是可以的。 ### 训练\n最主要的思路就是语言模型，因为在RNN中，每一个step的输出层的大小就是单词表的大小，要预测最大概率出现的那个词汇，即最大化最有可能是当前输出的单词所在神经元的概率，实际上就是一个多分类问题，使用softmax归一化表示概率。\nencoder就是简单的RNN系列模型，前文说的可以有多种方式计算语义向量c，并且c可以有两种方式参与到decoder的计算中。\ndecoder的主要训练方式就是将前一时刻的结果作为后一时刻的输入，也就是自回归。在训练中的体现是将语料进行错位训练，比如[“你好“ 世界”]，在训练中decoder中就是输入为[“S” ”你好“ “世界”]，而标签也就是实际值为[“你好” “世界” ”E“] ，按照这样的方式进行训练，训练过后，再进行测试，测试的过程就是严格按照前一时刻的输出作为后一时刻的输入，因为这时你也没有要输入的数据。也就是说训练时和测试时的decoder是不一样的。训练的时候我们有真实的数据，而预测的时候没有，只能自产自销，其实就是一个语言模型，叫做条件语言模型。这里引用两张图 既然是语言模型，可以用极大似然估计最大化输出序列的概率：\n\\[ \\begin{aligned} P(y_1,\\dots,y_{T} | x_1, \\dots x_T) = \\prod_{t=1}^TP(y_t|y_1 , \\dots y_{t-1}; c) \\end{aligned} \\] 在计算损失的时候，我们使用交叉熵作为损失函数，所以我们要找出这个V维向量中，正确预测对应的词的那一维的概率大小\\(\\hat{p}\\)，则这一步的损失就是它的负导数\\(-log(\\hat{p})\\)，将每一步的损失求和，即得到总体的损失函数：\n\\[ \\begin{aligned} J = -\\frac{1}{T}\\sum_{i}^Tlog(p(\\hat{y_i})) \\end{aligned} \\] 其中的\\(p(\\hat{y_i})\\)为时间t=i上的正确输出节点的概率值，即softmax值。 其中有三个特殊的标记，一个是S代表句子的开头，E代表句子的结尾，P代表Padding。 ## 束搜索(beam search)\n一般来说，用前一步的结果作为下一步的输出，这种方式就是贪心策略，但也存在问题，也就是说每一步最优并不是全局最优，改进的办法就是束搜索。思想很简单，每一步就是多选几个作为候选，最后综合考虑，选出最优的组合。是不是和HMM中的维特比算法很像呢？\n以下为束搜索的步骤： - 首先需要设定一个候选集的大小beam size=k。 - 每一步的开始，我们从每个当前输入对应的所有可能输出，计算每一条路的序列得分 - 保留序列得分最大的k个作为下一步的输入 - 不断重复以上步骤，直至结束，选择序列得分最大的那个序列作为最终结果。 其中序列得分为： \\[ score(y_1,y_2, \\dots y_t) = \\sum_{i=1}^t \\log P(y_i|y_1,y_2,\\dots y_{i-1};x) \\] 过程如图所示： 评价标准 BLUE指标 BLEU，全称是Bilingual Evaluation Understudy，它的主要思想是基于N-gram等特征来比较人工翻译和机器翻译结果的相似程度。 我们将BLEU定义为：\n\\[ \\exp \\left(\\min\\left(0,1-\\frac{len_{\\text {label }}}{len_{\\text {pred }}}\\right)\\right) \\prod_{n=1}^{k} p_{n}^{1 / 2^{n}} \\]\n长的 \\(n\\) 元语法。另外, 用 \\(p_{n}\\) 表示 \\(n\\) 元语法的精确度, 它是两个数量的比值：第一个是预测序 列与标签序列中匹配的 \\(n\\) 元语法的数量, 第二个是预测序列中 \\(n\\) 元语法的数量的比率。具体 地说, 给定标签序列 \\(A 、 B 、 C 、 D 、 E 、 F\\) 和预测序列 \\(A 、 B 、 B 、 C 、 D\\), 我们有 \\(p_{1}=4 / 5 、 p_{2}=3 / 4 、 p_{3}=1 / 3\\) 和 \\(p_{4}=0\\) 。 根据 (9.7.4)中BLEU的定义，当预测序列与标签序列完全相同时, BLEU为 1 。 此外, 由于 \\(n\\) 元语法越长则匹配难度越大, 所以BLEU为更长的 \\(n\\) 元语法的精确度分配更大的权重。具体 来说, 当 \\(p_{n}\\) 固定时, \\(p_{n}^{1 / 2^{n}}\\) 会随着 \\(n\\) 的增长而增加（原始论文使用 \\(p_{n}^{1 / n}\\) )。而且, 由于预测 的序列越短获得的 \\(p_{n}\\) 值越高, 所以 (9.7.4)中乘法项之前的系数用于惩罚较短的预测序列。 例如, 当 \\(k=2\\) 时，给定标签序列 \\(A 、 B 、 C 、 D 、 E 、 F\\) 和预测序列 \\(A 、 B\\) ，尽管 \\(p_{1}=p_{2}=1\\) ， 惩罚因子 \\(\\exp (1-6 / 2) \\approx 0.14\\) 会降低BLEU。\n代码 # code by Tae Hwan Jung @graykode import numpy as np import torch import torch.nn as nn # S: Symbol that shows starting of decoding input # E: Symbol that shows starting of decoding output # P: Symbol that will fill in blank sequence if current batch data size is short than time steps def make_batch(): input_batch, output_batch, target_batch = [], [], [] for seq in seq_data: for i in range(2): seq[i] = seq[i] + \u0026#39;P\u0026#39; * (n_step - len(seq[i])) input = [num_dic[n] for n in seq[0]] output = [num_dic[n] for n in (\u0026#39;S\u0026#39; + seq[1])] target = [num_dic[n] for n in (seq[1] + \u0026#39;E\u0026#39;)] input_batch.append(np.eye(n_class)[input]) output_batch.append(np.eye(n_class)[output]) target_batch.append(target) # not one-hot # make tensor return torch.FloatTensor(input_batch), torch.FloatTensor(output_batch), torch.LongTensor(target_batch) # make test batch def make_testbatch(input_word): input_batch, output_batch = [], [] input_w = input_word + \u0026#39;P\u0026#39; * (n_step - len(input_word)) input = [num_dic[n] for n in input_w] output = [num_dic[n] for n in \u0026#39;S\u0026#39; + \u0026#39;P\u0026#39; * n_step] input_batch = np.eye(n_class)[input] output_batch = np.eye(n_class)[output] return torch.FloatTensor(input_batch).unsqueeze(0), torch.FloatTensor(output_batch).unsqueeze(0) # Model class Seq2Seq(nn.Module): def __init__(self): super(Seq2Seq, self).__init__() self.enc_cell = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5) self.dec_cell = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5) self.fc = nn.Linear(n_hidden, n_class) def forward(self, enc_input, enc_hidden, dec_input): enc_input = enc_input.transpose(0, 1) # enc_input: [max_len(=n_step, time step), batch_size, n_class] dec_input = dec_input.transpose(0, 1) # dec_input: [max_len(=n_step, time step), batch_size, n_class] # enc_states : [num_layers(=1) * num_directions(=1), batch_size, n_hidden] _, enc_states = self.enc_cell(enc_input, enc_hidden) # outputs : [max_len+1(=6), batch_size, num_directions(=1) * n_hidden(=128)] outputs, _ = self.dec_cell(dec_input, enc_states) model = self.fc(outputs) # model : [max_len+1(=6), batch_size, n_class] return model if __name__ == \u0026#39;__main__\u0026#39;: n_step = 5 n_hidden = 128 char_arr = [c for c in \u0026#39;SEPabcdefghijklmnopqrstuvwxyz\u0026#39;] num_dic = {n: i for i, n in enumerate(char_arr)} seq_data = [[\u0026#39;man\u0026#39;, \u0026#39;women\u0026#39;], [\u0026#39;black\u0026#39;, \u0026#39;white\u0026#39;], [\u0026#39;king\u0026#39;, \u0026#39;queen\u0026#39;], [\u0026#39;girl\u0026#39;, \u0026#39;boy\u0026#39;], [\u0026#39;up\u0026#39;, \u0026#39;down\u0026#39;], [\u0026#39;high\u0026#39;, \u0026#39;low\u0026#39;]] n_class = len(num_dic) batch_size = len(seq_data) model = Seq2Seq() criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) input_batch, output_batch, target_batch = make_batch() for epoch in range(5000): # make hidden shape [num_layers * num_directions, batch_size, n_hidden] hidden = torch.zeros(1, batch_size, n_hidden) optimizer.zero_grad() # input_batch : [batch_size, max_len(=n_step, time step), n_class] # output_batch : [batch_size, max_len+1(=n_step, time step) (becase of \u0026#39;S\u0026#39; or \u0026#39;E\u0026#39;), n_class] # target_batch : [batch_size, max_len+1(=n_step, time step)], not one-hot output = model(input_batch, hidden, output_batch) # output : [max_len+1, batch_size, n_class] output = output.transpose(0, 1) # [batch_size, max_len+1(=6), n_class] loss = 0 for i in range(0, len(target_batch)): # output[i] : [max_len+1, n_class, target_batch[i] : max_len+1] loss += criterion(output[i], target_batch[i]) if (epoch + 1) % 1000 == 0: print(\u0026#39;Epoch:\u0026#39;, \u0026#39;%04d\u0026#39; % (epoch + 1), \u0026#39;cost =\u0026#39;, \u0026#39;{:.6f}\u0026#39;.format(loss)) loss.backward() optimizer.step() # Test def translate(word): input_batch, output_batch = make_testbatch(word) # make hidden shape [num_layers * num_directions, batch_size, n_hidden] hidden = torch.zeros(1, 1, n_hidden) output = model(input_batch, hidden, output_batch) # output : [max_len+1(=6), batch_size(=1), n_class] predict = output.data.max(2, keepdim=True)[1] # select n_class dimension decoded = [char_arr[i] for i in predict] end = decoded.index(\u0026#39;E\u0026#39;) translated = \u0026#39;\u0026#39;.join(decoded[:end]) return translated.replace(\u0026#39;P\u0026#39;, \u0026#39;\u0026#39;) print(\u0026#39;test\u0026#39;) print(\u0026#39;man -\u0026gt;\u0026#39;, translate(\u0026#39;man\u0026#39;)) print(\u0026#39;mans -\u0026gt;\u0026#39;, translate(\u0026#39;mans\u0026#39;)) print(\u0026#39;king -\u0026gt;\u0026#39;, translate(\u0026#39;king\u0026#39;)) print(\u0026#39;black -\u0026gt;\u0026#39;, translate(\u0026#39;black\u0026#39;)) print(\u0026#39;upp -\u0026gt;\u0026#39;, translate(\u0026#39;upp\u0026#39;)) ","date":"2022-11-09T00:00:00Z","permalink":"/seq2seq/","title":"seq2seq"},{"content":"DBSCAN属于密度聚类的一种。通常情形下，密度聚类算法从样 本密度的角度来考察样本之间的可连接性，并基于可连接样本不断扩展聚类簇 以获得最终的聚类结果。\nDBSCAN基于一组“邻域”参数\\((\\epsilon, Minpts)\\)来刻画样本分布的紧密程度，给定数据集\\(D=\\\\{x_1,x_2, \\dots,x_m \\\\}\\)，定义几个概念：\n\\(\\epsilon\\)-邻域：对\\(x_j\\in D\\)，其\\(\\epsilon\\)-邻域包含样本集D中与\\(x_j\\)的距离不大于\\(\\epsilon\\)的样本，即\\(N_{\\epsilon}(x_j) = \\\\{dist(x_i, x_j) \\leq \\epsilon\\\\}\\)。 核心对象 (core object): 若 \\(x_j\\) 的 \\(\\epsilon\\)-邻域至少包含 MinPts 个样本, 即 \\(\\left|N_\\epsilon\\left(\\boldsymbol{x}_j\\right)\\right| \\geqslant \\operatorname{MinPts}\\), 则 \\(\\boldsymbol{x}_j\\) 是一个核心对象; 密度直达(directly density-reachable): 若 \\(\\boldsymbol{x}_j\\) 位于 \\(\\boldsymbol{x}_i\\) 的 \\(\\epsilon\\)-邻域中, 且 \\(\\boldsymbol{x}_i\\) 是 核心对象, 则称 \\(\\boldsymbol{x}_j\\) 由 \\(\\boldsymbol{x}_i\\) 密度直达; 密度可达(density-reachable): 对 \\(\\boldsymbol{x}_i\\) 与 \\(\\boldsymbol{x}_j\\), 若存在样本序列 \\(\\boldsymbol{p}_1, \\boldsymbol{p}_2, \\ldots, \\boldsymbol{p}_n\\), 其中 \\(\\boldsymbol{p}_1=\\boldsymbol{x}_i, \\boldsymbol{p}_n=\\boldsymbol{x}_j\\) 且 \\(\\boldsymbol{p}_{i+1}\\) 由 \\(\\boldsymbol{p}_i\\) 密度直达, 则称 \\(\\boldsymbol{x}_j\\) 由 \\(\\boldsymbol{x}_i\\) 密度可达; 密度相连 (density-connected): 对 \\(\\boldsymbol{x}_i\\) 与 \\(\\boldsymbol{x}_j\\), 若存在 \\(\\boldsymbol{x}_k\\) 使得 \\(\\boldsymbol{x}_i\\) 与 \\(\\boldsymbol{x}_j\\) 均由 \\(\\boldsymbol{x}_k\\) 密度可达, 则称 \\(\\boldsymbol{x}_i\\) 与 \\(\\boldsymbol{x}_j\\) 密度相连. 既然是聚类，那就要定义簇的概念\n","date":"2022-11-03T00:00:00Z","permalink":"/dbscan/","title":"DBSCAN"},{"content":"参考：https://www.cnblogs.com/bill-h/p/14863262.html\n大家可能听说过用于宣传数据挖掘的一个案例:啤酒和尿布；据说是沃尔玛超市在分析顾客的购买记录时，发现许多客户购买啤酒的同时也会购买婴儿尿布，于是超市调整了啤酒和尿布的货架摆放，让这两个品类摆放在一起；结果这两个品类的销量都有明显的增长；分析原因是很多刚生小孩的男士在购买的啤酒时，会顺手带一些婴幼儿用品。\n不论这个案例是否是真实的，案例中分析顾客购买记录的方式就是关联规则分析法Association Rules。\n关联规则分析也被称为购物篮分析，用于分析数据集各项之间的关联关系。\n项集 item的集合，如集合{牛奶、麦片、糖}是一个3项集，可以认为是购买记录里物品的集合。\n频繁项集 顾名思义就是频繁出现的item项的集合。如何定义频繁呢？用比例来判定，关联规则中采用支持度和置信度两个概念来计算比例值\n支持度（support) 共同出现的项在整体项中的比例。以购买记录为例子，购买记录100条，如果商品A和B同时出现50条购买记录（即同时购买A和B的记录有50），那边A和B这个2项集的支持度为50%\n\\[ Support(A\\cap B) = \\frac{Freq(A\\cap B)}{N} \\]\n置信度（Confidence） 购买A后再购买B的条件概率，根据贝叶斯公式，可如下表示：\n\\[ Confidence = \\frac{Freq(A\\cap B)}{Freq(A)} \\]\n提升度 为了判断产生规则的实际价值，即使用规则后商品出现的次数是否高于商品单独出现的评率，提升度和衡量购买X对购买Y的概率的提升作用。如下公式可见，如果X和Y相互独立那么提升度为1，提升度越大，说明X-\u0026gt;Y的关联性越强\n","date":"2022-11-02T00:00:00Z","permalink":"/%E5%85%B3%E8%81%94%E8%A7%84%E5%88%99%E6%A6%82%E5%BF%B5/","title":"关联规则概念"},{"content":"字符串转换整数 (atoi) https://leetcode-cn.com/problems/string-to-integer-atoi/\n#重点是正则表达式 class Solution: def myAtoi(s: str): import re ss = re.findall(\u0026quot;^[\\+\\-]?\\d+\u0026quot;,s.strip()) res = int(*ss) if res \u0026gt; (231-1): res = (231-1) if res \u0026lt; -231: res = -231 return res WA了四次才整出来，太菜了，以为很简单，没有认真读题，要吸取教训。\n","date":"2022-10-26T00:00:00Z","permalink":"/%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BD%AC%E6%8D%A2%E6%95%B4%E6%95%B0-atoi/","title":"字符串转换整数 (atoi)"},{"content":"基础就是高斯混合模型，假设我们熟知的高斯分布的概率密度函数为\\(p(x\\mid \\mu, \\Sigma)\\)。则高斯混合分布为：\n\\[ p_{\\mathcal{M}}(\\boldsymbol{x})=\\sum_{i=1}^k \\alpha_i \\cdot p\\left(\\boldsymbol{x} \\mid \\boldsymbol{\\mu}_i, \\boldsymbol{\\Sigma}_i\\right) \\]\n分布共由 \\(k\\) 个混合成分组成, 每个混合成分对应一个高斯分布. 其中 \\(\\mu_i\\) 与 \\(\\Sigma_i\\) 是第 \\(i\\) 个高斯混合成分的参数, 而 \\(\\alpha_i\u0026gt;0\\) 为相应的 “混合系数” (mixture coefficient), \\(\\sum_{i=1}^k \\alpha_i=1\\)。 假设样本的生成过程由高斯混合分布给出: 首先, 根据 \\(\\alpha_1, \\alpha_2, \\ldots, \\alpha_k\\) 定义 的先验分布选择高斯混合成分, 其中 \\(\\alpha_i\\) 为选择第 \\(i\\) 个混合成分的概率; 然后, 根 据被选择的混合成分的概率密度函数进行采样, 从而生成相应的样本。\n聚类原理 如何利用高斯混合分布进行聚类？观察这个混合系数，思路就是有多少个混合的模型，就代表要聚多少类，对于给定数据集，可以定义\n\\[ \\gamma_{j k}= \\begin{cases}1, \u0026amp; \\text { 第 } j \\text { 个观测来自第 } k \\text { 个分模型 } \\\\\\\\ 0, \u0026amp; \\text { 否则 }\\end{cases} \\]\n则样本j的簇标记\\(\\lambda_j= \\underbrace{\\arg \\max}_{i\\in {1,2, \\dots ,k}} \\gamma_{jk}\\)\nEM算法 如何计算\\(\\gamma_{jk}\\)呢，如下式所示，其中\\(\\alpha_k\\)为混合系数，\\(\\theta_k\\)为第k个高斯分布的参数\n\\[ \\begin{aligned} \\hat{\\gamma}_{j k} \u0026amp;=E\\left(\\gamma_{j k} \\mid y, \\theta\\right)=P\\left(\\gamma_{j k}=1 \\mid y, \\theta\\right) \\\\\\\\ \u0026amp;=\\frac{P\\left(\\gamma_{j k}=1, y_j \\mid \\theta\\right)}{\\sum_{k=1}^K P\\left(\\gamma_{j k}=1, y_j \\mid \\theta\\right)} \\\\\\\\ \u0026amp;=\\frac{P\\left(y_j \\mid \\gamma_{j k}=1, \\theta\\right) P\\left(\\gamma_{j k}=1 \\mid \\theta\\right)}{\\sum_{k=1}^K P\\left(y_j \\mid \\gamma_{j k}=1, \\theta\\right) P\\left(\\gamma_{j k}=1 \\mid \\theta\\right)} \\\\\\\\ \u0026amp;=\\frac{\\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}{\\sum_{k=1}^K \\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}, \\quad j=1,2, \\cdots, N ; \\quad k=1,2, \\cdots, K \\end{aligned} \\]\n式子里的y其实就是观测样本。\n那么模型的参数要怎么估计呢，很显然可以使用EM算法，\\(\\gamma\\)为隐变量，其实我这里的叙述顺序是有问题的，其实是EM算法中求Q函数的过程中需要计算的一个值，详细的过程在本博客的EM算法里面。总之得到了\\(\\gamma_{jk}\\)后，就得到了Q函数:\n\\[ Q\\left(\\theta, \\theta^{(i)}\\right)=\\sum_{k=1}^K\\{n_k \\log \\alpha_k+\\sum_{j=1}^N \\hat{\\gamma}_{j k}\\left[\\log \\left(\\frac{1}{\\sqrt{2 \\pi}}\\right)-\\log \\sigma_k-\\frac{1}{2 \\sigma_k^2}\\left(y_j-\\mu_k\\right)^2\\right]\\} \\]\n极大似然估计Q函数就可以得到参数的下一轮估计值：\n\\[ \\theta^{(i+1)}=\\arg \\max_\\theta Q\\left(\\theta, \\theta^{(i)}\\right) \\]\n用 \\(\\hat{\\mu}_k, \\hat{\\sigma}_k^2\\) 及 \\(\\hat{\\alpha}_k, k=1,2, \\cdots, K\\), 表示 \\(\\theta^{(i+1)}\\) 的各参数。求 \\(\\hat{\\mu}_k, \\hat{\\sigma}_k^2\\) 只需分别对 \\(\\mu_k, \\sigma_k^2\\) 求偏导数并令其为 0 , 即可得到; 求 \\(\\hat{\\alpha}_k\\) 是在 \\(\\sum_{k=1}^K \\alpha_k=1\\) 条件 下求偏导数并令其为 0 得到的。结果如下:\n\\[ \\begin{gathered} \\hat{\\mu}_k=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k} y_j}{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}, \\quad k=1,2, \\cdots, K \\\\\\\\ \\hat{\\sigma}_k^2=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k}\\left(y_j-\\mu_k\\right)^2}{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}, \\quad k=1,2, \\cdots, K \\\\\\\\ \\hat{\\alpha}_k=\\frac{n_k}{N}=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}{N}, \\quad k=1,2, \\cdots, K \\end{gathered} \\]\n得到参数后，再进行新的一轮迭代，计算\\(\\gamma\\)值，如此反复。 算法收敛后，就可以对样本进行聚类，根据\\(\\lambda_j= \\underbrace{\\arg \\max}_{i\\in {1,2, \\dots ,k}} \\gamma_{jk}\\)可以得到每个样本的簇标记。具体的流程如下： 总结 高斯混合分布的形式就注定了它可以用来进行聚类，并且还有EM算法如此强大的数学工具进行模型参数的学习，高斯混合聚类与Kmeans都属于原型聚类。\n","date":"2022-10-25T00:00:00Z","permalink":"/%E9%AB%98%E6%96%AF%E6%B7%B7%E5%90%88%E8%81%9A%E7%B1%BB/","title":"高斯混合聚类"},{"content":"Tokenization技术 本文章主要说说NLP领域中的Tokenization技术，这是很基础的但也是很容易被忽视的一个步骤。在我接的单子中经常会有此类问题，并且都是外国学校的，说明外国学校还是比较注重这一块的基础的。 首先明确一个概念：token可以理解为一个符号，就代表一个语言单位，tokenize的意思就是把一个句子或语料分成token.\nword char 子词(subword) BPE BPE 是一种简单的数据压缩算法，它在 1994 年发表的文章“A New Algorithm for Data Compression”中被首次提出。下面的示例将解释 BPE。老规矩，我们先用一句话概括它的核心思想：\nBPE每一步都将最常见的一对相邻数据单位替换为该数据中没有出现过的一个新单位，反复迭代直到满足停止条件。\nBPE 确保最常见的词在token列表中表示为单个token，而罕见的词被分解为两个或多个subword tokens，因此BPE也是典型的基于subword的tokenization算法。\n合并字符可以让你用最少的token来表示语料库，这也是 BPE 算法的主要目标，即数据的压缩。为了合并，BPE 寻找最常出现的字节对。在这里，我们将字符视为与字节等价。当然，这只是英语的用法，其他语言可能有所不同。现在我们将最常见的字节对合并成一个token，并将它们添加到token列表中，并重新计算每个token出现的频率。这意味着我们的频率计数将在每个合并步骤后发生变化。我们将继续执行此合并步骤，直到达到我们预先设置的token数限制或迭代限制。\n算法过程 准备语料库，确定期望的 subword 词表大小等参数 通常在每个单词末尾添加后缀 ，统计每个单词出现的频率，例如，low 的频率为 5，那么我们将其改写为 “l o w ”：5 将语料库中所有单词拆分为单个字符，用所有单个字符建立最初的词典，并统计每个字符的频率，本阶段的 subword 的粒度是字符 挑出频次最高的符号对 ，比如说 t 和 h 组成的 th，将新字符加入词表，然后将语料中所有该字符对融合（merge），即所有 t 和 h 都变为 th。 重复上述操作，直到词表中单词数达到设定量 或下一个最高频数为 1 ，如果已经打到设定量，其余的词汇直接丢弃 例子 获取语料库，这样一段话为例：“ FloydHub is the fastest way to build, train and deploy deep learning models. Build deep learning models in the cloud. Train deep learning models. ”\n拆分，加后缀\u0026lt;/w\u0026gt; ，统计词频 建立词表，统计字符频率（顺便排个序）： 以第一次迭代为例，将字符频率最高的 d 和 e 替换为 de，后面依次迭代： 更新词表 继续迭代直到达到预设的 subwords 词表大小或下一个最高频的字节对出现频率为 1。 ### 优点 BPE 的优点就在于，可以很有效地平衡词典大小和编码步骤数（将语料编码所需要的 token 数量）。\n随着合并的次数增加，词表大小通常先增加后减小。迭代次数太小，大部分还是字母，没什么意义；迭代次数多，又重新变回了原来那几个词。所以词表大小要取一个中间值。\n适用范围 BPE 一般适用在欧美语言拉丁语系中，因为欧美语言大多是字符形式，涉及前缀、后缀的单词比较多。而中文的汉字一般不用 BPE 进行编码，因为中文是字无法进行拆分。对中文的处理通常只有分词和分字两种。理论上分词效果更好，更好的区别语义。分字效率高、简洁，因为常用的字不过 3000 字，词表更加简短。\n编码过程 BPE的总体思想就是利用替换字节对来逐步构造词汇表。在使用的过程有编码和解码两种。 在之前的算法中，我们已经得到了subword的词表，对该词表按照子词长度由大到小排序。编码时，对于每个单词，遍历排好序的子词词表寻找是否有token是当前单词的子字符串，如果有，则该token是表示单词的tokens之一。\n我们从最长的token迭代到最短的token，尝试将每个单词中的子字符串替换为token。 最终，我们将迭代所有tokens，并将所有子字符串替换为tokens。 如果仍然有子字符串没被替换但所有token都已迭代完毕，则将剩余的子词替换为特殊token，如\n编码的计算量很大。 在实践中，我们可以pre-tokenize所有单词，并在词典中保存单词tokenize的结果。 如果我们看到字典中不存在的未知单词。 我们应用上述编码方法对单词进行tokenize，然后将新单词的tokenization添加到字典中备用。\n代码 import re, collections def get_vocab(filename): vocab = collections.defaultdict(int) with open(filename, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as fhand: for line in fhand: words = line.strip().split() for word in words: vocab[\u0026#39; \u0026#39;.join(list(word)) + \u0026#39; \u0026lt;/w\u0026gt;\u0026#39;] += 1 return vocab def get_stats(vocab): # 构建字符对频数字典 pairs = collections.defaultdict(int) for word, freq in vocab.items(): symbols = word.split() for i in range(len(symbols)-1): pairs[symbols[i],symbols[i+1]] += freq return pairs def merge_vocab(pair, v_in): # 将频率最大的字符对替换 v_out = {} bigram = re.escape(\u0026#39; \u0026#39;.join(pair)) # 不转义 p = re.compile(r\u0026#39;(?\u0026lt;!\\S)\u0026#39; + bigram + r\u0026#39;(?!\\S)\u0026#39;) # 意思是bigram前面没有非空格字符，后面也没有非空格字符 for word in v_in: w_out = p.sub(\u0026#39;\u0026#39;.join(pair), word) v_out[w_out] = v_in[word] return v_out def get_tokens(vocab): tokens = collections.defaultdict(int) for word, freq in vocab.items(): word_tokens = word.split() for token in word_tokens: tokens[token] += freq return tokens vocab = {\u0026#39;l o w \u0026lt;/w\u0026gt;\u0026#39;: 5, \u0026#39;l o w e r \u0026lt;/w\u0026gt;\u0026#39;: 2, \u0026#39;n e w e s t \u0026lt;/w\u0026gt;\u0026#39;: 6, \u0026#39;w i d e s t \u0026lt;/w\u0026gt;\u0026#39;: 3} print(\u0026#39;==========\u0026#39;) print(\u0026#39;Tokens Before BPE\u0026#39;) tokens = get_tokens(vocab) print(\u0026#39;Tokens: {}\u0026#39;.format(tokens)) print(\u0026#39;Number of tokens: {}\u0026#39;.format(len(tokens))) print(\u0026#39;==========\u0026#39;) num_merges = 5 for i in range(num_merges): pairs = get_stats(vocab) if not pairs: break best = max(pairs, key=pairs.get) vocab = merge_vocab(best, vocab) print(\u0026#39;Iter: {}\u0026#39;.format(i)) print(\u0026#39;Best pair: {}\u0026#39;.format(best)) print(vocab) # tokens = get_tokens(vocab) # print(\u0026#39;Tokens: {}\u0026#39;.format(tokens)) wordpiece 构造 wordpiece 词表的构造与BPE很相似，都是选择两个子词合并成新的子词。 最大的区别在于，BPE是选择频数最高的相邻子词合并，而wordpiece选择能够提升语言模型概率最大的相邻子词加入词表。 如何理解？ 假设各个子词之间是独立存在的，则句子S的语言模型似然值等价于所有子词概率的乘积 假设把相邻位置的x和y两个子词进行合并，合并后产生的子词记为z，此时句子S似然值的变化可表示为： 从上面的公式，很容易发现，似然值的变化就是两个子词之间的互信息。简而言之，WordPiece每次选择合并的两个子词，他们具有最大的互信息值，也就是两子词在语言模型上具有较强的关联性，它们经常在语料中以相邻方式同时出现。 与BPE相似，通过以上方式构建词表。 ### 编码 1. 从第一个位置开始，由于是最长匹配，结束位置需要从最右端依次递减，所以遍历的第一个子词 是其本身 unaffable，该子词不在词汇表中 2. 结束位置左移一位得到子词 unaffabl，同样不在词汇表中 3. 重复这个操作，直到 un，该子词在词汇表中，将其加入 output_tokens，以第一个位置开始 的遍历结束 4. 跳过 un，从其后的 a 开始新一轮遍历，结束位置依然是从最右端依次递减，但此时需要在前 面加上 ## 标记，得到 ##affable 不在词汇表中 5. 结束位置左移一位得到子词 ##affabl，同样不在词汇表中 6. 重复这个操作，直到 ##aff，该字词在词汇表中， 将其加入 output_tokens，此轮遍历结束 7. 跳过 aff，从其后的 a 开始新一轮遍历，结束位置依然是从最右端依次递减。##able 在词汇 表中，将其加入 output_tokens 8. able 后没有字符了，整个遍历结束\n代码 (来自https://github.com/google-research/bert/blob/master/tokenization.py)\nclass WordpieceTokenizer(object): \u0026quot;\u0026quot;\u0026quot;Runs WordPiece tokenziation.\u0026quot;\u0026quot;\u0026quot; def __init__(self, vocab, unk_token=\u0026quot;[UNK]\u0026quot;, max_input_chars_per_word=200): self.vocab = vocab self.unk_token = unk_token self.max_input_chars_per_word = max_input_chars_per_word def tokenize(self, text): \u0026quot;\u0026quot;\u0026quot;Tokenizes a piece of text into its word pieces. This uses a greedy longest-match-first algorithm to perform tokenization using the given vocabulary. For example: input = \u0026quot;unaffable\u0026quot; output = [\u0026quot;un\u0026quot;, \u0026quot;##aff\u0026quot;, \u0026quot;##able\u0026quot;] Args: text: A single token or whitespace separated tokens. This should have already been passed through `BasicTokenizer. Returns: A list of wordpiece tokens. \u0026quot;\u0026quot;\u0026quot; text = convert_to_unicode(text) output_tokens = [] for token in whitespace_tokenize(text): chars = list(token) if len(chars) \u0026gt; self.max_input_chars_per_word: output_tokens.append(self.unk_token) continue is_bad = False start = 0 sub_tokens = [] while start \u0026lt; len(chars): end = len(chars) cur_substr = None while start \u0026lt; end: substr = \u0026quot;\u0026quot;.join(chars[start:end]) if start \u0026gt; 0: substr = \u0026quot;##\u0026quot; + substr if substr in self.vocab: cur_substr = substr break end -= 1 if cur_substr is None: is_bad = True break sub_tokens.append(cur_substr) start = end if is_bad: output_tokens.append(self.unk_token) else: output_tokens.extend(sub_tokens) return output_tokens Unigram Language Model 与WordPiece一样，Unigram Language Model(ULM)同样使用语言模型来挑选子词。不同之处在于，BPE和WordPiece算法的词表大小都是从小到大变化，属于增量法。而Unigram Language Model则是减量法,即先初始化一个大词表，根据评估准则不断丢弃词表，直到满足限定条件。ULM算法考虑了句子的不同分词可能，因而能够输出带概率的多个子词分段。\n初始时，建立一个足够大的词表(使用一些算法)。一般，可用语料中的所有字符加上常见的子字符串初始化词表，也可以通过BPE算法初始化。 针对当前词表，用EM算法求解每个子词在语料上的概率。 对于每个子词，计算当该子词被从词表中移除时，总的loss降低了多少，记为该子词的loss。 将子词按照loss大小进行排序，丢弃一定比例loss最小的子词(比如20%)，保留下来的子词生成新的词表。这里需要注意的是，单字符不能被丢弃，这是为了避免OOV情况。 重复步骤2到4，直到词表大小减少到设定范围。 可以看出，ULM会保留那些以较高频率出现在很多句子的分词结果中的子词，因为这些子词如果被丢弃，其损失会很大。 ### 代码\nencode代码 def encode_word(word, model): # word初步分词，model中为-log值 best_segmentations = [{\u0026quot;start\u0026quot;: 0, \u0026quot;score\u0026quot;: 1}] + [ {\u0026quot;start\u0026quot;: None, \u0026quot;score\u0026quot;: None} for _ in range(len(word)) ] for start_idx in range(len(word)): # This should be properly filled by the previous steps of the loop best_score_at_start = best_segmentations[start_idx][\u0026quot;score\u0026quot;] for end_idx in range(start_idx + 1, len(word) + 1): token = word[start_idx:end_idx] if token in model and best_score_at_start is not None: score = model[token] + best_score_at_start # 加上start，即前缀对应的损失，log中相加等于原来概率相乘 # If we have found a better segmentation ending at end_idx, we update if ( best_segmentations[end_idx][\u0026quot;score\u0026quot;] is None or best_segmentations[end_idx][\u0026quot;score\u0026quot;] \u0026gt; score # score即loss。越小越好 ): best_segmentations[end_idx] = {\u0026quot;start\u0026quot;: start_idx, \u0026quot;score\u0026quot;: score} print(token) print(best_segmentations[end_idx][\u0026quot;score\u0026quot;], score) print(best_segmentations) segmentation = best_segmentations[-1] if segmentation[\u0026quot;score\u0026quot;] is None: # We did not find a tokenization of the word -\u0026gt; unknown return [\u0026quot;\u0026lt;unk\u0026gt;\u0026quot;], None # 从后向前的最佳路径，即维特比算法 score = segmentation[\u0026quot;score\u0026quot;] start = segmentation[\u0026quot;start\u0026quot;] end = len(word) tokens = [] while start != 0: tokens.insert(0, word[start:end]) next_start = best_segmentations[start][\u0026quot;start\u0026quot;] end = start start = next_start tokens.insert(0, word[start:end]) return tokens, score 计算Loss def compute_loss(model): loss = 0 for word, freq in word_freqs.items(): _, word_loss = encode_word(word, model) loss += freq * word_loss return loss 计算score(用于删除token) import copy def compute_scores(model): scores = {} model_loss = compute_loss(model) for token, score in model.items(): # We always keep tokens of length 1 if len(token) == 1: continue model_without_token = copy.deepcopy(model) _ = model_without_token.pop(token) scores[token] = compute_loss(model_without_token) - model_loss return scores 参考 Unigram tokenization - Hugging Face NLP 课程\n","date":"2022-10-17T00:00:00Z","permalink":"/tokenization/","title":"tokenization"},{"content":"CoVe Cove代表上下文向量，它是一种有监督的预训练模型，其主要思想就是训练了一个NMT系统，并使用它的编码器，\n模型训练 主要假设是，为了翻译一个句子，NMT编码器学会理解句子。 因此来自编码器的向量包含有关单词上下文的信息。\n形式上，作者训练了一个带注意力的LSTM模型，比如Bahdanau Model，由于最终我们想使用经过训练的编码器来处理英文句子（不是因为我们只关心英文，而是因为下游任务的大多数数据集都是英文的），所以 NMT 系统必须从英文翻译成其他的语言（例如，德语）。\n双向编码器 请注意，在这个 NMT 模型中，编码器是双向的：它连接前向和后向 LSTM 的输出。因此，编码器输出包含有关令牌左右上下文的信息。\n获取表示(连接 GloVe 和 Cove 向量) 训练 NTM 模型后，我们只需要它的编码器。对于给定的文本，CoVe 向量是编码器的输出。对于下游任务，作者建议使用 Glove（代表单个令牌）和 CoVe（在上下文中编码的令牌）向量的串联。这个想法是这些向量编码不同类型的信息，它们的组合可能很有用。\n总结 其实思想很简单，就是将已经训练好的机器翻译模型的编码器作为编码，最后再与GloVe进行拼接，可以达到很好的效果，因为使用的是机器翻译模型，因此可以视为有监督学习，有监督的预训练模型是很少的，CoVe就是其中之一，其中更具体的分类可以看本博客的预训练模型。\n","date":"2022-10-09T00:00:00Z","permalink":"/cove/","title":"CoVe"},{"content":"EM算法 引入 我们经常会从样本观察数据中，找出样本的模型参数。 最常用的方法就是极大化模型分布的对数似然函数。（最大似然估计：利用已知的样本结果，反推最有可能导致这样结果的一组参数）但是在一些情况下，我们得到的观察数据有未观察到的隐含数据，此时我们未知的有隐含数据和模型参数，因而无法直接用极大化对数似然函数得到模型分布的参数。用EM算法可以解决。\nEM算法是一种迭代算法，用于含有隐变量的概率模型参数的极大似然估计，或极大后验概率估计。\nEM算法的每次迭代由两步组成：E步，求期望；M步，求极大。所以被称为期望极大算法。\nEM算法解决这个的思路是使用启发式的迭代方法，既然我们无法直接求出模型分布参数，那么我们可以先猜想隐含数据（EM算法的E步），接着基于观察数据和猜测的隐含数据一起来极大化对数似然，求解我们的模型参数（EM算法的M步)。由于我们之前的隐藏数据是猜测的，所以此时得到的模型参数一般还不是我们想要的结果。不过没关系，我们基于当前得到的模型参数，继续猜测隐含数据（EM算法的E步），然后继续极大化对数似然，求解我们的模型参数（EM算法的M步)。以此类推，不断的迭代下去，直到模型分布参数基本无变化，算法收敛，找到合适的模型参数。\n三硬币模型 首先介绍一个使用 EM算法的例子。 (三硬币模型) 假设有 3 枚硬币, 分别记作 A, B, C。这些硬币正面出现 的概率分别是 \\(\\pi, p\\) 和 \\(q\\) 。进行如下郑硬币试验: 先掷硬币 \\(\\mathrm{A}\\), 根据其结果选出硬币 \\(\\mathrm{B}\\) 或硬币 \\(\\mathrm{C}\\), 正面选硬币 \\(\\mathrm{B}\\), 反面选硬币 \\(\\mathrm{C}\\); 然后郑选出的硬币, 掷硬币的结果, 出现正 面记作 1 , 出现反面记作 0 ; 独立地重复 \\(n\\) 次试验 (这里, \\(n=10\\) ), 观测结果如下:\n\\[ 1,1,0,1,0,0,1,0,1,1 \\]\n假设只能观测到郑硬币的结果, 不能观测郑硬币的过程。问如何估计三硬币正面出现 的概率, 即三硬币模型的参数。\n解 三硬币模型可以写作\n\\[ \\begin{aligned} P(y \\mid \\theta) \u0026amp;=\\sum_z P(y, z \\mid \\theta)=\\sum_z P(z \\mid \\theta) P(y \\mid z, \\theta) \\\\\\\\ \u0026amp;=\\pi p^y(1-p)^{1-y}+(1-\\pi) q^y(1-q)^{1-y} \\end{aligned} \\]\n这里, 随机变量 \\(y\\) 是观测变量, 表示一次试验观测的结果是 1 或 0 ; 随机变量 \\(z\\) 是隐 变量, 表示末观测到的掷硬币 \\(\\mathrm{A}\\) 的结果; \\(\\theta=(\\pi, p, q)\\) 是模型参数。这一模型是以上数 据的生成模型。注意, 随机变量 \\(y\\) 的数据可以观测, 随机变量 \\(z\\) 的数据不可观测。 将观测数据表示为 \\(Y=\\left(Y_1, Y_2, \\cdots, Y_n\\right)^{\\mathrm{T}}\\), 末观测数据表示为 \\(Z=\\left(Z_1, Z_2, \\cdots, Z_n\\right)^{\\mathrm{T}}\\) 则观测数据的似然函数为\n\\[ P(Y \\mid \\theta)=\\sum_Z P(Z \\mid \\theta) P(Y \\mid Z, \\theta) \\]\n即\n\\[ P(Y \\mid \\theta)=\\prod_{j=1}^n\\left[\\pi p^{y_j}(1-p)^{1-y_j}+(1-\\pi) q^{y_j}(1-q)^{1-y_j}\\right] \\]\n考虑求模型参数 \\(\\theta=(\\pi, p, q)\\) 的极大似然估计, 即\n\\[ \\hat{\\theta}=\\arg \\max_\\theta \\log P(Y \\mid \\theta) \\]\n这个问题没有解析解, 只有通过迭代的方法求解。EM算法就是可以用于求解这 个问题的一种迭代算法。下面给出针对以上问题的 EM算法, 其推导过程省略。\nEM算法首先选取参数的初值, 记作 \\(\\theta^{(0)}=\\left(\\pi^{(0)}, p^{(0)}, q^{(0)}\\right)\\), 然后通过下面的 步骤迭代计算参数的估计值, 直至收敛为止。第 \\(i\\) 次迭代参数的估计值为 \\(\\theta^{(i)}=\\) \\(\\left(\\pi^{(i)}, p^{(i)}, q^{(i)}\\right)\\) 。EM算法的第 \\(i+1\\) 次迭代如下。 \\(\\mathrm{E}\\) 步：计算在模型参数 \\(\\pi^{(i)}, p^{(i)}, q^{(i)}\\) 下观测数据 \\(y_j\\) 来自郑硬币 \\(\\mathrm{B}\\) 的概率。这里就是使用的贝叶斯定理。\n\\[ \\mu_j^{(i+1)}=\\frac{\\pi^{(i)}\\left(p^{(i)}\\right)^{y_j}\\left(1-p^{(i)}\\right)^{1-y_j}}{\\pi^{(i)}\\left(p^{(i)}\\right)^{y_j}\\left(1-p^{(i)}\\right)^{1-y_j}+\\left(1-\\pi^{(i)}\\right)\\left(q^{(i)}\\right)^{y_j}\\left(1-q^{(i)}\\right)^{1-y_j}} \\]\n\\(\\mathrm{M}\\) 步：计算模型参数的新估计值\n\\[ \\pi^{(i+1)}=\\frac{1}{n} \\sum_{j=1}^n \\mu_j^{(i+1)} \\]\n\\[ \\begin{gathered} p^{(i+1)}=\\frac{\\sum_{j=1}^n \\mu_j^{(i+1)} y_j}{\\sum_{j=1}^n \\mu_j^{(i+1)}} \\\\\\\\ q^{(i+1)}=\\frac{\\sum_{j=1}^n\\left(1-\\mu_j^{(i+1)}\\right) y_j}{\\sum_{j=1}^n\\left(1-\\mu_j^{(i+1)}\\right)} \\end{gathered} \\]\n进行数值计算。假设模型参数的初值取为\n\\[ \\pi^{(0)}=0.5, \\quad p^{(0)}=0.5, \\quad q^{(0)}=0.5 \\]\n对 \\(y_j=1\\) 与 \\(y_j=0\\) 均有 \\(\\mu_j^{(1)}=0.5\\) 。 利用迭代公式, 得到\n\\[ \\pi^{(1)}=0.5, \\quad p^{(1)}=0.6, \\quad q^{(1)}=0.6 \\]\n\\[ \\mu_j^{(2)}=0.5, \\quad j=1,2, \\cdots, 10 \\]\n继续迭代, 得\n\\[ \\pi^{(2)}=0.5, \\quad p^{(2)}=0.6, \\quad q^{(2)}=0.6 \\]\n于是得到模型参数 \\(\\theta\\) 的极大似然估计:\n\\[ \\hat{\\pi}=0.5, \\quad \\hat{p}=0.6, \\quad \\hat{q}=0.6 \\]\n\\(\\pi=0.5\\) 表示硬币 A 是均匀的, 这一结果容易理解。\n如果取初值 \\(\\pi^{(0)}=0.4, p^{(0)}=0.6, q^{(0)}=0.7\\), 那么得到的模型参数的极大似然 估计是 \\(\\hat{\\pi}=0.4064, \\hat{p}=0.5368, \\hat{q}=0.6432\\) 。这就是说, EM算法与初值的选择有关, 选择不同的初值可能得到不同的参数估计值。\n一般地, 用 \\(Y\\) 表示观测随机变量的数据, \\(Z\\) 表示隐随机变量的数据。 \\(Y\\) 和 \\(Z\\) 连 在一起称为完全数据 (complete-data), 观测数据 \\(Y\\) 又称为不完全数据 (incompletedata）。假设给定观测数据 \\(Y\\), 其概率分布是 \\(P(Y \\mid \\theta)\\), 其中 \\(\\theta\\) 是需要估计的模型参数, 那么不完全数据 \\(Y\\) 的似然函数是 \\(P(Y \\mid \\theta)\\), 对数似然函数 \\(L(\\theta)=\\log P(Y \\mid \\theta)\\); 假设 \\(Y\\) 和 \\(Z\\) 的联合概率分布是 \\(P(Y, Z \\mid \\theta)\\), 那么完全数据的对数似然函数是 \\(\\log P(Y, Z \\mid \\theta)\\) 。\n算法步骤 输入: 观测变量数据 \\(Y\\), 隐变量数据 \\(Z\\), 联合分布 \\(P(Y, Z \\mid \\theta)\\), 条件分布 \\(P(Z \\mid Y, \\theta)\\); 输出：模型参数 \\(\\theta\\) 。 （1）选择参数的初值 \\(\\theta^{(0)}\\), 开始迭代; (2) \\(\\mathrm{E}\\) 步: 记 \\(\\theta^{(i)}\\) 为第 \\(i\\) 次迭代参数 \\(\\theta\\) 的估计值, 在第 \\(i+1\\) 次迭代的 \\(\\mathrm{E}\\) 步, 计算\n\\[ \\begin{aligned} Q\\left(\\theta, \\theta^{(i)}\\right) \u0026amp;=E_Z\\left[\\log P(Y, Z \\mid \\theta) \\mid Y, \\theta^{(i)}\\right] \\\\\\\\ \u0026amp;=\\sum_Z \\log P(Y, Z \\mid \\theta) P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\end{aligned} \\]\n这里, \\(P\\left(Z \\mid Y, \\theta^{(i)}\\right)\\) 是在给定观测数据 \\(Y\\) 和当前的参数估计 \\(\\theta^{(i)}\\) 下隐变量数据 \\(Z\\) 的条 件概率分布; (3) \\(\\mathrm{M}\\) 步：求使 \\(Q\\left(\\theta, \\theta^{(i)}\\right)\\) 极大化的 \\(\\theta\\), 确定第 \\(i+1\\) 次迭代的参数的估计值 \\(\\theta^{(i+1)}\\)\n\\[ \\theta^{(i+1)}=\\arg \\max_\\theta Q\\left(\\theta, \\theta^{(i)}\\right) \\]\n重复第 (2) 步和第 (3) 步, 直到收敛。 Q函数 函数 \\(Q\\left(\\theta, \\theta^{(i)}\\right)\\) 是 EM算法的核心, 称为 \\(Q\\) 函数 ( \\(Q\\) function)。 \\({Q}\\) 函数 : 完全数据 的对数似然函数 \\(\\log P(Y, Z \\mid \\theta)\\) 关于在给定观测数 据 \\(Y\\) 和当前参数 \\(\\theta^{(i)}\\) 下对未观测数据 \\(Z\\) 的条件概率分布 \\(P\\left(Z \\mid Y, \\theta^{(i)}\\right)\\) 的期望称为 \\(Q\\) 函数, 即\n\\[ Q\\left(\\theta, \\theta^{(i)}\\right)=E_Z\\left[\\log P(Y, Z \\mid \\theta) \\mid Y, \\theta^{(i)}\\right] = \\sum_Z \\log P(Y,Z\\mid \\theta) P\\left (Z\\mid Y, \\theta^{(i)}\\right) \\]\nJensen不等式 如果f是凸函数，X是随机变量，那么有\n\\[ E[f(X)] \\geq f[E(X)] \\]\n如果f是凹函数则相反 这个图可以比较清晰的看出这个结论。\nEM算法的导出 为什么 EM算法能近似实现对观测数据的极大似然估计 呢? 下面通过近似求解观测数据的对数似然函数的极大化问题来导出 EM算法, 由此 可以清楚地看出 EM算法的作用。\n我们面对一个含有隐变量的概率模型, 目标是极大化观测数据 (不完全数据) \\(Y\\) 关于参数 \\(\\theta\\) 的对数似然函数, 即极大化\n\\[ \\begin{aligned} L(\\theta) \u0026amp;=\\log P(Y \\mid \\theta)=\\log \\sum_Z P(Y, Z \\mid \\theta) \\\\\\\\ \u0026amp;=\\log \\left(\\sum_Z P(Y \\mid Z, \\theta) P(Z \\mid \\theta)\\right) \\end{aligned} \\]\n注意到这一极大化的主要困难是式中有末观测数据并有包含和 (或积分) 的 对数。 事实上, EM算法是通过迭代逐步近似极大化 \\(L(\\theta)\\) 的。假设在第 \\(i\\) 次迭代后 \\(\\theta\\) 的 估计值是 \\(\\theta^{(i)}\\) 。我们希望新估计值 \\(\\theta\\) 能使 \\(L(\\theta)\\) 增加, 即 \\(L(\\theta)\u0026gt;L\\left(\\theta^{(i)}\\right)\\), 并逐步达到极 大值。为此, 考虑两者的差:\n\\[ L(\\theta)-L\\left(\\theta^{(i)}\\right)=\\log \\left(\\sum_Z P(Y \\mid Z, \\theta) P(Z \\mid \\theta)\\right)-\\log P\\left(Y \\mid \\theta^{(i)}\\right) \\]\n利用 Jensen 不等式 (Jensen inequality)得到其下界，这里的f即为log函数，是凹函数，则结论与凸函数时的结论是相反的。:\n\\[ \\begin{aligned} L(\\theta)-L\\left(\\theta^{(i)}\\right) \u0026amp;=\\log \\left(\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\frac{P(Y \\mid Z, \\theta) P(Z \\mid \\theta)}{P\\left(Z \\mid Y, \\theta^{(i)}\\right)}\\right)-\\log P\\left(Y \\mid \\theta^{(i)}\\right) \\\\\\\\ \u0026amp; \\geqslant \\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log \\frac{P(Y \\mid Z, \\theta) P(Z \\mid \\theta)}{P\\left(Z \\mid Y, \\theta^{(i)}\\right)}-\\log P\\left(Y \\mid \\theta^{(i)}\\right) \\\\\\\\ \u0026amp;=\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log \\frac{P(Y \\mid Z, \\theta) P(Z \\mid \\theta)}{P\\left(Z \\mid Y, \\theta^{(i)}\\right) P\\left(Y \\mid \\theta^{(i)}\\right)} \\end{aligned} \\]\n令\n\\[ B\\left(\\theta, \\theta^{(i)}\\right) \\hat{=} L\\left(\\theta^{(i)}\\right)+\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log \\frac{P(Y \\mid Z, \\theta) P(Z \\mid \\theta)}{P\\left(Z \\mid Y, \\theta^{(i)}\\right) P\\left(Y \\mid \\theta^{(i)}\\right)} \\]\n则要求：\n\\[ L(\\theta) \\geqslant B\\left(\\theta, \\theta^{(i)}\\right) \\]\n即函数 \\(B\\left(\\theta, \\theta^{(i)}\\right)\\) 是 \\(L(\\theta)\\) 的一个下界, 可知,\n\\[ L\\left(\\theta^{(i)}\\right)=B\\left(\\theta^{(i)}, \\theta^{(i)}\\right) \\]\n因此, 任何可以使 \\(B\\left(\\theta, \\theta^{(i)}\\right)\\) 增大的 \\(\\theta\\), 也可以使 \\(L(\\theta)\\) 增大。这里回顾一下我们最原始的目标，就是为了最大化\\(L(\\theta)\\)，为了使 \\(L(\\theta)\\) 有尽可能大 的增长, 选择 \\(\\theta^{(i+1)}\\) 使 \\(B\\left(\\theta, \\theta^{(i)}\\right)\\) 达到极大, 即\n\\[ \\theta^{(i+1)}=\\arg \\max_\\theta B\\left(\\theta, \\theta^{(i)}\\right) \\]\n现在求 \\(\\theta^{(i+1)}\\) 的表达式。省去对 \\(\\theta\\) 的极大化而言是常数的项\n\\[ \\begin{aligned} \\theta^{(i+1)} \u0026amp;=\\arg \\max_\\theta\\left(L\\left(\\theta^{(i)}\\right)+\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log \\frac{P(Y \\mid Z, \\theta) P(Z \\mid \\theta)}{P\\left(Z \\mid Y, \\theta^{(i)}\\right) P\\left(Y \\mid \\theta^{(i)}\\right)}\\right) \\\\\\\\ \u0026amp;=\\arg \\max_\\theta\\left(\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log (P(Y \\mid Z, \\theta) P(Z \\mid \\theta))\\right) \\\\\\\\ \u0026amp;=\\arg \\max_\\theta\\left(\\sum_Z P\\left(Z \\mid Y, \\theta^{(i)}\\right) \\log P(Y, Z \\mid \\theta)\\right) \\\\\\\\ \u0026amp;=\\arg \\max_\\theta Q\\left(\\theta, \\theta^{(i)}\\right) \\end{aligned} \\]\n这等价于 EM算法的一次迭代, 即求 \\(Q\\) 函数及其极大化。EM算法是通过 不断求解下界的极大化逼近求解对数似然函数极大化的算法。 下图给出 EM算法的直观解释。注意两个曲线的交点就是在\\(\\theta^{(i)}\\)\n这里其实就是相当于推导为什么最大化Q函数对应的参数就是当前迭代的最佳参数。\n高斯混合模型 高斯混合模型是指有如下形式的概率分布模型：\n\\[ P(y\\mid \\theta ) = \\sum_{k=1}^K\\alpha_k \\Phi(y\\mid \\theta_k) \\]\n其中\\(\\alpha_k\\)为系数，\\(\\alpha_k \\geq 0, \\sum_{k=1}^K\\alpha_k=1\\); \\(\\Phi(y\\mid \\theta_k)\\)为高斯密度函数，\\(\\theta_k=(\\mu_k,\\sigma_k^2)\\)\n\\[ \\Phi(y\\mid \\theta_k) = \\frac{1}{\\sqrt{2\\pi}\\sigma_k}\\exp \\left(-\\frac{(y-\\mu_k)^2}{2\\sigma_k^2} \\right) \\]\n为第k个模型。\nEM算法的应用 明确隐变量, 写出完全数据的对数似然函数 可以设想观测数据 \\(y_j, j=1,2, \\cdots, N\\), 是这样产生的: 首先依概率 \\(\\alpha_k\\) 选择第 \\(k\\) 个高斯分布分模型 \\(\\phi\\left(y \\mid \\theta_k\\right)\\), 然后依第 \\(k\\) 个分模型的概率分布 \\(\\phi\\left(y \\mid \\theta_k\\right)\\) 生成观测数据 \\(y_j\\) 。这时观测数据 \\(y_j, j=1,2, \\cdots, N\\), 是已知的; 反映观测数据 \\(y_j\\) 来自第 \\(k\\) 个分模 型的数据是末知的, \\(k=1,2, \\cdots, K\\), 以隐变量 \\(\\gamma_{j k}\\) 表示, 其定义如下: \\[ \\gamma_{j k}= \\begin{cases}1, \u0026amp; \\text { 第 } j \\text { 个观测来自第 } k \\text { 个分模型 } \\\\\\\\ 0, \u0026amp; \\text { 否则 }\\end{cases} \\]\n\\[ j=1,2, \\cdots, N ; \\quad k=1,2, \\cdots, K \\]\n\\(\\gamma_{j k}\\) 是 0-1 随机变量。 有了观测数据 \\(y_j\\) 及末观测数据 \\(\\gamma_{j k}\\), 那么完全数据是\n\\[ \\left(y_j, \\gamma_{j 1}, \\gamma_{j 2}, \\cdots, \\gamma_{j K}\\right), \\quad j=1,2, \\cdots, N \\]\n于是, 可以写出完全数据的似然函数:\n\\[ \\begin{aligned} P(y, \\gamma \\mid \\theta) \u0026amp;=\\prod_{j=1}^N P\\left(y_j, \\gamma_{j 1}, \\gamma_{j 2}, \\cdots, \\gamma_{j K} \\mid \\theta\\right) \\\\\\\\ \u0026amp;=\\prod_{k=1}^K \\prod_{j=1}^N\\left[\\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)\\right]^{\\gamma_{j k}} \\\\\\\\ \u0026amp;=\\prod_{k=1}^K \\alpha_k^{n_k} \\prod_{j=1}^N\\left[\\phi\\left(y_j \\mid \\theta_k\\right)\\right]^{\\gamma_{j k}} \\\\\\\\ \u0026amp;=\\prod_{k=1}^K \\alpha_k^{n_k} \\prod_{j=1}^N\\left[\\frac{1}{\\sqrt{2 \\pi} \\sigma_k} \\exp \\left(-\\frac{\\left(y_j-\\mu_k\\right)^2}{2 \\sigma_k^2}\\right)\\right]^{\\gamma_{j k}} \\end{aligned} \\]\n式中, \\(n_k=\\sum_{j=1}^N \\gamma_{j k}, \\sum_{k=1}^K n_k=N\\) 。 那么, 完全数据的对数似然函数为\n\\[ \\log P(y, \\gamma \\mid \\theta)=\\sum_{k=1}^K\\left\\\\{n_k \\log \\alpha_k+\\sum_{j=1}^N \\gamma_{j k}\\left[\\log \\left(\\frac{1}{\\sqrt{2 \\pi}}\\right)-\\log \\sigma_k-\\frac{1}{2 \\sigma_k^2}\\left(y_j-\\mu_k\\right)^2\\right]\\right\\} \\]\nEM 算法的 \\(\\mathrm{E}\\) 步: 确定 \\(Q\\) 函数 \\[ \\begin{aligned} Q\\left(\\theta, \\theta^{(i)}\\right) \u0026amp;=E\\left[\\log P(y, \\gamma \\mid \\theta) \\mid y, \\theta^{(i)}\\right] \\\\\\\\ \u0026amp;=E\\left{\\sum_{k=1}^K\\left{n_k \\log \\alpha_k+\\sum_{j=1}^N \\gamma_{j k}\\left[\\log \\left(\\frac{1}{\\sqrt{2 \\pi}}\\right)-\\log \\sigma_k-\\frac{1}{2 \\sigma_k^2}\\left(y_j-\\mu_k\\right)^2\\right]\\right}\\right} \\\\\\\\ \u0026amp;=\\sum_{k=1}^K\\left\\\\{\\sum_{j=1}^N\\left(E \\gamma_{j k}\\right) \\log \\alpha_k+\\sum_{j=1}^N\\left(E \\gamma_{j k}\\right)\\left[\\log \\left(\\frac{1}{\\sqrt{2 \\pi}}\\right)-\\log \\sigma_k-\\frac{1}{2 \\sigma_k^2}\\left(y_j-\\mu_k\\right)^2\\right]\\right} \\end{aligned} \\]\n这里需要计算 \\(E\\left(\\gamma_{j k} \\mid y, \\theta\\right)\\), 记为 \\(\\hat{\\gamma}_{j k}\\) 。\n\\[ \\begin{aligned} \\hat{\\gamma}_{j k} \u0026amp;=E\\left(\\gamma_{j k} \\mid y, \\theta\\right)=P\\left(\\gamma_{j k}=1 \\mid y, \\theta\\right) \\\\\\\\ \u0026amp;=\\frac{P\\left(\\gamma_{j k}=1, y_j \\mid \\theta\\right)}{\\sum_{k=1}^K P\\left(\\gamma_{j k}=1, y_j \\mid \\theta\\right)} \\\\\\\\ \u0026amp;=\\frac{P\\left(y_j \\mid \\gamma_{j k}=1, \\theta\\right) P\\left(\\gamma_{j k}=1 \\mid \\theta\\right)}{\\sum_{k=1}^K P\\left(y_j \\mid \\gamma_{j k}=1, \\theta\\right) P\\left(\\gamma_{j k}=1 \\mid \\theta\\right)} \\\\\\\\ \u0026amp;=\\frac{\\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}{\\sum_{k=1}^K \\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}, \\quad j=1,2, \\cdots, N ; \\quad k=1,2, \\cdots, K \\end{aligned} \\]\n\\(\\hat{\\gamma}_{j k}\\) 是在当前模型参数下第 \\(j\\) 个观测数据来自第 \\(k\\) 个分模型的概率, 称为分模型 \\(k\\) 对 观测数据 \\(y_j\\) 的响应度。 将 \\(\\hat{\\gamma}_{j k}=E \\gamma_{j k}\\) 及 \\(n_k=\\sum_{j=1}^N E \\gamma_{j k}\\) 代入, 即得\n\\[ Q\\left(\\theta, \\theta^{(i)}\\right)=\\sum_{k=1}^K\\left\\\\{n_k \\log \\alpha_k+\\sum_{j=1}^N \\hat{\\gamma}_{j k}\\left[\\log \\left(\\frac{1}{\\sqrt{2 \\pi}}\\right)-\\log \\sigma_k-\\frac{1}{2 \\sigma_k^2}\\left(y_j-\\mu_k\\right)^2\\right]\\right\\\\\\} \\]\n确定 EM 算法的 \\(M\\) 步 迭代的 \\(\\mathrm{M}\\) 步是求函数 \\(Q\\left(\\theta, \\theta^{(i)}\\right)\\) 对 \\(\\theta\\) 的极大值, 即求新一轮迭代的模型参数: \\[ \\theta^{(i+1)}=\\arg \\max_\\theta Q\\left(\\theta, \\theta^{(i)}\\right) \\]\n用 \\(\\hat{\\mu}_k, \\hat{\\sigma}_k^2\\) 及 \\(\\hat{\\alpha}_k, k=1,2, \\cdots, K\\), 表示 \\(\\theta^{(i+1)}\\) 的各参数。求 \\(\\hat{\\mu}_k, \\hat{\\sigma}_k^2\\) 只需分别对 \\(\\mu_k, \\sigma_k^2\\) 求偏导数并令其为 0 , 即可得到; 求 \\(\\hat{\\alpha}_k\\) 是在 \\(\\sum_{k=1}^K \\alpha_k=1\\) 条件 下求偏导数并令其为 0 得到的。结果如下:\n\\[ \\begin{gathered} \\hat{\\mu}_k=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k} y_j}{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}, \\quad k=1,2, \\cdots, K \\\\\\\\ \\hat{\\sigma}_k^2=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k}\\left(y_j-\\mu_k\\right)^2}{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}, \\quad k=1,2, \\cdots, K \\\\\\\\ \\hat{\\alpha}_k=\\frac{n_k}{N}=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}{N}, \\quad k=1,2, \\cdots, K \\end{gathered} \\]\n重复以上计算, 直到对数似然函数值不再有明显的变化为止。 现将估计高斯混合模型参数的 EM算法总结如下。\n算法应用总结 (高斯混合模型参数估计的EM算法) 输入: 观测数据 \\(y_1, y_2, \\cdots, y_N\\), 高斯混合模型; 输出：高斯混合模型参数。 （1）取参数的初始值开始迭代; (2) \\(\\mathrm{E}\\) 步: 依据当前模型参数, 计算分模型 \\(k\\) 对观测数据 \\(y_j\\) 的响应度\n\\[ \\hat{\\gamma}_{j k}=\\frac{\\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}{\\sum_{k=1}^K \\alpha_k \\phi\\left(y_j \\mid \\theta_k\\right)}, \\quad j=1,2, \\cdots, N ; \\quad k=1,2, \\cdots, K \\]\n\\(\\mathrm{M}\\) 步：计算新一轮迭代的模型参数 \\[ \\hat{\\mu}_k=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{j k} y_j}{\\sum_{j=1}^N \\hat{\\gamma}_{j k}}, \\quad k=1,2, \\cdots, K \\]\n\\[ \\hat{\\sigma}_k^2=\\frac{\\sum_{j=1}^N \\hat{\\gamma}_{jk}(y_j-\\mu_k)^2}{\\sum_{j=1}^N \\hat{\\gamma}_{jk}}, \\quad k= 1,2,\\dots, K \\]\n\\[ \\hat{\\alpha}_k = \\frac{\\sum_{j=1}^N \\hat{\\gamma}_{jk}}{N} ,\\quad k=1,2,\\dots, K \\]\n重复直到收敛。 总结 总之来说em算法作为数据挖掘十大算法之一，应用范围十分广泛，它不能看作是一个具体的模型，常常用于模型的求解，比如HMM的学习参数问题等等，是必须要学会的算法之一。\n","date":"2022-10-03T00:00:00Z","permalink":"/em%E7%AE%97%E6%B3%95/","title":"EM算法"},{"content":"分发饼干 https://leetcode-cn.com/problems/assign-cookies/\nclass Solution: def findContentChildren(g, s) -\u0026gt; int: g = sorted(g) s = sorted(s) n = 0 for i in range(len(s)): if g[n] \u0026lt;= s[i]: n += 1 if n == len(g): return n return n 贪心算法的题目，考虑局部最优\n","date":"2022-10-03T00:00:00Z","permalink":"/%E5%88%86%E5%8F%91%E9%A5%BC%E5%B9%B2/","title":"分发饼干"},{"content":"正则表达式 [abcd]匹配中括号里的所有字符\n[^abcd]匹配除了括号里的所有字符\n[A-Za-z]匹配所有字母\n[\\s\\S]是匹配所有空白符，包括换行，非空白符，包括换行\n[\\w] 匹配字母、数字、下划线。等价于 [A-Za-z0-9_]\n匹配有特殊含义的，比如* ^ 等 记得要加上反斜杠进行转义\n？匹配前面的表达式0次或1次\n**.匹配除*\n+匹配前面的表达式1次或多次\n*匹配前面的表达式0次或多次\n{n}匹配n次，{n,m}最少匹配n次，最多m次，{n,}至少匹配n次\n定位符：^ $ 即限定在哪里匹配\n（）灵活应用\n[.]等价于\\.\n运算优先级 image.png 零宽度断言（前后预查） ?=…正先行断言 ?=...表示正先行断言，表示第一部分表达式之后必须跟着?=...定义的表达式。\n返回结果只包含满足匹配条件的第一部分表达式。 定义一个正先行断言要使用 ()。在括号内部使用一个问号和等号： (?=...)。\n正先行断言的内容写在括号中的等号后面。 例如，表达式 (T|t)he(?=\\sfat) 匹配 The 和 the，在括号中我们又定义了正先行断言 (?=\\sfat) ，即 The 和 the 后面紧跟着 (空格)fat。\n?!… 负先行断言 负先行断言 ?! 用于筛选所有匹配结果，筛选条件为 其后不跟随着断言中定义的格式。 正先行断言 定义和 负先行断言 正好相反。\n表达式 (T|t)he(?!\\sfat) 匹配 The 和 the，且其后不跟着 (空格)fat。 ### ?\u0026lt;=… 正后发断言\n正后发断言 记作(?\u0026lt;=...) 用于筛选所有匹配结果，筛选条件为 其前跟随着断言中定义的格式。 例如，表达式 (?\u0026lt;=(T|t)he\\s)(fat|mat) 匹配 fat 和 mat，且其前跟着 The 或 the。 ### ?\u0026lt;!… 负后发断言 负后发断言 记作 (?\u0026lt;!...) 用于筛选所有匹配结果，筛选条件为 其前不跟随着断言中定义的格式。 例如，表达式 (?\u0026lt;!(T|t)he\\s)(cat) 匹配 cat，且其前不跟着 The 或 the。\n此外还有一种是?:，用于括号匹配中，即non-capturing group， # 实战\nMatching a decimal numbers 来源:https://regexone.com/problem/matching_decimal_numbers\n意思就是要匹配Match的还要避开Skip的\n具体实现：\n^-?\\d+(,\\d+)*(\\.\\d+(e\\d+)?)?$\n解释：\n​ 开头匹配负号，其实这里可以变成[\\\\-\\\\+]? 以防出现正号\n​ 然后匹配带逗号的数字，匹配0次或多次\n​ 然后匹配带小数点的数字，匹配0次或1次，因为小数点最多出现一次\n​ 显然后面跟着匹配指数，显然指数也最多出现一次。\n","date":"2022-09-28T00:00:00Z","permalink":"/regex/","title":"regex"},{"content":"pandas实践1 在读取数据之前，我修改了表格里面的表头，以便程序的编写。\n先从 excel 读取数据,然后看看 shape 了解行数列数,然后调用 info 方法， 看看有没有缺失值，发现并没有缺失值，但题目里说了可能有重复或者格式 不对的数据，因为最主要的是学号,一般学号的长度都是 12 个数字，所以筛 选出不是 12 位数的\ndata[data['studentid'].apply(lambda x:len(x)!=12)]\n考虑到可能出现中文的情况，先尝试转化为整数试试\ndata[‘studentid’] = data[‘studentid’].astype(“int64”)\n发现报错了，然后就看见了那个学号是’忘记了’的 最后修改成了\ndata[data['studentid'].apply(lambda x:len(x)!=12 or x=='忘记了')]\n将这些数据删除\ndata = data.drop(data[data['studentid'].apply(lambda x:len(x)!=12 or x=='忘记了')].index)\n考虑到有重复，重复的两个因素就是姓名和学号，因此进行去重处理\ndata.drop_duplicates(subset=['name','studentid'],keep='first',inplace=Tru e)\n此外，对专业的处理，将无用的 xx-x 去掉即可，这里考虑到了正则表达式\ndata['class'] = data['class'].apply(lambda s:re.sub(r\"[\\s*\\d*\\-*\\—*\\ － *\\–*\\/*]?\",'',s))\n因为各种各样的-负号千奇百怪，我只能一次次修改后然后统计一下即调用 data[‘class’].value_counts() 有没有没有处理到的，然后把那个-符号加进去 还发现了有/号。\n最后就成了那样，写到这里我有了更好的想法，和下面的 某两个个例有关系。\n然后就是那个 maps 表，都简化为简称，对称呼进行统一，用了 apply 方 法 再统计一下，发现了两个专业后面带名字的学长学姐，因为就两个，就把他 们加到 maps 里面了，其实也可以判断名字是否在专业里面，如果在就替换 为空吧。 之后就差不多可以了，数据预处理完毕，按照要求保存即可。\n#数据预处理文件 import pandas as pd import re data = pd.read_excel(\u0026quot;附件1.xlsx\u0026quot;) #去除错误数据 data = data.drop(data[data[\u0026#39;studentid\u0026#39;].apply(lambda x:len(x)!=12 or x==\u0026#39;忘记了\u0026#39;)].index) #去重 data.drop_duplicates(subset=[\u0026#39;name\u0026#39;,\u0026#39;studentid\u0026#39;], keep=\u0026#39;first\u0026#39;, inplace=True) data[\u0026#39;class\u0026#39;] = data[\u0026#39;class\u0026#39;].apply(lambda s:re.sub(r\u0026quot;[\\s*\\d*\\-*\\—*\\－*\\–*\\/*]?\u0026quot;, \u0026#39;\u0026#39;, s)) maps = { \u0026#39;智能科学\u0026#39;:\u0026#39;智科\u0026#39;, \u0026#39;云计算\u0026#39;:\u0026#39;云计\u0026#39;, \u0026#39;应用统计学\u0026#39;:\u0026#39;统计\u0026#39;, \u0026#39;信息与计算科学\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;智能科学与技术\u0026#39;:\u0026#39;智科\u0026#39;, \u0026#39;应用统计\u0026#39;:\u0026#39;统计\u0026#39;, \u0026#39;软件工程\u0026#39;:\u0026#39;软工\u0026#39;, \u0026#39;信息与计算科学（云计算）\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;光电信息与科学\u0026#39;:\u0026#39;光电\u0026#39;, \u0026#39;信计（云计算）\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;光电信息科学与工程\u0026#39;:\u0026#39;光电\u0026#39;, \u0026#39;数据科学\u0026#39;:\u0026#39;大数据\u0026#39;, \u0026#39;智科科学\u0026#39;:\u0026#39;智科\u0026#39;, \u0026#39;信计学长\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;信计学姐\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;统计学\u0026#39;:\u0026#39;统计\u0026#39;, \u0026#39;信息计算与科学\u0026#39;:\u0026#39;信计\u0026#39;, \u0026#39;信计与计算科学\u0026#39;:\u0026#39;信计\u0026#39; } def replaces(clas): if clas in maps.keys(): return maps[clas] else: return clas data[\u0026#39;class\u0026#39;] = data[\u0026#39;class\u0026#39;].apply(replaces) res = pd.DataFrame() res[\u0026#39;账号\u0026#39;] = \u0026#39;21aidc\u0026#39; + data[\u0026#39;studentid\u0026#39;] res[\u0026#39;姓名\u0026#39;] = data[\u0026#39;name\u0026#39;] res[\u0026#39;密码\u0026#39;] = res[\u0026#39;账号\u0026#39;] res[\u0026#39;专业\u0026#39;] = data[\u0026#39;class\u0026#39;] res.to_excel(\u0026quot;result.xlsx\u0026quot;, index=False,encoding=\u0026#39;utf-8\u0026#39;) ","date":"2022-09-20T00:00:00Z","permalink":"/aidc%E6%B5%8B%E8%AF%95/","title":"aidc测试"},{"content":"moment(矩) 矩在数学中的定义，一阶矩(first moment)就是样本的均值(mean), 二阶矩就是方差（variance）。 ## 滑动平均 滑动平均(exponential moving average)，或者叫做指数加权平均(exponentially weighted moving average)，可以用来估计变量的局部均值，使得变量的更新与一段时间内的历史取值有关。在时间序列预测中也常用。\n变量 \\(v\\) 在 \\(t\\) 时刻记为 \\(v_{t} ，\\text{可以理解为0到t时刻的平均值} 。\\quad \\theta_{t}\\) 为变量 \\(v\\) 在 \\(t\\) 时刻的取值，即在不使用滑动平均模型时 \\(v_{t}=\\theta_{t}\\) ，在使用滑动平均模型后， \\(v_{t}\\) 的更新公式如下:\n\\[ v_{t}=\\beta \\cdot v_{t-1}+(1-\\beta) \\cdot \\theta_{t} \\]\n上式中， \\(\\beta \\in[0,1) ， \\beta=0\\) 相当于没有使用滑动平均。 这也是RMSProp和Adam等算法里使用的最重要的思想。通过滑动平均来降低梯度的波动值。 ## Adam 下面看最经典的伪代码： adam算法比起adagrad和RMSProp，不仅加入了一阶和二阶moment的计算。而且加入了bias-correction term。以下将展开分析：\nadam的更新率（stepsize) adam算法中最重要的就是每次迭代的迭代率（step size），他决定了adam算法的效率。根据上 文的算法， step size等于: \\(\\Delta_{t}=\\alpha \\cdot \\widehat{m}_{t} / \\sqrt{\\hat{v}_{t}}\\) 1) 当 \\(\\left(1-\\beta_{1}\\right)\u0026gt;\\sqrt{1-\\beta_{2}}\\) 的时候，它的上界满足不等式: \\(\\left|\\Delta_{t}\\right| \\leq \\alpha \\cdot\\left(1-\\beta_{1}\\right) / \\sqrt{1-\\beta_{2}}\\) 2) 否则 \\(\\left|\\Delta_{t}\\right| \\leq \\alpha\\) 1）通常发生在数据很稀疏的时候。当数据密集的时候， stepsize会更小。 3) 当 \\(\\left(1-\\beta_{1}\\right)=\\sqrt{1-\\beta_{2}}\\) 的时候，因为 \\(\\left|\\widehat{m}_{t} / \\sqrt{\\hat{v}_{t}}\\right|\u0026lt;1\\) 所以，也满足条件 2 的 \\(\\left|\\Delta_{t}\\right| \\leq \\alpha\\) 总结以上3个条件，可以近似得出stepsize 满足 \\(\\left|\\Delta_{t}\\right| \\cong \\alpha\\) 这里的 \\(\\widehat{m}_{t} / \\sqrt{\\hat{v}_{t}}\\) 通常也成为信噪比（Signal-to-noise ratio SNR)，并且满足SND越小， stepsize也越小。\n初始化偏差矫正项 原算法中的这两行 \\[ \\begin{aligned} \u0026amp;\\widehat{m}_{t} \\leftarrow m_{t} /\\left(1-\\beta_{1}^{t}\\right) \\\\\\\\ \u0026amp;\\hat{v}_{t} \\leftarrow v_{t} /\\left(1-\\beta_{2}^{t}\\right) \\end{aligned} \\] 称为偏差校正项(bias-correction term),他使用了滑动平均值(EMA: exponential moving average)的思想，例如计算二次moment的 \\(v_{t}=\\beta_{2} \\cdot v_{t-1}+\\left(1-\\beta_{2}\\right) \\cdot g_{t}^{2}\\) 可以写成如下的形 式： \\[ v_{t}=\\left(1-\\beta_{2}\\right) \\sum_{i=1}^{t} \\beta_{2}^{t-i} \\cdot g_{i}^{2} \\] 我们的目的是求得 \\(\\mathbb{E}\\left[v_{t}\\right]\\) (EMA) 和二阶moment \\(\\mathbb{E}\\left[g_{t}^{2}\\right]\\) 之间的关系，推导如下: \\[ \\begin{aligned} \\mathbb{E}\\left[v_{t}\\right] \u0026amp;=\\mathbb{E}\\left[\\left(1-\\beta_{2}\\right) \\sum_{i=1}^{t} \\beta_{2}^{t-i} \\cdot g_{i}^{2}\\right] \\\\\\\\ \u0026amp;=\\mathbb{E}\\left[g_{t}^{2}\\right] \\cdot\\left(1-\\beta_{2}\\right) \\sum_{i=1}^{t} \\beta_{2}^{t-i}+\\zeta \\\\\\\\ \u0026amp;=\\mathbb{E}\\left[g_{t}^{2}\\right] \\cdot\\left(1-\\beta_{2}^{t}\\right)+\\zeta \\end{aligned} \\] 最后得出 \\(\\mathbb{E}\\left[g_{t}^{2}\\right]=\\frac{\\mathbb{E}\\left[v_{t}\\right]-\\zeta}{\\left(1-\\beta_{2}^{t}\\right)}\\) 通常可以忽略常数 \\(\\zeta\\) 。得出\n\\[ \\bar{v_t} = \\frac{v_t}{1-\\beta_2^t} \\]\n综上所述，Adam 优化器可以根据历史梯度的震荡情况和过滤震荡后的真实历史梯度对变量进行更新\n","date":"2022-09-11T00:00:00Z","permalink":"/adam/","title":"Adam算法"},{"content":"精确率和召回率 混淆矩阵 True Positive(真正, TP)：将正类预测为正类数. True Negative(真负 , TN)：将负类预测为负类数. False Positive(假正, FP)：将负类预测为正类数 False Negative(假负 , FN)：将正类预测为负类数 精确率 \\[ P = \\frac{TP}{TP+FP} \\]\n准确率 \\[ ACC = \\frac{TP+TN}{TP+TN+FP+FN} \\]\n召回率 \\[ R = \\frac{TP}{TP+FN} \\]\nF1 \\[ \\frac{2}{F_1} = \\frac{1}{P} + \\frac{1}{R} \\]\n区别 精确率（查准率）：在所有预测为正的样本中（分母），真正为正的有多少（分子）。\n召回率（查全率）：在所有实际为正的样本中（分母），成功预测出来的有多少（分子）\nimg img ","date":"2022-08-21T00:00:00Z","permalink":"/%E7%B2%BE%E7%A1%AE%E7%8E%87%E5%92%8C%E5%8F%AC%E5%9B%9E%E7%8E%87/","title":"精确率和召回率"},{"content":"参考：https://cuijiahua.com/blog/2017/11/ml_2_decision_tree_1.html\n《机器学习》周志华\n决策树 决策树是什么？决策树(decision tree)是一种基本的分类与回归方法。举个通俗易懂的例子，如下图所示的流程图就是一个决策树，长方形代表判断模块(decision block)，椭圆形成代表终止模块(terminating block)，表示已经得出结论，可以终止运行。从判断模块引出的左右箭头称作为分支(branch)，它可以达到另一个判断模块或者终止模块。我们还可以这样理解，分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点(node)和有向边(directed edge)组成。结点有两种类型：内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性，叶结点表示一个类。蒙圈没？？如下图所示的决策树，长方形和椭圆形都是结点。长方形的结点属于内部结点，椭圆形的结点属于叶结点，从结点引出的左右箭头就是有向边。而最上面的结点就是决策树的根结点(root node)。这样，结点说法就与模块说法对应上了，理解就好。\n步骤 1.特征选择\n特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率，如果利用一个特征进行分类的结果与随机分类的结果没有很大差别，则称这个特征是没有分类能力的。经验上扔掉这样的特征对决策树学习的精度影响不大。通常特征选择的标准是信息增益(information gain)或信息增益比，为了简单，本文使用信息增益作为选择特征的标准。那么，什么是信息增益？在讲解信息增益之前，让我们看一组实例，贷款申请样本数据表。\n机器学习实战教程（二）：决策树基础篇之让我们从相亲说起 希望通过所给的训练数据学习一个贷款申请的决策树，用于对未来的贷款申请进行分类，即当新的客户提出贷款申请时，根据申请人的特征利用决策树决定是否批准贷款申请。\n特征选择就是决定用哪个特征来划分特征空间。比如，我们通过上述数据表得到两个可能的决策树，分别由两个不同特征的根结点构成。\n(1).香农熵\n在可以评测哪个数据划分方式是最好的数据划分之前，我们必须学习如何计算信息增益。集合信息的度量方式称为香农熵或者简称为熵(entropy)，这个名字来源于信息论之父克劳德·香农。\n如果看不明白什么是信息增益和熵，请不要着急，因为他们自诞生的那一天起，就注定会令世人十分费解。克劳德·香农写完信息论之后，约翰·冯·诺依曼建议使用”熵”这个术语，因为大家都不知道它是什么意思。\n熵定义为信息的期望值。在信息论与概率统计中，熵是表示随机变量不确定性的度量。如果待分类的事物可能划分在多个分类之中，则符号xi的信息定义为 ：\n\\[ l(x_i) = -log_{2}p(x_i) \\] 其中p(xi)是选择该分类的概率。有人可能会问，信息为啥这样定义啊？答曰：前辈得出的结论。这就跟1+1等于2一样，记住并且会用即可。上述式中的对数以2为底，也可以e为底(自然对数)。\n通过上式，我们可以得到所有类别的信息。为了计算熵，我们需要计算所有类别所有可能值包含的信息期望值(数学期望)，通过下面的公式得到：\n\\[ H = -\\sum_{i=1}^np(x_i)log_2p(x_i) \\] 期中n是分类的数目。熵越大，随机变量的不确定性就越大。\n当熵中的概率由数据估计(特别是最大似然估计)得到时，所对应的熵称为经验熵(empirical entropy)。什么叫由数据估计？比如有10个数据，一共有两个类别，A类和B类。其中有7个数据属于A类，则该A类的概率即为十分之七。其中有3个数据属于B类，则该B类的概率即为十分之三。浅显的解释就是，这概率是我们根据数据数出来的。我们定义贷款申请样本数据表中的数据为训练数据集D，则训练数据集D的经验熵为H(D)，|D|表示其样本容量，及样本个数。设有K个类Ck, = 1,2,3,.. .,K,|Ck|为属于类Ck的样本个数，因此经验熵公式就可以写为 ：\n\\[ H(D) = -\\sum_{k=1}^K \\frac{|c_k|}{|D|}log_2\\frac{|c_k|}{|D|} \\] 根据此公式计算经验熵H(D)，分析贷款申请样本数据表中的数据。最终分类结果只有两类，即放贷和不放贷。根据表中的数据统计可知，在15个数据中，9个数据的结果为放贷，6个数据的结果为不放贷。所以数据集D的经验熵H(D)为：\n\\[ H(D) = -\\frac{9}{15}log_2\\frac{9}{15} - \\frac{6}{15}log_2\\frac{6}{15} = 0.971 \\] (2)信息增益\n在上面，我们已经说过，如何选择特征，需要看信息增益。也就是说，信息增益是相对于特征而言的，信息增益越大，特征对最终的分类结果影响也就越大，我们就应该选择对最终分类结果影响最大的那个特征作为我们的分类特征。\n在讲解信息增益定义之前，我们还需要明确一个概念，条件熵。\n熵我们知道是什么，条件熵又是个什么鬼？条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性，随机变量X给定的条件下随机变量Y的条件熵(conditional entropy)H(Y|X)，定义为X给定条件下Y的条件概率分布的熵对X的数学期望：\n\\[ H(Y|X) = \\sum_{i=1}^np_iH(Y|X = x_i), \\\\\\\\ p_i = P(X = x_i) \\] 明确了条件熵和经验条件熵的概念。接下来，让我们说说信息增益。前面也提到了，信息增益是相对于特征而言的。所以，特征A对训练数据集D的信息增益g(D,A)，定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差，即：\n\\[ g(D,A) = H(D) - H(D|A) \\] 一般地，熵H(D)与条件熵H(D|A)之差称为互信息(mutual information)。决策树学习中的信息增益等价于训练数据集中类与特征的互信息。\n设特征A有n个不同的取值{a1,a2,···,an}，根据特征A的取值将D划分为n个子集{D1,D2，···,Dn}，|Di|为Di的样本个数。记子集Di中属于Ck的样本的集合为Dik，即Dik = Di ∩ Ck，|Dik|为Dik的样本个数。于是经验条件熵的公式可以些为\n\\[ H(D|A) = \\sum_{i=1}^n\\frac{|D_i|}{|D|}H(D_i) = -\\sum_{i=1}^n\\frac{|D_i|}{|D|}\\sum_{k=1}^K\\frac{|D_{ik}|}{|D_i|}log_2\\frac{|D_{ik}|}{|D_i|} \\]\n说了这么多概念性的东西，没有听懂也没有关系，举几个例子，再回来看一下概念，就懂了。\n以贷款申请样本数据表为例进行说明。看下年龄这一列的数据，也就是特征A1，一共有三个类别，分别是：青年、中年和老年。我们只看年龄是青年的数据，年龄是青年的数据一共有5个，所以年龄是青年的数据在训练数据集出现的概率是十五分之五，也就是三分之一。同理，年龄是中年和老年的数据在训练数据集出现的概率也都是三分之一。现在我们只看年龄是青年的数据的最终得到贷款的概率为五分之二，因为在五个数据中，只有两个数据显示拿到了最终的贷款，同理，年龄是中年和老年的数据最终得到贷款的概率分别为五分之三、五分之四。所以计算年龄的信息增益，过程如下：\n机器学习实战教程（二）：决策树基础篇之让我们从相亲说起 同理，计算其余特征的信息增益g(D,A2)、g(D,A3)和g(D,A4)。分别为：\n机器学习实战教程（二）：决策树基础篇之让我们从相亲说起 机器学习实战教程（二）：决策树基础篇之让我们从相亲说起 最后，比较特征的信息增益，由于特征A3(有自己的房子)的信息增益值最大，所以选择A3作为最优特征。\n由于特征A3(有自己的房子)的信息增益值最大，所以选择特征A3作为根结点的特征。它将训练集D划分为两个子集D1(A3取值为”是”)和D2(A3取值为”否”)。由于D1只有同一类的样本点，所以它成为一个叶结点，结点的类标记为“是”。\n对D2则需要从特征A1(年龄)，A2(有工作)和A4(信贷情况)中选择新的特征，计算各个特征的信息增益：\n机器学习实战教程（三）：决策树实战篇之为自己配个隐形眼镜 根据计算，选择信息增益最大的特征A2(有工作)作为结点的特征。由于A2有两个可能取值，从这一结点引出两个子结点：一个对应”是”(有工作)的子结点，包含3个样本，它们属于同一类，所以这是一个叶结点，类标记为”是”；另一个是对应”否”(无工作)的子结点，包含6个样本，它们也属于同一类，所以这也是一个叶结点，类标记为”否”。\n这样就生成了一个决策树，该决策树只用了两个特征(有两个内部结点)，生成的决策树如下图所示。\n机器学习实战教程（三）：决策树实战篇之为自己配个隐形眼镜 总结 我们已经学习了从数据集构造决策树算法所需要的子功能模块，包括经验熵的计算和最优特征的选择，其工作原理如下：得到原始数据集，然后基于最好的属性值划分数据集，由于特征值可能多于两个，因此可能存在大于两个分支的数据集划分。第一次划分之后，数据集被向下传递到树的分支的下一个结点。在这个结点上，我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。\n构建决策树的算法有很多，比如C4.5、ID3和CART，这些算法在运行时并不总是在每次划分数据分组时都会消耗特征。由于特征数目并不是每次划分数据分组时都减少，因此这些算法在实际使用时可能引起一定的问题。目前我们并不需要考虑这个问题，只需要在算法开始运行前计算列的数目，查看算法是否使用了所有属性即可。\n决策树生成算法递归地产生决策树，直到不能继续下去未为止。这样产生的树往往对训练数据的分类很准确，但对未知的测试数据的分类却没有那么准确，即出现过拟合现象。过拟合的原因在于学习时过多地考虑如何提高对训练数据的正确分类，从而构建出过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度，对已生成的决策树进行简化。\n决策树的一些优点：\n易于理解和解释。决策树可以可视化。 几乎不需要数据预处理。其他方法经常需要数据标准化，创建虚拟变量和删除缺失值。决策树还不支持缺失值。 使用树的花费（例如预测数据）是训练数据点(data points)数量的对数。 可以同时处理数值变量和分类变量。其他方法大都适用于分析一种变量的集合。 可以处理多值输出变量问题。 使用白盒模型。如果一个情况被观察到，使用逻辑判断容易表示这种规则。相反，如果是黑盒模型（例如人工神经网络），结果会非常难解释。 即使对真实模型来说，假设无效的情况下，也可以较好的适用。 决策树的一些缺点：\n决策树学习可能创建一个过于复杂的树，并不能很好的预测数据。也就是过拟合。修剪机制（现在不支持），设置一个叶子节点需要的最小样本数量，或者数的最大深度，可以避免过拟合。 决策树可能是不稳定的，因为即使非常小的变异，可能会产生一颗完全不同的树。这个问题通过decision trees with an ensemble来缓解。 概念难以学习，因为决策树没有很好的解释他们，例如，XOR, parity or multiplexer problems。 如果某些分类占优势，决策树将会创建一棵有偏差的树。因此，建议在训练之前，先抽样使样本均衡。 代码 import numpy as np import math def equalNums(label_list, label): \u0026quot;\u0026quot;\u0026quot; 函数说明： 计算标记集中某个标记的数量 Parameters： label_list - 标记集 label - 某个标记 Returns： num - 某个标记的数量 \u0026quot;\u0026quot;\u0026quot; return np.sum(label_list == label) def calcShannonEnt(label_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 计算信息熵 对应公式 Ent(D) = -∑ Pk*log2(Pk) k=1..len(label_set) Parameters： label_list - 标记集 Returns： shannonEnt - 当前标记集的信息熵 \u0026quot;\u0026quot;\u0026quot; label_set = set(label_list) len_label_list = label_list.size shannonEnt = 0.0 for label in label_set: prob = equalNums(label_list, label)/len_label_list shannonEnt -= prob * math.log2(prob) return shannonEnt def conditionnalEntropy(feature_list, label_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 计算条件信息熵，对应信息增益公式中的被减项 Parameters： feature_list - sample_list中某一列，表示当前属性的所有值 label_list - 标记集 Returns： entropy - 条件信息熵 \u0026quot;\u0026quot;\u0026quot; feature_list = np.asarray(feature_list) label_list = np.asarray(label_list) feature_set = set(feature_list) entropy = 0.0 for feat in feature_set: pro = equalNums(feature_list, feat)/feature_list.size entropy += pro * calcShannonEnt(label_list[feature_list == feat]) return entropy def calcInfoGain(feature_list, label_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 计算信息增益 Parameters： feature_list - sample_list中某一列，表示当前属性的所有值 label_list - 标记集 Returns： 当前属性的信息增益 \u0026quot;\u0026quot;\u0026quot; return calcShannonEnt(label_list) - conditionnalEntropy(feature_list, label_list) def splitDataSet(sample_list, label_list, axis, value): \u0026quot;\u0026quot;\u0026quot; 函数说明： 决策树在选好当前最优划分属性之后划分样本集 依据value选择对应样例，并去除第axis维属性 Parameters： feature_list - sample_list中某一列，表示当前属性的所有值 label_list - 标记集 Returns： return_sample_list, return_label_list \u0026quot;\u0026quot;\u0026quot; # sample_list[sample_list[...,axis] == value] 利用了numpy数组的布尔索引 filtered_sample_list = sample_list[sample_list[...,axis] == value] return_label_list = label_list[sample_list[...,axis] == value] # np.hstack 将数组横向拼接，也就是去除第axis维属性 return_sample_list = np.hstack((filtered_sample_list[...,:axis], filtered_sample_list[...,axis+1:])) return return_sample_list, return_label_list def chooseBestFeatureToSplit(sample_list, label_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 选取最优划分属性 Parameters： sample_list - 样本集 label_list - 标记集 Returns： bestFeat_index - 最优划分属性的索引值 \u0026quot;\u0026quot;\u0026quot; numFeatures = sample_list.shape[1] bestInfoGain = 0 bestFeat_index = -1 for i in range(numFeatures): infoGain = calcInfoGain(sample_list[..., i], label_list) if infoGain \u0026gt; bestInfoGain: bestInfoGain = infoGain bestFeat_index = i return bestFeat_index def createTree(sample_list, label_list, attr_list_copy): \u0026quot;\u0026quot;\u0026quot; 函数说明： 生成决策树 Parameters： sample_list - 样本集 label_list - 标记集 attr_list_copy - 属性集（之所以加copy是为了删属性的时候是在副本上，防止递归出错） Returns： myTree - 最终的决策树 \u0026quot;\u0026quot;\u0026quot; # attr_list 有del操作，不用副本的话递归会出错 attr_list = attr_list_copy.copy() if len(set(label_list)) == 1: # 如果只有一种标记，直接返回标记 return label_list[0] elif sample_list.size == 0: # 如果所有属性都被遍历，返回最多的标记 return voteLabel(label_list) # bestFeat_index 最优划分属性的索引值 # bestAttr 最优划分属性对应的名字 bestFeat_index = chooseBestFeatureToSplit(sample_list, label_list) bestAttr = attr_list[bestFeat_index] myTree = {bestAttr: {}} del(attr_list[bestFeat_index]) feat_set = set(sample_list[..., bestFeat_index]) # 依据最优划分属性进行划分，并向下递归 for feat in feat_set: return_sample_list, return_label_list = splitDataSet(sample_list, label_list, bestFeat_index, feat) myTree[bestAttr][feat] = createTree(return_sample_list, return_label_list, attr_list) return myTree def voteLabel(label_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 这个函数是用在遍历完所有特征时，返回最多的类别 Parameters： label_list: 标记列表 Returns： 数量最多的标记 \u0026quot;\u0026quot;\u0026quot; # unique_label_list 是label_list中标记种类列表 # label_num 是unique_label_list对应的数量列表 unique_label_list = list(set(label_list)) label_num_list = [] for label in unique_label_list: label_num_list.append(equalNums(label_list, label)) # label_num.index(max(label_num))是label_num数组中最大值的下标 return unique_label_list[label_num_list.index(max(label_num_list))] def classify(decisionTree, testVec, attr_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 对tesVec进行分类 Parameters： decisionTree - 决策树 attr_list - 属性名列表 testVec - 测试向量 Returns： label - 预测的标记 \u0026quot;\u0026quot;\u0026quot; feature = list(decisionTree.keys())[0] # feature为决策树的根节点 feature_dict = decisionTree[feature] # feature_dict为根节点下的子树 feature_index = attr_list.index(feature) # feature_index为feature对应的属性名索引 feature_value = testVec[feature_index] # feature_value为测试集中对应属性的值 label = None if feature_value in feature_dict.keys(): # 如果没有结果就继续向下找 if type(feature_dict[feature_value]) == dict: label = classify(feature_dict[feature_value], testVec, attr_list) else: label = feature_dict[feature_value] return label def testAccuracy(decisionTree, test_sample_list, test_label_list, attr_list): \u0026quot;\u0026quot;\u0026quot; 函数说明： 测试十次得到正确率均值 Parameters： decisionTree - 决策树 test_sample_list - 测试样本集 test_label_list - 测试标记集 attr_list - 属性集 Returns： \u0026quot;\u0026quot;\u0026quot; def oneTime(decisionTree, test_sample_list, test_label_list, attr_list): rightNum = 0 predict_label_list = [] for i in range(len(test_sample_list)): predict_label = classify(decisionTree, test_sample_list[i], attr_list) predict_label_list.append(predict_label) if predict_label == test_label_list[i]: rightNum += 1 accuracy = rightNum/len(test_sample_list) return accuracy sum = 0.0 for i in range(10): sum += oneTime(decisionTree, test_sample_list, test_label_list, attr_list) av_accuracy = sum/10 return av_accuracy dataSet = np.array([[0, 0, 0, 0, \u0026#39;no\u0026#39;], [0, 0, 0, 1, \u0026#39;no\u0026#39;], [0, 1, 0, 1, \u0026#39;yes\u0026#39;], [0, 1, 1, 0, \u0026#39;yes\u0026#39;], [0, 0, 0, 0, \u0026#39;no\u0026#39;], [1, 0, 0, 0, \u0026#39;no\u0026#39;], [1, 0, 0, 1, \u0026#39;no\u0026#39;], [1, 1, 1, 1, \u0026#39;yes\u0026#39;], [1, 0, 1, 2, \u0026#39;yes\u0026#39;], [1, 0, 1, 2, \u0026#39;yes\u0026#39;], [2, 0, 1, 2, \u0026#39;yes\u0026#39;], [2, 0, 1, 1, \u0026#39;yes\u0026#39;], [2, 1, 0, 1, \u0026#39;yes\u0026#39;], [2, 1, 0, 2, \u0026#39;yes\u0026#39;], [2, 0, 0, 0, \u0026#39;no\u0026#39;]]) labels = [\u0026#39;年龄\u0026#39;, \u0026#39;有工作\u0026#39;, \u0026#39;有自己的房子\u0026#39;, \u0026#39;信贷情况\u0026#39;] print(createTree(dataSet[:, :-1], dataSet[:, -1], labels)) {\u0026#39;有自己的房子\u0026#39;: {\u0026#39;0\u0026#39;: {\u0026#39;有工作\u0026#39;: {\u0026#39;0\u0026#39;: \u0026#39;no\u0026#39;, \u0026#39;1\u0026#39;: \u0026#39;yes\u0026#39;}}, \u0026#39;1\u0026#39;: \u0026#39;yes\u0026#39;}} ","date":"2022-08-21T00:00:00Z","permalink":"/%E5%86%B3%E7%AD%96%E6%A0%91/","title":"决策树"},{"content":"分类算法 主要区分一下生成模型和判别模型，首先要知道生成模型和判别模型都属于监督学习，即样本有其对应的标签的。还有一个概念就是硬分类和软分类，简单理解就是硬分类是直接分出类别，比如线性判别分析、感知机。而软分类是计算出概率，根据概率来得到类别，生成模型和判别模型都是软分类。\n生成模型 学习得到联合概率分布\\(P(x,y)\\)，即特征x与标签y共同出现的概率，然后求条件概率分布。能够学习到数据生成的机制。通俗的说就是如果有k类，就学习k个概率密度分布，对于样本，算出在每个概率密度分布下的概率，哪个概率大就属于哪一类。\n生成模型要求的数据量比较大，能够更好地估计概率密度。 ## 判别模型\n学习得到条件概率分布\\(P(y|x)\\)，即在特征x出现的情况下标记y出现的概率。\n判别模型对样本的要求没有那么多。\n理解 无论是生成还是判别模型都是来求有监督模型的，目的是通过分类函数或者条件概率函数进行数据分类。\n算出属于正负样本的概率再互相对比的就是生成模型，直接得到结果概率的就是判别模型，生成模型得到分布，判别模型得到最优划分。\n生成模型可以得到判别模型，反之不成立。\n生成模型是求联合概率分布，判别模型是求条件概率分布。\n生成方法的学习收敛速度更快，当样本容量增加的时候，学到的模型可以更快的收敛于真实模型。\n判别学习不能反映训练数据本身的特性，但它寻找不同类别之间的最优分类面，反映的是异类数据之间的差异，直接面对预测，往往学习的准确率高于生成模型。\n简单的说，生成模型是从大量的数据中找规律，属于统计学习；而判别模型只关心不同类型的数据的差别，利用差别来分类。\n生成式模型： 朴素贝叶斯 混合高斯模型\n隐马尔科夫模型(HMM) 贝叶斯网络\nSigmoid Belief Networks\n马尔科夫随机场(Markov Random Fields)\n深度信念网络(DBN) 判别式模型 K近邻(KNN) 线性回归(Linear Regression) 逻辑回归(Logistic Regression) 神经网络(NN) 支持向量机(SVM) 高斯过程(Gaussian Process) 条件随机场(CRF) CART(Classification and Regression Tree) ","date":"2022-08-12T00:00:00Z","permalink":"/%E5%88%86%E7%B1%BB%E7%AE%97%E6%B3%95%E6%A6%82%E8%BF%B0/","title":"分类算法概述"},{"content":"BM25算法 BM25算法，通常用来作搜索相关性平分。一句话概况其主要思想：对Query进行语素解析，生成语素qi；然后，对于每个搜索结果D，计算每个语素qi与D的相关性得分，最后，将qi相对于D的相关性得分进行加权求和，从而得到Query与D的相关性得分。\n原理 BM25一般公式如下：\n\\[ score(Q,d) = \\sum_{i}^nW_iR(q_i, d) \\] 其中Q表示为Query，\\(q_i\\)表示Q解析后的一个语素(对中文而言，我们可以把对Query的分词作为语素分析，每个词看成语素)。d表示一个搜索结果文档，\\(W_i\\)表示语素\\(q_i\\)的权重，\\(R(q_i, d)\\)表示语素\\(q_i\\)与文档d的相关性得分。\n权重 下面我们来看如何定义Wi。判断一个词与一个文档的相关性的权重，方法有多种，较常用的是IDF。这里以IDF为例，公式如下：\n\\[ IDF(q_i) = \\log \\frac{N-n(q_i)+0.5}{n(q_i)+0.5} \\]\n其中，N为索引中的全部文档数，n(qi)为包含了qi的文档数。\n根据IDF的定义可以看出，对于给定的文档集合，包含了qi的文档数越多，qi的权重则越低。也就是说，当很多文档都包含了qi时，qi的区分度就不高，因此使用qi来判断相关性时的重要度就较低。\n相关性得分 下面定义相关性得分\\(R(q_i,d)\\)\n首先来看BM25中相关性得分的一般形式：\n其中\\(k_1, k_2, b\\)为调节因子，根据经验设置，一般\\(k_1=2,b=0.75\\) ，\\(f_i\\)为\\(q_i\\)在文档d中出现的频率，\\(qf_i\\)为\\(q_i\\)在Query中出现的频率，dl为文档d的长度，avgdl为所有文档的平均长度，绝大部分情况下\\(q_i\\)在Query中只会出现一次，因此 \\(qf_i=1\\)，所以可以简化为： 从K的定义中可以看到，参数b的作用是调整文档长度对相关性影响的大小。b越大，文档长度的对相关性得分的影响越大，反之越小。而文档的相对长度越长，K值将越大，则相关性得分会越小。这可以理解为，当文档较长时，包含qi的机会越大，因此，同等fi的情况下，长文档与qi的相关性应该比短文档与qi的相关性弱。\n综上，BM25算法的相关性得分公式可总结为：\n从BM25的公式可以看到，通过使用不同的语素分析方法、语素权重判定方法，以及语素与文档的相关性判定方法，我们可以衍生出不同的搜索相关性得分计算方法，这就为我们设计算法提供了较大的灵活性。\n代码 import math import jieba import re text = \u0026#39;\u0026#39;\u0026#39; 自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。 它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。 自然语言处理是一门融语言学、计算机科学、数学于一体的科学。 因此，这一领域的研究将涉及自然语言，即人们日常使用的语言， 所以它与语言学的研究有着密切的联系，但又有重要的区别。 自然语言处理并不是一般地研究自然语言， 而在于研制能有效地实现自然语言通信的计算机系统， 特别是其中的软件系统。因而它是计算机科学的一部分。 \u0026#39;\u0026#39;\u0026#39; class BM25(object): def __init__(self, docs): self.D = len(docs) # doc个数 self.avgdl = sum([len(doc)+0.0 for doc in docs]) / self.D # 每篇平均长度 self.docs = docs self.f = [] # 列表的每一个元素是一个dict，dict存储着一个文档中每个词的出现次数 self.df = {} # 存储每个词及出现了该词的文档数量(count) self.idf = {} # 存储每个词的idf值，当作权重 self.k1 = 1.5 self.b = 0.75 self.init() def init(self): for doc in self.docs: tmp = {} for word in doc: tmp[word] = tmp.get(word, 0) + 1 # 存储每个文档中每个词的出现次数（也可以用defaultdict) self.f.append(tmp) # idx为索引，f[idx]为一个dict，dict存储着第idx+1个文档中每个词的出现次数,idx代表第几个文档。 for k in tmp.keys(): # 如果词k出现在了当前文档中，则df[k]即文档数量加1 self.df[k] = self.df.get(k, 0) + 1 for k, v in self.df.items(): self.idf[k] = math.log(self.D-v+0.5)-math.log(v+0.5) # 计算idf def sim(self, query, index): # 与单个文档的相似度 score = 0 for word in query: if word not in self.f[index]: continue d = len(self.docs[index]) # 当前文档的长度 score += (self.idf[word]*(self.f[index][word]/d)*(self.k1+1) / ((self.f[index][word]/d)+self.k1*(1-self.b+self.b*d / self.avgdl))) return score def simall(self, query): scores = [] for index in range(self.D): score = self.sim(query, index) scores.append(score) return scores def get_sentences(doc): line_break = re.compile(\u0026#39;[\\r\\n]\u0026#39;) # 以换行符分割 delimiter = re.compile(\u0026#39;[，。？！；]\u0026#39;) # 以中文标点符号分割 sentences = [] for line in line_break.split(doc): line = line.strip() if not line: continue for sent in delimiter.split(line): sent = sent.strip() if not sent: continue sentences.append(sent) return sentences if __name__ == \u0026#39;__main__\u0026#39;: sents = get_sentences(text) print(sents) doc = [] for sent in sents: words = list(jieba.cut(sent)) doc.append(words) # print(doc) s = BM25(doc) # print(s.f) # print(s.df) # print(s.idf) print(s.simall([\u0026#39;自然语言\u0026#39;, \u0026#39;计算机科学\u0026#39;, \u0026#39;领域\u0026#39;, \u0026#39;人工智能\u0026#39;, \u0026#39;领域\u0026#39;])) ","date":"2022-08-07T00:00:00Z","permalink":"/bm25/","title":"BM25"},{"content":"GPT 预训练(从左到右的 Transformer 语言模型) GPT 是一种基于 Transformer 的从左到右的语言模型。该架构是一个 12 层的 Transformer 解码器（没有解码器-编码器）。 ## 模型架构 就是12层的transformer-decoder。其中只使用了transformer模型中的decoder部分，并且把decoder里面的encoder-decoder attention部分去掉了，只保留了masked self-attention，再加上feed-forward部分。再提一句，masked self-attention保证了GPT模型是一个单向的语言模型。 另外，作者在position encoding上做了调整，使用了可学习的位置编码，不同于transformer的三角函数位置编码。\n微调：将 GPT 用于下游任务 微调损失包括特定于任务的损失以及语言建模损失：\n\\[ L = L_{xent} + \\lambda \\cdot L_{task}. \\]\n","date":"2022-07-27T00:00:00Z","permalink":"/gpt/","title":"GPT"},{"content":"Dropout 在标准dropout正则化中，通过按保留（未丢弃）的节点的分数进行归一化来消除每一层的偏差。换言之，每个中间激活值h以保留概率概率p由随机变量替换(即drop经过神经元后的值代替drop神经元)\n\\[ h^{\u0026#39;}= \\begin{cases} 0, \\quad 概率为1-p \\\\\\\\ \\frac{h}{p}, \\quad 概率为p \\end{cases} \\]\n注意期望不要变，即\n\\[ E[h^{\u0026#39;}] = (1-p)*0 + p *\\frac{h}{p} = h \\] 也可以训练时非丢弃单元不除以概率p，而是测试时模型参数乘以p，这样可以保证训练集和测试集的期望相同。和上面的效果相同。 注意：正则项（Dropout）只在训练过程中使用，因为其会影响模型参数的更新\n所以在推理过程中，丢弃法直接返回输入。\n代码 import torch from torch import nn from d2l import torch as d2l import torchvision from torchvision import transforms from torch.utils import data def dropout_layer(X,dropout): assert 0\u0026lt;=dropout \u0026lt;= 1 # 如果keep_prob设置为1，全部元素被保留 if dropout == 1: return X # 如果keep_prob设置为0，全部元素被丢弃 if dropout == 0: return torch.zeros_like(X) mask = (torch.rand(X.shape) \u0026lt; dropout).float() #使用mask而不是直接置零是为了提高计算效率 return mask * X/(dropout) ","date":"2022-07-21T00:00:00Z","permalink":"/dropout%E6%AD%A3%E5%88%99%E5%8C%96/","title":"Dropout正则化"},{"content":"最长回文子串 题目： ​ https://leetcode-cn.com/problems/longest-palindromic-substring/\n思路： ​ 一开始暴力解法，比较好想，结果超时了哎，后来看见了标签是动态规划，才知道不能暴力\nclass Solution: def longestPalindrome(self, s: str) -\u0026gt; str: if len(s) \u0026lt;= 1: return s maxs = -float(\u0026quot;inf\u0026quot;) res = collections.defaultdict(list) left,right = 0,len(s)-1 while left \u0026lt; right: for i in range(left,right+2): if s[left:i] == s[left:i][::-1]: maxs = max(maxs,len(s[left:i])) res[maxs].append(s[left:i]) left += 1 return max(res[max(res.keys())],key=len) 也用到了双指针，超时在情理之中。\n后来用到了动态规划\nclass Solution: def longestPalindrome(self, s: str) -\u0026gt; str: if len(s) \u0026lt;= 1: return s length = len(s) dp = [[False for _ in range(length)] for _ in range(length)] for i in range(length): dp[i][i] = True start = 0 max_len = 1 for j in range(1, length): for i in range(0, j): if s[i] == s[j]: if j - i \u0026lt; 3: dp[i][j] = True else: dp[i][j] = dp[i + 1][j - 1] else: dp[i][j] = False if dp[i][j]: cur_len = j - i + 1 if cur_len \u0026gt; max_len: max_len = cur_len start = i return s[start:start + max_len] ","date":"2022-07-21T00:00:00Z","permalink":"/%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/","title":"最长回文子串"},{"content":"有效的数独 https://leetcode-cn.com/problems/valid-sudoku/\n#有效的数独 难点在将3*3里的数取出来 class Solution: def isValidSudoku(board) -\u0026gt; bool: for line1,line2 in zip(board,zip(*board)): #行列 for n1,n2 in zip(line1,line2): if (n1 != \u0026#39;.\u0026#39; and line1.count(n1) \u0026gt; 1) or (n2!=\u0026#39;.\u0026#39; and line2.count(n2) \u0026gt;1): return False pal = [[board[i+m][j+n] for m in range(3) for n in range(3) if board[i+m][j+n] != \u0026#39;.\u0026#39;] for i in (0, 3, 6) for j in (0, 3, 6)] for line in pal: if len(set(line)) != len(line): return False return True ","date":"2022-07-20T00:00:00Z","permalink":"/%E6%9C%89%E6%95%88%E7%9A%84%E6%95%B0%E7%8B%AC/","title":"有效的数独"},{"content":" ","date":"2022-07-18T00:00:00Z","permalink":"/apriori%E7%AE%97%E6%B3%95/","title":"Apriori算法"},{"content":"使括号有效的最少添加 题目： https://leetcode-cn.com/problems/minimum-add-to-make-parentheses-valid/\n思路： 通过一个值来判断是否匹配\n代码： class Solution: def minAddToMakeValid(self, S: str) -\u0026gt; int: res,temp = 0,0 for i in S: if i == \u0026#39;(\u0026#39;: temp += 1 if i == \u0026#39;)\u0026#39;: temp -= 1 if temp == -1: temp = 0 res += 1 return res + temp 如果右括号过多的话，就在左边补一个左括号。这时结果+1\n如果一直是左括号的话，res 为0 temp就是应该补的个数\n如果都相匹配的话，temp = 0 相应 res也为0\n","date":"2022-07-17T00:00:00Z","permalink":"/%E4%BD%BF%E6%8B%AC%E5%8F%B7%E6%9C%89%E6%95%88%E7%9A%84%E6%9C%80%E5%B0%91%E6%B7%BB%E5%8A%A0/","title":"使括号有效的最少添加"},{"content":"集成学习 在机器学习的有监督学习算法中，我们的目标是学习出一个稳定的且在各个方面表现都较好的模型，但实际情况往往不这么理想，有时我们只能得到多个有偏好的模型（弱监督模型，在某些方面表现的比较好）。集成学习就是组合这里的多个弱监督模型以期得到一个更好更全面的强监督模型，集成学习潜在的思想是即便某一个弱分类器得到了错误的预测，其他的弱分类器也可以将错误纠正回来。 集成学习在各个规模的数据集上都有很好的策略。 数据集大：划分成多个小数据集，学习多个模型进行组合 数据集小：利用Bootstrap方法进行抽样，得到多个数据集，分别训练多个模型再进行组合\n集成学习主要有两类： 1. Bagging\nBoosting Bagging算法是这样做的：每个分类器都随机从原样本中做有放回的采样，然后分别在这些采样后的样本上训练分类器，然后再把这些分类器组合起来。简单的多数投票一般就可以。其代表算法是随机森林。\nBoosting的意思是这样，他通过迭代地训练一系列的分类器，每个分类器采用的样本分布都和上一轮的学习结果有关。其代表算法是AdaBoost, GBDT。\nBagging bagging的名称来源于 （ Bootstrap Aggregating ），意思是自助抽样集成，这种方法将训练集分成m个新的训练集，然后在每个新训练集上构建一个模型，各自不相干，最后预测时我们将这个m个模型的结果进行整合，得到最终结果。整合方式就是：分类问题用majority voting，回归用均值。\n因此Bagging使用的抽样方法是Bootstrap方法，即自助法，本质上就是一个有放回的随机抽样问题。 每一个样本在每一次抽的时候有同样的概率\\(\\frac{1}{N}\\)被抽中。没被抽中的概率为\\(1-\\frac{1}{N}\\)，一共抽了N次，即\\(1-(\\frac{1}{N})^N\\)当N趋于无穷时，由高等数学学的极限的求解可以算出来是\\(\\frac{1}{e}\\)，大概为36.8%，这些留下来的1/3的样本可以作为验证集，这样的方式叫做包外估计(out of bag estimate)\nBoosting Boosting与Bagging的区别就是取样方式不同，Bagging采用均匀取样，而Boosting根据错误率来取样，因此Boosting的分类精度要优于Bagging。。Bagging的训练集的选择是随机的，各轮训练集之间相互独立，而Boostlng的各轮训练集的选择与前面各轮的学习结果有关；Bagging的各个预测函数没有权重，而Boosting是有权重的；Bagging的各个预测函数可以并行生成，而Boosting的各个预测函数只能顺序生成。对于象神经网络这样极为耗时的学习方法。Bagging可通过并行训练节省大量时间开销。很好理解吧。\n一个故事 一个故事用于理解，来源：https://www.joinquant.com/view/community/detail/adfb5ce37f0b39e348aae32e8412c68c\n有一个医学专（砖）家，他看过很多很多病人，还记了小本本来归类这些病人的特征和病情来方便以后诊断。有一天来了个病人，这个专家就问病人了，“大爷您贵姓？多少岁，哪不舒服，病情怎么样？”大爷说 “我姓李，48，最近老吐痰，老咳嗽，还发烧…”。这个专家拿出他的小本本一查“”属性：“姓李”，“年龄48”，“吐痰”，“咳嗽”，“发烧”…,根据我的小本本以前这样的病例有80个，有76个是感冒，成，就诊断他是感冒了！”这个专家就是棵决策树\n镜头一转，来到医院A，同样的病人来医院A治病。医院有很大的病例数据库，有100个医学专家通过学习数据库的一部分知识形成了自己的诊断方案。医院想：“我财大气粗，为了提供更好的医疗服务，我让100个专家做诊断，然后他们投票决出最后的判断”。 随机森林 完成\n医院B就不爽了，你这治疗方案太受欢迎，把客户都抢走了，我要用科学的治疗方案来击败你。医院B想了想，大家投票不一定针对到用户情况，我先从我的100个专家里找一个最好的先给病人做一个诊疗方案，再根据第一个专家的不足找第二个，根据第二个再找第三个…… 最后不同专家再根据他们诊断的表现以不同权重投票。这样岂不是更针对病人痛点？ Boosting 方法就被这群人建立起来了\n到了医院C想搞差异化，你医院B根据上一个专家的全部不足找新专家，那我就根据上一个专家判断最偏颇的方向找专家，虽然听起来差不多，但我的差异化说不定就能更好。 Gradient Boosting 产生了\n更加财大气粗的医院D来了，他觉得虽然整个市场的大格调基本确定，但我可以通过提升整个诊疗的流程大大小小的细节来取胜啊！于是医院D在C的基础上改进了很多，于是找偏颇的方向更快更准，纠结专家，诊疗的速度也大大加快，整个医院的硬件设施也前所未有的提高。这差不多就是 XGBoost 了\n突然有股叫大数据的潮流吹来，本来医院D已经在医院C一分钟治疗10000人的基础上提升了10多倍速度，但新的要求是：一分钟不行，最多给你3秒，10万人也不行，我现在有全世界的数据，你得分秒内召集几百几千个专家，这些专家每一个的知识得相当于以前一个医院那没多，还得分秒内服务数10倍的病人，最后治疗的精度不能下降。 LightGBM 出场了，虽然精度提高不多，但速度大大加快了。\n","date":"2022-07-09T00:00:00Z","permalink":"/ensemble-learning/","title":"Ensemble Learning"},{"content":"旋转图像 https://leetcode-cn.com/problems/rotate-image/\n没难度的中等题，这方法很python\nclass Solution: def rotate(self, matrix: List[List[int]]) -\u0026gt; None: \u0026quot;\u0026quot;\u0026quot; Do not return anything, modify matrix in-place instead. \u0026quot;\u0026quot;\u0026quot; n = len(matrix) for i in list(map(list,map(reversed,zip(*matrix)))): matrix.append(i) del matrix[:n] ","date":"2022-07-07T00:00:00Z","permalink":"/%E6%97%8B%E8%BD%AC%E5%9B%BE%E5%83%8F/","title":"旋转图像"},{"content":"KNN 参考：https://cuijiahua.com/blog/2017/11/ml_1_knn.html\n《统计学习方法》李航（kd树）\n简介 k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是：存在一个样本数据集合，也称作为训练样本集，并且样本集中每个数据都存在标签，即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后，将新的数据的每个特征与样本集中数据对应的特征进行比较，然后算法提取样本最相似数据(最近邻)的分类标签。一般来说，我们只选择样本数据集中前k个最相似的数据，这就是k-近邻算法中k的出处，通常k是不大于20的整数。最后，选择k个最相似数据中出现次数最多的分类，作为新数据的分类。\n步骤 k-近邻算法步骤如下：\n计算已知类别数据集中的点与当前点之间的距离； 按照距离递增次序排序； 选取与当前点距离最小的k个点； 确定前k个点所在类别的出现频率； 返回前k个点所出现频率最高的类别作为当前点的预测分类。 总结 优点\n简单好用，容易理解，精度高，理论成熟，既可以用来做分类也可以用来做回归； 可用于数值型数据和离散型数据； 训练时间复杂度为O(n)；无数据输入假定； 对异常值不敏感 缺点\n计算复杂性高；空间复杂性高； 样本不平衡问题（即有些类别的样本数量很多，而其它样本的数量很少）； 一般数值很大的时候不用这个，计算量太大。但是单个样本又不能太少，否则容易发生误分。 最大的缺点是无法给出数据的内在含义。 关于algorithm参数kd_tree的原理，可以查看《统计学方法 李航》书中的讲解； 关于距离度量的方法还有切比雪夫距离、马氏距离、巴氏距离等； ","date":"2022-06-25T00:00:00Z","permalink":"/knn/","title":"KNN"},{"content":"主题模型 主题模型也可以看成一种词向量表达，主要有LSA、PLSA、LDA。按照这个顺序来逐渐发展的\n词袋模型 将所有词语装进一个袋子里，不考虑其词法和语序的问题，即每个词语都是独立的\n例子：\n句子1：我 爱 北 京 天 安 门 转换为 [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0] 句子2：我 喜 欢 上 海 转换为 [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1] from sklearn.feature_extraction.text import CountVectorizer corpus = [ \u0026#39;This is the first document.\u0026#39;, \u0026#39;This document is the second document.\u0026#39;, \u0026#39;And this is the third one.\u0026#39;, \u0026#39;Is this the first document?\u0026#39;, ] vectorizer = CountVectorizer() vectorizer.fit_transform(corpus).toarray() 结果：\n[[0 1 1 1 0 0 1 0 1] [0 2 0 1 0 1 1 0 1] [1 0 0 1 1 0 1 1 1] [0 1 1 1 0 0 1 0 1]] LSA LSA就是潜在语义分析。特点是通过矩阵分解发现文本与单词之间基于主题（话题）的语义关系。 首先要清楚几个概念：\n单词-文本矩阵 \\[ X=\\left[\\begin{array}{cccc} x_{11} \u0026amp; x_{12} \u0026amp; \\cdots \u0026amp; x_{1 n} \\\\\\\\ x_{21} \u0026amp; x_{22} \u0026amp; \\cdots \u0026amp; x_{2 n} \\\\\\\\ \\vdots \u0026amp; \\vdots \u0026amp; \u0026amp; \\vdots \\\\\\\\ x_{m 1} \u0026amp; x_{m 2} \u0026amp; \\cdots \u0026amp; x_{m n} \\end{array}\\right] \\]\n这是一个 \\(m \\times n\\) 矩阵, 元素 \\(x_{i j}\\) 表示单词 \\(w_i\\) 在文本 \\(d_j\\) 中出现的频数或权值。由于单 词的种类很多, 而每个文本中出现单词的种类通常较少, 所以单词-文本矩阵是一个稀 疏矩阵。 权值通常用单词频率-逆文本频率 (term frequency-inverse document frequency, TF-IDF）表示，其定义是\n\\[ \\operatorname{TFIDF}_{i j}=\\frac{\\mathrm{tf}_{i j}}{\\mathrm{tf}_{\\bullet j}} \\log \\frac{\\mathrm{df}}{\\mathrm{df}_i}, \\quad i=1,2, \\cdots, m ; \\quad j=1,2, \\cdots, n \\]\n直观上讲，可以直接用每一列作为文本语义表达， 因此可以通过余弦相似度等计算文本之间的相似性，并且矩阵稀疏，计算量较少。但其并不关心文本中词语出现的顺序等信息，因此需要改进。 ### 单词-主题矩阵 假设所有文本共含有 \\(k\\) 个话题。假设每个话题由一个定义在单词集合 \\(W\\) 上的 \\(m\\) 维向量表示, 称为话题向量, 即\n\\[ t_l=\\left[\\begin{array}{c} t_{1 l} \\\\\\\\ t_{2 l} \\\\\\\\ \\vdots \\\\\\\\ t_{m l} \\end{array}\\right], \\quad l=1,2, \\cdots, k \\]\n其中 \\(t_{i l}\\) 是单词 \\(w_i\\) 在话题 \\(t_l\\) 的权值, \\(i=1,2, \\cdots, m\\), 权值越大, 该单词在该话题中 的重要度就越高。这 \\(k\\) 个话题向量 \\(t_1, t_2, \\cdots, t_k\\) 张成一个话题向量空间 (topic vector 话题向量空间 \\(T\\) 也可以表示为一个矩阵, 称为单词-主题矩阵 (word-topic matrix）, 记作\n\\[ T=\\left[\\begin{array}{cccc} t_{11} \u0026amp; t_{12} \u0026amp; \\cdots \u0026amp; t_{1 k} \\\\\\\\ t_{21} \u0026amp; t_{22} \u0026amp; \\cdots \u0026amp; t_{2 k} \\\\\\\\ \\vdots \u0026amp; \\vdots \u0026amp; \u0026amp; \\vdots \\\\\\\\ t_{m 1} \u0026amp; t_{m 2} \u0026amp; \\cdots \u0026amp; t_{m k} \\end{array}\\right] \\]\n主题-文本矩阵 将单词-文本矩阵中的文本\\(x_j\\)投影到主题向量空间\\(J\\)中，得到在主题空间中的一个向量\\(y_j\\)。\n\\[ Y=\\left[\\begin{array}{cccc} y_{11} \u0026amp; y_{12} \u0026amp; \\cdots \u0026amp; y_{1 n} \\\\\\\\ y_{21} \u0026amp; y_{22} \u0026amp; \\cdots \u0026amp; y_{2 n} \\\\\\\\ \\vdots \u0026amp; \\vdots \u0026amp; \u0026amp; \\vdots \\\\\\\\ y_{k 1} \u0026amp; y_{k 2} \u0026amp; \\cdots \u0026amp; y_{k n} \\end{array}\\right] \\]\n从单词向量空间到主题向量空间的线性变换 单词-文本矩阵\\(X\\)可以近似表示为单词-主题矩阵\\(T\\)与主题-文本矩阵\\(Y\\)的乘积，这就是潜在语义分析：\n\\[ X\\approx TY \\]\n潜在语义分析 给定单词-文本矩阵\\(X\\)，每一行代表一个单词，每一列代表一个文本。其中的元素代表单词在文本中的权重或者频数（词袋模型）。\n截断奇异值分析 \\[ X \\approx U_k \\Sigma_k V_k^{\\mathrm{T}}=\\left[\\begin{array}{llll} u_1 \u0026amp; u_2 \u0026amp; \\cdots \u0026amp; u_k \\end{array}\\right]\\left[\\begin{array}{cccc} \\sigma_1 \u0026amp; 0 \u0026amp; 0 \u0026amp; 0 \\\\\\\\ 0 \u0026amp; \\sigma_2 \u0026amp; 0 \u0026amp; 0 \\\\\\\\ 0 \u0026amp; 0 \u0026amp; \\ddots \u0026amp; 0 \\\\\\\\ 0 \u0026amp; 0 \u0026amp; 0 \u0026amp; \\sigma_k \\end{array}\\right]\\left[\\begin{array}{c} v_1^{\\mathrm{T}} \\\\\\\\ v_2^{\\mathrm{T}} \\\\\\\\ \\vdots \\\\\\\\ v_k^{\\mathrm{T}} \\end{array}\\right] \\]\n接下来考虑文本在主题空间中的表示。\n\\[ \\begin{aligned} X \u0026amp;=\\left[\\begin{array}{llll} x_1 \u0026amp; x_2 \u0026amp; \\cdots \u0026amp; x_n \\end{array}\\right] \\approx U_k \\Sigma_k V_k^{\\mathrm{T}} \\\\\\\\ \u0026amp;=\\left[\\begin{array}{llll} u_1 \u0026amp; u_2 \u0026amp; \\cdots \u0026amp; u_k \\end{array}\\right]\\left[\\begin{array}{cccc} \\sigma_1 \u0026amp; \u0026amp; \u0026amp; \\\\\\\\ \u0026amp; \\sigma_2 \u0026amp; 0 \u0026amp; \\\\\\\\ 0 \u0026amp; \\ddots \u0026amp; \\\\\\\\ \u0026amp; \u0026amp; \\sigma_k \\end{array}\\right]\\left[\\begin{array}{cccc} v_{11} \u0026amp; v_{21} \u0026amp; \\cdots \u0026amp; v_{n 1} \\\\\\\\ v_{12} \u0026amp; v_{22} \u0026amp; \\cdots \u0026amp; v_{n 2} \\\\\\\\ \\vdots \u0026amp; \\vdots \u0026amp; \u0026amp; \\vdots \\\\\\\\ v_{1 k} \u0026amp; v_{2 k} \u0026amp; \\cdots \u0026amp; v_{n k} \\end{array}\\right] \\\\\\\\ \u0026amp;=\\left[\\begin{array}{llll} u_1 \u0026amp; u_2 \u0026amp; \\cdots \u0026amp; u_k \\end{array}\\right]\\left[\\begin{array}{cccc} \\sigma_1 v_{11} \u0026amp; \\sigma_1 v_{21} \u0026amp; \\cdots \u0026amp; \\sigma_1 v_{n 1} \\\\\\\\ \\sigma_2 v_{12} \u0026amp; \\sigma_2 v_{22} \u0026amp; \\cdots \u0026amp; \\sigma_2 v_{n 2} \\\\\\\\ \\vdots \u0026amp; \\vdots \u0026amp; \u0026amp; \\vdots \\\\\\\\ \\sigma_k v_{1 k} \u0026amp; \\sigma_k v_{2 k} \u0026amp; \\cdots \u0026amp; \\sigma_k v_{n k} \\end{array}\\right] \\end{aligned} \\]\n其中:\n\\[ u_l = \\begin{bmatrix}u_{1l} \\\\\\\\u_{2l} \\\\\\\\ \\vdots \\\\\\\\u_{ml} \\end{bmatrix}, \\quad l= 1, 2, \\dots, k \\]\n代表单词对主题的权重。\n由式知, 矩阵 \\(X\\) 的第 \\(j\\) 列向量 \\(x_j\\) 满足\n\\[ \\begin{aligned} x_j \u0026amp; \\approx U_k\\left(\\Sigma_k V_k^{\\mathrm{T}}\\right)\\_j \\\\\\\\ \u0026amp;=\\left[\\begin{array}{llll} u_1 \u0026amp; u_2 \u0026amp; \\cdots \u0026amp; u_k \\end{array}\\right]\\left[\\begin{array}{c} \\sigma_1 v_{j 1} \\\\\\\\ \\sigma_2 v_{j 2} \\\\\\\\ \\vdots \\\\\\\\ \\sigma_k v_{j k} \\end{array}\\right] \\\\\\\\ \u0026amp;=\\sum_{l=1}^k \\sigma_l v_{j l} u_l, \\quad j=1,2, \\cdots, n \\end{aligned} \\]\n则\\(\\Sigma_kV_k^T\\)每一个列向量是一个文本在主题向量空间中的表示。\nPLSA 生成模型 假设有单词集合 \\(W={w_1, w_2, \\cdots, w_M}\\), 其中 \\(M\\) 是单词个数; 文本 (指标) 集 合 \\(D={d_1, d_2, \\cdots, d_N}\\), 其中 \\(N\\) 是文本个数; 话题集合 \\(Z={z_1, z_2, \\cdots, z_K}\\), 其中 \\(K\\) 是预先设定的话题个数。随机变量 \\(w\\) 取值于单词集合; 随机变量 \\(d\\) 取值于文本集 合, 随机变量 \\(z\\) 取值于话题集合。概率分布 \\(P(d)\\) 、条件概率分布 \\(P(z \\mid d)\\) 、条件概率分 布 \\(P(w \\mid z)\\) 皆属于多项分布, 其中 \\(P(d)\\) 表示生成文本 \\(d\\) 的概率, \\(P(z \\mid d)\\) 表示文本 \\(d\\) 生 成话题 \\(z\\) 的概率, \\(P(w \\mid z)\\) 表示话题 \\(z\\) 生成单词 \\(w\\) 的概率。\n每个文本 \\(d\\) 拥有自己的话题概率分布 \\(P(z \\mid d)\\), 每个话题 \\(z\\) 拥有自己的单词概率分 布 \\(P(w \\mid z)\\); 也就是说一个文本的内容由其相关话题决定, 一个话题的内容由其相关单词决定。\n生成模型通过以下步骤生成文本-单词共现数据: (1) 依据概率分布 \\(P(d)\\), 从文本 (指标) 集合中随机选取一个文本 \\(d\\), 共生成 \\(N\\) 个文本; 针对每个文本, 执行以下操作; (2) 在文本 \\(d\\) 给定条件下, 依据条件概率分布 \\(P(z \\mid d)\\), 从话题集合随机选取一个 话题 \\(z\\), 共生成 \\(L\\) 个话题, 这里 \\(L\\) 是文本长度; (3) 在话题 \\(z\\) 给定条件下, 依据条件概率分布 \\(P(w \\mid z)\\), 从单词集合中随机选取一 个单词 \\(w\\) 。\n生成模型中, 单词变量 \\(w\\) 与文本变量 \\(d\\) 是观测变量, 话题变量 \\(z\\) 是隐变量。也就 是说模型生成的是单词-话题-文本三元组 \\((w, z, d)\\) 的集合, 但观测到的是单词-文本二 元组 \\((w, d)\\) 的集合, 观测数据表示为单词-文本矩阵 \\(T\\) 的形式, 矩阵 \\(T\\) 的行表示单词, 列表示文本, 元素表示单词-文本对 \\((w, d)\\) 的出现次数。\n从数据的生成过程可以推出, 文本-单词共现数据 \\(T\\) 的生成概率为所有单词-文本 对 \\((w, d)\\) 的生成概率的乘积,\n\\[ P(T)=\\prod_{(w, d)} P(w, d)^{n(w, d)} \\]\n这里 \\(n(w, d)\\) 表示 \\((w, d)\\) 的出现次数, 单词-文本对出现的总次数是 \\(N \\times L\\) 。每个单 词-文本对 \\((w, d)\\) 的生成概率由以下公式决定:\n\\[ \\begin{aligned} P(w, d) \u0026amp;=P(d) P(w \\mid d) \\\\\\\\ \u0026amp;=P(d) \\sum_z P(w, z \\mid d) \\\\\\\\ \u0026amp;=P(d) \\sum_z P(z \\mid d) P(w \\mid z) \\end{aligned} \\]\n即生成模型的定义。 生成模型假设在话题 \\(z\\) 给定条件下, 单词 \\(w\\) 与文本 \\(d\\) 条件独立, 即\n\\[ P(w, z \\mid d)=P(z \\mid d) P(w \\mid z) \\]\n共现模型 \\[ P(T)=\\prod_{(w, d)} P(w, d)^{n(w, d)} \\]\n每个单词-文本对 \\((w, d)\\) 的概率由以下公式决定:\n\\[ P(w, d)=\\sum_{z \\in Z} P(z) P(w \\mid z) P(d \\mid z) \\]\n式 (18.5) 即共现模型的定义。容易验证, 生成模型 (18.2) 和共现模型 (18.5) 是等价的。 共现模型假设在话题 \\(z\\) 给定条件下, 单词 \\(w\\) 与文本 \\(d\\) 是条件独立的, 即\n\\[ P(w, d \\mid z)=P(w \\mid z) P(d \\mid z) \\]\n直观解释： 与潜在语义分析的关系 共现模型也可以表示为三个矩阵乘积的形式。这样, 概率潜在语义分析与 潜在语义分析的对应关系可以从中看得很清楚。下面是共现模型的矩阵乘积形式:\n\\[ \\begin{aligned} X^{\\prime} \u0026amp;=U^{\\prime} \\Sigma^{\\prime} V^{\\prime \\mathrm{T}} \\\\\\\\ X^{\\prime} \u0026amp;=[P(w, d)]\\_{M \\times N} \\\\\\\\ U^{\\prime} \u0026amp;=[P(w \\mid z)]\\_{M \\times K} \\\\\\\\ \\Sigma^{\\prime} \u0026amp;=[P(z)]\\_{K \\times K} \\\\\\\\ V^{\\prime} \u0026amp;=[P(d \\mid z)]\\_{N \\times K} \\end{aligned} \\]\n概率潜在语义分析的算法 Plsa是含有隐变量的模型，其学习通常使用EM算法。 E步是计算Q函数，M步是极大化Q函数。\n设单词集合为 \\(W={w_1, w_2, \\cdots, w_M}\\), 文本集合为 \\(D={d_1, d_2, \\cdots, d_N}\\), 话 题集合为 \\(Z={z_1, z_2, \\cdots, z_K}\\) 。给定单词-文本共现数据 \\(T={n\\left(w_i, d_j\\right)}, i=\\) \\(1,2, \\cdots, M, j=1,2, \\cdots, N\\), 目标是估计概率潜在语义分析模型（生成模型）的 参数。如果使用极大似然估计, 对数似然函数是\n\\[ \\begin{aligned} L \u0026amp;=\\sum_{i=1}^M \\sum_{j=1}^N n\\left(w_i, d_j\\right) \\log P\\left(w_i, d_j\\right) \\\\\\\\ \u0026amp;=\\sum_{i=1}^M \\sum_{j=1}^N n\\left(w_i, d_j\\right) \\log \\left[\\sum_{k=1}^K P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)\\right] \\end{aligned} \\]\n但是模型含有隐变量, 对数似然函数的优化无法用解析方法求解, 这时使用 EM算法。 应用 EM算法的核心是定义 \\(Q\\) 函数。 \\(\\mathrm{E}\\) 步：计算 \\(Q\\) 函数 \\(Q\\) 函数为完全数据的对数似然函数对不完全数据的条件分布的期望。针对概率潜 在语义分析的生成模型, \\(Q\\) 函数是\n\\[ Q=\\sum_{k=1}^K{\\sum_{j=1}^N n\\left(d_j\\right)\\left[\\log P\\left(d_j\\right)+\\sum_{i=1}^M \\frac{n\\left(w_i, d_j\\right)}{n\\left(d_j\\right)} \\log P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)\\right]} P\\left(z_k \\mid w_i, d_j\\right) \\]\n式中 \\(n\\left(d_j\\right)=\\sum_{i=1}^M n\\left(w_i, d_j\\right)\\) 表示文本 \\(d_j\\) 中的单词个数, \\(n\\left(w_i, d_j\\right)\\) 表示单词 \\(w_i\\) 在文本 \\(d_j\\) 中出现的次数。条件概率分布 \\(P\\left(z_k \\mid w_i, d_j\\right)\\) 代表不完全数据, 是已知变量。条件概 率分布 \\(P\\left(w_i \\mid z_k\\right)\\) 和 \\(P\\left(z_k \\mid d_j\\right)\\) 的乘积代表完全数据, 是末知变量。 由于可以从数据中直接统计得出 \\(P\\left(d_j\\right)\\) 的估计, 这里只考虑 \\(P\\left(w_i \\mid z_k\\right), P\\left(z_k \\mid d_j\\right)\\) 的估计, 可将 \\(Q\\) 函数简化为函数 \\(Q^{\\prime}\\)\n\\[ Q^{\\prime}=\\sum_{i=1}^M \\sum_{j=1}^N n\\left(w_i, d_j\\right) \\sum_{k=1}^K P\\left(z_k \\mid w_i, d_j\\right) \\log \\left[P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)\\right] \\]\n\\(Q^{\\prime}\\) 函数中的 \\(P\\left(z_k \\mid w_i, d_j\\right)\\) 可以根据贝叶斯公式计算\n\\[ P\\left(z_k \\mid w_i, d_j\\right)=\\frac{P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)}{\\sum_{k=1}^K P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)} \\]\n其中 \\(P\\left(z_k \\mid d_j\\right)\\) 和 \\(P\\left(w_i \\mid z_k\\right)\\) 由上一步迭代得到。 \\(\\mathrm{M}\\) 步: 极大化 \\(Q\\) 函数。 通过约束最优化求解 \\(Q\\) 函数的极大值, 这时 \\(P\\left(z_k \\mid d_j\\right)\\) 和 \\(P\\left(w_i \\mid z_k\\right)\\) 是变量。因为 变量 \\(P\\left(w_i \\mid z_k\\right), P\\left(z_k \\mid d_j\\right)\\) 形成概率分布, 满足约束条件\n\\[ \\begin{aligned} \u0026amp;\\sum_{i=1}^M P\\left(w_i \\mid z_k\\right)=1, \\quad k=1,2, \\cdots, K \\\\\\\\ \u0026amp;\\sum_{k=1}^K P\\left(z_k \\mid d_j\\right)=1, \\quad j=1,2, \\cdots, N \\end{aligned} \\]\n应用拉格朗日法, 引入拉格朗日乘子 \\(\\tau_k\\) 和 \\(\\rho_j\\), 定义拉格朗日函数 \\(A\\)\n\\[ \\Lambda=Q^{\\prime}+\\sum_{k=1}^K \\tau_k\\left(1-\\sum_{i=1}^M P\\left(w_i \\mid z_k\\right)\\right)+\\sum_{j=1}^N \\rho_j\\left(1-\\sum_{k=1}^K P\\left(z_k \\mid d_j\\right)\\right) \\]\n将拉格朗日函数 \\(\\Lambda\\) 分别对 \\(P\\left(w_i \\mid z_k\\right)\\) 和 \\(P\\left(z_k \\mid d_j\\right)\\) 求偏导数, 并令其等于 0 , 得到下面 的方程组\n\\[ \\begin{aligned} \u0026amp;\\sum_{j=1}^N n\\left(w_i, d_j\\right) P\\left(z_k \\mid w_i, d_j\\right)-\\tau_k P\\left(w_i \\mid z_k\\right)=0, \\quad i=1,2, \\cdots, M ; \\quad k=1,2, \\cdots, K \\\\\\\\ \u0026amp;\\sum_{i=1}^M n\\left(w_i, d_j\\right) P\\left(z_k \\mid w_i, d_j\\right)-\\rho_j P\\left(z_k \\mid d_j\\right)=0, \\quad j=1,2, \\cdots, N ; \\quad k=1,2, \\cdots, K \\end{aligned} \\]\n解方程组得到 \\(M\\) 步的参数估计公式:\n\\[ P\\left(w_i \\mid z_k\\right)=\\frac{\\sum_{j=1}^N n\\left(w_i, d_j\\right) P\\left(z_k \\mid w_i, d_j\\right)}{\\sum_{m=1}^M \\sum_{j=1}^N n\\left(w_m, d_j\\right) P\\left(z_k \\mid w_m, d_j\\right)} \\]\n\\[ P(z_k\\mid d_j) = \\frac{\\sum_{i=1}^Mn(w_i, d_j)P(z_k\\mid w_i,d_j)}{n(d_j)} \\]\n总结算法 输入: 设单词集合为 \\(W={w_1, w_2, \\cdots, w_M}\\), 文本集合为 \\(D={d_1, d_2, \\cdots, d_N}\\), 话题集合为 \\(Z={z_1, z_2, \\cdots, z_K}\\), 共现数据 \\({n\\left(w_i, d_j\\right)}, i=1,2, \\cdots, M, j=1\\), \\(2, \\cdots, N\\); 输出: \\(P\\left(w_i \\mid z_k\\right)\\) 和 \\(P\\left(z_k \\mid d_j\\right)\\) 。 (1) 设置参数 \\(P\\left(w_i \\mid z_k\\right)\\) 和 \\(P\\left(z_k \\mid d_j\\right)\\) 的初始值。 (2) 迭代执行以下 \\(\\mathrm{E}\\) 步, \\(\\mathrm{M}\\) 步, 直到收敛为止。 \\(\\mathrm{E}\\) 步:\n\\[ P\\left(z_k \\mid w_i, d_j\\right)=\\frac{P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)}{\\sum_{k=1}^K P\\left(w_i \\mid z_k\\right) P\\left(z_k \\mid d_j\\right)} \\]\nM 步:\n\\[ \\begin{aligned} P\\left(w_i \\mid z_k\\right) \u0026amp;=\\frac{\\sum_{j=1}^N n\\left(w_i, d_j\\right) P\\left(z_k \\mid w_i, d_j\\right)}{\\sum_{m=1}^M \\sum_{j=1}^N n\\left(w_m, d_j\\right) P\\left(z_k \\mid w_m, d_j\\right)} \\\\\\\\ P\\left(z_k \\mid d_j\\right) \u0026amp;=\\frac{\\sum_{i=1}^M n\\left(w_i, d_j\\right) P\\left(z_k \\mid w_i, d_j\\right)}{n\\left(d_j\\right)} \\end{aligned} \\]\n用法 与LSA类似，可以把文档对各个主题的概率看作是文档的表示，最后用到的就是\\(P(z_k\\mid d_j)\\)。\nk就是我们自己设定的主题数，一般来说K远远小于文档个数和词汇表大小，这样也达到了降维的目的。\n优点与不足 优点 pLSA是在一套比较完整的思想的基础上提出来的，模型中各项参数有明确的物理含义，可解释性比较强。相比LSA，pLSA对人类生成文本机制的刻画更加细致、更加符合我们的常识，比如，pLSA基于条件概率，引入了一个“隐含变量”（相对于可以看到的文档和词语，是不可观测变的），即主题，来描述文本生成的过程。 #### 不足 pLSA的理论与我们的实践不是那么的统一: (1) 我们说话的时候，根本不会考虑” 我说这段话的概率大小”，即 \\(p\\left(d_t\\right)\\) (2) pLSA认为，我们说话时面向的主题分布，取决于 “文档” （实际上是文档ID)。这个假设显然是不合理的，小说家不会因为自己写到第666回而调整 主题。 (3) 类似 (2)，随着上下文的变化，我们围绕一个主题说话的内容和方式也 会发生改变。在主题模型中，这种改变的体现，就是一个主题下的词语概率分 布会发生改变。而pLSA忽略了这样的事实。\n从计算复杂度的角度看pLSA有两个比较大的缺陷: (1) pLSA中，对文档 出现的概率估计，来自对训练语料的学习。而对于一个 末知文档，我们是无法估计它出现的概率的一一因此pLSA无法对训练语料之 外的文档进行处理。pLSA的这个特点决定了，在在线(online) 场景中(数据是 持续增加的)，那么文档处理系统就需要定时使用pLSA对整个语料库进行计 算。因此，pLSA比较适合允许一定时滞的离线计算。 (2) pLSA认为一个文档对各个主题的隶属度是一定的——而一个主题对各个词语的隶属度也是一定的，因此pLSA在生成一个文档的各个词语时、使用了相同的词语概率分布。这样，pLSA需要为每一个文档记录一个专门的随着语料数据集规模的增加，pLSA的参数规模也会增加，导致模型训练越来越困难。\nLDA LDA模型是文本集合的生成概率模型。\nLDA 的文本集合的生成过程如下: 首先随机生成一个文本的话题分布, 之后在该 文本的每个位置, 依据该文本的话题分布随机生成一个话题, 然后在该位置依据该话 题的单词分布随机生成一个单词, 直至文本的最后一个位置, 生成整个文本。重复以 上过程生成所有文本。\nLDA 模型是含有隐变量的概率图模型。模型中, 每个话题的单词分布, 每个文 本的话题分布, 文本的每个位置的话题是隐变量; 文本的每个位置的单词是观测变 量。LDA 模型的学习与推理无法直接求解, 通常使用吉布斯抽样 (Gibbs sampling) 和 变分 EM算法 (variational EM algorithm), 前者是蒙特卡罗法, 而后者是近似算法。\n多项分布 (多项分布) 若多元离散随机变量 \\(X=\\left(X_1, X_2, \\cdots, X_k\\right)\\) 的概率质 量函数为\n\\[ \\begin{aligned} P\\left(X_1=n_1, X_2=n_2, \\cdots, X_k=n_k\\right) \u0026amp;=\\frac{n !}{n_{1} ! n_{2} ! \\cdots n_{k} !} p_1^{n_1} p_2^{n_2} \\cdots p_k^{n_k} \\\\\\\\ \u0026amp;=\\frac{n !}{\\prod_{i=1}^k n_{i} !} \\prod_{i=1}^k p_i^{n_i} \\end{aligned} \\]\n其中 \\(p=\\left(p_1, p_2, \\cdots, p_k\\right), p_i \\geqslant 0, i=1,2, \\cdots, k, \\sum_{i=1}^k p_i=1, \\sum_{i=1}^k n_i=n\\), 则称随机变 量 \\(X\\) 服从参数为 \\((n, p)\\) 的多项分布, 记作 \\(X \\sim \\operatorname{Mult}(n, p)\\) 。\n当试验的次数 \\(n\\) 为 1 时, 多项分布变成类别分布 (categorical distribution)。类 别分布表示试验可能出现的 \\(k\\) 种结果的概率。显然多项分布包含类别分布。\n狄利克雷分布 狄利克雷分布 (Dirichlet distribution) 是一种多元连续随机变量的概率分布, 是 贝塔分布 (beta distribution) 的扩展。在贝叶斯学习中, 狄利克雷分布常作为多项分 布的先验分布使用。 (狄利克雷分布) 若多元连续随机变量 \\(\\theta=\\left(\\theta_1, \\theta_2, \\cdots, \\theta_k\\right)\\) 的概率密 度函数为\n\\[ p(\\theta \\mid \\alpha)=\\frac{\\Gamma\\left(\\sum_{i=1}^k \\alpha_i\\right)}{\\prod_{i=1}^k \\Gamma\\left(\\alpha_i\\right)} \\prod_{i=1}^k \\theta_i^{\\alpha_i-1} \\]\n其中 \\(\\sum_{i=1}^k \\theta_i=1, \\theta_i \\geqslant 0, \\alpha=\\left(\\alpha_1, \\alpha_2, \\cdots, \\alpha_k\\right), \\alpha_i\u0026gt;0, i=1,2, \\cdots, k\\), 则称随机变量 \\(\\theta\\) 服从参数为 \\(\\alpha\\) 的狄利克雷分布, 记作 \\(\\theta \\sim \\operatorname{Dir}(\\alpha)\\) 。 式中 \\(\\Gamma(s)\\) 是伽马函数, 定义为\n\\[ \\Gamma(s)=\\int_0^{\\infty} x^{s-1} \\mathrm{e}^{-x} \\mathrm{~d} x, \\quad s\u0026gt;0 \\]\n具有性质：\n\\[ \\Gamma(s+1) = s\\Gamma(s) \\]\n当s为自然数时，有：\n\\[ \\Gamma(s+1) = s! \\]\n令\n\\[ \\mathrm{B}(\\alpha)=\\frac{\\prod_{i=1}^k \\Gamma\\left(\\alpha_i\\right)}{\\Gamma\\left(\\sum_{i=1}^k \\alpha_i\\right)} \\]\n则狄利克雷分布的密度函数可以写成\n\\[ p(\\theta \\mid \\alpha)=\\frac{1}{\\mathrm{~B}(\\alpha)} \\prod_{i=1}^k \\theta_i^{\\alpha_i-1} \\]\n\\(\\mathrm{B}(\\alpha)\\) 是规范化因子, 称为多元贝塔函数 (或扩展的贝塔函数)。由密度函数的性质\n\\[ \\int \\frac{\\Gamma\\left(\\sum_{i=1}^k \\alpha_i\\right)}{\\prod_{i=1}^k \\Gamma\\left(\\alpha_i\\right)} \\prod_{i=1}^{\\alpha_i-1} \\mathrm{~d} \\theta=\\frac{\\Gamma\\left(\\sum_{i=1}^k \\alpha_i\\right)}{\\prod_{i=1}^k \\Gamma\\left(\\alpha_i\\right)} \\int \\prod_{i=1}^k \\theta_i^{\\alpha_i-1} \\mathrm{~d} \\theta=1 \\]\n得\n\\[ \\mathrm{B}(\\alpha)=\\int \\prod_{i=1}^k \\theta_i^{\\alpha_i-1} \\mathrm{~d} \\theta \\]\n二项分布与贝塔分布 二项分布是多项分布的特殊情况, 贝塔分布是狄利克雷分布的特殊情况。 二项分布是指如下概率分布。 \\(X\\) 为离散随机变量, 取值为 \\(m\\), 其概率质量函数为\n\\[ P(X=m)=\\left(\\begin{array}{c} n \\\\\\\\ m \\end{array}\\right) p^m(1-p)^{n-m}, \\quad m=0,1,2, \\cdots, n \\]\n其中 \\(n\\) 和 \\(p(0 \\leqslant p \\leqslant 1)\\) 是参数。\n贝塔分布是指如下概率分布, \\(X\\) 为连续随机变量, 取值范围为 \\([0,1]\\), 其概率密度 函数为\n\\[ p(x)= \\begin{cases}\\frac{1}{\\mathrm{~B}(s, t)} x^{s-1}(1-x)^{t-1}, \u0026amp; 0 \\leqslant x \\leqslant 1 \\\\\\\\ 0, \u0026amp; \\text { 其他 }\\end{cases} \\]\n其中 \\(s\u0026gt;0\\) 和 \\(t\u0026gt;0\\) 是参数, \\(\\mathrm{B}(s, t)=\\frac{\\Gamma(s) \\Gamma(t)}{\\Gamma(s+t)}\\) 是贝塔函数, 定义为\n\\[ \\mathrm{B}(s, t)=\\int_0^1 x^{s-1}(1-x)^{t-1} \\mathrm{~d} x = \\frac{\\Gamma(s)\\Gamma(t)}{\\Gamma(s+t)} \\]\n当 \\(s, t\\) 是自然数时(\\(\\Gamma(s+1) = s!\\)),\n\\[ \\mathrm{B}(s, t)=\\frac{(s-1) !(t-1) !}{(s+t-1) !} \\]\n当 \\(n\\) 为 1 时, 二项分布变成伯努利分布（Bernoulli distribution）或 0-1 分布。 伯努利分布表示试验可能出现的 2 种结果的概率。显然二项分布包含伯努利分布。给出几种概率分布的关系。 基本想法 在LDA主题模型下，一篇文章由词语的序列组成。首先以一定概率选择一个主题，其次以一定概率在这个主题中选择一个词。如果一篇文章由1000个词组成，那么就把上述方式重复1000遍，就能组成这篇文章。那么值得注意的是，以一定概率选择一个主题是服从多项式分布的，而多项式分布的参数是服从Dirichlet分布的。以一定概率在特定主题中选择一个词也是服从多项式分布的，多项式分布的参数是服从Dirichlet分布的。为什么呢？因为Dirichlet分布是多项式分布的共轭分布，也就是说由贝叶斯估计得到的后验分布仍然是Dirichlet分布。 LDA与PLSA的关系 二者都是概率模型，都是利用概率生成模型对文本集合进行主题分析的无监督学习方法。\nPLSA是用了频率派的方法，利用极大似然进行学习，而LDA使用了贝叶斯派的方法，进行贝叶斯推断。\n二者都假设存在两个分布：话题是单词的多项分布，文本是话题的多项分布，不同的在于LDA认为多项分布的参数也服从一个分布，而不是固定不变的，使用狄利克雷分布作为多项分布的先验分布，也就是多项分布的参数服从狄利克雷分布。\n引入先验概率的作用可以防止过拟合。为啥选择狄利克雷分布呢？因为它是多项分布的共轭先验分布，先验分布与后验分布形式相同，便于由先验分布得到后验分布。\nLDA是在Plsa的基础上，为单词分布和主题分布增加了两个狄利克雷先验。 模型定义 模型要素 潜在狄利克雷分配 (LDA) 使用三个集合: 一是单词集合 \\(W={w_1, \\cdots, w_v, \\cdots}\\), , 其中 \\(w_v\\) 是第 \\(v\\) 个单词, \\(v=1,2, \\cdots, V, V\\) 是单词的个数。二是文本集合 \\(D={\\mathbf{w}_1, \\cdots, \\mathbf{w}_m, \\cdots, \\mathbf{w}_M}\\), 其中 \\(\\mathbf{w}_m\\) 是第 \\(m\\) 个文本, \\(m=1,2, \\cdots, M, M\\) 是文本 的个数。文本 \\(\\mathbf{w}_m\\) 是一个单词序列 \\(\\mathbf{w}_m=\\left(w_{m 1}, \\cdots, w_{m n}, \\cdots, w_{m N_m}\\right)\\), 其中 \\(w_{m n}\\) 是 文本 \\(\\mathbf{w}_m\\) 的第 \\(n\\) 个单词, \\(n=1,2, \\cdots, N_m, N_m\\) 是文本 \\(\\mathbf{w}_m\\) 中单词的个数。三是主题集合集合 \\(Z={z_1, \\cdots, z_k, \\cdots, z_K}\\), 其中 \\(z_k\\) 是第 \\(k\\) 个话题, \\(k=1,2, \\cdots, K, K\\) 是话题的个数。\n每一个话题 \\(z_k\\) 由一个单词的条件概率分布 \\(p\\left(w \\mid z_k\\right)\\) 决定, \\(w \\in W\\) 。分布 \\(p\\left(w \\mid z_k\\right)\\) 服从多项分布 (严格意义上类别分布), 其参数为 \\(\\varphi_k\\) 。参数 \\(\\varphi_k\\) 服从狄利克雷分布 (先验分布), 其超参数为 \\(\\beta\\) 。参数 \\(\\varphi_k\\) 是一个 \\(V\\) 维向量 \\(\\varphi_k=\\left(\\varphi_{k 1}, \\varphi_{k 2}, \\cdots, \\varphi_{k V}\\right)\\), 其中 \\(\\varphi_{k v}\\) 表示话题 \\(z_k\\) 生成单词 \\(w_v\\) 的概率。所有话题的参数向量构成一个 \\(K \\times V\\) 矩阵 \\(\\varphi=\\{\\varphi_k\\}_{k=1}^K\\) 。超参数 \\(\\beta\\) 也是一个 \\(V\\) 维向量 \\(\\beta=\\left(\\beta_1, \\beta_2, \\cdots, \\beta_V\\right)\\_{\\text {。 }}\\)(对于话题\\(z_k\\)其生成单词\\(w_v\\)先验服从狄利克雷分布，因此是一个V维向量) 每一个文本 \\(\\mathbf{w}_m\\) 由一个话题的条件概率分布 \\(p\\left(z \\mid \\mathbf{w}_m\\right)\\) 决定, \\(z \\in Z_{\\text {。 }}\\) 分布 \\(p\\left(z \\mid \\mathbf{w}_m\\right)\\) 服从多项分布 (严格意义上类别分布), 其参数为 \\(\\theta_m\\) 。参数 \\(\\theta_m\\) 服从狄利克雷分布 (先验分布), 其超参数为 \\(\\alpha\\) , 参数 \\(\\theta_m\\) 是一个 \\(K\\) 维向量 \\(\\theta_m=\\left(\\theta_{m 1}, \\theta_{m 2}, \\cdots, \\theta_{m K}\\right)\\), 其中 \\(\\theta_{m k}\\) 表示文本 \\(\\mathrm{w}_m\\) 生成话题 \\(z_k\\) 的概率。所有文本的参数向量构成一个 \\(M \\times K\\) 矩阵 \\(\\theta=\\{\\theta_m\\}_{m=1}^M\\) 。超参数 \\(\\alpha\\) 也是一个 \\(K\\) 维向量 \\(\\alpha=\\left(\\alpha_1, \\alpha_2, \\cdots, \\alpha_K\\right)\\) 。 每一个文本 \\(\\mathbf{w}_m\\) 中的每一个单词 \\(w_{m n}\\) 由该文本的话题分布 \\(p\\left(z \\mid \\mathbf{w}_m\\right)\\) 以及所有话 题的单词分布 \\(p\\left(w \\mid z_k\\right)\\) 决定。 生成过程 LDA 文本集合的生成过程如下: 给定单词集合 \\(W\\), 文本集合 \\(D\\), 话题集合 \\(Z\\), 狄利克雷分布的超参数 \\(\\alpha\\) 和 \\(\\beta\\) 。\n1.生成单词分布 随机生成 \\(K\\) 个话题的单词分布。具体过程如下, 按照狄利克雷分布 \\(\\operatorname{Dir}(\\beta)\\) 随机 生成一个参数向量 \\(\\varphi_k, \\varphi_k \\sim \\operatorname{Dir}(\\beta)\\), 作为话题 \\(z_k\\) 的单词分布 \\(p\\left(w \\mid z_k\\right), w \\in W, k=\\) \\(1,2, \\cdots, K\\) 。\n2.生成主题分布 随机生成 \\(M\\) 个文本的主题分布。具体过程如下: 按照狄利克雷分布 \\(\\operatorname{Dir}(\\alpha)\\) 随 机生成一个参数向量 \\(\\theta_m, \\theta_m \\sim \\operatorname{Dir}(\\alpha)\\), 作为文本 \\(\\mathbf{w}_m\\) 的主题分布 \\(p\\left(z \\mid \\mathbf{w}_m\\right), m=\\) \\(1,2, \\cdots, M_{}\\) 。\n3.生成文本的单词序列 随机生成 \\(M\\) 个文本的 \\(N_m\\) 个单词。文本 \\(\\mathbf{w}_m(m=1,2, \\cdots, M)\\) 的单词 \\(w_{m n}(n=\\) \\(\\left.1,2, \\cdots, N_m\\right)\\) 的生成过程如下:\n3.1 首先按照多项分布 \\(\\operatorname{Mult}\\left(\\theta_m\\right)\\) 随机生成一个话题 \\(z_{m n}, z_{m n} \\sim \\operatorname{Mult}\\left(\\theta_m\\right)\\) 3.2 然后按照多项分布 \\(\\operatorname{Mult}\\left(\\varphi_{z_{m n}}\\right)\\) 随机生成一个单词 \\(w_{m n}, w_{m n} \\sim \\operatorname{Mult}\\left(\\varphi_{z_{m n}}\\right)\\_{\\text {。 }}\\) 文本 \\(\\mathbf{w}_m\\) 本身是单词序列 \\(\\mathbf{w}_m=\\left(w_{m 1}, w_{m 2}, \\cdots, w_{m N_m}\\right)\\), 对应着隐式的话题序列 \\(\\mathbf{z}_m=\\left(z_{m 1}, z_{m 2}, \\cdots, z_{m N_m}\\right) 。\\)\n引用一下LDA数学八卦的图： \\(\\vec{\\alpha} \\rightarrow \\vec{\\theta}_m \\rightarrow z_{m, n}\\), 这个过程表示在生成第 \\(m\\) 篇文档的时候，先从第一个坛子中抽了一个doc-topic 骰子 \\(\\vec{\\theta}_m\\),然后投这个骰子生成了文档\\(m\\)中第 \\(n\\) 个词的topic编号 \\(z_{m, n}\\) ； \\(\\vec{\\beta} \\rightarrow \\vec{\\varphi}_k \\rightarrow w_{m, n} \\mid k=z_{m, n}\\), 这个过程表示用如下动作生成语料中第 \\(m\\) 篇文档的第 \\(n\\) 个词: 在上帝手头的 \\(K\\) 个topic-word 骰子 \\(\\vec{\\varphi}_k\\) 中，挑选编号为 \\(k=z_{m, n}\\) 的那个骰子进行投掷，然后生成 word \\(w_{m, n}\\) ; 理解 LDA最重要的就是理解这两个物理过程。LDA 模型在基于 \\(K\\) 个 topic 生成语料中的 \\(M\\) 篇文档的过程中， 由于是 bag-of-words 模型，有一些物理过程是相互独立可交换的。由此，LDA生成模型中， \\(M\\) 篇文档会对应 于 \\(M\\) 个独立的 Dirichlet-Multinomial 共轭结构；K个 个 topic 会对应于 \\(K\\) 个独立的 Dirichlet-Multinomial 共轭结 构。所以理解 LDA 所需要的所有数学就是理解 Dirichlet-Multiomail 共轭，其它都就是理解物理过程。\n总结 对于话题 \\(z_k(k=1,2, \\cdots, K)\\) : 生成多项分布参数 \\(\\varphi_k \\sim \\operatorname{Dir}(\\beta)\\), 作为话题的单词分布 \\(p\\left(w \\mid z_k\\right)\\); 对于文本 \\(\\mathbf{w}_m(m=1,2, \\cdots, M)\\); 生成多项分布参数 \\(\\theta_m \\sim \\operatorname{Dir}(\\alpha)\\), 作为文本的话题分布 \\(p\\left(z \\mid \\mathbf{w}_m\\right)\\); 对于文本 \\(\\mathbf{w}_m\\) 的单词 \\(w_{m n}\\left(m=1,2, \\cdots, M, n=1,2, \\cdots, N_m\\right)\\) : 采样生成话题 \\(z_{m n} \\sim \\operatorname{Mult}\\left(\\theta_m\\right)\\), 作为单词对应的话题; 采样生成单词 \\(w_{m n} \\sim \\operatorname{Mult}\\left(\\varphi_{z_{m n}}\\right)\\) 。 LDA 的文本生成过程中, 假定话题个数 \\(K\\) 给定, 实际通常通过实验选定。狄利 克雷分布的超参数 \\(\\alpha\\) 和 \\(\\beta\\) 通常也是事先给定的。在没有其他先验知识的情况下, 可以 假设向量 \\(\\alpha\\) 和 \\(\\beta\\) 的所有分量均为 1 , 这时的文本的话题分布 \\(\\theta_m\\) 是对称的, 话题的单 词分布 \\(\\varphi_k\\) 也是对称的。\n（帮助理解：主题数为3，假设\\(\\theta_m\\) = {0.4, 0.5, 0.1}，则说明主题\\(z_2\\)出现在文档m当中的概率为0.5，这就是多项分布的参数，再根据多项分布进行采样得到主题。）\n概率计算 LDA 模型整体是由观测变量和隐变量组成的联合概率分布, 可以表为\n\\[ p(\\mathbf{w}, \\mathbf{z}, \\theta, \\varphi \\mid \\alpha, \\beta)=\\prod_{k=1}^K p\\left(\\varphi_k \\mid \\beta\\right) \\prod_{m=1}^M p\\left(\\theta_m \\mid \\alpha\\right) \\prod_{n=1}^{N_m} p\\left(z_{m n} \\mid \\theta_m\\right) p\\left(w_{m n} \\mid z_{m n}, \\varphi\\right) \\]\n(其中M为文本数，\\(N_m\\)为文档m的长度，K为主题数) 其中观测变量 \\(\\mathrm{w}\\) 表示所有文本中的单词序列, 隐变量 \\(\\mathrm{z}\\) 表示所有文本中的话题序列, 隐变量 \\(\\theta\\) 表示所有文本的话题分布的参数, 隐变量 \\(\\varphi\\) 表示所有话题的单词分布的参 数, \\(\\alpha\\) 和 \\(\\beta\\) 是超参数。\n\\(p\\left(\\varphi_k \\mid \\beta\\right)\\) 表示超参数 \\(\\beta\\) 给定条件下第 \\(k\\) 个话题的单词分布的参数 \\(\\varphi_k\\) 的生成概率; \\(p\\left(\\theta_m \\mid \\alpha\\right)\\) 表示超参数 \\(\\alpha\\) 给定条件下第 \\(m\\) 个文本的话题分布的 参数 \\(\\theta_m\\) 的生成概率; \\(p\\left(z_{m n} \\mid \\theta_m\\right)\\) 表示第 \\(m\\) 个文本的话题分布 \\(\\theta_m\\) 给定条件下文本的 第 \\(n\\) 个位置的话题 \\(z_{m n}\\) 的生成概率; \\(p\\left(w_{m n} \\mid z_{m n}, \\varphi\\right)\\) 表示在第 \\(m\\) 个文本的第 \\(n\\) 个位 置的话题 \\(z_{m n}\\) 及所有话题的单词分布的参数 \\(\\varphi\\) 给定条件下第 \\(m\\) 个文本的第 \\(n\\) 个位 置的单词 \\(w_{m n}\\) 的生成概率。 第 \\(m\\) 个文本的联合概率分布可以表为\n\\[ p\\left(\\mathbf{w}_m, \\mathbf{z}_m, \\theta_m, \\varphi \\mid \\alpha, \\beta\\right)=\\prod_{k=1}^K p\\left(\\varphi_k \\mid \\beta\\right) p\\left(\\theta_m \\mid \\alpha\\right) \\prod_{n=1}^{N_m} p\\left(z_{m n} \\mid \\theta_m\\right) p\\left(w_{m n} \\mid z_{m n}, \\varphi\\right) \\]\n其中 \\(\\mathbf{w}_m\\) 表示该文本中的单词序列, \\(\\mathbf{z}_m\\) 表示该文本的话题序列, \\(\\theta_m\\) 表示该文本的话 题分布参数。 LDA 模型的联合分布含有隐变量, 对隐变量进行积分得到边缘分布。 参数 \\(\\theta_m\\) 和 \\(\\varphi\\) 给定条件下第 \\(m\\) 个文本的生成概率是\n\\[ p\\left(\\mathbf{w}_m \\mid \\theta_m, \\varphi\\right)=\\prod_{n=1}^{N_m}\\left[\\sum_{k=1}^K p\\left(z_{m n}=k \\mid \\theta_m\\right) p\\left(w_{m n} \\mid \\varphi_k\\right)\\right] \\]\n超参数 \\(\\alpha\\) 和 \\(\\beta\\) 给定条件下第 \\(m\\) 个文本的生成概率是\n\\[ p\\left(\\mathbf{w}_m \\mid \\alpha, \\beta\\right)=\\prod_{k=1}^K \\int p\\left(\\varphi_k \\mid \\beta\\right)\\left[\\int p\\left(\\theta_m \\mid \\alpha\\right) \\prod_{n=1}^{N_m}\\left[\\sum_{l=1}^K p\\left(z_{m n}=l \\mid \\theta_m\\right) p\\left(w_{m n} \\mid \\varphi_l\\right)\\right] \\mathrm{d} \\theta_m\\right] \\mathrm{d} \\varphi_k \\]\n超参数 \\(\\alpha\\) 和 \\(\\beta\\) 给定条件下所有文本的生成概率是\n\\[ p(\\mathbf{w} \\mid \\alpha, \\beta)=\\prod_{k=1}^K \\int p\\left(\\varphi_k \\mid \\beta\\right)\\left[\\prod_{m=1}^M \\int p\\left(\\theta_m \\mid \\alpha\\right) \\prod_{n=1}^{N_m}\\left[\\sum_{l=1}^K p\\left(z_{m n}=l \\mid \\theta_m\\right) p\\left(w_{m n} \\mid \\varphi_l\\right)\\right] \\mathrm{d} \\theta_m\\right] \\mathrm{d} \\varphi_k \\]\n吉布斯抽样 基本思想 有三个主要目标： - 话题序列的集合\\(z=(z_1, z_2, \\cdots, z_M)\\)的后验概率分布，其中\\(z_m\\)是第m个文本的主题序列，\\(z_m=(z_{m1}, \\cdots, z_{mN_{m}})\\); - 参数\\(\\theta=(\\theta_1, \\cdots, \\theta_{M})\\)，其中\\(\\theta_m\\)是第m个文本的主题分布的参数； - 参数\\(\\varphi=(\\varphi_1, \\cdots, \\varphi_K)\\)，其中\\(\\varphi_k\\)是第k个主题的单词分布的参数。\n对\\(p(\\mathbf{w}, \\mathbf{z}, \\theta, \\varphi \\mid \\alpha, \\beta)\\)进行估计\n吉布斯抽样, 这是一种常用的马尔可夫链蒙特卡罗法。为了估计 多元随机变量 \\(x\\) 的联合分布 \\(p(x)\\), 吉布斯抽样法选择 \\(x\\) 的一个分量, 固定其他分量, 按照其条件概率分布进行随机抽样, 依次循环对每一个分量执行这个操作, 得到联合 分布 \\(p(x)\\) 的一个随机样本, 重复这个过程, 在燃烧期之后, 得到联合概率分布 \\(p(x)\\) 的 样本集合。\nLDA 模型的学习通常采用收缩的吉布斯抽样 (collapsed Gibbs sampling) , 基本想法是, 通过对隐变量 \\(\\theta\\) 和 \\(\\varphi\\) 积分, 得到边缘概率分布 \\(p(\\mathbf{w}, \\mathbf{z} \\mid \\alpha, \\beta)\\) (也是联合分 布), 其中变量 \\(\\mathbf{w}\\) 是可观测的, 变量 \\(\\mathbf{z}\\) 是不可观测的; 对后验概率分布 \\(p(\\mathbf{z} \\mid \\mathbf{w}, \\alpha, \\beta)\\) 进 行吉布斯抽样, 得到分布 \\(p(\\mathbf{z} \\mid \\mathbf{w}, \\alpha, \\beta)\\) 的样本集合; 再利用这个样本集合对参数 \\(\\theta\\) 和 \\(\\varphi\\) 进行估计, 最终得到 LDA 模型 \\(p(\\mathbf{w}, \\mathbf{z}, \\theta, \\varphi \\mid \\alpha, \\beta)\\) 的所有参数估计。 #### 算法流程\n输入: 文本的单词序列 \\(\\mathbf{w}=\\{\\mathbf{w}_1, \\cdots, \\mathbf{w}_m, \\cdots, \\mathbf{w}_M\\}, \\mathbf{w}_m=\\left(w_{m 1}, \\cdots, w_{m n}, \\cdots\\right.\\), \\(\\left.w_{m_{N_m}}\\right)\\);\n输出: 文本的话题序列 \\(\\mathrm{z}=\\{\\mathbf{z}_1, \\cdots, \\mathbf{z}_m, \\cdots, \\mathbf{z}_M\\}, \\mathbf{z}_m=\\left(z_{m 1}, \\cdots, z_{m n}, \\cdots, z_{m_{N_m}}\\right)\\) 的后验概率分布 \\(p(\\mathbf{z} \\mid \\mathbf{w}, \\alpha, \\beta)\\) 的样本计数, 模型的参数 \\(\\varphi\\) 和 \\(\\theta\\) 的估计值; 参数: 超参数 \\(\\alpha\\) 和 \\(\\beta\\), 话题个数 \\(K\\) 。\n设所有计数矩阵的元素 \\(n_{m k}, n_{k v}\\), 计数向量的元素 \\(n_m, n_k\\) 初值为 0 ;\n对所有文本 \\(\\mathbf{w}_m, m=1,2, \\cdots, M\\) 对第 \\(m\\) 个文本中的所有单词 \\(w_{m n}, n=1,2, \\cdots, N_m\\)\n抽样话题 \\(z_{m n}=z_k \\sim \\operatorname{Mult}\\left(\\frac{1}{K}\\right)\\);(对于文本m，其多项分布的参数为\\(\\frac{1}{K}\\)，由\\(\\alpha\\)生成，即\\(\\theta_m \\sim Dir(\\alpha)\\)，\\(\\theta_m\\)为长度为K的向量。) 增加文本-话题计数 \\(n_{m k}=n_{m k}+1\\), 增加文本-话题和计数 \\(n_m=n_m+1\\), 增加话题-单词计数 \\(n_{k v}=n_{k v}+1\\), 增加话题-单词和计数 \\(n_k=n_k+1\\);\n（3）循环执行以下操作, 直到进入燃烧期 对所有文本 \\(\\mathbf{w}_m, m=1,2, \\cdots, M\\) 对第 \\(m\\) 个文本中的所有单词 \\(w_{m n}, n=1,2, \\cdots, N_m\\)\n当前的单词 \\(w_{m n}\\) 是第 \\(v\\) 个单词, 话题指派 \\(z_{m n}\\) 是第 \\(k\\) 个话题; 减少计数 \\(n_{m k}=n_{m k}-1, n_m=n_m-1, n_{k v}=n_{k v}-1, n_k=n_k-1\\);\n按照满条件分布进行抽样\n\\[ p\\left(z_i \\mid \\mathbf{z}_{-i}, \\mathbf{w}, \\alpha, \\beta\\right) \\propto \\frac{n_{k v}+\\beta_v}{\\sum_{v=1}^V\\left(n_{k v}+\\beta_v\\right)} \\cdot \\frac{n_{m k}+\\alpha_k}{\\sum_{k=1}^K\\left(n_{m k}+\\alpha_k\\right)} \\]\n得到新的第 \\(k^{\\prime}\\) 个话题, 分配给 \\(z_{m n}\\);\n增加计数 \\(n_{m k^{\\prime}}=n_{m k^{\\prime}}+1, n_m=n_m+1, n_{k^{\\prime} v}=n_{k^{\\prime} v}+1, n_{k^{\\prime}}=n_{k^{\\prime}}+1\\);\n得到更新的两个计数矩阵 \\(N_{K \\times V}=\\left[n_{k v}\\right]\\) 和 \\(N_{M \\times K}=\\left[n_{m k}\\right]\\), 表示后验 概率分布 \\(p(\\mathbf{z} \\mid \\mathbf{w}, \\alpha, \\beta)\\) 的样本计数;\n利用得到的样本计数, 计算模型参数 \\[ \\begin{aligned} \\theta_{m k} \u0026amp;=\\frac{n_{m k}+\\alpha_k}{\\sum_{k=1}^K\\left(n_{m k}+\\alpha_k\\right)} \\\\\\\\ \\varphi_{k v} \u0026amp;=\\frac{n_{k v}+\\beta_v}{\\sum_{v=1}^V\\left(n_{k v}+\\beta_v\\right)} \\end{aligned} \\]\n训练与推断 有了LDA模型，我们的目标有两个：\n估计模型中的参数\\(\\varphi_1, \\cdots, \\varphi_K\\)和\\(\\theta_1, \\cdots, \\theta_M\\); 对于新来的一篇doc，我们能够计算这篇文档的topic分布\\(\\theta_{new}\\)。 有了吉布斯采样公式就可以基于语料训练LDA模型，并应用训练得到的模型对新的文档进行topic语义分析，训练的过程就是通过Gibbs Samping获取语料中的（z,w）样本，而模型中的所有参数可以基于采样的样本进行估计。\n训练流程如下：\n随机初始化：对语料中的每篇文档的每个词w，随机赋一个topic编号z。 重新扫描语料库，对每个词按照吉布斯采样公式重新采样它的topic，在语料中进行更新。 重复以上语料库的重新采样过程直到吉布斯采样收敛。 统计语料库的topic-word共现频率矩阵，就是LDA的模型 由这个矩阵我们可以计算每一个\\(p(word\\mid topic)\\)概率，从而计算出模型参数\\(\\varphi_1, \\cdots, \\varphi_K\\)，也可以计算另一个参数\\(\\theta_1, \\cdots, \\theta_M\\)，只要在吉布斯抽样收敛后统计每篇文章的topic频率分布，就可以计算每一个\\(p(topic\\mid doc)\\)概率，由于它是和训练语料的每篇文章相关的，对于我们理解新的文档毫无用处，所以一般没有必要保留这个概率。\n如何对新的文档进行推断呢？其实和训练过程完全相似，对于新的文档，认为\\(\\varphi_{kt}\\)是稳定不变的，是由训练语料得到的模型提供的。采样过程只估计该文档的topic分布\\(\\theta_{new}\\)就好了。\n推断过程如下：\n随机初始化：对当前文档的每个词w，随机的赋一个topic编号z； 重新扫描当前文档，按照吉布斯抽样公式，对每个词w，重新采样它的topic； 重复以上过程直到吉布斯采样收敛 统计文档中的topic分布，该分布就是\\(\\theta_{new}\\) 代码 实现了吉布斯推断的python代码：\n\u0026quot;\u0026quot;\u0026quot; LDA implementation in Python @author: Michael Zhang \u0026quot;\u0026quot;\u0026quot; import matplotlib.pyplot as plt import numpy as np import scipy class LDA(object): def __init__(self, tdm, T, alpha = 1., beta=1., iteration=100): \u0026quot;\u0026quot;\u0026quot; tdm: the copus, of (D, Num_words_in_corpus), the value of each entry is the counts of corresponding words in this the corresponding document. e.g. tdm[d, w] = number of word w appears in document d. T: the number of topics \u0026quot;\u0026quot;\u0026quot; self.tdm = tdm self.D, self.W = self.tdm.shape self.alpha= alpha # count for expected value for hyper parameter alpha of theta, i.e. document-topic distribution. self.beta = beta # count for expected value for hyper parameter beta topic-word distribution. self.T = T self.iteration = iteration # z must take in (d,w,i) as input, corresponding to # topic indicator for i-th obserevation of word w in doc d self.z = {} self.topic_word_matrix = np.zeros((self.T, self.W)) # initialize the topic-word matrix. self.doc_topic_matrix = np.zeros((self.D, self.T)) # initialize the documnet-topic matrix. self.topic_counts = np.zeros(self.T) # initialize the topic counter for after sampling process, should be sum of value in self.topic_word_matrix self.doc_counts = np.zeros(self.D) # initialize the doc counter for after sampling process, should be sum of value in self.doc_topic_matrix self.log_likelihood = np.zeros(self.iteration) # store the value of log likelihood at each iteration self._init_matrix() # @pysnooper.snoop(\u0026#39;init.log\u0026#39;) def _init_matrix(self): \u0026quot;\u0026quot;\u0026quot; for all words 1. sample a topic randomly from T topics for each word 2. increment topic word count, self.topic_word_matrix 3. increment document topic count, self.doc_topic_matrix 4. update the topic indicator z. \u0026quot;\u0026quot;\u0026quot; for d in range(self.D): doc = scipy.sparse.coo_matrix(self.tdm[d]) word_freq_topic = zip(doc.col, doc.data) for w, frequency in word_freq_topic: # (word, freq) for i in range(frequency): ############ Finish the following initialization steps ############# # 1. sample a topic randomly from T topics for each word topic = np.random.randint(self.T) # 2. increment topic word count, self.topic_word_matrix self.topic_word_matrix[topic, w] += 1 # 3. increment document topic count, self.doc_topic_matrix self.doc_topic_matrix[d, topic] += 1 # 4. update the topic indicator z. self.z[(d, w, i)] = topic # d: document ID; w: word ID: i: instance ID，即在d中第几个w self.topic_counts = self.topic_word_matrix.sum(axis=1) self.doc_counts = self.doc_topic_matrix.sum(axis=1) # @pysnooper.snoop(\u0026#39;fit.log\u0026#39;) def fit(self): for it in range(self.iteration): # iterate over all the documents for d in range(self.D): # iterate over all the words in d for w in self.tdm[d].indices: # iterate over number of times observed word w in doc d for i in range(self.tdm[d, w]): # we apply the hidden-varible method of Gibbs sampler, the hidden variable is z[(d,w,i)] self.doc_topic_matrix[d,self.z[(d,w,i)]] -= 1 self.doc_counts[d] -= 1 self.topic_word_matrix[self.z[(d,w,i)],w] -= 1 self.topic_counts[self.z[(d,w,i)]] -= 1 # estimation of phi and theta for the current corpus phi_hat = (self.topic_word_matrix[:,w] + self.beta) / (self.topic_counts + self.beta * self.W) theta_hat = (self.doc_topic_matrix[d,:] + self.alpha) / (self.doc_counts[d] + self.alpha * self.T) # calculate the full conditional distribution full_conditional = phi_hat * theta_hat # normalize full_conditional such that it summation equals to 1. full_conditional = full_conditional / full_conditional.sum() # sample a topic for i-th obserevation of word w in doc d based on full_conditional new_topic = np.random.multinomial(1, full_conditional).argmax() # update z, doc_topic_matrix, doc_counts, topic_word_matrix, topic_counts here. self.z[(d,w,i)] = new_topic self.doc_topic_matrix[d,self.z[(d,w,i)]] += 1 self.topic_word_matrix[self.z[(d,w,i)],w] += 1 self.doc_counts[d] += 1 self.topic_counts[self.z[(d,w,i)]] += 1 ############################################################ # Equation 2 log P(w|z) for each iteration based on Equation [2] ## +++++++++ insert code below ++++++++++++++++++++++++### self.log_likelihood[it] = 0 for k in range(self.T): for w in range(self.W): self.log_likelihood[it] += self.topic_word_matrix[k,w] * np.log((self.topic_word_matrix[k,w] + self.beta) / (self.topic_counts[k] + self.beta * self.W)) ############################################################ print(\u0026#39;Iteration %i\\t LL: %.2f\u0026#39; % (it,self.log_likelihood[it])) 本质与使用条件 本质上说，主题模型根本上是实现文本数据的结构化，结构化的文档可以彼此比较和查询，实现传统的任务。 LDA主题模型本质上解决了两类问题： - 文档聚类 - 词汇聚类\n主要价值在于： 1）文档的结构化，相比于传统的词袋模型达到了降维的效果 2）完成了文档的聚类和词汇的聚类，实现文本信息的抽象化分析，帮助分析者探索隐含的语义内容。\n实践中数据要有以下性质才会有较好的结果：\n文档足够多 文档足够长 词汇特征够多 词频足够大 总结 历时好几周，终于完结了主题模型，主要是概率论没有学好，跟着推导的过程过于痛苦，不过也算是稍微理解了一点LDA，复述一下：\nLDA理解可以类比于PLSA，大体的思想都是根据文档生成主题分布，再根据主题分布和单词分布得到文档中的各个单词。不同的是LDA是贝叶斯派的思想，对于两种分布加入了狄利克雷先验概率。LDA的生成过程可以看成上帝掷骰子，从M个骰子中选取一个作为文本m的主题分布，从K个骰子中选取一个作为主题k的单词分布，（注意这里的多项分布的参数就是多项分布中的概率p，其服从狄利克雷分布，比如对于\\(\\theta_m\\)，它其实就是文本m生成不同主题k的概率\\(p(z\\mid d_m)\\)，是个K维的向量。对于\\(\\varphi_k\\)，是由主题k生成不同单词v的概率\\(p(w\\mid z_k)\\)，是个V维的向量。也就是根据狄利克雷分布采样得到的是一些概率，这些概率也是我们最终要求的参数，这些概率作为多项分布的参数再采样生成主题或者单词，还有就是\\(p(z_k\\mid d_m)\\)与\\(z_{mn}\\)的理解，前者就是相当于\\(\\theta_{mk}\\)，后者肯定是主题集合中的一个，不过是根据参数为\\(\\theta_m\\)的多项分布在位置n采样得到的。这就是LDA的整个的理解，当然模型的求解是使用吉布斯抽样的方法，与上面写的步骤不同。写这些是便于理解）。\n由主题分布可以对文本的每个位置赋值一个主题，再根据主题-单词分布可以生成整个文本。一切的一切都是和PLSA一样，求两个分布，以至于可以生成我们的文档。LDA也可以得到文档的主题分布，得到了主题分布和单词分布可以应用于各种任务当中。具体可以参考《LDA漫游指南》。\n现在知道了LDA是怎么一回事了，但还是感觉模模糊糊的，感觉如“通俗理解LDA主题模型”这篇文章开头所说的那样陷入了LDA的细枝末节中，所以写了一些主题，加深自己的印象与理解，经过代码的洗礼，又理解深入了一些，但感觉还没有掌握的很好，可能需要消化消化，那就先告一段落了。以后常看看就行。 ## 参考\nhttps://zhuanlan.zhihu.com/p/374924140 https://www.cnblogs.com/gasongjian/p/7631978.html\n","date":"2022-06-16T00:00:00Z","permalink":"/%E4%B8%BB%E9%A2%98%E6%A8%A1%E5%9E%8B/","title":"主题模型"},{"content":"Transformer \\[ -\\log \\frac{\\exp({\\operatorname{sim}\\left(\\mathbf{h}_i, \\mathbf{h}_i^{+}\\right) / \\tau})}{\\sum_{j=1}^N\\left(\\exp({\\operatorname{sim}\\left(\\mathbf{h}_i, \\mathbf{h}_j^{+}\\right) / \\tau})+\\exp({\\operatorname{sim}\\left(\\mathbf{h}_i, \\mathbf{h}_j^{-}\\right) / \\tau}\\right))} \\]\n背景 先从word2vec开始说起，word2vec可以看作是一个预训练模型，但是它有个问题就是它没有办法解决一词多义的问题，比如说bank这个词语，有银行的意思，但在某些语义下，它也有河岸的意思，但对于word2vec来说，它区别不了这两种含义，因为它们尽管上下文环境中出现的单词不同，但是在用语言模型训练的时候，不论什么上下文的句子经过word2vec，都是预测相同的单词bank，而同一个单词占的是同一行的参数空间，这导致两种不同的上下文信息都会编码到相同的word embedding空间里去。\n而ELMo就解决了这个问题，它使用了双向的LSTM，具体的可以看ELMo,总之使用RNN作为特征提取器，解决了多义词的问题，但现在来看，RNN的特征提取的能力是远不如本文的Transformer的，为什么要介绍这些东西呢，这就是原因，Transformer出现后，取代了RNN和CNN的地位，成为了最流行的特征提取器，大火的GPT和BERT都与Transformer离不开关系。拿bank为例，RNN在读取整个句子之前不会理解bank的含义，也就是RNN的并行能力比较差，而在Transformer中，token之间会互相交互，也就是所谓的自注意力机制，直观地说，Transformer 的编码器可以被认为是一系列推理步骤（层）。在每一步中，token都会互相看着对方（这是我们需要注意的地方——self-attention），交换信息并尝试在整个句子的上下文中更好地理解对方。这发生在几个层（例如，6 个）中。\n在每个解码器层中，前缀标记也通过自注意力机制相互交互。\n下面就详细介绍一下。\nself-attention 首先介绍一下最主要的self-attention，可以说是self-attention实现了上述的token之间交互的功能。\n自注意力是模型的关键组成部分之一。注意 和自注意之间的区别在于，自注意在相同性质的表示之间运行：例如，某个层中的所有编码器状态。\n形式上，这种直觉是通过查询键值注意来实现的。self-attention 中的每个输入标记都会收到三种表示，对应于它可以扮演的角色：\nquery key value 进入正题：\n作为我们想要翻译的输入语句“The animal didn’t cross the street because it was too tired”。句子中”it”指的是什么呢？“it”指的是”street” 还是“animal”？对人来说很简单的问题，但是对算法而言并不简单。\n当模型处理单词“it”时，self-attention允许将“it”和“animal”联系起来。当模型处理每个位置的词时，self-attention允许模型看到句子的其他位置信息作辅助线索来更好地编码当前词。如果你对RNN熟悉，就能想到RNN的隐状态是如何允许之前的词向量来解释合成当前词的解释向量。Transformer使用self-attention来将相关词的理解编码到当前词中。\n下面看一下self-attention是如何计算的：\n向量计算 第一步，根据编码器的输入向量，生成三个向量，比如，对每个词向量，生成query-vec, key-vec, value-vec，生成方法为分别乘以三个矩阵，这些矩阵在训练过程中需要学习。【注意：不是每个词向量独享3个matrix，而是所有输入共享3个转换矩阵；权重矩阵是基于输入位置的转换矩阵；有个可以尝试的点，如果每个词独享一个转换矩阵，会不会效果更厉害呢？】\n注意到这些新向量的维度比输入词向量的维度要小（512–\u0026gt;64），并不是必须要小的，是为了让多头attention的计算更稳定。\n第二步，计算attention就是计算一个分值。对“Thinking Matchines”这句话，对“Thinking”（pos#1）计算attention 分值。我们需要计算每个词与“Thinking”的评估分，这个分决定着编码“Thinking”时（某个固定位置时），每个输入词需要集中多少关注度。\n这个分，通过“Thing”对应query-vector与所有词的key-vec依次做点积得到。所以当我们处理位置#1时，第一个分值是q1和k1的点积，第二个分值是q1和k2的点积。这也就是所谓的注意力得分. 第三步和第四步，除以8(\\(=\\sqrt{dim_{key}}\\))，这样梯度会更稳定。然后加上softmax操作，归一化分值使得全为正数且加和为1。\nsoftmax分值决定着在这个位置，每个词的表达程度（关注度）。很明显，这个位置的词应该有最高的归一化分数，但大部分时候总是有助于关注该词的相关的词。\n第五步，将softmax分值与value-vec按位相乘。保留关注词的value值，削弱非相关词的value值。\n第六步，将所有加权向量加和，产生该位置的self-attention的输出结果。 上述就是self-attention的计算过程，生成的向量流入前向网络。在实际应用中，上述计算是以速度更快的矩阵形式进行的。下面我们看下在单词级别的矩阵计算。\n矩阵计算 第一步，计算query/key/value matrix，将所有输入词向量合并成输入矩阵\\(X\\)，并且将其分别乘以权重矩阵\\(W^q, W^k,W^v\\)\n最后，鉴于我们使用矩阵处理，将步骤2~6合并成一个计算self-attention层输出的公式。 多头注意力机制 论文进一步增加了multi-headed的机制到self-attention上，在如下两个方面提高了attention层的效果：\n多头机制扩展了模型集中于不同位置的能力。在上面的例子中，z1只包含了其他词的很少信息，仅由实际自己词决定。在其他情况下，比如翻译 “The animal didn’t cross the street because it was too tired”时，我们想知道单词”it”指的是什么。 多头机制赋予attention多种子表达方式。像下面的例子所示，在多头下有多组query/key/value-matrix，而非仅仅一组（论文中使用8-heads）。每一组都是随机初始化，经过训练之后，输入向量可以被映射到不同的子表达空间中。 如果我们计算multi-headed self-attention的，分别有八组不同的Q/K/V matrix，我们得到八个不同的矩阵。 这会带来点麻烦，前向网络并不能接收八个矩阵，而是希望输入是一个矩阵，所以要有种方式处理下八个矩阵合并成一个矩阵。\n上述就是多头自注意机制的内容，我认为还仅是一部分矩阵，下面尝试着将它们放到一个图上可视化如下。\n#### 代码 下面实现一下多头注意力机制，在原论文中，实现的方法如下： 也就是对每个W进行多头的设置，即为原维度/head，然后拼接后，再经过\\(hd_v\\times d_{model}\\)的转换又得到原来的维度，代码的实现不太一样，代码是W还是\\(d_{model}\\times d_{model}\\)的矩阵然后得到q,k,v之后再进行截断，实现如下。\nclass MultiHeadedAttention(nn.Module): def __init__(self, h, d_model, dropout=0.1) -\u0026gt; None: # h为head，这里为8，d_model为embedding的维度，这里为512 super().__init__() assert d_model % h == 0 self.d_k = d_model // h # 64 self.h = h self.Q_Linear = nn.Linear(d_model, d_model) self.K_Linear = nn.Linear(d_model, d_model) self.V_Linear = nn.Linear(d_model, d_model) self.res_Linear = nn.Linear(d_model, d_model) self.attn = None self.dropout = nn.Dropout(p=dropout) def forward(self, query, key, value, mask=None): if mask is not None: mask = mask.unsqueeze(1) batch_size = query.size(0) query = self.Q_Linear(query).view(batch_size, -1, self.h, self.d_k) # (batch_size, seq_len, h, d_k)即(batch_size, seq_len, 8, 64) query = query.transpose(1, 2) # (batch_size, h, seq_len, d_k)即(batch_size, 8, seq_len, 64) key = self.K_Linear(key).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) value = self.V_Linear(value).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout) # x为(batch_size, h, seq_len, d_k) # attn为(batch_size, h, seq_len1, seq_len2) x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.h * self.d_k) # (batch_size, h, seq_len, d_k) -\u0026gt; (batch_size, seq_len, h, d_k) -\u0026gt; (batch_size, seq_len, h * d_k) = (batch_size, seq_len, 512) return self.res_Linear(x) Masked self-attention 在训练的时候，主要是消除后面的信息对预测的影响，因为decoder输入的是整个句子，也就是我们所谓的参考答案，而实际预测的时候就是预测后面的token，用不到后面的token，如果不mask掉，当前的token将看到“未来”，这不是我们想要的，因此必须要mask掉。\n其实decoder里的sequence mask与encoder里的padding mask异曲同工，padding mask其实很简单，就是为了使句子长度一致进行了padding，而为了避免关注padding的位置，进行了mask，具体的做法就是将这些位置的值变成负无穷，这样softmax之后就接近于0了。\n而sequence mask思想也差不多：\n假设现在解码器的输入”\u0026lt; s \u0026gt; who am i \u0026lt; e \u0026gt;“在分别乘上一个矩阵进行线性变换后得到了Q、K、V，且Q与K作用后得到了注意力权重矩阵（此时还未进行softmax操作），如图17所示。 此时已经计算得到了注意力权重矩阵。由第1行的权重向量可知，在解码第1个时刻时应该将20%（严格来说应该是经过softmax后的值）的注意力放到’\u0026lt; s \u0026gt;’上，30%的注意力放到’who’上等等。不过此时有一个问题就是，模型在实际的预测过程中只是将当前时刻之前（包括当前时刻）的所有时刻作为输入来预测下一个时刻，也就是说模型在预测时是看不到当前时刻之后的信息。因此，Transformer中的Decoder通过加入注意力掩码机制来解决了这一问题。 当然还要进行softmax等计算。\n在网上查了很多资料，说法都很不一样，不过我更倾向于这样的看法。而在预测的时候是用前面的输出结果作为输入的。\n几张图帮助理解： 后面还有padding mask，所有的self attention都要用这个，因为pad的位置没有任何意义。 实践一下加深理解： 首先我们来定义模型：\n# 词典数为10， 词向量维度为8 embedding = nn.Embedding(10, 8) # 定义Transformer，注意一定要改成eval模型，否则每次输出结果不一样 transformer = nn.Transformer(d_model=8, batch_first=True).eval() 接下来定义我们的src和tgt：\n# Encoder的输入 src = torch.LongTensor([[0, 1, 2, 3, 4]]) # Decoder的输入 tgt = torch.LongTensor([[4, 3, 2, 1, 0]]) 然后我们将[4]送给Transformer进行预测，模拟推理时的第一步：\ntransformer(embedding(src), embedding(tgt[:, :1]), # 这个就是用来生成阶梯式的mask的 tgt_mask=nn.Transformer.generate_square_subsequent_mask(1)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 然后我们将[4, 3]送给Transformer，模拟推理时的第二步：\ntransformer(embedding(src), embedding(tgt[:, :2]), tgt_mask=nn.Transformer.generate_square_subsequent_mask(2)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598], [ 1.2726, -0.3516, 0.6584, 0.3297, 1.1161, -1.4204, -1.5652, -0.0396]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 出的第一个向量和上面那个一模一样。\n最后我们再将tgt一次性送给transformer，模拟训练过程：\ntransformer(embedding(src), embedding(tgt), tgt_mask=nn.Transformer.generate_square_subsequent_mask(5)) tensor([[[ 1.4053, -0.4680, 0.8110, 0.1218, 0.9668, -1.4539, -1.4427, 0.0598], [ 1.2726, -0.3516, 0.6584, 0.3297, 1.1161, -1.4204, -1.5652, -0.0396], [ 1.4799, -0.3575, 0.8310, 0.1642, 0.8811, -1.3140, -1.5643, -0.1204], [ 1.4359, -0.6524, 0.8377, 0.1742, 1.0521, -1.3222, -1.3799, -0.1454], [ 1.3465, -0.3771, 0.9107, 0.1636, 0.8627, -1.5061, -1.4732, 0.0729]]], grad_fn=\u0026lt;NativeLayerNormBackward0\u0026gt;) 可以看到使用mask后就可以保证前面的结果都是不变的，不然如果没有mask则计算attention时因为计算注意力变化所以结果都会变化，这就是Mask self-attention的意义。 到这里self-attention就介绍完了\n代码 def attention(query, key, value, mask=None, dropout=None): d_k = query.size(-1) scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k) # 最后两个维度相乘，即为scores，再scale一下。 if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) # 将mask的位置的scores置为-1e9 # 实际上pad mask的时候，pad也会作为key与其它token对应的k,v计算score，pad mask只是消除pad作为k,v时候的影响。但在最后softmax的时候，将pad的损失值全部置为0 p_attn = F.softmax(scores, dim=-1) # 将scores进行softmax，得到p_attn，这里是在最后一个维度上softmax，因为对每个query的所有key进行softmax if dropout: p_attn = dropout(p_attn) return torch.matmul(p_attn, value), p_attn class MultiHeadedAttention(nn.Module): def __init__(self, h, d_model, dropout=0.1) -\u0026gt; None: # h为head，这里为8，d_model为embedding的维度，这里为512 super().__init__() assert d_model % h == 0 self.d_k = d_model // h # 64 self.h = h self.Q_Linear = nn.Linear(d_model, d_model) self.K_Linear = nn.Linear(d_model, d_model) self.V_Linear = nn.Linear(d_model, d_model) self.res_Linear = nn.Linear(d_model, d_model) self.attn = None self.dropout = nn.Dropout(p=dropout) def forward(self, query, key, value, mask=None): if mask is not None: mask = mask.unsqueeze(1) batch_size = query.size(0) query = self.Q_Linear(query).view(batch_size, -1, self.h, self.d_k) # (batch_size, seq_len, h, d_k)即(batch_size, seq_len, 8, 64) query = query.transpose(1, 2) # (batch_size, h, seq_len, d_k)即(batch_size, 8, seq_len, 64) key = self.K_Linear(key).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) value = self.V_Linear(value).view(batch_size, -1, self.h, self.d_k).transpose(1, 2) x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout) # x为(batch_size, h, seq_len, d_k) # attn为(batch_size, h, seq_len1, seq_len2) x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.h * self.d_k) # (batch_size, h, seq_len, d_k) -\u0026gt; (batch_size, seq_len, h, d_k) -\u0026gt; (batch_size, seq_len, h * d_k) = (batch_size, seq_len, 512) return self.res_Linear(x) 模型架构 下面是原始论文中的架构： self-attention上面已经讲的比较详细了，下面说一下其余的部分。\nFFN(前馈网络) 除了注意力以外，每一层都有一个前馈网络：两个线性层之间具有ReLU非线性：\n\\[ FFN(x) = max(0, xW_1+b_1)W_2+b_2 \\] 在通过注意力机制查看其他令牌之后，模型使用 FFN 块来处理这些新信息。\nclass PositionwiseFeedForward(nn.Module): def __init__(self, d_model, d_ff, dropout=0.1): super().__init__() self.w_1 = nn.Linear(d_model, d_ff) self.w_2 = nn.Linear(d_ff, d_model) self.dropout = nn.Dropout(p=dropout) def forward(self, x): return self.w_2(self.dropout(F.relu(self.w_1(x)))) 残差连接 残差连接非常简单（将块的输入添加到其输出），但同时也非常有用：它们缓解了通过网络的梯度流并允许堆叠很多层。解决了网络退化的问题。\n在 Transformer 中，在每个注意力和 FFN 块之后使用残差连接。在上图中，残差显示为围绕一个块到黄色 “Add \u0026amp; Norm”层的箭头。在“Add \u0026amp; Norm”部分， “Add”部分代表残差连接。\nLayer Norm “Add \u0026amp; Norm”层中的“Norm”部分 表示 Layer Normalization。它批量独立地标准化每个示例的向量表示 - 这样做是为了控制“流”到下一层。层归一化提高了收敛稳定性，有时甚至提高了质量。\n这里的scale和bias都是可以训练的参数。\n注意Layer Norm与Batch Norm是不同的，这里引用一下沐神的视频：\n这是Batch Norm的切法，即对每个特征进行norm。\n这是Layer norm的切法，即对每个样本进行norm。\n为什么用layer norm而不用Batch norm呢？\n当你的样本长度变化比较大的时候，使用batch norm计算的均值和方差波动比较大，而且batch norm需要记录全局的均值和方差，当遇到新的测试样本的时候，由于长度的原因，之前的均值方差可能就效果不太好了。\n但是如果使用layer norm 的话就没有那么多的问题，因为它是每个样本自己计算均值方差，不需要存在一个全局的均值方差，所以会稳定一点。\nclass LayerNorm(nn.Module): def __init__(self, features, eps=1e-6) -\u0026gt; None: super().__init__() self.a_2 = nn.Parameter(torch.ones(features)) self.b_2 = nn.Parameter(torch.zeros(features)) self.eps = eps def forward(self, x): mean = x.mean(-1, keepdim=True) std = x.std(-1, keepdim=True) return self.a_2 * (x - mean) / (std + self.eps) + self.b_2 class SublayerConnection(nn.Module): def __init__(self, size, dropout) -\u0026gt; None: super().__init__() self.norm = LayerNorm(size) self.dropout = nn.Dropout(p=dropout) def forward(self, x, sublayer): return x + self.dropout(sublayer(self.norm(x))) # 这里和论文不同，先norm再扔给sublayer（比如多头注意力、ffd）,理论上是self.norm(x+self.dropout(sublayer(x))) 位置编码(position encoding) (Position Embedding是学习式，而Position Encoding为固定式) 请注意，由于 Transformer 不包含递归或卷积，它不知道输入标记(token)的顺序。因此，我们必须让模型明确地知道标记的位置。为此，我们有两组嵌入：用于标记（我们总是这样做）和用于位置（该模型所需的新嵌入）。那么令牌的输入表示是两个嵌入的总和：令牌和位置。\n位置嵌入是可以学习的，但作者发现固定的嵌入不会影响质量。Transformer 中使用的固定位置编码是：\n\\[ PE_{pos,2i} = sin(\\frac{pos}{10000^{\\frac{2i}{d_{model}}}}) \\]\n\\[ PE_{pos,2i+1} = cos(\\frac{pos}{10000^{\\frac{2i}{d_{model}}}}) \\] 可以看到，每个词的维度都是512维，假设句子长度为10，则位置编码的计算如上图所示。\n得到位置编码后，将位置编码与词嵌入简单相加即可。 #### 代码\nclass PositionalEncoding(nn.Module): def __init__(self, d_model, dropout, max_len=5000) -\u0026gt; None: super().__init__() self.dropout = nn.Dropout(p=dropout) position_embedding = torch.zeros(max_len, d_model) position = torch.arange(0, max_len).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model)) position_embedding[:, 0::2] = torch.sin(position * div_term) position_embedding[:, 1::2] = torch.cos(position * div_term) position_embedding = position_embedding.unsqueeze(0) # 增加一维预留batch size的位置，所以后面forward要在第二维上选取序列长度 self.register_buffer(\u0026#39;PositionalEncoding\u0026#39;, position_embedding) def forward(self, x): return self.dropout(x + Variable(self.PositionalEncoding[:, :x.size(1)], requires_grad=False)) 这里为了计算做了转换。 ### Padding Mask\n对于输入序列一般我们都要进行padding补齐，也就是说设定一个统一长度N，在较短的序列后面填充0到长度为N。对于那些补零的数据来说，我们的attention机制不应该把注意力放在这些位置上，所以我们需要进行一些处理。具体的做法是，把这些位置的值加上一个非常大的负数(负无穷)，这样经过softmax后，这些位置的权重就会接近0。Transformer的padding mask实际上是一个张量，每个值都是一个Boolean，值为false的地方就是要进行处理的地方。\nlabel smoothing(标签平滑) 神经网络会促使自身往正确标签和错误标签差值最大的方向学习，在训练数据较少，不足以表征所有的样本特征的情况下，会导致网络过拟合。\nlabel smoothing可以解决上述问题，这是一种正则化策略，主要是通过soft one-hot来加入噪声，减少了真实样本标签的类别在计算损失函数时的权重，最终起到抑制过拟合的效果。\n增加label smoothing后真实的概率分布有如下改变：\n代码 class LabelSmoothing(nn.Module): # [标签平滑](../Deep%20Learning/训练trick/标签平滑.md)损失函数 def __init__(self, size, padding_idx, smoothing=0.0) -\u0026gt; None: super().__init__() self.criterion = nn.KLDivLoss(size_average=False) self.padding_idx = padding_idx self.confidence = 1.0 - smoothing self.smoothing = smoothing self.size = size self.true_dist = None def forward(self, x, target): # x的shape为(batch.size * seq.len, target.vocab.size) # y的shape是(batch.size * seq.len) # x=logits，(seq.len, target.vocab.size) # 每一行，代表一个位置的词 # 类似于：假设seq.len=3, target.vocab.size=5 # x中保存的是log(prob) #x = tensor([[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233]]) # target 类似于： # target = tensor([2, 1, 0])，torch.size=(3) assert x.size(1) == self.size true_dist = x.data.clone() # true_dist = tensor([[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233]]) true_dist.fill_(self.smoothing / (self.size - 2)) # true_dist = tensor([[0.1333, 0.1333, 0.1333, 0.1333, 0.1333], #[0.1333, 0.1333, 0.1333, 0.1333, 0.1333], #[0.1333, 0.1333, 0.1333, 0.1333, 0.1333]]) # 注意，这里分母target.vocab.size-2是因为 # (1) 最优值 0.6要占一个位置； # (2) 填充词 \u0026lt;blank\u0026gt; 要被排除在外 # 所以被激活的目标语言词表大小就是self.size-2 true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence) # target.data.unsqueeze(1) -\u0026gt; # tensor([[2], #[1], #[0]]); shape=torch.Size([3, 1]) # self.confidence = 0.6 # 根据target.data的指示，按照列优先(1)的原则，把0.6这个值 # 填入true_dist: 因为target.data是2,1,0的内容， # 所以，0.6填入第0行的第2列（列号，行号都是0开始） # 0.6填入第1行的第1列 # 0.6填入第2行的第0列： # true_dist = tensor([[0.1333, 0.1333, 0.6000, 0.1333, 0.1333], #[0.1333, 0.6000, 0.1333, 0.1333, 0.1333], #[0.6000, 0.1333, 0.1333, 0.1333, 0.1333]]) true_dist[:, self.padding_idx] = 0 # true_dist = tensor([[0.0000, 0.1333, 0.6000, 0.1333, 0.1333], #[0.0000, 0.6000, 0.1333, 0.1333, 0.1333], #[0.0000, 0.1333, 0.1333, 0.1333, 0.1333]]) # 设置true_dist这个tensor的第一列的值全为0 # 因为这个是填充词\u0026#39;\u0026lt;blank\u0026gt;\u0026#39;所在的id位置，不应该计入 # 目标词表。需要注意的是，true_dist的每一列，代表目标语言词表 #中的一个词的id mask = torch.nonzero(target.data == self.padding_idx) # mask = tensor([[2]]), 也就是说，最后一个词 2,1,0中的0， # 因为是\u0026#39;\u0026lt;blank\u0026gt;\u0026#39;的id，所以通过上面的一步，把他们找出来 # 如果不加上nonzero，那么mask的shape就是torch.Size([3]) if mask.dim() \u0026gt; 0: true_dist.index_fill_(0, mask.squeeze(), 0.0) # 当target reference序列中有0这个\u0026#39;\u0026lt;blank\u0026gt;\u0026#39;的时候，则需要把 # 这一行的值都清空。 # 在一个batch里面的时候，可能两个序列长度不一，所以短的序列需要 # pad \u0026#39;\u0026lt;blank\u0026gt;\u0026#39;来填充，所以会出现类似于(2,1,0)这样的情况 # true_dist = tensor([[0.0000, 0.1333, 0.6000, 0.1333, 0.1333], # [0.0000, 0.6000, 0.1333, 0.1333, 0.1333], # [0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]) self.true_dist = true_dist return self.criterion(x, Variable(true_dist, requires_grad=False)) # 这一步就是调用KL loss来计算 # x = tensor([[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233], #[-20.7233, -1.6094, -0.3567, -2.3026, -20.7233]]) # true_dist=tensor([[0.0000, 0.1333, 0.6000, 0.1333, 0.1333], # [0.0000, 0.6000, 0.1333, 0.1333, 0.1333], # [0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]) 预测 预测过程与一般seq2seq不同的是，t时刻是将1到t-1时刻所有的预测结果作为序列进行预测，而seq2seq只是使用前一时刻的输出作为当前时刻的输入，这里困扰了我很久，实现了transformer代码后对比李沐老师的代码才理解。\n其中seq2seq: transformer: 注意ys最后与之前的ys使用cat函数合并在一起。 ## 总结 Transformer还有很多的模型细节，以后遇到了再记录一下，在面试中很容易问到这些细节，因此可以参考面经边学习边记录，可以查缺补漏也可以学到新的东西。接下来把代码复现一下可以加深理解，并且提高自己的代码水平和实践能力。\n一些问题 Transformer在哪里做了权重共享，为什么可以做权重共享？ Transformer在两个地方进行了权重共享：\n（1）Encoder和Decoder间的Embedding层权重共享；\n（2）Decoder中Embedding层和FC层权重共享。\n对于（1），《Attention is all you need》中Transformer被应用在机器翻译任务中，源语言和目标语言是不一样的，但它们可以共用一张大词表，对于两种语言中共同出现的词（比如：数字，标点等等）可以得到更好的表示，而且对于Encoder和Decoder，嵌入时都只有对应语言的embedding会被激活，因此是可以共用一张词表做权重共享的。\n论文中，Transformer词表用了bpe来处理，所以最小的单元是subword。英语和德语同属日耳曼语族，有很多相同的subword，可以共享类似的语义。而像中英这样相差较大的语系，语义共享作用可能不会很大。\n但是，共用词表会使得词表数量增大，增加softmax的计算时间，因此实际使用中是否共享可能要根据情况权衡。\n对于（2），Embedding层可以说是通过onehot去取到对应的embedding向量，FC层可以说是相反的，通过向量（定义为 x）去得到它可能是某个词的softmax概率，取概率最大（贪婪情况下）的作为预测值。\n那哪一个会是概率最大的呢？在FC层的每一行量级相同的前提下，理论上和 x 相同的那一行对应的点积和softmax概率会是最大的。\n因此，Embedding层和FC层权重共享，Embedding层中和向量 x 最接近的那一行对应的词，会获得更大的预测概率。实际上，Decoder中的Embedding层和FC层有点像互为逆过程。\n通过这样的权重共享可以减少参数的数量，加快收敛。\n为什么除以根号d 论文中的解释是：向量的点积结果会很大，将 softmax 函数 push 到梯度很小的区域，scaled 会缓解这种现象。\n\\[\\frac{\\partial\\mathbf{y}}{\\partial\\mathbf{x}}=\\mathrm{diag}(\\mathbf{y})-\\mathbf{y}\\mathbf{y}^T\\] 当\\(\\mathbf{y} =\\)softmax\\(( \\mathbf{x} )\\)时，\\(\\mathbf{y}\\)对\\(\\mathbf{x}\\)的梯度为： 这是一个jacobi矩阵\\(^{+}\\),表示y的每一个元素对x每一个元素的导数是什么。 展开： \\[\\frac{\\partial\\mathbf{y}}{\\partial\\mathbf{x}}=\\begin{bmatrix}y_1\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\\\0\u0026amp;y_2\u0026amp;\\cdots\u0026amp;0\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\\\\0\u0026amp;0\u0026amp;\\cdots\u0026amp;y_d\\end{bmatrix}-\\begin{bmatrix}y_1^2\u0026amp;y_1y_2\u0026amp;\\cdots\u0026amp;y_1y_d\\\\y_2y_1\u0026amp;y_2^2\u0026amp;\\cdots\u0026amp;y_2y_d\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\\\\y_dy_1\u0026amp;y_dy_2\u0026amp;\\cdots\u0026amp;y_d^2\\end{bmatrix}\\] 根据前面的讨论，当输入 \\(\\mathbf{x}\\) 的某一个元素较大时，softmax 会把大部分概率分布\\(^{+}\\)分配给最大的元 素，假设我们的输入数量级很大，那么就将产生一个接近 one-hot 的向量 \\[\\mathbf{y}\\approx[1,0,\\cdots,0]^\\top \\] 此时上面的矩阵变为如下形式 \\[\\frac{\\partial\\mathbf{y}}{\\partial\\mathbf{x}}\\approx\\begin{bmatrix}1\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\\\\0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\end{bmatrix}-\\begin{bmatrix}1\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\\\0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\\\\\vdots\u0026amp;\\vdots\u0026amp;\\ddots\u0026amp;\\vdots\\\\0\u0026amp;0\u0026amp;\\cdots\u0026amp;0\\end{bmatrix}=\\mathbf{0}\\] 也就是所有的梯度都接近0 除以\\(\\sqrt{ d }\\)后就使x的分布更加平缓，从而防止梯度消失。\nfrom scipy.special import softmax import numpy as np def test_gradient(dim, time_steps=50, scale=1.0): # Assume components of the query and keys are drawn from N(0, 1) independently q = np.random.randn(dim) ks = np.random.randn(time_steps, dim) x = np.sum(q * ks, axis=1) / scale # x.shape = (time_steps,) y = softmax(x) grad = np.diag(y) - np.outer(y, y)# softmax gradient(dy/dx) return np.max(np.abs(grad)) # the maximum component of gradients NUMBER_OF_EXPERIMENTS = 5 # results of 5 random runs without scaling print([test_gradient(100) for _ in range(NUMBER_OF_EXPERIMENTS)]) print([test_gradient(1000) for _ in range(NUMBER_OF_EXPERIMENTS)]) # results of 5 random runs with scaling print([test_gradient(100, scale=np.sqrt(100)) for _ in range(NUMBER_OF_EXPERIMENTS)]) print([test_gradient(1000, scale=np.sqrt(1000)) for _ in range(NUMBER_OF_EXPERIMENTS)]) 输出可看到下面的梯度比上面的梯度更大。\n这时又有一个问题，为什么多分类的softmax+交叉熵不需要除以东西呢？这是因为交叉熵中有一个log，log_softmax的梯度和刚才算出来的不同，就算输入的某一个x过大也不会梯度消失。所以就又可以推断出softmax+MSE会导致梯度消失，因为MSE中没有Log，这是为什么分类任务不使用MSE损失函数的原因之一。 ### 为什么 Transformer 需要进行 Multi-head Attention 实验证明多头是必要的，8/16个头都可以取得更好的效果，但是超过16个反而效果不好。每个头关注的信息不同，但是头之间的差异随着层数增加而减少。并且不是所有头都有用，有工作尝试剪枝，可以得到更好的表现。\n论文中提到模型分为多个头，形成多个子空间，每个头关注不同方面的信息。\n那为什么每个头的维度要降呢? 一言蔽之的话，大概是：在不增加时间复杂度的情况下，同时，借鉴CNN多核的思想，在更低的维度，在多个独立的特征空间，更容易学习到更丰富的特征信息。 ### 为什么 Transformer 的 Embedding 最后要乘dmodel 具体的原因是，如果使用 Xavier 初始化，Embedding 的方差为 1/d_model，当d_model非常大时，矩阵中的每一个值都会减小。通过乘一个 dmodel 可以将方差恢复到1。\n因为Position Encoding是通过三角函数算出来的，值域为[-1, 1]。所以当加上 Position Encoding 时，需要放大 embedding 的数值，否则规模不一致相加后会丢失信息。\n因为 Bert 使用的是学习式的Embedding，所以 Bert 这里就不需要放大。 # 参考\nBert/Transformer 被忽视的细节（或许可以用来做面试题） - 知乎 (zhihu.com)\n","date":"2022-06-08T00:00:00Z","permalink":"/transformer/","title":"Transformer"},{"content":"最大子序和 https://leetcode-cn.com/problems/maximum-subarray/\n一开始直接暴力，结果tle了最后\nclass Solution: def maxSubArray(nums): res = -float(\u0026#39;inf\u0026#39;) for i in range(len(nums)): for j in range(i,len(nums)): res = max(res,sum(nums[i:j+1])) return res 这说明在leetcode尽量不要嵌套循环，大概率Tle\nclass Solution: def maxSubArray(nums): for i in range(1,len(nums)): maxs = max(nums[i-1]+nums[i],nums[i]) nums[i] = maxs return max(nums) 最后巧妙地利用了替换的思想，将每次相加的值和当前比较，并将当前替换为较大的那个值，最后求整个列表的最大值。\n","date":"2022-06-08T00:00:00Z","permalink":"/%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C/","title":"最大子序和"},{"content":"使用最小花费爬楼梯 每日一题刷到的。\n动态规划类型的题目，重点就是找状态转移方程，因为我不太熟练，对动态规划的题目做的比较少，所以WA了好几次。\nclass Solution: def minCostClimbingStairs(cost): res = [] #res[i]就是到第i阶梯时最小的花费 res.append(cost[0]) #到第一阶梯最小就是0+cost[0] res.append(cost[1]) #第二阶梯最小就是0+cost[1] #状态转移方程:res[i] = min(res[i-1],res[i-2])+cost[i] for i in range(2,len(cost)): res.append(min(res[i-1],res[i-2])+cost[i]) # return min(res[-1],res[-2]) 踏上第i级台阶有两种方法：\n先踏上第i-2级台阶（最小总花费dp[i-2]），再直接迈两步踏上第i级台阶（花费cost[i]），最小总花费dp[i-2] + cost[i]；\n先踏上第i-1级台阶（最小总花费dp[i-1]），再迈一步踏上第i级台阶（花费cost[i]），最小总花费dp[i-1] + cost[i]；\n上述为引用的题解的说明，更加深了对动态规划的理解\n","date":"2022-06-01T00:00:00Z","permalink":"/%E4%BD%BF%E7%94%A8%E6%9C%80%E5%B0%8F%E8%8A%B1%E8%B4%B9%E7%88%AC%E6%A5%BC%E6%A2%AF/","title":"使用最小花费爬楼梯"},{"content":"CRF 概率图模型与无向图 图是由结点和连接结点的边组成的集合。结点和边分别记作v和e，结点和边的集合分别记作V和E，图记作\\(G=(V, E)\\)。\n无向图是指没有方向的图。\n概率图模型是由图表示的概率分布。设有联合概率分布P(Y), Y是一组随机变量，由无向图\\(G=(V,E)\\)表示概率分布P(Y)，即在图G中，结点\\(v\\in V\\)表示一个随机变量\\(Y_v\\)，\\(Y=(Y_v)\\_{v\\in V}\\)，边e表示随机变量之间的依赖关系。\n概率无向图模型 设有联合概率分布P(Y)，由无向图\\(G=(V,E)\\)表示，在图G中，结点表示随机变量，边表示随机变量之间的依赖关系。如果联合概率分布满足成对、局部或全局马尔科夫性，就称此联合概率分布称为概率无向图模型，或马尔科夫随机场。\n因子分解 首先给出无向图的团和最大团的定义：\n无向图G中任何两个结点均有边连接的结点子集称为团。若C是无向图G的一个团，并且不能再加进任何一个G的结点使其成为更大的团，则称此C为最大团。\n将无向图模型的联合概率分布表示为其最大团上的随机变量的函数的乘积形式的操作，称为概率无向图模型的因子分解。\n给定概率无向图模型, 设其无向图为 \\(G, C\\) 为 \\(G\\) 上的最大团, \\(Y_C\\) 表示 \\(C\\) 对应的 随机变量。那么概率无向图模型的联合概率分布 \\(P(Y)\\) 可写作图中所有最大团 \\(C\\) 上的 函数 \\(\\Psi_C\\left(Y_C\\right)\\) 的乘积形式, 即\n\\[ P(Y)=\\frac{1}{Z} \\prod_C \\Psi_C\\left(Y_C\\right) \\]\n其中, \\(Z\\) 是规范化因子 (normalization factor), 由式\n\\[ Z=\\sum_Y \\prod_C \\Psi_C\\left(Y_C\\right) \\]\n给出。规范化因子保证 \\(P(Y)\\) 构成一个概率分布。函数 \\(\\Psi_C\\left(Y_C\\right)\\) 称为势函数 (potential function)。这里要求势函数 \\(\\Psi_C\\left(Y_C\\right)\\) 是严格正的, 通常定义为指数函数:\n\\[ \\Psi_C\\left(Y_C\\right)=\\exp \\\\{-E\\left(Y_C\\right)\\\\\\} \\]\n条件随机场 条件随机场是指给定随机变量X的条件下，随机变量Y的马尔科夫随机场。一般的条件随机场主要是指线性链条件随机场，可以用于标注等问题。这里的\\(P(Y|X)\\)中，Y是输出变量，表示标注序列，X是输入变量，表示需要标注的观察序列。\n一般的条件随机场 (条件随机场) 设 \\(X\\) 与 \\(Y\\) 是随机变量, \\(P(Y \\mid X)\\) 是在给定 \\(X\\) 的条件 下 \\(Y\\) 的条件概率分布。若随机变量 \\(Y\\) 构成一个由无向图 \\(G=(V, E)\\) 表示的马尔可夫 随机场, 即\n\\[ P\\left(Y_v \\mid X, Y_w, w \\neq v\\right)=P\\left(Y_v \\mid X, Y_w, w \\sim v\\right) \\]\n对任意结点 \\(v\\) 成立, 则称条件概率分布 \\(P(Y \\mid X)\\) 为条件随机场。式中 \\(w \\sim v\\) 表示在 图 \\(G=(V, E)\\) 中与结点 \\(v\\) 有边连接的所有结点 \\(w, w \\neq v\\) 表示结点 \\(v\\) 以外的所有结 点, \\(Y_v, Y_u\\) 与 \\(Y_w\\) 为结点 \\(v, u\\) 与 \\(w\\) 对应的随机变量。\n线性链条件随机场 设\\(X=(X_1,X_2, \\dots, X_n), \\quad Y=(Y_1, Y_2, \\dots , Y_n)\\)均为线性链表示的随机变量序列，若在给定随机变量序列X的条件下，随机变量Y的条件概率分布\\(P(Y|X)\\)构成条件随机场，即满足马尔科夫性\n\\[ P\\left(Y_i \\mid X, Y_1, \\cdots, Y_{i-1}, Y_{i+1}, \\cdots, Y_n\\right)=P\\left(Y_i \\mid X, Y_{i-1}, Y_{i+1}\\right) \\]\n\\(i=1,2, \\cdots, n\\) (在 \\(i=1\\) 和 \\(n\\) 时只考虑单边) 则称 \\(P(Y \\mid X)\\) 为线性链条件随机场。在标注问题中, \\(X\\) 表示输入观测序列, \\(Y\\) 表示对 应的输出标记序列或状态序列。\n线性链条件随机场参数化形式 根据因子分解, 可以给出线性链条件随机场 \\(P(Y \\mid X)\\) 的因子分解式, 各因子是定 义在相邻两个结点 (最大团) 上的势函数。 (线性链条件随机场的参数化形式) 设 \\(P(Y \\mid X)\\) 为线性链条件随机 场, 则在随机变量 \\(X\\) 取值为 \\(x\\) 的条件下, 随机变量 \\(Y\\) 取值为 \\(y\\) 的条件概率具有如下 形式:\n\\[ P(y \\mid x)=\\frac{1}{Z(x)} \\exp \\left(\\sum_{i, k} \\lambda_k t_k\\left(y_{i-1}, y_i, x, i\\right)+\\sum_{i, l} \\mu_l s_l\\left(y_i, x, i\\right)\\right) \\]\n其中,\n\\[ Z(x)=\\sum_y \\exp \\left(\\sum_{i, k} \\lambda_k t_k\\left(y_{i-1}, y_i, x, i\\right)+\\sum_{i, l} \\mu_l s_l\\left(y_i, x, i\\right)\\right) \\]\n式中, \\(t_k\\) 和 \\(s_l\\) 是特征函数, \\(\\lambda_k\\) 和 \\(\\mu_l\\) 是对应的权值。 \\(Z(x)\\) 是规范化因子, 求和是在所 有可能的输出序列上进行的。 这两个式子是线性链条件随机场模型的基本形式, 表示给定输入序列 \\(x\\), 对输出序列 \\(y\\) 预测的条件概率。\\(t_k\\) 是定义在边上的特 征函数, 称为转移特征, 依赖于当前和前一个位置; \\(s_l\\) 是定义在结点上的特征函数, 称为状态特征, 依赖于当前位置。 \\(t_k\\) 和 \\(s_l\\) 都依赖于位置, 是局部特征函数。通常, 特 征函数 \\(t_k\\) 和 \\(s_l\\) 取值为 1 或 0 ; 当满足特征条件时取值为 1 , 否则为 0 。条件随机场完 全由特征函数 \\(t_k, s_l\\) 和对应的权值 \\(\\lambda_k, \\mu_l\\) 确定。 线性链条件随机场也是对数线性模型 (log linear model)。\n条件随机场的简化形式 为简便起见, 首先将转移特征和状态特征及其权值用统一的符号表示。设有 \\(K_1\\) 个转移特征, \\(K_2\\) 个状态特征, \\(K=K_1+K_2\\), 记\n\\[ f_k\\left(y_{i-1}, y_i, x, i\\right)= \\begin{cases}t_k\\left(y_{i-1}, y_i, x, i\\right), \u0026amp; k=1,2, \\cdots, K_1 \\\\\\\\ s_l\\left(y_i, x, i\\right), \u0026amp; k=K_1+l ; l=1,2, \\cdots, K_2\\end{cases} \\]\n然后, 对转移与状态特征在各个位置 \\(i\\) 求和, 记作\n\\[ f_k(y, x)=\\sum_{i=1}^n f_k\\left(y_{i-1}, y_i, x, i\\right), \\quad k=1,2, \\cdots, K \\]\n用 \\(w_k\\) 表示特征 \\(f_k(y, x)\\) 的权值, 即\n\\[ w_k= \\begin{cases}\\lambda_k, \u0026amp; k=1,2, \\cdots, K_1 \\\\\\\\ \\mu_l, \u0026amp; k=K_1+l ; l=1,2, \\cdots, K_2\\end{cases} \\]\n于是, 条件随机场可表示为\n\\[ \\begin{aligned} P(y \\mid x) \u0026amp;=\\frac{1}{Z(x)} \\exp \\sum_{k=1}^K w_k f_k(y, x) \\\\\\\\ Z(x) \u0026amp;=\\sum_y \\exp \\sum_{k=1}^K w_k f_k(y, x) \\end{aligned} \\]\n若以 \\(w\\) 表示权值向量, 即\n\\[ w=\\left(w_1, w_2, \\cdots, w_K\\right)^{\\mathrm{T}} \\]\n以 \\(F(y, x)\\) 表示全局特征向量, 即\n\\[ F(y, x)=\\left(f_1(y, x), f_2(y, x), \\cdots, f_K(y, x)\\right)^{\\mathrm{T}} \\]\n则条件随机场可以写成向量 \\(w\\) 与 \\(F(y, x)\\) 的内积的形式:\n\\[ P_w(y \\mid x)=\\frac{\\exp (w \\cdot F(y, x))}{Z_w(x)} \\]\n其中,\n\\[ Z_w(x)=\\sum_y \\exp (w \\cdot F(y, x)) \\]\n矩阵形式 对每个 标记序列引进特殊的起点和终点状态标记 \\(y_0=\\) start 和 \\(y_{n+1}=s t o p\\), 这时标注序列 的概率 \\(P_w(y \\mid x)\\) 可以通过矩阵形式表示并有效计算。 对观测序列 \\(x\\) 的每一个位置 \\(i=1,2, \\cdots, n+1\\), 由于 \\(y_{i-1}\\) 和 \\(y_i\\) 在 \\(m\\) 个标记中 取值, 可以定义一个 \\(m\\) 阶矩阵随机变量\n\\[ M_i(x)=\\left[M_i\\left(y_{i-1}, y_i \\mid x\\right)\\right] \\]\n矩阵随机变量的元素为\n\\[ \\begin{aligned} \u0026amp;M_i\\left(y_{i-1}, y_i \\mid x\\right)=\\exp \\left(W_i\\left(y_{i-1}, y_i \\mid x\\right)\\right) \\\\\\\\ \u0026amp;W_i\\left(y_{i-1}, y_i \\mid x\\right)=\\sum_{k=1}^K w_k f_k\\left(y_{i-1}, y_i, x, i\\right) \\end{aligned} \\]\n这里 \\(w_k\\) 和 \\(f_k\\) 分别由前面的式子给出, \\(y_{i-1}\\) 和 \\(y_i\\) 是标记随机变量 \\(Y_{i-1}\\) 和 \\(Y_i\\) 的取值。 这样, 给定观测序列 \\(x\\), 相应标记序列 \\(y\\) 的非规范化概率可以通过该序列 \\(n+1\\) 个矩阵的适当元素的乘积 \\(\\prod_{i=1}^{n+1} M_i\\left(y_{i-1}, y_i \\mid x\\right)\\) 表示。于是, 条件概率 \\(P_w(y \\mid x)\\) 是\n\\[ P_w(y \\mid x)=\\frac{1}{Z_w(x)} \\prod_{i=1}^{n+1} M_i\\left(y_{i-1}, y_i \\mid x\\right) \\]\n其中, \\(Z_w(x)\\) 为规范化因子, 是 \\(n+1\\) 个矩阵的乘积的 (start, stop) 元素, 即\n\\[ Z_w(x)=\\left[M_1(x) M_2(x) \\cdots M_{n+1}(x)\\right]_{\\text {start,stop }} \\]\n注意, \\(y_0=\\) start 与 \\(y_{n+1}=\\) stop 表示开始状态与终止状态, 规范化因子 \\(Z_w(x)\\) 是以 start 为起点 stop为终点通过状态的所有路径 \\(y_1 y_2 \\cdots y_n\\) 的非规范化概率 \\(\\prod_{i=1}^{n+1} M_i\\left(y_{i-1}, y_i \\mid x\\right)\\) 之和。\n概率计算问题 与HMM类似，引入前向和后向变量，递归的计算概率和一些期望值。\n前向-后向算法 对每个指标 \\(i=0,1, \\cdots, n+1\\), 定义前向向量 \\(\\alpha_i(x)\\) :\n\\[ \\alpha_0(y \\mid x)= \\begin{cases}1, \u0026amp; y=\\text { start } \\\\\\\\ 0, \u0026amp; \\text { 否则 }\\end{cases} \\]\n递推公式为\n\\[ \\alpha_i^{\\mathrm{T}}\\left(y_i \\mid x\\right)=\\alpha_{i-1}^{\\mathrm{T}}\\left(y_{i-1} \\mid x\\right)\\left[M_i\\left(y_{i-1}, y_i \\mid x\\right)\\right], \\quad i=1,2, \\cdots, n+1 \\]\n又可表示为\n\\[ \\alpha_i^{\\mathrm{T}}(x)=\\alpha_{i-1}^{\\mathrm{T}}(x) M_i(x) \\]\n\\(\\alpha_i\\left(y_i \\mid x\\right)\\) 表示在位置 \\(i\\) 的标记是 \\(y_i\\) 并且从 1 到 \\(i\\) 的前部分标记序列的非规范化概 率, \\(y_i\\) 可取的值有 \\(m\\) 个, 所以 \\(\\alpha_i(x)\\) 是 \\(m\\) 维列向量。 同样, 对每个指标 \\(i=0,1, \\cdots, n+1\\), 定义后向向量 \\(\\beta_i(x)\\) :\n\\[ \\begin{aligned} \\beta_{n+1}\\left(y_{n+1} \\mid x\\right) \u0026amp;= \\begin{cases}1, \u0026amp; y_{n+1}=\\text { stop } \\\\\\\\ 0, \u0026amp; \\text { 否则 }\\end{cases} \\\\\\\\ \\beta_i\\left(y_i \\mid x\\right) \u0026amp;=\\left[M_{i+1}\\left(y_i, y_{i+1} \\mid x\\right)\\right] \\beta_{i+1}\\left(y_{i+1} \\mid x\\right) \\end{aligned} \\]\n又可表示为\n\\[ \\beta_i(x)=M_{i+1}(x) \\beta_{i+1}(x) \\]\n\\(\\beta_i\\left(y_i \\mid x\\right)\\) 表示在位置 \\(i\\) 的标记为 \\(y_i\\) 并且从 \\(i+1\\) 到 \\(n\\) 的后部分标记序列的非规范化 概率。\n概率计算 按照前向-后向向量的定义, 很容易计算标记序列在位置 \\(i\\) 是标记 \\(y_i\\) 的条件概率 和在位置 \\(i-1\\) 与 \\(i\\) 是标记 \\(y_{i-1}\\) 和 \\(y_i\\) 的条件概率:\n\\[ P\\left(Y_i=y_i \\mid x\\right)=\\frac{\\alpha_i^{\\mathrm{T}}\\left(y_i \\mid x\\right) \\beta_i\\left(y_i \\mid x\\right)}{Z(x)} \\]\n\\[ P\\left(Y_{i-1}=y_{i-1}, Y_i=y_i \\mid x\\right)=\\frac{\\alpha_{i-1}^{\\mathrm{T}}\\left(y_{i-1} \\mid x\\right) M_i\\left(y_{i-1}, y_i \\mid x\\right) \\beta_i\\left(y_i \\mid x\\right)}{Z(x)} \\]\n其中,\n\\[ Z(x)=\\alpha_n^{\\mathrm{T}}(x) \\mathbf{1}=1 \\beta_1(x) \\]\n预测问题 维特比算法 还是使用维特比算法。\n\\[ \\begin{aligned} y^* \u0026amp;=\\arg \\max_y P_w(y \\mid x) \\\\\\\\ \u0026amp;=\\arg \\max_y \\frac{\\exp (w \\cdot F(y, x))}{Z_w(x)} \\\\\\\\ \u0026amp;=\\arg \\max_y \\exp (w \\cdot F(y, x)) \\\\\\\\ \u0026amp;=\\arg \\max_y(w \\cdot F(y, x)) \\end{aligned} \\]\n于是, 条件随机场的预测问题成为求非规范化概率最大的最优路径问题\n\\[ \\max_y(w \\cdot F(y, x)) \\]\n这里, 路径表示标记序列。其中,\n\\[ \\begin{aligned} w \u0026amp;=\\left(w_1, w_2, \\cdots, w_K\\right)^{\\mathrm{T}} \\\\\\\\ F(y, x) \u0026amp;=\\left(f_1(y, x), f_2(y, x), \\cdots, f_K(y, x)\\right)^{\\mathrm{T}} \\\\\\\\ f_k(y, x) \u0026amp;=\\sum_{i=1}^n f_k\\left(y_{i-1}, y_i, x, i\\right), \\quad k=1,2, \\cdots, K \\end{aligned} \\]\n注意, 这时只需计算非规范化概率, 而不必计算概率, 可以大大提高效率。为了求解最 优路径, 写成如下形式:\n\\[ \\max_y \\sum_{i=1}^n w \\cdot F_i\\left(y_{i-1}, y_i, x\\right) \\]\n其中,\n\\[ F_i\\left(y_{i-1}, y_i, x\\right)=\\left(f_1\\left(y_{i-1}, y_i, x, i\\right), f_2\\left(y_{i-1}, y_i, x, i\\right), \\cdots, f_K\\left(y_{i-1}, y_i, x, i\\right)\\right)^{\\mathrm{T}} \\]\n是局部特征向量。 下面叙述维特比算法。首先求出位置 1 的各个标记 \\(j=1,2, \\cdots, m\\) 的非规范化概率:\n\\[ \\delta_1(j)=w \\cdot F_1\\left(y_0=\\text { start, } y_1=j, x\\right), \\quad j=1,2, \\cdots, m \\]\n一般地, 由递推公式, 求出到位置 \\(i\\) 的各个标记 \\(l=1,2, \\cdots, m\\) 的非规范化概率的最 大值, 同时记录非规范化概率最大值的路径\n\\[ \\begin{gathered} \\delta_i(l)=\\max_{1 \\leqslant j \\leqslant m}\\left\\\\{\\delta_{i-1}(j)+w \\cdot F_i\\left(y_{i-1}=j, y_i=l, x\\right)\\right\\\\\\}, \\quad l=1,2, \\cdots, m \\\\\\\\ \\Psi_i(l)=\\arg \\max_{1 \\leqslant j \\leqslant m}\\left\\\\{\\delta_{i-1}(j)+w \\cdot F_i\\left(y_{i-1}=j, y_i=l, x\\right)\\right\\\\\\}, \\quad l=1,2, \\cdots, m \\end{gathered} \\]\n直到 \\(i=n\\) 时终止。这时求得非规范化概率的最大值为\n\\[ \\operatorname{max}_y(w \\cdot F(y, x))=\\max_{1 \\leqslant j \\leqslant m} \\delta_n(j) \\]\n及最优路径的终点\n\\[ y_n^* =\\arg \\max_{1 \\leqslant j \\leqslant m} \\delta_n(j) \\]\n由此最优路径终点返回,\n\\[ y_i^* =\\Psi_{i+1}\\left(y_{i+1}^* \\right), \\quad i=n-1, n-2, \\cdots, 1 \\]\n求得最优路径 \\(y^* =\\left(y_1^* , y_2^* , \\cdots, y_n^* \\right)^{\\mathrm{T}}\\) 。\n综上所述, 得到条件随机场预测的维特比算法。\n(条件随机场预测的维特比算法) 输入: 模型特征向量 \\(F(y, x)\\) 和权值向量 \\(w\\), 观测序列 \\(x=\\left(x_1, x_2, \\cdots, x_n\\right)\\); 输出: 最优路径 \\(y^* =\\left(y_1^* , y_2^* , \\cdots, y_n^* \\right)\\) 。 (1) 初始化\n\\[ \\delta_1(j)=w \\cdot F_1\\left(y_0=\\operatorname{start}, y_1=j, x\\right), \\quad j=1,2, \\cdots, m \\]\n递推。对 \\(i=2,3, \\cdots, n\\) \\[ \\begin{gathered} \\delta_i(l)=\\max_{1 \\leqslant j \\leqslant m}\\left\\\\{\\delta_{i-1}(j)+w \\cdot F_i\\left(y_{i-1}=j, y_i=l, x\\right)\\right\\\\\\}, \\quad l=1,2, \\cdots, m \\\\\\\\ \\Psi_i(l)=\\arg \\max_{1 \\leqslant j \\leqslant m}\\left\\\\{\\delta_{i-1}(j)+w \\cdot F_i\\left(y_{i-1}=j, y_i=l, x\\right)\\right\\\\\\}, \\quad l=1,2, \\cdots, m \\end{gathered} \\]\n（3）终止\n\\[ \\begin{gathered} \\max_y(w \\cdot F(y, x))=\\max_{1 \\leqslant j \\leqslant m} \\delta_n(j) \\\\\\\\ y_n^* =\\arg \\max_{1 \\leqslant j \\leqslant m} \\delta_n(j) \\end{gathered} \\]\n返回路径 \\[ y_i^* =\\Psi_{i+1}\\left(y_{i+1}^* \\right), \\quad i=n-1, n-2, \\cdots, 1 \\]\n求得最优路径 \\(y^* =\\left(y_1^* , y_2^* , \\cdots, y_n^* \\right)\\_{\\text {。 }}\\)\n自己的理解就是非规范化概率每个i代表时间步i，要对所有的\\(\\lambda_k t_k\\)和 \\(\\mu_ks_k\\) 进行筛选，找出符合条件的相加，这里要注意下标的理解。括号里的和HMM的类似，代表y的取值。\n实例 这里使用维特比算法求解\n参数学习问题 总结 ","date":"2022-05-12T00:00:00Z","permalink":"/%E6%9D%A1%E4%BB%B6%E9%9A%8F%E6%9C%BA%E5%9C%BA/","title":"条件随机场"},{"content":"Boosting算法的工作机制是首先从训练集用初始权重训练出一个弱学习器1，根据弱学习的学习误差率表现来更新训练样本的权重，使得之前弱学习器1学习误差率高的训练样本点的权重变高，使得这些误差率高的点在后面的弱学习器2中得到更多的重视。然后基于调整权重后的训练集来训练弱学习器2.，如此重复进行，直到弱学习器数达到事先指定的数目T，最终将这T个弱学习器通过集合策略进行整合，得到最终的强学习器。\n这段叙述里面有很多需要解决的东西，比如如何计算误差率，如何得到弱学习器权重系数，如何更新样本权重，如何结合。\nAdaboost 先看一下adaboost的算法流程\n将初始化样本的权重为\\(\\frac{1}{m}\\)，那么这个样本权重到底怎么用呢，主要是用于在构建弱分类器时计算弱学习器在训练集上的误差时使用，也就是计算误差：\n\\[ \\epsilon_t = \\sum_{i=1}^mP(h_t(x_i) \\neq y_i) =\\sum_{i=1}^m D_{ti}I(h_t(x_i)\\neq y_i) \\]\n如果弱分类器为决策树的话，肯定要选择误差最小的划分作为最佳划分，最小的误差最为本轮弱分类器的误差。实际上样本权重不参与训练，只是通过样本权重动态调节模型的关注重点，即通过计算误差，影响决策树的划分，来调节模型的关注重点，这就是样本权重的作用。\n然后计算弱学习器权重\\(\\alpha_t\\)，更新样本权重，对于分类错误的样本权重上升，对于分类正确的样本权重下调，再除以规范化因子，确保仍然是一个分布，一般就是\\(D_t\\)的和作为规范化因子，下一轮的模型就更注意划分错误的这些样本点。这样每一轮迭代都会得到弱分类器系数和弱分类器，这里具体的计算过程可以参考李航的《统计学习方法》，迭代完成以后，按照公式将权重与弱分类器相乘后相加，最后经过信号函数得到最终的分类结果。 ## 优点\nAdaboost作为分类器时，分类精度很高\n在Adaboost的框架下，可以使用各种回归分类模型来构建弱学习器，非常灵活。\n作为简单的二元分类器时，构造简单，结果可理解。\n不容易发生过拟合 缺点 对异常样本敏感，异常样本在迭代中可能会获得较高的权重，影响最终的强学习器的预测准确性。\n另一种解释 上面叙述的是一般的Adaboost算法的解释，其实还有另外一种解释，就是adaboost为加法模型、损失函数为指数函数、学习算法为前向分步算法的二类分类学习方法。 那么什么是前向分步算法？\n输入: 训练数据集\\(T=\\{(x_1, y_1 ),(x_2, y_2), \\cdots,\\left(x_N, y_N\\right)\\}\\); 损失函数 \\(L(y, f(x))\\); 基函数集 \\(\\{b(x ; \\gamma)\\}\\); 输出: 加法模型 \\(f(x)\\) 。\n初始化 \\(f_0(x)=0\\); 对 \\(m=1,2, \\cdots, M\\) 极小化损失函数 \\[ \\left(\\beta_m, \\gamma_m\\right)=\\arg \\min_{\\beta, \\gamma} \\sum_{i=1}^N L\\left(y_i, f_{m-1}\\left(x_i\\right)+\\beta b\\left(x_i ; \\gamma\\right)\\right) \\]\n得到参数 \\(\\beta_m, \\gamma_m\\) 。\n更新 \\[ f_m(x)=f_{m-1}(x)+\\beta_m b\\left(x ; \\gamma_m\\right) \\]\n得到加法模型 \\[ f(x)=f_M(x)=\\sum_{m=1}^M \\beta_m b\\left(x ; \\gamma_m\\right) \\]\n学过GBDT的话就感觉会很熟悉。实际上提升树和GBDT也都是使用的前向分步算法。 直接上结论吧，推导的感觉没有必要，毕竟都是boosting方法。具体证明的过程可以看李航老师的《统计学习方法》。\nAdaboost算法是前向分步加法算法的特例，这时，模型是由基本分类器组成的加法模型，损失函数是指数函数。\n总结 总是要写个总结的，adaboost一种boosting方法，就是不断迭代调整权重，最后将每一步的结果加权求和，可以说是一种典型的boosting的方法。 ","date":"2022-04-27T00:00:00Z","permalink":"/adaboost/","title":"Adaboost"},{"content":"分割等和子集 题目： https://leetcode-cn.com/problems/partition-equal-subset-sum/?utm_source=LCUS\u0026amp;utm_medium=ip_redirect\u0026amp;utm_campaign=transfer2china\n思路： 典型的01背包问题，利用套路框架做即可\n注意做了优化，把原本的二维dp降低了一维\n代码： class Solution: def canPartition(self, nums: List[int]) -\u0026gt; bool: if sum(nums) % 2: return False s = sum(nums) // 2 dp = [False for _ in range(s+1)] dp[0] = True for i in range(1,len(nums)+1): for j in range(s,nums[i-1]-1,-1): # 容量 dp[j] = dp[j] or dp[j-nums[i-1]] # 用了or操作符 return dp[s] 更一般的套路，定义二维数组，然后二维dp\n# i代表前i个物品,j代表背包容量。 class Solution: def canPartition(self, nums: List[int]) -\u0026gt; bool: if len(nums) \u0026lt;= 1: return False if sum(nums) % 2: return False s = sum(nums) // 2 dp = [[False for _ in range(s+1)] for _ in range(len(nums)+1)] for i in range(len(nums)+1): dp[i][0] = True # 背包容量为0时 永远都是满的 所以为true for i in range(1,len(nums)+1): # 物品个数 for j in range(1,s+1): # 背包容量，最大为总和的一半，也就是需要求的 if j - nums[i-1] \u0026lt; 0: # 如果容量小于当前物品的重量 dp[i][j] = dp[i-1][j] else: dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i-1]] if dp[i][s]: # 剪枝 return True return dp[len(nums)][s] \u0026#39;\u0026#39;\u0026#39;首先，由于i是从 1 开始的，而数组索引是从 0 开始的，所以第i个物品的重量应该是nums[i-1]，这一点不要搞混。 dp[i - 1][j-nums[i-1]]也很好理解：你如果装了第i个物品，就要看背包的剩余重量j - nums[i-1]限制下是否能够被恰好装满。 换句话说，如果j - nums[i-1]的重量可以被恰好装满，那么只要把第i个物品装进去，也可恰好装满j的重量；否则的话，重量j肯定是装不满的。\u0026#39;\u0026#39;\u0026#39; ","date":"2022-03-25T00:00:00Z","permalink":"/%E5%88%86%E5%89%B2%E7%AD%89%E5%92%8C%E5%AD%90%E9%9B%86/","title":"分割等和子集"},{"content":"kd树 knn算法就是用kd树实现的\n二分查找 很简单 就不说了\nBST 很简单 就不说了\n多维数组 假设数组B为\\([[6, 2], [6, 3], [3, 5], [5, 0], [1, 2], [4, 9], [8, 1]]\\)，有一个元素x，我们要找到数组B中距离x最近的元素，应该如何实现呢？比较直接的想法是用数组B中的每一个元素与x求距离，距离最小的那个元素就是我们要找的元素。假设x = [1, 1]，那么用数组B中的所有元素与x求距离得到[5.0, 5.4, 4.5, 4.1, 1.0, 8.5, 7.0]，其中距离最小的是1，对应的元素是数组B中的[1, 2]，所以[1, 2]就是我们的查找结果。\nkd-tree 如何建立 你 1. 建立根节点；\n选取方差最大的特征作为分割特征(或者根据深度选择)\n选择该特征的中位数作为分割点；\n将数据集中该特征小于中位数的传递给根节点的左儿子，大于中位数的传递给根节点的右儿子；\n递归执行步骤2-4，直到所有数据都被建立到KD Tree的节点上为止。\n不难看出，KD Tree的建立步骤跟BST是非常相似的，可以认为BST是KD Tree在一维数据上的特例。KD Tree的算法复杂度介于O(Log2(N))和O(N)之间。\n为什么选择方差最大的维度 数据分割后分散的比较开，主要是为了减少回溯时间，减少子树的访问。\n为什么选择中位数作为分割点 因为借鉴了BST，选取中位数，让左子树和右子树的数据数量一致，便于二分查找。\n查找元素 从根节点出发进行查找，根据当前深度计算比较的特征维度，若目标节点的特征值小于当前节点的特征值则遍历左子树，否则遍历右子树 找到叶子结点后，将其暂时标记为当前最邻近的点 递归地向上回退，在回退时需要做： 如果当前节点与目标节点的距离更近，则更新最邻近节点为当前节点 如果当前节点对应特征与目标节点对应特征的值距离小于当前最小值时，进入当前节点的另一个子节点（因为刚刚从一个子节点遍历回来）进行查找（如果存在子节点的话），有可能存在更近的节点。否则的话继续向上回退。 回退到根节点结束。得到最邻近点。 class Node: def __init__(self, data, left=None, right=None): self.val = data self.left = left self.right = right class KDTree: def __init__(self, k): self.k = k def create_Tree(self, dataset, depth): if not dataset: return None mid_index = len(dataset) // 2 # 中位数索引 axis = depth % self.k # 选择的维度 sort_dataset = sorted(dataset, key=(lambda x: x[axis])) # 按照维度排序 mid_data = sort_dataset[mid_index] # 中位数索引对应的数据 cur_node = Node(mid_data) # 创建节点 left_data = sort_dataset[:mid_index] # 左子树数据 right_data = sort_dataset[mid_index+1:] # 右子树数据 cur_node.left = self.create_Tree(left_data, depth+1) # 递归创建左子树 cur_node.right = self.create_Tree(right_data, depth+1) # 递归创建右子树 # print(cur_node.val) return cur_node def search(self, tree, new_data): # kd树的搜索 self.near_node = None # 最近的节点 self.near_val = None # 最近的节点的值 def dfs(node, depth): if not node: return axis = depth % self.k # 当前深度对应选择的维度 if new_data[axis] \u0026lt; node.val[axis]: # 如果新数据的维度值小于当前节点的维度值 dfs(node.left, depth+1) # 递归搜索左子树 else: dfs(node.right, depth+1) # 递归搜索右子树 # 到这就相当于到达了叶子节点 dist = self.distance(new_data, node.val) # 计算新数据与当前节点的距离 if not self.near_val or dist \u0026lt; self.near_val: # 如果当前节点的距离小于最近的节点的距离 self.near_val = dist # 更新最近的节点的距离 self.near_point = node.val # 更新最近的节点的值 #判断是否要进入兄弟节点寻找 if abs(new_data[axis] - node.val[axis]) \u0026lt; self.near_val: # 如果新数据的维度值与当前节点的维度值的差值小于最近的节点的距离，说明兄弟节点区域有可能存在更接近的值。 if new_data[axis] \u0026lt; node.val[axis]: # 控制去兄弟节点而不是刚刚回溯来的节点。 dfs(node.right, depth+1) else: dfs(node.left, depth+1) dfs(tree, 0) return self.near_point def distance(self, point_1, point_2): res = 0 for i in range(self.k): res += (point_1[i] - point_2[i]) ** 2 return res ** 0.5 if __name__ == \u0026#39;__main__\u0026#39;: data_set = [[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]] new_data = [1,5] k = len(data_set[0]) kd_tree = KDTree(k) our_tree = kd_tree.create_Tree(data_set, 0) predict = kd_tree.search(our_tree, new_data) print(\u0026#39;Nearest Point of {}: {}\u0026#39;.format(new_data,predict)) Nearest Point of [1, 5]: [2, 3] 借用一下别人画的解题过程\n## 参考\nhttps://zhuanlan.zhihu.com/p/499241064#:~:text=kd%E6%A0%91%E7%94%A8%E4%BA%8E%E5%AF%B9k%E7%BB%B4,%E7%9A%84%E6%97%B6%E5%80%99%E9%9D%9E%E5%B8%B8%E8%80%97%E6%97%B6%E3%80%82\n","date":"2022-03-21T00:00:00Z","permalink":"/kd%E6%A0%91/","title":"kd树"},{"content":"最小公众前缀 leetcode上的简单题，最小公众前缀\n有三种解法，一种常规，两种巧妙解法\n# 最小公共前缀 #解1：常规解法 思路就是一个一个判断 先判断所有字符串第一个是否相同，不相同就返回，否则然后依次往后判断 def longestCommonPrefix1(strs): if len(strs) == 0: return \u0026#39;\u0026#39; if len(strs) == 1: return strs[0] minl=min([len(x) for x in strs]) #求最小长度 end = 0 while end \u0026lt; minl: #判断是否到最小长度 for i in range(1,len(strs)): #以第一个字符串为基准 if strs[i][end] != strs[i-1][end]: #如果到end这里不再相等 则返回到end这里的字符串即最小公共前缀 return strs[0][:end] end+=1 return strs[0][:end] #常规方法容易想到 但是缺点是运行速度慢，从每次判断都要遍历所有字符串就可以看出 #解2: 通过ascii码来判断 #Python里字符串是可以比较的，按照ascII值排 def longestCommonPrefix2(strs): if not strs: return 0 s1 = max(strs) s2 = min(strs) #找出s1 s2的最小公共前缀即为整个列表的最小公共前缀 for i,s in enumerate(s2): if s1[i] != s: return s1[:i] return s2 #通过max 和 min 函数来找到列表里面最大最小的两个字符串 然后找到这两个字符串的最小公共前缀。 #解3：通过python语法糖 将每个字符串的每个对应字符串存为一组，用zip函数，比如说所有的字符串第一个存在一起，然后用set去重，如果留下了一个，则说明都重复了，则就是相同的 def longestCommonPrefix3(strs): if not strs: return 0 cc = list(map(set,zip(*strs))) #为什么用map呢 因为要对zip压缩后的每一个序列去重 res = \u0026#39;\u0026#39; #结果 for i,s in enumerate(cc): x = list(s) if len(x) \u0026gt; 1: #如果长度大于1 说明有不一样的 则直接退出 break res += x[0] return res 如上！\n","date":"2022-03-20T00:00:00Z","permalink":"/%E6%9C%80%E5%B0%8F%E5%85%AC%E4%BC%97%E5%89%8D%E7%BC%80/","title":"最小公众前缀"},{"content":"参考：https://cuijiahua.com/blog/2017/12/ml_13_regtree_1.html\n1、ID3算法的弊端 回忆一下，决策树的树构建算法是ID3。ID3的做法是每次选取当前最佳的特征来分割数据，并按照该特征的所有可能取值来切分。也就是说，如果一个特征有4种取值，那么数据将被切分成4份。一旦按某特征切分后，该特征在之后的算法执行过程中将不会再起作用，所以有观点认为这种切分方式过于迅速。\n除了切分过于迅速外，ID3算法还存在另一个问题，它不能直接处理连续型特征。只有事先将连续型特征离散化，才能在ID3算法中使用。但这种转换过程会破坏连续型变量的内在特性。\n2、CART算法 与ID3算法相反，CART算法正好适用于连续型特征。CART算法使用二元切分法来处理连续型变量。而使用二元切分法则易于对树构建过程进行调整以处理连续型特征。具体的处理方法是：如果特征值大于给定值就走左子树，否则就走右子树。\nCART算法有两步：\n决策树生成：递归地构建二叉决策树的过程，基于训练数据集生成决策树，生成的决策树要尽量大；自上而下从根开始建立节点，在每个节点处要选择一个最好的属性来分裂，使得子节点中的训练集尽量的纯。不同的算法使用不同的指标来定义”最好”： 决策树剪枝：用验证数据集对已生成的树进行剪枝并选择最优子树，这时损失函数最小作为剪枝的标准。 决策树剪枝我们先不管，我们看下决策树生成。\n在决策树的文章中，我们先根据信息熵的计算找到最佳特征切分数据集构建决策树。CART算法的决策树生成也是如此，实现过程如下：\n使用CART算法选择特征 根据特征切分数据集合 构建树 推导 选择最优切分变量j与切分点s：遍历变量j，对规定的切分变量j扫描切分点s，选择使下式得到最小 值时的(j,s)对。其中Rm是被划分的输入空间， \\(\\mathrm{cm}\\) 是空间Rm对应的固定输出值。\n\\[ \\min_{j, s}\\left[\\min_{c_{1}} \\sum_{x_{i} \\in R_{i}(j, s)}\\left(y_{i}-c_{1}\\right)^{2}+\\min_{c_{2}} \\sum_{x_{i} \\in R_{i}(j, s)}\\left(y_{i}-c_{1}\\right)^{2}\\right] \\]\n用选定的(j,s)对，划分区域并决定相应的输出值\n\\[ \\begin{gathered} R_{1}(j, s)={x \\mid x^{(j)} \\leq s}, R_{2}(j, s)={x \\mid x^{(j)}\u0026gt;s} \\\\\\\\ \\hat{c}_{m}=\\frac{1}{N_{m}} \\sum_{x_{i} \\in R_{m}(j, s)} y_{i} \\\\\\\\ x \\in R_{m}, m=1,2 \\end{gathered} \\]\n继续对两个子区域调用上述步骤，将输入空间划分为 \\(M\\) 个区域R1,R2,..,Rm，生成决策树。\n\\[ f(x)=\\sum_{m=1}^{M} \\hat{c}_{m} I\\left(x \\epsilon R_{m}\\right) \\]\n当输入空间划分确定时，可以用平方误差来表示回归树对于训练数据的预测方法，用平方误差最小 的准则求解每个单元上的最优输出值。\n实例 【机器学习】回归决策树-CSDN博客\n","date":"2022-03-16T00:00:00Z","permalink":"/%E6%A0%91%E5%9B%9E%E5%BD%92/","title":"树回归"},{"content":"","date":"2022-03-15T00:00:00Z","permalink":"/%E9%9A%8F%E6%9C%BA%E6%A3%AE%E6%9E%97/","title":"随机森林"},{"content":"搜索旋转排序数组 题目： https://leetcode-cn.com/problems/search-in-rotated-sorted-array/\n思路： 明显的二分查找，不过不是有序数组了，而是部分有序，所以需要有判断\n代码： class Solution(object): def search(self, nums, target): left, right = 0, len(nums) - 1 while left \u0026lt;= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid if nums[mid] \u0026lt; nums[right]:#右边为升序 if nums[mid] \u0026lt; target \u0026lt;= nums[right]: left = mid + 1 else: right = mid if nums[left] \u0026lt;= nums[mid]:#左边为升序 if nums[left] \u0026lt;= target \u0026lt; nums[mid]: right = mid else: left = mid + 1 return -1 ","date":"2022-03-13T00:00:00Z","permalink":"/%E6%90%9C%E7%B4%A2%E6%97%8B%E8%BD%AC%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84/","title":"搜索旋转排序数组"},{"content":"pandas补充学习 推荐网站：http://joyfulpandas.datawhale.club/Content/Preface.html\npandas核心操作手册：https://mp.weixin.qq.com/s/l1V5e726XixI0W3EDHx0Nw\npd.join和pd.merge 可以说merge包含了join操作，merge支持两个df间行方向或列方向的拼接操作，默认列拼接，取交集，而join只是简化了merge的行拼接的操作 pandas的merge方法提供了一种类似于SQL的内存链接操作，官网文档提到它的性能会比其他开源语言的数据操作（例如R）要高效。 如果对于sql比较熟悉的话，merge也比较好理解。 merge的参数\non：列名，join用来对齐的那一列的名字，用到这个参数的时候一定要保证左表和右表用来对齐的那一列都有相同的列名。\nleft_on：左表对齐的列，可以是列名，也可以是和dataframe同样长度的arrays。\nright_on：右表对齐的列，可以是列名，也可以是和dataframe同样长度的arrays。\nleft_index/ right_index: 如果是True的haunted以index作为对齐的key\nhow：数据融合的方法。\nsort：根据dataframe合并的keys按字典顺序排序，默认是，如果置false可以提高表现。 简单举一个刚刚在比赛中里面用到的例子\ndata = pd.merge(data1, data2, on=\u0026quot;carid\u0026quot;, how=\u0026quot;inner\u0026quot;) # 根据carid合并两个数据集 可以用pd.merge，也可以用dataframe.merge，更多的信息可以查阅官方API。 ## pd.concat 也是合并dataframe 用法：\npd.concat([df1, df2]) # 纵向合并 pd.concat([df1, df2], axis=1) # 横向合并 参数 - ignore_index=True，重新设置合并后的dataframe对象的index值 - sort=False，列的顺序保持原样 - join : {“inner”, “outer”}，默认为outer。 ## pd.append\n# 语法结构 df.append(self, other, ignore_index=False, verify_integrity=False, sort=False) other 是它要追加的其他 DataFrame 或者类似序列内容 ignore_index 如果为 True 则重新进行自然索引 verify_integrity 如果为 True 则遇到重复索引内容时报错 sort 进行排序 同结构 将同结构的数据追加在原数据后面 ### 不同结构 没有的列会增加，没有的相应内容为空。 ### 可以合并追加多个\nresult = df1.append([df2, df3]) 追加序列 s2 = pd.Series([\u0026#39;X0\u0026#39;, \u0026#39;X1\u0026#39;, \u0026#39;X2\u0026#39;, \u0026#39;X3\u0026#39;], index=[\u0026#39;A\u0026#39;, \u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;D\u0026#39;]) result = df1.append(s2, ignore_index=True) 追加字典列表 dicts = [{\u0026#39;A\u0026#39;: 1, \u0026#39;B\u0026#39;: 2, \u0026#39;C\u0026#39;: 3, \u0026#39;X\u0026#39;: 4}, {\u0026#39;A\u0026#39;: 5, \u0026#39;B\u0026#39;: 6, \u0026#39;C\u0026#39;: 7, \u0026#39;Y\u0026#39;: 8}] result = df1.append(dicts, ignore_index=True, sort=False) pd.rename DataFrame.rename(self, mapper=None, index=None, columns=None, axis=None, copy=True, inplace=False, level=None, errors=‘ignore’) 作用就是修改index或者columns的名字。使用时可以指定mapper，然后指定axis，默认axis=0，即修改index 也可以使用index=xxx,columns=xxx，同时进行修改。\npd.get_dummies 用于构造离散数据的独热编码 用法\ntraining = pd.get_dummies(train_data, columns=[\u0026quot;xxx\u0026quot;, \u0026quot;xx\u0026quot;, \u0026quot;x\u0026quot;]) # 对xxx、xx、x这三列进行onehot编码 pd.melt 直观的看就是将宽数据转化为长数据。转化为variable-value这样的形式。\npandas.melt(frame, id_vars=None, value_vars=None, var_name=None, value_name=\u0026#39;value\u0026#39;, col_level=None) 参数解释：\nframe:要处理的数据集。 id_vars:不需要被转换的列名。 value_vars:需要转换的列名，如果剩下的列全部都要转换，就不用写了。 var_name和value_name是自定义设置对应的列名。 col_level :如果列是MultiIndex，则使用此级别。 常常与seaborn的FacetGrid一起进行操作，示例：\nf = pd.melt(train_data, value_vars=numeric_features) g = sns.FacetGrid(f, col=\u0026quot;variable\u0026quot;, col_wrap=2, sharex=False, sharey=False) # col_warp限制一行只有两个，sharx、sharey默认为True,与matplotlib.pyplot.subplots相反。 g = g.map(sns.distplot, \u0026quot;value\u0026quot;) # 第一个参数可以自定义函数。 sns.pairplot seaborn一般与pandas的数据结合，因此不分开做笔记了。 根据官方文档，sns.pairplot是在数据集中绘制成对关系。用来展现变量两两之间的关系，比如线性、非线性、相关等等。 hue参数可以指定分类。与其它plot的hue参数一致。 一般都会使用参数diag_kind=\"kde\"，因为对角线上的变量x与y都是一样的，探究关系没有意义，因此展示核密度分布。\nseaborn.pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, kind=\u0026#39;scatter\u0026#39;, diag_kind=\u0026#39;hist\u0026#39;, markers=None, size=2.5, aspect=1, dropna=True, plot_kws=None, diag_kws=None, grid_kws=None)¶ 数据指定： \u0026gt; vars : 与data使用，否则使用data的全部变量。参数类型：numeric类型的变量list。 {x, y}_vars : 与data使用，否则使用data的全部变量。参数类型：numeric类型的变量list。 dropna : 是否剔除缺失值。参数类型：boolean, optional\n特殊参数： \u0026gt; kind : {‘scatter’, ‘reg’}, optional Kind of plot for the non-identity relationships. diag_kind : {‘hist’, ‘kde’}, optional。Kind of plot for the diagonal subplots.\n基本参数： \u0026gt; size : 默认 6，图的尺度大小（正方形）。参数类型：numeric hue : 使用指定变量为分类变量画图。参数类型：string (变量名) hue_order : list of strings Order for the levels of the hue variable in the palette palette : 调色板颜色 markers : 使用不同的形状。参数类型：list aspect : scalar, optional。Aspect * size gives the width (in inches) of each facet. {plot, diag, grid}_kws : 指定其他参数。参数类型：dicts\n","date":"2022-02-22T00:00:00Z","permalink":"/some_api/","title":"learn_four"},{"content":"线性判别分析(LDA) 线性判别分析，也就是LDA（与主题模型中的LDA区分开），现在常常用于数据的降维中，但从它的名字中可以看出来它也是一个分类的算法，而且属于硬分类，也就是结果不是概率，是具体的类别 ## 主要思想 1. 类内方差小 2. 类间方差大 ## 推导 这里以二类为例，即只有两个类别。\n首先是投影，我们假定原来的数据是向量 \\(x\\)，那么顺着 \\(w\\) 方向的投影就是标量：\n\\[ z=w^T\\cdot x(=|w|\\cdot|x|\\cos\\theta) \\]\n对第一点，相同类内部的样本更为接近，我们假设属于两类的试验样本数量分别是 \\(N_1\\)和 \\(N_2\\)，那么我们采用方差矩阵来表征每一个类内的总体分布，这里我们使用了协方差的定义，用 \\(S\\) 表示原数据的协方差：\n\\[ \\begin{align} C_1:Var_z[C_1]\u0026amp;=\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(z_i-\\bar{z_{c1}})(z_i-\\bar{z_{c1}})^T\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(w^Tx_i-\\frac{1}{N_1}\\sum\\limits_{j=1}^{N_1}w^Tx_j)(w^Tx_i-\\frac{1}{N_1}\\sum\\limits_{j=1}^{N_1}w^Tx_j)^T\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=w^T\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}(x_i-\\bar{x_{c1}})(x_i-\\bar{x_{c1}})^Tw\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=w^TS_1w\\\\\\\\\\\\\\\\ C_2:Var_z[C_2]\u0026amp;=\\frac{1}{N_2}\\sum\\limits_{i=1}^{N_2}(z_i-\\bar{z_{c2}})(z_i-\\bar{z_{c2}})^T\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=w^TS_2w \\end{align} \\]\n所以类内距离为：\n\\[ \\begin{align} Var_z[C_1]+Var_z[C_2]=w^T(S_1+S_2)w \\end{align} \\]\n对于第二点类间距离，我们可以用两类的均值表示这个距离：\n\\[ \\begin{align} (\\bar{z_{c1}}-\\bar{z_{c2}})^2\u0026amp;=(\\frac{1}{N_1}\\sum\\limits_{i=1}^{N_1}w^Tx_i-\\frac{1}{N_2}\\sum\\limits_{i=1}^{N_2}w^Tx_i)^2\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=(w^T(\\bar{x_{c1}}-\\bar{x_{c2}}))^2\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=w^T(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw \\end{align} \\]\n合这两点，由于协方差是一个矩阵，于是我们用将这两个值相除来得到我们的损失函数，并最大化这个值：\n\\[ \\begin{align} \\hat{w}=\\mathop{argmax}\\limits_wJ(w)\u0026amp;=\\mathop{argmax}\\limits_w\\frac{(\\bar{z_{c1}}-\\bar{z_{c2}})^2}{Var_z[C_1]+Var_z[C_2]}\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=\\mathop{argmax}\\limits_w\\frac{w^T(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw}{w^T(S_1+S_2)w}\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;=\\mathop{argmax}\\limits_w\\frac{w^TS_bw}{w^TS_ww} \\end{align} \\]\n这样，我们就把损失函数和原数据集以及参数结合起来了。下面对这个损失函数求偏导，注意我们其实对w的绝对值没有任何要求，只对方向有要求，因此只要一个方程就可以求解了：\n\\[ \\begin{aligned} \u0026amp;\\frac{\\partial}{\\partial w}J(w)=2S_bw(w^TS_ww)^{-1}-2w^TS_bw(w^TS_ww)^{-2}S_ww=0\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;\\Longrightarrow S_bw(w^TS_ww)=(w^TS_bw)S_ww\\nonumber\\\\\\\\\\\\\\\\ \u0026amp;\\Longrightarrow w\\propto S_w^{-1}S_bw=S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}})(\\bar{x_{c1}}-\\bar{x_{c2}})^Tw\\propto S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}}) \\end{aligned} \\]\n也就是说最后我们的结果就是\\(w=S_w^{-1}(\\bar{x_{c1}}-\\bar{x_{c2}})\\) 可以归一化求得单位的w值。\n多类情况 前面的很容易类比二类的情况，现在的目标函数变成了：\n\\[ \\frac{W^TS_bW}{W^TS_wW} \\] 现在的问题就是这些都是矩阵，不能像上面那样直接优化，需要替换优化目标。\n\\[ \\underbrace{arg\\;max}_W\\;\\;J(W) = \\frac{\\prod\\limits_{diag}W^TS_bW}{\\prod\\limits_{diag}W^TS_wW} \\] 其中 \\(\\prod_{diag}A\\)为A的主对角线元素的乘积,W为\\(n \\times d\\)的矩阵，n为原来的维度，d为映射到超平面的维度，则最终的目标就变成了：\n\\[ J(W) = \\frac{\\prod\\limits_{i=1}^dw_i^TS_bw_i}{\\prod\\limits_{i=1}^dw_i^TS_ww_i} = \\prod\\limits_{i=1}^d\\frac{w_i^TS_bw_i}{w_i^TS_ww_i} \\] 根据广式瑞利商，最大值是矩阵\\(S_w^{-1}S_b\\)的最大特征值,最大的d个值的乘积就是矩阵的 \\(S_w^{-1}S_b\\) 最大的d个特征值的乘积,此时对应的矩阵\\(W\\)为这最大的d个特征值对应的特征向量张成的矩阵。\n总结 LDA是一种监督学习的降维技术，也就是说它的数据集的每个样本是有类别输出的。这点和PCA不同。PCA是不考虑样本类别输出的无监督降维技术。LDA的思想可以用一句话概括，就是“投影后类内方差最小，类间方差最大”。什么意思呢？ 我们要将数据在低维度上进行投影，投影后希望每一种类别数据的投影点尽可能的接近，而不同类别的数据的类别中心之间的距离尽可能的大。\n实际上LDA除了可以用于降维以外，还可以用于分类。一个常见的LDA分类基本思想是假设各个类别的样本数据符合高斯分布，这样利用LDA进行投影后，可以利用极大似然估计计算各个类别投影数据的均值和方差，进而得到该类别高斯分布的概率密度函数。当一个新的样本到来后，我们可以将它投影，然后将投影后的样本特征分别带入各个类别的高斯分布概率密度函数，计算它属于这个类别的概率，最大的概率对应的类别即为预测类别。\nLDA用于降维，和PCA有很多相同，也有很多不同的地方，因此值得好好的比较一下两者的降维异同点。\n首先我们看看相同点：\n1）两者均可以对数据进行降维。\n2）两者在降维时均使用了矩阵特征分解的思想。\n3）两者都假设数据符合高斯分布。\n我们接着看看不同点：\n1）LDA是有监督的降维方法，而PCA是无监督的降维方法\n2）LDA降维最多降到类别数k-1的维数，而PCA没有这个限制。\n3）LDA除了可以用于降维，还可以用于分类。\n4）LDA选择分类性能最好的投影方向，而PCA选择样本点投影具有最大方差的方向。\n代码 mean_list = [] for i in range(2): mean_list.append(np.mean(X_train[y_train==i], axis=0)) mean_list = np.array(mean_list) S_W = np.zeros((X_train.shape[1], X_train.shape[1])) # 类内散度矩阵 for c, mv in zip(range(2), mean_list): class_scatter = np.zeros((X_train.shape[1], X_train.shape[1])) for row in X_train[y_train==c]: row, mv = row.reshape(X_train.shape[1], -1), mv.reshape(X_train.shape[1], -1) class_scatter += (row-mv).dot((row-mv).T) S_W += class_scatter over_all_mean = np.mean(X_train, axis=0) S_B = np.zeros((X_train.shape[1], X_train.shape[1])) # 类间散度矩阵 for i, mean_vec in enumerate(mean_list): n = X_train[y_train==i, :].shape[0] mean_list_temp = mean_list[i, :].reshape(1, -1) over_all_mean = over_all_mean.reshape(X_train.shape[1], 1) S_B += n*(mean_vec-over_all_mean).dot((mean_vec-over_all_mean).T) eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B)) eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i]) for i in range(len(eig_vals))] eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True) # eigv_sum = sum(eig_vals) # for i, j in enumerate(eig_pairs): # print(\u0026#39;eigenvalue {0:}: {1:.2%}\u0026#39;.format(i + 1, (j[0] / eigv_sum).real)) # 根据百分比显示特征值，从而选取最大的n个特征值 W = np.hstack((eig_pairs[0][1].reshape(X_train.shape[1], 1), eig_pairs[1][1].reshape(X_train.shape[1], 1))) ","date":"2022-02-19T00:00:00Z","permalink":"/%E7%BA%BF%E6%80%A7%E5%88%A4%E5%88%AB%E5%88%86%E6%9E%90/","title":"线性判别分析"},{"content":"条件概率 \\(P(B|A) = \\frac{P(AB)}{P(A)}\\)\n乘法法则 如果P(A) \u0026gt; 0 \\(P(AB) = P(A)P(B|A)\\) 如果\\(P(A_1 \\dots A_{n-1})\\) \u0026gt; 0 则\n\\[ \\begin{aligned} P(A_1A_2\\dots A_n) = P(A_1A_2\\dots A_{n-1})P(A_n | A_1A_2\\dots A_{n-1}) \\\\\\\\ = P(A_1)P(A_2|A_1)P(A_3|A_1A_2)\\dots P(A_n|A_1A_2\\dots A_{n-1}) \\end{aligned} \\]\n其中第一步使用了乘法公式，然后再对前者继续使用乘法公式，以此类推，就可以得到最后的结果。\n全概率公式(加法法则) \\[ P(A) = \\sum_{i=1}^n P(B_i)P(A\\lvert B_i) = \\sum_{i=1}^n P(AB_i) \\] 如果是连续变量则为\n\\[ P(A) = \\int P(A,B) \\, dB \\] （加法规则与乘法规则结合是一些推导的基础，注意连续中的积分等同于离散中的连加） 特例为:\n\\[ P(A)=P(A\\lvert B)P(B) + P(A\\lvert \\bar{B})P(\\bar{B}) \\]\n全概率公式的意义： 将复杂的事件A划分为较为简单的事件\n\\[ AB_1,AB_2,\\ldots,AB_n \\]\n再结合加法公式和乘法公式计算出A的概率 ## 贝叶斯公式\n先引入一个小例子。\n\\[ P(X=玩LOL)=0.6;\\\\\\\\ P(X=不玩LOL)=0.4 \\]\n这个概率是根据统计得到或者根据自身经验给出的一个概率值，我们称之为先验概率(prior probability) 此外\n\\[ P(Y=男性\\lvert X=玩LOL)=0.8,\\quad P(Y=小姐姐\\vert X=玩LOL)=0.2\\\\\\\\ P(Y=男性\\lvert X=不玩LOL)=0.2，\\quad P(Y=小姐姐\\vert X=不玩LOL)=0.8 \\]\n求在已知玩家为男性的情况下，他是LOL玩家的概率是多少： 根据贝叶斯准则\n\\[ P(X=玩LOL\\lvert Y=男性)=P(Y=男性\\lvert X=玩LOL)\\frac{P(X=玩LOL)}{[P(Y=男性\\lvert X=玩LOL)P(X=玩LOL)+P(Y=男性\\lvert X=不玩LOL)]P(X=不玩LOL)} \\]\n分母为全概率公式\n下面是贝叶斯公式的推导。\n\\[ P(B\\lvert A)=\\frac{P(AB)}{P(A)}=\\frac{P(BA)}{P(A)}\\iff \\frac{P(B)P(A\\lvert B)}{\\displaystyle \\sum_{j=1}^n P(B_j)P(A\\lvert B_j)} \\]\n贝叶斯公式的意义： 在事件A已经发生的条件下，贝叶斯公式可用来寻找导致A发生各种“原因”Bi的概率。 对于先验概率和后验概率来说，\n\\[ \\begin{aligned} P(B\\lvert A)为后验概率 \\\\\\\\ P(B)和P(A)为先验概率 \\\\\\\\ P(A\\vert B)为可能性 \\end{aligned} \\]\n介绍 朴素贝叶斯属于生成式模型， 其主要用于分类，属于是最简单的概率图模型，主要用到概率论中学到的贝叶斯公式，其中需要对模型进行假设，即贝叶斯假设。\n贝叶斯假设 条件独立性假设(最简单的概率图模型(有向图))，目的是简化计算\n推导 对于数据集\\(\\\\{(x_i, y_i)\\\\}^N_{i=1}\\)，\\(x_i \\in R^p , \\quad y_i \\in \\\\{ 0, 1\\\\}\\)\n\\[ \\begin{aligned} \\hat{y} \u0026amp;= \\arg \\max(y|X) \\\\\\\\ \u0026amp; = \\arg \\max\\frac{P(X,y)}{P(X)} \\\\\\\\ \u0026amp; = \\arg \\max\\frac{P(y)P(X|y)}{P(X)} \\\\\\\\ \u0026amp; = \\arg \\max(y) P(X|y) \\\\\\\\ \u0026amp; = \\arg \\max(y)P(x_1,x_2,\\dots x_p| y) \\end{aligned} \\]\n其中由于我们的条件独立性假设，因此\\(P(X|y)\\)可以写为\\(\\prod_{j=1}^pP(x_j|y)\\) 即最终的式子就是\n\\[ \\hat{y} = \\arg \\max(y)\\prod_{j=1}^p P(x_j|y) \\]\n这就是朴素贝叶斯的主要推导。 注意术语：\n\\(P(y)\\)为先验概率 \\(P(y|X)\\)为后验概率 \\(P(X,y)\\)为联合概率 MAP，即最大后验估计，选择有最高后验概率的类。 后验概率最大化的含义 （来自李航《统计学习方法》,比西瓜书上的更容易理解）\n极大似然估计 在朴素贝叶斯法中, 学习意味着估计 \\(P\\left(Y=c_{k}\\right)\\) 和 \\(P\\left(X^{(j)}=x^{(j)} \\mid Y=c_{k}\\right)\\) 。可以 应用极大似然估计法估计相应的概率。先验概率 \\(P\\left(Y=c_{k}\\right)\\) 的极大似然估计是\n\\[ P\\left(Y=c_{k}\\right)=\\frac{\\sum_{i=1}^{N} I\\left(y_{i}=c_{k}\\right)}{N}, \\quad k=1,2, \\cdots, K \\]\n设第 \\(j\\) 个特征 \\(x^{(j)}\\) 可能取值的集合为 \\(\\left\\{a_{j 1}, a_{j 2}, \\cdots, a_{j S_{j}}\\right\\}\\), 条件概率 \\(P\\left(X^{(j)}=a_{j l} \\mid Y=\\right.\\) \\(c_{k}\\) ) 的极大似然估计是\n\\[ \\begin{aligned} \u0026amp;P\\left(X^{(j)}=a_{j l} \\mid Y=c_{k}\\right)=\\frac{\\sum_{i=1}^{N} I\\left(x_{i}^{(j)}=a_{j l}, y_{i}=c_{k}\\right)}{\\sum_{i=1}^{N} I\\left(y_{i}=c_{k}\\right)} \\\\\\\\ \u0026amp;j=1,2, \\cdots, n ; \\quad l=1,2, \\cdots, S_{j} ; \\quad k=1,2, \\cdots, K \\end{aligned} \\]\n式中, \\(x_{i}^{(j)}\\) 是第 \\(i\\) 个样本的第 \\(j\\) 个特征; \\(a_{j l}\\) 是第 \\(j\\) 个特征可能取的第 \\(l\\) 个值; \\(I\\) 为指 示函数。\n\\(S_j\\)为\\(x^{(j)}\\)的可能取值数，\\(K\\)为类别数。\n拉普拉斯平滑 用极大似然估计可能会出现所要估计的概率值为 0 的情况。这时会影响到后验概 率的计算结果, 使分类产生偏差。解决这一问题的方法是采用贝叶斯估计。具体地, 条 件概率的贝叶斯估计是\n\\[ P_{\\lambda}\\left(X^{(j)}=a_{j l} \\mid Y=c_{k}\\right)=\\frac{\\sum_{i=1}^{N} I\\left(x_{i}^{(j)}=a_{j l}, y_{i}=c_{k}\\right)+\\lambda}{\\sum_{i=1}^{N} I\\left(y_{i}=c_{k}\\right)+S_{j} \\lambda} \\]\n式中 \\(\\lambda \\geqslant 0\\) 。等价于在随机变量各个取值的频数上赋予一个正数 \\(\\lambda\u0026gt;0\\) 。当 \\(\\lambda=0\\) 时就 是极大似然估计。常取 \\(\\lambda=1\\), 这时称为拉普拉斯平滑 (Laplacian smoothing)。显然, 对任何 \\(l=1,2, \\cdots, S_{j}, k=1,2, \\cdots, K\\), 有\n\\[ \\begin{aligned} \u0026amp;P_{\\lambda}\\left(X^{(j)}=a_{j l} \\mid Y=c_{k}\\right)\u0026gt;0 \\\\\\\\ \u0026amp;\\sum_{l=1}^{S_{j}} P\\left(X^{(j)}=a_{j l} \\mid Y=c_{k}\\right)=1 \\end{aligned} \\]\n同样, 先验概率的贝叶斯估计是\n\\[ P_{\\lambda}\\left(Y=c_{k}\\right)=\\frac{\\sum_{i=1}^{N} I\\left(y_{i}=c_{k}\\right)+\\lambda}{N+K \\lambda} \\]\n文本分类 接下来是朴素贝叶斯在文本分类中的运用，这里以简单的二分类问题，情感分析为例。\n如何定义几个概率？ \\(P(y=k)\\)很容易得到，可以只评估带有标签k的文档比例，即\n\\[ P(y=k) = \\frac{N(y=k)}{\\sum_iN(y=i)} \\]\n\\(P(x|y=k)= P(x_1,x_2,\\dots, x_n | y=k)\\) 这里假设文档x被表示为一组特征，例如一组它的词\\((x_1,x_2,\\dots, x_n)\\)\n这里需要两个假设，其中一个是上面提到的贝叶斯假设，即： - 条件独立假设：特征在给定类的情况下是独立的 - Bag of Words假设：词序无关紧要\n直观地说，假设 每个单词出现在类别为k的文档中的概率不依赖上下文，因此得到：\n\\[ P(x|y=k) = P(x_1,x_2,\\dots,x_n|y=k) = \\prod_{t=1}^nP(x_t|y=k) \\]\n概率\\(P(x_i|y=k)\\)为单词\\(x_i\\)出现在标签为k的文档中的频率，即\n\\[ P(x_i|y=k) = \\frac{N(x_i, y=k)}{\\sum_{t=1}^{|V|}N(x_t,y=k)} \\]\n但是有个问题就是有可能会出现\\(N(x_i, y=k)=0\\)的情况 这时就需要拉普拉斯平滑，即在所有的计数中都加入一个新的参数\\(\\delta\\)，\n\\[ P(x_i|y=k)=\\frac{ {\\delta} + N(x_i, y=k) }{\\sum\\limits_{t=1}^{|V|}( {\\delta} + N(x_t, y=k))} = \\frac{ {\\delta} + N(x_i, y=k) }{ {\\delta\\cdot |V|} + \\sum\\limits_{t=1}^{|V|} N(x_t, y=k)} , \\]\n直观地说，朴素贝叶斯期望某些词作为类指示符。例如，对于情感分类标记 awesome、 brilliant、 great 将有更高的概率给定正面类别然后负面类别。 类似地，给定负类比正类 ，标记awful, boring, bad的概率更高。\n在实践中，一般都是取log，单调性不变，变为\\(\\log(x, y=k) = \\log P(y=k) + \\sum \\log P(x_i|y=k)\\)\n补充：贝叶斯估计 易知\\(P(\\theta \\mid D)\\)称为后验概率，有三种估计\\(\\theta\\)的方法：\n使用后验分布的密度函数最大值点作为\\(\\theta\\)的点估计的最大后验估计（MAP）。 使用后验分布的中位数作为\\(\\theta\\)的点估计的后验中位数估计（不常用）。 使用后验分布的均值作为\\(\\theta\\)的点估计的后验期望估计。 其中后验期望估计也就是贝叶斯估计。\n贝叶斯估计是在MAP上做进一步拓展，不直接估计参数的值，而是允许参数服从一定的概率密度分布，先求出\\(\\theta\\)的后验分布\\(p(\\theta \\mid x)\\)，然后求出\\(\\theta\\)的期望值。\n","date":"2022-02-16T00:00:00Z","permalink":"/bayes/","title":"bayes"},{"content":"正为逆时针转，负为顺时针转。\nimport numpy as np mat = np.array([[1,3,5], [2,4,6], [7,8,9] ]) print mat, \u0026quot;# orignal\u0026quot; mat90 = np.rot90(mat, 1) print mat90, \u0026quot;# rorate 90 \u0026lt;left\u0026gt; anti-clockwise\u0026quot; mat90 = np.rot90(mat, -1) print mat90, \u0026quot;# rorate 90 \u0026lt;right\u0026gt; clockwise\u0026quot; mat180 = np.rot90(mat, 2) print mat180, \u0026quot;# rorate 180 \u0026lt;left\u0026gt; anti-clockwise\u0026quot; mat270 = np.rot90(mat, 3) print mat270, \u0026quot;# rorate 270 \u0026lt;left\u0026gt; anti-clockwise\u0026quot; 直接复制的代码，python2，能看懂就行。\n","date":"2022-02-12T00:00:00Z","permalink":"/rot90/","title":"rot90"},{"content":"数据挖掘比赛 对赛题进行理解 数据分析 EDA目标 EDA的价值在于熟悉数据集，了解数据集，对数据集进行验证来确定所获得数据集可以用于接下来的机器学习或者深度学习使用。 当了解了数据集之后我们下一步就是要去了解变量间的相互关系以及变量与预测值之间的存在关系。 引导数据科学从业者进行数据处理以及特征工程的步骤,使数据集的结构和特征集让接下来的预测问题更加可靠。 完成对于数据的探索性分析，并对于数据进行一些图表或者文字总结并打卡。 主要操作 载入各种数据科学以及可视化库: 数据科学库 pandas、numpy、scipy； 可视化库 matplotlib、seabon； 其他； 载入数据： 载入训练集和测试集； 简略观察数据(head()+shape)； 数据总览: 通过describe()来熟悉数据的相关统计量 通过info()来熟悉数据类型 判断数据缺失和异常 查看每列的存在nan情况 异常值检测 了解预测值的分布 总体分布概况（无界约翰逊分布等） 查看skewness and kurtosis 查看预测值的具体频数 特征分为类别特征和数字特征，并对类别特征查看unique分布 数字特征分析 相关性分析 查看几个特征得 偏度和峰值 每个数字特征得分布可视化 数字特征相互之间的关系可视化 多变量互相回归关系可视化 类型特征分析 unique分布 类别特征箱形图可视化 类别特征的小提琴图可视化 类别特征的柱形图可视化类别 特征的每个类别频数可视化(count_plot) 用pandas_profiling生成数据报告 主要步骤 对于数据的初步分析（直接查看数据，或.sum(), .mean()，.descirbe()等统计函数）可以从：样本数量，训练集数量，是否有时间特征，是否是时许问题，特征所表示的含义（非匿名特征），特征类型（字符类似，int，float，time），特征的缺失情况（注意缺失的在数据中的表现形式，有些是空的有些是”NAN”符号等），特征的均值方差情况。 分析记录某些特征值缺失占比30%以上样本的缺失处理，有助于后续的模型验证和调节，分析特征应该是填充（填充方式是什么，均值填充，0填充，众数填充等），还是舍去，还是先做样本分类用不同的特征模型去预测。 对于异常值做专门的分析，分析特征异常的label是否为异常值（或者偏离均值较远或者事特殊符号）,异常值是否应该剔除，还是用正常值填充，是记录异常，还是机器本身异常等。 对于Label做专门的分析，分析标签的分布情况等。 进步分析可以通过对特征作图，特征和label联合做图（统计图，离散图），直观了解特征的分布情况，通过这一步也可以发现数据之中的一些异常值等，通过箱型图分析一些特征值的偏离情况，对于特征和特征联合作图，对于特征和label联合作图，分析其中的一些关联性。 记录自己之前没用到的东西 ### 数据的偏度和峰度 - 数据的偏度(skewness)：dataframe.skew() - 数据的峰度(kurtosis)：dataframe.kurt() ### log变换 一般要求预测值需要符合正态分布，因此需要先log变换一下 ### sns.pairplot 用来展现变量两两之间的关系，比如线性、非线性、相关 hue参数可以指定分类。\nsns.heatmap 热度图，可以直观感受变量之间的相关程度。\n更多的处理方法可以看pandas笔记\n特征工程 主要步骤 异常处理： 通过箱线图（或 3-Sigma）分析删除异常值； BOX-COX 转换（处理有偏分布）； 长尾截断； 特征归一化/标准化： 标准化（转换为标准正态分布）； 归一化（抓换到 [0,1] 区间）； 针对幂律分布，可以采用公式：\\(log(\\frac{1+x}{1+median})\\) 数据分桶： 等频分桶； 等距分桶； Best-KS 分桶（类似利用基尼指数进行二分类）； 卡方分桶； 缺失值处理： 不处理（针对类似 XGBoost 等树模型）； 删除（缺失数据太多）； 插值补全，包括均值/中位数/众数/建模预测/多重插补/压缩感知补全/矩阵补全等； 分箱，缺失值一个箱； 特征构造： 构造统计量特征，报告计数、求和、比例、标准差等； 时间特征，包括相对时间和绝对时间，节假日，双休日等； 地理信息，包括分箱，分布编码等方法； 非线性变换，包括 log/ 平方/ 根号等； 特征组合，特征交叉； 仁者见仁，智者见智。 特征筛选： 过滤式（filter）：先对数据进行特征选择，然后在训练学习器，常见的方法有 Relief/方差选择发/相关系数法/卡方检验法/互信息法； 包裹式（wrapper）：直接把最终将要使用的学习器的性能作为特征子集的评价准则，常见方法有 LVM（Las Vegas Wrapper） ； 嵌入式（embedding）：结合过滤式和包裹式，学习器训练过程中自动进行了特征选择，常见的有 lasso 回归； 降维： PCA/LDA/LCA;本博客都有介绍 特征选择也是降维 ## 建模调参 ## 模型融合 ","date":"2022-01-26T00:00:00Z","permalink":"/eda/","title":"数据挖掘比赛"},{"content":"什么是前缀树？ 前缀树是N叉树的一种特殊形式。通常来说，一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个字符串（前缀）。每一个节点会有多个子节点，通往不同子节点的路径上有着不同的字符。子节点代表的字符串是由节点本身的原始字符串，以及通往该子节点路径上所有的字符组成的。 在上图示例中，我们在节点中标记的值是该节点对应表示的字符串。例如，我们从根节点开始，选择第二条路径 ‘b’，然后选择它的第一个子节点 ‘a’，接下来继续选择子节点 ‘d’，我们最终会到达叶节点 “bad”。节点的值是由从根节点开始，与其经过的路径中的字符按顺序形成的。\n值得注意的是，根节点表示空字符串。\n前缀树的一个重要的特性是，节点所有的后代都与该节点相关的字符串有着共同的前缀。这就是前缀树名称的由来。\n我们再来看这个例子。例如，以节点 “b” 为根的子树中的节点表示的字符串，都具有共同的前缀 “b”。反之亦然，具有公共前缀 “b” 的字符串，全部位于以 “b” 为根的子树中，并且具有不同前缀的字符串来自不同的分支。\n前缀树有着广泛的应用，例如自动补全，拼写检查等等。\n代码 import collections class TrieNode(object): # 定义节点 # Initialize your data structure here. def __init__(self): self.node = collections.defaultdict(TrieNode) self.char = \u0026quot;\u0026quot; self.is_word = False @property def data(self): return self.node def __getitem__(self, key): return self.node[key] def __str__(self) -\u0026gt; str: return self.char __repr__ = __str__ class Trie(object): def __init__(self): \u0026quot;\u0026quot;\u0026quot; Initialize your data structure here. \u0026quot;\u0026quot;\u0026quot; self.root = TrieNode() def insert(self, word): \u0026quot;\u0026quot;\u0026quot; Inserts a word into the trie. :type word: str :rtype: void \u0026quot;\u0026quot;\u0026quot; node = self.root for chars in word: temp = node.char node = node[chars] # 因为defaultdict，如果不存在则自动生成键 node.char = temp + chars # 键可以视为路径上的字符，节点可以视为从根到当前节点表示的前缀或单词。 node.is_word = True # 这里很重要，用于search判断是否为完整的单词 def search(self, word): \u0026quot;\u0026quot;\u0026quot; Returns if the word is in the trie. :type word: str :rtype: bool \u0026quot;\u0026quot;\u0026quot; node = self.root for chars in word: if chars not in node.data.keys(): # 因为defaultdict所以不能判断value为None，要判断键是否存在 return False node = node[chars] # 判断单词是否是完整的存在在trie树中 return node.is_word def startsWith(self, prefix): \u0026quot;\u0026quot;\u0026quot; Returns if there is any word in the trie that starts with the given prefix. :type prefix: str :rtype: bool \u0026quot;\u0026quot;\u0026quot; node = self.root for chars in prefix: if chars not in node.data.keys(): # 和上面同理 return False node = node[chars] return True def get_all_words(self): # 获取所有的单词 q = [self.root] while q: node = q.pop(0) # 相当于一个队列 for child in node.data.values(): if child.is_word: yield child.char q.append(child) 应用 前缀树在中文分词的应用，前缀树的实现和上面的可能不太一样，不过功能都是一样的，主要就是找句子中的词有没有出现在前缀树中。\nfrom trie import Trie import time class TrieTokenizer(Trie): \u0026quot;\u0026quot;\u0026quot; 基于字典树(Trie Tree)的中文分词算法 \u0026quot;\u0026quot;\u0026quot; def __init__(self, dict_path): \u0026quot;\u0026quot;\u0026quot; :param dict_path:字典文件路径 \u0026quot;\u0026quot;\u0026quot; super(TrieTokenizer, self).__init__() self.dict_path = dict_path self.create_trie_tree() self.punctuations = \u0026quot;\u0026quot;\u0026quot;！？｡＂＃＄％＆＇：（）＊＋，－／：；＜＝＞＠［＼］＾＿｀｛｜｝～｟｠｢｣､、〃》「」『』【】〔〕〖〗〘〙〚〛〜〝〞〟〰〾〿–—‘’‛“”„‟…‧﹏.\u0026quot;\u0026quot;\u0026quot; def load_dict(self): \u0026quot;\u0026quot;\u0026quot; 加载字典文件 词典文件内容如下，每行是一个词： AA制 ABC ABS AB制 AB角 :return: \u0026quot;\u0026quot;\u0026quot; words = [] with open(self.dict_path, mode=\u0026quot;r\u0026quot;, encoding=\u0026quot;utf-8\u0026quot;) as file: for line in file: words.append(line.strip().encode(\u0026#39;utf-8\u0026#39;).decode(\u0026#39;utf-8-sig\u0026#39;)) return words def create_trie_tree(self): \u0026quot;\u0026quot;\u0026quot; 遍历词典，创建字典树 :return: \u0026quot;\u0026quot;\u0026quot; words = self.load_dict() for word in words: self.insert(word) def mine_tree(self, tree, sentence, trace_index): \u0026quot;\u0026quot;\u0026quot; 从句子第trace_index个字符开始遍历查找词语，返回词语占位个数 :param tree: :param sentence: :param trace_index: :return: \u0026quot;\u0026quot;\u0026quot; if trace_index \u0026lt;= (len(sentence) - 1): if sentence[trace_index] in tree.data: trace_index = trace_index + 1 trace_index = self.mine_tree(tree.data[sentence[trace_index - 1]], sentence, trace_index) return trace_index def tokenize(self, sentence): tokens = [] sentence_len = len(sentence) while sentence_len != 0: trace_index = 0 # 从句子第一个字符开始遍历 trace_index = self.mine_tree(self.root, sentence, trace_index) if trace_index == 0: # 在字典树中没有找到以sentence[0]开头的词语 tokens.append(sentence[0:1]) # 当前字符作为分词结果 sentence = sentence[1:len(sentence)] # 重新遍历sentence sentence_len = len(sentence) else: # 在字典树中找到了以sentence[0]开头的词语，并且trace_index为词语的结束索引 tokens.append(sentence[0:trace_index]) # 命中词语作为分词结果 sentence = sentence[trace_index:len(sentence)] # sentence_len = len(sentence) return tokens def combine(self, token_list): \u0026quot;\u0026quot;\u0026quot; TODO:对结果后处理：标点符号/空格/停用词 :param token_list: :return: \u0026quot;\u0026quot;\u0026quot; flag = 0 output = [] temp = [] for i in token_list: if len(i) != 1: # 当前词语长度不为1 if flag == 0: output.append(i[::]) else: # [\u0026#39;该\u0026#39;, \u0026#39;方法\u0026#39;] # temp=[\u0026#39;该\u0026#39;] output.append(\u0026quot;\u0026quot;.join(temp)) output.append(i[::]) temp = [] flag = 0 else: if flag == 0: temp.append(i) flag = 1 else: temp.append(i) return output if __name__ == \u0026#39;__main__\u0026#39;: now = lambda: time.time() trie_cws = TrieTokenizer(\u0026#39;data/32w_dic.txt\u0026#39;) start = now() print(f\u0026quot;Build Token Tree Time : {now() - start}\u0026quot;) sentence = \u0026#39;该方法的主要思想：词是稳定的组合，因此在上下文中，相邻的字同时出现的次数越多，就越有可能构成一个词。因此字与字相邻出现的概率或频率能较好地反映成词的可信度。\u0026#39; \u0026#39;可以对训练文本中相邻出现的各个字的组合的频度进行统计，计算它们之间的互现信息。互现信息体现了汉字之间结合关系的紧密程度。当紧密程 度高于某一个阈值时，\u0026#39; \u0026#39;便可以认为此字组可能构成了一个词。该方法又称为无字典分词。\u0026#39; tokens = trie_cws.tokenize(sentence) combine_tokens = trie_cws.combine(tokens) end = now() print(tokens) print(combine_tokens) print(f\u0026quot;tokenize Token Tree Time : {end - start}\u0026quot;) ","date":"2022-01-19T00:00:00Z","permalink":"/%E5%89%8D%E7%BC%80%E6%A0%91/","title":"前缀树"},{"content":"Logistic回归 线性回归 线性回归表达式：\n\\[ y = w^Tx+b \\]\n广义回归模型：\n\\[ y = g^{-1}(w^Tx+b) \\]\nSigmoid函数 在分类任务中，需要找到一个联系函数，即g，将线性回归的输出值与实际的标签值联系起来。因此可以使用Sigmoid函数 即：\n\\[ \\delta(z) = \\frac{1}{1+e^{-z}} \\]\n对数几率其实是一种“sigmoid”函数，它将z值转化为一个接近 0 或 1 的 \\(y\\) 值:\n\\[ y=\\frac{1}{1+e^{-\\left(w^{T} x+b\\right)}} \\rightarrow \\operatorname{In} \\frac{y}{1-y}=w^{T} x+b \\]\n若将y视为样本 \\(x\\) 作为正例的可能性，则1-y是其反例的可能性，两者的比值 \\(\\frac{y}{1-y}\\) 称为“几率”，反映了x作为正例的相对可能性，对几率取对 数则得到 \\(\\operatorname{In} \\frac{y}{1-y}\\) ，可以看出，上式其实是在用线性回归模型的预测结果去逼近真实标记的对数几率。所以该模型也被称作“对数几率回 归”。 ## 损失函数 \\[ J = -\\frac{1}{m}\\sum_{i=1}^my_i\\log(\\hat{y_i})+(1-y_i)\\log(1-\\hat{y}) \\]\n实际上可以看作下面交叉熵损失函数形式在二分类问题上的形式：\n\\[ J = -\\frac{1}{m}\\sum_{i=1}^my_i\\log(\\hat{y_i}) \\]\n这里的\\(y_i\\)与\\(\\hat{y_i}\\)都是向量，其长度就是类别的数量。其中\\(y_i\\)代表实际分布，形式上为onehot向量。\\(\\hat{y_i}\\)是概率分布，为预测的值。\n其实这里可以想一下神经网络，对于sigmoid来说，输出层的神经元可以是一个，也可以是两个，如果是一个的话就可以用上面的形式，如果是两个的话可以用下面的这种形式。\n也可以这样理解，对于softmax的这种形式，对于二分类我们可以拆分成这样\n\\[ \\begin{cases} \\log \\hat{y_i}, \\quad y_i=1 \\\\\\\\ \\log (1-\\hat{y_i}), \\quad y_i=0 \\end{cases} \\]\n再结合起来，这样就可以得到逻辑回归的损失函数的结果。 ## 与极大似然估计的关系\n\\[ h(x;\\theta) = p(y=1|x;\\theta) = \\frac{1}{1+e^{-\\theta x+b}} \\]\n\\[ p(y=0|x;\\theta) = 1-p(y=1|x;\\theta) \\]\n则对于单个样本：\n\\[ p(y|x;\\theta) = h(x;\\theta)^y(1-h(x;\\theta))^{(1-y)} \\]\n接下来用极大似然估计估计出参数\\(\\theta\\)\n\\[ \\begin{aligned} L(\\theta) = \\prod_{i=1}^mp(y_i|x_i;\\theta)\\\\\\\\ =\\prod_{i=1}^mh(x_i;\\theta)^{y_i}(1-h(x_i;\\theta)) ^{1-y_i} \\end{aligned} \\]\n则：\n\\[ l(\\theta) = \\ln L(\\theta) = \\sum_{i=1}^my_i\\ln (h(x_i;\\theta ))+(1-y_i)ln(1-h(x_i|\\theta)) \\]\n极大这个函数，也就是最小化这个函数的负数，也就是上面的损失函数。\npython实现 class LogisticRegression: def __init__(self): pass def sigmoid(self,a): res = [] for x in a: if x \u0026gt;= 0: res.append(1/(1+np.exp(-x))) else: res.append(np.exp(x) / (np.exp(x) + 1)) return np.array(res) def train(self, X, y_true, n_iters=100, learning_rate=1): \u0026quot;\u0026quot;\u0026quot; 根据给定的训练集X和y来训练逻辑回归 \u0026quot;\u0026quot;\u0026quot; # 第零步：初始化参数 n_samples, n_features = X.shape #👆样本数m和特征量数n分别赋值为X的行数和列数 self.weights = np.zeros((n_features,1)) self.bias = 0 costs = [] for i in range(n_iters): # 第一步和第二步：计算输入的特征量和权值的线性组合，使用sigmoid函数 y_predict = self.sigmoid(np.dot(X,self.weights)+self.bias) # 第三步：计算代价值，用于之后计算代价函数值 cost = (-1/n_samples)*np.sum(y_true*np.log(y_predict+1e-5)+(1-y_true)*(np.log(1-y_predict+1e-5))) # 第四步：计算梯度 dw = (1/n_samples)*np.dot(X.T,(y_predict - y_true)) db = (1/n_samples)*np.sum(y_predict-y_true) # 第五步；更新参数 self.weights = self.weights - learning_rate * dw self.bias = self.bias - learning_rate * db costs.append(cost) if i%10 == 0: print(f\u0026quot;Cost after iteration {i}:{cost}\u0026quot;) # return self.weights,self.bias,costs def predict(self,X): \u0026quot;\u0026quot;\u0026quot; 对于测试集X，预测二元分类标签 \u0026quot;\u0026quot;\u0026quot; y_predict = self.sigmoid(np.dot(X,self.weights)+self.bias) return np.array(y_predict) ","date":"2022-01-15T00:00:00Z","permalink":"/logistic%E5%9B%9E%E5%BD%92/","title":"Logistic回归"},{"content":"pd.melt 用法 直观的看就是将宽数据转化为长数据。转化为variable-value这样的形式。\npandas.melt(frame, id_vars=None, value_vars=None, var_name=None, value_name=\u0026#39;value\u0026#39;, col_level=None) 参数解释：\nframe:要处理的数据集。 id_vars:不需要被转换的列名。 value_vars:需要转换的列名，如果剩下的列全部都要转换，就不用写了。 var_name和value_name是自定义设置对应的列名。 col_level :如果列是MultiIndex，则使用此级别。 ## 实例 import pandas as pd df = pd.DataFrame({\u0026#39;A\u0026#39;: {0: \u0026#39;a\u0026#39;, 1: \u0026#39;b\u0026#39;, 2: \u0026#39;c\u0026#39;}, \u0026#39;B\u0026#39;: {0: 1, 1: 3, 2: 5}, \u0026#39;C\u0026#39;: {0: 2, 1: 4, 2: 6} }) df \u0026#39;\u0026#39;\u0026#39; A B C 0 a 1 2 1 b 3 4 2 c 5 6 \u0026#39;\u0026#39;\u0026#39; pd.melt(df, id_vars=[\u0026#39;A\u0026#39;], value_vars=[\u0026#39;B\u0026#39;]) \u0026#39;\u0026#39;\u0026#39; A variable value 0 a B 1 1 b B 3 2 c B 5 \u0026#39;\u0026#39;\u0026#39; pd.melt(df, id_vars=[\u0026#39;A\u0026#39;], value_vars=[\u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;]) \u0026#39;\u0026#39;\u0026#39; A variable value 0 a B 1 1 b B 3 2 c B 5 3 a C 2 4 b C 4 5 c C 6 \u0026#39;\u0026#39;\u0026#39; pd.melt(df, id_vars=[\u0026#39;A\u0026#39;], value_vars=[\u0026#39;B\u0026#39;], var_name=\u0026#39;myVarName\u0026#39;, value_name=\u0026#39;myValueName\u0026#39;) \u0026#39;\u0026#39;\u0026#39; A myVarName myValueName 0 a B 1 1 b B 3 2 c B 5 \u0026#39;\u0026#39;\u0026#39; pd.melt(df, id_vars=[\u0026#39;A\u0026#39;], value_vars=[\u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;], ignore_index=False) \u0026#39;\u0026#39;\u0026#39; A variable value 0 a B 1 1 b B 3 2 c B 5 0 a C 2 1 b C 4 2 c C 6 \u0026#39;\u0026#39;\u0026#39; # 多重索引 df.columns = [list(\u0026#39;ABC\u0026#39;), list(\u0026#39;DEF\u0026#39;)] df \u0026#39;\u0026#39;\u0026#39; A B C D E F 0 a 1 2 1 b 3 4 2 c 5 6 \u0026#39;\u0026#39;\u0026#39; # 选择最外层索引 pd.melt(df, col_level=0, id_vars=[\u0026#39;A\u0026#39;], value_vars=[\u0026#39;B\u0026#39;]) \u0026#39;\u0026#39;\u0026#39; A variable value 0 a B 1 1 b B 3 2 c B 5 \u0026#39;\u0026#39;\u0026#39; # 选择内层索引 pd.melt(df, col_level=1, id_vars=[\u0026#39;D\u0026#39;], value_vars=[\u0026#39;E\u0026#39;]) # 选择复合索引 pd.melt(df, id_vars=[(\u0026#39;A\u0026#39;, \u0026#39;D\u0026#39;)], value_vars=[(\u0026#39;B\u0026#39;, \u0026#39;E\u0026#39;)]) \u0026#39;\u0026#39;\u0026#39; (A, D) variable_0 variable_1 value 0 a B E 1 1 b B E 3 2 c B E 5 \u0026#39;\u0026#39;\u0026#39; ","date":"2022-01-12T00:00:00Z","permalink":"/melt/","title":"melt"},{"content":"预训练模型 概述 预训练模型，则是使自然语言处理由原来的手工调参、依靠 ML 专家的阶段，进入到可以大规模、可复制的大工业施展的阶段。而且预训练模型从单语言、扩展到多语言、多模态任务。一路锐气正盛，所向披靡。\n预训练通过自监督学习从大规模数据中获得与具体任务无关的预训练模型。体现某一个词在一个特定上下文中的语义表征。第二个步骤是微调，针对具体的任务修正网络。训练数据可以是文本、文本-图像对、文本-视频对。预训练模型的训练方法可使用自监督学习技术（如自回归的语言模型和自编码技术）。可训练单语言、多语言和多模态的模型。此类模型可经过微调之后，用于支持分类、序列标记、结构预测和序列生成等各项技术，并构建文摘、机器翻译、图片检索、视频注释等应用。\n为什么我们要做预训练模型？首先，预训练模型是一种迁移学习的应用，利用几乎无限的文本，学习输入句子的每一个成员的上下文相关的表示，它隐式地学习到了通用的语法语义知识。第二，它可以将从开放领域学到的知识迁移到下游任务，以改善低资源任务，对低资源语言处理也非常有利。第三，预训练模型在几乎所有 NLP 任务中都取得了目前最佳的成果。最后，这个预训练模型+微调机制具备很好的可扩展性，在支持一个新任务时，只需要利用该任务的标注数据进行微调即可，一般工程师就可以实现。\n发展趋势 首先，第一个关键技术是 Transformer。它在 NLP 各个任务中都取得了优异的性能，它是预训练语言模型的核心网络。给定一句话或是一个段落作为输入，首先将输入序列中各个词转换为其对应的词向量，同时加上每一个词的位置向量，体现词在序列的位置。然后将这些词向量输入到多层 Transformer 网络中，通过自注意力（self-attention）机制来学习词与词之间的关系，编码其上下文信息，再通过一个前馈网络经过非线性变化，输出综合了上下文特征的各个词的向量表示。每一层 Transformer 网络主要由 Multi-head self-attention 层（多头自注意力机制）和前馈网络层两个子层构成。Multi-head self-attention 会并行地执行多个不同参数的 self-attention，并将各个 self-attention 的结果拼接作为后续网络的输入，self-attention 机制会在后面中做详细介绍。此后，我们得到了蕴含当前上下文信息的各个词的表示，然后网络会将其输入到前馈网络层以计算非线性层次的特征。\n在每一层 Transformer 网络中，会将残差连接（residual connection）把自注意力机制前或者前馈神经网络之前的向量引入进来，以增强自注意力机制或者前馈网络的输出结果向量。并且还做一个 layer normalization，也就是通过归一化把同层的各个节点的多维向量映射到一个区间里面，这样各层节点的向量在一个区间里面。这两个操作加入在每个子层后，可更加平滑地训练深层次网络。\nTransformer 可以用于编码，也可以用于解码。所谓解码就是根据一个句子的输入得到一个预想的结果，比如机器翻译（输入源语言句子，输出目标语言句子），或者阅读理解（输入文档和问题，输出答案）。解码时，已经解码出来的词要做一个自注意力机制，之后和编码得到的隐状态的序列再做一个注意力机制。这样可以做 N 层，然后通过一个线性层映射到词表的大小的一个向量。每个向量代表一个词表词的输出可能性，经过一个softmax 层得到每个词的输出概率。\n接下来介绍一下 self-attention 机制，以一个 head 作为示例。假定当前输入包含三个词，给定其输入词向量或是其上一层 Transformer 网络的输出，将其通过三组线性变换，转换得到三组 queries、keys 和 values 向量。Query 和 key 向量用来计算两两词之间的得分，也就是其依赖关系，这个得分会同其对应的 value 向量做加权和，以得到每个词综合上下文信息的表示。给定当前第一个词的 query 向量，其首先同各个词的 key 向量通过点积操作得到这两个词的得分，这些得分用来表示这两个词的依赖或是相关程度。这些得分之后会根据 query 等向量的维度做一定比例的缩放，并将这些得分通过 softmax 操作做归一化。之后，各个得分会同其相对应的 value 向量相乘得到针对第一个词加权的各个 value 向量，这些加权的 value 向量最终相加以得到当前第一个词的上下文表示。\n在得到第一个词的上下文表示后，给定第二个词的 query 向量，我们会重复之前的操作，计算当前 query 向量同各个词 key 向量的得分，对这些得分做 softmax 归一化处理，并将这些得分同其对应的 value 向量做加权和，以得到其编码上下文信息的表示。\n第二个关键技术是自监督学习。在预训练的模型中，AR（自回归）LM 和 AE（自动编码器）是最常用的自监督学习方法，其中，自回归 LM 旨在利用前面的词序列预测下个词的出现概率（语言模型）。自动编码器旨在对损坏的输入句子，比如遮掩了句子某个词、或者打乱了词序等，重建原始数据。通过这些自监督学习手段来学习单词的上下文相关表示。\n第三个关键技术就是微调。在做具体任务时，微调旨在利用其标注样本对预训练网络的参数进行调整。以我们使用基于 BERT（一种流行的预训练模型）为例来判断两个句子是否语义相同。输入是两个句子，经过 BERT 得到每个句子的对应编码表示，我们可以简单地用预训练模型的第一个隐节点预测分类标记判断两个句子是同义句子的概率，同时需要额外加一个线性层和 softmax 计算得到分类标签的分布。预测损失可以反传给 BERT 再对网络进行微调。当然也可以针对具体任务设计一个新网络，把预训练的结果作为其输入。\n总体来讲，预训练模型发展趋势：第一，模型越来越大。比如 Transformer 的层数变化，从12层的 Base 模型到24层的 Large 模型。导致模型的参数越来越大，比如 GPT 110 M，到 GPT-2 是1.5 Billion，图灵是 17 Billion，而 GPT-3 达到了惊人的 175 Billion。一般而言模型大了，其能力也会越来越强，但是训练代价确实非常大。第二，预训练方法也在不断增加，从自回归 LM，到自动编码的各种方法，以及各种多任务训练等。第三，还有从语言、多语言到多模态不断演进。最后就是模型压缩，使之能在实际应用中经济的使用，比如在手机端。这就涉及到知识蒸馏和 teacher-student models，把大模型作为 teacher，让一个小模型作为 student 来学习，接近大模型的能力，但是模型的参数减少很多。\n每个观点都可以看一下参考文章。\n深层表征阶段 在word embedding这篇文章里面，介绍了传统的词向量，也就是固定的词向量。本文将介绍deep contextualized词向量模型。也就是深层表征阶段。\n两个伟大的想法：\n编码的内容：从单词到上下文中的单词 （从 Word2Vec/GloVe/etc. 到 Cove/ELMo 的过渡） 用于下游任务：从仅替换特定任务模型中的词嵌入到替换整个特定任务模型 （从 Cove/ELMo 到 GPT/BERT 的过渡）。 具体的模型可以看本博客其余内容。\n参考 https://www.zhihu.com/question/327642286\nhttps://zhuanlan.zhihu.com/p/115014536\n","date":"2022-01-03T00:00:00Z","permalink":"/%E9%A2%84%E8%AE%AD%E7%BB%83%E6%A8%A1%E5%9E%8B/","title":"预训练模型"},{"content":"矩阵的反转，可以按照各个维度很好理解。 例子：\ncs_matrix = np.array([[ 4, 3, 2, 1, 0], [ 8, 7, 6, 5, 1], [11, 10, 9, 6, 2], [13, 12, 10, 7, 3], [14, 13, 11, 8, 4]]) np.flip(cs_matrix, 0) 变成了： np.flip(cs_matrix, 1) 变成了： ","date":"2022-01-01T00:00:00Z","permalink":"/flip/","title":"flip"},{"content":"ELMo 在Transformer中提到了ELMo解决了word2vec中存在的多义词问题，其使用双向的LSTM作为特征提取器，考虑了上下文的语义，所以可以解决多义词问题。这篇文章就详细介绍一下ELMo。\nElMo与CoVe很类似，不过它不是基于机器翻译模型，而是语言模型。仅仅通过用来自 LM 的嵌入替换词嵌入 (GloVe)，他们就在问答、共指解析、情感分析、命名实体识别等多项任务上获得了巨大的改进。\n模型训练(char-CNN 之上的前向和后向 LSTM-LMs) 该模型非常简单，它由两层 LSTM 语言模型组成：前向和后向。使用这两种模型是为了使每个标记都可以具有两个上下文：左和右。\n同样有趣的是作者如何获得初始单词表示（然后将其馈送到 LSTM）。让我们回想一下，在标准词嵌入层中，对于词汇表中的每个词，我们训练一个唯一的向量。在这种情况下， - 词嵌入不知道它们所包含的字符（例如，它们不知道单词represent, represents, represented, 和 representation在书面上是接近的） - OOV问题 为了解决这些问题，作者将单词表示为字符级网络的输出。正如我们从插图中看到的，这个 CNN 非常简单，由我们之前已经看到的组件组成：卷积、全局池化、highway connections和线性层。通过这种方式，单词表示通过构造知道它们的字符，我们甚至可以表示那些我们在训练中从未见过的单词。\n获取表示 训练模型后，我们可以使用它来获取单词表示。为此，对于每个单词，我们结合来自前向和后向 LSTM 的相应层的表示。通过连接这些前向和后向向量，我们构建了一个“知道”左右上下文的单词表示。\n总体而言，ELMo 表示具有三层：\n第 0 层（嵌入） - 字符级 CNN 的输出； 第 1 层- 来自前向和后向 LSTM 的第 1 层的连接表示； 第 2 层- 来自前向和后向 LSTM 的第 2 层的连接表示； 这些层中的每一层都对不同类型的信息进行编码：第 0 层 - 仅单词级别，第 1 层和第 2 层 - 上下文中的单词。比较第 1 层和第 2 层，第 2 层可能包含更多高级信息：这些表示来自相应 LM 的更高层。\n由于不同的下游任务需要不同种类的信息，ELMo 使用特定于任务的权重来组合来自三层的表示。这些是为每个下游任务学习的标量。得到的向量，即所有层表示的加权和，用于表示一个单词。\n总结 CoVe和ELMo都用了上下文单词，解决了word2vec中多义词的问题。但他们主要是替换嵌入层，并保持特定于任务的模型架构几乎完好无损。这意味着例如，对于共指解决，必须使用为此任务设计的特定模型，用于词性标记 - 一些其他模型，用于问答 - 另一个非常特殊的模型等。对于这些任务中的每一个，专门研究它的研究人员不断改进特定于任务的模型架构。\n与以前的模型相比，GPT/BERT 不是作为词嵌入的替代品，而是作为特定任务模型的替代品。在这个新设置中，首先使用大量未标记数据（纯文本）对模型进行预训练。然后，该模型在每个下游任务上进行微调。重要的是，现在在微调期间，您必须只使用任务感知输入转换（即以某种方式提供数据）， 而不是 修改模型架构。\n","date":"2021-12-12T00:00:00Z","permalink":"/elmo/","title":"ELMo"},{"content":"Stacking 思想简介 简单得理解，就是对于多个学习器，分别对结果进行预测，然后将预测的结果作为特征，再对结果进行预测。 上一张经典的图： 以这个5折stacking为例：\n首先将所有数据集生成测试集和训练集（假如训练集为10000,测试集为2500行），那么上层会进行5折交叉检验，使用训练集中的8000条作为训练集，剩余2000行作为验证集（橙色）。 每次验证相当于使用了蓝色的8000条数据训练出一个模型，使用模型对验证集进行验证得到2000条数据，并对测试集进行预测，得到2500条数据，这样经过5次交叉检验，可以得到中间的橙色的\\(5\\times 2000\\)条验证集的结果(相当于每条数据的预测结果)，\\(5\\times 2500\\)条测试集的预测结果。 接下来会将验证集的\\(5\\times 2000\\)条预测结果拼接成10000行长的矩阵，标记为A1，而对于\\(5\\times 2500\\)行的测试集的预测结果进行加权平均，得到一个2500一列的矩阵，标记为B1。 上面得到一个基模型在数据集上的预测结果A1、B1,这样当我们对3个基模型进行集成的话，相于得到了A1、A2、A3、B1、B2、B3六个矩阵。 之后我们会将A1、A2、A3并列在一起成10000行3列的矩阵作为training data,B1、B2、B3合并在一起成2500行3列的矩阵作为testing data，让下层学习器基于这样的数据进行再训练。 再训练是基于每个基础模型的预测结果作为特征（三个特征），次学习器会学习训练如果往这样的基学习的预测结果上赋予权重w，来使得最后的预测最为准确。 伪代码 ","date":"2021-11-25T00:00:00Z","permalink":"/stacking/","title":"Stacking"},{"content":"参考：https://cuijiahua.com/blog/2017/11/ml_11_regression_1.html\n什么是回归？ 回归的目的是预测数值型的目标值。最直接的办法是依据输入写出一个目标值的计算公式。\nHorsePower = 0.0015 * annualSalary - 0.99 * hoursListeningToPublicRadio\n这就是所谓的回归方程（regression equation），其中的0.0015和-0.99称为回归系数（regression weights），求这些回归系数的过程就是回归。一旦有了这些回归系数，再给定输入，做预测就非常容易了。具体的做法是用回归系数乘以输入值，再将结果全部加在一起，就得到了预测值。\n说到回归，一般都是指线性回归（linear regression），所以本文里的回归和线性回归代表同一个意思。线性回归意味着可以将输入项分别乘以一些常量，再将结果加起来得到输出。\n这里的线性可以是对变量的线性，也可以是对参数的线性。比如\\(y=x^2\\)可以认为是一个线性函数。\n正规方程推导 将向量表达形式转为矩阵表达形式，则有 \\(J(\\theta)=\\frac{1}{2}(X \\theta-y)^{2}\\) ，其中 \\(X\\) 为 \\(m\\) 行 \\(n\\) 列的矩阵（ \\(m\\) 为样本个数， \\(n\\) 为特 征个数)， \\(\\theta\\) 为 \\(n\\) 行1列的矩阵， \\(y\\) 为 \\(m\\) 行1列的矩阵，对 \\(J(\\theta)\\) 进行如下变换\n\\[ \\begin{aligned} \u0026amp;J(\\theta)=\\frac{1}{2}(X \\theta-y)^{T}(X \\theta-y) \\\\\\\\ \u0026amp;=\\frac{1}{2}\\left(\\theta^{T} X^{T}-y^{T}\\right)(X \\theta-y) \\\\\\\\ \u0026amp;=\\frac{1}{2}\\left(\\theta^{T} X^{T} X \\theta-\\theta^{T} X^{T} y-y^{T} X \\theta-y^{T} y\\right) \\end{aligned} \\]\n接下来对 \\(J(\\theta)\\) 偏导，需要用到以下几个矩阵的求导法则:\n\\[ \\begin{aligned} \u0026amp;\\frac{d A B}{d B}=A^{T} \\\\\\\\ \u0026amp;\\frac{d X^{T} A X}{d X}=2 A X \\end{aligned} \\]\n所以有:\n\\[ \\begin{aligned} \u0026amp;\\frac{\\partial J(\\theta)}{\\partial \\theta}=\\frac{1}{2}\\left(2 X^{T} X \\theta-X^{T} y-\\left(y^{T} X\\right)^{T}-0\\right) \\\\\\\\ \u0026amp;=\\frac{1}{2}\\left(2 X^{T} X \\theta-X^{T} y-X^{T} y-0\\right) \\\\\\\\ \u0026amp;=X^{T} X \\theta-X^{T} y \\\\\\\\ \u0026amp;\\text { 令 } \\frac{\\partial J(\\theta)}{\\partial \\theta}=0 ｝ \\\\\\\\ {\\text { 则有 } \\theta=\\left(X^{T} X\\right)^{-1} X^{T} y} \\end{aligned} \\]\n值得注意的是，上述公式中包含逆矩阵，也就是说，这个方程只在逆矩阵存在的时候使用，也即是这个矩阵是一个方阵，并且其行列式不为0。\n除了这种方法，也可以使用最小二乘法来解决。\n局部加权线性回归 线性回归的一个问题是有可能出现欠拟合现象，因为它求的是具有小均方误差的无偏估 计。显而易见，如果模型欠拟合将不能取得好的预测效果。所以有些方法允许在估计中引入一 些偏差，从而降低预测的均方误差。\n其中的一个方法是局部加权线性回归（Locally Weighted Linear Regression，LWLR）。 普通线性回归:\n\\[ \\min J_R=\\sum_{i=1}^m\\left(g\\left(x_i\\right)-y_i\\right)^2 \\]\n局部加权线性回归:\n\\[ \\min J_{L R}=\\sum_{i=1}^m \\theta_i\\left(g\\left(x_i\\right)-y_i\\right)^2 \\]\n这里唯一的区别是加入了权重 \\(\\theta\\), 采用之前的最小二乘法求解权数 \\(\\mathbf{w}\\) :\n\\[ \\hat{\\mathbf{w}}^* =\\arg \\min_{\\hat{\\mathbf{w}}} \\theta(\\mathbf{y}-\\mathbf{X} \\hat{\\mathbf{w}})^T(\\mathbf{y}-\\mathbf{X} \\hat{\\mathbf{w}}) \\]\n在该方法中，我们给待预测点附近的每个点赋予一定的权重。与kNN一样，这种算法每次预测均需要事先选取出对应的数据子集。该算法解除回归系数W的形式如下：\n\\[ \\hat{w} = (X^T\\theta X)^{-1}X^T\\theta y \\]\n其中W是一个矩阵，这个公式跟我们上面推导的公式的区别就在于W，它用来给每个店赋予权重。\nLWLR使用”核”（与支持向量机中的核类似）来对附近的点赋予更高的权重。核的类型可以自由选择，最常用的核就是高斯核，高斯核对应的权重如下：\n\\[ \\theta(i,i) = exp\\left ( \\frac{ (x^{(i)}-x)^2}{-2k^2}\\right) \\]\n这样就构造了一个只含对角元素的权重矩阵 \\(w\\) ，并且点xi与 \\(x\\) 越接近， \\(\\theta(i, i)\\) 的值越大，当 \\(x i\\) 与 \\(x\\) 非常接近时， \\(\\theta(i, i)\\) 的值趋于 1 ，我们再回头看之前的优化式:\n\\[ \\min J_{L R}=\\sum_{i=1}^m \\theta_i\\left(g\\left(x_i\\right)-y_i\\right)^2 \\]\n对于一个数据点，与其靠近的点，权重大，与其相距较远的点，权重小，从而优化问题会有所偏倚，靠近的点对该数据点的回归拟合起较大作用，而相距较远的 点由于权数很小，造成的影响基本可以忽略不计，这样就等同于每个点的回归都是基于与其相距较近的点为基础，而忽略了较远的点，这也就是同部加权线性回归局部的由来，因为它着重考虑目标点同部的数据点为回归基础.\n可以看到，加权函数只有一个系数，那就是分母上的 \\(k\\) ，当K取很小时， exp得到的很多值均趋于 0 ，此时只有很少一部分样本用于训练，而当k取很大时， exp的值 不会很快趋于 0 ，从而会有一大部分点用于训练，我们可以通过调整k的值，决定这个‘局部’的大小究竟是多大\n如果数据的特征比样本点还多应该怎么办？如果矩阵有多重共线性怎么办？很显然，此时我们不能再使用上文的方法进行计算了，因为矩阵X不是满秩矩阵，非满秩矩阵在求逆时会出现问题。\n解决的方法就是正则化。在线性回归中，正则化主要有L1正则化与L2正则化，L1正则化对应LASSO回归，L2正则化对应岭回归。\n#!/usr/bin/env python #-*- coding:utf-8 -*- from numpy import * import matplotlib.pyplot as plt \u0026quot;\u0026quot;\u0026quot; 打开一个用tab键分隔的文本文件 parameters: fileName -文件名 return: dataMat -数据矩阵 labelMat -目标值向量 \u0026quot;\u0026quot;\u0026quot; def loadDataSet(fileName): numFeat = len(open(fileName).readline().split(\u0026#39;\\t\u0026#39;)) - 1 #得到列数，不包括最后一列，默认最后一列值为目标值 dataMat = []; labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr =[] curLine = line.strip().split(\u0026#39;\\t\u0026#39;) for i in range(numFeat): lineArr.append(float(curLine[i])) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat \u0026quot;\u0026quot;\u0026quot; 计算最佳拟合直线 parameters: xArr -给定的输入值 yArr -给定的输出值 return: ws -回归系数 \u0026quot;\u0026quot;\u0026quot; def standRegres(xArr,yArr): xMat = mat(xArr); yMat = mat(yArr).T #将数据保存到矩阵中 #计算x.T *x xTx = xMat.T @ xMat #使用linalg.det()方法来判断它的行列式是否为零，即是否可逆 if linalg.det(xTx) == 0.0: return #使用最小二乘法计算w值 ws = linalg.inv(xTx) @ xMat.T @ yMat return ws \u0026quot;\u0026quot;\u0026quot; 计算局部加权线性回归系数 parameters: testPoint -待预测数据 xArr -给定输入值 yArr -给定输出值 k -高斯核的k值，决定对附近的点赋予多大的权重 return: testPoint * ws -回归预测的估计值 \u0026quot;\u0026quot;\u0026quot; def lwlr(testPoint, xArr, yArr, k=1.0): xMat = mat(xArr); yMat = mat(yArr).T #读入数据到矩阵 m = shape(xMat)[0] #创建对角权重矩阵，该矩阵为方阵，矩阵维数为样本点个数 theta = eye(m, m) #遍历整个数据集 for i, x in enumerate(xMat): #计算待预测数据与每个样本点的差值 x_i = (testPoint - x) @ (testPoint - x).T #计算每个样本点对应的权重值，随着样本点与待预测点距离的递增，权重将以指数级衰减 theta[i][i] = exp(x_i / (-2 * k ** 2)) #计算x.T θ x xT_theta_x = xMat.T @ theta @ xMat #判断矩阵是否可逆 if linalg.det(xT_theta_x) == 0: return #使用最小二乘法计算w值 ws = linalg.inv(xT_theta_x) @ (xMat.T @ (theta @ yMat)) return testPoint*ws \u0026quot;\u0026quot;\u0026quot; 测试函数 parameters: testArr -测试数据集 xArr -给定输入值 yArr -给定输出值 k -高斯核的k值 return: yHat -预测值 \u0026quot;\u0026quot;\u0026quot; def lwlrTest(testArr, xArr, yArr,k=1.0): m = shape(xArr)[0] yHat = zeros(m) for i in range(m): yHat[i] = lwlr(testArr[i],xArr,yArr,k) return yHat \u0026quot;\u0026quot;\u0026quot; 计算预测误差的平方和 parameters: yArr -给定y值 yHatArr -预测y值 return: ((yArr-yHatArr)**2).sum() -误差矩阵 \u0026quot;\u0026quot;\u0026quot; def rssError(yArr,yHatArr): return ((yArr-yHatArr)**2).sum() if __name__==\u0026#39;__main__\u0026#39;: abX,abY = loadDataSet(\u0026#39;linear_regression_abalone/abalone.txt\u0026#39;) yHat01 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],0.1) yHat1 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],1) yHat10 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],10) print(\u0026quot;使用局部加权线性回归预测误差：\u0026quot;) print(\u0026quot;核为0.1时：\u0026quot;,rssError(abY[0:99],yHat01.T)) print(\u0026quot;核为1时：\u0026quot;,rssError(abY[0:99],yHat1.T)) print(\u0026quot;核为10时：\u0026quot;,rssError(abY[0:99],yHat10.T)) yHat01 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],0.1) yHat1 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],1) yHat10 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],10) print(\u0026quot;使用局部加权线性回归预测误差在新数据上的表现：\u0026quot;) print(\u0026quot;核为0.1时：\u0026quot;,rssError(abY[100:199],yHat01.T)) print(\u0026quot;核为1时：\u0026quot;,rssError(abY[100:199],yHat1.T)) print(\u0026quot;核为10时：\u0026quot;,rssError(abY[100:199],yHat10.T)) ws = standRegres(abX[0:99],abY[0:99]) yHat = mat(abX[100:199])*ws print(\u0026quot;使用标准线性回归预测误差为：\u0026quot;,rssError(abY[100:199],yHat.T.A)) 岭回归 岭回归即我们所说的L2正则线性回归，在一般的线性回归最小化均方误差的基础上增加了一个参数w的L2范数的罚项，从而最小化罚项残差平方和：\n\\[ min\\mid \\mid Xw-y \\mid\\mid_2^2 + \\lambda \\mid\\mid w\\mid\\mid_2^2 \\] 简单说来，岭回归就是在普通线性回归的基础上引入单位矩阵。回归系数的计算公式变形如下：\n\\[ \\hat{w} = (X^TX+\\lambda I)^{-1}X^Ty \\] 式中，矩阵I是一个mxm的单位矩阵，加上一个λI从而使得矩阵非奇异，进而能对矩阵求逆。\n当\\(\\lambda\\)过小，则相当于原来的正规方程，会造成过拟合，而\\(\\lambda\\)过大时，模型的方差会更小，但偏差会变大，所以岭回归的关键是找到一个合理的\\(\\lambda\\)值来平衡模型的方差和偏差。\n岭回归最先用来处理特征数多于样本数的情况，现在也用于在估计中加入偏差，从而得到更好的估计。这里通过引入λ来限制了所有w之和，通过引入该惩罚项，能够减少不重要的参数，这个技术在统计学中也可以叫做缩减（shrinkage）。\n缩减方法可以去掉不重要的参数，因此能更好地裂解数据。此外，与简单的线性回归相比，缩减法能够取得更好的预测效果。\n图绘制了回归系数与log(λ)的关系。在最左边，即λ最小时，可以得到所有系数的原始值（与线性回归一致）；而在右边，系数全部缩减成0；在中间部分的某个位置，将会得到最好的预测结果。想要得到最佳的λ参数，可以使用交叉验证的方式获得，文章的后面会继续讲解。\nLasso Lasso对回归系数做了限定，对应的约束条件如下：\n\\[ \\sum_{k=1}^n|w_k| \\leq \\lambda \\]\n而岭回归的约束条件如下：\n\\[ \\sum_{k=1}^nw_k^2 \\leq \\lambda \\]\n唯一不同点在于，Lasso的约束条件用绝对值取代了平方和，虽然约束形式稍作变化，但结果大相径庭，细微的变化极大地增加了计算复杂度。所以用更简单的前向逐步回归来取代。\n前向逐步回归 前向逐步线性回归算法属于一种贪心算法，即每一步都尽可能减少误差。我们计算回归系数，不再是通过公式计算，而是通过每次微调各个回归系数，然后计算预测误差。那个使误差最小的一组回归系数，就是我们需要的最佳回归系数。\n该算法的伪代码如下：\n数据标准化，使其满足0均值和单位方差。\n在每轮迭代中： 设置当前最小误差lowestError为正无穷 对每个特征 增大或缩小 改变一个系数得到新的W 计算新W下的误差 如果误差Error小于当前最小误差lowestError：设置Wbest等于当前的W 将W设置为新的Wbest 代码 使用的优化算法是梯度下降法，还有一个随机梯度下降\nimport numpy as np import matplotlib.pyplot as plt np.random.seed(42) w = np.array([2, 1, 4, 5, 3]) d = len(w) X = [] Y = [] for _ in range(1000000): x = np.random.randn(d) y = w.dot(x) + np.random.randn() X.append(x) Y.append(y) X = np.array(X) Y = np.array(Y) def mse(y_true, y_test): return ((y_true - y_test) ** 2) / len(y_true) def gradient(y_true, y_test): return 2 * (y_test - y_true) / len(y_true) def batch_gradient_descent(w, alpha, x, y): y_pred = x.dot(w) error = mse(y, y_pred).mean() grad = np.dot(x.T, gradient(y, y_pred)) w = w - alpha * grad return w, error def stochastic_gradient_descent(w, alpha, x, y, epoch): alpha_update = alpha for i in range(len(x)): y_pred = x[i].dot(w) grad = np.dot(x[i].T, (y_pred - y[i])) * 2 / len(x) w = w- alpha_update * grad alpha_update = alpha_update / (epoch+1) error = mse(y, x.dot(w)).mean() return w, error X_test = [] Y_test = [] for _ in range(10000): x = np.random.randn(d) y = w.dot(x) + np.random.randn() X_test.append(x) Y_test.append(y) X_test = np.array(X_test) Y_test = np.array(Y_test) def l2_mse(y_true, y_test, l, w): return ((y_true - y_test) ** 2) / len(y_true) + l * np.sum(w ** 2) def l2_gradient(y_true, y_test): return 2 * (y_test - y_true) / len(y_true) def batch_gradient_descent_with_l2(w, alpha, x, y, l): y_pred = x.dot(w) error = l2_mse(y, y_pred, l, w).mean() grad = np.dot(x.T, l2_gradient(y, y_pred)) w = w - alpha * grad - alpha * l * w *2 return w, error if __name__ == \u0026quot;__main__\u0026quot;: train_loss = [] test_loss = [] print(\u0026quot;Batch Gradient Descent\u0026quot;) for epoch in range(1000): w, error = batch_gradient_descent(w, 0.01, X, Y) # train y_pred = X_test.dot(w) error_test = mse(Y_test, y_pred).mean() # test if epoch % 100 == 0: print(\u0026quot;Epoch: {}, TrainError: {}, TestError: {}\u0026quot;.format(epoch, error, error_test)) train_loss.append(error) test_loss.append(error_test) plt.plot(train_loss, label=\u0026quot;Train-No-L2\u0026quot;) plt.legend() plt.xlabel(\u0026quot;Epoch\u0026quot;) plt.ylabel(\u0026quot;Loss\u0026quot;) plt.show() plt.plot(test_loss, label=\u0026quot;Test-No-L2\u0026quot;) plt.xlabel(\u0026quot;Epoch\u0026quot;) plt.ylabel(\u0026quot;Loss\u0026quot;) plt.legend() plt.show() plt.plot(train_loss, label=\u0026quot;Train-No-L2\u0026quot;) plt.plot(test_loss, label=\u0026quot;Test-No-L2\u0026quot;) plt.legend() plt.show() # ============================================ train_loss = [] test_loss = [] print(\u0026quot;Batch Gradient Descent with L2\u0026quot;) l = 0.0001 # lambda for epoch in range(1000): w, error = batch_gradient_descent_with_l2(w, 0.01, X, Y, l) # train y_pred = X_test.dot(w) error_test = l2_mse(Y_test, y_pred, l, w).mean() # test if epoch % 100 == 0: print(\u0026quot;Epoch: {}, TrainError: {}, TestError: {}\u0026quot;.format(epoch, error, error_test)) train_loss.append(error) test_loss.append(error_test) plt.plot(train_loss, label=\u0026quot;Train-L2\u0026quot;) plt.legend() plt.xlabel(\u0026quot;Epoch\u0026quot;) plt.ylabel(\u0026quot;Loss\u0026quot;) plt.show() plt.plot(test_loss, label=\u0026quot;Test-L2\u0026quot;) plt.xlabel(\u0026quot;Epoch\u0026quot;) plt.ylabel(\u0026quot;Loss\u0026quot;) plt.legend() plt.show() plt.plot(train_loss, label=\u0026quot;Train-L2\u0026quot;) plt.plot(test_loss, label=\u0026quot;Test-L2\u0026quot;) plt.legend() plt.show() 其实很简单，就是数学公式的复现。 ## 总结\n缩减方法（逐步线性回归或岭回归），就是将一些系数缩减成很小的值或者直接缩减为0。这样做，就增大了模型的偏差（减少了一些特征的权重），通过把一些特征的回归系数缩减到0，同时也就减少了模型的复杂度。消除了多余的特征之后，模型更容易理解，同时也降低了预测误差。\n","date":"2021-11-25T00:00:00Z","permalink":"/%E7%BA%BF%E6%80%A7%E5%9B%9E%E5%BD%92/","title":"线性回归"},{"content":"计数质数 https://leetcode-cn.com/problems/count-primes/\n一开始直接暴力，隐约感觉会超时，果然不出我所料\nclass Solution: def countPrimes(self, n: int) -\u0026gt; int: counts = 0 for i in range(n): if self.isprime(i): counts += 1 return counts def isprime(self,n): from itertools import count if n \u0026lt;=1: return False for i in count(2): if i* i \u0026gt; n: return True if n % i == 0: return False 我还特意用了itertools库，没想到还是超时了\n埃氏筛 class Solution: def countPrimes(self, n: int) -\u0026gt; int: res = [1] * n count = 0 for i in range(2, n): if res[i]: count += 1 for j in range(i*i, n, i): res[j] = 0 return count 埃氏筛的原理：从 2 开始，将每个质数的倍数都标记为合数。同样的，标记到 根号n停止。\n假设一个数 i 为质数时，那么此时大于 i 且是 i 的倍数的数一定不是质数，例如 2i，3i…。那么我们将这些不是质数的数进行标记。\n这里需要注意，标记应该从 i * i 开始，而不是 2 * i 开始。因为对于每个数 i 来说，枚举是从小到大的，此时前面数字的倍数都已经进行了标记。对于 i 而言，2∗i 也肯定会被在枚举数字 2 时进行标记，[2, i) 区间的数同理。\n欧拉筛（线性筛） 具体的证明不说了，背板子就行\nclass Solution: def countPrimes(self, n: int) -\u0026gt; int: if n \u0026lt;= 1: return 0 is_prime = [True] * n is_prime[0] = is_prime[1] = False res = [] for i in range(2, n): if is_prime[i]: res.append(i) j = 0 while j \u0026lt; len(res) and (tmp:=i*res[j]) \u0026lt; n: is_prime[tmp] = False if i % res[j] == 0: # 如果res[j]为当前数的约数，则i*res[j+1]等后面的数必然会在后面的计算得到。就结束循环。这减少了重复计算。 break j += 1 return len(res) ","date":"2021-11-19T00:00:00Z","permalink":"/%E8%AE%A1%E6%95%B0%E8%B4%A8%E6%95%B0/","title":"计数质数"},{"content":"感知机算法 感知机印象中没有系统学习过但是是一个很简单的算法，最近看了一下李航老师的统计学习方法，发现感知机的思想和svm十分类似，并且比svm简单的多，不需要间隔最大，只需要分开就可以。同时老师在课堂上面讲的版本也有点不一样，主要是计算上的不同，本质还是一样的。然后就打算整理一下这一块。\n感知机模型 假设输入空间（特征空间) 是 \\(\\mathcal{X} \\subseteq \\mathbf{R}^n\\), 输出空间是 \\(\\mathcal{Y}=\\\\{+1,-1\\\\}\\) 。输入 \\(x \\in \\mathcal{X}\\) 表示实例的特征向量, 对应于输入空间 (特征空间 ) 的点; 输出 \\(y \\in \\mathcal{Y}\\) 表示实例的类别。由输入空间到输出空间的如下函数\n\\[ f(x)=\\operatorname{sign}(w \\cdot x+b) \\]\n称为感知机。其中, \\(w\\) 和 \\(b\\) 为感知机模型参数, \\(w \\in \\mathbf{R}^n\\) 叫作权值 (weight) 或权值向 量 (weight vector), \\(b \\in \\mathbf{R}\\) 叫作偏置 (bias), \\(w \\cdot x\\) 表示 \\(w\\) 和 \\(x\\) 的内积。sign 是符号 函数, 即\n\\[ \\operatorname{sign}(x)=\\left\\{\\begin{array}{cc} +1, \u0026amp; x \\geqslant 0 \\\\\\\\ -1, \u0026amp; x\u0026lt;0 \\end{array}\\right. \\]\n损失函数 假设训练数据集是线性可分的, 感知机学习的目标是求得一个能够将训练集正实 例点和负实例点完全正确分开的分离超平面。为了找出这样的超平面, 即确定感知 机模型参数 \\(w, b\\), 需要确定一个学习策略, 即定义 (经验) 损失函数并将损失函数极 小化。 损失函数的一个自然选择是误分类点的总数。但是, 这样的损失函数不是参数 \\(w\\), \\(b\\) 的连续可导函数, 不易优化。损失函数的另一个选择是误分类点到超平面 \\(S\\) 的总距 离, 这是感知机所采用的。为此, 首先写出输入空间 \\(\\mathbf{R}^n\\) 中任一点 \\(x_0\\) 到超平面 \\(S\\) 的 距离:\n\\[ \\frac{1}{\\|w\\|}\\left|w \\cdot x_0+b\\right| \\]\n这里, \\(\\|w\\|\\) 是 \\(w\\) 的 \\(L_2\\) 范数。 其次, 对于误分类的数据 \\(\\left(x_i, y_i\\right)\\) 来说,\n\\[ -y_i\\left(w \\cdot x_i+b\\right)\u0026gt;0 \\]\n成立。因为当 \\(w \\cdot x_i+b\u0026gt;0\\) 时, \\(y_i=-1\\); 而当 \\(w \\cdot x_i+b\u0026lt;0\\) 时, \\(y_i=+1\\) 。 因此, 误 分类点 \\(x_i\\) 到超平面 \\(S\\) 的距离是\n\\[ -\\frac{1}{\\|w\\|} y_i\\left(w \\cdot x_i+b\\right) \\]\n这样, 假设超平面 \\(S\\) 的误分类点集合为 \\(M\\), 那么所有误分类点到超平面 \\(S\\) 的总 距离为\n\\[ -\\frac{1}{\\|w\\|} \\sum_{x_i \\in M} y_i\\left(w \\cdot x_i+b\\right) \\]\n不考虑 \\(\\frac{1}{\\|w\\|}\\), 就得到感知机学习的损失函数。\n即\n\\[ L(w, b) = -\\sum_{x_i \\in M} y_i\\left(w \\cdot x_i+b\\right) \\]\n使用梯度下降算法更新参数，对损失函数求导得到：\n\\[ \\begin{aligned} \u0026amp;\\nabla_w L(w, b)=-\\sum_{x_i \\in M} y_i x_i \\\\\\\\ \u0026amp;\\nabla_b L(w, b)=-\\sum_{x_i \\in M} y_i \\end{aligned} \\]\n随机选取一个误分类点 \\(\\left(x_i, y_i\\right)\\), 对 \\(w, b\\) 进行更新:\n\\[ \\begin{gathered} w \\leftarrow w+\\eta y_i x_i \\\\\\\\ b \\leftarrow b+\\eta y_i \\end{gathered} \\] ## 算法流程 总结可得，感知机算法流程如下：\n输入: 训练数据集 \\(T=\\left\\{\\left(x_1, y_1\\right),\\left(x_2, y_2\\right), \\cdots,\\left(x_N, y_N\\right)\\right\\\\\\}\\), 其中 \\(x_i \\in \\mathcal{X}=\\mathbf{R}^n, y_i \\in\\) \\(\\mathcal{Y}=\\\\{-1,+1\\\\}, i=1,2, \\cdots, N\\); 学习率 \\(\\eta(0\u0026lt;\\eta \\leqslant 1)\\); 输出: \\(w, b\\); 感知机模型 \\(f(x)=\\operatorname{sign}(w \\cdot x+b)\\) 。 (1) 选取初值 \\(w_0, b_0\\); (2) 在训练集中选取数据 \\(\\left(x_i, y_i\\right)\\); (3) 如果 \\(y_i\\left(w \\cdot x_i+b\\right) \\leqslant 0\\),\n\\[ \\begin{aligned} \u0026amp;w \\leftarrow w+\\eta y_i x_i \\\\\\\\ \u0026amp;b \\leftarrow b+\\eta y_i \\end{aligned} \\]\n转至 (2), 直至训练集中没有误分类点。 这很容易理解。就是求解最佳参数\\(w\\)和\\(b\\)，使用梯度下降算法，对于每个样本，如果其真实的标签与预测的结果符号不一致，也就是sign函数之前的结果不同号，则说明分类错误，则就需要更新参数，不断地继续更新直到所有的样本都分类正确。\n另一种表达方式 感知器: 用数据训练线性模型 \\(g({x})={w}^T {x}+w_0\\) 增广的样本向量:\n\\[ {y}=\\left(1 ; x_1 ; x_2 ; \\ldots ; x_d\\right) \\]\n增广的权向量:\n\\[ {\\alpha}=\\left(w_0 ; w_1 ; \\ldots ; w_d\\right) \\]\n线性判别函数:\n\\[ g({y})={\\alpha}^T {y} \\]\n决策规则: 如果 \\(g({y})\u0026gt;0\\), 则 \\(y \\in \\omega_0\\); 如果 \\(g({y})\u0026lt;0\\), 则 \\(y \\in \\omega_1\\)\n若定义新变量 \\(y^{\\prime}\\), 使\n\\[ y_i^{\\prime}=\\left\\{\\begin{array}{lll} y_i, \\text { 若 } \u0026amp; {y}_i \\in \\omega_0 \\\\ -{y}_i, \\text { 若 } \u0026amp; {y}_i \\in \\omega_1 \\end{array} \\quad i=1,2, \\ldots, m\\right. \\]\n样本可分性条件变为：存在 \\(\\alpha\\), 使\n\\[ {\\alpha}^T {y}_i^{\\prime}\u0026gt;0, i=1,2, \\ldots, m \\]\n\\(y^{\\prime}\\) 称作规范化增广样本向量, 仍记作 \\(y\\) 。\n可以用这样的形式定义损失函数为：\n\\[ J(\\alpha) = \\sum_{\\alpha^Ty_k \\leq 0} (-\\alpha^Ty_k) \\] 其中w和b合并为了\\(\\alpha\\)。\\(y_k\\)为原来的x加上了1用于与偏置b对应。\n梯度下降迭代法求解 \\[ \\boldsymbol{\\alpha}(t+1)=\\boldsymbol{\\alpha}(t)-\\rho_t \\nabla J_P(\\boldsymbol{\\alpha}) \\]\n下一时刻的权向量是把当前时刻的权向量向目标函数的负梯度方向调整一个修 正量, \\(\\rho_t\\) 为调整的步长 (“学习率”)。\n\\[ \\nabla J_P(\\boldsymbol{\\alpha})=\\frac{\\partial J_P(\\boldsymbol{\\alpha})}{\\partial \\boldsymbol{\\alpha}}=\\sum_{\\alpha^T y_k \\leq 0}\\left(-y_k\\right) \\]\n所以\n\\[ \\alpha(t+1)=\\alpha(t)+\\rho_t \\sum_{\\alpha^T y_k \\leq 0} y_k \\]\n即每次迭代时把错分的样本按照某个系数加到权向量上。 当没有错分样本时, 得到一个合适的解 $^* $ 。\n固定增量法 （1）任意选择初始权向量 \\(\\alpha(0)\\); (2) 对样本 \\(y_j\\), 若 \\(\\alpha(t)^T y_j \\leq 0\\), 则 \\(\\alpha(t+1)=\\alpha(t)+y_j\\) (假设 \\(\\left.\\rho_t=1\\right)\\), 否则继 续; (3) 对所有样本重复 (2), 直至对所有的样本都有 \\(\\alpha(t)^T y_j\u0026gt;0\\), 即 \\(J_P(\\boldsymbol{\\alpha})=0\\)\n与梯度下降法的区别就是每次只对一个样本更新，可以这样理解： 原始的数据，对于第二类则增广之后取负数就可以理解为前面第一种表达的\\(y_i\\left(w \\cdot x_i+b\\right)\\)， 大于0则说明分类正确，否则说明分类错误，就需要更新参数。\n","date":"2021-11-16T00:00:00Z","permalink":"/%E6%84%9F%E7%9F%A5%E6%9C%BA%E7%AE%97%E6%B3%95/","title":"感知机算法"},{"content":"最大数 题目： https://leetcode-cn.com/problems/largest-number/\n思路： 一开始直接暴力搜索，把所有的情况都列举然后比较，结果超时了，最后利用了自定义排序的方法\n代码： class Solution: def largestNumber(self, nums: List[int]) -\u0026gt; str: class Comapre(str): def __lt__(self,other): return int(self+other) \u0026gt; int(other+self) nums.sort(key=Comapre) return str(int(\u0026#39;\u0026#39;.join(map(str,nums)))) 注意的是这里利用了自定义的比较类型，继承了str，也可以从functools里导入cmp_to_key方法来实现比较\npython3之后不支持cmp，所用key函数并不直接比较任意两个原始元素，而是通过key函数把那些元素转换成一个个新的可比较对象，也就是元素的key，然后用元素的key代替元素去参与比较。如果原始元素本来就是可比较对象，比如数字、字符串，那么不考虑性能优化可以直接sort(key=lambda e: e)。不过这种基于key函数的设计倾向于每个元素的大小有个绝对标准，但有时却会出现单个元素并没有一个绝对的大小的情况，此时可以使用 functools.cmp_to_key构建基于多个元素的比较函数。\n","date":"2021-11-08T00:00:00Z","permalink":"/%E6%9C%80%E5%A4%A7%E6%95%B0/","title":"最大数"},{"content":" image.png image.png ","date":"2021-11-02T00:00:00Z","permalink":"/lstm/","title":"LSTM"},{"content":"检查平衡性 题目： https://leetcode-cn.com/problems/check-balance-lcci/\n思路： 算深度，然后作差是否大于1\n代码： # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isBalanced(self, root: TreeNode) -\u0026gt; bool: if self.maxdepth(root) \u0026lt; 1: return True if abs(self.maxdepth(root.left) - self.maxdepth(root.right)) \u0026gt; 1: return False return self.isBalanced(root.right) and self.isBalanced(root.left) def maxdepth(self,root): if not root: return 0 return 1 + max(self.maxdepth(root.right),self.maxdepth(root.left)) ","date":"2021-10-27T00:00:00Z","permalink":"/%E6%A3%80%E6%9F%A5%E5%B9%B3%E8%A1%A1%E6%80%A7-1/","title":"检查平衡性"},{"content":"检查平衡性 题目： https://leetcode-cn.com/problems/check-balance-lcci/\n思路： 算深度，然后作差是否大于1\n代码： # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isBalanced(self, root: TreeNode) -\u0026gt; bool: if self.maxdepth(root) \u0026lt; 1: return True if abs(self.maxdepth(root.left) - self.maxdepth(root.right)) \u0026gt; 1: return False return self.isBalanced(root.right) and self.isBalanced(root.left) def maxdepth(self,root): if not root: return 0 return 1 + max(self.maxdepth(root.right),self.maxdepth(root.left)) ","date":"2021-10-27T00:00:00Z","permalink":"/%E6%A3%80%E6%9F%A5%E5%B9%B3%E8%A1%A1%E6%80%A7/","title":"检查平衡性"},{"content":"语言模型 语言模型是一个很大的主题，很多nlp的任务都是基于语言模型进行的，因此理解语言模型是很重要的。\n语言模型简单说就是 计算一个句子在语言中出现的概率。\n数学表示 一个语言模型通常构建字符串s的概率分布p(s)，这里p(s)反应字符串s作为一个句子出现的概率。\n对于一个由m个基元（可以是字、词或者短语）构成的句子\\(s=w_1,w_2, \\dots w_m\\)，其概率计算公式可以表示为(乘法公式，详见bayes)：\n\\[ p(s) = p(w_1)p(w_2\\mid w_1)p(w_3\\mid w_1, w_2)\\cdots p(w_m\\mid w_1w_2\\dots w_{m-1})= \\prod_{i=1}^mp(w_i\\mid w_1\\cdots,w_{i-1}) \\]\n但是很明显这个计算复杂度是极大的。 ## 评价指标 语言模型的常用评价指标是困惑度（perplexity）：在一个测试数据上的perplexity越低，说明建模的效果越好。perplexity计算公式如下：\n简单来说，困惑度就是刻画一个语言模型预测一个语言样本的能力，其实际上就是计算每一个词得到的概率倒数的几何平均，即模型预测下一个词的平均可选择数量。\n在实际应用中通常使用log的形式，即： 统计语言模型 n-gram 为了解决复杂度高的问题，因此引入马尔科夫假设，即当前词的预测概率只与之前n-1个词相关，基于此，语言模型可以修改如下：\n\\[ p(s) = p(w_1, w_2, \\dots, w_m) = \\prod_{i=1}^mp(w_i\\mid w_{i-n+1}, w_{i-1}) \\] 当n取1,2,3时，n-gram可以称为unigram、bigram、trigram。n越大复杂度越高。\nn-gram model一般采用MLE进行参数估计：\n\\[ p(w_i\\mid w_{i-n+1}, \\cdots, w_{i-1}) = \\frac{C(w_{i-n+1}, \\cdots,w_{i-1}, w_i)}{C(w_{i-n+1}, \\cdots, w_{i-1})} \\]\n即使训练语料再大，也存在参数为0的情况，这时候就需要引入数据平滑策略，其中最为常用的就是拉普拉斯平滑。\n拉普拉斯平滑 Add one 拉普拉斯平滑，即强制让所有的n-gram至少出现一次，只需要在分子和分母上分别做加法即可。这个方法的弊端是，大部分n-gram都是没有出现过的，很容易为他们分配过多的概率空间。 #### Add-K 在Add-one的基础上做了一点小改动，原本是加一，现在加上一个小于1的常数K K。但是缺点是这个常数仍然需要人工确定，对于不同的语料库K可能不同。 k取1时与add one 相同\nKneser-Ney Smoothing 这是目前ngram模型效果最好的平滑方法。 ## 神经网络语言模型\n具体可以看本博客有关神经网络语言模型的内容，NNLM是第一个出现的神经网络语言模型。\n","date":"2021-10-26T00:00:00Z","permalink":"/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/","title":"语言模型"},{"content":"隐马尔科夫模型 介绍 HMM可以看做是处理序列模型的传统方法。 一般来说HMM解决三个问题：\n评估观察序列概率。给定模型\\(\\lambda=(A,B,\\prod)\\)和观察序列\\(O=\\\\{o_1,o_2,\\dots,o_T\\\\}\\)，计算在模型\\(\\lambda\\)下观测序列O出现的概率\\(P(O\\lvert \\lambda)\\)，这个问题需要用到前向后向算法，属于三个问题中最简单的。 预测问题，也叫解码问题。即给定模型\\(\\lambda = (A,B,\\prod)\\)和观测序列\\(O=\\\\{o_1,o_2,\\dots,o_T\\\\}\\)，求在给定观测序列条件下，最可能出现的对应的状态序列，这个问题的求解需要用到基于动态规划的维特比算法，这个问题属于三个问题中复杂度居中的算法。 模型参数学习问题。即给定观测序列\\(O=\\\\{o_1,o_2,\\dots,o_T\\\\}\\)，估计模型\\(\\lambda = (A,B,\\prod)\\)的参数，使得该模型下观测序列的条件概率\\(P(O\\lvert\\lambda)\\)最大，这个问题的求解需要用到基于EM算法的鲍姆-韦尔奇算法。属于三个问题中最复杂的。 定义 设 \\(Q\\) 是所有可能的状态的集合, \\(V\\) 是所有可能的观测的集合:\n\\[ Q=\\left\\{q_1, q_2, \\cdots, q_N\\right\\}, \\quad V=\\left\\{v_1, v_2, \\cdots, v_M\\right\\} \\]\n其中, \\(N\\) 是可能的状态数, \\(M\\) 是可能的观测数。 \\(I\\) 是长度为 \\(T\\) 的状态序列, \\(O\\) 是对应的观测序列:\n\\[ I=\\left(i_1, i_2, \\cdots, i_T\\right), \\quad O=\\left(o_1, o_2, \\cdots, o_T\\right) \\]\n\\(A\\) 是状态转移概率矩阵:\n\\[ A=\\left[a_{i j}\\right]_{N \\times N} \\]\n其中,\n\\[ a_{i j}=P\\left(i_{t+1}=q_j \\mid i_t=q_i\\right), \\quad i=1,2, \\cdots, N ; \\quad j=1,2, \\cdots, N \\]\n是在时刻 \\(t\\) 处于状态 \\(q_i\\) 的条件下在时刻 \\(t+1\\) 转移到状态 \\(q_j\\) 的概率。 \\(B\\) 是观测概率矩阵:\n\\[ B=\\left[b_j(k)\\right]_{N \\times M} \\]\n其中,\n\\[ b_j(k)=P\\left(o_t=v_k \\mid i_t=q_j\\right), \\quad k=1,2, \\cdots, M ; \\quad j=1,2, \\cdots, N \\]\n是在时刻 \\(t\\) 处于状态 \\(q_j\\) 的条件下生成观测 \\(v_k\\) 的概率。 \\(\\pi\\) 是初始状态概率向量:\n\\[ \\pi=\\left(\\pi_i\\right) \\]\n其中,\n\\[ \\pi_i=P\\left(i_1=q_i\\right), \\quad i=1,2, \\cdots, N \\]\n是时刻 \\(t=1\\) 处于状态 \\(q_i\\) 的概率。 隐马尔可夫模型由初始状态概率向量 \\(\\pi\\) 、状态转移概率矩阵 \\(A\\) 和观测概率矩阵 \\(B\\) 决定。 \\(\\pi\\) 和 \\(A\\) 决定状态序列, \\(B\\) 决定观测序列。因此, 隐马尔可夫模型 \\(\\lambda\\) 可以用三元 符号表示, 即\n\\[ \\lambda=(A, B, \\pi) \\]\n\\(A, B, \\pi\\) 称为隐马尔可夫模型的三要素。\n状态转移概率矩阵 \\(A\\) 与初始状态概率向量 \\(\\pi\\) 确定了隐藏的马尔可夫链, 生成不 可观测的状态序列。观测概率矩阵 \\(B\\) 确定了如何从状态生成观测, 与状态序列综合确 定了如何产生观测序列。\n两个基本假设 齐次马尔可夫性假设, 即假设隐藏的马尔可夫链在任意时刻 \\(t\\) 的状态只依赖 于其前一时刻的状态, 与其他时刻的状态及观测无关, 也与时刻 \\(t\\) 无关: \\[ P\\left(i_t \\mid i_{t-1}, o_{t-1}, \\cdots, i_1, o_1\\right)=P\\left(i_t \\mid i_{t-1}\\right), \\quad t=1,2, \\cdots, T \\]\n观测独立性假设, 即假设任意时刻的观测只依赖于该时刻的马尔可夫链的状 态, 与其他观测及状态无关: \\[ P\\left(o_t \\mid i_T, o_T, i_{T-1}, o_{T-1}, \\cdots, i_{t+1}, o_{t+1}, i_t, i_{t-1}, o_{t-1}, \\cdots, i_1, o_1\\right)=P\\left(o_t \\mid i_t\\right) \\]\n观测序列生成的过程 输入: 隐马尔可夫模型 \\(\\lambda=(A, B, \\pi)\\), 观测序列长度 \\(T\\); 输出: 观测序列 \\(O=\\left(o_1, o_2, \\cdots, o_T\\right)\\) 。 (1) 按照初始状态分布 \\(\\pi\\) 产生状态 \\(i_1\\); (2) 令 \\(t=1\\); (3) 按照状态 \\(i_t\\) 的观测概率分布 \\(b_{i_t}(k)\\) 生成 \\(o_t\\) : (4) 按照状态 \\(i_t\\) 的状态转移概率分布 \\(\\left\\{a_{i_t i_{t+1}}\\right\\}\\) 产生状态 \\(i_{t+1}, i_{t+1}=1,2, \\cdots, N\\); (5) 令 \\(t=t+1\\); 如果 \\(t\u0026lt;T\\), 转步 (3); 否则, 终止。\n概率计算问题 直接计算（复杂度太高） 给定模型 \\(\\lambda=(A, B, \\pi)\\) 和观测序列 \\(O=\\left(o_1, o_2, \\cdots, o_T\\right)\\), 计算观测序列 \\(O\\) 出现 的概率 \\(P(O \\mid \\lambda)\\) 。最直接的方法是按概率公式直接计算。通过列举所有可能的长度为 \\(T\\) 的状态序列 \\(I=\\left(i_1, i_2, \\cdots, i_T\\right)\\), 求各个状态序列 \\(I\\) 与观测序列 \\(O=\\left(o_1, o_2, \\cdots, o_T\\right)\\) 的联合概率 \\(P(O, I \\mid \\lambda)\\), 然后对所有可能的状态序列求和, 得到 \\(P(O \\mid \\lambda)\\) 。 状态序列 \\(I=\\left(i_1, i_2, \\cdots, i_T\\right)\\) 的概率是:\n\\[ P(I \\mid \\lambda)=\\pi_{i_1} a_{i_1 i_2} a_{i_2 i_3} \\cdots a_{i_{T-1} i_T} \\]\n对固定的状态序列 \\(I=\\left(i_1, i_2, \\cdots, i_T\\right)\\), 观测序列 \\(O=\\left(o_1, o_2, \\cdots, o_T\\right)\\) 的概率是:\n\\[ P(O \\mid I, \\lambda)=b_{i_1}\\left(o_1\\right) b_{i_2}\\left(o_2\\right) \\cdots b_{i_T}\\left(o_T\\right) \\]\n\\(O\\) 和 \\(I\\) 同时出现的联合概率为\n\\[ \\begin{aligned} P(O, I \\mid \\lambda) \u0026amp;=P(O \\mid I, \\lambda) P(I \\mid \\lambda) \\\\\\\\ \u0026amp;=\\pi_{i_1} b_{i_1}\\left(o_1\\right) a_{i_1 i_2} b_{i_2}\\left(o_2\\right) \\cdots a_{i_{T-1} i_T} b_{i_T}\\left(o_T\\right) \\end{aligned} \\]\n然后, 对所有可能的状态序列 \\(I\\) 求和, 得到观测序列 \\(O\\) 的概率 \\(P(O \\mid \\lambda)\\), 即\n\\[ \\begin{aligned} P(O \\mid \\lambda) \u0026amp;=\\sum_I P(O \\mid I, \\lambda) P(I \\mid \\lambda) \\\\\\\\ \u0026amp;=\\sum_{i_1, i_2, \\cdots, i_T} \\pi_{i_1} b_{i_1}\\left(o_1\\right) a_{i_1 i_2} b_{i_2}\\left(o_2\\right) \\cdots a_{i_{T-1} i_T} b_{i_T}\\left(o_T\\right) \\end{aligned} \\]\n这种算法复杂度太高，计算量太大，有效算法为前向算法和后向算法。\n前向算法 首先定义前向概率。 给定隐马尔可夫模型 \\(\\lambda\\), 定义到时刻 \\(t\\) 部分观测序列为 \\(o_1, o_2, \\cdots, o_t\\) 且状态为 \\(q_i\\) 的概率为前向概率, 记作\n\\[ \\alpha_t(i)=P\\left(o_1, o_2, \\cdots, o_t, i_t=q_i \\mid \\lambda\\right) \\]\n可以递推地求得前向概率 \\(\\alpha_t(i)\\) 及观测序列概率 \\(P(O \\mid \\lambda)\\) 。 (观测序列概率的前向算法) 输入: 隐马尔可夫慔型 \\(\\lambda\\), 观测序列 \\(O\\); 输出: 观测序列概率 \\(P(O \\mid \\lambda)\\) 。 (1) 初值\n\\[ \\alpha_1(i)=\\pi_i b_i\\left(o_1\\right), \\quad i=1,2, \\cdots, N \\]\n（2）递推 对 \\(t=1,2, \\cdots, T-1\\),\n\\[ \\alpha_{t+1}(i)=\\left[\\sum_{j=1}^N \\alpha_t(j) a_{j i}\\right] b_i\\left(o_{t+1}\\right), \\quad i=1,2, \\cdots, N \\]\n终止 \\[ P(O \\mid \\lambda)=\\sum_{i=1}^N \\alpha_T(i) \\]\n前向算法, 步骤 (1) 初始化前向概率, 是初始时刻的状态 \\(i_1=q_i\\) 和观测 \\(o_1\\) 的 联合概率。步骤 (2) 是前向概率的递推公式, 计算到时刻 \\(t+1\\) 部分观测序列为 \\(o_1, o_2, \\cdots, o_t, o_{t+1}\\) 且在时刻 \\(t+1\\) 处于状态 \\(q_i\\) 的前向概率, 如图 \\(10.1\\) 所示。在式 (10.16) 的方括弧里, 既然 \\(\\alpha_t(j)\\) 是到时刻 \\(t\\) 观测到 \\(o_1, o_2, \\cdots, o_t\\) 并在时刻 \\(t\\) 处于状态 \\(q_j\\) 的前向概率, 那么乘积 \\(\\alpha_t(j) a_{j i}\\) 就是到时刻 \\(t\\) 观测到 \\(o_1, o_2, \\cdots, o_t\\) 并在时刻 \\(t\\) 处于 状态 \\(q_j\\) 而在时刻 \\(t+1\\) 到达状态 \\(q_i\\) 的联合概率。对这个乘积在时刻 \\(t\\) 的所有可能的 #### 统计学习方法中前向算法的例子\n### 后向算法 给定隐马尔可夫模型 \\(\\lambda\\), 定义在时刻 \\(t\\) 状态为 \\(q_i\\) 的条件下, 从 \\(t+1\\) 到 \\(T\\) 的部分观测序列为 \\(o_{t+1}, o_{t+2}, \\cdots, o_T\\) 的概率为后向概率, 记作\n\\[ \\beta_t(i)=P\\left(o_{t+1}, o_{t+2}, \\cdots, o_T \\mid i_t=q_i, \\lambda\\right) \\]\n可以用递推的方法求得后向概率 \\(\\beta_t(i)\\) 及观测序列概率 \\(P(O \\mid \\lambda)\\) 。 (观测序列概率的后向算法) 输入: 隐马尔可夫模型 \\(\\lambda\\), 观测序列 \\(O\\); 输出: 观测序列概率 \\(P(O \\mid \\lambda)\\) 。 (1)\n\\[ \\beta_T(i)=1, \\quad i=1,2, \\cdots, N \\]\n对 \\(t=T-1, T-2, \\cdots, 1\\) \\[ \\beta_t(i)=\\sum_{j=1}^N a_{i j} b_j\\left(o_{t+1}\\right) \\beta_{t+1}(j), \\quad i=1,2, \\cdots, N \\]\n后向算法到这一步只用到了第二个观测值，还有第一个观测值没有用到。 因此最后要乘上。 (3)\n\\[ P(O \\mid \\lambda)=\\sum_{i=1}^N \\pi_i b_i\\left(o_1\\right) \\beta_1(i) \\]\n结合 利用前向概率和后向概率的定义可以将观测序列概率 \\(P(O \\mid \\lambda)\\) 统一写成\n\\[ P(O \\mid \\lambda)=\\sum_{i=1}^N \\sum_{j=1}^N \\alpha_t(i) a_{i j} b_j\\left(o_{t+1}\\right) \\beta_{t+1}(j), \\quad t=1,2, \\cdots, T-1 \\]\n也可以写成：\n\\[ P(O\\mid \\lambda) = \\sum_{i=1}^N[\\alpha_t(i)\\beta_t(i)], \\quad t=1,2,\\cdots, T-1 \\]\n一些概率和期望问题 利用前向概率和后向概率, 可以得到关于单个状态和两个状态概率的计算公式。 1. 给定模型 \\(\\lambda\\) 和观测 \\(O\\), 在时刻 \\(t\\) 处于状态 \\(q_i\\) 的概率。记\n\\[ \\gamma_t(i)=P\\left(i_t=q_i \\mid O, \\lambda\\right) \\]\n可以通过前向后向概率计算。事实上，\n\\[ \\gamma_t(i)=P\\left(i_t=q_i \\mid O, \\lambda\\right)=\\frac{P\\left(i_t=q_i, O \\mid \\lambda\\right)}{P(O \\mid \\lambda)} \\]\n由前向概率 \\(\\alpha_t(i)\\) 和后向概率 \\(\\beta_t(i)\\) 定义可知:\n\\[ \\alpha_t(i) \\beta_t(i)=P\\left(i_t=q_i, O \\mid \\lambda\\right) \\]\n于是得到:\n\\[ \\gamma_t(i)=\\frac{\\alpha_t(i) \\beta_t(i)}{P(O \\mid \\lambda)}=\\frac{\\alpha_t(i) \\beta_t(i)}{\\sum_{j=1}^N \\alpha_t(j) \\beta_t(j)} \\]\n给定模型 \\(\\lambda\\) 和观测 \\(O\\), 在时刻 \\(t\\) 处于状态 \\(q_i\\) 且在时刻 \\(t+1\\) 处于状态 \\(q_j\\) 的概 率。记 \\[ \\xi_t(i, j)=P\\left(i_t=q_i, i_{t+1}=q_j \\mid O, \\lambda\\right) \\]\n可以通过前向后向概率计算:\n\\[ \\xi_t(i, j)=\\frac{P\\left(i_t=q_i, i_{t+1}=q_j, O \\mid \\lambda\\right)}{P(O \\mid \\lambda)}=\\frac{P\\left(i_t=q_i, i_{t+1}=q_j, O \\mid \\lambda\\right)}{\\sum_{i=1}^N \\sum_{j=1}^N P\\left(i_t=q_i, i_{t+1}=q_j, O \\mid \\lambda\\right)} \\]\n而\n\\[ P(i_t=q_i,i_{t+1}=q_j,O | \\lambda) = \\alpha_t(i)a_{ij}b_j(o_{t+1})\\beta_{t+1}(j) \\]\n所以\n\\[ \\xi_t(i, j)=\\frac{\\alpha_t(i) a_{i j} b_j\\left(o_{t+1}\\right) \\beta_{t+1}(j)}{\\sum_{i=1}^N \\sum_{j=1}^N \\alpha_t(i) a_{i j} b_j\\left(o_{t+1}\\right) \\beta_{t+1}(j)} \\]\n将 \\(\\gamma_t(i)\\) 和 \\(\\xi_t(i, j)\\) 对各个时刻 \\(t\\) 求和, 可以得到一些有用的期望值。 在观测 \\(O\\) 下状态 \\(i\\) 出现的期望值: \\[ \\sum_{t=1}^T \\gamma_t(i) \\]\n（2）在观测 \\(O\\) 下由状态 \\(i\\) 转移的期望值:\n\\[ \\sum_{t=1}^{T-1} \\gamma_t(i) \\]\n在观测 \\(O\\) 下由状态 \\(i\\) 转移到状态 \\(j\\) 的期望值: \\[ \\sum_{t=1}^{T-1} \\xi_t(i, j) \\]\n预测问题 近似算法 近似算法的想法是, 在每个时刻 \\(t\\) 选择在该时刻最有可能出现的状态 $i_t^* $, 从而得 到一个状态序列 \\(I^* =\\left(i_1^* , i_2^* , \\cdots, i_T^* \\right)\\), 将它作为预测的结果。 给定隐马尔可夫模型 \\(\\lambda\\) 和观测序列 \\(O\\), 在时刻 \\(t\\) 处于状态 \\(q_i\\) 的概率 \\(\\gamma_t(i)\\) 是\n\\[ \\gamma_t(i)=\\frac{\\alpha_t(i) \\beta_t(i)}{P(O \\mid \\lambda)}=\\frac{\\alpha_t(i) \\beta_t(i)}{\\sum_{j=1}^N \\alpha_t(j) \\beta_t(j)} \\]\n在每一时刻 \\(t\\) 最有可能的状态 $i_t^* $ 是\n\\[ i_t^* =\\arg \\max_{1 \\leqslant i \\leqslant N}\\left[\\gamma_t(i)\\right], \\quad t=1,2, \\cdots, T \\]\n从而得到状态序列 \\(I^* =\\left(i_1^* , i_2^* , \\cdots, i_T^* \\right)\\) 。 近似算法的优点是计算简单, 其缺点是不能保证预测的状态序列整体是最有可能 的状态序列, 因为预测的状态序列可能有实际不发生的部分。事实上, 上述方法得到 的状态序列中有可能存在转移概率为 0 的相邻状态, 即对某些 \\(i, j, a_{i j}=0\\) 时。尽管 如此, 近似算法仍然是有用的。\n近似算法就是一种贪心的算法，每个时刻都取最有可能的状态，但整体序列并不一定是最优解。\n维特比算法 维特比算法实际上是用动态规划解隐马尔科夫模型预测问题，用动态规划求解概率最大路径，一条路径对应着一条状态序列。\n首先导入两个变量 \\(\\delta\\) 和 \\(\\Psi\\) 。定义在时刻 \\(t\\) 状态为 \\(i\\) 的所有单个路径 \\(\\left(i_1, i_2, \\cdots, i_t\\right)\\) 中概率最大值为\n\\[ \\delta_t(i)=\\max_{i_1, i_2, \\cdots, i_{t-1}} P\\left(i_t=i, i_{t-1}, \\cdots, i_1, o_t, \\cdots, o_1 \\mid \\lambda\\right), \\quad i=1,2, \\cdots, N \\]\n后面的部分与前向算法的部分有点类似。\n由定义可得变量 \\(\\delta\\) 的递推公式:\n\\[ \\begin{aligned} \\delta_{t+1}(i) \u0026amp;=\\max_{i_1, i_2, \\cdots, i_t} P\\left(i_{t+1}=i, i_t, \\cdots, i_1, o_{t+1}, \\cdots, o_1 \\mid \\lambda\\right) \\\\\\\\ \u0026amp;=\\max_{1 \\leqslant j \\leqslant N}\\left[\\delta_t(j) a_{j i}\\right] b_i\\left(o_{t+1}\\right), \\quad i=1,2, \\cdots, N ; \\quad t=1,2, \\cdots, T-1 \\end{aligned} \\]\n定义在时刻 \\(t\\) 状态为 \\(i\\) 的所有单个路径 \\(\\left(i_1, i_2, \\cdots, i_{t-1}, i\\right)\\) 中概率最大的路径的 第 \\(t-1\\) 个结点为\n\\[ \\Psi_t(i)=\\arg \\max_{1 \\leqslant j \\leqslant N}\\left[\\delta_{t-1}(j) a_{j i}\\right], \\quad i=1,2, \\cdots, N \\]\n可以简单理解为找到使得从t-1的j到t的i式子\\(\\delta_{t-1}(j)a_{ji}\\)最大的j，也就是说\\(\\Psi_t(i)\\)代表t-1时刻的最佳状态值，如果t时刻的最佳状态值是i的话，那么t-1时刻的最佳状态值就是\\(\\Psi_t(i)\\)，后面回溯要用到\n下面介绍维特比算法。\n输入: 模型 \\(\\lambda=(A, B, \\pi)\\) 和观测 \\(O=\\left(o_1, o_2, \\cdots, o_T\\right)\\); 输出: 最优路径 \\(I^* =\\left(i_1^* , i_2^* , \\cdots, i_T^* \\right)\\) 。 （1）初始化\n\\[ \\begin{gathered} \\delta_1(i)=\\pi_i b_i\\left(o_1\\right), \\quad i=1,2, \\cdots, N \\\\\\\\ \\Psi_1(i)=0, \\quad i=1,2, \\cdots, N \\end{gathered} \\]\n前者和前向算法的初始化是一样的。\n递推。对 \\(t=2,3, \\cdots, T\\) \\[ \\begin{array}{ll} \\delta_t(i)=\\max_{1 \\leqslant j \\leqslant N}\\left[\\delta_{t-1}(j) a_{j i}\\right] b_i\\left(o_t\\right), \\quad i=1,2, \\cdots, N \\\\\\\\ \\Psi_t(i)=\\arg \\max_{1 \\leqslant j \\leqslant N}\\left[\\delta_{t-1}(j) a_{j i}\\right], \\quad i=1,2, \\cdots, N \\end{array} \\]\n终止 \\[ \\begin{gathered} P^* =\\max_{1 \\leqslant i \\leqslant N} \\delta_T(i) \\\\\\\\ i_T^* =\\arg \\max_{1 \\leqslant i \\leqslant N}\\left[\\delta_T(i)\\right] \\end{gathered} \\]\n这里得到的是最后一个时刻的最佳状态值，然后进行回溯。\n最优路径回溯。对 \\(t=T-1, T-2, \\cdots, 1\\) \\[ i_t^* =\\Psi_{t+1}\\left(i_{t+1}^* \\right) \\]\n求得最优路径 \\(I^* =\\left(i_1^* , i_2^* , \\cdots, i_T^* \\right)\\) 。\n书上的例子 看一个例子就很容易理解了\n#### 代码\ndef viterbi(obs, states, start_p, trans_p, emit_p): V = [{}] # 列表idx代表时间t，字典的键代表状态值，值代表概率 path = {} # 最佳路径 for y in states: V[0][y] = start_p[y] * emit_p[y].get(obs[0], 1e-5) path[y] = [0] # 都初始化为0 for t in range(1, len(obs)): V.append({}) for y in states: em_p = emit_p[y].get(obs[t], 1e-5) # 取出观测值对应的概率 (prob, state) = max([(V[t-1][y0]*trans_p[y0][y]*em_p, y0) for y0 in states]) V[t][y] = prob path[y] = path[y] + [state] # 记录路径，state是当前时间t状态为y时t-1的最佳状态，也就是从state转移到y的概率最大。如果最后时刻的最佳状态是y，则回溯从y开始，最后的状态也是y。 (prob, state) = max((V[len(obs)-1][y], y) for y in states) # 求最后时刻的最大概率和状态。 return path[state][1:] + [state] # 初始状态是0，所以去掉第一个0，再加上最后时刻的最大概率的状态，结果就是最佳路径。这里键对值的过程相当于回溯了。 A = { 0 : {0:0.5, 1:0.2, 2:0.3}, 1 : {0:0.3, 1:0.5, 2:0.2}, 2 : {0:0.2, 1:0.3, 2:0.5} } B = { 0: {\u0026#39;红\u0026#39;: 0.5, \u0026#39;白\u0026#39;: 0.5}, 1: {\u0026#39;红\u0026#39;: 0.4, \u0026#39;白\u0026#39;: 0.6}, 2: {\u0026#39;红\u0026#39;: 0.7, \u0026#39;白\u0026#39;: 0.3} } π = {0:0.2, 1:0.4, 2:0.4} viterbi([\u0026#39;红\u0026#39;, \u0026#39;白\u0026#39;, \u0026#39;红\u0026#39;], [0, 1, 2], π, A, B) 拿上面的例子进行实验，思路是完全按照李航老师书的思路来的。还是比较容易理解的，只是回溯的实现不太一样，这个严格来说不能叫做回溯。\n学习算法 参考 《统计学习方法》李航\nhttps://www.52nlp.cn/hmm-learn-best-practices-one-introduction\nhttps://www.cnblogs.com/pinard/p/6945257.html\n","date":"2021-10-25T00:00:00Z","permalink":"/hmm/","title":"HMM"},{"content":"What? TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术，常用于挖掘文章中的关键词，而且算法简单高效，常被工业用于最开始的文本数据清洗。\nTF-IDF有两层意思，一层是”词频”（Term Frequency，缩写为TF），另一层是”逆文档频率”（Inverse Document Frequency，缩写为IDF）。\n假设我们现在有一片长文叫做《量化系统架构设计》词频高在文章中往往是停用词，“的”，“是”，“了”等，这些在文档中最常见但对结果毫无帮助、需要过滤掉的词，用TF可以统计到这些停用词并把它们过滤。当高频词过滤后就只需考虑剩下的有实际意义的词。\n但这样又会遇到了另一个问题，我们可能发现”量化”、“系统”、“架构”这三个词的出现次数一样多。这是不是意味着，作为关键词，它们的重要性是一样的？事实上系统应该在其他文章比较常见，所以在关键词排序上，“量化”和“架构”应该排在“系统”前面，这个时候就需要IDF，IDF会给常见的词较小的权重，它的大小与一个词的常见程度成反比。\n当有TF(词频)和IDF(逆文档频率)后，将这两个词相乘，就能得到一个词的TF-IDF的值。某个词在文章中的TF-IDF越大，那么一般而言这个词在这篇文章的重要性会越高，所以通过计算文章中各个词的TF-IDF，由大到小排序，排在最前面的几个词，就是该文章的关键词。\n步骤 第一步，计算词频：\n\\[ 词频(TF) = \\frac{某个词在文章中出现次数}{文章的总词数} \\] 第二步，计算逆文档频率：\n\\[ 逆文档频率(IDF) = log(\\frac{语料库的文档总数}{包含该词的文档数+1}) \\]\n1.为什么+1？是为了处理分母为0的情况。假如所有的文章都不包含这个词，分子就为0，所以+1是为了防止分母为0的情况。\n2.为什么要用log函数？log函数是单调递增，求log是为了归一化，保证反文档频率不会过大。\n3.会出现负数？肯定不会，分子肯定比分母大。\n第三步，计算TF-IDF：\n\\[ TF-IDF = 词频(TF) \\times 逆文档频率(IDF) \\]\n优缺点 TF-IDF的优点是简单快速，而且容易理解。缺点是有时候用词频来衡量文章中的一个词的重要性不够全面，有时候重要的词出现的可能不够多，而且这种计算无法体现位置信息，无法体现词在上下文的重要性。如果要体现词的上下文结构，那么你可能需要使用word2vec算法来支持。\n代码 corpus = [\u0026#39;this is the first document\u0026#39;, \u0026#39;this is the second second document\u0026#39;, \u0026#39;and the third one\u0026#39;, \u0026#39;is this the first document\u0026#39;] words_list = list() for i in range(len(corpus)): words_list.append(corpus[i].split(\u0026#39; \u0026#39;)) 手动实现 def manual(): from collections import Counter count_list = list() for i in range(len(words_list)): count = Counter(words_list[i]) count_list.append(count) import math def tf(word, count): return count[word] / sum(count.values()) def idf(word, count_list): n_contain = sum([1 for count in count_list if word in count]) return math.log(len(count_list) / (1 + n_contain)) def tf_idf(word, count, count_list): return tf(word, count) * idf(word, count_list) for i, count in enumerate(count_list): print(\u0026quot;第 {} 个文档 TF-IDF 统计信息\u0026quot;.format(i + 1)) scores = {word : tf_idf(word, count, count_list) for word in count} sorted_word = sorted(scores.items(), key = lambda x : x[1], reverse=True) for word, score in sorted_word: print(\u0026quot;\\tword: {}, TF-IDF: {}\u0026quot;.format(word, round(score, 5))) gensim def gensim_work(): from gensim import corpora # 赋给语料库中每个词(不重复的词)一个整数id dic = corpora.Dictionary(words_list) # 创建词典 new_corpus = [dic.doc2bow(words) for words in words_list] # 元组中第一个元素是词语在词典中对应的id，第二个元素是词语在文档中出现的次数 from gensim import models tfidf = models.TfidfModel(new_corpus) tfidf.save(\u0026quot;tfidf.model\u0026quot;) # 载入模型 tfidf = models.TfidfModel.load(\u0026quot;tfidf.model\u0026quot;) # 使用这个训练好的模型得到单词的tfidf值 res = [tfidf[temp] for temp in new_corpus] print(res) sklearn def sklearn_work(): from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer() tfidf = vectorizer.fit_transform(corpus) print(tfidf.toarray()) # 权重 print(vectorizer.get_feature_names()) # 单词 print(vectorizer.vocabulary_) # 词典 ","date":"2021-10-24T00:00:00Z","permalink":"/tf-idf/","title":"TF-IDF"},{"content":"记录一下实训学到的内容 布尔索引 布尔索引不能使用and or not ，只能用\u0026amp; | ~ 因为只能用位操作符\n花哨索引 arr = np.arange(32).reshape((8, 4)) arr array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23], [24, 25, 26, 27], [28, 29, 30, 31]]) arr[[1, 5, 7, 2], [0, 3, 1, 2]] array([ 4, 23, 29, 10]) 更常用的方式为 arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]] # 行，列重置顺序 array([[ 4, 7, 5, 6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11, 9, 10]]) pandas.cut import pandas as pd import numpy as np cars = pd.read_csv(\u0026quot;second_cars_info.csv\u0026quot;,encoding=\u0026quot;gbk\u0026quot;) final_ = [cars.Sec_price.min()] + list(np.linspace(10,100,10)) + [cars.Sec_price.max()] pd.cut(cars[\u0026quot;Sec_price\u0026quot;],bins=final_).value_counts().sort_index() # 对区间进行排序 # labels参数给每个区间贴上标签 .str的用法 可以对行列进行python字符串一样的操作\nstud_alcoh[[\u0026quot;Mjob\u0026quot;,\u0026#39;Fjob\u0026#39;]].apply(lambda x:x.str.upper()) # 对Mjob Fjob两列变成大写 # 也可以用applymap stud_alcoh[[\u0026quot;Mjob\u0026quot;,\u0026#39;Fjob\u0026#39;]].applymap(lambda x:x.upper()) #区别就在于.str # 因为applymap只能对dataframe进行操作,apply可以对Dataframe和Series进行操作。 groupby与apply灵活运用 coffee = pd.read_excel(\u0026quot;coffee.xlsx\u0026quot;) coffee.groupby(\u0026quot;区域\u0026quot;).apply(lambda x:x[\u0026quot;销售额\u0026quot;].sum()) # 如果是DataFramegroupby对象调用apply，转换函数处理的就是每一个分组（dataframe） # 等价于 coffee.groupby(\u0026quot;区域\u0026quot;)[\u0026#39;销售额\u0026#39;].sum() 求各个区域的 销售总和、平均销售额、预计销售额与实际销售额的差异总和 def transfer(x_):# x_类型是dataframe res = pd.Series() res[\u0026quot;销售总和\u0026quot;] = x_[\u0026quot;销售额\u0026quot;].sum() res[\u0026quot;平均销售额\u0026quot;] = x_[\u0026#39;销售额\u0026#39;].mean() res[\u0026#39;差值\u0026#39;] = ( x_[\u0026quot;销售额\u0026quot;] - x_[\u0026#39;预计销售额\u0026#39;]).mean() return res # 返回的是一个series coffee.groupby(\u0026quot;区域\u0026quot;).apply(transfer) 求出个区域中，高于该区域平均利润的记录 def transfer(x): # x(dataframe) mean_value = x[\u0026quot;利润额\u0026quot;].mean() condition = x[\u0026quot;利润额\u0026quot;] \u0026gt; mean_value return x[condition] # dataframe c = coffee.groupby(\u0026quot;区域\u0026quot;).apply(transfer) # 二维索引 # c.loc[(\u0026quot;Central\u0026quot;,0)] groupby也可以嵌套 def top_3_data(x):# x 表示DataFrame 每个区域的数据集 res = x.groupby(\u0026quot;产品名称\u0026quot;).sum().sort_values(\u0026quot;销售额\u0026quot;,ascending=False).iloc[:3] return res coffee.groupby(\u0026quot;区域\u0026quot;).apply(top_3_data) nlargest nsmallest 求每一列最大的几个多最小的几个，一般配合groupby和apply使用\npd.Grouper grouper = pd.Grouper(key=\"订单日期\",freq=\"M\") 按月进行分组 。freq=\"Y\"就是按年分组\ndef top_sale_month(x): #x 表示DataFrame 每个产品的数据集 grouper = pd.Grouper(key=\u0026quot;订单日期\u0026quot;,freq=\u0026quot;M\u0026quot;) return x.groupby(grouper).sum().nlargest(1,\u0026quot;销售额\u0026quot;) coffee.groupby(\u0026quot;产品名称\u0026quot;).apply(top_sale_month).reset_index() reset_index()方法就是按照原来的index显示，不然就是按照分组的结果展示 ## plt.xticks plt.yticks 可以理解为自定义x轴的坐标\nplt.figure(figsize=(12,8)) width = 0.2 plt.bar(np.arange(4)+0,np.random.randint(3,10,(4)),color=\u0026#39;r\u0026#39;,width=width) plt.bar(np.arange(4)+width*1,np.random.randint(3,10,(4)),color=\u0026#39;g\u0026#39;,width=width) plt.bar(np.arange(4)+width*2,np.random.randint(3,10,(4)),color=\u0026#39;b\u0026#39;,width=width) plt.xticks(np.arange(4)+width/2,list(\u0026quot;abcd\u0026quot;)) # 将0 1 2 3 替换为 a b c d plt.show() np.where np.where(condition, [x, y]),这里三个参数,其中必写参数是condition(判断条件),后边的x和y是可选参数.那么这三个参数都有怎样的要求呢?\ncondition：array_like，bool,当为True时，产生x，否则产生y 情况1：\nnp.where([[True, False], [True, True]], [[1, 2], [3, 4]], [[9, 8], [7, 6]]) 返回：\narray([[1, 8], [3, 4]]) 条件中第0个元素中的第0个元素是true,那么取x中的相对应元素1;\n条件中第0个元素中的第1个元素是false,那么取y中的相对应元素8;\n条件中第1个元素中的第0个元素是ture,那么取x中相对应的元素3;\n条件中第1个元素中的第1个元素是ture,那么取x中相对应的元素4;\n所以最后的结果中取出的元素是1,8,3,4. 情况2：\nx = np.arange(9.).reshape(3, 3) np.where(x\u0026gt;5) # 返回的是索引 (array([2, 2, 2], dtype=int64), array([0, 1, 2], dtype=int64)) 第一个array是行坐标，第二个array为列坐标。 不想要索引想要具体的数值也很简单\nx[np.where(x\u0026gt;5)] array([6., 7., 8.]) np.where(x \u0026lt; 5, x, -1) array([[ 0., 1., 2.], [ 3., 4., -1.], [-1., -1., -1.]]) 可见小于五的部分不变，大于5的则变成了-1 np.where常用于pandas的Series中。\n","date":"2021-10-24T00:00:00Z","permalink":"/%E5%AE%9E%E8%AE%AD%E5%AD%A6%E4%B9%A0%E5%86%85%E5%AE%B9/","title":"实训学习内容"},{"content":"平衡括号字符串的最少插入次数 题目： https://leetcode-cn.com/problems/minimum-insertions-to-balance-a-parentheses-string/\n思路： 本题和前面的题属于同一系列的，都是平衡括号字符串，不过这个不是1:1 而是1:2\n思路还是差不多，不过判断条件需要改变\n代码： class Solution: def minInsertions(self, s: str) -\u0026gt; int: res,temp = 0,0 for i in s: if i == \u0026#39;(\u0026#39;: temp += 2 if temp % 2 == 1: res += 1 temp -= 1 if i == \u0026#39;)\u0026#39;: temp -= 1 if temp == -1: res += 1 temp = 1 return res + temp 开始还是初始化，temp代表需求的右括号的数量\n如果有左括号的话，则让右括号的需求+2 因为一个左对应两个右\n这里有个难点，如果需求的是奇数的话，则应添加一个右括号，然后让需求减1\n如果是右括号，则需求 减1 如果需求的成了-1 的话 则在左边补上左括号 res++ 此时还需要一个右括号，则temp再初始化为1\n最后还是输出\nQ:为什么最后不是 temp == -2 res += 1 temp=0 呢？\n看看这个例子:\n\")))))))\"\n这是7个右括号，最后减到最后的话，temp是个负数，影响了最后的结果。\n所以还是要用原来的那样，-1的时候就进行判断，不用考虑奇偶的问题了\n","date":"2021-10-19T00:00:00Z","permalink":"/%E5%B9%B3%E8%A1%A1%E6%8B%AC%E5%8F%B7%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%9C%80%E5%B0%91%E6%8F%92%E5%85%A5%E6%AC%A1%E6%95%B0/","title":"平衡括号字符串的最少插入次数"},{"content":"可变与不可变 “-=”操作符会调用__isub__函数，而”-“操作符会调用__sub__函数，一般对于可变对象来说“-=”操作符会直接改变self自身。\nimport torch x1 = 1 x2 = 2 params = [x1, x2] for p in params: print(id(p), id(x1), id(x2)) p -= 4 print(id(p), id(x1), id(x2)) print(params) x1 = torch.Tensor([1]) x2 = torch.Tensor([2]) params = [x1, x2] for p in params: print(id(p), id(x1), id(x2)) p -= 4 print(id(p), id(x1), id(x2)) print(params) 9784896 9784896 9784928 9784768 9784896 9784928 9784928 9784896 9784928 9784800 9784896 9784928 [1, 2] 139752445458112 139752445458112 139752445458176 139752445458112 139752445458112 139752445458176 139752445458176 139752445458112 139752445458176 139752445458176 139752445458112 139752445458176 [tensor([-3.]), tensor([-2.])] 可以看到对于int类型，地址变换了，而torch类型，地址却没有变化。 p -= 4等价于p.sub_(4)。这个可变对象改变了自身。写成p = p - 4则会调用构造函数，并返回一个新的变量，也就不可能作用到原先的“可变对象”。 int类没有发生就地变化是因为它是一个不可变对象。\n这是python “-” 与”-=“的一个坑\n微妙的字符串 a = \u0026#39;some_thing\u0026#39; b = \u0026#39;some\u0026#39;+\u0026#39;_\u0026#39;+\u0026#39;thing\u0026#39; id(a),id(b) (1957716471920, 1957716471920) a = \u0026#39;wtf\u0026#39; b = \u0026#39;wtf\u0026#39; a is b True a = \u0026#39;wtf!\u0026#39; b = \u0026#39;wtf!\u0026#39; a is b False a,b = \u0026#39;wtf!\u0026#39;,\u0026#39;wtf!\u0026#39; a is b True \u0026#39;a\u0026#39;*20 is \u0026#39;aaaaaaaaaaaaaaaaaaaa\u0026#39;,\u0026#39;a\u0026#39;*21 is \u0026#39;aaaaaaaaaaaaaaaaaaaaa\u0026#39; (True, False) Cpython 在编译优化时, 某些情况下会尝试使用已经存在的不可变对象,成为字符串驻留 发生驻留之后, 许多变量可能指向内存中的相同字符串对象 所有长度为 0 和长度为 1 的字符串都被驻留.\n字符串在编译时被实现 (‘wtf’ 将被驻留, 但是 ’‘.join([’w’, ’t’, ’f’] 将不会被驻留) 字符串中只包含字母，数字或下划线时将会驻留. 所以 ’wtf!’ 由于包含 ! 而未被驻留。\n当在同一行将 a 和 b 的值设置为 “wtf!” 的时候, Python 解释器会创建一个新对象, 然后同时引用第二个变量.\n常量折叠(constant folding) 是 Python 中的一种 窥孔优化(peephole optimization) 技术. 这意味着在编译时表达式 ‘a’*20 会被替换为 ‘aaaaaaaaaaaaaaaaaaaa’ 以减少运行时的时钟周期. 只有长度小于 20 的字符串才会发生常量折叠.\na = 1 b = 1 a is b,id(a) == id(b) (True, True) is 是比较对象是否相同(is 表示对象标识符即 object identity)，即用 id() 函数查看的地址是否相同，如果相同则返回 True，如果不同则返回 False。is 不能被重载。\n== 是比较两个对象的值是否相等，此操作符内部调用的是 _eq() 方法。所以 a==b 等效于a.___eq__(b)，所以 = 可以被重载\n是时候来点蛋糕了! some_dict = {} some_dict[5.5] = \u0026#39;ruby\u0026#39; some_dict[5.0] = \u0026#39;javascript\u0026#39; some_dict[5] = \u0026#39;python\u0026#39; print(some_dict[5.0]) python 5 == 5.0,hash(5) == hash(5.0) (True, True) Python 字典通过检查键值是否相等和比较哈希值来确定两个键是否相同. 具有相同值的不可变对象在Python中始终具有相同的哈希值\n本质上,我们都一样 class WTF: pass print(WTF() == WTF(),WTF() is WTF()) print(hash(WTF()) == hash(WTF())) print(id(WTF()) == id(WTF())) False False True True 当调用 id 函数时, Python 创建了一个 WTF 类的对象并传给 id 函数. 然后 id 函数获取其id值 (也就是内存地址), 然后丢弃该对象. 该对象就被销毁了.\n当我们连续两次进行这个操作时, Python会将相同的内存地址分配给第二个对象. 因为 (在CPython中) id 函数使用对象的内存地址作为对象的id值, 所以两个对象的id值是相同的.\nprint(id(id(WTF())) == id(id(WTF()))) ##无论多少个ID都是True 原因就在上面 ##虽然id(id(WTF())) == id(id(WTF())) 但是id(WTF()) is id(WTF()) 返回True ##原因就是id这个函数调用的过程特殊性 print(id(WTF()) is id(WTF())) True False class WTF(object): def __init__(self): print(\u0026quot;I\u0026quot;) def __del__(self): print(\u0026quot;D\u0026quot;) WTF() is WTF() ##这时是两个对象一起创建，然后一起销毁，所以id不一样 I I D D False id(WTF()) == id(WTF()) ##这时候先创建一个销毁，然后再创建。对象销毁的顺序是造成所有不同之处的原因. I D I D True 为什么？ some_string = \u0026quot;wtf\u0026quot; some_dict = {} for i, some_dict[i] in enumerate(some_string): pass some_dict Python 语法 中对 for 的定义是:\n{0: \u0026#39;w\u0026#39;, 1: \u0026#39;t\u0026#39;, 2: \u0026#39;f\u0026#39;} for_stmt: \u0026#39;for\u0026#39; exprlist \u0026#39;in\u0026#39; testlist \u0026#39;:\u0026#39; suite [\u0026#39;else\u0026#39; \u0026#39;:\u0026#39; suite] 其中 exprlist 指分配目标. 这意味着对可迭代对象中的每一项都会执行类似 {exprlist} = {next_value} 的操作.\nfor i in range(4): print(i) i = 10 0 1 2 3 列表副本 list1 = [1,2,3,4,5] list2 = list1 list2[0] = 6 print(list1,list2) [6, 2, 3, 4, 5] [6, 2, 3, 4, 5] list1 = [1,2,3,4,5] list2 = list1[:] list2[0] = 6 print(list1,list2) [1, 2, 3, 4, 5] [6, 2, 3, 4, 5] 执行时机差异 array = [1, 8, 15] g = (x for x in array if array.count(x) \u0026gt; 0) ##这时候x为[1,8,15]的解包 ##而后面的array变成了下面的 array = [2, 8, 22] print(list(g)) [8] 在生成器表达式中, in 子句在声明时执行, 而条件子句则是在运行时执行. 所以在运行前, array 已经被重新赋值为 [2, 8, 22], 因此对于之前的 1, 8 和 15, 只有 count(8) 的结果是大于 0 的, 所以生成器只会生成 8.\narray_1 = [1,2,3,4] g1 = (x for x in array_1) array_1 = [1,2,3,4,5] array_2 = [1,2,3,4] g2 = (x for x in array_2) array_2[:] = [1,2,3,4,5] print(list(g1)) print(list(g2)) [1, 2, 3, 4] [1, 2, 3, 4, 5] 第二部分中 g1 和 g2 的输出差异则是由于变量 array_1 和 array_2 被重新赋值的方式导致的.\n在第一种情况下, array_1 被绑定到新对象 [1,2,3,4,5], 因为 in 子句是在声明时被执行的， 所以它仍然引用旧对象 [1,2,3,4].\n在第二种情况下, 对 array_2 的切片赋值将相同的旧对象 [1,2,3,4] 原地更新为 [1,2,3,4,5]. 因此 g2 和 array_2 仍然引用同一个对象(这个对象现在已经更新为 [1,2,3,4,5]).\n出人意料的is a = 256 b = 256 a is b True a = 257 b = 257 ##256 是一个已经存在的对象, 而 257 不是 ##当你启动Python 的时候, -5 到 256 的数值就已经被分配好了. ##这些数字因为经常使用所以适合被提前准备好 a is b False a,b = 257,257 ##当 a 和 b 在同一行中使用相同的值初始化时，会指向同一个对象. print(a is b) print(id(a),id(b)) True 1957717387056 1957717387056 [] == [] True [] is [] ##两个空列表位于不同的内存地址 False 一蹴即至! row = [\u0026quot;\u0026quot;] * 3 board = [row] * 3 board [[\u0026#39;\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;]] board[0][0] = \u0026#39;X\u0026#39; board ##这是因为之前对row做乘法导致的 [[\u0026#39;X\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;X\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;X\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;]] ##如何避免这种情况？ board = [[\u0026#39;\u0026#39;]*3 for _ in range(3)] board[0][0] = \u0026#39;X\u0026#39; board [[\u0026#39;X\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;], [\u0026#39;\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;\u0026#39;]] 麻烦的输出 funcs = [] res = [] for x in range(7): def func(): return x funcs.append(func) res.append(func()) func_res = [func() for func in funcs] print(func_res,res) [6, 6, 6, 6, 6, 6, 6] [0, 1, 2, 3, 4, 5, 6] power_x = [lambda x:x**i for i in range(11)] print([func(2) for func in power_x]) [1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024] 在循环内部定义一个函数时, 如果该函数在其主体中使用了循环变量, 则闭包函数将与循环变量绑定, 而不是它的值. 因此, 所有的函数都是使用最后分配给变量的值来进行计算的.\n连Python也知道爱是难言的 import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren\u0026#39;t special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you\u0026#39;re Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it\u0026#39;s a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let\u0026#39;s do more of those! love = this this is love True love is True False love is False False love is not True or False True love is not True or False;love is love True 三个引号 print(\u0026#39;wtfpython\u0026#39;\u0026#39;\u0026#39;) wtfpython print(\u0026quot;wtf\u0026quot; \u0026quot;python\u0026quot;) wtfpython 布尔你咋了? mixed_list = [False, 1.0, \u0026quot;some_string\u0026quot;, 3, True, [], False] integers_found_so_far = 0 booleans_found_so_far = 0 for item in mixed_list: if isinstance(item, int): integers_found_so_far += 1 elif isinstance(item, bool): booleans_found_so_far += 1 integers_found_so_far 4 booleans_found_so_far 0 another_dict = {} another_dict[True] = \u0026quot;JavaScript\u0026quot; another_dict[1] = \u0026quot;Ruby\u0026quot; another_dict[1.0] = \u0026quot;Python\u0026quot; another_dict[True] \u0026#39;Python\u0026#39; 布尔值是 int 的子类\nsome_iterable = (\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;) def some_func(val): return \u0026quot;something\u0026quot; [x for x in some_iterable] [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;] [(yield x) for x in some_iterable] \u0026lt;generator object \u0026lt;listcomp\u0026gt; at 0x000001CC6FFC3888\u0026gt; list([(yield x) for x in some_iterable]) [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;] list(((yield x) for x in some_iterable)) [\u0026#39;a\u0026#39;, None, \u0026#39;b\u0026#39;, None] list(some_func((yield x)) for x in some_iterable) [\u0026#39;a\u0026#39;, \u0026#39;something\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;something\u0026#39;] 消失的外部变量 e = 7 try: raise Exception() except Exception as e: pass print(e) ##error! 从有到无 some_list = [1, 2, 3] some_dict = { \u0026quot;key_1\u0026quot;: 1, \u0026quot;key_2\u0026quot;: 2, \u0026quot;key_3\u0026quot;: 3 } some_list = some_list.append(4) some_dict = some_dict.update({\u0026quot;key_4\u0026quot;: 4}) some_list some_dict 大多数修改序列/映射对象的方法, 比如 list.append, dict.update, list.sort 等等. 都是原地修改对象并返回 None. 这样做的理由是, 如果操作可以原地完成, 就可以避免创建对象的副本来提高性能.\n迭代列表时删除元素 list_1 = [1, 2, 3, 4] list_2 = [1, 2, 3, 4] list_3 = [1, 2, 3, 4] list_4 = [1, 2, 3, 4] for idx, item in enumerate(list_1): del item for idx, item in enumerate(list_2): list_2.remove(item) for idx, item in enumerate(list_3[:]): list_3.remove(item) for idx, item in enumerate(list_4): list_4.pop(idx) list_1 ##没有修改list_1 [1, 2, 3, 4] list_2 ##每一次删除元素后 迭代的list_2也发生改变 比如第一次删除了1 list_2为[2,3,4]这时idx=1 所以下一个删除了3 [2, 4] list_3 ##迭代副本不会出现上述情况 [] list_4 [2, 4] 循环变量泄露 for x in range(7): if x == 6: print(x, \u0026#39;: for x inside loop\u0026#39;) print(x, \u0026#39;: x in global\u0026#39;) 6 : for x inside loop 6 : x in global ## 这次我们先初始化x x = -1 for x in range(7): if x == 6: print(x, \u0026#39;: for x inside loop\u0026#39;) print(x, \u0026#39;: x in global\u0026#39;) 6 : for x inside loop 6 : x in global x = 1 print([x for x in range(5)]) print(x, \u0026#39;: x in global\u0026#39;) [0, 1, 2, 3, 4] 1 : x in global 当心默认的可变参数 def some_func(default_arg=[]): default_arg.append(\u0026quot;some_string\u0026quot;) return default_arg some_func() [\u0026#39;some_string\u0026#39;] some_func() [\u0026#39;some_string\u0026#39;, \u0026#39;some_string\u0026#39;] some_func([]) [\u0026#39;some_string\u0026#39;] some_func() [\u0026#39;some_string\u0026#39;, \u0026#39;some_string\u0026#39;, \u0026#39;some_string\u0026#39;] Python中函数的默认可变参数并不是每次调用该函数时都会被初始化. 相反, 它们会使用最近分配的值作为默认值. 当我们明确的将 [] 作为参数传递给 some_func 的时候, 就不会使用 default_arg 的默认值, 所以函数会返回我们所期望的结果.\nsome_func.__defaults__ ([\u0026#39;some_string\u0026#39;, \u0026#39;some_string\u0026#39;, \u0026#39;some_string\u0026#39;],) 避免可变参数导致的错误的常见做法是将 None 指定为参数的默认值, 然后检查是否有值传给对应的参数. 例:\ndef some_func(default_arg=None): if not default_arg: default_arg = [] default_arg.append(\u0026quot;some_string\u0026quot;) return default_arg 同人不同命 a = [1, 2, 3, 4] b = a a = a + [5, 6, 7, 8] a [1, 2, 3, 4, 5, 6, 7, 8] b [1, 2, 3, 4] a = [1, 2, 3, 4] b = a a += [5, 6, 7, 8] a [1, 2, 3, 4, 5, 6, 7, 8] b [1, 2, 3, 4, 5, 6, 7, 8] a += b 并不总是与 a = a + b 表现相同. 类实现 op= 运算符的方式 也许 是不同的, 列表就是这样做的.\n表达式 a = a + [5,6,7,8] 会生成一个新列表, 并让 a 引用这个新列表, 同时保持 b 不变.\n表达式 a += [5,6,7,8] 实际上是使用的是 “extend” 函数, 所以 a 和 b 仍然指向已被修改的同一列表.\na_var = \u0026#39;global variable\u0026#39; def a_func(): print(a_var, \u0026#39;[ a_var inside a_func() ]\u0026#39;) a_func() print(a_var, \u0026#39;[ a_var outside a_func() ]\u0026#39;) global variable [ a_var inside a_func() ] global variable [ a_var outside a_func() ] a_var = \u0026#39;global value\u0026#39; def a_func(): a_var = \u0026#39;local value\u0026#39; print(a_var, \u0026#39;[ a_var inside a_func() ]\u0026#39;) a_func() print(a_var, \u0026#39;[ a_var outside a_func() ]\u0026#39;) local value [ a_var inside a_func() ] global value [ a_var outside a_func() ] a_var = \u0026#39;global value\u0026#39; def a_func(): global a_var a_var = \u0026#39;local value\u0026#39; print(a_var, \u0026#39;[ a_var inside a_func() ]\u0026#39;) print(a_var, \u0026#39;[ a_var outside a_func() ]\u0026#39;) a_func() print(a_var, \u0026#39;[ a_var outside a_func() ]\u0026#39;) global value [ a_var outside a_func() ] local value [ a_var inside a_func() ] local value [ a_var outside a_func() ] a_var = \u0026#39;global value\u0026#39; def outer(): a_var = \u0026#39;enclosed value\u0026#39; def inner(): a_var = \u0026#39;local value\u0026#39; print(a_var) inner() outer() local value a_var = \u0026#39;global variable\u0026#39; def len(in_var): print(\u0026#39;called my len() function\u0026#39;) l = 0 for i in in_var: l += 1 return l def a_func(in_var): len_in_var = len(in_var) print(\u0026#39;Input variable is of length\u0026#39;, len_in_var) a_func(\u0026#39;Hello, World!\u0026#39;) called my len() function Input variable is of length 13 a = \u0026#39;global\u0026#39; def outer(): def len(in_var): print(\u0026#39;called my len() function: \u0026#39;, end=\u0026quot;\u0026quot;) l = 0 for i in in_var: l += 1 return l a = \u0026#39;local\u0026#39; def inner(): global len nonlocal a a += \u0026#39; variable\u0026#39; inner() print(\u0026#39;a is\u0026#39;, a) print(len(a)) outer() print(len(a)) print(\u0026#39;a is\u0026#39;, a) a is local variable called my len() function: 14 called my len() function 6 a is global 大海捞针 x, y = (0, 1) if True else None, None x,y ((0, 1), None) ##正确做法 x,y = (0,1) if True else (None,None) x,y (0, 1) t = (\u0026#39;one\u0026#39;, \u0026#39;two\u0026#39;) for i in t: print(i) t = (\u0026#39;one\u0026#39;) for i in t: print(i) t = () print(t) one two o n e () ##明显上面的把t = (\u0026#39;one\u0026#39;) t当成字符串了，正确做法如下 t = (\u0026#39;one\u0026#39;,) ##注意逗号 for i in t: print(i) one ","date":"2021-10-18T00:00:00Z","permalink":"/wtf/","title":"WTF"},{"content":"共现矩阵 主要用于发现主题，解决词向量相近关系的表示 例如，语料库如下： - I like deep learning. - I like NLP. - I enjoy flying.\n则共现矩阵如下(窗口大小为1) 例如：“I like”出现在第1，2句话中，一共出现2次，所以=2。 对称的窗口指的是，“like I”也是2次\n将共现矩阵行(列)作为词向量表示后，可以知道like，enjoy都是在I附近且统计数目大约相等，他们意思相近。\n代码 import numpy as np word2ind = {w:i for i,w in enumerate(words)} # word到key，words就是词汇表 M = np.zeros((num_words, num_words)) # num_words是词汇表的长度 for c in corpus: # 假设语料库是一个列表，元素为一段文本。遍历语料库 for idx, word in enumerate(c): # 遍历文本的每一个词，这里默认空格分词 for i in range(1, window_size+1): # 对窗口大 小进行遍历 left = idx - i # 自己与自己不算共现，所以这里要加减 right = idx + i if left \u0026gt;= 0: # 左边元素 M[word2ind[word], word2ind[c[left]]] += 1 if right \u0026lt; len(c): # 右边元素 M[word2ind[word], word2ind[c[right]]] += 1 ","date":"2021-10-15T00:00:00Z","permalink":"/%E5%85%B1%E7%8E%B0%E7%9F%A9%E9%98%B5/","title":"共现矩阵"},{"content":"两个数的交换 # a = 1 # b = 2 # temp = b # b = a # a = temp # print(a,b) a = 1 b = 2 a,b = b,a print(a,b) 2 1 格式化字符串 a = 17 name = \u0026quot;wlb\u0026quot; # print(\u0026#39;%s is %d years old\u0026#39; % (name,a)) # print(\u0026#39;{} is {} years old\u0026#39;.format(name,a)) print(f\u0026#39;{name} is {a} years old\u0026#39;) #明显这个方法更简单 wlb is 17 years old yield与yield from def fib(n): a = 0 b = 1 for _ in range(n): yield a a,b = b,a+b for i in fib(10): print(i) #注释的内容与yield a效果相同，yield相当于使其成为一个迭代器 yield一个数后会立马传递出去，而return 要等列表都生成完毕后才会传出去 #他的优势在于一些耗时的操作 # 通过yield来进行dfs，由于没有实现__next__因此是个可迭代对象而不是一个迭代器 class Node: def __init__(self,value) -\u0026gt; None: self._value = value self._node = [] def __repr__(self) -\u0026gt; str: return f\u0026#39;Node({self._value})\u0026#39; def add_children(self,node:\u0026#39;Node\u0026#39;) -\u0026gt; \u0026#39;Node\u0026#39;: self._node.append(node) def __iter__(self): return iter(self._node) def dfs(self): yield self for i in self: yield from i.dfs() root = Node(0) children1 = Node(1) children2 = Node(2) root.add_children(children1) root.add_children(children2) children1.add_children(Node(3)) children1.add_children(Node(4)) children11 = Node(5) children2.add_children(children11) children11.add_children(Node(6)) for c in root.dfs(): print(c) from typing import Iterable def test_format(datas: Iterable[str], max_len: int): for data in datas: if len(data) \u0026gt; max_len: yield data[:max_len] + \u0026#39;...\u0026#39; else: yield data print(list(test_format([\u0026#39;vllbc\u0026#39;, \u0026#39;test_for_this_function\u0026#39;, \u0026#39;good\u0026#39;],5))) # 把长度大于5的部分变成省略号 #子生成器 def average_gen(): total = 0 count = 0 average = 0 while True: new_num = yield average if new_num is None: break count += 1 total += new_num average = total/count return total,count,average # 委托生成器 def proxy_gen(): while True: total,count,average = yield from average_gen() # yield from后面是一个可迭代对象,此文后面的将多维数组转化为一维数组中flatten函数就用到了yield from，原理就是如果列表中一个元素是列表就yield from这个列表，否则就直接yield这个元素，也利用了递归的方法。如果子生成器退出while循环了，就执行return以获取返回值。 print(total,count,average) def main(): t = proxy_gen() next(t) print(t.send(10)) print(t.send(15)) print(t.send(20)) t.send(None) main() 列表解析式 lists = [f\u0026quot;http://www.baidu.com/page{n}\u0026quot; for n in range(21)] lists#此方法在爬虫构造urls中非常常用 # lists = [f\u0026quot;http://www.baidu.com/page{n}\u0026quot; for n in range(21) if n%2==0] page偶数 # alp = \u0026quot;abcdefghigklmnopqrstuvwxyz\u0026quot; # ALP = [n.upper() for n in alp] 将小写转换为大写 enumerate lists = [\u0026#39;apple\u0026#39;,\u0026#39;banana\u0026#39;,\u0026#39;cat\u0026#39;,\u0026#39;dog\u0026#39;] for index,name in enumerate(lists): print(index,name) # 手动实现一下enumerate from typing import Iterable def enumerate_(Iterable:Iterable,start=0): yield from zip(range(start,start+len(Iterable)),Iterable) for i,item in enumerate_([1,2,3,4,5,6],9): print(i,item) 字典的合并 dic1 = {\u0026#39;qq\u0026#39;:1683070754, \u0026#39;phone\u0026#39;:123456789 } dic2 = { \u0026#39;height\u0026#39;:180, \u0026#39;handsome\u0026#39;:True } dic3 = {**dic1,**dic2} #合并两个字典 **叫做解包 #或者用dic1.update(dic2) 将dic2合并到dic1 相同键则dic2替代dic1 dic3 {\u0026#39;handsome\u0026#39;: True, \u0026#39;height\u0026#39;: 180, \u0026#39;phone\u0026#39;: 123456789, \u0026#39;qq\u0026#39;: 1683070754} 序列解包 name = \u0026quot;wang lingbo\u0026quot; xing,ming = name.split(\u0026quot; \u0026quot;) #split返回一个序列，分别赋给xing 和ming print(xing,ming) #x,*y,z = [1,2,3,4,5] #x:1 z:5 y:[2,3,4] wang lingbo 匿名函数lambda lists = [1,2,3,4,5,6] maps = map(lambda x:x*x,lists) print(maps) print(list(maps)) \u0026lt;map object at 0x000001911C8E03C8\u0026gt; [1, 4, 9, 16, 25, 36] 装饰器 def logging(level): def wapper(func): def inner_wapper(*args, **wbargs): print(f\u0026#39;{level} enter in {func.__name__}()\u0026#39;) return func(*args, **wbargs) #不写return 也可以 return inner_wapper return wapper @logging(\u0026#39;inner\u0026#39;) def say(a): print(\u0026#39;hello! {}\u0026#39;.format(a)) say(\u0026#39;wlb\u0026#39;) inner enter in say() hello! wlb import time def print_time(func): def wapper(*args,**wbargs): print(f\u0026#39;{func.__name__}()调用于{time.asctime(time.localtime(time.time()))}\u0026#39;) return func(*args,**wbargs) #不写return 也可以 return wapper @print_time def my_name(name): print(f\u0026#39;look!{name}\u0026#39;) my_name(\u0026quot;wlb\u0026quot;) my_name()调用于Wed Dec 9 21:21:00 2020 look!wlb map、reduce、filter # map print(list(map(abs,[-1,-2,-3,-4,-5]))) #也可以自己定义函数或者用匿名函数 # reduce from functools import reduce #python3中需要从内置库导入 print(reduce(lambda x,y:x+y,list(map(int,str(131351412))))) # filter a = [1,2,3,4,5,6,7,8,9] new_a = filter(lambda x:x%2!=0,a) #filter就是筛选 list(new_a) # 这三个都是函数式编程中常用的函数 join() # lists = [\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;] # \u0026#39;\u0026#39;.join(lists) lists = [1,2,3,4,5] \u0026#39;\u0026#39;.join(list(map(str,lists))) #join只能是字符串列表，所以要map转换一下 \u0026#39;12345\u0026#39; 将多维数组转换为一维 ab = [[1, 2, 3], [5, 8], [7, 8, 9]] print([i for item in ab for i in item]) #利用列表解析式 print(sum(ab, [])) # 利用sum函数 from functools import reduce print(reduce(lambda x,y:x+y,ab)) # 利用reduce from itertools import chain print(list(chain(*ab))) # 利用chain def flatten(items,ignore=(str,bytes)): for x in items: if isinstance(x,Iterable) and not isinstance(x,ignore): yield from flatten(x) else: yield x print(list(flatten(ab))) # 利用自己定义的函数 [1, 2, 3, 5, 8, 7, 8, 9] 将一个列表倒序 lists = [2,4,3,2,5,4] lists[::-1] # list(reversed(lists)) 随机生成密码 import random b = 8 t = \u0026#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\u0026#39; print(\u0026#39;\u0026#39;.join(random.sample(t,b))) # 主要就是用sample这个方法来取多个随机值 0KmtEZSU 断言 assert(True is True) #成功 print(\u0026#39;yes\u0026#39;) assert(True is False) #报错 print(\u0026#39;no\u0026#39;) yes 合并列表 list1 = [1,2,31,13] list2 = [5,2,12,32] # list1.append(list2) # print(list1) #错误方法 list1.extend(list2) print(list1) #正确方法 [1, 2, 31, 13, 5, 2, 12, 32] a = [1,2,3,4,5] b = [\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;e\u0026#39;] fin = dict() for k,i in zip(a,b): fin[k] = i print(fin) # {1: \u0026#39;a\u0026#39;, 2: \u0026#39;b\u0026#39;, 3: \u0026#39;c\u0026#39;, 4: \u0026#39;d\u0026#39;, 5: \u0026#39;e\u0026#39;} # 或者 d = {} for i,d[i] in zip(a,b): pass print(d) # {1: \u0026#39;a\u0026#39;, 2: \u0026#39;b\u0026#39;, 3: \u0026#39;c\u0026#39;, 4: \u0026#39;d\u0026#39;, 5: \u0026#39;e\u0026#39;} 为什么？在WTFpython中有讲 # 或者 fin = dict(zip(a,b)) print(fin) # {1: \u0026#39;a\u0026#39;, 2: \u0026#39;b\u0026#39;, 3: \u0026#39;c\u0026#39;, 4: \u0026#39;d\u0026#39;, 5: \u0026#39;e\u0026#39;} 对list进行解包 lists = [\u0026#39;dog\u0026#39;,\u0026#39;cat\u0026#39;,\u0026#39;you\u0026#39;] print(*lists) #想对一个列表进行zip操作时，可以这样 print(list(zip(*lists))) def test(*args): print(\u0026quot;args:\u0026quot;,args) test(*lists) dog cat you [(\u0026#39;d\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;y\u0026#39;), (\u0026#39;o\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;o\u0026#39;), (\u0026#39;g\u0026#39;, \u0026#39;t\u0026#39;, \u0026#39;u\u0026#39;)] args:(\u0026#39;dog\u0026#39;,\u0026#39;cat\u0026#39;,\u0026#39;you\u0026#39;) 对类的一些操作 class Test: x = 1 y = 2 print(Test.x,Test.y) #==\u0026gt;print(Test().x,Test().y) class Test: def __init__(self,x,y): self.x = x self.y = y test = Test(1,2) print(test.x,test.y) 1 2 1 2 class Test: def __init__(self,maxlen): self.maxlen = maxlen self.lists = [] def put(self,*args): for i in args: if len(self.lists) \u0026lt;= self.maxlen: self.lists.append(i) else: break def get(self): return self.lists.pop() def empty(self): if len(self.lists) != 0: return False else: return True def __len__(self): return len(self.lists) def __del__(self): print(\u0026quot;del this class\u0026quot;) def printfs(self): return self.lists test = Test(10) test.put(1,2,3,4,5,6) print(test.empty()) print(len(test)) print(test.printfs()) test.__del__() #直接调用test还存在，__del__是析构函数，垃圾回收时就会调用a print(test) #del test #print(test) 这时候就会报错，因为del将test这个对象直接删除了 False 6 [1, 2, 3, 4, 5, 6] del this class \u0026lt;__main__.Test object at 0x0000021B7DF33EB0\u0026gt; del this class 一些内置函数 all([True,True,False]) #False all([True,True,True]) #True any([True,True,False]) #True any([True,False,False])#True any([False,False]) #False import random for i in iter(lambda:random.randint(1,10),5): print(i) #相当于 while True: x = random.randint(1,10) print(x) if x == 5: break iter(object[, sentinel]) sentinel为可选参数，若不传入，则object必须为可迭代对象，传入则必须为可调用对象,当可调用对象的返回值为sentinel抛出异常，但for循环会处理这个异常，这常常用于IO操作 ​\n#这是cookbook里面的一个例子 import sys f = open(\u0026#39;xxx/xxx.txt\u0026#39;) for chunk in iter(lambda:f.read(10),\u0026#39;\u0026#39;): n = sys.stdout.write(chunk) #深入理解一下 import random class Test: def __init__(self): self.lists = [1,23,2,4,1,421,412] def __call__(self): return random.choice(self.lists) for i in iter(Test(),1): print(i) #这是可以正常输出的，因为实例化Test后是个可调用对象，返回列表的随机值，当返回1时则循环结束，如果把__call__魔法方法去了后，则会报错，如果想要不使用魔法方法的话可以用匿名函数 import random class Test: def __init__(self): self.lists = [1,23,2,4,1,421,412] # def __call__(self): # return random.choice(self.lists) for i in iter(lambda:random.choice(Test().lists),1): print(i) #总之，吹爆cookbook functools.partial #先看演示 from functools import partial def add(a,b): return a + b addOne = partial(add,1) addOne(2) #3 addOne(4) #5 #大概意思就是利用partial将函数的一个参数固定住了 def partial(func,*wargs): def wapper(*kargs): args = list(wargs) print(f\u0026quot;args:{args}\u0026quot;) print(f\u0026quot;kargs:{kargs}\u0026quot;) args.extend(kargs) print(f\u0026quot;last:{args}\u0026quot;) return func(*args) return wapper def add(a,b,c): return a + b + c addone = partial(add,1,2) #此时addone相当于wapper print(addone(3)) #调用wrapper 3为传入的kargs #输出： args:[1, 2] kargs:(3,) last:[1, 2, 3] 6 #上面是partial函数的简化版本 #很明显的闭包操作，很容易就可以理解 #当然也可以转换为装饰器操作 from functools import wraps from functools import wraps,partial def out_wapper(*wargs): def partialout(func): return partial(func,*wargs) # 这是使用partial原理的 # @wraps(func) # def wrapper(*kargs): # args = list(wargs) # print(f\u0026quot;args:{args}\u0026quot;) # print(f\u0026quot;kargs:{kargs}\u0026quot;) # args.extend(kargs) # print(f\u0026quot;last:{args}\u0026quot;) # return func(*args) # return wrapper return partialout @out_wapper(1,2) def add(a,b,c): return a + b + c print(add(3)) #6 #明显装饰器要麻烦一点实现，不过毕竟是封装好的函数，以后直接用就可以，不过了解这些有助于提高思维水平 @classmethod和@staticmethod class A(object): bar = 1 def func1(self): print (\u0026#39;foo\u0026#39;) @classmethod def func2(cls): print (\u0026#39;func2\u0026#39;) print (cls.bar) cls().func1() # 调用 foo 方法 A.func2() # 不需要实例化 func2 1 foo class A(object): # 属性默认为类属性（可以给直接被类本身调用） num = \u0026quot;类属性\u0026quot; # 实例化方法（必须实例化类之后才能被调用） def func1(self): # self : 表示实例化类后的地址id print(\u0026quot;func1\u0026quot;) print(self) # 类方法（不需要实例化类就可以被类本身调用） @classmethod def func2(cls): # cls : 表示没用被实例化的类本身 print(\u0026quot;func2\u0026quot;) print(cls) print(cls.num) cls().func1() # 不传递传递默认self参数的方法（该方法也是可以直接被类调用的，但是这样做不标准） def func3(): print(\u0026quot;func3\u0026quot;) print(A.num) # 属性是可以直接用类本身调用的 # A.func1() 这样调用是会报错：因为func1()调用时需要默认传递实例化类后的地址id参数，如果不实例化类是无法调用的 A.func2() A.func3() class A(object): def foo(self, x): print(\u0026quot;executing foo(%s,%s)\u0026quot; % (self, x)) print(\u0026#39;self:\u0026#39;, self) @staticmethod def static_foo(x): print(\u0026quot;executing static_foo(%s)\u0026quot; % x) 问题：@staticmethod修饰的方法函数与普通的类外函数，为什么不直接使用普通函数？ @staticmethod是把函数嵌入到类中的一种方式，函数就属于类，同时表明函数不需要访问这个类。通过子类的继承覆盖，能更好的组织代码。 from pydantic import BaseModel from typing import Sequence class Test(BaseModel): text: Sequence[str] @classmethod def create(cls,text: Sequence[str]) -\u0026gt; \u0026quot;Test\u0026quot;: # classmethod常用构造函数 return cls(text=text) def to_tuple(self) -\u0026gt; \u0026quot;Test\u0026quot;: return Test(text=tuple(self.text)) @classmethod def join(cls, *Tests): return cls.create(sum([i.text for i in Tests],[])) test = Test.create(list(\u0026quot;Hello world\u0026quot;)) t2 = Test.create(list(\u0026quot;NIHAO\u0026quot;)) print(Test.join(test, t2)) # text=[\u0026#39;H\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;w\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;r\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39;N\u0026#39;, \u0026#39;I\u0026#39;, \u0026#39;H\u0026#39;, \u0026#39;A\u0026#39;, \u0026#39;O\u0026#39;] 用类实现装饰器 #先看这样的代码 类实现装饰器要求类必须是可调用的 import time import functools class DelayFunc: def __init__(self, duration, func): self.duration = duration self.func = func def __call__(self, *args, **kwargs): print(f\u0026#39;Wait for {self.duration} seconds...\u0026#39;) time.sleep(self.duration) return self.func(*args, **kwargs) def eager_call(self, *args, **kwargs): print(\u0026#39;Call without delay\u0026#39;) return self.func(*args, **kwargs) def delay(duration): \u0026quot;\u0026quot;\u0026quot;装饰器：推迟某个函数的执行。同时提供 .eager_call 方法立即执行 \u0026quot;\u0026quot;\u0026quot; # 此处为了避免定义额外函数，直接使用 functools.partial 帮助构造 # DelayFunc 实例 return functools.partial(DelayFunc, duration) @delay(2) def add(a,b): print(a,b) add(1,2) #延迟两秒输出3 相当于delay(2)(add)(1,2) add.eager_call(1,2) #不延迟输出3 相当于delay(2)(add).eager_call(1,2) #额，当然，想更深入理解的话，也可以这么写 def delay(duration): def partial(func): return DelayFunc(duration,func) return partial # 上面的就相当于 partial(DelayFunc,duration),缺的func参数就是要修饰的函数 @delay(2) def add(a,b): return a + b print(add(1,2)) 与纯函数相比，我觉得使用类实现的装饰器在特定场景下有几个优势：\n实现有状态的装饰器时，操作类属性比操作闭包内变量更符合直觉、不易出错 实现为函数扩充接口的装饰器时，使用类包装函数，比直接为函数对象追加属性更易于维护 更容易实现一个同时兼容装饰器与上下文管理器协议的对象 BaseModel from pydantic import BaseModel,AnyUrl class Test(BaseModel): # 继承后可以用类属性创建实例 url: AnyUrl data: str def __str__(self): return self.url + self.data kwargs = { \u0026#39;url\u0026#39;: \u0026#39;https://www.baidu.com\u0026#39;, \u0026#39;data\u0026#39;: \u0026#39;/search\u0026#39; } print(Test(**kwargs)) python类型注释 from pydantic import BaseModel from typing import Any class cout(): def __init__(self, cls: \u0026quot;Test\u0026quot;, text: str) -\u0026gt; None: self.cls = cls self.text = text def __str__(self): return f\u0026quot;{self.cls} {self.text}\u0026quot; #程序到cout时 Test类并没有定义，但最后Test在变量空间中，所以加上引号 class Test(BaseModel): def __str__(self) -\u0026gt; str: return \u0026quot;I am Test Class\u0026quot; print(cout(cls=Test(), text=\u0026quot;hello world!\u0026quot;)) namedtuple from collections import namedtuple Test = namedtuple(\u0026quot;Test\u0026quot;, [\u0026#39;name\u0026#39;, \u0026#39;age\u0026#39;, \u0026#39;sex\u0026#39;]) def test_for_test(name: str, year: int, sex: str) -\u0026gt; \u0026quot;Test\u0026quot;: return Test( name=name.title(), age=2021 - year, sex=sex ) name,age,sex = test_for_test(\u0026#39;wlb\u0026#39;, 2002, \u0026#39;male\u0026#39;) print(name, age, sex) @property from pydantic import BaseModel class Test(): def __init__(self, cls, n): self.cls = cls self.n = n @property def to_string_cls(self): return self.cls @property def to_strings(self): return self.n class Test_For(BaseModel): num: int def __str__(self): return str(self.num) __repr__ = __str__ test = Test(Test_For, 22) print(test.to_string_cls(num=1)) # 1 print(test.to_strings) # 22 在边界处思考 from typing import Iterable from pydantic import BaseModel,conint,ValidationError class NumberInput(BaseModel): num: conint(ge=0, le=100) def input_a_number(): while True: n = input(\u0026quot;输入一个数\u0026quot;) try: n = NumberInput(num=n) except ValidationError as e: print(e) continue n = n.num break return n print(input_a_number()) #要求输入一个0-100的数 这样是不是很优雅 super()进阶 今天学习cookbook8-8子类中扩展property 先贴一下代码\nclass Person: def __init__(self, name): self.name = name # 有意思的是 这里的self.name是@property修饰的 这行代码调用name.setter # Getter function @property def name(self): return self._name # Setter function @name.setter def name(self, value): if not isinstance(value, str): raise TypeError(\u0026#39;Expected a string\u0026#39;) self._name = value # Deleter function @name.deleter def name(self): raise AttributeError(\u0026quot;Can\u0026#39;t delete attribute\u0026quot;) # 子类 class SubPerson(Person): @property def name(self): print(\u0026#39;Getting name\u0026#39;) return super().name @name.setter def name(self, value): print(\u0026#39;Setting name to\u0026#39;, value) super(SubPerson, SubPerson).name.__set__(self, value) @name.deleter def name(self): print(\u0026#39;Deleting name\u0026#39;) super(SubPerson, SubPerson).name.__delete__(self) 看到super(SubPerson, SubPerson)感到很疑惑，于是搜索资料大致搞明白了\n通俗说默认的super(SubPerson,self) (直接写super()也可) 返回的是一个类的实例 \u0026gt; 为了委托给之前定义的setter方法，需要将控制权传递给之前定义的name属性的 __set__() 方法。 不过，获取这个方法的唯一途径是使用类变量而不是实例变量来访问它。 这也是为什么我们要使用 super(SubPerson, SubPerson) 的原因。\n从书中这句话可以看出 super(cls,cls)返回的是一个类 不是一个实例，super()的参数的作用就是用于定位位置\n第一个cls必须是第二cls的父类或者二者相同，可以通过cls.__mro__查看继承顺序 比如在D里面super(A,D).__init__(self)\n而__mro__ 为 (\u0026lt;class '__main__.D'\u0026gt;, \u0026lt;class '__main__.A'\u0026gt;, \u0026lt;class '__main__.B'\u0026gt;, \u0026lt;class '__main__.C'\u0026gt;, \u0026lt;class '__main__.Base'\u0026gt;, \u0026lt;class 'object'\u0026gt;) 那么就调用从A以后的类的__init__()\n不过重点不在这里，重点是super(cls,cls)和super(cls,object)的区别 使用super(cls,cls)必须显示的传入self参数，即super(cls,cls).func(self,…)。总之其就是一个定位方法，别的作用我暂且不知。 ### 一个示例\nclass A: def say(self): print(\u0026quot;I am A\u0026quot;) class B(A): def say(self): # super(B, B).say(self) super().say() class C(A): def say(self): print(\u0026quot;I am C\u0026quot;) class D(B, C): def say(self): super().say() D().say() 上述的这段代码怎么修改D输出都是I am C，这是因为在B中super().say()相当于super(B,self).say()，而根据上述内容这是一个实例方法，其中self是D的实例，查看D的mro可以知道C在B的后面，所以根据super的作用，则会调用继承关系中B后面类的say方法，即C的say 方法，所以会得到匪夷所思的结果，将代码修改成注释的那样就可以解决这个问题，希望可以帮助理解。 ## dataclass\nfrom dataclasses import dataclass import random @dataclass(order=True) # 等于实现了各种比较方法例如=、\u0026gt;、\u0026lt;,排序函数都依赖比较两个对象 class A: n: int nums = [A(random.randint(1,10)) for _ in range(10)] nums = sorted(nums) print(nums, end=\u0026#39;\u0026#39;) x = \u0026#39;\u0026#39;\u0026#39;hello\u0026#39;\u0026#39;\u0026#39; print(x) dataclass可以自动添加__rapr__方法，不必自己实现 @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)\ninit：默认将生成 __init__ 方法。如果传入 False，那么该类将不会有 __init__ 方法。 repr：__repr__ 方法默认生成。如果传入 False，那么该类将不会有 __repr__ 方法。 eq：默认将生成 __eq__ 方法。如果传入 False，那么 __eq__ 方法将不会被 dataclass 添加，但默认为 object.__eq__。 order：默认将生成 __gt__、__ge__、__lt__、__le__ 方法。如果传入 False，则省略它们。 unsafe_hash：默认生成__hash__方法，用于构建可hashable的类 from dataclasses import dataclass @dataclass(unsafe_hash=True) class VisitRecordDC: first_name: str last_name: str phone_number: str # 跳过“访问时间”字段，不作为任何对比条件 date_visited: str = field(hash=False, compare=False) def find_potential_customers_v4(): return set(VisitRecordDC(**r) for r in users_visited_phuket) - \\ #求差集 set(VisitRecordDC(**r) for r in users_visited_nz) 自定义format class Student: def __init__(self, name, age): self.name = name self.age = age def __format__(self, format_spec): if format_spec == \u0026#39;long\u0026#39;: return f\u0026#39;{self.name} is {self.age} years old.\u0026#39; elif format_spec == \u0026#39;simple\u0026#39;: return f\u0026#39;{self.name}({self.age})\u0026#39; raise ValueError(\u0026#39;invalid format spec\u0026#39;) vllbc = Student(\u0026#39;vllbc\u0026#39;, \u0026#39;18\u0026#39;) print(f\u0026#39;{vllbc:simple}\u0026#39;) print(f\u0026#39;{vllbc:long}\u0026#39;) 抽象类实践 import collections from abc import ABC, abstractmethod from typing import List customer = collections.namedtuple(\u0026#39;customer\u0026#39;, [\u0026#39;name\u0026#39;, \u0026#39;points\u0026#39;]) class Goods(): def __init__(self, name: str, quantity: float, price: float) -\u0026gt; None: self.name = name self.quantity = quantity self.price = price def total(self) -\u0026gt; float: return self.quantity * self.price class Order(): def __init__(self, customer: customer, cart: List[Goods], prom=None) -\u0026gt; None: self.customer = customer self.cart = cart self.prom = prom def total(self): if not hasattr(self, \u0026#39;__total\u0026#39;): self.__total = sum(i.total() for i in self.cart) return self.__total def due(self): if self.prom is None: discount = 0 else: discount = self.prom.discount(self) return self.total() - discount def __repr__(self) -\u0026gt; str: return f\u0026#39;\u0026lt;Order total: {self.total():.2f} due: {self.due():.2f}\u0026gt;\u0026#39; class Prom(ABC): # 抽象类 @abstractmethod def discount(self,order) -\u0026gt; float: \u0026#39;\u0026#39;\u0026#39;discount\u0026#39;\u0026#39;\u0026#39; class discount1(Prom): def discount(self,order) -\u0026gt; float: return order.total() * 0.05 if order.customer.points \u0026gt;= 10000 else 0 john = customer(name=\u0026#39;vllbc\u0026#39;, points=100000) carts = [Goods(name=\u0026#39;apple\u0026#39;, quantity=5, price=10), Goods( name=\u0026#39;banana\u0026#39;, quantity=8, price=5), Goods(name=\u0026#39;peach\u0026#39;, quantity=4, price=8)] order = Order(customer=john, cart=carts,prom=discount1()) accumulate import itertools test_list = [i for i in range(1, 11)] for i in itertools.accumulate(test_list): print(i, end=\u0026quot;,\u0026quot;) # 1,3,6,10,15,21,28,36,45,55, print() for i in itertools.accumulate(test_list, lambda x, y: x * y): print(i, end=\u0026#39;,\u0026#39;) # 1,2,6,24,120,720,5040,40320,362880,3628800, 异步装饰器 from functools import wraps import asyncio def decorator(func): @wraps(func) async def hello(*args, **kwargs): await asyncio.sleep(2) return await func(*args,**kwargs) return hello @decorator async def test(): print(\u0026quot;hello\u0026quot;) asyncio.run(test()) bisect import bisect import time # BREAKPOINTS 必须是已经排好序的，不然无法进行二分查找 BREAKPOINTS = (1, 60, 3600, 3600 * 24) TMPLS = ( # unit, template (1, \u0026quot;less than 1 second ago\u0026quot;), (1, \u0026quot;{units} seconds ago\u0026quot;), (60, \u0026quot;{units} minutes ago\u0026quot;), (3600, \u0026quot;{units} hours ago\u0026quot;), (3600 * 24, \u0026quot;{units} days ago\u0026quot;), ) def from_now(ts): \u0026quot;\u0026quot;\u0026quot;接收一个过去的时间戳，返回距离当前时间的相对时间文字描述 \u0026quot;\u0026quot;\u0026quot; seconds_delta = int(time.time() - ts) unit, tmpl = TMPLS[bisect.bisect(BREAKPOINTS, seconds_delta)] # bisect类似于index方法，要是不存在会选择数值最接近的索引 return tmpl.format(units=seconds_delta // unit) contextlib from contextlib import contextmanager,ContextDecorator # contextmanager可以把一个函数变成一个上下文管理器，不需要自己去实现一个定义了__enter__和__exit__方法的类 @contextmanager def open_file(filename, methods=\u0026quot;r\u0026quot;): print(f\u0026quot;打开了文件{filename}\u0026quot;) res_file = open(filename, mode=methods) # __enter__方法 这里也可以是自己定义的类 try: yield res_file # 相当于在__enter__方法里面返回self yield后面为空的话就不用as了 except Exception as e: print(\u0026quot;有错误发生\u0026quot;, e) # __exit__方法里的错误处理 finally: res_file.close() # __exit__ with open_file(\u0026quot;testvim.txt\u0026quot;) as fp: print(fp) 读取大文件 from functools import partial def digits_(file,block_size=1024*8): # 分块读取 _read = partial(file.read, block_size) # 使用partial,也可以使用lambda:file.read(block_size) for line in iter(_read, \u0026quot;\u0026quot;): # 当读取完毕时退出 for s in line: if s.isdigit(): yield s # 使用yield def count_digits(fname): \u0026quot;\u0026quot;\u0026quot;计算文件里包含多少个数字字符\u0026quot;\u0026quot;\u0026quot; count = 0 with open(fname) as file: for _ in digits_(file=file): count+=1 return count __exit__、__enter__ # def __enter__(self): # 该方法将在进入上下文时调用 # return self # def __exit__(self, exc_type, exc_val, exc_tb): # 该方法将在退出上下文时调用 # exc_type, exc_val, exc_tb 分别表示该上下文内抛出的异常类型、异常值、错误栈 # __enter__()：主要执行一些环境准备工作，同时返回一资源对象。如果上下文管理器open(\u0026quot;test.txt\u0026quot;)的__enter__()函数返回一个文件对象。 # __exit__()：完整形式为__exit__(type, value, traceback),这三个参数和调用sys.exec_info()函数返回值是一样的，分别为异常类型、异常信息和堆栈。如果*执行体语句*没有引发异常，则这三个参数均被设为None。否则，它们将包含上下文的异常信息。__exit__()方法返回True或False,分别指示被引发的异常有没有被处理，如果返回False，引发的异常将会被传递出上下文。如果__exit__()函数内部引发了异常，则会覆盖掉执行体的中引发的异常。处理异常时，不需要重新抛出异常，只需要返回False，with语句会检测__exit__()返回False来处理异常。 enum from enum import IntEnum class Test(IntEnum): X = 2 Y = 1 print(2 == Test.X) pydantic数据验证 from pydantic import BaseModel, conint, ValidationError # pydantic主要功能就是作数据验证 from typing import ( List, Union, Optional, Dict ) class Test(BaseModel): name: Optional[str] sex: Union[str, List[str]] d: Dict[str, int] id: conint(ge=1,le=10) try: test = Test(name=\u0026#39;wlb\u0026#39;, sex=\u0026#39;male\u0026#39;, d={\u0026#39;dict\u0026#39;:1}, id=1) print(test.dict(), test.__annotations__) # {\u0026#39;name\u0026#39;: \u0026#39;wlb\u0026#39;, \u0026#39;sex\u0026#39;: \u0026#39;male\u0026#39;, \u0026#39;d\u0026#39;: {\u0026#39;dict\u0026#39;: 1}, \u0026#39;id\u0026#39;: 1} {\u0026#39;name\u0026#39;: typing.Union[str, NoneType], \u0026#39;sex\u0026#39;: typing.Union[str, typing.List[str]], \u0026#39;d\u0026#39;: typing.Dict[str, int], \u0026#39;id\u0026#39;: \u0026lt;class \u0026#39;__main__.ConstrainedIntValue\u0026#39;\u0026gt;} except ValidationError: print(\u0026quot;数据错误\u0026quot;) islice from itertools import islice def test(): t = 0 while True: yield t t += 1 for i in islice(test(), 10, 21, 2): print(i) __iter__、__next__ class Range7: # 可迭代类型 只需要实现__iter__即可 def __init__(self,start,end) -\u0026gt; None: self.start = start self.end = end def __iter__(self): return Range7iterator(self) class Range7iterator: #这是迭代器,一般的迭代器只能调用一次 def __init__(self,rangeobj) -\u0026gt; None: self.rangeobj = rangeobj self.cur = rangeobj.start def __iter__(self): return self def __next__(self): while True: if self.cur \u0026gt; self.rangeobj.end: raise StopIteration if self.is_7(self.cur): res = self.cur self.cur += 1 return res self.cur += 1 def is_7(self,num): if num == 0: return False return num%7==0 or \u0026quot;7\u0026quot; in str(num) for i in Range7(1,100): print(i,end=\u0026quot; \u0026quot;) #可迭代对象不一定是迭代器，但迭代器一定是可迭代对象 # 对可迭代对象使用 iter() 会返回迭代器，迭代器则会返回它自身 # 每个迭代器的被迭代过程是一次性的，可迭代对象则不一定 # 可迭代对象只需要实现 __iter__ 方法，而迭代器要额外实现 __next__ 方法 求字典的最大值 prices = { \u0026#39;ACME\u0026#39;: 45.23, \u0026#39;AAPL\u0026#39;: 612.78, \u0026#39;IBM\u0026#39;: 205.55, \u0026#39;HPQ\u0026#39;: 37.20, \u0026#39;FB\u0026#39;: 10.75 } print(max(zip(prices.values(),prices.keys()))) print(max(prices.items(),key=lambda x:x[1])) print(max(prices,key=lambda k:prices[k])) 注意循环变量 funcs = [] res = [] for x in range(7): def func(x=x): # 去掉x=x则出现[6,6,6,6,6,6] 在循环内部定义一个函数时, 如果该函数在其主体中使用了循环变量, 则闭包函数将与循环变量绑定, 而不是它的值. 因此, 所有的函数都是使用最后分配给变量的值来进行计算的. return x funcs.append(func) res.append(func()) func_res = [f() for f in funcs] print(func_res) def create_mult(): res = [] for i in range(5): def func(x, i=i): # 去掉i=i则全输出8，原因和上面一样 return x * i res.append(func) return res for cr in create_mult(): print(cr(2)) 空对象模式 修改前 import decimal class CreateAccountError(Exception): \u0026quot;\u0026quot;\u0026quot;Unable to create a account error\u0026quot;\u0026quot;\u0026quot; class Account: \u0026quot;\u0026quot;\u0026quot;一个虚拟的银行账号\u0026quot;\u0026quot;\u0026quot; def __init__(self, username, balance): self.username = username self.balance = balance @classmethod def from_string(cls, s): \u0026quot;\u0026quot;\u0026quot;从字符串初始化一个账号\u0026quot;\u0026quot;\u0026quot; try: username, balance = s.split() balance = decimal.Decimal(float(balance)) except ValueError: raise CreateAccountError(\u0026#39;input must follow pattern \u0026quot;{ACCOUNT_NAME} {BALANCE}\u0026quot;\u0026#39;) if balance \u0026lt; 0: raise CreateAccountError(\u0026#39;balance can not be negative\u0026#39;) return cls(username=username, balance=balance) def caculate_total_balance(accounts_data): \u0026quot;\u0026quot;\u0026quot;计算所有账号的总余额 \u0026quot;\u0026quot;\u0026quot; result = 0 for account_string in accounts_data: try: user = Account.from_string(account_string) except CreateAccountError: pass else: result += user.balance return result accounts_data = [ \u0026#39;piglei 96.5\u0026#39;, \u0026#39;cotton 21\u0026#39;, \u0026#39;invalid_data\u0026#39;, \u0026#39;roland $invalid_balance\u0026#39;, \u0026#39;alfred -3\u0026#39;, ] print(caculate_total_balance(accounts_data)) 空对象模式简介 额外定义一个对象来表示None\n好处 它可以加强系统的稳固性，能有有效地防止空指针报错对整个系统的影响，使系统更加稳定。 它能够实现对空对象情况的定制化的控制，能够掌握处理空对象的主动权。 它并不依靠Client来保证整个系统的稳定运行。 它通过isNone对==None的替换，显得更加优雅，更加易懂。 import decimal class Account: \u0026quot;\u0026quot;\u0026quot;一个虚拟的银行账号\u0026quot;\u0026quot;\u0026quot; def __init__(self, username, balance): self.username = username self.balance = balance @classmethod def from_string(cls, s): \u0026quot;\u0026quot;\u0026quot;从字符串初始化一个账号\u0026quot;\u0026quot;\u0026quot; try: username, balance = s.split() balance = decimal.Decimal(float(balance)) except ValueError: # raise CreateAccountError(\u0026#39;input must follow pattern \u0026quot;{ACCOUNT_NAME} {BALANCE}\u0026quot;\u0026#39;) return NullAccount() if balance \u0026lt; 0: return NullAccount() return cls(username=username, balance=balance) def caculate_total_balance(accounts_data): \u0026quot;\u0026quot;\u0026quot;计算所有账号的总余额 \u0026quot;\u0026quot;\u0026quot; return sum(Account.from_string(s).balance for s in accounts_data) class NullAccount: # 要返回的空对象 username = \u0026quot;\u0026quot; # 当发生错误时username的值 balance = 0 # 当发生错误时balance的值 def re_Null(): return NotImplementedError accounts_data = [ \u0026#39;piglei 96.5\u0026#39;, \u0026#39;cotton 21\u0026#39;, \u0026#39;invalid_data\u0026#39;, \u0026#39;roland $invalid_balance\u0026#39;, \u0026#39;alfred -3\u0026#39;, ] print(caculate_total_balance(accounts_data)) pathlib from pathlib import Path # 把txt文件重命名为csv文件 def unify_ext_with_pathlib(path): for fpath in Path(path).glob(\u0026quot;*.txt\u0026quot;): fpath.rename(fpath.with_suffix(\u0026quot;.csv\u0026quot;)) print(Path(\u0026quot;.\u0026quot;) / \u0026quot;test_pathlib.py\u0026quot;) # Path类型可以使用/运算符 print(Path(\u0026quot;testvim.txt\u0026quot;).read_text()) # 直接读取文件内容 # .resolve() 取绝对路径 # with_name() 修改文件名 with_suffix()修改后缀名 # 把当前目录下的文件批量重命名 # import os # from pathlib import Path # p = Path(\u0026quot;.\u0026quot;) # for filepath in p.glob(\u0026quot;test_*.py\u0026quot;): # name = filepath.with_name(str(filepath).replace(\u0026quot;test_\u0026quot;,\u0026quot;\u0026quot;)) # filepath.rename(name) 单元测试 def say_hello(name=None): if name: return f\u0026quot;hello {name}\u0026quot; return \u0026quot;hello world\u0026quot; import unittest from typing import List class sayhellotest(unittest.TestCase): def setUp(self,nums:List[int] = 0): return super().setUp() def tearDown(self): return super().tearDown() def test_sayhello(self): rv = say_hello() self.assertEqual(rv,\u0026quot;hello world\u0026quot;) def test_to_name(self): rv = say_hello(\u0026quot;wlb\u0026quot;) self.assertEqual(rv,\u0026quot;hello wlb\u0026quot;) if __name__ == \u0026#39;__main__\u0026#39;: unittest.main() takewhile和dropwhile from itertools import dropwhile,takewhile # 你想遍历一个可迭代对象，但是它开始的某些元素你并不感兴趣，想跳过它们，用dropwhile with open(\u0026#39;testvim.txt\u0026#39;,\u0026#39;r\u0026#39;) as fp: for i in dropwhile(lambda i:i.startswith(\u0026quot;#\u0026quot;),fp): # 跳过前面#号开头的 print(i) with open(\u0026quot;testvim.txt\u0026quot;,\u0026quot;r\u0026quot;) as fp: for i in takewhile(lambda i:i.startswith(\u0026quot;#\u0026quot;),fp): # 遍历带#号开头的，遇到不是#号开头的就退出循环，可以当做break使用 # 相当于 if not i.startwith(\u0026quot;#\u0026quot;): break print(i) 装饰器可以装饰方法 import random import wrapt # 为第三方库 def provide_number(min_num, max_num): @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): # 参数含义： # # - wrapped：被装饰的函数或类方法 # - instance： # - 如果被装饰者为普通类方法，该值为类实例 # - 如果被装饰者为 classmethod 类方法，该值为类 # - 如果被装饰者为类/函数/静态方法，该值为 None # # - args：调用时的位置参数（注意没有 * 符号） # - kwargs：调用时的关键字参数（注意没有 ** 符号） # num = random.randint(min_num, max_num) # 无需关注 wrapped 是类方法或普通函数，直接在头部追加参数 args = (num,) + args return wrapped(*args, **kwargs) return wrapper @provide_number(1, 100) def print_random_number(num): print(num) class Foo: @provide_number(1, 100) def print_random_number(self, num): print(num) Foo().print_random_number() print_random_number() # 使用 wrapt 模块编写的装饰器，相比原来拥有下面这些优势： # 嵌套层级少：使用 @wrapt.decorator 可以将两层嵌套减少为一层 # 更简单：处理位置与关键字参数时，可以忽略类实例等特殊情况 # 更灵活：针对 instance 值进行条件判断后，更容易让装饰器变得通用 ___getattribute__ ____getattribute__仅在新式类中可用，重载__getattrbute__方法对类实例的每个属性访问都有效。\nclass ClassA: x = \u0026#39;a\u0026#39; def __init__(self): self.y = \u0026#39;b\u0026#39; def __getattribute__(self, item): return \u0026#39;__getattribute__\u0026#39; if __name__ == \u0026#39;__main__\u0026#39;: a = ClassA() # 使用实例直接访问存在的类属性时,会调用__getattribute__方法 # 输出结果 __getattribute__ print(a.x) # 使用实例直接访问实例存在的实例属性时,会调用__getattribute__方法 # 输出结果 __getattribute__ print(a.y) # 使用实例直接访问实例不存在的实例属性时,也会调用__getattribute__方法 # 输出结果 __getattribute__ print(a.z) 由于__getattr__只针对未定义属性的调用，所以它可以在自己的代码中自由地获取其他属性，而__getattribute__针对所有的属性运行，因此要十分注意避免在访问其他属性时，再次调用自身的递归循环。\n当在__getattribute__代码块中，再次执行属性的获取操作时，会再次触发__getattribute__方法的调用，代码将会陷入无限递归，直到Python递归深度限制（重载__setter__方法也会有这个问题）。\n示例代码（无限递归）：\nclass ClassA: x = \u0026#39;a\u0026#39; def __getattribute__(self, item): print(\u0026#39;__getattribute__\u0026#39;) return self.item if __name__ == \u0026#39;__main__\u0026#39;: a = ClassA() a.x 运行结果引发异常。\n同时，也没办法通过从__dict__取值的方式来避免无限递归\nclass ClassA: x = \u0026#39;a\u0026#39; def __getattribute__(self, name): return self.__dict__[name] if __name__ == \u0026#39;__main__\u0026#39;: a = ClassA() # 无限递归 a.x 为了避免无限递归，应该把获取属性的方法指向一个更高的超类，例如object（因为__getattribute__只在新式类中可用，而新式类所有的类都显式或隐式地继承自object，所以对于新式类来说，object是所有新式类的超类）。\n修改代码（避免无限递归循环）：\nclass ClassA: x = \u0026#39;a\u0026#39; def __getattribute__(self, item): print(\u0026#39;__getattribute__\u0026#39;) return super().__getattribute__(self, item) if __name__ == \u0026#39;__main__\u0026#39;: a = ClassA() print(a.x) 结果：\n__getattribute__ a ___getattr__、__setattr__ 区别 getattribute 和 getattr，前者是任何通过 x.y 访问实例的属性时都会调用的特殊方法，而后者则是在正常访问形式下无法找到的情况下才会被调用。\nclass Chain(object): def __init__(self, path=\u0026#39;\u0026#39;): self._path = path def __getattr__(self, path): return Chain(\u0026#39;%s/%s\u0026#39; % (self._path, path)) def __str__(self): return self._path def users(self,name): return Chain(f\u0026quot;{self._path}/users/{name}\u0026quot;) __repr__ = __str__ chain = Chain(\u0026quot;vllbc\u0026quot;) print(chain.x.x.x.x.x) # out: vllbc/x/x/x/x/x 另外，当同时定义__getattribute__和__getattr__时，__getattr__方法不会再被调用，除非显示调用__getattr__方法或引发AttributeError异常。可以在__getattribute__中抛出异常来调用getattr。\n__getitem__ 元类 \u0026#39;\u0026#39;\u0026#39; 元类就是控制类的创建的类 \u0026#39;\u0026#39;\u0026#39; class ModelMetaclass(type): def __new__(cls, name, bases, attrs): if name == \u0026#39;Model\u0026#39;: return type.__new__(cls, name, bases, attrs) print(f\u0026quot;found model {name}\u0026quot;) maps = dict() for k, v in attrs.items(): if isinstance(v, Field): print(f\u0026quot;Found mapping {k} ==\u0026gt; {v}\u0026quot;) maps[k] = v for k, v in maps.items(): attrs.pop(k) attrs[\u0026#39;__mappings__\u0026#39;] = maps attrs[\u0026#39;__table__\u0026#39;] = name return type.__new__(cls, name, bases, attrs) class Field(object): def __init__(self, name, column_type): self.name = name self.column_type = column_type def __str__(self): return \u0026#39;\u0026lt;%s:%s\u0026gt;\u0026#39; % (self.__class__.__name__, self.name) class StringField(Field): def __init__(self, name, column_type=\u0026#39;TXT\u0026#39;): super().__init__(name, column_type) class IntegerField(Field): def __init__(self, name, column_type=\u0026#39;INT\u0026#39;): super().__init__(name, column_type) class Model(dict, metaclass=ModelMetaclass): def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r\u0026quot;\u0026#39;Model\u0026#39; object has no attribute \u0026#39;%s\u0026#39;\u0026quot; % key) def __setattr__(self, key, value): self[key] = value def save(self): fields = [] params = [] args = [] for k, v in self.__mappings__.items(): fields.append(k) params.append(\u0026#39;?\u0026#39;) args.append(getattr(self, k, None)) sql = \u0026#39;insert into %s (%s) values (%s)\u0026#39; % ( self.__table__, \u0026#39;,\u0026#39;.join(fields), \u0026#39;,\u0026#39;.join(params)) print(\u0026#39;SQL: %s\u0026#39; % sql) print(\u0026#39;ARGS: %s\u0026#39; % str(args)) class User(Model): # 定义类的属性到列的映射： id = IntegerField(\u0026#39;id\u0026#39;) name = StringField(\u0026#39;username\u0026#39;) email = StringField(\u0026#39;email\u0026#39;) password = StringField(\u0026#39;password\u0026#39;) # 创建一个实例： u = User(id=12345, name=\u0026#39;Michael\u0026#39;, email=\u0026#39;test@orm.org\u0026#39;, password=\u0026#39;my-pwd\u0026#39;) # 保存到数据库： u.save() 关于hash # set中的元素要求必须是可哈希的，但set本身是不可哈希的 a = set([1,2,3,4]) a.add([1,2,3]) # 会报错，因为列表是不可哈希的 hash(a) # 会报错，因为set本身是不可哈希的 b = tuple([1,2,3,4]) # 元组本身是可哈希的 c = tuple([1,2,3,[4,5]]) # 如果元组里面有不可哈希的元素 那么整个元组也是不可哈希的了 # 在类中定义__hash__方法就可以变成可哈希的类，注意避免返回可能重复的hash值 class My_Hash: def __hash__(self) -\u0026gt; int: return 111 # 还有简便的方法就是使用dataclass类，可以省时省力，本博客也有介绍。 处理列表越界 假如你请求的不是某一个元素，而是一段范围的切片。那么无论你指定的范围是否有效，程序都只会返回一个空列表 []，而不会抛出任何错误。 了解了这点后，你会发现像下面这种边界处理代码根本没有必要：\ndef sum_list(l, limit): \u0026quot;\u0026quot;\u0026quot;对列表的前 limit 个元素求和 \u0026quot;\u0026quot;\u0026quot; # 如果 limit 过大，设置为数组长度避免越界 if limit \u0026gt; len(l): limit = len(l) return sum(l[:limit]) 因为做切片不会抛出任何错误，所以不需要判断 limit 是否超出范围，直接做 sum 操作即可：\ndef sum_list(l, limit): return sum(l[:limit]) or操作符 在很多场景下，我们可以利用 or 的特点来简化一些边界处理逻辑。看看下面这个例子：\ncontext = {} # 仅当 extra_context 不为 None 时，将其追加进 context 中 if extra_context: context.update(extra_context) # 等同于 context.update(extra_context or {}) 因为 a or b or c or … 这样的表达式，会返回这些变量里第一个布尔值为真的值，直到最后一个为止。 含义为当extra_context为None时，会返回{} ## 字典的键 在python里面，Python 字典通过检查键值是否相等和比较哈希值来确定两个键是否相同.具有相同值的不可变对象(可哈希)在Python中始终具有相同的哈希值。 注意: 具有不同值的对象也可能具有相同的哈希值（哈希冲突）。 下面这个例子\nsome_dict = {} some_dict[5.5] = \u0026quot;Ruby\u0026quot; some_dict[5.0] = \u0026quot;JavaScript\u0026quot; some_dict[5] = \u0026quot;Python\u0026quot; \u0026gt;\u0026gt;\u0026gt; some_dict[5.5] \u0026quot;Ruby\u0026quot; \u0026gt;\u0026gt;\u0026gt; some_dict[5.0] \u0026quot;Python\u0026quot; \u0026gt;\u0026gt;\u0026gt; some_dict[5] \u0026quot;Python\u0026quot; 因为Python将 5 和 5.0 识别为 some_dict 的同一个键, 所以已有值 “JavaScript” 就被 “Python” 覆盖了.\n类__init__参数快速赋给self def __init__(self, x=1) -\u0026gt; None: super().__init__() self.__dict__.update(locals()) pass 即可，此时不需要self.x=x也可以直接使用self.x\n","date":"2021-10-10T00:00:00Z","permalink":"/skill/","title":"skill"},{"content":"长度最小的子数组 题目： https://leetcode-cn.com/problems/minimum-size-subarray-sum/\n思路： ​ 一开始想的是直接排序，然后从后面开始遍历，因为要求最小的\n然后出错了，，，，，\nclass Solution: def minSubArrayLen(self, s: int, nums: List[int]) -\u0026gt; int: nums.sort() end = len(nums) - 1 while end \u0026gt; 0: for i in range(end,-1,-1): if sum(nums[i:end+1]) \u0026gt;= s: return len(nums[i:end+1]) end -= 1 return 0 在\n213\n[12,28,83,4,25,26,25,2,25,25,25,12]\n出了错，结果试了一下排序后的列表\n[2, 4, 12, 12, 25, 25, 25, 25, 25, 26, 28, 83]\n结果居然是对的，说明是我的代码的问题，不应该排序\n那么该怎么办呢\nclass Solution: def minSubArrayLen(self, s: int, nums: List[int]) -\u0026gt; int: if not nums: return 0 left = 0 right = 0 res = float(\u0026#39;inf\u0026#39;) while right \u0026lt; len(nums): while sum(nums[left:right+1]) \u0026gt;= s: res = min(res, right-left +1) left += 1 else: right += 1 if res == float(\u0026#39;inf\u0026#39;): return 0 return res 思路也差不多，也是用到了双指针。不过必须注意要判断最小的这个条件啊。\n","date":"2021-09-30T00:00:00Z","permalink":"/%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/","title":"长度最小的子数组"},{"content":"SVM kernel 介绍 其实核函数和映射关系并不大，kernel可以看作是一个运算技巧。\n一般认为，原本在低维线性不可分的数据集在足够高的维度存在线性可分的超平面。\n围绕这个，那么我们所做的就是要在Feature Space套用原本在线性可分情况下的Input Space中使用过的优化方法，来找到那个Maximaizing Margin的超平面。原理机制一模一样，是二次规划，唯一不同是代入数据的不同，将原来的\\(x_i\\)替换成了高维空间中的\\(\\phi(x_i)\\)，这就是映射函数，映射到高维空间。\n具体的技巧(trick)，就是简化计算二次规划中间的一步内积计算。也即中间步骤有一步必须求得\\(\\phi(x_i) \\phi(x_j)\\)，我们可以定义核函数\\(K(x_i,x_j) = \\phi(x_i)\\phi(x_j)\\)，这样我们不需要显式计算每一个\\(\\phi(x_i)\\)，甚至不需要知道它的形式，就可以直接计算结果出来。\n也就是说，核函数、内积、相似度这三个词是等价的。因为inner product其实就是一种similarity的度量。核函数和映射是无关的。\n例子 举一个例子：\n考虑一个带有特征映射的二维输入空间 \\(\\chi \\subseteq \\mathbb{R}^{2}\\) : 特征映射二维到三维: \\(\\quad \\Phi: x=\\left(x_{1}, x_{2}\\right) \\rightarrow \\Phi(x)=\\left(x_{1}^{2}, x_{2}^{2}, \\sqrt{2} x_{1} x_{2}\\right) \\in F=\\mathbb{R}^{3}\\) 特征空间中的内积：\n\\[ \\begin{aligned} \\langle\\Phi(x), \\Phi(z)\\rangle \u0026amp;=\\left\\langle\\left(x_{1}^{2}, x_{2}^{2}, \\sqrt{2} x_{1} x_{2}\\right),\\left(z_{1}^{2}, z_{2}^{2}, \\sqrt{2} z_{1} z_{2}\\right)\\right\\rangle \\\\\\\\ \u0026amp;=x_{1}^{2} z_{1}^{2}+x_{2}^{2} z_{2}^{2}+2 x_{1} x_{2} z_{1} z_{2} \\\\\\\\ \u0026amp;=\\left\\langle x_{1} z_{1}+x_{2} z_{2}\\right\\rangle^{2} \\\\\\\\ \u0026amp;=\\langle x, z\\rangle^{2} \\end{aligned} \\]\n根据上面可得，核函数\\(k(x,z) = \\langle x,z \\rangle^2=\\phi(x)^T \\phi(z)\\)\n而这里为什么映射函数是这样的形式呢，其实可以是反推出来的，我也不知道，反正凑巧通过这种映射函数可以得到这个核函数。\n常用核函数理解 以高斯核函数为例， \\[ \\kappa\\left(x_{1}, x_{2}\\right)=\\exp \\left(-\\frac{\\left|x_{1}-x_{2}\\right|^{2}}{2 \\sigma^{2}}\\right) \\] 我们假设 \\(\\sigma=1\\) ，则\n\\[ \\begin{aligned} \\kappa\\left(x_{1}, x_{2}\\right) \u0026amp;=\\exp \\left(-\\frac{\\left|x_{1}-x_{2}\\right|^{2}}{2 \\sigma^{2}}\\right) \\\\\\\\ \u0026amp;=\\exp \\left(-\\left(x_{1}-x_{2}\\right)^{2}\\right) \\\\\\\\ \u0026amp;=\\exp \\left(-x_{1}^{2}\\right) \\exp \\left(-x_{2}^{2}\\right) \\exp \\left(2 x_{1} x_{2}\\right) \\\\\\\\ \u0026amp; \\text { Taylor } \\\\\\\\ \u0026amp;=\\exp \\left(-x_{1}^{2}\\right) \\exp \\left(-x_{2}^{2}\\right)\\left(\\sum_{i=0}^{\\infty} \\frac{\\left(2 x_{1} x_{2}\\right)^{i}}{i !}\\right) \\\\\\\\ \u0026amp;=\\sum_{i=0}^{\\infty}\\left(\\exp \\left(-x_{1}^{2}\\right) \\exp \\left(-x_{2}^{2}\\right) \\sqrt{\\left.\\frac{2^{i}}{i !} \\sqrt{\\frac{2^{i}}{i !}} x_{1}^{i} x_{2}^{i}\\right)}\\right.\\\\\\\\ \u0026amp;=\\sum_{i=0}^{\\infty}\\left(\\left[\\exp \\left(-x_{1}^{2}\\right) \\sqrt{\\frac{2^{i}}{i !}} x_{1}^{i}\\right]\\left[\\exp \\left(-x_{2}^{2}\\right) \\sqrt{\\frac{2^{i}}{i !}} x_{2}^{i}\\right]\\right) \\\\\\\\ \u0026amp;=\\phi\\left(x_{1}\\right)^{T} \\phi\\left(x_{2}\\right) \\end{aligned} \\]\nw这不，已经有了定义的那种形式，对于 \\(\\phi(x)\\) ，由于\n\\[ \\phi(x)=\\exp \\left(-x^{2}\\right) \\cdot\\left(1, \\sqrt{\\frac{2^{1}}{1 !}} x, \\sqrt{\\frac{2^{2}}{2 !}} x^{2}, \\cdots\\right) \\]\n所以，可以映射到任何一个维度上。\n核函数类别 其实常用的就那几个，高斯核函数最为常用。\n参考 https://www.cnblogs.com/damin1909/p/12955240.html https://blog.csdn.net/mengjizhiyou/article/details/103437423\n线性可分支持向量机 线性可分 在二维空间上，两类点被一条直线完全分开叫做线性可分。\n最大间隔超平面 以最大间隔把两类样本分开的超平面，也称之为最大间隔超平面。\n支持向量 样本中距离超平面最近的一些点，这些点叫做支持向量。\n最优化问题 SVM 想要的就是找到各类样本点到超平面的距离最远，也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述：\n\\[ w^Tx+b=0 \\]\n二维空间点(x,y)到直线\\(Ax+By+C=0\\)的距离公式为：\n\\[ \\frac{|Ax+By+C|}{\\sqrt{A^2+B^2}} \\]\n扩展到n维空间中，\\(x=(x_1, x_2,\\dots, x_n)\\)到直线\\(w^Tx+b=0\\)的距离为：\n\\[ \\frac{|w^Tx+b|}{||w||} \\] 如图所示，根据支持向量的定义我们知道，支持向量到超平面的距离为 d，其他点到超平面的距离大于 d。\n于是我们有这样的一个公式：\n之后得到:\n分母都是正数，因此可以令它为1。 合并得： 至此我们就可以得到最大间隔超平面的上下两个超平面：\n每个支持向量到超平面的距离可以写为： 所以我们得到： 最大化这个距离：\n这里乘上 2 倍也是为了后面推导，对目标函数没有影响。刚刚我们得到支持向量\\(y(w^Tx+b) = 1\\)，所以我们得到：\n\\[ \\max\\frac{2}{||w||} \\]\n对目标进行转换：\n\\[ \\min\\frac{1}{2}||w||^2 \\]\n所以得到的最优化问题是：\n对偶问题 拉格朗日乘数法、拉格朗日对偶和KKT条件 参考：https://zhuanlan.zhihu.com/p/38163970 给定约束优化问题：\n\\[ \\begin{aligned} \u0026amp;\\min f(x) \\\\\\\\ \u0026amp; s.t. g(x) = 0 \\end{aligned} \\]\n为方便分析，假设 f 与 g 是连续可导函数。Lagrange乘数法是等式约束优化问题的典型解法。定义Lagrangian函数\n\\[ L(x, \\lambda) = f(x) + \\lambda g(x) \\] 其中 λ 称为Lagrange乘数。Lagrange乘数法将原本的约束优化问题转换成等价的无约束优化问题 计算 L 对 x 与 λ 的偏导数并设为零，可得最优解的必要条件： 接下来是不等式约束：\n\\[ \\begin{aligned} \u0026amp; \\min f(x) \\\\ \u0026amp; s.t. g(x) \\leq 0 \\end{aligned} \\]\n据此我们定义可行域(feasible region)\\(K=x\\in R^n | g(x)\\leq 0\\)。设$x^* $为满足条件的最佳解，分情况讨论：\n\\(g(x^* ) \u0026lt; 0\\)，最佳解位于K的内部，为内部解，这时的约束是无效的。 \\(g(x^* ) = 0\\)，最佳解落在K的边界，称为边界解，此时的约束是有效的。 这两种情况的最佳解具有不同的必要条件。 具有不同的必要条件：\n内部解：在约束条件无效的情况下，\\(g(x)\\)不起作用，约束优化问题退化为无约束优化问题，因此$x^* \\(满足\\)= 0$ 边界解：在约束有效的情况下，约束不等式变为等式\\(g(x)=0\\)。此时拉格朗日函数在$x^* \\(的梯度为0，即\\)f = -g\\(，\\)f(x)\\(的极小值在边界取到，那么可行域内部的\\)f(x)\\(应该都是大于这个极小值，则\\)f(x)\\(的方向是可行域内部。而\\)g\\(的方向为可行域外部，因为约束条件是\\)g(x) \\(，也就是可行域外部都是\\)g(x) \u0026gt; 0\\(，所以梯度方向就是指向函数增加的方向。说明两个函数的梯度方向相反，要想上面的等式成立，必须有\\)\\(，这就是对偶可行性。 因此，不论是内部解或边界解， \\)g(x)=0$ 恒成立 整合上述两种情况，最佳解的必要条件包括Lagrangian函数的定常方程式、原始可行性、对偶可行性，以及互补松弛性： 这就是KKT条件。\n上面结果可推广至多个约束等式与约束不等式的情况。考虑标准约束优化问题(或称非线性规划)： 定义Lagrangian 函数\n则KKT条件为 应用 已知svm优化的主要问题：\n那么求解线性可分的 SVM 的步骤为：\n步骤1：\n构造拉格朗日函数：\n步骤2：\n利用强对偶性转化：\n现对参数 w 和 b 求偏导数：\n具体步骤： 在前面的步骤中即为：\n我们将这个结果带回到函数中可得：\n也就是说：\n步骤3： 由上述过程需要满足KKT条件（\\(\\alpha\\)就是本文中的\\(\\lambda\\)）： 易得，当\\(\\lambda_i\\)大于0，则必有\\(y_if(x_i)=1\\),所对应的样本点是一个支持向量，即位于最大间隔边界上。\n我们可以看出来这是一个二次规划问题，问题规模正比于训练样本数，我们常用 SMO(Sequential Minimal Optimization) 算法求解。\nSMO(Sequential Minimal Optimization)，序列最小优化算法，其核心思想非常简单：每次只优化一个参数，其他参数先固定住，仅求当前这个优化参数的极值。我们来看一下 SMO 算法在 SVM 中的应用。\n我们刚说了 SMO 算法每次只优化一个参数，但我们的优化目标有约束条件，没法一次只变动一个参数。所以我们选择了一次选择两个参数。具体步骤为：\n选择两个需要更新的参数\\(\\lambda _i\\)和\\(\\lambda_j\\)，固定其他参数。于是我们有以下约束： 其中\\(c = -\\sum_{k\\neq i, j}\\lambda_ky_k\\)， 因此可以得出\\(\\lambda_j = \\frac{c-\\lambda_iy_i}{y_j}\\)，这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题，仅有的约束是\\(\\lambda_i\u0026gt;0\\)\n对于仅有一个约束条件的最优化问题，我们完全可以在\\(\\lambda_i\\)上对优化目标求偏导，令导数为零，从而求出变量值\\(\\lambda_{inew}\\)，从而求出\\(\\lambda_{jnew}\\) 多次迭代直至收敛。 通过 SMO 求得最优解$^* $ 步骤4：\n我们求偏导数时得到：\n由上式可求得 w。\n由于所有\\(\\lambda_i\u0026gt;0\\)的点都是支持向量，可以随便找一个支持向量代入\\(y_s(w^Tx_s+b)=1\\)，求出b即可。\n两边同时乘以\\(y_s\\)，最后得\\(b = y_s-wx_s\\)\n为了更具鲁棒性，我们可以求得支持向量的均值：\n步骤5： w 和 b 都求出来了，我们就能构造出最大分割超平面：\\(w^Tx+b=0\\)\n分类决策函数：\\(f(x) = sign(w^Tx+b)\\)\n将新样本点导入到决策函数中既可得到样本的分类。\n线性支持向量机与软间隔 软间隔 在实际应用中，完全线性可分的样本是很少的，如果遇到了不能够完全线性可分的样本，我们应该怎么办？比如下面这个：\n于是我们就有了软间隔，相比于硬间隔的苛刻条件，我们允许个别样本点出现在间隔带里面，即允许出现分类错误的样本：\n我们允许部分样本点不满足约束条件：\n\\[ y_i(w^Tx_i+b) \\geq 1 \\]\n则优化目标变成了\n\\[ \\min_{w, b} \\frac{1}{2}\\|{w}\\|^{2}+C \\sum_{i=1}^{m} \\ell_{0 / 1}\\left(y_{i}\\left({w}^{\\mathrm{T}} {x}_{i}+b\\right)-1\\right), \\]\n其中 \\(C\u0026gt;0\\) 是一个常数, \\(\\ell_{0 / 1}\\) 是 “ \\(0 / 1\\) 损失函数”\n\\[ \\ell_{0 / 1}(z)= \\begin{cases}1, \u0026amp; \\text { if } z\u0026lt;0 \\\\\\\\ 0, \u0026amp; \\text { otherwise. }\\end{cases} \\]\n显然, 当 \\(C\\) 为无穷大时, \\(\\xi_i\\)必然无穷小，如此一来线性svm就又变成了线性可分svm，当\\(C\\)为有限值时，才会允许部分样本不遵循约束条件\n然而, \\(\\ell_{0 / 1}\\) 非凸、非连续, 数学性质不太好, 使得不易直接求解. 于 是, 人们通常用其他一些函数来代替 \\(\\ell_{0 / 1}\\), 称为 “替代损失” (surrogate loss). 替代损失函数一般具有较好的数学性质, 如它们通常是凸的连续函数且是 \\(\\ell_{0 / 1}\\) 的上界. 给出了三种常用的替代损失函数:\nhinge 损失: \\(\\ell_{\\text {hinge }}(z)=\\max(0,1-z)\\);\n指数损失(exponential loss): \\(\\ell_{\\exp }(z)=\\exp (-z)\\);\n对率损失(logistic loss): \\(\\ell_{\\log }(z)=\\log (1+\\exp (-z))\\).\n若采用 hinge 损失, 则变成\n\\[ \\min_{w, b} \\frac{1}{2}\\|{w}\\|^{2}+C \\sum_{i=1}^{m} \\max\\left(0,1-y_{i}\\left({w}^{\\mathrm{T}} {x}_{i}+b\\right)\\right) \\]\n为了度量这个间隔软到何种程度，我们为每个样本引入一个松弛变量\\(\\xi_i\\)，令\\(\\xi_i \\geq 0\\)，且\\(1-y_i(w^Tx_i+b)-\\xi_i\\leq 0\\)，如下图： 优化目标与求解 优化目标：\n步骤1：\n构造拉格朗日函数： 步骤2： 分别求导，得出以下关系：\n将这些关系带入拉格朗日函数中，得到：\n则：\n我们可以看到这个和硬间隔的一样，只是多了个约束条件。\n然后使用SMO算法求$^* $\n软间隔KKT条件 其中\\(\\alpha\\)对应本文的\\(\\lambda\\)，\\(\\mu\\)对应本文的\\(\\mu\\)\n因此由第三个式子得必有\\(\\lambda_i =0\\)或者\\(y_if(x_i) - 1+\\xi_i \\geq 0\\) \\(\\lambda_i=0\\)，则该样本对其没有任何影响。 \\(\\lambda_i \u0026gt; 0\\)，则样本为支持向量。 若\\(\\lambda_i \u0026lt;C\\),则\\(\\mu_i \u0026gt; 0\\)，进而有\\(\\xi_i=0\\)，则样本恰在最大间隔边界上。也是支持向量。 若\\(\\lambda_i=C\\),则有\\(\\mu_i=0\\)，此时若\\(\\xi_i\\leq 1\\)，则样本落在最大间隔内部。若\\(\\xi_i\u0026gt;1\\)则样本被错误分类。\n再看一下下面这图就理解了。 步骤3：\n然后我们通过上面两个式子求出 w 和 b，最终求得超平面\n这边要注意一个问题，在间隔内的那部分样本点是不是支持向量？\n我们可以由求参数 w 的那个式子可看出，只要 \\(\\lambda_i \u0026gt; 0\\)的点都能影响我们的超平面，因此都是支持向量。\n非线性支持向量机 我们刚刚讨论的硬间隔和软间隔都是在说样本的完全线性可分或者大部分样本点的线性可分。\n但我们可能会碰到的一种情况是样本点不是线性可分的，比如：\n这种情况的解决方法就是：将二维线性不可分样本映射到高维空间中，让样本点在高维空间线性可分，比如：\n对于在有限维度向量空间中线性不可分的样本，我们将其映射到更高维度的向量空间里，再通过间隔最大化的方式，学习得到支持向量机，就是非线性 SVM。\n我们用 x 表示原来的样本点，用\\(\\phi(x)\\)表示 x 映射到特征新的特征空间后到新向量。那么分割超平面可以表示为: \\(f(x) = w\\phi(x)+b\\)\n对于非线性 SVM 的对偶问题就变成了：\n区别就在于优化目标中的内积。\n核函数 我们不禁有个疑问：只是做个内积运算，为什么要有核函数的呢？\n这是因为低维空间映射到高维空间后维度可能会很大，如果将全部样本的点乘全部计算好，这样的计算量太大了。\n但如果我们有这样的一核函数\\(k(x,y) = (\\phi(x), \\phi(y))\\)，x与y在特征空间中的内积，就等于它们在原始空间中通过函数\\(k(x,y)\\)计算的结果，我们就不需要知道映射函数和计算高维空间中的内积了。\n有关内容看本文一开始对kernel的介绍。\n总结 SVM是深度学习流行之前的首选分类方法，在许多任务上都有很好的效果，稍微修改后可以用于回归任务中。总结一下svm算法的优缺点。\n优点 有严格的数学理论支持，可解释性强，不依靠统计方法，从而简化了通常的分类和回归问题； 能找出对任务至关重要的关键样本（即：支持向量）； 采用核技巧之后，可以处理非线性分类/回归任务； 最终决策函数只由少数的支持向量所确定，计算的复杂性取决于支持向量的数目，而不是样本空间的维数，这在某种意义上避免了“维数灾难”。 缺点 训练时间长。当采用 SMO 算法时，每次都需要挑选一对参数 当采用核技巧时，如果需要存储核矩阵，则空间复杂度为\\(O(N^2)\\) 模型预测时，预测时间与支持向量的个数成正比。当支持向量的数量较大时，预测计算复杂度较高。 ## 参考 https://zhuanlan.zhihu.com/p/77750026\n","date":"2021-09-20T00:00:00Z","permalink":"/svm/","title":"SVM"},{"content":"字符串的排列 题目： https://leetcode-cn.com/problems/permutation-in-string/\n思路： 滑动窗口加字典\n代码： class Solution(object): def checkInclusion(self, s1, s2): counter1 = collections.Counter(s1) N = len(s2) left = 0 right = len(s1) - 1 counter2 = collections.Counter(s2[0:right]) while right \u0026lt; N: counter2[s2[right]] += 1 if counter1 == counter2: return True counter2[s2[left]] -= 1 if counter2[s2[left]] == 0: del counter2[s2[left]] left += 1 right += 1 return False ","date":"2021-09-18T00:00:00Z","permalink":"/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%8E%92%E5%88%97/","title":"字符串的排列"},{"content":"类似于pandas的apply，就是在某一维上进行定义的函数操作\napply_along_axis(func1d, axis, arr, *args, **kwargs) 官网的例子\ndef my_func(a): return (a[0] + a[-1]) * 0.5 b = np.array([[1,2,3], [4,5,6], [7,8,9]]) np.apply_along_axis(my_func, 0, b) # 结果 array([ 4., 5., 6.]) # 结果 array([ 2., 5., 8.]) ","date":"2021-09-03T00:00:00Z","permalink":"/apply_along_axis/","title":"apply_along_axis"},{"content":"采购方案 题目： https://leetcode-cn.com/problems/4xy4Wx/\n思路： 题目很简单，思想就是双指针，感觉是个双指针的典型例子就写了下来\n先对数组进行从小到大排序，然后双指针从两边移动，如果一直大于target就一直左移right 然后right - left就是所有成立的数目，再移动left 进行筛选\n代码： class Solution: def purchasePlans(self, nums: List[int], target: int) -\u0026gt; int: nums.sort() left = 0 right = len(nums) - 1 res = 0 while left \u0026lt; right and left \u0026lt; len(nums): while left \u0026lt; right and nums[right] + nums[left] \u0026gt; target: right -= 1 res += right - left left += 1 return res % (1000000007) ","date":"2021-08-30T00:00:00Z","permalink":"/%E9%87%87%E8%B4%AD%E6%96%B9%E6%A1%88/","title":"采购方案"},{"content":"位运算的一个应用 翻了翻以前用python刷leetcode的记录，最后刷的一道题是这样的\nhttps://leetcode-cn.com/problems/single-number/\n叫只出现一次的数字，当时看题感觉非常简单啊！直接搞就行了\n当时一开始我的做法是这样的\nclass Solution: def singleNumber(self, nums): for i in set(nums): if nums.count(i) ==1: return i 信心满满的提交，结果发现TLE了。。\n然后看超时案例的输入，没有一个数字重复，也就是说我的set跟没有一样，所以说肯定不能这么做。\n然后又想到了哈希表\nclass Solution: def singleNumber(self, nums): dic={} for num in nums: if num in dic.keys(): dic[num]+=1 else: dic[num]=1 for i in dic.keys(): if dic[i] ==1: return i 这样也算是AC了。本以为这个题就这么结束了，结果无意中看到了别的题解震惊了\n代码数量比我短的多得多。\n然后就认识到了位运算的魔力。。\n先上代码：\nclass Solution: def singleNumber(self, nums): a = 0 for i in nums: a^=i return a 简单的一个异或运算就达到了目的\n真是太神奇了！\n找到相关资料\n交换律：a ^ b ^ c \u0026lt;=\u0026gt; a ^ c ^ b 任何数于0异或为任何数 0 ^ n =\u0026gt; n 相同的数异或为0: n ^ n =\u0026gt; 0 也就是说相同的数就异或为0了，达到了去重的目的。\n","date":"2021-08-23T00:00:00Z","permalink":"/%E5%8F%AA%E5%87%BA%E7%8E%B0%E4%B8%80%E6%AC%A1%E7%9A%84%E6%95%B0%E5%AD%97/","title":"只出现一次的数字"},{"content":"概率图模型概述 ","date":"2021-08-13T00:00:00Z","permalink":"/%E6%A6%82%E7%8E%87%E5%9B%BE%E6%A8%A1%E5%9E%8B%E6%A6%82%E8%BF%B0/","title":"概率图模型概述"},{"content":"","date":"2021-08-13T00:00:00Z","permalink":"/%E8%B0%B1%E8%81%9A%E7%B1%BB/","title":"谱聚类"},{"content":"B树 B树就是B-树，以前还以为这是两种树，现在才知道这俩就是一个东西。\n基本概念 所有的叶子结点都出现在同一层上，并且不带信息(可以看做是外部结点或查找失败的结点，实际上这些结点不存在，指向这些结点的指针为空)。 每个结点包含的关键字个数有上界和下界。用一个被称为 B-树的 最小度数 的固定整数 t≥2 来表示这些界 ，其中 t 取决于磁盘块的大小：\na.除根结点以外的每个结点必须至少有 t−1 个关键字。因此，除了根结点以外的每个内部结点有 t 个孩子。如果树非空，根结点至少有一个关键字。 每个结点至多包含 2t−1 个关键字。 一个包含x个关键字的结点有x+1个孩子。 一个结点中所有的关键字升序排列，两个关键字\\(k_1\\)和\\(k_2\\)之间的孩子结点的所有关键字key在\\((k_1, k_2)\\)的范围内。 其中最小度数和B树的阶不一样：\n度：一个结点含有的子结点的个数称为该结点的度\n阶：一棵树的最大孩子数\n最小度minimum degree（t）：用来衡量结点的关键字数量范围\n阶 order（m）：衡量B树中的结点的最大孩子数\n关系如下：\n\\[ t = ceil(\\frac{m}{2}) \\quad m = 2t \\] 可以简单理解为最小度是孩子数的最小值，阶是孩子数的最大值。最小度-1是节点关键字数的最小值，阶-1是节点关键字数的最大值。\n查找 查找很简单，对每个键中的索引进行比较然后查找就可以。\n插入 伪代码：\n初始化 x 作为根结点 当 x 不是叶子结点，执行如下操作： 找到 x 的下一个要被访问的孩子结点 y 如果 y 没有满，则将结点 y 作为新的 x 如果 y 已经满了，拆分 y ，结点 x 的指针指向结点 y 的两部分。 如果 k 比 y 中间的关键字小， 则将 y 的第一部分作为新的 x ，否则将 y 的第二部分作为新的 x ，当将 y 拆分后，将 y 中的一个关键字移动到它的父结点 x 当中。 当 x 是叶子结点时，第二步结束； 由于我们已经提前查分了所有结点，x 必定至少有一个额外的关键字空间，进行简单的插入即可。 删除 待删除的关键字 k 在结点 x 中，且 x 是叶子结点，删除关键字k 待删除的关键字 k 在结点 x 中，且 x 是内部结点，分一下三种情况 1). 如果位于结点 x 中的关键字 k 之前的第一个孩子结点 y 至少有 t 个关键字，则在孩子结点 y 中找到 k 的前驱结点 k0 ，递归地删除关键字 k0 ，并将结点 x 中的关键字 k 替换为 k0 直接前驱：当前关键字左侧指针 所指子树中“最右下”的元素\n删除 B-树中的关键字 G ，G 的前一个孩子结点 y 为 [D、E、F] ，包含 3个关键字，满足情况一，关键字 G 的直接前驱为关键 F ，删除 F ，然后将 G 替换为 F . 2).y 所包含的关键字少于 t 个关键字，则检查结点 x 中关键字 k 的后一个孩子结点 z 包含的关键字的个数，如果 z 包含的关键字的个数至少为 t 个，则在 z 中找到关键字 k 的直接后继 K0 ,然后删除 K0 ，并将关键 k 替换为 K0 .\n直接后继：当前关键字右侧指针 所指子树中“最左下”的元素\n删除 B-树中的关键字 C , y 中包含的关键字的个数为 2 个，小于 t = 3 ,结点 [C、G、L] 中的 关键字 C 的后一个孩子 z 为 [D、E、F] 包含 3 个关键字，关键字 C 的直接后继为 D ，删除 D ，然后将 C 替换为 D .\n3). 如果 y 和 z 都只包含 t -1 个关键字，合并关键字 k 和所有 z 中的关键字到 结点 y 中，结点 x 将失去关键字 k 和孩子结点 z，y 此时包含 2t -1 个关键字，释放结点 z 的空间并递归地从结点 y 中删除关键字 k. 删除关键字 C , 结点 y 包含 2 个关键字 ，结点 z 包含 2 个关键字，均等于 t - 1 = 2 个， 合并关键字 C 和结点 z 中的所有关键字到结点 y 当中：\n之后直接删除C即可。\n如果关键字 k 不在当前在内部结点 x 中，则确定必包含 k 的子树的根结点 x.c(i) （如果 k 确实在 B-树中）。如果 x.c(i) 只有 t - 1 个关键字，必须执行下面两种情况进行处理： 首先我们得确认什么是当前内部结点 x ，什么是 x.c(i) ,如下图所示， P 现在不是根结点，而是完整 B-树的一个子树的根结点：\n1). x.c(i) 仅包含 t - 1 个关键字且 x.c(i) 的一个兄弟结点包含至少 t 个关键字，则将 x 的某一个关键字下移到 x.c(i) 中，将 x.c(i) 的相邻的左兄弟或右兄弟结点中的一个关键字上移到 x 当中，将该兄弟结点中相应的孩子指针移到 x.c(i) 中，使得 x.c(i) 增加一个额外的关键字。\n我们以删除结点 [A、B] 中的结点 B 为例，上图中 x.c(i) 包含 2 个关键字，即 t - 1 个关键字， x.c(i) 的一个兄弟结点 [H、J、K] 包含 3 个关键字（满足至少 t 个关键字的要求），则将兄弟结点 [H、J、K] 中的关键字 H 向上移动到 x 中， 将 x 中的关键字 C 下移到 x.c(i) 中；删除关键字 B . 2).如果 x.c(i) 及 x.c(i) 的所有相邻兄弟都只包含 t - 1 个关键字，则将 x.c(i) 与 一个兄弟合并，即将 x 的一个关键字移动至新合并的结点，使之成为该结点的中间关键字，将合并后的结点作为新的 x 结点 .\n以此图为例：\n上面的图标明了相应的 x 及 x.c(i) ，我们以删除关键字 D 为例，此时当前内部结点 x 不包含关键字 D , 确定是第三种情况，我们可以确认关键 D 一定在结点 x 的第一个孩子结点所在的子树中，结点 x 的第一个孩子结点所在子树的跟结点为 x.c(i) 即 [C、L] . 其中 结点 [C、L] 及其相邻的兄弟结点 [T、W] 都只包含 2 个结点（即 t - 1) ，则将 [C、L] 与 [T、W] 合并，并将结点 x 当中仅有的关键字 P 合并到新结点中；然后将合并后的结点作为新的 x 结点，递归删除关键字 D ，发现D 此时在叶子结点 y 中，直接删除，就是 1. 的情况。\n","date":"2021-08-07T00:00:00Z","permalink":"/b%E6%A0%91/","title":"B树"},{"content":"和为s的连续正数序列 题目: https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/\n思路： 滑动窗口即可，滑动窗口就是选取数组的一部分来进行操作，left 和 right只能向右移动\n代码： class Solution: def findContinuousSequence(self, target: int) -\u0026gt; List[List[int]]: res = [] left,right = 1,2 while left \u0026lt;= target // 2: # 优化 减少时间复杂度 if sum(range(left,right+1)) \u0026lt; target: # 小于target 右指针移动 right += 1 elif sum(range(left,right+1)) \u0026gt; target: # 大于target 左指针移动 left += 1 else: res.append(list(range(left,right+1))) # 相等的话 两个指针都移动 right += 1 left += 1 return res ","date":"2021-08-06T00:00:00Z","permalink":"/%E5%92%8C%E4%B8%BAs%E7%9A%84%E8%BF%9E%E7%BB%AD%E6%AD%A3%E6%95%B0%E5%BA%8F%E5%88%97/","title":"和为s的连续正数序列"},{"content":"合并区间 题目： ​ https://leetcode-cn.com/problems/merge-intervals/\n思路： ​ 一开始思路想的是，根据每一个区间的left排序后，然后比较每一个数，再向前更新，然后写了半天，一直WA，感觉这个思路不太行了\n代码： ​ 先贴上错误的代码：\nclass Solution: def merge(self, res: List[List[int]]) -\u0026gt; List[List[int]]: if not res: return [] res.sort(key=lambda i:i[0]) n = len(res) - 1 for i in range(0,n): if res[i][1] \u0026lt; res[i+1][0]: continue else: res[i+1] = [res[i][0],max(res[i][1],res[i+1][1])] res[i] = res[i-1] ress = [] for i in res: if i not in ress: ress.append(i) return ress 在[[1,4],[0,2],[3,5]]\n​ 出错了\n输出：\n[[3,5],[0,5]]\n预期结果：\n[[0,5]]\n应该是思路的错误\n后来觉得不应该在原数组上操作\n又改了如下，终于过了\nclass Solution: def merge(self, intervals: List[List[int]]) -\u0026gt; List[List[int]]: if not intervals: return [] intervals.sort(key=lambda i:i[0]) res = [] for i in intervals: if len(res) == 0 or res[-1][1] \u0026lt; i[0]: res.append(i) else: res[-1][1] = max(res[-1][1],i[1]) return res 这个思路就是先创造一个空数组res\n然后如果数组为空或者题设的条件不成立的时候，把原数组的值加进去，要是条件成立的话，则将目前区间的right改为目前区间的right和原数组的right之间的最大值，预防[[1,4],[2,3]]这种情况。注意这个也是按left排序的\n我上面代码的思路和这个是一样的，看来类似的题目尽量不要在原数组上面操作，除非题目要求\n","date":"2021-08-03T00:00:00Z","permalink":"/%E5%90%88%E5%B9%B6%E5%8C%BA%E9%97%B4/","title":"合并区间"},{"content":"SVD奇异值分解 参考：https://www.cnblogs.com/pinard/p/6251584.html\n特征值与特征向量 首先回顾特征值与特征向量\\(Ax=\\lambda x\\)\n\\(\\lambda\\) 是矩阵A的一个特征值，x是矩阵A的特征值\\(\\lambda\\)对应的特征向量。\n求出特征值与特征向量可以将矩阵A进行特征分解。如果求出了A的n个特征值，以及这n个特征值所对应的特征向量\\({w_1,w_2,\\dots,w_n}\\)，如果这n个特征向量线性无关，则矩阵A就可以用下式进行表示：\n\\[ A = W\\sum W^{-1} \\]\n其中W为这n个特征向量所张成的\\(n\\times n\\)维矩阵，\\(\\sum\\)为这n个特征值为主对角线的矩阵。\n我们一般会把n个特征向量标准化，即\\(w_i^Tw_i=1\\)，此时W的n个特征向量为标准正交基，满足\\(W^TW=I\\)，即\\(W^T=W^{-1}\\)，也就是说W为酉矩阵。\n这样特征分解表达式可以写为\\(A=W\\sum W^T\\)\n特征分解要求A必须为方阵，如果行列不相同则使用SVD进行分解。\nSVD 假设A为一个\\(m\\times n\\)的矩阵，那么定义A的SVD为：\n\\[ A = U\\sum V^T \\]\n其中U是一个\\(m\\times n\\)的矩阵，\\(\\sum\\)是一个\\(m\\times n\\)的矩阵，除了主对角线上的元素全为0，主对角线上的每个元素成为奇异值，V是一个\\(n\\times n\\)的矩阵，U和V都是酉矩阵。 先得到\\(m\\times m\\)的方阵\\(AA^T\\),然后进行特征值分解，\\((AA^T)u_i=\\lambda_iu_i\\) 将\\(AA^T\\)的所有特征向量张成一个\\(m\\times m\\)的矩阵U，就是SVD公式里面的U矩阵了。\n后得到\\(n\\times n\\)的方阵\\(A^TA\\),然后进行特征值分解，\\((A^TA)v_i=\\lambda_iv_i\\) 将\\(A^TA\\)的所有特征向量张成一个\\(n\\times n\\)的矩阵V，就是SVD公式里面的V矩阵了。\n特征值和奇异值满足以下关系\\(\\sigma_i = \\sqrt{\\lambda_i}\\)，意思就是我们可以通过求\\(A^TA\\)的特征值取平方根来求奇异值。\n","date":"2021-07-16T00:00:00Z","permalink":"/svd/","title":"SVD"},{"content":"词嵌入 介绍 词嵌入是自然语言处理（NLP）中语言模型与表征学习技术的统称。概念上而言，它是指把一个维数为所有词的数量的高维空间嵌入到一个维数低得多的连续向量空间中，每个单词或词组被映射为实数域上的向量。\n词嵌入的方法包括人工神经网络、对词语同现矩阵降维、概率模型以及单词所在上下文的显式表示等。\n在底层输入中，使用词嵌入来表示词组的方法极大提升了NLP中语法分析器和文本情感分析等的效果。\n以上是百度百科中对词嵌入的定义。本文只介绍传统的词向量，也就是固定的词向量。deep contextualized词向量模型在本博客预训练模型内容里面。词嵌入也可以称为词表征(word representation)，可以粗略得把它分为三个阶段：\n一、特征工程阶段，以词袋模型为典型代表。\n二、浅层表证阶段，以word2vec为典型代表。\n三、深层表征阶段，以基于transformer的Bert为典型代表。\n本文介绍了一、二两部分内容\n语言模型 一句话，语言模型是这样一个模型：对于任意的词序列，它能够计算出这个序列是一句话的概率。\n具体的详见本博客有关语言模型的文章。\n为什么要介绍语言模型，因为NLP的做预训练一般选择用语言模型任务来做的。\n语言模型主要有： N-gram LM、FeedForward Neural Network LM、RNN LM和GPT系列\n本文会涉及到Neural Network LM，其本质是一个语言模型，词向量只是其在训练过程中的一个副产物。\n特征工程阶段(基于计数的方法) One-Hot 最简单的方法是将单词表示为 one-hot 向量：对于词汇表中的第 i 个单词，向量在第 i 个维度上具有 1，在其余维度上具有 0。在机器学习中，这是表示分类特征的最简单方法。\nOne-Hot的缺点很明显，它对自己代表的词一无所知，没有捕捉到词的意义。\n词袋模型 即无视词语的顺序，只关心出现的次数，以下都属于词袋模型。在文本匹配领域也有类似的字面意义的匹配。本博客有相关内容。 ### TFIDF\n权重也可以作为词向量表示。也可以计算文本相似度等，本博客有相关内容。\n共现矩阵 详见本博客共现矩阵 ### PPMI 详见本博客互信息相关博文。\n中文名字为正点互信息，公式如下\n\\[ PPMI(w, c) = max(0, PMI(w, c)) \\\\\\\\ PMI(w, c) = log\\frac{P(w,c)}{P(w)P(c)} = log \\frac{N(w,c)|(w,c)|}{N(w)N(c)} \\]\n事实证明，word2vec被证明可以隐式逼近PMI矩阵的因式分解。\n『Neural Word Embedding as Implicit Matrix Factorization』这篇论文就详细讨论了这个问题。 主题模型 这个更是重量级，见本博客。\n浅层表征阶段(基于推理的方法) NNLM 介绍 先来张经典的图片\n这就是大名鼎鼎的神经网络语言模型（其它的语言模型详见本博客语言模型部分）\n学习任务是输入某个句中单词\\(W_t=i\\)前面句子的t-1个单词，要求网络正确预测单词\\(W_t=i\\)，即最大化：\n\\[ P(W_t=i | W_1, W_2, \\dots W_{(t-1)}; \\theta) \\]\n前面任意单词 \\(W_i\\)用Onehot编码（比如：0001000）作为原始单词输入，之后乘以矩阵Q后获得向量 \\(C(W_i)\\)，每个单词的 \\(C(W_i)\\) 拼接，上接隐层，然后接softmax去预测后面应该后续接哪个单词。\n这个\\(C(W_i)\\)是什么？\n这其实就是单词对应的Word Embedding值，那个矩阵Q包含V行，V代表词典大小，每一行内容代表对应单词的Word embedding值。只不过Q的内容也是网络参数，需要学习获得，训练刚开始用随机值初始化矩阵Q，当这个网络训练好之后，矩阵Q的内容被正确赋值，每一行代表一个单词对应的Word embedding值。\n所以你看，通过这个网络学习语言模型任务，这个网络不仅自己能够根据上文预测后接单词是什么，同时获得一个副产品，就是那个矩阵Q，这就是单词的Word Embedding是被如何学会的。\n参数解释 训练样本(Context(w), w),w是语料C的每一个词，Context(w)为取其前n-1个词 投影层向量\\(X_w\\): 将该训练样本(Context(w), w)的前n-1个词的词向量首尾拼接在一起，这里的词向量可以用独热编码表示。\\(X_w\\)的形状为 \\((n-1)\\times m\\)，这里的m为词汇表的所有词的个数。 隐藏层向量\\(Z_w\\): \\[ Z_w = tanh(WX_w+p) \\]\n输出层向量\\(y_w\\): 维度为N=|D|,即词典D中词的个数。 \\[ y_w = Uz_w + q \\] 在对\\(y_w\\)做softmax后，\\(y_w\\)的分量就表示当前词是w的概率\n\\[ p(w|Context(w)) = \\frac{e^{y_w,iw}}{\\sum_{i-1}^N e^{y^{w, i}} } \\]\n优点与缺点 NNLM相对于N-grams语言模型，有以下优点：\n词语与词语间的相似度可以通过词向量来体现 基于词向量的模型自带『平滑化』功能，无需额外处理。 当然也有缺点，缺点就是计算量太大。\n下面就重点介绍一下word2vec，相比于NNLM来说这是专门训练词向量的一种工具，而词向量对于NNLM来说只是一个副产物，其本质还是一个语言模型。\n代码 代码来自https://github.com/graykode/nlp-tutorial\nimport torch import torch.nn as nn import torch.optim as optim def make_batch(): input_batch = [] target_batch = [] for sen in sentences: word = sen.split() # space tokenizer input = [word_dict[n] for n in word[:-1]] # create (1~n-1) as input target = word_dict[word[-1]] # create (n) as target, We usually call this \u0026#39;casual language model\u0026#39; input_batch.append(input) target_batch.append(target) return input_batch, target_batch # Model class NNLM(nn.Module): def __init__(self): super(NNLM, self).__init__() self.C = nn.Embedding(n_class, m) self.H = nn.Linear(n_step * m, n_hidden, bias=False) self.d = nn.Parameter(torch.ones(n_hidden)) self.U = nn.Linear(n_hidden, n_class, bias=False) self.W = nn.Linear(n_step * m, n_class, bias=False) self.b = nn.Parameter(torch.ones(n_class)) def forward(self, X): X = self.C(X) # X : [batch_size, n_step, m] X = X.view(-1, n_step * m) # [batch_size, n_step * m] tanh = torch.tanh(self.d + self.H(X)) # [batch_size, n_hidden] output = self.b + self.W(X) + self.U(tanh) # [batch_size, n_class] return output if __name__ == \u0026#39;__main__\u0026#39;: n_step = 2 # number of steps, n-1 in paper n_hidden = 2 # number of hidden size, h in paper m = 2 # embedding size, m in paper sentences = [\u0026quot;i like dog\u0026quot;, \u0026quot;i love coffee\u0026quot;, \u0026quot;i hate milk\u0026quot;] word_list = \u0026quot; \u0026quot;.join(sentences).split() # [\u0026#39;i\u0026#39;, \u0026#39;like\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;love\u0026#39;, \u0026#39;coffee\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;hate\u0026#39;, \u0026#39;milk\u0026#39;] word_list = list(set(word_list)) # [\u0026#39;i\u0026#39;, \u0026#39;like\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;love\u0026#39;, \u0026#39;coffee\u0026#39;, \u0026#39;hate\u0026#39;, \u0026#39;milk\u0026#39;] word_dict = {w: i for i, w in enumerate(word_list)} # {\u0026#39;i\u0026#39;:0, \u0026#39;like\u0026#39;:1, \u0026#39;dog\u0026#39;:2, \u0026#39;love\u0026#39;:3, \u0026#39;coffee\u0026#39;:4, \u0026#39;hate\u0026#39;:5, \u0026#39;milk\u0026#39;:6} number_dict = {i: w for i, w in enumerate(word_list)} # {0:\u0026#39;i\u0026#39;, 1:\u0026#39;like\u0026#39;, 2:\u0026#39;dog\u0026#39;, 3:\u0026#39;love\u0026#39;, 4:\u0026#39;coffee\u0026#39;, 5:\u0026#39;hate\u0026#39;, 6:\u0026#39;milk\u0026#39;} n_class = len(word_dict) # number of Vocabulary, just like |V|, in this task n_class=7 model = NNLM() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) input_batch, target_batch = make_batch() input_batch = torch.LongTensor(input_batch) target_batch = torch.LongTensor(target_batch) # Training for epoch in range(5000): optimizer.zero_grad() output = model(input_batch) # output : [batch_size, n_class], target_batch : [batch_size] loss = criterion(output, target_batch) if (epoch + 1) % 1000 == 0: print(\u0026#39;Epoch:\u0026#39;, \u0026#39;%04d\u0026#39; % (epoch + 1), \u0026#39;cost =\u0026#39;, \u0026#39;{:.6f}\u0026#39;.format(loss)) loss.backward() optimizer.step() # Predict predict = model(input_batch).data.argmax(1, keepdim=True) # Test print([sen.split()[:2] for sen in sentences], \u0026#39;-\u0026gt;\u0026#39;, [number_dict[n.item()] for n in predict.squeeze()]) word2vec Word2Vec是从大量文本语料中以无监督的方式学习语义知识的一种模型，它被大量地用在自然语言处理（NLP）中。那么它是如何帮助我们做自然语言处理呢？Word2Vec其实就是通过学习文本来用词向量的方式表征词的语义信息，即通过一个嵌入空间使得语义上相似的单词在该空间内距离很近。Embedding其实就是一个映射，将单词从原先所属的空间映射到新的多维空间中，也就是把原先词所在空间嵌入到一个新的空间中去。\nWord2Vec和NNLM不一样，NNLM的主要任务是要学习一个解决语言模型任务的网络结构，语言模型就是要看到上文预测下文，而word embedding只是无心插柳的一个副产品。但是Word2Vec目标不一样，它单纯就是要word embedding的，这是主产品，所以它完全可以随性地这么去训练网络。 (skip-gram与CBOW只是word2vec的变体。)\n训练思路 Word2Vec 是一个模型，其参数是词向量。这些参数针对某个目标进行迭代优化。目标迫使词向量“知道”一个词可能出现的上下文：训练向量以预测相应词的可能上下文。正如您从分布假设中所记得的那样，如果向量“知道”上下文，它们就“知道”单词的含义。\n获取一个巨大的文本语料库； 使用滑动窗口浏览文本，一次移动一个单词。在每一步，都有一个中心词和上下文词（此窗口中的其他词）； 对于中心词，计算上下文词的概率； 调整向量以增加这些概率。 推导 以Skip-grams模型为例，首先清楚几个概念，中心词、背景词（上下文词）、负采样词。中心词就是我们的输入，因为skip-grams相当于在一句话中扣去一个词，然后用这个词预测这句话的其余词。形式上给人的感觉就是一对多，这里的一句话其实不是一句话，是我们设定的窗口大小，比如一句话”I miss you very much”， 设置中心词为you，窗口大小为1，那么背景词就是”miss”和”very”。那么对于我们的模型来说，miss和very就是正例，就是我们的预测值(sigmoid后)的值接近于1的，而其余的词就是负例，就是使其值接近于0的。所以负采样就是从这些负例中随机抽取一些负例，不然每次都要计算单词表中所有单词的sigmoid值，这个计算量很大，而使用负采样就大大缩小了计算量。\n目标函数：负似然对数 对于每个位置\\(t=1,\\dots, T\\)，在文本语料库中，Word2Vec在给定中心词的m大小窗口内预测上下文词\\(w_t\\):\n\\[ Likelihood=L(\\theta)=\\prod_{t=1}^T\\prod_{-m\\leq j\\leq m, j\\neq0} P(w_{t+j} | w_t, \\theta) \\]\n目标函数\\(J(\\theta)\\)为平均负对数似然\n\\[ Loss = J(\\theta) = -\\frac{1}{T}\\sum_{t=1}^T\\sum_{-m\\leq j\\leq m, j\\neq0} logP(w_{t+j}|w_t, \\theta) \\]\n对于每个单词w我们都有两个向量：\n\\(v_w\\)当w是中心词 \\(u_w\\)当w时背景词 因为word2vec是一个专门训练词向量的神经网络，输入为onehot向量，经过第一层W的计算得到隐藏层，就相当于查表，数值为中心词的词向量，然后再与第二层的W计算得到输出，输出的维度和输入的维度相同，因此输出层的结果在计算上就是\\(u_c^Tv_w\\)。即输入层对应的中心词的词向量*输出层对应背景词的词向量，再经过softmax就是对应背景词的概率。 训练完毕后，我们只使用\\(v_w\\)，即只使用从输入层到隐藏层的权重。\n对于中心词w，上下文词c的概率为：\n\\[ P(c|w) = \\frac{exp(u_c^Tv_w)}{\\sum_{o\\in V}exp(u_o^Tv_w)} \\]\n这就是softmax函数。\n训练 \\(\\theta^{new} = \\theta^{old} - \\alpha \\nabla_\\theta J(\\theta)\\)\n一次进行一次更新，每次更新都是针对一对中心词和其中一个背景词。损失函数：\n\\[ Loss = J(\\theta) = -\\frac{1}{T}logL(\\theta)= -\\frac{1}{T}\\sum_{t=1}^T\\sum_{-m\\leq j\\leq m, j\\neq0} logP(w_{t+j}|w_t, \\theta)\\\\\\\\=\\frac{1}{T}\\sum_{t=1}^T\\sum_{-m\\leq j\\leq m, j\\neq0}J_{t,j}(\\theta). \\]\n其中\\(J_{t,j}(\\theta) = -logP(w_{t+j}|w_t, \\theta)\\)\n以”I miss you very much”这句话为例子，中心词为”you”，其中一个背景词为miss，则损失项为\n\\[ J_{t,j}(\\theta) = -logP(miss|you) = -log\\frac{exp(u_{miss}^Tv_{you})}{\\sum_{o\\in V}exp(u_o^Tv_{you})} = \\\\\\\\-u_{miss}^Tv_{you}+log\\sum_{o\\in V} exp(u_o^Tv_{you}) \\]\n这是中心词对应其中一个背景词的损失函数，如果要求总的损失，则将所有背景词的损失相加，然后将所有样本的损失求平均，就是上面的Loss。其实这就是交叉熵损失函数，是个多分类问题，预测的target相当于miss对应的数字，也就是序列值，具体的pytorch代码为\nloss = (-output_layer[:, Y] + torch.log(torch.sum(torch.exp(output_layer), dim=1))).mean()\n这里的output_layer就是神经网络的输出层，注意这里的都是对batch操作，这里求出的loss是这个batch上最终的loss，而不是一对中心词与背景词的loss。理解了这个简单的word2vec模型就算理解了。 下面求一下梯度： 注意这里的c为center word, o为上下文词 \\[ \\frac{\\partial logP(w_o|w_c)}{\\partial v_c} = \\frac{\\partial}{\\partial v_c}log\\frac{exp(u_0^Tv_c)}{\\sum_{i=1}^{|V|}exp(u_i^Tv_c)}\\\\\\\\= \\frac{\\partial}{\\partial v_c}log\\, exp(u_o^Tv_c) - \\frac{\\partial}{\\partial v_c}log\\sum_{i=1}^{|V|}exp(u_i^Tv_c) \\]\n左边的：\n\\[ \\frac{\\partial}{\\partial v_c}log\\, exp(u_o^Tv_c)=\\frac{\\partial}{\\partial v_c}u_o^Tv_c=u_o \\]\n第二部分推导\n\\[ \\begin{aligned} \\frac{\\partial}{\\partial {v}_{c}} \\log \\sum_{i=1}^{|V|} \\exp \\left({u}_{i}^{T} {v}_{c}\\right) \u0026amp;=\\frac{1}{\\sum_{i=1}^{|V|} \\exp \\left({u}_{i}^{T} {v}_{c}\\right)} \\cdot \\frac{\\partial}{\\partial {v}_{c}} \\sum_{x=1}^{|V|} \\exp \\left({u}_{x}^{T} {v}_{c}\\right) \\\\\\\\ \u0026amp;=\\frac{1}{A} \\cdot \\sum_{x=1}^{|V|} \\frac{\\partial}{\\partial {v}_{c}} \\exp \\left({u}_{x}^{T} {v}_{c}\\right) \\\\\\\\ \u0026amp;=\\frac{1}{A} \\cdot \\sum_{x=1}^{|V|} \\exp \\left({u}_{x}^{T} {v}_{c}\\right) \\frac{\\partial}{\\partial {v}_{c}} {u}_{x}^{T} {v}_{c} \\\\\\\\ \u0026amp;=\\frac{1}{\\sum_{i=1}^{|V|} \\exp \\left({u}_{i}^{T} {v}_{c}\\right)} \\sum_{x=1}^{|V|} \\exp \\left({u}_{x}^{T} {v}_{c}\\right) {u}_{x} \\\\\\\\ \u0026amp;=\\sum_{x=1}^{|V|} \\frac{\\exp \\left({u}_{x}^{T} {v}_{c}\\right)}{\\sum_{i=1}^{|V|} \\exp \\left({u}_{i}^{T} {v}_{c}\\right)} {u}_{x} \\\\\\\\ \u0026amp;=\\sum_{x=1}^{|V|} P\\left(w_{x} \\mid w_{c}\\right) {u}_{x} \\end{aligned} \\]\n综上所述\n\\[ \\frac{\\partial \\log P\\left(w_{o} \\mid w_{c}\\right)}{\\partial {v}_{c}}={u}_{o}-\\sum_{j \\in V} P\\left(w_{j} \\mid w_{c}\\right) {u}_{j} \\]\n通过上面计算得到梯度后，我们可以使用随机梯度下降来不断迭代模型参数\\(v_c\\)。其它模型参数\\(u_o\\)的迭代方式同理可得。最终，对于词典中任一索引为i的词，我们均得到该词作为中心词和背景词的两组词向量\\(v_i\\)和\\(u_i\\)\n负采样 在上面的示例中，对于每对中心词及其上下文词，我们必须更新上下文词的所有向量。这是非常低效的：对于每一步，进行更新所需的时间与词汇量大小成正比。\n但是为什么我们必须在每一步都考虑词汇表中的所有上下文向量呢？例如，假设在当前步骤中，我们考虑的不是所有单词的上下文向量，而是当前目标和几个随机选择的单词。\n以跳字模型为例讨论负采样。词典 \\(V\\) 的大小之所以会在目标函数中出现，是因为中心词 \\(w_{c}\\) 生成背景词 \\(w_{o}\\) 的概率 \\(P\\left(w_{o} \\mid w_{c}\\right)\\) 使用了 softmax，而 softmax 考虑到了背景词可能是词 典中任一词，并体现在了 softmax 的分母上 我们不妨换个角度，假设中心词 \\(w_{c}\\) 生成背景词 \\(w_{o}\\) 由以下两个互相独立的联合事件组成来近 似\n中心词 \\(w_{c}\\) 和背景词 \\(w_{o}\\) 同时出现在该训练数据窗口\n中心词 \\(w_{c}\\) 和噪声词不同时出现在该训练数据窗口\n中心词 \\(w_{c}\\) 和第 1 个噪声词 \\(w_{1}\\) 不同时出现在训练数据窗口（噪声词 \\(w_{1}\\) 按噪声词 分布 \\(P(w)\\) 随机生成) …. 中心词 \\(w_{c}\\) 和第 \\(K\\) 个噪声词 \\(w_{k}\\) 不同时出现在训练数据窗口 (噪声词 \\(w_{K}\\) 按噪声 词分布 \\(P(w)\\) 随机生成) 我们可以使用 \\(\\sigma(x)=\\frac{1}{1+\\exp (-x)}\\) 函数来表达中心词 \\(w_{c}\\) 和背景词 \\(w_{o}\\) 同时出现在训练数据 窗口的概率:\n\\[ P\\left(D=1 \\mid w_{o}, w_{c}\\right)=\\sigma\\left({u}_{o}^{T}, {v}_{c}\\right) \\]\n那么，中心词 \\(w_{c}\\) 生成背景词 \\(w_{o}\\) 的对数概率可以近似为\n\\[ \\log P\\left(w_{o} \\mid w_{c}\\right)=\\log \\left[P\\left(D=1 \\mid w_{o}, w_{c}\\right) \\prod_{k=1, w_{k} \\sim P(w)}^{K} P\\left(D=0 \\mid w_{k}, w_{c}\\right)\\right] \\]\n其中后面的表示中心词和噪声词不同时出现在训练数据窗口的概率。 假设噪声词 \\(w_{k}\\) 在词典中的索引为 \\(i_{k}\\) ，上式可改写为\n\\[ \\log P\\left(w_{o} \\mid w_{c}\\right)=\\log \\frac{1}{1+\\exp \\left(-{u}_{o}^{T} {v}_{c}\\right)}+\\sum_{k=1, w_{k} \\sim P(w)}^{K} \\log \\left[1-\\frac{1}{1+\\exp \\left(-{u}_{i_{k}}^{T} {v}_{c}\\right)}\\right] \\]\n因此，有关中心词 \\(w_{c}\\) 生成背景词 \\(w_{o}\\) 的损失函数是(\\(1-\\sigma(x) = \\sigma(-x)\\)):\n\\[ \\begin{aligned} -\\log P\\left(w_{o} \\mid w_{c}\\right)=-\\log \\frac{1}{1+\\exp \\left(-{u}_{o}^{T} {v}_{c}\\right)}-\\sum_{k=1, w_{k} \\sim P(w)}^{K} \\log \\frac{1}{1+\\exp \\left({u}_{i_{k}}^{T} {v}_{c}\\right)} \\\\\\\\ = -\\log \\sigma(u_o^Tv_c) - \\sum_{k=1, w_k\\sim P(w)} ^ K \\log\\sigma (-u_{ik}^Tv_c) \\end{aligned} \\]\n与PPMI的关系 在引入负采样方法后，也就是变成了Skip-Gram with Negative Sample，简称为SGNS。回顾一下他的目标函数：\n对于word2vec来说，训练完成以后，每一个token都会有两个向量，一个是作为中心词的，一个是作为上下文词的。假设存在一个矩阵\\(W=V_{word}*V_{context}\\)。\nword2vec也可以理解成是对矩阵W的矩阵分解。那么，问题就变成这个matrix W是什么？\n论文NIPS-2014-neural-word-embedding-as-implicit-matrix-factorization-Paper中有将SGNS的目标函数，一步步的变换，变成下面的形式（具体见论文）\n上面的公式，左边的部分就是PMI，PMI是dense matrix，对模型优化带来很多困难，所以一般会用sparse matrix PPMI（positive PMI）来替代它。\n至此我们找到了矩阵M，即 PPMI是基于计数的方法表达词向量，而word2vec是基于推理的方法表达词向量。后面介绍的Glove将基于计数的方法和基于推理的方法结合了起来。 #### 层序softmax\n层序softmax利用了二叉树，树的每个节点代表了词典V中的每个词。每个词\\(w_i\\)对应词向量\\(v_i\\)。\n设 \\(L(w)\\) 为从二叉树根节点到代表词 \\(w\\) 的叶子节点的路径上的节点数，并设 \\(n(w, i)\\) 为该路径上第 \\(i\\) 个节点，该节点的向量为 \\({u}_{n(w, j)}\\) 。以上图为例， \\(L\\left(w_{3}\\right)=4\\) 。那么，跳字模型和连续词袋模型所需 要计算的任意词 \\(w_{i}\\) 生成词 \\(w\\) 的概率为:\n\\[ P\\left(w \\mid w_{i}\\right)=\\prod_{j=1}^{L(w)-1} \\sigma\\left(\\left[n(w, j+1)=l e f t_{-} \\operatorname{child}(n(w, j))\\right] \\cdot {u}_{n(w, j)}^{T} {v}_{i}\\right) \\]\n其中，如果 \\(x\\) 为真， \\([x]=1\\) ；反之 \\([x]=-1\\) 由于 \\(\\sigma(x)+\\sigma(-x)=1, w_{i}\\) 生成词典中任何词的概率之和为 1 :\n\\[ \\sum_{w=1}^{V} P\\left(w \\mid w_{i}\\right)=1 \\]\n上面公式可能比较抽象，下面举个具体的例子，计算 \\(w_{i}\\) 生成 \\(w_{3}\\) 的概率，由于在二叉树中由根到 \\(w_{3}\\) 的路径需要向左、向右、再向左地遍历，所以得到\n\\[ P\\left(w_{3} \\mid w_{i}\\right)=\\sigma\\left({u}_{n\\left(w_{3}, 1\\right)}^{T} {v}_{i}\\right) \\cdot \\sigma\\left(-{u}_{n\\left(w_{3}, 2\\right)}^{T} {v}_{i}\\right) \\cdot \\sigma\\left({u}_{n\\left(w_{3}, 3\\right)}^{T} {v}_{i}\\right) \\]\n由此，我们就可以使用随机梯度下降在跳字模型和连续词袋模型中不断迭代计算词典中所有词向量 \\({v}\\)\n最后一个问题，层序 softmax 的二叉树是如何建立的?\n这里的二叉树 Huffman 树，权重是语料库中 word 出现的频率\n标准设置 模型: 带负采样的Skip-Gram(SGNS) 负采样的数目: 较小的数据集,15-20;较大的数据集,2-5。 词嵌入维度: 经常用的为300 滑动窗口大小: 5-10 代码 简单 import numpy as np import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt def random_batch(): random_inputs = [] random_labels = [] random_index = np.random.choice(range(len(skip_grams)), batch_size, replace=False) for i in random_index: random_inputs.append(np.eye(voc_size)[skip_grams[i][0]]) # target 相当于onehot编码 random_labels.append(skip_grams[i][1]) # context word return random_inputs, random_labels # Model class Word2Vec(nn.Module): def __init__(self): super(Word2Vec, self).__init__() # W and WT is not Traspose relationship self.W = nn.Linear(voc_size, embedding_size, bias=False) # voc_size \u0026gt; embedding_size Weight self.WT = nn.Linear(embedding_size, voc_size, bias=False) # embedding_size \u0026gt; voc_size Weight def forward(self, X): # X : [batch_size, voc_size] hidden_layer = self.W(X) # hidden_layer : [batch_size, embedding_size] output_layer = self.WT(hidden_layer) # output_layer : [batch_size, voc_size] return output_layer if __name__ == \u0026#39;__main__\u0026#39;: batch_size = 2 # mini-batch size embedding_size = 2 # embedding size sentences = [\u0026quot;apple banana fruit\u0026quot;, \u0026quot;banana orange fruit\u0026quot;, \u0026quot;orange banana fruit\u0026quot;, \u0026quot;dog cat animal\u0026quot;, \u0026quot;cat monkey animal\u0026quot;, \u0026quot;monkey dog animal\u0026quot;] word_sequence = \u0026quot; \u0026quot;.join(sentences).split() word_list = \u0026quot; \u0026quot;.join(sentences).split() word_list = list(set(word_list)) word_dict = {w: i for i, w in enumerate(word_list)} voc_size = len(word_list) # Make skip gram of one size window skip_grams = [] for i in range(1, len(word_sequence) - 1): target = word_dict[word_sequence[i]] context = [word_dict[word_sequence[i - 1]], word_dict[word_sequence[i + 1]]] for w in context: skip_grams.append([target, w]) model = Word2Vec() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # Training for epoch in range(5000): input_batch, target_batch = random_batch() input_batch = torch.Tensor(input_batch) target_batch = torch.LongTensor(target_batch) optimizer.zero_grad() output = model(input_batch) # output : [batch_size, voc_size], target_batch : [batch_size] (LongTensor, not one-hot) loss = criterion(output, target_batch) if (epoch + 1) % 1000 == 0: print(\u0026#39;Epoch:\u0026#39;, \u0026#39;%04d\u0026#39; % (epoch + 1), \u0026#39;cost =\u0026#39;, \u0026#39;{:.6f}\u0026#39;.format(loss)) loss.backward() optimizer.step() for i, label in enumerate(word_list): W, WT = model.parameters() x, y = W[0][i].item(), W[1][i].item() plt.scatter(x, y) plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords=\u0026#39;offset points\u0026#39;, ha=\u0026#39;right\u0026#39;, va=\u0026#39;bottom\u0026#39;) plt.show() 这是没有任何训练技巧的版本，使用Linear的话要注意输入是独热编码的形式，而且设置bias=False，因为需要的只是权重矩阵，不需要偏置。下面是引入了负采样的技巧的版本，直接使用了nn.Embedding。 ##### 复杂\n引入了负采样\nimport logging import pickle import torch.nn.functional as F import numpy as np import torch import torch.nn as nn import torch.utils.data as Data from collections import Counter import random import copy logging.basicConfig(level=logging.INFO, format=\u0026#39;%(asctime)s - %(levelname)s - %(message)s\u0026#39;) device = torch.device(\u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39;) C = 3 # window size K = 15 # Number of negitive samples epochs = 1 MAX_VOCAB_SIZE = 10000 # 单词表的长度 EMBEDDING_SIZE = 100 # 词嵌入的维数 batch_size = 32 lr = 1e-3 with open(\u0026quot;./text8/text8.train.txt\u0026quot;, \u0026quot;r\u0026quot;) as f: text = f.read() text = text.lower().split() vocab_dict = dict(Counter(text).most_common(MAX_VOCAB_SIZE-1)) vocab_dict[\u0026quot;\u0026lt;UNK\u0026gt;\u0026quot;] = len(text) - np.sum(list(vocab_dict.values())) word2idx = {word: i for i, word in enumerate(vocab_dict.keys())} idx2word = {i: word for i, word in enumerate(vocab_dict.keys())} word_counts = np.array([count for count in vocab_dict.values()]) word_freqs = word_counts / np.sum(word_counts) word_freqs = word_freqs ** (3/4) # 经验 class WordEmbeddingDataset(Data.Dataset): def __init__(self, text, word2idx, word_freqs): super(WordEmbeddingDataset, self).__init__() self.text_encoded = torch.LongTensor( [word2idx.get(word, word2idx[\u0026quot;\u0026lt;UNK\u0026gt;\u0026quot;]) for word in text]) self.word2idx = word2idx self.word_freqs = torch.Tensor(word_freqs) def __getitem__(self, index): center_words = self.text_encoded[index] pos_indices = list(range(index-C, index)) + \\ list(range(index+1, index+C+1)) pos_indices = [i % len(self.text_encoded) for i in pos_indices] pos_words = self.text_encoded[pos_indices] select_weights = copy.deepcopy(self.word_freqs) select_weights[center_words] = 0 select_weights[pos_words] = 0 neg_words = torch.multinomial( select_weights, K * pos_words.shape[0], True) return center_words, pos_words, neg_words def __len__(self): return len(self.text_encoded) dataset = WordEmbeddingDataset(text, word2idx, word_freqs) dataloader = Data.DataLoader(dataset, batch_size=batch_size, shuffle=True) class Model(nn.Module): def __init__(self, vocab_size, embedding_size): super(Model, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_size) self.embedding2 = nn.Embedding(vocab_size, embedding_size) def forward(self, input_lables, pos_labels, neg_labels): input_embedding = self.embedding(input_lables) # [batch_size, embedding_size] pos_embedding = self.embedding2(pos_labels) # [batch_size, window * 2, embedding_size] neg_embedding = self.embedding2(neg_labels) # [batch_size, K * window * 2, embedding_size] input_embedding = input_embedding.unsqueeze(2) # [batch_size, embedding_size, 1] pos_dot = torch.bmm(pos_embedding, input_embedding).squeeze(2) # [batch_size, window * 2] ## bmm == batch matrix multiply neg_dot = torch.bmm(neg_embedding, input_embedding).squeeze(2) # [batch_size, K * window * 2] log_pos = F.logsigmoid(pos_dot).sum(dim=1) # [batch_size] log_neg = F.logsigmoid(-1 * neg_dot).sum(dim=1) # [batch_size] return -(log_pos + log_neg).mean() # 对应上文推导的公式 def input_embedding(self): return self.embedding.weight.detach().cpu().numpy() model = Model(MAX_VOCAB_SIZE, EMBEDDING_SIZE).to(device) optimizer = torch.optim.Adam(model.parameters(), lr=lr) for epoch in range(epochs): for i, (input_labels, pos_labels, neg_labels) in enumerate(dataloader): input_labels = input_labels.long().to(device) pos_labels = pos_labels.long().to(device) neg_labels = neg_labels.long().to(device) optimizer.zero_grad() loss = model(input_labels, pos_labels, neg_labels) loss.backward() optimizer.step() if (i+1) % 100 == 0: logging.info(f\u0026quot;epoch: {epoch}, batch: {i+1}, loss: {loss.item()}\u0026quot;) embedding_weights = model.input_embedding() torch.save(model.state_dict(), f\u0026quot;embedding-{EMBEDDING_SIZE}.pth\u0026quot;) with open(\u0026quot;word2idx.pkl\u0026quot;, \u0026quot;wb\u0026quot;) as f: pickle.dump(word2idx, f) with open(\u0026quot;idx2word.pkl\u0026quot;, \u0026quot;wb\u0026quot;) as f: pickle.dump(idx2word, f) with open(\u0026quot;embedding_weights.pkl\u0026quot;, \u0026quot;wb\u0026quot;) as f: pickle.dump(embedding_weights, f) GloVe GloVe 模型是基于计数的方法和预测方法（例如 Word2Vec）的组合。模型名称 GloVe 代表“Global Vectors”，体现了它的思想：该方法利用 语料库中的全局信息来学习向量。\n正如我们之前看到的，最简单的基于计数的方法使用共现计数来衡量单词 w 和上下文c之间的关联：\\(N(w,c)\\)。 GloVe 也使用这些计数来构建损失函数：\n\\[ J(\\theta) = \\sum_{w,c\\in V}f(N(w,c)) \\cdot(u_c^Tv_w+b_c+\\bar{b_w}-logN(w,c)^2) \\]\n其中： \\[ f(x) = \\begin{cases} (\\frac{x}{x_{max}})^{0.75}, \\quad if \\;x \u0026lt; x_{max} \\\\\\\\ 1, \\quad if \\; x\\geq x_{max} \\end{cases} \\]\n具体的推导可以看这个博客：https://www.cnblogs.com/Lee-yl/p/11172255.html\n可以看到GloVe并没有使用神经网络的方法。\n与 Word2Vec 类似，我们也有不同的 中心词和上下文词向量——这些是我们的参数。此外，该方法对每个词向量都有一个标量偏置项。\n特别有趣的是 GloVe 控制稀有词和频繁词影响的方式：每对 ( w , c ) 的损失以如下方式加权\n罕见事件受到惩罚， 非常频繁的事件不会被过度加权。 关于word embedding word embedding只能通过语言模型的训练获取吗？ 不是的；事实上任何NLP任务都可以在训练过程中获取word embedding，甚至特定任务下的word embedding在特定任务中的使用效果还会更好（如基于fasttext的文本分类任务）。之所以我们平时提及的word embedding均是在语言模型任务中产生，私以为主要有这么几个原因：a).语言模型是无监督任务，存在海量训练语料，无需标注成本；b).语言模型任务本身要求较高，训练过程中可以学习大量的语义知识，进而生成高质的word representation。\n如何评估word embedding好坏？ 有两种方式：第一种，把word embedding融入现有系统中，看其对系统性能的提升；第二，从语言学的角度对word embedding进行分析，如相似度、语义偏移等。更细节的可以参考这里。 ## 参考\n参考：\nhttps://blog.csdn.net/malefactor/article/details/83961886 https://www.zybuluo.com/Dounm/note/591752 https://lena-voita.github.io/nlp_course/word_embeddings.html https://wmathor.com/index.php/archives/1430/ https://zhuanlan.zhihu.com/p/27234078\nRong X . word2vec Parameter Learning Explained[J]. Computer Science, 2014.\n","date":"2021-07-16T00:00:00Z","permalink":"/word-embedding/","title":"Word Embedding"},{"content":"三数之和 题目： https://leetcode-cn.com/problems/3sum/solution/\n思路： ​ 第一眼看就想到了用双指针，注意重复数值的处理问题，算是一个滑动窗口问题\n代码： class Solution: def threeSum(self, nums: List[int]) -\u0026gt; List[List[int]]: res = [] if len(nums) \u0026lt; 3: return [] nums.sort() for i, num in enumerate(nums): if num \u0026gt; 0: return res if i \u0026gt; 0 and nums[i] == nums[i-1]: continue left, right = i+1, len(nums) - 1 while left \u0026lt; right: temp = nums[i] + nums[left] + nums[right] if temp == 0: res.append([nums[i], nums[left], nums[right]]) while left \u0026lt; right and nums[right-1] == nums[right]: right -= 1 while left \u0026lt; right and nums[left+1] == nums[left]: left += 1 left += 1 right -= 1 if temp \u0026gt; 0: right -=1 if temp \u0026lt; 0: left += 1 return res ","date":"2021-07-16T00:00:00Z","permalink":"/%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C/","title":"三数之和"},{"content":"","date":"2021-06-29T00:00:00Z","permalink":"/%E5%B1%82%E6%AC%A1%E8%81%9A%E7%B1%BB/","title":"层次聚类"},{"content":"# ROC曲线 了解什么是ROC曲线和AUC之前，要先了解什么是混淆矩阵。\n混淆矩阵中有着Positive、Negative、True、False的概念，其意义如下：\n称预测类别为1的为Positive（阳性），预测类别为0的为Negative（阴性）。\n预测正确的为True（真），预测错误的为False（伪）。 因此有了True Postive Rate、False Postive Rate两个概念\n\\(TPRate = \\frac{TP}{TP+FN}\\)\n\\(FPRate = \\frac{FP}{FP+TN}\\)\nTPRate的意义是所有真实类别为1的样本中，预测类别为1的比例。\nFPRate的意义是所有真实类别为0的样本中，预测类别为1的比例。\n按照定义，AUC即ROC曲线下的面积，而ROC曲线的横轴是FPRate，纵轴是TPRate，当二者相等时，即y=x，如下图:\n这样的分类器和瞎猜没啥区别，我们可以看成AUC的最小值为0.5。\n而我们希望分类器达到的效果是：对于真实类别为1的样本，分类器预测为1的概率（即TPRate），要大于真实类别为0而预测类别为1的概率（即FPRate），即y＞x。这是理所当然的，分类器肯定要分对的嘛。\n我们知道，在二分类（0，1）的模型中，一般我们最后的输出是一个概率值，表示结果是1的概率。那么我们最后怎么决定输入的x是属于0或1呢？我们需要一个 阈值，超过这个阈值则归类为1，低于这个阈值就归类为0。\n所以，不同的阈值会导致分类的结果不同，也就是混淆矩阵不一样了，FPR和TPR也就不一样了。所以当阈值从0开始慢慢移动到1的过程，就会形成很多对(FPR, TPR)的值，将它们画在坐标系上，就是所谓的ROC曲线了。\n看这张图： 当阈值选取为0.5时，阈值右边的视为 预测为正例 ，阈值左边的视为 预测为负例 。由于准确度为\\(\\frac{TP+TN}{all}\\)，因此可得此时的准确度为90%。 看这张图： 阈值设定为0.6，即右边视为 预测为正例 ，红色的为实际为正例，蓝色的为实际为负例，因此很容易得到FP=0，因为阈值右边没有蓝色区域，可以这么理解。\n当蓝色区域与红色区域基本重叠时，ROC曲线就和接近y=x这条线了。\n其实，AUC表示的是正例排在负例前面的概率\n我们知道阈值可以取不同，也就是说，分类的结果会受到阈值的影响。如果使用AUC的话，因为阈值变动考虑到了，所以评估的效果更好。\n最后说说AUC的优势，AUC的计算方法同时考虑了分类器对于正例和负例的分类能力，在样本不平衡的情况下，依然能够对分类器作出合理的评价。\nAUC计算 ","date":"2021-06-26T00:00:00Z","permalink":"/roc%E6%9B%B2%E7%BA%BF/","title":"ROC曲线"},{"content":"删除排序数组中的重复项 删除排序数组中的重复项1 题目： ​ https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/\n思路： ​ 双指针，定义 nums[0...i] 为为非重复数列，遍历整个数列不断的维护这个定义\n代码： ​\nclass Solution: def removeDuplicates(self, nums: List[int]) -\u0026gt; int: start = 0 for i in range(len(nums)): if nums[i] != nums[start]: start += 1 nums[i],nums[start] = nums[start],nums[i] return start + 1 删除排序数组中的重复项2 题目： ​ https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/\n思路： ​ 也是利用双指针，一个指针用于遍历数组元素，一个指针指向要拷贝赋值的索引位置\n代码： ​\nclass Solution: def removeDuplicates(self, nums: List[int]) -\u0026gt; int: if len(nums) \u0026lt;= 2: #长度小于等于2时 return len(nums) count = 1 #用于重复的计数 j = 1 #指向多余重复的元素 for i in range(1,len(nums)): if nums[i] == nums[i-1]: count += 1 #重复了就加一 if count \u0026gt; 2: #如果重复两次以上就pass掉，等着被替换 pass else: nums[j] = nums[i] j += 1 else: nums[j] = nums[i] #如果不相等了 把多余重复的那个替换掉了 count = 1 #重置计数 j += 1 return j 这是一种思路比较清晰的写法\n","date":"2021-06-22T00:00:00Z","permalink":"/%E5%88%A0%E9%99%A4%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E9%A1%B9/","title":"删除排序数组中的重复项"},{"content":"import threading import time 多线程例子 def spider(): #..... time.sleep(0.02) def main1(): for i in range(100): spider() def main2(): thread_list = [] for i in range(100): thread = threading.Thread(target = spider) thread.start() thread_list.append(thread) for t in thread_list: t.join() if __name__ == \u0026quot;__main__\u0026quot;: start = time.time() main1() end = time.time() print(\u0026quot;time1 :{:.4f}\u0026quot;.format(end-start)) start = time.time() main2() end = time.time() print(\u0026quot;time2 :{:4f}\u0026quot;.format(end-start)) time1 :2.0523 time2 :0.037929 yield def fib(n): a,b = 0,1 while b\u0026lt;n: a,b = b,a+b yield a print(fib(100)) for i in fib(100): print(i) \u0026lt;generator object fib at 0x000002B1A7AA1E60\u0026gt; 1 1 2 3 5 8 13 21 34 55 89 协程 GEN_CREATED 创建完成，等待执行 GEN_RUNNING 解释器正在执行 GEN_SUSPENDED 在 yield 表达式处暂停 GEN_CLOSE 执行结束，生成器停止\nimport inspect def generator(): i = \u0026quot;激活生成器\u0026quot; while True: try: value = yield i except ValueError: print(\u0026quot;OVER\u0026quot;) i = value g = generator() print(inspect.getgeneratorstate(g)) #查看状态 next(g) # next(g)相当于g.send(None) 可以用后面的语句来预缴携程 GEN_CREATED \u0026#39;激活生成器\u0026#39; inspect.getgeneratorstate(g) #查看生成器状态 \u0026#39;GEN_SUSPENDED\u0026#39; g.send(\u0026quot;hello world\u0026quot;) \u0026#39;hello world\u0026#39; 暂停状态的生成器可以使用 send 方法发送数据，此方法的参数就是 yield 表达式的值，也就是 yield 表达式等号前面的 value 变量的值变成 ‘Hello Shiyanlou’，继续向下执行完一次 while 循环，变量 i 被赋值，继续运行下一次循环，yield 表达式弹出变量 i\ng.throw(ValueError) #抛出异常 结束 OVER \u0026#39;hello world\u0026#39; g.close() inspect.getgeneratorstate(g) #关闭了 \u0026#39;GEN_CLOSED\u0026#39; 预激协程 from functools import wraps def corcut(func): @wraps(func) def wrapper(*args,**kw): g = func(*args,**kw) next(g) return g return wrapper @corcut #装饰器 def generator(): i = \u0026quot;激活生成器\u0026quot; while True: try: value = yield i except ValueError: print(\u0026quot;OVER\u0026quot;) i = value g = generator() print(inspect.getgeneratorstate(g)) #此时已经用装饰器将生成器激活了 GEN_SUSPENDED @corcut def generator(): l = [] while True: value = yield if value == \u0026quot;CLOSE\u0026quot;: break l.append(value) return l g = generator() for i in [\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;CLOSE\u0026#39;]: try: g.send(i) except StopIteration as e: value = e.value value [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;] yield from用法 from itertools import chain c = chain({\u0026#39;one\u0026#39;,\u0026#39;two\u0026#39;,\u0026#39;three\u0026#39;},list(\u0026quot;abc\u0026quot;)) for i in c: print(i) three two one a b c def chains1(*args): for i in args: for n in i: yield n def chains2(*args): for i in args: yield from i #i为可迭代对象，避免嵌套循环 c1 = chains1({\u0026quot;one\u0026quot;,\u0026quot;two\u0026quot;,\u0026quot;three\u0026quot;},list(\u0026quot;abc\u0026quot;)) for i in c1: print(i) print(\u0026quot;\\n\u0026quot;) c2 = chains2({\u0026quot;one\u0026quot;,\u0026quot;two\u0026quot;,\u0026quot;three\u0026quot;},list(\u0026quot;abc\u0026quot;)) for i in c2: print(i) three two one a b c three two one a b c 转移控制权 from functools import wraps from faker import Faker import time def corout(func): @wraps(func) def wapper(*args,**kw): g = func(*args,**kw) next(g) return g return wapper # 子生成器 def generator(): l = [] while True: i = yield if i == \u0026quot;CLOSE\u0026quot;: break l.append(i) return sorted(l) # 委托生成器 @corout def generator2(): while True: l = yield from generator() print(\u0026quot;排序后的列表\u0026quot;,l) print(\u0026quot;-----------------\u0026quot;) # 客户端 if __name__ == \u0026quot;__main__\u0026quot;: fake = Faker().country_code nest_country = [[fake() for i in range(3)] for j in range(3)] for country in nest_country: print(\u0026#39;国家代号列表：\u0026#39;, country) c = generator2() for i in country: c.send(i) c.send(\u0026quot;CLOSE\u0026quot;) 国家代号列表： [\u0026#39;AM\u0026#39;, \u0026#39;ZA\u0026#39;, \u0026#39;BG\u0026#39;] 排序后的列表 [\u0026#39;AM\u0026#39;, \u0026#39;BG\u0026#39;, \u0026#39;ZA\u0026#39;] ----------------- 国家代号列表： [\u0026#39;UG\u0026#39;, \u0026#39;BE\u0026#39;, \u0026#39;SI\u0026#39;] 排序后的列表 [\u0026#39;BE\u0026#39;, \u0026#39;SI\u0026#39;, \u0026#39;UG\u0026#39;] ----------------- 国家代号列表： [\u0026#39;SC\u0026#39;, \u0026#39;KI\u0026#39;, \u0026#39;KI\u0026#39;] 排序后的列表 [\u0026#39;KI\u0026#39;, \u0026#39;KI\u0026#39;, \u0026#39;SC\u0026#39;] ----------------- yield显然不只是用来减小循环次数的，引用一下《流畅的python》中关于yield from 的意义： - 子生成器产出的值都直接传给委派生成器的调用方（即客户端代码）。 - 使用 send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是 None，那么会调用子生成器的 next() 方法。如果发送的值不是 None，那么会调用子生成器的 send() 方法。如果调用的方法抛出 StopIteration 异常，那么委派生成器恢复运行。任何其他异常都会向上冒泡，传给委派生成器。 - 生成器退出时，生成器（或子生成器）中的 return expr 表达式会触发 StopIteration(expr) 异常抛出。 - yield from 表达式的值是子生成器终止时传给 StopIteration异常的第一个参数。 为什么yield可以转移控制权，可以看一下这一段伪代码： 注意这里的6是委托生成器向子生成器发送_s，而_s是调用方向委托生成器发送的，发送后得到结果_y，并在下一个循环yield即抛出给调用方。 ## asyncio模块\nimport time import asyncio def one(): start = time.time() @asyncio.coroutine #1 def do_something(): #2 print(\u0026quot;start ------\u0026quot;) time.sleep(0.1) #3 print(\u0026quot;doing something\u0026quot;) loop = asyncio.get_event_loop() #4 coroutine = do_something() #5 loop.run_until_complete(coroutine) #6 end = time.time() print(\u0026quot;消耗时间:{:.4f}\u0026quot;.format(end-start))#7 one() start ------ doing something 消耗时间:0.1012 代码说明：\n1、使用协程装饰器创建协程函数\n2、协程函数\n3、模拟 IO 操作\n4、创建事件循环。每个线程中只能有一个事件循环，get_event_loop 方法会获取当前已经存在的事件循环，如果当前线程中没有，新建一个\n5、调用协程函数获取协程对象\n6、将协程对象注入到事件循环，协程的运行由事件循环控制。事件循环的 run_until_complete 方法会阻塞运行，直到任务全部完成。协程对象作为 run_until_complete 方法的参数，loop 会自动将协程对象包装成任务来运行。后面我们会讲到多个任务注入事件循环的情况\n7、打印程序运行耗时\nimport time import asyncio def two(): start = time.time() @asyncio.coroutine def do_something(): print(\u0026quot;start ------\u0026quot;) time.sleep(0.1) print(\u0026quot;doing something\u0026quot;) loop = asyncio.get_event_loop() coroutine = do_something() task = loop.create_task(coroutine) #1 print(\u0026quot;task是不是Task的示例？\u0026quot;,isinstance(task,asyncio.Task)) #2 print(\u0026quot;task state\u0026quot;,task._state) #3 loop.run_until_complete(task) #4 print(\u0026quot;take state\u0026quot;,task._state) end = time.time() print(\u0026quot;消耗时间:{:.4f}\u0026quot;.format(end-start)) two() task是不是Task的示例？ True task state PENDING start ------ doing something take state FINISHED 消耗时间:0.1013 1、事件循环的 create_task 方法可以创建任务，另外 asyncio.ensure_future 方法也可以创建任务，参数须为协程对象\n2、task 是 asyncio.Task 类的实例，为什么要使用协程对象创建任务？因为在这个过程中 asyncio.Task 做了一些工作，包括预激协程、协程运行中遇到某些异常时的处理\n**3、task 对象的 _state 属性保存当前任务的运行状态，任务的运行状态有 PENDING 和 FINISHED 两种**\n4、将任务注入事件循环，阻塞运行\nasync / await import functools def three(): start = time.time() #@asyncio.coroutine async def do_something(): #1 print(\u0026quot;start doing\u0026quot;) time.sleep(0.1) print(\u0026quot;done\u0026quot;) def callback(name,task): #2 print(\u0026quot;call back:{}\u0026quot;.format(name)) print(\u0026quot;call back:{}\u0026quot;.format(task._state)) loop = asyncio.get_event_loop() coroutine = do_something() task = loop.create_task(coroutine) task.add_done_callback(functools.partial(callback, \u0026#39;vllbc\u0026#39;)) #3 loop.run_until_complete(task) end = time.time() print(\u0026quot;total time {:.4f}\u0026quot;.format(end-start)) three() start doing done call back:vllbc call back:FINISHED total time 0.1013 代码说明：\n1、使用 async 关键字替代 asyncio.coroutine 装饰器创建协程函数\n2、回调函数，协程终止后需要顺便运行的代码写入这里，回调函数的参数有要求，最后一个位置参数须为 task 对象\n3、task 对象的 add_done_callback 方法可以添加回调函数，注意参数必须是回调函数，这个方法不能传入回调函数的参数，这一点需要通过 functools 模块的 partial 方法解决，将回调函数和其参数 name 作为 partial 方法的参数，此方法的返回值就是偏函数，偏函数可作为 task.add_done_callback 方法的参数\ndef four(): start = time.time() async def do_something(name,t): print(\u0026quot;start !\u0026gt;\u0026gt;\u0026quot;,name) await asyncio.sleep(t) #1 print(\u0026#39;Stop coroutine\u0026#39;, name) return \u0026#39;Coroutine {} OK\u0026#39;.format(name) #2 loop = asyncio.get_event_loop() coroutine1 = do_something(\u0026#39;wlb\u0026#39;,3) #3 coroutine2 = do_something(\u0026#39;yyh\u0026#39;,1) task1 = loop.create_task(coroutine1) #4 task2 = loop.create_task(coroutine2) gather = asyncio.gather(task1,task2) #5 loop.run_until_complete(gather) print(\u0026quot;task1\u0026quot;,task1.result()) print(\u0026quot;task2\u0026quot;,task2.result()) #result = loop.run_until_complete(gather) #这里result就是两个返回值组成的列表 即[\u0026#39;task1 Coroutine wlb OK\u0026#39;,\u0026#39;task2 Coroutine yyh OK\u0026#39;] end = time.time() print(\u0026quot;total time:{:.4f}\u0026quot;.format(end-start)) four() start !\u0026gt;\u0026gt; wlb start !\u0026gt;\u0026gt; yyh Stop coroutine yyh Stop coroutine wlb task1 Coroutine wlb OK task2 Coroutine yyh OK total time:3.0022 代码说明：\n1、await 关键字等同于 Python 3.4 中的 yield from 语句，后面接协程对象。asyncio.sleep 方法的返回值为协程对象，这一步为阻塞运行。asyncio.sleep 与 time.sleep 是不同的，前者阻塞当前协程，即 corowork 函数的运行，而 time.sleep 会阻塞整个线程，所以这里必须用前者，阻塞当前协程，CPU 可以在线程内的其它协程中执行\n2、协程函数的 return 值可以在协程运行结束后保存到对应的 task 对象的 result 方法中\n3、创建两个协程对象，在协程内部分别阻塞 3 秒和 1 秒\n4、创建两个任务对象\n5、将任务对象作为参数，asyncio.gather 方法创建任务收集器。注意，asyncio.gather 方法中参数的顺序决定了协程的启动顺序\n6、将任务收集器作为参数传入事件循环的 run_until_complete 方法，阻塞运行，直到全部任务完成\n7、任务结束后，事件循环停止，打印任务的 result 方法返回值，即协程函数的 return 值\n到这一步，大家应该可以看得出，上面的代码已经是异步编程的结构了，在事件循环内部，两个协程是交替运行完成的。简单叙述一下程序协程部分的运行过程：\n-\u0026gt; 首先运行 task1\n-\u0026gt; 打印 [corowork] Start coroutine ONE\n-\u0026gt; 遇到 asyncio.sleep 阻塞\n-\u0026gt; 释放 CPU 转到 task2 中执行\n-\u0026gt; 打印 [corowork] Start coroutine TWO\n-\u0026gt; 再次遇到 asyncio.sleep 阻塞\n-\u0026gt; 这次没有其它协程可以运行了，只能等阻塞结束\n-\u0026gt; task2 的阻塞时间较短，阻塞 1 秒后先结束，打印 [corowork] Stop coroutine TWO\n-\u0026gt; 又过了 2 秒，阻塞 3 秒的 task1 也结束了阻塞，打印 [corowork] Stop coroutine ONE\n-\u0026gt; 至此两个任务全部完成，事件循环停止\n-\u0026gt; 打印两个任务的 result\n-\u0026gt; 打印程序运行时间\n-\u0026gt; 程序全部结束\nawait的理解 从上文中也可以看到await其实是从yield from中转变过来的，当在代码中看到await时，可以知道当前协程要去运行await后面的任务，此时控制权回到了event loop手中，去执行其它的任务，当前面的任务完成了以后，则转去执行前面await后面的代码。注意当await直接跟一个coroutline时，此时相当于去yield from，会卡在那里，并不会实现真正的异步，所以要先将coroutline变为task或者future就可以直接await。 ## 异步编程\n一个买土豆的例子 import asyncio import random # potato类 class Potato: # 生成土豆 @classmethod def make(cls, num, *args, **kws): potatos = [] for i in range(num): potatos.append(cls.__new__(cls, *args, **kws)) return potatos all_potatos = Potato.make(5) ## 这是一个异步生成器，可以用async for迭代，nums为想买的数量。 async def take_photos(nums): count = 0 while True: # 如果没有土豆了，挂起当前任务请求生成土豆任务。 if len(all_potatos) == 0: await askfor_photos() else: photo = all_potatos.pop() # 如果有土豆将土豆抛出去 yield photo count += 1 if count == nums : break async def askfor_photos(): await asyncio.sleep(2) all_potatos.append(Potato.make(5)) async def buy_photos(): bucket = [] async for p in take_photos(50): bucket.append(p) print(f\u0026quot;Go photo {id(p)}\u0026quot;) loop = asyncio.get_event_loop() loop.run_until_complete(buy_photos()) requests例子 import asyncio import requests import time # 相当于委托生成器 async def result(url): res = await request_url(url) print(url, res) # 相当于子生成器 async def request_url(url): res = requests.get(url) print(url) await asyncio.sleep(2) print(\u0026quot;execute_time:\u0026quot;, time.time() - start) return res url_list = [\u0026quot;https://www.csdn.net/\u0026quot;, \u0026quot;https://vllbc.top/\u0026quot;, \u0026quot;https://www.baidu.com/\u0026quot;, ] # 以下相当于调用方 start = time.time() print(f\u0026quot;start_time:{start}\\n\u0026quot;) task = [result(url) for url in url_list] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(task)) endtime = time.time() - start print(\u0026quot;\\nendtime:\u0026quot;, time.time()) print(\u0026quot;all_execute_time:\u0026quot;, endtime) ","date":"2021-06-13T00:00:00Z","permalink":"/asyncio/","title":"asyncio"},{"content":"移除元素 还是以前刷过的题 https://leetcode-cn.com/problems/remove-element/\n以前的思路早忘了 然后我重新做了一下，一开始就一行代码\nclass Solution: def removeElement(self, nums: List[int], val: int) -\u0026gt; int: return len(list(filter(lambda x:x!=val,nums))) ​ 然后发现输出和正确输出不一样。于是看了了下面的提示，然后改了改\nclass Solution: def removeElement(self, nums: List[int], val: int) -\u0026gt; int: for i in range(nums.count(val)): nums.remove(val) return len(nums) 但这算不上叫算法，利用双指针做法如下：\nclass Solution: def removeElement(self, nums: List[int], val: int) -\u0026gt; int: left = 0 for i in range(len(nums)): if nums[i] != val: nums[left], nums[i] = nums[i], nums[left] left += 1 return left ","date":"2021-06-12T00:00:00Z","permalink":"/%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0/","title":"移除元素"},{"content":"参考：https://blog.csdn.net/asialee_bird/article/details/96894533\nTextRank算法是一种基于图的用于关键词抽取和文档摘要的排序算法，由谷歌的网页重要性排序算法PageRank算法改进而来，它利用一篇文档内部的词语间的共现信息(语义)便可以抽取关键词，它能够从一个给定的文本中抽取出该文本的关键词、关键词组，并使用抽取式的自动文摘方法抽取出该文本的关键句。\nPageRank算法 PageRank算法通过计算网页链接的数量和质量来粗略估计网页的重要性，算法创立之初即应用在谷歌的搜索引擎中，对网页进行排名。\nPageRank算法的核心思想如下：\n（1）链接数量：如果一个网页被越多的其他网页链接，说明这个网页越重要，即该网页的PR值（PageRank值）会相对较高；\n（2）链接质量：如果一个网页被一个越高权值的网页链接，也能表明这个网页越重要，即一个PR值很高的网页链接到一个其他网页，那么被链接到的网页的PR值会相应地因此而提高。\nTextRank算法 TextRank算法的基本思想是将文档看作一个词的网络，该网络中的链接表示词与词之间的语义关系。\nTextRank算法主要包括：关键词抽取、关键短语抽取、关键句抽取。\n（1）关键词抽取（keyword extraction） 关键词抽取是指从文本中确定一些能够描述文档含义的术语的过程。对关键词抽取而言，用于构建顶点集的文本单元可以是句子中的一个或多个字；根据这些字之间的关系（比如：在一个框中同时出现）构建边。根据任务的需要，可以使用语法过滤器（syntactic filters）对顶点集进行优化。语法过滤器的主要作用是将某一类或者某几类词性的字过滤出来作为顶点集。\n（2）关键短语抽取（keyphrase extration） 关键词抽取结束后，我们可以得到的N个关键词，在原始文本中相邻的关键词构成关键短语。因此，从get_keyphrases函数的源码中我们可以看到，它先调用get_keywords抽取关键词，然后分析关键词是否存在相邻的情况，最后确定哪些是关键短语。\n（3）关键句抽取（sentence extraction） 句子抽取任务主要针对的是自动摘要这个场景，将每一个sentence作为一个顶点，根据两个句子之间的内容重复程度来计算他们之间的“相似度”，以这个相似度作为联系，由于不同句子之间相似度大小不一致，在这个场景下构建的是以相似度大小作为edge权重的有权图。\n","date":"2021-05-20T00:00:00Z","permalink":"/textrank/","title":"TextRank"},{"content":"matplotlib.pyplot学习 绘图标记 import matplotlib.pyplot as plt import numpy as np ypoints = np.array([1,3,4,5,8,9,6,1,3,4,5,2,4]) plt.plot(ypoints, marker = \u0026#39;o\u0026#39;) # \u0026quot;o\u0026quot;代表实心圆 plt.show() maker可用的符号如下：\nfmt参数 fmt = \u0026#39;[marker][line][color]\u0026#39; 例如 o:r，o 表示实心圆标记，: 表示虚线，r 表示颜色为红色。\n线类型 线的类型可以使用 linestyle 参数来定义，简写为 ls。\n线的宽度 线的宽度可以使用 linewidth 参数来定义，简写为 lw，值可以是浮点数，如：1、2.0、5.67 等。\n颜色类型 线的颜色 线的颜色可以使用 color 参数来定义，简写为 c。\n标记大小与颜色 我们可以自定义标记的大小与颜色，使用的参数分别是：\nmarkersize，简写为 ms：定义标记的大小。 markerfacecolor，简写为 mfc：定义标记内部的颜色。 markeredgecolor，简写为 mec：定义标记边框的颜色。 import matplotlib.pyplot as plt import numpy as np ypoints = np.array([6, 2, 13, 10]) plt.plot(ypoints, marker = \u0026#39;o\u0026#39;, ms = 20) plt.show() Matplotlib 轴标签和标题 我们可以使用 xlabel() 和 ylabel() 方法来设置 x 轴和 y 轴的标签。\nimport numpy as np import matplotlib.pyplot as plt x = np.array([1, 2, 3, 4]) y = np.array([1, 4, 9, 16]) plt.plot(x, y) plt.xlabel(\u0026quot;x - label\u0026quot;) plt.ylabel(\u0026quot;y - label\u0026quot;) plt.show() 标题 我们可以使用 title() 方法来设置标题\n也可以自定义字体样式，通过传入fontdict参数\nimport matplotlib.pyplot as plt import numpy as np font = {\u0026quot;color\u0026quot;:\u0026quot;blue\u0026quot;,\u0026quot;size\u0026quot;:20} ypoints = np.array([1,3,4,5,8,9,6,1,3,4,5,2,4]) plt.plot(ypoints,marker=\u0026quot;o\u0026quot;,color=\u0026quot;r\u0026quot;,linestyle=\u0026quot;-.\u0026quot;) plt.title(\u0026quot;test\u0026quot;,fontdict=font) plt.xlabel(\u0026quot;x\u0026quot;,fontdict=font) plt.ylabel(\u0026quot;y\u0026quot;,fontdict=font) plt.show() Matplotlib 网格线 我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。\ngrid() 方法语法格式如下：\nmatplotlib.pyplot.grid(b=None, which=\u0026#39;major\u0026#39;, axis=\u0026#39;both\u0026#39;, ) 参数说明：\nb：可选，默认为 None，可以设置布尔值，true 为显示网格线，false 为不显示，如果设置 **kwargs 参数，则值为 true。 which：可选，可选值有 ‘major’、‘minor’ 和 ‘both’，默认为 ‘major’，表示应用更改的网格线。 axis：可选，设置显示哪个方向的网格线，可以是取 ‘both’（默认），‘x’ 或 ‘y’，分别表示两个方向，x 轴方向或 y 轴方向。 **kwargs：可选，设置网格样式，可以是 color=‘r’, linestyle=‘-’ 和 linewidth=2，分别表示网格线的颜色，样式和宽度。 以下实例添加一个简单的网格线，并设置网格线的样式，格式如下：\ngrid(color = \u0026#39;color\u0026#39;, linestyle = \u0026#39;linestyle\u0026#39;, linewidth = number) 参数说明：\ncolor：’b’ 蓝色，‘m’ 洋红色，‘g’ 绿色，‘y’ 黄色，‘r’ 红色，‘k’ 黑色，‘w’ 白色，‘c’ 青绿色，‘#008000’ RGB 颜色符串。\nlinestyle：’‐’ 实线，‘‐‐’ 破折线，‘‐.’ 点划线，‘:’ 虚线。\nlinewidth：设置线的宽度，可以设置一个数字。\nMatplotlib 绘制多图 我们可以使用 pyplot 中的 subplot() 和 subplots() 方法来绘制多个子图。\nsubpot() 方法在绘图时需要指定位置，subplots() 方法可以一次生成多个，在调用时只需要调用生成对象的 ax 即可。\nsubplot subplot(nrows, ncols, index, **kwargs) subplot(pos, **kwargs) subplot(**kwargs) subplot(ax) 以上函数将整个绘图区域分成 nrows 行和 ncols 列，然后从左到右，从上到下的顺序对每个子区域进行编号 1…N ，左上的子区域的编号为 1、右下的区域编号为 N，编号可以通过参数 index 来设置。\n设置 numRows ＝ 1，numCols ＝ 2，就是将图表绘制成 1x2 的图片区域, 对应的坐标为：\n(1, 1), (1, 2) 设置 numRows ＝ 2，numCols ＝ 2，就是将图表绘制成 2x2 的图片区域, 对应的坐标为：\n(1, 1), (1, 2) (2, 1), (2, 2) subplots() subplots() 方法语法格式如下：\nmatplotlib.pyplot.subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, **fig_kw) 参数说明：\nnrows：默认为 1，设置图表的行数。 ncols：默认为 1，设置图表的列数。 sharex、sharey：设置 x、y 轴是否共享属性，默认为 false，可设置为 ‘none’、‘all’、‘row’ 或 ‘col’。 False 或 none 每个子图的 x 轴或 y 轴都是独立的，True 或 ‘all’：所有子图共享 x 轴或 y 轴，‘row’ 设置每个子图行共享一个 x 轴或 y 轴，‘col’：设置每个子图列共享一个 x 轴或 y 轴。 squeeze：布尔值，默认为 True，表示额外的维度从返回的 Axes(轴)对象中挤出，对于 N1 或 1N 个子图，返回一个 1 维数组，对于 N*M，N\u0026gt;1 和 M\u0026gt;1 返回一个 2 维数组。如果设置为 False，则不进行挤压操作，返回一个元素为 Axes 实例的2维数组，即使它最终是1x1。 subplot_kw：可选，字典类型。把字典的关键字传递给 add_subplot() 来创建每个子图。 gridspec_kw：可选，字典类型。把字典的关键字传递给 GridSpec 构造函数创建子图放在网格里(grid)。 **fig_kw：把详细的关键字参数传给 figure() 函数。 常用技巧\nx = np.linspace(0, 2*np.pi, 400) y = np.sin(x**2) fig, axs = plt.subplots(2, 2) for i in axs.flatten(): # axs.flatten()将二维数组变为一维，方便循环。 i.plot(x, y) Matplotlib 散点图 我们可以使用 pyplot 中的 scatter() 方法来绘制散点图。\nscatter() 方法语法格式如下：\nmatplotlib.pyplot.scatter(x, y, s=None, c=None, marker=None, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, *, edgecolors=None, plotnonfinite=False, data=None, **kwargs) 参数说明：\nx，y：长度相同的数组，也就是我们即将绘制散点图的数据点，输入数据。\ns：点的大小，默认 20，也可以是个数组，数组每个参数为对应点的大小。\nc：点的颜色，默认蓝色 ‘b’，也可以是个 RGB 或 RGBA 二维行数组。\nmarker：点的样式，默认小圆圈 ‘o’。\ncmap：Colormap，默认 None，标量或者是一个 colormap 的名字，只有 c 是一个浮点数数组的时才使用。如果没有申明就是 image.cmap。\nnorm：Normalize，默认 None，数据亮度在 0-1 之间，只有 c 是一个浮点数的数组的时才使用。\nvmin，vmax：：亮度设置，在 norm 参数存在时会忽略。\nalpha：：透明度设置，0-1 之间，默认 None，即不透明。\nlinewidths：：标记点的长度。\nedgecolors：：颜色或颜色序列，默认为 ‘face’，可选值有 ‘face’, ‘none’, None。\nplotnonfinite：：布尔值，设置是否使用非限定的 c ( inf, -inf 或 nan) 绘制点。\n**kwargs：：其他参数。\n设置颜色条需要使用 cmap 参数，默认值为 ‘viridis’，之后颜色值设置为 0 到 100 的数组。\n如果要显示颜色条，需要使用 plt.colorbar() 方法：\nMatplotlib 柱形图 我们可以使用 pyplot 中的 bar() 方法来绘制柱形图。\nbar() 方法语法格式如下：\nmatplotlib.pyplot.bar(x, height, width=0.8, bottom=None, *, align=\u0026#39;center\u0026#39;, data=None, **kwargs) 参数说明：\nx：浮点型数组，柱形图的 x 轴数据。\nheight：浮点型数组，柱形图的高度。\nwidth：浮点型数组，柱形图的宽度。\nbottom：浮点型数组，底座的 y 坐标，默认 0。\nalign：柱形图与 x 坐标的对齐方式，‘center’ 以 x 位置为中心，这是默认值。 ‘edge’：将柱形图的左边缘与 x 位置对齐。要对齐右边缘的条形，可以传递负数的宽度值及 align=‘edge’。\n**kwargs：：其他参数。\n垂直方向的柱形图可以使用 barh() 方法来设置：\nMatplotlib 饼图 我们可以使用 pyplot 中的 pie() 方法来绘制饼图。\npie() 方法语法格式如下：\nmatplotlib.pyplot.pie(x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=0, radius=1, counterclock=True, wedgeprops=None, textprops=None, center=0, 0, frame=False, rotatelabels=False, *, normalize=None, data=None)[source] 参数说明：\nx：浮点型数组，表示每个扇形的面积。\nexplode：数组，表示各个扇形之间的间隔，默认值为0。\nlabels：列表，各个扇形的标签，默认值为 None。\ncolors：数组，表示各个扇形的颜色，默认值为 None。\nautopct：设置饼图内各个扇形百分比显示格式，%d%% 整数百分比，%0.1f 一位小数， %0.1f%% 一位小数百分比， %0.2f%% 两位小数百分比。\nlabeldistance：标签标记的绘制位置，相对于半径的比例，默认值为 1.1，如 \u0026lt;1则绘制在饼图内侧。\npctdistance：：类似于 labeldistance，指定 autopct 的位置刻度，默认值为 0.6。\nshadow：：布尔值 True 或 False，设置饼图的阴影，默认为 False，不设置阴影。\nradius：：设置饼图的半径，默认为 1。\nstartangle：：起始绘制饼图的角度，默认为从 x 轴正方向逆时针画起，如设定 =90 则从 y 轴正方向画起。\ncounterclock：布尔值，设置指针方向，默认为 True，即逆时针，False 为顺时针。\nwedgeprops ：字典类型，默认值 None。参数字典传递给 wedge 对象用来画一个饼图。例如：wedgeprops={‘linewidth’:5} 设置 wedge 线宽为5。\ntextprops ：字典类型，默认值为：None。传递给 text 对象的字典参数，用于设置标签（labels）和比例文字的格式。\ncenter ：浮点类型的列表，默认值：(0,0)。用于设置图标中心位置。\nframe ：布尔类型，默认值：False。如果是 True，绘制带有表的轴框架。\nrotatelabels ：布尔类型，默认为 False。如果为 True，旋转每个 label 到指定的角度。\n","date":"2021-05-02T00:00:00Z","permalink":"/pyplot/","title":"pyplot"},{"content":"范数的定义 \\[ {\\lVert x \\rVert}_p := \\left(\\sum_{i=1}^n{\\lvert x_i\\rvert}^p\\right)^{\\frac{1}{p}} \\]\n标准化与归一化 定义 归一化和标准化都是对数据做变换的方式，将原始的一列数据转换到某个范围，或者某种形态。 \u0026gt;归一化：将一列数据变化到某个固定区间(范围)中，通常，这个区间是[0, 1]，广义的讲，可以是各种区间，比如映射到[0，1]一样可以继续映射到其他范围，图像中可能会映射到[0,255]，其他情况可能映射到[-1,1]；\n标准化：将数据变换为均值为0，标准差为1的分布。切记，并非一定是正态的；\n中心化：还有一种处理叫做中心化，也叫零均值处理，就是将每个原始数据减去这些数据的均值。\n差异 归一化会严格限定变换后的数据的范围，标准化没有严格的区间，变换后的数据没有范围，只是其均值为0，标注差为1. 归一化对数据的缩放比例只与极值有关，而标准化缩放比例与所有的数据都有关。 标准化、归一化的好处 统计建模中，如回归模型，自变量\\(X\\)的量纲不一致导致了回归系数无法直接解读或者错误解读；需要将\\(X\\)都处理到统一量纲下，这样才可比； 机器学习任务和统计学任务中有很多地方要用到“距离”的计算，比如PCA，比如KNN，比如kmeans等等，假使算欧式距离，不同维度量纲不同可能会导致距离的计算依赖于量纲较大的那些特征而得到不合理的结果； 参数估计时使用梯度下降，在使用梯度下降的方法求解最优化问题时， 归一化/标准化后可以加快梯度下降的求解速度，即提升模型的收敛速度。 决策边界 所谓决策边界就是能够把样本正确分类的一条边界，主要有线性决策边界(linear decision boundaries)和非线性决策边界(non-linear decision boundaries)。注意：决策边界是假设函数的属性，由参数决定，而不是由数据集的特征决定。下面主要举一些例子，形象化的来说明线性决策边界和非线性决策边界。\n方差与偏差 当我们的模型表现不佳时，通常是出现两种问题，一种是 高偏差 问题，另一种是 高方差 问题。识别它们有助于选择正确的优化方式，所以我们先来看下 偏差 与 方差 的意义。\n偏差: 描述模型输出结果的期望与样本真实结果的差距。 方差: 描述模型对于给定值的输出稳定性。 就像打靶一样，偏差描述了我们的射击总体是否偏离了我们的目标，而方差描述了射击准不准。接下来让我们通过各种情况下 训练集 和 交叉验证集 的 误差 曲线来直观地理解 高偏差 与 高方差 的意义。\n对于 多项式回归，当次数选取较低时，我们的 训练集误差 和 交叉验证集误差 都会很大；当次数选择刚好时，训练集误差 和 交叉验证集误差 都很小；当次数过大时会产生过拟合，虽然 训练集误差 很小，但 交叉验证集误差 会很大（ 关系图如下 ）\n对于 正则化 参数，使用同样的分析方法，当参数比较小时容易产生过拟合现象，也就是高方差问题。而参数比较大时容易产生欠拟合现象，也就是高偏差问题。\n偏差和方差与数据噪声之和就是模型的泛化能力 模型的期望预测（这里x指所有的样本，期望预测为该模型的所有预测结果的期望。也可以表示有多个模型同时对x一个样本进行预测，期望预测为所有模型预测的期望）： 样本数相同的不同训练集产生的方差（可以理解为测试集预测结果与训练集输出期望之间的方差，也可以直接理解为一个模型中所有的预测与预测期望之间的平方差）： 噪声（这里的噪声为人工标注的错误。）： 期望输出与真实标记的差别称为偏差(也有两种理解，一种是多模型的预测期望与真实值之间的偏差，还有一种就直接是单模型的预测输出（因为单模型的预测期望就是它的输出了）与真实值之间的平方差就可以记为偏差的平方，其实这里应理解为多模型的情况\n泛化误差也就是期望风险。 参考 参考1 参考2\n改进策略 [高方差] 采集更多的样本数据，(参考正规方程) [高方差] 减少特征数量，去除非主要的特征 [高偏差] 引入更多的相关特征 [高偏差] 采用多项式特征 [高偏差] 减小正则化参数 λ [高方差] 增加正则化参数 λ 学习曲线 无论你是要检查你的学习算法是否正常工作或是要改进算法的表现，学习曲线 都是一个十分直观有效的工具。学习曲线 的横轴是样本数，纵轴为 训练集 和 交叉验证集 的 误差\n","date":"2021-05-01T00:00:00Z","permalink":"/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%9A%84%E4%B8%80%E4%BA%9B%E6%A6%82%E5%BF%B5/","title":"机器学习的一些概念"},{"content":"Softmax理解 主要记录了在使用softmax这个函数中遇到的一些问题，比较基础，但确实困扰了一段时间。\n在学习word2vec中, 使用的一般都是如下的损失函数：\n\\[ \\begin{aligned} Loss = J(\\theta) = -\\frac{1}{T}logL(\\theta)= -\\frac{1}{T}\\sum_{t=1}^T\\sum_{-m\\leq j\\leq m, j\\neq0} logP(w_{t+j}|w_t, \\theta) \\\\\\\\ =\\frac{1}{T}\\sum_{t=1}^T\\sum_{-m\\leq j\\leq m, j\\neq0}J_{t,j}(\\theta). \\end{aligned} \\]\n\\[ \\begin{aligned} J_{t,j}(\\theta) = -logP(miss|xwh) = -log\\frac{exp(u_{miss}^Tv_{xwh})}{\\sum_{o\\in V}exp(u_o^Tv_{xwh})} = \\\\\\\\ -u_{miss}^Tv_{xwh}+log\\sum_{o\\in V} exp(u_o^Tv_{xwh}) \\end{aligned} \\]\n但是说起交叉熵往往是下面的式子：\n\\[ L = -\\sum_{c=1}^Cy_clog(p_c) \\]\n在学习的时候就疑惑，这两种形式有什么区别与联系呢，最近看到一篇文章正好解答了这个疑惑。 下面给出结论： 第一种形式是只针对正确类别的对应点输出，将这个位置的softmax即概率最大化，而第二种形式是直接衡量真实分布和实际输出之间的距离，因为交叉熵就是由KL散度变形得来的。\n交叉熵 信息量 一条信息的信息量大小和它的不确定性有很大的关系。一句话如果需要很多外部信息才能确定，我们就称这句话的信息量比较大。 将信息的定义为下：\n\\(I(x_0) = -log(p(x_0))\\)\n熵 信息量是对于单个事件来说的，但是实际情况一件事有很多种发生的可能，比如掷骰子有可能出现6种情况，明天的天气可能晴、多云或者下雨等等。熵是表示随机变量不确定的度量，是对所有可能发生的事件产生的信息量的期望。公式如下：\n\\[ H(X) = -\\sum_{i=1}^np(x_i)log(p(x_i)) \\] ### 相对熵 相对熵也称之为KL散度，用于衡量对于同意随机变量x的两个分布p(x)和q(x)之间的差异。在机器学习中，p(x)通常描述样本的真实分布，例如[1, 0, 0, 0]表示样本属于第一类，而q(x)常常用于表示预测的分布，例如[0.7, 0.1, 0.1, 0.1] KL散度的定义公式如下：\n\\[ D_{KL}(p||q) = \\sum_{i=1}^np(x_i)log(\\frac{p(x_i)}{q(x_i)}) \\]\nKL越小则说明二者越接近 ### 交叉熵 将KL散度变形\n\\[ D_{KL}(p||q) = \\sum_{i=1}^np(x_i)log(p(x_i)) -\\sum_{i=1}^np(x_i)log(q(x_i)) = -H(p(x)) - \\sum_{i=1}^np(x_i)log(q(x_i)) \\]\n后半部分就是我们的交叉熵，常常用于评估predict和label之间的差别。 ## 理解 以一个三分类的问题来说，假设真实分布为[0, 1, 0]，则对于第二个式子来说\n\\[ L = -\\sum_{c=1}^Cy_clog(p_c) = -0 \\times log(p_0) - 1\\times log(p_1) - 0\\times log(p_3) = -log(p_1) \\]\n对于第一个式子就是\n\\[ loss_1 = -log(\\frac{e^{z_1}}{\\sum_{c=1}^C e^{z_c}}) = -log(p_1) = -z_1+log\\sum_{c=1}^Ce^{z_c} \\]\n所以说实际上这俩是一样的，只是出发点不一样。在skip-gram中，使用的就是第一种式子，对它来说，正确的类别就是背景词，就直接将背景词的概率最大，即损失函数最小。\nloss = (-output_layer[:, Y] + torch.log(torch.sum(torch.exp(output_layer), dim=1))).mean()\n这行代码就是对第一个式子的实现，需要注意的是所有的操作都是基于batch的。负采样的形式有一些不同，这里不再讨论，到这里我才终于稍稍理解了softmax这个函数。\n与二分类的联系 具体的说二分类就是两个类别，可以使用上述的方法定义，对于正类和负类都用不同的概率表示，也可以只计算正类，负类就等于1-正类，上述出现的y都是向量的形式，对于二分类，向量的长度就是2，就可以直接展开，最后的结果就是熟知的损失函数的形式。\n参考 https://zhuanlan.zhihu.com/p/105722023\n","date":"2021-04-30T00:00:00Z","permalink":"/%E4%BA%A4%E5%8F%89%E7%86%B5%E6%8D%9F%E5%A4%B1%E5%87%BD%E6%95%B0/","title":"交叉熵损失函数"},{"content":"验证二叉搜索树 https://leetcode-cn.com/problems/validate-binary-search-tree/\n# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def isValidBST(self, root: TreeNode) -\u0026gt; bool: return self.search(root,-(232),232) def search(self,root,mins,maxs): if root == None: return True if root.val \u0026gt; mins and root.val \u0026lt; maxs: pass else: return False return all([self.search(root.left,mins,root.val),self.search(root.right,root.val,maxs)]) 最后用了个all 也是简洁了代码\n","date":"2021-04-22T00:00:00Z","permalink":"/%E9%AA%8C%E8%AF%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/","title":"验证二叉搜索树"},{"content":"回看博客，发现深度学习的笔记空荡荡，才发觉一直没有详细得进行笔记，但也感觉确实没有什么可以记录的东西，都是一些网络和模型，具体的trick倒是记录了一些，不过为了博客的美观，还是写一些东西吧，最主要的还是一些小知识。 (MLP就不记录了)\nRNN其实很类似于一个自回归模型，用历史观测预测下一个观测，我们可以假设不适用所有的历史预测下一个，而是部分的历史，也就是说序列满足马尔科夫条件，而RNN，即循环神经网络，就符合一阶马尔科夫模型，即当前观测仅仅与前一个的观测有关，模型图如下：\n","date":"2021-04-21T00:00:00Z","permalink":"/rnn/","title":"RNN"},{"content":"范围+文本对象 范围 内部：i ，意指 inner 外部：a ，英文单词 a，一个的意思 文本对象 ( 或 ) ：一对 () b ：一对 () { 或 } ：一对 {} B ：一对 {} [ 或 ] ：一对 [] \u0026lt; 或 \u0026gt; ：一对 \u0026lt;\u0026gt; t ：tag （HTML 或 XML）标签 ' 或 ' ：一对 '' \" 或 \" ：一对 \"\" ` 或 ` ：一对 `` w ：一个单词 s ：一个句子；以 . ! ? 结尾即为一个句子 p ：一个段落；以一个换行符间隔即为一个段落(一般用于对函数体操作) 比如i'代表'内部，iw代表光标所在单词，注意到，加上了范围i或者a，则原本的w含义也发生了变化，注意这是两个东西，原本的w为一个移动，而这里的w代表文本对象即单词。\nmove some easy hjkl，左下上右 wbeWBE，单词级别 0和$，行首和行尾 ^和g _，行首和行尾，但不算空白字符 H和L，页面相关，基本就是映射到^和g _ %括号匹配 search 光标移到要搜索的单词，*向下查找，#向上查找 /向下模糊搜索，?向上模糊搜索，配合n和N f + 字符：自左往右移动光标到下一个匹配的字符中 F + 字符：自右往左移动光标到下一个匹配的字符中 t + 字符：自左往右移动光标到下一个匹配的字符的前一个字符中 T + 字符：自右往左移动光标到下一个匹配的字符的后一个字符中 ;：重复执行上一个搜索命令 ,： 与上一个命令方向相反地执行上一个搜索命令 动词 some easy d: 删除 c: 删除并进入insert模式 y: 复制 ys, ds, cs，vim-surround插件，对应添加括号，删除括号，修改括号 .重复上一个命令 u撤销上条命令 p粘贴剪切板内容 r替换 g+？整理 g : 定位到上次编辑位置 g c 注释 g h 弹出变量或者函数的详细信息 gu和gU 小写或大写，g u u和g U U整行小写大写 g b 多选相同的word gg文件首，G文件尾 3G或3gg或:3，跳到第3行 数量词 1234567...表示命令执行的次数。 结合起来吧 动词 + move d h，删除前一个字符 d w，删除字符到下一个单词开头 d tV，删除字符到下一个v字符前面 d 2fb，删除字符到第二个b，且包括这个b，等同于2 dfb 动词 + （范围） +文本对象 ys w'，给当前到下一个单词前添加'号 ds '，删除包裹的符号 cs [(，将[]修改为() yssb，整行添加括号，重复两次代表对行操作，b在前面介绍说了是文本对象，代表括号。 ysiw'，出现了两个文本对象，第一个与前面的i结合为iw代表单词，第二个文本对象代表即位要添加的引号，注意和第一条的区别，w所代表的不同。 gUw，大写直到下一个单词前，是动词+move，gUiw，大写光标所在单词，是动词+范围+文本对象。 ","date":"2021-04-20T00:00:00Z","permalink":"/vim/","title":"vim"},{"content":"L1 Loss 也称为Mean Absolute Error，即平均绝对误差（MAE），它衡量的是预测值与真实值之间距离的平均误差幅度，作用范围为0到正无穷。\n优点： 对离群点（Outliers）或者异常值更具有鲁棒性。\n缺点： 由图可知其在0点处的导数不连续，使得求解效率低下，导致收敛速度慢；而对于较小的损失值，其梯度也同其他区间损失值的梯度一样大，所以不利于网络的学习。\nL2 Loss 也称为Mean Squred Error，即均方差（MSE），它衡量的是预测值与真实1值之间距离的平方和，作用范围同为0到正无穷。\n优点： 收敛速度快，能够对梯度给予合适的惩罚权重，而不是“一视同仁”，使梯度更新的方向可以更加精确。\n缺点： 对异常值十分敏感，梯度更新的方向很容易受离群点所主导，不具备鲁棒性。\n提一嘴，真的不理解鲁棒性这个翻译的含义，后来才知道是音译，真的是，毒害了多少人。。理解成稳定性就行。\nSmooth L1 Loss 以上两种方法都有各自的优缺点，大部分的情况下其实二者都不使用，因此需要新的损失函数。即平滑的L1损失（SLL)。\nSLL通过综合L1和L2损失的优点，在0点处附近采用了L2损失中的平方函数，解决了L1损失在0点处梯度不可导的问题，使其更加平滑易于收敛。此外，在|x|\u0026gt;1的区间上，它又采用了L1损失中的线性函数，使得梯度能够快速下降。\n通过对这三个损失函数进行求导可以发现，L1损失的导数为常数，如果不及时调整学习率，那么当值过小时，会导致模型很难收敛到一个较高的精度，而是趋向于一个固定值附近波动。反过来，对于L2损失来说，由于在训练初期值较大时，其导数值也会相应较大，导致训练不稳定。最后，可以发现Smooth L1在训练初期输入数值较大时能够较为稳定在某一个数值，而在后期趋向于收敛时也能够加速梯度的回传，很好的解决了前面两者所存在的问题。\n","date":"2021-04-19T00:00:00Z","permalink":"/smooth-l1-loss/","title":"Smooth L1 Loss"},{"content":"无重叠区间 https://leetcode-cn.com/problems/non-overlapping-intervals/\n利用了贪心 移除的数目就是总数目减去条件成立的数目\nclass Solution: def eraseOverlapIntervals(self, intervals: List[List[int]]) -\u0026gt; int: if len(intervals) == 0: return 0 res = 0 mins = -float(\u0026quot;inf\u0026quot;) for i in sorted(intervals,key=lambda i:i[1]): if i[0] \u0026gt;= mins: res += 1 mins = i[1] return len(intervals) - res 注意是根据end进行排序的，引用别人的解释@HONGYANG\n比如你一天要参加几个活动，这个活动开始的多早其实不重要，重要的是你结束的多早，早晨7点就开始了然后一搞搞一天，那你今天也就只能参加这一个活动；但如果这个活动开始的不早，比如9点才开始，但是随便搞搞10点就结束了，那你接下来就还有大半天的时间可以参加其他活动。\n这就是为啥要着眼于end，而不是start。\n贪心就是考虑当前最优解\n","date":"2021-04-16T00:00:00Z","permalink":"/%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4/","title":"无重叠区间"},{"content":"对角线遍历 题目： ​ https://leetcode-cn.com/problems/diagonal-traverse/\n思路： ​ 每个对角线的两索引之和是一样的\n代码： class Solution: def findDiagonalOrder(self, matrix: List[List[int]]) -\u0026gt; List[int]: if not matrix: return [] hashs = collections.defaultdict(list) row, col = len(matrix), len(matrix[0]) for i in range(row): for j in range(col): hashs[j + i].append(matrix[i][j]) res = [] flag = True for k, v in sorted(hashs.items()): if flag: res.extend(v[::-1]) else: res.extend(v) flag = not flag return res 注意flag的作用\n","date":"2021-04-13T00:00:00Z","permalink":"/%E5%AF%B9%E8%A7%92%E7%BA%BF%E9%81%8D%E5%8E%86/","title":"对角线遍历"},{"content":"主成分分析(PCA) 主成分分析（Principal components analysis，以下简称PCA）是最重要的降维方法之一。在数据压缩消除冗余和数据噪音消除等领域都有广泛的应用。注意的是PCA属于无监督学习。\nPCA降维的原则是投影方差最大。\n使用PCA时如果有不同种类的数据，PCA会把这些数据混合在一起降维。\nPCA顾名思义，就是找出数据里最主要的方面，用数据里最主要的方面来代替原始数据。具体的，假如我们的数据集是n维的，共有m个数据(x(1),x(2),…,x(m))。我们希望将这m个数据的维度从n维降到n’维，希望这m个n’维的数据集尽可能的代表原始数据集。我们知道数据从n维降到n’维肯定会有损失，但是我们希望损失尽可能的小。那么如何让这n’维的数据尽可能表示原来的数据呢？\n协方差矩阵 在统计学中，方差是用来度量单个随机变量的离散程度，而协方差则一般用来刻画两个随机变量的相似程度，其中，方差的计算公式为:\n\\[ \\sigma_x^2 = \\frac{1}{n}\\sum_{i=1}^n(x-\\bar{x})^2 \\]\n协方差的公式为:\n\\[ \\sigma(x,y) = \\frac{1}{n-1}\\sum_{i=1}^n(x_i-\\bar{x})(y_i-\\bar{y}) \\]\n根据方差的定义，给定d个随机变量\\(x_k,k=1,2,\\dots,d\\)，这些随机变量的方差为\n\\[ \\sigma(x_k,x_k) = \\frac{1}{n-1}\\sum_{i=1}^n(x_{ki}-\\bar{x_k})^2,k=1,2,\\dots,d \\]\n因此可以求出两两之间的协方差\n\\[ \\sigma(x_m,x_k) = \\frac{1}{n-1}\\sum_{i=1}^n(x_{mi}-\\bar{x_m})(x_{ki}-\\bar{x_k}) \\]\n因此，协方差矩阵为\n\\[ \\sum = \\begin{bmatrix} \\sigma(x_1,x_1) \u0026amp; \\cdots \u0026amp; \\sigma(x_1,x_d) \\\\\\\\ \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\\\\\ \\sigma(x_d,x_1) \u0026amp; \\cdots \u0026amp; \\sigma(x_d,x_d) \\end{bmatrix} \\]\n拉格朗日乘数法优化 设原始数据矩阵 X 对应的协方差矩阵为 C，而 P 是一组基按行组成的矩阵，设 Y=PX，则 Y 为 X 对 P 做基变换后的数据。设 Y 的协方差矩阵为 D，我们推导一下 D 与 C 的关系：\n\\[ D = \\frac{1}{m}YY^T \\\\\\\\ =\\frac{1}{m}(PX)(PX)^T \\\\\\\\ =\\frac{1}{m}PXX^TP^T \\\\\\\\ =P(\\frac{1}{m}XX^T)P^T \\\\\\\\ =PCP^T \\]\n我们令P=\\(w^T\\),令原本的协方差为A，于是我们有优化目标如下：\n\\[ \\begin{cases} \\max{w^TAw} \\\\\\\\ s.t. w^Tw = 1 \\end{cases} \\]\n然后构造拉格朗日函数：\n\\[ L(w) = w^TAw + \\lambda(1-w^Tw) \\]\n对w求导：\n\\[ Aw = \\lambda w \\]\n则方差\\(D(x) = w^TAw = \\lambda w^Tw = \\lambda\\)\n于是我们发现，x 投影后的方差就是协方差矩阵的特征值。我们要找到最大方差也就是协方差矩阵最大的特征值，最佳投影方向就是最大特征值所对应的特征向量，次佳就是第二大特征值对应的特征向量，以此类推。\n对角矩阵 由上文知道，协方差矩阵 C 是一个是对称矩阵，在线性代数中实对称矩阵有一系列非常好的性质：\n实对称矩阵不同特征值对应的特征向量必然正交。 设特征向量\\(\\lambda\\) 重数为 r，则必然存在 r 个线性无关的特征向量对应于 \\(\\lambda\\) ，因此可以将这 r 个特征向量单位正交化。 实对称矩阵一定可以对角化 由上面两条可知，一个 n 行 n 列的实对称矩阵一定可以找到 n 个单位正交特征向量，设这 n 个特征向量为 \\(e_1,e_2,\\cdots,e_n\\)，我们将其按列组成矩阵： \\(E=(e_1,e_2,\\cdots,e_n)\\)。 对于协方差矩阵C有以下结论:\n\\[ E^TCE = \\begin{bmatrix} \\lambda_1 \\\\\\\\ \u0026amp;\\lambda_2 \\\\\\\\ \u0026amp;\u0026amp; \\ddots \\\\\\\\ \u0026amp;\u0026amp;\u0026amp; \\lambda_n \\end{bmatrix} \\]\n注：因为E为正交矩阵，则\\(E^{-1}\\) = \\(E^T\\) ,这个过程成为相似对角化，\\(\\lambda_n\\)为C的特征值，对应的特征向量为\\(e_n\\)\n这都是线代的基础知识。\nSVD 复制一下将协方差矩阵写成中心化的形式：\n\\[ \\begin{align}S\u0026amp;=\\frac{1}{N}\\sum\\limits_{i=1}^N(x_i-\\overline{x})(x_i-\\overline{x})^T\\nonumber\\\\\\\\ \u0026amp;=\\frac{1}{N}(x_1-\\overline{x},x_2-\\overline{x},\\cdots,x_N-\\overline{x})(x_1-\\overline{x},x_2-\\overline{x},\\cdots,x_N-\\overline{x})^T\\nonumber\\\\\\\\ \u0026amp;=\\frac{1}{N}(X^T-\\frac{1}{N}X^T\\mathbb{I}_{N1}\\mathbb{I}_{N1}^T)(X^T-\\frac{1}{N}X^T\\mathbb{I}_{N1}\\mathbb{I}_{N1}^T)^T\\nonumber\\\\\\\\ \u0026amp;=\\frac{1}{N}X^T(E_N-\\frac{1}{N}\\mathbb{I}_{N1}\\mathbb{I}_{1N})(E_N-\\frac{1}{N}\\mathbb{I}_{N1}\\mathbb{I}_{1N})^TX\\nonumber\\\\\\\\ \u0026amp;=\\frac{1}{N}X^TH_NH_N^TX\\nonumber\\\\\\\\ \u0026amp;=\\frac{1}{N}X^TH_NH_NX=\\frac{1}{N}X^THX \\end{align} \\]\n对中心化后的数据集进行奇异值分解： \\[ HX=U\\Sigma V^T,U^TU=E_N,V^TV=E_p,\\Sigma:N\\times p \\]\n于是： \\[ S=\\frac{1}{N}X^THX=\\frac{1}{N}X^TH^THX=\\frac{1}{N}V\\Sigma^T\\Sigma V^T \\] 因此，我们直接对中心化后的数据集进行 SVD，就可以得到特征值\\(\\Sigma^2\\)和特征向量 \\(V\\)，在新坐标系中的坐标就是： \\[ HX\\cdot V \\] ## 步骤 总结一下 PCA 的算法步骤：\n设有 m 条 n 维数据。\n对所有的样本进行中心化： \\(x^{(i)} = x^{(i)}-\\frac{1}{m}\\sum_{j=1}^mx^{(j)}\\)\n计算样本的协方差矩阵 \\(XX^T\\)\n对矩阵\\(XX^T\\)进行特征值分解 4）取出最大的n’个特征值对应的特征向量\\((w_1,w_2,...,w_{n′})\\), 将所有的特征向量标准化后，组成特征向量矩阵W。\n5）对样本集中的每一个样本\\(x^{(i)}\\),转化为新的样本\\(z^{(i)}=W^Tx^{(i)}\\) 6) 得到输出样本集\\(D^′=(z^{(1)},z^{(2)},...,z^{(m)})\\)\n实例 假设我们的数据集有10个二维数据(2.5,2.4), (0.5,0.7), (2.2,2.9), (1.9,2.2), (3.1,3.0), (2.3, 2.7), (2, 1.6), (1, 1.1), (1.5, 1.6), (1.1, 0.9)，需要用PCA降到1维特征。\n首先我们对样本中心化，这里样本的均值为(1.81, 1.91),所有的样本减去这个均值向量后，即中心化后的数据集为(0.69, 0.49), (-1.31, -1.21), (0.39, 0.99), (0.09, 0.29), (1.29, 1.09), (0.49, 0.79), (0.19, -0.31), (-0.81, -0.81), (-0.31, -0.31), (-0.71, -1.01)。\n现在我们开始求样本的协方差矩阵。 然后求出特征值\\((0.0490833989,1.28402771)\\)，对应的特征向量为 \\((0.735178656,0.677873399)^T\\),\\((−0.677873399,−0.735178656)^T\\)，,由于最大的k=1个特征值为1.28402771,对于的k=1个特征向量为\\((−0.677873399,−0.735178656)^T\\). 则我们的W=\\((−0.677873399,−0.735178656)^T\\)。 们对所有的数据集进行投影\\(z^{(i)}=W^Tx^{(i)}\\)，得到PCA降维后的10个一维数据集为：(-0.827970186， 1.77758033， -0.992197494， -0.274210416， -1.67580142， -0.912949103， 0.0991094375， 1.14457216, 0.438046137， 1.22382056)\n性质 缓解维度灾难：PCA 算法通过舍去一部分信息之后能使得样本的采样密度增大（因为维数降低了），这是缓解维度灾难的重要手段； 降噪：当数据受到噪声影响时，最小特征值对应的特征向量往往与噪声有关，将它们舍弃能在一定程度上起到降噪的效果； 过拟合：PCA 保留了主要信息，但这个主要信息只是针对训练集的，而且这个主要信息未必是重要信息。有可能舍弃了一些看似无用的信息，但是这些看似无用的信息恰好是重要信息，只是在训练集上没有很大的表现，所以 PCA 也可能加剧了过拟合； 特征独立：PCA 不仅将数据压缩到低维，它也使得降维之后的数据各特征相互独立； 代码 # 手动实现PCA算法 import numpy as np def pca(data): \u0026quot;\u0026quot;\u0026quot; 主成分分析 :param data: 数据集 :return: \u0026quot;\u0026quot;\u0026quot; # 数据集的行数 num_data, num_feat = data.shape # 对每一列的数据进行平均值的计算 mean_vec = np.mean(data, axis=0) # 对数据集中每一行的数据进行平均值的计算 data_mean_centered = data - mean_vec # 计算协方差矩阵 sigma = np.dot(data_mean_centered.T, data_mean_centered) / num_data # 计算特征值和特征向量 eig_val, eig_vec = np.linalg.eig(sigma) # 对特征值进行排序 eig_pairs = [(np.abs(eig_val[i]), eig_vec[:, i]) for i in range(len(eig_val))] eig_pairs.sort(key=lambda x: x[0], reverse=True) # 要降维的维数，这里以2为例。将特征向量以列向量形式拼合。 matrix_w = np.hstack([eig_pairs[i][1].reshape(-1, 1) for i in range(2)]) # 将原始数据进行投影。 res = data.dot(matrix_w) print(res) pca(np.array([[1, 2, 5], [3, 4, 6], [5, 6, 9], [3, 2 ,5]])) [[ 4.78637704 -1.46926342] [ 7.62278435 -0.82094287] [11.68194836 -0.79767573] [ 5.77746434 0.2656909 ]] 参考文章 https://zhuanlan.zhihu.com/p/77151308\nhttps://www.cnblogs.com/pinard/p/6239403.html\n","date":"2021-04-12T00:00:00Z","permalink":"/pca/","title":"PCA"},{"content":"Bert BERT 的模型架构非常简单，你已经知道它是如何工作的：它只是 Transformer 的编码器。新的是训练目标和 BERT 用于下游任务的方式。\n我们如何使用纯文本训练（双向）编码器？我们只知道从左到右的语言建模目标，但它仅适用于每个标记只能使用以前的标记（并且看不到未来）的解码器。BERT 的作者提出了其他未标记数据的训练目标。在讨论它们之前，让我们先看看 BERT 作为 Transformer 编码器的输入。\n训练输入：带有特殊标记的句子对 在训练中，BERT 看到用特殊的标记分隔符 [SEP] 分隔的句子对。为了让模型轻松区分这些句子，除了标记和位置嵌入之外，它还使用了段嵌入。\n另一个特殊标记是 [CLS] 。顾名思义，它就是表示整个句子的类别的token。在训练中，它用于我们接下来会看到的 NSP 目标。一旦模型被训练，它就会被用于下游任务。\n预训练目标：NSP Next Sentence Prediction (NSP) 目标是一个二元分类任务。根据特殊标记[CLS] 的最后一层表示 ，该模型预测这两个句子是否是某些文本中的连续句子。\n输入： [CLS] 这个人去了 [MASK] 商店 [SEP] 他买了一加仑 [MASK] 牛奶 [SEP]\n标签： isNext\n输入： [CLS] 男子去了 [MASK] 商店 [SEP] 企鹅 [MASK] 正在飞行##less 鸟 [SEP]\n标签： notNext\n该任务教模型理解句子之间的关系。正如我们稍后将看到的，这将使 BERT 能够用于需要某种推理的复杂任务。\n预训练目标：MLM（掩蔽语言模型） BERT 有两个训练目标，其中最重要的是 Masked Language Modeling (MLM) 目标。对于 MLM 目标，在步骤中会发生以下情况：\n选择一些标记 （每个标记以 15% 的概率被选中） 替换这些选定的标记\n（使用特殊标记 [MASK] (p=80%)，随机标记 (p=10%)，原始标记（保持不变）(p=10%)） 预测原始标记（计算损失） 其思想来自于完形填空，也借鉴了CBOW的思想。 MLM 仍然是语言建模：目标是根据文本的某些部分预测句子/文本中的一些标记。为了更清楚，让我们将 MLM 与标准的从左到右的语言建模目标进行比较 在每一步，标准的从左到右的 LMs 根据之前的标记预测下一个标记。这意味着最终表示，即来自最终层的用于预测的表示，仅编码先前的上下文，即它们 看不到未来。\n不同的是，MLM可以一次看到整个文本，但有些标记被破坏了：这就是 BERT 是双向的原因。请注意，为了让 ELMo 知道左右上下文，作者必须训练两个不同的单向 LM(即双向LSTM)，然后将它们的表示连接起来。在 BERT 中，我们不需要这样做：一个模型就足够了。\n注意一些细节，在代码实现的时候，注意特殊的标记如[SEP][CLS] 等不要替换， 还有[PAD]\n数据集构建代码 class BERTDataset(Dataset): def __init__(self, corpus_path, vocab, seq_len, encoding=\u0026quot;utf-8\u0026quot;, corpus_lines=None, on_memory=True): self.vocab = vocab self.seq_len = seq_len self.on_memory = on_memory self.corpus_lines = corpus_lines self.corpus_path = corpus_path self.encoding = encoding with open(corpus_path, \u0026quot;r\u0026quot;, encoding=encoding) as f: if self.corpus_lines is None and not on_memory: for _ in tqdm.tqdm(f, desc=\u0026quot;Loading Dataset\u0026quot;, total=corpus_lines): self.corpus_lines += 1 if on_memory: self.lines = [line[:-1].split(\u0026quot;\\t\u0026quot;) for line in tqdm.tqdm(f, desc=\u0026quot;Loading Dataset\u0026quot;, total=corpus_lines)] # 一行有两个句子，分隔符是\\t self.corpus_lines = len(self.lines) if not on_memory: self.file = open(corpus_path, \u0026quot;r\u0026quot;, encoding=encoding) self.random_file = open(corpus_path, \u0026quot;r\u0026quot;, encoding=encoding) for _ in range(random.randint(self.corpus_lines if self.corpus_lines \u0026lt; 1000 else 1000)): self.random_file.__next__() def __len__(self): return self.corpus_lines def __getitem__(self, item): t1, t2, is_next_label = self.random_sent(item) # is_next_label: 1 or 0，1代表t2是相邻句子，0代表不是相邻句子 t1_random, t1_label = self.random_word(t1) # mlm任务 t2_random, t2_label = self.random_word(t2) # [CLS] tag = SOS tag, [SEP] tag = EOS tag t1 = [self.vocab.sos_index] + t1_random + [self.vocab.eos_index] t2 = t2_random + [self.vocab.eos_index] t1_label = [self.vocab.pad_index] + t1_label + [self.vocab.pad_index] t2_label = t2_label + [self.vocab.pad_index] segment_label = ([1 for _ in range(len(t1))] + [2 for _ in range(len(t2))])[:self.seq_len] bert_input = (t1 + t2)[:self.seq_len] # 截断 bert_label = (t1_label + t2_label)[:self.seq_len] padding = [self.vocab.pad_index for _ in range(self.seq_len - len(bert_input))] #pad bert_input.extend(padding), bert_label.extend(padding), segment_label.extend(padding) output = {\u0026quot;bert_input\u0026quot;: bert_input, \u0026quot;bert_label\u0026quot;: bert_label, \u0026quot;segment_label\u0026quot;: segment_label, \u0026quot;is_next\u0026quot;: is_next_label} return {key: torch.tensor(value) for key, value in output.items()} def random_word(self, sentence): # 对sent token进行mask并返回mask后的label tokens = sentence.split() output_label = [] for i, token in enumerate(tokens): prob = random.random() if prob \u0026lt; 0.15: prob /= 0.15 # 80% randomly change token to mask token if prob \u0026lt; 0.8: tokens[i] = self.vocab.mask_index # 10% randomly change token to random token elif prob \u0026lt; 0.9: tokens[i] = random.randrange(len(self.vocab)) # 10% randomly change token to current token else: tokens[i] = self.vocab.stoi.get(token, self.vocab.unk_index) output_label.append(self.vocab.stoi.get(token, self.vocab.unk_index)) # 被mask掉的token作为label else: tokens[i] = self.vocab.stoi.get(token, self.vocab.unk_index) output_label.append(0) # label为0，这样计算loss时不用考虑，因为可以设置nn.NLLLoss(ignore_index=0) return tokens, output_label def random_sent(self, index): # 根据idx选择某对句子并随机返回相邻或不相邻的句子。 t1, t2 = self.get_corpus_line(index) # output_text, label(isNotNext:0, isNext:1) if random.random() \u0026gt; 0.5: return t1, t2, 1 else: return t1, self.get_random_line(), 0 def get_corpus_line(self, item): # 通过item idx选择某对句子 if self.on_memory: return self.lines[item][0], self.lines[item][1] else: line = self.file.__next__() if line is None: self.file.close() self.file = open(self.corpus_path, \u0026quot;r\u0026quot;, encoding=self.encoding) line = self.file.__next__() t1, t2 = line[:-1].split(\u0026quot;\\t\u0026quot;) return t1, t2 def get_random_line(self): # 随机选一行 if self.on_memory: return self.lines[random.randrange(len(self.lines))][1] line = self.file.__next__() if line is None: self.file.close() self.file = open(self.corpus_path, \u0026quot;r\u0026quot;, encoding=self.encoding) for _ in range(random.randint(self.corpus_lines if self.corpus_lines \u0026lt; 1000 else 1000)): self.random_file.__next__() line = self.random_file.__next__() return line[:-1].split(\u0026quot;\\t\u0026quot;)[1] 微调 分类 对于分类任务直接取第一个[CLS] token的final hidden state，然后加一层权重后softmax输出。\n\\[ P = softmax(CW^T) \\]\n其它任务 其它任务需要一些调整 适配器(Adapter) 到目前为止，我们只考虑了将知识从预训练模型（例如 BERT）转移到下游任务的标准方法：微调。“微调”意味着您采用预训练模型并以相当小的学习率训练您感兴趣的任务（例如，情感分类）。这意味着首先，您更新整个（大型）模型，其次，对于每个任务，您需要微调预训练模型的单独副本。最后，对于几个下游任务，您最终会得到很多大型模型 - 这是非常低效的！\nApdater-Bert的想法是将task-specific layer放在预训练模型中间，也就是加入Adapter结构，然后冻结住预训练模型参数，最后我们fientuning的时候，只更新Apdater、layerNorm以及与具体任务相关的layer的参数。具体结构图如下：\n左图是Adapter-BERT中的transformer layer，我们可以看到每一个transformer layer增加了两个Adapter layer，分别加在LayerNorm之前，当然了，在进行LayerNorm之前，我们需要进行讲Apdater layer的输出进行残差连接。 右图是Adapter layer的具体结构示意 \u0026gt;这里为什么要用残差连接？主要是因为当初始化的时候，权重都很小，残差连接可以保证模型输出与预训练模型相同。 总结 总之BERT就只有这么多新的特性，或者说创新，但是它一经问世就成为了新的霸主，可见效果之好，BERT还有很多细节上的问题，后面看到或者学习到的时候会继续记录下来。 ## 一些问题\n为什么 Bert 的三个 Embedding 可以进行相加？ 因为三个 embedding 相加等价于三个原始 one-hot 的拼接再经过一个全连接网络。和拼接相比，相加可以节约模型参数。\n引用苏建林老师的话： \u0026gt; Embedding的数学本质，就是以one hot为输入的单层全连接。\n也就是说，世界上本没什么Embedding，有的只是one hot。\n假设 token Embedding 矩阵维度是 [4,768]；position Embedding 矩阵维度是 [3,768]；segment Embedding 矩阵维度是 [2,768]。\n对于一个字，假设它的 token one-hot 是[1,0,0,0]；它的 position one-hot 是[1,0,0]；它的 segment one-hot 是[1,0]。\n那这个字最后的 word Embedding，就是上面三种 Embedding 的加和。\n如此得到的 word Embedding，和concat后的特征：[1,0,0,0,1,0,0,1,0]，再过维度为 [4+3+2,768] = [9, 768] 的全连接层，得到的向量其实就是一样的。\ntransformers中bert模型的输出 ","date":"2021-04-08T00:00:00Z","permalink":"/bert/","title":"BERT"},{"content":"常用命令 diff image.png git diff：当工作区有改动，临时区为空，diff的对比是“工作区”与最后一次commit提交的仓库的共同文件”；当工作区有改动，临时区不为空，diff对比的是“工作区”与“暂存区”的共同文件”。 git diff --cached：显示暂存区与最后一次commit的改动。 git diff \u003c分支1\u003e \u003c分支2\u003e 显示两个分支最后一次commit的改动。\ninit git init初始化一个仓库\nadd git add .将除.gitignore中声明的所有文件加入暂存区 也可以git add 特定文件，将文件加入暂存区。\ncommit image.png git commit -m \"提交说明\" 提交到工作区\ngit commit --amend 修改上次的提交记录\npush git push 提交到远程库。可以指定分支提交 比如：git push origin main 指定为main分支\npull git pull 将改动拉倒本地库，并合并，默认为Merge合并 如果加上rebase参数则合并方式变为rebase合并。\nlog git log 查看提交历史 git log --oneline简洁输出\nbranch git branch xxx 建立分支xxx 然后用git checkout xxx 切换到分支xxx 或者用git checkout -b xxx 完成同样的操作\ncheckout image.png git checkout 回退版本即head分离 但master不变 checkout可以切换分支，也可以head分离，但是分支的位置不变，但后面的reset和revert都会改变分支\nreset git reset --hard HEAD^回退一个版本（master也改变） git reset --hard HEAD~n 回退n个版本 git reset --hard xxxxxx xxxxxx为版本号 即为git log显示的每一个版本号 一般为前六位\nreset的参数有三种，其作用如下： 最危险但最常用的就是hard。soft也常用来修改某个提交，只修改提交，之后再进行提交即可达到修改的目的。\nrevert git revert命令用于撤消对仓库提交历史的更改。其他“撤消”命令，例如 git checkout 和 git reset，将HEAD和分支引用指针移动到指定的提交。git revert也需要一个指定的提交，但是，它并不会将 ref 指针移动到这个提交。revert 操作将采用反转指定的提交的更改，并创建一个新的“还原提交”。然后更新 ref 指针以指向新的还原提交，使其成为分支的HEAD。(创建一个新的commit结点)\nmerge git merge 比如在master分支里 执行git merge xxx 将xxx分支合并到master中，一般项目开发，一人一个分支，最后提交的时候合并再提交。不过更推荐用git rebase方法，这样合并后的分支更加直观。一般是进行三方合并。 branch 最常用的就是创建和删除分支\ngit branch -f master~3将分支强制回退三个版本，但head不动\ncherry-pick 当需要另一个分支的所有改动时，用git merge，但当需要部分改动时候，要用git cherry-pick xxx xxx为哈希值或者分支名，指定为分支名时候，将分支的最新改动合并过来 rebase 当不知道提交的哈希值时，可以用git rebase -i HEAD~x 来可视化管理，可以调整提交的顺序，可以删除不想要的提交，或合并提交 这是线性化的自动的 cherry-pick git rebase xx1 xx2将xx2分支上的提交记录放到xx1后面\n此外活用git rebase -i ,进行可视化的操作。 ### fetch git fetch获取远程仓库的数据，不会改变你本地仓库的状态，不会更新你的master,也没有更改你的本地磁盘文件，可以理解为单纯的下载操作。而git pull相当于git fetch + git merge即抓取后合并\nreflog git reflog 查看操作记录，这个操作可以撤销不小心用git reset回退版本的操作\nstash 它会保存当前工作进度，会把暂存区和工作区的改动保存到一个未完结变更的堆栈中；执行完这个命令后，在运行 git status 命令，就会发现当前是一个干净的工作区，没有任何改动。\ngit stash 是本地的，不会上传到服务器上； 可以通过使用git stash save 'message...'可以添加一些注释。 image.png 底层内容 这几天系统学习了一下git的底层内容，通透了很多，记录一些。贴一下原博客：https://www.lzane.com/tech/git-internal/ 首先看一个图\n首先要明确有四种object，第一种是记录文件内容，第二种是记录目录结构，第三种是记录提交信息，第四种是记录tag信息，第四种无关紧要。\n从下面开始看，最下面记录的文件内容，注意只记录文件内容，不包括文件名等其它内容。是一个blob类型的节点，将文件的内容信息经过SHA1哈希算法得到对应的哈希值作为这个object在Git仓库中的唯一身份证。\n然后再往上的三角形记录的是仓库的目录结构，它将当前的目录结构打了一个快照。从它储存的内容来看可以发现它储存了一个目录结构（类似于文件夹），以及每一个文件（或者子文件夹）的权限、类型、对应的身份证（SHA1值）、以及文件名。\n再往上就是记录的提交的信息，它储存的是一个提交的信息，包括对应目录结构的快照tree的哈希值，上一个提交的哈希值，提交的作者以及提交的具体时间，最后是该提交的信息。\n还有分支的信息和Head。HEAD、分支和普通的Tag可以理解为一个指针，指向对应commit的sha1值。\n仓库有三个分区：工作目录、index索引区域、Git仓库。\n当文件被修改后，只是工作目录发生了改变，其余两个是没有任何变化的。当运行git add xxx命令后，即将xxx文件加入了索引区域，此时新建了一个blob object，并且将原来指向xxx指向了新建的blob Object，记住索引索引的是add的所有文件，这时运行git commit，会生成一个tree object，然后创建commit object，将分支等信息指向新的commit。注意每次commit都是储存的全新的文件快照而不是变更部分。\n合并策略 Fast-forward(没有分叉) image.png Recursive(最常用最重要) Recursive是Git分支合并策略中最重要也是最常用的策略，是Git在合并两个有分叉的分支时的默认行为。其算法可以简单描述为：递归寻找路径最短的唯一共同祖先节点，然后以其为base节点进行递归三向合并。\n那怎么保证Git能够找到正确的合并base节点，尽可能的减少冲突呢？答案就是，Git在寻找路径最短的共同祖先节点时，如果满足条件的祖先节点不唯一，那么Git会继续递归往下寻找直至唯一。不唯一的多个祖先节点做一个临时的三向合并节点。\nRecursive策略已经被大量的场景证明它是一个尽量减少冲突的合并策略，我们可以看到有趣的一点是，对于两个合并分支的中间节点（如上图节点4，5），只参与了base的计算，而最终真正被三向合并拿来做合并的节点，只包括末端以及base节点。\n需要注意Git只是使用这些策略尽量的去帮你减少冲突，如果冲突不可避免，那Git就会提示冲突，需要手工解决。（也就是真正意义上的冲突）。 ### Ours \u0026amp; Theirs ### Octopus ## 参考\n图解Git (marklodato.github.io)\n这才是真正的Git——Git内部原理 - LZANE | 李泽帆（靓仔）\n这才是真正的Git——分支合并 - LZANE | 李泽帆（靓仔）\n","date":"2021-04-04T00:00:00Z","permalink":"/git/","title":"git"},{"content":"编辑距离 定义 编辑距离(Edit Distance)是针对两个字符串S1和S2的差异程度进行量化，计算方式是看至少需要多少次的处理才能将S1变成S2（和S2变成S1是等价的），用 EditDis(S1,S2)表示。\n其中处理的方式有三种：\n1.插入一个字符\n2.删除一个字符\n3.替换一个字符\n这是严格意义上的距离，满足”距离三公理”：\n1.对称性，EditDis(S1,S2) = EditDis(S2,S1) 2.非负性，EditDis(S1,S2) \u0026gt;= 0, 当且仅当S1=S2时，等号成立 3.三角不等式，EditDis(S1,S2) + EditDis(S1,S3) \u0026gt;= EditDis(S2,S3)\n动态规划求解 令 S1.substr(i)表示 S1前i个字符构成的子串，S2.substr(j)表示S2前j个字符构成的子串 \\(dp[i][j]\\)表示S1.substr(i)和S2.substr(j)的编辑距离。 注意，这句话的另一层含义是：当我们计算出了EditDis(S1,S2)则默认了S1.substr(i)已经通过三种处理方式变成了S2.substr(j)。 所以，当我们计算\\(dp[i+1][j+1]\\)时，我们可以利用\\(dp[i][j]\\)，\\(dp[i+1][j]\\)，\\(dp[i][j+1]\\)的信息。\n过程 1.确定最后一步： 令，S1的长度为len1，S2的长度为len2 \\(dp[len1][len2]\\)有三种方式可以实现，一种是S1.substr(len1-1)插入一个字符使之等于S2（\\(dp[len1][len2-1] + 1\\)），一种是S2.substr(len2)删除一个字符使之等于S1（\\(dp[len1-1][len2] + 1\\)），另一种是替换最后一个字符使S1和S2相等（\\(dp[len1-1][len2-1] + 1\\)） 确定转移方程: 3.确定边界和初始状态\n我们设定Dp二维数组大小是（Len+1） * （Len2+1），第0行代表 S1为空串，第0列代表S2为空串。\n显然，S1变成为空串需要的每次操作是\\(dp[i][0]=i\\)\nS2变成为空串需要的每次操作是\\(dp[0][j] = j\\)\n代码 class Solution: def minDistance(self, word1: str, word2: str) -\u0026gt; int: import numpy as np len1, len2 = len(word1), len(word2) dp = [[0]*(len2+1) for _ in range(len1+1)] # 定义 for i in range(1,len1+1): dp[i][0] = i # 边界 for j in range(1,len2+1): dp[0][j] = j # 边界 for i in range(1,len1+1): for j in range(1,len2+1): if word1[i-1] == word2[j-1]: dp[i][j] = dp[i-1][j-1] else: dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1) return int(dp[len1][len2]) ","date":"2021-04-03T00:00:00Z","permalink":"/%E7%BC%96%E8%BE%91%E8%B7%9D%E7%A6%BB/","title":"编辑距离"},{"content":"通过sklearn模块实现 import numpy as np import matplotlib.pyplot as plt from sklearn import metrics from sklearn.datasets import make_blobs from sklearn.cluster import KMeans from sklearn.datasets import load_iris %matplotlib inline X,y = make_blobs(n_samples=100,n_features=2,centers=[[-1,-1],[0,0],[1,1],[2,2]],cluster_std=[0.4,0.2,0.2,0.2])#使用make_blobs生成训练数据,生成100个样本,每个样本2个特征,共4个聚类,聚类中心分别为[-1,-1],[0,0],[1,1],[2,2],聚类方差分别为0.4,0.2,0.2,0.2 plt.scatter(X[:,0],X[:,1])#画出训练样本的散点图,散点图的横坐标为样本的第一维特征,纵坐标为样本的第二维特征 plt.show() kmeans = KMeans(n_clusters=3)#生成kmeans分类器,聚类数量为3,其余参数使用默认值。 y_pred = kmeans.fit_predict(X)#使用fit_predict方法计算聚类中心并且预测每个样本的聚类索引。 plt.scatter(X[:,0],X[:,1],c=y_pred)#画出训练样本的散点图,散点图的横坐标为样本的第一维特征,纵坐标为样本的第二维特征,将各聚类结果显示为不同的颜色 plt.show() kmeans = KMeans(n_clusters=4)#生成kmeans分类器,聚类数量为4,其余参数使用默认值。 y_pred = kmeans.fit_predict(X)#使用fit_predict方法计算聚类中心并且预测每个样本的聚类索引。 plt.scatter(X[:,0],X[:,1],c=y_pred)#画出训练样本的散点图,散点图的横坐标为样本的第一维特征,纵坐标为样本的第二维特征,将各聚类结果显示为不同的颜色 plt.show() iris = load_iris() #导入iris数据集,iris数据集包含了150个样本,分别属于3类,每个样本包含4个特征 data_train=iris.data #iris样本集的样本特征 label_train=iris.target #iris样本集的样本标签 kmeans = KMeans(n_clusters=3)#生成kmeans分类器,聚类数量为3,其余参数使用默认值。 y_predict = kmeans.fit_predict(data_train)#使用fit_predict方法计算聚类中心并且预测每个样本的聚类索引。 plt.scatter(data_train[:,0],data_train[:,2],c=y_predict)#画出训练样本的散点图,散点图的横坐标为样本的第一维特征,纵坐标为样本的第三维特征,将各聚类结果显示为不同的颜色 plt.show() 手动实现 import numpy as np import matplotlib.pyplot as plt center = np.array([[0,0],[0,1]]) cls_num = 2 X = np.array([[0,0],[0,1],[2,1],[2,3],[3,4],[1,0]]) max_iter = 10 cls = np.zeros(X.shape[0]) run = True while run and max_iter \u0026gt; 0: for i,x in enumerate(X): tmp = np.argmin(np.sum(np.power(x - center,2),axis=1)) cls[i] = tmp run = False # 重新计算聚类中心 for i in range(cls_num): data = X[cls==i] # 取相同类别的样本 new_center = np.mean(data,axis=0) # 对相同类别的x和y取平均值 if np.sum(np.abs(center[i]-new_center),axis=0) \u0026gt; 1e-4: center[i] = new_center # 更新中心 run = True max_iter -= 1 plt.scatter(X[:,0],X[:,1],c=cls) plt.show() ## kernel kmeans 已知kmeans的核心公式为下：\n这里可以看到,实际上就是计算每个样本点簇中心的距离,然后判断出到哪个簇中心距离最短,然后分给那个簇。然后下次迭代时,簇中心就按照新分配的点重新进行计算了,然后所有的点再同样计算样本点到簇中心的距离,重新分配到不同的簇中。所以这样不断迭代下去,就能够收敛了,得到最后的聚类效果。\n核k-means,概括地来说,就是将数据点都投影到了一个高维的特征空间中（为啥要这么做呢,主要是突显出不同样本中的差异）,然后再在这个高维的特征空间中,进行传统的k-means聚类。主要的思想就是这么简单,比起传统普通的k-means就多了一个步核函数的操作。所以它的公式也与传统k-means很相近：\n但实际中很难计算,因此需要改造, 改造成为\n代码 class KernelKmeans: def __init__(self, n_clusters, max_iter, kernel): self.n_clusters = n_clusters self.max_iter = max_iter self.kernel = kernel def fit(self, X): self.centroids = X[np.random.choice(X.shape[0], self.n_clusters, replace=False)] for i in range(self.max_iter): distances = np.array([self.kernel(X, centroid) for centroid in self.centroids]) self.labels = np.argmin(distances, axis=0) self.centroids = np.array([X[self.labels == j].mean(axis=0) for j in range(self.n_clusters)], dtype=np.float32) def predict(self, X): distances = np.array([self.kernel(X, centroid) for centroid in self.centroids]) return np.argmin(distances, axis=0) def gaussian_kernel(X, y): return np.exp(-np.linalg.norm(X - y, axis=1)**2 / 2) ","date":"2021-03-28T00:00:00Z","permalink":"/kmeans/","title":"kmeans"},{"content":"翻转二叉树 开始学习二叉树了\n先来个简单题\nhttps://leetcode-cn.com/problems/invert-binary-tree/\n很简单\n# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def invertTree(self, root: TreeNode) -\u0026gt; TreeNode: if root == None: return None temp = root.left root.left = root.right root.right = temp self.invertTree(root.left) self.invertTree(root.right) return root ","date":"2021-03-24T00:00:00Z","permalink":"/%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91/","title":"翻转二叉树"},{"content":"对称二叉树 题目： https://leetcode-cn.com/problems/symmetric-tree/\n思路： 利用双向队列，每次把对称的两个对应的节点放入队列中，然后取出来比较，如果值不相等则返回false,如果一边为空 一边不为空也返回false 符合条件的话就继续搜索\n代码： # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def isSymmetric(self, root: TreeNode) -\u0026gt; bool: from collections import deque d = deque() d.append((root,root)) while d: left,right = d.popleft() if not left and not right: continue elif not left or not right: return False elif left.val != right.val: return False else: d.append((left.left,right.right)) d.append((left.right,right.left)) return True ","date":"2021-03-21T00:00:00Z","permalink":"/%E5%AF%B9%E7%A7%B0%E4%BA%8C%E5%8F%89%E6%A0%91/","title":"对称二叉树"},{"content":"合并两个有序列表 题目： https://leetcode-cn.com/problems/merge-two-sorted-lists/\n思路： 利用递归的思想，比较两个当前值，因为是有序链表\n代码： # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -\u0026gt; ListNode: if l1 == None: return l2 if l2 == None: return l1 if l1.val \u0026lt;= l2.val: l1.next = self.mergeTwoLists(l1.next,l2) return l1 else: l2.next = self.mergeTwoLists(l1,l2.next) return l2 ","date":"2021-03-18T00:00:00Z","permalink":"/%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E6%9C%89%E5%BA%8F%E5%88%97%E8%A1%A8/","title":"合并两个有序列表"},{"content":"可获得的最大点数 题目： https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/\n思路： 滑动窗口题目，限定窗口大小然后滑动即可\n代码： class Solution: def maxScore(self, cardPoints: List[int], k: int) -\u0026gt; int: n = len(cardPoints) # 滑动窗口大小为 n-k windowSize = n - k # 选前 n-k 个作为初始值 s = sum(cardPoints[:windowSize]) minSum = s for i in range(windowSize, n): # 滑动窗口每向右移动一格，增加从右侧进入窗口的元素值，并减少从左侧离开窗口的元素值 s += cardPoints[i] - cardPoints[i - windowSize] minSum = min(minSum, s) return sum(cardPoints) - minSum ","date":"2021-03-03T00:00:00Z","permalink":"/%E5%8F%AF%E8%8E%B7%E5%BE%97%E7%9A%84%E6%9C%80%E5%A4%A7%E7%82%B9%E6%95%B0/","title":"可获得的最大点数"},{"content":"滑动窗口中位数 题目： https://leetcode-cn.com/problems/sliding-window-median/\n思路： 很明显的滑动窗口，首先定义一个求中位数的匿名函数，然后一点一点求出来\n代码： class Solution: def medianSlidingWindow(self, nums: List[int], k: int) -\u0026gt; List[float]: median = lambda a: (a[(len(a)-1)//2] + a[len(a)//2]) / 2 res = [] for i in range(len(nums)-k+1): res.append(median(sorted(nums[i:i+k]))) return res ","date":"2021-02-25T00:00:00Z","permalink":"/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E4%B8%AD%E4%BD%8D%E6%95%B0/","title":"滑动窗口中位数"},{"content":"Seq2Seq中的Attention 缺陷 在seq2seq这篇文章中详细介绍了seq2seq模型的细节，但是仅仅用一个语义编码c是完全不能够表示编码器的输入的，源的可能含义的数量是无限的。当编码器被迫将所有信息放入单个向量中时，它很可能会忘记一些东西。\n不仅编码器很难将所有信息放入一个向量中——这对解码器来说也很困难。解码器只看到源的一种表示。但是，在每个生成步骤中，源的不同部分可能比其他部分更有用。但在目前的情况下，解码器必须从相同的固定表示中提取相关信息——这不是一件容易的事。\n这个时候就需要引入注意力机制了，注意这里的注意力机制和transformer中的self-attention是不一样的。下面详细介绍一下。注意几个名词：注意力得分、注意力权重。其中注意力得分即score的计算有多种方法，权重就是对得分进行softmax归一化。\nattention 注意机制是神经网络的一部分。在每个解码器步骤中，它决定哪些源部分更重要。在此设置中，编码器不必将整个源压缩为单个向量 - 它为所有源标记提供表示（例如，所有 RNN 状态而不是最后一个）。\n步骤： - 接受注意输入：解码器状态\\(h_t\\)以及所有编码器状态\\(s_1,s_2,\\dots,s_m\\) - 计算每个编码器状态的注意力分数\\(s_k\\)，注意力分数表示它对解码器状态\\(h_t\\)的相关性，使用注意力函数，接收一个解码器状态和一个编码器状态并返回一个标量分数，即图中的\\(score(h_t,s_k)\\) - 计算注意力权重：即概率分布- 使用Softmax函数 - 计算注意力输出：具有注意力机制的编码器状态的加权和 即为如图所示内容为如何计算注意力。\n注意我们提到的注意力函数，这里的注意力分数的计算有很多种方法，下面介绍几种比较常见的办法： - 点积： 最简单的办法。 - 双线性函数 - 多层感知机 注意后两者都有要优化的参数的，第一个点积是直接运算，因此很简单。 在应用时可以直接将注意力的结果传输到最后的softmax，也可以将原始的\\(h_t\\)合并，下面介绍几种变体。\nBahdanau Model - 编码器使用双向的RNN - 利用上一时刻的隐层状态计算注意力输出c，然后和隐层状态一起作为当前时刻的输入，再得到结果\\(\\hat{y}\\)。这里再说一下训练的过程中当前步的输入使用的是真实的\\(y\\)，测试的时候才会使用上一步的输出作为输入。可以将上下文向量c（也就是注意力输出）与\\(x\\)拼接后作为输入。 - 注意力得分使用的是感知机。 这里引用一下李沐大佬的代码：\nclass Seq2SeqAttentionDecoder(AttentionDecoder): def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, dropout=0, **kwargs): super(Seq2SeqAttentionDecoder, self).__init__(**kwargs) self.attention = d2l.AdditiveAttention( num_hiddens, num_hiddens, num_hiddens, dropout) self.embedding = nn.Embedding(vocab_size, embed_size) self.rnn = nn.GRU( embed_size + num_hiddens, num_hiddens, num_layers, dropout=dropout) self.dense = nn.Linear(num_hiddens, vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): # outputs的形状为(batch_size，num_steps，num_hiddens). # hidden_state的形状为(num_layers，batch_size，num_hiddens) outputs, hidden_state = enc_outputs return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens) def forward(self, X, state): # enc_outputs的形状为(batch_size,num_steps,num_hiddens). # hidden_state的形状为(num_layers,batch_size, # num_hiddens) enc_outputs, hidden_state, enc_valid_lens = state # 输出X的形状为(num_steps,batch_size,embed_size) X = self.embedding(X).permute(1, 0, 2) # 转换是为了方便后面循环计算。 outputs, self._attention_weights = [], [] for x in X: # query的形状为(batch_size,1,num_hiddens) query = torch.unsqueeze(hidden_state[-1], dim=1) # -1是指在最后一层最后时刻的隐藏状态，作为query # context的形状为(batch_size,1,num_hiddens) context = self.attention( query, enc_outputs, enc_outputs, enc_valid_lens) # 在特征维度上连结 x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1) # 将x变形为(1,batch_size,embed_size+num_hiddens) out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state) outputs.append(out) self._attention_weights.append(self.attention.attention_weights) # 全连接层变换后，outputs的形状为 # (num_steps,batch_size,vocab_size) outputs = self.dense(torch.cat(outputs, dim=0)) return outputs.permute(1, 0, 2), [enc_outputs, hidden_state, enc_valid_lens] @property def attention_weights(self): return self._attention_weights Luong Model 这个模型的编码器比较常规，使用当前状态计算注意力输出，然后解码器中将隐层状态与注意力输出做一步结合，这样得到了新的隐层状态，然后再传递得到预测结果。\n注意力对齐 可以看到解码器关注的源token。\n到此seq2seq中的attention就介绍完毕了，其实还有很多细节，以后遇到了会持续补充。\nSelf-attention 移步transformer。Transformer\nMQA image.png 标准的mha中，KV heads的数量和Query heads的数量相同，每一个q head对应一个独立的kv head，但这样的开销比较大。 MQA (Multi Queries Attention): MQA比较极端，只保留一个KV Head，多个Query Heads共享相同的KV Head。这相当于不同Head的Attention差异，全部都放在了Query上，需要模型仅从不同的Query Heads上就能够关注到输入hidden states不同方面的信息。这样做的好处是，极大地降低了KV Cache的需求，但是会导致模型效果有所下降。（层内共享） # GQA 如上图所示，GQA就是在MHA和MQA之间做了一个平衡。对query heads进行分组，分成几组就对应多少个kv heads，然后每一组内的query Heads共享相同的KV head。 GQA可以在减少计算量和KV Cache同时确保模型效果不受到大的影响。 # online attention ### 3-pass \\(\\mathsf{NO}\\)TATIONS\n\\(\\{m_i\\}{:}\\max_{j=1}^i\\left\\{x_j\\right\\}\\), with initial value \\(m_0=-\\infty.\\) \\(\\{d_i\\}{:}\\sum_{j=1}^ie^{x_j-m_N}\\), with initial value \\(d_0=0,d_N\\) is the denominator of safe softmax. \\(\\{a_i\\}{:\\text{ the final softmax value}}.\\)\nBODY \\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[m_i\\leftarrow\\max\\left(m_{i-1},x_i\\right)\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[d_i\\leftarrow d_{i-1}+e^{x_i-m_N}\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[a_i\\leftarrow\\frac{e^{x_i-m_N}}{d_N}\\] \\(\\mathbf{end}\\)\n这是3step计算attention的方法，每一步都需要上一步的结果才可以继续计算。这样的话由于sram中没有足够的存储空间，因此需要多次访存。 ### online attention \\[\\begin{aligned} d_i^{\\prime}\u0026amp; =\\sum_{j=1}^ie^{x_j-m_i} \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1} e^{x_j-m_i}\\right)+e^{x_i-m_i} \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1} e^{x_j-m_{i-1}}\\right)e^{m_{i-1}-m_i}+e^{x_i-m_i} \\\\ \u0026amp;= d_{i-1}\u0026#39; e^{m_{i-1}-m_i}+e^{x_i-m_i} \\end{aligned}\\] 找到迭代式之后就可以从3step降到2step \\[\\begin{aligned}\u0026amp;\\mathbf{for~}i\\leftarrow1,N\\textbf{ do}\\\\\u0026amp;\u0026amp;\u0026amp;m_i\u0026amp;\u0026amp;\\leftarrow\u0026amp;\\max\\left(m_{i-1},x_i\\right)\\\\\u0026amp;\u0026amp;\u0026amp;d_i^{\\prime}\u0026amp;\u0026amp;\\leftarrow\u0026amp;d_{i-1}^{\\prime}e^{m_{i-1}-m_i}+e^{x_i-m_i}\\\\\u0026amp;\\mathbf{end}\\\\\u0026amp;\\mathbf{for~}i\\leftarrow1,N\\textbf{ do}\\\\\u0026amp;\u0026amp;\u0026amp;a_i\\leftarrow\u0026amp;\u0026amp;\\frac{e^{x_i-m_N}}{d_N^{\\prime}}\\\\\u0026amp;\\mathbf{end}\\end{aligned}\\] 好像FLOPs计算量并没有减少，甚至还略有增加，因为现在每次都需要计算额外的scale\nx值，也就是pre-softmax logits，由于需要O(N^2)的显存无法放在SRAM中。因此：\n1. 要么提前计算好x，保存在全局显存中，需要O(N^2)的显存，容易爆显存。\n2. 要么在算法中online计算，每次循环中去load一部分Q，K到片上内存，计算得到x。\nAttention优化的目标就是避开第一种情况，尽可能节省显存，否则，LLM根本无法处理类似100K以上这种long context的情况。而对于第二种情况，我们不需要保存中间矩阵x，节省了显存，但是计算没有节省，并且增加了HBM IO Accesses（需要不断地load Q, K）。此时，2-pass算法相对于3-pass算法，可以减少一次整体的load Q, K以及减少一次对 xi 的online recompute，因为在2-pass的第一个pass中， xi 是被两次计算共享的。类似online-softmax这种算法，对应到Attention中的应用，就是Memory Efficient Attention（注意不是FlashAttention）。\nflash attention safe softmax并没有1-pass算法，那么Attention会不会有呢？有！这就是FlashAttention！\n在使用online attention的情况下，从头开始计算attention score的过程如下： \\(\\operatorname{NOTATIONS}\\)\n\\(Q[k,:]:\\)the \\(k\\)-th row vector of \\(Q\\) matrix. \\(\\begin{aligned}O[k,:]:\\mathrm{~the~}k\\text{-th row of output }O\\mathrm{~matrix.}\\\\\\mathbf{V}[i,i]:\\mathrm{~the~}k\\text{-th row of output }O\\mathrm{~matrix.}\\end{aligned}\\) \\(V[i,:]{:\\text{ the }i\\text{-th row of }V\\text{ matrix}}.\\) \\(\\{\\boldsymbol{o}_i\\}{:}\\sum_{j=1}^ia_jV[j,:]\\), a row vector storing partial aggregation result \\(A[k,:i]\\times V[:i,:]\\) BODY\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[\\begin{aligned}x_i\u0026amp;\\leftarrow\\quad Q[k,:]\\:K^T[:,i]\\\\m_i\u0026amp;\\leftarrow\\quad\\max\\left(m_{i-1},x_i\\right)\\\\d_i\u0026#39;\u0026amp;\\leftarrow\\quad d_{i-1}\u0026#39;e^{m_{i-1}-m_i}+e^{x_i-m_i}\\end{aligned}\\] \\(\\mathbf{end}\\)\n\\(\\textbf{for }i\\leftarrow 1, N\\textbf{ do}\\) \\[\\begin{aligned}\u0026amp;a_i\\:\\leftarrow\\:\\frac{e^{x_i-m_N}}{d_N^{\\prime}}\\\\\u0026amp;o_i\\:\\leftarrow\\:o_{i-1}+a_i\\:V[i,:\\:]\\end{aligned}\\] \\(\\mathbf{end}\\) \\[O[k,:]\\leftarrow\\boldsymbol{o}_N\\]\n优化思路和online attention一样，将\\(o_{i}\\)的计算简化以便于可以写成迭代式。\n原来的\\(o_{i}\\)使用以下方式计算，依赖于全局的\\(m_{N}\\)和\\(d_{N}\\)。 \\[\\boldsymbol{o}_i:=\\sum_{j=1}^i\\left(\\frac{e^{x_j-m_N}}{d_N^{\\prime}}V[j,:]\\right)\\] 将其改写成如下形式： \\[\\boldsymbol{o}_i^{\\prime}:=\\left(\\sum_{j=1}^i\\frac{e^{x_j-m_i}}{d_i^{\\prime}}V[j,:]\\right)\\] 这样按照上面的方式拓展下去，可以找到一个循环迭代式。\n\\[\\begin{aligned} \\mathbf{o}_i^{\\prime}\u0026amp; =\\sum_{j=1}^i\\frac{e^{x_j-m_i}}{d\u0026#39;}V[j,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_i}}{d_i^{\\prime}}V[j,:] \\right)+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_{i-1}}}{d_{i-1}^{\\prime}}\\frac{e^{x_j-m_i}}{e^{x_j-m_{i-1}}}\\frac{d_{i-1}^{\\prime}}{d_i^{\\prime}}V[j,:]\\right)+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,:] \\\\ \u0026amp;= \\left(\\sum_{j=1}^{i-1}\\frac{e^{x_j-m_{i-1}}}{d_{i-1}^{\\prime}}V[j,.]\\right)\\frac{d_{i-1}^{\\prime}}{d_i^{\\prime}}e^{m_{i-1}-m_i}+\\frac{e^{x_i-m_i}}{d_i^{\\prime}}V[i,.] \\\\ \u0026amp;= \\boldsymbol{o}_{i-1}\u0026#39;\\frac{d_{i-1}\u0026#39;e^{m_{i-1}-m_i}}{d_i\u0026#39;}+\\frac{e^{x_i-m_i}}{d_i\u0026#39;}V[i,:] \\end{aligned}\\]\n这样就找到了\\(o_{i}\\)的递推表达式。\n之后对Q,K进行tiling后计算，得到如下： \\[\\begin{aligned}\u0026amp;\\textbf{for }i\\leftarrow1,\\#\\text{tiles do}\\\\\u0026amp;\u0026amp;\u0026amp;\\boldsymbol{x}_i\\quad\\leftarrow\\quad Q[k;\\cdot] K^T[\\cdot,(i-1) b; i b]\\\\\u0026amp;\u0026amp;\u0026amp;m_i^{(\\mathrm{local})}=\\begin{array}{c}\\overset{b}{\\operatorname*{max}}\\left(\\boldsymbol{x}_i[j]\\right)\\\\\\end{array}\\\\\u0026amp;\u0026amp;\u0026amp;m_i \\leftarrow \\max\\left(m_{i-1},m_i^{(\\mathrm{local})}\\right)\\\\\u0026amp;\u0026amp;\u0026amp;a_i^{\\prime} \\leftarrow d_{i-1}^{\\prime}e^{m_{i-1}-m_i}+\\sum_{j=1}^be^{\\boldsymbol{x}_i[j]-m_i}\\\\\u0026amp;\u0026amp;\u0026amp;\\boldsymbol{o}_i^{\\prime} \\leftarrow \\boldsymbol{o}_{i-1}^{\\prime}\\frac{d_{i-1}^{\\prime}e^{m_{i-1}-m_i}}{d_i^{\\prime}}+\\sum_{j=1}^b\\frac{e^{\\boldsymbol{x}_i[j]-m_i}}{d_i^{\\prime}}V[(i-1) b+j,:]\\\\\u0026amp;\\text{end}\\\\\u0026amp;\u0026amp;\u0026amp;O[k,:]\\leftarrow\\boldsymbol{o}_{N/b}^{\\prime}\\end{aligned}\\] 对于tiles，示意图如下： 可以理解成滑动窗口，\\(K^{T}\\)从左向右滑动（按列读取），\\(V\\)从上向下滑动（按行读取）。也可以直接理解成分块矩阵，具体为什么这么做，参考：Cuda 编程之 Tiling - 知乎 (zhihu.com) # 参考\n[KV Cache优化]🔥MQA/GQA/YOCO/CLA/MLKV笔记: 层内和层间KV Cache共享 - 知乎 (zhihu.com)\nTransformers KV Caching Explained | by João Lages | Medium\nFrom Online Softmax to FlashAttention. https://courses.cs.washington.edu/courses/cse599m/23sp/notes/flashattn.pdf\n","date":"2021-02-23T00:00:00Z","permalink":"/attention/","title":"Attention"},{"content":"PMI 点互信息 对于两个单词之间的PMI来说，可以这样计算：\n\\[ PMI(w,c) = \\log \\frac{p(w,c)}{p(w)p(c)} = \\log \\frac{N(w,c) |w,c|}{N(w)N(c)} \\] ## MI 在概率论和信息论中，两个随机变量的互信息（Mutual Information，简称MI）或转移信息（transinformation）是变量间相互依赖性的量度。不同于相关系数，互信息并不局限于实值随机变量，它更加一般且决定着联合分布 p(X,Y) 和分解的边缘分布的乘积 p(X)p(Y) 的相似程度。互信息(Mutual Information)是度量两个事件集合之间的相关性(mutual dependence)。互信息最常用的单位是bit。\n定义 正式地，两个离散随机变量 X 和 Y 的互信息可以定义为： 其中 p(x,y) 是 X 和 Y 的联合概率分布函数，而p(x)和p(y)分别是 X 和 Y 的边缘概率分布函数。 在结果上互信息与信息增益是一样的，下面是详细的推导。 应用到文本特征选择：\nU、C都是二值随机变量，当文档包含词项t时，U的取值为1，否则0；当文档属于类别c时，C的取值1，否则0。简单的理解就是对于文本来说，每一个token就是它的特征，取值只有有或者没有，也就是0或者1，互信息常用于文本特征的选择，也就 是选择有价值的token。在贝叶斯文本分类中用到了，特此记录。\n\\[ I_k = \\sum_{\\tilde{y}=0}^1 \\bigg(p(X = k | Y = \\tilde{y})p(Y = \\tilde{y}) \\log\\frac{p(X = k | Y = \\tilde{y})}{p(X=k)} + (1-p(X = k | Y = \\tilde{y}))p(Y = \\tilde{y}) \\log\\frac{1 - p(X = k | Y = \\tilde{y})}{1 - p(X = k)}\\bigg), \\]\n公式如上。\\(I_k\\)意味着单词k与Y之间的互信息。\n示例代码如下：\ndef calculateMI(dtm_ham_train, dtm_spam_train): ham_sums = np.sum(dtm_ham_train, axis=0) ham_probs = ham_sums / np.sum(ham_sums) spam_sums = np.sum(dtm_spam_train, axis=0) spam_probs = spam_sums / np.sum(spam_sums) all_sums = ham_sums + spam_sums all_probs = all_sums / sum(all_sums) mi = [] for i in range(len(all_probs)): if all_probs[i] == 0 or np.isnan(all_probs[i]): mi.append(0) else: mi.append(.5 * ham_probs[i] * np.log(ham_probs[i] / all_probs[i]) + .5 * (1 - ham_probs[i]) * np.log((1 - ham_probs[i])/(1 - all_probs[i])) + .5 * spam_probs[i] * np.log(spam_probs[i] / all_probs[i]) + .5 * (1 - spam_probs[i]) * np.log((1 - spam_probs[i])/(1 - all_probs[i]))) mi = np.array(mi) mi = np.where(np.isnan(mi), 0, mi) return mi PPMI ","date":"2021-02-22T00:00:00Z","permalink":"/ppmi/","title":"PPMI"},{"content":"至少有k个重复字符的最长字串 题目： https://leetcode-cn.com/problems/longest-substring-with-at-least-k-repeating-characters/\n思路： 利用递归，如果s中字符c的数目小于k,则以c作分割，分成的字串再次调用函数形成递归，然后从众多结果中找寻最大长度的。\n代码： class Solution(object): def longestSubstring(self, s, k): if len(s) \u0026lt; k: return 0 for c in set(s): if s.count(c) \u0026lt; k: return max(self.longestSubstring(t, k) for t in s.split(c)) return len(s) ","date":"2021-02-18T00:00:00Z","permalink":"/%E8%87%B3%E5%B0%91%E6%9C%89k%E4%B8%AA%E9%87%8D%E5%A4%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%97%E4%B8%B2/","title":"至少有k个重复字符的最长字串"},{"content":"最长递增子序列 题目： https://leetcode-cn.com/problems/longest-increasing-subsequence/\n思路： 动态规划 定义dp[i]为到nums[i]的最长递增子序列的长度，全部都初始化为1,因为本身就是长度为1的递增子序列\n代码： class Solution: def lengthOfLIS(self, nums: List[int]) -\u0026gt; int: dp = [1 for _ in range(len(nums))] for i in range(1,len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i]: dp[i] = max(dp[i],dp[j]+1) return max(dp) ","date":"2021-02-17T00:00:00Z","permalink":"/%E6%9C%80%E9%95%BF%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97/","title":"最长递增子序列"},{"content":"外观数列 https://leetcode-cn.com/problems/count-and-say/\n这题有意思\n可以打表，不过打表的过程也相当于做出来了\nclass Solution: def countAndSay(self,n: int) -\u0026gt; str: if n == 1: return \u0026#39;1\u0026#39; s = self.countAndSay(n - 1) n,res = 0,\u0026#39;\u0026#39; for ii,ss in enumerate(s): if ss != s[n]: res += str(ii-n) + s[n] n = ii res += str(len(s) - n) + s[-1] return res print(Solution().countAndSay(3)) 思路：\n​ 递归，将上一层计算出来的东西作为迭代对象。\n","date":"2021-02-16T00:00:00Z","permalink":"/%E5%A4%96%E8%A7%82%E6%95%B0%E5%88%97/","title":"外观数列"},{"content":"调用为np.lib.stride_tricks.as_strided() 可以分割一个数组为不同的shape块，有个问题就是什么是strides呢？可以看个例子：\na = np.arange(9, dtype=np.int32).reshape(3,3) print(a) \u0026#39;\u0026#39;\u0026#39; [[0 1 2] [3 4 5] [6 7 8]] \u0026#39;\u0026#39;\u0026#39; print(a.strides) \u0026#39;\u0026#39;\u0026#39; (12, 4) \u0026#39;\u0026#39;\u0026#39; 这里（12， 4）中的12表示在内存中a[n, 0]到a[n+1, 0]跨过多少byte，4表示在内存中a[n, 0]到a[n, 1]跨过多少byte。\n32int需要4byte是众所周知。\n看一下函数的参数：\nnumpy.lib.stride_tricks.as_strided(x, shape=None, strides=None, subok=False, writeable=True) x就是我们要分割的矩阵，可以当做是一个蓝图，shape，strides都是新矩阵的属性，也就是说这个函数按照给定的shape和strides来划分x，返回一个新的矩阵。\n对于X： 如果卷积核的大小是2x2，stride为1，那么就需要把矩阵X转换为包含如下4个小矩阵的新矩阵A：\n很明显A的维度为(2,2,2,2)。\n所以shape可以确定，但strides还不确定：\nA = as_strided(X, shape=(2,2,2,2), strides) 下面确定strides，从图中可以确定最低维的为4，因为所有数据都在X上，所以A的各个维度的跨度都要根据X来确定，而不是A中，以1和4为例子，在X中的距离为12字节，所以现在可以确定后两维：（?,?,12,4）。 再看更高维度: 从X中可以看到，第二维的距离为4。 第一维也不多说，是12。 最后可以strides =（12，4，12，4）。\n这就是整个分析的过程，可以方便卷积操作，不是嘛。\n再看一个例子就结束了，估计以后会忘，记录下来。 意思就是将一个向量拓展成这样的形式，用循环的方法很容易实现：\ndef sliding_stack_py(v, k): \u0026quot;Stack sliding windows of v of length k.\u0026quot; rows = [] for i in range(len(v) - k + 1): rows.append(v[i : (i + k)]) return np.array(rows) 但如果不能使用循环呢，就可以用刚说的这个函数了:\ndef sliding_stack_np(v, k): return np.lib.stride_tricks.as_strided(v, shape=(len(v) - k + 1, k), strides=(v.strides[0], v.strides[0])) 因为原向量是1维的，所以转换后的strides为[4,4]。希望可以帮助理解。\n","date":"2021-02-14T00:00:00Z","permalink":"/as_strided/","title":"as_strided"},{"content":"NER(命名实体识别) 参考：https://www.jianshu.com/p/16e1f6a7aaef\n命名实体识别（Named Entity Recognition，简称NER）是信息提取、问答系统、句法分析、机器翻译等应用领域的重要基础工具，在自然语言处理技术走向实用化的过程中占有重要地位。一般来说，命名实体识别的任务就是识别出待处理文本中三大类（实体类、时间类和数字类）、七小类（人名、机构名、地名、时间、日期、货币和百分比）命名实体。 举个简单的例子，在句子“小明早上8点去学校上课。”中，对其进行命名实体识别，应该能提取信息\n人名：小明，时间：早上8点，地点：学校。\n","date":"2021-02-06T00:00:00Z","permalink":"/ner/","title":"NER"},{"content":"阶乘函数后K个零(首个困难题) 题目： https://leetcode-cn.com/problems/preimage-size-of-factorial-zeroes-function/\n思路： 首先先写个判断阶乘后有多少个零的函数，思路就是找所有相乘的数中因数有5的个数。\n然后再用二分查找，找到有K个0的左界和右界，然后相减即可，就是要找的数目\nclass Solution: def preimageSizeFZF(self, K: int) -\u0026gt; int: return self.findright(K) - self.findleft(K) def whatzero(self,n): dis = 5 res = 0 while dis \u0026lt;= n: res += n // dis dis *= 5 return res def findleft(self,K): mins,maxs = 0,sys.maxsize while (mins \u0026lt; maxs): mid = mins + (maxs-mins) // 2 if self.whatzero(mid) \u0026lt; K: mins = mid + 1 elif self.whatzero(mid) \u0026gt; K: maxs = mid else: maxs = mid return mins def findright(self,K): mins,maxs = 0,sys.maxsize while (mins \u0026lt; maxs): mid = mins + (maxs-mins) // 2 if self.whatzero(mid) \u0026lt; K: mins = mid + 1 elif self.whatzero(mid) \u0026gt; K: maxs = mid else: mins = mid + 1 return maxs 注意这里的最大值要初始化为sys库里的maxsize 用float(“inf”)会返回nan值\n","date":"2021-02-05T00:00:00Z","permalink":"/%E9%98%B6%E4%B9%98%E5%87%BD%E6%95%B0%E5%90%8Ek%E4%B8%AA%E9%9B%B6%E9%A6%96%E4%B8%AA%E5%9B%B0%E9%9A%BE%E9%A2%98/","title":"阶乘函数后K个零(首个困难题)"},{"content":"打家劫舍 打家劫舍I 题目： https://leetcode-cn.com/problems/house-robber/\n思路: 一个简单题，不过踩了特例的坑。。可以暴力解决 也可以动态规划\n代码: 暴力解决\nclass Solution: def rob(nums): if nums == []: return 0 if len(nums) == 1: return nums[0] if len(nums) == 2: return max(nums[0],nums[1]) maxs = [] #max[i]代表到i+1家的最大价钱 maxs.append(nums[0]) maxs.append(nums[1]) for i in range(2,len(nums)): maxs.append(max(maxs[:i-1])+nums[i]) #从头到这家前面的第二家最大的价钱加上这一家的价钱 return max(maxs) 动态规划\nclass Solution: def rob(self, nums: List[int]) -\u0026gt; int: if len(nums) \u0026lt;= 2: return max(nums) dp = [0] * len(nums) dp[0], dp[1] = nums[0], max(nums[0], nums[1]) for i in range(2, len(nums)): dp[i] = max(dp[i-1], dp[i-2] + nums[i]) return max(dp) 打家劫舍II 题目： https://leetcode-cn.com/problems/house-robber-ii/\n思路： 跟上面的题目非常类似，只是加了一个限制条件，就是第一家和最后一家不能同时打劫。\n这里先写一个函数，表示从start 到end 范围里面的最大值，然后在主函数里面进行选择\n如果打劫第一家，就不能打劫最后一家以及不打劫第一家去打劫最后一家，这两者之间的最大值\n代码： class Solution: def rob(self, nums: List[int]) -\u0026gt; int: if len(nums) == 1: return nums[0] return max(self.dp(0,len(nums)-2,nums),self.dp(1,len(nums)-1,nums)) def dp(self,start,end,nums): dp = [0 for _ in range(len(nums)+2)] for i in range(end,start-1,-1): dp[i] = max(dp[i+1],dp[i+2]+nums[i]) return dp[start] ","date":"2021-02-04T00:00:00Z","permalink":"/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/","title":"打家劫舍"},{"content":"import threading import time 简单的创建 def run(n): print(\u0026quot;task\u0026quot;, n) time.sleep(1) print(\u0026#39;2s\u0026#39;) time.sleep(1) print(\u0026#39;1s\u0026#39;) time.sleep(1) print(\u0026#39;0s\u0026#39;) time.sleep(1) if __name__ == \u0026#39;__main__\u0026#39;: t1 = threading.Thread(target=run, args=(\u0026quot;t1\u0026quot;,)) t2 = threading.Thread(target=run, args=(\u0026quot;t2\u0026quot;,)) t1.start() t2.start() 通过类创建 class MyThread(threading.Thread): def __init__(self, n): super(MyThread, self).__init__() # 重构run函数必须要写 self.n = n def run(self): print(\u0026quot;task\u0026quot;, self.n) time.sleep(1) print(\u0026#39;2s\u0026#39;) time.sleep(1) print(\u0026#39;1s\u0026#39;) time.sleep(1) print(\u0026#39;0s\u0026#39;) time.sleep(1) if __name__ == \u0026quot;__main__\u0026quot;: t1 = MyThread(\u0026quot;t1\u0026quot;) t2 = MyThread(\u0026quot;t2\u0026quot;) t1.start() t2.start() 对比没有join()和join()的区别 def run(n): print(\u0026quot;task\u0026quot;, n) time.sleep(1) #此时子线程停1s print(\u0026#39;3\u0026#39;) time.sleep(1) print(\u0026#39;2\u0026#39;) time.sleep(1) print(\u0026#39;1\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: t = threading.Thread(target=run, args=(\u0026quot;t1\u0026quot;,)) t.setDaemon(True) #把子进程设置为守护线程，必须在start()之前设置 t.start() print(\u0026quot;end\u0026quot;) def run(n): print(\u0026quot;task\u0026quot;, n) time.sleep(1) #此时子线程停1s print(\u0026#39;3\u0026#39;) time.sleep(1) print(\u0026#39;2\u0026#39;) time.sleep(1) print(\u0026#39;1\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: t = threading.Thread(target=run, args=(\u0026quot;t1\u0026quot;,)) t.setDaemon(True) #把子进程设置为守护线程，必须在start()之前设置 t.start() t.join() # 设置主线程等待子线程结束 print(\u0026quot;end\u0026quot;) 锁的应用 def run(n, semaphore): semaphore.acquire() #加锁 time.sleep(1) print(\u0026quot;run the thread:%s\\n\u0026quot; % n) semaphore.release() #释放 if __name__ == \u0026#39;__main__\u0026#39;: num = 0 semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(22): t = threading.Thread(target=run, args=(\u0026quot;t-%s\u0026quot; % i, semaphore)) t.start() while threading.active_count() != 1: pass # print threading.active_count() else: print(\u0026#39;--\u0026#39;) 事件类 event = threading.Event() def lighter(): count = 0 event.set() #初始值为绿灯 while True: if 5 \u0026lt; count \u0026lt;=10 : event.clear() # 红灯，清除标志位 print(\u0026quot;\\33[41;1mred light is on...\\033[0m\u0026quot;) elif count \u0026gt; 10: event.set() # 绿灯，设置标志位 count = 0 else: print(\u0026quot;\\33[42;1mgreen light is on...\\033[0m\u0026quot;) time.sleep(1) count += 1 def car(name): while True: if event.is_set(): #判断是否设置了标志位（绿灯） print(\u0026quot;[%s] running...\u0026quot;%name) time.sleep(1) else: print(\u0026quot;[%s] sees red light,waiting...\u0026quot;%name) event.wait()#如果变为绿灯 print(\u0026quot;[%s] green light is on,start going...\u0026quot;%name) light = threading.Thread(target=lighter,) light.start() car = threading.Thread(target=car,args=(\u0026quot;MINI\u0026quot;,)) car.start() queue队列 import threading import queue,time q=queue.Queue(maxsize=10) def Producer(name): count=1 while True: q.put(\u0026quot;骨头 %s\u0026quot;%count) print(\u0026quot;{}生产了骨头\u0026quot;.format(name),count) count+=1 time.sleep(1) def Consumer(name): while True: print(\u0026quot;[%s] 取到 [%s] 并且吃了它。。。\u0026quot;%(name,q.get())) time.sleep(1) p=threading.Thread(target=Producer,args=(\u0026#39;wlb\u0026#39;,)) c=threading.Thread(target=Consumer,args=(\u0026quot;dog\u0026quot;,)) c1=threading.Thread(target=Consumer,args=(\u0026quot;cat\u0026quot;,)) p.start() c.start() c1.start() 互斥锁 由于线程之间是进行随机调度，并且每个线程可能只执行n条执行之后，当多个线程同时修改同一条数据时可能会出现脏数据，所以，出现了线程锁，即同一时刻允许一个线程执行操作。线程锁用于锁定资源，你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时，任何一个锁都可以锁这个资源，就好比你用不同的锁都可以把相同的一个门锁住是一个道理。\n由于线程之间是进行随机调度，如果有多个线程同时操作一个对象，如果没有很好地保护该对象，会造成程序结果的不可预期，我们也称此为“线程不安全”。\n为了方式上面情况的发生，就出现了互斥锁(Lock)\nfrom threading import Thread,Lock import os,time def work(): global n lock.acquire() temp=n time.sleep(0.1) n=temp-1 lock.release() if __name__ == \u0026#39;__main__\u0026#39;: lock=Lock() n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() 信号量 互斥锁同时只允许一个线程更改数据，而Semaphore是同时允许一定数量的线程更改数据 ，比如厕所有3个坑，那最多只允许3个人上厕所，后面的人只能等里面有人出来了才能再进去。\nimport threading import time def run(n, semaphore): semaphore.acquire() #加锁 time.sleep(1) print(\u0026quot;run the thread:%s\\n\u0026quot; % n) semaphore.release() #释放 if __name__ == \u0026#39;__main__\u0026#39;: num = 0 semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(22): t = threading.Thread(target=run, args=(\u0026quot;t-%s\u0026quot; % i, semaphore)) t.start() while threading.active_count() != 1: pass # print threading.active_count() else: print(\u0026#39;--\u0026#39;) GIL（Global Interpreter Lock）全局解释器锁 在非python环境中，单核情况下，同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中，无论有多少核，同时只能执行一个线程。究其原因，这就是由于GIL的存在导致的。\nGIL的全称是Global Interpreter Lock(全局解释器锁)，来源是python设计之初的考虑，为了数据安全所做的决定。某个线程想要执行，必须先拿到GIL，我们可以把GIL看作是“通行证”，并且在一个python进程中，GIL只有一个。拿不到通行证的线程，就不允许进入CPU执行。GIL只在cpython中才有，因为cpython调用的是c语言的原生线程，所以他不能直接操作cpu，只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。\nPython多线程的工作过程： python在使用多线程的时候，调用的是c语言的原生线程。\n拿到公共数据 申请gil python解释器调用os原生线程 os操作cpu执行运算 当该线程执行时间到后，无论运算是否已经执行完，gil都被要求释放 进而由其他进程重复上面的过程 等其他进程执行完后，又会切换到之前的线程（从他记录的上下文继续执行），整个过程是每个线程执行自己的运算，当执行时间到就进行切换（context switch）。 ","date":"2021-01-21T00:00:00Z","permalink":"/thread/","title":"thread"},{"content":"丑数系列 1.丑数 题目： https://leetcode-cn.com/problems/ugly-number/\n思路： 就是让这个数字不断地除以2.3.5 如果最后变成了1 就说明是个丑数\n代码： class Solution: def isUgly(self, num: int) -\u0026gt; bool: if num\u0026lt;=-231 or num\u0026gt;=231-1: return False while num \u0026gt;1: if num %2 == 0: num=int(num/2) elif num %3 ==0: num =int(num/3) elif num %5 ==0: num=int(num/5) else: break if num == 1: return True else: return False 丑数II 题目： https://leetcode-cn.com/problems/ugly-number-ii/\n思路： 利用三指针，维护i2 i3 i5三个指针分别指向2 3 5\n代码： class Solution: def nthUglyNumber(self, n: int) -\u0026gt; int: res = [1] # 先初始化为1 i2 = i3 = i5 = 0 # 初始化为0 for i in range(1,n): mins = min(res[i2]*2,res[i3]*3,res[i5]*5) # 从小到大找 res.append(mins) if res[i] == res[i2]*2: i2 += 1 if res[i] == res[i3]*3: i3 += 1 if res[i] == res[i5]*5: i5 += 1 return res[n-1] ","date":"2021-01-18T00:00:00Z","permalink":"/%E4%B8%91%E6%95%B0%E7%B3%BB%E5%88%97/","title":"丑数系列"},{"content":"梯度提升决策树(GBDT) GBDT(Gradient Boosting Decision Tree)是一种迭代的决策树算法，由多棵决策树组成，所有树的结论累加起来作为最终答案。\n回归树 选择最优切分变量j与切分点s：遍历变量j，对规定的切分变量j扫描切分点s，选择使下式得到最小 值时的(j,s)对。其中Rm是被划分的输入空间， \\(\\mathrm{cm}\\) 是空间Rm对应的固定输出值。\n\\[ \\min_{j, s}\\left[\\min_{c_{1}} \\sum_{x_{i} \\in R_{i}(j, s)}\\left(y_{i}-c_{1}\\right)^{2}+\\min_{c_{2}} \\sum_{x_{i} \\in R_{i}(j, s)}\\left(y_{i}-c_{1}\\right)^{2}\\right] \\]\n用选定的(j,s)对，划分区域并决定相应的输出值\n\\[ \\begin{gathered} R_{1}(j, s)=\\{x \\mid x^{(j)} \\leq s\\}, R_{2}(j, s)=\\{x \\mid x^{(j)}\u0026gt;s\\} \\\\\\\\ \\hat{c}_{m}=\\frac{1}{N_{m}} \\sum_{x_{i} \\in R_{m}(j, s)} y_{i} \\\\\\\\ x \\in R_{m}, m=1,2 \\end{gathered} \\]\n继续对两个子区域调用上述步骤，将输入空间划分为 \\(M\\) 个区域R1,R2,..,Rm，生成决策树。\n\\[ f(x)=\\sum_{m=1}^{M} \\hat{c}_{m} I\\left(x \\epsilon R_{m}\\right) \\]\n当输入空间划分确定时，可以用平方误差来表示回归树对于训练数据的预测方法，用平方误差最小 的准则求解每个单元上的最优输出值。\n提升树 梯度提升树是提升树（Boosting Tree）的一种改进算法，所以在讲梯度提升树之前先来说一下提升树。\n先来个通俗理解：假如有个人30岁，我们首先用20岁去拟合，发现损失有10岁，这时我们用6岁去拟合剩下的损失，发现差距还有4岁，第三轮我们用3岁拟合剩下的差距，差距就只有一岁了。如果我们的迭代轮数还没有完，可以继续迭代下面，每一轮迭代，拟合的岁数误差都会减小。最后将每次拟合的岁数加起来便是模型输出的结果。\n提升树算法: (1) 初始化 \\(f_{0}(x)=0\\) (2) 对 \\(m=1,2, \\ldots, M\\) (a) 计算残差\n\\[ r_{m i}=y_{i}-f_{m-1}(x), i=1,2, \\ldots, N \\]\n为什么残差是这种形式？ 当采用平方误差损失函数时,\n\\[ L(y, f(x))=(y-f(x))^2 \\]\n其损失变为\n\\[ \\begin{aligned} L\\left(y, f_{m-1}(x)+T\\left(x ; \\Theta_m\\right)\\right) \u0026amp;=\\left[y-f_{m-1}(x)-T\\left(x ; \\Theta_m\\right)\\right]^2 \\\\\\\\ \u0026amp;=\\left[r-T\\left(x ; \\Theta_m\\right)\\right]^2 \\end{aligned} \\]\n这里,\n\\[ r=y-f_{m-1}(x) \\]\n也可以用上一步的残差（定义的上一步的标签）减去拟合上一步残差的回归树。即：\n\\[ r_{mi} = r_{(m-1)i} - h_{m-1}(x) \\]\n易证明这两种形式等价。 (b) 拟合残差 \\(r_{m i}\\) 学习一个回归树，得到 \\(h_{m}(x)\\) (c) 更新 \\(f_{m}(x)=f_{m-1}+h_{m}(x)\\) (3) 得到回归问题提升树\n\\[ f_{M}(x)=\\sum_{m=1}^{M} h_{m}(x) \\]\nGBDT GBDT与提升树不同的是GBDT使用负梯度来近似残差。\nGBDT算法: (1) 初始化弱学习器\n\\[ f_{0}(x)=\\arg \\min_{c} \\sum_{i=1}^{N} L\\left(y_{i}, c\\right) \\]\n对 \\(m=1,2, \\ldots, M\\) 有: 对每个样本 \\(i=1,2, \\ldots, N\\) ，计算负梯度，即残差 \\[ r_{i m}=-\\left[\\frac{\\left.\\partial L\\left(y_{i}, f\\left(x_{i}\\right)\\right)\\right)}{\\partial f\\left(x_{i}\\right)}\\right]_{f(x)=f_{m-1}(x)} \\]\n一般回归的损失函数就是均方误差，但缺点是对于outlier比较敏感。 因此可以选择使用MAE或者Huber loss。所以说负梯度并不等于残差，损失函数选择MSE的时候才可以划等号。\n将上步得到的残差作为样本新的真实值，并将数据 \\(\\left(x_{i}, r_{i m}\\right), i=1,2, . . N\\) 作为下棵树的训练数据，得到一颗新的回归树 \\(f_{m}(x)\\) 其对应的叶子节点区域为 \\(R_{j m}, j=1,2, \\ldots, J\\) 。其中 \\(J\\) 为回归树的叶子节点的个数。 对叶子区域 \\(j=1,2, . . J\\) 计算最佳拟合值 \\[ \\Upsilon_{j m}=\\underbrace{\\arg \\min}_{\\Upsilon} \\sum_{x_{i} \\in R_{j m}} L\\left(y_{i}, f_{m-1}\\left(x_{i}\\right)+\\Upsilon\\right) \\]\n也可以理解为：\n\\[ \\Upsilon_{jm} = \\underbrace{\\arg \\min}_{\\Upsilon} \\sum_{x_i \\in R_{jm}} L(r_{im}, \\Upsilon) \\]\n损失函数L为MSE时，就与回归树的构建类似，最佳拟合值就是划分的叶子节点的均值。可以简单理解为提升树和GBDT的区别就是计算残差的方式不同。\n更新强学习器 \\[ f_{m}(x)=f_{m-1}(x)+\\sum_{j=1}^{J} \\Upsilon_{j m} I\\left(x \\in R_{j m}\\right) \\]\n得到最终学习器 \\[ f(x)=f_{M}(x)=f_{0}(x)+\\sum_{m=1}^{M} \\sum_{j=1}^{J} \\Upsilon_{j m} I\\left(x \\in R_{j m}\\right) \\]\n实例可以看参考里面。\n与梯度下降算法的关系 代码 import numpy as np class RegressionTree: def __init__(self, max_depth=2, min_samples_split=2): self.max_depth = max_depth self.min_samples_split = min_samples_split self.tree = {} def fit(self, X, y): self.X = X self.y = y self.n_features = X.shape[1] self.n_samples = X.shape[0] self.tree = self._build_tree(X, y) def predict(self, X): return np.array([self._predict(inputs) for inputs in X]) def _build_tree(self, X, y, depth=0): m = X.shape[0] n = X.shape[1] # 1. 终止条件 if m \u0026lt;= self.min_samples_split or depth \u0026gt;= self.max_depth: return self._leaf(y) # 2. 找到最优分裂特征和特征值 feature, value = self._best_split(X, y) # 3. 构建子树 left_idx, right_idx = self._split(X, feature, value) left = self._build_tree(X[left_idx, :], y[left_idx], depth + 1) right = self._build_tree(X[right_idx, :], y[right_idx], depth + 1) return {\u0026quot;feature\u0026quot;: feature, \u0026quot;value\u0026quot;: value, \u0026quot;left\u0026quot;: left, \u0026quot;right\u0026quot;: right} def _leaf(self, y): return np.mean(y) def _best_split(self, X, y): m = X.shape[0] n = X.shape[1] min_mse = np.inf best_feature = None best_value = None for feature in range(n): values = np.unique(X[:, feature]) for value in values: y1 = y[X[:, feature] \u0026lt; value] y2 = y[X[:, feature] \u0026gt;= value] mse = np.mean(y1) - np.mean(y2) if mse \u0026lt; min_mse: min_mse = mse best_feature = feature best_value = value return best_feature, best_value def _split(self, X, feature, value): left_idx = np.argwhere(X[:, feature] \u0026lt; value).flatten() right_idx = np.argwhere(X[:, feature] \u0026gt;= value).flatten() return left_idx, right_idx class GBDT: def __init__(self, n_estimators=10, learning_rate=0.1, max_depth=3): self.n_estimators = n_estimators self.learning_rate = learning_rate self.max_depth = max_depth self.trees = [] def fit(self, X, y): y_pred = np.zeros_like(y, dtype=np.float) for i in range(self.n_estimators): tree = RegressionTree(self.max_depth) tree.fit(X, -self.gradient(y, y_pred)) y_pred += self.learning_rate * tree.predict(X) self.trees.append(tree) def predict(self, X): y_pred = np.zeros((X.shape[0], ), dtype=np.float) for tree in self.trees: y_pred += self.learning_rate * tree.predict(X) return y_pred def gradient(self, y_true, y_pred): return y_true - y_pred 为什么xgboost/gbdt在调参时为什么树的深度很少就能达到很高的精度？ Boosting主要关注降低偏差，因此Boosting能基于泛化性能相当弱的学习器构建出很强的集成；Bagging主要关注降低方差，因此它在不剪枝的决策树、神经网络等学习器上效用更为明显。\n对于Bagging算法来说，由于我们会并行地训练很多不同的分类器的目的就是降低这个方差(variance) ,因为采用了相互独立的基分类器多了以后，h的值自然就会靠近。所以对于每个基分类器来说，目标就是如何降低这个偏差（bias),所以我们会采用深度很深甚至不剪枝的决策树。\n对于Boosting来说，每一步我们都会在上一轮的基础上更加拟合原数据，所以可以保证偏差（bias）,所以对于每个基分类器来说，问题就在于如何选择variance更小的分类器，即更简单的分类器，所以我们选择了深度很浅的决策树。 ## 参考\nhttps://blog.csdn.net/zpalyq110/article/details/79527653 https://zhuanlan.zhihu.com/p/280222403\n","date":"2021-01-06T00:00:00Z","permalink":"/gbdt/","title":"GBDT"},{"content":"两两交换链表中的节点 题目： https://leetcode-cn.com/problems/swap-nodes-in-pairs/\n思路: 先把第二位储存起来，然后将后面的递归操作后，再把第二位指向第一位，完成换位\n代码： # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: #假设为[1,2,3,4] def swapPairs(self, head: ListNode) -\u0026gt; ListNode: if not head or not head.next: #递归出口 return head newnode = head.next #储存第二位2 head.next = self.swapPairs(head.next.next) #此时为[1,4,3] newnode.next = head #[2,1,4,3] return newnode ","date":"2021-01-03T00:00:00Z","permalink":"/%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9/","title":"两两交换链表中的节点"},{"content":"种花问题（新年快乐!2021第一题） 新年快乐！2021年第一题，每日一题！希望2021年LC和github可以全绿！加油！\nhttps://leetcode-cn.com/problems/can-place-flowers/\n代码如下：\nclass Solution: def canPlaceFlowers(self, flowerbed: List[int], n: int) -\u0026gt; bool: flowerbed = [0] + flowerbed + [0] for i in range(1,len(flowerbed)-1): if flowerbed[i-1] == 0 and flowerbed[i] == 0 and flowerbed[i+1] == 0: n -= 1 flowerbed[i] = 1 return n \u0026lt;= 0 思路很暴力，就是三个0在一起就可以插进去。。\n主要是边界问题，这里构造了两个边界\n新的一年开开心心，完成自己的目标，让自己更优秀！\n","date":"2021-01-01T00:00:00Z","permalink":"/%E7%A7%8D%E8%8A%B1%E9%97%AE%E9%A2%98%E6%96%B0%E5%B9%B4%E5%BF%AB%E4%B9%902021%E7%AC%AC%E4%B8%80%E9%A2%98/","title":"种花问题（新年快乐!2021第一题）"},{"content":"零钱兑换 https://leetcode-cn.com/problems/coin-change/\n以我目前的水平做出来有点吃力，看了思路才做出来\nclass Solution: def coinChange(self, coins: List[int], amount: int) -\u0026gt; int: dp = [float(\u0026#39;inf\u0026#39;)] * (amount + 1) dp[0] = 0 for i in range(amount+1): for coin in coins: # if i \u0026gt;= coin: dp[i] = min(dp[i],dp[i-coin]+1) return -1 if (dp[-1] == float(\u0026quot;inf\u0026quot;)) else dp[-1] 伪代码如下\n# 伪码框架 def coinChange(coins: List[int], amount: int): # 定义：要凑出金额 n，至少要 dp(n) 个硬币 def dp(n): # 做选择，选择需要硬币最少的那个结果 for coin in coins: res = min(res, 1 + dp(n - coin)) return res # 题目要求的最终结果是 dp(amount) return dp(amount) ","date":"2020-10-10T00:00:00Z","permalink":"/%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2/","title":"零钱兑换"},{"content":"去除重复字母 一开始看到题目感觉挺简单的，没想到对现在的我挺有难度。。\nhttps://leetcode-cn.com/problems/remove-duplicate-letters/\n#1 class Solution: def removeDuplicateLetters(s: str): res = \u0026quot;\u0026quot; while s: #用递归也可以 loc = min(map(s.rindex,s)) #s.rindex是返回列表各值最后出现的索引 求这个最小的索引 a = min(s[:loc+1]) #求字典序最小的 res += a s = s[s.index(a):].replace(a,\u0026quot;\u0026quot;) #把已经加入的和与其重复的都去掉了 return res #2 #遍历字符串，压入栈，如果遇到比栈顶小的元素且当前字符后面还有与栈顶相同的元素时，移除栈顶元素 class Solution: def removeDuplicateLetters(s: str) -\u0026gt; str: stack = [] for i, t in enumerate(s): if t in stack: continue while stack !=[] and t \u0026lt; stack[-1] and s[i:].find(stack[-1]) != -1: stack.pop() stack.append(t) return \u0026quot;\u0026quot;.join(stack) 两个方法，第二个方法更好想点。第一个方法是copy的\n","date":"2020-09-02T00:00:00Z","permalink":"/%E5%8E%BB%E9%99%A4%E9%87%8D%E5%A4%8D%E5%AD%97%E6%AF%8D/","title":"去除重复字母"}]