昇腾910B大模型训练实战:从环境搭建到性能调优全流程

发布时间:2026/9/8 8:18:52
昇腾910B大模型训练实战:从环境搭建到性能调优全流程 去年团队进了一台昇腾910B服务器任务是把一个几十B参数规模的大模型训练流程完整搬到昇腾上跑通并且把吞吐量尽量往上提。那段时间基本上把昇腾从驱动、CANN到训练框架一整条链路的坑都踩了一遍从环境装不起来、模型迁移后loss不降、训练中途卡死到最后的性能调优每个环节都有印象深刻的教训。所以这篇东西想写成一份尽量完整的实战向总结覆盖昇腾大模型训练从环境搭建、模型迁移、并行配置到调试和性能调优的全流程给刚接触昇腾训练栈的朋友一条可以直接照着走的路线。先说清楚这篇内容适合谁团队已经拿到昇腾服务器、准备把现有PyTorch或Megatron训练脚本迁过来的工程师已经在昇腾上跑通小模型、想训练更大规模参数但不知道怎么下手的人以及被训练过程中的卡死、精度异常、性能低下折磨、想找系统排查思路的人。太底层的硬件原理我不会展开太多重点讲你怎么把活干完、干好。1. 昇腾训练环境搭建版本选型与容器化部署的实战记录1.1 昇腾不是GPU先搞清楚硬件形态和软件栈全貌昇腾910系列是AI处理器NPU不是GPU。这个认知非常重要因为很多人拿到机器后会习惯性地到处找nvidia-smi结果发现找不到。昇腾对应的命令是smi实际是npu-smi info设备编号也不是cuda:0而是npu:0。类似地昇腾的集合通信库叫HCCL对应NCCL异构计算架构叫CANN对应CUDA虽然概念上一一对应但细节差异很多后面会逐一讲到。昇腾训练的软件栈从下往上依次是固件和驱动 - CANN - 训练框架PyTorch torch_npu或MindSpore - 大模型训练套件ModelLink、AscendSpeed、MindFormers。这个分层结构决定了排错的基本思路出现问题时先确认是哪一层出的问题再逐层排查而不是一上来就怀疑模型代码。实际开发中我们接触最多的是最上面两层但下面两层的版本匹配问题往往是最先遇到的拦路虎。从应用场景看昇腾可不只做大模型训练。现在3DGS三维重建这类图形学与AI交叉的方向也有跑在昇腾上的案例生态覆盖面已经远超早期只支持少量CV模型的阶段。不过目前训练侧最主流的诉求仍然是LLM这也是这篇文章聚焦的方向。1.2 版本对应关系版本不匹配是第一个大坑昇腾的驱动、固件、CANN、框架适配层之间是强绑定关系不能随便乱配。我见过太多人第一步就栽在这CANN装好了PyTorch也装好了import torch_npu 直接报错日志显示版本不匹配。以昇腾910B为例一套实测可用的组合大致如下组件版本建议说明固件与驱动23.0.3系列或更新需与910B匹配建议用配套的商用版CANN8.0.RC1或更新对应Toolkit开发套件包PyTorch2.1.0官方PyTorch版本torch_npu2.1.0.post5或更新与CANN版本配套的适配插件但我必须强调一点昇腾的版本迭代很快上面的组合只是某一个时间点实测可用的快照具体部署前务必去官方Release Notes查准对应关系。这些版本信息通常在CANN安装包页面和torch_npu的发布说明里都有明确标注照着表选就不会差太远。另一个经验是升级驱动之后CANN和torch_npu大概率也要同步升级否则你会在一个奇怪的报错上浪费半天时间。1.3 容器化部署的具体操作昇腾官方提供了Ascend Docker Runtime支持通过环境变量ASCEND_VISIBLE_DEVICES来控制容器内可见的NPU设备用法跟GPU的CUDA_VISIBLE_DEVICES类似。相比手动挂载一堆/dev/davinci*设备用Runtime要省心得多。我常用的启动方式大致是这样# 构建镜像时安装好CANN、torch、torch_npu docker run -it --name ascend-train \ --ipchost \ --shm-size32g \ -v /data:/data \ -e ASCEND_VISIBLE_DEVICES0,1,2,3,4,5,6,7 \ ascend-train-env:latest \ bash两个容易被忽视的参数--shm-size一定要给大大模型训练数据预加载和分布式通信需要大量共享内存默认64MB绝对不够--ipchost是为了避免多进程DataLoader在共享内存通信时出问题。不加这两个参数轻则训练卡顿重则直接报共享内存不足崩溃。容器启动后先做一次快速自检npu-smi info正常能看到8张卡的型号、内存、温度和使用率说明驱动层没问题。然后进入Python验证框架层import torch import torch_npu print(torch.npu.device_count()) print(torch.npu.get_device_name(0))能正确输出卡数量和型号说明CANN、torch、torch_npu这条链路已经通了。1.4 多卡通信相关的初始检查大模型训练基本告别单卡所以HCCL通信链路的初始检查必须做。HCCL是昇腾的集合通信库和NCCL定位一样但底层实现是私有的。多卡训练前建议先跑一个简单的通信测试确认卡与卡之间通信正常避免在正式训练时才发现通信问题。常见的物理拓扑有两种HCCS高速互联和PCIe互联。HCCS是昇腾芯片之间的高速直连总线带宽远高于PCIeTensor并行这类高频通信操作应该优先走HCCS链路。服务器内部一般默认就是HCCS互联跨机的通信才走RoCE或IB网络。通过npu-smi info可以看到卡间的拓扑结构也可以用华为提供的hccn_tool工具查看详细的端口和链路信息。这些检查做完环境基本就绪了接下来才是真正的重头戏把模型训练脚本迁上来。2. 模型迁移适配从训练脚本到跑通的五个关键点2.1 先想清楚走哪条框架路线昇腾上的大模型训练框架路线现在主要有三条PyTorch torch_npu迁移成本最低适合已经有一份完整PyTorch训练脚本的团队。MindSpore华为原生框架和昇腾的融合最深但如果你没有MindSpore经验学习成本不低。大模型套件ModelLink / AscendSpeed / MindFormers适合不想自己造轮子的人直接改配置和少量脚本就能跑常见大模型。我的建议是如果团队已经有了成熟的PyTorch训练脚本那就走torch_npu路线改动量最小如果是从零开始训练常见结构的模型可以直接用AscendSpeed这类套件它内部做了很多昇腾相关的优化比自己从PyTorch层开始慢慢调要快得多。2.2 代码迁移的最小改动集如果你只是想把已有的PyTorch训练脚本跑在昇腾上最小改动其实只有三处import torch_npu设备指定改成npu模型和输入tensor迁移到npu一个最简示例import torch import torch_npu device torch.device(npu:0) torch.npu.set_device(device) model create_model().to(device) optimizer torch.optim.AdamW(model.parameters(), lr1e-4) for batch in dataloader: input_ids batch[input_ids].to(device) labels batch[labels].to(device) outputs model(input_idsinput_ids, labelslabels) loss outputs.loss loss.backward() optimizer.step() optimizer.zero_grad()分布式训练场景稍微多一步初始化通信后端时要把nccl改成hcclimport torch.distributed as dist import torch_npu dist.init_process_group(backendhccl, init_methodenv://)注意hccl这个后端是在import torch_npu之后才会注册的顺序不能反。很多人把import torch_npu放在dist.init_process_group之后结果报backend hccl not found就是这个原因。迁移过程中最麻烦的不是这些标准操作而是你自定义的那些算子或特殊操作比如自己写的一些CUDA扩展昇腾上跑不了得用torch_npu提供的算子替代或者改写为组合算子。遇到这种情况建议先查一下昇腾算子清单绝大多数常用算子都已经支持了真正需要手写的场景不多。2.3 混合精度与Loss Scaling大模型训练几乎必开混合精度。昇腾对FP16和BF16都支持常用的写法跟CUDA AMP很像from torch.npu.amp import autocast, GradScaler scaler GradScaler() for batch in dataloader: input_ids batch[input_ids].to(device) labels batch[labels].to(device) optimizer.zero_grad() with autocast(): outputs model(input_idsinput_ids, labelslabels) loss outputs.loss scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()有个实用经验训练初期如果loss异常先看一眼GradScaler的scale值是不是在持续下降。scale一直掉通常意味着梯度溢出这是混合精度的典型问题——要么学习率太大要么某些层在FP16下精度不够。此时可以尝试BF16或者给特定层单独做FP32。2.4 数据加载与device设置DataLoader迁移同样容易被忽略。PyTorch的DataLoader返回的tensor默认在CPU上你需要显式搬到npu。整个数据管线的搬运开销如果太大会直接拖慢训练尤其在大规模数据集下。几个实测有效的优化点num_workers设置为4到8是比较稳妥的范围太高会引发系统内存压力和SharedMemory不足。打开persistent_workersTrue避免每个epoch重复创建worker进程。如果数据预处理包含大量CPU操作可以考虑把图像或文本的tokenize环节在离线阶段做好训练时只做加载和搬移。另外昇腾数据加载支持CANN侧的加速组件比如torch_npu下也有一些数据增强相关的适配不过一般场景下标准DataLoader就够用了。3. 大模型并行训练落地DP、TP、PP配置的完整思路3.1 三种并行方式分别解决什么问题模型规模大到单卡装不下的时候就要走并行。昇腾大模型训练常见的并行维度有三个并行方式切分的对象通信频率适合场景数据并行DP训练数据分多份每卡一份完整模型副本每步同步梯度通信量取决于模型大小模型单卡放得下想用多卡扩大batch规模张量并行TP单个算子的矩阵按行或按列切到多卡每次前反向都有通信频率极高单卡放不下的超大模型层流水线并行PP按层切分每个stage跑一部分层只在stage边界传输激活和梯度减少单卡显存压力但会引入流水线气泡简单类比数据并行是多个厨师各自炒同一道菜最后对一下口味张量并行是一个厨师负责切菜、另一个负责颠勺每秒钟都要配合流水线并行是一条流水线上每人负责一道工序前面做完传给后面。三者不是互斥的实际训练中往往组合使用。3.2 显存估算与并行度选择的计算逻辑选择并行策略前要先做显存估算。我们以13B参数模型为例看看为什么必须并行模型参数13B * 2字节FP16 26GB梯度13B * 2字节FP16 26GBAdam优化器状态参数副本FP3252GB 一阶动量FP3252GB 二阶动量FP3252GB 156GB单看优化器状态13B模型就要156GB昇腾910B单卡64GB根本放不下。所以必须靠TP、PP或者类似ZeRO的优化器状态分片把这些状态分散到多卡上。假设用TP4、PP2的组合相当于先通过TP把每层参数切4份再通过PP把层切成2段显存占用能降低到单卡能承受的范围。工程上的经验法则是先满足显存约束再考虑通信开销和效率。一般来说TP优先选择2或4的幂次PP选择2或4DP填满剩余的卡数。3.3 通信开销与拓扑考量并行不是免费午餐通信开销往往决定最终性能。TP的通信发生在前向反向的每个算子内部量非常大所以TP的rank最好在同一节点内走HCCS高速互联跨机的TP基本是性能灾难。PP的通信只在流水线边界发生量相对小可以接受跨节点。DP的梯度同步通信量取决于模型大小跨节点时对网络带宽也有要求。所以实际集群配置时通常的排布是单机8卡先满足TP8或TP4PP按需跨机DP在更上层扩展。这种排布背后是物理拓扑决定的不是拍脑袋定的。3.4 实际训练启动配置示例以AscendSpeed跑7B规模模型为例关键启动参数大致长这样python -m torch.distributed.launch \ --nproc_per_node8 \ --master_port29500 \ pretrain_gpt.py \ --tensor-model-parallel-size 4 \ --pipeline-model-parallel-size 2 \ --micro-batch-size 1 \ --global-batch-size 32 \ --num-layers 32 \ --hidden-size 4096 \ --num-attention-heads 32 \ --seq-length 4096 \ --max-position-embeddings 4096 \ --train-iters 100000 \ --save-interval 1000 \ --log-interval 10--tensor-model-parallel-size 4 --pipeline-model-parallel-size 2意味着8卡上切分方式为TP4、PP2。--micro-batch-size 1表示每张卡每次前向的微批次大小是1--global-batch-size 32表示每次参数更新用32条样本中间靠梯度累积补足。梯度累积本质上是用时间换显存显存不够就调小micro batch size但训练速度会下降。关于ZeRO类策略昇腾侧也有对应的内存优化能力原理和DeepSpeed ZeRO一致把优化器状态、梯度、参数分片到多卡。和TP、PP同时使用时要格外注意显存和通信的平衡ZeRO本身会引入额外的通信一般不需要数据并行维度再叠加太多优化。4. 训练调试实录卡死、loss异常、显存爆掉三类问题的排查链路4.1 训练卡死怎么排查大模型训练中训练卡死是最高频的事故。我先给一个完整的排查链路这是从那几次深夜加班中总结出来的第一步确认是卡死还是慢。用npu-smi info看NPU利用率如果利用率波动正常只是日志输出慢那大概率是数据加载或日志打印拖累如果利用率持续为0而训练进程还在那才是真卡死。第二步看日志尾部。如果是分布式训练日志里搜索关键词timeout、wait、hang。最常见的是HCCL通信等待超时报错特征是一张卡等待其他卡的数据等到超时。这时检查各卡状态、以及是否有个别卡提前崩溃退出。第三步排查数据加载阻塞。DataLoader的worker进程数量过多可能导致系统内存耗尽某些进程被操作系统杀掉而主进程还在等待这批数据表现为训练进程还活着但就是不往下走。字符出问题的最典型特征是训练刚开始不久就卡住而且top能看到一堆python进程但CPU占用极低。如果是HCCL超时一个实用的预防手段是调大HCCL_CONNECT_TIMEOUT环境变量默认值相对保守大规模集群下建链时间可能超过默认超时值导致误报。4.2 loss异常和精度偏差的排查思路loss不降、loss突然变成NAN、同样的超参数在昇腾和原始GPU环境上结果对不上这三类精度问题在迁移初期极其常见。我的排查链路是这样的先固定随机种子包括PyTorch、Python、numpy的seed确认初始化一致。小规模、单卡、FP32训练跑几步排除并行和混合精度引入的干扰。如果FP32小规模下loss正常问题大概率出在混合精度或并行通信上如果loss仍然不正常就是模型实现或数据管线的问题。检查Loss Scaling是否在持续下降如果是换成BF16或给关键层单独开FP32。还有一个容易被忽略的点算子实现差异。同一个算子在不同硬件上的实现细节可能有微小精度差异累积下来就会让结果漂移。遇到完全对不上的情况不要死磕“谁更准”而是确认loss收敛趋势是否一致趋势对了就算正常。4.3 NPU显存不足的应对方式大模型训练最常见的显存错误就是类似Out Of Memory的报错。经验是不要只看报错的第一行要用npu-smi info确认是单卡溢出还是整体耗尽再看日志尾部的栈信息定位是哪个tensor在哪一步分配失败。应对手段从优到劣排列减小micro batch size保持global batch size不变让梯度累积接管剩余部分。开启激活重计算activation checkpointing用计算换显存前向时丢掉中间激活值反向时重算一遍。开启CANN侧的显存复用和swap能力把暂时不用的数据换出到CPU内存或磁盘。实在不行增加并行度把TP、PP参数调大。一个实用技巧如果你发现显存占用率一直在临界点波动先看是不是碎片化问题。大模型训练中不同大小的tensor反复分配释放会导致显存碎片化大部分显存可能显示未使用但无法分配大块内存。重启训练进程往往能解决这种问题。5. 性能调优把训练吞吐从“能跑”推到“跑得快”5.1 用msprof找到真正的性能瓶颈在昇腾上做性能调优不能靠感觉得靠数据。msprof是CANN自带的性能分析工具可以采集算子耗时、通信耗时、NPU利用率等信息。基本用法msprof --applicationpython train.py --param1value1 --output/home/user/profiling_result跑完一轮后在输出目录里会得到详细的profiling数据。我关心的核心指标有四个指标含义健康范围参考Step Time每一步训练总耗时随模型规模变化NPU利用率NPU计算资源忙闲比应高于80%通信耗时占比通信在step time中的占比应低于20%空闲等待占比卡在等待数据或同步的时间越低越好我遇到的一个典型案例7B模型初始step time是3.5秒msprof显示通信耗时占比高达45%计算占比52%数据加载3%。从数据看非常明确瓶颈在通信不是计算也不是数据。5.2 通信优化的几个实操手段针对通信占比过高的问题按优先级做了几件事第一调整梯度桶大小。PyTorch的反向传播会把梯度放入桶中统一通信桶太大通信次数少但单次数据量大桶太小通信次数多吞吐下降。实测把梯度桶调整到适合当前模型规模后通信占比有一定改善。第二开启通信与计算重叠。现代训练框架支持在反向传播的同时进行梯度通信让计算单元和通信单元并行工作。在torch_npu侧检查一下对应的分布式训练配置项是否默认开启如果没开打开后收益明显。第三调整梯度累积步数避免每步都触发梯度通信。在梯度累积模式下多个微步累积完梯度后再统一通信通信频率降低通信占比自然下降。经过上述调整那个案例的通信占比从45%降到了25%左右step time从3.5秒降到了2.4秒。5.3 算子级优化从融合到图模式通信优化做完后如果瓶颈到了计算侧就该看算子层面了。msprof的结果会列出各类算子的耗时排名。调优的核心思路是小算子合并成大算子减少kernel启动和显存搬运开销。典型例子LayerNorm后的激活函数可以算子融合多个相邻的elementwise操作可以合并Attention里的QKV投影可以合并成一个大矩阵乘。这些融合昇腾的图编译引擎自动能做一部分不够时就要在代码层面手动改写。另外昇腾和PyTorch配合时可以开启图模式编译把Python层动态图转换成静态图执行减少Python解释和调度开销。实测某些模型开图模式后整体训练时间能再缩短10%-15%。最后强调一条血泪经验调优一定要围绕profiling数据进行不要凭感觉猜瓶颈。很多人一上来就调学习率、调batch size最后发现瓶颈在数据加载做了半天无用功。先采集数据再动手这是性能调优的基本原则。整个昇腾训练调试调优的过程其实就是在“分层”思路下不断做排查和微调。硬件和驱动一层CANN一层框架适配一层模型代码一层每一层都有自己典型的报错模式和排查工具。你能清晰地把问题定位到某一层就成功了一大半。我个人体会最深的还不是那些具体的参数和命令而是血泪教训积攒出来的流程感环境版本先核对再跑通信测试接着小规模精度验证最后才上全量训练。这个顺序不要跳跳了就要付出成倍的返工时间。希望这篇文章能帮你把路走直一点少踩几个我已经替你踩过的坑。