Mobile AI Compute Engine (MACE) 是一个专为移动端异构计算平台优化的神经网络计算框架,旨在深度神经网络部署在移动端,是一个SoC上的神经网络实现。主要涉及的硬件资源主要包括CPU、GPU、DSP,对应的技术为ARM NEON、OPEN CL、HVX。
项目地址:https://github.com/XiaoMi/mace
本文先较为详细的分析一下kernel中的1x7卷积,再推广到另外3个卷积实现中。作为ARM卷积篇的第一文,先介绍一下基本的内存结构。在MACE中,1*7卷积的接口为:
// Ho = 1, Wo = 4, Co = 4void Conv2dNeonK1x7S1(constfloat *input,
constfloat *filter,
const index_t *in_shape,
const index_t *out_shape,
float *output);
三个浮点指针input、output、filter分别指向了输入tensor、输出tensor和卷积核kernel。in_shape和out_shape则分别表示输入和输出tensor的维度。一般tensor为4维,每个维度分别表示为 batch size x channel num x image height x image width。举个例子,CNN网络中某一层特征图大小为256x192(宽x高),特征图数目为128,batch大小设置为64.则该tensor的大小可以表示为 64x128x192x256。
而在CNN中,考虑输出层的所有通道的话,卷积核是一个4维的tensor,每个维度分别是output channel num x input channel num x kernel height x kernel width 。举个例子,CNN网络这里写代码片中C1层有128个特征图吗,C2层有256个特征图。C1到C2用3x3的卷积核做特征提取和映射时,卷积核tensor的大小可以表示为256x128x3x3。了解了这些基本内容后,就可以开始看源码了。
Tensor大小和整体结构
根据上面的介绍,MACE为了索引具体某个batch的某个通道图,先计算出了image size和batch size,如下所示:
const index_t in_image_size = in_shape[2] * in_shape[3];
const index_t out_image_size = out_shape[2] * out_shape[3];
const index_t in_batch_size = in_shape[1] * in_image_size;
const index_t out_batch_size = out_shape[1] * out_image_size;
先说明一下循环层次,用伪代码表示:
for batch +1 (源码36行)
forout_channel +4 (源码37行)
forin_channel +1 (源码53行)
for out_height +1 (源码75行)
for out_width +4 (源码76行)
伪代码中最后的+表示循环索引的步长,因为每个输出通道是由所有的输入通道分别做卷积再求和得到的,再加上batch
数,所以是5层循环。
注意一下函数签名前面的注释,该注释表明输出的特征图宽度和通道数的步长都为4。宽度步长为4是因为使用了NEON指令,可以一次处理4个浮点数。输出通道步长为4应该是为了手动循环展开,让编译器可以方便更好的做OOO(Out of Order)。
// Ho = 1, Wo = 4, Co = 4
因此 if (m + 3 < out_channels) 这句是为了保证输出通道不被4整除时,可以有代码去处理不足4的部分。