22 - 大语言模型原理¶
⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。
📌 导航提示:本章从机器学习视角提供LLM概览。详细LLM技术教程请参考
LLM学习/目录;LLM应用与实践请参考LLM应用/目录。
🗺️ LLM在ML体系中的位置¶
LLM是ML发展的自然延伸:更大的模型 + 更多的数据 + 更强的算力 = 涌现出传统ML不具备的能力。
📏 Scaling Law:规模定律¶
Scaling Law是理解LLM的关键框架,揭示了性能与三个变量的幂律关系:
| 变量 | 含义 | 趋势 |
|---|---|---|
| 模型参数量 N | 网络权重数量 | ↑ 对数线性提升性能 |
| 数据量 D | 训练token数 | ↑ 对数线性提升性能 |
| 计算量 C | FLOPs | ≈ 6ND(训练所需算力) |
3.1 Kaplan Scaling Laws (2020)¶
OpenAI在2020年发表的奠基性研究,首次系统性地量化了模型性能与规模的关系。
核心数学形式:
其中: - \(L\):交叉熵损失(越低越好) - \(N\):模型参数量 - \(D\):训练数据量(token数) - \(N_c, D_c\):常数项,代表"临界规模" - \(\alpha_N, \alpha_D\):幂律指数
Kaplan的关键发现:
| 参数 | 估计值 | 含义 |
|---|---|---|
| \(\alpha_N\) | ≈ 0.076 | 参数量每增加10倍,损失下降约15% |
| \(\alpha_D\) | ≈ 0.095 | 数据量每增加10倍,损失下降约20% |
| \(N_c\) | ≈ \(8.8 \times 10^{13}\) | 理论上的参数临界点 |
| \(D_c\) | ≈ \(5.4 \times 10^{13}\) | 理论上的数据临界点 |
组合损失函数:
其中 \(E\) 是不可约损失(irreducible loss),代表任务的理论下界。
Kaplan结论:在固定计算预算下,优先扩大模型规模比增加数据更有效。
3.2 Chinchilla Scaling Laws (2022)¶
DeepMind的Chinchilla研究修正了Kaplan的结论,提出了不同的最优分配策略。
核心公式:
Chinchilla参数估计:
| 参数 | 估计值 | 与Kaplan对比 |
|---|---|---|
| \(\alpha\) | ≈ 0.34 | 显著大于Kaplan的0.076 |
| \(\beta\) | ≈ 0.28 | 显著大于Kaplan的0.095 |
| \(A\) | ≈ 406.4 | - |
| \(B\) | ≈ 410.7 | - |
| \(E\) | ≈ 1.69 | - |
3.3 Chinchilla最优计算分配¶
计算预算约束:
最优分配推导:
在固定计算预算 \(C\) 下,最小化损失 \(L(N, D)\):
使用拉格朗日乘数法求解:
Chinchilla最优解:
| 计算预算 (FLOPs) | 最优参数量 | 最优数据量 (tokens) | 典型模型规模 |
|---|---|---|---|
| \(10^{20}\) | 440M | 9B | 小型模型 |
| \(10^{21}\) | 1.4B | 29B | GPT-2规模 |
| \(10^{22}\) | 4.4B | 91B | 中型模型 |
| \(10^{23}\) | 14B | 288B | LLaMA-13B |
| \(10^{24}\) | 44B | 912B | Chinchilla-70B |
| \(10^{25}\) | 139B | 2.9T | GPT-3.5规模 |
Kaplan vs Chinchilla对比:
# 对比两种Scaling Law的最优分配策略
import numpy as np
def kaplan_optimal(C):
"""Kaplan: 优先扩大模型"""
# Kaplan建议 N ∝ C^0.73, D ∝ C^0.27
N = 0.1 * (C ** 0.73)
D = C / (6 * N)
return N, D
def chinchilla_optimal(C):
"""Chinchilla: 模型和数据平衡扩展"""
# Chinchilla定律: C ≈ 6ND, 且N与D等比例增长
# 即 N ∝ C^0.5, D ∝ C^0.5, 满足 6*N*D = C
N = (C / 6) ** 0.5 # N = sqrt(C/6)
D = (C / 6) ** 0.5 # D = sqrt(C/6), 使得 6*N*D = C
return N, D
# 以GPT-3的计算预算为例 (约3.14 × 10^23 FLOPs)
C = 3.14e23
N_kaplan, D_kaplan = kaplan_optimal(C)
N_chinchilla, D_chinchilla = chinchilla_optimal(C)
print(f"计算预算: {C:.2e} FLOPs")
print(f"\nKaplan最优: N={N_kaplan/1e9:.1f}B, D={D_kaplan/1e9:.1f}B tokens")
print(f"Chinchilla最优: N={N_chinchilla/1e9:.1f}B, D={D_chinchilla/1e9:.1f}B tokens")
print(f"\nGPT-3实际: N=175B, D=300B tokens")
print(f"→ GPT-3更接近Kaplan策略(模型偏大,数据偏少)")
3.4 实践中的调整¶
现代LLM的偏离:
| 模型 | 参数量 | 训练数据 | 偏离Chinchilla原因 |
|---|---|---|---|
| LLaMA-7B | 7B | 1T tokens | 数据远超最优(~20x),推理效率优先 |
| LLaMA-65B | 65B | 1.4T tokens | 数据仍超最优,兼顾推理和训练 |
| GPT-4 | ~1.8T(推测) | ~13T(推测) | 推理成本主导,数据效率优先 |
偏离原因分析:
- 推理成本:小模型推理更快,因此用更多数据训练小模型
- 数据质量:高质量数据稀缺,需要更大的模型来补偿
- 后训练:SFT和RLHF阶段会进一步提升性能
- 领域适应:特定领域可能需要不同的最优分配
🌊 涌现能力理论¶
4.1 什么是涌现能力¶
定义:涌现能力(Emergent Abilities)是指模型在达到一定规模后突然出现、而较小模型完全不具备的能力。
数学描述:
4.2 主要涌现能力¶
| 能力 | 描述 | 临界规模 | 典型任务 |
|---|---|---|---|
| 上下文学习 (ICL) | 从示例中学习新模式 | ~1B | Few-shot learning |
| 思维链推理 (CoT) | 分步骤解决复杂问题 | ~10B | 数学推理、逻辑推理 |
| 指令遵循 | 理解并执行复杂指令 | ~100B | 多步骤任务执行 |
| 代码生成 | 生成可执行代码 | ~10B | 程序合成 |
| 数学推理 | 解决数学问题 | ~100B | GSM8K, MATH |
4.3 临界规模点量化¶
Wei et al. (2022) 的实证研究:
# 涌现能力的量化分析示例
import matplotlib.pyplot as plt
import numpy as np
# 模型规模(参数量,单位:B)
model_sizes = [0.1, 0.3, 1, 3, 10, 30, 100, 280]
# 模拟不同任务的性能曲线
def emergence_curve(N, N_crit, max_perf=100):
"""涌现能力的Sigmoid模型"""
k = 10 / N_crit # 陡峭度
return max_perf / (1 + np.exp(-k * (N - N_crit)))
# 不同任务的临界规模
tasks = {
"算术运算": 10,
"思维链推理": 30,
"多语言理解": 5,
"代码生成": 10,
"数学问题": 100,
}
plt.figure(figsize=(12, 6))
for task, N_crit in tasks.items():
perf = [emergence_curve(N, N_crit) for N in model_sizes]
plt.plot(model_sizes, perf, 'o-', label=f'{task} (临界点: {N_crit}B)')
plt.xscale('log')
plt.xlabel('模型参数量 (B)')
plt.ylabel('任务性能')
plt.title('涌现能力与模型规模的关系')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
4.4 涌现能力的理论解释¶
假设1:组合性假设 - 大模型学会了将基础能力组合成复杂能力 - 数学表示:\(\text{Complex} = f(\text{Basic}_1, \text{Basic}_2, ..., \text{Basic}_n)\)
假设2:临界数据假设 - 某些能力需要足够的训练数据才能触发 - 稀有模式在小数据集上无法有效学习
假设3:表示空间假设 - 大模型的表示空间维度更高 - 可以编码更复杂的语义关系
假设4:优化动力学假设 - 大模型的损失景观不同 - 存在小模型无法到达的局部最优
4.5 涌现能力的争议¶
Skepticism (Schaeffer et al., 2023): - 涌现可能是评估指标的"幻觉" - 使用非线性指标(如Exact Match)会产生突然跃升的假象 - 线性指标(如Token Accuracy)显示平滑提升
反驳观点: - 实际应用中,Exact Match等指标更有意义 - 即使是"幻觉",这种"幻觉"具有实用价值
🏋️ 训练稳定性¶
5.1 Loss Spike问题¶
定义:训练过程中损失突然大幅上升,可能导致训练崩溃。
Loss Spike的典型模式:
Loss
│
│ ╱╲
│ ╱ ╲╱╲
│ ╱ ╲___
│ ╱ ╲
│ ╱ ╲
│╱ ╲
└──────────────────→ Training Steps
↑ Loss Spike
主要原因:
| 原因 | 机制 | 缓解策略 |
|---|---|---|
| 学习率过大 | 优化步长超过损失曲面的曲率 | 学习率预热、梯度裁剪 |
| 数据异常 | 罕见或错误数据导致梯度爆炸 | 数据过滤、异常检测 |
| 梯度爆炸 | 梯度在反向传播中指数增长 | 梯度裁剪、LayerNorm |
| 参数漂移 | 长期训练导致参数偏离最优 | 学习率衰减、权重衰减 |
5.2 梯度问题与缓解¶
梯度爆炸/消失的数学分析:
对于深度为 \(L\) 的网络:
当 \(\|\frac{\partial h_l}{\partial h_{l-1}}\| > 1\) 时梯度爆炸,\(< 1\) 时梯度消失。
缓解策略:
import torch
import torch.nn as nn
class StableTransformerBlock(nn.Module):
"""稳定的Transformer块,包含多种梯度稳定技术"""
def __init__(self, d_model, n_head, dropout=0.1):
super().__init__()
# 1. Pre-LayerNorm(比Post-LN更稳定)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.attn = nn.MultiheadAttention(d_model, n_head, dropout=dropout)
self.ffn = nn.Sequential(
nn.Linear(d_model, 4 * d_model),
nn.GELU(), # GELU比ReLU更平滑
nn.Linear(4 * d_model, d_model),
)
self.dropout = nn.Dropout(dropout)
# 2. 可学习的缩放因子(用于稳定训练初期)
self.attn_scale = nn.Parameter(torch.ones(1) * 0.1)
self.ffn_scale = nn.Parameter(torch.ones(1) * 0.1)
def forward(self, x):
# Pre-LN结构
# 3. 残差连接 + 缩放
attn_out, _ = self.attn(self.norm1(x), self.norm1(x), self.norm1(x))
x = x + self.attn_scale * self.dropout(attn_out)
ffn_out = self.ffn(self.norm2(x))
x = x + self.ffn_scale * self.dropout(ffn_out)
return x
def gradient_clipping(model, max_norm=1.0):
"""梯度裁剪:防止梯度爆炸"""
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)
return model
def adaptive_gradient_clipping(model, clip_factor=0.01):
"""自适应梯度裁剪(AGC):根据参数范数动态调整"""
for param in model.parameters():
if param.grad is not None:
param_norm = torch.norm(param)
grad_norm = torch.norm(param.grad)
max_norm = param_norm * clip_factor
if grad_norm > max_norm and grad_norm > 0:
param.grad.data.mul_(max_norm / grad_norm)
return model
5.3 学习率调度策略¶
1. Warmup策略:
def warmup_lr(step, warmup_steps, base_lr):
"""线性预热学习率"""
return base_lr * min(1.0, step / warmup_steps)
2. Cosine Decay:
import math
def cosine_decay_lr(step, total_steps, max_lr, min_lr=0):
"""余弦衰减学习率"""
ratio = step / total_steps
return min_lr + 0.5 * (max_lr - min_lr) * (1 + math.cos(math.pi * ratio))
def cosine_decay_with_warmup(step, warmup_steps, total_steps, max_lr, min_lr=0):
"""带预热的余弦衰减(LLM训练标准配置)"""
if step < warmup_steps:
# 线性预热
return max_lr * step / warmup_steps
else:
# 余弦衰减
ratio = (step - warmup_steps) / (total_steps - warmup_steps)
return min_lr + 0.5 * (max_lr - min_lr) * (1 + math.cos(math.pi * ratio))
# 可视化学习率调度
def plot_lr_schedule():
import matplotlib.pyplot as plt
total_steps = 100000
warmup_steps = 2000
max_lr = 6e-4
min_lr = 6e-5
steps = list(range(total_steps))
lrs = [cosine_decay_with_warmup(s, warmup_steps, total_steps, max_lr, min_lr)
for s in steps]
plt.figure(figsize=(12, 4))
plt.plot(steps, lrs)
plt.axvline(x=warmup_steps, color='r', linestyle='--', label=f'Warmup End ({warmup_steps})')
plt.xlabel('Training Steps')
plt.ylabel('Learning Rate')
plt.title('Cosine Decay with Warmup Learning Rate Schedule')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
plot_lr_schedule()
3. WSD(Warmup-Stable-Decay)策略:
现代LLM(如Llama 3)采用的策略:
def wsd_schedule(step, warmup_steps, stable_steps, decay_steps, max_lr, min_lr):
"""
WSD学习率调度:Warmup -> Stable -> Decay
优点:
1. 稳定阶段便于checkpoint选择
2. 衰减阶段快速收敛
3. 适合继续预训练
"""
if step < warmup_steps:
return max_lr * step / warmup_steps
elif step < warmup_steps + stable_steps:
return max_lr
else:
decay_progress = (step - warmup_steps - stable_steps) / decay_steps
return min_lr + (max_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * decay_progress))
5.4 训练稳定性最佳实践¶
| 技术 | 作用 | 典型配置 |
|---|---|---|
| Pre-LayerNorm | 稳定梯度流 | 所有Transformer层 |
| 梯度裁剪 | 防止梯度爆炸 | max_norm=1.0 |
| 学习率Warmup | 稳定训练初期 | 前1-2%步数 |
| 权重衰减 | 防止过拟合 | 0.1 |
| Dropout | 正则化 | 0.1(大模型可降低或不用) |
| 混合精度训练 | 加速+正则化效果 | BF16/FP16 |
| μP (Maximal Update Parametrization) | 超参数迁移 | 简化大模型调参 |
完整的训练配置示例:
from dataclasses import dataclass
@dataclass
class LLMTrainingConfig:
"""LLM训练稳定性配置"""
# 模型配置
hidden_size: int = 4096
num_layers: int = 32
num_heads: int = 32
# 优化配置
learning_rate: float = 3e-4
weight_decay: float = 0.1
beta1: float = 0.9
beta2: float = 0.95 # AdamW的beta2,比默认0.999更小
# 学习率调度
warmup_steps: int = 2000
lr_scheduler: str = "cosine"
min_lr_ratio: float = 0.1 # 最终LR = max_lr * 0.1
# 梯度控制
max_grad_norm: float = 1.0
grad_clip_type: str = "global" # global或adaptive
# 批次配置
batch_size: int = 4_000_000 # 总token数
gradient_accumulation_steps: int = 1
# 精度配置
dtype: str = "bf16" # bf16比fp16更稳定
grad_dtype: str = "fp32" # 梯度用fp32保持精度
核心洞察: - 三者需要同步扩展,单一增长回报递减 - Chinchilla Law:最优策略是模型和数据同比例扩大(非一味加大模型) - 涌现能力:部分能力(思维链推理、上下文学习等)在模型达到特定规模后突然出现 - 训练稳定性:大模型训练需要精心设计的稳定性策略,包括梯度裁剪、学习率调度和正则化
🔄 训练范式概述¶
LLM的训练是一个多阶段流程:
| 阶段 | 目标 | 数据 | 一句话描述 |
|---|---|---|---|
| 预训练 (Pre-training) | 学习语言知识 | 万亿级无标注文本 | 通过下一词预测任务学习世界知识和语言规律 |
| 监督微调 (SFT) | 学会遵循指令 | 十万级高质量指令-回答对 | 让模型学会"对话"和"遵循指令"的格式 |
| RLHF / DPO | 对齐人类偏好 | 人类偏好排序数据 | 让模型输出更安全、有用、符合人类期望 |
🔗 LLM与传统ML的关系¶
| 维度 | 传统ML | LLM |
|---|---|---|
| 学习范式 | 从标注数据学习特定映射 | 从海量文本学习通用表示 |
| 任务适配 | 每个任务训练独立模型 | 一个模型适应多种任务(Prompt/ICL) |
| 特征工程 | 核心环节(人工设计) | 几乎消除(端到端学习) |
| 样本需求 | 任务相关标注数据 | 预训练无需标注,下游少样本即可 |
| 可解释性 | 部分模型可解释 | 黑盒,可解释AI是活跃研究方向 |
| 评估方式 | 准确率/F1等标准指标 | 新增:人类评估、MMLU等综合基准 |
关键联系: - LLM的优化仍是梯度下降 + 反向传播,与传统ML一脉相承 - 正则化、学习率调度、数据质量等ML基本功在LLM训练中同样至关重要 - 传统ML在表格数据、低延迟推理等场景依然不可替代
📋 面试要点¶
- 什么是Scaling Law? → 模型性能与参数量、数据量、计算量呈幂律关系;Chinchilla Law强调数据和模型需同步扩展
- 预训练→SFT→RLHF三阶段各解决什么问题? → 知识获取 → 指令遵循 → 人类偏好对齐
- LLM的涌现能力有哪些? → 上下文学习(ICL)、思维链推理(CoT)、指令遵循等,小模型不具备
- LLM vs 传统ML各自的优势场景? → LLM:自然语言理解与生成;传统ML:表格数据、低延迟、高可解释性需求
✏️ 练习¶
- 体系定位分析:绘制一张思维导图,展示从线性回归到LLM的技术演进路径,标注各阶段的核心突破点(表示学习、注意力机制、预训练、Scaling Law等)。
📖 下一步学习:23-可解释AI与因果推断.md | LLM详细教程 →
LLM学习/| LLM应用实践 →LLM应用/