FC2カウンター FPGAの部屋 「ゼロから作るDeep Learning」の畳み込みニューラルネットワークのハードウェア化8(性能向上を図る)

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

FPGAの部屋

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

「ゼロから作るDeep Learning」の畳み込みニューラルネットワークのハードウェア化8(性能向上を図る)

「ゼロから作るDeep Learning」の畳み込みニューラルネットワークのハードウェア化7(C/RTL協調シミュレーション)”の続き。

前回は、C/RTL協調シミュレーションを行って、波形を観察し、IP 化を行った。今回は、実際に使用するインターフェースのAXI4-Stream 入力、AXI4-Lite 出力にして、少しチューニングをしてみよう。

書き換えた mnist_conv_nn10.cppを示す。

// mnist_conv_nn10.cpp
// 2017/06/12 by marsee
// 畳み込み層のカーネル数 10
//

#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"

int mnist_conv_nn(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[784], ap_fixed<127, AP_TRN_ZERO, AP_SAT> out[10]){
#pragma HLS INTERFACE s_axilite register port=out
#pragma HLS INTERFACE axis register both port=in
#pragma HLS INTERFACE s_axilite port=return
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[28][28];
    ap_fixed<103, AP_TRN_ZERO, AP_SAT> conv_out[10][24][24];
#pragma HLS ARRAY_PARTITION variable=conv_out cyclic factor=2 dim=3
    ap_fixed<103, AP_TRN_ZERO, AP_SAT> pool_out[10][12][12];
#pragma HLS ARRAY_PARTITION variable=pool_out block factor=12 dim=3
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[10];

    buf_copy1: for(int i=0; i<28; i++)
        buf_copy2: for(int j=0; j<28; j++)
#pragma HLS PIPELINE II=1
            buf[i][j] = in[i*28+j];

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<10; i++){    // カーネルの個数
        CONV2: for(int j=0; j<24; j++){
            CONV3: for(int k=0; k<24; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
#pragma HLS PIPELINE II=7
                    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<10; i++){
        POOL2: for(int j=0; j<24; j += 2){
            POOL3: for(int k=0; k<24; k += 2){
                POOL4: for(int m=0; m<2; m++){
#pragma HLS PIPELINE II=4
                    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<10; i++){
            af1_dot3: for(int j=0; j<12; j++){
                af1_dot4: for(int k=0; k<12; k++){
#pragma HLS PIPELINE II=3
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*12*12+j*12+k][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<100; row++){
#pragma HLS PIPELINE II=4
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        out[col] = dot2[col];
    }

    return(0);
}


このソースコードでのC コードの合成結果を示す。
nn_fpga_ch7_44_170623.png

Estimated は 13.48 ns で 10 ns をオーバーしている。
レイテンシは 690991 クロックで、100 MHz の場合は、6.91 ms となる。大幅にリソースを増やすこと無しに、これ以上速くならなかったのが残念だ。
リソースもBRAM_18K が 55 % から 59 % になった。FF も LUT も増えているが、AXI バスにしたので、少し増えたのもある。なお、Vivado HLS のバージョンは 2016.4 だ。
AXI バスにしたので、Address Info を貼っておく。これを見るとレジスタとそのオフセット・アドレスが分かる。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x20 ~
// 0x3f : Memory 'out_V' (10 * 12b)
//        Word n : bit [11: 0] - out_V[2n]
//                 bit [27:16] - out_V[2n+1]
//                 others      - reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


次に、実際にインプリメントしてみて、10 ns に収まるかどうか?を見るために、Place and Route にチェックを入れてExport RTL を行った。
結果はちょっと危ないが、一応 10 ns に収まっている。
nn_fpga_ch7_45_170623.png

VirtualBox 上のUbuntu 16.04 上のVivado HLS 2016.4 でC/RTL協調シミュレーションをやってみた。結果を示す。
nn_fpga_ch7_46_170623.png

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

入力部分のAXI4-Stream 部分を拡大した。8ビットで転送されている。
nn_fpga_ch7_48_170623.png

結果の出力部分だ。
nn_fpga_ch7_49_170623.png

0x2C 番地のデータが 0x0ee901b4 で、bit [11: 0] - out_V[2n] が 0x01b4 で最大なので、結果は 6 で正解だ。あとの数はすべてマイナスだった。

とりあえず、これでIP にしようと思う。IP 化しVivado のプロジェクトで使用する前に、私の書いた手描き数字を認識できるかどうか?のテストをしたいと思う。
  1. 2017年06月23日 05:01 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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