ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

向量与矩阵范数详解:从L1/L2到谱范数的机器学习应用指南

向量与矩阵范数详解:从L1/L2到谱范数的机器学习应用指南

1. 为什么我们要关心“范数”这个数学工具

第一次接触“范数”这个词,很多人会觉得它离实际开发很远,像是数学系课本里才会出现的抽象概念。但如果你做过机器学习、数值计算、信号处理,甚至只是用过正则化去调一个过拟合的模型,那你其实已经在和范数打交道了。范数本质上就是“衡量一个东西有多大”的尺子,只不过这把尺子量的不是长度或重量,而是向量和矩阵的“大小”。

我在实际项目里踩过的最典型的坑,就是早期做特征缩放时,随手用了默认的欧氏距离去度量样本差异,结果在高维稀疏数据上效果极差,后来换成L1范数才把问题解决。这件事让我意识到,选错范数,模型的表现可能天差地别,而很多人根本没意识到自己正在做“范数选择”这件事。

这篇文章想做的事情很明确:把向量范数和矩阵范数这两块内容讲透,从定义、几何直觉、计算方式,到它们在正则化、数值稳定性、条件数分析中的实际用途,全部拆开讲。适合有一定线性代数基础、但没系统梳理过范数的开发者,也适合正在做机器学习或数值计算、想搞清楚“为什么这里要用L2而不是L1”的从业者。读完你至少能做到三件事:第一,看到任意一个范数定义能快速判断它属于哪一类;第二,知道在什么场景下该选哪种范数;第三,能手算常见的向量范数和矩阵范数,并理解背后的计算逻辑。

2. 向量范数:从“长度”到“一把可定制的尺子”

2.1 向量范数的三条公理到底在约束什么

向量范数说穿了就是一个函数,输入一个向量,输出一个非负实数。但这个函数不是随便定义的,它必须满足三条公理,这三条公理决定了它能不能被称为“范数”。

第一条是非负性:对任意向量 x,都有 ||x|| ≥ 0,且 ||x|| = 0 当且仅当 x 是零向量。这条看起来废话,但它排除了那些“把非零向量映射成0”的奇怪函数。第二条是齐次性:||αx|| = |α| · ||x||,也就是说你把向量放大 α 倍,范数也放大 |α| 倍。第三条是三角不等式:||x + y|| ≤ ||x|| + ||y||,这条最直观,两点之间直线最短,绕路不会更近。

我习惯用一个生活类比来解释这三条:把范数想象成“快递计费规则”。非负性是说运费不能是负数,且只有空包裹才免运费;齐次性是说包裹重量翻倍,运费也翻倍;三角不等式是说,把两个包裹合并成一个寄,运费不会比分开寄更贵。这个类比虽然不严谨,但能帮新手快速建立直觉。

注意:很多初学者会把“范数”和“距离”混为一谈。范数是度量单个向量的大小,距离是度量两个向量之间的差异。距离通常定义为 ||x - y||,它是基于范数导出来的,但两者概念不同。

2.2 最常用的三种向量范数:L1、L2、L∞

实际工作中,99%的场景只会用到三种向量范数,我把它们的定义、计算方式和典型用途整理成一张表,方便你随时对照。

范数类型定义式通俗叫法典型用途
L1范数Σ|xᵢ|曼哈顿范数、出租车范数稀疏特征选择、Lasso正则化
L2范数√(Σxᵢ²)欧氏范数距离度量、Ridge正则化
L∞范数max|xᵢ|最大范数、切比雪夫范数最坏情况分析、鲁棒优化

L1范数就是把所有分量的绝对值加起来。它的几何形状是一个菱形(二维情况下),这也是为什么L1正则化容易产生稀疏解——菱形的尖角正好落在坐标轴上,最优解很容易取到某个分量为0的点。

L2范数就是我们中学学的“向量长度”,先平方和再开根号。它的几何形状是圆形,没有尖角,所以L2正则化会让参数整体变小但很少直接变成0。

L∞范数取所有分量绝对值的最大值。它衡量的是“最突出的那个分量有多大”,在需要控制最坏情况的场景里特别有用。

2.3 手算示例:三种范数的实际计算过程

光看定义容易飘,我们拿一个具体向量来算一遍。假设 x = [3, -4, 0, 2]。

L1范数:|3| + |-4| + |0| + |2| = 3 + 4 + 0 + 2 =9。

L2范数:√(3² + (-4)² + 0² + 2²) = √(9 + 16 + 0 + 4) = √29 ≈5.385。

L∞范数:max(|3|, |-4|, |0|, |2|) = max(3, 4, 0, 2) =4。

这三个结果告诉我们同一件事的不同侧面:L1说“总共有9个单位的量”,L2说“直线距离约5.4”,L∞说“最大的单个分量是4”。你会发现 L∞ ≤ L2 ≤ L1,这不是巧合,而是一个普遍成立的不等式关系,后面讲范数等价性时会详细说。

2.4 p-范数:把三种范数统一成一个公式

L1、L2、L∞其实都是p-范数的特例。p-范数的定义是:

||x||_p = (Σ|xᵢ|^p)^(1/p)

当 p=1 时就是L1范数,p=2 时就是L2范数。那L∞怎么来的?当 p 趋向无穷大时,最大的那个分量会主导整个求和,其他分量的贡献被相对稀释,极限情况下就变成了取最大值。这个推导过程我建议你亲手推一遍,能极大加深理解。

p-范数里有个反直觉的点:p 越小,范数的值越大。比如同一个向量,L1范数总是大于等于L2范数,L2又大于等于L∞。这背后的原因是,p 越小,对大分量的“惩罚”越轻,对小分量的“计入”越重,所以总和更大。

实操心得:在特征工程里做归一化时,如果你不确定选哪个范数,先用L2做标准化(除以L2范数),这是最稳妥的默认选择。只有当你有明确理由相信特征应该稀疏时,才切换到L1。

3. 矩阵范数:当“大小”变成一个更复杂的问题

3.1 矩阵范数和向量范数的本质区别

向量范数好理解,因为向量就是一串数,量它的大小很直观。但矩阵是一个二维数组,它的“大小”到底指什么?是元素的最大值?是所有元素的总和?还是它作为一个线性变换的“放大能力”?不同的理解方式,导出了不同的矩阵范数。

这是很多人第一次学矩阵范数时最困惑的地方。我当初也是花了很久才想明白:矩阵范数分两大类,一类是元素级范数,直接把矩阵拉平当成向量来算;另一类是诱导范数(也叫算子范数),衡量的是矩阵作为线性变换时对向量长度的最大放大倍数。这两类范数的用途完全不同,混用会出大问题。

3.2 元素级范数:Frobenius范数和它的计算

最常用的元素级矩阵范数是Frobenius范数,记作 ||A||_F。它的定义非常直接:

||A||_F = √(ΣᵢΣⱼ aᵢⱼ²)

就是把矩阵所有元素平方求和再开根号,本质上就是把矩阵拉成一个长向量,然后算L2范数。

举个例子,A = [[1, 2], [3, 4]],那么 ||A||_F = √(1 + 4 + 9 + 16) = √30 ≈ 5.477。

Frobenius范数在机器学习里出现频率极高,比如矩阵分解的损失函数、神经网络的权重衰减,很多时候用的就是它。它的优点是计算简单、可导性好,缺点是它忽略了矩阵的结构信息——两个元素完全相同但排列不同的矩阵,Frobenius范数是一样的。

3.3 诱导范数:矩阵作为“放大器”的度量

诱导范数的定义稍微绕一点。给定一个向量范数 ||·||,矩阵A的诱导范数定义为:

||A|| = max{ ||Ax|| / ||x|| : x ≠ 0 }

翻译成人话就是:在所有非零向量里,找出被A放大倍数最大的那个,这个最大倍数就是A的诱导范数。它衡量的是矩阵作为线性变换的“最大放大能力”。

常用的诱导范数有三种,对应向量的L1、L2、L∞范数:

诱导范数计算方法特点
1-范数列和的最大值最大列绝对值和
2-范数最大奇异值计算复杂但理论重要
∞-范数行和的最大值最大行绝对值和

1-范数和∞-范数的计算有个好记的口诀:1-范数看列,∞-范数看行。因为1横过来像列,∞竖着像行(这个记忆法有点牵强,但确实好用)。

2-范数最特殊,它等于矩阵的最大奇异值,计算需要做奇异值分解,成本较高,但在理论分析中地位最高。

3.4 手算示例:三种诱导范数的完整计算

还是用 A = [[1, 2], [3, 4]] 来算。

1-范数:先算每列的绝对值和。第一列 |1| + |3| = 4,第二列 |2| + |4| = 6。取最大值,||A||_1 =6。

∞-范数:先算每行的绝对值和。第一行 |1| + |2| = 3,第二行 |3| + |4| = 7。取最大值,||A||_∞ =7。

2-范数:需要算 AᵀA 的最大特征值的平方根。AᵀA = [[10, 14], [14, 20]],特征值是 15 ± √221,最大特征值约 29.866,开根号约5.465。

这三个结果差异很大,说明它们度量的确实是不同的东西。1-范数关注“哪一列的总量最大”,∞-范数关注“哪一行的总量最大”,2-范数关注“最大的放大倍数”。

注意:诱导范数必须和某个向量范数绑定才有意义。说“矩阵的2-范数”时,默认是指由向量L2范数诱导出来的。如果你用的是其他向量范数,结果会不同。

4. 范数等价性:为什么换个范数结果不会崩

4.1 有限维空间里所有范数都等价

这是范数理论里最重要的结论之一:在有限维向量空间里,任意两个范数都是等价的。等价的意思是,存在两个正常数 c 和 C,使得对任意向量 x,都有:

c·||x||_a ≤ ||x||_b ≤ C·||x||_a

这个结论的实际意义是:如果你用L1范数判断一个向量“很大”,那用L2范数判断它也不会“很小”。它们对“大小”的判断在定性上是一致的,只是定量上有常数倍的差异。

我当初学到这里时有个疑问:既然都等价,那还分这么多种范数干嘛?后来才明白,等价是理论保证,但常数 c 和 C 可能相差极大。在高维空间里,L1和L2的比值可以差到 √n 倍,n 是维度。当 n 是几万维时,这个差异就完全不能忽略了。

4.2 等价常数的具体推导

以L1和L2为例,我们可以推导出它们之间的等价关系。

一方面,||x||_2 ≤ ||x||_1,因为平方和小于等于和的平方(当所有分量非负时取等号)。

另一方面,||x||_1 ≤ √n · ||x||_2,这个用柯西-施瓦茨不等式可以证明。

所以 √(1/n) · ||x||_1 ≤ ||x||_2 ≤ ||x||_1,等价常数是 1 和 √n。

这个推导过程建议你亲手写一遍,它不仅能帮你记住结论,还能让你理解为什么高维数据里L1和L2的行为差异那么大。

4.3 等价性在数值计算中的实际影响

等价性在理论上是好事,但在实践中要小心。比如你做梯度下降,用L2范数做梯度裁剪,阈值设为1.0。如果换成L1范数做裁剪,同样的阈值下,实际裁剪力度会完全不同,因为L1范数通常比L2范数大。

我在一个项目里就遇到过这个问题:团队里两个人分别用L1和L2做梯度裁剪,结果模型收敛速度差了一倍。排查了半天才发现是范数选择不一致导致的。统一范数选择,是团队协作里容易被忽视但非常重要的规范。

实操心得:在深度学习框架里,torch.nn.utils.clip_grad_norm_默认用的是L2范数。如果你要换成L1,记得同步调整阈值,否则裁剪力度会失控。

5. 范数在机器学习中的核心应用

5.1 L1和L2正则化:稀疏性与平滑性的取舍

正则化是范数最广为人知的应用。L1正则化(Lasso)在损失函数里加上 λ·||w||_1,L2正则化(Ridge)加上 λ·||w||_2²。

为什么L1产生稀疏解,L2不产生?几何解释是:L1的约束区域是菱形,顶点在坐标轴上,损失函数的等高线很容易和菱形在顶点处相切,导致某些权重正好为0。L2的约束区域是圆形,没有尖角,相切点一般不会正好落在坐标轴上。

从优化角度看,L1在0点不可导,次梯度包含0,所以最优解可以停在0点。L2在0点可导且导数为0,但损失函数的梯度在0点通常不为0,所以最优解很少正好是0。

实际选择时,如果你需要特征选择、想要一个稀疏模型,用L1。如果你只是想让模型别过拟合、权重别太大,用L2。两者也可以结合,就是弹性网络(Elastic Net),同时加L1和L2。

5.2 谱范数在对抗样本和GAN中的应用

谱范数就是矩阵的2-范数,等于最大奇异值。它在深度学习里有个重要应用:谱归一化。

在GAN训练中,判别器的 Lipschitz 常数如果太大,训练会不稳定。谱归一化通过把每层的权重矩阵除以它的谱范数,强制让每层的 Lipschitz 常数不超过1,从而稳定训练。这个技巧在WGAN-GP等模型里是标配。

计算谱范数需要做奇异值分解,成本较高。实践中常用幂迭代法近似,迭代几次就能得到不错的估计。这个近似方法我在多个项目里用过,收敛速度很快,通常5到10次迭代就够了。

5.3 核范数在矩阵补全中的角色

核范数是矩阵奇异值之和,记作 ||A||_*。它是矩阵秩的凸松弛,也就是说,最小化核范数近似于最小化矩阵的秩。

在推荐系统里,用户-物品评分矩阵通常是低秩的(因为用户偏好可以用少数几个隐因子解释)。矩阵补全问题就是已知部分评分,预测缺失评分。直接最小化秩是NP难的,但最小化核范数是凸优化问题,可以高效求解。

这个应用我做过一个完整项目,用核范数正则化的矩阵分解做电影推荐,效果比单纯的Frobenius范数正则化好不少,尤其是在评分数据很稀疏的时候。

6. 常见问题与排查技巧实录

6.1 范数计算中的典型错误

问题现象可能原因排查方法
范数值异常大忘记开根号或用了错误的p值检查公式,确认p值
矩阵范数和预期不符混淆了元素级范数和诱导范数确认用的是Frobenius还是诱导范数
正则化后模型全零L1正则化系数λ过大减小λ,或改用L2
梯度裁剪后不收敛范数类型和阈值不匹配统一范数类型,重新调阈值
谱范数计算太慢直接做SVD改用幂迭代近似

6.2 数值稳定性问题

计算L2范数时,如果向量分量很大,平方后可能溢出。稳妥的做法是先除以最大分量,算完再乘回去。这个技巧在数值计算库里有标准实现,但如果你手写代码,很容易忽略。

计算Frobenius范数时,如果矩阵很大,直接平方求和也可能溢出。可以分块计算,或者用缩放技巧。我在处理大规模矩阵时,一般会用numpy.linalg.norm,它内部做了数值优化,比手写循环稳得多。

6.3 范数选择的决策清单

面对一个具体问题,怎么选范数?我整理了一个简单的决策流程:

  1. 如果你需要稀疏解,选L1。
  2. 如果你需要平滑解、防止过拟合,选L2。
  3. 如果你关心最坏情况,选L∞。
  4. 如果你处理的是矩阵且关心整体能量,选Frobenius。
  5. 如果你关心矩阵作为变换的放大能力,选诱导2-范数(谱范数)。
  6. 如果你要最小化矩阵的秩,选核范数。

这个清单不是绝对的,但能覆盖大部分场景。遇到特殊情况,再具体分析。

最后分享一个小技巧:在调试正则化相关问题时,先把λ设为0,确认模型能正常过拟合,再逐步增大λ,观察权重范数的变化。这样能快速定位是正则化的问题还是模型本身的问题。

7. 从理论到代码:范数计算的实操演示

7.1 用NumPy计算各种范数

理论讲完了,落到代码上其实很简单。NumPy提供了numpy.linalg.norm函数,通过ord参数指定范数类型。

import numpy as np x = np.array([3, -4, 0, 2]) # 向量L1范数 l1 = np.linalg.norm(x, ord=1) # 9.0 # 向量L2范数 l2 = np.linalg.norm(x, ord=2) # 5.385 # 向量L∞范数 linf = np.linalg.norm(x, ord=np.inf) # 4.0 A = np.array([[1, 2], [3, 4]]) # Frobenius范数 fro = np.linalg.norm(A, ord='fro') # 5.477 # 诱导1-范数 norm1 = np.linalg.norm(A, ord=1) # 6.0 # 诱导∞-范数 norm_inf = np.linalg.norm(A, ord=np.inf) # 7.0 # 谱范数(诱导2-范数) spec = np.linalg.norm(A, ord=2) # 5.465

这段代码我建议你亲手跑一遍,对照前面的手算结果,确认理解无误。特别注意ord参数对矩阵和向量的行为不同:对向量ord=2是L2范数,对矩阵ord=2是谱范数。

7.2 手动实现范数计算

如果你想深入理解,可以自己实现一遍。下面是L1、L2、L∞的手动实现:

def l1_norm(x): return sum(abs(xi) for xi in x) def l2_norm(x): return sum(xi**2 for xi in x) ** 0.5 def linf_norm(x): return max(abs(xi) for xi in x)

手动实现的好处是你能完全掌控计算过程,也方便加一些特殊处理,比如数值稳定性优化。但在生产环境里,还是用库函数更稳妥,因为库函数经过了充分测试和优化。

7.3 范数在梯度裁剪中的实际代码

梯度裁剪是范数最常见的工程应用之一。下面是一个完整的例子:

import torch def clip_gradients(model, max_norm=1.0, norm_type=2): total_norm = 0.0 for p in model.parameters(): if p.grad is not None: param_norm = p.grad.data.norm(norm_type) total_norm += param_norm.item() ** norm_type total_norm = total_norm ** (1.0 / norm_type) clip_coef = max_norm / (total_norm + 1e-6) if clip_coef < 1: for p in model.parameters(): if p.grad is not None: p.grad.data.mul_(clip_coef) return total_norm

这段代码的关键点是1e-6这个极小量,防止除以0。另外clip_coef < 1的判断确保只在梯度超过阈值时才裁剪,不超过时不动。这个细节很多人会忽略,导致梯度被意外放大。

注意:PyTorch自带的clip_grad_norm_已经做了这些处理,直接用就行。手写版本主要用于理解原理,或者需要自定义范数类型时使用。

8. 范数选择的经验法则与避坑指南

8.1 高维稀疏数据优先考虑L1

在处理文本、推荐系统、基因数据这类高维稀疏数据时,L1范数往往比L2更合适。原因是高维空间里,L2范数会被大量小分量主导,而L1范数对大分量更敏感,能更好地捕捉稀疏结构。

我做过一个文本分类项目,特征维度几万维,用L2正则化效果一般,换成L1后不仅准确率提升,模型还变得非常稀疏,推理速度提升了好几倍。这个经验在稀疏场景里基本通用。

8.2 数值计算中优先用Frobenius范数

如果你只是想知道矩阵的“整体大小”,Frobenius范数是最安全的选择。它计算简单、可导性好、数值稳定,而且和矩阵的迹有直接关系:||A||_F² = trace(AᵀA)。这个关系在推导梯度时特别有用。

诱导范数虽然理论意义更强,但计算成本高,而且不可导(在最大值点处),不适合直接放进损失函数里优化。所以实践中,损失函数里的矩阵范数基本都是Frobenius。

8.3 谱范数用于稳定性分析

当你需要分析模型的稳定性、控制Lipschitz常数时,谱范数是不可替代的工具。它直接给出了矩阵对向量的最大放大倍数,是判断“梯度会不会爆炸”的关键指标。

在RNN和GAN的训练中,监控谱范数的变化能帮你提前发现训练不稳定的苗头。如果谱范数持续增大,说明梯度可能会爆炸,需要及时调整学习率或加正则化。

8.4 核范数用于低秩约束

当你需要矩阵是低秩的,但又不想直接优化秩(因为非凸),核范数是最佳选择。它是秩的凸包络,最小化核范数等价于在凸松弛意义下最小化秩。

矩阵补全、鲁棒PCA、子空间聚类这些任务里,核范数都是核心工具。不过核范数的计算需要SVD,大规模矩阵上成本较高,实践中常用随机化SVD或增量SVD来加速。

9. 范数背后的几何直觉与深层理解

9.1 单位球:范数的几何可视化

每个范数都对应一个单位球,也就是所有范数等于1的向量组成的集合。L1的单位球是菱形,L2的单位球是圆形,L∞的单位球是正方形。p-范数的单位球介于菱形和正方形之间,p越小越像菱形,p越大越像正方形。

单位球的形状决定了正则化的行为。菱形有尖角,所以L1正则化的解容易落在坐标轴上(稀疏)。圆形光滑,所以L2正则化的解均匀收缩。正方形有四个尖角,L∞正则化会让权重趋向于相等。

理解单位球的形状,比死记硬背正则化效果要直观得多。我建议你在纸上画一画不同p值的单位球,画完就再也不会忘了。

9.2 对偶范数:另一个视角

每个范数都有一个对偶范数,定义为:

||y||_* = max{ yᵀx : ||x|| ≤ 1 }

对偶范数在优化理论里非常重要,尤其是在推导对偶问题和分析次梯度时。L1的对偶是L∞,L2的对偶是L2(自对偶),L∞的对偶是L1。

这个对偶关系解释了一个现象:L1正则化产生稀疏解,等价于在对偶空间里用L∞约束。L∞约束的是最大值,所以只有少数分量能达到约束边界,其余分量为0,这就是稀疏性的来源。

9.3 范数和内积的关系

范数和内积有深刻联系。L2范数可以由内积导出:||x||_2 = √(xᵀx)。但L1和L∞范数不能由任何内积导出,它们不满足平行四边形法则。

这个区别很重要:由内积导出的范数对应希尔伯特空间,有正交性、投影定理等良好性质。不能由内积导出的范数对应巴拿赫空间,性质差一些,但在特定场景下更有用。

理解这一点,你就能明白为什么L2范数在理论分析里地位特殊——它背后的希尔伯特空间结构太丰富了,很多漂亮的结果都依赖它。

10. 写在最后:我踩过的那些范数坑

回过头看,我在范数上踩的坑主要集中在两个地方:一是混淆了元素级范数和诱导范数,二是在高维场景下没意识到L1和L2的巨大差异。

第一个坑的教训是:看到“矩阵范数”这个词,先问清楚是Frobenius还是诱导范数。这两个东西的计算方式和用途完全不同,混用会导致完全错误的结果。我现在写代码时,如果用到矩阵范数,一定会在注释里写清楚是哪种。

第二个坑的教训是:高维数据里,范数的选择不是“随便选一个都差不多”,而是可能带来数量级的差异。L1和L2在高维下的比值可以到 √n,n是维度。几万维的数据里,这个差异足以改变整个模型的行为。

最后一个实用建议:如果你不确定该用哪个范数,先做个小实验,把几个候选范数都试一遍,看哪个在验证集上表现最好。理论分析能帮你缩小范围,但最终还是要靠实验说话。范数选择没有银弹,只有适合当前问题的选择。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进