文章介绍
Qualcomm Neural Processing SDK (以下简称SNPE)支持Caffe、ONNX、PyTorch和TensorFlow等不同ML框架的算子。对于某些特定的不支持的算子,我们介绍一种算子等效替换的方法来完成模型转换。本案例来源于https://github.com/quic/qidk/tree/master/Model-Enablement/Model-Conversion-Layer-Replacement
以PyTorch MobilenetV3模型转换SNPE DLC的过程为例子,介绍如何使用等效算子替换的方法达成不支持算子的转换
前置条件
- SNPE-2.7.x-在PC上下载并设置好SNPE
相关步骤参考Snapdragon Neural Processing Engine SDK: SNPE Setup
- python version 3.6.x and 3.8.x
- Torch version 1.10
- 安装pip install jupyter
- 一台Linux机器
- 高通Snapdragon 安卓手机,推荐Snapdragon® 8 Gen 2系列手机
特别注意:Qualcomm Neural Processing SDK需要python 3.6版本,型号repo需要python 3.8版本。因此,建议使用两个不同的虚拟环境。
操作步骤:
一、获取模型
1. 运行以下命令获取模型:
cd generatedModels
wget https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth
2. 现在,我们将使用MobileNetV3的Pytorch模型来获得ONNX模型。我们通常建议使用这种方法从pytorch模型生成dlc,方法是先转换为onnx,然后再转换为dlc。
运行getModel.py生成ONNX文件:
python getModel.py
cd ..
3. 这将生成ONNX模型可以在generatedModels/ONNX/文件夹中找到。
4. 现在,我们可以将ONNX模型转换为DLC
二、模型转换错误信息
1. 注意:在继续操作之前,请确保generatedModels/ONNX/mobilenet_v3.ONNX文件存在。
2. 使用Qualcomm Neural Processing SDK检查模型到dlc的转换
snpe-onnx-to-dlc -i generatedModels/ONNX/mobilenet_v3.onnx -o generatedModels/ONNX/mobilenet_v3.dlc
3. 以上,我们可以看到“onnx_hardsigmoid”转换没有注册,这里将用我们自己的自定义harsigmoid实现来更改。
三、替换架构-Harsigmoid
1. 找到模型源文件。
<python3.8-packages>/torchvision/models/mobilenetv3.py。
2. 搜索Hardsigmoid的调用/实现。
3. 对该行进行注释,并使用新的CustomHardsigmoid调用添加新的代码行。
4. 正如我们所看到的,Hardsigmoid是从nn模块调用为nn.Hardsigmoid。因此,我们找到<python3.8_path>/site packages/torch/nn/dir来编写我们自己的CustomHardsigmoid实现。
5. Hardsigmoid是一个激活函数。因此,它位于nn/modules/activation.py文件中。
6. 在__all__变量中添加``CustomHardsigmoid```,即要定义的自定义函数的名称。
7. 在nn/modules/activation.py中实现您自己的CustomHardsigmoid。注意:SDK没有nn.Hardsigmoid的转换。因此,我们将以与SDK兼容的方式定义Hardsigmoid。
Hardsigmoid可以表示为- torch.clamp((input*0.167+0.5),0,1)
8. 参考如下CustomHardsigmoid的实现代码
https://github.com/quic/qidk/blob/master/Model-Enablement/Model-Conversion-Layer-Replacement/class/CustomHardsigmoid.py
class CustomHardsigmoid(Module):
__constants__ = ['inplace']
inplace: bool
def __init__(self, inplace : bool = False)->None:
super().__init__()
self.inplace = inplace
def forward(self, input: Tensor, inplace: bool = False) -> Tensor:
return torch.clamp((input*0.167+0.5), 0, 1)
9. 我们已经定义了自己的实现,现在我们将查找nn.Hardsigmoid的所有依赖项
10. 在同一torc/nn/modules/目录中,在__init__.py文件中添加CustomHarsigmoid调用。然后保存后继续操作
11. 到MobileNetV3目录,并使用新实现的Customhardsigmoid层转储模型。
python generatedModels/getModel.py
四、再次转换,分析错误
1. 将具有新实现的CustomHardsigmoid层的ONNX模型转换为dlc
snpe-onnx-to-dlc -i generatedModels/ONNX/mobilenet_v3.onnx -o generatedModels/ONNX/mobilenet_v3.dlc
Error :
2. 我们得到了一个新的框架错误,说“onnx_hadswish”转换没有注册,我们现在将来更改hardswish的实现。
五、替换架构-Hardswish
1. 找到模型源文件。
<python3.8-packages>/torchvision/models/mobilenetv3.py
2. 搜索Hardswish的调用/实现
Occurrence-1
Occurrence-2
Occurrence-3
Occurrence-4
3. 注释所有这些行,并使用新的CustomHardswish调用添加新的代码行。
4. 正如我们所看到的,Hardswish是从nn模块调用为nn.Hardswish的,因此,我们将到<python3.8_path>/site packages/torch/nn/目录来编写我们自己的CustomHardswish实现。
5. Hardswish是一个激活函数。因此,它位于nn/modules/activation.py文件中。
6. 在__all__变量中添加``CustomHardswish```,即要定义的自定义函数的名称。
7. 在nn/modules/activation.py中实现您自己的CustomHardswish。注意:SDK没有nn.Hardswish的转换。因此,我们将以与SDK兼容的方式定义Hardsigmoid。
Hardswish可以定义为一个简化的表达式- input*torch.clamp(input+3, min=0, max=6)/6
8. 参考CustomHardswish的实现代码
https://github.com/quic/qidk/blob/master/Model-Enablement/Model-Conversion-Layer-Replacement/class/CustomHardswish.py
class CustomHardswish(Module):
__constants__ = ['inplace']
inplace: bool
def __init__(self, inplace : bool = False) -> None:
super().__init__()
self.inplace = inplace
def forward(self, input: Tensor) -> Tensor:
return input*torch.clamp(input+3, min=0,max=6)/6
9. 我们已经定义了自己的实现,现在我们将查找nn.Hardswish的所有依赖项。
10. 在同一torch/nn/modules/目录中,在__init.py文件中添加CustomHarswish调用,保存修改,继续下一步操作
11. 到MobileNetV3目录,并使用新实现的Customhardswish层转储模型。
python generatedModels/getModel.py
六、再次转换DLC
1. 将具有新实现的CustomHardswish以及CustomHardsigmoid层的ONNX模型转换为dlc
snpe-onnx-to-dlc -i generatedModels/ONNX/mobilenet_v3.onnx -o generatedModels/ONNX/mobilenet_v3.dlc
2. 现在,我们可以使用snpe-onx-to-dlc将ONNX模型转换为dlc。
最后我们完成了DLC模型的转换
作者:高通工程师,戴忠忠(Zhongzhong Dai)