Numpy 广播 详解

2020/2/5 numpy

# 广播概念介绍

术语广播描述了 numpy 在算术运算期间如何处理具有不同形状的数组。受到某些约束,较小的数组将在较大的数组上“广播”,以便它们具有兼容的形状。广播提供了一种对数组操作进行矢量化的方法,从而使循环在 C 而不是 Python 中发生。它无需复制不必要的数据即可完成此操作,通常可以实现高效的算法实现。但是,在某些情况下,广播不是一个好主意,因为广播会导致内存使用效率低下,从而减慢计算速度。

NumPy 操作通常在逐个元素的数组对上完成。 在最简单的情况下,两个数组必须具有完全相同的形状,如以下示例所示:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> a * b
array([ 2.,  4.,  6.])
1
2
3
4

当阵列的形状满足某些约束条件时,NumPy 的广播规则将放宽此约束条件。 在操作中将数组和标量值组合在一起时,会出现最简单的广播示例:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0
>>> a * b
array([ 2.,  4.,  6.])
1
2
3
4

结果前面的示例相同,其中b是一个数组。 我们可以认为标量b在算术运算中被拉伸为与a形状相同的数组。 b中的新元素只是原始标量的副本。 延伸类推只是概念上的。 NumPy 足够聪明,可以使用原始标量值,而无需实际制作副本,因此广播操作尽可能节省内存并提高计算效率。

第二个示例中的代码比第一个示例中的代码更有效,因为广播在乘法过程中会移动较少的内存(b是标量而不是数组)。

# 一般广播规则

在两个数组上运算时,NumPy 逐元素比较其形状。 它从末尾维开始,一直向前计算。 两种维兼容当

  • 他们相等,或者
  • 其中之一为 1

如果不满足这些条件,则将引发ValueError: operands could not be broadcast together(ValueError:操作数不能一起广播的异常),这表明数组的形状不兼容。 所得数组的大小是沿每个输入轴的大小,而不是 1。

数组不必具有相同数量的维。 例如,如果您具有256x256x3的 RGB 值数组,并且希望将图像中的每种颜色缩放一个不同的值,则可以将图像乘以带有 3 个值的一维数组。 根据广播规则来排列这些数组的尾轴的大小,表明它们是兼容的:

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

当比较的任一维度为一个维度时,将使用另一个维度。 换句话说,维为 1 的维将被拉伸或“复制”以匹配彼此。

在以下示例中,AB数组均具有长度为 1 的轴,这些轴在广播操作期间会扩展为更大的尺寸:

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
1
2
3

这里还有更多示例:

A      (2d array):  5 x 4
B      (1d array):      1
Result (2d array):  5 x 4

A      (2d array):  5 x 4
B      (1d array):      4
Result (2d array):  5 x 4

A      (3d array):  15 x 3 x 5
B      (3d array):  15 x 1 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 1
Result (3d array):  15 x 3 x 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

以下是无法广播的形状示例:

A      (1d array):  3
B      (1d array):  4 # trailing dimensions do not match

A      (2d array):      2 x 1
B      (3d array):  8 x 4 x 3 # second from last dimensions mismatched
1
2
3
4
5

实践中的广播示例:

>>> x = np.arange(4)
>>> xx = x.reshape(4,1)
>>> y = np.ones(5)
>>> z = np.ones((3,4))

>>> x.shape
(4,)

>>> y.shape
(5,)

>>> x + y
ValueError: operands could not be broadcast together with shapes (4,) (5,)

>>> xx.shape
(4, 1)

>>> y.shape
(5,)

>>> (xx + y).shape
(4, 5)

>>> xx + y
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.],
       [ 4.,  4.,  4.,  4.,  4.]])

>>> x.shape
(4,)

>>> z.shape
(3, 4)

>>> (x + z).shape
(3, 4)

>>> x + z
array([[ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

广播提供了一种获取两个数组的外部乘积(或任何其他外部操作)的便捷方法。 以下示例显示了两个 1-d 数组的外部加法运算:

>>> a = np.array([0.0, 10.0, 20.0, 30.0])
>>> b = np.array([1.0, 2.0, 3.0])
>>> a[:, np.newaxis] + b
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])
1
2
3
4
5
6
7

在这里,newaxis索引运算符将新轴插入到a中,使其成为4x1二维数组。 将4x1数组与形状为(3,)b组合会产生4x3数组。

Last Updated: 2023-10-29T08:26:04.000Z