第 9 章 训练系统工程
第9章 训练系统工程
本章系统讨论大模型训练的工程化落地,依次覆盖稳定性与容错、分布式检查点、大规模数据加载、混合精度训练、优化 器选型、训练评估与模型评测、数据安全合规,最后以千卡训练实战收尾。
9.1 万卡训练稳定性
当训练集群规模突破万卡时,系统稳定性从「锦上添花」变为「生存问题」。本节从硬件故障、软件故障、故障率量化三 个维度,构建万卡级训练的稳定性全景图。
9.1.1 万卡集群的故障空间
万卡集群的故障空间远比单机或千卡集群复杂,故障模式呈现出明显的规模化特征。如图9-1所示,故障可划分为硬件、 软件与环境三类,其中硬件与软件故障最为常见,环境故障(电力波动、散热降频、机房基础设施)作为低频项存在。
- 硬件故障分类 万卡级 GPU 集群中最常见的硬件故障包括:
- GPU ECC 错误:HBM 中单比特可纠正错误(SBE)和双比特不可纠正错误(DBE)。在 10K GPU 集群中,SBE 发生率 约为每 GPU 每天 1-2 次,DBE 发生率约为每 GPU 每周 0.1 次。A100 引入并由 H100 继承的 row-remapping 机制可修 复部分 DBE,但无法消除。
- NVLink/NVSwitch 错误:NVLink 链路上的 CRC 校验错误和链路重训练。H100 SXM5 节点采用第四代 NVLink 与第三 代 NVSwitch,每 GPU 通过 18 条链路互联,节点内 GPU-to-GPU 双向聚合带宽约 900 GB/s;跨节点单端口 InfiniBand NDR 为 400 Gb/s,双向聚合约 50 GB/s。两类带宽均按双向聚合口径计,节点内比节点间高出约一个数量 级,这解释了为什么 H100 训练优先在节点内使用张量并行(TP)、节点间使用数据并行(DP)和流水线并行(PP), 跨节点 TP 的通信开销会严重拉低整体 MFU。故障影响随故障范围变化:部分 NVSwitch 故障时节点内聚合带宽按比例 下降,训练吞吐量可能下降 25-40%,应触发节点预警并加入 blacklist 候选队列;GPU 对等通信退化为单链路模式 时,有效带宽可下降 80% 以上。
- InfiniBand 链路抖动:IB 链路的 bit error rate (BER) 约为 10^-12 至 10^-15。在 10K GPU 集群的 4 层 Fat-Tree 拓扑 下,每日约有 2-5 次链路 flap 事件,触发 NCCL 通信重协商。
- 静默数据损坏 SDC:最隐蔽的故障模式。数据在存储或传输中发生未被 ECC/CRC 检测到的损坏。Meta 在 2021 年报告 中指出,大规模训练中 SDC 是导致 loss 异常的首要硬件原因。
- 存储节点与网络设备故障:存储服务器的 NVMe 盘故障、RAID 卡故障、交换机端口故障,导致检查点写入失败或数据 加载中断。
- 软件故障分类 软件层面的故障同样不可忽视:
- NCCL 通信死锁:NCCL 的 ring/tree 算法在链路变更或节点异常时进入无限等待状态。NCCL 2.18+ 版本引入了超时检 测和自动恢复机制。
- CUDA 非法内存访问:多进程环境中某 Rank 访问越界内存,触发 CUDA error,导致集体操作(AllReduce)卡死。
- Python 运行时异常:DataLoader 线程崩溃、内存泄漏导致的 OOM(Out of Memory)、Python GIL 竞争导致调度延 迟。
- 框架层面 Bug:Megatron-LM、DeepSpeed、FSDP 中特定并行配置下的未测试路径。例如 5D 并行中某维度数据分配 不均导致的 shape mismatch。 10K GPU Training Stability Hardware Fault Software Fault Environment Fault GPU ECC/NVLink IB link flapping Silent Data Corruption Storage/Switch Fault NCCL Deadlock/Timeout CUDA Error/Crash PyTorch Exception/OOM Framework Bug/Config Power Fluctuation Thermal Throttling Data Center Infrastructure 图9-1 万卡训练稳定性威胁模型
9.1.2 故障率量化分析
将故障率量化为可操作的工程指标,是设计容错系统的前提。
- MTBF 分析 对于万卡集群,设单 GPU 的 MTBF 为 T ,则集群整体 MTBF 近似为: gpu Tgpu Tcluster ≈ Ngpu × fcorrelation 其中 f 为故障相关性因子。考虑 NVSwitch、共享电源、共享散热等共因故障,f correlation 通常取 1.2-1.5。 correlation 实际情况中: •GPU 独立故障:单 GPU MTBF 约 3-5 年(25,000-44,000 小时),在 10K GPU 集群中,T 约 2.5-4.4 小时。即 gpu_cluster 大约每 3 小时发生一次 GPU 级故障。 •节点级故障:共享电源和散热,8 GPU 节点 MTBF 约 1-2 年,在 1250 节点集群中,约每 7-14 小时发生一次节点级故 障。 •网络故障:IB 交换机 MTBF 约 20 年,但在大型拓扑中,链路级别的异常事件每 4-8 小时发生一次。
- 故障恢复成本 一次训练任务重启的代价包括:
- 检查点回滚损失:最近一次检查点之后的所有进度丢失,典型检查点间隔为 1-2 小时。
- 重启开销:加载模型权重 + 加载优化器状态 + 重新初始化 DataLoader + 重新建立 NCCL 通信组。对于 70B 模型,单次 重启约需 5-10 分钟。
- 收敛延迟:频繁重启破坏学习率调度的连续性,可能影响最终模型质量。
- 主流团队实践经验 •OpenAI GPT-4:据公开证词,训练动用约 2.5 万张 A100、历时约 90-100 天,GPU 有效利用率仅 32%-36%,频繁的 故障重启是拉低利用率的主因之一。 •Meta Llama-3 405B:54 天训练周期内共记录 466 次中断,其中计划维护 47 次、意外中断 419 次;意外中断约 78% 归因硬件问题,GPU 类故障合计占 58.7%。依托高频检查点与自动恢复机制,有效训练时间保持在 90% 以上,全程仅 需 3 次人工干预。 •DeepSeek-V3:在 2K H800 集群训练中,通过自研 3FS 文件系统和主动故障检测机制,将故障恢复时间控制在 5 分钟 内,整体有效利用率达 90% 以上。
9.1.3 训练稳定性指标体系
衡量万卡训练稳定性的关键指标如表9-1所示。 表9-1 训练稳定性指标体系 指标 定义 目标值 Training Efficiency 有效训练步数 / 总步数(含重启) > 85% MTBF 两次故障之间的平均训练时间 > 12 小时 指标 定义 目标值 MTTR 故障发生后到恢复训练的平均时间 < 15 分钟 Checkpoint Success Rate 检查点保存成功率 > 99.5% Silent Error Detection 静默错误的自动检测率 > 95% Job Success Rate 训练任务一次成功率(无人工介入) > 90%
9.1.4 静默数据损坏检测
SDC 的检测是万卡训练中最具挑战性的问题之一:
- Hash 校验:在 AllReduce 通信前对梯度计算哈希,通信完成后验证哈希一致性。NCCL 2.20+ 内置了可选的 hash check 模式。
- Loss 异常检测:训练过程中实时检测 loss 的统计特征(均值、方差、自相关性),当 loss spike 超过 3σ 阈值时触发回 滚。
- 中间激活值校验:在特定层收集激活值的统计量(mean、max、min),与已知正常运行的参考值比较。Megatron-LM 的 FP8 训练健全性检查(fp8_sanity_check)即属于此类,当某层输出的均值或范数异常偏离历史基线时触发告警。
- 加权梯度校验:定期对梯度施加随机投影(random projection),在低维空间校验梯度一致性,检测代价远低于全量 校验。
9.1.5 训练稳定性度量与自愈闭环
稳定性指标体系回答了「测什么」,工程落地还要回答「如何用指标驱动改进」。本节把指标接入北极星度量、分层看板与 自愈闭环,形成从指标到改进的方法论;自愈动作的底层机制衔接容错设计,本节聚焦度量驱动的闭环本身。
- 训练稳定性北极星指标体系 北极星指标回答训练系统整体健康度这一顶层问题,选取四项,如表9-2所示。 表9-2 训练稳定性北极星指标 北极星指标 计算口径 目标值 ETTR(Effective Training 有效训练时间 / 墙钟总时间,有效训练时间按「步数 × 单步理论耗时」折算,重 > 90% Time Ratio) 启、等待与降速时段计入分母 MFU 实际产出 FLOPs / 硬件峰值 FLOPs,复用前述定义 > 40% 中断次数 单任务日均意外中断次数,按硬件、网络、软件分桶统计 日均 < 3 次 有效训练算力 ETTR × MFU × 集群峰值算力,衡量实际到手算力 长期趋势不 衰减 ETTR 与前述训练效率指标的关键差异在口径:训练效率以步数为分母,测不到「步数没断但变慢」的劣化;ETTR 以墙 钟时间为分母,重启窗口、调度排队与吞吐下降都会如实反映。有效训练算力把三个指标合并为一个对外口径,便于算力 交付核算。目标值须结合实际集群基线设定,表中取值对应 10K 卡规模下健康集群的参考区间。
- 稳定性看板 集群级看板按集群、作业、节点三层组织,各层监控对象与告警阈值如表9-3所示。 表9-3 稳定性看板分层监控 层 监控对象 关键指标 告警阈值 级 集 整体健康 活跃任务数、集群 ETTR 与 MFU、中断次数趋势、 checkpoint 耗时占比 > 5% 触发存储评估 群 度 checkpoint 耗时占比 层 监控对象 关键指标 告警阈值 级 作 单任务状 步数进度、最近中断与恢复、loss 与梯度范数 loss z-score > 5 触发回滚评估 业 态 节 硬件与链 SBE/DBE 计数、温度、NVLink 速率、IB 链路 flap、存储延迟 DBE 出现即隔离,链路 flap 每小时超 1 次触 点 路 发巡检
- 故障自愈闭环 度量与看板暴露问题,自愈闭环把问题收敛为训练不中断。闭环按检测、定位、自愈、验证、复盘五个环节运转,如图9- 2所示。 Metric Collect Anomaly Detect Fault Locate Auto Mitigate verify fail recur Health Verify Resume Training Postmortem 图9-2 度量驱动的稳定性自愈闭环 各环节要点:
- 检测:以看板指标为输入,对 ETTR、loss z-score、SBE/DBE 计数、链路 flap 设定阈值与趋势告警;检测追求高查 全,允许少量误报。
- 定位:按故障模式匹配根因,输出受影响的 rank 列表与推荐动作,缩小隔离范围。
- 自愈:按自愈优先级决定动作,可自动恢复的故障进入自动化流程,需人工判断的故障挂起等待。
- 验证:自愈后执行健康验证,验证通过才恢复训练,防止带病续跑。
- 复盘:事件归档进根因库,修正检测阈值与自愈策略,沉淀为回归用例。 自愈优先级与 MTTR 量化目标按「可自动恢复」与「需人工」划分,与前述 MTTR 指标衔接:
- 自动隔离:单 GPU 故障(SBE 超限、DBE、Xid 报错)30 秒内完成隔离与替换,任务回滚最近检查点自动重启, MTTR 目标 < 10 分钟;节点级故障排空后由调度器补充节点,MTTR 目标 < 20 分钟。
- 自动重试:IB 链路 flap、NCCL 超时、checkpoint 写失败等可重试故障自动重试,2-3 次仍失败再升级。
- 人工介入:loss 发散、SDC 疑似、多节点同时故障等,挂起任务并推送根因摘要,人工确认后恢复,MTTR 目标 < 60 分钟。
- 稳定性复盘与改进机制 闭环的长期效果依赖复盘沉淀与持续回归:
- 事故复盘:重大中断后按根因分类归档,根因分硬件、网络、软件、数据四类,与故障空间中的模式对应;复盘产出检 测规则更新、自愈策略修正与回归用例。
- 稳定性回归:对代码、框架版本、集群配置、存储与网络的任何变更,先跑稳定性冒烟(GPU burn、NCCL 带宽、100 步短训练),通过后再放量,防止变更引入新的不稳定。
- 北极星指标追踪:按周记录四项北极星指标趋势,ETTR 或有效训练算力连续两周下降时触发专项治理。 稳定性全景为容错系统设计提供了系统性的故障认知基础。
9.2 容错设计与故障恢复
在万卡训练中,故障不是异常而是常态。本节深入讨论从检测到恢复的全链路容错设计。
9.2.1 容错策略总览
大模型训练中的容错策略可分为检测、恢复与预防三个层级,如图9-3所示。 Heartbeat (heartbeat dete ction) Detection Layer NCCL (timeout monitorin g) Loss (anomaly detection) Checkpoint-Restart Fault Tolerance Strategy Recovery Layer Live Migration Elastic Training Node Blacklisting Prevention Layer Pre-flight (check) Straggler (mitigation) 图9-3 容错策略分层架构
9.2.2 Checkpoint-Restart 模式
这是最基础也是最广泛使用的容错策略。核心思想:定期保存完整训练状态,故障后从最近的检查点恢复。 完整状态保存包含: •模型参数(FP32 master weights + FP16/BF16 working weights) •优化器状态(Adam 的 first moment m、second moment v) •学习率调度器状态(warmup step、decay step) •DataLoader 状态(已消费的样本索引、shuffle 状态) •随机数生成器状态(RNG state for dropout、data augmentation) •分布式通信组状态(各 rank 的 world_size、local_rank 等) PyTorch 原生检查点保存示例:
# Source: checkpoint.py
import torch
import torch.distributed as dist
import os
def save_checkpoint(model, optimizer, scheduler, dataloader, epoch, step, path):
if dist.get_rank() == 0:
checkpoint = {'epoch': epoch, 'step': step, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'scheduler_state_dict': scheduler.state_dict(), 'rng_state': torch.get_rng_state(), 'cuda_rng_state': torch.cuda.get_rng_state_all(),
9.2.3 弹性训练与 TorchElastic
}
torch.save(checkpoint, path)
# Upload to remote storage asynchronously
upload_to_remote(path)
dist.barrier()弹性训练允许训练过程中动态调整节点数量,是现代分布式训练的核心容错能力。 TorchElastic(通过 torchrun 启动)管理一个 worker group,每个 worker 运行训练的单个 rank。当某个 worker 失 败时,TorchElastic 重新启动所有 worker,并从最近的检查点恢复。关键特性:
- Rendezvous 机制:基于 etcd 或 c10d store 的分布式共识,协调 worker 加入/离开。
- Dynamic Ranks:支持 nproc_per_node 的动态调整,节点数变化时透明重新分配 rank。
- Graceful Shutdown:接收到终止信号后完成当前 step 再退出。
# TorchElastic launch command
torchrun --nnodes=min:max --nproc_per_node=8 \
--rdzv_id=my_training_job \
--rdzv_backend=c10d \
--rdzv_endpoint=localhost:29400 \
train.py --checkpoint_dir=/mnt/checkpointsPyTorch Elastic 集成示例:
Source: train_elastic.py
from torch.distributed.elastic.multiprocessing.errors import record @record # Capture and record worker errors
def main():
dist.init_process_group(backend="nccl")
# ... training logic
load_checkpoint_if_exists(checkpoint_dir)
if __name__ == "__main__":
main()弹性训练的局限:当节点数量变化时,全局批次大小改变,需要调整学习率(linear scaling rule)。此外,DataLoader 的 sharding 需要重新计算。
9.2.4 流水线并行与容错交互
在万卡训练中,流水线并行(Pipeline Parallelism)是最常见的并行维度之一,但它引入了固有的计算气泡(Pipeline Bubble),在容错分析中必须加以考虑。 •气泡率公式:使用 GPipe/1F1B 调度方案时,稳态气泡率为 (p − 1)/(m + p − 1),其中 p 为流水线级数,m 为每步的微 批次数。以 PP=8、m=16 为例,气泡率 = 7/23 ≈ 30.4%。要将气泡率控制在 5% 以内,需要 m ≥ 19(p − 1),即 m ≥ 133 个微批次。 •Interleaved 1F1B 优化:Megatron-LM 将每个 stage 拆为 v 个虚拟 stage,气泡率降低为 ⋅ 。当 v = 2 时,上 1 p−1 例气泡率从 30.4% 降至约 15.2%,但每个 rank 的通信量增加 v 倍,IB 带宽受限时需权衡。 v m+p−1 •容错与气泡的交互:当流水线中某个 stage 的节点故障并触发重启时,整个流水线必须停止等待,哪怕只有 1 个 stage 故障,也会使所有 p 个 stage 浪费计算资源。这意味着 PP 越大,单点故障的影响面越大。Meta 在 Llama-3 训练中将 PP 控制在 8-16 之间,并通过 TP=8 补偿以减小每个 PP stage 的节点数,从而降低故障影响面积。
9.2.5 Straggler 检测与缓解
Straggler(慢节点)是指在集体通信操作中持续表现异常的节点,严重影响集群吞吐量。 检测方法:
- AllReduce 耗时监控:记录每次 AllReduce 操作各 rank 的完成时间,识别持续位于 P99 的 rank。
- GPU 健康指标:监控 HBM 带宽(通过 dcgm-exporter 暴露)、PCIe 带宽、NVLink 链路速度。
- 计算迭代耗时:通过 PyTorch Profiler 记录每个 step 的 forward/backward 耗时,定位异常 rank。
Use DCGM to monitor GPU
dcgmi diag -r 3 # Level 3 diagnostic (medium tests)
Check metrics: PCIe bandwidth
缓解策略:
- Blacklist 机制:连续 N 次迭代中表现最差的节点自动标记为不可用,训练任务自动排除该节点。Meta 的 Trainer 框架 内置了基于 Z-score 的 blacklisting。
- Partial Reduction:在 AllReduce 中,如果有少量 rank(例如 < 5%)超时,则放弃等待这些 rank,仅对已完成 rank 做 reduce。需要容忍由此引入的梯度偏差。
- Backup Workers:启动比实际需求更多的 worker(例如 1.05x),straggler 的梯度被忽略,由其余 worker 完成操 作。Google 的 TensorFlow 支持 backup workers 模式。
- 异步 AllReduce + bounded staleness:为 AllReduce 设置超时阈值,超时后以已收到的 rank 结果进行规约(允许少 量 rank 跳过本步),straggler 在下一步正常完成后自动恢复。注意这会引入梯度陈旧(staleness)的问题,需配合收 敛性分析使用。
9.2.6 自动重启策略
生产环境的自动重启策略需要平衡训练连续性和资源利用率:
Source: auto_restart_policy.py
AUTO_RESTART_CONFIG = { "max_restarts": 10, # Maximum number of automatic restarts "restart_window_minutes": 120, # Time window "backoff": { "initial_delay": 60, # Initial restart delay (seconds) "max_delay": 600, # Maximum delay "multiplier": 2.0, # Exponential backoff factor }, "escalation": { "3_restarts": "notify_oncall_engineer", "5_restarts": "notify_training_team", "10_restarts": "pause_training_and_notify_manager", }, "blacklist_nodes_on": { "gpu_errors": 3, # Blacklist node after 3 GPU errors "nccl_timeouts": 2, # Blacklist after 2 NCCL timeouts
9.2.7 Meta 的训练鲁棒性实践
}} Meta 在 Llama 系列模型训练中建立了完善的容错体系:
- Pre-flight 检查:训练启动前运行 nccl-tests 验证节点间通信带宽、运行 gpu-burn 检测 GPU 稳定性、执行端到 端的迷你训练(10 steps)验证完整数据路径。
- 动态工作组:Training 集群中的节点被划分为候选池,启动时从池中选取健康的节点组成训练组。故障节点自动替换, 非故障节点保持训练。
- Stateful Recovery:检查点不仅保存模型状态,还保存 DataLoader 的全局顺序。恢复后精确重放数据,保证确定 性。
- 冗余梯度校验:AllReduce 完成后,对每个 rank 的规约结果计算 SHA256 摘要,通过 AllGather 汇总所有 rank 的摘要 并比对,若存在不一致则判定本次通信出现数据损坏,触发检查点回滚。(注意:校验应在 AllReduce 之后进行,比对 各 rank 是否持有相同的规约结果;若在 AllReduce 之前只对本地梯度做摘要,只能检测本地计算错误,无法检测通信 引入的损坏。) 容错设计的下一步是分布式检查点系统的工程实现。
9.3 分布式检查点系统
对于万亿参数级别的模型,检查点的保存和恢复已经成为训练工程中的核心瓶颈。本节讨论大规模分布式检查点系统的设 计。
9.3.1 检查点规模的工程现实
以 Llama-2 70B 为例,完整检查点的存储需求如表9-4所示。 表9-4 Llama-2 70B 完整检查点存储需求 组件 大小(FP32) 说明 模型参数 280 GB 70B × 4 bytes 优化器状态(Adam) 840 GB FP32 主权重 + 一阶矩 m + 二阶矩 v,各 70B × 4 bytes(× 3) 总计 约1.12 TB 单个检查点 × 保留 5 个检查点 约5.6 TB 生产环境典型保留策略 对 1TB 模型(如 Llama-3 405B),完整检查点约需 6.5 TB。在 1000 节点集群中,如果所有 rank 向共享存储写入,I/O 压力急剧放大。
9.3.2 同步检查点
同步检查点(Barrier-based Checkpointing)是最简单也是最可靠的方案: Step N completes ▶ All Rank Barrier ▶ All Rank save simultaneously ▶ Barrier confirm ▶ Step N+1 starts 优点:实现简单,确定性高,恢复时状态一致性强。 缺点:
- Stalling Overhead:写检查点期间训练完全停止。对于 70B 模型写 1.12TB 数据到 HDD 共享存储,单次检查点约需 10-30 分钟。
- I/O 风暴:N 个 rank 同时写入存储,峰值带宽可能超过存储系统能力,导致 IOPS 饱和和长尾延迟。
- 存储带宽瓶颈:NFS/HDFS 共享存储的聚合写入带宽通常在 10-50 GB/s,而千卡集群的理论写入需求可达数百 GB/s。
9.3.3 异步检查点
异步检查点通过将数据暂存到本地高速介质,解耦训练迭代和持久化写入,其时序关系如图9-4所示。 Training Loop NVMe/Memory Staging "Remote Storage (S3/HDFS)" Step N: async copy model+optimizer state Immediately continue training Step N+1 Background continuous upload Step N+1, N+2, ... Notify after upload completes Mark checkpoint as available Training Loop NVMe/Memory Staging "Remote Storage (S3/HDFS)" 图9-4 异步检查点时序 实现关键:
- Persistent Staging Area:每节点配置 2-4 块 NVMe SSD(总计 4-8 TB),作为检查点的本地缓存。
- 内存映射 mmap 写入:使用 mmap 将模型状态直接映射到 NVMe 文件系统,GPU-to-NVMe 可通过 GPUDirect Storage (GDS) 实现零拷贝传输。
- 后台上传线程:独立的后台线程池将 NVMe 缓存中的检查点分块上传到 HDFS/S3。
- 流水线保护:至少保留双缓冲(ping-pong buffer),确保正在写入的缓冲区和正在上传的缓冲区互不干扰。 NCCL Checkpointer(通信流水线式异步保存): 与「本地暂存 + 后台上传」的思路不同,NCCL Checkpointer 直接在 GPU 间通过 NCCL 集合通信,把各 rank 的权重/优 化器状态搬运到专用接收进程(staging 节点),再写入持久化存储。其核心是把「拷贝到远端」变成可异步完成的 NCCL 传输,与下一批次的训练重叠,从而消除同步检查点的全局 barrier 停顿,也避免了 NVMe 本地暂存后再上传的二次拷 贝。Megatron-LM 的 --async-save 与 NVIDIA NeMo 即采用该机制,实测在 70B 规模可将检查点写盘对训练步的阻塞 降至毫秒级。局限是需要额外的 staging GPU/CPU 节点与充足 RDMA 带宽,且恢复语义仍依赖写入完成标记 (_SUCCESS)保证原子性。 DGX 节点典型配置: •8× H100 GPU(640 GB HBM per node) •8× 3.84 TB NVMe SSD(通过 GDS 直通 GPU) •GPU-to-NVMe 带宽:每 GPU 约6 GB/s(PCIe Gen5 ×4 per NVMe)
9.3.4 分片检查点
分片检查点是分布式检查点的主流方案:每个 rank 只保存自己负责的模型分片,避免全量数据集中到 rank 0。 PyTorch Distributed Checkpoint (DCP):
# Source: PyTorch DCP save example
import torch.distributed.checkpoint as dcp
from torch.distributed.checkpoint.state_dict import get_model_state_dict
def save_sharded_checkpoint(model, optimizer, step):
state_dict = {"model": get_model_state_dict(model), "optimizer": optimizer.state_dict(),
}
# dcp.save automatically handles sharding, each rank writes its own part
dcp.save(
state_dict=state_dict,
checkpoint_id=f"checkpoint-step-{step}",
storage_writer=dcp.FileSystemWriter("/mnt/checkpoints"),) # Sync after all ranks complete writing torch.distributed.barrier() Sharded Checkpoint 文件结构: checkpoint-step-1000/ ├── .metadata # Global metadata (DTensor layout info) ├── __0_0.distcp # Rank 0 shard ├── __1_0.distcp # Rank 1 shard ├── __2_0.distcp # ... ├── ... └── __999_0.distcp # 1000th rank shard 优势: •零串行化开销:rank 0 不再需要收集所有数据,写入带宽与 rank 数量线性扩展。 •恢复时并行读取:每个新 rank 只读取自己需要的分片。 •重分片恢复:支持从 N 个分片的检查点恢复到 M 个分片(通过 DTensor redistribute)。 FSDP 检查点的工程注意事项 PyTorch FSDP(Fully Sharded Data Parallel)的检查点接口与普通 DDP 有显著差异。FSDP 默认将同一分组内的所有参 数压平(flatten)成一个大的 FlatParameter,优化器状态也对应这个 FlatParameter,因此直接调用 model.state_dict() 返回的是 flat 版本,无法直接用于从不同并行度恢复。 FSDP 提供三种 StateDictType 控制检查点格式: •LOCAL_STATE_DICT:每个 rank 保存自己的 flat shard(最快,但不可跨配置恢复)。 •FULL_STATE_DICT:rank 0 聚合所有 shard 得到完整 unflat 权重(兼容性最好,但需将整个模型加载到单卡,内存 压力大)。 •SHARDED_STATE_DICT:每 rank 保存 unflat shard(推荐,结合 DCP 使用)。 import torch.distributed.fsdp as fsdp
Recommended: SHARDED_STATE_DICT
with fsdp.FullyShardedDataParallel.state_dict_type( model, fsdp.StateDictType.SHARDED_STATE_DICT ): sharded_sd = model.state_dict() 生产建议:万卡训练推荐 SHARDED_STATE_DICT 配合 PyTorch DCP,既避免 FULL_STATE_DICT 的 rank 0 内存瓶颈, 又保留跨并行度恢复(re-sharding)的灵活性。从 PyTorch 2.1 起, torch.distributed.checkpoint.save 原生支持 FSDP 的 SHARDED_STATE_DICT,无需手动处理 FlatParameter 映射。
9.3.5 增量检查点
增量(Incremental)检查点只保存从上次检查点以来的变化,适合高频检查点场景。 对于优化器状态(Adam 动量),相邻检查点之间大部分参数变化极小。使用 XOR/delta 编码压缩变化量,可实现 10- 100x 压缩比。但增量检查点有以下挑战: •增量链累积导致恢复延迟增加(需要回放所有增量)。 •基检查点损坏导致后续所有增量不可用。 •对于模型参数(训练中持续大幅更新),增量压缩效果有限。 生产实践建议采用「全量 + 增量」混合策略:每 1000 步保存全量检查点,每 100 步保存增量检查点。
9.3.6 DeepSeek 3FS 检查点设计
DeepSeek 为 V3/R1 训练自研的 3FS(Fire-Flyer File System)对检查点做了深度优化:
- 客户端直连:3FS 实现 FUSE 客户端绕过内核 VFS 层,直接从应用层将数据写入分布式存储节点。结合 RDMA,单客户 端写入带宽可达 40 GB/s。
- 条带化写入:每个检查点文件被切分为固定大小的 chunk(64 MB),跨越多个存储节点并行写入。256 个 rank 同时写 检查点,总写入带宽可达 2-3 TB/s。
- 版本化目录:检查点目录名包含 step 号和写入时间戳,支持快速回滚和 A/B 比较。
- 自动过期清理:3FS 后台任务自动清理过期的检查点,释放存储空间。
- 写入确认语义:所有 rank 写完各自的分片后,写入一个 _SUCCESS 标记文件,确保检查点的原子性。读取时先检查标 记文件是否完整。 与传统 NFS 检查点相比,3FS 的写性能与阻塞行为差异如表9-5所示。 表9-5 传统 NFS 与 3FS 检查点对比 对比维度 传统 NFS 检查点 3FS 检查点 单节点写入带宽 约1 GB/s 约40 GB/s 256 节点总写入带宽 约50 GB/s (瓶颈在服务器) 约2-3 TB/s 检查点写入时间 (1TB 模型) 约10-20 分钟 < 1 分钟 写入阻塞训练 是 否(异步) 存储利用率 约60% 约85% 检查点系统的优化是提升训练有效利用率的关键杠杆。
9.4 大规模数据加载器设计
数据加载是大规模训练的「隐形瓶颈」,当 GPU 算力达到百 PFLOPS 级别时,如果不能持续喂入数据,昂贵的算力资源 将被迫空转。
9.4.1 数据加载的规模化挑战
万卡训练对数据加载系统提出极高要求: 吞吐量需求:假设训练 Llama-3 405B,全局批次大小 16M tokens,每 token 约 4 bytes(int32 token ID;PyTorch DataLoader 通常以 int32/int64 存储),每个 step 需要约 64 MB 数据。如果训练速度为 1 step/s(经过充分优化的万卡 集群),每秒读取 64 MB。但考虑以下因素: •数据预处理:tokenization 将每个样本放大 3-5x(images → tokens) •多模态数据:视频、音频训练需数百 MB/s 的读取 •多 epoch 训练:shuffle 后重新读取要求全量 I/O 对于一个存储 100TB 训练数据的数据中心,随机访问(shuffle 训练)的 IOPS(每秒输入输出操作数)需求可达 10K- 100K IOPS。数据加载的完整数据流路径如图9-5所示。 Remote Object Storage Network I/O Local SSD Cache File I/O DataLoader Workers CPU to GPU Copy GPU HBM Forward Compute Training Step 图9-5 数据加载数据流路径 离线分片预处理(Sharded Preprocessing): 训练前的 tokenization、去重、过滤等重 I/O 步骤在独立的数据平台预处理作业中一次性完成,产出按 shard 切分、可直 接加载的二进制数据。训练时 DataLoader 仅消费已就绪的 shard,退化为纯读取,不再承担 tokenize、解码等 CPU 重 活,从而降低 CPU 需求并消除预处理抖动对 GPU 空转的影响。
9.4.2 数据加载器工程优化
生产环境合理配置 worker 数与预取深度即可隐藏 I/O 延迟,避免 GPU 饥饿。 Worker 崩溃处理:
# Source: robust_dataloader.py
import time
from torch.utils.data import DataLoader
class RobustDataLoader:
def __init__(self, dataset, max_retries=3, **kwargs):
self.dataset = dataset
self.max_retries = max_retries
self.kwargs = kwargs
self._loader = None
def __iter__(self):
for attempt in range(self.max_retries):try: self._loader = DataLoader(self.dataset, **self.kwargs) yield from self._loader break except RuntimeError as e: if attempt == self.max_retries - 1: raise print(f"DataLoader crashed, retrying... ({e})") time.sleep(10) Prefetch Pipeline 设计: 有效的预取管线需要将 I/O、CPU 预处理、GPU 拷贝流水线化:
# Source: prefetch_pipeline.py
import threading
from queue import Queue
class PrefetchPipeline:
def __init__(self, dataset, prefetch_depth=3):
self.dataset = dataset
self.queue = Queue(maxsize=prefetch_depth)
self.thread = threading.Thread(target=self._prefetch, daemon=True)
self.thread.start()
def _prefetch(self):
for sample in self.dataset:
self.queue.put(sample)
def __next__(self):
return self.queue.get(timeout=60) # Timeout indicates data starvation数据加载是整个训练工程中与计算、通信并列的第三大效率维度。
9.5 混合精度训练工程化
混合精度训练是现代大模型训练的标配:使用低精度(FP16/BF16/FP8)执行前向和反向传播,保持高精度(FP32)的 主权重副本。本节从格式选择到生产配置,系统讲述混合精度训练的工程实践。
9.5.1 数字格式基础
大模型训练涉及的主要浮点格式及其特性如表9-6所示。 表9-6 混合精度浮点格式对比 格式 符号位 指数位 尾数位 动态范围 精度特点 典型用途 FP32 1 8 23 ±3.4e38 单精度基线,7.2 位十进制精度 主权重、优化器状态 FP16 1 5 10 ±65504 尾数足但动态范围窄,易上下溢 前向、反向计算 BF16 1 8 7 ±3.4e38 与 FP32 同动态范围,尾数砍半 前向、反向计算 TF32 1 8 10 ±3.4e38 输入截尾到 10 位,累加 FP32 训练矩阵乘中间精度 FP8 E4M3 1 4 3 ±448 前向传播常用,精度最粗 Transformer Engine 前向 FP8 E5M2 1 5 2 ±57344 反向传播常用,范围大精度更粗 Transformer Engine 反向 BF16 vs FP16 的关键区别: BF16 与 FP32 具有相同的指数范围(8 位指数),因此不需要 loss scaling。但由于尾数仅 7 位,舍入误差较大。对于训 练收敛,指数范围比尾数精度更重要,这正是 BF16 取代 FP16 成为大模型训练主流格式的原因。 FP16 的 5 位指数范围仅约 10^±4.8,梯度值极易下溢为 0,必须配合 loss scaling 使用。
9.5.2 FP8 与 Transformer Engine
NVIDIA H100 引入了 FP8 Tensor Core,通过 Transformer Engine 库实现 FP8 训练。 FP8 的两种子格式: •E4M3(4 指数位,3 尾数位):精度更高,用于前向传播(需要更精确的激活值) •E5M2(5 指数位,2 尾数位):动态范围更大,用于反向传播(梯度值范围变化大)
- 延迟缩放(Delayed Scaling): FP8 的核心挑战是其极窄的动态范围。Transformer Engine 采用延迟缩放策略,用上一步收集的 amax 历史推算当前缩 放因子,其流程如图9-6所示。 Forward Scaling Manager Backward Step N: use previous scaling factor Collect max(abs) of activations/gradients Compute amax history for current tensor Update scaling factor: new_scale = max_val / FP8_MAX Step N: use the same scaling factor Prepare updated scaling factor for Step N+1 Forward Scaling Manager Backward 图9-6 FP8 延迟缩放策略时序
- FP8 量化粒度 FP8 的量化粒度对精度与吞吐量有重要影响,分为两种主要方案: 张量级缩放(Tensor-wise Scaling)是 Transformer Engine 早期版本采用的方案:对整个权重矩阵或激活张量使用同一 个缩放因子(amax)。实现简单、元数据开销可忽略,但当张量内部数值分布高度不均匀(如 Transformer 中常见的激 活异常值 outlier)时,单一缩放因子会导致其余元素的有效精度大幅下降。 块级缩放(Block-wise Scaling)是 NVIDIA Blackwell(GB200)FP8 实现及 DeepSeek-V3 所采用的进阶方案: DeepSeek-V3 采用细粒度 tile 量化,权重矩阵按行方向每 128 列为一组(1×128 tile),激活矩阵按列方向每 128 行为一 组(128×1 tile),每个 tile 独立计算并保存一个 FP32 缩放因子。相比张量级缩放,块级缩放通常额外引入约 0.4% 的元 数据存储开销,但可显著降低精度损失,尤其对含 outlier 的激活值(如 Q/K/V 投影输出)效果明显。DeepSeek-V3 技术 报告指出,采用块级 FP8 量化后,相比 BF16 基线的 loss 差距控制在 0.25% 以内,同时训练吞吐量相较 BF16 显著提 升。 实际选型建议:权重量化用 tensor-wise 即可(分布平滑);激活量化(Q/K/V)建议 block-wise(存在 outlier);梯度 量化用 tensor-wise + E5M2(动态范围更大);MoE 模型 expert 激活方差更大,建议 block-wise 或回退 BF16。
# Source: Transformer Engine FP8 training configuration
import transformer_engine.pytorch as te
from transformer_engine.common.recipe import Format, DelayedScaling
# FP8 training configuration
fp8_format = Format.HYBRID # E4M3 forward, E5M2 backward
fp8_recipe = DelayedScaling(
fp8_format=fp8_format,
amax_history_len=16, # Save 16 steps of amax history
amax_compute_algo="max", # Take the historical maximum
margin=0, # Scaling margin)
9.5.3 Loss Scaling 机制
Replace standard Linear with TE Linear
model = te.Linear(in_features, out_features, bias=True) Loss scaling 是 FP16 训练的关键技术:通过将 loss 乘以一个大的缩放因子,将小梯度值「推入」FP16 的可表示范围。
- 动态 Loss Scaling:
# Source: torch.cuda.amp GradScaler
scaler = torch.cuda.amp.GradScaler(
init_scale=2.**16, # Initial scale 65536
growth_factor=2.0, # Growth factor every N steps
backoff_factor=0.5, # Backoff factor when overflow is detected
growth_interval=2000, # Grow after 2000 steps without overflow
enabled=True,) for data, target in dataloader: with torch.cuda.amp.autocast(dtype=torch.float16):
output = model(data)
loss = criterion(output, target)
# Backward: gradient x scale
scaler.scale(loss).backward()
# Gradient unscaling + overflow detection
scaler.unscale_(optimizer)
# Gradient clipping (unscaled gradients)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# Update parameters, skip if overflow
scaler.step(optimizer)
# Update scaling factor
scaler.update()- 静态 Loss Scaling:对于 BF16,由于不需要 loss scaling,设置 init_scale=1.0 并使 scaler 处于禁用状态即可。 Loss Scale 的选择与模型规模相关,推荐取值如表9-7所示。 表9-7 Loss Scale 选择指南 模型规模 推荐初始 Scale 说明 < 1B params 2^16 (65536) 标准 FP16 配置 1-10B params 2^24 (约16M) 梯度范数增大
10B params 2^28 (约268M) 或 BF16 或用 BF16 避免 loss scaling MoE 模型 BF16 Expert routing 对精度敏感
9.5.4 自动混合精度
PyTorch 的 torch.cuda.amp 提供自动混合精度:
# Source: Complete AMP training loop
model = MyLargeModel().cuda()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
# BF16 does NOT need GradScaler (no overflow risk unlike FP16)
# Use enabled=False or remove scaler entirely for BF16
scaler = torch.cuda.amp.GradScaler(enabled=False)
for epoch in range(num_epochs):
for batch in dataloader:
optimizer.zero_grad()
# autocast context: auto-convert to BF16with torch.cuda.amp.autocast(dtype=torch.bfloat16):
output = model(batch)
loss = criterion(output, batch.labels)
# scaler.scale(loss).backward() is a no-op when enabled=False
# equivalent to: loss.backward()
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()哪些操作不应进入 autocast: •Softmax 和 LayerNorm:对精度敏感,应保持在 FP32 •Loss 计算:使用 FP32 保证梯度质量 •Attention 的 QK 乘积:BF16 下精度尚可,FP16 需关注 softmax 溢出 PyTorch 默认的 autocast 策略(可通过 torch.get_autocast_gpu_dtype() 查看当前 autocast 的 GPU dtype): •Linear、Conv、MatMul → BF16/FP16 •LayerNorm、Softmax、Loss → FP32 •Embedding → FP32(查找表精度要求)
9.5.5 精度与吞吐量权衡
混合精度训练的显存占用比例大致如下: Precision allocation strategy (memory ratio): ┌─────────────────────────────────────────────────────────┐ │ FP32 Master Weights: ~25% │ ├─────────────────────────────────────────────────────────┤ │ BF16 Working Weights: ~25% │ ├─────────────────────────────────────────────────────────┤ │ FP32 Optimizer States: ~50% │ ├─────────────────────────────────────────────────────────┤ │ BF16 Activations: ~10-20% (varies with sequence length)│ └─────────────────────────────────────────────────────────┘ 不同精度配置在 A100 80GB 上训练 Llama-2 7B 单 GPU 的性能对比如表9-8所示。 表9-8 精度配置性能对比 配置 吞吐量 (tokens/s) HBM 用量 收敛能力 FP32 2,000 约42 GB 最佳 FP16 + Loss Scaling 6,000 约22 GB 良好 BF16 6,000 约22 GB 良好 配置 吞吐量 (tokens/s) HBM 用量 收敛能力 FP8 (H100 only) 12,000 约14 GB 需要调优 注:上表「HBM 用量」列仅计算模型权重加少量激活值,未含 FP32 优化器状态。以 7B 模型 BF16 混合精度全量 训练为例,还需额外约 84 GB(FP32 主权重 28 GB + Adam 一阶矩 28 GB + 二阶矩 28 GB),合计约 98 GB 已超过 A100 80GB 容量,因此单卡全量训练 7B 实际需配合 ZeRO-2/3 或 CPU offload,表中数字反映的是权重级显存占 用而非完整训练显存。 FP8 训练的实际效果:NVIDIA 报告在 H100 上使用 FP8 训练 GPT-3 175B,吞吐量相比 BF16 提升约 1.8x。但 FP8 训练的 最终 loss 收敛质量需要仔细验证,部分模型报告有 0.1-0.5pp 的精度损失。 混合精度训练的配置选择直接影响训练效率,与在线评估监控形成闭环。
9.6 优化器内存分析与选型
优化器状态是训练显存的最大单一消费者。在混合精度训练中,模型权重以 FP16/BF16 存储,但优化器需要维护 FP32 的主权重副本和动量统计量,其显存占用通常是模型权重的 3-6 倍。选择正确的优化器和优化器配置,是在给定硬件上成 功训练大模型的先决条件。
9.6.1 主流优化器的内存模型
- SGD(Stochastic Gradient Descent): 最简单的优化器,无动量时仅需维护一份 FP32 主权重副本: SGD Memory (no momentum): params × 4 bytes (FP32 master weights only) SGD Memory (with momentum): params × 8 bytes (master weights + momentum buffer) SGD 内存效率最高,但收敛速度慢、对学习率和 batch size 敏感,在大模型训练中已基本被 Adam 系列取代。
- Adam / AdamW: Adam(Kingma & Ba, ICLR 2015)和 AdamW(Loshchilov & Hutter, ICLR 2019)是 LLM 训练的标配优化器。每个参数 维护两个 FP32 动量缓冲区: Adam Memory = params × 4 (FP32 master weights)
+ params × 4 (first moment m_t, FP32)
+ params × 4 (second moment v_t, FP32)
= params × 12 bytes以 Llama-2-70B(BF16,70B × 2 = 140 GB 权重)为例,其显存明细如表9-9所示。 表9-9 Llama-2-70B 优化器显存明细 组件 大小 说明 BF16 模型权重 140 GB 前向/反向传播使用 FP32 主权重 280 GB 优化器更新目标 Adam m(一阶矩) 280 GB 梯度指数移动平均 Adam v(二阶矩) 280 GB 梯度平方指数移动平均 优化器总计 840 GB 不含梯度和激活值 训练总显存(未分片) 约1 TB 含梯度 + 激活值约 100-200 GB 840 GB 的优化器状态远超单 GPU 显存(H100 80 GB),这就是为什么 ZeRO-1(优化器状态分片)和 FSDP 是必不可少 的。 3) Adafactor(Shazeer & Stern, ICML 2018): Adafactor 通过低秩分解将二阶矩 v 的存储从 O(n) 降至 O( n): Adafactor Memory (default, no first moment): ≈ params × 4 (FP32 master)
- O(√params) (second moment, factorized) ≈ params × 4-5 bytes Adafactor Memory (with first moment, beta1 > 0): ≈ params × 4 (FP32 master)
- params × 4 (first moment, FP32) ◀ optional, disabled by default
- O(√params) (second moment, factorized) ≈ params × 8 bytes 注意:Adafactor 原论文的核心设计之一是默认不使用一阶动量(beta1=None),以进一步节省显存。生产部署(PaLM- 540B、T5-11B)通常采用无一阶动量的版本,此时显存约 4-5 字节/参数,而非 8 字节。含一阶动量时约 8 字节,与表格 数据一致。相比 Adam 的 12 字节,无动量 Adafactor 节省约 60% 的优化器显存(而非仅 33%)。对于 70B 模型,无动 量 Adafactor 将优化器状态从 840 GB 降至约 280-350 GB。
- LAMB(You et al., ICLR 2020): LAMB 是为大批量训练(batch size ≥ 32K)设计的层级自适应优化器,将每层的学习率按权重范数与梯度范数的比例自 适应缩放。常用于需要极端 batch size(如万卡级数据并行)的场景,内存占用与 Adam 相同。
9.6.2 优化器内存对比
主流优化器的内存占用、收敛速度与适用场景对比如表9-10所示。 表9-10 主流优化器内存对比 优化器 每参数字节数 70B 模型优化器显存 相对 SGD 收敛速度 典型适用场景 SGD (无动量) 4 280 GB 1× 最慢 微调、蒸馏 SGD + Momentum 8 560 GB 2× 慢 CV 模型,LLM 不常用 Adam / AdamW 12 840 GB 3× 快(标配) 预训练、SFT Adafactor 约8 约560 GB 2× 中等 超大规模预训练(≥ 500B) LAMB 12 840 GB 3× 快(大批量) 万卡级 DP 训练 9.6.3 8-bit 优化器量化状态 8-bit Adam(Dettmers et al., 2022, bitsandbytes 库):将 Adam 的动量缓冲区从 FP32 量化为 INT8,通过逐块量化 (block-wise quantization),默认将每 2048 个参数分为一个 block(长训练推荐 block_size=64 以提升精度稳定性), 每个 block 使用独立的 scale,在几乎不损失收敛质量的前提下将优化器显存压缩约 2×: 8-bit Adam Memory ≈ params × 4 (FP32 master) + params × 1 (m_t, INT8) + params × 1 (v_t, INT8) ≈ params × 6 bytes 对 70B 模型,8-bit Adam 将优化器状态从 840 GB 降至 约420 GB。配合 FSDP/ZeRO-3 使用时,可将同规模 GPU 下的可 训练模型规模扩大约 40%。
# Source: bitsandbytes 8-bit Adam
import bitsandbytes as bnb
# Replace standard AdamW with 8-bit Adam
optimizer = bnb.optim.AdamW8bit(
model.parameters(),
lr=1e-4,
betas=(0.9, 0.95),
weight_decay=0.1,) 4-bit 优化器(LPMM, 2024)进一步将优化器状态降至 4-bit 精度,结合 group-wise quantization(group_size=64), 理论将 Adam 的动量缓冲区压缩至 0.5+0.5 bytes(各4-bit)/param,总优化器内存约 params × 5 bytes(4字节 FP32 master + 0.5字节 INT4 m + 0.5字节 INT4 v;不计量化scale开销约约5.1字节)。当前 4-bit 优化器在训练稳定性上仍需额 外验证,尚未成为生产级默认方案。
9.6.4 融合优化器
融合优化器(Fused Optimizer)将优化器的参数更新步骤(梯度缩放、动量更新、权重衰减、参数更新)在内核层面合 并为单次 GPU 运算,减少 CPU-GPU 同步和内核启动开销:
# Source: PyTorch Fused AdamW
import torch
# Standard AdamW: multiple kernel
optimizer_std = torch.optim.AdamW(model.parameters(), lr=1e-4, fused=False)
# Fused AdamW: single fused
optimizer_fused = torch.optim.AdamW(model.parameters(), lr=1e-4, fused=True)融合优化器的收益: •优化器 step 时间降低 10-20%(在 7B-70B 规模模型上测量) •峰值显存减少约 5-10%(省去梯度缩放等中间张量的分配) •PyTorch 2.0+ 通过 fused=True 参数原生支持融合 Adam/AdamW( torch.optim.AdamW(fused=True) ) •NVIDIA Apex 的 FusedAdam 是较早的实现,但当前推荐直接使用 PyTorch 原生融合版本
9.6.5 优化器状态卸载
当 GPU 显存不足以容纳优化器状态时,可以将部分或全部优化器状态卸载到 CPU DRAM: ZeRO-Offload(Ren et al., USENIX ATC 2021): 将优化器状态(FP32 master + m + v)与优化器 step 计算整体卸载到 CPU,GPU 仅保留 BF16 权重。卸载后优化器状态 完全移出 HBM,GPU 侧只需容纳权重、梯度与激活: GPU HBM: BF16 weights ~140 GB (70B model) Gradients (temporary) ~140 GB (freed after optimizer step) Activations checkpoint ~variable CPU DRAM: FP32 master weights ~280 GB Adam m ~280 GB Adam v ~280 GB Total optimizer state ~840 GB 是否启用卸载取决于显存预算:ZeRO 分片与 8-bit 优化器优先消化优化器状态的显存压力,当两者仍无法把优化器状态 压进 GPU 预算时才考虑卸载,以 CPU↔GPU 的 PCIe 往返换容量,是显存兜底手段而非首选方案。 DeepSpeed ZeRO-Infinity(Rajbhandari et al., SC 2021)将卸载范围扩展到 NVMe SSD,但 SSD 的延迟和带宽(约7 GB/s 顺序读)意味着仅适用于超大模型(≥ 500B)在极端硬件约束下的训练,更多是可行性证明而非日常方案。
9.6.6 优化器选型的决策框架
优化器的选择由模型规模、显存预算与训练规模共同决定,决策流程如图9-7所示。 Optimizer Selection Model Size? < 7B 7B-70B > 70B AdamW FP32, ZeRO-1 or F GPU HBM Budget? Training Scale? SDP Sufficient (full AdamW fits) Tight Insufficient Standard (128-1024 GPUs) Large Scale (4096+ GPUs) Extreme Scale (10K+ GPUs) AdamW FP32 + FSDP/ZeRO 8-bit Adam + FSDP/ZeRO-3 8-bit Adam + ZeRO-Offload AdamW FP32 + ZeRO-3 Adafactor + ZeRO-3 LAMB + ZeRO-3 -3 Training Stable? Yes No, Loss Spikes No, OOM Try: Reduce LR / Add Gradi Increase DP / Enable Offlo Use Current Config ent Clipping / Switch to Ad ading / Reduce Batch Size amW 图9-7 训练优化器选型决策树 核心选型原则:
- 预训练标配:AdamW(β1=0.9, β2=0.95, ε=1e-8, weight_decay=0.1),兼顾收敛速度和稳定性
- 显存超限:优先尝试 8-bit Adam( bitsandbytes ),2.5× 压缩比且几乎无损
- 超大模型:Adafactor 是已被大规模验证的替代方案
- 微调/SFT:SGD 可节省 3× 优化器显存,但需要更精细的学习率调优
- 万卡级 DP:LAMB 的层级自适应在大 batch 下优于 AdamW 的全局学习率策略 优化器选择与 ZeRO 策略深度耦合:选择了优化器即决定了优化器状态的规模,进而决定了需要 ZeRO-1(分片优化器状 态)、ZeRO-2(分片优化器+梯度)还是 ZeRO-3(分片全部)来满足显存约束。 优化器状态的规模也直接决定了检查点写入量和生产环境的配置参数。
9.7 训练评估与模型评测
评估体系回答「训练跑得正不正常」与「模型够不够好」两个问题,分别由在线监控与模型评测承担。本节先介绍训练过 程中的可观测性与异常检测,再展开离线模型评测的基准、流程、基础设施与评测工程。
9.7.1 评测体系概览
训练评估由在线监控与模型评测两部分构成。在线监控观测训练过程的实时状态(Loss、吞吐、硬件健康),回答「训练 跑得正不正常」;模型评测量化模型输出的能力水平,回答「模型够不够好」。二者互补,评测结果用于触发训练调整(如 数据配比、超参)、决定是否发布、以及对比候选方案。 评测贯穿模型生命周期:预训练阶段用少量通用基准周期性抽查训练方向是否偏离;对齐阶段用对话与指令基准验证 SFT/RLHF 效果;上线前用全量基准与人工评估把关;上线后用在线指标持续监控。不同环节对评测的时效性、成本与指 标口径要求不同。 一次完整评测涉及四要素:评测集(题目与参考答案)、评测协议(推理设置与评估方式)、评估指标(准确率、胜率、人 类偏好等)与评测基础设施(算力、编排、缓存)。四者的选择直接影响评测结论的可信度。
9.7.2 训练可观测性框架
训练可观测性从性能指标、训练动力学与模型质量三个维度展开,如图9-8所示。 Training Observability Performance Metrics Training Dynamics Model Quality Throughput: tokens/s/GP MFU: Model FLOPs Utilizati Comm Overhead Ratio Data Loading Wait Time Gradient Norm Trajectory Activation Outliers Attention Entropy Layer-wise Loss Training Loss Validation Perplexity Benchmark Score U on 图9-8 训练可观测性三维度
9.7.3 核心性能指标监控
MFU(Model FLOPs Utilization) 是衡量训练效率的黄金指标: 实际 FLOPs/s MF U = 理论峰值 FLOPs/s 对于 A100(312 TFLOPS BF16),单个 GPU 训练 Llama-2 7B 时: •单步有效计算量 ≈ 54 TFLOPs(forward ≈ 18T + backward ≈ 36T,比例约 1:2) •单步耗时 ≈ 0.31 秒 •MFU ≈ 54 / (312 × 0.31) ≈ 54 / 96.7 ≈ 55.8% ≈ 55% 万卡训练中 MFU 通常降至 30-45%,主要由通信开销、流水线气泡、DataLoader 等待导致。 实时监控代码(PyTorch 2.x):
# Source: training_monitor.py
import time
import torch
from collections import deque
class TrainingMonitor:
def __init__(self, window_size=100):
self.step_times = deque(maxlen=window_size)
self.compute_times = deque(maxlen=window_size)
self.comm_times = deque(maxlen=window_size)
self.tokens_processed = 0
def record_step(self, batch_size, seq_len):
t = time.time()
if hasattr(self, '_last_t'):
self.step_times.append(t - self._last_t)
self._last_t = t
self.tokens_processed += batch_size * seq_len
def get_metrics(self):
if not self.step_times:
return {}
avg_step_time = sum(self.step_times) / len(self.step_times)
throughput = self.tokens_processed / sum(self.step_times)
return {"avg_step_time_ms": avg_step_time * 1000, "throughput_tokens_per_sec": throughput, "steps_per_sec": 1.0 / avg_step_time if avg_step_time > 0 else 0,
9.7.4 Loss 异常检测与恢复
}Loss spike 是训练不稳定的最常见表现。有效的检测系统应能区分正常的噪声波动和真正的异常 spike:
# Source: loss_spike_detector.py
import numpy as np
class LossSpikeDetector:
def __init__(self, window=100, spike_threshold=5.0):
self.loss_history = np.zeros(window)
self.window = window
self.spike_threshold = spike_threshold
self.idx = 0
self.is_filled = False
self.spike_count = 0
def check(self, loss_value):
self.loss_history[self.idx % self.window] = loss_value
self.idx += 1
if self.idx >= self.window:
self.is_filled = True
if not self.is_filled:
return None
mu = np.mean(self.loss_history)
sigma = np.std(self.loss_history)
z_score = abs(loss_value - mu) / (sigma + 1e-8)
if z_score > self.spike_threshold:
self.spike_count += 1
return {"is_spike": True, "z_score": z_score, "rolling_mean": mu, "rolling_std": sigma, "spike_count": self.spike_count, "severity": "critical" if z_score > 10 else "warning",
9.7.5 训练动力学监控
}
return {"is_spike": False, "z_score": z_score}
# Usage example
detector = LossSpikeDetector(window=200, spike_threshold=5.0)
# In training loop
result = detector.check(loss.item())
if result and result["is_spike"]:
if result["severity"] == "critical":
# Trigger automatic rollback to the latest checkpoint
load_checkpoint_and_rollback()
logger.warning(f"Loss spike detected! Z-score: {result['z_score']:.2f}")梯度范数是训练稳定性的关键指标。在混合精度训练中,gradient norm 应保持在 0.5-5.0 的范围内(BF16 场景)。
# Source: gradient_monitor.py
def log_gradient_norms(model, step, writer):
total_norm = 0.0
layer_norms = {}
for name, param in model.named_parameters():
if param.grad is not None:
param_norm = param.grad.data.norm(2).item()
total_norm += param_norm ** 2layer_norms[name] = param_norm
total_norm = total_norm ** 0.5
writer.add_scalar("Gradient/TotalNorm", total_norm, step)
# Record gradient norm per layer for quick anomaly localization
for layer_name, norm in layer_norms.items():
writer.add_scalar(f"Gradient/LayerNorm/{layer_name}", norm, step)激活值中的异常大值(outliers)会导致 attention 计算的数值不稳定,尤其在长序列场景中。监控各层的激活值统计量 有助于早期发现发散风险。Attention 熵监控:
# Source: attention_monitor.py
def compute_attention_entropy(attn_weights):
# attn_weights: (batch, heads, seq_len, seq_len)
eps = 1e-12
entropy = -torch.sum(attn_weights * torch.log(attn_weights + eps), dim=-1)
return entropy.mean().item()attention entropy 突然下降(趋于 0)表明模型陷入了硬注意力模式,可能导致梯度消失;突然上升表明注意力过于均 匀,模型在「猜测」而非学习。
9.7.6 可观测性工具链
TensorBoard + PyTorch Profiler 集成:
# Source: integrated_monitoring.py
from torch.utils.tensorboard import SummaryWriter
from torch.profiler import profile, ProfilerActivity, schedule
writer = SummaryWriter("/mnt/logs/tensorboard")
# PyTorch Profiler configuration
profiler = profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=schedule(
wait=2, # Wait 2 steps
warmup=3, # Warmup 3 steps
active=5, # Record 5 steps
repeat=1, # Repeat 1 cycle), on_trace_ready=torch.profiler.tensorboard_trace_handler( "/mnt/logs/profiler", worker_name=f"rank_{dist.get_rank()}" ),
record_shapes=True,
profile_memory=True,
with_stack=True,) W&B (Weights & Biases) 集成示例:
import wandb
wandb.init(
project="llama-70b-training",
config={"model": "Llama-2-70B", "gpu_count": 1024, "batch_size": 4096, "learning_rate": 1.5e-4, "parallelism": "FSDP + TP4", }, name=f"run-{timestamp}", ) wandb.log({ "train/loss": loss.item(), "train/grad_norm": grad_norm, "train/lr": scheduler.get_last_lr()[0], "train/throughput_tokens": tokens_per_sec, "train/mfu": mfu, "eval/perplexity": val_ppl, }, step=global_step) MLflow 用于实验管理:
import mlflow
mlflow.set_tracking_uri("http://mlflow-server:5000")
mlflow.set_experiment("llama-70b-pretraining")with mlflow.start_run(run_name="fsdp-tp4-baseline"): mlflow.log_params({ "model_arch": "llama2-70b", "precision": "bf16", "parallel_strategy": "fsdp_wrap_size_4_tp_4", }) mlflow.log_metric("train_loss", loss.item(), step=global_step) 训练可观测性将监控数据反馈到训练流程中,形成数据驱动的训练优化闭环。
9.7.7 能力基准
能力基准(Benchmark)是评测集的标准化集合,覆盖不同能力维度。通用推理与知识类基准如表9-11所示。 表9-11 通用能力基准一览 基准 能力维度 典型设置 MMLU 多领域知识与推理 57 学科,4 选 1,5-shot GSM8K 数学推理 小学数学应用题,8-shot + CoT BBH 挑战性推理 23 项 BIG-Bench 任务,3-shot + CoT HumanEval 代码生成 164 题,pass@k MATH 竞赛数学 5 级难度,4-shot + CoT ARC-C 科学常识推理 挑战集,25-shot HellaSwag 常识推理 句子补全,10-shot WinoGrande 常识推理 代词消解,5-shot MMLU-Pro MMLU 增强版 更多选项与干扰项,5-shot 对话与指令能力用偏好型基准衡量,典型代表 AlpacaEval 2.0 与 MT-Bench。AlpacaEval 2.0 以 GPT-4 为裁判,计算模型 输出相对参考模型(AlpacaEval-2 参考模型)的胜率;MT-Bench 用 80 个多轮对话问题评测模型在推理、写作、编码等 维度的表现。这类基准分数与人类偏好的相关性已被广泛验证,成为对齐工作的标准度量。 专项基准覆盖细分场景:长上下文(LongBench、RULER)、工具调用(BFCL、ToolBench)、多语言(M-MMLU、 MGSM)、检索增强(RGB、CRAG)、多模态(MMMU、MMBench)、智能体(SWE-bench、WebArena)。 能力基准存在固有局限。其一,饱和效应,模型能力提升后基准分数逼近天花板,区分度下降(如 MMLU 超过 90 分后增 量难以区分);其二,泄漏风险,训练语料混入评测原题会导致分数虚高(见评测数据治理小节);其三,过拟合风险,反 复在固定基准上迭代会使模型记忆评测模式而非真正提升能力。成熟的评测体系应以多基准组合 + 人工抽检 + 在线指标相 互印证。
9.7.8 评估流程设计
评估流程设计决定评测结论的稳定性与可信度。评测集构建上,需保证样本规模足够(统计功效)、难度分布合理(不过 易不过难)、答案唯一性可判(减少主观歧义)。通用做法是从公开基准出发,补充内部构建的领域题目,并对全部题目建 立版本管理。 推理设置需显式固定。温度影响生成多样性:评分类任务(如 4 选 1)用 greedy decoding 或 temperature=0;生成类 任务(代码、写作)常用 temperature=0.2-0.8。Few-shot 与 CoT 提示词、max_tokens、系统提示均需在对比中保持 一致。评测时固定随机种子与解码参数,确保结果可复现。 指标选择上,分类类基准用准确率(Accuracy),代码生成用 pass@k(采样 k 次,至少 1 次通过),对话类用胜率 (Win-rate)或分数(Score),检索类用 Recall/Hit@k。多次独立运行取均值并报告方差,样本量小或分数接近时需做 显著性检验(如 McNemar 检验、bootstrap 置信区间),避免把噪声当提升。 多模型对比需控制变量:同一推理引擎、同一 batch 策略、同一提示模板。推理框架的数值细节(如 FP16/FP8、KV cache 量化)也会影响输出,对比时应注明模型推理的精度配置。
9.7.9 自动评估与 LLM-as-judge
人工评估成本高、扩展性差,自动评估成为大规模评测的主力。LLM-as-judge 用语言模型充当裁判,对模型输出打分或 判断胜负,典型实现是 AlpacaEval 2.0 与 MT-Bench 的评估协议:将候选输出与参考答案/参考模型输出一并交给裁判模 型,让其按 rubrics 打分或二选一。 以 AlpacaEval 2.0 为例,其胜率统计过程为:对每个指令样本,同时让被测模型与参考模型(AlpacaEval-2 参考模型) 生成回答,裁判模型判断哪个回答更好,最终胜率 = 被测模型胜出的样本占比。Llama 3 的迭代 DPO 训练中,每轮用约 100 万偏好对、模型自标注(LLM-as-judge)生成下一轮数据,经 5 轮迭代后 AlpacaEval 2.0 胜率从 42% 提升到 55.8% (vs GPT-4)。自动化评估支撑了这种大规模、多轮的训练迭代。 LLM-as-judge 存在系统性偏差。裁判模型可能偏好更长的输出(长度偏差)、偏向自身风格(自我偏好偏差)、对格式错 误敏感,以及不同裁判模型间结论不一致。缓解手段包括:使用强裁判模型(如 GPT-4 级别)、多次采样取多数、把输出 匿名化与乱序,以及定期引入人类标注锚点校准自动标注器。 奖励坍塌是自动评估在训练循环中的特有风险。当模型以自身输出为偏好数据来源并持续迭代时,评分信号可能收敛到单 一风格而丧失多样性,甚至出现越迭代越退化的「奖励黑客」。预防措施包括定期引入人类标注锚点、控制自标注数据的 比例、以及用多裁判模型交叉验证。
9.7.10 评测基础设施
评测基础设施是评测体系的工程底座,包含任务编排、并行评测、缓存复用与回归保护四部分。 任务编排层面,评测以「配置即任务」的方式组织:一次评测定义一个评测集、一个模型权重路径、一组推理与评估参 数,输出指标与中间结果落盘。常用评测框架(如 lm-evaluation-harness、OpenCompass)内置基准加载、few-shot 协议与指标计算,避免重复造轮子。 并行评测层面,评测推理与训练共用 GPU 集群。批量评测用 vLLM 等推理引擎以高吞吐方式服务样本,显著快于逐个请 求;多个评测任务按优先级排队,评测任务常设为低优先级、复用训练间隙的算力。长上下文与多模态评测对显存要求 高,需按评测集特性分配资源。 缓存复用是控制评测成本的关键。同一评测集 + 同一模型权重 + 同一解码参数组合的生成结果应缓存,仅当模型或配置变 化时重新评测;增量评估只跑变更相关的子集。回归保护用固定冒烟集在每次迭代后快速验证:模型更新后跑一遍冒烟 集,指标异常即阻断发布。 训练中的周期性评测衔接在线监控:预训练期间每隔固定步数(如每 1000-5000 步)对子集评测,监测能力曲线的爬升 与突变;对齐阶段每次 SFT/DPO 后全量评测。周期评测与在线监控共用指标看板,便于把 Loss 变化与能力变化关联分 析。
9.7.11 评测数据治理
评测数据治理防止「评测集污染」,即训练语料混入评测原题或答案,导致分数虚高、能力被严重误判。污染是 LLM 评测 最棘手的问题之一,主流模型报告中普遍声明去污染流程。 去污染的工程做法是 n-gram 重叠匹配:对每个评测样本切出 8-13 gram(GPT-3 用 13-gram,Llama 系列常用 8- gram),若训练文档与任一评测样本存在超过阈值的 n-gram 重叠,则将该文档(或重叠片段)从训练集移除。在 PB 级 语料上高效运行,需先对评测 n-gram 建哈希索引(Bloom filter 或倒排表),再对训练文档流式查询。 n 值选取是去污染的工程权衡:n 过小(如 5)会误杀大量正常文本,n 过大(如 20)则漏检改写型泄漏。跨集污染无法 靠训练集内部去重覆盖,去污染必须以外部评测清单为基准独立执行。评测集自身需版本管理与白名单机制:训练时声明 使用的评测集版本,发布时记录模型评测所基于的评测集与协议,保证可追溯。
9.7.12 异构芯片评测
模型评测聚焦输出质量,芯片评测则度量「同一模型在不同芯片上的执行能力」。芯片选型决策(规格对比、TCO)不在 本节范围,本节给出评测方法,用基准与实测验证纸面规格之外的芯片真实表现。异构芯片(NVIDIA、华为昇腾、寒武 纪等)峰值算力、显存容量、带宽各不相同,直接对比绝对吞吐会把规格差误判为工程差,因此必须先统一评测协议,再 按维度归一化。 统一评测协议 不同芯片的规格差异是评测的首要干扰项。消除它的手段是统一负载协议,让所有芯片在完全相同的条件下执行同一负 载,唯一变量是芯片本身。协议须固定以下要素: •模型:同一参数量、同一架构(如均为 Llama-70B),不同参数量或架构会导致绝对 FLOPs 不同,不可比。 •推理/训练设置:batch 大小、序列长度、上下文长度一致;解码参数(temperature、max_tokens、采样方式)一 致。 •精度位宽:统一 FP16 或统一 BF16 对比;若各芯片用各自最优位宽(如 FP8),须在结果中显式标注,禁止混比。 •框架与编译:同一框架版本、同一编译优化档位(如 vLLM 与 TensorRT-LLM 各用原生后端,但模型定义与加载逻辑一 致)。国产芯片经适配层(torch_npu 等)接入 PyTorch,评测须在真实框架而非厂商 demo 下进行。 协议统一的难点在显存:显存小的卡装不下大 batch。对策是「双轨负载」,以 batch=1 为保底负载(所有芯片可跑,测 decode 时延下限),再让各卡各自取最大可容纳 batch 测峰值吞吐并标注 batch 值,比「各自最大吞吐」而非「同一 batch」。 评测分层 芯片评测分层进行,逐层逼近真实负载,如图9-9所示。前一层是后一层的基线。 峰值规格核验 GEMM 微基准 算子覆盖与可达性能 标准模型前向+反向 模型级实测 MFU / 吞吐 / 时延 集群级评测 通信 / 扩展性 / 稳定性 图9-9 芯片评测四层递进
- 峰值规格核验 芯片规格(FP16/FP8 算力、显存带宽)是厂商公布的理论上限,评测第一步是核验纸面值是否可达。峰值算力用 GEMM 微基准测量:对 H100(BF16 989.5 TFLOPS 稠密)、昇腾 910C(FP16 约 800 TFLOPS,公开报道口径)、寒武纪思元 590(FP16 256-345 TFLOPS,第三方报道)等,跑大规模 GEMM 并计算实测 TFLOPS 相对峰值的占比。峰值核验的意 义在于建立上限基线,后续所有评测都以此为分母。
- 算子覆盖与可达性能 峰值只反映单个 GEMM 上限,真实模型由大量算子构成。用标准模型(如 Llama 系列)跑前向 + 反向,统计: •可达算力:实测 TFLOPS / 峰值 TFLOPS,反映算子库与编译器的整体利用效率。CUDA 生态下经优化的 GEMM 可达峰 值的 70-80%;国产软件栈(CANN、NeuWare)的算子库规模、框架适配深度与 CUDA 仍有代差,整体 MFU 通常明 显低于同等算力的 NVIDIA 卡。 •算子缺失与回退:MoE 路由、多模态编解码等长尾算子若不在算子库中,会回退到逐元素执行或 CPU 路径,单点性能 骤降。评测应覆盖训练与推理全链路的关键算子,而非仅标准 Transformer 结构。 •框架适配方式:国产芯片多依赖适配层(torch_npu 等)接入 PyTorch,而非上游原生支持;适配层的算子映射与显存 管理决定性能天花板。
- 模型级实测与归一化 模型级评测给出可直接对比的数字。由于芯片规格不同,需从多个维度归一化后才可比,每个维度回答一个独立问题,维 度定义如表9-12所示。 表9-12 芯片评测归一化指标 指标 定义 回答的问题 绝对吞吐 tokens/s(标注 batch) 跑同一模型谁更快 decode 时延 batch=1 下 TTFT/TPOT 单请求交互体验 MFU 实测算力 ÷ 芯片峰值 工程成熟度:算力吃透了几成 能效 tokens/s/W 电费口径下的实际成本 性价比 tokens/s/元 结合每小时成本的落地收益 显存容量 最大上下文/最大 batch 能力边界 •绝对吞吐:固定模型与设置,测 Prefill/Decode 吞吐。华为 CloudMatrix 384 超节点单卡推理约 2300 tokens/s(官方 发布会口径,2025-04)。此维度直接受芯片峰值与显存影响,须标注各自 batch。 •MFU: MFU = 有效算力 ÷ 硬件峰值 ,是消除规格差异的核心横评指标。同一模型下 MFU 反映「芯片 + 软件栈把算力 吃透的程度」:纸面 800 TFLOPS 的昇腾跑出 40% MFU,与 989 TFLOPS 的 H100 跑出 42%,说明两者利用率相当, 绝对吞吐差距来自峰值规模而非工程水平。Llama-70B 在 1024 卡 H100 上约 42%,摩尔线程 MTT S5000 宣称 Llama3-70B 训练 MFU 超 60%(官方口径,2026-01)。 •能效与性价比:能效 = tokens/s/W,计入整卡功耗;性价比 = tokens/s/元,结合每小时成本(衔接 TCO 模型)。这两 个指标让「规格高但功耗/成本高」的芯片现形,是部署决策的关键输入。 横评示例(同一负载:Llama-70B、BF16、seq 4096)如表9-13所示。 表9-13 芯片横评示例模板 指标 NVIDIA H100 昇腾 910C 寒武纪 590 batch=1 decode 时延 实测 实测 实测 最大 batch 吞吐 实测(标注 batch) 实测 实测 MFU 42% 级 实测 实测 能效 tokens/s/W 实测 实测 实测 结论原则:绝对吞吐看算力规模,MFU 看工程成熟度,能效与性价比看落地成本,四个维度各回答一个问题,规格不同 不再是障碍。模型级评测须固定模型版本、batch、上下文长度与精度(FP16/FP8),否则数字不可比。
- 集群级评测 单卡性能不直接等于集群性能。集群级评测关注集合通信与扩展性: •通信带宽:HCCS(昇腾)、MLU-Link(寒武纪,思元370 为 200GB/s)、NVLink 的实际 All-Reduce 吞吐,对比理论带 宽。 •扩展性:多卡 MFU 相对单卡的衰减曲线,千卡/万卡线性度(摩尔线程宣称万卡线性度 95%,官方口径)。 •稳定性:长时训练下的故障率、通信超时、显存泄漏(结合容错与通信分析中给出的可靠性模型)。 评测结果解读需注意口径陷阱:官方发布会数字与第三方实测常有差距(如昇腾 910C 芯片级 800 TFLOPS 为公开报道口 径,官方只公布产品级与集群级指标);「CUDA 兼容率超过 95%」(海光 Z110)指 API 兼容,不等于算子性能等价。评 测应以实测为准、注明数据来源与精度口径,并交叉验证厂商声明与第三方报告。 芯片评测的落地建议:建立统一评测基线(固定模型、固定框架版本、固定精度),覆盖单卡峰值、算子可达性、模型吞 吐与集群扩展四个层级;对国产芯片额外记录算子回退清单与框架适配差异;评测结果与 TCO 成本模型结合形成选型依 据。
9.7.13 精度评测
性能评测回答「多快」,精度评测回答「多准」。异构芯片上同一模型可能因数值格式与实现差异产生不同输出: FP16/BF16 的尾数与指数位宽不同、GEMM 累加顺序不同、FMA 融合与否、量化误差、算子融合改写,都会引入数值偏 差。精度评测分算子级、模型级与端到端三层,与性能评测的层次一一对应,且必须给出可量化的阈值,不能停留在「看 起来差不多」。
- 数值格式差异 异构芯片的精度问题首先来自数值格式本身。各浮点格式的位宽、动态范围与精度特点见表9-6。其中对精度评测影响最 大的差异:FP16 动态范围窄(最大 65504),Softmax 前的注意力分数(QK^T)与 LayerNorm 缩放容易溢出;BF16 动 态范围与 FP32 相同但只有 7 位尾数,逐元素精度粗,但配合 FP32 累加在深度网络中误差不累积(累加在主精度,舍入 只在存回时发生)。这是 BF16 成为主流训练格式、FP16 仍需损失缩放(loss scaling)的原因。
- 算子级精度分析 算子级评测度量单个算子的数值偏差,是精度问题的源头定位手段。用参考实现(CPU FP64)作为 ground truth,对比 被测芯片算子的输出,逐算子产出误差报告。核心指标: •最大绝对误差与相对误差: max |out - ref| 与 max |out - ref| / |ref| ,对 GEMM、Softmax、 LayerNorm、RMSNorm 逐算子统计。 •余弦相似度: cos(out, ref) ,对高维张量比逐元素误差更稳定,低于 0.999 通常意味着存在问题。 •误差分布:误差集中在哪个数值区间、是否有系统性偏置(舍入方向一致导致误差单向累积)。 •位级一致率:逐元素对比输出位模式(bit pattern),统计 bit-exact 比例。注意相同精度下不同实现(FMA 融合、累 加顺序、FlashAttention 重放)天然不等位,需区分「允许偏差」与「越界偏差」。 一个 GEMM 误差分析的最小实现(用于算子的快速对比):
import torch
def gemm_error(a, b, fn, ref_dtype=torch.float64):
# reference: FP64 matmul on CPU
ref = torch.matmul(a.to(ref_dtype), b.to(ref_dtype))
out = fn(a, b)
err = (out.to(ref_dtype) - ref).abs()
rel = err / (ref.abs() + 1e-12)
cos = torch.nn.functional.cosine_similarity(
out.to(ref_dtype).flatten(), ref.flatten(), dim=0) return { "max_abs": err.max().item(), "mean_abs": err.mean().item(), "max_rel": rel.max().item(), "cos": cos.item(), "bit_exact": (out.view(torch.int8).eq(
a.new_zeros(a.shape[0]).int()).float().mean().item()
if False else 0.0),
}关键坑:误差阈值与张量规模相关,大 GEMM(K 大)的累加误差随 K 增长, max_rel 阈值需按 K 标定;Softmax 用在 线(online)softmax 会因重放产生可忽略但非零的偏差,不能与「精确 Softmax」位级对比。算子级评测的输出是一个 误差基线表:哪些算子达标、哪些异常(常指向算子实现 bug、精度格式回退如 BF16 中间累加代替 FP32、或显存读写 错位)。 3) 模型级精度验证 算子级误差经多层传播会累积或抵消,模型级评测给出端到端结论: •数值一致性:相同输入下,被测芯片输出与参考实现的 Logits/隐状态差异,常用平均相对误差与最大偏差;对生成任 务对比 greedy decode 输出序列的一致性比例(sequence-level match rate)。经验阈值:隐状态余弦相似度 > 0.999、greedy 输出一致性 > 99% 视为达标。 •任务精度:在标准能力基准(见能力基准小节)上跑被测芯片,与参考实现分数差。分数差在噪声范围(基准单次运行 波动约 1-2%)内视为达标;显著下降则下钻到算子级定位。 •PPL 与量化精度:困惑度是语言模型的全局精度度量。实测参考值:FP8 量化精度损失通常 < 0.1 PPL;INT8 W8A8 权 重量化精度损失通常在 1% 以内;INT4 GPTQ/AWQ 在多数任务损失 < 2% 但代码/数学任务可能退化更多。PPL 变化作 为量化门槛,任务精度作为最终验收,二者都要看。 模型级数值一致性对比的最小实现:
import torch
def model_logits_similarity(model_a, model_b, tokenizer, texts, max_len=2048):
# compare logits of two models on the same inputstotal_cos, n = 0.0, 0 for t in texts: ids = tokenizer(t, return_tensors="pt").input_ids[:, :max_len] with torch.no_grad():
la = model_a(ids).logits
lb = model_b(ids).logits
total_cos += torch.nn.functional.cosine_similarity(
la.flatten(), lb.flatten(), dim=0).item() n += 1 return total_cos / n 4) 精度与性能的权衡 精度与性能在异构芯片上常需联合评测。同一模型可运行在多种精度位宽(FP16/BF16/FP8/INT8)与多种融合/量化配 置下,构成精度-性能平面: •精度配置档:固定 batch 与序列,扫描精度档位(BF16、FP8、INT8-W8A8、INT4),记录每档 PPL/任务精度与吞 吐,形成「精度损失 vs 加速比」曲线。典型结果:FP8 相对 BF16 约 1.5-2× 算力提升、INT8 W8A8 约 2× 显存减 半,INT4 约 4× 权重压缩但需反量化开销(Marlin 内核可近零开销)。 •达标门槛:以任务精度损失可接受(PPL 变化 < 0.5 或任务分数降幅 < 2%)为前提选择最快档位;对代码/数学等敏感 任务,档位门槛收紧到 < 1%。 •口径记录:任何性能数字必须标注精度配置,「FP8 下 2300 tokens/s」与「FP16 下 2000 tokens/s」不矛盾但不可混 比。这是异构芯片横评「精度位宽统一」要求的底层原因。 精度评测产出「精度-性能联合矩阵」:芯片 × 精度档位 × 模型的精度损失与性能数据,供模型与硬件团队做显式取舍。
9.7.14 评测工程与平台化
评测体系要支撑「用例编排、任务下发、指标采集、结果入库、报告产出的全链路自动化」,工程化程度决定评测的可维 护性与可信度。评测工程分四部分:流水线数据模型、任务调度与执行、结果聚合与报告、环境治理。
- 评测流水线数据模型 评测的每一步都产生结构化数据,统一 schema 是自动化与可追溯的基础。核心数据模型三层: •评测任务 Task:评测集 + 模型权重路径 + 推理配置 + 评估配置(few-shot、指标、采样参数)的组合,是下发的最小 执行单元。 •评测运行 Run:一次任务执行实例,记录时间、芯片/节点、软件栈版本、精度配置、日志路径与状态 (pending/running/success/failed)。 •指标结果 Metric:运行产生的指标键值对(accuracy、pass@k、win-rate、MFU、TTFT 等),附指标定义、样本数 与置信度。 数据模型设计关键是可复现性:Run 必须绑定完整「配方」(评测集版本、模型 commit、框架版本、推理引擎版本、精 度档位、环境镜像 tag),任何指标缺失配方都不可信。评测集与模型权重按资产管理,带版本号与哈希,禁止裸路径引 用。一个可复现配方的示例 schema: run: model: "llama3-70b" model_commit: "a1b2c3d4" dataset: "mmlu" dataset_version: "v5" engine: "vllm" engine_version: "0.6.3" precision: "fp8_e4m3" batch: 32 seq_len: 4096 image: "eval/vllm:0.6.3-cu12.1" chip: "huawei-ascend-910c" env: "cloud-train-3"
- 任务调度与执行 评测任务是短时、可并行、可中断的批式负载,适合专门调度器: •队列与优先级:按优先级排队(上线把关高、周期回归中、探索性评测低),复用训练间隙 GPU。 •资源约束:不同评测对显存/算力需求差异大(batch=1 单卡可跑,长上下文或多模态需多卡),按任务声明分配。 •幂等与重试:任务可重跑且结果可覆盖,失败自动重试或标记,避免部分失败污染结论。 •并发控制:同一模型同一评测集避免重复评测(命中缓存取历史结果),增量变更只重跑受影响子集。 评测的典型执行周期:对长上下文评测(如 128K RULER),单任务可能跑数小时;对通用基准(MMLU 5-shot)单卡 30-60 分钟;批量吞吐评测(offline)最快。调度器按任务类型与资源槽位预估排期,避免长任务阻塞短任务。
- 结果聚合与报告 •指标入库:写入数据库(时序库 + 关系库组合),按模型/芯片/评测集/精度档位多维索引,支撑跨版本、跨芯片历史对 比。 •回归与对比:同模型新版本 vs 旧版本指标差(回归检测,经验阈值:主要基准降幅 > 1% 即告警阻断),不同芯片/精度 档位对比,自动生成对比表与差异标注。 •报告产出:从入库数据自动生成评测报告(Markdown/HTML),含环境配方、指标汇总、与基线 diff、失败项;版本 化存档,作为发布与选型决策依据。 •API 对接:REST API 提供任务提交、状态查询、结果拉取,供 CI 流水线、模型团队与硬件团队集成。
- 评测环境治理 多平台评测的环境是最大工程负担,GPU 与多款 NPU 的驱动、容器、推理引擎、框架版本互相耦合。目标「环境可复 现、问题可隔离、切换低成本」: •镜像化管理:每芯片平台维护独立评测镜像(驱动 + 引擎 + 框架 + 评测代码),打 tag 记录版本;镜像即环境,杜绝共 享环境装包的状态漂移。 •版本兼容矩阵:维护「推理引擎 × 框架 × 驱动」兼容矩阵(如 vLLM 0.6.x 需 CUDA 12.x、MindIE 需特定昇腾 CANN 版本、SGLang 与 vLLM 对同一模型前后端差异),评测前校验,不兼容直接拒绝而非跑出无效结果。 •环境隔离:不同任务容器隔离(驱动与容器 runtime 除外);GPU 与 NPU 任务互不影响。 •排障工具链:预置每平台性能分析工具(Nsight Compute/Systems、msprof/torch_npu.profiler、CANN profiler、 DeepSpeed/nsight 日志)与日志采集,评测失败自动抓驱动/引擎日志与堆栈。 •版本回滚:评测产物与镜像保留历史版本,软硬件升级后回滚复跑,保证指标序列连续可对比。 评测工程化与平台化的落地标志:从「人肉跑评测、贴图到群里」升级为「提交即评测、结果自动入库、报告自动产出、 异常自动告警」的持续评测体系。
9.7.15 库级评测与评测框架选型
异构芯片评测有三个层次:算子级、库级与端到端。前述峰值核验覆盖算子级,模型级评测覆盖端到端,中间的库级评测 (加速库本身)是衔接层,算子实现藏在加速库内,库级基准直接度量加速库的交付质量。评测框架选型则决定「用什么 工具把评测跑起来」,是评测工程的第一决策。
- 库级评测 库级评测度量加速库(cuBLAS、cuDNN、ROCm HIP、CANN 算子库、MLU 算子库等)在典型输入下的性能与精度,不 关心模型整体。常用手段: •库自带的 benchmark:cuBLAS 提供 cublas_bench ,cuDNN 提供 cnn_bench ,CANN 提供算子性能测试工具;它 们暴露库支持的形状/算法空间(tile 大小、split-k、batch GEMM、StridedBatchGEMM),是库级性能的第一手数 据。 •GEMM 形状扫描:AI 负载的 GEMM 形状(M/N/K)分布与经典 HPC 基准(如 LINPACK)差异大,Transformer 的 GEMM 是 M 中等、K 很大(权重维度)、batch 为 1 的矩阵乘,与 m=8192 的大方阵不同。库级评测应扫描真实模型形 状(如 Llama 各层 QK^T/FFN 的 M/N/K),而非只跑方阵。 •库级精度:对同一 GEMM 输入,对比不同库实现(cuBLAS vs CANN 算子库)的输出与 FP64 参考的误差,定位精度差 异是库实现还是算子调用问题。 一个真实形状 GEMM 库级基准的示意:
import torch
def bench_gemm(shape, dtype=torch.float16, iters=100):
# sweep transformer-like GEMM shapes through cuBLASm, n, k = shape
a = torch.randn(m, k, dtype=dtype, device="cuda")
b = torch.randn(k, n, dtype=dtype, device="cuda")
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(iters):
torch.mm(a, b)
end.record()
torch.cuda.synchronize()
ms = start.elapsed_time(end) / iters
flops = 2 * m * n * k
return flops / (ms * 1e-3) / 1e12 # TFLOPS- 评测框架选型 模型能力评测的框架主要有三个,各有定位,如表9-14所示。 表9-14 评测框架对比 框架 定位 特点 lm-evaluation- 学术基线标准 HuggingFace 维护,基准覆盖全,few-shot/CoT 协议标准,学术论文引用基准的默 harness 认工具 OpenCompass 大规模评测平 上海 AI Lab 维护,支持并行评测、结果可视化、评测集管理,适合评测平台基建 台 HELM 可复现性优先 Stanford 维护,强调多场景、多指标、透明可复现,评估协议严谨 选型建议:单模型对比用 lm-evaluation-harness(结果可对照学术论文);评测平台建设用 OpenCompass(并行调度 + 结果聚合现成);跨机构对比可复现性要求高时参考 HELM 的多场景协议。三者的核心协议(few-shot 样例数、metric 计算)保持一致,同一模型用不同框架跑同基准结果应基本一致,若差异明显,优先排查 few-shot 设置与采样差异而非 框架 bug。
- MLPerf 方法论 MLPerf(附录 B 已列部分数据)是硬件/系统级评测的行业标准,方法论的三个要点值得评测工程师借鉴: •负载固定:MLPerf 固定模型、数据集、质量目标(如 target accuracy),各提交者只能优化实现不能改负载,这与异 构芯片横评「统一负载协议」原则一致。 •质量门控:先达标质量目标,再比性能,性能必须在质量达标的约束下测量,防止「快但错」的结果。 •分场景评测:训练(Training)与推理(Inference 的 offline/server 等场景)分开,场景决定指标口径(offline 看吞 吐,server 看延迟分位)。 MLPerf 的「负载固定 + 质量门控 + 分场景」三原则,可直接迁移到企业内部评测:任何性能对比都先固定负载与质量门 槛,再谈吞吐与延迟。 评测框架选型的落地建议:以 OpenCompass 或 lm-evaluation-harness 为能力评测基座,库级基准自建(真实形状 GEMM 扫描),MLPerf 方法论文档化作为硬件评测的协议模板;框架选型纳入前述评测流水线(配置即任务),避免「每 轮评测重写脚本」。
9.8 训练数据安全与合规
大模型训练涉及海量互联网数据,数据安全与合规不仅是技术问题,更是法律和伦理问题。本节从技术实现角度系统讨论 训练数据的安全与合规保障体系。
9.8.1 训练数据的隐私风险
大模型训练的隐私风险可分为三个层次,对应的对策如图9-10所示:
- 训练数据泄露:模型可能记住训练数据中的敏感信息(个人信息、API 密钥、专有代码)。Carlini 等人(2021)发现 GPT-2 可以从提示中复现完整的训练文档,泄露率与模型规模正相关。
- 成员推断攻击(Membership Inference Attack):攻击者可以判断特定数据点是否被用于训练模型。Shokri 等人 (2017)提出的基于影子模型(Shadow Model)的攻击,在 LLM 级别模型上达到显著高于随机猜测的推断准确率。
- 数据提取攻击:攻击者通过精心设计的提示词系统性提取训练数据。Nasr 等人(2023)展示了从 ChatGPT 中提取数 MB 训练数据的能力。 Data Privacy Risk Training Memorization Membership Inference Data Extraction Regex Filtering Differential Privacy Trainin DP-SGD (reduced inferenc Output Audit Log Output Content Filtering Rate Limiting g e success) 图9-10 数据隐私风险与对策
9.8.2 DP-SGD 差分隐私
差分隐私(Differential Privacy)通过向梯度注入噪声来提供可证明的隐私保护。 Differentially Private SGD(Abadi et al., 2016) 在每个训练步骤中:
- 计算每个样本的梯度
- 对梯度进行裁剪(限制个体影响,clip norm C)
- 向裁剪后的梯度注入高斯噪声(噪声尺度 σ)
- 通过 moments accountant 追踪隐私预算 (ε, δ)
# Source: DP-SGD training example (opacus)
from opacus import PrivacyEngine
model = LlamaForCausalLM.from_pretrained("meta-llama/Llama-2-7b")
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
# Attach privacy engine
privacy_engine = PrivacyEngine()model, optimizer, train_loader = privacy_engine.make_private(
module=model,
optimizer=optimizer,
data_loader=train_loader,
noise_multiplier=1.2, # sigma: noise scale
max_grad_norm=1.0, # C: gradient clipping threshold
# Note: target_epsilon/target_delta/epochs are not valid make_private() args
# Use make_private_with_epsilon() for epsilon-based configuration:
# model, optimizer, train_loader = privacy_engine.make_private_with_epsilon(
# ..., target_epsilon=8.0, target_delta=1e-5, epochs=10
# ))
for batch in train_loader:
optimizer.zero_grad()
output = model(batch)
loss = criterion(output, batch.labels)
loss.backward()
optimizer.step()
epsilon = privacy_engine.get_epsilon(delta=1e-5)DP 训练的计算代价: •梯度裁剪需要额外的反向传播遍历 •噪声注入降低训练收敛速度 •典型 ε=8 设置下,模型困惑度可能退步 2-5 个点 •训练时间增加 1.5x-3x(因 per-sample gradient 计算) DP 的实际应用: •Apple 使用 DP 训练 Siri 模型(ε=4-8) •Google 用 DP 训练 Gboard 语言模型 •Meta 在 Llama-2 的 RLHF 中未使用 DP,但发布了 DP 微调指南
9.8.3 数据去重与 PII 过滤
- 大规模去重: 训练数据的去重对隐私保护和模型质量都有积极影响。Lee 等人(2022)证明去重可减少模型记忆训练数据 10x 以上。
# Source: MinHash large-scale deduplication
from datasketch import MinHash, MinHashLSH
def deduplicate_corpus(documents, threshold=0.8):
lsh = MinHashLSH(threshold=threshold, num_perm=128)
seen_hashes = set()
unique_docs = []
for i, doc in enumerate(documents):
m = MinHash(num_perm=128)
for shingle in get_shingles(doc, k=5):
m.update(shingle.encode('utf8'))
# Check if similar to an existing document
if lsh.query(m):continue
lsh.insert(i, m)
unique_docs.append(doc)
return unique_docs- PII(个人身份信息)过滤: 生产环境的 PII 过滤管线应包括:
- 正则表达式匹配:邮箱、电话、身份证号、IP 地址、信用卡号
- NER 模型检测:人名、地名、机构名(使用 spaCy 或 Presidio)
- 自定义规则:内部代码 token、AWS/Azure 密钥模板
9.8.4 模型水印与版权保护
# Source: PII filtering example
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()
def filter_pii(text: str) -> str:
results = analyzer.analyze(text=text, language="en")
anonymized = anonymizer.anonymize(text=text, analyzer_results=results)
return anonymized.text模型水印技术允许模型所有者验证模型是否由特定数据训练而来:
- 数据集水印:在训练数据中植入特定的「水印样本」(如特殊短语组合),训练后的模型对这些样本产生特定的输出模 式。
- 模型指纹:通过模型对特定输入的输出统计特性验证所有权。
- 黑盒验证:不需要访问模型权重,仅通过 API 调用即可验证水印存在。 Google 的 SynthID-Text 通过修改 LLM 的采样策略嵌入水印,不影响生成质量,且对蒸馏、剪枝等攻击具有鲁棒性。
9.8.5 GDPR/CCPA 合规实践
大模型训练涉及的全球合规框架如表9-15所示。 表9-15 主要法规对训练的工程影响 法规 核心要求 对训练的工程影响 GDPR (EU) 数据最小化、目的限制、被遗忘权 需追溯训练数据来源,支持数据删除 法规 核心要求 对训练的工程影响 CCPA (California) 消费者知情权、退出权 需披露训练数据类别 AI Act (EU) 高风险 AI 系统的合规要求 GPAI 模型需提供训练数据摘要 中国个人信息保护法 告知同意、数据本地化、跨境管理 训练数据需审查跨境传输 工程实践:
- 数据来源追溯(Data Provenance):为每个训练样本记录元数据(来源 URL、收集时间、许可证、处理链),使用 immudb 或区块链确保不可篡改。
- 选择性删除(Machine Unlearning):当用户要求删除数据时,能够从模型中「反学习」特定数据的影响。SISA (Sharded, Isolated, Sliced, Aggregated)训练策略可用于支持高效遗忘。
- 训练数据透明报告:发布类似 Llama-2 的「模型卡片」(Model Card),披露训练数据的来源、规模和预处理方式。
9.8.6 安全计算与联邦
对于跨组织的协作训练场景: 联邦学习(Federated Learning): •数据不出本地,各方在本地计算梯度更新 •通过安全聚合(Secure Aggregation)协议合并各方的更新 •Google 的 Gboard 键盘模型使用 FL 训练 •通信开销和异构性是大规模 FL 的主要挑战 安全多方计算(Secure Multi-Party Computation, MPC): •多个参与方在不泄露各自数据的情况下共同训练模型 •基于秘密共享(Shamir’s Secret Sharing)或混淆电路 •Piranha 等框架使 MPC 训练的通信效率接近明文训练 大模型训练中的数据安全与合规是一个系统工程,需要技术手段与制度流程的配合。
9.9 千卡训练实战
本节提供从零搭建 Llama-2-70B 千卡训练系统的完整实战指南,涵盖集群配置、训练脚本、启动调优和故障排查。
9.9.1 集群环境准备
硬件规格: •128 台 DGX H100 节点(共 1024 个 H100 GPU) •每节点 8× H100 80GB SXM5,NVSwitch 全互联 •每节点 8× 3.84TB NVMe SSD •InfiniBand NDR400 网络(4× IB per node),Fat-Tree 拓扑 •共享存储:WEKA/Lustre 并行文件系统,聚合带宽 > 500 GB/s 软件栈:
Base software environment
export MLNX_OFED_VERSION=23.10-1.1.9.0 export CUDA_VERSION=12.3 export NCCL_VERSION=2.19.3 export PYTHON_VERSION=3.10
Install NVIDIA Enroot + Pyxis
sudo apt-get install -y enroot sudo apt-get install -y nvidia-container-toolkit
PyTorch environment
pip install torch==2.1.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install transformers==4.35.0 accelerate==0.24.0 pip install deepspeed==0.12.0 pip install tensorboard wandb mlflow 网络与存储验证:
9.9.2 模型准备
9.9.3 并行策略设计
# NCCL bandwidth test
mpirun -np 1024 -hostfile hosts.txt \
-x NCCL_DEBUG=INFO -x NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3 \
all_reduce_perf -b 8 -e 128M -f 2 -g 1
# Expected: allreduce busbw > 150 GB
# Expected: allreduce latency < 10us
# Storage bandwidth test
fio --name=write-test --rw=write --bs=4M --size=100G \
--numjobs=8 --directory=/mnt/shared_storage/ --group_reporting
# Filesystem write benchmark
mpirun -np 128 -hostfile hosts.txt \
bash -c 'dd if=/dev/zero of=/mnt/shared_storage/test_$OMPI_COMM_WORLD_RANK bs=1G count=10 oflag=direct'
# Download Llama-2-70B original
# Convert to HuggingFace format
python -m transformers.models.llama.convert_llama_weights_to_hf \
--input_dir /data/llama-2-70b/ \
--model_size 70B \
--output_dir /data/llama-2-70b-hf/对于 Llama-2-70B(FP32 约 280GB,BF16 约 140GB),在 1024 H100 上的并行配置:
# Source: parallel_strategy.py
# Parallelism calculation
model_params = 70e9
fp32_memory = model_params * 4 / 1e9 # 280 GB
bf16_memory = model_params * 2 / 1e9 # 140 GB
optimizer_memory = fp32_memory * 3 # 840 GB
activation_memory = 80 # Approximately 80 GB (depends on seq len and batch size)
total_per_gpu_min = bf16_memory + optimizer_memory + activation_memory
# For 80GB H100: 140 + 840 + 80 = 1060
# Requires Data Parallel + Tensor
# Recommended config: DP=32
# Model shard per GPU: 70
# Optimizer shard: 4.4 * 3 = 13.1
# Total: ~17.5 GB + activations
# Actual training script
training_config = {"model_size": "70B", "total_gpus": 1024, "data_parallel_size": 32, # DP=32 "tensor_parallel_size": 4, # TP=4 "pipeline_parallel_size": 8, # PP=8 "sequence_parallel": True, # Sequence parallelism (reduces activation memory) "use_distributed_optimizer": True, # ZeRO-1 distributed optimizer "global_batch_size": 4096, # Global batch size = 4096 "micro_batch_size": 1, # Micro-batch per GPU per step = 1 "gradient_accumulation_steps": 128, # grad_accum = global_batch / (dp * micro_bs) = 4096 / (32 * 1) = 128 "sequence_length": 4096, "precision": "bf16", } 验证并行划分:
# Verify configuration sanity
dp = 32
tp = 4
pp = 8
total_parallel = dp * tp * pp assert total_parallel == 1024, f"Parallelism mismatch: {total_parallel} != 1024"
effective_batch = dp * micro_batch_size * gradient_accumulation_steps
assert effective_batch == global_batch_size,
f"Batch mismatch: {effective_batch} != {global_batch_size}"
9.9.4 Megatron-LM 训练脚本
# Source: pretrain_llama_70b.sh
#!/bin/bash
# Launch Megatron-LM training
MASTER_ADDR=$(head -1 $HOSTFILE)
NUM_GPUS=$(( $(wc -l < $HOSTFILE) * 8 ))torchrun \
9.9.5 监控运维配置
--nnodes=$SLURM_NNODES \
--nproc_per_node=8 \
--rdzv_id=llama70b_train_$(date +%s) \
--rdzv_backend=c10d \
--rdzv_endpoint=$MASTER_ADDR:29400 \
--max_restarts=10 \
pretrain_gpt.py \
--tensor-model-parallel-size 4 \
--pipeline-model-parallel-size 8 \
--sequence-parallel \
--use-distributed-optimizer \
--num-layers 80 \
--hidden-size 8192 \
--num-attention-heads 64 \
--num-query-groups 8 \
--ffn-hidden-size 28672 \
--seq-length 4096 \
--max-position-embeddings 4096 \
--micro-batch-size 1 \
--global-batch-size 4096 \
--lr 1.5e-4 \
--min-lr 1.5e-5 \
--lr-decay-style cosine \
--lr-warmup-fraction 0.01 \
--weight-decay 0.1 \
--adam-beta1 0.9 \
--adam-beta2 0.95 \
--clip-grad 1.0 \
--bf16 \
--train-iters 500000 \
--eval-interval 1000 \
--eval-iters 50 \
--log-interval 1 \
--tensorboard-dir /mnt/logs/tensorboard \
--save /mnt/checkpoints/llama70b \
--save-interval 5000 \
--no-save-optim \
--data-path /mnt/data/llama_text_document \
--split 99,1,0 \
--tokenizer-type Llama2Tokenizer \
--tokenizer-model /mnt/data/tokenizer.modelSlurm 作业提交:
#!/bin/bash
#SBATCH --job-name=llama70b
#SBATCH --nodes=128
#SBATCH --ntasks-per-node=1
#SBATCH --gpus-per-node=8
#SBATCH --cpus-per-task=96
#SBATCH --time=30-00:00:00
#SBATCH --partition=llm
#SBATCH --output=/mnt/logs/%x-%j.out
#SBATCH --error=/mnt/logs/%x-%j.err
#SBATCH --exclusiveexport OMP_NUM_THREADS=12 export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3 export NCCL_IB_GID_INDEX=3 export NCCL_SOCKET_IFNAME=ib0 export NCCL_DEBUG=WARN export NCCL_IB_TIMEOUT=22 export NCCL_IB_RETRY_CNT=7 srun bash pretrain_llama_70b.sh 实时监控面板:
# Source: monitor_dashboard.py
# Grafana dashboard configuration
DASHBOARD_PANELS = [
{"title": "Training Throughput", "targets": [ "throughput_tokens_per_sec_gpu", "throughput_tokens_per_sec_total", ], "yaxis": {"format": "short"}, }, { "title": "GPU Utilization", "targets": [ "avg(DCGM_FI_DEV_GPU_UTIL{job='dcgm'}) by (node)", ], "thresholds": [{"value": 90, "color": "green"}, {"value": 70, "color": "orange"}], }, { "title": "NCCL Communication Time", "targets": [ "nccl_allreduce_time_us", ], "thresholds": [{"value": 500, "color": "red"}], }, { "title": "Training Loss & Gradient Norm", "targets": [ "train_loss", "gradient_norm", ], "yaxes": [{"format": "short"}, {"format": "short"}], }, { "title": "Loss Spike Detection", "targets": [ "loss_z_score", ], "thresholds": [{"value": 5, "color": "red", "label": "SPIKE"}], }, ]
9.9.6 预期性能与成本
性能基线(1024 H100,Llama-2-70B,BF16)如表9-16所示。 表9-16 Llama-2-70B 千卡训练性能基线 并行配置 总吞吐量 (tokens/s) 每 GPU 吞吐 MFU 训练时间 (2T tokens) TP4 PP8 DP32 926,000 904 42% 约25 天 TP8 PP8 DP16 860,000 840 39% 约27 天 TP4 PP16 DP16 794,000 775 36% 约29 天 注:吞吐量按 MFU 反推:70B 模型训练每 token 约需 6×70B = 420 GFLOPs,1024 卡 H100(BF16 峰值约 989 TFLOPS/卡)在 42% MFU 下约合 926K tokens/s(每 GPU 约 904 tokens/s)。若吞吐量远低于此量级(如 22K tokens/s),则与所列 MFU 和 2T tokens / 25 天的训练时间互相矛盾,属配置错误。 成本估算(以主流云厂商 H100 价格为例)如表9-17所示。 表9-17 单次 70B 训练成本估算 项目 用量 单价 (USD/hr) 月成本 H100 GPU(1024 卡) 1024 × 720 hr $3.50 $2,580,480 网络(IB NDR) 含在 GPU 中 - - 存储(100TB WEKA) 100 TB $0.04/GB-month $4,000 人力(4 人团队) 1 个月 - $60,000 单次 70B 训练总成本 约$2,650,000
9.9.7 常见问题与解决方案
训练过程中的常见问题、根因与解决方案如表9-18所示。 表9-18 训练常见问题与解决方案 问题 现象 根因 解决方案 NCCL timeout ,训练卡 IB 链路故障、 NCCL 超时 死在 AllReduce NCCL ring 形成异 export NCCL_IB_TIMEOUT=22 ;检查 ibstat 状态 常 OOM in Activation CUDA OOM at gradient 激活值重计算的内 增加 --recompute-granularity selective 或增 Checkpointing checkpoint recompute 存碎片 大 -activations-checkpoint-num-layers Loss Spike after 学习率 warmup 结束后 学习率过激;梯度 降低 LR → 1.0e-4;增加 warmup 比例 → 0.05 warmup loss 激增 范数 > 10 SBus Timeout Xid 31: GPU has GPU 硬件故障或散 排除故障 GPU;nvidia-smi 检查温度 fallen off the bus 热不足 Checkpoint IO 检查点保存时所有 GPU 卡 存储 IOPS 饱和 使用异步检查点;增加存储带宽容纳 Hang 住 DataLoader GPU 利用率 < 70%,计算 数据加载速度跟不 增加 num_workers;使用 StreamingDataset;SSD Starvation 间隔长 上计算 缓存 FP8 Training FP8 训练的 loss 比 BF16 FP8 精度损失累积 降低到 BF16 baseline 再切换 FP8;增大 Regression 差 0.1+ amax_history_len 训练成功率优化建议:
- 分阶段启动:先在 8 GPU 单节点验证 1000 步,再扩展到 128 GPU 16 节点验证 5000 步,最后上 1024 GPU。
- 健康检查脚本:每个节点启动前运行 GPU burn test (5 分钟) + NCCL 带宽测试 + 存储 I/O 测试。
- 告警设置:Slack/PagerDuty 集成,当 MFU < 25% 或 loss spike z-score > 5 时告警。
9.9.8 强化学习训练 Infra
预训练与 SFT 之外,强化学习(RL)后训练是当前大模型能力提升的主要路径,其基础设施形态与纯训练任务有本质差 异。本节讨论 RL 特有的「生成加训练」混合基础设施,推理引擎与并行策略的底层机制衔接前述内容,聚焦组件划分、 流水线调度与稳定性防护。
- RL 训练的计算特征 以 PPO 为代表的 RL 算法把每次迭代拆为 rollout 生成、奖励计算、策略与价值网络更新三个阶段,与预训练对比有两个 关键差异:
- 推理与训练交替占卡:rollout 阶段 GPU 跑批量生成(推理),更新阶段跑梯度计算(训练),两类负载交替占用同一批 卡,算力无法像预训练那样持续满负荷运行。
- 吞吐口径不同:生成阶段关注 tokens/s 与生成延迟,更新阶段关注 MFU,两类指标不可直接比较,资源核算需要分别 计量。
- 分布式 RL 的组件划分 RL 训练集群按角色分为生成器、训练器与奖励服务器三类组件,如图9-11所示。 Reward Server Reward Model Rule Filter batches Trainer Pool PPO Update responses new weights Generator Pool vLLM Replica 1 vLLM Replica 2 vLLM Replica N 图9-11 RL 生成训练混合架构 三类组件的职责与扩展方式:
- 生成器:多副本 vLLM 推理实例加载策略模型,接收 prompt 批量生成响应,副本无状态,可水平扩缩。
- 训练器:PPO 更新进程组,消费经验缓冲中的样本做策略与价值网络更新,并行方式沿用数据并行加模型并行的组 合。
- 奖励服务器:奖励模型打分与规则校验(格式、长度等硬约束),输出经验回填给训练器。
- 生成训练流水线 生成器与训练器之间通过经验缓冲解耦,形成生成、奖励、更新的流水线:
- rollout 批量生成:生成器按批次处理 prompt,每批样本量由 rollout 批量与生成长度决定,生成完成后经奖励服务器 过滤低质量响应。
- 经验缓冲:样本、奖励值与优势估计写入缓冲,训练器按固定 batch 消费;缓冲容量按一轮更新样本量的数倍配置, 避免训练器空转。
- 权重版本化:训练器每轮更新后推送新策略权重,生成器在下一批请求启用,保证生成与更新并行不互相阻塞。
- 调度耦合:生成器的 vLLM 批量推理以请求为粒度占卡,训练器的分布式更新以数据并行组为粒度占卡,调度器在同一 队列按生成吞吐与训练 ETTR 双目标分配。
- 资源分配与切换 RL 训练中推理与训练算力配比随训练阶段动态变化,集群调度按以下方式适配:
- 弹性扩缩生成器:生成器以副本粒度水平扩缩,按经验缓冲水位与生成队列长度自动调整;生成器无状态,扩缩不影响 训练进度。
- 配比按轮核算:推理算力占比由生成长度、prompt 分布与更新 batch 共同决定,不同轮次差异明显,调度器按轮重新 核算并迁移算力。
- 空闲算力复用:训练器在奖励计算与数据整理的空窗期把空闲卡临时划入生成器池,降低整体闲置。
- RL 训练的稳定性 PPO 类训练的稳定性风险高于预训练,基础设施需提供对应防护:
- KL 惩罚监控:监控新旧策略的 KL 散度,超过阈值(如单 token 平均 KL 约 0.1)触发告警,抑制策略发散。
- 奖励信号异常检测:按批记录奖励均值与方差,均值突变或方差骤增时回滚到上一轮策略,避免带噪奖励污染更新。
- 生成质量防护:检测长度退化与重复文本等生成质量异常,隔离对应生成器副本,防止污染经验缓冲。
- 更新防护:gradient norm 超限(如 > 10)跳过本轮更新,配合 clip 参数限制单步策略变化幅度。 掌握千卡预训练与强化学习训练系统的搭建方法后,可以进一步扩展到推理优化和推理服务架构的工程实践。