加速卷积神经网络这样做!
本文将概述一个加速卷积神经网络的训练,却不会对精度产生重大影响的方法。
完全连接的层是神经网络大内存占用的主要原因,它的速度很快,相比较来说,卷积虽然参数数量紧凑,但却会消耗大部分的计算能力。实际上,卷积是很容易计算的,这也是它们需要庞大的计算能力来训练和运行最先进的神经网络的主要原因。
那么,我们可以设计出快速高效的卷积吗?
在某种程度上,它的答案是肯定的!
我们需要一些方法可以加速卷积,而不会严重降低模型的准确性。在这篇博文中,我们将介绍以下方法。
·卷积核的分解/分解
·瓶颈层
·更广泛的分布
·深度可分卷积
接下来,会深入介绍所有这些方法实施背后的原因。
简单分解
我们从NumPy中的以下示例开始
您可能会问,为什么我会向您展示这个片段?答案是,它表明你可以写一个N×N矩阵,将卷积核看作是2个较小的矩阵/内核的乘积,其形状为Nx1和1xN。回想一下,卷积操作需要in_channels * n * n * out_channels参数或权重。此外,请记住,每个权重/参数都需要激活。所以,减少参数的数量将减少所需的操作次数和计算成本。
考虑到卷积运算实际上是用张量乘法完成的,而张量乘法是多项式依赖于张量的大小,正确应用的因式分解应该产生切实的加速。
在Keras上,看起来像这样:
但请注意,不建议将最接近输入卷积层的因素考虑在内。此外,分解3x3的卷积甚至可能损害网络的性能。最好让它们保存在更大的内核中。
在我们深入探讨这个话题之前,有一种更稳定的方式来分解大内核:将更小的内核堆叠起来。例如,不是使用5x5卷积,而是使用两个3x3卷积,或者如果要替换7x7内核,则使用3。
瓶颈层
瓶颈层背后的主要思想是通过减少输入通道的数量(也就是输入张量的深度)来减小内核大于1x1的卷积层中输入张量的大小。
下面是它的Keras代码:
几乎所有的CNN,从革命性的InceptionV1到现代的DenseNet,都以不同的方式使用瓶颈层。这种技术有助于保持参数的数量,从而降低计算成本。
更广泛的卷积
加速卷积的另一个简单方法是所谓的宽卷积层。你的模型有越多的卷积层,它会变得越慢。然而,你需要大量卷积的表现力。实际上你使用的是较少,但较胖的图层,其中的“脂肪”意味着每层有更多的内核。它为什么有效?因为GPU或其他大规模并行机器对于处理单个大块数据而不是很多小数据很容易。
深度可分卷积
在深入研究这种方法之前,请注意,它非常依赖于在给定框架中实现的可分离卷积。就我而言,TensorFlow可能会对此方法进行一些特定的优化,而对于其他后端,如Caffe、CNTK或PyTorch,则尚不清楚。
这个想法是,不是在图像的所有通道上共同进行卷积,而是在每个通道上运行单独的2D卷积。 in_channels * channel_multiplier的中间通道连接在一起,并使用1x1卷积映射到out_channel。这样一来,训练的参数就会显著减少。
这不是那么简单。请注意,可分离的卷积有时不会受到训练。在这种情况下,请将深度乘数从1修改为4或8。还要注意,这些对于小型数据集(如CIFAR 10,而且在MNIST上)并不那么有效。另外要记住的是,不要在网络的早期阶段使用可分离的卷积。
CP分解和高级方法
上面显示的分解方案在实践中运行良好,也相当简单。有许多作品,其中包括V. Lebedev等。它们向我们展示了不同的张量分解方案,这大大减少了参数的数量,从而减少了所需的计算次数。
受启发,下面是如何在Keras中进行CP分解的代码片段:
这并不令人遗憾,但是它让你直观了解了它在代码中的外观。顺便说一下,文章顶部的图像是CP分解如何工作的图形解释。
应该注意TensorTrain分解和Tucker等这样的方案。对于PyTorch和NumPy,有一个名为Tensorly的库,为您执行所有实现。在TensorFlow中,没有任何东西可以接近它,但是,这里还有一个TensorTrain aka TT方案的实现。
结语
完整的代码目前可用作带有Tesla K80 GPU加速器的Colaboratory笔记本电脑。给自己做一份拷贝,并在代码中随意修改。
希望上述所有内容都对您有所帮助。