FC2カウンター FPGAの部屋 (目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る2(ソースコード)
FC2ブログ

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

FPGAの部屋

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

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る2(ソースコード)

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る1(学習編)”の続き。

前回は、Jupyter Notebook を使用して MNIST データセットを使った2層の全結合層を持ったニューラル・ネットワークの学習を行って、重みとバイアスをC のヘッダファイルとして出力した。今回は、その重みとバイアスのヘッダファイルを使用して、Vivado HLS でニューラル・ネットワークを構成していこう。それで、今回は完成したソースコードを貼っておく。

Vivado HLS のソースコードとしては、”「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化4(Vivado HLS)”の mnist_nn.c をベースにし、そこに、固定小数点の重みやバイアスをコピペして、更に指示子を追加した。主に、ARRAY_PARTITION complete を追加した。これは重みやバイアスが BRAM にアサインされるとポートが 2 つになってしまうので、性能向上ができないからだ。その代わり、膨大なLUT が必要となる。
mnist_nn.c のメインのソースコードを示す。

int mnist_nn(ap_ufixed<8, 0, AP_TRN, AP_WRAP> in[784], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]){
#pragma HLS DATAFLOW
#pragma HLS ARRAY_PARTITION variable=af2_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af2_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=out complete dim=0
#pragma HLS ARRAY_PARTITION variable=in complete dim=0

    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot1[50];
#pragma HLS ARRAY_PARTITION variable=dot1 complete dim=1
    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot2[10];
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1

    af1_dot1: for(int col=0; col<20; col++){
#pragma HLS PIPELINE II=1
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<784; row++){
            dot1[col] += in[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++){
#pragma HLS PIPELINE II=1
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<20; 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.c を示す。

// 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<8, 0, AP_TRN, AP_WRAP> in[784], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]);
int mnist_nn_float(float in[784], float out[10]);
int max_ap_fixed(ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]);
int max_float(float out[10]);

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

int main(){
    float x_test_float[NUM_ITERATIONS][784];
    ap_fixed<13, 7, AP_TRN, AP_WRAP> 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++)
            x_test_float[i][j] = (float)x_test[i][j];

    for(int i=0; i<NUM_ITERATIONS; i++){
        mnist_nn(&x_test[i][0], &result_ap_fixed[i][0]);
        mnist_nn_float(&x_test_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 = y_test[i];

        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<20; 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<20; 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<13, 7, AP_TRN, AP_WRAP> out[10]){
    int max_id;
    ap_fixed<13, 5, AP_TRN, AP_WRAP> 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);
}


MNIST のデータ・セットをC のヘッダファイルに変換した mnist_data.h は 1000 個のデータでは、GCC がセグメンテーション・フォールトで落ちてしまうので、100 のデータに変更した。
  1. 2019年03月29日 04:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/4484-f30e8365
この記事にトラックバックする(FC2ブログユーザー)