
矩阵 A 的转置乘 A 是否一定正定这大概是线性代数课上被问得最多、也被答错得最多的问题之一。我在实际项目里见过不止一次有人很有把握地说A^T A 当然是正定的因为 x^T A^T A x 就等于 Ax 的范数平方肯定大于 0然后转头在最小二乘程序里收到一条LinAlgError: Matrix is not positive definite。问题就出在那个“肯定”上||Ax||^2 ≥ 0这个不等式只能给出半正定要升级成正定必须再补一个条件而且这个条件不是关于 G A^T A 自身的是关于 A 的。矩阵 A 的转置乘 A 是正定矩阵的充分必要条件一句话版本就是A 的列向量线性无关即 A 列满秩rank(A) n其中 A 是 m×n 的实矩阵。这句话往下拆可以牵出零空间、秩-零化度定理、奇异值、条件数平方、Cholesky 分解以及一堆看起来无懈可击其实有致命漏洞的伪证明。下面我按自己的理解顺序从为什么、怎么证、怎么算、怎么判、怎么用这条线把它捋一遍数学方向的学生、写数值代码的工程师、需要判断协方差矩阵性质的数据分析者应该都能从中拿到自己那一份。1. 先把半正定这件事钉死1.1 无条件成立的那个版本设 A 是 m×n 实矩阵定义 G A^T A。G 是 n×n 的并且 G^T (A^T A)^T A^T A G所以 G 一定是实对称矩阵。这一步跟 A 长什么样完全没关系A 是零矩阵也是对称的零矩阵当然对称。接下来对任意 x ∈ R^nx^T G x x^T A^T A x (Ax)^T (Ax) ||Ax||^2 ≥ 0范数平方非负所以 G 是半正定的。这三行没有任何前提条件不需要 A 满秩不需要 A 是方阵也不需要 m 和 n 的大小关系。顺手还能拿到两个副产品第一G 的对角元 G_ii 等于 A 第 i 列向量的范数平方所以只要 A 有一列是全零G 就有一个对角元为 0而正定矩阵的对角元必须全为正直接判死第二tr(G) 等于 A 所有元素平方和也就是 A 的 Frobenius 范数平方这个恒等式在调试时很好用如果它算出来是 0说明 A 就是零矩阵根本不用往下判。复数域里的版本完全平行把转置换成共轭转置G A^H A 一定是 Hermitian 半正定的x^H G x ||Ax||² 依旧成立。后面为了叙述方便全部按实矩阵写结论一字不改就能搬过去。1.2 从“非负”到“严格正”差的那一步半正定和正定的区别只有一个字二次型取零的那些点。定义上G 正定要求对所有非零 x 都有 x^T G x 0半正定只要求 ≥ 0允许存在某些非零 x 使二次型恰好等于 0。代进我们的表达式x^T G x 0 等价于 ||Ax||² 0也就等价于 Ax 0。所以如果存在非零 x 满足 Ax 0也就是 A 的零空间 N(A) 里除了零向量还有别的东西那么在这个 x 上二次型取到 0G 只是半正定不是正定如果 Ax 0 只有零解那么任何 x ≠ 0 都给出 Ax ≠ 0从而 ||Ax||² 0G 就是正定的。问题到这里已经被完整翻译成了“齐次方程 Ax 0 有没有非零解”而这正是列向量线性无关性的定义。整个证明其实已经结束了剩下的全是换个说法。有一条恒等式值得单独记下来N(A^T A) N(A)。包含关系 N(A) ⊆ N(A^T A) 是显然的Ax 0 当然推出 A^T Ax 0反过来若 x ∈ N(A^T A)则 x^T A^T A x 0即 ||Ax||² 0于是 Ax 0。两边一夹零空间完全相同。这条恒等式是后面所有秩等式的基础。1.3 为什么这里最容易翻车翻车点通常有两个。第一个是把“非负”当“正”这是纯粹的逻辑跳跃属于纸面上的粗心。第二个更隐蔽把退化情况当成思维里的特例直接跳过觉得“实际数据不会这么巧”。可我的经验恰恰相反退化在真实数据里出现的概率远比想象中高。做回归时如果同时放了“总价”和“总价的万元表示”两个特征A 立刻列不满秩G 立刻奇异正规方程直接崩做时间序列时如果两个传感器的读数只差一个固定比例同一件事。这不是什么人为构造的病态例子这就是数据预处理里最常见的失误。所以半正定和正定这一字之差在工程上就是“程序跑得通”和“程序抛异常”的差别。把这句话刻在脑子里A^T A 的正定性从来不是 A^T A 自己的性质而是 A 的列向量组的性质。判断对象搞错了后面全是白费。2. 充分必要条件把“A 列满秩”翻译成六种说法2.1 定理的完整陈述设 A 是 m×n 实矩阵。下列命题两两等价G A^T A 是正定矩阵rank(A) nA 的 n 个列向量线性无关齐次方程 Ax 0 只有零解即 N(A) {0}A^T A 可逆非奇异A 的所有奇异值严格大于 0A 作为线性映射 R^n → R^m 是单射。此外若 A 还是方阵m n以上全部等价于 det(A) ≠ 0也就是 A 本身可逆。这张清单的实用价值在于做实际问题时你几乎不会真的去构造 G 再算它的特征值而是直接看 A 的秩、看列向量是否相关、看最小奇异值离 0 有多远。从计算成本和数值稳定性两个角度讲后者永远是更优的选择原因后面第 4 节会细说。顺带说一句条件 7 的几何含义值得体会列满秩意味着 A 把 R^n 单射进 R^m 的一个 n 维子空间里不折叠、不塌陷。A^T A 的正定性本质上是在说“这个映射在每个方向上都有真实的伸缩”没有任何方向被压成 0。2.2 两条证明路线我推荐第一条路线一零空间路线。正定定义要求对任意 x ≠ 0x^T G x 0。x^T G x ||Ax||²所以 x^T G x 0 ⟺ Ax 0 ⟺ x ∈ N(A)。于是 G 正定 ⟺ N(A) {0}。再用秩-零化度定理 rank(A) dim N(A) nn 是列数也就是未知量个数立刻得到 rank(A) n。这条路线三行写完不依赖任何高级工具我推荐给所有刚接触这个问题的人。它还有个好处把“正定性”这个二次型语言的概念翻译成了“线性方程组解的唯一性”这个更朴素的语言直觉上更容易接受。路线二特征值路线。G 实对称所以存在正交矩阵 Q 使 Q^T G Q diag(λ_1, ..., λ_n)所有 λ_i 都是实数。半正定意味着所有 λ_i ≥ 0正定则要求所有 λ_i 0。而 λ_i 是 A^T A 的特征值√λ_i 正好是 A 的奇异值 σ_i。于是“G 正定” ⟺ “A 的奇异值全为正” ⟺ “A 列满秩”。这条路线顺便送来一个定量关系cond₂(A^T A) cond₂(A)²它是第 4 节数值陷阱那部分的主角。路线三顺序主子式不推荐。Sylvester 判据说对称矩阵正定 ⟺ 所有顺序主子式左上角 k×k 子式k 从 1 到 n都大于 0。用它来证 A^T A 正定是绕远路而且很容易踩坑。坑在于顺序主子式判据只对对称或 Hermitian 矩阵成立随手拿一个非对称矩阵套用会得出荒谬结论。做证明时我见过有人对一个普通方阵算顺序主子式全正就宣称正定那是根本性错误。不过这个判据在 A^T A 上有个漂亮的解释G 的第 k 个顺序主子式恰好是 A 的前 k 列构成的子矩阵 A_k 的 Gram 行列式 det(A_k^T A_k)它非零意味着前 k 列线性无关。所以顺序主子式全正其实是在逐列地检查“每一列都不能被前面的列线性表示”跟列满秩是同一件事的不同切法。2.3 形状决定命运三种局面速查关于 A 的形状有个非常实用的对照A 的形状A^T A 的阶数可能正定吗原因m n高瘦n×n可能列满秩时正定这是最常见的“数据够用”情形m n方阵n×n可能只要 A 可逆就正定m n矮胖n×n不可能rank(A) ≤ m n必然奇异必有零特征值第三种情况最容易被忽略。一个 2×3 的矩阵 A秩最多是 2而 A^T A 是 3×3 的秩最多 2也就意味着至少有一个零特征值。所以矮胖矩阵的 A^T A 永远只是半正定永远不正定无论你怎么挑元素、怎么调数值。这个结论对做欠定方程、压缩感知、或者不小心把数据表行列转置了的人特别有用——你不用试一试就是错的。顺着这个思路还能得到一个小推论如果 A 的列数大于行数你根本不需要计算直接就能下结论“不正定”。我在 review 代码时见过有人对 30×100 的矩阵算了半天特征值其实一句话就能否掉。3. 三个手算例子把直觉焊死在脑子里3.1 正例高瘦且列满秩取 A [[1,0], [0,1], [1,1]]3×2 的矩阵。先算 G A^T A第一行第一列 1² 0² 1² 2 第一行第二列 1·0 0·1 1·1 1 第二行第二列 0² 1² 1² 2所以 G [[2,1],[1,2]]。行列式 2·2 − 1·1 3 0迹 4 0特征值 (4 ± √(16−12))/2 (4 ± 2)/2得到 3 和 1两个都正正定。用定义直接验算更直观。令 x (x₁, x₂)x^T G x 2x₁² 2x₁x₂ 2x₂² x₁² x₂² (x₁ x₂)²三个平方项相加只有 x₁ x₂ 0 时才可能为 0这跟“列线性无关”完全对应。这里能配成平方和不是巧合任何 x^T A^T A x 都能写成 ||Ax||²也就是若干个线性组合的平方和配方只是把这个事实显式展开。3.2 反例一列成比例取 B [[1,1], [2,2], [3,3]]3×2第二列正好是第一列的两倍。G B^T B [[14,14],[14,14]]秩 1特征值 28 和 0。半正定但绝对不正定。取 x (1, −1) 直接得到 0连算都不用算。这个例子值得多看一眼B 不是零矩阵m n 形状看起来“应该没问题”元素也是普通的正整数可它就是不正定。这就是为什么不能只看形状还得看列相关性——而列相关性在真实数据里往往就是以“两个特征只差一个倍数”这种平淡无奇的形式出现的。3.3 反例二矮胖取 C [[1,0,0], [0,1,0]]2×3。G C^T C diag(1, 1, 0)特征值 1、1、0。取 x (0,0,1) 时二次型为 0。这里甚至不需要 C 的列有什么关联单纯是维度不够第三列在二维空间里无论如何都活不成独立方向。3.4 反例三方阵但奇异取 D [[1,2],[2,4]]方阵det 4 − 4 0。G D^T D [[5,10],[10,20]]一样是秩 1特征值 25 和 0。很多人的思维定式是“方阵嘛只要不是零矩阵大概就能求逆”这个例子就是专门打这种定式的。方阵可逆与否则看行列式转置乘自己并不会凭空创造出可逆性。3.5 一个必须单独说的坑特征值别乱猜不少人脑子里有个模糊印象“A^T A 的特征值就是 A 的特征值的平方”。这话在 A 对称时成立一般情况下是错的。反例A [[1,1],[0,1]]特征值 1、1看起来挺“正”。但 A^T A [[1,1],[1,2]]特征值是 (3 ± √5)/2约 2.618 和 0.382跟 1、1 差得远。正确说法是A^T A 的特征值是 A 的奇异值的平方。奇异值和特征值只有在 A 正规包含对称、正交、反对称这几种时才通过绝对值对上关系 σ_i |λ_i|。对于矩形矩阵“A 的特征值”这个说法本身就不成立根本无法比较。这个误区在判断正定性上的实际危害是你会因为 A 看起来特征值都非负就错误地推断 A^T A 一定正定。而矩阵是否正定完全由它自己的特征值符号决定跟 A 的特征值没有直接对应关系。4. 代码里怎么判五种判据的边界与实测4.1 判据对比判据数学形式计算成本数值稳健性适用场景定义法验证二次型恒正极高差证明用不用于计算顺序主子式所有 det(G_k) 0O(n⁴)很差n 极小的教学演示特征值λ_min 0O(n³)一般需要知道最小特征值有多小Cholesky 分解G LL^T 成功O(n³)/3较好算法内部首选秩判据rank(A) nO(mn²)最好判断 A^T A 正定的首选我的习惯很固定判断 A^T A 是否正定几乎永远走最后一行直接看 A 的秩用 SVD 做带相对容差。理由很直接——A^T A 的条件数是 A 的条件数的平方你在 G 上做判断等于把精度损失了一倍本来能分辨的东西变分辨不了了。Cholesky 是第二选择适合那些“我就是要分解出来用”的场景。它有个好处数据结构上不需要额外的容差成功就是成功抛异常就是不正定判定干脆。坏处是对于“几乎奇异”的矩阵它会因为一个微小的负值而失败而你其实很想把它当成正定来处理——这时候就需要正则化介入第 5 节会讲。4.2 Python 实操三行核心逻辑import numpy as np def check_ata(A, tol1e-10): A np.asarray(A, dtypefloat) m, n A.shape G A.T A s np.linalg.svd(A, compute_uvFalse) # 奇异值降序排列 if s.size 0: return G, False, s rank int((s tol * s[0]).sum()) # 相对容差判秩 return G, rank n, s A np.array([[1., 0.], [0., 1.], [1., 1.]]) G, is_pd, s check_ata(A) print(G) print(特征值:, np.linalg.eigvalsh(G)) print(奇异值:, s, 正定?, is_pd)跑出来 G 是[[2. 1.] [1. 2.]]特征值[1. 3.]奇异值[1.73205081 1.]正定判定为 True。注意奇异值是特征值的算术平方根1.732 是 √31 是 √1跟手算完全一致。换成坏例子B np.array([[1., 1.], [2., 2.], [3., 3.]]) G2, is_pd2, s2 check_ata(B) print(G2) print(特征值:, np.linalg.eigvalsh(G2)) print(奇异值:, s2, 正定?, is_pd2)G2 是[[14. 14.] [14. 14.]]特征值[0. 28.]奇异值[6.324 0.]判定 False。注意最小奇异值直接是 0不是 1e-17 之类的噪声因为这个例子是精确的秩亏浮点也很难算出非零。Cholesky 版本def is_pd_cholesky(G): try: np.linalg.cholesky(G) return True except np.linalg.LinAlgError: return False print(is_pd_cholesky(A.T A)) # True print(is_pd_cholesky(B.T B)) # False条件数对比实验用范德蒙德矩阵最直观t np.linspace(0.0, 1.0, 21) V np.vander(t, 8, increasingTrue) print(cond(V) , np.linalg.cond(V)) # 量级约 1e6 print(cond(V^T V) , np.linalg.cond(V.T V)) # 量级约 1e12两个数相差正好是平方关系。这就是为什么“列满秩但接近共线”的数据用正规方程解出来的系数会离谱到没法看——你在一个条件数 1e12 的系统上做消元双精度的 16 位有效数字基本被吃干净。4.3 秩判定到底怎么设容差np.linalg.matrix_rank的默认容差是 max(m, n) · eps · σ_max其中 eps 是机器精度双精度下约 2.22e-16。这个默认值在大多数场景下合理但如果 A 是病态构造的范德蒙德、指数衰减基函数、近似共线的特征就得自己动手。我自己踩过的坑用一组极其接近共线的特征做回归数学上传入的 A 列确实线性无关理论上正定但浮点计算之后 σ_min/σ_max 掉到 1e-17 量级比机器精度还小SVD 给出的 σ_min 干脆就是 0 或者 1e-18 的纯噪声。这时候你判“不正定”程序拒绝求解其实是物理上正确的——输入数据本身的数据质量不足以支撑这个精度的结论。工程上的正确处理不是提高精度而是要么删特征要么上正则化接受一个有偏但稳定的解。提示判断 A^T A 是否正定不要在 G 上看“特征值是否大于某个小数”。G 的最小特征值是 σ_min²平方一次就把 1e-8 的差别压成 1e-16直接掉进浮点噪声里。要看就看 A 的 σ_min保留原始精度。还有一个容易忽视的点如果 A 的各列量纲差异巨大一列是百万级的销量一列是零到一之间的比例G 的对角元会相差好几个数量级条件数被人为放大。这时候先做列标准化每列除以其范数再判断秩结果会诚实得多。我现在的习惯是任何涉及 A^T A 的判断前面都先做一次列归一化。5. 这个结论在工程里到底值多少钱5.1 最小二乘与正规方程解 min ||Ax − b||²对 x 求导令其为零得到正规方程 A^T A x A^T b。解的唯一性完全由 A^T A 的正定性决定A 列满秩 → A^T A 正定 → 唯一解 x (A^T A)⁻¹ A^T bA 列不满秩 → A^T A 奇异 → 无穷多解必须额外加约束比如取最小范数解用伪逆。这就是为什么“用 30 个样本拟合 100 个特征”的回归解不出来而“用 1000 个样本拟合 10 个特征”很轻松。不是算法的问题是矩阵结构在源头上就不允许。还要提一句“理论正确但工程错误”的经典案例即使 A 列满秩直接算 (A^T A)⁻¹A^T b 也是下策因为 cond(A^T A) cond(A)²。列满秩但接近共线时正规方程解出的系数可能跟真值差着好几个数量级。工程上的标准做法是改用 QR 分解对 A 直接做条件数是 cond(A) 而不是它的平方或 SVD。数学上两条路等价数值上差得很远。5.2 协方差矩阵、Gram 矩阵与核矩阵同样的结构在不同领域反复出现只是换了名字协方差矩阵中心化数据矩阵 Xn 个样本 × p 个特征协方差 C X^T X/(n−1)。C 正定 ⟺ X 列满秩 ⟺ p 个特征线性无关且 n ≥ p。做 PCA 时如果 p n协方差必然奇异这就是为什么高维小样本数据做 PCA 前必须降维或者用对偶形式。线性核的 Gram 矩阵K X X^T这是 A A^T 的形式正定条件是 X 行满秩。核方法的核矩阵有限个点上构造的核矩阵正定核的定义本身就要求它对任意有限点集半正定这个要求比“某一个核矩阵半正定”强得多。顺手记一条容易混的A A^T 和 A^T A 的非零特征值完全相同这是 AB 与 BA 非零特征值相同的特例但两个矩阵的正定性条件不同——A A^T 正定 ⟺ A 行满秩rank mA^T A 正定 ⟺ A 列满秩rank n。方阵时两者一致矩形矩阵时千万别混。我见过有人在小样本高维场景下误判 A A^T 的性质结论直接反了。5.3 正则化花一点偏差买一个正定实际工程里A^T A 不满秩的情况太常见——共线、样本不足、特征重复、编码时产生完全共线的哑变量处理办法不是硬求而是加一个“正定保险”A^T A λI λ 0这个矩阵对任意 A、任意 λ 0 都严格正定。验证只用一行x^T (A^T A λI) x ||Ax||² λ||x||² ≥ λ||x||² 0 x ≠ 0这就是岭回归也就是 Tikhonov 正则化。λ 在这里的作用不只是“防止过拟合”这种笼统说法它在数学上给出了一个特征值的硬下限最小特征值至少是 λ。于是 Ch