预训练策略:从 GPT 到 Llama,大模型的"九年义务教育"

7934 字
40 分钟
预训练策略:从 GPT 到 Llama,大模型的"九年义务教育"

1. 引言:什么是预训练?#

1.1 一个生动的比喻#

如果说大模型是一个学生,那么:

监督学习: 老师(人工标注)→ 学生
少量数据、精准指导
像家教
预训练: 海量文本(互联网)→ 学生
大量数据、自主学习
像"阅读大量书籍自学"

预训练(Pre-training)是利用海量无标注文本让模型自我学习的过程。

1.2 预训练的核心思想#

海量无标注文本
┌─────────────────────────────┐
│ 语言建模任务 │
│ "预测下一个 token" │
└─────────────────────────────┘
基础模型 (Foundation Model)
具备通用语言能力
下游任务微调 (Fine-tuning)

关键洞察:通过预测下一个 token,模型被迫学会:

  • 语法
  • 语义
  • 世界知识
  • 推理能力
  • 上下文学习

1.3 预训练简史#

2017: Transformer (Vaswani et al.)
2018: GPT-1 (Radford et al.)
│ 首次展示预训练 + 微调的范式
2018: BERT (Devlin et al.)
│ 双向编码预训练
2019: GPT-2 (1.5B)
│ 零样本学习的曙光
2020: GPT-3 (175B)
│ In-context learning 涌现
2021: Switch Transformer (1.6T)
│ 稀疏 MoE
2022: Chinchilla (70B)
│ 提出 Chinchilla Scaling Law
2023: Llama 2 / GPT-4
│ 开源 + 闭源双轨发展
2024: Llama 3 (405B)
│ 更大规模,更优数据
2025+: 多模态预训练
+ RLHF 集成
+ 工具集成

1.4 预训练 vs 其他训练阶段#

训练阶段数据量计算量目标
预训练TB 级万卡·月学习通用能力
继续预训练GB 级百卡·天学习新领域
监督微调 (SFT)MB 级十卡·天学习任务格式
RLHFKB 级单卡·小时对齐人类偏好

类比:

阶段教育阶段数据规模
预训练9 年义务教育海量课本
继续预训练专业预科一些专著
SFT专业培训案例练习
RLHF行为矫正评价反馈

2. 预训练核心任务#

2.1 语言建模:基础任务#

最经典的任务:给定上文,预测下一个 token:

# 输入文本
text = "今天天气很好,我们去"
# 模型任务:预测下一个 token
# 可能: "公园", "吃饭", "散步", ...
# 训练目标
target = "公园"
# 损失函数
loss = CrossEntropy(prediction, target)

数学表示:

L(θ)=t=1TlogPθ(xtx<t)\mathcal{L}(\theta) = -\sum_{t=1}^{T} \log P_\theta(x_t | x_{<t})

即最大化所有 token 的对数似然。

2.2 因果语言建模 (Causal LM)#

GPT 系列使用的单向语言建模

# 输入
tokens = [今天, 天气, 很好, ,, 我们, 去]
# 因果注意力掩码
# 位置 i 只能看到位置 0..i-1
# 第 0 个 token: 看不到任何上下文
# 第 1 个 token (天气): 只能看到 [今天]
# 第 2 个 token (很好): 只能看到 [今天, 天气]
# ...

数学形式

P(x)=t=1TP(xtx1,x2,,xt1)P(x) = \prod_{t=1}^{T} P(x_t | x_1, x_2, \ldots, x_{t-1})

2.3 掩码语言建模 (Masked LM)#

BERT 使用的双向语言建模

# 输入
tokens = [今天, 天气, [MASK], ,, 我们, 去]
# 模型任务:预测 [MASK] 处是什么
# 期望输出: "很好"
# 双向注意力:可以看到两侧

特点

  • ✓ 充分利用上下文
  • ✗ 不能直接用于生成

2.4 多种预训练目标#

class PretrainingObjectives:
"""预训练目标集合"""
CAUSAL_LM = "clm" # 因果 LM
MASKED_LM = "mlm" # 掩码 LM
PREFIX_LM = "prefix_lm" # 前缀 LM
SPAN_MASKING = "span_mask" # 跨度掩码
NEXT_SENTENCE = "nsp" # 下一句预测
SENTENCE_ORDER = "sop" # 句子顺序
REPLACEMENT = "RTD" # 替换检测 (ELECTRA)
CONTRASTIVE = "contrastive" # 对比学习
# 不同模型采用不同组合
MODELS_OBJECTIVES = {
'GPT': ['clm'],
'BERT': ['mlm', 'nsp'],
'T5': ['span_mask'],
'ELECTRA': ['RTD'],
'XLNet': ['permutation_lm'],
'Llama': ['clm'],
}

2.5 前缀语言建模 (Prefix LM)#

混合 CLM 和 MLM 的优点的方案:

输入: [用户: 今天天气]
[助手: 很好,我们去]
训练目标:
- 用户部分: 使用双向注意力
- 助手部分: 使用因果注意力
- 整体可以同时看到用户上下文
# 前缀 LM 掩码
# [用户文本 ] [助手文本 ]
# 双向 + 因果 因果
# ◀───▶ ◀──

在 PaLM、UL2 等模型中广泛使用。

2.6 去噪自编码 (Denoising AE)#

T5 系列的预训练方式:

# 原始句子
"今天天气很好,我们去公园散步"
# 加入噪声(随机打乱、删除等)
"今天 [MASK] [MASK],我们去 [MASK] 散步"
# 模型: 重建完整句子
"今天天气很好,我们去公园散步"

优势:双向注意力 + 自然的下游任务格式。

3. Tokenization:预训练的第一步#

3.1 为什么需要 Tokenization?#

神经网络无法直接处理文本,需要切分成 token:

"今天天气很好"
字符级: [今, 天, 天, 气, 很, 好] (6 tokens)
词语级: [今天, 天气, 很, 好] (4 tokens, 需要中文分词)
BPE: [今, 天, 天, 气, 很, 好] (6 subwords)

3.2 BPE 算法#

字节对编码 (Byte Pair Encoding)

def bpe_train(corpus, vocab_size):
"""训练 BPE"""
# 1. 初始化: 字符集合
vocab = set(char for text in corpus for char in text)
while len(vocab) < vocab_size:
# 2. 统计相邻 token 对的频率
pairs = count_pairs(corpus)
# 3. 找最频繁的对
most_frequent = max(pairs, key=pairs.get)
# 4. 合并
merge_pair(most_frequent, corpus)
vocab.add(most_frequent)
return vocab
# 示例:
# 初始: 今天 天气 很 好
# 统计: (今天, 天气) 出现 100 次最多
# 合并: 今天天气 → 一个 token
# 继续: 看 (今天天气, 很好) ...

3.3 现代 Tokenizer 工具#

3.3.1 SentencePiece#

Google 开发,主流选择
- 支持 BPE / Unigram
- 多语言友好
- Llama、PaLM、Falcon 使用

3.3.2 tiktoken#

OpenAI 开发
- 高性能
- GPT-3.5/4 使用
- 50x faster than alternatives

3.3.3 HuggingFace Tokenizers#

- 支持 BPE, WordPiece, Unigram
- 多语言
- 易于集成

3.4 Tokenizer 选型#

# 不同模型的 tokenizer
TOKENIZERS = {
'GPT-4': 'cl100k_base (tiktoken) - 100K vocab',
'GPT-3': 'BPE - 50K vocab',
'Llama': 'SentencePiece BPE - 32K vocab',
'Llama 3': 'SentencePiece BPE - 128K vocab',
'BERT': 'WordPiece - 30K vocab',
'T5': 'SentencePiece - 32K vocab',
'Falcon': 'BPE - 65K vocab',
'Mistral': 'BPE - 32K vocab',
'Claude': 'BPE - ~100K vocab',
}

3.5 中文 Tokenization 挑战#

# 挑战 1: 中文字符多
# 基础汉字 ~3500, 常用 ~7000, 大字符集 > 10 万
# 挑战 2: 分词歧义
"乒乓球拍卖完了" → 乒乓球拍卖 / 完了 ?
"喜欢吃 / 苹果" vs "喜欢 / 吃苹果"
# 解决方案:
# 1. 增加中文训练数据
# 2. 使用更大 vocab (32K → 64K)
# 3. 字符级别的 fallback
# 4. 专门的 Chinese-aware tokenizer

3.6 多语言 Tokenization#

class MultilingualTokenizer:
"""多语言 tokenizer"""
def __init__(self, vocab_size=100000):
# 让高频语言共享 token
# 低频语言用较长表示
self.vocab_size = vocab_size
def token_efficiency(self, text, lang):
"""不同语言的 token 效率"""
tokens = self.encode(text)
compression_ratio = len(tokens) / len(text)
# 高资源语言(英语): 1 token ≈ 4 字符
# 低资源语言(冰岛语): 1 token ≈ 1 字符
return compression_ratio

4. 数据工程#

4.1 数据的重要性#

“在机器学习中,垃圾进,垃圾出。最关键的资产是数据,而非模型。”

Llama 2 论文的关键发现:

  • 数据质量 > 数据量
  • 数据多样性 > 单一来源
  • 数据清洗 > 原始数据

4.2 数据规模#

模型规模 训练 token 数
─────────────────────────────────
GPT-2 (1.5B) ~10B
GPT-3 (175B) ~300B
GLaM (1.2T) ~1.5T
PaLM (540B) ~780B
Chinchilla (70B) ~1.4T (按 Chinchilla Law)
Llama 1 (65B) ~1.4T
Llama 2 (70B) ~2T
Llama 3 (405B) ~15.6T

4.3 数据来源#

DATA_SOURCES = {
'CommonCrawl': {
'size': '~250B 网页 (清洗后)',
'quality': '中',
'使用率': '~70%',
},
'GitHub': {
'size': '~50B 行代码',
'quality': '高(需要过滤)',
'使用率': '~10% (Llama)',
},
'Books': {
'size': '~10B books',
'quality': '高',
'使用率': '~5% (Books3, Project Gutenberg)',
},
'Wikipedia': {
'size': '~20M articles',
'quality': '极高',
'使用率': '~5%',
},
'Papers': {
'size': '~10M papers',
'quality': '高',
'使用率': '~5% (arXiv, PubMed)',
},
'问答/论坛': {
'size': '~10B',
'quality': '中',
'使用率': '~10% (StackExchange)',
}
}

4.4 数据配比#

# Llama 1 的数据配比
DATA_MIX = {
'CommonCrawl': 67.0,
'C4': 15.0,
'GitHub': 4.5,
'Wikipedia': 4.5,
'Books': 4.5,
'StackExchange': 2.0,
'其他': 2.5,
}
# Llama 2 调整为:
LLAMA2_MIX = {
'CommonCrawl': 60+,
'高质量来源': ~40%,
# 高质量来源占比增加
}
# 关键: 在训练过程中可能调整配比

4.5 数据清洗流程#

原始数据
[1] HTML/格式清洗(去标签、提内容)
[2] 语言识别(filter_english, fasttext)
[3] 质量过滤(启发式规则 + 模型评分)
├──▶ 去重(文档级、段落级)
[4] NSFW 过滤
[5] PII(个人身份信息)移除
[6] 数据增强/标准化
最终训练数据

4.6 数据去重#

class Deduplicator:
"""数据去重"""
def exact_dedup(self, documents):
"""精确去重"""
seen = set()
unique = []
for doc in documents:
if doc.hash not in seen:
seen.add(doc.hash)
unique.append(doc)
return unique
def fuzzy_dedup(self, documents, threshold=0.85):
"""模糊去重(基于 MinHash)"""
# MinHash 用于估计 Jaccard 相似度
unique = []
for doc in documents:
is_dup = False
for existing in unique:
similarity = self._minhash_similarity(doc, existing)
if similarity > threshold:
is_dup = True
break
if not is_dup:
unique.append(doc)
return unique

4.7 数据质量评分#

class QualityScorer:
"""基于模型的文档质量评分"""
def __init__(self, quality_model):
self.model = quality_model
def score(self, documents):
"""使用 kenlm / fastText 等打分"""
scores = []
for doc in documents:
# kenlm 困惑度(越低越好)
perplexity = self.model.perplexity(doc.text)
# 文档长度
length = len(doc.text.split())
# 符号比例
symbol_ratio = self._symbol_ratio(doc.text)
# 综合评分
score = self._compute_score(perplexity, length, symbol_ratio)
scores.append(score)
return scores

4.8 数据污染检测#

class ContaminationDetector:
"""检测训练数据与测试集的重叠"""
def detect(self, train_doc, test_questions):
"""检测污染"""
contaminated = []
for q in test_questions:
# 1. n-gram 重叠检测
overlap = self._ngram_overlap(train_doc, q)
if overlap > 0.5:
contaminated.append(q)
continue
# 2. 语义相似度
sim = self._semantic_similarity(train_doc, q)
if sim > 0.8:
contaminated.append(q)
return contaminated

5. 模型架构选择#

5.1 Transformer 变体#

class TransformerArchitectures:
"""Transformer 架构变体"""
DECODER_ONLY = {
'name': 'Decoder-Only',
'代表': 'GPT, Llama, Mistral',
'优势': '适合生成任务',
'劣势': '不能同时看到完整上下文',
'应用': '大多数现代 LLM',
}
ENCODER_ONLY = {
'name': 'Encoder-Only',
'代表': 'BERT, RoBERTa',
'优势': '双向注意力',
'劣势': '不适合生成长文本',
'应用': '分类、检索',
}
ENCODER_DECODER = {
'name': 'Encoder-Decoder',
'代表': 'T5, BART, FLAN-T5',
'优势': '输入输出分离',
'劣势': '复杂、参数利用率低',
'应用': '翻译、摘要',
}

5.2 现代 Decoder 改进#

5.2.1 RMSNorm#

# 替代 LayerNorm
class RMSNorm(nn.Module):
def __init__(self, dim, eps=1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
rms = torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
return x * rms * self.weight

5.2.2 SwiGLU Activation#

# Llama 使用的激活函数
class SwiGLU(nn.Module):
def forward(self, x):
x, gate = x.chunk(2, dim=-1)
return F.silu(gate) * x
# 在 FFN 中
class SwiGLU_FFN(nn.Module):
def __init__(self, dim, hidden_dim):
super().__init__()
# 注意:hidden_dim 是 2/3 维度以补偿门控
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w3 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
def forward(self, x):
return self.w2(F.silu(self.w1(x)) * self.w3(x))

5.2.3 RoPE 旋转位置编码#

RoPE(x,m)=xeimθ\text{RoPE}(x, m) = x \cdot e^{im\theta}
# Llama 等模型使用
def apply_rope(x, cos, sin):
# x: [..., seq_len, dim]
# cos, sin: [seq_len, dim]
x1, x2 = x[..., 0::2], x[..., 1::2]
rotated = torch.cat([
x1 * cos - x2 * sin,
x2 * cos + x1 * sin
], dim=-1)
return rotated
# 优势: 相对位置、外推到长序列

5.2.4 GQA 分组查询注意力#

# Multi-Head Attention (MHA)
class MHA:
"""标准多头注意力"""
def __init__(self, n_heads):
self.n_heads = n_heads
# 每个 head 都有自己的 K, V
# Grouped Query Attention (GQA)
class GQA:
"""分组查询注意力 - Llama 2 70B 等使用"""
def __init__(self, n_heads, n_kv_heads):
# n_heads 个 Q head
# 但只有 n_kv_heads 个 KV head
# 多个 Q head 共享 K, V
pass

GQA 优势

  • 减少 KV cache 内存
  • 加快推理
  • 性能接近 MHA

5.2.5 Flash Attention#

# IO 感知的注意力计算
# 传统 attention 需要 O(N²) 内存来存储注意力矩阵
# Flash Attention: 不存储矩阵,分块计算
class FlashAttention:
"""硬件优化的注意力"""
def forward(self, Q, K, V):
# 分块计算,避免存储完整注意力矩阵
# 复杂度 O(N²) 计算,O(N) 内存
# 利用 GPU 的 SRAM 加速
...

5.3 Llama 架构详解#

class LlamaBlock(nn.Module):
"""Llama 块"""
def __init__(self, dim, n_heads, n_kv_heads=None, hidden_dim=None):
super().__init__()
n_kv_heads = n_kv_heads or n_heads
hidden_dim = hidden_dim or 4 * dim
# 1. RMSNorm
self.attention_norm = RMSNorm(dim)
# 2. Self-Attention (GQA + RoPE)
self.attention = Attention(dim, n_heads, n_kv_heads)
# 3. RMSNorm
self.ffn_norm = RMSNorm(dim)
# 4. SwiGLU FFN
self.ffn = SwiGLU_FFN(dim, int(2/3 * 4 * dim))
def forward(self, x):
# 残差连接 + Pre-Norm
x = x + self.attention(self.attention_norm(x))
x = x + self.ffn(self.ffn_norm(x))
return x
class LlamaModel(nn.Module):
"""完整 Llama 模型"""
def __init__(self, vocab_size, dim, n_layers, n_heads, n_kv_heads=None):
super().__init__()
# 1. Token 嵌入
self.embed_tokens = nn.Embedding(vocab_size, dim)
# 2. Rotary Embeddings
self.rotary = RotaryEmbedding(dim)
# 3. N 个 Transformer Block
self.layers = nn.ModuleList([
LlamaBlock(dim, n_heads, n_kv_heads)
for _ in range(n_layers)
])
# 4. 最终 Norm + 输出
self.norm = RMSNorm(dim)
self.lm_head = nn.Linear(dim, vocab_size, bias=False)
# 5. 权重共享
self.lm_head.weight = self.embed_tokens.weight
def forward(self, input_ids):
x = self.embed_tokens(input_ids)
for layer in self.layers:
x = layer(x, self.rotary)
x = self.norm(x)
return self.lm_head(x)

5.4 关键超参数#

LLAMA_CONFIGS = {
'Llama-7B': {
'dim': 4096,
'n_layers': 32,
'n_heads': 32,
'vocab_size': 32000,
},
'Llama-13B': {
'dim': 5120,
'n_layers': 40,
'n_heads': 40,
},
'Llama-65B': {
'dim': 8192,
'n_layers': 80,
'n_heads': 64,
},
'Llama-3-8B': {
'dim': 4096,
'n_layers': 32,
'n_heads': 32,
'vocab_size': 128000,
},
'Llama-3-405B': {
'dim': 16384,
'n_layers': 126,
'vocab_size': 128000,
},
}

6. Scaling Laws:规模定律#

6.1 Chinchilla 定律#

DeepMind 的核心发现:

L(N,D)=E+ANα+BDβL(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}

其中:

  • LL:损失
  • NN:参数数量
  • DD:训练 token 数
  • E,A,B,α,βE, A, B, \alpha, \beta:拟合常数

核心结论

  • 模型和 token 应该同比增长
  • 最优比例约为 每个参数 20 个 token
模型大小 最优 token 数
──────────────────────────────────
7B 140B
13B 260B
70B 1.4T
175B 3.5T

6.2 Scaling Law 实证#

# Kaplan 2020 早期发现
KAPLAN_LAW = """
固定计算预算下:
- 大模型 + 少数据 比 小模型 + 多数据 好
- 但只看模型大小是不够的
"""
# Chinchilla 2022 修正
CHINCHILLA_LAW = """
固定计算预算下:
- 模型 和 数据 应该同比增长
- 最优比例 ≈ 20 token/参数
"""
# Llama 3 (2024) 实践
LLAMA3_APPROACH = """
训练了 405B 模型,超 15T tokens
虽然 Chinchilla 说应该 8T tokens
但高数据量在下游表现更好
"""

6.3 Compute-Optimal Frontier#

# 给出固定的计算预算,最优的 (模型大小, 数据量)
def optimal_compute_budget(compute_budget):
"""计算最优配置"""
# Chinchilla 公式
# C ≈ 6 N D
# L 是 N 和 D 的函数
# 近似计算
a, b = 0.34, 0.28
A = 406.4
B = 410.7
E = 1.69
# 由 C = 6ND 给出 N 和 D
# 假设 C = compute_budget
C = compute_budget
# 数值求解
N = (C / (6 * 20)) ** 0.5 # 20 tokens/param
D = 20 * N
return N, D

6.4 涌现能力 (Emergent Abilities)#

某些能力只在规模达到一定程度时出现:
规模门槛
─────────────────────────────────
~10B: 基本推理
~70B: 复杂推理
~100B+: In-context learning 显著涌现
~500B+: 多步推理、CoT
EMERGENT_ABILITIES = {
'in_context_learning': '规模 > 数十亿时涌现',
'chain_of_thought': '规模 > 100B 时可用',
'instruction_following': '主要靠 SFT 和 RLHF',
'reasoning': '大规模时显著提升',
}

6.5 Scaling 实践#

class ScalingExperiment:
"""Scaling 实验管理"""
def __init__(self):
self.results = []
def run(self, model_sizes, compute_budgets):
"""运行 Scaling 曲线"""
for compute in compute_budgets:
for size in model_sizes:
# 训练模型
model = train_model(size, compute)
# 评估
loss = evaluate(model)
self.results.append({
'compute': compute,
'model_size': size,
'loss': loss,
})
# 拟合 Scaling Law
params = fit_scaling_law(self.results)
return params

7. 训练基础设施#

7.1 分布式训练架构#

┌──────────────────────────────────────────────────────────────┐
│ LLM 训练集群 │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ZeRO / FSDP (内存优化) │ │
│ │ Model + Optimizer states 分片到所有 GPU │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tensor Parallel (张量并行) │ │
│ │ 单层切分到 8/16 个 GPU │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Pipeline Parallel (流水线并行) │ │
│ │ 不同层在不同的 GPU 组上 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Data Parallel (数据并行) │ │
│ │ 不同的数据批次到不同 GPU │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 节点间: InfiniBand / NVLink │ │
│ │ 节点内: NVLink / PCIe │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘

7.2 数据并行 (DP)#

class DataParallel:
"""数据并行"""
def setup(self, model, n_gpus=8):
# 复制模型到每个 GPU
replicas = [copy(model) for _ in range(n_gpus)]
# 每个 GPU 处理不同的数据批次
# 梯度同步(all-reduce)
def step(self, batch_id, gpu_id):
# 在 GPU gpu_id 上处理批次 batch_id
loss = replicas[gpu_id].forward(batch[batch_id])
loss.backward()
# 同步梯度
all_reduce(gradients)
# 同步更新
replicas[gpu_id].step()

问题:每个 GPU 都存完整模型,不切分

7.3 模型并行 (MP)#

7.3.1 张量并行 (TP)#

class TensorParallel:
"""张量并行"""
def parallelize_linear(self, layer, n_gpus):
"""切分线性层"""
# Column Parallel: 切分输出维度
# 假设 output_dim = 1024, n_gpus = 4
# 每个 GPU 处理 256 维
# Row Parallel: 切分输入维度
pass
def forward(self, x):
# 每个 GPU 独立计算自己的部分
# 通过 all-reduce 整合结果
...
# Megatron-LM 风格的实现
class MegatronLinear(nn.Linear):
def __init__(self, in_features, out_features, world_size, rank):
# Column-parallel: 切分输出
self.out_features_per_partition = out_features // world_size
super().__init__(in_features, self.out_features_per_partition)
self.weight.data = self.weight.data.chunk(world_size, dim=0)[rank]

7.3.2 流水线并行 (PP)#

class PipelineParallel:
"""流水线并行"""
def __init__(self, model, n_stages=4, n_microbatches=8):
self.n_stages = n_stages
self.n_microbatches = n_microbatches
# 将模型分为 n_stages 个阶段
# 每个阶段是一个连续的子层
self.stages = self._split_model(model, n_stages)
def forward(self, x):
# 将 micro-batch 流水线化
# 阶段 1 处理 micro-batch 1
# 阶段 2 处理 micro-batch 1, 阶段 1 处理 micro-batch 2
# ...
...

7.4 内存优化#

7.4.1 ZeRO#

class ZeROOptimizer:
"""ZeRO 优化器"""
def __init__(self, stage=3):
# Stage 1: Optimizer states 分片
# Stage 2: + Gradients 分片
# Stage 3: + Parameters 分片
if stage >= 1:
self._shard_optimizer_states()
if stage >= 2:
self._shard_gradients()
if stage >= 3:
self._shard_parameters()

7.4.2 FSDP#

class FSDPModel:
"""Fully Sharded Data Parallel"""
def setup(self, model):
# 类似 ZeRO-3
# 模型参数完全分片到所有 GPU
# Forward/Backward 时才 all-gather
...

7.5 混合精度训练#

class MixedPrecisionTraining:
"""混合精度训练"""
def __init__(self, model, optimizer):
self.model = model
self.optimizer = optimizer
self.scaler = GradScaler()
def step(self, batch):
# 1. 前向 (autocast FP16/BF16)
with autocast():
output = self.model(batch)
loss = compute_loss(output)
# 2. 反向
self.scaler.scale(loss).backward()
# 3. 优化器步骤
self.scaler.step(self.optimizer)
self.scaler.update()

精度选择

  • FP32:高精度但慢、内存大
  • FP16:加速但易溢出
  • BF16:与 FP32 相同范围,适合训练 ⭐

7.6 梯度累积与检查点#

class GradientAccumulation:
"""梯度累积"""
def __init__(self, accumulation_steps=8):
self.accumulation_steps = accumulation_steps
def step(self, batch_id, total_batches):
# 等效 batch_size = accumulation_steps * micro_batch_size
loss = model_forward(batch)
loss = loss / self.accumulation_steps
loss.backward()
if (batch_id + 1) % self.accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
class GradientCheckpointing:
"""梯度检查点"""
def forward(self, x):
# 不保存中间激活值
# 反向时重新计算
with checkpoint:
return self._forward(x)

7.7 实际集群配置#

Llama 3 训练:
─────────────────────────────────
GPU: 16,000 + NVIDIA H100
内存: 80GB/GPU
Interconnect: NVLink + InfiniBand
存储: FUSE + 数百 PB
训练时间: ~30 天
功耗: 数兆瓦
GPT-4 据传:
─────────────────────────────────
GPU: 25,000 + NVIDIA A100/H100
训练时间: ~3-6 个月
成本: $100M+

8. 优化器和学习率#

8.1 AdamW 优化器#

class AdamW:
"""AdamW - 主流 LLM 优化器"""
def __init__(self, params, lr=1e-4, betas=(0.9, 0.95), weight_decay=0.1):
self.params = params
self.lr = lr
self.beta1, self.beta2 = betas
self.weight_decay = weight_decay
# 一阶、二阶动量
self.m = [torch.zeros_like(p) for p in params]
self.v = [torch.zeros_like(p) for p in params]
def step(self):
for i, p in enumerate(self.params):
# 权重衰减(解耦)
p.data.mul_(1 - self.lr * self.weight_decay)
# 梯度
g = p.grad
# 动量更新
self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
self.v[i] = self.beta2 * self.v[i] + (1 - self.beta2) * g ** 2
# 偏差修正
m_hat = self.m[i] / (1 - self.beta1 ** self.t)
v_hat = self.v[i] / (1 - self.beta2 ** self.t)
# 参数更新
p.data -= self.lr * m_hat / (torch.sqrt(v_hat) + self.eps)

8.2 学习率调度#

8.2.1 Cosine Schedule#

def cosine_schedule(step, warmup_steps, total_steps, max_lr, min_lr=0):
"""Cosine 学习率调度"""
if step < warmup_steps:
# Warmup 阶段
return max_lr * step / warmup_steps
# Cosine 退火
progress = (step - warmup_steps) / (total_steps - warmup_steps)
return min_lr + 0.5 * (max_lr - min_lr) * (1 + math.cos(math.pi * progress))
# Llama 使用
def llama_schedule(step, warmup=2000, max_lr=3e-4, total=100000, min_lr=3e-5):
return cosine_schedule(step, warmup, total, max_lr, min_lr)

8.2.2 WSD (Warmup-Stable-Decay)#

def wsd_schedule(step, warmup, total, max_lr):
"""WSD 调度"""
# 1. Warmup 阶段
if step < warmup:
return max_lr * step / warmup
# 2. 稳定阶段(保持 max_lr)
if step < total * 0.8:
return max_lr
# 3. 衰减阶段
decay_start = int(total * 0.8)
progress = (step - decay_start) / (total - decay_start)
return max_lr * 0.5 * (1 + math.cos(math.pi * progress))

8.3 关键超参数#

TYPICAL_HYPERPARAMETERS = """
模型规模 峰值 LR Batch Size Warmup
───────────────────────────────────────────────
0.1B - 1B 3e-4 512 - 2M 2000-5000 steps
1B - 10B 1e-4 1M - 4M 2000-5000 steps
10B - 100B 6e-5 4M - 16M 5000 steps
100B+ 3e-5 16M+ 5000+ steps
"""

9. 训练目标与损失函数#

9.1 交叉熵损失#

def cross_entropy_loss(logits, targets):
"""预训练的标准损失"""
# logits: [batch, seq_len, vocab_size]
# targets: [batch, seq_len]
# Shift: 预测下一个 token
shift_logits = logits[..., :-1, :] # 去掉最后一个
shift_targets = targets[..., 1:] # 去掉第一个
# 交叉熵
loss = F.cross_entropy(
shift_logits.reshape(-1, shift_logits.size(-1)),
shift_targets.reshape(-1)
)
return loss

9.2 Loss 的多种解释#

L=1Ni=1NlogP(xix<i)\mathcal{L} = -\frac{1}{N} \sum_{i=1}^{N} \log P(x_i | x_{<i})

等价于:

  1. 困惑度 (Perplexity) 最小化
  2. 压缩 (Compression) 优化
  3. 预测准确率最大化

9.3 损失掩码#

def compute_loss_with_masking(logits, targets, mask):
"""部分 token 的损失计算"""
# 例如: 不对填充 token 计算损失
loss = F.cross_entropy(logits, targets, reduction='none')
loss = (loss * mask).sum() / mask.sum()
return loss

9.4 多任务预训练#

class MultiTaskPretraining:
"""多任务预训练目标"""
def compute_loss(self, batch):
task_type = batch['task']
if task_type == 'causal_lm':
return self.causal_lm_loss(batch)
elif task_type == 'masked_lm':
return self.masked_lm_loss(batch)
elif task_type == 'span_mask':
return self.span_mask_loss(batch)
elif task_type == 'classification':
return self.classification_loss(batch)
# ...

9.5 辅助损失#

# 一些模型添加辅助损失:
AUX_LOSSES = {
'load_balancing': 'MoE 模型的负载均衡',
'routing': '路由稳定性',
'contrastive': '对比学习',
}
# Llama 等稠密模型: 没有辅助损失

10. 训练稳定性#

10.1 Loss Spike#

训练中可能出现 loss 突然飙升:

class SpikeHandler:
"""Loss spike 处理"""
def __init__(self, spike_threshold=20, cooldown_steps=10):
self.spike_threshold = spike_threshold
self.cooldown_steps = cooldown_steps
def check(self, current_loss, previous_loss):
if current_loss > previous_loss * self.spike_threshold:
return True
return False
def handle(self):
# 跳过最近的梯度更新
# 减小学习率
# 在 cooldown 期间
...

10.2 梯度裁剪#

def clip_grad_norm(parameters, max_norm=1.0):
"""梯度裁剪"""
total_norm = 0
for p in parameters:
if p.grad is not None:
param_norm = p.grad.data.norm(2)
total_norm += param_norm.item() ** 2
total_norm = total_norm ** 0.5
# 缩放梯度
clip_coef = max_norm / (total_norm + 1e-6)
if clip_coef < 1:
for p in parameters:
if p.grad is not None:
p.grad.data.mul_(clip_coef)
return total_norm

10.3 初始化#

# 标准初始化
def init_weights(model):
"""标准初始化策略"""
for name, param in model.named_parameters():
if 'weight' in name:
if 'embed' in name:
# 嵌入层: 标准初始化
nn.init.normal_(param, mean=0, std=0.02)
elif 'norm' in name:
# Norm 层: 1
nn.init.ones_(param)
elif 'lm_head' in name:
# 输出层: 标准初始化
nn.init.normal_(param, mean=0, std=0.02)
else:
# 其他: 缩放初始化
std = 0.02 / math.sqrt(2 * n_layers)
nn.init.normal_(param, mean=0, std=std)
elif 'bias' in name:
nn.init.zeros_(param)

10.4 监控指标#

class TrainingMonitor:
"""训练监控"""
METRICS = [
'loss',
'grad_norm',
'learning_rate',
'tokens_per_second',
'mfu', # Model FLOPs Utilization
'memory_used',
'throughput',
]
def log(self, step, metrics):
"""记录每步"""
# 使用 wandb / tensorboard
wandb.log({k: v for k, v in metrics.items()}, step=step)
def check_health(self, metrics):
"""健康检查"""
if metrics['grad_norm'] > 100:
return 'gradient_explosion'
if metrics['grad_norm'] < 0.001:
return 'vanishing_gradients'
if metrics['loss'] > 20:
return 'loss_spike'
if metrics['mfu'] < 0.3:
return 'low_hardware_utilization'
return 'healthy'

10.5 MFU: Model FLOPs Utilization#

def calculate_mfu(model, batch_size, seq_len, step_time):
"""MFU: 模型利用硬件算力的效率"""
# 理论 FLOPs
n_params = sum(p.numel() for p in model.parameters())
forward_flops = 6 * n_params * batch_size * seq_len # 6 = 3(forward/backward)+ 3(multiplications)
# 实际吞吐
achieved_flops = forward_flops / step_time
# GPU 理论算力
gpu_peak = 312e12 # H100: 312 TFLOPS
# MFU
mfu = achieved_flops / gpu_peak
return mfu
# 良好的 MFU: 40% - 60%
# 当前最优: 50-60%

11. 评估预训练效果#

11.1 训练中的评估#

class PretrainingEvaluation:
"""训练中评估"""
EVAL_TASKS = [
'validation_loss', # 验证集损失
'hellaswag', # 常识推理
'arc_challenge', # 科学问答
'mmlu', # 多任务理解
'humaneval', # 代码生成
'gsm8k', # 数学
'truthfulqa', # 真实性
'winogrande', # 常识
]
def evaluate(self, model, eval_tasks):
results = {}
for task in eval_tasks:
score = self._run_eval(model, task)
results[task] = score
return results

11.2 评估基准对照#

BENCHMARK_RESULTS = {
'Llama-3-8B': {
'MMLU': 68.4,
'HumanEval': 33.5,
'GSM8K': 50.0,
'HellaSwag': 80.0,
},
'Llama-3-70B': {
'MMLU': 79.3,
'HumanEval': 70.2,
'GSM8K': 93.0,
'HellaSwag': 88.0,
},
'Llama-3-405B': {
'MMLU': 88.6,
'HumanEval': 89.0,
'GSM8K': 96.8,
},
}

11.3 训练 Loss 曲线分析#

LOSS_CURVE_INTERPRETATION = """
观察训练曲线:
1. 健康曲线:
- 平滑下降
- Train loss 与 val loss 都下降
- 没有 spike
2. 过拟合:
- Train loss 下降, val loss 上升
- 减少训练时间
- 或增加数据
3. 欠拟合:
- Loss 平稳但较高
- 增加模型大小
- 或改进架构
4. Loss spike:
- 突然飙升
- 通常降低 LR
- 或回滚参数
"""

12. 高级训练策略#

12.1 课程学习 (Curriculum Learning)#

class CurriculumScheduler:
"""课程学习"""
def __init__(self):
self.stages = [
# 阶段 1: 简单文本
{'difficulty': 0, 'data': 'simple_wikipedia'},
# 阶段 2: 中等
{'difficulty': 0.3, 'data': 'mixed'},
# 阶段 3: 复杂
{'difficulty': 0.7, 'data': 'full'},
]
def get_data(self, progress):
"""根据训练进度返回数据"""
stage_idx = int(progress * len(self.stages))
stage_idx = min(stage_idx, len(self.stages) - 1)
return self.stages[stage_idx]

12.2 数据调度#

class DataSchedule:
"""训练过程中的数据配比变化"""
def get_mix(self, step):
"""根据训练步数返回数据配比"""
if step < 100000:
# 早期: 多用高质量
return {'高质量': 0.7, 'CommonCrawl': 0.3}
else:
# 中后期: 增加数量
return {'高质量': 0.4, 'CommonCrawl': 0.6}

12.3 MoE 训练 (Sparse Upcycling)#

class MoEUpcycling:
"""从稠密模型升级到 MoE"""
def upcycle(self, dense_model, n_experts=8):
# 1. 复制 FFN 为多个专家
for layer in dense_model.layers:
layer.ffn_experts = nn.ModuleList([
copy(layer.ffn) for _ in range(n_experts)
])
# 2. 添加路由器
for layer in dense_model.layers:
layer.router = nn.Linear(dim, n_experts)
# 3. 继续训练
# MoE 通常比稠密模型训练效果更好

12.4 退火训练 (Annealing)#

class AnnealingPhase:
"""训练末期的退火阶段"""
def __init__(self, anneal_steps=2000, anneal_lr=1e-5):
self.anneal_steps = anneal_steps
self.anneal_lr = anneal_lr
def anneal(self, model, data_loader):
# 1. 使用小学习率持续训练
# 2. 使用最高质量数据
# 3. 通常 0.1% - 1% 的总训练量
for step in range(self.anneal_steps):
# 取最高质量的数据
batch = self.high_quality_loader.next()
# 低学习率
lr = self._anneal_lr(step)
# 训练一步
loss = model.step(batch, lr=lr)

13. 持续预训练#

13.1 什么时候需要继续预训练?#

CONTINUE_TRAINING_REASONS = {
'new_domain': '需要专业领域知识(医学、法律)',
'new_language': '增加新语言支持',
'data_update': '知识需要更新到最新',
'capability_boost': '增强特定能力(代码、数学)',
'recovery': '修复某个能力退化',
}

13.2 继续预训练的实现#

class ContinualPretraining:
"""继续预训练"""
def __init__(self, base_model, new_data):
self.model = base_model
self.new_data = new_data
def train(self):
# 1. 加载原模型
# 2. 准备新数据(domain-specific)
# 3. 使用较小学习率继续训练
# 4. 监控 catastrophic forgetting
# 学习率通常很小(1/10 of original)
lr = 6e-5 # vs original 6e-4
# 混合新旧数据避免遗忘
old_data_ratio = 0.5 # 50% 原始数据

13.3 Catastrophic Forgetting#

class ForgettingPreventer:
"""防止灾难性遗忘"""
def mix_data(self, new_data, old_data, ratio=0.7):
"""混合新旧数据"""
mixed = []
for i in range(self.num_batches):
if random.random() < ratio:
# 70% 旧数据(防遗忘)
mixed.append(next(old_data))
else:
# 30% 新数据(学新知识)
mixed.append(next(new_data))
return mixed
def elastic_weight_consolidation(self):
"""EWC: 保护重要参数"""
# 计算参数重要性 (Fisher 信息)
# 限制重要参数的变化
...

14. 高效训练技术#

14.1 Flash Attention#

class FlashAttention:
"""Flash Attention - 内存高效注意力"""
def forward(self, Q, K, V):
# 1. 将 Q, K, V 分块到 GPU SRAM
# 2. 在 SRAM 中计算注意力
# 3. 不需要存储中间注意力矩阵
# 复杂度: O(N²) 计算, O(N) 内存
# 优势:
# - 长序列支持 (32K, 128K)
# - 计算更快
# - 内存更少
...

版本演进

  • v1: 基础实现
  • v2: 进一步优化,并行性
  • v3: 支持 FP16 低精度

14.2 Ring Attention#

class RingAttention:
"""环形注意力 - 跨设备长序列"""
def forward(self, Q, K, V, devices):
# Q 在 device 0, K,V 分片到所有设备
# 环形通信计算注意力
# 支持超长上下文(百万级 token)
...

14.3 Sequence Parallelism#

class SequenceParallel:
"""序列并行 - 长序列训练"""
def forward(self, sequence):
# 沿序列维度分片到不同设备
# 每台设备处理序列的一部分
# 通过 all-gather 整合
...

14.4 Activation Recomputation#

class ActivationRecomputation:
"""激活重计算"""
def forward(self, x):
# 不保存中间激活值
# 反向时重新计算
# 节省内存,代价是 ~30% 计算量
with checkpoint(self.layer):
return self.layer(x)

14.5 8-bit 优化器#

class BitsAndBytesOptimizer:
"""8-bit 优化器 (bitsandbytes)"""
def __init__(self):
# AdamW 状态用 8-bit 存储
# 节省 75% 优化器内存
# 几乎不影响性能
pass

14.6 CPU Offloading#

class CPUOffloader:
"""CPU 卸载"""
def __init__(self):
# 优化器状态卸载到 CPU
# GPU 内存不够时使用
# 通信开销较大
pass

15. 成本估算#

15.1 训练成本#

模型规模 训练 token GPU 数 训练时间 估算成本
─────────────────────────────────────────────────────
7B 1T 256 14 天 $0.5M
13B 2T 512 20 天 $1.5M
70B 10T 1024 60 天 $10M
175B 3T 1024 30 天 $8M
405B 15T 16000 30 天 $100M+

15.2 训练 FLOPs 估算#

def estimate_training_flops(n_params, n_tokens):
"""估算训练 FLOPs"""
# 简化公式: 6 * N * D
# 6 = 前向(2N) + 反向(4N)
flops = 6 * n_params * n_tokens
return flops
# 示例
# Llama 3 405B, 15T tokens
flops = 6 * 405e9 * 15e12
# = 3.65e22 FLOPs
# = 36.5 ZettaFLOPs

15.3 成本优化策略#

COST_STRATEGIES = """
1. 模型架构优化
- 使用 GQA 减小 KV cache
- 使用 SwiGLU 提升效率
- 使用 RMSNorm 减小计算
2. 训练技术优化
- Flash Attention 加速
- ZeRO-3 减少内存
- Gradient checkpointing
3. 硬件选择
- H100 比 A100 快 2-3 倍
- 但价格更贵
- 平衡性价比
4. 训练时长
- 不要训练过长时间
- 在 Chinchilla 最优点停止
"""

16. 最新进展与未来#

16.1 创新点#

LATEST_INNOVATIONS = """
2023-2024:
- Chinchilla Scaling Law 验证
- Llama 2/3 开源
- Flash Attention 2/3
- 混合专家模型 (Mixtral)
2025+:
- 多模态预训练 (文本 + 图像 + 音频)
- 长上下文 (1M+ tokens)
- 更高效架构 (Mamba, RWKV 等)
- Agent 集成训练
- 自我演化训练
"""

16.2 替代架构#

ALTERNATIVE_ARCHITECTURES = """
1. State Space Models
- Mamba
- RWKV
- Jamba (Mamba + Transformer)
优势: O(n) 复杂度,长序列
2. Mixture of Experts
- Mixtral 8x7B
- DBRX
优势: 大参数,低计算
3. Hybrid Models
- Striped Hyena
- Jamba
优势: Transformer + SSM
4. Multi-modal Native
- GPT-4V, Gemini
- 文图一体训练
"""

16.3 持续训练趋势#

FUTURE_DIRECTIONS = """
1. Synthetic Data
- 用更强模型生成训练数据
- Phi-3 / Llama 3 都采用
2. Test-Time Training
- 推理时也学习
3. Self-Play
- 模型自我对抗训练
4. Continual Learning
- 不遗忘旧能力
5. RL 与预训练的融合
- 不只是 SFT + RLHF
- 预训练阶段就加入 RL 信号
"""

17. 核心概念总结#

17.1 预训练的关键决策#

KEY_DECISIONS = """
1. 训练目标
- 因果 LM (GPT 风格) ⭐
- 掩码 LM (BERT 风格)
- 前缀 LM
- 去噪自编码 (T5)
2. 模型架构
- Decoder-Only ⭐ (Llama, GPT)
- 位置编码: RoPE, ALiBi
- 注意力: MHA / GQA / MQA
- 激活: SwiGLU
- 归一化: RMSNorm
3. 数据工程
- 数据质量 > 数量
- 去重、清洗、配比
- 多领域、多语言
4. 训练策略
- Optimizer: AdamW
- Schedule: Cosine with warmup
- 混合精度: BF16
- 分布式: TP + PP + DP + ZeRO
5. 规模
- Chinchilla: 20 tokens/param
- 实际略高(Llama 3: 38 tokens/param)
"""

17.2 数学表达#

  1. 预训练损失
Lpretrain=1NilogPθ(xix<i)\mathcal{L}_{\text{pretrain}} = -\frac{1}{N} \sum_i \log P_\theta(x_i | x_{<i})
  1. Chinchilla 标度律
L(N,D)=E+ANα+BDβL(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}
  1. MFU 公式
MFU=Achieved FLOPs/sPeak FLOPs/s\text{MFU} = \frac{\text{Achieved FLOPs/s}}{\text{Peak FLOPs/s}}
  1. RoPE 旋转编码
f(x,m)=(xcosmθ,xsinmθ)f(x, m) = (x \cos m\theta, x \sin m\theta)

18. 实战建议#

18.1 决策清单#

DECISION_CHECKLIST = """
□ 选择模型规模(7B, 13B, 70B...)
□ 选择训练目标(CLM, MLM...)
□ 准备 tokenizer
□ 数据收集与清洗
□ 数据配比
□ 选择架构(Llama 风格 / 其他)
□ 设计超参数
□ 准备分布式训练脚本
□ 准备监控和 checkpoint
□ 评估策略
□ Loss spike 应对方案
"""

18.2 常见问题#

FAQ = """
Q: 训练 Loss 不下降?
A: 检查学习率、数据质量、初始化
Q: 训练 Loss spike?
A: 降低 LR、回滚到 spike 前的 checkpoint
Q: GPU 内存不够?
A: 使用 ZeRO-3 + activation checkpointing + CPU offload
Q: 训练很慢?
A: 优化 MFU, 检查数据加载瓶颈
Q: 训练成本过高?
A: 减小模型规模、使用高效训练技术、按 Chinchilla 停止
Q: 模型没有涌现能力?
A: 规模可能不够;继续按 Scaling Law 扩展
"""

18.3 推荐资源#

RESOURCES = """
学习资源:
📚 论文:
- Language Models are Few-Shot Learners (GPT-3)
- Training Compute-Optimal LLMs (Chinchilla)
- Llama 2 / Llama 3
- Scaling Laws for Neural LMs (Kaplan)
🛠️ 工具:
- DeepSpeed (Microsoft)
- Megatron-LM (NVIDIA)
- FSDP (PyTorch)
- Hugging Face Transformers
📖 框架:
- PyTorch Lightning
- Hugging Face Transformers
- Megatron-DeepSpeed
"""

19. 总结与展望#

19.1 预训练的核心思想#

预训练是 LLM 的根基,决定了模型的能力上限:

  1. 自监督学习:利用海量无标注数据
  2. 规模法则:更多数据、更大模型 = 更好能力
  3. 通用能力:一次预训练,多种任务
  4. Transformer 主宰:当前主流架构

19.2 关键挑战#

挑战描述方向
数据高质量数据耗尽合成数据、自我训练
算力训练成本指数增长高效架构、稀疏化
架构Transformer 局限SSM、混合架构
记忆知识更新困难RAG、持续学习
安全越狱、有害生成RLHF、对齐研究

19.3 未来方向#

2025+ 的预训练趋势:
┌──────────────────────────────────────────┐
│ - 多模态原生预训练 │
│ - Agent-原生预训练 (工具集成) │
│ - O(n) 复杂度架构成熟 │
│ - 合成数据规模化 │
│ - 持续在线学习 │
│ - 推理与训练深度融合 │
└──────────────────────────────────────────┘

19.4 结语#

预训练是 AI 时代最激动人心的技术之一。

每一次规模扩展都带来惊喜:

  • 10 亿参数能学语言
  • 100 亿能推理
  • 1000 亿能涌现
  • 万亿能做什么?未来由你书写

“我们不在构建模型,我们是在扩展智能的边界。” — Sam Altman

从 GPT 到 Llama,从 Transformer 到 Mamba,预训练策略的演进见证了 AI 从专用模型通用智能的飞跃。掌握预训练,就是掌握 LLM 时代的入场券

参考资料#

  1. Vaswani, A., et al. (2017). “Attention Is All You Need.” NeurIPS.
  2. Radford, A., et al. (2018). “Improving Language Understanding by Generative Pre-Training.” OpenAI.
  3. Radford, A., et al. (2019). “Language Models are Unsupervised Multitask Learners.” OpenAI.
  4. Brown, T., et al. (2020). “Language Models are Few-Shot Learners.” NeurIPS.
  5. Kaplan, J., et al. (2020). “Scaling Laws for Neural Language Models.” arXiv.
  6. Hoffmann, J., et al. (2022). “Training Compute-Optimal Large Language Models.” arXiv.
  7. Touvron, H., et al. (2023). “Llama 2: Open Foundation and Fine-Tuned Chat Models.” arXiv.
  8. Meta. (2024). “Llama 3: The Most Capable Open Large Language Model.” Meta AI.
  9. Fedus, W., et al. (2022). “Switch Transformer: Scaling to Trillion Parameter Models.” JMLR.
  10. Su, J., et al. (2021). “RoFormer: Enhanced Transformer with Rotary Position Embedding.” Neurocomputing.
  11. Chowdhery, A., et al. (2022). “PaLM: Scaling Language Modeling with Pathways.” arXiv.
  12. Dao, T., et al. (2022). “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness.” NeurIPS.
  13. Rajbhandari, S., et al. (2020). “ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.” SC.
  14. DeepSpeed Team. (2024). “DeepSpeed: Extreme-scale model training.” Microsoft.
  15. Korthikanti, V., et al. (2023). “Reducing Activation Recomputation in Large Transformer Models.” arXiv.
  16. Longpre, S., et al. (2024). “A Review of Deep Learning Approaches for Pretraining.” ACL.
  17. Touvron, H., et al. (2023). “Llama: Open and Efficient Foundation Language Models.” arXiv.
  18. Biderman, S., et al. (2023). “Pythia: A Suite for Analyzing Large Language Models.” arXiv.
  19. Computer, T. (2023). “RedPajama: An Open Source Recipe to Reproduce LLaMA Training Dataset.”
  20. Penedo, G., et al. (2023). “The RefinedWeb Dataset for Falcon LLM.” arXiv.

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

预训练策略:从 GPT 到 Llama,大模型的"九年义务教育"
https://aiattnstudio.link/posts/pretraining-strategies/
作者
Federico
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author

Federico

AI Research Lab

Hello, I'm Federico.

关于实验室 / About
公告

欢迎来到Federico的个人博客

分类
标签
站点统计
57文章
7分类
404标签
1
1. 引言:什么是预训练?
1.1 一个生动的比喻
1.2 预训练的核心思想
1.3 预训练简史
1.4 预训练 vs 其他训练阶段
2
2. 预训练核心任务
2.1 语言建模:基础任务
2.2 因果语言建模 (Causal LM)
2.3 掩码语言建模 (Masked LM)
2.4 多种预训练目标
2.5 前缀语言建模 (Prefix LM)
2.6 去噪自编码 (Denoising AE)
3
3. Tokenization:预训练的第一步
3.1 为什么需要 Tokenization?
3.2 BPE 算法
3.3 现代 Tokenizer 工具
3.3.1 SentencePiece
3.3.2 tiktoken
3.3.3 HuggingFace Tokenizers
3.4 Tokenizer 选型
3.5 中文 Tokenization 挑战
3.6 多语言 Tokenization
4
4. 数据工程
4.1 数据的重要性
4.2 数据规模
4.3 数据来源
4.4 数据配比
4.5 数据清洗流程
4.6 数据去重
4.7 数据质量评分
4.8 数据污染检测
5
5. 模型架构选择
5.1 Transformer 变体
5.2 现代 Decoder 改进
5.2.1 RMSNorm
5.2.2 SwiGLU Activation
5.2.3 RoPE 旋转位置编码
5.2.4 GQA 分组查询注意力
5.2.5 Flash Attention
5.3 Llama 架构详解
5.4 关键超参数
6
6. Scaling Laws:规模定律
6.1 Chinchilla 定律
6.2 Scaling Law 实证
6.3 Compute-Optimal Frontier
6.4 涌现能力 (Emergent Abilities)
6.5 Scaling 实践
7
7. 训练基础设施
7.1 分布式训练架构
7.2 数据并行 (DP)
7.3 模型并行 (MP)
7.3.1 张量并行 (TP)
7.3.2 流水线并行 (PP)
7.4 内存优化
7.4.1 ZeRO
7.4.2 FSDP
7.5 混合精度训练
7.6 梯度累积与检查点
7.7 实际集群配置
8
8. 优化器和学习率
8.1 AdamW 优化器
8.2 学习率调度
8.2.1 Cosine Schedule
8.2.2 WSD (Warmup-Stable-Decay)
8.3 关键超参数
9
9. 训练目标与损失函数
9.1 交叉熵损失
9.2 Loss 的多种解释
9.3 损失掩码
9.4 多任务预训练
9.5 辅助损失
10
10. 训练稳定性
10.1 Loss Spike
10.2 梯度裁剪
10.3 初始化
10.4 监控指标
10.5 MFU: Model FLOPs Utilization
11
11. 评估预训练效果
11.1 训练中的评估
11.2 评估基准对照
11.3 训练 Loss 曲线分析
12
12. 高级训练策略
12.1 课程学习 (Curriculum Learning)
12.2 数据调度
12.3 MoE 训练 (Sparse Upcycling)
12.4 退火训练 (Annealing)
13
13. 持续预训练
13.1 什么时候需要继续预训练?
13.2 继续预训练的实现
13.3 Catastrophic Forgetting
14
14. 高效训练技术
14.1 Flash Attention
14.2 Ring Attention
14.3 Sequence Parallelism
14.4 Activation Recomputation
14.5 8-bit 优化器
14.6 CPU Offloading
15
15. 成本估算
15.1 训练成本
15.2 训练 FLOPs 估算
15.3 成本优化策略
16
16. 最新进展与未来
16.1 创新点
16.2 替代架构
16.3 持续训练趋势
17
17. 核心概念总结
17.1 预训练的关键决策
17.2 数学表达
18
18. 实战建议
18.1 决策清单
18.2 常见问题
18.3 推荐资源
19
19. 总结与展望
19.1 预训练的核心思想
19.2 关键挑战
19.3 未来方向
19.4 结语
20
参考资料