16 bit float 存储_ARM CPU 1-bit推理,走向极致的道路

深度学习的端侧学习对轻量化有着极致的追求,从2016到现在,我们看到业界在推理时使用的数据类型位数一降再降:FP32、FP16、INT8……。显而易见,低比特能节省模型占用的存储空间和功耗,在某些精心设计的算法上面也能获得性能优势。在这一点上,1-bit毫无疑问是经典计算机上可以做到的极致。举一个实际的例子,Birealnet18(二值化的Resnet18)用FP32类型存储大概需要45MB,而通过Bolt转换后得到的1-bit+FP16存储只需要占用2.7MB,exciting!

那么,权重位数走向极致,是不是就能带来计算速度的提升呢?答案取决于你使用的硬件。现阶段深度学习的核心计算还是矩阵乘法,这里面涉及到的就是乘法和加法。大多数硬件的ALU可以处理的最小数据类型就是INT8,要是按照严格的矩阵乘法定义,低于8位的数据类型都得先展开到8位来操作(INT4和INT2哭晕在厕所)。

但是1-bit可以来个“降维打击”:权重只有0和1,干脆省掉乘法,直接全都做加减法,多么HAPPY。看到这里,你已经领会到Binary Weight Net的精神了,在经典CPU上已经可以靠着加法相比于乘法的优势行走江湖了。

还没有换台的观众们,估计眉头一锁,觉得此事没有那么简单。是的,深度学习早已经皈依了黄老板的皮衣神教,移动端CPU的绝对霸主ARM也推出了SIMD单元。这些个SIMD单元,乘法和加法用的就是同一个单元,甚至可以用一个指令完成乘加操作。

在现有的SIMD上,1-bit要想占有一席之地,必须再次发动降维。当我们把 feature map 也二值化了后,乘法的两端只会出现0和1,而最终的乘积也只会是0和1,直接位操作就行了,总有一款适合你,而且还不用改动现有的硬件——这就是BNN的精髓。

试想一下,中低端SoC是不支持FP16和INT8的专门优化的,能找到的估计就是跑FP32的CPU。部署FP32的模型,不仅存储感人,推理时间也会感人。但你要是训出一个BNN模型,存储一下子就下去了,而且还能轻松在原有芯片上打败FP32。有些读者可能在担心精度问题,但BNN已经取得长足的进步了,不搞识别,搞个实时检测还是很有价值的。

接下来,本文将会介绍Bolt是怎样基于ARM NEON进行BNN推理的。在这个事情上,Bolt没有抢到先发,我们被大缺弦老师的BNN框架(daBNN)截胡了。但我们也要感谢大老师选择了开源,特别是把他训练的birealnet18模型也放到了repo上,做BNN框架才有了真正意义上的benchmark。在我们调试该网络的过程中,他也知无不言,衷心感谢。

在一款搭载A55小核心的开发板上,daBNN实测birealnet18(带stem优化版本)单张图片是243ms,而Bolt的单张推理时间是78ms——对于这个结果我们当时也是很震惊的。

大家容易想到的第一个因素是在模型的浮点部分,Bolt用的是FP16啊,性能优势是这样来的吗?但是,我们最初开始开发的时候,直接把整个网络按FP16来跑,也只需要160ms。其实,在A55小核心上,由于关键指令无法双发射,daBNN是在客场作战的——Bolt几乎所有汇编kernel都有针对A55进行指令发射的优化(注重能耗的开发者记得pick我们)。那么,性能对比在A76上又如何呢?请容许我卖个关子,接下来先进入技术解密,看完你就更能理解Bolt BNN推理为什么有这么大的优势了。

==== 技术解密分割线 ====

正如大老师在他的论文中讲的,ARM上面做BNN推理最重要的是以下两个指令

AND:逻辑门位操作,要是使用XNORnet那就换一条逻辑门指令

CNT:在向量寄存器上每8个比特统计1的数目,得到0到8之间的数字,按照UINT8存回到那8个比特上

这两条指令应该怎么理解呢?在64位的芯片上,向量寄存器的宽度是128bit。一条AND指令就可以完成128个乘法,而一条CNT指令就完成了16组8个数字的加法。换言之,这两条指令就相当于16组8x8的向量内积。

那么,接下来就要说到Bolt和daBNN的重要区别了。大老师的128bit装的都是对应同一个输出通道的操作数,所以16组8x8内积得到的16个UINT8数字,还得通过ADDV指令加到一起,然后才加到对应的输出位置上,并行度突然就砍掉了。大老师也发现了ADDV指令是很吃亏的,于是他尽可能先把很多组16个UINT8数字通过向量ADD加起来——毕竟UINT8最大能存255嘛,只用8很浪费——然后再用ADDV。

但是,大家有没有想过可以完全省掉ADDV呢?AND+CNT输出的16个数字,要是对应的是16个输出通道,那我们就只需要直接ADD起来了。这其实是经典的tilegemm思想,就不在此赘述了。

可能有课代表发现了,直接ADD起来,顶天也就只能加到255啊,不可能一路ADD下去。是的,BNN卷积的结果大多数情况至少也要用INT16才能存储,我们的确也是需要累加到INT16向量的。跟上面一样的道理,为了节省INT16累加的指令,我们需要在ADD的时候尽量用满UINT8的空间。理论上,8x8内积最多产生数字8,而UINT8最大能存255,也就是最多约可以累加32次,追求极致的我们需要想这个32怎么来。

卷积的时候的每一组加法次数,等于ic*fh*fw。在Bolt中,我们要求BNN卷积层的输入通道数必须是32的倍数,每次8x8内积会用掉ic里的8,那么ic/8我们只能保证是4的倍数,距离32还差一个8。这个8要怎么来呢?欢迎大家想一想,也欢迎到qq群一起交流,群号833345709~

==== 技术解密分割线 ====

好的,完整的性能对比如下图:

6eeba50b5f495f5029a363fe6bad99a3.png
1-bit推理性能时间(ms)对比

感谢大老师放出来的模型,然后测试平台是麒麟990。由于stem版模型的onnx比较晚才放出来,做这张图的时候就没加上去。Bolt+stem在A55上是70.9ms,而在A76上是17.0ms。

感谢大家能够看到这里。Bolt里面的每一个kernel,每一个精度,甚至每一个功能,都经历了如上所示的严密推敲和不断优化,欢迎大家多多体验,这也是我们开源的初衷。大家如果想到了更好的做法,那就太好了,贡献出来,我们一起走向极致。

相关成果我们已经开源到Github:https://github.com/huawei-noah/bolt,方便大家复现和使用,欢迎大家试用反馈体验效果,欢迎大家在社区积极讨论。高性能,精确,轻量级,易用和安全是我们不懈追求的目标,未来我们会继续利用高性能计算优化技术和编译技术,加速深度学习,在计算机视觉和自然语言处理领域发力,将更多的研究结果带到社区。

欢迎高性能计算和深度学习算法方向的童鞋加入我们,简历请发送到rogy.liu@huawei.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值