English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

guest
1 / ?
back to lessons

注意力加 MLP,重複

Transformer 區塊 Pre-Norm 結構


兩個子層

一個 transformer 區塊包含正好兩個子層,每個子層操作形狀為 [batch, seq_len, d_model] 的 token 序列:


1. 多頭注意力子層。 標記彼此查看。活動 68 詳細涵蓋了這一點。輸出形狀與輸入形狀匹配。

2. 前饋 MLP 子層。 每個標記獨立通過兩層感知器轉換。無跨標記資訊流。輸出形狀與輸入形狀匹配。


兩個子層都保留 [batch, seq_len, d_model] 形狀。這種保留讓區塊能夠堆疊:層 N 的輸出無需形狀變換即可饋送到層 N+1 的輸入。


每個子層的貢獻

注意力將資訊跨位置移動:位置 17 的標記可以從位置 1 到 16 拉取資訊。MLP 在每個位置內轉換資訊:標記的表示通過學習的非線性函數重塑,但永遠不會看到其鄰居。


堆疊區塊會交替進行這兩個操作。Layer 1 attention 混合位置。Layer 1 MLP 重新塑形每個位置。Layer 2 attention 再次混合,現在是針對重新塑形的表示。這種交替隨著深度增加而提升表達能力。


ANDREA 的堆疊


Variantn_layern_headd_modelmlp_dim
ANDREA-12M6123841536
ANDREA-120M12127683072
ANDREA-480M162415366144

注意整個系列中 mlp_dim = 4 × d_model。這個比例在幾乎所有現代 transformer 中都成立。第 3 節將詳細解釋原因。

命名子層

Transformer 區塊包含兩個子層。依序命名它們,並針對每個子層說明它是跨位置移動資訊(token-to-token)還是單獨位置內轉換資訊(每個 token 獨立)。每個子層一句話。

為什麼跳躍連接很重要

殘差模式

每個子層將其計算包裝在殘差連接中。輸出會加回輸入:


x = x + Attention(LayerNorm(x))     # attention sublayer
x = x + MLP(LayerNorm(x))           # MLP 子層

在每個子層內,函數 Attention(...)MLP(...) 產生一個 delta。區塊不會取代輸入;它會加入一個學習到的修正。


為什麼這很重要

殘差連接主宰深度架構的三個原因:


1. 梯度流。 在反向傳播期間,殘差連接為梯度從輸出回傳到輸入提供了直接路徑,繞過子層。一個沒有殘差的 12 層堆疊會在到達嵌入層之前很久就丟失梯度訊號;有了殘差,梯度幅度在數百層中保持可用。


2. 身份初始化。 在初始化時,子層權重產生小的輸出。殘差連接意味著第 N 層最初幾乎不變地通過。訓練從一個有效的起點逐步學習增量。


3. 組合學習。 每個區塊學習一個細化,而不是替換。層 1 可能添加位置資訊。層 2 可能添加句法結構。層 7 可能添加語義關係。殘差流累積。


層歸一化

在每個子層之前,LayerNorm 將每個 token 的表示重新縮放到零均值與單位變異數,然後應用學習到的每個特徵增益與偏差:


y = gamma * (x - mean(x)) / sqrt(var(x) + epsilon) + beta

平均值與變異數是針對 d_model 維度計算的,對每個 token 分別獨立計算。兩個學習向量(gammabeta,每個形狀為 [d_model])恢復表現力尺度。正規化將激活值保持在數值穩定的範圍內;若無正規化,小型訓練不穩定性會像雪球般滾大,導致 NaN 梯度。

Pre-Norm 與 Post-Norm

一個細微但關鍵的選擇

將層歸一化(layer norm)連接到殘差子層(residual sublayer)的兩種方式:


後歸一化(post-norm,原 2017 年論文):

x = LayerNorm(x + Attention(x))

層歸一化位於殘差加法之後。殘差流本身在每一層都會被歸一化。


Pre-norm(現代標準,用於 ANDREA):

x = x + Attention(LayerNorm(x))

層歸一化位於子層之前,在殘差分支內部。殘差流保持未歸一化;只有子層的輸入會被重新縮放。


為什麼 Pre-Norm 勝出

Post-norm 在沒有 LR 預熱及小心超參數調整的情況下訓練效果差。梯度在早期層中爆炸,因為每個層歸一化會打亂殘差流的累積狀態。原始 2017 年論文使用 post-norm 並進行廣泛調整;後續工作(GPT-2、LLaMA、ANDREA)標準化採用 pre-norm。


Pre-norm 訓練穩定。殘差流在所有層中乾淨累積;僅子層輸入進行正規化以確保數值穩定性。現代 transformer 預設使用 pre-norm,& ANDREA 繼承了這一選擇。


最終區塊方程式

結合殘差、pre-norm 位置的層正規化,& 兩個子層,即 ANDREA 的完整區塊:


def block_forward(x):
x = x + Attention(LayerNorm(x))   # attention 子層
x = x + MLP(LayerNorm(x))         # MLP 子層
return x

兩個子層,兩個殘差加法,兩個層歸一化(注意:每個子層有自己的層歸一化;ANDREA-120M 在 12 個區塊中有 24 個層歸一化,加上輸出處的最後一個,總共 25 個)。重複 12 次。這就是 ANDREA-120M 的主幹。

為什麼 Pre-Norm 能穩定訓練

ANDREA 使用 pre-norm:`x = x + Attention(LayerNorm(x))`。與 post-norm:`x = LayerNorm(x + Attention(x))` 比較。從梯度流動的角度給出一個原因,解釋為什麼在深層堆疊中 pre-norm 比 post-norm 訓練更穩定。在回答中參考殘差流。

兩個線性層,一個激活函數

三個操作

MLP 子層是一個兩層感知器,層與層之間有一個非線性激活函數:


def mlp_forward(x):
h = x · W_1 + b_1        # 擴展:d_model → mlp_dim
h = GELU(h)              # 非線性激活
y = h · W_2 + b_2        # 收縮:mlp_dim → d_model
return y

三個操作。兩個線性,一個非線性。第一個線性擴展寬度;第二個收縮回來。


4× 擴展比率

現代 transformer 設定 mlp_dim = 4 × d_model。ANDREA-120M:


d_model = 768
mlp_dim = 4 × 768 = 3072
W_1 形狀 = [768, 3072]      # ~2.36M 參數
W_2 形狀 = [3072, 768]      # ~2.36M 參數
每個區塊的 MLP 參數 = 4.72M(忽略偏差)

兩個 MLP 位於每對注意力子層之間(每個區塊一個)。十二個區塊 × 4.72M ≈ 56.6M MLP 參數總計在 ANDREA-120M 中,約佔所有參數的一半。


為什麼是 4×

4× 比率是透過經驗得出的。較小的比率會降低模型容量。較大的比率在每參數花費上產生邊際效益遞減。在數十年的架構搜尋中,4× 一直屹立不搖;它出現在 GPT、BERT、T5、LLaMA 及 ANDREA 中。


近期研究 (PaLM、Chinchilla) 發現,閘控機制 (SwiGLU) 可以使用 8/3× 擴展,以較低的成本達到相當的容量;ANDREA 為了簡單,堅持經典的 GELU + 4×。

GELU:平滑激活函數

GELU 計算什麼

GELU (Gaussian Error Linear Unit) 是現代 Transformer 中 MLP 層之間的標準激活函數。其公式:


GELU(x) = x · Φ(x)

Φ(x) 是標準常態分佈的累積分佈函數:標準高斯隨機變數落在 x 或以下的機率。以數值方式計算:


Φ(x) ≈ 0.5 × (1 + tanh(sqrt(2/π) × (x + 0.044715 × x³)))

按區域的行為

- 對於大的正 x:Φ(x) ≈ 1,因此 GELU(x) ≈ x。類似 ReLU。

- 對於大的負 x:Φ(x) ≈ 0,因此 GELU(x) ≈ 0。類似 ReLU。

- 接近 x = 0:Φ(x) ≈ 0.5,因此 GELU(0) = 0 完全準確。通過原點的平滑過渡。


與 ReLU 不同,GELU 允許某些負輸入通過,由 Φ(x) 加權。一個小的負輸入仍貢獻一個小的負輸出,只是比完整輸入少。


為什麼 GELU 優於 ReLU

經驗上,使用 GELU 訓練的 transformer 在相同參數量下,能達到比使用 ReLU 訓練的 transformer 更低的損失。零點附近的平滑性很重要:ReLU 在零點的硬截斷會產生梯度不連續;GELU 的平滑曲線為反向傳播提供更乾淨的梯度。


ANDREA 的訓練引擎 microgpt_cuda.cu 內建手寫的 GELU CUDA 核心。該核心使用上述 tanh 近似;現代 GPU 將 tanh 作為單指令運算提供。

計算 MLP 參數

ANDREA-120M 具有 `d_model=768`、`mlp_dim=3072` 和 `n_layer=12`。計算所有 12 個區塊中 MLP 權重矩陣(`W_1` 和 `W_2`)的總參數數。忽略偏差。顯示你的計算過程。然後說明這佔 ANDREA-120M 約 120M 總參數的比例(四捨五入至小數點一位)。

十二個區塊組成 ANDREA-120M

從區塊到模型

ANDREA-120M 的完整前向傳遞:


def model_forward(token_ids):
x = token_embed(token_ids) + position_embed(positions)
for block_idx in range(n_layer):       # 12 個區塊
x = block_forward(x)               # 注意力機制 + MLP 搭配殘差連接
x = LayerNorm(x)                       # 最終正規化
logits = x · token_embed.T             # 輸出投影使用綁定權重
return logits

六行。主要部分位於 block_forward 內,被呼叫十二次。嵌入開始管道;綁定反嵌入(用於輸入查詢的相同矩陣,轉置用於輸出投影)結束它。


深度作為組合

每個區塊讀取殘差流,計算一個 delta,並加回它。經過十二次通過後,流包含每個區塊的累積貢獻。內部,層傾向於專門化:


- 早期層 (1-3):語法模式,位置結構

- 中間層 (4-8):詞語關係,片語邊界

- 晚期層 (9-12):語義內容,事實回憶


這種專門化來自訓練壓力,而非架構選擇。相同的均勻區塊設計在語言訓練時會產生專門化的層。


總區塊參數


組件每個區塊橫跨 12 個區塊
注意力投影 (4×W)2.36M28.3M
MLP 權重 (W_1 + W_2)4.72M56.6M
層歸一化 (gamma, beta)~3K (可忽略)~37K
每個區塊總計~7.1M~85M

主幹中有 85M 參數。加上 ~13M 的 token 嵌入 (8449 詞彙 × 768 d_model × 2 用於綁定輸入/輸出) 以及最終層歸一化,ANDREA-120M 的參數總數約為 120M。區塊設計佔三分之二;嵌入佔其餘部分。

追蹤一個 Token 通過一個區塊

一個 768 維的 token 向量進入 ANDREA-120M 的區塊 7。在 pre-norm 結構中,逐步說明它在區塊內發生的變化。提及:兩個層歸一化、兩個子層、兩個殘差加法,以及最終形狀。至少說明一個殘差流未被觸碰的地方,以及一個被修改的地方。