第 2 章 线性代数 II:内积、范数、相似度与正交变换

第 1 章建立了空间的结构,但还没回答"两个向量有多近、有多像"。本章补上度量这一层:内积给出夹角,范数给出长度,两者诱导出距离与相似度;正交变换则是在"不变长度"的前提下换坐标系,由此得到投影、最小二乘与 QR 分解。向量检索、嵌入归一化、LoRA 的正交初始化、注意力的缩放点积,全部依赖本章。

学习目标

读完本章,你应当能够:

  1. 默写内积的三组公理,并由此推出柯西-施瓦茨不等式与三角不等式;
  2. 计算欧氏距离、曼哈顿距离、余弦相似度、Jaccard 系数,并说明各自适用的数据形态;
  3. 推导 ‖u − v‖² = ‖u‖² + ‖v‖² − 2uᵀv 以及单位向量下 ‖u − v‖² = 2 − 2cosθ,并据此解释"归一化后内积检索与欧氏检索等价";
  4. 推导投影矩阵 P = A(AᵀA)⁻¹Aᵀ 并说明 P² = P、Pᵀ = P;
  5. 写出正规方程并给出最小二乘的几何解释(残差垂直于列空间);
  6. 比较经典 Gram-Schmidt、修正 Gram-Schmidt 与 Householder QR 的数值稳定性差异;
  7. 实现余弦相似度检索与浮点容差比较(EPS 判等)。

前置知识检查

你需要知道在哪一节补齐
向量、子空间、列空间、零空间第 1 章 1.1.3、1.3.1
矩阵转置与 (AB)ᵀ = BᵀAᵀ第 1 章 1.2.2
矩阵可逆的判定第 1 章 1.4.1
平方根、求和符号 Σ直接用,无额外前置
均值与方差(马氏距离会用到)第 4 章 4.3 节(此处只给出定义)
浮点数不能精确比较本节 2.5.3 直接补齐

2.1 内积:公理化定义

2.1.1 从"投影"到公理

最熟悉的内积是 R^n 上的点积:

1
xᵀy = Σ_{i=1}^{n} xᵢyᵢ = x₁y₁ + x₂y₂ + … + xₙyₙ

它衡量两件事:向量 x 在 y 方向上的"分量有多大"(投影长度),以及两者的方向是否一致。但点积依赖"有坐标"这一前提。在函数空间里没有坐标,我们却同样希望度量"两个函数有多像"。解决办法是把点积中真正起作用的性质抽出来当成公理。

设 V 是实向量空间。内积(inner product) 是一个二元函数 (u, v) ↦ uᵀv ∈ R(本书统一用 uᵀv 记号,不用 ⟨u,v⟩),满足:

类别公理内容
对称性Suᵀv = vᵀu
双线性L1(c₁u₁ + c₂u₂)ᵀv = c₁(u₁ᵀv) + c₂(u₂ᵀv)(对第一个变元线性)
双线性L2由 S 与 L1 自动得到对第二个变元线性
正定性P1uᵀu ≥ 0,且 uᵀu = 0 ⟺ u = 0

每一条都在承担明确职责,缺一条就会破坏后续结论:

  • 去掉对称性:夹角就没有意义,"x 在 y 上的投影"与"y 在 x 上的投影"会不一致,正交补也不存在。复内积空间里对称性被替换为共轭对称 uᵀv = conj(vᵀu)。
  • 去掉线性:投影算子 P = A(AᵀA)⁻¹Aᵀ 的推导每一步都用到线性,“最近点"的唯一性也不再成立。
  • 去掉正定性:uᵀu = 0 时 u 可能非零,那么"零向量"就无法由范数识别,距离函数退化为伪度量。核方法(kernel method)里的核矩阵就允许半正定而非正定,代价是"距离为 0 但两个样本不同”——这正好对应"核函数把不同样本映到同一点"。

2.1.2 由内积诱导范数

有了内积,长度可以自然定义为"向量与自身的内积再开方":

1
‖x‖₂ = √(xᵀx) = √(Σ xᵢ²)

这就是 L2 范数(Euclidean norm)。上式之所以能叫"长度",全靠正定性 P1 保证根号内的量非负。

2.1.3 柯西-施瓦茨不等式与夹角

定理(Cauchy–Schwarz):对任意 u, v,

1
|uᵀv| ≤ ‖u‖·‖v‖

等号成立当且仅当 u 与 v 线性相关。

证明(这个证明技巧在机器学习中反复出现,值得记住):对任意实数 t 考虑

1
0 ≤ ‖u − tv‖² = ‖u‖² − 2t(uᵀv) + t²‖v‖²

右端是关于 t 的二次函数,且对所有 t 都非负,说明它最多只有一个实根,判别式非正:

1
Δ = 4(uᵀv)² − 4‖u‖²‖v‖² ≤ 0    ⟹   (uᵀv)² ≤ ‖u‖²‖v‖²

开方即得。等号成立说明判别式为 0,二次函数在某点取 0,即存在 t 使 u = tv。这个证明的通用模式是"构造一个已知非负的二次型,用判别式夹出不等式",第 3 章证明正定矩阵、第 6 章证明凸性都会再用。

由柯西-施瓦茨,uᵀv / (‖u‖‖v‖) ∈ [−1, 1],于是可以定义夹角 θ:

1
cosθ = uᵀv / (‖u‖‖v‖) ,      θ ∈ [0, π]

以及正交:u ⊥ v ⟺ uᵀv = 0(此时 θ = π/2,由柯西-施瓦茨可见 cosθ = 0 是唯一可能)。这条不等式还是三角不等式的来源:

1
‖u + v‖² = ‖u‖² + 2uᵀv + ‖v‖² ≤ ‖u‖² + 2‖u‖‖v‖ + ‖v‖² = (‖u‖ + ‖v‖)²

2.1.4 常用范数族

范数定义几何形状(单位球 ‖x‖ = 1)典型用途
L0 “范数”非零分量个数不凸(严格说不是范数)稀疏性度量,实际用 L1 替代
L1Σ|xᵢ|菱形Lasso 稀疏正则;曼哈顿距离
L2√(Σxᵢ²)圆/球权重衰减;欧氏距离;梯度裁剪
L∞maxᵢ |xᵢ|正方形逐元素误差上界
Lp(Σ|xᵢ|^p)^(1/p)p = 1, 2 之间插值一般理论

形状差异带来两个工程后果。其一,L1 单位球在坐标轴上有"尖角",最优解容易落在尖角上,所以 L1 正则产生稀疏解;L2 单位球处处光滑,最优解不偏向坐标轴,所以 L2 正则只把权重整体缩小(第 8 章详解)。其二,L∞ 的球是方的,max 运算不可微,所以梯度裁剪更常用 L2 范数(见 2.5.3 节)。

重要

由内积诱导的范数还额外满足平行四边形法则:‖u+v‖² + ‖u−v‖² = 2‖u‖² + 2‖v‖²。这是判断"某个范数是否来自某个内积"的充要条件。L1 和 L∞ 不满足它,所以它们的几何与 L2 有本质差异——这是 L1 正则能产生稀疏解、而 L2 不能的深层原因。

2.2 距离与相似度度量

2.2.1 度量的三条公理

距离(度量,metric) d(·,·) 必须满足:

1
2
3
(1) 非负性与同一性: d(x,y) ≥ 0,且 d(x,y) = 0 ⟺ x = y
(2) 对称性:          d(x,y) = d(y,x)
(3) 三角不等式:      d(x,z) ≤ d(x,y) + d(y,z)

第 (3) 条是"度量"与"随便一个相似度分数"的分界线:它保证"经由中间点不会变近"这一几何直觉,也是 K-Means、KNN、DBSCAN 等算法正确性的隐含前提。余弦相似度不是距离——它不满足三角不等式(可以构造 cos(x,y) = 1、cos(y,z) = 1 但 cos(x,z) = −1 的例子)。要用它做聚类,必须先转成距离 d = 1 − cosθ,此时才满足度量公理。

2.2.2 五类度量的对比

度量公式对量纲/尺度敏感对异常值典型场景
欧氏距离d = ‖x − y‖₂ = √(Σ(xᵢ−yᵢ)²)敏感(量纲不同则被大数量级主导)敏感(平方放大)连续稠密特征、图像、坐标
曼哈顿距离d = Σ|xᵢ − yᵢ|敏感较稳健网格路径、稀疏特征、基站坐标题
余弦距离d = 1 − xᵀy/(‖x‖‖y‖)不敏感(只看方向)稳健文本/嵌入检索、RAG
马氏距离d = √((x−y)ᵀΣ⁻¹(x−y))不敏感(用 Σ 白化)中等特征相关性强、量纲悬殊
JaccardJ = |A∩B| / |A∪B|—稳健集合型数据、标签、去重

马氏距离(Mahalanobis distance)额外要理解的两点:

  • Σ 是数据的协方差矩阵(第 1 章 1.5.1 节:d×d)。用 Σ⁻¹ 而不是 I,等价于先把数据白化(旋转到主成分方向并缩放到单位方差),再算欧氏距离。所以它自动处理了"特征之间存在相关性"和"量纲不同"这两件事。
  • 当 Σ = I 时退化为欧氏距离;当 Σ 是对角阵时退化为按标准差归一化后的欧氏距离。Σ⁻¹ 必须存在,即各特征不能有完全线性相关(否则 Σ 奇异)——这也是它不如欧氏距离常用的原因。

余弦相似度与欧氏距离的关键区别:余弦只看方向,x 与 2x 的余弦相似度为 1 而欧氏距离可能很大。所以在文本与嵌入场景中,余弦更贴合"语义相同、强度不同"的需求。

2.2.3 内积检索与欧氏检索的等价条件

这是向量检索(Faiss、Milvus、pgvector)最核心的一条工程结论。展开平方距离:

1
2
3
‖u − v‖² = (u − v)ᵀ(u − v)
         = uᵀu − uᵀv − vᵀu + vᵀv
         = ‖u‖² + ‖v‖² − 2uᵀv

现在假设所有向量都已归一化,即 ‖u‖ = ‖v‖ = 1:

1
‖u − v‖² = 2 − 2uᵀv  = 2 − 2cosθ          (归一化后 uᵀv = cosθ)

这个式子说明:在归一化前提下,“距离最近"与"内积最大”(也就是"余弦最大")是同一个排序。因为 ‖u − v‖² 是 uᵀv 的严格单调递减函数(−2 为斜率)。于是:

  • 用内积做检索的索引(如 Faiss 的 IndexFlatIP)与用欧氏距离做检索(IndexFlatL2)在归一化向量上返回相同的 top-k;
  • 反之,若用 IndexFlatIP 却没归一化,检索结果会被向量长度带偏——长向量天然内积大,会挤掉真正语义匹配的短向量。

自检技巧:把 θ = π(完全反向)代入得 2 − 2(−1) = 4,这是最大可能值;θ = 0 代入得 0,即两向量重合。干扰项 1 − cosθ 在 θ = π 时给出 2,与"单位向量最大距离为 2(直径)“矛盾;cos²θ 给出 1,更不对。

2.2.4 三个真题计算

欧氏距离(词嵌入)v_x = [2,3]ᵀ、v_y = [4,1]ᵀ:

1
2
先做差: v_x − v_y = [2−4, 3−1]ᵀ = [−2, 2]ᵀ
再取范数:‖v_x − v_y‖₂ = √((−2)² + 2²) = √8 ≈ 2.828

陷阱项 √13 的来源是把两个向量的分量分别平方相加(4 + 9 = 13),漏了"先做差"这一步。欧氏距离的定义主体是差向量,这是必须条件反射的一步。

余弦相似度 x = (1,0,1)、y = (1,1,0):

1
2
3
分子 xᵀy = 1·1 + 0·1 + 1·0 = 1
分母 ‖x‖·‖y‖ = √2 · √2 = 2
cosθ = 1/2 = 0.5

陷阱项 0.7071 的来源是分母只开了一次根号(把 ‖x‖‖y‖ 误算成 √2)。

RAG 语义检索 q = [0.8,1.2,1.6,2.0]、c = [1.0,0.8,1.4,1.8]:

1
2
3
4
5
内积 qᵀc = 0.8·1.0 + 1.2·0.8 + 1.6·1.4 + 2.0·1.8
        = 0.8 + 0.96 + 2.24 + 3.6 = 7.6
‖q‖ = √(0.64 + 1.44 + 2.56 + 4.00) = √8.64 ≈ 2.939
‖c‖ = √(1.00 + 0.64 + 1.96 + 3.24) = √6.84 ≈ 2.615
分母 ≈ 7.687  ⟹  cosθ ≈ 7.6 / 7.687 ≈ 0.989

0.989 ≥ 0.95,属于极高匹配。这道题可以不算完整就选对:观察两组分量 q ≈ 0.8 × [1.0, 1.5, 2.0, 2.5]、c ≈ 0.8 × [1.25, 1.0, 1.75, 2.25],两者各分量近似成比例、方向几乎重合,余弦必然接近 1。注意到"长度差异不影响余弦"这一点,就能在大约 10 秒内锁定答案。

2.3 正交与投影

2.3.1 正交基与正交矩阵

一组向量 {q₁, …, qₖ} 若满足 qᵢᵀqⱼ = 0(i ≠ j)且 ‖qᵢ‖ = 1,称为标准正交组(orthonormal set)。它的好处是"分量可以直接用内积读出”:若 x = Σcᵢqᵢ,两边与 qⱼ 做内积得 cⱼ = qⱼᵀx——无需解方程组。这是傅里叶级数、小波变换、QR 分解共有的便利。

方阵 Q 若各列构成标准正交组,称为正交矩阵(orthogonal matrix),等价条件:

1
QᵀQ = I  ⟺  Q⁻¹ = Qᵀ  ⟺  各列标准正交

正交矩阵的核心性质是保内积、保长度、保夹角:

1
(Qx)ᵀ(Qy) = xᵀQᵀQy = xᵀy        推得 ‖Qx‖ = ‖x‖,cos(Qx,Qy) = cos(x,y)

同时 det(Q) = ±1:+1 表示纯旋转,−1 表示反射(改变手性)。这个符号是判断题的高频考点。

2.3.2 投影矩阵的推导

问题:给定 A ∈ R^{m×n}(列满秩)和 b ∈ R^m,把 b 投影到 Col(A) 上,求投影向量 p 与投影矩阵 P。

推导。设 p = A x̂(因为 p 必须在列空间里,而列空间的元素恰好都能写成 Ax 的形式)。要求"投影"意味着误差向量 e = b − p 与列空间整体垂直,即 e ⊥ Col(A),等价于 e 与 A 的每一列都正交:

1
Aᵀ(b − Ax̂) = 0    ⟹   AᵀAx̂ = Aᵀb        (正规方程,normal equation)

A 列满秩时 AᵀA 可逆(理由见 2.4.3),于是

1
2
x̂ = (AᵀA)⁻¹Aᵀb
p = Ax̂ = A(AᵀA)⁻¹Aᵀ b

所以投影矩阵为

1
P = A(AᵀA)⁻¹Aᵀ

验证两条投影应有的性质:

1
2
幂等 P² = A(AᵀA)⁻¹Aᵀ·A(AᵀA)⁻¹Aᵀ = A(AᵀA)⁻¹(AᵀA)(AᵀA)⁻¹Aᵀ = A(AᵀA)⁻¹Aᵀ = P
对称 Pᵀ = (A(AᵀA)⁻¹Aᵀ)ᵀ = A((AᵀA)⁻¹)ᵀAᵀ = A(AᵀA)⁻¹Aᵀ = P

幂等的含义是"投影过一次再投影一次结果不变",对称来自 AᵀA 是对称矩阵。反过来,任何满足 P² = P、Pᵀ = P 的矩阵都是某个子空间上的正交投影矩阵——这两条性质是它的充要特征。

正交补方向的投影是 I − P(投影到 N(Aᵀ) 上),它同样幂等对称。注意 P 与 I − P 的秩之和为 m:r(P) = n、r(I−P) = m − n,正好对应第 1 章四个基本子空间把 R^m 劈成两半。

警告

P = (AᵀA)⁻¹Aᵀ 不是投影矩阵,它是公式的一部分。丢掉前面的 A 是最常见的抄写错误。另外,投影矩阵一般不可逆(除非 P = I),因为它在某个方向上把向量压成 0;看到有人求 P⁻¹ 就要警惕。

2.3.3 最小二乘的几何意义

当 Ax = b 无解时(b ∉ Col(A)),我们退而求其次,找一个 x̂ 使 ‖Ax − b‖ 最小。几何上,这就是把 b 正交投影到 Col(A) 上:投影点 p = Pb 是 Col(A) 中距离 b 最近的点。

1
2
3
4
5
6
7
8
                    b
                    ●
                   ╱|
                  ╱ |  e = b − Pb        (残差,垂直于 Col(A))
                 ╱  |
                ╱   |
   ────────────●────┴──────────────────▶  Col(A)  (平面)
              p = Pb                       (b 在列空间上的投影)

为什么投影点就是最近点(这一步是理解最小二乘的关键):设 p = Ax̂ 是投影,y = Ax 是列空间中任意另一点。由勾股定理(b − p ⊥ p − y,因为两者都在 R^m 而 b − p 垂直于整个 Col(A)):

1
‖b − y‖² = ‖(b − p) + (p − y)‖² = ‖b − p‖² + ‖p − y‖² ≥ ‖b − p‖²

等号当且仅当 y = p,即投影点是唯一最近点。这个证明模式在机器学习中反复出现:把误差分解成"垂直于模型类"与"落在模型类内"两部分,前者无法优化、后者可优化到 0。它也是偏差-方差分解(第 7 章)与岭回归(第 8 章)的几何原型。

把几何翻译成微积分:目标 J(x) = ‖Ax − b‖² = xᵀAᵀAx − 2bᵀAx + bᵀb,求梯度并令其为 0:

1
∂J/∂x = 2AᵀAx − 2Aᵀb = 0    ⟹   AᵀAx = Aᵀb

得到同一个正规方程。两条路径给出同一结果,不是巧合:几何上"残差垂直于列空间"与微积分上"梯度为零"是同一条一阶最优性条件。

2.3.4 数值上的注意:别真的求 AᵀA 的逆

x̂ = (AᵀA)⁻¹Aᵀb 是数学表达式,不是算法。两个原因:

  • 条件数被平方。AᵀA 的条件数是 A 的条件数的平方(cond(AᵀA) = cond(A)²),原本 cond(A) ≈ 10⁸ 的矩阵在这一步变成 10¹⁶,在双精度(约 16 位有效数字)下有效信息被抹平(第 5 章详解)。
  • 显式求逆更差。应该解方程 AᵀAx = Aᵀb 而不是算 (AᵀA)⁻¹:前者用 Cholesky 或 QR,运算量约 n³/3 且误差更小;后者多一步求逆,稳定性和速度都劣于前者。

工业做法:用 QR 分解 A = QR,则

1
AᵀAx̂ = Aᵀb  ⟹  RᵀQᵀQRx̂ = RᵀQᵀb  ⟹  RᵀRx̂ = RᵀQᵀb  ⟹  Rx̂ = Qᵀb

最后是一个上三角回代,无需形成 AᵀA,条件数不被平方。NumPy 中 np.linalg.lstsq 走的正是这条路。

2.4 QR 分解:Gram-Schmidt 与 Householder

2.4.1 经典 Gram-Schmidt 正交化

给定线性无关的列 a₁, …, aₙ,逐个构造标准正交组 q₁, …, qₙ:

1
2
3
4
5
6
q₁ = a₁ / ‖a₁‖
对 k = 2, …, n:
    r_ik = qᵢᵀa_k          (i = 1,…,k−1)  ← 把已得方向上的分量算出来
    v_k  = a_k − Σ_{i<k} r_ik qᵢ            ← 减掉这些分量,剩下的必与它们正交
    r_kk = ‖v_k‖
    q_k  = v_k / r_kk

为什么 v_k 与所有 qᵢ 正交:直接算 qⱼᵀv_k = qⱼᵀa_k − Σ_{i<k} r_ik (qⱼᵀqᵢ) = r_jk − r_jk = 0,用到 qⱼᵀqᵢ = δ_ij。把上面的系数收集成矩阵就得到 QR 分解:

1
A = QR ,   Q 是 m×n 列标准正交(QᵀQ = Iₙ),R 是 n×n 上三角

R 的对角元恰是每一步的 r_kk = ‖v_k‖。这一点很关键:|det(A)| = Π r_kk,且 r_kk = 0 当且仅当 a_k 可由前面的列线性表示——QR 顺带给出了"哪一列开始出现线性相关"的信息。

2.4.2 更稳定的做法:修正 Gram-Schmidt 与 Householder

经典 Gram-Schmidt(CGS)的不稳定来源:投影系数 r_ik = qᵢᵀa_k 全部用原始的 a_k 计算。在浮点运算中,qᵢ 本身带微小误差,而这个误差被逐项累加到 v_k 上,最终 q_k 与前面已算出的 qᵢ 之间出现可观的非正交性。当 a_k 与前几列几乎共线时,v_k 是两个几乎相等的量相减(灾难性抵消),相对误差被放大到无法接受。

修正 Gram-Schmidt(MGS):把"用原始 a_k 一次性算全部系数"改成"算一次、减一次":

1
2
3
4
v = a_k
for i = 1 … k−1:
    r_ik = qᵢᵀv      ← 用已经减过的 v,而不是原始 a_k
    v = v − r_ik qᵢ

这一个小改动改变了误差的传播方式:MGS 相当于在每次减法后"重新正交化一次",实测正交性误差约比 CGS 小一个数量级。代价是不能再并行(有数据依赖)。

Householder 反射:完全换成另一种思路——不做逐列相减,而是每一步用一个正交变换把当前列的下三角部分一次性清零。

1
H = I − 2vvᵀ ,   v 是单位向量

几何上 H 是"关于与 v 垂直的超平面做镜面反射"。三条性质可以一行推完:

性质推导结论
对称Hᵀ = I − 2(vvᵀ)ᵀ = I − 2vvᵀ = HHᵀ = H
正交且自逆HᵀH = H² = I − 4vvᵀ + 4v(vᵀv)vᵀ = I − 4vvᵀ + 4vvᵀ = IH⁻¹ = H
行列式特征值为一个 −1(v 方向翻转)与 n−1 个 +1det(H) = −1

把 H₁, …, Hₙ₋₁ 依次作用得到

1
H_{n−1} … H₁ A = R   ⟹   A = (H₁ … H_{n−1}) R = QR ,  Q = H₁H₂…H_{n−1}

为什么 Householder QR 比 Gram-Schmidt 数值更稳定(这是必须能讲清的一条):

  1. 误差不累积放大。每一步都是正交变换,而正交变换保持 2-范数不变(‖Hx‖ = ‖x‖),所以不会把误差放大。Gram-Schmidt 的逐列相减没有这个保范性质,误差沿列方向叠加。
  2. 显式构造零。Householder 通过一次变换把整列的下三角部分置零,不是"靠减法把小数减掉",因此避开了灾难性抵消。
  3. 正交性自动保持到机器精度。Q 是正交矩阵之积,理论上严格正交;实际误差只来自浮点乘法,量级约为 O(ε_machine)。而 CGS 的 QᵀQ 偏离 I 的程度可达 O(cond(A)·ε),在病态矩阵上完全失效。
  4. 成本相当。Householder QR 的运算量约 2mn² − 2n³/3,与 MGS 同阶,甚至更省(MGS 的显式内积次数更多)。

结论:LAPACK 的 geqrf 用的就是 Householder。生产代码里不要手写 CGS 求 QR,np.linalg.qr 即可。

提示

记忆锚点(也是真题的判分点):Householder 是"一次镜面反射"。反射改变手性 ⟹ det = −1;旋转保持手性 ⟹ det = +1。看到"Householder 行列式为 +1“就可以直接判错,不用推导。另外它既对称又正交(因此 H² = I),这两条常被当成互斥的性质,其实在本例中同时成立。

2.5 正交变换在深度学习中的两处影子

2.5.1 注意力里的缩放因子 1/√d

注意力分数为 s = qᵀk / √d(d 是每个头的维度)。为什么要除以 √d?设 q、k 各分量独立同分布、均值 0、方差 1,则

1
2
E[qᵀk] = Σ E[qᵢ]E[kᵢ] = 0
Var(qᵀk) = Σ Var(qᵢkᵢ) = Σ E[qᵢ²]E[kᵢ²] = d

所以 qᵀk 的标准差是 √d,随维度增长。若不缩放,d = 128 时分数会落在 ±11 量级,Softmax 进入饱和区(一端接近 1、其余接近 0),梯度近乎消失。除以 √d 后方差归一到 1,Softmax 保持在梯度有效的区间。这就是"保范/归一方差"思想在架构设计中的直接应用(第 15 章详解)。

2.5.2 嵌入归一化与检索

把嵌入向量归一化到单位球面(x ← x/‖x‖)有三个收益:其一,余弦与内积统一(2.2.3 节);其二,内积被限制在 [−1, 1],分数可比、便于设阈值(RAG 里常用的 0.85、0.95 分级就依赖这个有界性);其三,避免长文本嵌入因范数大而在内积检索中天然占优。代价是丢失了"强度"信息——在需要区分"相关性强度"而非仅"方向"的任务里,不应归一化。

2.5.3 浮点容差比较

浮点数的加减乘除会引入舍入误差,因此两个数学上相等的量在浮点下可能相差 1e−15。所有涉及"判等"“比大小"的逻辑都必须用容差:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import math

EPS = 1e-9  # 容差阈值:比典型累积误差大几个数量级,又远小于业务上"有意义"的差异

def nearly_equal(a: float, b: float, eps: float = EPS) -> bool:
    """判断两个浮点数是否"相等",用于替代 a == b。"""
    # 直接用 == 比较:数学上相等的量在浮点下可能差 1e-15,导致分支走错
    return abs(a - b) <= eps * max(1.0, abs(a), abs(b))  # 相对容差,兼顾大数量级

def is_strictly_better(new_cost: float, best_cost: float, eps: float = EPS) -> bool:
    """判断 new_cost 是否"严格优于" best_cost,用于同分决胜。"""
    # 关键:必须减去 EPS 再比较。否则同分时谁胜出由末位误差随机决定,
    # 与题面"同分取序号靠前"的规则对不上,会随机挂掉若干测试用例
    return new_cost < best_cost - eps

这两段代码来自真实的机考场景:k-medoids 选址与距离排序题里,等距样本的取舍必须按"序号靠前"处理,而 d1 < d2 在 d1 与 d2 数学相等时会因末位误差给出随机答案。记住一条纪律:只要出现"比较两个浮点结果”,就必须配一个 EPS(第 26 章会系统展开)。

本章小结

关键结论一句话表述常见错误
内积公理对称、双线性、正定;三者缺一都会破坏后续结论忘记正定性,导致伪度量问题
柯西-施瓦茨|uᵀv| ≤ ‖u‖‖v‖,证明靠构造非负二次型看判别式以为对任意"相似度"都成立
距离公理非负、对称、三角不等式;余弦相似度本身不是距离直接拿 cosθ 做 K-Means 的距离
距离-余弦换算‖u−v‖² = ‖u‖²+‖v‖²−2uᵀv;单位向量时 = 2−2cosθ忘记先做差;分母少开一次根号
归一化等价性归一化后内积排序 ≡ 欧氏距离排序 ≡ 余弦排序未归一化就用内积索引,被范数带偏
投影矩阵P = A(AᵀA)⁻¹Aᵀ,满足 P² = P、Pᵀ = P记成 (AᵀA)⁻¹Aᵀ;试图求 P⁻¹
最小二乘残差垂直于列空间,与"梯度为零"是同一条最优性条件把正规方程当算法,显式求逆
QRCGS → MGS → Householder,稳定性递增手写 CGS 用于病态矩阵
Householder对称、正交、自逆、det = −1记成 det = +1(那是旋转)
浮点比较任何浮点判等/判优都要配 EPS用 == 或用 < 直接比较代价

自测题

Q1(计算)设 u = [1, 2, 2]ᵀ、v = [2, 1, −2]ᵀ。求 uᵀv、‖u‖、‖v‖、cosθ、‖u − v‖,并验证 ‖u−v‖² = ‖u‖² + ‖v‖² − 2uᵀv。

答案与解析
1
2
3
4
5
uᵀv = 1·2 + 2·1 + 2·(−2) = 2 + 2 − 4 = 0        ⟹ u ⊥ v
‖u‖ = √(1 + 4 + 4) = 3
‖v‖ = √(4 + 1 + 4) = 3
cosθ = 0 / (3·3) = 0        ⟹ θ = 90°,与 uᵀv = 0 一致
u − v = [−1, 1, 4]ᵀ ,  ‖u−v‖ = √(1 + 1 + 16) = √18 ≈ 4.243

验证恒等式:右端 = 9 + 9 − 2·0 = 18 = ‖u−v‖²,成立。

由于 u ⊥ v 且等长,这恰好是直角三角形的情形:‖u−v‖² = ‖u‖² + ‖v‖²,与勾股定理一致。这个恒等式在 u ⊥ v 时退化为勾股定理,也说明它本质上是"余弦定理"的向量形式。

Q2(推导)设 A ∈ R^{m×n} 列满秩,P = A(AᵀA)⁻¹Aᵀ。证明 r(P) = n 且 r(I − P) = m − n。

答案与解析

P 的像空间是 Col(P)。对任意 y,Py = A[(AᵀA)⁻¹Aᵀy] 是 A 乘以某个 n 维向量,所以 Col(P) ⊆ Col(A)。反过来,对任意 x ∈ R^n,取 y = Ax,则 Py = A(AᵀA)⁻¹AᵀAx = Ax。所以 Col(A) ⊆ Col(P)。两者合起来 Col(P) = Col(A),于是 r(P) = r(A) = n(列满秩)。

I − P 是投影到 N(Aᵀ) 上的矩阵(因为 P 投影到 Col(A),而 R^m = Col(A) ⊕ N(Aᵀ),见第 1 章 1.3.2)。所以 r(I − P) = dim Col(I−P) = dim N(Aᵀ) = m − r(A) = m − n。

也可以纯代数验证:(I−P)² = I − 2P + P² = I − P,且 P(I−P) = P − P² = 0,说明两者的像空间只交于零向量;再由维数公式 r(P) + r(I−P) = m(因为 P + (I−P) = I,且两者像空间互补)得 r(I−P) = m − n。

工程含义:P 把 b 分解成"可被 A 解释的部分”(Pb,落在列空间)与"无法解释的残差"((I−P)b,落在左零空间)。在梯度矩阵的例子里(第 1 章 1.3.4),前者是拟合方向,后者就是不可达的残差方向。

Q3(辨析)以下四个论断,哪些正确?

A. 若 ‖u − v‖² = 2 − 2cosθ,则 u、v 必为单位向量 B. Jaccard 系数可以作为距离使用,因为它满足三角不等式 C. A 列满秩时 AᵀA 必可逆 D. 未归一化时,用内积索引和用 L2 距离索引会返回相同的 top-k

答案与解析

只有 C 正确。

A:说反了。2 − 2cosθ 这个形式只在 u、v 都是单位向量时成立,它是一个前提,不是结论。一般情形是 ‖u−v‖² = ‖u‖² + ‖v‖² − 2uᵀv,只有当 ‖u‖ = ‖v‖ = 1 时才化简为 2 − 2cosθ。真题考的就是这个换算方向。

B:错。J(A,B) = |A∩B| / |A∪B| 是相似度(越大越像),本身不是距离。转成 d = 1 − J 后满足三角不等式,可以作为度量;但 J 本身满足的是 J(A,C) ≥ J(A,B)·J(B,C) 这类不等式,方向与三角不等式相反。要区分"相似度"与"距离"两类符号。

C:正确。证明:设 AᵀAx = 0,则 xᵀAᵀAx = 0,即 ‖Ax‖² = 0,故 Ax = 0;由 A 列满秩知 N(A) = {0},所以 x = 0。齐次方程只有零解 ⟹ AᵀA 可逆(第 1 章 1.4.1 的等价条件 6)。这就是投影矩阵公式里 (AᵀA)⁻¹ 存在的依据。注意反过来不成立:A 行满秩时 AᵀA 一定不可逆(AᵀA 是 n×n 而 r(AᵀA) = r(A) = m < n)。

D:错。这正是 2.2.3 节结论的反面:只有归一化之后两者排序才等价。未归一化时,‖u−v‖² = ‖u‖² + ‖v‖² − 2uᵀv 中多了 ‖v‖² 这一项依赖于候选向量自身的长度,内积最大的候选不一定是距离最近的候选。

Q4(计算)用 Gram-Schmidt 对 a₁ = [1,1,0]ᵀ、a₂ = [1,0,1]ᵀ 做正交化,写出 Q 与 R,并验证 A = QR。

答案与解析
1
2
3
4
5
6
7
8
9
q₁:‖a₁‖ = √2
    q₁ = [1,1,0]ᵀ / √2 = [0.7071, 0.7071, 0]ᵀ

k = 2:
    r₁₂ = q₁ᵀa₂ = (1·1 + 1·0 + 0·1)/√2 = 1/√2 ≈ 0.7071
    v₂ = a₂ − r₁₂q₁ = [1,0,1]ᵀ − (1/√2)·[1,1,0]ᵀ/√2
       = [1,0,1]ᵀ − [1/2, 1/2, 0]ᵀ = [1/2, −1/2, 1]ᵀ
    r₂₂ = ‖v₂‖ = √(1/4 + 1/4 + 1) = √(3/2) ≈ 1.2247
    q₂ = [1/2, −1/2, 1]ᵀ / √(3/2) = [0.4082, −0.4082, 0.8165]ᵀ

于是

1
2
3
Q = [ 0.7071   0.4082 ]        R = [ √2      1/√2   ] = [ 1.4142  0.7071 ]
    [ 0.7071  −0.4082 ]            [  0     √(3/2) ]   [ 0       1.2247 ]
    [  0       0.8165 ]

验证 A = QR。按列展开 aⱼ = Σ_i qᵢ r_ij(R 的列对应 A 的列,R 的行对应 q 的下标,这是最容易搞混的一处):

1
2
3
4
5
第 1 列:r₁₁q₁ = 1.4142 × [0.7071, 0.7071, 0]ᵀ = [1, 1, 0]ᵀ = a₁ ✓
第 2 列:r₁₂q₁ + r₂₂q₂
      = 0.7071 × [0.7071, 0.7071, 0]ᵀ + 1.2247 × [0.4082, −0.4082, 0.8165]ᵀ
      = [0.5, 0.5, 0]ᵀ + [0.5, −0.5, 1.0]ᵀ
      = [1, 0, 1]ᵀ = a₂ ✓

再验证正交性:q₁ᵀq₂ = 0.7071×0.4082 + 0.7071×(−0.4082) + 0×0.8165 = 0 ✓。

两个应记住的结构性事实:

  • R 的第 i 行第 k 列元素(i < k)等于 qᵢᵀa_k 或 qᵢᵀv_k,这正是 CGS/MGS 的定义差别所在(2.4.2 节)。
  • 对 3×2 矩阵,r₁₁·r₂₂ = √2·√(3/2) = √3 ≈ 1.732,等于两个列向量张成的平行四边形面积。一般地 Π r_kk = |det|(方阵情形),所以 R 的对角元能直接暴露线性相关程度:某个 r_kk 接近 0,说明对应的列几乎能被前面的列表示。

Q5(实现)写一个函数,在一组候选嵌入中找出与查询向量最相似的 k 个,要求同时支持余弦与欧氏距离并保证两者在归一化下结果一致。

答案与解析
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import numpy as np

EPS = 1e-9

def topk_similar(query: np.ndarray, cands: np.ndarray, k: int,
                 metric: str = "cosine", normalize: bool = True) -> list:
    """返回与 query 最相似的前 k 个候选的下标(按相似度降序)。

    query: (d,)   cands: (n, d)
    metric: "cosine" 或 "l2"
    normalize: 是否先归一化。归一化后两种 metric 返回完全相同的结果。
    """
    query = np.asarray(query, dtype=np.float64)
    cands = np.asarray(cands, dtype=np.float64)

    if normalize:
        # 归一化把余弦与内积统一,也让分数落在 [-1,1],便于设阈值
        query = query / max(np.linalg.norm(query), EPS)  # 加 EPS 防零向量除零
        norms = np.linalg.norm(cands, axis=1, keepdims=True)
        cands = cands / np.maximum(norms, EPS)

    if metric == "cosine":
        scores = cands @ query          # 归一化后内积即余弦
    elif metric == "l2":
        # ‖u−v‖² = 2 − 2uᵀv(归一化下),但这里直接算距离更通用
        scores = -np.linalg.norm(cands - query, axis=1)  # 取负号以统一为"越大越好"
    else:
        raise ValueError(f"unsupported metric: {metric}")

    # argsort 默认升序,取负号后降序;用 stable sort 保证同分时下标小的在前,
    # 这是"同分取序号靠前"这类题面规则的通用写法
    order = np.argsort(-scores, kind="stable")
    return order[:k].tolist()

关键点说明:

  1. 用 scores = cands @ query 而不是逐条算余弦,把 n 次向量化归约为一次矩阵乘法,正是向量库的基本操作(GEMM)。这也是为什么检索性能取决于矩阵乘法的优化程度(第 19 章)。
  2. np.maximum(norms, EPS) 防止零向量除零。零向量没有方向,余弦无定义;工程上要么跳过、要么返回 0,绝不能让它产生 NaN 污染整批结果。
  3. kind="stable" 保证同分时的确定性顺序。这与 2.5.3 节的 EPS 纪律是同一类问题:浮点环境下"顺序不确定"会让测试用例随机失败。
  4. 归一化后两种 metric 结果完全一致,这正是 2.2.3 节恒等式的可执行验证——可以在单元测试里断言两者输出相同。

与真题的关联

真题考查角度对应本章小节
【08-05 选择 Q4】v_x=[2,3]ᵀ、v_y=[4,1]ᵀ 的欧氏距离必须先做差再取范数;陷阱项 √13 漏了做差2.2.4
【08-05 选择 Q8】x=(1,0,1)、y=(1,1,0) 的余弦分母是两个范数之积;陷阱项只开一次根号2.2.2、2.2.4
【09-04 选择 Q2】单位向量下 ‖u−v‖₂² 与 cosθ2 − 2cosθ;代入 θ = π 自检得 42.2.3
【08-05 选择 Q10】RAG 语义检索相似度未归一化的余弦;分量近似成比例时可预判接近 12.2.2、2.2.4
【08-05 选择 Q16】Householder 变换的性质对称、正交、比 GS 稳定;det = −1 而非 +12.4.1、2.4.2
【09-04 编程题 1 / 09-11 编程题 2】浮点容差EPS 判等与"严格更优"的比较纪律2.5.3

延伸阅读

  • Gilbert Strang, Introduction to Linear Algebra, 第 5 版:第 4 章(正交性与最小二乘)、第 4.4 节(QR 分解)——本章 2.3、2.4 节的推导框架来自这里。
  • Gene Golub & Charles Van Loan, Matrix Computations, 第 4 版:第 5 章(正交化与最小二乘)——想看清 CGS/MGS/Householder 三者的误差界差异,这本是最权威的来源。
  • Lloyd N. Trefethen & David Bau, Numerical Linear Algebra:第 7~8 讲(QR 分解)、第 15 讲(条件数)——语言极简,专讲"为什么数值上会失败"。
  • 3Blue1Brown, Essence of Linear Algebra 第 9 集(点积与对偶性)——把"内积为何能衡量相似"的几何直觉讲得最清楚。