[Pytorch] PyTorch模型迁移常见问题FAQ

# Ascend PyTorch模型迁移常见问题FAQ ## 1 介绍     本文目标读者为Ascend模型迁移开发者,用于指导开发者在昇腾版本的 PyTorch + Apex 下,实现模型训练精度性能达标。这里仅列举模型迁移中遇到的常见问题与解决方法,持续更新。 ## 2 常见问题FAQ ### FAQ1在模型运行或者算子运行时遇到报错,“RuntimeError: ExchangeDevice:” * 现象描述 ![faq1.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164228eatjeem8aztizsju.png) * 原因分析     目前在一个线程内,只能调用一个npu设备,当切换不同的npu device时,会出现上述错误。 * 处理方法     检查代码中在调用torch.npu.set_device(device)、tensor.to(device)或者model.to(device)时,同一个线程内前后调用时device名称是否一致。对于多个线程情况(如多卡训练),每个线程也是只能调用一个npu device,不能切换。 ### FAQ2、在模型运行遇到报错,“terminate called after throwing an instance of 'c10::Error' what():  HelpACLExecute:” * 现象描述 ![faq2.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164235fd6jl3jfxzqpcq9z.png) * 原因分析     目前HelpACLExecute的报错信息是无法直接找到报错位置的,这里是在task任务下发时报错,由于开启TASK多线程下发(export TASK_QUEUE_ENABLE=1),报错信息被上层封装起来,无法获取更加详细的报错日志。 * 处理方法     遇到这种情况处理方式有两种:一是查看host日志,可以查看具体的报错日志信息。日志默认位置是/var/log/npu/slog/host-0/下,可能会有多个日志文件,日志文件以host-0为前缀,根据时间标识查找对应的日志文件。打开日志文件,搜索“ERROR”,查询具体的报错信息。二是关闭多线程下发(export TASK_QUEUE_ENABLE=0),然后再次运行代码,一般可根据终端报错信息知道错误原因。 ### FAQ3、在模型调测遇到报错,“RuntimeError: malloc:/..../pytorch/c10/npu/NPUCachingAllocator.cpp:293 NPU error, error code is 500000.” * 现象描述 ![faq3.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164243xvw1kiq33btyh7ns.png) * 原因分析     对于NPUCachingAllocator中malloc类型的错误,一般是npu显存不足,所需显存小于npu上可用显存,导致报错。 * 处理方法     在模型调测中,可用通过减小batch size参数,来减少NPU显存的分配,解决该问题。 ### FAQ4、在模型调测遇到报错,RuntimeError: Could not run 'aten::trunc.out' with arguments from the 'NPUTensorId' backend.  * 现象描述 ![faq4.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164303byzxfgkfn6mj2y5j.png) * 原因分析     目前npu设备仅支持pytorch部分算子,对于不支持的算子在使用时均会报上述错误,算子正在不断开发中。[查看NPU设备已经支持的算子](https://support.huaweicloud.com/opl-pytorch/atlasptol_09_0001.html), 持续更新。 * 处理方法     在模型调测中,可用通过减小batch size参数,来减少NPU显存的分配,解决该问题。 ### FAQ5、在模型调测中,遇到某个算子报错的情况,如下分别为MaxPoolGradWithArgmaxV1算子和max算子报错。 ![faq5.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164308sd0rzifhdejwqggo.png) ![faq6.png](https://bbs-img-cbc-cn.obs.cn-north-1.myhuaweicloud.com/data/attachment/forum/202011/10/164313ylahzapztj3vha3n.png) * 原因分析     在模型搭建中,算子输入参数是多样的。某些算子在特定参数下,计算报错或者不支持,根据报错信息可以定位到具体算子。 * 处理方法     根据报错信息定位到具体算子,解决步骤如下:     1) 排查模型中对该算子的调用方式和参数正确;     2) 根据报错算子构建单算子用例,构建报错场景;     3) 一般算子错误无法在python侧解决,构建出报错场景。在论坛中发帖附上报错场景,求助华为工程师即可。     注:输入参数shape 和 dtype需要重点关注,一般是导致算子报错的主要原因。             前述图中,根据报错信息,定位到是MaxPoolGradWithArgmaxV1算子和max算子报错。MaxPoolGradWithArgmaxV1是在反向计算过程中报错,那么构建测试用例时需要构建对应的反向场景;     而对于max算子,是正向计算时报错,构建正向场景即可。在模型中遇到算子报错,首选是仅构建单算子测试用例,确定报错场景和原因即可;若无法在单算子中构建单算子用例,则需要构建基于上下文的单算子场景, 可以参考第三部分编写用例。         # 3 测试用例构建 在模型中遇到问题时,使用整网复现问题成本较大,可以构建测试用例来复现问题,便于定位解决。构建测试用例一般有两种方式,一是单算子测试用例,直接调用该算子即可复现错误场景;二是基于上下文的单算子测试用例,一般用于与上下文场景有关的错误。  ## 3.1 单算子测试用例构建   以上述的错误场景为例,已经确定是max算子错误,构建如下场景:   ```python     import torch     import copy     from torch.testing._internal.common_utils import TestCase, run_tests      class TestMax(TestCase):             def cpu_op_exec(self, input1):             # 调用算子              output = torch.max(input1)             output = output.numpy()             return output         def npu_op_exec(self, input1):             # 调用对应npu算子              output = torch.max(input1)             return output         def test_max(self):             input = torch.randn(10,20))             input = input.to(torch.int64)   # 数据dtype转换             input_cpu = copy.deepcopy(input)             input_npu = copy.deepcopy(input).npu()             output_cpu = self.cpu_op_exec(input_cpu)             output_npu = self.npu_op_exec(input_npu)                          # 比较cpu和npu的计算结果,prec为允许误差             self.assertEqual(output_cpu, output_npu, prec = 1e-4)      if __name__ == '__main__':         run_tests()          # 运行上述代码,发现与原错误报错信息相同,说明单算子测试用例构建成功。     # 可进一步定位,注释数据dtype转换代码,发现测试用例无报错,可以说明在输入参数为torch.int64时,max算子在npu上运行报错。   ``` ## 3.1 基于上下文的单算子测试用例构建     ```python         # 基于上下文的单算子测试用例构建,但有时候不仅仅为一个操作,而是带有上下文的场景,还有时候是一个带参数Module,Module的方式是更通用的方法。此处构建一个包含conv2d的Module。         import torch         import copy         from torch.testing._internal.common_utils import TestCase, run_tests          class Model(nn.Module):             def __init__(self, in_channels=1, hooks=False):                 super(Model, self).__init__()                 self.conv = nn.Conv2d(in_channels, in_channels*2, kernel_size=64)                 if hooks:                     self.conv.weight.register_hook(lambda grad: print(grad))             def forward(self, x):                 out = self.conv(x)                 return out         class TestConv2d(TestCase):              def test_conv2d(self):                 model = Model(in_channels=16)                 # 若需要获取反向计算结果,则加入hooks获取反向即可                 # model = Model(in_channels=16, hooks=True)                 # 创建输入tensor                 input_tensor = torch.randn(4,16,64,64)                 input_tensor_cpu= copy.deepcopy(input_tensor)                 out = model(input_tensor_cpu)                 loss = out.sum()                 loss.backward()                 cpuout = out                 # 3 to NPU 运行,将model和input_tensor放到NPU运行                 torch.npu.set_device("npu:0") # 一般先set_device设定运行卡                 model_npu = Model(in_channels=16).npu()                 input_tensor_npu= copy.deepcopy(input_tensor).npu()                 out = model_npu(input_tensor_npu)                 loss = out.sum()                 loss.backward()                 npuout = out                 #根据结果,确定是否为报错场景                 self.assertEqual(cpuout, npuout, prec = 1e-4)     ```