CnnDroid 优化加速原理

原文链接:GPU-based Acceleration of Deep Convolutional Neural Networks on Mobile Platforms
github地址:CNNDroid
1.前置知识 移动GPU架构 【CnnDroid 优化加速原理】现在的移动GPU一般由多个平行计算单元SC(shader core)组成。每个SC又由多个平行算数逻辑单元ALU(arithmetic and logic unit)组成。
CnnDroid 优化加速原理
文章图片


例如,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.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: 20execution_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,依次处理输入数组,得到对应的输出矩阵。
例子:例子



    推荐阅读