<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Justin Huang (繁體中文)</title><description>這是 Justin Huang 的個人部落格，寫 AI、技術、寫作，也寫生活。</description><link>https://justinhuangai.github.io/</link><item><title>技術報告共讀：《Attention Residuals》（注意力殘差）</title><link>https://justinhuangai.github.io/zh-hant/posts/attention-residuals/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/attention-residuals/</guid><description>Kimi Team 的 Attention Residuals 技術報告：為什麼殘差連接也該「注意力化」，以及 Full AttnRes / Block AttnRes 如何把這個想法做成可訓練、可部署的系統</description><pubDate>Thu, 19 Mar 2026 08:49:27 GMT</pubDate><content:encoded>2026 年 3 月 16 日，Kimi Team 在 arXiv 上傳了一篇技術報告：[《Attention Residuals》](/papers/2603.15031v1.pdf)（注意力殘差）。

從整份報告的結構就能看出作者真正的重心：不是只發一個新模組，而是按 `motivation -&gt; AttnRes -&gt; Block AttnRes -&gt; infrastructure -&gt; experiments -&gt; discussion` 的順序，把「殘差連接到底在做什麼」這件事重新講了一遍。

## 0. 先認幾個詞

如果你完全沒有機器學習背景，可以順著這篇報告真正關心的問題，按下面這個順序先建立一個直覺：

- `Transformer`：今天大多數大模型的基礎架構。你可以先把它理解成一台一層一層處理資訊的機器。
- `hidden state`：模型在某一層裡的內部中間表示。可以粗略理解成「模型此刻腦子裡的臨時筆記」。
- `residual connection / 殘差連接`：層與層之間的一條「保留舊內容」的通道。它會先把上一層的內容留住，再把這一層新算出來的東西加上去。
- `residual / 殘差`：更接近「這一層新補上去的增量」，也就是上面那條殘差連接裡新增的那一部分。
- `attention`：從很多資訊裡，挑出「當前最該看哪一部分」的機制。你可以先把它記成「有選擇地看重點」。
- `PreNorm`：在進入一層之前，先把數值尺度調勻，再做後續計算。可以把它想成「先把音量調到合適，再繼續混音」。

## 1. 一句話說清楚

這份技術報告問了一個非常鋒利的問題：

**既然 Transformer 已經用 attention 取代了「序列維度上的遞迴」，為什麼大模型在「深度維度上的資訊聚合」還停留在固定加法？**

現代 LLM 幾乎都在用一種很常見的層結構：先做 PreNorm，再走 residual。直白地說，就是先把數值尺度調勻，再把這一層新算出來的結果加回原輸入。大家熟悉它的一個功能，是讓訓練過程更穩定，深層網路不那麼容易失控。但作者提醒我們，殘差連接其實還有另一個同樣重要、卻長期被忽視的角色：

**它定義了資訊怎樣沿著深度被彙總。**

如果下面的式子看不熟，不用卡住，直接看後面的「翻譯成人話」就夠了。

標準殘差的規則很簡單：

$$
h_l = h_{l-1} + f_{l-1}(h_{l-1})
$$

這裡可以直接把兩部分拆開看：

- $h_{l-1}$：舊內容，也就是上一層已經有的表示
- $f_{l-1}(h_{l-1})$：這一層新算出來的增量，更接近「殘差」這個詞本身

而把這兩部分重新加在一起的整條做法，才更準確地叫「殘差連接」。

把這個遞推式展開，你會得到：

$$
h_l = h_1 + \sum_{i=1}^{l-1} f_i(h_i)
$$

翻譯成人話就是：第 $l$ 層看到的輸入，本質上是「embedding 加上前面所有層輸出的統一加總」。每一層的權重都是 1，沒有選擇，沒有抑制，沒有「這一步我更該看第 3 層還是第 17 層」的機制。

AttnRes 的核心思想只有一句話：

**把 residual 從固定加法，改成沿深度做一次 softmax attention。**

## 2. 舊殘差到底哪裡有問題

這份技術報告最重要的地方，不在於它提出了一個新公式，而在於它把一個大家已經習慣了的東西重新問題化了。

標準殘差長期被視為「訓練穩定性工具」。只要能讓梯度過得去，它就算完成任務了。但從資訊流角度看，這條路徑其實非常粗糙。

想像你在寫一份持續迭代的文件。每一輪修改，你都不是「挑出最 relevant 的舊版本內容再整合」，而是把之前所有版本一股腦全文追加到文件末尾。第 20 輪的時候，前 3 輪的重要洞察當然還在，但它們已經淹沒在越來越厚的堆疊裡了。

PreNorm 的問題就在這裡。報告引用了 SiameseNorm 的觀察，並進一步強調：在 PreNorm 下，`hidden state` 的量級會隨著深度近似按 $O(L)$ 增長。這裡的 hidden state，說白了就是模型每一層裡那份「內部筆記」。結果就是：

- 越往後的層，看到的是一個越來越膨脹的「歷史總和」
- 早期層的資訊雖然沒有消失，但會被不斷稀釋
- 後面層如果還想「發出聲音」，就被迫輸出更大的量級

這篇技術報告把這個現象叫 `PreNorm dilution`。這是一個非常準確的命名。不是梯度斷了，不是模型炸了，而是每一層的相對貢獻被越來越稀。

報告裡有一句我很喜歡的潛台詞：我們在序列維度上早就不滿足於「所有過去 token 一視同仁」了，所以才有了 attention；那為什麼到了深度維度，卻還能接受「所有過去層統一權重相加」？

## 3. AttnRes 到底做了什麼

AttnRes 的形式很乾淨。第 $l$ 層不再機械地接收「前面所有層輸出的總和」，而是對這些歷史表示做一次加權選擇：

$$
h_l = \sum_{i=0}^{l-1} \alpha_{i \to l} \cdot v_i
$$

其中權重 $\alpha_{i \to l}$ 來自一層 softmax。你可以先把 softmax 理解成「把一組分數壓成一組權重，而且所有權重加起來等於 1」，這樣模型才能明確表達「更該看誰、少看誰」：

$$
\alpha_{i \to l} = \operatorname{softmax}\left(w_l^T \operatorname{RMSNorm}(k_i)\right)
$$

如果你沒接觸過 attention，還有一個最省力的理解方式：

- `query`：當前這一層現在想找什麼
- `key`：每一層歷史資訊各自貼著什麼「索引標籤」
- `value`：最後真正被取回來、參與彙總的內容

這裡最值得注意的設計有三個。

第一，**query 不是當前 hidden state 現算出來的，而是每層一個可學習的 pseudo-query 向量 $w_l$。**  
這有點反直覺。我們平時看到 attention，會自然以為 query 必須來自當前輸入。但作者故意把 query 設計成層級參數，而不是 token 級動態向量。這樣做的好處是：同一個 block 裡的多個 query 可以提前批量算，後面的基礎設施優化才有空間做。

第二，**key/value 直接來自前面層的輸出。**  
也就是說，真正帶來「輸入相關性」的不是 query，而是各層當前樣本上的表示本身。不同樣本經過前面層後得到的 key 不一樣，所以最後的深度注意力依然是 input-dependent 的。

第三，**key 前面加了 RMSNorm。**  
這是個很關鍵的小設計。因為如果不做歸一化，量級大的層會天然在點積裡佔便宜，你得到的就不是「誰更 relevant」，而更像「誰聲音更大」。

用 Python（基於 PyTorch）寫出來，大概就是這樣：

```python
import torch
from torch import nn


def attention_residual(
    sources: list[torch.Tensor],
    pseudo_query: torch.Tensor,
    norm: nn.RMSNorm,
) -&gt; torch.Tensor:
    keys = torch.stack([norm(source) for source in sources], dim=0)
    values = torch.stack(sources, dim=0)

    logits = keys @ pseudo_query
    weights = torch.softmax(logits, dim=0)
    return (weights.unsqueeze(-1) * values).sum(dim=0)
```

這個式子看上去像是「把 attention 用在 residual 上」。但我覺得更準確的說法是：

**它把 residual connection 從「固定的累加器」改成了「可選擇的深度檢索器」。**

## 4. 這份報告最妙的地方：它不是只給想法，也給工程

如果報告只寫到 Full AttnRes，這還只能算一個漂亮的 research idea。

Full AttnRes 讓每一層都 attend 到前面所有層，理論上很好理解，實際上也不算太貴。因為網路深度 $L$ 通常遠小於序列長度 $T$，所以作者說，單純算術量 $O(L^2 d)$ 並不是最可怕的問題。

真正的問題出現在大訓練裡：

- activation recomputation 會把本來可以丟掉的中間層輸出重新變成必須保存的對象
- pipeline parallelism 會讓這些跨層表示需要跨 stage 傳輸
- 一旦每層都要看所有前層，通訊和快取壓力會快速上去

所以他們又提出了 **Block AttnRes**。

做法是把 $L$ 層切成 $N$ 個 block。block 內部先用普通求和攢成一個 block representation，跨 block 再做 attention。這樣一來：

- Full AttnRes：看的是所有歷史層
- Block AttnRes：看的是所有歷史 block 的摘要，再加當前 block 的部分和

本質上是用「摘要級跨層注意力」換取可擴展性。

這還沒完。作者不是只說「我們分塊了，所以省記憶體」，而是真的把系統層的帳也算清楚了：

- 訓練階段用 **cross-stage caching**，避免 pipeline 裡重複傳歷史 block
- 推理階段用 **two-phase computation**
- 第一階段並行算 inter-block attention
- 第二階段順序算 intra-block lookback，再用 online softmax merge 合併

從附錄和 `table/memory_access.tex` 裡能看到最硬核的一組數字。按報告給的典型設定：

- 標準 residual：每層 residual 機制 I/O 是 `3d`
- naive Full AttnRes：`130d`
- 優化後的 Full AttnRes：`24d`
- Block AttnRes：`5.5d`
- mHC：`34d`

這組數字特別說明問題。Block AttnRes 不是「便宜到跟標準 residual 一樣」，但它已經從「明顯不現實」降到了「工程上值得試」。而且報告實測給出的代價也不大：

- 訓練端 wall-clock overhead 小於 4%
- 推理端 latency overhead 小於 2%

這就是我說它像一篇真正的系統級技術報告的原因。很多 paper 的問題在於「idea 是新的，帳是糊的」；這篇在帳本上反而做得很用力。

## 5. 實驗最該看什麼

### 5.1 縮放定律：不是偶然贏一把

作者先做了五個模型規模的 scaling law 實驗，對比 Baseline、Full AttnRes 和 Block AttnRes。

擬合出來的曲線是：

- Baseline：$1.891 \times C^{-0.057}$
- Block AttnRes：$1.870 \times C^{-0.058}$
- Full AttnRes：$1.865 \times C^{-0.057}$

這三條曲線最重要的資訊不是「斜率差了多少」，而是：

**AttnRes 在整個 compute 範圍裡都持續更低。**

報告給了一個很容易傳播的結論：在 `5.6 PFLOP/s-days` 這個預算點，Block AttnRes 的 loss 相當於 baseline 多花 `1.25x` 算力才能達到的水準。

換句話說，這不是「在某個模型大小上碰巧調對了」，而是有比較穩定的規模收益。

### 5.2 大模型主實驗：不是玩具規模

主實驗不是小模型 toy benchmark，而是基於 Kimi Linear 的一個大配置：

- `48B total / 3B activated parameters`
- 27 個 Transformer blocks，也就是 54 層
- 8-of-256 routed experts + 1 個 shared expert
- 預訓練 `1.4T tokens`

這說明作者不是只在「小模型上做漂亮曲線」，而是真的把這個 residual 改造塞進了一個大訓練配方裡。

### 5.3 最能說明問題的圖：輸出量級不再失控

正文裡最打動我的其實不是 benchmark 表，而是訓練動態那張圖。

Baseline 的 output magnitude 會隨著深度一路漲上去。訓練動態圖裡給的數值非常誇張：從前面 block 的 `0.04`、`0.06`、`0.10`，一路漲到後面 block 的 `10.47`、`12.15`。這就是 PreNorm dilution 的視覺化版本。

Block AttnRes 則完全不是這條曲線。它在 block 邊界形成一種週期性重置，量級大致在 `0.21` 到 `1.91` 之間波動，沒有出現一路失控上揚。

這非常重要，因為它說明 AttnRes 不是只在最後 benchmark 上「多拿了幾分」，而是真正在訓練動力學層面改變了表示如何沿深度堆積。

### 5.4 下游任務：提升最明顯的是推理和程式碼

預訓練後，AttnRes 在報告列出的全部評測上都不差於 baseline，幾個最亮眼的點包括：

- MMLU：`73.5 -&gt; 74.6`
- GPQA-Diamond：`36.9 -&gt; 44.4`
- Math：`53.5 -&gt; 57.1`
- HumanEval：`59.1 -&gt; 62.2`
- C-Eval：`79.6 -&gt; 82.5`

這裡最值得注意的是 GPQA、Math、HumanEval 這種多步推理或程式生成任務漲幅更大。報告作者的解釋是：如果後層能更有選擇地回收前層表示，那麼 compositional tasks 會更受益。我覺得這個解釋是說得通的。

因為複雜推理最怕的不是「資訊不存在」，而是「資訊在網路很深的地方被埋住了」。

## 6. 消融實驗告訴了我們什麼

這份報告的消融做得不錯，因為它不只是證明「有用」，還試圖證明「為什麼有用」。

幾個最有意思的結論：

- **DenseFormer 1.767，幾乎和 baseline 1.766 一樣。**  
  這說明「能訪問所有前層」本身還不夠，關鍵在於權重是不是 input-dependent。

- **mHC 到了 1.747，已經明顯變好。**  
  這說明深度維度上的動態混合確實有效。

- **Full AttnRes 到了 1.737。**  
  它比 baseline、DenseFormer、mHC 都更低，說明顯式的 softmax depth attention 是更強的一條路。

- **SWA（只看最近窗口）只有 1.764。**  
  這很有價值。它說明 AttnRes 的收益不只是「多看最近幾層」，而是「能選擇性地看更遠的層」。

- **Block size 從 2、4、8 變化時，loss 都在 1.746 左右。**  
  這就是為什麼作者最後固定大約 8 個 blocks。不是拍腦袋，而是工程和效果之間一個相當好的 sweet spot。

- **input-dependent query 版本做到 1.731，比 Full AttnRes 還好。**  
  這一點非常耐人尋味。它說明當前報告裡的 pseudo-query 設計並不是性能上限，而是一個為基礎設施優化讓路的折衷。也就是說，作者不是不知道更強的寫法，而是主動選了更容易擴展的寫法。

這正是我覺得這份報告有意思的原因。你從正文、消融和系統設計裡能更清楚地看到他們的真實取捨：不是盲目追求最低 loss，而是在追求「足夠強，同時真能訓起來」。

## 7. 我怎麼看這份報告

第一，這份報告最重要的，不是它發明了一個新模組，而是它把 residual connection 從「訓練穩定性工具」重新提升成了「資訊路由機制」。

這個視角一旦建立起來，很多東西都會被重新理解。殘差不再只是 gradient highway，它還是 depth aggregation rule。你會開始追問：

- 每一層到底能不能選擇性地訪問前層？
- 深度維度上有沒有 attention sink？
- 舊的 residual 變體本質上是不是 depth-wise linear attention？

而這正是報告討論部分真正厲害的地方。作者把一堆殘差變體統一進了一個 `depth mixing matrix` 的視角裡，進一步指出：

**很多已有方法，本質上都像是在深度維度上做 linear attention；AttnRes 做的是 depth-wise softmax attention。**

這個說法非常大膽，但也非常有啟發性。它等於是在說：Transformer 當年把序列維度從 recurrence 推到了 softmax attention；AttnRes 試圖把深度維度也推進一步。

第二，這篇技術報告的氣質很像「先把問題提對，再把系統做順」。它沒有執著於把每個部件都做到最 fancy。比如 query 故意做成 layer-specific 參數，而不是 token-dependent 動態向量，性能上未必絕對最強，但它給了 batching、two-phase computation、pipeline cache 一個成立的基礎。很多時候，一篇能落地的技術報告，靠的不是最激進的局部設計，而是整體約束下的取捨。

第三，我覺得這份報告真正值得記住的不是某個 benchmark，而是這句話：

**Why is depth-wise aggregation still fixed while everything else has become adaptive?**

這問得太對了。

## 8. 這份報告的邊界

再誇一句之前，也得把邊界說清楚。

第一，它目前是 **technical report / arXiv preprint**，不是已經過同行評審的會議論文。寫這類文章時，最穩妥的態度不是「它已經證明了未來」，而是「它提出了一個很強的視角，並給出了一套有工程可行性的實作」。

第二，它的大規模結果主要建立在 Kimi Linear 這條架構線上：MoE、KDA/MLA 混合注意力、Moonlight / DeepSeek-V3 風格訓練配方。雖然這不削弱結果本身，但也意味著我們還不能自動把結論外推到所有 dense decoder-only Transformer。

第三，報告自己也承認：Full AttnRes 其實更強，Block AttnRes 是今天硬體約束下的工程解。未來如果顯存、頻寬、interconnect 再往前走，或者更高效的 depth attention 變體出現，今天這版 Block 設計很可能不是終點。

所以我對它的判斷是：

- 它已經夠強，值得認真讀
- 它已經夠完整，值得認真做複現
- 它還沒有強到可以立刻蓋棺定論

## 9. 最後的感受

如果把過去十年大模型架構的主線粗暴地概括一下：

- Seq2Seq 在問：怎麼把一個序列壓成另一個序列？
- Bahdanau 在問：為什麼生成時不能回頭看輸入的不同位置？
- Transformer 在問：為什麼序列建模一定要靠遞迴？
- Chinchilla 在問：為什麼更多算力一定主要砸到參數量上？

那《Attention Residuals》（注意力殘差）問的是：

**為什麼深度上的資訊彙總，還停留在「所有歷史層統一加總」的時代？**

這個問題問出來，本身就已經很有價值。

我不確定幾年後 AttnRes 會不會像 PreNorm 一樣成為預設配置，但我很確定，這篇技術報告把 residual connection 重新變成了一個值得被思考、被設計、被優化的對象。

以前大家說 attention 改寫了序列建模。

這份技術報告在嘗試改寫 residual。

2026 年春，Kimi 團隊的工作已經說明：當Scaling Laws 開始顯出逼近瓶頸的跡象時，LLM 的結構創新將持續湧現。

---

**延伸閱讀**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 怎樣花算力最划算</content:encoded><category>Technical Report Reading</category><category>technical-report-reading</category><category>residual-connections</category><category>transformer</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>論文共讀：《Training Compute-Optimal Large Language Models》（訓練算力最優的大型語言模型）</title><link>https://justinhuangai.github.io/zh-hant/posts/training-compute-optimal-large-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/training-compute-optimal-large-language-models/</guid><description>Chinchilla 論文：為什麼大多數大模型其實訓練不足，以及如何聰明地分配算力預算，附真實 Python 核心程式碼</description><pubDate>Wed, 11 Mar 2026 08:58:04 GMT</pubDate><content:encoded>2022 年 3 月 29 日，DeepMind 的一組研究者在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文：[《Training Compute-Optimal Large Language Models》](/papers/2203.15556v1.pdf)（訓練算力最優的大型語言模型）。

第一作者是 Jordan Hoffmann，共同作者包括 Sebastian Borgeaud、Arthur Mensch、Elena Buchatskaya、Trevor Cai、Eliza Rutherford 等人：當時全部在 DeepMind。其中 Arthur Mensch 後來共同創立了 Mistral AI，歐洲最重要的 AI 公司之一。

這篇論文通常被稱為「Chinchilla 論文」，名字來自團隊為驗證發現而訓練的 700 億參數模型。這個名字流傳了下來：不是論文標題，而是那隻動物。在 AI 圈子裡，「Chinchilla scaling」成了這篇論文核心主張的代名詞。

而那個主張很簡單、很大膽，也讓整個業界很不舒服：**2022 年最大的那些語言模型，很多並不是「模型不夠大」，而是在各自的算力預算下被顯著欠訓練了。**

## 0. 先認幾個詞

這篇論文會頻繁談到「該把預算花在哪裡」，所以先把下面幾個詞認熟，會更容易抓住重點：

- `算力預算`：你願意為這次訓練總共花多少計算資源。
- `參數量`：模型有多大。
- `token / 詞元`：模型訓練時實際讀進去的最小文本單位，可以粗略理解成「模型看到的字或詞片段」。
- `loss / 損失`：模型整體錯得有多厲害，通常越低越好。
- `scaling law / 縮放定律`：當參數量、資料量、算力變化時，模型表現如何跟著變化。
- `欠訓練 / undertrained`：不是模型太小，而是訓練資料和訓練步數沒跟上，模型的潛力沒有被充分用出來。

## 1. 要解決什麼問題

到 2022 年初，AI 社群已經內化了 [Kaplan 等人（2020）](/zh-hant/posts/scaling-laws-for-neural-language-models/)的一個明確教訓：更大的模型可預測地更好。縮放定律論文已經表明性能遵循冪律，而且在固定算力預算下，你應該把模型做到盡可能大。

業界照做了。到 2022 年春天，GPT-3 有 1750 億參數，訓練了 3000 億詞元。DeepMind 自己的 Gopher 有 2800 億參數，訓練了 3000 億詞元。隨後 Google 又發布了 5400 億參數的 PaLM。趨勢很明顯：往上堆參數。

但一個問題藏在眼皮底下。Kaplan 等人的結論是，當你擴大算力時，大部分預算應該花在模型大小上（N ∝ C^0.73），而相對少地花在訓練資料上（D ∝ C^0.27）。這意味著：把模型做得巨大，用適量的資料訓練就好。

Hoffmann 的團隊問了一個簡單的問題：這真的對嗎？

## 2. 三種獨立方法，同一個答案

這篇論文格外有說服力的地方在於方法論。團隊沒有依賴單一實驗，而是從三個完全獨立的角度切入同一個問題，三者最終收斂到同一個答案。

**方法一：固定算力，改變分配。** 他們訓練了超過 400 個模型，參數量從 7000 萬到 160 億以上，每個模型在模型大小和訓練資料之間有不同的分配比例，但總算力相同。對每個算力水平，他們找出哪個模型大小能讓損失最低。

**方法二：IsoFLOP 曲線。** 他們訓練了 9 種不同大小的模型（從 7000 萬到 100 億參數），使用不同量的資料，特別設計為每組實驗使用大致相同的總算力。然後擬合曲線，找出每個算力水平對應的最優模型大小。

**方法三：擬合參數化損失函數。** 他們把以下方程式擬合到所有訓練結果：

$$
\hat{L}(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}
$$

其中 E 是不可約損失（自然語言的熵：任何模型都無法做得更好），A/N^α 反映模型大小的瓶頸，B/D^β 反映資料量的瓶頸。從擬合出的參數，他們推導出最優的 N 和 D 作為算力的函數。

三種方法一致同意：

$$
N_{\mathrm{opt}} \propto C^a, \quad D_{\mathrm{opt}} \propto C^b, \quad a \approx 0.50, \quad b \approx 0.50
$$

```python
def optimal_scaling(compute: float) -&gt; tuple[float, float]:
    a = 0.50
    b = 0.50
    n_opt = compute ** a
    d_opt = compute ** b
    return n_opt, d_opt
```

指數 a ≈ b ≈ 0.5 的真正含義是：隨著算力增長，模型大小和訓練資料應當按近似相同的比例一起擴張。算力增長 10 倍時，兩者都大約增到 3.2 倍；算力翻倍時，兩者都大約增到 1.4 倍。換句話說，模型大小每翻一倍，訓練資料也應該翻一倍。這直接推翻了 Kaplan 等人的結論：他們認為算力主要應該花在模型大小上。

## 3. 為什麼 Kaplan 會錯

這不是誰「做錯了」，而是實驗設定不同，最終導致了不同的最優分配結論。兩個團隊都做了嚴謹的工作。

Kaplan 等人使用了固定的學習率調度，沒有根據訓練長度進行調整。當你讓模型訓練更多步而不調整學習率調度時，性能會下降：不是因為模型本身更差，而是因為優化過程不理想。這讓長時間訓練看起來沒那麼有效，從而偏向了更大的模型配更少的訓練步數。

Hoffmann 的團隊為每次訓練調整了學習率調度，確保每種配置都有公平的機會。這樣做之後，在更多資料上訓練更久，其實比 Kaplan 的數字所暗示的有價值得多。

```python
from dataclasses import dataclass
from typing import Literal


@dataclass(frozen=True)
class TrainingConfig:
    n_params: float
    n_tokens: float
    schedule: Literal[&quot;fixed&quot;, &quot;cosine_with_warmup&quot;]
    warmup_steps: int
    total_steps: int
```

## 4. 參數化損失函數

論文的方法三值得細看，因為它給出了一個完整的性能數學模型：

$$
\hat{L}(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}
$$

其中擬合出的常數是：

- E = 1.69 — 不可約損失（自然語言的熵）
- A = 406.4, α = 0.34 — 模型大小項
- B = 410.7, β = 0.28 — 資料量項

這個方程式的結構值得細品。損失有三個組成部分：一個你永遠無法突破的下限（E），參數太少的懲罰（A/N^α），以及資料太少的懲罰（B/D^β）。模型大小的懲罰和資料量的懲罰是相加的：它們爭奪你的注意力和算力預算。

```python
def estimated_loss(n_params: float, n_tokens: float) -&gt; float:
    e = 1.69
    a = 406.4
    alpha = 0.34
    b = 410.7
    beta = 0.28
    return e + a / (n_params ** alpha) + b / (n_tokens ** beta)


def optimal_params_and_tokens(compute_flops: float) -&gt; tuple[float, float]:
    alpha = 0.34
    beta = 0.28
    a = beta / (alpha + beta)
    b = alpha / (alpha + beta)
    g = 2.0

    base = compute_flops / 6.0
    n_opt = g * (base ** a)
    d_opt = (1.0 / g) * (base ** b)
    return n_opt, d_opt
```

## 5. 觸目驚心的表格

論文的 Table 1 列出了當時幾個大模型的實際參數量和訓練詞元數，Table 3 則給出了不同模型大小下 compute-optimal 的詞元估計。把兩張表對照著看，就像一份對整個業界的審計報告：

| 模型 | 參數量 | 實際訓練詞元 | Chinchilla 最優詞元 |
|------|--------|-------------|---------------------|
| GPT-3 | 1750 億 | 3000 億 | 3.7 兆 |
| Gopher | 2800 億 | 3000 億 | 5.9 兆 |
| Jurassic-1 | 1780 億 | 3000 億 | 3.7 兆 |
| MT-NLG | 5300 億 | 2700 億 | 11.0 兆 |

每個模型都只用了大約 3000 億詞元。但按照 Chinchilla 的分析，GPT-3 應該用 3.7 兆詞元訓練：是實際的 12 倍以上。Gopher 應該接近 6 兆。最大的 MT-NLG（5300 億參數）應該用 11 兆詞元訓練：是實際訓練資料的 40 倍。

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ModelComparison:
    name: str
    params_billions: float
    tokens_used_billions: float
    optimal_tokens_billions: float


def industry_models() -&gt; list[ModelComparison]:
    return [
        ModelComparison(&quot;GPT-3&quot;, 175.0, 300.0, 3_700.0),
        ModelComparison(&quot;Gopher&quot;, 280.0, 300.0, 5_900.0),
        ModelComparison(&quot;Jurassic-1&quot;, 178.0, 300.0, 3_700.0),
        ModelComparison(&quot;MT-NLG&quot;, 530.0, 270.0, 11_000.0),
    ]
```

規律非常明顯。整個業界不約而同地使用了差不多的訓練資料量：大約 3000 億詞元：不管模型有多大。彷彿所有人都認定 3000 億詞元「夠了」，然後把所有多出來的算力都堆到更大的模型上。Chinchilla 說這恰恰做反了。

## 6. 實證：Chinchilla 對決 Gopher

為了驗證理論，團隊訓練了 Chinchilla：一個 700 億參數的模型，用 1.4 兆詞元訓練。Chinchilla 使用的算力預算和 Gopher（2800 億參數，3000 億詞元）相同：同樣的總訓練成本，只是分配方式不同。

結果很決定性。Chinchilla 在幾乎所有基準測試上都優於 Gopher，儘管小了 4 倍：

- **MMLU**（大規模多任務語言理解）：Chinchilla 67.6% vs. Gopher 60.0% vs. GPT-3 43.9%
- **閱讀理解**（RACE-h）：Chinchilla 73.3% vs. Gopher 71.6%
- **常識推理**（HellaSwag）：Chinchilla 80.8% vs. Gopher 79.2%
- **BIG-bench**：Chinchilla 在大多數任務上優於 Gopher

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ModelConfig:
    name: str
    params_billions: float
    tokens_billions: float
    mmlu_accuracy: float


def chinchilla_vs_gopher() -&gt; tuple[float, float]:
    gopher = ModelConfig(&quot;Gopher&quot;, 280.0, 300.0, 60.0)
    chinchilla = ModelConfig(&quot;Chinchilla&quot;, 70.0, 1_400.0, 67.6)

    gopher_flops = 6.0 * gopher.params_billions * 1e9 * gopher.tokens_billions * 1e9
    chinchilla_flops = 6.0 * chinchilla.params_billions * 1e9 * chinchilla.tokens_billions * 1e9
    return gopher_flops, chinchilla_flops
```

一個小 4 倍的模型在幾乎所有基準上打敗更大的模型：用同樣的算力：這是非常有力的實證。算力沒有浪費，只是從參數重新導向了資料。

## 7. 實際影響

Chinchilla 論文對業界產生了立即而具體的影響。

**更小的模型部署成本更低。** 訓練成本是一次性的，但推理成本：實際執行模型來生成文字的成本：與模型大小成正比，每一次使用者發送請求都要付出。一個 700 億參數的模型，部署成本是 2800 億參數模型的四分之一。如果更小的模型性能還更好，那是雙贏：更好的品質加上更低的成本。

**資料成了瓶頸。** Chinchilla 之前，限制因素是算力：你能搞到多少 GPU？Chinchilla 之後，限制因素變成了資料：你去哪裡找幾兆高品質的詞元？這引發了全業界對訓練資料的爭奪：大規模網路爬取、資料集篩選，最終催生了合成資料運動。

**LLaMA 時刻。** Meta 的 LLaMA（2023 年 2 月）可以說是 Chinchilla 縮放定律最直接的應用。LLaMA-13B 用 1 兆詞元訓練，在大多數基準上超過了 GPT-3（1750 億參數）。LLaMA-65B 用 1.4 兆詞元訓練，與 Chinchilla 和 PaLM-540B 處於同一水平。Meta 明確引用了 Chinchilla 論文，刻意訓練更小的模型、使用遠超過去慣例的資料量。

```python
def inference_cost_comparison() -&gt; tuple[float, float]:
    gopher_cost_per_token = 280.0
    chinchilla_cost_per_token = 70.0

    queries_per_day = 1_000_000.0
    tokens_per_query = 500.0

    daily_cost_gopher = queries_per_day * tokens_per_query * gopher_cost_per_token
    daily_cost_chinchilla = queries_per_day * tokens_per_query * chinchilla_cost_per_token
    return daily_cost_gopher, daily_cost_chinchilla
```

## 8. 我的思考

第一，這篇論文是一次修正：而且是優雅的修正。它沿用了 Kaplan 等人的框架，找出了方法論上的缺陷（固定學習率調度），修正了它，得出了不同的答案。它沒有否定前人的工作，而是在其基礎上推進。參數化損失函數 L̂(N, D) = E + A/N^α + B/D^β 是 Kaplan 公式的精煉，而非替代。科學最好的樣子正是如此：有人做了仔細的工作，另一群人做了更仔細的工作，整個領域因此進步。

第二，這篇論文最驚人的發現不是數學：而是理論與實踐之間的巨大鴻溝。業界所有人都看得到 3000 億詞元正在成為一個預設值。但直到這個團隊跑了數字，沒有人認真質疑過它。那些模型不是太小，而是被餓著了。解法不是做更大：而是餵更多。

第三，等比縮放的結果（a ≈ b ≈ 0.5）因其簡潔而優美。模型大小和資料之間沒有不對稱。如果你有更多算力，兩邊等量擴大。不需要複雜的分配策略。「我下一塊錢的算力該花在哪裡？」Chinchilla 給出的答案不是繼續單押參數量，而是讓模型大小和訓練資料近似等比例地一起增長。

第四，實際遺產是巨大的。Chinchilla 之前，通往更好 AI 的路徑是「把它做大」。Chinchilla 之後，路徑變成了「把它訓好」。這一個轉變讓強大的模型變得可及：那些負擔不起最大參數量的組織，只要能整理出大量資料集，就能訓練出有競爭力的模型。LLaMA、Mistral、以及整個開源 LLM 生態系，都直接受惠於這個洞察。

Kaplan 的論文說：更大的模型可預測地更好。Chinchilla 的論文說：沒錯，但你們一直在用錯誤的方式把它做大。別再囤參數了，開始餵資料。

一篇論文給了業界擴大規模的信心。另一篇教會了業界怎麼擴。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>chinchilla</category><category>scaling-laws</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>論文共讀：《Scaling Laws for Neural Language Models》（神經語言模型的縮放定律）</title><link>https://justinhuangai.github.io/zh-hant/posts/scaling-laws-for-neural-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/scaling-laws-for-neural-language-models/</guid><description>規模的數學：為什麼更大的模型可預測地更好，附真實 Python 核心程式碼</description><pubDate>Sun, 01 Mar 2026 08:45:39 GMT</pubDate><content:encoded>2020 年 1 月 23 日，OpenAI 的十位研究者在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文：[《Scaling Laws for Neural Language Models》](/papers/2001.08361v1.pdf)（神經語言模型的縮放定律）。

十位作者是 Jared Kaplan、Sam McCandlish、Tom Henighan、Tom B. Brown、Benjamin Chess、Rewon Child、Scott Gray、Alec Radford、Jeffrey Wu 和 Dario Amodei。當時全部在 OpenAI。

這份作者名單回頭看很有意思。Jared Kaplan 和 Sam McCandlish 是理論物理出身：Kaplan 在加入 OpenAI 之前是 Johns Hopkins 大學的弦理論教授。Dario Amodei 是研究副總裁。Tom B. Brown 後來成為 GPT-3 論文的第一作者。Alec Radford 設計了 GPT-1 和 GPT-2。不到兩年，Kaplan、McCandlish 和 Amodei 就離開 OpenAI，共同創立了 Anthropic（Claude 背後的公司）。

弦理論學家有個習慣：在複雜現象裡尋找簡潔的普適定律。

這個習慣貫穿了整篇論文。

## 0. 先認幾個詞

如果你平時不常看這種帶公式的論文，先把這幾個詞認熟，後面會順很多：

- `參數量`：模型裡一共有多少可學習參數，也就是模型的大小。
- `資料量`：訓練時一共餵給模型多少文本。
- `算力 / compute`：訓練時總共做了多少計算。你可以先把它當成「電費帳單」。
- `loss / 損失`：模型犯錯有多嚴重，通常越低越好。
- `冪律 / power law`：某個量按固定指數變化的關係；畫在對數座標上，經常會接近一條直線。
- `對數座標`：像 `1、10、100、1000` 這樣按倍數增長的刻度，不是 `1、2、3、4` 那樣均勻加一。

## 1. 要解決什麼問題

到 2020 年初，深度學習社群已經知道更大的模型通常表現更好。但「通常」不是科學。人們回答不了最基本的實際問題：如果我把算力預算翻倍，性能會提升多少？這筆預算該花在更大的模型、更多的資料、還是更長的訓練上？有沒有一個公式？

這篇論文回答了這些問題。不是靠直覺，不是靠經驗法則：靠方程式。

## 2. 冪律：核心發現

論文的核心發現是，語言模型的性能遵循**冪律（power laws）**。在論文測量到的區間內，只要性能主要受某一個因素限制、而不是被另外兩個因素卡住，測試損失（衡量模型預測下一個詞的能力：越低越好）和參數量、資料量、算力在雙對數座標上都近似呈直線關係。

三個方程式總結了整篇論文：

$$
L(N) \approx \left(\frac{N_c}{N}\right)^{\alpha_N}, \quad \alpha_N \approx 0.076
$$

$$
L(D) \approx \left(\frac{D_c}{D}\right)^{\alpha_D}, \quad \alpha_D \approx 0.095
$$

$$
L(C) \approx \left(\frac{C_c}{C}\right)^{\alpha_C}, \quad \alpha_C \approx 0.050
$$

不用被符號嚇到，拆開來看：

- **L** 是測試損失：一個數字就能概括模型表現如何。越低越好
- **N** 是參數數量（模型大小）。參數越多，模型能儲存的模式就越多
- **D** 是訓練用的資料詞元數量。資料越多，可以學到的模式就越多
- **C** 是訓練用的總算力，以 PetaFLOP-days 為單位（1 PetaFLOP-day = 10^15 次浮點運算跑一整天）
- **N_c, D_c, C_c** 是常數（曲線上的參考點）
- **α**（alpha）是指數：它告訴你雙對數圖上直線的斜率。指數越大，代表隨著規模擴大，性能提升越快

關鍵洞察：這些是冪律，不是對數曲線。對數曲線很快就會趨平：輸入翻倍，輸出幾乎不動。冪律則大方得多：至少在論文測量到的區間內，性能沒有出現明顯「撞牆」，而是持續沿著冪律改善。論文也提醒了：這個趨勢不可能無限延伸到零損失，最終一定會變平：但在觀測範圍內，趨勢乾淨俐落。

```python
def power_law_loss(x: float, x_c: float, alpha: float) -&gt; float:
    return (x_c / x) ** alpha


def scaling_law_examples() -&gt; dict[str, float]:
    alpha_n = 0.076
    alpha_d = 0.095
    alpha_c = 0.050

    return {
        &quot;10x_params&quot;: 10.0 ** alpha_n,
        &quot;10x_data&quot;: 10.0 ** alpha_d,
        &quot;10x_compute&quot;: 10.0 ** alpha_c,
    }
```

指數本身就說明了問題。資料集大小（α = 0.095）每多一個量級帶來的提升最大。模型大小（α = 0.076）次之。算力（α = 0.050）最小：因為如果不合理分配算力到模型大小和訓練時長上，單純堆算力是浪費的。真正的槓桿在於擴大正確的東西。

## 3. 在論文測試過的範圍內，架構形狀的重要性低於總規模

這是論文最讓人意外的地方。

團隊測試了不同深度（層數）、寬度（隱藏維度）、注意力頭數和前饋維度的 Transformer。在論文測試過的 Transformer 形狀範圍內，只要非嵌入參數量接近，深度和寬度的具體分配對損失影響很小。

一個只有 2 層但隱藏維度巨大的 Transformer？和一個 40 層但隱藏維度很小的 Transformer 損失差不多：前提是非嵌入參數預算接近。

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ArchitectureExperiment:
    n_layers: int
    d_model: int
    n_heads: int
    d_ff: int


def non_embedding_params(config: ArchitectureExperiment) -&gt; int:
    n = config.n_layers
    d = config.d_model
    d_ff = config.d_ff
    return n * (4 * d * d + 2 * d * d_ff + 4 * d)
```

這有一個深遠的含義：你不需要花好幾個禮拜去搜尋「最優」架構。選一個合理的 Transformer 形狀，然後把精力集中在把它做大就好。論文之所以把 embedding 參數從 N 中排除，是因為他們發現 embedding 參數對性能的貢獻遠小於非 embedding 參數：模型的「思考」能力在 Transformer 層裡，不在詞表裡。

## 4. 模型何時過擬合：資料瓶頸

更大不一定更好：如果你的資料集太小的話。論文這裡真正漂亮的地方，是給出了一個統一的二維公式，把模型規模和資料規模如何共同決定性能寫進了一個式子：

$$
L(N, D) = \left[\left(\frac{N_c}{N}\right)^{\alpha_N / \alpha_D} + \frac{D_c}{D}\right]^{\alpha_D}
$$

這個公式說的是：損失不是單獨由模型大小或資料大小決定的：而是由兩者共同決定。當 N 大到第一項消失時，剩下的項表明損失被資料卡住了。當 D 大到第二項消失時，剩下的是模型規模的瓶頸。公式在兩種情況之間平滑過渡，過擬合就是兩項競爭的自然結果。

從這個關係出發，論文給出了一個粗略的經驗門檻：過擬合開始明顯影響性能的臨界點：

$$
D \gtrsim 5 \times 10^3 \times N^{0.74}
$$

白話翻譯：模型越大，需要的資料就越多：但增長是次線性的。大 10 倍的模型只需要約 10^0.74 ≈ 5.5 倍的資料。更大的模型樣本效率更高：它們能從每個訓練詞元中提取更多資訊。

```python
def loss_nd(n_params: float, n_tokens: float) -&gt; float:
    n_c = 8.8e13
    d_c = 5.4e13
    alpha_n = 0.076
    alpha_d = 0.095
    ratio = alpha_n / alpha_d
    return ((n_c / n_params) ** ratio + d_c / n_tokens) ** alpha_d


def min_dataset_tokens(n_params: float) -&gt; float:
    return 5_000.0 * n_params ** 0.74
```

按這條關係粗略推算，175B 級別模型要把過擬合壓在論文討論的閾值附近，資料量應接近兆級 token。反過來看，GPT-3 的 3000 億詞元其實並不充裕。這也說明「模型多大、資料餵多少」並不是拍腦袋，而是有可分析的權衡：後來業界重新審視這個配比（最典型的就是 Chinchilla 論文，Hoffmann 等人，2022 年），正是因為意識到了許多大模型的資料其實不夠。

## 5. 計算最優訓練：真正的重點

如果你有固定的算力預算，該怎麼花？這是論文中最有實際意義的問題，答案卻違反直覺。

論文發現最優分配遵循：

$$
N_{\mathrm{opt}} \propto C^{0.73}
$$

$$
B_{\mathrm{opt}} \propto C^{0.24}
$$

$$
S_{\mathrm{opt}} \propto C^{0.03}
$$

翻譯一下：如果算力預算增加 10 倍，你應該把模型做大約 5.4 倍，批次大小增加約 1.7 倍，訓練時長幾乎不變（約多 1.07 倍的步數）。

違反直覺的地方在於：**你應該訓練非常大的模型，然後在遠未收斂之前就停下來。** 大多數人的本能是把一個小模型徹底訓練到收斂。縮放定律說的恰好相反：在相同的算力預算下，一個部分訓練的大模型優於一個完全訓練的小模型。

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ComputeAllocation:
    n_params: float
    batch_size: float
    training_steps: float


def optimal_allocation(compute: float) -&gt; ComputeAllocation:
    return ComputeAllocation(
        n_params=compute ** 0.73,
        batch_size=compute ** 0.24,
        training_steps=compute ** 0.03,
    )


def is_compute_efficient(n_params: float, compute: float) -&gt; bool:
    optimal_n = compute ** 0.73
    return abs(n_params / optimal_n - 1.0) &lt; 0.5
```

這個結果塑造了整個產業。五個月後發表的 GPT-3 直接遵循了這個邏輯：訓練一個在當時規模空前的 1750 億參數模型，而不是把一個小模型徹底訓練完。後來的「Chinchilla」論文（Hoffmann et al., 2022）更新了這些指數，並指出大多數大型模型相對於最優資料分配其實是訓練不足的：但核心洞察，即存在一個可計算的最優權衡，源頭在這裡。

## 6. 臨界批次大小：何時該增加平行度

論文還發現批次大小存在一個「甜蜜點」，而且它取決於當前的損失值：

$$
B_{\mathrm{crit}} \propto L^{-4.8}
$$

隨著訓練推進、損失降低，臨界批次大小會增長。訓練初期損失高，小批次就夠用：每個批次提供的梯度信號夠強。後期模型已經學完了簡單的模式，需要更大的批次來平均掉噪聲才能繼續進步。

低於臨界批次大小時，批次翻倍大致能讓訓練時間減半（完美的平行化）。高於臨界批次大小時，批次翻倍幾乎沒有幫助：只是在浪費算力。

```python
def critical_batch_size(loss: float, b_star: float, l_star: float) -&gt; float:
    return b_star * (l_star / loss) ** 4.8
```

這是很實用的工程智慧。很多團隊全程使用固定的批次大小。縮放定律告訴你應該隨著訓練推進逐步增大批次：開始用小批次，模型變好後再放大。

## 7. 我的思考

讀完這篇論文，有幾個感受。

第一，這篇論文最深層的貢獻不是任何一個具體的數字，而是證明了神經網路的性能受簡單、可預測的定律支配。在這篇論文之前，訓練大模型基本上是經驗主義：試各種東西，調超參數，然後祈禱效果好。這篇論文之後，你可以算。你可以在訓練之前就預測模型的性能。它至少把大模型訓練裡最貴、最關鍵的一部分：資源分配：從經驗試錯推進到了可估算、可規劃的工程問題。

第二，作者的背景很重要。Kaplan 和 McCandlish 帶來了理論物理的思維方式：精確測量，擬合冪律，尋找普適性。這不是大多數機器學習論文的寫法。大多數 ML 論文提出一個新架構，然後在基準測試上證明它比基線好。這篇論文沒有提出任何新架構。它提出了一種思考方式。工具不新：洞察是新的。

第三，「盡量把模型做大，但不用訓到頭就可以停」這個結論真的違反直覺，而且直接重塑了整個產業分配資源的方式。在這篇論文之前，常規做法是選一個模型大小然後訓練到完全收斂：把這筆算力預算花到底。有了這篇論文之後，問題變成了：同樣一筆算力預算，與其把一個小模型訓到極致，不如把模型做到盡可能大，訓到「夠用」就停：因為一個沒訓完的大模型，比一個訓透的小模型表現更好。這個思路直接催生了 GPT-3（1750 億參數，3000 億詞元），並影響了之後所有的大模型。

第四，從歷史脈絡來看，這篇論文可以被視為 [GPT-3 論文](/zh-hant/posts/language-models-are-few-shot-learners/)的理論基礎。GPT-3 直接引用了它，GPT-3 論文也明確展示了 few-shot 能力隨模型容量平滑提升。把 GPT-3 選擇 1750 億參數看作受縮放定律啟發，是合理的推斷：儘管 GPT-3 論文本身並沒有逐句寫明「我們按 Kaplan 公式設定了參數量」。但可以說，沒有縮放定律提供的信心，在那個規模上做決策的不確定性會大得多。

「更大的模型更強」，這句話在 2020 年之前只是一種感覺。這篇論文把它變成了一組方程式：告訴你強多少、花多少、怎麼花最划算。

AI 產業後來變成了一場算力競賽。讀完這篇論文你就明白了：這場競賽不是盲目的軍備競賽，而是有人先算清了帳。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>scaling-laws</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>OpenClaw 專題：架構分析 🦞</title><link>https://justinhuangai.github.io/zh-hant/posts/openclaw-architecture/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/openclaw-architecture/</guid><description>基於 v2026.3.8 原始碼，拆解一個自託管 AI 助手的工程骨架</description><pubDate>Tue, 24 Feb 2026 08:37:11 GMT</pubDate><content:encoded>![OpenClaw](/images/openclaw-logo-text-dark.webp)

[上一篇](/zh-hant/posts/openclaw-ecosystem/)聊了生態，這一篇拆架構。

OpenClaw 的程式碼量不小：43 萬行 TypeScript。但真正值得看的不是程式碼量，是它的架構選擇。一個要同時接二十多個聊天平台、管幾個 Agent、還能調工具的 AI 助手，它是怎麼把這些東西塞到一起還不散架的？

## 0. 先認幾個詞

如果你沒有系統架構背景，也沒關係。先記住 6 個詞，後面會順很多：

- `架構`：系統如何分層、每層分別負責什麼。
- `Channel / 頻道層`：負責和外部聊天平台「說同一種協議」的適配層。
- `Gateway / 閘道`：中央調度者，負責路由訊息、管理會話和 Agent。
- `Node / 執行節點`：真正去拍照、跑命令、操作裝置的執行端。
- `workspace / 工作區`：Agent 的設定、記憶、技能和會話檔案所在的本地目錄。
- `sandbox / 沙箱`：把高風險操作隔離起來的受限執行環境。

## 1. 3 層架構：Channel → Gateway → Node

先看全景圖：

![OpenClaw Architecture](/images/openclaw-architecture-zh-hant.svg)

你可以把 OpenClaw 想成一家公司的 3 個部門：

| 誰 | 幹什麼 | 打個比方 |
|------|------|--------|
| **Channel（頻道層）** | 對接 WhatsApp、Telegram、Discord、飛書等 20+ 聊天平台 | 前台：負責接電話、收郵件、接待來訪 |
| **Gateway（閘道）** | 中央調度，管理所有會話和 Agent | 大腦：誰的訊息該給誰處理、怎麼回覆，全在這裡決定 |
| **Node（執行節點）** | 在裝置上幹活：拍照、錄螢幕、跑命令 | 手和腳：大腦說「拍張照」，它去執行 |

整個系統的核心就是 Gateway：一個常駐運行的程序，預設只監聽本機（`127.0.0.1:18789`），不對外網開放。想遠端存取？走 Tailscale 隧道，不直接暴露埠號。這個設計叫「Loopback-First」，安全上很討巧：什麼埠號都不開，就沒有攻擊面。

為什麼只用一個程序不搞分散式？原因很實際：WhatsApp 協議要求同一時間只能有一個裝置在線，你搞兩個程序反而會打架。與其為了「架構正確」多搞一堆協調邏輯，不如老老實實一個程序管到底。對絕大多數個人使用者來說，夠用了。

## 2. 一條訊息的旅程

你在 Telegram 上跟你的 OpenClaw 說了一句「幫我查下明天台北天氣」，發生了什麼？

**第 1 步：接入。** Telegram 適配器收到訊息，把 Telegram 格式的資料翻譯成 OpenClaw 內部統一格式。不管訊息從哪個平台來，翻譯完都長一個樣。

**第 2 步：驗身份、找路由。** 你是誰？允不允許跟 Agent 說話？如果是陌生人，先彈一個配對碼讓主人批准。通過後，路由引擎決定這條訊息交給哪個 Agent。

**第 3 步：組裝上下文。** 從磁碟讀出你之前的聊天記錄，載入 Agent 的性格設定（SOUL.md）、行為規則（AGENTS.md），再從記憶庫裡語義搜尋相關內容。組裝成一份完整的「大腦簡報」。

**第 4 步：問大模型。** 這份簡報發給 Anthropic、OpenAI、DeepSeek、MiniMax、GLM、Qwen、Gemini 等大模型提供商（你設定了哪個就用哪個），模型一個字一個字地串流吐出回覆。

**第 5 步：執行工具。** 如果模型說「我需要跑個命令查天氣」，執行時會攔截這個請求，根據安全策略決定在哪裡執行（管理員的命令直接跑，陌生人的命令丟到 Docker 沙箱裡跑），然後把結果餵回給模型繼續生成。

**第 6 步：回覆。** 最終答案按 Telegram 的格式要求（字數限制、Markdown 規則等）拆好，發回給你。聊天記錄寫入磁碟。

整個鏈路中，真正慢的是第 4 步：等大模型吐第一個字。其他步驟基本都是毫秒級。

## 3. 頻道適配層：一次開發，到處說話

OpenClaw 對接了二十多個聊天平台，但不是每個平台都從零寫一套。它抽象了一層「適配器」，每個平台的適配器只負責四件事：

- **登入**：WhatsApp 掃 QR Code、Telegram 填 Bot Token、iMessage 用 macOS 原生能力：各平台各的規矩
- **翻譯進來的訊息**：不管是文字、圖片、回覆還是表情，統統翻譯成內部統一格式
- **管門禁**：誰能私聊、群裡要不要 @才回覆、哪些群允許
- **翻譯出去的回覆**：把 Agent 的回覆適配成各平台能顯示的格式

內建的有六個大平台（WhatsApp、Telegram、Discord、Slack、Signal、iMessage），其他三十多個（飛書、LINE、Matrix、Mattermost 等）透過外掛接入。

這層抽象最大的好處是：Agent 完全不需要知道訊息來自哪個平台。你在 WhatsApp 上聊的 Agent 和在 Telegram 上聊的是同一個，邏輯完全複用。頻道只是「傳話筒」，換一個傳話筒不影響說話的人。

## 4. Agent 執行時與工作區

Agent 執行時的核心來自 Pi-mono（一個開源程式設計 Agent），嵌入在 Gateway 裡面跑。

### 「一切皆文字」的工作區

這是 OpenClaw 最有意思的設計之一：每個 Agent 的所有組態，都是你能直接打開編輯的文字檔：

```
workspace/
├── AGENTS.md          # 定義 Agent 是誰、怎麼做事（相當於履歷+工作手冊）
├── SOUL.md            # 靈魂設定：性格、價值觀（寫好就不該改）
├── USER.md            # 記錄使用者資訊：你叫什麼、喜好是什麼
├── MEMORY.md          # 長期記憶：Agent 主動記下的重要事情
├── HEARTBEAT.md       # 定時任務：比如每天早上報天氣
├── memory/            # 日記本
│   └── YYYY-MM-DD.md  # 每天一頁，只追加不修改
├── skills/            # 技能包
└── sessions.json      # 會話記錄
```

沒有資料庫，沒有專用管理介面，純文字檔。想改 Agent 性格？打開 SOUL.md 改兩行就行。想看它記住了什麼？打開 MEMORY.md 直接看。這種「你能看到它的全部大腦」的透明感，是很多封閉 AI 產品做不到的。

### 每一輪對話怎麼跑

1. **分清身份**：你是管理員直聊、朋友私聊、還是群裡有人 @了它？不同來源，安全等級不同
2. **組裝記憶**：讀聊天記錄、載入性格和規則、搜尋相關的長期記憶，拼成一份完整上下文
3. **問大模型**：發給設定的模型，支援 fallback：主力模型掛了自動切備用
4. **幹活+記住**：執行工具呼叫，更新聊天記錄

上下文的組裝不是把所有東西一股腦塞進去。不相關的技能不載入、不需要的工具不注入：省 token 就是省錢。

## 5. 4 層記憶：讓 AI 真的「記住你」

普通聊天機器人關掉視窗就失憶了。OpenClaw 不會。它有 4 層記憶，從最深層的「我是誰」到最淺層的「剛才聊了啥」：

| 層 | 是什麼 | 打個比方 |
|------|--------|------|
| **SOUL** | 人格設定，永遠不變 | 你的性格：出生就定了 |
| **TOOLS** | 當前裝了哪些技能 | 你今天帶了哪些工具出門 |
| **USER** | 關於你的長期記憶 | 你的老朋友記得你愛吃什麼 |
| **Session** | 當前這次對話 | 此刻正在聊的話題 |

幾個巧妙的機制：

**每日日記。** 每天的對話會自動寫進 `memory/2026-03-12.md`（只追加，不覆蓋）。下次開聊時，Agent 會自動翻看今天和昨天的日記，保持連續感：「你昨天說要買的那本書，買了嗎？」

**自動搶救記憶。** 聊得太長，上下文快塞滿了怎麼辦？OpenClaw 會偷偷在背景跑一個隱形輪次，把重要資訊存進 MEMORY.md，然後壓縮掉舊內容。你察覺不到這個過程，但關鍵資訊不會丟。這個機制叫 Pre-Compaction。

**語義搜尋。** 你說「之前聊過的那個部署問題」，它能從記憶裡搜出來：不是靠關鍵字精確比對，而是靠語義理解。底層是向量搜尋（SQLite-vec）+ 傳統關鍵字搜尋（BM25）雙管齊下。

**跨平台認人。** 你在 Telegram 上跟它聊了半小時，切到 WhatsApp 繼續聊，它認得你。同一個人在不同平台的 ID 會被連結到同一個身份，共享同一份記憶。但群聊的記憶是隔離的：你在群裡說的話，不會洩露到私聊裡。

## 6. 工具系統：只給 4 把刀

這是 OpenClaw 最「叛逆」的設計。

別的 AI Agent 框架恨不得內建一百個工具。OpenClaw 只給了 4 個：

| 工具 | 一句話 |
|------|------|
| **Read** | 讀檔案 |
| **Write** | 寫檔案 |
| **Edit** | 改檔案 |
| **Bash** | 跑命令 |

就這？就這。

創辦人的邏輯是：有了命令列（Bash），你能幹任何事。想查天氣？`curl` 一下。想寄信？調個 CLI 工具。想操作資料庫？`psql` 命令搞定。不需要為每個場景預製一個專用工具。

這就是 Unix 哲學：小工具、可組合、文字流。代價是什麼？你需要一個夠聰明的模型，能自己想出該調什麼命令。所以 OpenClaw 推薦用 Claude Opus 這個級別的模型。弱模型可能搞不定。

在這 4 個核心工具之上，還有 55 個內建 Skills（技能）和 ClawHub 技能市場。Skills 是可以安裝和移除的：相當於給你的 Agent 裝 App。

**有意思的是，它故意不支援 MCP。** MCP 是 Anthropic 搞的工具協議標準，現在滿世界的 AI 框架都在接。OpenClaw 偏不。Peter 原話是：「MCP 是垃圾，不能 scale。你知道什麼能 scale？CLI。Unix。」替代方案是用內建的 `mcporter` 做橋接。

**更有意思的是自我擴展。** OpenClaw Agent 遇到不會幹的事，會自己寫一個 skill 來完成，寫完自動裝上。發現 skill 有 bug？自己改自己重新載入。這意味著你的 Agent 會在使用過程中越來越強：它在「養自己」。

## 7. 多 Agent 路由：一個大腦，多個人格

一個 Gateway 可以同時跑好幾個 Agent，各管各的。路由規則長這樣：

```json
{
  &quot;bindings&quot;: [
    { &quot;agentId&quot;: &quot;home&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;whatsapp&quot;, &quot;accountId&quot;: &quot;personal&quot; } },
    { &quot;agentId&quot;: &quot;work&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;slack&quot; } },
    { &quot;agentId&quot;: &quot;bot&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;discord&quot;, &quot;guildId&quot;: &quot;123456&quot; } }
  ]
}
```

翻譯一下：WhatsApp 私人號來的訊息給「家庭助手」處理，Slack 的給「工作助手」，Discord 某個伺服器的給「社群機器人」。三個 Agent 完全隔離：各自有自己的性格、記憶、技能和安全策略。

## 8. 安全：3 道門

你的 Agent 跑在你自己的伺服器上，能執行命令、讀寫檔案：安全當然是大事。OpenClaw 的安全模型分 3 道門：

**第一道門：你是誰？（私聊配對）** 陌生人給你 Agent 發訊息，Agent 不會直接回。它會發一個 6 位配對碼，你在已認證的頻道裡確認了，對方才能用。這是預設行為：關掉就意味著任何知道你號碼的人都能白嫖你的 API 額度。

**第二道門：VIP 通道（白名單）。** 信任的人可以直接加到白名單裡（`allowFrom`），跳過配對直接聊。

**第三道門：群裡別亂回（群組規則）。** 群聊裡 Agent 預設只在被 @ 時才回覆，不會對著群裡每條訊息都冒泡：既省 token，又不擾民。

再往下還有縱深防禦：工具權限五層過濾、Docker 沙箱隔離（陌生人的命令在沙箱裡跑）、安全稽核命令（`openclaw security audit`）。這些層是獨立的：配對碼洩露了？沙箱還在。沙箱被繞過了？工具策略還限制著能調什麼。

## 9. 幾個判斷

**單程序不是偷懶，是務實。** 對個人使用者來說，一個程序管到底比搞分散式靠譜得多。這個架構的天花板在哪？大概是同時在線的訊息量超過單機處理能力的時候：對絕大多數人來說，這一天不會來。

**頻道抽象層是最值錢的一層。** 二十多個平台的差異全封裝在適配器裡，Agent 完全不操心訊息從哪來。想加一個新平台？寫個適配器就行，Agent 邏輯一行不改。這個解耦做得非常乾淨。

**安全設計是認真的，但落地仍有差距。** 架構上從身份、沙箱到工具策略做了縱深防禦。但 Kaspersky 稽核發現了 512 個漏洞（8 個嚴重級別），說明藍圖畫得好和實際安全之間，隔著持續的工程投入。

**4 個核心工具的極簡路線是一場賭注。** 賭的是模型能力會持續上升，強到不需要預製工具就能「萬物皆可 Bash」。如果大模型的天花板卡住了，這條路會很難走。但如果模型能力繼續提升，這可能是最優雅的方案。

**最終的檢驗標準不是架構有多漂亮，而是跑得穩不穩。** 這篇分析基於原始碼靜態閱讀。實際效果：高併發下的 Gateway 穩定性、沙箱真的能不能防住攻擊、跨頻道認人的邊界情況：需要更多真實使用資料來驗證。

架構只是骨架，生產環境才是考場。</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category><category>openclaw</category></item><item><title>論文共讀：《Language Models are Few-Shot Learners》（語言模型是少樣本學習者）</title><link>https://justinhuangai.github.io/zh-hant/posts/language-models-are-few-shot-learners/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/language-models-are-few-shot-learners/</guid><description>更大的模型，更善於從上下文中誘發能力，附真實 Python 程式碼</description><pubDate>Wed, 11 Feb 2026 08:22:54 GMT</pubDate><content:encoded>2020 年 5 月 28 日，OpenAI 在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇 75 頁的論文：[《Language Models are Few-Shot Learners》](/papers/2005.14165v4.pdf)（語言模型是少樣本學習者）。

作者有 31 人，全部來自 OpenAI。第一作者 Tom B. Brown，其餘包括 Jared Kaplan（縮放定律的核心研究者）、Alec Radford（GPT-1 和 GPT-2 的主要設計者）、Ilya Sutskever（OpenAI 聯合創始人兼首席科學家）、Dario Amodei（OpenAI 研究副總裁）等。

這份作者名單後來分化出了幾家最重要的 AI 公司：Dario Amodei 和 Jared Kaplan 離開 OpenAI 創立了 Anthropic，Ilya Sutskever 後來也聯合創立了 Safe Superintelligence Inc.（SSI）。

論文的核心主張很直接：把語言模型做大，大到 1750 億參數，它就能在不更新任何權重的情況下，僅靠幾個示例就完成各種任務：有時甚至逼近經過專門微調的模型。

這不是任務級微調，而是在固定參數下，透過上下文適配任務的能力：論文稱之為**上下文學習（in-context learning）**。

## 0. 先認幾個詞

如果你對 GPT-3 這類模型的工作方式還沒有概念，先記住下面幾個詞就夠了：

- `語言模型`：給它一段前文，它的基本工作就是預測下一個詞。
- `參數量`：模型裡可學習的數字總數。你可以粗略把它理解成模型的「腦容量」。
- `prompt / 提示詞`：你餵給模型看的任務說明、示例和輸入。
- `上下文視窗`：模型一次能看到多少文本的容量。例子太多，塞不進去，就沒辦法一起看。
- `few-shot / one-shot / zero-shot`：分別指給多個例子、給一個例子、完全不給例子。
- `in-context learning / 上下文學習`：不改模型參數，只靠 prompt 裡的說明和例子，就讓模型臨時學會怎麼做任務。

## 1. 要解決什麼問題

[BERT](/zh-hant/posts/bert/) 確立的「預訓練 + 微調」範式在 2020 年已經是主流做法。效果很好，但論文指出了三個根本問題。

第一，每個新任務仍然需要一個標註資料集。標註資料的獲取成本高，且很多實際任務根本沒有對應的標註集。

第二，微調後的模型在測試基準上的表現，不一定反映真實泛化能力。模型可能只是學到了訓練資料中的虛假相關性（spurious correlations）：在基準集裡得分很高，但換個分布就崩了。

第三，人類不是這樣學習的。人類看一兩個例子，聽一句自然語言指令，就能完成新任務。而當時的 NLP 系統，每個新任務都需要成千上萬條標註資料來微調。

論文的出發點是：如果模型足夠大，它在預訓練階段累積的知識是否足以讓它直接「讀懂」任務描述和少量示例，然後給出答案？

## 2. 核心想法：不更新參數，只給提示

GPT-3 的評估方式和之前所有大模型都不一樣。它定義了三種設置，全部不涉及梯度更新：

**少樣本（Few-Shot）**：給模型一段任務描述，加上 10 到 100 個示例（具體數量取決於上下文窗口能裝多少），然後讓它完成新的輸入。不更新權重，不做反向傳播。

**單樣本（One-Shot）**：只給一個示例。這最接近人類學習新任務的方式：有人給你演示一次，你就上手。

**零樣本（Zero-Shot）**：連示例都不給，只有一句自然語言指令。這是最難的設置，但也是最實用的：如果模型真的「理解」了任務本身，它不應該需要任何例子。

```python
from dataclasses import dataclass
from typing import Union


@dataclass
class ZeroShot:
    instruction: str
    prompt: str


@dataclass
class OneShot:
    instruction: str
    example: tuple[str, str]
    prompt: str


@dataclass
class FewShot:
    instruction: str
    examples: list[tuple[str, str]]
    prompt: str


EvalSetting = Union[ZeroShot, OneShot, FewShot]


def build_prompt(setting: EvalSetting) -&gt; str:
    if isinstance(setting, ZeroShot):
        return f&quot;{setting.instruction}\n{setting.prompt}&quot;

    if isinstance(setting, OneShot):
        example_input, example_output = setting.example
        return f&quot;{setting.instruction}\n{example_input} {example_output}\n{setting.prompt}&quot;

    lines = [setting.instruction]
    lines.extend(f&quot;{example_input} {example_output}&quot; for example_input, example_output in setting.examples)
    lines.append(setting.prompt)
    return &quot;\n&quot;.join(lines)
```

論文把這種能力叫做**上下文學習**：模型在預訓練時，從海量文本中隱式地學到了各種任務的模式；推理時，示例被拼接進上下文，模型在前向傳播的過程中「識別」出當前任務是什麼，然後完成它。論文用「元學習」來描述這個過程：預訓練是外循環，上下文學習是內循環。

這和微調的區別是根本性的。微調修改模型參數來適應任務，上下文學習不修改任何東西：同一個模型，同一組權重，只靠輸入文本的不同，就能切換任務。

## 3. 模型架構與規模

GPT-3 的架構本身沒有新發明。它和 GPT-2 一樣，就是 [Transformer](/zh-hant/posts/attention-is-all-you-need/) 的解碼器部分，一層層堆起來。改動只有一處：在 Transformer 層中交替使用稠密注意力和局部帶狀稀疏注意力（來自 Sparse Transformer）。

真正不同的是規模。論文訓練了 8 個不同大小的模型，參數量跨越三個數量級：

| 模型 | 參數量 | 層數 | 隱藏維度 | 注意力頭數 |
|------|--------|------|----------|-----------|
| GPT-3 Small | 1.25 億 | 12 | 768 | 12 |
| GPT-3 Medium | 3.5 億 | 24 | 1024 | 16 |
| GPT-3 Large | 7.6 億 | 24 | 1536 | 16 |
| GPT-3 XL | 13 億 | 24 | 2048 | 24 |
| GPT-3 2.7B | 27 億 | 32 | 2560 | 32 |
| GPT-3 6.7B | 67 億 | 32 | 4096 | 32 |
| GPT-3 13B | 130 億 | 40 | 5140 | 40 |
| **GPT-3 175B** | **1750 億** | **96** | **12288** | **96** |

1750 億參數，96 層，96 個注意力頭，隱藏維度 12288。上下文窗口 2048 個詞元。這個規模在當時是前所未見的：比 GPT-2（15 億參數）大了 100 多倍。

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class GPT3Config:
    n_params: int
    n_layers: int
    d_model: int
    n_heads: int
    d_head: int
    d_ff: int
    n_ctx: int


def gpt3_175b() -&gt; GPT3Config:
    return GPT3Config(
        n_params=175_000_000_000,
        n_layers=96,
        d_model=12_288,
        n_heads=96,
        d_head=128,
        d_ff=49_152,
        n_ctx=2_048,
    )
```

論文訓練這些模型的目的很明確：驗證縮放定律（scaling laws）。之前 Kaplan 等人的研究（就是這篇論文的共同作者之一）已經表明，語言模型的損失和參數量之間存在平滑的冪律關係。GPT-3 把這個假設推到了 1750 億參數的規模，看看上下文學習能力是否也遵循同樣的規律。

答案是肯定的：模型越大，少樣本學習的提升越陡。零樣本性能隨模型規模穩步上升，少樣本性能的上升速度更快。這意味著大模型不只是「更準」，它們在利用上下文資訊的效率上也更高。

## 4. 訓練資料

GPT-3 在大約 3000 億個詞元上訓練，資料來自五個來源：

| 資料集 | 詞元數 | 訓練佔比 |
|--------|--------|----------|
| Common Crawl（過濾後） | 4100 億 | 約 60% |
| WebText2 | 190 億 | 約 22% |
| Books1 | 120 億 | 約 8% |
| Books2 | 550 億 | 約 8% |
| 英文 Wikipedia | 30 億 | 約 3% |

注意一個關鍵細節：資料集的取樣比例和它們的大小不成正比。品質更高的資料集（WebText2、Books、Wikipedia）被過取樣了：WebText2 在訓練中被看了 2.9 遍，Wikipedia 被看了 3.4 遍，而 Common Crawl 連一遍都沒看完（0.44 遍）。論文有意用少量過擬合的代價，換取更高品質的訓練訊號。

Common Crawl 的原始資料有 45TB，經過三步處理：（1）基於與高品質參考語料的相似度做過濾；（2）文件級模糊去重；（3）混入已知的高品質資料集來增加多樣性。過濾後剩下 570GB，約 4100 億詞元。

所有模型在 V100 GPU 上訓練，使用微軟提供的高頻寬叢集。

## 5. 實驗結果

論文在二十多個資料集上做了評估，覆蓋 9 大類任務。以下是幾個關鍵結果。

**語言建模**：在 Penn Tree Bank 上，GPT-3 少樣本困惑度（perplexity，衡量模型對文本的「意外程度」，越低越好）達到 20.50，刷新了當時的紀錄。在 LAMBADA（需要根據長距離上下文預測最後一個詞）上，零樣本準確率 76.2%，少樣本 86.4%，大幅超過之前的最好結果。

**翻譯**：GPT-3 從未被專門訓練過翻譯，但在法語→英語翻譯上，少樣本 BLEU 分數達到 32.6，超過了無監督神經機器翻譯的最好結果。不過英語→法語方向（25.2 BLEU）和微調模型的差距仍然很大。一個有趣的發現：GPT-3 翻譯成英語的能力明顯強於從英語翻譯出去，這和訓練資料以英語為主有直接關係。

**閉卷問答**：在 TriviaQA 上，少樣本準確率（exact match）71.2%，超過了同一閉卷設置下經過微調的模型。模型不看任何參考文件，純靠參數裡儲存的知識回答問題。

**SuperGLUE**：在這個綜合基準上，GPT-3 的少樣本表現已經接近一些經過微調的強基線，但仍落後於當時最強的專門微調系統。

**合成任務**：論文還設計了一些專門測試上下文學習能力的新任務。比如給模型幾個「造新詞」的例子（定義一個不存在的詞，然後用它造句），GPT-3 能正確地學會並使用這個新詞。再比如三位數加法，少樣本準確率接近 100%（兩位數加法也幾乎完美），但四五位數時急劇下降。

```python
from typing import Callable, Protocol


class AutoregressiveModel(Protocol):
    def forward(self, tokens: list[int]) -&gt; list[list[float]]:
        ...


def in_context_learning(
    model: AutoregressiveModel,
    examples: list[tuple[str, str]],
    query: str,
    tokenize: Callable[[str], list[int]],
    decode: Callable[[list[int]], str],
    sample_from: Callable[[list[float]], int],
    eos_token: int,
) -&gt; str:
    prompt_lines = [f&quot;{example_input} {example_output}&quot; for example_input, example_output in examples]
    prompt_lines.append(query)
    prompt = &quot;\n&quot;.join(prompt_lines)

    context = tokenize(prompt)
    output_tokens: list[int] = []

    while True:
        logits = model.forward(context)
        next_token = sample_from(logits[-1])
        if next_token == eos_token:
            break
        output_tokens.append(next_token)
        context.append(next_token)

    return decode(output_tokens)
```

## 6. 資料污染問題

論文在第四章花了大量篇幅討論一個棘手的問題：訓練資料和測試資料的重疊。

GPT-3 的訓練資料包含大量網路文本，而很多測試基準的內容也在網路上公開存在。這意味著模型可能在訓練時就「看過」了測試題。論文團隊嘗試在訓練前移除這些重疊，但由於一個處理流程中的 bug，部分重疊沒有被完全清除。而重新訓練一遍的成本太高，不現實。

他們的做法是：為每個基準建構一個「乾淨子集」（移除所有和訓練資料有 13-gram 重疊的樣本），然後對比模型在完整集和乾淨子集上的表現。結論是：大多數基準上，污染對結果的影響很小。但 PIQA 和 Winograd 兩個資料集存在可疑的表現下降，論文對這些結果加了星號標註。

這種誠實在當時相當罕見。多數論文對資料污染問題避而不談。GPT-3 不僅主動調查，還開發了系統化的檢測工具。這本身就是對後續研究的一個貢獻。

## 7. 局限性

論文在第五章對自身局限性的討論相當坦率。

**文本連貫性**：GPT-3 在文件級別仍然會出現語義重複、自相矛盾、甚至生成無意義句子的情況。生成品質雖然比 GPT-2 好了很多，但長文本的連貫性仍然不夠。

**常識物理**：GPT-3 對「把起司放進冰箱，它會融化嗎？」這類常識物理問題表現不佳。它能處理語言層面的推理，但對物理世界的理解仍然是膚淺的。

**單向性的代價**：作為自回歸模型，GPT-3 只能從左往右看。論文承認，在需要雙向上下文的任務上（比如判斷兩個句子裡同一個詞的含義是否相同），GPT-3 的少樣本表現不如經過微調的雙向模型。這說明在 GPT-3 的自回歸設定下，這類任務並不是它的強項；單向建模目標本身會帶來結構性偏好。

**取樣效率**：GPT-3 在預訓練階段看了約 3000 億個詞元，遠超人類一生接觸的文本量。論文明確指出，即使少樣本學習在推理時很高效，預訓練的資料需求仍然巨大。

**推理成本**：1750 億參數的模型，推理成本高且不方便部署。論文提到蒸餾（distillation，用大模型的輸出來訓練小模型）是一個可能的方向，但在千億參數量級上還沒有嘗試過。

## 8. 社會影響

論文用了整整一個章節（第六章）討論社會影響，涵蓋三個方面。

**濫用風險**：GPT-3 生成的新聞文章，人類評估者的識別準確率接近隨機猜測（約 52%）。模型越強，生成的虛假文本越難辨別。論文團隊表示已經在監控論壇和聊天群，追蹤惡意使用的趨勢。

**偏見**：論文用大量實驗測試了 GPT-3 在性別、種族和宗教方面的偏見。例如，在職業-性別關聯測試中，GPT-3 更傾向於將「nurse」和女性關聯、將「banker」和男性關聯。在宗教-情感關聯中，「Islam」更多地與暴力相關詞共現。論文承認這些偏見來自訓練資料，但沒有給出解決方案。

**能源消耗**：訓練 GPT-3 需要大量算力，論文引用了估算資料但沒有公布具體的能耗數字。不過論文指出，一旦訓練完成，模型可以被多次使用到不同任務上，比為每個任務單獨訓練模型更節能。

## 9. 我的思考

讀完這篇論文，有幾個感受。

第一，GPT-3 展示了一件事：規模能把上下文學習推到可用閾值。1750 億參數的模型不只是「更大的 GPT-2」，它在上下文學習上的表現比小模型強出了一個量級。模型在沒有任何參數更新的情況下，僅靠上下文中的幾個示例就能完成新任務。這種能力不是顯式手工設計出來的，而是在規模擴大過程中逐步增強，到 GPT-3 這個量級才第一次變得足夠清晰、足夠實用。BERT 證明了預訓練的價值，GPT-3 證明了規模的價值。

第二，論文的寫作方式值得注意。31 個作者，75 頁篇幅，用了大量實驗來回答一個簡單的問題：更大的模型是否更善於利用少量示例？他們沒有迴避局限性：文本連貫性、常識推理、資料污染、偏見：全部正面討論。這種嚴謹程度，在後來的大模型論文中反而越來越少見了。

第三，這篇論文的作者列表就是一部 AI 行業分裂史。Dario Amodei 和 Jared Kaplan 後來創立了 Anthropic（Claude 的開發商），Ilya Sutskever 後來離開 OpenAI 創立了 SSI。這些人在 2020 年還在同一個團隊裡合作寫論文，兩年後就走向了不同的方向。論文裡關於社會影響和安全風險的討論，也許正是後來分歧的伏筆。

第四，從技術演進的角度看，GPT-3 是從「預訓練 + 微調」到「預訓練 + 提示」的轉折點。BERT 的思路是：先學通用知識，再在每個任務上微調參數。GPT-3 說：如果模型夠大，微調這一步可以省掉：直接用自然語言告訴模型你要做什麼。這個思路後來演化成了 ChatGPT、Claude 等產品的核心互動範式：使用者用自然語言提問，模型直接回答。

從 Seq2Seq 的編碼-解碼，到 [Bahdanau 注意力](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)的「該看哪裡」，到 [Transformer](/zh-hant/posts/attention-is-all-you-need/) 的「所有位置同時看」，到 [BERT](/zh-hant/posts/bert/) 的「先學再調」，再到 GPT-3 的「大到不用調」：每一步都在減少人工干預，增加模型自主完成任務的能力。

GPT-3 不是終點。但它第一次讓人們認真思考一個問題：如果繼續把模型做大，還會湧現出什麼？

這個問題的答案，就是後來發生的一切。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學：為什麼更大的模型可預測地更好
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>gpt-3</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>OpenClaw 專題：生態分析 🦞</title><link>https://justinhuangai.github.io/zh-hant/posts/openclaw-ecosystem/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/openclaw-ecosystem/</guid><description>從一個開源專案到一個完整的 AI 助手生態</description><pubDate>Tue, 03 Feb 2026 08:09:32 GMT</pubDate><content:encoded>![OpenClaw](/images/openclaw-logo-text-dark.webp)

## 0. 先認幾個詞

如果你第一次看「生態分析」這類文章，可以先記住 5 個詞：

- `生態`：不只是一個倉庫，而是一組圍繞同一核心能力長出來的產品和工具。
- `執行環境 / runtime`：Agent 真正長期運行、負責調度和執行的核心程序。
- `技能市場`：給 Agent 安裝新能力的地方，有點像 App Store。
- `工作流引擎`：把重複的多步驟任務封裝成可以反覆呼叫的流程。
- `飛輪`：一種增長迴路；供給越好，使用越多，反過來又吸引更多供給。

## 1. 先看人

看專案先看人。

Peter Steinberger，奧地利人。2011 年創辦 PSPDFKit，做 PDF 底層技術，客戶有 Apple 和 Dropbox，服務了超 10 億台裝置。結果？外界的公開說法是「九位數級別」。然後他退居幕後。

然後他倦怠了。自己說「盯著螢幕發呆，寫不出程式碼」。買了張去馬德里的單程票，徹底斷開了一陣子。

2025 年中，他在部落格裡寫：狀態回來了。AI 已經不是 demo 階段了，能出產品原型了。他用大約一小時提示詞生成了專案雛形，11 月發佈，取名 Clawdbot：向 Anthropic 的 Claude 致敬（Claw = 爪子），選了龍蝦當吉祥物。

2026 年 1 月底，Anthropic 發了商標警告，名字和 Claude 太像。三天內從 Clawdbot 改成 Moltbot（Molt = 蛻殼），又改成 OpenClaw。改名這件事本身引爆了關注：48 小時新增 34,000 星標。

一個做出過九位數結果的人，把精力押在一個 MIT 開源專案上。動機且不論，這個選擇本身值得認真看。

## 2. 不只是一個專案，已經長出生態

大部分爆紅開源專案的「生態」約等於：文件裡一張路線圖 + 幾個佔位倉庫。

OpenClaw 不一樣。它已經長出了真實的產品分層：

| 元件 | 幹什麼 | 星標數 |
|------|------|--------|
| **OpenClaw** | 核心 Agent 執行環境：大腦 | 140k+ |
| **ClawHub** | 技能市場：Agent 的應用商店 | 5.4k |
| **Lobster** | 工作流引擎：把重複操作打包成一鍵執行 | ~800 |
| **acpx** | 無頭命令列工具 | ~780 |
| **openclaw-ansible** | 自動化部署：一條命令裝好 | ~490 |
| **nix-openclaw** | Nix 宣告式組態 | ~530 |

執行環境、技能市場、工作流引擎、部署工具：各管各的，職責邊界乾淨。這不是「把所有東西塞進一個倉庫」的粗暴擴張，是有設計的分層。

## 3. 幾個關鍵判斷

### 頻道覆蓋：不是炫技，是「不用你動」

WhatsApp、Telegram、Slack、Discord、Signal、iMessage、飛書、LINE、Matrix……二十多個平台直接接入。

這意味著什麼？你不用為了用 AI 助手下載一個新 App、學一套新介面、改變任何習慣。它直接出現在你已經在用的聊天裡。WhatsApp 裡發一條訊息就能用：跟給朋友發訊息一樣。

頻道越多，離真實使用場景越近，第一次用起來的門檻就越低。這種「低摩擦接入」帶來的不只是方便，更是採用率的碾壓優勢。底層當然有協議適配和維護成本，但那是代價，不是重點。重點是它把 AI 助手從「一個你要去打開的新工具」變成了「已經在你聊天裡的能力」。

### ClawHub：方向對，但還太早

技能市場用向量搜尋做語義匹配：你不用翻分類目錄，直接跟它說「我想要一個能發郵件的技能」，它就能幫你找到。設計意圖很好。

但技能市場的飛輪能不能轉起來，取決於兩件事：優質技能夠不夠多，發現效率夠不夠高。後者已經做到了。前者呢？第三方技能的發佈頻率、安裝量、更新頻率、審核情況：這些資料目前都沒有公開的儀表板可查。飛輪開始轉了，但離自己能轉還早。

### Lobster：解決了一個隱藏痛點

AI Agent 最大的隱性成本是什麼？重複規劃。

每次讓 Agent 幹一個多步驟任務，它都要從頭想一遍：先幹啥、再幹啥、怎麼幹。token 就是這麼燒掉的。Lobster 的思路是：把高頻操作打包成「一鍵執行」的流水線：封裝一次，以後直接呼叫，不用重新規劃。

它還有個審批關卡：關鍵步驟會停下來等你點頭，不讓 Agent 一路狂奔。這說明團隊對「Agent 該有多少自主權」這件事，至少認真想過。

### 安全：繞不過去的坎

Kaspersky 審計了 512 個漏洞，8 個嚴重級別（審計時還叫 Clawdbot）。Cisco 安全團隊直接稱之為「安全噩夢」。Gary Marcus 公開說這是「一場注定發生的災難」。

問題的本質是結構性的：你給 Agent 的權限越大，它能幹的事越多，但攻擊面也越大。提示詞注入可以騙它幹壞事，已經有技能被發現偷偷往外傳資料。

OpenClaw 不是沒意識到：配對碼、白名單、沙箱隔離、命令審批，一層一層在收緊。但「高權限 Agent + 第三方技能 + 二十多個入口」這個組合帶來的結構性張力，不是修幾個 bug 就能消除的。這是一場持久戰。

## 4. 風險，不說不行

說完好的，說不好的。

**安全債 ≠ 技術債，是信任債。** 一次出圈的安全事件，受傷的不只 OpenClaw，是所有走「自託管 AI Agent」路線的專案。整個賽道的敘事都會被拖下水。

**商業模式是個問號。** MIT 協議、無訂閱、使用者自帶 API 金鑰。Peter 在公開場合提過，專案每月伺服器成本在一兩萬美元。靠贊助撐的開源專案，可持續性取決於熱度能不能變成錢。目前還沒看到清晰路徑。

**增長品質存疑。** 1 月 30 日的爆發跟 Moltbook（AI Agent 社交網路）的走紅高度綁定。病毒式傳播帶來的星標，有多少會變成真正的生態貢獻者？星標 ≠ 程式碼貢獻 ≠ 生態厚度。

**單點依賴。** 18,000+ 次提交，核心路線和產品判斷仍然高度依賴創始人一個人。社群已經加了維護者，但能不能從「一個人的專案」變成「一群人的平台」，是後面真正的分水嶺。

## 5. 結論

OpenClaw 真正稀缺的地方不是熱度：熱度誰都能有一陣子。稀缺的是它已經從一個專案長出了生態的雛形：有分層、有分工、有產品形態。

但熱度終究會退。

退潮之後，決定命運的是 4 件事：

1. **安全收緊了沒有？** 配對、白名單、沙箱這些機制，從「可選」變成「預設」了嗎？
2. **技能生態轉起來了沒有？** 不看星標數，看高品質技能的發佈、安裝和審核有沒有形成正循環。
3. **非創始人貢獻佔比漲了沒有？** 這決定它是「明星專案」還是「可持續平台」。
4. **治理結構清晰了沒有？** 這決定它能不能從熱度專案變成長期基礎設施。

我會持續追蹤。</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category><category>openclaw</category></item><item><title>論文共讀：《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》（BERT：用於語言理解的深度雙向 Transformer 預訓練）</title><link>https://justinhuangai.github.io/zh-hant/posts/bert/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/bert/</guid><description>預訓練範式的確立，附真實 Python 程式碼</description><pubDate>Sat, 31 Jan 2026 08:52:21 GMT</pubDate><content:encoded>2018 年 10 月 11 日，Google AI Language 團隊在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文：[《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/papers/1810.04805v2.pdf)（BERT：用於語言理解的深度雙向 Transformer 預訓練）。

作者是 Jacob Devlin、Ming-Wei Chang、Kenton Lee 和 Kristina Toutanova，四人均來自 Google。Devlin 此前在微軟研究院工作，加入 Google 後主導了 BERT 的設計和實現。

BERT 的全名是 Bidirectional Encoder Representations from Transformers：來自 Transformer 的雙向編碼器表示。它做了一件在當時看來非常大膽的事：先在海量無標註文本上做通用預訓練，然後只需要加一層輸出層、在具體任務上做少量微調，就能拿到最優結果。

這個「先預訓練、再微調」的範式，後來成為了整個 NLP 領域的標準做法。GPT 系列也用了類似的思路，但走的是另一條路：單向生成。BERT 選擇了雙向理解。兩條路後來各自發展出了龐大的模型家族。

## 0. 先認幾個詞

如果你對大模型的訓練流程還不熟，可以先記住這篇論文最關鍵的幾個詞：

- `Transformer`：BERT 用的基礎架構。你可以先把它理解成一台能同時結合左右上下文來處理句子的機器。
- `預訓練`：先在海量通用文本上學語言本身，而不是一開始就做具體任務。
- `微調`：把預訓練得到的能力，再稍微調整到某個具體任務上。
- `雙向`：預測一個位置時，不只看左邊，也看右邊。
- `MLM / 遮蔽語言模型`：故意遮住一部分詞，讓模型根據上下文把它們猜出來。
- `NSP / 下一句預測`：讓模型判斷兩個句子是不是前後相連。

## 1. 要解決什麼問題

2018 年，NLP 領域有一個尷尬的現狀：每個任務都需要從頭設計專門的模型架構。做問答要一套模型，做情感分析要另一套，做命名實體識別又要一套。每個任務的標註資料都不多，訓練出來的模型也很難遷移到其他任務。

當時已經有人嘗試過預訓練的思路。ELMo 用雙向 LSTM 學習上下文表示，但它只是把預訓練的特徵「拼」到下游模型上，架構本身還是任務專用的。OpenAI GPT 用 Transformer 做預訓練再微調，但它只能從左往右看（單向），每個詞只能關注它前面的詞，看不到後面的。

論文認為，單向語言模型在需要深度雙向上下文的語言理解任務上存在明顯限制。比如：

&gt; &quot;他拿起了 _____ ，開始演奏。&quot;

如果只看左邊（&quot;他拿起了&quot;），填空的答案可能是任何東西。但看到右邊（&quot;開始演奏&quot;），你立刻知道是某種樂器。對很多語言理解任務來說，雙向上下文天然更有利。

## 2. 核心想法：遮住一些詞，讓模型猜

BERT 的解法很直覺：既然雙向語言模型沒法用傳統方式訓練（因為每個詞會間接「看到自己」），那就換個訓練目標。

**遮蔽語言模型（Masked Language Model，MLM）**：隨機遮住輸入中 15% 的詞：具體做法是把它們替換成一個特殊標記 \[MASK\]：然後讓模型根據上下文猜出被遮住的詞。這個想法來自心理學中的完形填空（Cloze task，1953 年 Taylor 提出），就像上面那道填空題一樣。

遮住之後，模型必須同時利用左邊和右邊的上下文來預測，雙向理解就自然產生了。

但直接把所有被選中的詞替換成 \[MASK\] 標記會引入一個問題：微調時輸入裡不會出現 \[MASK\]，預訓練和微調之間產生了不匹配。論文的解決方案：被選中的 15% 的詞裡，80% 替換成 \[MASK\]，10% 替換成隨機詞，10% 保持不變。這樣模型不能只靠「看到 \[MASK\] 就知道要預測」，而是必須對每個位置都保持理解能力。

```python
import random
from typing import Optional, Sequence


def mask_tokens(
    tokens: Sequence[str],
    mask_prob: float = 0.15,
    vocab: Optional[Sequence[str]] = None,
) -&gt; tuple[list[str], list[int], list[str]]:
    if vocab is None:
        vocab = tokens

    masked = list(tokens)
    positions: list[int] = []
    labels: list[str] = []

    for i, token in enumerate(tokens):
        if random.random() &lt; mask_prob:
            positions.append(i)
            labels.append(token)

            r = random.random()
            if r &lt; 0.8:
                masked[i] = &quot;[MASK]&quot;
            elif r &lt; 0.9:
                masked[i] = random.choice(vocab)

    return masked, positions, labels
```

## 3. 第二個預訓練任務：下一句預測

很多 NLP 任務（比如問答、自然語言推理）需要理解兩個句子之間的關係，但語言模型本身不直接建模這種關係。

論文加了第二個預訓練任務：**下一句預測（Next Sentence Prediction，NSP）**。給模型兩個句子 A 和 B，50% 的情況下 B 是 A 的真實下一句，50% 的情況下 B 是從語料庫裡隨機抽的。模型要判斷 B 是不是 A 的下一句。

這個任務的設計很簡單，但論文的消融實驗（ablation study，逐一去掉某個元件看效果變化）顯示，去掉 NSP 會明顯降低問答和自然語言推理任務的表現；不過後來也有工作（如 RoBERTa）對 NSP 的必要性提出了不同結論。

```python
from dataclasses import dataclass


@dataclass
class PretrainingExample:
    tokens: list[str]
    segment_ids: list[int]
    masked_positions: list[int]
    masked_labels: list[str]
    is_next: bool
```

## 4. 模型架構

BERT 的架構其實沒有什麼新發明。它就是 [Transformer](/zh-hant/posts/attention-is-all-you-need/) 的編碼器部分，一層層堆起來。

論文給出了兩個規格：

- **BERT_BASE**：12 層，隱藏維度 768，12 個注意力頭，參數量 1.1 億
- **BERT_LARGE**：24 層，隱藏維度 1024，16 個注意力頭，參數量 3.4 億

BERT_BASE 的參數量和 OpenAI GPT 差不多，方便直接對比。兩者最關鍵的區別只有一個：GPT 用的是單向注意力（每個詞只能看左邊），BERT 用的是雙向注意力（每個詞能看到所有位置）。

輸入的表示由三部分相加構成：

- **詞嵌入（Token Embedding）**：WordPiece 分詞，詞表 30,000
- **段嵌入（Segment Embedding）**：標記這個詞屬於句子 A 還是句子 B
- **位置嵌入（Position Embedding）**：告訴模型詞的位置（BERT 用的是學習得到的位置編碼，不是正弦餘弦）

每個輸入序列的開頭都加一個特殊標記 \[CLS\]，它在最後一層的隱藏狀態被用來做句子級別的分類（比如 NSP、情感分析）。兩個句子之間用 \[SEP\] 分隔。

```python
import torch
from torch import nn


class BertEmbeddings(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        hidden_size: int,
        max_positions: int,
        type_vocab_size: int = 2,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.token_embedding = nn.Embedding(vocab_size, hidden_size)
        self.segment_embedding = nn.Embedding(type_vocab_size, hidden_size)
        self.position_embedding = nn.Embedding(max_positions, hidden_size)
        self.layer_norm = nn.LayerNorm(hidden_size, eps=1e-12)
        self.dropout = nn.Dropout(dropout)

    def forward(
        self,
        token_ids: torch.Tensor,
        segment_ids: torch.Tensor,
    ) -&gt; torch.Tensor:
        position_ids = torch.arange(token_ids.size(1), device=token_ids.device).unsqueeze(0)
        position_ids = position_ids.expand_as(token_ids)
        embeddings = (
            self.token_embedding(token_ids)
            + self.segment_embedding(segment_ids)
            + self.position_embedding(position_ids)
        )
        embeddings = self.layer_norm(embeddings)
        return self.dropout(embeddings)


class BertModel(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        hidden_size: int = 768,
        max_positions: int = 512,
        num_layers: int = 12,
        num_heads: int = 12,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.embeddings = BertEmbeddings(vocab_size, hidden_size, max_positions, dropout=dropout)
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=hidden_size,
            nhead=num_heads,
            dim_feedforward=4 * hidden_size,
            dropout=dropout,
            activation=&quot;gelu&quot;,
            batch_first=True,
        )
        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)

    def forward(
        self,
        token_ids: torch.Tensor,
        segment_ids: torch.Tensor,
    ) -&gt; torch.Tensor:
        hidden = self.embeddings(token_ids, segment_ids)
        return self.encoder(hidden)
```

## 5. 微調：一個模型適配所有任務

BERT 最優雅的地方在於微調的簡單性。預訓練完成後，不管什麼下游任務，做法幾乎一樣：在 BERT 上面加一層任務相關的輸出層，然後用少量標註資料微調所有參數。

- **文本分類**（情感分析、自然語言推理）：取 \[CLS\] 位置的輸出向量，接一個線性分類器
- **問答**（給一段文章，找出答案的起止位置）：對每個詞的輸出向量做兩次線性變換，分別預測答案的開始和結束位置
- **序列標註**（命名實體識別）：對每個詞的輸出向量接一個分類器，逐詞預測標籤

預訓練可能需要幾天，但微調通常只要幾十分鐘到幾小時（單塊 TPU 上大部分任務不超過 1 小時）。這個效率差異是「預訓練 + 微調」範式的核心吸引力。

## 6. 實驗結果

論文在 11 個 NLP 任務上做了實驗，全部刷新了當時的紀錄。

**GLUE 基準**（通用語言理解評估，包含 8 個子任務）：
- BERT_LARGE 平均分 80.5%，比之前最好的 OpenAI GPT 高出 7.7 個百分點
- 在最大的子任務 MNLI 上提升了 4.6%

**SQuAD v1.1**（閱讀理解問答，Test F1）：
- BERT_LARGE 單模型 + TriviaQA 資料：F1 91.8，超過人類表現（91.2）
- BERT_LARGE 集成模型 + TriviaQA 資料：F1 93.2

**SQuAD v2.0**（包含無法回答的問題）：
- F1 達到 83.1，比之前最好的系統高出 5.1 個百分點

**SWAG**（常識推理）：
- 準確率 86.3%，比 OpenAI GPT 高出 8.3 個百分點

論文還做了模型大小的消融實驗，發現一個重要結論：更大的模型在所有任務上都更好，即使在標註資料很少（只有 3,600 條）的任務上也是如此。這和當時的直覺（小資料集容易過擬合大模型）不太一樣，說明預訓練提供的知識可以有效緩解小資料集的過擬合（模型把訓練資料「死記硬背」，對新資料表現差）風險。

## 7. 訓練細節

**預訓練資料**：BooksCorpus（8 億詞）+ 英文 Wikipedia（25 億詞），只使用文本段落，去掉了列表、表格和標題。論文強調必須用文件級語料而不是打亂的句子級語料，這樣才能提取長距離的上下文關係。

**分詞**：WordPiece，詞表大小 30,000。WordPiece 會把不常見的詞拆成更小的子詞單元，比如 &quot;playing&quot; 可能被拆成 &quot;play&quot; + &quot;##ing&quot;。

**最佳化器**：Adam，學習率 1e-4，前 10,000 步線性熱身，然後線性衰減。批次大小 256 個序列，最大序列長度 512。

**硬體**：BERT_BASE 在 4 塊 Cloud TPU（16 塊 TPU 晶片）上訓練 4 天。BERT_LARGE 在 16 塊 Cloud TPU（64 塊 TPU 晶片）上訓練 4 天。

**Dropout**：所有層的 dropout 率為 0.1。激活函數用的是 GELU（Gaussian Error Linear Unit），而不是 Transformer 原版的 ReLU。

## 8. 我的思考

讀完這篇論文，有幾個感受。

第一，BERT 的真正貢獻不是模型架構（它就是 Transformer 編碼器），而是訓練方法。遮蔽語言模型這個想法看起來簡單，但它巧妙地解決了一個根本矛盾：怎麼在不讓模型「作弊」的前提下，同時利用雙向上下文。80/10/10 的遮蔽策略更是精心設計的，解決了預訓練和微調之間的不匹配問題。

第二，BERT 和 GPT 的分野在這篇論文裡就很清楚了。GPT 的自回歸目標更天然適合生成；BERT 的雙向編碼更適合判別式語言理解任務。後來 GPT 走向了更大的規模和更強的生成能力，BERT 則衍生出了 RoBERTa、ALBERT、DeBERTa 等一系列理解型模型。兩條路線至今仍在各自的領域裡發揮作用。

第三，「預訓練 + 微調」這個範式的影響遠超 NLP。電腦視覺後來也全面轉向了類似的思路（ViT、MAE），甚至多模態模型（CLIP、GPT-4V）也是在大規模預訓練的基礎上做微調或提示。BERT 不是第一個做預訓練的，但它是第一個用如此簡潔的方式，把預訓練從一種有用技巧，推進成了 NLP 的主流工作範式。

第四，用真實 Python 重寫 BERT 的輸入處理時，你會感受到它的設計有多工整。\[CLS\] + 句子A + \[SEP\] + 句子B + \[SEP\]，配上三種嵌入相加，整個流程可以用一套統一的程式碼處理分類、問答、序列標註等完全不同的任務。這種「一個模型適配所有任務」的簡潔性，是它真正的力量所在。

這篇論文的標題裡有一個詞很關鍵：Pre-training。在 BERT 之前，每個 NLP 任務都在從零開始學。BERT 證明了一件事：語言的通用知識可以先學好，然後遷移到幾乎任何任務上。

這個想法改變了整個領域的工作方式。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學：為什麼更大的模型可預測地更好
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>bert</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>論文共讀：《Sequence to Sequence Learning with Neural Networks》（使用神經網路進行序列到序列學習）</title><link>https://justinhuangai.github.io/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/</guid><description>編碼器-解碼器範式的確立，附真實 Python 程式碼</description><pubDate>Sat, 24 Jan 2026 08:41:08 GMT</pubDate><content:encoded>2014 年 9 月 10 日，三個 Google 研究員在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文：[《Sequence to Sequence Learning with Neural Networks》](/papers/1409.3215v3.pdf)（使用神經網路進行序列到序列學習）。

作者是 Ilya Sutskever、Oriol Vinyals 和 Quoc V. Le，均來自 Google。Sutskever 是 AlexNet 的作者之一，與 Alex Krizhevsky、Geoffrey Hinton 合作完成了那篇引爆深度學習的電腦視覺論文，後來成為 OpenAI 聯合創始人之一；Vinyals 後來在 DeepMind 主導了 AlphaStar（星際爭霸 AI）；Quoc V. Le 則在 Google 推動了 AutoML 等研究。

這篇論文做了一件看似簡單的事：用一個神經網路讀完一句話，壓成一個向量，再用另一個神經網路從這個向量裡生成翻譯。輸入和輸出可以長度不同、語言不同、結構不同。這個框架有一個名字，叫「序列到序列」（Sequence to Sequence，簡稱 Seq2Seq）。

它確立了編碼器-解碼器（Encoder-Decoder）這個範式。後來 [Bahdanau 在此基礎上加入了注意力機制](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)，再後來 [Vaswani 等人用 Transformer 把整個架構重寫](/zh-hant/posts/attention-is-all-you-need/)。但起點，是這篇論文。

## 0. 先認幾個詞

如果你沒有機器學習背景，可以先按這篇論文的工作流程，記住下面幾個詞：

- `Seq2Seq / 序列到序列`：把一段輸入序列直接變成另一段輸出序列，比如把英文句子變成法文句子。
- `Encoder / 編碼器`：負責把輸入從頭到尾讀完。
- `Decoder / 解碼器`：負責把輸出一個詞一個詞寫出來。
- `RNN / 循環神經網路`：一種只能按順序處理文本的舊架構。
- `LSTM`：RNN 的改良版，更擅長在長句子裡記住前面的資訊。
- `向量 / vector`：你可以先把它理解成「用一串數字壓縮出來的一份摘要」。

## 1. 要解決什麼問題

2014 年，深度神經網路已經在圖像識別等任務上取得突破，但像機器翻譯這種「直接把一段可變長序列映射到另一段可變長序列」的任務，神經網路還不擅長。

一句英語可能是 5 個詞，翻譯成法語變成 7 個詞。輸入和輸出的長度不同，而且沒有簡單的一一對應關係。

傳統的解決方案是把大量人工設計的規則和統計特徵拼在一起，形成一個複雜的翻譯流水線（統計機器翻譯，SMT）。它能用，但每個元件都要單獨調參，而且很難整體最佳化。

論文提出了一個更簡潔的思路：能不能用一個端到端的神經網路，直接從源語言序列映射到目標語言序列？

## 2. 核心架構：編碼器-解碼器

論文的方法可以用一句話概括：**一個 LSTM 讀，另一個 LSTM 寫。**

LSTM（Long Short-Term Memory，長短期記憶網路）是一種特殊的 RNN，專門設計來處理長距離依賴問題。普通 RNN 在序列很長時容易「遺忘」前面的內容，LSTM 通過引入門控機制（決定哪些資訊保留、哪些丟棄）來緩解這個問題。

具體流程：

1. **編碼器**（一個 4 層深度 LSTM）從頭到尾讀完源句子，把整個句子壓縮成一組固定長度的最終狀態，交給解碼器作為起點
2. **解碼器**（另一個 4 層深度 LSTM）以這個向量為起點，一個詞一個詞地生成目標語言的翻譯，直到輸出結束符號 \&lt;EOS\&gt;

論文給出的機率公式：

$$
p(y_1, \ldots, y_{T&apos;} \mid x_1, \ldots, x_T) = \prod_t p(y_t \mid v, y_1, \ldots, y_{t-1})
$$

翻譯成人話：給定源句子 x，生成目標句子 y 的機率，等於每一步生成下一個詞的機率連乘起來。每一步的預測都依賴兩樣東西：編碼器壓縮出來的向量 v，以及之前已經生成的所有詞。

```python
import torch
from torch import nn


class Seq2Seq(nn.Module):
    def __init__(self, vocab_size: int, hidden_size: int) -&gt; None:
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, hidden_size)
        self.encoder = nn.LSTM(hidden_size, hidden_size, num_layers=4, batch_first=True)
        self.decoder = nn.LSTM(hidden_size, hidden_size, num_layers=4, batch_first=True)
        self.output_proj = nn.Linear(hidden_size, vocab_size)

    def encode(
        self,
        source_tokens: torch.Tensor,
    ) -&gt; tuple[torch.Tensor, tuple[torch.Tensor, torch.Tensor]]:
        embedded = self.embedding(source_tokens)
        outputs, state = self.encoder(embedded)
        return outputs, state

    def decode(
        self,
        encoder_state: tuple[torch.Tensor, torch.Tensor],
        max_steps: int,
        bos_token_id: int,
        eos_token_id: int,
    ) -&gt; list[int]:
        prev_token = torch.tensor([[bos_token_id]], dtype=torch.long, device=encoder_state[0].device)
        state = encoder_state
        generated: list[int] = []

        for _ in range(max_steps):
            embedded = self.embedding(prev_token)
            output, state = self.decoder(embedded, state)
            logits = self.output_proj(output[:, -1, :])
            next_token_id = int(logits.argmax(dim=-1).item())
            if next_token_id == eos_token_id:
                break
            generated.append(next_token_id)
            prev_token = torch.tensor([[next_token_id]], dtype=torch.long, device=logits.device)

        return generated
```

架構本身不複雜。論文的貢獻不在於發明了某個新元件，而在於證明了這個簡單的框架真的能用，而且效果好到能和精心調校的傳統系統競爭。

## 3. 三個關鍵設計決策

論文在實驗中發現了三個對性能影響很大的設計選擇：

**第一，用兩個獨立的 LSTM。** 編碼器和解碼器不共享參數。這樣做稍微增加了參數量，但讓模型能更好地分別處理源語言和目標語言的特性。論文提到這也讓同時訓練多個語言對成為可能。

**第二，用深層 LSTM。** 論文用了 4 層 LSTM，每多一層，困惑度降低近 10%。淺層 LSTM（1-2 層）的效果明顯更差。深度給了模型更大的表示空間。

**第三，把源句子倒過來讀。** 這是論文最出人意料的發現。把源句子 &quot;a, b, c&quot; 反轉成 &quot;c, b, a&quot; 再餵給編碼器，BLEU 分數從 25.9 跳到 30.6，提升了將近 5 分。

為什麼反轉有效？論文的解釋是：正常順序下，源句子第一個詞離目標句子第一個詞很遠（中間隔了整個源句子）。反轉之後，源句子的前幾個詞和目標句子的前幾個詞在時間上靠得很近，給梯度（模型用來調整參數的訊號）創造了更多的「短距離依賴」，讓最佳化變得更容易。

```python
import torch


def reverse_source(source_tokens: list[int]) -&gt; list[int]:
    return list(reversed(source_tokens))


source_sentence = [11, 23, 37, 42]
reversed_source = reverse_source(source_sentence)
source_tensor = torch.tensor([reversed_source], dtype=torch.long)
```

這個 trick 簡單到幾乎不像是正經的研究貢獻，但它確實有效，而且揭示了一個深層問題：RNN 對序列裡元素之間的距離很敏感，距離越近越容易學。這個問題後來被注意力機制從根本上解決了。

## 4. 實驗結果

論文在 WMT &apos;14 英法翻譯任務上做了實驗。

關鍵數字：
- **單個反轉 LSTM**，beam size 12：30.59 BLEU
- **5 個反轉 LSTM 的集成**，beam size 2：34.50 BLEU
- **5 個反轉 LSTM 的集成**，beam size 12：**34.81 BLEU**
- **傳統短語翻譯系統**（Moses baseline）：33.30 BLEU

在論文報告的實驗設置下，5 個 LSTM 的集成以 34.81 分超過了傳統短語翻譯系統的 33.30 分。考慮到 LSTM 的詞表只有 8 萬詞（遇到詞表外的詞只能輸出 UNK），而傳統系統的詞表幾乎不受限，這個結果很有說服力。

論文還用 LSTM 對傳統系統的 1000-best 候選列表做重排序，BLEU 分數進一步提升到 36.5，接近當時的最佳公開結果（37.0）。

另一個值得注意的發現：相比當時其他神經方法，LSTM 在長句子上的性能退化沒那麼嚴重。這和當時其他研究者報告的長句子性能急劇下降形成了對比，論文將此歸功於反轉源句子的策略。

## 5. 模型的「理解力」

論文還做了一個有趣的視覺化實驗。把不同句子輸入編碼器，取出最終的隱藏狀態向量，用 PCA 降維到二維平面上畫出來。

結果顯示：
- 意思相近的句子在向量空間裡聚在一起
- 主動語態和被動語態的句子（&quot;I gave her a card&quot; vs &quot;I was given a card by her&quot;）落在相近的位置
- 詞序不同但意思相同的句子也能被正確聚類

這至少說明，編碼器學到的表示不只是簡單的詞袋統計（把詞混在一起不管順序），而是包含了相當多的句法和語義資訊。

## 6. 訓練細節

**模型規格**：4 層 LSTM，每層 1000 個單元，詞嵌入維度 1000，總參數量 3.84 億。其中 6400 萬是純循環連接參數。

**硬體**：8 塊 GPU。每層 LSTM 分配一塊 GPU，剩餘 4 塊 GPU 用來並行化 softmax（因為詞表有 8 萬個詞，softmax 計算量很大）。訓練約 10 天。

**最佳化器**：SGD，不帶動量，初始學習率 0.7。訓練 5 個 epoch 之後每半個 epoch 將學習率減半，總共訓練 7.5 個 epoch。

**梯度裁剪**：當梯度的 L2 範數超過閾值 5 時，按比例縮小。這是為了防止梯度爆炸（梯度值突然變得極大，導致參數更新失控）。

**批次最佳化**：把長度相近的句子放在同一個批次裡，避免短句子為長句子「陪跑」浪費計算資源，帶來了 2 倍的訓練加速。

## 7. 我的思考

讀完這篇論文，有幾個感受。

第一，這篇論文的野心很大但方法很簡單。用一個 LSTM 讀，另一個 LSTM 寫，中間就靠一個向量傳遞資訊。沒有注意力，沒有複雜的對齊機制，甚至沒有任何關於語言結構的先驗假設。然後它真的跑通了，而且效果強到足以和當時精心調校的傳統系統競爭。這說明一個道理：在資料和算力充足的條件下，簡單的端到端方法可以出乎意料地強。

第二，反轉源句子這個發現很有啟發性。它不是一個優雅的解法，更像是一個 hack。但它揭示了 RNN 的根本局限：對序列裡元素之間的距離敏感。Bahdanau 的注意力機制讓模型可以「跳著看」，不再受距離限制。Transformer 更進一步，完全拋棄了順序處理，讓任意兩個位置之間的距離始終是 1。從反轉到注意力到 Transformer，是同一個問題的三代解法。

第三，這篇論文和 Bahdanau 的論文幾乎同時發表（都是 2014 年 9 月）。Sutskever 確立了編碼器-解碼器範式，Bahdanau 指出了固定長度向量的瓶頸並用注意力機制解決了它。兩篇論文像是同一枚硬幣的正反面：一面是框架，一面是修補框架最大缺陷的方法。

第四，用真實 Python 重寫的過程中，你會感受到這個架構有多簡潔。編碼器就是循環讀完，解碼器就是循環寫出來。但也正因為簡潔，它的天花板很明顯：所有資訊必須擠過一個固定長度的向量。這個瓶頸在你寫程式碼的時候會變得格外直觀。

一個向量能裝下多少資訊？這是這篇論文暗含的問題。

對更長、更複雜的句子來說，不夠。

於是後來有了注意力，有了 Transformer。

---

**論文共讀系列**

- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學：為什麼更大的模型可預測地更好
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>seq2seq</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>Clawdbot：一個值得關注的去中心化 AI 開源專案</title><link>https://justinhuangai.github.io/zh-hant/posts/clawdbot/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/clawdbot/</guid><description>一個把所有聊天渠道接入 AI Agent 的自託管平台</description><pubDate>Fri, 16 Jan 2026 08:34:57 GMT</pubDate><content:encoded>現在主流的 AI 助手，幾乎都是中心化的。

你的對話、你的資料、你的上下文，最終都沉澱在別人的伺服器裡。你在使用 AI，但你並不真正擁有它。

[Clawdbot](https://github.com/clawdbot/clawdbot) 這個開源專案，走的恰恰是反方向。

它想做的，不是再造一個更聰明的聊天機器人，而是把 AI 的能力真正放回使用者手中：跑在你自己的機器上，接入你已經在用的聊天工具，讓資料、上下文和控制權都留在你自己這裡。

我翻了一遍它的程式碼庫：二十多萬行 TypeScript，覆蓋 macOS、iOS、Android 三端原生應用，外加 50 多個技能模組。

![WhatsApp 中與 Clawd 對話](/images/whatsapp-clawd.webp)

## 0. 先認幾個詞

如果你還沒接觸過這類專案，先記住 5 個詞，這篇文章會更好讀：

- `中心化 AI 助手`：聊天記錄、記憶和控制權主要都留在平台方的伺服器裡。
- `自託管`：軟體跑在你自己的機器或伺服器上，部署權和資料權都在你手裡。
- `AI Agent`：不只是會聊天的機器人，而是能記住上下文、呼叫工具、替你做事的一套系統。
- `聊天渠道`：WhatsApp、Telegram、Slack、iMessage 這類你已經在用的聊天入口。
- `開源`：程式碼公開，別人可以檢查、修改，也可以自己部署。

這已經不是週末 Hackathon 式的「整活專案」了，而是一套按長期產品思路打磨出來的系統。很多細節裡，都能看出作者在產品取捨上是有 taste 的。

更難得的是，它的邊界感很好。

該有的能力都有；不該塞的東西，也沒硬塞進去。沒有為了「看起來更大」去堆砌功能，也沒有很多 AI 專案常見的炫技感和失控感。

這種克制，比「什麼都做一點」更難，也更說明問題。

所以我會覺得，Clawdbot 值得關注，不只是因為它是一個手藝很好的開源專案，更因為它代表了一種很少見、但越來越重要的方向：

不是把每個人都接到同一個 AI 平台上，
而是讓每個人都擁有屬於自己的 AI 系統。

Clawdbot 這樣的產品出現，就像大航海時代來臨前，先拍上岸邊的第一朵浪花。

浪花本身並不宣告什麼。
但它已經提前告訴你：潮水要來了。

那個每個人都擁有自己專屬 AI 的時代，可能比我們以為的更近。</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category></item><item><title>論文共讀：《Neural Machine Translation by Jointly Learning to Align and Translate》（通過聯合學習對齊與翻譯實現神經機器翻譯）</title><link>https://justinhuangai.github.io/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/</guid><description>注意力機制的起源，附真實 Python 程式碼</description><pubDate>Sun, 11 Jan 2026 08:26:19 GMT</pubDate><content:encoded>2014 年 9 月 1 日，三個人在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文：[《Neural Machine Translation by Jointly Learning to Align and Translate》](/papers/1409.0473v7.pdf)（通過聯合學習對齊與翻譯實現神經機器翻譯）。

這三個人是 Dzmitry Bahdanau、KyungHyun Cho 和 Yoshua Bengio，來自蒙特婁大學。Yoshua Bengio 是深度學習「三巨頭」之一，另外兩位是 Geoffrey Hinton 和 Yann LeCun；三人共同獲得了 2018 年圖靈獎。Bahdanau 當時還是博士生。

這篇論文的核心貢獻可以概括成一件事：讓翻譯模型在生成每個詞的時候，學會回頭看源句子的不同部分。聽起來理所當然，但在當時的神經機器翻譯研究裡，這是一個非常新的想法。它有一個名字，叫「注意力機制」。

三年後，Google 的八個人把這個想法推到了極致，寫出了[《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部）。所以如果你想理解 Transformer，這篇論文是最重要的前史之一。

## 0. 先認幾個詞

如果你完全沒有機器學習背景，先順著這篇論文真正想修補的地方，記住下面幾個詞：

- `編碼器-解碼器 / Encoder-Decoder`：一部分先把源句子讀完，另一部分再把目標句子一個詞一個詞寫出來。
- `RNN / 循環神經網路`：當時主流的序列模型。它必須按順序處理文本，不能一下子同時看整句。
- `hidden state / 隱藏狀態`：模型讀到某個位置時，手上那份臨時筆記。
- `alignment / 對齊`：源句子裡的哪一部分，對應目標句子當前要生成的這個詞。
- `attention`：生成每個詞時，不再只盯著一個總壓縮結果，而是主動回頭看源句子裡更 relevant 的位置。

## 1. 問題出在哪

2014 年的神經機器翻譯有一個標準架構：編碼器-解碼器（Encoder-Decoder）。編碼器是一個循環神經網路（RNN），把源句子從頭到尾讀一遍，把整個句子壓縮成一個固定長度的向量（可以理解為一串固定數量的數字）。解碼器是另一個 RNN，從這個向量出發，一個詞一個詞地生成翻譯。

問題很明顯：不管源句子是 5 個詞還是 50 個詞，編碼器都要把它壓進同一個長度的向量裡。短句子還行，長句子就丟資訊。就像你讓一個人讀完一整頁書，然後只能用一句話複述，句子越長，遺漏越多。

論文用實驗證明了這一點：當句子長度超過 30 個詞，傳統編碼器-解碼器的翻譯品質急劇下降。

這就是「固定長度瓶頸」。

## 2. 核心想法：別壓縮，讓解碼器自己去找

論文的解決方案很直覺：既然把整個句子壓成一個向量會丟資訊，那就不壓了。編碼器保留每個位置的註解向量（annotation，由雙向 RNN 的前向和後向隱藏狀態拼接而成，可以理解為每個詞處理完之後產生的中間結果），解碼器在生成每個目標詞時，自己決定該重點看源句子的哪些部分。

這就是注意力機制的核心：**不再強迫所有資訊擠過一個瓶頸，而是讓模型學會在需要的時候回頭找需要的資訊。**

具體來說，分三步：

**第一步，打分。** 解碼器在生成第 i 個目標詞之前，會用自己當前的狀態 s_{i-1} 和編碼器每個位置的隱藏狀態 h_j 做比較，算出一個「對齊分數」e_{ij}。分數越高，說明生成當前目標詞時，源句子的第 j 個位置越重要。

論文用的打分函數是：

$$
e_{ij} = a(s_{i-1}, h_j) = v_a^T \tanh(W_a s_{i-1} + U_a h_j)
$$

這叫「加性注意力」（additive attention）。把解碼器狀態和編碼器狀態各自做一次線性變換（乘以矩陣），加起來，過一個 tanh（一種把數值壓縮到 -1 到 1 之間的函數），再和一個向量 v_a 做點積，得到一個標量分數。

**第二步，歸一化。** 用 softmax 把所有位置的分數轉成機率，加起來等於 1：

$$
\alpha_{ij} = \operatorname{softmax}(e_{ij}) = \frac{\exp(e_{ij})}{\sum_k \exp(e_{ik})}
$$

**第三步，加權求和。** 用這些機率對編碼器的隱藏狀態做加權求和，得到一個「上下文向量」c_i：

$$
c_i = \sum_j \alpha_{ij} h_j
$$

這個上下文向量就是解碼器在生成第 i 個詞時，從源句子裡提取到的關鍵資訊。每生成一個詞，上下文向量都不一樣，因為模型關注的源句子位置不一樣。

用 Python（基於 PyTorch）寫出來：

```python
import torch
from torch import nn


def bahdanau_attention(
    decoder_state: torch.Tensor,
    encoder_outputs: torch.Tensor,
    w_a: nn.Linear,
    u_a: nn.Linear,
    v_a: nn.Linear,
) -&gt; tuple[torch.Tensor, torch.Tensor]:
    decoder_features = w_a(decoder_state).unsqueeze(1)
    encoder_features = u_a(encoder_outputs)
    scores = v_a(torch.tanh(decoder_features + encoder_features)).squeeze(-1)
    weights = torch.softmax(scores, dim=-1)
    context = torch.sum(weights.unsqueeze(-1) * encoder_outputs, dim=1)
    return context, weights
```

和後來 Transformer 用的「點積注意力」（Q 和 K 直接做點積）不同，這篇論文用的是「加性注意力」（先各自做線性變換，再加起來）。兩種方法各有特點，但點積注意力更適合用高效矩陣乘法實現；再加上 Transformer 去掉了 RNN 的順序依賴，注意力才真正成為可大規模並行的核心算子。

## 3. 編碼器：雙向 RNN

單向 RNN 從左往右讀句子，到了最後一個詞才輸出一個總結向量。問題是：每個位置的隱藏狀態主要只帶著左側上下文，看不到右邊。

論文用了雙向 RNN（BiRNN）來解決這個問題。一個 RNN 從左往右讀，另一個從右往左讀，然後把兩個方向的隱藏狀態拼起來。這樣每個位置的隱藏狀態就同時包含了左邊和右邊的上下文。

```python
import torch
from torch import nn


class BidirectionalRNN(nn.Module):
    def __init__(self, input_size: int, hidden_size: int) -&gt; None:
        super().__init__()
        self.rnn = nn.GRU(
            input_size=input_size,
            hidden_size=hidden_size,
            bidirectional=True,
            batch_first=True,
        )

    def forward(self, inputs: torch.Tensor) -&gt; torch.Tensor:
        outputs, _ = self.rnn(inputs)
        return outputs
```

論文裡每個方向各有 1000 個隱藏單元，拼起來就是 2000 維。這比單向 RNN 多了一倍參數，但換來的是每個位置都能看到完整的上下文。

## 4. 解碼器：每一步都重新對齊

把編碼器和注意力機制組裝起來，解碼器的工作流程就清楚了：

1. 編碼器用雙向 RNN 讀完源句子，保留每個位置的隱藏狀態（註解向量）
2. 解碼器開始生成翻譯，每生成一個詞之前：
   - 用當前狀態和所有註解向量算注意力權重
   - 加權求和得到上下文向量
   - 結合上下文向量、上一個生成的詞和當前狀態，預測下一個詞

```python
import torch
from torch import nn


class AttentionDecoder(nn.Module):
    def __init__(self, embedding_dim: int, hidden_size: int, vocab_size: int) -&gt; None:
        super().__init__()
        self.rnn = nn.GRU(
            input_size=embedding_dim + 2 * hidden_size,
            hidden_size=hidden_size,
            batch_first=True,
        )
        self.w_a = nn.Linear(hidden_size, hidden_size, bias=False)
        self.u_a = nn.Linear(2 * hidden_size, hidden_size, bias=False)
        self.v_a = nn.Linear(hidden_size, 1, bias=False)
        self.output_proj = nn.Linear(hidden_size, vocab_size)

    def decode_step(
        self,
        prev_word: torch.Tensor,
        prev_state: torch.Tensor,
        encoder_outputs: torch.Tensor,
    ) -&gt; tuple[torch.Tensor, torch.Tensor]:
        context, _ = bahdanau_attention(
            prev_state.squeeze(0),
            encoder_outputs,
            self.w_a,
            self.u_a,
            self.v_a,
        )
        rnn_input = torch.cat([prev_word, context.unsqueeze(1)], dim=-1)
        output, new_state = self.rnn(rnn_input, prev_state)
        logits = self.output_proj(output[:, -1, :])
        return logits, new_state
```

關鍵在於：每生成一個目標詞，解碼器都會重新計算注意力分布。翻譯第一個詞時可能重點關注源句子的開頭，翻譯最後一個詞時可能重點關注源句子的結尾。這種動態對齊能力，是之前的固定向量架構做不到的。

## 5. 實驗結果

論文在英法翻譯任務上做了實驗（使用 WMT &apos;14 資料集），用 BLEU 分數（衡量機器翻譯和人工翻譯接近程度的標準評分，滿分 100）衡量效果。

關鍵對比：
- **RNNencdec-50**（傳統編碼器-解碼器，訓練時最長 50 詞）：26.71 BLEU
- **RNNsearch-50**（帶注意力的模型，訓練時最長 50 詞）：**34.16 BLEU**
- **Moses**（當時最強的傳統短語翻譯系統）：33.30 BLEU

提升了 7.45 分。在論文報告的實驗設置裡，帶注意力的神經模型已經達到甚至超過了當時強勢的傳統短語翻譯系統。

更關鍵的發現在論文的 Figure 2：隨著句子長度增加，傳統編碼器-解碼器的 BLEU 分數急速下跌，而帶注意力的模型幾乎不受影響。這直接驗證了論文的核心假設：固定長度向量是瓶頸，注意力機制可以繞過它。

論文還展示了注意力權重的視覺化。在英法翻譯裡，注意力權重幾乎形成了一條對角線，說明模型自動學會了「英語第 1 個詞對應法語第 1 個詞，英語第 2 個詞對應法語第 2 個詞」的對齊關係。遇到語序不同的情況（比如法語的形容詞放在名詞後面），注意力權重也會對應地偏移。模型不需要任何人工對齊標註，就學會了這些。

## 6. 我的思考

讀完這篇論文，有幾個感受。

第一，這篇論文解決的問題極其明確：編碼器把整個句子壓成一個向量，長句子丟資訊。解決方案也極其直覺：別壓了，讓解碼器自己去找。好的研究往往就是這樣，問題清晰，解法自然。

第二，注意力機制在這篇論文裡還是 RNN 的配角。編碼器仍然是循環的（雙向 RNN），解碼器也是循環的，注意力只是在兩者之間架了一座橋。三年後 Vaswani 等人問了一個更激進的問題：既然注意力這麼好用，能不能把 RNN 整個扔掉，只留注意力？答案就是 Transformer。

第三，用真實 Python 重寫這篇論文的注意力機制時，你會發現它的計算流程比 Transformer 的 Scaled Dot-Product Attention 複雜不少。加性注意力需要額外的權重矩陣 W_a、U_a、v_a，而點積注意力只需要 Q 和 K 直接相乘再縮放。從「加法」到「乘法」，看似一小步，實際上大幅簡化了計算，也更適合用矩陣運算高效實現。

第四，Bahdanau 當時是博士生，Bengio 是他的導師。一個博士生的論文，定義了此後十年 AI 研究的核心元件。注意力機制從這裡開始，經過 Transformer 的放大，最終成為 GPT、BERT、LLaMA 的基石。

這篇論文沒有發明什麼複雜的數學。它只是問了一個簡單的問題：解碼器為什麼不能回頭看？

然後它讓解碼器回頭看了。

這一看，看出了整個時代。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Attention Is All You Need》](/zh-hant/posts/attention-is-all-you-need/)（注意力就是你所需要的全部） — 注意力成為主角，Transformer 的誕生
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學：為什麼更大的模型可預測地更好
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>attention</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>論文共讀：《Attention Is All You Need》（注意力就是你所需要的全部）</title><link>https://justinhuangai.github.io/zh-hant/posts/attention-is-all-you-need/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/attention-is-all-you-need/</guid><description>分享我對 Transformer 論文的理解，附真實 Python 程式碼</description><pubDate>Tue, 06 Jan 2026 08:18:46 GMT</pubDate><content:encoded>2017 年 6 月 12 日，八個人在 arXiv（一個學術論文預印本網站，論文不用等期刊審稿就能直接發布）上傳了一篇論文，標題只有五個詞：[《Attention Is All You Need》](/papers/1706.03762v7.pdf)（注意力就是你所需要的全部）。

這八個人是 Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan N. Gomez、Łukasz Kaiser 和 Illia Polosukhin，當時大多在 Google Brain 和 Google Research 工作。

論文發出之後，這個八人組幾乎全部散開。Noam Shazeer 離開 Google 創立了 Character.AI，後來又被 Google 高價請回；Aidan Gomez 從多倫多大學博士還沒畢業就創立了 Cohere，做企業級大模型；Llion Jones 去了日本，創立了 Sakana AI；Illia Polosukhin 走了一條誰都沒想到的路，創立了 NEAR Protocol，做區塊鏈；Ashish Vaswani 和 Niki Parmar 搭檔創立了 Adept AI，後來又一起創立了 Essential AI；Jakob Uszkoreit 創立了 Inceptive，用 AI 設計 RNA 藥物；Łukasz Kaiser 則加入了 OpenAI，參與了 GPT 系列的研發。

八位作者，七家公司，橫跨 AI、區塊鏈、生物技術。

近九年後的今天，ChatGPT、Claude、DeepSeek、Qwen，這些 AI 產品的底層架構思路，大多都能追溯到這 15 頁紙。

這篇文章是我讀完論文後的理解，附帶真實 Python 程式碼示例。不是翻譯，不是摘要。沒有技術背景也能讀下去。

## 0. 先認幾個詞

如果你沒有機器學習背景，先順著這篇論文真正想取代的舊方案，記住下面幾個詞就夠了：

- `RNN / 循環神經網路`：一種更早的序列模型。它處理句子時必須一個詞一個詞往後讀，像人用手指著文章逐行看。
- `attention`：從很多資訊裡，挑出當前最該看的那幾部分。你可以先把它理解成「有選擇地回頭看重點」。
- `Query / Key / Value`：注意力機制裡的三個角色。Query 像「我現在想找什麼」，Key 像「每段資訊貼著什麼標籤」，Value 則是「真正被取回來的內容」。
- `Transformer`：以 attention 為核心搭起來的一整套架構。它不靠循環一步步往前推，而是讓每個位置都能直接看其他位置。
- `並行`：這裡不是說模型更聰明，而是說它能同時處理很多位置，不必像 RNN 那樣排隊。

## 1. 一句話說清楚

在 Transformer 之前，AI 處理語言的方式像是一個人用手指著書，一個字一個字地往下讀。讀到第 100 個字的時候，第 1 個字說了什麼，已經記不太清了。句子越長，遺忘越嚴重。這就是循環神經網路（RNN，一種早期的 AI 架構）的根本瓶頸。

論文的作者們問了一個問題：**為什麼一定要按順序讀？**

和必須逐步處理的 RNN 不同，Transformer 可以並行處理整段輸入，直接建模任意兩個位置之間的關係。不用排隊，不用等前一個詞處理完才能看下一個。

論文管這個核心能力叫「注意力」。標題要表達的不是「模型裡真的只剩注意力」，而是：在序列建模裡，注意力第一次被推到了主角的位置，不再需要循環和卷積（一種通過滑動窗口提取局部特徵的方法）作為骨架。

## 2. 注意力到底在做什麼

想像你走進一個嘈雜的酒吧，二十個人同時在說話。你的大腦不會平均分配注意力給每個人。有人喊了你的名字，你的耳朵瞬間鎖定那個方向，其他聲音自動變成背景噪音。

Transformer 對每個詞做同樣的事。論文裡定義了三個角色：

- **Query（查詢）**：這個詞在找什麼資訊。相當於你的耳朵在搜尋「誰在叫我」
- **Key（鍵）**：這個詞能提供什麼資訊。相當於酒吧裡每個人的聲音特徵
- **Value（值）**：這個詞實際攜帶的內容。相當於那個人說的具體話

每個詞的 Query 會和其他詞的 Key 做匹配。匹配度高的，就從對方的 Value 裡獲取更多資訊。匹配度低的，直接忽略。

論文給出的公式叫 Scaled Dot-Product Attention：

$$
\operatorname{Attention}(Q, K, V) = \operatorname{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$

看到公式別慌。一步一步拆：

- **QK^T**：Q 和 K 做點積。什麼是點積？把兩組數字對應位置相乘，再加起來。比如 [1, 2] 和 [3, 4] 的點積是 1×3 + 2×4 = 11。數字越大，說明兩個詞越相關。這一步算的就是每對詞之間的「匹配分數」
- **/ √d_k**：除以一個數來縮放。d_k 是向量的長度（向量可以理解為「一串用來描述某個東西的數字」，比如用 64 個數字描述一個詞的含義）。為什麼要除？因為數字串越長，點積結果越大。不縮放時，維度越大點積的方差越大，softmax 容易進入飽和區（幾乎所有機率集中在一個詞上），梯度（模型用來調整自身參數的訊號）變得很小，訓練會不穩定
- **softmax**：把一組分數轉換成機率，所有機率加起來等於 1。比如三個詞的分數是 [10, 2, 1]，softmax 之後大概變成 [0.99, 0.007, 0.003]。分數最高的那個詞幾乎拿走了全部注意力，其他的被壓到接近零
- **× V**：用這些機率去加權每個詞的實際內容。機率高的詞貢獻大，機率低的詞貢獻小。最終輸出是一個融合了關鍵資訊的新向量

用 Python（基於 PyTorch）寫出來：

```python
import math
import torch


def scaled_dot_product_attention(
    query: torch.Tensor,
    key: torch.Tensor,
    value: torch.Tensor,
) -&gt; torch.Tensor:
    d_k = key.size(-1)
    scores = query @ key.transpose(-2, -1) / math.sqrt(d_k)
    weights = torch.softmax(scores, dim=-1)
    return weights @ value
```

就這麼幾行程式碼。很多後來改變行業的能力，底層都建立在這幾行運算之上。

## 3. 多頭注意力：同時從多個角度看

單個注意力頭通常只能偏向某一類關係模式。但語言這東西，一句話裡藏著好幾層意思。

拿「我昨天在深圳吃了潮汕牛肉火鍋」來說：
- 「我」和「吃了」之間是誰做了什麼的關係
- 「昨天」和「吃了」之間是時間關係
- 「深圳」和「潮汕牛肉火鍋」之間是地點與食物的關係

讓一個頭同時兼顧這麼多層次，很難。論文的做法是用多頭機制：派出 8 個頭並行運算，讓模型有機會從不同子空間同時觀察一句話，最後把各自的發現拼起來。

論文原文的公式：

$$
\operatorname{MultiHead}(Q, K, V) = \operatorname{Concat}(\text{head}_1, \ldots, \text{head}_h)\, W^O
$$

拆開看：
- **head_1, ..., head_h**：8 個頭各自獨立做一次注意力運算，得到 8 份結果
- **Concat**：把 8 份結果首尾相連，拼成一個長向量
- **W^O**：一次線性變換（可以理解為「乘以一個矩陣」），把拼接後的長向量壓回原來的維度。相當於一個主管聽完 8 個調查員的匯報，輸出一份綜合結論

```python
import math
import torch
from torch import nn


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, num_heads: int) -&gt; None:
        super().__init__()
        if d_model % num_heads != 0:
            raise ValueError(&quot;d_model must be divisible by num_heads&quot;)
        self.num_heads = num_heads
        self.d_head = d_model // num_heads
        self.q_proj = nn.Linear(d_model, d_model)
        self.k_proj = nn.Linear(d_model, d_model)
        self.v_proj = nn.Linear(d_model, d_model)
        self.out_proj = nn.Linear(d_model, d_model)

    def _split_heads(self, x: torch.Tensor) -&gt; torch.Tensor:
        batch_size, seq_len, _ = x.shape
        x = x.view(batch_size, seq_len, self.num_heads, self.d_head)
        return x.transpose(1, 2)

    def forward(
        self,
        query: torch.Tensor,
        key: torch.Tensor,
        value: torch.Tensor,
    ) -&gt; torch.Tensor:
        q = self._split_heads(self.q_proj(query))
        k = self._split_heads(self.k_proj(key))
        v = self._split_heads(self.v_proj(value))

        scores = q @ k.transpose(-2, -1) / math.sqrt(self.d_head)
        weights = torch.softmax(scores, dim=-1)
        heads = weights @ v

        batch_size, _, target_len, _ = heads.shape
        merged = heads.transpose(1, 2).contiguous()
        merged = merged.view(batch_size, target_len, self.num_heads * self.d_head)
        return self.out_proj(merged)
```

論文裡的參數：模型用 512 個數字描述一個詞（d_model = 512），8 個頭，每個頭分到 64 個數字（512 ÷ 8 = 64）。8 個頭的總計算量和 1 個 512 維的頭差不多，但表達能力強得多。用同樣的代價，換來多視角的理解力。這筆帳算得漂亮。

## 4. 位置編碼：告訴模型詞的順序

Transformer 並行處理整個句子，速度是快了，但代價是它丟掉了詞的先後順序。如果沒有額外的位置資訊，注意力機制本身並不知道「貓吃魚」和「魚吃貓」有什麼區別。這顯然不行。

怎麼補救？給每個位置生成一個獨一無二的「地址編碼」，加到詞的向量上。模型看到的不再是「貓」和「魚」，而是「第 1 個位置的貓」和「第 3 個位置的魚」。

論文用正弦和餘弦函數來生成這個編碼：

$$
\operatorname{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

$$
\operatorname{PE}(pos, 2i + 1) = \cos\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

公式看著唬人，核心思路很直觀：
- **pos**：詞在句子裡的位置（第 1 個、第 2 個、第 3 個……）
- **i**：向量的第幾個維度。偶數位置用 sin，奇數位置用 cos
- **10000^(2i/d_model)**：一個隨維度變化的縮放因子。低維度變化快，高維度變化慢。就像時鐘：秒針一分鐘轉一圈，時針十二小時才轉一圈。不同「指針」覆蓋不同的時間尺度，組合在一起就能精確定位任意時刻

最終效果：每個位置得到一串獨一無二的數字指紋，模型靠這個指紋區分詞的先後順序。

```python
import math
import torch


def positional_encoding(seq_len: int, d_model: int) -&gt; torch.Tensor:
    positions = torch.arange(seq_len, dtype=torch.float32).unsqueeze(1)
    div_term = torch.exp(
        torch.arange(0, d_model, 2, dtype=torch.float32)
        * (-math.log(10000.0) / d_model)
    )

    encoding = torch.zeros(seq_len, d_model)
    encoding[:, 0::2] = torch.sin(positions * div_term)
    encoding[:, 1::2] = torch.cos(positions * div_term)
    return encoding
```

為什麼偏偏選正弦餘弦？因為它有一個優雅的數學性質：兩個詞相隔固定距離，無論它們出現在句首還是句尾，位置編碼之間的關係是一樣的。模型不用死記「位置 3 和位置 8」的關係，只需要學會「相隔 5 個位置」意味著什麼。論文團隊也試過讓模型自己學位置編碼，效果差不多，但正弦版本有一個額外的優勢：它能處理訓練時沒見過的更長句子。

## 5. 編碼器與解碼器

Transformer 的完整架構分兩半。

**編碼器**（6 層堆疊）負責讀懂輸入。每層包含兩個子層：一個多頭自注意力，一個前饋網路。每個子層都有兩個保護機制：

- **殘差連接**：把子層的輸入直接加到輸出上，即 x + Sublayer(x)。為什麼？想像你給一張照片加濾鏡。如果濾鏡效果不好，殘差連接保證你還能看到原圖。在深層網路裡，資訊每經過一層都會被變換，傳到第六層可能已經面目全非。殘差連接讓原始訊號可以「抄近道」直達深層，防止資訊在傳遞中丟失
- **層歸一化**（LayerNorm）：把數值調整到統一範圍，防止有的數字大到爆炸、有的小到消失。類似於考試成績標準化，不管原始卷面分差異多大，標準化後都在一個可比較的區間

**解碼器**（6 層堆疊）負責生成輸出。結構和編碼器類似，但多了兩個關鍵設計：

第一，**交叉注意力**：解碼器生成每個詞時，會回頭「看」編碼器的輸出。翻譯場景下，就是一邊寫英文一邊回頭看中文原文。

第二，**遮罩**（masking）：生成第 3 個詞時，只允許看到前 2 個詞，第 4 個及之後的位置被遮蔽（注意力分數設為負無窮，經過 softmax 後變成零）。道理很簡單：你寫作文的時候，下一個字還沒寫出來，不能偷看。

```python
from typing import Optional

import torch
from torch import nn


class Transformer(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        d_model: int = 512,
        num_heads: int = 8,
        num_layers: int = 6,
        d_ff: int = 2048,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, d_model)

        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=num_heads,
            dim_feedforward=d_ff,
            dropout=dropout,
            batch_first=True,
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=d_model,
            nhead=num_heads,
            dim_feedforward=d_ff,
            dropout=dropout,
            batch_first=True,
        )

        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
        self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers)
        self.output_proj = nn.Linear(d_model, vocab_size)

    def forward(
        self,
        src_token_ids: torch.Tensor,
        tgt_token_ids: torch.Tensor,
        tgt_mask: Optional[torch.Tensor] = None,
    ) -&gt; torch.Tensor:
        memory = self.encoder(self.embedding(src_token_ids))
        hidden = self.decoder(self.embedding(tgt_token_ids), memory, tgt_mask=tgt_mask)
        return self.output_proj(hidden)
```

還有一個容易被忽略的元件：前饋網路。公式是 FFN(x) = max(0, xW1 + b1)W2 + b2。翻譯成白話：先把每個詞的 512 維向量擴大到 2048 維（乘以一個矩陣再加一個偏置），用 ReLU 過濾一遍（所有負數變成零，正數保留），再壓回 512 維。ReLU 這一步是關鍵：它引入了「非線性」，讓模型能學到直線畫不出來的複雜模式。如果全是線性變換，多層疊加在數學上仍可合併為單層，非線性是模型表示複雜模式的前提。

## 6. 訓練細節

架構設計完了，怎麼訓練它？論文在這裡也有不少講究。

**硬體**：8 塊 NVIDIA P100 GPU。基礎版模型訓練 12 小時（10 萬步），大號模型訓練 3.5 天（30 萬步）。放在今天看，這個成本低得驚人。

**最佳化器**：用的是 Adam（一種讓模型自動調整參數的演算法），但學習率的設計很巧妙。學習率決定了模型每一步「邁多大步子」。步子太大容易跨過最佳解，太小又走得慢。論文的策略是前 4000 步逐漸提速（warmup，熱身），避免一開始更新過猛；4000 步之後按計畫逐漸減速，讓訓練後期更穩定。先升後降，前半段大膽探索，後半段精細打磨。

**正則化**：兩招。第一招是 Dropout，訓練時隨機關掉 10% 的神經元（可以理解為網路中的計算節點），迫使模型不依賴任何單一路徑，學到更穩健的特徵。第二招是 label smoothing（標籤平滑，ε = 0.1）：訓練時不告訴模型「正確答案的機率是 100%」，而是說「90% 是正確答案，剩下 10% 分給其他選項」。這會讓模型在一個指標上變差（困惑度，衡量模型有多「拿不準」），但翻譯品質反而更好。直覺上說，一個承認自己不是 100% 確定的模型，比一個過度自信的模型更可靠。

**結果**：論文用 BLEU 分數（機器翻譯的標準評分，衡量機器翻譯和人工翻譯有多接近，滿分 100）來衡量效果。英德翻譯 28.4 分，英法翻譯 41.8 分，都刷新了當時的紀錄。訓練成本比之前的方法低了一到兩個數量級。更快，更強，更便宜。

## 7. 我的思考

讀完這篇論文，有幾個感受。

第一，這篇論文的核心洞察極其簡潔：扔掉順序處理的包袱，讓注意力機制直接建模任意兩個位置之間的關係。Self-Attention、殘差連接、Layer Normalization，沒有一個是新發明。真正的突破不在於發明新工具，而在於作者們敢賭「這些簡單的積木拼在一起就夠了」，然後用實驗證明了自己是對的。

第二，用真實 Python 程式碼寫出來的過程讓我更深地理解了每一個設計決策。當你自己寫出 Scaled Dot-Product Attention，你會切實感受到那個 √d_k 的縮放有多重要。當你實現 masking，你會理解自回歸生成的約束從何而來。論文讀十遍，不如自己寫一遍。

第三，真正讓我震撼的，不是它後來衍生出了多少模型，而是它當年就把問題改寫了：從「怎麼按順序記住一句話」，變成「怎麼讓每個位置直接找到它最該看的資訊」。GPT、BERT、T5、LLaMA，全是這個問題改寫之後的產物。

一個足夠好的架構，能走多遠，取決於有多少人願意在它上面繼續建設。

這篇論文給出了那個架構。

《Attention Is All You Need》（注意力就是你所需要的全部）。

---

**論文共讀系列**

- [《Sequence to Sequence Learning with Neural Networks》](/zh-hant/posts/sequence-to-sequence-learning-with-neural-networks/)（使用神經網路進行序列到序列學習） — 編碼器-解碼器範式的確立
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/zh-hant/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)（通過聯合學習對齊與翻譯實現神經機器翻譯） — 注意力機制的起源
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/zh-hant/posts/bert/)（BERT：用於語言理解的深度雙向 Transformer 預訓練） — 預訓練範式的確立
- [《Scaling Laws for Neural Language Models》](/zh-hant/posts/scaling-laws-for-neural-language-models/)（神經語言模型的縮放定律） — 規模的數學：為什麼更大的模型可預測地更好
- [《Language Models are Few-Shot Learners》](/zh-hant/posts/language-models-are-few-shot-learners/)（語言模型是少樣本學習者） — 更大的模型，更善於從上下文中誘發能力
- [《Training Compute-Optimal Large Language Models》](/zh-hant/posts/training-compute-optimal-large-language-models/)（訓練算力最優的大型語言模型） — 如何最有效地分配算力</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>transformer</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>👋 你好，世界</title><link>https://justinhuangai.github.io/zh-hant/posts/hello-world/</link><guid isPermaLink="true">https://justinhuangai.github.io/zh-hant/posts/hello-world/</guid><description>歡迎來到 Astro-Theme-Aither——一個相信文字本身就很美的 AI 原生 Astro 主題。</description><pubDate>Thu, 01 Jan 2026 08:07:13 GMT</pubDate><content:encoded>歡迎來到 Astro-Theme-Aither。

這是一個基於一個信念構建的 AI 原生部落格主題：文字本身就很美。統一的無襯線系統字型棧、Apple HIG 排版參數，以及不喧賓奪主的版面。這裡的一切都服務於一個目標——讓你的文字看起來優美、讀起來舒適。

## 為什麼再造一個部落格主題

網路上有無數部落格主題，那為什麼還要再做一個？答案在於優先順序。大多數主題為視覺衝擊力而最佳化——大圖、複雜版面、華麗動畫。這些在展示中很好看，但當讀者真正坐下來閱讀一篇兩千字的文章時，它們只會礙事。

Astro-Theme-Aither 從不同的前提出發：內容就是產品。主題的職責是以它應得的認真態度呈現內容：Apple HIG 正文參數（17px / 1.47 / -0.022em）、充足的留白、讓長文閱讀變得舒適而不是疲憊的垂直節奏。

技術決策也延續了這一理念。主題使用 Astro 的島嶼架構——只有互動元件（主題切換、語言切換、語言偵測、行動端導覽）載入 JavaScript。其他一切都是靜態 HTML 和 CSS。沒有版面偏移，沒有載入動畫。頁面載入完畢，你開始閱讀。

## 開始使用

搭建只需幾步：

1. **複製倉庫** — 使用 GitHub 範本按鈕或直接 `git clone`
2. **安裝依賴** — 執行 `pnpm install`
3. **設定網站** — 編輯 `src/config/site.ts` 設定標題、描述和導覽
4. **設定服務** — 複製 `.env.example` 為 `.env`，填入 API 金鑰（GA、Crisp、Giscus）
5. **替換內容** — 用你自己的文章替換 `src/content/posts/` 中的範例
6. **本地開發** — 執行 `pnpm dev` 啟動熱更新開發伺服器
7. **部署** — 推送到 `gh-pages`，內建 GitHub Pages 工作流會自動發佈網站

### 專案結構

```
src/
├── components/     # Astro 和 React 元件
├── config/         # 網站設定（site.ts）
├── content/        # Markdown 文章（按語言組織）
├── i18n/           # 翻譯和語言工具
├── layouts/        # 頁面版面（Layout.astro）
├── lib/            # 共用工具（posts, formatter, markdown-endpoint）
├── pages/          # 路由頁面（按語言）
└── styles/         # 全域 CSS + Tailwind v4 @theme 令牌
```

### 寫第一篇文章

在 `src/content/posts/zh-hant/` 下建立 `.md` 檔案：

```markdown
---
title: 我的第一篇文章
date: &quot;2026-01-15T16:27:43+08:00&quot;
category: General
description: SEO 和社群預覽的簡短摘要
tags: [topic, another]
pinned: false
---

正文從這裡開始。
```

`title`、`date`、`category` 是必填項。`date` 建議使用帶時區、精確到秒的 ISO 8601 格式，例如 `2026-01-15T16:27:43+08:00`。`description` 強烈建議填寫。設定 `pinned: false` 可將文章置頂。

多語系內容只需在對應語言目錄（`en/`、`zh-hans/`、`ko/`）建立同名檔案。

## 開箱即用

### 內容功能

- **RSS 訂閱** — 自動產生 `/rss.xml`
- **網站地圖** — 透過 `@astrojs/sitemap` 自動產生
- **SEO 標籤** — 每頁自動產生 Open Graph、Twitter Cards、規範 URL
- **JSON-LD** — Article 結構化資料，服務 AI 和搜尋引擎
- **深色模式** — 淺色 / 深色 / 系統切換，View Transitions API 圓形展開動畫
- **i18n** — 多語言支援，自動瀏覽器語言偵測
- **文章置頂** — 將重要文章固定在列表頂部
- **分頁** — 基於檔案的 SSG 分頁，帶頁碼導覽

### AI 原生功能

- **llms.txt** — AI agent 內容索引，`/llms.txt`
- **llms-full.txt** — AI 全文消費，`/llms-full.txt`
- **Markdown 端點** — 任何文章 URL 後加 `.md` 取得純 Markdown
- **robots.txt** — 明確歡迎 AI 爬蟲（GPTBot、ClaudeBot、PerplexityBot）

### 開發者功能

- **TypeScript** — 嚴格模式，全型別化
- **Content Collections** — 建構時 frontmatter 型別驗證
- **Tailwind CSS v4** — `@theme` 設計令牌
- **驗證鏈路** — 透過 `pnpm validate` 執行內容覆蓋檢查與 agent 協議 smoke test
- **部署** — 內建 GitHub Pages 工作流
- **Google Analytics** — 可選，環境變數設定
- **Crisp Chat** — 可選線上客服，環境變數設定
- **Giscus 評論** — 可選 GitHub Discussions 評論

### 效能

靜態 HTML + 最小 JavaScript 島嶼 = Lighthouse 四項滿分。

## 自訂

- **顏色** — 編輯 `src/styles/global.css` 中的 CSS 變數
- **字型** — 修改 Tailwind 主題設定中的字型族
- **導覽** — 更新 `src/config/site.ts` 中的導覽陣列
- **服務** — 在 `.env` 中設定 GA、Crisp、Giscus 環境變數
- **語言** — 在 `src/i18n/` 新增語言，建立對應路由

## 設計理念

主題的視覺簡潔是刻意的，但這不等於工程簡單。底層處理了大量關注點：Apple HIG 排版參數、明暗兩種模式的無障礙色彩對比、View Transitions API 動畫、自動瀏覽器語言偵測、語意化 HTML 結構、AI 友好的內容端點，以及從手機到超寬螢幕的閱讀體驗最佳化。

好的設計是隱形的。當你在這個主題上閱讀一篇文章，只是單純享受文字而完全沒注意到主題的存在——這就是設計在按預期工作。

祝寫作愉快。</content:encoded><category>Tutorial</category><category>hello</category><category>astro</category></item></channel></rss>