本文以NCNN框架为例,实践量化在推理中的巨大作用,加深对神经网络量化的理解。NCNN当前版本只支对称量化,下面以INT8精度为例介绍NCNN的量化使用方式:
编译NCNN
mkdir build && cd build && cmake ../


进入到build/tools/darknet目录,将来源于darknet的模型文件和权重文件拷贝一份到这里:
wget -c https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.weights

转换过程如下:
./darknet2ncnn ./yolov4-tiny.cfg ./yolov4-tiny.weights

优化:
./ncnnoptimize /home/czl/ncnn/ncnn/build/tools/darknet/ncnn.param /home/czl/ncnn/ncnn/build/tools/darknet/ncnn.bin yolov4-tiny-opt.param yolov4-tiny-opt.bin 0

同时,也可以得到MAC算力信息:

生成的优化过的模型如下:

检测实战,未优化的模型推理结果:



优化后的模型推理结果:

这里的yolov4其实就是yolov4-tiny,程序中是可以选择:


下载量化校准表图片
下载官方给出的1000张ImageNet图像,很多同学没有梯子,下载慢,可以用下这个链接:
图片内容包括:

进入到目录build/tools/quantize,创建images文件夹,之后将imagenet图片全部拷贝到此目录

之后,执行命令 find images/ -type f >imagelist.txt,创建图像文件列表。
![]()

之后执行命令:
./ncnn2table yolov4-tiny-opt.param yolov4-tiny-opt.bin imagelist.txt yolov4-tiny.table mean=[104,117,123] norm=[0.017,0.017,0.017] shape=[224,224,3] pixel=BGR thread=8 method=kl


这个过程可以类比acuity tools的模型量化阶段,也需要指定一些量化数据进行量化。
量化模型:
./ncnn2int8 yolov4-tiny-opt.param yolov4-tiny-opt.bin yolov4-tiny-int8.param yolov4-tiny-int8.bin yolov4-tiny.table


生成的yolov4-tiny-int8.bin即为量化后的权重文件,可以看到它的大小是量化前的 四分之一,这就是量化的一个优势,可以减小内存使用量。
验证量化效果:
修改examples/yolov4.cpp代码,使用上新创建的量化模型,同时调整推理参数,使用量化推理:

验证检测效果,主观上能够感受到检测精度方面还是有所损失的,不知道是否和我的操作有关。

对比上面同一张图像的推理效果,可以看到,int8量化会导致推理精度下降。


KL量化原理:

根据流程,貌似ncnn net对象可以得到网络卷积层的个数,然后根据网络推理的结果,得到每个卷积层的输出,之后计算直方图,得到概率信息,和原来的概率信息做kl-diverage散度计算,看信息损失度。根据net对象,可以得到每个卷积层的输入,输出blob,也就是tensor数据,根据这个思路,继续分析代码。

权重要不要进行KL散度量化?
答案是不需要,权重数据是确定性数据,没有不确定性,所以如果是对称量化的话,直接根据其绝对值大小除以量化范围得到scale即可。

在芯原的acuity 和IDE的处理中,也是同样的做法,权重的量化参数不依赖于dataset中的数据集。
而输入输出数据则不一样了,一者我们是用随机的量化图像数据进行仿真推理获取各层数据的,输出数据范围本身具有不确定性,而来量化图像数量也是不确定的。所以,要对量化各层的输出应用KL散度算法,确保产生的参数反量化后,各层的输出和真实值接近。

YOLOV4模型的NCNN导入量化推理
前面测试的是yolov4-tiny模型,通过代码我们知道,可以通过关闭YOLOV4-TINY宏使用大模型的YOLOV4进行推理,下面进行测试:

导入模型
./darknet2ncnn ./yolov4.cfg ./yolov4.weights

网络优化:
./../ncnnoptimize ./ncnn.param ./ncnn.bin yolov4-opt.param yolov4-opt.bin 0


测试:
./yolov4 ~/doudou.png
量化:
./ncnn2table ../darknet/yolov4-opt.param ../darknet/yolov4-opt.bin imagelist.txt yolov4.table mean=[104,117,123] norm=[0.017,0.017,0.017] shape=[608,608,3] pixel=BGR thread=8 method=kl
./ncnn2int8 ../darknet/yolov4-opt.param ../darknet/yolov4-opt.bin yolov4-int8.param yolov4-int8.bin yolov4.table


量化结果:

修改YOLOV4.cpp程序:

./yolov4 ~/doudou.png

yolov4大模型的NCNN量化推理实践成功,从上面量化表的生成过程可以看出,我们对参数的设置并没有按照网络的实际尺寸设置,但是似乎并没有对结果造成太多影响,推理大致还是正确的,原因上可能也是因为,即便设置的图像尺寸不对,但是量化参数产生过程也会根据错误的尺寸生成SCALE参数,这个参数和最有值之间不会偏差太大,毕竟也是参考同样的数据集产生的,所以最终的推理结果精度会受到影响,但是不会导致推理的完全错误。
如何调试NCNN
modify the CMakefileList in top file:
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")

then you get the execuable files with debug info.

code format with astyle:

quantilize和dequantize执行时序:

for a inference session, the network extract function is the key recurisve funtion to run the network.

NCNN inference invocation is network.extract, it is a rescurisive impl. you can test like this:

and bottom blob is a input tensor for specify netowrk, top blob is the output blob for specify netowork.
NCNN模型文件分析
模型文件的第一行是魔数,第二行的两个数字分别是层数和BLOB(我的理解是tensor)数。

NCNN KL量化分析
理论分析看下面博客
for (int i = 0; i < image_count; i++){

}
可以分析一下,针对每张图片,找到层的输出tensor,然后将通道的数据除以最大值,之后乘BIN数,目的是得到此数据归属的BIN,之后对对应的BIN加1,表示当前数据归属此BIN,对于多通道的TENSOR,则所有通道共用一个BIN序列和最大值,这说明NCNN不支持PER CHANNEL 量化。
当计算出所有TENSOR的BINS分布后,在将其累加到stat,stat每个输出TENSOR有一个。从这里看,bottom表示输出吧。
由于NCNN仅支持INT8对对称量化,所以对于量化来说,仅仅需要一个缩放系数即可,KL算法的关键就是找到这个缩放系数,通过找到一个介于128和2048的threshold,表示BIN为此值时信息损失最小,据此得到浮点threshold对应值,此值对应量化的127,即可得到缩放系数scale. 如下图所示:

clip_distribution:

这段代码的操作如下图所示:

从图示中可以明显看出变量名命名为clip_distritution的原因了吧。
关于不同BINS下的直方图的生成方式,可以参考如下图:

ceil and floor


NCNN推理的流程特点:
总体上,NCNN的推理是一个递归处理的过程,有点类似于下面的第归程序
#include <stdio.h>
#include <stdlib.h>
void print_array(unsigned char *a, int n)
{
if(n > 0)
{
print_array(a,n-1);
}
printf("[%d]: %d.\n", n, a[n]);
return;
}
int main(void)
{
int i;
unsigned char array[10];
for(i = 0; i < 10; i ++)
array[i] = i;
print_array(array, 9);
return 0;
}

总结:
NCNN目前没有实现非对称量化,只实现了对陈量化一种形式,此信息经过NCNN原作者NIHUI大佬确认如此,并且根据TOOLS目录的实现,只有tools/quantize/ncnn2table.cpp,ncnn2int8.cpp一个量化工具。

本文详细介绍了使用NCNN框架进行INT8量化的过程,包括编译NCNN、模型量化、验证效果、KL量化原理以及YOLOV4模型的量化推理。量化后模型的权重文件大小减小,但可能导致精度损失。通过分析,揭示了NCNN仅支持对称量化,以及在推理流程中的递归特点。

https://download.youkuaiyun.com/download/weixin_45829462/18704213


840





