什么是广播机制?
在 NumPy 中,广播(broadcasting)是一种非常强大的功能。广播机制使得我们在执行数组算术运算(如加、减、乘、除等)时,可以处理形状不同的数组。简单来说,广播机制帮助我们解决不同形状数组之间的运算问题。
例如,当一个数组与一个常数(标量)相加,或一个二维数组与一个一维数组相乘时,广播机制会自动扩展维度,使得它们的形状匹配,从而可以执行加、减、乘、除等操作。
当对两个数组进行算术操作时,NumPy 会逐个元素比较它们的形状。需要注意的是,只有在以下情况下,两个数组才能进行广播:
- 形状完全相同的两个数组:比如两个形状都是 (3, 4) 的数组。
- 维度不同时,两个数组的 “后缘维度” 相同:这里的 “后缘维度” 是指数组形状的最后一个维度。例如,形状为 (3, 4) 和 (4,) 的数组,它们的后缘维度相同,可以进行广播。
- 维度相同,其中一个数组某个维度的长度为 1:在这种情况下,NumPy 会将长度为 1 的维度 “拉伸” 以匹配另一个数组相应维度的长度。
- 较少维度的数组会自动补上维度,直到它们的维度数相同:例如,形状 (3, 4) 和 (4,) 可以通过补充维度后变为 (3, 4) 和 (1, 4),使其形状一致。
如果两个数组的形状不符合这些规则,NumPy 会抛出 ValueError 异常,表示形状不兼容。
在 NumPy 中,广播机制应用非常广泛,主要包括以下几个方面:
- 数组与标量运算:比如为所有数组元素加上一个常数。
- 不同维度的数组运算:比如二维数组与一维数组相加。
- 机器学习中的特征归一化、矩阵 - 向量计算。
- 图像处理中多通道操作。
NumPy 广播应用
接下来,我们通过几个简单的例子来讲解一下 NumPy 广播机制是怎样的。
1. 数组与标量的广播
在 NumPy 中,当一个数组与一个标量进行算术运算时,标量会被广播到数组的每一个元素上。也就是说,此时数组每个元素都会对标量进行相同运算操作。
示例 1:数组与标量的广播
import numpy as np
arr = np.array([1, 2, 3])
scalar = 5
result = arr + scalar
print(arr)
print(scalar)
print(result)运行结果如下。
[1 2 3]
5
[6 7 8]分析:
在这个例子中,标量 5 被广播到了数组 [1, 2, 3] 的每一个元素上,从而使得数组中每个元素都进行加 5 的操作。
2. 二维数组与一维数组的广播(行广播)
在 NumPy 中,当 “一个形状为 (m, n) 的二维数组” 与 “一个形状为 (n, ) 的一维数组” 进行算术运算时,一维数组会沿着二维数组的每一行进行广播。
示例 2:行广播
import numpy as np
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr1d = np.array([10, 20, 30])
result = arr2d + arr1d
print(arr2d)
print(arr1d)
print(result)运行结果如下。
[[1 2 3]
[4 5 6]
[7 8 9]]
[10 20 30]
[[11 22 33]
[14 25 36]
[17 28 39]]分析:
在这个例子中,一维数组 [10, 20, 30] 被广播到了二维数组的每一行上,从而实现了二维数组的每一行都加上这个一维数组的操作。
3. 二维数组与一维数组的广播(列广播)
在 NumPy 中,如果我们想要实现列广播,需要确保一维数组的形状与二维数组的列数相匹配,或者使用 reshape() 函数将一维数组转换为列向量(形状为 (m, 1))。
示例 3:列广播
import numpy as np
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr1d = np.array([10, 20, 30]).reshape(3, 1)
result = arr2d + arr1d
print(arr2d)
print(arr1d)
print(result)运行结果如下。
[[1 2 3]
[4 5 6]
[7 8 9]]
[[10]
[20]
[30]]
[[11 12 13]
[24 25 26]
[37 38 39]]分析:
在这个例子中,我们使用 reshape(3, 1) 将一维数组转换为列向量后,它会被广播到二维数组的每一列上,从而实现了二维数组的每一列都加上这个列向量的操作。
4. 形状不兼容的广播
在 NumPy 中,如果两个数组的形状不满足广播规则,则会直接报错。
示例 4:形状不兼容的广播
import numpy as np
arr1 = np.array([[1, 2],
[3, 4]])
arr2 = np.array([10, 20, 30])
try:
result = arr1 + arr2
print(result)
except ValueError as e:
print(e)运行结果如下。
(报错)operands could not be broadcast together with shapes (2,2) (3,)分析:
在这个例子中,arr1 的形状是 (2, 2),而 arr2 的形状是 (3, ),此时 arr1 和 arr2 的形状是不兼容的,因此无法进行广播操作。
最后来总结一下,如果想要判断两个数组是否可以广播,我们可以按照以下步骤:
- 比较两个数组的维度数。如果维度数不同,将维度较小的数组在其形状前部补上 1,直到维度数相同。
- 从后向前比较两个数组的形状。
- 对于每个维度,如果长度相等,或者其中一个数组的长度为 1,则它们在该维度上是兼容的。
- 如果所有维度都兼容,则可以进行广播。否则,不可以进行广播。
