阅读(4.9k) 书签 (0)

什么是Broadcasting

2018-12-09 11:17 更新

Broadcasting

术语 Broadcasting 描述了在算术运算中 numpy 如何处理不同形状的数组.在受到某些约束的情况下,较小的数组在较大的数组上“broadcasting”,以便它们具有兼容的形状.broadcasting 提供了一种向量化数组操作的方法,使循环发生在 C 而不是 Python 中.这样做不会造成不必要的数据副本,通常会导致高效的算法实现.但是,在有些情况下,broadcasting 是一个坏主意,因为它会导致内存使用效率低下,从而减慢计算速度.

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.])

当数组的形状满足一定的约束时,NumPy 的 broadcasting 规则放松了这个约束.最简单的 broadcasting 示例是在一个操作中组合一个数组和一个标量值时发生的:

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

这个结果等同于前面的例子,其中 b 是一个数组.我们可以认为标量 b 在算术运算过程中被拉伸成与 a 形状相同的数组,b 中的新元素只是原始标量的副本.拉伸的比喻只是概念上的.NumPy 足够聪明,可以使用原始标量值,而不需要实际制作副本,因此 broadcasting 操作尽可能具有记忆效率和计算效率.

第二个例子中的代码比第一个例子中的代码效率更高,因为在乘法(b是一个标量而不是一个数组)中,广播移动的内存较少.

一般broadcasting规则

在两个数组上操作时,NumPy 按照元素比较它们的形状.它从尾随的维度开始,并朝着前进的方向前进.两个维度是兼容的:

  1. 他们是相等的;
  2. 其中有一个是1

如果不满足这些条件, 则会引发异常,ValueError: frames are not aligned,表明数组具有不兼容的形状.结果数组的大小是沿输入数组的每个维度的最大大小.

数组不需要有相同数量的维度.例如,如果您有一个 256x256x3的 RGB 值的数组,并且您想通过不同的值缩放图像中的每个颜色,则可以将图像乘以一个具有3个值的一维数组.根据 broadcasting 规则排列这些数组尾随轴的大小,表明它们是兼容的:

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

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

在以下示例中,在 broadcasting 操作期间A,B数组都具有长度为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

以下是一些例子:

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

以下是不能 broadcasting 的形状的示例:

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

 broadcasting 实践中的一个例子:

>>> 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
<type 'exceptions.ValueError'>: shape mismatch: objects cannot be broadcast to a single shape

>>> 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.]])

 broadcasting 为两个数组的外部产品(或任何其他外部操作)提供了一种方便的方法.以下示例显示了两个一维数组的外部附加操作:

>>> 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.]])

这里的 newaxis 索引操作符将一个新的轴插入到 a 中,使其成为一个二维 4x1 数组.将 4x1 数组与 b 组合在一起,它有形状(3,),生成一个4x3数组.