CnnDroid 优化加速原理

原文链接:GPU-based Acceleration of Deep Convolutional Neural Networks on Mobile Platforms

github地址:CNNDroid

1.前置知识 移动GPU架构

现在的移动GPU一般由多个平行计算单元SC(shader core)组成。每个SC又由多个平行算数逻辑单元ALU(arithmetic and logic unit)组成。

CnnDroid 优化加速原理_第1张图片

例如,Samsung Exynos 5433芯片架构如上图。芯片由ARM Cortex-A53/A57 CPU 和 Mali T-760 GPU构成。每个T-760 GPU中的SC包含两个128-bit的ALU。每个128-bit的ALU能够执行SIMD(single instruction multiple data)计算。例如,每个128-bit的ALU能够同时执行两个64-bit,或4个32-bit,或8个16-bit的计算。而PowerVR GPU的每个SC由多个16-bit和32-bit的ALU构成。

移动GPU和电脑上的有很大不同。其有很大的面积限制,因此移动GPU都被设计为只有较少的核心。然而,较少的核心让并行线程管理更加简单。例如,在某些移动GPU的每个线程中都有其自己的程序计数器,因此branch divergence不是问题。另一个主要不同是,电脑上CPU 和 GPU 都有单独的内存,而移动芯片CPU和GPU共用同一块主内存。因而,不会有在CPU和GPU间复制数据的限制。

2.CNN on Mobile GPU

以下均基于图片识别任务的cnn网络

优化效果见下图

CnnDroid 优化加速原理_第2张图片

2.1. GPU-Based Basic Parallel Acceleration 

basic parallel acceleration方法是仍然按照序列计算每幅图片,但在对每幅图片卷积时采用并行计算。当存在ReLU层时,它的计算就被嵌入到两次卷积操作之间,而没有明显的耗时。这是通过利用CPU的空闲达到的。当GPU在卷积计算第i幅图片时,第(i-1)幅图的ReLU层就会被CPU计算。因此CPU与GPU同时处于工作状态。

2.2. GPU-Based Basic SIMD Acceleration

在前面的GPU架构中有提到,每个GPU的SC都包含多个SIMD ALU。每个ALU都能在一个时钟周期中处理多个计算操作。例如128-bit的SIMD ALU能同时执行4个32-bit的浮点计算。我们使用这个特性加速性能。

通常的卷积核尺寸都是单数,不能被4整除。因此,为了能完全利用SIMD单元,我们对输入的卷积层做了如下操作。因为输入矩阵的channel数能被4整除(RGBA),所以我们重新排列矩阵的维度,将channel移动到最低维,而height和width移动到更高维度(即[W,H,C]->[C,W,H])。考虑到我们进行的图片的维度变换,因此在计算时需要沿着chanel轴进行计算。(我的理解是同时对4个channel进行卷积)

2.3. GPU-Based Advanced SIMD Acceleration

在上个方法中,尽管读取数据时通过4维的向量,但每个线程只有一个元素在输出时计算。为了减少线程数,以减少帧与卷积核加载到GPU缓存的次数。我们每次在每个线程中同时计算多个元素(4个或8个)。

流程:

1. 解析net.txt 文件,生成多个卷积网络计算图。每一个网络层生成对应的层对象,放入一个ArrayList中

root_directory: "/sdcard/Cifar10/"

allocated_ram: 20

execution_mode: "parallel"

auto_tuning: "off"

layer {
  type: "Convolution"
  name: "conv1"
  parameters_file: "model_param_conv1.msg"
  pad: 2
  stride: 1
  group: 1
}


layer {
  type: "Pooling"
  name: "pool1"
  pool: "max"
  kernel_size: 3
  pad: 0
  stride: 2
}

layer {
	type: "ReLU"
	name: "ReLU1"
}

layer {
  type: "Convolution"
  name: "conv2"
  parameters_file: "model_param_conv2.msg"
  pad: 2
  stride: 1
  group: 1
}

2. 在生成Convolution层和FullyConnected层时,会通过其中的parameters_file找到对应的权重参数文件,并将其赋值到层对应的权重矩阵中

        float[][][][] inputBatch = new float[1][3][mWidthPixel][mHeightPixel];
        ImageView img = (ImageView) findViewById(R.id.imageView);
        TextView text = (TextView) findViewById(R.id.textView);
        
        // 读取图像源文件 并缩放到预定大小(32)
        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.airplane);
        Bitmap bmp1 = Bitmap.createScaledBitmap(bmp, imgSize, imgSize, true);
        Bitmap bmp2 = Bitmap.createScaledBitmap(bmp, mWidthPixel, mHeightPixel, false);
        img.setImageBitmap(bmp1);

        for (int j = 0; j < mWidthPixel; ++j) {
            for (int k = 0; k < mHeightPixel; ++k) {
                // 读取图像的pixel值,并归一化 means矩阵由网络初始化时读取mean.msg生成
                // 注意生成的矩阵顺序为 [batch, channel, width, height]
                int color = bmp2.getPixel(j, k);
                inputBatch[0][0][k][j] = (float) (android.graphics.Color.blue(color)) - mean[0][j][k];
                inputBatch[0][1][k][j] = (float) (android.graphics.Color.blue(color)) - mean[1][j][k];
                inputBatch[0][2][k][j] = (float) (android.graphics.Color.blue(color)) - mean[2][j][k];
            }
        }

        // 调用网络计算
        float[][] output = (float[][]) myConv.compute(inputBatch);

3.  循环存储网络层的ArrayList,依次处理输入数组,得到对应的输出矩阵。

例子:例子


你可能感兴趣的:(移动端深度学习)