Welcome to BinaryBrain's documentation!

はじめに

概要

BinaryBrain は主に当サイトが研究中の LUT(Look-Up Table)-Networkを実験することを目的に作成した ディープラーニング用のプラットフォームです。

LUT-Networkの評価を目的に作成しておりますが、それ以外の用途にも利用可能です。

以下の特徴があります

  • ニューラルネットのFPGA化をメインターゲットにしている
  • バイナリネットであるも関わらず変調技術によりAutoencodeや回帰分析が可能
  • 独自のSparse-LUTモデルにより、LUTの性能を最大限引き出したが学習できる
  • 量子化&疎行列のネットワークでパフォーマンスの良い学習が出来る環境を目指している
  • C++で記述されている
  • GPU(CUDA)に対応している
  • 高速でマニアックな自作レイヤーが作りやすい
  • Pythonからの利用も可能

クイックスタート(C++)

まずはじめに付属のMNISTサンプルを動かすまでを紹介します。

AXV2以降の命令が使えるCPUと、Windows7以降もしくは Linuxの環境を想定しております。 CUDAにも対応していまが、nvccが利用可能な環境でビルドする必要があります。

CUDAについてはNVIDIAのページを参考に事前にインストールください。 https://developer.nvidia.com/cuda-downloads

なお make 時に make WITH_CUDA=No と指定することで、GPUを使わないCPU版もビルド可能です。

Windows

  1. VisualStudio 2017 と CUDA 10.1 をインストールします
  2. git clone --recursive -b ver3_release https://github.com/ryuz/BinaryBrain.git
  3. http://yann.lecun.com/exdb/mnist/ からMNISTデータをダウンロードします
  4. ダウンロードした MNSIT データを "samplesmnist" に展開します
  5. VisuaStudio にて "samplesmnistsample_mnist.sln" を開きます
  6. "x64 Release" のターゲットにてビルドします
  7. run

Linux(Ubuntu 18.04.1)

1. install tools
% sudo apt update
% sudo apt upgrade
% sudo apt install git
% sudo apt install make
% sudo apt install g++
% # sudo apt install nvidia-cuda-toolkit
% wget http://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_418.87.00_linux.run
% sudo sh cuda_10.1.243_418.87.00_linux.run
2. build and run
% git clone --recursive -b ver3_release  https://github.com/ryuz/BinaryBrain.git
% cd BinaryBrain/samples/mnist
% make
% make dl_data
% ./sample-mnist All

Google Colaboratory

nvcc が利用可能な Google Colaboratory でも動作可能なようです。 以下あくまで参考ですが、ランタイムのタイプをGPUに設定した上で、下記のような操作で、ビルドして動作させることができます。

!git clone --recursive -b ver3_release  https://github.com/ryuz/BinaryBrain.git
%cd BinaryBrain/samples/mnist
!make all
!make run

クイックスタート(Python)

BinaryBrain は pybind11 を利用して Python からの呼び出しも可能にしています。 python3を前提としています。

pipでのインストール

下記のコマンドでインストール可能です。

% pip3 install binarybrain==3.13.*

Windowsなど環境によっては pip3 が存在せず、pip のみ場合は pip3 を pip に置き換えて実行ください。 インストール時にソースファイルがビルドされますので、コンパイラやCUDAなどの環境は事前に整えておく必要があります。 (Windows版はバイナリwheelが提供されるかもしれません)

Python用のサンプルプログラムは下記などを参照ください。

https://github.com/ryuz/BinaryBrain/tree/master/python/samples

setup.py でのインストール

事前準備

必要なパッケージを事前にインストールください

% pip3 install setuptools
% pip3 install pybind11
% pip3 install numpy
% pip3 install tqdm

Windows環境の場合、nvccのほかにも VisualStudio の 64bit 版がコマンドラインから利用できるようにしておく必要があります。 例えば以下のように実行しておきます。 x64 の指定が重要です。

> "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
インストール

下記のコマンドでインストール可能です。

% # install
% cd python
% python3 setup.py install

githubについて

現在 version3 は下記の branch で管理しています

ver3_develop
開発用ブランチです。ビルド不能な状態になることもあります。 最新のコードにアクセスしたい場合はここをご覧ください。
ver3_release
リリース作成用ブランチです。
master
リリースブランチで確認したものを反映。

tag は 開発都合で ver3_build0001 のような形式で定期的に打っており、 リリースのタイミングでバージョン番号のタグを打つようにしております。 (以前はリリースごとにver3_release1 のような形で打つように していました)。

まだ、開発初期で仕様が安定していませんので、再現性の確保などが 必要な際はタグを活用ください。

基本的な使い方

基本的には C++ や Python で、ネットワークを記述し、学習を行った後に その結果を verilog などで出力して、FPGA化することを目的に作成しています。

もちろんBinaryBrain自体は学習によってネットワークのパラメータも求めるまでが 主体ですので、その結果を使ってC言語を出力するルーチンをユーザー側で開発することも 自由です。

C++用のCPU版に関してはヘッダオンリーライブラリとなっているため、include 以下にある ヘッダファイルをインクルードするだけでご利用いただけます。 GPUを使う場合は、ヘッダ読み込みの際に BB_WITH_CUDA マクロを定義した上で、cuda 以下にある ライブラリをビルドした上でリンクする必要があります。

また、BB_WITH_CEREAL マクロを定義すると、途中経過の保存形式に json が利用可能となります。

Python版を使う場合は、import するだけで利用可能です。

使い方はsampleなどを参考にしてください。

事例紹介

リアルタイム認識

実装事例

フルバイナリネットワークで、遅延数ミリ秒(1000fps)での画像認識の例です。

_images/fpga_environment.jpg

下記のようなブロック図となっています。

_images/block_diagram.png

FPGAリソース

いくつかの認識について実験したものを以下に示します。

_images/fpga_resource.png

下記はカメラやOLEDなどの制御回路も含んだものもありますが、例えば MNIST の Simple DNN であればニューラルネット部分はわずか 1460個のLUTのみで88%の認識が可能です。 これは、今手に入るXILINXのもっとも小さなFPGAでも十分収まるサイズです。

これは 1024-360-60-10 の4層構造のネットワークであり、例えば200MHzで動かした場合、 4サイクル(=20ナノ秒)で認識が完了します。そのため極めてリアルタイム性の高い用途への応用も可能です。

もしカメラなどの入力に制約がなく、28x28の画像を毎サイクル供給可能であれば、 コア自体は 200Mfpsで動作可能となります。 これは1つの対象に対して条件を変えながら非常に多くの認識を行える帯域ですので、 1回の認識率は低くても、結果を二次加工することで実用的な認識率を目指すようなことも可能な帯域です。

Autoencoder

通常のバイナリネットワークは出力もバイナリであるため、例えばAutoencoderのような 多値出力が必要な用途には応用が難しいという課題があります。 (入力に関しては最初の数層を多値で扱う手はあります)

BinaryBrainでは、バイナリ変調を用いることで、入力から出力まで全層がバイナリである Fully binary neural network で多値データを扱う方法を提供しています。

MNIST

MNISTでの Autoencoder の実験結果です。

_images/autoencoder_mnist.png

MNIST画像自体が2値に近いのですが、輪郭付近でやや滑らかさが出ています。

CIFAR-10

同様にCIFAR-10のデータセットで扱ったものです。

_images/autoencoder_cifar10.png

ぼやけた感じは否めませんが、多値出力に対してある程度のことができているのは確認できます。

もともとがCIFAR-10のデータセット自体が Autoencoder のような学習を目的としたデータセットではないので、 多値の従来ネットワークでもかなりボケた画像しか作れない部分はあるので、まずは実験的な結果と言えます。

RTLの試し方

sampleの動かし方

C++, Pythonともに Verilog RTL のソースファイルの出力が可能です。 出力したRTLの試し方は

https://github.com/ryuz/BinaryBrain/tree/master/samples/mnist/verilog

の readme.txt を参照ください。

C++ API

概要

本章ではC++のAPIについて触れます。

現時点では細かなドキュメントが用意できておらず、ソースを読み人のために 概要を掴む為の情報を記載します。

なお BinaryBrain のコードは namespace に bb という名称を持ちます。

モデルクラス

基本クラス

すべてのレイヤーはModelクラスからの派生で生成されます。

Model(抽象クラス)

抽象クラスは直接生成できませんが、各レイヤーの基礎となっており、操作を定義します。 以下のようなメソッドを備えます。

SendCommand()
文字列によって汎用的に各レイヤーの属性変更などを行えます。 階層的にサブレイヤーに伝播させることを目的としておりますが、送信先クラス名を指定することで、 特定のレイヤにのみコマンドを送ることも出来ます。 現在の主な用途として "binary true" のようなコマンドで、バイナリ活性層を有効にしたり、 "host_only" コマンドで、部分的に動作をCPU版に切り替えたりできます。 将来的には、部分的に学習時のパラメータ学習を固定したりなど、いろいろな設定を追加していくことを考えています。 文字列なので、自作レイヤーに独自コマンドを追加することも簡単です。
GetClassName()
クラス名を取得します。SendCommand() で、コマンド送付先をクラス名で指定することが出来ます。
SetName()
クラス名とは別にインスタンス個別に自由に名前設定が出来ます。生成時に固有の名前をつけておけば、 後から SendCommand() で、個別に属性変更コマンドが送れます。
GetParameters()
内部パラメータの参照を取得します。重み係数などが取得対象です。 内部パラメータを持った自作レイヤーを作成する場合に実装が必要になります。
GetGradients()
内部パラメータの勾配への参照を取得します。Backward時に値が計算され、主に Optimizer が利用します。 内部パラメータを持った自作レイヤーを作成する場合に実装が必要になります。
SetInputShape()
入力のデータ形状を指定します。戻り値は出力のデータ形状となります。 階層的にサブレイヤーに伝播させることを目的としており、各レイヤーを連結後に呼びさすことで内部パラメータのサイズが決定され初期化されます。 自作レイヤーを作成する場合には必ず実装が必要になります。
Forward()
前方伝播を行います。階層的にサブレイヤーも実行することを想定しています。 自作レイヤーを作成する場合には必ず実装が必要になります。
Backward()
誤差逆伝播を行います。階層的にサブレイヤーも実行することを想定しています。 自作レイヤーを作成する場合には必ず実装が必要になります。
PrintInfo()
レイヤーの情報を表示します。 自作レイヤーを作成する場合に実装しておけば独自の情報を出力できます。

活性化層

Binarize クラス
バイナライズ層です。 Forward では、0を閾値に出力を0と1に二値化します。 Backward では hard-tanh として動作します。 バイナリネットワークの基礎となります。
ReLU クラス
普通のReLU です。 Binarize から派生しており、SendCommand() にて、"binary true" を送ることでBinarize層として動作します。
Sigmoid クラス
普通のSigmoid です。 Binarize から派生しており、SendCommand() にて、"binary true" を送ることでBinarize層として動作します。

演算層

SparseLutN クラス

LUT-Network の LUT に相当する部分を独自のモデルで学習させるためのレイヤーです。 パーセプトロンと異なる独自のモデルを用いており、単体でXORパターンを含めたLUTで 表現可能な空間すべてを効率的に学習可能です。

StochasticLutN クラス

LUT-Network の LUT に相当する部分をStochasticモデルに基づいて学習させるためのレイヤーです。 StochasticバイナリデータがStochastic性を持っている対象への学習に限定されますが、 SparseLutもでるよりも高速に学習させることが可能です。

MicroMlp クラス

LUT-Network の LUT に相当する部分をパーセプトロンを用いて学習させるレイヤーです。 内部は MicroMlpAffine + BatchNormalization + 活性化層 の3層で構成されます。 活性化層 は デフォルトは ReLU ですが、テンプレート引数で変更可能です。

MicroMlpAffine クラス

MicroMlp の構成要素で、入力数を6などに限定した疎結合、且つ、内部に隠れ層を備えた 小さなMLP(Multi Layer Perceptron)の集合体です。 入力数や隠れ層の数テンプレート引数で変更可能です。

DenseAffine クラス

いわゆる普通の浮動小数点による全結合のニューラルネットです。

BatchNormalization クラス

BatchNormalization層です。 活性化層でバイナリ化を行う前段ほぼ必須となってくる層です。

MaxPooling クラス

MaxPooling層です。

LutLayer (抽象クラス)

LUT-Network を記述する基本モデルです。 現在 ver2 の直接学習機能はまだ ver3 には未実装です。 MicroMlp などで逆伝播で学習した内容をテーブル化して写し取ることを目的としています。 テーブル化取り込みに ImportLayer() メソッドを備えます。

BinaryLutN クラス

各ノードの入力数を1つに固定したLUTモデルです。一般的なFPGAに適合します。 入力数はテンプレート引数で指定でき、FPGAでは 4 か 6 のものが一般的と思われます。 入力数を固定することで演算を高速化できますが、ver3 への移植はまだ行えていません。

補助層

Sequential クラス

各種の層を直列に接続して1つの層として扱えるようにします。

LoweringConvolution クラス

Lowering を行い畳こみ演算を行います。

ConvolutionIm2Col + 引数で渡したモデル + ConvolutionCol2Im DenseAffine を渡すと、通常のCNNになり、MicroMlp を用いたサブネットワークを渡すことで、

LUT-Network での畳込みが可能です。

ConvolutionIm2 クラス

畳み込みの為のLoweringを行います。通常、LoweringConvolutionクラス の中で利用されます。 Loweringされたデータに対して BatchNormalization するのも LUT-Network 学習時の特徴の一つかもしれません。

ConvolutionCol2Im クラス

畳み込みの為のLoweringの復元を行います。通常、LoweringConvolutionクラス の中で利用されます。

BinaryModulation クラス

内部でRealToBinary クラスとBinaryToRealクラスを組み合わせて、多値データをバイナリ化して学習するのに利用できます。

RealToBinary クラス

実数値をバイナライズします。 その際にframe方向に拡張して変調を掛ける(多重化)が可能です。 現在、PWM変調と、乱数での変調を実装しており、デフォルトでPWM変調となります(将来⊿Σなどの誤差蓄積機能も検討中です)。 変調を行うことで、入力値に対して確率的な0/1比率の値を生成できるため、出力も確率的なものとなります。

BinaryToReal クラス

多重化された確率的な0と1をカウンティングして実数値を生成します。 RealToBinary 対応しますが、こちらは時間方向だけでなく、空間方向のカウントも可能です。 オーバーサンプリングによる十分な多重化数が確保できれば、回路規模を増加させること無く回帰などの実数値へのフィッティング可能性が出てきます。

モデル以外のクラス

損失関数

LossSoftmaxCrossEntropy クラス

普通のSoftmax-CrossEntropyクラスです。

"

平均二乗誤差を損失とするクラスです。

評価関数

MetricsCategoricalAccuracy クラス

Categorical Classification の精度を評価値とするクラスです。

MetricsMeanSquaredError クラス

MSE(平均二乗誤差)を評価値とするクラスです。

最適化(Optimizer)

OptimizerSgd クラス

普通のSGDです。

OptimizerAdam クラス

普通のAdamです。

実行補助

Runner クラス

構築したモデルのフィッティングや評価などの実行を補助します。 論よりRUN。 Runner のソースが各種の使い方で、参考になるはずです。

データ保持

Tensor クラス

多次元のデータを保持できるクラスで、演算も可能です。 名前に反してまだ Tensor演算は実装できていません。

Variables クラス

複数の Tensor を束ねる機能を持ったクラスです。 形状が同じなら Variables 間での演算も可能です。 主にOptimizerでの利用を想定しています。

FrameBuffer クラス

1つの Tensor を 1 frame として、複数frame を保持できるクラスです。 ただし、内部では、NCHW や NHWC ではなく、CHWN 形式になるように並び替えてデータを保持しています。 これは Lowering されて frame数が十分増やされた疎行列に特化して性能を出すための配置で、BinaryBrainの特徴の一つです。 一方で、一般的な算術ライブラリに適合しない(並び替えが必要)ので注意が必要です。

各種関数

FPGAへのエクスポート

ExportVerilog_LutLayers 関数

LutLayer を Verilog-RTL で出力します。

ExportVerilog_LutCnnLayersAxi4s 関数

畳み込み層を含む LutLayer を纏めて Verilog-RTL で出力します。 MaxPoolingなどの入出力でデータが不連続になる層は最後に1つだけ指定することができます。

Python API

binarybrain module

LUT-Networkとは

概要

LUT-Networkとは、当サイトの提唱するパーセプトロンモデルの代わりにLUT(ルックアップテーブル)のモデルを利用した ディープニューラルネットワークのことです。 重みの乗算の代わりにテーブル引きを行うことで、パーセプトロンでは学習することのできない XORパターンのようなものも柔軟に学習することができます。 乗算を用いない為、低スペックな計算環境でも高速に推論を行うことが可能です。

特にこれをバイナリ化した Binary LUT-Network は、FPGAのLUTに直接変換可能であるため、極めて 高い演算機高率を実現できます。

また以下のバイナリ化の欠点をバイナリ変調技術で克服する方法も提供しています

  • ネットワーク部分は入力初段からフルバイナリネットワークを実現可能で高効率
  • 出力を多値に戻せるため、回帰分析やAutoEncoderなどのアプリケーションにも適用可能
  • FPGAだと1レイヤーの計算が1サイクルで終わるのでナノ秒クラスで認識できる(超リアルタイム)
  • 超廉価&低消費電力なワンコインFPGAから適用が可能

高価な乗算機アレイが不要となるので、特に電力やリアルタイム性、コストなどが課題となる エッジコンピューティング分野にちょっとした認識を実現するなどに適したネットワークです。

Sparse-LUTモデル

LUTによるテーブル参照を誤差逆伝搬で学習させているというと、奇妙に感じられるかもしれません。 驚くことにLUTによるテーブル参照を微分可能なモデルで表現し、これを実現しています。

Stochastic-LUTモデル

その計算モデルは、LUTのテーブル引きの回路演算を Stoachstic演算に置き換えて実験しているときに発見されました。 Stochastic-LUTモデルはSparse-LUTモデルの中にその一部として含まれています。

Stochastic演算とは、確率的な0/1が入力される回路におけるデジタル演算において、その確率値に対して 乗算などの演算子として機能する点に着目したものです。 例えばANDゲートは確率値に対しては乗算器として機能します。

_images/stochastic_and.png

後述しますが、BinaryBrainでは任意のフルバイナリネットワークに対して、バイナリ変調を施したデータを 入出力させながら学習させる機能があり、このバイナリ変調がStochastic演算を用いたモデルを有効に 機能させるのに役立たせることができます。。

さて、早速ですがLUTも回路的には単なるマルチプレクサですので、一度デジタル回路として考えた後に、 Stoachstic演算に置き換えて考えることで下記のように微分可能な計算で表すことができます。

_images/stochastic_lut.png

Wは各ルックアップテーブル内の値に対応し、テーブル内の値が1である確率を表します。 xは入力値が1である確率値であり、yは出力が1となる確率値です。

このモデルを使った学習は、入力同士に相関がなく、純粋に確率値として扱える範疇において、 正しく機能し、出力値が一定の確率で1を出力するようにネットワーク全体を学習させることが 可能です。

内部に備えたWの後にあるBinaeizerをONにして学習すれば、学習完了後にテーブル値は バイナリに置換することができます。

Sparse-LUTモデルの全体像

Stochastic-LUTの計算モデルを活用し、Stochastic性を持たないデータも視野に入れて広く 学習可能にするための疎結合ルックアップテーブル方式のモデルとして、Sparse-LUTモデルを 提唱しています。

BinaryBrain の備える SparseLUT クラスは下記の3つの使い方に対応しています。

_images/sparse_lut_app.png
  • Stochastic 値を扱うネットワークを学習可能
  • 非バイナリ(FP32など)の疎結合ネットワークで従来のパーセプトロンよりも高性能に機能
  • バイナリ疎結合ネットワークでLUTに置換可能なモデルとして学習可能

Sparse-LUTモデルによるFPGA化

Sparse-LUT を、Stochastic演算用や、Fully-Binary 用に利用した場合には、FPGAに 1個に割り当て可能なモデルとして学習させることができます。

特にFully-Binary 用に利用しする場合が広く汎用的に応用可能であり、下記のようなモデルになります。

_images/Sparse-LUT_model.png

バイナリ変調

概要

本章ではバイナリLUT-Networkに限らず、広くバイナリネットワークに適用可能な技術として、バイナリ変調の適用について述べます。 バイナリ変調とフルバイナリネットワークの組み合わせは、本サイトの提唱する技術の1つであり、入出力のに多値データが 要求される場合にバイナリネットワークを適用するための手法です。

従来のバイナリネットワーク

従来のバイナリネットワークでは、多値画像の認識などを行うために、入力側のいくつかの層をバイナライズせずに 多値入力とすることで多値データを扱っていました。 この方法は一定の効果はあるものの、入力層では乗算器を必要とする為リソースが大きく増加する上に、 出力はバイナリであり、クラスタ分類ぐらいにしか応用できないという課題がありました。

バイナリ変調

信号処理の世界にはバイナリ変調という技術があります。 例えばデジタルオーディオなどの分野では 1bit ADC やD級アンプの技術は非常に重要です。 こでは信号をオーバーサンプリングにより、高い周波数の 1bit のデータに量子化することで、 信号処理自体はバイナリで扱うにもかかわらず、入出力データには例えば16bit以上の高品質の 信号を得る技術です。

BinaryBrain では全く同じことをフルバイナリのニューラルネットに行うことで、 非常に小さな回路の認識率を上げたり、Autoencoderや回帰分析などの多値出力を 必要とする分野への適用可能性を広げました。

下記は、通常の Dense CNN の ReLU を Binarizer に置き換え、入力もバイナリ化して フルバイナリネットワーク化したものを用いて、バイナリ変調の効果を実験した結果です。

_images/binary_modulation.png

binary_x1 が1倍のオーバーサンプル、すなわち何もせずに単純にフルバイナリ化した場合ですが、 FP32での結果に比べて大きく認識率が落ち込みます。 そして、binary_x3、binary_x7, binary_x15, binary_x31 が、それぞれ3倍、7倍、15倍、31倍 のオーバーサンプリングでのバイナリ変調を行ったものですが、ある程度の回復を見せている 事がうかがえます。

同じ回路に、より高いフレームレートで、変調したデータを通すだけなので、スループットは 低下しますが、ネットワークを構成する回路自体のリソースは一切変化することなく、認識率だけが 向上しているのが特徴です。