一、总框架

大模型分布式技术主要解决两个问题:

  1. 模型放不下:一张GPU显存不足
  2. 训练速度慢:一张GPU计算能力不足

不同技术解决的问题不同:

二、一个模型到底是怎么训练的

假设模型看到一句话:

输入:我今天去学校
目标:预测下一个Token

一次训练经过四步。

1、前向传播 Forward

数据经过模型:

输入Token
  ↓
Embedding
  ↓
Transformer第1层
  ↓
Transformer第2层
  ↓
输出概率

模型根据当前参数计算预测结果。

例如:

模型预测:“我今天去公园”
正确答案:“我今天去学校”

2、计算损失Loss

Loss衡量预测结果与正确答案相差多少:

预测越准确 → Loss越小
预测越错误 → Loss越大

3、反向传播 Backward

反向传播计算:

每一个参数应该往哪个方向调整,才能让Loss下降?

这个调整方向叫梯度 Gradient。

假设一个参数当前是:

w = 0.5

反向传播算出:

gradient = 0.1

表示增大这个参数可能让Loss升高,因此优化器会尝试减小它。

4、优化器更新参数

最简单的更新形式是:

新参数 = 旧参数 - 学习率 × 梯度

不断重复这个过程,模型就逐渐学习到了数据规律。

三、训练为什么需要那么多显存

训练至少保存四类数据:

1、模型参数 Parameters

模型真正学习的内容,例如:

Embedding权重
Attention权重
MLP权重
LayerNorm参数

如果模型有10亿参数,即1B参数,使用FP16保存:

1B × 2字节 ≈ 2GB

这只是模型权重。

2、梯度 Gradients

每一个需要训练的参数通常都需要对应的梯度。

参数:w
梯度:dw

如果参数需要约2GB,FP16梯度也可能再占约2GB

3. 优化器状态 Optimizer States

Adam优化器不只保存参数,还会为参数维护一阶、二阶动量等状态。

简单理解:

参数
梯度
历史平均梯度
历史平方梯度
可能还有FP32参数副本

所以一个“参数权重只有2GB”的模型,完整训练可能需要十几GB甚至更多显存。

4. 激活值 Activations

前向传播时,每层产生的中间结果需要暂时保存,因为反向传播还要使用。

激活值通常受到以下因素影响:

Batch Size
序列长度
隐藏层大小
Transformer层数

所以序列越长、Batch越大,训练显存通常越高。

最终可以记成:

训练显存
≈ 模型参数
+ 梯度
+ 优化器状态
+ 激活值
+ 临时计算空间

这就是ZeRO存在的根本原因:它主要切分前三类模型训练状态。

四、什么是分布式训练

分布式训练就是:

让多个计算进程、多个GPU甚至多台机器共同完成一次模型训练。

例如有两张GPU:

GPU 0
GPU 1

但有两张GPU,不代表显存会自动合并。

假设每张卡24GB:

错误理解:两张卡自动变成48GB显存

实际上默认情况下:

GPU 0有自己的24GB
GPU 1有自己的24GB

两张卡拥有独立显存。只有把模型或训练状态主动切分,才能利用两张卡共同承载一个更大的模型。

因此,多GPU训练的核心是两个动作:

切分
通信

先决定把什么切开,再决定GPU之间如何交换数据。

五、分布式训练中的基础名词

假设一台服务器有两张GPU,通常启动两个训练进程:

进程0 → GPU 0
进程1 → GPU 1

Process

Process就是操作系统中的训练进程。

分布式GPU训练通常采用:

一张GPU对应一个进程

Rank

每个进程的全局编号。

双卡时:

进程0:rank = 0
进程1:rank = 1

rank 0通常负责打印日志、汇总结果,但它不代表性能更强。

World Size

总进程数量。

双卡单机通常是:

world_size = 2

四卡则是:

world_size = 4

Local Rank

当前机器上的进程编号。

单机双卡:

local_rank 0 → GPU 0
local_rank 1 → GPU 1

多机训练时,rank和local rank不一定相同。

六、数据并行 DDP

数据并行的核心是:

每张GPU保存一个完整模型,但处理不同的数据。

假设原始Batch有8条数据,两张GPU可以这样分:

GPU 0:完整模型 + 前4条数据
GPU 1:完整模型 + 后4条数据

具体流程如下:

为什么需要同步梯度

GPU 0看到了数据A,得到梯度:

g0

GPU 1看到了数据B,得到梯度:

g1

如果它们直接更新参数:

GPU 0使用g0更新
GPU 1使用g1更新

两份模型就会逐渐变得不同。

所以DDP会计算平均梯度:

g = (g0 + g1) / 2

然后两张卡都使用相同的 g 更新参数。这样两份模型仍然保持一致。

All-Reduce是什么

All-Reduce是一种集合通信操作。

可以把它理解成:

收集所有GPU的梯度
→ 求和或求平均
→ 把结果发回所有GPU

DDP的主要通信成本就来自梯度All-Reduce。

DDP解决什么问题

DDP主要提高训练吞吐:

一张卡一次处理4条
两张卡一次共处理8条

但它不能显著解决模型本身放不下的问题,因为每张卡仍保存完整模型、完整梯度和完整优化器状态。

例如模型训练需要30GB:

GPU 0:需要30GB
GPU 1:也需要30GB

即使两张GPU分别有24GB,DDP仍然会OOM。

全局Batch Size

DDP中:

全局Batch
= 每卡Batch
× 数据并行GPU数
× 梯度累积次数

例如:

每卡Batch = 4
GPU数 = 2
梯度累积 = 2

全局Batch = 4 × 2 × 2 = 16

DataParallel和DistributedDataParallel的区别

“数据并行”是一个思想。

PyTorch中有两个容易混淆的类:

  • DataParallel:旧的单进程多卡方式。
  • DistributedDataParallel,简称DDP:多进程多卡方式。

实际学习应该重点学习DDP。

七、ZeRO是什么

ZeRO全称是:

Zero Redundancy Optimizer

可以理解为:

消除数据并行中重复保存的训练状态。

DDP哪里存在重复

两张GPU进行DDP时:

GPU 0:
完整参数
完整梯度
完整优化器状态

GPU 1:
完整参数
完整梯度
完整优化器状态

两张卡保存了大量重复内容。

ZeRO认为:

既然GPU之间能够通信,就没必要让每张卡长期保存所有训练状态。

ZeRO-1

切分优化器状态:

GPU 0:保存一半优化器状态
GPU 1:保存另一半优化器状态

但是每张卡仍保存完整:

模型参数
梯度

ZeRO-2

继续切分梯度:

GPU 0:一半优化器状态 + 一半梯度
GPU 1:另一半优化器状态 + 另一半梯度

每张卡仍保存完整模型参数。

ZeRO-3

继续切分模型参数:

GPU 0:一半参数 + 一半梯度 + 一半优化器状态
GPU 1:另一半参数 + 另一半梯度 + 另一半优化器状态

参数切开后怎么计算

这是ZeRO-3最关键的底层问题。

某张GPU计算某一层时,需要这一层的完整参数。于是ZeRO-3会:

计算前:从其他GPU收集当前层需要的参数
计算中:使用完整的当前层参数
计算后:重新释放或分片这些参数

所以ZeRO-3可以大幅节省显存,但会增加参数收集通信。

因此:

ZeRO级别越高
通常越省显存
但通信和管理成本也可能越高

ZeRO的目标首先是“让模型能够训练”,而不是保证“训练一定更快”。

八、模型并行是什么

模型并行不是某一种具体技术,而是一大类方法:

一个模型太大或计算量太高,把模型本身拆到多张GPU上共同完成一次前向和反向传播。

它与数据并行最大的区别是:

数据并行:每张GPU都有完整模型,切分数据。
模型并行:一个模型分布在多张GPU上,GPU共同处理数据。

模型并行主要包括:

1、为什么需要模型并行

假设模型完整训练需要40GB显存,但你有两张32GB GPU。

如果使用数据并行DDP:

GPU 0:保存完整40GB模型状态 → OOM
GPU 1:保存完整40GB模型状态 → OOM

DDP不会自动合并两张卡的显存,因为每张GPU都需要保存完整模型。

模型并行则会尝试拆开模型:

GPU 0:保存模型的一部分
GPU 1:保存模型的另一部分

这样单张GPU只需要承担部分模型。

模型并行主要解决:

  • 一个模型放不进单卡;
  • 模型的一层放不进单卡;
  • 单卡计算速度不够;
  • 推理时希望多张GPU共同降低延迟。

2、张量并行 TP

张量并行的核心是:

把同一个神经网络层中的大矩阵拆到多张GPU上计算。

它也叫层内并行 Intra-layer Parallelism。

1. 从线性层理解

Transformer中大量计算都是矩阵乘法。假设一个线性层:

Y = XW

其中:

X:输入
W:模型权重
Y:输出

如果权重矩阵 W 很大,可以把它拆到两张GPU。

2. 按列切分

将权重矩阵按输出维度拆开:

W = [W₀, W₁]

GPU分别计算:

GPU 0:Y₀ = XW₀
GPU 1:Y₁ = XW₁

最后拼接:

Y = [Y₀, Y₁]

过程如下:

每张GPU只保存部分权重,也只计算部分输出。

这种方式通常叫:

Column Parallel
列并行

3. 按行切分

也可以把输入和权重按输入维度拆开:

X = [X₀, X₁]

W = [W₀
     W₁]

GPU分别计算:

GPU 0:Y₀ = X₀W₀
GPU 1:Y₁ = X₁W₁

完整结果是:

Y = Y₀ + Y₁

所以需要All-Reduce求和:

这种方式叫:

Row Parallel
行并行

4. Transformer里怎么切

一个Transformer层主要包括:

Attention
MLP
LayerNorm
残差连接
Attention切分

假设模型有16个Attention Head,使用TP=2:

GPU 0:负责Head 0~7
GPU 1:负责Head 8~15

每张卡只计算一半注意力头,然后通过通信合并结果。

所以通常要求:

Attention Head数量能够被TP数量整除

例如:

16个Head,TP=2:可以
16个Head,TP=4:可以
16个Head,TP=3:通常不可以
MLP切分

Transformer的MLP通常包含:

隐藏维度
→ 扩展维度
→ 激活函数
→ 恢复隐藏维度

假设:

hidden_size = 4096
intermediate_size = 11008

可以让:

GPU 0:负责一部分intermediate维度
GPU 1:负责另一部分intermediate维度

最后再通过All-Reduce合并。

5. TP为什么通信频繁

Transformer每一层都需要多张GPU共同计算:

第1层:GPU通信
第2层:GPU通信
第3层:GPU通信
……
第32层:GPU通信

因此TP非常依赖GPU之间的通信速度。

如果计算时间是:

每层计算:5毫秒
每层通信:1毫秒

影响不算太大。

如果模型很小:

每层计算:0.5毫秒
每层通信:1毫秒

那么双卡可能比单卡还慢。

这也是为什么:

张量并行主要用于大模型,而不是卡越多就越快。

6. TP解决什么问题

TP适合:

  • 一个Transformer层太大,单卡放不下;
  • 模型推理权重放不进单卡;
  • 希望多张GPU共同完成一次推理;
  • GPU之间具有较快连接。

TP的优点:

  • 分摊单层参数;
  • 分摊单层计算;
  • 可以降低单卡模型权重显存;
  • 大模型推理时可能降低延迟。

TP的缺点:

  • 几乎每层都需要通信;
  • 强依赖卡间带宽;
  • 模型维度需要满足切分条件;
  • 实现和Checkpoint管理较复杂。

3、流水线并行 PP

流水线并行的核心是:

按层切分模型,不同GPU负责不同层。

它也叫层间并行 Inter-layer Parallelism。

假设模型有12层,两张GPU:

GPU 0:Embedding + 第1~6层
GPU 1:第7~12层 + LM Head

训练数据流动过程:

前向传播

数据首先进入GPU 0:

输入
 ↓
GPU 0执行第1~6层
 ↓ 传递中间激活值
GPU 1执行第7~12层
 ↓
输出

GPU之间传递的不是完整模型参数,而是中间激活值。

反向传播则反过来:

GPU 1:计算第7~12层梯度
 ↓ 发送激活梯度
GPU 0:计算第1~6层梯度

所以PP主要通信:

前向:激活值
反向:激活值对应的梯度

TP主要在层内部频繁通信,而PP主要在流水线阶段边界通信。

流水线为什么会浪费时间

如果只有一个Batch:

时间1:GPU 0工作,GPU 1等待
时间2:GPU 1工作,GPU 0等待

这虽然解决了显存问题,却没有很好地并行计算。

因此需要Micro Batch。

Micro Batch

把一个Batch切成多个Micro Batch:

Micro Batch 1
Micro Batch 2
Micro Batch 3
Micro Batch 4

形成类似工厂流水线:

时刻1:GPU 0处理微批次1
时刻2:GPU 0处理微批次2,GPU 1处理微批次1
时刻3:GPU 0处理微批次3,GPU 1处理微批次2
流水线气泡

开始和结束阶段仍然会有GPU处于等待状态,这段空闲就叫流水线气泡。

假设:

流水线阶段数 = p
Micro Batch数量 = m

在简单调度下,流水线利用率可以粗略理解为:

利用率 ≈ m / (m + p - 1)

例如2个阶段:

m = 1:利用率约 1/2
m = 4:利用率约 4/5
m = 16:利用率约 16/17

Micro Batch越多,流水线利用率提高。

但Micro Batch增加也会影响调度、显存和优化过程。

PP的另一个难点:负载均衡

如果:

GPU 0负责的层计算需要1秒
GPU 1负责的层计算需要3秒

GPU 0完成后仍然要等待GPU 1。

所以不能只按“层数相等”切分,还要考虑:

每层参数量
每层计算量
激活值大小
通信数据量
......

九、TP和PP的核心区别

对比张量并行TP流水线并行PP
切分位置同一层的内部不同模型层
又叫层内并行层间并行
每张GPU保存每层的一部分一部分完整层
前向计算多张卡共同计算同一层数据依次经过不同GPU
主要通信All-Reduce、All-Gather激活值、梯度
通信频率基本每层都通信阶段边界通信
主要问题通信开销大流水线气泡
关键要求模型维度可切分各阶段负载均衡
适用场景单层太大模型层数多、整体太大

十、模型并行与ZeRO-3的区别

ZeRO-3也会切分模型参数,但它和TP仍然不同。

ZeRO-3

ZeRO-3主要切分的是参数存储:

GPU 0长期保存一部分参数
GPU 1长期保存另一部分参数

计算某一层前,再临时收集该层参数。

每个数据并行进程仍然处理自己的数据,并执行完整层计算。

核心目标是:

减少重复存储

TP

TP不仅切分参数存储,也切分矩阵计算:

GPU 0计算矩阵的一部分
GPU 1计算矩阵的另一部分

核心目标是:

分摊单层存储和单层计算

因此:

对比ZeRO-3TP
主要切分存储训练状态层内参数和计算
计算前收集当前层参数不需要每卡拥有完整矩阵
每张卡处理的数据通常不同通常共同处理同一批输入
主要目的省训练显存单层跨卡计算
通信参数收集、梯度分片层内All-Reduce/All-Gather

一句话:

ZeRO-3更像“分开保管,使用时临时收集”;TP更像“共同计算,每人完成一部分”。

十一、什么时候选择哪一种技术

情况1:模型能放入单卡,只是训练慢

选择:

DDP

情况2:模型参数能放入单卡,但优化器、梯度让训练OOM

选择:

ZeRO-1/2/3

情况3:模型的一层就放不进单卡

选择:

TP

情况4:每个层都能放进单卡,但完整模型放不下

选择:

PP
或ZeRO-3

情况5:模型非常大,多种方法单独使用都不够

选择:

DP + TP + PP

在多节点集群中通常倾向于:

节点内部高速互联:TP
节点之间:PP或DP

因为TP通信频率最高,应尽量放在通信最快的GPU组中。

更多推荐