在深度学习背景下,矩阵运算作为计算的基础部分,是决定了神经网络的运行速度的根本。对于绝大部分机器学习的算法工程师,调用框架去搭建网络就可以了,不太需要考虑到矩阵计算或者向量计算的层面。但是如果算法工程师需要深入研究模型加速,则必不可少地要对矩阵计算进行深入的理解和研究。
几种加速的方案
加速神经网络实际上有若干方法:
- 硬件加速 硬件加速实际上是所有加速方法中,最最有效的一种。当研究模型加速的研究者吭哧吭哧埋头苦干了一年地捣鼓出来了一个加速20%的算法,可能硬件厂商优化了芯片设计或者某些硬件底层支持使得浮点计算加速了30%。本人观点是,作为算法工程师一定要紧密跟进大厂的底层计算库更新。
举个例子,目前做物体检测的模型,无论是FasterRCNN还是SSD,都需要比较大的Backbone CNN做特征提取,i.e. VGG, ResNet等等。但是一旦涉及到具体的公司项目或者业务,不可避免地需要对时间性能做一定的要求。如果埋头苦干想着通过调参做到减小模型同时避免性能降低,就会费非常大的力气才能达到某种特定的时间要求。但是如果将一定的注意力放在底层计算库上,很多时候事情会变得简单很多。
对于GPU计算,更新到较新的CUDA或者更新到较新的cuDNN有非常大的速度提高,可以从NVidia的一些对比数据看到,例如这个链接,还有这个链接,分别说明了CUDA8以及cuDNN7的加速效果。想想如果这年头还在用cuDNN5,就真的玩不转了。
而对于CPU计算,则一定是盯着Intel,看看MKL和其相关发布。MKL的速度至少是OpenBLAS的两倍,AtLas的一点五倍,毫无疑问是BLAS库中的王者
-
框架选用 当底层计算库大致相同的话,选用不同的框架实际上代表的是不同的底层库调用实现。其中肯定或多或少有优劣之分,例如tensorflow对cuDNN的更新就比较差一些, caffe在RNN实现方面非常糟糕,等等。但是如果算法工程师能够通过自行编写一些Layer(或者称Operator)去调用性能更好的底层库借口,实际上也可以大大提高训练速度和推断速度。
-
模型调优 一切硬件和框架问题都应该根据自身情况去选取(根据业务选择CPU/GPU,根据条件安装合适的CUDA和cuDNN或者BLAS库,根据工程师的能力选用不同的框架),然后就进入模型调优来加速的阶段。关于模型的论文这些年数不胜数,AlexNet,VGG,ResNet,DenseNet,实际上都是在提高模型表达能力的同时尽量降低计算复杂度。后来更有MobileNet等主打速度优势的网络设计。很多时候不需要完整套用论文的网络,而是理解网络的卖点然后对自己的网络进行重新设计。
例如MobileNet能够比较快速的进行计算,核心在于Depth-wise Convolution和1*1 Convolution的结构取代普通的3*3卷积,同时这个结构理论上能够保证信息跨通道信息融合
- 压缩加速 最后终于到了本文的重点。在硬件条件和代码实现都到了比较稳定高效的条件下,要进一步对算法模型进行加速,就需要对Compression/Acceleration类的算法进行研究了。一篇论文能够代表近年来模型压缩加速的主流思路,名为Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding。其中起到加速作用的就是网络剪枝部分,核心思想是利用神经网络全连接的稀疏性,将响应较弱(即权值较小)的连接断开,然后用稀疏矩阵计算库进行矩阵乘法的加速。从本人的实践大概得到的结论是,可以剪掉70%~90%的弱响应连接。对于一个输出节点数比较大的全连接层,可以大大节省模型体积。但是实现这个压缩加速算法之后,我发现对于很多场合这个算法无法使用,因为能够支持稀疏矩阵运算的库目前比较好的似乎只有MKL的sparseBLAS和cuSPARSE。这样,实际上就回到了第一个点 :)
使用MKL SparseBLAS的例子,手册见链接
mkl_cspblas_?csrgemv()
其中cspblas
就是MKL里面的稀疏库中缀,?csrgemv
这几个字母分别有各自含义。
?
代表精度,一般为s,dcsr
代表的是一种稀疏矩阵的表示方式。稀疏矩阵有大量的0元素,所以把所有元素按照矩阵排列全部存起来显然不划算,所有有一些其他方式的稀疏表示。最简单的一种就是COO
,意思实际上是把非零元素的三元组,(值,x坐标,y坐标) 存起来,只要零元素的占比大于三分之二,这样的储存方式就是划算的。而CSR
则是在COO
基础上对行坐标和列坐标进行进一步改进的一种,是目前比较主流的稀疏表示方式。在搜索引擎搜一下可以看到不少相关资料,例如这个博客讲解得比较清晰。gemv
则是代表GEneral Matrix Vector calculation
, 矩阵和向量相乘。同理,gemm
就是矩阵和矩阵相乘。这两个类型基本上就是神经网络中使用最频繁最耗时的计算。
移动端的考验
上面提到的若干种加速神经网络的方法,实际上都有一定的硬件或者软件依赖。然而在移动端,常常没有比较充裕的硬件资源和软件底层库支持,但是移动端也不是完全没有办法对矩阵运算进行加速:)
- 选取可用的BLAS库: 目前比较方便在移动端使用的BLAS库是OpenBLAS,虽然远比不上MKL等大厂出产深度优化的BLAS,但是也比单纯使用for循环快速得多。
- 循环展开:这一个方法广泛运用在各种底层库,实际效果也是比较明显.
- neon优化:这是一种汇编层面的高级优化策略,使用寄存器暂存一些变量后,在寄存器进行向量乘法和加法,例如卷积核为3*3的一个卷积层,把卷积核展开为1*9的向量存放在寄存器中,把原图经过im2col后对应需要计算的滑动窗部分也放入寄存器然后进行计算
简单的循环展开例子如下
for(int i=0; i<1000; i++) {
// do something with index i
}
//可以展开成为
for(int i=0; i<1000/8; i+=8) {
// do something with index i ~ i+7
}
//最容易实现的但常常被忽略的方案
腾讯的ncnn和百度的paddle-mobile都做了neon优化。有兴趣的朋友可以看看源码。
小结
对于神经网络加速