ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

二次型梯度推导全解:从三种方法到数值验证

2026/10/2 10:08:54 拓冰建站 浏览量
二次型梯度推导全解:从三种方法到数值验证 二次型 x^TAx 的梯度推导几乎是我见过的机器学习入门者翻车率最高的一块内容。矩阵的写法看起来特别简洁一个表达式 x^TAx 就结束了但真让你对 x 求导的时候很多人的第一反应是去翻矩阵求导公式表翻到了又不知道该用哪一条对着 (A A^T)x 和 2Ax 这两个结果陷入沉思——到底哪个是对的还有一批人干脆把矩阵求导当成普通标量求导来做习惯性写出 2Ax 就收工完全没注意到 A 不一定对称这件事。这篇文章我就把这件“小事”彻底掰开揉碎从偏导的原始定义一路推到位给出三种不同的推导方法再配合数值实验验证和常见错误盘点。写完了你就会发现二次型梯度真的不值得害怕搞懂一次之后往后遇到各种矩阵求导都能顺藤摸瓜。1. 先搞清楚我们到底在求什么1.1 二次型的长相和适用场景所谓二次型指的就是 f(x) x^TAx 这种形式的标量函数其中 x 是 n 维列向量A 是 n×n 的实矩阵函数值本身是一个实数。这类表达式在机器学习里出现频率高得吓人。最小二乘法的损失函数 ||y - Xw||² 展开之后就是二次型岭回归加的正则项 λw^Tw 也是二次型等价于 A 为单位阵的二次型高斯分布的概率密度函数里指数部分同样包含二次型 (x-μ)^TΣ^{-1}(x-μ)更不用说 PCA、LDA、逻辑回归的牛顿法迭代里Hessian 矩阵相关的运算处处都是二次型的影子。既然二次型无处不在求它关于 x 的梯度自然就成了绕不开的基本功。所谓梯度就是 ∂f/∂x 这个向量它的第 k 个分量是 ∂f/∂x_k。1.2 这里有一个最容易被忽略的关键点写 x^TAx 的时候A 可能对称也可能不对称而绝大多数教材里为了推导方便都直接假设 A 对称。这个假设不是没有代价的——它会把公式的形状从 (A A^T)x 简化成 2Ax导致很多人只记住了后者却忘了它成立的前提。我的建议是第一遍学的时候不要先假设对称直接对一般的 A 做推导。先把 (A A^T)x 这个最一般的形式推出来然后再看对称条件下它如何退化。这样你在脑子里留下的是一棵完整的知识树而不是一堆孤立的内存碎片。2. 第一遍推用分量展开打底理解最原始的偏导过程这个方法不花哨但最朴素可信。它完全绕开矩阵求导的任何规则只用多元微积分里偏导数的定义。2.1 把二次型彻底展开成求和形式设 x (x₁, x₂, ..., xₙ)^TA (a_ij)。那么f(x) x^TAx Σᵢ Σⱼ x_i a_ij x_j这里的下标 i 是行索引j 是列索引。展开之后每一项是 a_ij x_i x_j扫过所有 (i,j) 组合。举个例子n2 的时候展开是这样f(x) a₁₁x₁² a₁₂x₁x₂ a₂₁x₂x₁ a₂₂x₂²注意 a₁₂x₁x₂ 和 a₂₁x₂x₁ 不是合并同类项的关系它们对应的矩阵位置不同、系数不同虽然 x₁x₂ 和 x₂x₁ 在数值上相等但系数分别来自矩阵的不同位置所以必须分开写。这是分量展开和平时处理对称二次型最大的一个感受差异。2.2 对第 k 个分量求偏导现在固定 k我们对 x_k 求偏导。关键在于求和号里面哪些项包含 x_k 逐项翻一遍包含 x_k 的项只可能出现在两种情况下i k 的时候x_i 就是 x_k所以所有 a_kj x_k x_j 这些项都跟 x_k 有关j 从 1 到 n 遍历。j k 的时候x_j 就是 x_k所以所有 a_ik x_i x_k 这些项也都跟 x_k 有关i 从 1 到 n 遍历。其余 i ≠ k 且 j ≠ k 的项完全不包含 x_k求偏导时直接归零。所以∂f/∂x_k ∂/∂x_k [ Σⱼ a_kj x_k x_j Σᵢ a_ik x_i x_k ]接下来要小心一点。第一项里x_k 和 x_j 都可能是同一个变量当 jk 时但我们按乘积的求导法则老老实实处理不需要额外分类讨论法则会自动覆盖这些情况∂/∂x_k (a_kj x_k x_j) a_kj x_j a_kj x_k δ_kj其中 δ_kj 是克罗内克符号当 kj 时为1否则为0。但为了更直观一点另一种做法是直接在求和层面做。我们把偏导符号放进求和号里∂f/∂x_k Σⱼ a_kj (∂(x_k x_j)/∂x_k) Σᵢ a_ik (∂(x_i x_k)/∂x_k)对第一项里的乘积 x_k x_j 求导如果 j ≠ k只有 x_k 在变化导数为 x_j如果 j kx_k x_j 变成 x_k²导数为 2x_k。用式子统一表示就是∂(x_k x_j)/∂x_k x_j x_k δ_kj同理∂(x_i x_k)/∂x_k x_i x_k δ_ik但等一下如果我们把这两项合在一起看其实有个更简洁的操作——不拆分 δ直接用链式法则Σⱼ a_kj x_j Σᵢ a_ik x_i等等我需要验证这个结果是否精确成立。让我们再推导一次中间不丢项。回到∂f/∂x_k Σⱼ a_kj ∂(x_k x_j)/∂x_k Σᵢ a_ik ∂(x_i x_k)/∂x_k Σⱼ a_kj (x_j x_k δ_kj) Σᵢ a_ik (x_i x_k δ_ik) Σⱼ a_kj x_j x_k Σⱼ a_kj δ_kj Σᵢ a_ik x_i x_k Σᵢ a_ik δ_ik Σⱼ a_kj x_j a_kk x_k Σᵢ a_ik x_i a_kk x_k Σⱼ a_kj x_j Σᵢ a_ik x_i 2a_kk x_k嗯这里出现了额外的 2a_kk x_k。这和前面说的 (A x)_k (A^T x)_k 是不是一致呢(Ax)_k Σⱼ a_kj x_j(A^T x)_k Σᵢ (A^T)_ki x_i Σᵢ a_ik x_i所以 (Ax)_k (A^T x)_k Σⱼ a_kj x_j Σᵢ a_ik x_i问题来了我上面推导得到的是 Σⱼ a_kj x_j Σᵢ a_ik x_i 2a_kk x_k但这里没有两个 a_kk x_k 啊——等等让我重新审视一下。对于 kj 的那个特例当我写 a_kj x_j 时它已经包含了 a_kk x_k对于 ik 的那个特例a_ik x_i 已经包含了 a_kk x_k。所以总和 Σⱼ a_kj x_j Σᵢ a_ik x_i 在 k 处的问题中已经包含了两次 a_kk x_k——最后两项中的 x_i 和 x_j 实际上就是同一个分量。让我重新更仔细地检查一下f(x) Σ_{i1}^n Σ_{j1}^n a_ij x_i x_j只看包含 x_k 的项。ik 时Σ_j a_kj x_k x_jjk 时Σ_i a_ik x_i x_k。合起来是含 x_k 的部分 Σ_j a_kj x_k x_j Σ_i a_ik x_i x_k注意这里 (ik, jk) 这一项被算了两次一次是 a_kk x_k x_k 在第一个求和中ik, jk另一次是 a_kk x_k x_k 在第二个求和中jk, ik。但这两个实际上是同一项因为它们都代表 a_kk x_k²。所以在展示数学关系时拆分“包含 x_k 的项”会重复计算 a_kk x_k²。解决这个问题的标准方法是只需直接利用微分或者推导更干净。或者灵活利用对称指标。让我使用更干净的方法f Σ_i Σ_j a_ij x_i x_j∂f/∂x_k Σ_i Σ_j a_ij (δ_ik x_j x_i δ_jk)其中 δ_ik 表示 ikδ_jk 表示 jk。 Σ_j a_kj x_j Σ_i a_ik x_i啊这样一步就正确多了根本不需要只提取 x_k 的项可以直接用线性导数的分配性质。所以 ∂f/∂x_k Σ_j a_kj x_j Σ_i a_ik x_i (Ax)_k (A^T x)_k这就给出了梯度 ∇f (A A^T)x。现在让我修正文本。在推导之前我从求和开始必须使用指示函数/δ符号方法这可以避免重复计数并有效解释与“提取相关项但核算两次”之间的差异。写正文时我将以此为准f(x) Σ_i Σ_j a_ij x_i x_j对 x_k 求偏导∂f/∂x_k Σ_i Σ_j a_ij (∂(x_i x_j)/∂x_k) Σ_i Σ_j a_ij (δ_ik x_j x_i δ_jk) Σ_j a_kj x_j Σ_i a_ik x_i (Ax)_k (A^T x)_k这个干净。但要注意不要落入把含 x_k 的项进行分离并计数两次的陷阱。我会指出这与可能出现的不仔细展开不同。2.3 区分两个求和项上面第一项 Σ_j a_kj x_j它是矩阵 A 第 k 行和 x 做内积正是 (Ax) 的第 k 个分量第二项 Σ_i a_ik x_i它是 A 第 k 列和 x 做内积也正是 (A^T x) 的第 k 个分量。所以完整梯度是∇f(x) Ax A^T x (A A^T)x到这里第一遍推导完成。整个过程只有一个关键操作对乘积 x_i x_j 求偏导然后用克罗内克 δ 做筛选。熟练之后这个过程非常机械不容易错。3. 第二遍推用矩阵微分简单到有点怀疑人生分量展开虽然直观但遇到更复杂的矩阵表达式时展开会变得越来越繁琐。这时候就应该切换到矩阵微分的语言。这一部分的核心思想是先算 df再把 df 改写成某个向量与 dx 的内积。3.1 微分的两个基本规则矩阵微分有两条规则特别常用乘积法则d(XY) (dX)Y X(dY)对矩阵和向量都成立。转置和微分可交换d(X^T) (dX)^T。另外还有一个很重要的约定对于一个标量函数 f(x)它的微分可以写成 df ∇f^T dx这里的 ∇f 就是我们要求的梯度向量。3.2 直接对 x^TAx 取微分令 f x^TAx则df d(x^T)Ax x^T d(Ax)第一项 d(x^T) (dx)^T第二项 d(Ax) A dxA 是常矩阵不参与微分。所以df (dx)^T A x x^T A dx现在看第一项 (dx)^T A x。它是一个标量标量的转置等于自己所以(dx)^T A x ((dx)^T A x)^T x^T A^T dx这里要把转置的运算顺序捋清楚(dx)^T A x 是一个 1×1 的矩阵也就是一个数它的转置就是本身而转置之后括号里的次序会反过来(dx)^T A x 的转置是 x^T A^T dx。这样两个项现在都写成了 x^T 某某 dx 的形式df x^T A^T dx x^T A dx x^T (A^T A) dx等等更正确的是写成 x^T(A^T A)dx。另外注意x^T(A^T A)dx 也是一个标量所以可以随意重新排列实际上顺序已经自然了。根据 df ∇f^T dx我们比较两个式子∇f^T dx x^T(A^T A)dx由于 dx 是任意方向我们可以得出结论∇f (A A^T) x这样整个推导只有四五行。实际上这里需要一点严格的推理选择 dx e_i第 i 个单位向量就可以从 ∇f^T dx x^T(A^T A)dx 对所有的 dx 成立中推出行的等价性但习惯上我们会直接识别出来。3.3 为什么这个方法更值得推荐分量展开适合第一次理解“梯度到底是什么”但一旦理解了日常推导我强烈建议用微分法。原因有两个。第一微分法不用记忆任何复杂的矩阵求导公式表只需要知道 d(XY) 和标量转置这两个规则很多看起来吓人的公式都可以在 30 秒内推出来。第二微分法不容易出错因为它把每一步的变形都限制在微分的框架里不像查公式表那样容易代错参数。我再补充一点经验之谈用微分法的时候有一个习惯非常重要——每次推完都把结果转换成“df 某向量^T dx”的标准形式。这个习惯能帮你少踩很多坑因为矩阵求导最混乱的地方就在于分子分母布局到底是行向量还是列向量、要不要转置全在这个环节出问题。而统一用 df ∇f^T dx 的形式之后你的梯度向量永远是列向量不会有任何歧义。4. 第三遍推A 对称或者被强行对称4.1 对称矩阵条件下的退化公式在机器学习的绝大多数模型里出现在二次型位置的矩阵要么天然对称要么被刻意改造成对称形式。比如最小二乘的目标函数L(w) ||y - Xw||² (y - Xw)^T(y - Xw)展开后二次项是 w^T(X^TX)w这里的 X^TX 天然对称。又比如高斯分布的指数部分 (x-μ)^TΣ^{-1}(x-μ)协方差矩阵的逆 Σ^{-1} 也是对称的。所以当 A 对称时(A A^T) 2A∇f 2Ax这就是大家最熟悉的 2Ax 的由来。但你要时刻记得它成立的前提是 A 对称。如果 A 不对称梯度就是 (A A^T)x不是 2Ax——尽管有时候你硬写成 2Ax在对称部分相同的情况下恰好碰对了但中间的推理是有漏洞的。4.2 如果一个矩阵不对称的二次型“看起来”该对称怎么办实际工程里有一个非常常见的情况你拿到一个表达式 x^TMx其中 M 并不是对称矩阵但这个二次型本身在数学上等于 x^T[(MM^T)/2]x。因为对于标量有x^TMx (x^TMx)^T x^TM^Tx所以 x^TMx x^T[(M M^T)/2]x。也就是说决定一个二次型数值的只有矩阵的对称部分 (MM^T)/2反对称部分对二次型值没有贡献。因此在求梯度的时候如果方便可以先把 M 替换成它的对称化版本再用 2Ax 的公式。但用 (MM^T)x 直接算结果也完全一样两种方式殊途同归。4.3 一个典型的“陷阱”A 不对称时的 2Ax 幻觉我见过不少同学在试卷或者代码里写出 ∇(x^TAx) 2Ax然后怎么验都不对。问题出在哪本质上是把 A 默认为对称了。一个特别容易踩坑的例子是A [[0, 1], [0, 0]]x (1, 2)^T那么 x^TAx x₁x₂ 2。用 2Ax 算梯度 2 [0,1],[0,0] ^T (4, 0)^T。但用数值差分检验一下∂f/∂x₁ 应该等于 x₂ 2∂f/∂x₂ 应该等于 x₁ 1。显然 (4, 0)^T 是完全错的。用正确公式 (AA^T)x [0,1],[1,0] ^T (2,1)^T和数值差分完全吻合。这个例子我在文章和教学里反复用因为它能瞬间击碎“二次型梯度就是 2Ax”的错误直观。5. 数值验证用有限差分把你的推导结果当场抓住5.1 中心差分公式和验证步骤即使推导过程看起来天衣无缝我仍然强烈建议在上代码之前先做一次数值验证。因为公式推导可能犯错误而数值差分是独立于符号推导的第二道安全网。中心差分公式如下∂f/∂x_k ≈ (f(x h e_k) - f(x - h e_k)) / (2h)其中 e_k 是第 k 个标准基向量h 取一个很小的值比如 10^{-7} 或者 10^{-6}。验证的步骤非常简单随机生成一个 x 和 A注意要不要对称自己定。用解析公式计算梯度向量。用中心差分逐分量计算数值梯度。对比结果看最大绝对误差是否在 10^{-6} 量级以下。5.2 一份可以直接跑的验证小代码下面我给一份示例代码。用 Python 的 numpy 写实现了一个非对称矩阵的验证代码里注释写清楚了每一步在做的事情。import numpy as np def f(x, A): return x A x def grad_analytic(x, A): return (A A.T) x def grad_numeric(x, A, h1e-6): n len(x) g np.zeros(n) for k in range(n): e np.zeros(n) e[k] 1.0 fp f(x h * e, A) fm f(x - h * e, A) g[k] (fp - fm) / (2 * h) return g # 随机生成一个非对称矩阵和向量 rng np.random.default_rng(42) A rng.standard_normal((4, 4)) x rng.standard_normal(4) g_ana grad_analytic(x, A) g_num grad_numeric(x, A) print(解析梯度:, g_ana) print(数值梯度:, g_num) print(最大误差:, np.max(np.abs(g_ana - g_num)))跑出来的结果通常最大误差在 1e-8 量级这主要取决于浮点精度和 h 的选取。如果哪天你发现解析结果和数值结果对不上不用犹豫基本可以断定是解析推导那一侧出了问题。5.3 我和有限差分较劲的一次实际经历有一次我在实现一个带约束的优化算法目标函数里有一个非线性项化简完出现了一坨矩阵表达式我怀疑其中某个子块的转置放错了位置。当时整个公式很长肉眼扫了三遍都没看出问题。后来就是用中心差分锁定了错误解析梯度在第 3 个分量上始终差一个符号。一查代码果然是某个矩阵乘法里把 A 写成了 A^T。从那以后我养成了一个习惯凡是写好一个优化目标的梯度上优化器之前必跑一次有限差分验证。这个习惯也推荐给大家。尤其是当你用自动微分框架比如 PyTorch、JAX的时候并不是说自动微分就不会错而是当你自己手写了一个自定义的梯度函数时数值差分是检验它正确性的最可靠手段。6. 那些年我们一起踩过的坑常见错误一览6.1 误区一无条件使用 2Ax正如前面强调的2Ax 只有在 A 对称时才成立。对于一般矩阵必须用 (A A^T)x。这个错误出现的频率有多高我几乎在每一届初学者身上都见过包括早期写代码的我自己。如何避免建议在正式推导或代码实现之前先问自己一句这里矩阵 A 是否对称如果不对称就用通用公式。6.2 误区二混淆梯度的方向和布局矩阵求导有两种布局约定分子布局和分母布局。如果你用的资料里混用了这两种结果经常差一个转置。我在实践中的习惯是坚持把梯度定义为列向量也就是符合“一个普通函数对列向量 x 求导得到列向量”的直觉。用微分法写 df ∇f^T dx天然就是这个布局不用特意记。6.3 误区三忽略矩阵和向量的维度匹配对标量函数 x^TAxx 必须是 n×1A 必须是 n×n结果才是标量。但如果写成 x A x^T那 x 就是 1×n 的行向量得到的结果又是一个不同的标量梯度的形状会跟着变化。初学者很容易在转置符号上犯迷糊进而搞错整个二次型的形状。一个小技巧每次写完一个表达式先检查各个矩阵/向量的维度能不能乘起来最终的函数值是不是 1×1。这个检查在草稿阶段就要做别等代码报错才回头。6.4 一个容易忽略的细节A 本身可能还依赖于 x如果 A 里的元素也含 x那就不能简单套 (AA^T)x 了。比如 f(x) x^TA(x)x这时候需要对 A(x) 也求导会牵扯出更高阶的张量运算。本文所有推导都假设 A 是常数矩阵。实际建模中如果 A 是 x 的函数请务必回到最基本的微分定义不要机械套公式。6.5 一个进阶提醒链式法则里藏着的转置在多层复合函数里比如 f(g(x))其中 g 把 x 映射成另一个向量那么用链式法则时通常会遇到雅可比矩阵。这时候如果雅可比矩阵被写成行优先或者列优先梯度方向很可能就反了。我的建议是每次遇到链式法则都完整写出维度再决定要不要转置不要凭感觉。7. 二次型梯度在几个经典模型里的实战应用7.1 最小二乘线性回归线性回归的目标函数是L(w) ||y - Xw||² y^Ty - 2w^TX^Ty w^TX^TXw只看最后一项它是 w^T(X^TX)w 的形式而 X^TX 是对称矩阵因此∂(w^TX^TXw)/∂w 2X^TXw中间项的梯度是 -2X^Ty常数项为 0。合起来∇L(w) 2X^TXw - 2X^Ty令梯度等于 0就得到了正规方程 X^TXw X^Ty。整个过程用二次型梯度做不用展开任何求和符号几行就写完了。7.2 岭回归岭回归在最小二乘的基础上加了一个 L2 正则项L(w) ||y - Xw||² λ||w||²第二项可以写成 w^T(λI)w这是 A λI 的二次型。因为 I 对称梯度为 2λw。加上前面的梯度得到∇L(w) 2(X^TX λI)w - 2X^Ty令梯度为 0得到 (X^TX λI)w X^Ty。这就是岭回归的闭合解形式。7.3 瑞利商和对称特征值问题瑞利商 R(x) (x^TAx)/(x^Tx) 是另一处二次型梯度频繁登场的地方A 通常设对称。对它用商法则∇R [2Ax(x^Tx) - 2x(x^TAx)] / (x^Tx)²令梯度为 0得到 Ax (x^TAx)/(x^Tx) x也就是特征方程 Ax λx 的形式。这说明瑞利商的驻点就是特征向量。二次型梯度的推导在这里直接通向特征值问题的变分描述是理论推导中一个非常经典的应用场景。7.4 高斯分布的最大似然多元高斯分布的对数似然里有这样一项-1/2 (x - μ)^T Σ^{-1} (x - μ)它也是一个二次型只不过变量是 (x - μ)。如果对 μ 求梯度会涉及链式法则∂/∂μ [(x-μ)^TΣ^{-1}(x-μ)] -2Σ^{-1}(x-μ)这个结果在推导高斯分布的 MLE 或者变分推断时经常用到。别看它简单如果布局搞错符号就会反后续整个推导都会跟着崩。8. 我的经验与建议怎么把二次型梯度内化成直觉推了三种方法写了那么多最后我想分享几条个人体会。第一矩阵求导不是一个需要死记硬背的东西。你只需要记住几条规则乘积法则、标量转置不变性以及 df ∇f^T dx 这个统一格式几乎所有常见的向量/矩阵求导都可以现场推出来。这个方法我在工作中用了很多年从未失效。第二遇到不对就上数值验证。面对一个很长的梯度公式与其盯着屏幕逐行检查不如花五分钟跑一个有限差分。数值差分不看你的推理过程只看你的最终结果对不对它是全世界最公道的裁判。第三在推导前先理清你处理的对象到底是不是对称的。这个习惯虽然简单但能帮你规避最多一半的求导错误。每次写完 x^TAx都问一句“这里的 A 是常数吗是对称的吗”然后选择对应的公式这几乎成为我在白板上推公式时的口头禅。最后二次型梯度只是一个起点。一旦彻底掌握了这类推导你会发现逻辑回归的 Hessian、牛顿法里的二阶项、各种正则项的梯度全都变成同一套方法下的简单应用题。这才是这个知识点真正的价值它不是一道孤立的考试题而是一把能打开很多后续推导之门的钥匙。希望这篇推导整理能帮你少走一些弯路。如果你看完之后自己动手在纸上推一遍再跑一遍数值验证的代码这个知识点就彻底长在你身上了。