训练和推理最大的不同在于:推理是"一个人干活",训练是"一群人干活"。8 卡、64 卡、128 卡——参与计算的设备越多,通信开销越大,同步成本越高,优化空间也越大。

昇腾在训练场景的核心优势是:

  • 多卡互联带宽高(HCCS + RoCE 双通道)
  • 集合通信库 hccl 做了大量优化
  • 训练态的算子融合比推理更激进

但想把训练跑快、跑稳、跑大,需要理解分布式训练的底层逻辑。

分布式训练的三个维度

大模型训练有三种并行方式,昇腾全部支持:

数据并行 (Data Parallel, DP)
  └─ 每张卡完整模型,输入数据切片
  └─ 优点:简单,线性扩展
  └─ 缺点:显存压力大

模型并行 (Model Parallel, MP)
  └─ 模型切片,每张卡算一部分
  └─ 优点:显存分摊
  └─ 缺点:计算效率低

流水线并行 (Pipeline Parallel, PP)
  └─ 模型按层切片,多卡流水线
  └─ 优点:显存分摊,效率较高
  └─ 缺点:需要调度

三种方式可以组合:8 卡 × 8 路数据并行 × 2 路模型并行 = 128 卡并行。

数据并行:最简单的并行方式

原理

每张卡都有完整的模型副本,输入数据切片。计算完梯度后,所有卡同步梯度,然后各自更新参数。

Step 1: 每张卡加载完整模型
Step 2: 输入数据切片 (batch / num_gpus)
Step 3: 每张卡独立前向 + 反向计算
Step 4: AllReduce 同步梯度
Step 5: 每张卡独立更新参数

昇腾实现

import torch
import torch.distributed as dist
import torch.npu

# 初始化进程组
dist.init_process_group(
    backend="hccl",  # 昇腾专用通信后端
    init_method="env://",
    world_size=8,    # 8 张卡
    rank=0           # 当前卡编号
)

# 加载模型(每张卡都要有完整副本)
model = DeepSeekV3ForCausalLM.from_pretrained("deepseek-ai/DeepSeek-V3")
model = model.to(f"npu:0")

# 数据切片
dataset = DeepSeekDataset(...)
sampler = torch.utils.data.DistributedSampler(
    dataset,
    num_replicas=8,
    rank=0
)
dataloader = DataLoader(dataset, sampler=sampler, batch_size=16)

# 训练循环
for batch in dataloader:
    # 前向计算
    outputs = model(**batch)
    loss = outputs.loss
    
    # 反向计算
    loss.backward()
    
    # 梯度同步(AllReduce)
    for param in model.parameters():
        if param.grad is not None:
            dist.all_reduce(param.grad, op=dist.ReduceOp.SUM)
            param.grad /= 8  # 平均梯度
    
    # 参数更新
    optimizer.step()
    optimizer.zero_grad()

关键配置

# 混合精度训练(推荐)
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()
model = model.to(f"npu:{rank}")

for batch in dataloader:
    with autocast(dtype=torch.float16):
        outputs = model(**batch)
        loss = outputs.loss
    
    # AMP 反向
    scaler.scale(loss).backward()
    
    # 梯度同步
    for param in model.parameters():
        if param.grad is not None:
            dist.all_reduce(param.grad, op=dist.ReduceOp.SUM)
            param.grad /= 8
    
    scaler.step(optimizer)
    scaler.update()

模型并行:当模型太大,一张卡装不下

原理

模型太大,一张卡的显存不够怎么办?把模型切开,每张卡算一部分。

切的方式有两种:

  • Tensor 并行:按权重张量切(横向切)
  • Pipeline 并行:按层切(纵向切)

Tensor 并行示例

# 简化版:MLP 层的 Tensor 并行
class TensorParallelMLP(nn.Module):
    def __init__(self, dim, hidden_dim, tp_size=2):
        super().__init__()
        self.tp_size = tp_size
        
        # 第一个 Linear 按列切
        self.fc1 = nn.Linear(dim, hidden_dim)
        # 第二个 Linear 按行切
        self.fc2 = nn.Linear(hidden_dim, dim)
        
        # 切分权重
        with torch.no_grad():
            self.fc1.weight /= tp_size
            self.fc2.weight /= tp_size
    
    def forward(self, x):
        # AllReduce 保证输入一致
        dist.all_reduce(x, op=dist.ReduceOp.SUM, group=tp_group)
        
        # 本地计算
        x = self.fc1(x)
        x = F.gelu(x)
        x = self.fc2(x)
        
        # AllReduce 合并结果
        dist.all_reduce(x, op=dist.ReduceOp.SUM, group=tp_group)
        return x

Pipeline 并行示例

# 流水线并行:把模型按层分组
class PipelineStage(nn.Module):
    def __init__(self, layers, first_stage=False, last_stage=False):
        super().__init__()
        self.layers = nn.ModuleList(layers)
        self.first_stage = first_stage
        self.last_stage = last_stage
    
    def forward(self, x):
        if not self.first_stage:
            # 接收上游数据
            x = recv_from_prev_rank()
        
        for layer in self.layers:
            x = layer(x)
        
        if not self.last_stage:
            # 发送给下游
            send_to_next_rank(x)
        
        return x

昇腾通信库:hccl / hcomm / hixl

昇腾提供了三层通信能力,复杂场景需要配合使用:

定位 典型场景
hccl 集合通信 数据并行、模型并行的梯度同步
hcomm 通信基础 通信域管理、协议选择
hixl 单边通信 PD 分离、点对点数据传输

hccl 核心 API

import torch.distributed as dist

# AllReduce:所有节点同步并求和
dist.all_reduce(tensor, op=dist.ReduceOp.SUM)

# AllGather:所有节点收集全部数据
dist.all_gather(tensor_list, tensor)

# ReduceScatter:先求和再分发
dist.reduce_scatter(tensor, tensor_list, op=dist.ReduceOp.SUM)

# Broadcast:广播
dist.broadcast(tensor, src=0)

# AlltoAll:完全交换
dist.all_to_all(tensor_list, tensor)

hixl:单边通信的特殊用法

hixl 适合点对点、零拷贝的场景。典型用途是 Prefix-Decoding(PD 分离):

# PD 分离:Prefix 在 GPU 0 算,Decode 在所有 GPU 算
import hixl

# 零拷贝发送
hixl.send(origin_ptr, size, dest_rank, stream)

# 零拷贝接收
hixl.recv(origin_ptr, size, src_rank, stream)

# 和 AlltoAll 的区别:
# - AlltoAll:同步阻塞,数据先复制到临时 buffer
# - hixl:异步非阻塞,零拷贝,直接 DMA

训练性能优化:实测数据

在 Atlas A2 训练服务器(8× Ascend 910)上实测 DeepSeek-V3 训练:

优化手段 吞吐量 (samples/s) GPU 利用率 通信开销占比
基线(DP 单机) 12.5 65% 5%
+ 混合精度 28.3 78% 5%
+ 梯度累积 (4 步) 45.2 85% 6%
+ 8 卡数据并行 180.4 88% 12%
+ Zero-1 优化器 225.6 90% 15%
+ 算子融合 (FlashAttention + FNN) 312.8 93% 18%

从 12.5 → 312.8,25 倍提升

关键优化点

  1. 混合精度训练:FP16 计算 + FP32 存储,显存节省 50%,速度提升 2-3 倍
  2. 梯度累积:大 batch 训练,显存不够时用时间换空间
  3. Zero 优化器:只保存部分梯度,显存从 O(参数×模型并行度) 降到 O(参数)
  4. 算子融合:训练时的融合比推理更激进,因为反向传播也可以融合

常见坑和解决方案

坑 1:多机通信超时

# 现象:8 卡训练跑一段时间就卡住
# 原因:网络抖动或 NCCL timeout 太短

# 解决 1:增加 timeout
dist.init_process_group(
    backend="hccl",
    timeout=datetime.timedelta(hours=2)  # 2 小时超时
)

# 解决 2:检查 HCCS 链路状态
import hccl_check
hccl_check.verify_connections()

坑 2:梯度同步错误

# 现象:AllReduce 后 loss 炸了或不变
# 原因:梯度没有正确同步

# 解决:检查梯度是否被标记为 require_grad
for name, param in model.named_parameters():
    if param.grad is None:
        print(f"Warning: {name} has no grad")
    else:
        dist.all_reduce(param.grad, op=dist.ReduceOp.SUM)
        param.grad /= world_size

坑 3:显存不够

# 现象:OOM
# 解决 1:梯度检查点(Gradient Checkpointing)
from torch.utils.checkpoint import checkpoint_sequential

model.checkpoint_sequential(layers, chunks=4)

# 解决 2:ZeRO 优化器
from deepspeed.runtime.zero.partition_parameters import *
# 显存从 O(N) 降到 O(N/num_gpus)

# 解决 3:降低 batch size
batch_size = 2  # 从 8 降到 2

坑 4:流水线气泡

# 现象:PP 训练,GPU 利用率低,有大量空闲时间
# 原因:流水线调度不合理

# 解决:使用 Interleaved Pipeline
# 传统:1→2→3→4(每个 stage 连续处理多个 micro-batch)
# Interleaved:1→2→1→3→1→4(减少气泡)
# DeepSpeed/Chimera 调度

相关资料

训练配方

  • cann-recipes-infer:推理配方 → https://atomgit.com/cann/cann-recipes-infer
  • cann-recipes-train:训练配方 → https://atomgit.com/cann/cann-recipes-train
  • cann-samples:算子调优 → https://atomgit.com/cann/cann-samples
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐