🏗️ 系统架构

多模态大模型架构

📅 2026-05-20 👤 资深架构师 ⏱️ 15 分钟阅读
分布式训练 Data Parallel Tensor Parallel Pipeline Parallel 混合精度 ZeRO

📌 背景与挑战

随着大语言模型(LLM)参数规模从十亿级增长到万亿级,单卡已无法承载完整模型的训练。单GPT-4预计拥有约1.8万亿参数,在FP16下需约36TB显存,仅推理就需要约80块80GB A100。因此,分布式训练成为必然选择。

💡 核心挑战

大模型训练面临三大核心挑战:显存墙(模型+优化器状态+梯度+激活值)、通信墙(多卡通信带宽瓶颈)、计算墙(算力需求远超单机供给)。

💾

显存瓶颈

以175B参数模型为例:FP16权重占350GB,优化器状态(Adam)占700GB,梯度占350GB,激活值占数百GB。总计约1.4TB,远超单卡能力。

🌐

通信开销

数据并行需全局AllReduce同步梯度,张量并行需AllReduce隐藏计算,流水线并行需P2P通信。通信与计算的重叠程度直接决定训练效率。

⚙️

扩展性

随着GPU数量增加,通信占比上升,有效计算效率下降。NVLink提供320GB/s但PCIe仅64GB/s,网络带宽成为分布式瓶颈。

🔄

一致性

分布式环境下保证模型权重一致性、确保收敛性与单机一致是重大挑战。同步策略、参数更新顺序都影响最终模型质量。

📊 数据并行 (Data Parallelism)

数据并行是最广泛使用的分布式训练策略。其核心思想是:每张卡持有完整模型副本,将batch数据切分到不同卡上并行计算,最后聚合梯度更新模型

主流实现方案

🔄

DDP (DistributedDataParallel)

PyTorch原生实现。采用Ring-AllReduce通信拓扑,梯度同步与反向计算overlap。通信量:每个GPU发送+接收梯度总量 = 2 × 参数大小。

📦

ZeRO (Zero Redundancy Optimizer)

微软DeepSpeed提出,分为ZeRO-1/2/3三个阶段。ZeRO-3将模型参数分片,彻底消除数据并行中的显存冗余,可支持70B模型在单卡32GB上训练。

FSDP (FullyShardedDataParallel)

PyTorch 1.11+引入,等效于ZeRO-3。支持分片策略配置,可与混合精度结合,通过绑卡(local_rank)优化通信。

ZeRO 阶段详解

阶段 分片内容 通信量 显存节省
ZeRO-1 优化器状态分片 与DDP相同 ~4x
ZeRO-2 优化器状态 + 梯度分片 与DDP相同 ~8x
ZeRO-3 优化器状态 + 梯度 + 参数分片 增加参数Gather通信 ~Nx (N=GPU数)
图1: 数据并行梯度同步流程
GPU 0 Forward Backward Local Grad ↓ GPU 1 Forward Backward Local Grad ↓ GPU N-1 Forward Backward Local Grad ↓ 🔄 Ring AllReduce Reduce Scatter → All Gather 通信量: 2 × (参数大小) × (N-1) / N 📦 Parameter Server (可选) 中心化梯度聚合

代码示例: PyTorch DDP

import torch
import torch.nn as nn
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

# 初始化分布式环境
setup():
    dist.init_process_group("nccl")
    local_rank = int(os.environ["LOCAL_RANK"])
    torch.cuda.set_device(local_rank)

# 创建模型并包装DDP
model = nn.Linear(768, 768).cuda()
model = DDP(model, device_ids=[local_rank])

# 训练循环
for data in dataloader:
    output = model(data)
    loss = output.sum()
    loss.backward()  # DDP自动完成梯度同步
    optimizer.step()

🔲 张量并行 (Tensor Parallelism)

张量并行(TP)将单一层的权重矩阵按列或行切分到多个GPU,使每张卡只持有部分参数。适用于单层参数量巨大的场景,如Transformer中的FNN和Attention层。

核心切分策略

📐

Column Parallel (1D)

将权重矩阵按切分。输入X保持完整,每卡计算Y = X × W_i,最终通过AllReduce汇总。适用:Linear(W:[h, h]) → W_1:[h, h/N], W_2:[h, h/N]

📏

Row Parallel (1D)

将权重矩阵按切分。输入X切分到各卡,每卡计算Y_i = X_i × W_i,再通过AllReduce求和。通信:先AllReduce再计算。

🔲

2D / 2.5D Parallel

Megatron-LM采用2D矩阵乘法切分,基于SUMMA算法。对矩阵A、B按块划分到N×N网格,降低通信复杂度到O(N)。

图2: 张量并行 — Attention层切分示意
Input X (b, s, h) QKV Projection W_q | W_k | W_v Q K V GPU 0 Q_0, K_0, V_0 Attn_0 = softmax(Q_0K_0/s)V_0 AllReduce ↓ GPU 1 Q_1, K_1, V_1 Attn_1 = softmax(Q_1K_1/s)V_1 AllReduce ↓ GPU N-1 Q_n, K_n, V_n Attn_n = ... AllReduce ↓ Output Proj W_o [h, h] Column切分 通信: AllReduce Output (b, s, h)

Megatron-LM 核心实现

# Megatron-LM: Column Parallel Linear
class ColumnParallelLinear(nn.Module):
    def __init__(self, input_size, output_size, world_size):
        self.world_size = world_size
        self.output_size_per_partition = output_size // world_size
        self.weight = nn.Parameter(
            torch.randn(self.output_size_per_partition, input_size))

    def forward(self, x):
        # 异步通信: 等待x到达 (可用CUDA stream overlap)
        output_parallel = F.linear(x, self.weight)
        # AllReduce汇总各卡输出 (每卡输出是Y的子列)
        output = all_reduce(output_parallel)
        return output

# 计算通信量: Column切分需AllReduce (每卡发送N-1次)
# 通信量 = 2 × (seq_len × hidden) × (world_size - 1) / world_size

⚠️ 张量并行注意事项

TP要求所有卡参与计算同一batch,且需要NVLink高速互联(否则通信成为瓶颈)。TP度等于Transformer层数,每层需两次AllReduce。GPT-3(175B)使用TP=8时,每层通信约3.4GB。

🔁 流水线并行 (Pipeline Parallelism)

流水线并行(PP)将模型按层切分到不同GPU,每个设备负责若干连续层的计算。数据在设备间以micro-batch为单位流水线式传递,大幅提升设备利用率。

调度策略对比

📥

Forward Only (1F)

先执行全部Forward,再执行全部Backward。简单但显存占用高,需保存所有激活值用于反向传播。

🔄

1F1B (One Forward One Backward)

交替执行Forward和Backward。最小化显存占用,但流水线填充和排空阶段存在空闲(GPU bubble)。

Interleaved 1F1B

每个设备处理多个micro-batch,以更细粒度调度减少bubble。通信量增加但效率更高。

🌊

Async Pipeline

允许各阶段异步执行,如PipeDream、Chimera等。牺牲严格梯度一致性换取吞吐量提升。

图3: 流水线并行 — 4阶段8 Micro-batch调度
时间 → Stage 0 Stage 1 Stage 2 Stage 3 F0 F1 F2 F3 B3 B2 B1 B0 idle - F0 - F1 F2 F3 B3 B2 B1 - - F0 F1 F2 B3 B2 - - F0 F1 B3 idle Forward Backward Bubble (空闲) Bubble比例 ≈ (P-1) / (2P) ,P为流水线阶段数。4阶段时bubble约37.5%。

PipeDream 实现要点

# PipeDream: 异步流水线 Forward/Backward 交错执行
class PipelineStage(nn.Module):
    def __init__(self, stage_id, num_stages):
        self.stage_id = stage_id
        self.num_stages = num_stages
        self.buffer = {}  # 缓存激活值用于反向

    def forward(self, microbatches):
        for i, x in enumerate(microbatches):
            output = self.model(x)
            self.buffer[i] = x.detach()  # 保存输入用于反向
            # 发送output到下一阶段 (异步send)
            async_send(output, dst=self.stage_id + 1)

    def backward(self):
        # Stale Gradient: 使用版本号处理异步一致性
        for i in reversed(range(len(self.buffer))):
            grad_output = recv(src=self.stage_id + 1)
            version = self.buffer_versions[i]
            # 检查版本差,使用weight staling补偿
            if version > expected_version:
                self.optimizer.step_warmup()  # 多次小步更新补偿

💡 Bubble 效率计算

流水线并行存在 "pipeline bubble":首尾阶段的空闲时间。气泡比例 ≈ (P-1) / (2m),其中P=阶段数,m=micro-batch总数。Interleaved调度可将bubble降低到(P-1) / (2Pm)

🔀 混合并行策略

实际大模型训练如GPT-3、PaLM均采用三维混合并行:数据并行(DP) × 流水线并行(PP) × 张量并行(TP)。每种并行在不同维度上解决不同问题,三者互补。

图4: 3D混合并行 — DP×PP×TP 布局
数据并行 (DP) — 副本间独立 每副本持有完整模型,DP数量 = 全局GPU数 / TP数 / PP数 TP Group (TP并行) 同一PP stage内按张量切分 TP Group PP Stage 0 Embedding + Layers 0-11 12 layers GPU0 GPU1 GPU2 GPU3 micro-batch PP Stage 1 Layers 12-23 12 layers GPU0 GPU1 GPU2 GPU3 PP Stage 2 Layers 24-35 12 layers GPU0 GPU1 GPU2 GPU3 DP Replica 0 DP Replica 1 DP Replica 2 GPU 拓扑映射 (8 GPU示例: TP=2, PP=4, DP=1) GPU 0,1 → TP group | GPU 0,2 → Stage 0 | GPU 1,3 → Stage 1 | ... PP依赖: FWD/BWD需按序执行 | TP依赖: AllReduce需同步 | DP依赖: AllReduce需同步

并行配置实例

模型 参数量 GPU TP PP DP ZeRO
GPT-3 175B 1024×A100 1 8 128 ZeRO-2
PaLM 540B 6144×TPU v4 1 12 512
Megatron-Turing 530B 2800×A100 8 10 35
LLaMA-2-70B 70B 128×A100 4 8 4 ZeRO-3

🎯 混合精度训练

混合精度(FP16/BF16)训练在保持模型精度的同时,将计算速度提升2-8倍、显存占用减半。NVIDIA Apex和PyTorch AMP是主要实现方式。

精度格式对比

🔢

FP32 (Full Precision)

32位浮点,1符号位 + 8指数位 + 23尾数位。Master weights存储格式,保证梯度更新精度。显存占用最高。

🔢

FP16 (Half Precision)

16位浮点,1符号位 + 5指数位 + 10尾数位。动态范围约6×10^4,训练稳定但可能溢出。NVIDIA TensorCore原生支持。

🔢

BF16 (Brain Float)

Google TPU引入,1符号位 + 8指数位 + 7尾数位。指数位与FP32相同,无溢出风险。LLM训练推荐格式,Ampere+支持。

混合精度训练流程

import torch.cuda.amp as amp

model, optimizer = initialize_model()

# PyTorch AMP (Automatic Mixed Precision)
scaler = amp.GradScaler()

for data, target in dataloader:
    with amp.autocast():
        output = model(data)
        loss = loss_fn(output, target)

    # 缩放损失防止下溢,梯度自动clip
    scaler.scale(loss).backward()

    # unscale后进行梯度裁剪
    scaler.unscale_(optimizer)
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

    # 更新参数,自动处理精度转换
    scaler.step(optimizer)
    scaler.update()

Loss Scaling 机制

FP16表示范围有限(约6×10^-5到6×10^4),反向传播梯度容易下溢(underflow)。GradScaler通过动态缩放损失值,将梯度"放大"到FP16可表示范围,待反向计算完毕后再恢复正确数值。

图5: 混合精度训练流程
1. Forward FP16 计算 模型 + 激活值 2. Loss Scaling 乘以scale因子 避免梯度下溢 3. Backward FP16 计算 缩放后梯度 4. Unscale + Clip 除以scale因子 梯度裁剪 5. 更新 FP32 Master

⚠️ BF16 vs FP16 选择

对于LLM训练,推荐使用BF16。BF16与FP32拥有相同的指数位范围(约1×10^-38到1×10^38),几乎消除了溢出风险。V100不支持BF16计算(仅存储),A100及以上支持完整的BF16 TensorCore计算。

🌐 通信优化

分布式训练中通信开销往往是性能瓶颈。通过计算-通信overlap、通信融合、拓扑感知等策略可显著提升训练效率。

🔀

计算-通信Overlap

利用CUDA Stream让反向计算与梯度同步并行执行。PyTorch DDP默认开启,通过delay_allreduce参数控制overlap粒度。

🔗

通信融合 (Fusion)

将多个小梯度张量合并为一次大通信。NCCL提供fuse因子配置,梯度在本地先求和再同步,减少通信启动开销。

🗺️

拓扑感知 (Topology-Aware)

NVLink > PCIe > CPU互联。PyTorch通过local_rank和NCCL_LOCAL_RANK感知拓扑,将TP/PP组优先绑定到NVLink域内。

📊

梯度压缩 (Compression)

1bit Adam、PowerGRAD等算法压缩梯度后再通信。压缩比8-32x,但引人额外计算开销,适合低带宽场景。

NCCL通信原语

原语 描述 通信量 典型场景
AllReduce 全局归约并广播到所有节点 2 × (N-1) / N × D DDP梯度同步
AllGather 收集所有节点数据并广播 (N-1) × D ZeRO-3参数收集
ReduceScatter 归约后分片到各节点 (N-1) × D Ring-AllReduce中间步骤
Broadcast 从根节点广播到所有 (N-1) × D 参数初始化广播
Send/Recv 点对点传输 D PP阶段间传递

PyTorch 通信优化示例

# 1. 梯度同步与计算Overlap
model = DDP(model, device_ids=[local_rank], broadcast_buffers=False)

# 2. 本地梯度预聚合再同步 (PyTorch 2.0 compile)
# 使用 torch.distributed._tensor 并行原语融合小通信
from torch.distributed.tensor.parallel import PairwiseParallel

model = tensor_parallelize(model, tp_size, style=PairwiseParallel())

# 3. NCCL配置优化
import os
os.environ["NCCL_ALGO"] = "NCCLAlgoRing"        # Ring算法更适合多节点
os.environ["NCCL_NSOCKS_PERTHREAD"] = "4"   # 增加连接数
os.environ["NCCL_SOCKET_NPEER"] = "8"          # 每socket对等数

⚙️ 分布式策略实践

实际项目中选择何种并行策略,需综合考虑模型规模、硬件拓扑、显存容量和通信带宽。

决策框架

模型规模 单卡容量 推荐策略 说明
< 7B 80GB ZeRO-3 + DP 单卡可放模型,ZeRO-3分片优化器状态
7B - 30B 80GB TP4 + PP + ZeRO-3 TP=4将层内参数切分,PP处理层间
30B - 100B 80GB TP8 + PP + ZeRO-2 TP=8 + 更高PP阶段,ZeRO-2分片梯度
> 100B 80GB TP8 + PP + DP + ZeRO-3 完整3D并行,深度优化通信overlap

DeepSpeed 配置示例

# ds_config.json — LLaMA-2 70B 训练配置
{
  "train_batch_size": 64,
  "train_micro_batch_size_per_gpu": 1,
  "gradient_accumulation_steps": 64,

  "zero_optimization": {
    "stage": 3,
    "offload_optimizer": {
      "device": "cpu"
    },
    "offload_param": {
      "device": "cpu"
    },
    "overlap_comm": true,
    "contiguous_gradients": true,
    "reduce_bucket_size": 5e8,
    "stage3_prefetch_bucket_size": 5e8
  },

  "fp16": {
    "enabled": true,
    "loss_scale_window": 100
  },

  "gradient_clipping": 1.0,
  "zero_allow_untested_optimizer": true
}

关键技术指标

📝 总结

大模型分布式训练是一个系统工程,需要在计算效率、显存效率、通信效率三者间取得平衡。

📊

数据并行

横向扩展,适用于大规模小模型或数据量巨大的场景。ZeRO彻底解决了显存冗余问题,使超大规模训练成为可能。

🔲

张量并行

层内参数切分,解决单层参数量超单卡显存的问题。需NVLink高速互联,通信密集,适合大模型单层。

🔁

流水线并行

层间切分,天然适合多层Transformer。Interleaved调度显著降低bubble,但需精细调度优化。

🎯

混合精度

BF16/FP16混合训练,显存减半速度提升2-8倍。配合Loss Scaling几乎无损,是LLM训练标配。

🔮 未来趋势

序列并行(SP)、专家并行(EP)、异步流水线、自定义通信内核、模型量化(KV Cache Int4)等技术将进一步提升分布式训练效率,降低LLM训练成本。