ある日、会社で「そのアルゴリズムで悩むならニューラル・ネットワークに推定させたほうが良くない?」と言ったら「いや、上手くいくかどうかわからないじゃないですか」「ニューラル・ネットワークの処理でコマ落ちしそう」と言われたので「やって見もせず何を言う」と実装してみたのだが、コマ落ちどころかミリ秒以下で処理できた。
ただ、Pythonのままだと実装上不都合なのでC言語に移植する必要があって、そういうときにどうするかというとChainerで訓練したニューラル・ネットワークからC++言語のプログラムを自動生成するプログラムを書くのである。
まあ細かい実装についてはあっちの連載(http://ch.nicovideo.jp/akiba-cyberspacecowboys/blomaga/ar1088216)で毎週紹介していくつもりではあるが(記事は全て書き終わってる)、結論だけ紹介すると、2キロバイト未満のニューラル・ネットワークならば全てキャッシュに乗るので1μsくらいで処理が終わるのである。
このグラフの単位は秒なので、0.001は1ミリ秒だが、実際にはこのベンチマークは1000回ループなのでそのさらに1/1000、つまり1マイクロ秒で処理が終わることになる。実にChainerの約500倍。
といっても、Chainerをそのまんま使ったとしても0.00047秒だから1ミリ秒以下で推定できることは間違いない。
L1キャッシュに乗るというのは絶大で、試しにMNISTを識別するオートエンコーダをC++で書くとこうなった。
なんと、Chainer版とC++版だけ(した2つ)を比較すると、30%程度しか速度が向上していない。
これはつまらないので仕方なく大人げないAVX命令を使うとやっとこ10倍程度の速度向上が出来た。
今回のニューラル・ネットワークの場合、MNISTの第一層は784次元x400次元なのでそれ単体で3.5MB(倍精度に拡張するので実質的には7MB)もある。
最新のCorei7でもL1キャッシュは32KB程度なので、こんな巨大なものがL1キャッシュに乗るわけがない。
まあニューラル・ネットワークをどうマシンに最適化して実装するかというのがそろそろ議論の俎上に上がってくる頃だから、ターゲットをどこに絞るかFPGA含めていろんな可能性を議論しなくてはならない。既にFPGAとかASICのIPとかも出始めてきてるしね。
まあしかし実装は大事だね。
ChainerとかTensorFlowとかは確かに便利なんだけど、実装を意識しないと中身でなにやってるかわかんないから「なんだかわからないから怖い」という黒魔術感に拍車が掛かってしまうけど、実装見てみりゃなんのこたない、かけ算と足し算をひたすらく返してるだけだからね。むしろ最終的には泥臭い実装が効くんだよね。
今頃は水面下で全国の最適化おじさんたちがアップを始めてるのかな
とはいえ、まだ実用的なニューラル・ネットワークの適用例が少ないから様子見だろうねえ。
まあ積和演算となると最適化おじさんたちの昔とった杵柄なので親和性高い気もするよねえ