放送
放送
1. 学習内容
- ❶ 3つの放送ルール
- ❷ スカラー → 1次元 → 2次元のブロードキャスティング
- ❸ 互換性の確認
- ❹ よくある落とし穴
- ❺ 放送とループ演奏の比較
2. ある開発者の実話
(1) 問題点
ボブは、4×3の行列の各行に1×3の行ベクトルを足し込みたいと考えています。そこで、4行のforループを書きましたが、このコードは見栄えが悪く、処理も遅く、インデックスを間違えやすいものです。
(2) 解決策
アリスは彼にこう説明した。「ループは必要ないわ。NumPyは自動的にブロードキャストを行うの。行ベクトルは加算される前に4×3に『ブロードキャスト』されるのよ。」ボブはマーメイドの図を見て、次元がどのように「仮想的に拡張」されるかを即座に理解した。
(3) 成果
ループを削除すると、コードは6行から1行に短縮され、実行速度は50倍以上向上します。ブロードキャストは、NumPyのベクトル化演算の基盤となっています。
3. 放送規則の詳細
(1) 3つのルール
ブロードキャスト機能により、異なる形状の配列でも、次元が自動的に「整列」されるため、算術演算で連携して処理することができ、手動でのデータコピーは不要です。
| ルール | 説明 | 例 |
|---|---|---|
| ルール 1: 揃え | 最も右側の次元から始め、左揃えにし、左側の不足している次元には 1 を埋める | (3,) + (2,3) → (1,3) + (2,3) に埋める |
| ルール 2:サイズ 1 の場合は拡張 | 次元のサイズが 1 の場合、その次元に沿ってコピーし、もう一方の配列と一致させる | (1,3) + (2,3) → (2,3) + (2,3) |
| ルール 3: 他の値は一致しなければならない | サイズ 1 以外の次元は完全に一致しなければならず、一致しない場合はエラーが発生する | (2,3) + (4,3) → ❌ 2≠4 |
クイックリファレンス
| シナリオ | 形状 A | 形状 B | 結果の形状 | 互換性あり? |
|---|---|---|---|---|
| スカラー + 配列 | () |
(3,4) |
(3,4) |
✅ |
| 1D + 2D | (3,) |
(4,3) |
(4,3) |
✅ |
| 列 + 行 | (4,1) |
(1,3) |
(4,3) |
✅ |
| 2D 2つ | (3,1) |
(1,5) |
(3,5) |
✅ |
| 不一致 | (2,3) |
(4,3) |
— | ❌ |
| 展開するサイズ-1なし | (3,) |
(4,) |
— | ❌ |
(2) 放送の手順の可視化
(4,3) 行列と (3,) 行ベクトルの加算:
graph TB
A["Matrix A<br/>shape=(4,3)"] --> D{"Rule 1:<br/>Align dimensions"}
B["Vector B<br/>shape=(3,)"] --> D
D --> E["Pad left dim<br/>B: (3,) → (1,3)"]
E --> F{"Rule 2:<br/>Expand dim=1"}
F --> G["Expand B along axis 0<br/>(1,3) → (4,3)"]
G --> H["A + B_expanded<br/>shape=(4,3)"]
style A fill:#4CAF50,color:#fff
style B fill:#2196F3,color:#fff
style D fill:#FF9800,color:#fff
style F fill:#FF9800,color:#fff
style G fill:#9C27B0,color:#fff
style H fill:#E91E63,color:#fff
(3) 放送の実践
import numpy as np
# Scalar + array
a = np.array([1, 2, 3])
print(a + 10) # [11 12 13]
# 1D + 2D
matrix = np.arange(12).reshape(4, 3)
row = np.array([10, 20, 30])
print(matrix + row)
# [[10 22 34]
# [13 25 37]
# [16 28 40]
# [19 31 43]]
# Column + row (outer operation)
col = np.array([[1], [2], [3], [4]]) # shape (4, 1)
print(col + row)
# [[11 21 31]
# [12 22 32]
# [13 23 33]
# [14 24 34]]
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
4. よくある落とし穴
(1) 形状の不一致
a = np.ones((3, 2))
b = np.ones((2, 3))
# a + b # ValueError: shapes (3,2) and (2,3) not aligned
(2) 予期せぬ放送
a = np.ones((3, 1))
b = np.ones((3,))
# a + b broadcasts to (3, 3) — may not be what you expect!
(3) 期待される結果
# To avoid broadcasting, use explicit shapes
a = np.ones((3, 1))
b = np.ones((3, 1)) # or b[:, np.newaxis]
print((a + b).shape) # (3, 1) — no broadcasting
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(1) ▶ サンプル
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: スカラーから2Dへのブロードキャスト (難易度 ⭐)
import numpy as np
# Scalar broadcasting
arr = np.array([[1, 2, 3],
[4, 5, 6]])
print("arr + 10:\n", arr + 10)
print("arr * 2:\n", arr * 2)
print("arr 2:\n", arr 2)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(2) ▶ サンプル
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: 列 + 行 = 外側演算 (難易度 ⭐⭐)
import numpy as np
# Standardize data: subtract mean, divide by std
data = np.random.randn(5, 3) # 5 samples, 3 features
mean = data.mean(axis=0) # shape (3,)
std = data.std(axis=0) # shape (3,)
standardized = (data - mean) / std # broadcasting!
print("Standardized shape:", standardized.shape) # (5, 3)
print("Mean of standardized:", standardized.mean(axis=0).round(6))
# [ 0. 0. 0.] (within floating point)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
5. 比較表
(1) ブロードキャストとループ
| 次元 | Pythonのforループ | NumPyのブロードキャスティング |
|---|---|---|
| 実行レイヤー | Pythonインタプリタ | Cレベルのufunc |
| 速度 | 低速 (10~100倍) | 高速 |
| コード | for i in range(n): ... |
a + b |
| メモリ | 追加の割り当てなし | 仮想 (データのコピーなし) |
| 読みやすさ | 低 | 高 |
(2) 放送との互換性
| 形状 A | 形状 B | 互換性あり? | 結果 |
|---|---|---|---|
| (3,) | (3,) | ✅ | (3,) |
| (3,) | (1,) | ✅ | (3,) |
| (4,3) | (3,) | ✅ | (4,3) |
| (4,3) | (4,1) | ✅ | (4,3) |
| (4,1) | (1,3) | ✅ | (4,3) |
| (2,3) | (4,3) | ❌ | エラー |
| (3,) | (4,) | ❌ | エラー |
6. 原理図
graph TB
A[Input arrays] --> B[Align dimensions from right]
B --> C{Size matches?}
C -->|Yes| D[Direct operation]
C -->|One is 1| E[Expand size-1 dimension]
E --> D
C -->|Neither is 1| F[ValueError: incompatible]
D --> G[Result array]
style A fill:#e1f5fe
style C fill:#fff9c4
style D fill:#c8e6c9
style F fill:#ffccbc
style G fill:#c8e6c9
(1) ▶ サンプル:newaxis を使った 3D 放送(難易度 ⭐⭐)
import numpy as np
# 3D array + 1D vector
arr_3d = np.arange(24).reshape(2, 3, 4)
vec = np.array([10, 20, 30, 40])
result = arr_3d + vec
print("Shape:", result.shape)
print("First layer:\n", result[0])
# Broadcasting with newaxis
col = np.array([0, 10, 20])[:, np.newaxis]
row = np.array([1, 2, 3, 4])
print("col + row:\n", col + row)
出力:
TEXT形状:(2, 3, 4) 最初の層: [[10 21 32 43] [14 25 36 47] [18 29 40 51]] 列 + 行: [[ 1 2 3 4] [11 12 13 14] [21 22 23 24]]
❓ よくある質問
np.einsum または np.matmul を使用してください。np.tile と np.repeat は、メモリ内でデータを物理的に展開します。一方、ブロードキャスティングは仮想的に展開を行います。ブロードキャスティングの方が処理が速く、メモリ使用量も少なくて済みます。 タイル/リピートは、展開された配列を他の演算に使用する必要がある場合にのみ使用してください。📖 まとめ
- ブロードキャスト処理では、ディメンションを右側から揃え、欠けているディメンションには「1」を埋め込み、サイズが1のディメンションを拡張します
- スカラー演算、行ベクトル/列ベクトル演算、およびデータの正規化では、いずれもブロードキャスティングが用いられる
- ブロードキャストは仮想的な処理であり、実際にはデータがコピーされることはなく、ストライドのメタデータのみが変更されます
- よくある落とし穴:形状が一致しないことによる意図しないブロードキャスト、
[:, np.newaxis]の使用を忘れること - ブロードキャストは、Pythonのループよりも常に高速であり、明示的な展開よりもメモリ使用量が少ない
📝 練習問題
-
初心者(難易度 ⭐):4×3の行列と1×3の行ベクトルを作成し、それらを足し合わせます。次に、4×1の列ベクトルを作成し、それを行列に足し合わせます。その結果の形状を確認してください。
-
中級(難易度 ⭐⭐):形状が (5, 4, 3) の3次元配列と、形状が (3,) の1次元配列を作成してください。 これらを足し合わせてください。どの次元がブロードキャストされるかを説明してください。次に、(4, 3)の配列を3次元配列に足し合わせてみてください。うまくいきますか?
-
上級(難易度 ⭐⭐⭐):各データポイントに5つの特徴量を持つデータポイントを1000個生成します。ブロードキャスティングを使用してデータを正規化(平均値を引いて、標準偏差で割る)します。次に、Pythonのforループを使用して同じ処理を実装します。実行時間を比較してください。