广播机制 (Broadcasting)
1. 定义
广播 (Broadcasting) 是 NumPy / PyTorch 里的一套规则:当两个形状不同的数组做逐元素运算时,自动把小的那个”拉伸/复制”到和大的对齐,从而无需手写循环。
一句话:形状不一样也能直接算,NumPy 会自动把维度补齐。 比如一个矩阵每行都加同一个向量,不用循环,直接
matrix + vector就行。
2. 解决了什么问题
没有广播,形状不同就得手动扩展或写循环:
# 想给 (3,3) 矩阵每一行都加上 [1,2,3]
# 笨办法:循环
for i in range(3):
A[i] = A[i] + np.array([1, 2, 3])
# 广播:一行搞定,还更快
A + np.array([1, 2, 3])广播让代码更短、更快(底层 C 实现,无 Python 循环),是向量化编程的核心。
3. 广播三条规则
两个数组做运算时,NumPy 从最右边的维度开始逐维比较:
- 维度数不同:给短的那个在左边补 1,直到维度数相同
(3,)和(2,3)→(3,)补成(1,3)
- 每一维比较:这一维两者要么相等,要么其中一个是 1
- **是 1 的那一维被”拉伸”**到另一个的大小(逻辑上复制,不真占内存)
若某一维两者既不相等、也都不是 1 → 报错(不兼容)。
示例:(2,3) + (3,)
(2, 3)
( 3) → 左补1 → (1, 3)
─────────
逐维:2 vs 1 → 拉伸 1 到 2 ✅ ;3 vs 3 → 相等 ✅
结果:(2, 3)
4. 常见例子
import numpy as np
# ① 矩阵 + 标量(标量广播到每个元素)
A = np.array([[1, 2, 3], [4, 5, 6]]) # (2,3)
A + 10 # 每个元素 +10
# ② 矩阵 + 行向量(每行都加这个向量)
A + np.array([10, 20, 30]) # (2,3)+(3,) → 每行加 [10,20,30]
# ③ 矩阵 + 列向量(每列都加)—— 需要 reshape 成 (2,1)
col = np.array([100, 200]).reshape(2, 1) # (2,1)
A + col # 每行分别加 100 / 200
# ④ 行向量 + 列向量 → 外积式扩展成矩阵
row = np.array([1, 2, 3]) # (3,) → (1,3)
col = np.array([10, 20]).reshape(2, 1) # (2,1)
row + col # (2,3):每个位置 = col[i] + row[j]5. 一个高频实战:数据标准化
广播最典型的用途——对每一列做归一化(减均值除标准差):
X = np.random.randn(100, 5) # 100 个样本,5 个特征
mean = X.mean(axis=0) # (5,) 每列均值
std = X.std(axis=0) # (5,) 每列标准差
X_norm = (X - mean) / std # (100,5) - (5,) → 广播,每行都减/除
(100,5)和(5,)相减,(5,)被广播成(1,5)再拉伸到(100,5)——一行代码完成对所有样本的标准化。
6. 常见坑
- 行向量 vs 列向量搞错:
(3,)默认当行向量(1,3)广播;要按列广播必须reshape(-1,1)变成列向量 - 一维数组的歧义:
(3,)和(3,1)、(1,3)广播行为不同,明确意图时显式 reshape - 意外的形状爆炸:
(1000,1) + (1,1000)会得到(1000,1000)的大矩阵,可能瞬间爆内存 - 以为报错其实静默广播:本想逐元素运算两个”应该同形”的数组,结果一个是
(n,)一个是(n,1),被广播成(n,n),结果全错但不报错——多用.shape检查 - 广播不复制内存但结果会:广播本身不占额外内存,但运算结果是新数组,大形状仍会占内存
7. 延伸阅读 / 关联概念
- NumPy 基础 — 广播依附于 NumPy 数组;见
numpy-basics.md - 矩阵运算 — 广播用于逐元素运算,和矩阵乘法
@是不同机制;见matrix-operations.md - PyTorch — 张量广播规则与 NumPy 完全一致;见
torch.md - 向量化 (Vectorization) — 用数组运算替代 Python 循环的编程范式,广播是其基石
- 官方文档:https://numpy.org/doc/stable/user/basics.broadcasting.html