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、算子支持

各个框架的算子支持情况请看:Snapdragon Neural Processing Engine SDK: Supported Network Layersicon-default.png?t=N7T8https://developer.qualcomm.com/sites/default/files/docs/snpe/network_layers.html

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中。

步骤如下:

  1. 计算输入数据的范围
  2. 计算编码的最小值最大值(固定0点,255点),步长(要求输入浮点数的0.0必须要能被映射到0~255上)
    • 如果是全是正数,则最小值给0,最大值不变
    • 如果全是负数,则最大值给0,最小值不变
    • 如果既有正也有负,那么计算步长,如果输入浮点的0点不能被映射那么将最小最大值同时向左或向右偏移几个步长
  3. 将输入浮点映射到0~255
  4. 输出量化值和编码参数

例如:

  • 输入:[-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只能输入量化

---------未完待续---------