FC2カウンター FPGAの部屋 白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)

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

FPGAの部屋

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

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)

白線追従走行用畳み込みニューラルネットワークの製作17(白線追従走行用画像データから一部を抜き出して画像にする)”の続き。

前回は、白線追従走行用の画像データを確認するために、白線追従走行用画像データから一部を抜き出して画像にするPython コードを作成した。今回は、いよいよVivado HLS 用のファイルがそろったので、Vivado HLS 2016.4 でプロジェクトを作成し、C シミュレーションを行う。

Vivado HLS 2016.4 で straight_conv_nn2 プロジェクトを作成した。これは、期待を込めてZYBO 用のプロジェクトとした。
wlt_cnn_85_170829.png

C シミュレーションを実行した。
wlt_cnn_86_170829.png

ログを示す。

Starting C simulation ...
/opt/Xilinx/Vivado_HLS/2016.4/bin/vivado_hls /home/masaaki/Vivado_HLS/ZYBO/straight_conv_nn2/solution1/csim.tcl
INFO: [HLS 200-10] Running '/opt/Xilinx/Vivado_HLS/2016.4/bin/unwrapped/lnx64.o/vivado_hls'
INFO: [HLS 200-10] For user 'masaaki' on host 'masaaki-VirtualBox2' (Linux_x86_64 version 4.4.0-92-generic) on Tue Aug 29 04:13:06 JST 2017
INFO: [HLS 200-10] On os Ubuntu 16.04.3 LTS
INFO: [HLS 200-10] In directory '/home/masaaki/Vivado_HLS/ZYBO'
INFO: [HLS 200-10] Opening project '/home/masaaki/Vivado_HLS/ZYBO/straight_conv_nn2'.
INFO: [HLS 200-10] Opening solution '/home/masaaki/Vivado_HLS/ZYBO/straight_conv_nn2/solution1'.
INFO: [SYN 201-201] Setting up clock 'default' with a period of 10ns.
INFO: [HLS 200-10] Setting target device to 'xc7z010clg400-1'
INFO: [SIM 211-2] *************** CSIM start ***************
INFO: [SIM 211-4] CSIM will launch GCC as the compiler.
Compiling ../../../straight_conv_nn_tb.cpp in debug mode
Generating csim.exe
id = 25, max_id_ref = 1, max_id_hw = 2
id = 25, max_id_ref = 1, max_id_sw = 2
id = 26, max_id_ref = 1, max_id_sw = 2
id = 30, max_id_ref = 1, max_id_sw = 2
id = 35, max_id_ref = 1, max_id_sw = 2
INFO: [SIM 211-1] CSim done with 0 errors.
INFO: [SIM 211-3] *************** CSIM finish ***************
Finished C simulation.


25, 26, 30, 35 の画像を見ると、直進指定なのだが、曲がってもおかしくないという画像だった。トレーニング用画像の作りに問題があるかもしれない?
wlt_cnn_87_170829.png

さて、ハードウェアが間違ったのが150 個のうちの 1 個、ソフトウェアが間違ったのが、150 個のうちの 4 個だ。ハードウェアの方が整合が良いという結果になった。ハードウェアの精度が 149 / 150 x 100 = 99.33 % で、ソフトウェアの精度が 146 / 150 x 100 = 97.33 % となった。

straight_conv_nn2.cpp を貼っておく。

// straight_conv_nn2.cpp
// 2017/08/28 by marsee
// 畳み込み層のカーネル数 2
//

#include <ap_fixed.h>

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

#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3

int straight_conv_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[ALL_PIXELS], ap_fixed<127, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]){
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];

    buf_copy1: for(int i=0; i<ROW_PIXELS; i++)
        buf_copy2: for(int j=0; j<COULMN_PIXELS; j++)
            buf[i][j] = in[i*COULMN_PIXELS+j];

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){    // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0)    // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

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

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

        out[col] = dot2[col];
    }

    return(0);
}


straight_conv_nn_tb.cpp を貼っておく。

// straight_conv_nn_tb.cpp
// 2017/08/28 by marsee
// 畳み込み層のカーネル数 2
//

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

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

#include "straight_data.h"

#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3

int straight_conv_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[ALL_PIXELS], ap_fixed<127, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]);
int straight_conv_nn_float(float in[ALL_PIXELS], float out[NUM_OF_OUTPUT]);
int max_ap_fixed(ap_fixed<127, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]);
int max_float(float out[NUM_OF_OUTPUT]);

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

int main(){
    float t_tran_float[NUM_ITERATIONS][ALL_PIXELS];
    ap_fixed<127, AP_TRN_ZERO, AP_SAT> result_ap_fixed[NUM_ITERATIONS][NUM_OF_OUTPUT];
    float result_float[NUM_ITERATIONS][NUM_OF_OUTPUT];
    int max_id_hw, max_id_sw, max_id_ref;

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

    for(int i=0; i<NUM_ITERATIONS; i++){
        straight_conv_nn(&t_train[i][0], &result_ap_fixed[i][0]);
        straight_conv_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 straight_conv_nn_float(float in[ALL_PIXELS], float out[NUM_OF_OUTPUT]){
    float buf[ROW_PIXELS][COULMN_PIXELS];
    float conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    float pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    float dot1[100];
    float dot2[NUM_OF_OUTPUT];

    buf_copy1: for(int i=0; i<ROW_PIXELS; i++)
        buf_copy2: for(int j=0; j<COULMN_PIXELS; j++)
            buf[i][j] = in[i*COULMN_PIXELS+j];

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){    // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_fweight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_fbias[i];

                if(conv_out[i][j][k]<0)    // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_fweight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_fbias[col];

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

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

        out[col] = dot2[col];
    }

    return(0);
}

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

    for(int i=0; i<NUM_OF_OUTPUT; 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[NUM_OF_OUTPUT]){
    int max_id;
    float max;

    for(int i=0; i<NUM_OF_OUTPUT; 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年08月29日 04:39 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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