A * B 是逐元素乘,A @ B 才是矩阵乘。两个结果毫无关系。
形状 (m, n) 乘 (n, p) 得 (m, p),中间那个 n 必须相等。
行列式为 0 就没有逆矩阵,这是「方程无唯一解」的信号。
这一篇是第 6 章最容易出错的一篇:星号和 @ 长得像,算的完全不是一回事。 矩阵乘、转置、逆、解方程、按轴求和,五件事覆盖了九成矩阵操作。
一、三种乘法,结果完全不同
A 的第一行是 1 和 2,第二行是 3 和 4;B 的第一行是 5 和 6,第二行是 7 和 8。
A * B 给 5、12、21、32,位置对位置各乘各的,排成两行两列。
A @ B 给 19、22、43、50,这是行乘列再相加。
两个结果完全不同。星号是逐元素,@ 是矩阵乘,没有默认哪个更常用。
np.dot(A, B) 和 A @ B 一样,@ 是后来加的短写法,建议用 @。
二、矩阵乘到底怎么算
A 的第 1 行是 [1, 2],B 的第 1 列是 [5, 7]。 1 乘 5 加 2 乘 7 等于 19,这就是结果的第一行第一列。 A 的第 1 行乘 B 的第 2 列 [6, 8],1 乘 6 加 2 乘 8 等于 22。
行乘列、逐个乘、再加起来。八个字把矩阵乘说完了。 形状规则:(m, n) 乘 (n, p) 得 (m, p)。中间那个 n 是「对消」的那一维。 两边对不上就报错。所以做矩阵乘之前先打 shape,比 debug 半天快。
三、转置、行列式、逆
转置把行变列、列变行,二维矩阵 .T 一下就翻转。
行列式 np.linalg.det(A) 这次给 -2.0。它是一个数,衡量这个矩阵「缩放了多大面积」。
逆矩阵 np.linalg.inv(A) 给 -2.0、1.0、1.5、-0.5,同样是两行两列。
验算:A 乘它的逆,得到单位矩阵,对角 1 其余 0。这就是逆的定义。 行列式为 0 就没有逆矩阵,inv 会抛 LinAlgError。 行列式 0 的意思是「这个变换把平面压成了一条线」,压扁了就没法还原。
四、解线性方程组
方程组 1x 加 2y 等于 5,3x 加 4y 等于 11。
系数写成矩阵 A,右边写成向量 b,np.linalg.solve(A, b) 直接给解。
这次解是 x 等于 1.00,y 等于 2.00。验算 A 乘 x 得 [5, 11],对上了。
解方程用 solve,不要先求逆再乘。solve 更快,数值上也更稳。 先求逆再乘相当于多做一次不必要的近似,误差会被放大。
五、矩阵的常用量
| 要什么 | 写法 | 这次的值 |
|---|---|---|
| 形状 | A.shape | (2, 3) |
| 元素个数 | A.size | 6 |
| 维度 | A.ndim | 2 |
| 转置后的形状 | A.T.shape | (3, 2) |
| 所有元素和 | A.sum() | 21 |
| 按列求和 | A.sum(axis=0) | [5, 7, 9] |
axis=0 是压掉行,也就是顺着行往下走,每列得一个数。 这个方向最容易记反。别记「横还是竖」,记「压掉哪一维」,一次就记住了。
六、动手 Demo:矩阵的基本操作
先并排打 A 乘 B 和 A 乘 B 的矩阵乘,看两个结果差多远。 然后转置、行列式、逆、解方程,最后把常用量列一遍。
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print("A * B 逐元素乘:")
print(A * B)
print("A @ B 矩阵乘:")
print(A @ B)
print("1 乘 5 加 2 乘 7 = %d" % (1 * 5 + 2 * 7))
print("1 乘 6 加 2 乘 8 = %d" % (1 * 6 + 2 * 8))
F = np.array([[1.0, 2.0], [3.0, 4.0]])
print("转置:")
print(F.T)
print("行列式 %.1f" % np.linalg.det(F))
print("逆矩阵:")
print(np.round(np.linalg.inv(F), 2))
print("A 乘逆矩阵:")
print(np.round(F @ np.linalg.inv(F), 10))
b = np.array([5.0, 11.0])
x = np.linalg.solve(F, b)
print("方程解:x = %.2f,y = %.2f" % (x[0], x[1]))
print("验算:%s" % np.round(F @ x, 10))
T = np.arange(1, 7).reshape(2, 3)
print("形状 %s,元素 %d,维度 %d" % (T.shape, T.size, T.ndim))
print("转置形状 %s" % (T.T.shape,))
print("全部和 %d,按列 %s" % (T.sum(), T.sum(axis=0).tolist()))
实跑输出:
--------------------------------------------------------------------
矩阵的基本操作
--------------------------------------------------------------------
① 三种乘法,结果完全不同
--------------------------------------------------------------------
A * B 是逐元素乘:
[5, 12]
[21, 32]
A @ B 是矩阵乘(行乘列再相加):
[19, 22]
[43, 50]
星号逐元素、@ 是矩阵乘。这两个搞混,结果是完全不同的两个矩阵。
! np.dot(A, B) 和 A @ B 一样。@ 是更短的新写法,建议用 @。
② 矩阵乘怎么算的
--------------------------------------------------------------------
A 的第 1 行 [1, 2] 乘 B 的第 1 列 [5, 7]:1×5 + 2×7 = 19。
A 的第 1 行乘 B 的第 2 列 [6, 8]:1×6 + 2×8 = 22。
所以结果的第一行是 [19, 22]。行乘列、逐个乘、再加起来。
! 形状规则:(m, n) 乘 (n, p) 得 (m, p)。中间那个 n 必须相等,否则报错。
③ 转置、逆、行列式
--------------------------------------------------------------------
转置:
[1.0, 3.0]
[2.0, 4.0]
行列式 -2.0
逆矩阵:
[-2.0, 1.0]
[1.5, -0.5]
验算 A 乘 A 的逆,应该是单位矩阵:
[1.0, 0.0]
[0.0, 1.0]
! 行列式为 0 就没有逆矩阵,inv 会报 LinAlgError。这是「方程无唯一解」的信号。
④ 解线性方程组
--------------------------------------------------------------------
方程组 1x + 2y = 5,3x + 4y = 11。
solve 给解:x = 1.00,y = 2.00。
验算 A @ x:[ 5. 11.],等于右边的 [5, 11]。
! 解方程用 solve,不要先求逆再乘。solve 更快也更准。
⑤ 矩阵的常用量
--------------------------------------------------------------------
| 要什么 | 写法 | 这次的值 |
|---|---|---|
| 形状 | A.shape | (2, 3) |
| 元素个数 | A.size | 6 |
| 维度 | A.ndim | 2 |
| 转置 | A.T | (3, 2) |
| 所有元素和 | A.sum() | 21 |
| 按列求和 | A.sum(axis=0) | [5, 7, 9] |
axis=0 是顺着行走(压掉行),得到每列一个数。这个方向最容易记反。
np.round(..., 10) 是为了把浮点误差抹掉:逆矩阵理论上是整数,算出来可能带 -1e-17。
验算那一步别省。A 乘逆矩阵得到单位矩阵,才说明逆求对了。
行列式 -2.0 是负数,说明这个变换把平面翻了个面,跟「有没有逆」无关。
30 秒实验
- 把 A @ B 换成 A * B,再把结果拿去解方程,看会不会报错。
- 造一个第一行是 1 和 2、第二行是 2 和 4 的矩阵,求它的行列式,看是不是 0,再试 inv。
- 想一想:为什么 solve 比「先求逆再乘」更好?多做一步会多出什么误差?
常见误区
- 以为
*是矩阵乘:它是逐元素乘,矩阵乘要写 @。 - 矩阵乘两边形状不对就去调参数:先打 shape,(2,3) 和 (2,3) 是乘不了的。
- 行列式 0 还去求逆:抛 LinAlgError,因为方程没有唯一解。
- 记「axis=0 是竖着算」:记「压掉行」,方向自然就出来了。
自测三问(点开对答案)
1. `A * B` 和 `A @ B` 有什么区别? —— 前者逐元素相乘,形状不变;后者行乘列,形状按 (m,n) 乘 (n,p) 得 (m,p)。 2. (3, 4) 的矩阵能乘 (2, 5) 的矩阵吗? —— 不能。中间要相等,也就是第一个的列数 4 得等于第二个的行数 2,这里对不上。 3. 行列式为 0 意味着什么? —— 矩阵不可逆,对应的线性方程组没有唯一解,inv 会报 LinAlgError。矩阵会算了,最后一篇收个尾:统计函数,还有两个一次算完多列的案例。
- ☐ (本篇完,下一篇 6.10 统计函数与两个综合案例)