1. 为什么“矩阵乘法”总让人一头雾水
线性代数这门课,很多人学完一学期,矩阵乘法算得飞快,但一问“矩阵乘法到底在干什么”,脑子里就只剩下一句“行乘列求和”。这不是你笨,而是大多数教材把顺序搞反了——先塞给你一堆计算规则,再告诉你它其实对应着空间变换的复合。我当初也是这么过来的,直到后来做图形学相关的东西,才真正把“矩阵乘法”和“线性变换的复合”这两件事焊死在脑子里。
这篇笔记想做的事情很纯粹:把矩阵乘法从“算术操作”还原成“变换的接力”。如果你正在学线性代数、做计算机图形学、搞机器学习里的特征变换,或者单纯想把当年没学明白的东西补回来,那这篇内容应该能帮你省下不少翻书的时间。我会从几何直觉讲起,把矩阵乘法的每一个细节拆开,告诉你为什么是那样算的、顺序为什么不能反、单位矩阵和逆矩阵到底在“复合”里扮演什么角色,最后再落到实际计算和常见坑上。
核心关键词就三个:矩阵乘法、线性变换、复合。这三个词其实说的是同一件事的三个面——矩阵是变换的记账本,乘法是变换的串联,复合是几何上的先后作用。把这三者打通,线性代数里一大半的困惑会自己消失。
2. 先把“线性变换”这件事说透
2.1 变换就是“把空间里的点挪个位置”
你可以把线性变换想象成一只无形的手,它伸进整个平面或者空间,把每一个点按照某种规则挪到新位置。但“线性”这两个字加了限制:第一,原点不能动,变换前后原点还在原点;第二,直线变换后还得是直线,不能弯;第三,网格线变换后必须保持等距平行。满足这三条,才叫线性变换。
为什么强调这三条?因为一旦满足,整个变换就可以用“基向量去了哪里”来完全描述。二维空间里,我们盯住两个基向量:一个是沿x轴的单位向量,记作i-hat,坐标是(1, 0);另一个是沿y轴的单位向量,记作j-hat,坐标是(0, 1)。只要知道变换后这两个向量落到哪里,空间里任何一个向量变换后的位置就唯一确定了。
举个例子,假设某个变换把i-hat拉到(3, 1),把j-hat拉到(-1, 2)。那么任意向量(x, y)变换后就是x乘以(3, 1)加上y乘以(-1, 2),也就是(3x - y, x + 2y)。这个结果完全由两个基向量的落点决定。而这两个落点的坐标,按列排起来,就构成了矩阵:
[ 3 -1 ] [ 1 2 ]第一列是i-hat的新位置,第二列是j-hat的新位置。这就是矩阵的几何含义——矩阵的列就是变换后的基向量。这个认知一旦建立,后面所有东西都会顺起来。
2.2 矩阵是变换的“记账本”,不是一堆数字
很多人把矩阵看成一张数字表格,这没错,但不够。更准确地说,矩阵是一份“基向量去向报告”。你写下一个2×2矩阵,就等于宣布:在这个变换下,i-hat去到了第一列那个坐标,j-hat去到了第二列那个坐标。
这个视角的好处是,当你看到矩阵
[ 0 -1 ] [ 1 0 ]你不会只看到四个数字,你会立刻意识到:i-hat从(1,0)转到了(0,1),j-hat从(0,1)转到了(-1,0)。这是逆时针旋转90度。再比如
[ 2 0 ] [ 0 2 ]i-hat和j-hat都拉长到原来的2倍,整个平面均匀放大两倍。矩阵的每一列都在讲故事,讲的是基向量搬家去了哪里。
注意:矩阵的列对应基向量,这个约定是“列向量在右乘”体系下的标准。如果你习惯行向量左乘,矩阵的行才对应基向量。两种体系不能混用,混了就会在复合顺序上翻车。我建议统一用列向量体系,因为绝大多数图形学和机器学习库都是这么定的。
2.3 从“一个变换”到“两个变换的接力”
单个变换理解了,复合就是自然延伸:先做一个变换,再做另一个变换,最终效果等于一个什么样的变换?这就是矩阵乘法要回答的问题。
假设先做变换A,再做变换B。注意这里的“先A后B”在书写上通常写成B·A(从右往左读),因为列向量体系下,向量先被A作用,再被B作用,写成B(A·v) = (B·A)·v。这个顺序问题是初学者最大的坑之一,后面会专门拆。
复合的核心问题是:如果A的矩阵是M_A,B的矩阵是M_B,那么复合变换对应的矩阵M_C是什么?答案就是M_C = M_B · M_A。而矩阵乘法的计算规则,正是为了保证这个等式成立而设计出来的。换句话说,矩阵乘法不是先有规则再找用途,而是先有复合需求再推导出规则。理解这一点,你就不会再觉得矩阵乘法是“天上掉下来的奇怪算法”。
3. 矩阵乘法到底怎么算,为什么这么算
3.1 从几何复合反推乘法规则
我们不用教材里“行乘列”的说法,直接从几何出发推一遍。设变换A把i-hat送到(a, c),把j-hat送到(b, d),所以
M_A = [ a b ] [ c d ]设变换B把i-hat送到(e, g),把j-hat送到(f, h),所以
M_B = [ e f ] [ g h ]现在问:复合变换C = 先A后B,它的矩阵是什么?我们只需要追踪两个基向量在复合后的落点。
先看i-hat。A作用后,i-hat变成(a, c)。然后B作用在这个向量上。B作用在任意向量(x, y)上的结果是x乘以B的第一列加上y乘以B的第二列,即x(e, g) + y(f, h)。把(x, y) = (a, c)代入,得到a(e, g) + c(f, h) = (ae + cf, ag + ch)。所以复合后i-hat落在(ae + cf, ag + ch),这就是M_C的第一列。
再看j-hat。A作用后变成(b, d)。B作用后是b(e, g) + d(f, h) = (be + df, bg + dh)。这是M_C的第二列。
于是
M_C = [ ae+cf be+df ] [ ag+ch bg+dh ]这就是矩阵乘法的完整推导。你对照教材里的“行乘列”规则,会发现完全一致:M_C第一行第一列是M_B第一行乘M_A第一列,第一行第二列是M_B第一行乘M_A第二列,以此类推。但通过几何推导,你不仅知道怎么算,还知道为什么这么算——每一列都是基向量经过两次接力后的落点。
3.2 一个具体例子把过程走一遍
光看公式容易飘,我们拿两个具体矩阵算一遍。设
M_A = [ 1 2 ] M_B = [ 3 0 ] [ 3 4 ] [ 1 2 ]先A后B,复合矩阵M_C = M_B · M_A。
第一列:M_A第一列是(1, 3)。B作用后是1·(3, 1) + 3·(0, 2) = (3, 1) + (0, 6) = (3, 7)。
第二列:M_A第二列是(2, 4)。B作用后是2·(3, 1) + 4·(0, 2) = (6, 2) + (0, 8) = (6, 10)。
所以
M_C = [ 3 6 ] [ 7 10 ]用“行乘列”验证:第一行第一列 = 3×1 + 0×3 = 3;第一行第二列 = 3×2 + 0×4 = 6;第二行第一列 = 1×1 + 2×3 = 7;第二行第二列 = 1×2 + 2×4 = 10。完全吻合。
这个例子里,M_A把i-hat拉到(1,3),j-hat拉到(2,4);M_B再把它们分别拉到(3,7)和(6,10)。复合矩阵的列就是这两个最终落点。整个过程没有任何神秘运算,就是“追踪基向量”四个字。
3.3 顺序为什么不能反:一个反例就够了
矩阵乘法不满足交换律,这是常识,但很多人只是背下来,没真正理解。我们用几何复合来解释:先A后B和先B后A,通常是两个完全不同的变换。
拿上面的M_A和M_B,算一下先B后A,即M_A · M_B。
第一列:M_B第一列是(3, 1)。A作用后是3·(1, 3) + 1·(2, 4) = (3, 9) + (2, 4) = (5, 13)。
第二列:M_B第二列是(0, 2)。A作用后是0·(1, 3) + 2·(2, 4) = (0, 0) + (4, 8) = (4, 8)。
所以
M_A · M_B = [ 5 4 ] [ 13 8 ]和M_B · M_A = [[3, 6], [7, 10]]完全不同。几何上也好理解:先拉伸再旋转,和先旋转再拉伸,结果当然不一样。就像你先穿袜子再穿鞋,和先穿鞋再穿袜子,虽然都是两个动作,但结果天差地别。
实操心得:每次写矩阵乘法,先问自己“谁先作用”。在列向量体系下,写在右边的先作用。如果你在推导图形学里的模型-视图-投影矩阵,顺序写反了,物体会飞到屏幕外面去,而且很难debug。我的习惯是先在纸上画两个基向量的落点,确认顺序对了再写代码。
4. 复合视角下的几个关键角色
4.1 单位矩阵:什么都不做的变换
单位矩阵I长这样:
[ 1 0 ] [ 0 1 ]它的列告诉你:i-hat还在(1,0),j-hat还在(0,1)。什么都没动。所以任何变换和单位矩阵复合,结果还是那个变换本身。用公式说就是I·M = M·I = M。这很符合直觉——“先做一个变换再什么都不做”和“什么都不做再做一个变换”,都等于只做那个变换。
单位矩阵在复合里的作用就像数字1在乘法里的作用。它看起来简单,但在求逆矩阵、解线性方程组、判断变换是否可逆时,它是基准参照物。一个变换可逆,意思就是存在另一个变换能把它“抵消”回单位变换。
4.2 逆矩阵:把变换倒回去
如果变换A把空间扭曲了,逆矩阵A⁻¹就是那个能把扭曲复原的变换。复合起来就是A⁻¹·A = I,先A后A⁻¹,等于什么都没做。
从几何上看,逆矩阵的列就是“原来的基向量应该从哪里来”。比如A把i-hat拉到(2,0),把j-hat拉到(0,3),那A⁻¹就要把(2,0)拉回(1,0),把(0,3)拉回(0,1)。算出来A⁻¹就是
[ 1/2 0 ] [ 0 1/3 ]复合A⁻¹·A,第一列:A第一列(2,0)被A⁻¹作用,得到2·(1/2, 0) + 0·(0, 1/3) = (1, 0)。第二列类似得到(0,1)。确实是单位矩阵。
这里有个重要结论:不是所有变换都有逆。如果变换把整个平面压扁成一条线,比如矩阵
[ 1 2 ] [ 2 4 ]两列成比例,i-hat和j-hat被压到同一条直线上。这时候你没法从压扁后的结果恢复出原来的信息,因为多个不同的点被压到了同一个位置。这种变换叫奇异变换,对应行列式为0,逆矩阵不存在。在复合的语境下,只要链条中有一个变换不可逆,整个复合就不可逆。
4.3 行列式:复合变换的面积缩放因子
行列式在复合里有一个非常漂亮的性质:det(M_B · M_A) = det(M_B) · det(M_A)。也就是说,复合变换的面积缩放因子等于各个变换缩放因子的乘积。
这很合理。如果A把面积放大2倍,B把面积放大3倍,先A后B自然把面积放大6倍。如果某个变换把面积缩到0(奇异变换),复合后的行列式也是0,整个复合不可逆。
从几何上理解行列式,就是看单位正方形(由i-hat和j-hat张成)变换后的面积。如果变换后i-hat和j-hat的落点构成的平行四边形面积是原来的k倍,行列式就是k。负的行列式表示变换翻转了空间的定向,就像照镜子一样。
注意:行列式为负不代表“面积是负的”,面积永远是正的,负号表示定向翻转。在二维里,如果变换后j-hat落在i-hat的顺时针方向,行列式就是负的。三维里类似,负行列式表示手性翻转。
5. 从二维到三维:复合的思维不变
5.1 三维矩阵的列是三个基向量的落点
三维空间里,基向量有三个:i-hat (1,0,0)、j-hat (0,1,0)、k-hat (0,0,1)。一个3×3矩阵的三列分别就是这三个基向量变换后的坐标。
比如绕z轴旋转θ角的矩阵:
[ cosθ -sinθ 0 ] [ sinθ cosθ 0 ] [ 0 0 1 ]第一列是i-hat的新位置(cosθ, sinθ, 0),第二列是j-hat的新位置(-sinθ, cosθ, 0),第三列是k-hat的新位置(0, 0, 1)。k-hat没动,因为旋转轴是z轴。这个矩阵的每一列都在告诉你一个基向量的去向。
三维复合的逻辑和二维完全一样:先A后B,复合矩阵的每一列就是对应基向量经过A再经过B的最终落点。计算规则形式上还是“行乘列”,但几何含义不变。
5.2 三维复合的一个实际场景
假设你先做一个绕z轴90度的旋转,再做一个沿x轴拉伸2倍的缩放。旋转矩阵R和缩放矩阵S分别是:
R = [ 0 -1 0 ] S = [ 2 0 0 ] [ 1 0 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 1 ]复合S·R(先旋转后缩放):
第一列:R第一列(0,1,0)被S作用,得到0·(2,0,0) + 1·(0,1,0) + 0·(0,0,1) = (0,1,0)。
第二列:R第二列(-1,0,0)被S作用,得到-1·(2,0,0) = (-2,0,0)。
第三列:R第三列(0,0,1)被S作用,得到(0,0,1)。
所以
S·R = [ 0 -2 0 ] [ 1 0 0 ] [ 0 0 1 ]这个复合变换的效果是:先把空间绕z轴转90度,再把x方向拉长2倍。注意,拉伸是在旋转之后的世界里进行的,所以被拉长的是旋转后的x轴方向,而不是原来的x轴。这就是复合顺序的威力——同样的两个变换,顺序不同,结果完全不同。
5.3 高维情况:思维框架完全一致
四维、五维甚至更高维,你没法直观画出来,但“矩阵的列是基向量落点”这个框架依然成立。n维空间有n个基向量,n×n矩阵的n列就是这n个基向量变换后的坐标。复合变换的矩阵乘法规则形式上不变,只是求和的项数变多。
在机器学习里,数据经常是几百维甚至几千维的,特征变换就是高维空间里的线性变换。你没法可视化,但可以用同样的逻辑推理:这个变换把哪些方向拉伸了、哪些方向压缩了、哪些方向旋转了。矩阵的列依然在告诉你基向量的去向,只是你看不见而已。
6. 实操中怎么用这套思维
6.1 手算复合矩阵的检查清单
每次手算矩阵乘法,我建议按这个顺序走:
- 确认顺序:谁先作用?写在右边的是先作用的。如果是“先A后B”,算B·A。
- 追踪第一列:把A的第一列拿出来,用B作用它,结果就是复合矩阵的第一列。
- 追踪第二列:把A的第二列拿出来,用B作用它,结果就是复合矩阵的第二列。
- 以此类推:n维矩阵就追踪n列。
- 用行乘列验证:随便挑一个元素,用行乘列算一遍,和追踪结果对比。
这个流程比直接套“行乘列”慢一点,但不容易出错,而且每一步都有几何意义。熟练之后可以加速,但初学阶段强烈建议按这个走。
6.2 编程实现时的注意事项
在代码里做矩阵乘法,大多数库(如NumPy、PyTorch、TensorFlow)都提供了现成函数。但有几个坑:
- 顺序问题:
A @ B在NumPy里表示矩阵乘法,但它是A左乘B,也就是先B后A(列向量体系)。如果你想要“先A后B”,应该写B @ A。这个顺序和数学书写一致,但和自然语言“先A后B”相反,容易搞混。 - 维度匹配:m×n矩阵只能和n×p矩阵相乘,结果是m×p。维度不匹配会直接报错,这是好事,能帮你抓住错误。
- 批量运算:深度学习里经常做批量矩阵乘法,比如
torch.bmm。这时候要特别注意batch维度在哪,不要和矩阵维度搞混。 - 数值精度:浮点数矩阵乘法会有累积误差,做大量复合时误差可能放大。如果发现结果不对,先检查是不是精度问题,再检查逻辑。
实操心得:我在做图形学变换时,习惯把每个变换矩阵单独写出来,然后在纸上标好顺序,最后再合成一个矩阵。合成之后,拿几个已知点验证一下——比如原点变换后还是原点(线性变换必须满足),单位向量变换后是不是预期的位置。这一步验证能抓住绝大多数顺序错误和符号错误。
6.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 复合结果和预期完全不符 | 乘法顺序反了 | 检查是先A后B还是先B后A,列向量体系下右边先作用 |
| 变换后原点动了 | 用了仿射变换而非线性变换 | 线性变换必须保持原点不动,检查矩阵是否多了平移项 |
| 行列式为0但以为可逆 | 变换把空间降维了 | 检查矩阵的列是否线性相关,成比例的列会导致奇异 |
| 手算和代码结果不一致 | 行/列向量体系混用 | 统一用列向量体系,代码里确认乘法方向 |
| 高维矩阵乘法报错 | 维度不匹配 | 检查前一个矩阵的列数是否等于后一个矩阵的行数 |
| 复合后面积缩放不对 | 行列式算错或顺序错 | 验证det(B·A) = det(B)·det(A),顺序错会导致结果不同 |
7. 把复合思维用到更远的地方
7.1 基变换:换一套坐标看同一个变换
复合思维还能解释基变换。假设你有一组新的基向量,想知道同一个线性变换在新基下长什么样。这相当于:先把新基坐标转成标准坐标(变换P),做原来的线性变换(矩阵A),再转回新基坐标(变换P⁻¹)。复合起来就是P⁻¹·A·P。
这个式子在线性代数里叫相似变换,用来判断两个矩阵是否表示同一个变换在不同基下的样子。从复合的角度看,它就是“换坐标-做变换-换回来”三步接力。理解了复合,相似变换就不再是死记硬背的公式。
7.2 特征向量:复合中方向不变的向量
特征向量是那些在变换下方向不变的向量,只被拉伸或压缩。用复合的语言说,如果v是A的特征向量,那么A·v = λv,λ是特征值。这意味着变换A作用在v上,效果等同于一个简单的缩放。
在复合变换里,特征向量有特殊地位。如果两个变换共享特征向量,它们的复合会简单很多。在微分方程、振动分析、主成分分析里,找特征向量就是找那些在复杂变换中保持方向稳定的方向。从几何上看,这些方向是变换的“骨架”。
7.3 矩阵分解:把复杂复合拆成简单复合
很多矩阵分解,比如LU分解、QR分解、奇异值分解,本质上都是把一个复杂变换拆成几个简单变换的复合。LU分解是把一个变换拆成下三角和上三角变换的接力;QR分解拆成正交变换和三角变换;奇异值分解拆成旋转、缩放、再旋转。
一旦你用复合的眼光看这些分解,它们就不再是抽象的代数技巧,而是“把一个复杂的空间扭曲拆解成几个容易理解的步骤”。这也是线性代数在数据压缩、推荐系统、图像处理里大放异彩的原因——复杂变换可以被拆成简单变换的接力,计算和存储都变得可行。
8. 我踩过的坑和最后的小建议
最开始学矩阵乘法时,我最大的坑就是顺序。当时做一个小图形程序,想让一个三角形先旋转再平移,结果写成了先平移再旋转,三角形直接飞出了视野。debug了半天才发现是矩阵乘法顺序反了。从那以后,我养成了一个习惯:每次写复合变换,先在纸上画三个点——原点、i-hat落点、j-hat落点——确认顺序对了再写代码。
另一个坑是混淆了线性变换和仿射变换。线性变换必须保持原点不动,但平移会移动原点,所以平移不是线性变换。在图形学里,为了用矩阵表示平移,我们引入齐次坐标,把二维点扩展成三维,用3×3矩阵表示二维仿射变换。这时候矩阵的最后一列不再是基向量落点,而是平移量。这个扩展很实用,但概念上要清楚:齐次坐标下的矩阵乘法,前两列还是线性部分,第三列是平移部分。
最后分享一个小技巧:如果你对某个复合变换的结果没把握,拿一个具体向量代进去算一遍。比如拿(1,0)和(0,1)分别走一遍复合流程,看看结果和复合矩阵的列是否一致。这个验证方法简单粗暴,但极其有效。我到现在做复杂的矩阵推导时,还会用这招来兜底。
线性代数的本质不是计算,而是理解空间变换的复合。矩阵乘法只是这个复合的记账方式。把几何直觉建立起来,计算规则自然就顺了。希望这篇笔记能帮你把这块硬骨头啃下来。