今回、文字認識ニューラルネットワークの勉強を深めたく、テーマとして” 7セグメントLED の認識”のニューラルネットワークを実装してみることにしました。論理回路ロジックよりも少し複雑ですが、判定は明確なテーマとなる予想です。Deep Learningとして適当かどうかとは別に、Pythonのプログラム学習も兼てテスト課題とています。Pythonの新しいメソッドも幾つか取り入れてみます。
1. 7-segment LED
1-1) 7セグメントの構成
まず 7-segment LEDを簡単に説明します。 7segment LEDは7つの長方のLEDをONにすることで0~9の数字を表現するデバイスです。ロジック回路で組むとチョット複雑になりますが、電卓などの数字液晶表示でよく使われます。数字以外にも幾つかのアルファベットも表示できます。(ここは数字表示にだけ対します。)
7segment LEDは長方の7つのLEDを組み合わせて、ON/OFFを7ビットの1/0に対応させます。7ビット全てが1(ON)ならば全てを点灯(ON)させて8を表示します。FIG.1の様になっています。(通常のLEDでは、8ビット目で右下に小数点の点LEDがありますがこれは略します)
[ Fig. X-1 ]
この表示のプログラムです。本来のLEDでは同じ色なのですが色を付けてみました。
[ Program X-1 ] 7-segment LED
【X-1をチョット解説】(プログラム初心者向けの説明になります)
① グラフライブラリのmatplotlib.pyplot をインポート、文字は'Times New Roman' にした。(文字は個人的嗜好)
② 7bitのsignalをリストで設定。figsize は横長(6,3)にしてグラフ範囲を横長(幅2[-1~1],高さ1[0~1])にした。
③ 7bitのSignalをx軸の負側(ha='right' に指定)にtext表示します。
④ LED表示はほぼ正方(幅[0~1],高さ[0~1])の範囲内で、数字が少し斜めに見える位置に、各LEDを配置します。 segment毎に色を変えて、OFF(0)の時は白色で描きます(本当のLED表示は単色です)。
⑤説明用にsegmentと対応したLEDに番号(0)~(6)を付けて描いています。
【解説 ここまで】
1-2) 0から9の7セグメントLED
0から9の10種の7segmentLEDを表示します。
[ Fig. X-2 ]
表示プログラムです。Program X-1を元に、Plot_Figを関数にします。MAINでは7segmentのビットデータを設定してプロット関数で7segmentLEDの状態をプロットします。
[ Program X-2 ] 7-segment 10_LED
【X-2をチョット解説】
①〔plot_7segments〕 plot_signalsを順にenumerate関数で呼び出して i, sig を設定します。 各LEDの表示位置(fig_x,fig_x)の原点と幅・高さを5行2列に設定します。
②〔plot_7segments〕 追加したグラフ軸に対してprogram X-1と同じ様にsegment描写を実行しています。ライン幅(lw)は適当なサイズを選びます。
③〔MAIN〕 教師データとしてLEDnumbersを0-9の配列に設定。入力としてLEDsignalsを7bitのON/OFF配列を10進数で設定します。10進数を内包表記によるlist(format())関数で配列に変換して、numpy配列[10,7]に変化しています。
④〔MAIN〕 プロットsizeを(6,5)にして、グラフ軸は描きません。 plot_7segments関数を呼び出して10個のLEDを表示します。
【解説 ここまで】
2. 2層Neuralnetworks による7-segment LED認識
2-1) Neural Networksのクラス
今回、Neural Networksをモジュール処理としてまとめます。 先ずは、前記事の IX:Neural Network の速度改善 の【プログラム IX-4】で使用したNeural Networks クラスとsigmoid 関数、softmax 関数とをモジュールにします。記事IXでは Neural Networksの各要素の更新をMain部分で行っていましたが、今回はNeural Networksクラスにreset_elementsメソッドとして追加します。 モジュール名の最後にNeural Networksのノード数を付けてみました。
[ module X-M1 ] Neural Networks_7_4_10
【X-M1をチョット解説】
① Neural Networksは計算処理だけをするのでインポートするモジュールはnumpyだけです。
② 2層のNeural Networksのクラス名はL2Networksとします。
③ オブジェクト生成時の初期設定(__init__)では、Neural Networksとその初期値設定し、それと同じサイズの配列を数値微分値用に準備します。δ値を定義をします。 入力が7bitで出力は0-9に対応した10個の計算値で、中間ノードはとりあえず4個としています(テストすると、この辺りがいいみたいです)。従って、Networksの要素配列はW1[7,4],B1[4],W2[4,10],B2[10]となります。
④ cross_entropy_errorメソッドで誤差計算をします。バッチ数は入力(in_x)の長さで判断します。
⑤ grad_multiメソッドでは、初期の誤差をerror_nowとして一度計算をして、各要素(W1,B1,W2,B2)を順に設定、numpy のnditerメソッドでelement_iの各要素を一つずつ特定して数値偏微分します。
⑥ reset_elementsメソッドを追加しました。 数値偏微分値を求めて各要素を1回更新します。学習効率ηは引数設定にします。更新をするだけなので返り値はありません。
【解説 ここまで】
2-2) 2層Neuralnetworks による認識学習
認識プログラムを作成します。 先ほど作ったNeural Networks_7_4_10モジュールをインポートしてnetとします。
MAINでは学習用データの作成と学習の条件設定(回数、バッチサイズ、η、ネットワーク)での繰り返し学習になります。繰り返し学習ではバッチデータの作成と、学習の実行(reset_elements)、結果のプロットをします。 Neural Networksの認識予測は、学習初期は10個が同じ予測になりますが、中間を過ぎて、1000回の学習をすると予測値は正確になります。 1000回の学習時間は2分程度でした。
[ Fig. X-3_a ] 初期 [ Fig. X-3_b ] 中間 [ Fig. X-3_c ] 最終
[ Program X-3 ] 7-segment_L2_learning
【X-3をチョット解説】
①〔plot_7segments〕 10個のプロット軸をオブジェクトとしてリスト(inset_ax)にするので、初期値に空のリスト(inset_ax =[ ])を準備します。
②〔plot_7segments〕 軸を追加生成(fig.add_axes)して配列inset_ax[i]のオブジェクトとして追加します。追加した軸にLED点灯の状態を描きます。
③〔plot_7segments〕 別の関数からプロット出来るように軸のオブジェクトリスト(inset_ax)を返り値にします。
③〔plot_prediction〕 予測結果のプロットとしてplot_prediction関数を作ります。引数に、プロットする軸(plot_7segmentsで生成した軸配列)、学習した回数(n)、現在のNeuralNetworks (predict_net)、入力配列(x_signals)、空状態のプロットオブジェクト(predictlist=[])を設定ます。
④〔plot_prediction〕 新しいプロットをする前に、1回前にプロットした学習回数と予測のプロットをpredictlistからオブジェクトが空になるまで消します。
⑤〔plot_prediction〕 今のNeural Networks での計算を行って、出力値(y)から.argmax()関数でone-hotの値に変換して予測結果とします。予測結果は各領域segment_axの右側[1.5,0]に描き、predictlist に追加していきます。
⑥〔MAIN〕 新しい処理を追加しています。学習数とバッチサイズ、ηの設定をしてNeuralNetworksのインスタンスとしてLED_netを生成ます。
⑦〔MAIN〕 ループで学習を数繰り返します。学習回数をstudy_iとすので、ループ設定は1から終了を学習数+1にしました。
⑧〔MAIN〕 バッチ数の入力データと教師データを作って、Neural Networksの更新メソッド(.reset_elements)にバッチとηを引き渡して、Neural Networksの各要素が1回更新されます。
⑨〔MAIN〕 更新したNeural Networksの予測を、プロットの関数(plot_prediction)で処理をして表示(pause)すれば学習の1回分が終わります。
⑩ 終了前にNeural Networksの学習結果の配列要素を印字して、終わります。
【解説 ここまで】
2-3) 7segmentLEDの認識誤差関数と正答数
学習の進捗状況を確認するためには、誤差関数をモニタリングするのがいいでしょう。ここでは交換エントロピー誤差で学習を行っています。一方、学習目的の文字認識では、認識正答数(または率)が学習の評価ともなりますので、この2つの指標を記録・グラフ化します。
まず、Neural Networksモジュールに誤差値と正答数を回答するerrorandmatchメソッドを追加します。学習用データと評価用のデータが違うデータセットの場合(MNIST も異なるデータセットが有る)があるので、入力データと教師データは都度の引数にします。
[ module X-M2 (errorandmatch] : Neural Networks_7_4_10 の追加メソッド部分だけ
【X-M2をチョット解説】
①クロスエントロピー誤差を入力(in_x)と正解の教師データ(teach_v)で求めます。
②argmax(y)の内包表記でOne-hotデータにして、教師データと一致する個数を求めます。 配列の一致判定を行って、論理結果を合計をすれば一致数が求められます。
【解説ここまで】
続けて、主Programでは、プロット領域を右に広げて新たにグラフを描くようにします。グラフは横軸を学習回数、縦軸は左側が誤差値として右側で正答数を示しことにしました。
[ Program X-4 ] 7segment_L2learning (変更1)
【X-4をチョット解説】
①〔plot_7segments〕 fig幅を2倍にしたので、追加axのx軸の位置と幅を1/2にします。
②〔MAIN〕 fig, ax設定でfigを横に2倍[12,5]にして、伸びた右半分に新しいグラフ軸をstudy_axとして追加設定。新しいグラフの右側を2番目の軸設定(.twinx())でstudy_ax2に設定します。
③〔MAIN〕 set_titleメソッドで、グラフのタイトルの所にパラメータを表示しました。縦軸と横軸のそれぞれのラベルを設定します。
④〔MAIN〕 study_ploted はプロット済みデータの範囲変数で、初期値は0にします。
⑤〔MAIN〕 配列変数study_dataを初期値0で設定して、学習数分の3変数[学習カウント、エラー誤差、正答数]を記録出来るようにします。 学習前(0回目)として一度、エラーと正解数を測定して入力します。 学習ループ中はstudy_dataに各変数を入力します。
⑥〔MAIN〕 学習成果のプロットはプロット済回数(study_ploted)から学習回数(study_i)までをプロットします。さらに、プロットするのを学習回数(study_i)が設定回数毎にまとめてプロットするようにして全体の処理時間を短縮します。
【解説ここまで】
結果の一部を紹介します。 600~800回ぐらいで、10個の正解(率100%)になります。
[ Fig. X-4_a ] (初期の200回)
[ Fig. X-4_b ] (1000回終了時)
3. Neuralnetworksの3層化とパラメータ設定
3-1) ニュートラルネットワークの隠れ2層化とパラメターの変数化
ニュートラルネットワークの隠れ層を2層にして、全3層のネットワークにします。1層目と2層目のノード数は引数で設定できるようにしました。 入力は7segmentなので7変数、出力は0~9の10変数のままです。 隠れ1層(全2層)の場合には2層目のノード数を省略するか、0に指定すれば対応できるようにします。
[ module X-M3] NuralNetworks_7_L1_L2_10
【X_M3をチョット解説】
①〔__init__〕 引数で1層目(L1)、2層目(L2)のノード数を引数にします。 隠れ1層(全2層)のNetworksにしたい場合は、L2のノードを'0'又は省略することにします。
②〔__init__〕 Neural Networksの要素数(self.elements)は、隠れ2層にそれぞれWとBの要素が有りますので、初期設定を6(W1,B1,W2,B2,W3,B3)とします。
③〔__init__〕 隠れ1層(全2層)のNetworksの場合は、L2_nodeが'0'又は省略されるので、self.elementsをW1,B1,W2,B2の4個にします。出力層として第2層目を使うので、初期設定としてのL2_nodeは10にしておきます。
④〔value_y〕 隠れ1層(全2層)のNetworksでL2_nodeが0又は省略された時には、self.elementsが4になっているので、出力値value_yメッソドでは2層の結果のself.a2をSoftMax関数処理して出力self.yにします。隠れ2層(全3層)場合は、self.a3をSoftMax関数処理して出力self.yにします。
【解説 ここまで】
主Programでは、インポートするモジュールをNuralNetworks_7_L1_L2_10 に変更して、ノード数L1、L2を引数にNeuralnetworks オブジェクトを生成します。 学習回数は10000回、バッチは6データ、η=0.5、L1ノード6個、L2ノード12個にしています。
他は、グラフのタイトルを修正する程度になりますが、今回は学習回数が多くなるので、処理経過のプロット処理を指定回数分まとめて行うようにしました。
[ Program X-5 ] 7segment_L3learning
【X-5をチョット解説】
①〔IMPORT〕 IMPORTするモジュールは隠れ1層2層に対応した'NuralNetworks_7_L1_L2_10'です。
②〔MAIN〕 初期値設定に隠れ層のノード数L1,L2を設定して、3層のNeuralnetworksのオブジェクト生成の引数にします。
②〔MAIN〕 学習ループ中のプロット処理を、学習回数が指定回数になった時に行わるようにします。(study_i%100==0で100回毎になる)。
【解説ここまで】
結果は、 学習300-400回ぐらいでやっと正解が2つなり、学習1000回でも5-6個しか正解になりません。 学習10,000回で正解ほぼ10個が正解(率100%)になります。 10,000回学習でも安定してはいない様子です。
[ Fig. X-5_a ] (1,000回終了時)
[ Fig. X-5_b ] (10,000回終了時)
この結果は、 隠れ1層(全2層)のNeuralnetworks の方が、この課題には適しいているという事のように思われます。 各種のパラメータを最適化すれば処理のスピードも変わって来るようです。
3-2) パラメータの比較プログラム
Neuralnetworks 学習には幾つかのパラメータが有ります。 今回の7segmentLEDの認識ネットワークで、入力は7bit、出力は0から9の10個でこの中からOne-Hotを選びます。全3層の場合は中間に隠れ1層目と隠れ2層目があり、ノード数の設定が出来ます。他にバッチ数、学習効率ηの設定も出来ます。 今度は、これらの設定パラメータを改善する試行用に、プログラムの改良をします。 ポイントは、
1) 最適化の判定は学習の速さ、'時間'とします。 同じ学習回数でも、隠れ層のノード数が多くなるとかなり時間が掛かるようになります。正答数が上がるまでの処理時間が分かる様にします。
2)図表の左側の予測表示を(処理時間 vs [誤差&正答数])の小グラフに変え、9個のグラフを並べて学習の収束速さを比較出来るようにします。
となります。 ここでは、Neuralnetworks モジュールの変更はしなくて済みます。
[ Program X-6 ] 7segment_L2learning(9map)
【X-6をチョット解説】
①〔IMPORT〕 timeをインポートして処理時間測定が出来るようにします。
② ProgramX-3で作成したプロット関数(plot_7segments)を削除して、パラメータ比較9プロットに変更します。比較プロットはMAINのループ内で設定しますのでprogramはMAINの部だけとなります。
③ 学習回数(study_num)を設定して、比較9ケースを実行するループにします。バッチ数、η(学習効率)、L1(隠れ1層目のノード数), L2(隠れ1層目のノード数) のどれか2つをstudy_case毎にリスト設定します。X-6は、隠れ1層のネットワークなのでL2=0、バッチ数とL1をリスト設定、学習効率η=0.5で固定です。右半分の進行状態のグラフ(study_ax、study_ax2、count_ax)はループ毎にクリア(.cla())します。
④ 学習ループ中は、左半分の予測プロットを削除だけが変更。
⑤ 学習ループが終了したら、誤差と正答数のグラフを右半分に9個プロットするようにします。グラフ軸名称等はスペースの確保のため省略した。
【解説ここまで】
結果は、
[ Fig. X-6_a ] 隠れ1層Neuralnetworks (パラメータ変更が2回目の学習400回目まで)
[ Fig. X-6_b ] 隠れ1層のNeuralnetworks (パラメータ変更が9回終了)
様々なパラメータを試した結果として、バッチ数=8、η(学習効率)=0.7、L1=4 なら良い結果を得ていることが判ります。
* 隠れ2層のNeuralnetworks の場合、
L2ノード設定を0以外にすれば同じ処理で済みます。 ただし、他のパラメータも適当なものを選ばないとなりません。
以下のようになりました。
[ Fig. X-6_C ] 隠れ2層のNeuralnetworks (パラメータ変更が9回終了)
バッチ数=12、 η(学習効率) =0.4、 L1= 6、 L2=12 ぐらいが適当です。 ただし、結果を比較するのに8000回程度の学習が必要で、 時間も4~5分(1条件)掛かりました。
4. まとめ
7segmentLEDの認識をNeuralnetworks 学習で実装してみました。 隠れ1層と2層のNeuralnetworks での実装をして、この認識では隠れ1層の方が適していることが判りました。 Neuralnetworks の実装における設定パラメータとして バッチ数、η(学習効率)、隠れ1層目のノード数, 隠れ2層目のノード数を設定出来るようにし、処理時間による比較試行によって最適化を試みました。
この試行により、判った特徴として、(1)隠れ層の数は1層の方が早く安定することが有り、必ずしも隠れ層が多い方がよいとは限らないこと。 (2)隠れ層のノード数は、少ないと処理は早くなるが不安定になる傾向が有る。ノード数を多くすれば処理は遅くなるが安定はしてくる。(3)学習効率は、小さいと処理が遅く、大きいと処理が早く進むが、大きいと不安定になりやすい。一度収束したように思えても再度発散する場合も多かった。
プログラム実行の様子をビデオにしました。
5. 終わりに
ということで、試行錯誤によってまあまあの結果が得られるニューラルネットワーク学習が出来ました。ただ、ほど良い条件抽出に多くの時間を割いても来ています。 もっと試行を繰り返せば、より最適なパラメータも有るのではないかと思われますが、先に進みたいのでこの課題は終了します。 次回こそは、出来上がったNeuralnetworks をMNISTのデータセットに適用してみたいと思います。
6.参考文献 / 参考リンク
[※] このブログは教本として、『ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装』 [ 斎藤 康毅 ] 【出版社: オライリージャパン 発売日: 2016/9/24 】 (ISBN-10:4873117585 ISBN-13:978-4873117584) を使っています。
[※] pythonの教本として何かまとまった1冊を持っているといいです。 ネットで調べれば細かい所や、実例を沢山見ることができますが、導入として「何を」調べたいのかを知るにはまとまった教本が必要です。最近は『独学プログラマー Python言語の基本から仕事のやり方まで [ コーリー・アルソフ ] 』を参考にしています。
*🐭*🐍*🐭*🐍*🐭*🐍*🐭*🐍*🐭*
0コメント