请选择 进入手机版 | 继续访问电脑版
查看: 1593|回复: 2

AI视觉组仙人一步之模型量化

[复制链接]
  • TA的每日心情
    开心
    3 天前
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3297

    主题

    6542

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    31909
    最后登录
    2024-3-29
    发表于 2021-1-28 11:01:07 | 显示全部楼层 |阅读模式
    AI视觉组仙人一步之模型量化


    神经网络模型最大的一个特点就是拥有宰相一样的肚量:宰相肚里能撑船,而神经网络模型的肚子里装满了权重。权重的多少往往直接反映出一个模型的复杂程度,而权重越大,模型的效果一般会更加好。


    那么问题来了,我们的目的是让模型在MCU上奔跑,但是MCU的计算资源和存储资源都远不能和PC相比。在PC上顺风顺水的大家伙,也想到MCU上玩儿玩儿,要怎么办呢?

    模型量化的概念

    出于这一目的,模型量化技术应运而生。而量化又分为训练中量化,以及训练后量化,我们此处着重介绍训练后量化。
    训练后量化技术可以理解成一种有损压缩,会损失一点精度,但是,会显著降低模型尺寸,同时提高运行效率。


    量化的原理其实很简单。模型在训练时一般是使用float32类型来存储的,每一个权重数据需要占用4个字节,这样才能精确地降loss。但是,模型的推理却没有这个要求了,那么是否可以换种方式来存储呢?比如改用int8,这样,每存储一个权重,将只消耗1个字节,模型的尺寸会缩小为之前的1/4,何乐而不为?


    幸运的是,这样对精度的影响通常极小,只要是在可接受的范围内,我们就降低了模型的存储尺寸。而且,目前神经网络加速器或是神经网络加速库,一般只支持int8类型的数据输入,对float类型无能为力。


    模型小也大大减轻了对存储器系统和Cache的压力。这样一来,在减小了模型尺寸的同时,还可以加速模型的运行。


    接下来我们会分别介绍tflite和nncu中的量化原理与实现方式。


    在tflite中的训练后量化

    关于训练后量化,不仅仅局限于上文所提到的int8类型量化,如下表所示,之后会重点介绍全整型量化:
    21.png
    动态范围量化
    首先介绍动态范围量化,这是简单的训练后量化方式,仅仅将权重数据由float类型转换为8位整数,其他例如激活层和输入输出还依然保留为float类型。


    ●  实现方式
    22.png


    这种方式有个局限性,当模型推理的时候,权重数据会被转换为浮点数进行计算,无法发挥Cortex-M内核的整数计算能力。不过,为了提高运行效率,这个转换只会被做一次并缓存,以减少延时。


    全整型量化
    全整型量化,顾名思义就是保证模型中的所有数据,都会被转换成整型数据表示,除了权重数据,还有各个中间结果数据,也就是常说的tensor(张量)。由于模型只存储了权重/偏置信息,所以模型尺寸上和动态范围量化相比不会有太大变化。但是,对于那些只能做整数运算的硬件设备或是硬件加速器,只有全整型量化才能派上用场。


    实现全整型量化有一个重大的问题要解决:要事先知道模型中所有float类型tensor的取值范围,包括输入,输出,每一层的激活值。然而,和那些静态数据(权重,偏置)比起来,它们是由模型的输入数据决定的,并且是随着模型的执行而动态变化的。


    这样看来,准确判断tensor的取值范围成了不可能的任务。幸运的是,这个范围可以是估计的(当然就会损失精度)或者是估算出来的。就如同统计学中的抽样,我们也同样提供一个mini数据集,representative_dataset(),以供迭代之用,并且用在它们上跑出来的各个tensor的取值范围来做代表。


    话不多说,放码过来:
    23.png
    这样一来,所有模型内部数据,包括模型的输入和输出,均会被转换为整数型表示,以保证兼容那些特殊的整型数据依赖设备。如果发现有不支持量化的操作符,会丢出一个异常。
    要注意的是,想要使用这些新的API,要保证tensorflow版本为2.3.0,tensorflow从2.3.0版本开始才加入了inference_input_type和inference_output_type属性的支持,即加入对输入输出节点量化的支持,实现真正的全整数量化支持。
    需要特别指出的是,tflite目前推出了最新的tflite-micro库,专门针对于嵌入式设备,
    其内部实现了与最新的CMSIS-NN库的加速支持,但其仅支持signed int8 (s8)的全整型量化模型,否则,会直接调用tflite的原生API,无法实现模型加速。在最新的CMSIS-NN中,有一众后缀为”_s8”的NN算子,tflite就是调用它们实现计算的。


    Tensorflow的训练中量化简介

    讲到这里,大家要注意一件事情,采用后量化的方式进行模型的量化,肯定会造成一定的精度损失。那么,当发现精度下降太大时候,可以增加representative dataset的规模或者换一批数据集的代表。


    考虑使用训练中量化,这种量化方式会将量化误差耦合进模型训练中,在根本上减小模型量化精度的损失。但是,比起后量化来说,我们需要修改模型训练脚本,插入被称作fake-quant的节点,对模型进行重新训练,可参考
    https://www.tensorflow.org/model_optimization/guide/quantization/training。
    而这样一来,我们就无法直接使用网上的预训练模型来进行直接部署,无疑使得可操作性变得复杂了许多。
    因此,还是推荐使用后量化的方式进行模型的量化。在实际使用中,要进行权衡以确定使用何种方式。


    在nncu中的模型训练后量化


    nncu只支持训练后量化。
    其实在在去年的电磁AI中,本加油站里有一篇文档就已经提到了nncu工具:AI机器学习实战の电磁智能车篇。
    这个推文简洁地介绍了nncu的GUI。下载了nncu的同学也可以在nncu根目录\docs\ nncu_user_guide_cn.pdf文件中查看更详细的介绍。
    量化原理
    在nncu底层使用的CMSIS-NN老式库中,量化系数给出了是由多少个位来表达tensor中的整数部分和分数部分,并且整数都是有符号整数。

    比如,对于8位量化,如果由3个位表示分数,那么就有1个符号位,4个整数位,和3个分数位,表达范围是[-128/8,127/8],分辨率为1/8;如果是4个位表达分数,则表达范围就是[-128/16,127/16],分辨率为1/16。下图就演示了手工指定全局(所有层)的量化都使用3个位来表达分数部分:
    24.png
    在nncu中量化模型,既可以由用户指定一个全局的数值来配置表达分数的位数,就像是在电磁AI中的做法;又可以像tflite那样提供一个代表性的数据集来估算各tensor的取值范围。下图是使用自动量化时的一个例子:
    25.png
    在自动量化模式下,可以限定nncu生成的量化系数中,最少有几个位来表达分数部分,建议不要设置得太大。
    手动还是自动
    由于电磁AI用的是9-16位量化,分辨率高,所以手动指定全局分数位数也不会造成什么精度损失。在本次比赛的图像分类任务上,当然也可以使用9-16位量化,不过没有8位量化产生的模型跑得快。只是,8位量化下,大一点的模型就不能再用全局量化参数了,否则精度很容易比较惨。
    迷你数据集
    在nncu上,自动量化也需要类似于tflite中“representative dataset”的机制,称为“迷你数据集(mini dataset)”,使用它们来指导量化。并且,nncu为了弥补CMSIS-NN旧式API在量化精度上的逐层损失,还添加了一种“启发式量化”的机制,来微调各层量化的分数位数,具体的说明也在nncu根目录\docs\ nncu_user_guide_cn.pdf文件中。简单来讲,需要制作迷你数据集并编写胶水代码来加载和使用。
    制作迷你数据集
    nncu支持所谓的”后处理”,这是nncu配套的量化质量测试工具所必需的。对于水果与动物的分类,后处理类型选用“分类”即可。
    要在nncu上使用迷你数据集,在nncu需要经过一系列的操作步骤得到2个文件:
    <数据集名_x>.npy
    <数据集名_y>.npy
    但是,由于本文中参考数据集已经足够“迷你”了,所以可以直接使用数据集中的“x.npy”和”y.npy”。
    借用cifar10示例来加载和使用迷你数据集。
    在制作了迷你数据集之后,还需要在.\nncu_pc\下的loaddata.py和test_file_gen.py中,分别添加迷你数据集的加载函数和测试向量的生成函数,步骤可能有点多。


    不过,有一个“移花接木”的方法:在本文给出的参考数据集中,动物和水果共有10种,恰好和nncu自带的”cifar-10”示例的类型相同。
    所以,可以用这个数据集中的npy文件替换nncu\datasets\cifar10\ 下的文件,具体来讲,是:
    用x.npy替换nncu根目录\datasets\cifar10\c10x.npy
    用y.npy替换nncu根目录\datasets\cifar10\c10y.npy
    替换后,就可以借用nncu自带的cifar10示例来量化了。在nncu的GUI中点击”加载…”按钮并且选择c10.ini,再在“选择模型文件”按钮中改用自己训练的模型即可。
    如果觉得这样做有点“赖皮”,有兴趣的同学可以跟着nncu_user_guide_cn.pdf中的完整介绍,为自己的数据集立一个名份。


    写在最后

    至此,部署模型中最关键的模型量化就介绍完了,万望同学们充分重视这至关重要的环节,以得到和训练模型尽量接近的量化效果。


    总结一下可以得到以下几个小贴士:


    1、nncu的量化性能高,但误差层层积累快;tflite量化精度高,但性能有损失
    2、8位量化效果不够好,改用tflite,或者nncu的9-16位量化(建议使用14位)
    3、使用代表性数据集(tflite)或迷你数据集(nncu)来指导量化

    4、在nncu中还可以使用启发式量化来微调各层的量化参数(慢)

    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2021-12-8 10:59
  • 签到天数: 305 天

    [LV.8]以坛为家I

    7

    主题

    1147

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    13661
    最后登录
    2024-1-4
    发表于 2021-1-29 17:54:12 | 显示全部楼层
    打卡学习。
    哎...今天够累的,签到来了~
    回复

    使用道具 举报

  • TA的每日心情
    开心
    1 小时前
  • 签到天数: 802 天

    [LV.10]以坛为家III

    70

    主题

    2410

    帖子

    24

    金牌会员

    Rank: 6Rank: 6

    积分
    5387
    最后登录
    2024-3-29
    发表于 2021-2-1 17:36:42 来自手机 | 显示全部楼层
    学习学习
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-3-29 22:35 , Processed in 0.115151 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表