今回も引き続き「ゼロから作るDeepLearning」をベースに、数値微分による学習アルゴリズムをRubyで実装してみました。www.oreilly.co.jp最初に書いておくと、今回の数値微分での実装は、実装はシンプルなもののその分処理に時間がかかり、手元の環境では繰り返し処理を終わらせることができませんでした。次回以降で書籍の次の章で解説されている、誤差逆伝播法での実装に変更してみたいと思います。2層ニューラルネットワークのクラス書籍のPython実装に基づき、2層のニューラルネットワークをTwoLayerNetという一つのクラスとして実装します。endend初期化まずはinitializeメソッドで重みとバイアスのパラメータを初期化し、インスタンス変数にHashとして保持します。合わせて外部からパラメータを参照するためのメソッドも用意しておきます。@paramsendinitializeメソッドの引数には、入力層のニューロン数、隠れ層のニューロン数、出力層のニューロン数を渡します。行列を用意し、rand_normメソッドで標準正規分布に従う乱数を設定します。メソッドで全てゼロの配列を用意します。画像データを引数にとり、推論処理を行います。softmax(a2)end処理内容としては前回の記事で実装したものと同じで、隠れ層の活性化関数にシグモイド関数、出力層の活性化関数にソフトマックス関数を使用しています。入力データ(画像データ)と教師データ(正解ラベル)を引数にとり、損失関数の値を求めます。t)end画像データからpredictメソッドで推論を行なった結果と正解ラベルから交差エントロピー誤差を求めています。入力データ(画像データ)と教師データ(正解ラベル)を引数にとり、認識精度を計算します。メソッドを用いて取得します。推論はバッチ処理で行うため、複数の画像データに対する結果として二次元配列として返ってくるので、二次元目を基準に最大値を求めるため、max_indexの引数には1を渡しています(0次元目、1次元目と考えるため)。10の配列だったら0〜99の値)として戻ってくるため、10で割ったあまりを求めることで、0〜9のラベルを表すようにしています。one-hot表現で渡されている複数データについての正解ラベルは二次元配列なので、10で割ることで0〜9のラベルを表すようにしています。10そして最後に推論結果のラベル配列と正解ラベル配列をeqメソッドで比較し、正解数(配列の値が1になっている数)を入力データの数で割ることで、正解率を求めています。この辺りのeqメソッドの使い方等は前回記事と同様です。入力データ(画像データ)と教師データ(正解ラベル)を引数にとり、各パラメータに対する勾配を計算します。gradsend損失関数を計算結果を取得するlambdaを用意し、勾配計算用のnumerical_gradientメソッドに各パラメータ共に渡し、結果をHashに格納して返します。gradendnumerical_gradientメソッドでは中心差分での数値微分によって各パラメータの勾配を計算します。結果の格納用に入力パラメータと同じ形のゼロ配列を用意します。計算して結果格納用の配列に格納し、最後にその配列を返しています。全要素数を取得して、timesでその要素数分ループを回して、順次配列の内容を参照するようにしています。ミニバッチ学習と評価の実装TwoLayerNetクラスを使ってMNISTデータセットを用いた学習と評価を行います。学習の実装は、訓練データから無作為に一部のデータを取り出して入力データとする、ミニバッチ学習で行います。0..1endMNISTデータの読み込みとパラメータ初期化まずは以前実装したMNISTデータのロード処理を使ってMNISTデータを読み込んだ後、TwoLayerNetの初期化と、繰り返し数等の設定を行います。10、隠れ層は50としています。確率勾配降下法によるパラメータ更新の繰り返し数は10,000回で、ミニバッチのサイズは100としています。繰り返し処理の中では、まず60,000件の画像データの中からミニバッチデータを取得しています。バッチサイズ分の要素数の配列を用意し、randメソッドで0以上60,000未満のランダム値を生成しています。これによって、60,000件の画像データのどのインデックス値のデータを取得するかを決めています。二次元配列になっているため、ランダムに生成したインデックス値の配列が一次元目を表すことを意味するように、二次元目はtrueを指定し、該当する二次元目のデータは全て取得するようにしています。勾配の計算とパラメータの更新ミニバッチデータをTwoLayerNetの勾配計算メソッドに渡して勾配データを取得し、学習率をかけたものを各パラメータから引くことで、各パラメータを更新します。grad[key]end損失関数の計算と記録ミニバッチデータから損失関数を計算し、繰り返しごとの結果を格納し、経過を記録します。loss認識精度の計算トレーニングデータとテストデータに対する認識精度を計算します。#{test_acc}"繰り返しごとに毎回認識精度を計算すると時間がかかってしまうため、1エポック(学習において全ての訓練データを使い切った時の回数。600回)ごとに認識精度を計算してその値を結果配列に格納し、コンソールにも表示します。最後に認識精度の推移をグラフに描画します。使わせていただきました。github.comこちらも参考にさせていただきました。手軽に使えて良さそうです。冒頭でも書きましたが、今回実装はしたものの、繰り返しの一回の処理でもかなり時間がかかり、10,000回の繰り返しは現実的な時間では終わりそうもありませんでした。書籍でも言われていますが、数値微分による実装は実装はシンプルなものの処理にはかなり時間がかかるので、次は誤差逆伝播法での実装で試してみたいと思います。今回のコードは下記リポジトリで公開してあります。