## 手动适配指南 由于框架机制差异,目前MindTorch还存在部分接口或参数未对标,需要手动适配成支持的写法。该部分功能我们在不断优化中,如果在使用过程中遇到问题,欢迎[交流](https://openi.pcl.ac.cn/OpenI/MindTorch/issues)和[参与贡献](CONTRIBUTING_CN.md)。 ### 1. 数据处理部分 通常情况下仅需将数据处理相关导入模块转换为mindtorch相应模块,即可实现PyTorch数据部分的迁移,示例如下: ```python from mindtorch.tools import mstorch_enable from torch.utils.data import DataLoader from torchvision import datasets, transforms # 手动转换 # from mindtorch.torch.utils.data import DataLoader # from mindtorch.torchvision import datasets, transforms transform = transforms.Compose([transforms.Resize((224, 224), interpolation=InterpolationMode.BICUBIC), transforms.ToTensor(), transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.247, 0.2435, 0.2616]) ]) train_images = datasets.CIFAR10('./', train=True, download=True, transform=transform) train_data = DataLoader(train_images, batch_size=128, shuffle=True, num_workers=2, pin_memory=True) ``` **TorchVision接口支持**: MindTorch torchvision是迁移自PyTorch官方实现的计算机视觉工具库,延用PyTorch官方API设计与使用习惯,内部计算调用MindSpore算子,实现与torchvision原始库同等功能。可以在TorchVision接口支持列表[TorchVision_SupportedList.md]中查看接口支持情况。 另外,如果遇到数据处理接口未完全适配的场景,可以暂时使用PyTorch原生的数据处理流程,将生成的数据PyTorch张量转为MindTorch支持的张量对象,请参考[convert_tensor 工具使用教程](https://openi.pcl.ac.cn/OpenI/MSAdapter/src/branch/master/mindtorch/tools)实现。 ### 2. 模型构建部分 #### 2.1 自定义module ```python from mindtorch.torch.nn import Module, Linear, Flatten class MLP(Module): def __init__(self): super(MLP, self).__init__() self.flatten = Flatten() self.line1 = Linear(in_features=1024, out_features=64) self.line2 = Linear(in_features=64, out_features=128, bias=False) self.line3 = Linear(in_features=128, out_features=10) def forward(self, inputs): x = self.flatten(inputs) x = self.line1(x) x = self.line2(x) x = self.line3(x) return x ``` 自定义Module写法和PyTorch原生写法一致,但需要注意下述问题: 1. 自定义module时可能出现变量名已被使用场景,如`self.phase`,需要用户自行变更变量名; 2. 自定义反向传播函数差异,反向函数需要满足MindSpore自定义反向函数格式要求,以下是适配案例,混合精度等更多信息可以参考自定义反向章节内容。 ```python # PyTorch 写法 class GdnFunction(Function): @staticmethod def forward(ctx, x, gamma, beta): # save variables for backprop ctx.save_for_backward(x, gamma, beta) ... return y @staticmethod def backward(ctx, grad_output): x, gamma, beta = ctx.saved_variables ... return grad_input, grad_gamma, grad_beta # MindTorch 写法 class GdnFunction(nn.Module): def __init__(self): super(GdnFunction, self).__init__() def forward(self, x, gamma, beta): ... return y def bprop(self, x, gamma, beta, out, grad_output): x = torch.Tensor(x) gamma = torch.Tensor(gamma) beta = torch.Tensor(beta) grad_output = torch.Tensor(grad_output) ... return grad_input, grad_gamma, grad_beta ``` #### 2.2 inplace类接口适配 1. 暂时无法对标inplace相关操作,当前此类并**不真实共享内存**,所以`torch.xxx(*, out=output)`接口推荐写成`output = torch.xxx(*)`形式,`tensor_a.xxx_(*)`推荐写成`tensor_b = tensor_a.xxx(*)`形式,则该接口在图模式下也可正常执行; 2. 切片后的inplace算子不生效,需修改为如下写法: ```python # PyTorch 原生写法 boxes[i,:,0::4].clamp_(0, im_shape[i, 1]-1) # MindTorch 推荐写法 a = boxes[i,:,0::4].clamp_(0, im_shape[i, 1]-1) boxes[i, :, 0::4] = a ``` ### 3. 执行流程部分 #### 3.1 指定执行硬件 PyTorch原生接口通过`to`等接口将数据拷贝到指定硬件中执行,但是MindTorch暂不支持指定硬件执行,实际执行的硬件后端由conetxt指定。如果您的程序运行在云脑2,则默认执行昇腾硬件,如果想执行在其他硬件后端可以参考如下代码; ```python import mindspore as ms ms.set_context(device_target="CPU") # 指定CPU执行 ms.set_context(device_target="Ascend", device_id=1) # 指定昇腾1号卡执行, device_id为可选参数,默认0号卡执行 ``` 如果未设置`device_target`参数,则使用MindSpore包对应的后端设备。 #### 3.2 自动微分求导 1. 当调用`ms.ops.value_and_grad`接口时,如果`has_aux`为True,不允许存在多层嵌套的输出(**优化中**),且求导位置必须为第一个输出; 2. `torch.nn.utils.clip_grad_norm_` 可替换为 `ms.ops.clip_by_global_norm`等价实现梯度裁剪功能; ### 4 自定义操作 当内置接口和算子不满足使用需求时,可以利用框架提供的接口和机制实现自定义反向以及硬件算子。MindTorch基于MindSpore框架提供了不同于PyTorch的算子定义方式和接口。 #### 4.1 自定义反向 在部分场景中,不仅需要自定义神经网络层的正向逻辑,也需要手动控制其反向的计算。MindTorch使用`nn.Module.bprop`代替`torch.autograd.Function.backward`来实现自定义反向。 PyTorch写法: ```python from torch.cuda.amp import custom_fwd, custom_bwd class CustomNet(torch.autograd.Function): @staticmethod @custom_fwd def forward(ctx, a, b): ctx.save_for_backward(a, b) return a.mm(b) @staticmethod @custom_bwd def backward(ctx, grad): a, b = ctx.saved_tensors return grad.mm(b.t()), a.t().mm(grad) output = CustomNet.apply(input) ``` MindTorch写法: ```python import mindtorch.torch as torch class CustomNet(torch.nn.Module): def forward(self, a, b): return a.mm(b) def bprop(self, a, b, out, dout): return dout.mm(b.t()), a.t().mm(dout) custom_net = CustomNet() custom_net = torch.amp.auto_mixed_precision(custom_net) output = custom_net(input) ``` 上述两种写法的差异点如下: 1. MindTorch的正向和反向计算分别使用`forward`和`bprop`代替`forward`和`backward`,并且无需使用`staticmethod`。 2. MindTorch目前不支持通过`ctx`保存正向计算的中间值,而是在反向计算中根据输入重新获取和计算。 3. `bprop`方法有三类入参: - `a`,`b`:正向输入,当正向输入有多个时,需同样数量的入参; - `out`:正向输出; - `dout`:反向传播时,当前Module执行前的反向结果。 4. 调用方式由`CustomNet()`代替`CustomNet.apply`。 5. 混合精度场景下,使用`auto_mixed_precision`接口代替`custom_fwd`和`custom_bwd`。 #### 4.2 自定义算子 由于框架机制和硬件差异,开发者基于PyTorch自定义实现的C++/cuda算子需要手动迁移到MindSpore框架,详细使用教程请参考[MindSpore自定义算子](https://mindspore.cn/tutorials/experts/zh-CN/r2.1/operation/op_custom.html)描述。 ### 5. 其他 1. 网络中如果调用了MindSpore原生接口,则需要调用`mindtorch.torch.cast_to_adapter_tensor`接口将输出tensor转换为MindTorch tensor后方可继续调用PyTorch风格接口。除网络训练部分,不推荐混用MindTorch接口和MindSpore接口; 2. MindTorch tensor暂不支持格式化输出,如`label = f"{class_names[labels[i]]}: {probs[i]:.2f}"`,可先转换为numpy后输出; 3. 代码中调用`torch.autograd.Variable`接口,替换为`torch.tensor`即可; 4. 输出tensor如果要输入到opencv等其他组件进行处理时需要先转为numpy后再执行; 5. 三方库适配:如果代码中使用的三方库不依赖PyTorch,可正常使用无需适配。如果使用的的三方库是基于PyTorch接口开发的,则需要将三方库相关功能代码迁移到MindTorch。目前我们也正在适配一些常用的三方库,例如einops,具体使用方式可参考[third_party](https://openi.pcl.ac.cn/OpenI/MSAdapter/src/branch/master/third_party/einops)路径下内容。