[人工智能]Python03:NumPy.Algebra代数实战指南
一份实用 NumPy 代数指南,覆盖数组代数、广播、矩阵乘法、张量、多项式、最小二乘、特征值和数值精度。
- 理解逐元素代数与矩阵代数的区别。
- 有意识地使用 shape、axis、广播和 dtype。
- 应用多项式、线性系统、投影和张量式操作。
- 通过残差、恒等式和数值稳定性检查验证代数结果。
示例均为独立代码片段,可复制到 Python 3 环境中运行。
1. 把数组视为代数对象
NumPy 数组支持标量、向量、矩阵和高维张量代数。首先应明确语义:数组代表独立数值、坐标、观测行、特征列,还是线性算子?shape 和 dtype 只编码了部分含义,程序员还必须在整个计算过程中保持约定一致。
|
对象 |
典型形状 |
代数角色 |
|
标量 |
() |
单个系数或数值 |
|
向量 |
(n,) |
坐标或系数 |
|
列向量 |
(n, 1) |
明确的矩阵兼容向量 |
|
矩阵 |
(m, n) |
线性映射或系数系统 |
|
张量 |
(d₁, d₂, ..., dₖ) |
批次或多轴数据 |
|
矩阵批次 |
(batch, m, n) |
重复代数操作 |
import numpy as np
scalar = np.array(3.0)
vector = np.array([1.0, 2.0, 3.0])
column = vector[:, None]
matrix = np.arange(12.0).reshape(3, 4)
tensor = np.arange(24.0).reshape(2, 3, 4)
for name, value in [("scalar", scalar), ("vector", vector), ("column", column), ("matrix", matrix), ("tensor", tensor)]:
print(name, value.shape, value.ndim, value.dtype)
Shape 是代数的一部分
- 表达一维向量行为时使用 (n);需要明确列向量时使用 (n, 1)。
- 每次矩阵乘法前检查内部维度是否匹配。
- 在可复用代码中把轴含义视为 API 约定。
- 选择能够表示中间结果的 dtype,而不只是能够表示最终结果的类型。
2. 逐元素运算、广播与归约
NumPy 的算术运算默认是逐元素的。广播允许尾部维度兼容的数组参与运算,而不需要手动复制数值。sum、mean、min 和 max 等归约会压缩一个或多个轴。这种组合支持简洁的向量化代数,但意外的 shape 也可能产生看似合理、实际含义错误的结果。

图 1:(3, 1) 和 (1, 4) 的数组广播后得到 (3, 4) 结果。
A = np.arange(12.0).reshape(3, 4)
row_offset = np.array([10.0, 20.0, 30.0])[:, None]
column_scale = np.array([1.0, 0.5, 2.0, 1.5])
shifted = A + row_offset
scaled = A * column_scale
centered = A - A.mean(axis=0, keepdims=True)
row_norms = np.linalg.norm(A, axis=1)
print(shifted.shape, scaled.shape, centered.mean(axis=0), row_norms)
|
操作 |
示例 |
常见用途 |
|
逐元素加法 |
A + b |
偏移和校准 |
|
逐元素乘法 |
A * scale |
特征或坐标缩放 |
|
幂运算 |
x ** 2 |
能量和平方误差 |
|
归约 |
A.sum(axis=0) |
列总和 |
|
条件运算 |
np.where(mask, a, b) |
分段代数 |
|
裁剪 |
np.clip(x, lo, hi) |
限制数值范围 |
广播检查表
- 从最后一个轴开始向前比较维度。
- 两个维度相等,或其中一个为 1 时兼容。
- 方向重要时使用 None 或 reshape 显式增加单例轴。
- 在测试中打印中间 shape,并用 assert 固化约定。
3. 矩阵乘法与几何变换
@ 运算符执行矩阵乘法,与 * 不同。矩阵可以把向量映射到向量、与另一个矩阵组合并变换几何对象。转置、逆、行列式、秩和范数描述不同性质,不能把任何一个当作理解运算的通用替代品。

图 2:矩阵乘法改变单位圆的几何形状。
A = np.array([[2.0, 0.5], [0.0, 1.5]])
B = np.array([[1.0, -1.0], [2.0, 0.0]])
x = np.array([3.0, 4.0])
print("element-wise:", A * B)
print("matrix product:
", A @ B)
print("transformed vector:", A @ x)
print("determinant:", np.linalg.det(A))
print("rank:", np.linalg.matrix_rank(A))
|
性质 |
NumPy 表达式 |
含义 |
|
转置 |
A.T |
交换矩阵轴 |
|
矩阵乘法 |
A @ B |
组合线性映射 |
|
行列式 |
np.linalg.det(A) |
方阵的有符号体积尺度 |
|
秩 |
np.linalg.matrix_rank(A) |
独立方向数量 |
|
逆 |
np.linalg.inv(A) |
可逆时映射回去;谨慎使用 |
|
条件数 |
np.linalg.cond(A) |
对输入扰动的敏感性 |
不要混淆代数含义
A.T 不是矩阵逆。A * B 不是 A @ B。方阵行列式非零表示可逆,但不保证数值上适合求逆。求解 Ax=b 时应优先使用 solve,而不是显式构造逆矩阵。
4. 多项式代数、根与近似
NumPy 的 polyval 和 roots 使用降幂系数顺序表示多项式。多项式操作包括求值、求导、积分、拟合和求根。现代代码也可以使用 polynomial 命名空间,其系数顺序更容易推理;不同约定混用前必须确认。

图 3:NumPy 可以计算二次多项式并寻找其根。
# 降幂系数:x^2 - 3x + 2
coefficients = np.array([1.0, -3.0, 2.0])
x = np.linspace(-1, 4, 100)
y = np.polyval(coefficients, x)
roots = np.roots(coefficients)
derivative = np.polyder(coefficients)
antiderivative = np.polyint(coefficients)
print(roots)
print(np.polyval(derivative, 1.0))
|
操作 |
函数 |
结果 |
|
求值 |
np.polyval(c, x) |
多项式值 |
|
求根 |
np.roots(c) |
可能为复数的根 |
|
求导 |
np.polyder(c) |
导数系数数组 |
|
积分 |
np.polyint(c) |
原函数系数 |
|
拟合 |
np.polyfit(x, y, degree) |
最小二乘多项式系数 |
|
卷积 |
np.polynomial.polynomial.polymul |
按指定系数约定相乘 |
多项式稳定性
高阶多项式的求值和求根可能是病态的。适当缩放或中心化 x,检查 p(root) 的残差,不要解释超过数据和数值方法支持精度的根。
5. 线性系统、最小二乘与投影
线性代数是 NumPy 代数的重要部分。solve 处理方阵系统,lstsq 处理超定系统,并返回秩和奇异值信息。最小二乘解是目标向量在设计矩阵列空间上的投影。

图 4:最小二乘直线使观测值与预测值之间的平方残差最小。
rng = np.random.default_rng(2026)
x = np.linspace(0, 10, 50)
y = 1.5 * x + 2 + rng.normal(0, 1.5, size=x.size)
A = np.column_stack([x, np.ones_like(x)])
coefficients, residuals, rank, singular_values = np.linalg.lstsq(A, y, rcond=None)
prediction = A @ coefficients
relative_residual = np.linalg.norm(prediction - y) / np.linalg.norm(y)
print(coefficients, rank, singular_values, relative_residual)
|
任务 |
方法 |
检查 |
|
求解 Ax=b |
np.linalg.solve(A, b) |
相对残差 |
|
拟合线性模型 |
np.linalg.lstsq(A, y) |
残差、秩和奇异值 |
|
最小范数解 |
np.linalg.pinv(A) @ b |
敏感性和截断 |
|
投影 |
A @ np.linalg.lstsq(A, y)[0] |
残差正交性 |
|
多个目标 |
solve(A, B) 或 lstsq(A, B) |
检查每个目标列 |
残差与正交性检查
- 计算绝对和相对残差;相对于很小的目标值,绝对残差也可能很大。
- 最小二乘中,残差应近似与 A 的各列正交。
- 特征冗余或近似共线时检查数值秩。
- 使用已知合成系数向量,区分求解器误差和观测噪声。
6. 高维数组、张量运算与 einsum
高维数组通过增加批次或特征轴推广矩阵代数。reshape 改变兼容数据的形状;transpose 或 moveaxis 改变轴顺序;einsum 使用索引收缩表达式,可以在明确记录索引含义后替代嵌套循环。

图 5:三维数组的不同切片拥有不同 shape 和含义。
tensor = np.arange(24.0).reshape(2, 3, 4)
first_batch = tensor[0]
last_feature = tensor[:, :, -1]
transposed = tensor.transpose(2, 0, 1)
# 对每个批次进行矩阵式加权
weights = np.arange(4.0)
weighted = np.einsum("bif,f->bi", tensor, weights)
print(first_batch.shape, last_feature.shape, transposed.shape, weighted.shape)
|
操作 |
模式 |
用途 |
|
重塑 |
x.reshape(...) |
改变形状但不改变元素总数 |
|
转置 |
x.transpose(...) |
重新排列轴 |
|
广播 |
x + bias |
应用低维量 |
|
堆叠 |
np.stack([...], axis=...) |
创建新轴 |
|
拼接 |
np.concatenate([...], axis=...) |
沿已有轴连接 |
|
Einstein 求和 |
np.einsum('bif,f->bi', ...) |
显式索引收缩 |
可读的张量代数
使用能表达含义的变量名和注释,说明每个轴代表什么。复杂 einsum 表达式优化前,应先写一个参考实现,或与 matmul 和归约结果进行测试。
7. 数值精度、特征值与验证
浮点代数是近似的。由于消去、溢出、下溢和运算顺序,代数等价的公式可能有不同舍入行为。NumPy 提供 allclose 和 isclose 进行带容差比较。特征值和特征向量应通过残差恒等式检查,而不是要求小数完全相等。

图 6:代数等价表达式在小参数下可能产生不同数值结果。

图 7:特征向量表示对称矩阵在缩放下保持的方向。
A = np.array([[3.0, 1.0], [1.0, 2.0]])
values, vectors = np.linalg.eigh(A)
for i, value in enumerate(values):
vector = vectors[:, i]
assert np.allclose(A @ vector, value * vector)
x = np.array([1.0, 1.0 + 1e-12])
y = np.array([1.0, 1.0])
print(np.array_equal(x, y))
print(np.allclose(x, y, rtol=1e-10, atol=1e-12))
|
验证 |
问题 |
实践 |
|
形状断言 |
维度是否有意设计? |
assert x.shape == expected |
|
有限性检查 |
数值是否有效? |
np.isfinite(x).all() |
|
恒等式检查 |
已知恒等式是否成立? |
np.allclose(left, right) |
|
残差检查 |
解是否满足方程? |
||Ax−b|| / ||b|| |
|
秩检查 |
方向是否独立? |
matrix_rank 和奇异值 |
|
容差记录 |
何时算相等? |
记录 rtol 和 atol |
NumPy 代数最终检查表
- 区分逐元素运算、广播和矩阵乘法。
- 明确轴含义、shape、dtype 和单位。
- 保持多项式系数约定一致,检查根和残差。
- 方程组使用 solve 或 lstsq,检查秩、条件性和残差。
- 使用容差和数学恒等式验证浮点结果。
更多推荐

所有评论(0)