在 Atlas 上跑通 DeepSeek-R1:KTransformers 昇腾 NPU 部署实战
【免费下载链接】ktransformersA Flexible Framework for Experiencing Heterogeneous LLM Inference/Fine-tune Optimizations项目地址: https://gitcode.com/GitHub_Trending/ktr/ktransformers
导语
为什么要把 LLM 搬到昇腾 NPU 上跑?最直接的答案是内存和成本。DeepSeek-R1 这类满血 MoE 模型的路由专家权重体量巨大,GPU 集群要么买不起、要么排队。Atlas 服务器配 1TB DDR5,用 CPU + NPU 异构分工承接推理,KTransformers 的 CPU 推理内核正好吃下这条路径——专家权重放在内存里,注意力与算子下沉到 NPU 执行。本文是一次真实部署的复盘:从 Atlas 硬件、CANN 软件栈、双权重合并,到 balance_serve 服务启动与性能核对,每一步的坑都标在原地。
部署前避坑清单
先知道会踩什么坑,后面装环境时心里有数:
- transformers 版本被钉死在
4.57.1,其他版本未验证,装错了直接报结构解析异常。 - torch_npu 不能直接用 pypi 预编译包。因为涉及新增算子,必须从源码编译 v2.5.1 分支,且编译后版本号里的哈希后缀要手动去掉(见下文)。
- 两份权重缺一不可。Q4 的 GGUF 与 W8A8 的 safetensor 必须先用合并脚本合成一份,只放一份进模型目录,服务起不来。
- 400GB 物理内存是下限。满血版 DeepSeek-R1/V3 的路由专家权重需要约 400GB 物理内存,内存不足的机器别硬试。
- CANN 环境变量没 source,一切白搭。
libhccl.so、libascend_hal.so缺失都是同一个原因,速查表里给了解法,这里不重复展开。
搭建 Atlas 硬件与操作系统
本节目标是把一台 Atlas 2UP 变成可用的 300I A2 推理节点。硬件与系统版本按下表核对:
| 项 | 规格 |
|---|---|
| 服务器 | Atlas 2UP |
| NPU | 300I A2(当前 KTransformers 适配的 NPU 型号) |
| CPU | HUAWEI Kunpeng 920 7270Z |
| 内存 | DDR5 服务器内存(1TB) |
| 操作系统 | Ubuntu 22.04 for aarch64 |
| 内核 | 5.15.0-25-generic |
装完系统后做两件事:关闭自动更新,并记录好 CANN、NPU 驱动的实际安装路径——后面 source 环境变量要用。硬件插拔与固件确认建议拉原厂支持一起完成,NPU 的固件版本直接影响后面 Kernel 能否加载。
配置 CANN 软件栈
软件栈分四层:HDK 固件驱动 → CANN → Python 环境 → torch_npu。顺序不能乱,版本必须严格对齐下表,任何一层漂移都会在算子加载阶段报错:
| 组件 | 版本 |
|---|---|
| Ascend HDK | 25.3.RC1 |
| CANN | 8.3.RC1.alpha003(需装 ToolKit、Kernel、NNAL 三件) |
| Python | 3.11(conda 环境,一行conda create -n py311 python=3.11即可) |
| PyTorch | torch==2.5.1/torchvision==0.20.1/torchaudio==2.5.1 |
| transformers | 4.57.1(必须,无替代) |
| torch_npu | v2.5.1 分支源码编译 |
torch_npu 编译前,先把 CANN 与 NNAL 的环境加载进当前 shell:
source /usr/local/Ascend/ascend-toolkit/set_env.sh source /usr/local/Ascend/nnal/atb/set_env.sh⚠️ 编译过程对 github、gitcode 等平台的网络访问敏感,网络不畅通时子模块拉取会静默失败。从源码仓库(Gitcode 上的 Ascend/pytorch)获取代码后编译 v2.5.1 分支。
编译装好后还有一个反直觉的步骤:打开 site-packages 下torch_npu/version.py(路径用pip show torch_npu确认),把形如__version__ = '2.5.1.post4+git69550dfc'的版本号改为__version__ = '2.5.1.post4',去掉哈希后缀。环境对版本号有严格匹配,带后缀会直接被拒。
合并 Q4 与 W8A8 双权重
KTransformers 对 NPU 路径的精度要求决定了权重要走"双份合并":Q4_K_M 的 GGUF 权重负责路由专家的 CPU 侧存储,W8A8 的 safetensor 权重负责 NPU 侧算子。两份原始权重都下载就位后,跑仓库里的合并脚本:
python merge_safetensor_gguf.py \ --safetensor_path /path/to/DeepSeek-R1-W8A8 \ --gguf_path /path/to/DeepSeek-R1-Q4_K_M \ --output_path /output/merged脚本位于 merge_tensors/。合并完成后,后续启动参数只指向合并后的目录,两份原始权重不再参与推理。
初始化项目与编译扩展
克隆仓库并拉取子模块:
git clone https://gitcode.com/gh_mirrors/ktr/ktransformers cd ktransformers git submodule update --init --recursive进入编译前,先安装构建依赖并加载 CANN 环境:
source /usr/local/Ascend/ascend-toolkit/set_env.sh apt install cmake libhwloc-dev pkg-config bash ./install.shinstall.sh 会编译 CPU 侧的 kt-kernel 扩展与 llamafile 算子库。⚠️ 在 aarch64(ARM82)上,如果链接阶段报iqk_mul_mat相关错误,把 third_party/llamafile/iqk_mul_mat_arm82.cpp 文件内的两行宏定义注释掉即可通过编译:
// #define iqk_mul_mat iqk_mul_mat_arm82 // #define iqk_mul_mat_moe iqk_mul_mat_moe_arm82这是 aarch64 工具链的已知适配点,x86 机器不用动这个文件。
启动 balance_serve 推理服务
服务入口是ktransformers/server/main.py,关键参数与 NPU 优化规则文件一起给出:
export USE_MERGE=0 export INF_NAN_MODE_FORCE_DISABLE=1 export TASK_QUEUE_ENABLE=0 source /usr/local/Ascend/ascend-toolkit/set_env.sh source /usr/local/Ascend/nnal/atb/set_env.sh python ktransformers/server/main.py \ --port 10002 \ --model_path <merged_weights> \ --gguf_path <merged_weights> \ --model_name DeepSeekV3ForCausalLM \ --optimize_config_path ./ktransformers/optimize/optimize_rules/npu/DeepSeek-V3-Chat-300IA2-npu-serve.yaml \ --max_new_tokens 1024 \ --cache_lens 20480 \ --backend_type balance_serve环境变量逐个说明(故障排查时只需对照这一段,速查表不重复):
TASK_QUEUE_ENABLE=0:关闭任务队列,保证算子下发顺序严格有序,这是开启图下沉的前置条件;USE_BALANCE_SERVE=1与USE_NUMA=1:启用 balance_serve 后端与 NUMA 绑定,启动脚本中按需导出;USE_MERGE=0:权重已离线合并,运行时不再做在线合并;INF_NAN_MODE_FORCE_DISABLE=1:禁用推理期的 NaN 强制检查,省去每步的开销;--backend_type balance_serve:指定服务后端;--cache_lens 20480决定 KV cache 预分配长度,按上下文预算调整。
优化规则文件位于 ktransformers/optimize/optimize_rules/npu/,300I A2 上 DeepSeek-V3 的服务化配置即上面启动参数引用的DeepSeek-V3-Chat-300IA2-npu-serve.yaml,同目录还有非 serve 版与 Qwen3 的对应配置可选。
用基准数据验证 NPU 推理性能
服务起来后,用固定长度 prompt 压一遍 prefill 与 decode。以下为 Batchsize=4、输出长度 1024 条件下的参考值(tokens/s):
| Prompt 长度 | 1K | 2K | 4K |
|---|---|---|---|
| Prefill | 174.68 | 169.52 | 167.15 |
| Decode | 16.07 | 16.12 | 16.48 |
实测与参考值偏差超过 20% 时,先查 NPU 侧算子是否真的走下去了(npu-smi看利用率),再查cache_lens是否被默认值截断。需要定位慢算子时,用PROF_PREFILL与PROF_DECODE两个环境变量分别开启两阶段的 profiling 输出。
故障速查
| 现象 | 处理 |
|---|---|
报libhccl.so缺失 | 见上文「配置 CANN 软件栈」:当前 shell 先source /usr/local/Ascend/ascend-toolkit/set_env.sh |
报libascend_hal.so缺失 | 补驱动库路径:export LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/driver:$LD_LIBRARY_PATH |
aarch64 编译期iqk_mul_mat链接错误 | 注释 third_party/llamafile/iqk_mul_mat_arm82.cpp 中两行宏定义(见「初始化项目与编译扩展」节) |
| torch_npu 导入即报错 | 检查version.py是否仍带+git...哈希后缀 |
| 服务启动 OOM | 物理内存不足 400GB,满血版无法容纳路由专家权重 |
下一步建议:把--cache_lens按业务真实上下文长度调到位,再用PROF_PREFILL抓一次 prefill 分布——prefill 吞吐从 170 tokens/s 量级再往上走,瓶颈基本都在长序列的注意力段。
【免费下载链接】ktransformersA Flexible Framework for Experiencing Heterogeneous LLM Inference/Fine-tune Optimizations项目地址: https://gitcode.com/GitHub_Trending/ktr/ktransformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考