《从零构建大模型》——从头实现GPT模型进行文本生成

0 阅读4分钟

总览

本章的核心目标是将第三章实现的注意力机制,扩展为一个完整、可运行的 GPT 类模型,并最终实现文本生成功能。整体逐层递进的知识脉络如下:

词嵌入→Transformer Block(多个堆叠)→输出层→文本生成

其中 Transformer Block 内部又包含四大关键组件:

  1. 层归一化(LayerNorm)—— 稳定训练
  2. 前馈网络(Feed Forward + GELU)—— 增加非线性表达能力
  3. 快捷连接(Shortcut Connection)—— 解决梯度消失
  4. 多头注意力(承接第三章)—— 捕捉上下文关系

最终,通过**自回归解码(贪心生成)**的方式,让训练好(或未训练)的模型逐词生成文本。

图片.png

知识点详解

1.GPT整体架构设计

GPT 的整体结构可以概括为"配置字典 + 堆叠模块":

GPT_CONFIG_124M = {
    "vocab_size": 50257,
    "context_length": 1024,
    "emb_dim": 768,
    "n_heads": 12,
    "n_layers": 12,
    "drop_rate": 0.1,
    "qkv_bias": False
}

关键点:

  • vocab_size:词表大小(决定输出层维度)
  • context_length:模型能处理的最大上下文长度
  • emb_dim:每个 token 的嵌入维度
  • n_layers:Transformer Block 堆叠的层数(越多,模型越深)

架构本质是:词嵌入 + 位置嵌入 → N 个相同结构的 TransformerBlock → 最终 LayerNorm → 线性输出层(映射到词表维度)

2.层归一化LayerNorm

作用:将每一层的输出归一化为均值 0、方差 1,防止梯度爆炸/消失,加速收敛。

公式:

x^i=xiμσ2+ϵ\hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}

yi=γx^i+βy_i = \gamma \hat{x}_i + \beta

其中:

  • μ,σ2\mu, \sigma^2:该层输入在特征维度上的均值和方差
  • ϵ\epsilon:极小值,防止除零
  • γ,β\gamma, \beta:可学习的缩放和偏移参数

注意:与 BatchNorm 不同,LayerNorm 是对每个样本的特征维度进行归一化,而不是对整个批次,因此更适合 NLP 中序列长度可变的场景。

3.前馈网络与GELU激活函数

前馈网络(FeedForward)结构:

FFN(x)=GELU(xW1+b1)W2+b2\text{FFN}(x) = \text{GELU}(xW_1 + b_1)W_2 + b_2

通常设计为"扩展-压缩"结构:先将维度扩大 4 倍,再压缩回原维度,增强模型的非线性表达能力。

GELU 激活函数公式(近似形式):

GELU(x)0.5x(1+tanh[2/π(x+0.044715x3)])\text{GELU}(x) \approx 0.5x\left(1 + \tanh\left[\sqrt{2/\pi}\left(x + 0.044715x^3\right)\right]\right)

相比传统 ReLU,GELU 曲线更平滑,在负值区域也保留了少量梯度,在 Transformer 类模型中表现更优。

4.残差连接

核心思想:将输入直接加到子层输出上,形成"跳跃路径":

output=x+SubLayer(x)

为什么重要

  • 在深层网络中,反向传播时梯度容易随层数增加而衰减(梯度消失)
  • 残差连接为梯度提供了一条"直达通道",即使中间层梯度很小,梯度依然可以通过加法直接传回浅层
  • 这是 ResNet 思想在 Transformer 中的应用

5.Transformer Block组装

将前面所有组件按固定顺序拼接,形成一个完整的 Block:

输入 x
  ├─→ LayerNorm → 多头注意力 → Dropout ──┐
  │                                      +(残差相加)
  └──────────────────────────────────────┘
  ├─→ LayerNorm → 前馈网络 → Dropout ────┐
  │                                      +(残差相加)
  └──────────────────────────────────────┘
输出

结构特点:这是"Pre-LayerNorm"结构(先归一化再进入子层),比原始 Transformer 的"Post-LayerNorm"训练更稳定。每个 Block 都包含两次残差连接(分别包裹注意力层和前馈层)。

6.完整GPT模型

组装逻辑:

图片.png

最终输出的是形状为 (batch, seq_len, vocab_size)logits(每个位置对词表中每个词的预测得分,尚未做 softmax)。

7.文本生成

生成过程是**自回归(Autoregressive)**的,核心循环逻辑:

def generate_text_simple(model, idx, max_new_tokens, context_size):
    for _ in range(max_new_tokens):
        idx_cond = idx[:, -context_size:]      # 截取上下文窗口
        logits = model(idx_cond)               # 前向传播
        logits = logits[:, -1, :]              # 只取最后一个位置
        probas = torch.softmax(logits, dim=-1) # 转为概率分布
        idx_next = torch.argmax(probas, dim=-1, keepdim=True)  # 贪心解码
        idx = torch.cat((idx, idx_next), dim=1) # 拼接进序列
    return idx

关键机制解释

  • 每次只用最后一个 token 位置的输出来预测下一个词
  • 使用 argmax 做贪心搜索(每步都选概率最大的词,非最优但最简单)
  • 生成的新 token 会被拼接回输入序列,作为下一步预测的上下文(这就是"自回归"的含义)
  • 由于此时模型尚未训练,生成结果通常是语法混乱、无意义的文本——这也为第五章"训练 GPT 模型"埋下伏笔

总结

本章完成了从"注意力机制的零件"到"完整可运行模型"的关键跨越,核心收获可以归纳为三点:

组件解决的问题
LayerNorm稳定每层输出分布,加速训练收敛
GELU + FeedForward增强模型非线性表达能力
残差连接缓解深层网络梯度消失问题

逻辑链条:嵌入层负责"输入编码",Transformer Block(注意力+前馈+归一化+残差)负责"特征提取与上下文融合",输出层负责"预测下一词",而生成函数则通过循环调用模型+贪心采样实现了逐词续写文本的能力。

⚠️ 需要特别注意的是:本章构建的模型权重是随机初始化的,因此生成的文本在语义上毫无意义。这正是为了引出后续章节的核心任务——如何通过预训练让这个"空壳"模型学会语言规律