<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Algorithm</title>
	<atom:link href="https://blog.gmem.cc/category/work/algorithm/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Mon, 06 Apr 2026 12:46:48 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>人工智能知识速查（理论）</title>
		<link>https://blog.gmem.cc/ai-knowledge-quick-ref</link>
		<comments>https://blog.gmem.cc/ai-knowledge-quick-ref#comments</comments>
		<pubDate>Fri, 03 Apr 2026 03:15:04 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[Math]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=40317</guid>
		<description><![CDATA[<p>基础数学 代数基础 运算律与代数式 代数式（Algebraic Expression）由常数、变量与运算构成；给定变量取值后即可求值。化简（Simplification）的目标是把表达式写成更可读、更便于推导/比较的等价形式：合并同类项（Like Terms）、提取公因子（Common Factor）、展开（Expansion）与因式分解（Factorization）是最常见的操作。 运算律（Algebraic Laws）本质上是在某个数系/代数结构（例如实数域）中成立的恒等式（Identity）；它们允许在不改变值的前提下重排/重写表达式。需要区分：加法/乘法满足交换律与结合律，但减法/除法一般不满足。 性质 公式 备注 交换律（Commutativity） 不适用于 、 结合律（Associativity） 允许不改变括号结构地分组 分配律（Distributivity） 展开与提因式的核心 恒等元（Identity Element） 0 <a class="read-more" href="https://blog.gmem.cc/ai-knowledge-quick-ref">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/ai-knowledge-quick-ref">人工智能知识速查（理论）</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">基础数学</span></div>
<div class="blog_h2"><span class="graybg">代数基础</span></div>
<div class="blog_h3"><span class="graybg">运算律与代数式</span></div>
<p>代数式（Algebraic Expression）由常数、变量与运算构成；给定变量取值后即可求值。化简（Simplification）的目标是把表达式写成更可读、更便于推导/比较的等价形式：合并同类项（Like Terms）、提取公因子（Common Factor）、展开（Expansion）与因式分解（Factorization）是最常见的操作。</p>
<p>运算律（Algebraic Laws）本质上是在某个数系/代数结构（例如实数域）中成立的恒等式（Identity）；它们允许在不改变值的前提下重排/重写表达式。需要区分：加法/乘法满足交换律与结合律，但减法/除法一般不满足。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">性质</td>
<td style="text-align: center;">公式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>交换律（Commutativity）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a+b=b+a\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(ab=ba\)</span></td>
<td>不适用于 <span displaypfx="inline-" class="mathjax-container">\(a-b\)</span>、<span displaypfx="inline-" class="mathjax-container">\(a/b\)</span></td>
</tr>
<tr>
<td>结合律（Associativity）</td>
<td><span displaypfx="inline-" class="mathjax-container">\((a+b)+c=a+(b+c)\)</span><br /><span displaypfx="inline-" class="mathjax-container">\((ab)c=a(bc)\)</span></td>
<td>允许不改变括号结构地分组</td>
</tr>
<tr>
<td>分配律（Distributivity）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a(b+c)=ab+ac\)</span></td>
<td>展开与提因式的核心</td>
</tr>
<tr>
<td>恒等元（Identity Element）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a+0=a\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(a\cdot 1=a\)</span></td>
<td>0 与 1 在代数推导中常被隐式使用</td>
</tr>
<tr>
<td>逆元（Inverse）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a+(-a)=0\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(a\cdot a^{-1}=1\)</span></td>
<td>第二式要求 <span displaypfx="inline-" class="mathjax-container">\(a\ne 0\)</span></td>
</tr>
<tr>
<td>零乘积原则（Zero Product）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(ab=0\Rightarrow a=0\ \text{或}\ b=0\)</span></td>
<td>把方程从“乘积=0”转为“因子=0”</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">因式分解（Factorization）</span></div>
<p>因式分解（Factorization）把一个表达式改写成若干因子（Factor）的乘积。它的直接价值是：把“值为 0 / 符号变化 / 约束条件”转成对各因子的分析。与之对偶的是展开（Expansion）：把乘积写成和式。</p>
<p>对多项式（Polynomial）<span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>，根（Root/Zero）与线性因子之间存在精确对应：若 <span displaypfx="inline-" class="mathjax-container">\(p(r)=0\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\((x-r)\)</span> 是 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span> 的因子（因子定理（Factor Theorem））。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">套路/恒等式</td>
<td style="text-align: center;">形式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>提公因子（Common Factor）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(ax+ay=a(x+y)\)</span></td>
<td>先把最大公因子提出来</td>
</tr>
<tr>
<td>平方差（Difference of Squares）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^2-b^2=(a-b)(a+b)\)</span></td>
<td>常用于构造可约因子</td>
</tr>
<tr>
<td>完全平方（Perfect Square）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^2\pm 2ab+b^2=(a\pm b)^2\)</span></td>
<td>与配方（Completing the Square）等价</td>
</tr>
<tr>
<td>立方和/差（Sum/Difference of Cubes）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^3-b^3=(a-b)(a^2+ab+b^2)\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(a^3+b^3=(a+b)(a^2-ab+b^2)\)</span></td>
<td>二次因子在实数域可能不可再分</td>
</tr>
<tr>
<td>二次式按根分解</td>
<td><span displaypfx="inline-" class="mathjax-container">\(ax^2+bx+c=a(x-r_1)(x-r_2)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(r_1,r_2\)</span> 可为复数</td>
</tr>
<tr>
<td>分组分解（Grouping）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(ax+ay+bx+by=(a+b)(x+y)\)</span></td>
<td>目标是制造共同因子</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">多项式（Polynomial）</span></div>
<p>一元多项式（Univariate Polynomial）是形如 <span displaypfx="inline-" class="mathjax-container">\(p(x)=\sum_{k=0}^{n} a_k x^k\)</span> 的函数，其中 <span displaypfx="inline-" class="mathjax-container">\(a_k\)</span> 是系数（Coefficient），<span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是次数（Degree）。多项式在实数域上处处可导、可积；在局部逼近（例如 Taylor）与特征构造中非常常用。</p>
<p>多元多项式（Multivariate Polynomial）可写成对多重指数（Multi-index）求和：若 <span displaypfx="inline-" class="mathjax-container">\(x\in\mathbb{R}^d\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[p(x)=\sum_{\alpha\in\mathbb{N}^d} c_\alpha\,x^\alpha,\quad x^\alpha=\prod_{i=1}^{d} x_i^{\alpha_i}\]</span>
<p>补充：多元二次多项式（Quadratic Polynomial）的纯二次部分在线性代数里通常称为二次型（Quadratic Form），可写成矩阵形式 <span displaypfx="inline-" class="mathjax-container">\(q(\mathbf{x})=\mathbf{x}^\top A\mathbf{x}\)</span>；其中 <span displaypfx="inline-" class="mathjax-container">\(x_ix_j\)</span>（例如 <span displaypfx="inline-" class="mathjax-container">\(xy\)</span>）是交叉项（Cross Term）。</p>
<p>在机器学习里，<span style="background-color: #c0c0c0;">多项式特征（Polynomial Features）</span>把输入映射到包含高阶项的特征空间，等价于显式构造某些核函数（Kernel）的有限维版本。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">概念</td>
<td style="text-align: center;">表述</td>
<td style="text-align: center;">用途</td>
</tr>
</thead>
<tbody>
<tr>
<td>首项/首项系数（Leading Term/Coefficient）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a_n x^n\)</span> / <span displaypfx="inline-" class="mathjax-container">\(a_n\)</span></td>
<td>决定远端增长阶与符号</td>
</tr>
<tr>
<td>余数定理（Remainder Theorem）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(p(x)=q(x)(x-a)+p(a)\)</span></td>
<td>快速计算 <span displaypfx="inline-" class="mathjax-container">\(p(a)\)</span></td>
</tr>
<tr>
<td>因子定理（Factor Theorem）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(p(a)=0\Leftrightarrow (x-a)\mid p(x)\)</span></td>
<td>把“根”与“线性因子”连接起来</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">一元二次方程（Quadratic Equation）</span></div>
<p>一元二次方程（Quadratic Equation）标准形式为 <span displaypfx="inline-" class="mathjax-container">\(ax^2+bx+c=0\)</span>（<span displaypfx="inline-" class="mathjax-container">\(a\ne 0\)</span>）。核心量是判别式（Discriminant）<span displaypfx="inline-" class="mathjax-container">\(\Delta=b^2-4ac\)</span>：它决定解的个数与类型。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">结论</td>
<td style="text-align: center;">公式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>求根公式（Quadratic Formula）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(x=\frac{-b\pm\sqrt{\Delta}}{2a}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\Delta&lt;0\)</span> 时根为共轭复数</td>
</tr>
<tr>
<td>配方（Completing the Square）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(ax^2+bx+c=a\left(x+\frac{b}{2a}\right)^2-\frac{\Delta}{4a}\)</span></td>
<td>同时给出顶点与最值</td>
</tr>
<tr>
<td>顶点（Vertex）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(x_v=-\frac{b}{2a},\quad f(x_v)=-\frac{\Delta}{4a}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a&gt;0\)</span> 时为全局最小；<span displaypfx="inline-" class="mathjax-container">\(a&lt;0\)</span> 时为全局最大</td>
</tr>
<tr>
<td>韦达定理（Vieta）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(r_1+r_2=-\frac{b}{a},\quad r_1r_2=\frac{c}{a}\)</span></td>
<td>无需显式求根即可得到对称量</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">分式与有理式（Rational Expressions）</span></div>
<p>分式/有理式（Rational Expression）是两个多项式之比：<span displaypfx="inline-" class="mathjax-container">\(\frac{p(x)}{q(x)}\)</span>，并要求 <span displaypfx="inline-" class="mathjax-container">\(q(x)\ne 0\)</span>。任何化简都必须保留定义域（Domain）约束：约分（Cancellation）只是在允许的点上重写表达式，不会“把不可取值点变得可取”。</p>
<p>典型例子： <span displaypfx="inline-" class="mathjax-container">\(\frac{x^2-1}{x-1}=\frac{(x-1)(x+1)}{x-1}=x+1\)</span>，但仍需强调 <span displaypfx="inline-" class="mathjax-container">\(x\ne 1\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(x=1\)</span> 是可去间断点（Removable Discontinuity）：原式无定义，而约分后的表达式在该点有值。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">操作</td>
<td style="text-align: center;">规则</td>
<td style="text-align: center;">要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>加减</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{a}{b}\pm\frac{c}{d}=\frac{ad\pm bc}{bd}\)</span></td>
<td>先通分（Common Denominator）</td>
</tr>
<tr>
<td>乘除</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{a}{b}\cdot\frac{c}{d}=\frac{ac}{bd}\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\frac{a}{b}\div\frac{c}{d}=\frac{a}{b}\cdot\frac{d}{c}\)</span></td>
<td>除法要求 <span displaypfx="inline-" class="mathjax-container">\(c\ne 0\)</span></td>
</tr>
<tr>
<td>约分</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{(x-r)u(x)}{(x-r)v(x)}=\frac{u(x)}{v(x)}\)</span></td>
<td>仍需保留 <span displaypfx="inline-" class="mathjax-container">\(x\ne r\)</span></td>
</tr>
<tr>
<td>符号分析</td>
<td>把数轴按零点/极点分段</td>
<td>有理不等式常用“区间符号表”</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">绝对值与分段（Absolute Value &amp; Piecewise）</span></div>
<p>绝对值（Absolute Value）<span displaypfx="inline-" class="mathjax-container">\(|x|\)</span> 表示到 0 的距离（Distance to Zero）。它把“正负”信息丢掉，只保留大小；因此绝对值相关方程/不等式通常要通过分段（Piecewise）把符号情况拆开讨论。</p>
<span displaypfx="" class="mathjax-container">\[|x|=\begin{cases}x,&amp; x\ge 0\\ -x,&amp; x&lt;0\end{cases}\]</span>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">形式</td>
<td style="text-align: center;">等价条件</td>
<td style="text-align: center;">前提</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(|x|=a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x=a\ \text{或}\ x=-a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a\ge 0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(|x|&lt;a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(-a&lt;x&lt;a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a&gt;0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(|x|\le a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(-a\le x\le a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a\ge 0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(|x|\ge a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x\le -a\ \text{或}\ x\ge a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a\ge 0\)</span></td>
</tr>
</tbody>
</table>
<p>三角不等式（Triangle Inequality）<span displaypfx="inline-" class="mathjax-container">\(|x+y|\le |x|+|y|\)</span> 是绝对值最重要的性质之一；它把“求和后的误差”上界化为“各自误差的和”，在误差分析与泛化界推导中高频出现。</p>
<p>深度学习里常见的 ReLU、hinge loss 等都是分段函数：<span style="background-color: #c0c0c0;">分段点处通常不可导，但仍可用次梯度（Subgradient）做优化</span>。</p>
<div class="blog_h3"><span class="graybg">常用代数技巧：配方 / 换元 / 估计</span></div>
<div class="blog_h4"><span class="graybg">配方（Completing the Square）</span></div>
<p>配方（Completing the Square）把二次式改写成“平方 + 常数”，从而直接读出最值、解的结构与可行区间：</p>
<span displaypfx="" class="mathjax-container">\[ax^2+bx+c=a\left(x+\frac{b}{2a}\right)^2-\frac{b^2-4ac}{4a}\]</span>
<p>例如 <span displaypfx="inline-" class="mathjax-container">\(x^2+6x+5=(x+3)^2-4\)</span>，因此方程 <span displaypfx="inline-" class="mathjax-container">\(x^2+6x+5=0\)</span> 等价于 <span displaypfx="inline-" class="mathjax-container">\((x+3)^2=4\)</span>，解为 <span displaypfx="inline-" class="mathjax-container">\(x=-1,-5\)</span>。</p>
<div class="blog_h4"><span class="graybg">换元（Substitution）</span></div>
<p>换元（Substitution）通过引入新变量，把原问题变成更低复杂度的标准形式，尤其适用于“重复结构”。例如：</p>
<span displaypfx="" class="mathjax-container">\[x^4-5x^2+4=0,\ \text{令 }u=x^2\Rightarrow u^2-5u+4=0\]</span>
<p>解得 <span displaypfx="inline-" class="mathjax-container">\(u=1,4\)</span>，再回代得到 <span displaypfx="inline-" class="mathjax-container">\(x=\pm 1,\pm 2\)</span>。</p>
<div class="blog_h4"><span class="graybg">估计（Bounding / Estimation）</span></div>
<p>估计（Bounding/Estimation）常把表达式改写为“非负项 + 常数”，或利用单调性把复杂项夹逼到可控区间。最常见的来源是“平方非负”（<span displaypfx="inline-" class="mathjax-container">\((\cdot)^2\ge 0\)</span>）：</p>
<span displaypfx="" class="mathjax-container">\[(x-1)^2\ge 0\Rightarrow x^2+1\ge 2x,\quad (|x|-1)^2\ge 0\Rightarrow x^2+1\ge 2|x|\]</span>
<p>这类估计在证明最值、构造上界/下界、以及把损失函数改写成“凸的主项 + 可控余项”时很有效。</p>
<div class="blog_h3"><span class="graybg">数系（Number Systems）</span></div>
<p>数系（Number Systems）描述“允许使用哪些数，以及这些数上哪些运算是封闭的”。常见链条是</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{N}\subset \mathbb{Z}\subset \mathbb{Q}\subset \mathbb{R}\subset \mathbb{C}\]</span>
<p>其中实数（Real Numbers）是有序（Ordered）且完备（Complete）的；复数（Complex Numbers）扩展了方程可解性（例如 <span displaypfx="inline-" class="mathjax-container">\(x^2+1=0\)</span> 在实数无解，但在复数有解）。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">数系</td>
<td style="text-align: center;">记号</td>
<td style="text-align: center;">典型元素</td>
<td style="text-align: center;">结构要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>自然数（Natural Numbers）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{N}\)</span></td>
<td>0,1,2,…（是否含 0 取决于约定）</td>
<td>对加法/乘法封闭；一般不可做减法/除法</td>
</tr>
<tr>
<td>整数（Integers）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{Z}\)</span></td>
<td>…,-2,-1,0,1,2,…</td>
<td>对加减乘封闭；除法不封闭</td>
</tr>
<tr>
<td>有理数（Rational Numbers）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{Q}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(p/q\)</span>（<span displaypfx="inline-" class="mathjax-container">\(p,q\in\mathbb{Z},q\ne 0\)</span>）</td>
<td>域（Field）：非零元素存在乘法逆元</td>
</tr>
<tr>
<td>实数（Real Numbers）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span></td>
<td>包含无理数（Irrational），如 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{2},\pi\)</span></td>
<td>有序完备域；极限/连续的基础</td>
</tr>
<tr>
<td>复数（Complex Numbers）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{C}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a+bi\)</span></td>
<td>代数闭包（Algebraic Closure）；不可定义全序</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">函数基础</span></div>
<div class="blog_h3"><span class="graybg">定义域与值域</span></div>
<p>函数（Function）是映射：把输入集合中的每个元素映到一个输出。定义域（Domain）是允许的输入集合；值域（Range/Image）是实际能取到的输出集合（通常是陪域（Codomain）的子集）。常用记法：</p>
<span displaypfx="" class="mathjax-container">\[f:D\to Y,\quad x\mapsto f(x)\]</span>
<p>从表达式读定义域的常见约束：</p>
<ul>
<li>分母不为 0： <span displaypfx="inline-" class="mathjax-container">\(\frac{p(x)}{q(x)}\)</span> 要求 <span displaypfx="inline-" class="mathjax-container">\(q(x)\ne 0\)</span>。</li>
<li>偶次根非负： <span displaypfx="inline-" class="mathjax-container">\(\sqrt{g(x)}\)</span> 要求 <span displaypfx="inline-" class="mathjax-container">\(g(x)\ge 0\)</span>（实数域）。</li>
<li>对数正数： <span displaypfx="inline-" class="mathjax-container">\(\ln g(x)\)</span> 要求 <span displaypfx="inline-" class="mathjax-container">\(g(x)&gt;0\)</span>。</li>
</ul>
<p>值域分析常用“解方程 + 约束”思路：令 <span displaypfx="inline-" class="mathjax-container">\(y=f(x)\)</span>，把 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 表示成 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 并推导可行条件；若 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在某区间单调，则可用反函数直接得到值域。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">函数</td>
<td style="text-align: center;">定义域</td>
<td style="text-align: center;">值域</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\sqrt{x}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x\ge 0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(y\ge 0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span></td>
</tr>
<tr>
<td>sigmoid（<span displaypfx="inline-" class="mathjax-container">\(\sigma(z)=\frac{1}{1+e^{-z}}\)</span>）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\tanh z\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\((-1,1)\)</span></td>
</tr>
<tr>
<td>ReLU（<span displaypfx="inline-" class="mathjax-container">\(\max(0,z)\)</span>）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\([0,+\infty)\)</span></td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">复合函数</span></div>
<p>复合函数（Function Composition）把一个函数的输出作为另一个函数的输入：若 <span displaypfx="inline-" class="mathjax-container">\(g:D\to E\)</span>、<span displaypfx="inline-" class="mathjax-container">\(f:E\to Y\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[(f\circ g)(x)=f(g(x))\]</span>
<p>定义域必须同时满足两层约束： <span displaypfx="inline-" class="mathjax-container">\(x\in\mathrm{dom}(g)\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(g(x)\in\mathrm{dom}(f)\)</span>。例如 <span displaypfx="inline-" class="mathjax-container">\(f(x)=\sqrt{x}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(g(x)=x^2-1\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\((f\circ g)(x)=\sqrt{x^2-1}\)</span> 的定义域是 <span displaypfx="inline-" class="mathjax-container">\(|x|\ge 1\)</span>。</p>
<p>复合满足结合律（Associativity）：<span displaypfx="inline-" class="mathjax-container">\((f\circ g)\circ h=f\circ(g\circ h)\)</span>，但一般不满足交换律： <span displaypfx="inline-" class="mathjax-container">\(f\circ g\ne g\circ f\)</span>。</p>
<div class="blog_h3"><span class="graybg">反函数</span></div>
<p>反函数（Inverse Function）把映射“倒过来”。若 <span displaypfx="inline-" class="mathjax-container">\(f:D\to Y\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 上是双射（Bijection），则存在 <span displaypfx="inline-" class="mathjax-container">\(f^{-1}:\mathrm{range}(f)\to D\)</span> 满足</p>
<span displaypfx="" class="mathjax-container">\[f^{-1}(f(x))=x,\quad f(f^{-1}(y))=y\]</span>
<p>注意 <span displaypfx="inline-" class="mathjax-container">\(f^{-1}\)</span> 表示反函数，不是倒数 <span displaypfx="inline-" class="mathjax-container">\(1/f\)</span>。求反函数的常用步骤是：设 <span displaypfx="inline-" class="mathjax-container">\(y=f(x)\)</span>，交换 <span displaypfx="inline-" class="mathjax-container">\(x,y\)</span> 并解出 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>。</p>
<p>例：线性函数 <span displaypfx="inline-" class="mathjax-container">\(y=ax+b\)</span>（<span displaypfx="inline-" class="mathjax-container">\(a\ne 0\)</span>）的反函数是 <span displaypfx="inline-" class="mathjax-container">\(f^{-1}(y)=\frac{y-b}{a}\)</span>。sigmoid 的反函数是 logit：若 <span displaypfx="inline-" class="mathjax-container">\(p=\sigma(z)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(z=\log\frac{p}{1-p}\)</span>（要求 <span displaypfx="inline-" class="mathjax-container">\(p\in(0,1)\)</span>）。</p>
<p>若函数不单调或不可一一对应（如 <span displaypfx="inline-" class="mathjax-container">\(f(x)=x^2\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span> 上），则必须限制定义域（例如限制为 <span displaypfx="inline-" class="mathjax-container">\(x\ge 0\)</span>）才能得到真正的反函数。</p>
<div class="blog_h3"><span class="graybg">奇偶性与单调性</span></div>
<p>奇偶性（Parity）描述对称性：偶函数（Even Function）满足 <span displaypfx="inline-" class="mathjax-container">\(f(-x)=f(x)\)</span>（关于 y 轴对称），奇函数（Odd Function）满足 <span displaypfx="inline-" class="mathjax-container">\(f(-x)=-f(x)\)</span>（关于原点对称）。</p>
<p>单调性（Monotonicity）描述“随输入增加，输出是否不减/不增”。在区间 <span displaypfx="inline-" class="mathjax-container">\(I\)</span> 上：</p>
<ul>
<li>单调递增（Monotone Increasing）：<span displaypfx="inline-" class="mathjax-container">\(x_1&lt;x_2\Rightarrow f(x_1)\le f(x_2)\)</span>。</li>
<li>严格递增（Strictly Increasing）：<span displaypfx="inline-" class="mathjax-container">\(x_1&lt;x_2\Rightarrow f(x_1)&lt;f(x_2)\)</span>。</li>
<li>单调递减/严格递减同理。</li>
</ul>
<p>单调函数在区间上必为单射（Injective），因此在该区间上可定义反函数。这也是为什么很多“不可逆”的函数（如 <span displaypfx="inline-" class="mathjax-container">\(x^2\)</span>）在限制到某个单调区间后就变得可逆。</p>
<div class="blog_h3"><span class="graybg">凸性与凹性</span></div>
<p>凸性（Convexity）是优化与泛化分析的核心几何性质。函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在区间/凸集上是凸函数（Convex Function），当且仅当对任意 <span displaypfx="inline-" class="mathjax-container">\(x_1,x_2\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\lambda\in[0,1]\)</span> 都有</p>
<span displaypfx="" class="mathjax-container">\[f(\lambda x_1+(1-\lambda)x_2)\le \lambda f(x_1)+(1-\lambda)f(x_2)\]</span>
<p>凹函数（Concave Function）则把不等号方向反过来。几何上：凸函数“弦在图像上方”，凹函数“弦在图像下方”。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 二阶可导，则一维判别很简单： <span displaypfx="inline-" class="mathjax-container">\(f''(x)\ge 0\)</span> 则凸， <span displaypfx="inline-" class="mathjax-container">\(f''(x)\le 0\)</span> 则凹；多变量情形把 <span displaypfx="inline-" class="mathjax-container">\(f''\)</span> 替换为 Hessian，要求其半正定/半负定。</p>
<p>典型例子： <span displaypfx="inline-" class="mathjax-container">\(x^2\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(e^x\)</span> 是凸函数；<span displaypfx="inline-" class="mathjax-container">\(\log x\)</span>（<span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span>）是凹函数。很多经典损失（如 MSE、logistic loss）对模型输出是凸的，但对深度网络参数整体通常非凸。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/convex.jpg"><img class="alignnone size-full wp-image-40553" src="https://blog.gmem.cc/wp-content/uploads/2026/03/convex.jpg" alt="convex" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">方程与超平面</span></div>
<div class="blog_h3"><span class="graybg">线性方程（Ax + By + C = 0）</span></div>
<p>二维平面中，线性方程（Linear Equation）<span displaypfx="inline-" class="mathjax-container">\(Ax+By+C=0\)</span>（<span displaypfx="inline-" class="mathjax-container">\((A,B)\ne(0,0)\)</span>）表示一条直线（Line）。向量 <span displaypfx="inline-" class="mathjax-container">\((A,B)\)</span> 是法向量（Normal Vector）：它与直线方向垂直；常数项 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 控制沿法向量方向的平移。</p>
<p>该形式与超平面形式 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\cdot\mathbf{x}+b=0\)</span> 完全一致：只需取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x,y)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}=(A,B)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(b=C\)</span>。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">等价形式</td>
<td style="text-align: center;">表达式</td>
<td style="text-align: center;">条件/说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>斜截式（Slope-Intercept）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(y=mx+b\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(B\ne 0\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(m=-A/B,\ b=-C/B\)</span></td>
</tr>
<tr>
<td>截距（Intercepts）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(x\text{-截距}=-C/A\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(y\text{-截距}=-C/B\)</span></td>
<td>分别要求 <span displaypfx="inline-" class="mathjax-container">\(A\ne 0\)</span>、<span displaypfx="inline-" class="mathjax-container">\(B\ne 0\)</span></td>
</tr>
<tr>
<td>点法式（Point-Normal）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\cdot(\mathbf{x}-\mathbf{x}_0)=0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_0\)</span> 是直线上一点</td>
</tr>
<tr>
<td>点到直线距离</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathrm{dist}=\frac{|Ax_0+By_0+C|}{\sqrt{A^2+B^2}}\)</span></td>
<td>来自把点沿法向量投影到直线</td>
</tr>
</tbody>
</table>
<p>两条直线相交/平行可由法向量判断：若 <span displaypfx="inline-" class="mathjax-container">\((A_1,B_1)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\((A_2,B_2)\)</span> 共线，则两直线平行（或重合）；否则相交，交点可由 2×2 线性方程组求解。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/linear-equation-geometry.jpg"><img class="alignnone size-full wp-image-40541" src="https://blog.gmem.cc/wp-content/uploads/2026/03/linear-equation-geometry.jpg" alt="linear-equation-geometry" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">超平面（w·x + b = 0）</span></div>
<p>超平面（Hyperplane）是高维空间中的“线性边界”。在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^d\)</span> 中，方程</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}\cdot \mathbf{x}+b=0\]</span>
<p>定义一个 <span displaypfx="inline-" class="mathjax-container">\((d-1)\)</span> 维的仿射子空间（Affine Subspace）。其中 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 是法向量（Normal Vector），决定边界的朝向；<span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是偏置（Bias），决定边界沿法向量方向的平移。</p>
<p>工程与论文里常写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top \mathbf{x}+b\)</span>：这里的转置（Transpose）符号 <span displaypfx="inline-" class="mathjax-container">\(^\top\)</span> 只是为了把列向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 变成行向量，从而与列向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 做矩阵乘法；数值上它等价于点积：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}^\top \mathbf{x}=\sum_{i=1}^{d} w_i x_i\]</span>
<p>例：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}=(2,3)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(4,5)^\top\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top \mathbf{x}=2\cdot4+3\cdot5=23\)</span>。</p>
<p>在机器学习里，线性分类器（Linear Classifier）可写成 <span displaypfx="inline-" class="mathjax-container">\(\hat y=\mathrm{sign}(\mathbf{w}^\top\mathbf{x}+b)\)</span>；逻辑回归（Logistic Regression）把它送入 sigmoid： <span displaypfx="inline-" class="mathjax-container">\(p(y=1|\mathbf{x})=\sigma(\mathbf{w}^\top\mathbf{x}+b)\)</span>。</p>
<div class="blog_h3"><span class="graybg">法向量与半空间</span></div>
<p>超平面把空间划分成两个半空间（Half-space）：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}\cdot\mathbf{x}+b\ge 0,\quad \mathbf{w}\cdot\mathbf{x}+b\le 0\]</span>
<p>法向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 指向“值更大”的一侧：沿 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 方向移动会增大 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\cdot\mathbf{x}+b\)</span>。这也是为什么在线性分类里，得分的正负号自然对应类别划分。</p>
<div class="blog_h3"><span class="graybg">点到超平面的距离</span></div>
<p>点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_0\)</span> 到超平面 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\cdot\mathbf{x}+b=0\)</span> 的欧氏距离（Euclidean Distance）为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}(\mathbf{x}_0,\ \mathbf{w}\cdot\mathbf{x}+b=0)=\frac{|\mathbf{w}\cdot\mathbf{x}_0+b|}{\|\mathbf{w}\|_2}\]</span>
<p>推导直觉：把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_0\)</span> 沿法向量方向投影到超平面上；分子是“沿法向量方向的带符号位移”，除以 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|\)</span> 把它变成真实距离。</p>
<div class="blog_h3"><span class="graybg">约束函数、梯度与法向量</span></div>
<p>约束优化中的边界通常由一个定义在整个空间上的标量函数（Scalar Function）<span displaypfx="inline-" class="mathjax-container">\(g(x)\)</span> 给出，并通过等值方程 <span displaypfx="inline-" class="mathjax-container">\(g(x)=0\)</span> 表示边界本身。函数 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 是求导对象；边界则是满足该方程的点集。梯度（Gradient）算子 <span displaypfx="inline-" class="mathjax-container">\(\nabla\)</span> 作用在约束函数 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 上，由此把边界的法向几何信息编码为一个向量场。</p>
<p>线性超平面的情形最直接。边界写成</p>
<span displaypfx="" class="mathjax-container">\[g(\mathbf{x})=\mathbf{w}^\top\mathbf{x}+b=0\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 是定义在整个 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^d\)</span> 上的线性函数，而边界只是它的零等值面（Zero Level Set）。由于线性函数的一阶导数处处相同，立刻得到</p>
<span displaypfx="" class="mathjax-container">\[\nabla g(\mathbf{x})=\mathbf{w}\]</span>
<p>因此，线性超平面的法向量是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span>，本质上等价于“定义该超平面的约束函数 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 的梯度等于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span>”。</p>
<p>这一结论对一般光滑边界同样成立。设 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 是边界 <span displaypfx="inline-" class="mathjax-container">\(g(x)=0\)</span> 上一点，且 <span displaypfx="inline-" class="mathjax-container">\(\nabla g(x^*)\ne 0\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 是该点处的切向量（Tangent Vector），则沿着 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 做无穷小移动仍停留在同一条边界上，因而 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 的一阶变化为 0：</p>
<span displaypfx="" class="mathjax-container">\[\frac{d}{d\epsilon}g(x^*+\epsilon t)\Big|_{\epsilon=0}=0\]</span>
<p>应用链式法则（Chain Rule）得到</p>
<span displaypfx="" class="mathjax-container">\[\nabla g(x^*)^\top t=0\]</span>
<p>这表示边界上的任意切向量都与 <span displaypfx="inline-" class="mathjax-container">\(\nabla g(x^*)\)</span> 正交。因此 <span displaypfx="inline-" class="mathjax-container">\(\nabla g(x^*)\)</span> 沿法向方向指向边界外侧或内侧，而不沿边界本身滑动；梯度正是边界法向量的解析表达。</p>
<p>KKT 条件中的 <span displaypfx="inline-" class="mathjax-container">\(\nabla g(x^*)\)</span> 正是以这种方式出现的。约束优化不是对“边界这个集合”求导，而是借助约束函数 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 的梯度，把边界的法向几何结构写入一阶最优性条件。驻点条件</p>
<span displaypfx="" class="mathjax-container">\[\nabla f(x^*)+\lambda^*\nabla g(x^*)=0\]</span>
<p>表达的是：在活跃边界上，目标函数剩余的下降趋势完全落在约束边界的法向空间里，并与乘子加权后的法向量达到平衡。</p>
<div class="blog_h2"><span class="graybg">基础几何（Basic Geometry）</span></div>
<p>解析几何（Analytic Geometry）、向量（Vector）、三角函数（Trigonometric Functions）以及很多 AI 中的空间直觉，都建立在更基础的几何概念上。这里先把最常用的几块地基补齐：距离、角度、弧度、比例与面积。</p>
<div class="blog_h3"><span class="graybg">点、线、角与角度单位</span></div>
<p>平面几何最基本的对象是点（Point）、线段（Segment）、直线（Line）与角（Angle）。角度描述两条射线的张开程度；常见有两种单位：</p>
<span displaypfx="" class="mathjax-container">\[360^\circ=2\pi\ \text{rad},\quad 180^\circ=\pi\ \text{rad},\quad 90^\circ=\frac{\pi}{2}\ \text{rad}\]</span>
<p>度数（Degree）更适合日常表达，弧度（Radian）更适合数学推导，因为它和圆弧长度、三角函数、导数公式天然兼容。后面遇到旋转矩阵（Rotation Matrix）、复数极坐标（Polar Form）、傅里叶分析（Fourier Analysis）时，默认几乎都使用弧度。</p>
<div class="blog_h3"><span class="graybg">勾股定理与欧氏距离</span></div>
<p>勾股定理（Pythagorean Theorem）是平面距离公式的根源。对直角三角形，若两条直角边长为 <span displaypfx="inline-" class="mathjax-container">\(a,b\)</span>，斜边长为 <span displaypfx="inline-" class="mathjax-container">\(c\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[a^2+b^2=c^2\]</span>
<p>把它应用到坐标平面，就得到两点之间的欧氏距离（Euclidean Distance）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\]</span>
<p>在高维空间里，这个公式直接推广为 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}-\mathbf{y}\|_2\)</span>。因此从二维几何到机器学习里的向量距离，本质上是一条连续的概念链。KNN、K-means、embedding 检索、对比学习（Contrastive Learning）都在反复使用这套“距离越小越相似”的几何直觉。</p>
<div class="blog_h3"><span class="graybg">弧度、弧长与扇形</span></div>
<p>弧度由圆弧长度直接定义：若半径为 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 的圆上有一段弧长 <span displaypfx="inline-" class="mathjax-container">\(s\)</span>，对应圆心角为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>（弧度），则</p>
<span displaypfx="" class="mathjax-container">\[\theta=\frac{s}{r},\quad s=r\theta\]</span>
<p>对应扇形面积（Sector Area）是：</p>
<span displaypfx="" class="mathjax-container">\[A=\frac{1}{2}r^2\theta\]</span>
<p>这正是弧度“自然”的原因：一旦用弧度记角，弧长与面积公式会变得非常干净。AI 里很多周期性表示都默认使用弧度输入，例如 <span displaypfx="inline-" class="mathjax-container">\(\sin\theta\)</span> / <span displaypfx="inline-" class="mathjax-container">\(\cos\theta\)</span> 的位置编码（Positional Encoding）、旋转位置编码（RoPE）以及频域特征（Fourier Features）。</p>
<div class="blog_h3"><span class="graybg">相似、缩放与比例</span></div>
<p>相似（Similarity）指图形形状相同、大小可以不同；等价地说，对应角相等、对应边成比例。若把一个图形按比例 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 缩放，则长度变为原来的 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 倍，面积变为原来的 <span displaypfx="inline-" class="mathjax-container">\(k^2\)</span> 倍。</p>
<p>这个看似初等的事实，在 AI 图像处理中极其常见：图片 resize、本征尺度（Scale）、特征金字塔（Feature Pyramid）、多尺度检测（Multi-scale Detection）都在处理“同一对象在不同尺度下如何保持可识别性”的问题。若缩放时不保持纵横比（Aspect Ratio），就会引入几何畸变，进而影响分类、检测与分割结果。</p>
<div class="blog_h3"><span class="graybg">面积、重叠与 IoU</span></div>
<p>基础几何里，面积（Area）衡量二维区域所占的大小。矩形面积是长乘宽，圆面积是</p>
<span displaypfx="" class="mathjax-container">\[A=\pi r^2\]</span>
<p>在 AI 的目标检测（Object Detection）与实例分割（Instance Segmentation）中，一个高频几何量是交并比（Intersection over Union, IoU）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{IoU}=\frac{\text{Intersection Area}}{\text{Union Area}}\]</span>
<p>它衡量预测框/预测区域与真实标注的重叠程度。这里用到的不是高深数学，而是最朴素的面积与重叠概念。</p>
<div class="blog_h3"><span class="graybg">基础几何和AI</span></div>
<p>若把这些内容压缩成一句话，它们在 AI 中分别承担不同角色：</p>
<ul>
<li>距离（Distance）：支撑近邻搜索、聚类、向量检索与损失函数中的相似度刻画。</li>
<li>角度（Angle）：支撑方向、夹角、余弦相似度（Cosine Similarity）与旋转直觉。</li>
<li>弧度（Radian）：支撑三角函数、周期建模、位置编码与频域表示。</li>
<li>比例与缩放（Scale）：支撑图像 resize、数据增强、特征金字塔与多尺度建模。</li>
<li>面积与重叠（Area &amp; Overlap）：支撑 IoU、检测框评估与分割质量度量。</li>
</ul>
<div class="blog_h2"><span class="graybg">解析几何（Analytic Geometry）</span></div>
<p>解析几何（Analytic Geometry）的核心做法是：选定坐标系（Coordinate System），用代数方程描述几何对象。一个几何对象可以被理解为“满足某个方程（或方程组）的所有点”的集合。</p>
<p>高中阶段最常见的两类：</p>
<ul>
<li>一次方程：直线（Line）/平面（Plane）/超平面（Hyperplane）。</li>
<li>二次方程：圆（Circle）与圆锥曲线（Conic Sections）；在三维中推广为二次曲面（Quadric Surfaces）。</li>
</ul>
<div class="blog_h3"><span class="graybg">坐标、距离与圆</span></div>
<p>在直角坐标系（Cartesian Coordinate System）中，点 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 用坐标 <span displaypfx="inline-" class="mathjax-container">\((x,y)\)</span> 表示。两点 <span displaypfx="inline-" class="mathjax-container">\(P_1(x_1,y_1)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(P_2(x_2,y_2)\)</span> 的欧氏距离（Euclidean Distance）是：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\]</span>
<p>圆（Circle）是到某个固定点距离恒定的点集；这个固定点叫圆心（Center）。若圆心为 <span displaypfx="inline-" class="mathjax-container">\((h,k)\)</span>、半径（Radius）为 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>，则圆的方程是：</p>
<span displaypfx="" class="mathjax-container">\[(x-h)^2+(y-k)^2=r^2\]</span>
<p>例：方程 <span displaypfx="inline-" class="mathjax-container">\(x^2+y^2-4x+6y-12=0\)</span> 通过配方（Completing the Square）可化为 <span displaypfx="inline-" class="mathjax-container">\((x-2)^2+(y+3)^2=25\)</span>，因此它表示圆心 <span displaypfx="inline-" class="mathjax-container">\((2,-3)\)</span>、半径 <span displaypfx="inline-" class="mathjax-container">\(5\)</span> 的圆。</p>
<div class="blog_h3"><span class="graybg">圆锥曲线（Conic Sections）：二次方程的几何形状</span></div>
<p>圆锥曲线（Conic Sections）最初来自“平面截圆锥”的几何构造，但在解析几何里，它们等价于二维的二次方程曲线（Second-degree Plane Curves，方程里变量的最高次数为 2）：</p>
<span displaypfx="" class="mathjax-container">\[Ax^2+Bxy+Cy^2+Dx+Ey+F=0\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(Bxy\)</span> 是交叉项（Cross Term）。交叉项的存在通常意味着曲线的主轴（Principal Axes）与坐标轴不对齐；通过旋转坐标轴（Rotation of Axes）可以把交叉项消掉，从而得到更“标准”的形状表达。</p>
<p>而一次项 <span displaypfx="inline-" class="mathjax-container">\(Dx+Ey\)</span> 与常数项 <span displaypfx="inline-" class="mathjax-container">\(F\)</span> 扮演的是另一类角色：它们通常不改变主轴方向，而主要影响图形在平面中的<span style="background-color: #c0c0c0;">位置与尺度</span>。更具体地说，一次项往往意味着曲线的中心/顶点不在原点；在消去交叉项之后，再通过平移坐标（Translation of Axes）与配方（Completing the Square）可以把一次项吸收到平方项里。常数项则相当于改变“等号右边的阈值”，会影响曲线是否有实点、整体大小以及离原点的偏置。简言之：<span style="background-color: #c0c0c0;">交叉项主要对应旋转，一次项主要对应平移，常数项主要对应整体偏移/尺度调整</span>。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/ellipse-term-effects.png"><img class="alignnone size-full wp-image-40767" src="https://blog.gmem.cc/wp-content/uploads/2026/03/ellipse-term-effects.png" alt="ellipse-term-effects" width="100%" /></a></p>
<p>下面给出最常用的四类圆锥曲线的标准方程（Standard Form）与直观定义：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">名称</td>
<td style="text-align: center;">标准方程</td>
<td style="text-align: center;">几何定义（直观）</td>
<td style="text-align: center;">形状关键词</td>
</tr>
</thead>
<tbody>
<tr>
<td>圆（Circle）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(x^2+y^2=r^2\)</span></td>
<td>到圆心距离恒为 <span displaypfx="inline-" class="mathjax-container">\(r\)</span></td>
<td>闭合；各向同性</td>
</tr>
<tr>
<td>椭圆（Ellipse）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{x^2}{a^2}+\frac{y^2}{b^2}=1\ (a\ge b&gt;0)\)</span></td>
<td>到两个焦点（Focus）距离和为常数</td>
<td>闭合；主轴/次轴</td>
</tr>
<tr>
<td>抛物线（Parabola）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(y^2=4px\ (p&gt;0)\)</span></td>
<td>到焦点（Focus）与准线（Directrix）距离相等</td>
<td>开口；无中心</td>
</tr>
<tr>
<td>双曲线（Hyperbola）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{x^2}{a^2}-\frac{y^2}{b^2}=1\)</span></td>
<td>到两个焦点（Focus）距离差的绝对值为常数</td>
<td>两支；有渐近线（Asymptotes）</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">椭圆（Ellipse）：两焦点“距离和恒定”</span></div>
<p>椭圆（Ellipse）可以用一句话定义：平面内到两个固定点 <span displaypfx="inline-" class="mathjax-container">\(F_1,F_2\)</span> 的距离之和为常数的点的集合。若该常数为 <span displaypfx="inline-" class="mathjax-container">\(2a\)</span>，则对椭圆上任意点 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 有：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}(P,F_1)+\mathrm{dist}(P,F_2)=2a\]</span>
<p>在以原点为中心、长轴沿 x 轴的标准位置下，椭圆方程是：</p>
<span displaypfx="" class="mathjax-container">\[\frac{x^2}{a^2}+\frac{y^2}{b^2}=1,\quad a\ge b&gt;0\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 分别是半长轴（Semi-major Axis）与半短轴（Semi-minor Axis）。焦距参数 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 定义为焦点到中心的距离，满足</p>
<span displaypfx="" class="mathjax-container">\[c^2=a^2-b^2\]</span>
<p>因此焦点坐标是 <span displaypfx="inline-" class="mathjax-container">\((\pm c,0)\)</span>。偏心率（Eccentricity）定义为 <span displaypfx="inline-" class="mathjax-container">\(e=c/a\)</span>，它量化“椭圆有多扁”： <span displaypfx="inline-" class="mathjax-container">\(e\in[0,1)\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(a=b\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(e=0\)</span>，椭圆退化为圆。</p>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(a=5,b=3\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(c=4\)</span>、<span displaypfx="inline-" class="mathjax-container">\(e=0.8\)</span>，椭圆为 <span displaypfx="inline-" class="mathjax-container">\(\frac{x^2}{25}+\frac{y^2}{9}=1\)</span>，焦点为 <span displaypfx="inline-" class="mathjax-container">\((\pm 4,0)\)</span>。</p>
<div class="blog_h3"><span class="graybg">三维推广：二次曲面（Quadric Surfaces）</span></div>
<p>在三维中，圆锥曲线推广为二次曲面（Quadric Surfaces）：满足三元二次方程的点集。最一般的形式是：</p>
<span displaypfx="" class="mathjax-container">\[Ax^2+By^2+Cz^2+Dxy+Exz+Fyz+Gx+Hy+Iz+J=0\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(Dxy,Exz,Fyz\)</span> 是交叉项（Cross Term），对应“坐标轴没有对齐到曲面的主轴方向”。通过平移（Translation）与旋转（Rotation）可以把它化为标准型（Standard Form）：平移等价于把原点挪到合适的位置（通常是“中心”附近），旋转等价于把坐标轴转到主轴方向，从而一眼看出是“球/椭球/抛物面/双曲面”等哪一类。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">名称</td>
<td style="text-align: center;">典型方程（标准型）</td>
<td style="text-align: center;">直观描述</td>
</tr>
</thead>
<tbody>
<tr>
<td>球（Sphere）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(x^2+y^2+z^2=r^2\)</span></td>
<td>到中心距离恒定的点集（3D 的圆）</td>
</tr>
<tr>
<td>椭球（Ellipsoid）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{x^2}{a^2}+\frac{y^2}{b^2}+\frac{z^2}{c^2}=1\)</span></td>
<td>三个方向缩放不同的“球”；仍然闭合</td>
</tr>
<tr>
<td>椭圆抛物面（Elliptic Paraboloid）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(z=\frac{x^2}{a^2}+\frac{y^2}{b^2}\)</span></td>
<td>“碗状”；水平截面是椭圆</td>
</tr>
<tr>
<td>双曲抛物面（Hyperbolic Paraboloid）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(z=\frac{x^2}{a^2}-\frac{y^2}{b^2}\)</span></td>
<td>“马鞍形”；沿一个方向上凸、另一个方向下凹</td>
</tr>
<tr>
<td>单叶双曲面（Hyperboloid of One Sheet）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{x^2}{a^2}+\frac{y^2}{b^2}-\frac{z^2}{c^2}=1\)</span></td>
<td>连通的一张曲面；截面随方向变化</td>
</tr>
<tr>
<td>双叶双曲面（Hyperboloid of Two Sheets）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(-\frac{x^2}{a^2}-\frac{y^2}{b^2}+\frac{z^2}{c^2}=1\)</span></td>
<td>上下分离的两张曲面</td>
</tr>
</tbody>
</table>
<p>二次曲面与优化中的“二次型/曲率”是同一套数学语言：把坐标轴旋到主轴方向后，表达式会变成各轴平方项的加权和/差，从而直接暴露“碗状（局部最小）”与“鞍形（Saddle）”的结构。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-quadric.png"><img class="alignnone size-full wp-image-40777" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-quadric.png" alt="plot-quadric" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">指数函数</span></div>
<p>指数函数（Exponential Function）最常用的是自然指数 <span displaypfx="inline-" class="mathjax-container">\(e^x\)</span>。指数运算把“加法结构”映射为“乘法结构”，对数（Logarithm）作为反函数则把乘法结构拉平成加法结构。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">性质/公式</td>
<td style="text-align: center;">表达式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>指数基本性质（Exponential Laws）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{a+b}=e^a e^b\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(e^{a-b}=\frac{e^a}{e^b}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a,b\in\mathbb{R}\)</span></td>
</tr>
<tr>
<td>指数运算律（Exponent Rules）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^0=1\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(a^m a^n=a^{m+n}\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\frac{a^m}{a^n}=a^{m-n}\)</span><br /><span displaypfx="inline-" class="mathjax-container">\((a^m)^n=a^{mn}\)</span><br /><span displaypfx="inline-" class="mathjax-container">\((ab)^n=a^n b^n\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a&gt;0,a\ne 1\)</span>；对整数指数最直接</td>
</tr>
<tr>
<td>自然常数（Euler's Number）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(e=\lim_{n\to\infty}\left(1+\frac{1}{n}\right)^n\)</span></td>
<td>极限刻画连续复利（Continuous Compounding）</td>
</tr>
<tr>
<td>微积分性质</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{d}{dx}e^x=e^x\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\frac{d}{dx}\ln x=\frac{1}{x}\)</span></td>
<td>以 <span displaypfx="inline-" class="mathjax-container">\(e\)</span> 为底时形式最简</td>
</tr>
<tr>
<td>连续增长微分方程</td>
<td><span displaypfx="inline-" class="mathjax-container">\(y'(t)=y(t),\ y(0)=1\Rightarrow y(t)=e^t\)</span></td>
<td>“增长率与当前值成正比”</td>
</tr>
<tr>
<td>与对数互逆</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln(e^x)=x\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(e^{\ln x}=x\)</span></td>
<td>第二式要求 <span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span></td>
</tr>
</tbody>
</table>
<p>常用取值：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">输入</td>
<td style="text-align: center;"><span displaypfx="inline-" class="mathjax-container">\(e^x\)</span></td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x=0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^0=1\)</span></td>
<td>基准点</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x=1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^1=e\approx 2.71828\)</span></td>
<td>自然对数底</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x=-1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{-1}=\frac{1}{e}\approx 0.36788\)</span></td>
<td>常见衰减尺度</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x=\ln 2\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{\ln 2}=2\)</span></td>
<td>对数域与线性域互换时常用</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x=\ln 10\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{\ln 10}=10\)</span></td>
<td>与 <span displaypfx="inline-" class="mathjax-container">\(\log_{10}\)</span> 换底相关</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">对数函数</span></div>
<p>对数函数（Logarithm）<span displaypfx="inline-" class="mathjax-container">\(\log x\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span> 上严格单调递增（Strictly Increasing），因此 <span displaypfx="inline-" class="mathjax-container">\(-\log x\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span> 上严格单调递减（Strictly Decreasing）。</p>
<p>复合后的单调性由内层决定：若 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 在某区间上单调递增且 <span displaypfx="inline-" class="mathjax-container">\(f(x)&gt;0\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(-\log(f(x))\)</span> 在该区间上单调递减；若 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 不单调，则外层单调并不能推出整体单调。</p>
<p>在损失函数里常见的形式是 <span displaypfx="inline-" class="mathjax-container">\(-\log\sigma(z)\)</span>（<span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 为 sigmoid）。因为 sigmoid 单调递增，所以该损失对 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 单调递减：增大 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 会降低损失。需要注意的是，这只说明“对中间量 z 的单调性”；对模型参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的单调性一般不成立，因为 <span displaypfx="inline-" class="mathjax-container">\(z=z(\theta)\)</span> 是高维非线性函数。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">性质/公式</td>
<td style="text-align: center;">表达式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>对数运算律（Logarithm Rules）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\log(ab)=\log a+\log b\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\log\!\left(\frac{a}{b}\right)=\log a-\log b\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\log(a^k)=k\log a\)</span></td>
<td>同一底数；典型要求 <span displaypfx="inline-" class="mathjax-container">\(a&gt;0,b&gt;0\)</span></td>
</tr>
<tr>
<td>换底公式（Change of Base）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_a x=\frac{\ln x}{\ln a}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a&gt;0,a\ne 1,x&gt;0\)</span></td>
</tr>
<tr>
<td>与指数互逆</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^{\log_a x}=x\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\log_a(a^x)=x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span></td>
</tr>
</tbody>
</table>
<p>常用取值：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">表达式</td>
<td style="text-align: center;">值</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln 1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x=1\)</span> 为基准点</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln e\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(1\)</span></td>
<td>自然对数的定义性质</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln 2\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 0.6931\)</span></td>
<td>二进制相关常数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln 10\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 2.3026\)</span></td>
<td>十进制相关常数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_{10}2\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=\frac{\ln 2}{\ln 10}\approx 0.3010\)</span></td>
<td>工程里常用于数量级估算</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_2 10\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=\frac{\ln 10}{\ln 2}\approx 3.3219\)</span></td>
<td>bit 与十进制数量级换算</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_2 e\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=\frac{1}{\ln 2}\approx 1.4427\)</span></td>
<td>nats 与 bits 换算常数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_{10}e\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=\frac{1}{\ln 10}\approx 0.4343\)</span></td>
<td>自然对数与常用对数换算</td>
</tr>
</tbody>
</table>
<p>在语言模型 softmax 中，logit 经过指数再归一化：<span displaypfx="inline-" class="mathjax-container">\(\exp(\text{logit})\)</span> 把分数映射为正数权重；取 log 则把乘法结构拉平成加法结构，便于用和式写出似然与损失。</p>
<div class="blog_h2"><span class="graybg">幂函数</span></div>
<p>幂函数（Power Function）里常见的两个扩展是负指数（Negative Exponent）与分数指数（Rational Exponent）。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类型</td>
<td style="text-align: center;">公式</td>
<td style="text-align: center;">条件/备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>负指数（Negative Exponent）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^{-n}=\frac{1}{a^n}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a\ne 0\)</span>；<span displaypfx="inline-" class="mathjax-container">\(n\)</span> 为正整数</td>
</tr>
<tr>
<td>分数指数（Rational Exponent）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^{p/q}=\sqrt[q]{a^p}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(q&gt;0,\gcd(p,q)=1\)</span>；实数域通常要求 <span displaypfx="inline-" class="mathjax-container">\(a&gt;0\)</span></td>
</tr>
<tr>
<td>例</td>
<td><span displaypfx="inline-" class="mathjax-container">\(2^{-3}=\frac{1}{8}\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(9^{1/2}=3\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(8^{2/3}=4\)</span></td>
<td>偶次根要求被开方数非负</td>
</tr>
</tbody>
</table>
<p>常用取值：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">表达式</td>
<td style="text-align: center;">值</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(2^{-3}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=\frac{1}{8}=0.125\)</span></td>
<td>负指数转倒数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(10^{-3}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(=0.001\)</span></td>
<td>毫（<span displaypfx="inline-" class="mathjax-container">\(10^{-3}\)</span>）尺度</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(2^{1/2}=\sqrt{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 1.4142\)</span></td>
<td>最常见的无理数根</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(2^{-1/2}=\frac{1}{\sqrt{2}}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 0.7071\)</span></td>
<td>正交归一化、幅度缩放常用</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(10^{1/2}=\sqrt{10}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 3.1623\)</span></td>
<td>对数刻度下的“半个数量级”</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(10^{-1/2}=\frac{1}{\sqrt{10}}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\approx 0.3162\)</span></td>
<td>与上式互为倒数</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">三角函数</span></div>
<div class="blog_h3"><span class="graybg">基本三角恒等式</span></div>
<p>三角函数（Trigonometric Functions）可以用单位圆（Unit Circle）定义：在圆上角度为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的点坐标是 <span displaypfx="inline-" class="mathjax-container">\((\cos\theta,\sin\theta)\)</span>。由此得到最基本恒等式：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center; width: 50%;">公式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>基本恒等式</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin^2\theta+\cos^2\theta=1\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\tan\theta=\frac{\sin\theta}{\cos\theta}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\cos\theta\ne 0\)</span> 时定义 <span displaypfx="inline-" class="mathjax-container">\(\tan\theta\)</span></td>
</tr>
<tr>
<td>与 <span displaypfx="inline-" class="mathjax-container">\(\tan,\cot\)</span> 相关</td>
<td><span displaypfx="inline-" class="mathjax-container">\(1+\tan^2\theta=\sec^2\theta\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(1+\cot^2\theta=\csc^2\theta\)</span></td>
<td>定义域同 <span displaypfx="inline-" class="mathjax-container">\(\tan,\cot\)</span></td>
</tr>
<tr>
<td>和差公式（Angle Addition）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin(\alpha\pm\beta)=\sin\alpha\cos\beta\pm\cos\alpha\sin\beta\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\cos(\alpha\pm\beta)=\cos\alpha\cos\beta\mp\sin\alpha\sin\beta\)</span></td>
<td>傅里叶分析、RoPE 等直觉常用</td>
</tr>
<tr>
<td>二倍角（Double-Angle）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin 2\theta=2\sin\theta\cos\theta\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\cos 2\theta=\cos^2\theta-\sin^2\theta=1-2\sin^2\theta=2\cos^2\theta-1\)</span></td>
<td>同一恒等式的不同等价写法</td>
</tr>
<tr>
<td>周期性（Periodicity）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin(\theta+2\pi)=\sin\theta\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\cos(\theta+2\pi)=\cos\theta\)</span></td>
<td>一个周期（Period）为 <span displaypfx="inline-" class="mathjax-container">\(2\pi\)</span></td>
</tr>
<tr>
<td>常用极限（<span displaypfx="inline-" class="mathjax-container">\(x\to 0\)</span>）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\lim_{x\to 0}\frac{\sin x}{x}=1\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\lim_{x\to 0}\frac{\tan x}{x}=1\)</span><br /><span displaypfx="inline-" class="mathjax-container">\(\lim_{x\to 0}\frac{1-\cos x}{x^2}=\frac{1}{2}\)</span></td>
<td>推导导数与近似时高频出现</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">常用特殊角（Special Angles）</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;"><span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>（弧度）</td>
<td style="text-align: center;">角度制</td>
<td style="text-align: center;"><span displaypfx="inline-" class="mathjax-container">\(\sin\theta\)</span></td>
<td style="text-align: center;"><span displaypfx="inline-" class="mathjax-container">\(\cos\theta\)</span></td>
<td style="text-align: center;"><span displaypfx="inline-" class="mathjax-container">\(\tan\theta\)</span></td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td>0°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\pi/6\)</span></td>
<td>30°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{\sqrt{3}}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{\sqrt{3}}\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\pi/4\)</span></td>
<td>45°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{\sqrt{2}}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{\sqrt{2}}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(1\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\pi/3\)</span></td>
<td>60°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{\sqrt{3}}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sqrt{3}\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\pi/2\)</span></td>
<td>90°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td>未定义（undefined）</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\pi\)</span></td>
<td>180°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(-1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(3\pi/2\)</span></td>
<td>270°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(-1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td>未定义（undefined）</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(2\pi\)</span></td>
<td>360°</td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(1\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">欧拉公式</span></div>
<p>欧拉公式（Euler's Formula）把指数与三角函数在复数域（Complex Domain）里统一起来：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">结论</td>
<td style="text-align: center;">公式</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>欧拉公式（Euler's Formula）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{i\theta}=\cos\theta+i\sin\theta\)</span></td>
<td>把“旋转”写成复指数</td>
</tr>
<tr>
<td>辐角相加对应指数相乘</td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{i\theta_1}e^{i\theta_2}=e^{i(\theta_1+\theta_2)}\)</span></td>
<td>复数乘法：模相乘、辐角相加</td>
</tr>
<tr>
<td>欧拉恒等式（Euler's Identity）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^{i\pi}+1=0\)</span></td>
<td>连接 <span displaypfx="inline-" class="mathjax-container">\(e,i,\pi,1,0\)</span></td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">基础函数图像</span></div>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/image-of-common-fns.jpg"><img class="alignnone size-full wp-image-40557" src="https://blog.gmem.cc/wp-content/uploads/2026/03/image-of-common-fns.jpg" alt="image-of-common-fns" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">复数</span></div>
<p>复数（Complex Number）是对实数系（Real Number System）的扩展，写作 <span displaypfx="inline-" class="mathjax-container">\(a+bi\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(a,b\in\mathbb{R}\)</span>，虚数单位（Imaginary Unit）满足 <span displaypfx="inline-" class="mathjax-container">\(i^2=-1\)</span>。几何上，复数可以表示为复平面（Complex Plane）上的点 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span>；但更重要的是，它在二维平面上提供了一套封闭、可逆且与乘法兼容的代数结构。</p>
<p>也正因为如此，复数不能简单等同于 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^2\)</span> 里的二维向量。表面上看，二维向量 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 与复数 <span displaypfx="inline-" class="mathjax-container">\(a+bi\)</span> 确实对应同一个平面点；但它们的<span style="background-color: #c0c0c0;">代数结构</span>完全不同，关键差别在于“乘法”是否自然、闭合且可逆。</p>
<p>对二维向量而言，加法非常自然，但乘法并不形成一个像实数那样稳定的数系：点乘（Inner Product）会把两个向量变成标量，叉乘（Cross Product）又会把结果带到垂直方向；因此二维向量空间本身并没有一个同时兼顾<span style="background-color: #c0c0c0;">封闭性（Closure）</span>与<span style="background-color: #c0c0c0;">可除性</span>的内建乘法。复数则不同：两个复数相乘后仍是复数，非零复数还总能做除法。这使得复平面不只是“几何上的二维平面”，而是一个可自由做加减乘除的完整数系。</p>
<p>这带来两个二维向量本身不具备的优势。第一，复数把二维旋转直接写进了乘法：若 <span displaypfx="inline-" class="mathjax-container">\(z=re^{i\theta}\)</span>，则乘以另一个复数时会自动实现“模相乘、角相加”。换句话说，<span style="background-color: #c0c0c0;">复数乘法天然就是缩放 + 旋转</span>；而若只用二维向量，通常还需要额外引入旋转矩阵。第二，复数让多项式方程的可解性闭合：例如 <span displaypfx="inline-" class="mathjax-container">\(x^2+1=0\)</span> 在实数域无解，但在复数域有解 <span displaypfx="inline-" class="mathjax-container">\(\pm i\)</span>。更深一层地，代数基本定理（Fundamental Theorem of Algebra）说明任意非常数多项式在复数域里都有根，因此复数成为代数方程的自然终点。</p>
<p>因此，二维向量更像是在描述“箭头、位移、速度、受力”的线性对象；复数则是在同一个平面上额外安装了一套兼容乘法、旋转与方程求解的代数机制。对于信号处理（Signal Processing）、交流电分析、傅里叶变换（Fourier Transform）、量子力学以及很多 AI 中的频域方法，复数都不是“二维向量的重复发明”，而是一个更强的二维代数系统。</p>
<div class="blog_h3"><span class="graybg">复数的表示：直角坐标（Rectangular Form）</span></div>
<p>复数（Complex Number）写作 <span displaypfx="inline-" class="mathjax-container">\(z=a+bi\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(a,b\in\mathbb{R}\)</span>，虚数单位（Imaginary Unit）满足 <span displaypfx="inline-" class="mathjax-container">\(i^2=-1\)</span>。把 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 视为二维平面上的点 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span>，就得到直角坐标（Rectangular Form）。</p>
<div class="blog_h3"><span class="graybg">复数的表示：极坐标（Polar Form）</span></div>
<p>同一个点也可用极坐标（Polar Form）表示：令 <span displaypfx="inline-" class="mathjax-container">\(r=|z|\)</span> 为模（Modulus），<span displaypfx="inline-" class="mathjax-container">\(\theta=\arg(z)\)</span> 为辐角（Argument），则</p>
<span displaypfx="" class="mathjax-container">\[z=r(\cos\theta+i\sin\theta)=re^{i\theta}\]</span>
<p>两种坐标之间的转换：</p>
<span displaypfx="" class="mathjax-container">\[r=\sqrt{a^2+b^2},\quad a=r\cos\theta,\quad b=r\sin\theta\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 通常用 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{atan2}(b,a)\)</span> 计算，并且 <span displaypfx="inline-" class="mathjax-container">\(\arg(z)\)</span> 不是唯一的：加上任意 <span displaypfx="inline-" class="mathjax-container">\(2\pi k\)</span>（<span displaypfx="inline-" class="mathjax-container">\(k\in\mathbb{Z}\)</span>）表示同一个方向。</p>
<p>例：<span displaypfx="inline-" class="mathjax-container">\(z=1+i\)</span> 的模是 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{2}\)</span>，辐角是 <span displaypfx="inline-" class="mathjax-container">\(\pi/4\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(z=\sqrt{2}\,e^{i\pi/4}\)</span>。</p>
<p>棣莫弗公式（De Moivre's Formula）给出幂运算的快捷形式：</p>
<span displaypfx="" class="mathjax-container">\[(\cos\theta+i\sin\theta)^n=\cos(n\theta)+i\sin(n\theta)\]</span>
<div class="blog_h3"><span class="graybg">共轭与模</span></div>
<p>复数 <span displaypfx="inline-" class="mathjax-container">\(z = a + bi\)</span> 的模（Modulus）定义为 <span displaypfx="inline-" class="mathjax-container">\(|z|=\sqrt{a^2+b^2}\)</span>，表示复平面（Complex Plane）中点 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 到原点的欧氏距离（Euclidean Distance）。</p>
<p>共轭（Conjugate）记作 <span displaypfx="inline-" class="mathjax-container">\(\bar z = a-bi\)</span>。几何上，它把点 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 关于实轴（Real Axis）镜像到 <span displaypfx="inline-" class="mathjax-container">\((a,-b)\)</span>；数值上，它把“相位（Phase）”取反而保持“幅值（Magnitude）”不变。</p>
<p>共轭与模的核心关系是 <span displaypfx="inline-" class="mathjax-container">\(z\bar z = |z|^2\)</span>，展开即可验证：</p>
<span displaypfx="" class="mathjax-container">\[(a+bi)(a-bi)=a^2+b^2=|z|^2\]</span>
<p>这个恒等式的一个直接用途是复数除法：为了避免分母含有虚部，把分母乘以共轭进行“有理化（Rationalization）”。</p>
<span displaypfx="" class="mathjax-container">\[\frac{a+bi}{c+di}=\frac{(a+bi)(c-di)}{(c+di)(c-di)}=\frac{(a+bi)(c-di)}{c^2+d^2}\]</span>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(\frac{1+2i}{3-4i}=\frac{(1+2i)(3+4i)}{3^2+4^2}=\frac{-5+10i}{25}=-\frac{1}{5}+\frac{2}{5}i\)</span>。这里分母变成实数，是因为 <span displaypfx="inline-" class="mathjax-container">\((3-4i)(3+4i)=3^2+4^2\)</span>。</p>
<div class="blog_h3"><span class="graybg">复数乘法与旋转</span></div>
<p>把复数写成极坐标（Polar Form）：<span displaypfx="inline-" class="mathjax-container">\(z=r(\cos\theta+i\sin\theta)=re^{i\theta}\)</span>。此时复数乘法的几何意义非常直接：</p>
<span displaypfx="" class="mathjax-container">\[z_1 z_2 = (r_1 e^{i\theta_1})(r_2 e^{i\theta_2}) = (r_1 r_2)e^{i(\theta_1+\theta_2)}\]</span>
<p>也就是说：<span style="background-color: #c0c0c0;">模相乘、辐角相加</span>。乘法同时完成缩放（Scaling）与旋转（Rotation）。</p>
<p>例：乘以 <span displaypfx="inline-" class="mathjax-container">\(i=e^{i\pi/2}\)</span> 会把任意复数逆时针旋转 90° 且不改变模；乘以 <span displaypfx="inline-" class="mathjax-container">\(-1=e^{i\pi}\)</span> 会旋转 180°。例如 <span displaypfx="inline-" class="mathjax-container">\((1+2i)\cdot i=-2+i\)</span>，几何上就是把点 <span displaypfx="inline-" class="mathjax-container">\((1,2)\)</span> 旋到 <span displaypfx="inline-" class="mathjax-container">\((-2,1)\)</span>。</p>
<div class="blog_h2"><span class="graybg">数列与级数</span></div>
<p>求和符号（Summation Symbol）<span displaypfx="inline-" class="mathjax-container">\(\sum\)</span> 与乘积符号（Product Symbol）<span displaypfx="inline-" class="mathjax-container">\(\prod\)</span> 是数列与级数推导里最常见的两个“聚合”记号：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{i=1}^{n} a_i=a_1+a_2+\cdots+a_n,\quad \prod_{i=1}^{n} a_i=a_1\cdot a_2\cdots a_n\]</span>
<p>英文里通常把 <span displaypfx="inline-" class="mathjax-container">\(\sum\)</span> 读作 “sigma” 或 “summation”，把 <span displaypfx="inline-" class="mathjax-container">\(\prod\)</span> 读作 “capital pi” 或 “product”。</p>
<div class="blog_h3"><span class="graybg">等差数列 / 等比数列</span></div>
<p>数列（Sequence）是一列按整数下标排列的数 <span displaypfx="inline-" class="mathjax-container">\(\{a_n\}_{n\ge 1}\)</span>。最常见的两类是等差数列（Arithmetic Sequence）与等比数列（Geometric Sequence）。它们都可用“递推定义 + 通项公式 + 前 n 项和”三件套描述。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类型</td>
<td style="text-align: center;">递推定义</td>
<td style="text-align: center;">通项（<span displaypfx="inline-" class="mathjax-container">\(a_n\)</span>）</td>
<td style="width: 35%; text-align: center;">前 n 项和（<span displaypfx="inline-" class="mathjax-container">\(S_n=\sum_{k=1}^{n} a_k\)</span>）</td>
</tr>
</thead>
<tbody>
<tr>
<td>等差数列（Arithmetic）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a_{n}=a_{n-1}+d\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a_n=a_1+(n-1)d\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(S_n=\frac{n}{2}(a_1+a_n)=\frac{n}{2}\left(2a_1+(n-1)d\right)\)</span></td>
</tr>
<tr>
<td>等比数列（Geometric）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(a_{n}=ra_{n-1}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a_n=a_1 r^{n-1}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(S_n=\begin{cases}\frac{a_1(1-r^n)}{1-r},&amp; r\ne 1 \\ na_1,&amp; r=1\end{cases}\)</span></td>
</tr>
</tbody>
</table>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(a_1=1,d=2\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(a_n=2n-1\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(S_n=n^2\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(a_1=1,r=\frac{1}{2}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(S_n=2\left(1-2^{-n}\right)\)</span>，并随 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 增大趋近于 2。</p>
<div class="blog_h3"><span class="graybg">无穷级数与收敛</span></div>
<p>无穷级数（Infinite Series）<span displaypfx="inline-" class="mathjax-container">\(\sum_{n=1}^{\infty} a_n\)</span> 的核心对象是部分和（Partial Sum）序列 <span displaypfx="inline-" class="mathjax-container">\(S_N=\sum_{n=1}^{N} a_n\)</span>。若极限 <span displaypfx="inline-" class="mathjax-container">\(\lim_{N\to\infty} S_N\)</span> 存在且为有限值，则级数收敛（Convergence）；否则发散（Divergence）。</p>
<p>必要条件：若 <span displaypfx="inline-" class="mathjax-container">\(\sum_{n=1}^{\infty} a_n\)</span> 收敛，则必有 <span displaypfx="inline-" class="mathjax-container">\(\lim_{n\to\infty} a_n=0\)</span>。反之不成立（例如调和级数 <span displaypfx="inline-" class="mathjax-container">\(\sum 1/n\)</span> 发散）。</p>
<p>绝对收敛（Absolute Convergence）指 <span displaypfx="inline-" class="mathjax-container">\(\sum |a_n|\)</span> 收敛；绝对收敛必推出原级数收敛。仅 <span displaypfx="inline-" class="mathjax-container">\(\sum a_n\)</span> 收敛但 <span displaypfx="inline-" class="mathjax-container">\(\sum |a_n|\)</span> 发散则为条件收敛（Conditional Convergence），此时项的重排可能改变和（甚至导致发散）。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/infinite-series.jpg"><img class="alignnone size-full wp-image-40597" src="https://blog.gmem.cc/wp-content/uploads/2026/03/infinite-series.jpg" alt="infinite-series" width="100%" /></a></p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">判别法</td>
<td style="text-align: center;">条件/计算量</td>
<td style="text-align: center;">结论</td>
</tr>
</thead>
<tbody>
<tr>
<td>几何级数（Geometric Series）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sum_{n=0}^{\infty} ar^n\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(|r|&lt;1\)</span> 时收敛，且和为 <span displaypfx="inline-" class="mathjax-container">\(\frac{a}{1-r}\)</span>；<span displaypfx="inline-" class="mathjax-container">\(|r|\ge 1\)</span> 时发散</td>
</tr>
<tr>
<td>p-级数（p-series）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sum_{n=1}^{\infty}\frac{1}{n^p}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(p&gt;1\)</span> 收敛；<span displaypfx="inline-" class="mathjax-container">\(p\le 1\)</span> 发散（调和级数为 <span displaypfx="inline-" class="mathjax-container">\(p=1\)</span>）</td>
</tr>
<tr>
<td>比较判别（Comparison）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\le a_n\le b_n\)</span>（充分大时）</td>
<td>若 <span displaypfx="inline-" class="mathjax-container">\(\sum b_n\)</span> 收敛，则 <span displaypfx="inline-" class="mathjax-container">\(\sum a_n\)</span> 收敛；若 <span displaypfx="inline-" class="mathjax-container">\(\sum a_n\)</span> 发散，则 <span displaypfx="inline-" class="mathjax-container">\(\sum b_n\)</span> 发散</td>
</tr>
<tr>
<td>比值判别（Ratio Test）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(L=\limsup_{n\to\infty}\left|\frac{a_{n+1}}{a_n}\right|\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(L&lt;1\)</span> 绝对收敛；<span displaypfx="inline-" class="mathjax-container">\(L&gt;1\)</span>（或无穷大）发散；<span displaypfx="inline-" class="mathjax-container">\(L=1\)</span> 不定</td>
</tr>
<tr>
<td>根值判别（Root Test）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\rho=\limsup_{n\to\infty}\sqrt[n]{|a_n|}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\rho&lt;1\)</span> 绝对收敛；<span displaypfx="inline-" class="mathjax-container">\(\rho&gt;1\)</span> 发散；<span displaypfx="inline-" class="mathjax-container">\(\rho=1\)</span> 不定</td>
</tr>
<tr>
<td>交错级数（Alternating Series）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sum (-1)^{n-1}b_n\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(b_n\downarrow 0\)</span></td>
<td>收敛；截断误差满足 <span displaypfx="inline-" class="mathjax-container">\(|S-S_N|\le b_{N+1}\)</span></td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">调和级数与调和数（Harmonic Series / Harmonic Numbers）</span></div>
<p>调和级数（Harmonic Series）是 <span displaypfx="inline-" class="mathjax-container">\(\sum_{n=1}^{\infty}\frac{1}{n}\)</span>。它是最经典的“项趋于 0 但级数仍发散”的例子：虽然 <span displaypfx="inline-" class="mathjax-container">\(\lim_{n\to\infty}\frac{1}{n}=0\)</span>，但部分和会无界增长。</p>
<p>其部分和称为调和数（Harmonic Number）：</p>
<span displaypfx="" class="mathjax-container">\[H_n=\sum_{k=1}^{n}\frac{1}{k}\]</span>
<p>调和数的渐近行为与对数紧密相关：<span displaypfx="inline-" class="mathjax-container">\(H_n=\log n+\gamma+o(1)\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 是欧拉-马歇罗尼常数（Euler–Mascheroni constant）。因此很多“累积量随步数缓慢增长”的分析最终都会出现 <span displaypfx="inline-" class="mathjax-container">\(\log n\)</span>。</p>
<p>在 AI/优化里，调和级数最常见的用途是解释与验证学习率（Learning Rate）衰减的收敛条件。经典随机逼近（Stochastic Approximation）的一个常用充分条件是：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{t=1}^{\infty}\eta_t=\infty,\quad \sum_{t=1}^{\infty}\eta_t^2&lt;\infty\]</span>
<p>取 <span displaypfx="inline-" class="mathjax-container">\(\eta_t=\frac{1}{t}\)</span> 时，第一项对应调和级数发散（保证“走得足够远”），第二项对应 <span displaypfx="inline-" class="mathjax-container">\(\sum 1/t^2\)</span> 收敛（保证噪声的累计影响有限）。很多 SGD（Stochastic Gradient Descent）及在线学习（Online Learning）的理论推导，会用这组“一个发散、一个收敛”的对比来控制偏差与方差项。</p>
<div class="blog_h3"><span class="graybg">Taylor 展开</span></div>
<p>Taylor 展开（Taylor Expansion）用多项式在局部逼近光滑函数。Taylor 定理（Taylor's Theorem）强调“有限阶近似 + 余项（Remainder）”，Taylor 级数（Taylor Series）强调“无限项级数在收敛时等于原函数”。</p>
<span displaypfx="" class="mathjax-container">\[f(x)=\sum_{k=0}^{n}\frac{f^{(k)}(a)}{k!}(x-a)^k+R_{n+1}(x)\]</span>
<p>当余项在 <span displaypfx="inline-" class="mathjax-container">\(n\to\infty\)</span> 时收敛到 0，才有 <span displaypfx="inline-" class="mathjax-container">\(f(x)=\sum_{k=0}^{\infty}\frac{f^{(k)}(a)}{k!}(x-a)^k\)</span>。并非所有光滑函数都等于其 Taylor 级数。</p>
<p>例：在 <span displaypfx="inline-" class="mathjax-container">\(a=0\)</span> 展开，<span displaypfx="inline-" class="mathjax-container">\(e^x\approx 1+x+\frac{x^2}{2}\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(|x|\)</span> 很小时，用低阶项就能得到高精度近似。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/taylor-expansion.jpg"><img class="alignnone size-full wp-image-40583" src="https://blog.gmem.cc/wp-content/uploads/2026/03/taylor-expansion.jpg" alt="taylor-expansion" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">组合数学</span></div>
<p>组合数学（Combinatorics）研究离散结构的计数、构造与存在性。它的典型问题是“有多少种可能”：当选择空间巨大时，计数结果直接决定搜索/采样的复杂度；当把计数结果归一化为概率时，就得到二项分布、超几何分布等常见模型。</p>
<div class="blog_h3"><span class="graybg">排列与组合</span></div>
<p>排列（Permutation）与组合（Combination）的区别只在一个点：<span style="background-color: #c0c0c0;">是否区分顺序</span>。把“先选哪些元素”与“再怎么排序”分开理解，可以避免大量记忆负担。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">对象</td>
<td style="text-align: center;">记号</td>
<td style="width: 30%; text-align: center;">公式</td>
<td style="text-align: center;">直觉</td>
</tr>
</thead>
<tbody>
<tr>
<td>阶乘（Factorial）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(n!\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(n!=n(n-1)\cdots 2\cdot 1,\ 0!=1\)</span></td>
<td>把 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个互异元素全部排列的方式数</td>
</tr>
<tr>
<td>排列（k-permutation）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(P(n,k)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(P(n,k)=\frac{n!}{(n-k)!}\)</span></td>
<td>从 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个里选 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个并排序：一步一步填位置</td>
</tr>
<tr>
<td>组合（k-combination）</td>
<td><span displaypfx="inline-" class="mathjax-container">\({n \choose k}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\({n \choose k}=\frac{n!}{k!(n-k)!}\)</span></td>
<td>从 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个里选 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个，不关心顺序：先选集合</td>
</tr>
<tr>
<td>排列与组合关系</td>
<td><span displaypfx="inline-" class="mathjax-container">\(P(n,k)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(P(n,k)={n \choose k}\,k!\)</span></td>
<td>先选 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个元素，再把它们排序</td>
</tr>
</tbody>
</table>
<p>例：从 5 个候选特征里挑 3 个做一个无序子集，有 <span displaypfx="inline-" class="mathjax-container">\({5 \choose 3}=10\)</span> 种；如果还要给这 3 个特征排“处理顺序”，则变为排列 <span displaypfx="inline-" class="mathjax-container">\(P(5,3)=5\cdot4\cdot3=60\)</span> 种。</p>
<div class="blog_h3"><span class="graybg">二项式定理</span></div>
<p>二项式定理（Binomial Theorem）描述 <span displaypfx="inline-" class="mathjax-container">\((a+b)^n\)</span> 的展开结构：</p>
<span displaypfx="" class="mathjax-container">\[(a+b)^n=\sum_{k=0}^{n}{n \choose k}a^{n-k}b^k\]</span>
<p>系数 <span displaypfx="inline-" class="mathjax-container">\({n \choose k}\)</span> 的组合解释非常直接：把 <span displaypfx="inline-" class="mathjax-container">\((a+b)^n\)</span> 看作 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个括号相乘，每一项由“从每个括号里选 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>”组成。若最终选了 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 次 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>，就需要从 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个位置里挑出这 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个位置，因此系数是 <span displaypfx="inline-" class="mathjax-container">\({n \choose k}\)</span>。</p>
<p>例：<span displaypfx="inline-" class="mathjax-container">\((x+2)^4={4 \choose 0}x^4+{4 \choose 1}x^3\cdot2+{4 \choose 2}x^2\cdot2^2+{4 \choose 3}x\cdot2^3+{4 \choose 4}2^4=x^4+8x^3+24x^2+32x+16\)</span>。</p>
<p>二项式系数还满足递推（Pascal's Rule）：<span displaypfx="inline-" class="mathjax-container">\({n \choose k}={n-1 \choose k-1}+{n-1 \choose k}\)</span>。这等价于“选与不选某个固定元素”的分类讨论。</p>
<p>与概率的连接：若独立伯努利试验（Bernoulli Trial）成功概率为 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，做 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次恰好成功 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 次的概率是 <span displaypfx="inline-" class="mathjax-container">\({n \choose k}p^k(1-p)^{n-k}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\({n \choose k}\)</span> 计数的是“成功出现在哪些轮次”。</p>
<div class="blog_h2"><span class="graybg">不等式</span></div>
<p>不等式（Inequality）是把“难算/难优化/难比较”的表达式替换为“可控的上界或下界”的工具。机器学习中的许多目标函数与泛化分析，本质上都在用不等式把复杂量压到可操作的形式（例如把非线性函数用线性或二次上界近似）。</p>
<div class="blog_h3"><span class="graybg">基本不等式</span></div>
<p>基本不等式（Basic Inequalities）常见来源有两类：一类来自非负量（例如 <span displaypfx="inline-" class="mathjax-container">\((\cdot)^2\ge 0\)</span>），另一类来自范数（Norm）与凸性（Convexity）的结构性质。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">不等式</td>
<td style="text-align: center;">形式</td>
<td style="text-align: center;">典型用途</td>
</tr>
</thead>
<tbody>
<tr>
<td>平方非负</td>
<td><span displaypfx="inline-" class="mathjax-container">\((a-b)^2\ge 0\Rightarrow a^2+b^2\ge 2ab\)</span></td>
<td>把乘积项 <span displaypfx="inline-" class="mathjax-container">\(ab\)</span> 转成平方项，便于求界或优化（常见于“配方”）</td>
</tr>
<tr>
<td>三角不等式（Triangle Inequality）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(|x+y|\le |x|+|y|\)</span></td>
<td>把“相加后的绝对值”上界成“绝对值之和”；用于误差传播与残差界</td>
</tr>
<tr>
<td>均值不等式（AM-GM）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{a+b}{2}\ge \sqrt{ab}\quad (a,b\ge 0)\)</span></td>
<td>在“和固定时乘积最大”或“乘积/几何平均”相关问题里给出最紧的经典界</td>
</tr>
</tbody>
</table>
<p>例（AM-GM 的“最大乘积”直觉）：在 <span displaypfx="inline-" class="mathjax-container">\(a,b\ge 0\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(a+b=10\)</span> 的约束下，乘积满足 <span displaypfx="inline-" class="mathjax-container">\(ab\le \left(\frac{a+b}{2}\right)^2=25\)</span>，并且当且仅当 <span displaypfx="inline-" class="mathjax-container">\(a=b=5\)</span> 取等号。</p>
<div class="blog_h3"><span class="graybg">Jensen 不等式</span></div>
<p>Jensen 不等式（Jensen's Inequality）回答一个很具体的问题：<span style="background-color: #c0c0c0;">“平均”与“非线性”交换顺序会发生什么</span>。它是把抽象的凸性（Convexity）变成可用结论的最常用工具之一。</p>
<p>先把术语说清楚：</p>
<ul>
<li>加权平均（Weighted Average）：给一组数 <span displaypfx="inline-" class="mathjax-container">\(x_1,\dots,x_n\)</span> 分配权重 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\ge 0\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(\sum_i\lambda_i=1\)</span>，则加权平均是 <span displaypfx="inline-" class="mathjax-container">\(\sum_i\lambda_i x_i\)</span>（把 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span> 视为“占比”即可）。</li>
<li>凸函数（Convex Function）：函数 <span displaypfx="inline-" class="mathjax-container">\(\varphi\)</span> 若满足对任意 <span displaypfx="inline-" class="mathjax-container">\(x_1,x_2\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\lambda\in[0,1]\)</span> 都有 <span displaypfx="inline-" class="mathjax-container">\(\varphi(\lambda x_1+(1-\lambda)x_2)\le \lambda\varphi(x_1)+(1-\lambda)\varphi(x_2)\)</span>，则称 <span displaypfx="inline-" class="mathjax-container">\(\varphi\)</span> 是凸的。直观上：它“向上弯”，因此对它做平均会被“惩罚”。</li>
<li>随机变量（Random Variable）：一个在不同试验/样本上会取不同值的量；把 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 视为“每次取到的数”。</li>
<li>期望（Expectation）<span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[\cdot]\)</span>：可把它理解为“按概率加权的平均”。</li>
</ul>
<p>Jensen 的形式（离散加权平均）：若 <span displaypfx="inline-" class="mathjax-container">\(\varphi\)</span> 凸，则</p>
<span displaypfx="" class="mathjax-container">\[\varphi\!\left(\sum_i \lambda_i x_i\right)\le \sum_i \lambda_i \varphi(x_i)\]</span>
<p>等价的期望形式：对随机变量 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>（且 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[\varphi(X)]\)</span> 存在），有</p>
<span displaypfx="" class="mathjax-container">\[\varphi(\mathbb{E}[X])\le \mathbb{E}[\varphi(X)]\]</span>
<p>如果 <span displaypfx="inline-" class="mathjax-container">\(\varphi\)</span> 是凹函数（Concave Function），不等号方向会反过来。</p>
<p>场景 1：凸惩罚下，“波动”本身有代价。设某个系统的代价函数是 <span displaypfx="inline-" class="mathjax-container">\(\varphi(t)=t^2\)</span>（二次惩罚，Convex Penalty），比较两种延迟（Latency）：</p>
<ul>
<li>稳定方案：每次都是 100ms，则平均代价是 <span displaypfx="inline-" class="mathjax-container">\(100^2=10000\)</span>。</li>
<li>波动方案：一半时间 50ms、一半时间 150ms（平均同样是 100ms），则平均代价是 <span displaypfx="inline-" class="mathjax-container">\(\frac{50^2+150^2}{2}=12500\)</span>。</li>
</ul>
<p>两者平均延迟一样，但二次代价更偏好稳定方案；这就是 Jensen 在 <span displaypfx="inline-" class="mathjax-container">\(\varphi(t)=t^2\)</span> 下的直接体现：<span displaypfx="inline-" class="mathjax-container">\(\left(\frac{50+150}{2}\right)^2\le \frac{50^2+150^2}{2}\)</span>。</p>
<p>场景 2：对数损失下，“偶尔很错”会被放大。分类里常用对数损失（Log Loss）<span displaypfx="inline-" class="mathjax-container">\(\varphi(p)=-\log p\)</span>（<span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是正确类别的预测概率），它在 <span displaypfx="inline-" class="mathjax-container">\((0,1]\)</span> 上是凸函数。假设一个模型在同一个样本上两次输出 <span displaypfx="inline-" class="mathjax-container">\(p=0.9\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(p=0.1\)</span>（一次很自信、一次几乎反过来），则</p>
<span displaypfx="" class="mathjax-container">\[-\log\!\left(\frac{0.9+0.1}{2}\right)=-\log 0.5\approx 0.693\le \frac{-\log 0.9-\log 0.1}{2}\approx 1.204\]</span>
<p>含义：在凸损失下，预测的波动会提高平均损失；这也是许多“用不等式构造上界/下界”方法（例如把难优化的期望目标变成可算的界）背后的数学原因。</p>
<p>何时取等号：当所有 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 相等（没有波动），或 <span displaypfx="inline-" class="mathjax-container">\(\varphi\)</span> 在相关区间上近似线性时，不等式可取等号。</p>
<div class="blog_h3"><span class="graybg">Cauchy-Schwarz 不等式</span></div>
<p>Cauchy-Schwarz 不等式（Cauchy–Schwarz Inequality）回答另一个非常基础的问题：<span style="background-color: #c0c0c0;">两组数“对齐相乘再求和”的结果，最多能有多大</span>。它是内积（Inner Product）与范数（Norm）体系的核心约束。</p>
<p>把术语说清楚后，这个不等式就不神秘：</p>
<ul>
<li>向量（Vector）：把一组数按顺序排成列表，例如 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}=(a_1,\dots,a_n)\)</span>。</li>
<li>点积/内积（Dot Product / Inner Product）：<span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}^\top\mathbf{b}=\sum_i a_i b_i\)</span>，可理解为“两组数在同一位置上的重叠程度”。</li>
<li>L2 范数（<span displaypfx="inline-" class="mathjax-container">\(\ell_2\)</span> Norm）：<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|_2=\sqrt{\sum_i a_i^2}\)</span>，几何上是向量长度（Length）。</li>
</ul>
<p>不等式本身是：</p>
<span displaypfx="" class="mathjax-container">\[|\mathbf{a}^\top\mathbf{b}|\le \|\mathbf{a}\|_2\,\|\mathbf{b}\|_2\]</span>
<p>几何解释：把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}^\top\mathbf{b}\)</span> 写成 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|_2\|\mathbf{b}\|_2\cos\theta\)</span>，Cauchy-Schwarz 等价于 <span displaypfx="inline-" class="mathjax-container">\(|\cos\theta|\le 1\)</span>。当且仅当两向量同向或反向（线性相关（Linearly Dependent））时取等号。</p>
<p>场景 1：为什么检索里常用“余弦相似度（Cosine Similarity）”。在向量检索中，常用点积 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}^\top\mathbf{d}\)</span> 衡量查询向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}\)</span> 与文档向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{d}\)</span> 的相似度。但点积同时受“方向”和“长度”影响：如果某个向量范数很大，即使方向一般，点积也可能很大。</p>
<p>一个最小例子：令查询 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}=(1,1)\)</span>，两篇候选文档向量为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{d}_1=(100,0)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{d}_2=(2,2)\)</span>。点积分别是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}^\top\mathbf{d}_1=100\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}^\top\mathbf{d}_2=4\)</span>，点积会更偏向 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{d}_1\)</span>。但如果把向量归一化到单位范数（Unit Norm）再比较，则</p>
<span displaypfx="" class="mathjax-container">\[\cos(\mathbf{q},\mathbf{d}_1)=\frac{\mathbf{q}^\top\mathbf{d}_1}{\|\mathbf{q}\|_2\|\mathbf{d}_1\|_2}=\frac{1}{\sqrt{2}},\quad \cos(\mathbf{q},\mathbf{d}_2)=\frac{\mathbf{q}^\top\mathbf{d}_2}{\|\mathbf{q}\|_2\|\mathbf{d}_2\|_2}=1\]</span>
<p>归一化后 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{d}_2\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{q}\)</span> 方向完全一致，更符合“语义对齐”的直觉。Cauchy-Schwarz 保证余弦相似度一定落在 [-1,1]，从而成为一个尺度稳定、可解释的相似度。</p>
<p>场景 2：为什么相关系数（Correlation Coefficient）不可能超过 1。把两组已中心化（Centered，均值为 0）的数据序列看成向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x},\mathbf{y}\)</span>，它们的“协方差方向”可以写成点积 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top\mathbf{y}\)</span>，而各自的尺度由 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_2,\|\mathbf{y}\|_2\)</span> 给出。Cauchy-Schwarz 直接推出</p>
<span displaypfx="" class="mathjax-container">\[\left|\frac{\mathbf{x}^\top\mathbf{y}}{\|\mathbf{x}\|_2\|\mathbf{y}\|_2}\right|\le 1\]</span>
<p>这就是“线性相关强度最多 100%”的数学原因；取等号对应完全线性关系，即 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}=c\mathbf{x}\)</span>。</p>
<div class="blog_h2"><span class="graybg">集合论基础</span></div>
<p>集合论（Set Theory）是现代数学语言的底座：几乎所有“对象 + 结构”的定义都能归结为集合及其上的运算。工程语境里，把对象抽象为集合的好处是：边界清晰、可组合、可用代数规则推导。</p>
<div class="blog_h3"><span class="graybg">集合运算（并 / 交 / 补）</span></div>
<p>集合（Set）是元素（Element）的无序聚合。记 <span displaypfx="inline-" class="mathjax-container">\(x\in A\)</span> 表示 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 属于集合 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，记 <span displaypfx="inline-" class="mathjax-container">\(A\subseteq B\)</span> 表示子集（Subset）。常用的运算如下。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">运算</td>
<td style="text-align: center; width: 150px;">记号</td>
<td style="text-align: center;">定义</td>
<td style="text-align: center;">例子</td>
</tr>
</thead>
<tbody>
<tr>
<td>并（Union）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(A\cup B\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{x\mid x\in A\ \text{或}\ x\in B\}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{1,2,3\}\cup\{3,4\}=\{1,2,3,4\}\)</span></td>
</tr>
<tr>
<td>交（Intersection）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(A\cap B\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{x\mid x\in A\ \text{且}\ x\in B\}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{1,2,3\}\cap\{3,4\}=\{3\}\)</span></td>
</tr>
<tr>
<td>差（Difference）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(A\setminus B\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{x\mid x\in A,\ x\notin B\}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\{1,2,3\}\setminus\{3,4\}=\{1,2\}\)</span></td>
</tr>
<tr>
<td>补（Complement）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(A^c\)</span></td>
<td>相对于全集 <span displaypfx="inline-" class="mathjax-container">\(U\)</span>： <span displaypfx="inline-" class="mathjax-container">\(A^c=U\setminus A\)</span></td>
<td>若 <span displaypfx="inline-" class="mathjax-container">\(U=\{1,2,3,4\}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\{1,2,3\}^c=\{4\}\)</span></td>
</tr>
</tbody>
</table>
<p>De Morgan 律（De Morgan's Laws）是“补运算”与“并/交”之间的互换规则：</p>
<span displaypfx="" class="mathjax-container">\[(A\cup B)^c=A^c\cap B^c,\quad (A\cap B)^c=A^c\cup B^c\]</span>
<p>计数场景常用容斥原理（Inclusion–Exclusion）：<span displaypfx="inline-" class="mathjax-container">\(|A\cup B|=|A|+|B|-|A\cap B|\)</span>。例：一个数据集里“命中规则 A”的样本有 120 个，“命中规则 B”的有 80 个，同时命中的有 30 个，则命中至少一个规则的样本数是 <span displaypfx="inline-" class="mathjax-container">\(120+80-30=170\)</span>。</p>
<div class="blog_h3"><span class="graybg">映射与关系</span></div>
<p>映射（Mapping / Function）用来描述“从输入到输出”的确定性规则。写作 <span displaypfx="inline-" class="mathjax-container">\(f:A\to B\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 是定义域（Domain），<span displaypfx="inline-" class="mathjax-container">\(B\)</span> 是陪域（Codomain）。对 <span displaypfx="inline-" class="mathjax-container">\(x\in A\)</span>，输出写作 <span displaypfx="inline-" class="mathjax-container">\(f(x)\in B\)</span>。像集（Image / Range）为 <span displaypfx="inline-" class="mathjax-container">\(f(A)=\{f(x)\mid x\in A\}\)</span>。</p>
<p>关系（Relation）是更一般的概念：它不要求“每个输入对应唯一输出”。在集合论里，一个二元关系 <span displaypfx="inline-" class="mathjax-container">\(R\)</span> 可以看成笛卡尔积（Cartesian Product）上的子集：</p>
<span displaypfx="" class="mathjax-container">\[R\subseteq A\times B,\quad (a,b)\in R\ \text{表示}\ a\ R\ b\]</span>
<p>典型例子：</p>
<ul>
<li>等价关系（Equivalence Relation）：满足自反、对称、传递。例如定义 <span displaypfx="inline-" class="mathjax-container">\(x\sim y\Leftrightarrow x-y\)</span> 能被 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 整除（同余），它把整数划分为若干等价类（Equivalence Class）。</li>
<li>偏序（Partial Order）：满足自反、反对称、传递。例如 <span displaypfx="inline-" class="mathjax-container">\(\le\)</span> 是实数上的偏序；集合的包含关系 <span displaypfx="inline-" class="mathjax-container">\(\subseteq\)</span> 也是偏序。</li>
<li>一般关系：例如“相似度大于阈值”定义了一个关系，但它通常不具备传递性，因此不是等价关系。</li>
</ul>
<p>把关系写成矩阵/邻接矩阵（Adjacency Matrix）是常用表示：若 <span displaypfx="inline-" class="mathjax-container">\(A=B=\{1,\ldots,n\}\)</span>，定义 <span displaypfx="inline-" class="mathjax-container">\(M_{ij}=1\)</span> 当且仅当 <span displaypfx="inline-" class="mathjax-container">\((i,j)\in R\)</span>。在图论（Graph Theory）与推荐/检索（Retrieval）里，这种表示会直接进入线性代数计算。</p>
<div class="blog_h1"><span class="graybg">线性代数</span></div>
<div class="blog_h2"><span class="graybg">向量</span></div>
<div class="blog_h3"><span class="graybg">向量加减法（Vector Addition / Subtraction）</span></div>
<p>在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^n\)</span> 中，向量加法与减法按分量（Component-wise）进行。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}=(a_1,\ldots,a_n)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(b_1,\ldots,b_n)\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{a}+\mathbf{b}=(a_1+b_1,\ldots,a_n+b_n),\quad \mathbf{a}-\mathbf{b}=(a_1-b_1,\ldots,a_n-b_n)\]</span>
<p>几何上，加法对应向量合成；减法 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}-\mathbf{b}\)</span> 是从 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 指向 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span> 的位移向量。</p>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}=(1,2)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(3,-1)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}+\mathbf{b}=(4,1)\)</span>，<span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}-\mathbf{b}=(-2,3)\)</span>。</p>
<p>在 AI 里，残差连接（Residual Connection）是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}+f(\mathbf{x})\)</span>；梯度下降（Gradient Descent）的更新可写成 <span displaypfx="inline-" class="mathjax-container">\(\theta\leftarrow\theta-\eta\nabla L(\theta)\)</span>。它们都直接依赖向量加减法。</p>
<div class="blog_h3"><span class="graybg">点积（Dot Product）</span></div>
<p>点积（Dot Product）把两个向量映射为标量（Scalar），常用于相似度、投影和方向一致性判断。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a},\mathbf{b}\in\mathbb{R}^n\)</span>，定义为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=\sum_{i=1}^n a_i b_i\)</span>。当 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a},\mathbf{b}\ne\mathbf{0}\)</span> 时，也可写为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=\|\mathbf{a}\|\|\mathbf{b}\|\cos\theta\)</span>。</p>
<p>点积满足交换律（Commutativity）：<span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=\mathbf{b}\cdot\mathbf{a}\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=0\)</span>，两向量正交（Orthogonal）。</p>
<p>向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 方向上的标量投影是 <span displaypfx="inline-" class="mathjax-container">\(\frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{b}\|}\)</span>。投影更常用的是向量形式（Vector Projection）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{proj}_{\mathbf{b}}(\mathbf{a})=\frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{b}\|^2}\mathbf{b}\]</span>
<p>当把向量归一化到单位范数（Unit Norm）后，点积就等于余弦相似度（Cosine Similarity）：<span displaypfx="inline-" class="mathjax-container">\(\cos\theta=\mathbf{\hat a}\cdot\mathbf{\hat b}\)</span>，这也是检索与表示学习中常见的相似度度量。</p>
<p>单位向量（Unit Vector）是范数为 1 的向量，常用来“只表示方向”。对非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span>，其单位向量是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{\hat a}=\frac{\mathbf{a}}{\|\mathbf{a}\|}\)</span>。</p>
<p>方向角（Direction Angles）/方向余弦（Direction Cosines）描述单位向量与各坐标轴的夹角：在三维中，若单位向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}=(u_x,u_y,u_z)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(u_x=\cos\alpha\)</span>、<span displaypfx="inline-" class="mathjax-container">\(u_y=\cos\beta\)</span>、<span displaypfx="inline-" class="mathjax-container">\(u_z=\cos\gamma\)</span>，并满足 <span displaypfx="inline-" class="mathjax-container">\(\cos^2\alpha+\cos^2\beta+\cos^2\gamma=1\)</span>。</p>
<p>点积之所以会出现“乘积”，是因为它等于“被投影长度 × 参照向量长度”： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=\|\mathbf{b}\|\cdot \mathrm{projLen}_{\mathbf{b}}(\mathbf{a})\)</span>。当两向量同向时，投影长度就是 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|\)</span>，于是点积变为 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|\|\mathbf{b}\|\)</span>。</p>
<div class="blog_h4"><span class="graybg">二维向量的复数表示</span></div>
<p>在二维空间里，向量 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 可以写成复数 <span displaypfx="inline-" class="mathjax-container">\(z=a+bi\)</span>。这不是把向量“变成另一种对象”，而是给同一个二维量换一种记法：实部对应 x 轴分量，虚部对应 y 轴分量。</p>
<p>若另一向量 <span displaypfx="inline-" class="mathjax-container">\((c,d)\)</span> 写成 <span displaypfx="inline-" class="mathjax-container">\(w=c+di\)</span>，则复共轭乘积为</p>
<span displaypfx="" class="mathjax-container">\[z\bar w=(a+bi)(c-di)=(ac+bd)+i(bc-ad)\]</span>
<p>其中实部 <span displaypfx="inline-" class="mathjax-container">\(ac+bd\)</span> 恰好就是二维向量的点积：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{a}\cdot\mathbf{b}=ac+bd=\mathrm{Re}(z\bar w)\]</span>
<p>若再把它们写成极坐标形式 <span displaypfx="inline-" class="mathjax-container">\(z=r_1e^{i\theta_1}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(w=r_2e^{i\theta_2}\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{a}\cdot\mathbf{b}=\mathrm{Re}(z\bar w)=r_1r_2\cos(\theta_1-\theta_2)\]</span>
<p>这里实部的来源可以直接从指数形式读出：因为 <span displaypfx="inline-" class="mathjax-container">\(\bar w=r_2e^{-i\theta_2}\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(z\bar w=r_1r_2e^{i(\theta_1-\theta_2)}\)</span>。再用欧拉公式 <span displaypfx="inline-" class="mathjax-container">\(e^{i\phi}=\cos\phi+i\sin\phi\)</span> 展开，就得到 <span displaypfx="inline-" class="mathjax-container">\(z\bar w=r_1r_2\big(\cos(\theta_1-\theta_2)+i\sin(\theta_1-\theta_2)\big)\)</span>；其中实部正是 <span displaypfx="inline-" class="mathjax-container">\(r_1r_2\cos(\theta_1-\theta_2)\)</span>。</p>
<p>因此，点积既可以看成分量乘加，也可以看成“复数乘积取实部”。后一种写法把<span style="background-color: #c0c0c0;">长度</span>与<span style="background-color: #c0c0c0;">相位差</span>放进同一个式子里，在位置编码、旋转表示与频域分析中尤其方便。后文 RoPE 的复数视角正是沿用这层关系：二维块先写成复数，再让相位随位置旋转。</p>
<p>例：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}=(3,4)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(4,0)\)</span>。则 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\cdot\mathbf{b}=12\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|=5\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{b}\|=4\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(\cos\theta=\frac{12}{20}=0.6\)</span>。而 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 方向上的标量投影是 <span displaypfx="inline-" class="mathjax-container">\(\frac{12}{4}=3\)</span>，恰好对应 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span> 的 x 分量。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/vector-ops.jpg"><img class="alignnone size-full wp-image-40599" src="https://blog.gmem.cc/wp-content/uploads/2026/03/vector-ops.jpg" alt="vector-ops" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">叉积（Cross Product）</span></div>
<p>叉积（Cross Product）定义在三维空间（3D Space）。结果是同时垂直于两个输入向量的向量，大小为 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\times\mathbf{b}\|=\|\mathbf{a}\|\|\mathbf{b}\|\sin\theta\)</span>，等于两向量张成平行四边形的面积。</p>
<p>计算上，若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}=(a_1,a_2,a_3)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(b_1,b_2,b_3)\)</span>，则：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{a}\times\mathbf{b}=(a_2b_3-a_3b_2,\ a_3b_1-a_1b_3,\ a_1b_2-a_2b_1)\]</span>
<p>例： <span displaypfx="inline-" class="mathjax-container">\((1,0,0)\times(0,1,0)=(0,0,1)\)</span>。这个例子在几何上对应两个单位正交基向量张成的“正方形面积为 1”，方向由右手定则给出。</p>
<p>叉积为零当且仅当两向量平行（Parallel/Colinear）或至少有一个为零向量：这是因为 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\times\mathbf{b}\|=\|\mathbf{a}\|\|\mathbf{b}\|\sin\theta\)</span>，为 0 只能来自 <span displaypfx="inline-" class="mathjax-container">\(\sin\theta=0\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{a}\|=0\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{b}\|=0\)</span>。</p>
<p>方向由右手定则（Right-Hand Rule）确定：四指从 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\)</span> 旋向 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span>，拇指方向即 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\times\mathbf{b}\)</span> 方向。旋转“正负”不是绝对物理事实，而是由坐标系（Coordinate System）与观察方向（View Direction）约定决定。</p>
<p>力矩（Torque）可作为叉积方向的直观例子： <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\tau}=\mathbf{r}\times\mathbf{F}\)</span>。这里保留物理解释的唯一目的，是帮助理解叉积的方向性。</p>
<div class="blog_h2"><span class="graybg">基（Basis）</span></div>
<p>在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^n\)</span> 中，一组向量 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{b}_1,\ldots,\mathbf{b}_n\}\)</span> 若线性无关（Linearly Independent）且张成（Span）整个空间，则称为一组基（Basis）。任何向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 都能唯一表示为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=\sum_{i=1}^{n} c_i\mathbf{b}_i\)</span>；系数向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}=(c_1,\ldots,c_n)^\top\)</span> 就是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 在该基下的坐标（Coordinates）。</p>
<p>把基向量按列堆成矩阵 <span displaypfx="inline-" class="mathjax-container">\(B=[\mathbf{b}_1\ \cdots\ \mathbf{b}_n]\in\mathbb{R}^{n\times n}\)</span>，则坐标与原向量满足 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=B\mathbf{c}\)</span>。换基（Change of Basis）在推导里本质就是在不同 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 之间切换坐标表示。</p>
<div class="blog_h3"><span class="graybg">换基（Change of Basis）：同一个向量，不同坐标</span></div>
<p>换基（Change of Basis）指的是：在不改变几何向量本身的前提下，改用另一组基（Basis）来描述它的坐标（Coordinates）。因此，换基改变的是<span style="background-color: #c0c0c0;">表示方式</span>，不是向量对象本身。直观上，可把几何向量理解为平面/空间里的一支箭头。</p>
<p>这也是为什么“换基”不能理解成“把向量变形”。真正发生变化的是参考基（Basis），因此同一向量在不同基下的坐标数值会不同，而几何对象本身保持不变。</p>
<p>为了不混淆“向量本身”和“向量的坐标表示”，可以用一个约定把它们分开：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span>：把同一个几何向量用标准基（Standard Basis）写出来的分量列向量（数值计算里最常用的表示）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}\)</span>：把同一个几何向量用某组新基 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{b}_i\}\)</span> 写出来的坐标向量（Coordinate Vector），也就是“在新基上要乘的系数”。</li>
</ul>
<p>把新基向量在标准基下的分量按列组成 <span displaypfx="inline-" class="mathjax-container">\(B=[\mathbf{b}_1\ \cdots\ \mathbf{b}_n]\)</span>（可称为基矩阵（Basis Matrix）），则</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}=B\mathbf{c}\]</span>
<p>读法：右边 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}\)</span> 是“在新基下的坐标”，左边 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 是“在标准基下的分量”。乘上 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 就把“新基坐标”换算回“标准基分量”。</p>
<p>反过来，如果你已知标准基下的分量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span>，想求新基坐标 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}\)</span>，就需要解线性方程组 <span displaypfx="inline-" class="mathjax-container">\(B\mathbf{c}=\mathbf{x}\)</span>。由于基向量线性无关，矩阵 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 必可逆（Invertible），因此可写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{c}=B^{-1}\mathbf{x}\]</span>
<p>更一般地，若旧基矩阵为 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>、新基矩阵为 <span displaypfx="inline-" class="mathjax-container">\(C\)</span>，同一几何向量满足 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=B\mathbf{c}_{B}=C\mathbf{c}_{C}\)</span>，于是坐标之间的换算是：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{c}_{C}=C^{-1}B\,\mathbf{c}_{B}\]</span>
<p>矩阵 <span displaypfx="inline-" class="mathjax-container">\(P=C^{-1}B\)</span> 常被称为换基矩阵（Change-of-basis Matrix）：它把“旧基坐标”直接映射为“新基坐标”。</p>
<p>例：在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^2\)</span> 取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}_1=(1,0)^\top,\ \mathbf{b}_2=(1,1)^\top\)</span>。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(2,3)^\top\)</span>，解 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=c_1\mathbf{b}_1+c_2\mathbf{b}_2\)</span> 得 <span displaypfx="inline-" class="mathjax-container">\(c_2=3,\ c_1=-1\)</span>，因此该基下坐标为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}=(-1,3)^\top\)</span>。同一个几何向量，在不同基下的坐标会不同。</p>
<p>把基矩阵写出来会更便于计算： <span displaypfx="inline-" class="mathjax-container">\(B=[\mathbf{b}_1\ \mathbf{b}_2]=\begin{bmatrix}1 &amp; 1\\ 0 &amp; 1\end{bmatrix}\)</span>。此时 <span displaypfx="inline-" class="mathjax-container">\(B\mathbf{c}=\begin{bmatrix}1 &amp; 1\\ 0 &amp; 1\end{bmatrix}\begin{bmatrix}-1\\ 3\end{bmatrix}=\begin{bmatrix}2\\ 3\end{bmatrix}=\mathbf{x}\)</span>；反过来，解 <span displaypfx="inline-" class="mathjax-container">\(B\mathbf{c}=\mathbf{x}\)</span> 等价于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}=B^{-1}\mathbf{x}\)</span>，因此“换基”在计算上就是解一个线性方程组。</p>
<div class="blog_h3"><span class="graybg">标准基（Standard Basis）</span></div>
<p>标准基（Standard Basis）是最常用的一组基。第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个标准基向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{e}_i\)</span> 只有第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量为 1，其余为 0：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{e}_1=(1,0,\ldots,0)^\top,\ \mathbf{e}_2=(0,1,0,\ldots,0)^\top,\ \ldots,\ \mathbf{e}_n=(0,\ldots,0,1)^\top\]</span>
<p>例：在 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^2\)</span> 中，<span displaypfx="inline-" class="mathjax-container">\((2,3)^\top=2\mathbf{e}_1+3\mathbf{e}_2\)</span>。把 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{e}_i\}\)</span> 作为列组成矩阵就是单位矩阵 <span displaypfx="inline-" class="mathjax-container">\(I\)</span>，因此标准基下 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=I\mathbf{x}\)</span> 对应“坐标与分量一致”。</p>
<div class="blog_h3"><span class="graybg">正交基（Orthogonal Basis）</span></div>
<p>正交基（Orthogonal Basis）是指基向量两两正交：对 <span displaypfx="inline-" class="mathjax-container">\(i\ne j\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}_i^\top\mathbf{b}_j=0\)</span>。它不要求单位长度。</p>
<p>正交基的一个关键性质是：坐标可以用投影直接算出。若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=\sum_i c_i\mathbf{b}_i\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{b}_i\}\)</span> 正交，则</p>
<span displaypfx="" class="mathjax-container">\[c_i=\frac{\mathbf{x}^\top\mathbf{b}_i}{\mathbf{b}_i^\top\mathbf{b}_i}\]</span>
<p>例：取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}_1=(1,1)^\top,\ \mathbf{b}_2=(1,-1)^\top\)</span>，它们点积为 0。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(2,1)^\top\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(c_1=\frac{3}{2},\ c_2=\frac{1}{2}\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=\frac{3}{2}\mathbf{b}_1+\frac{1}{2}\mathbf{b}_2\)</span>。</p>
<div class="blog_h3"><span class="graybg">正交标准基（Orthonormal Basis）</span></div>
<p>正交标准基（Orthonormal Basis）要求两两正交且每个基向量单位长度： <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{u}_i\|_2=1\)</span>，并满足 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}_i^\top\mathbf{u}_j=\delta_{ij}\)</span>（克罗内克 delta（Kronecker Delta））。</p>
<p>此时坐标就是内积/投影：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=\sum_i c_i\mathbf{u}_i\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(c_i=\mathbf{x}^\top\mathbf{u}_i\)</span>。计算上，这等价于把向量投影到各基方向。</p>
<p>例：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}_1=\frac{1}{\sqrt{2}}(1,1)^\top,\ \mathbf{u}_2=\frac{1}{\sqrt{2}}(1,-1)^\top\)</span>。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(2,1)^\top\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(c_1=\frac{3}{\sqrt{2}},\ c_2=\frac{1}{\sqrt{2}}\)</span>，于是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=c_1\mathbf{u}_1+c_2\mathbf{u}_2\)</span>。</p>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{u}_i\}\)</span> 按列组成矩阵 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(Q^\top Q=I\)</span>，并且坐标变换可写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}=Q^\top\mathbf{x}\)</span>、重构为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=Q\mathbf{c}\)</span>。在 PCA（Principal Component Analysis）与 SVD 中，主方向/奇异向量就是正交标准基；投影与重构只需要转置，不需要显式求逆。</p>
<div class="blog_h2"><span class="graybg">矩阵运算</span></div>
<p>矩阵运算（Matrix Operations）是机器学习（Machine Learning）中最核心的计算骨架。前向传播、反向传播和参数更新都可以表示为矩阵与向量的组合。</p>
<div class="blog_h3"><span class="graybg">加法与广播（Addition / Broadcasting）</span></div>
<p>矩阵加法按元素（Element-wise）进行：若 <span displaypfx="inline-" class="mathjax-container">\(X,B\in\mathbb{R}^{m\times n}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\((X+B)_{ij}=X_{ij}+B_{ij}\)</span>。减法同理。</p>
<p>在深度学习框架里常见的是广播（Broadcasting）：例如对 batch 特征 <span displaypfx="inline-" class="mathjax-container">\(X\in\mathbb{R}^{B\times d}\)</span> 与偏置 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\in\mathbb{R}^{d}\)</span>，写作 <span displaypfx="inline-" class="mathjax-container">\(Y=X+\mathbf{b}\)</span> 意味着把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 复制到每一行后再相加。这是线性层 <span displaypfx="inline-" class="mathjax-container">\(Y=XW+\mathbf{b}\)</span> 的标准形式。</p>
<div class="blog_h4"><span class="graybg">例：矩阵加法</span></div>
<span displaypfx="" class="mathjax-container">\[X=\begin{bmatrix}1 &amp; 2\\ 3 &amp; 4\end{bmatrix},\ B=\begin{bmatrix}10 &amp; 20\\ 30 &amp; 40\end{bmatrix}\Rightarrow X+B=\begin{bmatrix}11 &amp; 22\\ 33 &amp; 44\end{bmatrix}\]</span>
<div class="blog_h4"><span class="graybg">例：广播加偏置（Bias Broadcasting）</span></div>
<p>令 <span displaypfx="inline-" class="mathjax-container">\(X\in\mathbb{R}^{2\times 3}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\in\mathbb{R}^{3}\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[X=\begin{bmatrix}1 &amp; 2 &amp; 3\\ 4 &amp; 5 &amp; 6\end{bmatrix},\ \mathbf{b}=\begin{bmatrix}10 &amp; 20 &amp; 30\end{bmatrix}\]</span>
<p>广播的语义是“把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 复制到每一行”，因此</p>
<span displaypfx="" class="mathjax-container">\[Y=X+\mathbf{b}=\begin{bmatrix}11 &amp; 22 &amp; 33\\ 14 &amp; 25 &amp; 36\end{bmatrix}\]</span>
<p>从线性代数角度，把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 视作行向量，则广播等价于 <span displaypfx="inline-" class="mathjax-container">\(Y=X+\mathbf{1}\mathbf{b}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\in\mathbb{R}^{B\times 1}\)</span> 是全 1 列向量。</p>
<div class="blog_h3"><span class="graybg">矩阵乘法（Matrix Multiplication）</span></div>
<p>矩阵乘法（Matrix Multiplication）在神经网络里通常对应线性层（Linear Layer）：<span displaypfx="inline-" class="mathjax-container">\(Y=XW\)</span>。从“每个输出维度是一个点积”来看更容易记：</p>
<span displaypfx="" class="mathjax-container">\[y_j=\sum_{i=1}^{d_{\text{in}}} x_i W_{ij}\]</span>
<p>在批处理（Batch）场景下，常用形状约定是 <span displaypfx="inline-" class="mathjax-container">\(X\in\mathbb{R}^{B\times d_{\text{in}}}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(W\in\mathbb{R}^{d_{\text{in}}\times d_{\text{out}}}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(Y=XW\in\mathbb{R}^{B\times d_{\text{out}}}\)</span>。矩阵乘法一般不满足交换律（Non-commutativity），形状不匹配时也无法相乘。</p>
<p>从几何（Geometry）角度看，矩阵定义了一个线性变换（Linear Transformation）：它把空间中的点/向量整体“变形”（旋转、缩放、剪切、投影等）。一种实用记法是看基向量（Basis Vectors）如何被映射：如果用列向量约定，矩阵的每一列就是某个基向量变换后的像。</p>
<p>例（二维）：标准基向量（Standard Basis Vectors）为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{e}_1=(1,0)^\top\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{e}_2=(0,1)^\top\)</span>。对</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}a &amp; c\\ b &amp; d\end{bmatrix}\]</span>
<p>有</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{e}_1=\begin{bmatrix}a\\ b\end{bmatrix},\quad A\mathbf{e}_2=\begin{bmatrix}c\\ d\end{bmatrix}\]</span>
<p>因此 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的第 1/2 列分别是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{e}_1,\mathbf{e}_2\)</span> 的像。对任意 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=x_1\mathbf{e}_1+x_2\mathbf{e}_2\)</span>，根据线性变换的线性性（Linearity）<span displaypfx="inline-" class="mathjax-container">\(A(\alpha\mathbf{u}+\beta\mathbf{v})=\alpha A\mathbf{u}+\beta A\mathbf{v}\)</span>，可得</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{x}=x_1A\mathbf{e}_1+x_2A\mathbf{e}_2=x_1\begin{bmatrix}a\\ b\end{bmatrix}+x_2\begin{bmatrix}c\\ d\end{bmatrix}\]</span>
<p>数值例子：取</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}2 &amp; 1\\ 0 &amp; 1\end{bmatrix}\]</span>
<p>则 <span displaypfx="inline-" class="mathjax-container">\(A\mathbf{e}_1=(2,0)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(A\mathbf{e}_2=(1,1)^\top\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,4)^\top=3\mathbf{e}_1+4\mathbf{e}_2\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{x}=3A\mathbf{e}_1+4A\mathbf{e}_2=3\begin{bmatrix}2\\ 0\end{bmatrix}+4\begin{bmatrix}1\\ 1\end{bmatrix}=\begin{bmatrix}10\\ 4\end{bmatrix}\]</span>
<p>这就是“看列向量理解变换”的核心：先画出两条基向量被送到哪里，整个网格会按相同线性组合随之平移/剪切/旋转/缩放。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-trans.jpg"><img class="alignnone size-full wp-image-40629" src="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-trans.jpg" alt="matrix-trans" width="100%" /></a></p>
<p>这对应两种等价视角：</p>
<ul>
<li>变换向量（Active View）：固定坐标轴，让向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 变成 <span displaypfx="inline-" class="mathjax-container">\(A\mathbf{x}\)</span>。</li>
<li>变换坐标轴（Passive View）：固定几何点，把坐标系按 <span displaypfx="inline-" class="mathjax-container">\(A^{-1}\)</span> 变换；同一个点在新坐标系下的坐标会变化。</li>
</ul>
<p>两种视角描述的是同一个线性映射，只是“变的对象”不同。在做特征变换/白化（Whitening）/坐标变换推导时，这个区分能避免符号混乱。</p>
<p>矩阵乘法只有在内维度相等时才有定义：<span displaypfx="inline-" class="mathjax-container">\((m\times n)(n\times p)=(m\times p)\)</span>。把向量视为列向量（<span displaypfx="inline-" class="mathjax-container">\(m\times 1\)</span>）或行向量（<span displaypfx="inline-" class="mathjax-container">\(1\times m\)</span>）后，外积与点积也都可以统一为矩阵乘法的特例。</p>
<ol>
<li>
<p>一般矩阵乘法：<span displaypfx="inline-" class="mathjax-container">\((m\times n)(n\times p)=(m\times p)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}1 &amp; 2 &amp; 3\\ 4 &amp; 5 &amp; 6\end{bmatrix}\in\mathbb{R}^{2\times 3},\quad B=\begin{bmatrix}7 &amp; 8\\ 9 &amp; 10\\ 11 &amp; 12\end{bmatrix}\in\mathbb{R}^{3\times 2}\]</span>
<span displaypfx="" class="mathjax-container">\[AB=\begin{bmatrix}58 &amp; 64\\ 139 &amp; 154\end{bmatrix}\in\mathbb{R}^{2\times 2}\]</span>
</li>
<li>
<p>外积（Outer Product）：<span displaypfx="inline-" class="mathjax-container">\((m\times 1)(1\times n)=(m\times n)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{u}=\begin{bmatrix}1\\ 2\\ 3\end{bmatrix}\in\mathbb{R}^{3\times 1},\quad \mathbf{v}=\begin{bmatrix}4 &amp; 5\end{bmatrix}\in\mathbb{R}^{1\times 2}\]</span>
<span displaypfx="" class="mathjax-container">\[\mathbf{u}\mathbf{v}=\begin{bmatrix}4 &amp; 5\\ 8 &amp; 10\\ 12 &amp; 15\end{bmatrix}\in\mathbb{R}^{3\times 2}\]</span>
<p>这是一个秩一（Rank-1）矩阵：它把“列向量 × 行向量”变成矩阵；在低秩近似、注意力权重构造与二阶统计量里都很常见。</p>
</li>
<li>
<p>点积（Dot Product）：<span displaypfx="inline-" class="mathjax-container">\((1\times m)(m\times 1)=(1\times 1)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{r}=\begin{bmatrix}1 &amp; 2 &amp; 3\end{bmatrix}\in\mathbb{R}^{1\times 3},\quad \mathbf{c}=\begin{bmatrix}4\\ 5\\ 6\end{bmatrix}\in\mathbb{R}^{3\times 1}\]</span>
<span displaypfx="" class="mathjax-container">\[\mathbf{r}\mathbf{c}=\begin{bmatrix}32\end{bmatrix}\in\mathbb{R}^{1\times 1}\equiv 32\]</span>
<p>结果是标量 <span displaypfx="inline-" class="mathjax-container">\(32\)</span>，等价于点积： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{r}\mathbf{c}=\sum_{i=1}^{m} r_i c_i\)</span>。在实现里常写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top\mathbf{y}\)</span>。</p>
</li>
</ol>
<div class="blog_h3"><span class="graybg">仿射变换与仿射子空间</span></div>
<p>仿射（Affine）是线性（Linear）的一个自然扩展。线性变换要求 <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})=A\mathbf{x}\)</span>，必须把原点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{0}\)</span> 映到原点；仿射变换则允许再加一个平移项：</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x})=A\mathbf{x}+\mathbf{b}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 是线性部分，负责旋转、缩放、剪切、投影等形变； <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 是平移项（Translation / Bias），负责把整个空间整体推走一个位移。因此，仿射变换可以理解为“先做线性变换，再做平移”。</p>
<p>几何上，线性变换像在原点固定不动的前提下拉伸或扭转整张网格；仿射变换则像先把网格按 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 变形，再把整张网格连同原点一起平移到别的位置。两者最核心的区别是：<span style="background-color: #c0c0c0;">线性变换保原点，仿射变换不一定保原点</span>。</p>
<p>最简单的一维例子是 <span displaypfx="inline-" class="mathjax-container">\(f(x)=2x\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(g(x)=2x+3\)</span>。前者是线性的，因为 <span displaypfx="inline-" class="mathjax-container">\(f(0)=0\)</span>；后者是仿射的，因为它先把数轴按 2 倍拉伸，再整体平移 3 个单位，所以 <span displaypfx="inline-" class="mathjax-container">\(g(0)=3\)</span>，不再经过原点。二维里也是同样： <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})=\mathbf{x}\)</span> 是恒等线性变换，而 <span displaypfx="inline-" class="mathjax-container">\(g(\mathbf{x})=\mathbf{x}+\begin{bmatrix}1\\2\end{bmatrix}\)</span> 会把整张平面网格整体向右平移 1、向上平移 2。</p>
<p>仿射函数（Affine Function）在优化与机器学习里极其常见。一维情形的 <span displaypfx="inline-" class="mathjax-container">\(f(x)=ax+b\)</span> 就是最简单的仿射函数；高维里则写成 <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})=\mathbf{w}^\top\mathbf{x}+b\)</span>。因此，神经网络里通常口头说“线性层（Linear Layer）”，但如果带偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>，更严格的数学名称其实是仿射层（Affine Layer）：</p>
<span displaypfx="" class="mathjax-container">\[Y=XW+\mathbf{1}b^\top\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 是输入矩阵， <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 是线性变换矩阵， <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是偏置向量， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\)</span> 是把偏置广播到每个样本上的全 1 列向量。若没有偏置，才是严格意义上的线性映射。</p>
<p>仿射还有一个重要性质：它保持仿射组合（Affine Combination）。若 <span displaypfx="inline-" class="mathjax-container">\(\sum_i \alpha_i=1\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[f\!\left(\sum_i \alpha_i \mathbf{x}_i\right)=\sum_i \alpha_i f(\mathbf{x}_i)\]</span>
<p>这意味着直线、平面、平行关系和凸组合结构在仿射变换下会被保留；因此很多几何对象在经过仿射变换后，仍然保持“像线还是线，像平面还是平面”的基本类型，但位置和方向可能改变。</p>
<p>仿射子空间（Affine Subspace）则是“线性子空间整体平移后得到的集合”。若 <span displaypfx="inline-" class="mathjax-container">\(U\)</span> 是一个线性子空间， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_0\)</span> 是空间中的某个固定点，则</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{A}=\mathbf{x}_0+U=\{\mathbf{x}_0+\mathbf{u}:\mathbf{u}\in U\}\]</span>
<p>就是一个仿射子空间。它和线性子空间的差别也在于是否经过原点：线性子空间必须包含原点，仿射子空间不必。例如二维中的直线 <span displaypfx="inline-" class="mathjax-container">\(x+y=1\)</span> 就是一个仿射子空间；它的方向部分与线性子空间 <span displaypfx="inline-" class="mathjax-container">\(x+y=0\)</span> 相同，但整条直线被平移开了，因此不经过原点。</p>
<p>这正是为什么超平面 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=0\)</span> 被称为仿射子空间而不是线性子空间：当 <span displaypfx="inline-" class="mathjax-container">\(b\ne 0\)</span> 时，它通常不经过原点，只是由某个线性超平面整体平移而来。优化里的等式约束 <span displaypfx="inline-" class="mathjax-container">\(h_j(x)=0\)</span> 若要求 <span displaypfx="inline-" class="mathjax-container">\(h_j\)</span> 是仿射函数，含义也正是“约束边界仍然保持平直结构，但允许存在偏置和平移”。</p>
<p>更进一步，双仿射（Biaffine）也是同一族概念的延伸。它通常在两个向量之间做一个双线性项 <span displaypfx="inline-" class="mathjax-container">\(h_i^\top U h_j\)</span>，再加上线性项和偏置项，因此既包含“两个变量之间的乘性交互”，也包含仿射偏置修正。理解了仿射，就能把“线性 / 仿射 / 双线性 / 双仿射”看成一条逐步加复杂度的函数族谱。</p>
<div class="blog_h4"><span class="graybg">线性到双仿射的公式族谱</span></div>
<p>把输出视为一个标量时，这条族谱可以写成一组并列的标准形式：</p>
<span displaypfx="" class="mathjax-container">\[f_{\mathrm{linear}}(\mathbf{x})=\mathbf{w}^\top \mathbf{x}\]</span>
<span displaypfx="" class="mathjax-container">\[f_{\mathrm{affine}}(\mathbf{x})=\mathbf{w}^\top \mathbf{x}+b\]</span>
<span displaypfx="" class="mathjax-container">\[s_{\mathrm{bilinear}}(\mathbf{x},\mathbf{z})=\mathbf{x}^\top U\mathbf{z}\]</span>
<span displaypfx="" class="mathjax-container">\[s_{\mathrm{biaffine}}(\mathbf{x},\mathbf{z})=\mathbf{x}^\top U\mathbf{z}+\mathbf{a}^\top\mathbf{x}+\mathbf{c}^\top\mathbf{z}+b\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\in\mathbb{R}^{m},\mathbf{z}\in\mathbb{R}^{n}\)</span> 是两个输入向量， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},\mathbf{a}\in\mathbb{R}^{m}\)</span>， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}\in\mathbb{R}^{n}\)</span>， <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{R}^{m\times n}\)</span>， <span displaypfx="inline-" class="mathjax-container">\(b\in\mathbb{R}\)</span>。这里的 <span displaypfx="inline-" class="mathjax-container">\(U\)</span> 是双线性项的参数矩阵（Parameter Matrix）：它不作用于单个向量本身，而是给“ <span displaypfx="inline-" class="mathjax-container">\(x_p\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(z_q\)</span> 同时出现”这类二元交互分配权重。把式子展开后可写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top U\mathbf{z}=\sum_{p=1}^{m}\sum_{q=1}^{n}x_p\,U_{pq}\,z_q\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(U_{pq}\)</span> 直接控制第 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 个 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 特征与第 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 个 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 特征之间的交互强度。 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}^\top\mathbf{x}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}^\top\mathbf{z}\)</span> 是单边线性项，分别描述各自独立的角色偏好； <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是全局基线偏置。</p>
<p>把这个定义写成一个最小的 2×3 例子，会更容易直接看出“连接强度”来自哪里。令</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}=\begin{bmatrix}x_1\\x_2\end{bmatrix},\quad \mathbf{z}=\begin{bmatrix}z_1\\z_2\\z_3\end{bmatrix},\quad U=\begin{bmatrix}u_{11} &amp; u_{12} &amp; u_{13}\\u_{21} &amp; u_{22} &amp; u_{23}\end{bmatrix}\]</span>
<p>则双线性项为</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top U\mathbf{z}=\begin{bmatrix}x_1 &amp; x_2\end{bmatrix}\begin{bmatrix}u_{11} &amp; u_{12} &amp; u_{13}\\u_{21} &amp; u_{22} &amp; u_{23}\end{bmatrix}\begin{bmatrix}z_1\\z_2\\z_3\end{bmatrix}\]</span>
<span displaypfx="" class="mathjax-container">\[=x_1u_{11}z_1+x_1u_{12}z_2+x_1u_{13}z_3+x_2u_{21}z_1+x_2u_{22}z_2+x_2u_{23}z_3\]</span>
<p>这一步把抽象的矩阵乘法拆成了六条显式连接： <span displaypfx="inline-" class="mathjax-container">\(u_{11}\)</span> 控制 <span displaypfx="inline-" class="mathjax-container">\(x_1\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(z_1\)</span> 的连接强度， <span displaypfx="inline-" class="mathjax-container">\(u_{23}\)</span> 控制 <span displaypfx="inline-" class="mathjax-container">\(x_2\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(z_3\)</span> 的连接强度。若某个 <span displaypfx="inline-" class="mathjax-container">\(u_{pq}\)</span> 很大且为正，只要对应的 <span displaypfx="inline-" class="mathjax-container">\(x_p\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(z_q\)</span> 同时取大值，这一对特征就会显著抬高总分；若某个 <span displaypfx="inline-" class="mathjax-container">\(u_{pq}\)</span> 为负，则说明这对特征的共同出现会压低分数。</p>
<p>在同一个例子里，双仿射只是在双线性项之外再加上单边项与偏置：</p>
<span displaypfx="" class="mathjax-container">\[s_{\mathrm{biaffine}}(\mathbf{x},\mathbf{z})=\mathbf{x}^\top U\mathbf{z}+\mathbf{a}^\top\mathbf{x}+\mathbf{c}^\top\mathbf{z}+b\]</span>
<span displaypfx="" class="mathjax-container">\[=\mathbf{x}^\top U\mathbf{z}+(a_1x_1+a_2x_2)+(c_1z_1+c_2z_2+c_3z_3)+b\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(a_1,a_2\)</span> 描述 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 一侧各特征自身的偏好， <span displaypfx="inline-" class="mathjax-container">\(c_1,c_2,c_3\)</span> 描述 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 一侧各特征自身的偏好， <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 给出无条件基线分数。于是，双线性回答的是“这两组特征配在一起有多合适”，双仿射回答的则是“它们配在一起有多合适，并且各自本身是否已经带有倾向”。</p>
<p>若输出不是一个标量分数，而是 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个关系类别或标签分数，则通常不只使用一个 <span displaypfx="inline-" class="mathjax-container">\(U\)</span>，而是为每个类别准备一张交互矩阵 <span displaypfx="inline-" class="mathjax-container">\(U^{(k)}\)</span>，或等价地把它们堆成三阶张量 <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{R}^{K\times m\times n}\)</span>。这时每个类别都拥有自己的一套“特征两两交互”权重。</p>
<p>从函数结构看，线性与仿射的区别在于是否含偏置；双线性与双仿射的区别同样在于是否在交互项之外再加入单边项与偏置。固定 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 时， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top U\mathbf{z}\)</span> 对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 是线性的， <span displaypfx="inline-" class="mathjax-container">\(s_{\text{biaffine}}(\mathbf{x},\mathbf{z})\)</span> 对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 则是仿射的；固定 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 时，对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 也是同样的性质。这正是 “biaffine” 这个名字的数学含义。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/affine-biaffine-family.png"><img class="alignnone size-full" src="https://blog.gmem.cc/wp-content/uploads/2026/03/affine-biaffine-family.png" alt="affine-biaffine-family" width="1920" height="1203" /></a></p>
<p>图中上排用一维切片显示“是否经过原点”这一关键差异： <span displaypfx="inline-" class="mathjax-container">\(f(x)=ax\)</span> 必然经过原点，而 <span displaypfx="inline-" class="mathjax-container">\(f(x)=ax+b\)</span> 由于加入偏置项，整条直线沿输出轴发生平移。下排保持与公式族谱一致，仍写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top U\mathbf{z}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top U\mathbf{z}+\mathbf{a}^\top\mathbf{x}+\mathbf{c}^\top\mathbf{z}+b\)</span>；图像本身展示的是把高维向量关系压到二维后的一个切片。纯双线性项的零等值线体现为对称的交互边界；加入单边项与偏置后，零等值线整体偏移，分数面出现倾斜与平移，表示模型不仅关心“是否匹配”，还关心两个对象各自单独的倾向。</p>
<p>在依存句法（Dependency Parsing）、关系抽取（Relation Extraction）和成对匹配（Pairwise Matching）任务中，这个结构尤其有用。纯双线性项只能表达“组合后是否相容”，双仿射则进一步允许模型学习“某个对象本身就更像 head / dependent”或“某个实体本身就更像某类关系的一端”。因此，双仿射通常既比纯仿射更能表达交互，又比纯双线性更稳定。</p>
<div class="blog_h3"><span class="graybg">转置（Transpose）</span></div>
<p>转置（Transpose）把行列互换：对 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{m\times n}\)</span>，其转置 <span displaypfx="inline-" class="mathjax-container">\(A^\top\in\mathbb{R}^{n\times m}\)</span> 满足 <span displaypfx="inline-" class="mathjax-container">\((A^\top)_{ij}=A_{ji}\)</span>。</p>
<p>它常用于对齐乘法形状、把点积写成矩阵乘法，以及在推导中“移动”矩阵：例如 <span displaypfx="inline-" class="mathjax-container">\((AB)^\top=B^\top A^\top\)</span>。Transformer 注意力中的 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span> 就是典型的“先转置再相乘”。</p>
<div class="blog_h4"><span class="graybg">例：行列互换与形状变化</span></div>
<p>令</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}1 &amp; 2 &amp; 3\\ 4 &amp; 5 &amp; 6\end{bmatrix}\in\mathbb{R}^{2\times 3}\]</span>
<p>则</p>
<span displaypfx="" class="mathjax-container">\[A^\top=\begin{bmatrix}1 &amp; 4\\ 2 &amp; 5\\ 3 &amp; 6\end{bmatrix}\in\mathbb{R}^{3\times 2}\]</span>
<p>可以直接看到：原矩阵的第 1 行变成转置后的第 1 列；因此 <span displaypfx="inline-" class="mathjax-container">\((m\times n)^\top=(n\times m)\)</span>。</p>
<div class="blog_h3"><span class="graybg">Hadamard 乘积（Hadamard Product）</span></div>
<p>Hadamard 乘积（Hadamard Product）是逐元素（Element-wise）相乘：若 <span displaypfx="inline-" class="mathjax-container">\(X,M\)</span> 形状相同，则 <span displaypfx="inline-" class="mathjax-container">\((X\odot M)_{ij}=X_{ij}M_{ij}\)</span>。</p>
<p>典型用途是掩码（Masking）与门控（Gating）。例：令 <span displaypfx="inline-" class="mathjax-container">\(M\in\{0,1\}^{B\times d}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(X\odot M\)</span> 会把被屏蔽的特征位置直接置零；也可以用 <span displaypfx="inline-" class="mathjax-container">\(M\in[0,1]^{B\times d}\)</span> 做连续缩放。</p>
<div class="blog_h3"><span class="graybg">矩阵分解（Factorization / Decomposition）</span></div>
<p>矩阵分解（Decomposition）把矩阵写成更“易处理”的结构乘积，用于求解、降维与稳定计算。常见形式包括：</p>
<ul>
<li>SVD： <span displaypfx="inline-" class="mathjax-container">\(A=U\Sigma V^\top\)</span>，用于 PCA（Principal Component Analysis）、低秩近似与数值稳健求解。</li>
<li>QR： <span displaypfx="inline-" class="mathjax-container">\(A=QR\)</span>（<span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 正交、<span displaypfx="inline-" class="mathjax-container">\(R\)</span> 上三角），常用于最小二乘与正交化。</li>
<li>Cholesky：对对称正定（SPD）矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(A=LL^\top\)</span>，常用于高斯模型与二次优化中的快速求解。</li>
</ul>
<div class="blog_h3"><span class="graybg">迹（Trace）</span></div>
<p>矩阵的迹（Trace）定义为对角线元素之和：对方阵 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{n\times n}\)</span>，</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{tr}(A)=\sum_{i=1}^{n} A_{ii}\]</span>
<p>迹在推导里常用的性质是循环不变性（Cyclic Property）：<span displaypfx="inline-" class="mathjax-container">\(\mathrm{tr}(AB)=\mathrm{tr}(BA)\)</span>（形状匹配时）。一个高频等式是 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{tr}(A^\top A)=\|A\|_F^2\)</span>，它把“平方和”写成迹，便于做矩阵微分与正则化推导。</p>
<div class="blog_h3"><span class="graybg">范数（Matrix Norms）</span></div>
<p>范数（Norm）刻画矩阵的“大小”。最常用的是 Frobenius 范数：</p>
<span displaypfx="" class="mathjax-container">\[\|A\|_F=\sqrt{\sum_{i,j}A_{ij}^2}\]</span>
<p>它等价于把矩阵按元素展平后的 <span displaypfx="inline-" class="mathjax-container">\(\ell_2\)</span> 范数，常用于权重衰减（Weight Decay）/L2 正则。另一个常见的是谱范数（Spectral Norm）<span displaypfx="inline-" class="mathjax-container">\(\|A\|_2\)</span>（最大奇异值），用于控制 Lipschitz 常数与训练稳定性（如 spectral normalization）。</p>
<div class="blog_h3"><span class="graybg">外积与秩一更新（Outer Product / Rank-1 Update）</span></div>
<p>外积（Outer Product）把两个向量映射为矩阵：对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\in\mathbb{R}^{m}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\in\mathbb{R}^{n}\)</span>，</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{u}\mathbf{v}^\top\in\mathbb{R}^{m\times n}\]</span>
<p>它是一个秩一（Rank-1）矩阵。外积在统计与学习中常用于构造二阶量：例如样本协方差的无偏估计可写成中心化向量的外积平均 <span displaypfx="inline-" class="mathjax-container">\(\Sigma\approx \frac{1}{N}\sum_{k=1}^{N}(\mathbf{x}_k-\bar{\mathbf{x}})(\mathbf{x}_k-\bar{\mathbf{x}})^\top\)</span>。</p>
<p>例：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}=\begin{bmatrix}1\\ 2\\ 3\end{bmatrix}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}=\begin{bmatrix}4\\ 5\end{bmatrix}\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{u}\mathbf{v}^\top=\begin{bmatrix}4 &amp; 5\\ 8 &amp; 10\\ 12 &amp; 15\end{bmatrix}\]</span>
<p>直观上，外积得到的矩阵每一列都是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 的缩放：第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 列等于 <span displaypfx="inline-" class="mathjax-container">\(v_j\mathbf{u}\)</span>。因此所有列共线，矩阵的秩最多为 1（除非 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span> 为零向量）。</p>
<p>把外积视为线性算子更直接：对任意 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\in\mathbb{R}^{n}\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{u}\mathbf{v}^\top)\mathbf{x}=\mathbf{u}(\mathbf{v}^\top\mathbf{x})\)</span>。这表示先沿 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span> 做一次投影/打分得到标量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}^\top\mathbf{x}\)</span>，再沿 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 方向输出。例：取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=\begin{bmatrix}1\\ 1\end{bmatrix}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}^\top\mathbf{x}=9\)</span>，从而 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{u}\mathbf{v}^\top)\mathbf{x}=9\mathbf{u}=\begin{bmatrix}9\\ 18\\ 27\end{bmatrix}\)</span>。</p>
<p>秩一更新（Rank-1 Update）则是把矩阵写成 <span displaypfx="inline-" class="mathjax-container">\(A\leftarrow A+\mathbf{u}\mathbf{v}^\top\)</span>：只引入一个方向上的低秩结构，常用于用较低代价注入统计量/二阶近似，或在保持主结构的前提下做小幅调整。</p>
<div class="blog_h2"><span class="graybg">行列式</span></div>
<p>行列式（Determinant）把一个方阵 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{n\times n}\)</span> 映射为标量 <span displaypfx="inline-" class="mathjax-container">\(\det(A)\)</span>。几何上，它是线性变换对体积的缩放因子（Volume Scaling Factor）：绝对值表示缩放倍数，符号表示是否翻转取向（Orientation Flip）。</p>
<p>二维情形最直观：若</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}a &amp; b\\ c &amp; d\end{bmatrix}\]</span>
<p>则</p>
<span displaypfx="" class="mathjax-container">\[\det(A)=ad-bc\]</span>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(A=\begin{bmatrix}2 &amp; 1\\ 0 &amp; 3\end{bmatrix}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\det(A)=6\)</span>：面积被放大 6 倍。</p>
<p>关键结论：<span style="background-color: #c0c0c0;">方阵可逆（Invertible）当且仅当行列式非零</span>。当 <span displaypfx="inline-" class="mathjax-container">\(\det(A)=0\)</span> 时，变换会把体积压扁到低维（丢失信息），对应列向量线性相关（Linearly Dependent）。</p>
<p>常用性质：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\det(AB)=\det(A)\det(B)\)</span></li>
<li><span displaypfx="inline-" class="mathjax-container">\(\det(A^\top)=\det(A)\)</span></li>
<li><span displaypfx="inline-" class="mathjax-container">\(\det(I)=1\)</span></li>
</ul>
<p>若把特征值（Eigenvalues）记作 <span displaypfx="inline-" class="mathjax-container">\(\{\lambda_i\}_{i=1}^n\)</span>（按代数重数计），则 <span displaypfx="inline-" class="mathjax-container">\(\det(A)=\prod_i \lambda_i\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathrm{tr}(A)=\sum_i \lambda_i\)</span>。</p>
<div class="blog_h2"><span class="graybg">矩阵的秩</span></div>
<p>矩阵的秩（Rank）刻画“列（或行）里最多有多少个线性无关（Linearly Independent）的方向”。对 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{m\times n}\)</span>，秩定义为列空间（Column Space）的维数：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{rank}(A)=\dim(\mathrm{col}(A))\]</span>
<p>这一定义的直接含义是：若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rank}(A)=r\)</span>，那么 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的所有列向量都只能张成一个 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 维子空间，超出这个子空间的方向它完全无法表达。反过来，若某一列可以由其他列线性组合得到，它就不提供新的维度，因此不会增加秩。</p>
<p>它也等于行空间（Row Space）的维数（行秩=列秩）。把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 看作线性映射 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^n\to\mathbb{R}^m\)</span>，秩就是输出子空间的维度：最多能输出多少个自由方向。</p>
<p>满秩（Full Rank）通常指 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rank}(A)=\min(m,n)\)</span>。对方阵 <span displaypfx="inline-" class="mathjax-container">\(n\times n\)</span> 而言，满秩等价于可逆（也等价于 <span displaypfx="inline-" class="mathjax-container">\(\det(A)\ne 0\)</span>）。</p>
<p>线性方程组（Linear System）<span displaypfx="inline-" class="mathjax-container">\(A\mathbf{x}=\mathbf{b}\)</span> 的解与秩直接相关：设增广矩阵为 <span displaypfx="inline-" class="mathjax-container">\(\left[A\,\middle|\,\mathbf{b}\right]\)</span>，则</p>
<ul>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rank}(A)\ne \mathrm{rank}\!\left(\left[A\,\middle|\,\mathbf{b}\right]\right)\)</span>，无解。</li>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rank}(A)=\mathrm{rank}\!\left(\left[A\,\middle|\,\mathbf{b}\right]\right)=n\)</span>（未知数个数），唯一解。</li>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rank}(A)=\mathrm{rank}\!\left(\left[A\,\middle|\,\mathbf{b}\right]\right)&lt;n\)</span>，无穷多解（存在自由变量）。</li>
</ul>
<p>与 SVD 的关系非常实用：<span style="background-color: #c0c0c0;">秩等于非零奇异值（Singular Values）的个数</span>，因此在数值计算里常用“奇异值是否接近 0”判断有效秩（Numerical Rank）。</p>
<div class="blog_h2"><span class="graybg">二次型（Quadratic Form）</span></div>
<p>在高中里，一元二次函数常写成 <span displaypfx="inline-" class="mathjax-container">\(ax^2+bx+c\)</span>。把“二次”推广到多元，并且只保留二次项（没有一次项与常数项），就得到二次型（Quadratic Form）。二维里最常见的形式是：</p>
<span displaypfx="" class="mathjax-container">\[q(x,y)=ax^2+bxy+cy^2\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(bxy\)</span> 是交叉项（Cross Term）：它把不同变量“耦合在一起”。在解析几何（Analytic Geometry）里，交叉项常对应二次曲线（Conic Section）的主轴（Principal Axes）不与坐标轴对齐（图形呈旋转/倾斜）。</p>
<p>若再把一次项与常数项加回来，就得到更一般的二次多项式（Quadratic Polynomial）<span displaypfx="inline-" class="mathjax-container">\(ax^2+bxy+cy^2+dx+ey+f\)</span>。这时：交叉项 <span displaypfx="inline-" class="mathjax-container">\(bxy\)</span> 主要反映主轴旋转；一次项 <span displaypfx="inline-" class="mathjax-container">\(dx+ey\)</span> 往往表示图形的中心/顶点从原点平移出去；常数项 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 则改变整体基准值，进而影响图形的截距、大小以及是否与 <span displaypfx="inline-" class="mathjax-container">\(q(x,y)=0\)</span> 相交。只有把一次项和常数项都去掉时，我们讨论的才是纯粹的二次型。</p>
<p>下面两幅图都基于<span style="background-color: #c0c0c0;">等值线（Level Set）</span>生成：先固定常数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span>，在平面上求解 <span displaypfx="inline-" class="mathjax-container">\(q(x,y)=k\)</span> 得到等值线，再把不同 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 的结果叠加，并与 <span displaypfx="inline-" class="mathjax-container">\(z=q(x,y)\)</span> 的三维曲面对应显示。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/quadratic-forms.png"><img class="alignnone size-full wp-image-40671" src="https://blog.gmem.cc/wp-content/uploads/2026/03/quadratic-forms.png" alt="quadratic-forms" width="100%" /></a></p>
<p>第一幅展示无交叉项（<span displaypfx="inline-" class="mathjax-container">\(b=0\)</span>）的典型形态：等值线与坐标轴对齐，曲面主轴方向也与坐标轴一致。</p>
<p>&nbsp;</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/quadratic-forms-cross.png"><img class="alignnone size-full wp-image-40677" src="https://blog.gmem.cc/wp-content/uploads/2026/03/quadratic-forms-cross.png" alt="quadratic-forms-cross" width="100%" /></a></p>
<p>第二幅展示含交叉项（<span displaypfx="inline-" class="mathjax-container">\(b\ne 0\)</span>）的情形：等值线整体发生旋转/倾斜，三维曲面看起来更“不规则”。</p>
<p>在线性代数里，用矩阵（Matrix）把系数组织起来更方便。令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,\ldots,x_n)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{n\times n}\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[q(\mathbf{x})=\mathbf{x}^\top A\mathbf{x}=\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i x_j\]</span>
<p>上面这条等式不是“记号游戏”，而是把矩阵乘法按分量（Component）展开后的结果。把它分两步看最清楚：</p>
<ol>
<li>先做一次矩阵-向量乘法：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}=A\mathbf{x}\)</span>，则第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量是</li>
</ol>
<span displaypfx="" class="mathjax-container">\[y_i=(A\mathbf{x})_i=\sum_{j=1}^{n}A_{ij}x_j\]</span>
<ol start="2">
<li>再做一次点积： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top\mathbf{y}=\sum_{i=1}^{n}x_i y_i\)</span>。把第 1 步的 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 代入，就得到</li>
</ol>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top(A\mathbf{x})=\sum_{i=1}^{n}x_i\left(\sum_{j=1}^{n}A_{ij}x_j\right)=\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i x_j\]</span>
<p>这就是“双重求和（Double Summation）”的含义：每一个矩阵元素 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}\)</span> 都在给二次项 <span displaypfx="inline-" class="mathjax-container">\(x_i x_j\)</span> 分配一个权重；当 <span displaypfx="inline-" class="mathjax-container">\(i=j\)</span> 时就是平方项 <span displaypfx="inline-" class="mathjax-container">\(x_i^2\)</span>，当 <span displaypfx="inline-" class="mathjax-container">\(i\ne j\)</span> 时就是交叉项 <span displaypfx="inline-" class="mathjax-container">\(x_i x_j\)</span>。</p>
<p>二维情形最直观：若希望展开后得到 <span displaypfx="inline-" class="mathjax-container">\(ax^2+bxy+cy^2\)</span>，可以取</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}a &amp; \frac{b}{2}\\ \frac{b}{2} &amp; c\end{bmatrix}\]</span>
<p>这里把向量写成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x,y)^\top\)</span>，按矩阵乘法展开一遍：</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{x}=\begin{bmatrix}a &amp; \frac{b}{2}\\ \frac{b}{2} &amp; c\end{bmatrix}\begin{bmatrix}x\\ y\end{bmatrix}=\begin{bmatrix}ax+\frac{b}{2}y\\ \frac{b}{2}x+cy\end{bmatrix}\]</span>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top(A\mathbf{x})=\begin{bmatrix}x &amp; y\end{bmatrix}\begin{bmatrix}ax+\frac{b}{2}y\\ \frac{b}{2}x+cy\end{bmatrix}=ax^2+\frac{b}{2}xy+\frac{b}{2}yx+cy^2=ax^2+bxy+cy^2\]</span>
<p>可以看到：交叉项 <span displaypfx="inline-" class="mathjax-container">\(xy\)</span> 的系数 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 实际来自两处对称位置 <span displaypfx="inline-" class="mathjax-container">\(A_{12}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(A_{21}\)</span> 的“合力”（各贡献一半）。这也会自然导向下一节的结论：二次型只依赖矩阵的对称部分。</p>
<p>例：取 <span displaypfx="inline-" class="mathjax-container">\(q(x,y)=5x^2-4xy+5y^2\)</span>，对应 <span displaypfx="inline-" class="mathjax-container">\(A=\begin{bmatrix}5 &amp; -2\\ -2 &amp; 5\end{bmatrix}\)</span>。这一类表达式不仅在几何里出现（椭圆（Ellipse）/双曲线（Hyperbola）），在优化与统计里也高频出现（曲率（Curvature）、距离度量（Distance Metric））。</p>
<div class="blog_h3"><span class="graybg">对称化（Symmetrization）</span></div>
<p>对任意方阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，二次型只依赖其对称部分：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top\left(\frac{A+A^\top}{2}\right)\mathbf{x}\]</span>
<p>这句话的意思是：无论 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的非对称部分长什么样，只要 <span displaypfx="inline-" class="mathjax-container">\(\frac{A+A^\top}{2}\)</span> 不变，二次型 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}\)</span> 对所有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 的取值就完全不变。</p>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 拆开看更直观。定义对称部分（Symmetric Part）与反对称部分（Skew-symmetric Part）：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(S=\frac{A+A^\top}{2}\)</span>，满足 <span displaypfx="inline-" class="mathjax-container">\(S=S^\top\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(K=\frac{A-A^\top}{2}\)</span>，满足 <span displaypfx="inline-" class="mathjax-container">\(K^\top=-K\)</span>。</li>
</ul>
<p>这里“对称部分（Symmetric Part）”是一个<span style="background-color: #c0c0c0;">定义</span>：对任意方阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，把 <span displaypfx="inline-" class="mathjax-container">\(S=\frac{A+A^\top}{2}\)</span> 定义为它的对称部分。它与 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> <span style="background-color: #c0c0c0;">同型（同大小）</span>，并且一定是对称矩阵；它不是 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的某个“子矩阵”。</p>
<p>按元素（Entry-wise）写得更直观：对任意 <span displaypfx="inline-" class="mathjax-container">\(i,j\)</span>，</p>
<span displaypfx="" class="mathjax-container">\[S_{ij}=\frac{A_{ij}+A_{ji}}{2},\quad K_{ij}=\frac{A_{ij}-A_{ji}}{2}\]</span>
<p>也就是说：对称部分就是把每一对对称位置 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\((j,i)\)</span> 的元素取平均；反对称部分则记录它们的“差的一半”。因此 <span displaypfx="inline-" class="mathjax-container">\(A=S+K\)</span> 是把任意矩阵分解成“对称 + 反对称”的标准方式，并且这个分解是唯一的（Unique）。</p>
<p>为什么“取 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(A^\top\)</span> 的平均值”就得到对称部分？因为转置（Transpose）会把非对角元素成对交换： <span displaypfx="inline-" class="mathjax-container">\(A_{ij}\leftrightarrow A_{ji}\)</span>。把它们相加后，非对称性（即 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}-A_{ji}\)</span>）会被抵消，只留下“对称的那一半”（即 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}+A_{ji}\)</span>）。再除以 2，是为了把“加了两份”的量恢复到原始尺度：如果 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 本来就对称（<span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>），那么 <span displaypfx="inline-" class="mathjax-container">\(\frac{A+A^\top}{2}=A\)</span>，不会把矩阵放大一倍。</p>
<p>于是 <span displaypfx="inline-" class="mathjax-container">\(A=S+K\)</span>，并且</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top S\mathbf{x}+\mathbf{x}^\top K\mathbf{x}\]</span>
<p>关键点在于：对任意 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span>，都有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top K\mathbf{x}=0\)</span>。理由很短：它是一个标量，等于它自己的转置，而</p>
<span displaypfx="" class="mathjax-container">\[(\mathbf{x}^\top K\mathbf{x})^\top=\mathbf{x}^\top K^\top \mathbf{x}=\mathbf{x}^\top(-K)\mathbf{x}=-(\mathbf{x}^\top K\mathbf{x})\]</span>
<p>一个数如果等于它的相反数，只能是 0。于是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top S\mathbf{x}\)</span>，二次型确实只由对称部分决定。</p>
<p>二维展开能直接看到“只依赖对称部分”的具体含义。令 <span displaypfx="inline-" class="mathjax-container">\(A=\begin{bmatrix}a &amp; b\\ c &amp; d\end{bmatrix}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,x_2)^\top\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=ax_1^2+(b+c)x_1x_2+dx_2^2\]</span>
<p>交叉项系数只出现 <span displaypfx="inline-" class="mathjax-container">\(b+c\)</span>（也就是 <span displaypfx="inline-" class="mathjax-container">\(A_{12}+A_{21}\)</span>），而差值 <span displaypfx="inline-" class="mathjax-container">\(b-c\)</span>（反对称部分）完全不会出现。</p>
<div class="blog_h4"><span class="graybg">例：两个不同矩阵，二次型完全一样</span></div>
<p>下面给一个“看得见”的数值例子。取</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}2 &amp; 4\\ -2 &amp; 4\end{bmatrix}\]</span>
<p>它显然不是对称矩阵（因为 <span displaypfx="inline-" class="mathjax-container">\(A_{12}=4\ne -2=A_{21}\)</span>）。计算它的对称部分：</p>
<span displaypfx="" class="mathjax-container">\[\frac{A+A^\top}{2}=\frac{1}{2}\left(\begin{bmatrix}2 &amp; 4\\ -2 &amp; 4\end{bmatrix}+\begin{bmatrix}2 &amp; -2\\ 4 &amp; 4\end{bmatrix}\right)=\begin{bmatrix}2 &amp; 1\\ 1 &amp; 4\end{bmatrix}=S\]</span>
<p>现在比较二次型。对任意 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x,y)^\top\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{x}=\begin{bmatrix}2x+4y\\ -2x+4y\end{bmatrix}\Rightarrow \mathbf{x}^\top A\mathbf{x}=x(2x+4y)+y(-2x+4y)=2x^2+2xy+4y^2\]</span>
<span displaypfx="" class="mathjax-container">\[S\mathbf{x}=\begin{bmatrix}2x+y\\ x+4y\end{bmatrix}\Rightarrow \mathbf{x}^\top S\mathbf{x}=x(2x+y)+y(x+4y)=2x^2+2xy+4y^2\]</span>
<p>两者对所有 <span displaypfx="inline-" class="mathjax-container">\((x,y)\)</span> 都完全相同；例如取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(1,2)^\top\)</span>，都有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top S\mathbf{x}=22\)</span>。这就直观解释了“二次型只依赖对称部分”的含义：反对称的那一半怎么改，都不会改变 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}\)</span> 的值。</p>
<p>因此讨论二次型时通常可假设 <span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>。这也解释了为什么二次型与对称矩阵/半正定性（Positive Semi-Definite, PSD）紧密绑定。</p>
<div class="blog_h3"><span class="graybg">二次型的标准型（Standard Form）</span></div>
<div class="blog_h4"><span class="graybg">标准型与对角标准型是什么</span></div>
<p>二次型 <span displaypfx="inline-" class="mathjax-container">\(q(\mathbf{x})=\mathbf{x}^\top A\mathbf{x}\)</span> 本身是一个几何对象；它在不同坐标系（Coordinate System）/基（Basis）下的<span style="background-color: #c0c0c0;">矩阵表示</span>会不同：同一个几何对象，用不同坐标轴/基表示时，系数矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的元素会改变。</p>
<p>这里的标准型指的是：在一类允许的坐标变换（可逆线性变量替换（Invertible Linear Change of Variables））下，把同一个二次型写成某种<span style="background-color: #c0c0c0;">约定的简化代表</span>。不同教材的约定略有差异，但最常用的目标是：把交叉项（Cross Term）消掉，露出每个坐标轴方向上的“纯平方项”。</p>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=T\mathbf{y}\)</span> 理解成换基（Change of Basis）会更不容易出错： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 是同一几何向量在新基下的坐标，矩阵 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 由新基向量在旧基下的坐标组成。因为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 可逆，两套坐标是一一对应的，可以互相换回：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{y}=T^{-1}\mathbf{x},\quad \mathbf{x}=T\mathbf{y}\]</span>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=T\mathbf{y}\)</span> 代入可得</p>
<span displaypfx="" class="mathjax-container">\[q(\mathbf{x})=\mathbf{x}^\top A\mathbf{x}=\mathbf{y}^\top(T^\top A T)\mathbf{y}\]</span>
<p>这里要求 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 可逆（Invertible），意味着这个变量代换不会把空间压缩到低维（不会丢维度）。因此在新坐标 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 下，二次型对应的系数矩阵变为 <span displaypfx="inline-" class="mathjax-container">\(T^\top A T\)</span>（这叫合同变换（Congruence Transformation））。可以把 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 理解为“旋转/缩放后的新坐标轴”在旧坐标里的表示：同一个二次型在新坐标系里的系数就由 <span displaypfx="inline-" class="mathjax-container">\(T^\top A T\)</span> 给出。标准型的目标就是选取合适的 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>，把 <span displaypfx="inline-" class="mathjax-container">\(T^\top A T\)</span> 化到更简单的结构。</p>
<p>对角标准型（Diagonal Form）指把二次型写成“只有平方项、没有交叉项”的形式（也常称对角规范形（Diagonal Canonical Form））：</p>
<span displaypfx="" class="mathjax-container">\[q(\mathbf{x})=\sum_{i=1}^{n}\lambda_i y_i^2\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span> 是系数，<span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 是新坐标。对角标准型等价于：在新坐标系下，二次型对应的矩阵是对角矩阵（Diagonal Matrix）；“交叉项” <span displaypfx="inline-" class="mathjax-container">\(y_i y_j\)</span>（<span displaypfx="inline-" class="mathjax-container">\(i\ne j\)</span>）消失。</p>
<p>结论需要明确：标准型与原来的二次型描述的是<span style="background-color: #c0c0c0;">同一个二次型/同一组几何等值集合</span>，只是坐标系不同。给定可逆变换 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=T\mathbf{y}\)</span>，任何关于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 的几何描述都可以无损地翻译成关于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 的描述，并且可以随时“还原”回去。矩阵层面也一样：若 <span displaypfx="inline-" class="mathjax-container">\(A' = T^\top A T\)</span> 是标准型里的系数矩阵，则 <span displaypfx="inline-" class="mathjax-container">\(A=(T^{-1})^\top A' T^{-1}\)</span> 可把它变回原坐标下的表示。</p>
<p>接下来真正关心的是：<span style="background-color: #c0c0c0;">如何选 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 才能把交叉项消掉</span>。对二次型而言，一个关键简化是：二次型只依赖矩阵的对称部分，因此总可以先把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 对称化为 <span displaypfx="inline-" class="mathjax-container">\(\frac{A+A^\top}{2}\)</span> 而不改变 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}\)</span> 的值。于是“消交叉项”的核心问题就变成：对实对称矩阵，能否通过一次正交变基（Orthogonal Change of Basis）把它对角化（Diagonalize）。</p>
<div class="blog_h4"><span class="graybg">怎么来的：换到特征向量基消掉交叉项</span></div>
<p>对实对称矩阵，对角化与“消掉交叉项”来自同一个结构事实：它总可以通过一次正交变基（Orthogonal Change of Basis）写成对角形式。数学依据就是谱定理（Spectral Theorem，也常表述为“实对称矩阵可正交对角化（Orthogonal Diagonalization）”）。若 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{n\times n}\)</span> 是实对称矩阵（Real Symmetric Matrix, <span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>），则存在正交矩阵（Orthogonal Matrix）<span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 与实对角矩阵（Real Diagonal Matrix）<span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span> 使得</p>
<span displaypfx="" class="mathjax-container">\[A=Q\Lambda Q^\top,\quad \text{等价于}\quad Q^\top A Q=\Lambda\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(Q=[\mathbf{v}_1,\ldots,\mathbf{v}_n]\)</span> 的列向量是一组单位特征向量（Orthonormal Eigenvectors），<span displaypfx="inline-" class="mathjax-container">\(\Lambda=\mathrm{diag}(\lambda_1,\ldots,\lambda_n)\)</span> 的对角元素是对应特征值（Eigenvalues）。该定理同时包含两个常用事实：特征值都是实数；并且可以选出一组两两正交的特征向量作为基。</p>
<p>这就是你熟悉的特征值分解（Eigendecomposition / Eigenvalue Decomposition）的对称矩阵特例。</p>
<p>一般情况下，如果矩阵可对角化（Diagonalizable），可以写成 <span displaypfx="inline-" class="mathjax-container">\(A=V\Lambda V^{-1}\)</span>（或 <span displaypfx="inline-" class="mathjax-container">\(V^{-1}AV=\Lambda\)</span>），其中 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 的列是特征向量；但 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 不一定正交（Orthogonal），甚至矩阵可能不可对角化（Non-diagonalizable）。</p>
<p>对称矩阵的额外好处是：可以把 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 选成正交矩阵 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(V^{-1}=Q^{-1}=Q^\top\)</span>，分解变成数值上更稳定、几何上更直观的 <span displaypfx="inline-" class="mathjax-container">\(A=Q\Lambda Q^\top\)</span>。</p>
<p>两个最小例子能把“<span displaypfx="inline-" class="mathjax-container">\(V\)</span> 不一定正交 / 甚至不可对角化”说得更具体：</p>
<ul>
<li>
<p><span style="background-color: #c0c0c0;">例 1：可对角化，但 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 不正交</span>。取</p>
<span displaypfx="" class="mathjax-container">\[A_1=\begin{bmatrix}2 &amp; 1\\ 0 &amp; 1\end{bmatrix}\]</span>
<p>它的特征值是 <span displaypfx="inline-" class="mathjax-container">\(\lambda_1=2,\lambda_2=1\)</span>（两个不同特征值意味着在二维里一定能找到两条线性无关的特征向量，因此可对角化）。对应一组特征向量可以取</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{v}_1=\begin{bmatrix}1\\ 0\end{bmatrix},\quad \mathbf{v}_2=\begin{bmatrix}1\\ -1\end{bmatrix}\]</span>
<p>它们并不正交，因为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}_1^\top\mathbf{v}_2=1\ne 0\)</span>。把它们按列组成矩阵 <span displaypfx="inline-" class="mathjax-container">\(V=[\mathbf{v}_1\ \mathbf{v}_2]\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[V=\begin{bmatrix}1 &amp; 1\\ 0 &amp; -1\end{bmatrix},\quad \Lambda=\begin{bmatrix}2 &amp; 0\\ 0 &amp; 1\end{bmatrix},\quad V^{-1}=\begin{bmatrix}1 &amp; 1\\ 0 &amp; -1\end{bmatrix}\]</span>
<p>注意：这个例子里 <span displaypfx="inline-" class="mathjax-container">\(V^{-1}\)</span> 恰好等于 <span displaypfx="inline-" class="mathjax-container">\(V\)</span>（只是代数上的巧合），但它仍然不是正交矩阵，因为正交要求 <span displaypfx="inline-" class="mathjax-container">\(V^{-1}=V^\top\)</span>，而这里并不成立。</p>
<p>并且确实有 <span displaypfx="inline-" class="mathjax-container">\(A_1=V\Lambda V^{-1}\)</span>。这个例子说明：一般矩阵即使可对角化，特征向量也未必能选成“互相垂直的方向”。</p>
</li>
<li>
<p><span style="background-color: #c0c0c0;">例 2：不可对角化（特征值重复，但特征向量不够）</span>。取</p>
<span displaypfx="" class="mathjax-container">\[A_2=\begin{bmatrix}1 &amp; 1\\ 0 &amp; 1\end{bmatrix}\]</span>
<p>它的特征值只有 <span displaypfx="inline-" class="mathjax-container">\(\lambda=1\)</span>（在二维里重复出现）。求特征向量需要解 <span displaypfx="inline-" class="mathjax-container">\((A_2-I)\mathbf{v}=\mathbf{0}\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[A_2-I=\begin{bmatrix}0 &amp; 1\\ 0 &amp; 0\end{bmatrix}\Rightarrow (A_2-I)\begin{bmatrix}x\\ y\end{bmatrix}=\begin{bmatrix}y\\ 0\end{bmatrix}=\begin{bmatrix}0\\ 0\end{bmatrix}\Rightarrow y=0\]</span>
<p>因此所有特征向量都形如 <span displaypfx="inline-" class="mathjax-container">\((x,0)^\top\)</span>，只有 1 个线性无关方向。要写成 <span displaypfx="inline-" class="mathjax-container">\(A_2=V\Lambda V^{-1}\)</span>，矩阵 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 必须可逆（Invertible），这要求有足够多（在二维里是 2 个）线性无关特征向量作为列；该矩阵做不到，所以它不可对角化。</p>
</li>
</ul>
<p>把这个定理放回二次型就能立刻看出“交叉项为什么会消失”。令坐标变换 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}=Q^\top\mathbf{x}\)</span>（把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 在特征向量基下的坐标记作 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span>），则</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=\mathbf{x}^\top(Q\Lambda Q^\top)\mathbf{x}=\mathbf{y}^\top\Lambda\mathbf{y}=\sum_{i=1}^{n}\lambda_i y_i^2\]</span>
<p>注意这里同时出现了两种“换坐标”的写法：线性变换里常写 <span displaypfx="inline-" class="mathjax-container">\(Q^{-1}AQ\)</span>（相似变换（Similarity Transformation）），二次型里写 <span displaypfx="inline-" class="mathjax-container">\(Q^\top A Q\)</span>（合同变换（Congruence Transformation））。对正交矩阵而言 <span displaypfx="inline-" class="mathjax-container">\(Q^{-1}=Q^\top\)</span>，所以它们在这里完全一致：同一个正交变换既给出特征值分解，也把二次型化到没有交叉项的对角标准型。</p>
<p>因此：在对称矩阵的情形，“换到特征向量基”与“把二次型旋转到主轴”是同一件事，只是用不同语言描述。</p>
<p>直观上，特征向量（Eigenvector）给出“主轴方向”（把坐标轴转到这些方向后，交叉项会消失），特征值（Eigenvalue）则是标准型里各平方项前的系数。</p>
<p>详细例子：取</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}5 &amp; -2\\ -2 &amp; 5\end{bmatrix}\]</span>
<p>它是对称矩阵，因此可正交对角化。其特征值与一组单位特征向量可以取为：</p>
<span displaypfx="" class="mathjax-container">\[\lambda_1=3,\ \mathbf{v}_1=\frac{1}{\sqrt{2}}\begin{bmatrix}1\\ 1\end{bmatrix};\quad \lambda_2=7,\ \mathbf{v}_2=\frac{1}{\sqrt{2}}\begin{bmatrix}1\\ -1\end{bmatrix}\]</span>
<p>把它们组成正交矩阵与对角矩阵：</p>
<span displaypfx="" class="mathjax-container">\[Q=[\mathbf{v}_1\ \mathbf{v}_2]=\frac{1}{\sqrt{2}}\begin{bmatrix}1 &amp; 1\\ 1 &amp; -1\end{bmatrix},\quad \Lambda=\begin{bmatrix}3 &amp; 0\\ 0 &amp; 7\end{bmatrix}\]</span>
<p>则 <span displaypfx="inline-" class="mathjax-container">\(A=Q\Lambda Q^\top\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 不是任意新变量，而是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 在特征向量基 <span displaypfx="inline-" class="mathjax-container">\(\{\mathbf{v}_1,\mathbf{v}_2\}\)</span> 下的坐标： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=y_1\mathbf{v}_1+y_2\mathbf{v}_2=Q\mathbf{y}\)</span>。</p>
<p>由于 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 是正交矩阵（<span displaypfx="inline-" class="mathjax-container">\(Q^\top Q=I\)</span>），左乘 <span displaypfx="inline-" class="mathjax-container">\(Q^\top\)</span> 可得 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}=Q^\top\mathbf{x}\)</span>。这一步就是换基（Change of Basis）：把向量从标准坐标系表达改写为主轴坐标系表达。</p>
<p>因此可显式写出两组坐标关系：</p>
<span displaypfx="" class="mathjax-container">\[y_1=\frac{x_1+x_2}{\sqrt{2}},\quad y_2=\frac{x_1-x_2}{\sqrt{2}}\]</span>
<span displaypfx="" class="mathjax-container">\[x_1=\frac{y_1+y_2}{\sqrt{2}},\quad x_2=\frac{y_1-y_2}{\sqrt{2}}\]</span>
<p>代入标准型：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=\mathbf{y}^\top\Lambda\mathbf{y}=3y_1^2+7y_2^2\]</span>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 用 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 展开，可直接验证“交叉项被旋转消掉”：</p>
<span displaypfx="" class="mathjax-container">\[3y_1^2+7y_2^2=\frac{3}{2}(x_1+x_2)^2+\frac{7}{2}(x_1-x_2)^2=5x_1^2-4x_1x_2+5x_2^2\]</span>
<p>数值校验：取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(1,2)^\top\)</span>，原式为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}=17\)</span>；而 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}=Q^\top\mathbf{x}=\left(\frac{3}{\sqrt{2}},-\frac{1}{\sqrt{2}}\right)^\top\)</span>，代入 <span displaypfx="inline-" class="mathjax-container">\(3y_1^2+7y_2^2\)</span> 同样得到 17。</p>
<p>几何解释：正交矩阵 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 表示旋转/换基，把坐标轴对齐到“主轴方向”（特征向量）；对角矩阵 <span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span> 表示沿主轴的逐轴缩放（由特征值控制）。因此等值线在 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 坐标系里与轴对齐，形状由 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span> 决定。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span> 中既有正特征值也有负特征值，则二次型是不定的（Indefinite）：沿某些方向 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 增大，沿另一些方向 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 减小。二维里它的等值线（Level Set）典型呈双曲线（Hyperbola）形状，优化里对应鞍点（Saddle Point）结构。例：令 <span displaypfx="inline-" class="mathjax-container">\(A=\begin{bmatrix}1 &amp; 2\\ 2 &amp; 1\end{bmatrix}\)</span>，其特征值为 3 与 -1，在某个正交坐标 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 下有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^\top A\mathbf{x}=3y_1^2-y_2^2\)</span>，可取正也可取负。</p>
<p>进一步允许一般可逆线性变换（不要求是旋转）时，可把对角项缩放为 <span displaypfx="inline-" class="mathjax-container">\(+1,-1,0\)</span>（Sylvester 惯性定理（Law of Inertia））：二次型被分解为若干正平方项、负平方项与零方向，其中正/负/零项的个数在合同变换下保持不变（换言之，“正方向有几个、负方向有几个、平坦方向有几个”是坐标变换改不掉的性质）；正/负项的个数也称为签名（Signature）。在优化里，它们分别对应局部最小、鞍点与平坦方向。</p>
<div class="blog_h3"><span class="graybg">二次型与机器学习（Quadratic Form in Machine Learning）</span></div>
<p>下面这些场景看起来不同，但核心都在计算“某个方向上的能量/代价”：给一个向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span>，二次型 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}^\top A\mathbf{v}\)</span> 会告诉你它在矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 定义的几何里有多大、代价有多高。</p>
<ul>
<li>平方范数（Squared <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> Norm）：<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_2^2=\mathbf{x}^\top I\mathbf{x}\)</span>。直白地说，它就是“向量长度的平方”，在训练里常作为最基础的“大小惩罚”。例如权重衰减（Weight Decay）把过大的参数拉回去，本质是在最小化 <span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_2^2\)</span> 这种二次型。</li>
<li>最小二乘与二次损失（Least Squares / MSE）：线性回归目标 <span displaypfx="inline-" class="mathjax-container">\(\|X\mathbf{w}-\mathbf{y}\|_2^2\)</span> 展开后是关于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 的二次型。通俗理解：模型每偏一点，代价按“平方”增长，所以大误差会被更重惩罚。它的闭式解来自正规方程（Normal Equations）<span displaypfx="inline-" class="mathjax-container">\(X^\top X\mathbf{w}=X^\top\mathbf{y}\)</span>。</li>
<li>PCA（Principal Component Analysis）：对中心化（Centering）数据，方向 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 上的方差是 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}^\top\Sigma\mathbf{u}\)</span>。这句话的直觉是：“把数据投影到某个方向后，能展开多宽”。PCA 就是在所有单位方向里找让这个二次型最大的方向（主成分），因此主成分就是“信息最密集”的方向。</li>
<li>马氏距离（Mahalanobis Distance）与高斯负对数似然（Gaussian NLL）：核心项是 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{x}-\boldsymbol{\mu})^\top\Sigma^{-1}(\mathbf{x}-\boldsymbol{\mu})\)</span>。可以把它理解成“先按数据真实尺度做校正，再测距离”：方差大的方向偏离一点不算太异常，方差小的方向偏离同样大小则更异常。异常检测（Anomaly Detection）和高斯判别模型都依赖这个量。</li>
<li>二阶近似与优化曲率（Second-order Approximation / Curvature）：在参数点附近，损失变化可写成 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\Delta^\top H\Delta\)</span>。它告诉你“往哪个方向走会涨得快/慢”：特征值大表示该方向很陡，特征值小表示平坦，正负混合则是鞍点（Saddle）。这也是为什么牛顿法、预条件（Preconditioning）和学习率调度都在关心 Hessian 的谱结构。</li>
</ul>
<p>在机器学习的实现层面，二次型也常先通过可逆变量替换 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=T\mathbf{y}\)</span> 化到更易计算的表示： <span displaypfx="inline-" class="mathjax-container">\(q(\mathbf{x})=\mathbf{x}^\top A\mathbf{x}=\mathbf{y}^\top(T^\top A T)\mathbf{y}\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>，原坐标有 <span displaypfx="inline-" class="mathjax-container">\(\nabla_{\mathbf{x}}q=2A\mathbf{x}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\nabla^2_{\mathbf{x}}q=2A\)</span>；在新坐标下 <span displaypfx="inline-" class="mathjax-container">\(\nabla_{\mathbf{y}}q=2A'\mathbf{y}\)</span>（<span displaypfx="inline-" class="mathjax-container">\(A'=T^\top A T\)</span>）。若进一步化到对角标准型 <span displaypfx="inline-" class="mathjax-container">\(A'=\Lambda\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial q}{\partial y_i}=2\lambda_i y_i\)</span>，逐坐标解耦，推导与实现都会更直接。</p>
<p>这里要区分“性质不变”和“数值不变”：在可逆变量替换下，正定/半正定/不定性质与正负零方向个数（惯性（Inertia））保持不变，因此局部最小/鞍点等优化结构不变；但一般合同变换 <span displaypfx="inline-" class="mathjax-container">\(T^\top A T\)</span> 不要求逐个保留特征值数值，只有正交相似变换 <span displaypfx="inline-" class="mathjax-container">\(Q^\top A Q\)</span> 才逐个保留特征值。</p>
<div class="blog_h2"><span class="graybg">对角矩阵（Diagonal Matrix）</span></div>
<p>对角矩阵（Diagonal Matrix）是只有对角线元素可能非零的方阵。写作 <span displaypfx="inline-" class="mathjax-container">\(D=\mathrm{diag}(d_1,\ldots,d_n)\)</span>，其非对角元素全为 0：</p>
<span displaypfx="" class="mathjax-container">\[D=\begin{bmatrix}d_1 &amp; 0 &amp; \cdots &amp; 0\\ 0 &amp; d_2 &amp; \cdots &amp; 0\\ \vdots &amp; \vdots &amp; \ddots &amp; \vdots\\ 0 &amp; 0 &amp; \cdots &amp; d_n\end{bmatrix}\]</span>
<p>对角矩阵乘以向量等价于“逐维缩放”：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,\ldots,x_n)^\top\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(D\mathbf{x}=(d_1x_1,\ldots,d_nx_n)^\top\)</span>。例：令 <span displaypfx="inline-" class="mathjax-container">\(D=\mathrm{diag}(2,0.5)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,4)^\top\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(D\mathbf{x}=(6,2)^\top\)</span>。</p>
<p>在 AI 里，对角矩阵最常见的用途是把“逐元素缩放”写成线性算子：例如 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\odot \mathbf{s}=\mathrm{diag}(\mathbf{s})\mathbf{x}\)</span>。很多优化器的自适应学习率也可以视为对角预条件（Diagonal Preconditioner）：例如 Adam/Adagrad 里的 <span displaypfx="inline-" class="mathjax-container">\(1/\sqrt{v+\epsilon}\)</span> 本质上是按参数维度缩放梯度。</p>
<div class="blog_h2"><span class="graybg">单位矩阵（Identity Matrix）</span></div>
<p>单位矩阵（Identity Matrix）<span displaypfx="inline-" class="mathjax-container">\(I_n\)</span> 是对角线上全为 1、其他元素为 0 的方阵：</p>
<span displaypfx="" class="mathjax-container">\[I_n=\begin{bmatrix}1 &amp; 0 &amp; \cdots &amp; 0\\ 0 &amp; 1 &amp; \cdots &amp; 0\\ \vdots &amp; \vdots &amp; \ddots &amp; \vdots\\ 0 &amp; 0 &amp; \cdots &amp; 1\end{bmatrix}\]</span>
<p>它是矩阵乘法的单位元：对任意形状匹配的矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(AI=IA=A\)</span>；对任意向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(I\mathbf{x}=\mathbf{x}\)</span>。例：若 <span displaypfx="inline-" class="mathjax-container">\(I_2=\begin{bmatrix}1 &amp; 0\\ 0 &amp; 1\end{bmatrix}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(I_2(3,4)^\top=(3,4)^\top\)</span>。</p>
<p>在 AI/数值计算里，<span displaypfx="inline-" class="mathjax-container">\(A+\lambda I\)</span>（<span displaypfx="inline-" class="mathjax-container">\(\lambda&gt;0\)</span>）用于改善条件数、提高可逆性与数值稳定性：例如岭回归（Ridge Regression）把 <span displaypfx="inline-" class="mathjax-container">\(X^\top X\)</span> 替换为 <span displaypfx="inline-" class="mathjax-container">\(X^\top X+\lambda I\)</span>；在高斯模型与协方差估计里常见 <span displaypfx="inline-" class="mathjax-container">\(\Sigma+\epsilon I\)</span> 来保证 Cholesky 分解可用。</p>
<div class="blog_h2"><span class="graybg">对称矩阵（Symmetric Matrix）</span></div>
<p>对称矩阵（Symmetric Matrix）是满足 <span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span> 的实方阵，即 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}=A_{ji}\)</span>。直观上，它的上三角与下三角互为镜像。</p>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(\begin{bmatrix}2 &amp; 1\\ 1 &amp; 3\end{bmatrix}\)</span> 是对称矩阵；而 <span displaypfx="inline-" class="mathjax-container">\(\begin{bmatrix}2 &amp; 1\\ 0 &amp; 3\end{bmatrix}\)</span> 不是，因为非对角元素不成对相等。</p>
<p>对称矩阵拥有更“干净”的谱结构：所有特征值都是实数，并且可正交对角化（Spectral Theorem）：<span displaypfx="inline-" class="mathjax-container">\(A=Q\Lambda Q^\top\)</span>（<span displaypfx="inline-" class="mathjax-container">\(Q^\top Q=I\)</span>）。这使得很多推导都可以在旋转后的坐标系里逐维分析二次型、曲率与能量。</p>
<p>在 AI 里，对称矩阵高频出现于：</p>
<ul>
<li>协方差矩阵（Covariance Matrix）<span displaypfx="inline-" class="mathjax-container">\(\Sigma\)</span>：例如高斯模型与特征白化（Whitening）里，要求 <span displaypfx="inline-" class="mathjax-container">\(\Sigma\succeq 0\)</span>，并常用 <span displaypfx="inline-" class="mathjax-container">\(\Sigma+\epsilon I\)</span> 保证数值稳定。</li>
<li>Gram 矩阵（Gram Matrix）<span displaypfx="inline-" class="mathjax-container">\(X^\top X\)</span> 与核矩阵（Kernel Matrix）<span displaypfx="inline-" class="mathjax-container">\(K\)</span>：它们天然对称/半正定，是最小二乘、岭回归与核方法的核心对象。</li>
<li>海森矩阵（Hessian）：当目标函数二阶连续可导时，Hessian 对称；其特征值决定局部曲率，从而决定“极小/极大/鞍点”的类型与优化难度。</li>
</ul>
<div class="blog_h2"><span class="graybg">厄米矩阵（Hermitian Matrix）</span></div>
<p>厄米矩阵（Hermitian Matrix）是复数域上与对称矩阵对应的概念。若复矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{C}^{n\times n}\)</span> 满足</p>
<span displaypfx="" class="mathjax-container">\[A=A^\ast\]</span>
<p>则称 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 为厄米矩阵，其中 <span displaypfx="inline-" class="mathjax-container">\(A^\ast\)</span> 表示共轭转置（Conjugate Transpose）：先转置，再对每个元素取复共轭。因此，厄米矩阵满足按元素关系 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}=\overline{A_{ji}}\)</span>。可以把它直接理解为<span style="background-color: #c0c0c0;">复数版本的对称矩阵</span>。</p>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(\begin{bmatrix}1 &amp; 2+i\\ 2-i &amp; 3\end{bmatrix}\)</span> 是厄米矩阵，因为非对角元素互为复共轭，而对角线元素必须是实数。厄米矩阵保留了实对称矩阵最重要的好性质：特征值全为实数，并且可以被酉矩阵（Unitary Matrix）对角化。因此在复数信号处理、量子力学、复数优化与某些频域分析里，它扮演的角色与实对称矩阵在实数域中的角色完全对应。</p>
<div class="blog_h2"><span class="graybg">可逆矩阵与奇异矩阵（Invertible vs Singular）</span></div>
<p>可逆矩阵（Invertible Matrix）是存在逆矩阵的方阵：对 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{n\times n}\)</span>，若存在 <span displaypfx="inline-" class="mathjax-container">\(A^{-1}\)</span> 使得 <span displaypfx="inline-" class="mathjax-container">\(AA^{-1}=A^{-1}A=I\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 可逆；否则称 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 为奇异矩阵（Singular Matrix）。</p>
<p>等价判据（常用）：<span displaypfx="inline-" class="mathjax-container">\(A\)</span> 可逆 <span displaypfx="inline-" class="mathjax-container">\(\Leftrightarrow \det(A)\ne 0 \Leftrightarrow \mathrm{rank}(A)=n\)</span>（列向量线性无关）。</p>
<p>例（可逆）：令 <span displaypfx="inline-" class="mathjax-container">\(A=\begin{bmatrix}2 &amp; 1\\ 1 &amp; 1\end{bmatrix}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\det(A)=1\)</span>，并且 <span displaypfx="inline-" class="mathjax-container">\(A^{-1}=\begin{bmatrix}1 &amp; -1\\ -1 &amp; 2\end{bmatrix}\)</span>。</p>
<p>例（奇异）：令 <span displaypfx="inline-" class="mathjax-container">\(B=\begin{bmatrix}1 &amp; 2\\ 2 &amp; 4\end{bmatrix}\)</span>，第二行是第一行的 2 倍，因此秩为 1、行列式为 0，无法求逆。对应线性方程组 <span displaypfx="inline-" class="mathjax-container">\(B\mathbf{x}=\mathbf{b}\)</span> 可能无解（例如 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(3,5)^\top\)</span>），也可能有无穷多解（例如 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}=(3,6)^\top\)</span>）。</p>
<p>在 AI 里，奇异性最常出现在最小二乘与协方差：当特征共线、维度远大于样本数（<span displaypfx="inline-" class="mathjax-container">\(d\gg N\)</span>）时，<span displaypfx="inline-" class="mathjax-container">\(X^\top X\)</span> 往往奇异或病态（Ill-conditioned）。常见处理是正则化（<span displaypfx="inline-" class="mathjax-container">\(X^\top X+\lambda I\)</span>）或用 SVD/QR 求解并使用伪逆（Pseudoinverse）<span displaypfx="inline-" class="mathjax-container">\(A^+\)</span>。</p>
<p>实现上通常避免显式求 <span displaypfx="inline-" class="mathjax-container">\(A^{-1}\)</span>：更稳定的做法是直接求解 <span displaypfx="inline-" class="mathjax-container">\(A\mathbf{x}=\mathbf{b}\)</span>（Solve），或用分解（Cholesky / QR / SVD）替代。</p>
<div class="blog_h2"><span class="graybg">正交矩阵（Orthogonal Matrix）</span></div>
<p>正交矩阵（Orthogonal Matrix）是满足 <span displaypfx="inline-" class="mathjax-container">\(Q^\top Q=QQ^\top=I\)</span> 的实方阵。它的列向量（或行向量）构成一组正交标准基（Orthonormal Basis），因此保持长度与点积：对任意向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\(\|Q\mathbf{x}\|_2=\|\mathbf{x}\|_2\)</span>，对任意向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a},\mathbf{b}\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\((Q\mathbf{a})^\top(Q\mathbf{b})=\mathbf{a}^\top\mathbf{b}\)</span>。</p>
<p>正交矩阵的列向量不需要“沿着坐标轴方向”。要求只有一个：列向量两两正交且都是单位向量，也就是构成一组正交标准基。标准基（<span displaypfx="inline-" class="mathjax-container">\(\mathbf{e}_1,\mathbf{e}_2,\ldots\)</span>）只是其中最常用的一组。</p>
<p>例（旋转 45°）：令</p>
<span displaypfx="" class="mathjax-container">\[Q=\frac{1}{\sqrt{2}}\begin{bmatrix}1 &amp; -1\\ 1 &amp; 1\end{bmatrix}\]</span>
<p>它的两列分别是 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{\sqrt{2}}(1,1)^\top\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{\sqrt{2}}(-1,1)^\top\)</span>，都不与坐标轴对齐，但它们正交且单位长度，因此</p>
<span displaypfx="" class="mathjax-container">\[Q^\top Q=\frac{1}{2}\begin{bmatrix}1 &amp; 1\\ -1 &amp; 1\end{bmatrix}\begin{bmatrix}1 &amp; -1\\ 1 &amp; 1\end{bmatrix}=\begin{bmatrix}1 &amp; 0\\ 0 &amp; 1\end{bmatrix}=I\]</span>
<p>例（二维旋转 90°）：令</p>
<span displaypfx="" class="mathjax-container">\[R=\begin{bmatrix}0 &amp; -1\\ 1 &amp; 0\end{bmatrix},\quad R^\top=\begin{bmatrix}0 &amp; 1\\ -1 &amp; 0\end{bmatrix}\]</span>
<p>则</p>
<span displaypfx="" class="mathjax-container">\[R^\top R=RR^\top=\begin{bmatrix}1 &amp; 0\\ 0 &amp; 1\end{bmatrix}=I\]</span>
<p>在 AI 里，正交矩阵常用于正交初始化（Orthogonal Initialization）、QR 分解与正交约束参数化；核心目的是把谱范数控制在 1 附近，改善深层网络与 RNN 的数值稳定性。</p>
<div class="blog_h2"><span class="graybg">酉矩阵（Unitary Matrix）</span></div>
<p>酉矩阵（Unitary Matrix）是复数域上的“长度保持”线性变换。对复矩阵 <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{C}^{n\times n}\)</span>，若满足</p>
<span displaypfx="" class="mathjax-container">\[U^\ast U=UU^\ast=I\]</span>
<p>则称 <span displaypfx="inline-" class="mathjax-container">\(U\)</span> 为酉矩阵，其中 <span displaypfx="inline-" class="mathjax-container">\(U^\ast\)</span> 是共轭转置（Conjugate Transpose）。酉矩阵的列向量构成一组正交归一基（Orthonormal Basis），因此对任意向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 都有 <span displaypfx="inline-" class="mathjax-container">\(\|U\mathbf{x}\|_2=\|\mathbf{x}\|_2\)</span>。</p>
<p>实数域特例：当矩阵元素为实数时，酉矩阵退化为正交矩阵（Orthogonal Matrix），即满足 <span displaypfx="inline-" class="mathjax-container">\(Q^\top Q=QQ^\top=I\)</span>。</p>
<p>例（复数域）：令</p>
<span displaypfx="" class="mathjax-container">\[U=\begin{bmatrix}1 &amp; 0\\ 0 &amp; i\end{bmatrix},\quad U^\ast=\begin{bmatrix}1 &amp; 0\\ 0 &amp; -i\end{bmatrix}\]</span>
<p>则可直接计算：</p>
<span displaypfx="" class="mathjax-container">\[U^\ast U=\begin{bmatrix}1 &amp; 0\\ 0 &amp; -i\end{bmatrix}\begin{bmatrix}1 &amp; 0\\ 0 &amp; i\end{bmatrix}=\begin{bmatrix}1 &amp; 0\\ 0 &amp; 1\end{bmatrix}=I,\quad UU^\ast=\begin{bmatrix}1 &amp; 0\\ 0 &amp; i\end{bmatrix}\begin{bmatrix}1 &amp; 0\\ 0 &amp; -i\end{bmatrix}=I\]</span>
<p>在 AI 里，正交/酉矩阵常用于控制数值稳定性：例如正交初始化（Orthogonal Initialization）与正交/酉参数化可把谱范数压在 1 附近，缓解深层网络与 RNN 中的梯度爆炸/消失；一些长序列建模会使用 unitary/orthogonal RNN 来更好地传播长程信息。</p>
<div class="blog_h2"><span class="graybg">正定矩阵</span></div>
<p>正定矩阵（Positive Definite Matrix）把“二次型总是正”形式化。对对称矩阵（Symmetric Matrix）<span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>，若对任意非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\ne\mathbf{0}\)</span> 都有</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x} &gt; 0\]</span>
<p>则称 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 正定，记作 <span displaypfx="inline-" class="mathjax-container">\(A\succ 0\)</span>。若是 <span displaypfx="inline-" class="mathjax-container">\(\ge 0\)</span> 则为半正定（Positive Semi-Definite, PSD），记作 <span displaypfx="inline-" class="mathjax-container">\(A\succeq 0\)</span>。</p>
<p>几何上，把 <span displaypfx="inline-" class="mathjax-container">\(q(x,y)=\mathbf{x}^\top A\mathbf{x}\)</span> 画成 <span displaypfx="inline-" class="mathjax-container">\(z=q(x,y)\)</span> 的三维曲面时，正定对应“向上开口的碗”（椭圆抛物面（Elliptic Paraboloid））：原点是唯一最低点，任意非零方向都往上抬升。若没有交叉项（<span displaypfx="inline-" class="mathjax-container">\(bxy\)</span> 项为 0），等值线与坐标轴对齐；若有交叉项（<span displaypfx="inline-" class="mathjax-container">\(b\ne 0\)</span>），碗的主轴会旋转，但“向上碗”的本质不变。半正定则可能出现平坦方向（Flat Direction），典型形状是槽（Trough）而非严格碗底。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-pd-psd.png"><img class="alignnone size-full wp-image-40695" src="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-pd-psd.png" alt="matrix-pd-psd" width="100%" /></a></p>
<p>从特征值（Eigenvalues）角度看，对称矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的二次型类型可直接由特征值符号判别（下述以二维 <span displaypfx="inline-" class="mathjax-container">\(\lambda_1,\lambda_2\)</span> 为例）：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda_1&gt;0,\lambda_2&gt;0\)</span>：正定（Positive Definite, PD），向上开口碗。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda_1&lt;0,\lambda_2&lt;0\)</span>：负定（Negative Definite, ND），向下开口碗。</li>
<li>一个为 0、另一个大于 0：半正定（Positive Semi-Definite, PSD），出现平坦方向，形状更像槽。</li>
<li>一个为 0、另一个小于 0：半负定（Negative Semi-Definite, NSD），对应“倒槽”。</li>
<li>一正一负：不定（Indefinite），对应鞍面（Saddle Surface）。</li>
</ul>
<p>因此图里“有交叉项”并不改变正负定类型；它主要改变主轴方向（旋转等值线），而“是不是碗/槽/鞍”由特征值符号决定。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-nd-nsd.png"><img class="alignnone size-full wp-image-40703" src="https://blog.gmem.cc/wp-content/uploads/2026/03/matrix-nd-nsd.png" alt="matrix-nd-nsd" width="100%" /></a></p>
<p>例：令</p>
<span displaypfx="" class="mathjax-container">\[A=\begin{bmatrix}2 &amp; 1\\ 1 &amp; 2\end{bmatrix}\]</span>
<p>对任意 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,x_2)^\top\)</span>，有</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}^\top A\mathbf{x}=2x_1^2+2x_1x_2+2x_2^2=(x_1+x_2)^2+x_1^2+x_2^2&gt;0\quad (\mathbf{x}\ne\mathbf{0})\]</span>
<p>因此 <span displaypfx="inline-" class="mathjax-container">\(A\succ 0\)</span>。同时它的特征值为 3 与 1（均为正），并且存在 Cholesky 分解：</p>
<span displaypfx="" class="mathjax-container">\[A=LL^\top,\quad L=\begin{bmatrix}\sqrt{2} &amp; 0\\ \frac{1}{\sqrt{2}} &amp; \sqrt{\frac{3}{2}}\end{bmatrix}\]</span>
<p>等价刻画（常用）：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(A\succ 0\)</span> 当且仅当所有特征值 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i&gt;0\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(A\succ 0\)</span> 当且仅当存在 Cholesky 分解 <span displaypfx="inline-" class="mathjax-container">\(A=LL^\top\)</span>（<span displaypfx="inline-" class="mathjax-container">\(L\)</span> 下三角且对角为正）。</li>
</ul>
<p>它在优化里非常关键：若函数的海森矩阵（Hessian）在某点正定，则该点是严格局部极小；若 Hessian 半正定，则函数局部凸（Locally Convex）。</p>
<div class="blog_h2"><span class="graybg">特征值与特征向量</span></div>
<p>特征值（Eigenvalue）与特征向量（Eigenvector）描述线性变换的“固有方向”：若存在非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\ne\mathbf{0}\)</span> 与标量 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 使得</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{v}=\lambda \mathbf{v}\]</span>
<p>则 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span> 是特征向量，<span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 是对应特征值。几何上，沿特征向量方向的向量经过变换后方向不变，只被缩放（若 <span displaypfx="inline-" class="mathjax-container">\(\lambda&lt;0\)</span> 还会翻转）。</p>
<p>当矩阵可对角化（Diagonalizable）时，可写为 <span displaypfx="inline-" class="mathjax-container">\(A=V\Lambda V^{-1}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span> 是特征值对角矩阵，列向量 <span displaypfx="inline-" class="mathjax-container">\(V=[\mathbf{v}_1,\dots,\mathbf{v}_n]\)</span> 是特征向量。</p>
<p>对称矩阵（Symmetric Matrix）是最重要的特例：它的特征向量可取为一组正交归一基（Orthonormal Basis），因此</p>
<span displaypfx="" class="mathjax-container">\[A=Q\Lambda Q^\top,\quad Q^\top Q=I\]</span>
<p>这正是 PCA（Principal Component Analysis）等方法背后的谱分解（Spectral Decomposition）基础。</p>
<div class="blog_h2"><span class="graybg">谱、谱定理与谱范数</span></div>
<div class="blog_h3"><span class="graybg">谱（Spectrum）</span></div>
<p>矩阵的谱（Spectrum）指的是矩阵全部特征值构成的集合。对方阵 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{C}^{n\times n}\)</span>，常记为 <span displaypfx="inline-" class="mathjax-container">\(\sigma(A)\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\sigma(A)=\{\lambda\in\mathbb{C}\mid \det(A-\lambda I)=0\}\]</span>
<p>这里之所以会把行列式（Determinant）扯进来，是因为特征值定义本身就会自然导向这个条件。若 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 是特征值，按定义必须存在某个非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\ne \mathbf{0}\)</span> 使得</p>
<span displaypfx="" class="mathjax-container">\[A\mathbf{v}=\lambda \mathbf{v}\]</span>
<p>把右边移到左边，就是</p>
<span displaypfx="" class="mathjax-container">\[(A-\lambda I)\mathbf{v}=\mathbf{0}\]</span>
<p>这说明：矩阵 <span displaypfx="inline-" class="mathjax-container">\(A-\lambda I\)</span> 对应的齐次线性方程组必须有<span style="background-color: #c0c0c0;">非零解</span>。而线性代数里，一个方阵齐次方程组有非零解，当且仅当该矩阵不可逆；对方阵而言，不可逆又等价于行列式为 0。因此就得到</p>
<span displaypfx="" class="mathjax-container">\[\det(A-\lambda I)=0\]</span>
<p>也就是说，行列式在这里不是额外硬塞进来的工具，而是“特征向量存在非零解”这一要求的等价写法。解这个方程得到的多项式根，就是矩阵的全部特征值。</p>
<p>这个定义的关键不是“列出所有特征值”本身，而是把矩阵最核心的线性作用浓缩成一组标量。许多稳定性、可逆性、收敛性与几何性质，最终都可以回到谱上来判断。例：若 <span displaypfx="inline-" class="mathjax-container">\(0\in \sigma(A)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 不可逆；若所有特征值都为正，且 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 对称，则它是正定矩阵。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/spectrum.png"><img class="alignnone size-full wp-image-42063" src="https://blog.gmem.cc/wp-content/uploads/2026/03/spectrum.png" alt="spectrum" width="1024" height="1024" /></a></p>
<p>与谱紧密相关、但不能混为一谈的另一个概念是谱半径（Spectral Radius）：</p>
<span displaypfx="" class="mathjax-container">\[\rho(A)=\max_{\lambda\in\sigma(A)}|\lambda|\]</span>
<p>它只取特征值模长中的最大者。谱是整个特征值集合，谱半径只是其中的最大模长摘要。在线性迭代、RNN 稳定性与幂法（Power Method）里，谱半径尤其常见。</p>
<div class="blog_h3"><span class="graybg">谱定理（Spectral Theorem）</span></div>
<p>谱定理（Spectral Theorem）是线性代数里最重要的结构定理之一。对实对称矩阵 <span displaypfx="inline-" class="mathjax-container">\(A=A^\top\)</span>，存在正交矩阵 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 与实对角矩阵 <span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span>，使得</p>
<span displaypfx="" class="mathjax-container">\[A=Q\Lambda Q^\top\]</span>
<p>等价地说， <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 的列向量是一组两两正交的单位特征向量， <span displaypfx="inline-" class="mathjax-container">\(\Lambda\)</span> 的对角线上放着对应特征值。这条定理的含义非常深：对称矩阵不仅“有特征值”，而且它的全部作用都可以被解释为<span style="background-color: #c0c0c0;">先换到特征向量基，再按各坐标轴独立缩放</span>。这也是为什么二次型、协方差矩阵、Hessian、图 Laplacian 等对象一旦对称，分析就会变得异常清爽。</p>
<p>在复数域上，对应版本是厄米矩阵（Hermitian Matrix）<span displaypfx="inline-" class="mathjax-container">\(A=A^\ast\)</span> 可被酉矩阵（Unitary Matrix）对角化：</p>
<span displaypfx="" class="mathjax-container">\[A=U\Lambda U^\ast\]</span>
<p>这类“可由一组正交 / 酉基完全分解”的矩阵，在数值上更稳定、几何上更透明，也正因此成为机器学习里最重要的一类矩阵对象。</p>
<div class="blog_h3"><span class="graybg">谱范数（Spectral Norm）</span></div>
<p>谱范数（Spectral Norm）记作 <span displaypfx="inline-" class="mathjax-container">\(\|A\|_2\)</span>，定义为矩阵对单位向量能造成的最大放大倍数：</p>
<span displaypfx="" class="mathjax-container">\[\|A\|_2=\max_{\|\mathbf{x}\|_2=1}\|A\mathbf{x}\|_2\]</span>
<p>这一定义直接揭示了它的几何意义：找出所有长度为 1 的输入向量，看矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 最多能把哪一个方向拉得最长。对任意矩阵，都有</p>
<span displaypfx="" class="mathjax-container">\[\|A\|_2=\sigma_{\max}(A)=\sqrt{\lambda_{\max}(A^\top A)}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\sigma_{\max}(A)\)</span> 是最大奇异值。若 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 还是实对称矩阵，则奇异值等于特征值绝对值，于是谱范数进一步化为</p>
<span displaypfx="" class="mathjax-container">\[\|A\|_2=\max_i |\lambda_i(A)|\]</span>
<p>因此，对一般矩阵要通过奇异值来理解谱范数；对对称矩阵，则可以直接通过特征值来理解。要特别区分：谱范数与谱半径在对称 / 正规矩阵上常常一致，但对一般非正规矩阵并不总相同。</p>
<p>在 AI 中，谱范数最常用于表达“局部放大能力”与 Lipschitz 常数控制。若某层线性映射的谱范数过大，它会把某些方向的扰动显著放大，进而加重训练不稳定、梯度爆炸或对抗脆弱性；这也是谱归一化（Spectral Normalization）、正交初始化与某些鲁棒优化方法反复关注它的原因。</p>
<div class="blog_h2"><span class="graybg">奇异值分解（SVD）</span></div>
<p>奇异值分解（SVD, Singular Value Decomposition）对任意矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{m\times n}\)</span> 都成立：</p>
<span displaypfx="" class="mathjax-container">\[A=U\Sigma V^\top\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{R}^{m\times m}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(V\in\mathbb{R}^{n\times n}\)</span> 是正交矩阵（Orthogonal Matrix），<span displaypfx="inline-" class="mathjax-container">\(\Sigma\in\mathbb{R}^{m\times n}\)</span> 是对角（更准确说是“对角形”）矩阵，其对角线元素 <span displaypfx="inline-" class="mathjax-container">\(\sigma_1\ge\sigma_2\ge\cdots\ge 0\)</span> 为奇异值（Singular Values）。</p>
<p>几何解释非常直接：<span style="background-color: #c0c0c0;">先用 <span displaypfx="inline-" class="mathjax-container">\(V^\top\)</span> 旋转/换基，再用 <span displaypfx="inline-" class="mathjax-container">\(\Sigma\)</span> 沿坐标轴缩放，最后用 <span displaypfx="inline-" class="mathjax-container">\(U\)</span> 再旋转</span>。因此 SVD 是“旋转-缩放-旋转”的标准分解。</p>
<p>SVD 与特征值的关系要分左右两侧一起看：</p>
<ul>
<li>右奇异向量（Right Singular Vectors）是 <span displaypfx="inline-" class="mathjax-container">\(A^\top A\)</span> 的特征向量，即 <span displaypfx="inline-" class="mathjax-container">\(A^\top A\mathbf{v}_i=\sigma_i^2\mathbf{v}_i\)</span>。</li>
<li>左奇异向量（Left Singular Vectors）是 <span displaypfx="inline-" class="mathjax-container">\(AA^\top\)</span> 的特征向量，即 <span displaypfx="inline-" class="mathjax-container">\(AA^\top\mathbf{u}_i=\sigma_i^2\mathbf{u}_i\)</span>。</li>
</ul>
<p>其中<span displaypfx="inline-" class="mathjax-container">\(\sigma_i^2\)</span> 是 <span displaypfx="inline-" class="mathjax-container">\(A^\top A\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(AA^\top\)</span> 的特征值；<span displaypfx="inline-" class="mathjax-container">\(\sigma_i\)</span> 为奇异值（即这些非零特征值的平方根）。</p>
<p>矩阵形式分别是 <span displaypfx="inline-" class="mathjax-container">\(A^\top A=V\Sigma^\top\Sigma V^\top\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(AA^\top=U\Sigma\Sigma^\top U^\top\)</span>，因此非零奇异值满足 <span displaypfx="inline-" class="mathjax-container">\(\sigma_i=\sqrt{\lambda_i(A^\top A)}=\sqrt{\lambda_i(AA^\top)}\)</span>。这也解释了为什么奇异值总是非负，而一般矩阵的特征值可以为负甚至为复数。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(\sigma_i&gt;0\)</span>，左右奇异向量还可互相对应： <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}_i=\frac{A\mathbf{v}_i}{\sigma_i}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}_i=\frac{A^\top\mathbf{u}_i}{\sigma_i}\)</span>。</p>
<p>特殊地，若 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 是对称矩阵，则它可正交对角化，此时奇异值等于特征值的绝对值：<span displaypfx="inline-" class="mathjax-container">\(\sigma_i=|\lambda_i(A)|\)</span>；若 <span displaypfx="inline-" class="mathjax-container">\(A\succeq 0\)</span>（半正定），则 <span displaypfx="inline-" class="mathjax-container">\(\sigma_i=\lambda_i(A)\)</span>。</p>
<p>为什么 SVD 可用于压缩与降维（Compression &amp; Dimensionality Reduction）？因为很多数据/权重矩阵在有效意义下是低秩（Low-rank）的：只有前几个 <span displaypfx="inline-" class="mathjax-container">\(\sigma_i\)</span> 很大，后面的奇异值接近 0。保留前 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 项得到秩 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 近似：</p>
<span displaypfx="" class="mathjax-container">\[A_k=U_{(:,1:k)}\Sigma_{(1:k,1:k)}V_{(:,1:k)}^\top\]</span>
<p>它在 Frobenius 范数（Frobenius Norm）与谱范数（Spectral Norm）意义下都是最优的秩 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 近似（Eckart–Young 定理）：用最少的信息保留最大的能量（由奇异值平方决定）。</p>
<p>在 PCA 中，对中心化数据矩阵 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 做 SVD： <span displaypfx="inline-" class="mathjax-container">\(X=U\Sigma V^\top\)</span>，右奇异向量 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 给出主方向（Principal Directions），奇异值刻画各方向的方差贡献（Variance Explained）。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/svd.png"><img class="alignnone size-full wp-image-42053" src="https://blog.gmem.cc/wp-content/uploads/2026/03/svd.png" alt="svd" width="1024" height="1024" /></a></p>
<div class="blog_h2"><span class="graybg">范数（L0 / L1 / L2 / L∞）</span></div>
<p>范数（Norm）刻画“向量大小”。在 AI 中，它最常出现在三类地方：距离度量（Distance Metric）、正则化（Regularization）与鲁棒性约束（Robustness Constraint）。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\in\mathbb{R}^d\)</span>，当 <span displaypfx="inline-" class="mathjax-container">\(p\ge 1\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(L_p\)</span> 范数定义为</p>
<span displaypfx="" class="mathjax-container">\[\|\mathbf{x}\|_p=\left(\sum_{i=1}^{d}|x_i|^p\right)^{1/p},\quad p\ge 1\]</span>
<p>并且 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_\infty=\max_i |x_i|=\lim_{p\to\infty}\|\mathbf{x}\|_p\)</span>。</p>
<div class="blog_h3"><span class="graybg">L0 “范数”（L0 “norm”）</span></div>
<p><span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_0\)</span> 定义为非零分量的个数：</p>
<span displaypfx="" class="mathjax-container">\[\|\mathbf{x}\|_0=\#\{i\mid x_i\ne 0\}\]</span>
<p>严格来说它不是范数：例如对任意非零标量 <span displaypfx="inline-" class="mathjax-container">\(\alpha\ne 0\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(\|\alpha\mathbf{x}\|_0=\|\mathbf{x}\|_0\)</span>，不满足齐次性（Homogeneity）<span displaypfx="inline-" class="mathjax-container">\(\|\alpha\mathbf{x}\|=|\alpha|\|\mathbf{x}\|\)</span>。</p>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,0,-1,0)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_0=2\)</span>。</p>
<p>在 AI 里，<span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_0\)</span> 用来表达稀疏性（Sparsity）：特征选择（Feature Selection）、压缩感知（Compressed Sensing）与网络剪枝（Pruning）常以“非零个数最少”为目标。但直接优化 <span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_0\)</span> 一般是组合优化，常用 <span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_1\)</span> 或其他可优化的替代目标近似。</p>
<div class="blog_h3"><span class="graybg">L1 范数（L1 Norm）</span></div>
<span displaypfx="" class="mathjax-container">\[\|\mathbf{x}\|_1=\sum_{i=1}^{d}|x_i|\]</span>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,4)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_1=7\)</span>。几何上，二维 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 等值线是菱形；与 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 的圆相比，它更容易在坐标轴上产生“尖角”，对应优化时更容易把部分坐标推到 0。</p>
<p>在 AI 里，<span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 正则化是稀疏学习（Sparse Learning）的标准工具：在凸模型中会得到稀疏解（例如 Lasso）；在深度模型中也常用于诱导稀疏权重/稀疏特征、做轻量化。</p>
<div class="blog_h3"><span class="graybg">L2 范数（L2 Norm）</span></div>
<span displaypfx="" class="mathjax-container">\[\|\mathbf{x}\|_2=\sqrt{\sum_{i=1}^{d}x_i^2}\]</span>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,4)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_2=5\)</span>，对应欧氏距离（Euclidean Distance）。在连续优化中，<span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_2^2\)</span> 具有良好的光滑性（Smoothness），使得许多推导与数值计算更稳定。</p>
<p>在 AI 里，<span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 正则化（权重衰减（Weight Decay））会惩罚大权重、改善泛化并缓解病态问题；在二次目标里它也对应“加 <span displaypfx="inline-" class="mathjax-container">\(\lambda I\)</span>”的稳定化（例如岭回归）。</p>
<div class="blog_h3"><span class="graybg">L∞ 范数（L-infinity Norm）</span></div>
<span displaypfx="" class="mathjax-container">\[\|\mathbf{x}\|_\infty=\max_{i}|x_i|\]</span>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(3,0,-1,0)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{x}\|_\infty=3\)</span>。它度量的是“最大坐标幅度”。</p>
<p>在 AI 里，<span displaypfx="inline-" class="mathjax-container">\(L_\infty\)</span> 最常与鲁棒性相关：对抗样本（Adversarial Examples）中的 <span displaypfx="inline-" class="mathjax-container">\(L_\infty\)</span> 约束表示“每个像素的改动幅度不超过 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span>”；一些鲁棒优化与最坏情况界也会用 <span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_\infty\)</span> 表达最大误差。</p>
<div class="blog_h3"><span class="graybg">距离与正则化</span></div>
<p>由范数诱导的距离（Norm-induced Distance）写作 <span displaypfx="inline-" class="mathjax-container">\(d(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|\)</span>。常见对应关系：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(L_2\)</span>：欧氏距离（Euclidean Distance），<span displaypfx="inline-" class="mathjax-container">\(d_2(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|_2=\sqrt{\sum_{i=1}^{d}(x_i-y_i)^2}\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(L_1\)</span>：曼哈顿距离（Manhattan Distance），<span displaypfx="inline-" class="mathjax-container">\(d_1(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|_1=\sum_{i=1}^{d}|x_i-y_i|\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(L_\infty\)</span>：切比雪夫距离（Chebyshev Distance），<span displaypfx="inline-" class="mathjax-container">\(d_\infty(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|_\infty=\max_{i}|x_i-y_i|\)</span>。它度量的是“最坏维度”的偏差：例如 <span displaypfx="inline-" class="mathjax-container">\(d_\infty(\mathbf{x},\mathbf{y})\le \epsilon \Leftrightarrow \forall i,\ |x_i-y_i|\le \epsilon\)</span>，即每一维的误差都被同一个上界约束。直觉上，如果一次操作允许同时修改所有坐标、且每步每个坐标最多改 1，那么从 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 变到 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 的最少步数就是 <span displaypfx="inline-" class="mathjax-container">\(\max_i|x_i-y_i|\)</span>（更一般地，每步上限为 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 时步数为 <span displaypfx="inline-" class="mathjax-container">\(d_\infty(\mathbf{x},\mathbf{y})/\epsilon\)</span> 的向上取整）。</li>
</ul>
<p>关于 <span displaypfx="inline-" class="mathjax-container">\(L_0\)</span>：常见写法是 <span displaypfx="inline-" class="mathjax-container">\(d_0(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|_0=\#\{i\mid x_i\ne y_i\}\)</span>，它统计两向量在多少个坐标上不相等。本质上这是逐坐标“相等/不相等”的计数度量；当取值来自离散集合时，它对应哈明距离（Hamming Distance）。但 <span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_0\)</span> 严格来说不是范数，因此 <span displaypfx="inline-" class="mathjax-container">\(d_0\)</span> 不属于“由范数诱导”的距离家族。</p>
<p>在学习目标中，正则化通常写成：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\theta}\ \frac{1}{m}\sum_{i=1}^{m}\ell\!\left(f_{\theta}(x^{(i)}),y^{(i)}\right)+\lambda\Omega(\theta)\]</span>
<p>式中各成分含义如下：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>：模型参数（Parameters），优化的对象。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(m\)</span>：训练样本数（Number of Samples）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\((x^{(i)},y^{(i)})\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本与标签。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f_{\theta}(x^{(i)})\)</span>：模型对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本的预测。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\ell(\cdot,\cdot)\)</span>：单样本损失函数（Per-sample Loss），衡量预测与标签偏差。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{m}\sum_{i=1}^{m}\ell(\cdot)\)</span>：经验风险（Empirical Risk），即平均训练误差。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\Omega(\theta)\)</span>：正则项（Regularizer），约束参数复杂度。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span>：正则化系数（Regularization Strength），平衡“拟合训练数据”与“控制模型复杂度”。</li>
</ul>
<p>常见的 <span displaypfx="inline-" class="mathjax-container">\(\Omega(\theta)\)</span> 包括 <span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_1\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_2^2\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_0\)</span>（稀疏性目标）与 <span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_\infty\)</span>（最大幅度约束）。同一个思想也可写成“约束形式”（Constraint Form）：<span displaypfx="inline-" class="mathjax-container">\(\min_\theta \frac{1}{m}\sum_i \ell(\cdot)\ \text{s.t.}\ \Omega(\theta)\le c\)</span>。惩罚系数 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 与约束半径 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 在凸优化（Convex Optimization）里可通过对偶（Duality）联系起来。</p>
<div class="blog_h1"><span class="graybg">微积分</span></div>
<div class="blog_h2"><span class="graybg">极限与连续</span></div>
<div class="blog_h3"><span class="graybg">极限的定义</span></div>
<p>极限（Limit）回答的问题是：当输入“逼近”某个值时，函数输出“逼近”什么值。它关心的是趋势而不是是否刚好取到该点。</p>
<span displaypfx="" class="mathjax-container">\[\lim_{x\to a}f(x)=L\]</span>
<p>严格定义（<span displaypfx="inline-" class="mathjax-container">\(\varepsilon-\delta\)</span> 定义）可写为：</p>
<span displaypfx="" class="mathjax-container">\[\forall \varepsilon \gt 0,\ \exists \delta \gt 0,\ \text{s.t.}\ 0 \lt |x-a| \lt \delta \Rightarrow |f(x)-L| \lt \varepsilon\]</span>
<p>工程上可把它理解为：把输入控制得足够近，输出误差就能被压到任意小。</p>
<p>在 AI 里，极限直觉用于理解“收敛（Convergence）”：例如训练步长变小后，参数更新是否趋于稳定；以及损失函数在某点附近是否可被低阶展开近似。</p>
<div class="blog_h3"><span class="graybg">连续性</span></div>
<p>连续（Continuity）可理解为“函数图像没有跳断”。在点 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 处连续的三个条件是：</p>
<ol>
<li><span displaypfx="inline-" class="mathjax-container">\(f(a)\)</span> 有定义；</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lim_{x\to a}f(x)\)</span> 存在；</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lim_{x\to a}f(x)=f(a)\)</span>。</li>
</ol>
<p>连续是可导（Differentiable）的前提之一（但连续不必然可导）。例如 <span displaypfx="inline-" class="mathjax-container">\(|x|\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span> 连续但不可导。</p>
<p>在优化里，连续性保证“小步更新不会导致目标突变”，这也是学习率（Learning Rate）可调与训练可控的基础假设之一。</p>
<div class="blog_h3"><span class="graybg">无穷小与无穷大</span></div>
<p>无穷小（Infinitesimal）描述“趋近于 0 的量”，无穷大（Infinity）描述“无界增长”。在推导里常通过渐近记号（Asymptotic Notation）表达量级关系：</p>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(o\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 不是变量，而是两种记号：小 <span displaypfx="inline-" class="mathjax-container">\(o\)</span>（little-o）表示“严格更小一个量级”，大 <span displaypfx="inline-" class="mathjax-container">\(O\)</span>（big-O）表示“至多同量级的上界”。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(f(x)=o(g(x))\)</span>：当 <span displaypfx="inline-" class="mathjax-container">\(x\to a\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(f/g\to 0\)</span>（高阶小量，增长/衰减速度严格慢于 <span displaypfx="inline-" class="mathjax-container">\(g\)</span>）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f(x)=O(g(x))\)</span>：存在常数 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 使 <span displaypfx="inline-" class="mathjax-container">\(|f(x)|\le C|g(x)|\)</span>（同阶或更小的上界）。</li>
</ul>
<p>例如一阶 Taylor 展开里的 <span displaypfx="inline-" class="mathjax-container">\(o(\Delta x)\)</span> 表示“比 <span displaypfx="inline-" class="mathjax-container">\(\Delta x\)</span> 更小得多”的误差项。算法分析中的时间复杂度 <span displaypfx="inline-" class="mathjax-container">\(O(Nd)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(O(N^2)\)</span> 也属于同一套量级语言。</p>
<div class="blog_h2"><span class="graybg">常见求导法则与公式</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">法则</td>
<td style="text-align: center;">公式</td>
<td style="text-align: center;">条件</td>
</tr>
</thead>
<tbody>
<tr>
<td>常数倍法则</td>
<td><span displaypfx="inline-" class="mathjax-container">\((Cu)' = C u'\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(C\)</span> 为常数</td>
</tr>
<tr>
<td>和差法则</td>
<td><span displaypfx="inline-" class="mathjax-container">\((u \pm v)' = u' \pm v'\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(u,v\)</span> 可导</td>
</tr>
<tr>
<td>乘法法则</td>
<td><span displaypfx="inline-" class="mathjax-container">\((uv)' = u'v + uv'\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(u,v\)</span> 可导</td>
</tr>
<tr>
<td>除法法则</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\left(\frac{u}{v}\right)'=\frac{u'v-uv'}{v^2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(u,v\)</span> 可导，且 <span displaypfx="inline-" class="mathjax-container">\(v \ne 0\)</span></td>
</tr>
<tr>
<td>链式法则</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{d}{dx}f(g(x)) = f'(g(x))g'(x)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(f,g\)</span> 可导</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">常见函数导数</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">函数 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span></td>
<td style="text-align: center;">导数 <span displaypfx="inline-" class="mathjax-container">\(f'(x)\)</span></td>
<td style="text-align: center;">备注/条件</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(c\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(0\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(c\)</span> 为常数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x^n\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(n x^{n-1}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(n\)</span> 为常数；非整数 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 时需注意实数域定义域</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\sqrt{x}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2\sqrt{x}}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(e^x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^x\)</span></td>
<td>自然指数（Natural Exponential）</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(a^x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a^x\ln a\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(a&gt;0,a\ne 1\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{x}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\log_a x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{x\ln a}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0,\ a&gt;0,\ a\ne 1\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\cos x\)</span></td>
<td> </td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\cos x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(-\sin x\)</span></td>
<td> </td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\tan x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sec^2 x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\cos x\ne 0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(|x|\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathrm{sign}(x)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span> 时导数为 <span displaypfx="inline-" class="mathjax-container">\(1\)</span></p>
<p><span displaypfx="inline-" class="mathjax-container">\(x&lt;0\)</span> 时导数为 <span displaypfx="inline-" class="mathjax-container">\(-1\)</span></p>
<p>在 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span> 不可导（优化中常用次梯度 <span displaypfx="inline-" class="mathjax-container">\(g\in[-1,1]\)</span>）</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">导数（Derivative）</span></div>
<p>导数是点 <span displaypfx="inline-" class="mathjax-container">\(x_0\)</span> 处的瞬时变化率（Instantaneous Rate of Change）：</p>
<span displaypfx="" class="mathjax-container">\[f'(x_0)=\lim_{\Delta x\to 0}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(x_0\)</span> 不是“很小的数”，而是定义域中的固定位置；趋近于 0 的小量是 <span displaypfx="inline-" class="mathjax-container">\(\Delta x\)</span>（或记作 <span displaypfx="inline-" class="mathjax-container">\(dx\)</span>）。</p>
<p>一阶展开把导数解释为“线性项的系数”：当 <span displaypfx="inline-" class="mathjax-container">\(\Delta x\to 0\)</span> 时，</p>
<span displaypfx="" class="mathjax-container">\[f(x_0+\Delta x)=f(x_0)+f'(x_0)\Delta x+o(\Delta x)\]</span>
<p>可按“等于三项相加”理解：左边 <span displaypfx="inline-" class="mathjax-container">\(f(x_0+\Delta x)\)</span> 是扰动后的真实函数值；右边第一项 <span displaypfx="inline-" class="mathjax-container">\(f(x_0)\)</span> 是基点函数值，第二项 <span displaypfx="inline-" class="mathjax-container">\(f'(x_0)\Delta x\)</span> 是一阶线性近似，第三项 <span displaypfx="inline-" class="mathjax-container">\(o(\Delta x)\)</span> 是比 <span displaypfx="inline-" class="mathjax-container">\(\Delta x\)</span> 更小的高阶余项（High-order Remainder）。</p>
<div class="blog_h2"><span class="graybg">中值定理（Mean Value Theorems）</span></div>
<p>中值定理（Mean Value Theorems）是一组把“区间上的平均变化”与“某一点的瞬时变化率”连接起来的核心定理。它们在形式上不同，但共同结构是：在闭区间连续、在开区间可导，进而保证存在某个中间点 <span displaypfx="inline-" class="mathjax-container">\(c\in(a,b)\)</span> 使得斜率关系成立。</p>
<div class="blog_h3"><span class="graybg">罗尔中值定理</span></div>
<p>设 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\([a,b]\)</span> 上连续、在 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 上可导，且 <span displaypfx="inline-" class="mathjax-container">\(f(a)=f(b)\)</span>。则存在 <span displaypfx="inline-" class="mathjax-container">\(c\in(a,b)\)</span> 使</p>
<span displaypfx="" class="mathjax-container">\[f'(c)=0\]</span>
<p>几何直觉：若曲线两端高度相同，那么中间至少有一点的切线是水平的。</p>
<div class="blog_h3"><span class="graybg">拉格朗日中值定理</span></div>
<p>设 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\([a,b]\)</span> 上连续、在 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 上可导。则存在 <span displaypfx="inline-" class="mathjax-container">\(c\in(a,b)\)</span> 使</p>
<span displaypfx="" class="mathjax-container">\[f'(c)=\frac{f(b)-f(a)}{b-a}\]</span>
<p>几何直觉：区间割线斜率（平均变化率）等于某个点的切线斜率（瞬时变化率）。</p>
<div class="blog_h3"><span class="graybg">柯西中值定理</span></div>
<p>设 <span displaypfx="inline-" class="mathjax-container">\(f,g\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\([a,b]\)</span> 上连续、在 <span displaypfx="inline-" class="mathjax-container">\((a,b)\)</span> 上可导。则存在 <span displaypfx="inline-" class="mathjax-container">\(c\in(a,b)\)</span> 使</p>
<span displaypfx="" class="mathjax-container">\[(f(b)-f(a))g'(c)=(g(b)-g(a))f'(c)\]</span>
<p>当分母不为 0 时，可写成比值形式：</p>
<span displaypfx="" class="mathjax-container">\[\frac{f'(c)}{g'(c)}=\frac{f(b)-f(a)}{g(b)-g(a)}\]</span>
<p>它把“单函数斜率比较”推广为“两个函数变化率的比较”，是洛必达法则（L'Hospital's Rule）证明链中的关键步骤。</p>
<div class="blog_h3"><span class="graybg">三类中值定理的区别</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">定理</td>
<td style="text-align: center;">涉及函数</td>
<td style="text-align: center;">额外条件</td>
<td style="text-align: center; width: 40%;">结论形式</td>
<td style="text-align: center;">关系</td>
</tr>
</thead>
<tbody>
<tr>
<td>罗尔中值定理</td>
<td>1 个函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(f(a)=f(b)\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(f'(c)=0\)</span></td>
<td>拉格朗日定理的特殊情形</td>
</tr>
<tr>
<td>拉格朗日中值定理</td>
<td>1 个函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span></td>
<td>无额外端点相等条件</td>
<td><span displaypfx="inline-" class="mathjax-container">\(f'(c)=\frac{f(b)-f(a)}{b-a}\)</span></td>
<td>柯西定理取 <span displaypfx="inline-" class="mathjax-container">\(g(x)=x\)</span> 的特例</td>
</tr>
<tr>
<td>柯西中值定理</td>
<td>2 个函数 <span displaypfx="inline-" class="mathjax-container">\(f,g\)</span></td>
<td>两个函数都满足连续+可导</td>
<td><span displaypfx="inline-" class="mathjax-container">\((f(b)-f(a))g'(c)=(g(b)-g(a))f'(c)\)</span></td>
<td>三者中最一般形式</td>
</tr>
</tbody>
</table>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/mean-value-theorems.png"><img class="alignnone size-full wp-image-40785" src="https://blog.gmem.cc/wp-content/uploads/2026/03/mean-value-theorems.png" alt="mean-value-theorems" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">中值定理和AI</span></div>
<p>在 AI 系统中，中值定理主要作为理论工具出现：工程代码很少直接“调用定理”，但大量优化、稳定性与误差分析都在使用它的核心变形，即把“两个点的函数值差”转写为“某个中间点的导数（梯度）信息”。</p>
<ul>
<li>优化收敛分析（Optimization Convergence）：训练中常关心一步更新后损失变化 <span displaypfx="inline-" class="mathjax-container">\(L(\theta+\Delta)-L(\theta)\)</span>。中值定理把它连接到中间点的梯度，进而用于学习率条件与下降性证明。</li>
<li>Lipschitz 界与鲁棒性（Lipschitz Bound &amp; Robustness）：中值定理给出“输入小扰动导致输出变化”的上界形式。若梯度有界，则输出变化可控；这正是对抗鲁棒性分析和梯度惩罚（Gradient Penalty）类方法的数学基础之一。</li>
<li>有限差分与梯度估计（Finite Difference / Gradient Estimation）：割线斜率与切线斜率之间的关系来自拉格朗日中值定理，是数值优化里差分近似、线搜索（Line Search）和误差估计的核心依据。</li>
</ul>
<p>从“纯数学”到“AI 实践”的桥梁可以一句话概括：<span style="background-color: #c0c0c0;">中值定理把宏观变化量（函数值差）变成可优化的微观信息（导数/梯度）</span>。</p>
<div class="blog_h2"><span class="graybg">偏导数（Partial Derivative）</span></div>
<p>对多元函数 <span displaypfx="inline-" class="mathjax-container">\(f:\mathbb{R}^n\to\mathbb{R}\)</span>，偏导数把“只沿某一坐标轴方向的导数”形式化：固定其余变量，只让第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个变量变化。先写成最直接的极限定义：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial f}{\partial x_i}(x_0)=\lim_{h\to 0}\frac{f(x_{0,1},\dots,x_{0,i-1},x_{0,i}+h,x_{0,i+1},\dots,x_{0,n})-f(x_0)}{h}\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(h\)</span> 是“第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个变量的微小增量”（Increment）：只加在第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个坐标上，其他坐标保持不变。</p>
<p>在线性代数记号下，上式等价写成（更紧凑）：令 <span displaypfx="inline-" class="mathjax-container">\(e_i\)</span> 为第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个标准基向量（Standard Basis Vector），则</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial f}{\partial x_i}(x_0)=\lim_{h\to 0}\frac{f(x_0+h e_i)-f(x_0)}{h}\]</span>
<p>更常用的导数视角（计算视角）是：<span style="background-color: #c0c0c0;">把其余变量暂时当常数，把目标变量当自变量，直接套一元求导法则</span>。也就是说，极限定义负责“定义正确性”，日常计算通常用求导规则完成。</p>
<p>例如 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2y\)</span>：对 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 求偏导时把 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 视作常数，得 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f}{\partial x}=2xy\)</span>；对 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 求偏导时把 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 视作常数，得 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f}{\partial y}=x^2\)</span>。</p>
<p>切换为极限视角，令 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2y\)</span>，在点 <span displaypfx="inline-" class="mathjax-container">\((1,2)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial f}{\partial x}(1,2)=\lim_{h\to 0}\frac{f(1+h,2)-f(1,2)}{h}\]</span>
<span displaypfx="" class="mathjax-container">\[=\lim_{h\to 0}\frac{2(1+h)^2-2}{h}\]</span>
<span displaypfx="" class="mathjax-container">\[=\lim_{h\to 0}(4+2h)=4\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial f}{\partial y}(1,2)=\lim_{h\to 0}\frac{f(1,2+h)-f(1,2)}{h}\]</span>
<span displaypfx="" class="mathjax-container">\[=\lim_{h\to 0}\frac{(2+h)-2}{h}=1\]</span>
<p>可以看到：求 <span displaypfx="inline-" class="mathjax-container">\(\partial f/\partial x\)</span> 时把 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 当常数；求 <span displaypfx="inline-" class="mathjax-container">\(\partial f/\partial y\)</span> 时把 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 当常数。这正是“其余变量固定”的具体计算含义。</p>
<p>梯度（Gradient）就是把所有偏导数按坐标收集成向量： <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x)=(\partial f/\partial x_1,\dots,\partial f/\partial x_n)^\top\)</span>。</p>
<div class="blog_h2"><span class="graybg">微分（Differential）</span></div>
<p>在一元情形中，微分是“把函数增量线性化”的记号：给定一个小增量 <span displaypfx="inline-" class="mathjax-container">\(dx\)</span>，定义</p>
<span displaypfx="" class="mathjax-container">\[df\big|_{x_0}=f'(x_0)\,dx\]</span>
<p>此时真实增量满足 <span displaypfx="inline-" class="mathjax-container">\(\Delta f = df + o(dx)\)</span>。给定 <span displaypfx="inline-" class="mathjax-container">\(dx\)</span> 后， <span displaypfx="inline-" class="mathjax-container">\(df\)</span> 才对应一个确定数值。</p>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(f(x)=x^2\)</span>，在 <span displaypfx="inline-" class="mathjax-container">\(x_0=3\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(dx=0.1\)</span> 时， <span displaypfx="inline-" class="mathjax-container">\(df=2x_0dx=0.6\)</span>，而 <span displaypfx="inline-" class="mathjax-container">\(\Delta f=f(3.1)-f(3)=0.61\)</span>。微分给出一阶近似，误差来自高阶项。</p>
<div class="blog_h2"><span class="graybg">全微分（Total Differential）</span></div>
<p>对多元函数 <span displaypfx="inline-" class="mathjax-container">\(f(x_1,\dots,x_n)\)</span>，全微分把“多方向小扰动下的一阶线性响应”写成：</p>
<span displaypfx="" class="mathjax-container">\[df=\sum_{i=1}^n \frac{\partial f}{\partial x_i}dx_i\]</span>
<p>令 <span displaypfx="inline-" class="mathjax-container">\(d\mathbf{x}=(dx_1,\dots,dx_n)^\top\)</span>，则向量形式是：</p>
<span displaypfx="" class="mathjax-container">\[df=(\nabla f(x))^\top d\mathbf{x}\]</span>
<p>若进一步把扰动分解为“方向 + 步长”： <span displaypfx="inline-" class="mathjax-container">\(d\mathbf{x}=\mathbf{u}\,ds\)</span>（<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{u}\|_2=1\)</span>），则</p>
<span displaypfx="" class="mathjax-container">\[\frac{df}{ds}=\nabla f(x)\cdot \mathbf{u}\]</span>
<p>右侧就是方向导数（Directional Derivative）：全微分给出任意小位移的一阶近似，方向导数则把位移约束在单位方向并除去步长。</p>
<p>例：令 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2y\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f}{\partial x}=2xy\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f}{\partial y}=x^2\)</span>。在点 <span displaypfx="inline-" class="mathjax-container">\((x_0,y_0)=(1,2)\)</span>，若 <span displaypfx="inline-" class="mathjax-container">\(dx=0.1\)</span>、<span displaypfx="inline-" class="mathjax-container">\(dy=-0.05\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(df=2xy\,dx+x^2dy=2\cdot1\cdot2\cdot0.1+1\cdot(-0.05)=0.35\)</span>，给出 <span displaypfx="inline-" class="mathjax-container">\(\Delta f\)</span> 的一阶近似。</p>
<div class="blog_h2"><span class="graybg">向量微积分</span></div>
<p>Nabla 算子（Nabla Operator）记作 <span displaypfx="inline-" class="mathjax-container">\(\nabla\)</span>，本质上是把偏导算子按坐标排列成的“向量形式”：</p>
<span displaypfx="" class="mathjax-container">\[\nabla=\left(\frac{\partial}{\partial x_1},\dots,\frac{\partial}{\partial x_n}\right)^\top\]</span>
<p>它本身不是一个数，而是一个算子（Operator）。作用在标量场上得到梯度（Gradient）；与向量场点乘得到散度（Divergence）；与向量场叉乘得到旋度（Curl）；对标量场再取散度得到拉普拉斯（Laplacian）。</p>
<p>符号 <span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|\)</span>（双竖线）表示范数（Norm）。因此 <span displaypfx="inline-" class="mathjax-container">\(\|\nabla f(x)\|\)</span> 是梯度向量的长度；而 <span displaypfx="inline-" class="mathjax-container">\(\|\nabla\|\)</span> 作为“算子范数”在工程推导中很少直接使用，通常需要明确它作用的函数空间与范数定义。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/nabla.jpg"><img class="alignnone size-full wp-image-40609" src="https://blog.gmem.cc/wp-content/uploads/2026/03/nabla.jpg" alt="nabla" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">梯度</span></div>
<p>梯度（Gradient）把标量函数 <span displaypfx="inline-" class="mathjax-container">\(f:\mathbb{R}^n\to\mathbb{R}\)</span> 的局部变化率组织成向量：</p>
<span displaypfx="" class="mathjax-container">\[\nabla f(x)=\left(\frac{\partial f}{\partial x_1},\dots,\frac{\partial f}{\partial x_n}\right)^\top\]</span>
<p>它指向函数增长最快的方向（Steepest Ascent Direction），模长刻画该方向上的最大斜率。在优化里，梯度下降（Gradient Descent）沿 <span displaypfx="inline-" class="mathjax-container">\(-\nabla f\)</span> 走，是因为这是一阶近似下最快下降方向。</p>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2+2y^2\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\nabla f=(2x,4y)\)</span>，在点 <span displaypfx="inline-" class="mathjax-container">\((1,1)\)</span> 处梯度为 <span displaypfx="inline-" class="mathjax-container">\((2,4)\)</span>，表示沿 y 方向的增长更陡。</p>
<p>方向导数（Directional Derivative）把“沿某个方向的变化率”写成点积：若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 是单位方向向量，则</p>
<span displaypfx="" class="mathjax-container">\[D_{\mathbf{u}}f(x)=\nabla f(x)\cdot \mathbf{u}\]</span>
<p>符号先约定： <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是当前点， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{u}\)</span> 是单位方向（<span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{u}\|_2=1\)</span>）， <span displaypfx="inline-" class="mathjax-container">\(\varepsilon\)</span> 是沿该方向的步长（可正可负的小实数）。方向导数定义为</p>
<span displaypfx="" class="mathjax-container">\[D_{\mathbf{u}}f(x)=\lim_{\varepsilon\to 0}\frac{f(x+\varepsilon\mathbf{u})-f(x)}{\varepsilon}\]</span>
<p>对 <span displaypfx="inline-" class="mathjax-container">\(f(x+\varepsilon\mathbf{u})\)</span> 做一阶 Taylor 展开：</p>
<span displaypfx="" class="mathjax-container">\[f(x+\varepsilon\mathbf{u})=f(x)+\varepsilon\,\nabla f(x)\cdot\mathbf{u}+o(\varepsilon)\]</span>
<p>代回定义式：</p>
<span displaypfx="" class="mathjax-container">\[\frac{f(x+\varepsilon\mathbf{u})-f(x)}{\varepsilon}\]</span>
<span displaypfx="" class="mathjax-container">\[=\nabla f(x)\cdot\mathbf{u}+\frac{o(\varepsilon)}{\varepsilon}\]</span>
<p>令 <span displaypfx="inline-" class="mathjax-container">\(\varepsilon\to 0\)</span>，由于 <span displaypfx="inline-" class="mathjax-container">\(o(\varepsilon)/\varepsilon\to 0\)</span>，得到 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathbf{u}}f(x)=\nabla f(x)\cdot\mathbf{u}\)</span>。</p>
<div class="blog_h3"><span class="graybg">散度</span></div>
<p>场（Field）是“把空间中每个点映射到一个量”的函数。标量场（Scalar Field）<span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 在每个点输出一个标量；向量场（Vector Field）<span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}(x)\)</span> 在每个点输出一个向量。梯度作用在标量场上，散度/旋度作用在向量场上。</p>
<p>散度（Divergence）作用在向量场 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}=(F_1,\dots,F_n)^\top:\mathbb{R}^n\to\mathbb{R}^n\)</span> 上。这里先把两个术语说清楚：</p>
<span displaypfx="" class="mathjax-container">\[J_{\mathbf{F}}(x)=\left[\frac{\partial F_i}{\partial x_j}\right]_{i,j=1}^n\]</span>
<p>上式是雅可比矩阵（Jacobian）：第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 列表示 <span displaypfx="inline-" class="mathjax-container">\(F_i\)</span> 对 <span displaypfx="inline-" class="mathjax-container">\(x_j\)</span> 的偏导。迹（Trace）是矩阵对角线元素之和，因此</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{tr}(J_{\mathbf{F}})=\sum_{i=1}^n \frac{\partial F_i}{\partial x_i}\]</span>
<p>而散度按定义正是这条和式，所以“散度 = Jacobian 的迹”：</p>
<span displaypfx="" class="mathjax-container">\[\nabla\cdot \mathbf{F}(x)=\mathrm{tr}(J_{\mathbf{F}}(x))=\sum_{i=1}^n \frac{\partial F_i}{\partial x_i}\]</span>
<p>几何/物理直觉：把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}\)</span> 想成“流体速度场（Velocity Field）”，散度衡量一个点附近是否像“源（Source）/汇（Sink）”——单位体积的净流出率。散度为正表示净流出，为负表示净流入。</p>
<p>例：二维场 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}(x,y)=(x,y)\)</span> 的 Jacobian 是 <span displaypfx="inline-" class="mathjax-container">\(\begin{bmatrix}1&amp;0\\0&amp;1\end{bmatrix}\)</span>，其迹为 <span displaypfx="inline-" class="mathjax-container">\(1+1=2\)</span>，所以散度也等于 2，在任意点都表现为均匀“发散”。</p>
<div class="blog_h3"><span class="graybg">旋度</span></div>
<p>旋度（Curl）衡量向量场的局部旋转（Local Rotation）。在三维中：</p>
<span displaypfx="" class="mathjax-container">\[\nabla\times \mathbf{F}=\left(\frac{\partial F_3}{\partial y}-\frac{\partial F_2}{\partial z},\ \frac{\partial F_1}{\partial z}-\frac{\partial F_3}{\partial x},\ \frac{\partial F_2}{\partial x}-\frac{\partial F_1}{\partial y}\right)\]</span>
<p>直觉上，可把它理解为“放一个很小的桨轮（Paddle Wheel）在该点附近，桨轮是否会被带着转”。保守场（Conservative Field）满足 <span displaypfx="inline-" class="mathjax-container">\(\nabla\times\mathbf{F}=\mathbf{0}\)</span>，这与“路径无关”/“存在势函数”是等价刻画。</p>
<p>例：二维旋转场 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}(x,y)=(-y,x,0)\)</span> 的旋度为 <span displaypfx="inline-" class="mathjax-container">\((0,0,2)\)</span>，表示绕 z 轴的均匀旋转趋势。</p>
<p>拉普拉斯算子（Laplacian）作用在标量场上，定义为梯度的散度：</p>
<span displaypfx="" class="mathjax-container">\[\nabla^2 f=\nabla\cdot(\nabla f)=\sum_{i=1}^n \frac{\partial^2 f}{\partial x_i^2}\]</span>
<p>它常出现在扩散（Diffusion）与平滑（Smoothing）问题中，也常被用作“曲率/粗糙度”的度量。例： <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2+y^2\)</span> 则 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f=2+2=4\)</span>。</p>
<div class="blog_h2"><span class="graybg">Jacobian 矩阵</span></div>
<p>当函数的输出不再是一个标量，而是一个向量时，梯度这个概念就不够用了。此时需要把“每个输出分量对每个输入变量的一阶变化率”统一收集起来，这个对象就是 Jacobian 矩阵（Jacobian Matrix）。它是一阶导数在向量值函数上的自然推广，也是 Hessian 的直接前置概念。</p>
<p>设向量值函数</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{F}:\mathbb{R}^n\to\mathbb{R}^m,\qquad \mathbf{F}(\mathbf{x})=\begin{bmatrix}F_1(\mathbf{x})\\ \vdots\\ F_m(\mathbf{x})\end{bmatrix}\]</span>
<p>其中输入 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,\dots,x_n)^\top\)</span>，输出 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}(\mathbf{x})\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 个分量。Jacobian 矩阵定义为：</p>
<span displaypfx="" class="mathjax-container">\[J_{\mathbf{F}}(\mathbf{x})=\left[\frac{\partial F_i}{\partial x_j}(\mathbf{x})\right]_{i=1,\dots,m;\,j=1,\dots,n}\]</span>
<p>按矩阵展开，就是一个 <span displaypfx="inline-" class="mathjax-container">\(m\times n\)</span> 矩阵：</p>
<span displaypfx="" class="mathjax-container">\[J_{\mathbf{F}}(\mathbf{x})= \begin{bmatrix} \frac{\partial F_1}{\partial x_1} &amp; \frac{\partial F_1}{\partial x_2} &amp; \cdots &amp; \frac{\partial F_1}{\partial x_n}\\ \frac{\partial F_2}{\partial x_1} &amp; \frac{\partial F_2}{\partial x_2} &amp; \cdots &amp; \frac{\partial F_2}{\partial x_n}\\ \vdots &amp; \vdots &amp; \ddots &amp; \vdots\\ \frac{\partial F_m}{\partial x_1} &amp; \frac{\partial F_m}{\partial x_2} &amp; \cdots &amp; \frac{\partial F_m}{\partial x_n} \end{bmatrix}\]</span>
<p>这里第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 列的元素 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial F_i}{\partial x_j}\)</span> 表示：第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个输入变量发生微小变化时，第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个输出分量会怎样一阶变化。按矩阵读法，<span style="background-color: #c0c0c0;">行对应输出分量，列对应输入变量</span>，因此 Jacobian 本质上是一张局部灵敏度表（Local Sensitivity Table）。</p>
<p>它描述的不是全局非线性结构，而是函数在某个点附近的局部线性映射：输入空间里的一个小扰动向量 <span displaypfx="inline-" class="mathjax-container">\(d\mathbf{x}\)</span> 经过 Jacobian 作用后，变成输出空间中的一阶近似扰动 <span displaypfx="inline-" class="mathjax-container">\(d\mathbf{F}\)</span>。因此，Jacobian 可以理解为“该点附近最能代表原函数的一阶线性算子”。</p>
<p>它与全微分的关系最直接。若在点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 附近加入一个小扰动 <span displaypfx="inline-" class="mathjax-container">\(d\mathbf{x}\)</span>，则输出的一阶变化满足：</p>
<span displaypfx="" class="mathjax-container">\[d\mathbf{F}\approx J_{\mathbf{F}}(\mathbf{x})\,d\mathbf{x}\]</span>
<p>这条式子就是向量值函数的一阶线性化。也就是说，Jacobian 在局部扮演的角色，类似于一元函数里的导数：它给出“最好的线性近似”。若输出是一维，即 <span displaypfx="inline-" class="mathjax-container">\(m=1\)</span>，Jacobian 就退化成一个 <span displaypfx="inline-" class="mathjax-container">\(1\times n\)</span> 的行向量；它与梯度本质上包含同一组偏导数，只是排布约定可能不同。</p>
<p>一个二维到二维的例子最容易看清。设</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{F}(x,y)=\begin{bmatrix}x^2y\\ x+y^2\end{bmatrix}\]</span>
<p>则第一行来自分量函数 <span displaypfx="inline-" class="mathjax-container">\(F_1(x,y)=x^2y\)</span>，第二行来自 <span displaypfx="inline-" class="mathjax-container">\(F_2(x,y)=x+y^2\)</span>。逐项求偏导可得：</p>
<span displaypfx="" class="mathjax-container">\[J_{\mathbf{F}}(x,y)= \begin{bmatrix} 2xy &amp; x^2\\ 1 &amp; 2y \end{bmatrix}\]</span>
<p>例如在点 <span displaypfx="inline-" class="mathjax-container">\((1,2)\)</span>，Jacobian 为</p>
<span displaypfx="" class="mathjax-container">\[J_{\mathbf{F}}(1,2)= \begin{bmatrix} 4 &amp; 1\\ 1 &amp; 4 \end{bmatrix}\]</span>
<p>这表示：在该点附近，若输入发生微小变化 <span displaypfx="inline-" class="mathjax-container">\((dx,dy)^\top\)</span>，则输出变化近似为</p>
<span displaypfx="" class="mathjax-container">\[d\mathbf{F}\approx \begin{bmatrix} 4 &amp; 1\\ 1 &amp; 4 \end{bmatrix} \begin{bmatrix} dx\\ dy \end{bmatrix}\]</span>
<p>第一行说明输出第一分量大致按 <span displaypfx="inline-" class="mathjax-container">\(4dx+dy\)</span> 变化，第二行说明输出第二分量大致按 <span displaypfx="inline-" class="mathjax-container">\(dx+4dy\)</span> 变化。Jacobian 因而可以被理解为局部灵敏度矩阵（Sensitivity Matrix）。</p>
<p>对标量函数 <span displaypfx="inline-" class="mathjax-container">\(f:\mathbb{R}^n\to\mathbb{R}\)</span>，梯度 <span displaypfx="inline-" class="mathjax-container">\(\nabla f:\mathbb{R}^n\to\mathbb{R}^n\)</span> 本身就是一个向量值函数，因此 Hessian 可以直接看成梯度映射的 Jacobian：</p>
<span displaypfx="" class="mathjax-container">\[\nabla^2 f(\mathbf{x})=J_{\nabla f}(\mathbf{x})\]</span>
<p>这就是把 Jacobian 放在 Hessian 前面的原因：先理解“向量值函数的一阶导数如何组织成矩阵”，再看“标量函数的梯度再求一次导”，Hessian 的结构就不会显得突兀。</p>
<p>在机器学习中，Jacobian 最常出现在三类地方：</p>
<ol>
<li>反向传播（Backpropagation）：本质上是在链式法则（Chain Rule）下逐层组合这些局部 Jacobian。前一层输出的微小变化如何传到后一层，正是由对应层的 Jacobian 决定。</li>
<li>向量场分析：散度（Divergence）等于 Jacobian 的迹（Trace）。</li>
<li>生成模型、正则化与鲁棒性分析：常关心 Jacobian 的谱范数（Spectral Norm）或 Frobenius 范数，以度量局部放大效应。</li>
</ol>
<p>若输出维度和输入维度都很大，显式构造完整 Jacobian 也会很贵，因此工程上常直接计算 Jacobian-Vector Product 或 Vector-Jacobian Product，而不是把整块矩阵完全展开。</p>
<p>在神经网络语境里，Jacobian 到底“针对激活值还是针对权重”，取决于谁被当作自变量。若研究层与层之间的信号传播，常写成 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial h^{(l+1)}}{\partial h^{(l)}}\)</span>，这时它针对的是激活值（Activation）或层表示；若研究模型输出对输入的敏感度，可写成 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial y}{\partial x}\)</span>；若研究输出对参数的敏感度，也可以写成 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial y}{\partial \theta}\)</span>。因此，Jacobian 的本质是“一个向量怎样对另一个向量发生一阶变化”，并不预先限定变量一定是激活值还是权重。</p>
<div class="blog_h2"><span class="graybg">Hessian 矩阵</span></div>
<p>梯度（Gradient）回答的是“函数沿各坐标方向的一阶变化率”；Hessian 矩阵（Hessian Matrix）进一步回答的是“这些一阶变化率本身又在怎样变化”。这一点可以直接从“梯度是一个向量值函数”展开出来。对标量函数 <span displaypfx="inline-" class="mathjax-container">\(f:\mathbb{R}^n\to\mathbb{R}\)</span>，梯度写成</p>
<span displaypfx="" class="mathjax-container">\[\nabla f(\mathbf{x})=\begin{bmatrix}\frac{\partial f}{\partial x_1}\\ \frac{\partial f}{\partial x_2}\\ \vdots\\ \frac{\partial f}{\partial x_n}\end{bmatrix}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\nabla f\)</span> 已经不是标量，而是把输入向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 映射为另一个向量的函数。因此可以像对任何向量值函数那样，对它再求一次 Jacobian：</p>
<span displaypfx="" class="mathjax-container">\[J_{\nabla f}(\mathbf{x})=\frac{\partial (\nabla f)}{\partial \mathbf{x}}\]</span>
<p>把梯度各分量逐项代进去，Jacobian 的第 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 个元素就是“梯度第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量对输入第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个变量的偏导数”：</p>
<span displaypfx="" class="mathjax-container">\[\big[J_{\nabla f}(\mathbf{x})\big]_{ij}=\frac{\partial}{\partial x_j}\left(\frac{\partial f}{\partial x_i}\right)=\frac{\partial^2 f}{\partial x_j\partial x_i}\]</span>
<p>于是整块矩阵就是</p>
<span displaypfx="" class="mathjax-container">\[J_{\nabla f}(\mathbf{x})=\begin{bmatrix}\frac{\partial^2 f}{\partial x_1\partial x_1} &amp; \frac{\partial^2 f}{\partial x_2\partial x_1} &amp; \cdots &amp; \frac{\partial^2 f}{\partial x_n\partial x_1}\\ \frac{\partial^2 f}{\partial x_1\partial x_2} &amp; \frac{\partial^2 f}{\partial x_2\partial x_2} &amp; \cdots &amp; \frac{\partial^2 f}{\partial x_n\partial x_2}\\ \vdots &amp; \vdots &amp; \ddots &amp; \vdots\\ \frac{\partial^2 f}{\partial x_1\partial x_n} &amp; \frac{\partial^2 f}{\partial x_2\partial x_n} &amp; \cdots &amp; \frac{\partial^2 f}{\partial x_n\partial x_n}\end{bmatrix}\]</span>
<p>这正是 Hessian：</p>
<span displaypfx="" class="mathjax-container">\[\nabla^2 f(\mathbf{x})=J_{\nabla f}(\mathbf{x})\]</span>
<p>这个等式的含义很直接：Hessian 不是凭空引入的另一种矩阵，而就是“梯度这个向量场对输入的 Jacobian”。</p>
<p>直接从二阶导角度看，Hessian 是多元函数的二阶导数对象，用来描述局部曲率（Curvature）、凹凸性以及临界点附近的二阶形状。</p>
<p>设标量函数 <span displaypfx="inline-" class="mathjax-container">\(f:\mathbb{R}^n\to\mathbb{R}\)</span>，输入向量为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x_1,\dots,x_n)^\top\)</span>。Hessian 矩阵记作 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f(\mathbf{x})\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(H_f(\mathbf{x})\)</span>，定义为：</p>
<span displaypfx="" class="mathjax-container">\[\nabla^2 f(\mathbf{x})=\left[\frac{\partial^2 f}{\partial x_i\partial x_j}(\mathbf{x})\right]_{i,j=1}^{n}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 都从 <span displaypfx="inline-" class="mathjax-container">\(1\)</span> 遍历到 <span displaypfx="inline-" class="mathjax-container">\(n\)</span>，因此 Hessian 会收集所有变量对 <span displaypfx="inline-" class="mathjax-container">\((x_i,x_j)\)</span> 的二阶偏导。之所以自然形成一个 <span displaypfx="inline-" class="mathjax-container">\(n\times n\)</span> 的“全组合”矩阵，是因为一阶导数 <span displaypfx="inline-" class="mathjax-container">\(\nabla f\)</span> 本身已经有 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个分量；再对这 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个分量分别对 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个输入变量各求一次导，就得到 <span displaypfx="inline-" class="mathjax-container">\(n\times n\)</span> 个二阶偏导项。前面展开的 <span displaypfx="inline-" class="mathjax-container">\(J_{\nabla f}(\mathbf{x})\)</span> 就是这块矩阵，因此这里不再重复写一遍。</p>
<p>把梯度记为 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(\mathbf{x})=(g_1,\dots,g_n)^\top\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(g_i=\frac{\partial f}{\partial x_i}\)</span>，则 Hessian 的第 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 个元素可以直接写成 <span displaypfx="inline-" class="mathjax-container">\(H_{ij}=\frac{\partial g_i}{\partial x_j}\)</span>。这个记号把它的意义说得很清楚：它衡量“第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个方向上的斜率”会不会随着第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个变量的变化而改变。</p>
<p>对角线元素 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial^2 f}{\partial x_i^2}\)</span> 描述沿单一坐标方向的纯二阶曲率，也就是该方向本身的弯曲强弱；非对角元素 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial^2 f}{\partial x_i\partial x_j}\)</span> 描述不同方向之间的局部耦合（Local Coupling）。当 <span displaypfx="inline-" class="mathjax-container">\(H_{ij}=0\)</span> 时，意味着在当前点附近，变量 <span displaypfx="inline-" class="mathjax-container">\(x_j\)</span> 的微小变化不会改变第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个方向上的一阶斜率；当 <span displaypfx="inline-" class="mathjax-container">\(H_{ij}\neq 0\)</span> 时，两个方向在局部二阶结构上发生耦合。</p>
<p>这种耦合会直接反映在等高线（Contour）形状上：若非对角项接近零，局部二次近似更接近与坐标轴对齐的“正椭圆碗”；若非对角项明显非零，等高线会发生倾斜，说明改变一个变量会连带改变另一个方向上的坡度。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 足够光滑，满足二阶混合偏导连续，则由 Clairaut / Schwarz 定理可得</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial^2 f}{\partial x_i\partial x_j}=\frac{\partial^2 f}{\partial x_j\partial x_i}\]</span>
<p>因此 Hessian 矩阵是对称矩阵（Symmetric Matrix）。这点非常重要，因为一旦 Hessian 对称，就可以用特征值（Eigenvalue）来判断局部曲率：正特征值对应向上弯，负特征值对应向下弯，正负并存则意味着不同方向上的弯曲趋势相反。</p>
<p>Hessian 的几何意义可以通过二阶 Taylor 展开看得最清楚。对点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 附近的微小扰动 <span displaypfx="inline-" class="mathjax-container">\(\Delta \mathbf{x}\)</span>，有：</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x}+\Delta\mathbf{x})\approx f(\mathbf{x})+\nabla f(\mathbf{x})^\top \Delta\mathbf{x}+\frac{1}{2}\Delta\mathbf{x}^\top \nabla^2 f(\mathbf{x})\Delta\mathbf{x}\]</span>
<p>这里第一项 <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})\)</span> 是当前函数值，第二项 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(\mathbf{x})^\top \Delta\mathbf{x}\)</span> 是一阶线性变化，第三项 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\Delta\mathbf{x}^\top \nabla^2 f(\mathbf{x})\Delta\mathbf{x}\)</span> 就是二阶曲率修正项。也就是说，梯度告诉你“朝哪边走函数会立刻增减”，Hessian 则告诉你“地形本身是碗状、山丘状，还是马鞍状”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/hessian-locality-preview.png"><img class="alignnone size-full" src="https://blog.gmem.cc/wp-content/uploads/2026/03/hessian-locality.png" alt="hessian-locality" width="2307" height="1169" /></a></p>
<p>二维情形最直观。设</p>
<span displaypfx="" class="mathjax-container">\[f(x_1,x_2)=x_1^2+3x_1x_2+2x_2^2\]</span>
<p>先求梯度：</p>
<span displaypfx="" class="mathjax-container">\[\nabla f(x_1,x_2)=\begin{bmatrix}2x_1+3x_2\\3x_1+4x_2\end{bmatrix}\]</span>
<p>再求二阶偏导，可得：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial^2 f}{\partial x_1^2}=2,\qquad \frac{\partial^2 f}{\partial x_1\partial x_2}=3,\qquad \frac{\partial^2 f}{\partial x_2^2}=4\]</span>
<p>因此 Hessian 为：</p>
<span displaypfx="" class="mathjax-container">\[\nabla^2 f(x_1,x_2)=\begin{bmatrix}2 &amp; 3\\3 &amp; 4\end{bmatrix}\]</span>
<p>这个例子还有一个关键特征：Hessian 与 <span displaypfx="inline-" class="mathjax-container">\((x_1,x_2)\)</span> 无关，因此它说明该函数在整个空间里都是同一种二次曲面（Quadratic Surface）。对二次函数而言，Hessian 足以完整决定其曲率结构。</p>
<p>在极值判别里，Hessian 主要出现在临界点（Critical Point）附近。若某点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}^*\)</span> 满足 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(\mathbf{x}^*)=\mathbf{0}\)</span>，则：</p>
<ul>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f(\mathbf{x}^*)\)</span> 正定（Positive Definite），即对任意非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span> 都有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}^\top \nabla^2 f(\mathbf{x}^*)\mathbf{v}&gt;0\)</span>，则该点是局部极小值（Local Minimum）。</li>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f(\mathbf{x}^*)\)</span> 负定（Negative Definite），即对任意非零向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}\)</span> 都有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}^\top \nabla^2 f(\mathbf{x}^*)\mathbf{v}&lt;0\)</span>，则该点是局部极大值（Local Maximum）。</li>
<li>若 Hessian 不定（Indefinite），即存在方向使二次型为正，也存在方向使其为负，则该点是鞍点（Saddle Point）。</li>
<li>若 Hessian 只有半正定、半负定，或出现零特征值，则二阶信息不足以单独判定，还需要更高阶分析或结合函数结构进一步判断。</li>
</ul>
<p>这套判据和一维情形完全一致：一维里 <span displaypfx="inline-" class="mathjax-container">\(f''(x^*)&gt;0\)</span> 表示“碗底”， <span displaypfx="inline-" class="mathjax-container">\(f''(x^*)&lt;0\)</span> 表示“山顶”；Hessian 只是把这个二阶导概念推广到了多维空间。</p>
<p>在优化中，Hessian 的意义更直接。牛顿法（Newton's Method）用它近似局部曲面，并据此选择更合理的更新方向：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}_{t+1}=\mathbf{x}_t-\big(\nabla^2 f(\mathbf{x}_t)\big)^{-1}\nabla f(\mathbf{x}_t)\]</span>
<p>与只看一阶斜率的梯度下降不同，牛顿法还利用了局部曲率信息：若某个方向很陡但也弯得很厉害，步长就应更谨慎；若某个方向很平缓，更新可以相对更大。这也是为什么 Hessian 会出现在牛顿法、拟牛顿法（BFGS / L-BFGS）以及很多二阶优化分析中。</p>
<p>在机器学习里，Hessian 还常用来讨论损失面的尖锐度（Sharpness）、参数可辨识性以及训练稳定性。但深度学习模型维度极高，完整 Hessian 的存储与求逆代价非常大：若参数维度为 <span displaypfx="inline-" class="mathjax-container">\(d\)</span>，Hessian 大小就是 <span displaypfx="inline-" class="mathjax-container">\(d\times d\)</span>。因此工程上更常用 Hessian 向量积（Hessian-Vector Product）、对角近似、低秩近似，或拟牛顿方法来间接利用二阶信息，而不是显式构造整块矩阵。</p>
<p>在神经网络里，单独说 Hessian 时，默认通常指损失函数对参数向量 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的 Hessian，即 <span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta^2 L(\theta)\)</span>。原因很直接：优化真正要更新的是权重，因此人们最关心的是“损失面对参数空间的局部曲率”。当然，Hessian 也可以对输入或中间激活值来求，例如 <span displaypfx="inline-" class="mathjax-container">\(\nabla_x^2 f(x)\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\nabla_h^2 f(h)\)</span>，它们分别描述输入空间或表示空间中的局部弯曲；只是从工程语境看，这类用法通常不是默认含义。</p>
<div class="blog_h2"><span class="graybg">积分</span></div>
<div class="blog_h3"><span class="graybg">不定积分</span></div>
<p>不定积分（Indefinite Integral）也叫反导数、原函数。是“由变化率反推原函数（Antiderivative）”：已知 <span displaypfx="inline-" class="mathjax-container">\(f\)</span>，寻找所有满足 <span displaypfx="inline-" class="mathjax-container">\(F'(x)=f(x)\)</span> 的函数 <span displaypfx="inline-" class="mathjax-container">\(F\)</span>。</p>
<p><span style="background-color: #c0c0c0;">一句话强调：不定积分就是“已知导函数（导数），求原函数”的过程。</span></p>
<span displaypfx="" class="mathjax-container">\[\int f(x)\,dx=F(x)+C\]</span>
<div class="blog_h4"><span class="graybg">从变化率反推状态</span></div>
<p>把导数看作“瞬时变化率”最容易理解积分的方向。若 <span displaypfx="inline-" class="mathjax-container">\(s(t)\)</span> 是位置、<span displaypfx="inline-" class="mathjax-container">\(v(t)\)</span> 是速度，则 <span displaypfx="inline-" class="mathjax-container">\(s'(t)=v(t)\)</span>。当只知道 <span displaypfx="inline-" class="mathjax-container">\(v(t)\)</span> 时，要恢复 <span displaypfx="inline-" class="mathjax-container">\(s(t)\)</span> 就是在做积分。</p>
<span displaypfx="" class="mathjax-container">\[s'(t)=v(t)\ \Longrightarrow\ s(t)=\int v(t)\,dt\]</span>
<div class="blog_h4"><span class="graybg">为什么必须有 +C</span></div>
<p>因为微分会“抹掉常数项”。不同函数只要相差一个常数，导数就完全一样：</p>
<span displaypfx="" class="mathjax-container">\[\frac{d}{dx}(x^2)=\frac{d}{dx}(x^2+5)=\frac{d}{dx}(x^2-100)=2x\]</span>
<p>所以当你从 <span displaypfx="inline-" class="mathjax-container">\(f(x)=2x\)</span> 反推原函数时，只能确定主体是 <span displaypfx="inline-" class="mathjax-container">\(x^2\)</span>，无法确定常数偏移。这就是积分常数（Constant of Integration）<span displaypfx="inline-" class="mathjax-container">\(C\)</span> 的来源。若再给一个初值条件（Initial Condition），例如 <span displaypfx="inline-" class="mathjax-container">\(F(x_0)=y_0\)</span>，<span displaypfx="inline-" class="mathjax-container">\(C\)</span> 才会被唯一确定。</p>
<div class="blog_h4"><span class="graybg">几何图像：不是一条曲线，而是一族</span></div>
<p><span displaypfx="inline-" class="mathjax-container">\(\int f(x)\,dx\)</span> 的结果对应一族函数 <span displaypfx="inline-" class="mathjax-container">\(\{F(x)+C\mid C\in\mathbb{R}\}\)</span>。它们形状完全相同，只是沿 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 轴上下平移；在同一个 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 处，这族曲线的切线斜率都等于 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span>。</p>
<div class="blog_h4"><span class="graybg">一个最小可算例</span></div>
<span displaypfx="" class="mathjax-container">\[\int 2x\,dx=x^2+C\]</span>
<p>验算： <span displaypfx="inline-" class="mathjax-container">\(\frac{d}{dx}(x^2+C)=2x\)</span>。这一步强调的是“求导与积分互为逆过程”，但逆过程会保留一个常数不确定性。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/indefinite-integral1.png"><img class="alignnone size-full wp-image-40773" src="https://blog.gmem.cc/wp-content/uploads/2026/03/indefinite-integral1.png" alt="indefinite-integral" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">微积分基本定理（Fundamental Theorem of Calculus）</span></div>
<p>不定积分与定积分的连接点是微积分基本定理（Fundamental Theorem of Calculus）。它包含两条互补结论：</p>
<ol>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(F'(x)=f(x)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\int_a^b f(x)\,dx=F(b)-F(a)\)</span>。这告诉我们：定积分可以通过任意一个原函数在端点处相减来计算。</li>
<li>定义 <span displaypfx="inline-" class="mathjax-container">\(G(x)=\int_a^x f(t)\,dt\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(G'(x)=f(x)\)</span>。这告诉我们：把函数从 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 到 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 累积起来，得到的累积函数本身就是一个原函数。</li>
</ol>
<p><span style="background-color: #c0c0c0;">一句话强调：定积分就是原函数（不定积分）在区间两端的函数值“收尾相减”</span>，即 <span displaypfx="inline-" class="mathjax-container">\(\int_a^b f(x)\,dx=F(b)-F(a)\)</span>。</p>
<p>例： <span displaypfx="inline-" class="mathjax-container">\(f(x)=2x\)</span>，取原函数 <span displaypfx="inline-" class="mathjax-container">\(F(x)=x^2\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\int_1^3 2x\,dx=F(3)-F(1)=9-1=8\]</span>
<div class="blog_h3"><span class="graybg">定积分</span></div>
<p>定积分（Definite Integral）可看成“带符号面积（Signed Area）”或“连续求和”。Riemann 和定义为：</p>
<span displaypfx="" class="mathjax-container">\[\int_a^b f(x)\,dx=\lim_{n\to\infty}\sum_{k=1}^{n}f(\xi_k)\Delta x_k\]</span>
<p>若上下界中出现 <span displaypfx="inline-" class="mathjax-container">\(\infty\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(-\infty\)</span>，它仍然属于定积分范畴，只是更准确地叫广义积分（Improper Integral）或广义定积分：本质上仍是在一个区间上求累积量，只是需要先把无穷区间截断，再用极限恢复。例如</p>
<span displaypfx="" class="mathjax-container">\[\int_a^{\infty} f(x)\,dx:=\lim_{R\to\infty}\int_a^R f(x)\,dx\]</span>
<p>因此“上下界无穷”不是不定积分，而是<span style="background-color: #c0c0c0;">定积分的特殊形式</span>。</p>
<p>在 AI 里，连续概率密度函数（Probability Density Function, PDF）的概率计算 <span displaypfx="inline-" class="mathjax-container">\(P(a\le X\le b)=\int_a^b p(x)\,dx\)</span> 本质上就是定积分。</p>
<div class="blog_h3"><span class="graybg">常用积分公式</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">被积函数</td>
<td style="text-align: center;">积分结果</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(x^n\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{x^{n+1}}{n+1}+C\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(n\ne -1\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{x}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\ln|x|+C\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(x\ne 0\)</span></td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(e^x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(e^x+C\)</span></td>
<td>指数函数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(-\cos x+C\)</span></td>
<td>三角函数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\cos x\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\sin x+C\)</span></td>
<td>三角函数</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{1}{1+x^2}\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\arctan x+C\)</span></td>
<td>反三角函数</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">多重积分</span></div>
<p>多重积分（Multiple Integrals）是把一维积分的“连续求和”推广到更高维区域上的积分运算。它积分的对象不再只是线段上的小长度，而是平面区域上的小面积、空间区域中的小体积，或者曲面上的小面片。因此，多重积分处理的是“在二维、三维或更一般区域上，把局部量连续累加成整体量”的问题。</p>
<p>它的阅读难点通常不在计算本身，而在于符号会很快遮住几何直觉。理解的关键仍然是把“积分”看成连续求和，只不过求和对象已经从一维区间扩展到了更高维区域。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/multiple-integral.jpg"><img class="alignnone size-full wp-image-40789" src="https://blog.gmem.cc/wp-content/uploads/2026/03/multiple-integral.jpg" alt="multiple-integral" width="100%" /></a></p>
<div class="blog_h4"><span class="graybg">从一维面积到三维体积</span></div>
<p>一重积分 <span displaypfx="inline-" class="mathjax-container">\(\int f(x)\,dx\)</span> 常被理解为曲线下的面积：在 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 轴上切出很多极窄的小条，每条小条的面积近似是“高度 <span displaypfx="inline-" class="mathjax-container">\(\times\)</span> 宽度”，也就是 <span displaypfx="inline-" class="mathjax-container">\(f(x)\,dx\)</span>，再把它们全部加起来。</p>
<p>双重积分把这个想法从“一条线”推广到“一块区域”。设 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 是平面上的一块区域，曲面高度由 <span displaypfx="inline-" class="mathjax-container">\(z=f(x,y)\)</span> 给出，那么</p>
<span displaypfx="" class="mathjax-container">\[\iint_D f(x,y)\,dx\,dy\]</span>
<p>就可以理解为：在区域 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 的每个小块 <span displaypfx="inline-" class="mathjax-container">\(dx\,dy\)</span> 上立起一根很细的小柱子，柱高是 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)\)</span>。这些小柱子拼起来，形成曲面下方的一整块立体体积。于是双重积分最直观的几何意义就是：<span style="background-color: #c0c0c0;">把区域上方由高度函数堆出来的整块三维体积连续累加起来</span>。</p>
<p>再往上推广，三重积分</p>
<span displaypfx="" class="mathjax-container">\[\iiint_\Omega f(x,y,z)\,dV\]</span>
<p>表示在空间区域 <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span> 内，对每个微小体积元 <span displaypfx="inline-" class="mathjax-container">\(dV\)</span> 的函数值做累加。此时它不一定是在“求一个更高维体积”，更准确地说，是在整个空间体内部对某个量做总汇总，例如总质量、总能量、总代价或总概率权重。可以把它和一重积分放在同一条理解链上来看：速度函数在时间上的累积给出位移，密度函数在空间上的累积给出质量，能量密度（Energy Density）在区域或体积上的累积给出总能量。若 <span displaypfx="inline-" class="mathjax-container">\(\rho(x,y)\)</span> 是面密度，则 <span displaypfx="inline-" class="mathjax-container">\(\iint_D \rho(x,y)\,dA\)</span> 表示区域 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 上的总质量；若 <span displaypfx="inline-" class="mathjax-container">\(\rho(x,y,z)\)</span> 是体密度，则 <span displaypfx="inline-" class="mathjax-container">\(\iiint_\Omega \rho(x,y,z)\,dV\)</span> 表示空间区域 <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span> 内的总质量。积分符号的重数，本质上对应的是累积区域的维度。</p>
<div class="blog_h4"><span class="graybg">高度函数到底表示什么</span></div>
<p>双重积分里最需要读懂的是 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)\)</span>。它并不总是“真实高度”，而是一个随位置变化的量。你可以把它看成“每个点上放了多少东西”。积分的过程，就是把整块区域上的这些局部贡献全部收集起来。</p>
<ul>
<li>当 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=1\)</span> 时，每个位置的高度都恒为 1，所以积分值在数值上就等于区域 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 的面积。此时双重积分退化为“面积公式”。</li>
<li>当 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)\)</span> 是密度函数（Density Function）时，积分得到的是总质量、总电荷或总概率。也就是说，函数值越大，说明该位置“堆得越多”，对总量贡献越大。</li>
<li>当 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)\)</span> 表示代价、风险、热量、响应强度等场量时，积分得到的是这类量在整个区域上的累积结果。</li>
</ul>
<p>因此，多重积分做的是一件很具体的事：把无穷多个微小局部量累加成一个整体量。</p>
<div class="blog_h4"><span class="graybg">带圈的积分号是什么意思</span></div>
<p>有时会看到带圈的双积分号 ∯。这个圈表示积分域是闭合的（Closed）：不是一块敞开的平面区域，而是一个完整包起来的曲面，例如球壳表面、气泡表面或任意封闭外壳。为了把这个“带圈”符号直接看出来，下面就直接写成在闭合曲面 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 上的积分。</p>
<p style="text-align: center; font-size: 1.15em;"><span style="text-align: center; font-size: 2em;">∯</span><sub>S</sub> <span displaypfx="inline-" class="mathjax-container">\(\mathbf{F}\cdot d\mathbf{S}\)</span></p>
<p>这里的含义通常不是“求面积”，而是计算向量场（Vector Field）穿过闭合曲面的总通量（Flux）。直观地说，就是看有多少“流”从这个封闭外壳内部穿出，或者从外部流入。它和高斯散度定理（Gauss's Divergence Theorem）直接相关，因此在偏微分方程（PDE）、连续介质建模、计算流体以及物理信息神经网络（Physics-Informed Neural Networks, PINNs）中十分重要。</p>
<div class="blog_h4"><span class="graybg">多重积分和AI</span></div>
<p>在 AI 中，多重积分最常见的身份是<span style="background-color: #c0c0c0;">概率分布上的总量计算器</span>。如果 <span displaypfx="inline-" class="mathjax-container">\(p(x,y)\)</span> 是二维随机变量的概率密度，那么</p>
<span displaypfx="" class="mathjax-container">\[\iint p(x,y)\,dx\,dy=1\]</span>
<p>表示整张概率曲面下方的总体积必须等于 1。这句话的本质是：所有可能情况加起来，概率总和必须是 100%。</p>
<p>进一步，期望（Expectation）就是在这个概率地形上做加权平均。以一维为例，</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[X]=\int x\,p(x)\,dx\]</span>
<p>表示每个位置 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 都按其概率权重 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span> 参与平均；多维情形下则自然推广为多重积分。用几何语言说，期望就是这堆“概率质量”在各坐标方向上的重心位置。</p>
<p>这也是为什么高斯分布归一化、边缘分布（Marginal Distribution）、条件分布、变分推断（Variational Inference）、连续潜变量模型以及能量模型（Energy-Based Models）都离不开多重积分。很多时候模型真正要做的事，并不是求一个公式值，而是在高维概率空间里累计、归一化、消去变量或计算期望。</p>
<div class="blog_h2"><span class="graybg">卷积（Convolution）</span></div>
<p>卷积（Convolution）把两个函数/序列组合成第三个函数/序列，最常见的语义是：<span style="background-color: #c0c0c0;">“一个信号在另一个信号的权重/响应下做累积叠加”</span>。它同时是信号处理（Signal Processing）、系统理论（Systems Theory）与 CNN 的基础运算。</p>
<div class="blog_h3"><span class="graybg">连续卷积（Continuous Convolution）</span></div>
<p>对连续函数 <span displaypfx="inline-" class="mathjax-container">\(f(t)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(g(t)\)</span>，卷积定义为：</p>
<span displaypfx="" class="mathjax-container">\[(f*g)(t)=\int_{-\infty}^{+\infty} f(\tau)\,g(t-\tau)\,d\tau\]</span>
<p>经典记忆法：<span style="background-color: #c0c0c0;">翻转（Flip）→ 平移（Shift）→ 相乘（Multiply）→ 积分（Integrate）</span>。其中 <span displaypfx="inline-" class="mathjax-container">\(g(t-\tau)\)</span> 这一项等价于把 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 先时间反转再按 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 平移。</p>
<div class="blog_h3"><span class="graybg">离散卷积（Discrete Convolution）</span></div>
<p>对离散序列 <span displaypfx="inline-" class="mathjax-container">\(x[n]\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(h[n]\)</span>，卷积定义为：</p>
<span displaypfx="" class="mathjax-container">\[(x*h)[n]=\sum_{k=-\infty}^{+\infty} x[k]\,h[n-k]\]</span>
<p>当序列有限长度时，上式求和区间会自然收缩为有限项。二维卷积（2D Convolution）/图像滤波可以视为把求和从一维扩展到二维索引。</p>
<div class="blog_h3"><span class="graybg">因果性与单侧卷积（Causality）</span></div>
<p>工程系统常满足因果性（Causality）：系统在时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的输出只依赖过去与当前输入，不依赖未来输入。若输入 <span displaypfx="inline-" class="mathjax-container">\(f(t)\)</span> 与系统的冲激响应（Impulse Response）<span displaypfx="inline-" class="mathjax-container">\(g(t)\)</span> 都是因果的（<span displaypfx="inline-" class="mathjax-container">\(t&lt;0\)</span> 时为 0），则卷积积分可写成单侧形式：</p>
<span displaypfx="" class="mathjax-container">\[(f*g)(t)=\int_{0}^{t} f(\tau)\,g(t-\tau)\,d\tau\]</span>
<p>这个形式更符合直觉：时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的结果由 <span displaypfx="inline-" class="mathjax-container">\([0,t]\)</span> 区间内“所有过去输入”的贡献叠加而来。</p>
<div class="blog_h3"><span class="graybg">直觉例子：打点滴的累积药效</span></div>
<p>把卷积理解成“累积药效”通常更直观。设 <span displaypfx="inline-" class="mathjax-container">\(r(t)\)</span> 是单位时间的滴注速率（Infusion Rate），<span displaypfx="inline-" class="mathjax-container">\(h(t)\)</span> 是“单位剂量在体内的药效/浓度随时间衰减曲线”（可以把它理解为冲激响应）。在时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的总药效/浓度 <span displaypfx="inline-" class="mathjax-container">\(c(t)\)</span> 可以写成：</p>
<span displaypfx="" class="mathjax-container">\[c(t)=(r*h)(t)=\int_{0}^{t} r(\tau)\,h(t-\tau)\,d\tau\]</span>
<p>解释：在过去每个时刻 <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span> 滴入的一小部分药量 <span displaypfx="inline-" class="mathjax-container">\(r(\tau)\,d\tau\)</span>，到现在时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 已经“经过了” <span displaypfx="inline-" class="mathjax-container">\(t-\tau\)</span> 的代谢时间，因此它的残留贡献按 <span displaypfx="inline-" class="mathjax-container">\(h(t-\tau)\)</span> 衰减；把所有过去贡献叠加，就得到当前总效果。这正是卷积“用一个响应核去累积历史”的核心语义。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/convolution.jpg"><img class="alignnone size-full wp-image-40805" src="https://blog.gmem.cc/wp-content/uploads/2026/03/convolution.jpg" alt="convolution" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">卷积与互相关（Cross-Correlation）</span></div>
<p>互相关（Cross-Correlation）与卷积的形式非常接近，但没有“核翻转”（Kernel Flip）：一维离散互相关常写成 <span displaypfx="inline-" class="mathjax-container">\(\sum_k x[k]\,w[n+k]\)</span>（不同资料索引略有差异）。深度学习框架里多数“卷积层”实现的是互相关，而不是严格数学卷积；由于卷积核参数是可学习的，翻不翻转并不会改变可表达的函数族，但在信号处理里二者语义不同，需注意约定。</p>
<div class="blog_h2"><span class="graybg">链式法则</span></div>
<p>链式法则（Chain Rule）是反向传播（Backpropagation）的核心：当计算图（Computation Graph）由一系列函数复合组成时，总导数等于沿路径的局部导数相乘。</p>
<p>在微分语言下更紧凑：若 <span displaypfx="inline-" class="mathjax-container">\(y=g(x)\)</span>、<span displaypfx="inline-" class="mathjax-container">\(z=f(y)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(dz=f'(y)\,dy\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(dy=g'(x)\,dx\)</span>，合并得 <span displaypfx="inline-" class="mathjax-container">\(dz=f'(g(x))g'(x)\,dx\)</span>。反向传播做的就是把这些局部“系数”从输出一路乘回输入。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/rule-of-chains.jpg"><img class="alignnone size-full wp-image-40809" src="https://blog.gmem.cc/wp-content/uploads/2026/03/rule-of-chains.jpg" alt="rule-of-chains" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">最小二乘法</span></div>
<p>最小二乘法（Least Squares）解决“拟合误差最小”的问题。给定样本矩阵 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 与目标 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span>，线性模型 <span displaypfx="inline-" class="mathjax-container">\(\hat{\mathbf{y}}=X\mathbf{w}\)</span> 的目标是：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w}}\ \|X\mathbf{w}-\mathbf{y}\|_2^2\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(X^\top X\)</span> 可逆时，闭式解满足正规方程（Normal Equations）：</p>
<span displaypfx="" class="mathjax-container">\[X^\top X\mathbf{w}=X^\top\mathbf{y},\quad \mathbf{w}^*=(X^\top X)^{-1}X^\top\mathbf{y}\]</span>
<p>几何直觉： <span displaypfx="inline-" class="mathjax-container">\(X\mathbf{w}\)</span> 只能落在 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 的列空间（Column Space）中，最小二乘解就是把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 正交投影（Orthogonal Projection）到这个空间上，剩余误差向量与列空间正交。</p>
<p>在 AI/统计实践中，若 <span displaypfx="inline-" class="mathjax-container">\(X^\top X\)</span> 病态或奇异，常用岭回归（Ridge Regression）：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w}}\ \|X\mathbf{w}-\mathbf{y}\|_2^2+\lambda\|\mathbf{w}\|_2^2,\quad (X^\top X+\lambda I)\mathbf{w}=X^\top\mathbf{y}\]</span>
<p>它本质上是把二次目标做稳定化，降低方差并提升泛化。</p>
<div class="blog_h2"><span class="graybg">凸函数与凸优化</span></div>
<p>凸函数（Convex Function）是“碗状”的函数：任意两点连线上的函数值不超过端点函数值的线性插值。形式化定义：</p>
<span displaypfx="" class="mathjax-container">\[f(\lambda x_1+(1-\lambda)x_2)\le \lambda f(x_1)+(1-\lambda)f(x_2),\quad \lambda\in[0,1]\]</span>
<p>凸优化（Convex Optimization）之所以重要，是因为凸目标在凸可行域上没有“坏局部最小值”：任一局部最小值都是全局最小值。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 二阶可导，则 Hessian 提供了最直接的局部判据：在一维中 <span displaypfx="inline-" class="mathjax-container">\(f''(x)\ge 0\)</span> 对应凸；在多维中，若对所有 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 都有 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f(x)\succeq 0\)</span>（半正定，Positive Semidefinite），则 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 是凸函数。若处处 <span displaypfx="inline-" class="mathjax-container">\(\nabla^2 f(x)\succ 0\)</span>（正定，Positive Definite），则函数通常具有更强的严格凸性（Strict Convexity）。</p>
<p>损失函数的形状可以非常多样。需要区分两层含义：</p>
<ul>
<li>损失形式本身：例如 BCE/CE（对概率或对线性模型的 logits）是凸的，MSE 也是凸的。</li>
<li>对参数的整体目标：当把损失与深度网络的非线性参数化组合后，目标函数通常变成非凸（Non-convex），这不是“交叉熵不凸”，而是“网络映射让问题不凸”。</li>
</ul>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/loss-fn-convex.jpg"><img class="alignnone size-full wp-image-40813" src="https://blog.gmem.cc/wp-content/uploads/2026/03/loss-fn-convex.jpg" alt="loss-fn-convex" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">拉格朗日乘数法</span></div>
<p>拉格朗日乘数法（Lagrange Multiplier Method）处理约束优化（Constrained Optimization）：在满足约束的前提下最小化/最大化目标函数。常用写法是把约束写成函数形式：等式约束（Equality Constraints）<span displaypfx="inline-" class="mathjax-container">\(g_i(x)=0\)</span>，不等式约束（Inequality Constraints）<span displaypfx="inline-" class="mathjax-container">\(g_i(x)\le 0\)</span>。</p>
<p>对等式约束，构造拉格朗日函数（Lagrangian）：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(x,\lambda)=f(x)+\sum_{i=1}^{m}\lambda_i g_i(x)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 是目标函数（Objective），<span displaypfx="inline-" class="mathjax-container">\(g_i(x)\)</span> 是约束函数（Constraint Function），<span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span> 是拉格朗日乘子（Lagrange Multiplier）。新增部分 <span displaypfx="inline-" class="mathjax-container">\(\sum_i \lambda_i g_i(x)\)</span> 称为拉格朗日项（Lagrange Term），单个约束对应的项是 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i g_i(x)\)</span>。</p>
<p>在不同应用里乘子的记号可能不同：在优化理论里常写 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span>，在支持向量机（SVM）里常写 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span>（每个样本约束对应一个乘子）。</p>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}(x,\lambda)\)</span> 看作关于原变量 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与乘子 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 的二元函数后，鞍点（Saddle Point）结构是约束被编码进目标函数后的直接结果。 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的角色是压低总代价， <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 的角色是放大任何尚未满足的约束，因此两者天然形成极小极大（Min-Max）结构。</p>
<p>先看等式约束 <span displaypfx="inline-" class="mathjax-container">\(g(x)=0\)</span>。这里必须先把“为什么内层是最大化”说清楚：这不是记号习惯，而是为了把约束编码成一个<span style="background-color: #c0c0c0;">可行点保留原目标、不可行点直接罚到 <span displaypfx="inline-" class="mathjax-container">\(+\infty\)</span></span> 的机制。因此，固定某个 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 后，内层写成 <span displaypfx="inline-" class="mathjax-container">\(\max_\lambda \, (f(x)+\lambda g(x))\)</span>。如果 <span displaypfx="inline-" class="mathjax-container">\(g(x)\neq 0\)</span>，由于 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 不受符号限制，最大化者总能把 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 取到与 <span displaypfx="inline-" class="mathjax-container">\(g(x)\)</span> 同号且绝对值任意大，使 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}(x,\lambda)\to\infty\)</span>；只有当 <span displaypfx="inline-" class="mathjax-container">\(g(x)=0\)</span> 时，拉格朗日项才消失，内层最大值才退化为 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span>。因此 <span displaypfx="inline-" class="mathjax-container">\(\max_\lambda \mathcal{L}(x,\lambda)\)</span> 的作用，是把所有不满足等式约束的点直接排除掉。</p>
<p>不等式约束更容易看出这种“过滤”机制。若约束是 <span displaypfx="inline-" class="mathjax-container">\(g(x)\le 0\)</span> 且乘子满足 <span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>，那么当 <span displaypfx="inline-" class="mathjax-container">\(g(x)&gt;0\)</span> 时，最大化者会把 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 推大，使 <span displaypfx="inline-" class="mathjax-container">\(\lambda g(x)\to\infty\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(g(x)\le 0\)</span> 时，继续增大 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 只会让值变小，因此最优选择是 <span displaypfx="inline-" class="mathjax-container">\(\lambda=0\)</span>。于是 <span displaypfx="inline-" class="mathjax-container">\(\max_{\lambda\ge 0}\mathcal{L}(x,\lambda)\)</span> 对可行点返回原始代价 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span>，对不可行点返回“无限罚款”。外层再对 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 做最小化，就等价于只在可行域内最小化 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[\min_x\ \max_\lambda\ \mathcal{L}(x,\lambda)\]</span>
<p>几何上，这正对应马鞍形曲面：沿 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 方向切开， <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 像一个向上开的“碗”，因为这里在做最小化；沿 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 方向切开， <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 像一个向下开的“拱”，因为这里在做最大化。鞍点 <span displaypfx="inline-" class="mathjax-container">\((x^*,\lambda^*)\)</span> 的含义是：固定 <span displaypfx="inline-" class="mathjax-container">\(\lambda=\lambda^*\)</span> 时， <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 已经不能再把值压低；固定 <span displaypfx="inline-" class="mathjax-container">\(x=x^*\)</span> 时， <span displaypfx="inline-" class="mathjax-container">\(\lambda^*\)</span> 也已经不能再把值抬高。原问题的最优解与约束的恰当作用强度，就在这个交点同时确定。</p>
<p>记号约定：上标星号（Asterisk）<span displaypfx="inline-" class="mathjax-container">\(^*\)</span> 通常表示“最优/最优点处的取值”，例如 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 是最优解， <span displaypfx="inline-" class="mathjax-container">\(\lambda^*\)</span> 是与之对应的最优乘子。</p>
<p>在鞍点 <span displaypfx="inline-" class="mathjax-container">\((x^*,\lambda^*)\)</span>，固定 <span displaypfx="inline-" class="mathjax-container">\(\lambda=\lambda^*\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 使 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 最小；固定 <span displaypfx="inline-" class="mathjax-container">\(x=x^*\)</span> 时 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*\)</span> 使 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 最大，可用不等式写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(x^*,\lambda)\le \mathcal{L}(x^*,\lambda^*)\le \mathcal{L}(x,\lambda^*)\]</span>
<p>上面的鞍点不等式给出的是几何刻画；真正求解时，还需要把“这个点已经不能再降、也不能再升”的直观条件改写成可计算的方程。做法是检查拉格朗日函数（Lagrangian）<span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 对各个变量的一阶变化：若在候选点附近，沿某个允许方向还能继续把值压低（对 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>）或继续把值抬高（对 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span>），那么该点就不可能是鞍点。</p>
<p>这就是一阶必要条件（First-Order Necessary Condition）的来源：若 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 是最优解，则一阶（线性）变化项必须已经消失，否则还存在继续改进的方向。这里“必要”不等于“充分”：满足一阶条件只说明它有可能是最优点，不说明它一定最优；要得到充分结论，还需要二阶条件、凸性（Convexity）或其他结构。</p>
<p>无约束情形最直接。在一维中，若 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 是可微且不在边界上的局部最小点，则向左或向右移动都不能让函数更小，因此斜率必须满足 <span displaypfx="inline-" class="mathjax-container">\(f'(x^*)=0\)</span>；多维里把“斜率”推广为梯度（Gradient），于是条件变为 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)=0\)</span>。</p>
<p>把同样的思路移到等式约束上，求解目标就从“找原函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 的极小点”变成“找拉格朗日函数 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}(x,\lambda)\)</span> 的鞍点”。对可导问题，一阶条件写成：</p>
<span displaypfx="" class="mathjax-container">\[\nabla_x \mathcal{L}(x^*,\lambda^*)=0,\quad g_i(x^*)=0\]</span>
<p>第一式表示：固定 <span displaypfx="inline-" class="mathjax-container">\(\lambda=\lambda^*\)</span> 后，已经找不到能继续降低 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 方向；第二式就是可行性（Feasibility）本身。又因为 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial \lambda_i}=g_i(x)\)</span>，所以“对乘子求偏导为 0”本质上只是把约束 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)=0\)</span> 原样写回。</p>
<p>不等式约束时，乘子必须满足 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i\ge 0\)</span>，因此鞍点结构相应写成：</p>
<span displaypfx="" class="mathjax-container">\[\min_x\ \max_{\lambda\ge 0}\ \mathcal{L}(x,\lambda)\]</span>
<p>此时除了驻点条件，还必须同时满足 KKT 条件（Karush–Kuhn–Tucker Conditions）。其中最关键的是互补松弛（Complementary Slackness）<span displaypfx="inline-" class="mathjax-container">\(\lambda_i g_i(x^*)=0\)</span>：每个约束在最优点只有两种状态——要么它是紧的（Active），即 <span displaypfx="inline-" class="mathjax-container">\(g_i(x^*)=0\)</span>；要么它不紧，此时对应乘子必须为 0，表示该约束在最优点处没有实际作用。</p>
<p>例（等式约束）：最小化 <span displaypfx="inline-" class="mathjax-container">\(f(x,y)=x^2+y^2\)</span>，约束 <span displaypfx="inline-" class="mathjax-container">\(x+y=1\)</span>。拉格朗日函数：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(x,y,\lambda)=x^2+y^2+\lambda(x+y-1)\]</span>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-lagrange.png"><img class="alignnone size-full wp-image-40821" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-lagrange.png" alt="plot-lagrange" width="100%" /></a></p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\lambda(x+y-1)\)</span> 是拉格朗日项（Lagrange Term）：它把等式约束 <span displaypfx="inline-" class="mathjax-container">\(x+y-1=0\)</span> 以乘子加权的形式并入目标函数。</p>
<p>令偏导为零： <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial x}=2x+\lambda=0\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial y}=2y+\lambda=0\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial \lambda}=x+y-1=0\)</span>。注意 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial \lambda}=0\)</span> 就是把约束 <span displaypfx="inline-" class="mathjax-container">\(x+y=1\)</span> 写回来；联立可得 <span displaypfx="inline-" class="mathjax-container">\(x=y=\frac{1}{2}\)</span>。几何上，这意味着最优点处目标函数等高线与约束曲线相切。</p>
<p>例（不等式约束）：最小化 <span displaypfx="inline-" class="mathjax-container">\(f(x)=x^2\)</span>，约束 <span displaypfx="inline-" class="mathjax-container">\(x\ge 1\)</span>。直觉上，无约束最小值在 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span>，但它不满足约束，所以最优点只能落在边界 <span displaypfx="inline-" class="mathjax-container">\(x=1\)</span>。</p>
<ol>
<li>把约束改写成标准形式：令 <span displaypfx="inline-" class="mathjax-container">\(g(x)=1-x\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(x\ge 1\Leftrightarrow g(x)\le 0\)</span>。</li>
<li>构造拉格朗日函数： <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}(x,\lambda)=f(x)+\lambda g(x)=x^2+\lambda(1-x)\)</span>。其中 <span displaypfx="inline-" class="mathjax-container">\(\lambda(1-x)\)</span> 是拉格朗日项（Lagrange Term），不等式情形要求 <span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>。</li>
<li>写出 KKT 的核心条件：可行性（Feasibility）<span displaypfx="inline-" class="mathjax-container">\(1-x\le 0\)</span>；乘子非负 <span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>；驻点（Stationarity）<span displaypfx="inline-" class="mathjax-container">\(\frac{d\mathcal{L}}{dx}=2x-\lambda=0\)</span>；互补松弛（Complementary Slackness）<span displaypfx="inline-" class="mathjax-container">\(\lambda(1-x)=0\)</span>。</li>
<li>解：由 <span displaypfx="inline-" class="mathjax-container">\(2x-\lambda=0\)</span> 得 <span displaypfx="inline-" class="mathjax-container">\(\lambda=2x\)</span>。互补松弛要求 <span displaypfx="inline-" class="mathjax-container">\(\lambda=0\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(x=1\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(\lambda=0\)</span> 则 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span>，但违反 <span displaypfx="inline-" class="mathjax-container">\(x\ge 1\)</span>；因此 <span displaypfx="inline-" class="mathjax-container">\(x^*=1\)</span>，进而 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*=2\)</span>。</li>
<li>解释：在最优点处 <span displaypfx="inline-" class="mathjax-container">\(1-x^*=0\)</span>，该约束是“紧的”（Active），互补松弛允许乘子 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*&gt;0\)</span>，它刻画了该约束在最优点处对解的影响强度。如果最优点落在可行域（Feasible Set）内部（约束不紧， <span displaypfx="inline-" class="mathjax-container">\(1-x^*&lt;0\)</span>），互补松弛会强制 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*=0\)</span>。</li>
</ol>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-lagrange-2.png"><img class="alignnone size-full wp-image-40825" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-lagrange-2.png" alt="plot-lagrange-2" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">对偶问题</span></div>
<p>对偶问题（Dual Problem）从原始问题（Primal Problem）导出下界函数，用于分析可行性、间隙与最优性。标准形式：</p>
<span displaypfx="" class="mathjax-container">\[\min_x\ f(x)\ \text{s.t.}\ g_i(x)\le 0,\ h_j(x)=0\]</span>
<span displaypfx="" class="mathjax-container">\[g(\lambda,\nu)=\inf_x\Big(f(x)+\sum_i \lambda_i g_i(x)+\sum_j \nu_j h_j(x)\Big),\ \lambda_i\ge 0\]</span>
<span displaypfx="" class="mathjax-container">\[\max_{\lambda,\nu}\ g(\lambda,\nu)\ \text{s.t.}\ \lambda\ge 0\]</span>
<p>把括号中的表达式记为拉格朗日函数（Lagrangian）<span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}(x,\lambda,\nu)=f(x)+\sum_i \lambda_i g_i(x)+\sum_j \nu_j h_j(x)\)</span>，则对偶函数（Dual Function）就是 <span displaypfx="inline-" class="mathjax-container">\(g(\lambda,\nu)=\inf_x \mathcal{L}(x,\lambda,\nu)\)</span>：固定乘子后先对原变量求下确界，把它“消去”，再对乘子最大化这个下界。</p>
<div class="blog_h4"><span class="graybg">例：SVM 对偶函数是怎么把变量“消掉”的</span></div>
<p>SVM 的对偶函数（Dual Function）来自一个标准过程：先写出原始问题（Primal Problem）的拉格朗日函数（Lagrangian），再对原变量取下确界（Infimum），把优化问题改写为只依赖乘子变量的形式。在线性支持向量机（Support Vector Machine, SVM）里，这个过程最清楚，因为 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\alpha}\)</span> 的出现既解释了“为什么叫对偶”，也把“把原变量消掉”变成了可逐步计算的代换。</p>
<p>考虑硬间隔（Hard-margin）线性 SVM。原始问题是：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ \frac{1}{2}\|\mathbf{w}\|_2^2\quad \text{s.t.}\quad y_i(\mathbf{w}^\top \mathbf{x}_i+b)-1\ge 0,\ i=1,\dots,n\]</span>
<p>把约束改写成标准不等式形式：</p>
<span displaypfx="" class="mathjax-container">\[g_i(\mathbf{w},b)=1-y_i(\mathbf{w}^\top \mathbf{x}_i+b)\le 0\]</span>
<p>于是拉格朗日函数为：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(\mathbf{w},b,\boldsymbol{\alpha})=\frac{1}{2}\|\mathbf{w}\|_2^2+\sum_{i=1}^{n}\alpha_i\big(1-y_i(\mathbf{w}^\top \mathbf{x}_i+b)\big),\quad \alpha_i\ge 0\]</span>
<p>对偶函数（Dual Function）的定义是：固定乘子 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\alpha}\)</span>，对原变量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 取下确界：</p>
<span displaypfx="" class="mathjax-container">\[g(\boldsymbol{\alpha})=\inf_{\mathbf{w},b}\mathcal{L}(\mathbf{w},b,\boldsymbol{\alpha})\]</span>
<p>这里的关键结构已经出现了：原始问题直接优化 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span>，而对偶函数先把 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\alpha}\)</span> 固定住，把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 当成待消去变量。随后得到的最大化问题，只剩乘子变量 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\alpha}\)</span>。这就是“对偶”的来源：<span style="background-color: #c0c0c0;">同一个最优化问题，被改写成了另一组变量上的伴随优化问题</span>。</p>
<p>现在把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 完整消掉。先求驻点条件（Stationarity Conditions）。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 求偏导：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial \mathbf{w}}=\mathbf{w}-\sum_{i=1}^{n}\alpha_i y_i \mathbf{x}_i=\mathbf{0}\]</span>
<p>因此</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}=\sum_{i=1}^{n}\alpha_i y_i \mathbf{x}_i\]</span>
<p>对 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 求偏导：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial b}=-\sum_{i=1}^{n}\alpha_i y_i=0\]</span>
<p>因此</p>
<span displaypfx="" class="mathjax-container">\[\sum_{i=1}^{n}\alpha_i y_i=0\]</span>
<p>把这些结果代回拉格朗日函数之前，先把它展开成便于逐项代换的形式：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(\mathbf{w},b,\boldsymbol{\alpha})=\frac{1}{2}\mathbf{w}^\top\mathbf{w}+\sum_{i=1}^{n}\alpha_i-\sum_{i=1}^{n}\alpha_i y_i\,\mathbf{w}^\top\mathbf{x}_i-b\sum_{i=1}^{n}\alpha_i y_i\]</span>
<p>第一项代入 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}=\sum_{i=1}^{n}\alpha_i y_i \mathbf{x}_i\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\frac{1}{2}\mathbf{w}^\top\mathbf{w}=\frac{1}{2}\Big(\sum_{i=1}^{n}\alpha_i y_i \mathbf{x}_i\Big)^\top\Big(\sum_{j=1}^{n}\alpha_j y_j \mathbf{x}_j\Big)\]</span>
<span displaypfx="" class="mathjax-container">\[=\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<p>第二项保持不变：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{i=1}^{n}\alpha_i\]</span>
<p>第三项代入同一个 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 表达式：</p>
<span displaypfx="" class="mathjax-container">\[-\sum_{i=1}^{n}\alpha_i y_i\,\mathbf{w}^\top\mathbf{x}_i=-\sum_{i=1}^{n}\alpha_i y_i\Big(\sum_{j=1}^{n}\alpha_j y_j \mathbf{x}_j\Big)^\top\mathbf{x}_i\]</span>
<span displaypfx="" class="mathjax-container">\[=-\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_j^\top\mathbf{x}_i\]</span>
<span displaypfx="" class="mathjax-container">\[=-\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<p>最后一项利用 <span displaypfx="inline-" class="mathjax-container">\(\sum_{i=1}^{n}\alpha_i y_i=0\)</span> 直接消失：</p>
<span displaypfx="" class="mathjax-container">\[-b\sum_{i=1}^{n}\alpha_i y_i=0\]</span>
<p>因此</p>
<span displaypfx="" class="mathjax-container">\[g(\boldsymbol{\alpha})=\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j+\sum_{i=1}^{n}\alpha_i-\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<span displaypfx="" class="mathjax-container">\[=\sum_{i=1}^{n}\alpha_i-\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<p>于是对偶问题（Dual Problem）就是：</p>
<span displaypfx="" class="mathjax-container">\[\max_{\boldsymbol{\alpha}}\ g(\boldsymbol{\alpha})\quad \text{s.t.}\quad \alpha_i\ge 0,\ \sum_{i=1}^{n}\alpha_i y_i=0\]</span>
<p>此时“对偶”二字的含义就精确了：原始问题在参数空间里直接求 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span>，对偶问题则在乘子空间里求 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\alpha}\)</span>；两者来自同一个拉格朗日结构，描述的是同一个最优解的两种表示。对 SVM 而言，更重要的结构变化是：数据 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i\)</span> 不再单独出现，而只通过内积（Inner Product）<span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i^\top\mathbf{x}_j\)</span> 进入目标函数，这正是核技巧（Kernel Trick）的入口。</p>
<p>若问题是凸的且满足 Slater 条件，则强对偶（Strong Duality）成立，可以用鞍点形式表达“先最小化再最大化”与“先最大化再最小化”等价：</p>
<span displaypfx="" class="mathjax-container">\[\min_x\max_{\lambda\ge 0,\nu}\mathcal{L}(x,\lambda,\nu)=\max_{\lambda\ge 0,\nu}\min_x\mathcal{L}(x,\lambda,\nu)\]</span>
<p>对偶函数之所以是“下界”，来自一个简单但关键的不等式：对任意可行解 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>（满足所有约束）与任意 <span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>，都有</p>
<span displaypfx="" class="mathjax-container">\[f(x)\ge f(x)+\sum_i \lambda_i g_i(x)+\sum_j \nu_j h_j(x)\ge \inf_{x'}\Big(f(x')+\sum_i \lambda_i g_i(x')+\sum_j \nu_j h_j(x')\Big)=g(\lambda,\nu)\]</span>
<p>因此对任何 <span displaypfx="inline-" class="mathjax-container">\((\lambda,\nu)\)</span>，都有 <span displaypfx="inline-" class="mathjax-container">\(g(\lambda,\nu)\le p^*\)</span>（原始最优值），这就是弱对偶（Weak Duality）。</p>
<p>弱对偶（Weak Duality）总成立；也就是说，对偶问题给出的值永远不会高于原始问题的最优值。但弱对偶只保证“对偶值是下界”，并不保证这个下界恰好贴住原始最优值。若两者之间还差一截，这个差距就叫对偶间隙（Duality Gap）。</p>
<p>强对偶（Strong Duality）则更进一步：它要求原始最优值与对偶最优值完全相等，也就是对偶问题不只是给出一个保守下界，而是精确刻画了原问题的最优值。对很多凸优化问题而言，Slater 条件正是让这种“下界刚好贴住最优值”的关键保证。</p>
<p>从直观上看，Slater 条件要求可行域内部真的存在一个严格可行点。这意味着约束系统不是被边界死死卡住的退化结构，而是有真实的内部空间。可行域一旦有内部，拉格朗日乘子就更容易稳定地描述“目标函数下降趋势”和“约束反作用力”之间的平衡，因此原始问题与对偶问题之间更不容易出现缝隙。在凸优化里，这就是为什么 Slater 条件常被看作强对偶成立的重要通行证。</p>
<p>直观上，对偶变量（Dual Variables）可以看作约束的“影子价格（Shadow Price）”：如果把约束放宽一点点，最优目标值会如何变化。在很多问题中（例如支持向量机（SVM）），对偶化会把优化变量从“模型参数”转成“约束乘子”，并把数据依赖压缩为内积，从而自然导出核技巧（Kernel Trick）。</p>
<div class="blog_h4"><span class="graybg">影子价格（Shadow Price）</span></div>
<p>对偶变量（Dual Variable）常被称为影子价格（Shadow Price），因为它衡量的是：<span style="background-color: #c0c0c0;">如果把某条约束稍微放宽一点，最优目标值会改善多少</span>。这里的“价格”不是市场价格，而是“约束资源有多值钱”的边际刻度。</p>
<p>可以把约束想成一种稀缺资源。例如训练时有显存限制、预算限制、风险限制或几何边界限制；如果某条约束非常紧，那么它就像一个卡脖子的瓶颈。此时只要把这条约束稍微放宽一点，最优目标值就可能明显改善，于是它对应的对偶变量就会比较大。反过来，如果某条约束本来就很松，放宽它也几乎没有收益，那么它对应的对偶变量通常就是 0 或接近 0。</p>
<p>在数学上，这个直觉可以写成一种局部敏感度关系。若把约束写成</p>
<span displaypfx="" class="mathjax-container">\[g_i(x)\le b_i\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(b_i\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 条约束允许的“资源上限”或“边界位置”，那么对应的最优值函数可以记为 <span displaypfx="inline-" class="mathjax-container">\(p^*(b)\)</span>。在适当条件下，对偶变量 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\)</span> 可以理解为最优值对 <span displaypfx="inline-" class="mathjax-container">\(b_i\)</span> 的边际变化率，也就是“把第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 条约束放宽一点，最优值会朝什么方向变化、变化多快”。</p>
<p>在最小化问题中，若某条约束对应的 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\)</span> 很大，通常表示这条约束非常关键：它一旦被放宽，最优目标值会明显下降；若 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*=0\)</span>，则说明该约束在当前最优点并没有真正起作用，放宽它也不会立刻带来收益。这与前面的互补松弛完全一致：<span style="background-color: #c0c0c0;">只有真正卡住最优解的约束，才会拥有非零影子价格</span>。</p>
<p>因此，影子价格提供了一个非常有用的解释视角：原始变量告诉我们“最优解长什么样”，而对偶变量告诉我们“哪些约束最贵、最紧、最值得被放宽”。这也是为什么在优化理论、经济学和机器学习里，对偶变量不仅是求解工具，也是理解模型结构的重要语言。</p>
<div class="blog_h4"><span class="graybg">Slater 条件</span></div>
<p>Slater 条件（Slater's Condition）是凸优化（Convex Optimization）里最常见的正则性条件（Regularity Condition）之一。它的作用不是改变优化问题本身，而是保证对偶理论能够“工作得很干净”：在满足它时，很多凸问题会满足强对偶（Strong Duality），也就是原始问题最优值与对偶问题最优值相等；同时，KKT 条件也更容易从“必要条件”提升为判定最优性的核心条件。</p>
<p>对标准凸优化问题</p>
<span displaypfx="" class="mathjax-container">\[ \min_x f(x)\quad \text{s.t.}\quad g_i(x)\le 0,\ h_j(x)=0 \]</span>
<p>如果 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 和每个 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)\)</span> 都是凸函数（Convex Function），每个 <span displaypfx="inline-" class="mathjax-container">\(h_j(x)\)</span> 都是仿射函数（Affine Function），那么 Slater 条件要求：<span style="background-color: #c0c0c0;">至少存在一个严格可行点（Strictly Feasible Point）</span> <span displaypfx="inline-" class="mathjax-container">\(\tilde{x}\)</span>，使得所有不等式约束都被“严格满足”，也就是</p>
<span displaypfx="" class="mathjax-container">\[ g_i(\tilde{x})&lt;0,\ \forall i,\qquad h_j(\tilde{x})=0,\ \forall j \]</span>
<p>这里“严格满足”四个字最关键。普通可行点只要求 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)\le 0\)</span>，也就是允许刚好压在边界上；而 Slater 条件要求存在某个点，能让所有不等式约束都留出一点余量，也就是完全处在可行域内部，而不是贴着边界。</p>
<p>这个条件可以用一个非常直观的图像来理解。把不等式约束围成的可行域想成一个房间：</p>
<ul>
<li>如果房间内部真的有空间，存在一个点站在房间里，不碰任何墙，这就是满足 Slater 条件。</li>
<li>如果所谓“可行域”其实只是几面墙交出来的一条线、一个角、甚至一个点，根本没有真正的内部空间，那么 Slater 条件通常就不满足。</li>
</ul>
<p>因此，Slater 条件本质上是在说：<span style="background-color: #c0c0c0;">这个凸约束系统不能只是勉强拼出一个边界碎片，而要有真正的内部</span>。一旦内部存在，原始问题和对偶问题之间的间隙通常就会消失，拉格朗日乘子和 KKT 条件也会变得更稳定、更有解释力。</p>
<p>一个简单例子可以把它说清楚。考虑约束</p>
<span displaypfx="" class="mathjax-container">\[x\ge 0\]</span>
<p>写成标准形式是 <span displaypfx="inline-" class="mathjax-container">\(g(x)=-x\le 0\)</span>。这个约束满足 Slater 条件，因为取 <span displaypfx="inline-" class="mathjax-container">\(\tilde{x}=1\)</span> 时，有 <span displaypfx="inline-" class="mathjax-container">\(g(1)=-1&lt;0\)</span>。这说明可行域 <span displaypfx="inline-" class="mathjax-container">\([0,+\infty)\)</span> 不只是边界点 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span>，而是真正包含内部区域。</p>
<p>再看一个不满足 Slater 条件的例子：</p>
<span displaypfx="" class="mathjax-container">\[x^2\le 0\]</span>
<p>由于 <span displaypfx="inline-" class="mathjax-container">\(x^2\)</span> 永远不小于 0，这个约束唯一允许的点只有 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span>。可行域虽然非空，但没有任何点能让 <span displaypfx="inline-" class="mathjax-container">\(x^2&lt;0\)</span> 成立，所以不存在严格可行点，Slater 条件不成立。这样的约束系统只有边界、没有内部。</p>
<p>在机器学习常见的凸问题里，Slater 条件之所以频繁出现，是因为它几乎就是“强对偶成立的通行证”。例如在线性规划、逻辑回归的某些约束变体、支持向量机（SVM）的凸二次规划中，只要能找到一个严格可行点，就通常可以放心地从原始问题走到对偶问题，再用 KKT 条件解释最优解结构。反过来，如果没有 Slater 条件，就可能出现对偶间隙（Duality Gap），也就是对偶最优值严格小于原始最优值，此时只看对偶或只看 KKT 就不一定足够。</p>
<div class="blog_h4"><span class="graybg">概念速查</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">概念</td>
<td style="text-align: center;">它回答的问题</td>
<td style="text-align: center;">最核心的一句话</td>
<td style="text-align: center;">在这一章里的作用</td>
</tr>
</thead>
<tbody>
<tr>
<td>弱对偶（Weak Duality）</td>
<td>对偶问题和原始问题之间至少有什么关系</td>
<td>对偶最优值永远不会高于原始最优值；它天然是一个下界</td>
<td>先建立“对偶为什么有意义”的最低保证</td>
</tr>
<tr>
<td>对偶间隙（Duality Gap）</td>
<td>为什么有时对偶值还不等于原始最优值</td>
<td>如果对偶下界还没贴住原始最优值，两者之间的差就是对偶间隙</td>
<td>解释为什么“有对偶”不等于“对偶已经足够”</td>
</tr>
<tr>
<td>强对偶（Strong Duality）</td>
<td>什么时候对偶问题就足以精确刻画原问题</td>
<td>原始最优值与对偶最优值完全相等，对偶不再只是保守下界</td>
<td>为从原始问题转到对偶问题提供理论正当性</td>
</tr>
<tr>
<td>Slater 条件（Slater's Condition）</td>
<td>凸优化里什么条件有助于强对偶成立</td>
<td>只要可行域内部存在严格可行点，很多凸问题就能消除对偶间隙</td>
<td>说明为什么强对偶和 KKT 在凸问题里经常可用</td>
</tr>
<tr>
<td>影子价格（Shadow Price）</td>
<td>对偶变量到底在解释什么</td>
<td>它衡量“把某条约束放宽一点，最优值会改善多少”</td>
<td>赋予拉格朗日乘子清晰的经济 / 几何解释</td>
</tr>
<tr>
<td>KKT 条件</td>
<td>最优解在约束下必须满足哪些平衡关系</td>
<td>目标函数的下降趋势与约束施加的反作用力在最优点平衡</td>
<td>把可行性、乘子、驻点与互补松弛统一成最优性条件</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">KKT 条件</span></div>
<div class="blog_h4"><span class="graybg">背景和定义</span></div>
<p>KKT 条件（Karush–Kuhn–Tucker Conditions）讨论的是<span style="background-color: #c0c0c0;">带约束优化问题在最优点必须满足什么条件</span>。无约束优化里，常见做法是令梯度（Gradient）为 0；但一旦问题带有不等式约束或等式约束，只看 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x)=0\)</span> 就不够了，因为最优点很可能被约束“顶”在边界上，而不是落在自由空间里的普通驻点。</p>
<p>标准形式写作：</p>
<span displaypfx="" class="mathjax-container">\[\min_x f(x)\quad \text{s.t.}\quad g_i(x)\le 0,\ h_j(x)=0\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 是目标函数（Objective Function），表示希望最小化的量；<span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是优化变量（Optimization Variable）；<span displaypfx="inline-" class="mathjax-container">\(g_i(x)\le 0\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个不等式约束（Inequality Constraint）；<span displaypfx="inline-" class="mathjax-container">\(h_j(x)=0\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个等式约束（Equality Constraint）。KKT 条件给出的就是：当 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 真的是一个最优解时，它与对应的拉格朗日乘子（Lagrange Multipliers）之间必须满足一组相互配合的条件。</p>
<p>在凸优化（Convex Optimization）里，如果问题满足适当的正则性条件，例如 Slater 条件（Slater's Condition），KKT 条件往往不只是必要条件，还可以成为判定最优性的核心工具。在线性规划、二次规划、支持向量机（Support Vector Machine, SVM）和许多机器学习训练问题中，KKT 条件都直接参与求解过程。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/kkt-paraboloid-plane.png"><img class="alignnone size-full wp-image-41393" src="https://blog.gmem.cc/wp-content/uploads/2026/03/kkt-paraboloid-plane.png" alt="kkt-paraboloid-plane" width="2107" height="1134" /></a></p>
<div class="blog_h4"><span class="graybg">图解：四组条件如何逐条理解</span></div>
<p>上图使用的是活跃边界 <span displaypfx="inline-" class="mathjax-container">\(g(x)=1-x_1-x_2=0\)</span>。边界 <span displaypfx="inline-" class="mathjax-container">\(x_1+x_2=1\)</span> 的切向方向可取 <span displaypfx="inline-" class="mathjax-container">\((1,-1)\)</span>；与它垂直的向量都是法向量（Normal Vector），例如 <span displaypfx="inline-" class="mathjax-container">\((1,1)\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\((-1,-1)\)</span>。如果把约束写成函数 <span displaypfx="inline-" class="mathjax-container">\(g(x)=1-x_1-x_2\)</span>，那么梯度 <span displaypfx="inline-" class="mathjax-container">\(\nabla g(x)=(-1,-1)\)</span> 就是一条法向量；它指向 <span displaypfx="inline-" class="mathjax-container">\(g(x)\)</span> 增大的方向，也就是不可行侧。</p>
<p>图中的橙色箭头对应驻点条件（Stationarity）里真正参与平衡的两个向量：目标梯度 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)\)</span> 与乘子加权后的约束法向量 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*\nabla g(x^*)\)</span>。它们在最优点首尾相接后得到 0，于是 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)+\lambda^*\nabla g(x^*)=0\)</span> 不是抽象记号，而是边界最优点上的几何平衡：</p>
<ul>
<li><span style="background-color: #c0c0c0;">原始可行性（Primal Feasibility）</span>：解必须落在可行域内。对这张图而言，可行域是半空间 <span displaypfx="inline-" class="mathjax-container">\(x_1+x_2\ge 1\)</span>，最优点 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 落在它的活跃边界 <span displaypfx="inline-" class="mathjax-container">\(x_1+x_2=1\)</span> 上。</li>
<li><span style="background-color: #c0c0c0;">对偶可行性（Dual Feasibility）</span>：不等式约束的乘子必须非负，即 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*\ge 0\)</span>。图中的计算给出 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*=0.65\)</span>，表示这条边界在最优点处确实对目标下降施加了正的“反作用”。</li>
<li><span style="background-color: #c0c0c0;">驻点条件（Stationarity）</span>：沿边界的切向方向已经不能继续下降，因此目标梯度不再含有切向分量，只能落在法向空间里。单个活跃约束时，这就变成 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)=-\lambda^*\nabla g(x^*)\)</span>。图中的两根橙色箭头正是这两个大小相等、方向相反的向量。</li>
<li><span style="background-color: #c0c0c0;">互补松弛（Complementary Slackness）</span>：约束若不接触最优点，就不会产生乘子；约束一旦卡在最优点上，对应乘子才会变成正值。当前图像展示的是活跃边界情形，所以 <span displaypfx="inline-" class="mathjax-container">\(g(x^*)=0\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*&gt;0\)</span> 同时成立。</li>
</ul>
<p>法向量之所以关键，是因为它刻画了“离开边界最快”的方向；切向方向则刻画了“沿边界滑动”的方向。边界最优点的本质结论是：<span style="background-color: #c0c0c0;">目标函数想继续下降的那一部分趋势，已经完全被约束边界的法向作用抵消；沿边界本身则不存在进一步下降方向</span>。</p>
<div class="blog_h4"><span class="graybg">具像化描述</span></div>
<p>可以把 KKT 条件想成一个“遛狗”问题。优化目标是：狗想尽可能往远处跑；约束条件是：主人手里只有一根 5 米长的狗绳，因此狗的活动范围不能超过这条绳子的长度。不等式约束 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)\le 0\)</span> 的作用，就像在说“不能超过这根绳子允许的极限”；等式约束 <span displaypfx="inline-" class="mathjax-container">\(h_j(x)=0\)</span> 则对应那些必须被精确满足的固定条件。</p>
<p>这个比喻里最重要的是区分“绳子是否真正起作用”。如果狗最后只跑到 3 米处就停下，例如已经闻到味道、找到目标，或者在那里已经达到最优状态，那么绳子仍然是松的，还留有 2 米余量。这时约束虽然存在，但它没有真正限制住解。相反，如果狗拼命往前冲，最后正好跑到 5 米极限，绳子就会被拉紧；此时狗还想继续前进，但被约束挡住了。KKT 条件刻画的正是这种“<span style="background-color: #c0c0c0;">目标继续改进的趋势，与约束施加的阻拦作用，在最优点达到平衡</span>”的状态。</p>
<p>其中最形象的一条就是互补松弛（Complementary Slackness）。它表达的是：每一个不等式约束在最优点都只有两种状态。</p>
<ul>
<li>如果某个约束正好卡在边界上，即 <span displaypfx="inline-" class="mathjax-container">\(g_i(x^*)=0\)</span>，就像狗刚好把 5 米狗绳拉到极限，此时这条绳子真的“顶住了”最优解，它对应的乘子 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\)</span> 可以大于 0。</li>
<li>如果某个约束离边界还有余量，即 <span displaypfx="inline-" class="mathjax-container">\(g_i(x^*)&lt;0\)</span>，就像狗只跑到 3 米处，绳子仍然松弛，那么它对应的乘子必须是 0，也就是 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*=0\)</span>。</li>
</ul>
<p>因此，乘子 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\)</span> 可以直观理解为绳子的<span style="background-color: #c0c0c0;">拉力</span>，也就是约束对最优解施加的压力。在优化语言里，这个量也常被解释为影子价格（Shadow Price）：如果把这条约束稍微放宽一点，最优目标值会改善多少。拉力不为 0，说明这条约束正在真正影响解；拉力为 0，说明这条约束虽然存在，但在最优点处并没有发挥作用。</p>
<div class="blog_h4"><span class="graybg">公式逐元素解释</span></div>
<p>把原问题写成统一形式后，先定义拉格朗日函数（Lagrangian）：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(x,\lambda,\nu)=f(x)+\sum_i \lambda_i g_i(x)+\sum_j \nu_j h_j(x)\]</span>
<p>这个式子里的每个元素都有明确含义：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span>：拉格朗日函数，把目标函数和约束统一写进一个式子里。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(x\)</span>：原始变量（Primal Variable），也就是模型真正要优化的参数。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span>：目标函数，表示希望最小化的代价、损失或能量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(g_i(x)\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个不等式约束函数；要求它满足 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)\le 0\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(h_j(x)\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个等式约束函数；要求它满足 <span displaypfx="inline-" class="mathjax-container">\(h_j(x)=0\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda_i\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个不等式约束对应的拉格朗日乘子；它衡量该约束施加的“压力”或“拉力”大小，也可理解为对应约束资源的影子价格。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\nu_j\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个等式约束对应的拉格朗日乘子。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sum_i\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\sum_j\)</span>：分别表示把所有不等式约束和所有等式约束的影响累加起来。</li>
</ul>
<p>在此基础上，KKT 条件通常写成四组：</p>
<span displaypfx="" class="mathjax-container">\[\text{Primal feasibility: } g_i(x^*)\le 0,\ h_j(x^*)=0\]</span>
<span displaypfx="" class="mathjax-container">\[\text{Dual feasibility: } \lambda_i^*\ge 0\]</span>
<span displaypfx="" class="mathjax-container">\[\text{Stationarity: } \nabla f(x^*)+\sum_i \lambda_i^*\nabla g_i(x^*)+\sum_j \nu_j^*\nabla h_j(x^*)=0\]</span>
<span displaypfx="" class="mathjax-container">\[\text{Complementary slackness: } \lambda_i^* g_i(x^*)=0\]</span>
<p>这四组条件可以逐条理解：</p>
<ul>
<li><span style="background-color: #c0c0c0;">原始可行性（Primal Feasibility）</span>：最优解 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 首先必须是合法的，不能跑到可行域之外。也就是说，所有不等式约束都要满足 <span displaypfx="inline-" class="mathjax-container">\(g_i(x^*)\le 0\)</span>，所有等式约束都要精确满足 <span displaypfx="inline-" class="mathjax-container">\(h_j(x^*)=0\)</span>。</li>
<li><span style="background-color: #c0c0c0;">对偶可行性（Dual Feasibility）</span>：不等式约束的乘子必须非负，即 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\ge 0\)</span>。这保证了它们表达的是“阻止变量越界的压力”，而不是把解往不可行方向拉过去的反向力量。</li>
<li><span style="background-color: #c0c0c0;">驻点条件（Stationarity）</span>：在最优点处，目标函数的梯度 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)\)</span> 与所有约束梯度加权后的合力必须平衡为 0。这里 <span displaypfx="inline-" class="mathjax-container">\(\nabla f(x^*)\)</span> 表示目标函数在 <span displaypfx="inline-" class="mathjax-container">\(x^*\)</span> 处最陡上升方向；<span displaypfx="inline-" class="mathjax-container">\(\nabla g_i(x^*)\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\nabla h_j(x^*)\)</span> 分别表示约束边界的法向方向；乘子 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\nu_j^*\)</span> 则控制这些方向各自施加多少“反作用力”。这条式子本质上是在说：最优点处已经不存在任何仍然可行且还能继续下降的方向。</li>
<li><span style="background-color: #c0c0c0;">互补松弛（Complementary Slackness）</span>：每个不等式约束都满足 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^* g_i(x^*)=0\)</span>。因为这是两个量的乘积等于 0，所以只能出现两种情况：要么 <span displaypfx="inline-" class="mathjax-container">\(g_i(x^*)=0\)</span>，说明约束正好顶在边界上；要么 <span displaypfx="inline-" class="mathjax-container">\(\lambda_i^*=0\)</span>，说明这条约束在最优点没有施加压力。它精确区分了“活跃约束（Active Constraint）”和“不活跃约束（Inactive Constraint）”。</li>
</ul>
<p>一个一维例子可以把这些符号落到实处。考虑：</p>
<span displaypfx="" class="mathjax-container">\[\min_x\ (x+1)^2\quad \text{s.t.}\quad x\ge 0\]</span>
<p>把约束写成标准形式 <span displaypfx="inline-" class="mathjax-container">\(g(x)=-x\le 0\)</span>，于是拉格朗日函数是：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(x,\lambda)=(x+1)^2+\lambda(-x)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\((x+1)^2\)</span> 是目标函数；<span displaypfx="inline-" class="mathjax-container">\(-x\le 0\)</span> 是不等式约束；<span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 是这条约束对应的乘子。KKT 条件变成：</p>
<span displaypfx="" class="mathjax-container">\[x\ge 0,\quad \lambda\ge 0,\quad 2(x+1)-\lambda=0,\quad \lambda x=0\]</span>
<p>这四项分别表示：解必须在 <span displaypfx="inline-" class="mathjax-container">\(x\ge 0\)</span> 的合法区域内；乘子必须非负；目标函数与约束施加的作用力在最优点平衡；约束要么卡住解、要么不施加压力。无约束时， <span displaypfx="inline-" class="mathjax-container">\((x+1)^2\)</span> 的最低点在 <span displaypfx="inline-" class="mathjax-container">\(x=-1\)</span>，但这不满足 <span displaypfx="inline-" class="mathjax-container">\(x\ge 0\)</span>，所以真正最优点被“推”到边界 <span displaypfx="inline-" class="mathjax-container">\(x^*=0\)</span>；再代入驻点条件 <span displaypfx="inline-" class="mathjax-container">\(2(x+1)-\lambda=0\)</span>，得到 <span displaypfx="inline-" class="mathjax-container">\(\lambda^*=2\)</span>。这正对应“边界在最优点处确实对解施加了压力”。</p>
<p>如果把目标函数换成 <span displaypfx="inline-" class="mathjax-container">\(\min_x (x-2)^2\ \text{s.t.}\ x\ge 0\)</span>，无约束最优点就是 <span displaypfx="inline-" class="mathjax-container">\(x=2\)</span>，它本来就在可行域内部，因此约束没有真正碰到最优点。此时 KKT 会给出 <span displaypfx="inline-" class="mathjax-container">\(x^*=2\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\lambda^*=0\)</span>。这就是互补松弛最直观的含义：<span style="background-color: #c0c0c0;">边界没有碰到解，压力就自动消失；边界一旦碰到解，乘子就会变成非零</span>。</p>
<div class="blog_h1"><span class="graybg">概率论与统计</span></div>
<p>概率论与统计处理两类问题：第一类是<span style="background-color: #c0c0c0;">在不确定性下如何描述事件、变量与数据生成过程</span>；第二类是<span style="background-color: #c0c0c0;">在只观察到有限样本时，如何反推总体规律与模型参数</span>。在 AI 中，分类概率、回归噪声、采样、似然训练、置信评估与后验推断都建立在这套语言之上。</p>
<div class="blog_h2"><span class="graybg">基础概念</span></div>
<p>在进入公式前，先区分几个最常混淆的基础对象。概率论不是一开始就讨论“均值”和“方差”，而是先规定<span style="background-color: #c0c0c0;">随机试验的结果空间、结果上的事件，以及把结果映射成数的随机变量</span>，之后才谈分布、期望和统计推断。</p>
<ul>
<li>样本空间（Sample Space）<span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span>：一次随机试验所有可能结果的集合，其中希腊字母 <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span> 只是“全部可能结果”的记号。掷骰子时 <span displaypfx="inline-" class="mathjax-container">\(\Omega=\{1,2,3,4,5,6\}\)</span>，表示所有可能点数组成的集合。</li>
<li>事件（Event）：样本空间的子集，表示“哪些结果算作某件事发生”。例如“点数为偶数”对应集合 <span displaypfx="inline-" class="mathjax-container">\(\{2,4,6\}\)</span>；这句话的意思是，只要试验结果落在这个集合里，就说该事件发生。</li>
<li>随机变量（Random Variable）<span displaypfx="inline-" class="mathjax-container">\(X\)</span>：把随机结果映射为数的函数，可写作 <span displaypfx="inline-" class="mathjax-container">\(X:\Omega\to\mathbb{R}\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 是这个函数的名字， <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span> 是输入端的样本空间，箭头 <span displaypfx="inline-" class="mathjax-container">\(\to\)</span> 表示“映射到”， <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}\)</span> 表示实数集合。也就是说，每个随机结果都会被 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 变成一个实数。掷骰子时最自然的随机变量就是“点数本身”；在机器学习里，输入 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>、标签 <span displaypfx="inline-" class="mathjax-container">\(Y\)</span>、噪声 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 都是随机变量。</li>
<li>概率分布（Probability Distribution）：描述随机变量取不同值的概率规律。离散情形常写作 <span displaypfx="inline-" class="mathjax-container">\(P(X=x)\)</span>，意思是“随机变量 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 恰好取值 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的概率”；连续情形常写作密度 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>，它不是某一点的概率本身，而是概率密度函数（Probability Density Function, PDF），需要在区间上积分才得到概率。</li>
<li>期望（Expectation）<span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]\)</span>：按概率加权的平均，回答“长期来看这个随机变量的典型水平在哪里”。这里 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}\)</span> 是 expectation 的标准记号，方括号中的 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 表示“对哪个随机变量取期望”。</li>
<li>方差（Variance）<span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(X)\)</span>：围绕期望的波动强度，回答“它通常偏离平均水平多大”。其中 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}\)</span> 是 variance 的记号，括号里的 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 表示“考察哪个随机变量的波动”。</li>
<li>参数（Parameter）与统计量（Statistic）：参数描述总体，例如高斯分布中的 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 表示均值（Mean）， <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 表示方差（Variance）；统计量则由样本计算出来，例如 <span displaypfx="inline-" class="mathjax-container">\(\bar{x}\)</span> 表示样本均值（Sample Mean），上面的横线读作“x bar”，意思是“样本中所有 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的平均值”。统计学习的核心任务之一，就是用统计量去估计未知参数。</li>
</ul>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/probability.jpg"><img class="alignnone size-full wp-image-40841" src="https://blog.gmem.cc/wp-content/uploads/2026/03/probability.jpg" alt="probability" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">似然和概率</span></div>
<p>概率（Probability）与似然（Likelihood）都可以写成 <span displaypfx="inline-" class="mathjax-container">\(p(x|\theta)\)</span> 这样的形式，但它们把“谁是已知、谁是待判断对象”放在不同位置。概率把参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 视为已知、把数据 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 视为随机结果；似然则把数据 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 固定下来，把参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 当作待比较对象。</p>
<p>也正因为公式形式相同，二者很容易混淆；真正的区别不在写法，而在它们回答的是相反方向的问题。条件分布 <span displaypfx="inline-" class="mathjax-container">\(p(x|\theta)\)</span> 在统计里经常同时出现在两种语境中：当参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 固定、数据 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 变化时，它表示“在这个模型下观察到不同数据的概率有多大”；当数据 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 固定、参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 变化时，它表示“哪些参数更能解释这份已经观察到的数据”。</p>
<p>因此，概率的读法是<span style="background-color: #c0c0c0;">参数已知，看数据</span>。例如抛硬币模型里，若已知正面概率 <span displaypfx="inline-" class="mathjax-container">\(\theta=0.7\)</span>，那么 10 次里出现 8 次正面的概率是</p>
<span displaypfx="" class="mathjax-container">\[P(X=8|\theta=0.7)={10 \choose 8}0.7^8 0.3^2\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\({10 \choose 8}\)</span> 表示组合数（Binomial Coefficient）：从 10 次试验里选出 8 次作为正面的不同选法一共有多少种。它负责计数“8 次正面可以出现在哪 8 个位置上”，不是额外的概率项。</p>
<p>而 <span displaypfx="inline-" class="mathjax-container">\(0.7^8 0.3^2\)</span> 来自独立重复试验的乘法法则：如果每次抛掷相互独立，且正面概率是 <span displaypfx="inline-" class="mathjax-container">\(0.7\)</span>、反面概率是 <span displaypfx="inline-" class="mathjax-container">\(0.3\)</span>，那么任意一个“8 次正面、2 次反面”的具体序列，其概率都是 8 个 <span displaypfx="inline-" class="mathjax-container">\(0.7\)</span> 与 2 个 <span displaypfx="inline-" class="mathjax-container">\(0.3\)</span> 的乘积，也就是 <span displaypfx="inline-" class="mathjax-container">\(0.7^8 0.3^2\)</span>。</p>
<p>这里被当作变量的是结果 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>；问题是“给定参数，这样的数据是否常见”。这就是通常意义上的概率或概率密度。</p>
<p>似然的读法正好反过来：<span style="background-color: #c0c0c0;">数据已知，看参数</span>。假设现在已经观察到 10 次抛硬币里有 8 次正面，这组数据不再变化；真正变化的是候选参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>。于是同一个模型被改写为似然函数（Likelihood Function）：</p>
<span displaypfx="" class="mathjax-container">\[L(\theta|X=8)=P(X=8|\theta)={10 \choose 8}\theta^8(1-\theta)^2\]</span>
<p>此时 <span displaypfx="inline-" class="mathjax-container">\(L(\theta|x)\)</span> 不是“参数取某个值的概率”，而是一个关于 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的评分函数：哪个 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 让已观察到的数据 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 更容易出现，哪个 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的似然就更大。最大似然估计（Maximum Likelihood Estimation, MLE）做的正是这件事：寻找使 <span displaypfx="inline-" class="mathjax-container">\(L(\theta|x)\)</span> 最大的参数。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/likelihood.png"><img class="alignnone size-full wp-image-40879" src="https://blog.gmem.cc/wp-content/uploads/2026/03/likelihood.png" alt="likelihood" width="100%" /></a></p>
<p>这里有一个必须严格区分的点：<span style="background-color: #c0c0c0;">似然不是参数的概率分布</span>。对固定数据来说， <span displaypfx="inline-" class="mathjax-container">\(L(\theta|x)\)</span> 不要求对 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 积分为 1，也不能直接解释为 <span displaypfx="inline-" class="mathjax-container">\(P(\theta|x)\)</span>。只有在贝叶斯框架里，把似然与先验（Prior） <span displaypfx="inline-" class="mathjax-container">\(p(\theta)\)</span> 相乘并做归一化之后，才得到参数的后验概率（Posterior）：</p>
<span displaypfx="" class="mathjax-container">\[p(\theta|x)=\frac{p(x|\theta)p(\theta)}{p(x)}\]</span>
<p>所以可以把三者关系记成一条清晰的链： <span displaypfx="inline-" class="mathjax-container">\(p(x|\theta)\)</span> 作为“关于 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的函数”时是概率模型；作为“关于 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的函数”时是似然函数；再结合先验并归一化后，才变成参数的后验概率。很多机器学习教材里说“最小化交叉熵等价于最大化对数似然”，本质上就是固定数据后，在参数空间里寻找最能解释样本的模型。</p>
<div class="blog_h3"><span class="graybg">机器学习视角：概率、似然与损失</span></div>
<p>在机器学习模型里，这种“同一个式子，换个视角名字就变”的现象非常常见。设模型写成 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y|x)\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是输入或上下文， <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是真实标签或真实 token， <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 是模型参数。</p>
<p>当参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 固定、把输出 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 当作随机变量时， <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y|x)\)</span> 是概率模型：它回答“在这个模型已经定好的前提下，不同输出有多可能”。例如语言模型会对整个词表输出一个条件概率分布，其中 <span displaypfx="inline-" class="mathjax-container">\(c_t\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置之前的上下文（context）， <span displaypfx="inline-" class="mathjax-container">\(y_t\)</span> 表示该位置真实出现的 token，因此 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y_t|c_t)\)</span> 就是“在上下文 <span displaypfx="inline-" class="mathjax-container">\(c_t\)</span> 下，模型给真实 token <span displaypfx="inline-" class="mathjax-container">\(y_t\)</span> 分配的概率”。</p>
<p>当观测到的 <span displaypfx="inline-" class="mathjax-container">\((x,y)\)</span> 固定、把参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 当作变量时，同一个式子 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y|x)\)</span> 就变成似然：它回答“哪些参数更能让这条已经看到的数据显得合理”。于是训练时最大化似然，就等价于最小化负对数似然：</p>
<span displaypfx="" class="mathjax-container">\[-\log p_\theta(y|x)\]</span>
<p>因此可以把这个困惑直接拆开：<span style="background-color: #c0c0c0;">固定参数看输出，它是概率；固定输出看参数，它是似然；对它取负对数后，它就是训练里使用的损失项</span>。在语言建模里，对一个具体 token 而言，给定当前模型参数后， <span displaypfx="inline-" class="mathjax-container">\(-\log p_\theta(y_t|c_t)\)</span> 既可以看成该 token 的负对数概率，也可以在训练语境下看成该 token 对参数的负对数似然；两者是同一个数值对象，只是观察角度不同。</p>
<div class="blog_h2"><span class="graybg">基本概率</span></div>
<p>概率（Probability）描述不确定事件发生的可能性。设样本空间（Sample Space）为 <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span>，事件（Event）为 <span displaypfx="inline-" class="mathjax-container">\(A\subseteq\Omega\)</span>，则概率公理要求 <span displaypfx="inline-" class="mathjax-container">\(P(A)\in[0,1]\)</span>、<span displaypfx="inline-" class="mathjax-container">\(P(\Omega)=1\)</span>，以及互斥事件可加。这里“互斥事件可加”的意思是：如果两个事件不能同时发生，即 <span displaypfx="inline-" class="mathjax-container">\(A\cap B=\{\}\)</span>，那么“<span displaypfx="inline-" class="mathjax-container">\(A\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 发生”的概率就是两者概率直接相加：</p>
<span displaypfx="" class="mathjax-container">\[P(A\cup B)=P(A)+P(B),\quad A\cap B=\{\}\]</span>
<p>之所以能直接相加，是因为两者没有重叠部分，不会重复计数。若有重叠，则不能直接相加，而要减去交集 <span displaypfx="inline-" class="mathjax-container">\(P(A\cap B)\)</span>。</p>
<p>最小例子：掷一枚公平六面骰。样本空间是 <span displaypfx="inline-" class="mathjax-container">\(\Omega=\{1,2,3,4,5,6\}\)</span>。若事件 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 表示“点数为 1”，事件 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 表示“点数为 2”，则它们互斥，因此 <span displaypfx="inline-" class="mathjax-container">\(P(A\cup B)=1/6+1/6=1/3\)</span>。再看一个非互斥例子：若事件 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 表示“点数为偶数”，则 <span displaypfx="inline-" class="mathjax-container">\(C=\{2,4,6\}\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(P(C)=3/6=1/2\)</span>。</p>
<div class="blog_h3"><span class="graybg">联合概率</span></div>
<p>联合概率（Joint Probability）表示多个事件同时发生的概率，即 <span displaypfx="inline-" class="mathjax-container">\(P(A,B)=P(A\cap B)\)</span>。它回答的是“这些条件一起成立的可能性有多大”。</p>
<span displaypfx="" class="mathjax-container">\[P(A,B)=P(A\cap B)\]</span>
<p>仍用骰子例子：令 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 表示“点数为偶数”，<span displaypfx="inline-" class="mathjax-container">\(B\)</span> 表示“点数至少为 4”，则 <span displaypfx="inline-" class="mathjax-container">\(A\cap B=\{4,6\}\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(P(A,B)=2/6=1/3\)</span>。在机器学习里，联合分布 <span displaypfx="inline-" class="mathjax-container">\(p(x,y)\)</span> 表示“输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 一起出现”的总体规律。</p>
<div class="blog_h3"><span class="graybg">独立性与乘法法则</span></div>
<p>独立（Independence）表示一个事件是否发生，不会改变另一个事件发生的概率。若事件 <span displaypfx="inline-" class="mathjax-container">\(A,B\)</span> 相互独立，则它们同时发生的概率可直接写成概率乘积：</p>
<span displaypfx="" class="mathjax-container">\[P(A\cap B)=P(A)P(B)\]</span>
<p>更一般地，若 <span displaypfx="inline-" class="mathjax-container">\(A_1,\dots,A_n\)</span> 相互独立，则</p>
<span displaypfx="" class="mathjax-container">\[P\!\left(\bigcap_{i=1}^{n}A_i\right)=\prod_{i=1}^{n}P(A_i)\]</span>
<p>例：掷一枚公平硬币并同时掷一枚公平骰子。令 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 表示“硬币为正面”，<span displaypfx="inline-" class="mathjax-container">\(B\)</span> 表示“骰子为偶数”，则 <span displaypfx="inline-" class="mathjax-container">\(P(A)=1/2\)</span>、<span displaypfx="inline-" class="mathjax-container">\(P(B)=1/2\)</span>，且二者独立，因此 <span displaypfx="inline-" class="mathjax-container">\(P(A\cap B)=1/2\times 1/2=1/4\)</span>。</p>
<p>独立时还可等价写成 <span displaypfx="inline-" class="mathjax-container">\(P(A|B)=P(A)\)</span>：知道 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 发生，并不会改变对 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的判断。</p>
<p>要注意：互斥（Mutually Exclusive）和独立（Independent）不是一回事。互斥表示不能同时发生；独立表示是否发生彼此无关。对非零概率事件而言，互斥通常意味着<span style="background-color: #c0c0c0;">不独立</span>，因为一旦知道 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 发生，就立刻知道 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 不可能发生。</p>
<div class="blog_h3"><span class="graybg">条件概率</span></div>
<p>条件概率（Conditional Probability）表示“已知 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 发生时，<span displaypfx="inline-" class="mathjax-container">\(A\)</span> 发生的概率”：</p>
<span displaypfx="" class="mathjax-container">\[P(A|B)=\frac{P(A,B)}{P(B)},\quad P(B)&gt;0\]</span>
<p>关键点不是“再算一次概率”，而是<span style="background-color: #c0c0c0;">样本空间被缩小了</span>。在上面的骰子例子中，已知 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>（点数至少为 4）后，只剩 <span displaypfx="inline-" class="mathjax-container">\(\{4,5,6\}\)</span> 三种可能，其中偶数是 <span displaypfx="inline-" class="mathjax-container">\(\{4,6\}\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(P(A|B)=2/3\)</span>，明显不同于原来的 <span displaypfx="inline-" class="mathjax-container">\(P(A)=1/2\)</span>。</p>
<p>在建模中，几乎所有监督学习目标都可写成条件概率最大化，例如分类模型学习的是 <span displaypfx="inline-" class="mathjax-container">\(P(y|x)\)</span>：给定特征 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 的概率有多大。</p>
<div class="blog_h3"><span class="graybg">边缘概率</span></div>
<p>边缘概率（Marginal Probability）是把不关心的变量“求和/积分掉”后得到的概率：</p>
<span displaypfx="" class="mathjax-container">\[P(A)=\sum_b P(A,b),\quad p(x)=\int p(x,z)\,dz\]</span>
<p>这两个公式的意思完全一致，只是分别对应离散情形与连续情形。 <span displaypfx="inline-" class="mathjax-container">\(P(A,b)\)</span> 表示“<span displaypfx="inline-" class="mathjax-container">\(A\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 同时发生”的联合概率；若现在只关心 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，就必须把所有可能的 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 都加起来。连续情形下同理： <span displaypfx="inline-" class="mathjax-container">\(p(x,z)\)</span> 是关于 <span displaypfx="inline-" class="mathjax-container">\((x,z)\)</span> 的联合密度，若只关心 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，就把所有 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 的可能性沿该维度积分掉，得到 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>。</p>
<p>几何上，可以把边缘化（Marginalization）理解为<span style="background-color: #c0c0c0;">把高维分布沿某个方向压扁后得到的投影</span>。设二维联合分布的两个维度分别是身高与体重，平面上的每个位置都对应一个联合概率密度 <span displaypfx="inline-" class="mathjax-container">\(p(\text{height},\text{weight})\)</span>。如果现在根本不关心体重，只想知道身高的总体分布，那么就把每个固定身高处、沿着体重方向的所有概率质量全部累加起来；累加后的结果就是该身高对应的边缘密度 <span displaypfx="inline-" class="mathjax-container">\(p(\text{height})\)</span>。</p>
<p>因此，求和符号 <span displaypfx="inline-" class="mathjax-container">\(\sum_b P(A,b)\)</span> 或积分符号 <span displaypfx="inline-" class="mathjax-container">\(\int p(x,z)\,dz\)</span> 的几何含义，不是“神秘地消掉一个变量”，而是<span style="background-color: #c0c0c0;">把那个维度上的所有可能性叠加到剩余维度上</span>。它像从侧面看一个三维物体的投影：原来的结构仍然在，但你只保留了当前关心的坐标轴信息。</p>
<p>在 AI 中，边缘概率几乎总伴随着隐藏变量（Latent Variable）。例如主题模型里 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 可以表示隐藏主题（Hidden Topic），<span displaypfx="inline-" class="mathjax-container">\(x\)</span> 表示某个词；若只关心词出现的总体概率，就要把主题变量消去，得到 <span displaypfx="inline-" class="mathjax-container">\(p(x)=\sum_z p(x,z)\)</span>。很多推断算法的难点，本质上就是这个“沿隐藏维度求和/积分”的步骤代价很高。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/marginzation.jpg"><img class="alignnone size-full wp-image-40833" src="https://blog.gmem.cc/wp-content/uploads/2026/03/marginzation.jpg" alt="marginzation" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">补事件</span></div>
<p>补事件（Complementary Event）满足</p>
<span displaypfx="" class="mathjax-container">\[P(A^c)=1-P(A)\]</span>
<p>它表示“<span displaypfx="inline-" class="mathjax-container">\(A\)</span> 不发生”的概率等于总概率 1 减去 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 发生的概率。</p>
<div class="blog_h3"><span class="graybg">全概率公式</span></div>
<p>第二，若 <span displaypfx="inline-" class="mathjax-container">\(A_1,\dots,A_n\)</span> 把样本空间划分为互斥且完备的几部分（即两两互斥，且并集为 <span displaypfx="inline-" class="mathjax-container">\(\Omega\)</span>），则全概率公式（Law of Total Probability）为</p>
<span displaypfx="" class="mathjax-container">\[P(B)=\sum_{i=1}^{n}P(B|A_i)P(A_i)\]</span>
<p>它的含义是：事件 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 的总概率，可以拆成“先落入哪一种原因 <span displaypfx="inline-" class="mathjax-container">\(A_i\)</span>，再在该原因下发生 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>”的加权和。贝叶斯公式中的分母 <span displaypfx="inline-" class="mathjax-container">\(P(B)\)</span>，很多时候就是用全概率公式展开出来的。</p>
<div class="blog_h2"><span class="graybg">贝叶斯定理</span></div>
<p>贝叶斯定理（Bayes' Theorem）把“从原因到结果”的概率反转为“从结果反推原因”：</p>
<span displaypfx="" class="mathjax-container">\[P(A|B)=\frac{P(B|A)\,P(A)}{P(B)}\]</span>
<p>把式子写出来并不难，真正容易混淆的是每一项到底在说什么。下面直接用一个具体场景来解释：设 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 表示“患者真的患病”，<span displaypfx="inline-" class="mathjax-container">\(B\)</span> 表示“检测结果为阳性”。已知患病率为 1%，检测的灵敏度（Sensitivity）为 99%，假阳性率（False Positive Rate）为 5%。则：</p>
<ul>
<li>先验（Prior） <span displaypfx="inline-" class="mathjax-container">\(P(A)\)</span>：在还没看到这次检测结果前，对“此人患病”的原始判断。在这个例子里，先验就是总体患病率 <span displaypfx="inline-" class="mathjax-container">\(P(A)=0.01\)</span>。</li>
<li>似然（Likelihood） <span displaypfx="inline-" class="mathjax-container">\(P(B|A)\)</span>：如果一个人确实患病，那么检测为阳性的概率有多大。在这个例子里，检测灵敏度是 99%，因此 <span displaypfx="inline-" class="mathjax-container">\(P(B|A)=0.99\)</span>。</li>
<li>证据（Evidence） <span displaypfx="inline-" class="mathjax-container">\(P(B)\)</span>：不管一个人是否患病，检测结果为阳性在总体上出现的概率。它等于“真阳性 + 假阳性”的总和： <span displaypfx="inline-" class="mathjax-container">\(P(B)=0.99\times 0.01 + 0.05\times 0.99 = 0.0594\)</span>。</li>
<li>后验（Posterior） <span displaypfx="inline-" class="mathjax-container">\(P(A|B)\)</span>：在已经看到“检测阳性”这个证据之后，此人真正患病的更新后概率。代入上面的数值，有</li>
</ul>
<span displaypfx="" class="mathjax-container">\[P(\text{disease}|+) = \frac{0.99\times 0.01}{0.99\times 0.01 + 0.05\times 0.99} = \frac{1}{6} \approx 16.7\%\]</span>
<p>因此，贝叶斯定理说的不是“把公式套进去算一下”，而是一个非常具体的更新过程：<span style="background-color: #c0c0c0;">先从原始信念出发，用证据对它重新加权，再归一化，得到更新后的信念</span>。这也是为什么常把它概括成：<span style="background-color: #c0c0c0;">后验 = 似然 × 先验 ÷ 证据</span>。</p>
<p>这个例子最重要的结论是：检测阳性后，真实患病概率并不是 99%，而只有约 16.7%。原因不是检测太差，而是先验患病率本来就很低；假阳性虽然比例不高，但基数更大。换言之，<span style="background-color: #c0c0c0;">证据不会凭空决定结论，证据必须结合基线发生率一起解释</span>。在 AI 里，这就是“看到证据后更新信念”的统一公式：朴素贝叶斯分类、贝叶斯滤波、概率图模型都在做这件事。</p>
<div class="blog_h2"><span class="graybg">概率分布</span></div>
<p>这里的“分布（Distribution）”指随机变量取值的不确定性如何在取值空间上分配。严格地说，概率分布（Probability Distribution）是一种概率测度（Probability Measure），它为每个事件 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 赋予概率 <span displaypfx="inline-" class="mathjax-container">\(P(A)\)</span>。</p>
<p>对一维实值随机变量 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>，分布最常用的统一表述是累积分布函数（Cumulative Distribution Function, CDF）<span displaypfx="inline-" class="mathjax-container">\(F(x)=P(X\le x)\)</span>。离散与连续的差别，主要体现在如何从 <span displaypfx="inline-" class="mathjax-container">\(F\)</span> 得到“点上/区间上”的概率。</p>
<p>不同任务对应不同的数据分布假设（Distribution Assumption）。分布选得对，建模与推断会更稳定；分布假设错得太远，参数估计、置信区间乃至损失函数解释都会失真。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot_six_basic_dist.png"><img class="alignnone size-full wp-image-40921" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot_six_basic_dist.png" alt="plot_six_basic_dist" width="1848" height="1473" /></a></p>
<div class="blog_h3"><span class="graybg">概率密度/概率质量函数</span></div>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 是离散型随机变量，其概率质量函数（Probability Mass Function, PMF）为 <span displaypfx="inline-" class="mathjax-container">\(p(x)=P(X=x)\)</span>，并满足 <span displaypfx="inline-" class="mathjax-container">\(\sum_x p(x)=1\)</span>。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 是连续型随机变量，其概率密度函数（Probability Density Function, PDF）为 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>，满足 <span displaypfx="inline-" class="mathjax-container">\(p(x)\ge 0\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\int_{-\infty}^{+\infty} p(x)\,dx=1\)</span>；区间概率由积分给出：<span displaypfx="inline-" class="mathjax-container">\(P(a\le X\le b)=\int_a^b p(x)\,dx\)</span>。因此 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span> 本身不是“点上概率”。</p>
<p>两者都可写成同一 CDF 关系：连续情形 <span displaypfx="inline-" class="mathjax-container">\(F(x)=\int_{-\infty}^{x} p(t)\,dt\)</span>，离散情形 <span displaypfx="inline-" class="mathjax-container">\(F(x)=\sum_{t\le x} p(t)\)</span>。</p>
<div class="blog_h3"><span class="graybg">高斯分布（正态分布）</span></div>
<p>高斯分布（Gaussian / Normal Distribution）由均值 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 与方差 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 决定：</p>
<span displaypfx="" class="mathjax-container">\[p(x)=\frac{1}{\sqrt{2\pi}\sigma}\exp\!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)\]</span>
<p>它的图像是熟悉的“钟形曲线（Bell Curve）”：离 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 越近，概率密度越高；离得越远，概率密度衰减越快。大量独立小扰动叠加后常近似高斯（中心极限定理的结果），所以测量误差、回归残差、传感器噪声、嵌入向量某些方向上的统计近似都常采用高斯模型。</p>
<p>例：若成年男性身高近似服从 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}(170,6^2)\)</span>，则 170 cm 附近最常见，而 190 cm 属于离均值超过 3 个标准差的少见样本。回归里假设残差服从高斯，本质上是在说“误差多数小、极大误差少”。</p>
<div class="blog_h3"><span class="graybg">拉普拉斯分布</span></div>
<p>拉普拉斯分布（Laplace Distribution）也是定义在实数轴上的连续分布，常用位置参数（Location） <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 与尺度参数（Scale） <span displaypfx="inline-" class="mathjax-container">\(b&gt;0\)</span> 描述：</p>
<span displaypfx="" class="mathjax-container">\[p(x)=\frac{1}{2b}\exp\left(-\frac{|x-\mu|}{b}\right)\]</span>
<p>它的图像在中心点 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 处更尖、尾部比高斯分布更厚（Heavier Tails）。这表示模型更偏好“大多数误差很小，但偶尔出现相对较大偏差”这一类数据形状，因此它对离群点（Outliers）的容忍度通常高于高斯模型。</p>
<p>拉普拉斯分布和绝对误差（Absolute Error）关系非常紧密：若回归残差假设服从拉普拉斯分布，那么最大似然估计会导向 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 损失；若把参数先验设为拉普拉斯分布，最大后验估计则会导向 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 正则。这也是它在稀疏建模（Sparse Modeling）和鲁棒回归（Robust Regression）里很常见的原因。</p>
<p>例：若某传感器大多数时刻误差接近 0，但偶尔会因为抖动或遮挡产生较大的偏差，那么用拉普拉斯分布描述误差，往往比高斯分布更贴近这种“中心尖、尾部较厚”的统计形状。</p>
<div class="blog_h3"><span class="graybg">伯努利分布</span></div>
<p>伯努利分布（Bernoulli Distribution）描述一次二元结果试验（0/1）：</p>
<span displaypfx="" class="mathjax-container">\[P(X=1)=p,\quad P(X=0)=1-p\]</span>
<p>它是二分类标签建模的最小单元，也是逻辑回归与二分类交叉熵的概率基础。若 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 表示“用户是否点击广告”，则 <span displaypfx="inline-" class="mathjax-container">\(X=1\)</span> 代表点击、<span displaypfx="inline-" class="mathjax-container">\(X=0\)</span> 代表未点击，模型输出的 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 就是点击概率。</p>
<p>伯努利变量的期望是 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]=p\)</span>，方差是 <span displaypfx="inline-" class="mathjax-container">\(p(1-p)\)</span>。因此当 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 接近 0 或 1 时，不确定性反而更小；当 <span displaypfx="inline-" class="mathjax-container">\(p=0.5\)</span> 时，不确定性最大。</p>
<p>如果把同一个伯努利试验独立重复 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次，并把成功次数加总，那么得到的就不是伯努利分布，而是二项分布（Binomial Distribution）。换句话说，伯努利分布描述“单次是否成功”，二项分布描述“总共成功了多少次”。</p>
<div class="blog_h3"><span class="graybg">二项分布</span></div>
<p>二项分布（Binomial Distribution）描述 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次独立伯努利试验中的成功次数。若每次成功概率都是 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，随机变量 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 表示总成功次数，则</p>
<span displaypfx="" class="mathjax-container">\[P(X=k)={n \choose k}p^k(1-p)^{n-k},\quad k=0,1,\dots,n\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\({n \choose k}\)</span> 表示“从 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次试验里选出 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 次成功”的组合数；后面的 <span displaypfx="inline-" class="mathjax-container">\(p^k(1-p)^{n-k}\)</span> 表示某一种具体排列出现的概率。两者相乘，就得到“恰好成功 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 次”的总概率。</p>
<p>如果 <span displaypfx="inline-" class="mathjax-container">\(X_1,\dots,X_n\)</span> 独立同分布且 <span displaypfx="inline-" class="mathjax-container">\(X_i\sim\mathrm{Bernoulli}(p)\)</span>，那么它们的和</p>
<span displaypfx="" class="mathjax-container">\[S_n=\sum_{i=1}^{n}X_i\sim\mathrm{Binomial}(n,p)\]</span>
<p>因此，二项分布本质上就是“多个伯努利随机变量求和后的分布”。它的期望是 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]=np\)</span>，方差是 <span displaypfx="inline-" class="mathjax-container">\(np(1-p)\)</span>。</p>
<p>例：若一枚硬币正面概率为 <span displaypfx="inline-" class="mathjax-container">\(p=0.7\)</span>，连续抛 10 次，则“正面出现几次”服从二项分布。此时恰好出现 7 次正面的概率是</p>
<span displaypfx="" class="mathjax-container">\[P(X=7)={10 \choose 7}0.7^7 0.3^3\]</span>
<div class="blog_h3"><span class="graybg">多项式分布</span></div>
<p>多项式分布（Multinomial Distribution）是“多类别计数版”的伯努利：进行 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次独立试验，类别概率为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{p}\)</span>，计数向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{k}\)</span> 的概率为</p>
<span displaypfx="" class="mathjax-container">\[P(\mathbf{k})=\frac{n!}{\prod_i k_i!}\prod_i p_i^{k_i},\quad \sum_i k_i=n\]</span>
<p>若只有一次抽样，通常写作分类分布（Categorical Distribution）：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/distribution-categorical.png"><img class="alignnone size-full wp-image-40867" src="https://blog.gmem.cc/wp-content/uploads/2026/03/distribution-categorical.png" alt="distribution-categorical" width="100%" /></a></p>
<p>若把一次抽样重复 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 次并统计每类出现次数，就得到多项式分布，语言模型里的词频计数、主题模型中的词袋（Bag of Words）都与这种计数视角一致。概率分布一节的第三个图就是多项式分布：总计抽样6次，横轴表示分类1的次数，纵轴表示分类2的次数（因为只有3分类，因此不需要绘制三维），颜色表示抽取到不同分类的次数组合的概率。</p>
<div class="blog_h3"><span class="graybg">泊松分布</span></div>
<p>泊松分布（Poisson Distribution）描述单位时间或单位空间内稀有事件的发生次数：</p>
<span displaypfx="" class="mathjax-container">\[P(X=k)=e^{-\lambda}\frac{\lambda^k}{k!},\quad k=0,1,2,\ldots\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 既是平均发生次数，也是方差。它适用于“事件相互独立、平均速率稳定、短时间内多次同时发生概率很小”的场景，例如单位分钟请求数、单位小时故障数、单位页面点击数。</p>
<p>例：若一个接口平均每分钟收到 3 次请求，则 <span displaypfx="inline-" class="mathjax-container">\(\lambda=3\)</span>。此时一分钟内 0 次请求的概率是 <span displaypfx="inline-" class="mathjax-container">\(e^{-3}\approx 0.05\)</span>；5 次请求的概率是 <span displaypfx="inline-" class="mathjax-container">\(e^{-3}3^5/5!\approx 0.10\)</span>。泊松分布常被用来做“到达数 / 故障数 / 事件数”建模。</p>
<div class="blog_h2"><span class="graybg">期望</span></div>
<p>期望（Expectation）是随机变量的“按概率加权的平均”，回答“长期来看这个量的典型水平在哪里”。离散情形下，它把每个可能取值按对应概率加权；连续情形下，则对概率密度做积分：</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[X]=\sum_x x\,p(x)\ \ (\text{或 } \int x\,p(x)\,dx)\]</span>
<p><span style="background-color: #c0c0c0;">期望和均值很像，但不是同一个概念。</span> 期望（Expectation）是总体分布层面的量，由概率模型 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span> 决定，描述“如果无限次重复抽样，平均会稳定到哪里”；均值（Mean）通常有两种语境：一是把总体的中心位置也叫“总体均值”，这时它与期望是同一个量；二是指有限样本算出来的样本均值（Sample Mean） <span displaypfx="inline-" class="mathjax-container">\(\bar{x}=\frac{1}{n}\sum_{i=1}^{n}x_i\)</span>，这是用样本近似期望的统计量。</p>
<p>因此可以把三者关系记成：<span style="background-color: #c0c0c0;">总体均值 = 期望；样本均值 = 对期望的估计</span>。例如公平骰子点数的期望是 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]=3.5\)</span>；如果你真的掷 6 次，样本均值可能是 3、4 或 4.5，不必恰好等于 3.5，但随着试验次数增多，它会越来越接近期望。</p>
<p>期望最重要的性质之一是线性性（Linearity）：<span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[aX+b]=a\mathbb{E}[X]+b\)</span>，不要求独立。例：公平骰子点数 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 的期望是 <span displaypfx="inline-" class="mathjax-container">\((1+2+3+4+5+6)/6=3.5\)</span>；若收入模型写成 <span displaypfx="inline-" class="mathjax-container">\(Y=2X+1\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[Y]=2\times 3.5+1=8\)</span>。</p>
<div class="blog_h2"><span class="graybg">矩（Moments）</span></div>
<p>矩（Moment）是概率统计里用来概括分布形状的一组数。理论上，“第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 阶”指对 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 次幂取期望：矩天然与期望/均值绑定。</p>
<p>常用两类定义是：</p>
<ul>
<li>原点矩（Raw Moment / Non-central Moment）：<span displaypfx="inline-" class="mathjax-container">\(m_k=\mathbb{E}[X^k]\)</span>（以 0 为参照）。</li>
<li>中心矩（Central Moment）：<span displaypfx="inline-" class="mathjax-container">\(\mu_k=\mathbb{E}\big[(X-\mathbb{E}[X])^k\big]\)</span>（以均值为参照）。</li>
</ul>
<p>直觉上可以用“跷跷板”来理解：把概率质量看作分布在数轴上的“重量”，期望/均值决定“支点放哪里”才平衡；更高阶矩描述“重量围绕支点如何分布”。</p>
<p>一阶矩（First Moment）回答“中心在哪里”：均值/期望 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]\)</span> 给出平衡点。但<span style="background-color: #c0c0c0;">一阶矩无法刻画离散程度</span>：例如 <span displaypfx="inline-" class="mathjax-container">\(X\in\{-1,+1\}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(X\in\{-10,+10\}\)</span> 都满足 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]=0\)</span>，但后者离中心更远。</p>
<p>二阶量用平方偏差的期望刻画离散程度：平方保证非负、避免正负偏差相互抵消，并对远离均值的样本赋予更大权重。在统计里，这对应二阶中心矩——方差 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(X)=\mathbb{E}[(X-\mathbb{E}[X])^2]\)</span>；在力学类比里，这与转动惯量（Moment of Inertia）中按 <span displaypfx="inline-" class="mathjax-container">\(r^2\)</span> 加权的直觉一致：远处的“重量”对系统的“难摆动/难转动”贡献更大。</p>
<p>二阶原点矩与方差之间满足恒等式：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}(X)=\mathbb{E}\big[(X-\mathbb{E}[X])^2\big]=\mathbb{E}[X^2]-\mathbb{E}[X]^2\]</span>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[X^2]=\mathrm{Var}(X)+\mathbb{E}[X]^2\]</span>
<p>它说明二阶原点矩 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X^2]\)</span> 以 0 为参照，会把两类效应叠加在一起：一是分布整体离 0 的偏移（由均值项 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]^2\)</span> 表示），二是围绕自身均值的离散/抖动（由方差项 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(X)\)</span> 表示）。因此 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X^2]\)</span> 变大并不等价于“波动更大”；把所有取值整体平移一个常数，方差不变，但二阶原点矩会随偏移显著改变。</p>
<p>一个最小例子可以把差异看得非常清楚。令 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 等概率取值 <span displaypfx="inline-" class="mathjax-container">\(\{99,100,101\}\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[X]=\frac{99+100+101}{3}=100,\quad \mathbb{E}[X]^2=100^2=10000\]</span>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[X^2]=\frac{99^2+100^2+101^2}{3}=\frac{30002}{3}\approx 10000.67\]</span>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}(X)=\mathbb{E}[X^2]-\mathbb{E}[X]^2=\frac{30002}{3}-10000=\frac{2}{3}\]</span>
<p>可以直接读成一句话：这组数“离 0 很远”（所以 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X^2]\)</span> 很大），但“围绕自身均值很稳定”（所以方差很小）。</p>
<p>这也解释了 Adam 里的常见误解。Adam 说的“一阶矩/二阶矩”是把 mini-batch 梯度 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 当作随机变量，分别估计 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g]\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]\)</span>（逐参数维度）。它用的是二阶原点矩 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]\)</span>，不是方差 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(g)\)</span>。原因很直接：如果梯度在一段时间内几乎恒定（例如每步都是 <span displaypfx="inline-" class="mathjax-container">\(g=10\)</span>），那么方差为 0，拿它做除法会造成数值灾难；但 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]=100\)</span> 给出了稳定的“绝对尺度”，使更新项 <span displaypfx="inline-" class="mathjax-container">\(\frac{\mathbb{E}[g]}{\sqrt{\mathbb{E}[g^2]}}\)</span> 仍然是有界的（再加上 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 做数值稳定）。</p>
<div class="blog_h2"><span class="graybg">方差</span></div>
<p>方差（Variance）衡量随机变量围绕均值的波动大小。它不是“偏离均值后再平均”，而是“偏离均值的平方再平均”；平方的作用是避免正负抵消，并放大大偏差：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}(X)=\mathbb{E}[(X-\mu)^2]\]</span>
<p>因此，方差小表示样本更集中在均值附近；方差大表示波动更强。它刻画的是“不确定性的尺度”，而不是取值本身的大小。例如两个模型的平均误差相同，方差更大的那个模型，输出往往更不稳定。</p>
<div class="blog_h2"><span class="graybg">协方差</span></div>
<p>协方差（Covariance）描述两个变量是否倾向同向变化：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Cov}(X,Y)=\mathbb{E}[(X-\mu_X)(Y-\mu_Y)]\]</span>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Cov}(X,Y)&gt;0\)</span>，说明 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 大时 <span displaypfx="inline-" class="mathjax-container">\(Y\)</span> 也倾向变大；若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Cov}(X,Y)&lt;0\)</span>，说明一个变大时另一个倾向变小。直觉例子是：身高与体重常为正协方差，室外温度与暖气功率常为负协方差。</p>
<p>还要区分一点：协方差为 0 不必然意味着独立。例如令 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(\{-1,1\}\)</span> 上等概率取值，定义 <span displaypfx="inline-" class="mathjax-container">\(Y=X^2\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(Y\)</span> 被 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 完全决定，但 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Cov}(X,Y)=0\)</span>。因此“零相关”比“独立”弱得多。</p>
<p>协方差矩阵（Covariance Matrix）<span displaypfx="inline-" class="mathjax-container">\(\Sigma\)</span> 在 AI 中极其关键：PCA、马氏距离（Mahalanobis Distance）、卡尔曼滤波与高斯模型都直接依赖它。它编码的是“各方向上的尺度”与“不同维度之间的耦合”。</p>
<div class="blog_h2"><span class="graybg">标准差（Standard Deviation）</span></div>
<p>标准差（Standard Deviation）衡量数据相对均值（Mean）的离散程度（Dispersion）。总体标准差（Population Standard Deviation）定义为：</p>
<span displaypfx="" class="mathjax-container">\[\sigma=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(x_i-\mu)^2}\]</span>
<p>样本标准差（Sample Standard Deviation）使用贝塞尔校正（Bessel's Correction）：</p>
<span displaypfx="" class="mathjax-container">\[s=\sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2}\]</span>
<p>先平方再平均是为了避免正负抵消并加重大偏差；最后开平方是为了把单位恢复到原始尺度。若直接用方差，单位会变成“平方单位”，解释往往不直观；标准差则可以直接说成“典型偏离均值大约多少个原始单位”。</p>
<p>例：数据 <span displaypfx="inline-" class="mathjax-container">\(\{2,4,4,4,5,5,7,9\}\)</span> 的均值是 <span displaypfx="inline-" class="mathjax-container">\(5\)</span>，总体方差是 <span displaypfx="inline-" class="mathjax-container">\(4\)</span>，标准差是 <span displaypfx="inline-" class="mathjax-container">\(2\)</span>。这意味着一个典型样本与均值的偏离量级大约是 2，而不是每个点都恰好偏 2。</p>
<p>标准差还常被用来做标准化（Standardization）。例如某考试分数 80 分，班级均值 70、标准差 5，则它的 z-score 是 <span displaypfx="inline-" class="mathjax-container">\((80-70)/5=2\)</span>，表示它比均值高 2 个标准差。近似正态分布下，经验上约有 68% 样本落在 <span displaypfx="inline-" class="mathjax-container">\(\mu\pm\sigma\)</span>，95% 落在 <span displaypfx="inline-" class="mathjax-container">\(\mu\pm2\sigma\)</span>，99.7% 落在 <span displaypfx="inline-" class="mathjax-container">\(\mu\pm3\sigma\)</span>；这就是常见的 68-95-99.7 规则。</p>
<div class="blog_h2"><span class="graybg">最大似然估计（MLE）</span></div>
<p>最大似然估计（Maximum Likelihood Estimation, MLE）先把已经观察到的数据（例如上面抛硬币10次有8次正面）固定住，再在参数空间里寻找“最能生成这批数据”的参数。设数据集为 <span displaypfx="inline-" class="mathjax-container">\(D=\{x_1,\dots,x_n\}\)</span>，参数为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，则定义是：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MLE}}=\arg\max_{\theta}p(D|\theta)\]</span>
<p>其中<span displaypfx="inline-" class="mathjax-container">\(p(D|\theta)\)</span> 是“参数取 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 时看到整批数据 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 的概率/密度”； <span displaypfx="inline-" class="mathjax-container">\(\arg\max\)</span> 表示“找出让这个值最大的那个参数”。</p>
<p>若样本独立同分布（不相关且在同一个分布，例如抛硬币中10个独立事件。Independent and Identically Distributed, i.i.d.），联合似然可以拆成单个样本似然的乘积：</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta)=\prod_{i=1}^{n}p(x_i|\theta)\]</span>
<p>于是 MLE 常写成</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MLE}}=\arg\max_{\theta}\prod_{i=1}^{n}p(x_i|\theta)\]</span>
<p>实际训练几乎总是改为最大化对数似然（Log-Likelihood）：</p>
<span displaypfx="" class="mathjax-container">\[\ell(\theta)=\log p(D|\theta)=\sum_{i=1}^{n}\log p(x_i|\theta)\]</span>
<p><span style="background-color: #c0c0c0;">取对数不会改变最优解</span>，因为 <span displaypfx="inline-" class="mathjax-container">\(\log\)</span> 是单调递增函数；它只是把难处理的连乘变成易处理的求和。因此，最大化似然与最小化负对数似然（Negative Log-Likelihood, NLL）完全等价。</p>
<p>继续看抛硬币的例子。设单次结果 <span displaypfx="inline-" class="mathjax-container">\(x_i\in\{0,1\}\)</span>，其中 1 表示正面，0 表示反面。设正面概率为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，那么单个样本就服从伯努利分布（Bernoulli Distribution）。它的概率质量函数（Probability Mass Function, PMF，用于描述离散随机变量，直接给出概率。对应概率密度函数PDF，给出给定连续随机变量对应位置的密度，因为连续，特定点的概率必须为0，只能密度和变量范围积分得到概率）写成：</p>
<span displaypfx="" class="mathjax-container">\[p(x_i|\theta)=\theta^{x_i}(1-\theta)^{1-x_i},\quad x_i\in\{0,1\}\]</span>
<p>左边的 <span displaypfx="inline-" class="mathjax-container">\(p(\cdot|\theta)\)</span> 里的 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是“概率分布/概率质量函数”的记号， <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 是模型参数，也就是“正面概率”。很多教材会把参数也记成 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，写成 <span displaypfx="inline-" class="mathjax-container">\(p(x_i|p)\)</span>；这并不算错，因为参数本身就是一个概率，但两个 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 同时出现时很容易视觉混淆，所以这里改用 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 来区分“模型符号”和“参数符号”。</p>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(x_i=1\)</span> 时，</p>
<span displaypfx="" class="mathjax-container">\[p(x_i=1|\theta)=\theta^1(1-\theta)^0=\theta\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(x_i=0\)</span> 时，</p>
<span displaypfx="" class="mathjax-container">\[p(x_i=0|\theta)=\theta^0(1-\theta)^1=1-\theta\]</span>
<p>如果 10 次里看到 7 次正面、3 次反面，那么整批数据的似然就是：</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta)=\theta^7(1-\theta)^3\]</span>
<p>对应的对数似然是</p>
<span displaypfx="" class="mathjax-container">\[\ell(\theta)=7\log \theta+3\log(1-\theta)\]</span>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-binomial-loglik.png"><img class="alignnone size-full wp-image-41091" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-binomial-loglik.png" alt="plot-binomial-loglik" width="100%" /></a></p>
<p>我们现在要最大化似然，需要对 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 求导并令导数为 0：</p>
<span displaypfx="" class="mathjax-container">\[\frac{d\ell(\theta)}{d\theta}=\frac{7}{\theta}-\frac{3}{1-\theta}=0\Rightarrow \theta=0.7\]</span>
<p>所以 <span displaypfx="inline-" class="mathjax-container">\(\hat{\theta}_{\mathrm{MLE}}=0.7\)</span>。即“在所有候选 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 里， <span displaypfx="inline-" class="mathjax-container">\(\theta=0.7\)</span> 最能让 7 正 3 反这份数据显得不奇怪”。</p>
<div class="blog_h3"><span class="graybg">从概率建模到损失函数</span></div>
<p>在机器学习里，损失函数往往由<span style="background-color: #c0c0c0;">概率建模假设（Probabilistic Modeling Assumption）</span>诱导出来。做法是先写下观测数据在参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 下的概率模型，再把训练集的负对数似然（Negative Log-Likelihood, NLL）当作优化目标。于是，最小化损失就不再只是一个工程规定，而是等价于最大化“已观测数据在模型下出现的可能性”。</p>
<p>以最常见的回归假设为例，若模型输出写成 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span>，并假设真实标签满足</p>
<span displaypfx="" class="mathjax-container">\[y=f_\theta(x)+\varepsilon,\qquad \varepsilon\sim\mathcal{N}(0,\sigma^2)\]</span>
<p>这里的高斯分布指的不是 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 本身，也不是 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 本身，而是<span style="background-color: #c0c0c0;">误差项，也就是残差 <span displaypfx="inline-" class="mathjax-container">\(y-f_\theta(x)\)</span> 的分布</span>。误差总是相对于模型当前给出的预测中心 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 来计算：预测值附近最可能出现，偏离越远，概率越低。</p>
<p>从这个误差模型出发，可以把“误差分布”直接改写成“真实标签在给定输入下的条件分布”。因为 <span displaypfx="inline-" class="mathjax-container">\(\varepsilon\)</span> 服从均值为 0 的高斯分布，所以等价地，真实标签服从一个均值为 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 的高斯分布：</p>
<span displaypfx="" class="mathjax-container">\[y\,|\,x \sim \mathcal{N}(f_\theta(x),\sigma^2)\]</span>
<p>这条式子的含义是：给定输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 之后，模型并不把输出 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 看成一个确定值，而是把它看成一个<span style="background-color: #c0c0c0;">以预测值 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 为中心、方差为 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 的高斯随机变量</span>。根据高斯分布的概率密度函数，可进一步写出条件概率密度：</p>
<span displaypfx="" class="mathjax-container">\[p_\theta(y|x)=\frac{1}{\sqrt{2\pi\sigma^2}}\exp\left(-\frac{(y-f_\theta(x))^2}{2\sigma^2}\right)\]</span>
<p>这时 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y_i|x_i)\)</span> 的含义就自然了：它评估的是在当前参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 下，训练集中已经观测到的真实标签 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span>，作为“围绕 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 摆动的一个样本”是否足够合理。被评分的始终是<span style="background-color: #c0c0c0;">真实观测到的 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span></span>，而不是模型预测出来的 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span>。<span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 的作用不是直接成为被评分对象，而是作为条件分布的均值或位置参数，决定观测值最可能出现的位置。</p>
<p>这种思路并不限于高斯回归。分类任务里，条件分布不再是高斯，而会改成 Bernoulli 或 Categorical；若把噪声假设换成拉普拉斯分布（Laplacian Distribution），则导出的损失也会从平方误差变成绝对误差。更严谨地说，<span style="background-color: #c0c0c0;">损失函数是概率模型在训练集上的负对数似然写开后的结果</span>：高斯噪声导出平方误差（Squared Error），拉普拉斯噪声导出绝对误差（Absolute Error），Bernoulli 与 Categorical 分布则导出二分类或多分类交叉熵（Cross-Entropy）。</p>
<div class="blog_h3"><span class="graybg">高斯分布的MLE和最小二乘</span></div>
<p>先看监督回归（Supervised Regression）。训练集写成 <span displaypfx="inline-" class="mathjax-container">\(D=\{(x_i,y_i)\}_{i=1}^N\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 是输入特征，<span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 是真实输出。按照上面的统一框架，这里仍然是把 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 当作给定条件，只为输出建模条件分布 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y|x)\)</span>，并让真实观测到的 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 在模型下尽可能不意外：</p>
<span displaypfx="" class="mathjax-container">\[\hat\theta_{\mathrm{MLE}}=\arg\max_{\theta}\prod_{i=1}^{N}p_\theta(y_i|x_i)\]</span>
<p>这里的参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 不在 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 里，而是出现在假设函数 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 里。例如线性回归（Linear Regression）可写成 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)=\mathbf{w}^\top x+b\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\theta=(\mathbf{w},b)\)</span>。回归里常说的“高斯分布”指的不是整张训练集的 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 服从高斯分布，也不是模型预测值 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\)</span> 本身服从高斯分布，而是指<span style="background-color: #c0c0c0;">误差项，也就是残差 <span displaypfx="inline-" class="mathjax-container">\(y_i-f_\theta(x_i)\)</span> 的分布</span>。误差总是相对于当前模型给出的中心值 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 来计算。</p>
<p>具体假设是：</p>
<span displaypfx="" class="mathjax-container">\[y_i=f_\theta(x_i)+\varepsilon_i,\quad \varepsilon_i\sim\mathcal{N}(0,\sigma^2)\ \text{i.i.d.}\]</span>
<p>等价写成条件分布：</p>
<span displaypfx="" class="mathjax-container">\[y_i\,|\,x_i \sim \mathcal{N}(f_\theta(x_i),\sigma^2)\]</span>
<p>上式的意思是：在给定输入 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 的条件下，输出 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 不再被视为确定值，而被建模为随机变量，并且服从均值为 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span>、方差为 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 的一维高斯分布（Gaussian Distribution），当然，这里的不确定性就是模型引入的。根据高斯分布的概率密度公式，并结合 i.i.d. 假设（联合似然为逐样本似然的乘积），整批数据的似然为</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta,\sigma^2)=\prod_{i=1}^{N}\frac{1}{\sqrt{2\pi\sigma^2}}\exp\left(-\frac{(y_i-f_\theta(x_i))^2}{2\sigma^2}\right)\]</span>
<p>取负对数似然（Negative Log-Likelihood, NLL）得到训练时真正被最小化的量：</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta,\sigma^2)=\frac{N}{2}\log(2\pi\sigma^2)+\frac{1}{2\sigma^2}\sum_{i=1}^{N}(y_i-f_\theta(x_i))^2\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 视为常数时，第一项与 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 无关，因此最小化 NLL 关于 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 等价于最小化平方误差和（Sum of Squared Errors, SSE）：</p>
<span displaypfx="" class="mathjax-container">\[\hat\theta_{\mathrm{MLE}}=\arg\min_\theta\sum_{i=1}^{N}(y_i-f_\theta(x_i))^2\]</span>
<p>把残差（Residual）记为 <span displaypfx="inline-" class="mathjax-container">\(r_i=y_i-f_\theta(x_i)\)</span>，则上式就是最小化 <span displaypfx="inline-" class="mathjax-container">\(\sum_i r_i^2\)</span>。因此，在“高斯噪声 + 固定方差”的回归假设下，<span style="background-color: #c0c0c0;">最大化似然（等价最大化对数似然）就是最小化残差平方和</span>。</p>
<p>这就是“最小二乘（Least Squares）”为什么会从概率建模里自然出现；均方误差（Mean Squared Error, MSE）只是把 SSE 再除以 <span displaypfx="inline-" class="mathjax-container">\(N\)</span>，不改变最优解。</p>
<p>如果 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 也未知，则 MLE 会同时估计 <span displaypfx="inline-" class="mathjax-container">\((\theta,\sigma^2)\)</span>。在固定 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 时，对 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 的 MLE 为</p>
<span displaypfx="" class="mathjax-container">\[\hat\sigma^2_{\mathrm{MLE}}=\frac{1}{N}\sum_{i=1}^{N}(y_i-f_\theta(x_i))^2\]</span>
<p>注意这里是 <span displaypfx="inline-" class="mathjax-container">\(1/N\)</span>（MLE），而不是无偏估计常用的 <span displaypfx="inline-" class="mathjax-container">\(1/(N-1)\)</span>。</p>
<p>顺带一提：把回归模型退化为“常数预测” <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x)\equiv \mu\)</span>，就回到“在高斯噪声下估计均值 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span>”，其 MLE 是样本均值。这也是很多教材先从 <span displaypfx="inline-" class="mathjax-container">\(x_i=\mu+\varepsilon_i\)</span> 入手的原因：那是回归的一个最小特例（此时 <span displaypfx="inline-" class="mathjax-container">\(\theta=\mu\)</span>）。</p>
<div class="blog_h3"><span class="graybg">Bernoulli / Categorical 分布和交叉熵</span></div>
<p>分类任务与回归不同：标签通常是离散变量，因此这里使用的不是概率密度函数（Probability Density Function, PDF），而是概率质量函数（Probability Mass Function, PMF）。做最大似然估计时，最常见的做法同样是把输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 视为给定条件，只为标签建模条件分布 <span displaypfx="inline-" class="mathjax-container">\(p_\theta(y|x)\)</span>，然后让已观测标签在模型下尽可能“显得合理”。</p>
<p>先看二分类（Binary Classification）。设标签 <span displaypfx="inline-" class="mathjax-container">\(y_i\in\{0,1\}\)</span>，模型输出正类概率 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(x_i)=p_\theta(y_i=1|x_i)\)</span>，则单个样本服从伯努利分布（Bernoulli Distribution）：</p>
<span displaypfx="" class="mathjax-container">\[p_\theta(y_i|x_i)=\pi_\theta(x_i)^{y_i}\bigl(1-\pi_\theta(x_i)\bigr)^{1-y_i}\]</span>
<p>在 i.i.d. 假设下，整批数据的似然为</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta)=\prod_{i=1}^{N}\pi_\theta(x_i)^{y_i}\bigl(1-\pi_\theta(x_i)\bigr)^{1-y_i}\]</span>
<p>取负对数似然，得到</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta)=-\sum_{i=1}^{N}\Big[y_i\log \pi_\theta(x_i)+(1-y_i)\log\bigl(1-\pi_\theta(x_i)\bigr)\Big]\]</span>
<p>这正是二分类交叉熵（Binary Cross-Entropy, BCE）。因此，在“标签服从伯努利分布”的假设下，<span style="background-color: #c0c0c0;">最大化似然等价于最小化二分类交叉熵</span>。若再把 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(x)\)</span> 写成 sigmoid 作用在 logit <span displaypfx="inline-" class="mathjax-container">\(z_\theta(x)\)</span> 上，即 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(x)=\sigma(z_\theta(x))\)</span>，就得到工程实现里最常见的 sigmoid + BCE 训练形式。</p>
<p>再看多分类（Multiclass Classification）。设类别总数为 <span displaypfx="inline-" class="mathjax-container">\(C\)</span>，并用 one-hot 向量 <span displaypfx="inline-" class="mathjax-container">\(y_i=(y_{i1},\dots,y_{iC})\)</span> 表示真实标签，其中只有真实类别对应那一项为 1，其余为 0。若模型输出类别概率 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\theta,1}(x_i),\dots,\pi_{\theta,C}(x_i)\)</span>，且满足 <span displaypfx="inline-" class="mathjax-container">\(\sum_{c=1}^{C}\pi_{\theta,c}(x_i)=1\)</span>，则单个样本服从类别分布（Categorical Distribution）：</p>
<span displaypfx="" class="mathjax-container">\[p_\theta(y_i|x_i)=\prod_{c=1}^{C}\pi_{\theta,c}(x_i)^{y_{ic}}\]</span>
<p>整批数据的似然为</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta)=\prod_{i=1}^{N}\prod_{c=1}^{C}\pi_{\theta,c}(x_i)^{y_{ic}}\]</span>
<p>取负对数似然，得到</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta)=-\sum_{i=1}^{N}\sum_{c=1}^{C}y_{ic}\log \pi_{\theta,c}(x_i)\]</span>
<p>这正是多分类交叉熵（Categorical Cross-Entropy）。若标签是标准 one-hot，那么每个样本只会保留真实类别那一项，损失就退化成 <span displaypfx="inline-" class="mathjax-container">\(-\log \pi_{\theta,c^*}(x_i)\)</span>；若标签本身是软标签（Soft Label），则公式不变，只是 <span displaypfx="inline-" class="mathjax-container">\(y_{ic}\)</span> 不再只有 0 和 1，而是一个分布。</p>
<div class="blog_h3"><span class="graybg">拉普拉斯分布的MLE和绝对误差</span></div>
<p>若回归任务不再假设误差服从高斯分布，而是假设误差项 <span displaypfx="inline-" class="mathjax-container">\(\varepsilon\)</span> 服从拉普拉斯分布（Laplacian Distribution），即</p>
<span displaypfx="" class="mathjax-container">\[y_i=f_\theta(x_i)+\varepsilon_i,\qquad \varepsilon_i\sim \mathrm{Laplace}(0,b)\ \text{i.i.d.}\]</span>
<p>那么等价地，真实标签在给定输入后的条件分布可写成</p>
<span displaypfx="" class="mathjax-container">\[y_i\,|\,x_i \sim \mathrm{Laplace}(f_\theta(x_i),b)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 是位置参数（Location Parameter），<span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是尺度参数（Scale Parameter）。拉普拉斯分布的一维概率密度函数为</p>
<span displaypfx="" class="mathjax-container">\[p_\theta(y_i|x_i)=\frac{1}{2b}\exp\left(-\frac{|y_i-f_\theta(x_i)|}{b}\right)\]</span>
<p>在 i.i.d. 假设下，整批数据的似然为</p>
<span displaypfx="" class="mathjax-container">\[p(D|\theta,b)=\prod_{i=1}^{N}\frac{1}{2b}\exp\left(-\frac{|y_i-f_\theta(x_i)|}{b}\right)\]</span>
<p>取负对数似然，得到</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta,b)=N\log(2b)+\frac{1}{b}\sum_{i=1}^{N}|y_i-f_\theta(x_i)|\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 视为常数时，第一项与 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 无关，因此最小化 NLL 关于 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 等价于最小化绝对误差和（Sum of Absolute Errors, SAE）：</p>
<span displaypfx="" class="mathjax-container">\[\hat\theta_{\mathrm{MLE}}=\arg\min_\theta\sum_{i=1}^{N}|y_i-f_\theta(x_i)|\]</span>
<p>这正是平均绝对误差（Mean Absolute Error, MAE）背后的概率来源。与高斯分布相比，拉普拉斯分布在中心更尖、尾部更厚，因此它对应的损失不再像平方误差那样强烈放大大残差，而是按线性方式惩罚偏差。也正因为如此，MAE 通常比 MSE 对离群点（Outlier）更稳健。</p>
<p>因此，高斯分布与最小二乘、拉普拉斯分布与绝对误差、Bernoulli / Categorical 分布与交叉熵，其实是同一个统计逻辑在不同任务类型下的三种展开：<span style="background-color: #c0c0c0;">先假设观测数据服从某个条件分布，再对训练集做最大似然估计；把负对数似然写开后，就得到具体的训练损失</span>。</p>
<div class="blog_h2"><span class="graybg">最大后验估计（MAP）</span></div>
<p>最大后验估计（Maximum A Posteriori, MAP）在 MLE 的“数据解释能力”之外，再加入参数的先验（Prior）信息。它选择后验概率（Posterior）最大的参数：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MAP}}=\arg\max_{\theta}p(\theta|D)\]</span>
<p>根据贝叶斯定理：</p>
<span displaypfx="" class="mathjax-container">\[p(\theta|D)=\frac{p(D|\theta)p(\theta)}{p(D)}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(p(D|\theta)\)</span> 是似然（Likelihood）， <span displaypfx="inline-" class="mathjax-container">\(p(\theta)\)</span> 是先验， <span displaypfx="inline-" class="mathjax-container">\(p(D)\)</span> 是证据（Evidence）。因为 <span displaypfx="inline-" class="mathjax-container">\(p(D)\)</span> 与参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 无关，所以在求最大值时它只是常数，可以直接忽略：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MAP}}=\arg\max_{\theta}p(D|\theta)p(\theta)=\arg\max_{\theta}\big(\log p(D|\theta)+\log p(\theta)\big)\]</span>
<p>这个式子清楚地说明了 MAP 的结构： <span displaypfx="inline-" class="mathjax-container">\(\log p(D|\theta)\)</span> 负责拟合数据， <span displaypfx="inline-" class="mathjax-container">\(\log p(\theta)\)</span> 负责约束参数不要偏离先验认知。若改写成最小化形式，则有</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MAP}}=\arg\min_\theta\big(-\log p(D|\theta)-\log p(\theta)\big)\]</span>
<p>因此，MAP 可以理解为“负对数似然 + 先验诱导的惩罚项（Penalty）”。这也是为什么很多正则化（Regularization）能够从贝叶斯视角解释：L2 正则通常对应高斯先验，L1 正则通常对应拉普拉斯先验。若先验在可行域内与 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 无关（例如均匀先验： <span displaypfx="inline-" class="mathjax-container">\(p(\theta)=c\)</span>），则 <span displaypfx="inline-" class="mathjax-container">\(\log p(\theta)=\log c\)</span> 是关于 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的常数；在 <span displaypfx="inline-" class="mathjax-container">\(\arg\max_\theta\)</span> 中加上或去掉这一项都不改变最优点，因此 MAP 与 MLE 给出同一组参数（若均匀先验还隐含“只在某个范围内为常数、范围外为 0”，则等价于在该范围约束下做 MLE）。</p>
<p>继续用抛硬币解释。现在参数已经统一记作 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，因此先验也写成 <span displaypfx="inline-" class="mathjax-container">\(p(\theta)\)</span>，而不再写 <span displaypfx="inline-" class="mathjax-container">\(p(p)\)</span>。若对 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 设 Beta 分布（Beta Distribution）先验</p>
<p>Beta 分布是定义在区间 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span> 上的连续分布，最常用来描述“某个概率值本身的不确定性”，因此非常适合作为伯努利参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的先验。</p>
<span displaypfx="" class="mathjax-container">\[p(\theta)\propto \theta^{\alpha-1}(1-\theta)^{\beta-1},\quad 0\le \theta\le 1\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\propto\)</span> 表示“正比于”，意思是左边真正的概率密度函数还差一个归一化常数（Normalization Constant），这个常数负责保证密度在区间 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span> 上积分为 1。对 MAP 而言，这个常数不影响最大值位置，所以通常省略不写。</p>
<p>参数 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span> 控制分布形状： <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 越大，分布越偏向较大的 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>； <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span> 越大，分布越偏向较小的 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>。例如 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Beta}(2,2)\)</span> 会把概率质量更多放在中间区域，表达“更倾向认为硬币接近公平”； <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Beta}(8,2)\)</span> 则更偏向 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 接近 1。</p>
<p>那么它表达的是：在看到数据之前，我们已经对“硬币正面概率应该落在什么范围”有一个先验偏好。</p>
<p>把这个先验与 7 正 3 反的似然 <span displaypfx="inline-" class="mathjax-container">\(p(D|\theta)=\theta^7(1-\theta)^3\)</span> 相乘，得到后验：</p>
<span displaypfx="" class="mathjax-container">\[p(\theta|D)\propto \theta^{7+\alpha-1}(1-\theta)^{3+\beta-1}\]</span>
<p>这说明后验仍然是 Beta 分布：</p>
<span displaypfx="" class="mathjax-container">\[p(\theta|D)=\mathrm{Beta}(7+\alpha,3+\beta)\]</span>
<p>众数（Mode）指的是<span style="background-color: #c0c0c0;">概率密度函数最高的那个位置</span>，也就是“这个分布最偏好的参数值”。因为 MAP 的定义本来就是寻找后验概率/后验密度最大的参数，所以当后验分布已经写出来时，MAP 估计就是这个后验分布的众数。</p>
<p>因为后验与 <span displaypfx="inline-" class="mathjax-container">\(\theta^{7+\alpha-1}(1-\theta)^{3+\beta-1}\)</span> 成正比，所以只需最大化这个函数。取对数后，等价目标变成</p>
<span displaypfx="" class="mathjax-container">\[(7+\alpha-1)\log \theta+(3+\beta-1)\log(1-\theta)\]</span>
<p>对 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 求导并令导数为 0：</p>
<span displaypfx="" class="mathjax-container">\[\frac{7+\alpha-1}{\theta}-\frac{3+\beta-1}{1-\theta}=0\]</span>
<p>移项并整理可得</p>
<span displaypfx="" class="mathjax-container">\[(7+\alpha-1)(1-\theta)=(3+\beta-1)\theta\Rightarrow \hat{\theta}_{\mathrm{MAP}}=\frac{7+\alpha-1}{10+\alpha+\beta-2}\]</span>
<p>因此，当 <span displaypfx="inline-" class="mathjax-container">\(\alpha,\beta&gt;1\)</span> 时，Beta 分布的众数（Mode）也就是 MAP 估计。</p>
<p>若取 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Beta}(2,2)\)</span> 先验，则</p>
<span displaypfx="" class="mathjax-container">\[\hat{\theta}_{\mathrm{MAP}}=\frac{7+2-1}{10+2+2-2}=\frac{8}{12}=\frac{2}{3}\]</span>
<p>它比 MLE 的 <span displaypfx="inline-" class="mathjax-container">\(0.7\)</span> 更靠近 <span displaypfx="inline-" class="mathjax-container">\(0.5\)</span>。原因很具体： <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Beta}(2,2)\)</span> 可以理解为在真实观测之外，先额外放入了“1 次正面 + 1 次反面”的温和偏好，所以最终估计不会被7正3反的样本完全带着走。样本很少时，先验的影响明显；样本很多时，数据项占主导，MAP 会逐渐逼近 MLE。</p>
<p>在机器学习里，MAP 与正则化（Regularization）本质上是同一件事的两种表述。不是“随意加一个惩罚项”，而是：<span style="background-color: #c0c0c0;">一旦你给参数指定先验，取负对数后，这个先验就会自动变成优化目标里的正则项</span>。</p>
<p>先看一般形式。MAP 最大化的是后验概率 <span displaypfx="inline-" class="mathjax-container">\(p(\theta|D)\)</span>，等价于最小化负对数后验：</p>
<span displaypfx="" class="mathjax-container">\[-\log p(\theta|D)=-\log p(D|\theta)-\log p(\theta)+\mathrm{const}\]</span>
<p>第一项 <span displaypfx="inline-" class="mathjax-container">\(-\log p(D|\theta)\)</span> 是数据拟合项，第二项 <span displaypfx="inline-" class="mathjax-container">\(-\log p(\theta)\)</span> 就是先验带来的惩罚项。因此“正则化”并不是凭空发明出来的工程技巧，而是贝叶斯先验在优化问题里的直接体现。</p>
<p>若参数向量 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 服从零均值各向同性高斯先验（Isotropic Gaussian Prior），可以写成</p>
<span displaypfx="" class="mathjax-container">\[p(\theta)\propto \exp\left(-\frac{1}{2\tau^2}\|\theta\|_2^2\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\tau^2\)</span> 控制先验方差：方差越小，越强地偏好参数靠近 0。对它取负对数得到</p>
<span displaypfx="" class="mathjax-container">\[-\log p(\theta)=\frac{1}{2\tau^2}\|\theta\|_2^2+\mathrm{const}\]</span>
<p>把常数 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2\tau^2}\)</span> 记成 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span>，就得到熟悉的目标：</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta)+\lambda\|\theta\|_2^2\]</span>
<p>这正是 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 正则。它的作用可以理解为：先验认为“大权重不太可信”，所以优化时不仅要拟合数据，还要为过大的参数付出代价。由于高斯先验在 0 附近平滑、尾部衰减快，它通常会把参数整体压小，但不特别鼓励大量参数精确等于 0。</p>
<p>若先验改成拉普拉斯（Laplacian）分布：</p>
<span displaypfx="" class="mathjax-container">\[p(\theta)\propto \exp\big(-\lambda\|\theta\|_1\big)\]</span>
<p>则负对数先验变成</p>
<span displaypfx="" class="mathjax-container">\[-\log p(\theta)=\lambda\|\theta\|_1+\mathrm{const}\]</span>
<p>于是 MAP 等价于最小化</p>
<span displaypfx="" class="mathjax-container">\[-\log p(D|\theta)+\lambda\|\theta\|_1\]</span>
<p>这就是 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 正则。它与 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 的关键区别在于：拉普拉斯先验在 0 处尖得更厉害，因此更强地偏好很多参数直接变成 0，也就是产生稀疏性（Sparsity）。</p>
<p>所以可以把对应关系记成一条很清楚的链：<span style="background-color: #c0c0c0;">高斯先验对应 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span>，拉普拉斯先验对应 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span>；正则项的形状，本质上就是先验密度形状的负对数</span>。很多看起来像“人为添加的惩罚项”，其实都可以解释为“在做 MAP”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/regularization-distribution-assumptions.png"><img class="alignnone size-full" src="https://blog.gmem.cc/wp-content/uploads/2026/03/regularization-distribution-assumptions.png" alt="regularization-distribution-assumptions" width="1920" height="1064" /></a></p>
<p>这里的分布假设有两个不同落点。若假设的是数据误差或残差 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 的分布，它进入的是似然 <span displaypfx="inline-" class="mathjax-container">\(p(D|\theta)\)</span>，决定数据拟合项的形状；若假设的是参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 自身的分布，它进入的是先验 <span displaypfx="inline-" class="mathjax-container">\(p(\theta)\)</span>，决定正则项的形状。前者回答“数据会怎样围绕模型波动”，后者回答“参数本身更可能落在什么区域”。</p>
<p>因此，高斯和拉普拉斯并不只用于描述数据集。高斯残差假设会把负对数似然化成平方误差，拉普拉斯残差假设会把负对数似然化成绝对误差；与此同时，零均值高斯参数先验会把负对数先验化成 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 正则，零均值拉普拉斯参数先验会把负对数先验化成 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 正则。它们使用的是同一类分布族，但一个作用在数据项 <span displaypfx="inline-" class="mathjax-container">\(p(D|\theta)\)</span>，一个作用在参数项 <span displaypfx="inline-" class="mathjax-container">\(p(\theta)\)</span>。</p>
<div class="blog_h2"><span class="graybg">大数定律与中心极限定理</span></div>
<div class="blog_h3"><span class="graybg">大数定律（Law of Large Numbers, LLN）</span></div>
<p>大数定律说明：当独立同分布样本越来越多时，样本平均会稳定地靠近真实期望（Expectation）。若 <span displaypfx="inline-" class="mathjax-container">\(X_1,\dots,X_n\)</span> 独立同分布，且 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]\)</span> 存在，则</p>
<span displaypfx="" class="mathjax-container">\[\frac{1}{n}\sum_{i=1}^{n}X_i \to \mathbb{E}[X],\quad n\to\infty\]</span>
<p>这里的重点是“平均值会稳定下来”，而不是“每一次新观测都会让结果更接近真值”。它描述的是长期趋势：样本量越大，随机波动在平均过程中被不断抵消，样本平均就越不容易偏离真实期望太远。</p>
<p>最经典的例子是抛公平硬币。设正面记为 1、反面记为 0，则单次试验的期望是 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[X]=0.5\)</span>。前几次抛掷时，正面比例可能是 1、0、0.67、0.25，波动很大；但抛掷次数达到几百、几千次后，样本平均 <span displaypfx="inline-" class="mathjax-container">\(\bar{X}\)</span> 会越来越稳定地靠近 0.5。大数定律回答的是“为什么频率会稳定到概率附近”。</p>
<div class="blog_h3"><span class="graybg">中心极限定理（Central Limit Theorem, CLT）</span></div>
<p>中心极限定理（Central Limit Theorem, CLT）描述的是：在独立同分布且方差有限的条件下，样本均值经过适当标准化后，会趋近于正态分布（Normal Distribution）。它给出的不是“平均值最终落到哪里”的结论，而是“平均值围绕真值波动时，波动形状如何分布”的规律。</p>
<p>因此，中心极限定理讨论的重点不是“均值会不会收敛”，而是<span style="background-color: #c0c0c0;">样本均值在真值附近如何波动，以及这种波动的分布长什么样</span>。标准写法是：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\sqrt{n}(\bar{X}-\mu)}{\sigma}\Rightarrow \mathcal{N}(0,1)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mu=\mathbb{E}[X]\)</span> 是总体均值， <span displaypfx="inline-" class="mathjax-container">\(\sigma^2=\mathrm{Var}(X)\)</span> 是总体方差。所谓<span style="background-color: #c0c0c0;">标准化（Standardization）</span>，就是先减去均值 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span>，把中心移到 0；再除以标准差 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span>，把尺度统一成“多少个标准差”；再乘上 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{n}\)</span>，把样本均值随着样本量增大而缩小的波动重新放回可比较的尺度。标准化之后，不同样本量下的波动可以放到同一个参考系里比较。</p>
<p>所谓<span style="background-color: #c0c0c0;">方差有限</span>，就是随机变量的波动强度不是无限大，满足 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(X)&lt;\infty\)</span>。直观上，这意味着样本虽然会波动，但不会被极端值以“无限强”的方式主导。像伯努利分布、二项分布、泊松分布、均匀分布和高斯分布都满足这个条件；而某些重尾分布则可能不满足，因此不能直接套用最基础的 CLT 表述。</p>
<p>为什么要乘上 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{n}\)</span>？因为样本均值的波动规模大约是 <span displaypfx="inline-" class="mathjax-container">\(\sigma/\sqrt{n}\)</span>：样本越多，均值越稳定。如果不乘 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{n}\)</span>，当 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 增大时， <span displaypfx="inline-" class="mathjax-container">\(\bar{X}-\mu\)</span> 会越来越接近 0，看不出其分布形状；乘上 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{n}\)</span> 后，波动被拉回到常数量级，才会显现出稳定的正态极限。</p>
<p>继续看硬币例子。设 <span displaypfx="inline-" class="mathjax-container">\(X_i\in\{0,1\}\)</span> 且正面概率为 0.5，则 <span displaypfx="inline-" class="mathjax-container">\(\mu=0.5\)</span>， <span displaypfx="inline-" class="mathjax-container">\(\sigma^2=0.25\)</span>，标准差 <span displaypfx="inline-" class="mathjax-container">\(\sigma=0.5\)</span>。CLT 说的是：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\sqrt{n}(\bar{X}-0.5)}{0.5}\Rightarrow \mathcal{N}(0,1)\]</span>
<p>例如当 <span displaypfx="inline-" class="mathjax-container">\(n=100\)</span> 时，正面比例 <span displaypfx="inline-" class="mathjax-container">\(\bar{X}\)</span> 的标准差大约是 <span displaypfx="inline-" class="mathjax-container">\(0.5/\sqrt{100}=0.05\)</span>。这意味着正面比例通常会落在 <span displaypfx="inline-" class="mathjax-container">\(0.5\pm 0.1\)</span> 这一量级附近；更精确地，用正态近似可写成</p>
<span displaypfx="" class="mathjax-container">\[\bar{X}\approx \mathcal{N}\left(0.5,\frac{0.25}{100}\right)=\mathcal{N}(0.5,0.0025)\]</span>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-clt.png"><img class="alignnone size-full wp-image-40929" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot-clt.png" alt="plot-clt" width="100%" /></a></p>
<p>上图是样本量分别为1, 5, 20, 100的Bernoulli(0.5)分布，分别进行了30000轮测试，绘制的（每轮）标准化后的分布，可以看到样本量足够多时，其均值倾向于向中心（未标准化前的0.5对应此伯努利分布的总体均值）靠近。</p>
<p>所以 CLT 回答的是“为什么很多统计量在样本足够大时会近似高斯”，而 LLN 回答的是“为什么样本平均会逼近真值”。前者给出分布近似，后者给出收敛结论。置信区间、显著性检验、A/B test 和 mini-batch 梯度噪声分析，主要依赖的是 CLT 提供的近似正态性。</p>
<div class="blog_h3"><span class="graybg">置信度与置信区间</span></div>
<p>置信度（Confidence Level）与置信区间（Confidence Interval）属于统计推断（Statistical Inference）中的区间估计（Interval Estimation）。它们处理的不是“样本本身长什么样”，而是<span style="background-color: #c0c0c0;">在只看到一批有限样本时，怎样给未知总体参数画出一个有覆盖保证的范围</span>。在机器学习实验、A/B test、离线评测和指标报表里，样本均值、准确率、点击率、转化率和误差率后面常跟着一个区间，这个区间就是区间估计的产物。</p>
<p>若目标是估计总体均值 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span>，样本均值为 <span displaypfx="inline-" class="mathjax-container">\(\bar{X}\)</span>，且样本量足够大或总体近似正态，那么基于中心极限定理，可以构造近似的双侧置信区间：</p>
<span displaypfx="" class="mathjax-container">\[\bar{X}\pm z_{1-\alpha/2}\frac{\sigma}{\sqrt{n}}\]</span>
<p>若总体标准差 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 未知，而样本来自正态总体或样本量适中且使用样本标准差 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 近似，则常写作：</p>
<span displaypfx="" class="mathjax-container">\[\bar{X}\pm t_{1-\alpha/2,\,n-1}\frac{s}{\sqrt{n}}\]</span>
<p>这里每个元素的含义分别是：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\bar{X}\)</span>：样本均值（Sample Mean），是当前样本对总体均值的点估计（Point Estimate）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(n\)</span>：样本量（Sample Size）。样本越多，区间通常越窄。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span>：总体标准差（Population Standard Deviation）；若未知，常用样本标准差 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 替代。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\frac{\sigma}{\sqrt{n}}\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\frac{s}{\sqrt{n}}\)</span>：标准误（Standard Error），表示样本均值本身的波动尺度，而不是单个样本点的波动。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span>：显著性水平（Significance Level）。若置信度为 95%，则 <span displaypfx="inline-" class="mathjax-container">\(\alpha=0.05\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(z_{1-\alpha/2}\)</span>：标准正态分布的分位数（Quantile）；95% 置信度时大约是 <span displaypfx="inline-" class="mathjax-container">\(1.96\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(t_{1-\alpha/2,\,n-1}\)</span>：Student t 分布的分位数，带有 <span displaypfx="inline-" class="mathjax-container">\(n-1\)</span> 个自由度（Degrees of Freedom），用于总体方差未知时。</li>
</ul>
<p>在这个表达式里，<span style="background-color: #c0c0c0;">置信区间</span> 指的是最终得到的区间本身，例如 <span displaypfx="inline-" class="mathjax-container">\([1.8,2.4]\)</span>；<span style="background-color: #c0c0c0;">置信度</span> 指的是构造这个区间的方法在重复抽样下的长期覆盖率，例如 95%、99%。因此，95% 置信区间的严格含义是：如果用同一种抽样方式和同一种区间构造公式反复重复实验，那么大约 95% 的区间会覆盖真实参数。</p>
<p>这一定义有一个极其重要的解释边界。总体参数 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 在经典频率学派（Frequentist）统计里被视为固定但未知的常数；随机的是样本，因此随机的是区间端点。样本一旦观察完成，这次得到的区间要么覆盖 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span>，要么不覆盖，概率意义已经不再施加在参数本身上。于是，95% 置信度描述的是<span style="background-color: #c0c0c0;">区间构造程序的长期可靠性</span>，而不是“参数有 95% 概率落在这次区间里”的后验概率表述。</p>
<p>置信度并不局限于连续分布。它的定义依赖的是“重复抽样下的覆盖率”，而不是总体分布是否连续。对伯努利分布（Bernoulli Distribution）、二项分布（Binomial Distribution）、泊松分布（Poisson Distribution）这类离散分布，同样可以对参数构造 95% 或 99% 的区间估计。例如对二项分布中的成功概率 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，就可以根据观测到的成功次数构造 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 的置信区间；若参数或目标对象不是一维连续量，更宽泛的名称则是置信集合（Confidence Set）。</p>
<p>离散分布与连续分布的差别不在“能不能谈置信度”，而在于区间构造的细节。连续情形下，区间端点可以较平滑地调节到目标覆盖率附近；离散情形下，统计量只能取离散值，覆盖率往往无法恰好等于名义值，例如正好 95%，而常常只能做到“不低于 95%”。因此，离散分布里的精确区间常带有一定保守性（Conservativeness）：区间更宽一些，但覆盖保证更稳。这也是为什么在离散统计推断里，除了近似正态区间外，还常见 Clopper–Pearson 这类精确区间构造方法。</p>
<p>一个具体例子最容易说明这两个概念。设某模型在 100 次独立测试中的平均准确率估计为 <span displaypfx="inline-" class="mathjax-container">\(\bar{X}=0.82\)</span>，样本标准差为 <span displaypfx="inline-" class="mathjax-container">\(s=0.10\)</span>。若采用近似 95% 置信区间，可写成：</p>
<span displaypfx="" class="mathjax-container">\[0.82\pm 1.96\times \frac{0.10}{\sqrt{100}}=0.82\pm 0.0196\]</span>
<p>于是区间约为：</p>
<span displaypfx="" class="mathjax-container">\[[0.8004,\ 0.8396]\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\([0.8004,0.8396]\)</span> 是置信区间，95% 是置信度。它表示：按这种抽样和构造方式长期重复下去，约 95% 的此类区间会覆盖真实平均准确率；这次具体得到的这个区间只是其中一个实现结果。</p>
<p>区间宽度主要由三个因素共同决定：</p>
<ul>
<li>样本波动越大，即 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 越大，区间越宽。</li>
<li>样本量越大，即 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 越大，标准误越小，区间越窄。</li>
<li>置信度越高，例如从 95% 提高到 99%，对应分位数更大，区间也会变宽。</li>
</ul>
<p>因此，置信区间是在样本有限时对参数估计可靠性的定量表达；置信度则是这套区间构造方法愿意给出的覆盖承诺。两者必须一起理解：只有区间没有置信度，范围没有统计保证；只有置信度没有区间，覆盖承诺也无法落到具体参数估计上。</p>
<div class="blog_h2"><span class="graybg">随机过程</span></div>
<p>前面讨论的随机变量（Random Variable）通常只对应一次不确定试验，例如抛一次硬币、测一次身高、抽取一个样本标签。随机过程（Stochastic Process）则讨论<span style="background-color: #c0c0c0;">一串彼此有关、按时间或空间索引起来的随机变量</span>。它适合描述会随时间演化的不确定系统，例如天气变化、股价波动、队列长度、用户行为序列、传感器读数，以及自然语言中的 token 序列。</p>
<p>形式上，随机过程可记为：</p>
<span displaypfx="" class="mathjax-container">\[\{X_t\}_{t\in T}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 是索引集合（Index Set），常表示时间； <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 是时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的随机变量。若 <span displaypfx="inline-" class="mathjax-container">\(T=\{0,1,2,\dots\}\)</span>，过程是离散时间（Discrete-time）的；若 <span displaypfx="inline-" class="mathjax-container">\(T=[0,\infty)\)</span>，过程则是连续时间（Continuous-time）的。每个 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 都有自己的分布，但更重要的是这些随机变量之间的联合分布（Joint Distribution）与依赖结构，因为随机过程关心的不是“某一时刻单独会怎样”，而是“整个序列怎样一起变化”。</p>
<p>从结果形态看，一次随机过程的实现不再是一个点，而是一条轨迹（Trajectory）或样本路径（Sample Path）。例如，设 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 天的气温，那么一次观测得到的是一整段温度序列 <span displaypfx="inline-" class="mathjax-container">\((X_1,X_2,\dots,X_T)\)</span>；若 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 表示语言模型在位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 生成的 token，那么一次实现就是一整句文本。</p>
<p>随机过程之所以在机器学习里重要，是因为很多任务天生就是时序问题。时间序列预测关心未来值如何演化，隐马尔可夫模型（Hidden Markov Model, HMM）关心隐藏状态如何随时间转移，强化学习关心状态—动作—奖励序列怎样展开，自回归语言模型则是在建模 token 序列的联合概率。因此，随机过程可以看作“把单个随机变量扩展到整条时间轴上的概率建模语言”。</p>
<div class="blog_h3"><span class="graybg">马尔可夫性与马尔可夫过程</span></div>
<p>马尔可夫性（Markov Property）讨论的是随机过程中的“记忆如何被压缩”。它刻画这样一种情形：<span style="background-color: #c0c0c0;">如果当前状态已经把与未来演化有关的信息概括完整，那么预测下一步时就不再需要显式回看更久的历史</span>。在这种表示下，过去对未来的影响已经通过当前状态被浓缩进来了。</p>
<p>设随机过程为 <span displaypfx="inline-" class="mathjax-container">\(\{X_t\}_{t=0}^{\infty}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 表示时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的随机状态。若它满足一阶马尔可夫性，则有：</p>
<span displaypfx="" class="mathjax-container">\[P(X_{t+1}\mid X_t,X_{t-1},\dots,X_0)=P(X_{t+1}\mid X_t)\]</span>
<p>这条式子的含义是：在已经知道当前状态 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span> 的前提下，再额外知道更早的历史 <span displaypfx="inline-" class="mathjax-container">\(X_{t-1},\dots,X_0\)</span>，不会改变对下一时刻 <span displaypfx="inline-" class="mathjax-container">\(X_{t+1}\)</span> 的条件分布判断。左边是“条件在完整历史上的下一步分布”，右边是“条件在当前状态上的下一步分布”；两者相等，正是马尔可夫性的定义。</p>
<p>一个直接例子是天气变化。若把每天的天气记作随机变量 <span displaypfx="inline-" class="mathjax-container">\(X_t\)</span>，状态空间为 <span displaypfx="inline-" class="mathjax-container">\(\{\text{晴},\text{阴},\text{雨}\}\)</span>，那么常见建模假设是：明天是否下雨主要由今天的天气决定，而不需要把更早几天的天气逐项保留。此时“当前天气”就扮演了压缩历史信息的状态摘要。若今天是雨天，明天继续下雨的概率可以较大；若今天是晴天，明天下雨的概率可以较小。这种“只通过当前状态决定下一步分布”的结构，就是马尔可夫性。</p>
<p>满足马尔可夫性的随机过程，称为马尔可夫过程（Markov Process）或马尔可夫链（Markov Chain，离散时间、有限或可数状态时的常见名称）。因此，马尔可夫性是一个性质，马尔可夫过程是满足该性质的一类随机过程。若状态空间有限，常把一步转移概率写成转移矩阵（Transition Matrix） <span displaypfx="inline-" class="mathjax-container">\(P\)</span>，其中：</p>
<span displaypfx="" class="mathjax-container">\[P_{ij}=P(X_{t+1}=j\mid X_t=i)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(P_{ij}\)</span> 表示“当前在状态 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 时，下一步转移到状态 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的概率”。矩阵的每一行对应当前状态固定后的下一步分布，因此每一行元素都非负，且行和为 1。</p>
<p>马尔可夫过程之所以重要，是因为它把一个潜在上非常复杂的时序依赖问题，压缩成了局部转移规律。隐马尔可夫模型（Hidden Markov Model, HMM）、马尔可夫决策过程（Markov Decision Process, MDP）、很多时间序列状态模型，以及强化学习中的环境状态转移，都建立在这一思想之上。它们的差别不在于是否使用马尔可夫性，而在于：状态是否可见、是否存在动作、是否附带奖励，以及是否只关心下一步还是关心长期回报。</p>
<p>马尔可夫性是一种建模假设，不是自然界自动保证的真理。若“当前状态”定义得不充分，历史信息就没有被真正压缩进去，此时过程在这个状态表示下就不具有马尔可夫性。例如，仅用“当前股价”预测明天走势通常不够，因为成交量、市场情绪、宏观事件等信息并未包含在状态中；但若把更完整的市场状态向量一并纳入，马尔可夫近似会更合理。因此，马尔可夫性的关键不只是公式，而是<span style="background-color: #c0c0c0;">当前状态是否足以成为过去对未来影响的充分摘要</span>。</p>
<div class="blog_h2"><span class="graybg">信息论</span></div>
<div class="blog_h3"><span class="graybg">熵（Entropy）</span></div>
<p>熵（Entropy）刻画一个概率分布的不确定性（Uncertainty）。对离散分布 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[H(p)=-\sum_i p_i\log p_i\]</span>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(-\log p(x)\)</span> 理解为“事件 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的信息量（Information Content）/惊奇”，熵就是它的期望值：越均匀的分布越难预测，熵越大；越尖锐（某个事件概率接近 1）的分布越确定，熵越小。</p>
<p>例：若 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个类别上均匀（<span displaypfx="inline-" class="mathjax-container">\(p_i=1/K\)</span>），则</p>
<span displaypfx="" class="mathjax-container">\[H(p)=\log K\]</span>
<p>对数底决定单位：以 <span displaypfx="inline-" class="mathjax-container">\(\log_2\)</span> 为底单位是比特（Bits），以自然对数为底单位是纳特（Nats）。</p>
<p>语言也能帮助建立对熵的直觉。以字符为单位时，汉字集合更大，单个常用字符的平均出现概率往往更低，因此 <span displaypfx="inline-" class="mathjax-container">\(-\log p(x)\)</span> 更大；同时，汉语序列里的下一字符通常也更难直接预测，所以常给人“字符级信息更密、熵更高”的直觉。对比之下，韩语的字符系统更小，序列模式也通常更规则，下一字符更容易预测，因此字符级冗余感更强、熵的直觉更低。</p>
<p>若再做一个极粗略的字符级估算：把常用汉字集合近似看作 <span displaypfx="inline-" class="mathjax-container">\(V=3000\)</span>、把韩文字母集合近似看作 <span displaypfx="inline-" class="mathjax-container">\(V=40\)</span>，并暂时假设“下一字符”在各自词表上均匀分布，则最大熵分别约为 <span displaypfx="inline-" class="mathjax-container">\(\log_2 3000\approx 11.55\)</span> bits 与 <span displaypfx="inline-" class="mathjax-container">\(\log_2 40\approx 5.32\)</span> bits。这对应的就是：词表越大、单符号平均概率越低，单位符号的潜在信息量上界越高。</p>
<div class="blog_h3"><span class="graybg">KL 散度</span></div>
<p>KL 散度（Kullback–Leibler Divergence）衡量两个分布之间的“相对熵”（Relative Entropy）。对离散分布 <span displaypfx="inline-" class="mathjax-container">\(p,q\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[D_{\mathrm{KL}}(p\|q)=\sum_i p_i\log\frac{p_i}{q_i}\]</span>
<p>它满足 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(p\|q)\ge 0\)</span>，且当且仅当 <span displaypfx="inline-" class="mathjax-container">\(p=q\)</span> 时取 0；但它一般不对称（Asymmetric），即 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(p\|q)\ne D_{\mathrm{KL}}(q\|p)\)</span>。信息论解释是：用 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 的码本去编码来自 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 的样本，会额外多付出多少平均码长。</p>
<p>在机器学习里，KL 常作为正则项出现，例如把新策略/新模型约束在参考分布附近（KL Regularization）。</p>
<div class="blog_h3"><span class="graybg">交叉熵</span></div>
<p>交叉熵（Cross Entropy）是信息论里衡量“用分布 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 去编码来自分布 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 的样本”所需平均信息量的量。这里 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是真实分布（True Distribution）/数据分布， <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 是模型分布（Model Distribution）/预测分布：</p>
<span displaypfx="" class="mathjax-container">\[H(p,q)=-\sum_{i} p_i\log q_i\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 固定时，最小化 <span displaypfx="inline-" class="mathjax-container">\(H(p,q)\)</span> 等价于最小化 KL 散度（Kullback–Leibler Divergence）：</p>
<span displaypfx="" class="mathjax-container">\[H(p,q)=H(p)+D_{\mathrm{KL}}(p\|q)\]</span>
<p>分类问题里，真实标签通常用 one-hot 分布表示，这会把交叉熵简化成对“真实类别概率”的负对数：交叉熵损失（Cross-Entropy Loss）本质上就是负对数似然（Negative Log-Likelihood）。</p>
<p>“信息量为什么和概率有关？”因为在最优编码（Optimal Coding）里，一个事件 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的最短平均码长与 <span displaypfx="inline-" class="mathjax-container">\(-\log p(x)\)</span> 同阶；以 <span displaypfx="inline-" class="mathjax-container">\(\log_2\)</span> 为底时单位是比特（Bits），以自然对数为底时单位是纳特（Nats）。熵（Entropy）就是期望信息量。</p>
<p>一个具体例子：设真实分布 <span displaypfx="inline-" class="mathjax-container">\(p=(0.5,0.5)\)</span>，模型分布 <span displaypfx="inline-" class="mathjax-container">\(q=(0.9,0.1)\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[H(p,q)=-0.5\log 0.9-0.5\log 0.1\approx 1.204\ (\text{nats})\]</span>
<p>而真实熵 <span displaypfx="inline-" class="mathjax-container">\(H(p)=-0.5\log 0.5-0.5\log 0.5\approx 0.693\)</span>，两者的差就是 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(p\|q)\approx 0.511\)</span>：模型越“错得自信”，KL 越大：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/plot_entropy_cross.png"><img class="alignnone size-full wp-image-40943" src="https://blog.gmem.cc/wp-content/uploads/2026/03/plot_entropy_cross.png" alt="plot_entropy_cross" width="100%" /></a></p>
<div class="blog_h4"><span class="graybg">惊奇（Surprise）</span></div>
<p>惊奇（Surprise）/信息量是针对单个事件的度量：若事件 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 的概率为 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Surprise}(x)=-\log p(x)\]</span>
<p>概率越小，惊奇越大；概率越大，惊奇越小。在语言建模里，若当前模型参数已经固定，那么真实 token 的 <span displaypfx="inline-" class="mathjax-container">\(-\log p_\theta(y_t|c_t)\)</span> 本质上是该 token 的负对数概率，也就是它的“惊奇”。这里 <span displaypfx="inline-" class="mathjax-container">\(c_t\)</span> 表示当前位置之前的上下文， <span displaypfx="inline-" class="mathjax-container">\(y_t\)</span> 表示当前位置真实出现的 token。若把同一个式子看成关于参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的函数，它又是该 token 对参数的负对数似然（Negative Log-Likelihood, NLL）。因此这里的“惊奇”和 token-level NLL 在数值上是同一个对象，只是视角不同。如果再按各个事件的真实概率对这种“惊奇”做加权平均，得到的就是熵（Entropy）：<span displaypfx="inline-" class="mathjax-container">\(H(X)=\mathbb{E}[-\log p(X)]\)</span>。换句话说，<span style="background-color: #c0c0c0;">惊奇是单个事件的信息量，熵是这种信息量在整个分布下的期望</span>。</p>
<div class="blog_h4"><span class="graybg">困惑度（Perplexity）</span></div>
<p>困惑度（Perplexity）把“平均惊奇”指数化，得到一个更直观的尺度：可把它理解为模型在每一步预测时的“有效分支数（Effective Branching Factor）”。对长度为 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 的 token 序列，模型给出的条件概率是 <span displaypfx="inline-" class="mathjax-container">\(q(x_t|x_{&lt;t})\)</span>，则平均 NLL（也就是交叉熵）为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{NLL}=-\frac{1}{N}\sum_{t=1}^{N}\log q(x_t|x_{&lt;t})\]</span>
<p>若使用自然对数，困惑度定义为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{PPL}=\exp(\mathrm{NLL})\]</span>
<p>因此：分布越均匀（更不确定），熵越高，平均惊奇越大，困惑度也越高。注意困惑度强烈依赖 tokenization 与评测语料，跨不同分词器/词表直接比较往往没有意义。</p>
<div class="blog_h1"><span class="graybg">常用算法</span></div>
<div class="blog_h2"><span class="graybg">基础数据结构和算法</span></div>
<p>这一节处理的核心问题是：当面对搜索、更新、统计、调度、最短路径、依赖分析或训练流水线等任务时，数据应该怎样组织，操作应该怎样执行，才能既正确又高效。数据结构（Data Structure）决定“数据在内存里如何表示”，算法（Algorithm）决定“在这种表示上如何完成查询、插入、删除、遍历、排序与优化”。很多系统性能问题，本质上不是算力不足，而是底层组织方式与操作方式不匹配。</p>
<p>可以把它理解成“仓库布局与搬运规则”的组合：同样一批货物，若排成连续货架、串成链式节点、组织成树状目录，或连接成路网，后续的查找、插入、合并与运输成本会完全不同。现代 AI 工程虽然把注意力集中在模型上，但数据加载器、特征流水线、参数缓存、向量检索、计算图调度、图学习和索引系统，最终都建立在这些基础结构之上。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">结构 / 算法</td>
<td style="text-align: center;">核心能力</td>
<td style="text-align: center;">典型复杂度</td>
<td style="text-align: center;">常见场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>数组 / 动态数组</td>
<td>按下标随机访问；顺序扫描效率高</td>
<td>访问 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>；中间插入/删除 <span displaypfx="inline-" class="mathjax-container">\(O(n)\)</span></td>
<td>张量、批数据、embedding、排序、滑动窗口</td>
</tr>
<tr>
<td>链表</td>
<td>已知位置后插入/删除代价低</td>
<td>局部插删 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>；查找 <span displaypfx="inline-" class="mathjax-container">\(O(n)\)</span></td>
<td>LRU、任务拼接、频繁重排的序列</td>
</tr>
<tr>
<td>栈（Stack）</td>
<td>后进先出（LIFO）</td>
<td>push / pop / top 均为 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span></td>
<td>递归展开、表达式解析、单调栈</td>
</tr>
<tr>
<td>队列（Queue）</td>
<td>先进先出（FIFO）</td>
<td>enqueue / dequeue 均为 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span></td>
<td>BFS、任务队列、流式缓冲</td>
</tr>
<tr>
<td>哈希表（Hash Table）</td>
<td>按键快速索引</td>
<td>平均查找/插入/删除 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span></td>
<td>字典、词表、缓存、去重</td>
</tr>
<tr>
<td>Bloom Filter</td>
<td>近似集合成员查询</td>
<td>插入/查询均为 <span displaypfx="inline-" class="mathjax-container">\(O(k)\)</span></td>
<td>缓存预检查、去重预过滤、存储层键存在性判断</td>
</tr>
<tr>
<td>树（Tree）</td>
<td>表达层次关系与有序结构</td>
<td>平衡查找常为 <span displaypfx="inline-" class="mathjax-container">\(O(\log n)\)</span></td>
<td>索引、优先队列、前缀匹配、规则分裂</td>
</tr>
<tr>
<td>图（Graph）</td>
<td>表达任意对象之间的关系</td>
<td>遍历通常为 <span displaypfx="inline-" class="mathjax-container">\(O(|V|+|E|)\)</span></td>
<td>社交网络、知识图谱、路线规划、依赖分析</td>
</tr>
</tbody>
</table>
<p>复杂度表只给出渐近上界，不能直接替代工程判断。真实系统还要同时考虑缓存友好性（Cache Locality）、常数项、并发开销、内存占用和实现复杂度。例如链表在理论上支持常数时间插入，但它对 CPU 缓存并不友好；数组在理论上中间插入较慢，但顺序扫描极快，因此在现代硬件上经常更有优势。</p>
<div class="blog_h3"><span class="graybg">数组与动态数组</span></div>
<p>数组（Array）处理的核心问题是：当元素类型一致、数量可以按顺序编号时，如何支持最低成本的随机访问与批量扫描。它的关键性质是<span style="background-color: #c0c0c0;">连续内存（Contiguous Memory）</span>。若每个元素大小为 <span displaypfx="inline-" class="mathjax-container">\(s\)</span>，首地址为 <span displaypfx="inline-" class="mathjax-container">\(\text{base}\)</span>，则第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个元素地址为</p>
<span displaypfx="" class="mathjax-container">\[\text{addr}(a_i)=\text{base}+i\cdot s\]</span>
<p>这个式子说明数组访问为何是 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>：位置可以直接计算，不需要沿指针逐步跳转。矩阵、张量、mini-batch、时间序列缓存、本地特征块和 embedding 表中的一行，本质上都依赖这种“地址可算”的结构。</p>
<p>数组的代价也非常明确：若在中间插入或删除元素，后面的元素必须整体搬移，因此复杂度通常是 <span displaypfx="inline-" class="mathjax-container">\(O(n)\)</span>。这意味着数组适合“读多写少、顺序稳定”的任务，不适合“在任意位置频繁插入”的任务。</p>
<p>动态数组（Dynamic Array）是在数组上的工程扩展：容量不足时申请更大的连续空间，把原有元素整体拷贝过去，再继续追加。一次扩容代价很高，但若容量按倍数增长，则追加操作的均摊（Amortized）复杂度仍可视为 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>。Python 的 list、C++ 的 vector、Java 的 ArrayList 都遵循这一思想。</p>
<p>直觉上，数组像按编号排好的货架：拿第 137 件货非常快，但若要把一件货塞进中间，后面整排货物都要整体后移。</p>
<div class="blog_h3"><span class="graybg">链表</span></div>
<p>链表（Linked List）处理的是另一类问题：当序列顺序经常变化时，能否避免数组那样的大规模搬移。链表不要求连续内存，而是让每个节点（Node）保存数据和指向下一个节点的指针（Pointer）；双向链表（Doubly Linked List）还会额外保存前驱指针。</p>
<p>若已经拿到某个节点的位置，那么在其前后插入或删除节点只需要调整局部指针，代价通常是 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>。但链表无法像数组那样通过下标直接定位第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个元素，查找往往必须从头逐个走过去，因此通常是 <span displaypfx="inline-" class="mathjax-container">\(O(n)\)</span>。</p>
<p>链表适合做“结构改动频繁、定位方式不是按下标而是按已有节点句柄”的任务。例如 LRU 缓存中，经常需要把刚访问的元素移到头部；若配合哈希表记录节点位置，链表就能高效完成重排。</p>
<p>链表像一串用绳子串起来的标签。改顺序很方便，但想直接摸到第 500 个标签，就只能沿着绳子一个个数过去。</p>
<div class="blog_h3"><span class="graybg">栈（Stack）</span></div>
<p>栈（Stack）定义的是一种<span style="background-color: #c0c0c0;">后进先出（Last In First Out, LIFO）</span>的访问约束。它处理的问题不是“怎样存更多数据”，而是“怎样强制最近进入的状态最先退出”。典型操作包括入栈 push、出栈 pop 与查看栈顶 top，它们都发生在同一端，因此实现代价通常是 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>。</p>
<p>函数调用栈、递归回溯、表达式求值、括号匹配、深度优先搜索中的显式状态保存，都依赖这种结构。其本质是把“尚未处理完的上下文”按嵌套顺序压起来，等内部任务结束后再按相反顺序恢复。</p>
<p>单调栈（Monotonic Stack）是栈在算法中的重要变体。它通过维护一个单调递增或单调递减的栈，把“下一个更大元素”“柱状图最大矩形”等问题从 <span displaypfx="inline-" class="mathjax-container">\(O(n^2)\)</span> 降到 <span displaypfx="inline-" class="mathjax-container">\(O(n)\)</span>。原因在于每个元素最多入栈和出栈各一次。</p>
<div class="blog_h3"><span class="graybg">队列（Queue）</span></div>
<p>队列（Queue）定义的是<span style="background-color: #c0c0c0;">先进先出（First In First Out, FIFO）</span>的访问约束。进入得早的元素先被处理，后来进入的元素排在尾部等待。它适合表达“任务排队、波前扩张、按到达顺序消费”的过程。</p>
<p>广度优先搜索（Breadth-First Search, BFS）之所以使用队列，正是因为 BFS 要按距离层层扩展：先处理距离起点为 1 的节点，再处理距离为 2 的节点。这个“分层推进”机制与 FIFO 完全一致。</p>
<p>循环队列（Circular Queue）通过把底层数组首尾相连，可以避免频繁搬移；双端队列（Deque）则允许两端都做插入和删除，因此能够支持滑动窗口最值、0-1 BFS 等更复杂的算法模式。</p>
<div class="blog_h3"><span class="graybg">哈希表（Hash Table）</span></div>
<p>哈希表（Hash Table）处理的核心问题是：当数据按“键（Key）”组织，而不是按位置组织时，如何快速找到对应的值（Value）。其思想是先通过哈希函数（Hash Function）把键映射成一个整数，再把这个整数映射到桶（Bucket）或槽位（Slot）上。</p>
<p>若哈希函数分布均匀，且装载因子（Load Factor）控制合理，则查找、插入和删除的平均复杂度都可接近 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>。这正是词表映射、去重、缓存索引、参数名字典和特征 ID 映射大量采用哈希表的原因。</p>
<p>哈希表的难点在冲突（Collision）处理。多个键可能映射到同一位置，常见解决方案包括链地址法（Separate Chaining）和开放定址法（Open Addressing）。因此“哈希表平均 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span>”并不意味着永远常数时间，它依赖于哈希函数质量、负载控制和冲突处理策略。</p>
<div class="blog_h4"><span class="graybg">Bloom Filter（布隆过滤器）</span></div>
<p>Bloom Filter 本质上属于<span style="background-color: #c0c0c0;">概率型数据结构（Probabilistic Data Structure）</span>，更准确地说，是一种近似集合成员查询结构（Approximate Membership Query, AMQ）。它解决的问题并不是“把元素精确存下来”，而是“用极小内存快速判断某元素是否可能出现过”。因此它通常作为哈希表、数据库索引或缓存系统之前的一层预过滤结构。</p>
<p>Bloom Filter 由一个长度为 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 的比特数组 <span displaypfx="inline-" class="mathjax-container">\(B\in\{0,1\}^m\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个哈希函数 <span displaypfx="inline-" class="mathjax-container">\(h_1,\dots,h_k\)</span> 构成，其中每个哈希函数都把元素 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 映射到区间 <span displaypfx="inline-" class="mathjax-container">\(\{0,1,\dots,m-1\}\)</span> 中的一个位置。插入元素 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 时，执行</p>
<span displaypfx="" class="mathjax-container">\[B[h_1(x)]=B[h_2(x)]=\cdots=B[h_k(x)]=1\]</span>
<p>查询元素 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 时，检查 <span displaypfx="inline-" class="mathjax-container">\(B[h_1(x)],\dots,B[h_k(x)]\)</span>。只要其中至少有一个位置为 0，就可以断定 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 一定不在集合中；若这些位置全部为 1，则只能说明 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 可能在集合中。</p>
<p>这一定义直接带来 Bloom Filter 最重要的判定性质：<span style="background-color: #c0c0c0;">它允许假阳性（False Positive），但不允许假阴性（False Negative）</span>。原因在于，不同元素可能把同一批 bit 位置反复置为 1，于是一个从未插入过的元素也可能“碰巧”命中全 1；但只要某个位置仍为 0，就说明没有任何已插入元素覆盖过这条哈希路径，因此该元素一定不存在。</p>
<p>设一共插入了 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 个元素，则某个 bit 在所有插入结束后仍为 0 的概率近似为 <span displaypfx="inline-" class="mathjax-container">\(\left(1-\frac{1}{m}\right)^{kn}\approx e^{-kn/m}\)</span>。于是查询一个未出现元素时， <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个位置恰好都为 1 的假阳性概率近似为</p>
<span displaypfx="" class="mathjax-container">\[p\approx \left(1-e^{-kn/m}\right)^k\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 是 bit 数组长度， <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是已插入元素数， <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 是哈希函数数目， <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是假阳性概率。这个公式揭示了 Bloom Filter 的基本权衡： <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 越大，冲突越少； <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 越大，数组越接近被“染满”； <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 太小会降低区分能力，太大则会过度占满 bit 位。固定 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 时，常见的近似最优选择是</p>
<span displaypfx="" class="mathjax-container">\[k\approx \frac{m}{n}\ln 2\]</span>
<p>直觉上，Bloom Filter 像一排共享的指示灯。每来一个元素，就按亮若干盏灯；查询时，只要对应灯中有一盏没亮，就可以确认它从未出现过。若全部亮着，也只能说明“这些灯曾被某些元素点亮过”，却不能保证就是当前这个元素点亮的。</p>
<p>Bloom Filter 最适合用于“先快速排除绝大多数不存在项，再把少量可疑项交给精确结构复核”的场景。例如缓存系统可先判断某个 key 是否可能在缓存中，若 Bloom Filter 直接给出“不在”，就可以避免无意义回源；LSM-Tree 存储系统可用它判断某个键是否可能存在于某个 SSTable；爬虫去重、黑名单预过滤、向量检索候选预筛都大量使用这一思想。</p>
<p>Bloom Filter 的边界也很明确。第一，它不保存原始元素，因此不能枚举集合内容，也不能像哈希表那样返回关联值。第二，标准 Bloom Filter 不支持安全删除，因为把某个 bit 清零可能误伤其他元素留下的痕迹；若确实需要删除，通常要改用计数 Bloom Filter（Counting Bloom Filter）。第三，当假阳性代价非常高、系统需要完全精确的成员判断时，应优先使用哈希表、B 树或其他精确索引结构。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/bloom.jpg"><img class="alignnone size-full wp-image-41339" src="https://blog.gmem.cc/wp-content/uploads/2026/03/bloom.jpg" alt="bloom" width="1024" height="559" /></a></p>
<div class="blog_h3"><span class="graybg">树（Tree）</span></div>
<p>树（Tree）处理的是“层次结构”和“递归划分”问题。树中的节点之间具有父子关系，除了根节点（Root）外，每个节点都有唯一父节点。它天然适合表达目录层级、决策分裂、区间划分、优先级组织与前缀共享。</p>
<p>树之所以重要，在于它把原本线性的搜索空间组织成递归结构，使很多操作能通过“向左还是向右”“进入哪个子树”逐步缩小问题规模。若每次都能把候选空间缩小到原来的一半，复杂度就会从线性级下降到对数级。</p>
<div class="blog_h4"><span class="graybg">二叉树与遍历</span></div>
<p>二叉树（Binary Tree）规定每个节点至多有两个孩子。前序遍历（Preorder）、中序遍历（Inorder）、后序遍历（Postorder）和层序遍历（Level-order）分别对应不同的信息读取顺序：前序适合序列化结构，中序适合读取二叉搜索树中的有序键，后序适合先处理子问题再合并，层序适合按深度观察整体形状。</p>
<div class="blog_h4"><span class="graybg">二叉搜索树与平衡树</span></div>
<p>二叉搜索树（Binary Search Tree, BST）在每个节点上保持“左子树键值更小、右子树键值更大”的顺序约束，因此查找、插入和删除都可以沿着比较路径进行。若树高度为 <span displaypfx="inline-" class="mathjax-container">\(h\)</span>，这些操作的复杂度一般与 <span displaypfx="inline-" class="mathjax-container">\(O(h)\)</span> 成正比。</p>
<p>问题在于普通 BST 在极端情况下会退化成链表，此时 <span displaypfx="inline-" class="mathjax-container">\(h=n\)</span>。平衡树（Balanced Tree）如 AVL 树、红黑树（Red-Black Tree）通过旋转（Rotation）维护高度受控，使 <span displaypfx="inline-" class="mathjax-container">\(h=O(\log n)\)</span>，从而把查找、插入和删除稳定在对数复杂度。数据库索引和有序映射容器大量依赖这一思想。</p>
<div class="blog_h4"><span class="graybg">堆与优先队列</span></div>
<p>堆（Heap）维护的是局部顺序，而不是整棵树的全局有序性：在最小堆（Min-Heap）中，每个父节点都不大于子节点，因此根节点始终是全局最小值；最大堆（Max-Heap）则相反。它通常用数组实现，父子下标关系可以直接计算。</p>
<p>堆最适合实现优先队列（Priority Queue）：每次都要快速取出当前最重要、最小或最大的元素时，插入和弹出都只需 <span displaypfx="inline-" class="mathjax-container">\(O(\log n)\)</span>。Dijkstra、A* 搜索、任务调度、Top-K 维护和流式中位数都大量依赖优先队列。</p>
<div class="blog_h4"><span class="graybg">Trie 与前缀结构</span></div>
<p>Trie 树（Prefix Tree）把字符串按前缀共享组织起来。若插入单词集合 <span displaypfx="inline-" class="mathjax-container">\(\{w_1,\dots,w_m\}\)</span>，公共前缀只存一次，因此“是否存在某个前缀”“以某前缀开头的词有多少”都可以沿字符路径直接完成。</p>
<p>Trie 特别适合词典匹配、自动补全、敏感词过滤和子词切分。它牺牲了一部分空间，换来按字符长度而非按词典规模进行搜索的能力。</p>
<div class="blog_h3"><span class="graybg">图（Graph）</span></div>
<p>图（Graph）处理的是最一般的关系结构。若顶点集合为 <span displaypfx="inline-" class="mathjax-container">\(V\)</span>，边集合为 <span displaypfx="inline-" class="mathjax-container">\(E\)</span>，则图可写成 <span displaypfx="inline-" class="mathjax-container">\(G=(V,E)\)</span>。树本质上是图的一个特殊子类，但图允许环、允许多条连接、允许方向和权重，因此能表达社交关系、知识链接、网页跳转、道路网络、依赖图与神经网络计算图。</p>
<p>图的常见表示方式有邻接矩阵（Adjacency Matrix）和邻接表（Adjacency List）。前者适合稠密图，能 <span displaypfx="inline-" class="mathjax-container">\(O(1)\)</span> 判断两点是否相连；后者适合稀疏图，空间复杂度更低，遍历邻居更高效。</p>
<div class="blog_h4"><span class="graybg">BFS 与 DFS</span></div>
<p>广度优先搜索（BFS）与深度优先搜索（DFS）是图遍历的两种基本组织方式。BFS 使用队列按层推进，适合无权最短路、层次扩展与最少步数问题；DFS 使用递归或显式栈沿一条路径尽量走深，适合回溯、环检测、拓扑排序、强连通分量与树形动态规划。</p>
<p>对邻接表表示的图，两者的时间复杂度通常都是 <span displaypfx="inline-" class="mathjax-container">\(O(|V|+|E|)\)</span>。区别不在渐近复杂度，而在访问顺序：BFS 保证按距离层层扩展，DFS 更擅长描述“先深入、后回退”的结构性问题。</p>
<div class="blog_h4"><span class="graybg">最短路径</span></div>
<p>最短路径（Shortest Path）问题处理的是：从起点到终点，总代价最小的路径是什么。若图无权，BFS 就能得到边数最少的路径；若边权非负，常用 Dijkstra 算法。它每次从优先队列中取出当前距离估计最小的顶点，并尝试松弛（Relax）相邻边。</p>
<p>Dijkstra 的核心更新为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}[v]=\min\big(\mathrm{dist}[v],\mathrm{dist}[u]+w(u,v)\big)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{dist}[u]\)</span> 是当前已知的从源点到 <span displaypfx="inline-" class="mathjax-container">\(u\)</span> 的最短距离估计， <span displaypfx="inline-" class="mathjax-container">\(w(u,v)\)</span> 是边权。这个公式表达的是最短路的本质：若“先到 <span displaypfx="inline-" class="mathjax-container">\(u\)</span>，再走到 <span displaypfx="inline-" class="mathjax-container">\(v\)</span>”更便宜，就更新对 <span displaypfx="inline-" class="mathjax-container">\(v\)</span> 的距离认知。</p>
<div class="blog_h4"><span class="graybg">拓扑排序</span></div>
<p>拓扑排序（Topological Sort）处理的是有向无环图（Directed Acyclic Graph, DAG）中的依赖顺序。若边 <span displaypfx="inline-" class="mathjax-container">\(u\to v\)</span> 表示“<span displaypfx="inline-" class="mathjax-container">\(u\)</span> 必须先于 <span displaypfx="inline-" class="mathjax-container">\(v\)</span>”，那么拓扑序就是一种满足全部先后约束的线性排列。</p>
<p>课程先修关系、编译依赖、工作流调度、神经网络计算图执行次序，本质上都属于这一问题。拓扑排序的价值不只是“排出一个顺序”，而是把依赖图转成一条能够实际执行的流水线。</p>
<div class="blog_h4"><span class="graybg">最小生成树（Minimum Spanning Tree, MST）</span></div>
<p>最小生成树（Minimum Spanning Tree, MST）处理的是这样的问题：给定一个连通无向带权图 <span displaypfx="inline-" class="mathjax-container">\(G=(V,E)\)</span>，需要从边集合 <span displaypfx="inline-" class="mathjax-container">\(E\)</span> 中选出一部分边，把所有顶点连成一个整体，同时不产生环，并使总权重最小。若把所有生成树的集合记为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{T}(G)\)</span>，则标准形式可以写成</p>
<span displaypfx="" class="mathjax-container">\[T^*=\arg\min_{T\in \mathcal{T}(G)}\sum_{e\in T} w(e)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(T^*\)</span> 是最优生成树；<span displaypfx="inline-" class="mathjax-container">\(w(e)\)</span> 是边 <span displaypfx="inline-" class="mathjax-container">\(e\)</span> 的权重；<span displaypfx="inline-" class="mathjax-container">\(\sum_{e\in T} w(e)\)</span> 表示树中全部边的总代价。这里的“生成树”有三个同时成立的约束：第一， <span displaypfx="inline-" class="mathjax-container">\(T\subseteq E\)</span>；第二，图在边集 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 下必须连通；第三， <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 不能含环，因此边数必然满足 <span displaypfx="inline-" class="mathjax-container">\(|T|=|V|-1\)</span>。</p>
<p>这个定义明确了 MST 不是“找一棵看上去便宜的树”，而是在<span style="background-color: #c0c0c0;">所有能够覆盖全部顶点的无环连通方案</span>中做全局最小化。只强调“连通”会多出冗余边，只强调“边权小”又可能导致图不连通；MST 同时满足这两个条件。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/mst-graph.png"><img class="alignnone size-full wp-image-41335" src="https://blog.gmem.cc/wp-content/uploads/2026/03/mst-graph.png" alt="mst-graph" width="6242" height="2905" /></a></p>
<p>直觉上，可以把 MST 理解成“以最低总造价把一组城市接通，但不修多余的回路”。如果形成了环，说明这条网络中至少有一条边是重复支出；如果某些城市没有接入，说明方案根本不可用。MST 就是在“全覆盖”和“最低成本”之间取得最紧的平衡。</p>
<p>MST 成立的核心理论基础是<span style="background-color: #c0c0c0;">割性质（Cut Property）</span>：把顶点集切成两个不相交部分后，跨越这个切分的最小权边，一定存在于某棵最小生成树中。这个性质的含义是：局部最便宜的“安全边（Safe Edge）”可以被逐步加入，而不会破坏全局最优性。Prim 与 Kruskal 虽然组织方式不同，但本质上都在不断选择这样的安全边。</p>
<p>Prim 算法的思路是“从一个起点向外生长一棵树”。设当前已经纳入树中的顶点集合为 <span displaypfx="inline-" class="mathjax-container">\(S\)</span>，则 Prim 每一步都在所有满足 <span displaypfx="inline-" class="mathjax-container">\(u\in S,\ v\notin S\)</span> 的边中，选择权重最小的一条，把新顶点接入当前树。这个过程像不断把新城市接入已经建好的主干网，因此特别适合用优先队列维护“当前边界上最便宜的边”。若图用邻接表存储并配合二叉堆实现优先队列，时间复杂度通常为 <span displaypfx="inline-" class="mathjax-container">\(O(|E|\log |V|)\)</span>。</p>
<p>Kruskal 算法的思路是“按全图范围从便宜到昂贵依次选边”。它先对所有边按权重升序排序，然后从小到大扫描：若当前边连接的是两个不同连通块，就把它加入结果；若会在当前结构中形成环，就跳过。为了高效判断“两个端点是否已经连通”，Kruskal 通常配合并查集（Disjoint Set Union, DSU）。排序代价主导总复杂度，因此复杂度通常写成 <span displaypfx="inline-" class="mathjax-container">\(O(|E|\log |E|)\)</span>，与 <span displaypfx="inline-" class="mathjax-container">\(O(|E|\log |V|)\)</span> 在数量级上接近。</p>
<p>两种算法解决的是同一个优化问题，但适合的工程语境不同。Prim 更像“从局部网络不断扩张”，适合稠密图或从某个核心节点逐步向外建设的场景；Kruskal 更像“全局看所有候选边，再逐一合并连通块”，在边集天然可排序、图较稀疏时实现尤其直接。</p>
<p>一个最小例子可以把公式和过程连起来。设四个顶点 <span displaypfx="inline-" class="mathjax-container">\(A,B,C,D\)</span>，边权为： <span displaypfx="inline-" class="mathjax-container">\(w(A,B)=1\)</span>， <span displaypfx="inline-" class="mathjax-container">\(w(B,C)=2\)</span>， <span displaypfx="inline-" class="mathjax-container">\(w(A,C)=4\)</span>， <span displaypfx="inline-" class="mathjax-container">\(w(B,D)=3\)</span>， <span displaypfx="inline-" class="mathjax-container">\(w(C,D)=5\)</span>。Kruskal 会先按边权排序： <span displaypfx="inline-" class="mathjax-container">\((A,B),(B,C),(B,D),(A,C),(C,D)\)</span>。前 3 条边分别把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(C\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 连起来，此时已经得到 <span displaypfx="inline-" class="mathjax-container">\(|V|-1=3\)</span> 条边，且图连通无环，于是生成树为</p>
<span displaypfx="" class="mathjax-container">\[T=\{(A,B),(B,C),(B,D)\}\]</span>
<p>其总代价为</p>
<span displaypfx="" class="mathjax-container">\[\sum_{e\in T}w(e)=1+2+3=6\]</span>
<p>若改选边集 <span displaypfx="inline-" class="mathjax-container">\(\{(A,B),(A,C),(B,D)\}\)</span>，总代价是 <span displaypfx="inline-" class="mathjax-container">\(1+4+3=8\)</span>；若再加入 <span displaypfx="inline-" class="mathjax-container">\((B,C)\)</span>，虽然成本局部看不高，但边数会超过 <span displaypfx="inline-" class="mathjax-container">\(|V|-1\)</span> 并形成环，因此不再是树。这个例子把“最低成本”“连通”“无环”三项约束如何同时生效展示得很清楚。</p>
<p>MST 常见于网络布线、电力传输、骨架路网设计、图像分割、聚类和图压缩。层次聚类中的单链接（Single Linkage）就可以通过图的最小生成树来理解：先把点看成顶点，把样本间距离看成边权，再在 MST 上剪断最长的若干条边，就得到若干连通簇。这也是为什么 MST 不只是图论题型，而是很多数据分析与机器学习方法的底层结构。</p>
<p>MST 也有明确边界。它只适用于<span style="background-color: #c0c0c0;">无向、连通、带权</span>图上的“全连通最低总成本”问题；若任务要求的是“从源点到其余点的最短路”，应使用最短路径算法；若图有方向，目标就不再是普通 MST，而会进入最小树形图（Minimum Arborescence）等更复杂的问题。</p>
<div class="blog_h2"><span class="graybg">动态规划（Dynamic Programming, DP）</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>动态规划（Dynamic Programming, DP）处理的是这样一类问题：目标是求一个全局最优值、最优路径，或所有路径的总和，但如果直接把所有可能性全部枚举出来，计算量会迅速爆炸。它常见于序列决策、路径规划、字符串匹配、图搜索，以及隐马尔可夫模型（HMM）、条件随机场（CRF）这类结构化预测模型。</p>
<p>这类问题通常有两个共同特征。第一，<span style="background-color: #c0c0c0;">重叠子问题（Overlapping Subproblems）</span>：同一个中间子问题会被反复计算。第二，<span style="background-color: #c0c0c0;">最优子结构（Optimal Substructure）</span>：大问题的最优解可以由小问题的最优解递推得到。例如，在长度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 的序列上，若每一步有 <span displaypfx="inline-" class="mathjax-container">\(|\mathcal{S}|\)</span> 个可能状态，直接枚举所有状态路径往往需要考虑 <span displaypfx="inline-" class="mathjax-container">\(|\mathcal{S}|^T\)</span> 条候选路径；当 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 稍大时，这种暴力方法几乎不可用。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>动态规划的核心在于：<span style="background-color: #c0c0c0;">先定义能够代表子问题的状态，再写出状态之间的递推关系，并把已经算过的结果缓存下来复用</span>。因此，它本质上是一种计算组织方式，而不是某个固定公式。</p>
<p>一个直观比喻是出差换乘。设想要从起点出发，经过很多站点，最终到达目的地。暴力法会把“到达每一站的所有走法”全部记下来；动态规划不会这样做。它只会为每个中间站保留一份最有价值的摘要，例如“到达这个站的最低成本”或“到达这个站的最大得分”。当继续前往下一站时，系统只需要查这份账本，而不必回头展开所有历史路径。</p>
<p>因此，动态规划通常包含四个步骤：定义状态、定义边界条件、写出转移方程、确定计算顺序。状态定义决定“中间结果要存什么”；边界条件决定“第一步从哪里开始”；转移方程决定“当前结果如何从更小问题得到”；计算顺序则保证所有依赖项在使用前已经计算完毕。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/dp.jpg"><img class="alignnone size-full wp-image-41269" src="https://blog.gmem.cc/wp-content/uploads/2026/03/dp.jpg" alt="dp" width="1408" height="768" /></a></p>
<div class="blog_h3"><span class="graybg">为什么局部次优前缀不会漏掉全局最优</span></div>
<p>动态规划能够丢弃大量“暂时看起来不够好”的前缀路径，前提是<span style="background-color: #c0c0c0;">状态（State）已经完整刻画了未来决策所需的全部信息</span>。一旦这个条件成立，到达同一状态的两条前缀路径，未来能够接上的可行后缀集合完全相同，因此只需要保留其中更优的那一条。</p>
<p>设两条前缀路径都到达同一状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span>，其当前累计代价分别为 <span displaypfx="inline-" class="mathjax-container">\(f_1(s)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(f_2(s)\)</span>，且 <span displaypfx="inline-" class="mathjax-container">\(f_1(s)\le f_2(s)\)</span>。若从状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 出发，后续任意可行决策产生的附加代价记为 <span displaypfx="inline-" class="mathjax-container">\(g(s)\)</span>，并且这个附加代价只由当前状态决定，而不再依赖此前的完整历史，则有</p>
<span displaypfx="" class="mathjax-container">\[f_1(s)+g(s)\le f_2(s)+g(s)\]</span>
<p>这个不等式表明：在同一状态上，较差的前缀会被较优前缀完全支配（Dominated）。无论后面接哪一段后缀路径，较差前缀都不可能反超。因此 Bellman 最优性原理允许动态规划只保留“到达该状态的最优值”，而不必保留全部历史路径。</p>
<p>所谓“一个当前次优的路径，后来却通向全局最优”，本质上对应另一种情形：这条路径与当前更优路径虽然看起来到达了同一个位置，但它们对未来的可行动作并不相同。此时它们实际上并不属于同一个状态，而是状态定义缺失了关键信息。</p>
<p>一个典型例子是带资源约束的路径规划。若状态只写成当前位置 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span>，那么两条到达同一格子的路径会被合并；但若其中一条还保留一次传送机会，另一条已经把传送用掉，则它们未来的决策空间显然不同。正确的状态应扩展为 <span displaypfx="inline-" class="mathjax-container">\((i,j,\mathrm{used})\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\((i,j,\mathrm{fuel})\)</span> 这类更完整的形式。只有在状态把“剩余资源、上一步动作、已使用预算、是否持仓”等会影响未来的因素都编码进去后，动态规划的剪枝才是安全的。</p>
<p>因此，动态规划处理“局部次优可能导向全局最优”的方式，不是保留所有看起来有潜力的路径，而是通过<span style="background-color: #c0c0c0;">正确设计状态，使真正会影响未来的差异体现在不同状态上</span>。同一状态内部只保留最优前缀；不同状态之间分别递推。动态规划的正确性，最终依赖的正是这一点：未来只依赖当前状态，而不依赖通向当前状态的完整历史。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>若记 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 为阶段或时间步， <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 为当前状态，一个非常典型的动态规划写法是：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{DP}[t,s]=\max_{s'\in \mathrm{Prev}(s)}\left(\mathrm{DP}[t-1,s']+\mathrm{score}(s',s,t)\right)\]</span>
<p>这条式子表达的是：要得到“第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步处于状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 时的最优值”，不必重新枚举所有完整路径，而是只需查看所有能够转移到 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 的前驱状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span>，并在它们已有的最优值基础上，加上这一步的局部得分，再从中取最大。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{DP}[t,s]\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步、状态为 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 的最优子问题值。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{Prev}(s)\)</span>：所有可以转移到状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 的前驱状态集合。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{score}(s',s,t)\)</span>：从 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span> 转移到 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 时，在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步新增的局部得分或代价。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\max\)</span>：表示当前任务要找“最好的一条路径”。若任务目标是最小代价，则可改为 <span displaypfx="inline-" class="mathjax-container">\(\min\)</span>；若任务目标是把所有路径概率加总，则可改为 <span displaypfx="inline-" class="mathjax-container">\(\sum\)</span>。</li>
</ul>
<p>边界条件通常写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{DP}[1,s]=\mathrm{init}(s)+\mathrm{local}(s,1)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{init}(s)\)</span> 表示序列从状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 开始的初始代价或初始分数， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{local}(s,1)\)</span> 表示第一步在该状态产生的局部贡献。没有这个起点，后续递推就无从展开。</p>
<p>动态规划真正带来的收益来自复杂度压缩。以一阶序列模型为例，若每一步有 <span displaypfx="inline-" class="mathjax-container">\(|\mathcal{S}|\)</span> 个候选状态、总长度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>，暴力枚举往往需要考虑 <span displaypfx="inline-" class="mathjax-container">\(|\mathcal{S}|^T\)</span> 条完整路径；而若采用“时间步 + 当前状态”的动态规划状态定义，则通常只需在每个时间步枚举所有前驱状态，计算复杂度可以降为 <span displaypfx="inline-" class="mathjax-container">\(O(T|\mathcal{S}|^2)\)</span>。这种从指数级到多项式级的下降，正是动态规划在序列模型中不可替代的原因。</p>
<p>若任务不仅要求最优值，还要求恢复最优路径，则通常还会额外保存“当前最优值来自哪个前驱状态”的回溯信息（Backpointer）。这意味着动态规划不仅能回答“最优值是多少”，还能回答“这条最优路径具体怎么走”。</p>
<p>更重要的是，动态规划并不只对应一种运算。对于最优路径问题，递推中的核心运算往往是 <span displaypfx="inline-" class="mathjax-container">\(\max\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\min\)</span>；对于总概率、配分函数这类问题，核心运算则是 <span displaypfx="inline-" class="mathjax-container">\(\sum\)</span>。这也是为什么 HMM 的维特比算法、前向算法，以及 CRF 的前向后向算法，虽然目标不同，但都属于动态规划。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>在 HMM 中，动态规划最典型地体现在两类问题上。第一类是维特比算法（Viterbi Algorithm）：它要求“给定观测序列后，哪一条隐藏状态路径最可能”，因此递推中的核心运算是 <span displaypfx="inline-" class="mathjax-container">\(\max\)</span>。第二类是前向算法（Forward Algorithm）：它要求“所有隐藏状态路径合起来，总概率是多少”，因此递推中的核心运算是 <span displaypfx="inline-" class="mathjax-container">\(\sum\)</span>。两者使用的是同一张状态网格，只是“每一步如何聚合前驱信息”不同。</p>
<p>在 CRF 中，动态规划同样是核心计算工具。训练时，需要对所有可能标签路径做归一化，这对应配分函数（Partition Function）的计算；解码时，需要找得分最高的那条标签路径，这对应最优路径搜索。在线性链 CRF 中，这两件事都可以通过“时间步 + 当前标签”的动态规划状态来高效完成，否则若直接枚举所有标签序列，计算量会随序列长度呈指数增长。</p>
<p>因此，在机器学习语境里，动态规划可以概括为：<span style="background-color: #c0c0c0;">把原本必须整体枚举的结构化问题，改写成一系列局部状态上的递推计算，并通过缓存中间结果把重复计算消掉</span>。一旦看到“序列路径很多、局部决策可递推、同类子问题会重复出现”这三个信号，通常就应该优先考虑动态规划。</p>
<div class="blog_h4"><span class="graybg">例子 1：编辑距离（Edit Distance）</span></div>
<p>设字符串 <span displaypfx="inline-" class="mathjax-container">\(A=a_1a_2\dots a_m\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(B=b_1b_2\dots b_n\)</span>。编辑距离要回答的问题是：至少经过多少次插入、删除、替换，才能把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 变成 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>。若直接枚举所有编辑序列，可能性会指数增长；但若定义 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{DP}[i,j]\)</span> 表示“把 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 的前 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个字符变成 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 的前 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个字符所需的最小编辑次数”，问题就能递推解决。</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{DP}[i,j]=\min\begin{cases}\mathrm{DP}[i-1,j]+1\\ \mathrm{DP}[i,j-1]+1\\ \mathrm{DP}[i-1,j-1]+\mathbf{1}(a_i\ne b_j)\end{cases}\]</span>
<p>这里三项分别对应：删除 <span displaypfx="inline-" class="mathjax-container">\(a_i\)</span>、插入 <span displaypfx="inline-" class="mathjax-container">\(b_j\)</span>、或把 <span displaypfx="inline-" class="mathjax-container">\(a_i\)</span> 替换成 <span displaypfx="inline-" class="mathjax-container">\(b_j\)</span>（若本来相同，则替换代价为 0）。边界条件是 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{DP}[0,j]=j\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathrm{DP}[i,0]=i\)</span>，因为空串变成长度为 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的串需要做 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 次插入，反之需要做 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 次删除。这个例子非常典型地体现了动态规划：状态是前缀长度，转移是三种编辑操作，目标是求最小总代价。</p>
<div class="blog_h4"><span class="graybg">例子 2：网格最短路径（Grid Shortest Path）</span></div>
<p>设一个 <span displaypfx="inline-" class="mathjax-container">\(m\times n\)</span> 网格，每个格子 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 都有进入代价 <span displaypfx="inline-" class="mathjax-container">\(w_{i,j}\)</span>，只能向右或向下移动。问题是：从左上角走到右下角的最小总代价是多少。若定义 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{DP}[i,j]\)</span> 表示“到达格子 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 的最小总代价”，则递推很直接：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{DP}[i,j]=w_{i,j}+\min\big(\mathrm{DP}[i-1,j],\mathrm{DP}[i,j-1]\big)\]</span>
<p>因为到达 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 只有两种可能：从上方 <span displaypfx="inline-" class="mathjax-container">\((i-1,j)\)</span> 走下来，或从左侧 <span displaypfx="inline-" class="mathjax-container">\((i,j-1)\)</span> 走过来。边界条件是第一行与第一列只能沿单一路径累计。这个例子说明，动态规划并不局限于字符串或序列模型；只要问题具有“局部来源有限、全局目标可递推”的结构，就可以用同样的思路求解。</p>
<div class="blog_h2"><span class="graybg">贪心算法（Greedy Algorithm）</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>贪心算法（Greedy Algorithm）处理的是这样一类问题：希望快速构造一个全局可行解，并且每一步都只做当前看来最优的局部选择，而不回头修改已经作出的决定。它广泛出现在排序、调度、压缩、近似优化，以及许多机器学习训练与推断流程中。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>贪心的核心假设是：<span style="background-color: #c0c0c0;">当前最好的局部选择，能够导向全局最优，或至少导向足够好的近似解</span>。它像走山路时每一步都先选眼前最高、最稳的落脚点，而不是先把整座山的所有路径都完全规划出来。贪心的优势是快、简单、容易实现；风险是局部最优未必等于全局最优。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>若记第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步可选动作集合为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{A}_t\)</span>，一个抽象的贪心选择可写为：</p>
<span displaypfx="" class="mathjax-container">\[a_t^*=\arg\max_{a\in\mathcal{A}_t}\ \mathrm{score}(a\mid \text{current state})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{score}(a\mid \text{current state})\)</span> 是当前状态下动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 的局部收益；<span displaypfx="inline-" class="mathjax-container">\(\arg\max\)</span> 表示从所有可选动作里挑出得分最高的那个。贪心算法关心的是“眼下哪一步最好”，而不是“未来所有步骤联合起来后哪条完整路径最好”。</p>
<p>因此，贪心方法是否正确，取决于问题本身是否满足贪心选择性质（Greedy-choice Property）。如果这个性质成立，局部最优就能拼成全局最优；如果不成立，贪心通常只能作为启发式方法或近似算法。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>决策树训练就是一个典型例子。每个节点都不会提前规划整棵树的全局最优结构，而是只在当前节点上选择信息增益、基尼下降或误差下降最大的切分。这个过程本质上就是贪心：<span style="background-color: #c0c0c0;">每一步都先把当前最值得切的地方切开</span>。它训练快、解释性强，但也正因为是局部选择，单棵树通常不是全局最优树结构。</p>
<div class="blog_h2"><span class="graybg">分治算法（Divide and Conquer）</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>分治算法（Divide and Conquer）处理的是“大问题可以被拆成若干个同结构小问题”的场景。它广泛出现在排序、搜索、矩阵运算、索引构建，以及大规模数据处理与并行计算中。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>分治的思想可以概括为三步：<span style="background-color: #c0c0c0;">分解（Divide）— 递归求解（Conquer）— 合并（Combine）</span>。它像整理一大堆文档时，先按主题拆成若干小堆，再分别处理，最后再合并成有序结果。与动态规划不同，分治更强调“子问题相互独立”，而不是“子问题结果需要反复复用”。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>分治算法的时间复杂度常写成递推式：</p>
<span displaypfx="" class="mathjax-container">\[T(n)=aT\left(\frac{n}{b}\right)+f(n)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是问题规模；<span displaypfx="inline-" class="mathjax-container">\(a\)</span> 表示被拆成多少个子问题；<span displaypfx="inline-" class="mathjax-container">\(n/b\)</span> 是每个子问题的规模；<span displaypfx="inline-" class="mathjax-container">\(f(n)\)</span> 是“分解 + 合并”本身的额外代价。这个式子不告诉我们具体怎么做，但它准确描述了分治算法的结构骨架。</p>
<p>例如，归并排序（Merge Sort）把长度为 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 的数组分成两个规模约为 <span displaypfx="inline-" class="mathjax-container">\(n/2\)</span> 的子数组，递归排好序后再线性合并，因此它的复杂度递推就是 <span displaypfx="inline-" class="mathjax-container">\(T(n)=2T(n/2)+O(n)\)</span>。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>在机器学习工程中，分治思想常见于大规模近邻索引构建。例如构建 kd-tree 时，算法会按某个维度把样本集递归切成两半，再在左右子集上继续构树。这样得到的层次化空间划分，能显著加速后续的近邻搜索。它的本质并不是“学习一个模型”，而是通过递归拆分把原本需要全表扫描的搜索过程组织得更高效。</p>
<div class="blog_h2"><span class="graybg">图搜索与最短路径</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>许多软件与机器学习问题都可以抽象成图（Graph）：节点（Node）表示状态、样本、词、网页或知识实体，边（Edge）表示转移、相似性、依赖关系或可达关系。图搜索与最短路径算法要回答的问题是：如何从起点高效找到目标节点，或找到总代价最小的一条路径。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>图搜索的核心是“沿着边扩展状态空间，但尽量避免无意义的重复探索”。无权图最短路径常用广度优先搜索（BFS），因为它按层扩展，第一次到达目标通常就是步数最少的路径；带非负权图常用 Dijkstra，因为它总是优先扩展当前总代价最小的候选节点。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>带权最短路径算法里的基本更新步骤通常写成“松弛（Relaxation）”：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}(v)=\min\big(\mathrm{dist}(v),\ \mathrm{dist}(u)+w(u,v)\big)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{dist}(u)\)</span> 是当前已知从起点到节点 <span displaypfx="inline-" class="mathjax-container">\(u\)</span> 的最小代价， <span displaypfx="inline-" class="mathjax-container">\(w(u,v)\)</span> 是边 <span displaypfx="inline-" class="mathjax-container">\(u\rightarrow v\)</span> 的权重， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{dist}(u)+w(u,v)\)</span> 则是“先到 <span displaypfx="inline-" class="mathjax-container">\(u\)</span> 再走到 <span displaypfx="inline-" class="mathjax-container">\(v\)</span>”这条新候选路径的总代价。若它比当前记录的 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{dist}(v)\)</span> 更小，就更新。</p>
<p>这个式子看起来和动态规划很像，原因是二者都在做“由已知子结果递推新结果”。区别在于：图搜索更强调如何选择下一个要扩展的节点，以及如何在一般图结构中避免重复访问。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>在语音识别、机器翻译和图搜索推断中，解码过程经常会把候选状态组织成图或格（Lattice）。此时，寻找最优输出序列本质上就是图上的路径搜索问题。很多动态规划解码器也可以从“图上最优路径”的角度理解，因此图搜索是连接通用软件算法与结构化机器学习推断的重要桥梁。</p>
<div class="blog_h2"><span class="graybg">二分查找（Binary Search）</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>二分查找（Binary Search）处理的是“搜索空间有序，或可行性判断具有单调性”的问题。它不仅用于有序数组查找，也广泛用于阈值搜索、参数调优、数值逼近和工程系统中的边界定位。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>二分查找的核心是：<span style="background-color: #c0c0c0;">每次利用单调性砍掉一半搜索空间</span>。它像猜数字游戏：如果知道答案一定在某个区间里，而且中点左侧和右侧满足不同性质，那么每问一次都能把候选范围减半。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>若当前搜索区间为 <span displaypfx="inline-" class="mathjax-container">\([l,r]\)</span>，中点通常取：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{mid}=\left\lfloor\frac{l+r}{2}\right\rfloor\]</span>
<p>接着依据单调判定函数 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{check}(\mathrm{mid})\)</span> 缩小区间：</p>
<ul>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{check}(\mathrm{mid})\)</span> 为真，说明答案在左半边或恰好是中点，则令 <span displaypfx="inline-" class="mathjax-container">\(r=\mathrm{mid}\)</span>。</li>
<li>若为假，说明答案在右半边，则令 <span displaypfx="inline-" class="mathjax-container">\(l=\mathrm{mid}+1\)</span>。</li>
</ul>
<p>算法正确性的关键不在于公式本身，而在于维护区间不变式（Invariant）：在每一步更新后，真正的答案仍然留在当前区间中。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>在机器学习里，二分查找常用于阈值定位。例如，当需要找到“使召回率至少达到某个目标值的最小分类阈值”时，只要阈值越大召回率越低这一单调关系成立，就可以在阈值区间上做二分查找，而不必逐点穷举。类似地，很多数值求根、超参数边界搜索、分位数定位问题也都可写成二分框架。</p>
<div class="blog_h2"><span class="graybg">随机采样（Random Sampling）</span></div>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>随机采样（Random Sampling）处理的是这样一类问题：总体太大、精确计算太贵，或者目标本身就是概率性的，因此只能通过抽样近似整体行为。它是统计学习、蒙特卡洛估计、bootstrap、自助重采样、mini-batch 训练和负采样的共同基础。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>随机采样的核心是：<span style="background-color: #c0c0c0;">不必每次都看完整总体，而是通过足够有代表性的随机子样本估计总体性质</span>。它像民意调查：不可能每天逐个询问所有人，但若抽样方式合理，少量样本也能给出相对稳定的总体估计。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>若目标是估计随机变量 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 下某个函数 <span displaypfx="inline-" class="mathjax-container">\(f(X)\)</span> 的期望 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[f(X)]\)</span>，最常见的蒙特卡洛估计写为：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\mu}=\frac{1}{n}\sum_{i=1}^{n} f(x_i),\qquad x_i\sim p(x)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(x_i\sim p(x)\)</span> 表示样本 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 是按分布 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span> 随机抽到的； <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是样本数； <span displaypfx="inline-" class="mathjax-container">\(\hat{\mu}\)</span> 是用样本均值近似真实期望的估计量。样本越多，估计通常越稳定，但代价也越高。</p>
<p>这一思想在机器学习里非常普遍：SGD 不是每次都在全量数据上算梯度，而是用 mini-batch 的样本均值近似全数据梯度；negative sampling 不是对所有负类都求和，而是随机抽一小部分负样本近似完整目标。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>bootstrap 是一个很典型的例子。随机森林训练时，会对原始训练集做有放回采样，得到多份不同的 bootstrap 子集，再分别训练多棵树。这里真正起作用的不是“树”本身，而是随机采样制造了多个略有差异的数据视角，从而让集成后的模型更稳。</p>
<div class="blog_h1"><span class="graybg">机器学习基础概念</span></div>
<p>机器学习基础概念（Machine Learning Foundations）回答四类核心问题：数据从哪里来、模型在学什么、模型为什么能泛化、结果该如何评价。把这些问题分开看，会比死记算法名称更有效：学习范式决定监督信号来自哪里，假设空间与归纳偏置决定模型愿意相信什么，数据集工程决定模型实际看到了什么，模型评估决定这些学习结果是否真的能迁移到未见样本。</p>
<div class="blog_h2"><span class="graybg">假设/目标/代价/损失</span></div>
<p>这四个词描述的是同一条“训练=优化”的概念链，但位于不同层级。把层级理清后，公式与实现会自然对齐：模型 <span displaypfx="inline-" class="mathjax-container">\(f_\theta\)</span> 先给出预测，再用损失函数把预测变成数值误差，最后把误差在数据集上汇总成代价函数，并加入正则/约束得到最终的目标函数。</p>
<div class="blog_h3"><span class="graybg">假设函数（Hypothesis Function）</span></div>
<p>假设函数（Hypothesis Function）也常被直接称为模型（Model）或预测函数（Predictor），记作 <span displaypfx="inline-" class="mathjax-container">\(f_\theta\)</span>。它回答的问题是：<span style="background-color: #c0c0c0;">给定输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，模型输出什么</span>。参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 决定这条映射的具体形状。</p>
<p>线性回归（Linear Regression）的假设函数是最经典的例子：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=f_\theta(x)=\mathbf{w}^\top x+b,\quad \theta=(\mathbf{w},b)\]</span>
<div class="blog_h3"><span class="graybg">目标函数（Objective Function）</span></div>
<p>目标函数（Objective Function）记作 <span displaypfx="inline-" class="mathjax-container">\(J(\theta)\)</span>，是优化器真正要优化的函数。工程上最常见、也最清晰的写法是：<span style="background-color: #c0c0c0;">目标函数 = 代价函数 + 正则化项</span>（没有正则化时可视为正则项为 0，因此 <span displaypfx="inline-" class="mathjax-container">\(J(\theta)=L(\theta)\)</span>）。</p>
<span displaypfx="" class="mathjax-container">\[J(\theta)=L(\theta)+\lambda\,\Omega(\theta)\]</span>
<p>在线性回归里，若用 L2 正则（Ridge / Weight Decay），常见目标函数可以写成：</p>
<span displaypfx="" class="mathjax-container">\[J(\theta)=L(\theta)+\lambda\|\mathbf{w}\|_2^2\]</span>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span> 展开后，就是：</p>
<span displaypfx="" class="mathjax-container">\[J(\theta)=\frac{1}{N}\sum_{i=1}^{N}(\mathbf{w}^\top x_i+b-y_i)^2+\lambda\|\mathbf{w}\|_2^2\]</span>
<div class="blog_h3"><span class="graybg">代价/成本函数（Cost Function）</span></div>
<p>代价函数/成本函数（Cost Function）记作 <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span>，通常指把样本损失在训练集上做平均或求和后的整体量，也就是经验风险（Empirical Risk）。不少教材会把它直接称为训练损失（training loss），并且在不引起歧义时把它与目标函数混用。</p>
<p>在线性回归里，常用“均方误差的平均”作为代价函数：</p>
<span displaypfx="" class="mathjax-container">\[L(\theta)=\frac{1}{N}\sum_{i=1}^{N}\ell_i(\theta)\]</span>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(\ell_i(\theta)\)</span> 取为平方误差后，等价写法是：</p>
<span displaypfx="" class="mathjax-container">\[L(\theta)=\frac{1}{N}\sum_{i=1}^{N}(\mathbf{w}^\top x_i+b-y_i)^2\]</span>
<div class="blog_h3"><span class="graybg">损失函数（Loss Function）</span></div>
<p>损失函数（Loss Function）记作 <span displaypfx="inline-" class="mathjax-container">\(\ell\)</span>，通常定义在单个样本上，把“预测与目标的差距”映射为一个标量。它回答的问题是：<span style="background-color: #c0c0c0;">这一条样本我错了多少</span>。</p>
<p>在线性回归里，最常见的单样本损失是平方误差：</p>
<span displaypfx="" class="mathjax-container">\[\ell_i(\theta)=\ell(\hat y_i,y_i)=(\hat y_i-y_i)^2,\quad \hat y_i=f_\theta(x_i)\]</span>
<div class="blog_h2"><span class="graybg">假设空间、容量与归纳偏置</span></div>
<p>同一份训练数据，之所以会被不同模型学出完全不同的规律，根源在于每个模型都自带一套“允许学什么、不允许学什么”的结构约束。假设空间（Hypothesis Space）、模型容量（Model Capacity）与归纳偏置（Inductive Bias）共同描述的，就是这套约束。</p>
<div class="blog_h3"><span class="graybg">假设空间</span></div>
<p>假设空间（Hypothesis Space）是模型可表达函数的集合，常记为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{H}=\{f_\theta:\theta\in\Theta\}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(f_\theta\)</span> 是由参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 决定的预测函数， <span displaypfx="inline-" class="mathjax-container">\(\Theta\)</span> 是参数可取的范围。这个定义的关键不在于“参数有多少”，而在于<span style="background-color: #c0c0c0;">模型最终允许出现哪些映射形状</span>。例如，一元线性回归的假设空间只包含直线；二次多项式回归的假设空间包含抛物线；深度神经网络的假设空间则更大，能表示复杂得多的非线性函数。</p>
<p>因此，训练并不是在所有可能函数中盲目搜索，而是在某个特定假设空间里找一个最合适的函数。假设空间太小，真实规律可能根本装不进去；假设空间太大，模型又容易把偶然噪声也解释成模式。</p>
<div class="blog_h3"><span class="graybg">模型容量</span></div>
<p>模型容量（Model Capacity）描述的是假设空间的表达能力有多强，也就是模型能拟合多复杂规律。容量高，不代表一定更好；它只表示模型“有能力”表示复杂函数。是否真的学得好，还取决于数据量、正则化、优化过程和任务本身。</p>
<p>容量可以从多个角度理解。参数更多通常意味着容量更高，但这不是唯一标准；树的深度、核方法的核函数形式、特征维度、网络层数、隐藏维度、注意力头数，都会改变容量。工程上常用一个朴素判断：<span style="background-color: #c0c0c0;">如果模型连训练集主要结构都拟合不了，容量偏低；如果训练集几乎完美、验证集却明显变差，容量往往偏高或约束不足</span>。</p>
<p>容量与复杂度控制始终是一组平衡。表格数据上的浅层树模型可能已经足够；图像、语音、自然语言这类高度复杂任务，则通常需要更高容量的模型族。容量本身不是缺点，关键在于它是否与数据规模和任务难度匹配。</p>
<div class="blog_h3"><span class="graybg">归纳偏置</span></div>
<p>归纳偏置（Inductive Bias）是模型在有限样本下从已见数据推广到未见数据时，默认采用的结构性偏好。只靠训练集上有限个点，无法唯一确定整个输入空间上的函数；模型之所以还能做出泛化判断，是因为它隐含地偏好某些解释，而排斥另一些解释。</p>
<p>把学习目标写成经验风险最小化时，这一点会更清楚：</p>
<span displaypfx="" class="mathjax-container">\[\hat f=\arg\min_{f\in\mathcal{H}}\hat R_n(f),\qquad \hat R_n(f)=\frac{1}{n}\sum_{i=1}^{n}\ell(f(x_i),y_i)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\hat R_n(f)\)</span> 是经验风险（Empirical Risk），表示函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在训练集上的平均损失； <span displaypfx="inline-" class="mathjax-container">\(\hat f\)</span> 是最终选出的模型。关键约束正是 <span displaypfx="inline-" class="mathjax-container">\(f\in\mathcal{H}\)</span>：优化并不是在所有可能函数中找最优解，而是在一个被模型结构预先限制过的空间里找解。这个限制本身就是归纳偏置。</p>
<p>归纳偏置的来源很多。线性模型偏好线性关系；KNN（K-Nearest Neighbors）偏好局部相似样本给出相似输出；卷积神经网络（CNN）偏好局部连接与平移等变（Translation Equivariance）；树模型偏好分段常数的轴对齐切分；Transformer 则偏好通过注意力在 token 之间建立可变依赖。正则化、数据增强、参数共享、预训练初始化、优化器的更新轨迹，也都会进一步塑造模型的归纳偏置。</p>
<div class="blog_h2"><span class="graybg">期望风险、经验风险与结构风险</span></div>
<p>在统计学习（Statistical Learning）里，训练目标可以从三个层次来理解：期望风险（Expected Risk）描述模型在真实数据分布上的平均误差；经验风险（Empirical Risk）描述模型在有限训练集上的平均误差；结构风险（Structural Risk）则在经验风险之外，把模型复杂度一并纳入考虑。三者回答的是同一个问题的不同版本：<span style="background-color: #c0c0c0;">模型到底应该怎样才算“学得好”</span>。</p>
<div class="blog_h3"><span class="graybg">期望风险</span></div>
<p>期望风险也常被称为真实风险（True Risk）或总体风险（Population Risk）。若真实数据来自未知分布 <span displaypfx="inline-" class="mathjax-container">\(P(X,Y)\)</span>，模型为 <span displaypfx="inline-" class="mathjax-container">\(f\)</span>，单样本损失为 <span displaypfx="inline-" class="mathjax-container">\(\ell(f(x),y)\)</span>，则期望风险定义为：</p>
<span displaypfx="" class="mathjax-container">\[R(f)=\mathbb{E}_{(x,y)\sim P}\big[\ell(f(x),y)\big]\]</span>
<p>这条式子的含义很直接：把模型放到所有可能出现的真实样本上，计算平均损失。理论上，这才是机器学习真正想最小化的对象，因为泛化能力最终取决于模型在未知数据上的表现，而不是只取决于训练集上的表现。</p>
<p>困难在于，真实分布 <span displaypfx="inline-" class="mathjax-container">\(P(X,Y)\)</span> 并不可见。训练时手里只有有限样本，而没有“全体可能数据”的上帝视角。因此，期望风险通常不能被直接计算，只能被估计。</p>
<div class="blog_h3"><span class="graybg">经验风险</span></div>
<p>经验风险是用训练集对期望风险做出的现实近似。设训练集为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{D}=\{(x_i,y_i)\}_{i=1}^{n}\)</span>，则经验风险定义为：</p>
<span displaypfx="" class="mathjax-container">\[\hat R_n(f)=\frac{1}{n}\sum_{i=1}^{n}\ell\big(f(x_i),y_i\big)\]</span>
<p>它就是模型在当前这批已观测样本上的平均损失。工程上常见的 training loss，本质上反映的正是经验风险，或它的 mini-batch 近似。经验风险最小化（Empirical Risk Minimization, ERM）的训练逻辑也很朴素：既然期望风险不可直接计算，就先把训练集上的平均损失压低。</p>
<p>ERM 的关键前提是：训练集足够代表真实分布。当样本数量增加且采样足够合理时，经验风险通常会更接近期望风险；但在有限样本条件下，两者并不相等。二者之间的差距，本质上就是泛化误差（Generalization Gap）的来源。若模型只是把训练集中的偶然模式、局部噪声和标注误差也一并记住，那么 <span displaypfx="inline-" class="mathjax-container">\(\hat R_n(f)\)</span> 可以很低，而 <span displaypfx="inline-" class="mathjax-container">\(R(f)\)</span> 仍然很高，这正是过拟合（Overfitting）的典型形式。</p>
<div class="blog_h3"><span class="graybg">结构风险</span></div>
<p>结构风险（Structural Risk）是在经验风险之外，再把模型复杂度或假设空间规模纳入考虑的目标。它对应的思想是结构风险最小化（Structural Risk Minimization, SRM）：模型不仅要在训练集上拟合得好，还要避免复杂到足以随意记忆有限样本。</p>
<p>在统计学习理论的严格表述中，SRM 常写成在一族嵌套假设空间之间做选择；在工程实践里，它更常以“经验风险 + 复杂度惩罚”的形式出现，例如：</p>
<span displaypfx="" class="mathjax-container">\[J(f)=\hat R_n(f)+\lambda\,\Omega(f)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\Omega(f)\)</span> 是复杂度项（Complexity Penalty）， <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 控制数据拟合与复杂度约束之间的权衡。L2 正则化（L2 Regularization）、L1 正则化（L1 Regularization）、Weight Decay、早停（Early Stopping）以及对模型深度、宽度和树复杂度的限制，都可以看作结构风险最小化思想的具体实现。</p>
<p>因此，结构风险并不是对经验风险的否定，而是对 ERM 的补充：在有限样本条件下，<span style="background-color: #c0c0c0;">仅仅把训练误差压到最低，并不能保证模型在真实分布上表现最好</span>。模型必须同时控制复杂度，才能让“训练集上学到的规律”更有机会迁移到未见样本。</p>
<div class="blog_h3"><span class="graybg">为什么它重要</span></div>
<p>这三者共同构成了机器学习里最基本的张力。期望风险是理论上真正想优化的目标，但它不可直接见；经验风险是训练时可观测、可优化的替代量；结构风险则提醒我们，有限样本下不能把“训练集表现更好”直接等同于“真实世界表现更好”。换句话说，监督学习不仅是在找一个拟合训练集的函数，更是在有限数据和有限模型约束下，寻找一个最可能泛化的解释。</p>
<p>从这里继续往下，就会自然出现另一个问题：如果高维真实数据本身就带有强结构约束，那么经验风险为何常能在有限样本下逼近期望风险，模型又为何能够泛化到未见样本？流形假设（Manifold Hypothesis）正是对这个问题的一条几何回答：真实数据并不会任意填满整个高维空间，而是集中在某个低维、连续、受约束的结构附近。</p>
<div class="blog_h2"><span class="graybg">流形假设</span></div>
<p>流形假设（Manifold Hypothesis）给出了现代机器学习里一条极其重要的几何直觉：现实世界中有意义的数据，虽然表面上嵌在极高维空间里，但真正有效的变化自由度通常远低于表观维度。也就是说，高维观测往往不是填满整个空间，而是集中分布在一个低维流形（Low-dimensional Manifold）附近。</p>
<p>图像是最容易理解的例子。一个 <span displaypfx="inline-" class="mathjax-container">\(1024\times 1024\)</span> 的 RGB 图像在像素空间中维度极高，但自然图像并不会均匀占据这个巨大空间：物体形状、光照条件、视角变化、相机成像规律与纹理结构都受到强约束。因此，“像真实猫照片”的图像实际上只落在高维像素空间中的极小区域里。文本也类似。一个长度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 的 token 序列组合数极其巨大，但真正同时符合语法、语义和任务约束的文本，只占离散组合空间中很小的一部分。模型之所以能够泛化，一个重要原因正是：它并不需要学会覆盖整个高维空间，而只需要学会沿着这些低维结构建模。</p>
<div class="blog_h3"><span class="graybg">什么是流形</span></div>
<p>流形（Manifold）首先是一个几何对象。它的关键性质不是“弯不弯”，而是<span style="background-color: #c0c0c0;">局部上看起来像普通的低维欧几里得空间，整体上却可以弯曲、卷曲并嵌入到更高维空间中</span>。更正式地说，若一个集合对其上每一点，都存在一个足够小的邻域，可以用 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^d\)</span> 中的局部坐标平滑描述，那么这个集合就可以看作一个 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维流形。</p>
<p>地球表面是最直观的例子。站在操场或街道上时，局部地面几乎是平的，可以用二维坐标定位；但从整体看，地球表面显然不是二维平面，而是嵌入三维空间中的弯曲曲面。因此，地球表面就是一个嵌在三维空间里的二维流形。对只能沿地面运动的观察者而言，真正相关的不是三维空间全部坐标，而是地表上那两个局部自由度。</p>
<p>机器学习教材里常见的瑞士卷（Swiss Roll）把这个概念进一步可视化。可以先想象一张二维纸，再把它卷进三维空间。卷起来之后，样本点在外部看来落在三维空间里，但沿着纸面定位某一点时，真正需要的仍然只是二维坐标。外在维度变高了，内在结构却没有变。这正是流形概念在机器学习里最重要的几何直觉：<span style="background-color: #c0c0c0;">数据的表观维度可以很高，但它的内在维度却可能很低</span>。</p>
<p>这类卷曲结构还带来另一个机器学习里非常关键的后果：<span style="background-color: #c0c0c0;">嵌入空间中的欧氏距离（Euclidean Distance）与流形上的测地距离（Geodesic Distance）并不一定一致</span>。两点在外部空间里看起来可能很近，因为一条直线可以直接“穿过空气”连接它们；但若真实数据只能沿流形本身变化，那么真正相关的距离应当是沿曲面或曲线走过去的那条路径长度。流形学习（Manifold Learning）之所以强调邻域图、测地近似和局部结构，正是因为外部直线距离经常不能反映数据在内在结构上的真实远近关系。</p>
<p>放到高维数据上，这个判断尤其关键。一张 <span displaypfx="inline-" class="mathjax-container">\(1000\times1000\)</span> 的灰度图像在像素空间里有一百万维，但“真实人脸图像”显然不会填满整个一百万维空间。姿态、光照、表情、年龄、拍摄距离等因素彼此耦合，使真实样本只落在高维像素空间中一个极薄、极小、受连续约束的区域附近。文本也是同样的逻辑：虽然 token 组合空间极其巨大，但真正同时满足语法、语义、上下文与任务约束的句子，只会沿着某种低维结构变化，而不会任意填满整个离散组合空间。</p>
<p>因此，在机器学习语境中谈流形，真正想表达的并不是完整搬运微分几何的全部形式系统，而是一个更直接的判断：<span style="background-color: #c0c0c0;">有意义的数据并不会随机散落在高维空间中，而是集中在某个低维、连续、受约束的结构附近</span>。后面关于自由度、主成分、隐空间、低维近似以及 LoRA 任务子空间的讨论，都是围绕这个判断展开的不同形式化视角。</p>
<div class="blog_h3"><span class="graybg">自由度</span></div>
<p>沿着前面“表观维度高、内在结构低”的判断继续往下走，就会自然落到自由度（Degrees of Freedom）这个概念上。表观维度说的是“数据在形式上有多少个坐标轴”；自由度说的是“这些数据实际上有多少种彼此独立的有效变化方式”。二者并不相同。一个对象可以嵌在极高维空间里，但真正能变化的自由度却很少。</p>
<p>例如，一张脸部图片在像素空间里有数百万维，但很多像素并不能独立随意变化：头部转向、光照强弱、表情变化、年龄纹理、拍摄距离这些因素彼此耦合，共同决定了大部分像素的联动变化。因此，“一张脸”看起来是高维数组，真正支配它变化的自由度却远小于像素总数。文本也一样。句子表面上由许多 token 组成，但语法结构、主题、语气、说话者意图与上下文约束，使它不可能在每个位置上完全独立自由地变化。</p>
<p>这也是流形假设真正重要的地方：它不是抽象地说“数据在低维流形上”，而是在说<span style="background-color: #c0c0c0;">有效自由度远少于表观维度</span>。一旦把这层理解清楚，后面关于主成分、隐空间、隐主题、低秩近似乃至 LoRA 的很多思想都会变得顺理成章，因为它们都在试图用更少的自由度，去抓住决定数据或参数变化的核心结构。</p>
<div class="blog_h3"><span class="graybg">主成分、隐空间与隐主题</span></div>
<p>一旦接受了“高维数据实际靠近低维结构”这一点，后面许多术语就会自然连起来。主成分（Principal Components）强调几何视角：在一组高维数据里，哪些方向承载了最主要的变化。隐空间（Latent Space）强调表示视角：把原始高维观测压缩到一个更低维、但仍保留关键信息的内部空间。隐主题（Latent Topics）则更偏语义视角：在文本与文档分解里，低维方向常常可以被解释为若干潜在语义因素，例如“体育”“金融”“法律”这类人类能命名的主题轴。</p>
<p>这三个词并不完全同义，但常常指向同一个底层事实：<span style="background-color: #c0c0c0;">高维观测可以通过少数主导方向或潜在因子来近似描述</span>。主成分更强调方差最大的坐标轴；隐空间更强调模型内部那间低维“房间”；隐主题则是在某些任务里，对这些低维方向做出的语义解释。它们分别对应几何、表示与语义三种语言，但共享同一条低维结构主线。</p>
<div class="blog_h3"><span class="graybg">PCA 与低维近似</span></div>
<p>PCA（Principal Component Analysis）是这条思路最经典、也最直接的算法形式。它在无监督条件下寻找方差最大的几个方向，并把数据投影到这些方向张成的低维子空间中。若数据矩阵为 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>，PCA 本质上是在找一个低维线性子空间，使投影后的重建误差尽量小。在线性代数上，这与奇异值分解（SVD）直接对应：保留最大的前 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 个奇异值及其奇异向量，就得到最佳的 rank-<span displaypfx="inline-" class="mathjax-container">\(r\)</span> 近似。</p>
<p>这类思想并不只存在于经典降维里。自动编码器（Autoencoder）通过瓶颈层学习隐空间；潜在语义分析（Latent Semantic Analysis, LSA）和主题模型在文档-词矩阵里抽取潜在主题；词向量、句向量与深度表示模型把高维离散符号压缩到稠密向量空间。它们的目标函数和可解释性不同，但都默认：原始高维观测背后存在一个更低维、更有结构的变化空间。</p>
<div class="blog_h3"><span class="graybg">LoRA 与任务相关子空间</span></div>
<p>LoRA（Low-Rank Adaptation）通过把参数更新 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span> 限制在一个低秩子空间中，实现对大模型的参数高效微调（PEFT）。它的核心做法不是直接改写原始权重矩阵的全部自由度，而是把更新写成两个低秩矩阵的乘积：</p>
<span displaypfx="" class="mathjax-container">\[\Delta W = BA,\quad B\in\mathbb{R}^{d_{\text{out}}\times r},\ A\in\mathbb{R}^{r\times d_{\text{in}}},\ r\ll \min(d_{\text{in}},d_{\text{out}})\]</span>
<p>LoRA 应放在“低维结构”这条主线上理解，但不能把它与 PCA 直接等同。它的核心不是对输入数据做主成分分析，而是对模型参数更新施加低秩约束。</p>
<p>这个约束的含义是：模型不能在完整高维参数空间中任意改动，而只能在一个 rank-<span displaypfx="inline-" class="mathjax-container">\(r\)</span> 的低维更新子空间里移动。从思想上看，它确实与“只保留主导方向”高度相似；若事后对某个全量更新矩阵做 SVD，最佳低秩近似也会只保留最主要的奇异方向。但 LoRA 学到的并不是传统 PCA 意义上“数据方差最大”的主成分，而是<span style="background-color: #c0c0c0;">对当前任务损失下降最有用的低维更新方向</span>。前者是无监督的统计主轴，后者是由反向传播和任务目标共同决定的优化子空间。</p>
<p>因此，把 LoRA 理解为“逼迫模型只在少数主导方向上修改参数”是成立的；但这些方向更准确地说是任务相关的低维适配方向，而不是直接等同于原始数据的 PCA 主成分。也正因为如此，LoRA 与内在维度（Intrinsic Dimension）讨论天然相连：如果一个下游任务真正需要修改的有效自由度本来就不高，那么让模型只在一个低秩子空间里更新，不仅不会显著损失性能，反而会自动抑制大量无意义的噪声方向。</p>
<div class="blog_h2"><span class="graybg">学习范式</span></div>
<p>这里先按监督信号来源划分学习范式。监督学习（Supervised Learning）、无监督学习（Unsupervised Learning）、自监督学习（Self-supervised Learning）和强化学习（Reinforcement Learning）回答的是同一个问题：模型训练时的监督信号究竟来自哪里。它们属于同一分类标准，因此可以并列讨论。</p>
<div class="blog_h3"><span class="graybg">监督学习</span></div>
<p>监督学习（Supervised Learning）使用带标签的数据对 <span displaypfx="inline-" class="mathjax-container">\((x,y)\)</span> 训练模型：输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是特征（Feature），输出 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是目标或标签（Label）。模型学习的不是“记住答案”，而是学习一个映射 <span displaypfx="inline-" class="mathjax-container">\(f_\theta:x\to y\)</span>，使它对新样本也能给出合理预测。</p>
<p>但“学一个映射”还不够，训练时还必须回答另一个更具体的问题：<span style="background-color: #c0c0c0;">怎样才算模型学得好</span>。监督学习里最常见的回答，是在训练集上逐个比较预测与真实标签的差距，再把这些差距汇总成一个总体目标；这就导向经验风险最小化（Empirical Risk Minimization, ERM）。</p>
<p>经验风险最小化的典型目标写成：</p>
<span displaypfx="" class="mathjax-container">\[\frac{1}{N}\sum_{i=1}^{N}\ell\big(f_\theta(x_i),y_i\big)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 是样本数， <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 是模型预测， <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 是真实标签， <span displaypfx="inline-" class="mathjax-container">\(\ell\)</span> 是损失函数（Loss Function）。这条式子的含义不是抽象求和，而是：<span style="background-color: #c0c0c0;">逐个样本计算“预测错了多少”，再取平均，把平均错误压到尽可能小</span>。</p>
<p>例：垃圾邮件分类里， <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 可以是邮件文本特征， <span displaypfx="inline-" class="mathjax-container">\(y\in\{0,1\}\)</span> 表示“正常/垃圾”；房价预测里， <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 可以是面积、地段、楼龄， <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是价格。前者是分类（Classification），后者是回归（Regression），但“有标签地学映射、再用损失函数衡量误差”这一训练逻辑完全一致。</p>
<div class="blog_h4"><span class="graybg">分类任务</span></div>
<p>分类任务（Classification）要预测的是<span style="background-color: #c0c0c0;">离散类别</span>，也就是样本属于哪一类。输出可以是一个类别 id，也可以是一组类别概率。例如二分类里常见输出 <span displaypfx="inline-" class="mathjax-container">\(P(y=1|x)\)</span>，表示“给定特征 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 时，样本属于正类的概率”。</p>
<p>垃圾邮件识别、肿瘤良恶性判断、情感分析、图像里的猫狗识别都属于分类任务。它更像“做选择题”：模型最终要在有限候选里做判断。训练时常配合交叉熵（Cross-Entropy）这类损失，因为模型不仅要选对类别，还要给正确类别足够高的置信度。</p>
<div class="blog_h4"><span class="graybg">回归任务</span></div>
<p>回归任务（Regression）要预测的是<span style="background-color: #c0c0c0;">连续数值</span>，也就是标签不是几个固定类别，而是在某个数值区间内连续变化。输出通常直接是一个实数，或一个多维连续向量。</p>
<p>房价预测、销量预测、温度预测、广告点击率中的停留时长估计都属于回归任务。它更像“做填空题”：模型不能只说“高”或“低”，而必须给出具体数值。训练时常配合均方误差（MSE）或平均绝对误差（MAE），因为关心的是预测值与真实值到底差了多少。</p>
<p>分类与回归都属于监督学习，因为它们都有标签；真正的区别在于<span style="background-color: #c0c0c0;">标签空间的形状</span>：分类的标签空间是离散集合，回归的标签空间是连续区间。这个区别会直接决定模型输出层形式、损失函数选择以及评估指标。</p>
<div class="blog_h3"><span class="graybg">无监督学习</span></div>
<p>无监督学习（Unsupervised Learning）只有输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，没有人工标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>。它的目标不是拟合“标准答案”，而是从数据中发现结构（Structure），例如聚类（Clustering）、降维（Dimensionality Reduction）、密度估计（Density Estimation）与异常检测（Anomaly Detection）。</p>
<p>一个直观类比是：监督学习像“拿着答案册做题”，无监督学习像“没有答案册，只能自己把一堆材料按相似性归类”。例如电商用户没有现成“用户类型”标签，但可以根据浏览、购买、停留时间等行为聚成“价格敏感型”“冲动购买型”“高价值复购型”等群体，用于运营分层。</p>
<div class="blog_h3"><span class="graybg">自监督学习</span></div>
<p>自监督学习（Self-supervised Learning）介于监督与无监督之间：原始数据没有人工标签，但任务标签可以由数据本身自动构造出来。核心思想是<span style="background-color: #c0c0c0;">从数据内部制造预测任务</span>，让模型在完成这些任务的过程中学到可迁移表示（Representation）。</p>
<p>语言模型的下一个 token 预测就是最典型的自监督任务：前文是输入，后一个 token 是由原始文本自动给出的“监督信号”。图像领域里，旋转预测、遮挡恢复、不同增强视角匹配也属于同一路线。</p>
<div class="blog_h4"><span class="graybg">掩码预测</span></div>
<p>掩码预测（Masked Prediction）把输入中的一部分信息故意遮住，再要求模型恢复。例如 BERT 会把句子中的部分 token 替换成特殊标记 <span displaypfx="inline-" class="mathjax-container">\([MASK]\)</span>，模型要根据上下文预测被遮住的词。</p>
<p>类比来看，这像完形填空：你不是死记整句，而是学会根据上下文推断缺失信息。它迫使模型同时利用左侧和右侧上下文，因此特别适合编码器（Encoder）型表示学习。</p>
<div class="blog_h3"><span class="graybg">强化学习</span></div>
<p>强化学习（Reinforcement Learning, RL）研究的是：智能体（Agent）在环境（Environment）中持续交互，根据环境返回的奖励（Reward）学习策略（Policy），使长期累计回报（Cumulative Return）尽可能大。它关心的不是“单个输入该预测什么标签”，而是<span style="background-color: #c0c0c0;">一串连续动作最终能否带来更高的长期收益</span>。</p>
<p>形式化地，强化学习通常把问题写成马尔可夫决策过程（Markov Decision Process, MDP）：智能体在时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 观察状态 <span displaypfx="inline-" class="mathjax-container">\(s_t\)</span>，选择动作 <span displaypfx="inline-" class="mathjax-container">\(a_t\)</span>，环境转移到新状态 <span displaypfx="inline-" class="mathjax-container">\(s_{t+1}\)</span> 并返回奖励 <span displaypfx="inline-" class="mathjax-container">\(r_{t+1}\)</span>。若策略记为 <span displaypfx="inline-" class="mathjax-container">\(\pi(a\mid s)\)</span>，则典型目标是最大化期望累计折扣回报</p>
<span displaypfx="" class="mathjax-container">\[J(\pi)=\mathbb{E}_{\pi}\!\left[\sum_{t=0}^{\infty}\gamma^t r_{t+1}\right]\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\gamma\in[0,1)\)</span> 是折扣因子（Discount Factor），用于控制未来奖励相对于当前奖励的重要性。和监督学习相比，强化学习最根本的差异在于反馈信号的性质：监督学习收到的是<span style="background-color: #c0c0c0;">指导性反馈（Instructional Feedback）</span>，也就是“正确答案应当是什么”；强化学习收到的是<span style="background-color: #c0c0c0;">评估性反馈（Evaluative Feedback）</span>，也就是“这一步或这条轨迹好不好”。奖励只告诉策略结果优劣，却通常不直接给出当前状态下的最优动作。</p>
<div class="blog_h4"><span class="graybg">和监督学习的对比</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">维度</td>
<td style="text-align: center;">监督学习</td>
<td style="text-align: center;">强化学习</td>
</tr>
</thead>
<tbody>
<tr>
<td>反馈信号性质</td>
<td>每个样本通常都有明确标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>，模型收到的是指导性反馈：正确答案应当是什么</td>
<td>环境只返回标量奖励 <span displaypfx="inline-" class="mathjax-container">\(r_t\)</span> 或排序/评分信号，模型收到的是评估性反馈：当前决策好不好，而不是最优动作标签</td>
</tr>
<tr>
<td>数据分布</td>
<td>通常建立在固定离线数据集与独立同分布（i.i.d.）假设之上；模型当前预测不会改变未来会看到什么数据</td>
<td>数据往往来自与环境的持续交互；当前策略会决定未来访问哪些状态，因此数据分布是动态、相关且随策略变化的</td>
</tr>
<tr>
<td>优化目标</td>
<td>最小化经验风险（Empirical Risk）或预测损失，例如交叉熵、均方误差</td>
<td>最大化长期累计回报 <span displaypfx="inline-" class="mathjax-container">\(J(\pi)\)</span>，关注的是整条轨迹上的总收益，而不是单步误差</td>
</tr>
<tr>
<td>时间结构</td>
<td>通常可以看成静态映射 <span displaypfx="inline-" class="mathjax-container">\(x\mapsto y\)</span>；即使处理序列，监督信号也通常来自预先给定的整段标签</td>
<td>本质上是序贯决策（Sequential Decision Making）；当前动作会改变后续状态、可选动作与未来奖励</td>
</tr>
<tr>
<td>主要挑战</td>
<td>泛化（Generalization）、过拟合（Overfitting）、标签噪声与分布外泛化</td>
<td>探索与利用（Exploration vs. Exploitation）、时间信用分配（Temporal Credit Assignment）、高方差评估与训练不稳定性</td>
</tr>
<tr>
<td>误差归因与评估</td>
<td>输入与标签直接对应，误差归因较直接；通常可在验证集/测试集上用 Accuracy、F1、MSE 等固定指标评估</td>
<td>最终奖励需要回溯到哪些历史动作往往并不直接；评估通常依赖真实环境或高保真模拟器中的长期回报，因此方差更高、成本也更高</td>
</tr>
<tr>
<td>探索需求</td>
<td>通常不要求主动探索未知动作空间</td>
<td>必须持续权衡“利用已知高回报动作”与“探索可能更优但尚未验证的动作”</td>
</tr>
</tbody>
</table>
<p>因此，强化学习并不是监督学习的一个别名。它当然也会使用梯度下降、神经网络和损失函数，但这些只是优化工具；两者真正不同的是监督结构。监督学习寻找的是输入到输出的静态映射，强化学习寻找的是动态环境中的最优序贯决策策略。</p>
<p>工程上之所以容易混淆，是因为两者经常交叉出现。RLHF 中的奖励模型训练本身是监督学习，行为克隆（Behavioral Cloning）也是把专家轨迹当作标签来学习；SFT、DPO 这类方法也能在部分对齐任务上用监督式目标逼近强化学习的效果。但只要训练过程仍然缺少逐步给定的正确动作标签，并且目标仍是通过交互或评分信号改进长期决策策略，它在范式上仍然属于强化学习。</p>
<div class="blog_h4"><span class="graybg">核心循环：迷宫游戏</span></div>
<p>可以把强化学习先想成一个小机器人走迷宫。这个故事里有六个最核心的角色：</p>
<ul>
<li>智能体（Agent）：做决策的主体，也就是那个小机器人。</li>
<li>环境（Environment）：机器人所处的外部世界，例如迷宫、棋盘、游戏地图、推荐系统或对话上下文。</li>
<li>状态（State, <span displaypfx="inline-" class="mathjax-container">\(s\)</span>）：机器人当前看到的局面，例如“前面是墙，右边有路”。</li>
<li>动作（Action, <span displaypfx="inline-" class="mathjax-container">\(a\)</span>）：机器人此刻可以做的选择，例如左转、右转、前进。</li>
<li>奖励（Reward, <span displaypfx="inline-" class="mathjax-container">\(r\)</span>）：环境给这一步行动的反馈，例如捡到金币加 1，掉进陷阱减 1。</li>
<li>策略（Policy, <span displaypfx="inline-" class="mathjax-container">\(\pi\)</span>）：机器人脑子里的“通关秘籍”，也就是在什么状态下该怎么行动的规则。</li>
</ul>
<p>于是强化学习的目标可以直接翻译成一句人话：<span style="background-color: #c0c0c0;">不断修改这本“通关秘籍”，让整局游戏玩下来拿到的总分尽可能高</span>。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/rl.png"><img class="alignnone size-full wp-image-40981" src="https://blog.gmem.cc/wp-content/uploads/2026/03/rl.png" alt="rl" width="100%" /></a></p>
<p>为什么这里不只看即时奖励，而要看“整局总收益”？因为很多任务里，奖励会延迟出现。下棋时，一步好棋未必立刻得分，但可能为十步后的胜利铺路；推荐系统里，一次推荐是否合理，也要看用户后续点击、停留和转化。因此强化学习常用于游戏对战、机器人控制、资源调度、广告竞价、推荐排序，以及大模型对齐等场景。</p>
<p>为了衡量“从现在开始，这套打法最终值不值得”，强化学习定义长期回报（Return，也常称累计回报）：</p>
<span displaypfx="" class="mathjax-container">\[G_t=\sum_{k=0}^{\infty}\gamma^k r_{t+k+1},\quad \max_\pi\ \mathbb{E}_\pi[G_t]\]</span>
<p>这条式子不要硬背，可以直接按故事来读。前半部分 <span displaypfx="inline-" class="mathjax-container">\(G_t=\sum_{k=0}^{\infty}\gamma^k r_{t+k+1}\)</span> 说的是“从当前时刻开始，整局游戏最终能拿多少分”： <span displaypfx="inline-" class="mathjax-container">\(r_{t+k+1}\)</span> 是未来第 <span displaypfx="inline-" class="mathjax-container">\(k+1\)</span> 步得到的奖励， <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 是折扣因子（Discount Factor），表示未来奖励要不要打折。它的直觉很像“现在的 100 块通常比一年后的 100 块更值钱”：当 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 越接近 1，模型越重视长期收益；当它更小，模型就更短视。后半部分 <span displaypfx="inline-" class="mathjax-container">\(\max_\pi\ \mathbb{E}_\pi[G_t]\)</span> 则是在说：在所有可能的策略 <span displaypfx="inline-" class="mathjax-container">\(\pi\)</span> 里，去寻找那个能让期望长期回报最大的策略。这里的 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}_\pi\)</span> 是期望，后面的方括号 <span displaypfx="inline-" class="mathjax-container">\([G_t]\)</span> 只是表示“取期望的对象是 <span displaypfx="inline-" class="mathjax-container">\(G_t\)</span> 这个随机回报”，并不是额外的新符号。之所以要写期望，是因为同一个策略在同一个环境里重复运行，过程里往往带有随机性——例如环境可能有随机事件，策略本身也可能按概率选动作。所以强化学习真正优化的不是“某一次刚好打得特别好”的偶然结果，而是“长期重复玩这局游戏时，平均下来能拿到的总分”尽可能高。</p>
<div class="blog_h4"><span class="graybg">把故事写成数学：MDP</span></div>
<p>马尔可夫决策过程（Markov Decision Process, MDP）是强化学习的标准数学建模框架，用来描述“状态如何转移、动作如何作用、奖励如何定义，以及长期目标如何写成优化问题”。它就是把前面的迷宫故事写成一套可推导、可计算的数学记号。最常见的写法是五元组 <span displaypfx="inline-" class="mathjax-container">\((\mathcal{S},\mathcal{A},P,R,\gamma)\)</span>：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathcal{S}\)</span>：状态空间（State Space），也就是所有可能局面的集合。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathcal{A}\)</span>：动作空间（Action Space），也就是所有可选动作的集合。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(P(s'|s,a)\)</span>：状态转移概率，表示在状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 做动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 后，到达下一状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span> 的概率。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(R(s,a)\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(R(s,a,s')\)</span>：奖励函数，表示这一步能拿到什么反馈。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span>：折扣因子，决定未来奖励在总目标里占多大比重。</li>
</ul>
<p>“马尔可夫”这个词听着吓人，意思其实很简单：<span style="background-color: #c0c0c0;">如果当前状态已经把关键信息都概括好了，那么未来只取决于当前状态和当前动作</span>。像下棋时，只看当前棋盘局面就足够，不必把每一步历史都原样背下来。</p>
<p>理解了这个核心循环后，主流强化学习算法其实只是三大门派：价值派、策略派，以及在大模型时代出现的对齐派。它们不是在解决不同问题，而是在用不同方式学习同一件事：怎样改进策略。</p>
<div class="blog_h4"><span class="graybg">价值派：Q-Learning 与 DQN</span></div>
<p>价值派（Value-Based）的思路像一个精打细算的记账员。它不直接记“现在该怎么做”，而是先问一个更根本的问题：<span style="background-color: #c0c0c0;">如果我在当前状态下做这个动作，从长期看到底划不划算</span>。这个“划算程度”的估计，就是动作价值函数（Action-value Function） <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span>。</p>
<p>这里最容易混淆的是： <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 不是“这一步眼前立刻拿到的奖励”，也不是单独指“这一步的成本”。它表示的是：<span style="background-color: #c0c0c0;">在状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 下先执行动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span>，然后后面继续尽量做好选择，最终总共大约能拿到多少长期回报</span>。如果某个动作会消耗时间、体力、资源或带来风险，这些通常会被写进奖励函数里，表现成负奖励；因此 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 看的是长期净效果，而不是只看眼前奖励，也不是只看单独成本。</p>
<p>Q-Learning 之所以叫这个名字，是因为它要学习的对象就是动作价值函数 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 的估计（Q 源自 quality，表示动作在某状态下的“好坏程度”）。Q-Learning 的直觉是：如果我在状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 下选择动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span>，环境会先立刻返还一个即时奖励 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>，然后把我带到下一状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span>。这里的 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 只是在给“这一步刚刚发生了什么”打分；而 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 想学的则是“这一步连同后面整局走势一起看，最终值多少”。所以 Q-Learning 做的事情，不是把即时奖励直接当成答案，而是用这一步新看到的结果，回头修正账本里对 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 的动作价值估计：</p>
<span displaypfx="" class="mathjax-container">\[Q(s,a)\leftarrow Q(s,a)+\alpha\Big(r+\gamma\max_{a'}Q(s',a')-Q(s,a)\Big)\]</span>
<p>这条更新式最好逐项拆开看：</p>
<ul>
<li>更新式左边的 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 表示“这次更新后，要重新写回账本的那个值”。</li>
<li>箭头右边最后那个 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 表示“更新前账本里原来记着的旧估计”。同一个符号在箭头两边都出现，是因为它表示的是<span style="background-color: #c0c0c0;">同一个账本位置更新前后的值</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(s\)</span> 是当前状态，例如“机器人现在站在岔路口，左边是墙，右边能走”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(a\)</span> 是当前状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span> 下刚刚执行的那个动作，例如“向右走一步”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(r\)</span> 是执行动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 后，环境立刻返还的即时奖励。例如这一步踩到金币就加 1，踩到陷阱就减 1。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(s'\)</span> 是执行完当前动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 之后到达的下一状态。这里的撇号只是表示“下一个状态”，不是别的特殊运算。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(a'\)</span> 表示在下一状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span> 中任意一个可选动作。它带撇号，只是为了和当前动作 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 区分开。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(Q(s',a')\)</span> 表示：如果机器人已经来到下一状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span>，此时选择动作 <span displaypfx="inline-" class="mathjax-container">\(a'\)</span>，那么从那以后预期长期回报大约是多少。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\max_{a'}Q(s',a')\)</span> 的意思是：把下一状态 <span displaypfx="inline-" class="mathjax-container">\(s'\)</span> 里所有可选动作 <span displaypfx="inline-" class="mathjax-container">\(a'\)</span> 的长期价值都估一遍，再挑出其中最大的那个估计。它回答的问题是：<span style="background-color: #c0c0c0;">如果我已经走到了下一状态，并且从下一步开始尽量做最好的选择，后面大概还能再拿到多少长期回报</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\gamma\max_{a'}Q(s',a')\)</span> 表示把“未来还能拿到的最好长期回报”按折扣因子 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 打折后算回来，因为越远的未来通常权重越小。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(r+\gamma\max_{a'}Q(s',a')\)</span> 是新的目标值（TD target）：它把“这一步立刻拿到的即时奖励”与“从下一步开始最好情况下还能拿到的折扣后长期回报”加在一起。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(r+\gamma\max_{a'}Q(s',a')-Q(s,a)\)</span> 是新目标与旧估计之间的差，也就是时序差分误差（TD error）。如果这个差为正，说明原来低估了；如果这个差为负，说明原来高估了。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 是学习率，控制这次修正到底改多少。 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 大，账本改得快但更容易抖；小则更稳，但学得更慢。</li>
</ul>
<p>把整条式子连起来看，就是：先记录“当前这一步实际拿到了多少即时奖励”，再估计“到了下一状态后，如果接下来尽量做最好的选择，还能拿到多少折扣后长期回报”，把这两部分合成一个新的目标值，然后用它去修正旧账本里对 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 的动作价值估计。这就是 Q-Learning 的核心。</p>
<p>DQN（Deep Q-Network）并没有改变这个思路，它只是把“小本子记账”升级成“让神经网络来记账”。当状态非常大时，例如 Atari 游戏的原始像素画面，手工建一个巨大 Q 表几乎不可能，这时就用深度网络直接输入状态，输出每个动作的价值估计。DQN 的本质不是新的哲学，而是：<span style="background-color: #c0c0c0;">让价值派能处理高维感知输入</span>。</p>
<p>用迷宫里的“死胡同”再对照一遍，会更容易把这件事和更新式对上。常见有两种奖励设计：</p>
<ul>
<li>如果每走一步都有即时奖励（很多任务里是每步一个小的负奖励，表示时间或能量成本），那么一旦走进死胡同，哪怕还没走到尽头，后续回退/绕路的每一步都会继续累积这些代价。沿着死胡同走得越深，后续必然要付出的额外代价越多，于是死胡同深处的状态会先学到更低的 <span displaypfx="inline-" class="mathjax-container">\(\max_{a'}Q(s,a')\)</span>；再通过更新式里的 <span displaypfx="inline-" class="mathjax-container">\(\gamma\max_{a'}Q(s',a')\)</span> 把“更差的未来”逐步回传到入口处，最终拉低“最开始走进死胡同那一步”的 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span>。</li>
<li>如果奖励非常稀疏：只有到达终点那一刻给一次奖励，其余步奖励为 0，那么走进死胡同本身不会产生额外的负奖励，它的影响来自“把终点奖励推迟了”。假设终点奖励为 <span displaypfx="inline-" class="mathjax-container">\(R_{\mathrm{goal}}\)</span>，到达终点的时间为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>，则从时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 起的长期回报约为 <span displaypfx="inline-" class="mathjax-container">\(G_t=\gamma^{T-t-1}R_{\mathrm{goal}}\)</span>：走到终点越晚，<span displaypfx="inline-" class="mathjax-container">\(\gamma&lt;1\)</span> 时折扣越多，回报越小。这会表现为：越靠近终点的状态动作对先学到较大的 Q，越远的状态动作对学到的 Q 会多乘几个 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span>。若环境还有最大步数上限（time limit），超过上限视为失败并给 0 回报，那么绕进死胡同也可能把轨迹拖到上限之外，使得许多状态动作对的 TD target 直接变为 0，从而把入口处那一步的 <span displaypfx="inline-" class="mathjax-container">\(Q(s,a)\)</span> 也压低。</li>
</ul>
<div class="blog_h4"><span class="graybg">策略派：Policy Gradient 与 PPO</span></div>
<p>策略派（Policy-Based Methods）直接参数化策略（Policy）本身：给定状态 <span displaypfx="inline-" class="mathjax-container">\(s\)</span>，模型直接输出动作概率分布 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(a|s)\)</span>，再通过优化让高回报动作变得更可能被采样。这类方法把“该怎么行动”直接写进策略函数，而不是先显式学习一个动作价值表。</p>
<p>这条路线尤其适合连续动作空间，因为当动作不是“左转/右转”这种离散选项，而是“方向盘打多少角度”“机械臂关节转多少度”这种连续控制量时，根本不适合把每个动作都列成账本。于是它选择直接练本能：给定状态，直接输出动作概率分布，而这个分布本身就是策略 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(a|s)\)</span>。</p>
<p>策略梯度（Policy Gradient）的核心思想非常直白：<span style="background-color: #c0c0c0;">凡是最终带来高回报的动作，就提高它以后再次发生的概率；凡是带来低回报的动作，就降低它的概率</span>。它的基本形式写成</p>
<span displaypfx="" class="mathjax-container">\[\nabla_\theta J(\theta)=\mathbb{E}\big[\nabla_\theta \log \pi_\theta(a_t|s_t)\,G_t\big]\]</span>
<p>这条式子同样最好逐项拆开看：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(J(\theta)\)</span> 是策略的总体目标，也就是“让整条策略长期回报尽可能大”这件事。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta J(\theta)\)</span> 表示“如果我想把总目标 <span displaypfx="inline-" class="mathjax-container">\(J(\theta)\)</span> 变大，参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 应该往哪个方向改”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 是策略模型的参数。在机器人里它可能是控制器参数，在神经网络里它就是网络权重。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(s_t\)</span> 是时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的状态，也就是当前看到的局面。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(a_t\)</span> 是时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 真正执行的那个动作。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(a_t|s_t)\)</span> 是“在状态 <span displaypfx="inline-" class="mathjax-container">\(s_t\)</span> 下，策略给动作 <span displaypfx="inline-" class="mathjax-container">\(a_t\)</span> 分配了多大概率”。它不是奖励，也不是价值，而是策略本身的偏好强弱。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\log \pi_\theta(a_t|s_t)\)</span> 不是又引入了一个神秘对象，它只是对这个概率取对数。这样做的主要原因是数学上更容易求导，并且能把一整条轨迹上的概率乘积改写成对数求和。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta \log \pi_\theta(a_t|s_t)\)</span> 表示：如果想让动作 <span displaypfx="inline-" class="mathjax-container">\(a_t\)</span> 在状态 <span displaypfx="inline-" class="mathjax-container">\(s_t\)</span> 下更容易再次发生，参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 应该朝哪个方向改。它给的是“增大该动作概率的方向”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(G_t\)</span> 是从时刻 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 开始，这次动作后面最终带来的长期回报。它在这里扮演“事后评分”的角色：若 <span displaypfx="inline-" class="mathjax-container">\(G_t\)</span> 高，说明这次动作最终效果好；若低，说明效果差。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta \log \pi_\theta(a_t|s_t)\,G_t\)</span> 这项可以直观理解为：先找到“怎样让这次动作更常发生”的方向，再用 <span displaypfx="inline-" class="mathjax-container">\(G_t\)</span> 来决定这个方向该被强化多少。动作后果越好，就推得越用力；后果越差，就反过来压制。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[\cdot]\)</span> 表示对很多次采样结果取期望，因为单次轨迹有随机性。策略梯度真正优化的不是“某一次刚好运气好”的结果，而是长期平均表现。</li>
</ul>
<p>把整条式子连起来看，就是：先执行一个动作，等整段后果出来后，再回头问“这次动作究竟值不值”。如果值，就提高它以后再次出现的概率；如果不值，就降低它的概率。这就是策略派“直接练本能”的本质。</p>
<p>PPO（Proximal Policy Optimization）是在策略梯度上加护栏。纯策略梯度的问题是：如果某一步更新太猛，策略可能一下子学歪，训练直接崩掉。PPO 的核心就是限制“这次别改太多”：</p>
<span displaypfx="" class="mathjax-container">\[\min\Big(r_t(\theta)A_t,\ \mathrm{clip}(r_t(\theta),1-\epsilon,1+\epsilon)A_t\Big)\]</span>
<p>这条式子也可以逐项拆开看：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(r_t(\theta)=\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\mathrm{old}}}(a_t|s_t)}\)</span> 是新旧策略对同一动作概率的比值。若它接近 1，说明这次更新前后，策略对这个动作的看法变化不大；若它远离 1，说明改动已经很大。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\pi_{\theta_{\mathrm{old}}}(a_t|s_t)\)</span> 是更新前旧策略给这个动作的概率； <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(a_t|s_t)\)</span> 是更新后新策略给它的概率。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(A_t\)</span> 是优势函数（Advantage）。它回答的问题不是“这次总共拿了多少分”，而是“这个动作比当前状态下的平均水平到底好多少”。若 <span displaypfx="inline-" class="mathjax-container">\(A_t&gt;0\)</span>，说明这步动作比平均更好；若 <span displaypfx="inline-" class="mathjax-container">\(A_t&lt;0\)</span>，说明更差。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(r_t(\theta)A_t\)</span> 可以理解成“如果完全按这次新旧概率比去更新，那么这个动作会被奖励或惩罚多少”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{clip}(r_t(\theta),1-\epsilon,1+\epsilon)\)</span> 是 PPO 最关键的护栏：它强行规定概率比不要偏离 1 太远。 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 就是护栏宽度。</li>
<li>外面的 <span displaypfx="inline-" class="mathjax-container">\(\min(\cdot,\cdot)\)</span> 表示：原始更新和“截断后的保守更新”两者里，只取更保守的那个。这样即使优化器很激进，也不会让策略一步跳太远。</li>
</ul>
<p>所以 PPO 的本质可以直接说成人话：<span style="background-color: #c0c0c0;">方向仍然按“好的动作多做，差的动作少做”来改，但每次只允许小步微调，防止策略训练失控</span>。这也是它在机器人控制和大模型对齐里都非常常用的原因。</p>
<div class="blog_h4"><span class="graybg">大模型对齐：RLHF、DPO 与 GRPO</span></div>
<p>语言模型本来做的是下一个 token 预测：给定前文，猜下一个词最可能是什么。这很像一只会续写的鹦鹉。大模型对齐（Alignment）要解决的问题是：怎样让它不只是“会接话”，还要更有帮助、更安全、更符合人类偏好。</p>
<p>把语言模型放进强化学习视角后，整个映射会立刻清楚起来：用户输入的 prompt 加上当前已生成前缀 <span displaypfx="inline-" class="mathjax-container">\((x,c_t)\)</span> 就是状态；当前要选的下一个 token <span displaypfx="inline-" class="mathjax-container">\(y_t\)</span> 就是动作；整段最终回答 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 就是一条完整轨迹；而模型本身给出的下一个 token 条件概率分布 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta(y_t|c_t,x)\)</span>，就是所谓生成策略（Generation Policy）。所以“修改生成策略”并不神秘，本质上就是：<span style="background-color: #c0c0c0;">修改模型在每个上下文里更倾向说哪些 token</span>。</p>
<p>RLHF（Reinforcement Learning from Human Feedback）的做法是：先收集人类偏好数据，例如“回答 A 比回答 B 更好”；再训练一个奖励模型 <span displaypfx="inline-" class="mathjax-container">\(r_\phi(x,y)\)</span>，让它学会像裁判一样给回答打分；最后用 PPO 这类策略优化方法，把高分回答的概率调高、低分回答的概率调低。一个常见目标写成</p>
<span displaypfx="" class="mathjax-container">\[\max_\pi\ \mathbb{E}_{y\sim \pi(\cdot|x)}\big[r_\phi(x,y)-\beta\,\mathrm{KL}(\pi(\cdot|x)\|\pi_{\mathrm{ref}}(\cdot|x))\big]\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\mathrm{ref}}\)</span> 是参考模型，通常是监督微调（SFT）后的初始模型；KL 项的作用是防止模型为了讨好奖励模型而“跑偏”太远。可以把 RLHF 理解成：先找人类裁判总结出一套评分标准，再让模型在这套标准下稳步调整说话方式。</p>
<p>DPO（Direct Preference Optimization）认为这套流程有点重：既要训练奖励模型，又要跑强化学习优化。它利用偏好数据做了数学改写，直接把“回答 A 优于回答 B”的信息作用到策略本身，跳过了显式奖励模型与完整 RL 回路。直观地说，DPO 像是把“裁判打分 + 再训练”合成一步，工程更简单，训练也更稳定。</p>
<p>GRPO（Group Relative Policy Optimization）则进一步强调“相对好坏”而不是绝对打分。它让模型针对同一个问题先生成一组候选答案，再在组内比较谁更好、谁更差，然后根据这个相对排名更新策略。它的直觉很像班级作文比赛：不是先给每篇作文一个绝对分，而是先把同一组作文排个名次，再让模型朝更优答案的方向调整。GRPO 的优势在于省掉了沉重的价值网络（Critic），因此特别适合数学推理、代码生成这类可以比较优劣、但很难稳定打绝对分的任务。</p>
<div class="blog_h2"><span class="graybg">表示学习与适配策略</span></div>
<p>与“学习范式”不同，下面这些概念不再按监督信号来源分类，而是分别回答另外几个问题：表示该怎样学、计算该怎样近似、已有知识该怎样迁移、在极少样本下又该怎样适配。因此它们更适合看成与学习范式并列的训练目标或训练策略。</p>
<div class="blog_h3"><span class="graybg">表示学习（Representation Learning）</span></div>
<p>表示学习（Representation Learning）讨论的是：如何把原始输入自动变换成更有用的特征表示，使后续任务更容易处理。它关心的不只是“最后预测对不对”，还关心模型内部是否学到了稳定、可迁移、对任务有判别力的中间表示。</p>
<p>传统特征工程（Feature Engineering）与表示学习处理的是同一个核心问题：如何把原始输入变成更适合下游任务的表示。但二者的方法论不同。特征工程主要依赖人工设计表示，例如词频、n-gram、统计量、规则特征与人工交叉特征；表示学习则强调由模型通过优化过程自动学出表示，例如 PCA、自编码器、词向量、上下文化表示以及深度网络中的隐藏状态。因此，传统手工特征本身通常不直接归入表示学习；只有当表示是通过训练自动获得时，它才更准确地属于表示学习范畴。</p>
<p>从 one-hot、BoW、词嵌入，到 BERT 的上下文化表示、Sentence-BERT 的句向量，主线始终一致：把原始符号或原始观测映射到更适合计算的表示空间。监督学习、自监督学习、对比学习都可以被用来学习表示；区别只在于监督信号来自哪里、训练目标如何设计。</p>
<div class="blog_h3"><span class="graybg">对比学习</span></div>
<p>对比学习（Contrastive Learning）通过“拉近正样本、推远负样本”学习表示。这里的关键不是类别标签本身，而是样本之间的相对关系：哪些应该相似，哪些必须区分。因此它特别适合表示学习、检索、多模态对齐和度量学习（Metric Learning）。</p>
<p>它的真正价值不只在于“学会匹配”，更在于学会<span style="background-color: #c0c0c0;">区分性特征（Discriminative Features）</span>。如果训练信号只告诉模型“这两个文本有关”，模型很容易停留在泛泛的共性描述上；而当训练持续提供“相似对”和“不相似对”，模型就被迫回答更尖锐的问题：究竟是什么让这两个文本属于同一语义区域，又是什么让它们必须分开。对比学习因此天然擅长抑制“表面上正确但没有区分度”的表示，转而强化真正决定语义边界的特征。</p>
<p>例如在商品评论表示学习里，句子“物流很快，包装也完整”和“物流很快，但东西是坏的”都包含“物流很快”这类高频表述。若模型只抓住表面词汇重叠，就可能把二者编码得非常接近；但在对比学习里，前者可能与“发货速度快、体验不错”构成正样本，后者则会与“收到商品后无法使用”“质量有问题”这类负面评价更接近。模型因此会逐步学会：真正决定语义边界的，不是共享的套话，而是“包装完整”“东西是坏的”这类改变整体语义走向的区分性片段。</p>
<p>从几何角度看，对比学习学到的是一个更有结构的向量空间。语义接近的文本会在局部形成簇（Cluster），语义无关或语义相反的文本则被推向更远位置。情感分析、语义检索、重复问句检测、意图聚类之所以能直接建立在 embedding 之上，本质上就是因为模型已经把“哪些内容应当靠近、哪些内容应当远离”编码进了空间结构，而不只是输出一个任务特定的分类分数。</p>
<p>在 NLP 中，这条路线并不是突然出现的。Word2Vec 已经体现了早期的对比式思想：真实共现词是正样本，随机采样词是负样本，模型通过区分“真实上下文”和“噪声配对”学习词向量。后来的句向量和文档向量模型，则把这种思想从词级扩展到句子级和文档级：正样本可以是复述句、问答配对、查询与相关文档，负样本则是不相关句子或困难反例（Hard Negatives）。</p>
<p>一个常见形式是 InfoNCE 损失：</p>
<span displaypfx="" class="mathjax-container">\[-\log \frac{\exp(\mathrm{sim}(z_i,z_i^+)/\tau)}{\sum_{j}\exp(\mathrm{sim}(z_i,z_j)/\tau)}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(z_i\)</span> 是当前样本表示， <span displaypfx="inline-" class="mathjax-container">\(z_i^+\)</span> 是与它匹配的正样本表示， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{sim}(\cdot,\cdot)\)</span> 是相似度函数（常用余弦相似度）， <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span> 是温度参数（Temperature），控制分布尖锐程度。这个目标的含义是：在一堆候选中，让正确配对拿到最高分，同时把不相关样本推远。</p>
<p>对比学习在句向量任务中的意义尤其大。交叉编码器（Cross-Encoder）把两个句子拼接后联合编码，能够做非常细的交互判断，但它直接输出的是“这一对句子有多像”，而不是可复用的独立句向量；一旦候选集合很大，计算量会迅速爆炸。双编码器（Bi-Encoder）路线则把两个文本分别编码成独立向量，再用余弦相似度或点积比较。SBERT 正是这一路线的经典代表：它通过孪生网络（Siamese Network）与对比式微调，把原本不适合作为通用句向量的 BERT 表示空间，改造成适合检索、聚类与语义匹配的 embedding 空间。</p>
<p>工程上，负样本既可以来自同一 batch 中的其他样本（In-batch Negatives），也可以来自专门构造的困难负样本（Hard Negatives）。所谓困难负样本，指的不是“完全无关”的反例，而是<span style="background-color: #c0c0c0;">在表面上很像、但语义上不应被判为同一项</span>的样本。例如检索里，与查询主题相近但并不真正回答问题的文档；句向量训练里，措辞高度相似却语义立场不同的句子；推荐里，风格相近但用户最终没有点击或转化的候选。它们之所以“困难”，正是因为模型若只依赖浅层词汇重叠、模板结构或主题相近性，很容易把这类负样本误判成正样本。</p>
<p>困难负样本的价值在于：它迫使模型放弃过于粗糙的匹配捷径，转而学习更细粒度的区分信号。随机负样本通常太容易分开，训练后期提供的梯度会迅速变弱；而困难负样本更接近真实决策边界，能持续推动表示空间学习“看起来相似但本质不同”的区别。不过它也有代价：若负样本挖掘质量不高，容易把本来就相关的样本错当成负例，形成假负样本（False Negatives），反而会伤害表示质量。因此，现代检索和 embedding 训练里，Hard Negatives 往往与 in-batch negatives、教师模型挖掘（teacher mining）或 reranker 筛选结合使用，而不是完全依赖人工拍脑袋构造。</p>
<p>CLIP、Sentence-BERT、现代检索 embedding、推荐召回模型，乃至许多 query-document dual encoder，本质上都在利用这种“正样本拉近、负样本推远”的训练逻辑。区别主要不在原理，而在样本如何构造、负样本如何选择，以及表示对象是词、句子、文档还是跨模态对。</p>
<div class="blog_h4"><span class="graybg">负采样</span></div>
<p>负采样（Negative Sampling）是与对比学习和词向量训练密切相关的一类近似策略。它的核心动机是：当候选空间极大时，没有必要每次都与所有候选比较；只保留 1 个正样本和少量负样本，就能得到足够强的判别信号。换句话说，它把原本代价高昂的“大规模归一化选择问题”，近似成若干个“真配对还是噪声配对”的二分类判断。</p>
<p>在 Word2Vec 的 Skip-gram 中，若直接对全词表做 softmax，分母需要对 <span displaypfx="inline-" class="mathjax-container">\(|{\cal V}|\)</span> 个词求和，计算代价很高。负采样则对每个正样本对 <span displaypfx="inline-" class="mathjax-container">\((w,c)\)</span> 只保留少量噪声词 <span displaypfx="inline-" class="mathjax-container">\(w_i\)</span>，并最大化：</p>
<span displaypfx="" class="mathjax-container">\[\log\sigma(v_w^\top v_c)+\sum_{i=1}^{k}\log\sigma(-v_w^\top v_{w_i})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 是 sigmoid 函数，第一项鼓励真实配对的内积更大，第二项鼓励噪声配对的内积更小。这样一来，计算量就从与词表大小同阶，降到与 <span displaypfx="inline-" class="mathjax-container">\(1+k\)</span> 个样本同阶。负样本也不一定完全随机：Word2Vec 常按词频的 <span displaypfx="inline-" class="mathjax-container">\(0.75\)</span> 次方采样；现代对比学习则常用 in-batch negatives 或 hard negative mining。推荐系统召回、知识图谱嵌入、句向量训练等任务里，这种思想到今天仍然非常常见。</p>
<div class="blog_h3"><span class="graybg">迁移学习</span></div>
<p>迁移学习（Transfer Learning）讨论的是：先在数据更丰富、任务更通用的源任务上学到参数或表示，再把这些知识迁移到目标任务。它不是按监督信号划分出来的独立“学习方式”，而是一种<span style="background-color: #c0c0c0;">跨任务复用知识的训练策略</span>。现代大模型先预训练、再微调，本质上就是迁移学习。</p>
<p>BERT 就是这一思路的典型例子。它通常先在大规模通用文本上做语言建模预训练，例如维基百科（Wikipedia）这类覆盖面很广的语料；模型先学到词法、句法、语义关系以及上下文表示能力。随后再把这一预训练模型迁移到具体任务上，例如情感分类、自然语言推断（NLI）、命名实体识别（NER）或文本匹配，只需接上任务头并用该任务的数据继续微调，就能把通用语言知识转化为面向目标任务的能力。</p>
<p>它与对比学习不在同一层面。对比学习回答的是“预训练阶段该用什么目标来学表示”；迁移学习回答的是“学到的表示如何迁到新任务”。两者经常配合出现：例如先在海量无标签图像上用对比学习预训练视觉编码器，再把该编码器迁移到医学影像分类、工业缺陷检测或小样本识别任务上。</p>
<div class="blog_h3"><span class="graybg">少样本学习</span></div>
<p>少样本学习（Few-shot Learning）处理的是“每个任务只有极少标注样本”时如何仍然快速泛化。它通常建立在迁移学习或预训练模型之上：模型先学到一套通用表示，再在很少示例下快速适配新任务。困难不在于单个任务本身，而在于模型必须把以往经验迁移到新任务上。直觉上，它更像“学会如何快速学习”，而不是“把一个任务彻底学透”。</p>
<div class="blog_h4"><span class="graybg">零样本（Zero-shot）</span></div>
<p>零样本（Zero-shot）指模型在目标任务上没有任何专门示例，也能凭借已有知识完成任务。大语言模型通过指令理解实现的很多能力都属于这一类。例：不给任何情感分类样例，只写“判断下面评论是正面还是负面”，模型仍可能完成分类。</p>
<div class="blog_h4"><span class="graybg">单样本（One-shot）</span></div>
<p>单样本（One-shot）指只给 1 个示例。这个示例的价值不是提供统计规律，而是告诉模型“输出格式、任务边界和你想要的判别标准”。例如先给一条“商品评论 → 正面”的例子，再让模型判断下一条评论。</p>
<div class="blog_h4"><span class="graybg">K 样本（K-shot）</span></div>
<p>K 样本（K-shot）指给每类或每任务提供 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个示例。随着 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 增大，模型更容易对任务意图和判别标准形成稳定估计。工程上，prompt 中的 few-shot 示例本质上就是在上下文窗口里做一种“临时任务适配”。</p>
<div class="blog_h4"><span class="graybg">元学习（Meta-learning / MAML）</span></div>
<p>元学习（Meta-learning）研究“让模型更快适应新任务”。MAML（Model-Agnostic Meta-Learning）的核心不是直接学一个最终答案，而是学一个好的初始化参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，使模型只需少量梯度更新就能适配新任务。</p>
<p>MAML 的外层目标可概括为：</p>
<span displaypfx="" class="mathjax-container">\[\min_\theta \sum_{\mathcal{T}} \mathcal{L}_{\mathcal{T}}\big(\theta-\alpha\nabla_\theta \mathcal{L}_{\mathcal{T}}(\theta)\big)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{T}\)</span> 表示一个任务， <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 是内层更新步长。读法是：先用当前参数在某任务上走一步，再看更新后的参数在该任务上的表现好不好；如果“一步后就变好”，说明初始化是好的。类比来看，MAML 训练的不是“会做每道题的学生”，而是“只要老师讲一遍就能迅速举一反三的学生”。</p>
<div class="blog_h4"><span class="graybg">原型网络（Prototypical Networks）</span></div>
<p>原型网络（Prototypical Networks）把每个类别表示成嵌入空间中的一个“类中心（Prototype）”。对类别 <span displaypfx="inline-" class="mathjax-container">\(k\)</span>，其原型定义为该类支持集（Support Set）样本嵌入的平均：</p>
<span displaypfx="" class="mathjax-container">\[c_k=\frac{1}{|S_k|}\sum_{(x_i,y_i)\in S_k,\ y_i=k} f_\theta(x_i)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(f_\theta(x_i)\)</span> 是样本的向量表示， <span displaypfx="inline-" class="mathjax-container">\(S_k\)</span> 是类别 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 的支持样本集合。分类时，把新样本映射到嵌入空间，看它离哪个原型最近。直觉上，这像“每一类先算一个代表点，新样本按离哪个代表点最近来归类”。在 few-shot 图像分类中，这种方法往往比直接训练复杂分类头更稳。</p>
<div class="blog_h2"><span class="graybg">表示聚合与池化</span></div>
<p>池化（Pooling）可以先按一句人话来理解：<span style="background-color: #c0c0c0;">把一组相邻或相关的特征，压缩成更短、更稳定、更容易继续处理的摘要</span>。它不是重新发明新特征，而是对已有特征做聚合（Aggregation）或下采样（Downsampling）。这里的下采样指：<span style="background-color: #c0c0c0;">沿某些维度减少位置数或采样点数，让表示尺寸变小、分辨率变粗</span>。例如把 <span displaypfx="inline-" class="mathjax-container">\(4\times 4\)</span> 的特征图压成 <span displaypfx="inline-" class="mathjax-container">\(2\times 2\)</span>，或把一长段序列压成更短的摘要向量，都属于下采样。</p>
<p>若把一组输入特征记为 <span displaypfx="inline-" class="mathjax-container">\(x_1,\dots,x_k\)</span>，则池化可以抽象写成</p>
<span displaypfx="" class="mathjax-container">\[y=\mathrm{Pool}(x_1,\dots,x_k)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Pool}\)</span> 可以是最大值（Max Pooling）、平均值（Average Pooling）、求和（Sum Pooling）或更复杂的加权聚合。它们做的事不同，但主线一致：<span style="background-color: #c0c0c0;">把“多个位置/多个元素的表示”变成“更少的表示”</span>。</p>
<p>池化之所以重要，是因为很多任务并不需要保留每个细节位置的完整分辨率。图像分类不一定关心边缘恰好落在第 17 个还是第 18 个像素；句子分类也不一定要求记住某个情绪词出现在第 6 个还是第 7 个 token。此时，把局部细节适度压缩，往往能提升稳定性、降低计算量，并让后续层更关注“有没有出现模式”，而不是“模式的坐标是否一模一样”。</p>
<div class="blog_h3"><span class="graybg">最常见的几种池化</span></div>
<p>最大池化（Max Pooling）保留一组特征里最强的那个响应。若某个窗口里有一个边缘、某个关键词或某个邻居信号特别强，最大池化会把它留下来。它更像在问：<span style="background-color: #c0c0c0;">这一小块区域里，最显著的模式有没有出现</span>。</p>
<p>平均池化（Average Pooling）对一组特征取平均，更强调整体趋势而不是最强局部点。它更像在问：这一块区域总体上激活强不强、语义平均水平如何。</p>
<p>求和池化（Sum Pooling）常见于图网络和集合建模，用于累积总量信息。若节点数量本身有意义，求和会把“有多少邻居/总共多强”也编码进去；平均池化则更强调归一化后的平均强度。</p>
<p>全局池化（Global Pooling）表示不再只看局部窗口，而是直接把整张特征图、整段序列或整个节点集合压成一个向量。例如全局平均池化（Global Average Pooling, GAP）会把一整个空间维度平均掉，得到“每个通道在全局上的平均响应”。自适应池化（Adaptive Pooling）则把输出尺寸预先固定，例如无论输入特征图多大，最终都压成 <span displaypfx="inline-" class="mathjax-container">\(1\times 1\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(7\times 7\)</span>。</p>
<div class="blog_h3"><span class="graybg">不同网络里的含义</span></div>
<p>在卷积神经网络（CNN）里，池化最经典的含义是<span style="background-color: #c0c0c0;">沿空间维度做局部下采样</span>。例如一张特征图经过 <span displaypfx="inline-" class="mathjax-container">\(2\times 2\)</span> 最大池化后，宽高会缩小，局部最强响应被保留下来。它的直接收益有三点：减小特征图尺寸、扩大后续层的感受野（Receptive Field）、并降低模型对小幅平移和局部扰动的敏感度。</p>
<p>在时序模型和文本模型里，池化更常表示<span style="background-color: #c0c0c0;">沿时间或序列长度维度做聚合</span>。例如把所有 token 表示做平均池化，得到整句向量；把一段音频帧表示做最大池化，得到“这一整段里最强的模式”。这里池化的重点不再是二维空间下采样，而是把变长序列压成固定长度表示，方便做分类、检索或相似度计算。</p>
<p>在图神经网络（GNN）里，池化有两层常见含义。第一层是邻域聚合（Neighborhood Aggregation）：一个节点把邻居表示做均值、求和或最大值，再更新自己；这可以理解为“节点级局部池化”。第二层是图级读出（Graph-level Readout）：把整张图的节点表示再做一次全局聚合，得到整个图的表示，用于图分类、图回归等任务。</p>
<p>在 Transformer 里，池化通常不再以“池化层”这一模块形式高频出现，但概念仍然存在。句子分类常取 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{CLS}]\)</span> 位置表示，或对所有 token 做平均池化；Embedding 模型也常对最后一层隐藏状态做 mean pooling / max pooling 得到句向量。进一步看，注意力（Attention）本身也可以理解成一种<span style="background-color: #c0c0c0;">带内容依赖的加权聚合</span>：区别只在于普通池化的规则通常固定，而注意力的权重是由输入动态决定的。</p>
<div class="blog_h3"><span class="graybg">池化到底保留了什么、丢掉了什么</span></div>
<p>池化保留的是摘要信息，丢掉的是更精细的位置细节。最大池化更偏向“是否出现过显著模式”，平均池化更偏向“整体平均状态如何”，求和池化则更偏向“总量有多大”。因此，池化总带有一种 trade-off：表示更紧凑、更稳、更省算力，但精确定位能力会下降。</p>
<p>这也是为什么不同任务会选择不同聚合方式。图像分类往往欢迎一定程度的位置不敏感，因此池化很自然；语义检索希望一整句压成一个句向量，因此句级池化很常见；但像语义分割、目标检测、序列标注这类任务，输出本身依赖逐位置判断，就不能过早把位置信息池掉，否则细粒度边界会被抹平。</p>
<div class="blog_h3"><span class="graybg">上采样、插值与转置卷积</span></div>
<p>若说下采样（Downsampling）是在压缩表示，那么上采样（Upsampling）就是反过来<span style="background-color: #c0c0c0;">把较粗的表示扩展回更细的空间分辨率</span>。它做的不是凭空创造新信息，而是把低分辨率特征重新铺回更高分辨率的位置网格中，方便恢复空间结构、边界或局部细节。</p>
<p>插值（Interpolation）是最直接的上采样方式。最邻近插值（Nearest Neighbor Interpolation）相当于把原像素或原特征直接复制到更密的网格里，速度快、实现简单，但边界可能显得生硬；双线性插值（Bilinear Interpolation）则按周围位置做平滑加权，结果更连续，但也更容易把边界抹平。它们的共同特点是：上采样规则是固定的，不需要学习参数。</p>
<p>转置卷积（Transposed Convolution）则是<span style="background-color: #c0c0c0;">可学习的上采样</span>。它不是“把普通卷积矩阵简单转置后就 magically 还原图像”，而是指：若把普通卷积看作一个线性算子，转置卷积对应的是这个线性算子的转置形式，因此能够把较小的特征图映射到较大的特征图。因为它带有可学习卷积核，所以模型可以学习“该怎样把粗特征展开成更适合任务的细特征”。</p>
<p>在视觉任务里，这三者的分工很常见。语义分割、图像生成、超分辨率、U-Net 解码器、扩散模型解码器都需要上采样：有时先用插值把尺寸放大，再接普通卷积细化；有时直接用转置卷积一步完成“放大 + 学习性重组”。前者通常更稳、更少棋盘格伪影（Checkerboard Artifacts），后者表达力更强，但更依赖实现细节与核大小、步幅（Stride）设置。</p>
<div class="blog_h2"><span class="graybg">泛化、过拟合与偏差—方差</span></div>
<p>机器学习更关心模型离开训练集之后能否维持稳定表现。泛化（Generalization）、过拟合（Overfitting）、欠拟合（Underfitting）与偏差—方差权衡（Bias–Variance Tradeoff）描述的，就是这件事。</p>
<div class="blog_h3"><span class="graybg">泛化</span></div>
<p>泛化（Generalization）指模型在未见样本上的表现。若训练数据与未来输入都来自同一数据分布 <span displaypfx="inline-" class="mathjax-container">\(P(X,Y)\)</span>，则模型的总体风险可写成：</p>
<span displaypfx="" class="mathjax-container">\[R(f)=\mathbb{E}_{(x,y)\sim P}\big[\ell(f(x),y)\big]\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(R(f)\)</span> 是真实风险（Population Risk），表示模型 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 在整个真实分布上的平均损失； <span displaypfx="inline-" class="mathjax-container">\(\ell(f(x),y)\)</span> 是单样本损失；期望 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}\)</span> 表示对所有可能样本做平均。训练时真正能看到的只有有限样本，因此优化的通常是经验风险 <span displaypfx="inline-" class="mathjax-container">\(\hat R_n(f)\)</span>，而不是这个理想化的总体风险。</p>
<p>训练误差与测试误差之间的差距，常称为泛化间隙（Generalization Gap）。间隙小说明模型在未见数据上比较稳定；间隙大则说明模型过度依赖训练样本中的偶然细节。</p>
<div class="blog_h3"><span class="graybg">过拟合与欠拟合</span></div>
<p>欠拟合（Underfitting）表示模型过于简单，连训练集里的主结构都学不出来；过拟合（Overfitting）表示模型对训练集学得过细，把噪声、异常点和偶然波动也当成了规律。两者都属于泛化失败，只是失败方式不同。</p>
<p>判断时通常同时看训练误差与验证误差。欠拟合时，训练误差和验证误差都偏高；过拟合时，训练误差可能很低，但验证误差明显更高。前者更像“模型看不懂题”，后者更像“模型把练习册答案背熟了，却没有真正掌握规律”。</p>
<p>过拟合并不只由参数多导致。高维稀疏特征、标签噪声、样本量不足、分布偏移、数据泄露、过长训练时间和过弱正则化，都会放大过拟合风险。欠拟合则常见于模型容量不足、特征表达太弱、训练尚未收敛，或者任务本身需要非线性而模型只允许线性表达。</p>
<div class="blog_h3"><span class="graybg">偏差与方差</span></div>
<p>偏差（Bias）与方差（Variance）是分析泛化误差来源的经典视角。对平方损失（Squared Loss），常见分解写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}\big[(Y-\hat f(X))^2\big]=\mathrm{Bias}^2+\mathrm{Variance}+\sigma^2\]</span>
<p>左边的 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[(Y-\hat f(X))^2]\)</span> 是模型的平均平方误差； <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Bias}^2\)</span> 表示模型平均预测与真实函数之间的系统性偏离； <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Variance}\)</span> 表示模型对训练样本波动的敏感度； <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 是数据本身不可约的噪声（Irreducible Noise），即使模型和训练过程都完美，也无法完全消除。</p>
<p>高偏差通常对应模型表达能力不足，例如用直线去拟合强非线性关系；高方差通常对应模型过于敏感，例如少量训练样本变化就会让决策边界大幅摆动。工程上，降低偏差常靠更强模型、更好特征和更充分训练；降低方差常靠更多数据、正则化、数据增强、早停（Early Stopping）和集成学习（Ensemble Learning）。很多建模决策，本质上都是在这两类误差之间做权衡。</p>
<div class="blog_h2"><span class="graybg">经验风险最小化与正则化</span></div>
<p>大部分监督学习训练都可以概括成同一条主线：先定义单样本损失，再在训练集上取平均形成经验风险，最后通过优化算法把它压低。正则化（Regularization）是在这条主线之上加入额外约束，用来控制复杂度并改善泛化。</p>
<div class="blog_h3"><span class="graybg">经验风险最小化</span></div>
<p>经验风险最小化（Empirical Risk Minimization, ERM）是统计学习的基本训练原则。设训练集为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{D}=\{(x_i,y_i)\}_{i=1}^{n}\)</span>，则经验风险定义为：</p>
<span displaypfx="" class="mathjax-container">\[\hat R_n(f)=\frac{1}{n}\sum_{i=1}^{n}\ell(f(x_i),y_i)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是样本数， <span displaypfx="inline-" class="mathjax-container">\(f(x_i)\)</span> 是模型对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本的预测， <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 是真实标签， <span displaypfx="inline-" class="mathjax-container">\(\ell\)</span> 是损失函数。经验风险最小化就是在假设空间 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\)</span> 中寻找让这条平均损失最小的函数：</p>
<span displaypfx="" class="mathjax-container">\[\hat f_{\mathrm{ERM}}=\arg\min_{f\in\mathcal{H}}\hat R_n(f)\]</span>
<p>这条原则覆盖范围极广。线性回归最小化的是平方误差经验风险，逻辑回归和多分类神经网络最小化的是交叉熵经验风险，序列标注模型最小化的是序列级条件对数似然。算法形式不同，骨架是一致的。</p>
<div class="blog_h3"><span class="graybg">正则化</span></div>
<p>正则化（Regularization）是在经验风险之外，再加入一个偏好“更简单、更平滑、更稳定”解的约束项。常见写法是：</p>
<span displaypfx="" class="mathjax-container">\[\hat f=\arg\min_{f\in\mathcal{H}}\hat R_n(f)+\lambda\,\Omega(f)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\Omega(f)\)</span> 是正则项（Regularizer），刻画模型复杂度； <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 是正则化强度，决定“拟合训练集”和“控制复杂度”之间的权衡。 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 越大，模型越保守；越小，模型越自由。</p>
<p>L2 正则（L2 Regularization）偏好较小权重，常写成 <span displaypfx="inline-" class="mathjax-container">\(\Omega(f)=\|\mathbf{w}\|_2^2\)</span>；L1 正则（L1 Regularization）偏好稀疏解，常写成 <span displaypfx="inline-" class="mathjax-container">\(\Omega(f)=\|\mathbf{w}\|_1\)</span>。更广义地看，早停、Dropout、数据增强、权重共享、标签平滑、参数冻结、低秩适配，都是在不同层面对模型自由度施加约束，因此都可以看作正则化思想的工程实现。</p>
<div class="blog_h2"><span class="graybg">IID 与分布偏移</span></div>
<p>机器学习中的很多训练与评估结论，都建立在一个默认前提上：训练样本与未来样本来自同一统计机制。这个前提通常写成 IID（Independent and Identically Distributed，独立同分布）假设。只要这个前提破坏，训练集表现与线上表现之间就可能出现明显断裂。</p>
<div class="blog_h3"><span class="graybg">IID 假设</span></div>
<p>设样本对 <span displaypfx="inline-" class="mathjax-container">\((x_i,y_i)\)</span> 来自某个联合分布 <span displaypfx="inline-" class="mathjax-container">\(P(X,Y)\)</span>，IID 假设写成：</p>
<span displaypfx="" class="mathjax-container">\[(x_1,y_1),\dots,(x_n,y_n)\overset{\mathrm{iid}}{\sim}P(X,Y)\]</span>
<p>这里“独立（Independent）”表示一个样本是否出现，不影响另一个样本的生成；“同分布（Identically Distributed）”表示所有样本都来自同一个联合分布 <span displaypfx="inline-" class="mathjax-container">\(P(X,Y)\)</span>。这个假设让训练集平均损失能够作为总体风险的近似，也让交叉验证、置信区间和很多泛化理论成立。</p>
<p>IID 是理想化近似，而不是自然界的铁律。时间序列、推荐系统、金融交易、医疗数据、A/B 实验日志、用户行为数据，常常都存在相关性、群组效应、时间漂移或采样偏差，因此不能机械套用 IID 设定。</p>
<div class="blog_h3"><span class="graybg">分布偏移</span></div>
<p>当训练分布与测试分布不一致时，就发生了分布偏移（Distribution Shift）：</p>
<span displaypfx="" class="mathjax-container">\[P_{\mathrm{train}}(X,Y)\neq P_{\mathrm{test}}(X,Y)\]</span>
<p>分布偏移有几种常见形式。协变量偏移（Covariate Shift）指 <span displaypfx="inline-" class="mathjax-container">\(P(X)\)</span> 变化，而 <span displaypfx="inline-" class="mathjax-container">\(P(Y|X)\)</span> 基本稳定；例如线上用户年龄结构变了，但“给定用户画像时是否点击”的规律没明显变。标签偏移（Label Shift）指 <span displaypfx="inline-" class="mathjax-container">\(P(Y)\)</span> 变化，例如欺诈率在促销期突然上升。概念漂移（Concept Drift / Concept Shift）指 <span displaypfx="inline-" class="mathjax-container">\(P(Y|X)\)</span> 本身发生变化，例如垃圾邮件发送策略升级后，原来有效的文本模式不再可靠。</p>
<p>因此，训练集、验证集与测试集的切分不能只追求随机均匀，还必须尽量模拟未来部署环境。若线上是时间推进场景，测试集就应按时间后移；若线上按用户或设备泛化，切分就应按实体隔离；若业务分布持续漂移，还需要做持续监控、重训和再校准。分布偏移不是评估里的边角问题，而是机器学习系统走向生产后的主要失效来源之一。</p>
<div class="blog_h2"><span class="graybg">数据集工程</span></div>
<p>数据集工程（Dataset Engineering）决定了模型看到什么、以什么尺度看到、又会被哪些偏差误导。很多所谓“模型问题”，根源其实是数据问题：标签噪声、分布漂移、类别极不均衡或特征泄漏，都会直接扭曲训练结果。</p>
<div class="blog_h3"><span class="graybg">黄金/白银数据集</span></div>
<p>数据集工程里常见一个实用分层：黄金数据集（Gold Dataset）与白银数据集（Silver Dataset）。黄金数据集通常指由高质量人工标注、规则严格审核或专家确认得到的小而精数据，标签噪声低，适合做最终评测、关键验证集或高价值监督信号；白银数据集则通常来自启发式规则、弱监督（Weak Supervision）、模型打标、日志回收或大规模自动清洗，规模更大、成本更低，但噪声也更高。实际工程中，常见策略不是二选一，而是用白银数据集提供覆盖面和规模，用黄金数据集提供校准、纠偏与最终可信评估。</p>
<div class="blog_h3"><span class="graybg">数据划分</span></div>
<p>数据划分的目标，是把<span style="background-color: #c0c0c0;">学参数、做模型选择、汇报最终结果</span>这三件事严格隔离开。若同一批数据既用来训练参数，又用来调超参数，最后还拿来汇报效果，评估结果通常会乐观得不真实，因为模型已经间接“看过”了答案。</p>
<p>从统计学习角度看，这三类数据分别承担三种不同职责：训练集负责让模型学习参数；验证集负责帮助人或训练流程做工程决策；测试集负责模拟真正的未知数据，给出最后一次、尽量无偏的泛化评估。三者分工清楚，模型评估才有可信度。</p>
<div class="blog_h4"><span class="graybg">训练集</span></div>
<p>训练集（Training Set）用于更新模型参数。监督学习中，训练集包含输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>；模型在这批样本上计算损失（Loss）、反向传播梯度（Gradient）并更新参数，因此训练集直接决定“模型学到了什么”。它回答的问题是：<span style="background-color: #c0c0c0;">在已观测样本上，模型有没有学会输入与输出之间的对应关系</span>。</p>
<p>训练集通常应占数据的大头，因为参数学习需要足够多的样本来稳定估计模式。实践中常见比例是 70% 到 80%，但这不是固定规则：若数据总量非常大，训练集比例可以更高；若数据本来就少，则往往需要把更多精力放在交叉验证（Cross Validation）而不是死守固定比例。</p>
<p>训练集上的误差通常是三者里最低的，这并不说明模型已经具有泛化能力。一个模型完全可能在训练集上表现极好，却只是记住了样本中的噪声与偶然性。训练集成绩更多反映“拟合能力”，而不是“真实上线表现”。</p>
<div class="blog_h4"><span class="graybg">验证集</span></div>
<p>验证集（Validation Set）用于模型选择（Model Selection）和超参数调优（Hyperparameter Tuning）。它不直接参与参数更新，但会影响训练流程中的关键决策，例如学习率（Learning Rate）、正则化强度、模型深度、树的数量、batch size、阈值选择，以及是否执行 Early Stopping。</p>
<p>验证集回答的问题不是“模型能不能学会”，而是：<span style="background-color: #c0c0c0;">在若干候选配置里，哪一个更可能在新数据上表现最好</span>。因此，验证集像训练过程中的“模拟考试”：它不是最终成绩单，但会决定你在训练期间如何改模型、如何调参数、何时停止训练。</p>
<p>验证集通常占总数据的 10% 到 15% 左右。若数据量很小，单独留出一份验证集的代价会较高，此时更常见的做法是使用 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 折交叉验证，让每个样本轮流充当验证数据，以减少一次随机划分带来的偶然性。交叉验证的细节放在后面的“模型评估”部分展开。</p>
<div class="blog_h4"><span class="graybg">测试集</span></div>
<p>测试集（Test Set）用于最终评估模型的泛化能力（Generalization）。它应尽量只在方案冻结之后使用：模型结构、超参数、训练策略、阈值和后处理规则都不再修改时，才在测试集上做一次最终评估。它回答的问题是：<span style="background-color: #c0c0c0;">如果把模型部署到真实世界，它在新样本上的表现大致会怎样</span>。</p>
<p>测试集通常占总数据的 10% 到 15%。它的重要性不在于比例有多大，而在于它必须保持“未参与决策”。如果开发过程中反复查看测试集结果，并据此继续改模型，那么测试集就已经被污染，不再是独立评测，而变成了另一个隐性的验证集。</p>
<p>因此，测试集更像真正的“高考卷”或“盲测集”：它的价值在于最后一次、尽量无偏的评估，而不是参与训练流程本身。</p>
<div class="blog_h4"><span class="graybg">如何划分数据集</span></div>
<p>最常见的简单划分是训练集 / 验证集 / 测试集 = 70% / 15% / 15%，或 80% / 10% / 10%。这种划分适合样本量较大、类别分布较稳定的任务，因为单次随机切分已经足以给出相对稳定的训练与评估结果。</p>
<p>当数据量较小、类别极不平衡、或者不同子群体差异明显时，划分策略就必须更谨慎。分类任务常采用分层抽样（Stratified Split），确保训练、验证、测试三部分的类别比例大致一致；时间序列任务则必须按时间顺序切分，避免未来信息泄漏到过去；用户级、设备级、病人级任务常需要按实体分组切分，防止同一实体的样本同时出现在训练集和测试集中，造成过于乐观的结果。</p>
<p>因此，“如何划分”本身就是建模的一部分。划分方式若与真实部署场景不一致，即使指标很好，也可能只是评估设定过于宽松，而不是真正泛化能力强。</p>
<div class="blog_h4"><span class="graybg">数据泄露</span></div>
<p>数据泄露（Data Leakage）指测试集或验证集中的信息以直接或间接方式进入训练过程，从而导致模型评估结果虚高。它的危险不在于“代码报错”，而在于模型会表现得看似极好，却无法在真实新数据上复现。</p>
<p>最常见的数据泄露有几类。第一类是<span style="background-color: #c0c0c0;">先对全量数据做预处理，再切分数据</span>，例如先用全量数据计算标准化均值和方差，再划分训练 / 测试集；这样测试集的信息已经进入了训练流程。第二类是<span style="background-color: #c0c0c0;">用测试集反复调参</span>，例如每改一次模型就看一次测试集成绩，直到测试集最好看为止。第三类是<span style="background-color: #c0c0c0;">特征中混入未来或标签信息</span>，例如用预测时不可能知道的字段做输入，或把目标变量的某种变形偷偷带进特征。</p>
<p>避免数据泄露的原则只有一句：<span style="background-color: #c0c0c0;">任何依赖数据分布统计量、特征构造规则、模型选择决策或阈值选择的步骤，都只能在训练集内部完成，再把同样的变换应用到验证集和测试集</span>。标准化、特征选择、缺失值填补、目标编码（Target Encoding）、降维（PCA）和重采样（Resampling）都要遵守这一原则。</p>
<p>因此，数据集划分不只是“把数据分三份”这么简单，而是整个实验设计（Experimental Design）的一部分。只有训练集、验证集、测试集的职责边界清晰，交叉验证使用得当，且数据泄露被严格控制，模型指标才具有解释价值和可复现实验意义。</p>
<div class="blog_h3"><span class="graybg">归一化与标准化</span></div>
<p>归一化（Normalization）与标准化（Standardization）都在解决“不同特征量纲和尺度差异过大”问题，但含义不同。最常见的最小-最大归一化把数据映射到固定区间：</p>
<span displaypfx="" class="mathjax-container">\[x'=\frac{x-x_{\min}}{x_{\max}-x_{\min}}\]</span>
<p>它把特征压到 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span>，适合像像素值、比例值这类天然有上下界的量。标准化则是减去均值、再除以标准差：</p>
<span displaypfx="" class="mathjax-container">\[z=\frac{x-\mu}{\sigma}\]</span>
<p>标准化后的特征均值为 0、标准差为 1，更适合线性模型、距离模型和很多神经网络优化过程。类比来看，归一化像“把不同长度的尺子都缩到同一长度区间”；标准化像“先平移到共同中心，再按波动尺度统一单位”。</p>
<div class="blog_h3"><span class="graybg">特征工程</span></div>
<p>特征工程（Feature Engineering）是把原始数据加工成更利于模型学习的表示。它不是“多造点列”这么简单，而是在把领域知识编码进输入空间。例：时间戳可以拆成小时、星期、是否节假日；用户行为日志可以构造近 7 天点击次数、转化率、时间衰减统计；文本可以做 TF-IDF、n-gram 或实体抽取。</p>
<p>类比来看，特征工程像做菜前的备料：同样的原料，如果已经切片、去骨、配好比例，后续烹饪会顺畅得多。经典机器学习对特征工程高度依赖；深度学习则把一部分特征学习自动化了，但在表格数据、推荐、广告和风控里，特征工程仍然决定上限。</p>
<div class="blog_h3"><span class="graybg">类别不平衡处理</span></div>
<p>类别不平衡（Class Imbalance）指某些类别样本远多于另一些类别。欺诈检测、故障检测、医学筛查里最典型：正类往往极少。如果不处理，模型可能通过“永远预测多数类”获得看似不错的 Accuracy，却在关键少数类上彻底失效。</p>
<p>常见处理方法包括：重采样（过采样少数类、欠采样多数类）、类别加权（Class Weighting）、阈值调整（Threshold Tuning）和使用更合适的指标（如 Precision、Recall、PR-AUC）。例如在信用卡欺诈场景中，正类只占 0.1%，此时“全判正常”会有 99.9% Accuracy，但业务价值几乎为 0。</p>
<div class="blog_h2"><span class="graybg">模型评估</span></div>
<p>模型评估（Model Evaluation）回答的不是“模型能不能在训练集上做对”，而是“模型在新数据上是否可靠，以及错误代价如何”。不同任务对应的指标重点不同：分类关心类别区分，回归关心数值偏差，排序关心相对顺序。</p>
<div class="blog_h3"><span class="graybg">交叉验证</span></div>
<p>交叉验证（Cross Validation）在数据较少时特别重要。最常见的 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 折交叉验证把数据分成 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 份：每次用其中 1 份做验证、其余 <span displaypfx="inline-" class="mathjax-container">\(K-1\)</span> 份训练，循环 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 次，最后对 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个验证结果取平均。</p>
<p>它的作用像“轮流把不同一份数据拿出来当模拟考试卷”，从而降低一次随机划分带来的偶然性。对小数据集而言，单次划分可能刚好“运气好或坏”；交叉验证则给出更稳定的泛化估计。</p>
<div class="blog_h3"><span class="graybg">校准</span></div>
<p>校准（Calibration）讨论的是：<span style="background-color: #c0c0c0;">模型给出的概率值，是否真的能当概率解释</span>。分类模型不只输出“判成哪一类”，还常输出一个置信分数，例如 <span displaypfx="inline-" class="mathjax-container">\(0.9\)</span>。若一个模型在所有“预测概率约为 0.9”的样本子集上，最终真的有约 90% 预测正确，那么它就是校准良好的；若它经常把只有 60% 把握的样本说成 90%，就属于过度自信（Overconfident）。</p>
<p>二分类中，若模型输出正类概率 <span displaypfx="inline-" class="mathjax-container">\(\hat p(x)\in[0,1]\)</span>，理想校准条件可写成：</p>
<span displaypfx="" class="mathjax-container">\[P(Y=1\mid \hat p(X)=p)=p\]</span>
<p>这里左边表示：在所有预测概率等于 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 的样本中，真实为正类的条件概率；右边的 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是模型自己报出的概率。两者相等时，概率输出就与真实频率一致。多分类场景下，常把模型最大类别概率当作置信度，并检验“报 80% 置信度的样本，是否真的大约 80% 正确”。</p>
<p>校准与准确率不是同一件事。一个模型可以分类很准，但概率不可靠；也可以概率尺度较准，但分类边界并不最优。前者常见于深层神经网络：argmax 分类结果不错，但 softmax 概率偏尖，置信度系统性偏高。涉及风险控制、医学筛查、自动驾驶、检索重排、多阶段决策时，概率是否可信往往和“分对多少”同样重要，因为阈值决策、人工复核和代价加权都依赖这个概率尺度。</p>
<p>校准的可视化工具通常是可靠性图（Reliability Diagram）。做法是把预测置信度分成若干区间，例如 <span displaypfx="inline-" class="mathjax-container">\([0.0,0.1),[0.1,0.2),\dots\)</span>，然后对每个区间分别计算平均置信度与真实准确率。若图上的点接近对角线 <span displaypfx="inline-" class="mathjax-container">\(y=x\)</span>，说明校准较好；若点普遍落在对角线下方，说明模型报得比实际更自信；若点在对角线上方，则说明模型偏保守。</p>
<p>常用数值指标是期望校准误差（Expected Calibration Error, ECE）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{ECE}=\sum_{m=1}^{M}\frac{|B_m|}{n}\,\big|\mathrm{acc}(B_m)-\mathrm{conf}(B_m)\big|\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 是置信度分箱数， <span displaypfx="inline-" class="mathjax-container">\(B_m\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 个置信度区间中的样本集合， <span displaypfx="inline-" class="mathjax-container">\(|B_m|\)</span> 是该区间样本数， <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 是总样本数， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{acc}(B_m)\)</span> 是该区间的实际准确率， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{conf}(B_m)\)</span> 是该区间的平均预测置信度。ECE 的含义很直接：把每个置信区间里“说得多准”和“实际多准”的差值取绝对值，再按样本占比加权平均。ECE 越小，表示整体校准越好。</p>
<p>另一类常见指标是 Brier Score。对二分类，它定义为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Brier}=\frac{1}{n}\sum_{i=1}^{n}(\hat p_i-y_i)^2\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\hat p_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本的预测正类概率， <span displaypfx="inline-" class="mathjax-container">\(y_i\in\{0,1\}\)</span> 是真实标签。它既惩罚分类错误，也惩罚概率刻度不准，因此兼顾区分能力与概率质量。与单纯 Accuracy 不同，Brier Score 会区分“错得有多离谱”：把一个负样本报成 <span displaypfx="inline-" class="mathjax-container">\(0.51\)</span> 和报成 <span displaypfx="inline-" class="mathjax-container">\(0.99\)</span>，代价并不相同。</p>
<p>工程上最常见的后处理方法是温度缩放（Temperature Scaling）。设原始 logits 为 <span displaypfx="inline-" class="mathjax-container">\(z_i\)</span>，则缩放后的 softmax 概率写成：</p>
<span displaypfx="" class="mathjax-container">\[p_i=\frac{\exp(z_i/T)}{\sum_j \exp(z_j/T)}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(T&gt;0\)</span> 是温度参数。 <span displaypfx="inline-" class="mathjax-container">\(T&gt;1\)</span> 会把分布拉平，降低过度自信； <span displaypfx="inline-" class="mathjax-container">\(T&lt;1\)</span> 会把分布压尖，提高置信度。温度参数通常在验证集上通过最小化负对数似然（Negative Log-Likelihood, NLL）来拟合，然后固定用于测试或部署阶段。它不会改变类别排序，因此常能在几乎不影响 Accuracy 的前提下改善概率校准。</p>
<div class="blog_h3"><span class="graybg">分类指标</span></div>
<div class="blog_h4"><span class="graybg">混淆矩阵（Confusion Matrix）</span></div>
<p>分类指标通常从混淆矩阵（Confusion Matrix）出发。设正类预测结果统计为真阳性 <span displaypfx="inline-" class="mathjax-container">\(TP\)</span>、假阳性 <span displaypfx="inline-" class="mathjax-container">\(FP\)</span>、真阴性 <span displaypfx="inline-" class="mathjax-container">\(TN\)</span>、假阴性 <span displaypfx="inline-" class="mathjax-container">\(FN\)</span>。不同指标本质上是在回答不同问题：是看“总共判对多少”，还是看“判成正类时有多准”，还是看“真实正类抓到了多少”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/confusion-matrix.png"><img class="alignnone size-full wp-image-41785" src="https://blog.gmem.cc/wp-content/uploads/2026/03/confusion-matrix.png" alt="confusion-matrix" width="1024" height="776" /></a></p>
<div class="blog_h4"><span class="graybg">Accuracy</span></div>
<p>准确率（Accuracy）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Accuracy}=\frac{TP+TN}{TP+TN+FP+FN}\]</span>
<p>它衡量“总体上判对了多少比例”，适合类别相对平衡、不同错误代价接近的场景。但在类别极不平衡时会误导：例如癌症筛查里，99% 都是阴性时，模型全判阴性也可能有 99% Accuracy，却毫无检测价值。</p>
<div class="blog_h4"><span class="graybg">Precision</span></div>
<p>精确率（Precision）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Precision}=\frac{TP}{TP+FP}\]</span>
<p>它回答的是：“所有被模型判成正类的样本里，有多少真的为正。”当误报成本很高时，Precision 特别重要。例：垃圾邮件过滤里，如果把正常邮件误判成垃圾邮件代价很高，就要关心 Precision。</p>
<div class="blog_h4"><span class="graybg">Recall</span></div>
<p>召回率（Recall）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Recall}=\frac{TP}{TP+FN}\]</span>
<p>它回答的是：“所有真实正类里，有多少被模型找出来了。”当漏报成本很高时，Recall 更关键。例：医学筛查里漏掉患者可能比多做一次复检更危险，因此 Recall 往往比 Precision 更重要。</p>
<div class="blog_h4"><span class="graybg">F1 Score</span></div>
<p>F1 值（F1 Score）是 Precision 与 Recall 的调和平均：</p>
<span displaypfx="" class="mathjax-container">\[F_1=\frac{2\cdot \mathrm{Precision}\cdot \mathrm{Recall}}{\mathrm{Precision}+\mathrm{Recall}}\]</span>
<p>之所以用调和平均而不是普通平均，是因为它会惩罚“一高一低”的不平衡情况。若一个模型 Precision 极高但 Recall 很低，它并不能拿到高 F1。F1 适合正负样本不平衡、且希望兼顾漏报与误报的场景。</p>
<div class="blog_h4"><span class="graybg">AUC-ROC</span></div>
<p>AUC-ROC 衡量模型在不同分类阈值下区分正负样本的整体能力。ROC 曲线横轴是假阳性率（False Positive Rate），纵轴是真阳性率（True Positive Rate）。AUC 是曲线下面积，范围在 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span>；越接近 1，说明模型越能把正样本排在负样本前面。</p>
<p>它不依赖某一个固定阈值，因此适合比较“排序能力”。但在极端不平衡数据上，PR 曲线（Precision-Recall Curve）常更敏感，因为 ROC 容易被大量真阴性“冲淡”。</p>
<div class="blog_h3"><span class="graybg">回归指标</span></div>
<p>回归指标（Regression Metrics）衡量预测值与真实值之间的数值偏差。它们关注的不是“判对类别”，而是“偏差有多大、对大误差是否敏感、模型解释了多少波动”。以房价预测为例，预测 300 万和真实 320 万之间的差距，就是典型回归误差。</p>
<div class="blog_h4"><span class="graybg">MAE</span></div>
<p>平均绝对误差（Mean Absolute Error, MAE）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{MAE}=\frac{1}{N}\sum_{i=1}^{N}|\hat y_i-y_i|\]</span>
<p>它直接度量“平均差了多少个原始单位”，解释最直观。若房价单位是万元，MAE=12 就表示平均误差约 12 万元。由于使用绝对值，MAE 对离群点没有 MSE 那么敏感。</p>
<div class="blog_h4"><span class="graybg">MSE</span></div>
<p>均方误差（Mean Squared Error, MSE）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{MSE}=\frac{1}{N}\sum_{i=1}^{N}(\hat y_i-y_i)^2\]</span>
<p>平方会放大大误差，因此 MSE 对离群点更敏感。它常用于你希望“大错要被重罚”的场景。高斯噪声假设下，最小化 MSE 还对应最大似然估计，因此它不仅是工程指标，也是概率建模结果。</p>
<div class="blog_h4"><span class="graybg">RMSE</span></div>
<p>均方根误差（Root Mean Squared Error, RMSE）是</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{RMSE}=\sqrt{\mathrm{MSE}}\]</span>
<p>它保留了 MSE 对大误差更敏感的性质，同时把单位拉回原始量纲，因此更易解释。若房价 RMSE 为 20 万元，可以直接理解为“典型误差量级约 20 万元”。</p>
<div class="blog_h4"><span class="graybg">R²</span></div>
<p><span displaypfx="" class="mathjax-container">\[R^2\]</span>（决定系数，Coefficient of Determination）回答的是：<span style="background-color: #c0c0c0;">相比于最朴素的瞎猜基线，你的回归模型到底把预测提升了多少</span>。要理解它，只需要在脑子里放两条线：一条是“什么都不知道时只能猜平均值”的水平线，另一条是模型给出的预测曲线。</p>
<p>先看最朴素的基线。假设你要预测一批房子的价格，但你手里没有面积、地段、楼龄这些特征，别人却逼着你给出预测。此时最不容易挨打的办法，不是胡乱报一个数字，而是对所有房子都猜样本平均价 <span displaypfx="inline-" class="mathjax-container">\(\bar y\)</span>。在散点图上，这对应一条横向的水平线。</p>
<p>真实房价 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 会散落在这条平均线的上下。每个点到平均线的垂直距离 <span displaypfx="inline-" class="mathjax-container">\(y_i-\bar y\)</span>，就是“瞎蒙平均值”时犯下的误差。把这些误差平方后全部加起来，就得到</p>
<span displaypfx="" class="mathjax-container">\[\sum_i(y_i-\bar y)^2\]</span>
<p>这就是公式里的分母。它衡量的不是模型误差，而是这批数据本身原来就有多分散、多混乱。也可以把它理解为目标变量的<span style="background-color: #c0c0c0;">总波动</span>、总混沌程度，或者说“在完全不用特征时，世界原本有多少东西解释不了”。</p>
<p>现在再看你的模型。你训练出一个回归模型，它根据输入特征给出预测 <span displaypfx="inline-" class="mathjax-container">\(\hat y_i\)</span>。在图上，这不再是那条死板的水平线，而是一条试图穿过散点云中心的预测曲线。模型当然不可能完美，所以每个真实值 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 与预测值 <span displaypfx="inline-" class="mathjax-container">\(\hat y_i\)</span> 之间仍会有垂直误差，这个误差就是残差（Residual）。</p>
<p>把这些模型仍然没解释掉的误差平方后加起来，就得到</p>
<span displaypfx="" class="mathjax-container">\[\sum_i(\hat y_i-y_i)^2\]</span>
<p>这就是公式里的分子，也叫残差平方和（Residual Sum of Squares, RSS）。它代表模型已经尽力之后，世界上<span style="background-color: #c0c0c0;">依然残存的混沌</span>。分子越小，说明模型越贴近真实数据；分子越大，说明模型虽然复杂，但其实没把问题解释清楚。</p>
<p>于是</p>
<span displaypfx="" class="mathjax-container">\[R^2=1-\frac{\sum_i(\hat y_i-y_i)^2}{\sum_i(y_i-\bar y)^2}\]</span>
<p>这条式子就可以直接读成一句大白话：先看模型还剩下多少解释不了的波动，再除以最开始总共有多少波动，得到“模型搞不定的比例”；最后用 1 减掉它，剩下的就是<span style="background-color: #c0c0c0;">模型成功解释掉的波动比例</span>。</p>
<p>因此，若 <span displaypfx="inline-" class="mathjax-container">\(R^2=0.8\)</span>，意思不是“正确率 80%”，而是目标变量原本有 100 份波动，模型大约解释掉了其中 80 份，只剩 20 份还没解释；若 <span displaypfx="inline-" class="mathjax-container">\(R^2=0\)</span>，说明你的模型折腾半天，效果和“永远预测平均值”完全一样；若 <span displaypfx="inline-" class="mathjax-container">\(R^2&lt;0\)</span>，则表示模型比这个最朴素基线还差，常见原因是模型设错了、特征没信息，或实现上有 bug。</p>
<p>从老板视角看， <span displaypfx="inline-" class="mathjax-container">\(R^2\)</span> 的灵魂拷问其实只有一句：<span style="background-color: #c0c0c0;">相比直接拿平均值糊弄事，你这个复杂回归模型到底多解释了多少真实波动</span>。这也是为什么 <span displaypfx="inline-" class="mathjax-container">\(R^2\)</span> 特别适合回答“模型有没有真正利用特征学到东西”，但它不能替代 MAE、RMSE——因为 <span displaypfx="inline-" class="mathjax-container">\(R^2\)</span> 讲的是解释比例，而不是误差到底有多少个原始单位。</p>
<div class="blog_h2"><span class="graybg">优化算法</span></div>
<p>优化算法（Optimization Algorithms）解决的问题非常朴素：<span style="background-color: #c0c0c0;">模型参数该往哪个方向改，才能让损失函数持续下降</span>。只要训练目标能写成“最小化某个损失函数”，背后就需要一套更新参数的规则。线性回归、逻辑回归、神经网络、大语言模型训练，本质上都绕不开这个问题。</p>
<p>这里要先分清两件事。优化（Optimization）关心的是“训练损失能不能降下来”；泛化（Generalization）关心的是“模型在新样本上好不好”。一个优化器可能把训练集拟合得很好，但泛化依然一般；反过来，一个优化器如果连训练损失都压不下去，模型通常也谈不上有效。因此优化算法决定的是<span style="background-color: #c0c0c0;">你如何走向一个解</span>，而不是直接保证这个解一定最好。</p>
<div class="blog_h3"><span class="graybg">梯度下降</span></div>
<p>梯度下降（Gradient Descent）是最核心的一阶优化思想。设损失函数为 <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span>，参数为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，则梯度 <span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta L(\theta)\)</span> 给出“损失上升最快的方向”。既然梯度指向上坡，那么要让损失下降，就应沿着它的反方向更新参数：</p>
<span displaypfx="" class="mathjax-container">\[\theta_{t+1}=\theta_t-\eta\,\nabla_\theta L(\theta_t)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\theta_t\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步的参数， <span displaypfx="inline-" class="mathjax-container">\(\eta\)</span> 是学习率（Learning Rate），决定每一步走多大； <span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta L(\theta_t)\)</span> 是当前位置的斜率信息。学习率太大，容易一步跨过谷底甚至震荡发散；学习率太小，又会下降得极慢。它像蒙着雾下山：梯度告诉你脚下哪边更陡，学习率决定你每次迈多大步。</p>
<p>为什么很多模型不直接“解公式”，而要反复迭代？因为在深度学习里，参数维度极高，损失面又往往非凸（Non-convex），通常没有漂亮的闭式解（Closed-form Solution）。这时最现实的办法不是一次算出全局最优，而是利用局部斜率，一步一步把损失往下压。</p>
<p>当总损失是逐样本损失的平均时，梯度下降还可以写得更具体：</p>
<span displaypfx="" class="mathjax-container">\[L(\theta)=\frac{1}{N}\sum_{i=1}^{N}\ell(\theta;x_i)\]</span>
<span displaypfx="" class="mathjax-container">\[\nabla_\theta L(\theta)=\frac{1}{N}\sum_{i=1}^{N}\nabla_\theta \ell(\theta;x_i)\]</span>
<p>这也回答了“为什么可以批量喂输入”：梯度对求和是线性的，<span style="background-color: #c0c0c0;">整体梯度等于逐样本梯度的平均</span>，所以可以并行算每个样本的梯度，再求平均后统一更新参数。</p>
<p>满足这种“逐样本求和/求平均”结构的损失函数其实非常多。典型例子包括：线性回归里的均方误差（MSE）、平均绝对误差（MAE），二分类里的 logistic loss / binary cross-entropy，多分类里的交叉熵（Cross-Entropy），语言模型训练里的负对数似然（Negative Log-Likelihood, NLL）。它们都可以写成“每个样本先各自算一份损失，再在整个数据集上取平均”的形式，因此天然适合 mini-batch 训练。</p>
<p>但并不是所有目标都能严格写成这种完全独立的逐样本平均。若损失显式依赖<span style="background-color: #c0c0c0;">样本之间的相对关系</span>，情况就会复杂一些。例如排序学习里的 pairwise/listwise loss、度量学习中的 triplet loss，以及对比学习里的 InfoNCE，都会让一个样本的损失依赖同一个 batch 中的其他样本。此时虽然仍然可以按 batch 计算梯度，但它已经不是“每个样本各算各的、最后简单平均”那么干净的分解了。</p>
<p>工程上还常见另一种情况：目标函数可以写成“逐样本平均损失 + 正则项（Regularization）”。例如</p>
<span displaypfx="" class="mathjax-container">\[L(\theta)=\frac{1}{N}\sum_{i=1}^{N}\ell(\theta;x_i)+\lambda\Omega(\theta)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\Omega(\theta)\)</span> 可以是 <span displaypfx="inline-" class="mathjax-container">\(\|\theta\|_2^2\)</span> 这类参数惩罚项。前半部分仍然按样本分解，后半部分则是直接作用于参数本身，而不对应某一个单独样本。很多现代训练目标，本质上都是这两部分的组合。</p>
<p>几个常用训练量需要先分清：</p>
<ul>
<li>批大小（Batch Size）：一次更新使用多少个样本。</li>
<li>步（Step / Iteration）：一次参数更新。</li>
<li>轮（Epoch）：完整遍历一次训练集。</li>
</ul>
<p>若训练集大小为 <span displaypfx="inline-" class="mathjax-container">\(N\)</span>，batch size 为 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>，则每个 epoch 的步数约为 <span displaypfx="inline-" class="mathjax-container">\(\lceil N/B\rceil\)</span>。工程里经常会说“训练了多少 step”，因为真正发生参数变化的是 step，而不是 epoch 这个更粗的计数单位。</p>
<div class="blog_h4"><span class="graybg">批量梯度下降（BGD）</span></div>
<p>批量梯度下降（Batch Gradient Descent, BGD）每次都用<span style="background-color: #c0c0c0;">整个训练集</span>来计算一次精确梯度，再更新参数。这里的 batch 指的不是现代深度学习里常说的一个 mini-batch，而是“整批训练数据”。它的优点是方向最稳定、梯度方差最小；缺点是每一步都很贵，数据一大就几乎不可用。它更适合小数据集、凸优化问题，或教材里说明“梯度下降原理”时使用。</p>
<div class="blog_h4"><span class="graybg">随机梯度下降（SGD）</span></div>
<p>随机梯度下降（Stochastic Gradient Descent, SGD）每次只用一个样本的梯度做更新。它的方向噪声很大，看起来像“跌跌撞撞地下山”，但每一步极便宜、更新极频繁，因此在数据流式到来或样本极多时很有价值。</p>
<ul>
<li>优点：更新快、内存开销小、天然适合在线学习（Online Learning）与流式数据（Streaming Data）。</li>
<li>优点：梯度噪声有时反而是好事，更容易离开鞍点（Saddle Point）和较差的局部极小。</li>
<li>缺点：轨迹抖动大，若学习率控制不好，训练容易不稳定。</li>
</ul>
<p>它像店长根据每一位新顾客的反馈立刻调价：反应很快，但也容易被个别顾客带偏。</p>
<p>适用场景：当你需要<span style="background-color: #c0c0c0;">用最新样本尽快产生参数更新</span>时，SGD（batch size=1）是最直接的选择，典型包括：</p>
<ul>
<li>在线学习（Online Learning）/流式数据（Streaming Data）：样本持续到来，要求增量更新，而不是离线反复扫全量数据。</li>
<li>分布漂移（Distribution Shift）与非平稳（Non-stationary）环境：用户偏好、市场、策略对抗等持续变化，需要快速跟踪新分布。</li>
<li>低延迟更新需求：例如广告/推荐/风控的在线校准，需要“见到一条新反馈就更新一点”。</li>
<li>资源受限或样本极大：内存无法容纳大 batch 或无法频繁计算全量梯度时，用单样本更新换取更低的每步计算与存储成本。</li>
</ul>
<p>在现代深度学习里，若使用 GPU/TPU 训练，大多数时候会用小批量梯度下降来兼顾吞吐与稳定性；纯 SGD 更常出现在在线/增量训练与部分强化学习（Reinforcement Learning, RL）设置中。</p>
<div class="blog_h4"><span class="graybg">小批量梯度下降（Mini-batch SGD）</span></div>
<p>小批量梯度下降（Mini-batch SGD）是在 BGD 与 SGD 之间折中：每次用一个 batch 的平均梯度更新。它既能利用 GPU 的并行算力，又保留一定梯度噪声，因此现代深度学习几乎都在这个范式下训练。</p>
<p>batch 太小，梯度估计噪声会很大；batch 太大，虽然每步更稳定，但显存压力更高、更新频率更低，有时还会让优化和泛化都变钝。因此 batch size 不是“越大越好”，而是吞吐、稳定性、显存和泛化之间的折中。</p>
<div class="blog_h3"><span class="graybg">动量法（Momentum）</span></div>
<p>单纯的梯度下降在“峡谷形”损失面里很容易左右来回震荡：沿陡峭方向上下摆动，沿真正有用的谷底方向前进却很慢。动量法（Momentum）的直觉是：不要只看当前这一脚的斜率，而要把过去几步的方向累积成一种“惯性”。</p>
<span displaypfx="" class="mathjax-container">\[v_{t+1}=\beta v_t+(1-\beta)g_t,\quad \theta_{t+1}=\theta_t-\eta v_{t+1}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 是当前梯度， <span displaypfx="inline-" class="mathjax-container">\(v_t\)</span> 是累计出来的速度， <span displaypfx="inline-" class="mathjax-container">\(\beta\in[0,1)\)</span> 控制“记住过去多少信息”。 <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span> 越大，方向越平滑、惯性越强；越小，则越接近普通梯度下降。类比来看，它像推一个有重量的小球下山：不会因为脚下的微小凸凹就频繁改道，而会沿长期更一致的下降方向滚动。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/momentum.jpg"><img class="alignnone size-full wp-image-41017" src="https://blog.gmem.cc/wp-content/uploads/2026/03/momentum.jpg" alt="momentum" width="100%" /></a></p>
<div class="blog_h3"><span class="graybg">AdaGrad / RMSProp</span></div>
<p>AdaGrad 与 RMSProp 属于自适应学习率（Adaptive Learning Rate）方法。它们的核心思想是：<span style="background-color: #c0c0c0;">不同参数的梯度尺度不同，不应该所有维度都用同一个固定步长</span>。历史上梯度很大的维度，后续步子要缩小；梯度稀疏或很小的维度，则可以走得更积极。</p>
<p>AdaGrad 累积历史平方梯度：</p>
<span displaypfx="" class="mathjax-container">\[s_{t+1}=s_t+g_t\odot g_t,\quad \theta_{t+1}=\theta_t-\eta\,\frac{g_t}{\sqrt{s_{t+1}}+\epsilon}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(g_t\odot g_t\)</span> 表示逐元素平方， <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 是数值稳定项。这样一来，历史上梯度一直很大的维度会被自动缩小学习率。AdaGrad 在稀疏特征（Sparse Features）任务里很有效，例如早期的文本与推荐场景；但它的问题是 <span displaypfx="inline-" class="mathjax-container">\(s_t\)</span> 只增不减，训练到后期学习率可能衰减得过头，参数几乎不再动。</p>
<p>RMSProp 用指数滑动平均替代“无限累积”，缓解这个问题：</p>
<span displaypfx="" class="mathjax-container">\[s_{t+1}=\rho s_t+(1-\rho)(g_t\odot g_t),\quad \theta_{t+1}=\theta_t-\eta\,\frac{g_t}{\sqrt{s_{t+1}}+\epsilon}\]</span>
<p>这样历史信息会逐步“遗忘”，使学习率缩放更关注近期梯度尺度。可以把 AdaGrad 理解成“终身记账”，而 RMSProp 更像“滚动记账”。</p>
<div class="blog_h3"><span class="graybg">Adam</span></div>
<p>Adam（Adaptive Moment Estimation）把动量法（Momentum）的一阶矩估计与 RMSProp 的二阶矩估计结合起来，因此它同时解决优化中的两个核心痛点：<span style="background-color: #c0c0c0;">方向感（往哪走）</span>与<span style="background-color: #c0c0c0;">节奏感（每步走多大）</span>。直觉上，可以把它理解为“带惯性的方向盘 + 自动变速箱”：方向由平均梯度决定，步长由梯度的尺度自动缩放。</p>
<div class="blog_h4"><span class="graybg">一阶矩：方向感（Momentum）</span></div>
<span displaypfx="" class="mathjax-container">\[m_{t+1}=\beta_1 m_t+(1-\beta_1)g_t\]</span>
<ul>
<li><span style="background-color: #c0c0c0;">索引约定</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 次更新先在 <span displaypfx="inline-" class="mathjax-container">\(\theta_t\)</span> 处计算 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span>，再把它并入动量得到 <span displaypfx="inline-" class="mathjax-container">\(m_{t+1}\)</span>；下标 <span displaypfx="inline-" class="mathjax-container">\(t+1\)</span> 表示“更新后状态”，不是未来信息。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(m_{t+1}\)</span>：并入 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 后的一阶矩（动量）估计。</li>
<li>右边第一项 <span displaypfx="inline-" class="mathjax-container">\(\beta_1 m_t\)</span>：把历史动量按系数 <span displaypfx="inline-" class="mathjax-container">\(\beta_1\)</span> 保留下来，表示“历史方向对新方向的贡献”。</li>
<li>右边第二项 <span displaypfx="inline-" class="mathjax-container">\((1-\beta_1)g_t\)</span>：把当前梯度按系数 <span displaypfx="inline-" class="mathjax-container">\(1-\beta_1\)</span> 注入动量，表示“当前观测对新方向的贡献”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(g_t\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步的梯度，通常由当前 mini-batch 估计得到（<span displaypfx="inline-" class="mathjax-container">\(g_t=\nabla_\theta L(\theta_t)\)</span>）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(m_t\)</span>：一阶矩估计的滑动平均（动量项），可理解为“平均梯度方向”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\beta_1\in[0,1)\)</span>：动量衰减系数，越大表示记忆越长、方向越平滑。</li>
</ul>
<p>两项系数满足 <span displaypfx="inline-" class="mathjax-container">\(\beta_1+(1-\beta_1)=1\)</span>，因此这是指数滑动平均（Exponential Moving Average, EMA，越近发生的事情，参考价值越大；越久远的事情，参考价值越小）。当 <span displaypfx="inline-" class="mathjax-container">\(m_0=0\)</span> 时，可把它展开为：</p>
<span displaypfx="" class="mathjax-container">\[m_{t+1}=(1-\beta_1)\sum_{k=0}^{t}\beta_1^{t-k}g_k\]</span>
<p>上式说明：越久远的梯度 <span displaypfx="inline-" class="mathjax-container">\(g_k\)</span> 权重按 <span displaypfx="inline-" class="mathjax-container">\(\beta_1^{t-k}\)</span> 指数衰减；经验上可把有效记忆长度理解为 <span displaypfx="inline-" class="mathjax-container">\(O\!\left(\frac{1}{1-\beta_1}\right)\)</span>。因此 <span displaypfx="inline-" class="mathjax-container">\(\beta_1\)</span> 越大，平均窗口越长，方向越平滑但响应越慢；<span displaypfx="inline-" class="mathjax-container">\(\beta_1\)</span> 越小，平均窗口越短，方向更敏捷但更易受噪声影响。</p>
<div class="blog_h4"><span class="graybg">二阶矩：节奏感（RMSProp）</span></div>
<p>RMSProp（Root Mean Square Propagation）用梯度平方的指数滑动平均（Exponential Moving Average, EMA）来估计每个参数维度的“尺度”，再用该尺度对当前梯度做逐元素归一化，从而把更新步伐控制在更稳定的量级。</p>
<span displaypfx="" class="mathjax-container">\[v_{t+1}=\beta_2 v_t+(1-\beta_2)(g_t\odot g_t),\quad \tilde g_t=\frac{g_t}{\sqrt{v_{t+1}}+\epsilon}\]</span>
<ul>
<li><span style="background-color: #c0c0c0;">索引约定</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 次更新先在 <span displaypfx="inline-" class="mathjax-container">\(\theta_t\)</span> 处计算 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span>，再用它更新得到 <span displaypfx="inline-" class="mathjax-container">\(v_{t+1}\)</span>；用 <span displaypfx="inline-" class="mathjax-container">\(v_{t+1}\)</span> 缩放 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 表示“用本次更新后的尺度估计做归一化”，不涉及未来信息。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(v_t\)</span>：上一轮更新结束后的二阶原点矩估计（EMA 状态，“更新前的尺度缓存”）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(v_{t+1}\)</span>：梯度的二阶原点矩（Second Raw Moment）估计，逐（梯度）维近似 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]\)</span>，刻画“历史梯度大小”的典型尺度。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(g_t\odot g_t\)</span>：当前梯度的逐元素平方（<span displaypfx="inline-" class="mathjax-container">\(\odot\)</span> 为 Hadamard 乘积），只保留幅度信息。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sqrt{v_{t+1}}\)</span>：均方根（Root Mean Square, RMS）尺度；平方使量纲变为 <span displaypfx="inline-" class="mathjax-container">\(g^2\)</span>，开方把量纲还原到 <span displaypfx="inline-" class="mathjax-container">\(g\)</span>，从而可与 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 相除。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\tilde g_t\)</span>：按 RMS 尺度归一化后的梯度；分式表示逐元素相除（每个参数维度各自缩放）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\beta_2\in[0,1)\)</span>：衰减系数，控制尺度估计的记忆长度；越大表示对历史尺度更“长记忆”。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span>：数值稳定项，避免分母为 0 或过小。</li>
</ul>
<p>该缩放把“方向”和“尺度”解耦：方向仍由 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 给出；步长由 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{v_{t+1}}\)</span> 自适应调节。若某维长期梯度偏大，则 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{v_{t+1}}\)</span> 变大、该维更新被压小；若某维长期梯度偏小，则分母较小、该维相对步子更大。</p>
<p>若把 RMSProp 作为独立优化器使用，参数更新可写为 <span displaypfx="inline-" class="mathjax-container">\(\theta_{t+1}=\theta_t-\eta\,\tilde g_t\)</span>。在 Adam 中，同样的 RMS 缩放作用在一阶矩估计上：用 <span displaypfx="inline-" class="mathjax-container">\(\hat m_{t+1}\)</span> 替代 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 作为分子，并在下一节通过偏置修正得到 <span displaypfx="inline-" class="mathjax-container">\(\hat v_{t+1}\)</span> 作为分母。</p>
<div class="blog_h4"><span class="graybg">偏置修正：冷启动校正（Bias Correction）</span></div>
<p>因为 <span displaypfx="inline-" class="mathjax-container">\(m_0=v_0=0\)</span>，训练初期的滑动平均会系统性偏小（“没热起来”）。Adam 用偏置修正把它拉回合理尺度：</p>
<span displaypfx="" class="mathjax-container">\[\hat m_{t+1}=\frac{m_{t+1}}{1-\beta_1^{t+1}},\quad \hat v_{t+1}=\frac{v_{t+1}}{1-\beta_2^{t+1}}\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\hat m_{t+1},\hat v_{t+1}\)</span>：偏置修正后的估计，用来抵消初始化为 0 导致的早期偏小。</li>
<li>分母 <span displaypfx="inline-" class="mathjax-container">\(1-\beta^{t+1}\)</span>：校正“冷启动偏小”的缩放因子。由于初始化为 0，指数滑动平均在经历 <span displaypfx="inline-" class="mathjax-container">\(t+1\)</span> 次更新时只积累了约 <span displaypfx="inline-" class="mathjax-container">\(1-\beta^{t+1}\)</span> 的有效权重，因此会偏小；除以它相当于把估计值按同样比例放大回去。</li>
</ul>
<p>更严格地说：若假设 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g_t]=\mu\)</span> 近似稳定，则由递推可得 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[m_{t+1}]=(1-\beta_1^{t+1})\mu\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(\hat m_{t+1}=\frac{m_{t+1}}{1-\beta_1^{t+1}}\)</span> 是把它改成近似无偏估计。同理可得 <span displaypfx="inline-" class="mathjax-container">\(\hat v_{t+1}\)</span> 的修正。</p>
<div class="blog_h4"><span class="graybg">参数更新：方向 ÷ 尺度</span></div>
<span displaypfx="" class="mathjax-container">\[\theta_{t+1}=\theta_t-\eta\,\frac{\hat m_{t+1}}{\sqrt{\hat v_{t+1}}+\epsilon}\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\theta_t\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步参数（权重向量/张量）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\eta\)</span>：学习率（全局步长系数）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sqrt{\hat v_{t+1}}\)</span>：逐元素开方；整项 <span displaypfx="inline-" class="mathjax-container">\(\frac{\hat m_{t+1}}{\sqrt{\hat v_{t+1}}+\epsilon}\)</span> 表示逐元素相除。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span>：数值稳定项，避免除以 0 或极小数。</li>
</ul>
<p>分子给方向，分母给节奏。一个极端例子能看出为什么 Adam 用 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]\)</span> 而不是方差：若某维梯度连续很多步都是 <span displaypfx="inline-" class="mathjax-container">\(g_t=10\)</span>，方差为 0 会导致除法不稳定；但 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[g^2]\approx 100\)</span> 给出稳定尺度，最终更新量级约为 <span displaypfx="inline-" class="mathjax-container">\(10/\sqrt{100}=1\)</span>（再加 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 保底）。</p>
<p>Adam 往往更易调参、早期收敛更快，因此在 Transformer、扩散模型和很多深度网络里是默认起点。但它并不是无条件最好：有些任务上，最终泛化仍可能不如精心调过的 SGD。实践中，配合权重衰减（Weight Decay）时，通常会使用 AdamW，把权重衰减从梯度自适应缩放里解耦出来，效果更稳。</p>
<div class="blog_h3"><span class="graybg">AdamW</span></div>
<p>AdamW（Adam with Decoupled Weight Decay）把权重衰减（Weight Decay）从 Adam 的自适应梯度缩放里<span style="background-color: #c0c0c0;">解耦</span>出来。原因是：在 Adam 这类自适应方法里，如果你把 L2 正则化写进损失（等价于把 <span displaypfx="inline-" class="mathjax-container">\(\lambda\theta\)</span> 加到梯度里），这个正则项也会被二阶矩 <span displaypfx="inline-" class="mathjax-container">\(\hat v_t\)</span> 的缩放影响，从而导致不同参数维度的“衰减强度”不再可控。</p>
<p>正则化本来应该是一个可控的、与梯度尺度无关的收缩力度；解耦后 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 的含义更稳定。</p>
<p>对比两种写法会更清楚：</p>
<ul>
<li>把 L2 正则化并入目标（常被口语化地称为“weight decay”，但在自适应优化器里并不等价）：先算 <span displaypfx="inline-" class="mathjax-container">\(g_t=\nabla_\theta L(\theta_t)+\lambda\theta_t\)</span>，再把这个 <span displaypfx="inline-" class="mathjax-container">\(g_t\)</span> 送入 Adam 的 <span displaypfx="inline-" class="mathjax-container">\(m_t,v_t\)</span> 与自适应步长。</li>
<li>AdamW（解耦权重衰减）：先算纯数据梯度 <span displaypfx="inline-" class="mathjax-container">\(g_t=\nabla_\theta L(\theta_t)\)</span> 并完成 Adam 的自适应更新，然后单独对参数做一次权重衰减。</li>
</ul>
<p>AdamW 的参数更新可写成：</p>
<span displaypfx="" class="mathjax-container">\[\theta_{t+1}=\theta_t-\eta\,\frac{\hat m_{t+1}}{\sqrt{\hat v_{t+1}}+\epsilon}-\eta\,\lambda\,\theta_t\]</span>
<ul>
<li>前半段 <span displaypfx="inline-" class="mathjax-container">\(-\eta\,\frac{\hat m}{\sqrt{\hat v}+\epsilon}\)</span>：Adam 的自适应梯度更新（由数据损失驱动）。</li>
<li>后半段 <span displaypfx="inline-" class="mathjax-container">\(-\eta\,\lambda\,\theta_t\)</span>：解耦的 weight decay（参数按比例收缩），其中 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 是衰减强度。</li>
</ul>
<p>其中第一项是 Adam 的自适应更新，第二项是独立的权重衰减（等价于每步把权重按比例拉向 0）。这种分离让 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 更像一个真正可解释的“收缩强度”，在 Transformer 等模型上通常更稳定、更好调。工程实现里常见做法是：对 bias 与归一化层参数（如 LayerNorm 的 <span displaypfx="inline-" class="mathjax-container">\(\gamma,\beta\)</span>）不做 weight decay，以免对尺度/偏置项造成不必要的收缩。</p>
<div class="blog_h3"><span class="graybg">Muon</span></div>
<p>Muon 是一种面向神经网络隐藏层（Hidden Layer）权重矩阵（Weight Matrix）的优化器。它与 Adam/AdamW 的根本差异在于：AdamW 会为每个参数维度单独估计更新尺度，并对梯度/更新量做逐元素（Element-wise）归一化；Muon 则把一个二维权重张量视为一个整体，直接利用矩阵结构来塑造更新方向。因此，Muon 更像一种<span style="background-color: #c0c0c0;">矩阵感知（Matrix-aware）的优化器</span>。</p>
<p>它的核心做法不是改变“沿梯度下降”这个大方向，而是改变“更新张量应该具有什么几何形状”。典型写法可以概括为：先对梯度做动量（Momentum）累积，再把这个矩阵更新做一次正交化（Orthogonalization）后处理，然后才真正更新参数：</p>
<span displaypfx="" class="mathjax-container">\[M_{t+1}=\beta M_t+(1-\beta)G_t\]</span>
<span displaypfx="" class="mathjax-container">\[\Delta W_t=\mathrm{Orth}(M_{t+1}),\quad W_{t+1}=W_t-\eta\,\Delta W_t\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(G_t\)</span> 是当前梯度矩阵， <span displaypfx="inline-" class="mathjax-container">\(M_t\)</span> 是动量缓冲， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Orth}(\cdot)\)</span> 表示把更新矩阵变换到更“接近正交（Orthogonal）”的形状。工程实现里，这一步通常用 Newton-Schulz 迭代（Newton-Schulz Iteration）高效近似完成，因此 Muon 常被概括为“对动量更新做正交化”。</p>
<ul>
<li>优势：对线性层/MLP/注意力里的二维权重矩阵，Muon 往往能给出更结构化的更新方向；在一些大模型训练设定下，它的训练效率与收敛速度优于 AdamW。</li>
<li>边界：Muon 不是全参数通吃的默认方案。它通常只用于隐藏层的二维参数；embedding、bias、归一化参数、输出层等非二维或语义不同的参数，实践中常继续交给 AdamW。</li>
<li>工程特征：它强调更新矩阵的谱结构（Spectral Structure），而不是单个坐标的独立缩放；因此超参数分组与参数类型划分比 AdamW 更重要。</li>
</ul>
<p>可以把 AdamW 与 Muon 的差别记成一句话：<span style="background-color: #c0c0c0;">AdamW 解决“每个参数该走多大步”，Muon 进一步关心“整个矩阵应该以什么形状移动”</span>。因此 Muon 更像是隐藏层矩阵优化的专用工具，而不是对所有参数一视同仁的通用默认项。</p>
<div class="blog_h3"><span class="graybg">学习率调度</span></div>
<p>学习率（Learning Rate）往往是最敏感的超参数之一。即使优化器相同，只要学习率设错，训练就可能完全失败。学习率调度（Learning Rate Scheduling）/退火（Annealing）的核心思想是：前期用相对大的步长快速下降，后期逐渐减小步长做精细收敛。</p>
<p>常见调度方式包括：</p>
<ul>
<li>Step decay：按 epoch 或 step 乘以固定因子衰减，简单直接。</li>
<li>Linear decay：线性下降到较小值或 0，常用于大模型训练后段。</li>
<li>Warmup + decay：先小步热身，再进入正常学习率，最后逐步衰减；对 Transformer 和大 batch 训练尤其常见。</li>
<li>余弦退火（Cosine Annealing）：将学习率从较大值逐步衰减到较小值，把优化从“高温探索”过渡到“低温收敛”，在训练后期降低梯度噪声（gradient noise）与过冲（overshoot）风险，使参数能在极小值附近稳定细化。余弦曲线在起点与终点的一阶导数为 0，避免阶梯式衰减的突变，因而衰减更平滑、后期更柔和。</li>
</ul>
<p>Warmup 为什么有用？因为训练刚开始时，参数还处在一个非常“生”的区域，梯度统计不稳定，若一上来就用很大学习率，模型容易发散。先用较小步长热身几百或几千步，再拉到目标学习率，通常更稳。</p>
<p>余弦退火的典型写法（从 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\max}\)</span> 衰减到 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\min}\)</span>）为：</p>
<span displaypfx="" class="mathjax-container">\[\eta(t)=\eta_{\min}+\frac{1}{2}(\eta_{\max}-\eta_{\min})\left(1+\cos\left(\pi\frac{t}{T}\right)\right)\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\eta(t)\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步使用的学习率。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\eta_{\max}\)</span> / <span displaypfx="inline-" class="mathjax-container">\(\eta_{\min}\)</span>：一个退火周期内的最大学习率与最小学习率。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(t\)</span>：当前步数（通常从 0 开始计），<span displaypfx="inline-" class="mathjax-container">\(T\)</span>：该周期的总步数。</li>
</ul>
<p>这个公式可以直接做两次代入来验证边界：当 <span displaypfx="inline-" class="mathjax-container">\(t=0\)</span> 时，<span displaypfx="inline-" class="mathjax-container">\(\cos(0)=1\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(\eta(0)=\eta_{\max}\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(t=T\)</span> 时，<span displaypfx="inline-" class="mathjax-container">\(\cos(\pi)=-1\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(\eta(T)=\eta_{\min}\)</span>。中间学习率按半个余弦周期平滑下降。</p>
<p>通过余弦（Cosine）提供了一个非常干净的<span style="background-color: #c0c0c0;">端点平滑（smooth endpoints）</span>性质：在起点和终点处变化率为 0。对上式求导可得</p>
<span displaypfx="" class="mathjax-container">\[\frac{d\eta}{dt}=-\frac{1}{2}(\eta_{\max}-\eta_{\min})\frac{\pi}{T}\sin\left(\pi\frac{t}{T}\right)\]</span>
<p>因此 <span displaypfx="inline-" class="mathjax-container">\(\sin(0)=\sin(\pi)=0\)</span>，学习率在周期开始与结束都不会出现突兀的“拐点”。相比之下，step decay 有不连续跳变，linear decay 在末端通常会突然到达下限并停止变化（出现不光滑的折点）。在非凸深度网络里，这种平滑退火往往更稳：前期保持较大步长便于探索，后期自然减小步长便于精细收敛。</p>
<p>工程上常见扩展是余弦重启（Cosine Annealing with Warm Restarts, SGDR）：把训练过程切成多个周期，每个周期把 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 重新从 0 开始计（并可逐周期拉长 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>）。在标准 SGDR 中，<span style="background-color: #c0c0c0;">学习率在周期边界是不连续的</span>：它会在一个周期末端衰减到 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\min}\)</span>，并在下一个周期起点从 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\max}\)</span> 重新开始（参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 不会重置）。</p>
<p>这种“重启”的作用不是为了制造噪声，而是把优化过程从“后期小步精修”短暂切回“较大步长探索”，以应对非凸问题中的平台区（plateau）与次优盆地（suboptimal basin）。学习率拉高会增大每步更新幅度与梯度噪声（gradient noise）的有效影响，帮助轨迹跳出当前区域并探索新的吸引域；而如果当前区域确实是更稳健的平坦极小值（flat minimal），后续退火通常会把参数再次拉回并在附近更精细地收敛。工程上常见做法是把 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\max}\)</span> 设在“不会破坏稳定性”的范围内；若重启瞬间仍担心不稳定，也可以在每次重启后加一个很短的 warmup，让学习率在少量步数内从较小值爬升到 <span displaypfx="inline-" class="mathjax-container">\(\eta_{\max}\)</span>。</p>
<p>没有一种调度策略对所有任务都最好。真正有效的做法是结合损失曲线、梯度稳定性、验证集表现和训练预算一起看：如果前期降不动，往往学习率偏小；如果剧烈震荡甚至发散，往往学习率偏大；如果后期长期卡在平台区，通常需要更细的衰减策略。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/sgdr.jpg"><img class="alignnone size-full wp-image-41059" src="https://blog.gmem.cc/wp-content/uploads/2026/03/sgdr.jpg" alt="sgdr" width="100%" /></a></p>
<div class="blog_h2"><span class="graybg">集成策略（Ensemble Learning）</span></div>
<p>集成学习（Ensemble Learning）的核心思想是：<span style="background-color: #c0c0c0;">不要只信一个模型，而是让多个模型共同投票或共同修正</span>。它背后的统计直觉是：不同模型的误差如果不完全一致，组合后往往比单个模型更稳。类比来看，这像让多个医生会诊：一个人可能看偏，但几个人的综合意见通常更可靠。</p>
<div class="blog_h3"><span class="graybg">Bagging</span></div>
<p>Bagging（Bootstrap Aggregating）的核心做法是对训练集进行多次自助采样（bootstrap sampling），即反复执行有放回采样（sampling with replacement）生成多个训练子集，并在这些子集上分别训练基模型，以降低方差（Variance）。由于每个模型见到的数据子集略有差异，学到的决策边界不会完全一致；最后对预测结果做平均或多数投票，可抵消一部分过拟合噪声。</p>
<p>例：如果单棵决策树很容易被训练集中的偶然样本带偏，那么训练 100 棵在不同 bootstrap 样本上的树，再投票，通常会比只用 1 棵树稳定得多。这也是随机森林的核心直觉。</p>
<div class="blog_h3"><span class="graybg">Boosting</span></div>
<p>Boosting 的思路与 Bagging 相反：它不是“并行训练很多彼此独立的模型”，而是“串行训练一串弱学习器（Weak Learners），让后一个模型专门修正前一个模型没做好的部分”。因此它更像“老师批改作业”：每一轮都盯着上轮最容易出错的题继续强化。</p>
<p>以加法模型视角看，Boosting 逐步构造</p>
<span displaypfx="" class="mathjax-container">\[F_M(x)=\sum_{m=1}^{M}\alpha_m h_m(x)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(h_m(x)\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 个弱学习器， <span displaypfx="inline-" class="mathjax-container">\(\alpha_m\)</span> 是它的权重。公式不是为了堆符号，而是在表达：最终模型是“很多个简单模型的加权和”，每一轮都往当前模型上加一小块“修正项”。</p>
<div class="blog_h3"><span class="graybg">Stacking</span></div>
<p>Stacking（堆叠集成）不只是平均多个模型输出，而是再训练一个元学习器（Meta-Learner）去学习“什么时候该信哪个模型”。例如一个基模型擅长处理稀疏文本特征，另一个擅长处理数值特征，Stacking 可以学会在不同样本上动态加权它们。</p>
<p>它像“专家会诊 + 总负责人”：若文本模型和表格模型各有专长，元模型就负责综合判断谁在当前病例上更可信。</p>
<div class="blog_h2"><span class="graybg">机器学习编程</span></div>
<p>机器学习编程（Machine Learning Programming）处理的核心问题是：当一个模型被写成代码并真正运行起来时，<span style="background-color: #c0c0c0;">谁负责组织训练流程，谁负责表达数学操作，谁又负责在具体硬件上把这些操作算快</span>。这三层通常分别对应编程框架（Framework）、算子（Operator）和内核（Kernel）。理解这三个层次，有助于把“模型公式”与“工程实现”连接起来。</p>
<p>它们不是彼此独立的三个名词，而是一条自上而下的执行链。研究者或工程师先在框架里定义模型结构与训练流程；框架再把模型拆成一系列算子，例如矩阵乘法、卷积、归一化、激活、softmax；每个算子最终还需要落到某个硬件相关的内核实现上，才能在 CPU、GPU、TPU 或其他加速器上真正执行。因此，<span style="background-color: #c0c0c0;">框架决定开发体验，算子决定计算图语义，内核决定实际运行效率</span>。</p>
<div class="blog_h3"><span class="graybg">框架（Framework）</span></div>
<p>框架（Framework）并不是单一层次的概念。在现代机器学习工程里，至少要区分两层：一层是基础框架（Foundational Framework），负责张量（Tensor）、自动求导（Automatic Differentiation）、算子调度与底层执行；另一层是高层框架（High-level Framework），负责把某一类模型、某一类训练范式或某一类工程流程封装成更直接的接口。前者提供“地基”，后者提供“脚手架”和“现成结构”。</p>
<p>因此，PyTorch、TensorFlow、JAX 这类系统更适合放在基础框架层；而 Transformers、DeepSpeed、ModelScope、Lightning 这类工具，则更适合放在高层框架层。它们之间不是替代关系，而是典型的上下层关系：<span style="background-color: #c0c0c0;">高层框架通常建立在基础框架之上，用更强的任务抽象、更少的模板代码和更完整的工程能力，把常见训练与推理流程直接组织起来</span>。</p>
<div class="blog_h3"><span class="graybg">常见框架简介</span></div>
<p>从工程使用频率看，开发者最常接触的是一组已经按职责分层的常见框架：高层框架负责组织训练与推理流程，基础框架负责真正执行张量计算，某些系统还进一步偏向执行优化与部署。把这些名字放回分层结构里理解，比孤立记忆框架名称更清楚。</p>
<div class="blog_h4"><span class="graybg">高层框架（High-level Framework）</span></div>
<p>高层框架的核心价值在于，它们不重新发明张量计算和自动求导，而是在基础框架之上增加更强的任务语义、训练编排、模型生态或分布式能力。很多工程里，开发者日常接触最多的其实是这一层。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">高层框架</td>
<td style="text-align: center;">主要依赖的基础框架</td>
<td style="text-align: center;">核心定位</td>
<td style="text-align: center;">替你封装了什么</td>
<td style="text-align: center;">最适合的场景</td>
<td style="text-align: center;">与基础框架的关系</td>
</tr>
</thead>
<tbody>
<tr>
<td>Transformers</td>
<td>以 PyTorch 为主，也支持 TensorFlow 与 JAX / Flax</td>
<td>预训练 Transformer 模型与任务头生态</td>
<td>模型定义、权重加载、tokenizer / processor、Trainer、pipeline、任务头</td>
<td>NLP、LLM、多模态模型的微调与推理</td>
<td>通常不是自己实现底层训练系统，而是调用底层框架完成张量计算与反向传播</td>
</tr>
<tr>
<td>DeepSpeed</td>
<td>主要建立在 PyTorch 之上</td>
<td>大模型训练与推理优化框架</td>
<td>ZeRO、参数分片、优化器状态管理、分布式训练编排、推理加速</td>
<td>超大模型训练、多卡 / 多机扩展、显存受限训练</td>
<td>本质上是对 PyTorch 训练过程的增强与重写，而不是替代 PyTorch</td>
</tr>
<tr>
<td>Unsloth</td>
<td>主要建立在 PyTorch 之上，并与 Transformers、PEFT、TRL 等 Hugging Face 生态深度协同</td>
<td>面向 LLM 微调与对齐的性能导向高层框架</td>
<td>快速加载与训练配方、QLoRA / DoRA / RL 配置、量化微调、长上下文训练、导出到 GGUF / Ollama / vLLM / Hugging Face</td>
<td>单卡或少卡进行 LLM 微调、偏好对齐、消费级显卡上的高性价比实验</td>
<td>它不替代 PyTorch 或 Transformers，而是在它们之上把“高效微调 + 导出部署”这条链路进一步封装并做性能优化</td>
</tr>
<tr>
<td>ModelScope</td>
<td>主要建立在 PyTorch 之上，也兼容其他学习框架与平台能力</td>
<td>模型社区 + SDK + 训练 / 推理工作流</td>
<td>模型获取、pipeline、训练入口、评测、部署衔接、领域模型集成</td>
<td>中文生态、多模态任务、快速调用开源模型并做微调</td>
<td>更像“模型平台层 + 高层开发框架”，下层训练仍要落到基础框架执行</td>
</tr>
<tr>
<td>PyTorch Lightning</td>
<td>PyTorch</td>
<td>训练流程组织框架</td>
<td>Trainer、设备放置、日志、checkpoint、验证循环、分布式训练模板</td>
<td>希望保留 PyTorch 灵活性，同时减少训练样板代码</td>
<td>把 PyTorch 代码组织得更规范，但底层模型与梯度仍然是 PyTorch</td>
</tr>
<tr>
<td>Accelerate</td>
<td>PyTorch</td>
<td>分布式训练与多设备执行抽象</td>
<td>多 GPU / 多机启动、混合精度、设备管理、统一训练脚本适配</td>
<td>想在尽量少改代码的前提下把 PyTorch 训练扩展到分布式环境</td>
<td>不取代 PyTorch 训练代码，而是让同一份 PyTorch 代码更容易跨设备运行</td>
</tr>
<tr>
<td>Keras 3</td>
<td>可运行在 TensorFlow、JAX、PyTorch 之上，推理还可对接 OpenVINO</td>
<td>高层模型开发接口</td>
<td>Layer / Model 抽象、训练接口、callback、分布式 API、生态组件</td>
<td>需要高层建模接口且希望在多后端之间切换</td>
<td>处于基础框架之上，强调统一建模接口，而不是直接取代底层后端</td>
</tr>
<tr>
<td>Sentence Transformers</td>
<td>主要建立在 Transformers 与 PyTorch 之上</td>
<td>Embedding 与 reranker 高层框架</td>
<td>文本向量化、相似度训练、检索 / rerank 训练器、评测工具</td>
<td>语义检索、向量召回、文本匹配、reranking</td>
<td>属于面向特定任务族的高层框架，下层依赖 Transformers 与 PyTorch</td>
</tr>
<tr>
<td>MMEngine / OpenMMLab</td>
<td>主要建立在 PyTorch 之上</td>
<td>通用训练引擎与视觉算法框架底座</td>
<td>Runner、Hook、Config、数据流、训练 / 验证 / 测试流程组织</td>
<td>检测、分割、姿态估计等视觉任务</td>
<td>用统一工程抽象组织 PyTorch 训练，尤其适合复杂视觉实验体系</td>
</tr>
</tbody>
</table>
<p>这一层最容易让人产生“它自己就能训练模型”的直觉，但从执行链条看，它们大多只是把训练过程组织得更高级。以 Transformers 为例，<span style="background-color: #c0c0c0;">Trainer 可以发起训练，但底层的张量、梯度、优化器、自动求导与设备执行，通常仍然由 PyTorch 负责</span>；Lightning、Accelerate、DeepSpeed 也是同样的逻辑，只是它们封装的侧重点不同。</p>
<div class="blog_h4"><span class="graybg">Unsloth</span></div>
<p>Unsloth 最适合放在高层框架这一层理解。它并不重新定义 Tensor、自动求导（Autograd）或底层计算图，而是在 PyTorch、Transformers、PEFT、TRL 这一整套既有生态之上，把大语言模型（Large Language Model, LLM）的高频训练流程重新组织成更偏性能导向的开发体验。它解决的核心问题不是“怎样从零实现神经网络”，而是<span style="background-color: #c0c0c0;">怎样在尽可能小的显存和尽可能少的工程样板下，把 LLM 微调、对齐、导出与部署这条链路跑通</span>。</p>
<p>从开发者视角看，Unsloth 的典型工作流通常仍然是“加载 Hugging Face 模型 → 挂接 PEFT 适配器 → 用监督微调（SFT）或强化学习（RL）训练 → 导出到下游推理栈”。它的价值在于把这条链上几个最痛的环节一起压平：第一，量化微调（如 LoRA / QLoRA）与长上下文训练更容易直接落地；第二，针对 GRPO 等对齐训练给出更直接的入口；第三，把训练后的模型或适配器导出到 GGUF、Ollama、vLLM、SGLang、Hugging Face 等下游环境的路径做得更短。换言之，Unsloth 不是另起炉灶，而是把已有生态串得更紧，并对其中最贵的显存、最长的上下文和最复杂的导出环节做专项优化。</p>
<p>它与其他高层框架的边界也需要分清。Transformers 的优势在于模型家族与任务头生态最通用；Accelerate 更像多设备执行抽象；DeepSpeed 更偏分布式训练、参数分片与大规模集群优化；Unsloth 则把重心压在“单机到小规模多卡场景下，如何更快、更省显存地完成 LLM 微调与对齐”。因此，它尤其适合消费级 GPU、本地实验、小团队快速验证、Notebook 驱动的训练流程，以及以 LoRA / QLoRA / RL 为主的大模型增量训练。若任务是超大规模集群预训练或需要复杂的跨机并行策略，DeepSpeed / Megatron 一类系统通常仍然更中心；若任务是通用模型调用与标准微调，Transformers 仍然是最基础的入口。</p>
<p>工程上可以把 Unsloth 看成“面向 LLM 微调的高性能工作台”。它一端连接 Hugging Face 模型与适配器生态，另一端连接本地推理、GGUF、Ollama、vLLM、SGLang 等部署路径，中间则用一套更偏性能调优的训练封装把它们粘起来。对初学者，它降低的是上手门槛：少改几处配置就能跑通量化微调或 RL；对熟悉生态的工程师，它降低的是试验成本：同样的显存预算下，能尝试更长上下文、更复杂的对齐流程，或更快地把训练结果导出到不同推理栈。它的本质依然是高层工程抽象，而不是底层深度学习框架的替代品。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">比较维度</td>
<td style="text-align: center;">Transformers</td>
<td style="text-align: center;">DeepSpeed</td>
<td style="text-align: center;">Unsloth</td>
</tr>
</thead>
<tbody>
<tr>
<td>核心目标</td>
<td>统一模型与任务接口</td>
<td>放大训练规模与显存效率</td>
<td>压低 LLM 微调 / 对齐门槛并提升单机效率</td>
</tr>
<tr>
<td>最擅长的问题</td>
<td>模型加载、任务头、Trainer、pipeline</td>
<td>ZeRO、分布式并行、大模型集群训练</td>
<td>QLoRA、GRPO、长上下文、快速导出部署</td>
</tr>
<tr>
<td>典型使用者</td>
<td>几乎所有 NLP / LLM 开发者</td>
<td>大模型平台与多机训练团队</td>
<td>本地实验者、个人开发者、小团队 LLM 微调工程师</td>
</tr>
<tr>
<td>与 PyTorch 的关系</td>
<td>调用其张量与训练能力</td>
<td>增强其训练与并行系统</td>
<td>在其之上重组 LLM 微调与导出流程</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">基础框架（Foundational Framework）</span></div>
<p>基础框架直接定义张量运算、计算图、自动求导、优化器、算子调度与设备执行能力。它们离硬件更近，也离“神经网络真正怎样被算出来”更近。高层框架能否存在，首先取决于这一层是否提供了足够稳定和强大的底座。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">基础框架</td>
<td style="text-align: center;">核心抽象</td>
<td style="text-align: center;">主要优势</td>
<td style="text-align: center;">最适合的场景</td>
<td style="text-align: center;">典型局限</td>
</tr>
</thead>
<tbody>
<tr>
<td>PyTorch</td>
<td>Tensor、<pre class="crayon-plain-tag">nn.Module</pre>、autograd、动态图（Dynamic Computation Graph）</td>
<td>灵活、直观、研究生态最强，训练与调试体验优秀</td>
<td>研究、论文复现、大模型训练、需要自定义训练逻辑的任务</td>
<td>如果完全手写训练循环，工程样板代码较多，部署链路常需额外工具配合</td>
</tr>
<tr>
<td>TensorFlow</td>
<td>Tensor、Layer / Model、自动求导、图执行与编译</td>
<td>训练与部署体系完整，服务化、端侧与工业链路成熟</td>
<td>企业级生产环境、需要完整训练到部署闭环的场景</td>
<td>研究阶段的编码与调试直观性通常不如 PyTorch</td>
</tr>
<tr>
<td>JAX</td>
<td>数组（Array）+ 函数变换 + XLA 编译</td>
<td>编译优化强，函数式表达清晰，适合大规模并行数值计算</td>
<td>需要强编译能力、自定义并行策略、科研数值实验的任务</td>
<td>函数式编程习惯要求更高，工程生态相对更偏高级用户</td>
</tr>
<tr>
<td>PaddlePaddle</td>
<td>Tensor、动态图 / 静态图、训练与产业工具链</td>
<td>中文生态与产业落地支持强，训练推理工具链完整</td>
<td>产业应用、教育场景、中文任务与本土化生态</td>
<td>国际社区规模与通用论文实现数量通常少于 PyTorch</td>
</tr>
</tbody>
</table>
<p>如果把机器学习工程比作建楼，那么基础框架提供的是钢筋、水泥、电路和承重结构。高层框架之所以能让开发速度显著提升，正是因为底层这些张量、梯度和算子能力已经由基础框架稳定提供。</p>
<div class="blog_h4"><span class="graybg">执行与部署系统（Execution and Deployment Stack）</span></div>
<p>除了高层框架和基础框架，还存在一类经常与“框架”混称、但职责不同的系统：执行与部署系统。它们的核心目标不是让用户更方便地写模型，而是让已经定义好的模型在特定硬件上更快、更省、更稳定地运行。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">系统</td>
<td style="text-align: center;">主要定位</td>
<td style="text-align: center;">最常见作用</td>
<td style="text-align: center;">典型场景</td>
<td style="text-align: center;">与前两层的关系</td>
</tr>
</thead>
<tbody>
<tr>
<td>ONNX Runtime</td>
<td>跨框架推理执行系统</td>
<td>加载 ONNX 图，做图优化、算子调度与多后端执行</td>
<td>统一部署、跨框架导出后的推理执行</td>
<td>位于模型定义之后，更接近运行时（Runtime）而不是训练框架</td>
</tr>
<tr>
<td>TensorRT</td>
<td>NVIDIA GPU 推理优化系统</td>
<td>图优化、层融合、量化与 kernel 自动选择</td>
<td>低延迟在线推理、高吞吐批量服务</td>
<td>通常接收上层框架导出的模型，再做更深的硬件侧优化</td>
</tr>
<tr>
<td>OpenVINO</td>
<td>Intel 硬件推理栈</td>
<td>模型转换、图优化、Intel CPU / iGPU / VPU 推理</td>
<td>Intel 服务器与边缘设备部署</td>
<td>更接近部署后端，而不是通用训练框架</td>
</tr>
<tr>
<td>TVM</td>
<td>深度学习编译栈</td>
<td>自动调优、代码生成、异构硬件适配</td>
<td>边缘部署、自定义芯片、性能工程</td>
<td>站在算子和内核之间，为不同硬件生成更优执行实现</td>
</tr>
</tbody>
</table>
<p>因此，讨论“框架”时最好先分清是哪一层：<span style="background-color: #c0c0c0;">高层框架负责把任务和流程组织起来，基础框架负责把张量与梯度真正算出来，执行与部署系统负责把已经定义好的模型在目标硬件上跑到更优</span>。这三层一旦混在一起，很多看似相近的名词就会失去边界。</p>
<div class="blog_h3"><span class="graybg">算子（Operator）</span></div>
<p>算子（Operator）是计算图（Computation Graph）的基本运算单元。它定义一个明确的数学变换：输入什么张量（Tensor）、输出什么张量、张量的形状（Shape）和数据类型（Data Type）如何变化，以及反向传播时梯度如何计算。框架层写出的模块、层、网络块，最终都会被拆解成一串更细粒度的算子。</p>
<p>从工程实现上看，算子这一层负责表达“数学语义”。例如一个线性层（Linear Layer）会被拆成 MatMul 与 Bias Add；一个自注意力（Self-Attention）模块会被拆成 Q/K/V 投影、MatMul、Scale、Mask、Softmax、再一次 MatMul、Dropout、LayerNorm 等。高层模块能否被编译优化，本质上取决于这些算子能否被识别、融合与高效执行。</p>
<p>常用算子可以分成四大类：线性代数与张量形状类、神经网络前向计算类、序列与索引操作类、训练与优化类。下面的表格按这一方式展开。</p>
<div class="blog_h4"><span class="graybg">线性代数与张量形状类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">算子</td>
<td style="text-align: center;">核心作用</td>
<td style="text-align: center;">常见输入 / 输出</td>
<td style="text-align: center;">典型出现位置</td>
<td style="text-align: center;">实现与使用要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>MatMul / GEMM</td>
<td>矩阵乘法，完成线性投影与特征混合</td>
<td>二维或更高维张量，输出新的线性组合</td>
<td>全连接层、注意力投影、MLP</td>
<td>最核心的高密度算子之一，常直接决定训练吞吐</td>
</tr>
<tr>
<td>Batch MatMul</td>
<td>批量矩阵乘法，多个矩阵对并行相乘</td>
<td>三维及以上张量</td>
<td>多头注意力（Multi-Head Attention）</td>
<td>常与转置、缩放、mask 连用</td>
</tr>
<tr>
<td>Add / Bias Add</td>
<td>逐元素加法</td>
<td>两个可广播张量</td>
<td>残差连接、偏置项、特征融合</td>
<td>常被融合到前后算子中减少访存</td>
</tr>
<tr>
<td>Sub / Mul / Div</td>
<td>逐元素减法、乘法、除法</td>
<td>逐元素张量运算</td>
<td>归一化、门控、缩放</td>
<td>广播规则必须和张量形状匹配</td>
</tr>
<tr>
<td>Scale</td>
<td>用常数或向量对张量缩放</td>
<td>输入张量与标量 / 向量</td>
<td>attention 中的 <span displaypfx="inline-" class="mathjax-container">\(1/\sqrt{d_k}\)</span> 缩放</td>
<td>经常被编译器与前后算子融合</td>
</tr>
<tr>
<td>Transpose / Permute</td>
<td>重排维度顺序</td>
<td>输入张量到相同元素、不同布局的张量</td>
<td>NCHW / NHWC 转换，多头维度重排</td>
<td>逻辑上不改值，但常改变内存访问模式</td>
</tr>
<tr>
<td>Reshape / View</td>
<td>改变张量形状而不改变元素总数</td>
<td>同样的数据，不同 shape</td>
<td>展平、分头、合并头、batch 展开</td>
<td>若内存不连续，可能触发额外复制</td>
</tr>
<tr>
<td>Expand / BroadcastTo</td>
<td>按广播规则扩展维度</td>
<td>低维张量扩展为高维张量</td>
<td>偏置广播、mask 扩展</td>
<td>逻辑扩展不一定真实复制数据</td>
</tr>
<tr>
<td>Squeeze / Unsqueeze</td>
<td>删除或插入长度为 1 的维度</td>
<td>维度数变化，数据值不变</td>
<td>batch / channel 维调整</td>
<td>常用于接口对齐和算子拼接</td>
</tr>
<tr>
<td>Concat / Stack</td>
<td>拼接多个张量</td>
<td>多个同类型张量合并为一个</td>
<td>多特征合并、多分支网络</td>
<td>Concat 沿已有维度拼接，Stack 会新增维度</td>
</tr>
<tr>
<td>Split / Chunk</td>
<td>将一个张量拆成多个子张量</td>
<td>一个张量拆成若干块</td>
<td>Q/K/V 切分、多分支路径</td>
<td>与 Concat、Stack 常成对出现</td>
</tr>
<tr>
<td>ReduceSum / ReduceMean / ReduceMax</td>
<td>沿某些维度做聚合</td>
<td>高维张量压缩成低维张量</td>
<td>池化、loss 聚合、统计量计算</td>
<td>归约（Reduction）通常对并行实现要求较高</td>
</tr>
<tr>
<td>EinSum</td>
<td>用爱因斯坦求和规则表达复合张量运算</td>
<td>多个张量到一个张量</td>
<td>复杂线性代数、注意力原型实现</td>
<td>表达力强，但实际性能往往依赖后端是否能分解优化</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">神经网络前向计算类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">算子</td>
<td style="text-align: center;">核心作用</td>
<td style="text-align: center;">常见输入 / 输出</td>
<td style="text-align: center;">典型出现位置</td>
<td style="text-align: center;">实现与使用要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>Convolution</td>
<td>局部感受野加权求和</td>
<td>特征图与卷积核，输出新的特征图</td>
<td>CNN、视觉 backbone、语音模型</td>
<td>步幅、填充、分组卷积会显著影响性能与感受野</td>
</tr>
<tr>
<td>Depthwise / Group Convolution</td>
<td>按通道组或单通道卷积</td>
<td>特征图到特征图</td>
<td>MobileNet、轻量视觉网络</td>
<td>减少计算量，但对 kernel 实现要求更高</td>
</tr>
<tr>
<td>Pooling（Max / Avg）</td>
<td>局部下采样与聚合</td>
<td>特征图压缩为空间更小的特征图</td>
<td>CNN、时序聚合</td>
<td>降低分辨率与计算量，也带来信息损失</td>
</tr>
<tr>
<td>Adaptive Pooling</td>
<td>把输入压到固定输出尺寸</td>
<td>任意空间尺寸到固定尺寸</td>
<td>视觉分类头、全局池化</td>
<td>方便不同输入尺寸统一到下游全连接层</td>
</tr>
<tr>
<td>ReLU</td>
<td>负值截断为 0 的激活函数</td>
<td>逐元素非线性变换</td>
<td>MLP、CNN、分类头</td>
<td>实现简单，稀疏性强</td>
</tr>
<tr>
<td>GELU</td>
<td>平滑激活，保留小负值的连续变化</td>
<td>逐元素非线性变换</td>
<td>Transformer、LLM MLP</td>
<td>现代语言模型最常见的激活之一</td>
</tr>
<tr>
<td>SiLU / Swish</td>
<td>输入与 sigmoid 门控的乘积</td>
<td>逐元素非线性变换</td>
<td>高性能视觉与语言模型</td>
<td>平滑、效果稳定，常见于新型 backbone</td>
</tr>
<tr>
<td>Sigmoid</td>
<td>把实数映射到 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span></td>
<td>逐元素概率化</td>
<td>门控单元、二分类输出、多标签任务</td>
<td>饱和区梯度小，深层网络中通常不作主激活</td>
</tr>
<tr>
<td>Tanh</td>
<td>把实数映射到 <span displaypfx="inline-" class="mathjax-container">\((-1,1)\)</span></td>
<td>逐元素非线性变换</td>
<td>早期 RNN、门控结构</td>
<td>零中心，但同样存在饱和问题</td>
</tr>
<tr>
<td>Softmax</td>
<td>把一组分数归一化为概率分布</td>
<td>类别分数到概率</td>
<td>多分类头、attention 权重</td>
<td>常与交叉熵和 mask 配合，数值稳定性关键</td>
</tr>
<tr>
<td>LayerNorm</td>
<td>对单个样本的最后若干维做归一化</td>
<td>输入张量到同 shape 张量</td>
<td>Transformer、LLM</td>
<td>不依赖 batch 统计量，适合变长序列</td>
</tr>
<tr>
<td>BatchNorm</td>
<td>利用 batch 统计量做归一化</td>
<td>输入张量到同 shape 张量</td>
<td>CNN、视觉任务</td>
<td>训练与推理行为不同，小 batch 时效果可能下降</td>
</tr>
<tr>
<td>RMSNorm</td>
<td>基于均方根做归一化</td>
<td>输入张量到同 shape 张量</td>
<td>许多现代大语言模型</td>
<td>比 LayerNorm 更简洁，计算更轻</td>
</tr>
<tr>
<td>Attention / SDPA</td>
<td>基于相似度对值向量加权聚合</td>
<td>Q、K、V 到上下文表示</td>
<td>Transformer、跨模态模型</td>
<td>高层看是算子族，底层常映射到 FlashAttention 等 kernel</td>
</tr>
<tr>
<td>Embedding Lookup</td>
<td>根据离散索引查表取向量</td>
<td>token id 到 embedding 向量</td>
<td>NLP、推荐系统、类别特征</td>
<td>本质是参数矩阵的索引读取，不是普通 MatMul</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">序列与索引操作类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">算子</td>
<td style="text-align: center;">核心作用</td>
<td style="text-align: center;">常见输入 / 输出</td>
<td style="text-align: center;">典型出现位置</td>
<td style="text-align: center;">实现与使用要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>Gather</td>
<td>按给定索引抽取元素或切片</td>
<td>源张量与索引张量</td>
<td>embedding、beam search、采样</td>
<td>访问模式离散，容易受内存带宽限制</td>
</tr>
<tr>
<td>Scatter / ScatterAdd</td>
<td>按索引写回或累加</td>
<td>目标张量、索引、更新值</td>
<td>图神经网络、稀疏更新</td>
<td>并发写冲突和原子操作代价常是性能瓶颈</td>
</tr>
<tr>
<td>Index Select</td>
<td>按某一维选取指定位置</td>
<td>张量与一维索引</td>
<td>子序列抽取、类别筛选</td>
<td>语义上比通用 gather 更窄，但常更清晰</td>
</tr>
<tr>
<td>Slice / Narrow</td>
<td>截取连续区间</td>
<td>大张量切出子张量</td>
<td>窗口注意力、局部特征抽取</td>
<td>若数据连续，可几乎零开销视图化</td>
</tr>
<tr>
<td>Mask Fill / Select</td>
<td>按布尔掩码选择或填充值</td>
<td>张量与 mask</td>
<td>attention mask、padding 屏蔽</td>
<td>对变长序列与非法位置处理非常关键</td>
</tr>
<tr>
<td>Where</td>
<td>按条件在两个值之间选择</td>
<td>条件张量与候选张量</td>
<td>条件计算、loss 屏蔽、数值裁剪</td>
<td>本质是逐元素条件分支</td>
</tr>
<tr>
<td>Argmax / Argmin</td>
<td>返回最大 / 最小值所在索引</td>
<td>张量到索引</td>
<td>分类预测、贪心解码</td>
<td>输出是位置而非概率，通常不可导</td>
</tr>
<tr>
<td>TopK</td>
<td>返回前 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个值及其索引</td>
<td>张量到值与索引</td>
<td>检索、beam search、采样截断</td>
<td>常与排序、候选筛选结合</td>
</tr>
<tr>
<td>Sort / Argsort</td>
<td>排序并返回顺序</td>
<td>张量到排序结果</td>
<td>排序损失、候选重排</td>
<td>复杂度高，尽量只在必要路径中使用</td>
</tr>
<tr>
<td>Pad</td>
<td>在边界补零或补指定值</td>
<td>原张量到更大张量</td>
<td>卷积前处理、batch 对齐、序列补齐</td>
<td>padding 策略会影响有效计算比例</td>
</tr>
<tr>
<td>Pack / Unpack Sequence</td>
<td>压缩或还原变长序列表示</td>
<td>变长序列与紧凑表示之间转换</td>
<td>RNN、语音与时序模型</td>
<td>用于减少 padding 带来的无效计算</td>
</tr>
<tr>
<td>Position Encoding Add</td>
<td>注入位置信息</td>
<td>token 表示与位置编码</td>
<td>Transformer、序列模型</td>
<td>绝对位置、相对位置、RoPE 在实现形式上不同</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">训练与优化类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">算子</td>
<td style="text-align: center;">核心作用</td>
<td style="text-align: center;">常见输入 / 输出</td>
<td style="text-align: center;">典型出现位置</td>
<td style="text-align: center;">实现与使用要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>Dropout</td>
<td>训练时随机失活部分单元</td>
<td>输入张量到稀疏化后的张量</td>
<td>MLP、attention、分类头</td>
<td>训练和推理行为不同，推理阶段通常关闭</td>
</tr>
<tr>
<td>Cross-Entropy Loss</td>
<td>衡量预测分布与真实类别的差距</td>
<td>logits 与标签到标量损失</td>
<td>分类、语言模型、token 分类</td>
<td>实现中常与 log-softmax 融合提高稳定性</td>
</tr>
<tr>
<td>NLL Loss</td>
<td>负对数似然损失</td>
<td>对数概率与标签到损失</td>
<td>分类任务、序列建模</td>
<td>通常接在 log-softmax 之后</td>
</tr>
<tr>
<td>MSE Loss</td>
<td>均方误差</td>
<td>预测值与目标值到损失</td>
<td>回归、蒸馏、表示对齐</td>
<td>对异常值较敏感</td>
</tr>
<tr>
<td>L1 / SmoothL1 Loss</td>
<td>绝对误差或平滑绝对误差</td>
<td>预测值与目标值到损失</td>
<td>目标检测、鲁棒回归</td>
<td>比 MSE 对异常值更稳</td>
</tr>
<tr>
<td>KL Divergence</td>
<td>衡量两个分布之间的差异</td>
<td>两个概率分布到标量</td>
<td>知识蒸馏、VAE、分布对齐</td>
<td>输入通常需要是概率或对数概率</td>
</tr>
<tr>
<td>Backward / Gradient</td>
<td>沿计算图反向传播梯度</td>
<td>损失到各参数梯度</td>
<td>所有训练流程</td>
<td>框架自动求导的核心能力就体现在这里</td>
</tr>
<tr>
<td>Gradient Clip</td>
<td>限制梯度范数或幅值</td>
<td>梯度到裁剪后梯度</td>
<td>RNN、大模型训练</td>
<td>控制梯度爆炸，提升训练稳定性</td>
</tr>
<tr>
<td>Optimizer Step（SGD / Adam / AdamW）</td>
<td>根据梯度更新参数</td>
<td>参数、梯度、状态到新参数</td>
<td>每一步训练迭代</td>
<td>常被实现为 fused optimizer kernel 以减少开销</td>
</tr>
<tr>
<td>Weight Decay</td>
<td>对参数施加正则化收缩</td>
<td>参数到受约束更新</td>
<td>分类、语言模型、视觉模型</td>
<td>现代实现常与 AdamW 解耦</td>
</tr>
<tr>
<td>AllReduce</td>
<td>跨设备聚合梯度或统计量</td>
<td>多卡张量到同步后的张量</td>
<td>数据并行训练</td>
<td>严格说更接近通信算子，但在训练图中极常见</td>
</tr>
<tr>
<td>AllGather / ReduceScatter</td>
<td>跨设备收集或切分张量</td>
<td>多设备张量通信</td>
<td>张量并行、序列并行、ZeRO</td>
<td>大模型分布式训练不可缺少</td>
</tr>
</tbody>
</table>
<p>因此，算子层是连接“模型定义”和“底层执行”的语义中枢。看懂模型实际调用了哪些算子，基本就等于看懂了它在做哪些数学步骤，以及这些步骤能否被进一步融合和加速。</p>
<div class="blog_h3"><span class="graybg">内核（Kernel）</span></div>
<p>内核（Kernel）是算子的底层实现。它规定了某个算子如何映射到具体硬件：线程如何组织、数据如何分块、是否使用共享内存（Shared Memory）、是否调用向量化指令、是否走 Tensor Core、是否把多个小算子融合成一次执行。若说算子定义的是“做什么”，那么内核定义的就是“怎样在这台机器上把它做快”。</p>
<p>从性能工程角度看，内核层回答的是：<span style="background-color: #c0c0c0;">同一个数学算子，针对不同硬件、数据形状、精度格式和访存模式，哪种实现最优</span>。这也是为什么表面上同样是 MatMul、LayerNorm 或 Attention，不同框架和后端的速度会相差很大。</p>
<p>常见内核或内核族可以按下表理解：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">内核 / 内核族</td>
<td style="text-align: center;">主要服务的算子</td>
<td style="text-align: center;">典型硬件 / 后端</td>
<td style="text-align: center;">核心优化手段</td>
<td style="text-align: center;">最典型的收益</td>
<td style="text-align: center;">常见场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>cuBLAS GEMM kernel</td>
<td>MatMul、Linear、Batch MatMul</td>
<td>NVIDIA GPU</td>
<td>tile blocking、Tensor Core、流水线、寄存器复用</td>
<td>把矩阵乘法做到接近硬件峰值吞吐</td>
<td>全连接层、attention 投影、MLP</td>
</tr>
<tr>
<td>cuDNN Convolution kernel</td>
<td>Convolution、Pooling、部分归一化与激活</td>
<td>NVIDIA GPU</td>
<td>direct convolution、im2col + GEMM、Winograd、FFT 自动选择</td>
<td>按输入形状自动切换最优卷积路径</td>
<td>CNN、视觉 backbone、语音前端</td>
</tr>
<tr>
<td>oneDNN / MKL kernel</td>
<td>MatMul、Convolution、Normalization</td>
<td>x86 CPU</td>
<td>SIMD 向量化、cache blocking、线程并行</td>
<td>提升 CPU 推理与训练效率</td>
<td>服务器 CPU 推理、无 GPU 环境</td>
</tr>
<tr>
<td>NCCL communication kernel</td>
<td>AllReduce、AllGather、ReduceScatter、Broadcast</td>
<td>NVIDIA 多 GPU</td>
<td>环形通信（Ring）、树形通信（Tree）、链路拓扑优化</td>
<td>降低多卡同步开销</td>
<td>数据并行、张量并行、大模型训练</td>
</tr>
<tr>
<td>FlashAttention kernel</td>
<td>Scaled Dot-Product Attention</td>
<td>NVIDIA GPU 及其他支持相应实现的加速器</td>
<td>分块、在线 softmax、kernel fusion、减少 HBM 访存</td>
<td>把 attention 从显存瓶颈拉回到更接近计算瓶颈</td>
<td>Transformer、LLM、长序列建模</td>
</tr>
<tr>
<td>Fused LayerNorm / RMSNorm kernel</td>
<td>LayerNorm、RMSNorm、Bias Add、Residual Add</td>
<td>GPU / CPU 后端</td>
<td>多步逐元素运算融合为一次访存</td>
<td>显著降低 memory-bound 算子的开销</td>
<td>Transformer block、LLM 推理</td>
</tr>
<tr>
<td>Fused MLP kernel</td>
<td>Bias Add、GELU / SiLU、Dropout、Residual</td>
<td>GPU</td>
<td>把连续逐元素算子合并，减少中间张量写回</td>
<td>减少 kernel launch 次数与显存读写</td>
<td>MLP block、前馈网络</td>
</tr>
<tr>
<td>Triton custom kernel</td>
<td>任意自定义算子或 fused operator</td>
<td>GPU</td>
<td>开发者手写 tile、访存布局与并行策略</td>
<td>在通用库缺少最优实现时获得定制性能</td>
<td>大模型训练、研究型性能优化、自定义融合</td>
</tr>
<tr>
<td>TensorRT generated kernel</td>
<td>部署图中的卷积、MatMul、激活、归一化、量化路径</td>
<td>NVIDIA GPU</td>
<td>图优化、层融合、低精度选择、kernel autotuning</td>
<td>显著降低推理时延并提升吞吐</td>
<td>在线推理服务、边缘推理</td>
</tr>
<tr>
<td>XLA generated kernel</td>
<td>JAX / TensorFlow 图中的可融合算子子图</td>
<td>GPU、TPU 等</td>
<td>图级融合、静态形状分析、编译生成目标代码</td>
<td>让一串算子整体下沉为更大的执行单元</td>
<td>JAX 训练、TPU 训练、编译型执行场景</td>
</tr>
<tr>
<td>TVM generated kernel</td>
<td>自定义张量表达式对应的算子</td>
<td>CPU、GPU、边缘加速器</td>
<td>自动调度、自动搜索、代码生成</td>
<td>跨异构硬件获得针对性实现</td>
<td>端侧部署、自定义芯片适配</td>
</tr>
<tr>
<td>PagedAttention / KV-cache kernel</td>
<td>增量解码中的 attention 与缓存访问</td>
<td>LLM 推理后端</td>
<td>分页管理 KV cache、优化随机访问和 batch 合并</td>
<td>提升长上下文与多请求并发推理效率</td>
<td>大语言模型在线推理</td>
</tr>
</tbody>
</table>
<p>理解内核时最重要的一点是：<span style="background-color: #c0c0c0;">一个算子并不对应唯一实现</span>。同样是卷积，可以选 direct convolution、Winograd 或 FFT；同样是 attention，可以选普通分步实现、FlashAttention 或推理场景下的 paged attention。真正的差异往往不在数学定义，而在访存方式、融合策略、并行粒度和硬件利用率上。</p>
<p>因此，内核并不是建模阶段最先暴露给用户的对象，却往往决定模型最终的吞吐、时延、显存占用、能耗和成本。模型结构决定“上限在哪里”，内核质量决定“这个上限能兑现多少”。</p>
<div class="blog_h3"><span class="graybg">三者关系</span></div>
<p>把三者串起来看，一个卷积层或注意力层的执行路径通常是这样的：开发者先在 PyTorch 或 JAX 中写出一个模块；框架把它拆成若干算子，例如 MatMul、Softmax、LayerNorm 或 Convolution；运行时再为每个算子选择对应硬件上的 kernel，例如 cuBLAS 的 GEMM kernel、cuDNN 的 convolution kernel，或 Triton 写成的自定义 fused kernel。整个训练与推理过程由框架负责调度，算子负责表达数学语义，内核负责把语义变成高性能机器代码。</p>
<p>可以用一个具体例子来理解。若在 PyTorch 中定义一个卷积层，那么：</p>
<ol>
<li>PyTorch 作为框架负责接收模块定义、组织张量、记录梯度关系并调度执行。</li>
<li>卷积（Convolution）作为算子表示“输入特征图与卷积核做局部加权求和”这一数学操作。</li>
<li>底层可能调用 cuDNN 提供的卷积 kernel，在 GPU 上以高度优化的方式完成真正计算。</li>
</ol>
<p>同样地，在 Transformer 中写一层自注意力时，框架会组织前向与反向图；MatMul、Softmax、Mask、LayerNorm 等作为算子组成计算链；而 FlashAttention、fused LayerNorm、paged attention 这类高性能实现，则属于内核或 kernel-level optimization 的范畴。</p>
<p>因此，这三个层次的关系可以概括为：<span style="background-color: #c0c0c0;">框架管理整个建模与执行流程，算子定义模型中的数学步骤，内核负责把这些步骤在具体硬件上高效落地</span>。从研究到工程落地的能力鸿沟，往往正体现在能否同时理解这三层。</p>
<div class="blog_h1"><span class="graybg">经典机器学习</span></div>
<div class="blog_h2"><span class="graybg">选型指南</span></div>
<p>经典机器学习的模型选择，本质上是在<span style="background-color: #c0c0c0;">任务形式、数据规模、特征形态、可解释性要求、训练与推断成本</span>之间做匹配。只要先判断“有没有标签”“输出是什么类型”“样本量有多大”“特征是不是稀疏或非线性”“是否需要概率或规则解释”，大多数任务都可以迅速缩小到少数几个候选模型。</p>
<p>本章中的模型并不是按“先进程度”排序，而是按建模假设来区分。线性模型假设边界或关系接近线性；树模型更擅长表格数据中的非线性交互；概率模型更强调分布解释；近邻模型依赖局部相似性；聚类与降维模型关注无监督结构；HMM（Hidden Markov Model, HMM）和条件随机场（Conditional Random Field, CRF）则处理序列标签之间存在依赖的结构化预测问题。</p>
<p>下面的表格不是概览式罗列，而是直接服务于选型。每一行都回答五个问题：<span style="background-color: #c0c0c0;">什么情况下优先选它、它最依赖什么数据条件、它能解决什么核心诉求、为什么它在该场景里合适、以及什么情况下应当换模型</span>。在大多数工程场景里，先按这些表格缩小范围，再进入具体模型细节，效率最高。</p>
<div class="blog_h3"><span class="graybg">任务类型：分类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>逻辑回归</td>
<td>需要稳定强基线、需要概率输出、需要解释每个特征如何影响类别</td>
<td>特征可以线性分离到一定程度；高维稀疏特征尤其合适，如 one-hot、词袋、统计特征</td>
<td>训练快、概率自然、权重可解释，适合作为第一版可上线模型</td>
<td>类别边界高度非线性、特征交互复杂且没有显式特征工程时</td>
</tr>
<tr>
<td>支持向量机（SVM）</td>
<td>样本中小规模、类别边界较清晰、希望用最大间隔提升泛化稳健性</td>
<td>特征需要做尺度统一；核 SVM 更适合中小规模，不适合超大样本</td>
<td>对边界样本建模强，在线性不可分但结构仍较规整时往往优于纯线性模型</td>
<td>数据量很大、需要快速训练与部署、或者必须输出天然概率时</td>
</tr>
<tr>
<td>决策树</td>
<td>需要把模型直接解释成规则路径，或业务天然是“按阈值分流”的形式</td>
<td>表格特征、混合数值与类别特征都可；不强依赖标准化</td>
<td>规则可视化最直接，便于和业务规则、审计规则对齐</td>
<td>追求最稳泛化性能时；单树通常方差高，容易过拟合</td>
</tr>
<tr>
<td>随机森林</td>
<td>表格分类需要稳健基线、希望少调参、担心单棵树过拟合</td>
<td>适合中等规模表格数据；对噪声、缺失和特征尺度通常更宽容</td>
<td>比单树稳定，通常先于复杂 boosting 模型给出可靠结果</td>
<td>追求表格任务的极致精度，或需要很小的模型体积时</td>
</tr>
<tr>
<td>梯度提升树（GBDT）</td>
<td>表格分类精度优先，特征中存在明显非线性与交互效应</td>
<td>适合结构化特征，不要求线性关系；通常需要一定调参</td>
<td>能逐轮修正前一轮错误，在中等规模表格任务上常是强力基线</td>
<td>需要极快训练、极少调参，或数据已经极大到串行 boosting 成本明显偏高时</td>
</tr>
<tr>
<td>XGBoost</td>
<td>工业表格分类、竞赛任务、缺失值与正则化处理都很重要</td>
<td>适合中大规模结构化数据；对特征工程和超参数较敏感，但工程支持成熟</td>
<td>精度高、鲁棒、缺失值处理成熟，是很多表格分类任务的首选之一</td>
<td>极端大规模、极度强调训练速度与内存效率时，LightGBM 往往更优先</td>
</tr>
<tr>
<td>LightGBM</td>
<td>大规模表格分类、高维稀疏特征、需要更快训练与更低内存消耗</td>
<td>适合样本量大、特征维度高的结构化任务；对类别特征和稀疏特征较友好</td>
<td>训练快、工程效率高，在工业 CTR、风控、推荐特征场景很常见</td>
<td>数据量较小且噪声较大时；叶子生长策略若不控制，容易过拟合</td>
</tr>
<tr>
<td>朴素贝叶斯</td>
<td>需要极快文本分类基线、小样本启动、希望先验证特征是否有判别力</td>
<td>最适合词袋、词频、计数类高维稀疏特征；默认接受条件独立近似</td>
<td>训练和推断都极快，在垃圾邮件、主题粗分类等任务上常有效</td>
<td>强依赖复杂特征相关性、类别边界非线性明显、或需要高精度概率校准时</td>
</tr>
<tr>
<td>K 近邻（KNN）</td>
<td>样本规模小、相似样本应有相似标签、希望不做显式训练</td>
<td>距离度量必须有意义；所有特征应标准化，且维度不能过高</td>
<td>局部模式直观，适合做小数据原型验证或相似样本检索式分类</td>
<td>高维稀疏特征、大规模数据、低延迟在线推断场景</td>
</tr>
<tr>
<td>线性判别分析（LDA）</td>
<td>有监督分类同时希望压缩特征维度，且类别统计结构较稳定</td>
<td>更适合每类近似高斯、类内协方差可估计的情况；样本数不能太少</td>
<td>把“降维”和“分类判别”结合起来，适合先压缩再分类的流程</td>
<td>类别分布非常复杂、强非高斯、强非线性时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：回归</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>线性回归</td>
<td>需要连续值预测、希望建立可解释、可审计、可稳定复现的基线</td>
<td>目标与特征大致满足线性关系，或经过变换后接近线性</td>
<td>系数解释直观，便于分析“哪个因素把目标推高或拉低”</td>
<td>目标关系明显呈现复杂分段、交互或强非线性时</td>
</tr>
<tr>
<td>Lasso（L1 正则化）</td>
<td>特征很多、怀疑只有少数特征真正有效、希望模型自动做变量筛选</td>
<td>高维特征尤其适合；允许部分权重被压到 0</td>
<td>回归同时完成特征选择，适合构建更稀疏、更简洁的模型</td>
<td>大量强相关特征共同起作用时；它可能只保留其中部分特征</td>
</tr>
<tr>
<td>岭回归 / L2 正则化</td>
<td>特征共线性明显、担心普通线性回归权重不稳定</td>
<td>适合多个相关特征共同解释目标，而不希望稀疏淘汰其中一部分</td>
<td>通过收缩权重降低方差，使模型在相关特征场景下更稳定</td>
<td>首要目标是做特征筛选、希望很多系数直接变成 0 时</td>
</tr>
<tr>
<td>Elastic Net（L1 + L2 正则化）</td>
<td>既想做特征选择，又不希望在相关特征组上过于不稳定</td>
<td>高维、相关特征并存的回归任务最常见</td>
<td>综合 Lasso 与岭回归的优点，在稀疏性和稳定性之间折中</td>
<td>特征数量不多、模型目标非常简单时；其调参成本高于纯 L1 或 L2</td>
</tr>
<tr>
<td>决策树</td>
<td>目标值与输入关系近似分段函数，或业务逻辑天然围绕阈值展开</td>
<td>表格特征、混合类型特征都可；不需要严格线性假设</td>
<td>能学出“满足什么条件时预测值跳到哪个区间”的规则</td>
<td>希望预测曲线平滑、稳定，或希望泛化误差尽可能低时</td>
</tr>
<tr>
<td>随机森林</td>
<td>希望回归稳健、抗噪声、少调参，先拿到可靠效果</td>
<td>表格回归场景最常见；对特征尺度和局部异常较稳</td>
<td>综合多个树模型平均结果，通常比单树更不容易过拟合</td>
<td>要求外推能力强，或希望模型极度轻量、延迟极低时</td>
</tr>
<tr>
<td>梯度提升树（GBDT）</td>
<td>表格回归精度优先，目标和特征之间存在复杂非线性</td>
<td>适合异构表格特征；对异常值和长尾目标通常也较有韧性</td>
<td>在房价、评分、收益、风险等典型表格回归中常是强基线</td>
<td>算力非常紧、需要极低训练成本时</td>
</tr>
<tr>
<td>XGBoost / LightGBM</td>
<td>工业级表格回归、大规模特征、希望兼顾精度与工程效率</td>
<td>适合结构化数据；XGBoost 更稳健成熟，LightGBM 更强调速度与规模</td>
<td>常作为表格回归默认候选，能直接处理大量非线性和特征交互</td>
<td>数据关系本来就简单线性、并且强依赖系数解释时</td>
</tr>
<tr>
<td>K 近邻（KNN）</td>
<td>局部相似样本的目标值应当接近，希望用邻域平均直接预测</td>
<td>小数据、低维、有意义的距离度量是前提</td>
<td>局部平滑性强时实现简单有效，适合作为相似样本回归基线</td>
<td>高维、大样本、分布稀疏或在线延迟敏感时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：聚类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>K-Means</td>
<td>需要快速把样本分成若干组，并且预期每组都围绕某个均值中心展开</td>
<td>簇大致是球形或凸形；欧氏距离有意义；需要先给定 <span displaypfx="inline-" class="mathjax-container">\(K\)</span></td>
<td>速度快、实现简单，适合作为无监督分群第一选择</td>
<td>簇形状复杂、密度差异大、离群点很多或无法预先估计簇数时</td>
</tr>
<tr>
<td>层次聚类</td>
<td>不仅想得到分组结果，还想知道各组是如何逐层合并成层级结构的</td>
<td>更适合中小规模数据；需要能接受 <span displaypfx="inline-" class="mathjax-container">\(O(N^2)\)</span> 级别距离矩阵成本</td>
<td>能输出树状图，适合做群体结构分析与多粒度解释</td>
<td>数据量很大、只关心最终聚类标签、不关心层级关系时</td>
</tr>
<tr>
<td>DBSCAN</td>
<td>希望识别任意形状簇，并把稀疏孤立点单独作为噪声剔除</td>
<td>密度尺度相对统一；距离度量有意义；参数 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 邻域半径和最小点数可估计</td>
<td>不需要预设簇数，能自然处理非凸簇和离群点</td>
<td>不同区域密度差异很大时；单一密度阈值难兼顾所有簇</td>
</tr>
<tr>
<td>HDBSCAN</td>
<td>簇的密度明显不一致，希望保留 DBSCAN 的密度思想但更自适应</td>
<td>数据存在多密度结构；仍需合理距离度量</td>
<td>比 DBSCAN 更能处理“有的簇很密、有的簇较松”的真实数据</td>
<td>只需要一个快速、简单、可复现的基础分群结果时</td>
</tr>
<tr>
<td>高斯混合模型（GMM）</td>
<td>希望得到软聚类结果，或者认为每个簇更像椭球形概率团块</td>
<td>连续特征较适合；默认每个簇可近似为一个高斯成分</td>
<td>不仅给簇标签，还给每个样本属于各簇的概率</td>
<td>簇形状非常复杂、非高斯、多峰结构难用少量高斯逼近时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：降维与可视化</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>主成分分析（PCA）</td>
<td>希望做线性降维、压缩冗余特征、去噪或为下游模型降成本</td>
<td>主要结构能由少数线性主方向解释；不依赖标签</td>
<td>保留最大方差方向，是最稳健、最常用的无监督降维起点</td>
<td>真正关心的是类别判别性而不是总体方差，或数据结构强非线性时</td>
</tr>
<tr>
<td>线性判别分析（LDA）</td>
<td>有标签并希望把不同类别拉开后再做分类或可视化</td>
<td>类别标签可靠；类内散度与类间散度都可稳定估计</td>
<td>直接围绕“可分性”找投影，比 PCA 更贴近分类目标</td>
<td>没有标签、类别边界强非线性、或类别数太少导致可降维空间有限时</td>
</tr>
<tr>
<td>t-SNE</td>
<td>想把高维嵌入压到二维或三维，只为看局部邻域是否形成簇</td>
<td>更适合可视化，不适合直接做可逆特征压缩</td>
<td>局部邻域展示能力强，适合分析表征是否把相似样本聚到一起</td>
<td>需要把降维结果直接送入生产模型，或需要保留严格全局距离关系时</td>
</tr>
<tr>
<td>UMAP</td>
<td>希望做更快的大规模可视化，兼顾局部结构与部分全局连通性</td>
<td>适合高维嵌入、文本向量、图表示等复杂表征</td>
<td>通常比 t-SNE 更快，也更容易保留大体流形结构</td>
<td>需要线性可解释主方向，或需要结果完全稳定可重复到坐标级别时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：异常检测</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>孤立森林（Isolation Forest）</td>
<td>通用表格异常检测，希望先得到一个稳健、扩展性好的异常分数</td>
<td>适合中大规模数据；不要求明确概率分布形式</td>
<td>通过随机切分隔离少数样本，通常是异常检测第一基线</td>
<td>异常定义依赖非常精细的局部密度差异时</td>
</tr>
<tr>
<td>局部异常因子（LOF）</td>
<td>异常并不是全局离群，而是“在本地邻域里显得稀疏”</td>
<td>距离度量必须合理；样本规模不宜过大</td>
<td>能发现那些整体位置不极端、但局部密度明显偏低的点</td>
<td>高维距离失真严重、或需要高吞吐在线检测时</td>
</tr>
<tr>
<td>单类支持向量机（One-Class SVM）</td>
<td>只有正常样本，目标是学习正常数据的封闭边界</td>
<td>特征需标准化；更适合中小规模；核方法对边界形状有帮助</td>
<td>适合“正常类定义清楚、异常类没有稳定样本”的场景</td>
<td>数据量很大、特征维度很高、参数难以稳定选择时</td>
</tr>
<tr>
<td>高斯混合模型（GMM）</td>
<td>希望把异常定义为低概率区域，并明确得到似然分数</td>
<td>连续数据更合适；分布能用若干高斯混合近似</td>
<td>异常判定有明确概率语义，适合风险评分和阈值分析</td>
<td>数据分布非常复杂、非高斯、或异常并不等价于低密度时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：序列标注与结构化预测</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>隐马尔可夫模型（HMM）</td>
<td>序列较短、转移规律明显、希望在较小数据和较强先验下完成基础序列建模</td>
<td>状态转移与观测发射假设大致成立；问题适合生成式描述</td>
<td>结构清晰、推断高效，适合作为经典序列建模入门和小规模基线</td>
<td>标签强依赖全局上下文、特征复杂、需要大量判别式特征时</td>
</tr>
<tr>
<td>条件随机场（CRF）</td>
<td>输出不是单点分类而是整条标签序列，并且相邻标签的合法性非常关键</td>
<td>一维链式序列最合适；上游特征或表示需要至少能提供较强局部证据</td>
<td>通过整体解码约束标签转移，使最终标签序列更一致、更符合任务结构</td>
<td>长距离语义主要由强表征模型决定、标签约束作用很弱，或任务更适合 span 建模时</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">任务类型：密度估计与概率建模</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">优先选择条件</td>
<td style="text-align: center;">数据与特征前提</td>
<td style="text-align: center;">最主要价值</td>
<td style="text-align: center;">不建议优先使用的情况</td>
</tr>
</thead>
<tbody>
<tr>
<td>朴素贝叶斯</td>
<td>希望快速得到后验概率分类器，并接受条件独立近似</td>
<td>高维稀疏离散特征最合适，如文本词频、词出现计数</td>
<td>概率形式直接、估计简单，适合快速建模和在线系统</td>
<td>特征依赖结构复杂、需要精细表达联合分布时</td>
</tr>
<tr>
<td>高斯混合模型（GMM）</td>
<td>希望显式建模连续数据分布，或需要软聚类与密度估计统一完成</td>
<td>连续数据、多峰分布、可近似为若干高斯成分</td>
<td>每个样本都能得到各成分责任概率，解释和阈值分析都较自然</td>
<td>分布极端复杂、重尾严重、或高斯成分数难合理确定时</td>
</tr>
<tr>
<td>隐马尔可夫模型（HMM）</td>
<td>不仅要建模观测分布，还要建模隐藏状态在时间上的转移机制</td>
<td>观测是序列，且状态依赖主要体现在相邻时刻</td>
<td>把“序列生成机制”和“时序转移规律”统一到一个概率模型里</td>
<td>长程依赖很强、局部马尔可夫假设明显不成立时</td>
</tr>
</tbody>
</table>
<p>若只是要一个工程上可执行的默认起点，可以进一步压缩成如下规则：表格分类与回归优先从随机森林、GBDT、XGBoost、LightGBM 和线性模型里选；文本高维稀疏分类优先看逻辑回归和朴素贝叶斯；无监督分群先看 K-Means，再根据簇形状和噪声情况转向 DBSCAN、HDBSCAN 或 GMM；需要可视化时先区分是要线性压缩还是只要展示结构，再在 PCA、LDA、t-SNE、UMAP 中选择；涉及标签序列依赖时再进入 HMM 与 CRF。</p>
<div class="blog_h2"><span class="graybg">线性模型</span></div>
<p>线性模型（Linear Models）的核心不是“世界一定是线性的”，而是先用一个可解释、可优化、常常足够强的基线去刻画输入与输出的关系。很多复杂模型也可以看作“在线性读出层之前先做更强的特征变换”。</p>
<div class="blog_h3"><span class="graybg">线性回归</span></div>
<p>线性回归（Linear Regression）在回归任务中建模“输入特征的加权求和如何产生连续输出”。它的价值在于：<span style="background-color: #c0c0c0;">可解释</span>（权重直接对应特征影响）与<span style="background-color: #c0c0c0;">可优化</span>（凸问题，训练稳定）。</p>
<div class="blog_h4"><span class="graybg">模型与符号</span></div>
<p>给定训练集 <span displaypfx="inline-" class="mathjax-container">\(\{(\mathbf{x}_i,y_i)\}_{i=1}^{N}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i\in\mathbb{R}^d\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本的特征向量， <span displaypfx="inline-" class="mathjax-container">\(y_i\in\mathbb{R}\)</span> 是对应标签。线性回归假设单样本预测为：</p>
<span displaypfx="" class="mathjax-container">\[\hat y_i=\mathbf{w}^\top \mathbf{x}_i+b\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\hat y_i\)</span>：对 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 的预测。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\in\mathbb{R}^d\)</span>：权重向量（每个维度对应一个特征）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(b\in\mathbb{R}\)</span>：偏置（Bias），用于整体平移。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top \mathbf{x}_i\)</span>：内积（Dot Product），表示“按特征加权求和”。</li>
</ul>
<p>把全部样本写成矩阵形式。令设计矩阵（Design Matrix）<span displaypfx="inline-" class="mathjax-container">\(\mathbf{X}\in\mathbb{R}^{N\times d}\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i^\top\)</span>，标签向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\in\mathbb{R}^{N}\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量为 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span>，全 1 向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\in\mathbb{R}^{N}\)</span>。则：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\mathbf{y}}=\mathbf{X}\mathbf{w}+b\mathbf{1}\]</span>
<p>直觉类比：它像“按因素打分再加总”。例如房价预测里，面积、地段评分、楼龄都可作为特征；权重正负决定影响方向，绝对值大小决定影响强弱。</p>
<div class="blog_h4"><span class="graybg">目标函数（最小二乘）</span></div>
<p>最常见的训练目标是最小化均方误差（Mean Squared Error, MSE）的总和（或平均）：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ J_{\text{EN}}(\mathbf{w},b)=\frac{1}{N}\|\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\|_2^2+\lambda_1\|\mathbf{w}\|_1+\lambda_2\|\mathbf{w}\|_2^2\]</span>
<p>这个目标可以拆成三部分理解：第一项 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{N}\|\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\|_2^2\)</span> 是数据拟合误差，其中 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\)</span> 是所有样本的残差向量；第二项 <span displaypfx="inline-" class="mathjax-container">\(\lambda_1\|\mathbf{w}\|_1\)</span> 倾向把一部分权重直接压到 0；第三项 <span displaypfx="inline-" class="mathjax-container">\(\lambda_2\|\mathbf{w}\|_2^2\)</span> 倾向把权重整体缩小得更平滑。这里 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 是样本数， <span displaypfx="inline-" class="mathjax-container">\(\lambda_1,\lambda_2\)</span> 是正则化强度。若两者都取 0，就退化为普通最小二乘。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(J(\mathbf{w},b)\)</span>：目标函数（Objective）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\|\cdot\|_2\)</span>：二范数（Euclidean Norm），向量的长度。</li>
</ul>
<p>平方项会对大残差施加更大惩罚，因此训练会优先修正“偏得很离谱”的样本。并且在高斯噪声假设下，最小二乘等价于最大似然估计（Maximum Likelihood Estimation, MLE）导出的解。</p>
<div class="blog_h4"><span class="graybg">解析解（正规方程）</span></div>
<p>把偏置吸收到特征中更方便：定义增广特征 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{x}}_i=[\mathbf{x}_i;1]\in\mathbb{R}^{d+1}\)</span>，增广参数 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{w}}=[\mathbf{w};b]\in\mathbb{R}^{d+1}\)</span>，增广矩阵 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}=[\mathbf{X},\mathbf{1}]\in\mathbb{R}^{N\times(d+1)}\)</span>。则 <span displaypfx="inline-" class="mathjax-container">\(\hat{\mathbf{y}}=\tilde{\mathbf{X}}\tilde{\mathbf{w}}\)</span>，目标为 <span displaypfx="inline-" class="mathjax-container">\(\min_{\tilde{\mathbf{w}}}\|\mathbf{y}-\tilde{\mathbf{X}}\tilde{\mathbf{w}}\|_2^2\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}\)</span> 可逆，则正规方程（Normal Equation）给出闭式解：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\mathbf{w}}^*=\left(\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}\right)^{-1}\tilde{\mathbf{X}}^\top\mathbf{y}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{w}}^*\)</span> 表示最优增广参数，前 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维对应原权重 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span>，最后一维对应偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>。 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}\)</span> 汇总了特征之间的相关结构， <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}^\top\mathbf{y}\)</span> 汇总了特征与标签之间的相关程度，因此这个闭式解本质上是在“用整体相关关系一次性解出最优线性系数”。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}\in\mathbb{R}^{(d+1)\times(d+1)}\)</span>：Gram 矩阵，度量特征之间的相关性。</li>
<li><span displaypfx="inline-" class="mathjax-container">\((\cdot)^{-1}\)</span>：矩阵逆；不可逆时通常用伪逆（Pseudo-inverse）或加正则化处理。</li>
</ul>
<p>工程实现中，直接求逆并不推荐；更稳定的做法是解线性方程组或用 QR/SVD 分解。</p>
<div class="blog_h4"><span class="graybg">实例：把公式算一遍</span></div>
<p>用一维数据拟合 <span displaypfx="inline-" class="mathjax-container">\(y\approx wx+b\)</span>。给定三点 <span displaypfx="inline-" class="mathjax-container">\((x,y)\in\{(0,1),(1,3),(2,5)\}\)</span>。构造增广矩阵与标签：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\mathbf{X}}=\begin{bmatrix}0 &amp; 1\\ 1 &amp; 1\\ 2 &amp; 1\end{bmatrix},\quad \mathbf{y}=\begin{bmatrix}1\\ 3\\ 5\end{bmatrix}\]</span>
<p>这个增广矩阵的每一行对应一个样本；第一列是原始特征 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，第二列固定为 1，用来把偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 并入矩阵乘法。标签向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span> 则把三个样本的真实输出按顺序堆叠起来。</p>
<p>计算：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}=\begin{bmatrix}5 &amp; 3\\ 3 &amp; 3\end{bmatrix},\quad \tilde{\mathbf{X}}^\top\mathbf{y}=\begin{bmatrix}13\\ 9\end{bmatrix}\]</span>
<p>左边的矩阵可以看成所有样本在“特征与偏置”两个方向上的二阶统计量： <span displaypfx="inline-" class="mathjax-container">\(5\)</span> 来自 <span displaypfx="inline-" class="mathjax-container">\(0^2+1^2+2^2\)</span>， <span displaypfx="inline-" class="mathjax-container">\(3\)</span> 来自 <span displaypfx="inline-" class="mathjax-container">\(0+1+2\)</span>，右边向量 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\mathbf{X}}^\top\mathbf{y}\)</span> 则分别对应 <span displaypfx="inline-" class="mathjax-container">\(\sum_i x_i y_i\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\sum_i y_i\)</span>。这正是正规方程所需要的汇总量。</p>
<p>解线性方程 <span displaypfx="inline-" class="mathjax-container">\(\left(\tilde{\mathbf{X}}^\top\tilde{\mathbf{X}}\right)\tilde{\mathbf{w}}=\tilde{\mathbf{X}}^\top\mathbf{y}\)</span>，即：</p>
<span displaypfx="" class="mathjax-container">\[\begin{cases}5w+3b=13\\ 3w+3b=9\end{cases}\Rightarrow w=2,\ b=1\]</span>
<p>这组方程的未知量只有两个：斜率 <span displaypfx="inline-" class="mathjax-container">\(w\)</span> 和偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>。解出 <span displaypfx="inline-" class="mathjax-container">\(w=2\)</span> 表示特征每增加 1，预测值增加 2；解出 <span displaypfx="inline-" class="mathjax-container">\(b=1\)</span> 表示当 <span displaypfx="inline-" class="mathjax-container">\(x=0\)</span> 时，模型基线输出为 1。</p>
<p>因此预测为 <span displaypfx="inline-" class="mathjax-container">\(\hat y=2x+1\)</span>，恰好穿过三点。这个例子展示了：正规方程把“最小化平方误差”的优化问题，转换成一个线性方程组。</p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<ul>
<li>需要可解释的特征贡献（权重）与可校验的线性关系。</li>
<li>表格数据（Tabular）中，关系接近线性或可通过特征变换/交互项线性化。</li>
<li>作为强基线：先用线性模型定位数据问题、特征质量与噪声水平，再决定是否需要更复杂模型。</li>
<li>高维稀疏特征（如 one-hot、文本 bag-of-words）下，配合正则化可获得稳定解。</li>
</ul>
<p>当特征很多、共线性（Collinearity）强或数据量相对不足时，普通最小二乘（Ordinary Least Squares, OLS）会出现高方差：训练集拟合很好、验证集误差上升。正则化（Regularization）通过惩罚参数规模，把“拟合训练误差”与“控制模型复杂度”写进同一个目标函数。</p>
<div class="blog_h3"><span class="graybg">Lasso（L1 正则化）</span></div>
<p>Lasso 把参数惩罚写成一范数（<span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> Norm）：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ J_{\text{lasso}}(\mathbf{w},b)=\frac{1}{N}\left\|\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\right\|_2^2+\lambda\|\mathbf{w}\|_1\]</span>
<p>这个式子里，前半部分仍然是拟合误差，衡量预测和真实标签之间差多少；后半部分 <span displaypfx="inline-" class="mathjax-container">\(\lambda\|\mathbf{w}\|_1\)</span> 是稀疏惩罚，其中 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_1=\sum_j |w_j|\)</span> 会优先把不重要的权重压成 0。于是 Lasso 不只是“把权重变小”，而是经常顺带完成特征选择。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>：正则化强度（Regularization Strength）。越大表示越强的收缩。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_1=\sum_{j=1}^{d}|w_j|\)</span>：一范数，鼓励稀疏（Sparsity）。</li>
<li>通常不惩罚偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>；否则会把整体均值也一起往 0 拉。</li>
<li>Lasso 由于 <span displaypfx="inline-" class="mathjax-container">\(|\cdot|\)</span> 在 0 点不可导，一般没有像岭回归那样简洁的闭式解；常用坐标下降（Coordinate Descent）、近端梯度（Proximal Gradient）或 LARS 等算法求解。</li>
</ul>
<p>Lasso 的关键作用是让一部分权重被压到精确 0，而不是均匀缩小全部权重。它更像给参数设置了一个阈值：弱相关、贡献不足以抵消惩罚的特征，会被直接剔除。因此 Lasso 同时完成复杂度控制与特征选择（Feature Selection）。</p>
<p>几何上，在相同训练误差轮廓线下， <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 约束对应菱形/正八面体；这些尖角更容易与最优点相交在坐标轴上，因此常出现某些 <span displaypfx="inline-" class="mathjax-container">\(w_j=0\)</span> 的解。</p>
<ul>
<li>适合高维稀疏特征、希望自动筛特征的场景，例如广告、推荐、文本 one-hot 特征。</li>
<li>当特征高度相关时，Lasso 往往只保留其中少数几个，因此解的稳定性通常弱于岭回归。</li>
</ul>
<div class="blog_h3"><span class="graybg">岭回归 / L2 正则化</span></div>
<p>岭回归把参数惩罚写成二范数平方（<span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> Norm Squared）：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ J_{\text{ridge}}(\mathbf{w},b)=\frac{1}{N}\left\|\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\right\|_2^2+\lambda\|\mathbf{w}\|_2^2\]</span>
<p>岭回归的结构与普通线性回归相同，只是在误差项之外额外加入了 <span displaypfx="inline-" class="mathjax-container">\(\lambda\|\mathbf{w}\|_2^2\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_2^2=\sum_j w_j^2\)</span> 会连续惩罚过大的权重，因此它更擅长缓解共线性和过拟合，而不是像 Lasso 那样主动做稀疏选择。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda\ge 0\)</span>：正则化强度。越大表示越强的收缩。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_2^2=\sum_{j=1}^{d}w_j^2\)</span>：二范数平方，连续惩罚大权重。</li>
<li>通常不惩罚偏置 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>；否则会把整体均值也一起往 0 拉。</li>
</ul>
<p>从梯度角度看，惩罚项 <span displaypfx="inline-" class="mathjax-container">\(\lambda\|\mathbf{w}\|_2^2\)</span> 对单个参数 <span displaypfx="inline-" class="mathjax-container">\(w_j\)</span> 的梯度贡献是 <span displaypfx="inline-" class="mathjax-container">\(2\lambda w_j\)</span>。这就是 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 正则化在深度学习中被称为权重衰减（Weight Decay）的原因：它持续把权重按比例拉回 0。若取 <span displaypfx="inline-" class="mathjax-container">\(\lambda=1\)</span>，当 <span displaypfx="inline-" class="mathjax-container">\(w_j=10\)</span> 时，梯度为 <span displaypfx="inline-" class="mathjax-container">\(20\)</span>；优化器会施加强烈收缩，把这个大权重猛拉回去。当 <span displaypfx="inline-" class="mathjax-container">\(w_j=0.1\)</span> 时，梯度只有 <span displaypfx="inline-" class="mathjax-container">\(0.2\)</span>；往 0 拉的力量就很弱。也就是说，参数一旦变大， <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 惩罚立刻增强；参数已经很小时，它几乎不再干预。</p>
<p>岭回归仍是凸二次问题，并有闭式解（忽略/已中心化处理 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 的情况）：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}^*=\left(\mathbf{X}^\top\mathbf{X}+N\lambda\mathbf{I}\right)^{-1}\mathbf{X}^\top\mathbf{y}\]</span>
<p>与普通最小二乘相比，这里多出来的 <span displaypfx="inline-" class="mathjax-container">\(N\lambda\mathbf{I}\)</span> 是沿对角线加入的一项稳定化修正。 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{I}\)</span> 是单位矩阵，它不会改变特征之间的相对结构，但会让矩阵更容易求逆，因此在特征高度相关时更稳定。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{I}\in\mathbb{R}^{d\times d}\)</span>：单位矩阵（Identity Matrix）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{X}^\top\mathbf{X}+N\lambda\mathbf{I}\)</span>：对角线上加了 <span displaypfx="inline-" class="mathjax-container">\(N\lambda\)</span> 的稳定项，缓解共线性导致的病态（Ill-conditioning）。</li>
</ul>
<p>用一个最小可算的例子说明 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 如何改变解。考虑无偏置的一维回归 <span displaypfx="inline-" class="mathjax-container">\(\hat y=wx\)</span>，目标为：</p>
<span displaypfx="" class="mathjax-container">\[J(w)=\sum_{i=1}^{N}(y_i-wx_i)^2+\lambda w^2\]</span>
<p>这是单变量岭回归的简化形式。前一项把所有样本的平方误差加总，后一项 <span displaypfx="inline-" class="mathjax-container">\(\lambda w^2\)</span> 惩罚过大的斜率。它清楚展示了岭回归的核心：既要求拟合数据，又限制参数不要长得太大。</p>
<p>对 <span displaypfx="inline-" class="mathjax-container">\(w\)</span> 求导并令其为 0 得：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\mathrm{d}J}{\mathrm{d}w}=-2\sum_{i=1}^{N}x_i(y_i-wx_i)+2\lambda w=0\Rightarrow w^*=\frac{\sum_{i=1}^{N}x_i y_i}{\sum_{i=1}^{N}x_i^2+\lambda}\]</span>
<p>这个结果说明岭回归解和普通最小二乘解非常接近，只是分母里多了一个 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span>。它会把估计值往 0 方向收缩： <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 越大，分母越大，得到的 <span displaypfx="inline-" class="mathjax-container">\(w^*\)</span> 就越保守。</p>
<p>可见 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 直接进入分母，把 <span displaypfx="inline-" class="mathjax-container">\(w^*\)</span> 持续拉向 0。岭回归不会像 Lasso 那样大量制造精确 0，而是把所有权重更平滑地压小，因此更像“把所有旋钮都往小一点拧”，让模型整体更保守、更稳健。</p>
<ul>
<li>适合特征高度相关、希望保留全部特征但降低方差的场景；常见于经济学、医学和一般表格数据。</li>
<li>当既希望稀疏，又希望在强相关特征间保持稳定时，Elastic Net（<span displaypfx="inline-" class="mathjax-container">\(L_1+L_2\)</span>）通常比纯 Lasso 更稳。</li>
</ul>
<div class="blog_h3"><span class="graybg">Elastic Net（L1 + L2 正则化）</span></div>
<p>Elastic Net 把 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 惩罚合并到同一个目标中：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ J_{\text{EN}}(\mathbf{w},b)=\frac{1}{N}\|\mathbf{y}-\mathbf{X}\mathbf{w}-b\mathbf{1}\|_2^2+\lambda_1\|\mathbf{w}\|_1+\lambda_2\|\mathbf{w}\|_2^2\]</span>
<p>它同时保留两类效应： <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 项负责产生稀疏性（Sparsity），把一部分弱特征压到 0； <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 项负责平滑收缩，在特征高度相关时提高解的稳定性。因此 Elastic Net 通常用于“既希望自动做特征选择，又不希望在强相关特征之间选得过于激进”的场景。</p>
<ul>
<li>当 <span displaypfx="inline-" class="mathjax-container">\(\lambda_2=0\)</span> 时，退化为 Lasso。</li>
<li>当 <span displaypfx="inline-" class="mathjax-container">\(\lambda_1=0\)</span> 时，退化为岭回归（Ridge Regression）。</li>
<li>当特征高度相关且维度很高时，Elastic Net 往往比纯 Lasso 更稳，也比纯岭回归更稀疏。</li>
</ul>
<div class="blog_h3"><span class="graybg">逻辑回归</span></div>
<p>逻辑回归（Logistic Regression）是二分类的标准基线：它先用线性函数产生打分，再把该打分通过 sigmoid（Logistic Function）映射到 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span>，从而得到条件概率模型。</p>
<div class="blog_h4"><span class="graybg">模型、概率与 logit</span></div>
<p>对二分类，令标签随机变量（Random Variable）<span displaypfx="inline-" class="mathjax-container">\(Y\in\{0,1\}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(1\)</span> 表示正类， <span displaypfx="inline-" class="mathjax-container">\(0\)</span> 表示负类。给定输入 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 后，先定义线性打分：</p>
<span displaypfx="" class="mathjax-container">\[z=\mathbf{w}^\top\mathbf{x}+b\]</span>
<p>再定义 sigmoid 函数：</p>
<span displaypfx="" class="mathjax-container">\[\sigma(z)=\frac{1}{1+e^{-z}}\]</span>
<p>于是，在给定输入 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 的条件下，标签取正类的条件概率写成：</p>
<span displaypfx="" class="mathjax-container">\[p(Y=1\mid \mathbf{x})=\sigma(z)=\sigma(\mathbf{w}^\top\mathbf{x}+b)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mid\)</span> 表示“在给定……条件下”；左侧 <span displaypfx="inline-" class="mathjax-container">\(Y=1\)</span> 表示事件“标签随机变量取值为正类 1”。因此这个式子表示：在输入 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 已知时，事件 <span displaypfx="inline-" class="mathjax-container">\(Y=1\)</span> 发生的概率。</p>
<p>相应地，负类概率为：</p>
<span displaypfx="" class="mathjax-container">\[p(Y=0\mid \mathbf{x})=1-\sigma(z)\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\in\mathbb{R}^d\)</span>：特征向量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\in\mathbb{R}^d\)</span>：权重向量（Weight Vector）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(b\in\mathbb{R}\)</span>：偏置（Bias）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}\)</span>：线性打分；它也是 logit（对数几率，log-odds）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sigma(\cdot)\)</span>：把任意实数映射到 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 的非线性函数。</li>
</ul>
<p>logit 的含义来自恒等式：</p>
<span displaypfx="" class="mathjax-container">\[\log\frac{p(Y=1\mid \mathbf{x})}{1-p(Y=1\mid \mathbf{x})}=z=\mathbf{w}^\top\mathbf{x}+b\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\frac{p(Y=1\mid \mathbf{x})}{1-p(Y=1\mid \mathbf{x})}\)</span> 称为几率（Odds），表示“正类概率与负类概率之比”；再对它取对数，就得到 logit（对数几率，log-odds）：<span displaypfx="inline-" class="mathjax-container">\(\log\frac{p}{1-p}\)</span>。因此上式的含义不是再引入一个无关的新量，而是说明：逻辑回归把<span style="background-color: #c0c0c0;">正类概率的对数几率</span>建模为输入特征的线性函数。</p>
<p>换言之，在二分类逻辑回归里， <span displaypfx="inline-" class="mathjax-container">\(z=\mathbf{w}^\top\mathbf{x}+b\)</span> 就是线性部分的原始输出值，而这个原始输出值恰好等于 logit。它本身不是概率，可以取任意实数；经过 sigmoid 之后才变成 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 内的概率。多分类情形中，softmax 之前那一组线性输出通常统称为 logits。</p>
<p>因此 <span displaypfx="inline-" class="mathjax-container">\(w_j\)</span> 可以被解释为：特征 <span displaypfx="inline-" class="mathjax-container">\(x_j\)</span> 增加一个单位，会把对数几率增加 <span displaypfx="inline-" class="mathjax-container">\(w_j\)</span>（在其他特征不变时）。这就是逻辑回归的核心可解释性。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/logistic.jpg"><img class="alignnone size-full wp-image-41171" src="https://blog.gmem.cc/wp-content/uploads/2026/03/logistic.jpg" alt="logistic" width="1216" height="874" /></a></p>
<div class="blog_h4"><span class="graybg">训练目标（NLL / Cross-Entropy）</span></div>
<p>给定训练集 <span displaypfx="inline-" class="mathjax-container">\(\{(\mathbf{x}_i,y_i)\}_{i=1}^{N}\)</span>，记 <span displaypfx="inline-" class="mathjax-container">\(p_i=\sigma(\mathbf{w}^\top\mathbf{x}_i+b)\)</span>。逻辑回归通过最大化似然（Likelihood）训练；等价地，它最小化负对数似然（Negative Log-Likelihood, NLL），也即二分类交叉熵（Binary Cross-Entropy）：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ L(\mathbf{w},b)=-\sum_{i=1}^{N}\left(y_i\log p_i+(1-y_i)\log(1-p_i)\right)\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\log p_i\)</span>：当 <span displaypfx="inline-" class="mathjax-container">\(y_i=1\)</span> 时，鼓励 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 变大。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\log(1-p_i)\)</span>：当 <span displaypfx="inline-" class="mathjax-container">\(y_i=0\)</span> 时，鼓励 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 变小。</li>
</ul>
<p>加入 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 正则化时，常见形式为 <span displaypfx="inline-" class="mathjax-container">\(L(\mathbf{w},b)+\lambda\|\mathbf{w}\|_2^2\)</span>（通常不惩罚 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>）。该目标是凸的，因此不存在“坏局部最优”的训练不稳定问题。</p>
<div class="blog_h4"><span class="graybg">梯度：公式如何驱动参数更新</span></div>
<p>把样本堆叠成矩阵 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{X}\)</span>、标签向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{y}\)</span>，预测概率向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{p}\)</span>（第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量为 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span>）。则无正则项时梯度为：</p>
<span displaypfx="" class="mathjax-container">\[\nabla_{\mathbf{w}}L=\mathbf{X}^\top(\mathbf{p}-\mathbf{y}),\quad \frac{\partial L}{\partial b}=\mathbf{1}^\top(\mathbf{p}-\mathbf{y})\]</span>
<p>这两个梯度式子都围绕同一个误差向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{p}-\mathbf{y}\)</span> 展开：它表示“模型给出的正类概率”和“真实标签”之间的偏差。 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{X}^\top(\mathbf{p}-\mathbf{y})\)</span> 表示把这种偏差按特征方向汇总起来，从而告诉每个权重该往哪个方向改； <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}^\top(\mathbf{p}-\mathbf{y})\)</span> 则把所有偏差直接相加，用来更新偏置。</p>
<p>这两个式子揭示了训练机制：如果某个样本真实标签是 1 但模型给出小概率（<span displaypfx="inline-" class="mathjax-container">\(p_i-y_i&lt;0\)</span>），则梯度会推动 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 朝着增加该样本 logit 的方向更新；反之亦然。</p>
<div class="blog_h4"><span class="graybg">实例：单样本算概率、算损失、算一次梯度</span></div>
<p>考虑一维特征 <span displaypfx="inline-" class="mathjax-container">\(x=2\)</span>，参数 <span displaypfx="inline-" class="mathjax-container">\(w=1,b=-1\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(z=wx+b=1\)</span>，概率：</p>
<span displaypfx="" class="mathjax-container">\[p=\sigma(1)=\frac{1}{1+e^{-1}}\approx 0.731\]</span>
<p>若真实标签 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span>，该样本的负对数似然为：</p>
<span displaypfx="" class="mathjax-container">\[\ell=-\log p\approx 0.313\]</span>
<p>该样本对 <span displaypfx="inline-" class="mathjax-container">\(w\)</span> 的梯度为（单样本形式）：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \ell}{\partial w}=(p-y)x=(0.731-1)\cdot 2\approx -0.538\]</span>
<p>用学习率 <span displaypfx="inline-" class="mathjax-container">\(\eta\)</span> 做一次梯度下降： <span displaypfx="inline-" class="mathjax-container">\(w\leftarrow w-\eta\frac{\partial\ell}{\partial w}\)</span>。由于梯度为负，更新会把 <span displaypfx="inline-" class="mathjax-container">\(w\)</span> 增大，从而增大 <span displaypfx="inline-" class="mathjax-container">\(z\)</span>、提升 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，使模型更倾向把该样本判为正类。</p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<ul>
<li>二分类且需要概率输出/可校准阈值（例如欺诈检测、流失预测、医学风险评分）。</li>
<li>高维稀疏特征（如 one-hot、文本 bag-of-words），逻辑回归常是强基线。</li>
<li>对可解释性、训练稳定性要求高的工程场景（可用权重做审计/特征诊断）。</li>
<li>当决策边界高度非线性且特征工程不足时，需要树模型或神经网络补上非线性。</li>
</ul>
<div class="blog_h3"><span class="graybg">支持向量机（SVM）</span></div>
<p>支持向量机（Support Vector Machine, SVM）是一类以<span style="background-color: #c0c0c0;">最大间隔分类</span>为核心原则的判别模型。对线性可分的二分类问题，它要寻找一个超平面（Hyperplane），使两类样本被正确分开，同时边界到两侧样本的最小距离尽可能大。这个最小距离对应的缓冲区称为间隔（Margin）。</p>
<p>也正因为优化目标是最大间隔，SVM 并不是“随便找一条能分开两类样本的直线/超平面”，而是要找那条对两类样本都留出最大安全缓冲区的边界。边界离两类样本都越远，模型对噪声、标注扰动与局部数据波动通常越稳。</p>
<p>从数学形式看，SVM 最终会落到二次规划（Quadratic Programming, QP）。所谓二次规划，就是：<span style="background-color: #c0c0c0;">目标函数是变量的二次函数，约束是线性等式或线性不等式</span>。SVM 的目标是最小化 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\|\mathbf{w}\|_2^2\)</span>，约束则是每个样本都必须被放到正确一侧并留出至少 1 的函数间隔，因此它正好属于这一类优化问题。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/svm.jpg"><img class="alignnone size-full wp-image-41179" src="https://blog.gmem.cc/wp-content/uploads/2026/03/svm.jpg" alt="svm" width="1408" height="768" /></a></p>
<p>从结构上看，SVM 把三件事连在一起：</p>
<ol>
<li>几何：先定义“什么叫分得开”，以及“什么叫分得最稳”。</li>
<li>优化：把“最稳的边界”写成一个可求解的凸二次规划。</li>
<li>对偶：把问题改写成只依赖样本内积的形式，从而自然导出核技巧（Kernel Trick）。</li>
</ol>
<div class="blog_h4"><span class="graybg">从几何直觉到“最大间隔”</span></div>
<p>对二分类任务，设标签 <span displaypfx="inline-" class="mathjax-container">\(y_i\in\{-1,+1\}\)</span>。线性分类器先计算一个打分函数（Score Function）：</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x})=\mathbf{w}^\top\mathbf{x}+b\]</span>
<p>这里最好先把“函数写法”和“几何边界写法”区分清楚。像 <span displaypfx="inline-" class="mathjax-container">\(y=x\)</span> 这样的形式，强调的是“给定自变量（Independent Variable） <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，应变量（Dependent Variable） <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 如何变化”；但对分类器来说，更关键的问题不是“谁依赖谁”，而是<span style="background-color: #c0c0c0;">哪些点恰好落在边界上，以及点位于边界哪一侧</span>。因此同一条直线在几何里通常改写成隐式形式（Implicit Form） <span displaypfx="inline-" class="mathjax-container">\(x-y=0\)</span>。</p>
<p>一旦写成 <span displaypfx="inline-" class="mathjax-container">\(x-y=0\)</span>，就能直接看成二维超平面方程 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=0\)</span>：令 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}=(x,y)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}=(1,-1)^\top\)</span>、<span displaypfx="inline-" class="mathjax-container">\(b=0\)</span>，便有 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=x-y\)</span>。此时直线方向向量（Direction Vector）可以取 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{v}=(1,1)^\top\)</span>，因为沿这条线移动时 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 同时增加；而 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{v}=1\cdot 1+(-1)\cdot 1=0\)</span>，说明 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 与直线切向方向正交，所以它正是这条直线的法向量（Normal Vector）。更一般地，对任意边界 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=0\)</span>，系数向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 都垂直于边界，因此天然决定“正侧、负侧”和距离的度量方向。</p>
<p>再用它的符号做判别：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=\mathrm{sign}(f(\mathbf{x}))=\mathrm{sign}(\mathbf{w}^\top\mathbf{x}+b)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 是超平面的法向量（Normal Vector）。沿 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 方向移动， <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})\)</span> 会增大；沿反方向移动， <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})\)</span> 会减小。因此：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})=0\)</span>：点就在分类超平面上。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})&gt;0\)</span>：点落在法向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 指向的那一侧。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x})&lt;0\)</span>：点落在另一侧。</li>
</ul>
<p>超平面把空间分成正半空间（Positive Half-space）与负半空间（Negative Half-space）；分数的正负号直接给出样本位于哪一侧。</p>
<p>令 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}=\{\mathbf{x}:\mathbf{w}^\top\mathbf{x}+b=0\}\)</span> 表示超平面，令单位法向量（Unit Normal Vector）为 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{n}=\mathbf{w}/\|\mathbf{w}\|_2\)</span>。对样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i\)</span>，记 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_{\Pi,i}\)</span> 为它在超平面 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\)</span> 上的正交投影点（Orthogonal Projection），因此 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_{\Pi,i}+b=0\)</span>，且 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i-\mathbf{x}_{\Pi,i}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 平行。点 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i\)</span> 到超平面的带符号距离（Signed Distance）定义为该位移在单位法向量方向上的投影长度：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{dist}_{\pm}(\mathbf{x}_i,\mathcal{H})=\mathbf{n}^\top(\mathbf{x}_i-\mathbf{x}_{\Pi,i})=\frac{\mathbf{w}^\top\mathbf{x}_i+b}{\|\mathbf{w}\|_2}\]</span>
<p>分子 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_i+b\)</span> 衡量点在法向量方向上偏离超平面的代数量；分母 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_2\)</span> 对法向量做归一化，去掉“同一个超平面可写成不同倍数方程”的尺度影响；符号保留样本位于超平面哪一侧的信息。</p>
<p>带符号距离区分了超平面的两侧，但正类样本与负类样本的正确侧相反。将标签 <span displaypfx="inline-" class="mathjax-container">\(y_i\in\{-1,+1\}\)</span> 乘入后，得到几何间隔（Geometric Margin）：</p>
<span displaypfx="" class="mathjax-container">\[\gamma_i=\frac{y_i(\mathbf{w}^\top\mathbf{x}_i+b)}{\|\mathbf{w}\|_2}\]</span>
<p>几何间隔是<span style="background-color: #c0c0c0;">用标签修正后的带符号距离</span>。因此：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\gamma_i&gt;0\)</span>：样本在正确一侧，被正确分类。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\gamma_i=0\)</span>：样本正好压在边界上。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\gamma_i&lt;0\)</span>：样本落到错误一侧，被误分类。</li>
</ul>
<p>SVM 的目标是让所有训练样本中最小的那个 <span displaypfx="inline-" class="mathjax-container">\(\gamma_i\)</span> 尽可能大，即最大化“最坏样本到边界的正确方向距离”：</p>
<span displaypfx="" class="mathjax-container">\[\gamma=\min_i \gamma_i\]</span>
<p>这就是最大间隔（Maximum Margin）的含义：不是让“平均样本”离边界远，而是让最危险、最靠近边界的样本也尽量安全。</p>
<div class="blog_h4"><span class="graybg">硬间隔 SVM（Hard-margin SVM）原始问题</span></div>
<p>先看线性可分（Linearly Separable）的情形。所谓线性可分，就是存在某个 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{w},b)\)</span>，使每个样本都在与自己标签一致的一侧。这件事可以统一写成：</p>
<span displaypfx="" class="mathjax-container">\[y_i(\mathbf{w}^\top\mathbf{x}_i+b)&gt;0,\quad \forall i\]</span>
<p>为什么这里是 <span displaypfx="inline-" class="mathjax-container">\(&gt;0\)</span>？因为它等价于“符号一致”：当 <span displaypfx="inline-" class="mathjax-container">\(y_i=+1\)</span> 时，要求 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_i+b&gt;0\)</span>，即正类样本必须落在正半空间；当 <span displaypfx="inline-" class="mathjax-container">\(y_i=-1\)</span> 时，要求 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_i+b&lt;0\)</span>，即负类样本必须落在负半空间。若只等于 0，则样本恰好压在分类边界上，不属于严格可分，因为此时它没有任何安全间隔，符号判别也处在临界点。</p>
<p>不过， <span displaypfx="inline-" class="mathjax-container">\(y_i(\mathbf{w}^\top\mathbf{x}_i+b)\)</span> 还不是几何距离，因为把 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{w},b)\)</span> 同时乘以任意正常数 <span displaypfx="inline-" class="mathjax-container">\(t&gt;0\)</span>，超平面 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=0\)</span> 完全不变，但这个量会整体乘上 <span displaypfx="inline-" class="mathjax-container">\(t\)</span>。因此它只是一个未归一化的间隔量，通常称为函数间隔（Functional Margin）。</p>
<p>为了消掉这个缩放自由度，SVM 采用一个标准定标：强制所有样本的最小函数间隔等于 1，也就是要求</p>
<span displaypfx="" class="mathjax-container">\[y_i(\mathbf{w}^\top\mathbf{x}_i+b)\ge 1,\quad \forall i\]</span>
<p>这条约束把两类样本同时编码进来：当 <span displaypfx="inline-" class="mathjax-container">\(y_i=+1\)</span> 时，它变成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_i+b\ge 1\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(y_i=-1\)</span> 时，它变成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}_i+b\le -1\)</span>。于是分类边界两侧又出现两条平行的“间隔边界”（Margin Boundaries）：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{w}^\top\mathbf{x}+b=+1,\quad \mathbf{w}^\top\mathbf{x}+b=-1\]</span>
<p>因为平行超平面 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=c_1\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top\mathbf{x}+b=c_2\)</span> 之间的距离是 <span displaypfx="inline-" class="mathjax-container">\(|c_1-c_2|/\|\mathbf{w}\|_2\)</span>，所以这两条间隔边界之间的宽度为</p>
<span displaypfx="" class="mathjax-container">\[\frac{|(+1)-(-1)|}{\|\mathbf{w}\|_2}=\frac{2}{\|\mathbf{w}\|_2}\]</span>
<p>在这个定标下，离分类边界最近的样本几何间隔恰好是</p>
<span displaypfx="" class="mathjax-container">\[\gamma=\min_i \frac{y_i(\mathbf{w}^\top\mathbf{x}_i+b)}{\|\mathbf{w}\|_2}=\frac{1}{\|\mathbf{w}\|_2}\]</span>
<p>因此，最大化几何间隔就等价于最小化 <span displaypfx="inline-" class="mathjax-container">\(\|\mathbf{w}\|_2\)</span>；为了得到标准的凸二次目标，通常写成：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ \frac{1}{2}\|\mathbf{w}\|_2^2\quad \text{s.t.}\quad y_i(\mathbf{w}^\top \mathbf{x}_i+b)\ge 1\]</span>
<p>这就是硬间隔 SVM 的原始问题（Primal Problem）。现在“二次规划”这个术语也具体了：目标函数 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\|\mathbf{w}\|_2^2\)</span> 是关于参数的凸二次函数，而约束 <span displaypfx="inline-" class="mathjax-container">\(y_i(\mathbf{w}^\top \mathbf{x}_i+b)\ge 1\)</span> 对 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{w},b)\)</span> 是线性的，所以这是一个凸二次规划，并且可以求到全局最优解。</p>
<div class="blog_h4"><span class="graybg">拉格朗日函数与 KKT：为什么只剩少数“支持向量”</span></div>
<p>这一段只回答一个问题：训练集中明明有很多样本，为什么最后真正决定分类边界的，往往只有少数几个点。这个结论的严格来源是 KKT 条件，但它的直观含义并不抽象：<span style="background-color: #c0c0c0;">只有那些真正把最大间隔边界“卡住”的样本，才会在最优解里留下非零权重</span>。</p>
<p>硬间隔 SVM 的原始问题是：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\boldsymbol{w},b}\frac{1}{2}\|\boldsymbol{w}\|_2^2\quad \text{s.t.}\quad y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)\ge 1,\ \forall i\]</span>
<p>这里目标函数 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\|\boldsymbol{w}\|_2^2\)</span> 想把边界做得尽量“简单”，也就是让法向量尽量短，从而把间隔做大；而每个训练样本都在提出自己的硬约束：它不仅要被分对，还必须距离边界至少有 1 个单位的函数间隔。把这两股力量写到同一个式子里，就得到拉格朗日函数（Lagrangian）：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(\boldsymbol{w},b,\boldsymbol{\alpha})=\frac{1}{2}\|\boldsymbol{w}\|_2^2-\sum_{i=1}^{N}\alpha_i\big(y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)-1\big),\quad \alpha_i\ge 0\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本对应的拉格朗日乘子（Lagrange Multiplier）。在 SVM 里，可以把它直接理解为“第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本对当前分类边界施加了多大压力”。压力越大，说明这个样本越在真正影响边界的位置；压力为 0，说明这个样本虽然在训练集里，但最优边界并不需要它来支撑。</p>
<p>这个问题的读法可以想成一个“边界往外推、样本往回顶”的平衡过程：</p>
<ul>
<li><span style="background-color: #c0c0c0;">模型一侧</span> 想让 <span displaypfx="inline-" class="mathjax-container">\(\|\boldsymbol{w}\|_2\)</span> 尽量小，从而把间隔尽量做大。</li>
<li><span style="background-color: #c0c0c0;">约束一侧</span> 则由每个样本通过 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 施加压力：谁越接近边界、越可能破坏间隔，谁就越值得保留权重。</li>
</ul>
<p>于是，离边界很远的样本会发生什么，就变得非常直观。若某个样本满足</p>
<span displaypfx="" class="mathjax-container">\[y_i(\boldsymbol{w}^\top\boldsymbol{x}_i+b)&gt;1\]</span>
<p>说明它不仅分对了，而且还有额外安全余量。这个点对“边界能否继续外推”没有形成真正阻碍，因此最优时它对应的 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 会被压到 0。相反，若某个样本恰好满足</p>
<span displaypfx="" class="mathjax-container">\[y_i(\boldsymbol{w}^\top\boldsymbol{x}_i+b)=1\]</span>
<p>它就正贴在间隔边界上，是“再往外推一点就会出问题”的临界点。这样的样本才有资格在最优解中保留非零权重。</p>
<p>KKT 条件（Karush–Kuhn–Tucker Conditions）把这个直觉写成严格公式。第一条来自驻点条件（Stationarity）：</p>
<span displaypfx="" class="mathjax-container">\[\nabla_{\boldsymbol{w}}\mathcal{L}=0\Rightarrow \boldsymbol{w}=\sum_{i=1}^{N}\alpha_i y_i \boldsymbol{x}_i\]</span>
<p>这条式子的含义非常重要。它说明最终的法向量 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\)</span> 并不是由全部样本平均决定的，而是由训练样本的加权和决定的。这里：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个训练样本；</li>
<li><span displaypfx="inline-" class="mathjax-container">\(y_i\alpha_i\)</span> 是它的“带符号权重”；</li>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i=0\)</span>，该样本就完全不会出现在 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\)</span> 的表达式里。</li>
</ul>
<p>第二条关键条件是互补松弛（Complementary Slackness）：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_i\big(y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)-1\big)=0\]</span>
<p>这条式子可以直接逐项阅读：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本对边界施加的压力；</li>
<li><span displaypfx="inline-" class="mathjax-container">\(y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)-1\)</span> 是该样本相对于间隔边界的“松弛量”；当它大于 0 时，说明样本在安全区里；当它等于 0 时，说明样本正好贴边。</li>
</ul>
<p>由于这两个量的乘积必须等于 0，所以只可能出现两种情况：</p>
<ul>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)-1&gt;0\)</span>，说明该样本有安全余量，那么必须有 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i=0\)</span>。</li>
<li>若 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i&gt;0\)</span>，说明该样本仍在对边界施压，那么必须有 <span displaypfx="inline-" class="mathjax-container">\(y_i(\boldsymbol{w}^\top \boldsymbol{x}_i+b)=1\)</span>。</li>
</ul>
<p>这就是支持向量（Support Vector）的严格定义来源：<span style="background-color: #c0c0c0;">在硬间隔 SVM 中，只有恰好贴在间隔边界上的样本，才可能对应非零 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span>；这些样本共同决定最终超平面的位置</span>。其余样本虽然被正确分类，但因为离边界还有余量，所以在最优解里不再起作用。</p>
<p>如果继续沿着这个结论往下看，就会发现 SVM 的稀疏性完全不是偶然现象，而是 KKT 的直接产物。训练集里可以有大量“安全样本”，但最优边界只需要被少数临界样本支撑起来。名字“支持向量”说的正是这件事：这些点不是普通数据点，而是真正在几何上把边界撑住的点。</p>
<div class="blog_h4"><span class="graybg">对偶问题（Dual）：把“求边界”改写成“给样本分权重”</span></div>
<p>前面已经看到：KKT 让 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 变成了“谁在真正顶住边界”的刻度。但如果这里只停在 KKT，还是会留下一个疑问：<span style="background-color: #c0c0c0;">对偶问题到底是怎么从原始问题里长出来的</span>？关键动作只有一步：先把 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 固定住，把拉格朗日函数当成关于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 的函数来最小化；然后再回过头，只对 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 做最大化。</p>
<p>也就是说，原始问题里我们直接求“哪条边界最好”：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ \frac{1}{2}\|\mathbf{w}\|_2^2\quad \text{s.t.}\quad y_i(\mathbf{w}^\top\mathbf{x}_i+b)\ge 1\]</span>
<p>而引入拉格朗日乘子之后，可以先看下面这个函数：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}(\mathbf{w},b,\alpha)=\frac{1}{2}\|\mathbf{w}\|_2^2-\sum_{i=1}^{N}\alpha_i\Big(y_i(\mathbf{w}^\top \mathbf{x}_i+b)-1\Big),\quad \alpha_i\ge 0\]</span>
<p>对固定的 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span>，它就是一个关于 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 的凸函数。于是我们先做内层最小化：</p>
<span displaypfx="" class="mathjax-container">\[g(\alpha)=\min_{\mathbf{w},b}\ \mathcal{L}(\mathbf{w},b,\alpha)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(g(\alpha)\)</span> 就叫对偶函数（Dual Function）。它表示：<span style="background-color: #c0c0c0;">假设每个样本的施压强度已经给定，边界那一侧最好的回应会是什么</span>。</p>
<p>这一步的好处是， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 可以被显式消掉。对 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 求驻点条件，有：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial \mathbf{w}}=0\Rightarrow \mathbf{w}=\sum_{i=1}^{N}\alpha_i y_i\mathbf{x}_i\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial b}=0\Rightarrow \sum_{i=1}^{N}\alpha_i y_i=0\]</span>
<p>这两条式子非常关键：第一条说明法向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 完全由训练样本线性组合出来；第二条说明正负两类样本的“总施压”必须平衡。把它们代回去，原来依赖 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 的问题就变成只依赖 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 的问题：</p>
<span displaypfx="" class="mathjax-container">\[\max_{\alpha}\ \sum_{i=1}^{N}\alpha_i-\frac{1}{2}\sum_{i=1}^{N}\sum_{j=1}^{N}\alpha_i\alpha_j y_i y_j\,\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<span displaypfx="" class="mathjax-container">\[\text{s.t.}\quad \alpha_i\ge 0,\quad \sum_{i=1}^{N}\alpha_i y_i=0\]</span>
<p>这就是 SVM 的对偶问题（Dual Problem）。现在可以看出它为什么叫“对偶”：它没有再直接问“<span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 应该是多少”，而是改问“每个样本应该分到多大的权重 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span>，才能共同把最优边界顶出来”。</p>
<p>这样改写有两个直接收益。第一，支持向量为什么稀疏会变得一眼可见：若某个样本最终 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i^*=0\)</span>，它就自动从 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}=\sum_i \alpha_i y_i\mathbf{x}_i\)</span> 里消失。第二，对偶目标里出现的样本方式只剩内积 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i^\top\mathbf{x}_j\)</span>，这正是后面引入核函数（Kernel）的入口。</p>
<p>把最优权重 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i^*\)</span> 求出来后，分类器可以直接写成：</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x})=\mathrm{sign}\left(\sum_{i=1}^{N}\alpha_i^* y_i\,\mathbf{x}_i^\top \mathbf{x}+b^*\right)\]</span>
<p>这个式子的含义很具体：新样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 会与训练样本做内积（Inner Product），也就是计算相似度；每个训练样本按自己的类别符号 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 和影响系数 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i^*\)</span> 投一票；最后把这些票加总，再加上偏置 <span displaypfx="inline-" class="mathjax-container">\(b^*\)</span>，看结果落在哪一侧。</p>
<p>由于绝大多数样本的 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i^*=0\)</span>，真正参与这次投票的通常只有支持向量。因此 SVM 的预测阶段常带有一个很强的稀疏性（Sparsity）：<span style="background-color: #c0c0c0;">不是所有训练样本都在持续发声，真正起作用的只是边界附近那一小部分点</span>。</p>
<p>偏置 <span displaypfx="inline-" class="mathjax-container">\(b^*\)</span> 可以用任一支持向量恢复。若 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_k\)</span> 是一个支持向量，则它满足 <span displaypfx="inline-" class="mathjax-container">\(y_k(\mathbf{w}^{*\top}\mathbf{x}_k+b^*)=1\)</span>，因此</p>
<span displaypfx="" class="mathjax-container">\[b^*=y_k-\mathbf{w}^{*\top}\mathbf{x}_k\]</span>
<div class="blog_h4"><span class="graybg">核函数（Kernel）：把“线性边界”搬到更合适的空间</span></div>
<p>对偶问题真正重要的地方，不只是“把变量从 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w},b\)</span> 换成了 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span>”，而是它把 SVM 的全部数据依赖压缩成了样本之间的内积（Inner Product）：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{x}_i^\top\mathbf{x}_j\]</span>
<p>这一步非常关键，因为它说明：<span style="background-color: #c0c0c0;">SVM 在对偶形式里并不需要直接看到样本坐标本身，它只需要知道样本彼此有多相似</span>。只要这种“相似度”还能写成某个空间里的内积，SVM 的训练与预测公式就都可以照搬。</p>
<p>于是核技巧（Kernel Trick）的引入就变得自然了。设有一个特征映射（Feature Map）<span displaypfx="inline-" class="mathjax-container">\(\phi(\mathbf{x})\)</span>，它把原始样本送到更高维、甚至无限维的特征空间（Feature Space）。如果我们真的显式去算这个高维向量，代价往往很大；但对偶形式只关心内积，因此只要能直接计算</p>
<span displaypfx="" class="mathjax-container">\[K(\mathbf{x}_i,\mathbf{x}_j)=\phi(\mathbf{x}_i)^\top\phi(\mathbf{x}_j)\]</span>
<p>就等价于“隐式地”在特征空间里做线性 SVM，而不必真的把 <span displaypfx="inline-" class="mathjax-container">\(\phi(\mathbf{x})\)</span> 写出来。这个直接在原空间里计算特征空间内积的技巧，就叫核函数（Kernel Function）或核技巧（Kernel Trick）。</p>
<p>这也解释了为什么 kernel 是从 dual 里长出来的，而不是额外拼上去的：若你还停留在原始问题里，眼前看到的仍是显式参数 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span>；而一旦进入对偶形式，表达式里只剩 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}_i^\top\mathbf{x}_j\)</span>，把它替换成别的“合法相似度”就成了最自然的一步。</p>
<p>于是决策函数从</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x})=\mathrm{sign}\left(\sum_{i=1}^{N}\alpha_i^* y_i\,\mathbf{x}_i^\top \mathbf{x}+b^*\right)\]</span>
<p>变成</p>
<span displaypfx="" class="mathjax-container">\[f(\mathbf{x})=\mathrm{sign}\left(\sum_{i=1}^{N}\alpha_i^* y_i\,K(\mathbf{x}_i,\mathbf{x})+b^*\right)\]</span>
<p>要点是：<span style="background-color: #c0c0c0;">特征空间里仍然是线性超平面；只是映回原空间后，边界看起来变成了弯的</span>。因此 kernel 不是“把线性 SVM 换成非线性模型”，而是“先把数据换到更容易线性可分的表示里，再继续做线性 SVM”。</p>
<p>从直觉上看，核函数本质上是在重新定义“两个样本像不像”。线性核（Linear Kernel）比较原始方向是否一致；多项式核（Polynomial Kernel）强调特征之间的组合关系；RBF / Gaussian 核更强调局部邻近性，因此很容易形成局部、弯曲的决策边界。</p>
<ul>
<li>线性核（Linear Kernel）：<span displaypfx="inline-" class="mathjax-container">\(K(\mathbf{x},\mathbf{z})=\mathbf{x}^\top\mathbf{z}\)</span>。</li>
<li>多项式核（Polynomial Kernel）：<span displaypfx="inline-" class="mathjax-container">\(K(\mathbf{x},\mathbf{z})=(\mathbf{x}^\top\mathbf{z}+c)^d\)</span>。</li>
<li>RBF / Gaussian 核：<span displaypfx="inline-" class="mathjax-container">\(K(\mathbf{x},\mathbf{z})=\exp(-\gamma\|\mathbf{x}-\mathbf{z}\|_2^2)\)</span>。</li>
</ul>
<p>一个典型例子是“同心圆”二分类：在二维平面里，内圈和外圈无法用一条直线分开；但如果映射到包含半径平方等特征的空间，类别就可能被一个超平面分开。核方法的价值就在这里：<span style="background-color: #c0c0c0;">原空间里看到的是弯曲边界，特征空间里做的仍然是线性分类</span>。</p>
<div class="blog_h4"><span class="graybg">软间隔 SVM（Soft-margin SVM）：允许少量违约，但要付代价</span></div>
<p>现实数据通常含噪声、离群点（Outlier）或类别重叠。若仍然要求“所有点都必须在间隔之外”，硬间隔 SVM 很可能根本无解，或者被少数异常点强行拉歪。软间隔 SVM（Soft-margin SVM）因此引入松弛变量（Slack Variable）<span displaypfx="inline-" class="mathjax-container">\(\xi_i\ge 0\)</span>，允许个别样本违反间隔约束：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b,\xi}\ \frac{1}{2}\|\mathbf{w}\|_2^2+C\sum_{i=1}^{N}\xi_i\quad \text{s.t.}\quad y_i(\mathbf{w}^\top \mathbf{x}_i+b)\ge 1-\xi_i,\ \xi_i\ge 0\]</span>
<p>这个式子只表达一件事：边界仍然希望尽量大，但违约要交罚款，罚款强度由 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 决定。对单个样本， <span displaypfx="inline-" class="mathjax-container">\(\xi_i\)</span> 的含义可以直接按大小来读：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\xi_i=0\)</span>：样本分类正确，且在间隔边界上或之外。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(0&lt;\xi_i\le 1\)</span>：样本仍在正确一侧，但已经挤进了间隔内部。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\xi_i&gt;1\)</span>：样本跨过了分类边界，已经被误分。</li>
</ul>
<p><span displaypfx="inline-" class="mathjax-container">\(C\)</span> 控制的是“对违约有多敏感”：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(C\)</span> 大：更重视把训练集分对，边界更硬，对噪声也更敏感。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(C\)</span> 小：更重视大间隔和整体稳定性，允许少量训练误差。</li>
</ul>
<p>软间隔下，支持向量的范围也更宽：不仅贴着间隔边界的点重要，落在间隔内部甚至被误分的点也会直接影响最优解。对应到对偶变量，常见情形是 <span displaypfx="inline-" class="mathjax-container">\(0&lt;\alpha_i&lt;C\)</span> 的点贴在间隔上，而 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i=C\)</span> 的点往往是间隔内样本或误分类样本。</p>
<p>把松弛变量消去后，软间隔 SVM 还可以写成更常见的合页损失（Hinge Loss）形式：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\mathbf{w},b}\ \frac{1}{2}\|\mathbf{w}\|_2^2+C\sum_{i=1}^{N}\max\big(0,\ 1-y_i(\mathbf{w}^\top \mathbf{x}_i+b)\big)\]</span>
<p>第一项限制模型复杂度，第二项惩罚分类违约。SVM 因此可以被看作“<span style="background-color: #c0c0c0;">大间隔 + 违约惩罚</span>”的组合，而支持向量则是这两股力量平衡后仍然留在最前线的样本。</p>
<div class="blog_h2"><span class="graybg">树模型与集成方法</span></div>
<p>这一类方法处理的核心问题是：当输入与输出之间存在明显非线性、阈值效应与高阶特征交互时，线性模型往往表达力不足，但工程上仍然希望模型具备较强可解释性、对表格数据友好、并且训练稳定。树模型通过递归切分（Recursive Partitioning）把输入空间划成若干局部区域；集成方法则进一步通过 Bagging 或 Boosting 提升泛化能力与精度。</p>
<div class="blog_h3"><span class="graybg">决策树</span></div>
<p>决策树（Decision Tree）把预测过程写成一串逐层切分的规则：内部节点负责提问，边负责根据答案分流，叶节点负责输出最终结果。它的优势不在于公式复杂，而在于模型结构与业务规则天然同构：每一条从根到叶的路径，都对应一条可读的判断链。</p>
<div class="blog_h4"><span class="graybg">决策树、分类树、回归树的关系</span></div>
<p>决策树是总称；分类树（Classification Tree）与回归树（Regression Tree）是它在两类监督学习任务上的具体形式。三者共享同一种树结构，但目标变量、切分准则与叶子输出不同。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">概念</td>
<td style="text-align: center;">预测目标</td>
<td style="text-align: center;">叶子输出</td>
<td style="text-align: center;">常用切分准则</td>
</tr>
</thead>
<tbody>
<tr>
<td>决策树（Decision Tree）</td>
<td>树模型的总称</td>
<td>取决于具体任务</td>
<td>取决于具体任务</td>
</tr>
<tr>
<td>分类树（Classification Tree）</td>
<td>离散标签（如“流失/不流失”）</td>
<td>类别或类别概率</td>
<td>基尼不纯度（Gini）、熵（Entropy）、信息增益（Information Gain）</td>
</tr>
<tr>
<td>回归树（Regression Tree）</td>
<td>连续数值（如“价格”“时长”）</td>
<td>一个数值常数</td>
<td>平方误差（Squared Error）、MSE / SSE 下降</td>
</tr>
</tbody>
</table>
<p>因此不要把“决策树”和“分类树”当成并列概念。更准确的表述是：<span style="background-color: #c0c0c0;">分类树与回归树都是决策树；前者预测类别，后者预测数值</span>。</p>
<div class="blog_h4"><span class="graybg">结构与统一公式</span></div>
<p>设某个节点上落入的样本集合为 <span displaypfx="inline-" class="mathjax-container">\(S\)</span>，大小为 <span displaypfx="inline-" class="mathjax-container">\(|S|\)</span>。对某个候选切分（Split）<span displaypfx="inline-" class="mathjax-container">\(\phi\)</span>，例如“第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个特征 <span displaypfx="inline-" class="mathjax-container">\(x_j\le t\)</span>”，样本会被分成左右两个子节点：</p>
<span displaypfx="" class="mathjax-container">\[S_L(\phi)=\{(\mathbf{x}_i,y_i)\in S:\ x_{i,j}\le t\},\quad S_R(\phi)=S\setminus S_L(\phi)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(S_L\)</span> 是满足切分条件的样本集合，<span displaypfx="inline-" class="mathjax-container">\(S_R\)</span> 是剩余样本集合。树在每个节点都会尝试多个候选切分 <span displaypfx="inline-" class="mathjax-container">\(\phi\)</span>，并保留收益最大的那个。</p>
<p>为了避免分别记忆分类树与回归树的训练目标，可以先写成统一形式。设 <span displaypfx="inline-" class="mathjax-container">\(I(S)\)</span> 表示“节点 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 当前有多乱”或“在该节点上预测误差有多大”，则一次切分的收益可统一写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Gain}(S,\phi)=I(S)-\frac{|S_L(\phi)|}{|S|}I\!\left(S_L(\phi)\right)-\frac{|S_R(\phi)|}{|S|}I\!\left(S_R(\phi)\right)\]</span>
<p>这条式子的含义非常直接：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(I(S)\)</span>：切分前，这个节点有多混乱。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(I(S_L),I(S_R)\)</span>：切分后，左右子节点各自还有多混乱。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\frac{|S_L|}{|S|},\frac{|S_R|}{|S|}\)</span>：左右子节点占父节点样本的比例。要乘这个比例，是因为大子节点对总体误差的影响更大，小子节点不能和大子节点拥有同样权重。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{Gain}(S,\phi)\)</span> 越大，说明这次切分越有效。</li>
</ul>
<p>分类树与回归树的差别，正体现在 <span displaypfx="inline-" class="mathjax-container">\(I(S)\)</span> 具体取什么。</p>
<div class="blog_h4"><span class="graybg">决策树：一个整体例子</span></div>
<p>在贷款审批（Loan Approval）场景中，决策树可以直接写成规则链。根节点先按负债收入比（Debt-to-Income Ratio）切分；若负债收入比过高，再看是否有逾期记录；若负债收入比正常，再看信用评分（Credit Score）与近 6 个月收入稳定性。最终某个叶节点可能对应“直接通过”，另一个叶节点对应“人工复核”，再另一个叶节点对应“拒绝”。这就是决策树最重要的工程价值：它不只是给出一个分数，还给出一条可追溯的判断路径。</p>
<div class="blog_h4"><span class="graybg">分类树：目标、公式与含义</span></div>
<p>分类树的目标是让每个叶节点里的标签尽量单一。若一个节点里几乎都是同一类样本，这个节点就“纯”；若各类样本混在一起，这个节点就“不纯”。</p>
<p>设类别集合为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{K}\)</span>，类别 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 在节点 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 中的占比记为：</p>
<span displaypfx="" class="mathjax-container">\[p_k(S)=\frac{\text{节点 }S\text{ 中标签为 }k\text{ 的样本数}}{|S|}\]</span>
<p>常用的不纯度（Impurity）有两种。</p>
<p>基尼不纯度（Gini Impurity）：</p>
<span displaypfx="" class="mathjax-container">\[G(S)=1-\sum_{k\in\mathcal{K}}p_k(S)^2\]</span>
<p>它可以读成“随机抽两个样本时，标签不一致的倾向有多强”。若节点里全是同一类，则某个 <span displaypfx="inline-" class="mathjax-container">\(p_k=1\)</span>、其余为 0，此时 <span displaypfx="inline-" class="mathjax-container">\(G(S)=0\)</span>，说明节点已经纯净；若二分类里两类各占一半，则 <span displaypfx="inline-" class="mathjax-container">\(G(S)=1-(0.5^2+0.5^2)=0.5\)</span>，说明混杂程度较高。</p>
<p>熵（Entropy）：</p>
<span displaypfx="" class="mathjax-container">\[H(S)=-\sum_{k\in\mathcal{K}}p_k(S)\log p_k(S)\]</span>
<p>熵衡量的是“不确定性”。若节点里全是同一类，则不需要再猜，熵为 0；若各类比例接近，说明不确定性高，熵也更大。</p>
<p>信息增益（Information Gain）就是“切分前的不确定性”减去“切分后的加权不确定性”：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{IG}(S,\phi)=H(S)-\frac{|S_L(\phi)|}{|S|}H\!\left(S_L(\phi)\right)-\frac{|S_R(\phi)|}{|S|}H\!\left(S_R(\phi)\right)\]</span>
<p>这条公式的每一部分都对应一个明确动作：</p>
<ul>
<li>第一项 <span displaypfx="inline-" class="mathjax-container">\(H(S)\)</span> 是切分前的混乱程度。</li>
<li>后两项是切分后左右子节点各自的混乱程度，并按样本占比加权求和。</li>
<li>两者相减，就是这次切分让节点“变纯”了多少。</li>
</ul>
<p>有些实现使用基尼下降而不是信息增益，本质上是同一件事：<span style="background-color: #c0c0c0;">选择那个能让子节点更纯、让标签更集中的切分</span>。</p>
<div class="blog_h4"><span class="graybg">分类树：实际例子——用户流失预警</span></div>
<p>设任务是预测“一个用户未来 30 天是否流失”。标签只有两类：流失 / 未流失，因此这是典型分类树问题。候选特征可以包括：最近 7 天登录次数、最近 30 天是否投诉、是否还有未使用优惠券、最近一次下单距今天数。</p>
<p>假设根节点先尝试切分“最近 7 天登录次数 &lt; 2”。这次切分后，左子节点里的用户大多已经很久不活跃，且流失比例显著升高；右子节点里的用户则活跃度更高、留存率更好。此时无论用基尼还是熵计算，左右节点的加权不纯度都会明显低于父节点，因此这会成为一个高质量切分。</p>
<p>继续往下，左子节点还可以再按“最近 30 天是否投诉”切分：低活跃且有投诉的用户，叶节点里可能出现“82% 最终流失”；低活跃但无投诉的用户，叶节点里可能是“61% 流失”。此时叶子不只给出类别，还可给出经验概率。工程上，这样的输出就能直接用于运营动作：高风险叶子推召回优惠券，中风险叶子推客服回访，低风险叶子不干预。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/descision-tree-1.jpg"><img class="alignnone size-full wp-image-41221" src="https://blog.gmem.cc/wp-content/uploads/2026/03/descision-tree-1.jpg" alt="descision-tree-1" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">回归树：目标、公式与含义</span></div>
<p>回归树处理的是连续数值目标，例如价格、时长、销量、能耗。它不追求“类别更纯”，而追求“同一个叶节点里的数值尽量接近”。</p>
<p>若某个叶节点 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 最终只输出一个常数 <span displaypfx="inline-" class="mathjax-container">\(c\)</span>，那么在平方误差（Squared Error）下，最优输出不是中位数，而是均值：</p>
<span displaypfx="" class="mathjax-container">\[c^*(S)=\arg\min_c\sum_{(\mathbf{x}_i,y_i)\in S}(y_i-c)^2=\frac{1}{|S|}\sum_{(\mathbf{x}_i,y_i)\in S}y_i\]</span>
<p>之所以是均值，是因为平方误差会把所有偏差向两边拉平，而均值正是使平方偏差和最小的那个常数。</p>
<p>在这个叶节点上，最小平方误差和（Sum of Squared Errors, SSE）为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{SSE}(S)=\sum_{(\mathbf{x}_i,y_i)\in S}\left(y_i-c^*(S)\right)^2\]</span>
<p>它表示节点里所有样本值围绕叶子预测值 <span displaypfx="inline-" class="mathjax-container">\(c^*(S)\)</span> 的总波动。SSE 越大，说明这个节点里的样本值越分散，单用一个常数代表它们的效果越差。</p>
<p>因此一次切分的目标是让切分后的总误差尽量小，也可写成误差下降尽量大：</p>
<span displaypfx="" class="mathjax-container">\[\Delta(S,\phi)=\mathrm{SSE}(S)-\mathrm{SSE}\!\left(S_L(\phi)\right)-\mathrm{SSE}\!\left(S_R(\phi)\right)\]</span>
<p>若更喜欢看平均误差，也可以把它写成 MSE（Mean Squared Error）形式：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{MSE}(S)=\frac{1}{|S|}\mathrm{SSE}(S)\]</span>
<p>等价地，也可以最小化切分后的加权平均误差：<span displaypfx="" class="mathjax-container">\[\frac{|S_L|}{|S|}\mathrm{MSE}(S_L)+\frac{|S_R|}{|S|}\mathrm{MSE}(S_R)\]</span></p>
<p>两种写法完全等价：SSE 强调总误差，MSE 强调平均误差；本质都是寻找让子节点内部数值更集中的切分。</p>
<div class="blog_h4"><span class="graybg">回归树：实际例子——外卖配送时长预测</span></div>
<p>设任务是预测一笔订单从接单到送达需要多少分钟。这是连续数值目标，因此属于回归树。候选特征可以包括：配送距离、是否下雨、是否晚高峰、商家出餐速度、骑手当前手中订单数。</p>
<p>根节点可能先按“配送距离是否大于 3 公里”切分。因为近距离订单与远距离订单的时长分布差异很大，这一步通常能显著降低节点内部方差。对远距离子节点，再按“是否下雨”切分；下雨天路况更慢、波动更大。对近距离子节点，则可能按“是否处于午晚高峰”切分。</p>
<p>假设某个叶节点对应“距离 &gt; 3 公里、下雨、晚高峰”这类订单，这个叶节点里的训练样本平均送达时长是 47 分钟，那么该叶子的预测值就是 47。另一个叶节点若对应“距离 &lt; 2 公里、不下雨、非高峰”，其平均时长可能只有 18 分钟。回归树的预测逻辑不是拟合一条全局直线，而是把不同业务情境分段，再在每段内给出一个局部平均值。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/decision-tree-2.png"><img class="alignnone size-full wp-image-41227" src="https://blog.gmem.cc/wp-content/uploads/2026/03/decision-tree-2.png" alt="decision-tree-2" width="1469" height="1035" /></a></p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<ul>
<li>需要把模型输出翻译成可审计规则，例如风控、审批、运营分层、客服分流。</li>
<li>数据以表格（Tabular）为主，且存在显著非线性、阈值效应或特征交互。</li>
<li>希望同时处理连续特征与离散特征，并保留较强可解释性。</li>
<li>注意：单棵树方差高、容易过拟合，通常需要限制最大深度、最小叶子样本数或配合集成方法。</li>
</ul>
<div class="blog_h3"><span class="graybg">随机森林</span></div>
<p>随机森林（Random Forest）是 Bagging（Bootstrap Aggregating）在树模型上的经典实现：用 bootstrap 采样生成多份训练子集，训练多棵通常偏差较低、但对训练数据扰动高度敏感（高方差）的决策树，再把它们的输出聚合，以显著降低整体方差并提升鲁棒性。</p>
<p>这里的“高方差（High Variance）”指模型的估计方差（Estimator Variance）或预测方差（Prediction Variance）：如果训练集稍有变化，单棵树学到的分裂结构与最终预测就可能明显变化。随机森林利用多棵树的平均/投票，把这种由数据扰动带来的波动相互抵消。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/random-forest.jpg"><img class="alignnone size-full wp-image-41231" src="https://blog.gmem.cc/wp-content/uploads/2026/03/random-forest.jpg" alt="random-forest" width="1287" height="816" /></a></p>
<div class="blog_h4"><span class="graybg">算法与符号</span></div>
<p>给定训练集 <span displaypfx="inline-" class="mathjax-container">\(D=\{(\mathbf{x}_i,y_i)\}_{i=1}^{N}\)</span>。对 <span displaypfx="inline-" class="mathjax-container">\(m=1,\dots,M\)</span>：</p>
<ol>
<li>bootstrap 采样：从 <span displaypfx="inline-" class="mathjax-container">\(D\)</span> 有放回采样 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 次得到 <span displaypfx="inline-" class="mathjax-container">\(D_m\)</span>。所谓“有放回（Sampling with Replacement）”，是指每次抽到一个样本后，都先把它放回原数据集，再进行下一次抽样；因此同一个样本可能被重复抽中，而有些样本在这一轮里一次也没有被抽到。</li>
<li>训练一棵树 <span displaypfx="inline-" class="mathjax-container">\(T_m\)</span>：每个节点分裂时，只在随机选取的 <span displaypfx="inline-" class="mathjax-container">\(d'\)</span> 个特征上搜索最优切分（特征子采样，feature subsampling）。</li>
</ol>
<p>预测时，回归取平均，分类取多数投票：</p>
<span displaypfx="" class="mathjax-container">\[\hat y_{\text{reg}}(\mathbf{x})=\frac{1}{M}\sum_{m=1}^{M}T_m(\mathbf{x}),\quad \hat y_{\text{clf}}(\mathbf{x})=\mathrm{mode}\left(\{T_m(\mathbf{x})\}_{m=1}^{M}\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(T_m(\mathbf{x})\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 棵树对样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 的预测。回归任务把所有树的输出做平均，以减少波动；分类任务取众数 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{mode}(\cdot)\)</span>，也就是票数最多的类别。随机森林的稳定性正来自这种“多棵树共同决定”的聚合机制。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(M\)</span>：树的数量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(d'\)</span>：每次分裂考虑的特征数（常见经验：分类用 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{d}\)</span>，回归用 <span displaypfx="inline-" class="mathjax-container">\(d/3\)</span>）。</li>
</ul>
<div class="blog_h4"><span class="graybg">为什么有效：方差下降与“去相关”</span></div>
<p>若单棵树预测的方差为 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span>，不同树之间的相关系数近似为 <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span>（<span displaypfx="inline-" class="mathjax-container">\(0\le\rho\le 1\)</span>），则平均后的方差近似为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}\!\left(\frac{1}{M}\sum_{m=1}^{M}T_m(\mathbf{x})\right)\approx \sigma^2\left(\rho+\frac{1-\rho}{M}\right)\]</span>
<p>这个近似式子把随机森林为什么有效说得很清楚： <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 是单棵树自身的预测波动， <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span> 是树与树之间的相关性， <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 是树数。树越多， <span displaypfx="inline-" class="mathjax-container">\(\frac{1-\rho}{M}\)</span> 越小；树之间越不相似， <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span> 越低，最终平均后的波动就越小。</p>
<p>因此随机森林有两条主线：</p>
<ul>
<li>增加 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 降低 <span displaypfx="inline-" class="mathjax-container">\((1-\rho)/M\)</span> 项。</li>
<li>通过 bootstrap + 特征子采样降低相关性 <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span>，让集成真正“互补”。</li>
</ul>
<div class="blog_h4"><span class="graybg">OOB：不用额外验证集的误差估计</span></div>
<p>bootstrap 采样会重复抽到某些样本。对固定样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span>，一次采样中没被抽到的概率为 <span displaypfx="inline-" class="mathjax-container">\((1-\frac{1}{N})^N\approx e^{-1}\approx 0.368\)</span>。因此每棵树大约有 36.8% 的样本是袋外（Out-of-Bag, OOB）样本，可用它们评估该树对未见数据的表现，并对全森林给出近似验证误差。</p>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\((1-\frac{1}{N})\)</span> 表示“一次抽样没抽到某个固定样本”的概率，连续做 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 次后得到 <span displaypfx="inline-" class="mathjax-container">\((1-\frac{1}{N})^N\)</span>。当 <span displaypfx="inline-" class="mathjax-container">\(N\)</span> 足够大时，它逼近 <span displaypfx="inline-" class="mathjax-container">\(e^{-1}\)</span>，也就是约 36.8%。这就是为什么随机森林天然拥有一批“没参与这棵树训练”的 OOB 样本。</p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<ul>
<li>表格数据（Tabular）的强默认基线：非线性、特征交互、缺失值与尺度不一致都较鲁棒。</li>
<li>对超参数不敏感、训练稳定；可用特征重要性（Feature Importance）做解释与特征筛查。</li>
<li>当需要极致精度时，GBDT 家族往往更强；当需要更快推理/更小模型时，线性/浅层模型更合适。</li>
</ul>
<div class="blog_h3"><span class="graybg">梯度提升树（GBDT）</span></div>
<p>梯度提升树（Gradient Boosting Decision Tree, GBDT）是一类按序叠加回归树的加法模型（Additive Model）。模型从简单的初始预测 <span displaypfx="inline-" class="mathjax-container">\(F_0\)</span> 出发，在每一轮加入一棵新树，用于修正当前模型尚未拟合好的部分，从而逐步降低训练集上的经验风险（Empirical Risk）。</p>
<p>每一轮新增的树都对应一个修正函数（Correction Function）。它不直接重新学习标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>，而是拟合当前模型输出与目标之间尚未被解释的差异。多轮修正连续叠加后，模型会从粗糙预测逐步逼近目标函数。</p>
<p>一个直观比喻是：GBDT 像一组按顺序接手的阅卷老师。第一位老师先给出一个粗略分数，后面的每一位老师都不重做整张卷子，只专门检查前面模型错得最明显的地方，并在这些地方补上修正意见。树一棵接一棵叠加后，最终预测会越来越接近真实值。</p>
<p>这里每棵小树都不是独立完成任务的“大模型”，而是一个局部纠错器。它关心的是当前模型还没解释好的误差：哪些样本被高估了，哪些被低估了，以及这些误差集中出现在哪些特征区域。“梯度”对应当前损失下降最快的修正方向，“提升”则表示把这些小修正持续累加成一个更强的整体模型。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/gbdt.jpg"><img class="alignnone size-full wp-image-41247" src="https://blog.gmem.cc/wp-content/uploads/2026/03/gbdt.jpg" alt="gbdt" width="1408" height="768" /></a></p>
<div class="blog_h4"><span class="graybg">目标：最小化经验风险</span></div>
<p>GBDT 背后的目标非常直接：寻找一个函数 <span displaypfx="inline-" class="mathjax-container">\(F\)</span>，使训练集上的总损失最小：</p>
<span displaypfx="" class="mathjax-container">\[ \min_F\ \sum_{i=1}^{N}\ell\big(y_i,F(\mathbf{x}_i)\big) \]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\ell(y,F(\mathbf{x}))\)</span> 是单样本损失函数，平方损失、对数损失等都可以放进来。难点在于：函数 <span displaypfx="inline-" class="mathjax-container">\(F\)</span> 不是一个普通标量参数，而是一个复杂的预测函数；如果一次性同时优化所有树的结构和叶子输出，组合空间过大，几乎不可直接求解。</p>
<p>因此 GBDT 采用前向分步加法（Forward Stagewise Additive Modeling）：不一次求整个 <span displaypfx="inline-" class="mathjax-container">\(F\)</span>，而是把它写成逐步累加的形式，只在第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮新增一个修正函数 <span displaypfx="inline-" class="mathjax-container">\(f_m\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[ F_M(\mathbf{x})=F_0(\mathbf{x})+\nu\sum_{m=1}^{M}f_m(\mathbf{x}) \]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(F_M(\mathbf{x})\)</span> 表示：模型经过总共 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 轮提升后，对输入样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 输出的最终预测值。下标 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 表示“已经累计做了 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 次修正”，不是幂次，也不是某一棵单独的树。GBDT 的最终模型是许多轮小修正叠加后的总结果。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span>：一个输入样本的特征向量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(F_0(\mathbf{x})\)</span>：初始模型对样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 的预测，常取常数 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 使 <span displaypfx="inline-" class="mathjax-container">\(\sum_i \ell(y_i,c)\)</span> 最小。它可以理解为模型在还没有长出任何树之前给出的第一版粗略判断。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f_m(\mathbf{x})\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮新增的回归树在样本 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 上给出的修正值，负责弥补当前模型尚未拟合好的部分。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sum_{m=1}^{M}f_m(\mathbf{x})\)</span>：把前 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 轮所有修正树的输出加起来，得到总修正量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\nu\in(0,1]\)</span>：学习率（Shrinkage），控制每一步修正只走多大。它的作用不是改变方向，而是缩小步长，使训练更稳定、泛化更好。</li>
</ul>
<p>这个公式也可以按“底稿 + 反复批改”来理解： <span displaypfx="inline-" class="mathjax-container">\(F_0(\mathbf{x})\)</span> 是第一版预测，后面的每个 <span displaypfx="inline-" class="mathjax-container">\(f_m(\mathbf{x})\)</span> 都是在已有结果上补一小笔修正，最终的 <span displaypfx="inline-" class="mathjax-container">\(F_M(\mathbf{x})\)</span> 就是经历 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 次修正后的版本。</p>
<span displaypfx="" class="mathjax-container">\[ f_m=\arg\min_f\sum_{i=1}^{N}\ell\big(y_i,F_{m-1}(\mathbf{x}_i)+f(\mathbf{x}_i)\big) \]</span>
<p>这条式子表示：在第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮，模型要寻找一个新的修正函数 <span displaypfx="inline-" class="mathjax-container">\(f_m\)</span>，使它加到旧模型 <span displaypfx="inline-" class="mathjax-container">\(F_{m-1}\)</span> 上之后，训练集的总损失尽可能小。这里的 <span displaypfx="inline-" class="mathjax-container">\(\arg\min_f\)</span> 可以读作“在所有候选函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 里，找出那个能让目标最小的函数”。因此，求出来的不是一个数，而是当前这一轮最合适的新树。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(F_{m-1}(\mathbf{x}_i)\)</span>：前 <span displaypfx="inline-" class="mathjax-container">\(m-1\)</span> 轮模型在第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本上的当前预测值。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x}_i)\)</span>：候选新树在第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本上的修正值。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(F_{m-1}(\mathbf{x}_i)+f(\mathbf{x}_i)\)</span>：把新树加进去之后，这个样本的新预测值。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\ell\big(y_i,F_{m-1}(\mathbf{x}_i)+f(\mathbf{x}_i)\big)\)</span>：该样本在新预测下的损失。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sum_{i=1}^{N}\ell(\cdot)\)</span>：把所有样本的损失加总，得到这一轮希望尽量压低的整体目标。</li>
</ul>
<p>这一轮之所以写成 <span displaypfx="inline-" class="mathjax-container">\(F_{m-1}+f\)</span>，而不是重新求一个全新的 <span displaypfx="inline-" class="mathjax-container">\(F_m\)</span>，原因在于 GBDT 采用的是逐步修正策略：旧模型已经学到的部分先保留，新树只负责补上当前还没有拟合好的误差。这样每一轮只解决一个更小的局部问题，计算上更可行，也更符合“不断纠错”的直觉。</p>
<p>第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮只优化新增的修正函数 <span displaypfx="inline-" class="mathjax-container">\(f_m\)</span>，而把已有模型 <span displaypfx="inline-" class="mathjax-container">\(F_{m-1}\)</span> 视为固定量。这样做把原本难以整体求解的函数优化问题，拆成了一系列可逐步求解的局部优化问题。</p>
<div class="blog_h4"><span class="graybg">负梯度的来源</span></div>
<p>上面的子问题仍然不容易直接做，因为“最佳新树”本身还是一个复杂的函数搜索问题。GBDT 的关键近似是：在当前模型 <span displaypfx="inline-" class="mathjax-container">\(F_{m-1}\)</span> 附近，对损失做一阶展开，只看局部下降方向。</p>
<p>对单个样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span>，把新增修正记为 <span displaypfx="inline-" class="mathjax-container">\(f(\mathbf{x}_i)\)</span>，则有：</p>
<span displaypfx="" class="mathjax-container">\[ \ell\big(y_i,F_{m-1}(\mathbf{x}_i)+f(\mathbf{x}_i)\big) \approx \ell\big(y_i,F_{m-1}(\mathbf{x}_i)\big) + \frac{\partial \ell(y_i,F(\mathbf{x}_i))}{\partial F(\mathbf{x}_i)}\Big|_{F=F_{m-1}} f(\mathbf{x}_i) \]</span>
<p>一阶展开表明，新增函数 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 的最优局部方向由损失对模型输出的负梯度决定。因此第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮的伪残差（Pseudo-residual）定义为：</p>
<span displaypfx="" class="mathjax-container">\[ r_i^{(m)}=-\left.\frac{\partial \ell\left(y_i,F(\mathbf{x}_i)\right)}{\partial F(\mathbf{x}_i)}\right|_{F=F_{m-1}} \]</span>
<p>接下来的动作就自然了：不用树直接拟合标签 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span>，而是用一棵回归树去拟合这些伪残差 <span displaypfx="inline-" class="mathjax-container">\(r_i^{(m)}\)</span>。这棵树学到的，就是“当前模型在输入空间不同区域里，应该往哪个方向、修正多大”的分段常数近似。</p>
<p>模型更新写成：</p>
<span displaypfx="" class="mathjax-container">\[ F_m(\mathbf{x})=F_{m-1}(\mathbf{x})+\nu f_m(\mathbf{x}) \]</span>
<p>“梯度提升”这一名称对应的正是上述更新方式：优化对象不是有限维参数向量，而是预测函数本身；每一轮更新都沿着损失在函数空间中的负梯度方向加入一个新的修正函数。</p>
<div class="blog_h4"><span class="graybg">平方损失下的残差形式</span></div>
<p>若损失取平方损失</p>
<span displaypfx="" class="mathjax-container">\[ \ell(y,F)=\frac{1}{2}(y-F)^2 \]</span>
<p>则对模型输出求导：</p>
<span displaypfx="" class="mathjax-container">\[ \frac{\partial \ell(y,F)}{\partial F}=F-y \]</span>
<p>因此第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮的负梯度为：</p>
<span displaypfx="" class="mathjax-container">\[ r_i^{(m)}=-\left(F_{m-1}(\mathbf{x}_i)-y_i\right)=y_i-F_{m-1}(\mathbf{x}_i) \]</span>
<p>这正是当前预测与真实值之间的残差（Residual）。所以在回归任务里，人们常把 GBDT 说成“不断拟合残差”；这并不是另一套经验规则，而是平方损失下负梯度公式的直接结果。</p>
<div class="blog_h4"><span class="graybg">树作为局部修正器</span></div>
<p>因为树天然产生分段常数（Piecewise Constant）的局部修正。若某一轮中，一棵树把样本空间切成若干区域 <span displaypfx="inline-" class="mathjax-container">\(R_1,\dots,R_J\)</span>，那么它输出的是：</p>
<span displaypfx="" class="mathjax-container">\[ f_m(\mathbf{x})=\sum_{j=1}^{J}\gamma_j\mathbf{1}(\mathbf{x}\in R_j) \]</span>
<p>这意味着：模型不是对整个输入空间统一加一个修正，而是在每个局部区域里分别加一个常数修正 <span displaypfx="inline-" class="mathjax-container">\(\gamma_j\)</span>。这正适合处理表格数据里的阈值效应、非线性和特征交互。</p>
<div class="blog_h4"><span class="graybg">叶子输出的解析解</span></div>
<p>在平方损失下，若某个叶子覆盖的样本集合为 <span displaypfx="inline-" class="mathjax-container">\(S\)</span>，并用常数 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 拟合这一叶内的残差 <span displaypfx="inline-" class="mathjax-container">\(r_i\)</span>，则该叶子的局部目标是：</p>
<span displaypfx="" class="mathjax-container">\[ \min_{\gamma}\sum_{i\in S}(r_i-\gamma)^2 \]</span>
<p>对 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 求导并令其为 0：</p>
<span displaypfx="" class="mathjax-container">\[ \frac{d}{d\gamma}\sum_{i\in S}(r_i-\gamma)^2=-2\sum_{i\in S}(r_i-\gamma)=0 \]</span>
<span displaypfx="" class="mathjax-container">\[ \sum_{i\in S}r_i-|S|\gamma=0 \]</span>
<span displaypfx="" class="mathjax-container">\[ \gamma^*=\frac{1}{|S|}\sum_{i\in S}r_i \]</span>
<p>因此叶子输出之所以是均值，不是经验设定，而是平方损失下这个局部最小二乘问题的解析解。每一轮加入一棵树，本质上是在每个局部区域里补上一段“平均残差修正”。</p>
<div class="blog_h4"><span class="graybg">训练流程</span></div>
<ol>
<li>初始化 <span displaypfx="inline-" class="mathjax-container">\(F_0\)</span>，通常取使总体损失最小的常数模型。</li>
<li>对第 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 轮，计算所有样本的伪残差 <span displaypfx="inline-" class="mathjax-container">\(r_i^{(m)}\)</span>。</li>
<li>训练一棵回归树 <span displaypfx="inline-" class="mathjax-container">\(f_m\)</span> 去拟合 <span displaypfx="inline-" class="mathjax-container">\((\mathbf{x}_i,r_i^{(m)})\)</span>。</li>
<li>把这棵树按学习率 <span displaypfx="inline-" class="mathjax-container">\(\nu\)</span> 缩小后加到当前模型上。</li>
<li>重复多轮，直到验证集误差不再下降，或达到预设树数。</li>
</ol>
<p><span style="background-color: #c0c0c0;">GBDT 的本质，是把复杂的函数优化问题拆成许多轮局部修正，并在每一轮用一棵小树逼近当前损失下降最快的方向。</span></p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<ul>
<li>结构化表格任务中的强模型：广告、推荐、风控、运营排序、传统特征工程场景。</li>
<li>对特征尺度、非线性、缺失值较鲁棒；能自动学习高阶交互。</li>
<li>注意：对超参数更敏感（树深、学习率、树数、采样比例）；需要用验证集早停（Early Stopping）防止过拟合。</li>
</ul>
<div class="blog_h3"><span class="graybg">XGBoost</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>XGBoost（Extreme Gradient Boosting）是在梯度提升树（Gradient Boosting Decision Tree, GBDT）基础上的工程化强化版本。它关注的是：在保持高表达能力的同时，让树的生长过程更稳定、目标函数更明确、复杂度控制更系统。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>XGBoost 仍然采用加法模型（Additive Model）：当前模型由多棵树的输出求和得到，第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 轮只学习一棵新树 <span displaypfx="inline-" class="mathjax-container">\(f_t\)</span> 作为修正项。它的关键强化点有两条：第一，把新增树的学习写成显式的带正则优化问题；第二，对该目标做二阶近似，同时利用梯度（Gradient）和海森（Hessian）信息计算叶子输出与分裂增益。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>设第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 轮预测为 <span displaypfx="inline-" class="mathjax-container">\(\tilde y_i^{(t)}=\tilde y_i^{(t-1)}+f_t(\boldsymbol{x}_i)\)</span>。XGBoost 在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 轮优化的目标可写为：</p>
<span displaypfx="" class="mathjax-container">\[\text{Obj}^{(t)}=\sum_{i=1}^{N} \ell(y_i,\tilde y_i^{(t)})+\Omega(f_t)+\text{const}\]</span>
<p>在这个目标里， <span displaypfx="inline-" class="mathjax-container">\(\ell(y_i,\tilde y_i^{(t)})\)</span> 衡量第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个样本在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 轮更新后的预测误差， <span displaypfx="inline-" class="mathjax-container">\(\Omega(f_t)\)</span> 惩罚新树本身的复杂度， <span displaypfx="inline-" class="mathjax-container">\(\text{const}\)</span> 则表示与当前轮新树无关的常数项。也就是说，XGBoost 每一轮都在平衡“把误差降下去”和“不要把树长得太复杂”。</p>
<p>其中正则项通常取：</p>
<span displaypfx="" class="mathjax-container">\[\Omega(f)=\gamma T+\frac{\lambda}{2}\sum_{j=1}^{T}w_j^2\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 是叶子数， <span displaypfx="inline-" class="mathjax-container">\(\gamma T\)</span> 惩罚“树长出太多叶子”， <span displaypfx="inline-" class="mathjax-container">\(\frac{\lambda}{2}\sum_j w_j^2\)</span> 惩罚“每个叶子的输出值过大”。前者控制结构复杂度，后者控制数值幅度，两者合起来让模型更稳。</p>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 是叶子数， <span displaypfx="inline-" class="mathjax-container">\(w_j\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个叶子的输出， <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 控制“新增一个叶子是否值得”， <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 控制叶子输出过大的代价。对损失在当前预测附近做二阶泰勒展开，定义：</p>
<span displaypfx="" class="mathjax-container">\[g_i=\frac{\partial \ell(y_i,\tilde y_i)}{\partial \tilde y_i}\Big|_{\tilde y_i=\tilde y_i^{(t-1)}},\qquad h_i=\frac{\partial^2 \ell(y_i,\tilde y_i)}{\partial \tilde y_i^2}\Big|_{\tilde y_i=\tilde y_i^{(t-1)}}\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(g_i\)</span> 是一阶导数，表示“当前这个样本朝哪个方向改，损失下降最快”； <span displaypfx="inline-" class="mathjax-container">\(h_i\)</span> 是二阶导数，表示“这个方向有多陡、多稳定”。XGBoost 同时使用这两项信息，所以比只用一阶梯度的做法更精细。</p>
<p>则近似目标为：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\text{Obj}}^{(t)}\approx \sum_{i=1}^{N} \left(g_i f_t(\boldsymbol{x}_i)+\frac{1}{2}h_i f_t(\boldsymbol{x}_i)^2\right)+\Omega(f_t)\]</span>
<p>这条近似目标里， <span displaypfx="inline-" class="mathjax-container">\(f_t(\boldsymbol{x}_i)\)</span> 是新树在样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 上给出的修正值。线性项 <span displaypfx="inline-" class="mathjax-container">\(g_i f_t(\boldsymbol{x}_i)\)</span> 反映“沿当前方向修正是否有利”，二次项 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}h_i f_t(\boldsymbol{x}_i)^2\)</span> 反映“修正过大时会不会带来额外代价”。</p>
<p>若固定树结构，记落在叶子 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的样本集合为 <span displaypfx="inline-" class="mathjax-container">\(I_j\)</span>，并定义 <span displaypfx="inline-" class="mathjax-container">\(G_j=\sum_{i\in I_j}g_i\)</span>、<span displaypfx="inline-" class="mathjax-container">\(H_j=\sum_{i\in I_j}h_i\)</span>，则该叶子的最优输出为：</p>
<span displaypfx="" class="mathjax-container">\[w_j^*=-\frac{G_j}{H_j+\lambda}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(G_j\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个叶子里所有样本一阶梯度的总和， <span displaypfx="inline-" class="mathjax-container">\(H_j\)</span> 是二阶梯度总和。分子告诉模型这个叶子整体应该往哪个方向修正，分母则用二阶信息和正则项 <span displaypfx="inline-" class="mathjax-container">\(\lambda\)</span> 把修正幅度稳住。</p>
<p>某次分裂把父叶拆成左右两叶后的增益（Gain）为：</p>
<span displaypfx="" class="mathjax-container">\[\text{Gain}=\frac{1}{2}\left(\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{G^2}{H+\lambda}\right)-\gamma\]</span>
<p>这个增益式子比较的是“父叶不分裂”和“分成左右两叶”谁更划算。 <span displaypfx="inline-" class="mathjax-container">\(G_L,H_L\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(G_R,H_R\)</span> 分别是左右子叶的梯度统计量， <span displaypfx="inline-" class="mathjax-container">\(G,H\)</span> 是父叶统计量；最后减去 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span>，表示每多长一个叶子都要付复杂度代价。</p>
<p>这说明 XGBoost 的分裂同时考虑“收益”和“复杂度惩罚”，并以此控制结构扩张。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>用当前模型计算每个样本的一阶梯度 <span displaypfx="inline-" class="mathjax-container">\(g_i\)</span> 与二阶梯度 <span displaypfx="inline-" class="mathjax-container">\(h_i\)</span>。</li>
<li>在候选特征与候选阈值上搜索分裂增益最大的切分。</li>
<li>固定树结构后，利用 <span displaypfx="inline-" class="mathjax-container">\(w_j^*\)</span> 计算每个叶子的最优输出。</li>
<li>把新树加入现有模型，继续下一轮迭代。</li>
<li>预测时把所有树的输出累加，再映射到分类或回归结果。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在点击率预估（CTR Prediction）中，用户、广告和上下文之间往往存在复杂交互。XGBoost 可以通过逐轮加树自动学习“什么样的用户在什么时间、看到什么样的广告更容易点击”这类组合规则，因此在工业级表格任务中长期保持强竞争力。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：表格数据表现强，目标函数清晰，正则化明确，工程生态成熟。</li>
<li>局限：超参数较多；树深、学习率、树数、采样比例之间存在明显耦合。</li>
<li>适用场景：风控、广告、推荐、排序与一般结构化表格建模。</li>
</ul>
<div class="blog_h3"><span class="graybg">LightGBM</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>LightGBM 是面向大规模数据与高维稀疏特征优化的 GBDT 实现。它关注的核心问题是：当样本量和特征维度都很大时，如何降低分裂搜索的时间与内存成本，同时尽量不牺牲精度。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>LightGBM 的两个关键设计是直方图分桶（Histogram Binning）和叶子优先生长（Leaf-wise Growth）。前者把连续特征离散到有限个桶上，后者每一步都继续分裂当前收益最大的叶子，从而在相同叶子预算下更快降低训练误差。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>在优化目标上，LightGBM 与 GBDT / XGBoost 一致，仍然是逐轮加入树来降低损失。它的区别主要体现在计算方式：若某个连续特征被离散到 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 个桶，则算法只需在桶边界上搜索分裂点，而不必在所有实数取值上逐一枚举。这样，每个节点维护的是桶级梯度统计量，而非逐样本的原始实数值。</p>
<p>叶子优先生长意味着：算法始终选择当前分裂增益最大的叶子继续向下扩展。它通常比按层生长（Level-wise）更激进，因而在同样叶子数限制下更容易得到较低训练误差，但也更容易在局部区域长出很深的树。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>将连续特征离散到有限个桶，并统计桶级梯度信息。</li>
<li>在每个节点上基于桶统计量搜索最优切分。</li>
<li>选择全局增益最大的叶子继续分裂。</li>
<li>使用最大深度、最大叶子数、最小叶子样本数等约束抑制过拟合。</li>
<li>预测时沿树路径到达叶子并累加所有树的输出。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在大规模推荐粗排任务中，输入特征通常极高维且高度稀疏。LightGBM 能在保留较强拟合能力的同时显著缩短训练时间，因此非常适合需要频繁重训的工业流水线。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：训练快、内存占用低，对大规模稀疏特征友好。</li>
<li>局限：叶子优先策略若约束不足，局部树会过深，过拟合风险更高。</li>
<li>适用场景：超大规模表格数据、稀疏特征建模、追求训练效率的工业场景。</li>
</ul>
<div class="blog_h2"><span class="graybg">概率模型</span></div>
<p>这一类方法处理的核心问题是：模型不仅要给出“预测是什么”，还要明确回答“这个结果有多可信、数据是如何生成的、隐藏结构是什么”。因此它们直接建模概率分布（Probability Distribution）、隐变量（Latent Variables）或序列依赖关系，在分类、密度估计、软聚类、序列推断与不确定性表达中具有统一优势。</p>
<div class="blog_h3"><span class="graybg">朴素贝叶斯</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>朴素贝叶斯（Naive Bayes）用于分类问题。给定特征 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span>，它要估计样本属于每个类别 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 的后验概率 <span displaypfx="inline-" class="mathjax-container">\(p(y|\boldsymbol{x})\)</span>。该方法尤其适合高维稀疏、小样本或需要快速概率基线的任务。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>朴素贝叶斯的核心假设是：在给定类别 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 的条件下，各个特征条件独立（Conditional Independence）。这个假设通常并不严格成立，但它把高维联合分布的估计问题，转化为多个一维条件分布的估计问题。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>由贝叶斯公式：</p>
<span displaypfx="" class="mathjax-container">\[p(y|\boldsymbol{x})=\frac{p(\boldsymbol{x}|y)p(y)}{p(\boldsymbol{x})}\]</span>
<p>这个贝叶斯公式把后验概率拆成三部分： <span displaypfx="inline-" class="mathjax-container">\(p(y|\boldsymbol{x})\)</span> 是看到特征后属于类别 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 的概率； <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{x}|y)\)</span> 是该类别生成这组特征的可能性； <span displaypfx="inline-" class="mathjax-container">\(p(y)\)</span> 是类别先验； <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{x})\)</span> 则是对所有类别做归一化的总证据。</p>
<p>由于 <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{x})\)</span> 对所有类别相同，分类时只需比较：</p>
<span displaypfx="" class="mathjax-container">\[p(y|\boldsymbol{x})\propto p(y)\prod_{j=1}^{d}p(x_j|y)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\propto\)</span> 表示“成正比”。因为对同一个样本来说，分母 <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{x})\)</span> 对所有类别都相同，所以比较类别大小时只需看右边：先验 <span displaypfx="inline-" class="mathjax-container">\(p(y)\)</span> 乘上每个特征条件概率 <span displaypfx="inline-" class="mathjax-container">\(p(x_j|y)\)</span> 的连乘积。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(p(y)\)</span> 是先验概率（Prior）， <span displaypfx="inline-" class="mathjax-container">\(p(x_j|y)\)</span> 是条件似然（Likelihood）。根据特征类型不同，可以得到高斯朴素贝叶斯、伯努利朴素贝叶斯、多项式朴素贝叶斯等变体。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>从训练集统计每个类别的先验概率 <span displaypfx="inline-" class="mathjax-container">\(p(y)\)</span>。</li>
<li>估计每个类别下各特征的条件分布 <span displaypfx="inline-" class="mathjax-container">\(p(x_j|y)\)</span>。</li>
<li>推断时计算各类别的对数后验分数 <span displaypfx="inline-" class="mathjax-container">\(\log p(y)+\sum_j \log p(x_j|y)\)</span>。</li>
<li>选择分数最大的类别作为预测输出。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在垃圾邮件检测中，若某些词在垃圾邮件中显著更常见，那么这些词对应的条件概率会被估得更大，从而把包含这些词的邮件判到垃圾类别。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：训练快、推断快、对高维稀疏特征友好。</li>
<li>局限：条件独立假设常被破坏；概率校准有时较弱。</li>
<li>适用场景：文本分类、垃圾邮件过滤、简单可靠的概率基线。</li>
</ul>
<div class="blog_h3"><span class="graybg">高斯混合模型（GMM）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>高斯混合模型（Gaussian Mixture Model, GMM）处理的是“数据由多个潜在高斯成分混合生成”的建模问题。与 K-Means 只输出硬划分不同，GMM 希望估计每个样本属于各个簇的概率，并允许不同簇有不同的协方差结构。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>GMM 引入潜变量 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 表示样本来自哪一个高斯成分。整体分布由多个高斯分布按混合系数加权得到，因此每个样本对各个簇的归属是软的（Soft Assignment），并以概率形式表达。</p>
<p>一个直观比喻是：把数据想成混在一起的几类人群，只能看到每个人的外在特征，却看不到他原本属于哪一类。每一类人群都有自己的“中心位置”和“分散形状”，对应一个高斯成分；整个数据集则像这些人群按不同比例叠在一起形成的总体分布。与 K-Means 把每个样本硬塞进某一个簇不同，GMM 会给出“这个样本更像第 1 类，也有一部分像第 2 类”这样的软归属结果。</p>
<p>因此，混合系数 <span displaypfx="inline-" class="mathjax-container">\(\pi_k\)</span> 可以理解为各类人群在总体中的占比，均值 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\mu}_k\)</span> 是每类人群的中心，协方差 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\Sigma}_k\)</span> 描述该群体沿不同方向的扩散方式，而责任度 <span displaypfx="inline-" class="mathjax-container">\(\gamma_{ik}\)</span> 则表示“样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 有多大程度属于第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 类”。这也是 GMM 比 K-Means 更灵活的原因：它允许边界模糊，也允许簇具有不同大小、方向和椭球形状。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>对样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span>，GMM 的概率密度写为：</p>
<span displaypfx="" class="mathjax-container">\[p(\boldsymbol{x})=\sum_{k=1}^{K} \pi_k \mathcal{N}(\boldsymbol{x}\mid \boldsymbol{\mu}_k,\boldsymbol{\Sigma}_k)\]</span>
<p>这条式子说明 GMM 的整体密度不是由单个高斯给出，而是 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个高斯成分的加权和。 <span displaypfx="inline-" class="mathjax-container">\(\pi_k\)</span> 决定第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个成分在总体中的占比， <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}(\boldsymbol{x}\mid \boldsymbol{\mu}_k,\boldsymbol{\Sigma}_k)\)</span> 描述样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 在该成分下有多典型。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\pi_k\)</span> 是混合系数，满足 <span displaypfx="inline-" class="mathjax-container">\(\pi_k\ge 0\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(\sum_k \pi_k=1\)</span>。给定样本后，其属于第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个成分的责任度（Responsibility）为：</p>
<span displaypfx="" class="mathjax-container">\[\gamma_{ik}=p(z_i=k|\boldsymbol{x}_i)=\frac{\pi_k \mathcal{N}(\boldsymbol{x}_i\mid \boldsymbol{\mu}_k,\boldsymbol{\Sigma}_k)}{\sum_{j=1}^{K}\pi_j \mathcal{N}(\boldsymbol{x}_i\mid \boldsymbol{\mu}_j,\boldsymbol{\Sigma}_j)}\]</span>
<p>责任度 <span displaypfx="inline-" class="mathjax-container">\(\gamma_{ik}\)</span> 是“样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 属于成分 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 的后验概率”。分子表示“成分 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 解释这个样本的能力”，分母则把所有成分对该样本的解释能力加总起来做归一化，因此所有 <span displaypfx="inline-" class="mathjax-container">\(\gamma_{ik}\)</span> 加起来等于 1。</p>
<p>GMM 的参数通常通过期望最大化（Expectation-Maximization, EM）求解。E 步计算责任度，M 步更新参数：</p>
<span displaypfx="" class="mathjax-container">\[N_k=\sum_{i=1}^{N}\gamma_{ik},\qquad \pi_k=\frac{N_k}{N}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(N_k\)</span> 不是成分 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 的硬计数，而是软计数：每个样本只按自己的责任度贡献一部分。于是混合系数更新为 <span displaypfx="inline-" class="mathjax-container">\(\pi_k=\frac{N_k}{N}\)</span>，表示第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个成分在总体中的相对权重。</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{\mu}_k=\frac{1}{N_k}\sum_{i=1}^{N}\gamma_{ik}\boldsymbol{x}_i,\qquad \boldsymbol{\Sigma}_k=\frac{1}{N_k}\sum_{i=1}^{N}\gamma_{ik}(\boldsymbol{x}_i-\boldsymbol{\mu}_k)(\boldsymbol{x}_i-\boldsymbol{\mu}_k)^\top\]</span>
<p>均值更新式说明：每个样本按责任度大小对中心 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\mu}_k\)</span> 做加权贡献；协方差更新式则是在该加权中心周围统计离散程度。责任度越大，样本对该成分的中心和形状影响越大。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>初始化混合系数、均值和协方差。</li>
<li>E 步：计算每个样本对各高斯成分的责任度。</li>
<li>M 步：根据责任度更新参数。</li>
<li>重复 E / M，直到对数似然收敛。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在用户分群中，若人群天然分成多个椭球状子群体，那么 GMM 不仅能给出簇划分，还能输出“某个用户属于每个群体的概率”，这比 K-Means 的硬分配更细致。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：软聚类、概率解释清晰、能建模不同协方差形状。</li>
<li>局限：对初始化敏感；高维时协方差估计代价高。</li>
<li>适用场景：软聚类、密度估计、带概率解释的聚类分析。</li>
</ul>
<div class="blog_h3"><span class="graybg">隐马尔可夫模型（HMM）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>隐马尔可夫模型（Hidden Markov Model, HMM）用于序列建模，尤其适合处理序列标注（Sequence Labeling）问题。序列标注的任务形式是：给定一个按时间或位置排列的输入序列，为序列中的每个位置分配一个标签。例如，在词性标注中，句子“我爱北京天安门”的每个词或字都需要对应一个词性标签；在语音识别中，一段连续声学信号需要对应到一串离散文字或音素标签。</p>
<p>HMM 是序列模型中的经典早期方法，在语音识别等领域长期具有重要地位。它的优势在于结构清晰、推断高效，能够以较低的计算成本处理序列决策；与此同时，它主要依赖局部状态转移和局部发射关系，所使用的上下文信息相对有限。</p>
<p>HMM 的建模方式是：观测序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}_{1:T}\)</span> 可以看到，但生成这些观测的状态序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{z}_{1:T}\)</span> 看不到。这里的隐藏状态通常对应词性、命名实体类别或发音状态等潜在标签。围绕这一模型，核心问题通常包括计算某段观测序列的概率、恢复最可能的隐藏状态路径，以及估计模型参数。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>HMM 假设隐藏状态满足一阶马尔可夫性（First-order Markov Property）：当前状态只依赖前一个状态；同时每个观测只依赖当前状态。一个直接的比喻是：无法直接看到远方朋友每天所处的天气，但可以持续看到他发布的活动，例如“去游泳”“去逛街”或“在家睡觉”。在这个比喻里，天气是隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(z_t\)</span>，活动是观测 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span>。HMM 的生成逻辑正是<span style="background-color: #c0c0c0;">先生成隐藏状态序列，再由每个状态发射对应观测</span>。</p>
<p>如果今天是晴天，明天仍然晴天的概率通常较高；如果今天下雨，朋友在家休息的概率通常高于去游泳。这分别对应 HMM 中的状态转移概率 <span displaypfx="inline-" class="mathjax-container">\(p(z_t|z_{t-1})\)</span> 和发射概率 <span displaypfx="inline-" class="mathjax-container">\(p(x_t|z_t)\)</span>。因此，HMM 把序列建模为一个“剧本模拟器”：先按转移规律生成一条看不见的天气轨迹，再按照每一天的天气生成看得见的活动记录。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">HMM 概念</td>
<td style="text-align: center;">比喻中的对应物</td>
<td style="text-align: center;">含义</td>
</tr>
</thead>
<tbody>
<tr>
<td>隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(z_t\)</span></td>
<td>每天的天气</td>
<td>真实存在，但观察者不能直接看到。</td>
</tr>
<tr>
<td>观测 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span></td>
<td>朋友圈里的活动</td>
<td>可以直接看到，用来反推隐藏状态。</td>
</tr>
<tr>
<td>初始分布 <span displaypfx="inline-" class="mathjax-container">\(p(z_1)\)</span></td>
<td>第一天各种天气出现的概率</td>
<td>序列从什么状态开始。</td>
</tr>
<tr>
<td>转移概率 <span displaypfx="inline-" class="mathjax-container">\(p(z_t|z_{t-1})\)</span></td>
<td>天气从今天到明天的变化规律</td>
<td>例如“晴天后仍是晴天”的概率较高。</td>
</tr>
<tr>
<td>发射概率 <span displaypfx="inline-" class="mathjax-container">\(p(x_t|z_t)\)</span></td>
<td>某种天气下出现某种活动的概率</td>
<td>例如下雨时更可能“在家睡觉”。</td>
</tr>
<tr>
<td>隐藏状态序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{z}_{1:T}\)</span></td>
<td>整段天气变化轨迹</td>
<td>模型希望恢复的潜在过程。</td>
</tr>
<tr>
<td>观测序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}_{1:T}\)</span></td>
<td>整段活动记录</td>
<td>模型的输入数据。</td>
</tr>
</tbody>
</table>
<p>这个比喻也解释了 HMM 的优势与局限。它简单、快速、可解释，因为联合概率能够拆成局部转移和局部发射的乘积，并可用动态规划高效推断。但它的条件独立假设也很强：某一天的活动只由当天的天气决定，不直接依赖前后活动。在词性标注等任务中，一个词的标签往往同时受左右上下文影响，这种假设就会限制表达能力。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>HMM 的联合分布写为：</p>
<span displaypfx="" class="mathjax-container">\[p(\boldsymbol{x}_{1:T},\boldsymbol{z}_{1:T})=p(z_1)\prod_{t=2}^{T}p(z_t|z_{t-1})\prod_{t=1}^{T}p(x_t|z_t)\]</span>
<p>这条联合分布把一整条序列拆成三部分： <span displaypfx="inline-" class="mathjax-container">\(p(z_1)\)</span> 是初始状态概率， <span displaypfx="inline-" class="mathjax-container">\(\prod_{t=2}^{T}p(z_t|z_{t-1})\)</span> 是状态转移链， <span displaypfx="inline-" class="mathjax-container">\(\prod_{t=1}^{T}p(x_t|z_t)\)</span> 是每个状态发射观测的概率。正因为有这种乘积分解，HMM 才能用动态规划高效求解。</p>
<p>若记初始分布为 <span displaypfx="inline-" class="mathjax-container">\(\pi\)</span>，转移矩阵为 <span displaypfx="inline-" class="mathjax-container">\(A\)</span>，发射分布为 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>，则前向算法（Forward Algorithm）的核心量为：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_t(j)=p(x_{1:t},z_t=j)\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(\alpha_t(j)\)</span> 的含义是：看到前 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个观测，同时当前时刻状态恰好是第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个状态的联合概率。它把“历史观测信息”和“当前落在哪个状态”压缩成一个可递推的中间量。</p>
<p>其递推为：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_t(j)=\left[\sum_i \alpha_{t-1}(i)A_{ij}\right]B_j(x_t)\]</span>
<p>递推式可以分成两步看：先对所有上一步状态 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 求和，用 <span displaypfx="inline-" class="mathjax-container">\(A_{ij}\)</span> 累积“从 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 转到 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的可能性”；再乘上 <span displaypfx="inline-" class="mathjax-container">\(B_j(x_t)\)</span>，表示当前状态 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 发射出观测 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 的概率。</p>
<p>最可能状态路径可由维特比算法（Viterbi Algorithm）求解；参数估计通常采用 Baum-Welch 算法，即 HMM 上的 EM。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>定义状态空间、转移概率和发射概率。</li>
<li>若参数已知，用前向 / 后向算法做概率计算，用维特比算法做解码。</li>
<li>若参数未知，用 Baum-Welch 在训练集上迭代估计参数。</li>
<li>根据任务输出状态路径、序列概率或边缘状态分布。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在词性标注中，观测是单词序列，隐藏状态是词性标签。HMM 会同时利用“某个词在某种词性下更常见”的发射规律和“词性之间如何转移”的序列规律完成整体解码。</p>
<p>例如，对序列“我 / 爱 / 北京 / 天安门”，模型看到的是词本身，看不到的是背后的标签序列。HMM 会比较多种候选路径，如“代词（Pronoun）→ 动词（Verb）→ 专有名词（Proper Noun）→ 专有名词（Proper Noun）”与其他不合理组合的概率。由于“我”更容易由代词状态发射，“爱”更容易由动词状态发射，而“代词后接动词、动词后接名词性成分”又符合常见转移规律，这条标签路径就会获得更高概率。这个过程可以理解为：模型一边根据词本身猜标签，一边检查整条词性路径是否顺畅。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/hmm.jpg"><img class="alignnone size-full wp-image-41251" src="https://blog.gmem.cc/wp-content/uploads/2026/03/hmm.jpg" alt="hmm" width="1408" height="768" /></a></p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：序列结构清晰，动态规划推断高效，可解释性强。</li>
<li>局限：一阶马尔可夫假设较强，表达能力有限。</li>
<li>适用场景：基础序列标注、时间状态切换建模、中小规模序列任务。</li>
</ul>
<div class="blog_h3"><span class="graybg">条件随机场（CRF）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>条件随机场（Conditional Random Field, CRF）主要用于结构化预测（Structured Prediction），尤其是序列标注。它处理的问题是：在给定输入序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的条件下，如何联合预测输出标签序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}\)</span>，并显式建模标签之间的依赖关系。</p>
<p>CRF 可以看作对 HMM 的进一步发展：它不再试图解释输入序列如何被生成，而是直接在给定输入的条件下预测最合理的标签结构。通过引入全局特征和更灵活的上下文依赖，CRF 缓解了 HMM 仅依赖局部独立假设所带来的信息不足。在深度学习广泛进入 NLP 之前，CRF 长期是各类标注任务中的核心方法。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>CRF 的核心是在给定观测序列的条件下，对整条标签序列进行全局评分，而不是显式描述数据生成过程。仍以天气和活动的比喻来理解：已知一整周的活动记录后，CRF 会把各种可能的天气序列都拿来比较，判断哪一种与这组活动整体最一致。它建模的是条件分布 <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{y}|\boldsymbol{x})\)</span>，而不是 HMM 那样的联合分布 <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{x},\boldsymbol{y})\)</span>。</p>
<p>这种“全局把控”体现在两个层面。第一，CRF 的打分对象是完整的标签路径，而不是每个位置互相独立的局部决策；第二，打分依据可以是灵活定义的特征函数（Feature Function）。例如，若连续三天都出现“游泳”，对应“连续晴天”的标签组合就应得到更高分；若一周中出现“滑雪”这类活动，与“夏天”一致的天气标签组合就应被显著压低。特征函数可以同时利用当前位置、前后邻域以及相邻标签之间的组合关系。</p>
<p>因此，CRF 可以视为一个<span style="background-color: #c0c0c0;">面向整条序列的打分系统</span>：输入固定，模型比较不同标签序列的相对合理性，并选择得分最高的一条。它的优势是能够充分利用复杂上下文和标签依赖，在序列标注任务中通常比 HMM 更准确；代价是训练和推断都更重，需要计算整条序列上的归一化与动态规划。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>线性链 CRF 的条件分布通常写为：</p>
<span displaypfx="" class="mathjax-container">\[p(\boldsymbol{y}|\boldsymbol{x})=\frac{1}{Z(\boldsymbol{x})}\exp\left(\sum_{t=1}^{T}\sum_k \lambda_k f_k(y_{t-1},y_t,\boldsymbol{x},t)\right)\]</span>
<p>这个式子最好分三层看。先看最里面的 <span displaypfx="inline-" class="mathjax-container">\(\sum_{t=1}^{T}\sum_k \lambda_k f_k(y_{t-1},y_t,\boldsymbol{x},t)\)</span>：它表示对整条标签序列逐位置累积特征得分。再看外面的指数 <span displaypfx="inline-" class="mathjax-container">\(\exp(\cdot)\)</span>：它把“总得分”变成一个始终为正的数，而且总得分越大，这个数就越大。最后再除以 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span>，把这些正数正规化成概率。因此，CRF 的计算顺序可以理解为：<span style="background-color: #c0c0c0;">先打分，再变成正权重，最后归一化成概率</span>。</p>
<p>为了看清楚 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span> 在做什么，可以先临时把分子记成一个“未归一化分数”：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{p}(\boldsymbol{y}|\boldsymbol{x})=\exp\left(\sum_{t=1}^{T}\sum_k \lambda_k f_k(y_{t-1},y_t,\boldsymbol{x},t)\right)\]</span>
<p>这里特意加波浪号，是为了强调它<span style="background-color: #c0c0c0;">还不是概率</span>。原因很简单：把所有可能标签序列的 <span displaypfx="inline-" class="mathjax-container">\(\tilde{p}(\boldsymbol{y}|\boldsymbol{x})\)</span> 加起来，结果一般不会恰好等于 1。它只是每条路径的相对权重，表达“这条标签路径有多合理”。</p>
<p>这时配分函数（Partition Function）就出现了：</p>
<span displaypfx="" class="mathjax-container">\[Z(\boldsymbol{x})=\sum_{\boldsymbol{y}} \exp\left(\sum_{t=1}^{T}\sum_k \lambda_k f_k(y_{t-1},y_t,\boldsymbol{x},t)\right)\]</span>
<p>之所以你会觉得它“和上面那个公式一样”，是因为它确实就是<span style="background-color: #c0c0c0;">把上式分子对所有可能的标签序列整体求和</span>。上面的 <span displaypfx="inline-" class="mathjax-container">\(p(\boldsymbol{y}|\boldsymbol{x})\)</span> 针对的是某一条固定标签序列 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}\)</span>；而这里的 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span> 不是看某一条路径，而是把所有可能路径都算一遍，再全部加起来。所以它不是“另一条几乎一样的公式”，而是“分子在全标签空间上的总和”。</p>
<p>于是条件概率就变成：</p>
<span displaypfx="" class="mathjax-container">\[p(\boldsymbol{y}|\boldsymbol{x})=\frac{\tilde{p}(\boldsymbol{y}|\boldsymbol{x})}{Z(\boldsymbol{x})}\]</span>
<p>现在这个式子就容易理解了：某条路径的概率，等于“这条路径自己的权重”除以“所有路径权重的总和”。这和 softmax 的归一化逻辑完全一致，只不过 softmax 是在有限个类别上归一化，而 CRF 是在所有可能的标签序列上归一化。</p>
<p>配分函数这个名字来自统计物理，但在这里不需要物理背景也能理解：它本质上就是一个<span style="background-color: #c0c0c0;">归一化常数</span>。没有它，模型只能说“路径 A 比路径 B 更合理”；有了它，模型才能进一步说“路径 A 的概率是多少”。也正因为 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span> 需要把所有可能标签路径都考虑进去，CRF 训练时才必须借助动态规划，而不能暴力枚举。</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(f_k(y_{t-1},y_t,\boldsymbol{x},t)\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个特征函数，描述当前位置、相邻标签和输入之间的某种局部模式。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\lambda_k\)</span>：该特征的权重；越大表示模型越重视这个模式。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span>：固定输入序列；在讨论 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span> 时，它不变。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}\)</span>：某一条候选标签序列；计算 <span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span> 时要对所有可能的 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}\)</span> 求和。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sum_{t=1}^{T}\sum_k \lambda_k f_k(\cdot)\)</span>：整条标签序列的总得分。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\exp(\cdot)\)</span>：把总得分映射成正权重，并放大高分路径与低分路径之间的差异。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(Z(\boldsymbol{x})\)</span>：所有候选标签路径未归一化权重的总和，用于把相对权重变成概率。</li>
</ul>
<p>训练时最大化条件对数似然，解码时用维特比算法寻找最优标签序列。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>定义特征函数，描述输入与标签、标签与标签之间的关系。</li>
<li>用前向后向算法计算配分函数与梯度。</li>
<li>通过梯度法优化参数 <span displaypfx="inline-" class="mathjax-container">\(\lambda_k\)</span>。</li>
<li>推断时用维特比算法输出最优标签序列。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在命名实体识别（NER）中，局部分类器可能会把某个词单独判成人名，但 CRF 会进一步考虑相邻标签是否合法，从而减少孤立的局部误判。这里“是否合法”指的不是语法合法，而是<span style="background-color: #c0c0c0;">标签序列是否符合该任务定义下允许出现的邻接模式</span>。例如，在常见的 BIO 标注体系中， <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 表示 Begin，即实体开始； <span displaypfx="inline-" class="mathjax-container">\(I\)</span> 表示 Inside，即实体内部； <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 表示 Outside，即不属于任何实体。也有一些任务使用 BIOES 标注体系，其中 <span displaypfx="inline-" class="mathjax-container">\(E\)</span> 表示 End， <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 表示 Single。与 BIO 相比，BIOES 会把实体结束位置和单字实体显式标出来，因此边界信息更细。于是，标签 <span displaypfx="inline-" class="mathjax-container">\(B\text{-}PER\)</span> 表示一个人名实体的开始， <span displaypfx="inline-" class="mathjax-container">\(I\text{-}PER\)</span> 表示该人名实体的内部， <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 表示不属于任何实体。于是， <span displaypfx="inline-" class="mathjax-container">\(B\text{-}PER\rightarrow I\text{-}PER\)</span> 是常见且合法的相邻转移， <span displaypfx="inline-" class="mathjax-container">\(O\rightarrow B\text{-}PER\)</span> 也合法；但 <span displaypfx="inline-" class="mathjax-container">\(O\rightarrow I\text{-}PER\)</span> 通常不合法，因为一个实体内部标签不能无缘无故直接开始，前面必须先有对应的开始标签。同样， <span displaypfx="inline-" class="mathjax-container">\(B\text{-}LOC\rightarrow I\text{-}PER\)</span> 这类“实体类型突然不一致”的连接也通常应被压低分数。</p>
<p>例如，在句子“张三 在 北京 工作”中，若任务要识别人名（PER）和地点（LOC），一个合理的 BIO 标注序列可能是“张三 / 在 / 北京 / 工作”对应 <span displaypfx="inline-" class="mathjax-container">\(B\text{-}PER, O, B\text{-}LOC, O\)</span>。这里首先需要区分两件事：<span style="background-color: #c0c0c0;">某个位置更像哪一种实体类型</span>，以及<span style="background-color: #c0c0c0;">这些局部判断连起来是否构成一条合理的标签序列</span>。前者主要来自输入本身提供的证据，例如“张三”在词形上很像中文人名，“北京”本身强烈像地点名词，而“在 北京 工作”这种上下文也会继续加强“北京是地点”的判断。传统 CRF 会把这些信息写成特征函数，例如“当前词是否常见于人名词表”“当前词是否带有地名后缀”“左邻词是否是介词‘在’”“右邻词是否是动作词‘工作’”等；每个特征都会给某个候选标签加分或减分。</p>
<p>CRF 的作用是在这些局部类型证据之上，再做一次全局一致性的联合解码。换言之，实体类型 A 还是 B，并不是和 CRF 完全无关；但也不是由 CRF 凭空决定的。更准确地说，<span style="background-color: #c0c0c0;">实体类型的语义判断主要来自输入特征，CRF 负责把这些局部判断放到整条序列里统一协调</span>。例如，如果“北京”这个位置单看局部证据时，对 LOC 的分数高于 PER，那么 CRF 会倾向保留“地点”这一判断；但它还会进一步检查，当前位置前后的标签连接是否自然。如果某条候选路径把“张三”切成 <span displaypfx="inline-" class="mathjax-container">\(B\text{-}PER, O\)</span>，或者把“北京”接成 <span displaypfx="inline-" class="mathjax-container">\(B\text{-}PER\rightarrow I\text{-}LOC\)</span>，即使某个局部位置的分数不低，整条路径仍会因为边界断裂或类型转移不一致而被整体压低。于是 CRF 做的不是单点分类，而是“局部类型打分 + 全局路径约束”的联合决策。</p>
<p>从工程实现上看，这个分工在不同年代的模型里表现形式不同。在传统 CRF 中，“局部类型打分”通常来自人工设计的特征模板，例如当前词、前后词、词性、字形、是否出现在人名词典或地名词典中；CRF 再把这些手工特征组合成整条序列的全局分数。在 BiLSTM-CRF 或 BERT-CRF 这类现代模型中，局部证据不再主要依赖手工模板，而是先由 BiLSTM 或 BERT 生成上下文化表示（Contextual Representation），再由线性层给出每个位置对各标签的局部分数，最后仍由 CRF 层负责建模标签转移和整条路径解码。也就是说，上游编码器主要回答“这个位置像什么类型”，CRF 层主要回答“这些位置判断拼在一起是否构成一条最合理的标签序列”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/crf-1.jpg"><img class="alignnone size-full wp-image-41263" src="https://blog.gmem.cc/wp-content/uploads/2026/03/crf-1.jpg" alt="crf-1" width="1408" height="768" /></a></p>
<p>类似的全局打分思想也可以推广到依存句法分析（Dependency Parsing, DEP）这类结构化任务。句子中的每个词都需要找到自己的中心词（head），模型的目标不是孤立地决定“这个词连向谁”，而是评估整棵依存树是否合理。例如，在“她 喜欢 自然语言处理”中，“喜欢”通常更可能作为中心谓词，“她”依附到“喜欢”形成主谓关系，“自然语言处理”整体依附到“喜欢”形成宾语关系。若某个局部决策把“她”错误地连到“自然语言处理”，单看两个词的局部相似度未必很低，但放到整棵树的全局结构中就会显得不协调。CRF 的价值正体现在这里：它通过全局归一化和结构约束，偏好整体验证一致的输出结构，而不是一组彼此冲突的局部最优决策。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/crf-2.jpg"><img class="alignnone size-full wp-image-41257" src="https://blog.gmem.cc/wp-content/uploads/2026/03/crf-2.jpg" alt="crf-2" width="1408" height="768" /></a></p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：适合结构化输出，能显式编码标签依赖。</li>
<li>局限：训练与推断成本高于普通独立分类器。</li>
<li>适用场景：序列标注、分词、命名实体识别等条件结构化预测任务。</li>
</ul>
<div class="blog_h2"><span class="graybg">近邻模型</span></div>
<p>这一类方法处理的核心问题是：在缺少可靠全局函数形式时，是否可以直接依赖“局部相似样本通常有相似输出”这一假设完成预测。近邻模型不急于学习一个显式参数化函数，而是把相似性度量（Similarity Metric）本身作为建模中心：先找邻居，再由邻居投票、平均或加权得到结果。</p>
<div class="blog_h3"><span class="graybg">K 近邻（KNN）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>K 近邻（K-Nearest Neighbors, KNN）用于分类与回归。给定一个待预测样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span>，它要根据训练集中与其最相似的样本来决定输出。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>KNN 的基本假设是局部平滑性（Local Smoothness）：在特征空间中彼此接近的样本，往往具有相近的标签或数值。它不显式学习参数化模型，训练集本身就是局部比较的参照集。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>给定距离函数 <span displaypfx="inline-" class="mathjax-container">\(d(\boldsymbol{x},\boldsymbol{x}')\)</span> 与邻居数 <span displaypfx="inline-" class="mathjax-container">\(K\)</span>，若 <span displaypfx="inline-" class="mathjax-container">\(N_K(\boldsymbol{x})\)</span> 表示 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个最近邻，则分类时可写为：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=\mathrm{mode}\left(\{y_i:(\boldsymbol{x}_i,y_i)\in N_K(\boldsymbol{x})\}\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(N_K(\boldsymbol{x})\)</span> 是样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个最近邻集合，花括号中收集的是这些邻居的标签， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{mode}(\cdot)\)</span> 则返回出现次数最多的类别。也就是说，KNN 分类本质上就是“看最近的邻居们大多数是谁”。</p>
<p>回归时常取邻居标签平均：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=\frac{1}{K}\sum_{(\boldsymbol{x}_i,y_i)\in N_K(\boldsymbol{x})}y_i\]</span>
<p>回归版 KNN 只是把“多数投票”换成“数值平均”。 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{K}\sum\)</span> 表示把最近 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个邻居的输出做均值，因此预测值会受到局部邻域中所有数值样本的共同影响。</p>
<p>若距离使用欧氏距离，则默认所有特征尺度可比，因此标准化（Standardization）通常是必要前处理。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>训练阶段几乎不做参数学习，只保存全部训练样本。</li>
<li>推断时计算待测样本到训练样本的距离。</li>
<li>选出最近的 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个样本。</li>
<li>分类取多数投票，回归取平均或距离加权平均。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在简单的手写数字识别中，如果一张新图片与训练集中大量“3”的图像都很接近，而与“8”的图像明显更远，那么 KNN 会依据局部邻域投票把它判为 3。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：概念简单、无需复杂训练、局部非线性表达能力强。</li>
<li>局限：推断成本高；对特征尺度和无关特征敏感；维度灾难会削弱距离判别力。</li>
<li>适用场景：中小规模数据、快速基线、基于相似度的简单分类与回归。</li>
</ul>
<div class="blog_h2"><span class="graybg">聚类</span></div>
<p>聚类处理的核心问题是：在没有标签的前提下，如何仅根据样本之间的几何关系、密度结构或层次关系，把数据自动分成若干组。这里不存在唯一正确的“簇”定义：K-Means 假设簇围绕中心分布，层次聚类强调多粒度组织结构，DBSCAN / HDBSCAN 则把簇理解为高密度连通区域。因此，聚类算法的选择本质上是在选择“什么样的结构应被视为同一类”。</p>
<div class="blog_h3"><span class="graybg">K-Means</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>K-Means 处理的是无监督聚类问题：给定一组没有标签的样本，希望把它们分成 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个簇，使同一簇内样本尽量接近，不同簇之间尽量分开。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>K-Means 用“每个簇由一个中心点代表”的方式近似数据分布。算法不断重复两件事：把样本分配给最近的中心；再用簇内样本均值更新中心。它本质上是在最小化簇内平方误差。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/k-means.jpg"><img class="alignnone size-full wp-image-41287" src="https://blog.gmem.cc/wp-content/uploads/2026/03/k-means.jpg" alt="k-means" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>目标函数为：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\{c_k\},\{z_i\}} \sum_{i=1}^{N} \|\boldsymbol{x}_i-c_{z_i}\|_2^2\]</span>
<p>这个目标里， <span displaypfx="inline-" class="mathjax-container">\(c_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个簇中心， <span displaypfx="inline-" class="mathjax-container">\(z_i\)</span> 表示样本 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 被分到哪个簇， <span displaypfx="inline-" class="mathjax-container">\(\|\boldsymbol{x}_i-c_{z_i}\|_2^2\)</span> 是样本到所属簇中心的平方距离。K-Means 想做的，就是让所有样本离各自中心都尽可能近。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(c_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个簇中心， <span displaypfx="inline-" class="mathjax-container">\(z_i\in\{1,\dots,K\}\)</span> 表示样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}_i\)</span> 属于哪个簇。固定簇分配时，最优中心是该簇样本均值：</p>
<span displaypfx="" class="mathjax-container">\[c_k=\frac{1}{|S_k|}\sum_{\boldsymbol{x}_i\in S_k} \boldsymbol{x}_i\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(S_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个簇里所有样本的集合， <span displaypfx="inline-" class="mathjax-container">\(|S_k|\)</span> 是该簇样本数。这个更新式说明簇中心就是簇内样本的算术平均，因此 K-Means 的“中心”确实是均值意义上的代表点。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>初始化 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个簇中心。</li>
<li>分配步骤：把每个样本分到最近中心。</li>
<li>更新步骤：用各簇样本均值更新中心。</li>
<li>重复迭代直到簇分配稳定或目标下降很小。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在用户分群中，若特征是消费频次与客单价，K-Means 往往会自动形成“高频低客单”“低频高客单”“中频中客单”等若干均值中心明确的群体。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：实现简单、可扩展、训练速度快。</li>
<li>局限：需要预先指定 <span displaypfx="inline-" class="mathjax-container">\(K\)</span>；对初始化与离群点敏感；不适合非凸簇。</li>
<li>适用场景：簇近似球形、需要快速聚类或作为预处理的任务。</li>
</ul>
<div class="blog_h3"><span class="graybg">层次聚类</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>层次聚类（Hierarchical Clustering）输出的是一个由粗到细的聚类层次结构，因此簇数可以在观察树状图后再决定。它适合需要观察“簇是如何逐步合并或拆分”的任务。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>凝聚式层次聚类（Agglomerative Clustering）从每个样本单独成簇开始，每一步合并当前最相近的两个簇；分裂式层次聚类则从一个大簇开始不断拆分。实践中更常见的是凝聚式版本。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/h-clustering.jpg"><img class="alignnone size-full wp-image-41293" src="https://blog.gmem.cc/wp-content/uploads/2026/03/h-clustering.jpg" alt="h-clustering" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>关键在于定义簇间距离。常见联接准则（Linkage Criteria）包括：</p>
<span displaypfx="" class="mathjax-container">\[d_{\text{single}}(A,B)=\min_{\boldsymbol{x}\in A,\boldsymbol{y}\in B} d(\boldsymbol{x},\boldsymbol{y})\]</span>
<p>单链距离只看两簇之间最近的那一对点，因此它很容易把“靠得最近的局部桥梁”连起来，适合发现链式结构，但也更容易被噪声点串联。</p>
<span displaypfx="" class="mathjax-container">\[d_{\text{complete}}(A,B)=\max_{\boldsymbol{x}\in A,\boldsymbol{y}\in B} d(\boldsymbol{x},\boldsymbol{y})\]</span>
<p>完全链距离只看两簇之间最远的那一对点，因此它会避免把跨度过大的簇合并到一起，更偏好紧凑、直径较小的簇。</p>
<span displaypfx="" class="mathjax-container">\[d_{\text{average}}(A,B)=\frac{1}{|A||B|}\sum_{\boldsymbol{x}\in A,\boldsymbol{y}\in B} d(\boldsymbol{x},\boldsymbol{y})\]</span>
<p>平均链则对两簇之间所有点对的距离取平均。这里 <span displaypfx="inline-" class="mathjax-container">\(|A||B|\)</span> 是点对总数，因此它不只看最近点或最远点，而是综合考虑两簇整体的平均接近程度。</p>
<p>不同联接方式对应不同簇形偏好：单链更容易形成链式簇，完全链更偏向紧凑簇，平均链则居中。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>初始化每个样本为单独一个簇。</li>
<li>计算簇间距离矩阵。</li>
<li>反复合并距离最近的两个簇，并更新距离矩阵。</li>
<li>得到树状图（Dendrogram）后，在某个高度切开即可获得聚类结果。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在文档聚类中，层次聚类不仅能区分“体育”“财经”“科技”等大类，还能进一步展示每一大类内部的细粒度层级关系。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：无需预先指定簇数；可以输出多层次聚类结构。</li>
<li>局限：计算与存储成本较高；早期合并错误通常无法回退。</li>
<li>适用场景：中小规模数据、需要树状关系解释的聚类分析。</li>
</ul>
<div class="blog_h3"><span class="graybg">DBSCAN</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>DBSCAN（Density-Based Spatial Clustering of Applications with Noise）用于识别任意形状的高密度簇，并显式发现噪声点。它尤其适合处理非球形簇与含离群点数据。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>DBSCAN 通过局部密度定义簇。若一个点周围半径 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 内有足够多的邻居，则它是核心点（Core Point）；核心点可以把周围密度可达（Density-Reachable）的样本不断扩展成一个簇。既不够密、又不属于任何核心点邻域的样本被视为噪声。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/dbscan.jpg"><img class="alignnone size-full wp-image-41305" src="https://blog.gmem.cc/wp-content/uploads/2026/03/dbscan.jpg" alt="dbscan" width="1024" height="559" /></a></p>
<p>&nbsp;</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>记 <span displaypfx="inline-" class="mathjax-container">\(N_{\epsilon}(\boldsymbol{x})\)</span> 为点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 邻域。若：</p>
<span displaypfx="" class="mathjax-container">\[|N_{\epsilon}(\boldsymbol{x})|\ge \text{minPts}\]</span>
<p>判断核心点只需看两件事： <span displaypfx="inline-" class="mathjax-container">\(N_{\epsilon}(\boldsymbol{x})\)</span> 是点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 在半径 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 内的邻域集合， <span displaypfx="inline-" class="mathjax-container">\(|N_{\epsilon}(\boldsymbol{x})|\)</span> 是邻域里有多少点。只要这个数量不小于 <span displaypfx="inline-" class="mathjax-container">\(\text{minPts}\)</span>，就说明该点周围密度足够高，可以作为簇扩张的核心。</p>
<p>则 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 是核心点。若某点落在某个核心点邻域中但自身并非核心点，则为边界点（Border Point）；不属于任何簇的点为噪声（Noise）。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>遍历未访问样本，计算其 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 邻域。</li>
<li>若邻居数少于 <span displaypfx="inline-" class="mathjax-container">\(\text{minPts}\)</span>，则暂记为噪声或边界候选。</li>
<li>若为核心点，则以它为起点递归扩展所有密度可达的点。</li>
<li>重复直到所有样本都被标记。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在地理位置聚类中，餐馆、商圈、交通枢纽附近的点位通常形成形状复杂的密集区域。DBSCAN 可以识别这些非凸热点，同时把零散孤立点保留为噪声。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：无需预设簇数；可识别任意形状簇；对离群点鲁棒。</li>
<li>局限：对 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\text{minPts}\)</span> 敏感；难以同时适配不同密度簇。</li>
<li>适用场景：空间聚类、热点区域发现、非凸簇与带噪声数据。</li>
</ul>
<div class="blog_h3"><span class="graybg">HDBSCAN</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>HDBSCAN（Hierarchical DBSCAN）用于缓解 DBSCAN 的单一密度阈值问题。它面对的核心困难是：真实数据中的簇密度常常并不一致，用一组固定的 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\text{minPts}\)</span> 很难同时兼顾所有簇。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>HDBSCAN 会在多个密度尺度上构建层次结构，再从中选出稳定簇（Stable Clusters）作为结果。这让它比 DBSCAN 更适合处理密度差异显著的数据。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/hdbscan-intuition.png"><img class="alignnone size-full wp-image-41327" src="https://blog.gmem.cc/wp-content/uploads/2026/03/hdbscan-intuition.png" alt="hdbscan-intuition" width="3045" height="12714" /></a></p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>给定参数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span>，先定义核心距离（Core Distance）为点到其第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 近邻的距离，再定义互可达距离（Mutual Reachability Distance）：</p>
<span displaypfx="" class="mathjax-container">\[d_{\text{mreach},k}(\boldsymbol{x},\boldsymbol{y})=\max\big(\text{core}_k(\boldsymbol{x}),\text{core}_k(\boldsymbol{y}),d(\boldsymbol{x},\boldsymbol{y})\big)\]</span>
<p>互可达距离把三个量取最大值：点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的核心距离、点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}\)</span> 的核心距离，以及它们之间的原始距离。这样做的效果是：低密度区域的点会被“拉远”，从而更清楚地暴露不同密度簇之间的结构边界。</p>
<p>随后算法在互可达图上构建最小生成树（Minimum Spanning Tree），再转成聚类层次，并依据簇稳定性选择最终输出。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>计算所有点的核心距离。</li>
<li>基于互可达距离构造图并求最小生成树。</li>
<li>从图中得到随密度变化的层次聚类结构。</li>
<li>在压缩树（Condensed Tree）上选择稳定簇作为结果。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在用户行为嵌入空间中，有些兴趣群体很紧密，有些较松散。HDBSCAN 可以同时保留这两类簇，而不需要强行用同一个密度阈值描述它们。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：对多密度簇更稳健，仍保留噪声识别能力。</li>
<li>局限：实现更复杂，计算与内存开销通常高于 DBSCAN。</li>
<li>适用场景：嵌入聚类、用户分群、不同密度簇共存的数据。</li>
</ul>
<div class="blog_h2"><span class="graybg">升维</span></div>
<p>升维（Feature Expansion / Lifting）处理的问题，与降维正好互补。降维试图把高维表示压缩到更低维空间，以减少冗余、噪声与计算量；升维则试图把原始特征映射到一个更高维的表示空间，使原本难以表达、难以分离或难以拟合的结构，在新空间里变得更容易处理。它的目标不是“把维度变大本身”，而是<span style="background-color: #c0c0c0;">通过增加表示自由度，把非线性关系改写为更容易由简单模型处理的形式</span>。</p>
<div class="blog_h3"><span class="graybg">背景和问题定义</span></div>
<p>许多经典机器学习模型本体是线性的，例如线性回归、逻辑回归、线性支持向量机（Support Vector Machine, SVM）。它们直接在原始输入空间中学习一个线性决策函数或线性预测函数。若数据关系本身高度非线性，那么模型能力可能不足。升维的思路因此是：先把输入映射为一个更高维的新表示，再在新表示上使用线性模型。模型形式仍然简单，但由于工作空间改变了，整体表达能力会显著提升。</p>
<p>更一般地，若原始输入为 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\in \mathbb{R}^d\)</span>，则升维映射可写成：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\boldsymbol{x}}=\phi(\boldsymbol{x}),\qquad \phi: \mathbb{R}^d\to \mathbb{R}^D,\qquad D&gt;d\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 是原始输入； <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}\)</span> 是升维后的新特征； <span displaypfx="inline-" class="mathjax-container">\(\phi(\boldsymbol{x})\)</span> 是特征映射； <span displaypfx="inline-" class="mathjax-container">\(D&gt;d\)</span> 表示新的表示空间维度高于原空间维度。若后续模型写成 <span displaypfx="inline-" class="mathjax-container">\(f(\boldsymbol{x})=\boldsymbol{w}^\top \tilde{\boldsymbol{x}}+b\)</span>，那么它虽然在新空间中仍然是线性的，但在原始输入 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 上通常已经对应一个非线性函数。</p>
<div class="blog_h3"><span class="graybg">核心思想</span></div>
<p>升维的直观含义，是把原来“纠缠在一起”的关系展开。例如在二维平面里，某些数据可能无法被一条直线分开；但若把 <span displaypfx="inline-" class="mathjax-container">\((x_1,x_2)\)</span> 映射成 <span displaypfx="inline-" class="mathjax-container">\((x_1,x_2,x_1^2,x_2^2,x_1x_2)\)</span> 这样的更高维特征，原本的弯曲边界就可能对应高维空间中的一个超平面。于是复杂性并没有消失，而是被转移到了特征映射 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}=\phi(\boldsymbol{x})\)</span> 上。</p>
<p>这也是经典机器学习里一个非常常见的套路：<span style="background-color: #c0c0c0;">先做特征构造或特征展开，再用结构简单、优化稳定的线性模型</span>。因此，升维常常并不以“升维”这个名字出现，而是以多项式特征、基函数展开、核方法、one-hot 编码、N-gram 稀疏特征、随机特征等形式出现。</p>
<div class="blog_h3"><span class="graybg">公式和详细解释</span></div>
<p>升维后的线性模型通常写成：</p>
<span displaypfx="" class="mathjax-container">\[f(\boldsymbol{x})=\boldsymbol{w}^\top \tilde{\boldsymbol{x}}+b\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}\)</span> 是由原始输入 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 构造出来的高维特征向量； <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\in \mathbb{R}^D\)</span> 是新空间中的参数； <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是偏置。关键点在于：模型在 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}\)</span> 空间里仍然是线性的，但如果 <span displaypfx="inline-" class="mathjax-container">\(\phi(\boldsymbol{x})\)</span> 含有平方项、交叉项、基函数或核映射，那么 <span displaypfx="inline-" class="mathjax-container">\(f(\boldsymbol{x})\)</span> 相对于原始输入 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 就会表现出非线性。</p>
<p>以二次多项式特征为例，若原始输入为 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}=(x_1,x_2)\)</span>，则可构造：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\boldsymbol{x}}=(x_1,x_2,x_1^2,x_2^2,x_1x_2)\]</span>
<p>此时线性模型</p>
<span displaypfx="" class="mathjax-container">\[f(\boldsymbol{x})=w_1x_1+w_2x_2+w_3x_1^2+w_4x_2^2+w_5x_1x_2+b\]</span>
<p>在参数上仍然是线性的，但在输入上已经能够表达二次曲面或二次决策边界。这正是升维最核心的数学作用：<span style="background-color: #c0c0c0;">把原空间中的非线性关系，改写成高维空间中的线性关系</span>。</p>
<div class="blog_h3"><span class="graybg">常见升维方式</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">方式</td>
<td style="text-align: center;">升维形式</td>
<td style="text-align: center;">核心作用</td>
<td style="text-align: center;">典型场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>多项式特征</td>
<td>加入平方项、立方项、交叉项</td>
<td>把低阶线性模型扩展为可拟合非线性关系</td>
<td>线性回归、逻辑回归、线性分类器</td>
</tr>
<tr>
<td>基函数展开</td>
<td>高斯基、样条基、傅里叶基等</td>
<td>用一组预定义函数把局部或周期结构显式展开</td>
<td>回归、广义加性模型、核近似</td>
</tr>
<tr>
<td>核方法</td>
<td>通过核函数隐式映射到高维甚至无限维空间</td>
<td>在不显式展开特征的情况下提升可分性</td>
<td>SVM、核岭回归、核 PCA</td>
</tr>
<tr>
<td>One-hot 编码</td>
<td>把离散类别映射为高维稀疏向量</td>
<td>让类别变量进入线性模型并保持类别独立性</td>
<td>表格特征、推荐、广告、点击率预估</td>
</tr>
<tr>
<td>N-gram / 词袋</td>
<td>把文本映射为高维稀疏词项空间</td>
<td>显式展开局部共现与组合模式</td>
<td>传统文本分类、检索、朴素贝叶斯、线性 SVM</td>
</tr>
<tr>
<td>随机特征</td>
<td>用随机映射近似某些核空间</td>
<td>在显式高维表示与核方法之间做折中</td>
<td>Random Fourier Features、核近似</td>
</tr>
</tbody>
</table>
<p>其中，核方法是经典机器学习里最有代表性的升维思想。以 SVM 为例，若定义特征映射 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}=\phi(\boldsymbol{x})\)</span>，则线性分类器可写成 <span displaypfx="inline-" class="mathjax-container">\(f(\boldsymbol{x})=\boldsymbol{w}^\top \tilde{\boldsymbol{x}}+b\)</span>。核技巧（Kernel Trick）并不显式构造 <span displaypfx="inline-" class="mathjax-container">\(\tilde{\boldsymbol{x}}\)</span>，而是直接通过核函数</p>
<span displaypfx="" class="mathjax-container">\[K(\boldsymbol{x},\boldsymbol{x}')=\phi(\boldsymbol{x})^\top \phi(\boldsymbol{x}')\]</span>
<p>计算升维后特征空间中的内积。这样既保留了高维映射的表达力，又避免了显式展开到巨大维度的代价。也正因为如此，SVM 是很多人最先感受到“升维威力”的经典模型。</p>
<div class="blog_h3"><span class="graybg">应用实例</span></div>
<p>在圆形可分但线性不可分的数据中，原始二维平面上一条直线无法把内圈与外圈分开；但若加入半径相关的二次特征，例如 <span displaypfx="inline-" class="mathjax-container">\(x_1^2+x_2^2\)</span>，则问题可以转写为更高维空间中的线性分离。文本任务里，N-gram 稀疏特征同样是一种典型升维：原始句子是离散序列，经过词袋或 N-gram 展开后，就变成了数万维甚至更高维的稀疏向量，随后再交给逻辑回归、朴素贝叶斯或线性 SVM 处理。</p>
<p>表格任务中的类别变量处理也体现了同样思想。一个城市字段看起来只是一个离散取值，但 one-hot 编码后，它会被展开成一个高维稀疏向量，使模型能够为每个类别学习独立参数。推荐系统、广告点击率预估、工业风控中的大规模离散特征，长期都高度依赖这种升维方式。</p>
<div class="blog_h3"><span class="graybg">为什么经典机器学习更谨慎地升维</span></div>
<p>经典机器学习也会使用升维，但通常更谨慎。原因在于，维度一旦上去，过拟合风险、存储开销与计算开销都会迅速增加，这就是维度灾难（Curse of Dimensionality）的典型体现。于是经典方法常把“升维”与“控制复杂度”配套使用：一边通过特征展开提升表达能力，一边用正则化（Regularization）、特征选择（Feature Selection）或后续降维来抑制过拟合。</p>
<p>因此，在经典机器学习里，常见工作流并不是“只升维”或“只降维”，而是两者交替配合：先做有针对性的特征展开，让结构变得更容易表达；再通过正则化、筛选或压缩保留真正有效的部分。升维是在展开表达能力，降维是在压缩冗余信息，它们并不是对立操作，而是围绕表示空间做的两种互补控制。</p>
<div class="blog_h3"><span class="graybg">维度灾难</span></div>
<p>维度灾难（Curse of Dimensionality）指的是：当特征维度不断升高时，许多在低维空间中直观、有效的统计与几何规律会迅速恶化，导致数据需求、计算成本与建模难度同时上升。它不是某一个单独问题，而是一组高维效应的统称，包括样本空间体积指数级膨胀、样本变得极其稀疏、局部邻域难以稳定估计、距离与密度统计的判别力下降，以及模型更容易用复杂边界去记忆训练集。</p>
<p>其中一个最重要的现象确实是：<span style="background-color: #c0c0c0;">高维里距离往往会变得不再像低维那样有区分力</span>。直观地说，当维度很多时，样本之间的最近距离和最远距离可能越来越接近，导致“谁是真正近邻”这件事变得没那么清晰。依赖距离或局部邻域的方法，例如 KNN、聚类、核密度估计、局部异常检测等，往往会因此退化。这也是为什么高维数据上，距离度量、标准化、特征筛选和嵌入学习会变得格外重要。</p>
<p>但“高维更容易过拟合”并不只因为距离失去意义。更根本的原因是：当维度升高后，表示空间的自由度与可容纳的划分方式急剧增加，而训练样本相对于整个空间显得越来越稀疏。模型于是更容易找到一些只对训练集成立、却不能推广到新样本的偶然边界或偶然相关性。换句话说，<span style="background-color: #c0c0c0;">距离退化主要伤害的是邻域、相似度与密度估计；过拟合风险上升则更直接地来自空间稀疏化、参数自由度增加和有效样本覆盖不足</span>。</p>
<p>这也是为什么经典机器学习在做升维时往往必须同步引入约束：一方面用特征展开提升表达能力，另一方面通过正则化（Regularization）、特征选择（Feature Selection）、降维（Dimensionality Reduction）或更强的数据先验，限制模型不要把高维空间当成“背题空间”。因此，维度灾难并不是在说“高维一定不好”，而是在提醒：<span style="background-color: #c0c0c0;">维度每增加一层，模型就需要更多数据、更强归纳偏置和更谨慎的复杂度控制，才能让新增维度真正转化为有效表达能力</span>。</p>
<div class="blog_h3"><span class="graybg">和深度学习中的升维关系</span></div>
<p>深度学习中的很多操作，本质上也在做升维。Transformer 的前馈网络（Feed-Forward Network, FFN / MLP）常把 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 升到更大的 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{ff}}\)</span>，再降回原维度；这与经典机器学习里“先展开、再用简单变换处理”的思想是一脉相承的。差别在于，经典方法里的升维往往是人工设计或固定形式的，而深度学习中的升维通常是可学习的线性投影与非线性组合。</p>
<p>因此，若从表示学习角度看，升维在机器学习中并不罕见。SVM 的核空间、逻辑回归的多项式特征、文本的 N-gram 稀疏向量、推荐系统的 one-hot 离散展开，以及 Transformer MLP 中的中间维度扩张，本质上都属于同一条主线：<span style="background-color: #c0c0c0;">把原本难以处理的关系，展开到一个更容易表达与分离的表示空间里</span>。</p>
<div class="blog_h2"><span class="graybg">降维</span></div>
<p>降维处理的核心问题是：高维数据往往包含冗余、相关性与噪声，既增加计算成本，也削弱可视化与建模稳定性。目标不是简单“删维度”，而是在压缩表示的同时尽量保留有用结构——这个“有用”可以是方差、类别可分性、局部邻域、全局流形，具体取决于所采用的方法。</p>
<div class="blog_h3"><span class="graybg">主成分分析（PCA）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>主成分分析（Principal Component Analysis, PCA）处理的是线性降维问题：在尽量保留数据主要变化信息的前提下，把高维样本映射到更低维空间。它常用于压缩维度、去相关、可视化与噪声抑制。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/pca.jpg"><img class="alignnone size-full wp-image-41347" src="https://blog.gmem.cc/wp-content/uploads/2026/03/pca.jpg" alt="pca" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>PCA 用方差（Variance）近似衡量信息量。若数据在某个方向上的投影变化很大，说明该方向承载了更多结构；于是 PCA 选择能最大化投影方差的一组正交方向作为新的表示基底。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>给定中心化后的数据矩阵 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{X}_c\in\mathbb{R}^{N\times d}\)</span>，协方差矩阵为：</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{\Sigma}=\frac{1}{N}\boldsymbol{X}_c^\top \boldsymbol{X}_c\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{X}_c\)</span> 是已经减去均值后的数据矩阵， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{X}_c^\top \boldsymbol{X}_c\)</span> 汇总了各个特征之间如何共同变化；再除以 <span displaypfx="inline-" class="mathjax-container">\(N\)</span>，就得到平均意义下的协方差矩阵 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\Sigma}\)</span>。PCA 正是从这个矩阵里找“变化最大的方向”。</p>
<p>第一主成分对应的优化问题为：</p>
<span displaypfx="" class="mathjax-container">\[\max_{\boldsymbol{u}} \quad \boldsymbol{u}^\top \boldsymbol{\Sigma}\boldsymbol{u} \quad \text{s.t.} \quad \|\boldsymbol{u}\|_2=1\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{u}\)</span> 是候选投影方向， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{u}^\top \boldsymbol{\Sigma}\boldsymbol{u}\)</span> 表示数据投影到该方向后的方差，约束 <span displaypfx="inline-" class="mathjax-container">\(\|\boldsymbol{u}\|_2=1\)</span> 则防止通过把向量无限放大来虚增方差。于是这个优化问题真正寻找的是“单位长度下最能保留变化信息的方向”。</p>
<p>其解是协方差矩阵最大特征值对应的特征向量。取前 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个主成分组成矩阵 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{U}_k\)</span> 后，样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span> 的低维表示为：</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{z}=\boldsymbol{U}_k^\top(\boldsymbol{x}-\boldsymbol{\mu})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\mu}\)</span> 是原始数据均值， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}-\boldsymbol{\mu}\)</span> 先把样本中心化， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{U}_k\)</span> 由前 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个主成分方向组成，最终 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{z}\)</span> 就是样本在这组主方向上的低维坐标。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>对数据做中心化，必要时再做标准化。</li>
<li>计算协方差矩阵或直接对数据矩阵做奇异值分解（SVD）。</li>
<li>取前 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个主成分方向。</li>
<li>把数据投影到这些方向上得到低维表示。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在人脸图像压缩中，大量像素变化往往由少数全局因素驱动。PCA 可以用少量主成分保留大部分有效变化，从而显著降低特征维度。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：线性、稳定、可解释，常用作预处理和可视化。</li>
<li>局限：只能捕捉线性结构；对异常值敏感。</li>
<li>适用场景：线性降维、特征压缩、去相关与噪声过滤。</li>
</ul>
<div class="blog_h3"><span class="graybg">线性判别分析（LDA）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>线性判别分析（Linear Discriminant Analysis, LDA）用于监督降维与分类。与 PCA 只看输入分布不同，LDA 利用类别标签寻找一个投影空间，使同类样本尽量聚集、异类样本尽量分开。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/lda.jpg"><img class="alignnone size-full wp-image-41349" src="https://blog.gmem.cc/wp-content/uploads/2026/03/lda.jpg" alt="lda" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>LDA 同时考虑类内散度（Within-class Scatter）和类间散度（Between-class Scatter）。好的投影方向应当让类间距离大、类内波动小，因此它优化的核心是判别性。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>定义类内散度矩阵：</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{S}_W=\sum_{k=1}^{C}\sum_{\boldsymbol{x}_i\in \mathcal{C}_k}(\boldsymbol{x}_i-\boldsymbol{\mu}_k)(\boldsymbol{x}_i-\boldsymbol{\mu}_k)^\top\]</span>
<p>类内散度矩阵 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{S}_W\)</span> 统计的是“同一类内部有多分散”。 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{C}_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 类的样本集合， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\mu}_k\)</span> 是该类均值。若类内样本围绕各自均值分布得很紧， <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{S}_W\)</span> 就会较小。</p>
<p>类间散度矩阵：</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{S}_B=\sum_{k=1}^{C}N_k(\boldsymbol{\mu}_k-\boldsymbol{\mu})(\boldsymbol{\mu}_k-\boldsymbol{\mu})^\top\]</span>
<p>类间散度矩阵 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{S}_B\)</span> 统计的是“各类中心彼此有多分开”。其中 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\mu}\)</span> 是全局均值， <span displaypfx="inline-" class="mathjax-container">\(N_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 类样本数，因此样本多的大类会对类间结构贡献更大权重。</p>
<p>LDA 寻找投影向量 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\)</span> 使 Fisher 判别准则最大：</p>
<span displaypfx="" class="mathjax-container">\[J(\boldsymbol{w})=\frac{\boldsymbol{w}^\top \boldsymbol{S}_B \boldsymbol{w}}{\boldsymbol{w}^\top \boldsymbol{S}_W \boldsymbol{w}}\]</span>
<p>Fisher 准则的分子衡量“投影后类间有多分开”，分母衡量“投影后类内有多混在一起”。因此 <span displaypfx="inline-" class="mathjax-container">\(J(\boldsymbol{w})\)</span> 越大，说明方向 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\)</span> 越有助于把不同类别拉开、同时保持同类紧凑。</p>
<p>该问题可转化为广义特征值问题 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{S}_B\boldsymbol{w}=\lambda \boldsymbol{S}_W\boldsymbol{w}\)</span>。对 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 类问题，最多能得到 <span displaypfx="inline-" class="mathjax-container">\(C-1\)</span> 个有效判别方向。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>根据标签统计每一类的均值与全局均值。</li>
<li>构造类内散度矩阵和类间散度矩阵。</li>
<li>求解广义特征值问题，选择主要判别方向。</li>
<li>将样本投影到低维判别子空间中做分类或可视化。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在手写数字分类中，LDA 关注的是“哪些方向最有助于把不同数字分开”，因此在有标签监督时，它往往比只最大化总体方差的 PCA 更适合分类前降维。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：利用标签信息，投影方向更有判别性。</li>
<li>局限：线性假设较强；类内散度矩阵可能奇异。</li>
<li>适用场景：有标签监督的降维、分类前特征压缩、判别性可视化。</li>
</ul>
<div class="blog_h3"><span class="graybg">t-SNE</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>t-SNE（t-distributed Stochastic Neighbor Embedding）主要用于二维或三维可视化。它主要关注如何在低维图上尽量保留高维空间中的局部邻域关系。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>t-SNE 把“样本彼此接近”转写为概率相似度：在高维空间中，接近的样本应当有较大概率互为邻居；在低维嵌入中，也希望这种邻近关系继续成立。优化的目标是让两种邻域概率分布尽可能接近。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>在高维空间中，先定义邻域概率 <span displaypfx="inline-" class="mathjax-container">\(p_{ij}\)</span>；在低维空间中，对嵌入点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}_i\)</span> 定义：</p>
<span displaypfx="" class="mathjax-container">\[q_{ij}=\frac{(1+\|\boldsymbol{y}_i-\boldsymbol{y}_j\|_2^2)^{-1}}{\sum_{a\ne b}(1+\|\boldsymbol{y}_a-\boldsymbol{y}_b\|_2^2)^{-1}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}_i\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{y}_j\)</span> 是低维嵌入坐标，分子把距离越近的点赋予越大的相似度，分母则把所有点对的相似度加总起来做归一化，因此 <span displaypfx="inline-" class="mathjax-container">\(q_{ij}\)</span> 可以被解释成低维空间里的邻域概率。</p>
<p>t-SNE 最小化高维邻域分布与低维邻域分布之间的 KL 散度：</p>
<span displaypfx="" class="mathjax-container">\[\text{KL}(P\|Q)=\sum_{i\ne j} p_{ij} \log \frac{p_{ij}}{q_{ij}}\]</span>
<p>KL 散度衡量的是“低维邻域分布 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 与高维邻域分布 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 相差多少”。若某对样本在高维里很近，即 <span displaypfx="inline-" class="mathjax-container">\(p_{ij}\)</span> 很大，但在低维里被拉得太开， <span displaypfx="inline-" class="mathjax-container">\(q_{ij}\)</span> 就会过小，从而产生较大惩罚。</p>
<p>低维中采用重尾 Student-t 分布，主要是为了缓解拥挤问题（Crowding Problem），使远处点更容易被拉开。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>根据高维距离计算样本间的邻域概率。</li>
<li>随机初始化低维坐标。</li>
<li>计算低维相似度 <span displaypfx="inline-" class="mathjax-container">\(q_{ij}\)</span>。</li>
<li>通过梯度下降最小化 KL 散度并更新低维坐标。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在词向量或图像嵌入可视化中，t-SNE 常把语义相近的样本压到局部团簇中，从而帮助研究者直观看到表示空间里是否出现了合理分群。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：二维可视化效果强，局部邻域保持能力好。</li>
<li>局限：全局距离与簇间相对位置不稳定；对超参数和随机初始化敏感。</li>
<li>适用场景：嵌入可视化、表示质量诊断、探索性数据分析。</li>
</ul>
<div class="blog_h3"><span class="graybg">UMAP</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>UMAP（Uniform Manifold Approximation and Projection）同样用于低维可视化与非线性降维。它面对的任务与 t-SNE 类似，但更强调在保留局部结构的同时，尽量维持一定的全局几何关系，并提升速度与可扩展性。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/umap.jpg"><img class="alignnone size-full wp-image-41351" src="https://blog.gmem.cc/wp-content/uploads/2026/03/umap.jpg" alt="umap" width="1024" height="559" /></a></p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>UMAP 先在高维空间构建一个加权近邻图，把数据视为流形（Manifold）上的离散采样；随后在低维空间中寻找一张新图，使低维图与高维图的模糊连通结构尽量一致。它本质上是在匹配两张图的连通结构，而不只是在匹配两组欧氏距离。</p>
<p>这里的流形可以先从一个标准定义理解：设样本位于高维空间 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^D\)</span> 中，若它们实际上集中在某个低维集合 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{M}\subseteq\mathbb{R}^D\)</span> 附近，并且对 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{M}\)</span> 上任一点，都能在一个足够小的邻域内用 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维坐标平滑描述，其中 <span displaypfx="inline-" class="mathjax-container">\(d\ll D\)</span>，那么这个集合就可以看作一个 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维流形。流形的关键性质不是“它弯不弯”，而是<span style="background-color: #c0c0c0;">它在局部看起来像低维欧氏空间，在全局上则可以嵌入到更高维空间并发生弯曲、卷曲或拉伸</span>。</p>
<p>在数据分析里，这个概念表示：虽然原始特征有很多维，但样本并没有真正填满整个高维空间，而是分布在某种低维结构附近。例如一组人脸图像在像素空间里维度极高，但由姿态、光照、表情等少数潜在因素变化时，样本往往只覆盖其中一个低维子区域。UMAP 的出发点正是：若数据主要几何结构由这个低维流形决定，那么局部近邻关系比“全局欧氏直线距离”更能反映真实结构。</p>
<p>“离散采样”则表示：真实流形 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{M}\)</span> 本身是连续对象，但我们手里只有有限个样本点 <span displaypfx="inline-" class="mathjax-container">\(\{x_i\}_{i=1}^N\)</span>。UMAP 通过近邻图近似这些点在流形上的局部连通关系，再在低维空间中寻找一组坐标 <span displaypfx="inline-" class="mathjax-container">\(\{y_i\}_{i=1}^N\)</span>，使这种局部连通关系尽量被保留下来。因此它不是直接恢复整张流形，而是在有限样本层面恢复流形的邻域结构。</p>
<p>与 t-SNE 相比，UMAP 更强调“样本来自某个低维流形，并且近邻图是在近似这个流形的局部连通结构”；t-SNE 的核心对象则更偏向“高维邻域概率分布与低维邻域概率分布的匹配”。两者都重视局部关系，但 UMAP 的表述更几何化，t-SNE 的表述更概率化。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>若高维图边权为 <span displaypfx="inline-" class="mathjax-container">\(p_{ij}\)</span>，低维图边权常写为：</p>
<span displaypfx="" class="mathjax-container">\[q_{ij}=\frac{1}{1+a\|\boldsymbol{y}_i-\boldsymbol{y}_j\|_2^{2b}}\]</span>
<p>UMAP 用这个函数把低维距离转换成连通强度。 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 控制曲线形状：距离很近时 <span displaypfx="inline-" class="mathjax-container">\(q_{ij}\)</span> 接近 1，距离变远时迅速衰减到接近 0，因此它可以把“近邻关系”转写成平滑的图边权。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(a,b\)</span> 决定低维距离与连接强度的映射形状。UMAP 的优化目标通常是高维图与低维图之间的交叉熵（Cross-Entropy）：既鼓励高维中相连的点在低维中也靠近，也鼓励高维中不相连的点在低维中适当分开。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>计算近邻图，并为边赋予模糊连通权重。</li>
<li>初始化低维坐标。</li>
<li>通过随机优化最小化高维图与低维图之间的交叉熵。</li>
<li>得到二维或三维嵌入用于可视化或下游分析。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在单细胞测序、文本嵌入或推荐向量分析中，UMAP 常被用来把高维表示映射到二维平面，从而观察群体结构、类别分布与异常点。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：通常比 t-SNE 更快，较好兼顾局部与部分全局结构。</li>
<li>局限：嵌入结果仍依赖超参数与随机种子；二维距离不能机械等同于原空间距离。</li>
<li>适用场景：大规模嵌入可视化、非线性降维、聚类前的低维表示。</li>
</ul>
<div class="blog_h2"><span class="graybg">异常检测</span></div>
<p>异常检测处理的核心问题是：正常样本通常大量存在，而异常样本稀少、形态多变、甚至在训练阶段根本拿不到完整标签。模型因此不再主要学习“类别之间如何区分”，而是学习“什么算正常”以及“偏离正常结构有多严重”。不同方法对“异常”的定义并不相同：有的依赖隔离难易度，有的依赖局部密度，有的学习正常区域边界。</p>
<div class="blog_h3"><span class="graybg">什么叫异常</span></div>
<p>在业务语境里，“异常”并不等于“数值特别大”或“离均值很远”。更准确地说，异常是<span style="background-color: #c0c0c0;">相对于当前业务规则、历史模式或同类群体而言，不应当出现、很少出现、或者一旦出现就值得额外关注的样本</span>。因此异常是一个“相对概念”，必须依赖参照系：相对于谁、在哪个时间段、在什么上下文里、以什么代价衡量。</p>
<p>例如，单笔消费 5000 元在全国范围内不一定异常，但若这位用户平时只在本地便利店做几十元交易，而这次交易突然发生在异地、高风险设备、深夜时段，并伴随支付习惯突变，那么它在风控上就可能是异常。类似地，服务器 CPU 使用率 85% 在大促期间可能是正常负载，在凌晨低峰却可能意味着任务堆积；工厂传感器温度轻微升高若同时伴随振动模式变化，也可能预示故障正在形成。</p>
<p>这说明业务上的异常通常至少包含三类含义。第一类是<span style="background-color: #c0c0c0;">统计稀有</span>：样本落在低概率区域。第二类是<span style="background-color: #c0c0c0;">行为失配</span>：它与该对象自己的历史模式不一致。第三类是<span style="background-color: #c0c0c0;">群体失配</span>：它在全局上不一定极端，但相对于同类群体显著不同。异常检测算法的差异，本质上就在于它们分别更擅长刻画哪一种“失配”。</p>
<p>因此，做异常检测时首先要回答的不是“用哪种模型”，而是“业务到底把什么视为异常”。若异常意味着“明显稀少且容易与大部队分开”，隔离式方法更合适；若异常意味着“在本地邻域里显得稀疏”，应优先考虑密度比较；若正常样本边界清楚、异常样本类型杂乱，则更适合只学习正常区域。算法不是在定义业务，而是在实现业务已经确定的异常标准。</p>
<div class="blog_h3"><span class="graybg">孤立森林（Isolation Forest）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>孤立森林（Isolation Forest）用于无监督异常检测：在没有可靠异常标签时，仅根据数据分布本身识别“容易被孤立”的异常样本。它尤其适合高维表格数据与大规模检测场景。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>孤立森林利用一个非常直接的直觉：异常点通常更稀少、更孤立，因此在随机切分下更容易被提早隔离。路径越短，越可能是异常；路径越长，越像处于正常群体内部。若把正常样本理解为“扎堆生活在高密度区域里的大群体”，那么它们往往需要经过很多次随机切割才会被单独分离出来；而那些落在边缘、稀疏区域、或与主体分布明显脱节的样本，只需少量切分就会被单独留在某个叶节点里。</p>
<p>这种思路和距离或密度方法非常不同。K 近邻（K-Nearest Neighbors, KNN）或局部异常因子（Local Outlier Factor, LOF）会显式比较“离别人有多远”或“局部密度有多低”；孤立森林则直接把异常检测改写成一个更具操作性的判据：<span style="background-color: #c0c0c0;">一个样本在随机树里平均多快会被单独隔开</span>。因此它并不先估计复杂的概率分布，也不依赖全局距离结构，而是用“隔离难易度”来近似刻画异常性。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/random-forest.png"><img class="alignnone size-full wp-image-41363" src="https://blog.gmem.cc/wp-content/uploads/2026/03/random-forest.png" alt="random-forest" width="1600" height="1308" /></a></p>
<div class="blog_h4"><span class="graybg">图示解读</span></div>
<p>图中的示意数据由两个主要正常簇和若干离散分布的异常点构成。背景等高线展示的是孤立森林学到的“异常分数地形”：颜色越深，表示该区域样本平均路径长度越长，更接近模型眼中的正常高密度区域；颜色越浅，表示样本更容易在随机切分中被提早隔离，因此更接近潜在异常区域。</p>
<p>图中圆形点对应被模型判为正常的样本，它们主要聚集在两个深色中心附近；叉号对应被模型判为异常的样本，它们更多分布在边缘、簇间空白带或浅色区域。这种可视化非常适合帮助理解孤立森林的工作方式：它并不是在画一条严格的几何边界，而是在表达“哪里更容易被随机树迅速切出来”。因此，等高线的深浅更接近一种“隔离难度地图”，而不是传统分类器意义上的硬分类边界。</p>
<div class="blog_h4"><span class="graybg">隔离树是什么</span></div>
<p>隔离树（Isolation Tree, iTree）可以理解成一棵专门为了“把样本逐步切开”而构造的随机二叉树。它与决策树（Decision Tree）在外形上相似，但目标完全不同：决策树是在找最有区分力的切分规则，隔离树则故意随机选择一个特征，再在该特征当前取值范围内随机选择一个切分点，把样本递归分到左右子节点。</p>
<p>设当前节点包含样本集合 <span displaypfx="inline-" class="mathjax-container">\(S\)</span>，随机选到的特征为第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 维，其切分阈值为 <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span>，则一次切分可写成</p>
<span displaypfx="" class="mathjax-container">\[S_{\mathrm{left}}=\{\boldsymbol{x}\in S\mid x_j&lt;\tau\},\qquad S_{\mathrm{right}}=\{\boldsymbol{x}\in S\mid x_j\ge \tau\}\]</span>
<p>递归继续进行，直到某个节点只剩下 1 个样本，或所有样本在当前节点上已经无法再被有效区分。对一个本来就远离主体、所在区域又很稀疏的样本而言，随机切分往往只需要很少几步就能把它单独留在某个叶节点中；而对处在高密度正常群体内部的样本，通常要经过更多次切分才能被单独隔离。隔离树因此并不显式估计概率密度，而是把“异常”转写为“被随机切分提早单独分离”的难易度。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>对样本 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{x}\)</span>，记它在一棵隔离树中的路径长度为 <span displaypfx="inline-" class="mathjax-container">\(h(\boldsymbol{x})\)</span>。在多棵树上取平均路径长度 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[h(\boldsymbol{x})]\)</span> 后，异常分数定义为：</p>
<span displaypfx="" class="mathjax-container">\[s(\boldsymbol{x},n)=2^{-\mathbb{E}[h(\boldsymbol{x})]/c(n)}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(h(\boldsymbol{x})\)</span> 是样本在一棵树里被隔离所需的路径长度， <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[h(\boldsymbol{x})]\)</span> 是在整片森林中的平均值， <span displaypfx="inline-" class="mathjax-container">\(c(n)\)</span> 是针对样本规模 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 的归一化常数。路径越短，指数里的值越小，异常分数 <span displaypfx="inline-" class="mathjax-container">\(s(\boldsymbol{x},n)\)</span> 就越接近 1。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(c(n)\)</span> 是平均路径长度的归一化常数，用来把不同样本规模下的路径长度放到可比较的尺度上。它常写为 <span displaypfx="inline-" class="mathjax-container">\(c(n)=2H_{n-1}-2(n-1)/n\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(H_{n-1}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(n-1\)</span> 个调和数（Harmonic Number）。若某点明显比普通样本更早被切分隔离，则其平均路径长度更小，异常分数更接近 1。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>从训练集中随机抽取多个子样本。</li>
<li>为每个子样本构建随机隔离树。</li>
<li>对待测点计算其在所有树中的平均路径长度。</li>
<li>将平均路径长度映射为异常分数。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在交易风控中，若某条交易在金额、时间、地点、设备等多个维度上都明显偏离正常模式，它往往能在随机切分下被较早隔离出来，因此会获得更高异常分数。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：无需显式估计密度，也不依赖两两距离矩阵；因此在大规模数据上通常比基于邻域或密度的方法更高效。</li>
<li>优点：对高维表格数据较友好，对随机噪声通常也有较强鲁棒性，因为最终判断来自多棵随机树上的平均隔离行为，而不是某一次局部切分。</li>
<li>局限：对特征编码和特征尺度的业务含义仍然敏感；若异常与正常高度混叠、或者异常本身并不更容易被切开，隔离优势会下降。</li>
<li>适用场景：风控、日志异常、设备故障、指标监控等无监督异常检测，尤其适合作为高维表格场景中的强基线。</li>
</ul>
<div class="blog_h3"><span class="graybg">局部异常因子（LOF）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>局部异常因子（Local Outlier Factor, LOF）主要解决“全局上不远，但在局部邻域中显著稀疏”的异常检测问题。当数据不同区域密度差异很大时，只看全局距离通常不够稳定。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>LOF 比较的是“这个点相对于其邻居是否更稀疏”。若一个点周围的局部密度显著低于邻居自己的局部密度，则它更像局部异常。它识别的不是“全局上最远的点”，而是<span style="background-color: #c0c0c0;">相对于自己所在局部环境显得不协调的点</span>。因此，当不同区域本来就有不同密度时，LOF 往往比只看全局距离的方法更稳。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/lof.png"><img class="alignnone size-full wp-image-41371" src="https://blog.gmem.cc/wp-content/uploads/2026/03/lof.png" alt="lof" width="1000" height="800" /></a></p>
<div class="blog_h4"><span class="graybg">图示解读</span></div>
<p>图中的实心圆点表示样本点，围绕样本点的空心圆圈大小表示该点的 LOF 分数大小。圆圈越小，说明该点的局部密度与其邻居相近，更像正常簇中的内部样本；圆圈越大，说明该点所在位置相对于邻居显得更稀疏，因此更可能是局部异常点。</p>
<p>这张图直观展示了 LOF 的核心判断方式：它并不先问“这个点离全局中心有多远”，而是先问“这个点和自己周围那一圈邻居相比，是不是显得过于稀疏”。因此，大圆圈对应的未必是全局最远的点，而更可能是那些<span style="background-color: #c0c0c0;">周围邻居仍然较密、但它自己明显脱离了局部密度水平</span>的点。</p>
<p>若把这张示意图看作两个高密度簇与少量随机噪声点的组合，则邻居数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 决定了算法观察“局部环境”的尺度。 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 较小时，模型更敏感于非常局部的扰动； <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 较大时，密度比较更平滑，但也可能削弱对细粒度异常的敏感性。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>给定邻居数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span>，设 <span displaypfx="inline-" class="mathjax-container">\(N_k(\boldsymbol{p})\)</span> 表示点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{p}\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个最近邻集合。LOF 的计算可以拆成三层：先算可达距离（Reachability Distance），再算局部可达密度（Local Reachability Density, LRD），最后比较邻居密度与自身密度，得到局部异常因子（Local Outlier Factor, LOF）。</p>
<p>先定义可达距离（Reachability Distance）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{rd}_k(\boldsymbol{p},\boldsymbol{o})=\max\big(\text{k-distance}(\boldsymbol{o}),d(\boldsymbol{p},\boldsymbol{o})\big)\]</span>
<p>可达距离会把两个量取最大值：邻居点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{o}\)</span> 自己的第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 邻距离，以及点对之间的真实距离。这样做可以防止极近的点对把局部密度估得过于夸张，使密度估计更稳。</p>
<p>再定义局部可达密度（Local Reachability Density, LRD）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{lrd}_k(\boldsymbol{p})=\left(\frac{1}{|N_k(\boldsymbol{p})|}\sum_{\boldsymbol{o}\in N_k(\boldsymbol{p})} \mathrm{rd}_k(\boldsymbol{p},\boldsymbol{o})\right)^{-1}\]</span>
<p>局部可达密度 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{lrd}_k(\boldsymbol{p})\)</span> 本质上是“平均可达距离”的倒数：平均距离越小，周围越拥挤，密度越大；平均距离越大，周围越稀疏，密度越小。</p>
<p>最终的 LOF 分数为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{LOF}_k(\boldsymbol{p})=\frac{1}{|N_k(\boldsymbol{p})|}\sum_{\boldsymbol{o}\in N_k(\boldsymbol{p})} \frac{\mathrm{lrd}_k(\boldsymbol{o})}{\mathrm{lrd}_k(\boldsymbol{p})}\]</span>
<p>LOF 分数比较的是“邻居的局部密度”和“自己本身的局部密度”。若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{lrd}_k(\boldsymbol{p})\)</span> 明显小于邻居的密度，分数就会大于 1，说明该点相对周围环境显得更孤立。更具体地说，分子是邻居局部密度的平均水平，分母是点 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{p}\)</span> 自己的局部密度，因此这个比值本质上是在问：<span style="background-color: #c0c0c0;">你周围的人都很挤，而你自己是不是站得太空</span>。</p>
<p>结果通常可以这样解读：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{LOF}_k(\boldsymbol{p})\approx 1\)</span>：该点的局部密度与邻居相近，通常属于正常样本。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{LOF}_k(\boldsymbol{p})&lt;1\)</span>：该点甚至比邻居更密集，往往处于簇的核心区域。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{LOF}_k(\boldsymbol{p})&gt;1\)</span>：该点局部密度低于邻居，越大越像异常点。</li>
</ul>
<p>因此，LOF 回答的是“这个点在自己的局部邻域里是不是显得过于稀疏”，而不是“这个点离全局中心远不远”。这也是它能识别局部异常、却对距离度量与邻居数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 较敏感的根本原因。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>为每个样本找到 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个近邻。</li>
<li>计算可达距离与局部可达密度。</li>
<li>比较样本与邻居的局部密度，得到 LOF 分数。</li>
<li>按分数排序或设置阈值输出异常点。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在消费行为数据中，某一线城市用户的高消费在全国范围内未必异常，但在“同年龄、同区域、同收入”的邻域里可能明显偏离。LOF 正是通过这种局部密度比较识别这类异常。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：适合不同密度区域共存的数据，能识别“全局上不极端、但局部上明显失配”的异常。</li>
<li>优点：解释性较强，因为 LOF 分数直接来自“邻居密度 / 自身密度”的局部比较，便于回答异常是相对于谁显得异常。</li>
<li>局限：对距离度量、标准化和邻居数 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 很敏感； <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 太小会放大噪声，太大又可能抹平真正的局部异常。</li>
<li>局限：大规模近邻搜索成本较高，因此在超大数据上常需要索引加速或近似近邻方法配合。</li>
<li>适用场景：局部离群检测、消费异常、群体内部行为异常、同类用户或设备群体中的行为失配识别。</li>
</ul>
<div class="blog_h3"><span class="graybg">单类支持向量机（One-Class SVM）</span></div>
<div class="blog_h4"><span class="graybg">背景和问题定义</span></div>
<p>单类支持向量机（One-Class Support Vector Machine, One-Class SVM）用于“只有正常样本、缺少可靠异常样本”的异常检测任务。它的目标是学习一个描述正常样本区域的边界。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>One-Class SVM 通过核映射把样本送到高维特征空间，再寻找一个把大多数正常样本与原点分开的超平面。等价地看，它学习的是一个“包住正常样本”的高维边界，边界外的点更可能是异常。</p>
<div class="blog_h4"><span class="graybg">算法公式和详细解释</span></div>
<p>一种标准形式为：</p>
<span displaypfx="" class="mathjax-container">\[\min_{\boldsymbol{w},\rho,\boldsymbol{\xi}} \frac{1}{2}\|\boldsymbol{w}\|_2^2+\frac{1}{\nu N}\sum_{i=1}^{N} \xi_i-\rho\]</span>
<p>One-Class SVM 的目标由三部分组成： <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{2}\|\boldsymbol{w}\|_2^2\)</span> 控制边界不要太复杂， <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{\nu N}\sum_i \xi_i\)</span> 惩罚落在边界外或靠得太近的样本， <span displaypfx="inline-" class="mathjax-container">\(-\rho\)</span> 则鼓励把正常区域尽量向外推开。 <span displaypfx="inline-" class="mathjax-container">\(\nu\)</span> 越大，对违约样本的容忍度越高。</p>
<span displaypfx="" class="mathjax-container">\[\text{s.t.} \quad \boldsymbol{w}^\top \phi(\boldsymbol{x}_i) \ge \rho-\xi_i,\qquad \xi_i \ge 0\]</span>
<p>这些约束表示：样本映射到特征空间后，其投影值至少要达到阈值 <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span>；若做不到，就用非负松弛变量 <span displaypfx="inline-" class="mathjax-container">\(\xi_i\)</span> 记录违约程度。于是模型允许少量样本越界，但必须为此付出代价。</p>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}\)</span> 是超平面法向量， <span displaypfx="inline-" class="mathjax-container">\(\rho\)</span> 是阈值， <span displaypfx="inline-" class="mathjax-container">\(\xi_i\)</span> 是松弛变量， <span displaypfx="inline-" class="mathjax-container">\(\nu\in(0,1]\)</span> 控制允许落在边界外的比例与支持向量比例。判别函数为：</p>
<span displaypfx="" class="mathjax-container">\[f(\boldsymbol{x})=\text{sign}\left(\boldsymbol{w}^\top \phi(\boldsymbol{x})-\rho\right)\]</span>
<p>判别函数先计算样本在特征空间里相对边界的位置：若 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{w}^\top \phi(\boldsymbol{x})-\rho\)</span> 为正，说明样本位于学习到的正常区域一侧；若为负，则更可能落在边界之外，被视为异常。</p>
<p>若使用核函数 <span displaypfx="inline-" class="mathjax-container">\(K(\boldsymbol{x},\boldsymbol{x}')\)</span>，则该边界可以是非线性的，因此能表达复杂的正常区域。</p>
<div class="blog_h4"><span class="graybg">训练或推断流程</span></div>
<ol>
<li>只用正常样本训练模型，选择核函数与超参数 <span displaypfx="inline-" class="mathjax-container">\(\nu\)</span>。</li>
<li>在特征空间中学习分离超平面。</li>
<li>对新样本计算判别函数值。</li>
<li>若分数低于边界阈值，则判为异常。</li>
</ol>
<div class="blog_h4"><span class="graybg">应用实例</span></div>
<p>在设备健康监控中，异常故障类型往往变化很大，难以完整收集，但正常运行数据很多。One-Class SVM 可以只基于正常样本学习边界，一旦新样本落出该区域，就触发异常告警。</p>
<div class="blog_h4"><span class="graybg">优缺点与适用场景</span></div>
<ul>
<li>优点：只依赖正常样本；核方法可表达复杂边界。</li>
<li>局限：对特征缩放和核参数敏感；大规模训练较重。</li>
<li>适用场景：设备监控、入侵检测、质量控制等“正常样本丰富、异常样本稀缺”的任务。</li>
</ul>
<div class="blog_h1"><span class="graybg">神经网络</span></div>
<div class="blog_h2"><span class="graybg">前馈神经网络</span></div>
<div class="blog_h3"><span class="graybg">感知机</span></div>
<p>感知机（Perceptron）是最早的神经元模型之一，也是现代神经网络最基本的计算原型。无论是 MLP、CNN、RNN，还是 Transformer，本质上都由大量“线性变换 + 非线性变换”的单元堆叠而成；从这个意义上说，理解感知机，就是理解大型模型最小的功能部件。</p>
<p>最原始的感知机先做线性组合，再经过一个阈值函数给出二分类输出：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=\mathrm{sign}(\mathbf{w}^\top \mathbf{x}+b)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{x}\)</span> 是输入特征， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}\)</span> 是权重， <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是偏置。 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{w}^\top \mathbf{x}+b\)</span> 的含义是“沿着权重指定的方向对输入做加权打分”，而 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{sign}(\cdot)\)</span> 则把连续分数变成离散决策：高于阈值判为正类，低于阈值判为负类。</p>
<p>需要特别区分的是：最早的感知机确实直接做分类，但现代神经网络里的大多数“感知机式单元”并不直接输出最终类别。隐藏层单元更常见的形式是 <span displaypfx="inline-" class="mathjax-container">\(h=\phi(\mathbf{w}^\top \mathbf{x}+b)\)</span>，它们输出的是中间表示（Intermediate Representation），职责是检测局部模式、重组特征并为后续层提供更有用的表示；只有最后的任务头（Task Head）才把这些中间表示转成分类、回归或生成输出。</p>
<p>这里的中间表示（Intermediate Representation）本质上就是一组<span style="background-color: #c0c0c0;">可被后续层继续计算的数值特征</span>。它既可以是向量（Vector），也可以是矩阵（Matrix），更一般地说，它通常是张量（Tensor）。向量和矩阵都只是张量的特殊情形：若只看单个样本的 MLP 隐层输出，最常见的是向量 <span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{h}\in\mathbb{R}^{d}\)</span>；若看 Transformer 对整段序列的隐藏状态，常写成矩阵 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\times d}\)</span>；若再把 batch 维也带上，则会变成三维张量 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\in\mathbb{R}^{B\times L\times d}\)</span>。在卷积网络（Convolutional Neural Network, CNN）里，中间表示则常是特征图张量（Feature Map Tensor） <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\in\mathbb{R}^{C\times H\times W}\)</span>，或带 batch 的 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{B\times C\times H\times W}\)</span>。</p>
<p>因此，“中间表示”不是某种神秘对象，它就是网络在某一层对输入所形成的内部编码。它不像最终标签那样直接面向人类语义，而是把输入重写成更适合下一层处理的坐标系。例如，文本模型中的某一层隐藏状态可能同时编码词义、上下文关系、句法位置和任务相关线索；图像模型中的某一层特征图则可能突出边缘、纹理、局部部件或更高层形状。后续层与任务头读到的，正是这些内部编码。</p>
<p>感知机的重要性不止在于历史地位，更在于今天大型模型中的知识，本质上仍然是通过这类权重结构逐层编码进去的。训练过程并不会把知识像数据库那样逐条写成显式记录，而是不断调整参数，使某些输入模式被放大、某些输入模式被抑制。于是，模型在数据中反复见到的统计规律——词与词的共现、图像局部纹理、特征之间的组合关系——都会被压缩进参数矩阵的数值结构中。</p>
<p>更准确地说，大模型中的知识通常不是“某一个感知机单独存储一条事实”，而是以<span style="background-color: #c0c0c0;">分布式表示（Distributed Representation）</span>的形式分散在大量参数里。单个单元更像一个局部特征探测器（Feature Detector）：它只对某种模式敏感；许多单元级联后，网络才能把低层简单模式组合成高层抽象概念。模型规模越大、层数越深、参数越多，可被编码的模式组合也越丰富，这正是大模型具备强表达能力与“知识容量”的原因之一。</p>
<p>感知机能学会线性可分任务，但无法处理 XOR 这类线性不可分问题，这正是多层网络出现的动机：当一个超平面不够时，就需要通过多层组合把输入空间逐步重写成更容易分开的表示。</p>
<div class="blog_h3"><span class="graybg">多层感知机（MLP）</span></div>
<p>多层感知机（Multi-Layer Perceptron, MLP）处理的核心问题是：当输入与输出之间的关系不是一个超平面就能表达时，如何通过多层可学习变换，把原始特征逐步改写成更容易完成任务的表示。它由多层线性变换与逐元素非线性激活交替组成，是最基本也最通用的前馈网络结构。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/mlp.png"><img class="alignnone size-full wp-image-41415" src="https://blog.gmem.cc/wp-content/uploads/2026/03/mlp.png" alt="mlp" width="1024" height="1024" /></a></p>
<p>单层可写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{h}=\phi(W\mathbf{x}+\mathbf{b})\]</span>
<p>多层堆叠后，可写为：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{h}^{(1)}=\phi(W^{(1)}\mathbf{x}+\mathbf{b}^{(1)}),\quad \mathbf{h}^{(2)}=\phi(W^{(2)}\mathbf{h}^{(1)}+\mathbf{b}^{(2)}),\quad \hat{\mathbf{y}}=W^{(3)}\mathbf{h}^{(2)}+\mathbf{b}^{(3)}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(W^{(l)}\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}^{(l)}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层参数， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{h}^{(l)}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层隐藏表示。所谓“逐层重写表示”，就是每一层都在回答一个更具体的问题：哪些原始模式值得保留、哪些组合值得放大、哪些方向更利于后续任务。于是，早期层往往捕捉较局部、较简单的模式，后期层则把这些模式组合成更抽象的语义结构。</p>
<p>MLP 比单层感知机强，关键不在“层数更多”本身，而在于层与层之间插入了非线性激活函数 <span displaypfx="inline-" class="mathjax-container">\(\phi\)</span>。如果没有非线性，多层线性变换满足</p>
<span displaypfx="" class="mathjax-container">\[W^{(2)}(W^{(1)}\mathbf{x}+\mathbf{b}^{(1)})+\mathbf{b}^{(2)}=\tilde W\mathbf{x}+\tilde{\mathbf{b}}\]</span>
<p>最终仍然等价于一层线性变换，表达能力不会因为堆叠而提升。只有加入非线性后，网络才能把输入空间切分、折叠、拉伸并重新组合，形成复杂的分段线性或平滑非线性决策边界。</p>
<p>从几何角度看，单层模型像“用一个超平面切一次”；而多层 MLP 则是在表示空间中反复做坐标变换和非线性折叠，把原本难分的数据逐步变成线性头也能分开的形状。文中的激活函数对比图展示的正是这个过程：不同激活函数会把同一个三层网络变成完全不同的几何变换器。</p>
<p>在现代大模型中，MLP 的作用远不只是“附属模块”。在 Transformer 里，注意力层负责在 token 之间路由信息，而 MLP / Feed-Forward Network（FFN）则负责在每个位置上做通道维度的非线性特征变换，把路由来的信息重新编码进更强的表示。因此，很多语义模式、组合规则与任务相关知识，最终都会沉淀到这些大规模参数化的 MLP 权重中。</p>
<p>更进一步说，在 Transformer 的常见解释框架里，MLP / FFN 往往被看作<span style="background-color: #c0c0c0;">事实性知识的重要载体之一</span>。一个常见直觉是把 FFN 看成参数化的“键值存储器（Key-Value Memory）”：第一层线性变换更像在检测当前输入是否匹配某种模式或概念，第二层线性变换则把与该模式相关的语义方向重新写回残差流（Residual Stream）。这里的残差流，可以理解为 Transformer 里那条贯穿各层的主表示通道：每一层注意力与 MLP 的输出，都会通过残差相加的方式写回这条主通道，再交给后续层继续处理。因此，诸如“实体—属性”“术语—定义”“模式—响应”这类较稳定的关联，常常更容易在 MLP 权重里留下痕迹。</p>
<p>但这并不意味着“一个神经元就存一条事实”。更准确的描述是：<span style="background-color: #c0c0c0;">知识通常以分布式方式存在于许多层、许多通道和许多参数方向里</span>。单个神经元有时会对某种关系或概念特别敏感，因而出现所谓“知识神经元（Knowledge Neurons）”现象；但更稳定的事实表示，通常仍然依赖一组共同激活的单元和跨层传递的表示。可以把两类子层的分工概括为：注意力更擅长“去哪里找、和谁建立联系”，MLP 更擅长“把匹配到的模式变成可供后续层使用的语义内容”。因此，说 MLP 是知识的主要载体之一是合理的；说知识只存在于 MLP、完全不在注意力里，则过于简单。</p>
<div class="blog_h2"><span class="graybg">激活函数</span></div>
<p>激活函数（Activation Function）的作用是给线性层引入非线性。如果没有激活函数，多层线性层叠起来仍然等价于一层线性变换，深度就失去了意义。</p>
<p>先给出一个实用的选型总表。它不代替后文的机制分析，但可以先回答工程上最常见的问题：某个激活函数通常应该放在输出层、隐藏层，还是特定结构里。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">激活函数</td>
<td style="text-align: center;">输出范围</td>
<td style="text-align: center;">典型适用场景</td>
<td style="text-align: center;">主要原因</td>
</tr>
</thead>
<tbody>
<tr>
<td>Sigmoid</td>
<td><span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span></td>
<td>二分类输出层；LSTM / GRU 门控</td>
<td>天然给出概率或开关强度，但隐藏层易饱和</td>
</tr>
<tr>
<td>Tanh</td>
<td><span displaypfx="inline-" class="mathjax-container">\((-1,1)\)</span></td>
<td>较浅网络；需要零中心有界激活的结构</td>
<td>比 sigmoid 更利于优化，但深层仍会饱和</td>
</tr>
<tr>
<td>ReLU</td>
<td><span displaypfx="inline-" class="mathjax-container">\([0,+\infty)\)</span></td>
<td>深层前馈网络、CNN 的默认隐藏层</td>
<td>不饱和、计算便宜、分段线性、优化稳定</td>
</tr>
<tr>
<td>Leaky ReLU</td>
<td><span displaypfx="inline-" class="mathjax-container">\((-\infty,+\infty)\)</span></td>
<td>担心 Dying ReLU 的深层隐藏层</td>
<td>保留 ReLU 优点，同时避免负半轴完全断梯度</td>
</tr>
<tr>
<td>ELU</td>
<td><span displaypfx="inline-" class="mathjax-container">\((-\alpha,+\infty)\)</span></td>
<td>希望激活更平滑、且均值更接近 0 的隐藏层</td>
<td>负区间平滑并可取负值，但计算开销高于 ReLU</td>
</tr>
<tr>
<td>GELU</td>
<td><span displaypfx="inline-" class="mathjax-container">\((-\infty,+\infty)\)</span></td>
<td>Transformer、BERT 类模型的隐藏层</td>
<td>选择性强且过渡平滑，兼顾表达能力与优化平滑性</td>
</tr>
<tr>
<td>Swish / SiLU</td>
<td><span displaypfx="inline-" class="mathjax-container">\((-\infty,+\infty)\)</span></td>
<td>现代卷积网络；部分大模型隐藏层</td>
<td>软门控、平滑、比硬截断更柔和</td>
</tr>
<tr>
<td>Softmax</td>
<td>各分量在 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 且总和为 1</td>
<td>多分类输出层；语言模型词表分布输出</td>
<td>把 logits 归一化为概率分布，不用于普通隐藏层</td>
</tr>
</tbody>
</table>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/mlp-activation-summary.png"><img class="alignnone size-full wp-image-41289" src="https://blog.gmem.cc/wp-content/uploads/2026/03/mlp-activation-summary.png" alt="mlp-activation-summary" width="1920" height="957" /></a></p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/activation-functions-grid.png"><img class="alignnone size-full wp-image-41461" src="https://blog.gmem.cc/wp-content/uploads/2026/03/activation-functions-grid.png" alt="activation-functions-grid" width="1920" height="1079" /></a></p>
<div class="blog_h3"><span class="graybg">Sigmoid</span></div>
<p>Sigmoid 把实数压到 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\sigma(x)=\frac{1}{1+e^{-x}}\]</span>
<p>Sigmoid 的优势在于输出天然落在 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span>，因此非常适合表示概率，尤其常用于二分类输出层或门控结构中。但它在隐藏层里的主要问题是饱和（saturation）：当输入绝对值较大时，函数会迅速贴近 0 或 1，此时导数接近 0，梯度在反向传播时会被不断压缩，深层网络因此容易出现梯度消失（Vanishing Gradient）。从优化角度看，这意味着前面层参数即使有误，也很难收到足够强的更新信号。</p>
<p>此外，sigmoid 的输出始终为正，不以 0 为中心，这会使后续层接收到带偏移的激活分布，通常不利于优化动态的稳定性。因此，sigmoid 今天更多保留在“需要概率解释”的输出层，或在 LSTM / GRU 等门控结构中充当开关函数，而不再是深层前馈隐藏层的默认选择。</p>
<div class="blog_h3"><span class="graybg">Tanh</span></div>
<p>Tanh 的输出范围是 <span displaypfx="inline-" class="mathjax-container">\((-1,1)\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}\]</span>
<p>Tanh 可以看作“零中心版 sigmoid”：它同样会在大幅度输入时饱和，但输出分布位于 <span displaypfx="inline-" class="mathjax-container">\((-1,1)\)</span> 且以 0 为中心，这通常比 sigmoid 更利于优化，因为后续层接收到的激活不再整体偏向正侧。对于需要表达“正负方向”差异的隐藏表示，tanh 往往也比 sigmoid 更自然。</p>
<p>不过，tanh 并没有解决饱和带来的根本问题：当 <span displaypfx="inline-" class="mathjax-container">\(|x|\)</span> 很大时，导数仍接近 0，深层网络中的梯度传播依然会变弱。因此，在较深的前馈网络里，tanh 通常不如 ReLU 家族稳定；它更多出现在较浅网络、早期神经网络设计，或某些希望激活有界且零中心的结构中。</p>
<div class="blog_h3"><span class="graybg">ReLU</span></div>
<p>ReLU（Rectified Linear Unit）定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{ReLU}(x)=\max(0,x)\]</span>
<p>ReLU 看起来几乎“过于简单”，但它之所以长期有效，关键不在公式花哨，而在于它同时满足了深层优化最需要的几条性质。第一，ReLU 在正半轴不饱和（non-saturating）：当 <span displaypfx="inline-" class="mathjax-container">\(x&gt;0\)</span> 时导数恒为 1，梯度穿过这一单元时不会像 sigmoid / tanh 那样被持续压小，因此更有利于深层网络中的梯度传播。第二，ReLU 保留了非线性，但正半轴仍是线性的，这使整个网络变成<span style="background-color: #c0c0c0;">分段线性（piecewise linear）</span>系统：表达能力足够强，同时局部优化形状又比高度弯曲的饱和函数更“规整”。第三，负半轴直接截断会带来自然的稀疏激活（sparse activation）：不是所有单元都会在每个样本上同时活跃，这通常有助于提升表示分解能力，并降低无效共适应（co-adaptation）。这里的共适应，指多个神经元在训练中形成了过强的相互依赖：某个单元之所以有效，不是因为它单独学到了稳定、可迁移的模式，而是因为它总是和另外几个特定单元“成套工作”。一旦输入分布变化，或其中某些单元没有按训练时那样响应，这种脆弱的协同关系就容易失效，从而削弱泛化能力。</p>
<p>从工程角度看，ReLU 的优势还包括计算代价极低，只需一次比较运算；这在大规模训练中会被成千上万层和数十亿次前向/反向传播放大。更深层的原因是：深度网络真正需要的不是“平滑得很漂亮”的激活函数，而是一个既能打破线性、又不会在大范围内把梯度压扁、还能让优化器容易工作的非线性。ReLU 恰好在这三点之间取得了非常实用的平衡，这就是为什么一个形式上极其朴素的函数，反而成为现代深度学习最成功的默认选择之一。它的代价也很明确：负区间梯度为 0，单元可能长期失活，这就是后面 Leaky ReLU、ELU、GELU 等变体继续改进的出发点。</p>
<div class="blog_h3"><span class="graybg">Leaky ReLU</span></div>
<p>Leaky ReLU 给负半轴保留一个很小的斜率：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{LeakyReLU}(x)=\max(\alpha x,x),\quad \alpha\ll 1\]</span>
<p>这样做是为了缓解“死亡 ReLU（Dying ReLU）”问题：若某神经元长期落在负区间，普通 ReLU 的梯度可能一直为 0，而 Leaky ReLU 仍保留一点更新信号。</p>
<p>从机制上看，Leaky ReLU 的核心改动很小，但很有针对性：它保留了 ReLU 在正半轴的不饱和与分段线性优点，同时避免把负半轴完全切断。这样即使某个单元暂时落入负区间，参数仍有机会通过非零梯度被重新拉回活跃状态。因此，Leaky ReLU 可以看作对 ReLU 的保守修正：表达风格几乎不变，但训练风险更低。</p>
<div class="blog_h3"><span class="graybg">ELU</span></div>
<p>ELU（Exponential Linear Unit）在负半轴使用指数平滑：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{ELU}(x)=\begin{cases}x,&amp;x&gt;0\\ \alpha(e^x-1),&amp;x\le 0\end{cases}\]</span>
<p>它的目标是兼顾 ReLU 的优化优势和负区间的平滑性，使激活均值更接近 0。</p>
<p>与 Leaky ReLU 相比，ELU 在负半轴不再是简单直线，而是用指数曲线平滑衰减到负饱和值。这带来两个效果：一是避免了 ReLU 在 0 点附近过于生硬的折线结构；二是允许激活出现稳定的负值，从而减轻隐藏表示整体偏正的问题。代价是计算比 ReLU 更重，且负区间在极小值处同样会逐渐饱和，因此它通常被视为“更平滑、更零中心”的 ReLU 变体，而不是彻底不同的一类激活。</p>
<div class="blog_h3"><span class="graybg">GELU</span></div>
<p>GELU（Gaussian Error Linear Unit）可理解为“按输入大小平滑地决定保留多少信号”。它不像 ReLU 那样硬截断，而是对小正值和小负值做连续、概率化的保留，因此在 0 附近更平滑。</p>
<p>这种设计的价值在于：它仍然保留了 ReLU 家族的选择性——不是所有信号都被同等对待——但又避免了硬截断带来的尖锐折点和完全失活区。于是，GELU 往往能在“表达选择性”和“优化平滑性”之间取得更好的折中，这也是它在 Transformer、BERT 及其后续大量变体中被广泛采用的重要原因。</p>
<div class="blog_h3"><span class="graybg">Swish / SiLU</span></div>
<p>Swish / SiLU 定义为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{SiLU}(x)=x\,\sigma(x)\]</span>
<p>它是平滑、非单调的激活函数，在某些深层网络里表现优于 ReLU。其结构可以直接读成“输入值 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 乘上一个 sigmoid 门控 <span displaypfx="inline-" class="mathjax-container">\(\sigma(x)\)</span>”：当 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 很大时， <span displaypfx="inline-" class="mathjax-container">\(\sigma(x)\approx 1\)</span>，信号几乎原样通过；当 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 很小时， <span displaypfx="inline-" class="mathjax-container">\(\sigma(x)\approx 0\)</span>，信号被显著压低；在 0 附近则是连续、平滑的软过渡。</p>
<p>这种形式的价值在于：它既不像 ReLU 那样做硬截断，也不像 sigmoid 那样把输出彻底压进固定区间，而是让网络学到一种“按输入强度自适应通过多少”的软门控机制。结果是，SiLU / Swish 往往能在保持优化平滑性的同时，保留较强的表达灵活性，因此在一些现代卷积网络与大模型变体中表现良好。它可以看作介于 ReLU 家族与门控激活之间的一种折中设计。</p>
<div class="blog_h3"><span class="graybg">Softmax</span></div>
<p>Softmax 把一组实数分数（scores）映射为概率分布（Probability Distribution）。在分类与语言模型里，这组分数通常称为 logit（Logits）：它们是 softmax 之前的未归一化输出。</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{softmax}(z)_i=\frac{e^{z_i}}{\sum_{j=1}^{V} e^{z_j}}\]</span>
<p>logits 的两个关键性质：</p>
<ul>
<li>logits 不需要是概率，可以是任意实数；softmax 才把它变成 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span> 且和为 1 的分布。</li>
<li>softmax 对整体平移不敏感：对任意常数 <span displaypfx="inline-" class="mathjax-container">\(c\)</span>，有 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{softmax}(z)=\mathrm{softmax}(z+c\mathbf{1})\)</span>。因此实现里常用 <span displaypfx="inline-" class="mathjax-container">\(z\leftarrow z-\max_i z_i\)</span> 做数值稳定（Numerical Stability）。</li>
</ul>
<p>这说明 softmax 真正关心的是各个 logit 之间的相对差值，而不是某个 logit 的绝对数值。若把所有分数同时加 10，模型对“哪一类更占优”的判断不会改变，因为指数项会在分子和分母里同时乘上 <span displaypfx="inline-" class="mathjax-container">\(e^{10}\)</span>，最终完全约掉。改变 softmax 输出的，是某个类别相对其他类别高了多少，而不是整体抬高或压低所有分数。</p>
<p>也正因为如此，logit 更适合理解为“未归一化偏好分数（unnormalized preference scores）”，而不是“概率雏形”。例如两类 logits 从 <span displaypfx="inline-" class="mathjax-container">\((1,2)\)</span> 变成 <span displaypfx="inline-" class="mathjax-container">\((101,102)\)</span>，softmax 输出完全相同；但若从 <span displaypfx="inline-" class="mathjax-container">\((1,2)\)</span> 变成 <span displaypfx="inline-" class="mathjax-container">\((1,5)\)</span>，第二类相对第一类的优势被显著拉大，概率才会明显变化。</p>
<p>在语言模型（Language Model）中，给定最后一层隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d_{\text{model}}}\)</span>，线性输出头产生词表大小 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 的 logits： <span displaypfx="inline-" class="mathjax-container">\(z=hW_{\text{vocab}}+b\)</span>，再经 softmax 得到下一个 token 的分布。若采用权重共享（Weight Tying），则输入嵌入表 <span displaypfx="inline-" class="mathjax-container">\(E\in\mathbb{R}^{V\;\times d_{\text{model}}}\)</span> 与输出头满足 <span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab}}=E^\top\)</span>，于是可直接写成 <span displaypfx="inline-" class="mathjax-container">\(z=hE^\top+b\)</span>。</p>
<div class="blog_h4"><span class="graybg">Softmax和分类任务</span></div>
<p>在多分类任务里，这几个概念实际上是一条连续的计算链：任务头先输出 logits <span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}^{C}\)</span>，softmax 把它们变成条件概率 <span displaypfx="inline-" class="mathjax-container">\(p(y=i|x)\)</span>，再取真实类别 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 的负对数概率作为单样本损失，也就是负对数似然（Negative Log-Likelihood, NLL）。</p>
<span displaypfx="" class="mathjax-container">\[p(y=i|x)=\mathrm{softmax}(z)_i=\frac{e^{z_i}}{\sum_{j=1}^{C}e^{z_j}},\qquad \ell_{\mathrm{NLL}}(z,c)=-\log p(y=c|x)\]</span>
<p>把两步合起来，NLL 可以直接写成 logits 的函数：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{NLL}}(z,c)=-\log\frac{e^{z_c}}{\sum_{j=1}^{C}e^{z_j}}=-z_c+\log\sum_{j=1}^{C}e^{z_j}\]</span>
<p>这个式子把训练目标拆成了两部分：第一项 <span displaypfx="inline-" class="mathjax-container">\(-z_c\)</span> 要求真实类别的 logit 足够大；第二项 <span displaypfx="inline-" class="mathjax-container">\(\log\sum_j e^{z_j}\)</span> 是归一化项（log-sum-exp），它把所有类别的竞争都算进去。因此训练并不是单独把正确类别分数抬高，而是要让它相对其他类别更占优势。</p>
<p>softmax 的平移不变性（Translation Invariance）在这里也能直接看见。对任意常数 <span displaypfx="inline-" class="mathjax-container">\(a\)</span>，若把所有 logits 同时改为 <span displaypfx="inline-" class="mathjax-container">\(z+a\mathbf{1}\)</span>，则 softmax 概率不变，NLL 也不变：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{softmax}(z+a\mathbf{1})_i=\mathrm{softmax}(z)_i,\qquad \ell_{\mathrm{NLL}}(z+a\mathbf{1},c)=\ell_{\mathrm{NLL}}(z,c)\]</span>
<p>因此 logits 的绝对零点没有意义，真正有意义的是类别之间的相对差值。工程实现里常把 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 先整体减去 <span displaypfx="inline-" class="mathjax-container">\(\max_i z_i\)</span>，再计算 softmax 或 log-sum-exp。这样不会改变概率与损失，但能显著降低指数溢出的风险。</p>
<div class="blog_h2"><span class="graybg">损失函数</span></div>
<p>术语区分：假设函数（Hypothesis Function）/模型 <span displaypfx="inline-" class="mathjax-container">\(f_\theta\)</span> 定义“模型在做什么映射”；样本损失（Loss Function）定义在单样本上；代价函数/成本函数（Cost Function）是把样本损失在全数据集上做平均或求和后的经验风险；目标函数（Objective Function）是优化器真正要优化的函数，最常见写法是 <span displaypfx="inline-" class="mathjax-container">\(J(\theta)=L(\theta)+\lambda\Omega(\theta)\)</span>。</p>
<div class="blog_h3"><span class="graybg">回归损失</span></div>
<div class="blog_h4"><span class="graybg">MSE</span></div>
<p>均方误差（Mean Squared Error, MSE）定义为</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{MSE}}(y,\hat y)=(\hat y-y)^2\]</span>
<p>平方的作用是让大误差被放大处罚。若一个样本错 10，另一个样本错 1，那么前者在 MSE 里不是“重 10 倍”，而是“重 100 倍”。因此 MSE 很适合你明确希望重罚大错的场景，也对应前面讲过的高斯噪声假设。</p>
<div class="blog_h4"><span class="graybg">MAE</span></div>
<p>平均绝对误差（Mean Absolute Error, MAE）对应单样本形式</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{MAE}}(y,\hat y)=|\hat y-y|\]</span>
<p>它直接度量偏差大小，对离群点更鲁棒，因为不会像平方那样把大误差急剧放大。若做房价预测，数据中存在一批价格远高于主体分布的豪宅样本时，MAE 往往比 MSE 更稳；这里的“主体分布”指的是样本中占大多数的普通住宅价格区间，而豪宅样本相对它明显偏高。这样一来，哪怕豪宅样本数量不多，它们也会在 MSE 下因为误差被平方而获得过大的影响力。</p>
<div class="blog_h4"><span class="graybg">Huber Loss</span></div>
<p>Huber Loss 结合了 MSE 与 MAE：误差小时像平方误差，误差大时像绝对误差。设阈值 <span displaypfx="inline-" class="mathjax-container">\(\delta\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\ell_\delta(r)=\begin{cases}\frac{1}{2}r^2,&amp;|r|\le \delta\\ \delta(|r|-\frac{1}{2}\delta),&amp;|r|&gt;\delta\end{cases},\quad r=\hat y-y\]</span>
<p>这条式子的直觉是：小误差区间内保持平滑、便于优化；大误差区间内降低对离群点的过度敏感。它像“正常误差严肃处理，极端异常别让它一票否决整个模型”。</p>
<div class="blog_h3"><span class="graybg">分类损失</span></div>
<div class="blog_h4"><span class="graybg">交叉熵损失（Binary）</span></div>
<p>二分类交叉熵（Binary Cross-Entropy, BCE）用来训练输出概率 <span displaypfx="inline-" class="mathjax-container">\(p\in(0,1)\)</span> 的二分类器。这里 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 通常表示模型预测“正类”的概率， <span displaypfx="inline-" class="mathjax-container">\(y\in\{0,1\}\)</span> 是真实标签： <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 表示正类， <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span> 表示负类。</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{BCE}}(y,p)=-\Big(y\log p+(1-y)\log(1-p)\Big)\]</span>
<p>左边的 <span displaypfx="inline-" class="mathjax-container">\(\ell_{\mathrm{BCE}}(y,p)\)</span> 表示“单个样本在真实标签为 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>、模型预测正类概率为 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 时的 BCE 损失值”。也就是说，这不是新的概率，而是一个标量惩罚：预测越符合真实标签，它越小；预测越违背真实标签，它越大。</p>
<p>这条公式会根据 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 的取值自动选择应当惩罚哪一项。当 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 时，式中的 <span displaypfx="inline-" class="mathjax-container">\((1-y)=0\)</span>，因此第二项消失，损失化简为 <span displaypfx="inline-" class="mathjax-container">\(-\log p\)</span>；当 <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span> 时，第一项中的 <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span>，因此第一项消失，损失化简为 <span displaypfx="inline-" class="mathjax-container">\(-\log(1-p)\)</span>。于是它惩罚的本质就是：让真实类别对应的概率尽可能高。</p>
<p>数值例子（自然对数）：若 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 且 <span displaypfx="inline-" class="mathjax-container">\(p=0.9\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\ell\approx 0.105\)</span>；若 <span displaypfx="inline-" class="mathjax-container">\(p=0.1\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\ell\approx 2.303\)</span>。正确但不自信会被罚，错误且自信会被重罚。</p>
<div class="blog_h4"><span class="graybg">交叉熵损失（Categorical）</span></div>
<p>多分类交叉熵（Categorical Cross-Entropy, CE）与 softmax 通常配套使用。这里 <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 是真实分布（Ground-Truth Distribution）在第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 类上的概率质量， <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 是模型预测分布（Predicted Distribution）在第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 类上的概率。若类别总数为 <span displaypfx="inline-" class="mathjax-container">\(C\)</span>，则单样本损失写成：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{CE}}(y,p)=-\sum_{i=1}^{C} y_i\log p_i\]</span>
<p>左边的 <span displaypfx="inline-" class="mathjax-container">\(\ell_{\mathrm{CE}}(y,p)\)</span> 表示“当真实标签分布为 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>、模型预测分布为 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 时，这个样本对应的交叉熵损失值”。其本质是：<span style="background-color: #c0c0c0;">用真实标签分布 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 作为权重，对预测分布的负对数概率 <span displaypfx="inline-" class="mathjax-container">\(-\log p_i\)</span> 做加权平均</span>。</p>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是 one-hot 分布时，只有真实类别 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 那一维的权重为 1，其余维度权重都为 0，因此求和会自动塌缩成单项：</p>
<span displaypfx="" class="mathjax-container">\[\ell=-\log p_c\]</span>
<p>这正是分类任务里最常见的形式。它与最大似然估计（Maximum Likelihood Estimation, MLE）完全一致：最小化交叉熵等价于最大化真实类别的对数似然。</p>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 不再是 one-hot，而是一个在多个类别上分配了非零概率质量的分布，那么交叉熵就不再只读取单个类别 <span displaypfx="inline-" class="mathjax-container">\(p_c\)</span>，而是会对整条标签分布做加权。常见来源有三类。</p>
<p>第一类是软标签（Soft Label）。它指真实监督信号本身就是一个概率分布，而不是“只有一个绝对正确类别”的硬标签（Hard Label）。例如一张图像可能被标注为“70% 像猫、30% 像狐狸”，或一个样本本身就带有多标注者投票汇总后的类别分布。在这种情况下， <span displaypfx="inline-" class="mathjax-container">\(y_i\)</span> 直接表示第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 类的目标概率，交叉熵自然要对所有类别一起计算。</p>
<p>第二类是标签平滑（Label Smoothing）。它是一种正则化技术：原本 one-hot 标签会把真实类别的目标概率设为 1，其余类别全设为 0；标签平滑则故意把这件事放松一些，例如对 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 分类问题，取一个很小的 <span displaypfx="inline-" class="mathjax-container">\(\varepsilon\in(0,1)\)</span>，把目标分布改写为：</p>
<span displaypfx="" class="mathjax-container">\[y_i^{\mathrm{LS}}=(1-\varepsilon)\mathbf{1}[i=c]+\frac{\varepsilon}{C}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 是真实类别， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}[i=c]\)</span> 是指示函数：当 <span displaypfx="inline-" class="mathjax-container">\(i=c\)</span> 时取 1，否则取 0。这样做的效果是：真实类别仍占最大权重，但其他类别也获得一小部分概率质量。它可以抑制模型过度自信，使输出分布更平滑，并在一定程度上改善泛化与校准。</p>
<p>第三类是教师分布蒸馏（Knowledge Distillation from Teacher Distribution）。知识蒸馏（Knowledge Distillation）的做法是：不用人工标签单独监督学生模型（Student Model），而是让学生去拟合教师模型（Teacher Model）给出的类别分布。若教师在某个样本上输出 <span displaypfx="inline-" class="mathjax-container">\(q\)</span>，学生输出 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>，则训练目标常包含 <span displaypfx="inline-" class="mathjax-container">\(-\sum_i q_i\log p_i\)</span> 这样的交叉熵或等价的 KL 散度项。它传递的不只是“哪一类是对的”，还传递“其余类别分别有多像”，因此常被称为暗知识（Dark Knowledge）。</p>
<p>这三种情况的共同点是：标签本身已经不是单点答案，而是一条分布。于是交叉熵的计算对象就不再只是“真实类别那一项”，而是整个目标分布与预测分布之间的匹配程度。</p>
<p>从信息论角度看，交叉熵的标准定义是两个分布 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 之间的</p>
<span displaypfx="" class="mathjax-container">\[H(P,Q)=-\sum_x P(x)\log Q(x)\]</span>
<p>分类里的 <span displaypfx="inline-" class="mathjax-container">\(\ell_{\mathrm{CE}}(y,p)\)</span> 与这个定义并不矛盾，它只是把信息论中的 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 分别具体化成了“单个样本对应的真实标签分布 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>”与“模型在这个样本上的预测分布 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>”。若标签是 one-hot，那么 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 退化成一个只在真实类别处取值为 1 的离散分布，于是信息论里的交叉熵自然退化成 <span displaypfx="inline-" class="mathjax-container">\(-\log p_c\)</span>。</p>
<p>进一步地，交叉熵与 KL 散度（Kullback–Leibler Divergence）的关系可以直接写成：</p>
<span displaypfx="" class="mathjax-container">\[H(P,Q)=H(P)+D_{\mathrm{KL}}(P\|Q)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(H(P)=-\sum_x P(x)\log P(x)\)</span> 是真实分布 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 自身的熵（Entropy），只由数据分布决定； <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(P\|Q)=\sum_x P(x)\log\frac{P(x)}{Q(x)}\)</span> 是 KL 散度，用来衡量预测分布 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 相对真实分布 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 的偏离程度。于是，在训练数据固定时， <span displaypfx="inline-" class="mathjax-container">\(H(P)\)</span> 是常数，最小化交叉熵 <span displaypfx="inline-" class="mathjax-container">\(H(P,Q)\)</span> 就等价于最小化 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(P\|Q)\)</span>。</p>
<p>因此，“最小化交叉熵就是最小化 KL 散度”这句话在监督学习里通常成立，但更准确的表述是：<span style="background-color: #c0c0c0;">在真实分布固定不变时，最小化交叉熵与最小化预测分布对真实分布的 KL 偏离是等价的</span>。KL 散度确实可以理解为“与真实分布的差异或偏离”，但它不是对称距离（Symmetric Distance）：一般有 <span displaypfx="inline-" class="mathjax-container">\(D_{\mathrm{KL}}(P\|Q) \neq D_{\mathrm{KL}}(Q\|P)\)</span>，也不满足严格距离函数的三角不等式，因此更准确的名称是分布失配（distribution mismatch）或相对熵（relative entropy）。</p>
<p>若进一步把 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 写成 softmax 作用在 logits <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 上，则多分类交叉熵可直接写成：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{CE}}(z,c)=-\log\mathrm{softmax}(z)_c=-z_c+\log\sum_{j=1}^{C}e^{z_j}\]</span>
<p>这条式子把任务头和损失函数直接接起来：线性头先输出 logits，softmax 把它们归一化为概率，交叉熵再读取真实类别的负对数概率。由于 softmax 具有平移不变性，给所有 logits 同时加上同一个常数不会改变这个损失，因此实现中通常直接从 logits 计算交叉熵（log-sum-exp 形式），并先减去 <span displaypfx="inline-" class="mathjax-container">\(\max_j z_j\)</span> 做数值稳定，而不是显式先算 softmax 再取 log。</p>
<div class="blog_h4"><span class="graybg">Focal Loss</span></div>
<p>Focal Loss 常用于类别极不平衡的分类任务，尤其是目标检测（Object Detection）这类“负样本远多于正样本”的场景。它不是简单换掉交叉熵，而是在交叉熵前再乘一个与样本难度相关的调制因子，使已经分得很对的容易样本贡献变小，把梯度预算更多留给困难样本：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{focal}}(p_t)=-(1-p_t)^\gamma\log p_t\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(p_t\)</span> 表示“真实类别对应的预测概率”：若真实标签 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(p_t=p\)</span>；若 <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(p_t=1-p\)</span>。因此 <span displaypfx="inline-" class="mathjax-container">\(-\log p_t\)</span> 就是普通交叉熵，而前面的 <span displaypfx="inline-" class="mathjax-container">\((1-p_t)^\gamma\)</span> 是额外加上的难度调制项。参数 <span displaypfx="inline-" class="mathjax-container">\(\gamma\ge 0\)</span> 控制聚焦强度： <span displaypfx="inline-" class="mathjax-container">\(\gamma=0\)</span> 时，Focal Loss 退化回普通交叉熵； <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 越大，对容易样本的压低越明显。</p>
<p>这个机制的关键在于样本难度如何反映到权重上。若某个样本已经分得很对，例如真实类别概率 <span displaypfx="inline-" class="mathjax-container">\(p_t=0.99\)</span>，则调制因子 <span displaypfx="inline-" class="mathjax-container">\((1-p_t)^\gamma\)</span> 会非常小；以 <span displaypfx="inline-" class="mathjax-container">\(\gamma=2\)</span> 为例，权重大约只有 <span displaypfx="inline-" class="mathjax-container">\((0.01)^2=10^{-4}\)</span>，这意味着它对总损失和梯度的影响被大幅削弱。相反，若某个样本很难，例如 <span displaypfx="inline-" class="mathjax-container">\(p_t=0.2\)</span>，则权重约为 <span displaypfx="inline-" class="mathjax-container">\((0.8)^2=0.64\)</span>，其损失会被较大程度保留。于是训练过程不再被海量“早就分对的简单样本”主导，而会持续关注误分样本、边界样本和少数类样本。</p>
<p>目标检测是最典型的应用例子。以单阶段检测器（One-stage Detector）为例，一张图像上往往有成千上万个候选框（Anchors），但真正包含目标的正样本只占极少数；绝大多数候选框都是背景。若直接使用普通交叉熵，训练会被这些“背景且容易判断”的负样本淹没：它们单个损失虽小，但数量太多，累积后仍然主导梯度。Focal Loss 的作用正是把这批容易背景样本的权重压下去，让模型把更多注意力放在少数正样本、遮挡目标、边界模糊目标，以及那些看起来像目标但其实是背景的困难负样本上。这样做通常会显著改善长尾检测与前景-背景极不平衡时的训练效果。</p>
<div class="blog_h4"><span class="graybg">Hinge Loss</span></div>
<p>Hinge Loss 是 SVM 常用的分类损失：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{hinge}}(y,f)=\max(0,1-yf),\quad y\in\{-1,+1\}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(f\)</span> 是模型分数而不是概率。当 <span displaypfx="inline-" class="mathjax-container">\(yf\ge 1\)</span> 时，说明不仅分类正确，而且留出了足够间隔，损失为 0；当 <span displaypfx="inline-" class="mathjax-container">\(yf&lt;1\)</span> 时，就要受罚。它强调的不只是“分对”，而是“分对且留有安全距离”。</p>
<div class="blog_h3"><span class="graybg">度量学习损失</span></div>
<p>度量学习（Metric Learning）不直接预测类别，而是学习一个表示空间，让“应该相似的样本靠近，不该相似的样本拉远”。这类损失在检索、人脸识别、推荐召回和 embedding 学习中非常常见。</p>
<div class="blog_h4"><span class="graybg">Contrastive Loss</span></div>
<p>Contrastive Loss 处理样本对（pair）。若一对样本应相似，则拉近它们；若应不同，则至少推开到某个间隔 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 之外：</p>
<span displaypfx="" class="mathjax-container">\[\ell=y\,d^2+(1-y)\max(0,m-d)^2\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 是两者在嵌入空间中的距离， <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 表示正对， <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span> 表示负对。它像“朋友要坐得近，陌生人至少别挤在一起”。</p>
<div class="blog_h4"><span class="graybg">Triplet Loss</span></div>
<p>Triplet Loss 使用三元组：锚点（Anchor）、正样本（Positive）、负样本（Negative）。目标是让锚点离正样本比离负样本更近至少一个 margin：</p>
<span displaypfx="" class="mathjax-container">\[\ell=\max\big(0,\ d(a,p)-d(a,n)+m\big)\]</span>
<p>这条式子表达的是一种相对排序约束，而不是绝对相似度分数。它非常适合“谁比谁更像”的任务，例如人脸验证：同一个人的两张照片应比不同人的照片更近。</p>
<div class="blog_h4"><span class="graybg">InfoNCE</span></div>
<p>InfoNCE 是现代对比学习最常见的损失之一。对一个锚点来说，它把正样本放进一堆候选里，要求模型把正样本打分最高：</p>
<span displaypfx="" class="mathjax-container">\[-\log \frac{\exp(\mathrm{sim}(z_i,z_i^+)/\tau)}{\sum_j \exp(\mathrm{sim}(z_i,z_j)/\tau)}\]</span>
<p>分子是正确配对，分母是所有候选。这个结构和 softmax 分类非常像，只不过类别不再是固定标签，而是“在一堆候选里，谁才是真正匹配的那个”。在大语言模型 embedding、图像表征学习和多模态对齐里，它几乎是标准配置。</p>
<div class="blog_h2"><span class="graybg">任务头（Task Head）</span></div>
<p>任务头（Task Head）是把主干网络（Backbone）产出的隐藏表示（Hidden Representation）映射到具体任务输出空间的模块。主干负责抽取通用特征，任务头负责把特征“读出来”并对齐到目标形式（类别、数值、序列标签、跨度、关系等）。在工程上，绝大多数“用 Transformer 做下游任务”都可以写成：<span style="background-color: #c0c0c0;">Transformer backbone + task head + task loss</span>。</p>
<div class="blog_h3"><span class="graybg">中间表示、logits 与任务头的关系</span></div>
<p>主干网络输出的隐藏表示（Hidden Representation）是任务头的输入，任务头则是把这种内部表示读成具体任务输出的最后一层或最后几层变换。若把主干输出记为 <span displaypfx="inline-" class="mathjax-container">\(h\)</span>、<span displaypfx="inline-" class="mathjax-container">\(H\)</span> 或更一般的张量 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{H}\)</span>，则任务头通常先做一次线性读出（Linear Readout）得到分数，再视任务类型决定是否接 sigmoid、softmax、CRF 解码或直接保留实数输出。</p>
<p>logits 就是在这个读出阶段最常见的中间产物。它们是<span style="background-color: #c0c0c0;">任务头输出、但尚未归一化或尚未解码的原始分数</span>。例如，多分类头常先产生 <span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}^{C}\)</span>，这里 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 是类别数， <span displaypfx="inline-" class="mathjax-container">\(z_c\)</span> 表示模型对第 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 类的偏好分数；softmax 之后这些分数才变成概率。对 token 分类任务，task head 产生的不是单个向量，而是一整张 logits 矩阵 <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{L\times C}\)</span>；对语言模型，输出则是词表 logits <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{L\times V}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 是词表大小。</p>
<p>因此，关系可以概括为：<span style="background-color: #c0c0c0;">输入先被 backbone 编码成中间表示，中间表示再被 task head 读成 logits 或其他任务分数，最后再由概率映射、解码器或损失函数把这些分数变成最终预测</span>。logits 不是所有中间表示的统称，而是“距离最终任务输出只差一步”的那类任务分数；它们通常由任务头生成，而不是由主干网络中间每一层都显式生成。</p>
<div class="blog_h3"><span class="graybg">任务头输出对照表</span></div>
<p>不同任务头的差异，最核心地体现在“直接输出什么张量、这些张量后面还要经过什么处理”这两个问题上。下面这张表把常见任务头的输入形状、直接输出和后续处理并列起来，便于从工程实现角度快速对照。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">任务类型</td>
<td style="text-align: center;">任务头常见输入</td>
<td style="text-align: center;">任务头直接输出</td>
<td style="text-align: center;">后续处理</td>
</tr>
</thead>
<tbody>
<tr>
<td>二分类</td>
<td>单样本表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span></td>
<td>标量 logit <span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}\)</span>，或二维 logits <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\in\mathbb{R}^{2}\)</span></td>
<td>sigmoid 或 softmax，得到类别概率</td>
</tr>
<tr>
<td>多分类</td>
<td>单样本表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span></td>
<td>类别 logits 向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\in\mathbb{R}^{C}\)</span></td>
<td>softmax 后得到 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 类概率</td>
</tr>
<tr>
<td>多标签分类</td>
<td>单样本表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span></td>
<td>每个标签一个 logit，组成 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\in\mathbb{R}^{C}\)</span></td>
<td>对每一维独立做 sigmoid，而不是在类别间做 softmax</td>
</tr>
<tr>
<td>回归</td>
<td>单样本表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span></td>
<td>实数或向量 <span displaypfx="inline-" class="mathjax-container">\(\hat{\mathbf{y}}\in\mathbb{R}^{m}\)</span></td>
<td>通常不做概率归一化，直接配合回归损失</td>
</tr>
<tr>
<td>Token 分类 / NER</td>
<td>序列表示 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\times d}\)</span></td>
<td>token-level logits 矩阵 <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{L\times C}\)</span></td>
<td>逐 token softmax，或接 CRF 做全局解码</td>
</tr>
<tr>
<td>语言模型 / 文本生成</td>
<td>序列表示 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\times d}\)</span></td>
<td>词表 logits <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{L\times V}\)</span></td>
<td>对每个位置在词表维做 softmax，得到 next-token 分布</td>
</tr>
<tr>
<td>跨度抽取（Span Extraction）</td>
<td>序列表示 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\times d}\)</span></td>
<td>起点 logits <span displaypfx="inline-" class="mathjax-container">\(\mathbf{a}\in\mathbb{R}^{L}\)</span> 与终点 logits <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\in\mathbb{R}^{L}\)</span>，或 span 分数矩阵</td>
<td>在起止位置上做 softmax 或联合评分，输出片段边界</td>
</tr>
<tr>
<td>依存句法 / 关系抽取</td>
<td>成对表示 <span displaypfx="inline-" class="mathjax-container">\(h_i,h_j\)</span> 或序列表示 <span displaypfx="inline-" class="mathjax-container">\(H\)</span></td>
<td>边分数矩阵 <span displaypfx="inline-" class="mathjax-container">\(S\in\mathbb{R}^{L\times L}\)</span>，或关系分数张量 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{S}\in\mathbb{R}^{L\times L\times C}\)</span></td>
<td>argmax、biaffine 解码或图结构约束解码</td>
</tr>
<tr>
<td>度量学习 / 检索</td>
<td>单样本表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span></td>
<td>embedding 向量 <span displaypfx="inline-" class="mathjax-container">\(e\in\mathbb{R}^{d'}\)</span></td>
<td>不直接输出 logits；后续用相似度函数或对比损失比较</td>
</tr>
</tbody>
</table>
<p>这张表的关键在于区分“任务头直接输出什么”和“用户最终看到什么”。很多任务头直接输出的并不是概率，也不是标签，而是 logits、边分数、起止位置分数或 embedding。概率、标签、生成 token、依存边、异常分数等最终结果，通常还需要经过归一化、解码、阈值化或搜索过程才能得到。</p>
<div class="blog_h3"><span class="graybg">分类头</span></div>
<p>分类头（Classification Head）的核心职责，是把主干网络输出的表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span> 变成类别分数（Class Scores）或 logits。最常见的做法是一层线性映射：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{z}=W\mathbf{h}+\mathbf{b}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{h}\in\mathbb{R}^{d}\)</span> 是单个样本的隐藏表示， <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 是任务头权重矩阵， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{b}\)</span> 是偏置向量， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 是未归一化类别分数。分类任务的关键区别不在于“有没有线性层”，而在于：<span style="background-color: #c0c0c0;">输出空间是否互斥、每个样本允许几个标签成立、以及这些分数之后接什么归一化与损失</span>。</p>
<p>直觉上，这个线性头就是一个“可学习的读出（Readout）”：在高维表示空间里用超平面（Hyperplane）切分区域，或用线性映射把表示投影到目标坐标系。这里的“读出”指的是：主干网络先把输入编码成内部表示，而任务头再把这种内部表示转换成模型真正需要输出的量，例如类别 logits、词表 logits、回归值、span 分数或关系分数。换言之，读出不是“再提特征”，而是<span style="background-color: #c0c0c0;">把已经形成的表示翻译成任务空间中的可判定分数</span>。</p>
<div class="blog_h4"><span class="graybg">二分类</span></div>
<p>二分类（Binary Classification）要求每个样本只在两个互斥类别中选一个，例如“垃圾 / 非垃圾”“欺诈 / 正常”“阳性 / 阴性”。最常见的写法是输出一个标量 logit：</p>
<span displaypfx="" class="mathjax-container">\[z=\mathbf{w}^\top \mathbf{h}+b\]</span>
<p>然后通过 sigmoid 得到正类概率：</p>
<span displaypfx="" class="mathjax-container">\[p=P(y=1\mid x)=\sigma(z)=\frac{1}{1+e^{-z}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 是模型对正类的原始偏好分数， <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是正类概率，负类概率则是 <span displaypfx="inline-" class="mathjax-container">\(1-p\)</span>。训练时通常配合二分类交叉熵（Binary Cross-Entropy, BCE）。工程上也可以输出二维 logits <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\in\mathbb{R}^{2}\)</span>，再接 softmax；但若任务确实只有两个互斥类别，单 logit + sigmoid 更常见，也更经济。</p>
<div class="blog_h4"><span class="graybg">多分类</span></div>
<p>多分类（Multi-class Classification）要求每个样本在 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 个互斥类别中恰好选一个类别，例如“猫 / 狗 / 鸟”“体育 / 财经 / 科技 / 娱乐”。此时任务头不会只输出一个标量，而是输出长度为 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 的 logits 向量：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{z}=W\mathbf{h}+\mathbf{b},\qquad W\in\mathbb{R}^{C\times d},\ \mathbf{z}\in\mathbb{R}^{C}\]</span>
<p>其中第 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 维 <span displaypfx="inline-" class="mathjax-container">\(z_c\)</span> 表示模型对第 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 个类别的原始偏好分数。由于这些类别互斥，后续通常接 softmax，把整组分数归一化成概率分布：</p>
<span displaypfx="" class="mathjax-container">\[p(y=c\mid x)=\frac{e^{z_c}}{\sum_{j=1}^{C}e^{z_j}}\]</span>
<p>这条式子的含义很直接：分子 <span displaypfx="inline-" class="mathjax-container">\(e^{z_c}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 类的相对强度，分母 <span displaypfx="inline-" class="mathjax-container">\(\sum_{j=1}^{C}e^{z_j}\)</span> 把所有类别一起归一化，因此最终得到的 <span displaypfx="inline-" class="mathjax-container">\(p(y=c\mid x)\)</span> 落在 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 之间，且所有类别概率和为 1。也正因为总和必须为 1，多分类头天然表达的是“类间竞争”：某一类概率上升，其他类的总概率就必须下降。</p>
<p>训练时通常直接把 logits <span displaypfx="inline-" class="mathjax-container">\(\mathbf{z}\)</span> 输入多分类交叉熵（Cross-Entropy）损失，而不是手动先算 softmax 再取对数。这样做的原因是数值稳定：损失函数内部会把 softmax 与对数合并成 log-sum-exp 形式，避免指数溢出。推理时若只关心类别标签，直接取 <span displaypfx="inline-" class="mathjax-container">\(\arg\max_c z_c\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(\arg\max_c p(y=c\mid x)\)</span> 即可；若需要概率、阈值或校准，再显式使用 softmax。</p>
<div class="blog_h4"><span class="graybg">多标签分类</span></div>
<p>多标签分类（Multi-label Classification）与多分类名字相近，但任务结构完全不同。它不是“从多个类里选一个”，而是<span style="background-color: #c0c0c0;">同一个样本可以同时拥有多个标签</span>。例如一篇文章可以同时属于“AI、NLP、Transformer”，一张图片可以同时打上“室内、人物、宠物”三个标签。</p>
<p>这种任务里，标签之间不再互斥，因此不能使用 softmax。若仍用 softmax，所有标签概率会被强制归一化为和 1，相当于错误地假设“只能有一个标签成立”。多标签头通常输出 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 个 logits：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{z}=W\mathbf{h}+\mathbf{b},\qquad \mathbf{z}\in\mathbb{R}^{C}\]</span>
<p>但后续不是做一次整体 softmax，而是对每一维独立做 sigmoid：</p>
<span displaypfx="" class="mathjax-container">\[p_i=\sigma(z_i),\qquad i=1,\dots,C\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 表示“第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个标签是否成立”的独立概率。训练时通常使用逐维二分类交叉熵，即把每个标签都当作一个独立的二分类问题，再在标签维上求和或取平均：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{multi\mbox{-}label}}=-\sum_{i=1}^{C}\Big(y_i\log p_i+(1-y_i)\log(1-p_i)\Big)\]</span>
<p>因此，多标签头的关键不是“输出维度也叫 <span displaypfx="inline-" class="mathjax-container">\(C\)</span>”，而是<span style="background-color: #c0c0c0;">这 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 维之间不竞争，每一维都在独立回答一个 yes/no 问题</span>。推理时也不是取单个 argmax，而是对每一维做阈值判断，例如输出所有满足 <span displaypfx="inline-" class="mathjax-container">\(p_i&gt;0.5\)</span> 的标签，或按业务分别设定不同标签阈值。</p>
<div class="blog_h3"><span class="graybg">回归头</span></div>
<p>回归头（Regression Head）不负责在离散类别之间做判别，而是直接输出连续数值（Continuous Value）或连续向量。最常见的形式仍然是一层线性映射：</p>
<span displaypfx="" class="mathjax-container">\[\hat{\mathbf{y}}=W\mathbf{h}+\mathbf{b},\qquad \hat{\mathbf{y}}\in\mathbb{R}^{m}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 是回归目标维度。若 <span displaypfx="inline-" class="mathjax-container">\(m=1\)</span>，就是标量回归，例如房价预测、评分预测、温度预测；若 <span displaypfx="inline-" class="mathjax-container">\(m&gt;1\)</span>，则是多维回归，例如边界框坐标回归、姿态参数回归或多目标数值预测。</p>
<p>回归头通常不接 softmax，也不强制输出落在 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span>。原因很简单：回归任务关心的是数值大小本身，而不是类别概率竞争。训练时常配合均方误差（MSE）、平均绝对误差（MAE）或 Huber Loss。只有当目标值本身有明确范围约束时，才会额外接 sigmoid、tanh 或其他变换，把输出压到指定区间。</p>
<div class="blog_h3"><span class="graybg">语言模型头（LM Head）</span></div>
<p>语言模型头（Language Modeling Head, LM Head）是把隐藏表示映射回词表空间的输出头。只要任务目标是“在若干位置上对词表中的 token 做预测”，就会出现这一类头；因此它不只存在于 Decoder-only 大模型，也存在于 Encoder-only 的掩码语言模型（Masked Language Model, MLM）以及 Encoder-Decoder 的生成端。它读取主干网络在每个位置输出的隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\;\times d_{\text{model}}}\)</span>，并把每个位置的表示投影到整张词表空间，得到词表 logits：</p>
<span displaypfx="" class="mathjax-container">\[Z = HW_{\text{vocab}} + \mathbf{1}b^\top,\quad W_{\text{vocab}}\in\mathbb{R}^{d_{\text{model}}\;\times {V}},\ Z\in\mathbb{R}^{L\;\times {V}}\]</span>
<p>这条式子先描述整体矩阵，再自然落到单个位置。这里 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 是序列长度，表示当前一共有多少个位置； <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 是隐藏维度； <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 是词表大小（Vocabulary Size）。因此，隐藏状态矩阵 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\;\times d_{\text{model}}}\)</span> 的每一行对应一个位置的表示，输出权重矩阵 <span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab}}\in\mathbb{R}^{d_{\text{model}}\;\times {V}}\)</span> 的每一列对应“词表中某个 token 作为候选答案时的读出方向”，最终得到的 <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{L\;\times {V}}\)</span> 就是一个“位置 <span displaypfx="inline-" class="mathjax-container">\(\times\)</span> 词表”的打分表：行表示位置，列表示候选 token。</p>
<p>把这张打分表聚焦到第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 行，就得到 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,:}\in\mathbb{R}^{V}\)</span>，也就是“第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置对整张词表所有 token 的一整行 logits”。这里 <span displaypfx="inline-" class="mathjax-container">\(H_t\in\mathbb{R}^{d_{\text{model}}}\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置的隐藏状态，冒号 <span displaypfx="inline-" class="mathjax-container">\(:\)</span> 表示该行的全部列；若写成 <span displaypfx="inline-" class="mathjax-container">\(Z_{:,i}\)</span>，则表示第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 列，也就是“所有位置对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token 的分数”。继续缩小到单个元素 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,i}\)</span>，它表示“在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置，把词表中第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token 作为下一个输出时的原始分数”。</p>
<p>把矩阵形式按单个位置、单个候选 token 展开后，打分可写成：</p>
<span displaypfx="" class="mathjax-container">\[Z_{t,i}=H_t\cdot W_{\text{vocab},:,i}+b_i\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab},:,i}\)</span> 表示输出权重矩阵的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 列，也就是与词表第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token 对应的参数向量； <span displaypfx="inline-" class="mathjax-container">\(b_i\)</span> 是该 token 的偏置项。这个公式的读法是：拿第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置的隐藏表示 <span displaypfx="inline-" class="mathjax-container">\(H_t\)</span>，与“token <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 的读出向量”做一次点积，再加偏置，就得到该 token 在该位置的 logit。</p>
<p>LM Head 与分类头的根本区别在于输出空间。普通分类头通常只需输出 <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 个类别分数；LM Head 则要在每个位置输出 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 个分数，而 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 往往达到几万甚至几十万。因此，LM Head 本质上是“逐位置的大规模多分类器”：每一步都在问“下一个 token 应该是词表中的哪一个”。</p>
<p>同一个 LM Head 公式，在不同 Transformer 架构里的使用方式并不相同。对 Encoder-only 模型，LM Head 通常服务于掩码语言建模：模型先用双向注意力得到各位置隐藏状态，再只在被遮蔽的位置上读取 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,:}\)</span> 来预测原 token；这一过程通常是一次性编码，不涉及自回归生成，也没有 KV Cache 逐步增长的问题。对 Decoder-only 模型，LM Head 用于 next-token 预测：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置的隐藏状态对应预测 <span displaypfx="inline-" class="mathjax-container">\(x_{t+1}\)</span>，推理时会逐步生成，因此会配合因果注意力（Causal Self-Attention）和 KV Cache。对 Encoder-Decoder 模型，LM Head 位于解码器一侧：编码器先产出源序列表示，解码器再在因果自注意力与交叉注意力（Cross-Attention）的共同作用下生成目标侧隐藏状态，最后由 LM Head 映射到词表。</p>
<p>训练时，自回归语言模型（Autoregressive Language Model）通常采用 next-token 目标：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置的隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(H_t\)</span> 用来预测真实的下一个 token <span displaypfx="inline-" class="mathjax-container">\(x_{t+1}\)</span>。对应损失可写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{LM}}=-\sum_{t=1}^{L-1}\log \frac{\exp(Z_{t,x_{t+1}})}{\sum_{i=1}^{V}\exp(Z_{t,i})}\]</span>
<p>这个损失公式也可以逐项拆开理解。左边的 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}_{\mathrm{LM}}\)</span> 是整段序列的语言模型损失；求和下标 <span displaypfx="inline-" class="mathjax-container">\(t=1,\dots,L-1\)</span> 表示：前 <span displaypfx="inline-" class="mathjax-container">\(L-1\)</span> 个位置都要各自预测一次下一个 token。分子里的 <span displaypfx="inline-" class="mathjax-container">\(\exp(Z_{t,x_{t+1}})\)</span> 表示“真实下一个 token 在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置对应的指数化分数”；这里 <span displaypfx="inline-" class="mathjax-container">\(x_{t+1}\)</span> 是真实的下一个 token id，所以 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,x_{t+1}}\)</span> 表示在位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 对这个真实 token 的 logit。分母 <span displaypfx="inline-" class="mathjax-container">\(\sum_{i=1}^{V}\exp(Z_{t,i})\)</span> 则把整张词表所有候选 token 的分数全部加起来做归一化。因此整个分式就是“在位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 预测真实下一个 token 的概率”，外面的负对数再把它变成训练损失。</p>
<p>推理时则不是直接输出标签，而是先对 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,:}\)</span> 做 softmax 得到下一个 token 的条件分布。这里的 <span displaypfx="inline-" class="mathjax-container">\(Z_{t,:}\)</span> 不是一个标量，而是长度为 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 的向量，包含位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 对整张词表每个 token 的分数。对这整行做 softmax 后，得到的是：</p>
<span displaypfx="" class="mathjax-container">\[p(x_{t+1}=i\mid x_{\le t})=\frac{e^{Z_{t,i}}}{\sum_{j=1}^{V}e^{Z_{t,j}}},\qquad i=1,\dots,V\]</span>
<p>这条式子表示：在已经看到前缀 <span displaypfx="inline-" class="mathjax-container">\(x_{\le t}\)</span> 的条件下，下一个 token 取词表中第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个词的概率是多少。随后再配合贪心搜索（Greedy Decoding）、束搜索（Beam Search）、温度采样（Temperature Sampling）、top-k / top-p 等策略，从这组概率中选出真正生成的 token。也就是说，LM Head 负责把隐藏表示变成“词表级候选分数”，真正的文本生成还要再经过一层解码（Decoding）策略。</p>
<p>很多 LLM 还会使用权重共享（Weight Tying）：把输入嵌入表 <span displaypfx="inline-" class="mathjax-container">\(E\in\mathbb{R}^{V\;\times d_{\text{model}}}\)</span> 与输出头绑定，使 <span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab}}=E^\top\)</span>。这样一来，输入端“一个 token 的向量表示”与输出端“一个 token 作为候选答案时的原型向量”共用同一套参数空间。它通常既能减少参数量，也让输入和输出语义空间保持更强一致性。</p>
<p>从工程角度看，LM Head 往往比分类头更贵，因为它直接与词表大小 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 成正比：词表越大，最后一层的矩阵乘法、softmax 和采样都越重。因此，大模型的推理优化常会专门围绕 LM Head 展开，例如 fused softmax、采样优化、logits processor、词表裁剪或 speculative decoding 等。本质上，这些优化都在解决同一个问题：<span style="background-color: #c0c0c0;">如何更高效地从词表 logits 走到最终生成 token</span>。</p>
<div class="blog_h3"><span class="graybg">序列标注头（Token Classification）</span></div>
<p>序列标注（Sequence Labeling）/Token 分类（Token Classification）要求对每个 token 预测一个标签（例如 NER）。设主干输出 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{L\times d}\)</span>（长度 <span displaypfx="inline-" class="mathjax-container">\(L\)</span>，隐藏维 <span displaypfx="inline-" class="mathjax-container">\(d\)</span>），则逐 token 线性头为：</p>
<span displaypfx="" class="mathjax-container">\[Z=HW^\top+\mathbf{1}b^\top,\quad W\in\mathbb{R}^{C\times d},\ Z\in\mathbb{R}^{L\times C}\]</span>
<p>对第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个 token，用 softmax 得到 <span displaypfx="inline-" class="mathjax-container">\(p(y_t|x)\)</span> 并用逐 token 交叉熵训练。该做法把每个位置的标签看作条件独立，能工作，但会忽略标签之间的结构约束（例如 BIO 体系中不允许 <pre class="crayon-plain-tag">I-ORG</pre> 紧跟 <pre class="crayon-plain-tag">O</pre>）。</p>
<p>例：对 “Apple Inc. is in California” 的 NER（Named Entity Recognition），合理标签序列可能是 <pre class="crayon-plain-tag">B-ORG I-ORG O O B-LOC</pre>。若逐 token softmax 独立预测，模型可能输出不合法的组合；这类“结构错误”通常需要结构化任务头（Structured Head）来显式建模。</p>
<div class="blog_h4"><span class="graybg">条件随机场（CRF）</span></div>
<p>线性链条件随机场（Linear-chain Conditional Random Field, CRF）在 token 分类头上增加一个转移矩阵（Transition Matrix）<span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{C\times C}\)</span>，对整段标签序列做归一化建模。令发射分数（Emission Score）为 <span displaypfx="inline-" class="mathjax-container">\(s_t(y)=Z_{t,y}\)</span>，则序列 <span displaypfx="inline-" class="mathjax-container">\(y_{1:L}\)</span> 的总分为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{score}(x,y)=\sum_{t=1}^{L}\Big(A_{y_{t-1},y_t}+s_t(y_t)\Big)\]</span>
<p>并定义条件概率：</p>
<span displaypfx="" class="mathjax-container">\[p(y|x)=\frac{\exp(\mathrm{score}(x,y))}{\sum_{y'}\exp(\mathrm{score}(x,y'))}\]</span>
<p>训练最小化负对数似然（Negative Log-Likelihood）；解码用 Viterbi 算法（动态规划）求 <span displaypfx="inline-" class="mathjax-container">\(\arg\max_y \mathrm{score}(x,y)\)</span>。CRF 的收益是把“标签合法性/连贯性”学进转移项，从而显著减少结构错误。</p>
<div class="blog_h3"><span class="graybg">双仿射头（Biaffine）</span></div>
<p>双仿射（Biaffine）任务头常用于“成对打分”（Pairwise Scoring），例如依存句法（Dependency Parsing）里的“当前词应依附到哪个词”，或关系抽取（Relation Extraction）里的“两个实体之间是否存在某种关系”。它的名字可以按层级直接理解：对一个变量， <span displaypfx="inline-" class="mathjax-container">\(Wx\)</span> 是线性， <span displaypfx="inline-" class="mathjax-container">\(Wx+b\)</span> 是仿射；对两个变量， <span displaypfx="inline-" class="mathjax-container">\(h_i^\top U h_j\)</span> 是双线性（Bilinear）；在这个双线性项之外再加上线性项和偏置项，就得到双仿射（Biaffine）。因此，双仿射的本质是：<span style="background-color: #c0c0c0;">既建模两个表示之间的交互，又保留各自单独的角色偏好</span>。</p>
<span displaypfx="" class="mathjax-container">\[s(i,j)=h_i^\top U h_j + w^\top [h_i;h_j] + b\]</span>
<p>这条式子可以逐项拆开读。 <span displaypfx="inline-" class="mathjax-container">\(h_i,h_j\in\mathbb{R}^{d}\)</span> 是两个待配对对象的表示向量；在依存句法里，它们可分别表示“当前词”和“候选 head”；在关系抽取里，它们可分别表示“实体 1”和“实体 2”。 <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{R}^{d\times d}\)</span> 是双线性参数矩阵，因此 <span displaypfx="inline-" class="mathjax-container">\(h_i^\top U h_j\)</span> 建模的是二者之间的交互强度：不是只看 <span displaypfx="inline-" class="mathjax-container">\(h_i\)</span> 自己，也不是只看 <span displaypfx="inline-" class="mathjax-container">\(h_j\)</span> 自己，而是看“这两个向量放在一起是否匹配”。把它展开后就是 <span displaypfx="inline-" class="mathjax-container">\(\sum_{p=1}^{d}\sum_{q=1}^{d}(h_i)_p\,U_{pq}\,(h_j)_q\)</span>，因此 <span displaypfx="inline-" class="mathjax-container">\(U_{pq}\)</span> 可以理解为“第 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 个 dependent 特征与第 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> 个 head 特征同时出现时，该给多少分”。</p>
<p>若任务需要一次输出多类关系分数，工程实现通常会为每个类别各放一张矩阵 <span displaypfx="inline-" class="mathjax-container">\(U^{(k)}\)</span>，或直接使用三阶参数张量。这样不同关系类型就能拥有不同的交互模式，而不必共用同一套 <span displaypfx="inline-" class="mathjax-container">\(U\)</span>。</p>
<p><span displaypfx="inline-" class="mathjax-container">\([h_i;h_j]\in\mathbb{R}^{2d}\)</span> 表示把两个向量直接拼接； <span displaypfx="inline-" class="mathjax-container">\(w^\top [h_i;h_j]\)</span> 是普通仿射项，可以进一步拆成“只依赖 <span displaypfx="inline-" class="mathjax-container">\(h_i\)</span> 的线性项 + 只依赖 <span displaypfx="inline-" class="mathjax-container">\(h_j\)</span> 的线性项”。它的作用是补充单边信息：即使暂时不看两者之间的乘性交互，某些 token 或实体本身也可能更像 head、更像 dependent，或更像某类关系的一端。最后的 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是全局偏置，给整类配对提供一个基线分数。</p>
<p>具象地看， <span displaypfx="inline-" class="mathjax-container">\(h_i^\top U h_j\)</span> 像在问“这两个人之间是否搭得上”， <span displaypfx="inline-" class="mathjax-container">\(w^\top [h_i;h_j]\)</span> 像在问“这两个人各自单独看，是否本来就带某种角色倾向”。把两部分合起来，模型既能利用配对关系，也不会丢掉单个对象自身的类型线索。这正是双仿射通常比纯双线性更稳、更有表达力的原因。</p>
<p>把 <span displaypfx="inline-" class="mathjax-container">\(s(i,j)\)</span> 对所有 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 组合都算出来，就能形成一个 <span displaypfx="inline-" class="mathjax-container">\(L\times L\)</span> 的分数矩阵；对每个位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span>，再在所有候选 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 上做 <span displaypfx="inline-" class="mathjax-container">\(\arg\max\)</span> 或 softmax 分类，就能得到“它最可能依附到谁”或“它与谁最可能存在某种关系”。这种头的关键点在于：任务监督信号作用在“对”的层面，而不是单点分类。</p>
<div class="blog_h3"><span class="graybg">解析式 NLP（Analytical NLP）任务选型</span></div>
<p>解析式 NLP（Analytical NLP）指一类“结构化输出”的语言任务：输出不是一段自由文本，而是标签序列、树或图。这类任务的工程难点通常不在 backbone，而在<span style="background-color: #c0c0c0;">输出结构约束、标注成本、以及错误后果</span>（例如信息抽取用于合规/风控）。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">任务</td>
<td style="text-align: center;">输出结构</td>
<td style="text-align: center;">常见任务头</td>
<td style="text-align: center;">数据/标注成本</td>
<td style="text-align: center;">蒸馏难度</td>
</tr>
</thead>
<tbody>
<tr>
<td>NER</td>
<td>BIO 标签序列</td>
<td>Token 分类；CRF（可选）</td>
<td>中（需要一致的标注规范）</td>
<td>低~中（教师模型能稳定给出 token-level 或 span-level 标签）</td>
</tr>
<tr>
<td>DEP（Dependency Parsing）</td>
<td>树（每 token 一个 head）</td>
<td>Biaffine / 图解析器（Parser）</td>
<td>高（标注复杂；语言差异大）</td>
<td>中~高（结构约束更强；蒸馏需覆盖长尾句式）</td>
</tr>
<tr>
<td>SDP（Semantic Dependency Parsing）</td>
<td>有向图（多 head / 多边）</td>
<td>图结构头（Graph Head）</td>
<td>很高（语义标注成本高）</td>
<td>高（输出空间更大；错误更难用局部规则修正）</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">多任务与多头</span></div>
<p>同一个 backbone 可以挂多个任务头（Multi-head / Multi-task）：共享表示学习带来数据效率，但不同任务的梯度可能冲突，需要权衡损失权重（Loss Weighting）与采样策略。工程上也常见“同任务多头”：例如同时输出 token 标签与 span 边界，或同时输出检索 embedding 与分类 logits，用不同头对齐不同指标。</p>
<div class="blog_h2"><span class="graybg">反向传播</span></div>
<p>反向传播（Backpropagation）描述的是训练阶段中“如何把损失函数对输出的误差信号传回网络内部，并进一步求出各层参数梯度”的过程。理解这一章时，可以把它拆成三件事：前向传播先把值算出来，反向传播再把梯度传回去，而链式法则与 Jacobian 则给出这件事的数学形式。</p>
<div class="blog_h3"><span class="graybg">前向传播</span></div>
<p>前向传播（Forward Propagation）是训练与推理中首先发生的计算过程：给定输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 和当前参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span>，按照网络定义从前到后依次计算各层输出、最终预测 <span displaypfx="inline-" class="mathjax-container">\(\hat y\)</span>，以及训练时对应的损失 <span displaypfx="inline-" class="mathjax-container">\(L\)</span>。若把网络写成函数复合</p>
<span displaypfx="" class="mathjax-container">\[h_1=f_0(x),\quad h_2=f_1(h_1),\quad \cdots,\quad h_L=f_{L-1}(h_{L-1}),\quad \hat y=g(h_L),\quad L=\ell(\hat y,y)\]</span>
<p>那么前向传播做的就是按 <span displaypfx="inline-" class="mathjax-container">\(x\to h_1\to h_2\to \cdots \to h_L\to \hat y \to L\)</span> 的顺序把这些值算出来。它回答的问题是：<span style="background-color: #c0c0c0;">在当前参数下，这个样本会被模型算成什么结果，以及这个结果与真实标签相差多大</span>。</p>
<p>前向传播的产物不仅是最终预测，还包括中间激活值（Activation）。这些中间量一方面构成模型当前这次推理的内部表示，另一方面也是后续反向传播计算梯度时必须依赖的节点。因此，训练过程总是先有前向传播，再有反向传播：前者负责算值，后者负责算这些值对参数的敏感度。</p>
<div class="blog_h3"><span class="graybg">反向传播</span></div>
<p>反向传播（Backpropagation）是深度学习里用于<span style="background-color: #c0c0c0;">高效计算梯度（Gradient）</span>的算法。训练神经网络的目标，是最小化损失函数（Loss Function）<span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span>；而要用梯度下降（Gradient Descent）或 Adam 之类的优化器更新参数，就必须先知道每个参数对损失的影响，也就是 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial \theta}\)</span>。</p>
<p>这个需求在深层网络里会立刻变得庞大。一个模型往往包含从数万到数十亿个参数；如果对每个参数都单独做一次数值扰动，逐个估计“它变一点时损失会怎么变”，计算代价会高得无法训练。反向传播出现的直接原因，就是要把这个原本几乎不可做的问题，变成一次前向传播（Forward Pass）加一次反向传播（Backward Pass）即可完成的梯度计算流程。</p>
<p>它的来源并不神秘：神经网络本质上是许多简单函数的复合。线性层、激活函数、归一化、注意力、损失函数，都会把前一层输出当作后一层输入。只要这些局部变换可导（Differentiable）或几乎处处可导，就可以对整条复合链应用链式法则（Chain Rule）。反向传播就是把链式法则改写成一种适合计算图（Computational Graph）执行的程序：<span style="background-color: #c0c0c0;">前向时保存必要的中间结果，反向时从最终损失出发，把局部导数一层层乘回去，并把梯度分发给每个中间变量和参数</span>。</p>
<p>因此，反向传播不是另一条独立于微积分的“神经网络专用规则”，而是链式法则在复合函数上的工程化实现。它做的事情可以概括为两步：第一步，前向传播先算出各层激活值与最终损失；第二步，从 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial L}=1\)</span> 出发，把损失敏感度沿着依赖边反向传回去，依次得到 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_l}\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial W_l}\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial b_l}\)</span> 等量。到这一步为止，反向传播完成的是<span style="background-color: #c0c0c0;">梯度计算</span>；参数真正如何更新，则属于优化器（Optimizer）的职责。</p>
<p>最简单的梯度下降（Gradient Descent）更新写成</p>
<span displaypfx="" class="mathjax-container">\[\theta \leftarrow \theta - \eta \nabla_{\theta} L\]</span>
<p>这里更适合写梯度符号 <span displaypfx="inline-" class="mathjax-container">\(\nabla_{\theta}L\)</span>，因为 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 往往是整个参数向量或参数集合，而不是单个标量。若只讨论某一个标量参数，才可写成 <span displaypfx="inline-" class="mathjax-container">\(\theta \leftarrow \theta - \eta \frac{\partial L}{\partial \theta}\)</span>。从计算方式看，反向传播对应自动微分（Automatic Differentiation）中的反向模式（Reverse Mode AD）。它特别适合“输入参数很多、输出损失很少”的场景；而神经网络训练恰好就是这种结构：参数维度极大，但最终通常只关心一个标量损失。因此，反向传播成为现代深度学习训练的标准机制。</p>
<div class="blog_h4"><span class="graybg">一个最小手算例子</span></div>
<p>先看一个只多加一层激活函数（Activation）的最小网络。设输入 <span displaypfx="inline-" class="mathjax-container">\(x=2\)</span>，参数为权重 <span displaypfx="inline-" class="mathjax-container">\(w=1\)</span> 与偏置 <span displaypfx="inline-" class="mathjax-container">\(b=-1\)</span>，先做仿射变换（Affine Transform）</p>
<span displaypfx="" class="mathjax-container">\[z=wx+b=1\cdot 2-1=1\]</span>
<p>再经过 ReLU 激活函数</p>
<span displaypfx="" class="mathjax-container">\[a=\mathrm{ReLU}(z)=\max(0,z)=1\]</span>
<p>把激活值当作最终预测，即 <span displaypfx="inline-" class="mathjax-container">\(\hat y=a\)</span>。若真实标签 <span displaypfx="inline-" class="mathjax-container">\(y=0\)</span>，损失取平方损失（Squared Loss）</p>
<span displaypfx="" class="mathjax-container">\[L=\frac12(\hat y-y)^2=\frac12(1-0)^2=0.5\]</span>
<p>前向传播链条现在是</p>
<span displaypfx="" class="mathjax-container">\[x\ \longrightarrow\ z\ \longrightarrow\ a=\hat y\ \longrightarrow\ L\]</span>
<p>反向传播先从损失对输出的导数开始：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial \hat y}=\hat y-y=1\]</span>
<p>由于这里 <span displaypfx="inline-" class="mathjax-container">\(\hat y=a\)</span>，所以</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial a}=1\]</span>
<p>再经过激活函数这一层。因为当前 <span displaypfx="inline-" class="mathjax-container">\(z=1&gt;0\)</span>，ReLU 的导数为</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial a}{\partial z}=\mathrm{ReLU}'(z)=1\]</span>
<p>于是梯度传回仿射层输出：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial z}=\frac{\partial L}{\partial a}\cdot\frac{\partial a}{\partial z}=1\cdot 1=1\]</span>
<p>最后再看仿射层对参数的局部导数：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial z}{\partial w}=x=2,\qquad \frac{\partial z}{\partial b}=1\]</span>
<p>因此</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w}=\frac{\partial L}{\partial z}\cdot\frac{\partial z}{\partial w}=1\cdot 2=2\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial b}=\frac{\partial L}{\partial z}\cdot\frac{\partial z}{\partial b}=1\cdot 1=1\]</span>
<p>若学习率 <span displaypfx="inline-" class="mathjax-container">\(\eta=0.1\)</span>，梯度下降更新后</p>
<span displaypfx="" class="mathjax-container">\[w\leftarrow 1-0.1\cdot 2=0.8,\qquad b\leftarrow -1-0.1\cdot 1=-1.1\]</span>
<p>这个版本比纯线性例子多了一站 <span displaypfx="inline-" class="mathjax-container">\(z\to a\)</span>，因此链式法则也多乘了一个局部导数 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial a}{\partial z}\)</span>。这正是反向传播的一般形式：<span style="background-color: #c0c0c0;">误差信号每穿过一层，就乘上这一层自己的局部导数</span>。若这里的 <span displaypfx="inline-" class="mathjax-container">\(z&lt;0\)</span>，则 ReLU 导数为 0，上游梯度也会在这一层被截断。</p>
<div class="blog_h3"><span class="graybg">链式法则与雅可比连乘</span></div>
<p>反向传播（Backpropagation）本质是链式法则（Chain Rule）在计算图（Computational Graph）上的系统化应用：把最终损失对中间变量的导数沿依赖关系向后传。</p>
<p>固定一个训练样本 <span displaypfx="inline-" class="mathjax-container">\((x,y)\)</span> 后，损失 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 最终当然可以看成参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 的函数；训练时真正要更新的也确实是权重（Weight）。但在计算图里， <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span> 并不是一步直接从 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 跳到损失，而是先经过各层线性变换、激活值（Activation）和输出再到损失。因此，反向传播会先求 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_l}\)</span> 这类“损失对中间激活值的敏感度”，再由这些中间梯度继续求出 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial \theta_l}\)</span>。</p>
<p>Jacobian 一节已经给出局部线性化：若 <span displaypfx="inline-" class="mathjax-container">\(h_{l+1}=f_l(h_l)\)</span>，则在当前点附近有 <span displaypfx="inline-" class="mathjax-container">\(dh_{l+1}\approx J_l\,dh_l\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(J_l=\frac{\partial h_{l+1}}{\partial h_l}\)</span>。这条式子描述的是<span style="background-color: #c0c0c0;">输入扰动如何向前传到输出扰动</span>；反向传播关心的是相反方向的问题：输出端的损失敏感度如何传回输入端。</p>
<p>先看一层。由于损失 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 是标量，若采用与前文 Jacobian 一致的分子布局（Numerator Layout），把 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_l}\)</span> 记成 <span displaypfx="inline-" class="mathjax-container">\(1\;\times d_l\)</span> 的行向量，则向量链式法则写成：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_l}=\frac{\partial L}{\partial h_{l+1}}\frac{\partial h_{l+1}}{\partial h_l}=\frac{\partial L}{\partial h_{l+1}}J_l\]</span>
<p>这条式子最好按符号逐个读。先看 <span displaypfx="inline-" class="mathjax-container">\(h_l\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(h_{l+1}\)</span>：它们分别表示第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层的输入表示和该层输出表示。若 <span displaypfx="inline-" class="mathjax-container">\(h_l\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\(d_l\)</span> 个分量， <span displaypfx="inline-" class="mathjax-container">\(h_{l+1}\)</span> 有 <span displaypfx="inline-" class="mathjax-container">\(d_{l+1}\)</span> 个分量，那么</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_{l+1}}=\begin{bmatrix}\frac{\partial L}{\partial h_{l+1,1}} &amp; \frac{\partial L}{\partial h_{l+1,2}} &amp; \cdots &amp; \frac{\partial L}{\partial h_{l+1,d_{l+1}}}\end{bmatrix}\in\mathbb{R}^{1\;\times d_{l+1}}\]</span>
<p>它表示：损失 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 对第 <span displaypfx="inline-" class="mathjax-container">\(l+1\)</span> 层每个输出分量分别有多敏感。这里把它写成行向量，是因为前文采用的是分子布局（Numerator Layout）。</p>
<p>再看 Jacobian：</p>
<span displaypfx="" class="mathjax-container">\[J_l=\frac{\partial h_{l+1}}{\partial h_l}\in\mathbb{R}^{d_{l+1}\;\times d_l}\]</span>
<p>其中第 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 个元素是</p>
<span displaypfx="" class="mathjax-container">\[(J_l)_{ij}=\frac{\partial h_{l+1,i}}{\partial h_{l,j}}\]</span>
<p>意思是：第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个输入分量变化一点，会让第 <span displaypfx="inline-" class="mathjax-container">\(l+1\)</span> 层第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个输出分量变化多少。因此，Jacobian 记录的是“输入各分量 <span displaypfx="inline-" class="mathjax-container">\(\to\)</span> 输出各分量”的局部影响表。</p>
<p>现在看乘法</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_l}=\frac{\partial L}{\partial h_{l+1}}J_l\]</span>
<p>左边最终应该是一个关于 <span displaypfx="inline-" class="mathjax-container">\(h_l\)</span> 各分量的梯度，所以它必须有 <span displaypfx="inline-" class="mathjax-container">\(d_l\)</span> 个分量。维度上， <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_{l+1}}\)</span> 是 <span displaypfx="inline-" class="mathjax-container">\(1\;\times d_{l+1}\)</span>， <span displaypfx="inline-" class="mathjax-container">\(J_l\)</span> 是 <span displaypfx="inline-" class="mathjax-container">\(d_{l+1}\;\times d_l\)</span>，两者相乘后确实得到 <span displaypfx="inline-" class="mathjax-container">\(1\;\times d_l\)</span>。</p>
<p>如果把第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个分量单独展开，这个乘法其实就是</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_{l,j}}=\sum_{i=1}^{d_{l+1}}\frac{\partial L}{\partial h_{l+1,i}}\frac{\partial h_{l+1,i}}{\partial h_{l,j}}\]</span>
<p>这时含义就完全显出来了：第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个输入分量，会通过第 <span displaypfx="inline-" class="mathjax-container">\(l+1\)</span> 层的所有输出分量共同影响损失；因此要把“损失对每个输出分量的敏感度”乘上“该输出分量对当前输入分量的局部导数”，再对所有输出分量求和。矩阵乘法只是把这组求和一次性写成了紧凑形式。</p>
<p>把这条一层公式沿网络递推展开，对深度网络 <span displaypfx="inline-" class="mathjax-container">\(h_{l+1}=f_l(h_l)\)</span> 可得：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_0}=\frac{\partial L}{\partial h_L}J_{L-1}J_{L-2}\cdots J_0,\qquad J_l=\frac{\partial h_{l+1}}{\partial h_l}\]</span>
<p>这条公式的读法非常具体：前向传播按 <span displaypfx="inline-" class="mathjax-container">\(h_0\to h_1\to\cdots\to h_L\)</span> 计算；反向传播则从最终梯度 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_L}\)</span> 出发，依次乘上最后一层到第一层的 Jacobian，把敏感度一层层传回去。矩阵乘法满足结合律（Associativity），因此可以逐层累积；但不满足交换律，乘法顺序不能改，因为每个 Jacobian 都对应不同层的局部坐标变换。</p>
<p>一个最小可算例可以把这件事完全写开。设输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是标量，第一层有两个参数 <span displaypfx="inline-" class="mathjax-container">\(w_{11},w_{12}\)</span>，输出二维隐藏表示：</p>
<span displaypfx="" class="mathjax-container">\[h_1=\begin{bmatrix}h_{1,1}\\h_{1,2}\end{bmatrix}=\begin{bmatrix}w_{11}x\\w_{12}x\end{bmatrix}\]</span>
<p>第二层有两个参数 <span displaypfx="inline-" class="mathjax-container">\(w_{21},w_{22}\)</span>，把二维隐藏表示读成一个标量预测：</p>
<span displaypfx="" class="mathjax-container">\[\hat y=w_{21}h_{1,1}+w_{22}h_{1,2}\]</span>
<p>损失取最简单的平方损失（Squared Loss）：</p>
<span displaypfx="" class="mathjax-container">\[L=\frac12(\hat y-y)^2\]</span>
<p>这里总参数一共四个，但反向传播不会直接去“猜” <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial w_{11}}\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial w_{12}}\)</span> 等结果，而是先沿着计算图写出中间量：</p>
<span displaypfx="" class="mathjax-container">\[x\ \longrightarrow\ h_1\ \longrightarrow\ \hat y\ \longrightarrow\ L\]</span>
<p>先看第二层的局部 Jacobian。因为 <span displaypfx="inline-" class="mathjax-container">\(\hat y\)</span> 是标量、 <span displaypfx="inline-" class="mathjax-container">\(h_1\in\mathbb{R}^2\)</span>，所以</p>
<span displaypfx="" class="mathjax-container">\[J_1=\frac{\partial \hat y}{\partial h_1}=\begin{bmatrix}w_{21}&amp;w_{22}\end{bmatrix}\]</span>
<p>再看第一层的局部 Jacobian。因为 <span displaypfx="inline-" class="mathjax-container">\(h_1\in\mathbb{R}^2\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是标量，所以</p>
<span displaypfx="" class="mathjax-container">\[J_0=\frac{\partial h_1}{\partial x}=\begin{bmatrix}w_{11}\\w_{12}\end{bmatrix}\]</span>
<p>损失对最终输出的导数最容易先算：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial \hat y}=\hat y-y\]</span>
<p>于是，损失对隐藏表示的梯度由链式法则得到：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_1}=\frac{\partial L}{\partial \hat y}\frac{\partial \hat y}{\partial h_1}=(\hat y-y)\begin{bmatrix}w_{21}&amp;w_{22}\end{bmatrix}\]</span>
<p>这一步已经把“损失对输出的敏感度”传回到了中间激活值 <span displaypfx="inline-" class="mathjax-container">\(h_1\)</span>。继续往回乘第一层 Jacobian，就得到损失对输入的梯度：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial x}=\frac{\partial L}{\partial \hat y}J_1J_0\]</span>
<span displaypfx="" class="mathjax-container">\[=(\hat y-y)\begin{bmatrix}w_{21}&amp;w_{22}\end{bmatrix}\begin{bmatrix}w_{11}\\w_{12}\end{bmatrix}\]</span>
<span displaypfx="" class="mathjax-container">\[=(\hat y-y)(w_{21}w_{11}+w_{22}w_{12})\]</span>
<p>对参数的梯度也是同样的思路。第二层参数直接作用在 <span displaypfx="inline-" class="mathjax-container">\(\hat y\)</span> 上，因此</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{21}}=\frac{\partial L}{\partial \hat y}\frac{\partial \hat y}{\partial w_{21}}=(\hat y-y)h_{1,1}\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{22}}=\frac{\partial L}{\partial \hat y}\frac{\partial \hat y}{\partial w_{22}}=(\hat y-y)h_{1,2}\]</span>
<p>第一层参数不直接连到损失，而是先影响 <span displaypfx="inline-" class="mathjax-container">\(h_1\)</span>，再影响 <span displaypfx="inline-" class="mathjax-container">\(\hat y\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(L\)</span>，所以必须经过中间激活值这一站：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{11}}=\frac{\partial L}{\partial h_{1,1}}\frac{\partial h_{1,1}}{\partial w_{11}}=[(\hat y-y)w_{21}]\,x\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{12}}=\frac{\partial L}{\partial h_{1,2}}\frac{\partial h_{1,2}}{\partial w_{12}}=[(\hat y-y)w_{22}]\,x\]</span>
<p>若取一组具体数值 <span displaypfx="inline-" class="mathjax-container">\(x=2\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(w_{11}=1\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(w_{12}=-1\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(w_{21}=0.5\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(w_{22}=2\)</span>，则前向传播先得到</p>
<span displaypfx="" class="mathjax-container">\[h_1=\begin{bmatrix}2\\-2\end{bmatrix},\qquad \hat y=0.5\cdot 2+2\cdot(-2)=-3,\qquad \frac{\partial L}{\partial \hat y}=\hat y-y=-4\]</span>
<p>于是反向传播依次得到</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial h_1}=-4\begin{bmatrix}0.5&amp;2\end{bmatrix}=\begin{bmatrix}-2&amp;-8\end{bmatrix}\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial x}=\begin{bmatrix}-2&amp;-8\end{bmatrix}\begin{bmatrix}1\\-1\end{bmatrix}=6\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{21}}=-4\cdot 2=-8,\qquad \frac{\partial L}{\partial w_{22}}=-4\cdot(-2)=8\]</span>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial w_{11}}=(-2)\cdot 2=-4,\qquad \frac{\partial L}{\partial w_{12}}=(-8)\cdot 2=-16\]</span>
<p>这个最小例子把反向传播的结构完整展示了出来：损失函数最终是参数的函数，但梯度并不是“绕开中间层直接对权重求”。它必须先通过 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial \hat y}\)</span>、 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial L}{\partial h_1}\)</span> 这样的中间敏感度逐步回传。激活值因此不是“额外引入的变量”，而是计算图中必经的节点。</p>
<p>若改用深度学习里更常见的列向量梯度记号 <span displaypfx="inline-" class="mathjax-container">\(\nabla_{h_l}L\in\mathbb{R}^{d_l}\)</span>，同一件事会写成</p>
<span displaypfx="" class="mathjax-container">\[\nabla_{h_l}L=J_l^\top \nabla_{h_{l+1}}L\]</span>
<p>这与上面的公式没有本质区别，只是把“左乘 Jacobian 的行向量记号”改写成了“右侧乘 Jacobian 转置的列向量记号”。工程实现里常见的 Vector-Jacobian Product，本质上就是这一步。</p>
<p>对参数的梯度也是同一个模式。若第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层含参数 <span displaypfx="inline-" class="mathjax-container">\(\theta_l\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial L}{\partial \theta_l}=\frac{\partial L}{\partial h_{l+1}}\frac{\partial h_{l+1}}{\partial \theta_l}\]</span>
<p>因此 backward 的核心不是“把前向再算一遍”，而是复用每一层的局部 Jacobian，把同一个上游梯度分别传给输入变量和参数。</p>
<div class="blog_h3"><span class="graybg">梯度消失与爆炸</span></div>
<p>梯度消失（Vanishing Gradients）与梯度爆炸（Exploding Gradients）通常来自连乘的数值尺度：若这些雅可比的有效增益（Effective Gain）的奇异值（Singular Values）长期小于 1，则梯度指数级衰减；长期大于 1 则指数级放大。</p>
<p>在 RNN 的 BPTT 中，这种现象尤其明显：同一递归矩阵在时间轴上重复相乘。粗略地看，若 <span displaypfx="inline-" class="mathjax-container">\(W_{hh}\)</span> 的谱半径（Spectral Radius）<span displaypfx="inline-" class="mathjax-container">\(\rho(W_{hh})\)</span> 明显大于 1，梯度更易爆炸；明显小于 1 更易消失。但这不是“只要 <span displaypfx="inline-" class="mathjax-container">\(\lambda&gt;1\)</span> 就一定爆炸”的二选一结论，因为实际还受到激活函数导数、归一化、残差/门控结构、以及数据分布的共同影响。</p>
<p>“梯度随训练慢慢变小是否正常？”在接近最优点时，梯度范数下降是预期现象；需要警惕的是训练早期就出现系统性消失（例如大量饱和激活导致导数接近 0）或出现不稳定爆炸（loss/梯度频繁变成 NaN/Inf）。</p>
<p>常见应对：</p>
<ul>
<li>梯度裁剪（Gradient Clipping）：抑制爆炸。</li>
<li>合理初始化（Xavier/He）、归一化（BatchNorm/LayerNorm）、残差连接（Residual）。</li>
<li>门控结构（Gated Units）：LSTM/GRU 通过门控缓解长程梯度问题。</li>
</ul>
<div class="blog_h2"><span class="graybg">权重初始化</span></div>
<p>权重初始化（Weight Initialization）的目标不是“随机给一个小数”，而是控制信号在层间传播的数值尺度。设第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层的线性部分为 <span displaypfx="inline-" class="mathjax-container">\(z^{(l)}=W^{(l)}h^{(l-1)}+b^{(l)}\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(z^{(l)}\)</span> 的二阶原点矩（Second Raw Moment）在层间持续放大，前向激活与反向梯度都会倾向爆炸；若持续衰减，则会出现梯度消失。</p>
<p>对单个神经元，写成 <span displaypfx="inline-" class="mathjax-container">\(z=\sum_{i=1}^{n}w_i x_i+b\)</span>。初始化分析里通常进一步假设 <span displaypfx="inline-" class="mathjax-container">\(w_i\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(x_i\)</span> 相互独立，且权重满足 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[w_i]=0\)</span>。这里的 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[w_i]=0\)</span> 不是在声称“训练后的权重天然应当均值为 0”，而是在分析初始化时，把权重看作从一个以 0 为中心的随机分布中抽样得到；这样做的目的，是避免网络在一开始就对某一方向产生系统性偏置，并让前向输出的均值计算更干净。基于这一初始化假设，在线性部分有</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[z]=0,\quad \mathbb{E}[z^2]=n\,\mathrm{Var}(w)\,\mathbb{E}[x^2]\]</span>
<p>当输入也近似零均值时， <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[x^2]\approx \mathrm{Var}(x)\)</span>，于是常写成 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(z)\approx n\,\mathrm{Var}(w)\,\mathrm{Var}(x)\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 就是 fan_in。因此初始化的核心约束可以概括为：让 <span displaypfx="inline-" class="mathjax-container">\(n\,\mathrm{Var}(w)\)</span> 保持在 1 附近。</p>
<p>更严格地说，深度初始化分析常跟踪的是 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{E}[h^2]\)</span>，而不是所有层都精确使用方差；因为经过 ReLU 之后，激活不再零均值，此时二阶原点矩与方差不再完全相同。但在线性层与零均值近似下，两者是一致的，因此“保持方差稳定”仍是准确的工程表述。</p>
<p>两种朴素做法都会失败：若所有权重都初始化为 0，则各神经元保持完全对称，反向传播得到相同更新，网络退化为“许多拷贝的同一个单元”；若直接令 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(w)=1\)</span>，则线性输出的尺度会随层数按 fan_in 连乘，深层网络极易数值失稳。偏置（Bias）通常初始化为 0 或很小的常数；真正决定尺度的是权重矩阵的方差结构。</p>
<p>这里的 fan_in 与 fan_out 可以直接按“这层连接了多少路信号”来理解。对某个线性层中的一个输出神经元，fan_in 是流入它的输入通道数；输入通道越多，很多随机小贡献叠加后，输出就越容易变大。对某个输入神经元，fan_out 是它连接到下一层多少个输出通道；fan_out 越大，这个输入方向上的梯度在反向传播时就会被分发到更多支路。因此，fan_in 主要约束前向激活的放大量级，fan_out 主要约束反向梯度的放大量级。</p>
<p>具象地看，一个神经元像一个汇流节点：fan_in 决定有多少根水管把信号同时灌进来，fan_out 决定这股信号会被分流到多少个下游节点。初始化如果不考虑这两个连接数，网络就会在“汇流过猛”与“分流过弱”之间失衡。Xavier 正是在前向与反向之间做折中，He 则进一步把激活函数本身带来的能量损失也纳入补偿。</p>
<div class="blog_h3"><span class="graybg">Xavier 初始化</span></div>
<p>Xavier 初始化（Xavier Initialization）也称 Glorot 初始化（Glorot Initialization），针对近似线性的激活工作区间设计，例如 tanh 与 sigmoid 在原点附近的局部线性区域。它的目标是同时控制前向激活与反向梯度的尺度，使它们在穿过每一层时不发生系统性放大或衰减。</p>
<p>若暂时忽略激活函数对二阶原点矩的额外缩放，则前向保持尺度不变要求 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(w)\approx 1/\mathrm{fan\_in}\)</span>；反向对应地要求 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(w)\approx 1/\mathrm{fan\_out}\)</span>。Glorot 给出的折中形式因此写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}\]</span>
<p>常见实现包括正态版 <span displaypfx="inline-" class="mathjax-container">\(w\sim\mathcal{N}\!\left(0,\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}\right)\)</span>，以及均匀版 <span displaypfx="inline-" class="mathjax-container">\(w\sim U\!\left[-\sqrt{\frac{6}{\mathrm{fan\_in}+\mathrm{fan\_out}}},\sqrt{\frac{6}{\mathrm{fan\_in}+\mathrm{fan\_out}}}\right]\)</span>。</p>
<p>从直觉上看，Xavier 初始化像是在给每一层设置一个“不过度放大、也不过度压缩”的中性增益（neutral gain）。可以把一层线性变换想成一组并联的混音器：输入信号从上一层流入，经过许多权重通道混合后送到下一层。Xavier 的目标就是让这组混音器在初始状态下近似保持“总音量”不变，使信号既不会层层变得越来越吵，也不会层层变得越来越弱。</p>
<p>Xavier 的局限在于：它默认激活函数不会系统性丢失太多能量。对 ReLU 这类半波整流（Half-wave Rectification）激活，这个假设不再成立；即使线性层前后的尺度匹配，经过激活后信号的二阶原点矩仍会明显下降。</p>
<div class="blog_h3"><span class="graybg">He 初始化</span></div>
<p>He 初始化（He Initialization）也称 Kaiming 初始化（Kaiming Initialization），专门处理 ReLU 家族的整流效应。若线性输出 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 近似关于 0 对称，经过 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{ReLU}(z)=\max(0,z)\)</span> 后，大约一半的质量被截断为 0，并且</p>
<span displaypfx="" class="mathjax-container">\[\mathbb{E}[\mathrm{ReLU}(z)^2]=\frac{1}{2}\mathbb{E}[z^2]\]</span>
<p>因此，若仍使用 Xavier 量级，信号的二阶原点矩会随层数持续衰减。He 初始化通过把权重方差提高到</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}}\]</span>
<p>来补偿这一半波损失。对应实现可写为正态版 <span displaypfx="inline-" class="mathjax-container">\(w\sim\mathcal{N}\!\left(0,\frac{2}{\mathrm{fan\_in}}\right)\)</span>，或均匀版 <span displaypfx="inline-" class="mathjax-container">\(w\sim U\!\left[-\sqrt{\frac{6}{\mathrm{fan\_in}}},\sqrt{\frac{6}{\mathrm{fan\_in}}}\right]\)</span>。</p>
<p>具象地看，ReLU 像一道只允许正值通过的闸门：一批近似对称分布的信号经过后，负半边会被直接截成 0，等于天然损失了一部分能量。He 初始化做的事情就是在闸门前把信号预先放大一些，使它通过这道“半波闸门”之后，整体尺度仍能维持在稳定区间。Xavier 假定通道基本不漏能量，He 则明确把 ReLU 的漏损补偿计入初始化方差。</p>
<p>对 Leaky ReLU/PReLU，补偿因子可推广为 <span displaypfx="inline-" class="mathjax-container">\(\frac{2}{(1+a^2)\,\mathrm{fan\_in}}\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 是负半轴斜率。实践上，ReLU 及其变体默认优先使用 He 初始化；tanh/sigmoid 更适合 Xavier。归一化层与残差连接可以进一步放宽初始化的容错区间，但不能替代合理的初始尺度控制。</p>
<div class="blog_h3"><span class="graybg">大语言模型里的初始化</span></div>
<p>Transformer / 大语言模型（Large Language Model, LLM）里的初始化通常不按“整模统一套 Xavier”或“整模统一套 He”来理解，而更像一套围绕<span style="background-color: #c0c0c0;">残差流（Residual Stream）稳定性、归一化层和深度扩展</span>设计的小方差初始化配方。原因在于：LLM 的主干不是简单的“线性层 + 单一激活函数”堆叠，而是由自注意力、残差连接、LayerNorm / RMSNorm、MLP / SwiGLU 等子结构共同组成；纯粹以某个激活函数为中心推导的 Xavier / He，只能覆盖其中一部分局部直觉。</p>
<p>工程上最常见的做法是：嵌入层与线性层权重用零均值的小方差正态分布初始化，偏置置零或省略；随后依靠 LayerNorm / RMSNorm 与残差结构维持训练初期的数值稳定。BERT、GPT-2 一类经典 Transformer 常见做法是使用标准差约为 <span displaypfx="inline-" class="mathjax-container">\(0.02\)</span> 的正态或截断正态初始化；很多更现代的 Decoder-only LLM 仍延续“小方差高斯初始化”这一主线，只是在具体投影层上再叠加按隐藏维度、层数或残差分支做缩放的配方。</p>
<p>从直觉上看，LLM 初始化更像是在给一条很深的多车道主干道设置初始车流密度。若注意力里的 <span displaypfx="inline-" class="mathjax-container">\(Q/K/V/O\)</span> 投影、FFN 的升维/降维投影以及其他会把结果写回残差流的线性层初始尺度过大，残差分支会把信号越叠越猛，训练初期容易震荡；若尺度过小，几十层上百层残差块叠起来后，真正进入有效学习区的信号又会太弱。因此，现代 LLM 初始化的重点往往不是“严格选 Xavier 还是 He”，而是<span style="background-color: #c0c0c0;">让残差支路在深层网络中既能传递信息，又不会在训练一开始就把数值尺度推离稳定区</span>。</p>
<p>具体到模块分工，也可以这样理解：MLP 内部若使用 ReLU 家族激活，He 的补偿思想仍然成立；若使用 GELU、SiLU、SwiGLU 这类更平滑或带门控的激活，则实现里往往直接采用统一的小方差正态初始化，再由归一化、残差和训练配方共同保证稳定性。换言之，LLM 并没有抛弃 Xavier / He 背后的“方差守恒”原则，而是把这套原则嵌入到了更完整的 Transformer 初始化策略里。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">方法</td>
<td style="text-align: center;">核心方差</td>
<td style="text-align: center;">适用激活</td>
<td style="text-align: center;">主要问题 / 逻辑</td>
</tr>
</thead>
<tbody>
<tr>
<td>零初始化</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(w)=0\)</span></td>
<td>无</td>
<td>破坏对称性；所有神经元学到相同特征</td>
</tr>
<tr>
<td>标准正态随机</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathrm{Var}(w)=1\)</span></td>
<td>无</td>
<td>尺度随深度快速放大；易导致爆炸</td>
</tr>
<tr>
<td>Xavier / Glorot</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}\)</span></td>
<td>Tanh、Sigmoid</td>
<td>兼顾前向与反向尺度；假设激活近似线性</td>
</tr>
<tr>
<td>He / Kaiming</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\frac{2}{\mathrm{fan\_in}}\)</span></td>
<td>ReLU、Leaky ReLU</td>
<td>补偿 ReLU 的半波截断；保持二阶原点矩稳定</td>
</tr>
<tr>
<td>LLM 常用小方差高斯初始化</td>
<td>常取固定小标准差，或再叠加按宽度 / 深度缩放</td>
<td>Transformer 模块整体</td>
<td>服务残差流、归一化层与深层稳定训练；不是单纯按某一种激活推导</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">正则化</span></div>
<p>正则化（Regularization）在经验风险（Empirical Risk）上增加复杂度惩罚，缓解过拟合（Overfitting）。假设函数/模型 <span displaypfx="inline-" class="mathjax-container">\(f_\theta\)</span> 决定预测；样本损失 <span displaypfx="inline-" class="mathjax-container">\(\ell\)</span> 度量单样本误差；代价函数/成本函数 <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span> 汇总训练集误差；目标函数 <span displaypfx="inline-" class="mathjax-container">\(J(\theta)\)</span> 则是 <span displaypfx="inline-" class="mathjax-container">\(L(\theta)\)</span> 加上正则化项： <span displaypfx="inline-" class="mathjax-container">\(J(\theta)=L(\theta)+\lambda\Omega(\theta)\)</span>。</p>
<span displaypfx="" class="mathjax-container">\[J(\theta)=\frac{1}{m}\sum_{i=1}^{m}\ell\!\left(f_{\theta}(x^{(i)}),y^{(i)}\right)+\lambda\Omega(\theta)\]</span>
<div class="blog_h3"><span class="graybg">L1 正则化（Lasso）</span></div>
<p>L1 正则化（L1 Regularization）使用 <span displaypfx="inline-" class="mathjax-container">\(\Omega(\theta)=\|\theta\|_1\)</span>。它倾向产生稀疏解（Sparsity），即一部分权重被直接压到 0，因此可同时做参数学习和特征选择（Feature Selection）。</p>
<p>从优化角度看，L1 的梯度是次梯度（Subgradient）：对单个参数 <span displaypfx="inline-" class="mathjax-container">\(\theta_k\)</span>，当 <span displaypfx="inline-" class="mathjax-container">\(\theta_k\ne 0\)</span> 时有 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial}{\partial \theta_k}\|\theta\|_1=\mathrm{sign}(\theta_k)\)</span>；在 0 点是一段区间 <span displaypfx="inline-" class="mathjax-container">\([-1,1]\)</span>。这使得优化过程更容易把小权重“推过 0”，形成精确稀疏。</p>
<p>在很多实现中，会用近端算子（Proximal Operator）给出更清晰的“压到 0”机制：对标量 <span displaypfx="inline-" class="mathjax-container">\(w\)</span>，软阈值化（Soft-Thresholding）是</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{soft}(w,\alpha)=\mathrm{sign}(w)\max(|w|-\alpha,0)\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(|w|\le \alpha\)</span> 时直接变为 0。</p>
<div class="blog_h3"><span class="graybg">L2 正则化（Ridge / Weight Decay）</span></div>
<p>L2 正则化（L2 Regularization）使用 <span displaypfx="inline-" class="mathjax-container">\(\Omega(\theta)=\|\theta\|_2^2\)</span>。它倾向均匀缩小参数，但通常不会把参数精确压到 0。直观上，L1 在 0 点不可导，更容易触发“阈值化”解；L2 是光滑二次惩罚，更新更连续。</p>
<p>梯度层面，L2 会在原梯度上叠加一个“拉回原点”的项：若目标为 <span displaypfx="inline-" class="mathjax-container">\(\ell(\theta)+\lambda\|\theta\|_2^2\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(\nabla_\theta=\nabla \ell(\theta)+2\lambda\theta\)</span>。工程上常把这个效果称为权重衰减（Weight Decay）：每一步都把权重按比例缩小。</p>
<p>需要区分一个常见细节：在自适应优化器（如 Adam）里，“把 <span displaypfx="inline-" class="mathjax-container">\(\lambda\|\theta\|_2^2\)</span> 加到损失里”与“直接做 weight decay”在数值上并不完全等价；因此实践中常用解耦权重衰减（Decoupled Weight Decay，典型实现是 AdamW）来获得更可控的正则化行为。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/regularization-l1-l2-geometry.png"><img class="alignnone size-full" src="https://blog.gmem.cc/wp-content/uploads/2026/03/regularization-l1-l2-geometry.png" alt="regularization-l1-l2-geometry" width="1920" height="930" /></a></p>
<p>把带惩罚的目标改写成约束形式后，几何差异会非常直观： <span displaypfx="inline-" class="mathjax-container">\(\min_\theta L(\theta)\ \mathrm{s.t.}\ \|\theta\|_1\le t\)</span> 的可行域在二维里是菱形， <span displaypfx="inline-" class="mathjax-container">\(\min_\theta L(\theta)\ \mathrm{s.t.}\ \|\theta\|_2\le t\)</span> 的可行域则是圆盘。损失等高线从外向内收缩时，第一次接触 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 边界，往往更容易落在角点；角点恰好对应某些坐标精确等于 0。 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 边界处处光滑，接触点通常只是把所有坐标一起缩小，而不是把其中一部分直接压成 0。</p>
<p>从一维更新机制看，这个差异也对应两种不同的收缩方式。 <span displaypfx="inline-" class="mathjax-container">\(L_2\)</span> 更像连续比例收缩：权重越大，拉回原点的力越强，但通常不会在有限步内变成精确 0。 <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 则对应软阈值化（Soft-Thresholding）：一旦 <span displaypfx="inline-" class="mathjax-container">\(|w|\)</span> 小于阈值，就会被直接压成 0。因此， <span displaypfx="inline-" class="mathjax-container">\(L_1\)</span> 不只是“把权重变小”，而是会主动把一部分坐标从模型中删掉，这就是稀疏性（Sparsity）的来源。</p>
<div class="blog_h3"><span class="graybg">Dropout</span></div>
<p>Dropout 通过随机屏蔽部分神经元输出，减少共适应（Co-adaptation），等价于在训练时对网络做一种随机子网络集成（Ensemble）。这里的共适应，是指若干神经元彼此形成了固定搭配：模型过度依赖它们同时出现、按特定组合共同完成判断，而不是让每个单元都学到相对独立、稳健的特征。Dropout 随机拿掉其中一部分单元后，网络不能再把能力押注在某一组固定配合上，只能把有用模式分散到更稳健的表示里。令隐藏向量为 <span displaypfx="inline-" class="mathjax-container">\(h\)</span>，mask <span displaypfx="inline-" class="mathjax-container">\(m_i\sim \mathrm{Bernoulli}(p)\)</span>，常见的 inverted dropout 写法为：</p>
<span displaypfx="" class="mathjax-container">\[\tilde h = \frac{m\odot h}{p}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 是保留概率（Keep Probability）。这样推理时可直接使用原网络（不再采样 mask），避免额外缩放。</p>
<div class="blog_h3"><span class="graybg">Batch Normalization</span></div>
<p>Batch Normalization（BatchNorm）在训练时用 mini-batch 的均值/方差做归一化，并学习缩放/平移参数。对特征维上的某个分量 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，典型形式是：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{BN}(x)=\gamma\cdot \frac{x-\mu_{\text{batch}}}{\sqrt{\sigma_{\text{batch}}^2+\epsilon}}+\beta\]</span>
<p>推理阶段通常使用训练过程累积的运行均值（Running Mean）与运行方差（Running Variance），因此训练/推理行为不同。BatchNorm 在 CNN 中极常见，因为卷积特征图在同一通道上的空间位置具有较强同质性，跨样本统计量较容易稳定；但在 Transformer 中，主流做法并不是沿 batch 维做归一化，而是使用与 batch 统计无关的 LayerNorm 或 RMSNorm。</p>
<p>原因在于 Transformer 的基本计算单元是 token 表示。序列长度常常可变，batch size 在训练与推理中也经常变化，尤其大模型训练会受到显存限制而使用较小、波动甚至分布式切分后的 micro-batch。若使用 BatchNorm，某个 token 的归一化结果会显式依赖同一 batch 中其他样本与其他位置的统计量，这会引入跨样本耦合，使训练和推理的数值语义不一致，也不利于自回归解码阶段逐 token 稳定生成。</p>
<div class="blog_h3"><span class="graybg">Layer Normalization</span></div>
<p>Layer Normalization（LayerNorm）对每个样本（或每个 token）的特征维做归一化，不依赖 batch 统计量，因此更适合变长序列与自回归推理。对向量 <span displaypfx="inline-" class="mathjax-container">\(x\in\mathbb{R}^{d}\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{LN}(x)=\gamma\odot \frac{x-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta,\quad \mu=\frac{1}{d}\sum_{i=1}^{d}x_i,\ \sigma^2=\frac{1}{d}\sum_{i=1}^{d}(x_i-\mu)^2\]</span>
<p>这里的均值 <span displaypfx="inline-" class="mathjax-container">\(\mu\)</span> 与方差 <span displaypfx="inline-" class="mathjax-container">\(\sigma^2\)</span> 都只在当前样本、当前 token 的特征维内部计算，因此每个 token 都能独立完成归一化。这个性质与 Transformer 的残差流（Residual Stream）非常匹配：无论 batch 如何变化、序列如何裁剪、推理时是否一次只输入一个 token，归一化规则都保持一致。</p>
<p>当前主流的 Transformer 归一化实践可以概括为两类。Encoder-only 与很多 Vision Transformer（ViT）架构通常沿用 LayerNorm；Decoder-only 大语言模型（Large Language Model, LLM）则大量采用 Pre-Norm 残差块，并进一步把 LayerNorm 简化为 RMSNorm。前者保留去均值与方差缩放，后者只保留尺度归一化，计算更轻，也更适合超大规模训练。</p>
<div class="blog_h3"><span class="graybg">Early Stopping</span></div>
<p>Early Stopping 用验证集指标监控训练过程，在泛化性能不再提升时提前停止，从而避免在训练集上继续拟合噪声。工程上常用“耐心（Patience）”：若验证集指标连续 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 次评估都未改善，则停止训练并回滚到最佳 checkpoint。它属于训练流程级的隐式正则化（Implicit Regularization）。</p>
<div class="blog_h2"><span class="graybg">卷积神经网络（CNN）</span></div>
<div class="blog_h3"><span class="graybg">卷积层与池化层</span></div>
<p>卷积（Convolution）在连续形式下定义为函数重叠积分：</p>
<span displaypfx="" class="mathjax-container">\[(f*g)(t)=\int_{-\infty}^{+\infty} f(\tau)\,g(t-\tau)\,d\tau\]</span>
<p>在离散二维图像中常写为：</p>
<span displaypfx="" class="mathjax-container">\[(I*K)(i,j)=\sum_m\sum_n I(i-m,j-n)\,K(m,n)\]</span>
<p>深度学习框架里多数“卷积层”实现的是互相关（Cross-Correlation）而非严格翻转核的数学卷积，但工程上沿用“卷积”命名。卷积核（Kernel）共享权重（Weight Sharing），天然利用局部性（Locality）与平移等变（Translation Equivariance）。</p>
<p>直观上，卷积层做的事情是：对每个位置取一个局部窗口，把窗口里的像素与卷积核做加权求和，得到该位置的响应。不同卷积核学习不同的局部模式（边缘、纹理、角点等）。</p>
<p>一维互相关示例：输入 <span displaypfx="inline-" class="mathjax-container">\(x=[0,0,1,1,1,0,0]\)</span>，核 <span displaypfx="inline-" class="mathjax-container">\(w=[1,0,-1]\)</span>，则在从左到右滑动时，核会对“上升沿/下降沿”给出大幅响应（本质上是比较左右两侧的差异）。这就是经典的边缘检测直觉在离散信号上的对应。</p>
<p>卷积的“系统视角”能统一理解 CNN 与信号处理：把卷积核看作响应函数（Response Kernel），输出就是对历史输入的加权累积。参见“微积分 ➡ 卷积（Convolution）”中的因果卷积与“打点滴累积药效”直觉例子。</p>
<div class="blog_h3"><span class="graybg">经典架构</span></div>
<div class="blog_h4"><span class="graybg">LeNet</span></div>
<p>LeNet 可以看作现代卷积神经网络（Convolutional Neural Network, CNN）的原型。它面对的是手写数字识别这类“局部笔画决定整体类别”的任务，因此核心思想很直接：先用卷积层提取局部边缘、角点和笔画，再用池化（Pooling）降低分辨率与局部扰动敏感性，最后把高层特征送入全连接层做分类。</p>
<p>经典 LeNet-5 的结构可以概括为“卷积 - 池化 - 卷积 - 池化 - 全连接”。前面的卷积层负责把原始像素变成越来越抽象的特征图（Feature Map），后面的全连接层负责把这些局部特征整合成类别判断。它的重要性不只是“识别数字有效”，更在于证明了三件事可以协同工作：局部感受野（Local Receptive Field）、权重共享（Weight Sharing）和层级特征提取（Hierarchical Feature Learning）。</p>
<p>卷积层里的一个典型计算是：</p>
<span displaypfx="" class="mathjax-container">\[y_{i,j}^{(k)}=\sum_{u}\sum_{v}\sum_{c} W_{u,v,c}^{(k)}\,x_{i+u,j+v,c}+b^{(k)}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是输入图像或上一层特征图， <span displaypfx="inline-" class="mathjax-container">\(W^{(k)}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个卷积核， <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 是输入通道索引， <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 是空间位置， <span displaypfx="inline-" class="mathjax-container">\(y_{i,j}^{(k)}\)</span> 是输出特征图在该位置上的响应。这个公式表达的就是：用同一个卷积核在整张图上滑动，寻找“哪里出现了我关心的局部模式”。</p>
<p>训练上，LeNet 已经体现出现代深度学习的基本闭环：前向传播得到类别 logits，损失函数衡量预测与真实标签的差距，反向传播把梯度传回卷积核和全连接层参数，再用梯度下降更新权重。它最典型的应用是 MNIST 手写数字识别，也常被用作理解 CNN 的第一块教学样板，因为结构短、计算图清晰、局部模式学习的直觉非常强。</p>
<div class="blog_h4"><span class="graybg">AlexNet</span></div>
<p>AlexNet 标志着深度卷积网络在大规模视觉识别上的突破。它面对的不是手写数字这种相对简单的灰度图，而是 ImageNet 级别的彩色自然图像，因此核心思想从“能提特征”进一步推进到“更深、更宽、更可训练”：增加网络容量，用 ReLU（Rectified Linear Unit）加快优化，用 Dropout 缓解过拟合，用数据增强提升泛化，再借助 GPU 让大模型真正训得动。</p>
<p>其典型结构是多层卷积堆叠后接全连接层，早期卷积核较大、步幅较大，用于迅速降采样并提取低层纹理；中后期卷积核变小，重点转向更细粒度的组合特征。AlexNet 还使用了局部响应归一化（Local Response Normalization, LRN）这一今天较少使用、但在当时有历史意义的设计。整体上，它把 CNN 从“可用于小型任务的模型”推向了“可以在大规模视觉基准上碾压传统方法的通用架构”。</p>
<p>它最有代表性的非线性是 ReLU：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{ReLU}(z)=\max(0,z)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 是线性层或卷积层的输出。与 sigmoid / tanh 相比，ReLU 在正半轴不饱和，梯度传播更直接，因此深层网络更容易优化。AlexNet 的历史意义之一，就是把“激活函数的选择会显著影响深网络可训练性”这件事变成了工程共识。</p>
<p>训练上，AlexNet 典型地结合随机裁剪、翻转、颜色扰动等数据增强，并在全连接层使用 Dropout。它的直接应用是大规模图像分类；更深远的影响则是推动了整个视觉领域转向“预训练 CNN + 迁移学习”的工作流。很多后续检测、分割与检索系统，都曾把 AlexNet 当作特征提取骨干网络（Backbone）。</p>
<div class="blog_h4"><span class="graybg">VGG</span></div>
<p>VGG 的核心思想是：用结构极其规整的小卷积核反复堆叠，把网络做深。它放弃了早期“大卷积核 + 大步幅”的粗放设计，转而几乎全程使用 <span displaypfx="inline-" class="mathjax-container">\(3\times 3\)</span> 卷积，通过增加层数来扩大感受野、提高非线性表达能力，并让整套架构在工程上更统一、更易复用。</p>
<p>VGG 的典型结构非常整齐：若干个 <span displaypfx="inline-" class="mathjax-container">\(3\times 3\)</span> 卷积层组成一个 stage，stage 之间通过池化层下采样，最后再接全连接分类头。它的重要工程思想是“深度本身就是能力来源之一”。相较于更杂糅的早期网络，VGG 的层次感非常强：浅层提边缘和纹理，中层提局部部件，高层提更完整的语义结构。</p>
<p>为什么反复使用 <span displaypfx="inline-" class="mathjax-container">\(3\times 3\)</span> 卷积有效，可以从感受野和参数量两方面理解。两个连续的 <span displaypfx="inline-" class="mathjax-container">\(3\times 3\)</span> 卷积，其有效感受野接近一个 <span displaypfx="inline-" class="mathjax-container">\(5\times 5\)</span> 卷积，但中间多了一次非线性变换，参数量通常还更少。若忽略通道数变化，单层 <span displaypfx="inline-" class="mathjax-container">\(5\times 5\)</span> 卷积大约有 25 个核参数，而两层 <span displaypfx="inline-" class="mathjax-container">\(3\times 3\)</span> 卷积一共是 18 个核参数。</p>
<p>训练上，VGG 比 AlexNet 更依赖较好的初始化、较强的正则化和更大的算力预算，因为它的参数量尤其在全连接部分非常大。它的直接应用是图像分类，但工程上更著名的是作为“通用视觉特征提取器”：在风格迁移、感知损失（Perceptual Loss）、检测与分割早期系统中，VGG 特征长期是强基线。它的代价也很明显：参数多、推理重、显存占用高，这直接推动了后续更高效架构的出现。</p>
<div class="blog_h4"><span class="graybg">ResNet</span></div>
<p>ResNet（Residual Network）的关键突破不是再把卷积堆得更深，而是重新设计“深层网络应该学什么”。它提出残差学习（Residual Learning）：一层或一组层不必直接学习完整映射 <span displaypfx="inline-" class="mathjax-container">\(H(x)\)</span>，而是学习相对于输入的增量 <span displaypfx="inline-" class="mathjax-container">\(F(x)=H(x)-x\)</span>。这样网络输出就写成：</p>
<span displaypfx="" class="mathjax-container">\[y=F(x)+x\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是块输入， <span displaypfx="inline-" class="mathjax-container">\(F(x)\)</span> 是若干卷积层、归一化和激活组成的残差分支输出， <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是该残差块的最终输出。若最优映射本身就接近恒等映射（Identity Mapping），那么让网络学习“只改一点点”通常比“从零重建整个映射”更容易优化。</p>
<p>具象地看，普通深网络像要求每一层都重新写一遍完整答案；ResNet 则允许每一层在原答案旁边写批注：需要修改的地方补上增量，不需要改的地方直接走捷径。这个“捷径连接（Skip Connection）”让梯度能沿更短路径传播，因此网络深到几十层、上百层时仍可训练。ResNet 解决的核心不是表达能力不足，而是深层优化退化（Degradation）问题：层数增加后，训练误差反而上升。</p>
<p>训练上，ResNet 通常结合 BatchNorm、较深的 stage 结构和全局平均池化（Global Average Pooling）来替代庞大的全连接头。它在图像分类上大获成功后，很快成为检测、分割、关键点、视频理解等视觉任务的主流骨干网络。更深远的影响是：残差连接后来成为 Transformer、扩散模型和许多现代深网络的标准部件，因为它本质上是在为深层优化建立稳定的信息主通路。</p>
<div class="blog_h2"><span class="graybg">循环神经网络（RNN）</span></div>
<div class="blog_h3"><span class="graybg">RNN</span></div>
<p>循环神经网络（Recurrent Neural Network, RNN）按时间步（Time Step）处理序列。第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个时刻输入是 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span>，隐藏状态（Hidden State）递推为：</p>
<span displaypfx="" class="mathjax-container">\[h_t=\phi(W_{xh}x_t+W_{hh}h_{t-1}+b_h),\quad y_t=W_{hy}h_t+b_y\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(W_{xh}\)</span>（输入到隐层）与 <span displaypfx="inline-" class="mathjax-container">\(W_{hh}\)</span>（隐层到隐层）在所有时刻共享，这是 RNN 能在变长序列上泛化的关键。</p>
<p>把它展开（Unroll）到时间轴上会更直观：同一套参数在每个时间步重复使用，形成一个深度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 的计算图。这也是 RNN 训练常说的“通过时间的反向传播（Backpropagation Through Time, BPTT）”。</p>
<p>RNN 的经典难点是梯度消失/爆炸（Vanishing/Exploding Gradients）：反向传播时会反复乘以 <span displaypfx="inline-" class="mathjax-container">\(W_{hh}\)</span> 的雅可比，从而导致梯度范数指数级衰减或增长。LSTM/GRU 通过门控（Gating）与更“线性”的记忆通道缓解这一问题。</p>
<p>Seq2Seq（Sequence-to-Sequence）是任务范式，不限定具体单元。早期 Seq2Seq 常由 RNN/LSTM/GRU 的编码器-解码器（Encoder-Decoder）实现；后续被 Transformer 大规模替代。</p>
<p>“乘法 + 加法 + 非线性”为何有效：线性变换负责特征重表达，非线性激活（Nonlinearity）提供函数逼近能力，时间递推提供记忆路径，三者叠加形成高表达力。</p>
<div class="blog_h3"><span class="graybg">LSTM</span></div>
<p>LSTM（Long Short-Term Memory）是为了解决普通 RNN 难以稳定保留长程信息的问题而设计的门控循环结构。它的核心思想不是简单把隐藏状态不断往前传，而是显式维护一个记忆单元（Cell State） <span displaypfx="inline-" class="mathjax-container">\(c_t\)</span>，并用门控决定“忘掉什么、写入什么、读出什么”。这使得序列中的关键信息可以沿着一条更接近线性的通道向后传播，而不必在每个时间步都被强非线性反复改写。</p>
<p>LSTM 的典型更新写成：</p>
<span displaypfx="" class="mathjax-container">\[f_t=\sigma(W_f x_t+U_f h_{t-1}+b_f),\quad i_t=\sigma(W_i x_t+U_i h_{t-1}+b_i)\]</span>
<span displaypfx="" class="mathjax-container">\[\tilde c_t=\tanh(W_c x_t+U_c h_{t-1}+b_c),\quad c_t=f_t\odot c_{t-1}+i_t\odot \tilde c_t\]</span>
<span displaypfx="" class="mathjax-container">\[o_t=\sigma(W_o x_t+U_o h_{t-1}+b_o),\quad h_t=o_t\odot \tanh(c_t)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 是当前输入， <span displaypfx="inline-" class="mathjax-container">\(h_{t-1}\)</span> 是前一时刻隐藏状态， <span displaypfx="inline-" class="mathjax-container">\(c_{t-1}\)</span> 是前一时刻记忆单元； <span displaypfx="inline-" class="mathjax-container">\(f_t\)</span> 是遗忘门（Forget Gate），控制旧记忆保留多少； <span displaypfx="inline-" class="mathjax-container">\(i_t\)</span> 是输入门（Input Gate），控制当前候选记忆 <span displaypfx="inline-" class="mathjax-container">\(\tilde c_t\)</span> 写入多少； <span displaypfx="inline-" class="mathjax-container">\(o_t\)</span> 是输出门（Output Gate），控制当前记忆暴露给隐藏状态多少； <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 是 sigmoid，把门值压到 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 区间； <span displaypfx="inline-" class="mathjax-container">\(\odot\)</span> 是逐元素乘法。</p>
<p>具象地看，LSTM 像一条带阀门的记忆水管。普通 RNN 每走一步都把旧信息和新输入一锅重拌，长距离信息容易被冲淡；LSTM 则允许旧记忆沿主管道直接往后流，同时用三个阀门分别控制“放掉旧水”“注入新水”“输出多少”。这正是它能比普通 RNN 更稳定地记住长距离依赖的原因。</p>
<p>训练上，LSTM 仍然通过时间反向传播（BPTT）学习参数，但门控结构显著改善了梯度流。它曾长期是机器翻译、语音识别、语言建模、时间序列预测和序列标注的主力模型。在 Transformer 出现之前，大量 Seq2Seq 系统都建立在双向 LSTM 或多层 LSTM 之上；在某些中小规模时序任务里，LSTM 今天仍然是强而稳的基线。</p>
<div class="blog_h3"><span class="graybg">GRU</span></div>
<p>GRU（Gated Recurrent Unit）可以看作 LSTM 的简化版本。它保留了“用门控控制信息保留与更新”的核心思想，但把记忆单元与隐藏状态合并，不再单独维护 <span displaypfx="inline-" class="mathjax-container">\(c_t\)</span>，从而用更少参数换取更紧凑的结构与更快的训练速度。</p>
<p>GRU 的一组典型公式是：</p>
<span displaypfx="" class="mathjax-container">\[z_t=\sigma(W_z x_t+U_z h_{t-1}+b_z),\quad r_t=\sigma(W_r x_t+U_r h_{t-1}+b_r)\]</span>
<span displaypfx="" class="mathjax-container">\[\tilde h_t=\tanh(W_h x_t+U_h(r_t\odot h_{t-1})+b_h)\]</span>
<span displaypfx="" class="mathjax-container">\[h_t=(1-z_t)\odot h_{t-1}+z_t\odot \tilde h_t\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(z_t\)</span> 是更新门（Update Gate），决定旧状态保留多少、新候选状态写入多少； <span displaypfx="inline-" class="mathjax-container">\(r_t\)</span> 是重置门（Reset Gate），决定在构造候选状态 <span displaypfx="inline-" class="mathjax-container">\(\tilde h_t\)</span> 时，历史信息应当被参考到什么程度。若 <span displaypfx="inline-" class="mathjax-container">\(z_t\)</span> 很小，模型更倾向保留旧记忆；若很大，模型更倾向用新信息刷新状态。</p>
<p>从直觉上看，GRU 把 LSTM 的三道阀门压缩成两道更紧凑的控制逻辑：一方面决定“要不要更新”，另一方面决定“生成候选更新时要不要忘掉旧状态的一部分”。这让它在很多任务上能以更少参数达到与 LSTM 相近的效果，尤其适合数据量不极大、模型容量受限或推理效率敏感的场景。</p>
<p>训练上，GRU 与 LSTM 一样使用 BPTT。应用上，它常见于语音、时序预测、较轻量的编码器-解码器模型，以及很多工业界的表格时间序列任务。若需要更强的显式记忆控制，LSTM 往往更稳；若更看重结构简洁和训练效率，GRU 常是更自然的选择。</p>
<div class="blog_h2"><span class="graybg">生成模型</span></div>
<p>生成模型（Generative Model）关注的核心问题不是“这个样本属于哪一类”，而是“数据本身是如何产生出来的”。更形式化地说，它试图学习数据分布 <span displaypfx="inline-" class="mathjax-container">\(p(x)\)</span>，或条件分布 <span displaypfx="inline-" class="mathjax-container">\(p(x|c)\)</span>，从而能够采样、重建、补全、去噪或按条件生成新样本。不同生成模型的主要差别在于它们选择了不同的概率建模路径：有的学隐变量，有的学博弈过程，有的学逐步去噪，有的更偏重表示压缩。</p>
<div class="blog_h3"><span class="graybg">自编码器（AE）</span></div>
<p>自编码器（Autoencoder, AE）的核心思想是“先压缩，再重建”。它把输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 通过编码器（Encoder）映射到低维或受约束的隐表示（Latent Representation） <span displaypfx="inline-" class="mathjax-container">\(z\)</span>，再通过解码器（Decoder）把 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 重建回 <span displaypfx="inline-" class="mathjax-container">\(\hat x\)</span>。如果模型在受限瓶颈下仍能较好重建输入，就说明 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 抓住了数据中的关键结构。</p>
<p>其基本形式可以写成：</p>
<span displaypfx="" class="mathjax-container">\[z=f_{\theta}(x),\qquad \hat x=g_{\phi}(z)\]</span>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{AE}}=\|x-\hat x\|_2^2\quad \text{或}\quad -\sum_i x_i\log \hat x_i\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(f_{\theta}\)</span> 是编码器， <span displaypfx="inline-" class="mathjax-container">\(g_{\phi}\)</span> 是解码器， <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 是瓶颈表示， <span displaypfx="inline-" class="mathjax-container">\(\hat x\)</span> 是重建结果。若输入是连续值，常用均方误差；若输入近似二值或归一化到概率意义，常用逐维交叉熵。这个目标迫使模型学习“怎样用更紧凑的表示保存足够重建原样本的信息”。</p>
<p>自编码器本身并不天然是强生成模型，因为它主要学会的是“给定输入怎么重建自己”，而不是如何从一个规则、可采样的潜空间中稳定地产生新样本。它更适合理解为表示学习或降维模型。通过在结构或训练目标上增加限制，例如稀疏自编码器（Sparse AE）、去噪自编码器（Denoising AE）或收缩自编码器（Contractive AE），它可以学到更稳健的隐表示。</p>
<p>应用上，AE 常用于降维、异常检测、去噪、预训练和表征学习。例如在工业异常检测里，模型只用正常样本训练，推理时若某个输入无法被良好重建，重建误差就可能提示该样本偏离了正常分布。</p>
<div class="blog_h3"><span class="graybg">变分自编码器（VAE）</span></div>
<p>变分自编码器（Variational Autoencoder, VAE）是在 AE 基础上把“隐空间可采样”这件事做成概率建模的方案。它不再把编码器输出一个确定的潜向量，而是输出一个潜变量分布 <span displaypfx="inline-" class="mathjax-container">\(q_{\phi}(z|x)\)</span>；同时假设存在生成分布 <span displaypfx="inline-" class="mathjax-container">\(p_{\theta}(x|z)\)</span>。这样，模型既能重建输入，又能从一个规则的潜空间里采样新样本。</p>
<p>VAE 的核心训练目标是证据下界（Evidence Lower Bound, ELBO）：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{VAE}}=\mathbb{E}_{q_{\phi}(z|x)}[\log p_{\theta}(x|z)]-D_{\mathrm{KL}}\big(q_{\phi}(z|x)\,\|\,p(z)\big)\]</span>
<p>其中第一项是重建项，鼓励给定 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 时能把 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 生成回来；第二项是 KL 散度（Kullback-Leibler Divergence），把编码器输出的后验近似分布 <span displaypfx="inline-" class="mathjax-container">\(q_{\phi}(z|x)\)</span> 拉向先验分布 <span displaypfx="inline-" class="mathjax-container">\(p(z)\)</span>，通常取标准高斯 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}(0,I)\)</span>。这一步的作用是把隐空间整理成“规则、连续、可插值、可采样”的形状。</p>
<p>训练上的关键技巧是重参数化（Reparameterization Trick）：若直接从 <span displaypfx="inline-" class="mathjax-container">\(q_{\phi}(z|x)\)</span> 采样，梯度难以回传；VAE 通常把采样写成 <span displaypfx="inline-" class="mathjax-container">\(z=\mu+\sigma\odot \epsilon\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\sim\mathcal{N}(0,I)\)</span>，而 <span displaypfx="inline-" class="mathjax-container">\(\mu,\sigma\)</span> 由编码器输出。这样随机性被转移到与参数无关的 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 上，梯度就能顺利穿过 <span displaypfx="inline-" class="mathjax-container">\(\mu,\sigma\)</span> 回传。</p>
<p>应用上，VAE 特别适合做潜空间操作，例如插值生成、属性控制、缺失补全和概率建模。与 GAN 相比，VAE 生成结果往往更平滑、更稳定，但图像清晰度常较弱；与扩散模型相比，VAE 在采样效率上更高，但生成质量上通常不是最强路线。</p>
<div class="blog_h3"><span class="graybg">生成对抗网络（GAN）</span></div>
<p>生成对抗网络（Generative Adversarial Network, GAN）的核心思想是对抗式学习：让生成器（Generator）负责“伪造样本”，让判别器（Discriminator）负责“分辨真假”，两者在博弈中共同提高。生成器学会把随机噪声变成越来越像真实数据的样本，判别器则学会识别这些样本是否来自真实分布。</p>
<p>经典 GAN 的目标写成：</p>
<span displaypfx="" class="mathjax-container">\[\min_G\max_D\ V(D,G)=\mathbb{E}_{x\sim p_{\mathrm{data}}}[\log D(x)]+\mathbb{E}_{z\sim p(z)}[\log(1-D(G(z)))] \]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 是从先验分布中采样的随机噪声， <span displaypfx="inline-" class="mathjax-container">\(G(z)\)</span> 是生成器产生的假样本， <span displaypfx="inline-" class="mathjax-container">\(D(x)\)</span> 输出输入样本为真样本的概率。判别器希望让真实样本分数高、伪样本分数低；生成器则希望骗过判别器，让 <span displaypfx="inline-" class="mathjax-container">\(G(z)\)</span> 看起来足够真实。</p>
<p>具象地看，GAN 像“伪造者”和“鉴定师”的对抗升级。鉴定师越强，伪造者就被迫学会更精细的伪造技巧；伪造者越强，鉴定师也必须学会更细致的辨别规则。理论上，这种动态会把生成分布推向真实数据分布；工程上，它带来的最大问题是训练不稳定，常见现象包括模式崩塌（Mode Collapse）、震荡和判别器过强导致生成器梯度过弱。</p>
<p>GAN 在图像生成、图像翻译、超分辨率和风格迁移里曾经极其成功，因为它特别善于生成锐利、感知上真实的图像纹理。但它对训练技巧依赖很强，后来在大规模高保真图像生成上逐渐被扩散模型压过；即便如此，GAN 在需要低步数快速生成或特定视觉变换任务中仍然很有生命力。</p>
<div class="blog_h3"><span class="graybg">扩散模型（Diffusion）</span></div>
<p>扩散模型（Diffusion Model）的核心思想是：把“直接学会生成复杂数据”拆成“先逐步加噪，再逐步去噪”这条更稳定的路径。前向过程把真实样本一步步污染成近似高斯噪声，反向过程则训练一个神经网络学会在每一步去掉一小部分噪声。最终从纯噪声出发，经过多步反向去噪，就能逐步生成结构清晰的样本。</p>
<p>记号上，前向扩散链从真实样本 <span displaypfx="inline-" class="mathjax-container">\(x_0\)</span> 出发，依次得到 <span displaypfx="inline-" class="mathjax-container">\(x_1,x_2,\dots,x_T\)</span>。因此 <span displaypfx="inline-" class="mathjax-container">\(x_0\)</span> 表示原始数据样本， <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步加噪后的样本。</p>
<p>一个常见的前向加噪过程写成：</p>
<span displaypfx="" class="mathjax-container">\[q(x_t|x_{t-1})=\mathcal{N}\!\left(x_t;\sqrt{1-\beta_t}\,x_{t-1},\beta_t I\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\beta_t\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步的噪声强度：它控制从 <span displaypfx="inline-" class="mathjax-container">\(x_{t-1}\)</span> 走到 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 时，原信号衰减多少、随机噪声注入多少。训练时常把从 <span displaypfx="inline-" class="mathjax-container">\(x_0\)</span> 到 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 的若干步合并写成</p>
<span displaypfx="" class="mathjax-container">\[x_t=\sqrt{\bar\alpha_t}\,x_0+\sqrt{1-\bar\alpha_t}\,\epsilon,\qquad \epsilon\sim\mathcal{N}(0,I)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\bar\alpha_t\)</span> 是由噪声日程（Noise Schedule）累乘得到的系数，控制到第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步时，原始信号保留了多少、噪声混入了多少。实际训练中，网络通常不直接预测 <span displaypfx="inline-" class="mathjax-container">\(x_0\)</span>，而是预测噪声 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span>，典型损失是：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{diff}}=\mathbb{E}\big[\|\epsilon-\epsilon_{\theta}(x_t,t)\|_2^2\big]\]</span>
<p>这条路径之所以有效，在于“预测一步噪声”比“直接一次生成整张复杂图片”更容易优化。训练上，扩散模型相对稳定，较少出现 GAN 那种对抗不平衡；代价是采样通常需要多步迭代，推理速度较慢。应用上，扩散模型已经成为图像生成、文生图、图像编辑、超分辨率、视频生成和分子设计的重要主线。Stable Diffusion 一类系统，本质上就是把扩散过程放到了潜空间（Latent Space）里执行，以降低像素空间扩散的计算成本。</p>
<div class="blog_h2"><span class="graybg">图神经网络（GNN）</span></div>
<p>图神经网络（Graph Neural Network, GNN）处理的对象不是规则网格上的序列或图像，而是由节点（Node）和边（Edge）组成的图（Graph）。它的核心问题是：当样本之间存在不规则连接关系时，如何让一个节点的表示同时反映“自己的特征”和“邻居结构中的上下文”。因此，GNN 的基本直觉不是滑动卷积核，也不是按时间递推，而是让节点在图上传递消息、聚合邻居信息，再更新自身表示。</p>
<div class="blog_h3"><span class="graybg">图卷积网络（GCN）</span></div>
<p>图卷积网络（Graph Convolutional Network, GCN）把卷积思想推广到图结构上。它的核心思想是：一个节点的新表示，不应只由自己决定，还应由其邻居节点的表示共同决定；但这种聚合不能简单相加，而需要根据图结构做适当归一化，否则高度节点会在信息聚合中占据过大权重。</p>
<p>GCN 的经典一层更新公式写成：</p>
<span displaypfx="" class="mathjax-container">\[H^{(l+1)}=\sigma\!\left(\tilde D^{-1/2}\tilde A\tilde D^{-1/2}H^{(l)}W^{(l)}\right)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(H^{(l)}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层所有节点的表示矩阵； <span displaypfx="inline-" class="mathjax-container">\(W^{(l)}\)</span> 是该层可学习权重； <span displaypfx="inline-" class="mathjax-container">\(\tilde A=A+I\)</span> 表示在原邻接矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 上加自环（Self-loop），让节点保留自己的信息； <span displaypfx="inline-" class="mathjax-container">\(\tilde D\)</span> 是 <span displaypfx="inline-" class="mathjax-container">\(\tilde A\)</span> 的度矩阵（Degree Matrix）； <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 是非线性激活。中间那项对邻居求和并按度做归一化，本质上是在做“平滑的邻居平均”。</p>
<p>具象地看，GCN 像在社交网络里更新一个人的画像：不仅看这个人自己填写的特征，还参考他一跳邻居的大致特征，再做归一化，避免“朋友特别多的人”把自己的表示稀释得过于严重。多层堆叠后，一个节点就能间接接触到两跳、三跳甚至更远范围的信息。</p>
<p>训练上，GCN 常用于节点分类、图分类和链路预测。它的经典应用包括引文网络分类、分子图预测和推荐系统图表示学习。局限也很典型：层数太深时，节点表示会越来越相似，出现过平滑（Oversmoothing）；大图上直接用全图邻接矩阵训练也会带来显存与计算压力。</p>
<div class="blog_h3"><span class="graybg">图注意力网络（GAT）</span></div>
<p>图注意力网络（Graph Attention Network, GAT）在 GCN 的“统一归一化邻居平均”之上进一步提出：不同邻居的重要性不应被预先固定，而应由模型动态学习。它的核心思想是把注意力机制引入图结构，使每个节点在聚合邻居时，能够自适应决定“更该听谁的话”。</p>
<p>一层 GAT 的典型计算是：</p>
<span displaypfx="" class="mathjax-container">\[e_{ij}=a(Wh_i,Wh_j),\qquad \alpha_{ij}=\frac{\exp(e_{ij})}{\sum_{k\in \mathcal{N}(i)}\exp(e_{ik})}\]</span>
<span displaypfx="" class="mathjax-container">\[h_i'=\sigma\!\left(\sum_{j\in \mathcal{N}(i)} \alpha_{ij}\,Wh_j\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(h_i\)</span> 是节点 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 的输入表示， <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 是线性变换矩阵， <span displaypfx="inline-" class="mathjax-container">\(a(\cdot,\cdot)\)</span> 是注意力打分函数， <span displaypfx="inline-" class="mathjax-container">\(e_{ij}\)</span> 是节点 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 对节点 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 的未归一化重要性分数， <span displaypfx="inline-" class="mathjax-container">\(\alpha_{ij}\)</span> 是在邻居集合 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}(i)\)</span> 内 softmax 归一化后的注意力权重。</p>
<p>与 GCN 相比，GAT 的关键收益是更灵活：若某个邻居特别重要，模型可以给它更高权重；若某个邻居噪声较大，则可自动抑制。训练上，GAT 仍通过监督或自监督目标学习节点表示，常用多头注意力（Multi-head Attention）稳定训练。应用上，它常见于异质关系更复杂、邻居重要性差异显著的图任务，例如社交网络分析、知识图谱局部推断和分子性质预测。</p>
<div class="blog_h3"><span class="graybg">GraphSAGE</span></div>
<p>GraphSAGE（Graph Sample and Aggregate）的核心思想是把 GNN 从“转导式（Transductive）图编码”推进到“归纳式（Inductive）图表示学习”。传统 GCN 常依赖整张训练图；GraphSAGE 则强调：即使测试时出现训练中未见过的新节点，只要能拿到它的邻居特征，也应能在线生成它的表示。</p>
<p>其典型更新形式是先采样邻居，再做聚合：</p>
<span displaypfx="" class="mathjax-container">\[h_{\mathcal{N}(v)}^{(k)}=\mathrm{AGG}^{(k)}\big(\{h_u^{(k-1)}:u\in \mathcal{N}(v)\}\big)\]</span>
<span displaypfx="" class="mathjax-container">\[h_v^{(k)}=\sigma\!\left(W^{(k)}[h_v^{(k-1)}\|h_{\mathcal{N}(v)}^{(k)}]\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(h_v^{(k)}\)</span> 是节点 <span displaypfx="inline-" class="mathjax-container">\(v\)</span> 在第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 层的表示， <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}(v)\)</span> 是其邻居集合， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{AGG}\)</span> 可以是均值、池化或 LSTM 聚合器， <span displaypfx="inline-" class="mathjax-container">\([\cdot\|\cdot]\)</span> 表示向量拼接。GraphSAGE 的关键工程点是“采样”，因为超大图中不可能每次把全部邻居完整展开。</p>
<p>具象地看，GraphSAGE 像为每个节点建立一套“从邻居摘要中构造自我画像”的规则。它不要求记住整张训练图中每个节点的专属嵌入，而是学会一套可迁移的邻域聚合函数。这正是它能处理新节点、动态图和大规模图数据的原因。</p>
<p>应用上，GraphSAGE 在推荐系统、社交网络、风控图谱和工业知识图谱中非常常见，因为这些场景经常不断出现新节点、新边，归纳式能力比单纯在固定图上做转导预测更重要。</p>
<div class="blog_h3"><span class="graybg">消息传递机制（Message Passing）</span></div>
<p>消息传递（Message Passing）不是某一个具体模型，而是理解 GNN 的统一抽象框架。无论是 GCN、GAT 还是 GraphSAGE，本质上都可以拆成两步：第一步，节点从邻居那里接收消息；第二步，把这些消息与自己的旧表示结合，更新成新的节点表示。</p>
<p>这一抽象常写成：</p>
<span displaypfx="" class="mathjax-container">\[m_v^{(l+1)}=\mathrm{AGG}\Big(\{M^{(l)}(h_v^{(l)},h_u^{(l)},e_{uv})\,:\,u\in\mathcal{N}(v)\}\Big)\]</span>
<span displaypfx="" class="mathjax-container">\[h_v^{(l+1)}=U^{(l)}\big(h_v^{(l)},m_v^{(l+1)}\big)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(h_v^{(l)}\)</span> 是节点 <span displaypfx="inline-" class="mathjax-container">\(v\)</span> 在第 <span displaypfx="inline-" class="mathjax-container">\(l\)</span> 层的表示， <span displaypfx="inline-" class="mathjax-container">\(e_{uv}\)</span> 是边特征， <span displaypfx="inline-" class="mathjax-container">\(M^{(l)}\)</span> 是消息函数，决定一条边上传递什么信息； <span displaypfx="inline-" class="mathjax-container">\(\mathrm{AGG}\)</span> 是聚合函数，如求和、均值、最大值或注意力加权和； <span displaypfx="inline-" class="mathjax-container">\(U^{(l)}\)</span> 是更新函数，把旧表示与聚合后的消息合成为新表示。</p>
<p>这个框架的重要性在于，它把看似不同的图模型放进了一套统一语言里：GCN 相当于使用归一化线性消息与均值式聚合，GAT 相当于把注意力权重写进聚合，GraphSAGE 相当于强调采样与归纳式聚合。理解了消息传递，就能把很多图模型看成“消息函数、聚合函数、更新函数”三处设计选择的不同组合。</p>
<p>训练上，消息传递式 GNN 常用于三类任务：节点级任务（例如节点分类）、边级任务（例如链路预测）、图级任务（例如分子性质预测）。它们的共同难点包括：过平滑、邻居爆炸（Neighborhood Explosion）、异质图关系复杂，以及深层堆叠后长程依赖难以稳定传播。很多现代 GNN 改进，本质上都在围绕这几个瓶颈重新设计消息传递规则。</p>
<div class="blog_h1"><span class="graybg">Transformers</span></div>
<div class="blog_h2"><span class="graybg">概述</span></div>
<p>Transformer 是现代大模型最核心的统一架构。它最初被提出用于序列到序列（Sequence-to-Sequence）任务，但很快演化成大语言模型（Large Language Model, LLM）、视觉 Transformer、多模态模型以及各类基础模型（Foundation Model）的共同骨架。它之所以重要，不只是因为“效果好”，更因为它提供了一种高度模块化、可并行扩展、易于堆叠放大的建模方式：输入被表示成一串 token，对这些 token 的关系建模主要依赖注意力（Attention），而每一层又通过前馈网络（Feed-Forward Network, FFN / MLP）继续做非线性变换与特征重组。</p>
<p>从工程角度看，Transformer 的成功来自三件事的结合：第一，注意力机制让模型能直接建模长距离依赖，而不必像循环网络那样逐步传递状态；第二，层与层之间结构统一，非常适合在 GPU / TPU 上做大规模并行训练；第三，模型规模可以沿着层数、隐藏维度、注意力头数、词表大小与训练数据量持续扩展，于是它天然适合作为“可放大”的通用架构。</p>
<p>因此，理解 Transformer 不应只停留在“注意力公式怎么写”，还要把它看成一条完整的信息处理流水线：token 如何变成向量，向量如何在注意力里彼此通信，MLP 如何重组和放大模式，残差流（Residual Stream）如何把各层计算串接起来，最后这些中间表示又如何被任务头（Task Head）读出，变成分类结果、生成 token 或其他下游输出。<a href="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers.webp"><img class="alignnone size-large wp-image-41705" src="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers.webp" alt="transformers" width="1" height="1" /></a></p>
<p>&nbsp;</p>
<div class="blog_h3"><span class="graybg">整体架构</span></div>
<p>Transformer 的“基本计算单元”是一个 Transformer block：把注意力子层（Attention Sublayer）与前馈子层（FFN Sublayer）串联起来，并在每个子层外包一层残差连接（Residual Connection）与归一化（Normalization）。注意力子层的输出不是最终预测，而是作为中间表示继续送入 FFN 与下一层 Transformer block，逐层构建更抽象的特征。</p>
<p>典型层结构（概念上）可以写成：</p>
<span displaypfx="" class="mathjax-container">\[H'=\mathrm{Add\&amp;Norm}(H,\ \mathrm{Attention}(H)),\quad H^{\text{next}}=\mathrm{Add\&amp;Norm}(H',\ \mathrm{FFN}(H'))\]</span>
<p>这条式子描述的是一个 Transformer block 内部最核心的两步。这里 <span displaypfx="inline-" class="mathjax-container">\(H\)</span> 表示进入当前层的隐藏状态矩阵（Hidden States），形状通常是 <span displaypfx="inline-" class="mathjax-container">\(L\;\times d_{\text{model}}\)</span>： <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 是序列长度， <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 是每个 token 的隐藏维度。 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Attention}(H)\)</span> 表示注意力子层对整段序列做一次“彼此通信”后的结果：每个 token 会结合其他位置的信息，得到新的上下文化表示。</p>
<p>第一步 <span displaypfx="inline-" class="mathjax-container">\(H'=\mathrm{Add\&amp;Norm}(H,\ \mathrm{Attention}(H))\)</span> 中，<span style="background-color: #c0c0c0;">Add</span> 表示残差相加：把原输入 <span displaypfx="inline-" class="mathjax-container">\(H\)</span> 与注意力输出相加；<span style="background-color: #c0c0c0;">Norm</span> 表示再做归一化（通常是 LayerNorm）。残差的作用是保留原始信息并让梯度更容易穿过深层网络，归一化的作用是让数值尺度更稳定。经过这一步后，得到的 <span displaypfx="inline-" class="mathjax-container">\(H'\)</span> 可以理解为“已经完成一次上下文交互”的中间表示。</p>
<p>第二步 <span displaypfx="inline-" class="mathjax-container">\(H^{\text{next}}=\mathrm{Add\&amp;Norm}(H',\ \mathrm{FFN}(H'))\)</span> 则把 <span displaypfx="inline-" class="mathjax-container">\(H'\)</span> 送入前馈网络（Feed-Forward Network, FFN）。FFN 不负责 token 之间的信息交换，而是对每个位置的向量分别做非线性变换与特征重组。它更像是在每个 token 内部重新编码：放大有用模式、抑制无关模式，并把低层线索组合成更抽象的表示。再经过一次“残差相加 + 归一化”后，输出 <span displaypfx="inline-" class="mathjax-container">\(H^{\text{next}}\)</span>，作为下一层 Transformer block 的输入。</p>
<p>因此，这个公式的阅读顺序可以概括为：先让 token 之间通过注意力交换信息，再让每个 token 自己通过 FFN 重组特征。多层堆叠之后，模型就会沿着这条路径逐层把原始输入变成越来越适合任务头读取的高层表示。</p>
<p>这里还需要区分 Pre-LN（Pre-LayerNorm）与 Post-LN（Post-LayerNorm）。它们的区别在于 <span style="background-color: #c0c0c0;">LayerNorm 放在子层计算之前，还是放在残差相加之后</span>。</p>
<p>若是 Post-LN，概念上更接近前面那条写法：先做子层计算，再与输入做残差相加，最后归一化。例如注意力子层可写成 <span displaypfx="inline-" class="mathjax-container">\(H'=\mathrm{LN}(H+\mathrm{Attention}(H))\)</span>。若是 Pre-LN，则顺序改成“先归一化，再做子层计算，再走残差”：注意力子层更接近 <span displaypfx="inline-" class="mathjax-container">\(H'=H+\mathrm{Attention}(\mathrm{LN}(H))\)</span>，FFN 子层同理。</p>
<p>两者表达的功能主线相同：信息都要经过注意力与 FFN，再靠残差流向后传递。差异主要体现在训练动力学（Training Dynamics）上。Post-LN 更贴近原始 Transformer 论文的写法，直观上像“每次子层更新完，再把结果规范一下”；Pre-LN 则让梯度更容易沿残差路径稳定传播，因此在很深的大模型里更常见。工程实现会在 Pre-LN / Post-LN 之间选择，这会影响训练稳定性、学习率可用范围以及深层可训练性，但不会改变我们对 block 主流程的理解：<span style="background-color: #c0c0c0;">注意力负责跨 token 交互，FFN 负责单 token 特征重组，残差负责让信息与梯度顺畅穿层流动</span>。</p>
<p>Transformer 这个名字源自 “Attention Is All You Need” 论文：模型不再依赖循环结构来处理序列，而是通过注意力把序列表示不断变换（Transform）为更适合预测的表征。</p>
<p>Transformer 的参数（Parameters）不是单一矩阵，而是一组可学习张量的集合，主要包括嵌入（Embedding）、注意力投影（Attention Projections）、前馈网络（FFN）以及归一化的缩放/平移参数等。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">参数组</td>
<td style="text-align: center;">符号</td>
<td style="width: 35%; text-align: center;">典型形状（Typical Shape）</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>Token Embedding</td>
<td><span displaypfx="inline-" class="mathjax-container">\(E\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{V\;\times d_{\text{model}}}\)</span></td>
<td>词表大小 <span displaypfx="inline-" class="mathjax-container">\(V\)</span>；常与输出头权重共享（Weight Tying）。</td>
</tr>
<tr>
<td>位置嵌入（Learned）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(P\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{L_{\max}\;\times d_{\text{model}}}\)</span></td>
<td>仅当使用可学习绝对位置嵌入时存在；正弦位置编码无此参数。</td>
</tr>
<tr>
<td>注意力投影</td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_Q,W_K,W_V\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{d_{\text{model}}\;\times d_{\text{model}}}\)</span></td>
<td>实现上常把多头合并成一次线性投影，等价于 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{d_{\text{model}}\;\times (H d_k)}\)</span>。</td>
</tr>
<tr>
<td>注意力输出投影</td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_O\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{d_{\text{model}}\;\times d_{\text{model}}}\)</span></td>
<td>对拼接后的多头输出做线性混合；并非 <span displaypfx="inline-" class="mathjax-container">\(H\;\times d_v\;\times d_{\text{model}}\)</span> 的三维张量。</td>
</tr>
<tr>
<td>FFN</td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_1,W_2\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_1\in\mathbb{R}^{d_{\text{model}}\;\times d_{\text{ff}}},\ W_2\in\mathbb{R}^{d_{\text{ff}}\;\times d_{\text{model}}}\)</span></td>
<td>通常 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{ff}}\gg d_{\text{model}}\)</span>。</td>
</tr>
<tr>
<td>LayerNorm</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\gamma,\beta\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{d_{\text{model}}}\)</span></td>
<td>每个 LayerNorm 有一组缩放与平移参数。</td>
</tr>
<tr>
<td>输出头（LM Head）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab}},b\)</span></td>
<td><span displaypfx="inline-" class="mathjax-container">\(W_{\text{vocab}}\in\mathbb{R}^{d_{\text{model}}\;\times V}\)</span></td>
<td>把隐藏状态映射为词表 logits；常与 <span displaypfx="inline-" class="mathjax-container">\(E\)</span> 共享权重。</td>
</tr>
</tbody>
</table>
<p>不同 Transformer 变体在维度设置上差异很大。下面列的是几类典型公开模型的常见配置，既包括中等尺寸的主流模型，也包括 2025 到 2026 年仍处前沿位置的开源大模型。它们的共同点在于：即使是“中等尺寸”的主流模型，隐藏维度、层数、头数和 FFN 宽度也已经足够大；而到了开源前沿模型阶段，参数扩展往往不再只靠加深层数，而是同时叠加更宽的隐藏维度、更大的 FFN、MoE（Mixture of Experts）和更激进的注意力/KV 设计，因此模型内部表示天然是高维、分布式且跨层叠加的。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">架构类型</td>
<td style="text-align: center;">参数规模</td>
<td style="text-align: center;">层数</td>
<td style="text-align: center;">隐藏维度 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span></td>
<td style="text-align: center;">注意力头数</td>
<td style="text-align: center;">KV 头数</td>
<td style="text-align: center;">FFN / Intermediate 维度</td>
</tr>
</thead>
<tbody>
<tr>
<td>BERT-base</td>
<td>Encoder-only</td>
<td>110M 级（dense）</td>
<td>12</td>
<td>768</td>
<td>12</td>
<td>12</td>
<td>3072</td>
</tr>
<tr>
<td>GPT-2 Small</td>
<td>Decoder-only</td>
<td>124M 级（dense）</td>
<td>12</td>
<td>768</td>
<td>12</td>
<td>12</td>
<td>3072</td>
</tr>
<tr>
<td>Mistral 7B</td>
<td>Decoder-only</td>
<td>7B 级（dense）</td>
<td>32</td>
<td>4096</td>
<td>32</td>
<td>8</td>
<td>14336</td>
</tr>
<tr>
<td>Llama 3.1 8B</td>
<td>Decoder-only</td>
<td>8B 级（dense）</td>
<td>32</td>
<td>4096</td>
<td>32</td>
<td>8</td>
<td>14336</td>
</tr>
<tr>
<td>Qwen2.5 7B</td>
<td>Decoder-only</td>
<td>7B 级（dense）</td>
<td>28</td>
<td>3584</td>
<td>28</td>
<td>4</td>
<td>18944</td>
</tr>
<tr>
<td>Qwen3-235B-A22B</td>
<td>Decoder-only + MoE</td>
<td>235B 总参 / 22B 激活</td>
<td>94</td>
<td>4096</td>
<td>64</td>
<td>4</td>
<td>12288（dense）/ 1536（per-expert）</td>
</tr>
<tr>
<td>DeepSeek-V3 系列</td>
<td>Decoder-only + MoE + MLA</td>
<td>671B 总参 / 37B 激活</td>
<td>61</td>
<td>7168</td>
<td>128</td>
<td>128</td>
<td>18432（shared）/ 2048（per-expert）</td>
</tr>
</tbody>
</table>
<p>这张表也说明了一个很重要的趋势。到 2026 年，开源前沿模型已经不再沿着“单纯加深层数”这一条路线演化，而是出现了明显分化：Qwen3-235B-A22B 把层数推到 94 层，同时保持相对克制的隐藏维度，并通过 128 个专家、每 token 激活 8 个专家来放大总容量；DeepSeek-V3 系列则维持 61 层，但把隐藏维度提升到 7168，并叠加 DeepSeekMoE 与 MLA（Multi-head Latent Attention）来同时优化容量与推理成本。也就是说，前沿模型的“强”并不只表现为更深，而更多表现为<span style="background-color: #c0c0c0;">深度、宽度、专家稀疏性与注意力工程的联合扩展</span>。</p>
<p>对闭源顶级模型的层数，外界通常拿不到可靠公开配置，因此只能做工程上的区间推断。若它们仍以 Transformer block 为主体，那么从公开开源前沿模型的尺度看，显式层数大概率仍落在<span style="background-color: #c0c0c0;">数十层到一百多层</span>这一带，而不是简单增长到几百层甚至上千层；更常见的扩展手段，是增大隐藏维度、放大 FFN、引入 MoE、延长上下文、增加训练 token，或在同等层数下叠加稀疏注意力、递归计算与工具链调用。因此，对 GPT、Claude、Gemini 这类闭源顶级模型，更稳妥的判断不是“它们一定有多少层”，而是“它们很可能已经处在百层级上下、并辅以更复杂的宽度与稀疏化设计”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers-dims.png"><img class="alignnone size-large wp-image-41435" src="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers-dims.png" alt="transformers-dims" width="1" height="1" /></a>Transformer 的不可解释性（Lack of Interpretability）不是由某一个参数“导致”，而是由整体机制共同产生：表示是分布式（Distributed）且高维的，多层叠加的非线性变换把因果链条变长；注意力权重可视化能提供线索，但它不是完整解释。</p>
<div class="blog_h3"><span class="graybg">知识如何存储</span></div>
<p>大模型中的知识通常以分布式表示（Distributed Representation）的形式分散在大量参数里，而不是由某一个感知机单独存储一条事实。单个单元更像一个局部特征探测器（Feature Detector）：它只对某种模式敏感；许多单元级联后，网络才能把低层简单模式组合成高层抽象概念。模型规模越大、层数越深、参数越多，可被编码的模式组合也越丰富，这正是大模型具备强表达能力与“知识容量”的原因之一。</p>
<p>对于 Transformer 这样的模型，知识并不是以“一层一个概念、一神经元一事实”的方式整齐排布，而更像是沿着残差流（Residual Stream）在多层之间不断被提取、重组、放大和读出。注意力层更擅长在上下文中定位相关信息、建立 token 之间的依赖；MLP 层则更像参数化的模式变换器或记忆单元，会把某些已经被触发的模式映射成更强的语义方向，再写回主表示中。</p>
<p>从经验上看，这种知识分布有一些常见规律。较低层往往更接近词形、局部模式与浅层统计相关性；中间层更容易出现实体属性、关系模式和事实联想的组合；较高层则更接近任务相关读出，也就是更接近“最后怎样把内部表示变成具体输出”的阶段，例如下一 token 预测、答案选择或标签判别。但这更像统计趋势，而不是严格分工：同一类知识往往会跨多个层段冗余存在，并通过许多参数共同表达。</p>
<p>因此，更准确的理解不是“第几层存了什么知识”，而是“不同层在知识处理流水线里承担了什么功能”。有的层更偏检索线索，有的层更偏关系组合，有的层更偏把结果变成可供输出头使用的表示。单个 MLP 模块有时可以表现出类似键值记忆（Key-Value Memory）的行为，但真正稳定的知识通常仍然是跨层、跨参数、跨方向分布的。也正因为这种分布式编码，大模型既能表现出较强的知识容量，也会显得难以直接解释和精确定位。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers-knowledge.png"><img class="alignnone size-full wp-image-41449" src="https://blog.gmem.cc/wp-content/uploads/2026/03/transformers-knowledge.png" alt="transformers-knowledge" width="848" height="1264" /></a></p>
<div class="blog_h3"><span class="graybg">编码器-解码器</span></div>
<p>编码器-解码器（Encoder–Decoder）结构对应经典 Seq2Seq：编码器先对输入序列做双向自注意力（Bidirectional Self-Attention）编码，即编码器里的每个 token 都可以直接看到源序列中的其他 token，不使用因果掩码（Causal Mask），因此更擅长形成充分的上下文化输入表示；随后解码器在自回归生成（Autoregressive Generation）时，一边做因果自注意力（Causal Self-Attention），只看已经生成的前缀，一边通过交叉注意力（Cross-Attention）读取编码器输出。于是，编码器负责把“输入内容本身”编码清楚，解码器负责在“已生成前缀 + 编码器语义表示”条件下逐步生成输出。典型用于机器翻译、摘要、问答等“输入到输出”的条件生成任务（Conditional Generation），代表模型如 T5、BART。</p>
<div class="blog_h3"><span class="graybg">仅编码器</span></div>
<p>仅编码器（Encoder-only）结构使用双向自注意力（Bidirectional Self-Attention）：位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 可以看见所有位置（不做因果屏蔽）。它更擅长做“理解与表示”（Representation Learning），常见预训练目标是掩码语言建模（Masked Language Modeling, MLM）：把输入里部分 token 替换为 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{MASK}]\)</span>，训练模型根据上下文预测被遮住的 token。代表模型如 BERT、RoBERTa；ELECTRA 则用“替换检测（Replaced Token Detection）”作为预训练任务，但架构仍是 Encoder-only。</p>
<p>注意“掩码（Mask）”在这里指的是 <span style="background-color: #c0c0c0;">MLM 的 token masking</span>，不是自回归解码里的因果 attention mask。</p>
<div class="blog_h3"><span class="graybg">仅解码器</span></div>
<p>仅解码器（Decoder-only）结构使用因果自注意力（Causal Self-Attention）：位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 只能看见 <span displaypfx="inline-" class="mathjax-container">\(j\le i\)</span> 的历史 token，通过三角形掩码避免“偷看未来”。它天然对应自回归语言建模（Causal Language Modeling, CLM）：最大化 <span displaypfx="inline-" class="mathjax-container">\(\prod_t p(x_t|x_{&lt;t})\)</span>。代表模型如 GPT 系列、LLaMA、Qwen。</p>
<p>这里的“掩码（Mask）”指的是 <span style="background-color: #c0c0c0;">attention 里的因果屏蔽</span>，与 MLM 的 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{MASK}]\)</span> token 概念不同。</p>
<div class="blog_h3"><span class="graybg">预填充</span></div>
<p>对生成式 Transformer，尤其是 Decoder-only 模型以及 Encoder–Decoder 中的解码器侧，推理过程通常可分成两个阶段：预填充（Prefill）与解码（Decode）。Prefill 先把整段已知提示词（Prompt）一次性送入模型，计算每一层的隐藏状态，并把各层的 Key / Value 写入 KV Cache；Decode 则在此基础上逐步生成新 token，每一步只新增一个位置，再与历史缓存做注意力计算。</p>
<p>Prefill 阶段虽然仍然使用因果掩码（Causal Mask），但因为整段 prompt 在进入模型时已经全部已知，所以输入处理过程中所有 token 仍可并行处理：同一层里的 Query / Key / Value 投影、矩阵乘法以及 masked attention 都可以一次性并行完成。因果掩码只负责限制“当前位置不能看未来位置”，并不会把已知 prompt 重新变回按时间步串行处理。</p>
<p>生成阶段则通常是串行的。因为每一个新输出 token 都要作为前缀的一部分，参与下一个 token 的预测，所以 token 与 token 之间存在真实的自回归依赖，不能像 Prefill 那样沿序列长度整段并行展开。此时系统仍然可以利用 batch 并行、head 并行、张量并行、专家并行和内核并行，但在“生成顺序”这一维上通常必须逐步推进。这也是为什么长 prompt 场景下常说系统先经历一次计算密集（Compute-bound）的 Prefill，而进入连续生成后，瓶颈又经常转向 KV Cache 读取、显存带宽与调度开销主导的 Decode。</p>
<p>这个两阶段视角非常重要，因为后续很多工程优化都直接对应其中一个阶段：FlashAttention 对长序列 Prefill 的收益通常最显著；KV Cache、GQA / MQA、Paged Attention、Prompt Caching 与 Speculative Decoding 等，则更多是在优化 Decode 或同时兼顾两者。理解了 Prefill 与 Decode 的分工，再看 Transformer 推理优化时，许多“为什么这里快、那里慢”的现象就会变得自然。</p>
<div class="blog_h4"><span class="graybg">主流 Decoder-only 细节差异（选型表）</span></div>
<p>Decoder-only 成为主流之后，架构创新集中在“稳定性、KV Cache 成本与训练效率”三个轴：归一化/激活影响深层训练稳定性；注意力侧的 KV 结构决定长上下文推理成本；FFN 稠密/稀疏（MoE）与训练目标改造影响单位算力的有效学习信号。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">技术点</td>
<td style="text-align: center;">常见选项</td>
<td style="text-align: center;">动机</td>
<td style="text-align: center;">影响</td>
</tr>
</thead>
<tbody>
<tr>
<td>归一化（Normalization）</td>
<td>LayerNorm / RMSNorm</td>
<td>提升深层训练稳定性</td>
<td>RMSNorm 省掉去均值，算子更简单；实际表现依赖整体配方</td>
</tr>
<tr>
<td>激活/FFN（Activation/FFN）</td>
<td>GELU / SwiGLU / GLU 变体</td>
<td>门控提升表达力与稳定性</td>
<td>通常带来更好效果，但实现与吞吐会受内核支持影响</td>
</tr>
<tr>
<td>KV Cache 压力</td>
<td>MHA / GQA / MQA</td>
<td>减少 KV heads，降低显存与带宽</td>
<td>长上下文收益显著；可能牺牲部分表示自由度</td>
</tr>
<tr>
<td>KV 压缩（Latent KV）</td>
<td>低秩/潜变量压缩（如把 KV 投影到低维潜空间）</td>
<td>进一步压缩 KV Cache</td>
<td>上下文长度与并发能力提升，但架构更复杂、实现更依赖细节</td>
</tr>
<tr>
<td>FFN 稠密 vs 稀疏</td>
<td>Dense / MoE</td>
<td>用稀疏激活扩大参数容量</td>
<td>训练更复杂（路由/负载均衡）；推理吞吐依赖专家并行与缓存</td>
</tr>
<tr>
<td>预训练目标</td>
<td>Next-token / Multi-token Prediction（MTP）</td>
<td>提升单位 token 的监督信号密度</td>
<td>MTP 可能提高训练效率，但会改变解码对齐与训练配方</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">输入处理</span></div>
<p>Transformer 并不直接处理原始字符串。文本进入模型之前，必须先经过一条输入处理流水线：文本规范化、切分为 token、映射为 token id，再查表转成向量表示。只有完成这一步，后续的注意力、FFN 和位置编码才有可计算的离散输入。输入处理决定了模型“看见世界的最小单位”是什么，因此它不仅影响参数规模与推理效率，也会影响稀有词覆盖、跨语言能力、长度利用率以及生成结果的边界质量。</p>
<div class="blog_h3"><span class="graybg">Tokenization</span></div>
<p>Tokenization 的核心任务是把连续文本切分成模型可处理的离散符号序列。这个离散化过程看似只是“切词”，实际上定义了词表（Vocabulary）、序列长度、未知词处理方式以及字符到语义表示的映射粒度。若切得过粗，词表会过大、稀有词泛化差；若切得过细，序列会变长、计算成本升高。因此，现代语言模型通常采用子词分词（Subword Tokenization）：用有限词表在“整词”和“字符”之间取得平衡。</p>
<p>分词并不只是训练前的一道预处理工序，它深度参与了模型能力边界的形成。相同一句文本，换一种 tokenizer，模型看到的 token 序列长度、常见片段分布、数字和符号的切分方式都会变化，进而影响上下文利用率、训练效率、长文本成本、代码与多语言表现，甚至影响困惑度（Perplexity）等指标的可比性。也正因为如此，跨模型比较时，若 tokenizer 不同，很多“每 token 指标”都不能直接横向解读。</p>
<p>此外，现代 tokenizer 通常不只负责“切分”，还负责一组配套约定：例如保留哪些特殊 token（Special Tokens），如何处理大小写、空格、换行、标点、表情与 Unicode 字符，以及遇到词表里没有的片段时如何回退。像 <pre class="crayon-plain-tag">[UNK]</pre> 这样的未知词标记（Unknown Token）就是早期整词分词里常见的退路：当输入片段不在词表中时，直接映射成一个统一的“未知”符号。它的问题是信息损失很大，不同未知词都会塌缩成同一个 token。子词分词与字节分词之所以重要，一个核心原因就是它们大幅减少了对 <pre class="crayon-plain-tag">[UNK]</pre> 的依赖。</p>
<p>从风格上看，分词大致可以分为四类。第一类是整词分词（Word-level Tokenization）：把单词当作基本单位，优点是语义直观，缺点是词表会迅速膨胀，且对未登录词（Out-of-Vocabulary, OOV）非常敏感。第二类是字符分词（Character-level Tokenization）：把每个字符都当作 token，几乎没有 OOV 问题，但序列会显著变长，模型需要自己学习更多组合关系。第三类是子词分词（Subword Tokenization）：用常见片段构成词表，让高频词保持完整、低频词拆成片段，这是现代 NLP 最主流的折中路线。第四类是字节分词（Byte-level Tokenization）：直接在字节层处理输入，覆盖能力最强，跨语言和特殊符号最稳，但序列通常更长，对模型容量和训练配方要求更高。</p>
<p>因此，不同分词风格的本质取舍是：<span style="background-color: #c0c0c0;">词表越大，单个 token 的语义通常越完整，但 OOV 与稀疏性越严重；词表越小，覆盖越稳，但序列越长、建模负担越重</span>。现代大模型之所以大量采用 BPE、WordPiece、SentencePiece 或 byte-level BPE，本质上都是在这条权衡曲线上寻找更合适的工程平衡点。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">分词风格</td>
<td style="text-align: center;">基本单位</td>
<td style="text-align: center;">主要优点</td>
<td style="text-align: center;">主要代价</td>
<td style="text-align: center;">常见场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>整词分词</td>
<td>单词</td>
<td>语义直观；序列较短</td>
<td>词表膨胀；OOV 严重</td>
<td>早期 NLP；规则较强的封闭词表任务</td>
</tr>
<tr>
<td>字符分词</td>
<td>字符</td>
<td>覆盖稳定；几乎无 OOV</td>
<td>序列长；组合学习负担大</td>
<td>鲁棒输入建模；字符级任务</td>
</tr>
<tr>
<td>子词分词</td>
<td>高频片段 / 子词</td>
<td>词表与序列长度折中较好</td>
<td>切分方式影响语义边界</td>
<td>BERT、T5、LLaMA 等主流文本模型</td>
</tr>
<tr>
<td>字节分词</td>
<td>字节</td>
<td>覆盖最强；特殊符号与多语言稳健</td>
<td>序列更长；训练成本更高</td>
<td>byte-level BPE、多语言与噪声文本</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">BPE</span></div>
<p>BPE（Byte Pair Encoding）从字符（或字节）开始，通过统计合并高频相邻符号对（Pair Merge）逐步构建子词（Subword）词表。它的核心收益是用有限词表覆盖开放词汇：常见词被合并成整体，罕见词被拆成更小片段，减少 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{UNK}]\)</span>。</p>
<p>BPE 的直觉可以概括为“把最常一起出现的片段逐步固化成一个 token”。例如，若训练语料里 <pre class="crayon-plain-tag">t</pre> 和 <pre class="crayon-plain-tag">h</pre> 经常相邻，就可能先合并成 <pre class="crayon-plain-tag">th</pre>；若 <pre class="crayon-plain-tag">th</pre> 与 <pre class="crayon-plain-tag">e</pre> 又高频共现，就可能继续合并成 <pre class="crayon-plain-tag">the</pre>。经过大量合并之后，词表里会同时存在完整高频词、常见词根、后缀、数字片段和标点组合。这样一来，模型既能用短序列表达常见模式，又不必为每个罕见词都预留独立词条。</p>
<p>从工程谱系上看，GPT 家族总体属于 BPE 路线的延伸：早期 GPT / GPT-2 风格 tokenizer 采用 byte-level BPE，把文本先映射到字节层，再做 BPE 合并；这种设计能更稳地覆盖任意 Unicode 文本、空格和特殊符号。对 OpenAI 当前模型生态而言，官方开发工具链中程序化分词通常使用 tiktoken；它对应的是面向具体模型的 encoding 体系，但核心思想仍然是 BPE 家族的子词压缩与高覆盖率路线。对开发者来说，更重要的实践结论是：<span style="background-color: #c0c0c0;">GPT 并不是“按词”切分，而是按 BPE 家族 tokenizer 切成子词或字节片段</span>；同一个自然语言单词，可能被切成一个 token，也可能被切成多个 token，取决于它在词表中的合并状态。</p>
<div class="blog_h4"><span class="graybg">WordPiece</span></div>
<p>WordPiece 与 BPE 同属子词分词（Subword Tokenization），但合并准则更偏向最大化语言模型似然（Likelihood）。BERT 系列常用 WordPiece，因此会看到以 <pre class="crayon-plain-tag">##</pre> 标记的子词前缀（如 <pre class="crayon-plain-tag">play</pre> + <pre class="crayon-plain-tag">##ing</pre>）。</p>
<div class="blog_h4"><span class="graybg">SentencePiece</span></div>
<p>SentencePiece 是一种分词器（Tokenizer）训练与推理框架（常见算法包括 BPE 与 Unigram LM）。它可以直接在原始文本上训练（不依赖空格分词），因此在多语言与无空格语言（如中文、日文）上更常用；LLaMA 等模型的 tokenizer 通常基于 SentencePiece。</p>
<div class="blog_h3"><span class="graybg">Token Embedding</span></div>
<p>Token Embedding 的核心是一个可训练的嵌入表（Embedding Table，也常被称为嵌入矩阵（Embedding Matrix））：</p>
<span displaypfx="" class="mathjax-container">\[E\in\mathbb{R}^{V\;\times d_{\text{model}}}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 是词表大小（Vocabulary Size），每一行对应一个 token 的向量表示。给定输入 token id 序列 <span displaypfx="inline-" class="mathjax-container">\((t_1,\dots,t_L)\)</span>，查表得到输入嵌入序列（Embedding Output）：</p>
<span displaypfx="" class="mathjax-container">\[X=\begin{bmatrix}E_{t_1}\\ \vdots\\ E_{t_L}\end{bmatrix}\in\mathbb{R}^{L\;\times d_{\text{model}}}\]</span>
<p>一些材料会把 <span displaypfx="inline-" class="mathjax-container">\(E\)</span>（参数表）和 <span displaypfx="inline-" class="mathjax-container">\(X\)</span>（某次输入的嵌入结果）都叫“嵌入矩阵”，容易混淆。区分的一个简单方式是：<span style="background-color: #c0c0c0;">E 是全词表参数，X 是当前输入的嵌入输出</span>。</p>
<p>在语言模型里，这张输入嵌入表常与输出处理中的语言模型头（LM Head）共享参数，即权重共享（Weight Tying）。这里先记住这一点即可；它的具体计算方式与工程含义放在后面的“输出处理”中展开。</p>
<div class="blog_h2"><span class="graybg">位置编码</span></div>
<p>位置编码（Positional Encoding）解决一个根本问题：注意力机制本身对输入顺序是置换不变（Permutation-Invariant）的，如果不显式注入位置信息，模型无法区分“AB”和“BA”。因此需要把“位置”以某种方式编码进每个 token 的表示。</p>
<div class="blog_h3"><span class="graybg">绝对位置编码</span></div>
<p>绝对位置编码（Absolute Positional Encoding）最常见的做法之一是学习一个位置嵌入表（Position Embedding Table）：</p>
<span displaypfx="" class="mathjax-container">\[P\in\mathbb{R}^{L_{\max}\;\times d_{\text{model}}}\]</span>
<p><span displaypfx="inline-" class="mathjax-container">\(L_{\max}\)</span> 是模型支持的最大位置索引数量（Maximum Position Index）。对长度为 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 的输入序列，取 <span displaypfx="inline-" class="mathjax-container">\(P_{0:L}\)</span>（或 <span displaypfx="inline-" class="mathjax-container">\(P_{1:L}\)</span>，取决于实现）得到当前序列的位置嵌入矩阵 <span displaypfx="inline-" class="mathjax-container">\(P_{\text{seq}}\in\mathbb{R}^{L\;\times d_{\text{model}}}\)</span>。</p>
<p>Transformer 通常用逐元素相加把 token 嵌入与位置嵌入融合：</p>
<span displaypfx="" class="mathjax-container">\[H^{(0)} = X + P_{\text{seq}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(H^{(0)}\)</span> 仍然是 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 维向量序列，不是“位置标量”。位置是否用一个标量并不重要；重要的是这种表示能让后续的线性层与注意力计算利用位置关系。高维位置向量提供了更丰富的可学习空间。</p>
<p>“相加会不会把信息混在一起、无法区分？”这个直觉常见，但对表示学习而言关键不是可逆性，而是可用性：模型不需要从 <span displaypfx="inline-" class="mathjax-container">\(H^{(0)}\)</span> 精确还原 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(P_{\text{seq}}\)</span>，只需要用它们的组合完成预测。并且在高维空间里，模型可以把“语义”和“位置”分配到近似正交（Approximately Orthogonal）的方向，使得线性变换能有效解耦。</p>
<p>一个二维玩具例子：令 token 向量 <span displaypfx="inline-" class="mathjax-container">\(x=(1,0)\)</span>，位置向量 <span displaypfx="inline-" class="mathjax-container">\(p=(0,0.1)\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(h=x+p=(1,0.1)\)</span>。如果模型的某个线性读出只看第二维（例如乘以 <span displaypfx="inline-" class="mathjax-container">\((0,10)\)</span>），就能强烈感知位置而几乎不受语义影响。真实模型在上千维空间里有更大的自由度（Degree of Freedom, DOF）。</p>
<p>把位置“拼接”（Concatenation）到额外维度也能工作，但它会改变隐藏维度，影响后续层形状与参数规模；而加法保持 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 不变，是一种参数与工程都更稳定的设计选择。</p>
<div class="blog_h4"><span class="graybg">正弦位置编码（Sinusoidal Positional Encoding）</span></div>
<p>另一类绝对位置编码是正弦位置编码（Sinusoidal Positional Encoding），它不引入可学习参数，而是用不同频率的正弦/余弦把位置 <span displaypfx="inline-" class="mathjax-container">\(\text{pos}\)</span> 映射为向量（原始 Transformer 的设计）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{PE}(\text{pos},2i)=\sin\!\left(\frac{\text{pos}}{10000^{2i/d_{\text{model}}}}\right),\quad \mathrm{PE}(\text{pos},2i+1)=\cos\!\left(\frac{\text{pos}}{10000^{2i/d_{\text{model}}}}\right)\]</span>
<p>为什么要成对使用 <span displaypfx="inline-" class="mathjax-container">\(\sin\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\cos\)</span>（而不是全用 <span displaypfx="inline-" class="mathjax-container">\(\sin\)</span>）？因为对同一频率而言，<span displaypfx="inline-" class="mathjax-container">\((\sin\phi,\cos\phi)\)</span> 组成一个二维正交基（Orthogonal Basis），位置平移 <span displaypfx="inline-" class="mathjax-container">\(\phi\mapsto \phi+\Delta\)</span> 等价于二维平面上的旋转（Rotation）：</p>
<span displaypfx="" class="mathjax-container">\[\begin{bmatrix}\sin(\phi+\Delta)\\ \cos(\phi+\Delta)\end{bmatrix}=\begin{bmatrix}\cos\Delta &amp; \sin\Delta\\ -\sin\Delta &amp; \cos\Delta\end{bmatrix}\begin{bmatrix}\sin\phi\\ \cos\phi\end{bmatrix}\]</span>
<p>这让“相对位移”变成一个固定的线性变换，从而更容易被后续线性层和点积注意力利用；如果只用 <span displaypfx="inline-" class="mathjax-container">\(\sin\)</span>，相位信息会丢失，平移不再能用线性变换稳定表达。</p>
<p>若只取一个最小的 4 维例子，即 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}=4\)</span>，那么位置编码就会具体化成：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{PE}(\text{pos})=\big[\sin(\text{pos}),\ \cos(\text{pos}),\ \sin(\text{pos}/100),\ \cos(\text{pos}/100)\big]\]</span>
<p>这时每个位置都不再是“一个编号”，而是一个 4 维向量。前两维变化很快，负责较短尺度的位置区分；后两维变化很慢，负责较长尺度的位置区分。例如 <span displaypfx="inline-" class="mathjax-container">\(\text{pos}=0\)</span> 时编码是 <span displaypfx="inline-" class="mathjax-container">\([0,1,0,1]\)</span>；<span displaypfx="inline-" class="mathjax-container">\(\text{pos}=1\)</span> 时约为 <span displaypfx="inline-" class="mathjax-container">\([0.84,0.54,0.01,1.00]\)</span>；<span displaypfx="inline-" class="mathjax-container">\(\text{pos}=2\)</span> 时约为 <span displaypfx="inline-" class="mathjax-container">\([0.91,-0.42,0.02,1.00]\)</span>。因此，不同位置会同时在多种频率刻度上留下痕迹，而不是只靠一个单调递增的数字区分。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/sinusoidal-positional-encoding-4d.png"><img class="alignnone size-full" src="https://blog.gmem.cc/wp-content/uploads/2026/03/sinusoidal-positional-encoding-4d.png" alt="sinusoidal-positional-encoding-4d" width="2322" height="1354" /></a></p>
<p>上图把这个 4 维例子拆成三种读法。左侧热力图直接列出位置 <span displaypfx="inline-" class="mathjax-container">\(0\sim 7\)</span> 在四个维度上的编码值；右上角把前两维 <span displaypfx="inline-" class="mathjax-container">\((\sin(\text{pos}),\cos(\text{pos}))\)</span> 直接当成二维平面坐标，因此可以把它理解成：<span style="background-color: #c0c0c0;">位置每增加一点，平面上的点就沿圆周往前走一步</span>；右下角则把快频对与慢频对分开画出。图里第 3、4 维之所以先前看起来几乎是平的，不是因为它们不变，而是因为在标准公式里它们对应更低频率：在 <span displaypfx="inline-" class="mathjax-container">\(\text{pos}=0\sim 12\)</span> 这样很短的区间上， <span displaypfx="inline-" class="mathjax-container">\(\sin(\text{pos}/100)\)</span> 只从 0 变化到约 0.12， <span displaypfx="inline-" class="mathjax-container">\(\cos(\text{pos}/100)\)</span> 只从 1 下降到约 0.99，必须单独放大才容易看见变化。</p>
<p>模型利用这套编码的方式，可以直接理解成“拿多把不同刻度的尺子同时量位置关系”。同一对 token 的距离，在高频维度上会表现成较快的相位差，在低频维度上会表现成较慢的相位差；于是模型看到的就不是一个孤立的位置编号，而是一组跨多个尺度同时变化的模式。对于很近的 token，高频维度会给出很敏感的区分；对于距离更远的 token，低频维度仍然能保留稳定变化，不会太快绕回去。注意力层随后并不是逐维人工判读这些数值，而是在训练中学会：某些相位差组合通常意味着“相邻修饰”“短程依赖”，另一些更慢变化的组合更像“跨句呼应”或“长程对应”。这里并不存在一个必须被显式恢复出来的“角度标量”或“距离标量”。只要位置变化能够稳定地改变表示与点积结果，后续线性层和注意力头就可以把这种差异当作可利用特征。正弦位置编码的作用正是在于把位置关系改写成一组可被模型利用的周期信号，让模型自己在不同频率上学会读出距离与相对顺序。</p>
<div class="blog_h3"><span class="graybg">相对位置编码</span></div>
<p>相对位置编码（Relative Positional Encoding）不直接编码“绝对索引”，而是让注意力更显式地依赖 token 之间的相对距离 <span displaypfx="inline-" class="mathjax-container">\(i-j\)</span>。典型做法是在注意力打分里加入相对位置偏置（Relative Position Bias）：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_{ij}\propto \exp\!\left(\frac{q_i k_j^\top}{\sqrt{d_k}} + b_{i-j}\right)\]</span>
<p>这条式子描述的是：位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 的 query 去看位置 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的 key 时，未归一化注意力权重会受到两部分共同决定。第一部分 <span displaypfx="inline-" class="mathjax-container">\(\frac{q_i k_j^\top}{\sqrt{d_k}}\)</span> 是标准内容相关性打分：其中 <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个位置的查询向量（Query Vector），<span displaypfx="inline-" class="mathjax-container">\(k_j\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个位置的键向量（Key Vector），二者点积 <span displaypfx="inline-" class="mathjax-container">\(q_i k_j^\top\)</span> 衡量“位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 当前想找的信息，与位置 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 持有的信息是否匹配”；<span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 是 key/query 的维度，除以 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{d_k}\)</span> 是为了控制数值尺度，避免维度增大后 softmax 过早饱和。</p>
<p>第二部分 <span displaypfx="inline-" class="mathjax-container">\(b_{i-j}\)</span> 是只由相对距离决定的偏置项（Bias Term）。若 <span displaypfx="inline-" class="mathjax-container">\(i-j=1\)</span>，表示当前 token 正在看它左边紧邻的位置；若 <span displaypfx="inline-" class="mathjax-container">\(i-j=10\)</span>，表示它正在看更远的上文。这个偏置可以通过查表得到：给每一种相对距离，或给若干距离分桶（bucket）后的区间，各分配一个可学习标量；也可以由一个小网络根据 <span displaypfx="inline-" class="mathjax-container">\(i-j\)</span> 生成。它的作用是把“距离本身是否重要”直接加进打分，而不必完全依赖内容向量自己去隐式学出这种规律。</p>
<p>式子左边的 <span displaypfx="inline-" class="mathjax-container">\(\alpha_{ij}\)</span> 表示位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 对位置 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的注意力权重；这里写成 <span displaypfx="inline-" class="mathjax-container">\(\propto\)</span> 而不是等号，是因为右边还只是指数化前的未归一化权重。真正的注意力概率还要在固定 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 后，对所有 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 一起做 softmax 归一化：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_{ij}=\frac{\exp\!\left(\frac{q_i k_j^\top}{\sqrt{d_k}} + b_{i-j}\right)}{\sum_{j'}\exp\!\left(\frac{q_i k_{j'}^\top}{\sqrt{d_k}} + b_{i-j'}\right)}\]</span>
<p>因此，相对位置编码的含义可以概括为：注意力不只比较“内容是否匹配”，还显式比较“这个位置离我有多远”。很多语言现象更依赖相对距离而不是绝对序号，例如局部搭配、邻近修饰、长程指代和句法依赖，因此把 <span displaypfx="inline-" class="mathjax-container">\(i-j\)</span> 直接写进打分，往往比单纯依赖绝对位置索引更贴近任务结构。</p>
<div class="blog_h3"><span class="graybg">RoPE</span></div>
<p>RoPE（Rotary Position Embedding）把位置信息以“旋转”的方式注入到 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>/<span displaypfx="inline-" class="mathjax-container">\(K\)</span> 中。若按实数矩阵来写，就是把向量的每两维视为一个二维平面，再用角度与位置成正比的旋转矩阵作用在这两维上。对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个二维分量，令 <span displaypfx="inline-" class="mathjax-container">\(\theta_{m,i}\)</span> 表示位置 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 在该（每两维一个）频段上的旋转角，则</p>
<span displaypfx="" class="mathjax-container">\[\begin{bmatrix}x'_{2i}\\ x'_{2i+1}\end{bmatrix}=\begin{bmatrix}\cos\theta_{m,i} &amp; -\sin\theta_{m,i}\\ \sin\theta_{m,i} &amp; \cos\theta_{m,i}\end{bmatrix}\begin{bmatrix}x_{2i}\\ x_{2i+1}\end{bmatrix}\]</span>
<p>实现上，RoPE 不是“只把当前 query 旋转一下”，而是<span style="background-color: #c0c0c0;">对每个位置的 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 都各自按该位置做旋转</span>；随后不同位置之间再做点积匹配。这样一来，位置 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(Q_m\)</span> 和位置 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 的 <span displaypfx="inline-" class="mathjax-container">\(K_n\)</span> 在相遇时，二者各自携带的位置相位就会共同决定匹配结果。通常只有 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>/<span displaypfx="inline-" class="mathjax-container">\(K\)</span> 参与这种旋转， <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 不旋转，因为位置信息的关键作用点在“如何计算注意力权重”，而不是在“被加权汇总的内容值”本身。</p>
<p>上述矩阵式在实现上是正确的，但从理解角度看仍然偏“机械”。更直接的方式是用复数视角（Complex Perspective）：把每两维 <span displaypfx="inline-" class="mathjax-container">\((x_{2i},x_{2i+1})\)</span> 看成一个复数</p>
<span displaypfx="" class="mathjax-container">\[z_i = x_{2i} + \mathrm{i}x_{2i+1}\]</span>
<p>于是 RoPE 的位置注入就可以写成一个极其紧凑的式子：</p>
<span displaypfx="" class="mathjax-container">\[z_i' = z_i \, e^{\mathrm{i} m \theta_i}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 是位置索引， <span displaypfx="inline-" class="mathjax-container">\(\theta_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个频段的基础角速度， <span displaypfx="inline-" class="mathjax-container">\(e^{\mathrm{i} m \theta_i}\)</span> 表示“在复平面上旋转 <span displaypfx="inline-" class="mathjax-container">\(m\theta_i\)</span> 角”。这时 RoPE 的直觉就变得很清楚：<span style="background-color: #c0c0c0;">同一个向量本身不变，变化的是它在不同位置上附带的相位（phase）</span>。位置越靠后，相位就继续往前转。</p>
<p>这种写法的关键价值在于：相对位置会自然地从乘法里浮现出来。若位置 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 的 query 与位置 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 的 key 都经过旋转，则它们的匹配项可写成</p>
<span displaypfx="" class="mathjax-container">\[q_m^{(i)} e^{\mathrm{i} m \theta_i}\cdot \overline{k_n^{(i)} e^{\mathrm{i} n \theta_i}} = q_m^{(i)} \overline{k_n^{(i)}} e^{\mathrm{i}(m-n)\theta_i}\]</span>
<p>这里上划线表示复共轭（Complex Conjugate）。前文“二维向量的复数表示”已经给出同一条基本关系：二维点积可以写成复共轭乘积的实部，因此把二维块写成复数后，位置相位会直接进入匹配项。最重要的结果是指数项里只剩下 <span displaypfx="inline-" class="mathjax-container">\((m-n)\theta_i\)</span>：绝对位置 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 被自动折叠成了相对位移 <span displaypfx="inline-" class="mathjax-container">\(m-n\)</span>。因此，RoPE 并不是先写出绝对位置、再额外补一个相对位置项，而是通过“给每个位置乘一个相位因子”的方式，让相对位移直接出现在注意力匹配里。</p>
<p>若用一句更通俗的话概括，RoPE 做的事情是：<span style="background-color: #c0c0c0;">给每个位置的 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>/<span displaypfx="inline-" class="mathjax-container">\(K\)</span> 都拧上一点角度；两个位置一做点积，位置差就会体现在匹配分数里</span>。矩阵形式更像工程实现的展开式，复数形式更接近它的数学本质。模型并不需要在内部先还原出一个单独的“角度值”再决定如何注意；它只需要利用这种旋转所造成的分数差异与模式差异。只要某类相位关系稳定对应某类局部依赖、顺序关系或长程对应，训练过程就会把这些模式吸收到注意力头和后续层的参数里。也正因为这种“相对位移直接进入匹配”的结构，RoPE 在 Decoder-only 大模型中成为主流选择（例如 LLaMA 系列）。</p>
<div class="blog_h4"><span class="graybg">RoPE 长度外推（Length Extrapolation）</span></div>
<p>RoPE 的旋转角随位置线性增长。若训练阶段最大长度为 <span displaypfx="inline-" class="mathjax-container">\(L_{\text{train}}\)</span>，推理时直接扩展到 <span displaypfx="inline-" class="mathjax-container">\(L_{\text{test}}\gg L_{\text{train}}\)</span>，部分频段会出现“过快旋转”：模型开始在比训练时更长得多的位置区间上继续累积相位，而这些大角度相位组合在训练中几乎没有见过。结果是远距离 token 之间的相对相位关系超出训练分布，注意力更容易退化为近邻偏好，长上下文检索与推理准确率下降。</p>
<p>典型评测是大海捞针（Needle in a Haystack）：在很长的上下文中埋入一条关键信息（needle），要求模型在指定问题下准确复述该信息。常见现象是针落在开头/结尾时表现更好，但针落在中间位置时准确率显著下降；这通常与位置编码外推、注意力实现细节与 KV Cache 行为共同相关。</p>
<p>工程上常见的 RoPE 外推改造包括：</p>
<ul>
<li>位置插值（Position Interpolation, PI）：把推理位置按比例压缩回训练范围，相当于把 RoPE 角速度整体放慢。</li>
<li>NTK-aware 缩放（NTK-aware Scaling）：按“有效核宽度”视角调整频率谱，缓和远距离相对位移失真。</li>
<li>YaRN：对不同频段做分段/渐变缩放，尽量同时保住短程精度与长程外推。</li>
</ul>
<p>以 PI 为例，一个常用写法等价于把 RoPE 的位置 <span displaypfx="inline-" class="mathjax-container">\(\text{pos}\)</span> 映射为 <span displaypfx="inline-" class="mathjax-container">\(\text{pos}'=\text{pos}/s\)</span>（<span displaypfx="inline-" class="mathjax-container">\(s=L_{\text{test}}/L_{\text{train}}\)</span>），从而把角度压回训练范围：</p>
<span displaypfx="" class="mathjax-container">\[\theta'_{\text{pos},i}=\frac{\text{pos}/s}{\text{base}^{2i/d}},\quad s=\frac{L_{\text{test}}}{L_{\text{train}}}\]</span>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">方法</td>
<td style="text-align: center;">是否需要再训练</td>
<td style="text-align: center;">核心超参</td>
<td style="text-align: center;">优势</td>
<td style="text-align: center;">风险/备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>PI</td>
<td>建议配合长上下文继续预训练/微调</td>
<td>缩放因子 <span displaypfx="inline-" class="mathjax-container">\(s\)</span></td>
<td>实现简单；可在保持短程行为的同时扩展长度</td>
<td>若只做推理时改造，可能出现分布错配；需用 needle 测试验证“中间段”能力</td>
</tr>
<tr>
<td>NTK-aware scaling</td>
<td>可仅推理侧启用；配合微调更稳</td>
<td>频谱/基数缩放规则</td>
<td>对远距离更平滑；常用于把“可用上下文”拉长</td>
<td>不同实现差异大；需关注与 KV Cache、GQA/MQA 等工程优化的耦合</td>
</tr>
<tr>
<td>YaRN</td>
<td>通常建议配合继续预训练</td>
<td>分段/渐变缩放参数</td>
<td>兼顾短程精度与长程外推；对 needle 中段退化更友好</td>
<td>超参更多；需要系统评测（含不同位置、不同检索难度）</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">ALiBi</span></div>
<p>ALiBi（Attention with Linear Biases）直接在注意力 logits 上加一个与距离线性相关的偏置，而不改变表示维度，也不引入位置向量：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_{ij}\propto \exp\!\left(\frac{q_i k_j^\top}{\sqrt{d_k}} - m\cdot (i-j)\right),\quad j\le i\]</span>
<p>其中斜率 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 可按 head 设置。直觉上它鼓励模型更关注近邻 token，同时具备较好的长度外推（Length Extrapolation）行为。</p>
<div class="blog_h2"><span class="graybg">注意力机制</span></div>
<p>注意力机制（Attention Mechanism）是序列模型中的动态信息选择机制。对于一个由多个 token 构成的输入序列，模型不会把所有上下文位置等量混合，而是会针对当前正在计算的位置，动态判断哪些位置更相关、相关程度有多大，以及这些位置的信息应当如何组合成新的表示。这个过程本质上是一个与输入内容相关的加权汇聚：当前位置先形成查询信号，再在上下文中寻找与之匹配的位置，最后把这些位置承载的信息按权重聚合回来。</p>
<p>这种设计改变了传统序列建模的信息传递路径。循环结构主要依赖状态沿时间步逐步传递，卷积结构主要依赖固定大小的局部感受野，而注意力机制允许任意两个位置直接建立联系，并且联系强度由内容决定而不是由距离预先写死。长距离依赖（Long-Range Dependency）因此可以被更直接地建模：一个 token 可以立刻读取很远处但与当前语义高度相关的信息，而不必等待信息穿过很长的递归链条或许多层局部卷积。</p>
<p>Transformer 将注意力机制置于核心位置。自注意力（Self-Attention）让同一序列内部的各个 token 相互读取；交叉注意力（Cross-Attention）让一个序列读取另一个序列的表示；因果注意力（Causal Attention）则通过掩码限制当前位置只能访问过去的信息，从而支撑自回归生成（Autoregressive Generation）。这些形式都遵循同一条主线：先计算相关性分数，再把分数归一化为权重，最后对承载内容的向量做加权求和。其最经典、最常见的数学形式就是缩放点积注意力（Scaled Dot-Product Attention）。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/self-attention-mechanism.png"><img class="alignnone size-full wp-image-41471" src="https://blog.gmem.cc/wp-content/uploads/2026/03/self-attention-mechanism.png" alt="self-attention-mechanism" width="1920" height="1080" /></a></p>
<div class="blog_h3"><span class="graybg">Scaled Dot-Product Attention</span></div>
<p>自注意力（Self-Attention）中，输入表示 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 通过三组参数投影为：</p>
<span displaypfx="" class="mathjax-container">\[Q=XW_Q,\quad K=XW_K,\quad V=XW_V\]</span>
<p>注意力输出为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(W_Q,W_K,W_V\)</span>（以及多头里的 <span displaypfx="inline-" class="mathjax-container">\(W_O\)</span>）都是模型参数（Parameters），训练的目标不是“生成这些矩阵”，而是在损失函数（Loss）下通过梯度下降（Gradient Descent）把它们优化到能完成任务的取值。</p>
<p>把公式按 token 展开更清楚。设当前只看一个注意力头，序列长度为 <span displaypfx="inline-" class="mathjax-container">\(L\)</span>。对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token，模型先取出它的查询向量 <span displaypfx="inline-" class="mathjax-container">\(q_i\in\mathbb{R}^{d_k}\)</span>，再与序列中每个位置 <span displaypfx="inline-" class="mathjax-container">\(j=1,\dots,L\)</span> 的键向量 <span displaypfx="inline-" class="mathjax-container">\(k_j\in\mathbb{R}^{d_k}\)</span> 做点积，得到一个标量打分：</p>
<span displaypfx="" class="mathjax-container">\[s_{ij}=q_i k_j^\top\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 表示每个头里 Key / Query 向量的维度，也就是 <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(k_j\)</span> 的长度。它之所以记作 <span displaypfx="inline-" class="mathjax-container">\(d_k\)</span>，是因为这个维度首先由 Key 空间定义；而 Query 必须与 Key 处在同样维度里，才能做点积匹配。因此 <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(k_j\)</span> 的长度通常相同。Value 向量的维度记作 <span displaypfx="inline-" class="mathjax-container">\(d_v\)</span>；实践中常见设置是 <span displaypfx="inline-" class="mathjax-container">\(d_v=d_k\)</span>，但这不是数学上的硬要求。</p>
<p>接着，对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 query 的整行打分做缩放和 softmax，得到一组对所有位置的注意力权重：</p>
<span displaypfx="" class="mathjax-container">\[\alpha_{ij}=\mathrm{softmax}_j\!\left(\frac{s_{ij}}{\sqrt{d_k}}\right)=\frac{\exp\!\left(s_{ij}/\sqrt{d_k}\right)}{\sum_{t=1}^{L}\exp\!\left(s_{it}/\sqrt{d_k}\right)}\]</span>
<p>因此，对固定的 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 来说， <span displaypfx="inline-" class="mathjax-container">\(\alpha_{i1},\dots,\alpha_{iL}\)</span> 构成一个标量概率分布：它们都非负，且总和为 1。这个分布回答的是“第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token 应该从整段序列的哪些位置读取多少信息”。</p>
<p>输出 <span displaypfx="inline-" class="mathjax-container">\(o_i\)</span> 则是一个向量（Vector），由所有 Value 向量 <span displaypfx="inline-" class="mathjax-container">\(v_j\in\mathbb{R}^{d_v}\)</span> 按权重加权求和得到：</p>
<span displaypfx="" class="mathjax-container">\[o_i=\sum_{j=1}^{L}\alpha_{ij} v_j\]</span>
<p>把这一步画成示意图会更直观：固定第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 query 后，先得到一组对各位置 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的注意力权重；再用这些权重去加权汇总对应的 Value 向量；右侧输出向量的每一维，都是左侧各个 Value 向量对应维度的加权和。</p>
<p>查询向量（Query）与键向量（Key）负责“匹配打分”；值向量（Value）承载被聚合的信息内容。把注意力看作“内容寻址（Content-based Addressing）”：先用 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span> 计算“应该看谁”，再用权重对 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 做加权求和得到“看到了什么”。</p>
<p>缩放因子 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{d_k}\)</span> 的作用是控制数值尺度。由于 <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(k_j\)</span> 的点积是 <span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 个乘积项的求和，若各维分量方差相近，则点积分数的方差通常会随着 <span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 增长。维度一大， <span displaypfx="inline-" class="mathjax-container">\(s_{ij}\)</span> 的绝对值就更容易变大，softmax 会更快进入饱和区：某几个位置的权重接近 1，其余位置接近 0，梯度也会变小。除以 <span displaypfx="inline-" class="mathjax-container">\(\sqrt{d_k}\)</span> 后，分数尺度被拉回更稳定的范围，不同 head 维度设置下的 softmax 行为会更可控。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/attention-softmax-saturation.png"><img class="alignnone size-full wp-image-41453" src="https://blog.gmem.cc/wp-content/uploads/2026/03/attention-softmax-saturation.png" alt="attention-softmax-saturation" width="1920" height="1112" /></a></p>
<p>理论上可以让寻址与内容共用投影（例如令 <span displaypfx="inline-" class="mathjax-container">\(V=K\)</span> 或直接取 <span displaypfx="inline-" class="mathjax-container">\(V=X\)</span>），但实践中通常把 Q/K 与 V 分开，是为了让“打分空间”和“内容表示空间”解耦，提升表示能力与训练稳定性。</p>
<p>一个极简数值例子：若某个 Query 与 3 个 Key 的相似度（未缩放）为 <span displaypfx="inline-" class="mathjax-container">\([2,1,0]\)</span>，softmax 权重大约是 <span displaypfx="inline-" class="mathjax-container">\([0.665,0.245,0.090]\)</span>，输出就是把三个 Value 按这个比例加权求和。</p>
<div class="blog_h3"><span class="graybg">Multi-Head Attention</span></div>
<p>多头注意力（Multi-Head Attention）把注意力拆成 <span displaypfx="inline-" class="mathjax-container">\(H\)</span> 个头（Heads），每个头在不同的子空间里独立做一次注意力，然后在特征维度上拼接（Concatenation）并用输出矩阵混合：</p>
<span displaypfx="" class="mathjax-container">\[\text{head}_h=\mathrm{Attention}(XW_Q^{(h)},XW_K^{(h)},XW_V^{(h)})\]</span>
<span displaypfx="" class="mathjax-container">\[\mathrm{MultiHead}(X)=\mathrm{Concat}(\text{head}_1,\dots,\text{head}_H)W_O\]</span>
<p>若 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 固定，常见做法是每个头的维度 <span displaypfx="inline-" class="mathjax-container">\(d_k=d_v=d_{\text{model}}/H\)</span>，拼接后回到 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span>。多头的收益来自“并行关注不同关系”：有的头偏向局部邻域，有的头偏向长程依赖，有的头学到语法/实体指代等不同模式。</p>
<p>这种分工在标准训练里通常不是人工指定的，而是优化过程中的自发结果。每个头都拥有各自独立的 <span displaypfx="inline-" class="mathjax-container">\(W_Q^{(h)},W_K^{(h)},W_V^{(h)}\)</span> 参数，因此即使输入相同，它们也会把表示投影到不同子空间里，形成不同的匹配规则。随机初始化首先打破了头与头之间的对称性；随后，损失函数只约束“多头合起来的整体输出”是否有利于完成任务，而不要求每个头承担同一种功能。在这种条件下，若多个头完全重复，整体表示效率往往偏低；优化更容易把不同头推向不同关系模式，于是逐渐出现局部邻近、长程依赖、分隔符、指代、句法边界等不同偏好。</p>
<p>这种功能分化并不是严格保证。实际模型里常能观察到部分头高度相似，部分头贡献很小，甚至剪掉后性能几乎不变。若希望更强地控制不同头学习不同东西，就需要额外机制，例如对不同头加入多样性正则（Diversity Regularization）、局部窗口约束、特定监督信号，或在训练后做 head pruning / head specialization 分析。</p>
<div class="blog_h3"><span class="graybg">Masked Attention</span></div>
<p>Masked Attention（因果注意力 / Causal Attention）在自回归（Autoregressive）生成中使用：通过掩码（Mask）禁止位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 看到未来位置 <span displaypfx="inline-" class="mathjax-container">\(j&gt;i\)</span>。实现上通常是在 softmax 前把被禁止位置的打分加上一个极小值（如 <span displaypfx="inline-" class="mathjax-container">\(-\infty\)</span>）。</p>
<p>若把未加掩码的打分矩阵记为 <span displaypfx="inline-" class="mathjax-container">\(S=\frac{QK^\top}{\sqrt{d_k}}\in\mathbb{R}^{L\times L}\)</span>，则因果掩码可写成一个上三角被屏蔽的矩阵 <span displaypfx="inline-" class="mathjax-container">\(M\)</span>。以 <span displaypfx="inline-" class="mathjax-container">\(L=4\)</span> 为例：</p>
<span displaypfx="" class="mathjax-container">\[M=\begin{bmatrix} 0 &amp; -\infty &amp; -\infty &amp; -\infty\\ 0 &amp; 0 &amp; -\infty &amp; -\infty\\ 0 &amp; 0 &amp; 0 &amp; -\infty\\ 0 &amp; 0 &amp; 0 &amp; 0 \end{bmatrix}\]</span>
<p>然后在 softmax 之前做逐元素相加：</p>
<span displaypfx="" class="mathjax-container">\[P=\mathrm{softmax}(S+M)\]</span>
<p>这里主对角线及其左下区域为 0，表示当前位置及其历史位置允许被访问；右上区域为 <span displaypfx="inline-" class="mathjax-container">\(-\infty\)</span>，表示未来位置被强制屏蔽。softmax 之后，这些位置的权重会变成 0，因此第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行只能在 <span displaypfx="inline-" class="mathjax-container">\(j\le i\)</span> 的范围内分配概率。</p>
<p>从矩阵形状看，这就是一个<span style="background-color: #c0c0c0;">保留下三角、屏蔽上三角</span>的结构。它保证了解码器在位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 计算注意力时，只能读取已经出现的 token，而不能偷看未来 token。</p>
<p>注意力机制在训练阶段和推理阶段都会执行。区别在于：训练时通常一次性输入整段序列（Teacher Forcing）并使用因果掩码；推理时逐 token 解码，并结合 KV Cache 避免重复计算历史。</p>
<div class="blog_h3"><span class="graybg">Cross-Attention</span></div>
<p>交叉注意力（Cross-Attention）让一个序列“去读另一个序列”。在 Encoder–Decoder Transformer 里：解码器当前状态提供 Query，编码器输出提供 Key/Value。若编码器输出为 <span displaypfx="inline-" class="mathjax-container">\(H_{\text{src}}\in\mathbb{R}^{L_{\text{src}}\times d}\)</span>，解码器输入为 <span displaypfx="inline-" class="mathjax-container">\(H_{\text{tgt}}\in\mathbb{R}^{L_{\text{tgt}}\times d}\)</span>，则</p>
<span displaypfx="" class="mathjax-container">\[Q=H_{\text{tgt}}W_Q,\quad K=H_{\text{src}}W_K,\quad V=H_{\text{src}}W_V\]</span>
<span displaypfx="" class="mathjax-container">\[\mathrm{CrossAttn}(H_{\text{tgt}},H_{\text{src}})=\mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V\]</span>
<p>它与自注意力（Self-Attention）的区别仅在于 <span displaypfx="inline-" class="mathjax-container">\(K,V\)</span> 来自“别的序列”，因此能把“源序列信息”按需注入到“目标序列生成”中。</p>
<p>在交叉编码器（Cross-Encoder）语境里，很多实现并不显式写 cross-attention：它们把两段文本拼接成一个序列，用全连接自注意力直接建模跨序列交互；从效果上看等价于“允许任意 token 互相注意”。</p>
<p>Decoder-only 架构本身没有 cross-attention 子层；只有在做 Seq2Seq（有 encoder 输出）或显式引入外部记忆（Memory）时，才会在解码器里加入 cross-attention。</p>
<div class="blog_h3"><span class="graybg">稀疏注意力与滑动窗口注意力</span></div>
<p>标准密集自注意力（Dense Self-Attention）会让每个位置与所有可见位置计算打分，因此在长度 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 上通常带来 <span displaypfx="inline-" class="mathjax-container">\(O(L^2)\)</span> 级别的注意力矩阵与计算压力。稀疏注意力（Sparse Attention）的核心思路，就是预先限制“每个 token 允许看哪些位置”，只保留一部分连接，从而把长上下文建模的代价降下来。</p>
<div class="blog_h4"><span class="graybg">稀疏注意力</span></div>
<p>稀疏注意力是一类注意力连接模式的总称。它可以是局部窗口（Local Window）、块状稀疏（Block Sparse）、跨步连接（Strided Pattern）、少量全局 token（Global Tokens），也可以是这些模式的组合。Longformer、BigBird 这类长序列模型，都属于这条路线的经典代表。它保留 softmax 注意力的基本定义，同时把原本“谁都能看谁”的全连接关系改成一个更受约束的稀疏图。</p>
<p>从 2026 年的工程现实看，稀疏注意力仍然重要，但它已经不是通用旗舰语言模型的默认路线。它更常出现在长文档理解、超长上下文、显存/带宽受限，或专门强调长序列效率的模型中；而很多主流通用基座仍然更常采用密集因果注意力，再叠加 GQA、KV Cache、FlashAttention、KV 压缩等优化。这是因为稀疏模式虽然更省，但也会直接限制单层里可建立的依赖范围，训练与实现复杂度通常更高。</p>
<div class="blog_h4"><span class="graybg">滑动窗口注意力</span></div>
<p>滑动窗口注意力（Sliding Window Attention）是稀疏注意力里最常见、也最工程化的一种形式：位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 不是看全部历史，而只看距离自己最近的一段窗口，例如前面 <span displaypfx="inline-" class="mathjax-container">\(w\)</span> 个 token。这样单层注意力的代价就从“与整段长度线性增长的每行宽度”，压缩成“与固定窗口宽度相关”的局部计算。</p>
<p>它的优点是非常直接：局部模式、邻近依赖和短程语义通常仍能被稳定捕捉，而长上下文成本显著下降。代价是，两个相距很远的位置无法在同一层里直接交互，只能依靠多层传播，或额外引入全局层、全局 token、周期性全注意力层等机制来弥补。因此很多实际架构会采用“局部层 + 少量全局层”的混合设计，而不是把所有层都做成纯局部窗口。</p>
<p>到 2026 年，滑动窗口注意力仍然被部分主流模型持续使用，尤其是在长上下文或高性价比路线中；例如 Mistral 一类模型会显式采用 Sliding Window Attention，Gemma 2/3 一类模型也会在 local / global hybrid 结构中交替使用局部注意力层。但它并不是所有主流模型的统一默认配置。更准确的说法是：<span style="background-color: #c0c0c0;">通用“稀疏注意力”并非当代旗舰模型的普遍默认架构，而“滑动窗口注意力”则仍是今天主流工程实践里一条活跃的局部注意力路线</span>。</p>
<div class="blog_h3"><span class="graybg">KV Cache</span></div>
<p>KV Cache（Key-Value Cache）是自回归（Autoregressive）解码的关键工程优化：生成到第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步时，历史 token 的 Key/Value 已经在前序计算中得到；缓存它们可以避免每一步都重算整段历史的 K/V。</p>
<p>形式上，单层注意力在序列长度为 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 时需要缓存：</p>
<span displaypfx="" class="mathjax-container">\[K,V\in\mathbb{R}^{L\times n_{\text{kv}}\times d_k}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}\)</span> 是 KV 头数量（对标准多头注意力通常等于头数；对 GQA/MQA 通常更小），<span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 是每个 head 的维度。忽略实现细节（对齐、分块、paged layout）时，KV Cache 的显存规模近似线性增长：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Mem}_{\mathrm{KV}}\approx 2\cdot N_{\text{layers}}\cdot B\cdot L\cdot n_{\text{kv}}\cdot d_k\cdot \text{bytes}\]</span>
<p>这里前面的 2 来自同时缓存 K 与 V；<span displaypfx="inline-" class="mathjax-container">\(B\)</span> 是并发请求（batch）数；<span displaypfx="inline-" class="mathjax-container">\(\text{bytes}\)</span> 是每元素字节数（FP16/BF16 为 2）。因此 KV Cache 常成为长上下文与高并发推理的显存瓶颈。</p>
<p>KV Cache 的典型优化方向包括：</p>
<ul>
<li>减少 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}\)</span>，例如使用 GQA / MQA。</li>
<li>压缩 KV，例如 KV 量化、低秩表示、选择性缓存。</li>
<li>改进分配与复用，例如 Paged Attention、前缀缓存（Prompt Caching）。</li>
</ul>
<div class="blog_h3"><span class="graybg">FlashAttention</span></div>
<p>FlashAttention 是一种对标准注意力（Standard Attention）的高性能精确实现：它不改变 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{softmax}(QK^\top/\sqrt{d_k})V\)</span> 这个数学结果，而是通过分块（Tiling）、融合计算与在线 softmax，减少大规模中间矩阵在 HBM 与片上存储之间的来回搬运。因此，它首先是一种<span style="background-color: #c0c0c0;">注意力算子实现优化</span>，而不是新的模型结构。应用阶段上，训练与推理都可以使用 FlashAttention；在推理里，它最典型地加速的是预填充（Prefill）阶段，因为这时需要对整段输入做完整注意力计算，序列长、 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span> 代价高，FlashAttention 的收益最明显。到了逐 token 解码（Decode）阶段，单步 query 很短，瓶颈更常转向 KV Cache 读取、采样与调度，此时仍可使用面向解码优化的 Flash-Decoding / FlashAttention 变体，但收益模式已不同于预填充阶段。</p>
<p>从软件栈位置看，FlashAttention 可以放在<span style="background-color: #c0c0c0;">内核级别（Kernel-level）/ 后端级别（Backend-level）</span>来理解：上层框架仍然调用“注意力”这个算子，但底层并不一定走朴素的矩阵乘法 + softmax + 再乘 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 三步显式实现，而是改由高度融合的 GPU kernel 完成。是否真的启用 FlashAttention，取决于框架版本、后端实现、数据类型、head 维度、掩码形式以及硬件架构是否匹配。工程上常见支持平台是 NVIDIA 的 Ampere / Ada / Hopper，以及 AMD ROCm 生态中的部分高端 GPU；若硬件或后端条件不满足，框架通常会自动回退到 memory-efficient attention、cuDNN attention 或更普通的数学实现。</p>
<div class="blog_h4"><span class="graybg">为什么需要 FlashAttention</span></div>
<p>FlashAttention要解决的是标准注意力（Standard Attention）在长序列上的 <span style="background-color: #c0c0c0;">中间张量 IO 成本</span> 过高。标准缩放点积注意力（Scaled Dot-Product Attention）可写为：</p>
<span displaypfx="" class="mathjax-container">\[S=\frac{QK^\top}{\sqrt{d_k}},\quad P=\mathrm{softmax}(S),\quad O=PV\]</span>
<p>其中：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(Q\in\mathbb{R}^{N\times d_k}\)</span>：查询矩阵（Query Matrix），<span displaypfx="inline-" class="mathjax-container">\(N\)</span> 是序列长度， <span displaypfx="inline-" class="mathjax-container">\(d_k\)</span> 是每个 head 的查询/键维度。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(K\in\mathbb{R}^{N\times d_k}\)</span>：键矩阵（Key Matrix），与 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 做点积打分。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(V\in\mathbb{R}^{N\times d_v}\)</span>：值矩阵（Value Matrix），<span displaypfx="inline-" class="mathjax-container">\(d_v\)</span> 是每个 head 的值维度。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(S\in\mathbb{R}^{N\times N}\)</span>：注意力分数矩阵（Score Matrix），其中 <span displaypfx="inline-" class="mathjax-container">\(S_{ij}\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 query 对第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个 key 的未归一化打分。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(P\in\mathbb{R}^{N\times N}\)</span>：softmax 归一化后的注意力权重矩阵（Attention Probability Matrix）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(O\in\mathbb{R}^{N\times d_v}\)</span>：最终输出矩阵（Output Matrix）。</li>
</ul>
<p>问题集中在 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 和很多实现中的 <span displaypfx="inline-" class="mathjax-container">\(P\)</span>：它们都是 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 规模。序列一长，中间矩阵就会迅速膨胀。计算复杂度依然是 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(N^2)\)</span> 级别，但在 GPU 上更先撞上的往往不是算力上限，而是高带宽显存（High Bandwidth Memory, HBM）与片上共享内存 / SRAM（Static Random Access Memory, SRAM）之间的数据搬运成本。</p>
<p>传统实现通常经历三步：先算出整个 <span displaypfx="inline-" class="mathjax-container">\(S=QK^\top\)</span> 并写回显存；再把它读出来做 softmax，得到 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 并再次写回；最后再把 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 读出来与 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 相乘得到 <span displaypfx="inline-" class="mathjax-container">\(O\)</span>。这意味着真正拖慢速度的往往不是矩阵乘法本身，而是对 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span> 中间结果的反复显式物化（Materialization）与反复搬运。</p>
<p>一个直接类比是流水线工厂。普通注意力像“先把全部半成品都堆进仓库，再统一拿出来做下一道工序”；仓库本身就成了瓶颈。FlashAttention 则像“边加工边流转”的流水线：中间块只在车间里短暂停留，不建立巨大的中间仓库。</p>
<div class="blog_h4"><span class="graybg">核心思想</span></div>
<p>FlashAttention 的核心可以压缩成一句话：<span style="background-color: #c0c0c0;">分块（Tiling）+ 在线 softmax（Online Softmax）+ 融合输出（Fused Output Accumulation）</span>。</p>
<p>它并不改变注意力的数学目标，仍然精确计算同一个 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{softmax}(QK^\top/\sqrt{d_k})V\)</span>；它改变的是计算顺序与中间结果的存储方式。具体来说，FlashAttention 不再把整个 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 的注意力矩阵一次性算完并落到 HBM 中，而是把 <span displaypfx="inline-" class="mathjax-container">\(Q,K,V\)</span> 切成若干小块（tiles），每次只在 SRAM 中处理一小块分数、归一化和输出累加。</p>
<p>设查询块（query tile）为 <span displaypfx="inline-" class="mathjax-container">\(Q_i\in\mathbb{R}^{B_q\times d_k}\)</span>，键块和值块分别为 <span displaypfx="inline-" class="mathjax-container">\(K_j\in\mathbb{R}^{B_k\times d_k}\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(V_j\in\mathbb{R}^{B_k\times d_v}\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(B_q\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(B_k\)</span> 是 tile 大小，远小于完整序列长度 <span displaypfx="inline-" class="mathjax-container">\(N\)</span>。FlashAttention 每次只把这样的局部块搬进 SRAM，在块内完成当前 query tile 对当前 key/value tile 的全部贡献计算。</p>
<div class="blog_h4"><span class="graybg">数学本质：块级注意力与在线 softmax</span></div>
<p>对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 query 块和第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个 key/value 块，先计算块级分数矩阵：</p>
<span displaypfx="" class="mathjax-container">\[S_{ij}=\frac{Q_iK_j^\top}{\sqrt{d_k}},\qquad S_{ij}\in\mathbb{R}^{B_q\times B_k}\]</span>
<p>其中：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(S_{ij}\)</span>：当前块内的注意力打分矩阵。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(S_{ij}[r,c]\)</span>：query 块中第 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 行与 key 块中第 <span displaypfx="inline-" class="mathjax-container">\(c\)</span> 行的打分。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\sqrt{d_k}\)</span>：缩放因子，用于抑制点积随维度增长而导致的 softmax 饱和。</li>
</ul>
<p>难点在于 softmax 的分母依赖整行所有 key：对一个 query 而言，必须把它对所有位置的打分都考虑进去，才能完成归一化。FlashAttention 的关键突破是：不必先看到整行全部元素，再做 softmax；可以用在线算法维护“到目前为止的最大值、分母和分子累加量”，随着块不断读入而精确更新。</p>
<p>对当前 query 块 <span displaypfx="inline-" class="mathjax-container">\(Q_i\)</span>，FlashAttention 维护三个按行统计的状态：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{m}_i\in\mathbb{R}^{B_q},\qquad \boldsymbol{\ell}_i\in\mathbb{R}^{B_q},\qquad R_i\in\mathbb{R}^{B_q\times d_v}\]</span>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{m}_i\)</span>：每个 query 行到目前为止见过的最大分数（row-wise running max）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\boldsymbol{\ell}_i\)</span>：每个 query 行当前的 softmax 分母累加量。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(R_i\)</span>：每个 query 行对输出向量的未归一化加权和（unnormalized weighted sum）。</li>
</ul>
<p>初始时可设：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{m}_i=-\infty,\qquad \boldsymbol{\ell}_i=\mathbf{0},\qquad R_i=0\]</span>
<p>当读入第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个块时，先求该块每一行的局部最大值：</p>
<span displaypfx="" class="mathjax-container">\[\tilde{\mathbf{m}}_{ij}=\mathrm{rowmax}(S_{ij})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rowmax}(\cdot)\)</span> 表示对矩阵每一行取最大值，因此输出是长度为 <span displaypfx="inline-" class="mathjax-container">\(B_q\)</span> 的向量。再把旧最大值和当前块最大值合并成新的全局参考点：</p>
<span displaypfx="" class="mathjax-container">\[\mathbf{m}_i^{\mathrm{new}}=\max\!\left(\mathbf{m}_i,\tilde{\mathbf{m}}_{ij}\right)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\max\)</span> 是逐元素最大值（element-wise max），因为每个 query 行都维护自己的 softmax 参考值。</p>
<p>接着把当前块的指数项按新参考点重写：</p>
<span displaypfx="" class="mathjax-container">\[P_{ij}=\exp\!\left(S_{ij}-\mathbf{m}_i^{\mathrm{new}}\mathbf{1}^\top\right)\]</span>
<p>其中：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(P_{ij}\in\mathbb{R}^{B_q\times B_k}\)</span>：当前块中按新最大值平移后的指数权重。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\in\mathbb{R}^{B_k}\)</span>：全 1 向量，用于把 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{m}_i^{\mathrm{new}}\)</span> 广播到块内每一列。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\exp(\cdot)\)</span>：逐元素指数函数。</li>
</ul>
<p>然后更新分母累加量：</p>
<span displaypfx="" class="mathjax-container">\[\boldsymbol{\ell}_i^{\mathrm{new}}=\exp\!\left(\mathbf{m}_i-\mathbf{m}_i^{\mathrm{new}}\right)\odot \boldsymbol{\ell}_i+\mathrm{rowsum}(P_{ij})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\odot\)</span> 表示逐元素乘法， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{rowsum}(P_{ij})\)</span> 表示对 <span displaypfx="inline-" class="mathjax-container">\(P_{ij}\)</span> 每一行求和。这个式子的含义是：旧块已经累积的分母，先因为参考最大值改变而按 <span displaypfx="inline-" class="mathjax-container">\(\exp(\mathbf{m}_i-\mathbf{m}_i^{\mathrm{new}})\)</span> 重新缩放，再加上当前块的新贡献。</p>
<p>再更新输出分子的累加量：</p>
<span displaypfx="" class="mathjax-container">\[R_i^{\mathrm{new}}=\mathrm{Diag}\!\left(\exp\!\left(\mathbf{m}_i-\mathbf{m}_i^{\mathrm{new}}\right)\right)R_i+P_{ij}V_j\]</span>
<p>其中：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathrm{Diag}(\cdot)\)</span>：把向量放到对角线上形成对角矩阵，用于按行缩放 <span displaypfx="inline-" class="mathjax-container">\(R_i\)</span>。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(P_{ij}V_j\in\mathbb{R}^{B_q\times d_v}\)</span>：当前块对输出的新增贡献。</li>
</ul>
<p>所有 <span displaypfx="inline-" class="mathjax-container">\(K_j,V_j\)</span> 块处理完之后，当前 query 块的最终输出为：</p>
<span displaypfx="" class="mathjax-container">\[O_i=\mathrm{Diag}\!\left((\boldsymbol{\ell}_i)^{-1}\right)R_i\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\((\boldsymbol{\ell}_i)^{-1}\)</span> 表示对向量每个元素取倒数，作用是把“未归一化加权和”除以 softmax 分母，从而得到真正的注意力输出。</p>
<div class="blog_h4"><span class="graybg">为什么这仍然是精确 softmax</span></div>
<p>FlashAttention 通过一种保持数值等价的累计方式精确计算 softmax。设某一行已经处理过的旧分数集合为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{A}\)</span>，其旧最大值为 <span displaypfx="inline-" class="mathjax-container">\(m_{\mathrm{old}}\)</span>，旧分母为：</p>
<span displaypfx="" class="mathjax-container">\[\ell_{\mathrm{old}}=\sum_{x\in\mathcal{A}} e^{x-m_{\mathrm{old}}}\]</span>
<p>新读入一块分数集合 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{B}\)</span> 后，若新的全局最大值变成 <span displaypfx="inline-" class="mathjax-container">\(m_{\mathrm{new}}\)</span>，则旧部分相对于新参考点的分母贡献恰好变成：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{x\in\mathcal{A}} e^{x-m_{\mathrm{new}}}=e^{m_{\mathrm{old}}-m_{\mathrm{new}}}\sum_{x\in\mathcal{A}} e^{x-m_{\mathrm{old}}}=e^{m_{\mathrm{old}}-m_{\mathrm{new}}}\ell_{\mathrm{old}}\]</span>
<p>这正是在线更新公式里那一项缩放因子的来源。分子累加量 <span displaypfx="inline-" class="mathjax-container">\(R_i\)</span> 也是同样的道理：旧部分先按新参考点缩放，再加上新块贡献。因此块级处理结束后得到的 <span displaypfx="inline-" class="mathjax-container">\(O_i\)</span> 与一次性对整行做 softmax 再乘 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 的结果完全一致。</p>
<div class="blog_h4"><span class="graybg">与普通 Attention 的区别</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">维度</td>
<td style="text-align: center;">普通 Attention</td>
<td style="text-align: center;">FlashAttention</td>
</tr>
</thead>
<tbody>
<tr>
<td>数学目标</td>
<td>计算 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{softmax}(QK^\top/\sqrt{d_k})V\)</span></td>
<td>计算同一个精确结果，不改目标函数</td>
</tr>
<tr>
<td>中间矩阵</td>
<td>常显式存 <span displaypfx="inline-" class="mathjax-container">\(S\)</span>，很多实现还显式存 <span displaypfx="inline-" class="mathjax-container">\(P\)</span></td>
<td>不显式存完整 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 矩阵，只保留 tile 级临时块与行级累加状态</td>
</tr>
<tr>
<td>计算顺序</td>
<td>先全部算完分数，再整体 softmax，再乘 <span displaypfx="inline-" class="mathjax-container">\(V\)</span></td>
<td>边读块边更新 softmax，边把当前块对输出的贡献累加进去</td>
</tr>
<tr>
<td>显存特征</td>
<td>中间激活常呈 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(N^2)\)</span> 增长</td>
<td>额外中间存储近似降到 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(N)\)</span> 级别</td>
</tr>
<tr>
<td>性能瓶颈</td>
<td>更容易受 HBM 读写限制，属于强 memory-bound 场景</td>
<td>显著减少 HBM 往返，更接近 compute-bound</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">为什么它会快</span></div>
<p>FlashAttention 的速度优势主要来自 IO 模式优化，而不是渐近计算复杂度下降。它的核心收益主要来自四点：</p>
<ul>
<li>减少 HBM 访问：不再反复把 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 这类 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span> 中间张量写回、读回。</li>
<li>提升 SRAM 复用：一个 tile 被搬进片上后，会在同一块内连续完成分数计算、归一化和输出累加。</li>
<li>算子融合（Kernel Fusion）：原本分散的 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span>、softmax、<span displaypfx="inline-" class="mathjax-container">\(PV\)</span> 被压成一条更短的数据通路。</li>
<li>数值稳定：在线 softmax 仍然使用减最大值（max trick），避免指数溢出，也避免了“先大矩阵 softmax 再回写”带来的额外数值压力。</li>
</ul>
<p>因此，FlashAttention 的本质不是“更少的数学”，而是“更少的无效搬运”。从硬件视角看，它把一个明显受内存带宽制约的算子，改造成更能吃满矩阵乘法单元和 Tensor Core 的实现。</p>
<div class="blog_h4"><span class="graybg">复杂度与工程直觉</span></div>
<p>复杂度上需要严格区分“算了多少”和“存了多少”。FlashAttention 与普通注意力在算术复杂度上仍然同阶，因为每个 query 与每个 key 的交互并没有消失：</p>
<span displaypfx="" class="mathjax-container">\[\text{FLOPs: }\mathcal{O}(N^2d_k)\quad\text{vs.}\quad \mathcal{O}(N^2d_k)\]</span>
<p>但中间激活的显存复杂度发生了根本变化。若只看注意力算子额外需要保留的中间结果，则：</p>
<span displaypfx="" class="mathjax-container">\[\text{普通 Attention: }\mathcal{O}(N^2),\qquad \text{FlashAttention: }\mathcal{O}(N)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(N)\)</span> 指的是按行维护的 softmax 统计量与输出累加量；tile 临时块的大小由 <span displaypfx="inline-" class="mathjax-container">\(B_q,B_k\)</span> 控制，不随完整序列平方增长。工程直觉可以概括为：<span style="background-color: #c0c0c0;">算力阶数没变，但仓库规模从平方级中间仓库变成了线性级流水线缓存</span>。</p>
<div class="blog_h4"><span class="graybg">一个极简伪代码</span></div>
<pre class="crayon-plain-tag">for each query tile Q_i:
    m_i = -inf
    l_i = 0
    R_i = 0
    for each key/value tile (K_j, V_j):
        S_ij = Q_i K_j^T / sqrt(d_k)
        m_new = max(m_i, rowmax(S_ij))
        P_ij = exp(S_ij - m_new)
        l_i = exp(m_i - m_new) * l_i + rowsum(P_ij)
        R_i = diag(exp(m_i - m_new)) * R_i + P_ij * V_j
        m_i = m_new
    O_i = diag(1 / l_i) * R_i</pre>
<p>这段伪代码对应的正是“边看块、边归一化、边输出”的流水线结构。与普通实现相比，最大的变化不是公式，而是调度顺序。</p>
<div class="blog_h4"><span class="graybg">反向传播（Backward）：为什么也能不存 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span></span></div>
<p>FlashAttention 的关键价值不仅在前向传播（Forward Pass），也在反向传播（Backward Pass）。训练时真正吃显存的不只是前向输出，还包括为了求梯度而保留的中间激活。如果 backward 仍然要求把完整的 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 存下来，那么前向省出来的显存会被反向阶段重新吃掉。因此，FlashAttention backward 的核心原则与前向一致：<span style="background-color: #c0c0c0;">不保存 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 注意力矩阵，而是在 backward 中按块重算（recompute）它们</span>。</p>
<p>设前向定义为：</p>
<span displaypfx="" class="mathjax-container">\[S=\frac{QK^\top}{\sqrt{d_k}},\qquad P=\mathrm{softmax}(S),\qquad O=PV\]</span>
<p>设损失函数为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span>，并记上游传回的输出梯度为：</p>
<span displaypfx="" class="mathjax-container">\[G=\frac{\partial \mathcal{L}}{\partial O},\qquad G\in\mathbb{R}^{N\times d_v}\]</span>
<p>这里：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}\)</span>：整个模型的标量损失（scalar loss）。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(G\)</span>：损失对注意力输出 <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 的梯度，也就是 backward 进入注意力层时收到的上游信号。</li>
</ul>
<p>普通 attention 的 backward 可以按链式法则拆成四步。先对 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 求梯度：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial V}=P^\top G\]</span>
<p>这个式子表示：某个 value 向量 <span displaypfx="inline-" class="mathjax-container">\(v_j\)</span> 对多少个 query 产生了贡献，就会按相应注意力权重 <span displaypfx="inline-" class="mathjax-container">\(P_{ij}\)</span> 把这些上游梯度累加回来。</p>
<p>再对概率矩阵 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 求梯度：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial P}=GV^\top\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial P}\in\mathbb{R}^{N\times N}\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 项表示：若第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 列的注意力权重略有变化，会怎样影响损失。</p>
<p>关键一步是 softmax 的梯度。对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行，记 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 为第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行概率向量， <span displaypfx="inline-" class="mathjax-container">\(g_i^P\)</span> 为 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial P}\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行，则：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial s_i}=p_i\odot \left(g_i^P-\delta_i\mathbf{1}\right),\qquad \delta_i=\sum_{j=1}^{N} g_{ij}^P p_{ij}\]</span>
<p>其中：</p>
<ul>
<li><span displaypfx="inline-" class="mathjax-container">\(s_i\)</span>：分数矩阵 <span displaypfx="inline-" class="mathjax-container">\(S\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\odot\)</span>：逐元素乘法。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\delta_i\)</span>：第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行 softmax Jacobian 压缩后的标量项，用来扣掉“整行归一化”带来的耦合影响。</li>
<li><span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\in\mathbb{R}^{N}\)</span>：全 1 向量。</li>
</ul>
<p>把所有行拼起来，可写成矩阵形式：</p>
<span displaypfx="" class="mathjax-container">\[D=\mathrm{rowsum}\!\left(\frac{\partial \mathcal{L}}{\partial P}\odot P\right),\qquad \frac{\partial \mathcal{L}}{\partial S}=P\odot \left(\frac{\partial \mathcal{L}}{\partial P}-D\mathbf{1}^\top\right)\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(D\in\mathbb{R}^{N}\)</span> 是逐行标量向量，第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量就是 <span displaypfx="inline-" class="mathjax-container">\(\delta_i\)</span>。最后再通过 <span displaypfx="inline-" class="mathjax-container">\(S=QK^\top/\sqrt{d_k}\)</span> 回传到 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(K\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial Q}=\frac{\partial \mathcal{L}}{\partial S}\frac{K}{\sqrt{d_k}},\qquad \frac{\partial \mathcal{L}}{\partial K}=\left(\frac{\partial \mathcal{L}}{\partial S}\right)^\top\frac{Q}{\sqrt{d_k}}\]</span>
<p>若直接照这些公式实现，最大问题是：看起来必须先拿到完整的 <span displaypfx="inline-" class="mathjax-container">\(P\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial P}\)</span>，而它们又都是 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span>。FlashAttention backward 的突破在于，真正必须永久保存的量远比这少。</p>
<p>第一，前向阶段只需保存每一行的 log-sum-exp 统计量（Log-Sum-Exp Statistics），而不必保存整张 <span displaypfx="inline-" class="mathjax-container">\(P\)</span>。若前向某一行的最大值为 <span displaypfx="inline-" class="mathjax-container">\(m_i\)</span>，归一化因子为 <span displaypfx="inline-" class="mathjax-container">\(\ell_i\)</span>，则可存：</p>
<span displaypfx="" class="mathjax-container">\[L_i=m_i+\log \ell_i=\log\sum_{j=1}^{N} e^{S_{ij}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(L_i\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行 softmax 分母的对数。只要 backward 时重新算出某个块的分数 <span displaypfx="inline-" class="mathjax-container">\(S_{ij}\)</span>，就可以把该块的概率精确重建为：</p>
<span displaypfx="" class="mathjax-container">\[P_{ij}=\exp\!\left(S_{ij}-L_i\mathbf{1}^\top\right)\]</span>
<p>这说明 backward 不需要读取前向保存下来的整张 <span displaypfx="inline-" class="mathjax-container">\(P\)</span>；它只需要 <span displaypfx="inline-" class="mathjax-container">\(Q\)</span>、<span displaypfx="inline-" class="mathjax-container">\(K\)</span>、行级统计量 <span displaypfx="inline-" class="mathjax-container">\(L_i\)</span>，就能按块把局部概率重新算出来。</p>
<p>第二，softmax backward 中的行级标量 <span displaypfx="inline-" class="mathjax-container">\(\delta_i\)</span> 也可以不通过整张 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial P}\)</span> 显式求和，而是用一个更紧凑的等价式：</p>
<span displaypfx="" class="mathjax-container">\[\delta_i=\sum_{j=1}^{N} g_{ij}^P p_{ij}=g_i^\top o_i\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(g_i\)</span> 是上游梯度矩阵 <span displaypfx="inline-" class="mathjax-container">\(G\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行， <span displaypfx="inline-" class="mathjax-container">\(o_i\)</span> 是前向输出 <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 行。这个恒等式来自：</p>
<span displaypfx="" class="mathjax-container">\[g_i^P=g_iV^\top,\qquad o_i=p_iV\]</span>
<p>于是：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{j=1}^{N} g_{ij}^P p_{ij}=\sum_{j=1}^{N}(g_i v_j^\top)p_{ij}=g_i\left(\sum_{j=1}^{N}p_{ij}v_j\right)^\top=g_i o_i^\top\]</span>
<p>这一步非常关键，因为它说明 softmax backward 所需的行级校正项 <span displaypfx="inline-" class="mathjax-container">\(\delta_i\)</span>，可以直接由前向输出 <span displaypfx="inline-" class="mathjax-container">\(O\)</span> 和上游梯度 <span displaypfx="inline-" class="mathjax-container">\(G\)</span> 得到，而不需要显式展开整个 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 概率矩阵。</p>
<p>因此，FlashAttention backward 的块级流程可以概括为：</p>
<ol>
<li>读取一个 query tile <span displaypfx="inline-" class="mathjax-container">\(Q_i\)</span>、对应输出 tile <span displaypfx="inline-" class="mathjax-container">\(O_i\)</span>、上游梯度 tile <span displaypfx="inline-" class="mathjax-container">\(G_i\)</span>，以及该 tile 的行级统计量 <span displaypfx="inline-" class="mathjax-container">\(L_i\)</span>。</li>
<li>逐块读取 <span displaypfx="inline-" class="mathjax-container">\(K_j,V_j\)</span>，重算当前块分数 <span displaypfx="inline-" class="mathjax-container">\(S_{ij}=Q_iK_j^\top/\sqrt{d_k}\)</span>。</li>
<li>由 <span displaypfx="inline-" class="mathjax-container">\(L_i\)</span> 重建当前块概率 <span displaypfx="inline-" class="mathjax-container">\(P_{ij}=\exp(S_{ij}-L_i\mathbf{1}^\top)\)</span>。</li>
<li>用 <span displaypfx="inline-" class="mathjax-container">\(G_iV_j^\top\)</span> 得到当前块的 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial P_{ij}}\)</span>，再结合 <span displaypfx="inline-" class="mathjax-container">\(\delta_i=g_i^\top o_i\)</span> 计算当前块的 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial S_{ij}}\)</span>。</li>
<li>把该块对 <span displaypfx="inline-" class="mathjax-container">\(dQ_i\)</span>、<span displaypfx="inline-" class="mathjax-container">\(dK_j\)</span>、<span displaypfx="inline-" class="mathjax-container">\(dV_j\)</span> 的贡献直接累加到输出梯度中。</li>
</ol>
<p>写成块级公式，就是：</p>
<span displaypfx="" class="mathjax-container">\[dV_j \mathrel{+}= P_{ij}^\top G_i\]</span>
<span displaypfx="" class="mathjax-container">\[dP_{ij}=G_iV_j^\top\]</span>
<span displaypfx="" class="mathjax-container">\[dS_{ij}=P_{ij}\odot \left(dP_{ij}-\delta_i\mathbf{1}^\top\right)\]</span>
<span displaypfx="" class="mathjax-container">\[dQ_i \mathrel{+}= dS_{ij}\frac{K_j}{\sqrt{d_k}},\qquad dK_j \mathrel{+}= dS_{ij}^\top\frac{Q_i}{\sqrt{d_k}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(dQ_i,dK_j,dV_j\)</span> 分别表示当前块对 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial Q}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial K}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial V}\)</span> 的局部累加贡献；符号 <span displaypfx="inline-" class="mathjax-container">\(\mathrel{+}=\)</span> 表示“把当前块的贡献继续累加到已有梯度里”，而不是一次性覆盖赋值。</p>
<p>这个设计的代价是：backward 需要重算部分前向中的块级分数和概率，因此算术量会比“全存中间矩阵”的朴素实现略多；但现代 GPU 上，额外矩阵乘法通常比反复读写 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span> HBM 张量便宜得多。于是 FlashAttention backward 的工程哲学可以概括为：<span style="background-color: #c0c0c0;">用少量重算换大幅节省显存与 IO</span>。</p>
<p>一个直观类比是：普通 backward 像把前向每一道工序的全部半成品都堆满仓库，等回头算梯度时再逐件取出来；FlashAttention backward 则更像保留每条流水线的关键账本和最终产物，真正需要某段中间细节时，再按原流程快速重演一小段。仓库变小了，流水线也更连贯。</p>
<div class="blog_h4"><span class="graybg">FlashAttention v1：算法层优化</span></div>
<p>FlashAttention v1 的核心贡献在于算法层：它首先把“注意力必须显式存下 <span displaypfx="inline-" class="mathjax-container">\(N\times N\)</span> 矩阵”这一默认前提打破，给出了一种精确、稳定、块级流式的注意力实现。v1 的关键词是 <span style="background-color: #c0c0c0;">memory optimization</span>：让注意力从“被中间矩阵拖慢”转向“更像一个流式矩阵核”。</p>
<p>在这个阶段，最重要的不是把 GPU 跑满，而是先证明：不物化注意力矩阵，仍然可以精确完成前向与反向计算，并把显存墙显著后移。它解决的是“能不能这样算”的问题。</p>
<div class="blog_h4"><span class="graybg">FlashAttention v2：并行层优化</span></div>
<p>FlashAttention v2 保留了 v1 的数学等价性与在线 softmax 思路，但把优化重点从“省内存”推进到“把 GPU 吃满”。它关注的是并行工作划分（Work Partitioning）：如何把 query 块、head 维度、batch 维度和线程块（Thread Block）组织得更均匀，让更多流式多处理器（Streaming Multiprocessor, SM）同时处于忙碌状态。</p>
<p>v1 的一个现实限制是：虽然显存访问已经大幅减少，但某些场景下并行粒度仍然偏粗，导致 GPU 占用率（Occupancy）不够高。v2 因此重写了 kernel 调度策略，让同一个大任务能够拆给更多线程块并行处理，同时尽量减少线程同步（Synchronization）带来的停顿。</p>
<p>从本质上看，v2 做的不是“新的注意力公式”，而是“同一公式在 GPU 上的更优任务分发”。如果说 v1 的问题是“别把中间矩阵落盘”，那么 v2 的问题就是“别让 GPU 的很多 SM 闲着”。</p>
<p>这也是 v2 在反向传播（Backward Pass）上价值很高的原因。前向只解决一半问题；训练吞吐还取决于 backward kernel 能否在不恢复 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span> 显存占用的前提下保持高并行度。v2 在这一点上比 v1 更成熟，因此更适合作为训练时的高性能默认实现。</p>
<div class="blog_h4"><span class="graybg">FlashAttention v3：硬件协同优化</span></div>
<p>FlashAttention v3 的重点进一步从并行层推进到硬件协同设计（Hardware Co-design），尤其针对 NVIDIA Hopper / H100 这类新一代 GPU。它不再只关心“块怎么切、线程怎么分”，而是进一步追问：<span style="background-color: #c0c0c0;">数据加载、矩阵计算、结果写回能否形成异步流水线</span>。</p>
<p>v3 的几个代表性关键词包括：</p>
<ul>
<li>异步流水线（Asynchronous Pipeline）：加载下一块数据时，当前块已经在计算，从而重叠 load 与 compute。</li>
<li>Warp 专职分工（Warp Specialization）：不同 warp 分别负责搬运、计算、写回，减少彼此等待。</li>
<li>Tensor Core 深度利用：tile 尺寸与数据流更贴近 Tensor Core 最擅长的矩阵乘法路径。</li>
<li>更适合低精度数据类型：如 FP16、BF16，以及面向新硬件的 FP8 路径。</li>
</ul>
<p>如果把 v1 看成“算法上不建大仓库”，把 v2 看成“让更多工人同时开工”，那么 v3 更像“把整座工厂变成不停顿的装配线”：搬运、计算、写回三条流水同时进行，尽量让每一级硬件资源都不空转。</p>
<div class="blog_h4"><span class="graybg">版本演进总结</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">版本</td>
<td style="text-align: center;">主要优化层次</td>
<td style="text-align: center;">核心目标</td>
<td style="text-align: center;">本质关键词</td>
</tr>
</thead>
<tbody>
<tr>
<td>v1</td>
<td>算法层</td>
<td>避免 <span displaypfx="inline-" class="mathjax-container">\(N^2\)</span> 中间矩阵物化</td>
<td>分块、在线 softmax、融合计算</td>
</tr>
<tr>
<td>v2</td>
<td>并行层</td>
<td>提高 Occupancy，减少同步，提升训练吞吐</td>
<td>更细粒度 work partitioning</td>
</tr>
<tr>
<td>v3</td>
<td>硬件层</td>
<td>让 load / compute / store 深度重叠</td>
<td>异步流水线、warp specialization、Tensor Core 对齐</td>
</tr>
</tbody>
</table>
<p>因此，FlashAttention 的演进可以概括为三层推进：v1 解决“能否不存矩阵”、v2 解决“如何把 GPU 跑满”、v3 解决“如何贴着新硬件的数据通路跑”。三代版本的数学目标完全一致，差异主要体现在实现层面对 IO、并行性与硬件流水的挖掘深度。</p>
<div class="blog_h3"><span class="graybg">线性注意力（Linear Attention）</span></div>
<p>线性注意力（Linear Attention）不是“更快的实现”，而是对注意力公式做近似/改写，把复杂度从 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(L^2)\)</span> 降到近似 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(L)\)</span>。一类常见思路是用核函数特征映射（Kernel Feature Map）近似 softmax kernel：把 <span displaypfx="inline-" class="mathjax-container">\(\exp(q^\top k)\)</span> 写成 <span displaypfx="inline-" class="mathjax-container">\(\phi(q)^\top \phi(k)\)</span>，从而把“先算 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span> 再乘 <span displaypfx="inline-" class="mathjax-container">\(V\)</span>”改写为“先聚合 <span displaypfx="inline-" class="mathjax-container">\(\phi(K)\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 再与 <span displaypfx="inline-" class="mathjax-container">\(\phi(Q)\)</span> 交互”。</p>
<p>一个典型形式（省略实现细节）是：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Attn}(Q,K,V)\approx \frac{\phi(Q)\big(\phi(K)^\top V\big)}{\phi(Q)\big(\phi(K)^\top \mathbf{1}\big)}\]</span>
<p>线性注意力更适合“极长序列下的吞吐/显存”目标，但它往往需要在表示、数值稳定性与效果之间做取舍；在通用 LLM 上，主流路径仍然是“精确注意力 + 更好的内核 + 更强的位置/缓存工程”，线性注意力更多作为特定场景或混合架构的选项。</p>
<div class="blog_h3"><span class="graybg">状态空间模型（State Space Model, SSM / Mamba）</span></div>
<p>状态空间模型（State Space Model, SSM）用“隐状态递推（State Recurrence）”建模序列：每步用一个小状态 <span displaypfx="inline-" class="mathjax-container">\(s_t\)</span> 累积历史信息，避免显式构造 <span displaypfx="inline-" class="mathjax-container">\(L\times L\)</span> 注意力矩阵。经典线性 SSM 的抽象形式是：</p>
<span displaypfx="" class="mathjax-container">\[s_{t+1}=As_t+Bx_t,\quad y_t=Cs_t+Dx_t\]</span>
<p>近年的 Mamba 等结构可理解为在此基础上引入输入依赖的选择性/门控机制（Selective / Input-dependent Dynamics），使得模型在保持线性复杂度的同时具备更强的表征能力。工程上，SSM 的优势通常体现在长序列吞吐与显存；代价是“按内容随机访问历史”的能力不如注意力直观，因此在需要强检索/对齐的任务上常见的是混合架构或与注意力模块组合使用。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">路线</td>
<td style="text-align: center;">序列复杂度</td>
<td style="text-align: center;">显存瓶颈</td>
<td style="text-align: center;">强项</td>
<td style="text-align: center;">典型代价</td>
</tr>
</thead>
<tbody>
<tr>
<td>精确注意力（FlashAttention 等）</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(L^2)\)</span></td>
<td>注意力中间张量 + KV Cache</td>
<td>强检索/对齐；通用能力稳健</td>
<td>长上下文成本陡增；需要大量工程优化（GQA/分页/缓存）</td>
</tr>
<tr>
<td>线性注意力</td>
<td>近似 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(L)\)</span></td>
<td>缓存布局与数值稳定性</td>
<td>极长序列吞吐/显存友好</td>
<td>近似误差；需要专门核函数/特征映射设计</td>
</tr>
<tr>
<td>SSM / Mamba</td>
<td><span displaypfx="inline-" class="mathjax-container">\(\mathcal{O}(L)\)</span></td>
<td>状态与算子实现</td>
<td>长序列吞吐；流式友好</td>
<td>随机访问历史不如注意力直观；常需混合架构补齐能力</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">GQA（Grouped Query Attention）</span></div>
<p>GQA（Grouped Query Attention）用“更少的 KV 头”服务“更多的 Query 头”：多个 Query heads 共享同一组 Key/Value heads，从而显著降低 KV Cache 的显存与带宽压力。极端情形 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}=1\)</span> 称为 MQA（Multi-Query Attention）。</p>
<p>对比标准多头注意力（MHA）：MHA 通常 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}=n_q\)</span>；而 GQA 让 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}\ll n_q\)</span>，注意力仍然按 head 计算，但 KV 表示被“组共享”。在长上下文推理中，它带来的收益往往比对算力的节省更关键：KV Cache 与内存带宽近似按 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}/n_q\)</span> 比例下降。</p>
<p>代价是表示自由度下降：不同 Query heads 看到的 Key/Value 空间更相似，可能带来一定质量损失；工程上通常通过更大的模型维度、更多 Query heads、或更强的 FFN 来补偿。</p>
<p>进一步降低 KV Cache 成本的路线，还包括 Latent KV / MLA 一类潜空间压缩，以及 TurboQuant 一类面向内积保真的 KV 量化压缩。它们的直接优化对象都是长上下文推理里的 KV Cache 存储与带宽，因此更适合放在后文“推理阶段优化”中统一讨论。</p>
<div class="blog_h2"><span class="graybg">前馈网络（FFN）</span></div>
<div class="blog_h3"><span class="graybg">MLP</span></div>
<p>Transformer 层里的前馈网络（Feed-Forward Network, FFN）本质上就是一个位置前馈（Position-wise）MLP：它对每个 token 的向量独立作用，不在序列维度做混合（序列维度的混合由注意力完成）。典型形式是两层线性变换加非线性：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{FFN}(x)=\sigma(xW_1+b_1)W_2+b_2\]</span>
<p>这里为了贴近工程实现，把单个 token 表示写成行向量 <span displaypfx="inline-" class="mathjax-container">\(x\in\mathbb{R}^{1\;\times d_{\text{model}}}\)</span>。若中间宽度为 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{ff}}\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(W_1\in\mathbb{R}^{d_{\text{model}}\;\times d_{\text{ff}}}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(W_2\in\mathbb{R}^{d_{\text{ff}}\;\times d_{\text{model}}}\)</span>，因此这层先把 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 维表示升到更宽的 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{ff}}\)</span> 维，再投回原宽度。很多教材把它叫“MLP 模块”，强调的是它在每层里与注意力并列构成 Transformer block 的两大子层。</p>
<div class="blog_h4"><span class="graybg">经典 FFN：先升维，再降维</span></div>
<p>“升维（Up-Projection）”指的是用线性投影把输入映射到更高维的特征空间。新维度不是补零得到的，也不是旧特征的简单复制。若</p>
<span displaypfx="" class="mathjax-container">\[h_{\text{up}}=xW_1+b_1\in\mathbb{R}^{1\;\times d_{\text{ff}}}\]</span>
<p>则第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个中间维度满足</p>
<span displaypfx="" class="mathjax-container">\[(h_{\text{up}})_j=\sum_{i=1}^{d_{\text{model}}}x_i(W_1)_{ij}+b_{1j}\]</span>
<p>这意味着：升出来的每一维都在重新组合输入特征。有些维度更像“检测某种局部模式”，有些维度更像“混合多种语义线索”，中间宽度越大，可供模型学习的组合方式就越多。随后，非线性函数 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span>（常见如 GELU / ReLU）对这些组合结果做逐元素变换，把线性组合提升为非线性特征。</p>
<p>“降维（Down-Projection）”也不是简单删掉多余维度，而是再做一次线性组合：</p>
<span displaypfx="" class="mathjax-container">\[h_{\text{down}}=\sigma(h_{\text{up}})W_2+b_2\in\mathbb{R}^{1\;\times d_{\text{model}}}\]</span>
<p>因此，经典 FFN 的结构可以概括成：<span style="background-color: #c0c0c0;">先在更宽的特征空间里生成大量候选特征，再把有用的那部分重新组合回模型主宽度</span>。这就是“升维—非线性—降维”的真正含义。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/ffn-glu-structure-canvas.png"><img class="alignnone size-full wp-image-41679" src="https://blog.gmem.cc/wp-content/uploads/2026/03/ffn-glu-structure-canvas.png" alt="ffn-glu-structure-canvas" width="1920" height="1080" /></a></p>
<div class="blog_h4"><span class="graybg">门控 FFN：以 SwiGLU 为例</span></div>
<p>很多现代大模型会用门控线性单元（Gated Linear Unit, GLU）的变体替代“Linear → 激活 → Linear”的经典 FFN，例如 SwiGLU（Swish-Gated Linear Unit）：把中间层拆成两路并行投影，再做逐元素门控：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{SwiGLU}(x)=\Big(\mathrm{SiLU}(xW_1)\odot (xW_3)\Big)W_2\]</span>
<p>若仍按行向量写法，则 <span displaypfx="inline-" class="mathjax-container">\(W_1,W_3\in\mathbb{R}^{d_{\text{model}}\;\times d_{\text{ff}}}\)</span>， <span displaypfx="inline-" class="mathjax-container">\(W_2\in\mathbb{R}^{d_{\text{ff}}\;\times d_{\text{model}}}\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(W_1\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(W_3\)</span> 都是“升维投影”，但角色不同： <span displaypfx="inline-" class="mathjax-container">\(xW_1\)</span> 经过 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{SiLU}\)</span> 后形成门控分支（gate branch），决定每个中间维度应当放大、通过还是抑制； <span displaypfx="inline-" class="mathjax-container">\(xW_3\)</span> 则形成内容分支（value / candidate branch），携带候选特征本身。两者做逐元素乘法 <span displaypfx="inline-" class="mathjax-container">\(\odot\)</span> 后，得到“被门控筛选过的中间表示”，最后再由 <span displaypfx="inline-" class="mathjax-container">\(W_2\)</span> 投回 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span>。</p>
<p>因此， <span displaypfx="inline-" class="mathjax-container">\(W_3\)</span> 不是额外多出来的“神秘矩阵”，而是门控 FFN 里的第二条并行升维支路。没有它，模型只有“激活后的门”，却没有“真正被门控制的候选内容”；有了它，FFN 才能表达“哪些特征值得通过、哪些特征应被压制”这一层选择机制。</p>
<p>这种“哪些维度打开、哪些维度抑制”的规则并不是人工写死的，而是通过训练从数据中学出来的。对第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个中间维度，门控值可以写成 <span displaypfx="inline-" class="mathjax-container">\(g_j=\mathrm{SiLU}((xW_1)_j)\)</span>，候选内容写成 <span displaypfx="inline-" class="mathjax-container">\(c_j=(xW_3)_j\)</span>，二者相乘后该维输出为 <span displaypfx="inline-" class="mathjax-container">\(m_j=g_j c_j\)</span>。若某类输入模式下，让这个维度更大能够降低最终损失，则反向传播会推动 <span displaypfx="inline-" class="mathjax-container">\(W_1\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(W_3\)</span> 把对应的 <span displaypfx="inline-" class="mathjax-container">\(g_j\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(c_j\)</span> 调到更有利的方向；若某个维度会带来噪声、干扰或错误特征，则梯度会推动该维在这类输入上变小，于是门控值逐渐靠近 0，内容即使存在也难以通过。</p>
<p>因此，门控学习到的不是一个离散的“开 / 关开关”，而是一组随输入变化的连续缩放系数。某些维度在数学推理样本上可能长期被放大，在闲聊样本上则被压弱；某些维度对代码括号、缩进、关键字组合更敏感，另一些维度则更偏向实体关系或长距离语义线索。门控 FFN 的本质，是让模型在更宽的中间空间里先生成大量候选特征，再由可学习的输入相关门控决定哪些特征应该被保留、哪些应被抑制。</p>
<p>门控（Gating）让 FFN 具备“按特征选择通过 / 抑制”的能力，在相近参数规模下常带来更好的效果与训练稳定性。与经典两层 FFN 相比，门控 FFN 并不是单纯“多一层”，而是把中间表示拆成“控制信号”和“候选内容”两路，再在中间宽空间里完成细粒度筛选。</p>
<p>从能力角度看，增大 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{ff}}\)</span> 会增加中间表征的自由度（Degree of Freedom, DOF）与参数量，使模型能构造更丰富的非线性特征；但“维度更高”不等于“信息一定更多”，它提供的是可学习的表示空间与容量（Capacity），是否有效取决于数据与训练目标。</p>
<div class="blog_h3"><span class="graybg">Mixture of Experts（MoE）</span></div>
<p>MoE（Mixture of Experts）把 FFN 子层替换成“多个专家网络（Experts）+ 路由器（Router/Gate）”：对每个 token，路由器只激活少数几个专家（Top-k），因此计算量近似不随专家总数线性增长，但参数容量可以大幅增加。</p>
<p>一种常见形式（概念表达）是：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{MoE}(x)=\sum_{e\in\mathrm{TopK}(x)} p_e(x)\,\mathrm{Expert}_e(x),\quad p(x)=\mathrm{softmax}(W_g x)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(p_e(x)\)</span> 是路由概率，专家通常就是不同参数的 FFN。与稠密 FFN 的区别在于：稠密 FFN 对每个 token 都执行同一套参数；MoE 则先由路由器决定“这个 token 该送去哪些专家”，再只计算被选中的少数几个专家。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/moe.png"><img class="alignnone size-full wp-image-41675" src="https://blog.gmem.cc/wp-content/uploads/2026/03/moe.png" alt="moe" width="1024" height="1024" /></a></p>
<p>专家的差异化（Specialization）来源于<span style="background-color: #c0c0c0;">路由选择、梯度暴露和训练约束共同塑造的长期分工</span>。随机初始化只负责打破完全对称；真正让专家“越学越不一样”的，是后续每个专家持续处理不同 token 子分布，并在这些子分布上反复累积参数更新。</p>
<p>这个过程通常由以下几类机制共同推动：</p>
<ul>
<li><span style="background-color: #c0c0c0;">稀疏路由</span>：每个 token 前向时通常只进入 top-k 个专家，因此反向传播时，也只有被选中的专家接收到该 token 的主要梯度。不同专家长期看到的训练样本分布因此不再相同，参数更新方向也随之分化。</li>
<li><span style="background-color: #c0c0c0;">路由—能力自增强</span>：路由器先按当前表示给专家打分；某个专家一旦更常处理一类模式，就会在这类模式上进一步拟合得更好；下一轮遇到相似 token 时，路由器又更容易把它们送回这个专家。久而久之，专家会演化成代码型、数学型、长句法型或领域词汇型等不同处理器。</li>
<li><span style="background-color: #c0c0c0;">负载均衡损失</span>：若完全放任训练，路由器容易把大量 token 都送往少数“热门专家”，其余专家几乎得不到梯度。负载均衡（Load Balancing）辅助损失会惩罚这种失衡，推动更多专家获得稳定训练信号，从而保留分工空间，而不是塌缩成少数几个超忙专家。</li>
<li><span style="background-color: #c0c0c0;">容量限制</span>：工程实现常给每个专家设置每个 batch 最多接收多少 token 的上限。热门专家一旦满载，后续 token 就必须改道到其他专家。这相当于在训练期强行制造“分流”，避免所有高频模式都被同一专家垄断。</li>
<li><span style="background-color: #c0c0c0;">路由噪声与探索</span>：训练早期常在路由分数上加入噪声（Noisy Gating / Jitter）或采用更平滑的选择策略，使模型不会过早把某些专家永久冷启动掉。它的作用类似探索机制：先让更多专家接触不同 token，后续再由训练结果把分工逐步固化。</li>
<li><span style="background-color: #c0c0c0;">Top-k 竞争结构</span>：当多个专家为同一 token 竞争有限的 top-k 名额时，路由器天然在做离散化分配。专家之间并不是同时都拿到完整梯度，而是在竞争中各自吸附不同区域的输入分布。这比稠密加权平均更容易形成明确边界。</li>
<li><span style="background-color: #c0c0c0;">专家参数独立</span>：每个专家有自己独立的 FFN 权重，因此一旦早期路由稍有偏向，后续参数更新就会沿不同轨迹不断放大差异。若专家共享大部分参数，仅保留极少差异分支，则这种专门化能力会明显减弱。</li>
<li><span style="background-color: #c0c0c0;">数据分布本身的可分性</span>：训练语料若天然包含代码、自然语言、表格、数学推导、多语种等明显子分布，专家更容易形成稳定分工；若数据分布高度均匀、模式差异很弱，则专家专门化也会更弱，更接近“多份相似 FFN”。</li>
</ul>
<p>这些机制叠加后，MoE 中“每个专家学不同东西”就不再只是参数副本的偶然漂移，而是带有明确结构约束的分工过程。与多头注意力主要依赖独立参数的自发分化不同，MoE 额外利用<span style="background-color: #c0c0c0;">显式路由、稀疏梯度、负载约束与容量分流</span>来持续放大专家之间的功能差异。</p>
<p>MoE 结构本身不必然引入随机性。若路由使用确定性的 top-k，且推理使用确定性算子，则同一输入在同一权重下输出应是确定的。训练阶段常见的随机性主要来自 dropout、路由噪声（Noisy Gating）以及硬件/并行计算的非确定性；这些会影响训练轨迹，但不等价于“模型本质随机”。</p>
<div class="blog_h2"><span class="graybg">归一化</span></div>
<div class="blog_h3"><span class="graybg">Layer Normalization</span></div>
<p>层归一化（Layer Normalization, LayerNorm）在每个 token 的特征维度上做归一化（Normalization），与 BatchNorm 不同，它不依赖 batch 统计量，因此更适合变长序列与自回归推理。对向量 <span displaypfx="inline-" class="mathjax-container">\(x\in\mathbb{R}^{d_{\text{model}}}\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{LN}(x)=\gamma\odot \frac{x-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta,\quad \mu=\frac{1}{d}\sum_{i=1}^{d}x_i,\ \sigma^2=\frac{1}{d}\sum_{i=1}^{d}(x_i-\mu)^2\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\gamma,\beta\)</span> 是可学习的缩放与平移参数（Learnable Scale/Shift），<span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 是一个很小的正数，用于数值稳定性（Numerical Stability）。归一化本质上要除以标准差或均方根；若当前 token 的各维几乎相同，分母就可能非常接近 0，进而导致输出或梯度被异常放大，甚至出现 NaN / Inf。加入 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 相当于给分母设置一个下界。它通常很小，只在“方差或 RMS 过小”时介入；若 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 取值过大，则会把分母中的真实尺度差异压平，使归一化变弱，模型对幅值变化的敏感度下降。</p>
<p>当前主流的 Transformer 几乎都采用“token 内部归一化”，而不是跨 batch 的 BatchNorm：经典 Transformer、BERT、ViT 这一路架构以 LayerNorm 为主；许多更新的 Decoder-only 大模型则把每个残差块写成 Pre-Norm 结构，并进一步用 RMSNorm 取代标准 LayerNorm。</p>
<p>Pre-LN（Pre-LayerNorm）指先做归一化，再进入 Attention 或 MLP 子层，最后与残差分支相加；其典型形式可写为：</p>
<span displaypfx="" class="mathjax-container">\[y=x+\mathrm{Sublayer}(\mathrm{LN}(x))\]</span>
<p>这种写法把归一化放进残差支路内部，有利于维持深层网络中的梯度流稳定。结合后文残差连接的分析来看，Pre-LN 的一个直接优势是：梯度更容易沿着 <span displaypfx="inline-" class="mathjax-container">\(x\to x+\cdots\)</span> 这条恒等主路向后传播，而不会在进入子层之前就先经历一次“相加后再归一化”的整体重标定。与之对应，Post-LN 会写成 <span displaypfx="inline-" class="mathjax-container">\(y=\mathrm{LN}(x+\mathrm{Sublayer}(x))\)</span>；它在早期 Transformer 中出现较多，但随着层数、上下文窗口和参数规模持续增大，Pre-LN 在大模型训练中更常见。</p>
<p>BatchNorm 很少出现在 Transformer 主干中的原因，是它要求当前表示依赖同一 batch 里其他样本的统计量。对于序列模型，这会带来几个直接问题：</p>
<ul>
<li>变长序列和 padding 会污染 batch 统计。</li>
<li>训练与推理使用的统计规则不同，自回归逐 token 生成时尤其不自然。</li>
<li>大模型训练常依赖小 batch、梯度累积和跨设备切分，batch 统计噪声更大。</li>
</ul>
<p>LayerNorm / RMSNorm 则完全避免了这些问题，因为每个 token 的归一化只依赖其自身特征。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/pre-post-norm.png"><img class="alignnone size-full wp-image-41691" src="https://blog.gmem.cc/wp-content/uploads/2026/03/pre-post-norm.png" alt="pre-post-norm" width="1024" height="1024" /></a></p>
<div class="blog_h3"><span class="graybg">RMS Normalization</span></div>
<p>RMSNorm（Root Mean Square Normalization）与 LayerNorm 的相同点是：都在每个 token 的特征维度上做归一化；不同点是 RMSNorm <span style="background-color: #c0c0c0;">不做去均值</span>，只按均方根（RMS）缩放：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{RMSNorm}(x)=\gamma\odot \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d}x_i^2+\epsilon}}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\gamma\in\mathbb{R}^{d}\)</span> 是可学习缩放参数（通常不需要 <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span>），<span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 则是数值稳定项（Numerical Stability Term）。当 RMS 很小时，若没有 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span>，分母会过小，微小噪声也可能被异常放大；若 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 过大，又会把不同 token 之间本应存在的尺度差异压平，使归一化变弱。RMSNorm 省掉了均值计算，算子更简单，因此在许多 Decoder-only 大模型中被广泛采用（例如 LLaMA 系列）。</p>
<p>RMSNorm 之所以使用均方根，而不是直接对各维做算术平均，是因为这里要刻画的是<span style="background-color: #c0c0c0;">向量整体有多大</span>，而不是“各维带符号求平均后的中心位置”。若直接用 <span displaypfx="inline-" class="mathjax-container">\(\frac{1}{d}\sum_i x_i\)</span>，正负分量会彼此抵消：例如 <span displaypfx="inline-" class="mathjax-container">\((10,-10)\)</span> 的算术平均是 0，但这个向量的整体幅值显然并不小。均方根先平方再平均，保留了各维对整体能量的贡献，又与二范数只差一个 <span displaypfx="inline-" class="mathjax-container">\(\sqrt d\)</span> 的常数因子，因此很适合用来刻画表示的整体尺度。</p>
<p>这也是 RMSNorm 即使不做去均值，仍然常常有效的原因。对 Transformer 主干而言，更核心的问题通常不是“特征均值是否恰好为 0”，而是<span style="background-color: #c0c0c0;">表示的整体尺度能否在深层网络中保持稳定</span>。残差流里真正容易失控的，往往是表示向量的整体幅值在层与层之间持续放大或缩小，进而影响梯度传播、残差叠加与数值稳定。RMSNorm 保留了各维之间的相对方向与相对比例，只对整体大小做统一缩放，因此不会反复改写表示基线；对深层 Transformer 来说，这种“只管尺度、不强行去中心”的处理往往已经足够，而且算子更轻，更适合大规模训练与推理。也正因为如此，归一化与残差连接通常总是一起出现：前者负责稳定尺度，后者负责保留主通路。</p>
<div class="blog_h2"><span class="graybg">残差连接</span></div>
<p>残差连接（Residual Connection）把子层输出与输入做逐元素相加：</p>
<span displaypfx="" class="mathjax-container">\[y=x+\mathrm{Sublayer}(x)\]</span>
<p>它不改变主表示的维度（Dimension）——前提是 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Sublayer}(x)\)</span> 形状相同。这个写法的核心价值，不是“把两份向量简单相加”，而是把深层网络的每一层改写成：<span style="background-color: #c0c0c0;">在已有表示上追加一小步修正，而不是每层都彻底重写整份表示</span>。</p>
<p>若没有残差，网络某一层必须直接学出从输入到输出的完整映射；有了残差后，子层只需学习增量项 <span displaypfx="inline-" class="mathjax-container">\(\Delta(x)=\mathrm{Sublayer}(x)\)</span>。当最优行为接近恒等映射时，学习“加多少修正”通常比学习“整层重新变换成什么样”更容易。这也是残差连接与恒等映射（Identity Mapping）关系紧密的原因：主通路默认保留原信息，子层负责在其上叠加必要变化。</p>
<p>从优化角度看，残差连接直接改变了梯度传播路径。若把一层的输入 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 看成 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维向量，输出写成 <span displaypfx="inline-" class="mathjax-container">\(y=x+f(x)\)</span>，那么这里的求导就不再是标量对标量的导数，而是<span style="background-color: #c0c0c0;">向量对向量的 Jacobian 矩阵</span>。</p>
<p>先看最简单的恒等映射 <span displaypfx="inline-" class="mathjax-container">\(g(x)=x\)</span>。若 <span displaypfx="inline-" class="mathjax-container">\(x=(x_1,\dots,x_d)^\top\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(g_i(x)=x_i\)</span>。它的 Jacobian 第 <span displaypfx="inline-" class="mathjax-container">\((i,j)\)</span> 个元素是</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial g_i}{\partial x_j}=\frac{\partial x_i}{\partial x_j}=\begin{cases}1,&amp; i=j\\0,&amp; i\ne j\end{cases}\]</span>
<p>因此，向量对自身的导数不是数字 1，而是恒等矩阵：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial x}{\partial x}=I\]</span>
<p>再对残差块 <span displaypfx="inline-" class="mathjax-container">\(y=x+f(x)\)</span> 求导，就得到</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial y}{\partial x}=\frac{\partial x}{\partial x}+\frac{\partial f(x)}{\partial x}=I+\frac{\partial f(x)}{\partial x}\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(I\)</span> 正对应那条“把输入原样传过去”的恒等分支。它并不表示整层没有维度之间的交互，而只表示：在这条直连路径上，每一维对自身的导数是 1、对其他维的导数是 0。真正的维度混合、特征重组与 token 间交互，仍然由 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f(x)}{\partial x}\)</span> 负责。含义是：即使子层 <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 的局部 Jacobian 很小、很噪，或训练初期还没有学好，梯度仍然可以沿着这条恒等路径直接穿过该层，而不必完全依赖 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f(x)}{\partial x}\)</span>。深层网络因此更不容易出现梯度迅速衰减，训练也更稳定。</p>
<p>从表示角度看，残差连接建立了一条贯穿全网的主通道，这正是前面多次出现的残差流（Residual Stream）。在 Transformer 中，注意力子层负责跨 token 交换信息，MLP / FFN 负责对单个 token 做非线性重组，而它们的输出都不是“另起炉灶”的新表示，而是写回这条主通道。于是每一层都更像是在同一块工作记忆上持续读写：有的层补充局部依赖，有的层补充长程关系，有的层强化事实模式或语法结构。</p>
<p>残差连接还有一个很重要的工程意义：它允许模型在“保留已有信息”和“注入新特征”之间取得平衡。若某层子层输出很弱，网络行为就更接近恒等传递；若某层确实学到了有价值的新模式， <span displaypfx="inline-" class="mathjax-container">\(f(x)\)</span> 就会沿某些表示方向显著写回主通道。后续层不需要把两部分精确拆开，只需要继续利用这个叠加后的结果即可，因为后续线性映射、注意力和归一化会在新的坐标方向上重新组织这些信息。</p>
<p>从反向传播（Backpropagation）的角度看，残差连接的价值同样直接。若没有残差，深层网络中的梯度必须连续穿过许多子层 Jacobian，相当于做多次矩阵连乘；当这些局部导数长期偏小，梯度就容易逐层衰减，出现梯度消失（Vanishing Gradient）；当它们长期偏大，又可能造成梯度爆炸（Exploding Gradient）。加入残差后，每一层的局部导数从 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial f(x)}{\partial x}\)</span> 变成了 <span displaypfx="inline-" class="mathjax-container">\(I+\frac{\partial f(x)}{\partial x}\)</span>，于是梯度不再只能依赖子层本身，而始终保留了一条沿恒等分支传播的主路径。可以把它概括成一句话：<span style="background-color: #c0c0c0;">前向传播时保留原信息，反向传播时保留主梯度通路</span>。这正是残差连接能显著缓解深层网络优化困难的根本原因。</p>
<p>这也是为什么残差连接几乎成为现代深网络的标准部件。对于非常深的模型，真正困难的并不是“单层表达能力不够”，而是层数增加后，前向信息更容易被后续变换不断改写，反向梯度也更容易在长链路中衰减或失稳。残差连接用一条恒等主路同时缓解了这两个问题：前向上保留原信息，反向上保留主梯度通路。因此，ResNet、Transformer、扩散模型乃至许多大型序列模型，都会把它作为主干结构的一部分。</p>
<div class="blog_h2"><span class="graybg">输出处理</span></div>
<p>Transformer 主干（Backbone）本身的直接产物通常不是“类别”或“文字”，而是一组上下文化隐藏状态（Contextual Hidden States）。也就是说，在一次标准前向计算里，模型会先处理当前输入序列中的所有 token，把每个位置都编码成上下文化表示；若输入序列长度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>，模型宽度为 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span>，经过最后一层后常得到：</p>
<span displaypfx="" class="mathjax-container">\[H^{(L)}\in\mathbb{R}^{T\;\times d_{\text{model}}}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 是 Transformer 层数，因此 <span displaypfx="inline-" class="mathjax-container">\(H^{(L)}\)</span> 表示“经过第 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 层之后得到的隐藏状态矩阵”； <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 是当前序列的 token 数，所以这个矩阵一共有 <span displaypfx="inline-" class="mathjax-container">\(T\)</span> 行，每一行对应一个位置。若把第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置那一行单独记作 <span displaypfx="inline-" class="mathjax-container">\(h_t^{(L)}\)</span>，它表示的就是：第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个 token 在通过全部 <span displaypfx="inline-" class="mathjax-container">\(L\)</span> 层、吸收了上下文信息之后得到的最终向量表示。输出处理（Output Processing）的任务，就是把这组隐藏状态映射到具体任务所需的输出空间：可以是词表概率、类别分数、序列标签、起止位置分数，或回归数值。这里常说的读出（Readout），指的就是：<span style="background-color: #c0c0c0;">主干网络先形成内部表示，再由最后的输出层把这种表示转换成任务空间里的可解释结果</span>。更准确地说，Transformer 主干先产生整段序列的隐藏表示，再由具体任务的输出层决定如何读取这些表示：有的任务会逐位置读出，有的任务只取某个聚合位置；生成任务则在当前前缀对应的隐藏状态基础上，继续决定下一个 token 的输出。</p>
<div class="blog_h3"><span class="graybg">从隐藏状态到输出空间</span></div>
<p>最常见的输出处理是在线性读出（Linear Readout）层中，把 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span> 维隐藏状态投影到目标维度 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{out}}\)</span>。若按 token 逐位置读出，可写成：</p>
<span displaypfx="" class="mathjax-container">\[Z=HW_{\text{out}}+\mathbf{1}b^\top\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(H\in\mathbb{R}^{T\;\times d_{\text{model}}}\)</span> 是最后一层隐藏状态； <span displaypfx="inline-" class="mathjax-container">\(W_{\text{out}}\in\mathbb{R}^{d_{\text{model}}\;\times d_{\text{out}}}\)</span> 是输出投影矩阵； <span displaypfx="inline-" class="mathjax-container">\(b\in\mathbb{R}^{d_{\text{out}}}\)</span> 是偏置； <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}\in\mathbb{R}^{T}\)</span> 是全 1 列向量，用来把同一个偏置加到每个位置； <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{T\;\times d_{\text{out}}}\)</span> 则是每个位置对应的输出分数。若任务是词表预测，则 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{out}}=V\)</span>， <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 是词表大小；若任务是 token 分类，则 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{out}}=C\)</span>， <span displaypfx="inline-" class="mathjax-container">\(C\)</span> 是标签类别数。</p>
<p>并非所有任务都对每个 token 独立读出。序列分类常从整段序列中先取一个聚合表示，再做线性映射。例如 BERT 类模型常使用 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{CLS}]\)</span> 位置的隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(h_{\mathrm{CLS}}\)</span>，再输出：</p>
<span displaypfx="" class="mathjax-container">\[z=h_{\mathrm{CLS}}W_c+b_c\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}^{C}\)</span> 是整句的类别 logits。跨度抽取（Span Extraction）任务则常对每个位置分别给出“作为起点”和“作为终点”的分数；序列到序列任务中，解码器则对每个时间步读出一个词表分布。</p>
<div class="blog_h3"><span class="graybg">语言模型中的输出处理</span></div>
<p>在 Decoder-only 或 Encoder-Decoder 的生成端，输出处理通常还包含最后一次归一化层（如 LayerNorm 或 RMSNorm）以及语言模型头（Language Modeling Head, LM Head）。概念上可写成：</p>
<span displaypfx="" class="mathjax-container">\[\tilde H=\mathrm{Norm}(H^{(L)}),\qquad Z=\tilde H W_{\mathrm{vocab}}+\mathbf{1}b^\top\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\tilde H\in\mathbb{R}^{T\;\times d_{\text{model}}}\)</span> 是最终归一化后的隐藏状态； <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{vocab}}\in\mathbb{R}^{d_{\text{model}}\;\times V}\)</span> 是词表投影矩阵； <span displaypfx="inline-" class="mathjax-container">\(Z\in\mathbb{R}^{T\;\times V}\)</span> 是每个位置对整个词表的 logits。矩阵第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 行 <span displaypfx="inline-" class="mathjax-container">\(z_t\in\mathbb{R}^{V}\)</span> 描述的是：当模型已经看到当前位置之前允许访问的上下文后，当前位置对每个候选 token 的偏好分数。</p>
<p>这里最后再做一次归一化，并不是多余的重复，而是把“主干内部的表示空间”整理成更适合词表读出的数值形态。Transformer 主干中的隐藏状态一路沿着残差流（Residual Stream）传播，虽然语义信息已经形成，但向量整体尺度仍可能随着层数、上下文和激活模式发生波动。若直接把 <span displaypfx="inline-" class="mathjax-container">\(H^{(L)}\)</span> 送入 <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{vocab}}\)</span>，这些尺度变化会被直接放大到 logits 上，使 softmax 有时过尖、有时过平，输出分布与梯度都更难稳定。</p>
<p>最后一次归一化的作用，是在进入词表空间之前先把隐藏状态重新放回一个稳定坐标系里：一方面减弱“幅值忽大忽小”对 logits 的直接干扰，另一方面让 LM Head 更专注于“当前表示朝哪个语义方向更接近某个 token”，而不是过度依赖向量长度本身。换言之，主干网络负责把内容表示出来，最后的归一化负责把这种内容整理到一个尺度可控、便于读出的状态，再交给 <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{vocab}}\)</span> 做最终投影。这也是许多现代 Decoder-only 大模型会在输出头前保留一层 LayerNorm 或 RMSNorm 的原因。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/lmhead.png"><img class="alignnone size-large wp-image-41727" src="https://blog.gmem.cc/wp-content/uploads/2026/03/lmhead.png" alt="lmhead" width="710" height="710" /></a></p>
<div class="blog_h4"><span class="graybg">权重共享（Weight Tying）</span></div>
<p>语言模型里常把输入嵌入表 <span displaypfx="inline-" class="mathjax-container">\(E\in\mathbb{R}^{V\;\times d_{\text{model}}}\)</span> 与输出头权重绑定为同一组参数。若不共享，输出头通常写成 <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{vocab}}\in\mathbb{R}^{d_{\text{model}}\;\times V}\)</span>；若共享，则直接令</p>
<span displaypfx="" class="mathjax-container">\[W_{\mathrm{vocab}}=E^\top\]</span>
<p>这不是把“两份数据塞进一个矩阵”，而是让同一个参数矩阵在前向计算的两个位置重复使用：输入阶段按 token id 取出 <span displaypfx="inline-" class="mathjax-container">\(E\)</span> 的某一行作为该 token 的嵌入；输出阶段则把隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(h\)</span> 与所有 token 向量做点积，得到整张词表的 logits：</p>
<span displaypfx="" class="mathjax-container">\[z=hE^\top+b\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(z\in\mathbb{R}^{V}\)</span>，第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个分量 <span displaypfx="inline-" class="mathjax-container">\(z_i=h\cdot E_i+b_i\)</span> 表示当前隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(h\)</span> 与第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个 token 向量 <span displaypfx="inline-" class="mathjax-container">\(E_i\)</span> 的匹配分数。输入嵌入回答“这个 token 进来时长什么样”，输出头回答“当前语境最像词表里的哪个 token”；Weight Tying 让这两种词向量语义共用同一个坐标系。</p>
<p>这里共享的是参数。训练时，这张矩阵同时接收两类梯度：一类来自输入查表路径，更新当前 batch 真正出现过的 token 行；另一类来自输出 softmax 路径，推动隐藏状态与目标 token 更接近、与竞争 token 拉开。自动求导会把这两部分梯度加到同一份参数上，形成联合更新。因此它通常能减少参数量、增强输入与输出语义空间的一致性，并起到一定正则化（Regularization）作用。</p>
<p>只有在输入嵌入维度与输出读出维度一致时，这种共享才最直接。若模型在读出前额外引入了投影层，使输出维度不再等于 <span displaypfx="inline-" class="mathjax-container">\(d_{\text{model}}\)</span>，则需要先做维度变换，或不共享。Weight Tying 因而是常见做法，但不是所有架构都必须采用的硬规则。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/lm-output-head.png"><img class="alignnone size-full wp-image-41479" src="https://blog.gmem.cc/wp-content/uploads/2026/03/lm-output-head.png" alt="lm-output-head" width="1920" height="1080" /></a></p>
<div class="blog_h3"><span class="graybg">从分数到最终结果</span></div>
<p>输出处理的最后一步，是把 logits 变成任务可用的结果。训练时，很多损失函数会直接接收 logits，例如交叉熵损失（Cross-Entropy Loss）内部会把 softmax 与负对数似然（Negative Log-Likelihood）合并计算，以提高数值稳定性。推理时，则通常再做显式后处理：分类任务对 logits 做 softmax 或 sigmoid 得到概率；序列标注任务可在 logits 之上接 CRF 解码；生成任务则对词表 logits 做 softmax 后，再通过贪心搜索（Greedy Decoding）、束搜索（Beam Search）、Top-k 采样或 Top-p 采样等策略选择下一个 token。</p>
<p>以生成任务为例，若当前位置的词表 logits 为 <span displaypfx="inline-" class="mathjax-container">\(z_t\in\mathbb{R}^{V}\)</span>，则先得到条件分布：</p>
<span displaypfx="" class="mathjax-container">\[p(x_{t+1}=i\mid x_{\le t})=\frac{e^{z_{t,i}}}{\sum_{j=1}^{V}e^{z_{t,j}}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(V\)</span> 是词表大小， <span displaypfx="inline-" class="mathjax-container">\(z_{t,i}\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个位置对第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个候选 token 的 logit， <span displaypfx="inline-" class="mathjax-container">\(p(x_{t+1}=i\mid x_{\le t})\)</span> 则是在当前前缀 <span displaypfx="inline-" class="mathjax-container">\(x_{\le t}\)</span> 下，下一个 token 取第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个词的概率。解码策略的区别，不在于 logits 或 softmax 公式不同，而在于：<span style="background-color: #c0c0c0;">拿到这组概率之后，究竟用什么规则选出真正输出的 token</span>。</p>
<div class="blog_h4"><span class="graybg">贪心搜索</span></div>
<p>贪心搜索（Greedy Decoding）是最直接的策略：每一步都选当前概率最大的那个 token。写成公式，就是</p>
<span displaypfx="" class="mathjax-container">\[x_{t+1}=\arg\max_{i} \ p(x_{t+1}=i\mid x_{\le t})\]</span>
<p>它的优点是速度快、实现简单、结果确定；缺点是过于短视。因为它每一步都只看“眼前概率最高”，而不考虑“当前稍差一点、但后续整体更优”的路径。于是贪心搜索很容易陷入局部最优：第一步看起来最稳的选择，不一定能导向整句概率最好的结果。</p>
<p>直觉上，它像每到路口都选眼前最宽的一条路，而不回头评估整条路线是否更通畅。因此贪心适合需要稳定、低延迟输出的场景，但在开放生成任务里往往较保守，也更容易重复。</p>
<div class="blog_h4"><span class="graybg">束搜索</span></div>
<p>束搜索（Beam Search）是在每一步同时保留多个高分候选前缀，而不是像贪心那样只保留 1 条路径。设束宽（Beam Width）为 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>，则在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步，算法会维护 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 条当前最优候选序列；每条序列再向外扩展多个 token，最后从所有扩展结果中重新筛出新的 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 条最高分路径继续前进。</p>
<p>若一条候选序列为 <span displaypfx="inline-" class="mathjax-container">\(x_{1:T}\)</span>，其常见打分方式是对数概率和：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{score}(x_{1:T})=\sum_{t=1}^{T}\log p(x_t\mid x_{&lt;t})\]</span>
<p>因为概率连乘会非常小，所以实现里通常比较对数概率之和，而不是直接比较概率乘积。有时还会加长度惩罚（Length Penalty），避免模型系统性偏爱过短序列。</p>
<p>束搜索的优点是全局性比贪心更强，常用于机器翻译、摘要等更强调整体序列质量的任务；缺点是计算量更高，而且它本质上仍是“找高分路径”的搜索，不会主动引入随机性，因此输出可能仍然偏保守、偏模板化。</p>
<div class="blog_h4"><span class="graybg">Top-k 采样</span></div>
<p>Top-k 采样（Top-k Sampling）先把概率最高的 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个 token 保留下来，其余 token 概率全部截断为 0，然后在这 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个候选里重新归一化并随机采样。设保留下来的候选集合为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{K}_k\)</span>，则采样分布可写成：</p>
<span displaypfx="" class="mathjax-container">\[p_k(i)= \begin{cases} \frac{p_i}{\sum_{j\in \mathcal{K}_k}p_j}, &amp; i\in \mathcal{K}_k\\ 0, &amp; i\notin \mathcal{K}_k \end{cases}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span> 是 softmax 后原始概率， <span displaypfx="inline-" class="mathjax-container">\(\mathcal{K}_k\)</span> 是当前概率最高的 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个 token 集合。这样做的效果是：极小概率的长尾 token 不再参与抽样，从而降低胡言乱语或离谱跳转的风险；同时又保留了随机性，不会像贪心那样永远输出同一条路径。</p>
<p>Top-k 的关键超参数是 <span displaypfx="inline-" class="mathjax-container">\(k\)</span>。 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 太小，分布会重新变得接近贪心； <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 太大，又会把很多低质量候选放回来。它本质上是在“稳定性”和“多样性”之间做硬截断式折中。</p>
<div class="blog_h4"><span class="graybg">Top-p 采样</span></div>
<p>Top-p 采样（Top-p Sampling, Nucleus Sampling）不固定保留多少个 token，而是先按概率从高到低排序，再取最小的前缀集合 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{N}_p\)</span>，使其累计概率至少达到阈值 <span displaypfx="inline-" class="mathjax-container">\(p\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{i\in \mathcal{N}_p} p_i \ge p\]</span>
<p>然后只在这个“概率核心区”里重新归一化并随机采样。与 Top-k 相比，Top-p 的保留集合大小是动态变化的：如果当前分布非常尖锐，可能只需要少数几个 token 就能覆盖 90% 或 95% 的概率质量；如果当前分布较平，保留下来的 token 数量就会自动增多。</p>
<p>这种自适应机制更贴合语言生成的实际状态：有些位置模型非常确定，例如固定短语或语法闭合，此时候选空间本来就应很小；有些位置模型不那么确定，例如开放内容展开，此时候选空间应更大。Top-p 因而通常比固定的 Top-k 更灵活，也是现代大模型推理中非常常见的采样策略。</p>
<div class="blog_h4"><span class="graybg">温度与策略取舍</span></div>
<p>温度（Temperature）常与上述采样策略配合使用。若把 logits <span displaypfx="inline-" class="mathjax-container">\(z_{t,i}\)</span> 除以温度 <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span> 后再做 softmax，则有：</p>
<span displaypfx="" class="mathjax-container">\[p_\tau(i)=\frac{e^{z_{t,i}/\tau}}{\sum_{j=1}^{V}e^{z_{t,j}/\tau}}\]</span>
<p>当 <span displaypfx="inline-" class="mathjax-container">\(\tau&lt;1\)</span> 时，分布会变尖，模型更保守；当 <span displaypfx="inline-" class="mathjax-container">\(\tau&gt;1\)</span> 时，分布会变平，采样更发散。于是，解码策略的工程取舍可以概括为：</p>
<ul>
<li>贪心搜索：最快、最稳定，但最短视。</li>
<li>束搜索：更重视整句高分路径，但计算更贵、表达更保守。</li>
<li>Top-k 采样：固定候选数，简单直接，易于控制长尾噪声。</li>
<li>Top-p 采样：候选数自适应，通常更自然、更适合开放生成。</li>
</ul>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/temperature-softmax-distribution.png"><img class="alignnone size-full wp-image-41737" src="https://blog.gmem.cc/wp-content/uploads/2026/03/temperature-softmax-distribution.png" alt="temperature-softmax-distribution" width="1920" height="1785" /></a></p>
<div class="blog_h4"><span class="graybg">重复惩罚与频率控制</span></div>
<p>仅靠解码策略本身，往往还不足以避免模型进入重复、啰嗦或机械回环的状态。例如开放生成时，模型可能连续输出相同短语，或不断在几个近义表达之间打转。工程上因此常在 logits 层再加入一类后处理规则：对已经出现过的 token 施加惩罚，从而改变下一步的候选分布。</p>
<p>最常见的一类是重复惩罚（Repetition Penalty）。它的思想很直接：若某个 token 已经在当前上下文中出现过，就下调它再次被选中的倾向。实现细节在不同框架里略有差异，一种常见写法是对已出现 token 的 logit <span displaypfx="inline-" class="mathjax-container">\(z_i\)</span> 施加按符号分段的缩放：</p>
<span displaypfx="" class="mathjax-container">\[z_i'= \begin{cases} z_i / r, &amp; z_i&gt;0\\ z_i \cdot r, &amp; z_i\le 0 \end{cases},\qquad r&gt;1\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 是重复惩罚系数。这样处理的目的，是在不破坏 logit 正负号语义的前提下，整体压低“已经出现过的 token 再次被选中”的优势。 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 越大，惩罚越强；过大则可能把正常重复也压掉，使输出变得生硬。</p>
<p>另一类常见控制项是 presence penalty（出现惩罚）与 frequency penalty（频次惩罚）。它们的共同目标是抑制重复，但力度来源不同：前者只关心“出现过没有”，后者关心“已经出现了多少次”。若原始 logit 为 <span displaypfx="inline-" class="mathjax-container">\(z_i\)</span>，token <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 在当前已生成文本中的出现次数为 <span displaypfx="inline-" class="mathjax-container">\(c_i\)</span>，则一个常见抽象写法是：</p>
<span displaypfx="" class="mathjax-container">\[z_i' = z_i - \lambda_{\mathrm{pres}}\mathbf{1}[c_i&gt;0] - \lambda_{\mathrm{freq}}c_i\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\lambda_{\mathrm{pres}}\)</span> 是 presence penalty 系数， <span displaypfx="inline-" class="mathjax-container">\(\mathbf{1}[c_i&gt;0]\)</span> 是指示函数：只要 token <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 出现过至少一次，就减去一个固定惩罚； <span displaypfx="inline-" class="mathjax-container">\(\lambda_{\mathrm{freq}}\)</span> 是 frequency penalty 系数， <span displaypfx="inline-" class="mathjax-container">\(c_i\)</span> 越大，惩罚越强。因此：</p>
<ul>
<li>presence penalty 更像“出现过就提醒一次”，主要鼓励模型换新词、开新话题。</li>
<li>frequency penalty 更像“出现越多罚越重”，主要抑制机械重复和啰嗦堆叠。</li>
</ul>
<p>三者的作用位置都在 softmax 之前：先修改 logits，再重新归一化成概率。它们不是在训练阶段改变模型参数，而是在推理阶段临时改写候选分布。因此，它们更像输出控制器（Output Controller），而不是模型能力本身的一部分。</p>
<p>工程上，这些惩罚项通常与温度、Top-k、Top-p 一起调节。若目标是严谨、稳定、少跑偏的回答，常采用较低温度并只施加较轻的重复控制；若目标是创意写作或开放发散，则可能提高温度，同时保留较温和的 presence penalty，鼓励内容展开但避免原句循环。它们解决的不是“模型知不知道答案”，而是<span style="background-color: #c0c0c0;">模型在已知概率分布下，最终说话风格如何被约束</span>。</p>
<p>因此，Transformer 的“输出”需要分成两个层次理解。主干网络输出的是高维隐藏表示；真正面向任务的可解释结果，来自这些隐藏表示经过归一化、线性读出、概率映射与解码后的最终读出。输出处理连接了通用表示学习与具体任务目标，是 Transformer 从“会表示”走向“会预测、会生成、会决策”的最后一跳。</p>
<div class="blog_h1"><span class="graybg">语言模型</span></div>
<p>语言模型（Language Model）是对自然语言序列概率分布进行建模的模型。给定一段上下文，它学习并估计后续词元（token）或整个序列出现的概率，从而捕捉语言中的词法、语法、语义以及长程依赖结构。现代语言模型通常由参数化神经网络实现，其本质是把语言规律压缩进一个可计算的概率模型中。</p>
<div class="blog_h2"><span class="graybg">语言模型基础知识</span></div>
<div class="blog_h3"><span class="graybg">序列概率视角</span></div>
<p>从严格定义看，语言模型处理的对象是有顺序的 token 序列，而不是无序词集合。若把一句话写成 <span displaypfx="inline-" class="mathjax-container">\(x_1,x_2,\dots,x_T\)</span>，语言模型的核心任务就是为这段序列分配概率；等价地，它也可以被看成在每一步根据已有上下文估计下一个 token 的条件概率。</p>
<span displaypfx="" class="mathjax-container">\[p(x_1,\dots,x_T)=\prod_{t=1}^{T}p(x_t\mid x_1,\dots,x_{t-1})\]</span>
<p>这一定义说明了为什么语言模型天然关心词序、上下文依赖与条件生成。无论后续采用 n-gram 统计模型还是 Transformer，本质上都在近似这类序列概率分布。</p>
<div class="blog_h3"><span class="graybg">分词（Tokenization）</span></div>
<p>这里的分词（Tokenization）指的是：把原始文本切分成模型实际处理的 token 序列，并据此映射到词表（Vocabulary）中的离散 id。它服务于语言模型建模本身，决定模型看到的基本单位是什么。</p>
<p>这与全文检索（Full-text Retrieval）里的“分词”不是同一个概念。检索系统里的分词更强调索引构建、倒排表匹配与查询召回；语言模型里的 tokenization 更强调如何把文本编码成适合训练与推理的离散序列。一个 token 不一定等于自然语言里的“词”，它也可能是子词、单字、标点、空格片段，甚至字节。更具体的 tokenizer 类型与工程差异，见 Transformers 部分的 <pre class="crayon-plain-tag">Tokenization</pre> 小节。</p>
<div class="blog_h3"><span class="graybg">词袋模型（Bag of Words, BoW）</span></div>
<p>词袋模型（Bag of Words, BoW）本质上只做一件事：统计一段文本里各个词出现了多少次，或只记录它是否出现。它把文本表示成词表上的计数向量 <span displaypfx="inline-" class="mathjax-container">\(\mathbf{c}\in\mathbb{R}^{|\mathcal{V}|}\)</span>，其中每一维对应某个词的出现次数。除了这些词频统计之外，BoW 不保留任何顺序信息，也不建模句法结构、上下文依赖或条件概率。</p>
<p>因此，BoW 严格说并不是语言模型，而是一种早期文本表示方法。它常与朴素贝叶斯（Naive Bayes）、逻辑回归（Logistic Regression）或 TF-IDF 一起用于文本分类、检索和主题分析。它的重要性在于：它展示了“先把文本映射成向量，再交给下游模型处理”的经典思路；但由于它仅仅统计词是否出现以及出现次数，无法区分“我喜欢你”和“你喜欢我”这类序列差异，也不能承担现代语言模型那种条件生成任务。</p>
<div class="blog_h3"><span class="graybg">词嵌入（Word Embedding）</span></div>
<p>词嵌入（Word Embedding）把每个词映射到一个低维稠密向量。与 BoW 的计数统计不同，词嵌入不再把每个词看成彼此独立的离散符号，而是让共现模式或语义相近的词在向量空间里彼此接近。Word2Vec、GloVe 和 FastText 都属于这一类方法。</p>
<p>经典词嵌入通常是静态的：同一个词在任何上下文里共享同一个向量。以 <pre class="crayon-plain-tag">bank</pre> 为例，在 <pre class="crayon-plain-tag">open a bank account</pre> 中它指银行，在 <pre class="crayon-plain-tag">sit on the river bank</pre> 中它指河岸；传统词嵌入一般仍会给它同一组参数，因此无法在表示层直接区分这两种词义。这也是后来上下文化表示（Contextual Representation）变得重要的原因。</p>
<div class="blog_h3"><span class="graybg">句子嵌入（Sentence Embedding）</span></div>
<p>句子嵌入（Sentence Embedding）进一步把整句或整段文本表示为一个向量。它关注的不再是单词层面的局部语义，而是整个输入的综合语义，常用于分类、检索、匹配与聚类。</p>
<p>历史上，基于循环神经网络（Recurrent Neural Network, RNN）的序列到序列模型（Sequence-to-Sequence, Seq2Seq）曾经采用“先编码成一个固定长度向量，再由解码器生成输出”的路径。Sutskever、Vinyals 和 Le 在 2014 年提出的 Seq2Seq 工作，就用深层 LSTM 把输入序列压缩为固定维度向量；这种单向量压缩在长句上容易形成信息瓶颈，而 RNN 本身的串行计算方式也限制了并行效率，并使长程依赖建模变得困难。Bahdanau、Cho 和 Bengio 在 2014 年提出的注意力机制（Attention Mechanism）开始缓解这一问题：解码器在每一步都能直接参考输入序列的不同位置，而不必把整句信息全部压缩进单一向量中。</p>
<p>真正的结构转折点来自 2017 年的 <span class="lang:none">Attention Is All You Need</span>。这篇论文提出了 Transformer 架构，用自注意力（Self-Attention）替代 RNN 的递归路径，使模型更擅长并行训练，也更有效地建模长距离依赖。当前主流句子嵌入方法，通常都建立在 Transformer 之上：无论是编码单句得到表示的 Encoder-only 模型，还是用于检索的双编码器（Bi-Encoder）结构，如 SBERT、E5、BGE 和 text-embedding 系列，都属于这一路线的延伸。</p>
<div class="blog_h3"><span class="graybg">稠密向量（Dense Vector）</span></div>
<p>从表示形式看，无论词嵌入还是句子嵌入，本质上都属于稠密向量：用较低维的实值向量承载词、句子或文档的信息。向量的每一维不再直接对应某个具体词，而是由训练过程自动学习得到；因此，模型可以把共现模式、语义相似性以及部分上下文规律压缩进连续向量空间。</p>
<p>这也是后文嵌入模型（Embedding Model）、Word2Vec、Sentence-BERT 和 text-embedding 系列的共同基础。它们的差异在于：表示对象是词、句子还是文档，训练目标是预测上下文、对比学习还是任务特化微调；但核心思想一致，都是让语义结构在向量空间中变得可计算。</p>
<p>上述内容回答的是“文本如何被表示”。回到语言模型本身，还需要进一步区分模型究竟在学什么、输出什么、内部结构如何组织，以及它在工程系统中承担什么角色。</p>
<div class="blog_h2"><span class="graybg">语言模型分类</span></div>
<p>理解语言模型时，至少需要区分四个互相独立但彼此关联的维度：第一，模型在预训练时学的是什么；第二，模型最终主要输出什么；第三，模型内部的信息流结构如何组织；第四，模型在工程上是通用基座、指令对齐模型，还是任务特定模型。它们回答的是四个不同问题，因此一个模型完全可以同时拥有多重身份。例如，BERT 可以同时被描述为“掩码语言模型（Masked Language Model, MLM）+ 表示模型（Representation Model）+ Encoder-only 模型”；GPT / Qwen / LLaMA 则通常是“自回归语言模型（Autoregressive Language Model）+ 生成模型（Generative Model）+ Decoder-only 模型”。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">分类维度</td>
<td style="text-align: center;">它回答的问题</td>
<td style="text-align: center;">典型类别</td>
</tr>
</thead>
<tbody>
<tr>
<td>按预训练目标</td>
<td>模型在预训练阶段究竟被要求预测什么</td>
<td>掩码语言模型、自回归语言模型、替换检测、去噪重建</td>
</tr>
<tr>
<td>按输出与用途</td>
<td>模型最终主要产出向量、表示还是可直接生成的文本</td>
<td>嵌入模型、表示模型、生成模型</td>
</tr>
<tr>
<td>按架构信息流</td>
<td>模型内部如何读取上下文、如何组织编码与生成</td>
<td>Encoder-only、Decoder-only、Encoder–Decoder</td>
</tr>
<tr>
<td>按工程形态</td>
<td>模型在实际系统里扮演什么角色</td>
<td>通用预训练基座、指令对齐模型、任务特定模型</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">按预训练目标分类</span></div>
<p>按预训练目标分类，关注的是模型在大规模无标注文本上被要求完成什么自监督任务。这一维决定了模型最初学会的信息组织方式，但不直接等价于它最终能做什么任务。最经典的两类是掩码语言模型与自回归语言模型。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center;">核心训练目标</td>
<td style="text-align: center;">上下文可见性</td>
<td style="text-align: center;">典型模型</td>
<td style="text-align: center;">更常见优势</td>
</tr>
</thead>
<tbody>
<tr>
<td>掩码语言模型（MLM）</td>
<td>遮住部分 token，再根据其余上下文恢复被遮住内容</td>
<td>通常可双向看左右文</td>
<td>BERT、RoBERTa、DeBERTa</td>
<td>表示学习强；适合理解、分类、匹配、序列标注</td>
</tr>
<tr>
<td>自回归语言模型（CLM / ARLM）</td>
<td>根据前文预测下一个 token</td>
<td>因果约束，只看历史上下文</td>
<td>GPT、LLaMA、Qwen、Mistral</td>
<td>生成自然；统一接口强；适合对话、续写、代码生成</td>
</tr>
</tbody>
</table>
<p>两者的差异首先体现在条件概率分解方式上。自回归语言模型直接建模整段文本的联合概率：</p>
<span displaypfx="" class="mathjax-container">\[p(x_1,\dots,x_T)=\prod_{t=1}^{T}p(x_t\mid x_{&lt;t})\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个 token， <span displaypfx="inline-" class="mathjax-container">\(x_{&lt;t}\)</span> 表示它之前所有 token；模型在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 步输出的是“下一个 token 的词表分布”。掩码语言模型则不是按固定顺序展开整句，而是在输入中随机挑出若干位置 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 做遮蔽，训练目标可写成：</p>
<span displaypfx="" class="mathjax-container">\[\max \sum_{i\in M}\log p(x_i\mid x_{\setminus M})\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(M\)</span> 是被遮住的位置集合， <span displaypfx="inline-" class="mathjax-container">\(x_{\setminus M}\)</span> 表示其余未遮住 token。它学到的是“给定上下文，如何恢复缺失信息”，而不是“如何一步步把整段文本续写出来”。因此，MLM 天然更偏表示学习；ARLM 天然更偏生成建模。</p>
<p>这一维并不只有两类。ELECTRA 的替换检测（Replaced Token Detection, RTD）不直接恢复 mask，而是判断 token 是否被替换；T5、BART 的去噪重建（Denoising Reconstruction）则通过破坏输入再让模型恢复原文。因此，“掩码 vs 自回归”是最核心的一条主线，但不是全部可能性。</p>
<div class="blog_h3"><span class="graybg">按输出与用途分类</span></div>
<p>按输出与用途分类，关注的是模型最终主要产出什么，以及这些产出在工程系统里被如何使用。这里最容易混淆的是“嵌入模型”和“表示模型”。两者都能产出向量，但优化目标和默认使用方式并不相同。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center;">主要输出</td>
<td style="text-align: center;">优化重点</td>
<td style="text-align: center;">典型用途</td>
<td style="text-align: center;">典型代表</td>
</tr>
</thead>
<tbody>
<tr>
<td>嵌入模型（Embedding Model）</td>
<td>固定维度向量</td>
<td>让语义相近样本在向量空间里更近</td>
<td>检索、聚类、召回、语义匹配</td>
<td>Word2Vec、SBERT、BGE、E5、text-embedding 系列</td>
</tr>
<tr>
<td>表示模型（Representation Model）</td>
<td>上下文化隐藏表示</td>
<td>学到可迁移的中间表示，再交给任务头读出</td>
<td>分类、序列标注、匹配、判别式 NLU</td>
<td>BERT、RoBERTa、DeBERTa、ModernBERT</td>
</tr>
<tr>
<td>生成模型（Generative Model）</td>
<td>逐步生成的 token 分布与文本序列</td>
<td>最大化生成质量、上下文延续性与指令跟随能力</td>
<td>对话、写作、摘要、翻译、代码生成、结构化输出</td>
<td>GPT、Qwen、LLaMA、T5、BART</td>
</tr>
</tbody>
</table>
<p>嵌入模型的关键特征是：它的向量空间本身就是最终产品。用户真正拿来用的是向量之间的距离、余弦相似度或最近邻结构。表示模型则更像通用特征提取器：它输出的隐藏状态通常还要再接一个任务头（Task Head）或额外池化层，才能变成分类分数、序列标签或其他任务结果。生成模型的最终输出则是一个条件词表分布，经过解码后形成文本或结构化序列。</p>
<p>同一底座模型有时可以被改造成不同用途。例如，BERT 原本是表示模型，但经过对比学习和专门池化后可以变成句向量嵌入模型；Decoder-only 大模型原本是生成模型，但也可以通过取隐藏状态做 embedding。不过从默认训练目标与最强项看，这三类仍然应当区分。</p>
<div class="blog_h3"><span class="graybg">按架构与信息流分类</span></div>
<p>按架构分类，关注的是模型内部如何读取上下文，以及输入和输出是如何在网络中流动的。这一维对应的是结构设计，而不是预训练目标本身。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">架构类别</td>
<td style="text-align: center;">信息流特征</td>
<td style="text-align: center;">注意力方式</td>
<td style="text-align: center;">典型任务</td>
<td style="text-align: center;">典型代表</td>
</tr>
</thead>
<tbody>
<tr>
<td>Encoder-only</td>
<td>把输入编码成上下文化表示，不直接负责逐步生成</td>
<td>通常是双向自注意力</td>
<td>分类、检索、匹配、序列标注</td>
<td>BERT、RoBERTa、DeBERTa、ELECTRA</td>
</tr>
<tr>
<td>Decoder-only</td>
<td>按时间步自回归地产生输出</td>
<td>因果自注意力</td>
<td>对话、续写、代码生成、开放式问答</td>
<td>GPT、LLaMA、Qwen、Mistral、DeepSeek</td>
</tr>
<tr>
<td>Encoder–Decoder</td>
<td>先编码输入，再由解码器条件生成输出</td>
<td>编码器双向自注意力 + 解码器因果自注意力 + 交叉注意力</td>
<td>翻译、摘要、改写、条件生成</td>
<td>T5、BART</td>
</tr>
</tbody>
</table>
<p>这一维与前两维经常联动，但不是一一对应。Encoder-only 模型常与 MLM 或 RTD 结合，Decoder-only 模型常与自回归目标结合，Encoder–Decoder 模型则常与去噪或条件生成目标结合；但它们分别回答的是“结构长什么样”和“训练时学什么”的两个不同问题。</p>
<div class="blog_h3"><span class="graybg">按工程形态分类</span></div>
<p>在工程落地中，还需要区分模型处于哪种产品化形态。通用预训练基座（Base Model）强调语言知识与可迁移能力；指令对齐模型（Instruction-tuned Model）强调遵循人类指令、对话风格与格式约束；任务特定模型（Task-specific Model）则围绕某个明确监督目标继续微调，并常配合专门任务头工作。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">工程形态</td>
<td style="text-align: center;">核心特点</td>
<td style="text-align: center;">适合场景</td>
<td style="text-align: center;">典型例子</td>
</tr>
</thead>
<tbody>
<tr>
<td>通用预训练基座</td>
<td>保留通用语言知识，强调可迁移性与再训练空间</td>
<td>继续预训练、SFT、LoRA、蒸馏</td>
<td>BERT base、LLaMA base、Qwen base</td>
</tr>
<tr>
<td>指令对齐模型</td>
<td>通过指令微调与偏好优化提升对话和任务遵循能力</td>
<td>问答助手、Agent、工具调用、结构化生成</td>
<td>ChatGPT 类、Qwen-Instruct、LLaMA-Instruct</td>
</tr>
<tr>
<td>任务特定模型</td>
<td>围绕明确任务输出继续微调，并常接专门任务头</td>
<td>NER、分类、匹配、排序、信息抽取</td>
<td>DeBERTa-CRF、BERT 分类器、LLM + PEFT</td>
</tr>
</tbody>
</table>
<p>把四个维度合在一起看，模型的定位会变得清晰得多：</p>
<ul>
<li>BERT：更接近 MLM + 表示模型 + Encoder-only + 常作为任务特定模型底座。</li>
<li>SBERT 或 BGE：更接近 表示 / 对比学习 + 嵌入模型 + 常为双编码检索结构。</li>
<li>GPT / Qwen / LLaMA：更接近 自回归 + 生成模型 + Decoder-only + 常见指令对齐形态。</li>
<li>T5 / BART：更接近 去噪或 text-to-text 目标 + 生成模型 + Encoder–Decoder。</li>
</ul>
<div class="blog_h2"><span class="graybg">主流表示型语言模型</span></div>
<p>到 2026 年，表示型语言模型（Representation Model）的工程格局已经明显分成两条主线。第一条是以 Encoder-only 为核心的判别式表示模型，主要服务于分类、自然语言推断（Natural Language Inference, NLI）、命名实体识别（Named Entity Recognition, NER）、抽取式问答等理解任务；第二条是专门为句向量、检索、聚类和召回设计的嵌入模型。前者仍沿着 BERT 家族演化，后者则越来越多地直接采用 BGE、E5、Qwen3-Embedding、jina-embeddings 这类专门路线。因此，讨论“更好的表示模型”时，必须先区分目标到底是任务头微调，还是直接产出高质量向量表示。</p>
<div class="blog_h3"><span class="graybg">BERT</span></div>
<p>BERT（Bidirectional Encoder Representations from Transformers）是典型的 Encoder-only 模型：用双向注意力做表示学习，预训练目标以掩码语言建模（Masked Language Modeling, MLM）为主。原始 BERT 还包含下一句预测（Next Sentence Prediction, NSP）任务：输入通常写成句段 A [SEP] 句段 B，模型需要根据最终的 <pre class="crayon-plain-tag">[CLS]</pre> 表示判断 B 是否真的是 A 在原语料中的下一句，而不是随机抽来的另一句。与 Word2Vec 常见的负采样训练不同，BERT 的核心预训练是在被遮住的位置上直接做词表预测；而 NSP 则为句级判别额外提供了一条监督路径。输入序列开头通常会加入一个特殊的 <pre class="crayon-plain-tag">[CLS]</pre> token，用来聚合整段输入的信息；经过编码后，这个位置的输出隐藏状态常被当作整个序列的语义摘要，并接到分类头上用于文本分类、自然语言推断（NLI）等下游任务。</p>
<p>但这里必须把两件事分开。<pre class="crayon-plain-tag">[CLS]</pre> 很适合做<span style="background-color: #c0c0c0;">任务读出位置（readout position）</span>，却不天然等于“最好的通用句向量”。它之所以能被拿来接分类头，首先是因为它在结构上位于序列最前面，经过每一层双向自注意力后，都可以从整句其它 token 汇聚信息；其次是因为原始 BERT 的预训练里确实给过它专门监督：在下一句预测（Next Sentence Prediction, NSP）任务中，最终的分类就是直接读 <pre class="crayon-plain-tag">[CLS]</pre> 的输出隐藏状态，再接一个二分类头。也就是说，<pre class="crayon-plain-tag">[CLS]</pre> 从一开始就被当成“适合给任务头读取”的位置来训练。正因为如此，原始 BERT 的 <pre class="crayon-plain-tag">[CLS]</pre> 既是首位置隐藏状态，也是被句级判别任务直接塑形过的读出接口。</p>
<p>不过，<pre class="crayon-plain-tag">[CLS]</pre> 的训练目标并不是“把整句压缩成一个适合做余弦相似度的几何向量”。在 MLM（Masked Language Modeling）中，直接受监督的是被 mask 的 token 位置，而不是整句的句向量质量；<pre class="crayon-plain-tag">[CLS]</pre> 只会通过多层自注意力间接参与这些预测。在 NSP 里，它学到的是“这一对句子是否连续”这种特定判别目标，而不是“语义相近的句子在向量空间中应彼此靠近”。因此，<pre class="crayon-plain-tag">[CLS]</pre> 更像是为分类器准备的汇总接口，而不是为检索、聚类或最近邻搜索专门对齐过的句表示。</p>
<p>这也是它不能自然代替显式池化（Explicit Pooling）的根本原因。显式池化会把所有 token 的隐藏状态用平均、最大值或加权汇聚的方式整合成句向量，例如平均池化可写成</p>
<span displaypfx="" class="mathjax-container">\[e(x)=\frac{1}{n}\sum_{i=1}^{n} h_i\]</span>
<p>这里每个 token 的最终表示都会直接进入句向量构造过程；而 <pre class="crayon-plain-tag">[CLS]</pre> 路线则把整句压缩任务隐含地交给某一个特殊位置去完成，相当于要求模型把所有句级信息都写入单个状态向量中。对分类任务，这种单点读出通常足够，因为后面还有任务头继续适配；但对通用句嵌入，这种“单位置承担全部汇总”的方式往往不如显式池化稳定，也更容易受到预训练目标偏置的影响。</p>
<p>从几何上看，这个差异会进一步表现为表示各向异性（Anisotropy）：原始 BERT 的 <pre class="crayon-plain-tag">[CLS]</pre> 向量常常集中在高维空间的少数主方向上，不同句子的向量分布会显得过于拥挤，余弦相似度缺乏足够区分度。显式池化本身并不能自动解决所有问题，但它至少把“句向量由哪些 token 共同构成”这件事写成了可控、透明的操作；一旦再叠加 Sentence-BERT 这类句对监督或对比学习目标，模型就会直接围绕池化后的句向量去优化距离结构，而不是依赖 <pre class="crayon-plain-tag">[CLS]</pre> 在预训练阶段顺带形成的间接汇总能力。</p>
<p>BERT 以及其他表示型语言模型通常先在海量通用语料上做预训练，从而学到词法模式、句法结构、语义关系以及一定程度的世界知识。正因为这些知识不是为某一个具体任务单独学习出来的，它们非常适合作为通用特征提取器（General-purpose Feature Extractor）：在迁移学习框架下，只需接上分类头、序列标注头或匹配头，并在目标任务数据上继续微调，就可以把通用表示快速适配到具体自然语言处理任务。</p>
<p>BERT系列“是否支持中文”关键在词表与分词器（Tokenizer）。英文 BERT-base 的 WordPiece 词表主要覆盖英文子词；对中文文本可能会大量落到 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{UNK}]\)</span> 或被切成极碎片段，效果通常不理想。要做中文任务更常用中文 BERT、mBERT（multilingual BERT）或以 SentencePiece 为主的多语模型。</p>
<div class="blog_h3"><span class="graybg">RoBERTa</span></div>
<p>RoBERTa（Robustly Optimized BERT Approach）延续 BERT 的 Encoder-only 架构，但通过更大规模数据与训练配方改进（例如更长训练、更大 batch、动态 masking、移除或弱化 NSP 等）显著提升表示质量。它的工程意义在于：<span style="background-color: #c0c0c0;">在不改变基本架构的前提下，训练细节足以带来可观收益</span>。</p>
<p>RoBERTa 证明了一个重要事实：Encoder-only 模型的性能上限，不完全取决于“是否换了新架构”，训练数据规模、batch 策略、masking 方式和目标设计本身就足以显著改变表示质量。</p>
<div class="blog_h3"><span class="graybg">ELECTRA</span></div>
<p>ELECTRA（Efficiently Learning an Encoder that Classifies Token Replacements Accurately）仍属于 Encoder-only 模型，但它不再让主模型只做“把被遮住的词猜出来”的掩码语言建模（Masked Language Modeling, MLM）。它先用一个较小的生成器替换部分 token，再让主模型判断每个位置上的 token 是原词还是替换词，这就是替换检测（Replaced Token Detection, RTD）。这种训练方式让模型几乎在每个 token 上都获得监督信号，因此在相近预训练预算下通常比纯 MLM 更高效。</p>
<div class="blog_h3"><span class="graybg">DeBERTa / DeBERTa-V3</span></div>
<p>DeBERTa（Decoding-enhanced BERT with Disentangled Attention）可以看作对 BERT / RoBERTa 的一次结构级强化。它仍然是典型的判别式 Encoder-only 语言模型，因此特别适合文本分类、自然语言推断（Natural Language Inference, NLI）、命名实体识别（Named Entity Recognition, NER）和情感分析等理解类任务；但它不满足于只靠“更大数据、更长训练”来变强，而是直接对注意力机制本身做了精细改造。</p>
<p>它最核心的突破是解耦注意力（Disentangled Attention）。在传统 BERT 中，词的内容信息与位置信息通常会较早融合；DeBERTa 则把“这个 token 是什么”和“这个 token 在哪里”分开处理。对位置 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 的注意力打分，可直观写成三部分：</p>
<span displaypfx="" class="mathjax-container">\[A_{i,j}=\underbrace{C_iC_j^\top}_{\text{内容-内容}}+\underbrace{C_iP_{j|i}^\top}_{\text{内容-相对位置}}+\underbrace{P_{i|j}C_j^\top}_{\text{相对位置-内容}}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(C_i\)</span> 表示第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个位置的内容表示（Content Representation），<span displaypfx="inline-" class="mathjax-container">\(P_{j|i}\)</span> 和 <span displaypfx="inline-" class="mathjax-container">\(P_{i|j}\)</span> 表示相对位置表示（Relative Position Representation）。这个拆分的意义在于：模型不必把“词义”和“位置”混成一个整体再去匹配，而可以更精细地学习“哪个内容会关注什么位置关系”。对于短语修饰、依存关系和实体边界判定这类任务，这种改造往往更有利。</p>
<p>DeBERTa 的第二个关键设计是增强型掩码解码器（Enhanced Mask Decoder, EMD）。它的动机是：相对位置固然重要，但在掩码预测时，绝对位置有时也能提供额外信息。因此 DeBERTa 在接近输出端的位置再次显式注入绝对位置信息，让模型在做 MLM 预测时同时利用相对与绝对位置信号。这相当于在保持主体编码过程解耦的前提下，在最后的“读答案”阶段补上一层位置提醒。</p>
<p>从工程结果看，DeBERTa 长期是判别式 NLU 的强基线之一。它的重要性不只在于“分数更高”，还在于它说明了一个事实：<span style="background-color: #c0c0c0;">Encoder-only 模型的上限不只是由数据和算力决定，内容表示、位置表示和解码方式本身的结构设计也会显著影响表示质量</span>。后续的 DeBERTa-V3 又把替换检测（RTD）这类更高效的预训练目标引入进来，进一步改善了训练效率与效果，使其在很多精细理解任务里依然保持很强竞争力。</p>
<div class="blog_h3"><span class="graybg">ModernBERT</span></div>
<p>ModernBERT 代表的是 BERT 风格双向编码器在 2025 年之后的一次现代化重写。它仍然属于 Encoder-only，但不再停留在 2018 年 BERT 的上下文长度、注意力实现和推理路径上，而是把长上下文支持与现代高效实现直接纳入底座设计。其官方模型卡给出的关键信息包括：预训练规模达到 2T tokens，原生上下文长度扩展到 8192 token，并引入 RoPE、本地-全局交替注意力（Local-Global Alternating Attention）、unpadding 和 Flash Attention 等现代工程机制。</p>
<p>与原始 BERT 还有一个重要区别在于 <pre class="crayon-plain-tag">[CLS]</pre> 的默认训练来源。ModernBERT 官方定位是纯粹的掩码语言模型（Masked Language Model, MLM）：预训练时默认存在的是 MLM head，而不是像原始 BERT 那样再额外叠加 NSP 这种句级二分类目标。这意味着 ModernBERT 的 <pre class="crayon-plain-tag">[CLS]</pre> 主要是作为首位置隐藏状态在 MLM 路线下间接形成的可读出表示，而不是被预训练阶段的句级分类任务直接塑形过的“现成分类接口”。因此，ModernBERT 当然仍然适合做分类，但更准确的理解应是：它提供了一个现代化、长上下文、可高效微调的编码底座；真正的分类头通常仍要在下游任务里显式接上并训练出来。</p>
<p>这类改造的意义非常直接。传统 BERT 家族最舒服的输入长度通常仍停留在几百 token 到一两千 token 量级，而 ModernBERT 这一路则把长文档分类、长文本检索、代码检索和大段上下文理解一并纳入可用范围。于是，在英文或以英文 / 代码为主的判别式任务里，ModernBERT 往往比 DeBERTa-V3 更接近 2026 年的默认新基线；DeBERTa-V3 仍然是高质量经典强基线，但 ModernBERT 明显更符合当前对长上下文与吞吐效率的要求。</p>
<p>它的交替局部-全局注意力也进一步改变了人们对 <pre class="crayon-plain-tag">[CLS]</pre> 的直觉。经典 BERT 可以把 <pre class="crayon-plain-tag">[CLS]</pre> 看成每层都在做全局汇总的位置；ModernBERT 则只在周期性的全局层里进行真正的全局信息混合，局部层更强调效率与长序列处理能力。因此，把 ModernBERT 的 <pre class="crayon-plain-tag">[CLS]</pre> 直接理解成“天然全局句向量”会更加不准确；它更像一个可被下游读取的首位置表示，而不是默认就已经为通用句嵌入优化好的几何向量。</p>
<div class="blog_h3"><span class="graybg">多语表示模型</span></div>
<p>多语表示模型的选型逻辑与英文单语场景并不完全相同。XLM-R（XLM-RoBERTa）仍然是经典多语 Encoder-only 基线之一：其预训练覆盖 100 种语言，核心价值是把多语文本映射到共享表示空间，再用于序列分类、序列标注和问答等下游任务。mDeBERTa-V3 则把 DeBERTa-V3 的结构与训练目标迁移到多语场景，在跨语言自然语言推断等零样本迁移任务上，相比 XLM-R-base 给出更强的官方基线结果。因此，若目标是稳定、成熟、适合任务头微调的多语编码器，XLM-R 与 mDeBERTa-V3 依然是 2026 年非常现实的主流选择。</p>
<p>更前沿的一支则沿着 ModernBERT 的方向继续推进。2025 年发布的 mmBERT 直接把 ModernBERT 的现代编码器路线扩展到大规模多语场景，官方介绍强调其训练覆盖 3T 以上 token 和 1800 多种语言，并把它定位为首个在性能和速度上同时明显超过 XLM-R 的新一代多语编码器。这说明多语表示学习也正在从“经典 XLM-R 世代”向“ModernBERT 世代”迁移，只是就生态成熟度与工程复用性而言，XLM-R / mDeBERTa-V3 仍然更稳，mmBERT 更像下一阶段的高端新底座。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">架构</td>
<td style="text-align: center;">预训练目标</td>
<td style="text-align: center;">特点</td>
<td style="text-align: center;">常见用途</td>
</tr>
</thead>
<tbody>
<tr>
<td>BERT</td>
<td>Encoder-only</td>
<td>MLM（+ NSP 变体）</td>
<td>经典通用基线；生态成熟</td>
<td>分类、匹配、序列标注</td>
</tr>
<tr>
<td>RoBERTa</td>
<td>Encoder-only</td>
<td>MLM</td>
<td>更强训练配方；经典英文 NLU 强基线</td>
<td>理解类任务强基线</td>
</tr>
<tr>
<td>ELECTRA</td>
<td>Encoder-only</td>
<td>替换检测（Replaced Token Detection）</td>
<td>训练效率高；对小算力友好</td>
<td>理解类任务、低成本预训练</td>
</tr>
<tr>
<td>DeBERTa-V3</td>
<td>Encoder-only</td>
<td>MLM / RTD</td>
<td>解耦注意力 + 更高效预训练；经典高精度 NLU 底座</td>
<td>高精度 NLU、NER、文本分类</td>
</tr>
<tr>
<td>ModernBERT</td>
<td>Encoder-only</td>
<td>MLM</td>
<td>2T tokens 预训练、8192 上下文、现代高效实现</td>
<td>长文档分类、长文本理解、代码检索、现代英文编码任务</td>
</tr>
<tr>
<td>XLM-R</td>
<td>多语 Encoder-only</td>
<td>MLM</td>
<td>100 语言经典共享表示空间；多语生态成熟</td>
<td>多语分类、NER、问答、跨语言迁移</td>
</tr>
<tr>
<td>mDeBERTa-V3</td>
<td>多语 Encoder-only</td>
<td>MLM / RTD</td>
<td>DeBERTa-V3 的多语延伸；零样本迁移更强</td>
<td>多语 NLU、多语分类、跨语言推断</td>
</tr>
<tr>
<td>Qwen3-Embedding / BGE-M3 / multilingual-e5 / jina-embeddings-v3</td>
<td>专用表示模型</td>
<td>对比学习 / 指令化检索 / 检索特化目标</td>
<td>不以任务头分类为首要目标，而是直接优化向量空间质量</td>
<td>语义检索、聚类、召回、RAG、跨语言匹配</td>
</tr>
</tbody>
</table>
<p>因此，到 2026 年若任务是分类、序列标注、NLI 或抽取式问答，主流候选通常已经变成 DeBERTa-V3、ModernBERT，以及多语场景下的 XLM-R / mDeBERTa-V3；若任务本质上是检索、聚类、向量召回或 RAG，则应直接转向后文的主流嵌入模型，而不是继续把通用 Encoder-only 分类底座当作最优句向量来源。</p>
<div class="blog_h2"><span class="graybg">主流生成式语言模型</span></div>
<div class="blog_h3"><span class="graybg">GPT 系列</span></div>
<p>GPT 系列是典型 Decoder-only：以 CLM（自回归 next-token）为主目标，把各种任务统一为“续写”。工程上其优势来自统一接口与强 in-context learning；代价是推理成本高（尤其长上下文与高并发场景）。</p>
<div class="blog_h3"><span class="graybg">LLaMA</span></div>
<p>LLaMA 系列是开源 Decoder-only 基座的重要代表，强调稳定的训练配方与生态可用性（tokenizer、推理支持、微调社区）。它常作为“可控、可复现”的研究/工程基线，用于 SFT、LoRA、RAG 与本地部署。</p>
<div class="blog_h3"><span class="graybg">Qwen</span></div>
<p>Qwen 系列同属开源 Decoder-only 生态，中文与多语言能力通常更受关注；在工具调用、代码与多模态等方向也有较多衍生版本。工程上，它常被用作中文业务与多语言场景的基座候选。</p>
<div class="blog_h3"><span class="graybg">Mistral</span></div>
<p>Mistral 系列强调“更高性价比的推理与训练”：通过架构与工程优化在相近成本下获得更强的生成质量与吞吐表现，常见于高并发推理与轻量级部署场景。</p>
<div class="blog_h3"><span class="graybg">DeepSeek</span></div>
<p>DeepSeek 系列更强调训练与推理效率的极致：在注意力 KV 压缩、稀疏结构（MoE）与训练目标（如 Multi-Token Prediction）等方向探索更激进的工程取舍，目标是在同等算力预算下提升有效容量与上下文能力。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">家族</td>
<td style="text-align: center;">定位</td>
<td style="text-align: center;">常见优势</td>
<td style="text-align: center;">常见代价</td>
</tr>
</thead>
<tbody>
<tr>
<td>LLaMA</td>
<td>稳健开源基座</td>
<td>生态成熟；微调与推理支持广</td>
<td>配置与版本较多，需选对上下文/推理配方</td>
</tr>
<tr>
<td>Qwen</td>
<td>多语言/中文友好基座</td>
<td>中文场景覆盖好；衍生模型多</td>
<td>需关注 tokenizer/指令对齐数据分布</td>
</tr>
<tr>
<td>Mistral</td>
<td>高性价比推理</td>
<td>吞吐与质量兼顾；工程落地友好</td>
<td>不同版本/配方差异会影响最佳实践</td>
</tr>
<tr>
<td>DeepSeek</td>
<td>效率优先（MoE/压缩）</td>
<td>在算力/显存约束下追求更强能力</td>
<td>架构复杂度更高；推理与部署依赖实现细节</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">主流嵌入模型</span></div>
<div class="blog_h3"><span class="graybg">Word2Vec（CBOW / Skip-gram）</span></div>
<p>Word2Vec 用一个简单的自监督目标（Self-supervised Objective）学习词向量（Word Embeddings）：监督信号来自文本的共现上下文（Context），而不是人工标签。</p>
<p>它可以理解为学习两个嵌入表：输入词向量 <span displaypfx="inline-" class="mathjax-container">\(V\in\mathbb{R}^{|{\cal V}|\times d}\)</span> 与输出词向量 <span displaypfx="inline-" class="mathjax-container">\(U\in\mathbb{R}^{|{\cal V}|\times d}\)</span>（<span displaypfx="inline-" class="mathjax-container">\(|{\cal V}|\)</span> 为词表大小）。训练结束后，常用 <span displaypfx="inline-" class="mathjax-container">\(V\)</span>（或 <span displaypfx="inline-" class="mathjax-container">\((V+U)/2\)</span>）作为词向量。</p>
<p>Word2Vec 有两种经典训练方式：CBOW（Continuous Bag of Words）根据上下文预测中心词，Skip-gram 则根据中心词预测上下文。二者的预测方向相反，但都利用局部共现关系学习词向量。下面以 Skip-gram 为例说明它最核心的训练机制。</p>
<p>Word2Vec 的样本不是人工标注出来的，而是通过<span style="background-color: #c0c0c0;">滑动窗口（Sliding Window）</span>在语料上自动生成。设窗口半径为 <span displaypfx="inline-" class="mathjax-container">\(m\)</span>，当滑动窗口扫到位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 时，中心词 <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span> 会与它左右 <span displaypfx="inline-" class="mathjax-container">\(m\)</span> 个位置内的上下文词组成正样本对；越靠近句首句尾，可用上下文自然会变少。对单个中心词，Skip-gram 的局部目标可写成：</p>
<span displaypfx="" class="mathjax-container">\[\sum_{-m\le j\le m,\ j\ne 0}\log p(x_{t+j}\mid x_t)\]</span>
<p>这意味着：窗口每向前滑动一步，模型就把“中心词与邻近词共同出现”当作新的监督信号。CBOW 则反过来，把窗口内多个上下文词聚合起来预测中心词；但两者的训练样本都来自同一套滑动窗口机制。</p>
<p>在 Skip-gram 中，给定中心词（center word）<span displaypfx="inline-" class="mathjax-container">\(w_c\)</span>，预测窗口内的上下文词（context word）<span displaypfx="inline-" class="mathjax-container">\(w_o\)</span>。若用 full softmax：</p>
<span displaypfx="" class="mathjax-container">\[p(w_o|w_c)=\frac{\exp\left(u_{w_o}^\top v_{w_c}\right)}{\sum_{w\in{\cal V}}\exp\left(u_{w}^\top v_{w_c}\right)}\]</span>
<p>如果保留这个完整 softmax，模型会被迫在整个词表上做归一化比较；真实上下文概率升高时，其他词的概率就必须相应下降。但如果改成更便宜的“只学习哪些词对是真的”而又只提供正样本，模型就会立刻出现投机空间：它完全可以把几乎所有词对都打成高分，因为目标里从来没有人告诉它哪些配对是假的。换句话说，<span style="background-color: #c0c0c0;">只基于正样本训练一个二分类式共现目标，最容易学到的不是语义结构，而是“永远预测真”</span>。</p>
<p>因此，实践里常用负采样（Negative Sampling）：对每个正样本对 <span displaypfx="inline-" class="mathjax-container">\((w_c,w_o)\)</span>，再随机采样 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个噪声词 <span displaypfx="inline-" class="mathjax-container">\(w_k\)</span>，把 <span displaypfx="inline-" class="mathjax-container">\((w_c,w_k)\)</span> 当作负样本，并最大化</p>
<span displaypfx="" class="mathjax-container">\[\log\sigma(u_{w_o}^\top v_{w_c})+\sum_{k=1}^{K}\log\sigma(-u_{w_k}^\top v_{w_c})\]</span>
<p>第一项要求真实共现词对的内积更大，第二项要求随机噪声词对的内积更小。这样模型学到的就不再是“所有词都彼此相似”，而是“哪些词在局部上下文里更可能共同出现”。这里的“模型参数”主要就是这些词向量；模型输入通常是 token id（或 one-hot）经查表得到的 <span displaypfx="inline-" class="mathjax-container">\(v_{w_c}\)</span>。</p>
<p>Word2Vec 的负样本通常也不需要过度精心设计。经典做法就是按词频分布的 <span displaypfx="inline-" class="mathjax-container">\(0.75\)</span> 次方做随机采样，让高频词仍然更常被抽到，但不会垄断全部负样本。它的核心目标不是构造“最难反例”，而是持续给模型提供足够多的噪声对照，让真实共现和随机拼接之间产生可学习的区分。后来的对比学习常会显式挖掘 hard negatives，但在经典 Word2Vec 里，简单而稳定的随机负采样通常已经足够有效。</p>
<div class="blog_h3"><span class="graybg">GloVe</span></div>
<p>GloVe（Global Vectors for Word Representation）用全局共现统计学习词向量：目标是让词向量的内积拟合共现概率（或其对数）。与 Word2Vec 相比，它更强调全局统计的一致性；但二者都属于“静态词向量”（Static Embedding），无法像 Transformer 那样根据上下文动态改变词义表示。</p>
<div class="blog_h3"><span class="graybg">Sentence-BERT</span></div>
<p>Sentence-BERT（SBERT）是文本嵌入（Text Embedding）中的经典双编码器（Bi-Encoder / Dual Encoder）范式。它与后面的 text-embedding 系列并不是两种不同任务；二者都把文本映射为向量，用于相似度计算、检索、聚类与召回。区别主要在于：SBERT 更像一条开源方法路线，而 text-embedding 系列更像近年的通用嵌入模型或 API 产品家族。</p>
<div class="blog_h4"><span class="graybg">交叉编码（Cross-Encoder）</span></div>
<p>在 SBERT 之前，句子嵌入任务通常沿用<span style="background-color: #c0c0c0;">交叉编码器（Cross-Encoder）+ BERT</span> 的范式实现相似度建模：把两个句子同时输入 Transformer 网络，常见形式是将句子 A 与句子 B 拼接成单个序列，中间用分隔符隔开，然后在原始 BERT 顶部增加分类头或回归头，直接输出这一对句子的相似度分数。这种架构的优势在于两个句子的 token 可以在同一次自注意力计算中充分交互，因此非常擅长做细粒度匹配判断；但它输出的是“句对分数”，而不是两个可独立复用的句向量。</p>
<p>这会直接带来大规模计算问题。若要在一个包含 10000 个句子的集合中找出相似度最高的匹配对，交叉编码器原则上需要对几乎所有句对分别做一次联合编码，计算量约为 <span displaypfx="inline-" class="mathjax-container">\(\frac{10000\times 9999}{2}=49{,}995{,}000\)</span> 次前向比较。也就是说，问题规模从“编码 10000 个句子”膨胀成了“编码近五千万个句对”。由于每个候选句子都必须与其他句子重新拼接、重新过一遍 BERT，这类方法几乎无法承担大规模语义检索、聚类或召回的第一阶段计算。SBERT 的关键突破，正是在保留 BERT 语义建模能力的同时，把句子表示改造成可预先编码、可缓存、可直接做余弦相似度比较的独立向量。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/cross-encoder.jpg"><img class="alignnone size-full wp-image-41927" src="https://blog.gmem.cc/wp-content/uploads/2026/03/cross-encoder.jpg" alt="cross-encoder" width="1024" height="1024" /></a></p>
<div class="blog_h4"><span class="graybg">双编码（Bi-Encoder）与孪生网络</span></div>
<p>因此，SBERT 的核心价值首先体现在计算结构的改变上：它把原本“对句对打分”的问题，改写成“分别编码句子，再比较向量距离”的问题。这样一来，候选句子可以预先编码并缓存，检索阶段只需要做向量相似度计算，而不必让每一对候选都重新经过一次完整的 BERT 联合编码。</p>
<p>它与 Cross-Encoder 的关键差异在于计算结构：</p>
<ul>
<li>双编码器：两个输入分别编码，可离线预计算候选向量，适合大规模检索（ANN）。</li>
<li>交叉编码器（Cross-Encoder）：把两个输入拼接后一起编码，匹配更精细但无法离线索引，适合重排序（Reranking）。</li>
</ul>
<p>在结构上，SBERT 的核心是<span style="background-color: #c0c0c0;">孪生架构（Siamese Architecture）</span>：两侧使用一模一样的编码器，参数完全共享，但分别接收一个句子作为输入。训练时，句子对会分别经过这两个共享权重的编码塔，各自得到固定维度的句向量，再基于余弦相似度、三元组损失（Triplet Loss）或对比损失（Contrastive Loss）优化距离关系。共享权重保证了两个句子被投射到同一个表示空间中，因此向量之间的距离才具有可比较性。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/sbert.png"><img class="alignnone size-full wp-image-41879" src="https://blog.gmem.cc/wp-content/uploads/2026/03/sbert.png" alt="sbert" width="1280" height="1280" /></a></p>
<div class="blog_h4"><span class="graybg">训练方式</span></div>
<p>SBERT 的训练过程可以拆成两个步骤。第一步是<span style="background-color: #c0c0c0;">把每个句子单独编码成向量</span>。设句子 <span displaypfx="inline-" class="mathjax-container">\(x=(t_1,\dots,t_n)\)</span>，经过共享编码器后得到逐 token 的隐藏状态 <span displaypfx="inline-" class="mathjax-container">\(H=[h_1,\dots,h_n]\)</span>；随后用池化（Pooling）把它压缩成句向量 <span displaypfx="inline-" class="mathjax-container">\(e(x)\in\mathbb{R}^d\)</span>。经典实现最常用平均池化：</p>
<span displaypfx="" class="mathjax-container">\[e(x)=\frac{1}{n}\sum_{i=1}^{n} h_i\]</span>
<p>有些实现还会继续做 L2 归一化，得到 <span displaypfx="inline-" class="mathjax-container">\(\hat e(x)=e(x)/\|e(x)\|_2\)</span>，以便后续直接用余弦相似度比较方向。这里的关键点是：左右两侧并不是两套不同模型，而是同一组参数共享的编码器分别处理句子 A 和句子 B，最终得到 <span displaypfx="inline-" class="mathjax-container">\(\hat e_a\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\hat e_b\)</span> 两个可独立复用的句向量。</p>
<p>训练语料的形状也必须与损失函数匹配。最常见的几类数据形式如下：</p>
<ul>
<li><span style="background-color: #c0c0c0;">带连续分数的句对</span>：形如 <span displaypfx="inline-" class="mathjax-container">\((x_a,x_b,y)\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 是 0 到 1、或 0 到 5 再归一化后的语义相似度分数。这类数据最适合 STS（Semantic Textual Similarity）任务，监督信号不是“是不是同类”，而是“到底有多像”。</li>
<li><span style="background-color: #c0c0c0;">二元正负句对</span>：形如 <span displaypfx="inline-" class="mathjax-container">\((x_a,x_b,y)\)</span>，其中 <span displaypfx="inline-" class="mathjax-container">\(y\in\{0,1\}\)</span>。这里 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 表示复述句、同义问法、相关 query-document 或其它正样本对；<span displaypfx="inline-" class="mathjax-container">\(y=0\)</span> 表示语义无关、错误匹配或人工拒绝的负样本对。</li>
<li><span style="background-color: #c0c0c0;">检索式正配对</span>：形如 <span displaypfx="inline-" class="mathjax-container">\((q,p)\)</span>，只显式给出 query 与其正确匹配的正样本，不单独列出负样本。训练时，通常把同一 batch 中其它 <span displaypfx="inline-" class="mathjax-container">\(p_j\)</span> 当作 <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 的负例，这就是批内负样本（In-batch Negatives）的基本数据组织方式。</li>
<li><span style="background-color: #c0c0c0;">三元组</span>：形如 <span displaypfx="inline-" class="mathjax-container">\((a,p,n)\)</span>，其中锚点 <span displaypfx="inline-" class="mathjax-container">\(a\)</span> 与正样本 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 应靠近，而与负样本 <span displaypfx="inline-" class="mathjax-container">\(n\)</span> 应拉开距离。这类数据天然适合排序与检索，因为它直接表达了“哪个比哪个更相关”。</li>
</ul>
<p>因此，SBERT 训练并不要求数据必须带精确分数；它既可以吃连续相似度打分，也可以吃正负标签，甚至只需要 query-positive 配对或三元组。真正关键的是，训练样本必须能清楚告诉模型：哪些句子应该靠近，哪些句子应该远离，以及这种关系是绝对打分还是相对排序。</p>
<p>第二步是<span style="background-color: #c0c0c0;">根据标注关系定义损失</span>。若训练数据给的是连续相似度分数，例如 0 到 1 之间的语义相似度标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>，最直接的做法是先计算两向量的余弦相似度</p>
<span displaypfx="" class="mathjax-container">\[s(\hat e_a,\hat e_b)=\cos(\hat e_a,\hat e_b)=\frac{\hat e_a^\top \hat e_b}{\|\hat e_a\|_2\|\hat e_b\|_2}\]</span>
<p>再让模型输出的相似度逼近人工标签，例如最简单的回归式目标可以写成</p>
<span displaypfx="" class="mathjax-container">\[L=(s(\hat e_a,\hat e_b)-y)^2\]</span>
<p>这时，相似句子的标签 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 更高，损失会推动两向量余弦相似度上升；不相似句子的标签更低，损失则推动相似度下降。原始 SBERT 在 STS（Semantic Textual Similarity）类任务上，常用的正是这种“句对回归到相似度分数”的训练方式。</p>
<p>若训练数据只有二元标签，即“相似”或“不相似”，则更常见的是对比式损失（Contrastive Loss）。设距离 <span displaypfx="inline-" class="mathjax-container">\(d(\hat e_a,\hat e_b)\)</span> 可以取欧氏距离，也可以取 <span displaypfx="inline-" class="mathjax-container">\(1-\cos(\hat e_a,\hat e_b)\)</span>，则典型形式为</p>
<span displaypfx="" class="mathjax-container">\[L=y\cdot d(\hat e_a,\hat e_b)^2+(1-y)\cdot \max(0,m-d(\hat e_a,\hat e_b))^2\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(y=1\)</span> 表示正样本对，损失会逼迫距离变小；<span displaypfx="inline-" class="mathjax-container">\(y=0\)</span> 表示负样本对，损失会要求两句至少相隔一个 margin <span displaypfx="inline-" class="mathjax-container">\(m\)</span>。这就把“相似句子拉近，不相似句子推远”写成了显式几何约束。</p>
<p>若任务更接近检索或召回，现代实践更常用批内负样本（In-batch Negatives）或多负样本排序损失（Multiple Negatives Ranking Loss）。设一个 batch 中第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个查询句子的正样本是 <span displaypfx="inline-" class="mathjax-container">\(p_i\)</span>，其余 <span displaypfx="inline-" class="mathjax-container">\(p_j\)</span> 都视为负例，则可写成</p>
<span displaypfx="" class="mathjax-container">\[L_i=-\log \frac{\exp(s(\hat e_{q_i},\hat e_{p_i})/\tau)}{\sum_{j=1}^{B}\exp(s(\hat e_{q_i},\hat e_{p_j})/\tau)}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span> 是温度（Temperature）参数。这个目标并不需要显式为每个查询单独准备大量负样本，而是直接把同一 batch 里的其它正样本当作自己的负样本；于是优化方向很清楚：正确配对的句子相似度必须高于 batch 中所有错误配对。许多现代 embedding 模型，包括大量 SBERT 派生模型，都是沿着这条路线训练出来的。</p>
<p>另一类经典形式是三元组损失（Triplet Loss）。它不再只看一对句子，而是同时给出锚点 <span displaypfx="inline-" class="mathjax-container">\(a\)</span>、正样本 <span displaypfx="inline-" class="mathjax-container">\(p\)</span> 和负样本 <span displaypfx="inline-" class="mathjax-container">\(n\)</span>，要求锚点与正样本的距离小于锚点与负样本的距离，且至少留出一个 margin：</p>
<span displaypfx="" class="mathjax-container">\[L=\max\bigl(0,\ d(\hat e_a,\hat e_p)-d(\hat e_a,\hat e_n)+m\bigr)\]</span>
<p>它表达的仍然是同一个原则：相似句子靠近，不相似句子远离；只是监督信号从“单对标签”变成了“相对排序关系”。</p>
<p>前文已经提到，原始 BERT 的 <pre class="crayon-plain-tag">[CLS]</pre> 表示更适合接分类头，而不是直接充当高质量通用句向量；同样地，若未经句向量任务优化就简单平均最后一层表示，效果通常也不理想。SBERT 的关键改动，正是在共享权重的双编码训练框架中，把池化和句对监督显式写入训练过程，使生成出来的 <span displaypfx="inline-" class="mathjax-container">\(e(x)\)</span> 不再只是“顺手拿来的中间表示”，而是被专门优化成适合做余弦相似度、最近邻检索与聚类的句向量。其改进原因之一，正是缓解了原始 BERT 表示的各向异性（Anisotropy）问题：向量不再高度挤在高维空间的少数方向上，余弦相似度也因此更有区分度。</p>
<p>这里也需要区分 Sentence-BERT 与 sentence-transformers。前者更严格地指 2019 年提出的 SBERT 方法路线：在 BERT、RoBERTa 等编码器基础上，通过孪生网络（Siamese Network）/三元组网络（Triplet Network）或后续对比式训练，把原本不适合作为通用句向量的表示空间改造成更适合相似度计算的句向量空间。后者则主要指围绕这一路线发展出来的开源框架与模型生态，用于统一训练、评测、发布和调用各类句向量模型。因此，sentence-transformers 不是与 SBERT 并列的另一类基础模型，而更像 SBERT 方法在工程上的延伸与集合。</p>
<p>从工程角度看，SBERT 仍然有明确实用价值：它特别适合私有化部署、领域微调、本地低延迟语义检索，以及“双编码器召回 + Cross-Encoder 重排”的两阶段检索流水线。它不是被现代 embedding 模型淘汰，而是在开源可控、可微调、可离线部署这些约束下依然非常常用。</p>
<div class="blog_h3"><span class="graybg">text-embedding 系列</span></div>
<p>如果把 SBERT 看作“如何训练和使用句向量”的经典范式，那么 text-embedding 系列代表的就是近年的通用嵌入模型实现。BGE、E5、GTE、OpenAI 的 text-embedding、Cohere Embed 等，本质上都属于同一任务族：生成可用于相似度、检索、聚类、召回的向量表示。它们的主要差异不在“是否属于 embedding”，而在训练数据、模型规模、多语言能力、上下文长度、向量维度压缩策略，以及是开源模型还是托管 API 服务。</p>
<p>通用嵌入模型（General-purpose Embedding Model）的目标通常是“语义相似近、语义无关远”，因此天然适配检索与聚类。也可以把嵌入模型微调成“任务特化嵌入”（Task-specialized Embedding）：用监督标签构造正/负样本对（同类为正、异类为负），用对比学习目标把同类拉近、异类推远，然后用最近邻/类原型（Prototype）实现分类。</p>
<p>与“表示模型 + 分类头（Representation Model + Classifier Head）”相比，二者取舍通常是：</p>
<ul>
<li>特化嵌入：推理时只算一次向量 + 相似度，便于大规模检索/多标签扩展；但输出是距离分数，概率校准与细粒度判别能力通常不如专门的分类头。</li>
<li>分类头微调：直接最小化分类损失，闭集分类效果与可解释的概率输出更强；但对大规模候选检索不友好，且不同任务往往需要不同 head。</li>
</ul>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center;">代表模型/服务</td>
<td style="text-align: center;">特点</td>
<td style="text-align: center;">适用场景</td>
<td style="text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>开源通用嵌入（General-purpose）</td>
<td>BGE / E5 / GTE</td>
<td>部署可控；可做领域微调</td>
<td>私有化 RAG；向量检索；聚类</td>
<td>选型关注多语言、长度与 license</td>
</tr>
<tr>
<td>商用 API 嵌入</td>
<td>text-embedding-3 / Cohere Embed</td>
<td>效果稳定；无需运维</td>
<td>快速上线；跨团队复用</td>
<td>成本与数据合规是主约束</td>
</tr>
<tr>
<td>领域特化嵌入（Task-specialized）</td>
<td>对比学习微调后的 embedding</td>
<td>对业务分布拟合更强</td>
<td>垂直领域检索；闭集分类</td>
<td>需要高质量正/负样本构造</td>
</tr>
<tr>
<td>多向量/late interaction</td>
<td>ColBERT 类</td>
<td>token-level 匹配更细</td>
<td>高精度检索；精排候选压缩</td>
<td>索引与存储成本更高</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">主流 Encoder–Decoder 模型</span></div>
<div class="blog_h3"><span class="graybg">T5</span></div>
<p>T5（Text-to-Text Transfer Transformer）是典型 Encoder–Decoder：把所有任务统一为“文本到文本”的生成问题（text-to-text）。它既可用于摘要、翻译等生成任务，也可用于分类，此时模型直接生成类别名对应的 token 序列。</p>
<p>在预训练阶段，T5采用的是掩码式去噪目标（Denoising Objective），但它不是只遮住单个 token 再逐个恢复，而是会对连续的 token span 做遮掩（Span Corruption）。输入中的若干片段会被替换为哨兵标记（Sentinel Token），模型则在解码端按顺序生成这些被遮住的片段。这样的训练方式既保留了“根据上下文恢复缺失内容”的掩码语言建模思想，又让模型从一开始就以 Encoder–Decoder 的生成方式学习条件重建。</p>
<p>在后续适配阶段，T5 延续了统一的 text-to-text 框架：翻译、摘要、问答、分类等任务都写成文本输入到文本输出的形式。沿着这条路线继续发展后，研究者又引入了更大规模的多任务指令微调（Instruction Tuning）：把大量带自然语言任务描述的监督任务混合起来训练，迫使模型学习“读懂任务说明，再按说明生成答案”。FLAN-T5 就是这一路线的代表，即在 T5 底座之上经过 FLAN 指令微调得到的系列模型；它相比原始 T5 更强调零样本（Zero-shot）和少样本（Few-shot）泛化能力。</p>
<div class="blog_h3"><span class="graybg">BART</span></div>
<p>BART（Bidirectional and Auto-Regressive Transformers）同属 Encoder–Decoder，但预训练更强调去噪自编码（Denoising Autoencoding）：对输入做扰动（mask、shuffle、delete 等），让模型恢复原文。它在摘要、生成式改写与条件生成任务上常用作强基线。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模型</td>
<td style="text-align: center;">架构</td>
<td style="text-align: center;">预训练直觉</td>
<td style="text-align: center;">强项</td>
</tr>
</thead>
<tbody>
<tr>
<td>T5</td>
<td>Encoder–Decoder</td>
<td>统一 text-to-text</td>
<td>任务统一；文本生成与分类都自然</td>
</tr>
<tr>
<td>BART</td>
<td>Encoder–Decoder</td>
<td>去噪重建</td>
<td>摘要与生成式改写强基线</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">任务特定语言模型</span></div>
<div class="blog_h3"><span class="graybg">概述</span></div>
<p>任务特定语言模型（Task-specific Language Model）指在通用预训练模型基础上，围绕某个明确监督任务附加任务头（Task Head）并继续微调的模型。常见任务包括句段级分类、token 级序列标注、文本匹配、排序与信息抽取。这里“句段级”指分类对象是一段独立文本，可以是单句，也可以是能够整体输入模型的段落。工程上它并不是一类全新架构，而是“预训练主干 + 任务头 + 对应损失函数”的组合：同样是 BERT、DeBERTa、T5 或大语言模型（Large Language Model, LLM），接什么头、优化什么目标，决定了它最终服务什么任务。</p>
<p>对 BERT 类 Encoder-only 模型，多分类通常采用“句向量 + 线性分类头（Linear Classification Head）”的形式。设句子表示为 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span>，类别数为 <span displaypfx="inline-" class="mathjax-container">\(K\)</span>，则分类头输出 logits：</p>
<span displaypfx="" class="mathjax-container">\[z=Wh+b,\quad W\in\mathbb{R}^{K\times d},\quad b\in\mathbb{R}^{K}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(h\)</span> 是主干模型抽取出的整句语义表示，常取自 <pre class="crayon-plain-tag">[CLS]</pre> 对应隐藏状态或池化结果；<span displaypfx="inline-" class="mathjax-container">\(W\)</span> 把 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 维表示映射到 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 个类别；<span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是偏置项；<span displaypfx="inline-" class="mathjax-container">\(z_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 类的原始分数（logit）。若需要概率，可再经过 softmax：</p>
<span displaypfx="" class="mathjax-container">\[p(y=k|x)=\frac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}}\]</span>
<p>训练时通常直接把 logits <span displaypfx="inline-" class="mathjax-container">\(z\)</span> 输入交叉熵（Cross-Entropy）损失，softmax 与对数运算一般由损失函数内部完成，以获得更好的数值稳定性；推理时若需要概率分布、阈值决策或置信度排序，再显式做 softmax。实践中还常在分类头前加入 Dropout，以降低小样本场景下的过拟合风险。</p>
<p>命名实体识别（Named Entity Recognition, NER）等 token 级任务的输出结构不同。设序列长度为 <span displaypfx="inline-" class="mathjax-container">\(T\)</span>，第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个 token 的隐藏状态为 <span displaypfx="inline-" class="mathjax-container">\(h_t\)</span>，则每个位置的标签打分可写成：</p>
<span displaypfx="" class="mathjax-container">\[z_t=Wh_t+b,\quad t=1,\dots,T\]</span>
<p>此时分类头不再输出“整句一个类别”，而是为每个 token 输出一组 BIO / BIOES 标签 logits。若任务更强调标签转移的一致性，还可在 token 分类头后叠加条件随机场（Conditional Random Field, CRF），显式约束标签序列的合法转移。</p>
<p>因此，任务头的输出形式始终取决于任务本身：句子分类输出 <span displaypfx="inline-" class="mathjax-container">\(\mathbb{R}^{K}\)</span> 上的一组 logits，token 分类为每个位置输出一组 logits，匹配/排序任务常输出单个相关性分数，生成任务则在词表维度输出下一 token 的 logits。主干负责提供中间表示（Intermediate Representation），任务头负责把这种表示投影成可直接优化的任务空间。</p>
<p>到 2026 年，任务特定语言模型的工程选型已经稳定分化。对高并发、闭集标签、边界清晰且标注数据相对充足的文本分类与 NER，DeBERTa、ModernBERT 一类 Encoder-only 模型仍然具有极高性价比：延迟低、吞吐高、概率校准更稳、部署成本更可控。这里的输入并不只限于单句；传统 Encoder-only 模型常见有效长度大约在 512 tokens，而较新的长上下文 Encoder-only 模型已经普遍扩展到 8K tokens，因此数百字的段落在 2026 年通常也仍属于可直接处理的范围。对样本极少、语义规则复杂、输出结构开放，或需要“理解 + 生成”一体化的任务，LLM 配合参数高效微调（Parameter-Efficient Fine-Tuning, PEFT）通常更有优势。</p>
<p>在这类 LLM 任务里，LoRA 仍然是默认起点：它最适合指令跟随、风格迁移、格式约束、轻量领域适配等大多数常规需求。若显存是第一约束，QLoRA 往往是最自然的落点；若任务要求更接近全参数微调的表达力，例如深领域知识吸收、复杂推理、困难边界判别或更强的稳定性，则可优先考虑 DoRA 或 Q-DoRA。全参数微调仍然保留在少数高门槛场景：领域迁移极深、训练数据极大，或需要改动词表、上下文长度、位置编码等底层结构时，它仍然提供最高上限。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">任务形态</td>
<td style="text-align: center;">更常见方案</td>
<td style="text-align: center;">主要原因</td>
<td style="text-align: center;">典型模型</td>
</tr>
</thead>
<tbody>
<tr>
<td>闭集文本分类</td>
<td>Encoder-only + 分类头</td>
<td>判别边界清晰；推理便宜；概率输出稳定</td>
<td>BERT / RoBERTa / DeBERTa / ModernBERT</td>
</tr>
<tr>
<td>标准 NER / 序列标注</td>
<td>Encoder-only + token head（可叠加 CRF）</td>
<td>天然适配 token 级标签；边界学习直接</td>
<td>BERT-CRF / DeBERTa-CRF</td>
</tr>
<tr>
<td>低资源复杂分类</td>
<td>LLM + LoRA / QLoRA</td>
<td>预训练知识丰富；少样本泛化强；显存门槛低</td>
<td>Qwen / Llama / Gemma + LoRA</td>
</tr>
<tr>
<td>复杂结构抽取 / JSON 输出</td>
<td>LLM + PEFT 或指令微调</td>
<td>可同时完成理解、抽取、归纳与结构化生成</td>
<td>Qwen / Llama / Mistral 系列</td>
</tr>
<tr>
<td>高难推理 / 深领域适配</td>
<td>LLM + DoRA / Q-DoRA</td>
<td>比普通 LoRA 更接近全参数微调；适合高质量知识注入</td>
<td>Qwen / Llama + DoRA</td>
</tr>
<tr>
<td>极致吞吐线上服务</td>
<td>LLM 标注或蒸馏，Encoder-only 上线</td>
<td>兼顾数据质量、速度与运维成本</td>
<td>LLM 教师 + DeBERTa 学生</td>
</tr>
<tr>
<td>超大规模深度迁移 / 底层结构改造</td>
<td>全参数微调</td>
<td>需要改动表示空间本身，低秩适配容量不足</td>
<td>领域基座继续训练或全量 SFT</td>
</tr>
</tbody>
</table>
<p>工业系统中也常采用混合路线：先用强 LLM 做数据清洗、弱标注、难例发现或标签体系归并，再把任务蒸馏到更轻的 Encoder-only 模型上线。这种做法利用了 LLM 的语义泛化能力，也保留了小模型在延迟、吞吐与稳定性上的优势。</p>
<div class="blog_h3"><span class="graybg">基于嵌入的推荐系统</span></div>
<p>推荐系统也经常以“任务特定模型”的方式落地，尤其是在召回（Recall）阶段。若每首歌曲都能通过歌词、标题、风格标签、歌手简介或多模态信息编码成一个向量 <span displaypfx="inline-" class="mathjax-container">\(e_i\)</span>，那么系统就可以把用户已经选择、收藏或反复播放的歌曲向量聚合成一个用户兴趣表示 <span displaypfx="inline-" class="mathjax-container">\(u\)</span>。一个最常见的做法是把若干已选歌曲的嵌入做平均或加权平均：</p>
<span displaypfx="" class="mathjax-container">\[u=\frac{1}{N}\sum_{i=1}^{N} e_i\]</span>
<p>随后，对候选歌曲 <span displaypfx="inline-" class="mathjax-container">\(s_j\)</span> 计算它与用户兴趣向量的相似度，例如余弦相似度（Cosine Similarity）：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{score}(u,s_j)=\cos(u,e_j)\]</span>
<p>得分越高，说明该歌曲与用户已选择歌曲在嵌入空间里越接近，也就越可能在风格、主题、情绪或语义上相似。于是，如果用户最近连续选择了几首节奏轻快、独立流行、以失恋叙事为主题的歌曲，系统就会优先召回在向量空间中靠近这些歌曲的其它曲目，而不是只依赖“同歌手”或“同标签”的硬规则。</p>
<p>这类推荐的关键不是显式分类，而是<span style="background-color: #c0c0c0;">把“用户喜欢什么”和“歌曲像什么”统一表示到同一嵌入空间，再用最近邻搜索完成相似歌曲推荐</span>。工程上它通常对应双塔模型（Two-Tower Model）或文本/多模态嵌入模型：一侧编码用户历史，一侧编码候选歌曲，训练时用点击、收藏、完整播放等行为构造正样本，再配合负采样或对比学习把用户喜欢的歌曲拉近、不感兴趣的歌曲推远。这样得到的推荐结果，本质上是基于语义相关性而不是基于精确关键词匹配。</p>
<div class="blog_h3"><span class="graybg">基于表示模型的分类</span></div>
<p>一条常见路线是把 BERT、RoBERTa、DeBERTa 这类表示模型（Representation Model）当作固定特征提取器（Feature Extractor）使用：先用预训练基座把输入文本编码成一个向量表示，再在其上训练一个轻量分类器，而不继续更新基座模型参数。这种做法的重点不是“让语言模型重新学习任务”，而是直接利用其已经学到的通用语义表示。</p>
<p>工程上，一个典型流程是：先冻结（Freeze）BERT 系列底座的全部参数，只保留前向编码功能；然后对每条输入文本提取句级表示 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span>，这个表示可以取自 <pre class="crayon-plain-tag">[CLS]</pre> 位置，也可以取池化后的整句向量；最后在 <span displaypfx="inline-" class="mathjax-container">\(h\)</span> 之上训练一个分类器。若用逻辑回归（Logistic Regression）或等价的线性 softmax 分类头，则可写成：</p>
<span displaypfx="" class="mathjax-container">\[z=Wh+b,\quad p(y=k\mid x)=\frac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}}\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(b\)</span> 是新训练的分类器参数，而 BERT 底座参数保持不变。这样做的优点是训练成本低、显存压力小、过拟合风险更可控，也便于把同一个表示底座复用到多个小任务上。代价是分类边界完全依赖预训练表示的可分性：如果任务与通用语料差距较大，或标签语义非常细，冻结底座通常不如继续微调主干模型灵活。</p>
<div class="blog_h3"><span class="graybg">基于嵌入模型的分类</span></div>
<p>与此相近的还有基于嵌入模型（Embedding Model）的分类。它的做法更直接：先用 SBERT、BGE、E5、GTE 或 text-embedding 一类嵌入模型，把整段文本映射成一个句向量 <span displaypfx="inline-" class="mathjax-container">\(e(x)\in\mathbb{R}^{d}\)</span>；然后直接在这个嵌入向量后面接一个分类器，例如逻辑回归、线性分类器或一个很小的 MLP，学习从嵌入空间到类别空间的映射。</p>
<p>若使用最简单的线性分类器，这条路线在形式上与前面的表示模型分类并没有本质区别，仍然可以写成</p>
<span displaypfx="" class="mathjax-container">\[z=We(x)+b\]</span>
<p>不同之处不在“后面接不接分类器”，而在于前面的底座向量是怎么来的：表示模型通常是为理解类任务设计的上下文化表示；嵌入模型则更强调在向量空间中保持语义距离结构，使相似文本彼此接近、不相似文本彼此远离。因此，基于嵌入模型的分类与基于表示模型的分类，主要区别在底座优化目标而不是分类头形式。表示模型更偏向判别式理解，输出表示通常更适合直接服务闭集分类、序列标注和细粒度 NLU；嵌入模型更偏向语义检索与相似度结构，优势在于跨任务复用、向量检索兼容性和“分类 + 召回”一体化。</p>
<p>工程上可以把两者概括成一句话：<span style="background-color: #c0c0c0;">表示模型分类强调“先得到适合判别的表示，再做分类”；嵌入模型分类强调“先得到适合度量的向量，再在其上学习分类边界”</span>。如果任务是标准闭集分类、标签边界清晰、追求最高分类精度，BERT 一类表示模型通常更自然；如果系统本身已经以 embedding 为中心，例如既要分类又要相似检索、聚类、召回或原型匹配，那么直接在嵌入模型后接分类器会更统一，也更容易复用同一套向量基础设施。</p>
<div class="blog_h3"><span class="graybg">基于嵌入模型的聚类</span></div>
<p>嵌入模型（Embedding Model）的另一条典型用途是文本聚类（Text Clustering）。代表性方法如 BERTopic：先用句向量模型把文档映射到嵌入空间，再做降维与聚类，最后从每个簇中抽取代表词或主题词。这里真正决定簇结构的并不只有嵌入模型；降维模型与聚类模型本身仍然是经典机器学习方法。</p>
<p>一个通用流程通常分成三步：</p>
<ol>
<li>使用嵌入模型将文档转换为向量表示，例如把每篇文档编码为 <span displaypfx="inline-" class="mathjax-container">\(e(x_i)\in\mathbb{R}^{d}\)</span>。</li>
<li>使用降维模型把高维向量压缩到更适合聚类的低维空间。</li>
<li>使用聚类模型在降维后的表示上得到簇标签，或进一步识别离群点。</li>
</ol>
<p>降维阶段的典型选型是 PCA（Principal Component Analysis）或 UMAP（Uniform Manifold Approximation and Projection）。PCA 是线性方法，适合作为快速、稳定的基线；UMAP 更擅长保留非线性邻域关系与整体簇结构，因此在文本聚类里往往更常见。工程上常把 UMAP 先降到 5 到 10 维，作为后续聚类的默认起点；这个范围通常已经足以保留主要结构，同时明显降低噪声与计算成本。</p>
<p>UMAP 的一些常见设置也会直接影响聚类形状。<pre class="crayon-plain-tag">min_dist</pre> 控制低维空间中点与点允许靠得多近；若把它设为 0，低维表示通常会形成更紧密的簇。距离度量常设为 <pre class="crayon-plain-tag">cosine</pre>，因为文本表示在高维空间中更常体现为方向相似性；无论是高维稀疏词向量还是高维稠密嵌入，欧氏距离都容易出现判别力下降的问题。</p>
<p>聚类阶段则常见 K-means 或 HDBSCAN。K-means 适合簇数大致已知、簇形状相对规则的场景；HDBSCAN 更适合密度不均、簇形状复杂，或希望显式识别“不属于任何簇”的离群文档。BERTopic 之所以常见，正是因为它把“嵌入模型 + UMAP + HDBSCAN + 主题表示”这条工程链路封装成了一个相对稳定的默认方案。</p>
<div class="blog_h3"><span class="graybg">基于嵌入模型的主题建模</span></div>
<p>基于嵌入模型的主题建模（Topic Modeling）与上一节的聚类路线一脉相承：先把文档映射为向量，再做降维与聚类；不同之处在于，这里还要继续回答“每个簇到底在谈什么”。因此，在聚类结果之上还需要增加一个<span style="background-color: #c0c0c0;">主题提取（Topic Extraction）</span>步骤，为每个簇生成一组能概括其内容的主题关键词。</p>
<p>这一步的典型做法是 c-TF-IDF（class-based TF-IDF）。它不是对单篇文档计算 TF-IDF，而是先把同一簇里的所有文档拼接成一个“类别文档”，再统计词在该簇中的相对频率，并结合它在其它簇中的区分度计算权重。于是，一个词若在当前簇中频繁出现、但在其它簇中并不常见，它的 c-TF-IDF 权重就会更高；反之，那些在所有簇里都常见的泛化词，其权重会被压低。这样提取出来的关键词，描述的是“这个簇相对于其它簇最有代表性的内容”，而不是“整个语料里最常见的词”。</p>
<p>从工程流程看，这条路线可以写成四步：先用嵌入模型得到文档向量，再用降维与聚类得到簇，随后用向量化器统计每个簇中的词项分布，最后用 c-TF-IDF 为每个簇生成主题词。BERTopic 的核心价值就在于，它把“嵌入模型 + UMAP + HDBSCAN + c-TF-IDF”串成了一个统一框架，因此既保留了 embedding 在语义空间中的表达能力，也保留了词袋统计在主题解释上的可读性。</p>
<p>不过，c-TF-IDF 产出的关键词顺序仍然主要依赖词频统计与类间区分度，语义相关性未必总是最优。于是 BERTopic 又提供了主题表示微调（Representation Tuning）机制：先用 c-TF-IDF 生成候选关键词，再对这些候选词做重新排序。这里最常见的表示模型之一是 KeyBERTInspired。它会先利用 c-TF-IDF 为每个主题挑出一组代表性文档，把这些代表文档聚合成该主题的语义表示，再用与文档编码相同的嵌入模型去计算“候选关键词与主题表示”的语义相似度，最后按相似度重排关键词顺序。在实践中，这种表示方式通常还能进一步压低停用词在最终主题表示中的占比，使主题词列表更干净。</p>
<p>因此，KeyBERTInspired 并不是重新做一遍聚类，也不是替代 c-TF-IDF；它更像是在 c-TF-IDF 给出的候选集之上增加一层语义重排序。这样做的结果通常是：靠前的主题词更连贯、停用词和噪声词更少，主题标签也更接近人类对“这个簇在讲什么”的直觉。对 BERTopic 而言，这一步属于主题表示优化，而不是主题发现本身。</p>
<p>即便经过上述处理，主题关键词之间仍可能存在明显冗余，例如多个高频近义词反复出现在同一主题里。此时还可以进一步使用最大边际相关性（Maximal Marginal Relevance, MMR）做关键词多样化：它在选择下一个关键词时，同时考虑“该词与主题表示有多相关”以及“该词与已经选中的关键词有多相似”，从而找到一组彼此具有差异性、但仍然和目标文档或主题表示保持相关的关键词。于是，MMR 的作用不是提升聚类质量，而是让最终主题表示更分散、更少重复，也更适合人工阅读与命名。</p>
<p>在此基础上，还可以再走一步：把已经得到的一组主题关键词交给生成模型（Generative Model），例如 FLAN-T5，让模型基于这些关键词生成一个更短、更自然的主题标签或一小段摘要式说明。这样做并不改变主题发现本身，而是把“关键词列表”进一步压缩成更适合展示给用户阅读的主题名称。</p>
<div class="blog_h3"><span class="graybg">基于嵌入模型的零样本分类</span></div>
<p>嵌入模型还可以直接用于零样本分类（Zero-shot Classification）。做法是不训练额外分类器，而是先把每个候选类别改写成自然语言标签描述，再把输入文本与这些标签描述同时编码到同一嵌入空间中，通过相似度完成类别判断。若影评任务只有“正面”和“负面”两类，就可以构造两个标签文本，例如“这是一条正面影评”和“这是一条负面影评”，然后分别计算影评向量与两个标签向量的余弦相似度：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{score}_k=\cos(e(x),e(t_k))\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 是待分类影评，<span displaypfx="inline-" class="mathjax-container">\(t_k\)</span> 是第 <span displaypfx="inline-" class="mathjax-container">\(k\)</span> 个类别对应的标签描述。若 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{score}_{\text{positive}}\)</span> 高于 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{score}_{\text{negative}}\)</span>，系统就把该影评判为正面评价。这个过程本质上是<span style="background-color: #c0c0c0;">把分类问题改写为“文本与标签描述谁更接近”的相似度匹配问题</span>。</p>
<p>这条路线的优点是部署快、几乎不需要任务专用标注数据，也不必重新训练分类头；只要标签语义写得足够清晰，就可以立刻在新任务上工作。它的代价同样直接：由于模型并未使用该任务的监督信号显式学习分类边界，零样本分类的准确率通常低于有标注数据时训练出的监督式分类器，尤其在标签定义细、类别边界接近、文本包含反讽或领域术语时更明显。</p>
<div class="blog_h3"><span class="graybg">基于生成模型分类</span></div>
<p>另一条更轻量的工程路线是基于生成模型（Generative Model）做分类，即直接调用大语言模型（Large Language Model, LLM），通过提示词工程（Prompt Engineering）把分类任务表述为指令，例如要求模型在读完一段文本后只输出“正面”或“负面”。这种做法本质上是把分类看作条件生成：模型先理解输入，再生成最符合提示约束的类别标签。这里的“生成模型”不只包括 Decoder-only LLM；像 T5 这样的 Encoder–Decoder 模型，虽然结构上属于编解码器，也同样可以在不给额外训练的情况下，直接通过输入提示词完成分类，因此在使用方式上也可以视作这一类路线。</p>
<p>它的优点是上手快、改标签体系方便、对少样本或零样本任务尤其灵活；局限是输出稳定性、延迟和成本通常不如专门训练的分类器，而且类别边界是否清晰、提示词是否严格，会明显影响结果一致性。因此，这条路线更适合作为快速原型、弱标注工具或低频复杂任务的分类接口，而不是高吞吐、强约束场景下的默认方案。</p>
<div class="blog_h1"><span class="graybg">多模态模型</span></div>
<p>多模态系统通常先建立稳定的单模态编码器，再通过对齐或指令微调把不同模态接到统一框架里，而不是从零开始同时学习图像与文本。视觉 Transformer（Vision Transformer, ViT）处在这条链路的前端：它负责把原始图像变成可供分类、检索或跨模态建模使用的数值表示。</p>
<div class="blog_h2"><span class="graybg">视觉编码器</span></div>
<div class="blog_h3"><span class="graybg">ViT</span></div>
<p>ViT（Vision Transformer）本身不是视觉-语言模型（Vision-Language Model, VLM），因为它只处理图像，不直接建模文本，也不负责跨模态对齐。它的角色是视觉编码器（Visual Encoder）或视觉骨干网络（Backbone）：把非结构化的像素输入转换为结构化的 token 序列表示，再交给 Transformer 编码器提取全局特征。</p>
<p>ViT 的关键创新是把图像按固定大小切成网格化的 patch，并把每个 patch 视作一个视觉 token。这里的 token 与文本 token 不同：文本 token 可以通过词表映射到固定 ID，而图像 patch 没有天然离散词表，因此不能直接分配类似词 ID 的符号编号。工程上通常先把每个 patch 展平，再通过可学习的线性投影映射到统一维度的向量空间，使其在形式上与文本 token embedding 相同，然后叠加位置编码（Positional Encoding）送入 Transformer 编码器。</p>
<p>这种设计把卷积网络擅长处理的二维图像，重写为 Transformer 可直接处理的一维 token 序列。结果不是“理解语言”，而是得到可用于分类、检测、检索或跨模态对齐的高层视觉表示。因此，在 CLIP、LLaVA 一类系统里，ViT 更准确的定位是图像侧表征学习模块，而不是完整的多模态模型。</p>
<div class="blog_h2"><span class="graybg">视觉-语言模型</span></div>
<div class="blog_h3"><span class="graybg">CLIP</span></div>
<p>CLIP（Contrastive Language–Image Pre-training）用对比学习（Contrastive Learning）把图像与文本对齐到同一嵌入空间：图像编码器与文本编码器分别输出向量，通过 InfoNCE 类目标让匹配对相似度更高、非匹配对更低。它既可用于图文检索，也可把类别名/提示词当作文本侧输入实现零样本分类（Zero-shot Classification）。</p>
<p>CLIP 的训练前提就是图像与文本的配对数据。原始论文使用的是大规模图文对语料：每个样本包含一张图像及其对应文本，监督信号不是人工精标类别，而是“哪段文本与哪张图像匹配”。因此，CLIP 的核心不在生成，而在跨模态表示对齐。</p>
<div class="blog_h3"><span class="graybg">OpenCLIP</span></div>
<p>OpenCLIP 是 CLIP 路线的开源实现与扩展生态。它复现并扩展了 OpenAI CLIP 的双编码器（Dual Encoder）对比学习框架，使研究者和工程团队可以在公开数据、公开代码和公开权重上继续训练、评测与部署。</p>
<p>工程上，OpenCLIP 的价值主要体现在两点。第一，它把 CLIP 从“论文范式”变成了可复现基础设施：常见模型直接训练或发布在 LAION-400M、LAION-2B、DataComp-1B 等公开图文对数据集之上。第二，它把视觉塔和训练配方做成了可替换组件，既能使用 ViT，也能扩展到 ConvNeXt 等视觉骨干。因此在检索系统、零样本分类和多模态 LLM 前端里，OpenCLIP 往往是比原始 CLIP 更常见的工程起点。</p>
<div class="blog_h3"><span class="graybg">BLIP-2</span></div>
<p>BLIP-2 的核心目标是让现成的文本生成模型获得图像理解输入。它把预训练视觉编码器与预训练大语言模型（Large Language Model, LLM）冻结下来，只训练中间的轻量桥接模块，从而尽量保留两侧已有能力：视觉编码器继续负责稳定的图像表征，LLM 继续负责成熟的语言生成。</p>
<p>这个桥接模块在论文中的标准名称是 Q-Former（Querying Transformer），不是单纯意义上的“Q-Transformer”。Q-Former 通过一组可学习 query 向量，从冻结视觉编码器输出的图像特征中抽取对语言生成最有用的信息，再把这些压缩后的视觉表示投影到 LLM 可接受的输入空间。它本质上充当跨模态接口：一端接视觉特征，一端接语言模型，使跨模态信息可以传入文本生成链路。</p>
<p>因此，BLIP-2 的方法论很清晰：不破坏已有单模态模型优势，只训练一个参数量相对较小的连接层完成跨模态迁移。这类设计直接影响了后续大量“视觉编码器 + LLM”系统，因为它证明了多模态能力并不一定依赖昂贵的全模型联合训练，也可以通过桥接模块高效获得。</p>
<div class="blog_h3"><span class="graybg">LLaVA</span></div>
<p>LLaVA（Large Language and Vision Assistant）属于“视觉编码器 + LLM”的多模态对话范式：用视觉编码器（如 ViT）提取图像特征，通过投影层把视觉特征对齐到语言模型输入空间，再做视觉指令微调（Visual Instruction Tuning），让模型学会围绕图像进行问答、描述与推理。</p>
<div class="blog_h3"><span class="graybg">GPT-4V / Gemini</span></div>
<p>GPT-4V / Gemini 这类通用多模态大模型（General-purpose Multimodal LLM）通常把视觉理解、OCR、推理与工具使用统一到一个端到端系统中，并以 API 形态提供能力。工程上它们常作为“强但贵”的最终裁决器：当规则与小模型不稳时，用多模态 LLM 做兜底或少量精排。</p>
<div class="blog_h2"><span class="graybg">当前前沿系统怎么做</span></div>
<p>截至 2026 年，多模态 SOTA 更适合从系统设计理解，而不是从单一榜单理解。闭源前沿模型的完整架构通常不公开，但公开资料已经足够说明一个稳定趋势：当前领先系统正在从“图像接到语言模型前面”演化为“统一感知、统一推理、统一动作”的多模态基础模型。</p>
<p>第一类趋势是<span style="background-color: #c0c0c0;">原生多模态（Native Multimodality）</span>。这类系统不再把语音识别、视觉编码、文本生成严格拆成彼此独立的流水线，而是尽量让不同模态进入同一推理主干。这样做的价值在于保留跨模态细节，例如语音中的语气、图像中的局部布局、视频中的时间连续性。工程意义也很直接：模型不只是“看图回答”，而是能在文本、图像、音频、视频之间共享上下文。</p>
<p>第二类趋势是<span style="background-color: #c0c0c0;">更强的视觉前端</span>。当前领先系统很少满足于固定分辨率图片分类，而是强调高分辨率文档、图表、界面截图和长视频理解。对应做法包括动态分辨率（Dynamic Resolution）、窗口注意力（Window Attention）、时间维采样以及跨模态位置编码。这说明视觉模块已经不再只是提供粗粒度 caption feature，而是在承担 OCR、布局解析、目标定位、视频时序理解等更细的感知职责。</p>
<p>第三类趋势是<span style="background-color: #c0c0c0;">把推理与工具调用纳入同一闭环</span>。当前最强系统的目标不只是输出一句描述，而是把“看见什么”“如何思考”“接下来调用什么工具”串成连续决策过程。于是多模态模型逐步具备结构化抽取、框选定位、网页或桌面操作、检索增强、实时语音对话等能力。此时它更接近通用代理（General Agent），而不只是视觉问答模型。</p>
<p>从代表系统看，OpenAI 的 GPT-4o 路线强调端到端原生多模态统一；Gemini 当前公开产品线则明显把长上下文、实时音视频交互与原生音频推理并到主模型能力里；Anthropic 的 Claude 多模态路线更突出长上下文推理、工具使用与计算机操作；开源路线里，Qwen2.5-VL 则把这些趋势写得最具体，包括动态分辨率 ViT、长视频建模、目标定位、结构化输出以及视觉代理能力。</p>
<p>因此，今天讨论多模态 SOTA，重点已经不是“有没有图像输入”，而是四个问题：是否原生统一多模态、是否支持高分辨率与长视频、是否能把感知结果转成结构化中间表示、以及是否能在推理过程中可靠调用外部工具。能同时把这四点做好，才接近当前前沿系统的真实标准。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center;">典型代表</td>
<td style="text-align: center;">训练目标</td>
<td style="text-align: center;">强项</td>
<td style="text-align: center;">局限</td>
</tr>
</thead>
<tbody>
<tr>
<td>视觉编码器（单模态基础）</td>
<td>ViT</td>
<td>图像 patch token 化与表征学习</td>
<td>全局视觉特征强；易作为多模态系统的图像 backbone</td>
<td>本身不建模文本；不直接完成跨模态对齐</td>
</tr>
<tr>
<td>对比学习对齐（Dual Encoder）</td>
<td>CLIP / OpenCLIP</td>
<td>图文对比（InfoNCE）</td>
<td>检索与零样本分类强；向量可索引</td>
<td>生成能力弱；细粒度推理受限</td>
</tr>
<tr>
<td>视觉编码器 + LLM</td>
<td>BLIP-2 / LLaVA 类</td>
<td>桥接对齐 + 视觉指令微调</td>
<td>对话与推理能力强；可扩展工具调用</td>
<td>推理成本高；视觉细节依赖对齐质量</td>
</tr>
<tr>
<td>通用多模态 LLM</td>
<td>GPT-4V / Gemini</td>
<td>多任务端到端</td>
<td>能力上限高；工程集成成熟</td>
<td>成本与可控性；数据合规</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">图像生成模型</span></div>
<div class="blog_h3"><span class="graybg">DALL-E</span></div>
<p>DALL-E 属于“文本到图像”（Text-to-Image, T2I）生成模型路线之一：输入提示词生成图片。工程上更关注提示词控制、风格一致性与安全过滤，而不是单纯的像素指标。</p>
<div class="blog_h3"><span class="graybg">Stable Diffusion</span></div>
<p>Stable Diffusion 属于扩散模型（Diffusion Model）的潜空间版本（Latent Diffusion）：在潜变量空间做扩散与去噪，再解码为图像。它的工程优势在于开源生态、可控微调（LoRA/ControlNet 等）与本地部署可能性。</p>
<div class="blog_h3"><span class="graybg">Imagen</span></div>
<p>Imagen 是文本到图像扩散路线的代表之一，强调高质量生成与文本-图像对齐。与其他 T2I 模型一样，最终效果高度依赖数据与训练配方。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">类别</td>
<td style="text-align: center;">代表</td>
<td style="text-align: center;">优势</td>
<td style="text-align: center;">常见玩法</td>
</tr>
</thead>
<tbody>
<tr>
<td>商业 T2I</td>
<td>DALL-E / Imagen</td>
<td>效果稳定；产品化完善</td>
<td>提示词工程；风格控制；安全策略</td>
</tr>
<tr>
<td>开源扩散</td>
<td>Stable Diffusion</td>
<td>可本地部署；可深度定制</td>
<td>LoRA/ControlNet；自定义 checkpoint；工作流编排</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">语音模型</span></div>
<div class="blog_h3"><span class="graybg">Whisper</span></div>
<p>Whisper 是自动语音识别（Automatic Speech Recognition, ASR）模型代表之一：输入音频输出转写文本。工程落地的关键在于流式推理（Streaming）、噪声鲁棒性、领域词表与端到端延迟控制。</p>
<div class="blog_h3"><span class="graybg">语音合成（TTS）</span></div>
<p>语音合成（Text-to-Speech, TTS）把文本转为音频波形。工程关注点包括音色一致性（Voice Consistency）、韵律（Prosody）、多说话人控制、以及与 ASR/对话系统的整体延迟。</p>
<div class="blog_h2"><span class="graybg">多模态对齐</span></div>
<div class="blog_h3"><span class="graybg">对比学习对齐（CLIP 范式）</span></div>
<p>对比学习对齐（Contrastive Alignment）用“匹配对相似、非匹配对不相似”的目标把不同模态映射到同一空间。它的最大价值是：对齐后可以做检索（Retrieval）、聚类与零样本迁移（Zero-shot Transfer），并为后续的多模态 LLM 提供可用的视觉表示初始化。</p>
<div class="blog_h3"><span class="graybg">视觉指令微调</span></div>
<p>视觉指令微调（Visual Instruction Tuning）把多模态输入组织为对话/指令格式，用监督数据训练模型按指令理解图像并输出文本。与纯对比对齐相比，它更强调“可用性”：模型能解释、能推理、能按格式回答。</p>
<div class="blog_h1"><span class="graybg">预训练与微调</span></div>
<p>对生成式大语言模型（Large Language Model, LLM）而言，训练通常不是一步完成，而是一个逐层收紧目标的三阶段过程。第一阶段是预训练（Pretraining）：让模型“学会语言”，掌握通用世界知识、语言统计规律与基础生成能力。第二阶段是监督微调（Supervised Fine-Tuning, SFT）：让模型“学会按指令做事”，把通用生成能力收束到更明确的任务格式、回复风格和指令遵循能力上。第三阶段是偏好对齐（Preference Alignment）：让模型“生成得更好”，在多个候选答案中更稳定地偏向人类认为更有帮助、更安全、更相关的输出。</p>
<p>这三步也对应了三种不同的模型状态。只做完预训练的模型通常称为基础模型（Base Model）：它拥有语言能力，但并不天然理解“用户现在真正想要什么”。经过监督微调后，模型开始具备指令理解与任务对齐能力，这一步常被称为指令微调（Instruction Tuning）。再往后，通过偏好对齐把模型行为进一步向人类目标、价值约束和回答偏好靠拢，这一步才构成更完整的对齐（Alignment）过程。当前主流通用助手模型，基本都建立在这条“预训练 → SFT → 偏好对齐”的标准范式之上。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">阶段</td>
<td style="text-align: center;">核心目标</td>
<td style="text-align: center;">主要训练信号</td>
<td style="text-align: center;">产出能力</td>
</tr>
</thead>
<tbody>
<tr>
<td>预训练（Pretraining）</td>
<td>学会语言与通用知识</td>
<td>大规模通用语料上的自监督目标，如 next-token prediction</td>
<td>基础语言建模能力，形成 Base Model</td>
</tr>
<tr>
<td>监督微调（SFT）</td>
<td>学会遵循指令与任务格式</td>
<td>高质量指令-回答、任务输入-输出配对数据</td>
<td>更稳定的指令理解、格式控制与领域适配能力</td>
</tr>
<tr>
<td>偏好对齐（Preference Alignment）</td>
<td>让输出更符合人类偏好</td>
<td>偏好排序、奖励模型（Reward Model）、RLHF / DPO 等对齐信号</td>
<td>更有帮助、更安全、更符合人类判断的回答</td>
</tr>
</tbody>
</table>
<p>从工程视角看，微调的本质不是重新训练一个模型，而是在保留预训练通用能力的前提下，对模型进行二次开发。领域适配、风格约束、指令遵循、安全边界和用户体验优化，几乎都发生在预训练之后。也正因为如此，微调并不是可有可无的附加步骤，而是把“会说话的模型”变成“可用的产品模型”的关键环节。</p>
<div class="blog_h2"><span class="graybg">预训练（Pre-training）</span></div>
<div class="blog_h3"><span class="graybg">数据收集、清洗与配比</span></div>
<p>预训练首先是一条数据管线（Data Pipeline）。对大语言模型而言，模型最终学到什么、遗漏什么、偏向什么，首先取决于它看到了哪些语料，以及这些语料在进入训练前经历了怎样的筛选、清洗与混合。训练配方当然重要，但在大规模预训练里，<span style="background-color: #c0c0c0;">数据分布本身往往比单个优化技巧更具决定性</span>。</p>
<p>第一步通常是收集训练数据。来源往往包括公开网页、百科、书籍、论文、新闻、论坛、代码仓库、问答站点、对话语料以及经过授权的专有文本。不同来源的作用并不相同：网页和百科提供广覆盖的语言统计与世界知识，代码语料强化程序生成与形式化模式，论文和书籍提升长程结构与知识密度，对话数据则更贴近后续助手形态。预训练阶段谈“知识注入”，最底层的载体首先就是这些原始语料源。</p>
<p>第二步是数据清洗（Data Cleaning）。原始互联网语料通常充满模板页、导航栏、广告、乱码、截断文本、语言混杂、低信息密度页面和大规模重复内容，直接拿来训练只会把噪声写进模型。常见清洗动作包括：语言识别、文本抽取、HTML / Markdown 噪声剥离、异常字符过滤、长度过滤、文档质量评分、敏感内容过滤，以及近重复或完全重复文档去除。它的目的不是把数据“洗得越干净越好”，而是把明显无价值、重复或高风险的部分尽量挡在训练集之外。</p>
<p>第三步是数据去重与质量过滤。对现代大模型来说，重复数据并不只是浪费 token 预算，还会放大训练分布中的头部模式，使模型更容易过拟合少数高频模板、降低有效数据多样性，并污染后续评测。于是，工程上通常既要做文档级去重，也要做段落级、片段级甚至近似语义去重；同时配合质量分类器、启发式规则或小模型过滤，把低信息密度、机器生成垃圾、SEO 内容农场和错误密集文本压低占比。</p>
<p>第四步是数据配比（Data Mixture）。预训练通常不是把所有清洗后的数据简单拼接后均匀抽样，而是会显式控制不同来源、语言、领域和模态的采样比例。原因在于：不同语料的规模差异极大，若完全按原始数量采样，网页噪声和头部来源往往会淹没更高质量但规模更小的数据，例如书籍、论文和代码。数据配比的本质，是决定模型应该把多少训练预算分配给广覆盖、多少分配给高质量、多少分配给特定能力方向。</p>
<p>这种配比通常带来直接的能力权衡。代码比例升高，模型的程序生成和形式化推理往往更强，但自然语言对话风格未必同步变好；高质量书面语比例升高，模型的行文稳定性和知识密度往往改善，但口语互动和开放域覆盖可能下降；多语比例升高，则跨语言泛化更强，但单语极致性能未必最优。因此，数据配比并不是纯粹的数据工程细节，而是预训练目标函数之外最重要的能力分配器之一。</p>
<p>第五步才是把整理后的语料送入真正的训练阶段。也正因为前面已经做过收集、清洗、去重和配比，后面的“初期训练、中期训练、退火训练”才有明确的数据基础：初期通常强调大规模广覆盖混合，中后期再逐步提高高质量数据、特定能力语料或长上下文样本的权重。换句话说，阶段化训练并不是独立于数据工程存在的，它本身就是建立在<span style="background-color: #c0c0c0;">先构造可控数据分布，再按阶段调整采样分布</span>这一前提之上。</p>
<div class="blog_h3"><span class="graybg">阶段化训练与知识注入</span></div>
<p>现代大语言模型的预训练，通常不是把同一种数据、同一种上下文长度和同一组优化超参数一路跑到结束，而更接近一种分阶段课程学习（Curriculum Training）。所谓“知识注入”，本质上也不是把某条事实单独写入参数，而是通过逐步调整数据分布、上下文长度、学习率和训练目标，让模型先建立通用语言统计骨架，再吸收更高质量、更长程或更专业的模式。</p>
<p>工程上常见的三段式可以概括为：</p>
<ul>
<li><span style="background-color: #c0c0c0;">初期训练</span>。这一阶段通常以海量、多样、相对较短的上下文为主，重点是尽快建立词法、句法、语义组合、事实共现与基础推理的统计骨架。之所以大量使用短上下文，是因为在标准注意力下，序列长度增加会显著抬高训练成本；在固定算力预算下，较短序列通常能换来更多 token 更新和更稳定的早期收敛。</li>
<li><span style="background-color: #c0c0c0;">中期训练（Mid-training）</span>。当模型已经具备基本语言能力后，训练重点会从“广覆盖”逐步转向“高价值分布塑形”。这一阶段更常看到更严格过滤的高质量语料、代码、推理数据、专业领域语料，或逐步扩展的上下文长度。它的作用不是重学语言本身，而是把模型的能力重心推向更有用的区域，例如更强的代码能力、更稳的长程依赖、更贴近目标领域的表达分布。</li>
<li><span style="background-color: #c0c0c0;">退火训练（Annealing Phase）</span>。这是预训练后段的精修阶段，通常伴随更小的学习率、更保守的更新幅度，以及更精选、更低噪声的数据混合。它的目标不是再靠大步更新去扩张知识覆盖面，而是收束参数、压低噪声影响、强化高质量模式，并把模型最终的能力形态稳定下来。很多现代配方会把更专业或更高质量的数据留到这一阶段，以获得更好的下游表现。</li>
</ul>
<p>从“注入什么知识”的角度看，这三段关注的重点并不相同。初期训练主要注入广覆盖的语言统计、世界常识共现和通用结构先验；中期训练主要注入能力相关的分布偏好，例如代码、推理、长文档和领域语料；退火训练则更像把高价值知识和高质量行为模式做最后收束，使模型从“已经学会很多”走向“把重要能力学得更稳”。</p>
<p>长上下文能力也常在这一框架下被放到中后期处理。原因并不神秘：长上下文训练既昂贵，又更容易让优化目标与数据工程复杂化；如果在模型尚未建立稳定短程语言骨架时就大规模拉长序列，单位算力的有效学习信号往往并不划算。因此，很多训练配方会先用短上下文把基础能力打牢，再在中后段逐步扩展到更长上下文，或者单独追加一段上下文扩展训练。</p>
<p>因此，预训练阶段谈“知识注入”时，更准确的理解不是一次性灌入，而是<span style="background-color: #c0c0c0;">按训练阶段逐步改变模型看到的分布与约束条件</span>：先学会语言，再学会更有价值的语言分布，最后把这些能力收束成一个更稳定的基座模型。</p>
<div class="blog_h3"><span class="graybg">自回归语言建模（CLM）</span></div>
<p>自回归语言建模（Causal Language Modeling, CLM）把文本建模为从左到右的条件概率连乘：给定前缀 <span displaypfx="inline-" class="mathjax-container">\(x_{&lt;t}\)</span> 预测下一个 token <span displaypfx="inline-" class="mathjax-container">\(x_t\)</span>。训练目标是最小化 next-token 交叉熵：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{CLM}}(\theta)=-\sum_{t=1}^{L}\log p_\theta(x_t\mid x_{&lt;t})\]</span>
<p>CLM 与 Decoder-only 架构天然匹配：因果 attention mask 保证模型只能看见历史 token，避免训练-推理不一致。绝大多数通用生成式大模型都以 CLM 为主目标。</p>
<div class="blog_h4"><span class="graybg">Multi-Token Prediction（MTP）</span></div>
<p>多 token 预测（Multi-Token Prediction, MTP）是在 CLM 基础上的“监督信号加密”：除了预测 <span displaypfx="inline-" class="mathjax-container">\(x_{t+1}\)</span>，还额外让模型在同一隐藏状态上预测更远的未来 token（例如 <span displaypfx="inline-" class="mathjax-container">\(x_{t+2}\)</span>、<span displaypfx="inline-" class="mathjax-container">\(x_{t+3}\)</span>），从而在相同序列长度下产生更多训练信号。一个抽象写法是：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{MTP}}(\theta)=-\sum_{t=1}^{L}\sum_{j=1}^{K}\log p_\theta(x_{t+j}\mid x_{&lt;t})\]</span>
<p>MTP 通常作为辅助损失（Auxiliary Loss）提升训练效率或长程规划能力；但推理阶段是否能“真正一次生成多个 token”取决于解码与验证策略，不能简单由训练目标推出。</p>
<div class="blog_h3"><span class="graybg">掩码语言建模（MLM）</span></div>
<p>掩码语言建模（Masked Language Modeling, MLM）随机遮住输入中的一部分 token（替换为 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{MASK}]\)</span> 或其他扰动），训练模型用双向上下文预测被遮住位置的 token。它是 Encoder-only 表示模型（如 BERT 系列）的典型预训练目标：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{MLM}}(\theta)=-\sum_{t\in \mathcal{M}}\log p_\theta(x_t\mid x_{\setminus \mathcal{M}})\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{M}\)</span> 是被 mask 的位置集合。MLM 的优势是能学到更强的双向表示，但它与生成式解码不天然一致，因此“理解类任务”更常用 MLM 预训练模型，“生成类任务”更常用 CLM。</p>
<div class="blog_h3"><span class="graybg">对比学习预训练</span></div>
<p>对比学习预训练（Contrastive Pre-training）把“相似样本拉近、非相似样本推远”作为核心目标。它广泛用于句向量/图像-文本对齐等场景：例如 CLIP 用图像编码器与文本编码器产生表示，对匹配对最大化相似度；Sentence-BERT 等句向量模型也常用对比目标训练。</p>
<p>典型形式是 InfoNCE：对 batch 内正对（Positive Pair）与负对（Negative Pair）做 softmax，对每个 query 只奖励其匹配的 key：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}=-\sum_{i}\log \frac{\exp(\mathrm{sim}(q_i,k_i)/\tau)}{\sum_{j}\exp(\mathrm{sim}(q_i,k_j)/\tau)}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\tau\)</span> 是温度（Temperature），<span displaypfx="inline-" class="mathjax-container">\(\mathrm{sim}\)</span> 常用余弦相似度或内积。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">目标</td>
<td style="text-align: center;">代表架构</td>
<td style="text-align: center;">擅长</td>
<td style="text-align: center;">典型下游</td>
</tr>
</thead>
<tbody>
<tr>
<td>CLM</td>
<td>Decoder-only</td>
<td>生成（Generation）</td>
<td>对话、写作、代码生成</td>
</tr>
<tr>
<td>MLM</td>
<td>Encoder-only</td>
<td>表示学习（Representation）</td>
<td>分类、匹配、序列标注、reranking</td>
</tr>
<tr>
<td>对比学习</td>
<td>Dual Encoder / 多塔</td>
<td>对齐与检索（Alignment/Retrieval）</td>
<td>Embedding、图文检索、聚类</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">继续预训练（Continual Pre-training）</span></div>
<div class="blog_h3"><span class="graybg">领域适配</span></div>
<p>继续预训练（Continual Pre-training）位于“通用预训练”与“下游微调”之间。它不直接把模型改造成某个具体任务的分类器或助手，而是先用目标领域的大规模无标注语料，对已经完成通用预训练的模型再训练一段时间，让参数分布、词汇统计、上下文共现和知识重心向目标领域迁移。对 Encoder-only 模型，这一步通常仍以掩码语言建模（Masked Language Modeling, MLM）为主；对 Decoder-only 生成模型，则通常继续做自回归语言建模（Autoregressive Language Modeling）。</p>
<p>它的核心价值是<span style="background-color: #c0c0c0;">先让底座学会“这个领域怎样说话”，再让它学会“这个任务怎样输出”</span>。若直接拿通用模型去做医疗、法律、金融、代码仓库、企业内部知识库等垂直场景的监督微调，模型往往会同时面对两类落差：一类是领域词汇和表达方式本身就不熟，另一类是下游任务的标签或指令又要求它立即做出稳定判断。继续预训练先处理前一类问题，把底座拉近目标域分布，后续监督微调只需要处理任务映射，训练通常会更稳定，也更节省标注数据。</p>
<p>因此，继续预训练本质上是一种领域自适应预训练（Domain-Adaptive Pretraining, DAPT）。如果继续预训练的语料不仅来自某个大领域，而是更进一步贴近最终任务的输入分布，例如只使用某个具体产品线、某类工单、某种法律文书或某一学科论文语料，那么它也可以被视作任务自适应预训练（Task-Adaptive Pretraining, TAPT）。两者的区别不在训练算法，而在语料与最终应用的距离：DAPT 更强调“进入这个领域”，TAPT 更强调“贴近这个任务”。</p>
<p>领域适配能带来的收益通常体现在四个层面。第一，模型会更熟悉目标域词汇和短语共现，例如医学缩写、金融术语、企业内部专有名词、代码 API 与日志模式。第二，模型对目标域上下文的概率分布会重新校准，原本罕见的搭配在该领域里会变成高频结构。第三，后续监督微调需要学习的东西会减少，因为模型不必一边补语言常识、一边学任务映射。第四，在低标注数据场景下，继续预训练常常比一上来就重监督微调更稳，因为它先利用了最容易获得的大规模原始文本。</p>
<p>继续预训练最适合三类场景：</p>
<ul>
<li><span style="background-color: #c0c0c0;">领域语料很多、标注很少</span>。这是最经典的适用条件，因为继续预训练最能利用大规模无标注文本，而不要求先构造高成本监督数据。</li>
<li>目标文本分布与通用互联网语料差异极大。例如长文档、半结构化记录、专业术语密集文本、代码与自然语言混合语料；这类差异首先是语言分布差异，而不是标签定义差异。</li>
<li>模型需要吸收的变化更接近“知识与表达分布迁移”，而不是单纯“输出标签变了”或“格式要求变了”。后者通常更适合直接做监督微调或 PEFT。</li>
</ul>
<p>从训练流程看，更合理的顺序通常是：先完成通用预训练，再做领域继续预训练，最后再进入监督微调、参数高效微调或偏好对齐。原因很简单：继续预训练改变的是基座对语言分布和知识结构的建模，而监督微调改变的是输出行为。先做基座适配，再做行为适配，优化目标更清晰，也更符合迁移学习的层次结构。</p>
<div class="blog_h3"><span class="graybg">灾难性遗忘问题</span></div>
<p>继续预训练的主要风险，是灾难性遗忘（Catastrophic Forgetting）。它指的是模型在吸收新分布时，原来在通用语料上学到的能力被明显冲掉：例如领域内术语理解变强了，但通用语言理解、跨领域泛化、常识问答、原始格式鲁棒性或多语言能力反而下降。这个问题并不是继续预训练独有的，但在“新语料分布很窄、训练步数又较长”时尤其容易出现。</p>
<p>其根本原因在于参数共享。神经网络并不会为“旧知识”和“新知识”自动分出两套互不干扰的存储区；当优化器持续在窄领域语料上更新同一组权重时，原先支持通用能力的参数方向会被新的梯度不断改写。如果新领域文本的语言风格、词频结构和任务偏置都高度集中，模型就会把有限参数容量优先分配给当前最常见的模式，从而牺牲原本更广的覆盖面。</p>
<p>灾难性遗忘最常见的外在表现包括：继续预训练阶段训练损失持续下降，但回到通用基准或旧任务验证集上时指标明显退化；模型在目标领域内更流畅，却在开放域输入上变得更僵硬、更偏模板化；对窄领域高频术语反应更强，但对跨域问题的泛化能力下降。这些现象都说明模型不是单纯“学到了更多”，而是在重新分配有限表示能力。</p>
<p>缓解灾难性遗忘有几条经典路线：</p>
<ul>
<li><span style="background-color: #c0c0c0;">控制继续预训练强度</span>。包括减少训练步数、降低学习率、使用更保守的 warmup 与衰减策略，避免模型在窄分布上过度漂移。</li>
<li><span style="background-color: #c0c0c0;">混合语料训练</span>。在领域语料之外保留一定比例的通用语料，让模型在吸收新分布的同时持续回顾旧分布。</li>
<li><span style="background-color: #c0c0c0;">参数隔离</span>。例如只对部分层做继续预训练，或采用 Adapter、LoRA 这类参数高效路径，把领域偏移写进新增参数，而不是完全重写主干。</li>
<li><span style="background-color: #c0c0c0;">保留旧能力验证</span>。继续预训练不应只看领域损失，还应并行跟踪若干通用验证集，否则模型退化往往到很晚才会被发现。</li>
</ul>
<p>因此，继续预训练并不是“领域语料越多、训练越久越好”。更准确的目标应当是：在尽量少破坏通用能力的前提下，把模型的统计重心向目标领域移动。它追求的不是把模型变成只会某个领域的专家，而是在通用底座之上增加一层更贴近目标分布的适配。工程上真正好的继续预训练，通常表现为<span style="background-color: #c0c0c0;">领域内显著增益、领域外可控退化，甚至几乎无退化</span>，而不是单纯把领域损失压到最低。</p>
<div class="blog_h2"><span class="graybg">监督微调（SFT）</span></div>
<p>监督微调（Supervised Fine-Tuning, SFT）用“输入 ➡ 期望输出”的监督数据继续训练预训练模型，使其在特定分布上更符合目标行为。对自回归语言模型（Autoregressive LM）而言，SFT 仍然是 next-token 交叉熵：给定提示词 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与目标回复 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>，最小化</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{SFT}}(\theta)=-\sum_{t}\log \pi_\theta\!\left(y_t\mid x,y_{&lt;t}\right)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(\pi_\theta\)</span> 表示由参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 决定的模型条件概率分布，也就是“在给定前缀条件下，模型对下一个 token 的预测分布”；<span displaypfx="inline-" class="mathjax-container">\(\pi_\theta\!\left(y_t\mid x,y_{&lt;t}\right)\)</span> 就表示模型在看到提示词 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 和目标回复此前各 token <span displaypfx="inline-" class="mathjax-container">\(y_{&lt;t}\)</span> 时，对当前位置正确 token <span displaypfx="inline-" class="mathjax-container">\(y_t\)</span> 赋予的概率。</p>
<p>训练通常使用教师强制（Teacher Forcing）：把 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(y\)</span> 拼接作为输入，但只在目标回复 token 上计算损失（对提示词部分做标签掩码，label masking），避免模型被迫“复述提示词”。</p>
<p>SFT 的必要性，来自基础模型（Base Model）与“可用助手模型”之间的行为差异。基础模型的原始目标只是预测下一个 token，因此它本质上擅长的是<span style="background-color: #c0c0c0;">续写（Completion）</span>，而不是理解人类正在发出什么指令。给它一句 “The car is”，它自然会继续补全常见续文；给它一个问题 “What is 1+1?”，它在没有对齐之前也完全可能把这当成一段待续写文本，而不是一个必须回答的任务。SFT 的作用，就是把这种“见到前缀就续写”的行为，重塑成“读懂输入意图，再输出目标答案”的行为。</p>
<div class="blog_h3"><span class="graybg">全量微调</span></div>
<p>全量微调（Full Fine-tuning）更新模型的全部参数，表达能力最强，但训练成本高、对数据规模与分布漂移更敏感，也更容易出现灾难性遗忘（Catastrophic Forgetting）。它与预训练在优化形式上并没有本质断裂，区别主要在于数据：预训练依赖海量无标注通用语料，全量微调则依赖规模更小但质量更高、目标更明确的标注数据集。正因为所有参数都会被更新，它在特定任务上的性能上限通常最高，但显存、训练时间和权重存储成本也最高；每做一次完整微调，本质上都在生成一个完整的新模型副本。</p>
<div class="blog_h3"><span class="graybg">部分参数微调</span></div>
<p>部分参数微调（Partial Fine-tuning / Selective Fine-tuning）处在全量微调与参数高效微调（PEFT）之间。它的基本做法不是给模型增加新的适配器参数，而是<span style="background-color: #c0c0c0;">只解冻原模型中一部分已有参数</span>，其余参数保持冻结。这样做的直接收益是显存占用更低、训练更快、过拟合风险更可控；代价则是可调空间受限，性能上限通常低于全量微调。</p>
<p>这类方法的核心思想是：并不是每个任务都需要改写整套参数。若下游变化主要集中在输出读出方式、输入符号分布或某一局部计算结构，那么只更新最相关的一小部分参数，往往就足以完成适配。它与后文的 LoRA、Adapter 有一个重要区别：部分参数微调优化的是<span style="background-color: #c0c0c0;">原模型内部已经存在的参数子集</span>；PEFT 则更常通过新增低秩矩阵、瓶颈层或软提示，把任务偏移写进额外参数。</p>
<div class="blog_h4"><span class="graybg">输出层微调</span></div>
<p>输出层微调（Output-layer Fine-tuning）只更新模型最靠近输出读出的部分，例如语言建模头（LM Head）、分类头（Classification Head）、奖励头（Reward Head），或最后少数几层与任务头直接相连的参数，而把主体 Transformer 基本冻结。它最适合“底层表示已经足够好，但最终读出方式需要重塑”的场景。</p>
<p>对表示模型，这通常意味着冻结编码器主体，只训练顶层分类器；对生成模型或对齐流程，则常见于基于已有 SFT 模型训练奖励模型：保留主干表示层，移除原 LM Head，换成输出单一分数的奖励头，再主要围绕这个输出读出层继续训练。它的优势是参数量极小、训练稳定、成本最低；局限在于它基本不改变主干内部表示，因此当任务真正需要重排中间语义结构时，单靠输出层往往不够。</p>
<div class="blog_h4"><span class="graybg">输入层微调</span></div>
<p>输入层微调（Input-layer Fine-tuning）主要更新输入嵌入相关参数，例如 token embedding、位置嵌入，或与新词表、新符号、新模态入口直接相连的输入投影层，而冻结大部分主体网络。它适合输入分布变化显著、但主体推理与表示能力仍然可复用的场景，例如新增领域术语、扩展专有 token、接入特殊控制符，或需要让模型先学会“看懂新输入”。</p>
<p>这条路线在词表扩展与领域符号接入时尤其有价值。因为很多变化并不在“模型不会推理”，而在“模型还没有为这些新符号建立合适入口”。此时先调输入层，可以把新 token 映射到已有表示空间附近，减少一开始就全模型漂移的风险。在一些更重的训练配方里，也会先单独训练输入嵌入，再逐步解冻更深层参数做融合；但其本质始终是先处理<span style="background-color: #c0c0c0;">输入接口适配</span>，再决定是否需要更深层的结构性更新。</p>
<div class="blog_h4"><span class="graybg">局部结构微调</span></div>
<p>局部结构微调（Local-structure Fine-tuning）只选择模型内部某些特定结构或参数类型来更新，例如仅训练偏置项的 BitFit、仅训练归一化参数的 LayerNorm Tuning、只解冻注意力层参数的 Attention Tuning，或只解冻最后若干层的局部 block。它的共同点是：参数选择不是按“输入端 / 输出端”划分，而是按<span style="background-color: #c0c0c0;">网络内部哪类结构最可能承载任务偏移</span>来划分。</p>
<p>这类方法适合算力极其受限、数据量较小，或已经对任务偏移位置有较强先验的场景。例如，若任务主要要求重新标定特征尺度或阈值边界，LayerNorm Tuning 可能就足够；若任务更多是在改变“关注哪里、聚合哪些信号”，只调注意力层可能比盲目放开全部层更高效；若只是希望用极低成本给模型一点任务校正能力，BitFit 这类只训偏置的方案也有现实价值。它们的上限通常不如更强的 PEFT，但在轻量实验、消融研究和极端资源约束环境中依然很有意义。</p>
<div class="blog_h4"><span class="graybg">BitFit</span></div>
<p>BitFit 的做法极端简单：冻结几乎所有权重矩阵，只训练偏置项（Bias Terms）。若某一层的线性变换写成 <span displaypfx="inline-" class="mathjax-container">\(h'=Wh+b\)</span>，BitFit 只更新其中的 <span displaypfx="inline-" class="mathjax-container">\(b\)</span>，而把 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 保持不动。它的参数量因此极小，通常只占全模型参数的很小一部分。</p>
<p>它背后的核心假设是：对不少下游任务而言，预训练模型已经学到了足够强的表示空间与主要变换方向，任务适配真正需要的，未必是重写整块权重矩阵，而可能只是<span style="background-color: #c0c0c0;">调整各层激活的平移、阈值和默认响应水平</span>。从这个角度看，BitFit 更像是在重新标定网络内部各单元的“触发基线”，而不是重建新的特征子空间。</p>
<p>这也解释了它为什么在一些小数据分类、文本匹配或轻量行为校正任务里常常表现得比直觉预期更强：如果任务边界与预训练表示已经高度接近，那么改变少量偏置，就足以让原本已经存在的特征更容易被激活，或更容易跨过最终判别阈值。反过来，当任务需要新的知识写入、复杂结构重排或明显不同的推理路径时，BitFit 往往会很快触到容量上限，因为它几乎无法改变表示之间的主导交互方向。</p>
<div class="blog_h4"><span class="graybg">LayerNorm Tuning</span></div>
<p>LayerNorm Tuning 只更新归一化层中的可学习缩放与偏移参数，典型写法可记为：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{LN}(h)=\gamma\odot \frac{h-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta\]</span>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(\gamma\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span> 就是主要可训练对象，而主体权重矩阵保持冻结。它的参数量同样很小，但比 BitFit 更直接作用于每层隐藏状态的尺度（Scale）与中心（Shift）。</p>
<p>它背后的假设是：很多任务偏移并不需要创造全新的特征，只需要重新调节已有特征在各层中的相对幅度与数值范围。因为 Transformer 中大量残差路径都会经过归一化，LayerNorm 参数对信息流的“放大 / 压低 / 重新居中”有全局影响。于是，只调归一化参数，就可能在不改写主干矩阵的前提下，系统性地改变哪些特征更容易穿过后续层并主导输出。</p>
<p>这类方法尤其适合已经拥有较强基座、但需要重新校准风格、阈值、稳定性或局部行为边界的场景。它通常比 BitFit 稍强，因为它直接控制每层表示的尺度结构；但它仍然主要是在<span style="background-color: #c0c0c0;">重标定现有表示</span>，而不是构造新的复杂变换，因此面对大幅领域迁移或新知识注入时，上限仍然有限。</p>
<div class="blog_h4"><span class="graybg">Attention Tuning</span></div>
<p>Attention Tuning 只解冻注意力模块中的参数，例如 <span displaypfx="inline-" class="mathjax-container">\(W_Q,W_K,W_V,W_O\)</span>，而继续冻结 FFN / MLP 与其他大部分结构。它的核心判断是：不少任务真正需要改变的，不是模型是否拥有某些知识，而是模型在当前任务中<span style="background-color: #c0c0c0;">应该关注哪些 token、怎样聚合远近信息、如何在上下文中分配注意力</span>。</p>
<p>它背后的假设比 BitFit 更强，也更结构化：预训练模型中的知识与模式大体已经存在，任务适配更多是在改变“信息路由（Information Routing）”而不是“知识存储（Knowledge Storage）”。如果这个假设成立，只调整注意力层就能显著改变模型的上下文读取方式，例如更关注结尾句、否定词、实体关系、长程依赖或某些格式锚点，而无需重写 FFN 中更重的参数块。</p>
<p>这也是为什么 Attention Tuning 在行为调整类任务上常有不错的性价比：它比 BitFit 和 LayerNorm Tuning 拥有更强的表示重排能力，又比全量微调和大范围 PEFT 更轻。但它的边界也很清楚。若任务核心是注入新事实、学习新术语本体、补足模型原本缺失的知识映射，仅靠改变注意力路由通常不够，因为许多稳定知识关联最终仍要落在 FFN / MLP 所承载的表示重编码里。</p>
<p>从整体上看，部分参数微调提供的是一种<span style="background-color: #c0c0c0;">选择性解冻原参数</span>的思路：输出层微调优先改读出，输入层微调优先改入口，局部结构微调优先改网络内部某个被认为最关键的子结构。若这些选择性更新已经足够，就没有必要进入更重的全量微调；若它们的容量仍然不够，下一步才更自然地转向后文的 LoRA、Adapter、Prefix Tuning 这类参数高效微调路线。</p>
<div class="blog_h3"><span class="graybg">指令微调（Instruction Tuning）</span></div>
<p>指令微调（Instruction Tuning）是 SFT 的一种数据组织方式：把任务描述（Instruction）显式写进输入，使模型学习“读懂指令并按指令输出”。典型样本是三元组：指令（instruction）、输入（input，可为空）、输出（output）。</p>
<pre class="crayon-plain-tag">{&quot;instruction&quot;:&quot;回答以下问题&quot;,&quot;input&quot;:&quot;世界上最高的山是什么？&quot;,&quot;output&quot;:&quot;珠穆朗玛峰。&quot;}</pre>
<p>对话/FAQ 场景更常用多轮消息格式（Chat Format），把 role（system/user/assistant）显式编码进序列；训练时同样只在 assistant 角色对应的目标 token 上计算损失。</p>
<pre class="crayon-plain-tag">{&quot;messages&quot;:[
  {&quot;role&quot;:&quot;system&quot;,&quot;content&quot;:&quot;你是一个严谨的技术助手。&quot;},
  {&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;解释什么是交叉熵损失。&quot;},
  {&quot;role&quot;:&quot;assistant&quot;,&quot;content&quot;:&quot;交叉熵损失用于衡量预测分布与真实分布的差异&hellip;&hellip;&quot;}
]}</pre>
<p>数据规模没有统一答案，但工程上最关键的是质量（Quality）与覆盖（Coverage）。常见实践：</p>
<ul>
<li>领域 SFT：从 <span displaypfx="inline-" class="mathjax-container">\(10^3\sim10^5\)</span> 级别的高质量样本起步，先跑通指标与错误分析，再扩充数据与任务覆盖。</li>
<li>通用指令微调：更常见的是 <span displaypfx="inline-" class="mathjax-container">\(10^5\sim10^6+\)</span> 的多任务指令样本，用多样性换泛化。</li>
<li>偏好对齐数据：比较对（Preference Pairs）常在 <span displaypfx="inline-" class="mathjax-container">\(10^4\sim10^6\)</span> 级别，且对标注一致性要求更高。</li>
</ul>
<div class="blog_h3"><span class="graybg">拒绝采样微调（Rejection Sampling Fine-Tuning）</span></div>
<p>拒绝采样微调（Rejection Sampling Fine-Tuning）本质上仍然属于监督微调路线。这里讨论的是 rejection sampling fine-tuning 这一路线，而不是近年某些语境里也会写成 RFT 的 reinforcement fine-tuning。它的核心做法是：监督数据不再完全来自人工直接编写，而是先由模型生成多个候选，再通过规则、验证器（Verifier）、奖励模型（Reward Model）或人工筛选，只保留其中最优或通过阈值的样本，最后把这些“被接受”的输出重新写回监督数据集，再按普通 SFT 的方式继续训练。</p>
<p>若把提示词记为 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，候选回答记为 <span displaypfx="inline-" class="mathjax-container">\(\{y^{(k)}\}_{k=1}^{K}\sim \pi_{\mathrm{old}}(\cdot|x)\)</span>，评分函数记为 <span displaypfx="inline-" class="mathjax-container">\(s(x,y)\)</span>，那么拒绝采样微调通常先从这组候选中选出满足 <span displaypfx="inline-" class="mathjax-container">\(s(x,y)\ge \tau\)</span> 的回答，或直接取最高分回答 <span displaypfx="inline-" class="mathjax-container">\(y^\star=\arg\max_k s(x,y^{(k)})\)</span>，再把 <span displaypfx="inline-" class="mathjax-container">\((x,y^\star)\)</span> 当作新的监督样本。后续优化目标并没有变成策略梯度或显式偏好损失，仍然是标准的 next-token 交叉熵。</p>
<p>因此，它可以被理解为一种<span style="background-color: #c0c0c0;">先筛选、再监督</span>的微调方式。与普通 SFT 相比，它利用模型自身采样与外部评分器把“哪种输出更好”这层信息先转成更高质量的目标答案；与 DPO、PPO 这类偏好优化相比，它并不直接学习候选之间的相对排序关系，而是把筛选结果硬化成新的监督标签。工程上，它经常处在普通 SFT 与显式偏好优化之间，既比纯手工 SFT 更能利用自动评估信号，又比完整 RLHF 或 DPO 更容易复用现有监督训练栈。</p>
<p>这条路线尤其适合<span style="background-color: #c0c0c0;">存在较强可验证信号</span>的任务，例如数学推导、代码生成、结构化输出、工具调用轨迹筛选，以及能用单元测试、规则校验、解析器或外部判分器稳定判断好坏的场景。因为一旦评分器足够可靠，拒绝采样就能把“生成多个候选、只留下正确或更优的那个”直接转化为高质量训练样本。</p>
<p>它的边界同样明确。若评分器本身噪声很大，或任务质量强依赖开放式偏好、语气细节、多维安全判断，那么把复杂偏好硬压成“通过 / 不通过”很容易损失信息；筛选过严还会让训练分布变得过窄，导致模型只会复现少数高分写法而削弱多样性。因此，拒绝采样微调更像一种<span style="background-color: #c0c0c0;">高质量数据再蒸馏</span>手段，而不是偏好对齐的终极替代品。</p>
<div class="blog_h3"><span class="graybg">聊天微调（Chat Fine-tuning）</span></div>
<p>聊天微调（Chat Fine-tuning）强调多轮对话一致性：除单轮问答外，还需要覆盖上下文承接、拒答策略、工具调用格式、长对话记忆等。它通常仍是 SFT，只是数据分布更贴近真实对话。</p>
<div class="blog_h2"><span class="graybg">参数高效微调（PEFT）</span></div>
<p>参数高效微调（Parameter-Efficient Fine-Tuning, PEFT）把“微调”从“更新全部参数”变成“冻结基座（Base Model），只训练一小部分新增参数”。它仍然是当前大模型落地的主流选择之一：成本更低、训练更快、便于为不同任务保存多份轻量适配（例如每个业务一套 LoRA）。PEFT 并不只包含 LoRA、Adapter、IA3 这类“在模型内部加参数”的方法，也包含 Prefix Tuning、Prompt Tuning 这类<span style="background-color: #c0c0c0;">软提示（Soft Prompt）</span>路线；它们的共同点不是结构长得像不像，而是都遵循“冻结大部分预训练参数，只训练极小任务参数”这一基本范式。</p>
<div class="blog_h3"><span class="graybg">Adapter</span></div>
<p>Adapter 是<span style="background-color: #c0c0c0;">插在每个 Transformer block 内部的一条很窄的可训练残差支路</span>。设某一层原本输出为 <span displaypfx="inline-" class="mathjax-container">\(h\in\mathbb{R}^{d}\)</span>，Adapter 会先把它投影到一个远小于 <span displaypfx="inline-" class="mathjax-container">\(d\)</span> 的瓶颈维度 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>，经过非线性后再投影回原维度，再以残差形式加回主干：</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Adapter}(h)=W_{\mathrm{up}}\;\sigma\!\left(W_{\mathrm{down}}h+b_{\mathrm{down}}\right)+b_{\mathrm{up}},\quad h'=h+\mathrm{Adapter}(h)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{down}}\in\mathbb{R}^{r\times d}\)</span> 负责降维， <span displaypfx="inline-" class="mathjax-container">\(W_{\mathrm{up}}\in\mathbb{R}^{d\times r}\)</span> 负责升维，且通常有 <span displaypfx="inline-" class="mathjax-container">\(r\ll d\)</span>。这就是所谓的瓶颈结构（Bottleneck Structure）：主干隐藏维度也许是几千，而 Adapter 内部只开放几十到几百维的可训练通道，因此新增参数量远小于全量微调。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/adapter-structure-canvas.jpg"><img class="alignnone size-full wp-image-41967" src="https://blog.gmem.cc/wp-content/uploads/2026/03/adapter-structure-canvas.jpg" alt="adapter-structure-canvas" width="1438" height="877" /></a></p>
<p> 它通常插在自注意力子层或前馈网络子层之后，也就是“原子层输出 + LayerNorm / 残差”附近的位置。最常见的做法是在每个 block 中放两处：一处跟在 attention 输出之后，另一处跟在 FFN 输出之后。这样设计的直觉很直接：主干模型仍负责保留通用语言能力，而 Adapter 只学习<span style="background-color: #c0c0c0;">相对于原模型的任务特定偏移</span>。由于它是加法残差支路，模型一开始可以非常接近原始基座；随着训练推进，Adapter 再逐步学会把主干表示往当前任务需要的方向轻推一把。</p>
<p>这也是为什么 Adapter 常被初始化为近似恒等映射：例如让升维层初始非常小，使 <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Adapter}(h)\approx 0\)</span>。这样做的效果是，训练初期模型几乎等同于原始基座，不会因为新增模块而立刻破坏预训练表示；随后再通过反向传播逐步放大这条残差分支，让它承担领域偏移、标签边界重塑或任务路由修正。与“直接改写主干权重”相比，这种路径更稳定，也更容易控制灾难性遗忘。</p>
<p>从参数量角度看，单个 Adapter 的主要新增参数就是两次线性映射，大约是 <span displaypfx="inline-" class="mathjax-container">\(2dr\)</span> 量级，而不是全层的 <span displaypfx="inline-" class="mathjax-container">\(d\times d\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(d\times d_{\mathrm{ff}}\)</span> 量级。因此，只要瓶颈维度 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 足够小，就能在保持表达力的同时，把训练显存、优化器状态和存储成本压到很低。这一点与 LoRA 的“低秩更新”在精神上相近，但它们的结构并不相同：Adapter 是显式新增一条小型 MLP 支路，LoRA 则是在原线性层内部参数化一个低秩增量。</p>
<p>工业实践里，Adapter 通常不是只挂在单个层上，而是分布式地插入到所有 Transformer block 中，从而让每一层都具备任务适配能力。它的一个重要工程优势是“插拔式（Plug-and-play）”任务切换：同一个基座模型可以加载不同任务的 Adapter 包，在情感分析、NER、检索重排等任务之间快速切换，而不必为每个任务都保存一整份完整模型。这也是 Adapter 在多任务部署和组织内模型复用场景中一直很有吸引力的原因。</p>
<div class="blog_h3"><span class="graybg">LoRA</span></div>
<p>LoRA（Low-Rank Adaptation）不改动原权重 <span displaypfx="inline-" class="mathjax-container">\(W\)</span>，而是学习一个低秩增量 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[W' = W + \Delta W,\quad \Delta W = BA,\quad B\in\mathbb{R}^{d_{\text{out}}\times r},\ A\in\mathbb{R}^{r\times d_{\text{in}}},\ r\ll \min(d_{\text{in}},d_{\text{out}})\]</span>
<p>由于 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 很小，可训练参数与优化器状态显著减少。实践里常把 LoRA 加在注意力投影（如 <span displaypfx="inline-" class="mathjax-container">\(W_Q,W_K,W_V,W_O\)</span>）和/或 FFN 上。</p>
<p>截至 2026 年，LoRA 仍然是大语言模型参数高效微调里最常见的默认方案。原因并不神秘：它与主流 Transformer 线性层天然兼容，适配器权重体积小，便于为不同任务单独保存、热切换与合并；同时它对训练框架、推理框架和量化框架的兼容性也最成熟。因此，工程上常把 LoRA 看成 PEFT 的基线接口：后续很多方法，本质上都是在 LoRA 的参数化、量化方式或更新几何上继续细化。</p>
<div class="blog_h4"><span class="graybg">A、B 矩阵的区别</span></div>
<p>在上面的记号里， <span displaypfx="inline-" class="mathjax-container">\(A\in\mathbb{R}^{r\times d_{\text{in}}}\)</span> 负责把原始输入方向投影到一个 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 维低秩子空间， <span displaypfx="inline-" class="mathjax-container">\(B\in\mathbb{R}^{d_{\text{out}}\times r}\)</span> 再把这个低维表示映射回输出空间。因此，对输入向量 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 而言，LoRA 增量的作用顺序是：</p>
<span displaypfx="" class="mathjax-container">\[\Delta y=\Delta Wx=BAx\]</span>
<p>也就是说，先由 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 做“进低秩空间”的投影，再由 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 做“回原输出空间”的回投。直觉上， <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 更像在问“哪些组合方向值得被拿出来单独调”， <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 则更像在问“这些低维方向最终该怎样广播回原模型的输出通道”。</p>
<p>不同实现里，A、B 的命名和矩阵形状有时会看起来对调，这是因为有的库按数学乘法顺序命名，有的库按代码中参数张量的存储顺序命名。但概念并没有变：总有一个矩阵负责把高维输入压到低秩子空间，另一个矩阵负责把低秩更新再映射回原空间。只要抓住“先降到 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 维，再升回原维度”这一点，就不会被不同实现的符号差异干扰。</p>
<p>LoRA 的经典初始化也依赖这两个矩阵的不同角色。常见做法是让其中一个矩阵采用小随机初始化，而另一个矩阵初始化为 0；在当前这组记号下，更常见的叙述是让 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 随机初始化、 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 零初始化，于是训练开始时有：</p>
<span displaypfx="" class="mathjax-container">\[\Delta W = BA = 0\]</span>
<p>这样做有两个直接好处。第一，训练初始时模型输出与原基座完全一致，不会因为 LoRA 分支突然注入随机扰动而破坏预训练能力。第二，参数不会陷入完全对称的零状态：若两边都初始化为 0，梯度传播会受阻；若两边都随机初始化，训练一开始又会平白给模型加上不必要的噪声。采用“单边随机、单边为零”的非对称初始化，既保证了初始增量为零，又保留了可学习性，这是 LoRA 训练稳定性的关键细节之一。</p>
<p>从梯度角度看，这个设计也有非常直接的理由。若记损失对增量矩阵的梯度为 <span displaypfx="inline-" class="mathjax-container">\(G=\frac{\partial \mathcal{L}}{\partial \Delta W}\)</span>，则有：</p>
<span displaypfx="" class="mathjax-container">\[\frac{\partial \mathcal{L}}{\partial B}=GA^\top,\qquad \frac{\partial \mathcal{L}}{\partial A}=B^\top G\]</span>
<p>因此，当 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 随机、 <span displaypfx="inline-" class="mathjax-container">\(B=0\)</span> 时，训练开始的第一步通常会先更新 <span displaypfx="inline-" class="mathjax-container">\(B\)</span>，因为 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial B}=GA^\top\)</span> 一般不为 0；而 <span displaypfx="inline-" class="mathjax-container">\(\frac{\partial \mathcal{L}}{\partial A}=B^\top G=0\)</span>，所以 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 会在后续几步中随着 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 脱离零状态后再开始获得梯度。也正因为如此，LoRA 的非对称初始化并不会“学不起来”，它只是让学习过程以一种更平稳的方式启动。</p>
<div class="blog_h4"><span class="graybg">主要参数</span></div>
<p>LoRA 真正需要重点理解的超参数并不多，但每一个都在控制不同维度的权衡：容量、增量强度、挂载范围、正则化和训练稳定性。它们之间并不是简单的“越大越好”关系。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">参数</td>
<td style="text-align: center;">控制对象</td>
<td style="text-align: center;">理论含义与实践影响</td>
</tr>
</thead>
<tbody>
<tr>
<td>rank <span displaypfx="inline-" class="mathjax-container">\(r\)</span></td>
<td>低秩子空间维度</td>
<td>决定 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span> 最多能沿多少个独立方向修改原权重。 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 越小，参数越省、正则效应越强，但容量也越受限； <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 越大，表达力更强，却更容易抬高显存、训练成本与过拟合风险。</td>
</tr>
<tr>
<td><span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span></td>
<td>增量缩放强度</td>
<td>通常与 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 一起通过 <span displaypfx="inline-" class="mathjax-container">\(\frac{\alpha}{r}\)</span> 作用在 LoRA 分支上。它控制的是“已经学到的低秩方向到底以多大幅度影响原模型”，因此更接近幅值旋钮，而不是容量旋钮。调大 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 不能替代更高的 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>；它只能放大已有方向，而不会创造新方向。</td>
</tr>
<tr>
<td>target_modules</td>
<td>挂载位置</td>
<td>决定 LoRA 写入模型的哪一部分。只挂注意力投影时，参数最省、更偏行为与路由调整；把 MLP / FFN 一并纳入时，容量更强，也更适合知识关系与复杂边界适配，但训练更重。</td>
</tr>
<tr>
<td>lora_dropout</td>
<td>适配器正则化</td>
<td>主要用于抑制小数据或高重复语料上的过拟合。它并不改变 LoRA 的基本结构，而是在训练时降低低秩分支对局部样本模式的过度依赖。数据量很小时更有价值；数据充分且任务稳定时常保持较低甚至关闭。</td>
</tr>
<tr>
<td>学习率</td>
<td>优化步长</td>
<td>虽然学习率不是 LoRA 独有参数，但 LoRA 对学习率通常比全参数微调更敏感。因为可训练参数很少、每一步更新更集中，学习率过高时更容易直接把行为边界推歪；而 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 较大时，这种不稳定会被进一步放大。</td>
</tr>
</tbody>
</table>
<p>这几个参数里，最容易被混淆的是 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span>。 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 控制的是“允许模型沿多少个方向改”， <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 控制的是“这些方向的改动最终放大到多强”。前者对应容量，后者对应强度。增加 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 会改变可表达子空间本身；增加 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 只是把已存在的低秩更新放大。</p>
<p>从经验上看，LoRA 的调参顺序通常也应当遵循这个逻辑：先确定挂载哪些模块、需要多大 rank 才能容纳任务偏移，再去调节 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 与学习率，让训练稳定落在合适幅度上。若一开始就只靠提高 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 去追求效果，得到的往往不是更强的表达力，而是更剧烈的扰动。</p>
<p>LoRA 挂在哪些线性层上，并不是纯工程细节，而与希望改变模型的哪一部分能力直接相关。若目标更偏风格迁移、格式控制、对话行为调整或路由方式改变，注意力投影层上的 LoRA 往往已经能带来明显效果；但若目标是注入新的领域术语、事实关联、实体属性映射或专业概念之间的稳定关系，FFN / MLP 往往更关键。原因在于：Transformer 里的 MLP 常被视为知识写入与模式重编码的重要位置，因此很多“新知识”最终要落到这些大规模前馈权重所张成的表示子空间里。</p>
<p>这也是为什么在不少实践中，LoRA 不只挂在 <span displaypfx="inline-" class="mathjax-container">\(W_Q,W_K,W_V,W_O\)</span> 上，还会同时挂在 FFN 的线性层上，甚至在资源允许时为 FFN 分配更高的秩（Rank）。低秩更新的本质是在原有权重空间附近增加一个受限的可训练子空间；如果希望修改的是知识关联本身，而不仅是信息流动方式，那么只改注意力层常常不够，需要让 MLP / FFN 也获得足够的适配容量。沿着这条思路发展的变体，如 DoRA（Weight-Decomposed Low-Rank Adaptation），本质上也是在不做全参数微调的前提下，给参数更新更强的表达能力。</p>
<p>这里的低秩假设作用于微调增量 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span>，而不是作用于预训练知识本身的存储方式。预训练模型中的知识通常以分布式表示（Distributed Representation）的方式编码在大量参数 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 里；LoRA 近似的是“为了适配当前任务，需要沿哪些方向改动这些参数”。前者讨论的是 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 如何承载知识，后者讨论的是 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span> 如何改变模型行为与输出边界。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/lora.png"><img class="alignnone size-full wp-image-41973" src="https://blog.gmem.cc/wp-content/uploads/2026/03/lora.png" alt="lora" width="1024" height="1024" /></a></p>
<p>很多微调任务并不要求模型写入大规模新知识，而是要求它重新组织已有表示：调整回答风格、强化指令跟随、遵守输出格式、放大某些线索、抑制另一些线索。这类任务的有效更新方向往往集中在少数子空间中，小秩 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 就足以带来明显收益。若任务要求模型稳定吸纳新的领域本体（Ontology）、术语体系、事实关系或复杂规则，适配增量的有效维度通常会上升，低秩近似就更容易成为容量瓶颈。</p>
<p>分布式存储也不意味着所有参数同等重要。即便在全参数微调（Full Fine-tuning）中，显著变化的往往也是少数关键方向；LoRA 的核心假设是，这些方向可以被一个较低维的子空间有效覆盖。当任务迁移幅度较小，这个假设通常成立；当关键更新分散在许多彼此独立的方向上，就需要更高的秩、更广的挂载范围，尤其是让 FFN / MLP 参与适配，必要时再转向 DoRA 或全参数微调。工程上，这体现为一条清晰的权衡：LoRA 优先优化效率，全参数微调提供更高上限；二者对应的是不同任务内在维度（Intrinsic Dimension）下的不同最优解。</p>
<div class="blog_h4"><span class="graybg">LoRA 合并</span></div>
<p>LoRA 的一个工程优势是“可合并（Mergeable）”：推理前可把增量权重并入基座权重，从而不引入额外前向分支。对单个 LoRA，合并后得到的有效权重就是 <span displaypfx="inline-" class="mathjax-container">\(W_{\text{merged}}=W+\Delta W\)</span>（实践中常包含缩放系数）。</p>
<p>当存在多份 LoRA（多任务/多领域适配）时，最简单的合并是对增量做加权和：</p>
<span displaypfx="" class="mathjax-container">\[W' = W + \sum_{j=1}^{M}\lambda_j\,\Delta W^{(j)}\]</span>
<p>这种“线性缝合”实现简单，但容易出现任务干扰（Interference）：不同 LoRA 在同一参数子空间里叠加，可能让模型对多个任务都变差。若你需要同时服务多个领域，更稳健的方案往往是“运行时选择哪一份 LoRA”或引入路由（Routing）机制，而不是把它们永久混成一份权重。</p>
<div class="blog_h4"><span class="graybg">LoRA 缩放</span></div>
<p>LoRA 的低秩增量在工程实现里通常写成带缩放的形式，而不是直接写成 <span displaypfx="inline-" class="mathjax-container">\(\Delta W=BA\)</span>：</p>
<span displaypfx="" class="mathjax-container">\[\Delta W=\frac{\alpha}{r}BA\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 是秩（Rank），决定低秩子空间的维度；<span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 是缩放系数（Scaling Factor），决定这条增量支路最终以多大强度作用于原权重。把 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 放在一起，不是书写习惯，而是为了在改变 rank 时尽量保持更新量的数值尺度处于可控范围。若只增大 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 而不做归一化，低秩分支的整体幅度往往也会随之增大，使不同配置之间难以直接比较。</p>
<p>因此， <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 控制的是“LoRA 支路有多强”， <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 控制的是“LoRA 支路能沿多少个方向改动权重”。前者更接近幅值控制，后者更接近容量控制。单纯调大 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span>，只是放大既有低秩方向的影响；单纯调大 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>，则是在扩大可表达的更新子空间。</p>
<p>缩放在合并时同样不会消失。真正并回基座的不是裸 <span displaypfx="inline-" class="mathjax-container">\(BA\)</span>，而是已经乘上系数后的有效增量，因此合并后的权重应写成：</p>
<span displaypfx="" class="mathjax-container">\[W_{\text{merged}}=W+\frac{\alpha}{r}BA\]</span>
<p>工程上，较小数据集与较轻任务迁移通常更适合温和缩放，因为此时更重要的是在保留基座先验的同时做局部修正；当任务迁移更深、希望 LoRA 更积极地重写行为边界或知识关联时，才会提高 <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 或放宽 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>。它本质上是在调节“基座保守性”与“任务增量强度”之间的平衡。</p>
<div class="blog_h3"><span class="graybg">DoRA</span></div>
<p>DoRA（Weight-Decomposed Low-Rank Adaptation）的核心不是把 LoRA 的两个低秩矩阵 <span displaypfx="inline-" class="mathjax-container">\(A\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(B\)</span> 再拆一层，而是先把原始权重 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 按“幅值（Magnitude）+ 方向（Direction）”重写，再只让低秩更新作用在方向部分。对第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 个输出通道，也就是权重矩阵的第 <span displaypfx="inline-" class="mathjax-container">\(j\)</span> 列，可先写成：</p>
<span displaypfx="" class="mathjax-container">\[W_{:,j}=\left\|W_{:,j}\right\|_2\cdot \frac{W_{:,j}}{\left\|W_{:,j}\right\|_2}=m_j\,\hat v_j,\quad \left\|\hat v_j\right\|_2=1\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\frac{W_{:,j}}{\left\|W_{:,j}\right\|_2}\)</span> 就是“把一个向量除以自己的 <span displaypfx="inline-" class="mathjax-container">\(\ell_2\)</span> 范数（L2 Norm）”。这样得到的新向量长度恰好等于 1，因此它不再携带原来的大小信息，只保留方向信息，也就是单位方向向量（Unit Direction Vector）。</p>
<p>这里 <span displaypfx="inline-" class="mathjax-container">\(m_j\)</span> 是一个标量，表示这一列权重的整体长度； <span displaypfx="inline-" class="mathjax-container">\(\hat v_j\)</span> 是单位向量，表示这一列“指向哪里”。这一步只是重参数化（Reparameterization）：没有改动原模型，只是把每一列从“一个普通向量”改写成“长度 × 单位方向”。</p>
<p>DoRA 的更新写法通常记为：</p>
<span displaypfx="" class="mathjax-container">\[W'_{:,j}=m'_j\frac{V_{:,j}+\Delta V_{:,j}}{\left\|V_{:,j}+\Delta V_{:,j}\right\|_2},\quad \Delta V=BA\]</span>
<p>理解这条式子的关键，在于分清谁在控制方向，谁在控制大小。分式里的 <span displaypfx="inline-" class="mathjax-container">\(V_{:,j}+\Delta V_{:,j}\)</span> 先经过 <span displaypfx="inline-" class="mathjax-container">\(\ell_2\)</span> 归一化，因此无论 <span displaypfx="inline-" class="mathjax-container">\(\Delta V\)</span> 本身把这个向量拉长还是压短，归一化之后保留下来的都只有<span style="background-color: #c0c0c0;">方向信息</span>。换句话说，LoRA 产生的低秩增量 <span displaypfx="inline-" class="mathjax-container">\(BA\)</span> 在这里主要决定“这一列朝哪个方向偏转”，而不会直接把这一列的范数放大或缩小，因为范数已经被分母除掉了。</p>
<p>真正决定输出通道大小的是前面的标量 <span displaypfx="inline-" class="mathjax-container">\(m'_j\)</span>。如果把 <span displaypfx="inline-" class="mathjax-container">\(m'_j\)</span> 固定住，那么更新后的列向量范数始终满足 <span displaypfx="inline-" class="mathjax-container">\(\left\|W'_{:,j}\right\|_2=m'_j\)</span>，此时低秩更新确实只在改方向、不改大小；如果把 <span displaypfx="inline-" class="mathjax-container">\(m'_j\)</span> 设为可学习参数，那么 DoRA 就是在<span style="background-color: #c0c0c0;">两个通道里分别学习</span>：低秩分支 <span displaypfx="inline-" class="mathjax-container">\(\Delta V\)</span> 负责方向修正，标量 <span displaypfx="inline-" class="mathjax-container">\(m'_j\)</span> 负责幅值修正。无论是哪一种，方向与大小都不再像原始 LoRA 那样纠缠在同一个增量矩阵里。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/dora.png"><img class="alignnone size-full wp-image-41985" src="https://blog.gmem.cc/wp-content/uploads/2026/03/dora.png" alt="dora" width="1024" height="1024" /></a></p>
<p>这也是 DoRA 比原始 LoRA 更接近全参数微调的原因之一。普通 LoRA 直接对 <span displaypfx="inline-" class="mathjax-container">\(W\)</span> 加一个低秩增量 <span displaypfx="inline-" class="mathjax-container">\(\Delta W\)</span>，因此“方向变化”和“范数变化”混在同一个更新里；DoRA 则把这两件事显式拆开，使优化器可以分别决定“该往哪里转”与“该放大多少”。当任务需要更深地改写领域知识、重塑复杂判别边界或吸收更稳定的专业概念关系时，这种解耦往往更有表达力；代价则是额外的参数、归一化计算与实现复杂度。</p>
<div class="blog_h3"><span class="graybg">QLoRA</span></div>
<p>QLoRA 在 LoRA 基础上进一步把基座权重量化（Quantize）到低比特（常见 4-bit），以极小显存加载大模型；训练时仍只更新 LoRA 参数。它把“能不能放得下”从硬约束变成可控工程问题，是许多个人/小团队微调 7B/13B 的关键技术路径之一。</p>
<p>其核心思路是：冻结量化后的基座权重，只在前向/反向计算时对它们做解量化（Dequantize），而真正需要学习的仍是低秩增量。一个简化写法是：</p>
<span displaypfx="" class="mathjax-container">\[Y=X\,\mathrm{Dequant}(W_q)+\frac{\alpha}{r}XBA,\quad W_q=\mathrm{Quant}(W)\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(X\)</span> 是输入激活， <span displaypfx="inline-" class="mathjax-container">\(W_q\)</span> 是量化后冻结的基座权重， <span displaypfx="inline-" class="mathjax-container">\(\mathrm{Dequant}(W_q)\)</span> 表示把低比特权重恢复到计算精度后的近似值， <span displaypfx="inline-" class="mathjax-container">\(\frac{\alpha}{r}BA\)</span> 是 LoRA 分支， <span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span> 是缩放系数（Scaling Factor），用来控制低秩增量对原模型的影响强度。</p>
<p>这里的“解量化”不是把模型永久还原回全精度权重再训练，而是指：权重在显存或存储中仍以低比特形式保存，只是在一次具体矩阵乘法发生时，临时把对应块恢复成计算所需的近似浮点值，再与输入激活相乘。也就是说，量化主要解决的是<span style="background-color: #c0c0c0;">存储与显存占用</span>，而解量化解决的是<span style="background-color: #c0c0c0;">如何让这些低比特权重仍然参与正常线性计算</span>。</p>
<p>因此，所谓“解量化计算路径”指的是：训练框架是否知道如何从低比特权重 <span displaypfx="inline-" class="mathjax-container">\(W_q\)</span> 出发，在前向过程中正确恢复近似浮点表示、完成矩阵乘法，并在反向传播时把梯度只传给 LoRA 分支而不是错误地写回量化权重本体。若这条路径存在，那么量化基座虽然被冻结，仍然可以作为可计算的主干参与训练；若这条路径不存在，量化权重就只是某种压缩后的静态文件，能用于推理加载，却不能自然地嵌入 QLoRA 的训练图中。</p>
<p>这里的前提不是“训练者必须先拿到一份原始全精度基座，再亲手把它量化”，而是必须拥有一份<span style="background-color: #c0c0c0;">训练兼容的冻结量化基座</span>。如果基座本身已经是可被训练框架直接加载、解量化并挂接 PEFT 的 4-bit / 8-bit 版本，那么它完全可以直接作为 QLoRA 起点；但如果它只是面向推理部署的静态量化模型，例如某些只强调推理速度或离线压缩格式的 checkpoint，那么它往往并不适合作为 QLoRA 的训练底座。决定因素不在于“是不是量化过”，而在于这种量化形式是否仍然保留了训练时所需的解量化计算路径与 PEFT 兼容性。</p>
<p>QLoRA 的关键不只是“4-bit”，而是分块量化（Block-wise Quantization）：权重不会用一套全局刻度统一压缩，而是被划分成许多小块，每块各自保存缩放因子。若第 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 个块的量化码为 <span displaypfx="inline-" class="mathjax-container">\(\hat{w}^{(g)}\)</span>，对应缩放因子为 <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span>，则可抽象写成：</p>
<span displaypfx="" class="mathjax-container">\[w^{(g)}\approx s_g\,\hat{w}^{(g)}\]</span>
<p>这个“分块”通常不是按语义结构切分，而是按固定块大小（例如若干连续权重为一组）直接切开。原因很简单：连续切块最容易实现，也最适合 GPU 并行。对每一个块，系统会单独估计一个比例尺 <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span>，再用这个块自己的尺度去压缩和还原权重。于是更完整的写法通常是：</p>
<span displaypfx="" class="mathjax-container">\[w^{(g)}\approx s_g\cdot Q\!\left(\frac{w^{(g)}}{s_g}\right)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(Q(\cdot)\)</span> 表示把归一化后的数值映射到低比特量化码。也就是说，比例尺 <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span> 的作用是先把第 <span displaypfx="inline-" class="mathjax-container">\(g\)</span> 个块拉到一个统一的局部数值范围，再交给 4-bit 码本处理；反量化时再乘回 <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span>。在最朴素的实现里， <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span> 可以由该块的最大绝对值、均方根，或其他稳健统计量导出，本质都是在问同一个问题：<span style="background-color: #c0c0c0;">这一小块权重大致处在什么量级上</span>。</p>
<p>一个极小的数值例子能说明为什么需要逐块比例尺。假设某一块权重大致落在 <span displaypfx="inline-" class="mathjax-container">\([-0.1,0.1]\)</span>，另一块却落在 <span displaypfx="inline-" class="mathjax-container">\([-3,3]\)</span>。如果整个张量只共享一个全局比例尺，那么为了容纳大块的幅度，小块里的许多细微差异都会被压扁，量化后落到相同的低比特值；而若分别为这两块设置 <span displaypfx="inline-" class="mathjax-container">\(s_1\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\(s_2\)</span>，两块都能在自己的局部范围里充分利用有限的 4-bit 表示能力。也正因为如此，分块量化比全局共用一个缩放因子保留了更多有效信息，尤其更能缓解离群值（Outlier）对整体刻度的污染。</p>
<p>NF4（Normalized Float 4）进一步改进的不是“是否分块”，而是块内映射到哪一套 4-bit 码本。普通均匀量化更像是把一个区间机械地分成若干等宽小段；NF4 则利用很多 Transformer 权重在局部块内常呈现零中心、近似正态分布的事实，预先设计一套更贴近这种分布的离散代表值。于是块内每个权重更接近写成：</p>
<span displaypfx="" class="mathjax-container">\[w_i^{(g)}\approx s_g\cdot c_{q_i}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(c_{q_i}\)</span> 是 NF4 码本中的代表值， <span displaypfx="inline-" class="mathjax-container">\(q_i\)</span> 是对应的 4-bit 索引。它不是假设所有权重必须严格落在同一个固定区间内，而是先按块做尺度归一化，再用更贴近正态权重分布的码本去逼近这些局部值。</p>
<p>双重量化（Double Quantization）与分页优化器（Paged Optimizer）则是在这套分块量化之上继续做工程压缩。前者的思路是：每个块都要保存自己的 <span displaypfx="inline-" class="mathjax-container">\(s_g\)</span> 或相关元数据，这些量的数量虽然远少于权重数，但它们的精度通常更高，而且每个因子只服务一个较小的权重块，因此把这部分成本均摊回“每个参数”之后，并不总能忽略。举例说，若一个缩放因子用 32 bit 保存、对应一个 64 权重的块，那么仅缩放因子这一项就相当于给每个参数额外分摊了 <span displaypfx="inline-" class="mathjax-container">\(32/64=0.5\)</span> bit；在 4-bit 权重场景里，这已经不是一个可以随手忽略的附加成本。双重量化做的，就是把这些缩放因子或相关元数据再压一层，继续降低这笔“元数据税”。后者则借鉴操作系统的分页思想，把优化器状态按页在 GPU 与 CPU 内存之间调度，从而避免 Adam 一类优化器把峰值显存推得过高。它们都不改变 LoRA 的学习目标，也不改变 QLoRA 的基本数学形式，而是在工程层面继续压低“把大模型微调跑起来”的资源门槛。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/qlora.png"><img class="alignnone size-full wp-image-42007" src="https://blog.gmem.cc/wp-content/uploads/2026/03/qlora.png" alt="qlora" width="1024" height="1024" /></a></p>
<div class="blog_h3"><span class="graybg">Q-DoRA</span></div>
<p>Q-DoRA 可以看作 QLoRA 与 DoRA 的组合：基座仍采用低比特量化以节省显存，但更新形式不再是普通 LoRA，而是“量化基座 + 方向/尺度解耦”的 DoRA 结构。一个简化表达是：</p>
<span displaypfx="" class="mathjax-container">\[W'_{:,j}=m_j\frac{\mathrm{Dequant}(W_{q,:,j})+\Delta V_{:,j}}{\left\|\mathrm{Dequant}(W_{q,:,j})+\Delta V_{:,j}\right\|_2},\quad \Delta V=BA\]</span>
<p>它的工程含义很直接：用 QLoRA 解决“显存放不下”的问题，用 DoRA 缓解“低秩更新表达力不够”的问题。若资源非常紧、任务主要是格式控制与轻量指令对齐，普通 QLoRA 往往已经足够；若任务更偏逻辑推理增强、专业知识注入、复杂边界判别或高质量垂直领域适配，Q-DoRA 往往是更稳妥的折中。对应代价是训练更慢、实现更复杂，且并非所有推理栈都像原始 LoRA 那样原生支持。</p>
<div class="blog_h3"><span class="graybg">LoRA-MoE</span></div>
<p>LoRA-MoE 可以理解为“适配器级的 MoE”：不把 FFN 变成稀疏专家，而是保留基座不变，准备多份 LoRA 作为“领域专家”，再用一个路由器（Router）按请求/句子/甚至 token 选择或加权组合这些 LoRA。直觉上，它用极小的可训练参数，为同一个基座提供多域能力，同时避免把所有任务硬合并到一份权重里。</p>
<p>一种抽象表达是把输出写成“基座 + 适配器混合”：</p>
<span displaypfx="" class="mathjax-container">\[h' = f_{\text{base}}(h) + \sum_{e\in \mathcal{E}} g_e(x)\,f_{\text{lora},e}(h),\quad \sum_e g_e(x)=1\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(g_e(x)\)</span> 是路由权重，可以来自显式分类器（域识别）、检索到的任务标签，或一个可训练的 gating 网络。工程上，LoRA-MoE 的关键不在公式，而在路由与评测：你需要定义“什么输入该走哪套 LoRA”，并防止路由错误导致质量抖动。</p>
<p>截至 2026 年，LoRA-MoE 的实际地位更接近<span style="background-color: #c0c0c0;">高级可选架构</span>，而不是参数高效微调里的默认主流基线。它已经形成了一条持续演进的方法线，说明“多 LoRA + 路由”并非概念玩具；但在更常见的工业部署里，成熟默认方案仍然往往是“单基座 + 多个独立 LoRA 适配器”，按请求或租户切换，而不是把路由器永久并入模型主干。原因并不神秘：LoRA-MoE 除了要训练适配器本身，还要额外处理路由质量、专家利用不均、冷专家几乎不被激活、线上可观测性以及请求分布变化带来的稳定性问题。只有当任务确实需要<span style="background-color: #c0c0c0;">在同一个运行图里动态融合多域能力</span>，而不是简单地在不同 LoRA 之间切换时，LoRA-MoE 的额外复杂度才更值得支付。</p>
<div class="blog_h3"><span class="graybg">多 LoRA 热切换与共享基座</span></div>
<p>比 LoRA-MoE 更常见、也更容易落地的方案，是<span style="background-color: #c0c0c0;">多个独立 LoRA 共享同一个基座模型</span>。这里的共享不是把多份 LoRA 合并成一套永久权重，而是让基座参数在 GPU 中只保留一份；不同请求到来时，再按请求绑定对应的适配器。这样做的直接收益是：显存里最重的那部分参数不需要为每个任务重复存一遍，而任务差异主要体现在额外加载的轻量增量权重上。</p>
<p>从推理执行角度看，这种“热切换”并不意味着每来一个请求就重新加载整个模型。更常见的做法是：基座常驻显存，LoRA 适配器按需驻留在 GPU 或 CPU 侧缓存中；请求只需声明“当前使用哪一份适配器”，调度器就会在对应层上把这一份 LoRA 增量接入当前 forward。若某个适配器近期很少被访问，它可以被换出；当请求再次到来时，再从本地盘、对象存储或 Hub 拉回。于是系统真正管理的是<span style="background-color: #c0c0c0;">适配器缓存与调度</span>，而不是整模型重载。</p>
<p>这条路线之所以在 2026 年更主流，是因为它把“多域能力”问题拆成了两个更容易控制的子问题：第一，训练阶段各自产出独立 LoRA，任务之间天然隔离；第二，推理阶段只做选择和缓存，不必额外训练路由器，也不会把多个领域永久混到同一组权重里。代价主要落在工程侧：适配器的 rank、目标模块集合、张量并行配置与基座版本必须兼容；同时服务系统还要决定 GPU 能同时保留多少份 LoRA、超出容量时按什么策略驱逐，以及批内是否允许不同请求混用不同适配器。</p>
<p>截至 2026 年，这已经不是纸面方案，而是主流高吞吐推理框架的标准能力之一。vLLM 支持按请求选择 LoRA，既可以在服务启动时预注册，也支持通过运行时 API 与解析插件动态加载；SGLang 支持同一批次中的不同序列绑定不同 LoRA，并提供适配器加载、驱逐、后端 kernel 与批内 LoRA 数量控制；Hugging Face TGI 也支持在启动时加载多份 LoRA 并在请求中指定 adapter；TensorRT-LLM 则已经提供多 LoRA 推理示例与运行时请求绑定接口。换句话说，<span style="background-color: #c0c0c0;">多 LoRA 共享基座</span>在今天更像是一种成熟的服务形态，而不是实验性质的技巧。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">方案</td>
<td style="text-align: center;">参数/存储</td>
<td style="text-align: center;">推理开销</td>
<td style="text-align: center;">多域能力</td>
<td style="text-align: center;">主要风险</td>
</tr>
</thead>
<tbody>
<tr>
<td>多 LoRA 合并</td>
<td>单份权重</td>
<td>最低（一次 forward）</td>
<td>不稳定（易相互干扰）</td>
<td>合并策略难；回滚困难</td>
</tr>
<tr>
<td>LoRA-MoE（路由）</td>
<td>多份 LoRA + 路由器</td>
<td>低~中（取决于是否多专家叠加）</td>
<td>强（可按域选择）</td>
<td>路由错误；线上一致性与可观测性要求更高</td>
</tr>
<tr>
<td>全量 MoE（FFN 专家）</td>
<td>多专家权重</td>
<td>中（Top-k 专家计算）</td>
<td>强（容量大）</td>
<td>训练与部署复杂；负载均衡与稳定性</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">基于 Prompt 的微调</span></div>
<p>与 LoRA、Adapter 这类“直接改模型内部参数化”的路线不同，基于 Prompt 的微调把任务适配写在输入条件上。它的核心不是重写主干权重，而是构造一小段能够引导模型行为的任务条件，让模型在保持基座冻结的前提下，沿着这段条件生成更符合目标任务的输出。</p>
<p>这里需要先把两类 Prompt 区分开。硬提示（Hard Prompt）是人工编写的离散文本提示，本质上属于提示工程（Prompt Engineering），而不是参数高效微调；软提示（Soft Prompt）则是一组可训练的连续向量，通常可以看成“不对应真实词表 token 的虚拟 token embedding”。前者没有训练参数，可解释性强但搜索空间受限；后者进入连续空间后更容易通过梯度优化找到有效解，因此才构成 Prompt Tuning、Prefix Tuning、P-Tuning、P-Tuning v2 这一路软提示微调家族。</p>
<p>从机制上看，软提示路线的共同点是：人为构造或学习一小段任务向量，把它们拼接到原始输入或注意力状态中，再让这些额外向量参与模型的注意力计算，从而影响后续真实 token 的生成和判别。它的工程优势非常明确：主干参数无需为每个任务复制一份，多任务场景下只需切换不同的 Prompt 参数即可。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">路线</td>
<td style="text-align: center;">作用位置</td>
<td style="text-align: center;">可训练参数量</td>
<td style="text-align: center;">主要优点</td>
<td style="text-align: center;">主要边界</td>
</tr>
</thead>
<tbody>
<tr>
<td>硬提示</td>
<td>输入文本</td>
<td>0</td>
<td>可读、可解释、适合快速验证</td>
<td>离散搜索困难，效果上限受人工设计限制</td>
</tr>
<tr>
<td>Prompt Tuning</td>
<td>输入层</td>
<td>极少</td>
<td>最轻、最易多任务切换</td>
<td>只影响输入端，表达力最弱</td>
</tr>
<tr>
<td>Prefix Tuning</td>
<td>各层注意力</td>
<td>很少</td>
<td>比纯输入层软提示更强，能在每层引导注意力</td>
<td>实现更复杂，与模型结构耦合更深</td>
</tr>
<tr>
<td>LoRA / QLoRA</td>
<td>模型内部线性层</td>
<td>较少</td>
<td>效果更稳、更通用</td>
<td>需要改写模型参数化与训练图</td>
</tr>
</tbody>
</table>
<p>因此，硬提示、软提示与 LoRA 的差异，并不只是“参数多少”，而是任务条件被写入模型的层次不同。硬提示只改自然语言输入；Prompt Tuning 把任务条件写进输入嵌入；Prefix Tuning 把任务条件送进每层注意力；LoRA 则直接改写模型内部线性映射的参数化。条件写得越深，通常表达力越强，但实现和系统复杂度也越高。</p>
<div class="blog_h3"><span class="graybg">Prefix Tuning</span></div>
<p>Prefix Tuning 属于软提示类 PEFT。它学习的不是自然语言前缀文本，而是一组连续可训练向量（Continuous Prefix），并把这组向量作为每一层注意力里的额外 Key / Value 注入。若某层原本的注意力键值对为 <span displaypfx="inline-" class="mathjax-container">\(K,V\)</span>，则 Prefix Tuning 可以理解为把它们扩展成 <span displaypfx="inline-" class="mathjax-container">\([K_{\text{prefix}};K]\)</span> 与 <span displaypfx="inline-" class="mathjax-container">\([V_{\text{prefix}};V]\)</span>，让后续 token 在每一层都能访问这段任务特定“前缀记忆”。</p>
<p>它的关键不只是“在输入前加几个向量”，而是<span style="background-color: #c0c0c0;">在所有层分别注入前缀状态</span>。不同层的前缀通常并不共享；每一层都有自己的 prefix 参数，因为浅层和深层承担的表示功能并不相同。于是，Prefix Tuning 更像是在每一层都额外挂上一小段可学习上下文，让模型在整条前向路径中持续感知任务条件，而不是只在输入口看一眼提示后就完全交给主干自行传播。</p>
<p>若前缀长度记为 <span displaypfx="inline-" class="mathjax-container">\(m\)</span>、模型隐藏维度记为 <span displaypfx="inline-" class="mathjax-container">\(d\)</span>、层数记为 <span displaypfx="inline-" class="mathjax-container">\(L\)</span>，那么最粗略的参数量量级可以理解为 <span displaypfx="inline-" class="mathjax-container">\(O(Lmd)\)</span>。这也是它为什么通常比全量微调和 LoRA 更轻，但又明显重于单纯输入层 Prompt Tuning：它的参数量来自“每层各有一小段前缀”，而不是只在输入层保存一组虚拟 token。</p>
<div class="blog_h4"><span class="graybg">训练稳定性与重参数化</span></div>
<p>直接把前缀向量当作自由参数去优化，并不总是最稳定。因为这些向量一开始就要进入每层注意力，如果初始化过于随意，训练前期很容易让注意力分布出现较大抖动。为此，Prefix Tuning 的经典实现常引入一层小型重参数化网络，例如用一个 MLP 先把较低维或更结构化的中间表示映射成真正送入各层的 prefix Key / Value。</p>
<p>这种做法的本质不是给 Prefix Tuning 增加永久推理负担，而是把训练阶段的优化空间改造成更平滑、更容易收敛的形式。训练完成后，这个 MLP 生成出的前缀状态通常可以被直接缓存或固化，推理时未必需要继续保留完整重参数化模块。因此，它更像一种<span style="background-color: #c0c0c0;">训练期稳定化技巧</span>，而不是 Prefix Tuning 必须背负的长期结构成本。</p>
<div class="blog_h4"><span class="graybg">适用性评估</span></div>
<p>它与 Prompt Tuning 的差别不在于“前缀长短”，而在于注入位置。Prompt Tuning 只在输入嵌入层增加一小段软提示；Prefix Tuning 则把任务参数直接送进每层注意力，因此它通常更有表达力，也更接近“在每层引导模型如何读写上下文”。代价是实现更复杂，模型结构耦合更深，训练与推理栈也更需要原生支持。</p>
<p>到 2026 年，Prefix Tuning 仍然是成立且标准的 PEFT 方法，但它在主流大语言模型指令微调里的存在感已经明显弱于 LoRA。它最有价值的场景通常是：希望极小参数量地控制条件生成行为、使用 Encoder-Decoder 或较经典的条件生成架构，或者研究上需要把“任务条件”明确写进每层注意力。若任务是当代 Decoder-only LLM 的通用指令对齐、风格迁移或领域适配，LoRA / QLoRA 往往仍是默认起点：更稳、更通用、推理框架支持也更成熟。</p>
<div class="blog_h3"><span class="graybg">Prompt Tuning</span></div>
<p>Prompt Tuning（软提示/Soft Prompt）同样属于 PEFT，但它比 Prefix Tuning 更轻：只在输入嵌入层前面拼接一小段可训练“虚拟 token embedding”，而不改动 Transformer 内部层的参数结构。设输入嵌入序列为 <span displaypfx="inline-" class="mathjax-container">\(E(x)\)</span>，软提示为 <span displaypfx="inline-" class="mathjax-container">\(P\in\mathbb{R}^{m\times d}\)</span>，则模型实际看到的是拼接后的序列 <span displaypfx="inline-" class="mathjax-container">\([P;E(x)]\)</span>。训练时更新的只有 <span displaypfx="inline-" class="mathjax-container">\(P\)</span>，基座参数保持冻结。</p>
<p>它的核心假设是：对于某些任务，模型原本的能力已经足够，真正缺少的只是一个足够好的“任务启动条件”。如果这组输入层虚拟 token 能把模型推到合适的工作点，后面的冻结主干就能沿着原有能力完成任务。因此，Prompt Tuning 在参数量上往往可以做到比 LoRA 还小一个量级，尤其适合“大量轻任务共享同一基座”的场景。</p>
<div class="blog_h4"><span class="graybg">与 Prefix Tuning 的区别</span></div>
<p>Prompt Tuning 与 Prefix Tuning 的根本区别，不在于二者都用了虚拟 token，而在于任务条件写入的深度不同。Prompt Tuning 只在输入层插入软提示，后续所有层看到的都是这段输入在主干网络中自然传播后的结果；Prefix Tuning 则直接在每一层注意力中附加前缀状态，使任务条件持续存在于整条注意力链路中。前者最轻，后者更强。</p>
<p>也正因为如此，Prompt Tuning 在超大模型上有时会随着基座规模增大而变得更有效，因为大模型本身已经足够强，输入端的一点点软条件就足以触发所需能力；而在中小模型或需要强行为控制的任务上，它往往不如 Prefix Tuning、LoRA 稳定。</p>
<div class="blog_h4"><span class="graybg">家族扩展与适用性</span></div>
<p>围绕这一路线还发展出 P-Tuning、P-Tuning v2 等变体。它们的共同目标，都是让软提示不只停留在“输入前拼一小段向量”这么简单，而是通过更强的参数化或更深层的注入方式，提高在理解类任务和较小模型上的表现。若把家族关系压缩来看：Prompt Tuning 是最轻的输入层软提示；Prefix Tuning 把软提示推进到各层注意力；P-Tuning / P-Tuning v2 则在“如何生成这些提示、提示该注入多深”上继续增强。</p>
<p>到 2026 年，Prompt Tuning 仍然实用，但更像<span style="background-color: #c0c0c0;">轻量特化选项</span>而不是主流默认路线：当目标是极小参数、海量任务复用、低存储部署或 prompt-adapter 风格服务时，它仍有现实价值；当目标是指令遵循、复杂格式约束、长对话行为修正或稳定领域适配时，LoRA / QLoRA 往往更稳妥，Prefix Tuning 也通常比纯输入层软提示更有表达力。</p>
<div class="blog_h2"><span class="graybg">微调技术选型</span></div>
<p>前面列出的全量微调、部分参数微调、Prompt 系软提示、Adapter、LoRA、QLoRA 并不是互相替代的“流行名词清单”，而是针对不同约束条件的不同最优解。真正决定选型的，通常不是单一维度上的“效果最好”，而是四个问题同时成立时的交集：样本量够不够、GPU 预算有多紧、任务到底是在改行为还是改知识、上线时更看重单任务极致效果还是多任务复用与切换。</p>
<p>样本量是第一道分界线。数据很少时，更应优先考虑冻结主干参数的路线，例如 Prompt Tuning、Prefix Tuning、LoRA、QLoRA 或更轻的部分参数微调。原因并不只是“省显存”，而是冻结主干更容易保留预训练先验，降低小数据把模型硬拉向局部模式的风险。数据足够大、分布足够稳定、目标能力又确实需要深度改写时，全量微调才更值得支付它的高成本，因为只有在这种条件下，放开全部参数带来的表达上限才真正有机会被利用。</p>
<p>GPU 资源是第二道分界线。若显存非常紧，QLoRA 往往是生成模型微调的现实起点；若任务更轻、希望一个基座承载大量小任务，Prompt Tuning 或 Prefix Tuning 的存储优势会更突出；若 GPU 充裕且追求最强任务特化，上限仍然在全量微调一侧。换句话说，量化 LoRA 解决的是“放不放得下”，LoRA 解决的是“如何低成本改行为”，而全量微调解决的是“是否要把整套模型一起重写”。</p>
<p>任务性质决定第三道分界线。若变化主要发生在输入接口，例如新增专有 token、符号或特殊控制标记，输入层微调与软提示通常比全模型更新更自然；若变化主要体现在最终读出或评分方式，输出层微调往往就够；若目标是稳定调整指令遵循、格式约束、风格边界、多轮行为或一般性领域适配，LoRA / QLoRA 通常是默认解；若真正需要吸收大量新知识、重构深层表示、改变词表、上下文长度或位置编码等底层设定，则继续预训练或全量微调才更匹配问题本质。</p>
<p>推理形态决定第四道分界线。多任务在线服务最看重“一个基座 + 多个轻量增量”时，LoRA 及其热切换形态通常最实用；Prompt Tuning 与 Prefix Tuning 也具备同样的任务切换优势，只是主流推理框架与工业实践对 LoRA 的支持更成熟。Adapter 虽然同样具备插拔式优点，但它会在前向路径里保留额外计算分支，因此在当代大模型场景里通常不再是默认首选。若目标是最低推理延迟，合并后的 LoRA 与单体全量微调模型通常更占优；若目标是海量任务共享一个基座、频繁热切换，则运行时加载轻量适配器更灵活。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">微调技术</td>
<td style="text-align: center;">何时优先考虑</td>
<td style="text-align: center;">主要优点</td>
<td style="text-align: center;">主要代价或边界</td>
</tr>
</thead>
<tbody>
<tr>
<td>全量微调</td>
<td>GPU 资源充足、样本量充足、任务需要深度改写模型能力</td>
<td>表达上限最高，最适合深领域迁移与强任务特化</td>
<td>显存、时间和存储成本最高，也最容易削弱通用泛化</td>
</tr>
<tr>
<td>部分参数微调</td>
<td>只需要改特定层或特定结构，或现有框架不便直接接入 PEFT</td>
<td>选择性强，能用较低成本试探“真正该改哪里”</td>
<td>容量有限，往往更像折中方案而不是通用默认解</td>
</tr>
<tr>
<td>Prompt Tuning / Prefix Tuning</td>
<td>样本较少、任务很多、希望极小增量复用同一基座</td>
<td>参数极少，保留主干泛化，适合多任务轻量切换</td>
<td>表达力通常弱于 LoRA；Prefix 实现更复杂，Prompt 在复杂行为控制上更弱</td>
</tr>
<tr>
<td>Adapter</td>
<td>需要显式模块化、任务插拔或特定架构兼容路径</td>
<td>结构清晰、任务隔离好、便于组织内复用</td>
<td>前向路径保留额外分支，在大模型场景里主流度已弱于 LoRA</td>
</tr>
<tr>
<td>LoRA</td>
<td>通用生成模型微调、多任务适配、需要效果与效率平衡</td>
<td>效果稳、生态成熟、可合并、可热切换，是当前主流默认基线</td>
<td>仍需选择 rank、挂载位置与训练稳定性权衡；深知识注入时可能容量不足</td>
</tr>
<tr>
<td>QLoRA / 量化 LoRA</td>
<td>GPU 资源非常有限，但仍需要微调较大生成模型</td>
<td>显著降低显存门槛，让 7B / 13B 级模型微调更可落地</td>
<td>训练链路更复杂；若任务要求极强表达力，最终仍可能需要更重路线</td>
</tr>
</tbody>
</table>
<p>因此，微调技术选型可以压缩成一条很实际的经验顺序：先判断是否根本不该训练参数，而应优先做参数外优化；若需要训练，再判断任务是否只是轻量行为适配，此时 LoRA / QLoRA 往往是默认起点；若样本极少且强调多任务极致轻量切换，软提示路线才更有吸引力；若任务要求深度改写底座知识或结构，再考虑继续预训练与全量微调。只有当这些路线都无法满足目标时，才值得继续向更重、更贵的训练方式推进。</p>
<p>再往前走一步，就是下一节的偏好对齐问题：如果模型已经学会了任务本身，却仍然不会在多个可行回答中稳定偏向人类真正想要的那个，那么问题就不再只是“选哪种微调技术”，而是要不要进入奖励模型、DPO、PPO、GRPO 这一层相对偏好优化。</p>
<div class="blog_h2"><span class="graybg">强化学习对齐</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">路线</td>
<td style="text-align: center;">直接监督信号来自哪里</td>
<td style="text-align: center;">参考模型的作用</td>
<td style="text-align: center;">如何防止偏离 SFT 太远</td>
</tr>
</thead>
<tbody>
<tr>
<td>RLHF + PPO</td>
<td>奖励模型输出的 reward；奖励模型本身来自人类偏好数据</td>
<td>作为参考策略 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\mathrm{ref}}\)</span>，通常是 SFT 模型的冻结副本</td>
<td>显式加入 KL 项，把当前策略锚定在参考分布附近</td>
</tr>
<tr>
<td>DPO</td>
<td>显式偏好对 <span displaypfx="inline-" class="mathjax-container">\((x,y_w,y_l)\)</span></td>
<td>作为锚点，比较“当前模型相对参考模型是否更偏向好答案、远离差答案”</td>
<td>不单独写 KL 惩罚项，但在损失中隐式约束模型不要脱离参考模型过远</td>
</tr>
<tr>
<td>GRPO</td>
<td>同一 prompt 下一组回答的评分、排序或规则反馈</td>
<td>常作为参考策略或 KL 正则锚点；具体是否使用取决于实现</td>
<td>通过组内相对优势更新，必要时再叠加参考模型 KL 约束</td>
</tr>
</tbody>
</table>
<p>强化学习对齐（RL-based Alignment）更准确的说法是“偏好对齐（Preference Alignment）”：用偏好信号把模型输出推向“更符合人类/评审标准”的区域。监督微调（SFT）解决的是“模型是否会按指令作答”，偏好对齐解决的则是“在多个看似都能回答问题的候选答案中，模型是否会稳定偏向更有帮助、更安全、更符合人类预期的那个”。两者并不重复，而是前后衔接的两层约束。</p>
<p>监督微调本身当然可以承担一部分对齐工作。只要训练数据里显式包含拒答样例、安全边界、好坏答案对照、批判依据（Critique Rationale）、自我修正链路，模型就能通过有监督学习吸收相当一部分“什么回答风格更合适、什么回答应当避免”的行为模式。很多现代对齐流程的第一步，本来就是把这些规则先写进 SFT 数据，再让模型学会基础行为边界。</p>
<p>但 SFT 的学习目标本质上仍然是<span style="background-color: #c0c0c0;">给定输入，去拟合某个目标输出</span>。若把提示词记为 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>、参考答案记为 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>，那么它优化的是 <span displaypfx="inline-" class="mathjax-container">\(\log \pi_\phi(y|x)\)</span> 这一类似然目标。即使数据里同时给出“好答案、坏答案、批判依据”，SFT 也主要是在学习如何复现这些文本本身，而不是直接学习“在多个候选回答之间，哪个应该被稳定偏好”。这意味着它更擅长教会模型<span style="background-color: #c0c0c0;">怎样说</span>，却不天然等价于教会模型<span style="background-color: #c0c0c0;">怎样在多个可行答案里做排序</span>。</p>
<p>拒绝采样微调可以看作这两层之间的一条中间路线。它先让模型对同一提示词生成多个候选，再借助验证器、规则或奖励信号只保留最好的一部分，把筛选结果重新写回监督数据，再继续做 SFT。这样做比纯手工监督更能利用自动评估，但仍然没有显式保留“胜者为何优于败者”的相对排序结构，因此它更像<span style="background-color: #c0c0c0;">把偏好信号先离散化成高质量目标答案，再交给监督微调吸收</span>。</p>
<p>偏好对齐之所以需要单独成层，关键就在这里。很多真实问题并不存在唯一标准答案，而是存在一组“都不算错、但质量不同”的候选回答：有的更完整，有的更安全，有的更符合语气预期，有的虽然事实没错却明显不够有帮助。把这种问题压成单一参考答案做 SFT，模型容易学到某一种写法，却未必真正学到“为什么 A 应优于 B”。批判依据也有同样的局限：模型可以学会输出一段像样的批判文本，但这并不自动保证它在自由生成时，会稳定地把这些批判原则内化为回答排序规则。</p>
<p>因此，今天更准确的技术分层是：SFT 可以完成<span style="background-color: #c0c0c0;">基础行为对齐</span>，而偏好优化负责处理<span style="background-color: #c0c0c0;">相对偏好排序</span>。前者让模型学会回答、学会拒答、学会遵守基本格式；后者让模型在多个都看似合理的答案之间，更稳定地偏向人类真正想要的那个。也正因为如此，现代对齐并不必然等于“强化学习”本身：PPO / RLHF 是一条路线，DPO、ORPO 等把偏好直接写进损失的路线则是另一条路线。它们解决的是同一个问题，只是优化手段不同。</p>
<p>这一层之所以重要，还因为大模型的质量并不能被单一指标完整刻画。古德哈特定律（Goodhart's Law）指出：一旦某个指标变成优化目标，它往往就不再是一个好的指标。放到模型对齐里，这意味着如果只盯住某个狭窄基准分数，模型很可能学会迎合该指标，却牺牲真正的可用性、稳健性与安全性。偏好对齐因此更强调相对排序、多维度评审与参考模型约束，而不是把“好答案”简化成单一静态分数。</p>
<p>从工程上看，主流路线分为两类：</p>
<ul>
<li>显式奖励：先训练奖励模型（Reward Model），再用 PPO 等策略优化算法做 RLHF。</li>
<li>直接偏好：不显式训练奖励模型，直接用偏好对优化策略（例如 DPO）。</li>
</ul>
<div class="blog_h3"><span class="graybg">奖励模型（Reward Model）</span></div>
<p>奖励模型（Reward Model, RM）把（提示词 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，回复 <span displaypfx="inline-" class="mathjax-container">\(y\)</span>）映射为一个标量分数 <span displaypfx="inline-" class="mathjax-container">\(r_\theta(x,y)\in\mathbb{R}\)</span>。它的职责不是生成答案，而是充当自动评审器：输入“问题 + 回答”，输出一个可比较的质量信号，用来近似人类对回答质量的偏好判断。</p>
<div class="blog_h4"><span class="graybg">偏好数据</span></div>
<p>奖励模型的训练数据通常是成对偏好数据，而不是单条样本加绝对分数。对同一提示词 <span displaypfx="inline-" class="mathjax-container">\(x\)</span>，先让模型生成多个候选回答，再由人工标注员或更强的评审模型选择其中更优的一条，形成三元组 <span displaypfx="inline-" class="mathjax-container">\((x,y_w,y_l)\)</span>：其中 <span displaypfx="inline-" class="mathjax-container">\(y_w\)</span> 是被接受的回答（chosen response），<span displaypfx="inline-" class="mathjax-container">\(y_l\)</span> 是被拒绝的回答（rejected response）。</p>
<p>这种二选一偏好标注通常比直接打 1 到 5 分更稳定。原因并不复杂：绝对分数依赖个人尺度，主观漂移大；相对偏好只要求判断 A 和 B 谁更好，一致性通常更高，标注成本也更低。因此，现代偏好对齐流程更常见的监督信号是排序关系，而不是绝对评分。</p>
<div class="blog_h4"><span class="graybg">模型形态</span></div>
<p>奖励模型通常以已经完成 SFT 的语言模型为骨干（Backbone）进行改造，而不是从零开始训练：保留主体 Transformer 表示层，移除原先用于生成下一个 token 的语言建模头（LM Head），换成一个输出单一标量的质量头（Reward Head）。这样做的好处是，奖励模型继承了 SFT 模型对指令与回答结构的理解，只需要继续学习“哪种回答更好”这一层偏好判断。</p>
<p>对一对“胜/负”样本 <span displaypfx="inline-" class="mathjax-container">\((x,y_w,y_l)\)</span>，常用 Bradley–Terry / Logistic 形式把分数差转为偏好概率：</p>
<span displaypfx="" class="mathjax-container">\[\Pr(y_w \succ y_l\mid x)=\sigma\!\left(r_\theta(x,y_w)-r_\theta(x,y_l)\right)\]</span>
<p>并用对数损失训练：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{RM}}(\theta)=-\log \sigma\!\left(r_\theta(x,y_w)-r_\theta(x,y_l)\right)\]</span>
<p>关键点：sigmoid 用在“分数差”上，而不是对每个 <span displaypfx="inline-" class="mathjax-container">\(r_\theta(x,y)\)</span> 再套一层 sigmoid。分数本身不需要限制在 <span displaypfx="inline-" class="mathjax-container">\([0,1]\)</span>；概率来自差值的 logistic 映射。</p>
<p>参数 <span displaypfx="inline-" class="mathjax-container">\(\theta\)</span> 表示奖励模型的参数集合（Parameter Set），包含全部权重矩阵与偏置项，并非单一标量。</p>
<p>单调性视角：因为 <span displaypfx="inline-" class="mathjax-container">\(\sigma\)</span> 单调递增且 <span displaypfx="inline-" class="mathjax-container">\(-\log(\cdot)\)</span> 在 <span displaypfx="inline-" class="mathjax-container">\((0,1)\)</span> 上单调递减，所以 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{L}_{\mathrm{RM}}\)</span> 对分数差 <span displaypfx="inline-" class="mathjax-container">\(\Delta r=r_\theta(x,y_w)-r_\theta(x,y_l)\)</span> 单调递减。训练会推动 <span displaypfx="inline-" class="mathjax-container">\(\Delta r\)</span> 变大，从而把胜者分数推高、败者分数压低。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/reward.png"><img class="alignnone size-full wp-image-42015" src="https://blog.gmem.cc/wp-content/uploads/2026/03/reward.png" alt="reward" width="1024" height="1024" /></a></p>
<p>&nbsp;</p>
<div class="blog_h3"><span class="graybg">RLHF</span></div>
<p>RLHF（Reinforcement Learning from Human Feedback）把整个偏好对齐流程拆成三段：先收集偏好数据，再训练奖励模型，最后用奖励模型为主语言模型提供优化信号。它的核心贡献不在于“使用了强化学习”本身，而在于把原本昂贵、缓慢、难以规模化的人类评审，转化成可以自动计算的奖励分数。</p>
<p>在 RLHF 里，语言生成被改写为一个强化学习问题：token 是动作（Action），策略是语言模型 <span displaypfx="inline-" class="mathjax-container">\(\pi_\phi(y|x)\)</span>，奖励来自 <span displaypfx="inline-" class="mathjax-container">\(r_\theta(x,y)\)</span>。早期最经典、也最具代表性的做法，是以 PPO（Proximal Policy Optimization）作为策略更新算法，用奖励模型给出的分数提高高质量回答的生成概率，同时压低低质量回答的概率。常见目标写成：</p>
<span displaypfx="" class="mathjax-container">\[\max_{\phi}\ \mathbb{E}_{y\sim \pi_\phi(\cdot|x)}\big[r_\theta(x,y)\big]-\beta\,D_{\mathrm{KL}}\!\left(\pi_\phi(\cdot|x)\,\|\,\pi_{\mathrm{ref}}(\cdot|x)\right)\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\mathrm{ref}}\)</span> 通常不是额外训练出来的一套神秘模型，而就是<span style="background-color: #c0c0c0;">PPO 开始前那一版 SFT 模型的冻结副本</span>。标准顺序通常是：先从预训练基座得到 SFT 模型，再把这份 SFT checkpoint 复制成两路，一路继续作为可训练策略 <span displaypfx="inline-" class="mathjax-container">\(\pi_\phi\)</span>，一路冻结为参考模型 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\mathrm{ref}}\)</span>。因此并不存在“先有参考模型还是先有策略模型”的循环依赖；二者都来自同一个 SFT 起点，只是一个继续更新，一个保持不动。</p>
<p>式子里的 KL 项与 KL 散度（Kullback–Leibler Divergence）是直接对应的关系：这里所谓的“KL 项”，就是目标函数中的 <span displaypfx="inline-" class="mathjax-container">\(\beta\,D_{\mathrm{KL}}(\pi_\phi\|\pi_{\mathrm{ref}})\)</span> 这一项，只不过前面再乘了一个权重系数 <span displaypfx="inline-" class="mathjax-container">\(\beta\)</span>。它的作用不是做额外评测，而是作为正则约束，把当前策略锚定在 SFT 参考分布附近，防止模型为了讨好奖励模型而偏离过远，出现奖励黑客（Reward Hacking）、语言退化或能力崩塌。PPO 在这类任务里长期被广泛采用，原因正是它对策略更新幅度加了“护栏”，能在追求更高奖励和保持原有能力之间维持相对稳定的折中。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/rlhf.png"><img class="alignnone size-full wp-image-42019" src="https://blog.gmem.cc/wp-content/uploads/2026/03/rlhf.png" alt="rlhf" width="1024" height="1024" /></a></p>
<div class="blog_h4"><span class="graybg">PPO</span></div>
<p>PPO（Proximal Policy Optimization）之所以长期是 RLHF 的默认优化器，不是因为它在理论上最优，而是因为它足够稳。普通策略梯度（Policy Gradient）的问题在于：一旦某次更新把策略推得过远，语言模型就可能突然偏离原有分布，表现为回复风格失真、可读性下降、奖励黑客，甚至整体能力退化。PPO 的核心改进就是限制“这一步最多改多少”，让策略朝高奖励方向移动，但每次只允许小步修正。</p>
<p>它最经典的形式是 clipped objective。若当前策略与旧策略的概率比记为 <span displaypfx="inline-" class="mathjax-container">\(\rho_t=\frac{\pi_\phi(a_t|s_t)}{\pi_{\phi_{\mathrm{old}}}(a_t|s_t)}\)</span>，对应优势函数（Advantage Function）为 <span displaypfx="inline-" class="mathjax-container">\(A_t\)</span>，则目标可写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{PPO}}(\phi)=\mathbb{E}_t\left[\min\left(\rho_t A_t,\ \mathrm{clip}(\rho_t,1-\epsilon,1+\epsilon)A_t\right)\right]\]</span>
<p>这里的 <span displaypfx="inline-" class="mathjax-container">\(\epsilon\)</span> 是更新护栏宽度。若某次更新让概率比偏离 1 太远，clip 就会截断继续放大的收益，阻止模型为了追求更高奖励而走得过猛。对 LLM 而言，这个机制尤其重要，因为语言模型的输出分布非常高维，只要少量 token 的条件概率被过度放大，就可能连锁改变整段回答的行为模式。</p>
<p>放到 RLHF 流程里，PPO 的完整闭环通常是：先用当前策略对同一提示词采样若干回答，再用奖励模型评分，并结合参考模型的 KL 惩罚构造最终回报；随后估计优势 <span displaypfx="inline-" class="mathjax-container">\(A_t\)</span>，再用 clipped objective 更新策略。也正因为这里同时牵涉采样、奖励模型、参考模型、旧策略快照与优势估计，PPO 路线的工程链条明显比 DPO 更长，训练成本和调参复杂度也更高。</p>
<p>工业实践中，奖励往往也不是单一维度。一个典型做法是分别训练“有用性（Helpfulness）”与“安全性（Safety）”奖励模型，再按加权和形成总奖励，例如 <span displaypfx="inline-" class="mathjax-container">\(R_{\mathrm{total}}=\alpha R_{\mathrm{helpful}}+\beta R_{\mathrm{safety}}\)</span>。这样可以显式控制不同对齐目标之间的权重，而不是把所有偏好都压缩进一个不可分解的单一评分器里。</p>
<div class="blog_h3"><span class="graybg">非典型算法形态</span></div>
<p>到了大模型对齐阶段，很多方法在问题定义上仍然属于强化学习，但在算法形态上已经明显偏离经典强化学习教材中的标准样子。原因并不神秘：LLM 对齐面对的是一种极其特殊的决策问题。若强行套用经典 RL 映射，可以把状态（State）理解为“当前提示词 + 已生成 token 序列”，把动作（Action）理解为“从巨大词表中选择下一个 token”，把奖励（Reward）理解为“整段回答生成完之后得到的评分”，把环境（Environment）理解为模型自身的自回归生成过程。这个映射在概念上成立，但工程代价极高。</p>
<p>困难主要集中在三点。第一，动作空间极大：每一步都要在成千上万个 token 中做选择。第二，奖励高度延迟：很多时候只有整段回答生成完毕后，才能得到一个总体评分，时间信用分配（Temporal Credit Assignment）比经典控制任务更棘手。第三，策略优化链条极重：若完整采用 PPO 式 RLHF，训练时往往要同时维护可训练策略、旧策略快照、参考模型、奖励模型，很多实现里还要有价值网络（Value / Critic），显存、吞吐和稳定性压力都非常大。</p>
<p>因此，大模型对齐逐渐出现了一条清晰演化路径：<span style="background-color: #c0c0c0;">保留强化学习的问题定义，重写强化学习的求解形态</span>。所谓“保留问题定义”，指的是目标仍然来自评估性反馈（Evaluative Feedback）而不是逐步给定的标准答案；模型仍然要学会在多个候选回答之间偏向更优者。所谓“重写求解形态”，指的是不再机械照搬经典 Actor-Critic 或全流程在线 RL，而是通过数学消元、离线偏好优化、组内相对比较、弱化价值网络等方式，把问题改写成更适合大模型训练的目标。</p>
<p>从这个视角看，DPO 与 GRPO 都不是对强化学习问题本身的放弃，而是对经典算法形态的工程重构。DPO 走得更远：它把“奖励建模 + 策略优化”折叠成一个静态偏好对损失，形式上已经非常接近监督学习或对比学习。GRPO 则保留了“采样 - 评分 - 更新”的策略优化闭环，但用组内相对优势替代显式价值网络，属于一种极简化的策略优化路线。前者更像对 RL 目标的解析化改写，后者更像对 PPO 结构的裁剪与瘦身。</p>
<div class="blog_h3"><span class="graybg">DPO</span></div>
<p>DPO（Direct Preference Optimization）是这种“非典型 RL 形态”里最典型的一类：它直接用偏好对 <span displaypfx="inline-" class="mathjax-container">\((x,y_w,y_l)\)</span> 优化策略，不显式训练奖励模型，也不需要 PPO rollout。它的出发点很明确：既然手里已经有“哪个回答更好”的偏好数据，那么没有必要再额外训练一个奖励模型，再把奖励模型嵌入完整强化学习闭环；可以直接把偏好关系作用到策略本身。</p>
<p>DPO 仍然保留一个冻结的参考模型 <span displaypfx="inline-" class="mathjax-container">\(\pi_{\mathrm{ref}}\)</span> 作为锚点，并同时比较当前可训练模型与参考模型在“被接受回答”和“被拒绝回答”上的相对概率。这里的概率不是单个 token 的局部值，而是整条回答在 token 级对数概率上的汇总，因此<span style="background-color: #c0c0c0;">本质上是在比较“当前模型是否比参考模型更偏向好答案、同时更远离差答案”</span>。典型目标写成：</p>
<span displaypfx="" class="mathjax-container">\[\mathcal{L}_{\mathrm{DPO}}(\phi)=-\log \sigma\!\Big(\beta\big[\log\pi_\phi(y_w|x)-\log\pi_\phi(y_l|x)-\log\pi_{\mathrm{ref}}(y_w|x)+\log\pi_{\mathrm{ref}}(y_l|x)\big]\Big)\]</span>
<p>直觉上，DPO 直接增大“胜者相对败者”的对数概率优势（log-odds margin），同时用参考模型作为锚点。它把 RLHF 中“奖励建模 + PPO 优化”的两步折叠成一步有监督式偏好优化，因此工程更轻、训练更稳定，也更容易复用现有 SFT 训练栈。正因为这一点，DPO 已经成为许多中小团队进行偏好对齐时的默认起点。</p>
<p>从经典 RL 的角度看，DPO 最“不像强化学习”的地方在于：它几乎拿掉了在线探索、环境交互和显式优势估计这些传统组件，直接在离线偏好数据上优化策略。这也是为什么它在形式上看起来更像监督学习；但它解决的仍然是评估性反馈下的偏好优化问题，而不是普通的标签拟合问题。</p>
<p>DPO 训练并不是要把“好样本相对差样本的概率优势”硬拉到某个固定阈值才算结束。它优化的是整个偏好数据集上的相对排序损失，而不是某个预设的绝对 margin。对容易区分的样本，胜者优势会很快变大，梯度也随之减弱；对天然模糊、偏好边界不清的样本，这个优势不可能无限扩大。若继续强行训练，模型更可能开始过拟合偏好数据、放大表面写法差异，甚至损害生成分布稳定性。因此，DPO 的停止标准本质上仍然是<span style="background-color: #c0c0c0;">验证集偏好指标、生成质量与分布稳定性是否已经饱和</span>，而不是“margin 必须大于某个固定常数”。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2026/03/dpo.png"><img class="alignnone size-full wp-image-42031" src="https://blog.gmem.cc/wp-content/uploads/2026/03/dpo.png" alt="dpo" width="1024" height="1024" /></a></p>
<div class="blog_h3"><span class="graybg">GRPO</span></div>
<p>GRPO（Group Relative Policy Optimization）比 DPO 更接近经典策略优化，但它同样属于“非典型 RL 形态”：对同一个提示词 <span displaypfx="inline-" class="mathjax-container">\(x\)</span> 采样一组候选回复 <span displaypfx="inline-" class="mathjax-container">\(\{y_k\}_{k=1}^{K}\)</span>，用奖励/偏好信号在组内排序或打分，再把“相对好坏”转成策略梯度更新。它的核心动机是：用 <span style="background-color: #c0c0c0;">组内比较</span> 构造优势（Advantage）或基线（Baseline），从而减少对显式价值网络（Critic / Value Function）的依赖。</p>
<p>一种常见的做法是把组内奖励做标准化得到相对优势：</p>
<span displaypfx="" class="mathjax-container">\[A_k=\frac{r_k-\mu_r}{\sigma_r+\epsilon},\quad \mu_r=\frac{1}{K}\sum_{k=1}^{K}r_k,\ \sigma_r^2=\frac{1}{K}\sum_{k=1}^{K}(r_k-\mu_r)^2\]</span>
<p>并用 PPO 风格的 clipped objective 更新策略（仍然可带 KL 正则到参考模型）：</p>
<span displaypfx="" class="mathjax-container">\[\max_{\phi}\ \mathbb{E}\Big[\min\big(\rho_k A_k,\ \mathrm{clip}(\rho_k,1-\epsilon,1+\epsilon)\,A_k\big)\Big]-\beta D_{\mathrm{KL}}\!\left(\pi_\phi\,\|\,\pi_{\mathrm{ref}}\right),\quad \rho_k=\frac{\pi_\phi(y_k|x)}{\pi_{\phi_{\mathrm{old}}}(y_k|x)}\]</span>
<p>真正决定“奖惩”的不是某个回答的绝对分数，而是它在同组候选中的相对位置。若某个回答的组内奖励 <span displaypfx="inline-" class="mathjax-container">\(r_k\)</span> 高于这一组的平均水平 <span displaypfx="inline-" class="mathjax-container">\(\mu_r\)</span>，则 <span displaypfx="inline-" class="mathjax-container">\(A_k&gt;0\)</span>，训练会提高模型再次生成这类回答的概率；若某个回答低于组内平均水平，则 <span displaypfx="inline-" class="mathjax-container">\(A_k&lt;0\)</span>，训练会压低模型对这类回答的偏好。换句话说，GRPO 奖励的是“在同一个 prompt 下，比同组其他回答更好的样本”，惩罚的是“在同一个 prompt 下，相对更差的样本”，而不是单独给每个回答设一个全局固定门槛。</p>
<p>从优化角度看，这种“压制”并不是额外加一个独立惩罚按钮，而是让该回答在目标函数里贡献<span style="background-color: #c0c0c0;">负优势（Negative Advantage）</span>更新。结果上，它等价于让这类回答对应的策略概率逐步下降：模型以后再采样到相似回答时，会更倾向于远离它，而不是继续强化它。</p>
<p>因此，GRPO 与经典 PPO 的最大差别，并不在于“还有没有策略梯度”，而在于它把传统 Actor-Critic 结构大幅裁剪了。经典 PPO 往往依赖一个显式价值网络去估计 baseline，以降低方差；GRPO 则直接用同组候选回答之间的相对分数生成基线，把“谁比平均更好、谁比平均更差”写进组内统计量。这使它在大模型场景下明显更省显存，也更容易扩展到规则打分、程序验证和裁判模型评分等复杂奖励来源。</p>
<p>这种机制尤其适合答案质量强依赖上下文、很难用一个全局绝对分数刻画的场景。对某些 prompt，70 分的回答可能已经是组内最优，应当被正向强化；对另一些 prompt，80 分的回答仍可能只是组内倒数，应当被压制。GRPO 的核心就在于：目标模型并不是因为“达到某个统一分数线”而得到奖励，而是因为它在<span style="background-color: #c0c0c0;">同题候选集合里相对更优</span>而受到正向更新。</p>
<p>GRPO 仍然需要某种奖励/偏好信号（显式奖励模型、规则打分、对比标注等）；它改变的是“如何用这些信号构造稳定的更新”，而不是免除奖励来源本身。把几条路线放在一起看，会更清楚：RLHF + PPO 强调显式奖励建模与稳定策略更新，DPO 强调跳过奖励模型后的直接偏好优化，GRPO 则强调用组内相对比较构造更稳定的优势信号。它们共享同一个目标，只是在“偏好信号如何表达、如何被模型消化”这件事上采取了不同工程路径。</p>
<div class="blog_h2"><span class="graybg">参数外优化</span></div>
<p>参数外优化（Parameter-free Optimization）指的是：在不更新模型权重的前提下，通过改写提示词（Prompt）、输出约束、示例组织、工具说明、工作流与评估闭环来提升系统表现。它优化的对象不是模型内部参数，而是模型与任务之间的接口层。</p>
<p>这条路线之所以重要，是因为很多任务的瓶颈并不在模型“不会”，而在任务描述不够精确、输出约束不够严格、示例覆盖不足，或评估回路设计不清。此时，更合理的第一步往往不是立刻进入 SFT、LoRA 或 DPO，而是先做参数外优化：不改模型参数，只改模型使用方式。</p>
<div class="blog_h3"><span class="graybg">自动化 Prompt 优化（APO）</span></div>
<p>自动化 Prompt 优化（Automatic Prompt Optimization, APO）的核心思想是：把人工反复修改 Prompt 的经验循环，改写成一个自动化搜索与评估过程。它不更新模型权重，而是把 Prompt 本身当作待优化对象，再用验证集上的错误信号驱动 Prompt 迭代。换句话说，APO 优化的不是模型内部参数，而是模型与任务之间的接口描述。</p>
<p>这条路线之所以重要，是因为很多任务的误差并不来自“模型完全不会”，而来自任务定义不够精确、输出约束不够严格、规则优先级表达不清，或者 few-shot 示例没有覆盖真正的边界情况。对这些问题，先做 Prompt 优化通常比直接微调更便宜，也更容易定位问题来源。</p>
<div class="blog_h4"><span class="graybg">基本流程</span></div>
<p>APO 可以概括为一个闭环迭代过程。</p>
<ol>
<li>从当前 Prompt 出发，在固定验证集上运行模型，得到一轮可量化的表现。</li>
<li>收集错误样本，重点关注 false positive、false negative 以及模型在边界样本上的失误模式。</li>
<li>把这些错误样本与当前 Prompt 一起交给一个更强的语言模型，要求它分析当前 Prompt 的缺陷，例如规则表述含糊、优先级冲突、示例覆盖不足、输出格式不稳定，或对某些语义模式缺乏约束。</li>
<li>基于这些分析生成若干候选 Prompt。候选改动可以包括：重写系统提示、重排规则顺序、增加约束语句、补充反例、加入 few-shot 示例，或强化输出格式说明。</li>
<li>用同一个验证集重新评估这些候选 Prompt，并按预先定义的指标选出当前最优版本。</li>
<li>重复这一过程，直到验证集指标收敛，或进一步修改已经不能带来稳定收益。</li>
</ol>
<p>这个闭环的本质是“用验证集驱动 Prompt 搜索”。人工调 Prompt 时，工程师通常也是先看错例，再猜原因，再改写提示词，再重新评估；APO 只是把这条经验流程交给模型辅助完成，并把迭代过程系统化。</p>
<div class="blog_h4"><span class="graybg">APO 依赖什么</span></div>
<p>要让 APO 真正有效，至少要具备三样东西：</p>
<ol>
<li>高质量验证集：它不一定需要极大规模，但必须足够准确，且覆盖关键边界情况。</li>
<li>清晰的评价指标：分类任务通常可以直接使用 Accuracy、Precision、Recall、F1；抽取、排序、生成任务则需要对应的可重复评价标准。</li>
<li>被优化对象必须足够明确：例如系统提示词、用户提示模板、few-shot 示例集、输出格式约束或工具调用说明。</li>
</ol>
<p>如果缺少这三者中的任意一个，APO 都很容易退化成“让模型随意重写 Prompt”，最终只是在做风格漂移，而不是真正基于验证信号优化任务表现。因此，APO 的核心不是“会不会改 Prompt”，而是“能否把 Prompt 修改纳入可验证的实验闭环”。</p>
<div class="blog_h4"><span class="graybg">验证信号质量</span></div>
<p>参数外优化对验证信号质量极其敏感。因为它并不通过海量训练样本去平均噪声，而是直接把验证集上的错误模式反向写入 Prompt 结构，所以一旦验证集标签本身含糊、冲突或混入大量低置信样本，优化器就很容易朝错误方向改写规则。高质量、边界清晰的验证集通常能显著提升 APO 的稳定性；相反，模糊数据会让优化过程更像是在追逐评测噪声，而不是纠正模型真实缺陷。</p>
<div class="blog_h4"><span class="graybg">与微调的区别</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">维度</td>
<td style="text-align: center;">微调</td>
<td style="text-align: center;">APO</td>
</tr>
</thead>
<tbody>
<tr>
<td>优化对象</td>
<td>模型权重</td>
<td>Prompt、示例、输出约束</td>
</tr>
<tr>
<td>资源需求</td>
<td>通常需要训练框架、显存和数据管线</td>
<td>通常只需要验证集与可调用的模型</td>
</tr>
<tr>
<td>成本</td>
<td>高</td>
<td>低</td>
</tr>
<tr>
<td>可解释性</td>
<td>较弱，行为被写入权重</td>
<td>强，Prompt 变更可直接审查</td>
</tr>
<tr>
<td>性能上限</td>
<td>更高，可把规则和模式内化进模型</td>
<td>受限于基座模型自身能力</td>
</tr>
<tr>
<td>更适合</td>
<td>数据充足、任务长期稳定、性能要求高</td>
<td>快速迭代、规则频繁变化、数据量有限</td>
</tr>
</tbody>
</table>
<p>因此，APO 和微调并不是互相替代的关系，而更像两层不同成本的优化手段。若模型本身已经具备足够能力，但任务接口还没有调顺，先做 APO 往往收益最高；若 Prompt 已经被压到比较成熟，但模型仍然持续犯系统性错误，才更适合进入微调阶段。</p>
<div class="blog_h4"><span class="graybg">适用边界</span></div>
<p>APO 最适合规则可文本化、评估指标清晰、且基座模型本身已经具备足够语义理解能力的任务。例如分类、抽取、轻量结构化生成、审核规则执行、问答格式控制和工具调用提示约束，往往都能从 APO 中获益。尤其当任务规则经常变化、人工需要频繁更新 Prompt 时，APO 的价值会非常明显。</p>
<p>它的边界同样清晰。若任务需要模型学习新的领域知识、记住稳定事实、吸收大量风格样本，或长期存在系统性能力缺口，那么单纯修改 Prompt 的收益通常很快见顶。此时，Prompt 优化可以继续作为上层控制手段存在，但性能提升的主战场已经会转移到微调与继续预训练。</p>
<div class="blog_h3"><span class="graybg">多分支 Prompt 优化（AMPO）</span></div>
<p>AMPO（Automatic Multi-Branched Prompt Optimization）可以看作 APO 的结构化升级版。普通 APO 往往默认“只有一条主 Prompt 流程”，优化方式主要是改写文字、调整规则顺序或补充示例；AMPO 则进一步把 Prompt 看作一种可演化的决策结构。它的目标不只是把单条 Prompt 写得更好，而是让 Prompt 从单流程逐步生长为多分支结构，使模型在面对不同输入模式时，能够沿着更合适的子路径完成判断。</p>
<p>这种思路来自一个很强的人类专家直觉：复杂任务往往并不是靠一条统一规则解决，而是靠“先识别情形，再走对应流程”。因此，AMPO 的真正创新不只是自动改 Prompt，而是把 Prompt 优化从“文案修订”提升成“结构搜索”。在这种框架下，提示词已经不只是几句话，而更像一个树状决策蓝图。</p>
<div class="blog_h4"><span class="graybg">为什么需要多分支</span></div>
<p>当任务内部同时包含多种错误模式时，单分支 Prompt 很容易不断堆叠例外说明，最后变成冗长、脆弱且难以维护的规则串。AMPO 的判断是：如果不同错误模式对应的是不同处理逻辑，就不应强行把它们揉进同一段线性指令，而应允许 Prompt 显式分叉。这样做有三个直接收益。第一，结构更清晰：每个分支各自负责一类模式，规则可解释性更强。第二，复杂场景适应性更好：模型不必在一条过长指令中硬找适用规则，而是先定位情形，再走对应分支。第三，后续维护更容易：新增模式时可以局部增补或重构特定分支，而不必重写整个 Prompt。</p>
<div class="blog_h4"><span class="graybg">三大模块</span></div>
<p>AMPO 的核心流程可以分成三个协同模块：模式识别（Pattern Recognition）、分支调整（Branch Adjustment）和分支剪枝（Branch Pruning）。这三个模块合在一起，构成了一个从错误样本出发、逐步生长并控制复杂度的优化回路。</p>
<p><span style="background-color: #c0c0c0;">模式识别</span>负责把零散坏例归纳成少数根因模式。它通常不是直接把所有错误样本原样塞进一个大 Prompt，而是采用角色分工：一个分析器（Analyzer）逐个解释失败原因，另一个总结器（Summarizer）把这些解释压缩成更高层的模式，并给模式分配重要性。这样做的关键价值在于：优化目标从“修这几个具体坏例”转成“修这一类错误背后的共同规则缺口”。</p>
<p><span style="background-color: #c0c0c0;">分支调整</span>负责决定 Prompt 结构该如何变化。这里最重要的决策不是“改不改”，而是“深化已有分支，还是新增分支”。若新模式与某个已有分支高度相关，只需补充约束或细节，那么更合理的做法是深化；若新模式与现有逻辑明显不同，继续堆进原分支只会制造冲突，就应当拓宽，新增一个独立子分支。也正因为如此，AMPO 优化的不是词句表面，而是 Prompt 的控制流结构。</p>
<p><span style="background-color: #c0c0c0;">分支剪枝</span>负责抑制过拟合。多分支优化的自然风险是：随着迭代次数增加，Prompt 可能长出大量只服务于少数训练样本的局部规则，最终在未知数据上退化。AMPO 因此显式引入两层剪枝：</p>
<ol>
<li>预剪枝（Pre-pruning）：相当于基于独立验证集的早停机制。若新增分支不再带来稳定收益，就停止继续扩张。</li>
<li>后剪枝（Post-pruning）：要求优化器在输出最终 Prompt 前重新审视各分支，删掉不必要、过于具体或明显带有训练集记忆痕迹的规则。</li>
</ol>
<div class="blog_h4"><span class="graybg">坏例抽样</span></div>
<p>AMPO 的一个很有代表性的设计，是每轮并不分析大量失败样本，而是只抽取很少量的代表性坏例，例如固定 <span displaypfx="inline-" class="mathjax-container">\(K=5\)</span>。这不是随意取值，而是一种典型的小样本归纳策略：在模式识别任务里，最前面的少数高信息量样本往往已经足以揭示一类错误的共性，而继续增加样本数量，边际信息收益会快速下降。对具备较强归纳能力的 LLM 来说，少量围绕同一主题的坏例通常已经足够提炼出根本模式。</p>
<p>更重要的是，AMPO 并不要求 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 随总坏例数量成比例扩大。固定的小 <span displaypfx="inline-" class="mathjax-container">\(K\)</span> 让每轮优化的成本更可控，也避免分析器被大量重复、低信息量坏例淹没。参数外优化的关键不是“看尽可能多的错误”，而是“看足够有代表性的错误”。</p>
<div class="blog_h4"><span class="graybg">是否需要失败输出</span></div>
<p>AMPO 还提供了一个很重要的方法论提醒：分析错误原因时，并不总是必须把模型当轮生成的失败输出显式提供给分析器。很多时候，输入、当前 Prompt、正确标签以及“这条样本被判错了”这一事实，已经足以让分析器定位规则缺失。因为优化器真正需要回答的问题不是“模型具体说错了什么句子”，而是“当前指令为什么会把模型引向这类错误”。当失败输出本身噪声较大、措辞随机性较强时，强行把它加入分析，反而可能干扰模式归纳。</p>
<p>换言之，参数外优化的关注重点应当放在<span style="background-color: #c0c0c0;">规则缺口</span>而不是<span style="background-color: #c0c0c0;">错误表面</span>上。一个高质量分析器更像在做“指令失效诊断”，而不是对错误回答做逐字复盘。</p>
<div class="blog_h4"><span class="graybg">优化器与执行器解耦</span></div>
<p>AMPO 还强化了一个现实而重要的工程模式：用于分析与重写 Prompt 的优化器模型，并不必与实际执行任务的目标模型相同。前者更强调总结、重写和结构设计能力，后者则更关注线上推理成本、延迟、稳定性和部署约束。把“谁负责优化 Prompt”和“谁负责执行 Prompt”解耦，往往能在成本与效果之间得到更好的组合。</p>
<div class="blog_h4"><span class="graybg">实验启示</span></div>
<p>从论文记录呈现的实验关注点看，AMPO 主要验证了三件事。第一，优化效率：多分支结构在复杂任务上往往能更快探索到高质量 Prompt，而不是在单分支上做无穷尽的局部修补。第二，收敛性：随着迭代推进，Prompt 结构会逐步稳定下来，说明这种优化并非纯随机搜索。第三，消融结果：模式识别、分支调整和分支剪枝都不是装饰性组件，拿掉任一模块，性能与稳定性都会受到影响。与普通 APO 相比，AMPO 的价值并不只是“Prompt 更长”，而是 Prompt 结构更像一个经过错误模式驱动后生长出来的决策树。</p>
<p>因此，参数外优化不应只被理解成“自动改几个词”。从 APO 到 AMPO，它实际上形成了一条清晰的演进路径：先把 Prompt 当作可搜索的文本接口，再把 Prompt 当作可演化的结构接口。前者已经足以覆盖大量规则型任务，后者则更适合复杂、异质、边界模式较多的任务。</p>
<div class="blog_h2"><span class="graybg">基座模型选择</span></div>
<p>在进入具体训练场景之前，通常应先判断任务应该建立在什么类型的基座模型之上。这个选择会直接决定后续数据形态、训练目标、推理链路与上线成本。很多项目的真正分水岭并不在 LoRA、QLoRA、DPO 这些技术细节，而在于一开始是否选对了模型范式：到底应当使用 BERT 一类表示模型（Representation Model），还是使用 Decoder-only 生成模型（Generative Model）。</p>
<div class="blog_h3"><span class="graybg">表示模型适合什么</span></div>
<p>BERT、RoBERTa、DeBERTa 一类 Encoder-only 模型，最擅长的是把输入压缩成稳定表示，再围绕固定目标做判别。它们特别适合闭集分类（Closed-set Classification）、文本匹配、检索、序列标注、重排序，以及“输入充分、标签空间明确、输出形式固定”的任务。只要目标可以被表述为“给这段输入打一个标签”或“判断这两段文本是否匹配”，表示模型通常都是更高效、更便宜、也更容易评估的选择。</p>
<p>这里需要明确一点：BERT 并不是完全没有顺序信息。它同样通过位置编码（Positional Encoding）与自注意力看到序列先后关系，因此能区分“先发生什么、后发生什么”。真正的限制不在“看不到顺序”，而在于它通常把整段输入压缩为一个判别表示，再直接映射到标签空间。对于需要显式执行规则、动态权衡多段证据、并把局部冲突统一到最终结论上的任务，这种一次性判别路径往往不够灵活，也缺乏可解释的中间推理结构。</p>
<div class="blog_h3"><span class="graybg">生成模型适合什么</span></div>
<p>生成式大语言模型的优势出现在另一类任务中：任务目标本身包含开放式语义判断、复杂规则执行、跨轮次状态整合，或者需要先形成中间结论，再决定最终输出。这类任务往往不只是“看完文本后打标签”，而是要求模型先判断哪些证据重要、哪些证据只是过程噪声，再决定最终答案。对这类问题，生成模型更容易通过指令约束、上下文推理和多步语义整合完成任务，因此更适合作为基座。</p>
<p>例如，在对话满意度判定中，若规则是“过程中的波折、局部负面情绪视为过程成本，只要最终方案被接受、问题收尾清晰，就优先按已解决处理”，任务本质上就不再是简单情感分类。模型需要区分中间波折与最终状态，识别“收尾是否清晰”，并对冲突信号做优先级排序。这里最困难的部分不是识别负面词，而是执行一条带有<span style="background-color: #c0c0c0;">结尾优先、过程降权</span>结构的规则。生成模型在这种场景下通常更稳，因为它更容易在长上下文中整合多段证据，并按指令执行“先看结尾，再回看过程”的判断逻辑。</p>
<div class="blog_h3"><span class="graybg">BERT 类模型的边界</span></div>
<p>只要任务满足以下条件，BERT 类模型通常仍然足够胜任：</p>
<ul>
<li>输出空间固定且较小，例如满意 / 不满意、风险 / 非风险、升级 / 不升级。</li>
<li>决定标签的证据主要是局部可见、模式稳定的文本特征，而不是依赖复杂跨轮推理。</li>
<li>规则可以被充分体现在标注数据里，使模型通过监督学习稳定内化这种判别边界。</li>
</ul>
<p>典型例子包括情感分类、意图分类、FAQ 匹配、实体识别、工单主题归类，以及大量结构清晰的客服路由任务。</p>
<p>若任务开始依赖以下能力，BERT 类模型的风险就会显著上升：</p>
<ul>
<li>需要对长对话做结尾优先的全局判断。</li>
<li>需要把中间负面情绪降权，但又不能完全忽略。</li>
<li>需要区分“问题解决了但过程不愉快”与“问题根本没解决”。</li>
<li>需要持续引入新规则，并要求模型在推理时可控地遵循这些规则。</li>
</ul>
<p>此时，即使通过多段编码、层级聚合、结尾加权或级联分类等工程技巧勉强构造出一个系统，上限通常也受限于任务本身的推理复杂度，而且系统维护成本往往会迅速上升。</p>
<div class="blog_h3"><span class="graybg">选择准则</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">任务特征</td>
<td style="text-align: center;">更合适的基座</td>
<td style="text-align: center;">原因</td>
</tr>
</thead>
<tbody>
<tr>
<td>固定标签分类、匹配、检索、序列标注</td>
<td>BERT 类表示模型</td>
<td>判别目标清晰，推理链短，部署成本低，训练与评估都更直接</td>
</tr>
<tr>
<td>需要开放式回答、格式生成、工具调用、复杂指令遵循</td>
<td>生成式模型</td>
<td>输出本身就是生成任务，Encoder-only 模型不适合作为主干</td>
</tr>
<tr>
<td>多轮对话总结、结尾优先判断、冲突证据加权、规则动态注入</td>
<td>生成式模型</td>
<td>需要跨轮整合与规则执行，往往不能稳定压缩成一次性判别</td>
</tr>
<tr>
<td>局部模式强、业务规则稳定、标注数据充分的分类任务</td>
<td>BERT 类表示模型</td>
<td>表示模型更省资源，延迟更低，也更适合大规模批量预测</td>
</tr>
</tbody>
</table>
<p>因此，基座模型选择的真正问题不是“哪个模型更先进”，而是“任务本质上是在做判别，还是在做规则驱动的语义决策”。前者优先考虑 BERT 一类表示模型，后者通常应直接进入生成式模型范式。把这一步判断做对，后面的训练场景、微调路径和评估方式才会自然收敛。</p>
<div class="blog_h2"><span class="graybg">训练场景</span></div>
<div class="blog_h3"><span class="graybg">小数据集微调</span></div>
<p>小数据集微调（Small-data Fine-tuning）讨论的不是“有没有数据”，而是“可用于更新模型权重的有效监督信号是否足够”。到了 2026 年，这个问题已经出现一个很值得重视的经验转向：在小数据场景里，微调容量并不是越大越好。很多情况下，越轻量的适配反而越稳，尤其当数据本身同时带有长尾、噪声、分布偏移和验证波动时更是如此。</p>
<div class="blog_h4"><span class="graybg">核心判断</span></div>
<p>小数据微调最容易误判的一点，是把“任务复杂”直接等同于“应该开放更多可训练参数”。在大多数小样本任务里，真正需要学习的往往不是重写整套表示空间，而只是沿少数方向对基座模型做任务相关偏移。更大的可训练子空间确实提高了拟合训练集的能力，但也更容易把头部模式、局部模板、错标样本和偶然噪声一起写进参数更新，最终损害泛化。</p>
<p>因此，小样本适配的默认逻辑通常不是“先开大再收缩”，而是<span style="background-color: #c0c0c0;">先用更强约束保护基座模型，再按验证集证据逐步放大更新空间</span>。以 LoRA 为例，低 rank 更新 <span displaypfx="inline-" class="mathjax-container">\(\Delta W = AB\)</span> 的价值不仅是省显存，更是把参数更新限制在一个低维子空间中，使模型更难直接记忆训练集表面模式。这也是为什么在低资源任务里，参数效率与泛化能力往往不是对立关系，而是同一套约束机制的两个结果。</p>
<div class="blog_h4"><span class="graybg">微调什么地方</span></div>
<p>小数据集微调不仅要决定“调多大”，还要决定“调哪里”。这个问题通常有两个维度：第一，更新哪些层；第二，更新每层里的哪些矩阵。它们共同决定模型最终学到的是局部表面模式，还是更稳定的高层语义偏移。</p>
<p>从层位分工看，Transformer 各层的注意力并不是完全同质的。浅层通常更偏局部句法、词性与相邻词关系，通用性最强，因此往往不应轻易扰动；中层开始形成更复杂的语义组合，处理跨句指代、因果联系和较长范围的信息整合；深层则更接近任务特定的高层语义，情感、意图、分类边界和最终决策信号通常更多集中在这里。对于情感判断、满意度判断、意图识别等高层语义任务，优先从中深层，尤其是后部层开始微调，通常更符合信号分布。</p>
<p>从矩阵类型看，小数据行为调整任务通常应先从注意力侧开始，而不是默认同时打开 FFN。Q 决定“向哪里发起关注”，V 决定“实际取出什么信息”，因此它们往往最直接影响模型如何组织证据与整合线索；K 和 O 也会影响结果，但通常不是最低成本起点。若任务主要是在已有知识之上重排注意力优先级、加强长程依赖或改变决策依据，先调 Q、V 往往最稳。只有当验证集持续显示模型确实缺少领域知识写入或表示重编码能力时，再逐步把 FFN、K 或 O 纳入更新范围更合适。</p>
<div class="blog_h4"><span class="graybg">主要风险来源</span></div>
<p>小数据场景的核心矛盾是<span style="background-color: #c0c0c0;">有效监督信号稀疏，模型容量却仍然巨大</span>。如果数据高度重复、标签边界模糊、头部模式占据绝大多数样本，或者训练集与真实线上分布存在偏移，那么模型面对的就不是“少量但可靠的规律”，而是“少量规律 + 大量重复与噪声”。这时，训练往往不是训练不动，而是下降得过快、记忆得过深。</p>
<p>其中最容易被低估的是长尾问题。头部样本会主导梯度方向，新增容量也最容易先被头部模式吸收；结果是总体指标继续上涨，尾部类别、边界样本和罕见情形却未必同步改善，甚至可能恶化。平均 Accuracy 往往会掩盖这种退化，因此小数据微调若不单独观察尾部表现，很容易把“更会处理常见样本”误判成“整体更强”。</p>
<p>实践中，以下信号通常说明当前瓶颈不在容量不足，而在数据质量、长尾覆盖、验证设计或规则表达：</p>
<ul>
<li>训练损失快速下降而验证集停滞。这通常说明模型正在高效记忆训练集，却没有学到可迁移的判别规律，额外容量只会让这种记忆更彻底。</li>
<li>总体指标改善但尾部类别恶化。这说明新增容量主要被头部模式吸收，模型对高频样本更熟练，却以牺牲罕见场景为代价换取平均分提升。</li>
<li>不同随机种子之间波动很大。这意味着当前结果对初始化、数据划分或训练噪声过于敏感，说明监督信号本身不稳定。这里的不稳定，通常就来自数据分布不均、标签质量不足或验证集设计过小，例如头部样本占绝大多数、尾部样本只出现几次，或同类边界样本在不同标注员之间标准并不一致。继续放大可训练空间通常只会把这种不确定性进一步放大。</li>
<li>新增参数带来的提升只集中在头部模式。这表明模型并没有真正学到更普适的决策边界，而是在更深地贴合样本最密集的局部区域。</li>
<li>模型开始出现明显的风格漂移与任务外退化。这往往意味着局部小数据已经开始覆盖预训练先验，模型虽然在当前任务里更贴近训练分布，却损伤了原本更广泛的泛化能力。</li>
</ul>
<div class="blog_h4"><span class="graybg">2026 年的实践建议</span></div>
<p>小数据场景下，更稳妥的默认策略是按证据逐步放开可训练空间：</p>
<ol>
<li>把轻量适配作为默认起点。若使用 LoRA / QLoRA，应优先把它理解为控制更新容量的手段，而不只是省显存工具。</li>
<li>先尝试只调后半层的 Q、V，或更保守地只调最后三分之一层。它的参数量最小，过拟合风险也最低，适合监督极少、验证集波动较大的情况。</li>
<li>若验证集显示模型对长程结构或中层语义组合仍然适配不足，再比较全层 Q、V 的极低 rank 配置。这样做的出发点是：任务信号不一定只存在于最深层，中层也可能承担一部分长程整合与语义组合；给所有层一点点可调空间，有时比只改最后几层更容易保持泛化。</li>
<li>只有当这些方案仍然显示出明确的知识注入或表示重编码瓶颈，再把 K、O 或 FFN 逐步放开。</li>
<li>把验证集当作主导信号，而不是训练损失。小数据场景下，训练集拟合速度通常极快，真正有意义的是验证集是否持续改善，以及尾部样本是否同步获益。</li>
<li>把参数选择和数据问题一起看。若长尾、噪声和分布偏移没有处理好，继续增加微调容量往往只会更快过拟合这些问题。</li>
</ol>
<p>这条顺序的核心是把可训练空间按证据逐步展开，避免在一开始就把过大的自由度交给少量数据，而不是追求一次命中最优结构。更成熟的理解方式，是把小数据集微调视为<span style="background-color: #c0c0c0;">受强约束的增量适配</span>，而不是缩小版的大规模微调。</p>
<div class="blog_h3"><span class="graybg">训练嵌入模型</span></div>
<div class="blog_h4"><span class="graybg">简介</span></div>
<p>嵌入模型（Embedding Model）并不天然等于“通用语义相似度模型”。它可以围绕特定目标进行训练，使向量空间优先保留某一类任务真正关心的判别信号。例如在情感分类（Sentiment Classification）场景里，模型更关心“正面 / 负面 / 中性”的倾向是否一致，而不一定关心两段文本在主题或措辞上是否高度相似。于是，训练得到的嵌入空间可能会把“物流很快，体验很好”和“包装一般，但整体满意”拉得较近，因为它们在情感方向上同属正向；反过来，即使两条评论都在谈“物流”，只要情感倾向相反，也可能被推向更远位置。</p>
<p>从更抽象的角度看，无论目标是情感、相关性、意图、风险、偏好还是图文匹配，嵌入模型始终都在学习一件事：让<span style="background-color: #c0c0c0;">与当前任务定义下“相关”的文档特征在向量空间中更接近，让“不相关”或“应被区分”的特征更远离</span>。区别只在于“相关”的定义来自哪里。通用 embedding 把相关性主要定义为语义相似；任务特化 embedding 则把相关性改写为某个业务目标下的等价关系，例如同一标签、同一情感、同一用户意图、同一风险等级，或“查询与正确答案匹配”。</p>
<p>正因为如此，嵌入训练与对比学习（Contrastive Learning）天然契合。只要能够构造正样本对与负样本对，就可以把任务目标转写成“哪些样本应靠近、哪些样本应分开”的几何约束。监督标签、点击行为、人工偏好、检索点击日志、FAQ 配对、复述句、图文配对，最终都可以落回这一范式：通过对比式目标把任务真正关心的结构写进表示空间。这样得到的 embedding 既可以直接用于最近邻检索、聚类和召回，也可以作为下游分类器或 reranker 的输入表示。Sentence-BERT 就是文本领域最常见的一条对比式嵌入技术路线之一，前文已经展开其结构，这里只把它当作训练范式的代表。</p>
<p>这里尤其要强调<span style="background-color: #c0c0c0;">替代选项（Alternative Option）</span>的重要性。对嵌入训练而言，替代选项本质上就是负样本：模型不只要知道“什么应该靠近什么”，还要知道“它为什么不是另一个看起来也很像的东西”。如果没有负样本，模型最容易学到的是一组宽泛、正确但区分度不足的共有特征；只有把相似但不同的替代选项放进训练过程，表示空间的边界才会真正被压实。</p>
<p>例如，如果想教模型理解“马”这一概念，只告诉它“有嘴巴、有鼻子、四条腿、长尾巴”，这些特征当然不算错，但它们并不能有效区分马和斑马，因为斑马同样满足这些描述。真正有区分度的，反而是“有没有条纹”、更接近哪种奔跑方式、整体体态和纹理模式这类能把两者分开的特征。把“马”和“斑马”作为相互竞争的替代选项放进对比训练后，模型才会被迫降低那些共有特征的权重，转而提升真正决定分类边界的特征权重。这也是为什么高质量负样本往往比继续堆更多正样本更能提升 embedding 的判别力。</p>
<div class="blog_h4"><span class="graybg">训练方式</span></div>
<p>如果把特定目标的 embedding 训练落到一个可执行流程，通常可以分成六步。</p>
<p>但无论流程写得多完整，训练或微调 embedding 模型的主要难点始终都不在 Trainer 本身，而在数据。可用数据不仅要足够大，质量门槛也很高；正例对通常相对容易收集，例如复述句、点击匹配、NLI 蕴含对、FAQ 问答对，但真正困难的是构造高质量的难负例，因为它们既要足够接近真实混淆项，又不能把本该相关的样本误标为负例。</p>
<ol>
<li>构造对比样本。最常见的起点是自然语言推断（Natural Language Inference, NLI）类句对数据，因为它天然提供“哪些句子应该更近、哪些句子应该更远”的监督信号。以蕴含（Entailment）关系作为正例、以矛盾（Contradiction）关系作为负例，是非常常见的做法；中立（Neutral）样本则可按任务目标决定是作为弱负例还是直接舍弃。工程实践里，经常直接使用 SNLI、MNLI 或二者合并后的 AllNLI。GLUE（General Language Understanding Evaluation）则更适合作为上层参照系：它汇总了九个语言理解任务，可用于分析模型在句对理解、推断和相似度相关任务上的整体表现，但并不是把九个任务原样全部转成对比样本。</li>
<li>定义评估器。训练过程不能只看训练损失，还需要一套稳定的验证指标。最常见的选择是 STS-B（Semantic Textual Similarity Benchmark）：它由人工标注句子对相似度，原始标签通常位于 1 到 5 的区间，适合评估句向量是否学到了连续的语义距离。若需要更全面的外部评估，则可以进一步使用 MTEB（Massive Text Embedding Benchmark）一类综合基准，它覆盖多类嵌入任务与大量数据集，能更系统地检查模型在检索、聚类、分类和语义匹配等场景中的迁移能力。</li>
<li>选择基座模型。特定目标的嵌入训练通常从一个现成的 Encoder-only Transformer 开始，例如 microsoft/mpnet-base、BERT、RoBERTa 或其领域变体。若任务更接近通用句向量，也可以直接从已经具备较强句向量能力的基座继续微调；若任务明显偏领域化，则优先选择语料分布更接近业务场景的编码器。</li>
<li>调用 sentence-transformers 进行训练。它提供了从数据集封装、池化、损失函数到 Trainer 的完整流水线。默认情况下，模型参数并不会被自动冻结，整个编码器都会参与更新；虽然也可以手动冻结底层若干层以节省显存或降低训练不稳定性，但对 embedding 任务而言，表示空间往往需要全层共同调整，因此在资源允许时，全量解冻通常比只训练顶部少数层更容易得到更好的句向量。</li>
<li>设置超参数。最关键的超参数通常是训练轮次（Epochs）、批次大小（Batch Size）和学习率预热（Learning Rate Warmup）。批次大小会直接影响 in-batch negatives 的数量，因此不仅关系到吞吐，也关系到对比学习的难度；训练轮次决定模型能否真正把目标关系写入向量空间；预热则用于降低训练初期的梯度震荡，避免刚开始就把预训练表示空间破坏掉。</li>
<li>选择损失函数。若目标是得到高质量 embedding，一般不建议把 SoftmaxLoss 当作默认选项，因为它更偏向“把当前任务做成分类”，而不是直接优化向量空间的几何结构。若手里有连续相似度分数，常用余弦相似度相关目标，例如 CosineSimilarityLoss；若任务是检索、召回或通用句向量训练，MultipleNegativesRankingLoss 往往是默认优先尝试的方案之一，因为它会把同一 batch 中其他样本自然当作负例，直接优化“正确匹配更近、错误匹配更远”的排序关系。不过它并不是所有任务上的统一最优解，最终仍取决于数据格式、负样本质量和任务目标。</li>
</ol>
<p>这六步背后的主线始终一致：先定义“相关”和“不相关”，再把这种关系写进向量空间。任务标签、蕴含关系、点击行为和人工偏好只是构造这种几何关系的不同来源；一旦正负样本定义清楚，训练目标就会自然收敛到对比学习的框架里。</p>
<p>在这条主线里，<span style="background-color: #c0c0c0;">难负例（Hard Negatives）</span> 往往能够显著提升嵌入模型的判别力。随机负例通常太容易，模型很快就能把它们推远；真正能继续塑造决策边界的，往往是那些“表面上很像、但在任务定义下并不相关”的样本。对检索、匹配和语义区分任务而言，负例越接近真实混淆项，模型越容易学到更有区分度的表示。</p>
<p>一个常见的负例收集流程如下：</p>
<ol>
<li>获取简单负例（Easy Negatives）。最直接的方法是从训练集里随机采样文档或句子，和当前 query/anchor 拼成负样本对。这一步成本最低，适合快速建立基础对比信号，但训练到中后期往往会变得过于容易。</li>
<li>获取半难负例（Semi-hard Negatives）。可以先用一个预训练 embedding 模型遍历训练集，为每个样本召回一批“看起来较相似”的候选，再排除真实正例、重复样本和语义等价样本，把剩余候选作为半难负例。这类负例已经靠近当前表示空间的边界，通常比随机负例更能提升检索质量。</li>
<li>获取难负例（Hard Negatives）。更强的做法是人工构造，或借助数据合成（Data Synthesis）生成高混淆样本。例如为同一个 query 人工编写“主题相关但答案错误”的文档，或利用大模型生成与正例高度相似但标签相反、结论错误、实体错配的文本。这样的负例最有训练价值，但也最容易混入假负例（False Negatives），因此质量控制必须更严格。</li>
</ol>
<p>难负例并不是越难越好。若负例实际上与正例同样合理，或者只是标注遗漏导致的“假负例”，模型就会被迫把本应接近的样本推远，反而损害 embedding 空间的结构。真正有效的 hard negative，是对模型足够难、但在任务定义下又明确应该分开的样本。</p>
<div class="blog_h3"><span class="graybg">特定目标嵌入微调</span></div>
<p>从头训练嵌入模型（Embedding Model）通常不是多数团队的首选路径。原因并不神秘：它既需要大规模高质量句对或 query-document 数据，也需要持续的负例挖掘、评估基准建设和较长训练周期；若多语言还要兼顾长文本、跨语言检索和任务泛化，成本会进一步抬升。对绝大多数工程团队而言，更高效的做法是从一个已经具备稳定表示空间的基座继续微调（Fine-tuning），把现有 embedding 几何结构朝业务目标方向“推一小步”，而不是从零发明一整个向量空间。</p>
<p>这也是为什么 embedding 项目的真正瓶颈往往不在“有没有训练框架”或“能不能跑通微调”，而在于能否拿到足够多、足够干净、又足够贴近业务分布的数据。正例对开发相对直接，难的是负例设计，尤其是高价值 hard negatives：它们决定模型能否学会区分那些最容易混淆、最接近真实线上错误的样本。</p>
<p>Sentence-BERT（SBERT）路线的实用价值就在这里体现得很明显：它并不要求必须从头构建一个新的 embedding 模型，而是允许直接以现有 SentenceTransformer 模型或预训练编码器为基础继续训练。这样做的收益有两个。第一，预训练阶段已经学到大量通用语言结构，微调只需要重塑与当前任务最相关的距离关系。第二，训练资源会集中花在“任务适配”而不是“重新学习基本语言知识”上，因此更适合企业内部检索、垂直领域分类、多语言 RAG 和跨语言匹配这类目标明确的场景。</p>
<p>实际选基座时，不应只看单一榜单名次，而应同时看四个因素：语言覆盖、上下文长度、是否指令化（Instruction-aware）、以及微调成本。榜单可以帮助筛掉明显过时的模型，但真正决定工程效果的，往往是“它是否与你的数据形态和训练预算匹配”。截至 2026 年 3 月，下面几类开源基座尤其值得优先考虑，尤其是在多语言场景中。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">基座</td>
<td style="text-align: center;">多语言能力</td>
<td style="text-align: center;">核心特点</td>
<td style="text-align: center;">更适合的微调目标</td>
</tr>
</thead>
<tbody>
<tr>
<td>Qwen3-Embedding-8B / 4B / 0.6B</td>
<td>100+ 语言</td>
<td>当前公开多语言榜单前列的强基座；支持指令感知、最长 32K 上下文、可自定义输出维度</td>
<td>多语言检索、跨语言召回、长文档检索、代码与自然语言混合语料</td>
</tr>
<tr>
<td>BGE-M3</td>
<td>100+ 语言</td>
<td>同时支持 dense / sparse / multi-vector；最长 8192 token；对混合检索和 RAG 结构非常友好</td>
<td>多语言 RAG、混合检索、长文档场景、需要兼容 BM25 风格稀疏信号的系统</td>
</tr>
<tr>
<td>multilingual-e5-large-instruct</td>
<td>94 语言</td>
<td>指令式 embedding 路线成熟；query 端显式带任务描述；体量相对可控</td>
<td>任务定义清晰的检索、问答召回、跨语言语义匹配</td>
</tr>
<tr>
<td>jina-embeddings-v3</td>
<td>多语言；重点调优 30 种语言</td>
<td>8192 token 长上下文；内置任务 LoRA 适配器；支持 Matryoshka 截断维度</td>
<td>一套基座服务多任务、需要分类 / 检索 / text-matching 共用底座的系统</td>
</tr>
</tbody>
</table>
<p>若强调“当前最强的开源多语言底座”，Qwen3-Embedding 系列是首先应试的对象；若强调“检索形态复杂、需要 dense + sparse + rerank 协同”，BGE-M3 的工程灵活性仍然非常突出；若更在意成熟的指令式 query-document 训练范式，multilingual-E5-large-instruct 依然是很稳的起点；若希望在同一基座上兼顾多任务并降低任务切换成本，jina-embeddings-v3 的任务适配设计更有吸引力。</p>
<p>因此，特定目标的嵌入微调并不是“随便挑一个 embedding 模型然后继续训”。更合理的顺序是：先根据任务选择合适的基座拓扑，再设计正负样本与评估器，最后用微调把表示空间朝业务目标压缩。对今天的大多数团队来说，真正的竞争力很少来自“从零训练一个全新 embedding 模型”，而更多来自“是否用合适的底座，把微调目标、负例设计和评测体系做对”。</p>
<div class="blog_h3"><span class="graybg">基于少量数据的嵌入微调</span></div>
<p>在标注数据非常有限的场景里，增强型 SBERT（Augmented SBERT）提供了一条经典而务实的路径：用少量高质量标注，扩展出一套足够大的嵌入训练集。它利用的是双编码器（Bi-Encoder）与交叉编码器（Cross-Encoder）的互补性：双编码器推理快、适合检索，但通常需要较多训练数据；交叉编码器推理慢，却能在句对打分上提供更高精度。因此，可以先让交叉编码器学会目标任务，再利用它为大量未标注句对生成伪标签，最后反过来训练一个可高效部署的 SBERT。</p>
<p>这个方法的关键不在于引入全新的模型结构，而在于重新组织数据生产流程。少量人工标注但可靠的数据，构成黄金数据集（Gold Dataset）；由交叉编码器离线打标生成的大规模伪标签数据，构成白银数据集（Silver Dataset）。黄金数据集负责提供可信监督，白银数据集负责放大覆盖面。两者组合后，就能把“标注稀缺”的问题转化成“高精度慢模型辅助生成训练信号”的问题。</p>
<p>因此，增强型 SBERT 本质上是一种<span style="background-color: #c0c0c0;">低数据场景下的数据增强（Data Augmentation）与知识蒸馏（Knowledge Distillation）</span>策略：先用少量黄金数据把交叉编码器调准，再让交叉编码器把自己的句对判断能力迁移给双编码器。最终得到的不是一个更慢的模型，而是一个依然可以做大规模向量检索、但在目标任务上明显更强的嵌入模型。</p>
<div class="blog_h4"><span class="graybg">核心流程</span></div>
<p>增强型 SBERT 的整体流程可以概括为四步。</p>
<ol>
<li>先用少量黄金数据集微调交叉编码器。这里的黄金数据通常规模不大，但标签质量高，足以让交叉编码器学会“在当前任务里什么样的句对应该更相似，什么样的句对应该更远”。</li>
<li>再生成一批新的候选句子对。这一步既可以来自额外的未标注语料，也可以从现有语料中重新组合样本，目的是构造一个远大于黄金数据集的候选池。</li>
<li>然后让已经微调好的交叉编码器为这些候选句子对打分，生成白银数据集。这里的标签不是人工真值，而是高精度模型给出的伪标签，因此质量通常高于简单启发式规则，却又远比全人工标注便宜。</li>
<li>最后用“黄金数据集 + 白银数据集”一起训练双编码器。这样训练出来的 SBERT 保留了双编码器可预编码、可缓存、可做向量检索的速度优势，同时通过白银数据学到了更多与目标任务一致的距离关系。</li>
</ol>
<div class="blog_h4"><span class="graybg">白银数据集如何构造</span></div>
<p>白银数据集的质量，决定了增强型 SBERT 能否真正成立。若手里本来就有大量未标注句对或 query-document 数据，最直接的做法就是把它们交给交叉编码器离线标注，再转成伪标签训练集。这是最标准也最稳定的路径，因为候选样本来自真实语料分布，白银数据更接近真实任务。</p>
<p>如果没有现成的大规模未标注句对，也可以从现有黄金数据出发构造更多候选样本。例如把不同句子的前半部分与后半部分重新组合，或把不同 query 与 candidate 文档重新配对，生成新的候选对。但纯随机组合通常会制造过多明显不相似的负例，导致数据分布过于偏斜，模型学到的主要是“轻松区分非常不像的样本”，而不是处理真正困难的边界。</p>
<p>因此，更有效的做法通常是先用一个预训练 embedding 模型做粗检索：为每个句子或 query 召回若干看起来较相似的候选，再把这些候选送给交叉编码器做精标。这样生成的白银数据会包含更多“高混淆但仍可判别”的样本，训练价值明显高于随机拼接。换句话说，预训练 embedding 在这里不负责给出最终标签，而是负责提高候选样本的质量；真正的伪标签仍然由交叉编码器给出。</p>
<div class="blog_h4"><span class="graybg">为什么适合少量数据微调</span></div>
<p>增强型 SBERT 的价值，在低资源场景下尤其明显。少量黄金数据本身往往不足以直接把 SBERT 微调到理想状态，因为双编码器更依赖足够多的成对训练信号去塑造向量空间；但这同一小批黄金数据，往往已经足够把交叉编码器调成一个“能较准打分”的教师模型。之后，只要有额外候选样本，教师模型就能持续扩充白银数据集，从而把训练信号放大很多倍。</p>
<p>这也是它与普通监督微调的根本差别：普通微调直接把少量标注样本喂给双编码器；增强型 SBERT 则多加了一层“教师打标”环节，用交叉编码器把少量高质量监督扩展成大量可用监督。因此，它特别适合文本匹配、语义检索、问答匹配、句子对排序等 pairwise sentence scoring 任务。</p>
<div class="blog_h4"><span class="graybg">边界与适用条件</span></div>
<p>增强型 SBERT 并不是“数据越少越万能”。它仍然要求黄金数据足够准确，否则交叉编码器会先学歪，再把错误批量复制到白银数据里。它也要求候选样本池与真实任务分布足够接近，否则伪标签再多，也只是把错误分布放大。更关键的是，白银数据集并不能替代最终评估：真正决定模型是否可用的，仍应是人工标注的黄金验证集与测试集。</p>
<p>因此，这条路线最适合的场景不是“完全没有数据”，而是“有少量高质量标注、但不足以直接训练出强双编码器”。在这种情况下，增强型 SBERT 提供了一条非常自然的过渡路径：先用高精度慢模型吸收黄金数据，再把这种判断能力蒸馏成一个推理高效的嵌入模型。</p>
<div class="blog_h3"><span class="graybg">无监督嵌入模型训练</span></div>
<div class="blog_h4"><span class="graybg">过渡背景</span></div>
<p>增强型 SBERT 已经把监督数据需求压得很低，但它仍然依赖少量黄金数据去微调交叉编码器。再往前推进一步，现实里还存在更苛刻的场景：没有人工标注句对，甚至没有可靠的点击日志、排序日志或问答配对数据。此时，嵌入模型训练只能转向无监督学习（Unsupervised Learning）或更准确地说，自监督学习（Self-supervision）：训练信号不再来自显式标签，而来自原始语料自身的结构、扰动视图（Augmented Views）或重建目标。</p>
<p>因此，无监督嵌入模型训练处理的是“零标注”条件下的表示学习问题。它并不是放弃监督，而是把监督信号改写为模型可以从原始文本中自动构造出的约束：哪些表示应该在扰动前后保持一致，哪些句子应被视为同一语义对象的不同视图，哪些带噪输入必须恢复到原始句子。这类方法尤其适合冷启动和领域适配，因为垂直领域最容易获得的往往不是标签，而是大量原始文本。</p>
<div class="blog_h4"><span class="graybg">主流路线</span></div>
<p>无监督嵌入训练的核心目标仍然是学习一个有判别力的句向量空间，只是实现方式不同。当前常见路线可以概括为四类。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">方法</td>
<td style="text-align: center;">全称</td>
<td style="text-align: center;">核心原理</td>
<td style="text-align: center;">特点</td>
</tr>
</thead>
<tbody>
<tr>
<td>SimCSE</td>
<td>Simple Contrastive Learning of Sentence Embeddings</td>
<td>把同一句子在不同 dropout 下得到的表示视为正例，用 batch 内其他句子作负例做对比学习</td>
<td>结构极简，是无监督句嵌入的经典标杆路线</td>
</tr>
<tr>
<td>CT</td>
<td>Contrastive Tension</td>
<td>通过对比张力机制重新调整预训练表示，使语义相近样本更聚合、无关样本更分离</td>
<td>强调对预训练表示的语义重调</td>
</tr>
<tr>
<td>TSDAE</td>
<td>Transformer-based Sequential Denoising Auto-Encoder</td>
<td>先破坏原句，再要求模型从带噪输入重建原句，逼迫编码器学习句级语义压缩</td>
<td>在无监督训练和领域适配中都非常强，是这一节的重点</td>
</tr>
<tr>
<td>GPL</td>
<td>Generative Pseudo-Labeling</td>
<td>从无标签语料出发自动生成 query 与伪标签，再训练 dense retriever</td>
<td>更接近弱监督，但非常适合无人工标注的检索场景</td>
</tr>
</tbody>
</table>
<p>这些方法的共同点是：都在尝试从原始文本中自动制造训练信号；差别在于信号是来自对比、重建，还是生成式伪标签。无监督训练真正的难点依然是数据，只不过难点从“标注是否充足”转移成“自监督信号是否有效”。如果正样本过于容易、负样本过于随机，模型学到的往往只是表面相似性；若扰动方式破坏了核心语义，模型又会被迫学习错误的不变性。</p>
<div class="blog_h4"><span class="graybg">TSDAE</span></div>
<p>TSDAE（Transformer-based Sequential Denoising Auto-Encoder）是无监督句嵌入中非常重要的一条路线，尤其适合领域适配（Domain Adaptation）。它的核心思想可以概括为“破坏 - 重建”：先对原始句子加噪，例如随机删除部分词、打乱局部结构或施加其他轻度破坏；再让模型从这个带噪版本重建原始句子。模型若想完成这项任务，就不能只记住局部 token，而必须把句子的整体语义压缩到编码表示中。</p>
<p>TSDAE 与掩码语言建模（Masked Language Modeling, MLM）的差别也很关键。MLM 主要学习“根据上下文补出被遮住的词”，重点仍然是词级预测；TSDAE 学习的是“从受损输入恢复整个句子”，目标天然更偏句级语义表示。因此，TSDAE 训练出来的编码器更容易直接拿来生成句嵌入，而不必再从词级预测目标里间接提炼句向量。</p>
<p>这条路线在垂直领域里尤其有效。原因是领域适配最常见的现实条件正是：有大量目标域原始文本，但缺乏成对标注数据。TSDAE 可以先在这些无标签文本上训练一个更贴近目标领域分布的编码器，把通用嵌入空间向领域语义挪动；随后若再获得少量黄金数据，就可以继续用常规 SBERT 或增强型 SBERT 做最后一步任务对齐。</p>
<div class="blog_h4"><span class="graybg">领域适配闭环</span></div>
<p>从工程角度看，最有价值的是把“监督训练”“少样本微调”“无监督训练”看成一条连续路线，而不是彼此割裂。标注充足时，直接做监督训练；标注很少时，用增强型 SBERT 放大黄金数据；标注几乎为零但有大量原始语料时，先做 TSDAE 或类似无监督适配，再在后续叠加少量监督微调。这样就形成了一个完整闭环：覆盖有监督、低监督到零监督三种数据条件。</p>
<p>因此，无监督嵌入训练并不天然优于监督微调。它的价值在于为 embedding 模型打地基，并在领域迁移时先把表示空间校正到目标语料分布附近。若后续能够获得少量高质量黄金数据，再叠加监督微调，通常会比单独依赖无监督训练更稳，也更接近真实业务目标。</p>
<div class="blog_h3"><span class="graybg">表示模型继续预训练</span></div>
<p>表示模型继续预训练（Continued Pretraining）处理的是这样一种典型情况：基座表示模型已经在通用开放语料上完成预训练，例如互联网文本、维基百科、新闻语料或大规模网页数据，因此具备稳定的通用语言能力；但它并不天然掌握特定领域知识，例如医学术语、金融表述、法律条文、企业内部缩写和业务语境。此时，问题往往不在于模型“不会语言”，而在于模型虽然懂通用语言，却还不够懂目标领域。</p>
<p>这正是继续预训练的切入点。传统 BERT 路线通常只有两阶段：先在通用语料上做预训练，再直接在下游分类或序列标注任务上微调。继续预训练在两者之间插入了一个新的中间层，形成<span style="background-color: #c0c0c0;">通用预训练 → 领域继续预训练 → 下游任务微调</span>的三阶段流程。它的目标不是立刻输出分类结果，而是先用目标领域的无标注文本，重新校正编码器的词汇分布、上下文统计与语义偏好，让表示空间先贴近领域，再去做具体任务。</p>
<div class="blog_h4"><span class="graybg">为什么需要继续预训练</span></div>
<p>通用预训练模型对“movie”“doctor”“interest”“appeal”这类词的理解，默认来自开放语料中的统计分布；一旦进入垂直场景，这些词的含义和搭配方式可能会明显变化。医学文本中的 drug、lesion、metastasis，金融文本中的 guidance、yield、hedging，法律文本中的 plaintiff、statute、liability，都承载着更窄、更稳定、更专业的语境。如果直接拿通用模型去做下游微调，模型往往会在专业术语、长尾表达和领域共现关系上吃亏。</p>
<p>继续预训练的价值就在于：它允许模型先用领域无标注数据补上这层知识，再进入任务微调阶段。因此它本质上是一种领域自适应预训练（Domain-Adaptive Pretraining, DAPT）策略。医疗领域的 BioBERT、金融领域的 FinBERT、法律领域的 LawBERT，本质上都属于这一路线的不同落地版本。</p>
<div class="blog_h4"><span class="graybg">核心训练任务</span></div>
<p>对 BERT 一类 Encoder-only 表示模型而言，继续预训练最经典的目标仍然是掩码语言建模（Masked Language Modeling, MLM）。它可以理解成一种受控的“完形填空”：随机选择输入序列中约 15% 的 token 作为预测目标，其中 80% 替换成 [MASK]，10% 替换成随机 token，剩余 10% 保持不变，但仍要求模型预测原始 token。这样做的目的，是迫使模型根据双向上下文恢复被遮蔽的信息，从而继续学习领域语料中的词汇、搭配和上下文统计。</p>
<p>在继续预训练场景中，MLM 的关键价值不在于“学会猜词”本身，而在于让词向量与上下文表示持续向领域分布靠拢。通用 BERT 看到 What a horrible [MASK]! 时，可能只学到通用情绪词与常见名词；若继续在影评语料上做 MLM，它会更容易把 horror、ending、premise、performance 这类领域表达织进表示空间。医学、金融、法律乃至企业内部文档都是同样的道理。</p>
<div class="blog_h4"><span class="graybg">训练流程</span></div>
<p>如果把表示模型继续预训练落到一个可执行的理论流程，通常可以分成六步。</p>
<ol>
<li>确定基础模型。起点通常是一个已经完成通用预训练的表示模型，例如 BERT、RoBERTa、DeBERTa 或其领域相近变体。这里不需要从头训练模型，而是直接继承已有语言能力。</li>
<li>收集目标领域的无标注语料。继续预训练最重要的资源不是标签，而是高质量领域文本。它既可以来自公开领域语料，例如 PubMed 医学文献、金融新闻、法律判例，也可以来自企业内部数据，例如业务文档、客服对话、知识库与工单记录。</li>
<li>完成分词与语料预处理。继续预训练阶段通常不再保留下游标签，只保留原始文本并转换成模型输入序列。此时最重要的是保证文本清洗、截断策略、特殊符号处理与分词器保持一致，因为模型更新的是对领域文本分布的内部表示，而不是标签映射。</li>
<li>选择掩码策略。最基础的是词元掩码（Token Masking），即对子词粒度随机掩码；若希望模型更充分学习完整术语和专业表达，也可以使用整词掩码（Whole Word Masking, WWM），让一个完整单词的所有子词同时被遮蔽。整词掩码通常训练更难、收敛更慢，但在领域术语密集的场景下更有价值。</li>
<li>执行继续预训练。用领域无标注语料继续运行 MLM 目标，让模型参数在不丢失通用语言能力的前提下，逐步适配目标领域。这个阶段更新的不是分类头，而是整个编码器本身，因此它更像“重塑表示空间”，而不是“学习某个具体任务标签”。</li>
<li>切换到下游微调。等继续预训练完成后，再把更新后的表示模型接到具体任务上，例如文本分类、语义搜索、命名实体识别（Named Entity Recognition, NER）或关系抽取。此时，下游微调面对的已经不是通用基座，而是一个更懂目标领域语境的表示模型。</li>
</ol>
<div class="blog_h4"><span class="graybg">企业级场景</span></div>
<p>继续预训练在企业内部尤其有价值。很多企业并不缺文本，而是缺可公开复用的标注数据。客服对话、知识库、工单、合同、操作手册、会议纪要、内部 wiki，这些数据天然带有组织级语境和术语。如果直接把通用模型拿去做企业任务微调，模型经常会在缩写、术语和内部表述上显得迟钝；但若先用这些无标注内部数据继续预训练，再做客服主题分类、语义搜索或实体识别，下游效果通常会明显更稳。</p>
<p>因此，继续预训练最适合的不是“领域知识已经充分内化到通用模型”的场景，而是“目标领域有大量原始文本，但通用模型对其语境仍然陌生”的场景。它本身不是终点，而是一个连接通用语言能力与领域任务能力的中间适配层。</p>
<div class="blog_h3"><span class="graybg">分类目标表示模型微调</span></div>
<p>除了直接训练 embedding 模型，另一条非常常见的路线是围绕分类目标（Classification Objective）微调表示模型（Representation Model）。这类方法的基本结构是：以预训练编码器作为基座，在顶层接一个专用分类头（Classification Head），然后用分类损失共同优化表示与决策边界。它适用于情感分类、主题分类、风险识别、意图分类等闭集标签任务，目标不是学习一个通用距离空间，而是让表示尽可能服务于当前分类边界。</p>
<p>在这种设定下，基座模型参数既可以冻结（Freeze），也可以与分类头一起更新。冻结时，训练只发生在分类头，优点是显存占用小、训练稳定、对小数据集更保守；缺点是表示空间几乎不变，模型只能在既有语义表征上学习一个浅层决策边界。若不冻结，则分类头与基座参数会在训练中<span style="background-color: #c0c0c0;">协同进化</span>：分类头不断把梯度传回编码器，编码器又持续调整自己的表示方式去配合分类目标，最终得到更贴近任务边界的内部表征。这通常能带来更高上限，但也更依赖数据质量、学习率设置和正则化控制。</p>
<p>从经验上看，把基座模型全部冻结后，分类微调效果通常会明显受限，尤其当任务分布与预训练语料存在偏移时更是如此。资源受限时，部分解冻（Partial Unfreezing）是一种常见折中：只更新分类头往往太弱，而全量解冻又可能超出显存或训练预算。在某些具体实验里，只解冻少数几个 Transformer 模块就已经能得到足够好的结果。对文本分类而言，更常见、更合理的做法通常是优先解冻靠后的高层模块，因为后层表示更接近任务语义与决策边界；前层更偏向词法与局部句法特征，保留冻结状态往往问题不大。若任务与预训练域差异很大，或输入风格明显特殊，再考虑进一步向下解冻更多层。换句话说，部分解冻的关键不在于固定解冻“哪 N 层”，而在于把有限资源优先用在最可能影响任务边界的高层表示上。</p>
<div class="blog_h4"><span class="graybg">训练流程</span></div>
<p>以烂番茄影评数据集（Rotten Tomatoes Movie Review Dataset）的情感分类为例，分类目标表示模型微调通常可以拆成六步。</p>
<ol>
<li>选择任务与数据集。烂番茄影评数据集是二分类情感任务：输入是一段影评文本，输出是正面或负面标签。它非常适合说明“表示模型 + 分类头”这一路线，因为情感边界往往依赖整体语义与局部措辞共同决定。</li>
<li>加载数据并完成划分。最基本的划分是训练集（Training Set）与测试集（Test Set）；若训练流程中还需要调超参数或做早停，则还应额外保留验证集（Validation Set）。这里的重点不是机械切分比例，而是保证测试集不参与模型选择，从而让最终分类指标具有解释价值。</li>
<li>加载基座模型与分词器（Tokenizer）。基座通常选择预训练 Transformer 编码器，例如 BERT、RoBERTa、DeBERTa 或更轻量的蒸馏版本；分词器负责把原始文本转成 token 序列、attention mask 以及模型可接收的输入张量。模型与 tokenizer 必须配套，因为词表、特殊 token 和预训练时的文本规范共同决定了输入表示。</li>
<li>进行分词与样本编码。文本在进入模型前需要完成截断（Truncation）、编码与必要的长度控制。这个阶段的目标不是简单把字符串变成整数，而是把“原始语言序列”转换成“可被编码器稳定处理的张量化输入”。序列最大长度、是否保留句首句尾、以及是否对长文本做裁剪策略，都会直接影响分类效果。</li>
<li>构建专用数据整理器（Data Collator）。它负责把长度不一的样本动态组织成批次（Batch），例如按当前 batch 的最长序列做填充（Padding），并同步构建 attention mask，保证同一批次内张量形状一致。更进一步，数据整理器也可以承载轻量数据增强策略，例如随机裁剪、句段保留或噪声注入；但它的最基本职责仍然是稳定、高效地完成批次构造，而不是单纯“把数据拼起来”。</li>
<li>定义评估指标函数。分类目标的训练不仅需要损失函数，还需要一组与业务目标对齐的评估指标。最基本的是 Accuracy；若类别不平衡或更关心误报 / 漏报，则通常还要同时看 Precision、Recall 与 F1。对情感分类这类任务，宏平均 F1（Macro-F1）往往比单独 Accuracy 更能反映模型是否真正学到了稳定的标签边界。</li>
</ol>
<p>这种训练路线与前文 embedding 微调的根本区别在于优化目标。embedding 微调强调“让相关样本在向量空间中更近，让无关样本更远”；分类目标微调强调“让表示空间直接服务于当前标签决策”。前者更适合检索、聚类、匹配与召回，后者更适合闭集判别任务。实际工程中，两条路线并不冲突：很多系统会先训练或微调一个较强的 embedding / encoder 基座，再在其上叠加分类头完成特定标签任务。</p>
<div class="blog_h3"><span class="graybg">少量样本分类目标微调</span></div>
<p>少量样本分类目标微调（Few-shot Classification Fine-tuning）处理的是另一类很常见的现实约束：标签体系已经明确，但每个类别只有极少数标注样本，往往只有 8、16、32 或几十个例子。这类场景下，直接按常规监督流程全量微调一个分类模型，很容易过拟合到表面措辞或偶然噪声；真正有效的方法通常不是“把普通微调硬做小”，而是换用对低样本更友好的训练机制。</p>
<p>少样本分类本质上仍然属于监督学习，只是把“每类有大量标注样本”的常规设定，收缩成“每类只有极少量高质量样本”的稀缺设定。它特别适合标注成本高、样本获取慢、但标签体系明确的任务，例如小众领域文本分类、垂直领域情感分析、专业工单归类或内部知识标签识别。少样本方法的核心，不是让模型凭空学会分类，而是尽可能榨出每一条标注样本中的监督信号。</p>
<div class="blog_h4"><span class="graybg">SetFit</span></div>
<p>SetFit（Sentence Transformer Fine-tuning）是少样本文本分类里最实用的一条路线之一。它基于 sentence-transformers 生态构建，但并不直接把少量样本喂给一个普通分类器，而是先把这些样本改写成大量句子对训练信号，再用两阶段流程完成分类。它的核心价值在于：仅靠极少量标注样本，就能让嵌入模型学到任务相关的类别结构，随后再用一个很轻量的分类头完成判别。</p>
<p>SetFit 的完整流程可以概括为三步：</p>
<ol>
<li>采样训练数据。先基于少量原始标注样本构造句子对：同一类别下任意两个文本组成正例，不同类别下的文本组成负例。这样一来，即使每类只有 2 到 8 条样本，也能通过类内 / 类间组合迅速扩展出大量训练对。少样本的关键不再只是“原始样本有多少”，而变成“能否从这些样本中构造出足够有判别力的相似 / 不相似关系”。</li>
<li>微调嵌入模型。利用这些正负句子对，对预训练的 Sentence Transformer 做对比学习微调。正例要求模型把同类文本的句向量拉近，负例要求模型把异类文本推远。这个阶段优化的是表示模型本身，也就是 SetFit 的 body。</li>
<li>训练分类器。等嵌入模型被调到更适配当前任务后，再用这些高质量句向量作为特征，训练一个轻量分类头（head），例如逻辑回归、线性分类器或其他简单监督分类器。最终推理时，文本先被编码成嵌入，再由分类头输出类别概率。</li>
</ol>
<p>这套设计的本质是两阶段训练：第一阶段先让嵌入空间学会“同类靠近、异类远离”；第二阶段再在这个已经整理过的表示空间里学习分类边界。相比直接对大模型做全参数分类微调，这种路线对小样本更稳定，也更节省资源。</p>
<p>SetFit 的优势主要体现在四个方面。第一，少样本效率高：每类只需极少数标注样本，就可能逼近常规大数据分类微调的效果。第二，无需提示词：它不像提示式 few-shot 方法那样依赖 prompt 或 verbalizer 设计。第三，训练成本低：大部分计算都集中在句向量微调和轻量分类头训练上。第四，数据利用率高：通过句子对采样，有限标注被最大化放大。</p>
<p>以烂番茄影评（Rotten Tomatoes）这类二分类情感任务为例，SetFit 的典型做法是先按类别均衡抽取极少量样本，例如每类 16 条；之后通过正负配对把几十条原始样本扩展成上千个训练对，再完成对比学习与分类头训练。这个例子最能说明 SetFit 的关键巧思：真正被放大的不是“文本本身”，而是文本之间的监督关系。</p>
<p>SetFit 也有边界。它最适合短文本、句子级、闭集标签明确的分类任务；若类别语义高度重叠、任务严重依赖复杂推理，或标签本身更像开放式生成目标，那么单纯依赖句向量空间分离的办法未必最优。在这些场景里，提示式 few-shot 或更强的生成式模型可能更有优势。</p>
<div class="blog_h4"><span class="graybg">提示式 Few-shot 微调</span></div>
<p>另一大类主流方案是提示式（Prompt-based）few-shot 微调，其代表方法包括 PET（Pattern-Exploiting Training）和 LM-BFF（Better Few-shot Fine-tuning of Language Models）。它们不再直接把分类任务看成“输入文本 → 标签 id”，而是把任务改写成 cloze 风格或自然语言提示，让预训练语言模型去预测标签词（label words）或完成模式匹配。PET 的特点是利用 prompt 把少样本监督对齐到语言模型原本更擅长的预训练目标，并进一步用少量标注模型为未标注样本分配软标签；LM-BFF 则把 prompt 搜索、label word 选择和 demonstration 设计系统化，以提高 few-shot 稳定性。</p>
<p>这类方法在标签语义清晰、prompt 设计得当时非常强，但工程代价通常高于 SetFit。它们更依赖 prompt 模板、verbalizer 质量以及不同随机种子的稳定性，迁移到新任务时也往往需要额外搜索和调参。换句话说，提示式 few-shot 的上限很高，但工程摩擦也更大。</p>
<div class="blog_h4"><span class="graybg">参数高效微调</span></div>
<p>第三类主流路线是参数高效微调（Parameter-Efficient Fine-Tuning, PEFT），例如 LoRA、IA3、Prefix Tuning、Prompt Tuning 等。它们的共同点是：大部分预训练参数保持冻结，只训练一小部分新增参数或适配器参数，从而显著降低显存与存储成本。这类方法更直接解决“模型太大，如何降低微调成本”的问题，因此在大基座模型上尤其有价值。</p>
<p>PEFT 与 SetFit 的关注点并不相同。SetFit 解决的是“样本太少，如何更高效地榨出监督信号”；PEFT 更直接解决“模型太大，如何降低微调成本”。在少样本分类里，二者并不互斥：完全可以把少样本策略与参数高效策略叠加。例如，当基座模型较大、显存非常紧张时，可以优先采用 LoRA / IA3 这类方法；若样本极少且更看重训练稳定性与部署成本，则 SetFit 往往是更直接的起点。</p>
<div class="blog_h4"><span class="graybg">如何选型</span></div>
<p>如果任务是典型的短文本或句子级闭集分类，且每类只有极少样本，SetFit 往往是首选起点，因为它训练快、对样本效率高、工程上也最直接。若任务标签本身具有很强自然语言语义，且团队愿意投入 prompt 设计与搜索成本，PET / LM-BFF 这类提示式 few-shot 往往有更高上限。若主要矛盾不是样本太少，而是模型太大、显存和部署预算太紧，则应优先考虑 LoRA、IA3 或其他 PEFT 方案。实际系统中，最稳妥的做法通常不是先争论“哪条路线绝对最好”，而是先判断当前瓶颈究竟是样本、算力，还是 prompt 工程复杂度。</p>
<div class="blog_h3"><span class="graybg">生成模型高效微调</span></div>
<p>生成模型高效微调（Parameter-Efficient Fine-Tuning for Generative Models）面向的是 Decoder-only 大语言模型（Large Language Model, LLM）的指令对齐与领域适配场景。它处理的不是“如何训练一个分类器”或“如何训练一个检索向量空间”，而是如何在显存、训练时间和存储预算都有限的条件下，让基座生成模型学会遵循指令、稳定输出目标格式，并吸收特定领域的表达习惯。对绝大多数 7B、13B 乃至更大规模的开源生成模型而言，基于 QLoRA 的 PEFT 已经成为默认起点。</p>
<div class="blog_h4"><span class="graybg">适用场景</span></div>
<p>这一路线最适合三类任务。第一类是指令微调（Instruction Tuning），即让基座模型从“擅长续写”转向“稳定遵循用户指令”；第二类是风格或格式约束，例如客服回复风格、结构化 JSON 输出、企业内部答复模板；第三类是轻量领域适配，例如让模型更熟悉某个组织、行业或产品线的术语与常见问答模式。若主要瓶颈是显存不足，QLoRA 往往优于全量微调；若目标是深度改写模型底层知识、继续预训练大规模领域语料，或修改上下文长度、词表与位置编码等底层结构，则继续预训练或全参数微调仍然更合适。</p>
<div class="blog_h4"><span class="graybg">训练流程</span></div>
<p>从理论上看，基于 QLoRA 的生成模型高效微调通常遵循六步流程。</p>
<ol>
<li>确定基座模型与任务目标。基座一般选择已经具备稳定因果语言建模能力的生成模型，例如 Llama、Qwen、Mistral 或 TinyLlama 一类架构；任务目标则通常是监督微调（Supervised Fine-Tuning, SFT）意义上的指令跟随，而不是从头学习语言能力。</li>
<li>构造高质量指令数据。训练样本通常采用<span style="background-color: #c0c0c0;">instruction / input / output</span>三段式结构，或更一般的多轮对话消息结构。这里最关键的不是样本数量本身，而是格式与质量：用户角色、助手角色、轮次边界、结束标记、系统提示词都必须稳定一致。若基座模型已经绑定特定 chat template，就应沿用同一模板组织训练语料；否则模型学到的首先不是任务能力，而是混乱的对话边界。实际工程里，过滤后的 UltraChat 风格指令对话之所以常被拿来做示例，核心原因也正在于此：它提供了相对稳定、结构清晰的监督信号。</li>
<li>以低比特方式加载基座。QLoRA 的“Q”来自量化（Quantization）：基座权重以 4-bit 形式存储，常见配置包括 NF4、双重量化以及 BF16 / FP16 计算精度。这样做的目的不是改变模型结构，而是把静态权重占用压到足够低，使更大模型能够在有限显存中完成微调。NF4 尤其适合这一场景，因为它针对 Transformer 权重常见的零中心、近似正态分布做了量化设计。</li>
<li>挂接 LoRA 适配器。QLoRA 的训练对象不是量化后的基座权重，而是附加在目标投影层上的低秩适配器参数。实践中通常会把 LoRA 挂到注意力层的 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> / <span displaypfx="inline-" class="mathjax-container">\(k\)</span> / <span displaypfx="inline-" class="mathjax-container">\(v\)</span> / <span displaypfx="inline-" class="mathjax-container">\(o\)</span> 投影上，必要时再扩展到 MLP 的 up / down / gate 投影。这样得到的是“冻结量化基座 + 可训练低秩分支”的结构：原模型保留通用语言能力，增量参数专门负责吸收任务相关行为。</li>
<li>执行监督微调。训练过程本质上仍然是自回归下一个 token 预测，只是训练语料已经被改写为指令遵循格式。由于显存约束依然存在，单卡 batch size 往往较小，因此通常依靠梯度累积（Gradient Accumulation）来获得更合理的等效批大小；学习率调度常采用 warmup 后接 cosine 衰减；优化器常配合分页优化器以压低峰值显存；最大序列长度则决定模型一次看到多少上下文，也直接决定训练成本。</li>
<li>导出训练产物。训练完成后，最常见的保存形式有两种：一种是只保存 LoRA 适配器，部署时与同一基座模型组合使用，这最适合多任务、可插拔部署；另一种是把适配器权重合并回基座，得到单体模型，便于独立推理与分发。前者更省存储、更灵活，后者更接近传统“一个模型直接上线”的部署习惯。</li>
</ol>
<div class="blog_h4"><span class="graybg">关键超参数</span></div>
<p>QLoRA 的表现很大程度上由少数关键超参数决定。它们分别约束适配器容量、优化稳定性、上下文覆盖范围与显存预算。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">超参数</td>
<td style="text-align: center;">控制对象</td>
<td style="text-align: center;">实践含义</td>
</tr>
</thead>
<tbody>
<tr>
<td>LoRA rank <span displaypfx="inline-" class="mathjax-container">\(r\)</span></td>
<td>低秩适配器的容量</td>
<td><span displaypfx="inline-" class="mathjax-container">\(r\)</span> 越大，适配器表达力越强，但显存与训练成本也越高；过小容易欠拟合，过大则削弱 PEFT 的资源优势。</td>
</tr>
<tr>
<td>lora_alpha</td>
<td>LoRA 更新量的缩放强度</td>
<td>它决定适配器增量对原模型输出的影响幅度，通常与 <span displaypfx="inline-" class="mathjax-container">\(r\)</span> 配合设置；过大容易训练不稳，过小则适配不足。</td>
</tr>
<tr>
<td>target_modules</td>
<td>LoRA 挂接位置</td>
<td>只覆盖 <span displaypfx="inline-" class="mathjax-container">\(q\)</span> / <span displaypfx="inline-" class="mathjax-container">\(v\)</span> 投影时最省资源；同时覆盖注意力与 MLP 投影时，通常有更高上限，但训练更重。</td>
</tr>
<tr>
<td>lora_dropout</td>
<td>适配器分支正则化</td>
<td>小数据集或高重复训练语料更容易过拟合，适度 dropout 有助于稳定；数据充足时则通常保持较低取值。</td>
</tr>
<tr>
<td>学习率（Learning Rate）</td>
<td>参数更新步长</td>
<td>QLoRA 只训练少量适配器参数，因此学习率通常可以高于全参数微调；但过高仍会导致输出风格漂移、格式崩溃或损失震荡。</td>
</tr>
<tr>
<td>批大小与梯度累积</td>
<td>等效 batch 规模</td>
<td>单卡显存通常只允许很小的 per-device batch size，因此需要靠梯度累积换取更稳定的优化轨迹。真正重要的是等效 batch，而不是单步看到多少样本。</td>
</tr>
<tr>
<td>最大序列长度（Max Sequence Length）</td>
<td>单样本上下文覆盖范围</td>
<td>序列越长，训练成本增长越快；过短又会截断多轮对话、长指令或结构化输出。它是质量与成本之间最直接的杠杆之一。</td>
</tr>
<tr>
<td>计算精度与优化器</td>
<td>数值稳定性与显存占用</td>
<td>支持时优先使用 BF16；否则常退回 FP16。分页 AdamW 一类优化器更适合量化微调，因为它们能显著压低优化器状态带来的峰值显存。</td>
</tr>
<tr>
<td>学习率调度器（Scheduler）</td>
<td>训练初期稳定性与后期收敛</td>
<td>cosine 衰减配合短 warmup 是常见默认配置：前期避免梯度过猛，后期逐步收敛到更稳的解。</td>
</tr>
</tbody>
</table>
<div class="blog_h4"><span class="graybg">适用边界</span></div>
<p>QLoRA 的核心优势是把“生成模型微调”从高门槛算力工程，压缩成可在有限资源上反复迭代的日常流程。因此，只要任务主要是指令跟随、格式控制、轻量领域适配或特定风格注入，它通常都应作为第一选择。它的边界同样明确：当任务需要深度改写基座知识、吸收大规模新领域语料、重构模型底层能力或逼近全参数微调的极限上限时，QLoRA 更适合作为基线而不是终点。此时更合理的路线通常是继续预训练、Q-DoRA，或直接转向更重的全参数微调。</p>
<div class="blog_h3"><span class="graybg">生成模型拒绝采样微调</span></div>
<p>生成模型拒绝采样微调处理的是这样一类场景：模型已经具备基本生成能力，甚至已经完成一轮 SFT，但仍希望进一步提高答案正确率、格式稳定性或可验证任务表现；同时，团队又不希望立刻进入完整 RLHF、PPO 或 DPO 训练链路。此时，最自然的做法往往不是直接改写优化目标，而是先让模型对同一提示词生成多个候选，再通过外部评分器筛选出最优答案，把它重新写回 SFT 数据，再继续监督训练。</p>
<p>从训练形态上看，它通常遵循五步流程。</p>
<ol>
<li>确定起点模型。起点通常是 Base Model 经过一轮指令微调后的模型，因为拒绝采样依赖“先能生成基本可读候选”这一前提；若模型连任务格式都不稳定，后续筛选只会浪费大量采样预算。</li>
<li>为每个提示词生成多个候选。生成阶段的目标不是一次命中标准答案，而是提供一个足够有区分度的候选池，因此通常会适度提高采样多样性，让模型在同一问题上给出若干不同解法、表述或结构。</li>
<li>使用外部评分器做筛选。评分器可以是规则校验、可执行验证、单元测试、格式解析器、奖励模型、人类打分，或它们的组合。只要能较稳定地区分“明显更好”和“明显更差”，就足以支撑拒绝采样式数据构造。</li>
<li>把通过筛选的样本回写成监督数据。最常见的做法是只保留每个提示词下得分最高或达到阈值的候选，把它们整理成新的 prompt-response 数据集。这样做之后，后续训练仍然是标准 SFT，而不是显式偏好优化。</li>
<li>按监督目标继续训练，并周期性重新采样。随着模型能力提升，旧一轮采样得到的高分样本可能不再代表新的最优边界，因此很多实践会多轮迭代：采样、筛选、回写、再训练，再用更新后的模型继续采样。</li>
</ol>
<p>这条路线的关键超参数主要有五类：每个提示词生成多少候选、采样温度与 top-p 等多样性控制、接受阈值或保留比例、评分器本身的一致性与噪声水平，以及每轮回写数据占原始 SFT 数据的比例。它们共同决定一个核心权衡：候选越多、筛选越严格，样本平均质量可能越高，但成本也越高，且更容易把训练分布压窄；候选太少或筛选过松，则回写数据与原始 SFT 的差异不够明显，改进幅度往往有限。</p>
<p>与 DPO 相比，拒绝采样微调不会直接保留“胜者优于败者”这层相对关系，而是只把胜者留下来，因此信息利用率更低，但训练链路更简单；与 PPO 相比，它没有显式策略优化与在线回报建模，因此更容易稳定落地。工程上，它特别适合<span style="background-color: #c0c0c0;">答案是否正确较容易验证</span>的任务，而对开放式偏好、帮助性、安全性与语气细粒度排序，更常需要再叠加 DPO、RLHF 或其他偏好优化方法。</p>
<div class="blog_h3"><span class="graybg">生成模型直接偏好调优</span></div>
<p>生成模型直接偏好调优（Direct Preference Tuning for Generative Models）处理的是生成模型训练中的下一层目标：模型已经通过监督微调（SFT）学会了基本的指令跟随，但在多个可行回答之间，仍未必稳定偏向人类真正想要的输出。此时，训练重点不再是“把任务教给模型”，而是“把偏好写进模型”。在当前工程实践里，DPO（Direct Preference Optimization）是最典型、也最实用的直接偏好调优方案。</p>
<p>它之所以称为“直接”，就在于它绕开了“先训练奖励模型，再用 PPO 做强化学习”的显式 RLHF 流程，直接用偏好数据本身更新策略。对资源受限、希望复用现有 SFT 训练栈的团队而言，这通常是比传统 RLHF 更轻、更稳的选择。</p>
<div class="blog_h4"><span class="graybg">适用前提</span></div>
<p>直接偏好调优几乎总是建立在一个已经完成 SFT 的模型之上，而不是直接从 Base Model 开始。原因很直接：偏好数据表达的是“在两个都还算合理的回答之间，哪个更好”；如果模型连基本指令都还不会遵循，那么偏好优化得到的首先不是更好的对齐，而是更混乱的行为。因此，标准路径通常是<span style="background-color: #c0c0c0;">Base Model → SFT / QLoRA SFT → DPO</span>。若显存非常紧，DPO 阶段也仍然可以继续沿用 LoRA / QLoRA 这一类参数高效微调方案。</p>
<div class="blog_h4"><span class="graybg">训练流程</span></div>
<p>从工程与理论结合的角度看，生成模型直接偏好调优通常可以拆成六步。</p>
<ol>
<li>确定起点模型。直接偏好调优的起点通常不是原始基座，而是已经完成指令微调的模型。若前一阶段使用的是 LoRA / QLoRA，则这里有两种常见路径：要么先把 SFT 适配器合并回模型，再继续挂接新的 DPO 适配器；要么保留 SFT 结果作为起始状态，在其之上继续叠加偏好调优适配器。无论采用哪条路径，本质都一样：DPO 学习的是“在 SFT 行为之上继续排序优化”，而不是重新学习指令能力。</li>
<li>构造偏好数据集。训练样本的基本形态是三元组：提示词（prompt）、被接受回答（chosen）、被拒绝回答（rejected）。它要求两条回答都与同一个提示词对应，并且都具备一定可读性，否则优化信号会退化成“学会排除明显坏答案”，而不是学习细粒度偏好边界。高质量 DPO 数据的关键，不只是 chosen 比 rejected 更好，还在于两者足够接近、足够可混淆，这样模型才会被迫学习真正决定人类偏好的因素，例如信息完整性、语气、格式遵循、安全边界与事实可靠性。</li>
<li>建立参考模型。DPO 不显式训练奖励模型，但仍然需要一个冻结的参考模型（Reference Model）作为锚点。这个参考模型通常就是 SFT 后的模型快照，它定义了“原本模型认为哪些回答更可能”。训练模型的目标不是盲目提高 chosen 的概率，而是相对于参考模型，进一步提高 chosen 的相对优势，并压低 rejected 的相对优势，从而避免模型在优化偏好时过度漂移。</li>
<li>配置可训练参数。若采用 PEFT 路线，训练对象通常仍是 LoRA 适配器，而不是全参数更新。此时需要重新决定 DPO 阶段的 LoRA 容量与挂载范围。实践中，一个常见选择是让 DPO LoRA 覆盖注意力投影与 MLP 投影，因为偏好优化往往既涉及回答内容选择，也涉及风格、结构和安全边界的重排。若只覆盖极少数层，偏好容量可能不足；若覆盖过广，则训练成本与过拟合风险都会上升。</li>
<li>执行直接偏好优化。DPO 训练时会同时比较当前模型和参考模型在 chosen / rejected 两条回答上的条件概率。优化目标可以概括为：让当前模型比参考模型更偏向 chosen，同时更远离 rejected。与 PPO 不同，这一过程不需要 rollout、奖励建模或价值函数估计，因此训练流程更接近“带特殊损失函数的 SFT”，也更容易保持数值稳定。</li>
<li>保存与合并训练产物。若使用 LoRA / QLoRA，训练完成后最常见的产物仍然是 DPO 适配器，而不是完整模型。部署时可以只加载适配器与基座组合使用；若希望得到单体模型，也可以按顺序把 SFT 适配器与 DPO 适配器依次合并。这个顺序很重要，因为偏好调优是在指令能力之上继续修正输出排序，若跳过 SFT 阶段直接只保留 DPO 适配器，模型通常不会得到预期行为。</li>
</ol>
<div class="blog_h4"><span class="graybg">重要参数</span></div>
<p>直接偏好调优的关键超参数主要决定四件事：偏好优化强度、可训练容量、序列覆盖范围，以及在有限显存下能否稳定收敛。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">参数</td>
<td style="text-align: center;">控制对象</td>
<td style="text-align: center;">实践含义</td>
</tr>
</thead>
<tbody>
<tr>
<td>beta</td>
<td>DPO 偏好强度 / 正则化强度</td>
<td>它决定 chosen 相对 rejected 的概率优势要被放大到什么程度。取值过大，模型容易偏离参考模型过快；取值过小，偏好信号又会过弱。工程上常把它理解为“偏好更新的力度旋钮”。</td>
</tr>
<tr>
<td>学习率（Learning Rate）</td>
<td>更新步长</td>
<td>DPO 阶段通常比早期 SFT 更接近“行为微调”而非“能力学习”，因此学习率往往需要更保守。学习率过高时，最先损坏的往往不是困惑度，而是回复风格、格式一致性与安全边界。</td>
</tr>
<tr>
<td>批大小与梯度累积</td>
<td>等效 batch 规模</td>
<td>DPO 每个样本都包含 prompt、chosen、rejected 三部分，显存压力通常高于普通单输出 SFT，因此更依赖小 batch 配合梯度累积来换取稳定训练。</td>
</tr>
<tr>
<td>max_prompt_length</td>
<td>提示词截断长度</td>
<td>它控制参考上下文保留多少信息。过短会丢失任务条件，过长则显著抬高显存与计算成本。</td>
</tr>
<tr>
<td>max_length</td>
<td>整体序列长度上限</td>
<td>它决定 prompt 与回答总共能占多少 token。对多轮对话、长解释和结构化输出任务而言，这个参数直接影响偏好信号能否覆盖完整回答。</td>
</tr>
<tr>
<td>warmup_ratio</td>
<td>训练初期学习率预热比例</td>
<td>偏好训练通常数据量小、梯度信号陡峭，预热有助于避免一开始就把模型推离参考分布。</td>
</tr>
<tr>
<td>优化器与精度</td>
<td>显存与数值稳定性</td>
<td>若沿用 QLoRA 路线，分页 AdamW、混合精度与梯度检查点通常仍是默认组合，它们解决的是“偏好训练能否在消费级显存下跑稳”的问题，而不是损失函数本身的问题。</td>
</tr>
<tr>
<td>LoRA 的 <span displaypfx="inline-" class="mathjax-container">\(r\)</span>、<span displaypfx="inline-" class="mathjax-container">\(\alpha\)</span>、target_modules、dropout</td>
<td>可训练容量与挂载位置</td>
<td>这些参数控制 DPO 阶段到底允许模型改动多大子空间。偏好差异若主要体现在语气、格式和细粒度行为边界上，低秩适配器通常足够；若 chosen / rejected 差异深度依赖复杂知识、推理链条或长程一致性，则更高秩或更广覆盖范围往往更稳。</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">推理阶段优化</span></div>
<p>推理优化（Inference Optimization）目标是在质量约束下同时降低延迟（Latency）、显存（Memory）和成本（Cost）。主线方法包括量化（Quantization）、KV Cache 优化、推测解码（Speculative Decoding）和连续批处理（Continuous Batching）。</p>
<p>关于“温度（Temperature）设为 0 仍可能不完全一致”的现象：温度为 0 通常等价于贪心解码（Greedy Decoding），理论上对固定权重与固定数值计算应是确定的；但线上推理系统可能包含非确定性来源（Non-determinism），例如并行算子中的浮点归约顺序差异、不同硬件/内核实现、以及服务端的动态调度与缓存策略。若业务需要强确定性，除了设置 <span displaypfx="inline-" class="mathjax-container">\(\text{temperature}=0\)</span>，还需要禁用采样相关选项并启用确定性计算（Deterministic Kernels）/固定随机种子（Seed）（若推理框架支持）。</p>
<div class="blog_h2"><span class="graybg">量化</span></div>
<p>量化（Quantization）通过降低数值精度减少带宽与显存占用。常见路径：FP16/BF16 到 INT8/INT4/FP8。大模型推理最常见的是权重量化（Weight-only Quantization），因为它实现简单、收益稳定；进一步的激活量化（Activation Quantization）能带来更大的加速空间，但对硬件与校准（Calibration）更敏感。</p>
<p>常见概念：</p>
<ul>
<li>PTQ（Post-Training Quantization）：训练后量化，依赖少量校准数据估计尺度（Scale）与零点（Zero-point）。</li>
<li>QAT（Quantization-Aware Training）：训练时模拟量化误差，通常精度更好但成本更高。</li>
<li>按粒度：per-tensor / per-channel / group-wise，粒度越细通常越准但开销更高。</li>
</ul>
<ul>
<li>INT8：8-bit 整数量化，精度与兼容性通常最稳，常作为权重量化的保守起点。</li>
<li>INT4：4-bit 整数量化，压缩率更高，但更依赖量化算法、分组方式与校准质量。</li>
<li>FP8：8-bit 浮点格式，动态范围通常优于 INT8，更适合高端硬件上的高吞吐推理与训练。</li>
<li>GGML：最早的本地 CPU/GPU 推理张量库与算子生态，强调轻量、本地部署与量化推理。</li>
<li>GGUF：建立在 GGML 生态上的统一模型文件格式，用于封装权重、词表、量化元数据和模型配置，已成为 llama.cpp 一类本地推理工具的主流分发格式。</li>
</ul>
<div class="blog_h2"><span class="graybg">KV Cache 缓存</span></div>
<p>KV 缓存（Key-Value Cache, KV Cache）用于避免重复计算历史 token 的 Key/Value。在解码器（Decoder-only）模型中，生成第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 个 token 时需要与前 <span displaypfx="inline-" class="mathjax-container">\(t-1\)</span> 个位置做注意力；若不缓存，每一步都会重复算一遍历史的 K/V，代价极高。</p>
<p>代价分解常用“预填充（Prefill）+ 解码（Decode）”来理解：Prefill 处理提示词（Prompt）并写入 KV；Decode 每生成一个 token 只需计算新 token 的 Q/K/V，并与缓存做一次注意力。这把每步成本从“重算整段历史”降为“读取缓存并做一次加权求和”。</p>
<p>KV Cache 的主要代价是显存：缓存规模与层数、头数、上下文长度线性增长，因此优化会围绕缓存布局（如 Paged Attention）、压缩（如 KV 量化）与复用策略展开。</p>
<p>粗略的量级估算：若每层缓存张量形状近似为 <span displaypfx="inline-" class="mathjax-container">\(K,V\in\mathbb{R}^{L\times n_{\text{kv}}\times d_k}\)</span>，则单 batch 的显存规模约为</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{Mem}_{\mathrm{KV}}\approx 2\cdot N_{\text{layers}}\cdot B\cdot L\cdot n_{\text{kv}}\cdot d_k\cdot \text{bytes}\]</span>
<p>其中 <span displaypfx="inline-" class="mathjax-container">\(n_{\text{kv}}\)</span> 在 GQA/MQA 中显著小于 Query 头数，因此 <span style="background-color: #c0c0c0;">GQA/MQA 往往是降低 KV Cache 的一阶有效手段</span>；进一步的手段包括 KV 量化、Paged KV（按块管理与回收）、以及对重复前缀做复用/持久化（Prompt Caching）。</p>
<div class="blog_h3"><span class="graybg">Paged Attention</span></div>
<p>Paged Attention 是一种面向推理阶段的 KV Cache 分页管理与访问机制。它把逻辑上连续的一段 KV 序列拆成固定大小的块（Blocks / Pages），再用块表把请求看到的连续上下文，映射到物理上未必连续的显存块，从而支持动态分配、回收与复用。</p>
<p>它解决的重点不是“如何把 KV Cache 压得更小”，而是“如何把不断增长的 KV Cache 更高效地组织、分配和访问”。</p>
<p>这种设计的收益主要有三点。第一，减少显存碎片（Fragmentation）：请求长度不断变化时，不再需要为每条序列预留一整段连续大内存。第二，提升动态批处理与并发调度效率：不同请求可以共享统一的块分配与回收机制，更容易在在线服务里做 token 级调度。第三，和前缀复用天然兼容：当某段前缀已经生成过，对应的 KV blocks 可以直接挂到新请求的块表上，而不必搬移整段缓存。</p>
<p>因此，Paged Attention 的本质是<span style="background-color: #c0c0c0;">KV Cache 的分页管理与访问优化</span>，而不是新的注意力数学形式。它不改变注意力结果本身，改变的是推理引擎如何在显存里存放和调度这些历史状态。也正因为如此，它通常与连续批处理（Continuous Batching）、Prompt Caching 和块级回收策略一起出现，是高吞吐推理引擎里的基础设施级组件。</p>
<div class="blog_h4"><span class="graybg">为什么连续预留显存会失效</span></div>
<p>若沿用传统的连续分配思路，系统通常需要为每条请求预留一大段连续显存，用来容纳“未来可能继续增长”的 KV Cache。但生成长度在服务开始时并不可知：有的请求很快结束，有的请求会持续生成很长文本。于是，预留得太保守会频繁扩容甚至失败，预留得太激进又会浪费大量显存。</p>
<p>这会同时带来两类碎片。内部碎片（Internal Fragmentation）来自“已经预留但尚未使用”的那部分连续空间；外部碎片（External Fragmentation）则来自请求不断进入和退出后，显存里出现许多零散空洞。即使总空闲显存仍然足够，也可能因为拿不到一整段足够长的连续区域，而无法容纳新的长请求。Paged Attention 的出发点正是消除这种“连续大块内存”假设。</p>
<div class="blog_h4"><span class="graybg">块表是如何工作的</span></div>
<p>Paged Attention 借鉴了虚拟内存（Virtual Memory）的分页思想。推理引擎先把可用于 KV Cache 的显存切成大量固定大小的物理块（Physical Blocks），每个块只容纳固定数量 token 的 K/V。对单条序列而言，模型逻辑上仍然看到一段从位置 1 到位置 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 的连续上下文；但在物理层，这些 token 对应的 KV 可能分散存放在许多互不相邻的块里。</p>
<p>块表（Block Table）负责维护这种映射关系：逻辑上的第 <span displaypfx="inline-" class="mathjax-container">\(i\)</span> 个块，对应显存中的哪一个物理块。当当前尾块写满后，系统只需从空闲池中再取一个新块，把它挂到块表末尾即可，并不要求新块和前一块在物理地址上相邻。这样一来，序列增长就从“申请更长的一整段连续空间”变成了“追加一个固定大小的新块”。</p>
<p>这种布局把碎片问题压到很小的范围内。内部浪费通常只会出现在最后一个尚未写满的尾块中，而不会沿整条序列累计；外部碎片则因为不再要求大块连续空间而大幅缓解。工程上真正被频繁分配和回收的，不再是“整条请求的整段缓存”，而是细粒度、等尺寸的块。</p>
<div class="blog_h4"><span class="graybg">注意力访问路径如何改变</span></div>
<p>在 Paged Attention 中，注意力访问路径（Access Path）指的是：当前 query 如何找到并读取历史 token 对应的 Key / Value，再完成注意力计算。它改变的是这条访问路径，而不是注意力公式本身。对当前 query 而言，模型仍然需要与全部历史 token 的 Key / Value 交互；不同之处在于，这些历史状态不再通过一块连续显存读取，而是由 kernel 按块表顺序逐块定位、聚合并完成注意力计算。逻辑上的序列连续性由块表保证，物理上的离散布局由运行时屏蔽。</p>
<p>因此，Paged Attention 更准确地说是一种<span style="background-color: #c0c0c0;">分页式 KV 访问内核</span>。它要求注意力实现能够接受“逻辑连续、物理离散”的缓存布局，并在 kernel 内高效完成地址映射、块级遍历和结果聚合。也正因为如此，Paged Attention 并不是简单的内存分配器技巧，而是需要推理 runtime 与注意力 kernel 协同设计的系统级能力。</p>
<div class="blog_h4"><span class="graybg">前缀共享与写时复制</span></div>
<p>分页布局还自然支持前缀共享（Prefix Sharing）。当多个请求拥有相同前缀时，它们可以在块表层共同指向同一组前缀块，而不必各自复制一份完整的前缀 KV。这种共享对系统提示词固定、多轮追问、束搜索（Beam Search）或树状探索都很重要，因为这些场景往往存在大段公共前缀。</p>
<p>一旦不同请求在后续 token 上开始分叉，系统再为各自分配新的尾部块即可；已经共享的旧块保持只读并可继续复用。这就是写时复制（Copy-on-Write）的典型思想：真正发生差异时才复制，未分叉前尽量共享。后文的 Prompt Caching 可以看作这种能力在“跨请求、跨时间窗口前缀复用”上的工程化延伸。</p>
<div class="blog_h3"><span class="graybg">Prompt Caching（前缀缓存）</span></div>
<p>Prompt Caching（前缀缓存）缓存的是“提示词前缀的 KV Cache”，目标是避免在多轮对话或重复前缀场景下反复做 Prefill：当新请求的开头 token 序列与某个已缓存前缀完全一致时，推理引擎可以直接复用这段前缀的 KV blocks，只对新增 token 做增量计算。</p>
<p>它直接优化两个指标：</p>
<ul>
<li>首 token 延迟（Time To First Token, TTFT）：减少或跳过重复前缀的 Prefill。</li>
<li>成本：若推理服务对缓存命中（Cache Hit）的前缀按更低费率计费，则输入 token 成本显著下降。</li>
</ul>
<p>Prompt Caching 的工程前提是“字节级一致（Exact Prefix Match）”：通常要求 tokenizer 后的 token 序列完全一致，任何空格/标点/系统提示词差异都会导致 cache miss。因此它更适合“系统提示词固定 + 文档前缀固定 + 多次追问”的产品形态，而不是随意变化的自由对话。</p>
<p>这类缓存的生命周期通常不是固定 TTL，而更像一种<span style="background-color: #c0c0c0;">受显存压力驱动的短生命周期缓存</span>。最常见的形态是直接驻留在 GPU 显存里，只要显存充足就保留，一旦新请求挤压缓存池，就按 LRU 等策略优先淘汰不活跃前缀。也有系统会把不活跃的 KV blocks 下沉到 CPU 内存形成分层缓存，以换取更长的可复用时间；但完整 KV Cache 很少作为常规路径直接落盘长期持久化，因为它体积大、回读慢、反序列化开销高，往往不如重新做一次 Prefill 划算。</p>
<div class="blog_h3"><span class="graybg">多租户隔离（Multi-tenancy Isolation）</span></div>
<p>在共享推理后端中，Prompt Caching 必须严格做多租户隔离（Multi-tenancy Isolation）：缓存命中不能只依赖“前缀文本哈希”，还需要把租户/用户身份与模型版本纳入缓存键（Cache Key），避免跨用户“串台”与侧信道泄露。典型复合键（Composite Key）包括：</p>
<ul>
<li>租户 ID（Org/User ID）</li>
<li>模型版本（Model/Weights Version）</li>
<li>前缀 token 哈希（Prefix Hash）与长度等元数据</li>
</ul>
<p>缓存生命周期通常很短：KV Cache 占用显存，后端会用 LRU（Least Recently Used）等策略在压力下淘汰缓存；因此“隔天再聊成本回到原价”是常见现象。若业务需要跨小时/跨天复用长前缀，工程上一般转向两类手段：长效上下文缓存（Context Caching，若服务支持）或 RAG/摘要把长前缀变成可检索的外部状态。</p>
<div class="blog_h2"><span class="graybg">KV Cache 压缩</span></div>
<p>KV Cache 压缩（KV Cache Compression）讨论的是：在不显著破坏注意力行为的前提下，把每个 token 需要缓存的 Key / Value 表示存得更小。它的直接目标不是训练提速，而是长上下文推理时的显存占用、带宽压力和并发能力。因此，从<span style="background-color: #c0c0c0;">优化对象</span>看，Latent KV、MLA、KV 量化、TurboQuant 都属于推理阶段优化；只是从<span style="background-color: #c0c0c0;">实现方式</span>看，它们并不全是推理后处理技巧，有些方法需要在模型架构和训练阶段就内建进去。</p>
<div class="blog_h3"><span class="graybg">Latent KV / MLA</span></div>
<p>一条路线是潜空间压缩（Latent-space Compression）。它不是直接缓存完整维度的 Key / Value，而是先把每个 token 的 KV 投影到更低维的潜空间（Latent Space），缓存潜变量；当需要参与注意力计算时，再由模型内部结构把潜变量还原成用于打分和聚合的表示。这类方法常被概括为 Latent KV，典型代表就是 MLA（Multi-head Latent Attention）。</p>
<p>这类方法的本质是一种<span style="background-color: #c0c0c0;">架构级的推理友好设计</span>。它服务的是推理阶段的 KV 成本问题，但并不是像 KV 量化那样在现成模型外部直接套一个压缩器；模型通常需要在训练时就学会如何在潜空间里存储和恢复有效的注意力信息。因此，它应被放在推理优化里讨论，但不能误解成“任何现有模型都可无缝加上的后处理插件”。</p>
<p>相对于 GQA/MQA 只是在“头数”上减少缓存，Latent KV / MLA 更进一步，直接压缩每个 token 的 KV 表示维度。收益通常体现为更长上下文、更高并发和更低带宽占用；代价则是算子更复杂、实现更依赖内核与数值稳定性，而且表示压缩本身会改变模型的内部信息流，因此往往需要专门的训练配方与模型容量补偿。</p>
<div class="blog_h3"><span class="graybg">KV 量化</span></div>
<p>KV 量化（KV Quantization）指的是：在保留 Key / Value 语义角色不变的前提下，把 KV Cache 从 FP16 / BF16 等较高精度压缩到更低 bit 的数值格式，以减少存储开销与读取带宽。它服务的仍然是原始注意力结构，只是把缓存表示改成更节省显存和带宽的低比特形式。</p>
<p>这条路线不依赖潜空间替换，而是保留原始 Key / Value 的语义角色，直接压缩缓存张量。这类方法更接近传统意义上的推理后处理：对现有模型更友好，工程接入成本通常低于 Latent KV / MLA，但量化误差是否会放大到注意力分布中，是成败关键。</p>
<div class="blog_h3"><span class="graybg">TurboQuant</span></div>
<p>TurboQuant 是一种面向 KV Cache 的内积保真量化（Inner-product-preserving Quantization）方法。它属于 KV 量化路线，但优化目标并不只是在坐标层面重建原向量，而是尽量保住注意力计算真正依赖的内积关系。</p>
<p>这也是它与普通低比特量化的关键区别。对 Key 而言，最重要的不是逐坐标把原向量还原得多么精确，而是尽量保住 <span displaypfx="inline-" class="mathjax-container">\(q\cdot k\)</span> 这类内积关系，因为注意力分数本身就是由这些内积驱动的。换句话说，TurboQuant 优先保护的是<span style="background-color: #c0c0c0;">注意力几何结构</span>，而不只是原始向量外形。</p>
<div class="blog_h4"><span class="graybg">为什么普通 KV 量化不够</span></div>
<p>如果只从存储角度看，KV Cache 似乎只是把一串浮点数改成更低 bit 的数字格式；但注意力并不是逐坐标读取这些数，而是先计算</p>
<span displaypfx="" class="mathjax-container">\[s_i=\frac{q\cdot k_i}{\sqrt{d_k}},\quad \alpha_i=\mathrm{softmax}(s_i)\]</span>
<p>然后再用 <span displaypfx="inline-" class="mathjax-container">\(\alpha_i\)</span> 去聚合对应的 <span displaypfx="inline-" class="mathjax-container">\(v_i\)</span>。这意味着，Key 上很小的量化误差，一旦改变了 <span displaypfx="inline-" class="mathjax-container">\(q\cdot k_i\)</span> 的相对大小，就可能在 softmax 之后被放大成明显不同的注意力分布。也正因为如此，普通“逐坐标尽量还原”的量化器未必就能得到好的注意力质量，因为它优化的是向量外形，而注意力真正依赖的是内积排序与相对间隔。</p>
<p>从这个角度看，TurboQuant 更像一种<span style="background-color: #c0c0c0;">面向在线内积估计的向量量化</span>，而不是普通的低比特存储。它特别适合 KV Cache 这类在线场景：缓存必须边生成边写入，解码时又要反复读取并参与 <span displaypfx="inline-" class="mathjax-container">\(QK^\top\)</span> 计算，因此量化器不能只在离线重建误差上表现好，还必须在在线注意力分数上尽量稳定。</p>
<div class="blog_h4"><span class="graybg">核心直觉</span></div>
<p>TurboQuant 的核心直觉可以概括为一句话：<span style="background-color: #c0c0c0;">先把向量变成更容易量化的形状，再用极低成本修补量化后最关键的内积偏差</span>。如果把原始向量直接送进低比特量化器，少数高能量坐标、异常值和不均匀分布往往会主导误差；而注意力最敏感的又不是“某一维少了多少”，而是“整体内积关系是否还成立”。因此，TurboQuant 并不把压缩过程当作一次简单的数值离散化，而是分成了主压缩与内积校正两个层次。</p>
<div class="blog_h4"><span class="graybg">两阶段结构</span></div>
<p>它的整体结构可以理解为两阶段。第一阶段负责主量化（Main Quantization）：先对向量施加随机旋转（Random Rotation），再执行高质量的低比特量化。第二阶段负责残差校正（Residual Correction）：不再追求把每个坐标补得更精确，而是针对第一阶段留下的残差，额外附加一个极轻量的编码，用来降低量化后内积估计的系统偏差。这样一来，TurboQuant 的目标就从“压缩向量”推进成了“压缩后仍尽量保住注意力分数”。</p>
<p>若用概念来源去理解，这条路线可以看成“PolarQuant 风格的主量化 + QJL 风格的残差校正”的组合。前者解决“怎么把向量本体存得足够省且失真足够低”，后者解决“怎么让压缩后内积估计不要系统性跑偏”。这也是为什么 TurboQuant 看上去像一个量化方法，实际上却比普通低比特缓存更接近一个<span style="background-color: #c0c0c0;">面向注意力运算的压缩管线</span>。</p>
<div class="blog_h4"><span class="graybg">为什么先做随机旋转</span></div>
<p>随机旋转的作用是改善向量分布的可量化性，而不是“制造随机性”。原始 KV 向量往往各维统计特性差异很大，有的维度能量特别集中，有的维度接近冗余；直接低比特量化时，少数尖峰维度会迫使量化器把刻度对齐到这些极端值，结果是大量普通维度的有效分辨率被浪费掉。随机正交旋转之后，向量能量会更均匀地摊到各个方向上，各维统计特性更接近，量化器就更容易在固定 bit 预算下稳定工作。</p>
<p>更直观地说，随机旋转做的是“先把难量化的尖峰分布打散，再做统一压缩”。这一步并不改变向量之间真正的几何关系，却显著改变了逐坐标量化时面对的数值形态。因此，它服务的不是语义本身，而是后续低比特编码的数值条件。</p>
<div class="blog_h4"><span class="graybg">为什么还需要残差校正</span></div>
<p>如果只有第一阶段，TurboQuant 仍然只是一个“更会量化向量”的方法，还不能真正保证注意力分数稳定。问题在于：即使主量化已经把总体失真压得很低，内积估计仍可能存在系统偏差，而 softmax 对这种偏差非常敏感。于是第二阶段不再继续追求全面重建，而是把预算集中用在“修正内积”这件事上。</p>
<p>这种设计背后的判断非常重要：在注意力里，全面重建所有坐标并不是最高优先级，优先级更高的是让“谁该被关注、关注强度大概多大”不要被量化误差改写。也正因为如此，TurboQuant 的第二阶段看起来只加了很轻的一层编码，却能显著改变注意力保真度，因为它修补的是最承重的误差，而不是平均分摊误差。</p>
<div class="blog_h4"><span class="graybg">如何理解它的收益</span></div>
<p>TurboQuant 的收益并不主要体现在“把某个向量压缩得多漂亮”，而体现在<span style="background-color: #c0c0c0;">相同显存下能缓存更长上下文，或在相同上下文下支持更高并发</span>。它直接作用于解码阶段最贵的那部分状态，即各层持续增长的 KV Cache。对长上下文推理而言，这通常比单步算子优化更承重，因为一旦缓存存不下，系统就会被迫降低 batch、缩短上下文，或转向更慢的外部交换路径。</p>
<p>但这类收益能否真正转化为吞吐提升，还取决于实现细节。若压缩后每一步都要做很重的解码、解包和额外访存，理论上的存储优势就可能被运行时开销抵消。因此，TurboQuant 不只是一个“理论上更省”的方法，它是否好用还取决于推理引擎是否能把旋转、量化、残差校正与注意力计算做成足够紧的执行路径。</p>
<div class="blog_h4"><span class="graybg">与 Latent KV / MLA 的关系</span></div>
<p>因此，TurboQuant 与 Latent KV / MLA 的差异非常明确。TurboQuant 是<span style="background-color: #c0c0c0;">量化压缩</span>，核心是把同一个 KV 向量存得更省，同时尽量保住注意力内积；Latent KV / MLA 是<span style="background-color: #c0c0c0;">潜空间压缩</span>，核心是先换成更低维的内部表示再缓存。前者更接近推理栈里的压缩器，后者更接近模型架构层面的推理优化设计。它们都属于 KV Cache 压缩，但技术路线和接入条件并不相同。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">场景</td>
<td style="text-align: center;">策略</td>
<td style="text-align: center;">成本</td>
<td style="text-align: center;">延迟</td>
<td style="text-align: center;">风险/约束</td>
</tr>
</thead>
<tbody>
<tr>
<td>连续多轮对话（分钟级）</td>
<td>Prompt Caching</td>
<td>低（命中时前缀“打折”）</td>
<td>低（TTFT 改善明显）</td>
<td>要求前缀严格一致；缓存易被淘汰</td>
</tr>
<tr>
<td>跨小时/跨天复用长文档前缀</td>
<td>Context Caching / 预热</td>
<td>中（通常含存储费）</td>
<td>中-低</td>
<td>依赖服务能力；需要明确缓存生命周期与权限</td>
</tr>
<tr>
<td>大规模文档问答</td>
<td>RAG（检索增强生成）</td>
<td>低且稳定</td>
<td>稳定</td>
<td>需要索引构建、召回/重排与证据注入治理</td>
</tr>
<tr>
<td>长对话状态维护</td>
<td>滚动摘要（Rolling Summary）</td>
<td>低</td>
<td>稳定</td>
<td>摘要漂移与可验证性问题；需结构化约束</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Speculative Decoding</span></div>
<p>推测解码（Speculative Decoding）用一个更小更快的草稿模型（Draft Model）一次提出多个候选 token，再由大模型（Target Model）并行验证并接受其中尽可能长的前缀。若接受率高，就能显著减少大模型需要执行的解码步数；若接受率低，则会退化并产生额外验证开销。实际收益取决于草稿模型速度、分布匹配程度与实现细节。</p>
<div class="blog_h2"><span class="graybg">批处理</span></div>
<p>批处理（Batching）讨论的是：如何把多个请求或多个 token 步骤拼到同一轮计算里，以提高硬件利用率。它本质上是在吞吐（Throughput）、单请求延迟（Latency）和调度复杂度之间做权衡。推理系统里常见的批处理并不只有一种，固定批处理、动态批处理和连续批处理服务的是不同流量形态。</p>
<div class="blog_h3"><span class="graybg">固定批处理（Static Batching）</span></div>
<p>固定批处理（Static Batching）是最传统的做法：先攒够固定数量的请求，再统一组成一个 batch 执行。它的优点是实现简单、执行路径稳定、硬件利用方式容易预测，因此很适合离线推理、批量评测、Embedding 批处理或输入长度相近的后台任务。</p>
<p>它的问题同样直接。第一，等待时间明显：如果系统必须凑满 batch 才发车，单请求延迟会被批量收集时间拉长。第二，padding 浪费常常很大：当同一批次中的序列长度差异明显时，短序列必须补到与长序列对齐，等于用无效 token 占用了算力。也正因为如此，固定批处理通常更适合“任务离线、流量可控、长度相近”的环境，而不适合作为通用在线 LLM 服务的主调度方式。</p>
<div class="blog_h3"><span class="graybg">动态批处理（Dynamic Batching）</span></div>
<p>动态批处理（Dynamic Batching）是在固定批处理上的现实改进。系统不再死等“凑满固定 batch”，而是在一个较短时间窗口内，把相近时间到达的请求尽量拼进同一批次里。这样做的好处是：既能保留一部分并行执行收益，又能避免固定批处理带来的长时间等待。</p>
<p>它适合中等并发的在线推理服务，尤其是在请求长度相对可控、生成长度不算太长的场景里表现不错。但它仍然主要以“请求”为单位成批，而不是以“token 步骤”为单位重组，因此当不同请求的生成过程拉得很长、长度分化越来越大时，批次内部的同步等待和 padding 问题依然存在。换句话说，动态批处理改善了固定批处理的僵硬性，但还没有真正解决 LLM 自回归解码里的异步性问题。</p>
<div class="blog_h3"><span class="graybg">连续批处理（Continuous Batching）</span></div>
<p>连续批处理（Continuous Batching）面向在线服务：请求不断到达、序列长度各不相同。与“固定 batch + padding”相比，连续批处理按 token 粒度调度，把不同请求的下一步解码拼到同一个批次里，从而提升吞吐并减少 padding 浪费。它不再把“一整条请求”视为不可拆分的调度单位，而是把系统里所有活跃序列都当作可持续重组的解码状态。</p>
<p>这类方法之所以重要，是因为 LLM 解码阶段天然是异步的：有的请求很快结束，有的请求仍在长输出，有的请求刚刚进入系统。若继续用请求级批处理，GPU 经常会被“最慢那几个序列”拖住；而连续批处理允许系统在每一步把已经完成的序列移出，再把新请求或其他活跃序列的下一步补进来，使 batch 始终维持较高利用率。</p>
<div class="blog_h4"><span class="graybg">为什么静态批处理不够</span></div>
<p>静态批处理的问题并不只是“padding 多一点”这么简单，而是它把一整条请求当成不可拆分的运行单元。假设同一批里有四个请求，其中三个很快生成结束，另一个却还要继续生成很长一段文本，那么 GPU 在后续很多步里实际上只能继续为这一个长请求服务，原先空出来的位置却不能被新请求及时填补。这样造成的不是数学错误，而是硬件空转：算力被“批次里最慢的那个请求”绑住了。</p>
<p>因此，连续批处理真正改变的不是 batch 的大小，而是<span style="background-color: #c0c0c0;">调度颗粒度</span>。传统静态批处理按“请求”调度，连续批处理按“下一 token 的解码步”调度。只要某个请求结束、被取消、或暂时被抢占，调度器就可以立刻把它从运行队列中移出，再用等待队列里的新请求补位。</p>
<div class="blog_h4"><span class="graybg">vLLM 到底在连续什么</span></div>
<p>在 vLLM 这类系统里，所谓“连续”指的是：调度器会以 decode 迭代为粒度，持续重组当前运行中的请求集合，而不是一次性固定一个 batch 直到所有请求全部结束。连续批处理的“连续”，本质上是运行队列和解码步的持续流动。</p>
<p>具体来说，调度器会在几乎每一次 decode 迭代之后重新审视当前运行集合。若当前正在运行的请求集合记为 <span displaypfx="inline-" class="mathjax-container">\(\mathcal{R}_t\)</span>，每个请求在第 <span displaypfx="inline-" class="mathjax-container">\(t\)</span> 轮各生成一个新 token，那么在进入第 <span displaypfx="inline-" class="mathjax-container">\(t+1\)</span> 轮之前，系统就会检查：哪些请求已经生成到结束符、达到最大长度或被上层中断；哪些等待中的新请求应被拉入运行队列。于是，运行中的 batch 并不是一次性固定下来的，而是在 decode 过程中持续流动。</p>
<p>这也是为什么连续批处理本质上更像<span style="background-color: #c0c0c0;">token 级流水调度</span>，而不是传统意义上的“把若干完整请求捆成一批”。高吞吐来自这种持续补位机制：只要有位置空出来，调度器就尝试让新的工作立刻进来，从而保持 GPU 上活跃序列数尽量稳定。</p>
<div class="blog_h4"><span class="graybg">Prefill 与 Decode 如何混排</span></div>
<p>连续批处理的工程难点在于，新进来的请求并不处在和老请求相同的计算阶段。老请求通常已经进入逐 token 解码（Decode），而新请求刚到达时还需要先完成整段提示词的预填充（Prefill）。这两个阶段的计算特征并不相同：Prefill 更接近长序列的大矩阵计算，往往更偏计算密集；Decode 则更依赖 KV Cache 读取与单步增量计算，往往更偏带宽与调度密集。</p>
<p>高吞吐推理引擎的关键能力之一，就是能把这两类工作纳入同一套调度体系，而不是强制等所有 Prefill 做完才统一进入 Decode。于是，新请求在进入系统后会先经历一次 Prefill，把提示词对应的 KV 写入缓存；随后它就能在下一个调度周期加入解码队列，和其他正在生成的请求一起按 token 粒度推进。工程实现上是否在同一次前向里混合执行 Prefill 与 Decode，取决于具体 runtime 的算子与调度设计；但从系统抽象看，它们必须被统一编排，否则连续批处理就无法真正发挥价值。</p>
<div class="blog_h4"><span class="graybg">为什么必须配合分页式 KV 管理</span></div>
<p>连续批处理之所以直到近年的高吞吐推理引擎才真正成熟，一个核心原因就在 KV Cache 管理。若每条请求都必须占据一整段预留好的连续显存，那么请求不断进出时，显存会迅速碎片化：短请求结束后留下很多小空洞，长请求却可能因为拿不到足够长的一段连续内存而无法被调度进去。此时即使调度逻辑想连续补位，底层显存布局也会把它卡死。</p>
<p>这正是 Paged Attention / Paged KV 发挥作用的地方。通过把 KV Cache 切成固定大小的块，再用块表去描述逻辑上的连续序列，系统就不再要求“每个请求必须拥有一整段连续显存”。于是，块可以被细粒度分配、回收、复用和共享，连续批处理才真正有了工程基础。也正因为如此，在现代 LLM 服务栈里，Paged KV 与 Continuous Batching 几乎总是成对出现：前者解决“缓存如何活着”，后者解决“调度如何持续流动”。</p>
<p>它的实现难点也最高。连续批处理要和 KV Cache 管理、Paged Attention、块分配/回收、抢占策略和流式输出协同设计，还必须在吞吐与 P99 延迟之间做持续权衡。也正因为如此，它通常不是单独存在的一项小优化，而是高吞吐推理引擎的核心调度机制。</p>
<div class="blog_h2"><span class="graybg">推理框架</span></div>
<p>推理框架（Inference Serving Stack）把“模型权重 + 推理图 + 调度策略”封装成可部署的服务。选型时优先看三件事：是否支持你的模型与精度（Compatibility）、是否能把 KV Cache 与批处理调度做对（Scheduler + KV Management）、以及在你的硬件上能否稳定达到目标吞吐/延迟（Performance Envelope）。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">框架</td>
<td style="text-align: center;">定位</td>
<td style="text-align: center;">强项</td>
<td style="text-align: center;">代价/约束</td>
<td style="text-align: center;">适用场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>vLLM</td>
<td>生产级高吞吐推理</td>
<td>Paged KV + 连续批处理；并发与吞吐强</td>
<td>部署与调优门槛更高；更依赖 CUDA 与服务化环境</td>
<td>多租户在线服务；RAG/Agent 高并发；企业 API</td>
</tr>
<tr>
<td>TensorRT-LLM</td>
<td>NVIDIA 推理加速栈</td>
<td>内核与图级优化；低延迟上限高</td>
<td>构建/调参成本高；硬件绑定强</td>
<td>对延迟敏感的核心服务；固定模型部署</td>
</tr>
<tr>
<td>TGI（Text Generation Inference）</td>
<td>通用推理服务</td>
<td>生态成熟；易集成；支持常见部署形态</td>
<td>极致吞吐/显存利用率取决于具体模型与配置</td>
<td>快速上线；标准化 HuggingFace 模型服务</td>
</tr>
<tr>
<td>SGLang</td>
<td>面向 LLM 应用的推理与编排</td>
<td>更贴近应用侧的执行模型；对复杂推理/工具调用友好</td>
<td>需要接受其编排抽象；生态仍在快速演进</td>
<td>复杂 Agent/RAG pipeline；结构化推理任务</td>
</tr>
<tr>
<td>llama.cpp（GGUF）</td>
<td>本地/边缘推理</td>
<td>CPU/小 GPU 友好；量化生态完善；分发简单</td>
<td>吞吐与模型规模受硬件限制；在线并发能力有限</td>
<td>个人/离线实验；边缘设备；小规模服务</td>
</tr>
<tr>
<td>Ollama</td>
<td>本地运行与分发工具</td>
<td>安装简单；模型拉取、管理与切换顺手</td>
<td>更偏单机体验而非高并发服务性能</td>
<td>个人原型验证；本地开发；小规模离线使用</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Ollama 与 vLLM</span></div>
<p>如果只看最常见的部署分叉，Ollama 与 vLLM 实际上分别代表了两种完全不同的目标函数。Ollama 解决的是“如何让个人开发者或小团队在本地几乎零门槛地把模型跑起来”；vLLM 解决的则是“如何让同一份模型在服务端硬件上稳定承载更多请求，并把 KV Cache、批处理和显存调度做到足够高效”。前者更像本地模型工厂，后者更像生产环境推理引擎。</p>
<p>部署体验上的差异最直接。Ollama 把模型下载、量化版本管理、本地 API 与交互入口统一封装起来，目标是尽量减少环境配置摩擦；vLLM 则仍然更像一个服务端运行时，需要围绕 Python / CUDA / PyTorch 版本、模型路径、并发参数、显存预算和服务启动方式做明确配置。二者服务的优化目标不同：Ollama 优先优化可用性，vLLM 优先优化服务效率。</p>
<p>性能侧的分界点通常出现在并发与显存管理。低并发、单人本地交互时，Ollama 往往已经足够顺畅，而且对量化模型和消费级显卡更友好；当请求数上升、上下文变长、需要更稳定地榨出 GPU 吞吐时，vLLM 的 PagedAttention、连续批处理（Continuous Batching）和更系统化的 KV 管理优势就会明显体现出来。前一节所说那种按 token 步骤持续补位的调度机制，正是 vLLM 一类高吞吐引擎的核心能力之一。换句话说，Ollama 更偏“把一次请求跑得足够方便”，vLLM 更偏“把很多请求一起跑得足够经济”。</p>
<p>因此，可以把这条选型规则压缩成一句话：<span style="background-color: #c0c0c0;">本地开发、个人使用、边缘设备与隐私优先场景更偏向 Ollama；企业服务、高并发 API、多租户平台和多卡扩展场景更偏向 vLLM</span>。若一个项目未来很可能从“个人原型”走向“在线服务”，最常见的工程路径也往往是先用 Ollama 快速验证模型行为，再迁移到 vLLM 一类更强的服务端推理栈。</p>
<div class="blog_h3"><span class="graybg">vLLM</span></div>
<p>vLLM 是面向大模型推理的高吞吐推理引擎（Inference Engine），核心目标是在显存受限的情况下提升并发与吞吐。它的代表性设计是 PagedAttention：把 KV Cache 按固定大小切成块（Blocks），用块表（Block Table）把“逻辑连续的序列”映射到“物理上不必连续的显存块”。</p>
<p>这样做的收益是：按需分配 KV、降低内存碎片（Fragmentation），并支持前缀共享（Prefix Sharing）：当多个请求共享同一段前缀（例如相同系统提示词/相同文档前缀）时，可以复用同一份缓存块，从而节省显存并降低 Prefill 成本。</p>
<p>vLLM 的真正价值并不只在某个单独算子，而在于它把<span style="background-color: #c0c0c0;">KV Cache 管理、连续批处理、调度和服务接口</span>统一成了面向高并发场景的整体系统。具体到调度层，它做的正是前文展开的那种 token 级连续批处理：每一轮 decode 之后重审运行队列，把已经结束的序列移出，再让等待中的新请求完成 Prefill 并尽快补入后续 decode 周期。对企业级 API、RAG 服务、多轮聊天机器人或多租户平台而言，请求通常不会整齐地同时开始和结束；连续批处理能让系统在 decode 过程中持续吸纳新请求，而不是死板地按静态 batch 切分，这正是它在生产环境里更占优势的原因。</p>
<p>它的边界也很清楚。vLLM 本身不是给普通用户做交互体验优化的工具，而是给工程团队做吞吐、延迟、监控和资源利用率优化的 runtime。于是它更适合有明确服务化目标的团队：需要 API 暴露、监控指标、批处理调优、多 GPU 并行和较强可观测性时，vLLM 往往比本地导向工具更自然；但在单机小显存、低并发、快速原型阶段，它未必是最省力的第一选择。</p>
<div class="blog_h3"><span class="graybg">TensorRT-LLM</span></div>
<p>TensorRT-LLM 是面向 NVIDIA GPU 的大模型推理加速栈，重点在于高效算子（Kernel）与图级优化（Graph-level Optimization）：通过更强的算子融合、更贴近硬件的精度与布局选择（例如 FP16/BF16/FP8、weight-only quantization），把通用 Transformer 计算编译成更高吞吐、更低延迟的推理执行计划。</p>
<p>它更偏“部署与性能工程”路径：需要围绕目标 GPU、精度与 batch/seq 长度做构建与调优；换来的是更稳定的延迟与更高的吞吐上限。</p>
<div class="blog_h3"><span class="graybg">Ollama</span></div>
<p>Ollama 是面向本地开发的模型运行与分发工具：提供统一的模型拉取、量化版本管理与本地推理接口，降低“把模型跑起来”的工程摩擦。它更适合个人/小团队在本机进行原型验证与离线实验；若追求高并发在线服务，通常会使用更专业的推理引擎与服务编排。</p>
<p>它的核心价值并不在于把吞吐推到极限，而在于把本地使用路径做得足够短：模型名称、下载、版本切换、交互入口和本地 API 都被统一包装起来。对个人学习、隐私敏感的小规模应用、本地插件开发或“先验证模型行为再说”的探索阶段，这种低门槛通常比极致性能更重要。</p>
<p>也正因为如此，Ollama 对消费级机器往往更友好。单卡、小显存、量化模型、本地命令行交互，这些都是它的舒适区；但一旦目标转向多租户、高并发、细粒度监控和多卡线性扩展，它就不再是最自然的中心组件。更准确地说，Ollama 优化的是<span style="background-color: #c0c0c0;">单机可用性与本地开发体验</span>，而不是生产环境下的系统吞吐极限。</p>
<div class="blog_h3"><span class="graybg">TGI（Text Generation Inference）</span></div>
<p>TGI（Text Generation Inference）是面向 HuggingFace 模型生态的通用推理服务：提供标准化的 HTTP/gRPC 接口与常见的批处理/流式输出能力，适合把“能跑的模型”快速变成“可用的服务”。它的价值在于工程整合与稳定性，而不是把每个场景都推到极致性能。</p>
<div class="blog_h3"><span class="graybg">SGLang</span></div>
<p>SGLang 强调“把推理当作可编排的程序执行”：当应用需要多步生成、结构化输出、工具调用或复杂控制流时，推理框架不仅要快，还要能把控制逻辑表达清楚并与 KV Cache 调度协同。它更像是服务端的“推理 DSL + runtime”。</p>
<div class="blog_h3"><span class="graybg">llama.cpp（GGUF）</span></div>
<p>llama.cpp 是一套本地/边缘推理 runtime：围绕 GGUF/量化权重与 CPU/GPU 混合执行做了大量工程优化。它在“把模型放到普通机器上跑起来”这一目标上极具性价比，但并不追求云端多租户场景下的极限吞吐。</p>
<div class="blog_h1"><span class="graybg">上下文工程（Context Engineering）</span></div>
<div class="blog_h2"><span class="graybg">从提示词工程到上下文工程</span></div>
<p>提示词工程（Prompt Engineering）最早强调的是“怎么写一句更有效的话”，关注点集中在措辞、顺序、示例和限制条件。随着大模型应用从单轮问答扩展到智能体（AI Agent）、RAG、多工具调用与长会话系统，工程重点逐渐转向上下文工程（Context Engineering）：真正决定效果的，不再只是某一句提示词本身，而是<span style="background-color: #c0c0c0;">系统提示、用户输入、示例、检索结果、对话历史、工具返回、结构化状态与输出约束如何被整体组织成一次模型调用</span>。</p>
<p>因此，上下文工程可以看作比提示词工程更大的外层概念。提示词仍然重要，但它只是上下文中的一个组件；在生产系统里，更关键的问题通常是“哪些信息应该进入上下文、以什么顺序进入、保留多久、如何压缩、何时替换、如何约束输出”。这一变化反映的不是术语时尚，而是应用形态已经从“写一句 prompt”演进为“设计一套输入装配系统”。</p>
<div class="blog_h2"><span class="graybg">上下文学习</span></div>
<p>上下文学习（In-context Learning）指模型在不更新参数的前提下，仅凭当前输入中的任务说明、示例与约束完成新任务。它依赖的是上下文中的条件信息，而不是额外微调。</p>
<div class="blog_h3"><span class="graybg">零样本（Zero-shot）</span></div>
<p>零样本（Zero-shot）是在不给示例的情况下，直接通过任务描述让模型完成目标。例如“判断以下影评是正面还是负面，只输出标签”。它的优点是成本低、迁移快；缺点是任务边界一旦含糊，模型更容易自由发挥。</p>
<div class="blog_h3"><span class="graybg">少样本（Few-shot）</span></div>
<p>少样本（Few-shot）是在提示词中附带少量示例，让模型在当前上下文里归纳输入输出模式。它特别适合标签定义不够直观、格式要求严格或任务带有领域习惯的场景。示例的价值不只是“给答案”，更是显式定义任务边界、异常情况与输出风格。</p>
<div class="blog_h2"><span class="graybg">提示词结构</span></div>
<p>一个可维护的提示词通常由若干功能块组合而成，而不是一段散乱文本。把这些块拆开，能够显著提高复用性、可调试性与一致性。</p>
<div class="blog_h3"><span class="graybg">角色定位</span></div>
<p>角色定位（Role）定义模型在本次任务中的身份与职责边界，例如“你是严谨的法律信息抽取器”或“你是面向儿童解释科学概念的讲解者”。它的作用是设定默认行为策略，而不是替代具体任务指令。</p>
<div class="blog_h3"><span class="graybg">指令</span></div>
<p>指令（Instruction）直接说明模型要完成什么任务，是提示词中最核心的控制块。好的指令应当明确目标、操作步骤、禁止事项与完成标准，避免把多个含糊目标混在同一层表达里。</p>
<div class="blog_h3"><span class="graybg">上下文</span></div>
<p>上下文（Context）提供完成任务所需的背景信息，例如产品文档、会话历史、用户约束、检索片段或前一步工具返回。它回答的是“模型基于什么信息工作”，而不是“模型该怎么工作”。</p>
<div class="blog_h3"><span class="graybg">输出格式</span></div>
<p>输出格式（Output Format）规定结果应该长什么样，例如纯文本、项目符号、JSON、表格或固定字段。格式要求越明确，后续系统越容易解析、验证与链接到其它模块。</p>
<div class="blog_h3"><span class="graybg">受众</span></div>
<p>受众（Audience）决定解释深度、术语密度与默认背景知识。例如 ELI5（Explain Like I'm 5）指面向完全没有背景知识的读者，需要更浅白的表达；面向资深工程师时，则应保留专业术语、边界条件与实现细节。</p>
<div class="blog_h3"><span class="graybg">语气</span></div>
<p>语气（Tone）控制表达风格，例如正式、简洁、鼓励式、审慎式或客服式。它影响的是话语风格和风险表达方式，但不应改变事实本身或任务判断标准。</p>
<div class="blog_h3"><span class="graybg">数据</span></div>
<p>数据（Data）指与任务本身直接相关的材料，例如待分类文本、待总结文档、用户表单、日志片段、表格记录或检索回来的证据。很多提示词失败，根源在于真正决定任务结果的数据并没有进入上下文。</p>
<div class="blog_h2"><span class="graybg">上下文构建</span></div>
<div class="blog_h3"><span class="graybg">系统提示（System Prompt）</span></div>
<p>系统提示（System Prompt）是整个上下文的最高层行为约束，通常用于定义角色、原则、边界和长期稳定规则。它应该稳定、简短且高信号，承载通用策略，而不适合塞入频繁变化的任务细节。</p>
<div class="blog_h3"><span class="graybg">对话历史与记忆</span></div>
<p>多数大模型 API 是无状态（Stateless）的：模型在一次调用中只“看到”你发送的输入（Prompt），不会自动记住上一次调用的对话内容。因此对话应用通常需要把对话历史（Conversation History）连同当前用户输入一起发给模型。</p>
<p>这会带来两个直接后果：</p>
<ul>
<li>token 占用会随着历史增长而上升（直到触达上下文窗口上限）。</li>
<li>并非“发得越多越好”：冗余历史会稀释注意力、提高成本，并可能引入过时或冲突信息。</li>
</ul>
<p>工程上常见的记忆（Memory）分层是：</p>
<ul>
<li>短期记忆（Short-term Memory）：保留最近 N 轮原文对话，确保局部连贯。</li>
<li>工作记忆（Working Memory）：把对话状态压缩成结构化摘要，例如用户偏好、已确认事实与当前任务约束。</li>
<li>长期记忆（Long-term Memory）：把历史片段写入外部存储（向量库/数据库），需要时检索回填。</li>
</ul>
<div class="blog_h3"><span class="graybg">工具调用上下文</span></div>
<p>工具调用上下文（Tool Context）指模型在调用搜索、数据库、代码执行器或业务 API 后得到的中间结果。它的关键不只是“把结果贴回模型”，而是把工具返回整理成模型真正可用的状态：保留必要字段、去掉噪声、标明来源与时间，并避免把原始日志整段塞进上下文。</p>
<div class="blog_h3"><span class="graybg">渐进式披露</span></div>
<p>渐进式披露（Progressive Disclosure）强调：不要在一开始把所有文档、规则、工具说明和历史对话一次性塞进上下文，而应只注入当前步骤真正需要的那一层信息。模型先看到最小可行上下文，只有当任务推进到下一层时，才继续展开更具体的约束、领域知识或执行细节。</p>
<p>这样做的原因不只是节省 token，更是为了保持推理质量。多组工程实践都观察到，上下文利用率一旦超过大约 40%，模型就可能从“聚焦求解”进入“信息过载”状态，开始出现幻觉、循环、格式错误或低质量实现。渐进式披露的目标，就是让 Agent 长时间停留在这个甜蜜区，而不是被无关材料拖进噪声区。</p>
<div class="blog_h2"><span class="graybg">上下文窗口管理</span></div>
<div class="blog_h3"><span class="graybg">Token 预算</span></div>
<p>Token 预算（Token Budget）是把上下文窗口（Context Window）当作一种稀缺资源来管理：一次请求的输入 token 与输出 token 都要计入模型的最大长度限制，并直接影响延迟与成本。</p>
<p>经验做法是把 prompt 切成可控的几块：系统提示尽量短且稳定；对话历史只保留近期原文；把长期信息通过检索（Retrieval）按需注入。</p>
<div class="blog_h3"><span class="graybg">压缩与摘要</span></div>
<p>压缩与摘要（Compression &amp; Summarization）的核心不是把文本“变短”，而是把信息从逐字转录（Transcript）变成可被模型继续使用的状态（State）。常见策略：</p>
<ul>
<li>滚动摘要（Rolling Summary）：每轮对话后更新一段固定长度的当前状态。</li>
<li>层级摘要（Hierarchical Summarization）：长文先分块总结，再总结摘要。</li>
<li>结构化记忆（Structured Memory）：用 JSON 或表格保存关键字段（偏好、约束、已决策项），避免自然语言摘要漂移。</li>
</ul>
<p>摘要要可验证：优先保留可操作事实（约束、数值、名称、决策），少写主观评价。</p>
<div class="blog_h3"><span class="graybg">长文本处理策略</span></div>
<p>当最新一次对话依赖很久以前的信息时，最可靠的方法不是无限追加历史，而是检索增强：把历史切成片段并建立索引（向量索引、关键词索引、主题标签），在每次请求前先检索与当前问题最相关的片段，再把这些片段注入上下文。</p>
<p>在当前智能体开发里，最主流的组合通常是：<span style="background-color: #c0c0c0;">滑动窗口 + 滚动摘要 + 向量检索（RAG）</span>。早期的外部记忆网络（Memory Networks / Neural Turing Machine）更多是研究范式，工程落地上更常见的是向量数据库与检索管线。</p>
<div class="blog_h2"><span class="graybg">结构化输出</span></div>
<div class="blog_h3"><span class="graybg">JSON Schema 约束</span></div>
<p>JSON Schema 约束把“输出应该长什么样”前置为机器可验证的结构定义，例如字段名、字段类型、必填项、枚举值与嵌套关系。它的价值在于把格式控制从“模型尽量照做”提升到“系统可以检查对不对”，从而显著降低后处理复杂度。</p>
<div class="blog_h3"><span class="graybg">函数调用（Function Calling）</span></div>
<p>函数调用（Function Calling）把模型输出从自由文本转成“选择哪个工具、填写哪些参数”的结构化决策。它的工作方式是让模型先生成一个可被程序消费的调用意图，再由外部系统负责权限校验、真实执行与结果回填。</p>
<div class="blog_h2"><span class="graybg">高级提示词策略</span></div>
<p>这里讨论的高级提示词策略，指的是仅通过提示词设计改变模型求解过程，而不借助额外框架、搜索控制器或推理编排器。它们本质上仍属于上下文工程：通过改变输入结构，让模型在一次或少数几次调用中显式展开中间推理步骤。</p>
<div class="blog_h3"><span class="graybg">思维链（Chain-of-Thought）</span></div>
<p>思维链（Chain-of-Thought, CoT）的核心做法是：在提示词中明确要求模型先分步分析，再给出最终答案。例如，可以把任务写成“先列出关键事实，再逐步推理，最后只输出结论”。若任务本身较复杂，也可以在 few-shot 示例里直接展示“问题 ➡ 分步推理 ➡ 最终答案”的格式，让模型在上下文中模仿这种求解模式。</p>
<p>在纯提示词使用方式下，CoT 的关键不是一句固定咒语，而是把推理过程结构化地写进输出要求。例如，可以把输出格式限定为“步骤 1 / 步骤 2 / 结论”，或要求模型先检查条件、再排除候选、最后汇总结论。这样做通常有利于多步算术、条件判断、规则推导与长链依赖任务；但对简单任务、严格结构化输出任务或成本敏感场景，强行要求展开思维链反而会增加 token 开销与噪声。</p>
<div class="blog_h3"><span class="graybg">思维树（Tree of Thoughts）</span></div>
<p>思维树（Tree of Thoughts, ToT）可以看作思维链的分支化版本：提示词不只要求模型给出一条线性推理路径，而是要求它先生成多个候选思路，再比较这些思路，最后选择更优的一条继续展开。在不借助框架的前提下，这种策略仍可以通过提示词实现，例如要求模型“先给出三个可能方案，分别说明优缺点，再选择最合理的方案给出最终答案”。</p>
<p>纯提示词版 ToT 的本质，是把“分支生成 + 分支比较 + 继续展开”都压进一次或少数几次模型调用里。它适合开放式规划、方案比较、复杂写作提纲和策略搜索这类任务，因为这类任务往往不存在唯一直接路径。代价同样明显：提示词更长，输出更长，模型也更容易在分支之间漂移。因此，ToT 更适合高价值、需要比较多个候选方案的任务，而不适合作为所有请求的默认模式。</p>
<div class="blog_h1"><span class="graybg">Harness Engineering</span></div>
<p>Harness Engineering（驾驭工程）研究的是：当模型推理与生成能力已经足够强时，决定 Agent 能否稳定完成复杂任务的，往往是模型外围的整套工作系统。Harness 指模型之外的所有代码、配置与执行逻辑——工具接口、状态管理、上下文装配、架构约束、验证机制、回滚策略与持续清理，全部属于这一层。裸模型只能接收输入、输出文本；只有当 Harness 为它提供状态、工具、约束和反馈回路后，它才能成为一个持续干活的 Agent。</p>
<p>目前 Harness Engineering 落地最广、讨论最充分的场景仍然是 Coding Agent，因此本章中的很多方法首先来自代码生成、调试、验证、跨会话交接和多 Agent 编排的工程实践。但它并不局限于写代码。凡是 Agent 需要长期持有状态、调用外部工具、在约束下执行任务、接受反馈并持续纠偏的场景，都可以应用同样的方法论，例如研究 Agent、数据分析 Agent、运维自动化 Agent、业务流程 Agent 以及多模态交互系统。随着 Agent 从“生成回答”走向“持续执行任务”，Harness Engineering 也会从 Coding Agent 的经验集合，扩展为更一般的 Agent 系统工程。</p>
<p>下文将沿着“为什么 Agent 会失效 ➡ 如何搭建控制面 ➡ 如何长期治理系统 ➡ 工程师角色如何变化”这条主线展开。</p>
<div class="blog_h2"><span class="graybg">三层模型和五大支柱</span></div>
<p>Prompt Engineering（提示词工程）、Context Engineering（上下文工程）与 Harness Engineering 构成了层层外扩的系统模型：前者决定如何表达任务，中间层决定模型能看到什么，最外层决定整个系统如何执行、纠偏与长期维持质量。</p>
<div class="blog_h3"><span class="graybg">三层模型</span></div>
<div class="blog_h4"><span class="graybg">提示词工程</span></div>
<p>提示词工程回答的是“如何把任务说清楚”。它关注指令措辞、角色设定、Few-shot 示例、输出格式和推理引导，目标是在单次调用里把任务表达得足够明确，让模型更容易走向期望行为。</p>
<div class="blog_h4"><span class="graybg">上下文工程</span></div>
<p>上下文工程回答的是“模型应该看到什么”。它关注记忆注入、检索回填、窗口压缩、状态摘要和工具返回整理，目标是控制输入信息的相关性与密度，使模型在有限上下文里获得完成当前任务所需的关键材料。</p>
<div class="blog_h4"><span class="graybg">Harness Engineering</span></div>
<p>Harness Engineering 回答的是“模型在什么系统里工作”。它处理的不再只是输入文本，而是工具接口、状态持久化、架构边界、验证回路、错误恢复、执行顺序与持续清理。到了这一层，关注点已经从“如何让模型回答得更好”转向“如何让 Agent 在真实系统中稳定完成工作”。</p>
<p>Harness 并不等同于某一句系统提示，也不等同于某个框架的名称。框架（Framework）、运行时（Runtime）和工具链都可以参与实现 Harness，但 Harness 这个词强调的是<span style="background-color: #c0c0c0;">模型外围整套工作系统</span>，而不是其中任何一个单独组件。</p>
<div class="blog_h3"><span class="graybg">五大支柱</span></div>
<p>从系统设计视角，一个可用的 harness 通常由五类承重件组成：上下文管理（Context Management）、工具编排（Tool Orchestration）、安全护栏（Safety Guardrails）、反馈回路（Feedback Loops）与可读性/可观测性（Legibility / Observability）。前两者解决“Agent 知道什么、能做什么”，中间两者解决“何时纠偏、如何避免越界”，最后一类解决“系统当前到底发生了什么”。</p>
<div class="blog_h4"><span class="graybg">上下文管理</span></div>
<p>上下文管理决定 Agent 在每一步究竟能看到什么信息。它的目标不是堆砌更多材料，而是把系统提示、短期记忆、长期知识、工具返回和任务状态按层组织，让模型始终工作在信息足够但不过载的区间。</p>
<div class="blog_h4"><span class="graybg">工具编排</span></div>
<p>工具编排决定 Agent 能做什么，以及如何把动作接回推理链路。搜索、数据库、代码执行器、浏览器自动化和业务 API 只有在权限、输入输出格式、调用顺序与结果回填都被设计清楚后，才会从“外挂能力”变成稳定的执行系统。</p>
<div class="blog_h4"><span class="graybg">安全护栏</span></div>
<p>安全护栏负责限制 Agent 的越界空间。它包括写权限边界、架构约束、审批节点、沙箱、敏感操作限制与结构化状态约束，作用是把“模型可能做错事”转化为“系统不允许它在关键位置随意犯错”。</p>
<div class="blog_h4"><span class="graybg">反馈回路</span></div>
<p>反馈回路负责告诉 Agent 当前结果究竟对不对。测试、lint、evaluator、Sprint Contract、健康检查和人工验收都属于这一层。没有反馈回路，Agent 只能凭语言流畅性误判成功；有了反馈回路，系统才能把错误重新送回执行链路中修复。</p>
<div class="blog_h4"><span class="graybg">可读性与可观测性</span></div>
<p>可读性与可观测性负责让 Agent 看见系统真实状态。UI、日志、指标、追踪、截图、DOM 快照和运行时事件，都是 Agent 判断“系统发生了什么”的依据。若系统对 Agent 不可见，很多问题即使存在，也只能靠盲目试错去碰。</p>
<p>Harness 的成熟度，本质上就是这五类控制面被工程化到什么程度。后文的各个部分，基本都可以看作对这五类支柱的展开：先看 Agent 为什么会系统性失效，再分别讨论上下文、编排、知识、约束、可读性、验证与熵控制如何把这些失效压回系统边界之内。</p>
<div class="blog_h2"><span class="graybg">长运行 Agent 的系统性失效</span></div>
<p>长运行 Agent（Long-Running Agent）必须跨多个上下文窗口持续工作，每个新会话在启动时对前一会话发生的事情没有任何记忆。即使使用最强的前沿模型，如果只给一个高级别的提示词，Agent 也无法稳定构建出生产质量的复杂系统。实践观察到四类典型失效：</p>
<ul>
<li><span style="background-color: #c0c0c0;">过早宣告完成（Premature Victory Declaration）</span>：在项目进行到一定阶段后，Agent 环视已完成的工作，宣告整个项目已经完成，忽略仍未实现的功能。</li>
<li><span style="background-color: #c0c0c0;">脏状态遗留（Dirty State Handoff）</span>：会话结束时遗留未修复的 bug 或未记录的进度，下一个会话必须先花大量 token 恢复工作环境，而不是推进新功能。</li>
<li><span style="background-color: #c0c0c0;">伪完成标记（False Completion）</span>：Agent 完成代码改动并运行单元测试或 curl 命令后，在没有进行端到端验证的情况下把功能标记为已完成，而该功能实际上并不能正常工作。</li>
<li><span style="background-color: #c0c0c0;">上下文焦虑（Context Anxiety）</span>：部分模型在上下文窗口填充到一定程度后，会主动收尾、提前结束任务——即便任务尚未完成，这种过早的"善后行为"会导致中途截断的工作状态。</li>
</ul>
<p>这四类失效都不能靠"换一个更好的模型"自动解决。它们是系统性问题，需要 Harness 层面的工程设计来对抗。</p>
<div class="blog_h3"><span class="graybg">失败驱动的 Harness 演化</span></div>
<p>Harness 是通过失败持续生长的控制层。一个重要的工程判断是：当 Agent 在某类任务上反复失败时，应先把失败当作 Harness 缺口的定位信号。失败可能意味着工具接口缺失、文档不可达、架构边界不清、状态交接不足，或验收标准仍停留在模糊自然语言层面。</p>
<p>因此，Harness Engineering 的关键动作是把失败外化成新的系统组件：增加脚本、补充文档、收紧 lint 规则、拆出更明确的 Contract、补上 evaluator 或可观测性接入。每次失败若都能回写为新的约束与支撑物，Agent 系统就会逐步从“靠经验驱动”演化为“靠机制驱动”。</p>
<div class="blog_h2"><span class="graybg">上下文管理策略</span></div>
<div class="blog_h3"><span class="graybg">上下文焦虑与两种应对策略</span></div>
<p>上下文焦虑（Context Anxiety）是指模型在感知到上下文窗口即将耗尽时，主动把任务包装成"已完成"状态——即便还有大量工作未做。Anthropic 在 Claude Sonnet 4.5 上观察到这一现象尤为明显。</p>
<p>应对上下文耗尽有两种互相对立的策略：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">策略</td>
<td style="text-align: center;">机制</td>
<td style="text-align: center;">保留连续性</td>
<td style="text-align: center;">消除上下文焦虑</td>
<td style="text-align: center;">代价</td>
</tr>
</thead>
<tbody>
<tr>
<td>压缩（Compaction）</td>
<td>将早期对话摘要替换原文，同一 Agent 继续运行</td>
<td>是</td>
<td>否（焦虑可能持续）</td>
<td>摘要信息损失</td>
</tr>
<tr>
<td>重置（Context Reset）</td>
<td>清空上下文，新 Agent 从结构化交接物（Handoff Artifact）重启</td>
<td>否（全新起点）</td>
<td>是</td>
<td>需要构造高质量交接物；增加编排复杂度与延迟</td>
</tr>
</tbody>
</table>
<p>对于 Sonnet 4.5，仅靠 Compaction 不足以支撑长任务，Context Reset 成为 Harness 设计的必要组件。随着 Opus 4.6 的推出，其长上下文检索能力显著提升、上下文焦虑基本消除，Context Reset 可以从 Harness 中移除——这正是 Harness 组件应随模型能力动态调整的典型示例。</p>
<p>一个很有价值的经验规律是：上下文窗口并不是“越满越好”。多组实践都观察到，随着无关文档、工具说明和历史对话不断堆积，Agent 的推理质量会先升后降。工程上可以把这理解为上下文利用率的“甜蜜区间”：模型需要足够信息才能稳定工作，但一旦被冗余材料淹没，就会出现幻觉、循环、格式错误或低质量实现。Harness 的任务不是把更多 token 塞进去，而是让 Agent 在任意时刻只看到当前步骤真正需要的信息。</p>
<div class="blog_h3"><span class="graybg">跨会话状态传递</span></div>
<p>长运行 Agent 在会话之间传递状态，不能依赖模型的内部记忆，必须依靠外化的持久化制品（Persistent Artifacts）。实践中形成了一套固定的状态传递套件：</p>
<ul>
<li><span style="background-color: #c0c0c0;">进度文件（Progress File）</span>：纯文本日志，记录每个会话完成了什么、遇到了什么问题、下一步是什么。每个会话开始时读取，结束时更新。</li>
<li><span style="background-color: #c0c0c0;">功能列表文件（Feature List）</span>：结构化的功能需求清单，初始全部标记为未完成，Agent 逐项实现并通过测试后方可标记通过。使用 JSON 而非 Markdown——实验发现模型不当覆写或修改 JSON 文件的概率远低于 Markdown 文件。</li>
<li><span style="background-color: #c0c0c0;">启动脚本（init.sh）</span>：由初始化代理预先写好的环境恢复入口，负责安装依赖、启动必要服务并打印访问入口。后续 fresh session 不再重新猜测项目如何运行，而是把它作为标准恢复脚本，在需要时检查并调用。</li>
<li><span style="background-color: #c0c0c0;">Git 历史</span>：每个会话以描述性的 commit message 结束。Git 历史既是进度时间线，也是恢复机制——Agent 可以通过 <pre class="crayon-plain-tag">git revert</pre> 从错误改动中恢复。</li>
</ul>
<p>这套设计借鉴的是人类软件工程师的轮班交接实践：进度文件对应交班笔记，git commit 对应有记录的工作移交，init.sh 对应标准化的环境搭建步骤，功能列表对应待办看板。</p>
<p>当单个 Agent 已经能够跨会话持续推进后，下一个问题就不再是“如何记住过去”，而是“如何把复杂任务拆给更合适的执行者”。这正是多 Agent 架构出现的背景。</p>
<div class="blog_h2"><span class="graybg">多 Agent 架构模式</span></div>
<div class="blog_h3"><span class="graybg">GAN 启发的 Generator–Evaluator 结构</span></div>
<p>让单个 Agent 对自己的输出进行评估会产生<span style="background-color: #c0c0c0;">自评偏差（Self-Evaluation Bias）</span>：即使输出质量明显一般，模型也倾向于给出积极评价。这一问题在主观任务上尤为突出，在可验证任务上同样存在。</p>
<p>一个有效的应对结构来自生成对抗网络（Generative Adversarial Network, GAN）的启发：将生成者和评估者分离成两个独立 Agent。</p>
<pre class="crayon-plain-tag">[Generator Agent] &larr;── feedback ──[Evaluator Agent]
        │                                 │
        └──────── output artifact ────────┘</pre>
<p>关键发现：调教一个独立的评估者使其保持怀疑态度，远比让生成者对自己的输出保持批判性更为可行。评估者仍然是 LLM，仍然有宽容倾向，但针对评估者进行专项提示调优的效果可以稳定收敛，而自评调优往往无效。</p>
<div class="blog_h3"><span class="graybg">Planner–Generator–Evaluator 三 Agent 架构</span></div>
<p>在 Generator–Evaluator 基础上增加一个 Planner Agent，构成完整的三 Agent 架构：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center; width: 15%;">Agent</td>
<td style="text-align: center; width: 20%;">输入</td>
<td style="text-align: center; width: 20%;">输出</td>
<td style="text-align: center;">设计要点</td>
</tr>
</thead>
<tbody>
<tr>
<td>Planner</td>
<td>1–4 句用户提示</td>
<td>完整产品规格书</td>
<td>只关注产品上下文与高层技术设计，不指定实现细节（过度具体的规格会将错误级联到下游）；被提示在规格中寻找机会融入 AI 功能</td>
</tr>
<tr>
<td>Generator</td>
<td>产品规格 + Sprint Contract</td>
<td>可运行代码</td>
<td>按 Sprint 增量构建（早期模型）或持续运行（强模型）；完成 Sprint 后先自评，再交 Evaluator 审查；拥有版本控制权限</td>
</tr>
<tr>
<td>Evaluator</td>
<td>运行中的应用 + Sprint Contract 验收标准</td>
<td>通过/失败判定 + 具体可行的问题报告</td>
<td>通过 Playwright MCP 实际操作运行中的应用；设有硬性阈值，任一标准未达到则 Sprint 失败并返回修复</td>
</tr>
</tbody>
</table>
<p>Agent 之间的通信完全基于文件：一个 Agent 写文件，另一个读文件并响应。这种方式简单、可追溯、不依赖框架内部状态。</p>
<div class="blog_h3"><span class="graybg">初始化 Agent 与编码 Agent 的分离</span></div>
<p>对于需要跨多个上下文窗口持续工作的场景，一种实用模式是将第一次会话与后续会话用不同的提示驱动：</p>
<ul>
<li><span style="background-color: #c0c0c0;">初始化 Agent（Initializer Agent）</span>：仅在第一个会话运行，负责构建整套脚手架：根据项目规格生成 init.sh、功能列表 JSON、初始 git commit 和进度文件，把“项目如何启动、功能如何追踪、状态如何恢复”一次性外化成可执行制品。</li>
<li><span style="background-color: #c0c0c0;">编码 Agent（Coding Agent）</span>：从第二个会话起运行，先读取进度文件、功能列表和最近的 git 历史，再按需调用 init.sh 恢复环境，每次只实现一个功能，并以"干净状态（Clean State）"结束会话。</li>
</ul>
<p>两个"Agent"在技术上是同一套系统提示和工具集，区别仅在于初始用户提示不同——这是一种提示策略，而非架构分离。</p>
<p>这种设计的关键，不是简单多了一个 shell 脚本，而是把“如何把项目跑起来”从高 token 成本的推断问题，转成低成本、可重复、可审计的脚本执行问题。后续每个 fresh session 的标准启动序列因此可以稳定下来：先用 <pre class="crayon-plain-tag">pwd</pre> 与最近的 <pre class="crayon-plain-tag">git log</pre> 确认当前工作状态，读取进度文件和功能列表定位下一项任务，再检查并在需要时调用 <pre class="crayon-plain-tag">init.sh</pre> 恢复依赖、服务和访问入口。模型不再反复猜测包管理器、启动顺序、端口和运行命令，上下文预算因此可以集中到真正的实现与验证上。</p>
<div class="blog_h3"><span class="graybg">Sprint Contract：预协议的完成定义</span></div>
<p>Sprint Contract 是 Generator 与 Evaluator 在任何代码被写下之前，对"这一阶段完成的标准"达成共识的协议制品：Generator 提出将要构建什么以及如何验证成功，Evaluator 审查并确认，双方迭代直到达成一致。其目的是弥合高层用户故事与可测试实现之间的鸿沟，防止模糊需求在实现阶段演变为争议。</p>
<p>任务拆分解决了“谁来做”，但还没有解决“知识从哪里来、如何保持最新”。一旦 Agent 工作跨越多个会话、多个角色和多个代码域，知识组织方式本身就会成为 Harness 的一部分。</p>
<div class="blog_h2"><span class="graybg">知识库工程（Repository as System of Record）</span></div>
<div class="blog_h3"><span class="graybg">给 Agent 地图而非手册</span></div>
<p>“给 Agent 地图而非手册”讨论的是知识入口（Knowledge Entry Point）的设计原则：在大型、持续演化的代码库中，Agent 需要的是一套能够把它稳定引导到正确信息源的导航结构，而不是一份试图囊括所有细节的单体说明书。知识库工程的关键目标是让上下文可定位、可更新、可裁剪，而不是一次性塞满。</p>
<p>因此，这里的首要挑战是"如何让 Agent 在需要时找到正确信息"。"一个巨型 AGENTS.md 文件"的方案会失败，原因是多方面的：</p>
<ul>
<li>上下文是稀缺资源。一个巨大的指令文件会挤占任务本身、代码和相关文档的空间，导致 Agent 要么遗漏关键约束，要么对错误的目标进行优化。</li>
<li>过多的指导等于没有指导。当所有内容都被标记为"重要"时，Agent 会退化成局部模式匹配，而不是有目的地导航。</li>
<li>单体文档会迅速腐烂。人类停止维护，Agent 无法分辨哪些规则仍然有效，文件变成一个充满过期规则的吸引力陷阱。</li>
</ul>
<p>有效的替代方案是：<span style="background-color: #c0c0c0;">AGENTS.md 充当目录（Table of Contents），而非百科全书</span>。一份约 100 行的精简 AGENTS.md 作为上下文的入口，通过指针将 Agent 引导至结构化 docs/ 目录中的具体知识源。</p>
<p>AGENTS.md 还有一个更重要的角色：它不应是一次写完后长期冻结的静态文档，而应是失败驱动的活反馈循环。每当 Agent 因为命令使用错误、目录理解偏差、架构约束遗漏或工具接入方式不清而出错，最直接的修复方式往往就是把这类经验回写进 AGENTS.md 或其指向文档。这样，文档不再只是说明书，而是把历史失败压缩成未来会话可直接继承的系统记忆。</p>
<pre class="crayon-plain-tag">AGENTS.md            &larr; 约100行，地图角色
ARCHITECTURE.md
docs/
├── design-docs/
│   ├── index.md
│   └── core-beliefs.md
├── exec-plans/
│   ├── active/
│   └── completed/
├── generated/
│   └── db-schema.md
├── product-specs/
│   └── index.md
├── references/
│   ├── design-system-reference-llms.txt
│   └── ...
├── DESIGN.md
├── FRONTEND.md
├── PLANS.md
└── QUALITY_SCORE.md</pre>
<p>这种结构实现了渐进披露（Progressive Disclosure）：Agent 从一个小而稳定的入口开始，被引导到它需要的具体知识，而不是在启动时被所有信息淹没。</p>
<div class="blog_h3"><span class="graybg">组织级 Golden Path</span></div>
<p>当团队反复构建相似类型的系统时，Harness 不应只停留在单项目经验，而应上升为组织级的服务模板（Service Template）或黄金路径（Golden Path）。现实中的软件形态并不是无限多样的，常见项目通常集中在少数几类技术拓扑：前端应用、后端服务、数据流水线、内部工具。若能把每类拓扑常用的目录结构、启动脚本、验证流水线、可观测性接入、架构约束和 Agent 指令打包成模板，新项目就不必从零设计 Harness。</p>
<p>这类模板的价值不只是“脚手架复用”，更在于组织把高频工程判断沉淀成标准化控制面。团队成员在真实项目中学到的约束、回滚经验和验证方法，可以持续回流到模板；模板更新后，又会反过来提升后续所有项目的默认质量。Harness 一旦进入这个阶段，便不再只是某个工程师的个人技巧，而是组织级生产力资产。</p>
<div class="blog_h3"><span class="graybg">Agent 可见性边界</span></div>
<p>从 Agent 的视角来看，任何它在运行时无法在上下文中访问的知识，实际上不存在。存在于 Google Docs、Slack 线程或人脑中的决策，对 Agent 来说与从未发生过没有区别。唯一对 Agent 有效的知识是代码库中版本化的制品：代码、Markdown、Schema、可执行计划。</p>
<p>这一约束倒逼团队把越来越多的上下文推入代码库：对齐团队架构决策的 Slack 讨论、产品原则、工程规范，都需要以 Agent 可读的形式在仓库中存在。这不仅是文档实践，更是系统设计约束。</p>
<div class="blog_h3"><span class="graybg">机械化知识维护</span></div>
<p>知识库的有效性需要机械化强制执行，而不能依靠人工自律。专用的 linter 和 CI 任务验证知识库是否最新、交叉引用是否完整、结构是否正确。一个周期性运行的"文档园丁 Agent（doc-gardening agent）"扫描文档与实际代码行为之间的偏差，发现过期或不一致的内容后自动开启修复 Pull Request。</p>
<p>知识工程解决的是“找得到信息”，架构约束工程解决的是“即使知道该做什么，也不能随意乱做”。两者结合，Agent 才既有方向感，也有边界感。</p>
<div class="blog_h2"><span class="graybg">架构约束工程</span></div>
<div class="blog_h3"><span class="graybg">分层领域架构</span></div>
<p>Agent 在具有严格边界和可预测结构的环境中工作效果最好。在全 Agent 生成的代码库中，这一原则需要在工程层面提前落地：等到有数百名工程师时再引入架构规则，通常已经太晚了——而在 Agent 驱动的代码库中，混乱会以比人类团队快得多的速度蔓延。</p>
<p>一种行之有效的结构是<span style="background-color: #c0c0c0;">分层领域架构（Layered Domain Architecture）</span>：每个业务域被划分为固定的层集合，依赖方向被严格验证，仅允许有限的跨层边。例如，在一个业务域内部，代码只能沿固定顺序向前依赖（Types → Config → Repo → Service → Runtime → UI）；跨切面关注点（认证、连接器、遥测、特性标志）通过单一显式接口进入。其他所有依赖方向都被机械地禁止。</p>
<div class="blog_h3"><span class="graybg">机械化强制执行</span></div>
<p>架构规则通过定制 linter 和结构测试强制执行，而不是依赖文档和代码审查中的人工判断。关键实践：</p>
<ul>
<li>linter 的错误信息被设计成直接包含修复指令，从而将约束违反转化为 Agent 可消费的上下文。</li>
<li>在对人类团队而言显得迂腐的细粒度规则，对 Agent 来说是乘数效应：一旦编码，立即在所有代码上生效，无需逐一审查。</li>
<li>约束划定边界，边界内部的实现方式允许 Agent 自由选择——这类似于大型平台工程组织的管理模式：集中强制边界，局部授权自治。</li>
</ul>
<p>这套约束还包括"品味不变量（Taste Invariants）"：结构化日志、Schema 与类型命名规范、文件大小限制、平台特定的可靠性要求。这些规则用静态分析强制执行，将人类工程师的审美判断固化成可机器检查的规则。</p>
<div class="blog_h3"><span class="graybg">结构化状态与写权限约束</span></div>
<p>对 Agent 可以修改的内容施加精确的写权限，是防止状态腐蚀的重要手段。典型模式：功能列表文件中，Agent 只允许修改 <pre class="crayon-plain-tag">passes</pre> 字段，不得删除或改写任何测试条目。这种狭义写权限用强措辞的指令约束来传达："删除或编辑测试是不可接受的行为，这会导致功能遗漏或引入 bug。"</p>
<p>此外，在数据边界处解析数据形状（而不是 YOLO 式地推断数据结构），使用类型化 SDK 或 Schema 验证库，是保持 Agent 可以安全推理的代码库形态的基础约束。</p>
<p>边界清晰之后，系统仍然需要对 Agent “可见”。否则即使约束被写得很严，Agent 也只能在黑箱里试错，无法高效定位运行时问题。</p>
<div class="blog_h2"><span class="graybg">Agent 可读性（Legibility）</span></div>
<p>Agent 可读性（Agent Legibility）指的是：系统的 UI、日志、状态和指标对 Agent 来说是否足够可见、可解析、可操作，从而使模型能够直接观察应用行为、复现问题并验证修复结果。它关注的是系统是否为推理提供了足够清晰的外部反馈面。</p>
<p>随着代码吞吐量提升，人工 QA 容量会成为瓶颈。此时，关键路径不再是增加人力，而是让应用自身的 UI、日志和指标对 Agent 直接可读，使 Agent 能够自主复现 bug、验证修复并对应用行为进行推理。</p>
<div class="blog_h3"><span class="graybg">应用可读性</span></div>
<p>使应用对 Agent 可读的关键手段：</p>
<ul>
<li><span style="background-color: #c0c0c0;">每个 git worktree 独立启动一个应用实例</span>：Agent 可以针对自己的变更启动隔离的应用版本，互不干扰。</li>
<li><span style="background-color: #c0c0c0;">Chrome DevTools Protocol（CDP）接入</span>：将 CDP 接入 Agent 运行时，并封装操作 DOM 快照、截图和页面导航的技能，使 Agent 能够驱动浏览器、复现 bug 并验证修复，而不依赖人工截图传递。</li>
<li><span style="background-color: #c0c0c0;">Playwright/Puppeteer MCP</span>：在多 Agent 架构中，评估 Agent 使用浏览器自动化工具实际操作运行中的应用，而不是静态分析代码。这使得"应用看起来工作"与"应用实际可用"之间的差距得以被发现。需注意：浏览器原生弹窗（alert modal）对这类工具不可见，这是已知盲区。</li>
</ul>
<div class="blog_h3"><span class="graybg">可观测性栈</span></div>
<p>将完整的可观测性栈暴露给 Agent，使其可以查询日志、指标和追踪数据，是将模糊性能目标转化为 Agent 可执行任务的关键。每个 worktree 配备临时的本地可观测性栈（完成后自动销毁），Agent 可通过 LogQL 查询日志、PromQL 查询指标、TraceQL 查询链路追踪。</p>
<p>有了这种接入能力，"确保服务启动在 800ms 以内完成"或"这四条关键用户路径中没有 span 超过两秒"这类提示就变得可执行——Agent 可以直接查询数据、定位问题根源并实施修复，而不需要人工解读日志然后描述问题。</p>
<p>不过，看得见系统状态还不等于能正确判断任务是否完成。可读性回答的是“发生了什么”，验证闭环回答的是“这样是否算成功”。</p>
<div class="blog_h2"><span class="graybg">验证闭环</span></div>
<div class="blog_h3"><span class="graybg">主观评判的量化</span></div>
<p>对于没有二元正确性检验的任务（如界面设计、用户体验），需要将主观判断转化为具体的、可评分的标准，才能构建有效的验证闭环。一套已验证有效的前端设计评分框架包含四个维度：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">维度</td>
<td style="text-align: center;">评估内容</td>
<td style="text-align: center;">权重</td>
</tr>
</thead>
<tbody>
<tr>
<td>Design Quality（设计质量）</td>
<td>配色、字体、布局、视觉意象是否融合为一个有清晰气质和独特身份的整体，而非各部分的简单堆砌</td>
<td>高</td>
</tr>
<tr>
<td>Originality（原创性）</td>
<td>设计决策是否经过定制，还是使用了模板布局、库默认值、AI 生成惯例模式（紫色渐变白色卡片、未经修改的 stock 组件）</td>
<td>高</td>
</tr>
<tr>
<td>Craft（工艺）</td>
<td>字体层级、间距一致性、颜色对比度等技术执行质量</td>
<td>低（模型默认已做得较好）</td>
</tr>
<tr>
<td>Functionality（功能性）</td>
<td>用户能否理解界面功能、找到主要操作、完成任务而无需猜测</td>
<td>低（模型默认已做得较好）</td>
</tr>
</tbody>
</table>
<p>重要发现：评估者提示中的措辞本身（如"最好的设计是博物馆级别的"）会直接影响生成器的输出风格——不仅仅在评估反馈阶段生效，在第一次生成时就已经开始引导模型远离通用默认值。评估者需通过 Few-shot 示例加详细评分解析进行校准，以保持判断一致性并减少分数漂移。</p>
<div class="blog_h3"><span class="graybg">端到端测试与健康检查门控</span></div>
<p>单元测试和 curl 命令验证的是代码片段，而不是用户体验。端到端测试的原则是：<span style="background-color: #c0c0c0;">像人类用户一样测试</span>——通过浏览器自动化工具实际操作运行中的应用，才能发现"代码逻辑看似正确但功能整体不可用"的问题。</p>
<p>实施健康检查门控（Health-Check Gate）：在每个会话开始时，Agent 必须先运行基础端到端功能测试，确认应用处于正常工作状态，再开始实现新功能。如果发现应用处于损坏状态，应先修复，否则新功能会在损坏的基础上叠加，问题会更难追溯。</p>
<p>干净状态（Clean State）的定义：每个会话结束时，代码库应处于可合并到主分支的状态——无重大 bug，代码有序且有文档，后续开发者（或 Agent）可以直接开始新功能，而无需先清理遗留的乱局。</p>
<div class="blog_h3"><span class="graybg">功能验证缺口</span></div>
<p>Harness 案例里常见一个容易被忽略的盲点：系统也许已经很好地约束了内部质量，却未必充分验证了外部行为是否真的符合产品意图。目录结构、架构 lint、单元测试、Clean State 和持续回收都很重要，但这些机制首先保证的是内部一致性，而不是最终功能一定可用。</p>
<p>因此，成熟的 Harness 必须把<span style="background-color: #c0c0c0;">功能验证（Functionality Verification）</span>视为独立承重件，而不是默认附属物。真正可靠的系统需要对“用户是否真的能完成任务”建立单独的验证回路，例如任务级验收标准、浏览器自动化、独立评估 Agent、人工 spot check，或基于运行时信号的行为检查。能持续写代码只是起点；能持续验证行为，才是 Harness 真正闭环的标志。</p>
<p>即使单次任务已经具备了约束、可读性和验证，长期运行后的系统仍会累积漂移。Harness 因此不仅要管每一轮执行是否正确，还要管整个代码库是否在时间尺度上持续失真。</p>
<div class="blog_h2"><span class="graybg">熵控制与垃圾回收</span></div>
<p>Agent 会复制代码库中已存在的模式——即使这些模式并不理想。随着时间推移，全 Agent 生成的代码库会发生不可避免的漂移：重复工具函数扩散、文档与代码行为不同步、架构规则被逐渐侵蚀。早期的应对方式是每周花费 20% 的团队时间手动清理，但这完全不可扩展。</p>
<p>有效的替代方案是将"黄金原则（Golden Principles）"直接编码进代码库，并建立周期性的自动化垃圾回收流程：</p>
<ul>
<li>在代码库中明确记录有主见的、机械可检查的原则（如：优先使用共享工具包而非手写 helper；必须在边界处解析数据形状，不得基于猜测的结构构建逻辑）。</li>
<li>周期性运行后台 Agent 任务，扫描原则违反、更新质量评分、开启针对性重构 Pull Request。</li>
<li>大多数这类 PR 可以在一分钟内完成审查并自动合并。</li>
</ul>
<p>技术债务与高息贷款相似：持续小额偿还几乎总是优于积累后集中清算。人类工程师的品味判断被捕获一次，随后在每一行代码上持续强制执行——这是 Agent 驱动开发模式下特有的杠杆效应。</p>
<p>但这些组件本身并不是一成不变的。任何垃圾回收策略、验证门控或多 Agent 编排，背后都隐含着对当前模型能力的判断；一旦模型变化，Harness 的承重结构也需要重新评估。</p>
<div class="blog_h2"><span class="graybg">Harness 随模型演化（Load-Bearing Analysis）</span></div>
<p>Harness 中的每一个组件都编码了一个假设：模型目前无法独立完成这件事，所以需要这层脚手架。这些假设有两个失效原因：</p>
<ul>
<li>假设从一开始就是错的——模型其实能做到，但工程师没有测试验证就加了脚手架。</li>
<li>假设因模型能力提升而过时——昨天需要 Sprint 分解的任务，今天的模型可以持续完成而不失去连贯性。</li>
</ul>
<p>因此，评估哪些 Harness 组件是真正的<span style="background-color: #c0c0c0;">承重结构（Load-Bearing Components）</span>——每当新模型发布时，剥除不再有效的部分，添加利用新能力的新部分——是 Harness 工程师持续的工作职责。</p>
<p>一个典型的演化轨迹：Claude Sonnet 4.5 需要 Context Reset + Sprint 分解 + 完整三 Agent 架构；Opus 4.5 在三 Agent 架构下表现更好，Sprint 结构依然必要；Opus 4.6 消除了上下文焦虑，Generator 可以持续运行超过两小时而无需 Sprint 分解，Evaluator 从"始终必要"变为"在任务超出模型原生能力时才有价值"。这套 Harness 对于 Sonnet 4.5 来说恰到好处，对于 Opus 4.6 来说则是过度设计。</p>
<p>一个重要的元原则：<span style="background-color: #c0c0c0;">随着模型能力提升，有趣的 Harness 组合空间不会缩小，而是移动</span>。工程师的工作不是等待模型强大到不需要 Harness，而是持续找到下一个新颖的组合。</p>
<p>一旦系统的主要难点从“写出代码”转移到“设计并维护这套控制面”，工程师的职责也会随之改变。</p>
<div class="blog_h2"><span class="graybg">工程师角色的演变</span></div>
<p>在 Harness Engineering 语境下，工程师的工作重心发生了系统性上移。原本直接产出代码的工作，转变为设计 Agent 工作的环境、指定意图与验收标准、构建使 Agent 能可靠工作的反馈闭环，以及将人类判断和品味持续编码进系统。</p>
<p>核心原则可以概括为一句话：<span style="background-color: #c0c0c0;">Humans steer, agents execute</span>。这意味着人类在更高的抽象层次上工作：确定优先级、将用户反馈转化为验收标准、验证结果。当 Agent 遇到障碍时，工程师更应追问"缺少什么能力？如何让这个需求变得对 Agent 可理解且可强制执行？"</p>
<p>随着代码吞吐量的提升，许多传统工程规范变得适得其反。PR 在高吞吐系统中应该是短命的；测试 flakiness 有时更适合通过后续补丁修复而非阻塞合并；纠错的成本极低，等待的成本极高。这与低吞吐人工团队的工作假设完全相反。</p>
<p>不过，这些方法目前验证得最充分的，仍然主要是绿地项目或受控实验环境。一旦进入历史包袱深重的老系统，Harness 的建设顺序与成本结构都会发生变化。</p>
<div class="blog_h2"><span class="graybg">棕地改造的难题</span></div>
<p>当前公开的成功 Harness 案例大多发生在绿地项目、受控实验或可以从零搭脚手架的环境中。真正更难的问题，是如何把 Harness Engineering 引入一个已经运行多年、缺少架构边界、测试质量不稳定、文档残缺且历史包袱沉重的棕地代码库。这里最大的风险不是 Agent 不会写代码，而是现有系统本身已经缺乏足够清晰的控制面，Agent 一旦接手，只会更快复制并放大原有混乱。</p>
<p>因此，棕地改造不能照搬绿地模板，通常需要渐进式引入：先补最关键的边界约束，再建立最小可用的知识入口、验证门控和可观测性接入，最后才考虑多 Agent 编排或更高自治级别。换言之，Harness 在棕地场景里的首要任务不是提升速度，而是先把代码库变成一个对 Agent 可理解、可约束、可验证的环境。</p>
<div class="blog_h2"><span class="graybg">参考案例</span></div>
<p>OpenAI Codex 内部实验（2026 年 2 月）：三名工程师在约五个月内，通过零人工手写代码的流程，构建了一个拥有内部日常用户和外部 alpha 测试用户的内部 beta 产品。代码库规模约一百万行，覆盖应用逻辑、测试、CI 配置、文档、可观测性和内部工具。约 1500 个 PR 被合并，平均吞吐为每名工程师每天 3.5 个 PR，且随团队扩张吞吐还在提升。核心投入集中在：精简的 AGENTS.md 目录结构、分层领域架构 + 机械化 linter、应用 + 可观测性对 Agent 的完整可读性、黄金原则与周期性垃圾回收 Agent。</p>
<p>Anthropic 复古游戏编辑器对比实验（2026 年）：使用 Claude Opus 4.5，对同一个 prompt "构建一个 2D 复古游戏制作工具"进行两种模式的比较。单 Agent 单次运行：20 分钟，花费 9 美元，结果是布局浪费空间、核心玩法无法运行。完整三 Agent Harness（Planner + Generator + Evaluator，含 Sprint Contract）：6 小时，花费 200 美元，结果是 Planner 将一句 prompt 扩展为横跨 10 个 Sprint 的 16 项功能规格，游戏核心机制正常运行，并额外实现了精灵动画、AI 辅助关卡设计等超出原始 prompt 的功能。成本约为单次运行的 22 倍，但输出质量远超。</p>
<p>Anthropic 浏览器 DAW 实验（2026 年）：使用 Claude Opus 4.6（已移除 Sprint 分解），通过 prompt "构建一个基于 Web Audio API 的全功能浏览器 DAW"，三轮构建 + QA 循环总计 3 小时 50 分钟，花费 124.70 美元。最终产物包含可用的编排视图、混音台和传输控制；内置 AI Agent 可通过自然语言提示设置节拍/调性、演奏旋律、建立鼓轨、调节混音和添加混响，完成一段完整的短曲创作。</p>
<div class="blog_h1"><span class="graybg">检索增强生成（RAG）</span></div>
<p>检索增强生成（Retrieval-Augmented Generation, RAG）把“外部知识”通过检索在推理时注入到上下文里：先检索，再生成。它解决的不是模型能力问题，而是信息可得性与可控性问题：把知识放在可更新的外部存储里，而不是指望模型参数记住一切。</p>
<p>真正落地到生产后，RAG 很快会从“模型 + 向量库”的玩具结构，变成一套完整的知识处理系统。一个更接近现实的抽象是：<span style="background-color: #c0c0c0;">知识源接入、文档获取、分块、增强、索引、召回、融合、重排、上下文回拼、生成</span>。其中任何一步做得粗糙，最终回答质量都会明显退化。</p>
<p>从工程视角看，RAG 的核心是把知识库建成一个可持续演化的外部记忆系统，而不是简单把文本切块后丢进向量数据库。这里通常至少会区分三层对象：知识库（Base）负责来源配置、召回策略与索引策略；文档（Document）负责内容文件、语言、状态与分块配置；块（Chunk）负责可检索的最小语义单元及其摘要、问题、向量、命中统计等元数据。再往上，很多系统还会加一个目录（Catalog）层，把知识按业务树、产品线、权限域或租户边界组织起来。这种分层的意义在于：<span style="background-color: #c0c0c0;">RAG 检索的不是“一个文本文件夹”，而是一个带治理、带生命周期、带配置继承的知识对象系统</span>。</p>
<div class="blog_h2"><span class="graybg">生产 RAG 的系统视角</span></div>
<p>一个成熟的知识库系统通常会显式区分知识源（Source）与检索目标（Target）。知识源回答“内容从哪里来”，例如上传文件、网页抓取、代码仓库、目录扫描；检索目标回答“内容最终由谁提供召回能力”，可以是系统自建索引，也可以是外部知识平台。这样设计的好处是把“接入来源”与“服务出口”解耦：同一套文档采集和治理流程，可以接到不同的检索后端，而不用把采集逻辑和向量库实现绑死。</p>
<p>另一个关键决策是把知识处理做成异步流水线，而不是同步上传即就绪。现实中的知识库往往需要经历扫描、下载、解析、分块、摘要生成、问题生成、向量化、索引构建等多个阶段。把这些阶段拆开，一方面是因为它们耗时、依赖不同资源且失败模式不同；另一方面是为了支持恢复、重试、并发控制与增量更新。于是，生产 RAG 往往天然带有一个状态机：知识库有自己的状态，文档有自己的状态，块也有自己的状态。这个状态机的作用是把长链路处理过程显式化，使每个阶段都能被观察、重跑和局部修复。</p>
<p>教程中的“上传文档 ➡ embedding ➡ 检索 ➡ 回答”只覆盖了最小闭环。真实系统里的知识库会持续新增、修改、下线、重建索引，并不断调整切分方式和召回参数。因此，RAG 更接近“搜索系统 + 知识处理中台 + 生成模型”的组合，而不是一次性的离线脚本。</p>
<div class="blog_h2"><span class="graybg">分块策略（Chunking）</span></div>
<p>分块（Chunking）决定了“检索的最小单位”。块太大：召回相关信息但携带大量噪声；块太小：召回片段零散，缺上下文导致生成不稳。分块应与文档结构、问题类型、上下文窗口预算一起设计。</p>
<div class="blog_h3"><span class="graybg">固定大小分块</span></div>
<p>固定大小分块（Fixed-size Chunking）按 token/字符数切片，简单稳定，适合结构较弱的纯文本与大规模离线构建。常配合重叠窗口（Overlap）避免跨边界信息断裂。</p>
<div class="blog_h3"><span class="graybg">语义分块</span></div>
<p>语义分块（Semantic Chunking）用段落/标题/语义边界切分，目标是让一个 chunk 自洽（Self-contained）。它通常更适合技术文档与带层级结构的内容，但需要更复杂的解析与规则。</p>
<div class="blog_h3"><span class="graybg">递归分块</span></div>
<p>递归分块（Recursive Chunking）先按大结构切（章节/标题），再对子块继续切（段落/句子/固定大小），兼顾结构与长度约束，是很多工程实现的折中方案。</p>
<p>生产系统里的分块通常远比这三类更细。除了固定大小、递归和语义切分，还会按 Markdown、HTML、PDF、代码、表格、JSON、LaTeX、句子、段落、滑动窗口等内容类型选择不同 chunker。这背后的设计判断非常重要：<span style="background-color: #c0c0c0;">分块不是一个通用算法参数，而是内容理解策略的一部分</span>。代码和 API 文档更适合按语法结构或标题层级切；表格和 PDF 更需要保留版面边界；Markdown/HTML 则需要保留层次结构，甚至形成父子 chunk 树。</p>
<p>这类设计会直接改变召回质量。若把所有内容都按固定长度切块，标题、层级、页面边界、代码块和表格结构都会被抹平；若系统保存 chunk 的父子关系、边界分隔符、正文起止位置、重叠前后文与结构元数据，那么检索命中的就不再只是孤立片段，而是文档结构中的一个明确位置。此时的 RAG 更接近结构化文档检索。</p>
<p>Agentic Chunking 进一步把 LLM 引入切分阶段，由模型判断哪些内容应属于同一语义单元。它在复杂文档上可能带来更强的语义完整性，但代价同样明显：构建成本高、可重复性差、调试难度大，提示词质量会直接影响索引结果。因此它更适合作为高价值内容的增强型切分，而不适合作为默认的全量基础设施。</p>
<div class="blog_h2"><span class="graybg">向量数据库</span></div>
<p>向量数据库（Vector Database）存储每个 chunk 的向量表示（Embedding Vector）及其元数据（Metadata），支持近似最近邻（Approximate Nearest Neighbor, ANN）检索。向量并不“包含全文”，它是语义相似度的索引键；检索结果仍然需要回源拿到原文片段。</p>
<p>工程上需要区分“向量索引（Vector Index）”与“向量数据库（Vector Database）”：前者强调 ANN 检索算法与数据结构；后者强调持久化、增量更新、元数据过滤（Metadata Filtering）、分片/副本与多租户（Multi-tenancy）等系统能力。</p>
<p>生产知识库通常会同时维护多个索引平面：chunk 正文索引、chunk 摘要索引、chunk 问题索引，必要时还有文档摘要索引。它们对应的是不同的检索语义：正文索引擅长直接召回原文；摘要索引更适合长文压缩后的主题级匹配；问题索引则把 chunk 改写成更贴近用户查询的可检索意图。因此，生产 RAG 的索引设计本质上是“同一内容的多种检索视图”。</p>
<p>双后端架构也是常见现实。高质量知识库常把 Milvus/Qdrant 一类向量系统与 Elasticsearch/OpenSearch 一类全文检索系统并行使用：前者负责稠密向量、稀疏向量与 ANN 检索，后者负责 BM25、字段过滤和业务搜索。这种组合对应的是信号分工：向量擅长语义匹配，词项系统擅长精确关键词与过滤。真正的难点随之转移到一致性和融合上：索引命名要对齐，更新要同步，删除要清理，跨系统分数不能直接比较。</p>
<div class="blog_h3"><span class="graybg">实现形态与选型</span></div>
<p>向量检索组件通常有三种部署形态：向量索引库（如 FAISS/ScaNN）偏“库”；向量数据库（如 Milvus/Qdrant/Weaviate）偏“服务”；全文检索引擎（如 Elasticsearch/OpenSearch）则以 BM25 为核心并逐步补齐向量检索能力。许多生产系统采用“双引擎”组合：<span style="background-color: #c0c0c0;">向量库负责高性能 ANN，全文检索负责复杂过滤与关键词召回</span>，最终在融合/重排阶段统一排序。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">形态</td>
<td style="text-align: center;">代表实现</td>
<td style="text-align: center;">优势</td>
<td style="text-align: center;">局限</td>
<td style="text-align: center;">适用场景</td>
</tr>
</thead>
<tbody>
<tr>
<td>向量索引库（In-process Vector Index）</td>
<td>FAISS / ScaNN</td>
<td>单机性能极强；集成简单；易做离线批构建</td>
<td>分布式/多租户/权限/运维能力弱；元数据过滤能力有限</td>
<td>原型验证；单机检索；离线候选生成</td>
</tr>
<tr>
<td>向量数据库（Vector Database Service）</td>
<td>Milvus / Qdrant / Weaviate</td>
<td>持久化 + CRUD；支持元数据过滤；更易做水平扩展</td>
<td>需要运维；延迟/吞吐取决于索引与集群配置</td>
<td>生产 RAG；增量更新频繁；需要过滤/权限</td>
</tr>
<tr>
<td>全文检索 + 向量检索（Hybrid Search Engine）</td>
<td>Elasticsearch / OpenSearch</td>
<td>BM25 + 过滤能力强；生态成熟；适合业务检索</td>
<td>向量能力与性能细节高度依赖具体版本/配置；向量索引形态可选项较少</td>
<td>强关键词依赖；复杂过滤/字段检索；混合检索一体化</td>
</tr>
<tr>
<td>组合架构（Vector DB + Search Engine）</td>
<td>Milvus/Qdrant + ES</td>
<td>各取所长；向量与词项召回分别优化；融合/重排可控</td>
<td>系统更复杂；需要去重、打分对齐与一致性策略</td>
<td>对检索质量要求高，且有复杂过滤/排序逻辑</td>
</tr>
<tr>
<td>托管服务（Managed Service）</td>
<td>Pinecone / 各云厂商向量服务</td>
<td>运维成本低；弹性扩缩容；通常带权限与监控</td>
<td>成本较高；可控性与部署形态受限</td>
<td>快速上线；团队缺乏检索基础设施经验</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">ANN 索引与过滤</span></div>
<p>ANN 索引（ANN Index）在“速度、召回率（Recall）与内存”之间权衡。检索侧常见痛点是“向量相似度可算，但业务过滤很难做快”：元数据过滤会破坏 ANN 的近邻结构，使得系统需要在“先过滤再向量检索”和“先向量检索再过滤”之间做策略选择。</p>
<p>在生产知识库里，过滤通常不是可选项，而是第一等公民。目录树（Catalog Tree）、启用状态、文档类型、租户边界、语言、内容类型、业务域前缀，都可能成为过滤条件。也正因为如此，许多系统会把 catalog 前缀过滤、enabled 标志、source_type/source_id 等字段同时写入 Milvus 和 ES，两边都能先做过滤再做召回。否则，召回质量再高，也可能把“本不该返回的内容”混进上下文。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">索引/策略</td>
<td style="text-align: center;">核心思路</td>
<td style="text-align: center;">优点</td>
<td style="text-align: center;">代价</td>
</tr>
</thead>
<tbody>
<tr>
<td>Flat（Exact）</td>
<td>全量计算相似度</td>
<td>精确；实现最简单</td>
<td>规模上去后延迟/成本不可接受</td>
</tr>
<tr>
<td>HNSW</td>
<td>分层小世界图近邻搜索</td>
<td>高召回、低延迟；对在线增量友好</td>
<td>内存占用较高；过滤条件复杂时性能波动</td>
</tr>
<tr>
<td>IVF / IVF-PQ</td>
<td>先粗聚类（倒排），再在桶内搜索；PQ 进一步压缩</td>
<td>更省内存；适合大规模离线构建</td>
<td>更新成本高；召回/延迟依赖参数调优</td>
</tr>
<tr>
<td>DiskANN / Hybrid Memory</td>
<td>把索引/向量放到 SSD，内存放导航结构</td>
<td>降低内存压力；可支持更大规模</td>
<td>I/O 成为瓶颈；工程复杂度更高</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">检索策略</span></div>
<div class="blog_h3"><span class="graybg">稠密检索（向量相似度）</span></div>
<p>稠密检索（Dense Retrieval）用向量相似度（如余弦相似度或内积）做 top-k 召回，擅长语义匹配与同义改写，但可能漏掉精确关键词（如错误码、版本号）。</p>
<p>它还有一个常被低估的前提：embedding 模型的训练分布必须与目标领域足够接近。若把主要基于互联网通用语料训练出来的嵌入模型，直接拿去检索法律、医疗、专利或企业内部术语密集的文本，向量空间中的“相似”往往就不再等于业务上的“相关”，召回质量会明显下降。此时更稳妥的做法通常是引入领域微调 embedding、混合检索，或让 sparse / full-text 路径共同兜底。</p>
<div class="blog_h3"><span class="graybg">全文检索（BM25 / ES）</span></div>
<p>全文检索（Lexical Retrieval）以 BM25 等词项匹配为核心，擅长精确匹配与关键词召回。它对拼写、专有名词、数字更敏感，但对语义改写不够鲁棒。</p>
<div class="blog_h3"><span class="graybg">稀疏检索（Sparse Retrieval / SPLADE）</span></div>
<p>稀疏检索（Sparse Retrieval）用“稀疏向量”表示文本：向量维度对应词项（或子词），权重表示该词项对匹配的贡献。与 BM25 相比，稀疏检索的权重来自模型学习而非纯统计；与稠密检索相比，它保留了词项级可解释性与对罕见词/数字的敏感性。</p>
<p>典型路线包括 SPLADE（Sparse Lexical and Expansion Model）：通过学习得到“词项扩展（Lexical Expansion）”，让语义相关但不共词的文本仍能在词项空间相遇。工程上，稀疏检索常作为混合检索的一路召回信号，而不是单独替代 BM25。</p>
<div class="blog_h3"><span class="graybg">混合检索</span></div>
<p>混合检索（Hybrid Retrieval）把稠密检索与全文检索结合：先分别召回，再融合排序（如加权、去重、学习排序）。这是目前最稳健的通用策略之一。</p>
<p>在更完整的知识库系统里，混合检索不只是“dense + BM25”两路，而可能是 <span style="background-color: #c0c0c0;">chunk / summary / question</span> 三类检索目标与 <span style="background-color: #c0c0c0;">dense / sparse / fulltext</span> 三类检索路径的组合矩阵。换句话说，生产 RAG 召回的是多个“信号通道”，而不是单一相似度函数。它的优势是覆盖面更强：原文命中、主题命中、问句改写命中可以互补；但代价也很直接：路径暴涨、分数更不可比、日志更难读、回归测试更复杂。</p>
<p>这类系统往往还会按知识库能力做“有效方法裁剪”。例如，用户请求 fullhybrid，但某个知识库只建了 dense 和 sparse 索引，那么实际就只能退化成 hybrid；某个库没有开启 question recall，就不该在该路径上浪费资源。这个细节反映出一个成熟的 RAG 判断：<span style="background-color: #c0c0c0;">检索策略不是全局写死的，而是知识库级配置与请求级策略共同决定的</span>。</p>
<div class="blog_h4"><span class="graybg">分数融合（Score Fusion）</span></div>
<p>混合检索的难点不在“多路召回”，而在“分数不可比（Incomparable Scores）”：BM25 分数、余弦相似度/内积分数、稀疏向量分数往往不在同一尺度，不能直接相加。常见融合策略：</p>
<ul>
<li>归一化后加权：对各路分数做 min-max / z-score 等归一化，再做加权求和或加权乘积。</li>
<li>基于排序的融合：不依赖原始分数尺度，例如 RRF（Reciprocal Rank Fusion）：<span displaypfx="inline-" class="mathjax-container">\(\mathrm{score}(d)=\sum_{s}\frac{1}{k+\mathrm{rank}_s(d)}\)</span>。</li>
<li>学习排序（Learning to Rank）：把各路分数与特征（BM25、embedding sim、字段匹配、长度等）作为特征，训练一个排序模型做融合。</li>
</ul>
<p>生产系统通常更偏爱基于排序的融合，而不是直接拼原始分数。这是因为 ES 的 <span displaypfx="inline-" class="mathjax-container">\(_score\)</span>、Milvus 稠密距离、Milvus 稀疏 BM25 分数往往来自完全不同的评分体系。RRF 或加权 RRF 的价值就在于鲁棒：它不要求各后端分数可比，只要求各路径给出相对顺序。若再往上追求精度，则会在粗召回后接一个模型重排器，对融合后的候选做统一判断。</p>
<div class="blog_h3"><span class="graybg">相关性阈值</span></div>
<p>相关性阈值（Relevance Threshold）决定的是：召回结果里哪些片段值得继续送给模型，哪些片段应该被直接丢弃。很多 RAG 系统的问题并不是“完全没召回到东西”，而是把大量边缘相关、低置信度甚至明显错误的片段一并塞进上下文，结果让生成阶段被噪声牵着走。</p>
<p>因此，top-k 本身通常不够；工程上还需要结合相似度阈值、分数差值阈值或最小证据数量规则共同判断。阈值设得过低，会把噪声片段混进上下文；阈值设得过高，又可能让系统在真实有答案时错误地进入“未检索到结果”分支。成熟系统往往会把阈值当作知识库级和请求级都可调的参数，并配合离线标注集或在线反馈持续校准。</p>
<div class="blog_h2"><span class="graybg">查询改写</span></div>
<p>查询改写（Query Rewriting）解决的问题是：用户真正表达的检索意图，经常并不等于用户字面上输入的那句话。尤其当用户提问冗长、上下文依赖强、代词很多、问题里混有解释性语句而不是检索关键词时，直接拿原问题去召回，往往不如先把问题改写成更适合检索的形式。</p>
<p>查询改写的核心是把问题重新组织成更适合索引命中的检索表达，例如补全省略主语、显式写出实体名、拆开复合问题、提取关键词、或把对话上下文中的隐含约束展开成可检索文本。</p>
<div class="blog_h3"><span class="graybg">多查询 RAG</span></div>
<p>多查询 RAG（Multi-Query RAG）会先把一个原始问题改写成多个语义相近但表述不同的检索查询，再分别召回并合并结果。它的动机很直接：单一路径的 query 很容易受措辞影响，而多个改写版本可以覆盖不同的关键词、别名、问题角度与表达方式，从而提高召回覆盖率。</p>
<div class="blog_h3"><span class="graybg">多跳 RAG</span></div>
<p>多跳 RAG（Multi-hop RAG）适合那些答案依赖多段证据链的查询。它通常不会指望一次检索就拿到最终答案，而是先检索第一跳证据，再根据第一跳结果生成下一跳 query，继续检索后续证据。于是，检索过程本身就变成了一条“检索 ➡ 读证据 ➡ 改写下一问 ➡ 再检索”的链式推理流程。</p>
<div class="blog_h3"><span class="graybg">智能体 RAG</span></div>
<p>智能体 RAG（Agentic RAG）则把查询改写提升为一个可反复决策的过程：模型不只改写一次 query，而是根据当前召回质量、证据缺口、工具反馈与上下文状态，决定是否继续搜索、换一种问法、切换知识库、触发重排，或干脆停止检索进入回答阶段。它的重点已经不只是“改写 query”，而是让检索成为 Agent 控制流的一部分。</p>
<div class="blog_h2"><span class="graybg">重排序（Reranking）</span></div>
<p>重排序（Reranking）在“召回”（Recall）之后提升“相关性排序”（Precision@k）：用更强但更慢的模型对候选片段重新打分。</p>
<p>成熟 RAG 系统里的 rerank 还常分成两层。第一层是每个知识库内部的局部重排：对该库自己的候选做加权、RRF 或模型精排。第二层是多知识库结果合并后的全局重排：当查询同时打到多个知识库时，再在总候选集合上做一次统一排序。这样做的动机很现实：单库内部的分数可比较，并不代表跨库结果也天然可比较；若不做第二层，全局 top-k 往往会被某个打分体系更“激进”的知识库垄断。</p>
<div class="blog_h3"><span class="graybg">Cross-Encoder Reranker</span></div>
<p>Cross-Encoder Reranker 把 query 与候选 chunk 拼接后输入同一个编码器，用注意力做细粒度匹配，相关性通常更高，但计算更慢，适合对 top-k（例如 50→10）做精排。</p>
<p>典型输入形式是 <pre class="crayon-plain-tag">[CLS] query [SEP] doc [SEP]</pre>（或对应模型的特殊 token）。在 BERT 类模型中，token type embeddings（也称 segment embeddings）可用于区分两段文本；随后 Transformer 的全连接自注意力允许 query token 与 doc token 在每一层发生深度交互，这正是 cross-encoder “cross” 的本质。</p>
<p>打分通常取 <span displaypfx="inline-" class="mathjax-container">\([\mathrm{CLS}]\)</span> 位置的最终隐向量 <span displaypfx="inline-" class="mathjax-container">\(h_{\mathrm{CLS}}\)</span> 过一个线性层得到相关性分数（或二分类 logits）。与双编码器（Bi-Encoder / Dual Encoder）相比，cross-encoder 更准但无法离线预计算候选向量，因此在线成本更高。</p>
<div class="blog_h3"><span class="graybg">LLM Reranker</span></div>
<p>LLM Reranker 用大模型直接判断相关性或做对比选择，能融合更复杂的语义与任务约束，但成本更高且需要防止“自信乱判”。工程上常用它做少量候选的最终筛选。</p>
<div class="blog_h3"><span class="graybg">生成式重排序（Generative Reranking）</span></div>
<p>生成式重排序（Generative Reranking）把“相关性打分”转写为生成任务：把 query 与 doc 拼接输入解码器（Decoder-only）或编码器-解码器（Encoder–Decoder），让模型生成一个标签 token（例如 <pre class="crayon-plain-tag">Yes/No</pre>）或一个短评分文本，并用生成概率作为分数。</p>
<p>常见的点式打分形式是二分类 log-odds：令 <span displaypfx="inline-" class="mathjax-container">\(y\in\{0,1\}\)</span> 表示是否相关，则</p>
<span displaypfx="" class="mathjax-container">\[\mathrm{score}(q,d)=\log p_\theta(y=1\mid q,d)-\log p_\theta(y=0\mid q,d)\]</span>
<p>相比传统 BERT 类 cross-encoder，生成式 reranker 往往更擅长处理长文本匹配、复杂约束与隐含推理；工程上常用“轻量级 LLM + 领域数据微调”来获得显著增益。</p>
<div class="blog_h3"><span class="graybg">列表式重排序（Listwise Reranking）</span></div>
<p>列表式重排序（Listwise Reranking）把多个候选 doc 一次性送入模型，在同一个上下文窗口中共同建模“相对顺序”，而不是对每个（query, doc）独立跑一次 forward。直觉上，它让候选之间在 self-attention 中“同台竞争”，从而提升排序一致性，并降低多次推理带来的总开销。</p>
<p>实现上常见做法是：把候选 doc 按截断长度拼接成一个 list，要求模型输出每个 doc 的分数或名次；也有实现会在末尾使用一个专门的“汇聚 token”对每个 doc 读取表示。Listwise 方法的工程约束更强：候选数、每段 doc 的截断长度与模型上下文窗口会直接决定可吞吐的 top-k。</p>
<div class="blog_h3"><span class="graybg">训练目标与 Hard Negatives</span></div>
<p>精排训练的难点是“区分相似但不相关”：需要硬负样本（Hard Negatives）来逼迫模型学习细粒度差异。硬负样本通常来自第一阶段召回：对同一 query，取检索 top-k 中不相关的候选作为 negatives（也可混入 BM25 negatives、in-batch negatives、或对抗式 negatives）。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">目标</td>
<td style="text-align: center;">形式</td>
<td style="text-align: center;">直觉</td>
<td style="text-align: center;">常见风险</td>
</tr>
</thead>
<tbody>
<tr>
<td>点式（Pointwise）</td>
<td>学习 <span displaypfx="inline-" class="mathjax-container">\(s(q,d)\)</span> 或 <span displaypfx="inline-" class="mathjax-container">\(p(y=1\mid q,d)\)</span></td>
<td>把相关性当作二分类/回归</td>
<td>分数标定（Calibration）难；对“相对顺序”监督较弱</td>
</tr>
<tr>
<td>对式（Pairwise）</td>
<td>学习 <span displaypfx="inline-" class="mathjax-container">\(s(q,d^+)&gt;s(q,d^-)\)</span></td>
<td>直接优化排序边际（Margin）</td>
<td>负样本质量决定上限；采样偏差可能放大</td>
</tr>
<tr>
<td>列表式（Listwise）</td>
<td>对候选列表联合优化（如 softmax over list）</td>
<td>更贴近 NDCG/MRR 等排序指标</td>
<td>计算与实现复杂；受上下文窗口约束</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">蒸馏（Knowledge Distillation）</span></div>
<p>蒸馏（Knowledge Distillation）把一个强但慢的教师模型（Teacher）产生的打分/偏好，转移给一个更快的学生模型（Student）。在重排序里，蒸馏最常见的目标不是“压缩参数”，而是<span style="background-color: #c0c0c0;">把 LLM 级别的相关性判断能力下放到可规模化的 reranker</span>，使线上能承载更大的 top-k、更高的 QPS 或更低的 P99。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">Teacher → Student</td>
<td style="text-align: center;">监督信号</td>
<td style="text-align: center;">常见损失</td>
<td style="text-align: center;">收益</td>
<td style="text-align: center;">主要风险</td>
</tr>
</thead>
<tbody>
<tr>
<td>LLM / 生成式 reranker → Cross-Encoder</td>
<td>软分数（log-odds / graded）或偏好对</td>
<td>回归（MSE）/ 对式（pairwise logistic）</td>
<td>显著降成本；保留较强语义与推理能力</td>
<td>学生上限受教师约束；教师偏差会被复制</td>
</tr>
<tr>
<td>Cross-Encoder → Bi-Encoder</td>
<td>相对顺序/分数</td>
<td>对比学习（InfoNCE）/ 蒸馏排序边际</td>
<td>把精排能力“下放”到召回，提高召回质量</td>
<td>需要大量 hard negatives；对领域漂移敏感</td>
</tr>
<tr>
<td>LLM → 数据标注（作为训练集构造器）</td>
<td>弱标注标签 + 解释/证据</td>
<td>按目标任务训练（点式/对式/列表式）</td>
<td>快速构造大规模领域数据；迭代快</td>
<td>噪声标注；需抽样人工复核与在线 A/B 验证</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">返回内容不是只给命中文本</span></div>
<p>RAG 检索阶段的最终产物并不一定只是“命中的那一小段 chunk”。很多高质量系统会把返回内容模式做成显式策略：只返回命中块正文、返回去掉 overlap 的核心正文、返回命中块加邻近上下文、或按文档中的位置范围把相关块递归拼回更完整的片段。这样做的原因是：模型生成真正需要的是<span style="background-color: #c0c0c0;">足够完整且边界清晰的证据上下文</span>，而不是孤立句子。</p>
<p>这里的关键判断是：检索命中的是索引单元，但喂给模型的应是生成单元。索引单元倾向短小、便于召回；生成单元则要兼顾上下文完整性、token 预算和可引用性。若两者完全绑定，系统通常会在召回精度与生成可读性之间来回拉扯。</p>
<div class="blog_h2"><span class="graybg">从项目设计能看到的几条经验</span></div>
<p>第一，RAG 的上限不只由 embedding 模型决定，而是由“分块质量、索引视图、融合策略、回拼策略、状态治理”共同决定。把注意力只放在换一个更强 embedding 上，通常抓不住主矛盾。</p>
<p>第二，知识库不是静态文件集合，而是带状态机的外部知识资产。只有显式区分 base、document、chunk，并让它们有自己的生命周期，系统才可能支持增量更新、失败恢复、重建和审计。</p>
<p>第三，生产 RAG 常常天然走向双后端和多路径召回。这个方向的收益是更稳健的覆盖率，代价是系统复杂度、索引一致性成本和观测难度都会明显上升。因此它适合高价值知识库，不一定适合所有团队的第一版系统。</p>
<p>第四，把摘要和问题也视作可检索对象，是一个非常务实的设计。它等于承认“原文表述”与“用户提问方式”经常不一致，于是用知识增强知识本身的可检索性。但它也会带来额外索引、额外 embedding 成本与额外一致性问题，因此只有在召回质量确实成为瓶颈时，这种多视图索引才值得引入。</p>
<p>第五，外部知识平台同步并不等于“复制一份数据就结束”。一旦系统允许一部分知识由外部平台托管、另一部分知识由内部系统自建索引，那么“谁是事实源、谁负责更新、谁负责删除、谁负责召回”就必须在架构上说清楚。否则最容易出现的不是召回不到，而是召回出重复、过期或跨系统不一致的内容。</p>
<div class="blog_h3"><span class="graybg">主流选型对比</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">阶段</td>
<td style="text-align: center;">常见架构</td>
<td style="text-align: center;">代表实现</td>
<td style="text-align: center;">核心逻辑</td>
<td style="text-align: center;">速度</td>
<td style="text-align: center;">精度</td>
</tr>
</thead>
<tbody>
<tr>
<td>召回</td>
<td>双编码器（Bi-Encoder）</td>
<td>BGE-Embedding<br />text-embedding-3</td>
<td>向量相似度</td>
<td>极快</td>
<td>中</td>
</tr>
<tr>
<td>精排（稳健基线）</td>
<td>交叉编码器（Cross-Encoder）</td>
<td>BAAI<br />bge-reranker-v2-m3</td>
<td><span displaypfx="inline-" class="mathjax-container">\([q;d]\)</span> 深度交互</td>
<td>中</td>
<td>高</td>
</tr>
<tr>
<td>精排（顶配）</td>
<td>生成式 / 列表式（LLM-based）</td>
<td>BGE-Reranker-v2-Gemma<br />Jina-Reranker-v3</td>
<td>生成式打分或 listwise 竞争</td>
<td>较慢</td>
<td>极高</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">生成与融合</span></div>
<p>生成与融合（Generation &amp; Fusion）的关键在于：把检索到的证据（Evidence）以可控格式注入 prompt，并要求模型“基于证据回答”。常见做法包括：</p>
<ul>
<li>Stuffing：把 top-k 片段直接拼接进上下文（最简单，最易超预算）。</li>
<li>Map-Reduce：先对各片段分别提炼要点，再汇总生成最终答案（更稳，成本更高）。</li>
<li>Refine：先用少量证据生成初稿，再逐片段增量修订（适合长证据链）。</li>
</ul>
<div class="blog_h1"><span class="graybg">智能体（Agent）</span></div>
<p>检索增强生成（RAG）解决的是“模型缺少外部知识”的问题；智能体（Agent）解决的是“模型如何在真实环境中持续完成任务”的问题。一个真正可用的 Agent 不再是单次 prompt 的包装，而是一个带有状态（State）、工具（Tools）、记忆（Memory）与控制流（Control Flow）的运行时系统。它能够观察环境、决定下一步动作、调用外部能力、根据反馈修正策略，并在多轮交互中维持任务连续性。</p>
<div class="blog_h2"><span class="graybg">核心概念</span></div>
<p>从工程视角看，Agent 的本质是“以大模型为决策核心的软件系统”。模型负责理解、推理与生成；外层系统负责注入上下文、执行动作、保存状态、施加约束，并把环境反馈重新送回模型。只有这几层协同起来，模型才会从一次性回答器变成可持续工作的执行体。</p>
<div class="blog_h3"><span class="graybg">计划-执行</span></div>
<p>计划-执行（Plan-Execute）是智能体最常见的控制流之一：先把“用户目标”分解为一组可操作步骤（计划），再逐步调用工具/模型完成这些步骤（执行），并把中间结果写回状态（State）。当环境反馈与计划假设不一致时，触发再计划（Replan）。工程上，它的价值是把一次长输出变成可观测（Observable）、可重试（Retryable）、可中断（Interruptible）的多步过程。</p>
<div class="blog_h3"><span class="graybg">感知-推理-行动循环</span></div>
<p>感知-推理-行动（Perception-Reasoning-Action）循环是 Agent 的基本闭环。感知阶段读取环境状态，例如用户输入、网页 DOM、数据库返回值、日志、文件内容或工具回执；推理阶段根据当前目标、约束和历史状态决定下一步策略；行动阶段把决策落实为具体操作，例如搜索、调用 API、写文件、点击界面或向下游智能体发消息；随后再读取新反馈，进入下一轮循环。这个闭环的关键不在“会不会思考”，而在“每一轮是否拿到了正确观测、是否执行了正确动作、是否把结果正确写回状态”。</p>
<p>因此，Agent 设计首先是状态机（State Machine）设计。很多失败并不是模型推理能力不足，而是观测不完整、动作不可验证、状态写回混乱，导致下一轮推理建立在错误世界模型之上。一个好的 Agent runtime 会显式记录当前目标、最近动作、动作结果、异常信息与下一步计划，从而让每一轮循环都建立在稳定状态上。</p>
<div class="blog_h3"><span class="graybg">工具使用（Tool Use）</span></div>
<p>工具使用（Tool Use）让语言模型突破“只会生成文本”的边界，把外部系统接成自己的感官与执行器。工具既可以是只读型能力，例如搜索、数据库查询、文件读取；也可以是动作型能力，例如发送请求、执行 shell、操作浏览器、写入代码仓库。模型本身不直接执行这些动作，而是输出结构化调用意图，再由外层系统负责真正执行、捕获结果并返回。</p>
<p>工程上，Tool Use 的难点不在“把工具接上”，而在“把工具定义清楚”。一个好的工具接口需要明确输入 schema、输出 schema、权限边界、失败语义和重试策略。工具定义越模糊，模型越容易产生参数幻觉（Argument Hallucination）、错误调用和无效循环；工具定义越稳定，模型越容易学会把工具当作程序原语来组合。</p>
<div class="blog_h3"><span class="graybg">记忆系统</span></div>
<p>记忆系统（Memory System）负责解决跨步骤与跨会话连续性问题。没有记忆的模型每次调用都像“重新开机”；有记忆的 Agent 才能累积上下文、复用历史决策、根据过去错误调整行为。记忆并不等同于“把更多 token 塞进上下文窗口”，而是要把不同时间尺度、不同重要性的状态放进不同存储层，再按需检索。</p>
<div class="blog_h4"><span class="graybg">短期记忆（上下文窗口）</span></div>
<p>短期记忆（Short-term Memory）通常由上下文窗口（Context Window）承载，保存当前任务最活跃的信息：系统指令、最近几轮对话、正在执行的计划、刚拿到的工具结果、当前工作草稿等。它的优势是读取成本低、和当前推理绑定最紧；它的限制也同样明显：容量昂贵、注意力会稀释、信息越多不代表效果越好。</p>
<p>因此，短期记忆的核心工作不是“堆满”，而是“裁剪”。优秀的 Agent 会把原始轨迹压缩成摘要（Summary）、状态（State）和待办项（Next Actions），只保留当前决策真正需要的变量。短期记忆承担的是工作记忆（Working Memory）的职责：它服务当前这一轮推理，而不是承担长期知识库的角色。</p>
<div class="blog_h4"><span class="graybg">长期记忆（外部存储）</span></div>
<p>长期记忆（Long-term Memory）存放在上下文窗口之外，常见载体包括向量数据库、关系数据库、对象存储、文档库、日志系统、代码仓库、任务看板与事件流。它保存的是“现在不必全部加载，但未来可能需要回忆”的信息，例如用户偏好、历史案例、项目规范、过去失败总结、已验证的事实与中间产物。</p>
<p>长期记忆要解决三个问题：写什么、怎么检索、何时遗忘。写入过多会让存储退化为噪声池；只做向量相似度检索又容易漏掉结构化约束与时间关系。成熟做法通常会把结构化状态、全文检索、向量召回与人工定义的索引结合起来，让 Agent 既能“语义回忆”，也能“精确定位”。</p>
<div class="blog_h4"><span class="graybg">记忆类型：情景 / 语义 / 程序</span></div>
<p>目前主流 Agent 设计常借用认知科学中的三类记忆划分：情景记忆（Episodic Memory）、语义记忆（Semantic Memory）与程序记忆（Procedural Memory）。这三类记忆对应的是“发生过什么”“知道什么”“怎么做”。把它们混在一个存储里，检索和写入策略很快就会失控；分层建模则更容易设计稳定的读写规则。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">记忆类型</td>
<td style="text-align: center;">保存内容</td>
<td style="text-align: center;">典型载体</td>
<td style="text-align: center;">工程价值</td>
</tr>
</thead>
<tbody>
<tr>
<td>情景记忆（Episodic）</td>
<td>具体事件、行动轨迹、失败案例、会话历史</td>
<td>任务日志、轨迹摘要、审计记录</td>
<td>支持回放、复盘、反思与重试</td>
</tr>
<tr>
<td>语义记忆（Semantic）</td>
<td>稳定知识、规则、事实、领域概念</td>
<td>知识库、文档索引、结构化事实表</td>
<td>支持检索、问答、约束判断</td>
</tr>
<tr>
<td>程序记忆（Procedural）</td>
<td>工作流程、技能脚本、工具使用模式</td>
<td>系统提示、工具说明、可复用 playbook</td>
<td>把“会做事”沉淀为稳定操作习惯</td>
</tr>
</tbody>
</table>
<p>其中，程序记忆对工程系统尤其关键。很多所谓“Agent 经验”最终都会沉淀成程序记忆，例如某个 API 的调用顺序、某类错误的排查流程、某个项目的提交流程。Harness Engineering 本质上就是把这些隐性经验逐步外化成可持续复用的程序记忆。</p>
<div class="blog_h2"><span class="graybg">推理与规划</span></div>
<p>推理（Reasoning）解决的是“当前这一步怎么想”；规划（Planning）解决的是“整个任务怎么走”。现代 Agent 往往同时需要二者：局部步骤要有足够的推理精度，长期任务要有阶段化计划与中途重规划能力。近两年的研究基本沿着四条路线收敛：交错式思考与行动、反思式自我修正、显式规划后求解，以及基于搜索的多路径探索。</p>
<div class="blog_h3"><span class="graybg">ReAct</span></div>
<p>ReAct（Reason + Act）是当前 Agent 范式最有影响力的起点之一。它把“思考轨迹”和“动作轨迹”交错起来：模型先给出一小步 reasoning，再发起 action，读取 observation 后继续下一步 reasoning。与只做链式思维（Chain-of-Thought）的做法相比，ReAct 的优势在于每一步都能接触外部环境，因此能在事实不确定、需要探索、需要工具辅助的任务上持续修正路径。</p>
<p>ReAct 的核心价值并不是让模型“想得更多”，而是让推理过程与环境反馈形成闭环。它也有明显代价：若没有停机条件、步数上限和工具约束，轨迹很容易冗长、循环或过度调用工具。因此，ReAct 更像 Agent 控制流的原型范式，生产系统通常会在其外层再加计划、验证、预算控制与错误恢复机制。</p>
<div class="blog_h3"><span class="graybg">Reflection（自我反思）</span></div>
<p>Reflection（自我反思）把“错误”变成下一轮决策的输入。其基本思想是：一次执行失败后，不只返回原始错误信号，还额外生成一段高信息密度的经验总结，说明失败原因、已验证无效的路径、下一次应当修改的策略。这样，模型获得的不是一个抽象的 reward，而是一段可以直接进入上下文的文字化梯度。</p>
<p>从研究谱系看，Reflexion 将这种思路系统化为 verbal reinforcement learning：把环境反馈转写成文本反思，并存入 episodic memory，供后续回合检索。工程上，这类方法通常以“执行后 Critic”“失败总结卡片”“经验回放摘要”等形式落地。它最适合高代价试错任务，例如代码修复、网页操作、长任务代理；前提是系统能可靠地区分“失败是策略问题”还是“失败只是偶然噪声”。</p>
<div class="blog_h3"><span class="graybg">Plan-and-Solve</span></div>
<p>Plan-and-Solve 的思想很直接：先显式列出求解计划，再根据计划执行。它针对的是零样本链式思维常见的 missing-step 问题，即模型直接开始求解时容易跳步骤、漏约束或在中途漂移。把“先规划、后执行”做成两个阶段后，模型的注意力会先聚焦在任务分解，再聚焦在逐步完成，从而显著降低长任务中的结构性遗漏。</p>
<p>这一思路在工程系统里几乎已经成为默认模式。很多 Coding Agent、Research Agent 与 Workflow Agent 都把 planner 和 executor 拆成不同阶段，甚至拆成不同角色。原因很简单：计划是一种低成本、高杠杆的中间产物。它既能让人类在写代码前审查方向，也能让系统在执行中持续对照“当前动作是否仍服务于原始目标”。</p>
<div class="blog_h3"><span class="graybg">MCTS（蒙特卡洛树搜索）</span></div>
<p>MCTS（Monte Carlo Tree Search，蒙特卡洛树搜索）把 Agent 的决策过程从“单路径生成”升级为“多路径探索”。系统不再只让模型给出一个下一步动作，而是展开多个候选分支，对不同轨迹进行模拟、评估和回传分值，再把计算预算集中到更有希望的分支上。对于需要长程依赖、路径选择、试探性探索的任务，这种显式搜索比单次贪心生成更稳健。</p>
<p>Language Agent Tree Search（LATS）是这一路线的代表性工作，它把 MCTS 与语言模型结合，让模型同时承担候选动作生成、状态评估和自我反思等角色。代价同样明确：搜索带来更高 token 成本、更复杂的状态管理和更重的工程编排。因此，MCTS 更适合高价值、高不确定性任务，例如复杂推理、博弈式决策、困难编程与网页任务，而不是每个简单问答都默认启用的通用套路。</p>
<div class="blog_h2"><span class="graybg">工具与行动</span></div>
<p>如果说推理模块决定“应该做什么”，那么工具层决定“究竟能做到什么”。当前主流 Agent 平台基本收敛出几类高频动作接口：结构化函数调用、受控代码执行、Web 检索、图形界面操作，以及把外部工具和资源标准化接入的协议层。它们共同构成了 Agent 的执行面。</p>
<div class="blog_h3"><span class="graybg">Function Calling</span></div>
<p>Function Calling（函数调用）是最基础、最通用的 Tool Use 形式。模型输出的不再是自然语言描述，而是一个满足 schema 的调用请求，例如函数名、参数对象、调用意图；应用侧接收到请求后真正执行代码，并把结果作为 tool result 返回。这样，模型负责“决定调用什么”，应用负责“保证执行正确”。</p>
<p>Function Calling 的真正意义是把自由文本接口收窄为结构化程序接口。只要参数 schema 足够清晰，模型就能稳定学会把外部系统当成可组合原语。这也是今天大部分 Agent 框架的底层抽象：上层看起来是“智能体会用工具”，底层通常仍是“模型在做结构化函数选择与参数填充”。</p>
<div class="blog_h3"><span class="graybg">Code Interpreter</span></div>
<p>Code Interpreter（代码解释器）让模型在受控沙箱中执行代码，从而把“语言推理”转换为“可验证计算”。它特别适合数据清洗、表格处理、统计分析、绘图、文件转换、轻量脚本自动化与需要精确数值结果的任务。与纯文本推理相比，Code Interpreter 的优势在于中间状态可执行、可复现、可检查。</p>
<p>这类能力本质上是把 Python 或其他运行时变成模型的外部工作台。模型负责提出程序，沙箱负责执行，系统再把 stdout、文件产物、异常栈和图像反馈给模型。只要隔离、资源限制和文件边界设置得当，Code Interpreter 往往是让 Agent “从会解释变成会计算”的最直接跃迁。</p>
<div class="blog_h3"><span class="graybg">Web 搜索</span></div>
<p>Web 搜索（Web Search）解决的是模型知识静态化问题。基础模型参数中固化的是训练时刻之前的分布，而 Agent 经常需要回答最新价格、最新政策、最新产品文档、最新论文或当前网页状态。把搜索能力接入后，模型可以先检索，再基于来源生成回答，并在必要时附带引用。</p>
<p>工程上，Web 搜索不是“把搜索结果贴给模型”这么简单。系统还需要处理查询重写、来源筛选、去重、可信度排序、片段抽取与多源融合。对于需要最新信息的任务，搜索应当被视为一等能力，而不是可有可无的外挂；否则 Agent 的错误会表现为“推理看起来正确，但事实已经过时”。</p>
<div class="blog_h3"><span class="graybg">Computer Use</span></div>
<p>Computer Use（计算机使用）让 Agent 直接在图形界面层面操作软件与网页，例如看截图、定位元素、移动鼠标、键入内容、滚动页面与读取屏幕变化。它的价值在于：当目标系统没有现成 API，或者真正业务流程本来就发生在浏览器/桌面界面中时，Agent 仍然可以像人类操作员一样完成任务。</p>
<p>这类能力也是最脆弱的一类动作接口。界面结构变化、网络延迟、弹窗遮挡、验证码、权限确认和安全风险都会放大失败概率。因此，Computer Use 应被视为“最后一层兼容性接口”：没有 API 时才使用，并始终运行在隔离环境中，为高风险动作保留人工确认与回滚机制。</p>
<div class="blog_h3"><span class="graybg">MCP（模型上下文协议）</span></div>
<p>MCP（Model Context Protocol，模型上下文协议）试图把“给模型接工具与上下文”这件事标准化。它定义了一套客户端-服务端协议，让宿主应用可以连接多个 MCP server，并以统一方式暴露工具（Tools）、资源（Resources）与提示模板（Prompts）。这样，模型不必为每个外部系统重新学习私有接线方式，应用也不必为每种模型重复造一套集成层。</p>
<p>在工程语义上，MCP 解决的是“工具可移植性”和“上下文供给标准化”。一个搜索 server、一个数据库 server、一个设计文档 server，只要遵循同一协议，就能被不同 Agent host 复用。当前生态已经把 MCP 从“工具协议”扩展成“上下文协议”：除了调用动作，也强调让模型按需读取结构化资源，而不是把所有材料一次性塞进 prompt。</p>
<p>截至 2026 年 3 月，MCP 官方稳定协议版本为 2025-11-25，2026 路线图正在推进会话扩展（session handling）、Server Cards、Registry 与 agent-to-agent 协作能力。对应用开发者而言，更重要的不是版本号本身，而是它标志着一件事：Agent 运行时正在从各家私有 Tool API，逐步走向可协商、可复用、可观测的开放接口层。</p>
<div class="blog_h2"><span class="graybg">多智能体系统</span></div>
<p>多智能体系统（Multi-agent System）指的是：把一个复杂任务分解给多个具有不同职责或不同上下文边界的 agent，由它们通过委派、协作、验证和状态传递共同完成目标。它关注的核心是如何把规划、执行、审查和并行探索写成一个可控的协作结构。</p>
<p>是否拆成多个 agent，取决于任务是否存在天然的角色分工、上下文隔离需求、并行空间与独立验证价值。很多简单任务用一个强单体 agent 加好工具即可；只有当单一上下文开始拥塞、单角色难以兼顾规划与执行、或多个分支可以并行推进时，多智能体系统才真正体现优势。</p>
<div class="blog_h3"><span class="graybg">角色分工</span></div>
<p>角色分工（Role Decomposition）指的是：在多智能体系统中，为不同 agent 分配稳定且边界清晰的职责，使每个 agent 只处理自己最应该承担的那一类决策与操作。它的作用是把复杂任务拆成多个可管理的视角与责任域，降低单一上下文同时兼顾规划、执行和审查的负担。</p>
<p>最常见的基本三角是 Planner、Executor 与 Critic：Planner 负责分解目标，Executor 负责实际操作，Critic 负责验证与挑错。这个结构的本质是在系统内部显式制造不同视角，避免同一个上下文同时承担规划、执行与审查，最终让错误在更早阶段暴露出来。</p>
<div class="blog_h4"><span class="graybg">Planner</span></div>
<p>Planner 负责把模糊目标转成结构化任务图，例如目标拆解、依赖关系、优先级、完成标准与失败回退条件。一个好的 Planner 不直接沉迷于实现细节，而是尽快产出可被执行层消费的中间表示，例如任务列表、阶段计划、验收清单或执行 DAG。这样，后续 agent 的上下文会更短、更稳定。</p>
<div class="blog_h4"><span class="graybg">Executor</span></div>
<p>Executor 负责把计划变成具体动作：调用 API、写代码、运行测试、检索资料、修改文件、操作界面。它通常需要最严格的权限控制，因为真正改变外部世界的是执行层，而不是规划层。把 Executor 的工具范围、写入范围和预算边界限定清楚，往往比继续优化模型 prompt 更能提升整体可靠性。</p>
<div class="blog_h4"><span class="graybg">Critic</span></div>
<p>Critic 负责验证“结果是否正确”，而不是复述“过程看起来合理”。它可以检查事实一致性、运行测试、比对输出格式、审查架构约束、回放浏览器流程，或要求执行层补充证据。Critic 的价值在于给系统引入独立的否决权：没有独立验证的多智能体系统，本质上仍然只是把同一种错误复制了几遍。</p>
<div class="blog_h3"><span class="graybg">协作模式</span></div>
<p>角色定义清楚之后，下一步是决定这些角色如何协作。常见模式主要有顺序执行、并行执行与层级委派。三者区别不在名称，而在状态如何传递、谁拥有控制权、失败后如何收敛，以及系统是否允许多个 agent 同时写入同一世界状态。</p>
<div class="blog_h4"><span class="graybg">顺序执行</span></div>
<p>顺序执行（Sequential Execution）是最稳的模式：上一步完成并产出明确结果后，下一步才开始。它最适合依赖链强、可验证点清晰的流程，例如“先规划，再实现，再审查，再修复”。优点是状态简单、问题定位容易；缺点是吞吐量受限，前一环的错误会整体阻塞后续流程。</p>
<div class="blog_h4"><span class="graybg">并行执行</span></div>
<p>并行执行（Parallel Execution）用于把独立子任务同时推进，例如多个资料检索、多个候选方案探索、多个代码模块并行实现。它能显著提高吞吐，但前提是任务切分足够干净，或者系统拥有足够好的合并策略。并行化的真正难点在于如何避免它们争抢同一上下文、重复劳动或互相覆盖结果。</p>
<div class="blog_h4"><span class="graybg">层级委派</span></div>
<p>层级委派（Hierarchical Delegation）由一个上层 supervisor 或 manager 统一掌控目标和全局状态，再把子任务委派给不同 specialist。它适合长任务和复杂流程，因为全局控制权集中在上层，局部上下文压力则下沉到子 agent。代价是 supervisor 很容易成为瓶颈，因此需要明确委派粒度、回收机制与升级路径，避免系统退化成“一个总管在不停转述消息”。</p>
<div class="blog_h2"><span class="graybg">主流框架</span></div>
<p>Agent Framework 解决的是“如何把模型能力组织成长期可运行的软件系统”。主流框架的差异主要体现在三个层面：控制流表达方式、状态管理方式，以及多智能体协作的原生支持程度。选型时最重要的是它是否契合你的系统拓扑：你需要图式工作流、事件驱动编排、还是平台托管的工具执行与追踪能力。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">框架/形态</td>
<td style="text-align: center;">控制流模型</td>
<td style="text-align: center;">强项</td>
<td style="text-align: center;">代价</td>
<td style="text-align: center;">适用</td>
</tr>
</thead>
<tbody>
<tr>
<td>LangChain / LangGraph</td>
<td>链式/图式（Graph）</td>
<td>组件化；易组合 RAG、工具与记忆；LangGraph 适合复杂状态机</td>
<td>抽象层较多；需要明确工程边界</td>
<td>生产 RAG/Agent pipeline</td>
</tr>
<tr>
<td>AutoGen</td>
<td>事件驱动多智能体（Event-driven）</td>
<td>消息传递清晰；适合团队式协作与研究</td>
<td>系统设计自由度高；需要自定治理边界</td>
<td>多智能体实验；复杂协作流程</td>
</tr>
<tr>
<td>CrewAI</td>
<td>角色 + 任务流水线</td>
<td>任务编排直观；适合“岗位分工”式流程</td>
<td>可控性取决于框架提供的扩展点</td>
<td>面向业务流程的多角色 Agent</td>
</tr>
<tr>
<td>OpenAI Responses API / Agents SDK</td>
<td>平台托管（Managed）</td>
<td>内建工具、Tracing、Handoff 与托管能力完善</td>
<td>更依赖平台抽象；深度定制时需额外设计 runtime</td>
<td>快速构建生产 Agent；希望复用官方工具栈</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">LangChain / LangGraph</span></div>
<p>LangChain 偏组件化抽象，LangGraph 偏图式控制流。前者适合把模型、检索器、工具、记忆等模块快速拼接起来；后者适合把复杂 Agent 流程显式建模为图（Graph），把循环、分支、检查点和人机介入点写成状态转移。对于生产系统，LangGraph 的意义尤其明显：它让“Agent 不是一段 prompt，而是一个状态机”这件事可以被直接编码。</p>
<p>在多智能体方面，LangGraph 当前更强调基于工具调用（tool-calling）的 supervisor 模式，而不是把所有 agent 都包装成自由对话体。原因很实际：当协作关系被写成显式图结构后，上下文边界、路由条件和失败恢复都会更好控制。</p>
<div class="blog_h3"><span class="graybg">AutoGen</span></div>
<p>AutoGen 的长处在于把多智能体协作建模为显式消息系统。不同 agent 可以作为独立节点收发消息、触发工具、交换中间状态，因此很适合研究“团队式智能体”以及需要复杂路由的实验性系统。新版 AutoGen 也更强调事件驱动（Event-driven）与可扩展运行时，而不只是几个人工角色互相聊天。</p>
<p>它的代价也非常明确：自由度越高，系统边界越需要开发者自己画清楚。权限、可观测性、记忆持久化与异常恢复如果没有在框架外补齐，多智能体对话很容易演化成难以调试的消息洪流。</p>
<div class="blog_h3"><span class="graybg">CrewAI</span></div>
<p>CrewAI 把多智能体系统表达为“角色（Role）+ 任务（Task）+ 工具（Tool）”的业务编排语言，适合把组织分工直接映射到系统设计中。它的上手成本较低，尤其适合内容生成、运营流程、分析流水线这类天然按岗位拆分的任务。</p>
<p>这类框架的优势是表达直观，代价是底层控制流往往被隐藏得更深。只要任务依赖、重试策略、共享状态和权限边界开始复杂化，就需要确认框架是否允许你把隐含运行时重新显式化。</p>
<div class="blog_h3"><span class="graybg">OpenAI Responses API / Agents SDK</span></div>
<p>从当前官方工具栈看，OpenAI 已经把 Responses API 作为构建 agent-like application 的统一接口，并以 Agents SDK 提供更轻量的编排、handoff、guardrail 与 tracing 能力。这一路线的核心价值是把常见基础设施平台化：函数调用、Web 搜索、Computer Use、Code Interpreter、Tracing 与多智能体 handoff 都可以沿着同一套接口组织。</p>
<p>托管式能力的优点是默认路径短、集成成本低、官方工具之间协同更顺滑；缺点是当业务开始需要深度定制的状态机、自定义持久化策略或异构执行环境时，应用仍然需要在 SDK 之上补一层自己的 runtime。换句话说，托管平台减少的是“起步成本”，而不是彻底消灭 Agent 工程本身。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/ai-knowledge-quick-ref">人工智能知识速查（理论）</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/ai-knowledge-quick-ref/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
