Skip to content

概念卡片

KV Cache(Key-Value 快取)

是什麼 在自迴歸生成中,每個時間步計算注意力時,需要目前 token 的 Query 與所有過去 token 的 Key 和 Value 做計算。KV Cache 將過去 token 的 K、V 向量儲存下來,避免每次重新計算。

在 llama2.c 中的實作

c
s->key_cache = calloc(p->n_layers * p->seq_len * kv_dim, sizeof(float));
s->value_cache = calloc(p->n_layers * p->seq_len * kv_dim, sizeof(float));

三維陣列:[layer][position][kv_dim]

優點

  • 時間複雜度從 O(n³) 降為 O(n²)
  • 每個新 token 只需計算自身的 QKV,過去 token 的 KV 直接從 cache 讀取

注意事項

  • 需要 O(n_layers × seq_len × kv_dim) 的額外記憶體
  • 對長序列生成,記憶體占用可能成為瓶頸

RoPE(Rotary Position Embedding)

是什麼 一種相對位置編碼方法,將位置資訊透過旋轉矩陣注入到 Q 和 K 向量中。

核心公式

Rot(Θ, pos) = [cos(posθ)  -sin(posθ)]
              [sin(posθ)   cos(posθ)]

每個維度對的旋轉頻率 θ_i = 10000^(-2i/d) 不同。

在 llama2.c 中的實作

c
for (int i = 0; i < dim; i+=2) {
    int head_dim = i % head_size;
    float freq = 1.0f / powf(10000.0f, head_dim / (float)head_size);
    float val = pos * freq;
    float fcr = cosf(val), fci = sinf(val);
    // 旋轉 q[i], q[i+1] 和 k[i], k[i+1]
}

特點

  • 絕對位置編碼的形式,相對位置編碼的效果
  • 當 q 和 k 都旋轉相同角度時,它們的 dot product 只取決於位置差
  • 無需學習額外參數
  • 支援序列長度外推

RMSNorm(Root Mean Square Layer Normalization)

是什麼 一種簡化的 Layer Normalization,只做縮放(scale)不做平移(shift)。

公式

RMSNorm(x) = weight * x / sqrt(mean(x²) + eps)

與 LayerNorm 的比較

LayerNormRMSNorm
(x - μ) / σ * γ + βx / RMS(x) * weight
需要計算均值 μ不需要
2 個可學習參數 (γ, β)1 個可學習參數 (weight)
計算量大約 2×計算量較少

為什麼 LLM 用 RMSNorm

  • 計算效率更高(少了減均值的操作)
  • 在深層 Transformer 中效果與 LayerNorm 相當
  • Meta 的 Llama 系列使用 RMSNorm

GQA(Grouped Query Attention)

是什麼 多頭注意力的一種變體,將 Query head 分成若干組,每組共享一個 Key/Value head。

示意圖

Q heads:  [H1][H2][H3][H4][H5][H6]
              \   /       \   /
KV heads:     [KV1]       [KV2]

n_heads=6, n_kv_heads=2 → kv_mul=3

在 llama2.c 中的實作

c
int kv_dim = (p->dim * p->n_kv_heads) / p->n_heads;
int kv_mul = p->n_heads / p->n_kv_heads;

// 在 attention 中:
float* k = s->key_cache + loff + t * kv_dim + (h / kv_mul) * head_size;

優點

  • 減少 KV Cache 的記憶體用量(約 n_kv_heads/n_heads)
  • 推理速度更快
  • 模型品質損失極小

GQA vs MHA vs MQA

類型KV heads典型值
MHA (Multi-Head Attention)= n_heads32
GQA (Grouped Query Attention)介於 1 ~ n_heads8
MQA (Multi-Query Attention)= 11

Tokenizer(分詞器)

是什麼 將文字字串轉換為整數 ID 序列的元件,LLM 處理的是 ID 而非原始文字。

SentencePiece + BPE llama2.c 使用 SentencePiece 訓練的 BPE(Byte Pair Encoding)tokenizer:

  1. 從 UTF-8 位元組開始
  2. 反覆合併最頻繁的相鄰 token pair
  3. 形成次詞(subword)詞彙表

特殊 Token

IDToken意義
0<unk>未知 token
1<s>BOS(Beginning of Sequence)
2</s>EOS(End of Sequence)

在 llama2.c 中

  • encode():文字 → token IDs(含 BPE 合併)
  • decode():token ID → 文字片段
  • str_lookup():用二分搜尋在排序後的 vocab 中查詢字串

Quantization(量化)

是什麼 將模型權重從 float32 降為較低精度(如 int8),減少儲存空間並加速推理。

Q8_0 格式(llama2.c 使用)

  • 對稱量化(symmetric),範圍 [-127, 127]
  • 每組 (block) 有獨立的 scale 參數(float32)
  • 權重儲存為 int8,運算時反量化為 float32

runq.c 的量化推理 僅量化參與矩陣乘法的權重(matmul),RMSNorm 等層仍使用 float32。

效果

指標float32int8
儲存空間26GB (7B)6.7GB (7B)
推理速度~4.6 tok/s~14 tok/s
模型品質基準略微下降

SwiGLU(Swish-Gated Linear Unit)

是什麼 LLM 中 MLP 層使用的激活函數,結合 Swish (SiLU) 和門控機制。

公式

SwiGLU(x) = w2(SiLU(w1(x)) * w3(x))
其中 SiLU(x) = x * sigmoid(x) = x / (1 + e^(-x))

與 ReLU MLP 的比較

ReLU MLPSwiGLU MLP
w2(ReLU(w1(x)))w2(SiLU(w1(x)) * w3(x))
2 個權重矩陣3 個權重矩陣
參數較少參數較多,但效果更好

在 llama2.c 中:

c
matmul(hb, xb, w1, dim, hidden_dim);   // w1(x)
matmul(hb2, xb, w3, dim, hidden_dim);  // w3(x)
for (i...) hb[i] = SiLU(hb[i]) * hb2[i]; // 門控相乘
matmul(xb, hb, w2, hidden_dim, dim);   // w2()