@muojpさんが早速、dockerイメージをアップしています。
Pyhton 2.6+で動作し、NVIDIAのGPUを利用する場合は、CUDA 8.0 (cuDNNライブラリ)が必要です。
フレームワークには、Chainer 1.23.0 or 1.24.0 を使っています。
フレームワークには、Chainer 1.23.0 or 1.24.0 を使っています。
SDSoCは、2016.4 (or 2017.1) 。先日、2017.2がリリースされてしまいましたが。。。。
サポートしているFPGAボードは、Xilinx ZC702, ZCU102, Digilent Zedboard, Zyboです。
PYNQもサポート予定です。ということは、Arty-Z7でも動くでしょう。
サポートしているFPGAボードは、Xilinx ZC702, ZCU102, Digilent Zedboard, Zyboです。
PYNQもサポート予定です。ということは、Arty-Z7でも動くでしょう。
GUINNESS GUIを使って、CNN Specificationを選択しますが、事前にサポートされているモデルのみなのかな。
各モデルをロード後、そのモデルを構成するLayerの削除や新規にLayerを追加することも可能です。
サポートしているLayerは、
サポートしているLayerは、
・Conv (Int) ・Conv (Bin) ・Max Pool ・Ave Pool ・Denseです。
学習後、FPGAへのマッピングのために、ボードが選択できます。
PYNQやArty-Z7用のSDSoC用プラットフォームが出てくれば、ここに追加するだけでOKだと思います。
・Zed ・Zybo ・VC702 <= これ、ZC702の誤り? ・ZCU102これらのボードは、SDSoCが標準でサポートしているボードですね。
PYNQやArty-Z7用のSDSoC用プラットフォームが出てくれば、ここに追加するだけでOKだと思います。
GUINNESSはバックエンドに、Xilinx社のSDSoCを使っているので、
Zynq or Zynq UltraScale+ MPSoCが搭載しているボードであれば、
そのボードのSDSoC用プラットフォームを作ればいいのです。
Zynq or Zynq UltraScale+ MPSoCが搭載しているボードであれば、
そのボードのSDSoC用プラットフォームを作ればいいのです。
生成されるファイルは、
・HLS ディレクトリ ・sdsoc ディレクトリ ・config.pickle ・eval.py ・net3.py ・temp.model ・temp_log.csv ・test1.proj
このファイルを使って、SDSoCにて指定したボードのBitStreamを生成します。
ここまでが上記のドキュメントに書いてある内容です。
ここからは、あたしが得意のソースコードの解析です。
template_Makefile をテンプレートに、プロジェクト/sdsoc/Makefile を
template_cpp_r7_bcnn.cpp をテンプレートに、プロジェクト/sdsoc/cnn.cpp を
template_cpp_r7_main.cpp をテンプレートに、プロジェクト/sdsoc/main.cpp を
template_cpp_r7_socket_main.cpp をテンプレートに、sdsoc/socket_main.cpp を生成しています。
template_cpp_r7_bcnn.cpp をテンプレートに、プロジェクト/sdsoc/cnn.cpp を
template_cpp_r7_main.cpp をテンプレートに、プロジェクト/sdsoc/main.cpp を
template_cpp_r7_socket_main.cpp をテンプレートに、sdsoc/socket_main.cpp を生成しています。
template_Makefileを見てみると、
PLATFORM = (TARGET_BOARD) SDSFLAGS = -sds-pf ${PLATFORM} \ -sds-hw BinCNN (CNN_C_SOURCE) -sds-end \ -poll-mode 1 CC = sds++ ${SDSFLAGS}
とあります。GUINNESS GUIで指定したターゲットボードを SDSoCのプラットフォーム (-sds-pf オプション)として、
-sds-hw で機能名(BinCNN)を生成したCNNのソースコードを使って、sds++コマンドで BitStream を生成しています。
-sds-hw で機能名(BinCNN)を生成したCNNのソースコードを使って、sds++コマンドで BitStream を生成しています。
-sds-hw で機能名として指定した BinCNN は、template_cpp_r7_bcnn.cppの最後の方にあります。。
以下のように、なっています。
以下のように、なっています。
#ifdef __SDSCC__ #pragma SDS data access_pattern(t_bin_convW: SEQUENTIAL) #pragma SDS data access_pattern(t_BNFb: SEQUENTIAL) #pragma SDS data access_pattern(t_in_img: SEQUENTIAL) #pragma SDS data zero_copy(t_bin_convW[0:(WEIGHT_SIZ)]) #pragma SDS data zero_copy(t_BNFb[0:(BIAS_SIZ)]) #pragma SDS data zero_copy(t_in_img[0:(IMGSIZ)*(IMGSIZ)]) #endif void BinCNN( #ifdef __SDSCC__ int *t_bin_convW, int *t_BNFb, ap_int<64> t_in_img[(IMGSIZ)*(IMGSIZ)], int fc_result[(OUT_DENSE_SIZ)], int init #else int t_bin_convW[(WEIGHT_SIZ)], int t_BNFb[(BIAS_SIZ)], ap_int<64> t_in_img[(IMGSIZ)*(IMGSIZ)], int fc_result[(OUT_DENSE_SIZ)], int init #endif ) { if( init == 1) setup( t_bin_convW, t_BNFb); else kernel( t_in_img, fc_result); }
引数 init によって、setup 関数と kernel 関数を切り替えています。
引数 init を 1 に設定することで、setup 関数が実行され、重みとバイアスメモリの初期化を行います。
引数 init を 0 に設定することで、kernel 関数が実行され、入力した画像データに対する推論を行います。
引数 init を 1 に設定することで、setup 関数が実行され、重みとバイアスメモリの初期化を行います。
引数 init を 0 に設定することで、kernel 関数が実行され、入力した画像データに対する推論を行います。
メイン関数の template_main.cpp では、
printf("setup... \n"); BinCNN( t_bin_convW, t_BNFb, t_tmp_img, fc_result, 1);のように、最後の引数 init に 1を設定して、重みとバイアスを設定しています。
後半の下記の部分で、プログラムの第二引数で指定された回数、推論を行います。。。
printf("Inference %d times ... ", cnt); for( i = 0; i < cnt; i++){ BinCNN( t_bin_convW, t_BNFb, t_tmp_img, fc_result, 0); } printf("OK\n");
template_cpp_r7_socket_main.cpp では、サーバーとソケット通信して、画像データを取り込み推論するというもののようです。
このプログラムとhttps://github.com/HirokiNakahara/GUINNESS-Tutorial/blob/master/cnn_capture.py を使って、
PCのカメラから取り込んだ画像を FPGAボードで推論し、PCに送り返しています。
このプログラムとhttps://github.com/HirokiNakahara/GUINNESS-Tutorial/blob/master/cnn_capture.py を使って、
PCのカメラから取り込んだ画像を FPGAボードで推論し、PCに送り返しています。
追記)、2017.09.09
CNNの各層のコードを生成する部分は、gen_cpp_code_v3.py の下記の部分です。
layer_typeによって各層の実行コードを呼んでいます。
回路的には、セレクタになるんでしょうね。
layer_typeによって各層の実行コードを呼んでいます。
回路的には、セレクタになるんでしょうね。
引用 #(DEF_CNN_LAYER) from collections import Counter def_cnn_layer = '' bn_idx = 0 dense_idx = 0 counter = Counter(initial_options) for layer_type, cnt in counter.items(): if layer_type == 0 and cnt > 0: for i in range(len(initial_options)): if initial_options[i] == 0: def_cnn_layer += ' case %d:\n' % i def_cnn_layer += ' int_conv2d_layer<bit_64, bit_%d, 64, %d, %d, %d>\n ( in_img, fb_tmp, conv0W, b0_BNFb);\n break;\n' % (max_bconv_width,max_bconv_width,int(infmap_siz[0]),int(infmap_siz[0])) elif layer_type == 1 and cnt > 0: for i in range(len(initial_options)): if initial_options[i] == 1: def_cnn_layer += ' case %d:\n' % i def_cnn_layer += ' bin_conv2d_pipeline(fb_tmp,bin_layer_idx,fsize[layer],n_in[layer],n_out[layer]);\n bin_layer_idx++;\n break;\n' elif layer_type == 2 and cnt > 0: for i in range(len(initial_options)): if initial_options[i] == 2: def_cnn_layer += ' case %d:\n' % i def_cnn_layer += ' max_pooling_layer<bit_%d, %d, %d>(fb_tmp);\n break;\n' % (max_bconv_width,int(imgsiz),int(infmap_siz[i])) elif layer_type == 3 and cnt > 0: for i in range(len(initial_options)): if initial_options[i] == 3: def_cnn_layer += ' case %d:\n' % i def_cnn_layer += ' {\n' def_cnn_layer += ' ap_int<%d>mask = 0x1;\n' % int(n_in_fmaps[i]) def_cnn_layer += ' for( of = 0; of < %d; of++){\n' % int(n_ou_fmaps[i]) def_cnn_layer += ' ap_int<11> tmp = 0;\n' def_cnn_layer += ' for( y = 0; y < %d; y++){\n' % int(infmap_siz[i]) def_cnn_layer += ' for( x = 0; x < %d; x++){\n' % int(infmap_siz[i]) def_cnn_layer += ' if( (fb_tmp[y][x] & mask) != 0)\n' def_cnn_layer += ' tmp++;\n' def_cnn_layer += ' }\n' def_cnn_layer += ' }\n' def_cnn_layer += ' if( tmp >= %d*%d/2)\n' % (int(infmap_siz[i]),int(infmap_siz[i])) def_cnn_layer += ' fc_tmp[of] = 1;\n' def_cnn_layer += ' else\n' def_cnn_layer += ' fc_tmp[of] = 0;\n' def_cnn_layer += ' mask = mask << 1;\n' def_cnn_layer += ' }\n }\n break;\n' elif layer_type == 4 and cnt > 0: for i in range(len(initial_options)): if initial_options[i] == 4: def_cnn_layer += ' case %d:\n' % i def_cnn_layer += ' fc_layer< %d, %d>( fc_tmp, fc%dW, b%d_BNFb, fc_result);\n break;\n' % (int(n_ou_fmaps[i]),int(n_in_fmaps[i]),dense_idx,bn_idx) bn_idx += 1 dense_idx += 1 elif initial_options[i] == 0 or initial_options[i] == 1: bn_idx += 1 def_cnn_layer += ' default: break;\n'
こんな感じですね。。。