一、廣播的定義

廣播是指 NumPy 在計算時,將較小的數組 “廣播” 到較大數組的形狀,以便兩者可以進行 element-wise(元素級)操作的機制。

換句話説,當兩個數組形狀不同時,NumPy 會嘗試將它們調整為相同的形狀,然後再執行運算。

二、廣播的規則

NumPy 的廣播機制遵循一套嚴格的規則,以確定兩個數組是否可以進行廣播,以及如何進行廣播。

廣播規則( Broadcasting Rules):

為了確定兩個數組是否兼容,NumPy 會從尾部開始比較它們的形狀維度。

  1. 維度兼容
  • 如果兩個數組的維度數量不同,NumPy 會在維度較少的數組前面補 1,直到兩個數組的維度數量相同。
  • 然後,比較它們的每一對維度大小。對於每一對維度,以下情況視為兼容:a. 兩個維度大小相等。b. 其中一個維度大小為1
  1. 結果形狀
  • 如果兩個數組根據上述規則是兼容的,那麼廣播後的結果數組的形狀,將在每一個維度上取兩個數組對應維度大小的最大值
  1. 廣播過程
  • 維度大小為 1 的數組,會在其維度上進行複製,以匹配另一個數組對應維度的大小。

三、廣播的示例

讓我們通過幾個例子來理解這些規則。

示例 1:標量(0-D 數組)與數組

這是最常見也最簡單的廣播場景。

python

運行

import numpy as np

a = np.array([1, 2, 3])
b = 2

result = a + b

print(f"a: {a}, shape: {a.shape}")
print(f"b: {b}, shape: {b.shape}")
print(f"a + b: {result}, shape: {result.shape}")

輸出:

plaintext

a: [1 2 3], shape: (3,)
b: 2, shape: ()
a + b: [3 4 5], shape: (3,)

分析:

  1. a 的形狀是 (3,)b 的形狀是 ()
  2. 規則 1:b 維度較少,前面補 1,形狀變為 (1,)。現在比較 (3,) 和 (1,)
  3. 規則 1a/b:3 和 1 是兼容的。
  4. 規則 2:結果形狀是 (max(3,1),) -> (3,)
  5. 規則 3:b(值為 2)在其維度(大小為 1)上被複制 3 次,變成 [2, 2, 2],然後與 a 相加。

示例 2:二維數組與一維數組

python

運行

import numpy as np

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

result = a + b

print(f"a:\n{a}, shape: {a.shape}")
print(f"b: {b}, shape: {b.shape}")
print(f"a + b:\n{result}, shape: {result.shape}")

輸出:

plaintext

a:
[[1 2 3]
 [4 5 6]], shape: (2, 3)
b: [10 20 30], shape: (3,)
a + b:
[[11 22 33]
 [14 25 36]], shape: (2, 3)

分析:

  1. a 的形狀是 (2, 3)b 的形狀是 (3,)
  2. 規則 1:b 維度較少,前面補 1,形狀變為 (1, 3)。現在比較 (2, 3) 和 (1, 3)
  3. 規則 1a/b:2 和 1 兼容,3 和 3 兼容。
  4. 規則 2:結果形狀是 (max(2,1), max(3,3)) -> (2, 3)
  5. 規則 3:b 的形狀從 (1, 3) 廣播到 (2, 3),即第一行 [10, 20, 30] 被複制到第二行,然後與 a 相加。

示例 3:兩個數組都需要廣播

python

運行

import numpy as np

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

result = a + b

print(f"a:\n{a}, shape: {a.shape}")
print(f"b:\n{b}, shape: {b.shape}")
print(f"a + b:\n{result}, shape: {result.shape}")

輸出:

plaintext

a:
[[1 2]
 [3 4]], shape: (2, 2)
b:
[[10]
 [20]], shape: (2, 1)
a + b:
[[11 12]
 [23 24]], shape: (2, 2)

分析:

  1. a 的形狀是 (2, 2)b 的形狀是 (2, 1)
  2. 規則 1:維度數量相同,直接比較。2 vs 2 (兼容), 2 vs 1 (兼容)。
  3. 規則 2:結果形狀是 (2, 2)
  4. 規則 3:b 的形狀從 (2, 1) 廣播到 (2, 2),即每一列 [10] 和 [20] 被複制到第二列,然後與 a 相加。

示例 4:不兼容的情況

python

運行

import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6]])
b = np.array([10, 20])

# 嘗試相加
try:
    result = a + b
except ValueError as e:
    print(f"錯誤: {e}")

輸出:

plaintext

錯誤: operands could not be broadcast together with shapes (2,3) (2,)

分析:

  1. a 的形狀是 (2, 3)b 的形狀是 (2,)
  2. 規則 1:b 補 1 後形狀變為 (1, 2)。現在比較 (2, 3) 和 (1, 2)
  3. 比較維度:
  • 從尾部開始:3 vs 2
  • 3 和 2 既不相等,也沒有一個是 1。不兼容
  1. 因此,廣播失敗,拋出 ValueError

四、廣播的優勢

  1. 代碼簡潔:避免了繁瑣的 for 循環,使代碼更易讀、更優雅。
  2. 性能高效:廣播是在 C 語言級別實現的,比 Python 原生的循環快得多。它避免了不必要的數據複製,直接在原始內存上進行操作。
  3. 內存高效:當一個小數組廣播到一個大數組時,NumPy 並不會實際創建一個更大的數組副本,而是在邏輯上 “擴展” 它,從而節省內存。

五、總結

數組廣播是 NumPy 中一個極其重要的特性,它使得對不同形狀數組進行算術運算變得非常方便和高效。

核心要點:

  • 廣播遵循一套從右到左的維度兼容性規則。
  • 維度大小為 1 的數組可以被 “複製” 以匹配另一個數組的維度。
  • 如果兩個數組的維度在任何位置都不兼容(既不相等也非 1),則廣播失敗。
  • 熟練掌握廣播規則,能讓你寫出更簡潔、更高效的 NumPy 代碼。