MNN/docs/tools/convert.md

402 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 模型转换工具
模型转换工具能够将其他格式的模型ONNX, TFLITE, TorchScript, Tensorflow等转换为MNN模型以方便MNN模型在各种平台上部署。
- 从源码编译可参考[这里](../compile/other.html#id2)
- 从pip安装`pip install MNN`)使用可以参考[这里](python.html#mnnconvert)
## 参数说明
```bash
Usage:
MNNConvert [OPTION...]
-h, --help Convert Other Model Format To MNN Model
-v, --version 显示当前转换器版本
-f, --framework arg 需要进行转换的模型类型, ex: [TF,CAFFE,ONNX,TFLITE,MNN,TORCH, JSON]
--modelFile arg 需要进行转换的模型文件名, ex: *.pb,*caffemodel
--batch arg 如果模型时输入的batch是动态的可以指定转换后的batch数
--keepInputFormat 是否保持原始模型的输入格式,默认为:是;
--optimizeLevel arg 图优化级别默认为1
- 0 不执行图优化仅针对原始模型是MNN的情况
- 1 保证优化后针对任何输入正确;
- 2 保证优化后对于常见输入正确,部分输入可能出错;
--optimizePrefer arg 图优化选项默认为0
- 0正常优化
- 1优化后模型尽可能小
- 2优化后模型尽可能快
--prototxt arg caffe模型结构描述文件, ex: *.prototxt
--MNNModel arg 转换之后保存的MNN模型文件名, ex: *.mnn
--fp16 将conv/matmul/LSTM的float32参数保存为float16
模型将减小一半精度基本无损运行速度和float32模型一致
--bizCode arg MNN模型Flag, ex: MNN
--debug 使用debug模型显示更多转换信息
--forTraining 保存训练相关算子如BN/Dropoutdefault: false
--weightQuantBits arg arg=2~8此功能仅对conv/matmul/LSTM的float32权值进行量化
仅优化模型大小加载模型后会解码为float32量化位宽可选2~8
不开启动态量化的情况下运行速度和float32模型一致。8bit时精度基本无损模型大小减小4倍
default: 0即不进行权值量化
--weightQuantAsymmetric 与weightQuantBits结合使用决定是否用非对称量化默认为`true`
--compressionParamsFile arg
使用MNN模型压缩工具箱生成的模型压缩信息文件或根据用户提供的量化参数来生成对应的量化模型量化参数文件可参考tools/converter/user_provide_quant_params.json 。如果文件不存在且开启了weightQuantBits等量化功能会在相应路径生成模型压缩信息文件(json格式),可后续编辑
--saveStaticModel 固定输入形状,保存静态模型, default: false
--targetVersion arg 兼容旧的推理引擎版本例如1.2f
--customOpLibs arg 用户自定义Op库用于TorchScript模型中自定义算子的实现libmy_add.so
--info 当-f MNN时打印模型基本信息输入名、输入形状、输出名、模型版本等
--authCode arg 认证信息,指定模型的认证信息,可用于鉴权等逻辑
--inputConfigFile arg 保存静态模型所需要的配置文件, ex: ~/config.txt。文件格式为
input_names = input0,input1
input_dims = 1x3x224x224,1x3x64x64
--testdir arg 测试转换 MNN 之后MNN推理结果是否与原始模型一致。
arg 为测试数据的文件夹,生成方式参考 "正确性校验" 一节
--thredhold arg 当启用 --testdir 后,设置正确性校验的误差允可范围
若不设置,默认是 0.01
--JsonFile arg 当-f MNN并指定JsonFile时可以将MNN模型转换为Json文件
--alignDenormalizedValue arg
可选值:{0, 1} 默认为1, 当`float(|x| < 1.18e-38)`会被视为0
--detectSparseSpeedUp 检测权重是否使用稀疏化加速/压缩,有可能减少模型大小,但增大模型转换时间
--saveExternalData 将权重常量等数据存储在额外文件中默认为0也就是`false`
--useGeluApproximation 在进行Gelu算子合并时使用Gelu的近似算法默认为1 ,也就是`true`
--useOriginRNNImpl LSTM和GRU算子是否使用原始算子实现默认关闭。若开启性能可能提升但无法进行LSTM/GRU的量化
```
**说明1: 选项weightQuantBits使用方式为 --weightQuantBits numBitsnumBits可选2~8此功能仅对conv/matmul/LSTM的float32权值进行量化仅优化模型大小加载模型后会解码为float32量化位宽可选2~8运行速度和float32模型一致。经内部测试8bit时精度基本无损模型大小减小4倍。default: 0即不进行权值量化。**
**说明2如果使用Interpreter-Session C++接口开发因为NC4HW4便于与ImageProcess结合可以考虑在转换模型时使用自动内存布局`--keepInputFormat=0`**
## 其他模型转换到MNN
### TensorFlow to MNN
```bash
./MNNConvert -f TF --modelFile XXX.pb --MNNModel XXX.mnn --bizCode biz
```
注意:`*.pb`必须是frozen model不能使用saved_model
### TensorFlow Lite to MNN
```bash
./MNNConvert -f TFLITE --modelFile XXX.tflite --MNNModel XXX.mnn --bizCode biz
```
### Caffe to MNN
```bash
./MNNConvert -f CAFFE --modelFile XXX.caffemodel --prototxt XXX.prototxt --MNNModel XXX.mnn --bizCode biz
```
### ONNX to MNN
```bash
./MNNConvert -f ONNX --modelFile XXX.onnx --MNNModel XXX.mnn --bizCode biz
```
### TorchScript to MNN
```shell
./MNNConvert -f TORCH --modelFile XXX.pt --MNNModel XXX.mnn --bizCode biz
```
注意TorchScript模型要求使用torch.jit导出的模型**不要直接使用Pytorch的权重文件作为模型转换**;导出模型的代码如下:
```python
import torch
# ...
# model is exported model
model.eval()
# trace
model_trace = torch.jit.trace(model, torch.rand(1, 3, 1200, 1200))
model_trace.save('model_trace.pt')
# script
model_script = torch.jit.script(model)
model_script.save('model_script.pt')
```
### MNN to Json
想了解MNN模型的具体结构输入输出信息时可以将模型转换为Json文件并查找相关信息获取。
```bash
./MNNConvert -f MNN --modelFile XXX.mnn --JsonFile XXX.json
```
### Json to MNN
可以通过将MNN模型转换为Json文件对Json文件进行编辑修改然后在转换为MNN模型达到对模型修改微调的目的。
```bash
./MNNConvert -f JSON --modelFile XXX.json --MNNModel XXX.mnn
```
## 正确性校验
为了便于开发者排查问题,对于 PB / Tflite / Onnx MNN 提供了正确性校验工具(位于 tools/scripts 目录),以检查 MNN 推理结果是否与 原始模型一致。
相关脚本为:
- testMNNFromTf.py :适用 pb
- testMNNFromTflite.py :适用 tflite
- testMNNFromOnnx.py :适用 onnx
- testMNNFromTorch.py :适用 pt (torchscript)
注意:
- 如果模型是动态输入形状MNN 在脚本中默认不固定部分为1有可能在 Tensorflow / OnnxRuntime / Torch 验证阶段报错。此时需要修改脚本中对应的输入部分,比如 testMNNFromOnnx.py 中的 run_onnx(self) 函数,把输入替换为有效的输入形状和内容。
- 对于由Torchscript转换的模型一般都需要自行修改`testMNNFromTorch.py`中的的输入信息来测试。
- 如果模型输出层是 Identity 产生的,会因为 MNN 图优化的缘故丢失,此时需要校验上一层的输出,即在脚本后接输出名来测试,如: python3 ../tools/scripts/testMNNFromTf.py XXX.pb $NAME$
### 前置
- 测试 pb / tflite :安装`tensorflow`(`pip install tensorflow`
- 测试 onnx : 安装`onnxruntime`(`pip install onnxruntime`
- 测试 torchscript安装`torch`(`pip install torch`)
- 【可选】MNN模型转换工具编译完成编译完成产生`MNNConvert`可执行文件)
### 使用
- 使用在MNN的`build`目录下(包含`MNNConvert`)运行`python3 testMNNFromTf.py SRC.pb`Onnx为`python3 testMNNFromOnnx.py SRC.onnx`Tflite 类似),若最终结果为`TEST_SUCCESS`则表示 MNN 的模型转换与运行结果正确
- 若路径下面没有编译好的 MNNConvert 可执行文件,脚本会使用 pymnn 去进行校验
- 由于 MNN 图优化会去除 Identity ,有可能出现 find var error ,这个时候可以打开原始模型文件,找到 identity 之前的一层(假设为 LAYER_NAME )校验,示例:`python3 ../tools/script/testMNNFromTF.py SRC.pb LAYER_NAME`
- 完整实例如下以onnx为例
- 成功执行,当结果中显示`TEST_SUCCESS`时,就表示模型转换与推理没有错误
```bash
cd build
cmake -DMNN_BUILD_CONVERTER=ON .. && make -j4
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx # 模型转换后推理并与ONNXRuntime结果对比
Dir exist
onnx/test.onnx
tensor(float)
['output']
inputs:
input
onnx/
outputs:
onnx/output.txt (1, 1000)
onnx/
Test onnx
Start to Convert Other Model Format To MNN Model...
[21:09:40] /Users/wangzhaode/copy/AliNNPrivate/tools/converter/source/onnx/onnxConverter.cpp:40: ONNX Model ir version: 6
Start to Optimize the MNN Net...
108 op name is empty or dup, set to Const108
109 op name is empty or dup, set to BinaryOp109
110 op name is empty or dup, set to Unsqueeze110
112 op name is empty or dup, set to Unsqueeze112
97 op name is empty or dup, set to Unsqueeze97
98 op name is empty or dup, set to Const98
inputTensors : [ input, ]
outputTensors: [ output, ]
Converted Success!
input
output: output
output: (1, 1000, )
TEST_SUCCESS
```
- 默认只支持限定数值范围的输入随机生成,如需修改,请自己修改脚本
### 出错及解决
- 出现 Test Error 或者 MNN 的 crash 可直接反馈(提 github issue 或者钉钉群反馈)
- 如需自查testMNNFromOnnx.py 提供 debug 功能,可方便定位出错的 layer / op ,示例:
- python3 testMNNFromOnnx.py SRC.onnx DEBUG
- 示例以ONNX为例
- 假设存在错误此处为实验将MNN的Binary_ADD实现修改为错误实现执行上述测试脚本效果如下显示`TESTERROR`表明可以转换但是推理结果有错误:
```bash
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx
Dir exist
onnx/test.onnx
tensor(float)
['output']
inputs:
input
onnx/
outputs:
onnx/output.txt (1, 1000)
onnx/
Test onnx
Start to Convert Other Model Format To MNN Model...
[21:43:57] /Users/wangzhaode/copy/AliNNPrivate/tools/converter/source/onnx/onnxConverter.cpp:40: ONNX Model ir version: 6
Start to Optimize the MNN Net...
108 op name is empty or dup, set to Const108
109 op name is empty or dup, set to BinaryOp109
110 op name is empty or dup, set to Unsqueeze110
112 op name is empty or dup, set to Unsqueeze112
97 op name is empty or dup, set to Unsqueeze97
98 op name is empty or dup, set to Const98
inputTensors : [ input, ]
outputTensors: [ output, ]
Converted Success!
input
output: output
output: (1, 1000, )
TESTERROR output value error : absMaxV:5.814904 - DiffMax 32.684010
Error for output output
Save mnn result to .error director
```
- 对于推理出错的情况,可以使用`可视化工具`查看模型结果,测试每一层的输出,直至发现错误层:
```bash
# test layer output 365: ERROR
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 365
...
365: (1, 32, 28, 28, )
TESTERROR 365 value error : absMaxV:3.305553 - DiffMax 5.069034
Error for output 365
Save mnn result to .error director
# binary search test layers ...
# test layer output 339: ERROR, 339's inputs is [489, 498]
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 339
...
output: 339
339: (1, 24, 56, 56, )
TESTERROR 339 value error : absMaxV:3.704849 - DiffMax 5.504766
Error for output 339
Save mnn result to .error director
# test layer output 489: SUCCESS
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 489
...
output: 489
489: (1, 24, 56, 56, )
TEST_SUCCESS
# test layer output 498: SUCCESS
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 498
...
output: 498
498: (1, 24, 56, 56, )
TEST_SUCCESS
# so bug is layer 339
```
- 对于ONNX的模型可以使用自动定位功能在模型后输入`DEBUG`,便会执行基于支配树的二分查找,直至找到错误层:
```bash
python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx DEBUG
...
Test Node : Conv_14 True
### First Error Node is : Add_15
```
## 算子支持列表
```bash
./MNNConvert -f CAFFE --OP
./MNNConvert -f TF --OP
./MNNConvert -f ONNX --OP
./MNNConvert -f TORCH --OP
```
## 模型打印
将MNN模型文件dump成可读的类json格式文件以方便对比原始模型参数同时也可以对模型进行修改。
可以使用`MNNConvert`或`MNNDump2Json`将模型转换成Json文件在对模型进行修改后还可以使用`MNNConvert`或`MNNRevert2Buffer`将Json文件转回MNN模型执行方式如下
```bash
./MNNDump2Json mobilenet_v1.mnn mobilenet_v1.json
# do some change in mobilenet_v1.json
./MNNRevert2Buffer mobilenet_v1.json mobilenet_v1_new.mnn
cat mobilenet_v1.json
{ "oplists":
[
{ "type": "Input", "name": "data", "outputIndexes":
[ 0 ]
, "main_type": "Input", "main":
{ "dims":
[ 1, 3, 224, 224 ]
, "dtype": "DT_FLOAT", "dformat": "NC4HW4" }
, "defaultDimentionFormat": "NHWC" }
,
{ "type": "Convolution", "name": "conv1", "inputIndexes":
[ 0 ]
, "outputIndexes":
[ 1 ]
, "main_type": "Convolution2D", "main":
{ "common":
{ "dilateX": 1, "dilateY": 1, "strideX": 2, "strideY": 2, "kernelX": 3, "kernelY": 3, "padX": 1, "padY": 1, "group": 1, "outputCount": 32, "relu": true, "padMode": "CAFFE", "relu6": false, "inputCount": 0 }
, weight:
[ -0.0, -0.0, 0.0, -0.0, ... ]
, bias:
[ -0.000004, 0.694553, 0.416608, ... ]
}
, "defaultDimentionFormat": "NHWC" }
,
...
]
, "tensorName":
[ "data", "conv1", "conv2_1/dw", "conv2_1/sep", "conv2_2/dw", "conv2_2/sep", "conv3_1/dw", "conv3_1/sep", "conv3_2/dw", "conv3_2/sep", "conv4_1/dw", "conv4_1/sep", "conv4_2/dw", "conv4_2/sep", "conv5_1/dw", "conv5_1/sep", "conv5_2/dw", "conv5_2/sep", "conv5_3/dw", "conv5_3/sep", "conv5_4/dw", "conv5_4/sep", "conv5_5/dw", "conv5_5/sep", "conv5_6/dw", "conv5_6/sep", "conv6/dw", "conv6/sep", "pool6", "fc7", "prob" ]
, "sourceType": "CAFFE", "bizCode": "AliNNTest", "tensorNumber": 0, "preferForwardType": "CPU" }
```
## Python版
我们提供了预编译的MNNConvert Python工具[mnnconvert](python.html#mnnconvert)
## MNN2QNNModel
### 功能
该工具针对特定的高通硬件架构为原始的MNN模型生成MNN-QNN后端需要的替代模型以及QNN离线产物。目前支持静态形状的模型以及有限输入形状组合的模型。
### 运行环境要求
该工具必须在 x86_64 架构的 Linux 系统上运行部分QNN SDK中的离线工具必须在此环境中运行
### 编译
添加额外的CMAKE变量并编译`-DMNN_QNN=ON -DMNN_QNN_CONVERT_MODE=ON -DMNN_WITH_PLUGIN=OFF -DMNN_BUILD_TOOLS=ON -DMNN_SUPPORT_TRANSFORMER_FUSE=ON`。
### 用法说明
该工具的用法如下
```
./MNN2QNNModel <qnnSDKPath> <socId> <hexagonArch> <srcMNNPath> <outputDir> [totalShapeNum] [inputShape1] [inputShape2] ...
```
参数配置说明如下:
| 参数 | 说明 | 是否必须 |
| :--- | :--- | :--- |
| `<qnnSDKPath>` | QNN SDK 的根目录路径。 | 是 |
| `<socId>` | 目标 SoC 的 ID。常用 ID 参考8Gen2 -> `43`, 8Gen3 -> `57`, 8 Elite -> `69`。其他型号请参考高通官方文档。 | 是 |
| `<hexagonArch>` | Hexagon架构版本。常用架构参考8Gen2 -> `73`, 8Gen3 -> `75`, 8 Elite -> `79`。其他型号请参考高通官方文档。 | 是 |
| `<srcMNNPath>` | 待转换的原始 MNN 模型文件路径(`.mnn` 文件)。 | 是 |
| `<outputDir>` | 用于存放生成产物的目录。工具会在此目录下生成一个新的 `.mnn` 文件(替代模型)和一个 `.bin` 文件QNN离线产物。 | 是 |
| `[totalShapeNum]` | 需要支持的动态输入形状的总数量。 | 否 |
| `[inputShapeN]` | 具体的输入形状配置。根据 `totalShapeNum` 的数量,提供相应个数的形状描述。形状信息可以是以下两种格式之一:<br>1. **形状字符串**:例如 `1x3x512x512`。对于多输入模型,用下划线 `_` 分隔,例如 `1x3x512x512_1x256`<br>2. **MNN 文件路径**:提供一个包含所需输入信息的 `.mnn` 文件路径。 | 否 |
#### 示例
假设 QNN SDK 路径为 /path/to/qnn/sdk目标设备为 8Gen3 (socId=57, hexagonArch=75),原始模型为 model.mnn输出目录为 /path/to/output
- 使用默认输入形状进行转换
```
./MNN2QNNModel /path/to/qnn/sdk 57 75 model.mnn /path/to/output
```
- 为单输入模型指定单种输入形状进行转换
```
./MNN2QNNModel /path/to/qnn/sdk 57 75 model.mnn /path/to/output 1 1x3x256x256
```
- 为单输入模型指定多种输入形状进行转换
```
./MNN2QNNModel /path/to/qnn/sdk 57 75 model.mnn /path/to/output 2 1x3x256x256 1x3x512x512
```
- 为多输入模型指定多种输入形状进行转换
```
./MNN2QNNModel /path/to/qnn/sdk 57 75 model.mnn /path/to/output 2 1x3x256x256_1x100 1x3x512x512_1x200
```
#### 产物
工具执行成功后,会在指定的 `<outputDir>` 目录下生成两个文件。文件名由原始模型名、SoC ID 和 Hexagon 架构版本共同决定,格式为 `<原始模型名>_<socId>_<hexagonArch>.<suffix>`
- **替代模型**:一个 `.mnn` 文件。文件名格式为`<原始模型名>_<socId>_<hexagonArch>.mnn`。
- **QNN离线产物**:一个 `.bin` 文件QNN离线产物包含了优化后的模型和权重。文件名格式为`<原始模型名>_<socId>_<hexagonArch>.bin`。
例如,对于上述示例(原始模型为 `model.mnn`socId=57hexagonArch=75产物将位于 `/path/to/output/` 目录下:
```
/path/to/output/
├── model_57_75.mnn # 替代模型
└── model_57_75.bin # QNN离线产物
```
关于如何使用这些产物,可进一步参考[QNN离线构图模式的使用说明](../inference/npu.md#离线构图模式推理常规模型)。
## compilefornpu
对于较复杂的模型通过compilefornpu及对应的`npu_convert.py`分段转换为NPU该工具目前仅在llm相关模型的转换中使用