VOL.II · 矩阵CH 07( 07 , 24 )

矩阵乘法:变换的复合

先穿袜子再穿鞋,和先穿鞋再穿袜子,结果大不一样——这个幼儿园常识,就是矩阵乘法不可交换的全部秘密。上一章你学会了「一个矩阵 = 一次变换」;这一章处理两次变换的接力:先旋转再剪切,合起来等于什么?答案催生了那条你背了多年却从未理解的「行乘列」公式,顺手解释 preScalepostScale 为何效果不同,还附赠一个能把计算量砍掉四个数量级的工程彩蛋。矩阵乘法是大模型烧掉的每一度电的去向,值得你彻底看穿它。

复合变换不可交换结合律计算顺序

两次变换,一份新说明书

设想两台机器流水作业:向量 x 先进机器 A(比如剪切),产出 Ax;再进机器 B(比如旋转),产出 B(Ax)。问:有没有一台单独的机器,一步到位干完这两步?

有——因为两个线性变换接力,整体仍然线性(两条军规逐条验证即可:先加后过流水线 = 先过流水线后加)。既然整体是线性变换,它就必然有自己的说明书,一个矩阵。我们把这台合成机器记作 BA,并约定它表示「先 A,后 B」:

(BA)x = B(Ax)从右往左读:x 先遇到 A

为什么顺序看起来是反的?锅在函数记号:数学里复合函数写 f(g(x)),也是 g 先干活。矩阵继承了这个传统——矩阵串从最靠近向量的那个开始执行,像洋葱从里往外剥。读深度学习论文时看到 W₃·σ(W₂·σ(W₁x)),数据的旅程是 W₁ → W₂ → W₃,和书写顺序恰好相反。这个「从右往左」是新手第一大坑,现在把它钉死。

行乘列公式的出生证明

合成机器 BA 的说明书怎么写?用上一章的读法:说明书的列 = 基向量的去向。î 经过流水线的旅程:先被 A 送到「A 的第一列」;这支新箭再进机器 B,被 B 变换。所以:

BA 的第 j 列 = B · (A 的第 j 列)合成说明书 = 把 A 的每列过一遍 B

就这一行,矩阵乘法的全部定义。展开成分量,你就得到教科书那条公式:C 的第 (i, j) 格 = B 的第 i 与 A 的第 j 的点积。「行乘列」不是天条,是「基向量去向的接力登记」的机械展开。用一个 2×3 与 3×2 的例子逐格点亮一遍,你会看得非常清楚:

矩阵乘法分步执行器DEMO 05

点亮过程中注意观察配色:橙红高亮的是左家的一整行,青蓝高亮的是右家的一整列,紫色格子是它们点积的落点——三种颜色的分工与全书一致。四格点完,顺便验收形状规则:(2×3) 乘 (3×2) 得 (2×2)。内侧的 3 必须相等——左家的行要和右家的列做点积,长度不同没法点;外侧的 2 和 2 存活下来,决定结果的尺寸。用流水线语言说更透彻:A 的输出维度,必须接得上 B 的输入维度,接口对不上,流水线报废。深度学习框架里那句著名报错 mat1 and mat2 shapes cannot be multiplied,说的就是接口没对上。

同一个乘法,四种读法

矩阵乘法 C = BA 有四种等价的读法,像同一座建筑的四张图纸。全部认识一遍,以后读公式就能随时切换到最顺眼的那张:

  • 逐格读(行×列):C 的每一格 = B 的一行点积 A 的一列。刚才 Demo 演的就是它,适合手算和验证。
  • 按列读:C 的第 j 列 = B 乘 A 的第 j 列 = 以 A 的第 j 列为配方,组合 B 的各列。适合思考「变换把基送去了哪」。
  • 按行读:C 的第 i 行 = B 的第 i 行乘 A = 以 B 的第 i 行为配方,组合 A 的各行。数据矩阵(每行一个样本)乘权重时,这个视角最自然:每个样本各自被变换,互不干扰。
  • 按外积读(高级但值回票价):C = B 的第 1 列 ⊗ A 的第 1 行 + 第 2 列 ⊗ 第 2 行 + ⋯,即一堆「列向量乘行向量」的薄片叠加。

第四种里的 外积(outer product)值得单独介绍:一支 m 维列向量乘一支 n 维行向量,得到一个 m×n 矩阵,(uvᵀ)ij = uᵢvⱼ——它是点积的「反面」:点积把两支向量压成一个数,外积把两支向量撑成一张表。外积造出的矩阵有个特别的气质:每一列都是 u 的倍数(倍数由 v 的分量给),整张矩阵的信息量其实只有 m + n 个数,却占着 m×n 的地盘——这种「一支向量撑起来的矩阵」叫秩一矩阵,是第 12 章「秩」的最小积木、第 17 章 SVD 的三幕剧演员、第 23 章 LoRA 的本体。「任何矩阵乘法都是若干秩一薄片的叠加」,这句话此刻不必消化,存进收藏夹。

单位矩阵:乘法世界的数字 1

上一章 Demo 的「复位」按钮,数学身份是单位矩阵 I:对角线全 1、其余全 0,「什么都不做」的变换。它是矩阵乘法的单位元:AI = IA = A,地位等同数字乘法里的 1。三个用途现在就能立起来:① 幂的起点 A⁰ = I;② 「撤销」的标尺——第 09 章将定义逆矩阵为「乘起来等于 I 的那个矩阵」;③ 工程里的「跳过」——残差连接 x + F(x) 可以改写成 (I + F)(x),「至少把原样传下去」的保底,说明书就是 I。

不可交换:鞋和袜子的几何证据

AB 和 BA 一般不相等,给一个三秒钟的几何证据。取「逆时针旋转 90°」和「水平拉伸两倍」:先旋转再拉伸,一支朝上的箭会先倒向左边、再被横向拉长——结果又长又横;先拉伸再旋转,朝上的箭没被拉伸影响(它没有水平分量),旋转后还是原长——结果又短又横平。两条流水线,两种产品。你也可以在上一章的 Demo 里模拟:先点「旋转 45°」记住样子,复位后想象先剪切再旋转,网格的姿态不一样。

不可交换不是缺陷,是表达力:变换的世界里先后本来就有意义,矩阵乘法忠实地记录了这份意义。真正要小心的是它对代数习惯的连环冲击:

⚠ 坑

中学代数的三条肌肉记忆在矩阵世界全部作废:① (A+B)² = A² + AB + BA + B²,中间两项不能合并成 2AB;② AB = 0 推不出 A 或 B 是零矩阵(两个「压扁」变换可以把彼此的幸存者刚好杀光);③ AB = AC 推不出 B = C(A 压扁过的世界里,不同的前史可以留下相同的现场)——除非 A 可逆,这正是第 09 章「可逆」为什么金贵的原因之一。展开任何矩阵表达式时,请像对待不认识的代码库一样保持敬畏:先查交换性,再动手化简。

结合律:免费,但价值连城

交换律阵亡,结合律却毫发无伤:(CB)A = C(BA),随便加括号。为什么?回到流水线视角,这甚至不需要计算:三台机器按 A、B、C 的顺序排好,「先把 B、C 合并成一台,再接在 A 后面」和「先把 A、B 合并,再接上 C」,描述的是同一条流水线——执行顺序根本没变,变的只是你心里怎么分组。结合律不是巧合,是「复合」这个概念天生自带的。

这条「免费」的定律,在工程上值一座金矿。考虑计算 A·B·x,其中 A、B 都是 4096×4096 的矩阵,x 是一支向量。两种加括号方式:

  • (AB)·x:先算矩阵乘矩阵,约 2·4096³ ≈ 1.4×10¹¹ 次运算,再乘向量;
  • A·(Bx):先算矩阵乘向量(2·4096² ≈ 3.4×10⁷),再来一次矩阵乘向量——总共 ≈ 6.7×10⁷

同一个数学表达式,计算量差出两千倍。只因为括号加的位置不同。这类「矩阵链乘的最优顺序」是算法课的经典动态规划题,更是 AI 工程的日常:LoRA(第 23 章)故意把大矩阵拆成两个瘦长小矩阵的乘积,推理时永远「先让向量过瘦矩阵」;各种论文里「我们避免显式构造该矩阵」的句子,九成是在玩这一手。请把这个例子放进你的工程直觉库,它可能是本章最实用的一段。

矩阵的幂:同一把拧法,反复施加

矩阵的幂还有一个和「变换」看似无关的妙用:数路径。把一张图(社交网络、网页链接、地铁线路)写成邻接矩阵 A——第 (i, j) 格为 1 表示 i 到 j 有一条边。那么 A² 的第 (i, j) 格,按行乘列展开是 Σₖ AikAkj:枚举所有中转站 k,「i 到 k 有边」且「k 到 j 有边」的组合数——恰好是从 i 经两步到 j 的路径条数!同理 Aᵏ 数 k 步路径。「好友的好友」推荐、知识图谱的多跳推理,底层都是这笔账。矩阵乘法的求和结构 Σₖ 天生就是「枚举所有中间可能」——这个理解方式在概率转移、动态规划里反复通用。

A 乘自己记作 A²,意思是同一个变换连做两次;A¹⁰⁰ 就是拧一百次。有些变换拧多了会「收敛」:比如一个轻微向某方向压缩的变换,反复施加后,任何初始向量都被压到同一条线上。这个看似无聊的观察,是 Google 起家算法 PageRank 的数学核心——把「随机点击链接的网民」写成一个转移矩阵,拧无穷多次,网页的排名就浮现在极限里。细节留给第 16 章,但请记住:「反复施加同一变换会发生什么」是一个价值千亿美元的问题。

分块:把矩阵当乐高拼

最后一个思维工具:大矩阵可以切成,把每一块当成一个「元素」,然后惊人地——分块之后的乘法规则和普通乘法一模一样,行块乘列块,只是格子里的乘法换成小矩阵乘法。这不是杂技,是工程日常:GPU 内核就是按块搬运计算的;多头注意力(第 22 章)把一个大投影矩阵切成 32 个头,每头一块,「拼接再投影」在代数上就是分块乘法;LoRA 的推导里「把 W 和 ΔW 并排看」也是分块视角。你不需要现在就熟练分块运算,只需要建立一个印象:矩阵是可以递归的——矩阵的元素可以又是矩阵,这让「百亿参数」的庞然大物依然能被人脑分而治之。

写成代码:@、形状与算力账

matmul.py
import numpy as np

A = np.random.randn(2, 3)
B = np.random.randn(3, 4)
C = A @ B                      # shape (2, 4):内侧 3 咬合消失

# 结合律的算力差异,亲手计时
M = np.random.randn(2000, 2000)
N = np.random.randn(2000, 2000)
x = np.random.randn(2000)
# %timeit (M @ N) @ x   → 数百毫秒(先做 2000³ 的大乘法)
# %timeit M @ (N @ x)   → 约 1 毫秒(两次矩阵乘向量)

# 批量乘法:32 句话 × 每句 128 词 × 每词 64 维
Q = np.random.randn(32, 128, 64)
K = np.random.randn(32, 128, 64)
scores = Q @ K.transpose(0, 2, 1)   # (32, 128, 128):32 张注意力分数表

最后一段是真实模型代码的形状:第一维 32 是 batch(32 个句子并行),后两维才是矩阵。@ 会自动「对 batch 里每一份各做一次矩阵乘」——这叫批量矩阵乘(bmm),GPU 最爱的进食方式。算力账也可以立个公式:(m×n) 乘 (n×p) 需要约 2mnp 次浮点运算——训练报告里的「FLOPs」就是把整个网络的 2mnp 全加起来。有了它,你可以像查水表一样审计任何模型:大头永远在那几个最胖的矩阵乘上。

还有一层硬件真相值得知道:矩阵乘法之所以能把 GPU 喂到接近理论峰值,不只因为可并行,还因为它的「计算/搬运比」极高——把一块数据搬进高速缓存后能反复用 n 次(分块 tiling 技巧),而逐元素加法搬一次只用一次。所以工程师说矩阵乘是 compute-bound(算力瓶颈)、其他多数操作是 memory-bound(带宽瓶颈)。大模型推理优化的半壁江山——KV 缓存、算子融合、FlashAttention——本质都在跟「怎么少搬数据」搏斗。这句话眼下超纲,但等你哪天读推理优化的文章,会想起它是从矩阵乘法的性格里长出来的。

转置:把说明书沿对角线翻个面

顺路收编一个高频符号。矩阵 A 的转置 Aᵀ,是把它沿主对角线翻面:行变列、列变行,(i, j) 位置的元素跑到 (j, i)。两条立刻有用的性质:① (AB)ᵀ = BᵀAᵀ——转置会把乘法顺序整个反过来,像「先穿袜子再穿鞋」的脱法是「先脱鞋再脱袜子」;② 第 04 章说过 vw 是点积的矩阵式写法;③ 若一个矩阵翻面后等于自己(Aᵀ = A),它叫对称矩阵——协方差矩阵、格拉姆矩阵都是这个体质,而对称矩阵在第 15 章会享受一条极漂亮的定理待遇,先记住这张脸。至于转置的几何意义(它和「换个方向量同一件事」有关),要等第 14 章投影登场才好讲透,先把符号焐热。

◆ AI 连接

现在可以精确解读两个天天见的表达式了。① 注意力分数 QKᵀ:Q 是「每行一个查询向量」的矩阵,Kᵀ 把「每行一个键向量」翻成「每列一个键」,按行乘列规则,结果第 (i, j) 格 = 第 i 个查询点积第 j 个键——一次矩阵乘法,批发 n² 个点积,这就是第 04 章「批量点积」承诺的兑现。② 整个网络 W₃σ(W₂σ(W₁x)):去掉 σ 的话,三个 W 可以用结合律合并成一个矩阵——百层网络塌缩成单层!这就是上一章「非线性是把多层变成深度的工序」的代数证明:σ 像楔子一样卡在矩阵之间,不让它们合并。

▸ Android 类比

兑现悬念:android.graphics.MatrixpreScale / postScale,就是「乘在右边」和「乘在左边」。M.preScale(s) 得到 M·S——S 靠近向量,缩放再做 M 原有的变换;M.postScale(s) 得到 S·M——缩放排在流水线末尾。你调试「为什么图片绕错了中心转」的那些下午,本质都在调矩阵乘法的顺序。经典配方「绕点 p 旋转」= 先把 p 平移到原点、旋转、再平移回去——三个矩阵按序相乘,translate(p)·rotate(θ)·translate(−p),一个不可交换性的教科书应用。

✋ 动手自测

① 用「BA 的列 = B 乘 A 的列」手算 [0,−1;1,0]·[1,1;0,1](先剪切后旋转),再交换顺序算一遍,验证两者不同;

② (5×3) 矩阵能左乘 (3×7) 矩阵吗?结果形状?反过来呢?

A·B·C·x,A 是 1000×1000,B 是 1000×1000,C 是 1000×10,x 是 10 维——最省的计算顺序是什么?

④ 为什么 (AB)ᵀ = BᵀAᵀ 而不是 AᵀBᵀ?用「穿脱衣服」给出一句话解释;

⑤ 如果没有激活函数,128 层网络等价于几层?

小结

矩阵乘法是变换的复合,从右往左执行;它的行乘列公式源于「把 A 的各列过一遍 B」,而同一个乘法还能按列、按行、按外积三种方式重读,外积视角埋下了秩一分解的种子;交换律阵亡(先后有意义),结合律免费(分组无意义),而结合律的自由加括号权,在工程上能差出几个数量级的算力;单位矩阵是乘法的 1,幂是反复施加,邻接矩阵的幂顺手数清了多跳路径;QKᵀ 是点积的批发,无激活的深网是单层的伪装,pre/post 之谜是左乘右乘之别。你已经掌握了矩阵世界的「动词组合语法」。从「一个矩阵会动」到「一串矩阵会接力」,变换的动词库已经齐了。下一章我们给每个变换配一个体检指标:它把空间的面积放大了几倍?这个指标只是一个数,却能一眼看出变换有没有「压扁」空间——它叫行列式,而你从此不用再背它的公式。