当前部分算子接口已经进行了NPU亲和优化,并提升了性能。将算子替换为高性能的NPU亲和自定义算子,可以优化性能。本调优案例以已经适配NPU的EfficientNet模型为例,介绍此性能调优方法。
将已经进行了NPU亲和优化的算子替换为NPU自定义算子。NPU自定义算子信息可参考《支持Caffe&TensorFlow&ONNX&PyTorch算子清单》中对应PyTorch版本的“NPU自定义算子”章节。
点击获取链接,获取EfficientNet模型,并根据readme准备训练环境和数据集。
cd examples/imagenet vi main.py
bash ./test/train_performance_1p.sh --data_path=/data/xxx/ # 请根据实际情况更改data_path
以训练一个epoch为例,耗时如下图。
可以发现在使用Swish函数时,引入了小算子,影响了性能。
昇腾PyTorch目前以支持NPU自定义算子torch_npu.npu_silu(),可以替换原模型代码中的Swish函数,提升性能。
cd efficientnet_pytorch vi utils.py
import torch_npu
# A memory-efficient implementation of Swish function class SwishImplementation(torch.autograd.Function): @staticmethod def forward(ctx, i): result = i * torch.sigmoid(i) ctx.save_for_backward(i) return result @staticmethod def backward(ctx, grad_output): i = ctx.saved_tensors[0] sigmoid_i = torch.sigmoid(i) return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i))) class MemoryEfficientSwish(nn.Module): def forward(self, x): return SwishImplementation.apply(x)
检视代码,可以发现模型代码是在MemoryEfficientSwish函数中调用SwishImplementation函数来使用Swish,且Silu是Swish的一种特例。
Swish函数公式为
SiLU函数公式为
因此将class MemoryEfficientSwish中的调用方式修改为torch_npu.npu_silu()即可。
修改后代码如下:
class MemoryEfficientSwish(nn.Module): def forward(self, x): return torch_npu.npu_silu(x)
bash ./test/train_performance_1p.sh --data_path=/data/xxx/ # 请根据实际情况更改data_path
以训练一个epoch为例,耗时如下图。
对比图1与图2中调优前的性能数据,可以发现TIME列耗时减少且每秒处理的图片数从1502.6提升到1767.2,模型性能提升。