FC2カウンター FPGAの部屋 今まで作ってきた層のテンプレートをMNIST のCNN に適用する1(C コードの合成1)

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

FPGAの部屋

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

今まで作ってきた層のテンプレートをMNIST のCNN に適用する1(C コードの合成1)

全結合層のテンプレートの変更”で全結合層のテンプレートを変更したが、今まで作ってきた畳み込みニューラルネットワーク(CNN) の層のテンプレートをMNIST のCNN に適用して、お手軽に素早くCNN をFPGAに実装できるかどうか?を確かめてみた。

まずは、C コードの合成を行って、どのくらいの性能になるのか?をチェックする。

HDL に合成されるソースコードはテンプレートを使って、層の実体を作り、以前のMNISTのCNN のパラメータを入れていくだけの簡単な作業なので、直ぐ実装することができた。今回の層構成は今まで作ってきた白線間走行用CNN と同じなので、パラメータを変更するだけだった。

全体を結合するトップレベルのファイルの mnist_conv_nn10_hlss.cpp と mnist_conv_nn10_hlss.h を貼っておく。
mnist_conv_nn10_hlss.h から貼っておく。

// mnist_conv_nn10_hlss.h
// 2018/05/18 by marsee
//

#ifndef __MNIST_CONV_NN10_HLSS_H__
#define __MNIST_CONV_NN10_HLSS_H__
#include <ap_fixed.h>

template<int U, int TI, int TD>
    struct float2_axis{
        struct data {
            float data0;
            float data1;
        } data;
        ap_uint<1> keep;
        ap_uint<1> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int U, int TI, int TD>
    struct float1_axis{
        struct data {
            float data0;
        } data;
        ap_uint<1> keep;
        ap_uint<1> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

#define NUMBER_OF_OUTPUT_LAYER    10

typedef ap_uint<4> output_type;

typedef ap_fixed<12,7,AP_TRN,AP_WRAP> out_affine_type;
#endif


mnist_conv_nn10_hlss.cpp を貼っておく。

// mnist_conv_nn10_hlss.cpp
// 2018/05/18 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>

#include "layer_general.h"
#include "mnist_conv_nn10_hlss.h"

int input_layer(hls::stream<ap_axiu<8,1,1,1> >&ins,
    hls::stream<ap_fixed_axis<9,1,1,1> >&outs);

int conv_layer1(hls::stream<ap_fixed_axis<9,1,1,1> >& ins,
    hls::stream<ap_fixed_axis<10,3,10,1> >& outs);

int relu_conv1(hls::stream<ap_fixed_axis<10,3,10,1> >& ins,
    hls::stream<ap_fixed_axis<10,3,10,1> >& outs);

int max_pooling(hls::stream<ap_fixed_axis<10,3,10,1> >& ins,
    hls::stream<ap_fixed_axis<10,3,10,1> >& outs);

int affine_layer1(hls::stream<ap_fixed_axis<10,3,10,1> >& ins,
    hls::stream<ap_fixed_axis<13,7,1,1> >& outs);

int relu_affine1(hls::stream<ap_fixed_axis<13,7,1,1> >& ins,
    hls::stream<ap_fixed_axis<13,7,1,1> >& outs);

int affine_layer2(hls::stream<ap_fixed_axis<13,7,1,1> >& ins,
    hls::stream<ap_fixed_axis<12,7,1,1> >& outs);

int output_layer(hls::stream<ap_fixed_axis<12,7,1,1> >& ins, output_type& output,
    out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]);

int all_layers(hls::stream<ap_axiu<8,1,1,1> >& ins, output_type& output,
    out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]){
#pragma HLS INTERFACE s_axilite port=output
#pragma HLS INTERFACE s_axilite port=dot2
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=ins

    hls::stream<ap_fixed_axis<9,1,1,1> > outs_input_layer;
// #pragma HLS STREAM variable=outs_input_layer depth=560 dim=1
    hls::stream<ap_fixed_axis<10,3,10,1> > outs_conv_layer;
// #pragma HLS STREAM variable=outs_conv_layer depth=312 dim=1
    hls::stream<ap_fixed_axis<10,3,10,1> > outs_relu_conv1;
// #pragma HLS STREAM variable=outs_relu depth=312 dim=1
    hls::stream<ap_fixed_axis<10,3,10,1> > outs_max_pooling;
// #pragma HLS STREAM variable=outs_max_pooling depth=78 dim=1
    hls::stream<ap_fixed_axis<13,7,1,1> > outs_affine_layer1;
// #pragma HLS STREAM variable=outs_affine_layer1 depth=100 dim=1
    hls::stream<ap_fixed_axis<13,7,1,1> > outs_relu_affine1;
// #pragma HLS STREAM variable=outs_relu_affine1 depth=100 dim=1
    hls::stream<ap_fixed_axis<12,7,1,1> > outs_affine_layer2;
// #pragma HLS STREAM variable=outs_affine_layer2 depth=3 dim=1

    input_layer(ins, outs_input_layer);
    conv_layer1(outs_input_layer, outs_conv_layer);
    relu_conv1(outs_conv_layer, outs_relu_conv1);
    max_pooling(outs_relu_conv1, outs_max_pooling);
    affine_layer1(outs_max_pooling, outs_affine_layer1);
    relu_affine1(outs_affine_layer1, outs_relu_affine1);
    affine_layer2(outs_relu_affine1, outs_affine_layer2);
    output_layer(outs_affine_layer2, output, dot2);

    return(0);
}



Vivado HLS 2017.3 の mnist_conv_nn10_hlss プロジェクトを示す。
mnist_conv_nn10_hlss_1_180519.png

さて、早速、C コードの合成を行った。結果を示す。
mnist_conv_nn10_hlss_2_180519.png
mnist_conv_nn10_hlss_3_180519.png

Estmated は 9.40 ns だったが、例によってUncertainty は 3.00 ns に設定している。
Latency は、min で 31144 クロック、max で 32584 クロックだった。Interval は min は 31113 クロック、max は 32553 クロックだった。
リソース使用量はBRAM_18K が 434 個、155 % でZYBO Z7-20 のZynq の量を超えてしまっているので、実装できない。DSP48E は、180 個、FF は 8923 個、LUT は 16481 個使用してる。

以前の実装は、”「ゼロから作るDeep Learning」の畳み込みニューラルネットワークのハードウェア化6(再度Vivado HLS )”だった。その合成結果を示す。
nn_fpga_ch7_24_170620.png

以前の実装のLatency は 2745569 クロックなので、今回の実装での実行時間は、約 1/84 になっている。つまり性能は 84 倍になったということだ。ただし、リソース使用量は増えている。これは演算を並列動作させているので仕方がない。
次は、PYNQボードのZynq に入るように指示子を変更してみよう。
  1. 2018年05月19日 06:49 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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