<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Siyu Chen</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://sssssimonk.github.io/</id>
  <link href="https://sssssimonk.github.io/" rel="alternate"/>
  <link href="https://sssssimonk.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Siyu Chen</rights>
  <subtitle>
    <![CDATA[AI Research Notes & Engineering Logs]]>
  </subtitle>
  <title>Siyu Chen</title>
  <updated>2026-07-01T14:00:00.000Z</updated>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="Blog" scheme="https://sssssimonk.github.io/categories/Blog/"/>
    <category term="AI" scheme="https://sssssimonk.github.io/tags/AI/"/>
    <category term="Research" scheme="https://sssssimonk.github.io/tags/Research/"/>
    <category term="Notes" scheme="https://sssssimonk.github.io/tags/Notes/"/>
    <content>
      <![CDATA[<p>这个博客会主要记录我对 AI 论文、算法思想和工程实践的理解。</p><p>我希望每篇文章都尽量回答三个问题：</p><ol><li>这篇论文或技术真正解决了什么问题？</li><li>它的核心假设、方法设计和实验结论是否站得住？</li><li>如果把它放到真实系统或后续研究里，它给我的启发是什么？</li></ol><p>后续内容会以论文阅读、方法拆解、实验复现、工程经验和研究想法为主。相比只摘录结论，我会更重视自己的判断：哪里简单有效，哪里可能被过度包装，哪里值得继续追。</p><p>一个简单的公式示例：</p><p>$$<br>\mathcal{L}(\theta) &#x3D; - \sum_{t&#x3D;1}^{T} \log p_\theta(y_t \mid y_{&lt;t}, x)<br>$$</p><p>一个代码块示例：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">summarize_claim</span>(<span class="hljs-params">paper</span>):<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;problem&quot;</span>: paper.problem,<br>        <span class="hljs-string">&quot;method&quot;</span>: paper.method,<br>        <span class="hljs-string">&quot;evidence&quot;</span>: paper.experiments,<br>        <span class="hljs-string">&quot;my_take&quot;</span>: paper.limitations,<br>    &#125;<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://sssssimonk.github.io/2026/07/01/about-this-blog/</id>
    <link href="https://sssssimonk.github.io/2026/07/01/about-this-blog/"/>
    <published>2026-07-01T14:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这个博客会主要记录我对 AI]]>
    </summary>
    <title>关于这个博客</title>
    <updated>2026-07-01T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="后训练笔记" scheme="https://sssssimonk.github.io/categories/%E5%90%8E%E8%AE%AD%E7%BB%83%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="后训练" scheme="https://sssssimonk.github.io/tags/%E5%90%8E%E8%AE%AD%E7%BB%83/"/>
    <category term="RLVR" scheme="https://sssssimonk.github.io/tags/RLVR/"/>
    <category term="OPD" scheme="https://sssssimonk.github.io/tags/OPD/"/>
    <category term="蒸馏" scheme="https://sssssimonk.github.io/tags/%E8%92%B8%E9%A6%8F/"/>
    <category term="On-Policy" scheme="https://sssssimonk.github.io/tags/On-Policy/"/>
    <content>
      <![CDATA[<p>这一篇整理 OPD，也就是 On-Policy Distillation。</p><p>OPD 是近期后训练里很值得关注的一条路线。它和 GRPO &#x2F; RLVR 的目标有些相似：都想让模型在自己的生成分布上变强。但它给训练信号的方式不一样。</p><p>GRPO 通常是：</p><blockquote><p>student 自己生成 response，然后 verifier &#x2F; reward model 给一个 scalar reward。</p></blockquote><p>OPD 更像是：</p><blockquote><p>student 自己生成 response，然后 teacher 在这些 on-policy responses 上给 dense supervision。</p></blockquote><p>所以 OPD 的关键词是：on-policy、teacher、distillation、dense signal。</p><h2 id="1-普通-distillation-是什么"><a href="#1-普通-distillation-是什么" class="headerlink" title="1. 普通 distillation 是什么"></a>1. 普通 distillation 是什么</h2><p>Knowledge distillation 的基本想法是：用强 teacher model 指导弱 student model。</p><p>最简单的 supervised distillation 是让 student 模仿 teacher 的输出：</p><p>$$<br>\mathcal{L}<em>{KD} &#x3D; D</em>{KL}(p_{\mathrm{teacher}}(\cdot \mid x) || p_{\mathrm{student}}(\cdot \mid x))<br>$$</p><p>在 LLM 里，可以让 teacher 生成高质量 answers，然后 student 做 SFT。</p><p>这类方法的优点是简单。</p><p>但它通常是 off-policy 的：训练数据来自 teacher 或静态数据集，不一定来自 student 当前会生成的分布。</p><p>这会带来一个问题：student 在推理时遇到的状态，可能和训练时看到的不一样。</p><h2 id="2-为什么需要-on-policy"><a href="#2-为什么需要-on-policy" class="headerlink" title="2. 为什么需要 on-policy"></a>2. 为什么需要 on-policy</h2><p>语言模型生成是自回归的。</p><p>一旦前面某个 token 偏了，后面的上下文就变成 student 自己制造出来的分布。</p><p>如果训练时只看 teacher 的完美轨迹，student 没有学会在自己的错误轨迹上恢复。</p><p>这就是 distribution shift。</p><p>On-policy 的意思是：训练数据来自当前 student policy。</p><p>OPD 会让 student 先 rollout，生成它自己会生成的 responses，然后 teacher 在这些 responses 或中间 token 上提供监督。</p><p>直觉上：</p><blockquote><p>不要只教学生看标准答案，也要在学生自己的解题过程上指出应该怎么走。</p></blockquote><p>这点和 RL 类似。PPO&#x2F;GRPO 也是在当前 policy 的 outputs 上学习。</p><p>区别在于，RL 用 reward；OPD 用 teacher distribution。</p><h2 id="3-RLVR-的-sparse-reward-问题"><a href="#3-RLVR-的-sparse-reward-问题" class="headerlink" title="3. RLVR 的 sparse reward 问题"></a>3. RLVR 的 sparse reward 问题</h2><p>以数学题为例。</p><p>GRPO 可能只知道最终答案是否正确：</p><p>$$<br>r(y) \in {0,1}<br>$$</p><p>如果答案错了，模型不知道哪一步错。</p><p>如果答案对了，模型也不知道中间哪些 token 真的关键。</p><p>这种 scalar reward 很稀疏。</p><p>OPD 希望用 teacher 提供更密集的信号。比如在 student rollout 的每个 token 位置，teacher 都可以给出概率分布或 log probability。</p><p>这样 student 不只是知道整段 response 好不好，而是能看到 teacher 在每个位置上更倾向什么。</p><h2 id="4-OPD-的基本流程"><a href="#4-OPD-的基本流程" class="headerlink" title="4. OPD 的基本流程"></a>4. OPD 的基本流程</h2><p>一个简化的 OPD 流程可以写成：</p><ol><li>从 prompt dataset 里取一批 prompts</li><li>student policy $\pi_\theta$ 生成 responses</li><li>teacher model $\pi_T$ 对这些 on-policy responses 计算 token-level supervision</li><li>student 用 distillation loss 更新</li></ol><p>如果 student 生成：</p><p>$$<br>y &#x3D; (y_1, y_2, …, y_T)<br>$$</p><p>teacher 可以在每个位置给：</p><p>$$<br>\log \pi_T(y_t \mid x, y_{&lt;t})<br>$$</p><p>student 则优化自己在这些 on-policy tokens 上的行为。</p><p>这和纯 SFT 不一样。SFT 的 response 通常来自数据集；OPD 的 response 来自 student 当前 policy。</p><h2 id="5-Reverse-KL-的直觉"><a href="#5-Reverse-KL-的直觉" class="headerlink" title="5. Reverse KL 的直觉"></a>5. Reverse KL 的直觉</h2><p>OPD 论文里经常会出现 KL objective。</p><p>Forward KL 和 reverse KL 的行为不一样。</p><p>Forward KL：</p><p>$$<br>D_{KL}(p_T || p_\theta)<br>$$</p><p>倾向于让 student 覆盖 teacher 的分布。</p><p>Reverse KL：</p><p>$$<br>D_{KL}(p_\theta || p_T)<br>$$</p><p>倾向于让 student 避开 teacher 认为低概率的区域。</p><p>在生成模型里，reverse KL 往往更 mode-seeking。它会鼓励 student 更集中地落到 teacher 支持的高质量区域。</p><p>这对后训练很有吸引力，因为 student 的 on-policy rollout 可能包含很多质量一般的 token。Teacher 的 dense signal 可以把它往更好的区域拉。</p><h2 id="6-OPD-和-GRPO-的核心差别"><a href="#6-OPD-和-GRPO-的核心差别" class="headerlink" title="6. OPD 和 GRPO 的核心差别"></a>6. OPD 和 GRPO 的核心差别</h2><p>GRPO 的训练信号通常是 response-level reward。</p><p>比如：</p><p>$$<br>A_i &#x3D; \frac{r_i - \mu}{\sigma}<br>$$</p><p>它告诉模型：这一组 responses 里哪个更好。</p><p>OPD 的训练信号来自 teacher 对 token distribution 的判断。</p><p>它告诉模型：在 student 自己走到的上下文里，teacher 会怎么分布概率。</p><p>所以两者的区别是：</p><ul><li>GRPO：用 scalar reward 做 outcome-driven learning</li><li>OPD：用 teacher probabilities 做 dense behavior shaping</li></ul><p>这也是 OPD 可能更高效的原因。Scalar reward 只在终点给信号，teacher distribution 可以在每一步给信号。</p><h2 id="7-OPD-和-SFT-的核心差别"><a href="#7-OPD-和-SFT-的核心差别" class="headerlink" title="7. OPD 和 SFT 的核心差别"></a>7. OPD 和 SFT 的核心差别</h2><p>SFT 是在静态数据上模仿。</p><p>OPD 是在 student 当前 rollout 上蒸馏。</p><p>如果 student 已经接近 teacher，SFT 可能足够。</p><p>如果 student 会生成很多偏离 teacher 的中间状态，OPD 更有价值，因为 teacher 可以在这些状态上给反馈。</p><p>这有点像老师批改学生自己的草稿，而不是只给学生看范文。</p><h2 id="8-Online-OPD-的成本"><a href="#8-Online-OPD-的成本" class="headerlink" title="8. Online OPD 的成本"></a>8. Online OPD 的成本</h2><p>标准 OPD 的一个问题是成本。</p><p>训练时需要 student rollout，然后 teacher 对这些 rollouts 计算 supervision。</p><p>如果 teacher 很大，就需要 live teacher inference server。</p><p>这会带来：</p><ul><li>推理成本高</li><li>系统复杂</li><li>训练吞吐低</li><li>teacher 服务和 student 训练要同步</li><li>多机异步时容易出现 stale samples</li></ul><p>所以 OPD 虽然训练信号更密集，但系统上并不轻。</p><p>这也是 Lightning OPD、StableOPD、f-OPD 这些工作的背景。</p><h2 id="9-Lightning-OPD：把-online-teacher-变成-offline"><a href="#9-Lightning-OPD：把-online-teacher-变成-offline" class="headerlink" title="9. Lightning OPD：把 online teacher 变成 offline"></a>9. Lightning OPD：把 online teacher 变成 offline</h2><p>Lightning OPD 关注一个问题：能不能不用 live teacher server？</p><p>它的思路是 offline on-policy distillation。</p><p>核心是预计算 teacher log-probabilities，然后训练时复用。</p><p>但这里有一个关键条件：teacher consistency。</p><p>如果 SFT 阶段和 OPD 阶段使用的 teacher 不一致，或者 teacher 对 student rollouts 的分布不一致，就可能引入不可消除的 gradient bias。</p><p>Lightning OPD 的一个重要结论是：只要保持 teacher consistency，offline OPD 可以接近 standard OPD 的 optimum，同时显著降低系统成本。</p><p>这对个人或学术实验很重要。因为 live teacher server 是很高的门槛。</p><h2 id="10-StableOPD：长度膨胀和重复崩溃"><a href="#10-StableOPD：长度膨胀和重复崩溃" class="headerlink" title="10. StableOPD：长度膨胀和重复崩溃"></a>10. StableOPD：长度膨胀和重复崩溃</h2><p>OPD 也有自己的失败模式。</p><p>一个问题是 length inflation。</p><p>Student on-policy rollouts 可能越来越长，训练目标又在这些 rollouts 上蒸馏，最后导致长而重复的输出变多。</p><p>如果 responses 被截断，训练数据里会出现大量 truncated trajectories。这会造成 biased gradient signal。</p><p>StableOPD 这类方法尝试用 reference-based divergence constraint 和 rollout mixture distillation 稳定训练。</p><p>直觉上，它在防止 student 被自己的坏 rollout 分布拖走。</p><p>这说明 OPD 虽然不用 scalar reward，但仍然有 policy drift 问题。</p><h2 id="11-f-OPD：异步训练里的-freshness"><a href="#11-f-OPD：异步训练里的-freshness" class="headerlink" title="11. f-OPD：异步训练里的 freshness"></a>11. f-OPD：异步训练里的 freshness</h2><p>大规模训练通常不是完全同步的。</p><p>Rollout 生成、teacher 计算、student 更新可能是异步执行。</p><p>这会带来 stale sample 问题。</p><p>一个样本生成时对应的是旧 student policy，但真正训练时 student 已经更新过很多步。</p><p>Teacher supervision 的上下文也可能变旧。</p><p>f-OPD 把这种偏差拆成 rollout drift 和 supervision drift，并引入 freshness score 来判断样本是否还可靠。</p><p>直觉上：</p><blockquote><p>样本不是越多越好，旧到一定程度的样本会偏离 on-policy 目标。</p></blockquote><p>这对 long-horizon agentic tasks 尤其重要。任务越长，轨迹越复杂，staleness 的影响越大。</p><h2 id="12-OPD-适合什么任务"><a href="#12-OPD-适合什么任务" class="headerlink" title="12. OPD 适合什么任务"></a>12. OPD 适合什么任务</h2><p>OPD 适合这些场景：</p><ul><li>有强 teacher model</li><li>Student 需要在自己的生成分布上学习</li><li>Sparse reward 太弱</li><li>希望 token-level dense supervision</li><li>任务轨迹较长，单一 scalar reward 不够</li></ul><p>比如数学 reasoning、代码生成、视频定位、agentic tool use，都可能适合 OPD。</p><p>但 OPD 不是所有场景都合适。</p><p>如果没有强 teacher，OPD 的上限会受限。</p><p>如果 teacher 推理成本太高，系统负担很重。</p><p>如果 student rollout 质量太差，teacher supervision 可能长期在很差的状态上补救，训练效率不一定高。</p><h2 id="13-OPD-和-RL-能不能结合"><a href="#13-OPD-和-RL-能不能结合" class="headerlink" title="13. OPD 和 RL 能不能结合"></a>13. OPD 和 RL 能不能结合</h2><p>可以。</p><p>OPD 和 RLVR 不是互斥关系。</p><p>一种可能是先用 OPD 做 dense behavior shaping，让 student 接近 teacher 的高质量 reasoning 分布。</p><p>再用 RLVR 针对最终正确率做强化。</p><p>另一种可能是在 RL 过程中加入 teacher regularization，防止模型为了 reward 走向奇怪分布。</p><p>也可以把 OPD 看成一种替代 sparse reward 的中间路线：它不像 SFT 那么离线，也不像 RL 那么依赖 scalar reward。</p><h2 id="14-OPD-的风险"><a href="#14-OPD-的风险" class="headerlink" title="14. OPD 的风险"></a>14. OPD 的风险</h2><p>第一，teacher bias。</p><p>Student 会继承 teacher 的偏好和错误。</p><p>第二，teacher ceiling。</p><p>如果目标任务需要超过 teacher 的能力，单纯 OPD 很难突破 teacher。</p><p>第三，计算成本。</p><p>Teacher log-probs 对大模型来说很贵。</p><p>第四，length &#x2F; repetition instability。</p><p>On-policy rollouts 可能产生越来越长、越来越重复的样本。</p><p>第五，staleness。</p><p>异步训练时，rollout 和 supervision 可能过期。</p><p>所以 OPD 不是“比 RL 更简单”的万能方法。它只是把问题从 reward design 转移到 teacher supervision 和系统效率上。</p><h2 id="15-读-OPD-论文时看什么"><a href="#15-读-OPD-论文时看什么" class="headerlink" title="15. 读 OPD 论文时看什么"></a>15. 读 OPD 论文时看什么</h2><p>我会看这些问题：</p><ul><li>Teacher model 是什么</li><li>Student model 是什么</li><li>Rollouts 来自当前 student 还是旧 policy</li><li>Teacher supervision 是 token-level 还是 response-level</li><li>用 forward KL 还是 reverse KL</li><li>是否需要 live teacher server</li><li>是否 offline 预计算 teacher log-probs</li><li>如何处理 length inflation</li><li>如何处理 stale samples</li><li>是否和 GRPO &#x2F; PPO 在同等 compute 下比较</li><li>推理时是否真的提升 greedy performance</li></ul><p>尤其要注意 compute。OPD 如果性能提升但 teacher 成本巨大，实际价值要重新评估。</p><h2 id="16-小结"><a href="#16-小结" class="headerlink" title="16. 小结"></a>16. 小结</h2><p>OPD 可以理解成一条介于 SFT 和 RLVR 之间的后训练路线。</p><p>SFT 在静态数据上模仿。</p><p>GRPO 在 on-policy samples 上用 scalar reward 学。</p><p>OPD 在 on-policy samples 上用 teacher dense supervision 学。</p><p>我的理解是：</p><blockquote><p>OPD 的价值在于，它保留了 on-policy 学习的分布匹配，又用 teacher 分布缓解了 sparse reward 的问题。</p></blockquote><p>但它的难点也很明确：teacher 成本、teacher consistency、长度膨胀、异步 staleness。</p><p>如果后面 reasoning &#x2F; coding &#x2F; agentic post-training 继续发展，OPD 这条线很可能会越来越重要。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Hinton et al., <a href="https://arxiv.org/abs/1503.02531">Distilling the Knowledge in a Neural Network</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li><li>Wu et al., <a href="https://arxiv.org/abs/2604.13010">Lightning OPD: Efficient Post-Training for Large Reasoning Models with Offline On-Policy Distillation</a></li><li>Luo et al., <a href="https://arxiv.org/abs/2604.08527">Demystifying OPD: Length Inflation and Stabilization Strategies for Large Language Models</a></li><li>Chen et al., <a href="https://arxiv.org/abs/2605.17862">f-OPD: Stabilizing Long-Horizon On-Policy Distillation with Freshness-Aware Control</a></li><li>Li et al., <a href="https://arxiv.org/abs/2602.02994">Video-OPD: Efficient Post-Training of Multimodal Large Language Models for Temporal Video Grounding via On-Policy Distillation</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/11/29/post-training-notes-05-opd/</id>
    <link href="https://sssssimonk.github.io/2025/11/29/post-training-notes-05-opd/"/>
    <published>2025-11-29T14:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 OPD，也就是 On-Policy Distillation。</p>
<p>OPD 是近期后训练里很值得关注的一条路线。它和 GRPO &#x2F; RLVR]]>
    </summary>
    <title>后训练笔记 05：OPD，On-Policy Distillation</title>
    <updated>2025-11-29T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="后训练笔记" scheme="https://sssssimonk.github.io/categories/%E5%90%8E%E8%AE%AD%E7%BB%83%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="GRPO" scheme="https://sssssimonk.github.io/tags/GRPO/"/>
    <category term="后训练" scheme="https://sssssimonk.github.io/tags/%E5%90%8E%E8%AE%AD%E7%BB%83/"/>
    <category term="DAPO" scheme="https://sssssimonk.github.io/tags/DAPO/"/>
    <category term="GSPO" scheme="https://sssssimonk.github.io/tags/GSPO/"/>
    <category term="KTO" scheme="https://sssssimonk.github.io/tags/KTO/"/>
    <category term="偏好优化" scheme="https://sssssimonk.github.io/tags/%E5%81%8F%E5%A5%BD%E4%BC%98%E5%8C%96/"/>
    <content>
      <![CDATA[<p>这一篇整理 GRPO 的变体，以及 KTO。</p><p>这里需要先区分一下：KTO 不是 GRPO 的变体。它更像 DPO 同一类的 direct alignment 方法。之所以放在这一篇，是因为它属于“PPO&#x2F;GRPO 之外的后训练目标改造”，而且经常和 DPO、IPO、ORPO 放在一起比较。</p><p>所以这一篇分成两条线：</p><ul><li>GRPO 变体：围绕 RLVR 里的 group sampling、advantage、clipping、sequence-level 更新做改造</li><li>KTO：围绕 preference data 的形式做改造，从 pairwise preference 变成 binary feedback</li></ul><h2 id="1-GRPO-的基本问题"><a href="#1-GRPO-的基本问题" class="headerlink" title="1. GRPO 的基本问题"></a>1. GRPO 的基本问题</h2><p>GRPO 的基础想法很清楚：同一个 prompt 采样多个 responses，用组内 reward 相对值估计 advantage。</p><p>但真实训练里会遇到几个问题。</p><p>第一，组内 reward 没有差异。</p><p>如果一组全对或全错，标准差接近 0，训练信号很弱。</p><p>第二，reward 太稀疏。</p><p>数学题只有最终答案对错，代码题只有测试通过与否。中间推理过程没有细粒度反馈。</p><p>第三，token-level clipping 可能浪费训练信号。</p><p>有些 token 的 ratio 被 clip 后，梯度贡献变小甚至消失。</p><p>第四，长度会膨胀。</p><p>模型可能生成越来越长的 reasoning，因为长输出更容易覆盖更多尝试，也可能更容易骗过某些 reward。</p><p>第五，MoE 模型 RL 更不稳定。</p><p>RL 更新会影响 routing、专家激活和序列分布，sequence-level 稳定性变得更重要。</p><p>GRPO 变体基本都在修这些问题。</p><h2 id="2-Dr-GRPO：重新看-normalization"><a href="#2-Dr-GRPO：重新看-normalization" class="headerlink" title="2. Dr.GRPO：重新看 normalization"></a>2. Dr.GRPO：重新看 normalization</h2><p>GRPO 的 advantage 常写成：</p><p>$$<br>A_i &#x3D; \frac{r_i - \mu}{\sigma}<br>$$</p><p>其中 $\mu$ 和 $\sigma$ 是同一组 responses 的 reward 均值和标准差。</p><p>这个标准化看起来很自然，但它也会改变训练信号强度。</p><p>如果组内 reward 差异很小，$\sigma$ 很小，更新可能被放大或不稳定。</p><p>如果组内全同，$\sigma$ 接近 0，训练信号又会失效。</p><p>Dr.GRPO 这类方法的核心思路，是重新审视 group normalization 是否真的合理，尤其是在二元 reward 场景里。</p><p>二元 reward 下，一组 responses 的 reward 只可能是 0 或 1。此时标准差本质上反映的是这个 prompt 下样本是否有分歧。</p><p>如果全对或全错，没有分歧，就没有可学习的相对信号。</p><p>如果一半对一半错，分歧最大，训练信号最强。</p><p>这说明 GRPO 的学习重点其实被 group disagreement 控制了。</p><h2 id="3-DAPO：dynamic-sampling-和-clipping"><a href="#3-DAPO：dynamic-sampling-和-clipping" class="headerlink" title="3. DAPO：dynamic sampling 和 clipping"></a>3. DAPO：dynamic sampling 和 clipping</h2><p>DAPO 可以理解成对 GRPO 训练细节的进一步修补。</p><p>它关注两个问题：</p><ul><li>哪些 prompts &#x2F; groups 值得训练</li><li>policy ratio 的 clipping 怎样更合理</li></ul><p>如果一个 group 全对或全错，继续训练价值低。Dynamic sampling 会更关注那些能产生有效差异的样本。</p><p>这和人刷题有点像。</p><p>太简单的题已经都会，太难的题全不会，最有训练价值的是“有时会、有时不会”的题。</p><p>对于 LLM RLVR，也是这样。能让同一个 prompt 下的 samples 产生分歧，才有足够的相对学习信号。</p><p>Clipping 的问题则是：固定 clip range 可能不适合所有 token、所有阶段。模型训练初期和后期需要的更新幅度不同，高置信 token 和低置信 token 也不同。</p><p>所以 DAPO 这类方法会尝试让 sampling 和 clipping 更动态。</p><h2 id="4-GSPO：从-token-level-到-sequence-level"><a href="#4-GSPO：从-token-level-到-sequence-level" class="headerlink" title="4. GSPO：从 token-level 到 sequence-level"></a>4. GSPO：从 token-level 到 sequence-level</h2><p>GSPO 是 Group Sequence Policy Optimization。</p><p>它的一个重要变化是把优化从 token-level ratio 推向 sequence-level ratio。</p><p>在语言模型里，reward 很多时候是 response-level 的。数学答案对不对，是整段 response 的结果；代码能不能过测试，也是整段 response 的结果。</p><p>如果 reward 是 sequence-level，但优化时过度依赖 token-level ratio，就可能出现不匹配。</p><p>GSPO 的想法是：既然 reward 是整段序列的结果，就直接在 sequence level 处理 importance ratio、clipping 和优化。</p><p>这对 MoE 模型尤其重要。因为 MoE 的 token-level routing 更复杂，token-level 更新可能带来不稳定。Sequence-level objective 更贴近最终任务评价，也可能更稳定。</p><p>可以这样理解：</p><blockquote><p>GRPO 问的是“这组回答里哪个更好”；GSPO 进一步问“整段回答作为一个整体，应该怎样被加强或削弱”。</p></blockquote><h2 id="5-DCPO：dynamic-clipping"><a href="#5-DCPO：dynamic-clipping" class="headerlink" title="5. DCPO：dynamic clipping"></a>5. DCPO：dynamic clipping</h2><p>DCPO 关注的是 GRPO 里的 zero gradient 和 clipping 问题。</p><p>在 PPO&#x2F;GRPO 类目标里，clipping 是为了防止 policy 更新过猛。</p><p>但 clipping 太强会损失训练信号。</p><p>如果很多 token 的 ratio 都被 clip，模型即使生成了有价值的 response，也不能有效学习。</p><p>DCPO 的思路是动态调整 clipping 策略，让不同 token 或不同训练阶段有更合适的更新空间。</p><p>它还关注 reward standardization 的问题，希望提升 response-level 样本利用率。</p><p>核心不是“clip 越少越好”，而是：</p><blockquote><p>该限制的地方限制，该学习的地方不要过早截断。</p></blockquote><h2 id="6-DRA-GRPO：把-diversity-纳入-reward"><a href="#6-DRA-GRPO：把-diversity-纳入-reward" class="headerlink" title="6. DRA-GRPO：把 diversity 纳入 reward"></a>6. DRA-GRPO：把 diversity 纳入 reward</h2><p>GRPO 采样一组 responses，但如果这些 responses 高度相似，组内比较的价值有限。</p><p>DRA-GRPO 这类方法关注 diversity。</p><p>它希望不同 reasoning paths 不要因为最终 reward 相同就被完全等价对待。</p><p>比如两条解法都错，但一条接近正确思路，另一条完全胡乱推理；或者两条都对，但一条更简洁，另一条绕远路。单纯 0&#x2F;1 reward 分不出来。</p><p>Diversity-aware reward adjustment 试图让训练信号不仅看正确性，也看候选之间的语义差异和探索价值。</p><p>这对 reasoning 很重要。因为模型不是只需要记住一个答案，而是要学习更可靠的解题路径。</p><h2 id="7-GRPO-变体的共同主线"><a href="#7-GRPO-变体的共同主线" class="headerlink" title="7. GRPO 变体的共同主线"></a>7. GRPO 变体的共同主线</h2><p>这些方法名字很多，但不要被缩写带跑。</p><p>它们基本在改四件事。</p><p>第一，采样。</p><p>哪些 prompts 要采，采几个 responses，如何过滤全对&#x2F;全错。</p><p>第二，advantage。</p><p>组内 reward 怎么归一化，是否使用标准差，是否引入 difficulty &#x2F; diversity。</p><p>第三，clipping。</p><p>policy ratio 怎么限制，token-level 还是 sequence-level，固定还是动态。</p><p>第四，reward。</p><p>只看最终答案，还是加入格式、长度、过程、diversity、verifier confidence。</p><p>理解这四个维度，比记住每个方法缩写更有用。</p><h2 id="8-KTO：为什么不是-DPO-的简单替代"><a href="#8-KTO：为什么不是-DPO-的简单替代" class="headerlink" title="8. KTO：为什么不是 DPO 的简单替代"></a>8. KTO：为什么不是 DPO 的简单替代</h2><p>DPO 需要 preference pair：</p><p>$$<br>(x, y_w, y_l)<br>$$</p><p>也就是同一个 prompt 下，一个 chosen response，一个 rejected response。</p><p>但真实产品数据经常不是这样。</p><p>用户可能只是点了赞或踩：</p><p>$$<br>(x, y, \mathrm{desirable})<br>$$</p><p>或者：</p><p>$$<br>(x, y, \mathrm{undesirable})<br>$$</p><p>没有配对 rejected response。</p><p>KTO 的动机就是：能不能直接用这种 binary feedback 做 alignment？</p><p>KTO 全称是 Kahneman-Tversky Optimization，借用了 prospect theory 里的损失厌恶、参考点等想法。</p><h2 id="9-KTO-的-implied-reward"><a href="#9-KTO-的-implied-reward" class="headerlink" title="9. KTO 的 implied reward"></a>9. KTO 的 implied reward</h2><p>KTO 也使用 policy 和 reference 的 log-ratio 来定义 implied reward：</p><p>$$<br>r_\theta(x,y) &#x3D; \beta \log \frac{\pi_\theta(y \mid x)}{\pi_{\mathrm{ref}}(y \mid x)}<br>$$</p><p>这个量表示：相对于 reference model，当前 policy 对这个 response 的偏好提高了多少。</p><p>如果 $y$ 是 desirable，应该提高它的 implied reward。</p><p>如果 $y$ 是 undesirable，应该降低它的 implied reward。</p><p>和 DPO 不同，KTO 不需要同一个 prompt 下同时有 chosen 和 rejected。它只需要知道某个 response 是好还是坏。</p><p>这在真实数据里更容易获得。</p><h2 id="10-KTO-的参考点"><a href="#10-KTO-的参考点" class="headerlink" title="10. KTO 的参考点"></a>10. KTO 的参考点</h2><p>KTO 不只是把好样本推高、坏样本压低。</p><p>它引入 reference point，类似 prospect theory 里人们不是看绝对收益，而是看相对某个参考点的 gain&#x2F;loss。</p><p>可以粗略理解成：</p><blockquote><p>对 desirable response，希望它比参考点更好；对 undesirable response，希望它比参考点更差。</p></blockquote><p>这种设计让 KTO 更适合不成对的二元反馈。</p><p>但它也带来新问题：desirable 和 undesirable 数据分布是否匹配？好样本和坏样本是否来自相同类型 prompt？类别比例是否平衡？</p><p>如果点赞数据和点踩数据分布差很多，模型可能学到数据分布偏差，而不是偏好本身。</p><h2 id="11-DPO-和-KTO-的区别"><a href="#11-DPO-和-KTO-的区别" class="headerlink" title="11. DPO 和 KTO 的区别"></a>11. DPO 和 KTO 的区别</h2><p>DPO 学的是 pairwise preference。</p><p>它看的是：</p><blockquote><p>对同一个 prompt，$y_w$ 应该比 $y_l$ 更可能。</p></blockquote><p>KTO 学的是 binary desirability。</p><p>它看的是：</p><blockquote><p>这个 prompt-response pair 是 desirable 还是 undesirable。</p></blockquote><p>DPO 的信号更强，因为同一个 prompt 下直接比较两个 responses。</p><p>KTO 的数据更容易收集，因为不需要配对。</p><p>所以两者不是谁完全替代谁，而是数据形态不同。</p><p>如果有高质量 pairwise preference，DPO 很自然。</p><p>如果只有线上 thumbs up &#x2F; thumbs down，KTO 更合适。</p><h2 id="12-KTO-和-GRPO-的区别"><a href="#12-KTO-和-GRPO-的区别" class="headerlink" title="12. KTO 和 GRPO 的区别"></a>12. KTO 和 GRPO 的区别</h2><p>KTO 不是 RLVR。</p><p>它不需要对同一个 prompt 采样一组 responses，也不依赖 verifier 给 reward。</p><p>它更接近 DPO 的 direct alignment。</p><p>GRPO 适合数学、代码这种可验证任务。</p><p>KTO 适合真实用户反馈、偏好稀疏、难以构造 pairwise comparison 的场景。</p><p>所以不要把 KTO 放进 GRPO 变体里。它应该放在 direct preference optimization 这条线。</p><h2 id="13-读-GRPO-变体论文时看什么"><a href="#13-读-GRPO-变体论文时看什么" class="headerlink" title="13. 读 GRPO 变体论文时看什么"></a>13. 读 GRPO 变体论文时看什么</h2><p>我会看这些问题：</p><ul><li>它改的是 sampling、advantage、clipping，还是 reward？</li><li>是否仍然需要 group sampling？</li><li>是否处理全对&#x2F;全错 group？</li><li>是否从 token-level 改到 sequence-level？</li><li>是否控制 length inflation？</li><li>是否在 greedy decoding 下提升，而不是只在多采样下提升？</li><li>对 MoE 模型是否稳定？</li><li>训练成本是否增加？</li></ul><p>很多变体会在 benchmark 上提升，但可能用了更复杂的采样、筛选、reward shaping。要看清楚收益来自算法本身，还是额外工程。</p><h2 id="14-读-KTO-论文时看什么"><a href="#14-读-KTO-论文时看什么" class="headerlink" title="14. 读 KTO 论文时看什么"></a>14. 读 KTO 论文时看什么</h2><p>我会看这些问题：</p><ul><li>数据是 pairwise preference 还是 binary feedback？</li><li>desirable &#x2F; undesirable 是否平衡？</li><li>好坏样本是否来自同一 prompt 分布？</li><li>Reference model 是什么？</li><li>是否和 DPO 在同等数据条件下比较？</li><li>是否对真实线上反馈更有优势？</li><li>是否出现过度压制某类回答的问题？</li></ul><p>KTO 的价值不只是 loss 公式，而是它放宽了数据要求。</p><p>在真实系统里，二元反馈通常比 pairwise preference 更容易拿到。</p><h2 id="15-小结"><a href="#15-小结" class="headerlink" title="15. 小结"></a>15. 小结</h2><p>GRPO 变体主要在修 RLVR 训练信号的问题：采样、advantage、clipping、sequence-level 稳定性。</p><p>KTO 则是在修偏好数据形态的问题：从成对偏好变成二元反馈。</p><p>我的理解是：</p><blockquote><p>GRPO 变体关心“怎么从一组 sampled responses 里学得更稳”；KTO 关心“没有 chosen&#x2F;rejected pair 时，怎么从好&#x2F;坏反馈里学”。</p></blockquote><p>这两条线都属于后训练，但不要混成一类。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Shao et al., <a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li><li>Zheng et al., <a href="https://arxiv.org/abs/2507.18071">Group Sequence Policy Optimization</a></li><li>Yang et al., <a href="https://arxiv.org/abs/2509.02333">DCPO: Dynamic Clipping Policy Optimization</a></li><li>Chen et al., <a href="https://arxiv.org/abs/2505.09655">DRA-GRPO: Exploring Diversity-Aware Reward Adjustment</a></li><li>Ethayarajh et al., <a href="https://arxiv.org/abs/2402.01306">KTO: Model Alignment as Prospect Theoretic Optimization</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/11/22/post-training-notes-04-grpo-variants-and-kto/</id>
    <link href="https://sssssimonk.github.io/2025/11/22/post-training-notes-04-grpo-variants-and-kto/"/>
    <published>2025-11-22T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 GRPO 的变体，以及 KTO。</p>
<p>这里需要先区分一下：KTO 不是 GRPO 的变体。它更像 DPO 同一类的 direct alignment 方法。之所以放在这一篇，是因为它属于“PPO&#x2F;GRPO 之外的后训练目标改造”，而且经常和]]>
    </summary>
    <title>后训练笔记 04：GRPO 变体与 KTO</title>
    <updated>2025-11-22T13:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="后训练笔记" scheme="https://sssssimonk.github.io/categories/%E5%90%8E%E8%AE%AD%E7%BB%83%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="DPO" scheme="https://sssssimonk.github.io/tags/DPO/"/>
    <category term="GRPO" scheme="https://sssssimonk.github.io/tags/GRPO/"/>
    <category term="RLHF" scheme="https://sssssimonk.github.io/tags/RLHF/"/>
    <category term="后训练" scheme="https://sssssimonk.github.io/tags/%E5%90%8E%E8%AE%AD%E7%BB%83/"/>
    <category term="RLVR" scheme="https://sssssimonk.github.io/tags/RLVR/"/>
    <category term="PPO" scheme="https://sssssimonk.github.io/tags/PPO/"/>
    <content>
      <![CDATA[<p>这一篇整理 PPO、DPO 和 GRPO。</p><p>这三个方法经常一起出现，但它们解决的问题不完全一样。</p><p>PPO 是经典 RLHF 路线里的 policy optimization 方法。</p><p>DPO 是直接从偏好对里学习，不显式训练 reward model，也不跑在线 RL。</p><p>GRPO 是 reasoning model 里很常见的 RLVR 方法，用同一个 prompt 下的一组 responses 做相对比较，省掉 value model。</p><p>可以先抓住一句话：</p><blockquote><p>PPO、DPO、GRPO 的核心区别，不是公式长得像不像，而是训练信号从哪里来。</p></blockquote><h2 id="1-三种训练信号"><a href="#1-三种训练信号" class="headerlink" title="1. 三种训练信号"></a>1. 三种训练信号</h2><p>PPO 的训练信号来自 reward model 或 verifier，再加 value model 估计 advantage。</p><p>DPO 的训练信号来自 preference pair，也就是 chosen response 和 rejected response。</p><p>GRPO 的训练信号来自 group relative reward：同一个 prompt 采样多个 responses，看谁在组内更好。</p><p>这三者的结构可以粗略写成：</p><ul><li>PPO：prompt -&gt; policy rollout -&gt; reward&#x2F;value&#x2F;reference -&gt; policy update</li><li>DPO：prompt + chosen&#x2F;rejected -&gt; policy&#x2F;reference log-ratio -&gt; preference loss</li><li>GRPO：prompt -&gt; sample group -&gt; rewards -&gt; group advantage -&gt; policy update</li></ul><p>如果只看名字，很容易觉得都是“对齐算法”。但从工程角度看，它们需要的模型、数据、采样方式完全不同。</p><h2 id="2-PPO：经典-RLHF-路线"><a href="#2-PPO：经典-RLHF-路线" class="headerlink" title="2. PPO：经典 RLHF 路线"></a>2. PPO：经典 RLHF 路线</h2><p>PPO 原本是通用强化学习算法，后来被用在 RLHF 里。</p><p>在 LLM RLHF 中，通常会有四个模型角色：</p><ul><li>Policy model：正在训练的模型</li><li>Reference model：冻结的参考模型，用来限制 policy drift</li><li>Reward model：给 response 打分</li><li>Value model：估计 baseline &#x2F; value</li></ul><p>流程大概是：</p><ol><li>policy 根据 prompt 生成 response</li><li>reward model 给 response 打分</li><li>reference model 计算 KL penalty</li><li>value model 估计 baseline</li><li>用 PPO 更新 policy</li></ol><p>PPO 的目标不是简单让 reward 最大，而是在 reward 和分布约束之间平衡。</p><h2 id="3-PPO-clipping-的直觉"><a href="#3-PPO-clipping-的直觉" class="headerlink" title="3. PPO clipping 的直觉"></a>3. PPO clipping 的直觉</h2><p>PPO 的核心是限制 policy 更新幅度。</p><p>定义 probability ratio：</p><p>$$<br>r_t(\theta) &#x3D; \frac{\pi_\theta(a_t \mid s_t)}{\pi_{\theta_{\mathrm{old}}}(a_t \mid s_t)}<br>$$</p><p>如果 $r_t(\theta)$ 太大，说明新 policy 比旧 policy 更倾向于采取这个 action。</p><p>PPO 会把 ratio clip 到一个范围里：</p><p>$$<br>\mathrm{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)<br>$$</p><p>目标函数可以写成：</p><p>$$<br>\mathcal{L}_{PPO} &#x3D; \mathbb{E}_t[\min(r_t(\theta)A_t, \mathrm{clip}(r_t(\theta),1-\epsilon,1+\epsilon)A_t)]<br>$$</p><p>直觉上，PPO 在说：</p><blockquote><p>如果一个 action 好，可以提高它的概率，但不要一次提高太多；如果一个 action 差，可以降低它的概率，但也不要一次降太多。</p></blockquote><p>语言模型是很脆弱的分布。更新过猛，模型可能学到 reward model 的漏洞，或者输出风格突然崩掉。</p><h2 id="4-PPO-为什么复杂"><a href="#4-PPO-为什么复杂" class="headerlink" title="4. PPO 为什么复杂"></a>4. PPO 为什么复杂</h2><p>PPO 的问题不是理论上不能用，而是系统上很重。</p><p>它通常需要：</p><ul><li>在线采样 responses</li><li>reward model 推理</li><li>reference model 推理</li><li>value model 训练或推理</li><li>advantage estimation</li><li>KL 控制</li><li>多轮 policy update</li></ul><p>这对显存、吞吐、工程稳定性都不友好。</p><p>尤其是大模型场景下，policy 本身已经很大，再加 reward&#x2F;value&#x2F;reference，训练链路会非常重。</p><p>所以后面出现了很多“简化 PPO”的路线：DPO 直接避开 RL；GRPO 去掉 value model；OPD 用 teacher dense supervision 替代 sparse reward。</p><h2 id="5-DPO：直接从偏好对学习"><a href="#5-DPO：直接从偏好对学习" class="headerlink" title="5. DPO：直接从偏好对学习"></a>5. DPO：直接从偏好对学习</h2><p>DPO 的出发点很漂亮。</p><p>经典 RLHF 里，人类偏好先训练 reward model，再用 PPO 优化 policy。</p><p>DPO 认为：如果 reward model 和最优 policy 之间存在对应关系，那能不能跳过 reward model，直接从偏好数据推导 policy loss？</p><p>DPO 数据通常是：</p><ul><li>prompt $x$</li><li>preferred response $y_w$</li><li>rejected response $y_l$</li></ul><p>DPO 不需要在线采样。它直接用这些 preference pairs 训练 policy。</p><h2 id="6-DPO-的-log-ratio"><a href="#6-DPO-的-log-ratio" class="headerlink" title="6. DPO 的 log-ratio"></a>6. DPO 的 log-ratio</h2><p>DPO 会比较 policy model 和 reference model 对 chosen&#x2F;rejected 的相对偏好。</p><p>核心量是：</p><p>$$<br>\log \frac{\pi_\theta(y_w \mid x)}{\pi_{\mathrm{ref}}(y_w \mid x)} - \log \frac{\pi_\theta(y_l \mid x)}{\pi_{\mathrm{ref}}(y_l \mid x)}<br>$$</p><p>如果这个值变大，说明相对于 reference，当前 policy 更偏向 chosen 而不是 rejected。</p><p>DPO loss 可以写成：</p><p>$$<br>\mathcal{L}<em>{DPO} &#x3D; -\log \sigma \left(\beta \left[\log \frac{\pi</em>\theta(y_w \mid x)}{\pi_{\mathrm{ref}}(y_w \mid x)} - \log \frac{\pi_\theta(y_l \mid x)}{\pi_{\mathrm{ref}}(y_l \mid x)}\right]\right)<br>$$</p><p>这个公式看起来复杂，其实直觉很简单：</p><blockquote><p>好回答的概率应该相对变高，差回答的概率应该相对变低，但变化要以 reference model 为锚。</p></blockquote><p>Reference model 在这里也很重要。它防止 policy 只是盲目拉高 chosen 的绝对概率，而是关注相对 reference 的偏好变化。</p><h2 id="7-DPO-的优点和局限"><a href="#7-DPO-的优点和局限" class="headerlink" title="7. DPO 的优点和局限"></a>7. DPO 的优点和局限</h2><p>DPO 的优点很明显：</p><ul><li>不需要训练 reward model</li><li>不需要在线 RL rollout</li><li>不需要 value model</li><li>训练稳定性通常比 PPO 简单</li><li>工程实现更接近 supervised fine-tuning</li></ul><p>但 DPO 也有局限。</p><p>第一，它依赖 preference pair。真实产品里经常只有 thumbs up &#x2F; thumbs down，而不是成对比较。</p><p>第二，它是离线训练。数据分布如果和当前 policy 差很多，可能不如 on-policy 方法。</p><p>第三，它优化的是偏好排序，不一定直接优化可验证正确率。</p><p>第四，chosen&#x2F;rejected 的质量很关键。如果 rejected 只是很差的回答，模型学到的边界可能很粗；如果 chosen 和 rejected 差异太小，训练信号也可能不稳定。</p><p>所以 DPO 很适合偏好对齐，但不是所有 post-training 问题的终点。</p><h2 id="8-GRPO：从-PPO-到-group-relative-advantage"><a href="#8-GRPO：从-PPO-到-group-relative-advantage" class="headerlink" title="8. GRPO：从 PPO 到 group relative advantage"></a>8. GRPO：从 PPO 到 group relative advantage</h2><p>GRPO 是 Group Relative Policy Optimization。</p><p>它可以看成 PPO 在 reasoning &#x2F; RLVR 场景下的一种简化。</p><p>PPO 需要 value model 来估计 baseline。GRPO 不单独训练 value model，而是对同一个 prompt 采样一组 responses，用组内 reward 的相对关系估计 advantage。</p><p>对一个 prompt $x$，采样 $G$ 个 responses：</p><p>$$<br>{y_1, y_2, …, y_G}<br>$$</p><p>每个 response 得到 reward：</p><p>$$<br>{r_1, r_2, …, r_G}<br>$$</p><p>然后用组内均值和标准差归一化：</p><p>$$<br>A_i &#x3D; \frac{r_i - \mathrm{mean}(r_1,…,r_G)}{\mathrm{std}(r_1,…,r_G)}<br>$$</p><p>这就是 group relative 的意思。</p><h2 id="9-GRPO-为什么适合-reasoning"><a href="#9-GRPO-为什么适合-reasoning" class="headerlink" title="9. GRPO 为什么适合 reasoning"></a>9. GRPO 为什么适合 reasoning</h2><p>Reasoning 任务里，同一个问题可以采样多个解法。</p><p>比如一道数学题，模型生成 8 个 responses，其中 3 个答案对，5 个答案错。</p><p>正确的 responses 得到更高 reward，错误的 responses 得到更低 reward。组内比较后，正确 responses 的 advantage 为正，错误 responses 的 advantage 为负。</p><p>这样就不需要 value model 去预测这个题本身有多难。组内样本直接给出了相对基线。</p><p>这对数学、代码这种可验证任务很自然：</p><ul><li>数学题可以匹配答案</li><li>代码题可以跑测试</li><li>格式题可以规则检查</li></ul><p>所以 GRPO 和 RLVR 很搭。</p><h2 id="10-GRPO-的核心代价"><a href="#10-GRPO-的核心代价" class="headerlink" title="10. GRPO 的核心代价"></a>10. GRPO 的核心代价</h2><p>GRPO 省掉了 value model，但它不是免费。</p><p>它需要对每个 prompt 采样多个 responses。</p><p>如果 group size 是 8，训练时生成成本就明显增加。</p><p>更麻烦的是，如果一组 responses 全对或全错，组内 reward 没有差异，advantage 可能接近 0。</p><p>这意味着没有有效学习信号。</p><p>比如某个题太简单，8 个 response 全对；或者太难，8 个 response 全错。它们都不能很好地区分哪些轨迹更值得加强。</p><p>这也是后面 GRPO 变体要解决的问题之一：怎样让采样、reward、advantage 更有效。</p><h2 id="11-PPO、DPO、GRPO-的关键差别"><a href="#11-PPO、DPO、GRPO-的关键差别" class="headerlink" title="11. PPO、DPO、GRPO 的关键差别"></a>11. PPO、DPO、GRPO 的关键差别</h2><p>可以从四个维度比较。</p><p>第一，是否在线采样。</p><p>PPO 和 GRPO 通常需要在线采样；DPO 不需要。</p><p>第二，是否需要 reward model。</p><p>PPO 通常需要 reward model；GRPO 可以用 verifier 或 reward model；DPO 不显式需要 reward model。</p><p>第三，是否需要 value model。</p><p>PPO 需要；DPO 不需要；GRPO 不需要。</p><p>第四，训练信号粒度。</p><p>PPO 用 reward + value 得到 advantage。</p><p>DPO 用 chosen&#x2F;rejected 的偏好差。</p><p>GRPO 用同一 prompt 下多个 responses 的相对 reward。</p><p>这几个差别决定了工程成本和适用场景。</p><h2 id="12-三者分别适合什么"><a href="#12-三者分别适合什么" class="headerlink" title="12. 三者分别适合什么"></a>12. 三者分别适合什么</h2><p>PPO 适合经典 RLHF 场景：已经有 reward model，希望在线优化 policy，并且有足够工程资源。</p><p>DPO 适合偏好数据充足的场景：有 chosen&#x2F;rejected pairs，希望稳定、低成本地做对齐。</p><p>GRPO 适合可验证 reasoning 场景：数学、代码、逻辑推理，能自动给 reward，并且愿意为每个 prompt 采样多个 responses。</p><p>如果从个人学习角度看，我会这样排序：</p><p>先理解 DPO，因为它最简洁，能帮助理解 reference model 和 log-ratio。</p><p>再理解 PPO，因为它是 RLHF 的经典路线。</p><p>最后理解 GRPO，因为它建立在 RL 直觉上，同时更贴近 DeepSeek-R1 这类 reasoning model。</p><h2 id="13-为什么-DPO-不是-RL，但又和-RLHF-有关系"><a href="#13-为什么-DPO-不是-RL，但又和-RLHF-有关系" class="headerlink" title="13. 为什么 DPO 不是 RL，但又和 RLHF 有关系"></a>13. 为什么 DPO 不是 RL，但又和 RLHF 有关系</h2><p>DPO 经常被放在 RLHF 的替代方法里，但它本身不是在线 RL。</p><p>它不采样当前 policy 的新 outputs 来计算 reward，也不做环境交互。</p><p>但 DPO 和 RLHF 有理论联系：DPO 从 reward model 的隐式形式出发，把偏好优化转成 policy loss。</p><p>所以 DPO 更像是：</p><blockquote><p>把“先学 reward，再优化 policy”的两步合成一个监督式目标。</p></blockquote><p>这也是为什么 DPO 训练起来像 SFT，但作用却更接近偏好对齐。</p><h2 id="14-为什么-GRPO-不是简单版-PPO"><a href="#14-为什么-GRPO-不是简单版-PPO" class="headerlink" title="14. 为什么 GRPO 不是简单版 PPO"></a>14. 为什么 GRPO 不是简单版 PPO</h2><p>GRPO 省掉 value model，但它引入了 group sampling。</p><p>它不是“PPO 少一个模型”这么简单。</p><p>PPO 的 baseline 来自 value model。</p><p>GRPO 的 baseline 来自同一 prompt 下其他 samples。</p><p>这意味着 GRPO 的训练效果很依赖采样分布。如果 samples 没有差异，学习信号就弱。如果 samples 很混乱，advantage 也可能噪声很大。</p><p>所以 GRPO 的关键不只是公式，而是：</p><ul><li>group size 怎么选</li><li>sampling temperature 怎么设</li><li>reward 怎么设计</li><li>是否过滤无效 groups</li><li>如何控制 response length</li><li>KL penalty 怎么加</li></ul><p>这些细节决定了 GRPO 是否稳定。</p><h2 id="15-读-PPO-DPO-GRPO-论文时看什么"><a href="#15-读-PPO-DPO-GRPO-论文时看什么" class="headerlink" title="15. 读 PPO &#x2F; DPO &#x2F; GRPO 论文时看什么"></a>15. 读 PPO &#x2F; DPO &#x2F; GRPO 论文时看什么</h2><p>我会看这些问题：</p><ul><li>数据是离线 preference pair，还是 online rollout？</li><li>Reward 来自人类偏好、reward model，还是 verifier？</li><li>是否需要 value model？</li><li>Reference model 是哪个 checkpoint？</li><li>KL 系数怎么设？</li><li>每个 prompt 采样几个 responses？</li><li>是否使用 length penalty？</li><li>是否报告训练 token 成本？</li><li>提升是 greedy decoding 下提升，还是采样多次后提升？</li><li>有没有看 reward hacking 或长度膨胀？</li></ul><p>很多方法 benchmark 看起来提升，但可能是采样更多、输出更长、筛选更强带来的。读的时候要把训练方法和推理策略分开看。</p><h2 id="16-小结"><a href="#16-小结" class="headerlink" title="16. 小结"></a>16. 小结</h2><p>PPO、DPO、GRPO 是三条不同的后训练路线。</p><p>PPO 是经典 RLHF：强大但重。</p><p>DPO 是直接偏好优化：简单稳定，但依赖 preference pairs。</p><p>GRPO 是 reasoning RL 常用路线：省掉 value model，但依赖 group sampling 和可验证 reward。</p><p>我的理解是：</p><blockquote><p>PPO 学 reward，DPO 学偏好差，GRPO 学组内相对好坏。</p></blockquote><p>后面讲 GRPO 变体时，很多改动都围绕一个问题展开：怎么让这个“组内相对好坏”的训练信号更稳定、更有效。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Schulman et al., <a href="https://arxiv.org/abs/1707.06347">Proximal Policy Optimization Algorithms</a></li><li>Ouyang et al., <a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a></li><li>Rafailov et al., <a href="https://arxiv.org/abs/2305.18290">Direct Preference Optimization: Your Language Model is Secretly a Reward Model</a></li><li>Shao et al., <a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/11/15/post-training-notes-03-ppo-dpo-grpo/</id>
    <link href="https://sssssimonk.github.io/2025/11/15/post-training-notes-03-ppo-dpo-grpo/"/>
    <published>2025-11-15T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 PPO、DPO 和 GRPO。</p>
<p>这三个方法经常一起出现，但它们解决的问题不完全一样。</p>
<p>PPO 是经典 RLHF 路线里的 policy optimization 方法。</p>
<p>DPO 是直接从偏好对里学习，不显式训练]]>
    </summary>
    <title>后训练笔记 03：PPO、DPO 与 GRPO</title>
    <updated>2025-11-15T13:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="后训练笔记" scheme="https://sssssimonk.github.io/categories/%E5%90%8E%E8%AE%AD%E7%BB%83%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="SFT" scheme="https://sssssimonk.github.io/tags/SFT/"/>
    <category term="后训练" scheme="https://sssssimonk.github.io/tags/%E5%90%8E%E8%AE%AD%E7%BB%83/"/>
    <category term="LoRA" scheme="https://sssssimonk.github.io/tags/LoRA/"/>
    <category term="Prefix Tuning" scheme="https://sssssimonk.github.io/tags/Prefix-Tuning/"/>
    <category term="P-Tuning" scheme="https://sssssimonk.github.io/tags/P-Tuning/"/>
    <category term="PEFT" scheme="https://sssssimonk.github.io/tags/PEFT/"/>
    <content>
      <![CDATA[<p>这一篇整理 SFT 和 PEFT。</p><p>在大模型后训练里，SFT 通常是第一步。Base model 经过大规模预训练后，已经学到语言、知识和模式，但它不一定会按指令回答。SFT 的作用就是先把模型拉到一个“能正常当助手”的状态。</p><p>PEFT 则是另一类问题：模型太大，全参数微调成本太高，所以只训练很少一部分参数，让模型适配新任务。</p><p>这篇把两件事放一起看：</p><blockquote><p>SFT 解决“训练什么行为”，PEFT 解决“用多低成本训练”。</p></blockquote><h2 id="1-Base-model-为什么不能直接用"><a href="#1-Base-model-为什么不能直接用" class="headerlink" title="1. Base model 为什么不能直接用"></a>1. Base model 为什么不能直接用</h2><p>Base model 的训练目标通常是 next token prediction：</p><p>$$<br>\max_\theta \sum_t \log p_\theta(x_t \mid x_{&lt;t})<br>$$</p><p>它学的是文本分布。</p><p>如果给 base model 一个问题：</p><blockquote><p>请解释一下 LoRA 是什么。</p></blockquote><p>它可能回答，也可能继续补全网页文本，或者生成一段类似问答数据的续写。问题不一定是它“不知道”，而是它没有被训练成稳定的 instruction-following model。</p><p>SFT 的目标就是把输入输出形式固定下来：</p><p>$$<br>\max_\theta \log \pi_\theta(y \mid x)<br>$$</p><p>其中 $x$ 是 instruction，$y$ 是高质量回答。</p><h2 id="2-SFT-具体学到了什么"><a href="#2-SFT-具体学到了什么" class="headerlink" title="2. SFT 具体学到了什么"></a>2. SFT 具体学到了什么</h2><p>SFT 不只是“灌知识”。</p><p>它主要学这些行为：</p><ul><li>遵循指令</li><li>按对话格式回答</li><li>输出更清晰的结构</li><li>遇到不确定问题时更谨慎</li><li>学会某些任务格式，比如代码解释、数学推理、摘要</li><li>初步形成 reasoning trace 的风格</li></ul><p>比如 base model 可能知道 AdamW，但 SFT model 更可能用分点方式解释 Adam 和 AdamW 的区别。</p><p>这也是为什么 chat model、instruct model、reasoning model 往往都是从同一个 base model 派生出来，但行为差很多。</p><h2 id="3-SFT-的局限"><a href="#3-SFT-的局限" class="headerlink" title="3. SFT 的局限"></a>3. SFT 的局限</h2><p>SFT 本质上是 imitation learning。</p><p>数据是什么样，模型就往什么样学。</p><p>如果数据里回答很模板化，模型也会模板化。</p><p>如果数据里推理过程只是表面解释，模型会学会写“看起来像推理”的文本，但不一定真的更会解题。</p><p>如果数据里有错误，模型会跟着学。</p><p>更关键的是，SFT 不知道两个正确回答哪个更好。</p><p>比如一个回答很简洁，一个回答更完整；一个回答更安全，一个回答更直接。SFT 只能模仿目标答案，不能表达偏好排序。</p><p>所以 SFT 通常只是后训练的起点，后面还需要 DPO、PPO、GRPO、KTO 或 OPD。</p><h2 id="4-Full-fine-tuning-的成本"><a href="#4-Full-fine-tuning-的成本" class="headerlink" title="4. Full fine-tuning 的成本"></a>4. Full fine-tuning 的成本</h2><p>全参数微调会更新模型所有参数。</p><p>对于小模型，这很直接。对于几十亿、几百亿参数模型，成本很高：</p><ul><li>optimizer states 占显存</li><li>gradient 占显存</li><li>checkpoint 很大</li><li>多个任务需要存多份模型</li><li>训练和部署都更重</li></ul><p>Adam 优化器通常需要为每个参数维护一阶、二阶动量，所以显存压力不只是模型参数本身。</p><p>这就是 PEFT 出现的背景：不改全部参数，只训练小模块。</p><h2 id="5-PEFT-的基本想法"><a href="#5-PEFT-的基本想法" class="headerlink" title="5. PEFT 的基本想法"></a>5. PEFT 的基本想法</h2><p>PEFT 是 parameter-efficient fine-tuning。</p><p>核心想法是：</p><blockquote><p>冻结大部分预训练参数，只训练少量可学习参数，让模型适配新任务。</p></blockquote><p>这样做的好处是：</p><ul><li>训练显存低</li><li>存储成本低</li><li>多任务可以共用同一个 base model</li><li>adapter 可以按需加载</li><li>实验迭代更快</li></ul><p>但它也有代价。训练参数少意味着表达空间受限。如果任务和 base model 差距很大，PEFT 可能不如 full fine-tuning。</p><h2 id="6-LoRA：低秩更新"><a href="#6-LoRA：低秩更新" class="headerlink" title="6. LoRA：低秩更新"></a>6. LoRA：低秩更新</h2><p>LoRA 的核心想法是：不要直接训练原始权重矩阵 $W$，而是冻结 $W$，只学习一个低秩更新：</p><p>$$<br>W’ &#x3D; W + \Delta W<br>$$</p><p>其中：</p><p>$$<br>\Delta W &#x3D; BA<br>$$</p><p>如果 $W \in \mathbb{R}^{d \times k}$，那么可以设：</p><p>$$<br>B \in \mathbb{R}^{d \times r}, \quad A \in \mathbb{R}^{r \times k}, \quad r \ll \min(d,k)<br>$$</p><p>训练参数从 $dk$ 变成 $r(d+k)$。</p><p>直觉上，LoRA 假设任务适配所需的权重变化不需要覆盖完整高维空间，而可以落在一个低秩子空间里。</p><p>这不是说模型能力只剩低秩。Base model 的原始参数仍然在，LoRA 只是学习一个附加方向。</p><h2 id="7-LoRA-插在哪里"><a href="#7-LoRA-插在哪里" class="headerlink" title="7. LoRA 插在哪里"></a>7. LoRA 插在哪里</h2><p>LoRA 常插在 Transformer 的线性层上，比如：</p><ul><li>attention 的 $W_Q, W_K, W_V, W_O$</li><li>FFN 的 up projection &#x2F; down projection</li></ul><p>具体插哪里会影响效果和成本。</p><p>只插 attention 层，参数更少，但适配能力可能有限。</p><p>attention 和 FFN 都插，能力更强，但训练参数更多。</p><p>对很多任务来说，LoRA rank、target modules、learning rate、数据质量都会显著影响效果。</p><p>不要把 LoRA 理解成固定魔法。它是一个低成本适配工具，具体效果依赖配置。</p><h2 id="8-LoRA-为什么推理时方便"><a href="#8-LoRA-为什么推理时方便" class="headerlink" title="8. LoRA 为什么推理时方便"></a>8. LoRA 为什么推理时方便</h2><p>LoRA 的更新是：</p><p>$$<br>h &#x3D; Wx + BAx<br>$$</p><p>训练时可以把 $BA$ 当成旁路。</p><p>部署时，如果只服务一个 adapter，可以把 $BA$ 合并回 $W$：</p><p>$$<br>W_{\mathrm{merged}} &#x3D; W + BA<br>$$</p><p>这样推理时不一定增加额外延迟。</p><p>如果要服务多个 adapter，就可以保留 base model，按任务加载不同 LoRA 权重。</p><p>这也是 LoRA 在个人微调和多任务部署中很受欢迎的原因。</p><h2 id="9-Prefix-Tuning：给模型加可学习前缀"><a href="#9-Prefix-Tuning：给模型加可学习前缀" class="headerlink" title="9. Prefix Tuning：给模型加可学习前缀"></a>9. Prefix Tuning：给模型加可学习前缀</h2><p>Prefix tuning 不直接改模型权重，而是在每层 attention 里加入可学习 prefix。</p><p>可以理解成给模型提供一组虚拟的 key&#x2F;value tokens，让后续 token 在 attention 时能读到这些可学习信息。</p><p>它和手写 prompt 不一样。手写 prompt 是离散文本，prefix tuning 学的是连续向量。</p><p>直觉上：</p><blockquote><p>Prefix tuning 不是告诉模型“请你这样回答”，而是在模型内部提供一组可学习上下文。</p></blockquote><p>它的优势是训练参数少。</p><p>缺点是表达能力受限，而且会占用一定上下文或 attention 空间。</p><h2 id="10-Prompt-Tuning-和-P-Tuning"><a href="#10-Prompt-Tuning-和-P-Tuning" class="headerlink" title="10. Prompt Tuning 和 P-Tuning"></a>10. Prompt Tuning 和 P-Tuning</h2><p>Prompt tuning 学的是输入 embedding 前面的 soft prompt。</p><p>如果原输入是：</p><p>$$<br>[x_1, x_2, …, x_n]<br>$$</p><p>prompt tuning 会在前面拼上一组可学习向量：</p><p>$$<br>[p_1, p_2, …, p_m, x_1, x_2, …, x_n]<br>$$</p><p>P-Tuning 则进一步用更复杂的方式生成或组织这些 continuous prompts，比如使用 MLP &#x2F; LSTM 等 prompt encoder。</p><p>它们的共同点是：冻结模型主体，只训练 prompt-like 参数。</p><p>这类方法在早期很重要，但在当前 LLM 微调实践里，LoRA 通常更常见，因为 LoRA 对生成任务更灵活，工程生态也更成熟。</p><h2 id="11-LoRA、Prefix-Tuning、P-Tuning-怎么选"><a href="#11-LoRA、Prefix-Tuning、P-Tuning-怎么选" class="headerlink" title="11. LoRA、Prefix Tuning、P-Tuning 怎么选"></a>11. LoRA、Prefix Tuning、P-Tuning 怎么选</h2><p>可以粗略这样看。</p><p>LoRA 适合需要改模型行为、任务风格、领域适配的场景。它改的是线性层权重的低秩增量，表达能力相对强。</p><p>Prefix tuning 适合希望用少量参数控制模型行为，同时不动主体权重的场景。</p><p>Prompt tuning &#x2F; P-Tuning 更像学习软提示，参数少，但对大模型和复杂生成任务不一定总是最强。</p><p>如果是个人博客里的实践建议，我会优先写：</p><ul><li>小成本实验：LoRA &#x2F; QLoRA</li><li>需要多任务 adapter：LoRA</li><li>只想做轻量控制：prefix &#x2F; prompt tuning</li><li>追求最好效果且资源够：full fine-tuning 或更大规模 post-training</li></ul><h2 id="12-QLoRA：进一步省显存"><a href="#12-QLoRA：进一步省显存" class="headerlink" title="12. QLoRA：进一步省显存"></a>12. QLoRA：进一步省显存</h2><p>QLoRA 是 LoRA 的重要实践扩展。</p><p>它把 base model 量化，比如 4-bit 存储，同时仍然用 LoRA adapter 训练。</p><p>关键点是：base model 不参与梯度更新，只需要在前向&#x2F;反向中提供计算；可训练的是 LoRA 参数。</p><p>这样可以在更小显存上微调较大模型。</p><p>但量化也可能带来数值误差。QLoRA 的效果依赖量化格式、计算精度、rank、数据质量。</p><h2 id="13-SFT-数据比方法更重要"><a href="#13-SFT-数据比方法更重要" class="headerlink" title="13. SFT 数据比方法更重要"></a>13. SFT 数据比方法更重要</h2><p>SFT 和 PEFT 经常被讨论成训练技术，但真正决定结果的通常是数据。</p><p>高质量 SFT 数据要满足：</p><ul><li>指令清楚</li><li>回答准确</li><li>风格一致但不模板化</li><li>覆盖目标任务</li><li>没有大量幻觉</li><li>reasoning trace 不只是装样子</li></ul><p>如果数据质量差，LoRA 再省显存也没用。</p><p>如果数据太窄，模型会过拟合某种格式。</p><p>如果数据里充满“当然可以，下面是……”这种套话，模型会变得很像客服脚本。</p><p>所以 SFT 的核心不是把 loss 跑低，而是把模型行为拉到你想要的区域。</p><h2 id="14-SFT-和后续-RL-DPO-的关系"><a href="#14-SFT-和后续-RL-DPO-的关系" class="headerlink" title="14. SFT 和后续 RL &#x2F; DPO 的关系"></a>14. SFT 和后续 RL &#x2F; DPO 的关系</h2><p>SFT 给模型一个可控起点。</p><p>DPO &#x2F; KTO 进一步从偏好信号中学习什么回答更好。</p><p>PPO &#x2F; GRPO 进一步用 reward 或 verifier 优化结果。</p><p>OPD 则可以用 teacher 对 student 的 on-policy outputs 做 dense supervision。</p><p>如果 SFT 起点太差，后面 RL 可能要花很多成本纠正基础格式问题。</p><p>如果 SFT 起点太强但过度模板化，后面偏好优化可能很难恢复多样性。</p><p>所以 SFT 不是越多越好，而是要给后续训练留下空间。</p><h2 id="15-读-SFT-PEFT-论文时看什么"><a href="#15-读-SFT-PEFT-论文时看什么" class="headerlink" title="15. 读 SFT &#x2F; PEFT 论文时看什么"></a>15. 读 SFT &#x2F; PEFT 论文时看什么</h2><p>我会看这些问题：</p><ul><li>Base model 是什么</li><li>SFT 数据来自哪里</li><li>数据规模和质量如何</li><li>是否包含 reasoning traces</li><li>是 full fine-tuning 还是 PEFT</li><li>LoRA rank 是多少</li><li>LoRA 插在哪些模块</li><li>是否使用 QLoRA</li><li>是否只评估 benchmark，还是也看真实对话质量</li><li>后面有没有接 DPO&#x2F;RL&#x2F;OPD</li></ul><p>尤其要注意：很多报告里“用了 LoRA”不是主要贡献。LoRA 只是训练方式，真正重要的是数据、目标和评估。</p><h2 id="16-小结"><a href="#16-小结" class="headerlink" title="16. 小结"></a>16. 小结</h2><p>SFT 是后训练的入口。它让 base model 从“会续写”变成“会回答”。</p><p>PEFT 是低成本适配工具。LoRA、Prefix Tuning、P-Tuning 都是在减少可训练参数，但它们改模型的方式不同。</p><p>我的理解是：</p><blockquote><p>SFT 决定模型学什么行为，PEFT 决定用多少参数去学这个行为。</p></blockquote><p>后面讲 PPO、DPO、GRPO 时，要一直记住：这些算法通常不是从 raw base model 开始，而是在 SFT 模型基础上继续塑形。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Wei et al., <a href="https://arxiv.org/abs/2109.01652">Finetuned Language Models Are Zero-Shot Learners</a></li><li>Hu et al., <a href="https://arxiv.org/abs/2106.09685">LoRA: Low-Rank Adaptation of Large Language Models</a></li><li>Li and Liang, <a href="https://arxiv.org/abs/2101.00190">Prefix-Tuning: Optimizing Continuous Prompts for Generation</a></li><li>Liu et al., <a href="https://arxiv.org/abs/2110.07602">P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks</a></li><li>Dettmers et al., <a href="https://arxiv.org/abs/2305.14314">QLoRA: Efficient Finetuning of Quantized LLMs</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/11/08/post-training-notes-02-sft-and-peft/</id>
    <link href="https://sssssimonk.github.io/2025/11/08/post-training-notes-02-sft-and-peft/"/>
    <published>2025-11-08T12:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 SFT 和 PEFT。</p>
<p>在大模型后训练里，SFT 通常是第一步。Base model 经过大规模预训练后，已经学到语言、知识和模式，但它不一定会按指令回答。SFT 的作用就是先把模型拉到一个“能正常当助手”的状态。</p>
<p>PEFT]]>
    </summary>
    <title>后训练笔记 02：SFT 与 PEFT</title>
    <updated>2025-11-08T12:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="后训练笔记" scheme="https://sssssimonk.github.io/categories/%E5%90%8E%E8%AE%AD%E7%BB%83%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="强化学习" scheme="https://sssssimonk.github.io/tags/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
    <category term="RLHF" scheme="https://sssssimonk.github.io/tags/RLHF/"/>
    <category term="后训练" scheme="https://sssssimonk.github.io/tags/%E5%90%8E%E8%AE%AD%E7%BB%83/"/>
    <category term="RLVR" scheme="https://sssssimonk.github.io/tags/RLVR/"/>
    <category term="Policy Gradient" scheme="https://sssssimonk.github.io/tags/Policy-Gradient/"/>
    <content>
      <![CDATA[<p>这一篇先整理后训练里会用到的强化学习基础。</p><p>大模型后训练里的 RL 和传统 RL 不太一样。传统 RL 常见场景是 agent 在环境里反复交互，比如机器人控制、游戏、推荐系统。LLM 后训练里的“环境”往往更窄：给一个 prompt，模型生成 response，然后由人类偏好、reward model、规则 verifier、测试用例或者答案匹配器给出反馈。</p><p>所以这里不用先背完整 RL 教材。先抓住一条主线：</p><blockquote><p>后训练里的 RL，本质上是在让模型生成的回答更容易得到高 reward，同时又不能偏离原模型太远。</p></blockquote><p>这句话里有三个关键词：policy、reward、reference。</p><h2 id="1-语言模型里的-policy-是什么"><a href="#1-语言模型里的-policy-是什么" class="headerlink" title="1. 语言模型里的 policy 是什么"></a>1. 语言模型里的 policy 是什么</h2><p>在 RL 里，policy 通常写成：</p><p>$$<br>\pi_\theta(a \mid s)<br>$$</p><p>它表示在状态 $s$ 下采取动作 $a$ 的概率。</p><p>放到语言模型里，可以这样对应：</p><ul><li>状态（state）：当前 prompt 加上已经生成的 tokens</li><li>动作（action）：下一个 token</li><li>policy：语言模型给下一个 token 的概率分布</li></ul><p>如果把完整回答看成一个 token 序列：</p><p>$$<br>y &#x3D; (y_1, y_2, …, y_T)<br>$$</p><p>那么模型生成这个回答的概率是：</p><p>$$<br>\pi_\theta(y \mid x) &#x3D; \prod_{t&#x3D;1}^{T} \pi_\theta(y_t \mid x, y_{&lt;t})<br>$$</p><p>其中 $x$ 是 prompt。</p><p>这也是为什么很多后训练算法会用 log probability：</p><p>$$<br>\log \pi_\theta(y \mid x) &#x3D; \sum_{t&#x3D;1}^{T} \log \pi_\theta(y_t \mid x, y_{&lt;t})<br>$$</p><p>因为整段 response 的概率是 token 概率连乘，直接乘容易数值很小，取 log 后就变成求和。</p><h2 id="2-Token-level-action-和-response-level-action"><a href="#2-Token-level-action-和-response-level-action" class="headerlink" title="2. Token-level action 和 response-level action"></a>2. Token-level action 和 response-level action</h2><p>理论上，LLM 每一步生成 token 都是一个 action。</p><p>但很多 reward 是 response-level 的。比如数学题最后答案对不对、代码是否通过测试、回答是否更符合人类偏好，这些通常是整段回答生成完之后才知道。</p><p>这就带来一个 credit assignment 问题：</p><blockquote><p>如果最终回答错了，到底是哪几个 token 造成的？</p></blockquote><p>PPO 里会尝试用 value model 和 advantage estimation，把整段 reward 分摊到 token-level 更新上。</p><p>GRPO 里则常见做法是对同一个 prompt 采样多个 responses，再用组内 reward 相对大小得到 advantage。</p><p>DPO 更进一步，不显式做在线 RL，而是直接比较 chosen response 和 rejected response 的 log probability。</p><p>这些算法看起来差很多，但都绕不开同一个问题：训练信号怎样从“整段回答好不好”传回到模型参数。</p><h2 id="3-Reward：后训练真正优化的目标"><a href="#3-Reward：后训练真正优化的目标" class="headerlink" title="3. Reward：后训练真正优化的目标"></a>3. Reward：后训练真正优化的目标</h2><p>Reward 可以来自很多地方。</p><p>在人类偏好场景里，reward 可能来自 reward model：</p><p>$$<br>r_\phi(x, y)<br>$$</p><p>它输入 prompt 和 response，输出一个标量分数。</p><p>在 reasoning &#x2F; coding 场景里，reward 经常是 verifiable reward：</p><ul><li>数学题最终答案是否匹配</li><li>代码是否通过单元测试</li><li>格式是否满足要求</li><li>工具调用是否成功</li></ul><p>这类方法常被叫做 RLVR，也就是 reinforcement learning from verifiable rewards。</p><p>RLVR 的优势是 reward 更客观，不需要每个样本都人工标注偏好。</p><p>但它也有明显局限：reward 很稀疏。一个数学题只有最终答案对&#x2F;错，模型并不知道中间哪一步推理值得奖励。</p><p>这也是为什么 reasoning model 训练里经常会关心：</p><ul><li>如何采样更多候选</li><li>如何过滤高质量 reasoning traces</li><li>如何避免只学会猜答案</li><li>如何让 reward 不只看最终答案</li></ul><h2 id="4-Return、value-和-advantage"><a href="#4-Return、value-和-advantage" class="headerlink" title="4. Return、value 和 advantage"></a>4. Return、value 和 advantage</h2><p>在一般 RL 里，return 是从当前时刻往后能拿到的累计 reward：</p><p>$$<br>G_t &#x3D; \sum_{k&#x3D;t}^{T} \gamma^{k-t} r_k<br>$$</p><p>LLM 后训练里，很多任务只有最终 reward，所以可以粗略理解为整段回答的得分。</p><p>Value function 估计的是某个状态下未来能拿到多少 reward：</p><p>$$<br>V^\pi(s) &#x3D; \mathbb{E}_{\pi}[G_t \mid s_t&#x3D;s]<br>$$</p><p>Advantage 衡量的是某个 action 比平均水平好多少：</p><p>$$<br>A_t &#x3D; G_t - V^\pi(s_t)<br>$$</p><p>这个量很重要。</p><p>如果一个回答得分是 0.8，不一定说明它很值得加强。也许这个 prompt 很简单，随便生成都能 0.8。</p><p>如果另一个回答得分是 0.6，也不一定差。也许这个 prompt 很难，大多数回答都是 0.1。</p><p>Advantage 想表达的是：相对于当前状态的预期表现，这个 action &#x2F; response 到底更好还是更差。</p><h2 id="5-Policy-gradient-的直觉"><a href="#5-Policy-gradient-的直觉" class="headerlink" title="5. Policy gradient 的直觉"></a>5. Policy gradient 的直觉</h2><p>Policy gradient 的经典形式可以写成：</p><p>$$<br>\nabla_\theta J(\theta) &#x3D; \underset{\pi_\theta}{\mathbb{E}}[\nabla_\theta \log \pi_\theta(a \mid s) A(s,a)]<br>$$</p><p>放到 LLM 里，可以粗略理解成：</p><blockquote><p>如果某个 response 的 advantage 为正，就提高它的生成概率；如果 advantage 为负，就降低它的生成概率。</p></blockquote><p>但这里有两个细节。</p><p>第一，提高的是整段 response 里 tokens 的 log probability，不是直接给最终答案加分。</p><p>第二，更新不能太大。语言模型是高维分布，一次更新如果把概率分布拉得太远，可能会破坏原来的语言能力。</p><p>这就引出 KL constraint。</p><h2 id="6-KL-constraint：为什么需要-reference-model"><a href="#6-KL-constraint：为什么需要-reference-model" class="headerlink" title="6. KL constraint：为什么需要 reference model"></a>6. KL constraint：为什么需要 reference model</h2><p>后训练里通常会保留一个 reference model，记作 $\pi_{\mathrm{ref}}$。</p><p>它通常是 SFT 后的模型，或者训练开始前的 policy snapshot。</p><p>更新 policy 时，会惩罚当前模型和 reference model 的差异：</p><p>$$<br>D_{KL}(\pi_\theta || \pi_{\mathrm{ref}})<br>$$</p><p>直觉上，KL constraint 在说：</p><blockquote><p>可以朝高 reward 方向走，但不要走到完全不像原来的模型。</p></blockquote><p>没有 KL 约束会有什么问题？</p><p>模型可能为了骗 reward model 生成奇怪模式。比如 reward model 偏好长回答，模型就越来越啰嗦；reward model 偏好自信语气，模型就更容易胡说；verifier 只看最终答案，模型可能学会投机格式。</p><p>所以后训练不是简单最大化 reward，而是：</p><p>$$<br>\max_\theta \mathbb{E}[r(x,y)] - \beta D_{KL}(\pi_\theta || \pi_{\mathrm{ref}})<br>$$</p><p>这里 $\beta$ 控制 reward 优化和保持原模型分布之间的平衡。</p><h2 id="7-On-policy-和-off-policy"><a href="#7-On-policy-和-off-policy" class="headerlink" title="7. On-policy 和 off-policy"></a>7. On-policy 和 off-policy</h2><p>On-policy 指训练数据来自当前 policy。</p><p>Off-policy 指训练数据来自旧 policy、其他模型，或者离线数据集。</p><p>LLM 后训练里，这个区别非常关键。</p><p>如果模型训练时看到的是自己当前会生成的回答，那训练分布和推理分布更接近。这是 on-policy 的优势。</p><p>但 on-policy 代价高，因为训练过程中要不断采样新 responses。</p><p>Off-policy 成本低，可以直接用已有数据训练。但如果数据分布和当前模型差很多，模型学到的更新可能不稳定。</p><p>这就是为什么 PPO &#x2F; GRPO &#x2F; OPD 都强调 on-policy 或接近 on-policy。它们希望模型在自己的生成分布上学习，而不是只模仿静态数据。</p><h2 id="8-RLHF、RLVR-和-preference-optimization"><a href="#8-RLHF、RLVR-和-preference-optimization" class="headerlink" title="8. RLHF、RLVR 和 preference optimization"></a>8. RLHF、RLVR 和 preference optimization</h2><p>这几个词容易混。</p><p>RLHF 通常指 reinforcement learning from human feedback。经典流程是：人类偏好数据 -&gt; reward model -&gt; PPO 优化 policy。</p><p>RLVR 是 reinforcement learning from verifiable rewards。它不一定需要人类偏好，而是用规则、答案、测试来给 reward。</p><p>Preference optimization 更宽，包括 DPO、IPO、KTO 等直接从偏好或二元反馈训练的算法。它们不一定显式训练 reward model，也不一定跑在线 RL。</p><p>可以这样记：</p><ul><li>RLHF：人类偏好先变 reward model，再做 RL</li><li>RLVR：可验证任务直接给 reward</li><li>DPO&#x2F;KTO：从偏好数据直接优化 policy，不显式训练 reward model</li><li>OPD：用 teacher 的 dense supervision 替代 sparse reward</li></ul><p>这些路线都属于后训练，但训练信号来源不一样。</p><h2 id="9-为什么-reasoning-model-更依赖-RL"><a href="#9-为什么-reasoning-model-更依赖-RL" class="headerlink" title="9. 为什么 reasoning model 更依赖 RL"></a>9. 为什么 reasoning model 更依赖 RL</h2><p>SFT 可以让模型学会解题格式，但它本质上是模仿。</p><p>如果 SFT 数据里有高质量 reasoning traces，模型会学到“像这样推理”。但它没有直接被奖励“答案正确”。</p><p>RLVR 可以直接优化最终正确率。比如同一个数学题，模型生成多个答案，只有正确答案拿到 reward。经过训练，模型会提高能得到正确答案的生成轨迹概率。</p><p>这也是 DeepSeek-R1 这类 reasoning model 重要的地方：它不是只靠人工写好的 CoT，而是通过可验证 reward 强化推理行为。</p><p>不过这也带来风险。</p><p>如果 reward 只看最终答案，模型可能学到短路策略。比如中间推理不可靠，但答案格式正确；或者生成更长 reasoning 来提高偶然命中率。</p><p>所以 reasoning RL 的关键不是“用了 RL”，而是 reward、采样、过滤、KL、长度控制一起设计。</p><h2 id="10-后训练里的几个常见失败模式"><a href="#10-后训练里的几个常见失败模式" class="headerlink" title="10. 后训练里的几个常见失败模式"></a>10. 后训练里的几个常见失败模式</h2><p>第一，reward hacking。</p><p>模型学会利用 reward 的漏洞，而不是真的变好。</p><p>第二，mode collapse。</p><p>模型输出变得单一，缺少多样性。</p><p>第三，length inflation。</p><p>模型生成越来越长，因为长回答更容易看起来努力，或者更容易覆盖答案。</p><p>第四，over-optimization。</p><p>训练集 reward 越来越高，但真实评估变差。</p><p>第五，distribution drift。</p><p>policy 离 reference model 太远，语言能力或安全边界受损。</p><p>这些问题后面讲 PPO、GRPO、OPD 时都会反复出现。</p><h2 id="11-读后训练论文时先看什么"><a href="#11-读后训练论文时先看什么" class="headerlink" title="11. 读后训练论文时先看什么"></a>11. 读后训练论文时先看什么</h2><p>我会先看这几个问题：</p><ul><li>训练信号来自哪里：人类偏好、reward model、verifier、teacher model？</li><li>数据是 on-policy 还是 off-policy？</li><li>reward 是 token-level、step-level，还是 response-level？</li><li>有没有 reference model 和 KL constraint？</li><li>是否需要 value model？</li><li>每个 prompt 采样多少 responses？</li><li>如何处理全对&#x2F;全错样本？</li><li>如何控制长度膨胀？</li><li>benchmark 提升是否只是来自更多采样或更长输出？</li></ul><p>这些问题比算法名字本身更重要。</p><h2 id="12-小结"><a href="#12-小结" class="headerlink" title="12. 小结"></a>12. 小结</h2><p>后训练里的 RL 可以压缩成一句话：</p><blockquote><p>让模型在自己的生成分布上尝试回答，用 reward 或偏好信号判断好坏，再提高好回答的概率，同时用 reference model 限制分布漂移。</p></blockquote><p>PPO、GRPO、DPO、KTO、OPD 的区别，主要在于训练信号怎么来、advantage 怎么算、是否需要在线采样、是否需要额外模型。</p><p>所以这一章先把 RL 语言打通。后面再看具体算法时，就不会只是在背缩写。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Schulman et al., <a href="https://arxiv.org/abs/1707.06347">Proximal Policy Optimization Algorithms</a></li><li>Ouyang et al., <a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a></li><li>Rafailov et al., <a href="https://arxiv.org/abs/2305.18290">Direct Preference Optimization: Your Language Model is Secretly a Reward Model</a></li><li>Shao et al., <a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/11/01/post-training-notes-01-rl-basics/</id>
    <link href="https://sssssimonk.github.io/2025/11/01/post-training-notes-01-rl-basics/"/>
    <published>2025-11-01T12:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇先整理后训练里会用到的强化学习基础。</p>
<p>大模型后训练里的 RL 和传统 RL 不太一样。传统 RL 常见场景是 agent 在环境里反复交互，比如机器人控制、游戏、推荐系统。LLM 后训练里的“环境”往往更窄：给一个 prompt，模型生成]]>
    </summary>
    <title>后训练笔记 01：强化学习基础</title>
    <updated>2025-11-01T12:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="多模态" scheme="https://sssssimonk.github.io/tags/%E5%A4%9A%E6%A8%A1%E6%80%81/"/>
    <category term="VLM" scheme="https://sssssimonk.github.io/tags/VLM/"/>
    <category term="Qwen-VL" scheme="https://sssssimonk.github.io/tags/Qwen-VL/"/>
    <category term="Omni" scheme="https://sssssimonk.github.io/tags/Omni/"/>
    <content>
      <![CDATA[<p>这一篇整理多模态模型。</p><p>多模态模型经常被描述成“模型能看图、看视频、听音频”。但从结构上看，问题其实是：</p><blockquote><p>怎么把不同模态的信息变成 LLM 能处理的 representation。</p></blockquote><p>LLM 本来处理的是 token sequence。图像、视频、音频不是离散文本 token，所以要先经过编码、投影、对齐，再接入语言模型。</p><h2 id="1-VLM-的基本结构"><a href="#1-VLM-的基本结构" class="headerlink" title="1. VLM 的基本结构"></a>1. VLM 的基本结构</h2><p>一个典型 vision-language model 可以拆成三块：</p><ul><li>Vision encoder</li><li>Projector &#x2F; adapter</li><li>LLM</li></ul><p>Vision encoder 把图片编码成视觉特征。</p><p>Projector 把视觉特征映射到 LLM 的 hidden dimension。</p><p>LLM 把视觉 token 和文本 token 放在一起处理，并生成回答。</p><p>粗略写成：</p><p>$$<br>z_v &#x3D; \mathrm{VisionEncoder}(I)<br>$$</p><p>$$<br>h_v &#x3D; \mathrm{Projector}(z_v)<br>$$</p><p>然后把 $h_v$ 和文本 embedding 拼接，送入 LLM。</p><p>这个结构很直接，但每一步都有细节。</p><h2 id="2-图像-token-是怎么来的"><a href="#2-图像-token-是怎么来的" class="headerlink" title="2. 图像 token 是怎么来的"></a>2. 图像 token 是怎么来的</h2><p>图像通常会被切成 patches。</p><p>ViT 的做法是把图片分成固定大小 patch，然后每个 patch 映射成一个向量。</p><p>如果一张图片被切成 $N$ 个 patches，就会得到 $N$ 个视觉 token。</p><p>问题是：图片分辨率越高，patch 数越多，视觉 token 越多。</p><p>视觉 token 多了以后，LLM 的上下文压力变大。</p><p>所以多模态模型经常要做 token compression 或 dynamic resolution。</p><p>这里的核心 trade-off 是：</p><ul><li>token 多，视觉细节保留更好，但计算更贵</li><li>token 少，推理更便宜，但细节可能丢失</li></ul><p>比如 OCR、图表理解、数学几何题，都需要细粒度视觉信息。普通场景描述可能不需要那么多 token。</p><h2 id="3-Projector-不只是维度对齐"><a href="#3-Projector-不只是维度对齐" class="headerlink" title="3. Projector 不只是维度对齐"></a>3. Projector 不只是维度对齐</h2><p>很多入门解释会说 projector 只是把 vision encoder 输出维度变成 LLM hidden size。</p><p>这不算错，但太简单。</p><p>Projector 还承担模态对齐作用。</p><p>Vision encoder 的表示空间来自图像预训练，LLM 的表示空间来自文本预训练。两者不是天然兼容。</p><p>Projector 要把视觉特征变成 LLM 能理解的“类 token 表示”。</p><p>如果 projector 太弱，视觉信息进了 LLM 也不好用。</p><p>如果 projector 太强或训练不稳定，又可能破坏语言模型原有能力。</p><p>所以多模态训练经常分阶段：</p><ol><li>先训练 projector，让视觉特征对齐文本空间</li><li>再做 instruction tuning，让模型学会多模态问答</li><li>后面再加入更复杂的 reasoning、OCR、video、agent 数据</li></ol><h2 id="4-拼接视觉-token-vs-cross-attention"><a href="#4-拼接视觉-token-vs-cross-attention" class="headerlink" title="4. 拼接视觉 token vs cross-attention"></a>4. 拼接视觉 token vs cross-attention</h2><p>视觉信息接入 LLM 有不同方式。</p><p>一种是直接把视觉 token 拼到文本 token 前面或中间，让 LLM self-attention 统一处理。</p><p>这种方式简单，符合 decoder-only LLM 的接口。</p><p>另一种是 cross-attention。语言 token 在某些层通过 cross-attention 去读取视觉特征。</p><p>直接拼接的好处是结构统一。</p><p>Cross-attention 的好处是视觉特征可以作为单独 memory，控制更灵活。</p><p>但两者都绕不开一个问题：视觉 token 太多时，attention 成本会很高。</p><p>所以现代 VLM 的关键不是“能不能接图像”，而是“怎么高效接入足够多的视觉信息”。</p><h2 id="5-多图输入更难在哪里"><a href="#5-多图输入更难在哪里" class="headerlink" title="5. 多图输入更难在哪里"></a>5. 多图输入更难在哪里</h2><p>单图问答已经不简单，多图更难。</p><p>多图任务需要模型区分不同图片，并在图片之间建立关系。</p><p>比如：</p><blockquote><p>图 1 是模型结构图，图 2 是实验结果表。请判断结构改动是否带来了稳定提升。</p></blockquote><p>这时模型不只是描述图像，而是要跨图对齐信息。</p><p>多图输入涉及：</p><ul><li>每张图的边界</li><li>图片顺序</li><li>跨图引用</li><li>视觉信息和文本问题的对应关系</li></ul><p>如果视觉 token 全部拼在一起，模型必须学会哪些 token 属于哪张图。</p><p>这就是为什么多模态位置编码和 image boundary token 很重要。</p><h2 id="6-视频：多了时间维度"><a href="#6-视频：多了时间维度" class="headerlink" title="6. 视频：多了时间维度"></a>6. 视频：多了时间维度</h2><p>视频不是图片列表这么简单。</p><p>视频有时间顺序、运动、事件持续、因果关系。</p><p>如果只抽几帧，可能丢掉关键动作。</p><p>如果抽很多帧，token 数会爆炸。</p><p>所以视频模型通常要处理三个问题：</p><ul><li>frame sampling：取哪些帧</li><li>temporal encoding：怎么表示时间顺序</li><li>long video understanding：怎么保留长时间信息</li></ul><p>举个例子。</p><p>如果视频里一个人先拿起杯子，再把杯子放进柜子，最后关上柜门。只看最后一帧，模型可能知道柜门关了，但不知道杯子被放进去了。</p><p>视频理解需要事件链，而不是静态描述。</p><h2 id="7-音频：连续信号怎么变-token"><a href="#7-音频：连续信号怎么变-token" class="headerlink" title="7. 音频：连续信号怎么变 token"></a>7. 音频：连续信号怎么变 token</h2><p>音频比图像更不一样。</p><p>文本是离散 token，音频是连续波形。</p><p>音频模型通常会先把波形变成特征，比如 spectrogram，或者用 audio encoder 得到表示。</p><p>如果要生成语音，还需要把文本或 hidden states 转成 speech tokens 或 codec tokens，再解码成波形。</p><p>Qwen3-Omni 这类模型会涉及 Thinker-Talker 结构。可以粗略理解为：</p><ul><li>Thinker 负责多模态理解和推理</li><li>Talker 负责生成自然语音</li></ul><p>这样做的原因是理解和语音生成不是同一个问题。理解需要语义、推理、对齐；语音生成还需要音色、节奏、流式延迟。</p><h2 id="8-Omni-model-难在哪里"><a href="#8-Omni-model-难在哪里" class="headerlink" title="8. Omni model 难在哪里"></a>8. Omni model 难在哪里</h2><p>Omni model 希望一个模型同时处理文本、图像、视频、音频，并且可能还要输出文本和语音。</p><p>这比 VLM 更难，因为不同模态的时间尺度和信息密度不同。</p><p>一张图片可以被压成几百个视觉 token。</p><p>一段音频可能每秒产生大量 acoustic tokens。</p><p>视频又同时包含空间和时间信息。</p><p>把这些东西统一到一个模型里，需要解决：</p><ul><li>模态表示统一</li><li>长上下文压力</li><li>训练数据混合</li><li>不同任务 loss 的平衡</li><li>实时交互延迟</li><li>输出模态控制</li></ul><p>所以 omni model 不是简单把 VLM、ASR、TTS 拼起来。真正难的是统一训练和统一推理。</p><h2 id="9-多模态-reasoning"><a href="#9-多模态-reasoning" class="headerlink" title="9. 多模态 reasoning"></a>9. 多模态 reasoning</h2><p>多模态模型最有价值的地方不是“描述图片”，而是跨模态 reasoning。</p><p>比如：</p><ul><li>看图表并推断趋势</li><li>看论文公式截图并解释推导</li><li>看 UI 截图并判断用户下一步该点哪里</li><li>看视频并找出事件因果</li><li>听语音并结合语气理解意图</li></ul><p>这些任务要求模型把视觉或音频证据和语言推理结合。</p><p>所以多模态 benchmark 也在从 captioning 走向 MMMU、MathVista、video QA、document understanding 等更复杂任务。</p><h2 id="10-幻觉问题更复杂"><a href="#10-幻觉问题更复杂" class="headerlink" title="10. 幻觉问题更复杂"></a>10. 幻觉问题更复杂</h2><p>文本模型会 hallucinate，多模态模型也会，而且更难检查。</p><p>比如模型可能说图片里有某个物体，但实际没有。</p><p>或者 OCR 把数字看错，后面推理全错。</p><p>或者视频里漏掉关键动作，却生成一个合理故事。</p><p>多模态 hallucination 的根源可能来自：</p><ul><li>视觉 encoder 没捕捉到细节</li><li>projector 对齐不好</li><li>LLM 依赖语言先验脑补</li><li>图像分辨率不够</li><li>训练数据里 caption 偏泛化</li></ul><p>所以多模态模型的回答要特别注意 evidence grounding。最好能让模型指出依据来自图像哪个区域、哪一帧、哪段音频。</p><h2 id="11-训练数据决定上限"><a href="#11-训练数据决定上限" class="headerlink" title="11. 训练数据决定上限"></a>11. 训练数据决定上限</h2><p>多模态模型很依赖数据。</p><p>图片-caption 数据可以教模型描述图片。</p><p>OCR 数据可以教模型读文字。</p><p>图表问答数据可以教模型理解可视化。</p><p>视频问答数据可以教模型时间推理。</p><p>音频对话数据可以教模型语音交互。</p><p>如果数据只覆盖普通图片描述，模型很难自动变成强视觉推理模型。</p><p>这和语言模型一样：结构提供可能性，数据决定能力落点。</p><h2 id="12-读多模态技术报告时看什么"><a href="#12-读多模态技术报告时看什么" class="headerlink" title="12. 读多模态技术报告时看什么"></a>12. 读多模态技术报告时看什么</h2><p>我会看这些问题：</p><ul><li>Vision encoder 是什么</li><li>视觉 token 数怎么控制</li><li>Projector 是线性层、MLP，还是更复杂结构</li><li>是否冻结 LLM 或 vision encoder</li><li>训练分几个阶段</li><li>支持单图、多图、视频、音频中的哪些输入</li><li>是否支持语音输出</li><li>benchmark 覆盖 caption、OCR、chart、math、video reasoning 吗</li><li>长视频或高分辨率图片怎么处理</li><li>有没有 grounding 或 hallucination 评估</li></ul><p>尤其要看“输入支持”和“能力支持”的区别。能输入视频，不等于能理解长视频事件。能输入音频，不等于能做自然低延迟语音对话。</p><h2 id="13-小结"><a href="#13-小结" class="headerlink" title="13. 小结"></a>13. 小结</h2><p>多模态模型的核心问题是模态对齐。</p><p>图像、视频、音频都要先变成 LLM 能处理的表示，然后再和文本 token 一起完成理解和生成。</p><p>VLM 的关键是视觉 token、projector 和视觉语言对齐。</p><p>视频模型多了时间维度。</p><p>音频模型多了连续信号、codec、实时生成的问题。</p><p>Omni model 则进一步要求一个系统统一处理多个模态的输入和输出。</p><p>所以我会把多模态看成现代大模型从“语言接口”走向“世界接口”的一步。但这一步不是靠一个接口参数实现的，而是结构、数据、训练阶段和系统延迟一起决定的。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Radford et al., <a href="https://arxiv.org/abs/2103.00020">Learning Transferable Visual Models From Natural Language Supervision</a></li><li>Liu et al., <a href="https://arxiv.org/abs/2304.08485">Visual Instruction Tuning</a></li><li>Bai et al., <a href="https://arxiv.org/abs/2511.21631">Qwen3-VL Technical Report</a></li><li>Xu et al., <a href="https://arxiv.org/abs/2509.17765">Qwen3-Omni Technical Report</a></li><li>OpenAI, <a href="https://openai.com/research/video-generation-models-as-world-simulators">Video generation models as world simulators</a></li><li>GLM-4.5 Team, <a href="https://arxiv.org/abs/2508.06471">GLM-4.5: Agentic, Reasoning, and Coding Foundation Models</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/10/11/modern-llm-notes-06-multimodal-models/</id>
    <link href="https://sssssimonk.github.io/2025/10/11/modern-llm-notes-06-multimodal-models/"/>
    <published>2025-10-11T12:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理多模态模型。</p>
<p>多模态模型经常被描述成“模型能看图、看视频、听音频”。但从结构上看，问题其实是：</p>
<blockquote>
<p>怎么把不同模态的信息变成 LLM 能处理的]]>
    </summary>
    <title>现代大模型笔记 06：多模态模型，图像、视频和音频怎么接进 LLM</title>
    <updated>2025-10-11T12:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="Post-training" scheme="https://sssssimonk.github.io/tags/Post-training/"/>
    <category term="SFT" scheme="https://sssssimonk.github.io/tags/SFT/"/>
    <category term="DPO" scheme="https://sssssimonk.github.io/tags/DPO/"/>
    <category term="GRPO" scheme="https://sssssimonk.github.io/tags/GRPO/"/>
    <category term="RLHF" scheme="https://sssssimonk.github.io/tags/RLHF/"/>
    <content>
      <![CDATA[<p>这一篇整理 post-training。</p><p>Pretraining 让模型学会语言、知识和模式，但一个 pretrained base model 通常不是好用的助手。它可能会续写问题、模仿网页文本、输出不稳定格式，也不一定遵循用户指令。</p><p>Post-training 的目标是把 base model 变成更符合使用需求的模型。</p><p>可以先用一句话区分：</p><blockquote><p>Pretraining 学“世界和语言的统计结构”，post-training 学“怎么作为一个模型产品来回答问题”。</p></blockquote><p>这句话不完全严谨，但很有用。</p><h2 id="1-Base-model-为什么不够用"><a href="#1-Base-model-为什么不够用" class="headerlink" title="1. Base model 为什么不够用"></a>1. Base model 为什么不够用</h2><p>Base model 的训练目标通常是 next token prediction：</p><p>$$<br>\max_\theta \sum_t \log p_\theta(x_t \mid x_{&lt;t})<br>$$</p><p>它看到的是大规模文本，目标是预测下一个 token。</p><p>所以当你问：</p><blockquote><p>请解释一下 AdamW 和 Adam 的区别。</p></blockquote><p>Base model 可能确实知道相关知识，但它未必会以你期待的方式回答。它可能继续补全一段网页，也可能生成多个问答样例，也可能格式混乱。</p><p>这不是知识缺失，而是行为没有对齐。</p><p>Post-training 就是在解决行为问题。</p><h2 id="2-SFT：先学会怎么回答"><a href="#2-SFT：先学会怎么回答" class="headerlink" title="2. SFT：先学会怎么回答"></a>2. SFT：先学会怎么回答</h2><p>SFT 是 supervised fine-tuning。</p><p>给模型一批指令和高质量回答，让它模仿这些回答。</p><p>训练目标仍然是 next token prediction，只不过数据变成了 instruction-response 格式：</p><p>$$<br>\max_\theta \log p_\theta(y \mid x)<br>$$</p><p>其中 $x$ 是用户指令，$y$ 是期望回答。</p><p>SFT 的作用很基础：</p><ul><li>学会遵循指令</li><li>学会问答格式</li><li>学会拒答边界</li><li>学会输出更清晰的结构</li><li>学会一些特定任务风格</li></ul><p>如果没有 SFT，模型可能有知识，但不好用。</p><h2 id="3-SFT-的局限"><a href="#3-SFT-的局限" class="headerlink" title="3. SFT 的局限"></a>3. SFT 的局限</h2><p>SFT 本质上是模仿。</p><p>它的问题是：数据里是什么样，模型就往什么样靠。</p><p>如果 SFT 数据质量一般，模型会学到平庸回答。</p><p>如果 SFT 数据太模板化，模型会变得啰嗦、套话。</p><p>如果 SFT 数据覆盖不到复杂场景，模型遇到新任务还是不稳。</p><p>更关键的是，很多问题没有唯一标准答案。</p><p>比如两个回答都正确，但一个更简洁，一个更详细；一个更安全，一个更有帮助。SFT 很难表达这种偏好排序。</p><p>这就引出了 preference learning。</p><h2 id="4-RLHF：从偏好里学习"><a href="#4-RLHF：从偏好里学习" class="headerlink" title="4. RLHF：从偏好里学习"></a>4. RLHF：从偏好里学习</h2><p>RLHF 的经典流程大概是：</p><ol><li>先训练一个 SFT model</li><li>对同一个 prompt 采样多个回答</li><li>人类标注哪个回答更好</li><li>训练 reward model</li><li>用 PPO 等 RL 方法优化 policy</li></ol><p>Reward model 学的是：</p><p>$$<br>r_\phi(x,y)<br>$$</p><p>也就是给定 prompt $x$ 和回答 $y$，判断这个回答有多好。</p><p>然后语言模型作为 policy，优化目标大概是让 reward 更高，同时不要偏离原模型太远。</p><p>通常会加 KL penalty：</p><p>$$<br>\max_\theta \mathbb{E}[r_\phi(x,y)] - \beta D_{KL}(\pi_\theta || \pi_{ref})<br>$$</p><p>这个 KL 项很重要。没有它，模型可能为了骗 reward model 走到奇怪分布。</p><h2 id="5-PPO：经典但复杂"><a href="#5-PPO：经典但复杂" class="headerlink" title="5. PPO：经典但复杂"></a>5. PPO：经典但复杂</h2><p>PPO 是 RLHF 里常见的算法。</p><p>它把语言模型看成 policy，生成回答就是 action sequence。</p><p>PPO 的优点是通用，能直接优化 reward。</p><p>但它也复杂：</p><ul><li>需要 reward model</li><li>需要采样</li><li>需要 value model 或 advantage estimation</li><li>训练不稳定</li><li>超参数敏感</li><li>成本高</li></ul><p>所以后来大家开始寻找更简单的 preference optimization 方法。</p><p>这里不能简单说 PPO 落后。它仍然很重要，尤其是在更复杂的 RL 场景里。但对于很多偏好对齐任务，DPO 这类方法更容易训练。</p><h2 id="6-DPO：把偏好优化变简单"><a href="#6-DPO：把偏好优化变简单" class="headerlink" title="6. DPO：把偏好优化变简单"></a>6. DPO：把偏好优化变简单</h2><p>DPO 的核心是：不显式训练 reward model，也不跑完整 RL，而是直接用偏好数据优化模型。</p><p>偏好数据通常是三元组：prompt、chosen response、rejected response。</p><p>DPO 让模型提高 chosen 的相对概率，降低 rejected 的相对概率，同时参考一个 reference model。</p><p>它的损失可以写成类似：</p><p>$$<br>\mathcal{L}<em>{DPO} &#x3D; -\log \sigma \left(\beta \log \frac{\pi</em>\theta(y_w|x)}{\pi_{ref}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{ref}(y_l|x)} \right)<br>$$</p><p>其中 $y_w$ 是 preferred response，$y_l$ 是 rejected response。</p><p>直觉上，DPO 在说：</p><blockquote><p>相比 reference model，新模型应该更偏向好回答，而不是差回答。</p></blockquote><p>DPO 的优点是稳定、简单、成本低。</p><p>但它依赖偏好数据质量，也不一定适合所有需要复杂探索的任务。</p><h2 id="7-GRPO：为什么-reasoning-里会出现它"><a href="#7-GRPO：为什么-reasoning-里会出现它" class="headerlink" title="7. GRPO：为什么 reasoning 里会出现它"></a>7. GRPO：为什么 reasoning 里会出现它</h2><p>GRPO 是 DeepSeekMath 和 DeepSeek-R1 里提到的重要方法。</p><p>它可以看成 PPO 的一种变体，核心是省掉单独的 value model，用一组采样回答的相对奖励来估计 advantage。</p><p>对同一个问题，模型生成一组回答：</p><p>$$<br>{y_1, y_2, …, y_G}<br>$$</p><p>每个回答得到 reward。然后用组内平均和标准差做归一化，判断某个回答相对这一组是好还是坏。</p><p>直觉上：</p><blockquote><p>不一定要知道一个回答的绝对价值，只要知道它在同一组候选里相对更好还是更差。</p></blockquote><p>这对数学、代码这类可验证任务很自然。因为 reward 可以来自答案是否正确、测试是否通过。</p><p>GRPO 的优势是减少 value model 带来的额外显存和训练复杂度。</p><p>但它仍然是 RL，不是免费的。采样多个回答、计算 reward、控制 KL、保持训练稳定，都是成本。</p><h2 id="8-SFT、DPO、GRPO-的位置"><a href="#8-SFT、DPO、GRPO-的位置" class="headerlink" title="8. SFT、DPO、GRPO 的位置"></a>8. SFT、DPO、GRPO 的位置</h2><p>可以粗略这样理解：</p><ul><li>SFT：教模型怎么回答</li><li>DPO：教模型在两个回答里偏向更好的那个</li><li>GRPO&#x2F;RL：让模型通过奖励信号强化解决问题的策略</li></ul><p>SFT 更像 imitation。</p><p>DPO 更像 preference shaping。</p><p>RL 更像 outcome-driven optimization。</p><p>这三者不是互斥关系。很多现代模型会组合使用：先 SFT，再偏好优化，再针对 reasoning&#x2F;code 做 RL。</p><h2 id="9-Post-training-改的是能力还是行为"><a href="#9-Post-training-改的是能力还是行为" class="headerlink" title="9. Post-training 改的是能力还是行为"></a>9. Post-training 改的是能力还是行为</h2><p>这是一个容易混的点。</p><p>Post-training 有时主要改行为，比如让模型更礼貌、更清晰、更遵循指令。</p><p>但在 reasoning、coding 任务里，post-training 也可能显著提升能力。</p><p>原因是这些任务的“能力”很依赖解题策略。</p><p>Base model 可能已经有知识，但不会稳定地分解问题、检查答案、使用工具、运行测试。</p><p>通过 post-training，模型学到更有效的行为策略，于是表现看起来像能力提升。</p><p>所以能力和行为在 LLM 里不是完全分开的。</p><h2 id="10-Reward-hacking-和过优化"><a href="#10-Reward-hacking-和过优化" class="headerlink" title="10. Reward hacking 和过优化"></a>10. Reward hacking 和过优化</h2><p>只要有 reward，就有 reward hacking 风险。</p><p>如果 reward model 偏好长回答，模型可能变啰嗦。</p><p>如果 reward 偏好自信语气，模型可能更会胡说。</p><p>如果数学 reward 只看最终答案，模型可能生成错误推理但碰巧答案对。</p><p>如果代码 reward 只看公开测试，模型可能过拟合测试。</p><p>所以 post-training 的关键不是“把 reward 拉满”，而是设计和验证 reward 是否真的代表目标。</p><p>这也是为什么现代模型训练要大量 eval、红队、安全测试、人工审查。</p><h2 id="11-Post-training-和数据质量"><a href="#11-Post-training-和数据质量" class="headerlink" title="11. Post-training 和数据质量"></a>11. Post-training 和数据质量</h2><p>Post-training 对数据质量非常敏感。</p><p>一批高质量 SFT 数据可能比十倍低质量数据更有用。</p><p>偏好数据也一样。标注者是否一致、偏好标准是否清晰、chosen 和 rejected 差异是否有意义，都会影响训练。</p><p>对 reasoning model 来说，高质量 reasoning traces 更关键。</p><p>如果训练数据里充满表面步骤，模型会学会“写步骤”，但不一定学会“做推理”。</p><p>所以数据构造不是辅助工作，而是 post-training 的核心。</p><h2 id="12-为什么同一个-base-model-会有不同版本"><a href="#12-为什么同一个-base-model-会有不同版本" class="headerlink" title="12. 为什么同一个 base model 会有不同版本"></a>12. 为什么同一个 base model 会有不同版本</h2><p>一个 base model 可以派生出很多版本：</p><ul><li>instruct model</li><li>chat model</li><li>reasoning model</li><li>code model</li><li>tool-use model</li><li>domain-specific model</li></ul><p>它们底层知识可能相近，但 post-training 目标不同。</p><p>比如 code model 会更强调代码数据、测试反馈、仓库级任务。</p><p>Reasoning model 会更强调数学、逻辑、可验证 reward、长 reasoning trace。</p><p>Chat model 会更强调安全、有帮助、对话体验。</p><p>所以看到模型名时，要区分 base 和 post-trained variant。只比较参数量没有意义。</p><h2 id="13-读-post-training-报告时看什么"><a href="#13-读-post-training-报告时看什么" class="headerlink" title="13. 读 post-training 报告时看什么"></a>13. 读 post-training 报告时看什么</h2><p>我会看这些问题：</p><ul><li>Base model 是什么</li><li>SFT 数据规模和来源是什么</li><li>是否使用偏好数据</li><li>偏好优化用 PPO、DPO，还是其他方法</li><li>RL reward 是人工 reward model，还是规则验证</li><li>是否针对 reasoning&#x2F;code&#x2F;agent 做专门训练</li><li>是否蒸馏自更强模型</li><li>有没有控制 KL，reference model 是什么</li><li>eval 是否覆盖安全、幻觉、长上下文、代码、数学</li></ul><p>特别要警惕只展示 benchmark 分数但不讲训练细节的报告。Post-training 的细节往往决定模型实际行为。</p><h2 id="14-小结"><a href="#14-小结" class="headerlink" title="14. 小结"></a>14. 小结</h2><p>Post-training 是现代大模型从“会续写”变成“能使用”的关键阶段。</p><p>SFT 解决基本指令跟随。</p><p>RLHF&#x2F;PPO 让模型从偏好中优化，但复杂且不稳定。</p><p>DPO 把偏好学习变成更简单的目标。</p><p>GRPO 适合在可验证任务上做 group-based RL，尤其和 reasoning&#x2F;coding 结合紧密。</p><p>我会把 post-training 看成一套行为塑形系统。它不只是让模型更礼貌，而是在很多任务上改变模型解决问题的方式。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Ouyang et al., <a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a></li><li>Rafailov et al., <a href="https://arxiv.org/abs/2305.18290">Direct Preference Optimization: Your Language Model is Secretly a Reward Model</a></li><li>Shao et al., <a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li><li>Qwen Team, <a href="https://arxiv.org/abs/2505.09388">Qwen3 Technical Report</a></li><li>GLM-4.5 Team, <a href="https://arxiv.org/abs/2508.06471">GLM-4.5: Agentic, Reasoning, and Coding Foundation Models</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/10/04/modern-llm-notes-05-post-training/</id>
    <link href="https://sssssimonk.github.io/2025/10/04/modern-llm-notes-05-post-training/"/>
    <published>2025-10-04T12:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 post-training。</p>
<p>Pretraining 让模型学会语言、知识和模式，但一个 pretrained base model]]>
    </summary>
    <title>现代大模型笔记 05：Post-training，SFT、DPO、GRPO 到 RL 推理</title>
    <updated>2025-10-04T12:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="Reasoning" scheme="https://sssssimonk.github.io/tags/Reasoning/"/>
    <category term="CoT" scheme="https://sssssimonk.github.io/tags/CoT/"/>
    <category term="DeepSeek-R1" scheme="https://sssssimonk.github.io/tags/DeepSeek-R1/"/>
    <category term="强化学习" scheme="https://sssssimonk.github.io/tags/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<p>这一篇整理 reasoning model。</p><p>现在很多模型会区分 thinking mode 和 non-thinking mode，也会强调数学、代码、复杂推理能力。表面上看，这是“模型回答前多想一会”。但如果只这么理解，就太浅了。</p><p>Reasoning model 的变化至少包括三层：</p><ul><li>推理时愿意花更多 token 和计算</li><li>训练时用更强的推理数据和偏好信号</li><li>后训练阶段用 RL 等方法强化可验证任务上的表现</li></ul><p>所以这篇从 CoT 开始，逐步过渡到 DeepSeek-R1 这类模型。</p><h2 id="1-普通-next-token-prediction-的限制"><a href="#1-普通-next-token-prediction-的限制" class="headerlink" title="1. 普通 next token prediction 的限制"></a>1. 普通 next token prediction 的限制</h2><p>语言模型的基本目标是预测下一个 token：</p><p>$$<br>\max_\theta \sum_t \log p_\theta(x_t \mid x_{&lt;t})<br>$$</p><p>这个目标很强，能学到语法、知识、代码模式、常识关系。</p><p>但复杂推理不只是“最像下一个词”。很多题需要中间状态：</p><ul><li>先拆条件</li><li>再选择公式</li><li>再计算</li><li>再检查结果</li></ul><p>如果模型直接从问题跳到答案，中间任何一步错了都很难纠正。</p><p>所以 reasoning 的一个核心思路是：不要急着输出最终答案，先生成中间推理过程。</p><h2 id="2-Chain-of-thought-的作用"><a href="#2-Chain-of-thought-的作用" class="headerlink" title="2. Chain-of-thought 的作用"></a>2. Chain-of-thought 的作用</h2><p>Chain-of-thought（CoT）最早可以理解成一种 prompting 方法。</p><p>给模型几个带中间步骤的例子，模型在新问题上也更倾向于写出中间步骤。</p><p>比如简单问题：</p><blockquote><p>小明有 3 个苹果，又买了 5 个，吃掉 2 个，还剩几个？</p></blockquote><p>直接回答也能做。</p><p>但如果问题变成多条件、多变量、多约束，直接回答容易跳步。</p><p>CoT 的作用是把一次复杂映射拆成多步局部映射：</p><p>$$<br>\text{problem} \rightarrow \text{intermediate steps} \rightarrow \text{answer}<br>$$</p><p>这不是魔法。它更像给模型更多计算轨迹，让模型在输出空间里保留中间状态。</p><h2 id="3-CoT-为什么会提升能力"><a href="#3-CoT-为什么会提升能力" class="headerlink" title="3. CoT 为什么会提升能力"></a>3. CoT 为什么会提升能力</h2><p>我觉得可以从三个角度理解。</p><p>第一，token budget 变多了。</p><p>模型不是在一个 token 或一句话里压出答案，而是有更多 token 做中间计算。</p><p>第二，中间步骤提供了隐式 scratchpad。</p><p>模型可以把已经得到的结论写下来，后面继续引用。</p><p>第三，训练分布更匹配。</p><p>如果模型在训练或示例中见过大量“逐步解题”的文本，它就会学到这种生成模式。</p><p>但 CoT 也有问题。模型写出来的推理不一定忠实。有时它只是生成一个看起来合理的解释，而真正导致答案的内部机制未必和文本推理一致。</p><p>所以 CoT 提升表现，不等于 CoT 完全可解释。</p><h2 id="4-Test-time-compute：推理时多花计算"><a href="#4-Test-time-compute：推理时多花计算" class="headerlink" title="4. Test-time compute：推理时多花计算"></a>4. Test-time compute：推理时多花计算</h2><p>Reasoning model 的一个核心趋势是 test-time compute。</p><p>传统模型更强调训练阶段投入：预训练更多数据、更大参数。</p><p>Reasoning model 则强调推理阶段也可以多花计算。</p><p>比如：</p><ul><li>生成更长 reasoning trace</li><li>采样多个解法再选择</li><li>自我检查</li><li>用 verifier 打分</li><li>分解问题再逐步求解</li></ul><p>这背后的想法是：对于复杂任务，生成一个短答案太便宜，也太冒险。</p><p>如果一道数学题需要 20 步推导，模型只生成 2 行答案，省下的 token 可能就是错误来源。</p><h2 id="5-Thinking-mode-和-non-thinking-mode"><a href="#5-Thinking-mode-和-non-thinking-mode" class="headerlink" title="5. Thinking mode 和 non-thinking mode"></a>5. Thinking mode 和 non-thinking mode</h2><p>Qwen3 这类模型提出 thinking mode 和 non-thinking mode 的统一框架。</p><p>这很实际。</p><p>不是所有问题都需要长推理。</p><p>问“北京今天几点”这种问题，如果模型长篇 reasoning，反而浪费时间。</p><p>但问一道复杂证明题、代码 debug、实验设计，就需要更多推理 token。</p><p>所以 thinking mode 的关键不是“永远思考”，而是根据任务复杂度分配计算。</p><p>这和人也像。简单问题直接答，复杂问题先列步骤。</p><p>从系统角度看，这叫 latency-quality trade-off。更长思考可能提高质量，但一定增加延迟和成本。</p><h2 id="6-DeepSeek-R1-Zero：只靠-RL-能出现什么"><a href="#6-DeepSeek-R1-Zero：只靠-RL-能出现什么" class="headerlink" title="6. DeepSeek-R1-Zero：只靠 RL 能出现什么"></a>6. DeepSeek-R1-Zero：只靠 RL 能出现什么</h2><p>DeepSeek-R1 里最有意思的部分是 R1-Zero。</p><p>R1-Zero 不先做 supervised fine-tuning，而是直接对 base model 做大规模 RL，目标是增强 reasoning 能力。</p><p>报告里提到，模型在 RL 过程中自然出现了一些推理行为，比如更长的思考、更强的自我验证。</p><p>这个结果很重要，因为它说明 reasoning 行为不一定完全依赖人工标注的 CoT 数据，也可以通过可验证奖励被强化出来。</p><p>但 R1-Zero 也有问题，比如可读性差、语言混杂、输出格式不稳定。</p><p>这说明“能推理”和“能以人类喜欢的方式推理”不是一回事。</p><h2 id="7-DeepSeek-R1：cold-start-multi-stage-RL"><a href="#7-DeepSeek-R1：cold-start-multi-stage-RL" class="headerlink" title="7. DeepSeek-R1：cold-start + multi-stage RL"></a>7. DeepSeek-R1：cold-start + multi-stage RL</h2><p>DeepSeek-R1 在 R1-Zero 的基础上加入 cold-start data。</p><p>也就是先给模型一些高质量、可读性更好的推理样本，让模型有一个更好的起点，然后再做 RL。</p><p>后面再经过多阶段训练，把 reasoning 能力、通用问答能力、可读性等拉到更平衡的状态。</p><p>这体现了一个很重要的训练思路：</p><blockquote><p>RL 可以强化能力，但如果起点太乱，最终行为可能不好控制。</p></blockquote><p>SFT 提供格式和初始行为，RL 提供目标导向的能力强化。</p><p>两者不是互斥，而是互补。</p><h2 id="8-可验证奖励为什么关键"><a href="#8-可验证奖励为什么关键" class="headerlink" title="8. 可验证奖励为什么关键"></a>8. 可验证奖励为什么关键</h2><p>Reasoning RL 最适合数学、代码这类任务，因为它们容易验证。</p><p>数学题可以看最终答案对不对。</p><p>代码题可以跑测试。</p><p>这类 reward 比“这个回答好不好”更明确。</p><p>如果 reward 很模糊，RL 容易学到投机行为。比如生成看起来更自信、更长、更像标准答案的文本，但不一定更正确。</p><p>所以 reasoning model 的突破，很大程度来自可验证任务的 reward 设计。</p><p>这也解释了为什么数学和代码经常是 reasoning model 的核心 benchmark。</p><h2 id="9-Distillation：小模型学-reasoning"><a href="#9-Distillation：小模型学-reasoning" class="headerlink" title="9. Distillation：小模型学 reasoning"></a>9. Distillation：小模型学 reasoning</h2><p>DeepSeek-R1 还强调 distillation，把大 reasoning model 的能力蒸馏到较小模型上。</p><p>蒸馏的直觉是：大模型生成高质量 reasoning data，小模型用这些数据做 supervised training。</p><p>这说明一个现象：</p><blockquote><p>reasoning 能力的一部分可以通过行为模仿迁移。</p></blockquote><p>但蒸馏也有上限。</p><p>小模型可以学到大模型的解题风格和部分策略，但参数容量、基础知识、搜索深度都有限。它不可能在所有场景都复制大模型能力。</p><p>所以蒸馏模型适合低成本部署，但不能简单等同于原始 teacher model。</p><h2 id="10-Reasoning-不是更长越好"><a href="#10-Reasoning-不是更长越好" class="headerlink" title="10. Reasoning 不是更长越好"></a>10. Reasoning 不是更长越好</h2><p>一个常见误区是：thinking token 越多，答案越好。</p><p>实际不是。</p><p>过长 reasoning 可能带来：</p><ul><li>延迟变高</li><li>成本变高</li><li>中间步骤引入错误</li><li>模型绕远路</li><li>简单问题过度分析</li></ul><p>所以 thinking budget 很重要。</p><p>理想状态是：简单任务短思考，复杂任务长思考；模型知道什么时候该停。</p><p>这也是 Qwen3 把 thinking budget 作为机制来讲的原因。</p><h2 id="11-Reasoning-trace-是否可信"><a href="#11-Reasoning-trace-是否可信" class="headerlink" title="11. Reasoning trace 是否可信"></a>11. Reasoning trace 是否可信</h2><p>模型写出的 reasoning trace 可以帮助人检查，但不能完全等同于模型内部真实推理。</p><p>有时模型会先在内部形成答案，再生成一段看似合理的解释。</p><p>有时它会在文本推理中走错，但最后答案碰巧对。</p><p>有时它的中间推理看起来很漂亮，但关键假设是错的。</p><p>所以我更愿意把 reasoning trace 看成一种可检查的输出过程，而不是完整透明的内部机制。</p><p>它有用，但不能盲信。</p><h2 id="12-读-reasoning-model-报告时看什么"><a href="#12-读-reasoning-model-报告时看什么" class="headerlink" title="12. 读 reasoning model 报告时看什么"></a>12. 读 reasoning model 报告时看什么</h2><p>我会看这些问题：</p><ul><li>Base model 是什么</li><li>有没有 cold-start SFT</li><li>RL reward 怎么设计</li><li>任务是否可验证</li><li>是否使用 rejection sampling 或 verifier</li><li>thinking token 平均多长</li><li>是否支持非 thinking 模式</li><li>benchmark 提升来自训练、采样，还是更长输出</li><li>蒸馏模型和原模型差距多大</li></ul><p>尤其要看成本。一个 reasoning model 如果每题多花 10 倍 token，benchmark 提升就要放在成本背景下理解。</p><h2 id="13-小结"><a href="#13-小结" class="headerlink" title="13. 小结"></a>13. 小结</h2><p>Reasoning model 的本质不是“模型突然会思考”，而是：</p><blockquote><p>训练目标、数据构造、RL reward 和推理阶段计算分配共同改变了模型解决复杂问题的方式。</p></blockquote><p>CoT 给模型 scratchpad。</p><p>Test-time compute 让模型推理时多花计算。</p><p>RL 在可验证任务上强化正确行为。</p><p>Distillation 把昂贵推理模型的行为迁移到小模型。</p><p>这几件事合起来，才是现代 reasoning model 的主线。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Wei et al., <a href="https://arxiv.org/abs/2201.11903">Chain-of-Thought Prompting Elicits Reasoning in Large Language Models</a></li><li>Wang et al., <a href="https://arxiv.org/abs/2203.11171">Self-Consistency Improves Chain of Thought Reasoning in Language Models</a></li><li>Shao et al., <a href="https://arxiv.org/abs/2402.03300">DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</a></li><li>Qwen Team, <a href="https://arxiv.org/abs/2505.09388">Qwen3 Technical Report</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/09/27/modern-llm-notes-04-reasoning-models/</id>
    <link href="https://sssssimonk.github.io/2025/09/27/modern-llm-notes-04-reasoning-models/"/>
    <published>2025-09-27T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 reasoning model。</p>
<p>现在很多模型会区分 thinking mode 和 non-thinking]]>
    </summary>
    <title>现代大模型笔记 04：Reasoning Model，从 CoT 到 DeepSeek-R1</title>
    <updated>2025-09-27T13:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="注意力机制" scheme="https://sssssimonk.github.io/tags/%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="RoPE" scheme="https://sssssimonk.github.io/tags/RoPE/"/>
    <category term="KV cache" scheme="https://sssssimonk.github.io/tags/KV-cache/"/>
    <category term="长上下文" scheme="https://sssssimonk.github.io/tags/%E9%95%BF%E4%B8%8A%E4%B8%8B%E6%96%87/"/>
    <category term="FlashAttention" scheme="https://sssssimonk.github.io/tags/FlashAttention/"/>
    <content>
      <![CDATA[<p>这一篇整理长上下文。</p><p>很多模型现在都会宣传 32K、128K、1M context window。这个数字很吸引人，但它很容易被误解。</p><p>长上下文不是“把 max length 参数调大”。真正困难的地方有三类：</p><ul><li>attention 计算量变大</li><li>KV cache 显存变大</li><li>上下文变长后，模型未必能稳定用好信息</li></ul><p>所以这篇重点不是列方法，而是看长上下文到底在解决什么瓶颈。</p><h2 id="1-Self-attention-为什么贵"><a href="#1-Self-attention-为什么贵" class="headerlink" title="1. Self-attention 为什么贵"></a>1. Self-attention 为什么贵</h2><p>标准 self-attention 是：</p><p>$$<br>\mathrm{Attention}(Q,K,V)&#x3D;\mathrm{softmax}\left(\frac{QK^\top}{\sqrt{d}}\right)V<br>$$</p><p>如果 sequence length 是 $n$，那么 $QK^\top$ 是一个 $n \times n$ 的矩阵。</p><p>这意味着 attention 的时间和显存复杂度通常和 $n^2$ 有关。</p><p>当上下文从 4K 增加到 32K，长度变成 8 倍，attention matrix 的规模大约变成 64 倍。</p><p>这就是为什么长上下文不是线性加 token 那么简单。</p><p>当然，实际实现里有 FlashAttention、分块计算、并行策略等优化，不一定真的显式保存完整 attention matrix。但底层的两两 token 交互压力仍然存在。</p><h2 id="2-Prefill-和-decode-是两种成本"><a href="#2-Prefill-和-decode-是两种成本" class="headerlink" title="2. Prefill 和 decode 是两种成本"></a>2. Prefill 和 decode 是两种成本</h2><p>LLM 推理可以拆成两个阶段。</p><p>Prefill 阶段：模型一次性处理 prompt，把所有输入 token 的 hidden states 和 KV cache 算出来。</p><p>Decode 阶段：模型每次生成一个新 token，并复用前面的 KV cache。</p><p>这两个阶段的瓶颈不同。</p><p>长 prompt 会让 prefill 很贵，因为 prompt 内部 token 之间要做 attention。</p><p>长生成会让 decode 过程中 KV cache 越来越大，因为每一步都要关注过去所有 token。</p><p>所以同样是“长上下文”，有两种场景：</p><ul><li>读一个很长的文档，然后回答几个问题</li><li>持续进行很长的对话或生成很长的内容</li></ul><p>它们的系统瓶颈并不完全一样。</p><h2 id="3-KV-cache-为什么重要"><a href="#3-KV-cache-为什么重要" class="headerlink" title="3. KV cache 为什么重要"></a>3. KV cache 为什么重要</h2><p>自回归生成时，第 $t$ 步要用前面所有 token 的 key 和 value。</p><p>如果每一步都重新计算前面所有 token，成本会爆炸。</p><p>KV cache 的做法是把历史 token 的 key&#x2F;value 存下来。</p><p>这样第 $t$ 步只需要算新 token 的 query，然后和缓存里的 key&#x2F;value 做 attention。</p><p>但 cache 本身很占显存。</p><p>粗略看，KV cache 大小和这些因素成正比：</p><p>$$<br>\text{layers} \times \text{sequence length} \times \text{KV heads} \times \text{head dimension}<br>$$</p><p>所以 sequence length 越长，cache 越大。模型层数越多，cache 也越大。</p><p>这也是为什么 GQA、MQA、MLA 这类方法重要。它们都在不同程度上减少 key&#x2F;value 的存储或计算成本。</p><h2 id="4-MHA、MQA、GQA-与-MLA"><a href="#4-MHA、MQA、GQA-与-MLA" class="headerlink" title="4. MHA、MQA、GQA 与 MLA"></a>4. MHA、MQA、GQA 与 MLA</h2><p>标准 MHA 中，每个 query head 都有自己的 key&#x2F;value head。</p><p>MQA 让所有 query heads 共享一组 key&#x2F;value。</p><p>GQA 折中，让一组 query heads 共享一组 key&#x2F;value。</p><p>它们主要是在减少 KV cache。</p><p>DeepSeek 的 MLA 更进一步。MLA 把 key&#x2F;value 压到一个 latent 表示里，再在需要时恢复出用于 attention 的表示。它的目标也是降低 KV cache，但做法不是简单减少 KV heads，而是改变缓存内容的表示方式。</p><p>这里可以抓住一个直觉：</p><blockquote><p>长上下文推理里，缓存什么、缓存多少、怎么缓存，是和 attention 公式同样重要的问题。</p></blockquote><p>很多论文看起来在改 attention，实际是在改 inference memory footprint。</p><h2 id="5-FlashAttention-解决的不是同一个问题"><a href="#5-FlashAttention-解决的不是同一个问题" class="headerlink" title="5. FlashAttention 解决的不是同一个问题"></a>5. FlashAttention 解决的不是同一个问题</h2><p>FlashAttention 优化的是 attention 计算过程中的内存读写。</p><p>标准实现可能会把 $QK^\top$ 这样的中间矩阵写入 HBM，再读回来做 softmax 和乘 $V$。</p><p>FlashAttention 用 tiling 和 online softmax，把计算分块放进更快的 SRAM，减少 HBM 访问。</p><p>所以它不是让 attention 从 $O(n^2)$ 变成 $O(n)$，而是让同样的 exact attention 在 GPU 上跑得更高效、占用更少显存。</p><p>这点很关键。</p><p>如果一个方法宣称“更长上下文”，要看它是在：</p><ul><li>降低理论复杂度</li><li>降低实际显存占用</li><li>降低 KV cache</li><li>改善位置外推</li><li>提升长文本检索能力</li></ul><p>这些不是一回事。</p><h2 id="6-位置编码的外推问题"><a href="#6-位置编码的外推问题" class="headerlink" title="6. 位置编码的外推问题"></a>6. 位置编码的外推问题</h2><p>模型训练时如果只见过 4K 长度，直接推到 32K 可能出问题。</p><p>因为 position embedding 或 RoPE 的位置范围发生了变化。</p><p>RoPE 的好处是相对位置信息更自然，但它也有频率外推问题。位置很大时，旋转角度分布可能和训练时不一样。</p><p>所以会有 RoPE scaling、NTK-aware scaling、YaRN 等方法。</p><p>这些方法的目标不是让模型“突然理解长文档”，而是先解决位置编码在更长长度下不崩。</p><p>换句话说：</p><blockquote><p>位置外推解决的是模型能不能在更长坐标系里正常工作，不等于模型真的会做长程推理。</p></blockquote><p>这也是为什么有些模型标称 128K，但实际问很长文档里的细节时，表现并不稳定。</p><h2 id="7-Lost-in-the-middle"><a href="#7-Lost-in-the-middle" class="headerlink" title="7. Lost in the middle"></a>7. Lost in the middle</h2><p>长上下文还有一个能力问题：模型可能看得见，但用不好。</p><p>一种常见现象是 lost in the middle。信息放在开头或结尾时，模型更容易找到；信息放在中间时，模型容易漏掉。</p><p>这说明 context window 大小和 effective context length 不一样。</p><p>Context window 是系统允许输入多少 token。</p><p>Effective context length 是模型在任务中能可靠利用多少 token。</p><p>这两个数字差很多时，长上下文就更像“能塞进去”，而不是“能读明白”。</p><h2 id="8-长上下文任务的类型"><a href="#8-长上下文任务的类型" class="headerlink" title="8. 长上下文任务的类型"></a>8. 长上下文任务的类型</h2><p>我觉得长上下文至少要分三类看。</p><p>第一类是 retrieval-heavy 任务。</p><p>比如在长文档里找一个事实。这里关键是模型能不能定位证据。</p><p>第二类是 synthesis-heavy 任务。</p><p>比如总结一本书、多篇论文、一个项目代码库。这里关键不是找一句话，而是整合多个位置的信息。</p><p>第三类是 reasoning-heavy 任务。</p><p>比如给很长的实验日志，判断哪一步导致结果异常。这里需要模型在长上下文里做因果判断。</p><p>很多 benchmark 只测第一类，但真实使用里后两类更难。</p><h2 id="9-Context-compression"><a href="#9-Context-compression" class="headerlink" title="9. Context compression"></a>9. Context compression</h2><p>如果上下文太长，一种思路是压缩。</p><p>压缩可以发生在很多层面：</p><ul><li>文本层面：先摘要，再输入模型</li><li>检索层面：只取相关 chunks</li><li>表示层面：把多个 token 压成少量 memory tokens</li><li>KV 层面：压缩或丢弃部分 cache</li></ul><p>这些方法的风险是信息丢失。</p><p>压缩越强，成本越低，但越可能丢掉后面问题需要的细节。</p><p>所以长上下文和 RAG 不是完全替代关系。长上下文让模型能直接读更多内容，RAG 让系统先筛掉明显无关内容。实际系统里经常两者结合。</p><h2 id="10-Sparse-attention"><a href="#10-Sparse-attention" class="headerlink" title="10. Sparse attention"></a>10. Sparse attention</h2><p>Sparse attention 试图减少 token 两两交互。</p><p>不是每个 token 都关注所有 token，而是只关注局部窗口、全局 token，或者某种稀疏模式。</p><p>它的好处是复杂度更低。</p><p>问题是稀疏模式可能限制信息流。如果任务需要两个相距很远的位置直接交互，稀疏 attention 未必能很好处理。</p><p>所以 sparse attention 的关键问题是：稀疏模式是不是和任务结构匹配。</p><p>代码、文档、视频、对话的长程依赖形态不一样。一个固定稀疏模式很难对所有任务都最优。</p><h2 id="11-为什么-128K-不等于真正会用-128K"><a href="#11-为什么-128K-不等于真正会用-128K" class="headerlink" title="11. 为什么 128K 不等于真正会用 128K"></a>11. 为什么 128K 不等于真正会用 128K</h2><p>假设模型支持 128K context。</p><p>这至少包含三层含义：</p><p>第一，系统层面可以接收 128K token。</p><p>第二，模型结构和位置编码在 128K 下不会明显崩。</p><p>第三，模型在真实任务中能稳定利用 128K 里的关键信息。</p><p>很多时候宣传的是第一层或第二层，用户期待的是第三层。</p><p>这就是长上下文体验落差的来源。</p><p>所以读模型报告时，不能只看 max context length，还要看它怎么评估长上下文能力。Needle-in-a-haystack 只能说明模型能不能找针，不能完全说明它能不能理解一整份复杂材料。</p><h2 id="12-读长上下文论文时看什么"><a href="#12-读长上下文论文时看什么" class="headerlink" title="12. 读长上下文论文时看什么"></a>12. 读长上下文论文时看什么</h2><p>我会看这些问题：</p><ul><li>训练时最大长度是多少，推理时最大长度是多少</li><li>使用什么位置编码和 scaling 方法</li><li>attention 是 exact、sparse、linear，还是其他变体</li><li>KV cache 有没有压缩</li><li>prefill 和 decode 的吞吐分别如何</li><li>benchmark 是检索型，还是综合理解型</li><li>长度增加后性能下降曲线怎样</li><li>有没有和 RAG 或 compression 结合</li></ul><p>尤其要注意：有些方法提升的是“能跑更长”，有些方法提升的是“用得更好”。这两个目标相关，但不是同一个目标。</p><h2 id="13-小结"><a href="#13-小结" class="headerlink" title="13. 小结"></a>13. 小结</h2><p>长上下文的核心不是把窗口开大，而是同时处理三个问题：</p><ul><li>算得动</li><li>存得下</li><li>用得好</li></ul><p>FlashAttention、GQA、MLA 主要偏向算得动和存得下。</p><p>RoPE scaling、YaRN 主要解决位置外推。</p><p>长文本训练、检索式评估、context compression 更接近用得好的问题。</p><p>所以看一个长上下文模型时，我不会只看 32K、128K、1M 这些数字。真正要问的是：它在什么任务上证明了自己能稳定使用这么长的上下文。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Dao et al., <a href="https://arxiv.org/abs/2205.14135">FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness</a></li><li>Ainslie et al., <a href="https://arxiv.org/abs/2305.13245">GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints</a></li><li>Su et al., <a href="https://arxiv.org/abs/2104.09864">RoFormer: Enhanced Transformer with Rotary Position Embedding</a></li><li>Peng et al., <a href="https://arxiv.org/abs/2309.00071">YaRN: Efficient Context Window Extension of Large Language Models</a></li><li>Liu et al., <a href="https://arxiv.org/abs/2307.03172">Lost in the Middle: How Language Models Use Long Contexts</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2412.19437">DeepSeek-V3 Technical Report</a></li><li>Qwen Team, <a href="https://arxiv.org/abs/2505.09388">Qwen3 Technical Report</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/09/20/modern-llm-notes-03-long-context/</id>
    <link href="https://sssssimonk.github.io/2025/09/20/modern-llm-notes-03-long-context/"/>
    <published>2025-09-20T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理长上下文。</p>
<p>很多模型现在都会宣传 32K、128K、1M context window。这个数字很吸引人，但它很容易被误解。</p>
<p>长上下文不是“把 max length]]>
    </summary>
    <title>现代大模型笔记 03：长上下文，注意力、KV cache 与位置编码</title>
    <updated>2025-09-20T13:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="MoE" scheme="https://sssssimonk.github.io/tags/MoE/"/>
    <category term="稀疏激活" scheme="https://sssssimonk.github.io/tags/%E7%A8%80%E7%96%8F%E6%BF%80%E6%B4%BB/"/>
    <category term="DeepSeek" scheme="https://sssssimonk.github.io/tags/DeepSeek/"/>
    <category term="Qwen" scheme="https://sssssimonk.github.io/tags/Qwen/"/>
    <content>
      <![CDATA[<p>这一篇整理 MoE，也就是 mixture of experts。</p><p>现在很多前沿开源模型都会强调两个数字：total parameters 和 activated parameters。比如一个模型可能有 235B 总参数，但每个 token 只激活 22B 参数。</p><p>第一次看到这个说法时很容易觉得矛盾：到底是 235B 模型，还是 22B 模型？</p><p>MoE 的关键就在这里。它不是让所有参数每次都参与计算，而是给每个 token 选择一小部分 expert 来算。</p><h2 id="1-Dense-model-的问题"><a href="#1-Dense-model-的问题" class="headerlink" title="1. Dense model 的问题"></a>1. Dense model 的问题</h2><p>普通 Transformer 是 dense model。</p><p>每个 token 经过每一层时，都会使用这一层的全部参数。</p><p>如果模型变大，计算量也跟着变大。一个 dense 70B 模型和一个 dense 7B 模型相比，容量更大，但每个 token 的计算成本也明显更高。</p><p>这带来一个很现实的问题：参数规模、训练成本、推理成本几乎绑在一起。</p><p>想要更强模型，就要更大参数；参数越大，每次推理越贵。</p><p>MoE 试图拆开这件事：</p><ul><li>总参数可以很大，用来提供更高容量</li><li>每个 token 只用一部分参数，用来控制计算成本</li></ul><p>也就是说，MoE 想要的是“大容量”和“低激活成本”同时存在。</p><h2 id="2-Expert-是什么"><a href="#2-Expert-是什么" class="headerlink" title="2. Expert 是什么"></a>2. Expert 是什么</h2><p>在很多 MoE Transformer 里，attention 层仍然是 shared 的，MoE 主要替换 FFN 层。</p><p>普通 FFN 是一个大的前馈网络：</p><p>$$<br>\mathrm{FFN}(x)<br>$$</p><p>MoE FFN 则有多个 expert：</p><p>$$<br>E_1(x), E_2(x), …, E_N(x)<br>$$</p><p>每个 expert 本质上可以理解成一个 FFN。区别是，不是每个 token 都跑所有 expert，而是由 router 选择其中几个。</p><p>如果使用 top-2 routing，那么 token $x$ 的输出大概是：</p><p>$$<br>y &#x3D; g_1(x)E_{i}(x) + g_2(x)E_{j}(x)<br>$$</p><p>这里 $E_i$ 和 $E_j$ 是被选中的两个 experts，$g_1$ 和 $g_2$ 是 router 给出的权重。</p><p>直觉上，模型在说：</p><blockquote><p>这个 token 更适合交给这些 expert 处理。</p></blockquote><h2 id="3-Router：MoE-的真正难点"><a href="#3-Router：MoE-的真正难点" class="headerlink" title="3. Router：MoE 的真正难点"></a>3. Router：MoE 的真正难点</h2><p>MoE 听起来像“多放几个专家，然后自动选择”。但真正难的是 router。</p><p>Router 通常会对每个 token 算一个分数：</p><p>$$<br>s &#x3D; xW_r<br>$$</p><p>再从所有 expert 里选 top-k。</p><p>问题是，如果 router 学得不好，所有 token 都挤向少数几个 expert，其他 expert 几乎不用。这样不仅浪费参数，还会导致某些 expert 负载爆炸。</p><p>所以 MoE 训练里经常要处理 load balancing。</p><p>Switch Transformer 里用了 auxiliary loss 来鼓励 token 更均匀地分配到不同 expert。DeepSeek-V3 则强调 auxiliary-loss-free 的 load balancing，试图减少辅助损失对模型主目标的干扰。</p><p>这里有一个技术取舍：</p><ul><li>加 auxiliary loss，路由更容易均衡，但可能影响语言建模目标</li><li>不加或弱化 auxiliary loss，主目标更干净，但路由稳定性更难保证</li></ul><p>MoE 的难点不是“有多个 expert”，而是“怎么让 expert 被合理使用”。</p><h2 id="4-总参数和激活参数"><a href="#4-总参数和激活参数" class="headerlink" title="4. 总参数和激活参数"></a>4. 总参数和激活参数</h2><p>MoE 报告里经常出现两个数字。</p><p>Total parameters 指模型所有参数，包括所有 experts。</p><p>Activated parameters 指处理一个 token 时实际参与计算的参数。</p><p>比如一个 MoE 模型有 64 个 experts，每次只选 2 个。那总参数可能很大，但每个 token 只走其中一小部分。</p><p>这就解释了为什么一个模型可以同时标注为 235B total parameters 和 22B activated parameters。</p><p>它的容量接近一个超大模型，但单 token 计算量更接近一个中等模型。</p><p>不过这句话不能简单理解成“235B 的效果，22B 的成本”。因为 MoE 还有通信、路由、负载均衡、batching 等额外问题。</p><h2 id="5-MoE-为什么适合大规模预训练"><a href="#5-MoE-为什么适合大规模预训练" class="headerlink" title="5. MoE 为什么适合大规模预训练"></a>5. MoE 为什么适合大规模预训练</h2><p>预训练阶段，模型会看到海量 token。不同 token 的统计特征差异很大：代码、数学、中文、英文、表格、对话、网页文本。</p><p>Dense model 对所有 token 使用同一组 FFN 参数。</p><p>MoE 则允许不同 token 走不同 expert。理论上，不同 expert 可以学到不同类型的模式。</p><p>比如某些 expert 可能更常处理代码 token，某些 expert 可能更常处理数学符号，某些 expert 更常处理自然语言。</p><p>这里要注意，我不会把 expert 理解成非常清晰的人类职业分工。它们不一定真的变成“数学专家”“代码专家”。更准确地说，expert 是被 router 动态选择的参数子空间。</p><p>这个区别很重要。技术报告里如果没有分析 expert specialization，就不要强行给每个 expert 贴人类标签。</p><h2 id="6-MoE-的通信成本"><a href="#6-MoE-的通信成本" class="headerlink" title="6. MoE 的通信成本"></a>6. MoE 的通信成本</h2><p>MoE 在单卡上很容易理解，但大模型训练通常跨很多 GPU。</p><p>如果 expert 分布在不同 GPU 上，token 被 router 分配后，就需要在 GPU 之间传输。</p><p>这会引入 all-to-all communication。</p><p>Dense model 的通信主要来自模型并行、数据并行、梯度同步等。MoE 额外增加了 token dispatch 和 combine 的通信。</p><p>这就是为什么 MoE 论文和技术报告会关心：</p><ul><li>expert parallelism</li><li>load balancing</li><li>token dropping</li><li>capacity factor</li><li>communication-computation overlap</li></ul><p>这些听起来像工程细节，但它们决定 MoE 能不能真的跑起来。</p><p>一个 routing 很漂亮但通信开销巨大的 MoE，在真实训练里可能并不划算。</p><h2 id="7-Capacity-factor-和-token-dropping"><a href="#7-Capacity-factor-和-token-dropping" class="headerlink" title="7. Capacity factor 和 token dropping"></a>7. Capacity factor 和 token dropping</h2><p>每个 expert 每个 batch 能处理的 token 数通常会有限制。</p><p>如果某个 expert 被分配了太多 token，超出的 token 可能被 drop，或者走 fallback 路径。</p><p>Capacity factor 控制每个 expert 的容量上限。</p><p>如果 capacity 太小，容易 drop token，训练信号受损。</p><p>如果 capacity 太大，显存和计算浪费增加。</p><p>所以 MoE 的训练不是简单调 learning rate。它多了一组系统参数，而且这些参数和硬件拓扑、batch size、sequence length 都有关。</p><h2 id="8-DeepSeekMoE-的思路"><a href="#8-DeepSeekMoE-的思路" class="headerlink" title="8. DeepSeekMoE 的思路"></a>8. DeepSeekMoE 的思路</h2><p>DeepSeek 系列技术报告里，MoE 是核心结构之一。</p><p>DeepSeek-V3 使用 671B total parameters，但每个 token 激活约 37B 参数。它还结合了 MLA、multi-token prediction、FP8 training 等工程优化。</p><p>这里值得注意的是：DeepSeek-V3 的强并不只是因为 MoE。</p><p>MoE 提供参数容量和计算效率，MLA 降低 KV cache 成本，FP8 降低训练成本，多 token prediction 尝试提高生成效率。</p><p>所以读 DeepSeek-V3 时，不要把结论压缩成“MoE 很强”。更准确的说法是：</p><blockquote><p>DeepSeek-V3 是一组结构、训练和系统优化叠加出来的结果，MoE 是其中负责参数容量扩展的一块。</p></blockquote><h2 id="9-Qwen3-MoE-的位置"><a href="#9-Qwen3-MoE-的位置" class="headerlink" title="9. Qwen3 MoE 的位置"></a>9. Qwen3 MoE 的位置</h2><p>Qwen3 技术报告里同时有 dense 和 MoE 模型。</p><p>这很有意思，因为它说明 dense 和 MoE 不是谁完全替代谁，而是面向不同规模和部署场景的选择。</p><p>小模型用 dense 可能更简单、更稳定、更容易部署。</p><p>大模型用 MoE 可以把总容量拉高，同时控制每 token 计算。</p><p>所以模型家族通常会同时提供 dense 和 MoE 版本，让用户在延迟、成本、能力之间选。</p><h2 id="10-MoE-不是免费午餐"><a href="#10-MoE-不是免费午餐" class="headerlink" title="10. MoE 不是免费午餐"></a>10. MoE 不是免费午餐</h2><p>MoE 最大的误区是：只看 activated parameters，然后以为成本就等于一个小 dense 模型。</p><p>实际不是。</p><p>MoE 至少有这些额外代价：</p><ul><li>Router 本身要计算</li><li>Token dispatch 需要通信</li><li>Expert 负载不均衡会降低硬件利用率</li><li>小 batch 推理时可能不如 dense 规整</li><li>训练稳定性更复杂</li><li>Serving 系统更难做</li></ul><p>这也是为什么 MoE 更像系统工程，而不只是模型结构。</p><p>如果部署环境很简单，dense model 可能反而更好。MoE 的优势通常要在足够大规模、足够好的并行系统里才能发挥。</p><h2 id="11-一个简单例子"><a href="#11-一个简单例子" class="headerlink" title="11. 一个简单例子"></a>11. 一个简单例子</h2><p>假设一个客服模型要处理三类问题：</p><ul><li>订单状态</li><li>退款政策</li><li>技术故障</li></ul><p>Dense model 会用同一套 FFN 参数处理所有问题。</p><p>MoE model 可能学到某些 token 更常走某些 expert。和订单相关的表达经常激活一组 expert，技术故障相关的表达激活另一组 expert。</p><p>但真实 LLM 里的分工不会这么干净。一个 token 的意义受上下文影响，同一个词在代码、数学、闲聊里可能完全不同。</p><p>所以 MoE 的 router 不是按关键词查表，而是根据 hidden state 做动态选择。</p><h2 id="12-读-MoE-模型报告时看什么"><a href="#12-读-MoE-模型报告时看什么" class="headerlink" title="12. 读 MoE 模型报告时看什么"></a>12. 读 MoE 模型报告时看什么</h2><p>我会重点看这些问题：</p><ul><li>总参数和激活参数分别是多少</li><li>每层都有 MoE，还是部分层有 MoE</li><li>每次 routing 选 top-1、top-2，还是更多 expert</li><li>有没有 shared expert</li><li>load balancing 怎么做</li><li>是否使用 auxiliary loss</li><li>expert 数量是多少</li><li>训练和推理时的通信开销怎么处理</li><li>benchmark 提升是来自 MoE 本身，还是同时叠加了数据、RL、上下文扩展</li></ul><p>这些问题比“用了 MoE”更重要。</p><h2 id="13-小结"><a href="#13-小结" class="headerlink" title="13. 小结"></a>13. 小结</h2><p>MoE 的核心思想很直接：</p><blockquote><p>用稀疏激活把总参数容量和单 token 计算成本拆开。</p></blockquote><p>但 MoE 的真实难点也很明确：</p><blockquote><p>参数变稀疏以后，训练和系统复杂度会显著上升。</p></blockquote><p>所以我会把 MoE 看成一种大规模模型扩展路线，而不是一个单纯提升模型能力的小技巧。</p><p>它适合的问题是：当 dense scaling 越来越贵时，能不能用更聪明的参数激活方式继续扩展模型容量。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Shazeer et al., <a href="https://arxiv.org/abs/1701.06538">Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer</a></li><li>Fedus et al., <a href="https://arxiv.org/abs/2101.03961">Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity</a></li><li>Qwen Team, <a href="https://arxiv.org/abs/2505.09388">Qwen3 Technical Report</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2412.19437">DeepSeek-V3 Technical Report</a></li><li>GLM-4.5 Team, <a href="https://arxiv.org/abs/2508.06471">GLM-4.5: Agentic, Reasoning, and Coding Foundation Models</a></li><li>Moonshot AI, <a href="https://arxiv.org/abs/2507.20534">Kimi K2: Open Agentic Intelligence</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/09/13/modern-llm-notes-02-moe/</id>
    <link href="https://sssssimonk.github.io/2025/09/13/modern-llm-notes-02-moe/"/>
    <published>2025-09-13T12:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 MoE，也就是 mixture of experts。</p>
<p>现在很多前沿开源模型都会强调两个数字：total parameters 和 activated parameters。比如一个模型可能有 235B 总参数，但每个 token 只激活 22B]]>
    </summary>
    <title>现代大模型笔记 02：MoE，为什么大模型开始变稀疏</title>
    <updated>2025-09-13T12:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="现代大模型与前沿论文笔记" scheme="https://sssssimonk.github.io/categories/%E7%8E%B0%E4%BB%A3%E5%A4%A7%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%89%8D%E6%B2%BF%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="注意力机制" scheme="https://sssssimonk.github.io/tags/%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6/"/>
    <category term="Transformer" scheme="https://sssssimonk.github.io/tags/Transformer/"/>
    <category term="大模型" scheme="https://sssssimonk.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="RoPE" scheme="https://sssssimonk.github.io/tags/RoPE/"/>
    <category term="KV cache" scheme="https://sssssimonk.github.io/tags/KV-cache/"/>
    <content>
      <![CDATA[<p>这一篇先把现代大模型的结构底座梳理出来。</p><p>很多技术报告会说模型基于 Transformer，但如果只停留在 2017 年原始 Transformer 的结构，很容易读不懂后面的细节。现在主流 LLM 的基本骨架还是 Transformer，但很多关键部件已经换过一轮：decoder-only、pre-norm、RMSNorm、SwiGLU、RoPE、GQA、KV cache、FlashAttention 等等。</p><p>这些改动单独看都不复杂，但合在一起，才构成现在 LLM block 的真实样子。</p><h2 id="1-从-encoder-decoder-到-decoder-only"><a href="#1-从-encoder-decoder-到-decoder-only" class="headerlink" title="1. 从 encoder-decoder 到 decoder-only"></a>1. 从 encoder-decoder 到 decoder-only</h2><p>原始 Transformer 是为机器翻译设计的，所以它有 encoder 和 decoder。</p><p>Encoder 负责读完整个输入句子，decoder 负责一步一步生成目标句子。</p><p>但现在很多通用语言模型，比如 GPT、LLaMA、Qwen、DeepSeek，主要采用 decoder-only 结构。它没有单独的 encoder，而是把所有任务都改写成 next token prediction：</p><p>$$<br>p(x_t \mid x_1, x_2, …, x_{t-1})<br>$$</p><p>也就是说，模型只需要学会一件事：给定前面的 token，预测下一个 token。</p><p>这个形式非常统一。翻译、问答、总结、代码生成、数学推理，都可以变成“前面是一段 prompt，后面继续生成答案”。</p><p>这里有一个重要区别：</p><ul><li>BERT 这类 encoder-only 模型更像是在理解一整段文本</li><li>GPT 这类 decoder-only 模型更像是在从左到右生成文本</li></ul><p>LLM 选择 decoder-only，不是因为 encoder 不好，而是因为自回归生成和大规模预训练的目标天然对齐。</p><h2 id="2-Causal-attention：只能看过去"><a href="#2-Causal-attention：只能看过去" class="headerlink" title="2. Causal attention：只能看过去"></a>2. Causal attention：只能看过去</h2><p>Decoder-only 模型使用 causal self-attention。</p><p>普通 self-attention 中，每个 token 可以看见所有 token。但语言生成时，当前位置不能偷看未来 token，否则 next token prediction 就变成了作弊。</p><p>所以 causal attention 会加一个 mask，让第 $t$ 个位置只能关注 $1$ 到 $t$ 的 token。</p><p>如果一句话是：</p><blockquote><p>The cat sits on the</p></blockquote><p>模型预测下一个词时，可以看见 “The cat sits on the”，但不能提前看见答案 “mat”。</p><p>这个 mask 看起来只是训练细节，但它决定了模型的能力形态：LLM 是一步一步生成的，不是一次性把完整答案算出来。</p><p>这也解释了为什么长回答里有时会出现前后不一致。模型每一步都基于已经生成的内容继续走，如果前面方向偏了，后面会被带偏。</p><h2 id="3-Pre-Norm：深层网络更容易训练"><a href="#3-Pre-Norm：深层网络更容易训练" class="headerlink" title="3. Pre-Norm：深层网络更容易训练"></a>3. Pre-Norm：深层网络更容易训练</h2><p>原始 Transformer 使用 Post-Norm，大概形式是：</p><p>$$<br>x_{l+1} &#x3D; \mathrm{LayerNorm}(x_l + F(x_l))<br>$$</p><p>现代 LLM 更常见的是 Pre-Norm：</p><p>$$<br>x_{l+1} &#x3D; x_l + F(\mathrm{Norm}(x_l))<br>$$</p><p>差别是 normalization 放在 residual branch 前面还是后面。</p><p>Pre-Norm 的好处是深层模型训练更稳定。直觉上，residual path 更像一条“干净的高速通道”，梯度可以更直接地往前传，不会每层都被 normalization 包住。</p><p>这件事在小模型里可能没那么明显，但模型层数变深后，训练稳定性会变成核心问题。很多现代结构的变化，本质上都不是为了让单层表达力变强，而是为了让几百亿、几千亿参数模型真的能训起来。</p><h2 id="4-RMSNorm：少算一点，但保留关键尺度控制"><a href="#4-RMSNorm：少算一点，但保留关键尺度控制" class="headerlink" title="4. RMSNorm：少算一点，但保留关键尺度控制"></a>4. RMSNorm：少算一点，但保留关键尺度控制</h2><p>LayerNorm 会减均值、除标准差：</p><p>$$<br>\mathrm{LayerNorm}(x) &#x3D; \frac{x-\mu}{\sqrt{\sigma^2+\epsilon}} \odot g + b<br>$$</p><p>RMSNorm 去掉了减均值，只保留 root mean square 的尺度归一化：</p><p>$$<br>\mathrm{RMSNorm}(x) &#x3D; \frac{x}{\sqrt{\frac{1}{d}\sum_i x_i^2+\epsilon}} \odot g<br>$$</p><p>它关心的是向量整体尺度，而不是每一维相对均值的位置。</p><p>这有两个好处。</p><p>第一，计算更简单。对超大模型来说，每个 block 里省一点，整体就能省很多。</p><p>第二，它保留了最关键的稳定性作用：控制 activation 的尺度。</p><p>我觉得这里可以这样记：现代 LLM 里很多设计不是追求数学形式最完整，而是在“足够稳定”和“足够便宜”之间找平衡。</p><h2 id="5-FFN-从-ReLU-走向-SwiGLU"><a href="#5-FFN-从-ReLU-走向-SwiGLU" class="headerlink" title="5. FFN 从 ReLU 走向 SwiGLU"></a>5. FFN 从 ReLU 走向 SwiGLU</h2><p>Transformer block 里除了 attention，还有一个 feed-forward network。</p><p>原始 Transformer 用的是：</p><p>$$<br>\mathrm{FFN}(x) &#x3D; \max(0, xW_1+b_1)W_2+b_2<br>$$</p><p>现代 LLM 常用 SwiGLU 这类 gated FFN：</p><p>$$<br>\mathrm{SwiGLU}(x) &#x3D; \mathrm{Swish}(xW_1) \odot (xW_2)<br>$$</p><p>再接一个输出投影。</p><p>Gating 的直觉是：模型不只是对特征做非线性变换，还学会“哪些通道该打开，哪些通道该压下去”。</p><p>如果把 FFN 看成每个 token 独立做一次特征加工，那么 SwiGLU 相当于给这次加工加了一个动态开关。</p><p>很多模型报告不会花很大篇幅讲 FFN，但它非常重要。Transformer 里 attention 负责 token 之间的信息混合，FFN 负责每个 token 内部的特征变换。只看 attention，会低估 FFN 对模型容量的贡献。</p><h2 id="6-RoPE：把位置信息放进旋转里"><a href="#6-RoPE：把位置信息放进旋转里" class="headerlink" title="6. RoPE：把位置信息放进旋转里"></a>6. RoPE：把位置信息放进旋转里</h2><p>Self-attention 本身不包含顺序信息。对 attention 来说，如果不加位置编码，”A B C” 和 “C B A” 只是 token 集合不同排列，很难知道谁在前谁在后。</p><p>原始 Transformer 用 absolute positional encoding，把位置向量加到 token embedding 上。</p><p>现代 LLM 常用 RoPE，也就是 rotary position embedding。它不是简单把位置向量加进去，而是在 query 和 key 上做旋转，使 attention score 自然带上相对位置信息。</p><p>可以粗略理解成：</p><p>$$<br>q_m^\top k_n \rightarrow \text{a function of token content and relative distance } (m-n)<br>$$</p><p>RoPE 的关键好处是相对位置关系更自然，也更适合做一定程度的位置外推。</p><p>但这里不能过度神化 RoPE。RoPE scaling 可以帮助模型扩展 context length，但不等于模型自动学会利用超长上下文。位置能编码是一回事，模型能不能从 100K token 里稳定找出关键证据，是另一回事。</p><h2 id="7-Multi-head-attention-到-MQA-GQA"><a href="#7-Multi-head-attention-到-MQA-GQA" class="headerlink" title="7. Multi-head attention 到 MQA &#x2F; GQA"></a>7. Multi-head attention 到 MQA &#x2F; GQA</h2><p>标准 multi-head attention 里，每个 head 都有自己的 $Q, K, V$：</p><p>$$<br>\mathrm{Attention}(Q,K,V)&#x3D;\mathrm{softmax}\left(\frac{QK^\top}{\sqrt{d}}\right)V<br>$$</p><p>多头的作用是让模型从不同子空间看 token 之间的关系。</p><p>但推理时有一个很大的成本：KV cache。</p><p>生成第 $t$ 个 token 时，前面所有 token 的 key 和 value 都要保留下来。context 越长，KV cache 越大。</p><p>MQA（multi-query attention）让多个 query heads 共享一组 key&#x2F;value。GQA（grouped-query attention）则折中一下，让一组 query heads 共享一组 key&#x2F;value。</p><p>直觉上：</p><ul><li>MHA：每个注意力头都有自己的 K&#x2F;V，效果强但 cache 大</li><li>MQA：所有头共享 K&#x2F;V，cache 小但可能损失表达力</li><li>GQA：分组共享 K&#x2F;V，在效果和成本之间折中</li></ul><p>这类设计说明一个现实问题：现代 LLM 的结构不只是为了训练 loss，也要考虑 serving 成本。</p><p>一个模型如果 benchmark 很强但推理时 KV cache 爆炸，线上使用会很痛苦。</p><h2 id="8-KV-cache：生成式模型的核心工程问题"><a href="#8-KV-cache：生成式模型的核心工程问题" class="headerlink" title="8. KV cache：生成式模型的核心工程问题"></a>8. KV cache：生成式模型的核心工程问题</h2><p>自回归生成时，如果每生成一个 token 都重新计算前面所有 token 的 K&#x2F;V，成本会非常高。</p><p>KV cache 的做法是：前面 token 的 key 和 value 已经算过，就缓存起来，后面直接复用。</p><p>第一个 token 生成时，模型处理整个 prompt。</p><p>之后每一步生成新 token 时，只需要为新 token 计算新的 query、key、value，然后让 query 去关注缓存中的所有 K&#x2F;V。</p><p>这让推理快很多，但代价是显存占用会随着 sequence length 线性增长。</p><p>所以长上下文模型的一个核心瓶颈不是参数量，而是 cache。</p><p>这也解释了为什么 DeepSeek 的 MLA、Qwen 的 GQA、各种 KV compression 都很重要。它们不是论文里的小优化，而是直接影响模型能不能便宜地跑长上下文。</p><h2 id="9-FlashAttention：不是近似-attention"><a href="#9-FlashAttention：不是近似-attention" class="headerlink" title="9. FlashAttention：不是近似 attention"></a>9. FlashAttention：不是近似 attention</h2><p>FlashAttention 容易被误解成某种近似 attention。其实它算的是 exact attention，主要优化的是内存访问。</p><p>标准 attention 会显式构造很大的 attention matrix：</p><p>$$<br>QK^\top<br>$$</p><p>当 sequence length 很长时，这个矩阵非常大。</p><p>FlashAttention 的核心思路是 tiling：把计算分块搬到 GPU SRAM 里做，减少高带宽显存 HBM 的读写次数。</p><p>所以它解决的是一个工程瓶颈：不是 FLOPs 看起来多少，而是数据在 GPU 内存层级之间搬来搬去有多贵。</p><p>这一点很重要。读现代论文时，不能只看数学复杂度，也要看硬件实际执行。一个理论上复杂度低的方法，如果 GPU 上跑不快，未必有实际价值。</p><h2 id="10-一个现代-LLM-block-大概长什么样"><a href="#10-一个现代-LLM-block-大概长什么样" class="headerlink" title="10. 一个现代 LLM block 大概长什么样"></a>10. 一个现代 LLM block 大概长什么样</h2><p>把这些东西合起来，一个现代 decoder-only LLM block 可以粗略写成：</p><p>$$<br>x’ &#x3D; x + \mathrm{Attention}(\mathrm{RMSNorm}(x))<br>$$</p><p>$$<br>y &#x3D; x’ + \mathrm{SwiGLU}(\mathrm{RMSNorm}(x’))<br>$$</p><p>attention 里面可能包含 RoPE、GQA、KV cache、FlashAttention 等实现细节。</p><p>所以“基于 Transformer”这句话背后，其实已经包含很多工程化选择。</p><h2 id="11-读技术报告时可以看什么"><a href="#11-读技术报告时可以看什么" class="headerlink" title="11. 读技术报告时可以看什么"></a>11. 读技术报告时可以看什么</h2><p>看到一篇新模型技术报告，我会先扫这些点：</p><ul><li>是 decoder-only、encoder-decoder，还是 mixture 架构</li><li>用什么 normalization：LayerNorm、RMSNorm，pre-norm 还是 post-norm</li><li>FFN 用什么：普通 FFN、SwiGLU，还是 MoE FFN</li><li>attention 用什么：MHA、MQA、GQA、MLA、sparse attention</li><li>位置编码用什么：RoPE、ALiBi、RoPE scaling、YaRN</li><li>context length 是多少，怎么扩展出来的</li><li>KV cache 成本有没有特殊优化</li></ul><p>这些点比“参数量多大”更能说明模型到底做了什么。</p><p>参数量只是结果，结构选择才是模型训练和部署时真正要付出的成本。</p><h2 id="12-小结"><a href="#12-小结" class="headerlink" title="12. 小结"></a>12. 小结</h2><p>现代 LLM 没有抛弃 Transformer，而是在 Transformer 的关键部件上做了系统性替换。</p><p>我的理解可以压缩成一句话：</p><blockquote><p>Transformer 提供了大模型的基本计算图，现代 LLM 的创新很多是在稳定训练、提高吞吐、降低推理成本、扩展上下文这几个方向上补工程和结构细节。</p></blockquote><p>所以读后面的 MoE、长上下文、reasoning model、多模态模型时，不要只看最外层的概念。很多真正影响模型能力和成本的东西，藏在 block 级别的设计里。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Vaswani et al., <a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a></li><li>Shazeer, <a href="https://arxiv.org/abs/2002.05202">GLU Variants Improve Transformer</a></li><li>Su et al., <a href="https://arxiv.org/abs/2104.09864">RoFormer: Enhanced Transformer with Rotary Position Embedding</a></li><li>Dao et al., <a href="https://arxiv.org/abs/2205.14135">FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness</a></li><li>Ainslie et al., <a href="https://arxiv.org/abs/2305.13245">GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints</a></li><li>Qwen Team, <a href="https://arxiv.org/abs/2505.09388">Qwen3 Technical Report</a></li><li>DeepSeek-AI, <a href="https://arxiv.org/abs/2412.19437">DeepSeek-V3 Technical Report</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2025/09/06/modern-llm-notes-01-transformer-after-transformer/</id>
    <link href="https://sssssimonk.github.io/2025/09/06/modern-llm-notes-01-transformer-after-transformer/"/>
    <published>2025-09-06T12:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇先把现代大模型的结构底座梳理出来。</p>
<p>很多技术报告会说模型基于 Transformer，但如果只停留在 2017 年原始 Transformer 的结构，很容易读不懂后面的细节。现在主流 LLM 的基本骨架还是]]>
    </summary>
    <title>现代大模型笔记 01：Transformer 之后到底变了什么</title>
    <updated>2025-09-06T12:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="深度学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="深度学习" scheme="https://sssssimonk.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="注意力机制" scheme="https://sssssimonk.github.io/tags/%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6/"/>
    <category term="Transformer" scheme="https://sssssimonk.github.io/tags/Transformer/"/>
    <category term="自注意力" scheme="https://sssssimonk.github.io/tags/%E8%87%AA%E6%B3%A8%E6%84%8F%E5%8A%9B/"/>
    <content>
      <![CDATA[<p>这一篇整理注意力机制和 Transformer。它是 Deep Learning 章节和后面现代大模型章节之间的桥。</p><p>这篇不展开 GPT、BERT、MoE、RLHF 这些内容，只看 Transformer 为什么出现，以及 self-attention 到底在做什么。</p><p>可以先抓住一句话：</p><blockquote><p>Attention 让模型在处理某个位置时，直接从其他位置取信息，而不是把所有历史信息都压进一个固定 hidden state。</p></blockquote><h2 id="1-RNN-的瓶颈"><a href="#1-RNN-的瓶颈" class="headerlink" title="1. RNN 的瓶颈"></a>1. RNN 的瓶颈</h2><p>RNN 处理序列时，每个时间步依赖上一个 hidden state：</p><p>$$<br>h_t &#x3D; f(x_t, h_{t-1})<br>$$</p><p>这带来两个问题。</p><p>第一，长距离信息要经过很多步传递，容易衰减。</p><p>第二，计算难以并行，因为第 $t$ 步必须等第 $t-1$ 步算完。</p><p>比如翻译句子时，目标词可能需要关注源句中很远的词。RNN 要把这些信息逐步压缩进 hidden state，压力很大。</p><p>Attention 的想法是：不要只依赖最后一个 hidden state，而是让模型需要什么就去看什么。</p><h2 id="2-注意力机制（attention）的直觉"><a href="#2-注意力机制（attention）的直觉" class="headerlink" title="2. 注意力机制（attention）的直觉"></a>2. 注意力机制（attention）的直觉</h2><p>Attention 可以理解成一个信息检索过程。</p><p>当模型处理当前位置时，它会问：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">当前 token 需要从哪些其他 token 获取信息？<br></code></pre></td></tr></table></figure><p>比如句子：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">The animal didn&#x27;t cross the street because it was too tired.<br></code></pre></td></tr></table></figure><p>这里 “it” 指的是 “animal”，而不是 “street”。模型处理 “it” 时，需要关注前面的 “animal”。</p><p>Attention 就是在学习这种关联强度。</p><h2 id="3-查询、键和值（query、key、value）"><a href="#3-查询、键和值（query、key、value）" class="headerlink" title="3. 查询、键和值（query、key、value）"></a>3. 查询、键和值（query、key、value）</h2><p>Attention 常用 query、key、value 来描述。</p><p>可以用检索类比：</p><ul><li>Query：我现在想找什么</li><li>Key：每个位置提供的索引</li><li>Value：每个位置真正携带的信息</li></ul><p>对每个 token，模型都会生成三个向量：</p><p>$$<br>q_i &#x3D; x_iW_Q<br>$$</p><p>$$<br>k_i &#x3D; x_iW_K<br>$$</p><p>$$<br>v_i &#x3D; x_iW_V<br>$$</p><p>当前位置的 query 会和所有位置的 key 做相似度计算，得到 attention score。</p><h2 id="4-缩放点积注意力（scaled-dot-product-attention）"><a href="#4-缩放点积注意力（scaled-dot-product-attention）" class="headerlink" title="4. 缩放点积注意力（scaled dot-product attention）"></a>4. 缩放点积注意力（scaled dot-product attention）</h2><p>Transformer 里常用 scaled dot-product attention：</p><h1 id="text-Attention-Q-K-V"><a href="#text-Attention-Q-K-V" class="headerlink" title="$$\text{Attention}(Q,K,V)"></a>$$<br>\text{Attention}(Q,K,V)</h1><p>\text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V<br>$$</p><p>分成三步看：</p><ol><li>$QK^\top$ 计算 query 和 key 的相似度</li><li>除以 $\sqrt{d_k}$ 控制数值尺度</li><li>softmax 得到权重，再对 $V$ 做加权平均</li></ol><p>为什么要除以 $\sqrt{d_k}$？</p><p>如果向量维度很高，dot product 的数值可能变大，softmax 会变得过于尖锐，梯度不稳定。缩放项可以缓解这个问题。</p><h2 id="5-自注意力（self-attention）：序列内部互相看"><a href="#5-自注意力（self-attention）：序列内部互相看" class="headerlink" title="5. 自注意力（self-attention）：序列内部互相看"></a>5. 自注意力（self-attention）：序列内部互相看</h2><p>Self-attention 指 query、key、value 都来自同一个序列。</p><p>也就是说，每个 token 都可以关注同一句话里的其他 token。</p><p>比如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">The bank approved the loan.<br></code></pre></td></tr></table></figure><p>和：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">The bank is near the river.<br></code></pre></td></tr></table></figure><p>“bank” 的含义要靠上下文判断。Self-attention 让 “bank” 可以关注 “loan” 或 “river”，从而形成不同语义表示。</p><p>这也是 Transformer 适合语言建模的原因之一：每个 token 的表示不是孤立的，而是上下文化的。</p><h2 id="6-多头注意力（multi-head-attention）"><a href="#6-多头注意力（multi-head-attention）" class="headerlink" title="6. 多头注意力（multi-head attention）"></a>6. 多头注意力（multi-head attention）</h2><p>Multi-head attention 不是只做一次 attention，而是并行做多组 attention：</p><p>$$<br>\text{head}_i &#x3D; \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)<br>$$</p><p>然后把多个 head 拼接起来：</p><p>$$<br>\text{MultiHead}(Q,K,V)&#x3D;\text{Concat}(\text{head}_1,\dots,\text{head}_h)W^O<br>$$</p><p>直觉上，不同 head 可以关注不同关系。</p><p>一个 head 可能关注主谓关系，一个 head 可能关注指代关系，一个 head 可能关注局部相邻 token。当然实际 head 不一定这么干净可解释，但多头机制给了模型并行建模不同关系的能力。</p><h2 id="7-位置编码（positional-encoding）：Transformer-怎么知道顺序"><a href="#7-位置编码（positional-encoding）：Transformer-怎么知道顺序" class="headerlink" title="7. 位置编码（positional encoding）：Transformer 怎么知道顺序"></a>7. 位置编码（positional encoding）：Transformer 怎么知道顺序</h2><p>Self-attention 本身不包含顺序信息。</p><p>如果只看 attention 公式，输入 token 换个顺序，模型并不会天然知道谁在前谁在后。</p><p>所以 Transformer 需要加入 positional encoding。</p><p>原始 Transformer 使用正弦余弦位置编码：</p><p>$$<br>PE_{(pos,2i)}&#x3D;\sin\left(\frac{pos}{10000^{2i&#x2F;d}}\right)<br>$$</p><p>$$<br>PE_{(pos,2i+1)}&#x3D;\cos\left(\frac{pos}{10000^{2i&#x2F;d}}\right)<br>$$</p><p>现代模型里也常见 learned positional embedding、RoPE 等位置编码方式。</p><p>位置编码的核心目的很简单：让模型知道 token 的顺序和相对位置。</p><h2 id="8-Transformer-模块（Transformer-block）的基本结构"><a href="#8-Transformer-模块（Transformer-block）的基本结构" class="headerlink" title="8. Transformer 模块（Transformer block）的基本结构"></a>8. Transformer 模块（Transformer block）的基本结构</h2><p>一个 Transformer block 通常包含：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">self-attention<br>-&gt; residual connection<br>-&gt; layer normalization<br>-&gt; feed-forward network<br>-&gt; residual connection<br>-&gt; layer normalization<br></code></pre></td></tr></table></figure><p>Feed-forward network 通常是对每个 token 独立应用的 MLP：</p><p>$$<br>FFN(x)&#x3D;W_2\sigma(W_1x+b_1)+b_2<br>$$</p><p>Self-attention 负责 token 之间的信息交互，FFN 负责对每个 token 的表示做非线性变换。</p><p>Residual connection 让深层网络更容易训练，LayerNorm 让表示尺度更稳定。</p><h2 id="9-编码器（encoder）和解码器（decoder）"><a href="#9-编码器（encoder）和解码器（decoder）" class="headerlink" title="9. 编码器（encoder）和解码器（decoder）"></a>9. 编码器（encoder）和解码器（decoder）</h2><p>原始 Transformer 有 encoder 和 decoder。</p><p>Encoder 读入输入序列，生成上下文表示。机器翻译里，encoder 读源语言句子。</p><p>Decoder 生成输出序列。它有 masked self-attention，保证生成第 $t$ 个 token 时不能偷看未来 token。</p><p>简单对比：</p><table><thead><tr><th>结构</th><th>作用</th><th>典型模型</th></tr></thead><tbody><tr><td>Encoder-only</td><td>理解输入</td><td>BERT</td></tr><tr><td>Decoder-only</td><td>自回归生成</td><td>GPT</td></tr><tr><td>Encoder-decoder</td><td>输入到输出转换</td><td>T5、原始翻译 Transformer</td></tr></tbody></table><p>这部分后面讲现代大模型时还会展开。这里只要先记住：GPT 类模型主要是 decoder-only Transformer。</p><h2 id="10-为什么-Transformer-更适合大规模训练"><a href="#10-为什么-Transformer-更适合大规模训练" class="headerlink" title="10. 为什么 Transformer 更适合大规模训练"></a>10. 为什么 Transformer 更适合大规模训练</h2><p>相比 RNN，Transformer 的优势很明显。</p><p>RNN 必须按时间步顺序计算：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">h1 -&gt; h2 -&gt; h3 -&gt; ... -&gt; hn<br></code></pre></td></tr></table></figure><p>Transformer 的 self-attention 可以并行处理整段序列：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">all tokens -&gt; attention matrix -&gt; contextual representations<br></code></pre></td></tr></table></figure><p>这让它更适合 GPU 并行计算。</p><p>此外，self-attention 让任意两个 token 之间的信息路径更短。RNN 里第 1 个 token 到第 100 个 token 要经过很多步，Transformer 里可以直接通过 attention 建立联系。</p><p>代价是 self-attention 的计算和显存通常随序列长度平方增长：</p><p>$$<br>O(n^2)<br>$$</p><p>所以长上下文 Transformer 需要各种效率优化。</p><h2 id="11-几个点"><a href="#11-几个点" class="headerlink" title="11. 几个点"></a>11. 几个点</h2><p>Attention 的本质是根据相关性对 value 做加权汇聚。</p><p>Query、key、value 不需要背成术语。Query 是当前位置的问题，key 是其他位置的索引，value 是真正要取的信息。</p><p>Self-attention 让每个 token 的表示变成上下文化表示。</p><p>Multi-head attention 给模型多个并行视角，不同 head 可以关注不同关系。</p><p>Transformer 能替代很多 RNN 场景，一个关键原因是并行训练效率高，另一个关键原因是长距离依赖路径短。</p><p>Transformer 不是大模型本身，但它是现代 LLM 的基础结构。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a></li><li><a href="https://jalammar.github.io/illustrated-transformer/">The Illustrated Transformer</a></li><li><a href="https://d2l.ai/chapter_attention-mechanisms-and-transformers/index.html">Dive into Deep Learning: Attention Mechanisms</a></li><li><a href="https://web.stanford.edu/class/cs224n/">Stanford CS224N: Natural Language Processing with Deep Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/08/31/deep-learning-notes-05-attention-transformer/</id>
    <link href="https://sssssimonk.github.io/2024/08/31/deep-learning-notes-05-attention-transformer/"/>
    <published>2024-08-31T14:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理注意力机制和 Transformer。它是 Deep Learning 章节和后面现代大模型章节之间的桥。</p>
<p>这篇不展开 GPT、BERT、MoE、RLHF 这些内容，只看 Transformer 为什么出现，以及 self-attention]]>
    </summary>
    <title>深度学习笔记 05：注意力机制与 Transformer</title>
    <updated>2024-08-31T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="深度学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="深度学习" scheme="https://sssssimonk.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="CNN" scheme="https://sssssimonk.github.io/tags/CNN/"/>
    <category term="RNN" scheme="https://sssssimonk.github.io/tags/RNN/"/>
    <category term="LSTM" scheme="https://sssssimonk.github.io/tags/LSTM/"/>
    <category term="ResNet" scheme="https://sssssimonk.github.io/tags/ResNet/"/>
    <content>
      <![CDATA[<p>这一篇整理 CNN、RNN、LSTM、GRU 这些经典结构。这里不写成模型百科，只抓它们各自利用了什么数据结构假设。</p><p>可以先从一个问题出发：</p><blockquote><p>如果 MLP 已经可以拟合复杂函数，为什么还需要 CNN、RNN 这些结构？</p></blockquote><p>答案是：不同数据有不同结构。图像有局部空间结构，序列有时间顺序结构。专门的网络结构把这些先验写进模型里，让模型更高效地学习。</p><h2 id="1-归纳偏置（inductive-bias）"><a href="#1-归纳偏置（inductive-bias）" class="headerlink" title="1. 归纳偏置（inductive bias）"></a>1. 归纳偏置（inductive bias）</h2><p>Inductive bias 可以理解成模型对数据结构的默认假设。</p><p>MLP 的假设很弱。它把输入展开成向量后，每个输入维度和每个 hidden unit 都连接起来。</p><p>如果输入是一张图片，MLP 并不知道左上角像素和它旁边像素关系更近，也不知道同一个边缘模式可以出现在图片不同位置。</p><p>CNN 就把这些假设写进模型：</p><ul><li>局部连接：相邻像素更相关</li><li>权重共享：同一个 pattern 可以出现在不同位置</li><li>平移等变：输入平移时，feature map 也对应平移</li></ul><p>RNN 则把序列假设写进模型：</p><ul><li>当前状态依赖过去状态</li><li>同一组参数可以反复处理不同时间步</li><li>顺序本身有意义</li></ul><p>这些结构不只是为了减少参数，也是在告诉模型应该怎样看数据。</p><h2 id="2-卷积神经网络（CNN）为什么适合图像"><a href="#2-卷积神经网络（CNN）为什么适合图像" class="headerlink" title="2. 卷积神经网络（CNN）为什么适合图像"></a>2. 卷积神经网络（CNN）为什么适合图像</h2><p>图像有很强的局部结构。一个像素通常和周围像素关系更大，而不是和很远的像素关系同等重要。</p><p>CNN 使用 convolution kernel 在图像上滑动：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">small kernel<br>-&gt; slide over image<br>-&gt; produce feature map<br></code></pre></td></tr></table></figure><p>比如一个 $3 \times 3$ kernel 每次只看局部区域。它可以检测边缘、角点、纹理等局部 pattern。</p><p>如果某个 kernel 学会检测垂直边缘，那么它可以在整张图上重复使用。同一个边缘检测器不需要为每个位置单独学一套参数。</p><p>这就是 weight sharing。</p><h2 id="3-卷积（convolution）的几个概念"><a href="#3-卷积（convolution）的几个概念" class="headerlink" title="3. 卷积（convolution）的几个概念"></a>3. 卷积（convolution）的几个概念</h2><p>常见概念：</p><table><thead><tr><th>概念</th><th>含义</th></tr></thead><tbody><tr><td>Kernel &#x2F; filter</td><td>用来扫描局部区域的小矩阵</td></tr><tr><td>Feature map</td><td>kernel 扫描后得到的响应图</td></tr><tr><td>Stride</td><td>每次滑动的步长</td></tr><tr><td>Padding</td><td>在边缘补 0，控制输出尺寸</td></tr><tr><td>Channel</td><td>输入或输出的通道数</td></tr></tbody></table><p>如果 stride 大，输出 feature map 会变小；如果 padding 合适，可以让输出保持原尺寸。</p><p>一个直觉例子：检测猫脸时，前面层可能检测边缘和纹理，中间层检测眼睛、耳朵、鼻子，后面层组合成更完整的猫脸结构。</p><p>CNN 的层级结构和图像视觉层级比较匹配。</p><h2 id="4-池化（pooling）：降低空间分辨率"><a href="#4-池化（pooling）：降低空间分辨率" class="headerlink" title="4. 池化（pooling）：降低空间分辨率"></a>4. 池化（pooling）：降低空间分辨率</h2><p>Pooling 常用于降低 feature map 尺寸。</p><p>Max pooling 取局部窗口里的最大值：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">2x2 window -&gt; max value<br></code></pre></td></tr></table></figure><p>它的作用包括：</p><ul><li>降低计算量</li><li>增大感受野</li><li>提供一定平移不变性</li></ul><p>比如一个边缘稍微移动几像素，max pooling 后的响应可能仍然相似。</p><p>不过现代 CNN 里不一定大量依赖 pooling，有些结构会用 stride convolution 来完成下采样。</p><h2 id="5-ResNet：为什么残差连接（residual-connection）重要"><a href="#5-ResNet：为什么残差连接（residual-connection）重要" class="headerlink" title="5. ResNet：为什么残差连接（residual connection）重要"></a>5. ResNet：为什么残差连接（residual connection）重要</h2><p>网络变深后，训练会变难。一个问题是：更深的网络理论上表达能力更强，但实际训练时可能反而效果变差。</p><p>ResNet 的核心是 residual connection：</p><p>$$<br>y &#x3D; F(x) + x<br>$$</p><p>模型不直接学习完整映射 $H(x)$，而是学习 residual：</p><p>$$<br>F(x) &#x3D; H(x) - x<br>$$</p><p>直觉上，如果某几层暂时不需要做复杂变换，模型可以让 $F(x)$ 接近 0，这样输出接近 $x$。这让深层网络更容易优化。</p><p>Residual connection 也让梯度可以更直接地传回前面层。这个思想后来不只用于 CNN，也广泛出现在 Transformer 里。</p><h2 id="6-RNN：处理序列的基本思路"><a href="#6-RNN：处理序列的基本思路" class="headerlink" title="6. RNN：处理序列的基本思路"></a>6. RNN：处理序列的基本思路</h2><p>RNN 用 hidden state 记录过去信息。</p><p>最基本形式：</p><p>$$<br>h_t &#x3D; \phi(W_xx_t + W_hh_{t-1} + b)<br>$$</p><p>$$<br>y_t &#x3D; W_yh_t<br>$$</p><p>其中，$x_t$ 是当前时间步输入，$h_{t-1}$ 是上一个时间步的 hidden state。</p><p>比如句子：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">I really like this movie<br></code></pre></td></tr></table></figure><p>RNN 会按顺序读 token，每一步更新 hidden state。理论上，最后的 hidden state 可以包含前面读到的信息。</p><p>RNN 的优点是结构自然适合序列；缺点是长序列训练困难，难以捕捉很长距离的依赖。</p><h2 id="7-RNN-的梯度问题"><a href="#7-RNN-的梯度问题" class="headerlink" title="7. RNN 的梯度问题"></a>7. RNN 的梯度问题</h2><p>RNN 的反向传播要沿时间展开，也叫 backpropagation through time。</p><p>如果序列很长，gradient 会经过很多时间步反传。这和深层网络里的梯度消失&#x2F;爆炸类似。</p><p>一个常见问题是 long-term dependency。</p><p>比如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">The book that I bought last week and left on the table is interesting.<br></code></pre></td></tr></table></figure><p>如果要判断主语和谓语关系，模型需要记住很早之前的 “book”。普通 RNN 很容易在长距离依赖上表现差。</p><h2 id="8-LSTM-和-GRU：用门控机制控制记忆"><a href="#8-LSTM-和-GRU：用门控机制控制记忆" class="headerlink" title="8. LSTM 和 GRU：用门控机制控制记忆"></a>8. LSTM 和 GRU：用门控机制控制记忆</h2><p>LSTM 的核心是 cell state 和 gate。</p><p>它通过门控机制控制：</p><ul><li>忘掉哪些旧信息</li><li>写入哪些新信息</li><li>输出哪些状态</li></ul><p>可以把 LSTM 想成比普通 RNN 多了一条更稳定的记忆通道。</p><p>GRU 是更简化的门控 RNN，参数更少，训练更轻一些。</p><p>常见对比：</p><table><thead><tr><th>模型</th><th>核心</th><th>特点</th></tr></thead><tbody><tr><td>RNN</td><td>hidden state</td><td>简单，但长依赖困难</td></tr><tr><td>LSTM</td><td>cell state + gates</td><td>更擅长长依赖，参数较多</td></tr><tr><td>GRU</td><td>simplified gates</td><td>比 LSTM 简洁</td></tr></tbody></table><p>LSTM &#x2F; GRU 不是彻底解决长距离依赖，只是比普通 RNN 更稳定。</p><h2 id="9-为什么注意力机制（attention）后来替代很多-RNN"><a href="#9-为什么注意力机制（attention）后来替代很多-RNN" class="headerlink" title="9. 为什么注意力机制（attention）后来替代很多 RNN"></a>9. 为什么注意力机制（attention）后来替代很多 RNN</h2><p>RNN 有一个天然问题：它按时间一步步处理序列，难以并行。</p><p>如果序列长度是 1000，RNN 需要从第 1 步一路算到第 1000 步。后一步依赖前一步 hidden state。</p><p>Attention 的思路不同。它允许当前位置直接看序列中其他位置，不一定要把信息压进一个 hidden state 里一步步传递。</p><p>这带来两个优势：</p><ul><li>更容易建模长距离依赖</li><li>更容易并行训练</li></ul><p>所以在 NLP 和大模型里，Transformer 逐渐成为主流。</p><h2 id="10-几个点"><a href="#10-几个点" class="headerlink" title="10. 几个点"></a>10. 几个点</h2><p>CNN 的关键不是“卷积公式”，而是局部连接和权重共享。它利用了图像的空间结构。</p><p>ResNet 的 residual connection 让深层网络更容易优化，这个思想后来影响很大。</p><p>RNN 适合序列，但长距离依赖和并行效率是它的核心瓶颈。</p><p>LSTM &#x2F; GRU 通过门控机制缓解 RNN 的梯度和记忆问题，但没有完全摆脱序列计算瓶颈。</p><p>CNN、RNN、Transformer 的区别，本质上是它们对数据结构的不同假设。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://cs231n.github.io/convolutional-networks/">Stanford CS231n: Convolutional Neural Networks</a></li><li><a href="https://arxiv.org/abs/1512.03385">Deep Residual Learning for Image Recognition</a></li><li><a href="https://colah.github.io/posts/2015-08-Understanding-LSTMs/">Understanding LSTM Networks</a></li><li><a href="https://d2l.ai/chapter_recurrent-neural-networks/index.html">Dive into Deep Learning: Recurrent Neural Networks</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/08/24/deep-learning-notes-04-cnn-rnn-classic-architectures/</id>
    <link href="https://sssssimonk.github.io/2024/08/24/deep-learning-notes-04-cnn-rnn-classic-architectures/"/>
    <published>2024-08-24T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 CNN、RNN、LSTM、GRU 这些经典结构。这里不写成模型百科，只抓它们各自利用了什么数据结构假设。</p>
<p>可以先从一个问题出发：</p>
<blockquote>
<p>如果 MLP 已经可以拟合复杂函数，为什么还需要 CNN、RNN]]>
    </summary>
    <title>深度学习笔记 04：CNN、RNN 与经典结构</title>
    <updated>2024-08-24T13:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="深度学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="深度学习" scheme="https://sssssimonk.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="优化器" scheme="https://sssssimonk.github.io/tags/%E4%BC%98%E5%8C%96%E5%99%A8/"/>
    <category term="Adam" scheme="https://sssssimonk.github.io/tags/Adam/"/>
    <category term="AdamW" scheme="https://sssssimonk.github.io/tags/AdamW/"/>
    <category term="正则化" scheme="https://sssssimonk.github.io/tags/%E6%AD%A3%E5%88%99%E5%8C%96/"/>
    <content>
      <![CDATA[<p>这一篇整理深度网络训练里最常遇到的一组问题：梯度为什么会不稳定，初始化和归一化为什么重要，以及优化器从 SGD 到 AdamW 到底在改什么。</p><p>可以先把训练过程看成一条链：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">initialize parameters<br>-&gt; forward pass<br>-&gt; compute loss<br>-&gt; backprop gradients<br>-&gt; optimizer updates parameters<br>-&gt; repeat<br></code></pre></td></tr></table></figure><p>深度学习训练难，主要不是因为这个流程难写，而是因为每一步都可能影响梯度传播和收敛稳定性。</p><h2 id="1-梯度消失与梯度爆炸"><a href="#1-梯度消失与梯度爆炸" class="headerlink" title="1. 梯度消失与梯度爆炸"></a>1. 梯度消失与梯度爆炸</h2><p>深层网络里，gradient 要从 loss 一层层传回前面层。</p><p>如果每一层的梯度因子都小于 1，多层相乘后会越来越小，这就是 vanishing gradients。</p><p>如果每一层的梯度因子都大于 1，多层相乘后会越来越大，这就是 exploding gradients。</p><p>一个粗略例子：</p><p>$$<br>0.5^{20} \approx 9.5 \times 10^{-7}<br>$$</p><p>如果反向传播里连续乘很多个 0.5，前面层几乎收不到有效梯度。</p><p>反过来：</p><p>$$<br>1.5^{20} \approx 3325<br>$$</p><p>如果连续乘很多个大于 1 的因子，梯度可能爆炸，训练变得不稳定。</p><p>这也是为什么 deep network 不是简单“多堆几层”就行。层数变深后，梯度能不能稳定传过去本身就是问题。</p><h2 id="2-参数初始化（initialization）"><a href="#2-参数初始化（initialization）" class="headerlink" title="2. 参数初始化（initialization）"></a>2. 参数初始化（initialization）</h2><p>初始化的目标不是找一个好模型，而是给训练一个合适起点。</p><p>如果权重太小，信号逐层传递后可能越来越弱；如果权重太大，activation 和 gradient 可能变得不稳定。</p><p>常见初始化包括 Xavier initialization 和 He initialization。</p><p>Xavier initialization 适合 tanh &#x2F; sigmoid 这类 activation，核心想法是让每层输入输出的方差保持相对稳定。</p><p>He initialization 更适合 ReLU，因为 ReLU 会把一部分负值截断为 0。</p><p>直觉上，初始化是在控制每一层信号的尺度。尺度合适，forward 和 backward 都更容易稳定。</p><h2 id="3-归一化（normalization）"><a href="#3-归一化（normalization）" class="headerlink" title="3. 归一化（normalization）"></a>3. 归一化（normalization）</h2><p>Normalization 的作用是让中间表示的分布更稳定。</p><p>Batch Normalization 通常对 batch 维度做归一化：</p><p>$$<br>\hat{x} &#x3D; \frac{x-\mu_B}{\sqrt{\sigma_B^2+\epsilon}}<br>$$</p><p>其中，$\mu_B$ 和 $\sigma_B^2$ 是当前 batch 的均值和方差。</p><p>BatchNorm 在 CNN 里很常见。它能让训练更稳定，也允许使用更大的 learning rate。</p><p>Layer Normalization 则通常对单个样本内部的 hidden dimension 做归一化。Transformer 里更常用 LayerNorm，因为序列任务里 batch 统计不一定稳定，而且不同 token 长度和并行方式会让 BatchNorm 不方便。</p><p>可以这样粗略理解：</p><table><thead><tr><th>方法</th><th>主要归一化维度</th><th>常见场景</th></tr></thead><tbody><tr><td>BatchNorm</td><td>batch 维度</td><td>CNN</td></tr><tr><td>LayerNorm</td><td>feature &#x2F; hidden 维度</td><td>Transformer、RNN</td></tr></tbody></table><p>Normalization 不只是让数值好看，它会影响优化过程。</p><h2 id="4-随机失活（dropout）：训练时故意丢掉一部分连接"><a href="#4-随机失活（dropout）：训练时故意丢掉一部分连接" class="headerlink" title="4. 随机失活（dropout）：训练时故意丢掉一部分连接"></a>4. 随机失活（dropout）：训练时故意丢掉一部分连接</h2><p>Dropout 是一种正则化方法。训练时随机把一部分 hidden units 置为 0：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">h = dropout(h, p=0.5)<br></code></pre></td></tr></table></figure><p>它的直觉是：不要让模型过度依赖某几个 neuron，而是让不同子网络都能工作。</p><p>测试时通常不再随机丢弃，而是使用完整网络，并对激活值做相应缩放。</p><p>Dropout 在早期深度网络里很常见。现在大模型里也会用 dropout，但很多大规模预训练模型会把 dropout 设得比较小，甚至某些设置下不用。</p><h2 id="5-学习率（learning-rate）和-scheduler"><a href="#5-学习率（learning-rate）和-scheduler" class="headerlink" title="5. 学习率（learning rate）和 scheduler"></a>5. 学习率（learning rate）和 scheduler</h2><p>Learning rate 控制每次参数更新的步长。</p><p>如果太小，训练慢；如果太大，loss 可能震荡甚至发散。</p><p>深度学习里常见 scheduler：</p><ul><li>warmup：训练初期逐渐增大学习率</li><li>step decay：训练到某些 epoch 后降低学习率</li><li>cosine decay：按余弦曲线逐渐降低学习率</li></ul><p>Warmup 在 Transformer 训练里尤其常见。训练初期参数还很随机，如果一开始 learning rate 太大，更新可能过猛。Warmup 让模型先稳定进入训练状态。</p><h2 id="6-SGD：最基础的随机梯度下降"><a href="#6-SGD：最基础的随机梯度下降" class="headerlink" title="6. SGD：最基础的随机梯度下降"></a>6. SGD：最基础的随机梯度下降</h2><p>Stochastic Gradient Descent 使用 mini-batch gradient 更新参数：</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \eta g_t<br>$$</p><p>其中，$\eta$ 是 learning rate，$g_t$ 是当前 mini-batch 上的 gradient。</p><p>SGD 的优点是简单、泛化表现常常不错。缺点是更新方向受当前 batch 影响，可能噪声较大，收敛也可能比较慢。</p><p>比如 loss surface 像一个狭长山谷，SGD 可能在山谷两侧来回震荡，同时沿着谷底方向前进很慢。</p><h2 id="7-动量法（momentum）：加入历史方向"><a href="#7-动量法（momentum）：加入历史方向" class="headerlink" title="7. 动量法（momentum）：加入历史方向"></a>7. 动量法（momentum）：加入历史方向</h2><p>Momentum 的想法是：不要只看当前 gradient，也看过去一段时间的更新方向。</p><p>常见形式：</p><p>$$<br>v_t &#x3D; \beta v_{t-1} + g_t<br>$$</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \eta v_t<br>$$</p><p>其中，$v_t$ 是 velocity，$\beta$ 控制保留多少历史方向。</p><p>直觉上，如果多个 step 的 gradient 方向一致，Momentum 会加速；如果某个方向来回震荡，正负更新会互相抵消。</p><p>这很像推一个球下山。SGD 每一步都只看当前坡度，Momentum 让球带一点惯性。</p><h2 id="8-RMSProp：给每个参数自适应学习率"><a href="#8-RMSProp：给每个参数自适应学习率" class="headerlink" title="8. RMSProp：给每个参数自适应学习率"></a>8. RMSProp：给每个参数自适应学习率</h2><p>RMSProp 关注的是每个参数最近 gradient 的平方平均。</p><p>$$<br>s_t &#x3D; \rho s_{t-1} + (1-\rho)g_t^2<br>$$</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \frac{\eta}{\sqrt{s_t}+\epsilon}g_t<br>$$</p><p>如果某个参数的 gradient 一直很大，$s_t$ 会变大，于是它的有效学习率会变小。</p><p>如果某个参数的 gradient 一直很小，$s_t$ 小，有效学习率相对更大。</p><p>RMSProp 的直觉是：不同参数不一定应该用同一个步长。频繁大幅变化的方向要走得谨慎，变化小的方向可以走得相对积极。</p><h2 id="9-Adam：动量法（momentum）-RMSProp"><a href="#9-Adam：动量法（momentum）-RMSProp" class="headerlink" title="9. Adam：动量法（momentum）+ RMSProp"></a>9. Adam：动量法（momentum）+ RMSProp</h2><p>Adam 可以看作把 Momentum 和 RMSProp 结合起来。</p><p>它维护一阶矩估计：</p><p>$$<br>m_t &#x3D; \beta_1 m_{t-1} + (1-\beta_1)g_t<br>$$</p><p>也维护二阶矩估计：</p><p>$$<br>v_t &#x3D; \beta_2 v_{t-1} + (1-\beta_2)g_t^2<br>$$</p><p>然后做 bias correction：</p><p>$$<br>\hat{m}_t &#x3D; \frac{m_t}{1-\beta_1^t}<br>$$</p><p>$$<br>\hat{v}_t &#x3D; \frac{v_t}{1-\beta_2^t}<br>$$</p><p>最后更新：</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon}<br>$$</p><p>Adam 的直觉：</p><ul><li>$m_t$ 像 Momentum，记录梯度方向的滑动平均</li><li>$v_t$ 像 RMSProp，记录梯度平方的滑动平均</li><li>分母让每个参数有自适应步长</li></ul><p>Adam 的默认参数经常是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">beta1 = 0.9<br>beta2 = 0.999<br>epsilon = 1e-8<br></code></pre></td></tr></table></figure><p>Adam 的优点是上手快、收敛稳定、对 learning rate 没有 SGD 那么敏感。这也是它在深度学习里非常常用的原因。</p><h2 id="10-AdamW：把-weight-decay-解耦出来"><a href="#10-AdamW：把-weight-decay-解耦出来" class="headerlink" title="10. AdamW：把 weight decay 解耦出来"></a>10. AdamW：把 weight decay 解耦出来</h2><p>AdamW 是理解现代训练时很重要的优化器。</p><p>先看 Adam 里常见的 L2 regularization。如果把 L2 penalty 加到 loss 里：</p><p>$$<br>L’(\theta)&#x3D;L(\theta)+\frac{\lambda}{2}|\theta|^2<br>$$</p><p>那么梯度会变成：</p><p>$$<br>g’_t &#x3D; g_t + \lambda \theta_t<br>$$</p><p>在 SGD 里，这和 weight decay 基本等价：</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \eta(g_t + \lambda\theta_t)<br>$$</p><p>可以整理成：</p><p>$$<br>\theta_{t+1} &#x3D; (1-\eta\lambda)\theta_t - \eta g_t<br>$$</p><p>也就是每步先把权重衰减一点，再按 gradient 更新。</p><p>但在 Adam 里，情况不一样。Adam 会用二阶矩 $\sqrt{\hat{v}_t}$ 对梯度做自适应缩放。如果把 $\lambda\theta_t$ 混进 gradient，它也会被 Adam 的自适应项缩放。</p><p>这样 weight decay 就不再是“统一把权重变小”，而是和每个参数的梯度历史纠缠在一起。</p><p>AdamW 的做法是把 weight decay 从 gradient update 里解耦出来：</p><p>$$<br>\theta_{t+1} &#x3D; \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t}+\epsilon} - \eta\lambda\theta_t<br>$$</p><p>也可以理解成：</p><ul><li>Adam update: use gradient statistics to update parameters</li><li>Weight decay: separately shrink weights</li></ul><p>这就是 AdamW 的核心变化：weight decay 不再被 Adam 的自适应梯度缩放影响。</p><h2 id="11-Adam-和-AdamW-的区别怎么记"><a href="#11-Adam-和-AdamW-的区别怎么记" class="headerlink" title="11. Adam 和 AdamW 的区别怎么记"></a>11. Adam 和 AdamW 的区别怎么记</h2><p>可以用一句话记：</p><blockquote><p>Adam 里的 L2 regularization 是把 weight decay 混进 gradient；AdamW 是把 weight decay 作为单独的参数衰减步骤。</p></blockquote><p>在 Adam 里：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">gradient = gradient + weight_decay * parameter<br>adaptive_update(gradient)<br></code></pre></td></tr></table></figure><p>在 AdamW 里：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">adaptive_update(gradient)<br>parameter = parameter - lr * weight_decay * parameter<br></code></pre></td></tr></table></figure><p>这也是为什么训练 Transformer &#x2F; LLM 时，AdamW 比 Adam 更常见。它让 weight decay 的作用更接近正则化直觉。</p><h2 id="12-优化器对比"><a href="#12-优化器对比" class="headerlink" title="12. 优化器对比"></a>12. 优化器对比</h2><table><thead><tr><th>优化器</th><th>核心想法</th><th>优点</th><th>常见问题</th></tr></thead><tbody><tr><td>SGD</td><td>当前 mini-batch gradient</td><td>简单，泛化常不错</td><td>收敛慢，对 lr 敏感</td></tr><tr><td>Momentum</td><td>加入历史方向</td><td>减少震荡，加速一致方向</td><td>多一个 momentum 参数</td></tr><tr><td>RMSProp</td><td>自适应缩放每个参数</td><td>处理不同尺度梯度</td><td>只看二阶信息</td></tr><tr><td>Adam</td><td>Momentum + RMSProp</td><td>稳定、好用、收敛快</td><td>weight decay 处理不理想</td></tr><tr><td>AdamW</td><td>Adam + decoupled weight decay</td><td>现代深度学习常用</td><td>仍然需要调 lr 和 decay</td></tr></tbody></table><p>实际训练里，优化器不是越复杂越好。SGD 在一些视觉任务里仍然很强，AdamW 在 Transformer 训练里非常常见。</p><h2 id="13-几个点"><a href="#13-几个点" class="headerlink" title="13. 几个点"></a>13. 几个点</h2><p>深度网络训练的难点不是只会不会写 <code>loss.backward()</code>，而是梯度是否稳定、参数尺度是否合理、优化器是否适合任务。</p><p>Initialization 和 normalization 都在服务同一个目标：让信号和梯度在网络中稳定传播。</p><p>SGD 只看当前 gradient，Momentum 加入历史方向，RMSProp 加入梯度平方的滑动平均，Adam 同时使用一阶和二阶矩估计。</p><p>AdamW 的关键不是“比 Adam 多一个 W”，而是 decoupled weight decay。这个区别在现代大模型训练里很重要。</p><p>Learning rate 仍然是最重要的超参数之一。用了 AdamW 也不代表不需要认真调学习率。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://arxiv.org/abs/1412.6980">Adam: A Method for Stochastic Optimization</a></li><li><a href="https://arxiv.org/abs/1711.05101">Decoupled Weight Decay Regularization</a></li><li><a href="https://d2l.ai/chapter_optimization/index.html">Dive into Deep Learning: Optimization Algorithms</a></li><li><a href="https://cs231n.github.io/neural-networks-3/">CS231n: Neural Networks Part 3</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/08/17/deep-learning-notes-03-training-and-optimizers/</id>
    <link href="https://sssssimonk.github.io/2024/08/17/deep-learning-notes-03-training-and-optimizers/"/>
    <published>2024-08-17T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理深度网络训练里最常遇到的一组问题：梯度为什么会不稳定，初始化和归一化为什么重要，以及优化器从 SGD 到 AdamW 到底在改什么。</p>
<p>可以先把训练过程看成一条链：</p>
<figure class="highlight]]>
    </summary>
    <title>深度学习笔记 03：深度网络训练与优化器</title>
    <updated>2024-08-17T13:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="深度学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="深度学习" scheme="https://sssssimonk.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="MLP" scheme="https://sssssimonk.github.io/tags/MLP/"/>
    <category term="反向传播" scheme="https://sssssimonk.github.io/tags/%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/"/>
    <category term="计算图" scheme="https://sssssimonk.github.io/tags/%E8%AE%A1%E7%AE%97%E5%9B%BE/"/>
    <content>
      <![CDATA[<p>这一篇整理最基础的神经网络：多层感知机（MLP），以及神经网络到底如何通过前向传播和反向传播完成训练。</p><p>可以先抓住一句话：</p><blockquote><p>Forward pass 负责算预测和 loss，backpropagation 负责把 loss 对每个参数的梯度算出来，optimizer 再根据梯度更新参数。</p></blockquote><p>这条链路是 deep learning 的训练核心。</p><h2 id="1-神经元（neuron）在做什么"><a href="#1-神经元（neuron）在做什么" class="headerlink" title="1. 神经元（neuron）在做什么"></a>1. 神经元（neuron）在做什么</h2><p>一个最简单的 neuron 可以写成：</p><p>$$<br>z &#x3D; w^\top x + b<br>$$</p><p>$$<br>h &#x3D; \sigma(z)<br>$$</p><p>其中，$x$ 是输入，$w$ 是权重，$b$ 是 bias，$\sigma$ 是 activation function。</p><p>如果没有 activation，这就是线性模型的一部分；加上 activation 后，它可以引入非线性。</p><p>一个具体例子：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">x = [study_hours, sleep_hours]<br>w = [0.8, 0.3]<br>b = -2<br></code></pre></td></tr></table></figure><p>这个 neuron 可能在粗略判断“考试通过倾向”。学习时间和睡眠时间越高，$z$ 越大，经过 sigmoid 后输出越接近 1。</p><p>当然真实神经网络里的 neuron 不会这么直接可解释，但这个例子能帮助理解它的基本计算。</p><h2 id="2-层（layer）就是一组神经元"><a href="#2-层（layer）就是一组神经元" class="headerlink" title="2. 层（layer）就是一组神经元"></a>2. 层（layer）就是一组神经元</h2><p>把多个 neuron 放在一起，就是一层。</p><p>线性层通常写成：</p><p>$$<br>z &#x3D; Wx + b<br>$$</p><p>$$<br>h &#x3D; \sigma(z)<br>$$</p><p>这里 $W$ 是矩阵，$b$ 是向量。每一行权重可以看成一个 neuron。</p><p>如果输入是 batch，写法会变成：</p><p>$$<br>H &#x3D; \sigma(XW^\top + b)<br>$$</p><p>实际框架里不会手动写每个 neuron，而是通过矩阵乘法一次算完整层。</p><h2 id="3-多层感知机（MLP）"><a href="#3-多层感知机（MLP）" class="headerlink" title="3. 多层感知机（MLP）"></a>3. 多层感知机（MLP）</h2><p>MLP 由多个 fully connected layer 组成。</p><p>一个两层 MLP 可以写成：</p><p>$$<br>h &#x3D; \sigma(W_1x + b_1)<br>$$</p><p>$$<br>\hat{y} &#x3D; W_2h + b_2<br>$$</p><p>其中，$h$ 是 hidden representation。</p><p>分类任务里，最后通常接 softmax：</p><p>$$<br>p(y&#x3D;k|x) &#x3D; \frac{e^{z_k}}{\sum_j e^{z_j}}<br>$$</p><p>回归任务里，最后可以直接输出连续值。</p><p>MLP 的缺点是没有针对图像、序列、文本结构做特殊假设。它很通用，但也很“笨”：如果输入是图片，MLP 不知道相邻像素之间有局部关系；如果输入是序列，MLP 不知道 token 顺序有意义。</p><p>所以后面才会出现 CNN、RNN、Transformer 这些结构。</p><h2 id="4-前向传播（forward-pass）"><a href="#4-前向传播（forward-pass）" class="headerlink" title="4. 前向传播（forward pass）"></a>4. 前向传播（forward pass）</h2><p>Forward pass 就是从输入一路算到输出和 loss。</p><p>以二分类 MLP 为例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">x<br>-&gt; linear layer<br>-&gt; ReLU<br>-&gt; linear layer<br>-&gt; sigmoid<br>-&gt; binary cross entropy loss<br></code></pre></td></tr></table></figure><p>写成公式：</p><p>$$<br>h &#x3D; \text{ReLU}(W_1x+b_1)<br>$$</p><p>$$<br>\hat{y} &#x3D; \sigma(W_2h+b_2)<br>$$</p><p>$$<br>L &#x3D; -[y\log(\hat{y}) + (1-y)\log(1-\hat{y})]<br>$$</p><p>Forward pass 只解决一个问题：当前参数下，模型预测得怎么样。</p><p>但训练还需要知道：如果 loss 大，参数应该怎么改。</p><h2 id="5-反向传播（backpropagation）"><a href="#5-反向传播（backpropagation）" class="headerlink" title="5. 反向传播（backpropagation）"></a>5. 反向传播（backpropagation）</h2><p>Backpropagation 的核心是 chain rule。</p><p>如果：</p><p>$$<br>L &#x3D; L(\hat{y}), \quad \hat{y}&#x3D;f(h), \quad h&#x3D;g(\theta)<br>$$</p><p>那么：</p><h1 id="frac-partial-L-partial-theta"><a href="#frac-partial-L-partial-theta" class="headerlink" title="$$\frac{\partial L}{\partial \theta}"></a>$$<br>\frac{\partial L}{\partial \theta}</h1><p>\frac{\partial L}{\partial \hat{y}}<br>\frac{\partial \hat{y}}{\partial h}<br>\frac{\partial h}{\partial \theta}<br>$$</p><p>这就是反向传播的直觉：loss 对前面参数的影响，要沿着计算路径一层层传回去。</p><p>它不是一种神秘算法，本质上就是高效地在计算图上应用链式法则。</p><h2 id="6-一个小计算图例子"><a href="#6-一个小计算图例子" class="headerlink" title="6. 一个小计算图例子"></a>6. 一个小计算图例子</h2><p>看一个最简单的例子：</p><p>$$<br>z &#x3D; wx + b<br>$$</p><p>$$<br>\hat{y} &#x3D; z<br>$$</p><p>$$<br>L &#x3D; (\hat{y} - y)^2<br>$$</p><p>现在要求 $L$ 对 $w$ 的梯度。</p><p>根据 chain rule：</p><h1 id="frac-partial-L-partial-w"><a href="#frac-partial-L-partial-w" class="headerlink" title="$$\frac{\partial L}{\partial w}"></a>$$<br>\frac{\partial L}{\partial w}</h1><p>\frac{\partial L}{\partial \hat{y}}<br>\frac{\partial \hat{y}}{\partial z}<br>\frac{\partial z}{\partial w}<br>$$</p><p>分别计算：</p><p>$$<br>\frac{\partial L}{\partial \hat{y}} &#x3D; 2(\hat{y}-y)<br>$$</p><p>$$<br>\frac{\partial \hat{y}}{\partial z} &#x3D; 1<br>$$</p><p>$$<br>\frac{\partial z}{\partial w} &#x3D; x<br>$$</p><p>所以：</p><p>$$<br>\frac{\partial L}{\partial w} &#x3D; 2(\hat{y}-y)x<br>$$</p><p>如果预测太大，$\hat{y}-y$ 为正，梯度方向会让 $w$ 下降；如果预测太小，梯度方向会让 $w$ 上升。这个例子很简单，但多层网络也是同一个逻辑，只是计算图更大。</p><h2 id="7-自动求导（automatic-differentiation）"><a href="#7-自动求导（automatic-differentiation）" class="headerlink" title="7. 自动求导（automatic differentiation）"></a>7. 自动求导（automatic differentiation）</h2><p>实际训练时通常不会手动推导每个参数的梯度。PyTorch、TensorFlow 这类框架会构建计算图，然后自动反向传播。</p><p>PyTorch 里的训练大概是：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python">optimizer.zero_grad()<br>pred = model(x)<br>loss = criterion(pred, y)<br>loss.backward()<br>optimizer.step()<br></code></pre></td></tr></table></figure><p>这里：</p><ul><li><code>pred = model(x)</code> 是 forward pass</li><li><code>loss.backward()</code> 是 backpropagation</li><li><code>optimizer.step()</code> 是参数更新</li></ul><p><code>zero_grad()</code> 也很重要，因为 PyTorch 默认会累积 gradient。如果不清零，下一轮的 gradient 会叠加到上一轮上。</p><h2 id="8-激活函数（activation-function）的作用"><a href="#8-激活函数（activation-function）的作用" class="headerlink" title="8. 激活函数（activation function）的作用"></a>8. 激活函数（activation function）的作用</h2><p>Activation function 提供非线性。</p><p>常见 activation：</p><table><thead><tr><th>函数</th><th>形式</th><th>特点</th></tr></thead><tbody><tr><td>Sigmoid</td><td>$\frac{1}{1+e^{-x}}$</td><td>输出在 0 到 1，容易梯度饱和</td></tr><tr><td>Tanh</td><td>$\tanh(x)$</td><td>输出在 -1 到 1，也可能饱和</td></tr><tr><td>ReLU</td><td>$\max(0,x)$</td><td>简单高效，深度网络常用</td></tr><tr><td>GELU</td><td>平滑版本</td><td>Transformer 里常见</td></tr></tbody></table><p>Sigmoid 和 tanh 在输入绝对值很大时，曲线会变平，梯度接近 0。深层网络里这会导致前面层学得很慢。</p><p>ReLU 的优点是正半轴梯度稳定，计算简单。但 ReLU 也可能出现 dead neuron：如果某个 neuron 长期输出 0，它对应参数可能很难更新。</p><h2 id="9-几个点"><a href="#9-几个点" class="headerlink" title="9. 几个点"></a>9. 几个点</h2><p>MLP 是理解深度网络的基础。CNN、RNN、Transformer 虽然结构不同，但仍然离不开 forward、loss、backprop 和 optimizer。</p><p>Forward pass 负责算当前预测，backpropagation 负责算参数该怎么改。</p><p>Backpropagation 本质是 chain rule 在计算图上的高效实现。</p><p>Activation function 不是装饰。没有非线性，多层网络会退化成线性模型。</p><p>自动求导让训练变得方便，但理解计算图和梯度传播仍然很重要，否则调试训练问题时会很被动。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.deeplearningbook.org/">Deep Learning Book: Chapter 6</a></li><li><a href="https://d2l.ai/chapter_multilayer-perceptrons/index.html">Dive into Deep Learning: Multilayer Perceptrons</a></li><li><a href="https://cs231n.github.io/optimization-2/">CS231n: Backpropagation</a></li><li><a href="https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html">PyTorch Autograd</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/08/10/deep-learning-notes-02-mlp-forward-backprop/</id>
    <link href="https://sssssimonk.github.io/2024/08/10/deep-learning-notes-02-mlp-forward-backprop/"/>
    <published>2024-08-10T12:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理最基础的神经网络：多层感知机（MLP），以及神经网络到底如何通过前向传播和反向传播完成训练。</p>
<p>可以先抓住一句话：</p>
<blockquote>
<p>Forward pass 负责算预测和 loss，backpropagation 负责把]]>
    </summary>
    <title>深度学习笔记 02：MLP、前向传播与反向传播</title>
    <updated>2024-08-10T12:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="深度学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="深度学习" scheme="https://sssssimonk.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="表示学习" scheme="https://sssssimonk.github.io/tags/%E8%A1%A8%E7%A4%BA%E5%AD%A6%E4%B9%A0/"/>
    <category term="神经网络" scheme="https://sssssimonk.github.io/tags/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <content>
      <![CDATA[<p>机器学习章节里，大部分模型都可以理解成：先把数据整理成特征，再用一个模型把特征映射到预测结果。</p><p>深度学习和传统机器学习最大的区别，不是“模型更复杂”这么简单，而是它把 <strong>feature learning</strong> 也放进了模型里。以前很多时候要人工设计特征，深度学习希望模型自己从原始数据中学出有用表示。</p><p>比如图像分类任务：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">traditional ML:<br>image -&gt; hand-crafted features -&gt; classifier -&gt; label<br><br>deep learning:<br>image -&gt; neural network -&gt; label<br></code></pre></td></tr></table></figure><p>这里不是说传统 ML 不重要，而是 deep learning 把“特征怎么来”这个问题也变成了可训练的部分。</p><h2 id="1-神经网络仍然是函数"><a href="#1-神经网络仍然是函数" class="headerlink" title="1. 神经网络仍然是函数"></a>1. 神经网络仍然是函数</h2><p>神经网络本质上还是一个函数：</p><p>$$<br>\hat{y} &#x3D; f_\theta(x)<br>$$</p><p>其中，$x$ 是输入，$\hat{y}$ 是预测，$\theta$ 是网络参数。</p><p>训练的目标仍然是找一组参数，让模型预测更接近真实标签：</p><p>$$<br>\theta^* &#x3D; \arg\min_\theta L(f_\theta(x), y)<br>$$</p><p>所以 deep learning 没有跳出机器学习的基本框架。它仍然是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">input<br>-&gt; model prediction<br>-&gt; loss<br>-&gt; gradient<br>-&gt; parameter update<br></code></pre></td></tr></table></figure><p>区别在于 $f_\theta$ 变得很灵活，可以由很多层可学习变换组成。</p><h2 id="2-从人工特征到表示学习（representation-learning）"><a href="#2-从人工特征到表示学习（representation-learning）" class="headerlink" title="2. 从人工特征到表示学习（representation learning）"></a>2. 从人工特征到表示学习（representation learning）</h2><p>传统机器学习里，特征工程经常决定上限。</p><p>比如判断一封邮件是不是垃圾邮件，可能会手动构造这些特征：</p><table><thead><tr><th>特征</th><th>含义</th></tr></thead><tbody><tr><td><code>contains_free</code></td><td>是否出现 free</td></tr><tr><td><code>num_links</code></td><td>邮件里的链接数量</td></tr><tr><td><code>sender_reputation</code></td><td>发件人信誉</td></tr><tr><td><code>has_attachment</code></td><td>是否有附件</td></tr></tbody></table><p>模型看到的不是原始邮件，而是这些被设计好的 feature。</p><p>深度学习的思路是：把原始数据输入网络，让网络逐层学出中间表示。</p><p>以图像为例，早期层可能学边缘和纹理，中间层学局部形状，后面层学更接近语义的结构。这个过程不是人为写规则，而是通过 loss 和 gradient 学出来的。</p><p>这就是 representation learning。</p><h2 id="3-什么叫表示（representation）"><a href="#3-什么叫表示（representation）" class="headerlink" title="3. 什么叫表示（representation）"></a>3. 什么叫表示（representation）</h2><p>Representation 可以理解成模型内部对输入的重新编码。</p><p>假设输入是一张图片，原始像素只是一个大矩阵。对模型来说，像素本身不直接等于“猫”“狗”“车”。网络需要把像素变成更有用的中间特征：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">pixels<br>-&gt; edges / textures<br>-&gt; parts<br>-&gt; object-level features<br>-&gt; prediction<br></code></pre></td></tr></table></figure><p>在 NLP 里也类似。一个词或者一句话会被表示成 embedding。Embedding 不是原始文本，而是模型学出来的一组向量，用来承载语义和上下文信息。</p><p>比如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">&quot;king&quot; -&gt; [0.21, -0.18, 0.73, ...]<br>&quot;queen&quot; -&gt; [0.19, -0.11, 0.69, ...]<br></code></pre></td></tr></table></figure><p>这些数字本身没有人类可读含义，但它们让模型可以计算相似度、组合上下文、做预测。</p><h2 id="4-为什么要多层"><a href="#4-为什么要多层" class="headerlink" title="4. 为什么要多层"></a>4. 为什么要多层</h2><p>单层线性模型表达能力有限：</p><p>$$<br>\hat{y} &#x3D; Wx + b<br>$$</p><p>如果数据关系本身很复杂，只靠一个线性变换很难表达。</p><p>多层网络的想法是把简单变换组合起来：</p><p>$$<br>h_1 &#x3D; \sigma(W_1x + b_1)<br>$$</p><p>$$<br>h_2 &#x3D; \sigma(W_2h_1 + b_2)<br>$$</p><p>$$<br>\hat{y} &#x3D; W_3h_2 + b_3<br>$$</p><p>每一层都在前一层表示的基础上再做一次变换。这样模型可以逐步把原始输入变成更适合任务的表示。</p><p>一个简单例子：判断图片里有没有车。第一层不太可能直接学“车”，它更可能学边缘；中间层组合边缘形成轮子、车窗；后面层再组合成车的整体概念。</p><h2 id="5-非线性为什么重要"><a href="#5-非线性为什么重要" class="headerlink" title="5. 非线性为什么重要"></a>5. 非线性为什么重要</h2><p>如果没有 activation function，多层线性网络仍然等价于一个线性模型。</p><p>比如：</p><p>$$<br>h &#x3D; W_1x<br>$$</p><p>$$<br>\hat{y} &#x3D; W_2h &#x3D; W_2W_1x<br>$$</p><p>这里 $W_2W_1$ 仍然只是一个新的矩阵。堆很多层也没有本质变化。</p><p>所以神经网络需要在层与层之间加入非线性函数：</p><p>$$<br>h &#x3D; \sigma(Wx+b)<br>$$</p><p>常见 activation 有 ReLU、sigmoid、tanh。现代深度网络里 ReLU 及其变体很常见，因为它简单、梯度更稳定、训练效率高。</p><h2 id="6-端到端学习（end-to-end-learning）"><a href="#6-端到端学习（end-to-end-learning）" class="headerlink" title="6. 端到端学习（end-to-end learning）"></a>6. 端到端学习（end-to-end learning）</h2><p>End-to-end learning 指模型从输入到输出整体一起训练。</p><p>比如语音识别：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">audio waveform -&gt; neural network -&gt; text<br></code></pre></td></tr></table></figure><p>相比传统 pipeline：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">audio<br>-&gt; acoustic features<br>-&gt; phoneme model<br>-&gt; language model<br>-&gt; text<br></code></pre></td></tr></table></figure><p>端到端模型的优点是中间步骤不需要完全人工设计，模型可以为了最终目标自动调整内部表示。</p><p>但它也有代价：</p><ul><li>需要更多数据</li><li>需要更大计算资源</li><li>可解释性通常更弱</li><li>出问题时不一定容易定位是哪一层表示出了问题</li></ul><p>所以 end-to-end 不是永远更好，只是当数据和算力足够时，它可以减少人工特征设计的限制。</p><h2 id="7-深度学习的成功条件"><a href="#7-深度学习的成功条件" class="headerlink" title="7. 深度学习的成功条件"></a>7. 深度学习的成功条件</h2><p>Deep learning 不是单靠一个想法突然成功。它通常需要几个条件同时出现：</p><table><thead><tr><th>条件</th><th>作用</th></tr></thead><tbody><tr><td>大数据</td><td>让大模型有足够样本学习表示</td></tr><tr><td>GPU &#x2F; TPU</td><td>让矩阵计算足够快</td></tr><tr><td>更好的优化方法</td><td>让深层网络能稳定训练</td></tr><tr><td>更好的结构设计</td><td>CNN、ResNet、Transformer 等</td></tr><tr><td>软件生态</td><td>PyTorch、TensorFlow、CUDA 等</td></tr></tbody></table><p>如果数据很少、任务很简单，传统机器学习模型可能仍然更合适。Deep learning 的优势通常在原始输入复杂、人工特征难设计、数据规模足够大的场景里更明显。</p><h2 id="8-几个点"><a href="#8-几个点" class="headerlink" title="8. 几个点"></a>8. 几个点</h2><p>Deep learning 不是另一个完全独立的范式，它还是机器学习，只是模型函数更复杂，并且把表示也纳入学习过程。</p><p>Representation learning 是理解 deep learning 的核心。模型不是直接“理解”数据，而是在训练目标的驱动下学出对任务有用的内部表示。</p><p>多层结构的意义在于逐步组合特征。低层更接近局部和简单模式，高层更接近抽象和任务相关模式。</p><p>非线性 activation 很关键。没有非线性，多层线性网络仍然只是线性模型。</p><p>End-to-end learning 减少了手工设计特征的负担，但也带来数据、算力和可解释性问题。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.deeplearningbook.org/">Deep Learning Book</a></li><li><a href="https://d2l.ai/">Dive into Deep Learning</a></li><li><a href="https://cs231n.github.io/">Stanford CS231n: Convolutional Neural Networks for Visual Recognition</a></li><li><a href="https://introtodeeplearning.com/">MIT 6.S191: Introduction to Deep Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/08/03/deep-learning-notes-01-framework/</id>
    <link href="https://sssssimonk.github.io/2024/08/03/deep-learning-notes-01-framework/"/>
    <published>2024-08-03T12:00:00.000Z</published>
    <summary>
      <![CDATA[<p>机器学习章节里，大部分模型都可以理解成：先把数据整理成特征，再用一个模型把特征映射到预测结果。</p>
<p>深度学习和传统机器学习最大的区别，不是“模型更复杂”这么简单，而是它把 <strong>feature learning</strong>]]>
    </summary>
    <title>深度学习笔记 01：从特征工程到表示学习</title>
    <updated>2024-08-03T12:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="机器学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="机器学习" scheme="https://sssssimonk.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="评估" scheme="https://sssssimonk.github.io/tags/%E8%AF%84%E4%BC%B0/"/>
    <category term="交叉验证" scheme="https://sssssimonk.github.io/tags/%E4%BA%A4%E5%8F%89%E9%AA%8C%E8%AF%81/"/>
    <category term="模型选择" scheme="https://sssssimonk.github.io/tags/%E6%A8%A1%E5%9E%8B%E9%80%89%E6%8B%A9/"/>
    <content>
      <![CDATA[<p>这一篇整理模型评估、验证和模型选择。训练 loss 下降只能说明模型更适合训练数据，不等于模型真的有用。评估指标和验证方式决定了最后会选择哪个模型，也决定了这个选择是否可靠。</p><h2 id="1-评估指标不是装饰"><a href="#1-评估指标不是装饰" class="headerlink" title="1. 评估指标不是装饰"></a>1. 评估指标不是装饰</h2><p>模型评估的核心问题是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">这个模型在真正关心的场景里，到底有没有变好？<br></code></pre></td></tr></table></figure><p>不同任务里，“好”的定义不一样。房价预测关心预测误差，垃圾邮件识别关心别把正常邮件误删，疾病筛查关心别漏掉阳性病例。</p><p>所以指标不是越多越好，而是要对应任务代价。</p><h2 id="2-回归指标（regression-metrics）"><a href="#2-回归指标（regression-metrics）" class="headerlink" title="2. 回归指标（regression metrics）"></a>2. 回归指标（regression metrics）</h2><p>回归任务预测连续值，常见指标有 MSE、RMSE、MAE、$R^2$。</p><h3 id="MSE-和-RMSE"><a href="#MSE-和-RMSE" class="headerlink" title="MSE 和 RMSE"></a>MSE 和 RMSE</h3><p>MSE：</p><p>$$<br>MSE&#x3D;\frac{1}{n}\sum_{i&#x3D;1}^{n}(y_i-\hat{y}_i)^2<br>$$</p><p>RMSE：</p><p>$$<br>RMSE&#x3D;\sqrt{MSE}<br>$$</p><p>MSE 对大误差更敏感，因为误差被平方。RMSE 和原始目标变量单位一致，更容易解释。</p><p>比如预测房价时，RMSE &#x3D; 20 万，比 MSE &#x3D; 400 亿更直观。</p><h3 id="MAE"><a href="#MAE" class="headerlink" title="MAE"></a>MAE</h3><p>MAE：</p><p>$$<br>MAE&#x3D;\frac{1}{n}\sum_{i&#x3D;1}^{n}|y_i-\hat{y}_i|<br>$$</p><p>MAE 对 outlier 没有 MSE 那么敏感。</p><p>如果数据里有少数极端样本，比如一两套超级豪宅，MSE 会被这些样本强烈影响，MAE 会更稳一些。</p><h3 id="R-2"><a href="#R-2" class="headerlink" title="$R^2$"></a>$R^2$</h3><p>$R^2$ 衡量模型解释了多少目标变量的 variance：</p><p>$$<br>R^2&#x3D;1-\frac{\sum_i(y_i-\hat{y}_i)^2}{\sum_i(y_i-\bar{y})^2}<br>$$</p><p>如果 $R^2&#x3D;0.8$，可以粗略理解为模型解释了 80% 的变化。它适合看整体拟合程度，但不一定能说明具体业务误差是否可接受。</p><h2 id="3-分类指标（classification-metrics）"><a href="#3-分类指标（classification-metrics）" class="headerlink" title="3. 分类指标（classification metrics）"></a>3. 分类指标（classification metrics）</h2><p>分类任务常见指标都可以从 confusion matrix 出发。</p><table><thead><tr><th></th><th align="right">Predicted Positive</th><th align="right">Predicted Negative</th></tr></thead><tbody><tr><td>Actual Positive</td><td align="right">TP</td><td align="right">FN</td></tr><tr><td>Actual Negative</td><td align="right">FP</td><td align="right">TN</td></tr></tbody></table><p>TP 是真正例，FP 是假正例，FN 是假负例，TN 是真负例。</p><h3 id="准确率（accuracy）"><a href="#准确率（accuracy）" class="headerlink" title="准确率（accuracy）"></a>准确率（accuracy）</h3><p>$$<br>Accuracy&#x3D;\frac{TP+TN}{TP+TN+FP+FN}<br>$$</p><p>Accuracy 直观，但在 imbalanced dataset 上容易误导。</p><p>比如一个疾病数据集中，99% 都是阴性。模型全部预测阴性，也有 99% accuracy，但它完全找不到阳性病人。</p><h3 id="精确率（precision）"><a href="#精确率（precision）" class="headerlink" title="精确率（precision）"></a>精确率（precision）</h3><p>$$<br>Precision&#x3D;\frac{TP}{TP+FP}<br>$$</p><p>Precision 关心预测为正的样本里，有多少是真的正。</p><p>垃圾邮件识别里 precision 很重要，因为把正常邮件误判成垃圾邮件会影响用户。</p><h3 id="召回率（recall）"><a href="#召回率（recall）" class="headerlink" title="召回率（recall）"></a>召回率（recall）</h3><p>$$<br>Recall&#x3D;\frac{TP}{TP+FN}<br>$$</p><p>Recall 关心真实正样本里，有多少被找出来。</p><p>疾病筛查里 recall 很重要，因为漏诊的代价可能很高。</p><h3 id="F1-分数（F1-score）"><a href="#F1-分数（F1-score）" class="headerlink" title="F1 分数（F1 score）"></a>F1 分数（F1 score）</h3><p>$$<br>F1&#x3D;2\cdot\frac{Precision\cdot Recall}{Precision+Recall}<br>$$</p><p>F1 是 precision 和 recall 的调和平均。它适合在两者都重要时作为综合指标。</p><p>但 F1 也不是万能的。如果业务上 FP 和 FN 代价明显不同，还是要单独看 precision 和 recall。</p><h2 id="4-ROC-和-AUC：阈值变化下的分类表现"><a href="#4-ROC-和-AUC：阈值变化下的分类表现" class="headerlink" title="4. ROC 和 AUC：阈值变化下的分类表现"></a>4. ROC 和 AUC：阈值变化下的分类表现</h2><p>ROC 曲线关注不同 threshold 下 TPR 和 FPR 的变化。</p><p>$$<br>TPR&#x3D;\frac{TP}{TP+FN}<br>$$</p><p>$$<br>FPR&#x3D;\frac{FP}{FP+TN}<br>$$</p><p>AUC 是 ROC 曲线下面积。它也可以理解为：随机抽一个正样本和一个负样本，模型把正样本排在负样本前面的概率。</p><p>这就是为什么 AUC 常用于排序、推荐、广告等场景。很多时候模型输出的不是最终类别，而是一个 score，业务再根据 threshold 或排序位置使用这个 score。</p><p>一个小例子：</p><table><thead><tr><th>用户</th><th align="right">是否点击</th><th align="right">模型 score</th></tr></thead><tbody><tr><td>A</td><td align="right">1</td><td align="right">0.90</td></tr><tr><td>B</td><td align="right">0</td><td align="right">0.80</td></tr><tr><td>C</td><td align="right">1</td><td align="right">0.70</td></tr><tr><td>D</td><td align="right">0</td><td align="right">0.20</td></tr></tbody></table><p>AUC 关心的是正样本是否整体排在负样本前面，而不是某个固定 threshold 下分对了多少。</p><h2 id="5-PR-曲线（PR-curve）"><a href="#5-PR-曲线（PR-curve）" class="headerlink" title="5. PR 曲线（PR curve）"></a>5. PR 曲线（PR curve）</h2><p>PR Curve 的横轴是 recall，纵轴是 precision。</p><p>在正负样本极度不平衡时，PR curve 往往比 ROC curve 更敏感。</p><p>比如欺诈检测里，正样本非常少。ROC-AUC 可能看起来还不错，但 precision 可能很低：模型找出一堆“疑似欺诈”，里面真正欺诈的比例很小。这个时候 PR curve 更能反映实际使用体验。</p><h2 id="6-类别不平衡数据集（imbalanced-dataset）"><a href="#6-类别不平衡数据集（imbalanced-dataset）" class="headerlink" title="6. 类别不平衡数据集（imbalanced dataset）"></a>6. 类别不平衡数据集（imbalanced dataset）</h2><p>类别不平衡时，accuracy 通常不够。</p><p>常见处理方式：</p><ul><li>换指标：precision、recall、F1、AUC、PR-AUC</li><li>重采样：oversampling &#x2F; undersampling</li><li>调整 loss：给少数类更高权重</li><li>调 threshold：根据业务代价调整预测阈值</li><li>anomaly detection：把少数类当异常点处理</li></ul><p>一个例子：阳性率只有 1% 的疾病筛查。模型如果全部预测阴性，accuracy 是 99%，但 recall 是 0。这种模型没有实际价值。</p><h2 id="7-验证（validation）：怎么可靠地估计泛化能力"><a href="#7-验证（validation）：怎么可靠地估计泛化能力" class="headerlink" title="7. 验证（validation）：怎么可靠地估计泛化能力"></a>7. 验证（validation）：怎么可靠地估计泛化能力</h2><p>训练集表现不能代表泛化能力，所以需要 validation。</p><h3 id="留出法验证（holdout-validation）"><a href="#留出法验证（holdout-validation）" class="headerlink" title="留出法验证（holdout validation）"></a>留出法验证（holdout validation）</h3><p>最常见方式是 train &#x2F; validation &#x2F; test split。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">training set: train model parameters<br>validation set: tune hyperparameters and choose models<br>test set: final evaluation<br></code></pre></td></tr></table></figure><p>test set 不应该频繁用来调模型。如果每次调参都看 test score，test set 也会被间接过拟合。</p><h3 id="K-折交叉验证（K-fold-cross-validation）"><a href="#K-折交叉验证（K-fold-cross-validation）" class="headerlink" title="K 折交叉验证（K-fold cross validation）"></a>K 折交叉验证（K-fold cross validation）</h3><p>K-fold cross validation 把数据分成 $K$ 份，每次用其中一份做 validation，其余做 training，最后平均结果。</p><p>它比单次 holdout 更稳定，尤其适合数据量不大的情况。</p><p>缺点是训练成本更高。比如 5-fold 就要训练 5 次。</p><h3 id="Bootstrap"><a href="#Bootstrap" class="headerlink" title="Bootstrap"></a>Bootstrap</h3><p>Bootstrap 是有放回采样。每次从原始数据中采出一个同样大小的数据集，有些样本会重复，有些样本不会被采到。没被采到的样本可以用于评估。</p><p>它在统计估计里很常见，但日常模型训练中更常用 holdout 和 K-fold。</p><h2 id="8-模型选择（model-selection）"><a href="#8-模型选择（model-selection）" class="headerlink" title="8. 模型选择（model selection）"></a>8. 模型选择（model selection）</h2><p>Model selection 不只是选模型类型，也包括选超参数。</p><p>比如：</p><ul><li>Linear Regression vs Random Forest vs XGBoost</li><li>Decision tree 的 <code>max_depth</code></li><li>KNN 的 <code>K</code></li><li>Logistic regression 的 regularization strength</li><li>XGBoost 的 learning rate、tree depth、number of estimators</li></ul><h3 id="网格搜索（grid-search）"><a href="#网格搜索（grid-search）" class="headerlink" title="网格搜索（grid search）"></a>网格搜索（grid search）</h3><p>Grid search 枚举所有组合。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs text">max_depth = [3, 5, 7]<br>learning_rate = [0.01, 0.1]<br></code></pre></td></tr></table></figure><p>总共会试 $3 \times 2 &#x3D; 6$ 种组合。</p><p>优点是简单直接，缺点是组合多时成本很高。</p><h3 id="随机搜索（random-search）"><a href="#随机搜索（random-search）" class="headerlink" title="随机搜索（random search）"></a>随机搜索（random search）</h3><p>Random search 从参数空间中随机采样。它经常比 grid search 更高效，因为并不是所有超参数都同等重要。</p><p>比如 10 个超参数里真正敏感的可能只有 2 个，grid search 会浪费很多组合，random search 反而可能更快碰到好区域。</p><h3 id="贝叶斯优化（Bayesian-optimization）"><a href="#贝叶斯优化（Bayesian-optimization）" class="headerlink" title="贝叶斯优化（Bayesian optimization）"></a>贝叶斯优化（Bayesian optimization）</h3><p>Bayesian optimization 会根据已有实验结果，估计哪些参数区域更可能好，然后优先尝试这些区域。</p><p>它比 random search 更聪明，但实现和调试更复杂。小任务里不一定需要，上规模后才更有价值。</p><h2 id="9-数据泄漏（data-leakage）"><a href="#9-数据泄漏（data-leakage）" class="headerlink" title="9. 数据泄漏（data leakage）"></a>9. 数据泄漏（data leakage）</h2><p>Data leakage 是评估里最危险的问题之一。它指训练过程中用了不该提前知道的信息。</p><p>例子：预测用户未来是否流失，但 feature 里包含“用户是否已经取消订阅”。这个特征在预测时根本不可用，但训练时会让模型表现非常好。</p><p>另一个例子：先对全量数据做 normalization，再划分 train&#x2F;test。这样 test set 的统计信息已经泄漏到了 training process。正确做法是只在 training set 上 fit scaler，再 apply 到 validation&#x2F;test set。</p><p>Data leakage 会让离线评估虚高，线上效果崩掉。</p><h2 id="10-几个点"><a href="#10-几个点" class="headerlink" title="10. 几个点"></a>10. 几个点</h2><p>Accuracy 不适合严重类别不平衡的问题。</p><p>Precision 和 recall 要结合 FP&#x2F;FN 的业务代价看，不要机械比较谁更大。</p><p>AUC 更像排序指标，适合看 score 的整体区分能力。</p><p>PR curve 在正样本稀少时更敏感。</p><p>Validation set 用来选模型，test set 留到最后做最终评估。</p><p>数据泄漏比模型小 bug 更可怕，因为它会让结果看起来特别好。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://scikit-learn.org/stable/model_selection.html">scikit-learn User Guide: Model Selection and Evaluation</a></li><li><a href="https://scikit-learn.org/stable/modules/model_evaluation.html">scikit-learn User Guide: Metrics and Scoring</a></li><li><a href="https://www.statlearning.com/">An Introduction to Statistical Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/07/14/ml-notes-06-evaluation-validation-model-selection/</id>
    <link href="https://sssssimonk.github.io/2024/07/14/ml-notes-06-evaluation-validation-model-selection/"/>
    <published>2024-07-14T14:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理模型评估、验证和模型选择。训练 loss 下降只能说明模型更适合训练数据，不等于模型真的有用。评估指标和验证方式决定了最后会选择哪个模型，也决定了这个选择是否可靠。</p>
<h2 id="1-评估指标不是装饰"><a href="#1-评估指标不是装饰"]]>
    </summary>
    <title>机器学习笔记 06：评估、验证与模型选择</title>
    <updated>2024-07-14T14:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="机器学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="机器学习" scheme="https://sssssimonk.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="无监督学习" scheme="https://sssssimonk.github.io/tags/%E6%97%A0%E7%9B%91%E7%9D%A3%E5%AD%A6%E4%B9%A0/"/>
    <category term="聚类" scheme="https://sssssimonk.github.io/tags/%E8%81%9A%E7%B1%BB/"/>
    <category term="PCA" scheme="https://sssssimonk.github.io/tags/PCA/"/>
    <content>
      <![CDATA[<p>这一篇整理无监督学习和降维。监督学习有 label，模型知道自己要预测什么；无监督学习没有明确答案，目标更多是发现数据结构。最常见的两类任务是 clustering 和 dimensionality reduction。</p><h2 id="1-什么是无监督学习（unsupervised-learning）"><a href="#1-什么是无监督学习（unsupervised-learning）" class="headerlink" title="1. 什么是无监督学习（unsupervised learning）"></a>1. 什么是无监督学习（unsupervised learning）</h2><p>无监督学习只有输入 $x$，没有标签 $y$。</p><p>比如有一批用户行为数据：</p><table><thead><tr><th>用户</th><th align="right">访问次数</th><th align="right">平均停留时间</th><th align="right">购买次数</th></tr></thead><tbody><tr><td>A</td><td align="right">50</td><td align="right">8 分钟</td><td align="right">5</td></tr><tr><td>B</td><td align="right">3</td><td align="right">1 分钟</td><td align="right">0</td></tr><tr><td>C</td><td align="right">45</td><td align="right">7 分钟</td><td align="right">4</td></tr><tr><td>D</td><td align="right">5</td><td align="right">2 分钟</td><td align="right">0</td></tr></tbody></table><p>没有人告诉模型谁是“高价值用户”，但模型可以发现 A 和 C 比较像，B 和 D 比较像。这就是 clustering 的典型使用场景。</p><p>无监督学习不是为了直接预测 label，而是为了整理数据：哪些样本相似，数据有没有低维结构，有没有异常点。</p><h2 id="2-聚类（clustering）：把相似样本放在一起"><a href="#2-聚类（clustering）：把相似样本放在一起" class="headerlink" title="2. 聚类（clustering）：把相似样本放在一起"></a>2. 聚类（clustering）：把相似样本放在一起</h2><p>Clustering 的目标是把样本分成若干组，让同一组内部尽量相似，不同组之间尽量不同。</p><p>但这里有一个麻烦：没有 label，所以“分得好不好”不像分类任务那样容易判断。</p><p>不同 clustering 方法对 cluster 形状有不同假设。KMeans 偏向球形 cluster，DBSCAN 偏向密度连通区域，hierarchical clustering 更强调层级关系。</p><h2 id="3-KMeans：最经典的聚类方法"><a href="#3-KMeans：最经典的聚类方法" class="headerlink" title="3. KMeans：最经典的聚类方法"></a>3. KMeans：最经典的聚类方法</h2><p>KMeans 是最经典的 clustering 方法之一。它需要先指定 cluster 数量 $K$。</p><p>基本流程：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs text">choose K initial centers<br>assign each point to the nearest center<br>update each center by averaging assigned points<br>repeat until centers stop changing<br></code></pre></td></tr></table></figure><p>目标可以理解为最小化簇内平方距离：</p><p>$$<br>\sum_{k&#x3D;1}^{K}\sum_{x_i \in C_k}|x_i-\mu_k|^2<br>$$</p><p>其中，$C_k$ 是第 $k$ 个 cluster，$\mu_k$ 是这个 cluster 的中心。</p><p>一个简单例子：如果用户按“访问次数”和“购买次数”分布明显，一群是高访问高购买，另一群是低访问低购买，KMeans 很容易把它们分开。</p><p>KMeans 的问题也很明显：</p><ul><li>需要提前指定 $K$</li><li>对初始中心敏感</li><li>对 outlier 敏感</li><li>更适合接近球形的 cluster</li><li>特征 scale 会影响距离，通常要先 normalization</li></ul><h2 id="4-怎么选-K"><a href="#4-怎么选-K" class="headerlink" title="4. 怎么选 K"></a>4. 怎么选 K</h2><p>常见方法是 elbow method 和 silhouette score。</p><p><strong>Elbow method</strong> 看不同 $K$ 下的簇内误差。随着 $K$ 增大，误差一定会下降，因为 cluster 越多，样本离中心越近。但下降幅度会逐渐变小。拐点附近的 $K$ 通常是一个候选。</p><p><strong>Silhouette score</strong> 同时考虑样本离自己 cluster 的距离，以及离其他 cluster 的距离。分数越高，说明样本更像自己 cluster，而不像其他 cluster。</p><p>实际使用时，不要只机械看指标。比如用户分群最后要服务业务策略，那 cluster 数量还要考虑是否可解释、是否方便运营。</p><h2 id="5-DBSCAN：基于密度的聚类（clustering）"><a href="#5-DBSCAN：基于密度的聚类（clustering）" class="headerlink" title="5. DBSCAN：基于密度的聚类（clustering）"></a>5. DBSCAN：基于密度的聚类（clustering）</h2><p>DBSCAN 不需要提前指定 cluster 数量。它通过密度来定义 cluster。</p><p>两个重要参数：</p><ul><li><code>eps</code>：邻域半径</li><li><code>min_samples</code>：成为核心点需要的邻居数量</li></ul><p>DBSCAN 会把点分成三类：</p><table><thead><tr><th>类型</th><th>含义</th></tr></thead><tbody><tr><td>Core point</td><td>eps 范围内有足够多邻居</td></tr><tr><td>Border point</td><td>不满足 core 条件，但在某个 core point 邻域内</td></tr><tr><td>Noise point</td><td>不属于任何密度区域</td></tr></tbody></table><p>DBSCAN 的优点是能发现非球形 cluster，也能识别 outlier。</p><p>比如二维空间里有两个弯月形 cluster，KMeans 可能分不好，因为它偏向球形；DBSCAN 更可能按密度连通性分出来。</p><p>缺点是参数敏感。如果 <code>eps</code> 太小，很多点会被当成 noise；如果太大，不同 cluster 可能被连在一起。</p><h2 id="6-层次聚类（hierarchical-clustering）"><a href="#6-层次聚类（hierarchical-clustering）" class="headerlink" title="6. 层次聚类（hierarchical clustering）"></a>6. 层次聚类（hierarchical clustering）</h2><p>Hierarchical clustering 关注层级结构。最常见的是 agglomerative clustering，也就是自底向上合并。</p><p>流程：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">start with each point as its own cluster<br>merge the two closest clusters<br>repeat until only one cluster remains or target number reached<br></code></pre></td></tr></table></figure><p>它的结果可以用 dendrogram 表示。虽然这里不画图，但可以把它想成一棵树：底部是单个样本，越往上 cluster 越大。</p><p>关键在于怎么定义 cluster 之间的距离：</p><ul><li>single linkage：两个 cluster 中最近点的距离</li><li>complete linkage：两个 cluster 中最远点的距离</li><li>average linkage：所有点对距离的平均</li><li>Ward linkage：合并后簇内方差增加最小</li></ul><p>Hierarchical clustering 的优点是能看到层级关系，不一定要一开始就指定 cluster 数。缺点是计算成本高，大数据上不一定适合。</p><h2 id="7-聚类（clustering）方法对比"><a href="#7-聚类（clustering）方法对比" class="headerlink" title="7. 聚类（clustering）方法对比"></a>7. 聚类（clustering）方法对比</h2><table><thead><tr><th>方法</th><th>核心假设</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td>KMeans</td><td>cluster 接近球形</td><td>简单、快</td><td>要指定 K，对 outlier 敏感</td></tr><tr><td>DBSCAN</td><td>cluster 是密度区域</td><td>能识别噪声，支持非球形</td><td>参数敏感，高维效果可能差</td></tr><tr><td>Hierarchical</td><td>数据有层级结构</td><td>结果可解释</td><td>大数据成本高</td></tr></tbody></table><p>KMeans 更像是在找中心点，DBSCAN 更像是在找密度连通区域，hierarchical clustering 更像是在整理样本之间的层级关系。</p><h2 id="8-降维（dimensionality-reduction）：为什么要降维"><a href="#8-降维（dimensionality-reduction）：为什么要降维" class="headerlink" title="8. 降维（dimensionality reduction）：为什么要降维"></a>8. 降维（dimensionality reduction）：为什么要降维</h2><p>降维的目标是把高维数据变成低维表示，同时尽量保留重要信息。</p><p>常见动机：</p><ul><li>可视化：把高维 embedding 投到 2D&#x2F;3D</li><li>去噪：去掉不重要的方向</li><li>压缩：减少特征数量</li><li>缓解维度灾难：高维空间中距离可能变得不稳定</li></ul><p>比如一个用户有 1000 个行为特征，很多特征之间高度相关。降维可以把它们压成几十个综合特征，保留主要变化方向。</p><h2 id="9-PCA：用主成分保留主要变化"><a href="#9-PCA：用主成分保留主要变化" class="headerlink" title="9. PCA：用主成分保留主要变化"></a>9. PCA：用主成分保留主要变化</h2><p>PCA 的核心思想是：找到数据方差最大的方向，然后把数据投影到这些方向上。</p><p>基本流程：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">standardize features<br>compute covariance matrix<br>compute eigenvectors and eigenvalues<br>choose top principal components<br>project data to lower-dimensional space<br></code></pre></td></tr></table></figure><p>如果一个二维数据集大致沿着一条斜线分布，PCA 的第一主成分就是这条斜线方向。把数据投影到这条线，就能用一维保留大部分变化。</p><p>数学上，PCA 会找一组正交方向，第一主成分解释最多 variance，第二主成分在和第一主成分正交的条件下解释剩余最多 variance。</p><h2 id="10-PCA-的几个注意点"><a href="#10-PCA-的几个注意点" class="headerlink" title="10. PCA 的几个注意点"></a>10. PCA 的几个注意点</h2><p>PCA 对 scale 敏感。一个特征范围是 $[0,100000]$，另一个是 $[0,1]$，如果不 standardize，大范围特征会主导 principal components。</p><p>PCA 是线性降维。如果数据本身是复杂非线性结构，PCA 可能表达不好。</p><p>PCA 的主成分不一定好解释。原始特征可能有明确含义，但主成分是多个特征的线性组合，解释起来会更抽象。</p><h2 id="11-t-SNE-和-UMAP"><a href="#11-t-SNE-和-UMAP" class="headerlink" title="11. t-SNE 和 UMAP"></a>11. t-SNE 和 UMAP</h2><p>t-SNE 和 UMAP 常用于可视化高维 embedding，比如把文本 embedding 或图像 embedding 投到二维。</p><p>它们更关注局部邻域结构，适合看样本是否聚在一起。但这类方法主要用于 visualization，不太适合直接当成严肃的全局距离解释。</p><p>一个常见误区是看见二维图上两个 cluster 离得远，就断言原始高维空间里它们也有同样的全局距离关系。这个不一定成立。</p><h2 id="12-几个点"><a href="#12-几个点" class="headerlink" title="12. 几个点"></a>12. 几个点</h2><p>Clustering 没有 label，所以评估本来就更难。指标只能辅助，最后还要看任务目的。</p><p>KMeans 前基本要做 scaling，否则距离会被大尺度特征主导。</p><p>DBSCAN 适合找异常点和非球形 cluster，但参数 <code>eps</code> 很关键。</p><p>PCA 保留的是最大 variance 方向，不等于保留对下游任务最有用的方向。</p><p>降维后的图很好看，不代表结论一定可靠。可视化是探索工具，不是最终证明。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://scikit-learn.org/stable/modules/clustering.html">scikit-learn User Guide: Clustering</a></li><li><a href="https://scikit-learn.org/stable/modules/decomposition.html">scikit-learn User Guide: Decomposition</a></li><li><a href="https://scikit-learn.org/stable/modules/manifold.html">scikit-learn User Guide: Manifold Learning</a></li><li><a href="https://www.statlearning.com/">An Introduction to Statistical Learning</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/07/06/ml-notes-05-unsupervised-learning-dimensionality-reduction/</id>
    <link href="https://sssssimonk.github.io/2024/07/06/ml-notes-05-unsupervised-learning-dimensionality-reduction/"/>
    <published>2024-07-06T14:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理无监督学习和降维。监督学习有 label，模型知道自己要预测什么；无监督学习没有明确答案，目标更多是发现数据结构。最常见的两类任务是 clustering 和 dimensionality reduction。</p>
<h2]]>
    </summary>
    <title>机器学习笔记 05：无监督学习与降维</title>
    <updated>2024-07-06T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Siyu Chen</name>
    </author>
    <category term="机器学习笔记" scheme="https://sssssimonk.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="笔记" scheme="https://sssssimonk.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
    <category term="机器学习" scheme="https://sssssimonk.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="决策树" scheme="https://sssssimonk.github.io/tags/%E5%86%B3%E7%AD%96%E6%A0%91/"/>
    <category term="随机森林" scheme="https://sssssimonk.github.io/tags/%E9%9A%8F%E6%9C%BA%E6%A3%AE%E6%9E%97/"/>
    <category term="XGBoost" scheme="https://sssssimonk.github.io/tags/XGBoost/"/>
    <content>
      <![CDATA[<p>这一篇整理 tree-based models。树模型的核心思想很直观：不断用特征把数据切开，让每个子节点里的样本越来越“纯”。单棵树容易 overfit，所以实际中经常用 ensemble tree models，比如 Random Forest、GBDT、XGBoost。</p><h2 id="1-决策树（decision-tree）的基本想法"><a href="#1-决策树（decision-tree）的基本想法" class="headerlink" title="1. 决策树（decision tree）的基本想法"></a>1. 决策树（decision tree）的基本想法</h2><p>Decision Tree 每一步都在问一个问题：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">这个 feature 按某个 threshold 切开后，数据会不会更容易预测？<br></code></pre></td></tr></table></figure><p>比如判断一个用户是否会购买会员，可以有这样的分裂：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">访问次数 &gt; 10 ?<br>    yes -&gt; 停留时间 &gt; 5 分钟 ?<br>    no  -&gt; 预测不购买<br></code></pre></td></tr></table></figure><p>每个内部节点是一个 split rule，每个叶子节点给出最终预测。</p><p>分类树希望叶子节点里的类别尽量单一；回归树希望叶子节点里的目标值尽量接近。</p><h2 id="2-什么叫节点更“纯”"><a href="#2-什么叫节点更“纯”" class="headerlink" title="2. 什么叫节点更“纯”"></a>2. 什么叫节点更“纯”</h2><p>假设一个节点里有 10 个样本：</p><table><thead><tr><th>类别</th><th align="right">数量</th></tr></thead><tbody><tr><td>正类</td><td align="right">5</td></tr><tr><td>负类</td><td align="right">5</td></tr></tbody></table><p>这个节点很不纯，因为正负各一半。</p><p>如果一次 split 后变成：</p><table><thead><tr><th>子节点</th><th align="right">正类</th><th align="right">负类</th></tr></thead><tbody><tr><td>left</td><td align="right">5</td><td align="right">1</td></tr><tr><td>right</td><td align="right">0</td><td align="right">4</td></tr></tbody></table><p>那这个 split 就不错，因为两个子节点都更容易判断。</p><p>Decision Tree 的 splitting metric，本质上就是量化“split 前后到底变纯了多少”。</p><h2 id="3-熵（entropy）"><a href="#3-熵（entropy）" class="headerlink" title="3. 熵（entropy）"></a>3. 熵（entropy）</h2><p>Entropy 衡量不确定性：</p><p>$$<br>H(S)&#x3D;-\sum_{c}p_c\log_2(p_c)<br>$$</p><p>其中，$p_c$ 是类别 $c$ 在节点中的比例。</p><p>二分类时，如果正负样本各一半，entropy 最大；如果节点里全是同一类，entropy 为 0。</p><p>例子：</p><table><thead><tr><th>节点情况</th><th>Entropy 直觉</th></tr></thead><tbody><tr><td>5 正 &#x2F; 5 负</td><td>不确定性高</td></tr><tr><td>9 正 &#x2F; 1 负</td><td>不确定性低</td></tr><tr><td>10 正 &#x2F; 0 负</td><td>完全纯</td></tr></tbody></table><p>Entropy 越低，节点越纯。</p><h2 id="4-信息增益（information-gain）"><a href="#4-信息增益（information-gain）" class="headerlink" title="4. 信息增益（information gain）"></a>4. 信息增益（information gain）</h2><p>Information Gain 衡量 split 后 entropy 降低了多少：</p><p>$$<br>IG(S,A)&#x3D;H(S)-\sum_{v \in Values(A)}\frac{|S_v|}{|S|}H(S_v)<br>$$</p><p>前半部分是 split 前的不确定性，后半部分是 split 后各子节点不确定性的加权平均。</p><p>如果 split 后子节点变得很纯，后半部分就小，information gain 就大。</p><p>ID3 使用 information gain 来选 split。</p><p>一个直觉例子：用“是否经常访问网站”切用户，如果切完后高访问用户大多购买、低访问用户大多不购买，那么 information gain 就高。如果切完后两边还是正负混杂，那这个 feature 没什么用。</p><h2 id="5-增益率（gain-ratio）"><a href="#5-增益率（gain-ratio）" class="headerlink" title="5. 增益率（gain ratio）"></a>5. 增益率（gain ratio）</h2><p>Information Gain 有一个问题：它偏好取值很多的特征。</p><p>比如用户 ID 这种特征，每个用户几乎都是唯一值。按用户 ID 切，训练集上可以变得很纯，但完全没有泛化意义。</p><p>Gain Ratio 会对这种情况做惩罚：</p><p>$$<br>GainRatio(S,A)&#x3D;\frac{InformationGain(S,A)}{SplitInfo(S,A)}<br>$$</p><p>C4.5 使用 gain ratio。直觉上，它不只看 split 后纯不纯，也看这个 split 是不是把数据切得过于碎。</p><h2 id="6-基尼指数（Gini-index）"><a href="#6-基尼指数（Gini-index）" class="headerlink" title="6. 基尼指数（Gini index）"></a>6. 基尼指数（Gini index）</h2><p>CART 分类树常用 Gini Index：</p><p>$$<br>Gini(S)&#x3D;1-\sum_c p_c^2<br>$$</p><p>Gini 也衡量节点不纯度。节点越纯，Gini 越小。</p><p>二分类时：</p><table><thead><tr><th align="right">正类比例</th><th align="right">Gini</th></tr></thead><tbody><tr><td align="right">0.5</td><td align="right">0.5</td></tr><tr><td align="right">0.9</td><td align="right">0.18</td></tr><tr><td align="right">1.0</td><td align="right">0</td></tr></tbody></table><p>Gini 和 entropy 的目标很接近：都希望 split 后子节点更纯。实际使用中，Gini 计算更简单一些。</p><h2 id="7-ID3、C4-5、CART"><a href="#7-ID3、C4-5、CART" class="headerlink" title="7. ID3、C4.5、CART"></a>7. ID3、C4.5、CART</h2><p>三类经典树可以这样记：</p><table><thead><tr><th>算法</th><th>Split metric</th><th>任务</th></tr></thead><tbody><tr><td>ID3</td><td>Information Gain</td><td>Classification</td></tr><tr><td>C4.5</td><td>Gain Ratio</td><td>Classification</td></tr><tr><td>CART</td><td>Gini &#x2F; Squared Error</td><td>Classification &#x2F; Regression</td></tr></tbody></table><p>CART 是二叉树，每次 split 生成两个子节点。分类时常用 Gini，回归时常用 squared error 或 variance reduction。</p><p>回归树的直觉也很简单：如果一次 split 能让两个子节点内部的目标值更接近，这个 split 就有价值。</p><p>比如预测房价时，按“是否靠近地铁”切分后，如果靠近地铁的一组房价普遍更高，不靠近的一组更低，那么这个 split 就能降低每个子节点内部的误差。</p><h2 id="8-剪枝（pruning）：为什么树需要剪枝"><a href="#8-剪枝（pruning）：为什么树需要剪枝" class="headerlink" title="8. 剪枝（pruning）：为什么树需要剪枝"></a>8. 剪枝（pruning）：为什么树需要剪枝</h2><p>单棵树很容易 overfit。如果不限制深度，它可以不断 split，直到叶子节点里只剩很少样本，甚至每个叶子只对应一个训练样本。</p><p>这在训练集上很好，但对新数据很差。</p><p>常见限制方式：</p><ul><li><code>max_depth</code>：限制树深度</li><li><code>min_samples_split</code>：节点样本太少就不再切</li><li><code>min_samples_leaf</code>：叶子节点至少保留一定样本</li><li><code>max_leaf_nodes</code>：限制叶子节点数</li></ul><p>Pruning 分两类：</p><p><strong>Pre-pruning</strong>：树还没长完就提前停止，比如限制最大深度。</p><p><strong>Post-pruning</strong>：先长出一棵比较完整的树，再从底部往上剪掉泛化收益不大的分支。</p><p>简单说，剪枝就是不让树把训练集记得太细。</p><h2 id="9-随机森林（random-forest）：多棵树投票"><a href="#9-随机森林（random-forest）：多棵树投票" class="headerlink" title="9. 随机森林（random forest）：多棵树投票"></a>9. 随机森林（random forest）：多棵树投票</h2><p>Random Forest 属于 Bagging 方法。它训练很多棵 decision tree，然后让它们投票或取平均。</p><p>Random Forest 的随机性主要来自两点：</p><ol><li>每棵树使用 bootstrap sample，也就是有放回地抽训练样本。</li><li>每次 split 时，只从部分特征中选择最佳 split。</li></ol><p>这样每棵树都不太一样，错误也不会完全一致。</p><p>单棵树 variance 很高，Random Forest 通过多棵树平均来降低 variance。</p><p>一个直觉例子：一个人判断可能很偏，但如果很多个相对独立的人投票，最终结果通常更稳定。Random Forest 也是类似的思路。</p><h2 id="10-提升方法（boosting）、GBDT-和-XGBoost"><a href="#10-提升方法（boosting）、GBDT-和-XGBoost" class="headerlink" title="10. 提升方法（boosting）、GBDT 和 XGBoost"></a>10. 提升方法（boosting）、GBDT 和 XGBoost</h2><p>Boosting 和 Random Forest 不一样。Random Forest 里多棵树大致是并行、独立训练的；Boosting 是一棵接一棵训练，后面的树重点修正前面模型的错误。</p><p>GBDT 的思路是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">initial prediction<br>-&gt; compute residual / negative gradient<br>-&gt; train a new tree to fit the residual<br>-&gt; add this tree to the ensemble<br>-&gt; repeat<br></code></pre></td></tr></table></figure><p>如果第一棵树预测房价总是偏低，下一棵树就会学习这个偏差，把预测往正确方向拉。</p><p>XGBoost 可以看作更工程化、更正则化、更高效的 gradient boosting tree 实现。它常见特点包括：</p><ul><li>使用一阶和二阶梯度信息</li><li>在目标函数中加入正则项</li><li>支持列采样和行采样</li><li>对缺失值有默认 split direction</li><li>工程实现上做了很多并行和缓存优化</li></ul><p>这也是为什么 XGBoost 在传统 tabular data 上经常很强。</p><h2 id="11-几个点"><a href="#11-几个点" class="headerlink" title="11. 几个点"></a>11. 几个点</h2><p>树模型不需要像 KNN 那样强依赖 feature scaling。因为树只关心某个特征是否大于 threshold，不关心距离。</p><p>树模型擅长处理非线性关系和特征交互。比如“访问次数高并且最近 7 天活跃”这种规则，树可以比较自然地表达。</p><p>单棵树容易 overfit。实际项目里更常用 Random Forest、GBDT、XGBoost 这类 ensemble。</p><p>Random Forest 更偏降低 variance，Boosting 更偏逐步降低 bias，但这只是粗略理解，实际效果还和参数、数据、噪声有关。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://scikit-learn.org/stable/modules/tree.html">scikit-learn User Guide: Decision Trees</a></li><li><a href="https://scikit-learn.org/stable/modules/ensemble.html">scikit-learn User Guide: Ensemble Methods</a></li><li><a href="https://www.statlearning.com/">An Introduction to Statistical Learning</a></li><li><a href="https://xgboost.readthedocs.io/">XGBoost Documentation</a></li></ul>]]>
    </content>
    <id>https://sssssimonk.github.io/2024/06/29/ml-notes-04-tree-based-models/</id>
    <link href="https://sssssimonk.github.io/2024/06/29/ml-notes-04-tree-based-models/"/>
    <published>2024-06-29T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>这一篇整理 tree-based models。树模型的核心思想很直观：不断用特征把数据切开，让每个子节点里的样本越来越“纯”。单棵树容易 overfit，所以实际中经常用 ensemble tree models，比如 Random]]>
    </summary>
    <title>机器学习笔记 04：树模型</title>
    <updated>2024-06-29T13:30:00.000Z</updated>
  </entry>
</feed>
