前言
因为最近都在关注深度学习模型压缩相关的工作,所以今天给大家介绍的也是一篇关于模型压缩的方法。这是一篇非常有意思的工作,来自于三星研究院在ICLR2017上发表的论文:DeepRebirth: A General Approach for Accelerating Deep Neural Network Execution on Mobile Devices。常用的模型压缩手段往往是从网络的参数,也就是weight入手(可以参考本人之前的几篇关于模型压缩方法的综述性博文),因为weight占用了大量的计算资源和存储空间,这往往是最直接有效的。而本文却另辟蹊径,从非权重层入手来进行模型压缩。
动机
作者认为,目前常用的模型,如ResNet、GoogLenet等的卷积层都是由很小的卷积核组成,本身就非常紧致了,并且也去掉了非常占参数量的全连接层。而Non-tensor layer(也就是非权重层,如pooling、BN、LRN、ReLU等等)反而成为了模型在cpu以及其他嵌入式硬件上达到real-time的最大阻碍,见下图表:
可见无论在哪一种硬件平台上,这些Non-Tensor层都占用了非常多的运行时间。因此作者认为,如果能够找到一种方法将这些Non-Tensor层剔除,就能够大幅度的提升模型的运算时间。
方法
作者提出了两种剔除Non-Tensor层的方法:StreamLine Merging和Branch Merging,如下图:
StreamLine Merging
所谓的StreamLine,流水线,顾名思义,就是将这一连串的层合并起来,如上图,作者将这里的Non-Tensor层(Pooling、LRN)与它们相邻近的Tensor层(Conv)合并在一起,合并的方式也非常简单粗暴:对于Pooling层,将stride直接乘到Conv层中,然后将Pooling去掉;对于其他非Pooling层,如BN、LRN、ReLU直接去掉。如上图中,这样一个合并的过程将运行时间153.8ms降到了16.6ms。Branch Merging
这种融合主要针对GoogLeNet中的Inception结构。GoogLeNet虽然参数比较少,但是由于细小层比较多,参数比较碎,因此运行起来速度也收到了一些阻碍。作者将比较细小的卷积层(1*1)以及Pooling层所在的分支,直接合并到和它并排的大卷积分支中,而合并后的分支的输出通道也进行了相应的增加。考虑到大卷积(3*3或5*5)的输出通道增加,会增大参数量和运算量,因此作者又将这些分支的输入通道进行了一定的缩减,以达到一个降低参数量和运算量的作用。
以上两种方法就是作者提出的将Non-Tensor层搞掉的方法,但是搞掉之后,模型的性能必然会降低(实验都是在预训练模型的基础上进行的),因此作者在这里使用一个retraining的方式来恢复性能,这个retraining的方式也其他模型压缩方法中所用到的retraining都有一些不同。对于一个预训练模型,作者逐层进行合并,合并得到的新层使用标准的初始化方式,其他层的参数保留原预训练模型的参数,然后将新层的学习率调高为其他层的10倍,进行finetuning,对于某些层,如GoogLenet中的Inception 4b-4d可以一起进行合并在finetuning。
实验结果
作者使用GoogLenet在ImageNet上的结果如下表:
在经过一系列的合并之后,模型的准确率仅仅降低了0.4%。那么加速效果如何呢:
上表是作者在Samsung Galaxy S5上测试得到的结果,发现经过这一系列的合成,将运行所消耗的时间降低了3倍以上,并且这个合并的方法也可以和之前的一种叫做Tucker分解的方法一起使用,可以将时间消耗降低到6倍以上。
在不同硬件平台上的效果:
我们发现,由于GPU(Titan X)强大的计算性能,这些Non-Tensor层所带来的时间消耗可能看不出来什么,但是在便携式的硬件平台上,如手机,这加速的效果就非常明显了。这也使得深度学习模型能在便携式硬件上达到real-time又更近了一步。
总结
本文确实是一篇非常有意思的论文,也是一项很有启发性的工作,我们在设计模型包括压缩和使用模型时,不能光考虑理论计算量,还应该考虑到硬件特性、带宽等因素,就比如在这里,Pooling层本来没有任何参数,不会带来任何的理论计算量,但是在CPU上却会带来额外的时间消耗。但是本文这种retraining的方式可能会给模型的性能大打折扣,因为新合并的层需要重新进行训练,虽然其它层的参数以及达到了一个非常好的状态,但是这个新的合并层能训到一个什么样的效果,在这里还是持怀疑态度。不过总的来说,这篇文章无论是从创新点还是实用性上,都是一项非常有意义的工作。