从零手搓大模型(七)GPT 转 Llama:从教学版 GPT 走向现代 LLM 架构

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 设计。
相关推荐
李二苟1 小时前
从智能眼镜到云端工作流:Rokid AI旅游搭子的设计与实现
人工智能
MY_TEUCK1 小时前
MY_TRUCK - AI 应用|AI 编程三件套:OpenSpec、Superpowers、gstack 如何把 AI 从“手速实习生”变成“高级工程师”
人工智能
阿里技术1 小时前
从 Coder 到 Designer :电商团队数据研发的 Harness Engineering 实践
人工智能·microsoft·机器学习
带娃的IT创业者1 小时前
深度解析 Apple Foundation Models:端侧 AI 的真正革命
人工智能·移动开发·大语言模型·apple·端侧ai
战族狼魂2 小时前
广东备案大模型超百款
人工智能·算法·大模型·大语言模型
Geeys2 小时前
淘宝新店一般要熬几个月?淘宝新店破周期提速实操方案
大数据·人工智能
QN1幻化引擎2 小时前
DalinX V8 — 整体能力地图与 AGI 阶段评估
人工智能·神经网络·目标检测·语言模型·数据挖掘·agi
星核0penstarry2 小时前
从单模型到多模型调度:AI API 工程化实战复盘
人工智能
MoonBit月兔2 小时前
MoonBit v0.10.4版本更新
开发语言·人工智能·编程·moonbit