Posts文章
Gen AI生成式 AI20 June 2026 · 16 min read2026年6月20日 · 閱讀約 27 分鐘

LLM Pretrain: Anatomy of a Decoder-Only TransformerLLM 預訓練:拆解純解碼器為主的Transformer架構

A rigorous, step-by-step breakdown of the LLM generation engine. Unpack the exact mechanics of RoPE, causal self-attention, and grouped-query attention to see how a prompt becomes a prediction.本文一步步拆解 LLM 的生成引擎——深入 RoPE、因果自注意力機制與分組查詢注意力的確切運作機制,看清一個提示詞究竟是如何變成一個預測結果的。

LLM Pretrain: Anatomy of a Decoder-Only Transformer

A transformer is often explained as a stack of attention layers, treated as if serving the model is an entirely separate infrastructure problem. In reality, the two are inseparable. A model's mathematical architecture directly determines what data must be saved during text generation—and that data dictates memory capacity, server batching behavior, latency, and ultimately your cloud bill.

This is the first article in a three-part series that follows a decoder-only transformer from language modelling to production serving. Before we can solve the memory bottlenecks (Part 2) or scale the engine to handle thousands of concurrent users (Part 3), we must understand the engine itself. In this post, we will strip away the magic and look at the exact mechanics of how a prompt becomes a predicted token, following the data through tokenization, causal self-attention, and feed-forward layers.

The content is structured as:

  1. The mechanics of next-token prediction
  2. Why transformers?
  3. Decoder architecture
  4. Where inference begins

A separate notebook series implements the same ideas in PyTorch and then carries the model into serving experiments.

1. The mechanics of next-token prediction

Consider a short sequence:

The company reported higher revenue.

A language model learns from this sequence by repeatedly answering one question:

Given the tokens seen so far, which token should come next?

The same sentence creates several training examples:

Context available to the modelTarget token
Thecompany
The companyreported
The company reportedhigher
The company reported higherrevenue
The company reported higher revenue.

This next-token task is the foundation for both training and generation. For every position, the model learns a probability distribution for the next token:

p(xtx1,,xt1)p(x_t\mid x_1,\ldots,x_{t-1})

For example, after the context The company reported higher, the model might assign different probabilities to revenue, earnings, costs, and many other tokens. Training encourages it to assign more probability to the token that actually appears next.

Repeating this task across large amounts of text helps the model learn patterns in language, documents, and code. These patterns are distributed across many internal weights rather than stored as individual sentences in one location.

Next-token prediction has a strict information rule: the prediction at position tt may use the current context and earlier tokens, but not later tokens. Regardless of the underlying architecture, the model must be prevented from looking ahead at future information during training. The training sequence supplies both inputs and targets simply by shifting the same tokens by one position:

Input:   The company reported higher revenue
Target:      company reported higher revenue .

For tokens y1,,yTy_1,\ldots,y_T, the cross-entropy objective is:

L(θ)=t=1Tlogpθ(yty<t)\mathcal{L}(\theta) = -\sum_{t=1}^{T} \log p_\theta(y_t\mid y_{<t})

The model compares its predictions with the actual next tokens, adjusting its weights when it guesses incorrectly. The fundamental engineering challenge here is speed: because each position only relies on earlier tokens, the predictions for an entire training sequence could theoretically be calculated simultaneously. How a model actually achieves that parallelism without violating the strict "no looking ahead" rule is what separates modern architectures from the previous generation of neural networks.

2. Why transformers?

Before 2017, sequence modelling was dominated by recurrent neural networks (RNNs) and LSTMs. They pass information through recurrent state updates, meaning they must process text strictly word-by-word. This creates a hard dependency between consecutive positions, which severely limits parallelism during training and forces distant tokens to communicate through a long, easily degraded information path.

The transformer replaced recurrent state updates with self-attention as its primary mechanism for exchanging information across positions. Attention gives each token a direct, instant mathematical path to the earlier context it is allowed to use, completely removing the step-by-step bottleneck. As a result, all known positions in a massive training sequence can be processed in parallel.

The original architecture introduced in Attention Is All You Need paired an encoder stack with a decoder. However, many general-purpose generative LLMs today have dropped the encoder entirely. To maintain the strict "no looking ahead" rule while still processing tokens in parallel, a decoder-only transformer relies on a causal mask— a mathematical filter that actively hides future tokens from earlier positions during self-attention.

Depending on how this visibility is restricted, the transformer architecture dictates its ideal use case:

ArchitectureSelf-attention visibilityTypical use
Encoder-onlyEach token can attend to every input token, both earlier and laterClassification, retrieval, token labelling, span extraction
Encoder–decoderEncoder tokens attend in both directions; decoder tokens attend only to themselves and earlier output tokens, while also attending to the encoded inputTranslation, summarisation, structured transformation
Decoder-onlyEach token attends only to itself and earlier tokens; later positions are hidden by a causal maskCompletion, conversation, instruction following, open-ended generation

3. Decoder Architecture

To understand how a model actually generates text, we first need to look at the end-to-end anatomy of the process. Think of this as the roadmap of a single prompt traveling through the model's architecture.

The complete generation path is:

Prompt text
    ↓
Tokenizer and token IDs
    ↓
Input representations and positional information   (Section 3.1)
    ↓
Stack of decoder transformer blocks                (Section 3.2)
    ├── RMSNorm and residual connections         (Section 3.2.3)
    ├── Causal self-attention                    (Section 3.2.1)
    └── Gated feed-forward network               (Section 3.2.2)
    ↓
Final normalisation and vocabulary projection      (Section 3.3)
    ↓
Next-token probabilities                           (Section 3.3)
    ↓
Selected token, appended to the prompt             (Section 3.3)

The following subsections will progressively unpack this pipeline, starting from the initial representations that enter the model and moving all the way through to the final token prediction.

3.1 Input representations and positional information

Text does not enter the model as words. A tokenizer divides it into tokens from a fixed vocabulary and maps each token to an integer ID. Depending on the tokenizer, a token may represent a word, a subword, punctuation, whitespace, or a byte-level fragment.

Each ID acts as an index to retrieve a specific row from a learned vocabulary matrix (often called an embedding table). If the vocabulary has 50,000 tokens, this matrix has the shape [50000, d_model]. This lookup operation maps the integer ID to a continuous embedding vector of width dmodeld_{model}. For batch size BB and sequence length SS, the initial residual stream has shape:

X0RB×S×dmodelX_0 \in \mathbb{R}^{B \times S \times d_{model}}

For example,

Batch size:       2
Sequence length:  128 tokens
Model width:      4,096 values per token

Residual-stream shape: [2, 128, 4,096]

Self-attention by itself has no notion of token order. The original transformer added fixed sinusoidal positional encodings to token embeddings. Many current decoders instead use Rotary Position Embeddings (or RoPE). RoPE rotates pairs of query and key coordinates according to token position, making relative position affect their dot product without adding a separate vector to the residual stream.

Comparison of Sinusoidal encoding and RoPE
PropertySinusoidal encodingRoPE
Applied toToken embeddings/residual inputQueries and keys
OperationAdditionRotation
Position representationPrimarily absolute, with recoverable relative relationshipsRelative displacement appears directly in query-key scores
Learned parametersNone in the standard formNone in the standard form
Applied to valuesIndirectly, because position is added before projectionsNo
Common usageOriginal transformer and some later modelsMany modern decoder-only LLMs

RoPE is normally a deterministic transformation rather than a learned projection matrix. Its configuration still matters: base frequencies (where different vector dimensions rotate at progressively slower speeds to encode both fine-grained and coarse-grained distances), scaling method, and maximum context assumptions affect long-context behaviour.

RoPE

RoPE does not add a position vector to the token embedding. Instead, it rotates pairs of coordinates in the query and key vectors. The processing order is:

Token representation
        ↓
Q and K projections
        ↓
Position-dependent rotation
        ↓
Attention scores

For one two-dimensional pair, RoPE applies a rotation matrix:

Rθ=[cosθsinθsinθcosθ]R_{\theta}= \begin{bmatrix}\cos\theta & -\sin\theta\\ \sin\theta & \cos\theta \end{bmatrix}

At position mm, a query vector is rotated by an angle based on mm:

qmRoPE=Rmqmq_m^{\text{RoPE}}=R_mq_m

At position nn, a key is rotated by its corresponding angle:

knRoPE=Rnknk_n^{\text{RoPE}}=R_nk_n

Because rotation matrices are orthogonal, the dot product of the rotated query and key simplifies to isolate the relative distance:

(Rmqm)(Rnkn)=qmRmRnkn=qmRnmkn(R_m q_m)^\top (R_n k_n) = q_m^\top R_m^\top R_n k_n = q_m^\top R_{n-m} k_n

This is RoPE’s most important property: the query-key score naturally and mathematically contains information about the relative displacement nmn-m.


In other words, attention can distinguish:

The matching token is 1 position earlier.
The matching token is 100 positions earlier.

even when the token content is the same.


Why rotate only Q and K?

  • Query (Q): What am I looking for?
  • Key (K): What information does this token offer?
  • Value (V): The actual information to retrieve.

RoPE rotates Q and K so their matching score includes how far apart the tokens are. V is not rotated because it carries the content after the match is decided.

RoPE also preserves vector magnitude because rotation changes direction but not length.

3.2 Inside the transformer block

The token representations pass through a sequence of transformer blocks:

Initial representations X₀
        ↓
Transformer block 0
        ↓
Transformer block 1
        ↓
       ...
        ↓
Transformer block L−1
        ↓
Final representations Xᴸ

Every block shares the same structural blueprint but contains its own unique learned weights. Each block reads the current residual stream, injects new contextual information, and passes the updated representations up to the next block. Through this sequence, the model's understanding of the text is progressively refined across the stack.

A modern pre-norm decoder block relies on three fundamental mechanisms:

  1. Causal self-attention: which routes and mixes information across different token positions.
  2. Feed-forward network (MLP): which transforms the representation at each position independently.
  3. RMSNorm and residual connections: which stabilize the learning process and ensure information flows cleanly through the deep network without degrading.

A pre-norm decoder-only transformer block showing causal self-attention, residual connections, and a gated feed-forward network

The block receives a residual stream xx_\ell and produces the input to the next block, x+1x_{\ell+1}. By stacking many blocks with different learned parameters, the model builds deep, complex representations.

The mathematical flow through layer \ell ties these three mechanisms together:

xₗ
 ↓ RMSNorm → Attention update → Residual addition
hₗ
 ↓ RMSNorm → MLP update → Residual addition
xₗ₊₁

Here, xx_\ell is the block input, hh_\ell is the intermediate representation after the attention update, and x+1x_{\ell+1} is the final block output passed to the next layer in the stack.

3.2.1 Causal self-attention

Self-attention is the mechanism that decides which tokens in the context should influence the current token. To achieve this, it relies on three elements: Queries, Keys, and Values.

Queries, Keys, and Values

For a sequence of hidden states XX, the attention layer creates queries, keys, and values through learned linear projections:

Q=XWQ,K=XWK,V=XWVQ=XW_Q, \qquad K=XW_K, \qquad V=XW_V

These three representations serve distinct roles:

  • A query describes what the current token is looking for;
  • A key describes what a token offers for matching; and
  • A value carries the information returned when that token receives attention weight.

The core operation is scaled dot-product attention:

Attention(Q,K,V)=softmax(QKdk+M)V\operatorname{Attention}(Q,K,V) = \operatorname{softmax}\left( \frac{QK^\top}{\sqrt{d_k}}+M \right)V

The query-key dot product (QKQK^\top) measures the compatibility between tokens. Dividing this score by dk\sqrt{d_k} prevents the magnitude of the dot product from growing too large with the head width. Without this scaling, the values would push the softmax function into an extremely saturated regime, leading to vanishing gradients during training.

The Causal Mask

In the formula above, MM represents the causal mask. To enforce the strict autoregressive rule (no looking into the future), entries in MM representing future positions are set to -\infty before softmax is applied.

Because the softmax of -\infty is zero, token ii can attend to itself and earlier positions (jij \le i), but its attention weight for any future position (j>ij>i) is zeroed out. This mask is what allows the transformer to process all positions in a training sequence simultaneously while preserving the integrity of next-token prediction.

Note: Attention is best understood as contextual information routing, not a strict database lookup. A high attention score does not act as definitive proof for why the model produced a specific answer. A useful representation emerges across many heads and layers, and it should not be assumed that one attention head corresponds cleanly to one human-interpretable function.

Multi-head and grouped-query attention

Instead of applying one massive attention operation across the full model width, multi-head attention splits the queries, keys, and values into smaller, parallel "heads." This allows the model to attend to different types of relationships simultaneously—for example, one head might track grammatical structure while another resolves pronoun references.

Each head performs attention in its own learned subspace. The results are then concatenated and passed through a final projection matrix WOW_O:

MHA(X)=Concat(head1,,headh)WO\operatorname{MHA}(X) = \operatorname{Concat}(head_1,\ldots,head_h)W_O

Concatenation places the head outputs side by side. The learned matrix WOW_O then mixes the information across those distinct head dimensions and maps the combined result back to the original model width (dmodeld_{model}). This step is crucial: the attention output must match the width of the residual stream before the two can be added together. Even if the concatenated width already equals dmodeld_{model}, WOW_O ensures the model learns how to optimally combine the different heads rather than leaving them as separate groups of features.

Not every modern decoder uses a strict 1:1 ratio of query heads to key-value heads. Grouped-query attention (GQA) is an architectural variant that allows several query heads to share a single key-value head.

Grouped-query attention (GQA)

Grouped-query attention lets several query heads share the same key and value heads. For example:

8 query heads
2 key/value heads

Query heads 1–4 → share KV head 1
Query heads 5–8 → share KV head 2

Comparison of architectures:

  • Multi-head attention (MHA): Every query head has its own unique K and V.
  • Grouped-query attention (GQA): A group of query heads shares K and V.
  • Multi-query attention (MQA): All query heads share a single K and V.

Why do this?

The main benefit of GQA is a significantly smaller KV cache and reduced memory bandwidth bottlenecks during generation. GQA preserves more modelling capacity and output quality than sharing a single KV head (MQA), making it the standard practical compromise between text quality and serving efficiency in modern open-weights models.

This is a critical optimization choice that has major consequences for production serving, which will be explored in depth later in this series.

3.2.2 Gated feed-forward network (MLP)

While self-attention routes information between different positions (inter-token), the feed-forward network (or MLP), transforms the representation at each position completely independently (intra-token) using the same shared learned weights.

The original transformer used two linear projections separated by a standard ReLU activation. However, most modern decoders have adopted a "gated" architecture, such as SwiGLU:

MLP(x)=Wdown(SiLU(Wgatex)Wupx)\operatorname{MLP}(x) = W_{down}\left( \operatorname{SiLU}(W_{gate}x)\odot W_{up}x \right)

In this design, each token enters as a vector of dmodeld_{model} values. The MLP temporarily expands it into a much wider feature space, dffd_{ff}, processes it, and compresses it back down:

Input (x):                 d_model values
                                ↓
Up projection (W_up):      d_ff candidate features
Gate projection (W_gate):  d_ff gate values
                                ↓
Activation (SiLU):         Applies non-linearity to the gate
                                ↓
Multiplication (⊙):        Element-wise multiplication
                                ↓
Down projection (W_down):  d_model values

Notice that the network creates two expanded vectors of the exact same size (dffd_{ff}):

  • The up projection (WupxW_{up}x) creates the raw candidate features.
  • The gate projection (WgatexW_{gate}x) decides how strongly each candidate feature should pass through to the next stage.

The SiLU (Sigmoid Linear Unit) activation transforms the gate projection into a set of learned "dimmer switches." These two wide vectors are then multiplied together one value at a time (\odot):

outputi=candidatei×activated_gatei\text{output}_i = \text{candidate}_i \times \text{activated\_gate}_i

The dimmer switches strengthen useful features and suppress less useful ones. Finally, the down projection (WdownW_{down}) compresses these selected features back to the original dmodeld_{model} width, allowing the result to be cleanly added back into the residual stream. (Note: this "expansion" strictly refers to the number of features per token, not the number of tokens in the sequence).

Because of this massive internal expansion space, MLPs frequently contain the largest share of a dense transformer's total parameters—though the exact percentage depends on the model's width, vocabulary size, and whether it uses sparse experts (MoE). Research often associates these dense MLP layers with the model's factual recall and associative memory, though it is important to remember that model knowledge is holistic and does not live exclusively in one isolated component.

3.2.3 RMSNorm and residual connections

Modern decoder families commonly use a "pre-normalization" architecture: the input is normalized immediately before passing through a transformation, while an unnormalized residual path bypasses it entirely.

Using RMSNorm, the mathematical flow for one complete block can be written as:

h=x+Attention(RMSNorm(x))h_\ell = x_\ell + \operatorname{Attention}(\operatorname{RMSNorm}(x_\ell)) x+1=h+MLP(RMSNorm(h))x_{\ell+1} = h_\ell + \operatorname{MLP}(\operatorname{RMSNorm}(h_\ell))

RMSNorm rescales the input to keep activation magnitudes stable before the attention or MLP layers process them. The residual connection (the +x+ x_\ell and +h+ h_\ell) acts as an information highway. It preserves the existing representation, allowing the attention and MLP layers to add their new results as updates, rather than completely replacing the original information. (This is exactly why both attention and the MLP must project their final outputs back to the model width dmodeld_{model}—the dimensions must match so they can be added directly back into the residual stream).

While older models like the original transformer used LayerNorm, modern decoders largely prefer RMSNorm. LayerNorm centers (subtracts the mean) and rescales activations; RMSNorm only rescales them. Dropping the mean-centering step provides a noticeable boost to computational speed and efficiency during training and inference without degrading the model's performance.

3.3 Vocabulary projection and token selection

After the sequence passes through the final transformer block (L1L-1), the residual stream holds a deeply contextualized representation for every token in the prompt. However, because next-token prediction operates strictly left-to-right, the model only looks at the representation of the very last token in the sequence to determine what comes next.

Final normalization and projection

This final vector first passes through one last RMSNorm layer to stabilize its values. Then, it hits the language modeling head (or un-embedding matrix). This learned projection matrix maps the vector from the internal model width back out to the full vocabulary size.

Final token representation:  [d_model]
                                ↓
Vocabulary projection:       [vocab_size]

If the model has a vocabulary of 50,000 tokens, this projection outputs a vector of 50,000 raw, unnormalized scores known as logits.

Next-token probabilities

Because logits can be any real number (positive or negative), they must be converted into a valid probability distribution. The model applies a softmax function:

p(xtx<t)=softmax(logits)p(x_t \mid x_{<t}) = \operatorname{softmax}(\text{logits})

This forces all 50,000 scores into a range between 0 and 1, ensuring they sum perfectly to 1.0 (or 100%). At this moment, the model has completed its forward pass. It has assigned a probability to every possible word, subword, and punctuation mark it knows.

Selection and the autoregressive loop

The model itself does not strictly choose the next token; it only provides the probabilities. The actual selection is dictated by the decoding strategy:

  • Greedy decoding: Simply selects the token with the highest probability (the argmax\operatorname{argmax}).
  • Sampling (Temperature, Top-P, Top-K): Adjusts the logits to make the distribution flatter (more creative) or sharper (more focused), and then randomly selects a token based on those weighted probabilities.

Once a token is chosen, the loop closes: the new token is appended to the original prompt, forming a new, slightly longer sequence. This new sequence is fed directly back into the tokenizer at Step 1, and the entire massive architectural process repeats to predict the token after that.

4. Where inference begins

The architecture we just explored explains exactly what must happen to execute a single forward pass. But production serving introduces an entirely different challenge:

How can a system generate many tokens, for many concurrent requests, without repeatedly recomputing the mathematical history of every prompt?

The next article, LLM Inference: How KV Caching and PagedAttention Save LLM Inference, separates the generation process into prefill and decode phases, exposing exactly how the self-attention state transforms from a theoretical mechanism into a massive production memory bottleneck.

Transformer 常被簡化成「一疊注意力層」來介紹,而部署模型服務則被當成完全獨立的基礎設施問題。但實際上,這兩者密不可分。模型的數學架構,直接決定了生成文字時必須保留哪些資料——而這些資料,又進一步決定了記憶體容量、伺服器批次處理的行為、延遲時間,最終還會反映在你的雲端帳單上。

本文是三部曲系列的第一篇,將帶你追蹤一個純解碼器 Transformer,從語言建模一路走到正式上線服務。在我們著手解決記憶體瓶頸(第二篇)、或是把引擎擴展到能同時服務成千上萬名使用者(第三篇)之前,我們得先真正理解這具引擎本身。在這篇文章中,我們會把「魔法」的外衣剝開,仔細追蹤資料如何流經 tokenization(分詞)、因果自注意力機制與前饋層,一步步看清一個提示詞究竟是如何變成一個被預測出來的 token 的。

本文結構如下:

  1. 下一個 token 預測的運作機制
  2. 為什麼是 Transformer?
  3. 解碼器架構
  4. 推論從這裡開始

另有一系列獨立的 notebook,會用 PyTorch 實作同樣的概念,並進一步將模型帶入服務端的實驗。

1. 下一個 token 預測的運作機制

來看一個簡短的序列:

The company reported higher revenue.

語言模型會透過反覆回答同一個問題,從這個序列中學習:

根據目前為止看到的 token,下一個 token 應該是什麼?

同一個句子,會產生好幾筆訓練樣本:

模型可取用的上下文目標 token
Thecompany
The companyreported
The company reportedhigher
The company reported higherrevenue
The company reported higher revenue.

這個「預測下一個 token」的任務,正是訓練與生成兩者共同的基礎。對序列中的每一個位置,模型都會學習一個關於下一個 token 的機率分布:

p(xtx1,,xt1)p(x_t\mid x_1,\ldots,x_{t-1})

舉例來說,在看過 The company reported higher 這段上下文之後,模型可能會為 revenueearningscosts 等許多不同的 token 分配不同的機率。訓練的作用,就是鼓勵模型把更高的機率,分配給實際上真正接下來出現的那個 token。

在大量文本上反覆執行這項任務,能讓模型學到語言、文件與程式碼中的各種模式。這些模式,是分散儲存在模型內部眾多的權重之中,而不是把個別句子原封不動地存放在單一位置。

下一個 token 預測,遵循一條嚴格的資訊規則:位置 tt 的預測,只能使用目前的上下文與更早之前的 token,絕不能用到後面的 token。無論底層架構為何,模型在訓練時都必須被禁止「偷看」未來的資訊。訓練序列只需將同一串 token 整體位移一個位置,就能同時提供輸入與目標:

Input:   The company reported higher revenue
Target:      company reported higher revenue .

對於 token 序列 y1,,yTy_1,\ldots,y_T,其交叉熵目標函數為:

L(θ)=t=1Tlogpθ(yty<t)\mathcal{L}(\theta) = -\sum_{t=1}^{T} \log p_\theta(y_t\mid y_{<t})

模型會將自己的預測結果,與實際的下一個 token 做比較;猜錯的時候,訓練過程就會微調模型的權重。這裡真正核心的工程挑戰在於速度:由於每個位置都只依賴更早之前的 token,理論上整個訓練序列的預測,其實可以同時一次計算完成。而一個模型究竟要如何在不違反「禁止偷看未來」這條嚴格規則的前提下,真正達成這種平行運算——正是現代架構與上一代神經網路之間,最關鍵的分野。

2. 為什麼是 Transformer?

在 2017 年之前,序列建模的主流是循環神經網路(RNN)與 LSTM。它們透過循環的狀態更新來傳遞資訊,也就是說,必須嚴格按照逐詞的順序處理文本。這造成相鄰位置之間存在著硬性的依賴關係,嚴重限制了訓練時的平行化程度,也迫使相隔較遠的 token,只能透過一條又長、又容易劣化的資訊路徑彼此溝通。

Transformer 用自注意力機制取代了循環的狀態更新,將其作為跨位置交換資訊的主要機制。注意力機制讓每個 token,都能直接、即時地用數學方式連結到它被允許使用的更早期上下文,徹底移除了逐步處理的瓶頸。因此,一個龐大訓練序列中所有已知的位置,都能夠平行處理。

Attention Is All You Need〉這篇論文提出的原始架構,是將一組編碼器與一個解碼器搭配使用。然而,今日許多通用型的生成式 LLM,都已經完全捨棄了編碼器。純解碼器 Transformer 為了在平行處理 token 的同時,仍然維持「禁止偷看未來」這條嚴格規則,仰賴的是因果遮罩——一種在自注意力運算中,主動將未來的 token 隱藏起來、不讓較早位置看見的數學濾網。

依照這種可見範圍受限制的方式不同,Transformer 架構也各自適合不同的使用情境:

架構自注意力可見範圍典型用途
純編碼器每個 token 都能關注到輸入中所有的 token,無論在它之前或之後分類、檢索、token 標註、片段擷取
編碼器-解碼器編碼器的 token 可雙向關注;解碼器的 token 則只能關注自己與更早的輸出 token,同時也會關注已編碼的輸入內容翻譯、摘要、結構化轉換
純解碼器每個 token 只能關注自己與更早的 token;較晚的位置會被因果遮罩隱藏文字接龍、對話、指令遵循、開放式生成

3. 解碼器架構

要理解模型究竟是如何生成文字的,我們得先看過整個流程從頭到尾的完整「解剖圖」。可以把它想像成一份路線圖,描繪著單一提示詞如何在模型架構中一路前行。

完整的生成路徑如下:

提示詞文字
    ↓
Tokenizer 與 token ID
    ↓
輸入表示與位置資訊                    (第 3.1 節)
    ↓
一疊解碼器 Transformer block          (第 3.2 節)
    ├── RMSNorm 與殘差連接           (第 3.2.3 節)
    ├── 因果自注意力機制             (第 3.2.1 節)
    └── 閘控前饋網路                 (第 3.2.2 節)
    ↓
最終正規化與詞彙表投影                (第 3.3 節)
    ↓
下一個 token 的機率                   (第 3.3 節)
    ↓
選出 token,附加回提示詞               (第 3.3 節)

接下來的小節,會循序漸進地拆解這整條流程,從進入模型的最初表示開始,一路講到最終的 token 預測。

3.1 輸入表示與位置資訊

文字並不是以「詞」的形式進入模型的。Tokenizer(分詞器)會依照固定的詞彙表,將文字切分成一個個 token,並將每個 token 對應到一個整數 ID。依照所使用的 tokenizer 不同,一個 token 可能代表一個完整的詞、一個子詞、標點符號、空白,或是位元組層級的片段。

每個 ID 都像一個索引,用來從一個學習得來的詞彙矩陣(常稱為嵌入表)中取出對應的那一列。如果詞彙表有 50,000 個 token,這個矩陣的形狀就是 [50000, d_model]。這個查找動作,會把整數 ID 對應到一個寬度為 dmodeld_{model} 的連續嵌入向量。對於批次大小 BB 與序列長度 SS 而言,初始的殘差流形狀為:

X0RB×S×dmodelX_0 \in \mathbb{R}^{B \times S \times d_{model}}

舉例來說,

批次大小:           2
序列長度:           128 個 token
模型寬度:           每個 token 4,096 個數值
 
殘差流形狀:[2, 128, 4,096]

自注意力機制本身,對 token 的順序毫無概念。最初的 Transformer,是在 token 嵌入上加入固定的正弦位置編碼。而許多目前的解碼器,則改用旋轉位置編碼(Rotary Position Embeddings,簡稱 RoPE)。RoPE 會依照 token 的位置,旋轉 query 與 key 座標中成對的維度,讓相對位置直接影響它們的點積結果,而不需要在殘差流中額外加入一個獨立的向量。

正弦位置編碼與 RoPE 的比較
特性正弦位置編碼RoPE
作用對象Token 嵌入/殘差流輸入Query 與 Key
運算方式相加旋轉
位置表示方式主要為絕對位置,但相對關係仍可還原相對位移直接反映在 query-key 分數中
是否有學習參數標準形式下沒有標準形式下沒有
是否作用於 Value間接影響,因為位置資訊在投影之前就已加入
常見使用場合原始 Transformer 及部分後續模型許多現代的純解碼器 LLM

RoPE 通常是一種確定性的變換,而不是一個學習得來的投影矩陣。不過,它的設定仍然重要:基礎頻率(不同的向量維度,會以逐漸放慢的速度旋轉,藉此同時編碼精細與粗略的距離資訊)、縮放方法,以及對最大上下文長度的假設,都會影響模型在長上下文情境下的表現。

RoPE 的技術細節

RoPE 並不會把位置向量加到 token 嵌入上,而是旋轉 query 與 key 向量中成對的座標。其處理順序為:

Token 表示
        ↓
Q 與 K 投影
        ↓
依位置而定的旋轉
        ↓
注意力分數

對於單一一組二維的座標對,RoPE 會套用一個旋轉矩陣:

Rθ=[cosθsinθsinθcosθ]R_{\theta}= \begin{bmatrix}\cos\theta & -\sin\theta\\ \sin\theta & \cos\theta \end{bmatrix}

在位置 mm 上,query 向量會依據 mm 旋轉一個對應的角度:

qmRoPE=Rmqmq_m^{\text{RoPE}}=R_mq_m

在位置 nn 上,key 則會旋轉其對應的角度:

knRoPE=Rnknk_n^{\text{RoPE}}=R_nk_n

由於旋轉矩陣具有正交性,旋轉後的 query 與 key 做點積時,會化簡成只留下相對距離的形式:

(Rmqm)(Rnkn)=qmRmRnkn=qmRnmkn(R_m q_m)^\top (R_n k_n) = q_m^\top R_m^\top R_n k_n = q_m^\top R_{n-m} k_n

這正是 RoPE 最重要的特性:query-key 分數,會在數學上自然而然地包含相對位移 nmn-m 的資訊。


換句話說,注意力機制能夠分辨:

匹配的 token 在 1 個位置之前。
匹配的 token 在 100 個位置之前。

即使 token 的內容完全相同,也能做出這樣的區分。


為什麼只旋轉 Q 和 K?

  • Query(Q):我正在尋找什麼?
  • Key(K):這個 token 能提供什麼資訊?
  • Value(V):真正要被取用的實際資訊。

RoPE 只旋轉 Q 與 K,讓兩者的匹配分數能反映出 token 之間相隔多遠。V 則不旋轉,因為它承載的是匹配結果確定「之後」才會用到的實際內容。

此外,RoPE 也能保留向量的長度,因為旋轉只會改變方向,不會改變長度。

3.2 深入 Transformer Block 內部

Token 的表示,會依序通過一連串的 Transformer block:

初始表示 X₀
        ↓
Transformer block 0
        ↓
Transformer block 1
        ↓
       ...
        ↓
Transformer block L−1
        ↓
最終表示 Xᴸ

每一個 block 都共用同一套結構藍圖,卻各自擁有獨一無二的學習權重。每個 block 都會讀取目前的殘差流、注入新的上下文資訊,再將更新後的表示往上傳遞給下一個 block。透過這樣一層層堆疊的過程,模型對文字的理解,會被逐步精煉。

一個現代的 pre-norm 解碼器 block,仰賴三個基本機制:

  1. 因果自注意力機制:負責在不同 token 位置之間傳遞並混合資訊。
  2. 前饋網路(MLP):針對每個位置獨立進行轉換。
  3. RMSNorm 與殘差連接:穩定學習過程,確保資訊能乾淨地流經這個深層網路,而不會逐漸劣化。 一個 pre-norm 純解碼器 Transformer block,展示因果自注意力機制、殘差連接與閘控前饋網路

這個 block 接收殘差流 xx_\ell 作為輸入,並產生下一個 block 的輸入 x+1x_{\ell+1}。透過堆疊許多個各自擁有不同學習參數的 block,模型得以建構出既深且複雜的表示。

\ell 層的數學流程,將這三個機制串連在一起:

xₗ
 ↓ RMSNorm → 注意力更新 → 殘差相加
hₗ
 ↓ RMSNorm → MLP 更新 → 殘差相加
xₗ₊₁

這裡,xx_\ell 是 block 的輸入,hh_\ell 是經過注意力更新後的中間表示,而 x+1x_{\ell+1} 則是傳遞給堆疊中下一層的最終 block 輸出。

3.2.1 因果自注意力機制

自注意力機制,是決定上下文中哪些 token 該影響目前這個 token 的機制。要做到這一點,它仰賴三個要素:Query、Key 與 Value。

Query、Key 與 Value

對於一串隱藏狀態 XX,注意力層會透過學習得來的線性投影,產生 query、key 與 value:

Q=XWQ,K=XWK,V=XWVQ=XW_Q, \qquad K=XW_K, \qquad V=XW_V

這三種表示,各自扮演不同的角色:

  • Query 描述的是目前這個 token 正在尋找什麼;
  • Key 描述的是某個 token 能提供什麼來供人匹配;
  • Value 承載的則是當某個 token 獲得注意力權重時,會被回傳的實際資訊。 核心運算是縮放點積注意力(scaled dot-product attention):
Attention(Q,K,V)=softmax(QKdk+M)V\operatorname{Attention}(Q,K,V) = \operatorname{softmax}\left( \frac{QK^\top}{\sqrt{d_k}}+M \right)V

Query 與 key 的點積(QKQK^\top)衡量的是 token 之間的相容程度。將這個分數除以 dk\sqrt{d_k},能避免點積的量級隨著 head 寬度增加而變得過大。若沒有這個縮放步驟,這些數值會把 softmax 函數推向極度飽和的區域,導致訓練時出現梯度消失的問題。

因果遮罩

在上面的公式中,MM 代表因果遮罩。為了強制執行嚴格的自迴歸規則(禁止窺看未來),MM 中代表未來位置的項目,在套用 softmax 之前,會先被設為 -\infty

由於 -\infty 經過 softmax 後會變成零,token ii 可以關注自己與更早的位置(jij \le i),但對任何未來位置(j>ij>i)的注意力權重都會被歸零。正是這個遮罩,讓 Transformer 得以同時處理訓練序列中的所有位置,同時仍完整保留下一個 token 預測的正確性。

附註:與其把注意力機制理解成嚴格的資料庫查找,不如將它理解為一種上下文資訊的路由機制。高注意力分數,並不能作為模型為何產生某個特定答案的確鑿證據。一個真正有用的表示,是橫跨許多 head 與許多層才逐漸浮現的,不應假設某一個注意力 head,就恰好對應到人類能理解的某一種功能。

多頭注意力與分組查詢注意力

多頭注意力(multi-head attention)不是對整個模型寬度做一次龐大的注意力運算,而是把 query、key、value 拆分成較小、可平行運算的「head」。這讓模型能同時關注不同類型的關係——例如,某個 head 可能專門追蹤語法結構,另一個 head 則負責解析代名詞的指涉對象。

每個 head 都在自己學習得來的子空間中執行注意力運算。這些結果接著會被串接起來,再通過最終的投影矩陣 WOW_O

MHA(X)=Concat(head1,,headh)WO\operatorname{MHA}(X) = \operatorname{Concat}(head_1,\ldots,head_h)W_O

串接的作用,是把各個 head 的輸出並排放在一起。學習得來的矩陣 WOW_O,接著會混合這些不同 head 維度之間的資訊,並將合併後的結果映射回原本的模型寬度(dmodeld_{model})。這一步至關重要:注意力輸出的寬度,必須與殘差流一致,兩者才能相加。即使串接後的寬度已經等於 dmodeld_{model}WOW_O 仍然確保模型能學到如何以最佳方式組合各個 head,而不是把它們留成一組組彼此獨立的特徵。

並非每個現代解碼器,都採用嚴格 1:1 比例的 query head 對 key-value head。分組查詢注意力(GQA)是一種架構上的變體,允許好幾個 query head 共用同一個 key-value head。

分組查詢注意力(GQA)的技術細節

分組查詢注意力,讓好幾個 query head 共用相同的 key 與 value head。舉例來說:

8 個 query head
2 個 key/value head
 
Query head 1–4 → 共用 KV head 1
Query head 5–8 → 共用 KV head 2

架構比較:

  • 多頭注意力(MHA):每個 query head 都各自擁有獨一無二的 K 與 V。
  • 分組查詢注意力(GQA):一組 query head 共用 K 與 V。
  • 多查詢注意力(MQA):所有 query head 共用單一一組 K 與 V。

為什麼要這麼做?

GQA 最主要的好處,是能大幅縮小 KV 快取,並降低生成階段的記憶體頻寬瓶頸。相較於只共用單一 KV head 的 MQA,GQA 保留了更多的建模能力與輸出品質,因此成為現代開放權重模型中,在文字品質與服務效率之間,最常見的實務折衷方案。

這是一個關鍵的優化選擇,對正式上線服務有重大影響,本系列後續文章會再深入探討。

3.2.2 閘控前饋網路(MLP)

自注意力機制負責在不同位置之間(token 與 token 之間)傳遞資訊,而前饋網路(或稱 MLP),則是使用同一組共享的學習權重,完全獨立地(在單一 token 內部)轉換每個位置的表示。

最初的 Transformer,使用的是兩個線性投影中間夾著一個標準 ReLU 激活函數。然而,如今大多數現代解碼器,都已改採「閘控」架構,例如 SwiGLU:

MLP(x)=Wdown(SiLU(Wgatex)Wupx)\operatorname{MLP}(x) = W_{down}\left( \operatorname{SiLU}(W_{gate}x)\odot W_{up}x \right)

在這種設計中,每個 token 都是以一個 dmodeld_{model} 維的向量進入。MLP 會先暫時將它擴展到寬得多的特徵空間 dffd_{ff},進行處理後,再壓縮回原本的維度:

輸入(x):                  d_model 個數值
                                ↓
Up projection(W_up):      d_ff 個候選特徵
Gate projection(W_gate):  d_ff 個閘門數值
                                ↓
激活函數(SiLU):            對閘門施加非線性
                                ↓
逐元素相乘(⊙):             逐一元素相乘
                                ↓
Down projection(W_down):  d_model 個數值

請注意,這個網路會產生兩個維度完全相同(都是 dffd_{ff})的擴展向量:

  • Up projection(WupxW_{up}x)產生的是原始的候選特徵。
  • Gate projection(WgatexW_{gate}x)決定的是每個候選特徵,該以多強的力道傳遞到下一階段。 SiLU(Sigmoid Linear Unit)激活函數,會把 gate projection 轉換成一組學習得來的「調光開關」。接著,這兩個寬向量會逐一元素相乘(\odot):
outputi=candidatei×activated_gatei\text{output}_i=\text{candidate}_i\times\text{activated\_gate}_i

這些調光開關,會強化有用的特徵、抑制較不重要的特徵。最後,down projection(WdownW_{down})會把這些篩選過的特徵壓縮回原本的 dmodeld_{model} 寬度,讓結果得以乾淨地加回殘差流。(附註:這裡的「擴展」,指的嚴格來說是每個 token 的特徵數量,而不是序列中 token 的數量。)

正因為內部有這麼龐大的擴展空間,MLP 往往佔了一個密集型(dense)Transformer 總參數量中最大的一部分——不過確切比例,仍取決於模型的寬度、詞彙表大小,以及是否採用了稀疏專家(MoE)架構。研究常將這些密集的 MLP 層,與模型的事實記憶及聯想記憶連結在一起,但仍要記得,模型的知識是整體性的,並不會只獨立存在於某一個孤立的元件之中。

3.2.3 RMSNorm 與殘差連接

現代的解碼器家族,普遍採用「pre-normalization」(前置正規化)架構:輸入在進入某個轉換之前,會先立即被正規化,同時有一條未經正規化的殘差路徑,完全繞過這個轉換。

使用 RMSNorm 時,一個完整 block 的數學流程可以寫成:

h=x+Attention(RMSNorm(x))h_\ell = x_\ell + \operatorname{Attention}(\operatorname{RMSNorm}(x_\ell)) x+1=h+MLP(RMSNorm(h))x_{\ell+1} = h_\ell + \operatorname{MLP}(\operatorname{RMSNorm}(h_\ell))

RMSNorm 會重新縮放輸入,讓激活值的量級在進入注意力層或 MLP 處理之前,保持穩定。殘差連接(也就是 +x+ x_\ell+h+ h_\ell 這兩個加法)扮演的角色,就像一條資訊高速公路:它保留了既有的表示,讓注意力層與 MLP 得以把新的結果,當作一種「更新」疊加上去,而不是徹底取代原本的資訊。(這正是為什麼注意力與 MLP,最終都必須把輸出投影回模型寬度 dmodeld_{model}——維度必須一致,才能直接加回殘差流。)

雖然像原始 Transformer 這樣的較舊模型使用的是 LayerNorm,但現代解碼器大多偏好 RMSNorm。LayerNorm 會將激活值置中(減去平均值)並重新縮放;RMSNorm 則只做重新縮放這一步。省去置中這個步驟,能在訓練與推論時,帶來明顯的運算速度與效率提升,同時不會犧牲模型的表現。

3.3 詞彙表投影與 token 選擇

序列通過最後一個 transformer block(L1L-1)之後,殘差流中保存著提示詞裡每一個 token、經過深度上下文化後的表示。不過,由於下一個 token 預測嚴格依由左至右的順序進行,模型在決定接下來會出現什麼時,只會參考序列中最後一個 token 的表示。

最終正規化與投影

這個最終向量,會先通過最後一層 RMSNorm,穩定其數值,接著送進語言模型頭(language modeling head,又稱反嵌入矩陣)。這個學習得來的投影矩陣,會把向量從模型內部的寬度,映射回完整的詞彙表大小。

最終 token 表示:              [d_model]
                                ↓
詞彙表投影:                   [vocab_size]

如果模型的詞彙表有 50,000 個 token,這個投影就會輸出一個由 50,000 個原始、未正規化分數組成的向量,這些分數稱為 logits。

下一個 token 的機率

由於 logits 可以是任何實數(正數或負數),必須先轉換成合法的機率分布。模型會套用 softmax 函數:

p(xtx<t)=softmax(logits)p(x_t \mid x_{<t}) = \operatorname{softmax}(\text{logits})

這會強制把全部 50,000 個分數,壓縮到 0 到 1 之間的範圍,並確保它們的總和恰好等於 1.0(也就是 100%)。到了這一刻,模型已經完成了一次前向傳播,為它所知道的每一個可能的詞、子詞與標點符號,都分配好了一個機率。

選擇與自迴歸迴圈

嚴格來說,模型本身並不會挑選下一個 token,它只負責提供機率。真正的選擇,是由解碼策略來決定的:

  • 貪婪解碼(Greedy decoding):直接選擇機率最高的那個 token(也就是 argmax\operatorname{argmax})。
  • 採樣(溫度、Top-P、Top-K):調整 logits,讓機率分布變得更平(更有創意)或更尖銳(更聚焦),接著再依照這些加權後的機率,隨機選出一個 token。 一旦選定 token,這個迴圈就閉合了:新的 token 會被附加到原本的提示詞後面,形成一個稍微長一點的新序列。這個新序列,會直接被送回第 1 步的 tokenizer,於是整套龐大的架構流程再次重複執行,預測出再下一個 token。

4. 推論從這裡開始

我們剛才探討的架構,精確地說明了執行單一次前向傳播究竟需要發生哪些事。但正式上線服務,帶來的是一個截然不同的挑戰:

一個系統要如何在為許多並發請求生成大量 token 的同時,不必為每一個提示詞反覆重新計算其數學歷史?

下一篇文章〈LLM 推論:KV 快取與 PagedAttention 如何拯救 LLM 推論〉,會把生成過程拆分成預填充(prefill)與解碼(decode)兩個階段,精確揭示自注意力機制的狀態,究竟是如何從一個理論上的機制,轉變成正式服務中巨大的記憶體瓶頸。