这两年AI Infra圈子里,昇腾(Ascend)平台和PyTorch生态的适配话题,热度一直没降过。我自己的团队从去年开始把一批模型从CUDA迁移到昇腾910B上,环境搭建这个环节踩了不少坑,也沉淀出一套还算稳定的流程。这篇就围绕PyTorch生态在昇腾平台上的适配,把环境搭建和安装的细节完整梳理一遍,给后续接手的人少走点弯路。
先说清楚这篇文章适合谁看:你手头有昇腾设备,想跑PyTorch模型,但不知道从哪下手;或者你已经在CUDA上写好了训练脚本,想低成本迁移到昇腾上;再或者你只是想了解国产AI加速卡和PyTorch生态到底是怎么结合的。无论哪种情况,这篇都能给你一个完整可落地的参考路径。
1. 为什么要在昇腾上跑PyTorch:背景与核心价值
1.1 这个项目到底在解决什么问题
昇腾是国产AI加速芯片的代表性产品线,从早期的310推理卡到910系列训练卡,再到现在的910B、910B2,算力规格一步步上来。芯片本身的能力是一回事,但真正决定一个算力平台能不能用起来的,是它的软件生态。PyTorch作为目前学术界和工业界使用最广泛的深度学习框架,几乎成了模型侧的事实标准。所以昇腾平台面临的第一个核心问题就是:怎么让PyTorch生态里的模型能无缝跑在昇腾的NPU上。
这里说的"适配",不是简单地把CUDA代码换成别的API,它主要包括三件事:算子层支持、图编译层对接、训练/推理框架的集成。PyTorch本身有一套完整的算子库,昇腾无法一句句去改PyTorch源码,而是通过一套适配插件torch_npu,把PyTorch的算子调用翻译成昇腾NPU能执行的算子。这套翻译层的质量,直接决定了模型跑得快不快、稳不稳定。
1.2 昇腾平台和CUDA生态的关系
很多人第一次接触昇腾时,会下意识地把NPU想象成"国产GPU",然后拿它跟NVIDIA的CUDA生态去做一一对应。这种对应有一定参考价值,但会误导人。
CUDA生态是一套完整封闭的软硬一体方案:驱动、运行时库、编译工具、深度学习加速库(cuDNN、cuBLAS)、通信库(NCCL)全部由NVIDIA自己维护。而昇腾的软件栈相对更开放也更分层:底层有驱动和固件,再往上是CANN(Compute Architecture for Neural Networks)异构计算架构,它负责提供算子库、图编译引擎、运行时管理和集合通信能力。torch_npu则是CANN与PyTorch之间的"翻译官"。
理解这个关系很重要,因为你在排查问题时,问题可能出在三个不同层面:PyTorch原生层、torch_npu适配层、CANN算子层。我见过不少同事在环境搭不起来时,反反复复重装PyTorch,结果问题出在CANN版本太旧,换了个版本的CANN就好了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:硬件认知与版本选型
2.1 硬件侧需要先搞清楚的几件事
动手装环境之前,先花十分钟把硬件情况摸清楚,能省后面的很多事。昇腾的机型常见的有训练服务器(如Atlas 800系列)、推理服务器(如Atlas 300系列)、以及开发板形态(如Atlas 200 DK)。不同形态的设备,对应驱动、CANN版本、甚至PyTorch适配方式都有差异。
我这边主力设备是Atlas 800训练服务器配910B芯片,单卡64GB HBM,半精度算力在376 TFLOPS上下(不同型号略有差异)。训练服务器通常配8张卡,和NVIDIA的DGX系列形态类似。通过npu-smi命令可以查看卡的状态,类似NVIDIA的nvidia-smi。装上驱动后,如果npu-smi能正常输出,说明硬件和驱动这层是通的。
有一点要特别提醒:昇腾设备对host侧CPU架构有要求。x86和ARM(鲲鹏)两种架构的软件包不通用。大多数公有云上的是鲲鹏架构,自有物理机则多是x86,装之前一定看清楚,别下载错了包。
2.2 版本对应关系与选型思路
昇腾平台最让新手头疼的,就是版本对应关系太复杂。PyTorch有一个版本,torch_npu有一个版本,CANN又有自己的版本,三者必须匹配,否则装上就是各种算子报错。
我整理了目前在用的稳定组合,方便参考:
| PyTorch版本 | torch_npu版本 | 推荐CANN版本 | Python版本 |
|---|---|---|---|
| 2.1.0 | 2.1.0.post6 | 7.0.0 | 3.8-3.10 |
| 2.3.1 | 2.3.1.post1 | 8.0.RC1 | 3.8-3.10 |
| 2.5.1 | 2.5.1.post2 | 8.1.0 | 3.9-3.11 |
这里给一个选型建议:新项目尽量选PyTorch 2.3.1或2.5.1,因为社区活跃度高,遇到问题能搜到更多解决方案,同时和最新大模型生态的兼容性更好。如果是为了复现老项目,老老实实跟着老版本走,别随意升级。
注意:昇腾社区版的torch_npu发布节奏是"跟随PyTorch版本",但不会每个小版本都出适配包。所以规划项目时,尽量在适配包已覆盖的PyTorch版本里选,避免自己看源码适配,那个工作量不是一般的大。
3. 环境搭建实操:从干净系统到可跑通
下面进入正题。我假设你拿到了一台干净的昇腾服务器,Ubuntu 20.04或22.04系统,x86架构。全程按顺序操作,中间不要跳过。
3.1 驱动与固件安装
驱动和固件是整套软件栈的地基。不同型号的昇腾芯片,驱动固件包不同,千万别混用。以910B为例,需要从昇腾社区下载对应固件和驱动包,格式通常是.run文件。
安装步骤大致如下:
bash复制# 以root用户执行,先安装固件
./Ascend-hdk-910b-firmware_xxx.run --full
# 再安装驱动
./Ascend-hdk-910b-npu-driver_xxx.run --full
# 安装完成后重启
reboot
装完后用npu-smi info验证,能正确列出卡信息就说明驱动正常。这里有个实操细节:部分新机型如果装不上驱动,先检查系统内核版本是否在兼容列表里。昇腾驱动对内核版本有一定要求,内核太新或太旧都可能编译不过。
3.2 CANN Toolkit安装与环境变量
CANN是昇腾的计算架构层,相当于CUDA Toolkit的角色。下载CANN Toolkit安装包后:
bash复制# 以root用户安装
./Ascend-cann-toolkit_8.1.0_linux-x86_64.run --install
# 安装完成后,设置环境变量
source /usr/local/Ascend/ascend-toolkit/set_env.sh
推荐把source命令写入~/.bashrc或在启动脚本里统一source,避免每次开终端都要手动执行。CANN还自带了算子开发、图编译等工具包,但这些对绝大多数使用PyTorch的用户来说用不到,不用额外安装,徒增环境复杂度。
3.3 创建Python虚拟环境并安装PyTorch与torch_npu
官方强烈建议使用Python虚拟环境,防止CANN自带的Python包和系统环境互相污染。我用的是Conda:
bash复制# 创建虚拟环境
conda create -n ascend_pytorch python=3.10
conda activate ascend_pytorch
# 安装PyTorch(CPU版即可,NPU不依赖CUDA版PyTorch)
pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cpu
# 安装torch_npu(从昇腾社区源获取)
pip install torch-npu==2.5.1.post2 --index-url https://repo.huaweicloud.com/ascend/pypi/simple
# 安装apex(用于混合精度训练,可选)
pip install apex-ascend==2.5.1 --index-url https://repo.huaweicloud.com/ascend/pypi/simple
这里展开讲一下为什么PyTorch要装CPU版。很多人第一次接触昇腾时,习惯性地去找"CUDA版PyTorch"来装,这是最大的误解。torch_npu的机制是:在CPU版PyTorch之上通过torch_npu插件注册NPU设备后端,让PyTorch能够识别和操作昇腾NPU。如果你装了CUDA版PyTorch,反而会出现设备不匹配的怪异问题。
另外,安装时建议用昇腾社区源。如果用默认的PyPI源直接安装torch_npu,大概率拿不到最新版,版本对应关系也会乱。
3.4 安装验证:跑第一个NPU版PyTorch程序
装完不要急着跑模型,先用一段极简代码验证环境是否通畅:
python复制import torch
import torch_npu
# 检查NPU是否可用
print(torch.npu.is_available())
# 在NPU上创建张量
x = torch.randn(100, 100).npu()
y = torch.randn(100, 100).npu()
z = torch.matmul(x, y)
# 确认设备信息
print(z.cpu().shape)
print(torch.npu.get_device_name(0))
如果这段代码能正常运行,说明驱动、CANN、PyTorch、torch_npu这四层已经打通。接下来可以把模型代码里所有的.cuda()和.to('cuda')改成.npu()和.to('npu'),大部分场景就能直接跑起来。
4. 核心适配原理解析:torch_npu是如何工作的
4.1 从CUDA到NPU的桥接逻辑
既然要长期使用昇腾平台跑PyTorch,理解torch_npu的工作原理是值得的,它决定了你遇到问题时的排查方向。
PyTorch内部有一套设备抽象机制,通过扩展机制可以注册自定义设备后端。torch_npu做的事情,简单来说就是向PyTorch注册了一个名为"npu"的设备类型,并把PyTorch的算子调度请求拦截下来,映射到昇腾CANN的算子库执行。
这个映射不是一一对应的。PyTorch有上千个算子,但昇腾算子库不保证全覆盖。覆盖面之外的部分,有两条处理路径:一是通过算子分解,把一个复杂算子拆成多个昇腾原生支持的算子组合;二是调用CANN的图编译引擎,将整张计算图做优化和改写。这也是为什么同样的模型,在CUDA和昇腾上跑,计算图不完全一样——因为算子融合策略不同。
4.2 算子适配与图模式
实际使用中很直观的感受是,训练模式(Eager模式)下PyTorch和昇腾的算子级差异会直接暴露。比如某些PyTorch自研算子在昇腾上没有对应实现,或者有实现但性能很低。这时候就需要打开torch_npu的图模式优化。
在PyTorch生态中,有两种方式可以使用图模式:
- 使用torch.compile(PyTorch原生的编译模式)
- 使用昇腾的静态图模式(通过torch_npu.contrib或CANN的aclnn接口)
torch.compile在昇腾平台上目前支持度在快速提升中,但不是所有模型都能成功编译。我的经验是:如果模型不大、训练时间不长,直接用Eager模式更省心;如果是大模型长时间训练或推理场景,必须考虑图模式,否则性能损失很可观。
这里补个实操技巧:在跑模型之前,先用环境变量开启算子耗时统计:
bash复制export ASCEND_GLOBAL_LOG_LEVEL=3
export PROFILING_MODE=true
这样在训练日志里能看到每个算子在NPU上的执行耗时,方便定位是哪个算子拖慢了整体速度。训练结束后记得关闭,否则日志量会非常大。
5. 常见问题与排查技巧实录
5.1 版本不匹配类问题
这类问题是新手遇到最多,且排查最费时的。典型报错包括:
ImportError: libascendcl.so: cannot open shared object file:没有正确source CANN环境变量RuntimeError: NPU error: ACL_ERROR_INVALID_PARAM:CANN版本与torch_npu不匹配AttributeError: module 'torch' has no attribute 'npu':torch_npu未成功导入
排查这类问题时,我的套路是:先看版本对应关系表,确认当前装的PyTorch、torch_npu、CANN三者是不是官方推荐组合;再看环境变量是否生效;最后看Python环境里导入的torch是不是当前虚拟环境里的版本,而不是系统自带的其他版本。
这里贴一个快速检查环境状态的命令组合:
bash复制# 确认CANN环境
ls /usr/local/Ascend/ascend-toolkit/latest
# 确认Python环境中的包版本
pip list | grep -E "torch|npu"
# 确认环境变量
echo $ASCEND_TOOLKIT_HOME
5.2 算子不支持类问题
算子报错的表现形式很多样,常见的是:
NotImplementedError: The operator [xxx] is not supported on NPURuntimeError: ACL execute op failed, error code: xxxx
如果遇到算子不支持,我的经验是三步排查法:
- 先用CPU跑一遍同样的代码,确认模型的逻辑本身没问题
- 查看昇腾官方算子支持清单,确认这个算子是否在列
- 如果清单里没有,考虑更换实现方式,比如把自定义算子改写成更基础的PyTorch算子组合
提示:昇腾社区对算子缺失的反馈通道效率还算高,但走内部提工单的周期比较长,短期内有急迫需求,先在应用层做workaround更实际。
5.3 大模型场景的常见坑
最近大模型相关的load量明显增加,昇腾上跑大模型有两个高频问题。
一个是vLLM这类推理框架的适配。vLLM官方目前主要以CUDA为主,昇腾侧如果要使用vLLM,需要借助专门的适配分支。我自己测试embedding向量模型和reranker模型时,确实遇到过怎么都启动不起来的场景。这类问题通常跟Flash Attention的NPU实现、以及vLLM的图模式缓存有关,排查方向比较固定:
- 确认vLLM-Ascend适配版本与当前CANN是否匹配
- 确认Attention后端是否设置为NPU支持的模式
- 查看启动日志里的核心报错,判断是设备初始化失败还是显存分配失败
另一个是显存分配问题。大模型加载时,昇腾NPU的HBM管理策略和CUDA不完全一致,直接沿用CUDA方式的显存清理逻辑,容易遇到申请不到显存的情况。我的做法是在脚本开头统一设置环境变量:
bash复制export PYTHONPATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages:$PYTHONPATH
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3
这样可以看到NPU显存的使用情况,类似CUDA_VISIBLE_DEVICES的效果。
6. 性能调优与后续扩展方向
6.1 单卡性能调优
环境跑通只是第一步,真正让模型在昇腾上跑得快,还需要做一些针对性的调优。
首推的是混合精度训练。910B对FP16和BF16的支持都很成熟,配合Apex库,效果明显。代码层面的改动非常简单:
python复制from apex import amp
model, optimizer = amp.initialize(model, optimizer, opt_level="O1")
# 在反向传播前
with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()
其次是DataLoader的配置。昇腾平台的数据搬运开销在某些小模型场景下占比很高,建议把num_workers设成CPU核数的一半,prefetch_factor设成4以上。我实测过同样一个视觉模型,调完DataLoader参数后,训练吞吐提升了15%左右。
6.2 多卡训练与分布式
需要多卡训练时,昇腾有自己的一套分布式通信库(HCCL),对标NVIDIA的NCCL。好在torch_npu已经把分布式通信封装得很接近PyTorch原生API了。原来用DDP的代码,基本只改两处:
python复制# 原来
torch.distributed.init_process_group(backend="nccl")
# 改为
torch.distributed.init_process_group(backend="hccl")
python复制# 原脚本启动方式
python main.py
# 改为(以8卡为例)
torchrun --nproc_per_node=8 main.py
HCCL的性能在910B上多卡互联做得还可以,8卡训练时线性扩展比大约在0.75-0.85之间,跟卡间互联拓扑强相关。如果是跨机训练,需要额外处理与网卡绑核,这个先从单机多卡练起,后续有必要再展开写。
6.3 从训练到推理的迁移路径
如果你的目标不只是训练,还要把模型部署到昇腾上进行推理服务,路径会比训练稍微绕一些。目前性价比比较高的方案是把PyTorch模型导出为ONNX,再通过CANN的ATC工具转换为昇腾的离线模型格式(OM格式),最后用MindSpore推理框架或昇腾自带的推理引擎加载。
这个方案的优点是推理性能可以优化到很高的水平,缺点是调试过程不那么透明,模型结构稍微复杂就容易转换失败。另一种更省事的方案是直接用torch_npu在Python环境下做推理,适合并发要求不高的内部场景。
无论是哪条路,我的建议都是先确保训练侧的PyTorch代码在NPU上完全跑通,再考虑推理侧的性能优化。训练侧跑通,说明算子和图结构都是昇腾平台上验证过的,推理侧转换的成功率也会高很多。
我自己这段时间用下来的整体感受是,昇腾平台的成熟度已经比前几年好非常多,尤其像910B这种主力训练卡,在主流模型上的算子覆盖和性能都已经到了可用的水平。但PyTorch生态实在太庞大了,总会有长尾算子和场景需要自己去试探。环境搭建只是万里长征第一步,这一关过了,后面接触的分布式通信、图优化、推理部署,才是真正发挥昇腾平台价值的地方。
最后再分享一个小技巧。别怕折腾环境,但每次折腾前都先做好记录:驱动版本、CANN版本、PyTorch版本、torch_npu版本、Python版本、操作系统版本,这六个信息组合起来就是一份完整的"环境画像"。凡是遇到问题,先把这份画像拿出来核对,能过滤掉一半以上的"假故障"。养成这个习惯,在昇腾平台上的实操效率会有质的提升。
