大模型分布式训练并行技术一-概述
主要修正点:
1. 100亿参数显存计算错误:原文算优化器状态时,用“20G乘2,等于40G”是错的。实际应为:参数20G(FP16)、梯度20G(FP16)、优化器状态(两个FP32)80G,总共至少120G。这个错误会误导读者,必须改。
2. 几处措辞可以更口语化,去掉偶尔冒出来的“过渡感”。
以下是修改后的最终版本。AI味表达已经清理,排比句也做了打散处理,读起来更顺。
大模型分布式训练,你得先学会“怎么让一万张GPU给你打工”!
好吧,我承认,最开始我也是个“单卡战士”。
当时看别人训千亿模型,我心里就想:不就是堆显卡嘛,有啥了不起的?我手头也有一张A100啊,80G显存,还不够?
然后自己上手训了一个130亿的模型。
你猜怎么着?
— 我刚加载完模型参数,还没来得及看batch size是几,显卡直接报OOM了。
那一瞬间,我真想把电脑砸了。
后来我盯着报错信息,脑子里突然蹦出一句话:你以为显存是房子,实际上它就是个存钱罐。 存完模型参数,存完优化器状态,存完梯度……就满了。前向传播算到一半,还没反向呢,它先炸了。
这就是分布式训练真正的起点:不是因为我们想炫技,是因为我们真的被逼到墙角了。
你永远猜不到,一个100亿参数的模型到底有多能吃显存
来,我跟你算一笔账。
100亿参数,如果全部用FP16(2字节),光模型参数就得20G。梯度同样20G。
最狠的是优化器状态——Adam要存两个东西:动量和方差,这两个玩意儿必须用FP32来存,不然精度崩了,训出来的模型根本没法用。
每个参数要存俩FP32,等于8个字节,加起来80G。
这么一算:20(参数)+ 20(梯度)+ 80(优化器) = 120G。
你一张80G的A100,连裸模型都塞不进去,更别说跑了。前向传播时每一层输出的激活值?想都别想。
所以事实很残酷:一张卡别说训,连站的地方都不够。
说到这儿,你可能会问:那这么多卡,到底怎么分的活儿?好,我给你拆明白了。
拆活儿的艺术:一篇文章看完四大并行技术
网上关于并行技术的文章,十个有九个在装模作样。什么数据并行、张量并行、流水线并行……看了半天,每个名词都认识,但就是不知道它们到底在拆什么。
我换个方式讲,你当菜谱记就行。
第一道菜:数据并行(DP/DDP/FSDP)——“人海战术”
每个GPU上都放一份完整的模型。每张卡只处理自己分到的数据。大家算完梯度,然后通过AllReduce通信把梯度合并,再各自更新参数。
优点是啥?PyTorch里一行代码就搞定了!
缺点呢?你想想,每张卡都得存一份完整的模型状态。 当模型大到单卡都放不下的时候,数据并行就跟没有一样。因为卡连模型都塞不进去,你还怎么并行?
那怎么办?微软搞出了一个好东西:ZeRO。
ZeRO的核心思想特别简单,但当时我看论文的时候是真震惊了— 别让每张卡都存整份,大家一起分摊,谁用谁现取。
优化器状态、梯度、参数,全都切碎,分到不同的GPU上存着。计算的时候用AllGather把需要的部分取回来,用完就扔。
这个思想后来变成了FSDP(Fully Sharded Data Parallel)。
你敢信?就是靠这么一个简单的“分着存,谁用谁拿”的思路,以前训不动的模型,现在跑起来了。
第二道菜:张量并行(TP)——“切蛋糕”
上一道菜是每张卡存完整的模型,这一道菜不一样:层内部的切分。
比如Transformer里的MLP有两个线性层。你想想,原来一个层要算一个巨大的矩阵乘法,现在把权重矩阵按列切成两块,分别放到两个GPU上。
每个GPU只算一半,算完再拼。
Attention的QKV也可以这么切。
这听起来很完美,对吧?
但这里有个坑,我踩过一次,差点把项目搞黄了。
TP最大的问题是什么?通信是致命的。 每次前向或反向都要来回做AllReduce。如果你跨机做TP(比如两台服务器之间走以太网或IB),延迟直接爆炸。
后来我老老实实看了Megatron-LM的推荐:TP只能在单节点内做,跨节点千万别碰。
为啥?因为NVLink带宽是跨节点的10倍以上!你跨节点硬做TP,性能还不如不拆。
第三道菜:流水线并行(PP)——“玩接力”
这个很好理解:按层切分。
假设你有一个48层的Transformer。切成4段,每段12层,放到不同的GPU上。输入先经过GPU0,再传给GPU1,依次类推。
但是,这个做法有个致命的毛病:同一时刻只有一块GPU在干活,其他都在等着。
你想想,如果只有一块卡在工作,其他三块闲着,那你用了4张卡,跟用1张卡有什么区别?
后来Gpipe给了个解决方案:把一个大batch再切成micro-batch,像流水线一样送进去。
每个micro-batch在GPU0算完后立刻传给GPU1,让GPU0腾出来接下一个。这样所有GPU都能忙起来。
但又有新问题:micro-batch数量太少,设备空闲的比例(Bubble)会非常高。
我测试过这个比例,你知道多少吗?如果总batch才切4份,GPU利用率可能不到40%。后来我提到16以上,利用率才勉强到70%。
说到这儿,你发现没有?这事就像工厂流水线:每个工位干活的速度要差不多,而且工件要足够多,才能让整条线别停。
第四道菜:序列并行(SP/CP)——“对付长文本的绝招”
这个我还没在生产环境大规模用过,但读Megatron-LM源码时发现他们已经在用了。
问题出在哪?Transformer的Attention计算复杂度是O(seqlen²)。序列长了,显存爆炸。
怎么解决?把序列切分成段,每个GPU只处理一段。
关键技术叫Ring-Attention,让GPU们像接龙一样,每轮只算自己手里的这一段和收到的KV块,然后轮转。
简单说就是:每个GPU不用记住整本书,只记住自己看过的那几页。想看其他页的时候,从别的GPU那里拿过来。
为什么没有“一套通吃”的方案?
你可能会问:这些技术能不能放到一起用?有没有一个万能配方?
答案特别直接:没有。
现实里大家都是混合着用的。
我给你看个真实的案例:
Meta训练Llama 3时,用上了数据并行+张量并行+流水线并行,在16K GPU上达到每卡400 TFLOPS以上的算力利用率。
400 TFLOPS!这什么概念?就是每张卡的计算效率几乎顶满了。
但你想想,这背后有多少通信调度和资源分配的问题?
一般搭配思路是这样的:
- **单节点内(8卡):** 用张量并行。因为节点内通信带宽高,NVLink一般600GB/s往上。
- **节点之间:** 用流水线并行。因为节点间通信带宽相对低,但PP的通信量小(只传激活值,不传梯度)。
- **更大规模:** 再用数据并行/FSDP。因为DP的通信可以用AllReduce在非常多GPU上做,而且ZeRO还能省显存。
最近有个朋友问我:“我们团队有32张H800,想训70B的模型,怎么搭?”
我仔细想了想:70B模型每层参数量大,如果TP设置成8,每卡负责的部分会变得很小,反而浪费了通信带宽。
所以我建议:TP设置为4,每个节点内再分2个流水线stage。整个集群4个节点共8个stage,配合数据并行度=4。
听起来很合理?
后来呢?我们折腾了好几周,才把Bubble比例降下来。
后来我得出一个结论,送给你:网络拓扑是爹。
你配得再花哨,如果跨节点通信是千兆以太网,TP一跨节点直接崩。
说白了,做分布式训练,千万别忽略网络带宽这个硬门槛。忽略它?直接把你从天堂拉回地狱。
第一次试水,该选哪个?
很多人问我:“你是大佬,你给个推荐呗?”
好,我老实说:如果是第一次尝试分布式训练,老老实实上FSDP。
为啥?因为它对代码改动极小,基本上只要包装一下模型,设个参数就行。
PyTorch 2.0之后,torch.distributed.fsdp.FullyShardedDataParallel已经非常好用了,自动做分片,你甚至不用手写任何通信原语。
但是,有几个坑一定要绕开:
第一,sharding_strategy要选对。
FULL_SHARD把参数、梯度、优化器状态全部分片,最省显存。
SHARD_GRAD_OP只分片梯度和优化器状态,参数还复制着。
如果你的模型正好卡着显存边界,前者更稳妥。
第二,CPU offload要慎用!
ZeRO-Offload看起来很美,但我试过一次,差点没把我当场送走。
一旦把优化器状态放到CPU上,每一步都要等CPU-GPU传数据。训练速度呢?直接腰斩。
适合实验室没那么多卡的情况,生产环境?绝不建议。
第三,FSDP跟模型并行混用时,层级要规划好。
FSDP默认对每个transformer layer做一次forward-allgather/backward-reduce-scatter。
但如果模型已经用了张量并行,每层的参数量被切小了,FSDP的overhead就会变大。
所以很多框架在TP内部不套FSDP,而是对TP出来的完整模型再做数据并行。
说人话就是:别什么都叠一起。
结尾:你以为的训练,其实只是开始
有人问我:“这些并行技术到底是训练专用还是推理也能用?”
推理也会用到张量并行和流水线并行来分担超大模型的显存。
比如GPT-4推理时,如果单卡放不下,就需要把模型拆到多卡上,每张卡只算一部分,然后通过通信拼结果。
不过推理没有反向传播,不需要保存梯度,显存压力小很多。但延迟和吞吐需要权衡。
还有人问:“那么多并行度,怎么决定各自多大?”
这其实是自动并行搜索的问题。现在有一些框架会自动帮你搜最优切分方案。
但据我所知,很多团队还是靠经验和手工配置。
一般来说:TP不超过节点内的卡数(比如8),PP的stage数不要超过GPU总数/TP大小太多,DP作为兜底把剩下的GPU并起来。
分布式训练并行技术是一个‘工程 > 理论’的领域。 很多最优实践都是被逼出来的。
我写这个系列的目的,就是把我踩过的坑和试过的方案摊开来,给你当个参考。
最后,还有一个我打算专门开一篇来讲的东西—— 专家并行(EP)。
它是MoE模型的专属并行方式。现在很多超大模型(DeepSeek V3、Mixtral)都用了MoE,把FFN层替换成多个专家,每个专家分配到不同GPU,通过路由把token发到对应的专家那里。
通信模式更复杂(All-to-All),但能大幅减少计算量。
说到这儿,我突然想起一句话:
分布式训练这堵墙,不是撞开它,而是绕着它走。
而你的每一次尝试,就是给自己多一把钥匙。
下一篇,我会专门讲数据并行,包括DP、DDP、FSDP以及ZeRO的细节。如果你有被显存问题困扰过,那篇应该能帮到你。
敢不敢在评论区留个“期待”?咱们下一篇见!
读者评论 2