NumPy矩阵运算:广播机制

NumPy中的广播机制类似于我们日常做算术题,但区别在于我们不再是对单个数字操作,而是直接对整个数组进行。设想你有两个形状相同的数组,比如数组a和数组b,它们就像两个大小相同的盒子,里面装着数量一致的糖果。此时,若你将a和b中对应位置的糖果相乘,每个糖果都会找到其配对伙伴,共同“变身”为新的糖果(实际上是生成了新的数组元素)。
然而,这种对应相乘的前提是两个数组的形状必须一致,就像两个盒子的大小必须相同,且内含糖果的数量也要相等,以确保每个糖果都能找到其唯一的配对。
因此,广播是NumPy中一种让形状相同的数组能够直接进行对应算术运算的便捷方式,其操作过程与我们日常做算术题一样直观简单。

理解广播机制


例如:

import numpy as np
a = np.array([1, 2, 3])
print(a*2)
# 运行结果为array([2,4,6])

在NumPy中,当你对数组执行与标量(在这个例子中是数字2)的乘法操作时,这个操作会自动地应用于数组中的每一个元素。这种操作被称为广播(broadcasting),它是NumPy中的一个核心概念,允许NumPy在执行数组运算时更加灵活和高效。

具体来说,a*2这个操作会将数组a中的每个元素都乘以2。因此,数组a中的1变成了2,2变成了4,3变成了6。结果是一个新的NumPy数组,包含元素[2, 4, 6]。

再看一个例子:

import numpy as np
a = np.array([[0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]])
b = np.array([1, 2, 3])
print(a+b)

输出结果为:
array([[ 1, 2, 3],
[11, 12, 13],
[21, 22, 23],
[31, 32, 33]])

在上面的例子中,数组a是一个4x3的二维数组,而数组b是一个一维数组,长度为3。在NumPy中,当你尝试对形状不匹配的数组执行操作时,广播机制会尝试将它们“对齐”以便能够执行元素级操作。
在这个例子中,b数组的形状是(3,),而a数组的形状是(4, 3)。为了使这两个数组能够相加,NumPy会将b数组“扩展”或“广播”成一个形状为(4, 3)的数组,这个新数组的每一行都是b数组的副本。然后,这个被广播的b数组就可以与a数组进行元素级的加法操作了。

具体来说,广播后的b数组看起来像这样:

[[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]

现在,可以将这个被广播的b数组与a数组相加:

a + b =
[[0, 0, 0] + [1, 2, 3],
[10, 10, 10] + [1, 2, 3],
[20, 20, 20] + [1, 2, 3],
[30, 30, 30] + [1, 2, 3]]
= [[1, 2, 3],
[11, 12, 13],
[21, 22, 23],
[31, 32, 33]]

因此,打印的结果将是:

[[ 1 2 3]
[11 12 13]
[21 22 23]
[31 32 33]]

广播规则

例如:

import numpy as np
a = np.array([[0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]])
b = np.array([0, 1, 2, 3])
print(a+b)

在上面的例子中,a 是一个 4x3 的二维数组,而 b 是一个一维数组,长度为 4。当执行 a + b 操作时,NumPy 的广播机制会尝试将这两个数组“对齐”以便进行元素级加法操作。

但是b 数组的维度(一维)与 a 数组的第二个维度(列数)不匹配,而是与 a 数组的第一个维度(行数)相匹配。在 NumPy 的广播规则中,如果两个数组的维度不完全相同,NumPy 会从数组的最后一个维度开始向前比较,并尝试在这些维度上找到兼容性。

因此NumPy 不会将 b 数组视为一个列向量并尝试将其扩展为与 a 的列数相同的形状(即 4x3)。而是将 b 视为一个行向量,并将其广播到与 a 的行数相同的形状(即 4x1), b数组扩展后, 其长度不足以匹配 a 的任何一行(因为 a 的每行有三个元素,而 b 只有四个元素)。这导致了形状不匹配的错误。

import numpy as np

a = np.array([[0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]])
b = np.array([0, 1, 2, 3])

# 将 b 转换为一个列向量
b = b[:, np.newaxis]

# 现在 a 和 b 的形状可以广播了

print(a+b)

这里 b[:, np.newaxis] 的作用是增加一个轴,使得 b 的形状从 (4,) 变为 (4, 1),这样它就可以被广播为 (4, 3) 以匹配 a 的形状。

 

为了解决这个问题,需要确保 b 在进行加法操作时被视为一个列向量。这可以通过在 b 上添加一个新的轴来实现,修正后的代码: