最近在信创项目里干了一件很多人觉得麻烦、但我个人还挺感兴趣的事把一套基于 PyTorch 的大模型训练链路从 GPU 环境整体搬到华为昇腾算力上。大模型训练这四个字听起来全是算法问题真正落到昇腾上以后会发现八成时间都花在环境、算子、通信、并行策略这些工程细节上。只要有一个版本不匹配或者某段数据管线的写法不兼容训练就会以各种姿势崩给你看。这篇文章不打算讲特别玄的理论更多是把我在昇腾上跑大模型训练时走过的路、踩过的坑、验证过的配置整理出来。如果你正准备接触昇腾想把手头模型迁过去或者只是被分到了一个信创大模型训练项目读完至少能搭起一张完整的实施地图知道每一步该查什么、该防什么。1. 信创项目里的昇腾大模型训练先想清楚目标1.1 信创大模型训练和普通训练的核心差异很多人一开始把这件事想简单了模型代码是现成的数据也是现成的换一块硬件重新装个驱动不就能跑了吗真不是这样。昇腾的硬件能力和主流的加速卡在算力指标上并不差但它的软件生态、算子集合、分布式通信方式和团队原本熟悉的生态是有差异的。信创大模型训练本质上不是在“跑模型”而是在“重建一套训练工程栈”。这里有一个很容易被低估的点团队原本积累的很多经验比如怎么调梯度累积、怎么切分模型并行、怎么排查显存碎片不一定能直接搬到昇腾上。因为底层通信库变了算子行为变了甚至显存管理策略都变了。我以前习惯用某个工具直接看显存张量占用到了昇腾环境就只能换一套观测方式。所以做信创昇腾训练第一步不是写代码而是把技术栈边界画清楚知道哪些环节可以复用老经验哪些环节必须要重新学。1.2 昇腾训练技术栈的完整全景昇腾不是一块单纯的加速卡它是一整套从驱动到上层框架的家庭套装。为了方便理解可以把它类比成大家更熟悉的 GPU 生态昇腾 NPU 类似加速卡CANN 类似 CUDA 生态HCCL 类似 NCCLMindSpore 类似 PyTorch。这样一对应很多陌生名词就好懂了。技术层级常见组件对应说明硬件层昇腾 910 系列训练卡、Atlas 训练服务器提供算力、显存和机内高速互联底层驱动Driver、Firmware完成 NPU 设备的识别和管理异构计算框架CANN Toolkit类似 CUDA负责算子库、图编译、任务调度集合通信HCCL类似 NCCL负责多卡多机的梯度同步和通信训练框架MindSpore、torch_npu负责模型开发、反向传播和自动微分大模型套件MindFormers、AscendSpeed提供大模型预训练、微调、推理的工程化脚本运维观测npu-smi、msprof查看设备状态、分析训练性能这套技术栈每层都有自己的版本要求排列组合起来之后就是一个“版本矩阵”。很多训练事故追根溯源都是顶层框架和底层 CANN 版本不匹配而不是模型代码本身有问题。1.3 迁移的影响范围不只是改脚本信创大模型训练的影响范围往往比技术负责人最初预想的大得多。算法工程师要重新处理算子和模型结构基础设施工程师要重新规划环境部署、资源监控和故障恢复数据团队可能要调整数据处理方式。原来一套跑得很顺的流程换到昇腾后每个环节都要再过一遍。还有一个经常被忽略的是团队技能树。以前遇到性能问题大家第一反应是查 CUDA 相关的信息换到昇腾后排查 way 完全不同至少要熟悉 CANN 的日志体系、HCCL 的报错特征、MindSpore 的并行配置。这个学习成本不能算小但也不是没法克服。我的经验是先让团队里一个人把环境版本矩阵跑通其他人再跟进比全网铺开效率高得多。2. 环境准备先把硬件和软件版本矩阵定死2.1 硬件层面的选型与互联规划昇腾大模型训练硬件选型不能只看单卡算力还要看显存容量和卡间互联方式。训练大模型显存是真正的稀缺资源尤其是在做张量并行时模型切分后权重要频繁地在卡间通信如果互联带宽不够算力再高也白搭。我在实际项目里比较关注三件事单卡显存。昇腾训练卡常见显存从几十 GB 到更大容量不等如果预算允许尽量选大显存版本能少切几层模型并行通信开销更小。机内互联。昇腾服务器内部有专门的高速互联通道同一台机器内多卡通信比跨机器快很多。所以在规划资源时尽量用 8 卡整机作为最小分配单元减少跨机流量。跨机组网。大规模训练一定会用到多台机器机器之间一般走 RoCE 网络。这个网络的连通性和带宽同样重要跑分布式训练之前必须先测一下多机通信不能等到训练启动后才发现节点挂了。2.2 软件版本对齐清单昇腾软件栈的版本匹配是整件事里最容易踩坑、也最需要提前定死的环节。我见过太多人装完环境第一步import torch就报错最后发现是 CANN 的版本太老或者 torch_npu 和 PyTorch 版本对不上。所以动手部署前先列一份版本对齐清单宁可花半天时间确认版本也不要花两天时间排查兼容性问题。软件项建议操作操作系统优先使用昇腾官方验证过的 Linux 发行版和内核版本Driver / Firmware按照设备型号安装配套版本安装完用 npu-smi 确认CANN Toolkit安装和驱动配套的 CANN 版本并配置环境变量Python建议使用 3.8 或 3.9安装前确认框架支持范围PyTorch根据 torch_npu 的兼容列表选择具体版本torch_npu必须和 PyTorch 版本严格对应不能只看大版本号MindSpore根据模型套件文档选择稳定版本这里我不建议“所有组件都装最新版”。昇腾生态迭代很快但最新版不一定互相兼容。更靠谱的做法是照着官方发布的一个成熟组合整组安装不要去混搭多个不同时期的版本。2.3 环境验证的 3 条命令环境装完之后别急着跑大模型先用三条基本命令验证一下基础能力# 查看 NPU 设备信息 npu-smi info # 加载 CANN 环境 source /usr/local/Ascend/ascend-toolkit/set_env.sh # 验证 PyTorch torch_npu 基本可用 python -c import torch, torch_npu; print(torch.npu.is_available())如果第一条命令看不到设备问题大概率在驱动或固件层第二条命令如果提示路径不存在说明 CANN 没有装到默认位置或环境变量没配对第三条命令如果返回False可能是 torch_npu 没装好或者 PyTorch 版本和 torch_npu 对不上。这三条命令只是入门。真正跑大模型之前我还会再跑一个很小的随机模型训练比如一个只有几层的小网络用 NPU 跑几十个 step确认前向、反向、优化器更新都能正常执行。这一步能筛掉大量环境问题后面进大模型时会省很多事。3. 模型迁移从 PyTorch 一路搬到 MindFormers3.1 路线一PyTorch 脚本直接接 torch_npu如果团队现有的模型是用 PyTorch 写的同时又不想马上重构成 MindSpore最快速的路线是引入 torch_npu。torch_npu 是昇腾的 PyTorch 适配层装上以后代码改动量可以压得非常小。基础迁移路径是这样的import torch import torch_npu # 指定使用 NPU 设备 torch.npu.set_device(0) device torch.device(npu:0) # 模型参数搬到 NPU model model.to(device) # 输入数据搬到 NPU input_ids input_ids.to(device) # 前向计算、loss、反向传播与原来的写法保持一致 output model(input_ids) loss loss_fn(output, labels) loss.backward() optimizer.step()这套改法的核心思路是“设备替换”。原来写cuda的地方改成npu原来用 CUDA 工具函数的地方换成 torch_npu 提供的新接口。对于已经封装得比较干净的项目半天时间就能改完。但这里要提醒一句快速迁移能跑通不代表能跑好。PyTorch 脚本里如果用了某些比较偏门的算子昇腾上不一定有对应的高性能实现可能跑得很慢甚至直接报错。所以这个路线适合中等规模模型或者作为迁移初期的验证手段。3.2 路线二MindSpore/MindFormers 重写如果你的目标是把大模型在昇腾上长期稳定地训练我更推荐走 MindSpore 路线尤其是配合 MindFormers 这类大模型套件。原因不复杂昇腾的原生生态在 MindSpore 上磨合得更好分布式并行、混合精度、重计算这些大模型训练必备功能在原生套件里往往是开箱即用的。MindFormers 的使用方式也比较工程化一般流程是按需要选择一个模型配置模板比如 Llama 系列或常见开源大模型的预训练配置。准备数据集按套件要求的格式做 tokenization 和二进制转换。在 yaml 配置文件里设置模型结构、并行策略、学习率、训练步数等参数。用启动脚本拉起多卡训练。# 这是一个示意命令实际以套件文档为准 msrun --worker_num8 --local_worker_num8 \ --config configs/llama2/run_pretrain_llama2_7b.yaml这个路线的成本在前期如果模型结构不在套件支持列表里需要先做模型结构适配。但一旦跑通你会发现很多工程问题已经被框架提前解决了尤其是多卡通信和并行切分比自己手写 PyTorch 分布式稳定得多。3.3 算子不兼容改写的常见套路不管走哪条路线都会遇到算子不兼容的问题。昇腾的算子库已经覆盖了绝大多数大模型常用算子但总会有一些边缘算子不在支持列表里。遇到这种情况我一般按下面顺序处理先升级 CANN 版本很多时候新版本的算子库会补上缺失算子。看官方算子映射文档很多不兼容算子都能找到替代方案。如果只是某个计算逻辑特殊尝试用几个基础算子组合实现。实在不行再考虑自定义算子但这会让训练脚本的维护成本明显上升。有一个细节值得注意有些算子名义上兼容但在昇腾上会跑得很慢比如某些动态 shape 的切片拼接操作。我习惯在训练早期做一次算子性能统计把耗时前 20 的算子拉出来看一遍优先优化那些明显不合理的实现。3.4 权重转换和数据集对齐模型代码能跑起来以后还有一个容易被忽视的环节权重文件怎么搬。PyTorch 的state_dict和 MindSpore 的权重格式并不一样如果直接加载通常要么报错要么静默地把很多参数名匹配不上。我在迁移时习惯先做几件事先把 PyTorch 预训练权重导出成通用格式再转成 MindSpore 可加载的结构。转换后跑一次前向推理选几条固定样本比较原模型输出和新模型输出确认权重没转错。训练数据的 tokenizer 也要对齐。同一个模型在不同生态里可能用不同的分词实现数据预处理必须保持一致否则训练出来就是另一个模型。这些工作听起来繁琐但不做的话后面训练只会越来越乱。权重转换错训练再久也没有意义。4. 多卡并行训练参数、脚本和显存账4.1 选择并行策略DP/TP/PP 怎么搭配大模型训练很少只靠一张卡所以并行策略一定要提前设计。昇腾环境里常用的是数据并行、张量并行、流水并行和序列并行。我习惯用一个表格来梳理这些并行方式的核心逻辑并行方式切分对象通信频率适用场景数据并行 DP切分训练数据每卡持有一份完整模型每步全量梯度同步模型单卡放得下扩大吞吐张量并行 TP切分模型内部张量每卡持有模型一部分每层前向反向都要通信模型单卡放不下机内多卡协同流水并行 PP按层切分模型每卡负责若干层每步只和上下游通信超大规模模型跨机部署序列并行 SP切分序列长度维度注意力层通信较多长序列场景这里最重要的原则是能用数据并行解决的不要轻易上张量并行。张量并行虽然能拆模型但通信开销非常大它的收益只有在单卡确实放不下模型时才明显。如果模型能塞进显存只是训练太慢优先加数据并行或加大 batch 更划算。昇腾的多卡训练通常通过 HCCL 完成集合通信。在 PyTorch 迁移方案里分布式初始化一般可以这样写import torch import torch.distributed as dist import torch_npu dist.init_process_group(backendhccl) torch.npu.set_device(local_rank)初始化成功之后梯度同步、数据分发这些逻辑就和常规分布式训练基本一致了。但要注意HCCL 的初始化对组网环境更敏感节点之间的网络不能有防火墙拦截否则很容易初始化超时。4.2 显存估算清单和优化开关大模型训练最怕的报错就是显存溢出。显存的占用来源其实很固定大致包括总显存占用 ≈ 模型权重 梯度 优化器状态 激活值 通信缓冲区以 7B 规模模型为例如果使用 BF16 训练模型权重大约是 14GB梯度也差不多 14GB。优化器状态如果使用完整的 Adam包括 FP32 主权重和两个动量累计起来会大得夸张所以必须做优化器状态分片让多卡共同分担这部分显存。激活值则和序列长度、batch size、模型层数强相关最有效的控制手段是开启重计算牺牲一部分计算换显存。我通常在配置里关注下面几个开关recompute或 checkpoint activation开启后激活值大幅下降训练速度会损失一些但稳定性显著提升。优化器状态分片让多卡分担优化器显存几乎是训练大模型的必选项。梯度累积小显存环境下通过累积梯度模拟更大的 batch而不是硬撑大 batch。混合精度昇腾训练大模型一般用 BF16比 FP16 更稳定不容易出现 loss 突然变成 NaN。4.3 一条可参考的训练启动流程一个完整的昇腾多卡训练任务我一般会分成三个阶段冒烟测试、小规模验证、正式训练。冒烟测试只跑几百个 step数据量很小主要目的是验证通信、算子和显存占用。小规模验证会跑几千个 step观察 loss 曲线是否有下降趋势同时监控算力利用率。确认一切正常再开正式训练否则直接全量启动出问题时排障成本会很高。一个典型的 MindFormers 配置文件大概长这样具体字段以实际套件为准model: type: llama dtype: bf16 seq_len: 4096 parallel: data_parallel: 8 tensor_parallel: 1 pipeline_parallel: 1 optimizer_sharding: true train: recompute: true global_batch_size: 64 gradient_accumulation_steps: 4 learning_rate: 1e-4配置好之后先用一条简单命令确认所有设备都能被识别再启动训练。训练起来后第一件事不是看 loss而是开一个终端持续观察npu-smi info确认八张卡的算力都拉起来了没有某张卡在空转。5. 训练跑起来以后性能调优和长稳运行5.1 从 npu-smi 到 msprof 的观测套路训练启动后最常用的命令就是npu-smi info它有点像大家在集群上查看 GPU 状态的习惯能显示每张卡的算力利用率、显存占用、温度等信息。通过它快速判断训练是卡在计算还是卡在数据。如果想做更细致的性能分析可以用 msprof 这类工具采集训练任务里的算子耗时、通信耗时和线程耗时。我用它的时候一般分两步先整体看一遍有没有明显的性能不均衡比如某个算子耗时特别突出。再针对热点算子看它是在 NPU 上执行还是被推到了 CPU 上执行。如果发现热点算子落到 CPU 上训练速度会肉眼可见地慢下来这种情况需要改写实现或者更换算子。采集性能数据确实会增加训练开销不建议一直开着可以在正式训练之前跑一个短任务做性能摸底。5.2 通信瓶颈怎么判断和优化多卡训练里通信可能是比计算更难解决的问题。现象是每张卡的算力利用率不高但训练还是没有跑快。这时候大概率不是计算瓶颈而是梯度同步的通信开销太大。优化通信我常用的手段有三个加大梯度累积步数减少梯度同步的次数把通信频率降下来。用同机内的高速互联通信尽量避免跨机通信进入热路径。检查通信超时配置比如 HCCL 相关的时间参数避免网络抖动导致任务中断。有一类比较隐蔽的问题和混合精度有关如果梯度在通信前没有统一为低精度通信的数据量会明显增加。昇腾环境里对梯度通信的优化通常有专门配置建议训练前仔细看一遍官方文档别用默认值硬扛。5.3 长稳训练最怕的几类事故大模型训练经常一跑就是好多天中途出任何一次事故都可能让前面的算力白费。我在长稳训练时最警惕的是下面几件事loss 突然变 NaN。大部分情况下和混合精度、学习率、数据异常有关遇到先停一下检查最近几个 step 的输入数据。训练中途进程挂掉。如果正好做跨机训练先排查节点之间的网络稳定性再看 HCCL 通信日志。显存慢慢涨最后溢出。这种情况往往是某个环节的显存没有释放或者缓存越积越多需要通过监控曲线观察显存变化趋势。checkpoint 保存时间过长。模型大了以后保存一次权重可能要好几分钟这段时间其他卡都在等待既浪费算力也可能引发通信超时。应对这些事故最好的办法不是事后处理而是预先加好保护。训练脚本里要有自动重启机制、踩点保存机制、关键指标告警机制。哪怕只是把保存频率降低一点也能让训练稳定性大幅提升。6. 问题排查实录与速查表6.1 高频故障排查表下面是我在昇腾大模型训练过程中遇到的典型问题整理成一张速查表方便后续遇到类似情况直接对照。问题现象可能原因处理思路import torch_npu报错torch_npu 与 PyTorch 版本不匹配卸载后按官方兼容组合重装npu-smi info看不到设备驱动或固件加载失败检查驱动日志重新安装驱动并重启HCCL 初始化超时跨机网络不通、端口被防火墙限制检查网络连通性调大通信超时参数训练显存溢出batch 太大、未开重计算、优化器未分片开启 recompute 和优化器分片降低 batch 或 seq_len算子运行报错CANN 版本太老或算子不被支持升级 CANN或将算子改写为等价实现loss 震荡或 NaN学习率偏大、混合精度策略不当降低学习率检查 loss scaling 配置算力利用率低数据加载成为瓶颈检查 CPU 负载、DataLoader 并行度、数据预处理耗时保存 checkpoint 卡死I/O 等待严重或无盘带宽不够减少保存频率使用高速存储或异步保存这张表只是高频问题实际场景里还会有更奇怪的现象。遇到没见过的报错先看日志别盲目改代码。昇腾生态的日志信息虽然不如某些成熟生态那样简洁但绝大多数问题都能在日志里找到线索。6.2 再补一条避开“人肉排障”的操作习惯有件事我想多提一句项目里人多手杂环境版本混乱往往是最大的隐患。同一个集群上不同节点可能装着不同版本的 CANN或者不同机器上的 Python 环境不完全一致。这种不一致在日常训练中可能不会马上暴露但一旦节点扩容或故障切换训练就会莫名其妙出问题。我后来习惯在做昇腾训练项目时把环境版本矩阵写进一个部署文档并且用脚本一键检查所有节点。每次训练启动前先跑一遍环境检查发现问题先解决再继续训练。这个习惯帮我省掉过很多次半夜爬起来重启任务的尴尬。另外一个经验是尽量保持训练脚本和模型结构简单。能把模型切成几个标准模块就别写太多动态分支。昇腾对静态计算图和固定 shape 更友好代码结构越是规整编译优化和并行切分的效果越好。很多训练加速并不是靠玄学调参而是靠把代码整理得更容易被底层优化器识别。最后再分享一个小技巧做任何昇腾大模型训练任务之前先在单机单卡上跑一个最小可用案例确认数据、模型、优化器、权重加载全部正常再加分布式、再加大数据集。这个顺序看着保守但确实能让整个迁移过程少走很多弯路。我在多个项目里用过这套流程基本上都能把“传说的坑”控制在可控范围内。