预训练工程深拆:Scaling_Law、数据管线与分布式并行

训练一个千亿参数模型,在数学上只是优化一个交叉熵损失;在工程上,它同时是预算规划、数据科学、显存算术和通信拓扑的联合优化问题。这四个子问题彼此咬合:预算表决定数据量与参数量的配比,数据质量决定损失的下界,显存算术决定并行拓扑,而拓扑的通信效率决定那张千万美元的账单最终烧多久。本文按这个顺序逐层拆解。

一、Scaling Law:先有预算表,后有模型

1.1 两条定律的交锋

2020 年 Kaplan 等人的第一篇 Scaling Law 论文给出了损失随参数量的幂律衰减;2022 年 DeepMind 的 Chinchilla 修正了它------前者在小数据上外推,结论系统性偏科"堆参数"。Chinchilla 的参数化形式:

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

NNN 是参数量,DDD 是 Token 数,EEE 是语料的不可约熵(任何模型都不可能低于它),后两项分别是容量瓶颈与数据瓶颈。论文报告的拟合值量级:α≈0.34,β≈0.28\alpha \approx 0.34, \beta \approx 0.28α≈0.34,β≈0.28。

训练算力有个经典估算公式:前向 2ND2ND2ND,反向约两倍,合计 6ND6ND6ND FLOPs。于是问题变成:给定预算 CCC,怎么花最划算?拉格朗日数值求解:

python 复制代码
import numpy as np
from scipy.optimize import minimize

E, A, alpha, B, beta = 1.69, 406.4, 0.34, 410.7, 0.28  # Chinchilla 拟合值

def loss(N, D):
    return E + A / N**alpha + B / D**beta

def optimal_ND(C):
    """固定算力 C (FLOPs),求最优 (N, D)"""
    def obj(logN):
        N = np.exp(logN[0])
        D = C / (6 * N)          # 预算约束: 6ND = C
        return loss(N, D)
    res = minimize(obj, x0=[np.log(np.sqrt(C / 6))])
    N = np.exp(res.x[0]); D = C / (6 * N)
    return N, D, D / N           # 返回 tokens/param 比值

# 三个量级的预算,比值都收敛在 ~20 附近:
for C in [1e21, 1e22, 1e23]:
    N, D, ratio = optimal_ND(C)
    print(f"C={C:.0e}: N={N:.1e}, D={D:.1e}, D/N={ratio:.0f}")
# 这就是"每个参数配 20 个 Token"的出处

这组公式的预测力是实打实的:GPT-3 用 175B 参数只喂了 300B Token(每参数 1.7 个),严重偏科容量项;Chinchilla 用 70B/1.4T(每参数 20 个)直接打平甚至反超了 280B 的 Gopher。同样的算力,配比对了就是一代人的差距。

1.2 后 Chinchilla 时代:推理成本改写最优解

Scaling Law 的隐含前提是"算力只在训练时花一次"。现实里推理成本随部署规模线性增长,于是 2023 年之后的公开配方普遍越过 Chinchilla 点过度训练小模型:

模型 参数 Token D/N 比值
GPT-3 175B 300B 1.7
Chinchilla 70B 1.4T 20
Llama-3 8B 8B 15T 1875

逻辑很简单:训练成本是一次性的,推理成本是永续的。把 8B 模型喂到 1875 tokens/param,损失略高于同算力的"最优大模型",但部署侧省下的显存与带宽在生命周期内远超训练差价。这条推理最优(inference-optimal)scaling 的研究线,正是 Chinchilla 结论在产品经济学约束下的修正版。

二、数据管线:损失的下界在这里决定

模型只是拟合器,损失的理论下界 EEE 完全由数据决定。万亿 Token 语料的工业化管线大致是:抓取(Common Crawl)→ 正文抽取 → 质量过滤 → 去重 → 配比混合 → 退火收尾。

2.1 去重:MinHash + LSH

网页的自然重复率惊人,且重复数据有双重危害:浪费算力,以及制造记忆。Carlini 等人的可提取记忆研究表明,一条文本在语料中出现几十次后,模型会近乎逐字记住它------训练数据的隐私与版权问题直接由此而来。

精确去重用哈希表即可,难点是近似重复(换几个词的洗稿、模板化页面)。标准武器是 MinHash:用 kkk 个随机哈希函数对文档的 shingle 集合生成签名,两个文档的 Jaccard 相似度近似为签名重合率;再用 LSH 分带把候选对找出来:

python 复制代码
import numpy as np

def shingles(text, n=3):
    words = text.split()
    return set(tuple(words[i:i+n]) for i in range(len(words)-n+1))

def minhash(sigs, k=128, seed=0):
    rng = np.random.default_rng(seed)
    a, b = rng.integers(1, 2**31, k), rng.integers(0, 2**31, k)
    p = (1 << 61) - 1
    return tuple(min((a[i]*h + b[i]) % p for h in map(hash, sigs))
                 for i in range(k))

def lsh_bands(sig, bands=16):
    r = len(sig) // bands
    return [hash(sig[i*r:(i+1)*r]) for i in range(bands)]

# 相似度 s 的两文档至少撞出一个 band 的概率 = 1-(1-s^r)^bands
# r=8, bands=16: s=0.5 → 检出率 0.5%; s=0.8 → 99.997%
# ------ 天然的阈值闸门: 明显不同的放过, 明显相似的拦下

分带参数(r×b=kr \times b = kr×b=k)就是相似度阈值的调节旋钮:上面注释里 s=0.5 与 s=0.8 的检出率对比,展示了这个机制如何把"模棱两可"的中间地带做窄。

2.2 质量与配比

过滤分两派:启发式(语言检测、符号/文字比、平均句长、Bad Word 列表)和分类器派(FineWeb-Edu 用一个微调小模型给"教育价值"打分,保留高分文档)。消融结果反复指向同一结论:数量级相同的语料,质量过滤带来的损失下降可以抵掉一个数量级的规模。

配比是第二个杠杆。代码数据占比 30% 级别的混合配比,在多个独立消融里都被验证能提升自然语言推理基准的成绩------代码的因果结构(输入-输出对、类型约束、测试用例)是天然的逻辑训练素材。多语言配比、数学语料、以及训练末段的高质量数据退火(learning-rate 收尾时切换到精选分布,Llama-3 与 Qwen 配方均有此步骤),共同构成"数据课程"。

污染(contamination)是配比的反面:基准测试集的泄漏会让评测虚高。工业界标准检测是 n-gram 重叠(典型 13-gram)加模糊匹配,训练前剔掉与评测集近邻的文档。评测污染至今没有彻底解法,这也是为什么盲测和事后新题基准(LiveBench 类)的地位在上升。

三、显存算术:16 字节/参数的铁律

混合精度训练下,AdamW 优化器的每参数开销是一笔精确的账:

python 复制代码
def train_memory_gb(n_params, zero=0, gpus=1):
    per = 2 + 2 + (4 + 4 + 4)
    # bf16 参数 2B + bf16 梯度 2B
    # + fp32 master 权重 4B + Adam m/v 各 4B
    # ------ 合计 16 字节/参数(ZeRO 论文的经典口径)
    states = per * n_params  # 优化状态 + 参数 + 梯度
    shard = {0: 8, 1: 4, 2: 2, 3: 1}[zero]  # 切分比例
    return (8 * n_params + (16 - 8) * n_params * shard / gpus) / 1024**3

print(f"7B, 单卡:   {train_memory_gb(7e9, 0, 1):.0f} GB")   # 112 GB
print(f"7B, 8卡Z2:  {train_memory_gb(7e9, 2, 8):.0f} GB")    # 17 GB

单卡训 7B,光状态就 112 GB------没有任何卡装得下(H100 是 80GB),这就是并行训练的物理起因。ZeRO 系列的三个阶段对应切分三样东西:

阶段 切分对象 通信代价 显存节省
ZeRO-1 优化器状态 (8B/param) 与数据并行持平 4×
ZeRO-2 + 梯度 (2B) 与数据并行持平 8×
ZeRO-3 / FSDP + 参数 (2B) 参数 all-gather,升高 线性于卡数

注意 ZeRO 的本质仍是数据并行------每张卡看到不同 batch,只是把"每卡全量持有副本"改成"用的时候临时聚合"。它解决显存,不解决单层计算量。单层算不动或单层放不下,要靠下面两类并行。

此外还有激活值:不含 checkpointing 时与 batch × seq_len × 层数 × 隐藏宽度成正比。梯度检查点 (gradient checkpointing)只保存每层入口,反向时重算层内前向,用约 33% 的时间换激活显存降到 \sqrt{} 量级------长上下文训练几乎必开。

四、并行拓扑:DP × TP × PP 的三维组合

4.1 三种正交的切法

数据并行(DP):每卡持完整模型副本,处理不同 batch,反向后 all-reduce 梯度。扩展吞吐但不扩展模型容量。

张量并行(TP) :把单个权矩阵切开。Transformer 层的标准切法是:注意力按头切、MLP 的第一个矩阵按列切第二个按行切(Megatron-LM 布局)。代价是每层前向 2 次 all-reduce、反向 2 次,通信密度极高,要求 NVLink 级带宽------所以TP 几乎从不跨机,单机 8 卡是常见上限。

流水线并行(PP):按层切,把 40 层分给 4 台机器,各持 10 层。微批次(microbatch)流过流水线,1F1B 调度控制显存水位。气泡占比是经典公式:

python 复制代码
def bubble_fraction(stages, microbatches):
    # 空泡: 流水线灌满与排空的时间
    return (stages - 1) / (microbatches + stages - 1)

print(f"4 级 × 8 微批:  {bubble_fraction(4, 8):.1%}")    # 23%
print(f"4 级 × 64 微批: {bubble_fraction(4, 64):.1%}")   # 4.4%

气泡靠加大微批次数摊薄,代价是慢的层拖住快的层、跨机通信只容忍低带宽(每微批只传边界激活)。PP 跨机、TP 机内、DP 最外层,这个三维组合(3D 并行)就是 GPT-3 时代到今天训练万亿参数模型的标准形态。

4.2 怎么选拓扑

经验法则浓缩成一张表:

约束 首选 原因
单层放不下 / 单层算太慢 TP(机内) 唯一切细单个算子的方案
机间带宽低 PP 每微批只传边界张量
只要吞吐 DP 梯度 all-reduce 一次
状态放不下 ZeRO/FSDP 切优化器状态

实际部署是四者的联立方程。DeepSpeed、Megatron-LM、FSDP 提供的是把这套拓扑声明化的工程封装,选错拓扑的表现非常具体:TP 跨机会让通信占比直接吞掉 MFU,PP 微批太少会让 GPU 大量空转。

衡量整条管线的终指标是 MFU(Model FLOPs Utilization)= 实测 FLOPs / (卡数 × 峰值 × 时间)。头部万亿级训练的公开数字在 40~50%------也就是说,有一半的峰值算力天然消耗在通信、等待和访存上,这可以视为分布式训练的物理折扣率。

五、训练稳定性:loss spike 是常态,不是事故

数月量级的训练中,损失曲线几乎必然会出几次尖峰。OPT 与 BLOOM 的公开训练日志都记录了大量 spike、硬件故障与回滚重启。现代配方的应对是成体系的:

数值格式:fp16 需要动态 loss scaling 防下溢,bf16 有 fp32 同宽的指数域、免 scaling------A100/H100 时代 bf16 已是默认。

稳定化正则:QK-Norm 把注意力的 Q、K 归一化,掐掉注意力 logits 的量纲漂移;z-loss 约束输出层 logsumexp 的量级:

python 复制代码
def z_loss(logits):
    Z = torch.logsumexp(logits.float(), dim=-1)
    return 1e-3 * (Z ** 2).mean()
# PaLM 引入: logits 无界增长是训练后期 spike 的常见前兆

Spike 处置手册(综合各家日志的标准动作):梯度范数连续监控设告警 → 触发后先降学习率或跳过当前数据批次 → 尖峰持续则回滚到上一个健康 checkpoint → 事后排查是否特定数据(超长重复、乱码、异常分布的文档)引发,加入过滤规则。一次几十分钟的尖峰如果放任,可能烧掉数万美元算力并把曲线拖离最优轨迹。

优化器侧:AdamW 仍是主力,但 2024 年后 Muon(正交化动量)类优化器在同等 loss 下显著减少所需 Token 数,已在部分前沿训练(如 K2)里上岗------优化器公式本身仍是活跃的研究前线,而非 settled 的工程件。

六、边界:什么没有随规模自动到来

最后校准两个容易过度解读的叙事。

其一,涌现可能是度量幻象。Schaeffer 等人(2023)指出:许多"涌现能力"出现在非线性指标(精确匹配、多选题全对)上,换成连续指标(对数概率、token 级准确率)后曲线是平滑的。能力是渐进积累的,跃变发生在"衡量方式"而非"模型本身"------这个批判不推翻 scale 的价值,但提醒你别把基准的断崖当物理相变去做预算。

其二,MFU 的复利是真实的。从 GPT-3 时代到今天,同等损失所需算力持续下降:更好的数据管线、更优的并行实现、更稳的数值配方,每一项都在压低同一目标的成本。Scaling Law 描述的是"给定配方下损失的轨迹",而配方本身的斜率也在被优化------两条曲线相乘,才是这两年模型成本下降快于任何单一因子解释的原因。

收束成一张总表:

环节 核心问题 决定性的量 代表技术
预算规划 算力怎么花 D/N 配比 Chinchilla / 推理最优 scaling
数据 损失下界 质量与去重率 MinHash+LSH、分类器过滤、退火
显存 装不装得下 16 字节/参数 ZeRO、FSDP、梯度检查点
拓扑 通信与气泡 带宽层级 机内 TP × 跨机 PP × 外层 DP
稳定性 数月不崩 spike 处置 bf16、QK-Norm、z-loss、回滚
效率 峰值利用率 MFU 40~50% FlashAttention、调度优化、Muon

预训练的终局判断可以一句话说清:这是把"更多算力"翻译成"更低损失"的翻译器,翻译精度由数据质量和工程效率共同决定------前者设上限,后者定单价。

相关推荐
pjj198541 小时前
Spark RDD 核心知识详解:算子、依赖与执行模型
大数据·分布式·spark
浔溺2 小时前
Spark核心基石:RDD全方位深度解析(原理、算子、实战、优化)
大数据·分布式·spark
catino3 小时前
RabbitMQ详解
分布式·rabbitmq·ruby
爱吃火鸡面呀3 小时前
Spark RDD 创建与转换算子详解
大数据·分布式·spark
叶总没有会18 小时前
RabbitMQ高级
分布式·rabbitmq
ABB自动化1 天前
ABB 500CIM06 (1MRB150077R0111) | REB500 通信接口
大数据·分布式·系统架构·系统安全·模块测试·plc 控制器
ZENERGY-众壹1 天前
告别本地工控机:分布式光伏 SCADA 架构如何实现云边协同
分布式·架构·光伏运维·能源数字化·电站监控
code_slave(码畜)1 天前
微服务架构落地:读写分离架构 —— 主从延迟引发的“刚开单查不到”灾难与破局
spring boot·分布式·spring cloud·微服务·云原生·架构·读写分离
JAVA_AI人工智能项目实战1 天前
分布式大模型训练_学习笔记
人工智能·分布式·语言模型·自然语言处理