问题: NumPy中如何执行矩阵乘法,有哪些常用方法?

👇 点击展开/折叠 面试背诵模板(建议先背这个) 推荐使用 @ 操作符或 np.matmul 执行矩阵乘法,规则为“前行乘后列”累加。形状匹配规则:第一个矩阵的列数必须等于第二个矩阵的行数,结果形状为“第一个矩阵行数 × 第二个矩阵列数”。注意区分星号 * 是逐元素相乘(对应位置),不是矩阵乘。一维数组参与乘法时会自动降维,结果为1D数组。
---

三种计算方式的直观对比

操作 语法 计算方式 示例 A(2,2) * B(2,2)
逐元素乘 A * B 对应位置相乘 [[1*5, 2*6], [3*7, 4*8]]
矩阵乘 A @ B 行×列求和 [[1*5+2*7, 1*6+2*8], [3*5+4*7, 3*6+4*8]]
矩阵乘(函数) np.matmul(A, B) 同 @ 同上

形状匹配核心规则

两个矩阵能做乘法,只看中间两个数字:

  • A(2行, 3列) 乘 B(3行, 4列) → 中间的 3 必须相等,结果形状为 (2行, 4列)
  • 不满足则报错:ValueError: shapes (2,3) and (5,4) not aligned

一维数组的特殊行为(注意点)

NumPy 的一维数组 (N,) 在矩阵乘中会被灵活处理:

1
2
3
4
5
6
import numpy as np
A = np.array([[1,2],[3,4]]) # (2,2)
x = np.array([1,2]) # (2,)

print(A @ x) # [5, 11] 二维×一维 → 一维(结果丢掉了行列壳)
print(x @ A) # [7, 10] 一维×二维 → 一维
  • 二维 @ 一维:右侧一维被视作列向量,结果降维为一维
  • 一维 @ 二维:左侧一维被视作行向量,结果降维为一维

高维批量乘法(深度学习常用)

多个矩阵同时相乘时,@ 会保留前面的“批次维度”:

1
2
3
4
A = np.random.rand(32, 2, 3)   # 32个 2×3 矩阵
B = np.random.rand(32, 3, 4) # 32个 3×4 矩阵
C = A @ B # 结果 (32, 2, 4)
# 每个批次独立做矩阵乘,最后一批次维度不变

常见误区提醒

  • 千万别用 * 做矩阵乘,它是逐个位置相乘,不是线性代数里的乘法。
  • 看到一维数组参与运算时,心里默认它没有行/列之分,结果也会丢掉行列,这是正常的。
  • 大矩阵优先用 @ 或 matmul,底层调 BLAS 库,速度比手写循环快成千上万倍。

🔥 第一关:先分清“两种乘法”(这是你混乱的根源)

符号 名字(土话) 干什么用的? 你的代码里是哪个?
* (星号) “对应位相乘”(元素级乘法) 两个表格同一位置的数字相乘。形状不同时,触发你刚学的广播机制(复印)。 你PPT里的 a * b 就是这个。
@ (或者 np.dot) “矩阵乘法”(线性代数乘法) 左边表格的行 和 右边表格的列 做“搭配组合”。 你报错的 np3 @ np4 就是这个。

一句话救命:看见 * 就想着 “一对一”;看见 @ 就想着 “行对列,乘完求和”。


第二关:矩阵乘法(@)到底在算啥?(大白话+动图)

矩阵乘法不是对应位置相乘!它的规则是:“左行的元素,逐个去碰右列的元素,碰完加总。”

举个生活中的例子(卖菜记账本)

假设你是食堂采购员:

  • 矩阵A(菜价表):3行(代表3个菜市场),2列(代表2种菜:土豆、西红柿)。
菜市场 土豆(元/斤) 西红柿(元/斤)
市场1 2 3
市场2 4 1
市场3 5 2
  • 矩阵B(采购单):2行(2种菜),3列(代表周一到周三的采购量)。
菜品 周一(斤) 周二(斤) 周三(斤)
土豆 1 4 2
西红柿 2 1 3

问:市场1在周一的买菜总花费是多少钱?
你的大脑会立刻算:(土豆价2 × 周一土豆量1) + (西红柿价3 × 周一西红柿量2) = 2 + 6 = 8。

矩阵乘法 A @ B 干的就是这件事——一次性把所有组合算完!


✍️ 第三关:手把手教你怎么算(口诀)

口诀:“左行固定,右列滑动,乘完加和,放进格子。”

拿上面的例子,结果是一个 3行(市场数) × 3列(天数) 的矩阵。

第1步:算“市场1 在 周一”的花费(结果第1行第1列)

  • 拿出 A的第1行 [2, 3]
  • 拿出 B的第1列 [1, 2]
  • 计算:(2×1) + (3×2) = 2 + 6 = 8

第2步:算“市场1 在 周二”的花费(结果第1行第2列)

  • 拿出 A的第1行 [2, 3]
  • 拿出 B的第2列 [4, 1]
  • 计算:(2×4) + (3×1) = 8 + 3 = 11

一直算下去,直到把 A的3行 和 B的3列 全部搭配完,得到最终结果:

市场\天数 周一 周二 周三
市场1 8 11 13
市场2 6 17 11
市场3 9 22 16

🚧 第四关:为什么你的 np3 @ np4 报错了?(关键!)

矩阵乘法的铁律(入场券):

左边矩阵的“列数” 必须 等于 右边矩阵的“行数”。
因为你要把左边的“行元素”和右边的“列元素”一一配对相乘,数量对不上,就没法配对!

看你之前的报错代码:

  • np3 形状是 3行 2列。
  • np4 形状是 3行 2列。

套用铁律:

  • 左边的列数是 2
  • 右边的行数是 3
  • 2 ≠ 3,配不上对! 所以电脑直接报错,拒绝计算。

那为什么加了个 .T(转置)就行了?

  • np4.T 把表格横过来了,形状变成了 2行 3列。
  • 现在左边的列数是 2,右边的行数也是 2。对上了!
  • 所以 np3 @ np4.T 结果就是 3行(左边的行数) × 3列(右边的列数),得到一个 3x3 的方阵。

🧠 终极灵魂总结(给你装进脑子里)

  1. 看符号认亲妈:

    • 看到 * 号 → 用广播规则(对应位置相乘,小的复印)。
    • 看到 @ 或 np.dot → 用矩阵规则(左行碰右列,乘完求和)。
  2. 矩阵乘法只看两数:

    • 写出来 (m行, n列) @ (n行, p列),中间的 n 必须相等!
    • 结果一定是 **(m行, p列)**。
  3. 现实意义:

    • *(广播):给表格里每个格子都做相同的加减乘除(比如全场打8折)。
    • @(矩阵):做系统性的组合变换(比如根据单价和数量算总价,或者图片旋转、神经网络计算)。

现在你再回头看看那个报错,是不是觉得特别清晰了?(3,2) 和 (3,2) 中间是 2 和 3,不相等,所以 @ 不伺候,但 * 因为广播规则能伺候(因为形状兼容)。记住:*管对应,@管组合! 👍