Yuens' blog
骁龙(SnapDragon)神经处理引擎(SNPE)是一个针对高通骁龙加速深层神经网络的运行时软件,高通在CSDN和其官网都提供了下载。
本文以SNPE 1.23为基准,将结合高通官方的SDK说明文档(高通在CSDN也提供了开发者社区,中文社区论坛,以及SNPE部分文档),介绍SNPE这一高通官方的神经网络处理引擎开发包。
开发中有疑问可在高通的CreatePoint检索相关文档或者在SNPE论坛提交疑问,或发邮件到support.cdmatech@qti.qualcomm.com。
因为SNPE的特殊性,本文重点考察:
本文将以如下目录展开:
这部分的内容改写自百度百科的词条:骁龙。高通骁龙是Qualcomm Technologies(美国高通公司)的产品。
在2013年之前,骁龙处理器分为S1,S2,S3,S4四个层级,以区分不同的四代产品。
这一部分虽然只是罗列,但是能看出S1~S4的每一代都几乎都是基于ARM架构+自家DSP+GPU的异构平台。
后来的骁龙处理器,主要包括:800系列、700系列、600系列、400系列和200系列处理器。
800系列特点:
其它系列:
支持SNPE的骁龙处理器列表如下:
参照前面骁龙处理器系列,可以看出SNPE主要针对中高端机型中的800、600系列。
Snapdragon Device | CPU | GPU | DSP | AIP |
---|---|---|---|---|
Qualcomm Snapdragon 855 | Yes | Yes | Yes (CDSP) | Yes (CDSP) |
Qualcomm Snapdragon 845 | Yes | Yes | Yes (CDSP) | No |
Qualcomm Snapdragon 835 | Yes | Yes | Yes (ADSP) | No |
Qualcomm Snapdragon 821 | Yes | Yes | Yes (ADSP) | No |
Qualcomm Snapdragon 820 | Yes | Yes | Yes (ADSP) | No |
Qualcomm Snapdragon 710 | Yes | Yes | Yes (CDSP) | No |
Qualcomm Snapdragon 660 | Yes | Yes | Yes (CDSP) | No |
Qualcomm Snapdragon 652 | Yes | Yes | No | No |
Qualcomm Snapdragon 636 | Yes | Yes | No | No |
Qualcomm Snapdragon 630 | Yes | Yes | No | No |
Qualcomm Snapdragon 625 | Yes | Yes | No | No |
Qualcomm Snapdragon 605 | Yes | Yes | Yes (CDSP) | No |
Qualcomm Snapdragon 450 | Yes | Yes | No | No |
除了关注CPU和GPU外,比较引人注目的是DSP和AIP。其实AIP也是一种DSP,但根据表格中的值发现有CDSP和ADSP之分。只在官方文档中的支持设备表格与例子部分看到有CDSP,其它地方并没检索到更多信息,不过文档中说CDSP还是ADSP取决于设备:
This module runs on the ADSP or the CDSP, depending on the target. Please refer to the Snapdragon Device Support Matrix table in the Overview section to determine SNPE support of DSP on various Snapdragon devices. SNPE automatically detects the appropriate DSP.
但也在网上查到高通有2种DSP:
下图是骁龙800处理器,来自Hexagon hotchips2013的PPT,可以看到有aDSP与mDSP。
列表中CPU、GPU都比较熟悉,但是对后两个DSP和AIP不了解。根据和高通平台的手机产品中的Hexagon百度百科对词条Hexagon(高通骁龙处理器的DSP名称,DSP是数字信号处理器)的解释:
Hexagon Digital Signal Processor (DSP),高通的Hexagon计算DSP,作为高通研发的世界一流处理器,集成了CPU和DSP功能,能支持移动平台多媒体和modem功能的深度嵌入处理。拥有高级的可变指令长度、超长指令字、支持硬件多线程机制的处理器架构,Hexagon架构和核心家族给高通带来了modem和多媒体应用的性能和功耗优势,是高通骁龙处理器的关键组件。
自骁龙品牌建立之初就有了DSP,早期DSP仅用于语音和简单的音频视频解码播放,随着智能手机使用需求加大,包括摄像头和传感器功能的信号处理任务都可由DSP完成,DSP比CPU更擅长在低功耗下处理这些任务。
其基本特点有:
下图是几种典型的Hexagon DSP比较:
Hexagon Core Architecture | ||||
---|---|---|---|---|
Hexagon 400 | Hexagon 500 | Hexagon 600 | ||
Key Attributes | Fixed Point | Floating Point | Hexagon Vector eXtensions (HVX) | |
Snapdragon Chipsets(SDxxx) | Premium Tier | 801, 805, 808, 810 | 820, 821 | |
High Tier | 602A | 615, 161, 617, 625, 650, 652 | ||
Mid Tier | 410, 412, 415, 430 | |||
Low Tier | 208, 210, 212 | |||
SDK Version | AddOn_600 | SDK 2.0 | SDK 3.0 | |
Command Line Tools | 5.0, 5.1 | 5.0, 5.1, 6.2, 6.4 | 7.2, 7.3 | |
RTOS | QuRT | QuRT | QuRT |
上表分别是Hexagon400,5000和600系列,其中关键特性可以看到Hexagon 400、500、600系列分别支持定点、浮点、Hexagon Vector eXtension(HVX)。根据不同的系列也有不同的Snapdragon的芯片型号,分别对应旗舰、高端、中端和低端,其中Snapdragon 800系列都属于旗舰级别的芯片,占据了Hexagon 500和600系列的DSP。不同DSP系列也有对应的不同的SDK版本和命令行工具,支持的实时操作系统是QuRT(更多的信息我就没找到了)。
几乎每一年高通都在为性能和低能耗而改动架构和实现:
Qualcomm_HexagonDSP开发入门与提高概述描述了其架构设计核心与特性,下图是Hexagon 680 DSP版本的特性图:
其特性包括如下几个特点(可能有重复):
因而,我们也可以看出Hexagon架构设计核心:如何在低功耗的情况下能够高性能地处理各种各样的应用。
此外,需要特别说明的是因DSP具有不同于CPU和GPU架构,因而有两个关键特点:
以上是对高通Hexagon DSP的一个基本认识,基本概念的知识铺垫完了,下面继续讲解SNPE的相关内容。
以下SNPE的特点改写自官方文档:
使用SNPE部署模型的一般工作流程分为如下图中所描述的两个阶段:第一阶段由Caffe/Caffe2/TensorFlow完成模型训练;第二阶段将模型部署到高通设备上:
部署阶段前,先将模型用SNPE提供的工具,将模型转换为SNPE特有的DLC(Deep Learning Container)文件格式,转换好后就可在骁龙的CPU/GPU/DSP核心上执行推理了。具体而言包含以下步骤(或见上图):
上面那个图略显复杂,下面这张图的流程描述根据这4个步骤,更清楚一些:
以上便是SNPE的特点和工作流。
接下来介绍SNPE的运行时即Runtime,分两步骤介绍:Snapdragon NPE Runtime与AIP。前者是总览全局即4个Runtime,后者针对AIP这一特别的DSP着重介绍。
先说全局的Snapdragon NPE Runtime,其包含CPU、GPU和DSP(AIP也是DSP,他们的使用教程可参考官方文档Tutorials Setup章节),所以整体架构如下图,这也是对整个SNPE推理框架的概括性架构描述:
上图包括三个独立的硬件部分,分别是ARM CPU、Adreno GPU和Hexagon DSP:
灰色框的ARM CPU,其内的蓝色框就是Snapdragon NPE Runtime Library。该图从一个高层级来描述SNPE Runtime整个软件架构,其中除去最底层的Compute Runtimes外,还包含:
Adreno GPU
Hexagon DSP:DSP主要的应用就是AI,一篇针对高通855芯片(小米9采用)的深度解析文章:2019年安卓旗舰第一芯片 高通骁龙855强在哪?表示,Snapdragon 855内集成Hexagon 690。
本部分针对DSP介绍的较少,后面会更关注DSP开发和底层调度原理等相关背景知识。
AIP (AI Processor) Runtime是Q6、HVX和HTA三者在执行模型时候的一层软件抽象。
让模型在AIP上执行的前提条件:
SNPE在用AIP计算时,会通过加载DSP的动态库从而与AIP runtime通讯,但调度管理器我想应该是Q6,通过Q6去管理使用HTA和HNN,下图的结构描述可以看到Q6是Hexagon DSP上的一部分,Q6这部分应该都是软件层面。
模型调用AIP执行过程中,SNPE Runtime上的AIP runtime会调用DSP执行器(executor,位于DSP动态库中),DSP执行器来调用HTA和HVX来管理模型的执行:
不同部分在DLC量化过程中将相应的描述信息写到了量化后的DLC文件中,DSP executor遇到对应描述信息从而选择对应硬件驱动调用执行,见下图。
上图中,我的理解是分为ARM CPU和Hexagon DSP两部分,两部分又有各自软硬件层面,箭头是指调用关系。两部分中,最底层都是硬件上层是Runtime或者调度器,没查到Q6的信息,我想可能是固化到硬件层面的调度器类似driver,但根据SNPE文档描述Q6类似调度器(orchestrated by Q6),让Hexagon NN与HVX硬件、HTA Driver与HTA硬件打通。
因量化时可以选是否启用–hta_partitions以及对应的层数,若启用得到的模型会包含subnet。在执行过程中遇到subnet会被AIP runtime打断,根据subnet描述信息调用不同的硬件设备执行。
目前,AIP只支持单独的HTA subnet + 单独的HNN subnet。此外,Adding HTA sections小节中还提到其它限制:HTA和HNN subnets只支持层的单一输入与输出;HTA subnet需要从第一层开始。
为了清楚地描述模型执行流程,描述DLC中的模型组成如下图所示,圆圈表示模型中的计算操作,方块表示一层。
因而根据HTA、HNN subnets的分片,可将模型执行分为如下几种情况:
下面是SNPE在小米MIX2上3个Runtime上做的benchmark。
benchmark的4个模型有:AlexNet,GoogleNet,MobileNetV1/V2,SqueezeNetV1.1。一切参数根据原文作者的CaffeModel和Prototxt基于SNPE提供的转换或量化工具而来。
ms | cpu | fxp-cpu | gpu | gpu-fp16 | fxp-dsp |
---|---|---|---|---|---|
total inference time | 196-278 | 202-327 | 25 | 23 | 23 |
forward propagate time | 191-272 | 187-313 | 24 | 23 | 22 |
正确性验证 | ✔️ | ❌ | ✔️ | ✔️ | ✔️ |
ms | cpu | fxp-cpu | gpu | gpu-fp16 | fxp-dsp |
---|---|---|---|---|---|
total inference time | 418 | 222 | 31 | 29 | 22 |
forward propagate time | 412 | 212 | 31 | 28 | 22 |
正确性验证 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
ms | cpu | fxp-cpu | gpu | gpu-fp16 | fxp-dsp |
---|---|---|---|---|---|
total inference time | 909 | segfault | 12 | 13 | 15 |
forward propagate time | 902 | segfault | 12 | 13 | 14 |
正确性验证 | ❌ | segfault | ❌ | ❌ | ❌ |
ms | cpu | fxp-cpu | gpu | gpu-fp16 | fxp-dsp |
---|---|---|---|---|---|
total inference time | 1194 | 509 | 11 | 12 | 12 |
forward propagate time | 1187 | 495 | 11 | 12 | 11 |
正确性验证 | ❌ | ❌ | ❌ | ❌ | ❌ |
ms | cpu | fxp-cpu | gpu | gpu-fp16 | fxp-dsp |
---|---|---|---|---|---|
total inference time | 116 | 96 | 9 | 8 | 12 |
forward propagate time | 110 | 88 | 9 | 8 | 11 |
正确性验证 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
可以看出:
上面的benchmark数据中,mobilenet计算结果都是错的。在高通的SNPE论坛上发了关于MobileNet的int8分类精度问题,官方人员称目前的确有问题,反而设计出一种对量化友好的MobileNet的Seperable Convolution的方式:A Quantization-Friendly Separable Convolution for MobileNets,后面会讨论。
不过在此之前,我们先来看一下当前SNPE的量化方式,文档称SNPE采用的int8量化方式与TensorFlow一致(也正因如此,被坑了,mobilenet系列的分类网络,在int8量化计算结果上都不正确):
- Quantized DLC files use 8 bit fixed point representations of network parameters. The fixed point representation is the same used in Tensorflow quantized models.
此外,还提供了针对不同Runtime的量化情况表格:
Runtime | Quantized DLC | Non-Quantized DLC |
---|---|---|
CPU or GPU | Compatible. The model is dequantized by the runtime, increasing network initialization time. Accuracy may be impacted. | Compatible. The model is native format for these runtimes. Model can be passed directly to the runtime. May be more accurate than a quantized model. |
DSP | Compatible. The model is native format for DSP runtime. Model can be passed directly to the runtime. Accuracy may be different than a non-quantized model | Compatible. The model is quantized by the runtime, increasing network initialization time. Accuracy may be different than a quantized model. |
FXP_CPU | Compatible. The model is in supported format for FXP_CPU runtime. Model can be passed directly to the runtime. | Incompatible. Non-quantized models are not supported by the FXP_CPU runtime. |
AIP | Compatible. The model is in supported format for AIP runtime. Model can be passed directly to the runtime. | Incompatible. Non-quantized models are not supported by the AIP runtime. |
需注意的是,FXP_CPU和AIP不支持非量化模型。FXP_CPU会出现runtime error,AIP不清楚没有试过。
SNPE支持量化的模型,正如前面所述。
虽然SNPE对其量化代码没开源,但是文档表示其float转int8定点表示与TensorFlow中的量化方式一样,下面让我们一探究竟。
基本信息:
算法流程:
给定输入input_values = [-1.8, -1.0, 0, 0.5]。现根据上面算法描述做定点量化,并之后恢复回原浮点。
输入:
反量化公式,根据量化公式quant_value = round( 255 * (float_value - encoding_min) / (encoding_max - encoding_min) )推导出来float_value。
反量化公式即:float_value = quant_value * (encoding_max - encoding_min) / 255 + encoding_min;
则有反量化结果:
结果汇总表:
输入浮点 | 定点 | 反量化 | (反量化与输入)绝对误差abs_error | 误差占区间比例(encoding_max与encoding_min的区间)error_rate |
---|---|---|---|---|
-1.8 | 0 | -1.7960784313725493 | 0.003921568627450744 | 0.0017050298380220628 |
-1.0 | 88 | -1.0023529411764711 | 0.0023529411764711128 | 0.0010230179028135275 |
0 | 199 | -0.0011764705882355564 | 0.0011764705882355564 | 0.0005115089514067637 |
0.5 | 255 | 0.5039215686274505 | 0.003921568627450522 | 0.0017050298380219663 |
encoding_min | -1.7960784313725493 | |||
encoding_max | 0.5039215686274507 | |||
encoding_range | 2.3 | |||
step_size | 0.009019607843137253 | |||
(反量化与输入)绝对误差abs_error | abs(input_float_val - dequant_val) | |||
误差占区间比例(encoding_max与encoding_min的区间)error_rate | abs_error / encoding_range |
支持两种8bit定点的模式,主要区别在于量化参数的选择。
但第二种方式的计算精度,在某些长尾情况下,比简单直接使用真实最大最小的情况要好。
量化对模型对DSP是必不可少,因为DSP只能跑定点模型,而定点模型又会影响到精度,某些模型量化效果不好甚至出现错误结果。根据SNPE文档,评估量化模型的指标有:
因为int8精度低的问题,高通官方提出一种方法:A Quantization-Friendly Separable Convolution for MobileNets,一种新的MobileNet卷积核心模块,如下图。
上图分别表示经典标准的卷积模块、MobileNetV1原文中的Depthwise和Pointwise卷积,以及作者提出的卷积模块。效果上最好的架构是下图中的最后一个,也是上图第三个Top1分类精度为68.3%。
设计出如上的结构,也是基于作者实验得到的一些结论:BatchNorm和ReLU6操作是MobileNetV1的IN8精度损失的主要原因,因而作者移除了所有Depthwise Conv中的BatchNorm以及将ReLU6换为ReLU。
参考github上ReLU6的正向计算实现:out[i] = min( max(x[i], 0)+negative_slope*min(x[i], 0), 6 ),relu6一般在低精度运算中使用,尤其移动端设备。和relu相比,relu6的输出被限制在了一个固定区间内,根据tensorflow的文档说明:
This is useful in making the networks ready for fixed-point inference. If you unbound the upper limit, you lose too many bits to the Q part of a Q.f number. Keeping the ReLUs bounded by 6 will let them take a max of 3 bits (upto 8) leaving 4/5 bits for .f this used to be called a “hard sigmoid” paper:Convolutional Deep Belief Networks on CIFAR-10
在stackoverflow上的一则关于Why the 6 in relu6?问题中,有人回答道,AlexNet的作者Alex Krizhevsky在其一篇论文Convolutional Deep Belief Networks on CIFAR-10中解释说:
Our ReLU units differ from those of [8] in two respects. First, we cap the units at 6, so our ReLU activation function is y = min(max(x, 0), 6).
In our tests, this encourages the model to learn sparse features earlier. In the formulation of [8], this is equivalent to imagining that each ReLU unit consists of only 6 replicated bias-shifted Bernoulli units, rather than an infinute amount. We will refer to ReLU units capped at n as ReLU-n units.
注:参考[8]是Geoffrey Hinton为第二作者ICML2010的文章:Rectified Linear Units Improve Restricted Boltzmann Machines。
表明ReLU6是为定点推理设计的,通过限制上限让值域在[0, 6],2^3=8,则0-6占了7个,可以实现低bit模型量化,如无符号int2定点,或有符号int3。从下图其ReLU6的函数图像上,可以看出类似Sigmoid(x) = 1 / (1+e^(-x)),sigmoid函数图象是值域为(0, 1)的S型曲线,与ReLU6作为对照,ReLU6位于[0, 6)的输入区间是线性的,而输入大于等于6时的值域为6,因此也被称为Hard Sigomid。
再就是从实验结果来说,ReLU6在训练阶段会早早地学到稀疏化特征,不知是针对当前这一层还是最后一层的feature map。对于提到的伯努利units这块,对原文翻译是:每个ReLU6单元只包含6个偏移贝努利单元,而不是一个无限量,我们将RELU单元称为RELU-N单元,上限为N。我对其理解是伯努利单元是说原本是ReLU,后面是无限量,即max(x, 0),但是ReLU6使用6这个上限将其拆分成3截,说到这里,就提一下伯努利分布(Bernoulli distribution)又名两点分布或0-1分布,伯努利实验是只有两种可能结果的单次随机试验,那么我对伯努利单元的理解就是从原本的max(x, 0)的二选一,对其进行6个单位的偏移,即公式中的max(x, 6),即伯努利单元。
一位群友解释了6 replicated bias-shifted Bernoulli units:
relu(x) = max(0, x),而relu6(x) = min(max(0, x), 6),与relu不同的relu6并非上界是无限量,而是上界限定为6,因为Bernoulli units 是 0-1分布,那么6 replicated bias-shifted Bernoulli units 就是 min(max(0, x), 1) + min(max(1, x), 2) + … + min(max(5, x), 6) 。
6个函数的叠加(6 replicated),每个函数的上下界差1,而下界不再为0,所以称为bias-shifted。
但是我对着6个叠加的函数分别画出图像如下,累加起来结果并不是relu6,比方就看[0,1]区间内,x=0.5时,y = x+1+2+3+4+5 = 15.5。
然后和那位同学又讨论了下,应该是对原relu函数重复6次,大概验证了下是正确的。