C++とJAVA

本当に、JAVAの速さに驚いた動揺は未だに冷めない。784X300X150X10のネットワークをサクサクと学習する。ウェイトが、784X300+300X150+150*10=281700個ある。この順方向の計算と誤差逆伝搬によるウェイトの修正を一つのデータに関して行うのにかかる時間は1.67ミリ秒なのである。つまり、60000個のデータを約100秒で修正し終えるので、1サイクル28万個のウェイトを使った一個のデータの計算と修正には、0.00167秒、つまり、1.67ミリ秒しかかからないのである。
本来、JAVAに乗り換えたのは、スレッドの並列計算を導入しやすくする目的だったが、今の所、必要ないので1スレッドで動かしている。いずれ、使用スレッドを指定できるようにするつもりだ。
それはそうと、書いておきたかったのは、JAVAにして改めて、C++の違いに「えっ!」と気づいたことがあったからだ。C++とJAVAは、とてもよく似ていて、書き換えはスムーズにできる。今回、やってみて、クラスを配列に入れた時の振る舞いが違う。
例えば、仮にNeuronというクラスを作成して、この複数のインスタンス(オブジェクトというべきか)を配列neuronsに入れるとしよう。
C++の場合は、vectorを使って、インスタンス neuronを
vector<Neuron> neurons;
neuron.push_back(neuron);
としてvector配列に入れる。
この配列から、
Neuron neuron = neurons.at(1);
という感じで取り出して、このneuronの何かのメンバー変数の値を変更したら、
neuron.at(1) = neuron;
として戻してやらなければ、配列の中に保持されているインスタンスのメンバー変数の値は変わらない。
しかし、JAVAはクラス配列のインスタンスを取り出して、その値を変更すると、元の配列の中のインスタンスそのものの値も変更されているのだ。
だから、埋め戻しが不要だ。
C++愛好家とJAVA愛好家との間では、参照渡しなのか値渡しなのかがよく議論になるが、それと同じようなことだ。もちろん、JAVAのような仕様の方が助かる。なんだから、時間ロスも少ないような気がする。

ディープラーニング(Autoencoder)のJavaプログラムをMNISTテストする

ニューラルネットのC++プログラムをJAVAに書き換えたら、すさまじく速くなったということは先に書いた。
これでディープラーニングの入り口である、Autoencoder(自動符号化器)のプログラムを作成した。バグもほぼ取れているような感じなので、
https://github.com/toyowa/jautoencoder
に公開している。
MNISTデータは、入力は、28X28=784ニューロンで、出力は、数字ラベルの10ニューロン。そこで、隠れ層を、300ニューロンと150ニューロン挟んで、4層にした。これまでと同様に、MNISTの6万個の手書き数字データと10000個のテストデータを実行した。
結果は、
<正解数 = 9413 不正解数 = 587 正解率 = 0.9413>
だ。同じプログラムで、改めて実施した、隠れ層400ニューロンだけの、Autoencoder抜きの結果は、
<正解数 = 9310 不正解数 = 690 正解率 = 0.931  >
なので、明らかに、正解率は上昇した。
しかも、隠れ層が300-150で、Autoencoderなしにニューラルネットを実施した場合は、全くダメ、というか収束しないので、Autoencoderの効果は確かめられた。
正解率のヒストグラムは、以下のようなものである。400の方が、0.999以上の頻度は多いのだが、総合的パフォーマンスは300-150のAutoencoderの方が高い。理論的に予測されているように、Autoencoderを入れた方が、柔軟に認識ている感じだ。