系列:《从小白到 AI 大模型开发工程师的进阶之路》 技术点:AI-0213 矩阵运算 主人公:小蓝伞|环境:Python 3.13.9、NumPy 2.3.5
小蓝伞把样本矩阵从 (batch, feature) 转成了 (feature, batch),矩阵乘法报错后反复改循环;另一个脚本对病态矩阵直接求逆,结果被浮点误差放大。矩阵乘法首先是形状契约,求解方程优先使用 solve,不要把"能求逆"当成默认步骤。
结论与取舍
(m,n) @ (n,p) 才能得到 (m,p);转置改变轴语义,必须写清。解 Ax=b 用 np.linalg.solve(A,b),只有确实需要显式逆矩阵且确认可逆时才调用 inv。
| 做法 | 优点 | 风险 | 建议 |
|---|---|---|---|
A @ B |
直接表达线性映射 | 形状不兼容 | 先断言轴 |
solve(A,b) |
数值更稳定、少存中间量 | A 必须可解 | 首选 |
inv(A) @ b |
公式直观 | 放大误差、成本高 | 不作默认 |
最小复现
css
import numpy as np
A = np.array([[2., 0.], [0., 4.]])
b = np.array([4., 8.])
x = np.linalg.solve(A, b)
assert np.allclose(A @ x, b)
assert (A @ np.ones((2, 3))).shape == (2, 3)
失败输入 A(2,3) @ B(2,2) 应抛形状错误;奇异矩阵 [[1,2],[2,4]] 用 solve 应抛 LinAlgError,而不是返回可信数字。
误判、实验与边界
误判是只看报错行不看两边 shape;定位证据是入口日志和 A.shape、B.shape;根因是轴含义丢失。实验分别测可逆、奇异和条件数较大的矩阵,比较 solve 残差 ||Ax-b||,重复 10 次取中位数。小矩阵结果不能推出大模型线性层吞吐;GPU、稀疏矩阵和自动微分另有实现。
验证清单与下一期
- 乘法前断言内轴相等,输出 shape 与业务约定一致。
- 用
allclose验证残差,不用字符串比较浮点结果。 - 奇异、近奇异、整数输入和批量右端项都有测试。
下一期 AI-0214 特征值与特征向量会说明分解结果如何服务 PCA,以及为什么尺度会改变解释。
官方资料
实验记录与工程追问
记录矩阵时不要只写两个数字,要写每个轴的含义,例如"行是样本、列是特征"。转置后轴含义也随之变化;如果只把报错改到能运行,可能把批次当成特征,最终得到完全不同的模型。对批量右端项,明确 b 是 (m,) 还是 (m,k),两者广播和输出形状不同。
病态矩阵的危险不一定表现为异常,它可能返回一个残差看似不大的答案,却对输入噪声极度敏感。实验中同时扰动 b 的最后几位,比较解的变化,并记录条件数。若业务允许,应采用正则化、QR/SVD 或专门的最小二乘求解,而不是把 inv 包在 try/except 里继续运行。
矩阵乘法的微基准必须说明 CPU/GPU、线程数、是否包含数据搬运和预热。小矩阵在 Python 调度上的开销可能比乘法本身更大;大矩阵则受缓存和 BLAS 实现影响。本文只建立形状和数值稳定性的判断,不替代模型服务压测。