SNPE教程三:模型转换、量化
一、模型转换
1、转换指令:
这里只讲解onnx转换,其他类似
onnx转到dlc(snpe的模型文件)需要用到一个工具就是snpe-onnx-to-dlc。
snpe-onnx-to-dlc
格式 | 意义 | 是否必要 |
---|---|---|
--i 输入的onnx模型 | 指定输入模型 | 必要 |
--output_path 输出dlc文件的路径 | 指定输出路径 | |
--disable_batchnorm_folding | 禁止将BN层融合到卷积中 | |
--input_type 输入名字 输入类型 (类型支持image, default, opaque) | 指定多个输入的输入类型 image就是输入均值是0.0f,最大值是255.0f那么就会将他银蛇到无符号八位 default就是浮点输入到dsp然后被量化 opaque就是如果输入时浮点特定层输入也要求浮点那就不量化 | |
--input_encoding 输入名字 指定的编码格式 | 可以指定多个层级的输入的编码格式(包括(bgr, rgb, rgba, argb32, nv21, time_series, other) | |
--validation_target 运行环境 运行设备 | 在指定的运行环境和运行设备上验证 运行环境:cpu,gpu, dsp 运行设备:snapdragon_801, snapdragon_820, snapdragon_835 如果不指定会在所有设备上验证 | |
--strict | 会严格在指定设备验证 | |
--dry_run DRY_RUN | 评估没有经过任何算子转化的模型并返回不支持的算子 DRY_RUN = {‘info’, 'debug'} debug可以看到更多信息 | |
--udo_config_paths 自定义算子路径 | 指定自定义算子路径 |
例子:snpe-onnx-to-dlc --input_network models/bvlc_alexnet/bvlc_alexnet/model.onnx --output_path bvlc_alexnet.dlc
2、算子支持
3、网络resize
不管是在模型转换中还是 在运行环境中都不支持可变的输入张量。但可以通过代码将网络重新resize
示例:
转换后的模型的输入是1,3,244,244
snpe-caffe2-to-dlc -p predict_net.pb -e init_net.pb -i data 1,3,244,244 -d model.dlc
#include <SNPE/SNPE.hpp>
auto container = zdl::DlContainer::IDlContainer::open("model.dlc");
//创建一个新的形状
zdl::DlSystem::TensorShapeMap inputShapeMap;
inputShapeMap.add("data", {3,1080,1440,3})
zdl::SNPE::SNPEBuilder builder(container.get());
//重新设置输入的shape
builder.setInputDimensions(inputShapeMap);
auto snpe = builder.build();
二、模型量化
1、量化算法
这里用到的量化算法其实也很简单就是把一堆浮点数完整的映射到0~255中。
步骤如下:
- 计算输入数据的范围
- 计算编码的最小值最大值(固定0点,255点),步长(要求输入浮点数的0.0必须要能被映射到0~255上)
- 如果是全是正数,则最小值给0,最大值不变
- 如果全是负数,则最大值给0,最小值不变
- 如果既有正也有负,那么计算步长,如果输入浮点的0点不能被映射那么将最小最大值同时向左或向右偏移几个步长
- 将输入浮点映射到0~255
- 输出量化值和编码参数
例如:
- 输入:[-1.8, -1.0, 0, 0.5]
- 最小值-1.8, 最大值0.5
- 计算步长 = (0.5 - -1.8)/ 255 = 0.009
- 浮点0点对应(0.0 - -1.8) / 2.3 x 255 = 199.59 (精确到0.01)
- 不能被完全映射所以将最小最大值同时向左偏移 0.43 x 0.009 = 0.00391
- 所以调整后的最小值为-1.8 - 0.391 = -1.80391, 最大值为0.5 - 0.00369 = 0.49609
- 输出[0, 89, 200, 255]
量化位宽一般默认为8位,但是偏移也可以设置为32位,32位偏移可能会带来一些精度提升
2、量化模式
- 默认量化模式:如上所属
- 增强量化模式:一般适用于长尾分布的数据(这里值的长尾分布是输入的值集中在某个小范围)。增强量化会使用最小范围并且遵从输入浮点0点能被完全映射原则
- 调整权重量化模式:只有在量化到8位时才能用,修改输入的最大最小值同样遵从最小范围和输入浮点0点能被完全映射原则
3、量化参数覆盖
量化参数也可以自己提供用来做量化,这些自己提供的参数会覆盖模型转化或用snpe工具量化产生的参数。参数文件是个json,格式如下
{
"activation_encodings": { #激活部分的量化参数
"Conv1:0": [ #指定要被覆盖参数的张量名
{
"bitwidth": 8, #指定位宽
"max": 12.82344407824954,
"min": 0.0,
"offset": 0, #输入浮点数0点对应到0~255上的值
"scale": 0.050288015993135454#步长
}
],
"input:0": [
{
"bitwidth": 8,
"max": 0.9960872825108046,
"min": -1.0039304197656937,
"offset": 127,
"scale": 0.007843206675594112
}
]
},
"param_encodings": { #权重或偏移的量化参数
"Conv2d/weights": [
{
"bitwidth": 8,
"max": 1.700559472933134,
"min": -2.1006477158567995,
"offset": 140,
"scale": 0.01490669485799974
}
]
}
}
4、量化要求
量化工具snpe-dlc-quantize要求指定模型输入批次为一。为了计算量化参数的范围需要一些具有代表性的数据集。一般5-10张就够了,如果要求稳定性,推荐50-100张但是不要用训练集里的数据
对于在AIP上运行的模型必须要量化成八位
5、量化指令
snpe-dlc-quantize
6、数据列表
数据列表txt格式如下
#<output_layer_name>[<space><output_layer_name>] //指定哪几层输出
%<output_tensor_name>[<space><output_tensor_name>]
路径..xxx.raw
路径..xxx.raw
路径..xxx.raw
路径..xxx.raw
路径..xxx.raw
路径..xxx.raw
路径..xxx.raw
.
.
.
7、输入数据预处理
输入数据形状: NCHW
8、不同运行环境
运行环境 | 量化 / 非量化 |
---|---|
CPU / GPU | 量化非量化都可以输入,但是只运行非量化。所以如果输入一个量化DLC,初始化时,模型会被反量化,增加初始化时间,精度也可能受影响。 |
DSP | 量化非量化都可以输入,但是只运行量化。所以如果输入一个非量化DLC,初始化时模型会被强制量化,增加初始化时间。 |
AIP | 只能输入量化 |
---------未完待续---------