バイナリニューラルネットへのバイナリ変調の適用実験

# はじめに
現在ニューラルネットの分野では、凄まじい速度で新しいネットワークが研究され、それらを使った多くのアプリケーションも日々開発されています。

それらニューラルネットワークの多くは浮動小数点などを用いてGPUや専用LSIを用いて高速に実行されています。
一方で、学習時に関しては浮動小数点を用いるにしても、推論に関してはもっと量子化を行い、低ビットの演算で実施することで、コストや電力を下げ、適用範囲を広げようという試みもなされています。

特に、際立った量子化として1bitまで量子化してしまうバイナリネットワークがいくつか考案されており、これらは計算機の演算量やメモリ帯域を節約するだけでなく、FPGA(Field Programmable Gate Array)に適用することでも高いパフォーマンスを発揮することが知られています。

本記事は、そのようなバイナリネットワークのFPGAへの適用を念等に、本体はバイナリを演算する回路のまま構成しつつ、バイナリデータの方に変調を施すことで、成功改善を試みた際の実験記録です。
十分な実験を行ったとはいい難い部分はありますが、あくまで一例としてご紹介できればと思います。

あくまで限定的な実験ですが、FPGAや小規模マイコンなど、組み込みターゲットにニューラルネットワークのプログラミングに挑戦する人にとって何かのアイデアの参考になれば幸いです。

以前、発表させていただいた[こちら](https://ieeexplore.ieee.org/document/8966187)の中でも今回の技法は用いており、精度の底上げに寄与しております。


# オーバーサンプリングとバイナリ変調について
多値の信号で周波数帯域が決まっているものは、より高い周波数でオーバーサンプリングすることで、より少ないbit数表現に量子化することが可能です。

例えばCD音源は16bit 44.1kHz ですが、より高い周波数で1bit ADC を用いて高音質化されていることは知っている方も多いのではないでしょうか。 D級アンプなどもこの仕組みで、音圧に応じた頻度で確率的に現れる1と0の繰り返しで信号を表現することが可能です。

音に限らず、映像などの場合も同様で、人間の目は60Hz程度から感度が急激に下がっていきますので、例えばOLEDやプラズマディスプレイなどは、ONとOFFの2値しかないのに、これを高速に行うことで人の目には残像現象で鮮やかな階調に見えたりします。

数kHzで動く高度な演算をするプログラムが、数MHzで動くシンプルなプログラムに置き換わるとしたらとても面白いことだと思います。

もっとも簡単なバイナリ変調は[PWM変調](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%AB%E3%82%B9%E5%B9%85%E5%A4%89%E8%AA%BF)ではないかと思います。

このような変調を前提にバイナリニューラルネットワークを学習させたら何が起こるかというのが本実験の趣旨です。

# 実験環境
そうは言っても自由度高くバイナリネットワークを扱えるプラットフォームはあまりありません。
実験は拙作の[BinaryBrain](https://github.com/ryuz/BinaryBrain)という、FPGA用のバイナリネットワーク学習用の環境を利用しました。
この環境自体は、[LUT-Network](https://www.slideshare.net/ryuz88/lutnetwork-revision2)というFPGAのLUTを直接学習させることでFPGA用に高密度のバイナリネットワークを構築することを目的としていますが、通常のニューラルネットもある程度実験できます。

そこで、比較のため、今回は普通のCNNの活性化層(ReLU)のみをBinarizerに置き換えて、バイナリ化による性能劣化と、それを、バイナリ変調によって回復できないかという試みを実験しています。
ニューラルネットは、線形のパーセプトロンで構成された層と非線形の活性化層の組み合わせを重ねて行きますが、活性化層を2値化層にするだけでも一種のバイナリネットワークが出来上がります。

意外に思われる方も居られるかも知れませんが、ReLU を Binarizerに置き換えても案外学習は行われ、MNISTなどではかなり高いスコアを出します(ですので今回は差が出るようにCIFAR-10で行っています)。この手法の良いところは条件を揃えて、バイナリ化で起こる変化を観測できることです。

次に、オーバーサンプリングについてです。BinaryBrainには RealToBinaryクラスという、浮動小数点の入力をオーバーサンプリングしつつPWM変調など施したバイナリに変換するクラスと、BinaryToRealクラスというオーバーサンプリングされたバイナリをカウンティングして浮動小数点に戻すクラスがあります。
これをバイナリネットの前後に取り付けて、オーバーサンプリングの度合いを変えて学習を行います。オーバーサンプリングによりミニバッチのサイズはその区間で増えますが、各層の入出力はバイナリデータになります。
なお、RealToBinaryとBinaryToRealをセットで扱い、バイナリネットをラッピングするモデルとして、BinaryModulationクラスがあり、最新のサンプルプログラムではこれを使っています。

# バイナリ変調の実験結果

BinaryBrain では、binary_mode の設定により、ReLU を Binarizer に切り替えることが簡単に出来ます。この機能とBinaryModulationクラスでのオーバーサンプリング量を変えて実験を行っています。
オーバーサンプリングは例えば、15倍のオーバーサンプリングであれば、最大で15個のビットがカウントできるので、0～15までの4bit分のデータを変調できます。

ここでは、バイナリ化なし(FP32)、x1、x3、x7、x15、x16 のオーバーサンプリングで、CIFAR-10 という画像セットの10クラス分類を8epochづつ学習を行い、その結果精度をプロットしました。
以下にその結果を示します。

![BinModulation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/172a37f5-13b4-6301-ea09-7f01e9df6b2b.png)

当然、FP32がもっとも良い結果を返していますが、それでも単純にバイナリ化した x1 に比べて、x31に近づくほどに精度が回復していきます。
またFP32もtrainデータのスコアは上昇しますが、testデータが飽和しているので過学習が始まっている可能性もあるかと思います。バイナリ化は正則化でもあるので過学習の防止にも一役買っている可能性はあります。

# bit幅を広げた場合のとの効果の比較

ここで、「こんなことしなくとも各層のbit幅を広げればよいのではないか？」というアイデアがあるかと思います。
そこで、各層のbit幅を倍にした実験も行いました。下記のとおりです。

![wide_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/1cff965e-28f3-aac4-9191-db3637a842e1.png)

確かに、bit幅を増やすことで認識率は向上するのですが、その効果は少なくともx1同士の比較では高くは無いようです。一方で、bit幅を増やしてもあまり認識率が向上しない状態でも、オーバーサンプリングでまだ認識率が上げられるようです。

bit幅を増やすと、FPGA化したときの回路規模が単純に倍になりますので、認識率の点では単純にbit幅を増やすのはどこかで効率が悪くなりそうです。空間方向にリソースを増やす場合は、アンサンブル学習などを考えた方がメリットが大きいかもしれません。


# 考察

このオーバーサンプリングは、バイナリネットワークの回路において、空間的なリソースを時間に転嫁したに他なりません。いくら1bitになっても、例えば 16bitの精度を得るのに65535回演算していたのでは電力的に不利です。
一方で、x15で 4bitの精度を得るぐらいであれば、非常に効率のいい方法になりえます。

また、出口の復号化で多値に戻るため、バイナリ回路でありながら回帰問題などに利用できる可能性もうまれてくる可能性があります。


# 実験方法

現在、最新のBinaryBrainのリリースから同じような実験を行う場合は、CUDA SDKがセットアップされた環境で下記のようなコマンドを実行すれば評価できます(それなりのメモリを持ったCUDAの使えるGPUが必要です。私はGoogle Colabを利用させてもらいました)。

```
# github から clone (実験時のリリースタグ)
git clone --recursive -b ver3_release4 --depth=1 https://github.com/ryuz/BinaryBrain.git

# サンプルをビルド
cd BinaryBrain/sample/cifar10/
make

# データをダウンロード
make dl_data


# 単精度実数版
./sample-cifar10 DenseCnn -binary_mode 0 -mini_batch 32 -epoch 8

# バイナリをオーバーサンプリング量を変えて順に実行
./sample-cifar10 DenseCnn -binary_mode 1 -mini_batch 32 -modulation_size 1  -epoch 8
./sample-cifar10 DenseCnn -binary_mode 1 -mini_batch 32 -modulation_size 3  -epoch 8
./sample-cifar10 DenseCnn -binary_mode 1 -mini_batch 32 -modulation_size 7  -epoch 8
./sample-cifar10 DenseCnn -binary_mode 1 -mini_batch 32 -modulation_size 15 -epoch 8
./sample-cifar10 DenseCnn -binary_mode 1 -mini_batch 32 -modulation_size 31 -epoch 8
```

# おわりに
このバイナリ変調機能自体は LUT-Network の初期から実装していたのですが、先だってようやく、一般的な全結合回路で定量化でき、広く既存のバイナリネットワークにも応用できる可能性を感じましたので記事にさせていただきました。

また、今回はx15などの単位で区切っていますが、変調の本質として、連続的に変化する値に対して適用可能であると考えます。

FPGAや組み込みマイコンによるバイナリニューラルネットワークはまだまだ未開拓の部分も多いのではないかと思います。GPUほどの自由度はありませんが、使い方次第でGPUでは出来ない面白い分野への適用も出来ますので、一つのアイデアとして何かのきっかけになれば幸いです。

# 追加情報(2020/05/10追記)
本記事の実験の続きを[こちら](https://ryuz.hatenablog.com/entry/2021/04/03/211029)で行っております。