GPT 转 Llama:从教学版 GPT 走向现代 LLM 架构
这个模块教你把 GPT 骨架逐步改造成 Llama:RMSNorm、RoPE、SwiGLU、GQA、现代 tokenizer、权重加载。
1. GPT 和 Llama 的关系
GPT 和 Llama 都是 decoder-only Transformer。
它们的主干非常像:
text
token embedding
-> 多层 Transformer block
-> final norm
-> output head
差异主要在 block 内部细节:
text
GPT: LayerNorm + learned positional embedding + GELU FFN + MHA
Llama: RMSNorm + RoPE + SwiGLU FFN + GQA
这些差异就是现代 LLM 架构演化的重点。
2. 第一个变化:LayerNorm -> RMSNorm
GPT 主线里用的是 LayerNorm。
Llama 用的是 RMSNorm。
LayerNorm 会使用均值和方差:
text
(x - mean) / sqrt(var + eps)
RMSNorm 只用均方根:
text
x / sqrt(mean(x^2) + eps)
公式可以理解成:
text
RMSNorm 不减均值,只按整体大小缩放。
好处是:
text
计算更简单
训练效果通常也很好
现代 LLM 大量使用
3. 第二个变化:GELU FFN -> SwiGLU FFN
GPT 的 feed forward 通常是:
text
Linear -> GELU -> Linear
Llama 使用 SwiGLU:
python
class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.fc1 = nn.Linear(cfg["emb_dim"], cfg["hidden_dim"], bias=False)
self.fc2 = nn.Linear(cfg["emb_dim"], cfg["hidden_dim"], bias=False)
self.fc3 = nn.Linear(cfg["hidden_dim"], cfg["emb_dim"], bias=False)
def forward(self, x):
x_fc1 = self.fc1(x)
x_fc2 = self.fc2(x)
x = nn.functional.silu(x_fc1) * x_fc2
return self.fc3(x)
关键是这行:
python
nn.functional.silu(x_fc1) * x_fc2
它是门控结构:
text
一路生成内容
一路控制哪些内容通过
SwiGLU 是现代 LLM feed forward 的常见标配。
4. 第三个变化:绝对位置 embedding -> RoPE
GPT 主线里用了 learned positional embedding:
text
token embedding + position embedding
Llama 使用 RoPE,Rotary Position Embedding。
RoPE 不再把位置向量直接加到 token embedding 上,而是在 attention 里旋转 query 和 key。
代码路线是:
python
cos, sin = compute_rope_params(...)
queries = apply_rope(queries, cos, sin)
keys = apply_rope(keys, cos, sin)
直觉理解:
text
RoPE 把位置信息编码进 Q/K 的角度关系里。
这样 attention score 里天然带有相对位置信息。
5. RoPE 的核心代码
先预计算 cos/sin:
python
def compute_rope_params(head_dim, theta_base=10_000, context_length=4096, freq_config=None):
inv_freq = 1.0 / (
theta_base ** (torch.arange(0, head_dim, 2).float() / head_dim)
)
positions = torch.arange(context_length)
angles = positions.unsqueeze(1) * inv_freq.unsqueeze(0)
angles = torch.cat([angles, angles], dim=1)
cos = torch.cos(angles)
sin = torch.sin(angles)
return cos, sin
再应用到 Q/K:
python
def apply_rope(x, cos, sin):
x1 = x[..., : head_dim // 2]
x2 = x[..., head_dim // 2 :]
rotated = torch.cat((-x2, x1), dim=-1)
x_rotated = (x * cos) + (rotated * sin)
return x_rotated.to(dtype=x.dtype)
你不用一开始完全推导数学,先记住:
text
RoPE 只作用在 attention 的 Q/K 上,不作用在 V 上。
6. 第四个变化:MHA -> GQA
Llama 3 开始更典型地使用 GQA。
GQA 的核心是:
text
query heads 多
key/value groups 少
多个 query heads 共享 K/V
在 GroupedQueryAttention 里:
python
self.W_query = nn.Linear(d_in, d_out, bias=False)
self.W_key = nn.Linear(d_in, num_kv_groups * head_dim, bias=False)
self.W_value = nn.Linear(d_in, num_kv_groups * head_dim, bias=False)
然后:
python
keys = keys.repeat_interleave(self.group_size, dim=1)
values = values.repeat_interleave(self.group_size, dim=1)
这让 Llama 更省 KV cache 显存,更适合长上下文推理。
7. Llama TransformerBlock 的结构
Llama block 仍然是:
text
norm -> attention -> residual
norm -> feed forward -> residual
示意:
python
shortcut = x
x = self.norm1(x)
x = self.att(x, mask, cos, sin)
x = x + shortcut
shortcut = x
x = self.norm2(x)
x = self.ff(x)
x = x + shortcut
这叫 pre-norm 结构。
和 GPT 主线相比,整体骨架没变,变的是 norm、attention、FFN 的内部实现。
8. Llama3Model 主体
standalone-llama32.ipynb 里的模型大致是:
python
class Llama3Model(nn.Module):
def __init__(self, cfg):
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
self.trf_blocks = nn.ModuleList(
[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]
)
self.final_norm = nn.RMSNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
forward 里动态创建 causal mask:
python
mask = torch.triu(
torch.ones(num_tokens, num_tokens, device=x.device, dtype=torch.bool),
diagonal=1
)
然后把 x, mask, cos, sin 传进每个 block。
9. 为什么不预存巨大 mask
Llama 3.1 / 3.2 支持很长上下文。
如果预先创建:
text
128k x 128k causal mask
内存会非常夸张。
所以 notebook 里强调:mask 可以 forward 时按实际 num_tokens 动态创建。
这是从教学模型走向真实长上下文模型时非常重要的工程意识。
10. Llama 2 -> Llama 3 / 3.1 / 3.2
这个目录里不是只讲 Llama 2。
推荐路线是:
text
GPT -> Llama 2
Llama 2 -> Llama 3
Llama 3 -> Llama 3.1 / 3.2
变化主要包括:
- tokenizer 改变。
- 词表大小改变。
- 上下文长度改变。
- RoPE 频率缩放策略改变。
- 使用 GQA。
- 模型配置不同。
你可以把它看成"现代开源模型结构演进"的压缩课。
11. 权重加载为什么重要
主线章节训练的是小模型。
这个 bonus 会加载 Meta / Hugging Face 上的预训练权重。
这一步能帮助你理解:
text
从零实现模型结构
然后把官方权重映射到自己的 PyTorch 类里
这是学习开源模型实现非常重要的能力。
权重加载函数常见逻辑:
text
检查 shape 是否匹配
把外部权重复制到自己模型的参数里
处理命名差异
12. 建议
text
Llama 不是推翻 GPT,而是在 GPT decoder-only 骨架上换成更现代的 norm、位置编码、FFN 和 attention 设计。