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)組成。


例如,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網絡

優化效果見下圖


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,依次處理輸入數組,得到對應的輸出矩陣。

例子:例子


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章