整体理解
这是一篇来自康奈尔大学的作品,在我看来,这篇二值化工作的主要贡献有四点:
(1)基于ReactNet的想法,通过改变激活层与BN层的位置,提出了一种更优秀的二值卷积结构
(2)第一卷积层的二值化方案,并保持精度不降
(3)分数卷积的提出,为了更高的精度,作者选择了2bit的激活值,但是很巧妙的对其进行了拆分跟组合,使卷积过程是1bit的卷积,从而便于硬件采用xnor与popcount加速
(4)完成了FPGA上的加速器实现,ImageNet上Top-1精度到达了71.8%
背景简介
论文首先分析了目前二值神经网络的两大主要问题,其一是老生常谈的二值化带来的精度损失;其二是在大部分的二值化方案中,第一卷积层其实都还是保留了全精度的卷积过程以避免精度的大幅下降。在GPU上实现算法时,第一层卷积只需处理三个输入通道,因此与传统CNN的其他层相比,涉及较少的浮点MAC操作,但一旦要真正做到FPGA的硬件加速器上,由于DSP资源有限,浮点输入层的计算难以实现高并行度。此外,硬件实现时必须实例化一个专用的浮点卷积引擎来针对第一卷积层,这样就导致这个引擎不能被其他层重用,造成资源的浪费。
具体方法
(1)二值卷积结构设计:
左图是ReactNet提出的二值卷积模块,右图是FracBNN的方案,我们通过对比可以看到,
FracBNN将BPReLU移动到
⨁
\bigoplus
⨁连接之前(Improved training of binary networks for human pose estimation and image recognition和Training binary neural networks with real-to-binary convolution两篇工作提到,位于
⨁
\bigoplus
⨁连接之前的激活函数往往表现得更好)。此外,FracBNN在每个
⨁
\bigoplus
⨁连接后添加BN层,通过BN层的仿射变换使得输出中的正负值的数量更加平衡。这种分布上的变化对于ReactNet中二进制卷积层的特征学习是至关重要的,由于BPReLU和BN层只包含通道参数,所以它们对模型参数总数的影响可以忽略不计。更进一步的,由于BN层已经包含了偏差项,添加了BN层后,符号函数也已经不需要可学习的阈值。这样看来的话,FracBNN的二值卷积模块达到了与ReactNet同样的数值域调整的目的,但是省略了符号函数的偏置项的计算,这部分在硬件上会省略部分访存与计算的开销。作者以Resnet20在cifar-10上验证了这个二值卷积模块的有效性,对比对象包括了IR-Net与ReactNet等优秀方案。
(2)第一卷积层的二值化
这块的想法还挺有意思的,因为这篇文章是做了加速器设计的,所以他们额外考虑到了硬件的加速问题,针对性的解决了第一层卷积二值化的问题。在输入层中对权重和激活进行二值化的挑战是输入的特征图一般是RGB三个通道,3个通道不足以支撑二值化,关于这件事其实也很好理解,我们只要分清第一层卷积的输入数据与其它层卷积的区别就可以,如图所示,其它层卷积因为通道数的增加,导致二值化以后的数据表达范围增大,同时因为上一层的输出经过BN处理,导致二值化的相对损失变小。因此将图像分割成更多的通道就很有必要,FracBNN的想法正基于此。
丰富通道的一种自然想法是将每个像素视为 8 维二进制向量,因为像素是 8 位定点数。尽管如此,每个二进制数字都有自己的相关权重,如下图所示标记在每个位的右上角,而当一个像素被转换成一个二值向量时,权重信息就丢失了,每个位的大小变得相同。有人可能会说,可以让BNN也学习二进制数字的权重以进行等效变换,然而,这对于 BNN 训练来说是一项具有挑战性的任务。在这项工作中,作者选择使用 thermometer encoding的编码方式(这种编码方式最早出现在Thermometer Encoding: One Hot Way To Resist Adversarial Examples)这种编码方式其实很好理解,我们先定义几个变量,
p
p
p表示某个像素点的RGB值,
L
L
L表示整个码的维数,
i
∈
{
1
,
.
.
.
,
L
}
i\in\{1,..., L\}
i∈{1,...,L}代表索引值,
T
V
TV
TV代表整个编码,
R
R
R是分辨率用来控制码
T
V
TV
TV的粒度,
T
V
TV
TV定义如下式:
以
p
p
p=109、
R
R
R=32举个例子,从VT的第6位开始至第8位为1,其余为0,每一位代表32的粒度。
在本文的实验中,取
R
R
R=8,也就是说可以将原本数值范围为0-255的一个通道扩充为数值为0或1的32维的通道,这样就为二值化的卷积带来了空间,但其实这样做也增加了些计算量。下面是其thermometer encoding实验的结果,这种编码方式达到了精度无损。
(3)二值卷积过程的拆分与组合
如下图所示,为了更好的保护信息,FracBNN的激活值其实是2bit的,但是作者很巧妙的设计了一个拆分过程,通过将2bit的激活分为两支(MSB与LSB,其中MSB是高位,LSB是低位),两支的激活分别各自为1bit,这样就能利用xnor和popcount来进行硬件加速,最后再将MSB的popcount左移一位,再加上LSB的popcount结果,得到最终的输出,而且这边还有个细节,LSB的popcount值只取大于等于某个阈值
△
\triangle
△的结果,在图中这个阈值为4。
这种做法相较于单纯二值化激活的方案,一定程度上保护了信息,但同时也引入了更多的计算次数,虽然作者也试图通过阈值
△
\triangle
△的设计带来一些稀疏性,但总体而言,计算次数是增加了的。
(4)FPGA架构设计:
主要运算模块包括:33分数卷积、11分数卷积、3*3二值卷积(输入层)、平均池化、BN、BPRelu、残差与concat结构,系统整体架构如下图所示。
分数卷积的伪代码逻辑:
实验结果:根据我们前面的分析,可以看到FracBNN的FPMAC次数确实是减少了,但是BMAC次数也增加了
(本文仅代表个人见解,如有不妥,欢迎大家共同探讨!如需转载,请联系本人。)