1. 这件事到底在说什么:从一条假期热搜说起
假期里技术圈突然热闹起来,起因是一条消息:DeepSeek 和华为在昇腾平台上做了一件被很多人称为“CUDA 国产替代”的事。我第一反应不是兴奋,而是想搞清楚——它到底替代了什么,替代到什么程度,跟我手头这些跑模型的活儿有没有直接关系。
先把结论摆前面:这件事的核心不是“英伟达不能用了”,而是在昇腾 NPU 上跑 DeepSeek 系列模型,从算子、通信到推理框架这一整条链路,有了可用的、经过验证的国产方案。对做本地部署、做推理服务、做模型微调的人来说,这意味着多了一条不依赖 CUDA 生态的路。
CUDA 是什么,用大白话讲:它是英伟达 GPU 的“编程语言 + 工具箱”。你写的 PyTorch 代码,底层要经过 CUDA 才能让 GPU 干活。十几年下来,整个 AI 训练和推理的软件栈几乎都长在 CUDA 上。所以“CUDA 替代”这四个字分量很重——它不是换个驱动,而是要把从框架到算子的整条路重新铺一遍。
这篇东西我打算按一个实际从业者的视角来拆:为什么昇腾这条路值得关注、它的技术链路长什么样、部署的时候具体怎么操作、会遇到哪些坑。适合正在做模型本地部署、推理服务搭建、或者单纯想搞清楚国产算力现状的人看。不管你是刚接触昇腾的新手,还是已经在 CUDA 上跑了几年模型的老手,都能从里面找到能直接抄的部分。
2. 为什么“CUDA 替代”这件事值得认真对待
2.1 先搞清楚 CUDA 到底卡在哪里
很多人对 CUDA 的理解停留在“装个驱动”。实际上 CUDA 生态的护城河有三层,一层比一层难绕。
第一层是硬件指令集。英伟达 GPU 有自己的 SM 架构、Tensor Core、显存层级,CUDA 是把这些硬件能力暴露给上层的接口。第二层是算子库,比如 cuBLAS、cuDNN、NCCL,矩阵乘法、卷积、集合通信这些高频操作都有高度优化的实现,你调一个 API 就能跑满算力。第三层是框架适配,PyTorch、TensorFlow、vLLM、llama.cpp 这些框架默认后端就是 CUDA,换硬件意味着要改后端。
这三层叠起来,导致一个现实:你换掉 GPU 硬件容易,换掉 CUDA 软件栈极难。很多团队试过用别的加速卡,最后卡在“算子不全、精度对不上、性能掉一半”上。
昇腾要做的替代,就是在这三层上分别给出对应物:硬件层是达芬奇架构的 AI Core,算子层是 CANN(异构计算架构),框架层通过 torch_npu 这类插件把 PyTorch 接过来。DeepSeek 和华为这次做的事,本质是把第三层和部分第二层打通,让 DeepSeek 的模型能在昇腾上真正跑起来、跑得动。
2.2 昇腾这条路的真实定位
我不太喜欢“替代”这个词被滥用。准确说,昇腾目前解决的是推理场景的可用性,以及部分训练场景的可行性。它不是在所有维度上对标 CUDA 的性能,而是在“能不能跑、跑得稳不稳、成本划不划算”这三个问题上给出答案。
对大多数团队来说,推理才是日常。你训练一次模型可能几周,但推理要跑几个月甚至几年。推理对生态的依赖比训练低——不需要那么多冷门算子,不需要极致的分布式通信优化,只要主流模型能高效跑起来就行。DeepSeek 系列模型在昇腾上的适配,正好打在这个点上。
提示:判断一个国产算力方案值不值得投入,先看你的场景是训练还是推理。推理场景的迁移成本通常低一个数量级。
2.3 为什么是 DeepSeek 和华为一起做
这里有个容易被忽略的逻辑:模型和硬件必须联合优化才能出效果。DeepSeek 的模型结构有自己的特点,比如 MoE 架构、特定的注意力实现、量化方案。如果只是把模型丢给硬件厂商去适配,厂商不一定懂模型的细节;如果只是模型团队去适配硬件,又不一定懂底层算子。
两边一起做,才能把模型里的关键算子针对昇腾的 AI Core 做定制优化,把通信模式针对 HCCS(昇腾的高速互联)做调整。这种联合优化的效果,往往比单方面适配好一大截。这也是为什么这次的事情被单独拿出来说——它不是简单的“支持列表加一行”,而是有实质的工程投入。
3. 昇腾软件栈拆解:从驱动到框架的完整链路
3.1 五层结构,一层都不能少
昇腾的软件栈从上到下大致是这么几层,我按实际部署时会碰到的顺序说:
| 层级 | 组件 | 作用 | 对应 CUDA 生态 |
|---|---|---|---|
| 应用层 | vLLM、MindIE、llama.cpp | 推理服务、模型加载 | vLLM、llama.cpp |
| 框架层 | PyTorch + torch_npu | 模型定义与执行 | PyTorch + CUDA |
| 算子层 | CANN、AscendCL | 算子库、运行时 | cuDNN、cuBLAS |
| 驱动层 | NPU Driver、Firmware | 硬件驱动 | NVIDIA Driver |
| 硬件层 | Ascend 910/310 系列 | AI 计算芯片 | GPU |
新手最容易踩的坑是:只装了驱动就以为能跑,结果框架层没装 torch_npu,或者 CANN 版本和驱动版本对不上。这五层必须版本互相匹配,任何一层错位都会导致“能识别设备但跑不了模型”。
3.2 CANN 是什么,为什么它是关键
CANN 全称是 Compute Architecture for Neural Networks,可以理解成昇腾版的“CUDA + cuDNN + 部分 NCCL”。它包含几个核心部分:
- GE(Graph Engine):图编译和优化,把上层框架的计算图转成昇腾能执行的图。
- AscendCL:编程接口,类似 CUDA Runtime API。
- 算子库:内置大量常用算子,也支持自定义算子开发。
- HCCL:集合通信库,对应 NCCL,多卡多机通信靠它。
CANN 的版本管理是部署里最烦人的部分。它和驱动、固件、torch_npu 之间有严格的对应关系。我见过太多人因为 CANN 版本比驱动新一个小版本,卡在初始化阶段报错。
注意:装之前一定去官方文档查版本配套表,不要凭感觉装最新版。最新版往往和你的驱动不匹配。
3.3 torch_npu 的角色
torch_npu 是 PyTorch 的昇腾适配插件。它的作用是让 PyTorch 的 tensor 能放到 NPU 上,让.to('npu')这种调用生效。装好之后,代码改动量可以很小——很多情况下只需要把cuda换成npu。
但这里有个认知误区:能跑不等于跑得好。torch_npu 保证的是功能可用,性能优化要靠 CANN 的算子实现和模型本身的适配。有些算子在 NPU 上走的是通用实现,速度不如 CUDA 上的定制版本。所以迁移之后一定要做性能对比,不能只看“跑通了”。
4. 实操:在昇腾上部署 DeepSeek 模型的完整流程
4.1 环境准备与版本核对
假设你拿到一台昇腾 A2 服务器(比如 Atlas 800 系列),要部署 DeepSeek 模型。第一步不是急着装东西,而是核对版本。
# 查看驱动和固件版本 npu-smi info # 输出里会显示 Driver Version 和 Firmware Version # 记下这两个版本号,去官方配套表查对应的 CANN 版本版本核对的原则是:驱动版本决定 CANN 版本上限,CANN 版本决定 torch_npu 版本。三者是一条链,不能跳。
我一般会建一个版本记录表,把每个组件的版本写清楚,方便后面排查:
| 组件 | 版本 | 获取方式 |
|---|---|---|
| NPU Driver | 查 npu-smi | 随机器预装 |
| Firmware | 查 npu-smi | 随机器预装 |
| CANN | 按配套表选 | 官网下载 |
| torch_npu | 按 CANN 选 | pip 或源码 |
| PyTorch | 按 torch_npu 选 | pip |
| Python | 3.8-3.10 | 系统自带或 conda |
4.2 安装 CANN 与依赖
CANN 的安装包分两种:run 包和 tar 包。run 包是交互式安装,适合新手;tar 包适合批量部署。我一般用 run 包,因为能看到每一步在装什么。
# 给安装包加执行权限 chmod +x Ascend-cann-toolkit_*.run # 执行安装,按提示选择安装路径 ./Ascend-cann-toolkit_*.run --install # 安装完成后配置环境变量 source /usr/local/Ascend/ascend-toolkit/set_env.sh环境变量这一步很多人会漏。不 source 的话,后面编译算子、跑框架都会找不到库。建议把 source 写进~/.bashrc,省得每次手动敲。
装完 CANN 之后,还要装对应的内核态驱动包(如果机器没预装)。这一步通常需要 root 权限,而且装完要重启。我踩过的坑是:驱动装完没重启就去跑模型,结果设备识别异常,白白排查了半天。
4.3 安装 PyTorch 与 torch_npu
这一步的顺序很关键:先装 PyTorch,再装 torch_npu,而且版本必须严格对应。
# 以 PyTorch 2.1 为例,先装对应版本 pip install torch==2.1.0 # 再装匹配的 torch_npu pip install torch-npu==2.1.0.post版本号torch_npu 的版本号里带 post 后缀,这个后缀和 CANN 版本挂钩。装错了会出现“import torch_npu 成功但调用时报错”的情况。
装完之后验证:
import torch import torch_npu # 检查 NPU 是否可用 print(torch.npu.is_available()) print(torch.npu.device_count()) # 建一个 tensor 放到 NPU 上 x = torch.randn(3, 3).npu() print(x.device)如果is_available()返回 False,八成是环境变量没 source,或者驱动和 CANN 版本不匹配。
4.4 模型加载与推理验证
环境通了之后,加载 DeepSeek 模型。这里分两条路:一条是用 vLLM 这类推理框架,一条是用 transformers 直接加载。
用 transformers 的话,改动很小:
from transformers import AutoModelForCausalLM, AutoTokenizer import torch import torch_npu model_path = "/path/to/deepseek-model" tokenizer = AutoTokenizer.from_pretrained(model_path) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, device_map="auto" ) # 关键:把模型移到 NPU model = model.to("npu") inputs = tokenizer("你好,请介绍一下你自己", return_tensors="pt").to("npu") outputs = model.generate(**inputs, max_new_tokens=100) print(tokenizer.decode(outputs[0], skip_special_tokens=True))第一次跑建议用一个小模型或者小 max_new_tokens 验证链路,别一上来就加载完整大模型。加载大模型动辄几分钟,出错了排查成本高。
提示:如果显存(NPU 内存)不够,优先考虑量化版本。DeepSeek 有多个量化方案,INT8 和 INT4 在昇腾上都有对应支持。
4.5 性能调优的几个抓手
跑通之后就是调优。昇腾上影响性能的主要有这几个因素:
- batch size:NPU 对 batch 的敏感度和 GPU 不同,需要实测找最优点。
- 算子选择:部分算子有多个实现,CANN 会自动选,但可以通过环境变量干预。
- 通信优化:多卡场景下 HCCL 的配置影响很大,尤其是 all-reduce 的算法选择。
- 内存管理:NPU 内存分配策略和 GPU 不同,大模型要注意碎片问题。
我一般会先用小 batch 测单次推理延迟,再逐步加大 batch 测吞吐,画出延迟-吞吐曲线,找拐点。这个方法和 GPU 上调优一样,但具体数值会差很多,不能直接套用 GPU 的经验。
5. 迁移过程中最容易踩的坑
5.1 版本不匹配:九成的报错都源于此
我统计过自己遇到的昇腾报错,大概九成是版本问题。表现形式五花八门:有的是 import 就失败,有的是跑到某个算子才报错,有的是性能异常低。
排查方法很简单:把所有组件的版本列出来,对着官方配套表逐个核对。不要相信“应该差不多”,昇腾的版本兼容性比 CUDA 严格得多。
| 报错现象 | 大概率原因 | 排查方向 |
|---|---|---|
| import torch_npu 失败 | torch 和 torch_npu 版本不匹配 | 核对两者版本 |
| 设备识别不到 | 驱动未装或未重启 | npu-smi info |
| 算子报错 | CANN 版本过低 | 升级 CANN |
| 性能异常低 | 算子走了通用实现 | 查 CANN 日志 |
| 内存溢出 | 分配策略问题 | 调 batch 或量化 |
5.2 算子不支持怎么办
DeepSeek 模型里有些算子可能在昇腾上没有高度优化的实现。这时候有几个选择:
第一,看 CANN 有没有更新版本支持。第二,用自定义算子开发(TBE 或 Ascend C),但成本高。第三,改模型结构,用等价算子替换。第四,回退到 CPU 执行该算子(性能会掉,但能跑通)。
实际项目里,我一般先查官方算子支持列表,确认哪些是原生支持、哪些是通用实现、哪些完全不支持。完全不支持的算子如果出现在关键路径上,就要认真评估迁移成本了。
5.3 精度对齐问题
从 CUDA 迁到昇腾,精度可能有细微差异。原因是浮点运算的实现顺序、累加方式不同。大多数情况下差异在可接受范围,但有些对精度敏感的任务(比如某些科学计算)需要额外验证。
验证方法:同一组输入,分别在 GPU 和 NPU 上跑,对比输出。如果差异超过阈值,就要定位是哪个算子引入的。这个过程比较繁琐,但关键项目必须做。
5.4 多卡通信的坑
单卡跑通不代表多卡能跑。HCCL 的配置和 NCCL 不同,多机场景下网络配置、rank 设置、通信算法选择都有讲究。
我遇到过的典型问题:多卡启动后卡在初始化,原因是 rank table 配置错误;或者通信性能远低于预期,原因是没走 HCCS 高速互联而是走了普通网络。这些都要在部署前确认硬件拓扑和通信配置。
6. 这件事对普通开发者的实际影响
6.1 你需不需要现在就迁移
我的建议是分情况:
如果你在做推理服务,且对成本敏感,昇腾值得评估。尤其是批量推理场景,国产算力的性价比在某些配置下确实有优势。
如果你在做模型训练,尤其是大规模预训练,目前 CUDA 生态还是更成熟。昇腾在训练上的适配在进步,但冷门算子和分布式训练的成熟度还有差距。
如果你只是学习研究,手头有 GPU 就先用 GPU,没必要为了“国产”而折腾。等技术更成熟、文档更完善再上手,学习成本更低。
6.2 迁移的成本收益怎么算
迁移不是免费的。成本包括:环境搭建时间、代码适配时间、性能调优时间、踩坑排查时间。收益包括:硬件成本、供应链稳定性、特定场景的性能优势。
我一般会做一个简单的评估:如果迁移成本能在半年内通过硬件成本节省收回,就值得做;如果模型里有大量冷门算子,迁移成本可能高到不划算。
6.3 生态还在快速变化
必须承认,昇腾的软件生态还在快速迭代。今天不支持的算子,下个版本可能就支持了;今天的性能瓶颈,下个版本可能就优化了。所以评估的时候要看趋势,不能只看当前状态。
我的做法是:保持关注,小规模试点,等生态成熟到满足自己核心需求时再大规模投入。这次 DeepSeek 和华为的合作,就是生态成熟度提升的一个信号。
7. 几个实操中总结的小技巧
第一个技巧:建一个版本快照。每次环境搭好之后,把npu-smi info、pip list、环境变量都存一份。下次出问题,对比快照就能快速定位是不是环境变了。
第二个技巧:先用小模型验证链路。不要一上来就加载完整大模型。用一个小模型跑通“驱动-CANN-框架-模型”整条链路,再换大模型。这样出问题容易定位。
第三个技巧:日志级别调高。昇腾的日志默认级别可能不够详细,排查问题时把ASCEND_GLOBAL_LOG_LEVEL调成 INFO 或 DEBUG,能看到更多细节。但注意日志量会很大,排查完记得调回去。
第四个技巧:善用官方社区和配套表。昇腾的版本配套表、算子支持列表、常见问题文档都在官方渠道,遇到问题先查这些,比盲目搜索效率高得多。
第五个技巧:性能对比要控制变量。对比 GPU 和 NPU 性能时,确保 batch size、精度、模型版本、输入长度都一致,否则对比没有意义。我见过有人拿 GPU 的 FP16 对比 NPU 的 INT8,得出“NPU 快一倍”的结论,这是不严谨的。
8. 关于国产算力替代的一点个人看法
我在这个圈子里待了这些年,见过太多“替代”的口号,也见过太多半途而废的项目。这次 DeepSeek 和华为的合作,我的判断是:它不是一个终点,而是一个有实质内容的节点。
实质内容在于:它证明了在主流大模型推理这个场景上,国产软硬件栈可以做到可用。可用之后,才有资格谈好用、谈性能、谈生态。从 0 到 1 的这一步,比从 1 到 10 难得多。
对普通开发者来说,我的建议是保持理性:既不盲目追捧,也不轻易否定。手头的活儿该用 CUDA 就用 CUDA,遇到合适的场景可以试试昇腾。技术选型看的是解决问题,不是站队。
最后分享一个我自己的习惯:每次遇到新的技术方案,我都会问三个问题——它解决了什么具体问题?它的边界在哪里?它的成本收益比如何?这三个问题问下来,基本就能判断值不值得投入时间了。这次昇腾的 CUDA 替代方案,我的答案分别是:解决了推理场景的国产化可用性问题;边界在训练和冷门算子上;成本收益比取决于你的具体场景。至于你的答案是什么,得结合你自己的活儿来判断。