前回、ニューラルネットワークのプログラムを実装して独自課題(7segmentLED)によってその理解を深めました。
今回は出来上がったニューラルネットワークによるMNIST(エムニスト)の文字認識を行います。
1. MNIST データの取得
MNIST データについて簡単に紹介します。(MNIST の説明は多くの説明サイトがあります。ここではほんの概要だけです。)
MNIST は画像認識用のサンプルデータで、数字の画像データを多量に収集したものです。 テキストにしている書籍「ゼロから作るDeepLearning」に紹介されているGit-Hab[*1]から、プログラムと一緒にダウンロードすることができます。 その他にも多くの公開されていてサイト[*2]からも入手できます。
MNIST データセットは4つあり、.ファイル形式はgzです。
'train-labels-idx1-ubyte.gz' , 'train-images-idx3-ubyte.gz' , ' t10k-labels-idx1-ubyte.gz' , 't10k-images-idx3-ubyte.gz' のファイルが有り、手書き数字のラベルとイメージの2種類が2個づつ、全部で4個あります。
train- で始まるファイルは学習用データ。6万個の手書き数字データがあります。
train-labels- は数字のラベル(正解のデータということ)で、train-images-が手書きのイメージデータとなります。ファイル冒頭部分にはデータ情報が記録されています。
> labels- ファイルは
(0-3) ファイル識別番号、 (4-7) データ数、 (8-) ラベルデータ
> images- ファイルは
(0-3) ファイル識別番号、 (4-7) イメージのxデータ数、 (8-11) イメージのyデータ数、(12-) イメージデータ(x*yで1イメージ)
となっています。 mages-ファイル入っているのは2次元のイメージデータで、データ数はラベルデータの数と同じです。
t10k- で始まるファイルは学習効果の評価用データで、train- とは別に1万個の手書き数字データがあります。 データ形式は同じです。 t10k- のデータは実力テスト問題と解答の様なもので、学習には使用しません。
まず、MNIST データを読み込むプログラムを作ります。 MNIST データを提供しているサイトからgzファイルをダウンロードして作るPythonプログラムと同じフォルダに入れます。 同じフォルダーならフォルダー階層の指定が省略できるからです。 別の階層に入れた場合は、ファイル指定にディレクトリー指定が必要となります。
パッケージインストールでgzipパッケージをインストールしてgz形式データを読み込みこめるようにします。 読み込みにはwith文でファイルを開いて取り込みます。 ラベルデータとイメージデータの読み込みに別々の関数、def read_labels (f_name) と def read_images(f_name) を作りました。 前のプログラムの<def plot_7segments( )>を改良して、学習用の6万個のデータからランダムに取り出した100個を10×10のイメージでプロットするようにしています。
【プログラムXI-1】
説明
・ def read_labels (f_name)のファイル読み込みにはwith文を使用します。with文でgzipファイルをOPENすればwith文の終わりにはファイルCLOSEしてくれるので、CLOSE忘れが防げるので標準的な使い方となります。
・ gzip でファイルをバッファに読み込んでバッファからNumpyのfrombufferメソッドでデータ数とラベルを読み込みます。
・ frombuffer のパラメータは(buffer, dtype, count, offset)になっています。countは読み込むデータの数ですが、count=‐1はすべてになります。
・ int.from_bytesでバイナリーデータを整数に変換します。データの並び方指定は’big’です。def read_images()ではfrombufferメソッドでイメージのサイズ(x,y)を読み込んで、イメージデータを読み込みます。イメージデータを1次元配列で格納しているので、2次元に復元をします。
・ plot_MNIST(plot_ax, plot_index, plot_image )は、MAINからプロットの軸(plot_ax)とイメージ(plot_image) の正解となる数を受け取ります。
・ 2次元イメージはmatplotlib.pyplot のimshowメソッドを使います。imshow メソッドでのプロットでは軸の長さ設定はしません。
・ 正解となる数はイメージの左上の小文字にします。
・MAINでは、MNIST データの読み込み関数の実行と、イメージのプロット位置の座標(image_ax)設定、データのランダム選定、プロット関数(plot_MNIST)の実行だけになります。後で学習をする時にはバッチ処理データにするので変数名にbatchを使いました。
【実行結果】
2. MINST用の2層ニュートラルネットワーク
MNIST をデータとするニューラルネットワークです。 前の記事でニューラルネットワークに関連するものをパッケージにしてます。ここでもMNIST 用にニュートラルネットワークパッケージに修正します。 MNISTのイメージは28×24ピクセルから成るので784個の入力ノードがあります。出力は0~9の10ノードです。今回のニュートラルネットワークパッケージでは入力と出力のノード数が指定できるようにします。前回の< NuralNetworks_7_L1_L2_10.py > を元に修正しました。
【ニューラルネットワークパケージプログラム P-1】
説明 (前回の< NuralNetworks_7_L1_L2_10.py >からの変更点です。変更ない所は説明を省略しますので前回の記事を参照してください。)
・ 画像データのピクセル数を入力ノード数x_nodeとして、隠れ1層目のノード数L1_node、隠れ2層目のノード数L2_node、出力ノード数y_nodeを引数にします。引数なしでもL1_node,L2_node,y_nodeにはデフォルトを設定しました。
・ 隠れ層の数をself.hide_Layersとして設定。L2_nodeが0(又は省略)ならself.hide_Layersは1で、L2_nodeは出力y_nodeと同じにします。
・ set_arraysメソッドを追加しました。予め配列定義などが必要な場合に設定するメソッドです。ここでは、バッチ数をオブジェクト変数にセットして、学習の度にバッチ数の算定処理を繰り返さないようにします。
・ cross_entropy_errorメッソドでは、交差エントロピー誤差を求めるだけの処理として、ネットワーク出力結果と教師データを引数にしました。これによって、ニューラルネットワークの出力は別に計算実行して引数として渡す必要があります。
後の処理で、この方が良いと思われたので、変更しました。
・ また、cross_entropy_errorメッソドでは、バッチ処理での平均値を結果として戻り値にしています。一方、バッチ平均を算出する前の値も、クラス内で使用するクラス変数(self.cross_error)にしています。
・ grad_multi メソッドで偏微分値を求めるます。cross_entropy_errorメッソドの引数に出力値が必要なので、ネットワーク出力(self.y)を求めるvalue_yメソッドを実行して、結果をcross_entropy_errorメッソドに引き渡します。
・ reset_elements メソッドは修正なしです。
・ errorandmatch メソッドはほぼそのままですが、正答数をバッチ数で割って成長率を戻す様にしました。
※ softmax 関数はバッチ処理に対応するように変更しています。(前回までのsoftmax 関数はバッチに対応出来ていないようでした。softmax 関数が入力値の大小関係を変えない関数なので結果的には処理出来ていたと考えられます。)
3. ニュートラルネットワークによるMNISTデータの認識実装
ニューラルネットワークの準備が出来たので、MNIST のトレーニングデータによる認識学習を行います。NISTM データの読み込みと表示は上のものを使用して、学習の部分は7セグメンLED認識での実装(ProgramX-5)を参照しながら改良します。
【プログラムXI-2】
説明 (上記のProgramXI-1と前回のProgramX-5をそのまま流用したことろは省略します)
・ 先に作ったニューラルネットワークのパッケージ(’NuralNetworks_x_L1_L2_y.py’ としています)をインポートしてnet としておきます。
・ plot_MNIST 関数では、バッチデータが毎回更新されるので、プロット結果を書き直のclaと軸のoffセットをします。
・ plot_prediction 関数を追加します。7セグメンLED認識(Program X-3)のとほぼ同じで、テキスト位置を調整しています。
・ MAINではsubplot 設定で2分割の右半分にグラフを描くことにしました。
・ MNIST_netの名前でニューラルネットワークのオブジェクトを生成します。入力ノード数はMNIST のイメージサイズ(tr_image_xsize * tr_image_ysize) です。バッチ数をset_arrays メッソドで設定します。
・ グラフの正答数は正答率(%)に変えます。
・ 学習前の状態確認として、一度バッチデータを作って予測結果をプロットして於きました。(初回の学習結果表示まで少し間がありますが、実行していることが判ります。)
・ 学習の進捗を記録するstudy_data はチェック用データ't10k-'ファイルに設定します。
・ 学習進捗のプロットはProgram X-3 とほぼ同じですが,実行順番を少し変更しています。
・ 1回のニューラルネットワークの学習時間が長いので、ここではプロットの間引きはしていません。
【実行結果】
<隠れ1層>: 学習回数100回、L1=5、L2=0、η=0.06、batch_size = 100 で実行しました。
1回目の学習終了時です。
この後、交差エントロピー誤差は着実に小さくなりますが、正答率はなかなか上がりません。100回の学習では40%ぐらいまでです。100回学習に約30分かかってしまいます。
100回目の学習終了時です。
<隠れ2層>: 学習回数100回、L1=8、L2=5、η=0.06、batch_size = 100 で実行しました。一回の学習にかかる時間がさらに長くなりました。 40分~50分かかって、100回の学習終了時です。
ニュートラルネットワークのパラメータ設定には、複数の実施結果を並べてみる事になりますので、前回のプログラムを改良となります。 しかし、1回の学習にかかる時間が長く、待ちくたびれるプログラムになってしまします。 なので、紹介は省略します。
30分程度の処理時間で、一番良かったもので、隠れ2層で、L1 =18,η=0.08 バッチ100 。 これだと30分で 25回の学習しかし出来ませんが、53%ぐらいまでの正答率に到達できました。
4. 最後に
ついに(なんとか)、MNIST の文字認識ニューラルネットワークは出来ました。基本的にはこれで終わりですが、これでは遅くて実用化できそうにも有りません。 次回は高速化を予定しています。 Deel Learningの説明の中では難しい内容とされているような誤差逆伝搬法を実装してみます。 計算速度が上がれば、パラメータの調整もスムーズに出来るようになるでしょう。
5. 参考文献 / 参考リンク
[*1]書籍『ゼロから作る Deep Learning』(オライリー・ジャパン発行)のサポートサイト https://github.com/oreilly-japan/deep-learning-from-scratch ここからダウンロードする時には、dataset フォルダにMNISTデータが入っています。
[*2] LeCun. “The MNIST Handwritten Digit Database”. Yann LeCun's Website yann.lecun.com. http://yann.lecun.com/exdb/mnist/
*🐭*🐍*🐭*🐍*🐭*🐍*🐭*🐍*🐭*
0コメント