FC2カウンター FPGAの部屋 「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化4(Vivado HLS)

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化4(Vivado HLS)

「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化3”の続き。

Vivado HLSに持っていくための重みとバイアスのC の配列を生成するPython コードを紹介した。更に、Vivado HLSのテストベンチに必要なMNISTデータセットの一部をC の配列に変換するPython コードも紹介した。最後にMNISTデータセットを画像として見るためのPython コードも紹介した。
今回は、前回作成した重みやバイアス、MNISTデータセットの一部のヘッダファイルを使用して、Vivado HLS でMNISTの手書き数字を判定するハードウェアを作る。ただし、Softmax は実装が難しいし、最大値を取れば数字は推定できるので、実装していない。

さて、Vivado HLS 2017.1 で mnist_nn プロジェクトを作成した。そのプロジェクトを示す。
nn_fpga_ch5_13_170605.png

C シミュレーションを行った。
nn_fpga_ch5_14_170605.png

ハードウェア(固定小数点)では、id = 8 と id = 61 が間違っている。ソフトウェア(浮動小数点)では、id = 8 だけ間違っている。
ちなみに、id = 8 は数字の 5 だった。
nn_fpga_ch5_15_170605.png

id = 61 は数字の 8 だった。
nn_fpga_ch5_16_170605.png

この 8 を間違うのは人間にとってはいただけない気がするが、やはり飽和演算してしまった結果なのだろうか?
100 個やっての結果なので、ハードウェアの精度は 98 % で、ソフトウェアの精度は 99 % となった。

C コードの合成を行った。結果を示す。なお、ソースコードは下に貼ってあるが、指示子は1つも書いていないので、デフォルトのままである。
nn_fpga_ch5_17_170605.png

Latency は 278711 クロックとなった。これは、100 MHz 動作とすると、約 2.79 ms となる。28 x 28 画像の処理に 2.8 ms 程度なので、357 fps ということになり、カメラ画像の解析としては十分なスピードとなった。

リソース使用量を見ると、BRAM が38 個で 13 % 使用している。重みはここに入っているようだ。DSP48E が 1 個、FF が 447 個、LUT が 970 個使用している。全体的に見れば、少ないリソース使用量となっているのではないだろうか?

合成された HDLコードがどうなっているか?だが、C ソースコードは指示子を書いていないデフォルトの状態なので、配列はメモリのインターフェースになるので、アドレスとCE, WE, データの信号線がある。
nn_fpga_ch5_20_170606.png

Analysis 画面を示す。
nn_fpga_ch5_18_170605.png

af1_weight_V_U でBRAM が 36 個使用されているのが分かる。

NUM_ITERATIONS を 100 から 2 に変更して、C/RTL協調シミュレーションを行った。結果を示す。
nn_fpga_ch5_19_170605.png

C/RTL協調シミュレーション波形の全体を示す。
nn_fpga_ch5_21_170606.png

最初の in の入力部分を示す。
nn_fpga_ch5_22_170606.png

1番目の手書き数字認識の出力部分を示す。
nn_fpga_ch5_23_170606.png

out_V_address0 が 7 の時に最大値の 0b2c が出力されているのが分かる。結果は 7 となった。

Export RTL を行った。Vivado synthesis, place and route にチェックを入れたので、論理合成、インプリメントを行った結果を見ることができる。
nn_fpga_ch5_24_170606.png

タイミングはメットしている。

ソースコードの mnist_nn.cpp を示す。ソースコードはコメントを入れても 45 行だ。これは、元があったので、10分で書けた。

// mnist_nn.cpp
// 2017/06/01 by marsee
//

#include <stdio.h>
#include <ap_fixed.h>

#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

int mnist_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[784], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[10]){
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[784];
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot1[50];
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot2[10];

    buf_copy: for(int i=0; i<784; i++)
        buf[i] = in[i];

    af1_dot1: for(int col=0; col<50; col++){
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<784; row++){
            dot1[col] += buf[row]*af1_weight[row][col];
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)    // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<10; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<50; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        if(dot2[col] < 0)    // ReLU
            dot2[col] = 0;
        out[col] = dot2[col];
    }

    return(0);
}


最後にテストベンチの mnist_nn_tb.cpp を示す。

// mnist_nn_tb.cpp
// 2017/06/02 by marsee
//

#include <stdio.h>
#include <ap_fixed.h>

#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"
#include "mnist_data.h"

int mnist_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[784], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[10]);
int mnist_nn_float(float in[784], float out[10]);
int max_ap_fixed(ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[10]);
int max_float(float out[10]);

#define NUM_ITERATIONS    100 // C Simulation
// #define NUM_ITERATIONS    2 // C/RTL CoSimulation

int main(){
    float t_tran_float[NUM_ITERATIONS][784];
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> result_ap_fixed[NUM_ITERATIONS][10];
    float result_float[NUM_ITERATIONS][10];
    int max_id_hw, max_id_sw, max_id_ref;

    for(int i=0; i<NUM_ITERATIONS; i++)
        for(int j=0; j<784; j++)
            t_tran_float[i][j] = (float)t_train[i][j];

    for(int i=0; i<NUM_ITERATIONS; i++){
        mnist_nn(&t_train[i][0], &result_ap_fixed[i][0]);
        mnist_nn_float(&t_tran_float[i][0], &result_float[i][0]);
    }

    int errflag=0;
    for(int i=0; i<NUM_ITERATIONS; i++){
        max_id_hw = max_ap_fixed(&result_ap_fixed[i][0]);
        max_id_sw = max_float(&result_float[i][0]);
        max_id_ref = max_float(&t_test[i][0]);

        if(max_id_ref != max_id_hw){
            printf("id = %d, max_id_ref = %d, max_id_hw = %d\n", i, max_id_ref, max_id_hw);
            errflag = 1;
        }
        if(max_id_ref != max_id_sw){
            printf("id = %d, max_id_ref = %d, max_id_sw = %d\n", i, max_id_ref, max_id_sw);
            errflag = 1;
        }
    }
    if(errflag == 0)
        printf("No Error\n");

    return(0);
}

int mnist_nn_float(float in[784], float out[10]){
    float dot1[50];
    float dot2[10];

    af1_dot1: for(int col=0; col<50; col++){
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<784; row++){
            dot1[col] += in[row]*af1_fweight[row][col];
        }
        dot1[col] += af1_fbias[col];

        if(dot1[col] < 0)    // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<10; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<50; row++){
            dot2[col] += dot1[row]*af2_fweight[row][col];
        }
        dot2[col] += af2_fbias[col];

        if(dot2[col] < 0)    // ReLU
            dot2[col] = 0;
        out[col] = dot2[col];
    }

    return(0);
}

int max_ap_fixed(ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[10]){
    int max_id;
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int max_float(float out[10]){
    int max_id;
    float max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

  1. 2017年06月06日 04:09 |
  2. DLNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog19.fc2.com/tb.php/3821-966f8809
この記事にトラックバックする(FC2ブログユーザー)