はじめに
XilinxがBNN-PYNQというプロジェクトを公開したことにより、FPGA初心者でも簡単にDeep LearningをFPGA実行することができるようになりました。早速ボードを購入してデモ実行まで試してみました。
事前説明
PYNQ
Xilinxのオープンソースプロジェクトで、XilinxのZynqに実装したFPGAロジックを、Pythonから簡単に使えるようにするためのもののようです。
通常、Zynqでプログラムを実行する際は、CPUで実行するPS(Processing System)と、FPGAで実行するPL(Programmable Logic)に分かれています。Deep Learningでは、Deep Learningを利用するアプリケーションをPSに実装し、並列化による高速化が見込める畳み込み処理やニューラルネットワークの各層の計算処理などをPLに実装するイメージですかね。PYNQでは、PSをPythonで記述することができます。
さらに、大きな特徴として、Overlayという考え方を持っています。Overlayでは、ソフトウェアライブラリのようにPLを扱うことで、Pythonから動的にPL部分を変更することができます。例えば、MNISTを実行するときは、MNISTのOverlayをPythonでロードするだけで、MNISTのNetworkがPLに展開されるイメージです。
PYNQの詳細は下記が参考になると思います。
PYNQ-Z1 Board
PYNQプロジェクトを正式にサポートしているボードが、PYNQ-Z1 Boardです。Dual-Core ARM® Cortex®-A9が搭載されています。また、HDMIのIN/OUTも持っているため、画像やビデオ処理などにも活躍しそうです。
PYNQ: PYTHON PRODUCTIVITY ON ZYNQ
現状、日本での購入はできなさそうなため、Digilentサイトから購入しました。
金額は送料なども含めて3万円ちょっとくらいで、注文から1週間程度で届きました。配送はFedExだったのですが、商品とは別で、税金の請求書(1,500円くらい)が届きました。請求書はコンビニで支払いができました。
BNN-PYNQ
BNN-PYNQとは、Binarized Neural Network (BNN)をPYNQ上で実行することができるプロジェクトです。Deep Learningは、推論と学習で構成されますが、BNN-PYNQで公開されているのは、推論のみです。
アルゴリズム
FPGAでは、計算資源の制約から、2値化したアルゴリズムを用いることが主流のようです。また、2値化することで、XNOR計算となり高速化が見込めるみたいです。BNN-PYNQでは、論文[FINN: A Framework for Fast, Scalable Binarized Neural Network Inference]で紹介されているCNVとLFCが、PYNQのOverlayとして公開されています。
BNNの詳細は、論文を読むのがもっともいいと思います。また、2値化アルゴリズムに関しては、下記が参考になると思います。
- [Survey]Binarized Neural Networks: Training Deep Neural Networks with Weights and Activations Constrained to +1 or -1
- 2値化CNN on FPGAでGPUとガチンコバトル(公開版)
- BinaryNetとBinarized Deep Neural Network
実装
BNN-PYNQでは、Deep Learningをxilinx-tiny-cnnというライブラリを使って実装しています。xilinx-tiny-cnnは、tiny-dnnを基にしており、次の点が変更されているとのことです。
BNN-PYNQは、tiny-dnnを利用しています。
- added batchnorm layer (currently feedforward only, no training)
- support for offloaded layer
- interleave layer
- binarized layers
tiny-dnnの開発者は日本人のようです。すごいですね。。。
C++ヘッダだけでDeep Learning、tiny-dnnの紹介
デモの実行
環境
BNN-PYNQを実行するにあたり、下記を準備しました。
- PYNQ-Z1 Board
- Micro-SDカード(8GB以上を推奨)
- LANケーブル
- USBケーブル
電源用に使います。ACアダプタの方がいいと思います。 - Mac mini
PYNQのイメージをSDカードに焼くためと、PYNQのJupyterを開くために使います。
PYNQの初期設定
ドキュメントのGetting Startedに沿って進めます。
イメージの作成
まずは、イメージのダウンロードです。上記ドキュメントの手順内にあるDownload and the PYNQ-Z1 image
からダウンロードします。また、ダウンロードは、Digilentのサイトからもできるようです。(違いはわかりません)
記事を書いている時点では、pynq_z1_image_2017_02_10.zip
がダウンロードされました。
ダウンロードしたPYNQ-Z1 Image
のzipファイルを解凍します。
$ tar zxvf pynq_z1_image_2017_02_10.zip
x pynq_z1_image_2017_02_10.img
解凍したイメージをSDカードにインストールします。まずは、イメージをインストールするSDカードを確認します。
$ df -ah
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk1s1 30Gi 2.5Mi 30Gi 1% 0 0 100% /Volumes/UNTITLED
SDカードをFAT32でフォーマットします。
$ diskutil eraseDisk FAT32 PYNQ /dev/disk1
Started erase on disk1
Unmounting disk
Creating the partition map
Waiting for the disks to reappear
Formatting disk1s2 as MS-DOS (FAT32) with name PYNQ
512 bytes per physical sector
/dev/rdisk1s2: 62501024 sectors in 1953157 FAT32 clusters (16384 bytes/cluster)
bps=512 spc=32 res=32 nft=2 mid=0xf8 spt=32 hds=255 hid=411648 drv=0x80 bsec=62531584 bspf=15260 rdcl=2 infs=1 bkbs=6
Mounting disk
Finished erase on disk1
SDカードをアンマウントします。
※SDカードは抜きません。
$ diskutil unmountDisk /dev/disk1
Unmount of all volumes on disk1 was successful
先ほどのイメージをSDカードに書き込みます。
$ sudo dd bs=1024m if=pynq_z1_image_2017_02_10.img of=/dev/rdisk1
Password:
これでSDカードの準備は完了です!
起動
PYNQ Boardを下記のイメージ通りに設定します。
⓪ 電源スイッチがOFFであることを確認します
① JP4(USB HOSTの隣)をSDにします
② 先ほど作成したSDカードを挿入します
③ USBケーブル(電源ケーブル)を接続します
④ LANケーブルを接続します
私の場合は、電源をUSBからとるため、上記に加えてJP5(電源スイッチの隣)をUSBに変更しました。すべて設定した後...
⑤ PYNQに電源を入れます
Jupyter Notebook / SSH
PYNQでは、Jupyter Notebookが起動しています。そのため、下記のリンクにアクセスすることで、Jupyter Notebook上でPythonのプログラムが可能です。
http://[PYNQのIPアドレス]:9090
最初にアクセスすると下のようなログインページになります。パスワードは「xilinx」です。
使い方は通常のJupyterと同じです。
また、PYNQに、SSHでアクセスすることもできます。アカウント名「xilinx」、パスワード「xilinx」です。
PYNQを最新化するために下記のコマンドを実行します。
xilinx@pynq:~$ sudo /home/xilinx/scripts/update_pynq.sh
[sudo] password for xilinx:
Info: This operation will overwrite all the example notebooks
Press any key to continue...
Github Repo Detected. Pulling latest changes from upstream..
fatal: A branch named 'master' already exists.
Already on 'master'
Your branch is up-to-date with 'origin/master'.
remote: Counting objects: 13, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 13 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (13/13), done.
From https://github.com/Xilinx/PYNQ
3ed304a..0309566 master -> origin/master
Updating 3ed304a..0309566
Fast-forward
python/pynq/gpio.py | 210 ++++++++++++++++++++++++++++++++++++++----------------
python/pynq/iop/iop.py | 22 +++---
python/pynq/tests/test_gpio.py | 1 +
3 files changed, 161 insertions(+), 72 deletions(-)
checking out v1.4
Verifying current SDCard image supports this pynq release.
Completed
Build libsds_lib
cd /home/xilinx/pynq_git/scripts/xlnkutils && make && make install
make[1]: Entering directory '/home/xilinx/pynq_git/scripts/xlnkutils'
gcc wrapper.c -fPIC -shared -rdynamic -o libsds_lib.so -Wl,--whole-archive libsds_lib.a -l pthread -Wl,--no-whole-archive
make[1]: Leaving directory '/home/xilinx/pynq_git/scripts/xlnkutils'
make[1]: Entering directory '/home/xilinx/pynq_git/scripts/xlnkutils'
cp -avf libsds_lib.so /usr/lib/
‘libsds_lib.so’ -> ‘/usr/lib/libsds_lib.so’
cp -arvf libxlnk_cma.h /usr/include/
‘libxlnk_cma.h’ -> ‘/usr/include/libxlnk_cma.h’
make[1]: Leaving directory '/home/xilinx/pynq_git/scripts/xlnkutils'
Pip install latest pynq python package
python3.6 /home/xilinx/scripts/stop_pl_server.py
rm -rf /opt/python3.6/lib/python3.6/site-packages/pynq/*
cp -rf /home/xilinx/pynq_git/Pynq-Z1/sdk/bin/*.bin /home/xilinx/pynq_git/python/pynq/iop/
cp -rf /home/xilinx/pynq_git/Pynq-Z1/bitstream /home/xilinx/pynq_git/python/pynq/
cd /home/xilinx/pynq_git/python ; sudo -H python3.6 -m pip install --upgrade .
Processing /home/xilinx/pynq_git/python
Installing collected packages: pynq
Found existing installation: pynq 1.4
Uninstalling pynq-1.4:
Successfully uninstalled pynq-1.4
Running setup.py install for pynq ... done
Successfully installed pynq-1.4
python3.6 /home/xilinx/scripts/start_pl_server.py &
Update scripts and notebooks
cp -arf /home/xilinx/pynq_git/Pynq-Z1/notebooks/* /home/xilinx/jupyter_notebooks
cp -f /home/xilinx/pynq_git/scripts/linux/rc.local /etc/
mkdir -p /home/xilinx/jupyter_notebooks/getting_started
mkdir -p /home/xilinx/jupyter_notebooks/getting_started/images
cp /home/xilinx/pynq_git/docs/source/3_jupyter_notebook.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/1_jupyter_notebook.ipynb
cp /home/xilinx/pynq_git/docs/source/4_programming_python.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/2_programming_python.ipynb
cp /home/xilinx/pynq_git/docs/source/5_programming_onboard.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/3_programming_onboard.ipynb
cp /home/xilinx/pynq_git/docs/source/8_base_overlay_iop.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/4_base_overlay_iop.ipynb
cp /home/xilinx/pynq_git/docs/source/9_base_overlay_video.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/5_base_overlay_video.ipynb
cp /home/xilinx/pynq_git/docs/source/10_base_overlay_audio.ipynb \
/home/xilinx/jupyter_notebooks/getting_started/6_base_overlay_audio.ipynb
chown -R xilinx:xilinx /opt/python3.6/lib/python3.6/site-packages/pynq/*
chmod -R a+rw /home/xilinx/jupyter_notebooks /opt/python3.6/lib/python3.6/site-packages/pynq
chmod -R a+x /home/xilinx/scripts/*
chmod a+x /root/*.sh
chmod a+x /etc/rc.local
chown -R xilinx:xilinx /home/xilinx/jupyter_notebooks /home/xilinx/scripts /opt/python3.6/lib/python3.6/site-packages/pynq
Notebooks folder is at: /home/xilinx/jupyter_notebooks
Scripts folder is at: /home/xilinx/scripts
Completed PYNQ update.
xilinx@pynq:~$
PYNQ 1.4になりました。
BNN-PYNQのインストール
BNN-PYNQをインストールします。
BNN-PYNQのQuick Startに記載されていますが、下記のコマンドでインストールできます。
xilinx@pynq:~$ sudo pip3.6 install git+https://github.com/Xilinx/BNN-PYNQ.git
[sudo] password for xilinx:
The directory '/home/xilinx/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/xilinx/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting git+https://github.com/Xilinx/BNN-PYNQ.git
Cloning https://github.com/Xilinx/BNN-PYNQ.git to /tmp/pip-7pt0wn6t-build
Installing collected packages: bnn-pynq
Running setup.py install for bnn-pynq ... done
Successfully installed bnn-pynq-0.1
BNN-PYNQの実行
インストールが完了すると、Jupyter上でbnnフォルダができているかと思います。
このフォルダの中には、いくつかのサンプルが準備されています。
この中から、Cifar10を実行してみたいと思います。Cifar10は、画像を10種類に分類するサンプルです。
とりあえず、[Run All]してみます。
すべて実行されました。
ソースコードを見てみるとわかりますが、Pythonでは、推論対象画像の読み込みと、classify_imageの呼び出しを行っているだけですね。
CPUとFPGAによる速度の比較結果は、下記の箇所で表示されています。
PYNQのCPUが遅いこともありますが、360倍以上の差が出ていますね。
4. Launching BNN in hardware → FPGAを使用
2223.00 microseconds
5. Launching BNN in software → CPUのみを使用
817744.00 microseconds
ソースコード
PIPでインストールした、BNN-PYNQのbnn.pyを簡単に見てみます。
まず、CnvClassifierクラス
の初期化です。
class CnvClassifier:
def __init__(self, params, runtime=RUNTIME_HW):
self.bnn = PynqBNN(runtime, network=NETWORK_CNV)
self.bnn.load_parameters(params)
ここでは、PynqBNNのインスタンス生成と、ネットワークの学習パラメーターをロードしています。
次に、PynqBNNクラス
の初期化です。
class PynqBNN:
def __init__(self, runtime=RUNTIME_HW, network=NETWORK_CNV, load_overlay=True):
self.bitstream_name = None
if runtime == RUNTIME_HW:
self.bitstream_name="{0}-pynq.bit".format(network)
self.bitstream_path=os.path.join(BNN_BIT_DIR, self.bitstream_name)
if PL.bitfile_name != self.bitstream_path:
if load_overlay:
Overlay(self.bitstream_path).download()
else:
raise RuntimeError("Incorrect Overlay loaded")
dllname = "{0}-{1}.so".format(runtime, network)
if dllname not in _libraries:
_libraries[dllname] = _ffi.dlopen(
os.path.join(BNN_LIB_DIR, dllname))
self.interface = _libraries[dllname]
self.num_classes = 0
ここでは、指定されたネットワークに応じて、Overlayがロードされています。
ちなみに、Overlay(self.bitstream_path).download()
の中では、bitstreamファイルを読み込み、/dev/xdevcfg
というデバイスファイルに書き出しているらしいです。
PYNQ-Z1のOverlay読み込みとPythonからのFPGA PLの制御
また、FPGAにアクセスするための共有ライブラリの読み込みを行っています。BNN-PYNQでは、cffi.FFI.dlopen
で共有ライブラリにアクセスすることで、共有ライブラリを経由してFPGAを利用しているんですね。
最後に、PynqBNNクラス
の推論です。
def inference(self, path):
usecperimage = _ffi.new("float *")
result_ptr = self.interface.inference(path.encode(), _ffi.NULL, len(self.classes), usecperimage)
print("Inference took %.2f microseconds" % (usecperimage[0]))
print("Classification rate: %.2f images per second" % (1000000.0/usecperimage[0]))
return result_ptr
推論するために、self.interface.inference
を呼び出しています。
先ほど、表示されていた推論時間はここでprint
されたもののようですね。
おわりに
現状、推論だけですが、FPGAでDeep Learningを実行することができました。
CPUと比較してかなり早かったです。
また、Python(Jupyter Notebook)で利用できるので、アプリケーションに埋め込むことや、テストすることが簡単にできそうだと思いました。
ただ、今回はPLには手を出していないため、FPGAを使った実感はあまりなかったです。
共有ライブラリ以降のソースコードも公開されており、リビルド方法も記載されているため、見ていきたいです。
PYNQが紹介されており、とても興味深い記事でした。
機械学習/Deep Learningの仕事が増える2017年、ソフトウェアエンジニアがFPGAを学ぶべき理由