2、深度学习训练基础:模型训练流程、显存瓶颈、数据并行与模型并行概念

在正式进入 DeepSpeed 的零冗余优化之前,必须先回到深度学习训练本身:一次训练迭代到底发生了什么?显存被谁吃掉了?当单卡放不下模型时,我们有哪些拆分思路?只有把这些问题看清楚,后面理解 ZeRO 的“分片”才有落脚点。

一、一次训练迭代的完整流程

无论模型多大,单步训练都可以拆成四个阶段:前向传播、损失计算、反向传播、参数更新。前向传播把输入 x 逐层变换得到预测 y_hat;损失函数比较预测与标签得到标量 loss;反向传播从 loss 出发,按链式法则求出每个参数对应的梯度;优化器再根据梯度更新参数。

for x, y in dataloader:
    y_hat = model(x)          # 前向传播
    loss  = criterion(y_hat, y)  # 损失计算
    loss.backward()           # 反向传播,产生 .grad
    optimizer.step()          # 参数更新
    optimizer.zero_grad()     # 梯度清零

关键点在于:反向传播需要用到前向传播时保存的中间激活值。也就是说,前向阶段不仅要算出结果,还要把每层的输入、输出等张量留在显存里,供反向阶段计算梯度使用。这直接决定了显存占用的规模。

二、显存到底被什么占满了

训练显存大致由四部分组成,理解它们的比例是排查 OOM 的基础:

  • 模型参数:权重本身,FP16 下每个参数 2 字节。
  • 梯度:与参数量同规模,通常也是 2 字节。
  • 优化器状态:Adam 会为每个参数保存一阶动量与二阶动量,混合精度训练还常保留 FP32 主权重,这部分往往是参数量的数倍。
  • 激活值:前向保存的中间张量,与 batch size、序列长度强相关,是波动最大的一块。
组成典型精度相对参数量倍数是否随 batch 变化
模型参数FP162 字节/参数否
梯度FP162 字节/参数否
优化器状态FP32约 12 字节/参数否
激活值FP16与层数、序列长度相关是
常见误区:很多人以为“模型能加载进显存就能训练”。实际上 Adam + 混合精度下,训练显存常是推理显存的 4~6 倍。能推理不等于能训练。

三、数据并行与模型并行

当单卡放不下时,第一反应通常是加卡。但“加卡”有两种完全不同的切法。

数据并行(Data Parallelism,DP):每张卡都持有一份完整模型副本,把一个大 batch 切成若干小 batch 分给各卡。各卡独立前向、反向,得到各自梯度,再通过 AllReduce 求平均,最后每张卡用相同梯度更新,从而保持参数一致。它的优点是实现简单、扩展性好;缺点是每张卡都要存完整模型、梯度、优化器状态和激活,显存并没有被分摊。

模型并行(Model Parallelism,MP):把模型本身切开。张量并行把单个矩阵乘按行或列拆到多卡,流水线并行把不同层分到不同卡。它能让单卡放不下的超大模型跑起来,但通信频繁、对切分策略和调度要求高,且容易出现卡间负载不均。

一句话区分:数据并行是“数据分家、模型复制”;模型并行是“模型分家、数据流动”。前者省时间,后者省显存。
数据并行 vs 模型并行 数据并行(DP) 完整模型 GPU0 完整模型 GPU1 完整模型 GPU2 batch 0 batch 1 batch 2 AllReduce 梯度平均 → 同步更新 每卡显存 = 完整模型 + 梯度 + 优化器 + 激活 模型并行(MP) 第 1~4 层 GPU0 第 5~8 层 GPU1 第 9~12 层 GPU2 同一份数据依次流过各卡 层间通信 / 流水线调度 每卡显存 = 部分模型 + 对应状态

四、一个真实场景:为什么 8 卡 DP 仍然 OOM

假设有一个 7B 参数模型,用 8 张 40GB 卡做数据并行,混合精度 + Adam:

参数 FP16:        7B × 2  = 14 GB
梯度 FP16:        7B × 2  = 14 GB
优化器状态 FP32:  7B × 12 = 84 GB
激活值:           随 batch 变化,约 10~30 GB
合计:             约 122~142 GB / 每卡

可以看到,即使有 8 张卡,每张卡依然要装下全部 122GB 以上的状态,40GB 显存远远不够。数据并行只分摊了数据,没有分摊模型状态。这正是 ZeRO 要解决的问题:把参数、梯度、优化器状态也切开,分给各卡保存,需要时再通信取回。

复盘思路:遇到 OOM 时,先判断是“激活值太大”还是“模型状态太大”。前者可以减小 batch、用梯度检查点;后者才需要 ZeRO 这类分片方案。方向错了,调参再多也没用。

五、小结与关键概念对照

  • 训练迭代 = 前向 + 损失 + 反向 + 更新,反向依赖前向保存的激活。
  • 显存四大块:参数、梯度、优化器状态、激活值,其中优化器状态占比最高。
  • 数据并行复制模型、切分数据,省时间不省显存。
  • 模型并行切分模型,能装下超大模型,但通信与调度复杂。
  • ZeRO 属于“数据并行 + 状态分片”,在保留 DP 易用性的同时分摊显存。

理解了这些基础,接下来看 DeepSpeed 如何用 ZeRO 把优化器状态、梯度、参数逐级分片,就能明白它为什么能在不改变模型代码的前提下大幅降低单卡显存。