---
tags: [chapter, ch03, GNN, message-passing]
status: 完成
confidence: high
sources:
  - ../repo-analysis/mace.md
last_updated: 2026-06-11
---

# Chapter 3：Graph Neural Network 與 Message Passing

> **一句話**：GNN 把結構變成圖——原子是 node、r_cut 內的鄰近關係是 edge——再用兩三輪 message passing 讓每個原子「認識」自己的配位環境；最後每個原子各出一份能量、加總成總能，力則是能量的梯度。

## 這章要解決的問題

神經網路吃的是**固定長度的向量**，但化學結構天生不配合：原子數不固定（8 顆水分子和 800 原子的 slab 要用同一個模型）、原子沒有順序（把第 3 號和第 7 號原子在輸入檔裡對調，物理上是同一個結構）、座標系任意（整體平移旋轉，能量不准變）。

怎麼把這種「不定長、無順序、座標系任意」的東西餵給神經網路？答案是**圖表示＋訊息傳遞（message passing）**。這是 MACE、CHGNet、MatterSim 共同的底層語言——這章一次講通，後面三章就只剩各家的口音差異。

## 給化學系學生的直覺說法

**原子是人，cutoff 半徑內的鄰居是「認識的人」。** message passing 就是大家**同時**跟認識的人打聽消息：

- 第 1 輪結束：每個原子知道自己的**第一配位圈**長什麼樣。
- 第 2 輪結束：因為鄰居在第 1 輪已經吸收了「鄰居的鄰居」的資訊，所以每個原子**間接**知道了第二配位圈。

這個「局部打聽」的設計有化學根據：**原子的能量主要由局部配位環境決定**——化學鍵是短程的、金屬中的擾動有屏蔽。這就是為什麼 cutoff 取 5 Å 左右、傳兩輪，對大多數體系就夠了。你不陌生：配位數、配位場、d-band center，全都是「局部環境決定性質」的同一信仰。

**請現在就打開互動 demo**：[`interactive/gnn-message-passing.html`](../../interactive/gnn-message-passing.html)。操作劇本：(1) 點選頂端的 H 原子當中心，看 cutoff 虛線圈住哪些鄰居；(2) 按「下一步」兩次，看 H 的特徵色環從純色逐步混入鄰居的顏色；(3) 把 r_cut 滑桿拉到 2.0 Å，看「孤島原子」警告——一個沒有鄰居的原子，模型對它的環境一無所知；(4) 走到第 3 步，看每個原子各自吐出 ε，加總成 E_total。這四個動作就是本章全部的核心概念。

## 核心概念

### 1. 圖表示（graph representation）

- **node（節點）＝原子**，攜帶一個特徵向量，初始值由元素種類決定（見 embedding）。
- **edge（邊）＝「距離 < r_cut」的鄰近關係**，攜帶距離與方向資訊。注意：edge 的判據只有距離——它**不是化學鍵**。金屬裡沒有定域共價鍵，照樣建圖建得理直氣壯。
- 這個「找出 r_cut 內所有鄰居」的操作，就是 ASE 的 neighbor list——你跑 RDF 時早就用過。

### 2. Embedding：元素的「向量身分證」

最簡單的元素表示是 one-hot（Fe = [1,0,0,…]），但現代 MLIP 用**可學習的 embedding 向量**：訓練過程中，模型自己學會把化學行為相似的元素放在向量空間的相近位置——像一張由資料自己長出來的週期表。這也是 foundation model 能涵蓋 89 種元素的基礎設施。

### 3. Message passing 更新式（讀懂即可，不用會推）

每一輪兩步——先**聚合**鄰居的訊息，再**更新**自己：

$$\mathbf{m}_i = \sum_{j \in N(i)} \phi\big(\mathbf{h}_j,\, r_{ij}\big) \qquad\qquad \mathbf{h}_i' = \psi\big(\mathbf{h}_i,\, \mathbf{m}_i\big)$$

中文逐符號：$\mathbf{h}_j$ 是鄰居 j 目前的特徵；$\phi$ 決定「鄰居的話要怎麼聽」（依距離 $r_{ij}$ 加權）；$\sum$ 把所有鄰居的話加起來（求和保證了「鄰居沒有順序」）；$\psi$ 決定「聽完之後我變成什麼樣」。$\phi、\psi$ 裡的權重就是模型要學的參數。demo 裡我們用「自己＋鄰居取平均」當 $\phi,\psi$ 的最簡替身——原理一模一樣，只是真模型的權重是學出來的。

### 4. 感受野（receptive field）

傳 k 輪，每個原子的資訊半徑就是 $k \times r_{cut}$。MACE 兩輪、r_cut=5 Å → 有效感受野約 10 Å。這同時是能力也是限制：**10 Å 以外的世界，模型看不到**——長程靜電、長波長的彈性場，天生在它的視野外（長程修正是進階主題，入門先知道邊界在哪）。

### 5. Readout：每原子能量與它的代價

$$E = \sum_i \varepsilon_i\big(\mathbf{h}_i^{(L)}\big)$$

總能量＝每個原子依自己最終特徵各出一份能量、加總。這個設計讓同一個模型天然適用任何大小的體系（8 原子或 8 萬原子都是「加總」）。代價是一個規範自由度：**單一原子的 ε_i 沒有絕對物理意義**（你可以把每個 ε 平移、總和不變），只有總能差有意義——這正對應你熟的「吸附能必須同參考系相減」。

### 6. Force = −∇E：免費而且守恆的力

力不是另外訓練的輸出，是能量對座標的**自動微分**：$\mathbf{F}_i = -\partial E / \partial \mathbf{r}_i$。好處有二：力場保證保守（能量守恆的 MD 的前提）；訓練時力標籤的梯度資訊直接回傳到能量模型。notebook 第 7 節用數值微分驗證了這件事到 10⁻¹⁰ 的精度。

## 和 DFT / 材料 / 催化的關係

- 「局部環境決定能量」是 GNN 與你既有化學直覺的握手點：d-band center 是用一個數字描述局部環境，GNN 是用一個學出來的高維向量描述同一件事——**表達力更強，代價是不可直接解讀**。
- **HEA 伏筆**：HEA 的本質就是「每個位點的局部成分環境都不同」。GNN 天生逐位點編碼局部環境，所以它不只能算 HEA，而且是描述「局部成分效應」最自然的語言——Ch6 的 active-site distribution 直接踩在這個性質上。
- E=Σε 的規範自由度提醒你：比較不同模型（或模型 vs DFT）的能量，**永遠比較能量差**（吸附能、反應能、相對穩定性），不要比較絕對值。

## 最小數學

上面三條式子（聚合、更新、readout）就是全部。一個常被問的點：為什麼聚合用「求和」而不是「串接」？因為求和對鄰居的順序不敏感（permutation invariant）——把鄰居列表洗牌，結果不變。對稱性不是裝飾，是把物理寫進架構。

## 最小 code

**Level 1（pseudo-code）**

```text
graph = build_neighbor_list(structure, r_cut)
h     = embed(elements)                  # 初始特徵
for round in 1..2:
    for i in atoms:
        m[i] = aggregate(h[j] for j in neighbors(i))
    h = update(h, m)
E = sum(per_atom_energy(h_i) for i in atoms)
F = -grad(E, positions)
```

**Level 2（minimal Python：20 行玩具 message passing）**

```python
import numpy as np

pos  = np.array([[0,0],[2.5,0],[5.0,0],[1.25,2.2]])   # 4 個原子（2D 示意）
elem = ["Ni","Fe","Ni","H"]
ELEMS = ["Ni","Fe","H"]
h = np.array([[float(e==x) for x in ELEMS] for e in elem])  # one-hot 特徵

r_cut = 3.0
adj = [[j for j in range(len(pos))
        if j != i and np.linalg.norm(pos[i]-pos[j]) < r_cut]
       for i in range(len(pos))]

for round_ in range(2):                      # 兩輪 message passing
    h_new = h.copy()
    for i, nbrs in enumerate(adj):
        m = h[nbrs].sum(axis=0)              # 聚合：鄰居特徵求和
        h_new[i] = (h[i] + m) / (1 + len(nbrs))   # 更新：含自己取平均
    h = h_new

print(np.round(h, 2))   # 每列＝一個原子「打聽完」的環境組成
```

跑一次、改 `r_cut` 再跑一次——你會看到互動 demo 裡同一件事的數字版。

**Level 3（real repo pointer）**

- 晶體 → 圖的工業級實作：CHGNet 的 `chgnet/graph/converter.py`（含週期性邊界處理）
- 等變版的 message passing 模組：MACE 的 `mace/modules/`（進階選讀；先看懂本章，再看它就只是「把純量特徵升級成帶方向的特徵」）

## 常見誤解

- **「edge 就是化學鍵。」** edge 只代表「距離夠近」。鍵級、共價性，模型若需要會自己從資料學，不靠你畫鍵。
- **「message passing 是在模擬電子轉移。」** 它是資訊聚合的數學操作，不對應任何物理過程。別在組會上說「電子傳了兩輪」。
- **「輪數越多越好。」** 感受野變大，但成本上升、特徵被過度平均（over-smoothing），梯度也更難訓。MACE 的解法是提高**單輪**訊息的階數（Ch4），而不是堆輪數。
- **「GNN 能看到長程靜電。」** cutoff 外天生看不到。離子液體、帶電表面、鐵電體要小心，必要時找帶長程修正的變體。

## 小練習

1. 在互動 demo 把 r_cut 從 5.0 Å 調到 3.0 Å 再到 2.0 Å：圖怎麼變？哪個原子最先變孤島？它的特徵在兩輪後是什麼？（驗證你的預測。）
2. 兩輪傳遞後，中心 H 原子「知道」哪些原子的存在？先在紙上推（一跳鄰居、二跳鄰居各是誰），再用 demo 的金色高亮環驗證。
3. 用 Level 2 的玩具碼：把第 4 個原子（H）移到 (10, 10)（遠離所有人），重跑。它的特徵向量是什麼？這對應 demo 裡的什麼現象？對「模型預測它的能量」意味著什麼？

## 延伸閱讀

- Batatia et al., "MACE: Higher Order Equivariant Message Passing Neural Networks", *NeurIPS* (2022)：§2 的架構圖配著本章讀，不必推導
- [repo-analysis/mace.md](../repo-analysis/mace.md)：「化學系研究生必懂的 5 個概念」一節

## 我學完後應該能說出什麼

- 「node / edge / cutoff / embedding / message passing / readout 六個詞各是什麼，並能用配位圈的語言講給同學聽。」
- 「為什麼力是能量的梯度而不是另外訓練的輸出，以及這保證了什麼。」
- 「感受野怎麼算、它劃出了模型『看得到』與『看不到』的物理邊界。」

---

### 本章配套

- 互動 demo：[`interactive/gnn-message-passing.html`](../../interactive/gnn-message-passing.html) ✓ 已驗證
- 投影片：[`slides/ch03-gnn.html`](../../slides/ch03-gnn.html)
- 練習題（含詳解）：[`exercises/ch03.md`](../../exercises/ch03.md)
