メインモジュールの実装|Excel VBAでMNIST機械学習

本ページはゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装を参考にニューラルネットワークを作成していきます。参考書籍では「Affineレイヤ」「ReLUレイヤ」「Softmax-with-Lossレイヤ」という3つのレイヤを作成し、それらを組み合わせることでニューラルネットワークを構築していきます。本サイトでも同じように3つのレイヤをVBAで作成し実装していきます。

これまでに「Affineレイヤ」「ReLUレイヤ」「Softmax-with-Lossレイヤ」、それらを組み合わせたニューラルネットワーク本体である「TwoLayerNetクラス」をVBAで実装しました。

本ページではニューラルネットワークを呼び出し、学習を行うための機能をメインモジュールを実装していきます。また、別ページではこのメインモジュール内に推論を行う機能も追加します。つまり、マクロとして実行するのは今回実装するメインモジュールの関数のみとなっています。

 

メインモジュールの機能

メインモジュールでは「学習」「推論」の2つの機能を持ちます。
(本ページでは「学習」の機能のみを実装します)
 

ニューラルネットワークの学習

「学習」とは、順伝播と逆伝播を繰り返し、重みパラメータとバイアスパラメータの値を徐々に更新していき『適正な値』を見つけ出すことを意味しています。

例えば「2」と描かれた画像を入力したら「2」という値を出力、「3」と描かれた画像を入力したら「3」という値を出力するように、「0〜9」どの数字で”誰が描いた数字”だとしても正しく出力できるようなパラメータ値を見つけます。

適切なパラメータの値はこれまでに実装したクラス/レイヤの関数を順番に実行していくことで導き出すことができます。導き出す方法はいくつかありますが、今回実装している方法は「誤差逆伝播法」という1番シンプルで簡単な仕組みです。
とても極端な話をすれば「TwoLayerNetクラス」の関数「gradient」と関数「ParamsUpdate」をループ処理に入れるだけで学習が可能になっています。(引数を準備する必要がありますが)
 

ニューラルネットワークの推論

「推論」とは、入力された画像に何の数字が描かれているかをニューラルネットが自ら考え導き出すことです。

処理としては非常にシンプルで「学習」で求めた適切なパラメータの値をそのまま使い、順伝播を1回行うだけです。この順伝播によって出力された値がニューラルネットワークの最終的な答えとなります。

1度学習済みのパラメータさえ求めることができれば同じニューラルネットワークに入れることで何度でも使い回すことができます。つまり今回の場合、1度学習を終えパラメータ値が取得できればMNISTと同じ構成の画像データに描かれている「0~9」のいずれかの数字を当てるという処理は何度でも行ことができます。

ただ注意しないといけないのは、正解できる確率(精度)は学習の内容によるという点です。
正しく十分な学習ができていれば正解をうまく導き出すことができますし、逆に学習が不十分の場合はうまく導き出すことができません。

そのため学習中に一定間隔で精度を求める機能を追加していきます。
これにより現在の学習状態での学習の精度を随時確認することができるので、精度を確認しつつ自分の好きなタイミングで学習を終了させることができます。

 

メインモジュールの実装

まずは標準モジュールで「Main」というモジュールを作成します。
これまでに実装したクラス/レイヤと揃えるため、本モジュールでも「Option Base 1」を使い配列を「1」スタートにします。

以下はメインモジュール(学習のみ)の全コードです。

 
以下では上記のコードでどのような処理を行っているかを解説していきます。
※メインモジュールではこれまでに実装したクラス/レイヤ/関数を全て呼び出します。
 適宜、各実装ページに戻り本モージュールとの繋がりやレイヤごとの繋がりを確認しましょう。

 

Sub Train

関数「Train」ではこれまでに実装したレイヤや関数を呼び出してニューラルネットワークの学習を行う関数です。

ここでは「この関数がどのような処理を行なっているのか」を部分ごとに分けて上から順に説明していきます。
 

Windows APIの宣言

まずはメインモジュールの設定を行います。
「Functions」モジュールで実装した関数「KeyPress」を実行するためにWindows APIの「GetAsyncKeyState関数」を宣言します。(詳しくは「GetAsyncKeyState関数」ページを参照ください)

 

変数の宣言

次に変数の宣言を行います。
プロシージャ外で宣言する必要のない変数もありますが、ハイパーパラメータ類は1つに固めておきたかったのでここでまとめて宣言しています。中には「Public」を使ってグローバル変数として宣言しているものもあるので注意してください。

 

ハイパーパラメータ設定

ここからは「Sub Train()」プロシージャ内のコードです。
はじめに、学習の初期設定ともいえるハイパーパラメータを設定します。

ハイパーパラメータとは各層のニューロン数や学習率などのニューラルネットワークの根源となるパラメータのことをいいます。このハイパーパラメータの値は学習前に変更することが可能で、学習結果に応じて変更したり自分の好きな設定で学習を行うことができます。

今回作成したニューラルネットワークではハイパーパラメータの値を変更できるものは少ないですが、コードを書き足したり、レイヤを増やすことで隠れ層の数を増やしたり、活性化関数を変更したりすることも可能になります。(ニューラルネットワークについて理解が深まったらぜひコードを書き換えてみてください)

基本的に今回変更できるのは以下のハイパーパラメータだけです。

hidden_size:隠れ層のニューロン数
batch_size:バッチサイズ(まとめて何個のデータを学習するかを決める)
LearningRate:学習率(この値が小さいほど学習時間は長くなるが精度の高い学習ができる)
train_size:学習用データのデータ数
test_size:テスト用データのデータ数
MinLoss:最少Loss値(Loss値がこの値になったら学習を終了する)
MaxEpoc:最大エポック数(batch_size×MaxEpoc個のデータを学習したら学習を終了する)

batch_size、MaxEpocについては本来の意味とは少し違うので注意してください。
(詳しい説明は後ほど出てきます)
 

学習途中データの確認

次に学習途中データの確認を行います。
今回実装するニューラルネットワークは最終的に「Parameters」という名前のシートを作成しそこに結果を書き出します。ここでは既に「Parameters」というシートが存在するかを確認し、以降でどの処理を行うかをユーザーに選択させます。

ユーザーの選択により処理は以下のように分岐します。

Parametersが存在しない場合 → 学習開始
Parametersが存在する場合    → 続きから学習をする(Parametersに学習結果を上書き)
                 → 新規で学習をする(現在のParametersを別名に変更する)
                 → キャンセル

Parametersが存在する場合はメッセージボックスでさらに分岐しますが、学習済みのシート(つまりはParameters)がない場合はすぐに学習が始まります。

ここでは上記の分岐を行うために「シートの設定」と変数「ParamsCheck」の設定を行います。(ParamsCheckはグローバル変数なのでこの値を使ってTwoLayerNetクラスなどの処理を分岐させます)
 

学習

次にTwoLayerNetのインスタンスを作成し、ループ分を使って学習を行なっていきます。

複雑なように見えますが処理の流れを大雑把にみると以下の通りです。

① ランダムで学習データを取得(このとき学習データの値を0〜1の範囲で表現するため255で割る)
② ①の画像データの正解ラベルを取得しone-hot表現に変換する
③ ①と②を引数としてTwoLayerNetの関数「gradient」を実行
④ ③で取得した勾配を使い、各Affineレイヤのパラメータを更新
⑤ ①〜④の処理を「batch_size」回行う(「batch_size」回の学習結果の平均の精度を出力する)
⑥ ⑤の処理を学習終了条件を満たすまでループする

この処理の流れはミニバッチ学習から来ています。
本来のミニバッチ学習では③の処理の時点で複数の画像データをまとめてTwoLayerNetに渡し、まとめて計算を行います。(通常これを1エポックという)

しかしVBAでミニバッチ学習を行う場合、入力する画像データが増えることは配列の次元が増えることを意味します。ここでは機能の理解をメインとしているためあまり複雑な計算は避けています。そのため本来のミニバッチ学習とは少し変えた処理で、ミニバッチ学習に似せた処理をしています。(つまりミニバッチ学習自体は実装できていません)

上記の処理には書いていませんが、ループの中ではテストデータの精度確認も併せて行います。
処理の内容自体は上記の内容とほとんど同じなので割愛しますが、最終的にはこのテストデータの精度で学習の良し悪しを判断します。(詳しくは実際に学習をする際に解説します)

ここの処理は説明を聞くよりも1行1行コードを見ながら何をしているかを確認していった方が理解しやすいと思います。おそらくニューラルネットワークを学ぶ上で1番つまづきやすいのが「学習」の部分なのでここはじっくりとやっていきましょう。
 

学習終了処理

最後に学習終了条件を満たした際の処理を書いておきます。
ここではTwoLayerNetの関数「ExportParameters」を呼び出すだけです。
引数となる「ParamsSheet」は先の「学習途中データの確認」のコードで条件に合わせて中身のシートを変更しているため、引数自体を変える必要はありません。

 

まとめ

ここではゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装を参考に、ニューラルネットワークで学習を行うためのメインモジュールを作成しました。

今回実装したメインモジュールの機能をまとめると以下の通りです。

Sub Train:学習を行う

長いコードにはなっていますが機能としては「学習を行う」の1つだけです。
これまでに実装してきたレイヤやクラスの関数を呼び出して学習していくので、初めのうちは少しややこしく感じるかもしれません。ただ、コードが理解できればやっている内容が思ったよりはシンプルだということがわかります。
このシンプルな内容を理解することで、ニューラルネットワークの仕組みも理解できるのでじっくり理解していってください。

今回のメインモジュールで「学習」に関するコードはすべてです。
次回はこれらのコードを使いMNIST学習を行う方法を解説していきます。
※これまでのコードをコピペして実行してもエラーが発生します。
 詳しくは次回のMNISTデータセットを学習させてみようを参照下さい。

 

メインページ
 

 icon-book 参考書籍

 

2020年9月9日AI, Deep Learning, VBA

Posted by Lic