カテゴリー「Deep Learning」の22件の記事

2020年3月23日 (月)

Deep Learning の開発フロー

当方FPGAで遊ぶために少し齧っているレベルで、この分野はの専門でも何でもないのですが、自分用の整理も含めて少しいつもより上流の視点で簡単な資料にしてみましたので置いておきます。(将来の勉強会などでプレゼン書く時の素材用という意味も含めて)。

SlideShareに置いております。

PowerPiontのデータは ダウンロード - deep_learning_development_flow.pptx

(画像処理分野でしかも認識器に偏った記述になってますがご容赦ください)

 

私はLUT-Networkの開発で初めてDeep Learningに触れたわけですが、従来のアルゴリズムありきでそれをなんらかの計算機(FPGA含む)に実装していくやりかたと、Deep Learning ではどこがどう違うのかということはやはり整理しながら理解していきたいところではあります。

やはりデータトリブンなところを前提に、「欲しいものを手に入れるために用意しなければいけないもの」、「元から持っている人間の知見を入れ込むにはどうすればいいのか?」と言ったあたりが大きく異なるようには思います。

どうしても実用的な使い方を考えてみようとすると、もう少し幅を広げて取り扱っていく必要があり、また違ったスキルが必要になってくるようには思います。

 

 

2020年3月 9日 (月)

LUT-Networkの蒸留とMobileNet風構成とセマンティックセグメンテーション

はじめに

従来のパーセプトロンモデルを使った学習ではなく、回路そのものを微分してFPGAを直接学習してしまおうという当サイトオリジナルのディープラーニングLUT-Networkですが、ここのところ深いネットを学習させるために蒸留(Knowledge Distillation)に取り組んでいました。

その一つの成果として、MNISTデータを使ったセマンティックセグメンテーション(もどき)を試してみたのでブログに記録しておきます。

 

まずは先に結果

まず先に最新の結果を記載いたします。MNISTベースの画像を入力して、それぞれの数字領域を色塗りするセマンティックセグメンテーション(もどき)を学習させてみました。

入力画像
Mnist_test_640x480

 

出力画像

上記の入力画像をもとに Verilog のRTLシミュレーションで得た結果画像が以下です。
Result

 

FPGAリソース

下記が実際にRTLを合成した場合のリソース量です。DNN部のみですが、畳み込みの為のラインバッファなどの制御回路のLUTも含んでいます。

Resource

以上のように、ZYBO Z7-20 のリソースを半分も使うことなく、また外部メモリを使うこともなく、上に記載のリソースのみでスループット1のDNN回路が出来上がりました。MobileNet風の接続の導入で配線がスリムになったことで合成時のルーティングなどでの問題もまったく発生していません。
一応、学習済みのソースをここに置いておきます。

ダウンロード - mnist_seg.zip

 

本回路はビデオ伝送路にラインバッファ分の遅延のみで、ビデオ信号の経路にフィルタとして挿入可能です。またLUT-NetはLUT1段で1層ですので非常に高クロックで合成可能で、例えば300MHz程度で合成も可能です。ピクセルクロック300MHzといえば4k画像30fpsに相当しますといえば想像しやすいのではないかと思います。

細かい部分はまだいろいろチューニング余地がありますが、まずはフレーム単位でメモリに溜めないと演算できないGPUに対してリソース量もリアルタイム性も非常に高いFPGAでのAIの可能性が少しだけ広がったのではないかと思います。

 

ネットワーク構成

ちなみにFPGAリソースこそ少ないですが、depthwise CNV と pointwise CNV で合わせて57層という結構深いネットになっています。
一応、出力させたネット構造のみテキストファイルで置いておきます。

ベースモデル:ダウンロード - training_model.txt
蒸留後のモデル:ダウンロード - lutnet_model.txt

 

MobileNet風の構成と蒸留(Distillation)の小規模実験

今回の進展にあたり、大きな変化となったのが MobileNet v1 風の考え方の取り込みと、蒸留(Distillation)という考え方です。
まず、セマンティックセグメンテーション風の色塗りをやる前に行った、普通のMNITのクラス分類で適用したネットワークの構成が以下です。

Mnist99_net

MobileNet v1 を参考に depthwise/pointwiseの畳み込み層を準備しています。その際にLUT-Net特有の改善としてdepthwise層の出力チャネルを入力チャネルより多くし、同じチャネルを処理するノードを複数割り当てています(今回は各4個づつ)。
全結線のノードと違い、深さ方向に各1個だと1チャネルの属性を十分捉えらてないため、異なる成分をとらえることが可能になるように多様性を持たせています。なお、多様化した結果、結局使われなかったノードは後の蒸留時に消滅しますのでここは安心して増やすことが可能です。

ちなみにこのMobileNet風の構成は蒸留することなくランダム結線のままでも比較的効率よく学習してくれる模様で、以下がその学習時のログです。

Mobile_lut_net_train_graph

63epoch目で 99.08% の認識率まで確認しています。
特徴的なのが、train と test でほとんど挙動に差がなく、LUT-Net の汎化性能に関してはパーセプトロン型のDNN同等以上の可能性も感じています。

 

蒸留の効果確認

ちなみに先の例では蒸留の有無にかかわらず99%超えてしまい、効果がよくわからないのでもう少し回路を小さくして実験しました。
その際の回路構成が下記です。
Mobile_lut_net_little

LUT 3k個程度の小さな回路ですが、飽和するまで回したところ

蒸留した場合:98.42%
ランダム結線:97.06%

となり、蒸留によって結線の最適化をした方で若干の改善が見られました。

 

まとめ

LUT-Netの開発もしばらく停滞していた感もありましたが、MobileNet構成の実験と、蒸留の可能性が見えてきたことで適用可能性が一気に増えてきたように思います。
特に全結線層を使えばバイナリネットであっても、比較的深い層も通常のDNN同様に学習できることが見え初めてきましたのでそこからLUT-Netに持ち込めるというのは大きな成果に思います。
またその際もすべて全結線層にするのではなく、depthwise層は初めからLUT-Netにしていますので比較的相性良く組み合わせる手が見えてきたと思っております。

引き続き、もっといろいろな応用ができればと思う次第です。
今回のセグメンテーションは、Pooling層無しで行っており、すべてのノードが100%演算している反面、フィルタの段数分(ラインバッファの範囲)でしか判別ができません。よって小さな範囲を見て判定できるアプリにしか使えませんが、それでも小さい範囲で認識が完結する欠陥検知や異物検知系の見分け補助とか、線画やモノクロ写真の色付けとか、がリアルタイムでできないものか、とか、いろいろとワクワクする応用例は思いつくわけです(実用になるかは別として)。

広い領域を見るにはU-Net的なことをする必要が出てきますが、その場合は一度中間結果をメモリに入れないといけないのと、PoolingするとLUT-Netの場合演算器の利用効率が下がるので、やるなら並列して1frame前の画像を通常型の演算器でやって、最新フレームの入力に追加チャネルとして入れるのがいいのかなと思っていたりもします。まだまだ先が長いですのでのんびり取り組んで行ければと思います。

 

 


追記(2020/03/13)

セマンティックセグメンテーションについて動画を作りましたので置いておきます。
全体の合成でも配線混雑やタイミング収束などで苦しむことは全くなく、あっさり合成出来きました。MobileNetの効果は大きいです。
1msの遅延で1000fpsがAI処理付きでカメラからOLEDに通り抜けており、OLEDが画面がまるでガラスのように向こう側が遅延なくAI重畳されて映し出されています。
AI眼鏡の可能性の提示と、電脳コイルの世界への入り口となること祈りつつ。

 

 

2019年12月29日 (日)

LUT-Networkの蒸留(Distillation)について

今日は、Networkの蒸留(Distillation)について考察してみたいと思います。

LUT-Networkに限らないのですが、その課題に学習時のGPUのメモリの不足と、学習時間の問題があります。


例えばMobileNetという軽量なネットワークがあります。これは畳み込み層をpointwiseとdepthwiseに分けて行うことで係数の数を大幅に削減しています。
軽量にすると学習時も計算量も減るように思ってしまいますが、実際には処理を分割したことで層が増えてしまい、逆にGPUの消費メモリが増え、学習時のメモリへの読み書きも増えてしまいます。

この延長で、超SparseであるLUT-Netは大規模な学習が非常に困難です。そこで、大規模な学習がしやすいネットワークで学習してから、メモリに収まる範囲で少しづつLUT-Networkに写し取っていくというというアイデアを考えています。
これを蒸留(Distillation)と呼ぶそうです。

現在、取り組もうとしている流れが以下の図です。

 

Distillation

 

まず、一般的な畳み込み(Convotution)ネットワークの畳み込み層をバイナリ化する場合、ReLUなどのActivation層を2値化を行うBinary-Activation層に置き換えることになります。この時、2値化の手前で BatchNormalizationを行うのが効果的です。

BatchNormalizationは通常 Inferrence時には単なるスケーリングのみとなります。スケーリングのみであるため、2値化と組み合わせる場合、2値化の閾値自体をずらしてやればスケーリングも不要となります。

しかしながら、ここで前提として計算の中間結果が非バイナリであることが求められます。GPUを用いる場合、GPU内の内部演算が一時的に多ビットになることは特にデメリットはありません。しかしながらFPGA化する場合、そのままリソース規模に効いてくるのでこれは致命的です。
また、画素サイズ分のスケーリング係数が発生するので規模の点でも非常に厳しくなります。

そこで、LUT-Networkでは、当初より BatchNormalizationとBinary-Activationを畳み込み処理に取り込んでいます。これは工夫したというより、こうするしかLUT-Net は成立しないので、特に気にすることなく当初からこうしていたわけですが、LUT-Netの特徴の一つでもあります。
一般的な畳み込み(Convotution)は、効率化のためにGPUなどで一括で計算されますが、処理量やメモリを増やして良ければ、Loweringを行い、im2col と col2im の処理に挟まれる形でDenseAffine計算に分解することができます。

Loweringを行うと、学習時のメモリ消費や計算時間は増えますが、畳み込みの中にDenseAffine以外の処理が入れ込めます。

今回考えているのは、従来のネットワークに、LUT-Networkで行っているこれらの仕組みのみを適用して学習させ、学習後に蒸留して取り込もうというアイデアです。

蒸留は、今のところ

  • 元のDenseAffineの重み順で結線すると効果的
  • 元のDenseAffineの結線数が少ないと模倣しやすい(おそらくMobileNetは効果的)

などが、見えており、もうひと頑張りといったところです。

なお、今回は蒸留を目的に従来のネットワークを変形して学習させようとしていますが、おそらくこの変形は蒸留することなくそのままFPGAにしても効果的ではないかと考えています。

 

DenseAffine + BatchNormalization + Binary-Activation はInferrence時に XNOR-Net のような構造にできるので、そのままHLSなどで合成すれば効果的にFPGA化できる可能性があります。

 

先日の記事で、他のバイナリネットワークに対するLUT-Networkのリソース規模の大雑把な比較を行っていますが、その際に前提としてLUT-Networkに対して行っている上記のような技法を適用しているのを前提としていた部分がありました。

またXNOR-Netについてはかなり概算が入っていたので、実際にいくつか合成実験も行ってみました。
私自身XNOR-Netは試したことがないので理解不十分かもしれませんが、理解の範囲で実験しています。


Xnornet_20191229212001

思っていたよりXNORネットはFPGA化において高効率に思います。

一方で全結線という点ではNに対してO(N log N)で規模増大する可能性を有しているのと、従来のパーセプトロンモデル以上のことはできない(単段ではXORが解けない)など、LUT-Netに比べて非効率な部分はあるかと思いますが、如何せん、PyTorchなどの既存のプラットフォームで学習できる可能性がある点で、手が付けやすいのは事実かと思います。

LUT-Net が一般的なネットと遠いので、なかなか蒸留できる接点を作っていくのが難しいのですが、FPGA化時の高効率化のためのポイントを押さえていけば、その中間的なところも組み立てていけそうな気もしますし、それ自体もいろいろと面白い検討ができそうな気がしています。

2019年12月15日 (日)

FPGAでのDNN(Deep Neural Network)の整理(LUT-Netまとめ)

はじめに

 現在DNN(Deep Neural Network)の実装において、FPGAの活用はしばし議論の対象になっています。もちろんDNN分野全体からするとニッチな部類に入るとは考えますが、FPGAベンダーはここに非常に力を入れており、作成したネットワークのデプロイ先としてFPGAが選択しやすくなるような各種のソリューションが用意され始めており、日々進化しています。
 ここでのFPGAのメリットは、低消費電力であったり、コストであったりします。DNNの実行にはクラウドであっても電力というランニングコストは馬鹿になりませんし、エッジコンピューティング、特にバッテリー駆動のモバイル分野においては電力は極めて重要です。またイニシャルコストの重要性はどちらも同じでしょう。

 ここでFPGAベンダーはこぞって、「GPUと同じように開発できます」をキャッチフレーズに、GPUを使って研究開発をしている多くのAI技術者を取り込むソリューションの開発に力を入れています。学習(Training)自体はまだまだGPUの力を借りることが必須ですので、仕上がったネットワークを推論(Inference)専用に最適化してデプロイするときにGPUと同じように扱えるメリットは非常に大きいのです。

 一方で、これではFPGAにGPUと同じ計算を少し効率的にやらせているに過ぎないことになります。FPGAの面白さは「GPUと同じことが効率よくできる」だけではなく、「FPGAでないとできない」ことができることにあると私は考えます。
 ここ1年程、そのような観点でFPGAでのDNNに取り組んできましたので、ここで少し整理しておきたいと思います。


DNNをFPGAに実装する2つのアプローチ

 下記は、私の考えるDNNをFPGAに実装する際の2つのアプローチのイメージ図です。
 「GPUと同じように使う」のが右側、当サイトの主張する「FPGAらしく使う」のが左側です。
 ネットワークの探索や試行錯誤、学習自体はGPU上で行い、それをFPGAにインプリメントするという流れは同じです。
 ただし、おそらく左のアプローチは学習フェーズで右側以上にFPGAを意識しておく必要があり、より高い次元でのシステム最適化スキルが必要となります。開発も辛うじてHSLが使える程度で、本格的にやるとRTLがまだまだ必要な領域になります。

Dnnfpga

 

GPUのように使うという事

 先に図の右側のソフトウェア的なアプローチ、すなわち「GPU的に使う」場合のアプローチについて述べます。

 この場合、まずソフトウェアでのネットワーク記述ありきの状態で、これをFPGAを使ってどうアクセラレートするかという話になります。
 ですので使い方としてとてもGPUと似てきます。
 しかしながらGPUが汎用計算機であり、INT1からFP64まですべてに対応したエンジンが固定数だけ搭載されているのに対して、FPGAでは本当に必要な演算のみを必要な個数だけ構成することができます。特にINT8以下の領域などを狙った実装などではGPUに対して優位性が出しやすくなってきます。

 その一方で、システムのデータフロー自体はソフトウェア的であり、電力やコスト以外でFPGAならではの効果を付与するのは難しくなってきます。

 

FPGAらしさを活かす試み

 次に先の図の左側のハードウェア的なアプローチ、すなわちFPGAをFPGAらしく、得られたネットワークを計算する専用の回路、すなわちネットワーク構造そのものを計算構造としてインプリメントしてしまおうという試みについてです。

 こうなると既存のソフトウェアのデータフローはいったん置いておいて、「ニューラルネットワークを計算する計算機アーキテクチャをゼロベースで新規に考える」というところが出発点になりますので、大変な反面、とてもたくさん新しいことに取り組む余地が生まれてくると考えています。

 最大の利点は、低遅延やリアルタイム性だと考えます。入力に対して「計算」以上のことは何も行わずに出力に直結可能ですので、理屈上最短の遅延で結果を出力することができますし、計算以外ではメモリ転送用の回路などのリソースも不要になります。一方で、メモリを介さないため演算器は常にカメラやLCDなど外部機器と同期して計算できることも求められます。
 メモリと異なり、計算が追い付かない場合にカメラに対してready信号を下げて「ちょっと待ってくれ」といってもカメラは待ってくれません。このような計算機の外にあるデバイスとの連携しながら計算することこそがエッジコンピューティングの醍醐味であり、FPGAの得意とする領域と考えます。実際にFPGAが得意とする画像処理の中にはこのような仕組みの中で実装されているものも多数あります。今後ロボティクスなどとの融合でも一層重要になると考えます。その枠組みにそのままDNNを入れてしまうというのは「FPGAらしさ」を活かすための第一歩の取り組みかと考えています。

 

 

FPGAらしさを活かす為のテクニック

 ここでFPGAらしさを活かすのに有効な取り組みを考えてみたいと思います。これらは現在取り組み中のBinaryBrainにて順次実験中の内容です。

 FPGAに搭載されているリソースの量は決まっており、上げられる周波数もあらかじめ決まっていますので、時間当たりに割り当てできる回路リソースの上限はどのような手法をとっても大きくは変わりません。そうした「FPGAらしい使い方」を目指す中で、スループットを1サイクル1データを維持したいといったエッジコンピューティングならではの要件も出てきます。
 そこで、DMAなどの演算に使わない回路リソースを減らしたり、FPGAのリソースを有効利用できる演算方式を使った学習を行ったり、演算器を休まず稼働させるアーキテクチャを考えたりして、更なる付加価値性能を引き出すテクニックを考えていくことになります。

 

量子化(Quantization)

 量子化はあらゆる場面で役立ちます。単純に演算bit数が半分になれば回路規模は半分になりますので量子化の効果は大きいです。この方法をデータが1bitになるまで適用したものがバイナリネットワークとなります。そしてネットワークのバイナリ化はFPGAの性能を引き出すうえでは最もパワフルな手法の一つであります。
 DNNで最も演算量が多いのは乗算となりますが、重み(Weight)と活性化(Activation)のどちらか一方をバイナリ化すれば乗算が加算に置き換わり、両方をバイナリ化するとXNOR演算に置き換わります。

 しかしここで注意点があります。FPGAの場合、乗算は乗算器マクロを使うのに対して、加算やXNORは通常LUTを使うのでリソースバランス的に必ずしもメリットがないケースもあります。ハードウェアマクロとして持つ乗算機は数は変わらないので使わないともったいないという話にもなります。
 また、XNOR-Netなど乗算部をXNORに置き換えたものであっても、そのあとの加算(bitカウンティング)は複数bitで行う必要があるという点があります。例えば256個の接続がある場合、Binary Activation を行うまでの加算は最終的に8bitの出力が求められます。ソフトウェア的には積和演算のループでよいのですが、計算構造として空間展開すると特に加算部分がやや大きな加算器ツリーを構成することになってくるのでここは注意点です。

Sparse化/枝刈り(Pruning)

 私は、ニューラルネットワークのSparse化がGPUに比べてFPGAに特に効果が高い手法ではないかなと考えております。N個の入力にM個の出力を行う全線接続層(fully connected layer)を考えた場合、N×M の積和演算が発生し、バイナリ化しても内部では log2(N)のbit数の計算が求められます。
 したがって、接続数Nを減少させることができれば計算量は減少します。もともとDropoutなどの技法が通用する背景には学習させるときに必要な自由度確保のための接続数と、最後に本当に必要な接続数の乖離にあると思われますので、学習後に枝刈りが成立するのはある程度自然なことなのかと思います。
 接続数を抑えるという観点で MobileNet などのアプローチも有効である可能性は高いと思われます。

 しかしながら、ソフトウェア的アプローチを行う場合、しばしば「行列乗算のアクセラレータを作ってこれを共有演算器として使う」という事が行われます。その場合演算の対象が枝刈りなどで疎行列(Sparse Matrix)になったからと言って特別な仕組みを入れない限り「係数を0にしてそのまま計算した方が速い」ということになります。
 この点についてネットワーク構造をそのまま回路構造に反映させる手法においては、コストゼロでSparse化の恩恵に預かることができます。

 また、枝刈りとは異なるアプローチで、初めからSparse化して学習させるという点でFPFAでのSparse化の究極形が当サイトの主張するLUT-Networkだと考えております。LUT-Networkでは接続数を4や6などに固定して学習させることでFPGAのLUT構造そのものに学習結果を対応付けさせるので高効率にインプリメントが可能です(究極的には2に固定してゲートレベルの回路を学習で生成する可能性も秘めています)。

 

パーセプトロンよりFPGAに適したモデルの適用

 これもLUT-Network で主張するところであり、LUTで計算可能な範囲を効率するモデルとしてStochastic-LUTという構造を提案しています。パーセプトロンのモデルが複数層無いとXORが解けないのは有名な話ですがLUTは1層でXORが解けます。ですのでLUTの表現能力を活かせる学習の為にはパーセプトロンのモデルでは不足するわけです。
 Stochastic-LUTでは、各入力を0/1ではなく「1である確率」として扱い、LUTの値も「テーブルに1が格納されている確率」とし、「出力が1になる確率」を求める演算となります。一定条件下でこの演算はStochastic演算としてそのままバイナリ回路を学習できますし、そうでない場合でも演算モデル自体がLUTを汎用に学習させるのに使えることが見えてきました。
 このようにStochastic演算を考えていた時に見つけた計算モデルですが、計算式としては N次元の線形補間であり、バイリニア→トリリニア→ を4次や6次に拡張したモデルと等価です。
 多次元を絵にするのは難しいので簡単のために2入力LUTとして、XORを学習した場合を図にすると以下のようになります。
Stochastic_lut_xor
 ルックアップテーブル値で保持する内部の状態はXORも表現可能であり、これはそのまま誤差逆伝搬により学習され様々な形に変形可能です。

 つまるところDNNは、なにも積和演算器を中心としたCPU/GPUに合わせて学習する必然性はなく、微分可能な演算式であれば様々なモデルが学習可能です。FPGAなどを考える場合、先入観にとらわれずに、そのアーキテクチャゲートレベルで見たもっとも演算効率の良いモデルで学習するということは十分成り立つ話なのです。このような考え方は、そのデバイスのリソースの有効利用をして性能向上を図るうえで十分に効果的だと考えています。

オーバーサンプリングによるバイナリ変調(Oversampling Binary Modulation)

 当サイトの主張する第二の技術としてバイナリ変調に触れておきます。バイナリ化は回路削減に大きな効果がありますが、認識精度を低下させる要因でもあります。
 XNOR-Netなど、初段のいくつかの層は多値で扱うというアプローチも多いようですが、当サイトではフルバイナリに挑戦しています。

 一般に、回路効率を上げる方法として「係数を変えて回路を再利用する」というのがよくあるソフトウェア的なアプローチです。汎用的な行列演算エンジンを作っておいて、そこに次々と新しい係数+データのセットを投入することで1つの演算器を何度も再利用し、計算効率を上げます。
 ここでハードウェア的なアプローチとして「係数を変えずに回路を再利用する」というテクニックでアプローチしているのがバイナリ変調です。回路は同一のバイナリネットワークのまま、そこに投入するデータの方をオーバーサンプリングして複数の認識結果を得て再統合することで認識率を上げます。リアルタイムでのデータ水増し(Dynamic data augmentation)ともいえるかと思います。
 このテクニックはリアルタイム性の増加ともとても相性が良く、bit数を増やすという空間方向の演算拡張に対して、時間軸にデータを拡張しています。

 CNNが空間方向の係数の再利用であれば、この方法は時間方向の係数の再利用と言えるかと思います。

 

蒸留(Knowledge Distillation)

 これはまだまだこれからの取り組みなのですが、蒸留の技術は非常に重要だと考えます。というのもバイナリネットワークは深いネットワークを学習するのがまだまだ難しいからです。これはネットワークの構造的な問題と、学習時の演算リソースの両方の問題があります。
 そういった中で、一旦、従来型の方式で学習した結果を参考にFPGA向きの回路に結果を反映させていくという手法は今後重要になると考えます。

 蒸留については現在、全線接続されたバイナリネットで学習して、全線接続層を複数のSparse層に蒸留して写し取る実験を行っているところです。成果が出ればまたブログに書きたいと思います。

(2020/03/11追記:ある程度蒸留が効果を出し始めました

ストキャスティック演算(Stochastic Operation)

 量子化されたネットに、バイナリ変調のような時間軸拡張されたデータを扱う場合、これはとても面白い考え方だと思っております。
 実際量子化されたデータを確率的に扱うと、確率共鳴のように本来切り捨てられたはずの量子化単位以下の情報が浮かび上がってきます。これらに対する演算を確率値を確率値のまま扱うことができれば幅が広がりそうです。バイナリに関する扱いであれば、ストキャスティック演算は実装上は乗算機が単なるANDゲートになるなど非常に効率的です。
 実際、MNISTの学習ぐらいならそこそこまでうまくいくようです。まだまだ課題が多いのですが、リアルタイムイな処理系の中に信号処理として入れ込む場合、連続時間の中で様々な角度で信号を見ていくことができる例だと思っています。

 

おわりに

 今回、少しFPGAで行うDNN全般の視点からLUT-Netの位置づけを見直してみました。
 こうしてみると、LUT-Netに限定する必要なく、FPGAでDNNをやる場合に重要になる技術がある程度整理して見えてきますし、FPGAでDNNをやる新しいメリットを探索する場合の手掛かりになる部分も見えてくるのではないかと思います。

 「FPGAでDNNをやろう!」といったときに、「VitisやSDxでDNNをFPGA化して終わり」ではなく、もっともっとFPGAらしい面白い使い方を探索してみる余地があるのではないかと思っております。
 AIの時代もFPGAが活躍して、くれると嬉しいなと思います。計算機アーキテクチャを考えるということは引き続きとても楽しいことであってほしいと思います。

 LUT-Netの方も、まだまだ取り組み途中で、やることはたくさん残っていますが、引き続き、無理のない範囲で楽しくやっていきたいと思います。引き続きよろしくお願いいたします。

 

2019年12月 8日 (日)

LUT-NetのFPGAリソースについて

久しく作業が止まっていましたが、今日はLUT-Netと従来のネットワークとのFPGA化したときのリソース規模の違いを俯瞰しておきたいと思います。

あらかじめ断っておきますが、ここで行う比較は認識率が異なるので公平な比較ではありません。通常のNN(Neural Network)は、まずやりたいことができる認識率を確保して、その後に、「それをいかにして速く小さくインプリメントするか?」が命題となります。

一方で、LUT-Net は初めからFPGA内で使えるリソースとハードリアルタイムな時間要件を先に決めてから、「そのリソース範囲でどこまで認識率が上がるか?」というアプローチをとっています。したがってそもそも土台が違うので一概に比較できません。

しかしながら、LUT-Net がどういうオーダーでリソース追求型のネットワークであるかの規模間を可視化するうえで役立つと考えますので、このような比較を載せておきます。

まず、先にLUT-Netでの畳み込みによる32x32サイズの画像分類に用いているネットワークの概算が下記です。

Lutnet

トータルとして、LUT数 153k, BRAM21個 のみですので、既存のFPGAに入れ込むことが可能です。
特徴として、スループットが1であり、1cycle毎に1認識の結果を出力可能です。つまり画像処理において、FPGAの動作周波数=カメラピクセルクロックとすれば、リアルタイムにすべての画素一の認識が完了します。VGA程度であれば1000fps(1kHz)動作も可能であり、認識処理のリアルタイム活用が可能です。

 

次に、この1cycle毎に1認識の性能を、通常の INT8 の CNN などで、同じ構造のネットワークを組んだらどうなるかの試算です。


Cnn

基本的にConv(畳み込み)もFC(全結合層)も、行列演算ですので、N×M個の乗算機が必要になります。そしてそれらを積算するのに接続数に比例する加算器が必要となります。ですので、計算上は、現存するどんなFPGAにも入りきれない、超巨大なリソース規模になります。

 

 

次にXNOR-Netの場合です。XNOR-Netは接続も重みもバイナリなので、リソースを大きく削減してくれます。

Xnornet

しかしながら、基本的に 乗算機がXOR演算となり、各種演算が INT8 から 1bit 化しただけなので、オーダー的には各演算が 1/8 になった程度の効果しか得られません。

 

もちろんスループット1なんて考えなければこんなことにはならないわけですが、それでもFPGAを使うメリットとしてはこの領域が必要な信号処理は多いわけで、そういったケースにAI的な適用ができれば非常に面白いのではないかと思う次第です。

 

2019年9月 2日 (月)

MNIST認識のリアルタイム動作環境更新

先日MNIST環境強化したのですがTwitterに書き込んでそのままだったので今更ながら更新しておきます。

今回は

  • Data Augmentation により、若干のスケール/回転/併進/ネガポジ耐性を付与
  • 数字部分かを判定する2クラス分類器を追加し、判定できない箇所の判定を抑制
  • LPF(ローパスフィルタ)として、1-TAPのIIRを追加(exponential smoothingが可能)

の3点が変更点となります。

 

Mnist_ar

 

これがシミュレーションの結果です。上半分が入力データで、下半分が検出結果を重畳したものです。

色は抵抗のカラーコードでして

黒:0 茶:1 赤:2 橙:3 黄:4 緑:5 青:6 紫:7 灰:8 白:9 となっています。

Sim_result

 

合成後の回路規模です。システム全体の規模となります。

Fpga_result

こんな感じでモノクロ反転した文字も認識しています。

おかげで、手で書いた文字もまあまあ行けました。

 Photo_20190902215501

 

ローパスフィルタは下の図のような感じです。入力と出力の間には遅延素子はなく、あくまで過去のデータも加えて結果を作るというだけで、本質的な点でリアルタイムが可能なデータパスであることが重要です。

なお、1000fps駆動なのでフレーム間の連続性も高く、過去の結果も活用することで認識精度の向上が可能です。

ただし画像の場合は音声などの1次元のデータと異なり、1タップのフィルタを組むのに画像1フレーム分のメモリが必要なところが課題です。

Iir

で、シミュレーションだと、数%程度認識率が上がるのですが、90%越えたあたりで数%変わっても見た目は良くわからないですね。

Lpf

この実物が見たい方は是非ICCE BERLIN 2019にお越しください(私は行けませんが)。

 

なお、現在はさらにDVI出力も付与しております。

Dsc_1204

 

余談ですが、認識結果ではなく画像の方にLPFを掛けると1000fpsの画像を60fpsのモニタに出すときに美しく出来ます。

OLEDなどの1000fpsの表示装置だと人間の目がLPFの役目を果たしてくれるのですが、LCDモニタの場合、そのまま間引いて表示すると露光時間不足でザラザラになってしまうので。

Lpf_dvi

ただしこれもメモリを消費するので、使い方は限定的ですね。

 

ちなみに今、これらの学習を行う BinaryBrain の Python 対応を developブランチで進めております。

まだまだ掛かりそうですが、頑張ります。

 

<追記 2019/09/06>

そういえば最新のリソース状況とかUPできていなかったので追加しておきます。

あくまで合成まで確認できているネットの中での認識率です。ソフトのみの実験とかだともう少しいけてます。

Lutnetwork_resource

2019年6月10日 (月)

LUT-NetworkでAutoEncoder

AutoEncoderが動いた

しばらく開発に打ち込んでいたり、いろいろと行事もあって、ブログの更新が出来ていませんでした。

その隙にLUT-Networkの開発の方は順調に進みまして、いくつかの成果が得られています。

Twitterの方には報告していましたが、流れていってしまうのでひとまず最新の成果であるAutoEncoderの適用をブログにも書いておきます。

MLPでの最初の1枚

下記が、一番最初に何も考えずに、もともと作っていた、MLP(Multi Layer Perceptron)でMNISTを行うネットの出口を32にして、Encoder適当につけて回したときの1枚だけの画像ダンプです。x7 でのオーバーサンプルでバイナリ変調していますが、回路自体はFPGA用のバイナリ回路です。

きっと歴史的な1枚に違いありません(大嘘)

Auto_encoder

まさか動くと思ってなかったのですが、見てのとおりなんとなく数字の7が見えます。

急にやる気が出てきました。


最終段でのBatchNormalization取り外し

さて、先ほどの数字の周辺ですが、MNISTでは外周は殆ど黒で学習は簡単なはずなのに砂嵐状のノイズがいます。

これは今回、律儀に全部のBinarizerの前にBatchNormalization を入れていたので、「全部0を出力する」ということが出来なくなっていたためと思われます。

早速、最終段のみ BatchNormalization を取り外して、1 epoch だけ回して見たのが下記です。
Auto_encoder2

1 epoch なので、いろいろイマイチですが、BatchNormalization の課題は見えてきました。

もしかしたら最終段以外もBatchNormalizationの多用は、偏った表現を禁じてしまう点で不利な部分もあるのかもしれません。今後の研究課題です。

CNNに挑戦

さて、AutoEncoder といえばやはり畳み込みでしょう。

ということでUpSamplerを作って、ちゃんと普通のAutoEncoderを作ってみました。

Ae_cnn

間は32bitに絞っているので、表現能力はそのレベルですが、かなり特徴を掴んだエンコード&デコードが出来ているようです。
AutoEncoderは派生でいろいろな応用があるようなので、今後まだまだ改善の余地はありそうです。

 

おわりに

なお、私は、普通のネットで普通のAutoEncoderをやったことがありません。

自作プラットフォームで、バイナリで、疎結合で、基本素子もパーセプトロンとは異なる独自素子で、FPGA化用。
それでも動いちゃう不思議な面白い世界です。ちゃんと絵が出ました。
もちろん先人の情報あればこそですが、面白くなってきました。

ちなみにAutoEncoderが動くということは、認識だけじゃなくて、生成もやれる可能性が出てきたという理解でおります。流行のGANとかも限定的になにかやれるかもしれないですね。

なお、AutoEncoderの前に実験して、これまた Twitterにだけ流して終わっていた、バイナリ変調については、調子に乗ってQiitaに記事を書いてみました。

2019年4月29日 (月)

パーセプトロンに代わる素子の可能性について

新しい学習モデルの可能性

 前回までFPGA回路の為に、FPGA内のバイナリLUT(ルックアップテーブル)の値を学習させることをターゲットにやってきました。
今日は、今日はその試行錯誤の副産物として、バイナリとかFPGAとかから離れた、普通のCPUやGPUでやるディープラーニング向けの新しい学習モデルについて可能性を提起してみたいと思います。
 相変わらず、第三者の検証を挟んでいないという点でトンデモ科学の領域なのですが、ネタをネタとして楽しめればなと思っております。

(おさらい)Stochastic演算でのLUTモデル

 本題に先立って、今日紹介するモデルの元となった、Stochastic演算でのLUT記述について記載します。

 Stochastic演算についてはこちらの記事などが分かりやすいと思います。ハードウェア派の人にはD級アンプなど想像してもらうと分かりやすいかもしれませんが、オーバーサンプリングの世界では、実際の値を0/1を出現頻度に置き換えて取り扱うことが可能です。
AND/OR/NAND/XOR...など、すべてのデジタルゲートは、入力の0/1を出現確率として捉えた場合、その演算を確率演算に置き換えることが出来ます(前提として互いが無相関にバイナリ化されているとみなせることが前提ではありますが)。

 要するに、デジタルの組み合わせ回路であれば、どんな電気回路でも入力を確率的な0/1の羅列として扱えるシーンであれば、すべて確率演算に置き換える事が可能であり、LUTもその例外ではありません。そして、重要なのはLUTはテーブル次第で非線形だろうがどんな計算でもフィッティングできる万能演算素子であるということです。
 加えて、確率演算は基本的に単なる乗算なので、このレベルまで分解すれば、FPGAのLUTを含めたどんな回路でも置き換え可能であり、しかもそれは微分可能であり、誤差逆伝播で学習可能です。
 そして、実際に現実世界のデータの多くは元々確率的に振舞いますし、そうでないデータもデジタル量子化の段階でそう振舞うように変調することは比較的容易なので、この手法は意外に応用性の広い技術となります。

 6入力LUTのモデルを書くと大変なので、以下に2入力LUTのStochastic-LUTの演算グラフを書きます。

Lut2

 このグラフの理解はあまり難しくはありません。2入力LUTを仮定した場合、入力が2bitでテーブルが4個存在します。
 テーブルの値が上の図のW0~W3の4個に対応します。2つの入力を持ち、入力0に1が入力される確率をx0,入力1に1が入力される確率をx1、出力が1になる確率をyとしています。それぞれ1になる確率なので、例えば入力0が、0になる確率は 1-x0 で計算できます。
 テーブルの0番(W0)が参照される可能性は 入力がすべて0の時ときですから、(1-x0)*(1-x1)となります。このように

 W0が参照される確率:(1-x1)*(1-x0)
 W1が参照される確率:(1-x1)*x0
 W1が参照される確率:x1*(1-x0)
 W1が参照される確率:x1*x0

 となり、これらの総和が出力が1になる確率で、二値化関数を Bin(x) とすると
 y = Bin(W0)*(1-x1)*(1-x0) + Bin(W1)*(1-x1)*x0 + Bin(W2)*x1*(1-x0) + Bin(W3)*x1*x
 のように表せます。

 実際は4入力や6入力など実際に存在するFPGAの素子にあった式を実装しており、この式をGPGPUなどで計算することで、一定の条件下のLUT-Networkを非常に効率よく学習出来ております。
 この演算の良いところは、LUTが取りうる値はXORも含めて単独で学習可能なことです。一般のパーセプトロンで同じことをするには隠れ層を持った2段以上のモデルにした上で、非線形な活性化層を加える必要があります。しかしこのモデルだと、単独でLUTが取りうるすべての値が学習対象となります。
 バイアス項が無いのは気にはなっているのですがLUTに置き換わる以上は回路上の表現は十分なはずなのと、MNIST程度に関しては実によく学習してくれています。
 バイナリでStochastic演算を行うには、例えば4bit精度が必要なら15倍のオーバーサンプリング、8bitで255倍、16bitで65535倍、とNbit精度に2^N-1倍のーバーサンプリングが必要になります。乗算器の規模は N^2 のオーダーなので、Nが比較的小さい場合に空間より時間に展開したほうが有利な領域が発生すると考えています。


 

(本題)パーセプトロンに代わる素子の可能性

 さて、いよいよ本題です。では先のモデルから2値化項を取り払って、Stochastic演算という言葉も一回忘れて、計算グラフのモデルだけを「普通の実数空間のディープラーニングに応用したらどうなるか?」という話です。
 要するに従来のパーセプトロンのモデルの箇所をこのモデルに入れ替えてどうなるか実験してみたというのが本記事の要旨です。
 幸い、LUT-Networkの学習用に作成中のBinaryBrainという学習環境は、ベンチマークの為に従来からある普通のディープラーニングも出来るように作っており、実数も扱えます。また疎行列学習を目的にメモリ配置なども工夫しており、今回のような実験には最適です。
 ここで、CIFAR-10のデータを使って、従来の普通のCNNと、この新モデルを使った学習とを行い比較してみました。

 

ダウンロード - densecnn_log.txt

ダウンロード - sparsecnn_log1.txt

ダウンロード - sparsecnn_log2.txt

 学習の概要部分を下記に示します。

[従来のDenseCNN]

Densecnn

[SparseCNNその1]
Sparsecnn1

[SparseCNNその2]
Sparsecnn2

 SparseCNNはリソースを増やして2回試しています。

 accuracy は testデータを使ったものと、trainデータを使ったものの2種計算していますが、testデータを使ったもの(test accuracy)を参照してください。train accuracy が高いのは単なる過学習であり、本来はDropoutなどを組み合わせて抑制が必要な項目です。
 現在、疎結合な層間でランダム結線を採用しているのがSparseモデルで過学習を抑制できている原因と考えています。

 また、実行は、すべてGoogle ColaboratoryのTesla T4 で実行したもので、計算時間も参考にしていただければと思います。DenseCNNはcuBLASを利用しており、Sparseの方は私がCUDAコードを記載しています。
(上記の画像はGoogle Colaboratoryの画面スナップショットなのですが、最後だけ色がおかしくなってしまいすみません)。
 

モデルの構造

 下記に今回発案のモデルの計算モデルを記載します。
 実際に実験に用いたのは6入力のモデルです。

Formula

 なお、式は巨大ですが、実際は共通化できる演算が多く、プログラムに書き下ろして最適化すると大した計算量にはなりません。

 このモデルの特徴としては

  • 単独でXORのような問題が解ける
  • 活性化層無しで非線形性を保有する

 という点があります。
 実際にはこの素子にBatchNormalizationを組み合わせています。BatchNormalizationは一般にバッチデータの平均(mean)と偏差(var)に対して、y=(x-mean)/sqrt(var) * gamma + beta という演算を行います。今回は gamma=0.2, beta=0.5 に固定して、平均と分散補正のみ行っています。学習時は平均と分散を求めるのでBackward含めていろいろな計算を行うのですが、推論時には単なる線形なスケーリング(y=ax+b)を行うだけの形に帰着するので、学習 の効率化に関しては非常に強力ですが、本質は違うところにあると考えています。
 ただ、BatchNormalizationが無いと学習が進まないケースも多いのと、先に述べた既存モデルにバイアス項がないのがここで補われている可能性はあります(それならそれで大した計算ではないのでモデルに加えれば済む話ではありますが)。

計算量の話

 今回の素子モデルは素子モデル内の計算量は固定でO(1)です。しかし入力数が固定なので実際にはカスケードに繋いで接続数を増やす必要があります。しかしながら、入力数をNとした場合の計算オーダーは O(n) となり、従来と変わりません。これは中央値の中央値などのアルゴリズムがO(n)に収まるのと同じですね。

 層が増える分、接続方式にバリエーションが取れる分、今回のモデルには面白みがあるかもしれません。

Graph

 なお、実際には上記のような構造化した接続はまだ考慮できておらず、今回のモデルを各層で適当な数ばらまいて、層の間をランダムに結線するという乱暴なことをやっています。が、このやり方がある程度過学習の抑制にもなっていそうな傾向が見れています。
 またこのようなやり方の場合、層の段数は概ね入力数をNとするとlog N の深さになるかと思います。N対Nの場合、全結合だとO(N^2)になるのに対して、O(N・log N) のオーダーに近づくのであればそれはそれで面白いと思う次第です。

実行環境

 なお、今回のコードは ver3_release3 というタグ名で github にpushしております。
 お試しいただきたい場合は、Google Colaboratory上の Notebook にて下記のコマンドを実行いただければ今のところ実行可能です。

!git clone --recursive -b ver3_release3 https://github.com/ryuz/BinaryBrain.git
%cd BinaryBrain/sample/cifar10
!make all
!make dl_data
!./sample-cifar10 SparseCnn

 私の環境では、今のところ運よく Tesla T4 が割り当てられていますが、K80などが割り当てられる場合もあるようですし、今後もnvccなどの環境が同一に提供されるかどうかは分かりませんが、ある程度メモリのあるGPU上でCUDAが動く環境があれば試すことは出来るかと思います。

(追記 平成31年4月30日)逆伝播の式について

  逆伝播は難しくなく機械的に解けるのですが、一応、ざっと手元で今解きなおしたものを従来のパーセプトロンの比較もあわせて下記に追記しておきます。(平成最後の日ですね)。

Backward

 個人的には、パーセプトロンと違い、入力への逆伝播の項に他の入力が現れるところに興味を持ってます。
 入力に入力が戻るということは、逆伝播が自己再帰ループを組んでいることになるかと思います。
 データを変えずに、forward->backward->updateの反復で値変化が進行する可能性があります。バイナリ素子の乗算器回路とかでやったら、素因数分解とか、暗号解析系とに使えないかなというのは前に書いたとおりですね。

おわりに

  このような研究をされている方は多分沢山おられるのだとは思いますが、ある程度実用的にGPGPU上でプラットフォーム作成とあわせて行えたというのは珍しいのではないでしょうか?
 Wikipediaを見るとXOR問題が示されたのが1969年のようなので、ぴったり半世紀後の今、XOR問題を持たない素子で、ある程度実用的な実験ができたのはなかなか面白いところに来れたように思います。

 既存パーセプトロンのモデルの一つの課題は、全結線時の結線の多さであり、専用計算機を設計する際の工夫のしどころでもあるし、組み込みAIのような分野ではメモリ帯域を逼迫させる要因ではないかと思います。一つのアプローチとしてこういったやり方があっても面白いのではないかと思う次第です。

 

 特に専用ASICなどを考える場合、疎結合であればメモリを解さずに直接結線できるかもしれませんし、ストレージの難しいアナログ系の素子を使ったLSIなどへの応用とかだとな面白そうな気がしています。
 トランジスタやOPアンプ並べて色々実験したら意外に楽しいかもしれません。アナログ素因数分解器作れないかな (^^;

2019年4月 9日 (火)

LUT-Networkの最新資料更新

FPGAXでお話のお時間を頂いて資料を作ってからある程度開発が進展してきました。

Twitter上中心に、多くの方からご支援いただけたことをまことに感謝しております。
特に marsee101さんには、FPGAの部屋にてBinaryBrainの記事を記載いただきました。この場をお借りして厚く御礼申し上げます。

さて、肝心の BinaryBrain Version3 ですがGPGPU(CUDA)に対応したことで計算速度が増加したのもありますが、前回書いた確率的LUTが極めて効率的に動き始めたというのが、もっとも大きな成果かと思います。

このようなオリジナルの素子の実験がやりやすくなったこともVer3の改善点です。

更新版の資料を

https://www.slideshare.net/ryuz88/lutnetwork-revision2

にアップロードいたしました。

また、今回はTOEIC300点台の私が、無謀にもgoogle翻訳頼みで英語版を作成するという暴挙にも出ております。(^^;

今回の更新は、アルゴリズムレベルというか、ニューロン(パーセプトロン)のモデル自体を生物の脳の模倣から電脳(電子計算機)の模倣にシフトしたという点で、ディープラーニングのあり方にもっと根本的な議論を投げかけることが出来たのでは無いかと思っています。

これが凄いことなのかどうかは私もAI分野の知識が浅いので測りかねていますので、有識者のご意見をお待ちする次第です。

いずれにせよ、LUTを使ったリアルタイム機械学習認識が高速高精度に行える道がだいぶ開けてきたのはそのとおりなので、新しい計算機の応用の道が増えれば嬉しくは思います。

LUT-NetworkはもちろんFPGA用なのですが、例えば6入力LUTの64bitテーブルは64bitCPU(ARMとか)のint型にパッキングできますし、4入力(16bit)なら16bitCPUでも1命令でテーブル引きできます。
ARMのNEONなどでさらに並列化も出来るかもしれず、組み込みAI分野にもっと手広く応用できる可能性も感じております。

これらは、TensorFlowなどの既存のDLのプラットフォームを使って、その範囲で試行錯誤していたのではたどり着けなかった成果かとは思っているので、最終形が仮にTensorFlowで実現可能であったとしても、BinaryBrain作ってよかったなと思っている次第です。

まだまだ、実用的なネットのインプリが出来ていないという点で先は長いですし、価値のあるものになるかは未知数なのですが、ちょっとした思い付きからここまで来れたのは本当に多くの方に支えられてのことだなと思います。

引き続き頑張りたいと思います。

 

<追記>
海外の方で同じようなアイデアを思いついた方々が出始めたようです。
LUTNet: Rethinking Inference in FPGA Soft Logic
教えてくださった方々有難うございます。

マイナーな分野なんで、いろいろ心配だったのですが、同じようなことを考える人が居るのは嬉しいことです。
FPGA特化なので人口少ないですが、AIの主戦場分野は本当に切磋琢磨の世界なのだろうなと思います。

<さらに追記>

 FaceBookで「Activation無しで線形性が出せるのか?」という話が少し盛り上がったので転記しておきます。
 もともと隣の入力に逆伝播している時点で線形性のある式ではなくなっているとは思っていますが、私の考えとして、特に2入力LUTの場合、「AND/OR/NOR/XOR/etc.. ないろんなゲートが確率的に重なり合って存在している状態」を想像してもらうと良いんじゃないかなと考えました。

ゲートごとにも距離があるので、答えをテーブルで数値に置き換えて、最適にするために選択するゲートの確率比率が連続的に変動していくと。で、それを6bitテーブルなどに拡張していってもちゃんと機能しているということです。

もともとボルツマンマシンのモデル自体確率モデルですし、そこからやり直したほうがいいのかもしれません。

今はまだ、「テーブルを確率変数に置き換えて立式したもので学習に成功した」という結果論でとまっているのでまだまだこれからですね。
今のAIは「説明できないけどうまく行っている」は沢山あるので、順番が逆になる(理論が後から着いてくる)のはそれほどおかしなことでもないのかもしれません。

 

2019年3月31日 (日)

祝! LUT-NetworkのLUTモデルの逆伝播学習動作

 前回メモで書いた内容がどうやら動作したようなので、速報レベルで記事を書いておきます。

 

Lut_copy_1

上記はMNISTデータをFP32にて、入力値がそれぞれバイナリの1になる確率として計算した後に、実際にバイナリモデルにテーブルコピーを行って動作確認したものです。

まずは原理検証ということで、FP32精度で軽く accuracy 0.8379 まで学習した後、バイナリのLUTモデルにコピーして x15(4bit精度) のPWM変調で accuracy 0.8084 が確認できました。コピー先のモデルは普通にFPGAのLUTをバイナリで模擬しているモデルなので、ちゃんと機能していることが確認できます。

アナログ値を例えばPWM変調などを掛けてバイナリにすると、もとの値に応じて確率的に1と0になります。このときの1になる確率から各テーブルの引かれる確率にして、最終的な結果が1になる確率を求める素子を構成し、この組み合わせを誤差逆伝播で学習させることで直接LUTテーブルを求めています。

ソースコードは

https://github.com/ryuz/BinaryBrain/blob/ver3_develop/include/bb/StochasticLut6.h

を参照下さい、Forward と Backward を見ていただければよいかと思います。

6入力LUTのモデルであり、重みとして W のパラメータが64個のテーブルに相当します。

Forwardでは、64個あるテーブルのそれぞれの参照される確率を求めた後に、テーブルの内容を2値化したものを元に、出力が1になる確率を求めるという他愛の無い計算で、Backwardではその微分を計算しています。

やっていることはシンプルなのですが、「テーブル引き」というモデルをそのまま数式にしてしまったところが新しいかと思いますし、テーブル引き時点で従来のいわゆるパーセプトロンとはかなり異なるモデルであることがご理解いただけるかと思います。

これがうまく行けば、極めて効率的にLUT-Networkの学習が可能になります。
また、学習過程では、「バイナリ値が1になる確率」という形で、FP32値を forward でも伝播させますので、回帰問題も効率的に学習できます(FPGAポーティング後の予測でオーバーサンプリング必要なのは従来どおりです)。

今回作った素子モデルは別にバイナリである必要もなくて

  • N入力に対して、2^N個の重み係数を持つ
  • forwardもbackwardも乗算と加算のみ
  • 単独でXORが解ける素子
  • 重みをfoward時に二値化するとFPGAに出来る

Nが小さい(疎行列)ことだけ重要で、ひょっとすると2値化せずに普通のDLの素子として使っても情報密度は従来のパーセプトロンのモデルより高いかも知れません。


従来のパーセプトロンを組み合わせて作ったμMLPよりも、早く高密度に学習しているような気がします。このあたりもおいおい追加検証が必要になりそうです。

現在CNNも実験中ですが、順調に学習が進行しています。

実行中の画面のスナップショットを以下に張っておきます。

 

Lut_cnn2

このアイデアはまだCUDA化していないので重たいですね。ただまだ2epoc目で92%まで来ています。その後、95%をあっさり超えました。保存したパラメータでLUTのモデルで実行したところちゃんと accuracy : 0.9441 の結果も得られました。

このモデルはメモリアクセスが少なくて、乗算が非常に多いので、CUDAにとても向いていると思います。

そもそも論として、FPGAに限らずに、ワーキングセットが小さく出来ればキャッシュのヒット率その他、計算機の効率が上げやすいという方向なので、ワーキングセットを小さくして代わりにfpsを上げて認識性能を出していくという試みは、いろいろと面白いのではないかと思います。

 

(2019/04/01追記)
 DNNの学習だけではなく、もっと手広く使えそうな気がしてきたのでTwitterに書いたことも転記しておきます。

 今回のモデルの計算グラフが下記です。6入力LUTを書くとテーブルが64個となり書くのが大変なので、2入力LUT(テーブルサイズ4)で書いてみました。
 実際にはこれの6入力版をパーセプトロン素子の代わりに使ってLUTネットワークの学習に成功しているわけです。もはやニューラルネットと呼んでよいのか微妙になってきましたが、複数のパーセプトロン素子でも等価なものが作れるのでぎりぎり許してください。

2lut_model

 各テーブル(W0~W3)が引かれる確率を求めているため、構造的にWを掛け算する前に、入力同士の乗算が発生しているのが特徴かと思います。また各テーブル値は回路化すると0か1しか取らないので、forward時は2値化して、backward時はそのまま使っています。FPGA化しないなら2値化自体不要と思います。
 通常のパーセプトロンのモデルは入力同士が最終段の加算部でのみ結合するので、単素子の逆伝搬ではお互い干渉しません。今回のモデルはクロスバー的には乗算で絡まっていて激しく干渉するのですがちゃんと動いていて自分が一番信じられないですが、CNNの例のとおり学習に関しては効率的に動作しているようです。
 このモデルは出力と他の入力値の両方の影響を受けるのでXORが学習できてしまいます。接続数が増えると破綻するので疎結合でしか利用できませんが、逆に言えば1段で全結合という手を取らなければ回避できます。生物の脳をモデルにしてたら思い付きようがないモデルと思っていまして、FPGAをモデルにしたからこその成果と思います。

 で、今回のモデルはもともとがデジタル論理素子の直接学習なので、あらゆるデジタル電気回路を記述可能です。するとこれを逆に使うことで、デジタル回路をネットに置き換えて逆伝播を行うことが可能と思われます。
 つまり、今回のモデルを使えばあらゆるone-way functionの逆解を求めるのに特別な知識なしにGPU回すだけの状態に変換可能な気がします。初期値依存なので実用的なパフォーマンスが出るか不明ですが、いわゆる暗号解読などの分野にDLの計算環境を活用できる可能性があります。
 例えば、デジタル乗算器の回路をLUT-Networkに写し取って、テーブル固定して、素因数分解したい値を教師にして、逆伝搬を繰り返せば素因数分解が出来ることになります。
 素因数分解などは、まだ、十分研究されている分野ですが、それ以外の複雑な系に関して、片方向の演算が定義できれば、簡単に逆計算がGPUを活用して探索できます。
 このモデルの入力同士で逆伝播に相互干渉するというのは、一種の自己参照ループと思っていまして、OPアンプのフィードバックとか想像してもらうと分かりやすいかと思いますが、解が存在すればそこに向かって系が収束していきます。フラットなグラフで最適化したいたら自己参照ループがうまく解けないケースが多いのですが、今回はDNNのテクニックで非常にうまく微分値が反映できています。
 かつて、チューリングらが、エニグマの暗号解読に自己参照ループを活用したチューリングボンベという非チューリングマシンを使って挑んだ話は有名ですが、こういったアプローチが、NP問題とか数学的にも解かれていない領域に対してあたらしい切り口が出来れば非常に面白いと思う次第です。

 幸いBinaryBrainはこういう実験もやれそうな構造に作っているので、どこかで挑戦してみたいと思います。