从 CNN 性能优化说起(一)
本文详解贾杨清于2014年写了一篇简短的 memo,描述了他的通过把 CNN 变成矩阵乘的加速方法 https://github.com/Yangqing/caffe/wiki/Convolution-in-Caffe:-a-memo 。这个被扬清谦虚地称为“临时方案”的做法被后来的各种深度学习框架复现,甚至影响了深度学习编译器的研究。
本文复用了一篇很不错但读者不多的英语博客中的叙事顺序和图示;其原文在 https://sahnimanas.github.io/post/anatomy-of-a-high-performance-convolution ,并且修正了一些借用的图示。此外,本文增加对 convolution 计算过程的介绍,以承上。并且简单介绍 TVM 和 MLIR 背后的思路,以启下。
Convolution 的计算过程
先得知道 convolution layer 的计算过程才好优化。Convolution layer 对一幅图像 A 做 convolution,结果输出为图像 B。所谓的 convolution 操作是指给定一个 K x K 的小矩阵(kernel),把图 A 中每个 K x K 个像素构成的小片(patch)中的每个像素值乘以 kernel 中对应的数值,然后所有乘积加起来,作为图像 B 中一个像素的值。大致过程如下图。其中左边的大矩阵是图像 A,右边的大矩阵是图像 B,中间的 3×3 的小矩阵是 kenel。
</p>
<p>实际上一副图像可能有多个 channels。比如彩色图像通常有 RGB 三个channel。实际操作里,经常是图像A有 Ca 个 channels,而每个 kernel 是 Ca x K x K 的一个立方体 —— 这里请注意,kernel 的 channel 数和图像 A 的一样,都是 Ca。这样,广义的 convolution 是大小为 CaxKxK 的 patch 和同样大小的 kernel 做 element-wise 乘,然后结果相加,成为B中的一个像素。这样 B 的 channel 数是 1。</p>
<p>进一步泛化,我们可以假设有 Cb 个大小为 Ca x K x K 的 filters 参与 convolution 计算。这样得到的图像 B 的 channel 就是 Cb 了。这个泛化在深度学习里很常见。因为网络里经常有连续多层 CNN layers,每层的输入和输出的 channels 数都大于 1。</p>
<figure data-size=)

贾杨清的加速法
扬清在 Caffe 的 memo 里记录的方法是把图像 A 里的每个 patch 都展开成一个向量,然后把所有向量拼成一个大矩阵 A’。类似的,把 Cb 个 kernel 也展开成一个大矩阵 K’。则 convolution 就可以变成这两个矩阵相乘 A’ x K’。得到的大矩阵就是 B。
