本文是对NCNN社区int8模块的重构开发,再也不用担心溢出问题了,速度也还行。
作者:圈圈虫
首发知乎
传送门
从去年8月初首次向社区提交armv7a版本的int8功能模块到现在过去半年了,中途经过N次迭代。原本以为提交后就可以去别处摸鱼打望,谁知已掉进不断自己挖坑填坑的过程。中间多次尝试放弃,社区维护比想象的困难大得多:速度上不去,精度掉得多,armv7-a位速度OK,arm64-v8a位负优化,x86模拟int8没问题,切换到arm平台精度就狂掉,各种trick不work,社区大佬们天天吹水。
有幸同nihui共进晚餐(哈哈,有些人羡慕得都流口水了……)本尊原话:“ncnn也是我摸鱼创造出来的,2017年开源到现在(2019年1月)也快两年了,你不做int8就没人做了,反正大家喜欢当伸手党,既然开源出来,就要做到第一梯队,推动业界发展,向大家提供一个新的思路也算成功!”得嘞,我被上纲上线了,摊手.gif,幸运的是,感谢圈内各路大神的鼓励和指点,最终还是熬过来了,完成了这次int8模块的重构开发,再也不用担心溢出问题了,速度也还行吧。
这里非常感谢公司以及合作伙伴@格灵深瞳DeepGlint对开源社区的大力支持。
起因
去年(2018年8月)首次向ncnn社区提交的int8功能的模块,源于公司内部项目的一小部分,当初只是为了实现人脸检测网络模型加速,所以只支持conv3x3/conv1x1的卷积成,而且还只支持armv7-a架构。后面考虑社区对arm64-v8a平台需求更大,现在能用的手机也几乎100%是64位arm架构的,所以去年国庆节又疯狂码代码7天,补全arm64-v8a架构的int8支持。美滋滋上班不到一周被社区打回原形了,arm平台存在相当严重的溢出问题(基本上就不能用)。当初方案背锅了,由于最初的代码采用s8 * s8 -> s16,s16累加8次 -> s32,那么问题就出现了
不听高叔叔的指点,打脸了吧……
打脸都打肿了。退而求其次,只能采用s8->s16,s16* s16->s32的方案了,牺牲速度,满足int8量化任务顺利完成,开启本次int8模块重构旅程。
目标
- 解决卷积乘加累加导致的溢出问题
- int8 conv3x3s1速度比fp32 conv3x3s1慢的问题
- 含有dwconv layer的网络模型,量化后精度下降过多的问题
- 其余常规尺寸卷积层的int8支持
实现过程
解决卷积乘加累加导致的溢出问题
方案很简单,就是用s16乘加替换原有s8乘加,初次在hisi3519(Cortex-A17,armv7a)平台上完成conv1x1s1 layer的新版实现,实测速度有惊喜,咦~速度比之前还快了一丢丢,我简直是天才(出来混迟早要还的,后续在arm64-v8a上无限制打脸)。真是服了arm当初的流水线设计,arm64-v8a上neon浮点比整型多一个发射单元,于是在arm64-v8a(例如Cortex-A72)上单精度浮点乘加指令与s16整型乘加指令的吞吐率是一样的。当初qnnpack也提及过,合理使用sadalp和smlal指令实现Dual-Issue ,提高IPC(instruction per cycle)。
int8 conv3x3s1速度比fp32 conv3x3s1慢的问题
这个问题很麻烦,conv3x3s1是有Winograd F(6,3)算法增益的,理论计算量缩小5.0625倍,wino43是4倍,wino23是2.25倍,当然实际工程还要考虑两次trans和访存开销,加速比并没那么高。 int8直接卷积计算的速度肯定是赶不上了。能否实现int8 winograd呢?
答案是可以的,毕竟2018年中旬的时候,商汤科技已经发了paper了,虽然是基于FPGA平台的,但也说明工程应用是完全可行。Intel的mkl-dnn模块也已经实现了int8 winograd F(2,3),winograd-cnn的作者(给大佬倒冰红茶.gif)也在其github中置顶issue进行了相关讨论。于是站在巨人的肩膀上实现了int8 winograd F(2,3),哈哈全面超越fp32了,一天后在arm64-v8a上被打脸,于是又实现了int8 winograd F(4,3)。下面是int8 winograd F(2,3)实现的部分细节(假设你们都懂winograd在cnn中的实现原理)
Int8 Winograd F(2,3)实现技巧
基于armv7a框架上的wino-fp32 F(6,3)与wino-int8 F(2,3)/F(4,3)速度对比
含有ConvolutonDepthwise layer的网络模型,量化后精度下降过多的问题
当初精度效果差,原因在于权重没有分通道量化,实现分通道量化后,精度美滋滋。
其余常规尺寸卷积层的int8支持
其余尺寸的实现为了赶进度,就偷懒使用常规的im2col+sgemm的方式了,目前只实现了sgemm_int8_4x4_kernel,先解决有没有,再说快不快吧~
结果
圈子太小,就不和别的框架对比了,ncnn-int8全面超越ncnn-fp32,成功!(下图貌似有模型打脸了,说明还有优化空间
荣耀V10 Kirin970(Cortex-A73@2.3GHz)
荣耀V10 Kirin970(Cortex-A53@1.8GHz)
RK3288(Cortex-A17@1.8GHz)
感想
今年将是深度学习-计算机视觉领域模型低比特量化产品落地的第一年,毕竟各路NPU厂家均采用int8量化压缩方案,安谋科技(我偏不说ARM……)推出了新的框架Cortex-A76/A55,Mali-G76全面支持int8 dot指令,高通的超强DSP int8 tensor虎视眈眈,Intel的AVX512默默吃瓜,NVIDIA黄老板身穿皮衣手持TensorRT说“我不是针对谁……”,三大炼丹炉(TensorFlow、PyTorch、MxNet)也开始发布自己的int8模型训练方案。希望年末int8模型能够满足当前所有的任务精度需求。
相关传送门
- Winograd CNN论文作者的github
- Intel mkl-dnn中关于int8 winograd的issue
- 皮衣黄教主的TensorRT Int8 PPT
- MxNet中的实现
- 基于FPGA的快速Winograd算法
更多AI移动端优化相关技术干货请关注嵌入式AI专栏 及知乎账号圈圈虫