2.15 Python 中的广播
还是用原本视频的例子最为贴近来解释关于广播的矩阵算法。
这是一个不同食物(每 100g)中不同营养成分的卡路里含量表格,表格为 3 行 4 列,列表示不同的食物种类,从左至右依次为苹果,牛肉,鸡蛋,土豆。行表示不同的营养成分,从上到下依次为碳水化合物,蛋白质,脂肪。
那么,我们现在想要计算不同食物中不同营养成分中的卡路里百分比。
现在计算苹果中的碳水化合物卡路里百分比含量,首先计算苹果(100g)中三种营养成分卡路里总和 56+1.2+1.8 = 59,然后用 56/59 = 94.9%算出结果。
可以看出苹果中的卡路里大部分来自于碳水化合物,而牛肉则不同。
对于其他食物,计算方法类似。首先,按列求和,计算每种食物中(100g)三种营养成分总和,然后分别用不用营养成分的卡路里数量除以总和,计算百分比。
按照正常的逻辑,我们会用显性的for循环,但是我们之前也说了,要用矩阵算法就会运行的快一些,也不用写那么多的代码。我们打算使用两行代码完成,第一行代码对每一列进行求和,第
二行代码分别计算每种食物每种营养成分的百分比。
所以先写下这个数组
下面使用如下代码计算每列的和,可以看到输出是每种食物(100g)的卡路里总和。
其中 sum 的参数 axis=0 表示求和运算按列执行。
这里解释一下axis 用来指明将要进行的运算是沿着哪个轴执行,在 numpy 中,0 轴是垂直的,也就是列,而 1 轴是水平的,也就是行。
接下来计算百分比,这条指令将 3 × 4的矩阵除以一个1 × 4的矩阵,得到了一个 3 ×
4的结果矩阵,这个结果矩阵就是我们要求的百分比含量。
现在才是真正的用到广播的时候了。
这里再贴一下广播的规则:
维度对齐:从数组的最右侧维度(末尾)开始对齐,逐维度比较。
维度兼容性:对于每一对维度:维度大小相等,或其中一个维度的大小为 1。
缺失维度补1:如果两个数组维度数不同,在形状较短数组的左侧补 1。
只有所有维度都满足上述条件时,广播才成立。
看着麻烦,其实我们来看一个例子就知道了
这里是一个2 × 3的矩阵和一个 1 × 3 的矩阵相加,在执行加法操作时,其实是将1*n的矩阵复制成为m*n的矩阵,然后两者做逐元素加法得到结果。针对这个具体例子,相当于在矩阵的第一列加 100,第二列加 200,第三列加 300。这就是在前一张幻灯片中计算卡路里百分比的广播机制。
很简单不是吗,就像我们所说的向量的加减
看下面这个图总结一下,所有广播的基本情况都在这了。
在拓展一下广播和一维数组的关系,在行向量和列向量的创建的时候会出现细节上的问题:
一维数组的广播歧义
一维数组在运算时会被 NumPy 自动补全维度,导致广播方向不明确。
import numpy as np
a = np.array([1, 2, 3]) # 形状 (3,)
b = np.array([[4], [5]]) # 形状 (2, 1)
result = a + b # 广播歧义!可能报错或得到意外结果
这里 a 被补全为 (1, 3),而 b 形状是 (2, 1),最终试图广播为 (2, 3),可能会对齐规则冲突导致错误
一维数组与二维数组的维度不匹配
A = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
b = np.array([10, 20]) # 形状 (2,)
# 错误情况
result = A + b # 报错:ValueError: operands could not be broadcast together with shapes (2,3) (2,)
# 正确做法
b = b.reshape(-1, 1) # 将 b 转换为列向量 (2, 1)
result = A + b
print(result)
# 输出:
# [[11 12 13]
# [24 25 26]]
所以经过这两个细节我们要注意的是:
避免一维数组:在涉及矩阵运算时,始终用二维数组(行/列向量)明确维度。
检查形状:使用 arr.shape 确认数组维度是否符合预期。
使用 np.newaxis 或 reshape:强制指定行或列的方向
不要羞涩于使用reshape,否则一维数组不直观的时候,你根本难以定位bug所在位置。
总结一下就是
那么到这里我们第二周的课程也结束了,下一篇起我们就要讲浅层神经网络了。如果你看到了这里,那真的很感谢你愿意看我写的文章,希望对你有所帮助,感谢阅读,我们下一篇见!