読者です 読者をやめる 読者になる 読者になる

tkato’s blog

ブログ名は暫定。

BNN-PYNQを理解する(3) ハードウェア概要・実装

BNN-PYNQのハードウェアの概要・実装、ソフトウェアのシーケンスなど更新しました。 (追記5/21 上記の更新したつもりができていませんでした。本日更新しました)

https://tkat0.github.io/study-BNN-PYNQ/

論文とアクセラレータ側の実装を読みつつ、概要から順にまとめているところです。

以下、アクセラレータの実装を読んで、C++のテンプレートの使い方が面白いなと感じたので記事にしました。*1

BNN-PYNQのハードウェア実装について

BNN-PYNQのアクセラレータのコアであるMVTU*2の実装を見るとわかりますが、C++のテンプレートで定数を記述してます。

BNN-PYNQ/matrixvector.h at master · Xilinx/BNN-PYNQ · GitHub

最初実装を見たとき、普通の引数で変数を渡せばいいじゃん、と思っていたのですが、これは高位合成したHWの性能を出すために必要なんですね。

高位合成で性能を出すためには、挙動がコンパイル時に静的に決まるような書き方をすることがポイント

C++のテンプレートを使えば、関数呼び出し毎に定数を変えられる(定数の異なる関数を生成できる)

C++のテンプレートを上手く使うと、ハードウェア化したときに性能がでるC++コードを簡単に記述できて便利だと思いました。

高位合成の実装をするときは、こんな感じで使い分ければよいかな?

  • コンパイル時に決定できる定数(ノード数など)
    • 関数呼び出し毎に変えたいパラメータ
      • →テンプレート定数
    • 関数呼び出し毎に同じパラメータ
      • →定数(const)
  • コンパイル時に決定できない変数(バッチサイズなど)
    • →引数で変数を渡す

ただし、Vivado 2017.1では可変長のループでも最適化されるようになったとの噂。 もしかして、今後は上記のような実装を意識しなくても効率的な実装ができるのかな?

FPGAの部屋 Vivado 2017.1 の新機能1(概要)

*1:私はC++新人研修以来書いたことがないので、ごくごく当たり前の話かも

*2:行列とベクトルの積和演算器

BNN-PYNQを理解する(2) ソフトウェア全体像

今回は、BNN-PYNQのソフトウェアの構成をざっくり図にしてみました。

f:id:tkat0:20170504161200p:plain

詳細は、以下にて。

https://tkat0.github.io/study-BNN-PYNQ/

BNN-PYNQについては、今後は上記でまとめていきたいと思います。

理由としては、

  • ブログより単一のサイトとしてまとまっていた方が後から読みやすい
  • 文書の構成管理をしたい
  • オフラインでも作業できる (結構 移動時間が多いのです…)

などなど

とりあえず、手軽に初められるSphinxで書いてGitHub Pagesで公開してみました。

BNN-PYNQを理解する(1) FAQ

3月以降、仕事が激しくなり、余暇もほとんど仕事に関わる分野の勉強に当てていました。 以下の5/20のイベント出席に向けて、DeepLearning x FPGAの勉強を再開しました。

「PYNQ祭り」延長戦 : FPGAディープラーニング実践懇親会 - connpass

まずは、手近な勉強材料としてBNN-PYNQのソースを解析してみました。 勉強した内容は、ブログに何回かに分けて書きたいと思います。

尚、BNN-PYNQのソースコードは5/2時点で最新のものを使いました。

GitHub - Xilinx/BNN-PYNQ at a86e0863418ce4161ed61b69ba89ec1481014362

初回は、自分も含めて多くの人が気になりそうな話題について、ざっくりFAQ形式で書きたいと思います。

BNN-PYNQってなに?

ホビーFPGAボード"PYNQ(ピンク)“で動作するNeuralNetworkのシステム。 行列演算などをハードウェア化することで、DeepLearningアルゴリズムを、CPUより高速化(アクセラレーション)する試みが、至る所で行われています。これもその一種。

推論に関しては、Pythonのアプリケーション層、Cのドライバ、高位合成用の実装まで、全コード公開されているためDeepLearningのアルゴリズムFPGAで動かすことを学ぶには良い素材です。

アプリケーションは、例えば以下。

BNN-PYNQ/Cifar10.ipynb at master · Xilinx/BNN-PYNQ · GitHub

BNN-PYNQは同じNeuralNetworkをCPUとFPGAそれぞれで動かせるようになっていて、「CPUよりFPGA化したほうがこんなに速いよ」という内容が上記でも確認できます。

BNN-PYNQは、以下のような工夫をして高速化しています。 詳細は論文*1に記載があります。

  • 演算精度のバイナリ化(0,1)
    • メモリの使用量、転送量を削減
    • ビット演算器を活用した高速化
  • 演算の並列化

学習も推論もFPGAでやるの?

No。学習はPCで行い推論をFPGAで実行する仕組み。

PCのDeepLeraningフレームワークで学習したモデルを、BNN-PYNQ用のデータフォーマットに変換するスクリプトは付属しています。 現時点でTheanoで学習したパラメータの変換に対応しているようです。 サンプルは、Lasagne(Theanoをバックエンドにできる)で記述されています。

データフォーマットの変換だけなので、ちょいとスクリプトを書けば、TensorFlowやChainerなどとも連携できるはずです。(今度のイベント向けにやってみようかな)

認識精度は?

付属の学習済みモデルに関する認識率は、ここに記載があります。

BNN-PYNQ/bnn/src/training at master · Xilinx/BNN-PYNQ · GitHub

バイナリ化しない場合に比べて、GPUでの学習時間がすこし長い印象がありますが、どうなんでしょうか?

PCで動く任意のモデルをFPGA化できるの?

No。現状はLFCとCNVと呼ばれる2種類のモデルに対応してます。 例えばLSTMなど含むモデルは動かせません。

ただし、BNN-PYNQが対応しているレイヤーで構成されるモデルであれば、FPGAのリソースに乗る範囲でレイヤーを組み替えて任意のモデルは作れると思います。

BNN-PYNQでは、バイナリ化したConvolutionやFullyConnectedレイヤーのハードウェアを使います。 そのため、PC側もそれに合わせてバイナリ化したレイヤーを定義して学習しないと、学習した重みをFPGAに乗せられません。 通常のPC側のフレームワークだとfloat(32bit)で計算する部品がほとんどです。 そのため、BNN-PYNQでは、PCのフレームワーク用にもバイナリ化したレイヤーを定義して、それを用いてモデルを作り、学習しています*2

もし、ConvolutionやFullyConnected以外を使いたい場合は、ハード側にそのレイヤーを実装するか、そのレイヤーをソフト処理するかだと思います。 しかし、ソフト処理は現実的ではないと思います。BNN-PYNQは推論開始後、最期のレイヤが計算完了するまで制御がCPUに戻らない仕組みです。そのため、途中の一部のレイヤだけソフト処理にするのはBNN-PYNQのコアの修正が必要で、できたとしても速度が遅くなると思います。 そのため、追加したいレイヤーのCPUよりも高効率なハードウェアアルゴリズムを考えて、それを高位合成なりでハード化するのが面白いんじゃないでしょうか??

任意のデータで既存のモデル(LFCとCNV)を再学習できるの?

Yes

CNV, LFCは それぞれ学習データのフォーマットが決まっているので、それに従い作成したデータなら学習可能です。

任意のタスクにBNN-PYNQを応用する場合は、まずは既存のネットワーク(LFC,CNV)を使うところから考えるのが良さそうですね。

CPUとアクセラレータ間のデータ転送のタイミングは?

データのコピーが性能上のボトルネックになるので、どのタイミングでコピーしているかは重要です。 ソースを見ると、以下のタイミングでコピーが発生しているようです。

  1. モデル初期化時に、全レイヤーのパラメータをCPUからアクセラレータへコピー
  2. 推論開始時に、入力画像データを、CPUからアクセラレータへコピー
  3. 推論終了時に、推論結果をアクセラレータからCPUへコピー

推論中は、アクセラレータとメモリ内で閉じて演算しているようです。

最期に

今回は書けなかったのですが、ソフト側のソースはざっくり読みました。

以下のような流れでPythonからアクセラレータを叩いています。 モデル依存のC実装は薄くて、コアライブラリ(共通部)が厚いかんじ。

  1. Pythonのラッパーから、CFFIを使ってモデルのCライブラリを叩く
  2. モデルのCライブラリは、コアライブラリを叩いて、パラメータの設定や推論の実行を命令する
  3. コアライブラリは、アクセラレータのドライバを叩いて、アクセラレータ用のバッファ管理やレジスタ制御を行う

アプリからハードまで、ほんとに勉強になりますね…

次回はソフト側の記事を書きたいと思います。

*1:https://arxiv.org/abs/1612.07119

*2:元祖 Binarized Neural Networksの論文の実装をベースにしてるぽいですね

BNN-PYNQをリビルドする

本日3/7、XilinxがPYNQで動くBinarized Neural Network (BNN)の実装を公開しました。 DeepLearnigのアルゴリズムFPGAにオフロードしたい人にとっては、とても良い勉強材料になると思います。

リポジトリ公開当初は、Pythonで共有ライブラリをキックする部分とビルド済みの共有ライブラリしか公開されておらず、ドライバとアクセラレータのソースは非公開でした。

github.com

中身を詳しく追う前に、とりあえずビルドしてみました。

ビルド環境

  • Ubuntu14.04
  • SDSoC 2016.1

ビルド用のスクリプトが付属しているので、それを実行するだけでできました。 SDSoCそのものは使いません。付属のVivadoとVivado HLSを使います。 最初Windowsで試してみましたが、うまくいかず…*1

ビルド手順(ハードウェア)

以下に記載があります

BNN-PYNQ/README.md at master · Xilinx/BNN-PYNQ · GitHub

ハードウェアのビルドは「Hardware design rebuilt」に記載がある通り。

環境変数XILINX_BNN_ROOTは/BNN_PYNQ/bnn/src/network/では動かなかった。/BNN_PYNQ/bnn/src/が正しそう。*2

BNNのアーキテクチャはCNVとLFCがあります。 今回LFCを試したところ、実行開始から10分程度でbitstream生成まで終わりました。

./make-hw.sh lfc-pynq pynq a

f:id:tkat0:20170307231518j:plain

f:id:tkat0:20170307231514j:plain

ビルド手順(ソフトウェア)

Pythonとハードウェアの間にあるCのドライバのビルド方法。こちらは、PYNQ上でビルドする必要があるようです。上記READMEの「Quick Start」に記載ありますね。

その他

自前で書いていたBNNはビルドに少なくとも20分はかかる上、性能もソフトより遅かったので、BNN-PYNQのコードが公開されたことは勉強する上で非常に嬉しい。ソースを見ていると、hls_streamがデータフローの基本になっているよう。

*1:今回Git Bashをつかってスクリプトを叩いた。ちゃんと環境構築すればWindowsでもできるかも?

*2:追記(03/10):公式に修正されたようですね

SDSoC 2015.4でPYNQのOverlayを開発する(3)

SDSoC 2015.4でPYNQの簡単にプラットフォームを作る話です。次はSWプラットフォームの作成。

これはAdam Taylor氏の以下のブログのやり方をそのままやりました。ビルド済みのカーネルをそのままコピーし、自分ではなにもしないので簡単。

forums.xilinx.com

まずGitHubのPYNQリポジトリ*1より、以下をSDSoCプラットフォームのディレクトリ(任意)へコピー

  • devicetree.dtb
  • fsbl.elf
  • u-boot.elf
  • boot.bif

次に、PYNQのSDカードイメージ*2をSDカードに焼き、boot領域からuImageをSDSoCプラットフォームのディレクトリへコピー

最期に、SDSoC用のpfmファイルを作成。パスは、pfmファイルからの相対パス

<?xml version="1.0" encoding="UTF-8"?>
<xd:repository xd:vendor="xilinx.com" xd:library="xd" xd:name="pynq" xd:version="1.0" xmlns:xd="http://www.xilinx.com/xd" >

    <xd:description>Basic platform targeting the PYNQ-Z1 board</xd:description>

    <xd:bootFiles
      xd:os="linux"
      xd:bif="boot/boot.bif"
      xd:readme="boot/generic.readme"
      xd:devicetree="boot/devicetree.dtb"
      xd:linuxImage="boot/uImage"
      xd:ramdisk="boot/uramdisk.image.gz" # これはたしかZyboかなにかのplatformからコピーしてきたような。。。
    />
  
</xd:repository>

SDSoC 2015.4でPYNQのOverlayを開発する(2)

SDSoC 2015.4でPYNQの簡単にプラットフォームを作る話です。まずはHWプラットフォームの作成。

前回のまとめ

現時点でリリースされているPYNQのROMはSDSoC 2015.4でビルドされたライブラリを含むため、それ以外のSDSoCのバージョンを使う場合は、Linuxカーネルのリビルドが必要。SDSoC 2015.4を使うのが一番簡単。

ポイント

HWとSWの2つのプラットフォームを作る必要があります。基本的にAdam Taylor氏のブログの手順と同じことをします。*1。以下のリンクの前後の記事もPYNQの話なので読むことをおすすめ。

forums.xilinx.com

SDSoCプラットフォームの作成については、UG1146 - SDSoC 環境プラットフォーム開発ガイドや以下の動画で全体像を把握しておくことをおすすめ。

SDSoC 環境入門 (パート 3) : カスタム プラットフォームの生成 (日本語吹替) - YouTube

プラットフォーム作成手順を簡単に概要をまとめます。

  • HWプラットフォームの作成
    • Vivado 2015.4で新規プロジェクトを作成
    • ZYNQとreset IP、割り込み信号のconcat IPだけの簡単なデザインをつくる
    • HWプラットフォーム作成用のtclコマンドを実行し、*.pfmファイルを生成
    • メタデータファイルの作成
  • SWプラットフォームの作成

HWプラットフォームの作成

PYNQのリポジトリには、Base OverlayのVivadoプロジェクトファイルがあります。これをベースにしてHWプラットフォームを作成するのが簡単かと思いました。しかし、このプロジェクトはVivado 2016.1で作成されているためVivado 2015.4で扱うことができませんでした。

そのため、Vivado 2015.4でプロジェクトを新規作成することにしました。

Block Designはこのように作りました。

f:id:tkat0:20170215062559p:plain

Adam Taylorのブログでは、LEDなどの結線もありますが今回は省略。これらは、Linux起動直後に呼ばれる以下のPythonスクリプトでLEDを点滅してるのに対応するためのようです。

github.com

ZYNQの設定は、本来ならベンダーが提供する設定ファイル(Zynq Presets)*2をインポートすれば済むはずですが、インポート時にエラーが生じたため、Vivado 2016.1でPYNQのBase Overlayのプロジェクトを開きZYNQの設定を1つ1つ確認しながら、Vivado 2015.4でも同じ設定をしました。

あとは制約ファイル(Master XDC)*3の追加も忘れずに。

最期に、pfmファイルを作成するtclコマンドを実行して、pfmファイルを作ります。tclコマンドは、以下のスクリプトファイルにまとめておいて、実行しました。事前にSDSoCのインストール先のscripts/vivado/sdsoc_pfm.tclを実行して、sdsocのコマンドをVivadoに読み込ませる必要がある点に注意。

set pfm [sdsoc::create_pfm pynq_hw.pfm]
sdsoc::pfm_name        $pfm "xilinx.com" "xd" "pynq" "1.0"
sdsoc::pfm_description $pfm "PYNQ-Z1"
sdsoc::pfm_clock       $pfm FCLK_CLK0 ps7 0 false proc_sys_reset_0_100M
sdsoc::pfm_clock       $pfm FCLK_CLK1 ps7 1 false proc_sys_reset_1_142M
sdsoc::pfm_clock       $pfm FCLK_CLK2 ps7 2 true  proc_sys_reset_2_200M
sdsoc::pfm_clock       $pfm FCLK_CLK3 ps7 3 false proc_sys_reset_3_166M
sdsoc::pfm_axi_port    $pfm M_AXI_GP0 ps7 M_AXI_GP
sdsoc::pfm_axi_port    $pfm M_AXI_GP1 ps7 M_AXI_GP
sdsoc::pfm_axi_port    $pfm S_AXI_ACP ps7 S_AXI_ACP
sdsoc::pfm_axi_port    $pfm S_AXI_HP0 ps7 S_AXI_HP
sdsoc::pfm_axi_port    $pfm S_AXI_HP1 ps7 S_AXI_HP
sdsoc::pfm_axi_port    $pfm S_AXI_HP2 ps7 S_AXI_HP
sdsoc::pfm_axi_port    $pfm S_AXI_HP3 ps7 S_AXI_HP
for {set i 0} {$i < 16} {incr i} {
  sdsoc::pfm_irq       $pfm In$i xlconcat
}
sdsoc::generate_hw_pfm $pfm

これで、pynq_hw.pfmがVivadoのカレントディレクトリに生成されます。HWプラットフォームの作成は以上。

おわりに

プラットフォームを作成したりするとなると、結局Vivadoの知識やLinuxの知識が必要です。Vivadoについては以下の本を読んで概要を理解しました。おすすめです。

www.shuwasystem.co.jp

*1:ただし、使用したSDSoCのバージョンの記載がないなど色々行間を読む必要があり、この記事だけではプラットフォームを作れなかったので前回の記事の通りAnurag Dubey氏に質問したのでした

*2:http://store.digilentinc.com/pynq-z1-python-productivity-for-zynq/

*3:http://store.digilentinc.com/pynq-z1-python-productivity-for-zynq/

SDSoC 2015.4でPYNQのOverlayを開発する(1)

ブログを始めてみました。 この記事は、SDSoC 2015.4でPYNQ向けの環境構築〜開発を行うまでをまとめていくものです。

PYNQとSDSoCは相性が良いです。Cで書いたハードウェアアルゴリズムを高位合成し、それを簡単にPythonから呼び出すことができました。高位合成したIPによってはハードウェアの制約で入力バッファをDRAM上の連続領域に確保する必要がありますが、PythonからLinuxの連続領域を確保することも、PYNQが提供するPythonライブラリが面倒を見てくれます。そのため、PythonからNumPyのようなライブラリを使う感覚で自作のIPをつかった演算ができました*1

PYNQ向けのSDSoCの環境構築は、現状骨が折れる作業です*2。SDSoCはFPGAボード毎に”プラットフォーム”を作成する必要があります。ボードによっては、SDSoCに標準でプラットフォームが付属しているものもありますが、PYNQはそうではありません(今後に期待)。SDSoCプラットフォームの作成方法は、公式のドキュメントやYouTubeの解説動画などが参考になります。しかし、私はHWプラットフォームは作れたもの、SWプラットフォームを上手く作れませんでした…

それでもなんとかSDSoCでPYNQの開発がしたかったので、有識者に聞くことにしました。Anurag Dubey氏です。彼はPYNQのコミッターでもあり、SDSoCを使って開発したPYNQ用のFIRフィルタをGitHubで公開していました*3。メールで「どうやって作ったの?」という内容の質問をしたところ、以下の回答がありました。かなりフレンドリーに回答してくれました。

  • pynqfireはSDSoC 2015.4で開発した
  • PYNQの/usr/lib/libsds_lib.soはSDSoC 2015.4でビルドしてある
  • それ以外のバージョンでビルドした実行ファイルはPYNQで動かない
  • もし違うバージョンのSDSoCを使うなら、Linuxカーネルのリビルドが必要

SDSoC 2015.4を使うのが、一番簡単な方法だとわかりました。

次の記事から、SDSoC 2015.4を使ってPYNQ用のOverlayを作る方法を記載します。 すでに作ったものは以下にあります。

github.com

*1:多少HW寄りの知識は必要ですが、ドライバなど書かずにPython感覚でHWを叩けるのは感動

*2:@hasegawさん達がSDSoC 2016.3のPYNQプラットフォームを構築できたとのこと。私も環境を提供していただき動作確認中

*3:https://github.com/hackwa/pynqfire