![8044925ffea8dc2fb4000bcd3784d4c7.png](https://i-blog.csdnimg.cn/blog_migrate/f4991291bb7158a55d14a5e2640b1f7a.jpeg)
在CNN架构中,大部分时间都是由卷积层消耗的。我们将讨论Winograd算法,它可以将浮点乘法的数量减少2.25倍。请参阅http://arxiv.org/abs/1509.09308。
卷积是如何实现的呢?可能你最简单直观的想法是for循环,但这样的卷积太慢。为了有效利用CPU缓存和参考局部性。(局部参考性也称为局部性原理,是指处理器很有可能会在短时间内重复访问同一组存储器位置。),我们可以将卷积运算转换为矩阵乘法。
1. 更快的卷积算法im2col
我们将展示一种将卷积运算转换为矩阵乘法的方法。这有利于以更多内存使用为代价更快地计算。我们使用im2col操作将输入图像或批处理转换为矩阵,然后我们将此矩阵与reshape的内核相乘。然后在最后,我们使用col2im操作将该相乘后的结果矩阵reshape为图像。
卷积基本上是过滤器内核和移动窗口选择的局部区域(采样)之间的点积,采样的小块(patch)与内核大小是相同的。如果我们在内存上扩展所有可能的窗口并将点积作为矩阵乘法运行,会发生什么。那将可能导致200倍或更多的加速,以更多的内存消耗为代价。(使用BLAS库来执行矩阵乘法,例如CuBLAS(GPU)或Intel MKL(CPU),它们针对矩阵乘法进行了优化。)
![effc03f2910fadf22d927c6b2a577fbc.png](https://i-blog.csdnimg.cn/blog_migrate/57bad17fa9fe27001726319592bc00f1.jpeg)
例如,如果大小为[227x227x3]步幅4和填充0的输入与11x11x3滤波器进行卷积,那么我们将在输入中采样[11x11x3]像素块并将每个像素块拉伸为大小 11 * 11 * 3 = 363的列向量。
对于具有步幅4和填充0的大小为227的输入,沿宽度和高度会有((227-11)/ 4)+1 = 55个结果位置,得到尺寸为[363×3025]的输出矩阵X_col。这里的每列都是伸展的感受野,总共有55 * 55 = 3025个。
总结一下如何计算im2col输出大小:
[img_height, img_width, img_channels] = size(img);
newImgHeight = floor(((img_height + 2*P - ksize) / S)+1);
newImgWidth = floor(((img_width + 2*P - ksize) / S)+1);
cols = single(zeros((img_channels*ksize*ksize),(newImgHeight * newImgWidth)));
CONV层的权重以类似的方式伸展成行。例如,如果有96个大小为[11x11x3]的过滤器,则会得到一个大小为[96 x 363]的矩阵W_row(11x11x3 = 363)。
![6de65c279dc917aedb8db8b914fb00b6.png](https://i-blog.csdnimg.cn/blog_migrate/0f6eb7aff912f1a765b71fc1a4b95c4e.jpeg)
在转换图像和内核之后,卷积可以实现为简单的矩阵乘法,在我们的例子中,W_col [96 x 363]乘以X_col [363 x 3025]得到的矩阵[96 x 3025],最后reshape为[55x55x96]。
![ab58f1d50a78df8927681a3e05286e49.png](https://i-blog.csdnimg.cn/blog_migrate/c44e9a55bad5a16d665847b9cf6175dd.jpeg)
2. Winograd算法
假设我们有输入图像˚F大小为4和 过滤器大小为3。
![c0b23108edef1072462e1c3eed355180.png](https://i-blog.csdnimg.cn/blog_migrate/42a3b0f3870649921b4809460babd634.png)
然后,使用上面介绍的im2col函数将输入图像转换为
![6fbbd821a3494428225896ac19dee58b.png](https://i-blog.csdnimg.cn/blog_migrate/b4ddcf354bcb4e857581b82221678ef1.png)
![d168849254117cb92d817f431a8f2d96.png](https://i-blog.csdnimg.cn/blog_migrate/d6b4e2007f4eb5940bf65dfe23a6a003.png)
那么,Winograd怎样才能进一步提高速度呢?它舍弃使用点积,而是使用下面的公式计算结果矩阵。
![b738ce25c644841596095ac4846aa714.png](https://i-blog.csdnimg.cn/blog_migrate/71c8b76208e17533059b0c4773655233.png)
也就是,
![5a15d22392de489c63d8cb9f48f8e125.png](https://i-blog.csdnimg.cn/blog_migrate/1da081fccf12a13690fa419043489f54.png)
其中,
![5c3f30476cd4635995a42cd29f5243c2.png](https://i-blog.csdnimg.cn/blog_migrate/34a08f050f33dd0e94e06175a161bb7b.png)
这样我们就可以得到m1,m2,m3,m4的值。然后用它们来计算卷积而无需计算矩阵的点积。明显,经过这样的变换后,在每个卷积运算时不需要多次计算(g0 + g1 + g2)/ 2和(g0-g1 + g2)/ 2的值,因为滤波器的值是一样的。我们可以在训练网络期间在卷积之前计算一次,并且可以在推理期间预先计算保存。
使用这个算法,我们需要 4次 ADD和4次 MUL操作计算m1,m2,m3,m4,然后基于计算好的m1,m2,m3,m4的值,使用4 个ADD操作得到结果。而在进行普通的点积时,我们将进行6次MUL操作而不是4次。明显Winograd可以将计算成本高昂的MUL操作减少1.5倍,这对速度的提高是非常重要的。
在上面的例子中,我使用了F(4,3),即f(4)和g(3),它需要2次卷积。最小1D算法 F(m,r)与其自身嵌套以获得最小2D算法F(mxm,rxr)。如果我们尝试使用f(4,4)和g(3,3),这需要4次卷积,Winograd算法使用4 * 4 = 16个MUL,对比普通的卷积使用2 * 2 * 9 = 36个MUL,这样可以将MUL减少2.25倍。
![7a084160d96fc4f93a9c0281b21f8302.png](https://i-blog.csdnimg.cn/blog_migrate/65e6ac499442df2bfde6d1ff29d89f2f.png)
【参考文章】
- https://leonardoaraujosantos.gitbooks.io/artificial-inteligence/content/making_faster.html?source=post_page---------------------------
2. https://medium.com/@dmangla3/understanding-winograd-fast-convolution-a75458744ff
![f6350a8c6507d4172e6be05b60d84088.png](https://i-blog.csdnimg.cn/blog_migrate/f7ce5fadb49bfa84b8e032298c74a9a2.jpeg)