深入解析 NumPy 与 TensorFlow 中的 Broadcasting 广播机制
本文深入解析 NumPy 与 TensorFlow 中的 Broadcasting 广播机制。通过标量、二维及三维数组的具体案例,详细阐述后缘维度匹配规则、形状扩展逻辑及常见报错处理,帮助开发者高效进行张量运算。
NumPy 与 TensorFlow 广播机制概述
在使用 TensorFlow 进行开发时,经常遇到参与运算的数组形状不一致的情况。然而,有时这些不同形状的数组仍能成功执行加减乘除等运算,这主要归功于 TensorFlow 的 Broadcasting(广播)机制。该机制的设计初衷是效仿 NumPy 中的广播策略,旨在简化多维数组的计算流程。本文将深入探讨 NumPy 和 TensorFlow 中广播机制的具体实现原理与应用场景。
NumPy 广播原理详解
1、NumPy 广播原理
标量与数组的广播计算
1.1 数组与标量的广播计算
当标量与数组进行运算时,会触发基础的广播行为:标量会自动与数组中的每一个元素进行对应的计算操作。
以下是一个简单的示例:
arr = np.arange(5)
arr * 4
计算得到的输出结果为:
array([ 0, 4, 8, 12, 16])
上述标量与数组的运算逻辑较为直观,接下来我们将重点分析两个数组之间进行广播计算的复杂情况。
数组间计算的广播规则
1.2 数组间计算的广播规则
根据相关文档描述,两个数组进行广播的核心规则如下:若两个数组的后缘维度(即从最后一个维度向前计算的维度)的轴长度相等,或者其中一方的长度为 1,则认定这两个数组是广播兼容的。广播操作将在缺失维度或长度为 1 的维度上自动进行扩展。
上述规则表述较为抽象,我们通过几个具体案例来辅助理解:
二维数组的广播情况
假设存在一个二维数组,我们需要计算它减去在 0 轴(行方向)和 1 轴(列方向)上的均值,此时广播机制是如何工作的呢?
首先分析减去 0 轴均值的情况:
arr = np.arange(12).reshape(4,3)
arr-arr.mean(0)
最终输出的结果为:
array([[-4.5, -4.5, -4.5], [-1.5, -1.5, -1.5], [ 1.5, 1.5, 1.5], [ 4.5, 4.5, 4.5]])
0 轴的平均值数组为 [4.5, 5.5, 6.5],其形状为 (3,),而原始数组的形状为 (4, 3)。在执行广播时,系统从后往前比较两个数组的形状:首先比较最后一个维度,3 等于 3,满足条件;继续向前比较时发现其中一个数组的维度已遍历完毕,因此广播会在缺失的 0 轴上进行扩展。
可以将其直观理解为:将均值数组在 0 轴方向上复制 4 份,使其形状变为 (4, 3),然后再与原始数组进行逐元素计算。
书中的插图形象地展示了这一过程(注:图中数据可能与实际示例不同,请忽略具体数值差异):

接下来,我们分析减去 1 轴均值的情况,即每行数据减去该行自身的平均值:
arr - arr.mean(1)
此时程序报错:

再次回顾广播规则:均值数组的形状为 (4,),而原始数组形状为 (4, 3)。按照从后往前的比较规则,4 不等于 3,因此不满足广播条件,导致报错。
正确的处理方式是什么?由于原始数组在 0 轴上的形状为 4,而我们需要比较的是最后一个维度(值为 3),因此均值数组必须有一个维度值为 1 才能满足广播规则。显然,这个值就是 1。所以,我们需要先将均值数组重塑为 (4, 1) 的形状,然后再进行运算:
arr-arr.mean(1).reshape((4,1))
此时得到正确的计算结果:
array([[-1., 0., 1.],
[-1., 0., 1.],
[-1., 0., 1.],
[-1., 0., 1.]])
三维数组的广播情况
三维数组的广播情况
理解了二维数组的广播逻辑后,三维数组的情况便容易推导。
首先参考下图:

根据广播原则分析:arr1 的 shape 为 (3, 4, 2),arr2 的 shape 为 (4, 2)。它们的后缘轴长度均为 (4, 2),因此可以在 0 轴上进行广播。具体而言,arr2 在 0 轴上复制三份,shape 变为 (3, 4, 2),随后进行计算。
不仅 0 轴,1 轴和 2 轴同样可以进行广播,但必须满足特定的形状条件。例如,若 arr1 的 shape 为 (8, 5, 3),想要在 0 轴上广播,则 arr2 的 shape 应为 (1, 5, 3) 或 (5, 3);若要在 1 轴上广播,arr2 的 shape 应为 (8, 1, 3);若要在 2 轴上广播,arr2 的 shape 必须为 (8, 5, 1)。

我们编写几个具体示例来验证:
arr2 = np.arange(24).reshape((2,3,4))
arr3_0 = np.arange(12).reshape((3,4))
print("0轴广播")
print(arr2 - arr3_0)
arr3_1 = np.arange(8).reshape((2,1,4))
print("1轴广播")
print(arr2 - arr3_1)
arr3_2 = np.arange(6).reshape((2,3,1))
print("2轴广播")
print(arr2 - arr3_2)
输出结果如下:
0轴广播 [[[ 0 0 0 0] [ 0 0 0 0] [ 0 0 0 0]]
[[12 12 12 12] [12 12 12 12] [12 12 12 12]]] 1轴广播 [[[ 0 0 0 0] [ 4 4 4 4] [ 8 8 8 8]]
[[ 8 8 8 8] [12 12 12 12] [16 16 16 16]]] 2轴广播 [[[ 0 1 2 3] [ 3 4 5 6] [ 6 7 8 9]]
[[ 9 10 11 12] [12 13 14 15] [15 16 17 18]]]
如果我们希望在两个轴上同时进行广播,arr2 的 shape 需要满足什么条件呢?
| arr1.shape | 广播轴 | arr2.shape |
|---|---|---|
| (8,5,3) | 0,1 | (3,),(1,3),(1,1,3) |
| (8,5,3) | 0,2 | (5,1),(1,5,1) |
| (8,5,3) | 1,2 | (8,1,1) |
具体示例此处不再赘述,供读者自行练习。
TensorFlow 广播示例
2、TensorFlow 广播示例
TensorFlow 中的广播机制与 NumPy 保持一致,因此我们提供几个简单的 TensorFlow 示例:
二维情况的广播
二维情况的广播
sess = tf.Session()
a = tf.Variable(tf.random_normal((2,3),0,0.1))
b = tf.Variable(tf.random_normal((2,1),0,0.1))
c = a - b
sess.run(tf.global_variables_initializer())
sess.run(c)
输出结果为:
array([[-0.1419442 , 0.14135399, 0.22752595], [ 0.1382471 , 0.28228047, 0.13102233]], dtype=float32)
三维情况的广播
三维情况的广播
sess = tf.Session()
a = tf.Variable(tf.random_normal((2,3,4),0,0.1))
b = tf.Variable(tf.random_normal((2,1,4),0,0.1))
c = a - b
sess.run(tf.global_variables_initializer())
sess.run(c)
输出结果为:
array([[[-0.0154749 , -0.02047186, -0.01022427, -0.08932371], [-0.12693939, -0.08069084, -0.15459496, 0.09405404], [ 0.09730847, 0.06936138, 0.04050628, 0.15374713]],
[[-0.02691782, -0.26384184, 0.05825682, -0.07617196], [-0.02653179, -0.01997554, -0.06522765, 0.03028341], [-0.07577246, 0.03199019, 0.0321 , -0.12571403]]], dtype=float32)
错误示例分析
错误示例分析
sess = tf.Session()
a = tf.Variable(tf.random_normal((2,3,4),0,0.1))
b = tf.Variable(tf.random_normal((2,4),0,0.1))
c = a - b
sess.run(tf.global_variables_initializer())
sess.run(c)
运行时报错信息如下:
ValueError: Dimensions must be equal, but are 3 and 2 for 'sub_2' (op: 'Sub') with input shapes: [2,3,4], [2,4].
至此,关于 NumPy 与 TensorFlow 中 Broadcasting 广播机制的探讨告一段落。如需了解更多相关内容,建议查阅本站过往文章或继续浏览后续更新。感谢大家的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









