07-大模型的"基础教育":预训练如何赋予模型语言能力?

4 阅读4分钟

什么是预训练?

在上一章中,我们学习了训练的基本概念:前向传播、反向传播、梯度下降。现在让我们聚焦于大模型训练的第一阶段:预训练(Pre-training)

定义

预训练(Pre-training)是指在大规模无标注文本数据上,通过自监督学习让模型学习语言的基础知识。

类比

预训练就像一个人的基础教育

  • 预训练:小学到高中,学习语文、数学、历史、科学等基础知识(通用能力)
  • 微调(Fine-tuning):大学或职业培训,学习专业技能(特定任务)

一个受过良好基础教育的人,可以快速适应各种专业领域; 一个经过充分预训练的模型,可以快速适应各种下游任务。

预训练 vs 传统监督学习

传统监督学习

  • 需要大量标注数据(输入-输出对)
  • 针对特定任务训练
  • 数据标注成本高

预训练

  • 使用无标注文本数据
  • 通过自监督任务学习通用语言知识
  • 数据获取成本低(互联网上有海量文本)
特性传统监督学习预训练
数据类型标注数据(输入-标签对)无标注文本
数据规模通常较小(万-百万级)极大(万亿Token级)
训练目标特定任务(分类、翻译等)通用语言理解
标注成本高(人工标注)低(自动获取)
模型能力单一任务多任务通用能力

预训练的核心任务:Next Token Prediction

大模型预训练的核心任务非常简单:预测下一个Token(Next Token Prediction)

任务定义

给定前面的Token序列,预测下一个Token:

P(xt+1x1,x2,,xt)P(x_{t+1} | x_1, x_2, \ldots, x_t)

举例

输入:"今天天气很"

  • 模型需要预测:下一个Token是"好"的概率、是"差"的概率、是"热"的概率...

为什么这个任务有效?

自监督学习

  • 不需要人工标注
  • 文本本身就包含了"答案"(下一个词就是标签)
  • 可以利用互联网上的海量文本

学到的能力

通过预测下一个Token,模型被迫学习:

  1. 语法

    • "我是"后面应该接名词("学生"、"老师")
    • 不应该接动词(❌"跑步")
  2. 语义

    • "今天天气很"后面应该是天气相关的词("好"、"热"、"冷")
    • 不应该是完全无关的词(❌"手机")
  3. 常识

    • "太阳从"后面是"东方升起"
    • "水的沸点是"后面是"100摄氏度"
  4. 推理

    • "他很饿,所以他"后面可能是"吃饭"
    • 需要理解因果关系
  5. 上下文理解

    • "小明今天没来,他"后面的"他"指的是"小明"
    • 需要理解指代关系

具体例子

输入文本:"猫是一种哺乳动物,它们喜欢"

分解为训练样本

输入序列目标Token
"猫""是"
"猫是""一"
"猫是一""种"
"猫是一种""哺"
"猫是一种哺""乳"
"猫是一种哺乳""动"
"猫是一种哺乳动""物"
"猫是一种哺乳动物"","
"猫是一种哺乳动物,""它"
"猫是一种哺乳动物,它""们"
"猫是一种哺乳动物,它们""喜"
"猫是一种哺乳动物,它们喜""欢"

每个位置都是一个训练样本!

一句话可以产生多个训练样本,这就是为什么预训练可以使用海量数据。

数学形式

对于一个长度为 nn 的序列 [x1,x2,,xn][x_1, x_2, \ldots, x_n],预训练的目标是最大化对数似然:

L=t=1nlogP(xtx<t)\mathcal{L} = \sum_{t=1}^{n} \log P(x_t | x_{<t})

其中:

  • x<t=[x1,x2,,xt1]x_{<t} = [x_1, x_2, \ldots, x_{t-1}]:前面的所有Token
  • P(xtx<t)P(x_t | x_{<t}):给定前文,预测第 tt 个Token的概率

目标:让模型给正确Token的概率尽可能高。

Causal Language Modeling

这种"只看前文,预测下一个词"的方式叫做因果语言模型(Causal Language Modeling, CLM)

  • 因果(Causal):只能看"过去",不能看"未来"
  • 自回归(Autoregressive):逐个生成Token

实现方式:通过Causal Mask(因果掩码)实现。

在注意力计算中,防止位置 ii 看到位置 j>ij > i 的信息:

Attention_Mask[i,j]={0if ji(可以看)if j>i(不能看)\text{Attention\_Mask}[i, j] = \begin{cases} 0 & \text{if } j \leq i \quad \text{(可以看)} \\ -\infty & \text{if } j > i \quad \text{(不能看)} \end{cases}

可视化(4个Token的序列):

       看到的Token
       t1  t2  t3  t4
预测 t1 [ ✓  ✗  ✗  ✗ ]  只能看t1
     t2 [ ✓  ✓  ✗  ✗ ]  可以看t1,t2
     t3 [ ✓  ✓  ✓  ✗ ]  可以看t1,t2,t3
     t4 [ ✓  ✓  ✓  ✓ ]  可以看t1,t2,t3,t4

这保证了模型在预测 tt 时,只能使用 tt 之前的信息,符合真实生成场景。

预训练数据

预训练的效果很大程度上取决于数据的质量和规模。

数据来源

现代大模型的预训练数据主要来自:

1. 网页数据(Web Pages)

来源

  • Common Crawl:定期抓取全网的公开数据
  • C4(Colossal Clean Crawled Corpus):清洗后的Common Crawl

规模

  • GPT-3:约 4100 亿Token
  • LLaMA:约 1.4 万亿Token

优点

  • 规模巨大
  • 覆盖广泛的主题

缺点

  • 质量参差不齐
  • 包含噪声、垃圾内容
  • 可能有偏见、错误信息

2. 书籍(Books)

来源

  • BookCorpus:约 11,000本书
  • Project Gutenberg:公版书籍
  • Books3:更大规模的书籍数据集

优点

  • 文本质量高
  • 语言流畅、结构完整
  • 包含深度内容和复杂推理

缺点

  • 规模相对较小
  • 版权问题

3. 代码(Code)

来源

  • GitHub:公开代码仓库
  • StackOverflow:问答数据

规模

  • GPT-3:未包含代码
  • Codex/GPT-3.5:大量代码数据
  • LLaMA:约 5%是代码

优点

  • 提升模型的代码能力
  • 学习逻辑推理
  • 提高结构化思维

4. 学术论文(Papers)

来源

  • arXiv:物理、数学、计算机等领域的预印本
  • PubMed:医学论文

优点

  • 高质量、专业内容
  • 提升科学推理能力
  • 包含专业术语和知识

5. 对话数据(Conversations)

来源

  • Reddit:论坛讨论
  • StackExchange:问答社区

优点

  • 对话风格的数据
  • 提升问答能力
  • 包含多样化观点

数据规模对比

模型训练Token数主要数据来源
GPT-2100 亿WebText
GPT-33000 亿Common Crawl, Books, Wikipedia
LLaMA-11.4 万亿Common Crawl, C4, GitHub, Books, arXiv, Wikipedia
LLaMA-22 万亿公开数据(具体未披露)
GPT-4未披露可能 >10 万亿

趋势:数据规模不断增大。

数据预处理

原始数据需要清洗和预处理:

1. 去重(Deduplication)

问题:网页数据有大量重复内容(模板、转载等)

解决

  • 精确去重:完全相同的文本
  • 近似去重:使用MinHash等算法找相似文本

重要性:去重可以显著提升模型质量,减少记忆效应。

2. 质量过滤(Quality Filtering)

过滤低质量内容

  • 过短或过长的文本
  • 重复字符过多(如"aaaaaaaaa")
  • 非自然语言(乱码)
  • 色情、暴力、仇恨言论

方法

  • 启发式规则(长度、特殊字符比例等)
  • 分类器(训练一个质量分类器)
  • 困惑度过滤(用语言模型评估流畅度)

3. 敏感内容过滤

过滤

  • 个人隐私信息(PII):邮箱、电话、地址
  • 版权内容
  • 有害内容

4. Tokenization

将文本转换为Token序列:

"今天天气很好"  [1234, 5678, 9012, 3456, 7890]

常用方法

  • BPE(Byte Pair Encoding):GPT系列
  • WordPiece:BERT
  • SentencePiece:LLaMA

数据混合(Data Mixing)

不同来源的数据按一定比例混合:

LLaMA的数据混合(简化)

数据源占比Token数
CommonCrawl67%~1万亿
C415%~2000亿
GitHub4.5%~630亿
Wikipedia4.5%~630亿
Books4.5%~630亿
ArXiv2.5%~350亿
StackExchange2%~280亿

原则

  • 高质量数据(Books、Wikipedia)可以适当过采样
  • 低质量数据(原始网页)需要过滤和降采样
  • 代码数据的比例影响代码能力

预训练过程

完整流程

┌───────────────────────────────────────────────────────────┐
│                    1. 数据准备                             │
│  - 收集数据(网页、书籍、代码等)                           │
│  - 清洗过滤(去重、质量过滤)                               │
│  - Tokenization                                           │
│  - 构建训练数据集                                          │
└────────────────────┬──────────────────────────────────────┘
                     ↓
┌───────────────────────────────────────────────────────────┐
│                    2. 模型初始化                           │
│  - 随机初始化所有参数                                       │
│  - 设置模型配置(层数、维度、头数等)                        │
└────────────────────┬──────────────────────────────────────┘
                     ↓
┌───────────────────────────────────────────────────────────┐
│                    3. 训练循环                             │
│  ┌────────────────────────────────────────────┐           │
│  │  For each batch in dataset:                │           │
│  │    1. 采样一批文本(batch_size个序列)     │           │
│  │    2. 前向传播:计算logits和loss           │           │
│  │    3. 反向传播:计算梯度                   │           │
│  │    4. 参数更新:AdamW优化                  │           │
│  │    5. 学习率调整:warmup + cosine decay    │           │
│  │    6. 记录指标:loss、困惑度等             │           │
│  │    7. 定期保存checkpoint                   │           │
│  └────────────────────────────────────────────┘           │
│  重复数百万到数千万步                                       │
└────────────────────┬──────────────────────────────────────┘
                     ↓
┌───────────────────────────────────────────────────────────┐
│                    4. 模型评估                             │
│  - 在验证集上计算困惑度(Perplexity)                       │
│  - 评估下游任务性能                                        │
│  - 选择最佳checkpoint                                      │
└────────────────────┬──────────────────────────────────────┘
                     ↓
                预训练完成

训练代码(伪代码)

# 1. 模型和优化器初始化
model = GPT(vocab_size=50257, n_layer=12, n_head=12, n_embd=768)
model = model.to('cuda')

optimizer = AdamW(
    model.parameters(),
    lr=6e-4,
    betas=(0.9, 0.95),
    weight_decay=0.1
)

lr_scheduler = CosineAnnealingWarmup(
    optimizer,
    warmup_steps=2000,
    max_steps=300000
)

# 2. 训练循环
global_step = 0
for epoch in range(num_epochs):
    for batch in dataloader:
        # (1) 准备数据
        input_ids = batch['input_ids'].to('cuda')   # [batch_size, seq_len]
        # 目标是输入右移一位(预测下一个Token)
        target_ids = input_ids[:, 1:]                # [batch_size, seq_len-1]
        input_ids = input_ids[:, :-1]               # [batch_size, seq_len-1]

        # (2) 前向传播
        logits = model(input_ids)                    # [batch_size, seq_len-1, vocab_size]

        # (3) 计算Loss
        loss = F.cross_entropy(
            logits.reshape(-1, logits.size(-1)),     # [batch*seq, vocab]
            target_ids.reshape(-1),                  # [batch*seq]
            ignore_index=PAD_TOKEN_ID                # 忽略padding
        )

        # (4) 反向传播
        loss.backward()

        # (5) 梯度裁剪(防止梯度爆炸)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

        # (6) 参数更新
        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()

        # (7) 记录指标
        global_step += 1
        if global_step % 100 == 0:
            perplexity = torch.exp(loss).item()
            current_lr = lr_scheduler.get_last_lr()[0]
            print(f"Step {global_step}: Loss={loss.item():.4f}, "
                  f"PPL={perplexity:.2f}, LR={current_lr:.6f}")

        # (8) 保存checkpoint
        if global_step % 10000 == 0:
            torch.save({
                'step': global_step,
                'model_state_dict': model.state_dict(),
                'optimizer_state_dict': optimizer.state_dict(),
                'loss': loss.item(),
            }, f'checkpoint_{global_step}.pt')

关键训练设置

Batch Size

有效Batch Size = batch_size × gradient_accumulation_steps × num_gpus

模型有效Batch SizeToken数/Batch
GPT-2512~262K Token
GPT-33.2M Token3.2M Token
LLaMA-65B4M Token4M Token

大Batch Size的优点

  • 梯度更稳定
  • 训练更快(更少的参数更新次数)

实现大Batch Size

  • 梯度累积(Gradient Accumulation)
  • 多GPU训练(Data Parallel、Model Parallel)

Sequence Length

序列长度:每个训练样本的Token数

模型序列长度
GPT-21024
GPT-32048
GPT-48192-32768
Claude-2100K

趋势:序列长度不断增加,支持更长的上下文。

挑战

  • 内存消耗:O(n2)O(n^2)(注意力计算)
  • 计算量:与序列长度的平方成正比

训练步数

训练多少步?

训练步数=总Token数Batch Size×Seq Length\text{训练步数} = \frac{\text{总Token数}}{\text{Batch Size} \times \text{Seq Length}}
模型总Token数训练步数
GPT-3300B~100K 步
LLaMA-11.4T~350K 步
LLaMA-22T~500K 步

Scaling Law

更多的Token → 更好的性能

但收益递减:从1T到2T的提升 < 从100B到1T的提升

训练监控

1. Loss曲线

理想的Loss曲线

Loss
  ^
  |
  |\
  | \____
  |      ----___
  |             ------____
  +-------------------------> Step

稳定下降,最终趋于平缓。

异常情况

Loss
  ^
  |          /\
  |    /\   /  \
  |   /  \ /    \
  |  /    X      \
  +-----------------> Step

震荡、发散 → 学习率太大、数据有问题、数值不稳定

2. 困惑度(Perplexity, PPL)

定义

PPL=exp(Loss)\text{PPL} = \exp(\text{Loss})

或更准确地:

PPL=exp(1Ni=1NlogP(xix<i))\text{PPL} = \exp\left( -\frac{1}{N} \sum_{i=1}^{N} \log P(x_i | x_{<i}) \right)

直观理解

  • PPL = 10:模型在每个位置平均要在10个词中选择
  • PPL = 100:模型在每个位置平均要在100个词中选择
  • PPL越小,模型越确定,效果越好

典型值

模型验证集PPL
随机~50000(词表大小)
较差的模型~1000
中等模型~100
好的模型~10-20
人类~1-2(非常确定)

3. 学习率曲线

Learning Rate
  ^
  |      ___-----\___
  |     /            \___
  |    /                 \___
  |   /                      ---
  |  /
  +----------------------------> Step
   warmup   peak    cosine decay

4. 梯度范数

监控梯度的L2范数:

L=θ(Lθ)2\|\nabla L\| = \sqrt{\sum_{\theta} \left( \frac{\partial L}{\partial \theta} \right)^2}

正常范围:0.1 ~ 10

异常

  • 100:梯度爆炸

  • < 0.001:梯度消失

分布式训练

大模型通常无法在单个GPU上训练,需要分布式训练。

1. Data Parallel(数据并行)

思想:每个GPU有完整的模型副本,处理不同的数据

GPU 0: Model Copy → Batch 0
GPU 1: Model Copy → Batch 1
GPU 2: Model Copy → Batch 2
GPU 3: Model Copy → Batch 3
          ↓
    Average Gradients
          ↓
    Update All Copies

适用:中小型模型(能放进单GPU)

2. Model Parallel(模型并行)

思想:将模型拆分到多个GPU

Pipeline Parallelism

GPU 0: Layer 1-3   Batch 0
GPU 1: Layer 4-6   (wait)
GPU 2: Layer 7-9   (wait)
GPU 3: Layer 10-12  (wait)

     然后Batch 0传递到GPU 1  GPU 2  GPU 3

Tensor Parallelism

将单层的矩阵乘法拆分到多个GPU:

Y=XW=X[W1W2]=[XW1XW2]Y = X \cdot W = X \cdot [W_1 | W_2] = [X \cdot W_1 | X \cdot W_2]

适用:超大模型(单GPU放不下)

3. 混合并行

GPT-3、LLaMA等使用Data + Pipeline + Tensor并行的组合。

GPT-3 (175B)的训练

  • 使用10,000个GPU(NVIDIA V100)
  • Pipeline Parallelism:将96层分到多个GPU
  • Tensor Parallelism:将单层的矩阵分到多个GPU
  • Data Parallelism:在此基础上做数据并行

训练时间:数周到数月。

预训练的挑战

1. 计算资源

成本

  • GPT-3(175B):估计 $4.6M(训练一次)
  • LLaMA-65B:约 $2-3M
  • 需要数千个GPU,数周时间

趋势:成本不断上升,但单位成本在下降。

2. 数据质量

问题

  • 互联网数据质量参差不齐
  • 偏见、虚假信息
  • 版权问题

解决

  • 更好的数据清洗
  • 高质量数据集(Books、Papers)
  • 合成数据(用模型生成)

3. 训练稳定性

问题

  • Loss突然爆炸
  • 梯度消失/爆炸
  • 数值不稳定(NaN、Inf)

解决

  • 梯度裁剪
  • 更小的学习率
  • 混合精度训练(FP16 + FP32)
  • 更好的初始化

4. 评估困难

问题

  • 困惑度不能完全反映实际能力
  • 需要在多种任务上评估

解决

  • 构建评估基准(MMLU、HellaSwag等)
  • 人工评估
  • 对比不同模型

预训练后的模型

预训练完成后,得到一个基础模型(Base Model)

能力

具备的能力

  1. ✅ 语言理解:理解语法、语义
  2. ✅ 知识:从数据中学到的事实性知识
  3. ✅ 简单推理:因果、类比等
  4. ✅ 代码理解(如果训练数据包含代码)
  5. ✅ 多语言能力(如果训练数据包含多语言)

不具备的能力

  1. ❌ 指令遵循:不会按照用户指令行事
  2. ❌ 对话能力:可能输出不连贯的文本
  3. ❌ 安全性:可能输出有害内容
  4. ❌ 针对性任务能力:需要微调

使用方式

Base Model的典型行为

输入:"请写一首诗"

Base Model输出(续写模式):

请写一首诗,描述春天的美景。
请写一首诗,题目是《友谊》。
请写一首诗,用七言律诗的格式。
...

它只会续写,不会遵循指令

为什么?

Base Model学习的是"文本接龙",它看到"请写一首诗",会认为这是一个列表的开头,继续生成类似的句子。

解决方案:需要进行指令微调(Instruction Tuning),这是下一阶段的训练。

小结

  1. 预训练定义

    • 在大规模无标注数据上的自监督学习
    • 通过Next Token Prediction学习通用语言能力
  2. 核心任务

    • Next Token Prediction:P(xtx<t)P(x_t | x_{<t})
    • 因果语言模型(Causal LM)
    • 每个位置都是训练样本
  3. 预训练数据

    • 来源:网页、书籍、代码、论文、对话
    • 规模:万亿Token级别
    • 预处理:去重、过滤、清洗
  4. 训练过程

    • 大Batch Size(数百万Token)
    • 长序列(2048-8192 Token)
    • 数十万到数百万步
    • 分布式训练(数千GPU)
  5. 监控指标

    • Loss:交叉熵损失
    • Perplexity:困惑度
    • 学习率曲线
    • 梯度范数
  6. 挑战

    • 计算成本高(数百万美元)
    • 数据质量问题
    • 训练稳定性
    • 评估困难
  7. 预训练后的模型

    • 具备通用语言能力
    • 但不会指令遵循
    • 需要后训练(指令微调、RLHF)

预训练是大模型能力的基础,它让模型从随机参数变成一个"读过万卷书"的语言大师。但要让它成为一个好的对话助手,还需要后续的训练——这就是我们下一章要讲的内容。