☰
AI Infra 知识全景:从硬件到编排的系统学习笔记
2026/10/22 4:01:40 网站建设 项目流程

1. 为什么我要系统梳理 AI Infra 这张知识地图

过去两年,我陆续参与过几个模型训练与推理平台的搭建项目,从最初只会调参跑脚本,到后来需要独立负责一个推理服务的稳定性,中间踩过的坑几乎覆盖了整条技术栈。最深的感受是:AI Infra 这个领域最大的问题不是知识点太难,而是知识太散。你可能今天在调 GPU 显存分配,明天在研究分布式通信原语,后天又要去搞容器编排和存储加速,每个方向单独拎出来都有大量资料,但很少有人把它们串成一条线讲清楚彼此之间的关系。

这就是我动手整理这份知识全景学习笔记的起因。它不是某一项技术的深度教程,而是一张覆盖 AI Infra 主要分支的认知地图,帮你在面对一个具体问题时,能快速定位它属于哪个层次、依赖哪些上下游组件、常见的解决思路有哪些。适合已经有一定工程基础、想从“会用”进阶到“懂原理”的开发者,也适合刚转入这个方向、需要快速建立全局视野的同学。

整份笔记我按“硬件层—通信层—训练框架层—推理与服务平台层—编排与运维层”这条主线来组织,每一层都会讲清楚它解决什么问题、核心组件有哪些、关键参数怎么算、实际落地时容易在哪里翻车。下面就把我整理过程中的思路和干货逐层展开。

2. 整体知识框架的设计思路

2.1 为什么按“层次”而不是按“工具”来组织

一开始我尝试按工具来分类,比如 PyTorch 一块、CUDA 一块、Kubernetes 一块。但很快发现这种分法有个致命问题:同一个问题往往横跨多个工具。举个例子,“训练时显存不够”这个现象,可能涉及数据加载策略、梯度累积、混合精度、通信缓冲区大小、甚至容器内的共享内存配置。如果按工具切分,你得在四五个章节之间来回跳,很难形成因果链。

按层次组织的好处是,每一层有明确的职责边界。硬件层负责算力和显存,通信层负责多卡多机之间的数据交换,框架层负责把模型定义翻译成硬件能执行的指令,推理层负责把训练好的模型高效地服务出去,编排层负责把这些组件调度到集群上并保证可用性。当你遇到一个问题时,先判断它发生在哪一层,然后沿着层与层之间的接口往上或往下排查,思路会清晰很多。

注意:层次划分不是绝对的,有些组件横跨多层,比如 NCCL 既涉及通信也涉及硬件拓扑感知。我在笔记里会把它归到通信层,但在相关章节会标注它与其他层的交互点。

2.2 每一层需要回答的三个核心问题

为了让笔记不流于表面,我给每一层都设定了三个必须回答的问题。第一,这一层存在的根本原因是什么,也就是它解决了什么不可回避的矛盾。第二,这一层的核心抽象和关键组件有哪些,它们各自的职责边界在哪里。第三,这一层最常见的性能瓶颈和故障模式是什么,排查手段有哪些。

以通信层为例。它存在的根本原因是单卡显存和算力有物理上限,模型规模一旦超过单卡承载能力,就必须把计算图切分到多卡上,而切分之后卡与卡之间需要交换梯度、激活值或参数,这就产生了通信需求。核心抽象包括集合通信原语(AllReduce、AllGather、ReduceScatter 等)和通信拓扑(Ring、Tree、NVLink、InfiniBand 等)。常见瓶颈则是通信带宽不足导致计算单元空转,或者拓扑感知没做好导致跨节点通信走了低速链路。

这三个问题贯穿每一层,你在阅读后续章节时也可以带着它们去对照,看看自己是否真的理解了每一层的设计动机。

2.3 知识全景的依赖关系图(文字描述)

虽然不能用图表,但我可以用文字把层与层之间的依赖关系说清楚。最底层是硬件层,包括 GPU、高速互联总线、网卡和存储设备。硬件层之上是驱动与运行时层,包括 GPU 驱动、CUDA 运行时、通信库运行时。再往上是对外暴露编程接口的通信层和计算库层,比如 NCCL、cuBLAS、cuDNN。框架层构建在这些库之上,提供张量抽象、自动微分和分布式策略。推理与服务平台层则面向线上服务,处理批处理、量化、缓存和弹性伸缩。最上层是编排与运维层,负责资源调度、监控告警和故障恢复。

这个依赖关系意味着,当你排查一个上层问题时,往往需要逐层往下确认。比如推理服务延迟高,可能是编排层资源不足,也可能是推理层批处理策略不合理,还可能是框架层算子实现低效,甚至硬件层降频。有了这张全景图,你就知道该按什么顺序去排除。

3. 硬件与驱动层的关键细节

3.1 GPU 显存到底被谁吃掉了

很多人第一次遇到显存溢出时,第一反应是模型太大。但实际项目中,模型参数往往只占显存的一半甚至更少。剩下的显存被几部分吃掉:激活值、梯度、优化器状态、通信缓冲区、CUDA 上下文和碎片。

以一个 7B 参数的模型为例,假设用 FP16 存储参数,光参数就是 14GB。训练时梯度同样需要 14GB,如果用的是 Adam 优化器,它还会为每个参数维护一阶和二阶动量,又是 28GB。这三项加起来已经 56GB,还没算激活值。所以单卡 80GB 显存训练 7B 模型时,如果不做任何优化,激活值稍微大一点就会溢出。

激活值的大小取决于批次大小和序列长度。粗略估算,每层激活值约占batch_size × seq_len × hidden_dim × 层数 × 精度字节数。实际中因为注意力机制的存在,序列长度对激活值的影响是平方级的。这也是为什么长序列训练特别吃显存。

实操心得:排查显存问题时,先用torch.cuda.memory_summary()看 reserved 和 allocated 的差值。如果 reserved 远大于 allocated,说明碎片严重,可以尝试设置PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True来缓解。

3.2 高速互联总线对训练效率的实际影响

单机多卡训练时,卡与卡之间的通信走的是高速互联总线。不同代际的总线带宽差异很大,这直接决定了张量并行和流水线并行的效率。如果总线带宽不足,通信时间会超过计算时间,多卡加速比就会惨不忍睹。

我实测过一个对比:同样的模型和批次大小,在总线带宽较高的机器上,四卡训练的吞吐是单卡的 3.6 倍;而在带宽较低的机器上,四卡吞吐只有单卡的 2.3 倍。差距就来自通信开销。所以做多卡训练前,一定要确认卡间互联的拓扑和带宽,别盲目堆卡。

拓扑感知也很重要。如果四张卡不是两两直连,而是通过桥接芯片中转,那么通信路径就会变长。你可以用nvidia-smi topo -m查看拓扑矩阵,确认卡间连接类型。理想情况下,参与张量并行的卡应该在同一高速互联域内。

3.3 驱动版本与运行时版本的匹配陷阱

驱动和运行时版本不匹配是新手最容易踩的坑之一。CUDA 运行时版本、GPU 驱动版本、深度学习框架编译时依赖的 CUDA 版本,这三者必须兼容。常见现象是框架能 import 但一跑算子就报错,或者多卡通信库初始化失败。

我的经验是,先确定框架版本,然后查它官方文档推荐的 CUDA 版本,再根据 CUDA 版本去选驱动版本。不要反过来先装最新驱动再随便装框架。另外,容器化部署时,宿主机驱动版本必须不低于容器内运行时要求的最低版本,否则容器内会看不到 GPU 或者通信库初始化失败。

注意:升级驱动前一定要确认当前训练任务是否在跑,驱动升级通常需要重启机器,会中断所有 GPU 任务。生产环境建议先在测试节点验证。

4. 通信层的核心机制与调优

4.1 集合通信原语到底在做什么

集合通信原语是多卡训练的基石。最常用的是 AllReduce,它的作用是把所有卡上的梯度求和后广播回每一张卡。Ring AllReduce 是最经典的实现,它把每张卡的数据分成 N 份,通过两轮环形传递完成求和与广播,通信量与卡数无关,只与数据量有关。

除了 AllReduce,还有 AllGather、ReduceScatter、Broadcast、AllToAll 等。AllGather 是把所有卡的数据拼起来分发到每张卡,常用于张量并行中拼接分片结果。ReduceScatter 是先求和再分散,常与 AllGather 配合实现 AllReduce 的等价效果但更省显存。AllToAll 则用于专家并行中把 token 路由到对应的专家所在卡。

理解这些原语的关键是搞清楚数据在卡之间怎么流动、通信量是多少、能否与计算重叠。比如在流水线并行中,如果能把通信和计算重叠起来,就能隐藏大部分通信开销。

4.2 通信与计算重叠的实操方法

通信与计算重叠是提升多卡效率的核心手段。基本思路是在计算当前微批次的同时,传输上一个微批次需要通信的数据。框架层面,PyTorch 的 DDP 已经内置了梯度分桶和通信重叠,但默认配置不一定最优。

你可以调整bucket_cap_mb参数来控制梯度分桶大小。桶太小,通信次数多,启动开销大;桶太大,重叠效果差,因为要等整个桶的梯度算完才能开始通信。经验值是在 25MB 到 50MB 之间,具体取决于模型结构和网络带宽。我通常先用默认值跑一轮,看通信时间占比,再逐步调整。

另一个技巧是使用gradient_as_bucket_view=True,让梯度直接写入通信桶,减少一次内存拷贝。这个选项在显存紧张时特别有用,但要注意它和某些自定义梯度操作可能不兼容。

4.3 多机通信的瓶颈定位

多机训练时,瓶颈往往出现在节点间的网络上。先用ibstat或网卡厂商工具确认链路速率和状态。然后跑一个纯通信的基准测试,比如 NCCL 自带的测试工具,看实际带宽是否达到链路标称值。

如果带宽远低于预期,检查几个点:一是网卡是否绑定了正确的驱动和固件;二是通信是否走了低速路径,比如本该走高速网络却走了管理网;三是是否有其他任务在抢占带宽。我遇到过一次,两台机器之间的通信带宽只有标称的十分之一,最后发现是其中一台机器的网卡协商速率降级了,换线后恢复正常。

实操心得:多机训练前,务必先跑一次全节点通信基准测试,把每个节点对的带宽都测一遍。不要假设新机器就没问题,线缆和光模块的故障率比想象中高。

5. 训练框架层的分布式策略

5.1 数据并行、张量并行、流水线并行的选择逻辑

数据并行是最简单的并行方式,每张卡持有完整模型副本,处理不同批次数据,梯度通过 AllReduce 同步。它的优点是实现简单、扩展性好,缺点是每张卡都要存完整模型和优化器状态,显存占用高。

张量并行把单个算子切分到多张卡上,比如把矩阵乘法按列切分。它的优点是能降低单卡显存占用,缺点是通信频繁,对卡间带宽要求极高,通常只在单机内使用。

流水线并行把模型按层切分成多个阶段,每个阶段放在不同卡上,数据像流水线一样依次通过各阶段。它的优点是能跨节点扩展,缺点是存在流水线气泡,需要精心设计微批次调度来减少空闲时间。

实际项目中,这三种策略经常混合使用。比如单机内用张量并行降低显存,机间用流水线并行扩展规模,同时外层再套数据并行提升吞吐。选择哪种组合,取决于模型大小、集群规模、卡间带宽和节点间带宽。

5.2 混合并行下的显存与通信平衡

混合并行最难的是一碗水端平。张量并行度太高,通信开销大;流水线并行度太高,气泡多;数据并行度太高,显存不够。我通常按以下顺序决策:先看单卡显存能放下多大的模型分片,确定张量并行度;再看单机内卡数是否够用,不够则引入流水线并行;最后用数据并行填满剩余资源。

举个例子,假设有 16 台机器,每台 8 张卡,共 128 张卡。模型有 70B 参数,FP16 下参数加优化器状态约 560GB。如果单卡 80GB,至少需要 8 张卡才能放下模型状态,所以张量并行度设为 8,正好用满一台机器。剩下 16 台机器做数据并行,每台机器内 8 卡做张量并行。这样配置下,机内通信走高速总线,机间通信只同步梯度,压力小很多。

5.3 梯度累积与微批次调度的配合

梯度累积是在显存受限时增大有效批次大小的常用手段。它的原理是多次前向反向累积梯度,再一次性更新参数。但梯度累积和流水线并行配合时要注意,微批次的数量要能被流水线阶段数整除,否则会出现气泡或数据不均衡。

我一般会先确定全局批次大小,然后根据单卡显存确定微批次大小,再算出需要累积多少步。如果同时用流水线并行,还要确保累积步数是流水线阶段数的整数倍。这些参数在训练脚本里通常都有对应配置项,但文档往往讲得不清楚,需要自己推导一遍。

注意:梯度累积时,如果用了 BatchNorm,要注意统计量的更新频率。累积步数大于 1 时,BatchNorm 的 running mean 和 variance 更新会变慢,可能需要调整 momentum 或改用 GroupNorm。

6. 推理与服务平台层的核心要点

6.1 推理引擎的批处理策略怎么选

推理服务和训练最大的区别是,推理对延迟敏感,而且请求是动态到达的。批处理策略直接决定了吞吐和延迟的平衡。静态批处理是攒够一批再一起推理,延迟高但吞吐大。动态批处理是请求随到随处理,延迟低但吞吐受限于单次处理能力。连续批处理则是在一个批次里,已经完成的序列提前退出,新请求随时加入,兼顾吞吐和延迟。

选择哪种策略取决于业务场景。离线批量推理可以用静态批处理,把批次开到最大。在线对话服务适合连续批处理,因为请求长度差异大,连续批处理能显著提升 GPU 利用率。我实测过一个对话模型,用连续批处理比静态批处理吞吐提升了近三倍,平均延迟还降低了。

6.2 量化对推理性能的实际影响

量化是降低推理成本的重要手段。常见的量化方案有 INT8 和 INT4。INT8 量化通常能把显存占用减半,吞吐提升 1.5 到 2 倍,精度损失很小。INT4 量化能把显存占用降到四分之一,吞吐提升更多,但精度损失需要评估。

量化不是万能的。有些模型对量化敏感,尤其是小模型或者输出分布尖锐的模型。我建议先在小规模流量上做 A/B 测试,对比量化前后的输出质量和延迟指标。另外,量化后的模型不一定在所有硬件上都能加速,有些老卡对 INT4 的支持不好,反而会变慢。

实操心得:量化时优先量化权重,激活值量化要谨慎。权重分布相对稳定,激活值受输入影响大,量化后容易出异常值。如果必须量化激活值,建议用动态量化或者保留部分层不量化。

6.3 推理服务的弹性伸缩与冷启动

推理服务的流量往往有波峰波谷。弹性伸缩能根据负载自动调整实例数,但冷启动是个大问题。模型加载、显存分配、预热推理都需要时间,如果扩容太慢,波峰来了服务已经超时了。

我的做法是保留一个最小实例数,保证基本容量。同时设置基于队列长度的扩容策略,而不是基于 CPU 或 GPU 利用率,因为利用率有滞后性。另外,模型加载可以做成懒加载加分片加载,先加载部分层就能开始服务,剩余层后台加载。预热则是在实例启动后先跑几轮空推理,把显存池和计算图都初始化好。

7. 编排与运维层的落地经验

7.1 资源调度如何避免 GPU 碎片

GPU 是稀缺资源,调度不当会产生大量碎片。比如一个任务申请 4 张卡,但集群里只有分散的 2+2,任务就调度不上去。解决思路一是用拓扑感知调度,尽量把任务调度到同一节点或同一高速互联域内;二是用 gang scheduling,要么全部满足要么不调度,避免部分分配导致的死锁。

Kubernetes 生态里,设备插件负责上报 GPU 资源,调度器负责分配。但默认调度器对 GPU 拓扑不感知,需要额外配置。我通常会给节点打上拓扑标签,然后在调度策略里加亲和性规则,让同一任务的卡尽量落在同一节点。

7.2 监控告警该盯哪些指标

AI Infra 的监控和普通服务不同,除了 CPU、内存、网络,还要盯 GPU 利用率、显存占用、通信带宽、温度、功耗和 ECC 错误。GPU 利用率低但显存占用高,可能是数据加载瓶颈。通信带宽低但计算利用率高,可能是通信没重叠好。温度高导致降频,会直接拉低训练速度。

告警阈值要根据业务设定。训练任务对延迟不敏感,但对中断敏感,所以显存溢出和 ECC 错误要立即告警。推理服务对延迟敏感,所以 P99 延迟和队列长度要重点监控。我一般会设置两级告警,警告级别先通知,严重级别直接触发自动扩容或重启。

7.3 故障恢复与断点续训的实操细节

大规模训练任务跑几天甚至几周,硬件故障是常态。断点续训能救命,但要做好并不简单。首先要定期保存检查点,保存频率取决于任务时长和可接受的回滚损失。其次要保证检查点的一致性,多卡保存时要等所有卡都完成再标记检查点有效。

恢复时要注意随机数状态、数据加载器状态和优化器状态都要恢复。只恢复模型参数是不够的,否则训练曲线会跳变。我习惯在检查点里额外保存当前步数、学习率调度器状态和每个数据加载器的偏移量。恢复后先跑几十步观察 loss 是否连续,确认无误再继续。

注意:检查点存储要选高吞吐的共享存储,不要存在本地盘。本地盘故障会导致检查点丢失,而且多机恢复时本地盘不可见。共享存储的写入带宽要能支撑所有卡同时写检查点,否则保存时间会很长。

8. 常见问题与排查技巧实录

8.1 训练速度突然变慢的排查顺序

训练速度变慢是最常见的问题,排查要按从外到内的顺序。先看硬件层,用nvidia-smi确认 GPU 是否降频、温度是否过高、是否有 ECC 错误。再看通信层,跑一个通信基准测试,确认带宽是否正常。然后看框架层,检查是否有数据加载瓶颈,比如 CPU 利用率是否打满、数据预处理是否太慢。最后看模型层,确认是否有异常值导致计算变慢。

我遇到过一次训练速度突然掉一半,最后发现是数据加载的 num_workers 设得太小,GPU 经常等数据。把 num_workers 调大后恢复正常。这个问题的隐蔽性在于,GPU 利用率看起来不低,但实际有效计算时间少。

8.2 多卡训练 loss 不收敛的常见原因

多卡训练 loss 不收敛,首先要确认数据是否被正确切分。如果每张卡拿到的是相同数据,梯度就失去了多样性,等价于单卡训练但批次没变。其次检查学习率是否随卡数调整,数据并行下全局批次变大,学习率通常要相应放大。然后检查梯度同步是否正确,有没有卡掉队导致梯度不一致。

还有一个容易被忽略的点是随机种子。如果每张卡的随机种子相同,数据增强和 dropout 的行为会完全一致,降低模型泛化能力。正确做法是给每张卡设置不同的种子,但保证整体可复现。

8.3 推理服务延迟毛刺的定位方法

推理服务延迟毛刺通常来自几个方面:批处理策略导致的长尾请求、显存碎片导致的分配延迟、GC 或后台任务抢占资源、网络抖动。定位时先看延迟分布,如果 P99 高但 P50 正常,说明是长尾问题,重点查批处理策略和请求长度分布。如果整体都高,查资源利用率和网络。

我处理过一次毛刺问题,最后发现是日志组件在高峰期同步写磁盘导致主线程阻塞。改成异步写日志后毛刺消失。这个问题的教训是,推理服务里的任何同步 IO 都可能成为毛刺源,包括日志、监控上报和配置读取。

问题现象可能原因排查手段解决方向
训练速度骤降GPU 降频、通信降级、数据瓶颈nvidia-smi、通信基准、CPU 监控散热、换线、调 num_workers
loss 不收敛数据未切分、学习率未调、种子相同检查数据加载、学习率配置、种子设置修正切分、缩放学习率、差异化种子
推理延迟毛刺批处理长尾、同步 IO、显存碎片延迟分布、IO 监控、显存摘要连续批处理、异步 IO、内存池配置
多机通信慢网卡降级、路由错误、带宽抢占ibstat、基准测试、流量监控换线、修路由、隔离流量
显存溢出激活值大、碎片、缓冲区大显存摘要、批次大小、通信桶梯度累积、内存池、调桶大小

9. 我在这条路上踩过的几个典型坑

第一个坑是盲目追求最新版本。有一次我把框架和通信库都升到最新,结果多卡训练直接 hang 住,回退版本后正常。后来我养成了习惯,生产环境只用经过验证的稳定版本组合,新版本先在测试环境跑通再上。

第二个坑是忽略数据加载的瓶颈。早期我总以为 GPU 是瓶颈,后来发现很多任务里 GPU 利用率只有百分之六七十,剩下时间都在等数据。优化数据加载后,同样的硬件训练速度提升了近四成。数据加载的优化包括用更快的存储、增加 worker 数、预取和缓存。

第三个坑是检查点保存太频繁。有一次我设了每 100 步保存一次,结果保存时间占了总训练时间的百分之十五。后来改成每 1000 步保存一次,并且用异步保存,训练效率明显提升。检查点频率要在容错和效率之间找平衡,任务越稳定,保存间隔可以越长。

第四个坑是监控指标太多导致告警疲劳。一开始我把所有能采集的指标都设了告警,结果每天收到几百条告警,真正重要的反而被淹没。后来我精简到只保留核心指标,并且分级告警,才让监控真正发挥作用。

这些坑的共同点是,它们都不是技术难题,而是工程决策问题。AI Infra 的很多挑战不在于某个算法多难,而在于如何在资源、效率、稳定性和成本之间做出合理的权衡。这份笔记里的每一层,最终都要落到这些权衡上。你在实际项目中遇到具体问题时,可以回到对应的层次,看看这一层的核心矛盾是什么,再结合手头的资源和约束去选择方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询