https://petewarden.com/2015/04/20/why-gemm-is-at-the-heart-of-deep-learning/
我大部分时间都在担心如何通过神经网络更快,更省电地进行深度学习。实际上,这意味着专注于一个名为GEMM的函数。它是BLAS(基本线性代数子程序)库的一部分,该库于1979年首次创建,直到我开始尝试优化神经网络之前,我从未听说过它。为了解释它为什么如此重要的原因,这是我的朋友杨庆佳论文的图表:
这正在打破使用Alex Krizhevsky的Imagenet架构进行典型的深度卷积神经网络进行图像识别的时间。以fc(对于全连接)或conv(对于卷积)开头的所有层都是使用GEMM实现的,几乎所有时间(GPU版本的95%,CPU的89%)都花在了这些层上。
那么什么是GEMM?
它代表通用矩阵到矩阵乘法,并且基本上可以上所说的那样进行操作,将两个输入矩阵相乘得到一个输出矩阵。
它与我在3D图形世界中习惯的矩阵运算类型之间的区别在于,它处理的矩阵通常很大。例如,典型网络中的单层可能需要将256行1152列矩阵与1152行192列矩阵相乘以产生256行192列的结果。天真的,这需要5700万(256 x 1152x 192)个浮点运算,并且在现代体系结构中可能有数十个这样的层,因此我经常看到需要数十亿个FLOP来计算单个帧的网络。这是我绘制的示意图,以帮助我直观地了解其工作原理:
全连接层:
全连接层是已经存在了数十年的经典神经网络,它可能最容易get “如何使用GEMM”
FC层的每个输出值查看输入层中的每个值,然后将它们全部乘以该输入索引对应的权重,然后对结果求和以得到其输出。根据上图,它看起来像这样:
There are ‘k’ input values, and there are ‘n’ neurons:
each one of which has its own set of learned weights[k rows] for every input value.
There are ‘n’ output values, one for each neuron, calculated by doing a dot product of its weights and the input values.
卷积层:
对于卷积层使用GEMM并不是一个显而易见的选择。
A conv layer 将其输入视为二维图像,每个像素具有多个通道,非常类似于具有宽度,高度和深度的经典图像。与我以前处理过的图像不同,通道数可以是数百个,而不仅仅是RGB或RGBA!
卷积运算通过获取多个“内核”权重来产生其输出(The convolution operation produces its output by taking a number of ‘kernels’ of weights)并将它们应用于整个图像。输入图片和单个内核如下所示:
每个内核是另一个三维数字数组,其深度与输入图像相同,但是宽度和高度要小得多,通常约为7×7。为了产生结果,将内核应用于整个输入图像上的点网格( a kernel is applied to a grid of points across the input image)。At each point where it’s applied,,所有corresponding输入值和权重都将相乘,然后求和以在该点产生一个输出值。这是外观上的样子:
您可以将此操作视为边缘检测器(an edge detector.)之类的东西。内核包含权重的模式(The kernel contains a pattern of weights),当输入图像中要查看的部分具有相似的模式时,它将输出较高的值。如果输入与模式不匹配,则结果是该位置的数字较小。以下是网络的第一层学习的一些典型模式,这些模式是由Caffe所提供的,并在NVIDIA博客上进行了介绍:
and when the part of the input image it’s looking at has a similar
pattern it outputs a high value. When the input doesn’t match the
pattern, the result is a low number in that position.
因为第一层的输入是RGB图像,所以所有这些内核也可以可视化为RGB,它们显示了网络正在寻找的原始模式。这96个内核中的每个内核都以网格模式应用到整个输入中,结果是一系列96个二维数组,这些数组被视为深度为96通道的输出图像。如果您习惯于像Sobel运算符这样的图像处理操作,则可能可以想象其中的每一个有点像针对图像中不同的重要图案进行了优化的边缘检测器,因此每个通道都是这些图案的位置的映射跨输入发生。
Because the input to the first layer is an RGB image, all of these kernels can be visualized as RGB too, and they show the primitive patterns that the network is looking for. Each one of these 96 kernels is applied in a grid pattern across the input, and the result is a series of 96 two-dimensional arrays, which are treated as an output image with a depth of 96 channels. If you’re used to image processing operations like the Sobel operator, you can probably picture how each one of these is a bit like an edge detector optimized for different important patterns in the image, and so each channel is a map of where those patterns occur across the input.
您可能已经注意到,我对于将内核应用于哪种网格一直不太清楚。对此的关键控制因素是一个称为“步幅”的参数,该参数定义了内核应用程序之间的间距。例如,如果跨度为1,则256×256输入图像将在每个像素上应用内核,并且输出的宽度和高度与输入的宽度和高度相同。跨度为4时,同一输入图像将仅每四个像素应用一次内核,因此输出将仅为64×64。典型的步幅值小于内核的大小,这意味着在可视化内核应用程序的图中,许多步幅实际上会在边缘重叠。
GEMM如何为卷积工作
这似乎是一项非常专业的操作。最后,它涉及很多乘法和求和,例如全连接层,但尚不清楚如何或为什么我们应该将其转换为GEMM的矩阵乘法。我将在最后讨论动机,但是这里是如何用矩阵乘法来表示运算的。
**
- 第一步是将来自图像的输入(实际上是3D数组)转换为2D数组.
2我们可以将其视为矩阵。应用每个内核的地方是图像中的一个三维三维立方体,因此我们将输入值的那些立方体中的每个立方体作为一个单独的列复制到矩阵中。我相信这是im2col( image-to-column, I believe from an original Matlab function)我相信是从原始的Matlab函数中得出的,这是我如何将其可视化的方法:
现在,如果您是像我这样的图像处理极客,那么,如果步幅小于内核大小,则在执行此转换时发生的内存大小扩展可能会让您感到震惊。这意味着包含在重叠内核位置中的像素将在矩阵中复制,这似乎效率很低。您必须相信我, this wastage is outweighed by the advantages though.
- 现在您已经有了矩阵形式的输入图像,您可以对每个内核的权重执行相同的操作,将3D多维数据集序列化为行,作为乘法的第二个矩阵。最终的GEMM如下所示:
“ k”是每个补丁和内核中值的数量,因此是内核宽度 * 内核高度 * 深度。产生的矩阵列高是“Number of patches”,行宽是“Number of kerne”行宽。通过将the number of kernels dimension作为深度,然后根据补丁在输入图像中的原始位置,将补丁分成行和列,该矩阵实际上通过后续操作被视为3D阵列。
Here ‘k’ is the number of values in each patch and kernel, so it’s kernel width * kernel height * depth. The resulting matrix is ‘Number of patches’ columns high, by ‘Number of kernel’ rows wide. This matrix is actually treated as a 3D array by subsequent operations, by taking the number of kernels dimension as the depth, and then splitting the patches back into rows and columns based on their original position in the input image.
Why GEMM works for Convolutions
希望您现在可以看到如何将卷积层表示为矩阵乘法,但是为什么这样做仍不清楚。简短的答案是,事实证明,Fortran科学程序员的世界已经花费了数十年的时间 优化代码 以执行大型矩阵到矩阵的乘法,并且非常规则的内存访问模式所带来的收益超过了浪费的存储成本。
Nvidia的这篇论文很好地介绍了您可以使用的一些不同方法,但是它们也描述了为什么他们最终选择了GEMM的修改版本作为他们偏爱的方法。能够同时针对相同的内核批处理大量输入图像还有很多优点,本文有关Caffe控制的论文将这些效果很好地使用了。 GEMM方法的主要竞争对手是使用傅立叶变换在频率空间中进行运算, but the use of strides in our convolutions makes it hard to be as efficient.
由于深层网络已被证明对语音,NLP和计算机视觉的大量应用有用,因此我希望在未来几年内看到巨大的进步,就像对3D游戏的广泛需求引发了一场革命一样。通过推动顶点和像素处理操作的革命来实现GPU。
summary:
operation:imgtocol
merit:矩阵到矩阵的乘法,并且非常规则的内存访问模式所带来的收益超过了浪费的存储成本。