numpy广播规则

解释一下 numpy 广播规则,这是一个之前很容易混淆的点,而且很多资料我感觉完全没解释清楚。

广播规则

结合官方网址(网址1)的资料和 AI 的解释,我整理的一般的广播规则如下(和官网意思一下,我觉得下面这样写更好理解)

  1. 如果两个数组的维度数不同,将维度较小的那个数组的形状在前面(左边)补1,直到两数组维度相同。
  2. 对于每个维度,从最右边的维度开始比对,如果两数组在该维度上的大小相等,或者其中一个数组在该维度上的大小为1,则这两个数组在该维度上是兼容的。
  3. 如果两个数组在所有维度上都兼容,则它们可以广播。
  4. 广播后,每个数组的形状将取两个数组在每个维度上的最大值。
  5. 在任何一个维度上,如果一个数组的大小为1,而另一个大于1,则将该维度大小为1的数组拉伸(复制)到与另一个数组相同的大小。

如果不能广播,则会给出下面的报错提示

1
ValueError: operands could not be broadcast together

例如,如果你有一个 256×256×3 的 RGB 值数组,并希望将图像中每个颜色分别按不同值进行缩放,你可以将图像乘以一个包含三个值的一维数组。根据广播规则对这些数组的后向轴大小进行对齐后,可以看出它们是兼容的:

1
2
3
Image  (3d array): 256 x 256 x 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3

当比较的两个维度中有一个为1时,就使用另一个维度。换句话说,大小为1的维度会被拉伸或“复制”以匹配另一个。

在以下示例中,数组A和B的轴长度均为1,在广播操作期间被扩展为更大的尺寸:

1
2
3
A      (4d array):  8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 8 x 7 x 6 x 5

更多例子

1d数组 和 2d数组的广播举例如下

1
2
3
4
5
6
a = np.array([[ 0.0,  0.0,  0.0],
[10.0, 10.0, 10.0],
[20.0, 20.0, 20.0],
[30.0, 30.0, 30.0]])
b = np.array([1.0, 2.0, 3.0])
a + b

输出为

1
2
3
4
array([[  1.,   2.,   3.],
[11., 12., 13.],
[21., 22., 23.],
[31., 32., 33.]])

一个错误例子为

1
2
b = np.array([1.0, 2.0, 3.0, 4.0])
a + b

报错信息为

1
ValueError: operands could not be broadcast together with shapes (4,3) (4,) 

下面是这两个例子的广播示意图

这里和数组的存储顺序无关(默认按行存储,也就是 C 顺序),下面按照该 F 顺序也是一样的

1
2
3
4
5
6
a = np.array([[ 0.0,  0.0,  0.0],
[10.0, 10.0, 10.0],
[20.0, 20.0, 20.0],
[30.0, 30.0, 30.0]], order='F')
b = np.array([1.0, 2.0, 3.0], order='F')
a + b

甚至我测试一个数组按 C 顺序,一个按 F 顺序,都可以。

1
2
3
4
5
6
7
8
a = np.array([[ 0.0,  0.0,  0.0],
[10.0, 10.0, 10.0],
[20.0, 20.0, 20.0],
[30.0, 30.0, 30.0]], order='F')
b = np.array([1.0, 2.0, 3.0])
c = a + b
print(c)
print(c.flags['F_CONTIGUOUS'])

输出如下

1
2
3
4
5
[[ 1.  2.  3.]
[11. 12. 13.]
[21. 22. 23.]
[31. 32. 33.]]
True

内存顺序(C 还是 F)只影响数据在内存里的物理排布,不影响广播。

order 在什么情况下才有影响?

order 只在两件事上有实质影响 :

1. 性能 / 缓存局部性:NumPy 的 ufunc 底层是 C 循环,遍历顺序如果和内存布局匹配,缓存命中率高、速度快;不匹配则慢。但对 a + b 这种小规模运算,肉眼感知不到差别。

2. 某些就地操作(in-place)可能触发复制:NumPy 2.x 对内存布局不兼容的就地广播赋值会更严格,有时静默复制 。但你这里是 a + b 产生新数组,不是 a += b 这种就地写回,所以不涉及。

💡 一句话:广播的合法性由 shape 决定,order 只影响底层遍历效率。两个数组一个 C、一个 F,只要 shape 兼容,运算就合法,结果也正确;NumPy 会通过各自的 stride 信息去正确的内存位置取数,不需要你干预 。

所以你完全可以放心混用——在实际项目中,从 HDF5 文件读进来的 Fortran 顺序数组,和内存里默认的 C 顺序数组直接相加,是再常见不过的场景。

广播提供了对两个数组进行外积(或其他任何外运算)的便捷方法。以下示例展示了两个一维数组的外加法运算:

1
2
3
a = np.array([0.0, 10.0, 20.0, 30.0])
b = np.array([1.0, 2.0, 3.0])
a.reshape(4,1) + b

输出为

1
2
3
4
array([[ 1.,  2.,  3.],
[11., 12., 13.],
[21., 22., 23.],
[31., 32., 33.]])

参考资料

  1. https://numpy.org/doc/2.0/user/basics.broadcasting.html
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2026 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信