昇腾部署第 F1 课:模型迁移——把 GPU 上跑得通的脚本搬到昇腾上
编制:观澜|日期:2026-10-05|级别:T2 标准级 层级:F 层 · 场景实战(F 层第 1 课;前接 A–E 全部零件课) 素材来源:昇腾官方 ModelZoo 文档(《迁移方式比较》《自动迁移》《工具迁移》《手工迁移》)+《分析迁移工具(msTransplant)用户指南》 本课定位:前面 A–D 讲的是「怎么装」,F 层讲的是「怎么用」。 第一件事就是把已经在 GPU 上跑得通的脚本搬到昇腾上——这件事有个正式名字:模型迁移。
第一段 章前定位
到这里,零件已经全部就位:驱动、固件、CANN、框架、服务层,每一件都知道装在哪、怎么验、坏了查哪。
但这些都是「把机器准备好」。从这一课开始,问题换了一个:
我手上这堆脚本、模型、训练代码,怎么让它在这台机器上跑起来?
绝大多数人的脚本不是为昇腾写的——它们是为 GPU 写的。里面到处都是 torch.cuda、device='cuda'、nccl。
模型迁移就是把这些「为 GPU 写的部分」换成「为 NPU 写的」。
官方给了三种做法,本课把三种都讲清楚,并且告诉你什么情况下该选哪一种——这是官方文档里分散着、但现场最需要的一句话。
第二段 学习目标
学完这一课,你应该能:
- ✅ 说出三种迁移方式的名字,并说清它们的判据——什么时候做转换
- ✅ 用自动迁移把一份脚本改到昇腾上跑(只需一行代码,但有三条约束要知道)
- ✅ 用工具迁移跑出迁移报告,并读懂报告里的三样东西
- ✅ 说出手工迁移必须改的两个点(含多卡那条)
- ✅ 知道什么时候不能用自动迁移,该退回哪种方式
- ✅ 迁移后怎么算"成功了"——不是"没报错",而是"迭代日志开始打印、权重能存下、结果对得上"
第三段 全景:三种迁移方式(动手前先记一条铁律)
在讲三种方式之前,先把官方的前置条件放在最前面——因为它被跳过的时候最多:
⚠️ 迁移前要保证该脚本能在 GPU 或 CPU 上运行。(官方原文 CAUTION)
这句话的意思是:迁移是「换执行设备」,不是「修 bug」。
一份本来就跑不通的脚本,迁到昇腾上只会以一种你更不熟悉的方式跑不通 ✗。
所以正确顺序是:
① 先在 GPU 或 CPU 上把脚本跑通 ← 这一步不能省
② 再迁移到昇腾
③ 迁移后对照结果是否一致
为什么这条最重要:迁移之后如果出错,你无法判断是「迁移引入的问题」还是「脚本本来就有问题」。 先在原平台上跑通,就把这两种可能分开了——这是排查成本的分水岭。
顺带说明本课的适用范围:官方的迁移文档主要围绕 PyTorch 训练脚本展开(ModelZoo 传统模型训练;官方明确支持 PyTorch 2.1.0 / 2.6.0 / 2.7.1 / 2.8.0 四个版本的脚本迁移)。 推理服务的部署脚本(如 vLLM Ascend)走的是另一条路,见 D 层。
三种方式(本课的核心)
官方《迁移方式比较》原文:
目前支持 3 种迁移方式:自动迁移(推荐)、工具迁移、手工迁移。 推荐用户使用最简单的自动迁移方式。
三种方式最本质的区别只有一个——转换发生在什么时候:
| 方式 | 什么时候转换 | 一句话记住 | 官方推荐度 |
|---|---|---|---|
| 自动迁移 | 边训练边转换 | 加一行代码,跑起来它自己换 | ⭐ 推荐 |
| 工具迁移 | 先转换脚本,再训练 | 跑个工具,产出一份改好的脚本 + 一份报告 | 次选 |
| 手工迁移 | 人自己改 | 对比 GPU/NPU 接口,逐个动手改 | 最后手段 |
判据:
想省事、脚本不太特殊 → 自动迁移
要一份「改了什么」的书面记录 → 工具迁移(它产迁移报告)
自动迁移用不了(见第五段三条约束)→ 工具迁移
工具迁移也搞不定 → 手工迁移
⭐ 记法:自动迁移是「边跑边换」,工具迁移是「先换再跑」。 这一句就分清了前两种。
第四段 【如果我来做】——从「脚本在 GPU 上跑通」到「在昇腾上跑起来」
这一节是本课的核心。 前三段讲的是"模型迁移是什么、有哪三条路",这一节讲的是:如果现在把这件事交给我,我从哪里开始、按什么顺序做、每一步凭什么判断。
4.0 先亮结论:我会按这个顺序做
为什么顺序不能反:迁移最容易犯的错是顺序颠倒——
- 脚本本身没跑通就先迁移,之后一报错,分不清是"迁移引入的"还是"脚本本来就有"(第三段那条铁律讲的就是这个);
- 或者一上来就手工改一整份脚本,结果自动迁移一行就能解决,白改了一天。
我会按这个顺序做(顺序 = 我"手伸进去"的顺序,不是教科书的顺序):
① 先跑通原平台:先在 GPU 或 CPU 上把脚本跑成功(官方前置铁律)——先把"脚本本身的问题"和"迁移引入的问题"分开 ② 摸底(下命令查,不靠猜):查 PyTorch 版本、脚本里有没有
torch.jit.script/channel_last/amp_C/ APEX FusedAdam——这几条直接决定选哪种方式 ③ 定方案(选哪一种):默认先试自动迁移(官方推荐);命中第五段那三条约束之一,就转工具迁移 ④ 动手·自动迁移(一行代码 + 立刻验):加from torch_npu.contrib import transfer_to_npu,跑起来看有没有开始打印迭代日志 ⑤ 卡住转工具迁移(要那份迁移报告):跑pytorch_gpu2npu.sh,拿「不支持算子列表」,照着报告逐条调 ⑥ 还不行转手工迁移(人自己改):对比 GPU/NPU 接口改设备;多卡把通信改成 HCCL ⑦ 收口(用完整流程当验收):不是"没报错"就算过——完整跑一遍(含保存权重),结果与参考平台对得上,才算迁移成功 ⑧ 诚实(官方没给的地方明说):性能数字、通用的「不支持算子清单」官方都没给——这里要自己查、自己试,我不编
下面把中间的每一步展开。
4.1 第 0 步:先跑通原平台(不迁任何东西)
目的:在动手之前,先拿到一份「能跑通的基准」。
我会先做这三件事(都不涉及昇腾):
- 在 GPU 或 CPU 上,把这份脚本完整跑一遍(哪怕只跑 1 个 epoch),确认它能正常开始训练、正常保存权重。
- 记下这次运行的输出(迭代日志、loss、权重文件)——这是后面「迁移后对照」的基准。
- 记下脚本的 PyTorch 版本(工具迁移的
-v参数要用;官方只支持 2.1.0 / 2.6.0 / 2.7.1 / 2.8.0)。
为什么先做这一步:迁移完如果出错,这一步是唯一能证明"不是我改坏的"的东西。 跳过它的代价,是之后每一次报错都多花一倍时间去分辨"是我迁的,还是本来就有的"。
4.2 第 1 步:摸底——查四个"选方式的开关"
摸底是"下命令查事实",不是"凭感觉选方式"。 我会在脚本里搜这四个词:
| 搜什么 | 查到了意味着 | 去哪一步 |
|---|---|---|
torch.jit.script |
自动迁移会冲突(官方:会屏蔽该功能) | 直接转工具迁移 |
channel_last |
自动迁移暂不支持该特性 | 改用 contiguous,或转工具迁移 |
amp_C |
NPU 平台不支持,工具迁移也处理不了 | 手动删除 import amp_C 相关代码 |
APEX 的 FusedAdam |
官方:自动迁移和工具迁移都不支持 | 自行修改 |
另外两个"不可返工"的事实也要在这一步查清:
- 原脚本必须能在 GPU 环境、且 Python 3.7 及以上运行成功(官方约束)——不满足这条,后面全白做;
- 脚本的数据加载是不是走
torch.utils.data.DataLoader——只有走它,多卡迁移(distributed)才支持。
4.3 第 2 步:定方案(三条路选一条)
回到第三段的判据:默认先试自动迁移;命中上面任一"开关",就按下表退一档:
有 torch.jit.script / 与已适配套件冲突 → 自动迁移不能用 → 工具迁移
要一份「改了什么」的书面记录 → 工具迁移
工具转换后仍报算子异常 / 要完全掌控改动 → 手工迁移
多卡(单卡迁多卡) → 工具迁移的 distributed 模式
⚠️ 别默认上最重的那条路。手工迁移要"对 AI 模型有迁移基础",是最后手段,不是首选。
4.4 第 3、4 步:动手,且每步立刻验
两条具体的动手路线,命令都放在后面:
- 自动迁移(官方推荐)→ 见第五段:加一行
from torch_npu.contrib import transfer_to_npu,跑起来。 - 工具迁移 / 手工迁移 → 见第六段:跑
pytorch_gpu2npu.sh出报告;或逐个接口手改。
⭐ 一个我每次都会坚持的原则:每改一处,立刻跑一次,不攒着一起跑。 攒着一起跑,一旦报错,你不知道是哪一处改坏的——这是迁移阶段最常见的返工来源。
4.5 第 5 步:怎么算"迁移成功"(不是"没报错")
官方的判据是正向的(《自动迁移》原文):
- 如果训练正常进行,开始打印迭代日志,说明训练功能迁移成功。
- 如果成功保存权重,说明保存权重功能迁移成功。
- 如果模型包含评估、在线推理功能,对比评估推理结果和日志打印情况,判断与 GPU/CPU 是否一致,决定迁移是否成功。
⭐ 第 3 条才是真正的验收口径:"迭代日志在打印"只说明"跑得动";"结果和参考平台对得上"才说明"迁移对了"。 这两件事不是一回事——下一课(F2 精度调试)专门处理"跑得动但结果不对"。
4.6 这一节的方法论(它比具体命令更耐用)
把上面八步抽象出来,就是一条通用做法:
① 先跑通原平台:迁移前先在 GPU/CPU 上跑成功——把"不可返工的事"在这里查出来 ② 摸底:不装任何东西,下命令把事实查清楚(版本、四个开关、加载方式) ③ 定方案:先想清楚"选哪一条路",别默认上最重的手工迁移 ④ 动手:按官方给的命令照做,每步都验,不攒着一起验 ⑤ 卡住就换道:自动迁移卡住→工具迁移,工具迁移卡住→手工迁移,别在一条路上死磕 ⑥ 收口:拿"真实的完整流程跑一遍(含保存权重)"当验收,而不是拿"没报错"当验收 ⑦ 出证据:把迁移报告、迭代日志、权重文件留下——可给别人看 ⑧ 诚实:官方没给答案的地方(性能数字、通用不支持算子清单),明说"这里要自己查",不编
第 ⑤ 条和第 ⑧ 条是本课最容易被跳过的两条: 第 ⑤ 条——很多人卡在自动迁移报的错里死磕,其实换工具迁移就过去了; 第 ⑧ 条——迁完被问"快了多少",官方没给数字,就如实说"没给,要自己测",不编一个。
第五段 自动迁移(推荐):一行代码 + 三条约束
5.1 怎么用(官方原文)
自动迁移操作简单,且修改内容少,只需在训练脚本中添加
from torch_npu.contrib import transfer_to_npu即可完成脚本迁移。
命令 from torch_npu.contrib import transfer_to_npu
含义:导入自动迁移的库代码——它是"导入即生效",不需要调用任何函数,这正是它叫"自动"的原因 拆解:
torch_npu(昇腾的 PyTorch 插件包,见 C1)→contrib(contribution,扩展模块)→transfer_to_npu(把 CUDA 调用转接到 NPU) 用法:加在训练脚本的 import 区—— · PyTorch 2.4.0 及之前:放在import torch_npu之后 · PyTorch 2.5.1 及之后:设备插件自动加载,无需再写import torch_npu,直接加这一行即可 · 官方注明:仅 PyTorch 框架下需要加这一行 期望:脚本运行时自动把 CUDA 接口替换成 NPU 接口,整体"边训练边转换" 不过会怎样:报No module named 'torch_npu'→ TorchNPU 没装好或没 source 环境变量(回 C1); 若是多卡且与 backend 判断相关的报错,见 5.3 约束二
官方给的放置位置(PyTorch 2.4.0 及之前版本):
import torch
import torch_npu
...
from torch_npu.contrib import transfer_to_npu
官方说明:脚本在运行时会自动将脚本中的 CUDA 接口替换为昇腾 AI 处理器支持的 NPU 接口, 整体过程为边训练边转换。
5.2 跑起来看结果(官方示例命令)
命令 python main.py -a resnet50 --gpu 1 --epochs 1 --dummy
含义:拉起一次训练,验证迁移是否成功(官方"快速入门"里用的样例:ResNet50 / ImageNet) 拆解:
main.py(训练入口脚本)+-a resnet50(选 ResNet50 模型)+--gpu 1(用卡 1)+--epochs 1(只迭代 1 次)+--dummy(用虚拟数据集,先把迁移跑通) 用法:把main.py和这些参数换成你自己脚本的入口和参数;想快速验证就加--dummy用虚拟数据 期望:开始打印迭代日志——官方原文:"开始打印迭代日志,说明训练功能迁移成功";再完整跑一遍能成功保存权重,则保存权重功能也迁移成功 不过会怎样:卡在开始前报错 → 分两类查: ·import阶段就报 → 环境问题(TorchNPU / 版本配套,回 C1、B3) · 跑起来才报某个 CUDA 接口 → 该 API 可能不支持(见 5.4)
5.3 ⚠️ 三条使用约束(官方原文,一条都不能漏)
约束一:channel_last 暂不支持
当前自动迁移暂不支持
channel_last特性,建议用户使用contiguous代替。
约束二:nccl 会被替换,但「判断语句里的字符串」不会
若原脚本中使用的 backend 为
nccl,在init_process_group初始化进程组后, backend 会被自动迁移工具替换为hccl。 如果后续代码逻辑包含 backend 是否为 nccl 的判断,例如assert backend in ['gloo', 'nccl']、if backend == 'nccl', 请手动将字符串nccl改写为hccl。
⭐ 这条最容易踩:自动迁移能改接口调用,但改不了写在字符串里的平台名——
因为它不知道你把 'nccl' 写在那个位置是想干什么 ✗。
约束三:torch.jit.script 会冲突
自动迁移工具使用了 Python 的动态特性,但
torch.jit.script不支持 Python 的动态语法, 因此原训练脚本中包含torch.jit.script时,使用自动迁移功能会产生冲突。 目前自动迁移时会屏蔽torch.jit.script功能; 若用户脚本中必须使用torch.jit.script功能,请使用工具迁移进行迁移。
⭐ 这条是「该选哪种方式」的硬判据:
脚本里必须用 torch.jit.script → 自动迁移不能用 → 改用工具迁移
另外官方还提示:自动迁移工具与已适配的套件可能存在功能冲突,若发生冲突,也改用工具迁移。
5.4 跑了但报错怎么办(官方给的下一步)
官方《自动迁移·注意事项》原文:
若训练过程中提示部分 CUDA 接口报错,可能是部分 API(算子 API 或框架 API)不支持引起的。 使用 PyTorch Analyse 工具对模型脚本进行分析,获得支持情况存疑的 API 列表, 进入昇腾开源社区提出 ISSUE 求助;算子请参考《TorchNPU 框架特性》中的"自定义算子适配开发"章节进行适配。
这条很实用:官方把"自动迁移报错"的下一步直接指到了工具迁移的分析能力(拿到"存疑 API 列表")—— 所以自动迁移卡住时,不用自己猜,转工具迁移就能拿到一份清单。
5.5 自动迁移的取舍(归纳)
| 优点 | 代价 |
|---|---|
| 改动最小(一行) | 三条约束要自己核对 |
| 不用额外装工具依赖 | 没有迁移报告——改了什么不留痕 |
| 边跑边转换,快 | 出问题时,改动是「隐式」的,不好定位 |
第六段 工具迁移与手工迁移:命令 + 迁移报告 + 两个必改点
A · 工具迁移(要一份迁移报告,就选它)
工具迁移会产出一份「迁移报告」(官方原文):
使用迁移工具 PyTorch GPU2Ascend,自动将训练脚本中的 CUDA 接口替换为 NPU 接口, 并生成迁移报告(脚本转换日志、不支持算子的列表、脚本修改记录)。 训练时,运行转换后的脚本。整体过程为先转换脚本,再进行训练。
报告里的三样东西,每一样都有用,而且都能在输出目录里找到对应的结果文件:
| 报告内容 | 它能回答什么问题 | 官方结果文件 |
|---|---|---|
| 脚本转换日志 | 转换过程有没有报错、改了哪些文件 | msFmkTranspltlog.txt |
| 不支持算子的列表 | ⭐ 哪些算子昇腾上没有——迁移后最容易出问题的地方 | unsupported_api.csv(另有 unknown_api.csv 存疑、cuda_op_list.csv) |
| 脚本修改记录 | 一共动了多少处、动了哪里(可审计) | change_list.csv |
这份报告是工具迁移最大的价值,也是它相对于自动迁移的不可替代之处: 自动迁移是「黑箱换掉」,工具迁移是「白箱改写」。
第 1 步:装依赖(官方原文)
命令 pip3 install pandas
含义:安装迁移工具依赖的四个 Python 包之一 拆解:
pip3(Python3 包管理器)+install(安装)+pandas(数据表格处理库,官方要求版本 ≥ 1.2.4) 用法:四条一起装(以 root 用户安装为例;非 root 用户每条都要加--user):pip3 install pandas(版本需 ≥ 1.2.4)/pip3 install libcst(Python 语法树解析器,用于解析 Python 文件)/pip3 install prettytable(把数据可视化为表格)/pip3 install jedi(跨文件解析,官方建议安装) 期望:四条都显示Successfully installed ...不过会怎样:报No matching distribution/ 连不上源 → 换内网源或走离线包(B4); 报Permission denied→ 加--user或用 root 装
第 2 步:进入迁移工具所在路径(官方原文)
命令 cd Ascend-cann-toolkit安装目录/cann/tools/ms_fmk_transplt/
含义:切到迁移工具本体所在的目录 拆解:
Ascend-cann-toolkit安装目录(装 Toolkit 时的路径:root 为/usr/local/Ascend,非 root 为${HOME}/Ascend,见 B1)+cann/tools/(CANN 自带的一批工具)+ms_fmk_transplt(迁移工具目录,名字含义 models framework transplant,模型框架移植) 用法:先用 CANN 运行用户source环境变量,再 cd 期望:prompt 变成该目录;ls能看到pytorch_gpu2npu.sh不过会怎样:报No such file or directory→ 路径写错,或 Toolkit 没装(回 B1)
第 3 步:执行脚本迁移任务——单卡(官方原文)
命令 ./pytorch_gpu2npu.sh -i /home/train/ -o /home/out -v 2.1.0
含义:把
/home/train/下的 GPU 脚本迁移为 NPU 脚本,结果写到/home/out拆解:-iinput(原始脚本所在文件夹路径,必选)+-ooutput(结果输出路径,必选)+-vversion(原脚本的 PyTorch 版本,必选;官方支持 2.1.0 / 2.6.0 / 2.7.1 / 2.8.0) 用法:单卡就这么敲;要高级特性才加可选的-s(官方提示:有可能导致原本脚本中分布式功能失效,非必要不加) 期望:命令跑完,输出路径下生成xxx_msft目录(xxx为原始脚本所在文件夹名),里面有msFmkTranspltlog.txt、unsupported_api.csv、change_list.csv等结果件 不过会怎样:报缺少依赖 → 回第 1 步补装pandas/libcst/prettytable/jedi; 报「输出路径不存在」→ 官方要求输出路径在运行前必须已存在; 报脚本含amp_C→ 先手动删除import amp_C相关代码(见"两条使用约束")
第 3 步(变体):单卡迁多卡(官方原文)
命令 ./pytorch_gpu2npu.sh -i /home/train/ -o /home/out -v 2.1.0 distributed -m /home/train/train.py
含义:把 GPU 单卡脚本迁移为 NPU 多卡脚本 拆解:
distributed(写在全场最后;仅支持用torch.utils.data.DataLoader加载数据的场景)+-mmain(训练脚本的入口 Python 文件,必选)+ 可选-ttarget_model(实例化模型变量名,默认model,变量名不叫 model 才写) 用法:/home/train/换成你的脚本目录、/home/train/train.py换成你的训练入口文件;变量名不是model时才补-t my_model期望:输出目录名变成xxx_msft_multi(⭐ 官方命名规则:不带distributed→xxx_msft;带distributed→xxx_msft_multi),并多出一个run_distributed_npu.sh不过会怎样:报-t参数无效 → 变量名不是model却没写-t; 报distributed不支持 → 数据加载不是DataLoader方式,退回单卡迁移
第 4 步(多卡才需要):跑多卡启动脚本(官方原文)
命令 bash run_distributed_npu.sh
含义:按迁移工具生成的脚本,在指定 NPU 上拉起多卡训练 拆解:
run_distributed_npu.sh里MASTER_ADDR/MASTER_PORT(通信地址/端口)、NPUS(用哪些卡)、RANK_SIZE(卡数)、DEVICE_ID/RANK_ID(设备号/逻辑号) 用法:先把脚本里的please input your shell script here替换成你原来的训练 shell 脚本(如bash model_train_script.sh --data_path data_path),再进入脚本所在目录执行 期望:执行后生成指定 NPU 的 log 日志(output_npu_<i>.log) 不过会怎样:多卡不失效的前提——官方:若原训练命令里含指定卡号的单卡参数(如--gpu),必须删除该参数; 忘了替换please input your shell script here→ 日志里只剩这行占位符、什么都没跑起来
工具迁移的两条使用约束(官方原文)
- 若用户训练脚本中包含 NPU 平台不支持的
amp_C模块,需要用户手动删除后再进行训练。- 由于转换后的脚本与原始脚本平台不一致,迁移后的脚本在调试运行过程中可能会由于算子差异等原因而抛出异常,导致进程终止, 该类异常需要用户根据异常信息进一步调试解决。
第二条要理解到位:工具迁移不负责保证迁移后一定跑通 ✗—— 它负责把能自动换的换掉并如实报告 ✓。剩下的要靠人调 ✓。
B · 手工迁移(前两条都不行才用它)
适用场景:前两种都不行的时候——比如脚本结构特殊、有大量自定义 CUDA 扩展、 或者你想完全掌控每一处改动。
官方给的迁移要点:
手工迁移:通过分析模型,对比 GPU 与 NPU 接口,手动对训练脚本进行修改,以支持在昇腾 AI 处理器上执行训练。迁移要点如下: - 定义 NPU 为训练设备,将训练脚本中适配 GPU 的接口切换至适配 NPU 的接口; - 多卡迁移需将通信方式修改为昇腾支持的 HCCL。
单卡迁移的三个必改点(《手工迁移》原文,含官方给的具体接口对照):
| 要改的点 | 迁移前(GPU) | 迁移后(NPU) |
|---|---|---|
| ① 导入 NPU 库 | — | import torch / import torch_npu |
| ② 指定训练设备(to(device) 方式) | device = torch.device('cuda:{}'.format(local_rank)) |
device = torch.device('npu:{}'.format(local_rank)) |
| ② 指定训练设备(set_device 方式) | torch.cuda.set_device(local_rank) |
torch_npu.npu.set_device(local_rank) |
| ③ 替换设备可用判断 | torch.cuda.is_available() |
torch_npu.npu.is_available() |
| ③ 替换模型上卡 | model.cuda(local_rank) |
model.npu(local_rank) |
| ③ 替换数据上卡 | images.cuda(local_rank, non_blocking=True) |
images.npu(local_rank, non_blocking=True) |
多卡迁移(分布式场景):除上面的单卡改动外,还要直接修改 init_process_group 的 backend 值(官方原文对照):
| 通信方式 | |
|---|---|
| 迁移前(GPU) | dist.init_process_group(backend='nccl', ...) |
| 迁移后(NPU) | dist.init_process_group(backend='hccl', ...) |
⭐ 手工迁移的必改点,一句话记住: ① 设备从 CUDA 换成 NPU(
cuda→npu);② 多卡通信从 NCCL 换成 HCCL(nccl→hccl)。 第二点尤其要注意——忘了改通信,单卡能跑、一上多卡就挂。
官方还提示:手工迁移需要用户对 AI 模型有迁移基础, 了解 GPU 与 NPU 接口设计的异同点以及各种迁移手段。
第七段 出问题怎么查
排障原则:先定"卡在哪一步",再看技术细节。 迁移阶段的问题,绝大多数能归到下面四类:
| 现象 | 大概是什么 | 先查什么 |
|---|---|---|
| 脚本连 import 都过不去 | 环境层:TorchNPU 没装好 / 版本不配套 | 回 C1(torch.npu.is_available())、B3(版本配套) |
| 报「找不到某个算子」 | 该算子在昇腾上没有,或算子库型号不对 | 看工具迁移报告里的 unsupported_api.csv(不支持算子列表);再回 B2(算子库) |
| 单卡能跑、多卡报通信错 | 通信方式还是 NCCL,或 --gpu 之类参数没删 |
改 HCCL(本课第六段);删单卡卡号参数 |
| 能跑完但结果和原平台不一致 | 精度问题——不是"跑不通",是"跑得不对" | 这是 F 层的下一课(精度调试),不是迁移本身的问题 |
⭐ 一个判断法:
报错发生在「导入/编译阶段」 → 环境问题(C/B 层)
报错发生在「跑起来之后」 → 算子、通信、精度问题(本层 + B2 + 下一课)
⭐ 按"选哪条路"排的排查顺序(迁移特有):
① 先确认脚本在原平台(GPU/CPU)能跑通 —— 跑不通,先解决脚本本身
② 自动迁移报错,分三类看:
· import 阶段就报 → 环境问题(C1 / B3)
· 报 torch.jit.script 冲突 → 正常,是官方行为:改用工具迁移
· 跑起来才报某 CUDA 接口 → 该 API 可能不支持(5.4),转工具迁移拿"存疑 API 列表"
③ 工具迁移后报异常 → 先看 unsupported_api.csv;再按异常信息逐条调
④ 多卡报错 → 查 HCCL / 删 --gpu / 先替换 run_distributed_npu.sh 里的占位符
第八段 常见报错清单
迁移阶段的报错,官方有一部分收录在《分析迁移工具》的 FAQ 里(如
Segmentation fault、引用库找不到、Muls算子不支持 int64、No supported Ops kernel and engine are found for [ReduceStdV2A])。 下表按现象给排查方向;具体报错原文请到官方案例库按现象检索:https://www.hiascend.com/document/caselibrary—— 按报错里的稳定特征词检索,不要按"模型迁移"检索。
| 报错 / 现象 | 大概属于 | 先查什么 |
|---|---|---|
ModuleNotFoundError: No module named 'torch_npu' |
环境 | TorchNPU 没装好 / 没 source 环境变量(回 C1) |
加了 transfer_to_npu 后报 torch.jit.script 相关冲突 |
官方行为 | 不是故障:自动迁移会屏蔽 torch.jit.script;必须用就用工具迁移 |
assert backend in ['gloo', 'nccl'] 断言失败 / if backend == 'nccl' 走错分支 |
约束二 | 自动迁移只换了接口没改字符串:手动把字符串 nccl 改成 hccl |
报 channel_last 不支持 |
约束一 | 官方建议:改用 contiguous 代替 |
报 amp_C 相关找不到 / 不支持 |
工具迁移约束 | 官方:手动删除 import amp_C 相关代码后再训练 |
含 APEX FusedAdam 优化器迁移失败 |
官方限制 | 官方:自动迁移和工具迁移都不支持 FusedAdam,需自行修改 |
工具迁移后跑出 No supported Ops kernel and engine are found for [...] |
算子 | 该算子不支持:看 unsupported_api.csv;到昇腾开源社区提 ISSUE 或按 5.4 适配 |
C++ 侧 Segmentation fault / 引用库找不到 |
运行 | 官方 FAQ 收录项:查《分析迁移工具》FAQ(引用库路径、LD_LIBRARY_PATH) |
| 单卡能跑、多卡报通信错 | 通信 | 通信方式还是 NCCL(改 HCCL);或 --gpu 单卡参数没删 |
| 能跑完但结果与参考平台不一致 | 精度 | 不是迁移问题:属 F2 精度调试 |
⭐ 检索技巧(E1 讲过,这里再用一次):去掉具体化信息。 不要搜"我们的训练脚本迁不过去",要搜报错原文里的稳定特征词(错误码、函数名、算子名、库名)。
第九段 小结与下一课
这一课要带走的三句话:
- 迁移前必须先在 GPU 或 CPU 上跑通——迁移是换设备,不是修 bug。
- 三种方式的判据是「什么时候转换」:自动=边跑边换,工具=先换再跑,手工=人自己换;官方推荐自动。
- 必须用
torch.jit.script时,自动迁移不能用——这是选方式的硬判据; 卡住了就换道:自动 → 工具 → 手工,别在一条路上死磕。
三种方式的选择顺序(串联本课):
先试自动迁移(一行代码)
├─ 报冲突 / 有 jit.script 需求 → 转工具迁移(要迁移报告、要不支持算子列表)
│ └─ 转换后仍报算子异常 → 按报告逐条调;还不行 → 手工迁移
└─ 多卡场景 → 记得 backend 字符串手改 hccl、删单卡卡号参数
下一课预告(F2): 迁移完能跑,只说明「跑得动」。下一课解决「跑得对不对」—— 精度调试:当结果与参考平台不一致时,怎么一步步定位到具体是哪一层的哪个算子。
附录 A · 本课引用的官方出处
| 内容 | 来源文档 |
|---|---|
| 三种迁移方式、官方推荐自动迁移、前置条件 CAUTION | 《迁移方式比较》(ModelZoo · 传统模型训练 · PyTorch · 模型迁移) |
| 自动迁移一行代码、三条使用约束、注意事项(部分 CUDA 接口报错→用 PyTorch Analyse 分析并提 ISSUE) | 《自动迁移》 |
工具迁移命令、参数表、输出目录命名(xxx_msft / xxx_msft_multi)、两条约束、多卡注意事项、结果文件清单(msFmkTranspltlog.txt / cuda_op_list.csv / unknown_api.csv / unsupported_api.csv / change_list.csv / run_distributed_npu.sh) |
《工具迁移》 |
手工迁移两个要点、CUDA→NPU 接口对照(cuda→npu、nccl→hccl)、单卡/多卡三改点 |
《手工迁移》 |
| 依赖包(pandas / libcst / prettytable / jedi)、支持版本(2.1.0 / 2.6.0 / 2.7.1 / 2.8.0)、约束(GPU 环境且 Python 3.7+ 跑通、APEX FusedAdam 不支持)、FAQ 篇目、ResNet50 快速入门示例 | 《分析迁移工具(msTransplant)用户指南》 |
本课未做的事(避免越界):
- 未给任何性能数字(迁移前后加速比等)——官方文档中未给出,故不编造。
- 未展开「不支持算子」的具体列表——该列表由迁移工具针对具体脚本实时生成(unsupported_api.csv),不存在通用清单。
- 手工迁移的具体接口名(torch_npu.npu.set_device、torch.device('npu:...') 等)已按官方《手工迁移》原文写出;
更多替换接口以官方《常见 PyTorch 迁移替换接口》与《TorchNPU 自定义 API 参考》为准。
附录 B · 命令与代码逐条详解
自动迁移的那一行代码
from torch_npu.contrib import transfer_to_npu
| 部分 | 含义 |
|---|---|
torch_npu |
昇腾的 PyTorch 插件包(C1 讲过:它是插件不是分支) |
contrib |
contribution,贡献/扩展模块 |
transfer_to_npu |
「把(CUDA 调用)转接到 NPU」——导入即生效 |
放置位置:
- PyTorch 2.4.0 及之前:放在 import torch_npu 之后。
- PyTorch 2.5.1 及之后:设备插件自动加载,无需再写 import torch_npu,直接加这一行。
注意:它是「导入即生效」,不需要调用任何函数——这是它叫「自动」的原因。
pip3 install 那四条依赖
| 包 | 干什么 | 官方给的版本要求 |
|---|---|---|
pandas |
数据表格处理 | ≥ 1.2.4 |
libcst |
语义分析库——迁移工具靠它解析并改写你的 Python 文件 | — |
prettytable |
把数据可视化输出成表格 | — |
jedi |
跨文件解析,官方建议安装 | — |
非 root 用户安装时每个
pip3 install都要加--user。
cd 到迁移工具目录
cd Ascend-cann-toolkit安装目录/cann/tools/ms_fmk_transplt/
| 部分 | 含义 |
|---|---|
Ascend-cann-toolkit安装目录 |
装 Toolkit 时的路径(root:/usr/local/Ascend;非 root:${HOME}/Ascend,见 B1) |
cann/tools/ |
CANN 自带的一批工具 |
ms_fmk_transplt |
迁移工具本体所在目录(名字含义:models framework transplant,模型框架移植) |
./pytorch_gpu2npu.sh |
该目录下的迁移脚本:PyTorch · GPU → NPU |
pytorch_gpu2npu.sh 的核心参数
| 参数 | 记法 |
|---|---|
-i input |
input,原始脚本在哪 |
-o output |
output,结果写到哪(运行前必须已存在) |
-v version |
version,原脚本的 PyTorch 版本(必填;官方支持 2.1.0 / 2.6.0 / 2.7.1 / 2.8.0) |
-s specify-device |
specify,指定 device;官方提示可能让原分布式功能失效,非必要不加 |
distributed |
写在全场最后;把单卡脚本迁成多卡脚本(仅支持 DataLoader 加载场景) |
-m main |
多卡时的入口文件(必填) |
-t target_model |
多卡时的模型变量名,默认 model;变量名不叫 model 才需要写 |
-h help |
显示帮助信息 |
输出目录怎么认
| 你跑的命令 | 输出目录名 |
|---|---|
不带 distributed |
<原文件夹名>_msft |
带 distributed |
<原文件夹名>_msft_multi |
run_distributed_npu.sh(多卡启动脚本,迁移工具生成)
| 变量 | 说明 |
|---|---|
MASTER_ADDR |
指定训练服务器的 IP |
MASTER_PORT |
指定训练服务器的端口 |
HCCL_WHITELIST_DISABLE |
HCCL 通信白名单校验 |
NPUS |
指定在特定 NPU 上运行 |
RANK_SIZE |
指定调用卡的数量 |
DEVICE_ID / RANK_ID |
指定调用的 device_id / 逻辑 ID |
执行前必须把脚本里的
please input your shell script here替换成你原来的训练 shell 脚本。
手工迁移的 CUDA → NPU 接口对照(官方《手工迁移》原文)
| GPU | NPU |
|---|---|
torch.cuda.set_device(local_rank) |
torch_npu.npu.set_device(local_rank) |
torch.device('cuda:{}'.format(local_rank)) |
torch.device('npu:{}'.format(local_rank)) |
torch.cuda.is_available() |
torch_npu.npu.is_available() |
model.cuda(local_rank) |
model.npu(local_rank) |
images.cuda(local_rank, non_blocking=True) |
images.npu(local_rank, non_blocking=True) |
dist.init_process_group(backend='nccl', ...) |
dist.init_process_group(backend='hccl', ...) |