
# GPU迁移到NPU：手写数字识别
#### 操作场景
在AI大模型训练与推理中，昇腾NPU作为国产算力平台，提供了与NVIDIA GPU对标的计算能力。对于已有基于GPU的PyTorch代码，开发者往往关心迁移成本与适配难度。torch_npu作为昇腾PyTorch适配包，使得标准PyTorch代码仅需极少量改动即可在NPU上运行，大幅降低迁移门槛。本教程适用于已有基于GPU的PyTorch训练代码、需迁移至昇腾NPU环境的开发者。
本教程以MNIST手写数字识别为例，详细介绍如何将GPU上的PyTorch训练代码迁移至昇腾NPU，通过对比GPU与NPU代码的差异，帮助开发者快速掌握迁移方法，并在ModelArts平台上完成训练与推理验证。
#### 相关概念
| 概念                                             | 说明                                                               |
|:---|:---|
| 昇腾NPU                                          | 华为自研的AI处理器，提供与NVIDIA GPU对标的计算能力，搭载ARM架构系统，支持大模型训练与推理。            |
| CANN（Compute Architecture for Neural Networks） | 昇腾计算架构，是昇腾NPU的基础软件平台，类似于NVIDIA的CUDA，提供算子库、运行时和编译优化等能力。           |
| torch_npu                                      | 昇腾PyTorch适配包，在PyTorch框架与CANN之间建立适配层，使标准PyTorch代码仅需少量改动即可在NPU上运行。 |
| CUDA（Compute Unified Device Architecture）      | NVIDIA的并行计算平台和编程模型，GPU训练代码中常用的cuda相关API（如torch.cuda）均基于此。        |
   
#### 迁移原理
torch_npu的工作原理是在PyTorch框架和昇腾CANN软件栈之间建立适配层，将PyTorch的CUDA调用自动转换为昇腾NPU的对应操作。因此，开发者无需重写模型逻辑，仅需替换设备相关的API调用即可完成迁移。
典型迁移场景：当您的业务需要从NVIDIA GPU集群迁移到昇腾NPU集群时（如国产化替代、成本优化等），可参照本案例的迁移方法，将现有PyTorch训练/推理代码快速适配至NPU环境。
表1GPU与NPU API对照表 
| **功能** | **GPU**                   | **NPU**                  |
|:---|:---|:---|
| 设备可用性  | torch.cuda.is_available() | torch.npu.is_available() |
| 张量移至设备 | .cuda()                   | .npu()                   |
| 设备指定   | torch.device("cuda")      | torch.device("npu")      |
| 加载模型映射 | map_location="cuda:0"     | map_location="npu:0"     |
| 设备监控   | nvidia-smi                | npu-smi info             |
   
**期望效果**：分别在GPU和NPU环境下完成MNIST手写数字识别模型的训练，对比迁移前后代码差异，验证NPU训练精度与GPU基本对齐。训练完成后将分别生成mnist_gpu.pt和mnist_npu.pt模型权重文件。
#### 前提条件
- 已完成华为云账号注册、实名认证及相关权限授权。具体操作，请参见[一、前置准备：账号与权限](https://support.huaweicloud.com/qs-modelarts/modelarts_06_0006.html#section0)。
- Notebook实例的状态为运行中，且满足如下要求。如需创建新的Notebook实例，请参见[创建Notebook实例](https://support.huaweicloud.com/devtool-modelarts/devtool-modelarts_3495.html)。
  实例规格、镜像和磁盘规格的详细说明如下表：
  
  | 参数   | NPU环境                                                                         | GPU环境                                                                  | 说明              |
  |:---|:---|:---|:---|
  | 实例规格 | 推荐使用单卡规格（Ascend: 1\*ascend snt9b1）或更高配置。                                      | 推荐使用单机1卡规格或更高配置。                                                       | GPU为可选项，用于交叉验证。 |
  | 镜像   | 选择ModelArts预置镜像pytorch_2.7.1-cann_8.3.rc1-py_3.11-hce_2.0.2509-aarch64-snt9b。 | 选择ModelArts预置镜像pytorch_1.12.1-cuda_10.2-py_3.9.11-ubuntu_20.04-x86_64。 | 需选择对应架构的镜像。     |
  | 磁盘规格 | 建议5GiB以上。                                                                     | 建议5GiB以上。                                                              | 用于存储数据集和模型权重。   |
     
  
 
#### 计费说明
在ModelArts开发环境中运行Notebook实例时，会使用计算资源和存储资源，产生计算资源和存储资源的累计值计费。计费详情请参见[开发环境计费项](https://support.huaweicloud.com/price-modelarts/price-modelarts-0010.html)。
#### 注意事项
迁移前请务必备份原始GPU代码和模型权重文件。架构差异（注意事项1）会导致X86镜像在NPU上完全不可用，请确认镜像架构匹配后再创建实例，避免资源浪费。
| 注意事项     | 说明                                                  | 影响程度 |
|:---|:---|:---|
| 架构差异     | 昇腾NPU搭载ARM系统，X86上构建的Docker镜像无法直接使用，需删除重新构建aarch64镜像 | 高    |
| import顺序 | import torch_npu必须在import torch之后、使用NPU功能之前导入       | 高    |
| 算子兼容     | 少数自定义CUDA算子需使用Ascend C重写，标准PyTorch算子一般无需修改          | 中    |
| 模型权重通用   | torch.save保存的权重文件在GPU和NPU间通用，仅map_location需对应调整     | 低    |
   
#### 案例核心
本案例的核心是"环境验证 → 代码迁移 → 训练验证 → 推理对比"的闭环流程，能够清晰、直观地掌握PyTorch代码从GPU到NPU的迁移全貌。
**迁移核心：仅需3处改动（参见[步骤三]）。**
- 第1处，在import torch后添加import torch_npu。
- 第2处，将设备标识torch.cuda替换为torch.npu。
- 第3处，将设备标识cuda替换为npu，网络模型定义、训练逻辑、评估逻辑等代码无需任何修改。
**权重通用**：PyTorch保存的模型权重文件（.pt）在GPU和NPU间通用，仅需调整map_location参数即可跨设备加载。
**监控对照**：NPU使用npu-smi info替代GPU的nvidia-smi，其中AICore(%)对应NPU利用率，GPU-Util对应GPU利用率，HBM-Usage(MB)对应显存使用量。
#### 步骤一：打开Notebook实例
1. 登录[ModelArts管理控制台](https://console.huaweicloud.com/modelarts/)，在左侧导航栏按需选择以下操作。
   - 新版控制台：选择"模型开发与训练 \> Notebook"，进入"Notebook"页面。
   
   - 旧版控制台：选择"开发空间 \> Notebook"，进入"Notebook"页面。
    
2. 在"Notebook"页面的"操作"列，单击"接入环境"，在"接入方式"对话框的"WebIDE"页签，单击"JupyterLab 接入"右侧的"接入"。
3. 进入JupyterLab页面后，自动打开ModelArts Launcher页面。在"ModelArts Launcher"页面的"Notebook"区域，单击PyTorch，创建一个新的PyTorch Notebook。
   图1打开PyTorch   
   ![](https://support.huaweicloud.com/bestpractice-modelarts/figure/zh-cn_image_0000002688580957.png "点击放大") 
 
#### 步骤二：环境验证
在开始迁移实践前，先验证当前运行环境是否可用。确认设备类型和计算能力正常后，再进行后续的代码迁移。
1. 设备检测。
   执行以下命令，自动检测当前运行环境是GPU、NPU还是CPU。
   ```
   import torch
   if torch.cuda.is_available():
       device = torch.device("cuda")
       print(f"当前环境：GPU - {torch.cuda.get_device_name(0)}")
   elif hasattr(torch, 'npu') and torch.npu.is_available():
       import torch_npu
       device = torch.device("npu")
       print(f"当前环境：NPU - {torch.npu.get_device_name(0)}")
   else:
       device = torch.device("cpu")
       print("当前环境：CPU")
   print(f"设备标识：{device}")
   ```
   运行后应输出当前设备类型及设备标识，输出示例如下。如果输出"当前环境：CPU"，请检查镜像选择是否正确或资源规格是否分配成功。
   - NPU输出示例：
     ```
     当前环境：NPU - xxx
     设备标识：npu
     ```
     
   
   - GPU输出示例：
     ```
     当前环境：GPU - xxx
     设备标识：cuda
     ```
     
    
2. 矩阵运算验证。 通过矩阵乘法验证设备计算能力。以下两个单元格分别对应GPU和NPU环境，根据当前运行环境执行。
   - **GPU矩阵运算验证：**
     ```
     import torch
     try:
         _has_gpu = torch.cuda.is_available()
     except Exception:
         _has_gpu = False
     if _has_gpu:
         print("===== GPU 矩阵运算 =====")
         x = torch.randn(10000, 10000).cuda()
         y = torch.randn(10000, 10000).cuda()
         z = x.mm(y)
         print(f"结果形状：{z.shape}")
         print(f"设备位置：{z.device}")
         print("GPU矩阵运算验证通过！")
     else:
         print("当前环境无GPU，跳过GPU矩阵运算验证")
     ```
     预期输出：
     ```
     ===== GPU 矩阵运算 =====
     结果形状：torch.Size([10000, 10000])
     设备位置：cuda:0
     GPU矩阵运算验证通过！
     ```
     
   
   
   
   - **NPU矩阵运算验证：**
     ```
     import torch
     try:
         import torch_npu
         _has_npu = torch.npu.is_available()
     except Exception:
         _has_npu = False
     if _has_npu:
         print("===== NPU 矩阵运算 =====")
         x = torch.randn(10000, 10000).npu()
         y = torch.randn(10000, 10000).npu()
         z = x.mm(y)
         print(f"结果形状：{z.shape}")
         print(f"设备位置：{z.device}")
         print("NPU矩阵运算验证通过！")
     else:
         print("当前环境无NPU，跳过NPU矩阵运算验证")
     ```
     预期输出：
     ```
     ===== NPU 矩阵运算 =====
     结果形状：torch.Size([10000, 10000])
     设备位置：npu:0
     NPU矩阵运算验证通过！
     ```
     
   
   
   **迁移要点：**NPU版本仅增加了import torch_npu，并将.cuda() 替换为.npu()，其余代码完全一致。
   **验证方法：**如果设备可用，应输出"矩阵运算验证通过！"及结果形状torch.Size(\[10000, 10000\])。
   
 
#### 步骤三：MNIST手写数字识别
本步骤完成MNIST手写数字识别的完整训练流程，包括：网络模型定义、数据准备与加载、训练与评估函数定义、GPU/NPU训练。其中仅训练部分的代码存在GPU与NPU差异，其余代码在两种环境下完全一致。
1. 网络模型定义。 网络模型定义与设备无关，GPU和NPU无需任何差异。本案例使用LeNet-5变体卷积神经网络，适用于28×28灰度图像分类。网络包含2个卷积层（Conv2d）、2个Dropout层和2个全连接层（Linear），最终输出10类概率分布。
   ```
   import torch
   import torch.nn as nn
   import torch.nn.functional as F
   class Net(nn.Module):
       def __init__(self):
           super(Net, self).__init__()
           self.conv1 = nn.Conv2d(1, 32, 3, 1)
           self.conv2 = nn.Conv2d(32, 64, 3, 1)
           self.dropout1 = nn.Dropout(0.25)
           self.dropout2 = nn.Dropout(0.5)
           self.fc1 = nn.Linear(9216, 128)
           self.fc2 = nn.Linear(128, 10)
       def forward(self, x):
           x = self.conv1(x)
           x = F.relu(x)
           x = self.conv2(x)
           x = F.relu(x)
           x = F.max_pool2d(x, 2)
           x = self.dropout1(x)
           x = torch.flatten(x, 1)
           x = self.fc1(x)
           x = F.relu(x)
           x = self.dropout2(x)
           x = self.fc2(x)
           output = F.log_softmax(x, dim=1)
           return output
   print("网络模型定义完成")
   ```
   预期输出：
   ```
   网络模型定义完成
   ```
   
2. 数据准备。
   下载MNIST数据集并构建数据加载器，该部分代码在GPU和NPU环境下完全一致。MNIST数据集提供以下两种下载方式，请根据网络环境选择：
   
   | 下载方式       | 适用场景        | 说明              |
   |:---|:---|:---|
   | 方式一：直接下载   | 网络可访问GitHub | 从GitHub下载原始数据文件 |
   | 方式二：OBS桶下载 | 网络受限环境      | 从华为云OBS桶下载已打包数据 |
      
   - **下载方式一：直接下载**
     ```
     # 普通方式
     !mkdir -p ./data/MNIST/raw
     !wget https://raw.githubusercontent.com/leo-mao/MNIST_data/master/train-images-idx3-ubyte.gz -O ./data/MNIST/raw/train-images-idx3-ubyte.gz
     !wget https://raw.githubusercontent.com/leo-mao/MNIST_data/master/train-labels-idx1-ubyte.gz -O ./data/MNIST/raw/train-labels-idx1-ubyte.gz
     !wget https://raw.githubusercontent.com/leo-mao/MNIST_data/master/t10k-images-idx3-ubyte.gz -O ./data/MNIST/raw/t10k-images-idx3-ubyte.gz
     !wget https://raw.githubusercontent.com/leo-mao/MNIST_data/master/t10k-labels-idx1-ubyte.gz -O ./data/MNIST/raw/t10k-labels-idx1-ubyte.gz
     ```
     ```
     import os
     import gzip
     from torchvision import datasets, transforms
     os.makedirs('./data/MNIST/raw', exist_ok=True)
     # 解压 .gz
     for f in os.listdir('./data/MNIST/raw/'):
         if f.endswith('.gz'):
             with gzip.open(f'./data/MNIST/raw/{f}', 'rb') as gz:
                 with open(f'./data/MNIST/raw/{f[:-3]}', 'wb') as out:
                     out.write(gz.read())
     ```
     
   
   - **下载方式二：OBS桶下载**
     ```
     # obs桶方式
     !pip install download
     %env no_proxy='a.test.com,127.0.0.1,2.2.2.2'
     from download import download
     # 下载MNIST数据集
     url = "https://eduhicomputing.obs.cn-south-1.myhuaweicloud.com:443/%20ComputerVision2/MNIST_Data.zip"
     path = download(url, "./data/MNIST/raw", kind="zip", replace=True)
     !mv ./data/MNIST/raw/MNIST_Data/test/* ./data/MNIST/raw/MNIST_Data/train/* ./data/MNIST/raw/ && rm -rf ./data/MNIST/raw/MNIST_Data
     ```
     
     
3. 加载数据集。使用torchvision加载MNIST数据集，创建训练集和测试集的DataLoader。
   ```
   from torchvision import datasets, transforms
   transform = transforms.Compose([
       transforms.ToTensor(),
       transforms.Normalize((0.1307,), (0.3081,))
   ])
   dataset1 = datasets.MNIST('./data', train=True, download=False, transform=transform)
   dataset2 = datasets.MNIST('./data', train=False, download=False, transform=transform)
   print(f"训练集大小：{len(dataset1)}")
   print(f"测试集大小：{len(dataset2)}")
   ```
   预期输出：
   ```
   训练集大小：60000
   测试集大小：10000
   ```
   如果数据加载失败，请检查数据文件是否下载完整。
   
4. 训练与评估函数。定义训练函数train和评估函数test，通过传入的device参数自动适配GPU或NPU，函数逻辑无需任何修改。 训练函数在每个batch中将数据和标签移至指定设备，评估函数统计损失和准确率。两个函数均通过device参数适配不同设备，无需修改。
   ```
   def train(model, device, train_loader, optimizer, epoch):
       """训练模型，返回平均损失与准确率"""
       model.train()
       total_loss = 0
       correct = 0
       total = 0
       for batch_idx, (data, target) in enumerate(train_loader):
           data, target = data.to(device), target.to(device)
           optimizer.zero_grad()
           output = model(data)
           loss = F.nll_loss(output, target)
           loss.backward()
           optimizer.step()
           total_loss += loss.item()
           pred = output.argmax(dim=1, keepdim=True)
           correct += pred.eq(target.view_as(pred)).sum().item()
           total += len(data)
       avg_loss = total_loss / len(train_loader)
       accuracy = 100. * correct / total
       print(f'Train Epoch: {epoch} | Loss: {avg_loss:.4f} | Accuracy: {correct}/{total} ({accuracy:.0f}%)')
       return avg_loss, accuracy
   def test(model, device, test_loader):
       """评估模型，返回平均损失与准确率"""
       model.eval()
       test_loss = 0
       correct = 0
       with torch.no_grad():
           for data, target in test_loader:
               data, target = data.to(device), target.to(device)
               output = model(data)
               test_loss += F.nll_loss(output, target, reduction='sum').item()
               pred = output.argmax(dim=1, keepdim=True)
               correct += pred.eq(target.view_as(pred)).sum().item()
       test_loss /= len(test_loader.dataset)
       accuracy = 100. * correct / len(test_loader.dataset)
       print(f'Test  | Average loss: {test_loss:.4f} | Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.0f}%)')
       return test_loss, accuracy
   print("训练与评估函数定义完成")
   ```
   预期输出：
   ```
   训练与评估函数定义完成
   ```
   
5. GPU/NPU训练。分别使用GPU和NPU进行模型训练，对比迁移前后代码差异。训练3个Epoch后保存模型权重文件。
   - **GPU训练** ：以下代码在GPU实例上运行。
     ```
     import torch
     import torch.optim as optim
     from torch.optim.lr_scheduler import StepLR
     try:
         import torch.cuda 
         cuda_available = torch.cuda.is_available()
     except (ImportError, AttributeError):
         cuda_available = False
     # ====== GPU训练 ======
     if cuda_available:
         device = torch.device("cuda")
         print(f"使用设备：{device} - {torch.cuda.get_device_name(0)}")
         train_kwargs = {'batch_size': 64, 'shuffle': True}
         test_kwargs = {'batch_size': 1000, 'shuffle': False}
         train_loader = torch.utils.data.DataLoader(dataset1, **train_kwargs)
         test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs)
         model = Net().to(device)
         optimizer = optim.Adadelta(model.parameters(), lr=1.0)
         scheduler = StepLR(optimizer, step_size=1, gamma=0.7)
         for epoch in range(1, 4):
             train(model, device, train_loader, optimizer, epoch)
             test(model, device, test_loader)
             scheduler.step()
         torch.save(model.state_dict(), "mnist_gpu.pt")
         print("GPU训练完成，模型已保存为 mnist_gpu.pt")
     else:
         print("当前环境无GPU，跳过GPU训练")
     ```
     预期输出：
     ```
     使用设备：cuda - Tesla V100S-PCIE-32GB
     Train Epoch: 1 | Loss: 0.1968 | Accuracy: 56419/60000 (94%)
     Test  | Average loss: 0.0542 | Accuracy: 9826/10000 (98%)
     Train Epoch: 2 | Loss: 0.0759 | Accuracy: 58647/60000 (98%)
     Test  | Average loss: 0.0417 | Accuracy: 9867/10000 (99%)
     Train Epoch: 3 | Loss: 0.0580 | Accuracy: 59008/60000 (98%)
     Test  | Average loss: 0.0313 | Accuracy: 9891/10000 (99%)
     GPU训练完成，模型已保存为 mnist_gpu.pt
     ```
     验证方法：如使用GPU镜像资源，训练完成后应输出"GPU训练完成，模型已保存为mnist_gpu.pt"，3个Epoch的测试准确率应在97%-99%范围内。如果提示"当前环境无GPU"，请检查实例资源规格是否包含GPU。
     
   
   - **NPU训练** ：以下代码在NPU实例上运行。
     迁移要点：与[GPU训练]对比，仅有三处改动：
     1. 添加import torch_npu
     
     2. torch.cuda→torch.npu
     
     3. cuda→npu
     
     
     ```
     import torch
     import torch.optim as optim
     from torch.optim.lr_scheduler import StepLR
     try:
         import torch_npu  # 【改动1】导入NPU适配包
         npu_available = torch.npu.is_available()  # 【改动2】torch.cuda → torch.npu
     except (ImportError, AttributeError):
         npu_available = False
     # ====== NPU训练 ======
     if npu_available:
         device = torch.device("npu")   # 【改动3】cuda → npu
         print(f"使用设备：{device} - {torch.npu.get_device_name(0)}")
         train_kwargs = {'batch_size': 64, 'shuffle': True}
         test_kwargs = {'batch_size': 1000, 'shuffle': False}
         train_loader = torch.utils.data.DataLoader(dataset1, **train_kwargs)
         test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs)
         model = Net().to(device)
         optimizer = optim.Adadelta(model.parameters(), lr=1.0)
         scheduler = StepLR(optimizer, step_size=1, gamma=0.7)
         for epoch in range(1, 4):
             train(model, device, train_loader, optimizer, epoch)
             test(model, device, test_loader)
             scheduler.step()
         torch.save(model.state_dict(), "mnist_npu.pt")
         print("NPU训练完成，模型已保存为 mnist_npu.pt")
     else:
         print("当前环境无NPU，跳过NPU训练")
     ```
     预期输出：
     ```
     使用设备：npu - Ascend910B4
     Train Epoch: 1 | Loss: 0.2067 | Accuracy: 56290/60000 (94%)
     Test  | Average loss: 0.0442 | Accuracy: 9844/10000 (98%)
     Train Epoch: 2 | Loss: 0.0761 | Accuracy: 58734/60000 (98%)
     Test  | Average loss: 0.0344 | Accuracy: 9885/10000 (99%)
     Train Epoch: 3 | Loss: 0.0544 | Accuracy: 59053/60000 (98%)
     Test  | Average loss: 0.0319 | Accuracy: 9890/10000 (99%)
     NPU训练完成，模型已保存为 mnist_npu.pt
     ```
     验证方法：如使用NPU镜像规格，训练完成后应输出"NPU训练完成，模型已保存为mnist_npu.pt"，3个epoch的测试准确率应与GPU训练结果基本一致。如果提示"当前环境无NPU"，请检查镜像是否选择了CANN版本。
     
    
 
**核心Diff对比**
```
+ import torch_npu                    # 改动1：导入NPU适配包
- if torch.cuda.is_available():       # 改动2：设备检测
+ if torch.npu.is_available():
- device = torch.device("cuda")       # 改动3：设备指定
+ device = torch.device("npu")
```
#### 步骤四：模型推理与跨设备加载
本步骤使用训练好的模型进行推理验证，包括：加载模型权重文件、对测试集图片进行预测并可视化结果。PyTorch权重文件在GPU和NPU间通用，仅需调整map_location参数即可跨设备加载。
1. 加载训练好的模型权重文件进行推理。代码会自动检测当前设备类型，并加载对应的模型权重。 PyTorch保存的权重文件（.pt）在GPU和NPU间通用，仅需调整map_location参数。
   迁移要点：本案例模型权重文件通用。NPU场景仅map_location需从"cuda:0"改为"npu:0"。除此之外，网络定义、训练逻辑、评估逻辑、数据加载等代码完全一致。
   ```
   import torch
   # 自动检测设备并加载对应模型
   if torch.cuda.is_available():
       device = torch.device("cuda")
       model_path = "mnist_gpu.pt"
       map_location = "cuda:0"  
   elif hasattr(torch, 'npu') and torch.npu.is_available():
       import torch_npu
       device = torch.device("npu")
       model_path = "mnist_npu.pt"
       map_location = "npu:0"  # NPU场景，map_location需从“cuda:0”改为“npu:0”
   else:
       device = torch.device("cpu")
       model_path = "mnist_gpu.pt"
       map_location = "cpu"
   model = Net().to(device)
   model.load_state_dict(torch.load(model_path, map_location=map_location))
   model.eval()
   print(f"模型加载完成，设备：{device}")
   ```
   预期输出：
   - NPU：
     ```
     模型加载完成，设备：npu
     ```
     
   
   - GPU：
     ```
     模型加载完成，设备：cuda
     ```
     
    
2. 从测试集中选取12张图片，使用加载的模型进行推理预测，展示预测结果与真实标签的对比。
   ```
   import matplotlib.pyplot as plt
   num = 12
   cols = 4
   rows = (num + cols - 1) // cols
   fig, axes = plt.subplots(rows, cols, figsize=(15, 6))
   model.eval()
   with torch.no_grad():
       for i in range(num):
           image, label = dataset2[i]
           output = model(image.unsqueeze(0).to(device))
           pred = output.argmax(dim=1).item()
           ax = axes[i // cols, i % cols]
           ax.imshow(image.squeeze(), cmap='gray')
           color = 'green' if pred == label else 'red'
           ax.set_title(f'L:{label} P:{pred}', color=color, fontsize=10)
           ax.axis('off')
   plt.tight_layout()
   plt.show()
   ```
   预期输出：
   ![](https://support.huaweicloud.com/bestpractice-modelarts/figure/zh-cn_image_0000002688700777.png "点击放大")
   预测结果图中绿色标题表示预测正确，红色表示预测错误。
   
 
#### 步骤五：设备监控
训练和推理过程中，可通过设备监控命令查看NPU或GPU的算力利用率、显存使用量等，帮助判断训练是否正常运行。GPU使用nvidia-smi命令，NPU使用npu-smi info命令，两者的核心监控指标对照如下。
- **GPU监控**
  在GPU实例上运行以下命令nvidia-smi，查看GPU使用率、显存使用量等。
  ```
  import torch
  if torch.cuda.is_available():
      !nvidia-smi
  else:
      print("当前环境无GPU，跳过nvidia-smi")
  ```
  预期输出：
  ```
  Wed Jul 29 11:50:13 2026       
  +-----------------------------------------------------------------------------+
  | NVIDIA-SMI 470.57.02    Driver Version: 470.57.02    CUDA Version: 11.4     |
  |-------------------------------+----------------------+----------------------+
  | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
  | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
  |                               |                      |               MIG M. |
  |===============================+======================+======================|
  |   0  xxx...  On   | 00000000:00:0D.0 Off |                    0 |
  | N/A   35C    P0    37W / 250W |   2925MiB / 32510MiB |      0%      Default |
  |                               |                      |                  N/A |
  +-------------------------------+----------------------+----------------------+
  +-----------------------------------------------------------------------------+
  | Processes:                                                                  |
  |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
  |        ID   ID                                                   Usage      |
  |=============================================================================|
  +-----------------------------------------------------------------------------+
  ```
  
- **NPU监控**
  在NPU实例上运行以下命令npu-smi info，查看NPU使用率、显存使用量等。其中AICore(%) 是NPU的算力利用率，等同于GPU的GPU-Util；HBM-Usage(MB) 是显存使用量，等同于GPU的Memory-Usage。
  ```
  import torch
  if hasattr(torch, 'npu') and torch.npu.is_available():
      !npu-smi info
  else:
      print("当前环境无NPU，跳过npu-smi info")
  ```
  预期输出：
  ```
  +------------------------------------------------------------------------------------------------+
  | npu-smi 25.5.1                   Version: 25.5.1                                               |
  +---------------------------+---------------+----------------------------------------------------+
  | NPU   Name                | Health        | Power(W)    Temp(C)           Hugepages-Usage(page)|
  | Chip                      | Bus-Id        | AICore(%)   Memory-Usage(MB)  HBM-Usage(MB)        |
  +===========================+===============+====================================================+
  | 0     xxx               | OK            | 87.5        42                0    / 0             |
  | 0                         | 0000:C1:00.0  | 0           0    / 0          4822 / 32768         |
  +===========================+===============+====================================================+
  +---------------------------+---------------+----------------------------------------------------+
  | NPU     Chip              | Process id    | Process name             | Process memory(MB)      |
  +===========================+===============+====================================================+
  | 0       0                 | 3180          | python                   | 1999                    |
  +===========================+===============+====================================================+
  ```
  
 
表2GPU与NPU监控指标对照 
| 指标    | GPU (nvidia-smi) | NPU (npu-smi info) |
|:---|:---|:---|
| 算力利用率 | GPU-Util         | AICore(%)          |
| 显存使用  | Memory-Usage     | HBM-Usage(MB)      |
| 设备温度  | Temp             | Temp(C)            |
| 功耗    | Pwr              | Power(W)           |
   
**实时监控（持续刷新）**
如果需要持续监控，可执行以下命令。
- GPU：
  ```
  watch -n 1 nvidia-smi（每1秒刷新）
  ```
  
- NPU：
  ```
  watch -n 1 npu-smi info（每1秒刷新）
  ```
  
#### 常见问题
- 运行NPU训练代码时提示当前环境无NPU，如何排查？ 请依次检查：
  1. Notebook实例的资源规格是否选择了NPU类型。
  
  2. 镜像是否选择了包含CANN的aarch64架构镜像。
  
  3. 实例是否已成功启动并分配到NPU资源。
   
- GPU训练保存的模型权重能否直接在NPU上加载？ 可以。PyTorch权重文件与设备无关，仅需调整map_location参数即可。部分场景需先将权重通过map_location=cpu加载到CPU，再转换至NPU。
  
更多迁移问题排查，请参见[GPU业务迁移至昇腾训练推理](https://support.huaweicloud.com/bestpractice-modelarts/modelarts_10_0039.html)。
#### 相关文档
| 文档名称                                                                                                    | 说明                          |
|:---|:---|
| [GPU业务迁移至昇腾训练推理](https://support.huaweicloud.com/bestpractice-modelarts/modelarts_10_0039.html)         | 完整的GPU到NPU迁移指南，涵盖更多场景和进阶配置。 |
| [torch_npu官方文档](https://gitee.com/ascend/pytorch)                                                       | 昇腾PyTorch适配包的API参考和使用说明。    |
| [ModelArts Notebook使用指南](https://support.huaweicloud.com/devtool-modelarts/devtool-modelarts_0002.html) | Notebook实例的创建、配置和管理方法。      |
| [ModelArts计费说明](https://support.huaweicloud.com/price-modelarts/price-modelarts-0010.html)              | 各资源规格的计费标准和费用估算。            |
   
