NumPy 的 norm函数怎么用:向量与矩阵范数示例

NumPy 的 norm函数怎么用:向量与矩阵范数示例

NumPy 的 norm函数怎么用:向量与矩阵范数示例

NumPy 的 np.linalg.norm 用来计算向量或矩阵的范数,常见于向量长度计算、距离与误差评估、矩阵大小比较、正则化和数值分析。它的接口看起来简单,但结果取决于输入维度以及 ord 和 axis 的组合:同一个 ord=2,用于向量时表示欧几里得长度,用于矩阵时则表示谱范数。

本文从常用场景开始,逐步解释参数、批量计算、矩阵范数、边界情况和易错点。代码可直接运行,需要先安装 NumPy。

1. 基本用法

函数的常见调用形式如下:

numpy.linalg.norm(x, ord=None, axis=None, keepdims=False)
  • x:数组或可转换为数组的对象。通常传入数值数组。
  • ord:范数的类型。省略或设为 None 时,计算规则取决于输入的维度和 axis。
  • axis:指定沿哪个轴计算向量范数,或指定哪两个轴组成矩阵。
  • keepdims:是否保留被计算的轴,并将其长度设为 1,便于后续广播。

最简单的例子是计算一个向量的长度:

import numpy as np

v = np.array([3.0, 4.0])

length = np.linalg.norm(v)
print(length)  # 5.0

对于一维向量,默认结果是欧几里得范数,也就是各分量平方和再开平方:

||v||₂ = sqrt(|v₁|² + |v₂|² + ... + |vₙ|²)

这里的绝对值写法也适用于复数向量。对实数输入,它就是通常所说的向量长度。

2. 向量范数:常用 ord 选项

向量的 ord 决定采用哪一种长度度量。下面的示例在同一个向量上计算多种范数:

import numpy as np

v = np.array([3.0, -4.0, 0.0])

for p in [None, 1, 2, np.inf, -np.inf, 0]:
    print(f"ord={p!s:>4}: {np.linalg.norm(v, ord=p)}")

常见选项及其含义如下:

  • ord=None 或 ord=2:欧几里得范数,计算平方和的平方根。
  • ord=1:曼哈顿范数,即分量绝对值之和。
  • ord=np.inf:最大范数,即最大的分量绝对值。
  • ord=-np.inf:最小范数,即最小的分量绝对值。
  • ord=0:非零分量的个数。它常被称为“零范数”,但严格来说并不是数学意义上的范数。
  • 其他实数 p:按 (sum(abs(x)**p))**(1/p) 计算。负数 p 也可以用于非零数据,但结果不满足通常范数的全部性质,且零分量会造成除零问题。

例如,针对 [3, -4, 0]:

import numpy as np

v = np.array([3.0, -4.0, 0.0])

print(np.linalg.norm(v, ord=1))       # 7.0
print(np.linalg.norm(v, ord=2))       # 5.0
print(np.linalg.norm(v, ord=np.inf))  # 4.0
print(np.linalg.norm(v, ord=0))       # 2.0

不同范数适合不同问题。欧几里得范数常用于几何距离和残差大小;L1 范数常用于绝对误差汇总或稀疏正则化;无穷范数适合关注最大分量偏差的场景。选用哪一种,应由问题定义决定,而不是仅凭数值大小判断。

3. 矩阵范数:ord 的含义会改变

二维数组通常被视为矩阵。矩阵范数与逐元素范数不是一回事:矩阵的 ord=2 是谱范数,也就是最大奇异值,不是把所有元素平方后求和再开方。

常见矩阵范数如下:

  • ord=None 或 ord='fro':Frobenius 范数,即所有元素平方模之和的平方根。
  • ord='nuc':核范数,即所有奇异值之和。
  • ord=2:谱范数,即最大奇异值。
  • ord=-2:最小奇异值。
  • ord=1:矩阵各列绝对值之和的最大值,即最大列和。
  • ord=-1:矩阵各列绝对值之和的最小值。
  • ord=np.inf:矩阵各行绝对值之和的最大值,即最大行和。
  • ord=-np.inf:矩阵各行绝对值之和的最小值。

下面用一个小矩阵对比几种结果:

import numpy as np

A = np.array([
    [1.0, 2.0],
    [3.0, 4.0],
])

print(np.linalg.norm(A, ord="fro"))  # sqrt(30)
print(np.linalg.norm(A, ord=1))      # 6.0,最大列和
print(np.linalg.norm(A, ord=np.inf)) # 7.0,最大行和
print(np.linalg.norm(A, ord=2))      # 最大奇异值,约 5.465

如果需要矩阵的谱范数或核范数,计算通常涉及奇异值分解,成本可能高于行和、列和等计算。对很大的矩阵,应把计算成本纳入设计;如果只想评估某个特定量,也不要误把 Frobenius 范数当作谱范数。

4. axis:对指定轴计算范数

实际数据往往不只是单个向量或矩阵,而是批量样本、时间序列或多维张量。此时应使用 axis 明确要沿哪个轴计算。

例如,数组的每一行是一条二维向量,想得到每行的欧几里得长度:

import numpy as np

points = np.array([
    [3.0, 4.0],
    [5.0, 12.0],
    [8.0, 15.0],
])

row_lengths = np.linalg.norm(points, ord=2, axis=1)
print(row_lengths)  # [ 5. 13. 17.]

axis=1 表示沿每行的列方向计算,因此每行得到一个结果。若数组按列存放向量,则通常使用 axis=0:

column_lengths = np.linalg.norm(points, ord=2, axis=0)
print(column_lengths)

对三维数组,axis=1 仍然表示沿第二个维度计算。需要准确区分“数组的轴编号”和“数据语义上的行、列、批次”,必要时检查数组的 shape:

print(points.shape)  # (3, 2)

当输入是矩阵,且希望沿一对轴计算矩阵范数时,axis 应传入两个轴组成的元组。例如,数组形状为“批次 × 行 × 列”,可以对每个批次中的矩阵分别计算 Frobenius 范数:

import numpy as np

batch = np.arange(24.0).reshape(2, 3, 4)

batch_frobenius = np.linalg.norm(
    batch,
    ord="fro",
    axis=(1, 2),
)
print(batch_frobenius.shape)  # (2,)
print(batch_frobenius)

这里的 axis=(1, 2) 表示每个批次项由第 1 轴和第 2 轴组成矩阵。矩阵范数的 ord 选项应使用矩阵范数支持的形式,例如 'fro'、'nuc'、1、-1、2、-2、np.inf 和 -np.inf。不要把向量的任意实数 p 选项直接套到矩阵范数上。

5. keepdims:保留维度以便广播

默认情况下,被计算的轴会从结果形状中移除。若后续要用范数结果对原数组归一化,保留维度通常更方便:

import numpy as np

X = np.array([
    [3.0, 4.0],
    [5.0, 12.0],
])

lengths = np.linalg.norm(X, axis=1, keepdims=True)
print(lengths.shape)  # (2, 1)

normalized = X / lengths
print(normalized)
print(np.linalg.norm(normalized, axis=1))  # [1. 1.]

如果没有使用 keepdims=True,lengths 的形状是 (2,)。在这个例子中,NumPy 广播规则仍可能得到预期结果,但更复杂的形状下,维度对齐容易与预想不同。保留维度可以让计算意图更清楚,也能减少广播错误。

6. 应用示例:计算向量距离与残差

两条向量的欧几里得距离,可以通过先相减再计算二范数得到:

import numpy as np

predicted = np.array([2.5, 3.0, 4.2])
target = np.array([2.0, 3.5, 4.0])

distance = np.linalg.norm(predicted - target, ord=2)
print(distance)

在线性代数中,也常计算方程残差 A @ x - b 的大小:

import numpy as np

A = np.array([
    [2.0, 1.0],
    [1.0, 3.0],
])
x = np.array([1.0, 2.0])
b = np.array([4.0, 7.0])

residual = A @ x - b
residual_norm = np.linalg.norm(residual)
print("残差:", residual)
print("残差二范数:", residual_norm)

范数会把多个分量压缩成一个标量,便于比较,但也会丢失误差的方向和分布信息。调试时最好同时检查残差向量本身,不能只看一个范数值。

7. 参数限制与边界情况

7.1 ord 必须与计算对象匹配

ord 支持哪些取值,取决于计算的是向量范数还是矩阵范数。比如 ord='nuc' 用于矩阵核范数,不是向量范数;矩阵的 ord=2 也不是向量的平方和开方。遇到不支持的组合,NumPy 会报错,而不是自动改用某一种范数。

当 axis 是整数时,计算的是沿该轴的向量范数;当 axis 是两个轴组成的元组时,计算的是由这两个轴定义的矩阵范数。轴编号必须有效,且矩阵范数的轴元组需要是两个不同的轴。若拿不准,先用 print(x.shape) 确认维度。

7.2 空数组、零向量和负阶范数

零向量的常见正阶范数为 0。若将零向量用于归一化,会出现除以 0 的问题:

import numpy as np

v = np.array([0.0, 0.0])
length = np.linalg.norm(v)

if length == 0:
    normalized = v.copy()
else:
    normalized = v / length

print(normalized)

负阶向量范数会对小分量特别敏感;当分量中包含 0 时,计算中可能出现除零,并产生警告或无穷大等结果。使用前应确认这符合问题定义。ord=0 则是在统计非零分量;对包含浮点计算误差的数据,接近零但不等于零的数仍会被计为非零。如果需要按阈值判断,应先明确阈值并自行构造布尔条件。

7.3 NaN、无穷大和浮点误差

如果输入包含 NaN 或无穷大,范数结果可能受到它们影响。计算前可按需求检查:

import numpy as np

x = np.array([1.0, np.nan, 3.0])

print(np.isfinite(x))
print(np.all(np.isfinite(x)))

不要假设范数会自动忽略缺失值;需要忽略缺失值时,应明确采用合适的掩码或缺失值处理策略。极大或极小的数也可能带来浮点精度问题。对需要严格数值控制的计算,应使用合适的数据类型,并检查输入范围、警告和结果是否有限;不要仅凭公式形式推断所有极端输入都能得到理想精度。

8. 常见错误总结

  • 把矩阵 ord=2 当成 Frobenius 范数。它实际是谱范数。需要所有元素平方和的平方根时,使用 ord='fro'。
  • 把 axis 理解成结果轴。axis 指定被计算的轴,不是结果保留的轴;需要保留被计算轴时设置 keepdims=True。
  • 忽略数组布局。同一数组沿 axis=0 与 axis=1 得到不同结果。先核对 shape 和每个轴对应的数据含义。
  • 直接用范数除以归一化向量。当范数为 0 时会除零。应先检查范数,或按业务规则处理零向量。
  • 认为 ord=0 是严格的数学范数。它表示非零元素计数,不满足范数的全部数学性质。
  • 认为所有 ord 都适用于所有输入。向量范数、矩阵范数的可用选项不同,异常时先核对输入维度、axis 和 ord 的组合。

9. 范数能否用于安全判断

范数是数值计算工具,不是安全校验机制。它可以用于衡量扰动、残差或输入变化的大小,但单独设置一个范数阈值,不能证明数据可信、程序无漏洞或系统具备对抗安全性。不同范数对数据变化的敏感方式不同,阈值也必须结合数据尺度、单位、误报漏报代价和威胁模型设计。

在安全敏感的程序中,如果范数结果用于拒绝请求、限制输入或触发保护措施,还应考虑非有限值、零值、极端数值、类型转换和阈值比较边界。尤其不要把浮点范数比较当作密码学完整性校验或认证:这类需求应使用适合的密码学方案与经过验证的实现。范数只能承担它实际定义的数值度量职责。

10. 选择方法速查

需求常见写法说明
单个向量的欧几里得长度np.linalg.norm(v)一维输入默认按二范数计算
每行向量的长度np.linalg.norm(X, axis=1)按行计算向量范数
矩阵 Frobenius 范数np.linalg.norm(A, ord="fro")所有元素平方模之和再开方
矩阵谱范数np.linalg.norm(A, ord=2)最大奇异值
批量矩阵的范数np.linalg.norm(batch, ord="fro", axis=(1, 2))对每个批次项的两个矩阵轴计算
归一化时保留维度np.linalg.norm(X, axis=1, keepdims=True)结果更容易与原数组广播

使用 np.linalg.norm 时,最重要的是先判断正在处理的是向量还是矩阵,再明确选择 ord 和 axis。如果结果要参与广播,考虑 keepdims;如果要做归一化或阈值判断,则额外处理零值、非有限值和浮点边界。这样既能避免常见误读,也能让范数结果与实际问题定义保持一致。

© 版权声明

相关文章