これまでの記事ではDeep_Learningの要素となるプログラム紹介をして来ました。
1)ニューラルネットワークの概要と考え方
2)ニューラルネットワークに必要な処理プログラム
①第1層、第2層に使用するネットワーク
②第1層出力を非線形に取り扱うシグモイド関数
③第2層の出力(エラー値)から判定を行うソフトマックス関数
④ネットワークの誤差(エラー値)を最小にする偏微分
ここまでは各関数を個別に、その特徴を見てきましたが。
今回は、これらを統合してニューラルネットワークを1つ完成させることにします。
テキスト(「ゼロから作るDeep Learning」以下同じです。)の完成したニューラルネットワークをGithubからダウンロードすれば、実行可能なプログラムは手に入ります。
しかし、ダウンロードしたのでは、プログラミング学習をスキップしてしまい、苦労しない分、隠れたテクニックを見落としてしまいます。
自前の課題を設定し、ゼロからプログラムを構築することにしました。 判らないことや知らないことが沢山。。ググったり、参考書を読んだりしてなんとか進めます。 以下では、悩んだところも少し説明を加えてます。
さて今回、設定したのは論理ゲートのニューラルネットワーク化です。 テキストの最初の方(2.2)で AND,OR,NAND,XORゲートの紹介がありました。 XORゲートでは線形処理では判断が出来ないということでした。 そこで、4っの論議ゲートのニューラルネットワーク学習をする事にします。 AND,OR,NAND、XORはいずれも2入力の1出力のゲートです。 プログラム上はほぼ同じに作れます。
なお、テキストではミニバッチ学習(4.2.3)の紹介が早めにありましたが、適当な例題が思いつかなかったので、ここまではスキップしました。 今回のディープラーニングにミニバッチも組み込んでいます。 課題の論議ゲートの入力が4種類しかないので、いい例ではないですが、それらしくはなったと思われます。
さらに、今回はプログラミング初学者向けとしてプログラムの全部の行に簡単なコメントを入れることにしてみました。 内容は、設定理由だったり、コマンドの解説だったり、まちまちですが参考にして下さい。
1. 論理ゲートの3D表示
論理ゲートの復習と、ニューラルネットワーク推定の結果表示を3Dアニメーションにします。
1) ANDゲートとXORゲートの論理表
ANDゲートとXORゲートの論理表は次のようになります。
[Program VIII-1] ANDゲートの論理表
2) XORゲートの 2D表示
次にゲートの2Ⅾ表示をします。 XORゲートです。 出力が0の時は赤い丸、1の時は青い三角になります。
プログラムは次のようになります。 追加・変更部分には#2のコメントを付けてます。
[Program VIII-2] XORゲートの2D表示
[Program VIII-2 :付記]
2-1) 2Dグラフ表示では縦横の比率を同じにしたいのでサブプロットエリアを軸x、軸yで同じに設定しています。
2-2) x1、x2で指定した位置にマークをするだけなのでscatterメソッドをつかってマークの大きさ調整するようにしています。
2-3) マークと色の選択はscatterメソッドの引数をその場でリストから選択するようにしました。マークが軸の上になるようにzorder=2 に設定します。
3) XORゲートの 3D表示
2D表示では変化が良く解らないので、3D表示にすることにします。
[Program VIII-3] XORゲートの3D表示
[Program VIII-3 :付記]
3-1) 3Dグラフは後でアニメーション化するのでplot_gateとして関数に設定しています。
3-2) 3Dプロットは図と軸の設定(fig ,ax)に subplot_kw=dict(projection='3d' を追加しています。matplotlib のバージョンによって設定の仕方が変わっているようです。
3-3) 3Dプロットの引数には、入力の4点とゲートの出力を設定しています。 ゲートを変えればプロット結果も変えられるようになります。
プロットを関数にしたので、メインのプログラムはシンプルになりました。
2. ニューラルネットワークの設定 と学習
ニューラルネットワークの実装に入ります。 論理ゲートは2入力1出力となりますが、隠れ層を1層・2ノードとします。
ネットワーク関数の出力は2出力で、one-hotの場合は最大値を持つ方のインデックス(0又は1)を最終出力とします。
1) クラスNetWorks の定義
まずニューラルネットワークとして、NetWorks というクラスを定義します。 すでに紹介済みの式
の実装です。
(1)は入力層から第1層目への信号伝達
(2)はシグモイド関数
(3)は第1層目から第2層目への信号伝達
(4)はソフトマックス関数
となります。2ノードの隠れ層を1層持つニューラルネットワークとなります。
[Program VIII-4] NetWorks クラスの定義
クラスNetWorks のオブジェクトを生成すると重み行列WとバイアスBの初期設定をします。 ニューラルネットワークによる推計計算はロジックの入力値を引数にしたメソッド(value_y)を実行した時に行います。 このプログラムでは、まだ論理ゲートの入出力をプロットするだけなので、推計計算はプロット関数だけで呼び出します。 シグモイド関数(sigmoid_func)とソフトマックス関数(SoftMax)はクラス定義の外の関数として作ります。
[Program VIII-4 :付記]
4-1)w1,b1,w2,b2初期値はテキスト(4.5.1)に従って、w1,w2をガウス分布で0.01近くの値にしますnp.random.randn(2,2)で2行2列のガウス分布を作れます。
4-2)b1,b2はnp.zeros(2)で2データ配列の0にしています。
4-3)メソッドvalue_y()で出力Yを計算します。引数は入力xの2数値x1,x2となります。 yとして返す値はソフトマック関数の2数値になります。
4-4)plot_gate関数でネットワーク出力y(2値)の最大値判定を行って(配列yのy[1]が0.5以上かどうかでも判定可能)、ゲート出力をone-hot関数の出力vにします。
4-5)●と△は適当なサイズに選んで、入力(x1,x2)、出力y[1],zをその周辺に印刷します。
[Program VIII-4 :結果]
結果の3Dグラフはこうなりました。 w1,w2の初期値に乱数を使っているため結果は毎回少し異なります。
1回目 2回目
2) ニューラルネットワークによる学習
ロジックゲートのニューラルネットワークによる学習の実装です。 交差エントロピー誤差のメソッド、偏微分のメソッド、繰り返し学習の設定をします。
交差エントロピー誤差を計算するメソッドは次の式の実装です。
偏微分は次の式を実行するメソッドになります。 ニューラルネットワークの要素w1,b1,w2,b2の各値にδを加えた時のエラー値E(+δ)からδを引いた時のエラー値E(-δ)の差分を2倍のδで割った数値微分値です。
[Program VIII-5] ロジックゲートのニューラルネットワーク学習
対象はANDgateです。 実際、XORgate は少し計算が遅いので最初に組むプログラム用には不向きと判断しました。
w1,w2の初期値は [[0.1, -0.1][-0.1, 0.1]] の固定値とします。 これでも収束出来ることは確認しています。 初期値を固定にすることで、同じ結果になりまうので、プログラム作成の初心者に付きもののプログラムミスの有無を、結果から判断することが出来るようにします。(もしかすると、ここに記載のプログラムが間違っているかもしれません。) 偏微分値はw1,b1,w2,b2と同じサイズの配列 grad_w1 ,grad_b1, grad_w2, grad_b2 に格納します。 今回の論理ゲートは教師データのパターンが4種類しかないので、順番に4つの入力パターンを当てはめてニューラルネットワークの要素w1,b1,w2,b2の学習をさせます。
このHPのHTMLに文字制限?が有るようなので、上の前半(クラスとその関連メソッド)と下の後半(プロットの関数とメイン)に分かれています。 復元は、2つを繋げて下さい。
[Program VIII-5 :付記]
5-1) メソッドcross_entropy_errorでメソッドvalue_yを実施するのでin_xを引数にする。
5-2)メソッドcross_entropy_errorは要素数2の配列で、教師データが2値のone-hotなので、そのままインデックスとして使います。
5-3) メソッドgradではw1,b1,w2,b2の各要素について、δ値が+(plus)と-(minus)の時のcross_entropy_errorを求めている。
5-4)メソッドgradで各要素での偏微分値を求めた後は、別の要素の偏微分の為に、計算済みの要素の値を元に戻す必要が有る。(デルタ値を1つ加える)
5-5)plot_gate関数では、アニメーション化の為、絵をメソッドax.cla()で毎回消して、x、y、z軸の設定か行います。
[Program VIII-5 :結果] 400回学習後の結果です。
プログラム終了時にw1,b1,w2,b2をプリントしているので確認。
w1 = [[-2.858537 -2.08391708]
[-3.04581455 -1.61847332]] , b1 = [3.76457784 1.74390035]
w2 = [[ 4.06614962 -4.06614962]
[ 2.33301203 -2.33301203]] , b2 = [-2.09185252 2.09185252]
当然ですが、繰り返しても同じ結果です。
以上で、ニューラルネットワークのプログラムとしては一応の完了となります。 教師データをOR, NAND, XORに変えればそれぞれの結果を得ることが出来ます。
3. ミニバッチ処理
テキストでは相互エントロピー誤差の説明すぐ後にミニバッチ学習(4.2.3)の解説があります。 しかし、実プログラムがないとその挙動が良く解りませんでした。 今回のロジックゲートの処理で組込みます。 ミニバッチは、適当な少数(ミニ)のデータ(教師データ)を束(バッチ)として学習を行います。 テキストでは100枚分の画像データをバッチにしていますが、ロジックゲートの入力は4種類しかありませんので、4つの教師データをバッチにします(少ない方がプログラムチェックは簡単です。導入としてはちょうどいいかもしれません) 。 ミニバッチで得られた複数の結果を平均化して1回の学習を行います。 プログラムの処理では、どの計算段階で、どの変数を平均化するのかということが気になります。 原則的には偏微分メソッドgradまでは平均化せずに計算して、バッチ数分の偏微分値を求めてから平均化するのが正しいように思われます。 しかし、線形処理をしている間はどこで平均化しても、結果は同じはずなので、出来るだけ早い段階で平均化した方が処理が早いと考えられます。 偏微分のメソッドから遡っていくと、非線形計算の交差エントロピー誤差にたどり着きます。 先ほど示した式です。
この計算は、tがone-hot関数(最大値は1でそれ以外は0)なので、単純な線形処理とは言えません(線形処理にも出来るのだけれど、内積処理で複雑) 。
そこで、交差エントロピー誤差の処理の時に同時に平均化もすることにします。 プログラム上では交差エントロピー誤差は次の式を実装します。 この計算をすれば、その後はバッチとしての計算はしなくてもよいことになります。
この式では、交差エントロピー誤差を正の値する(”-”を付ける)処理を、one-hot関数 をインデックスとするより前に行うことを示しています。 また、Σ(合計)処理が、バッチ処理のない場合(上側の式)とバッチ処理がある場合(下側の式)で異なるので分けるようにしています。
[Program VIII-6] ニューラルネットワークのバッチ学習
プログラム修正は クラスNetWorksのメソッドdef cross_entropy_errorの修正と、関数 def batch_gradの追加、メインプログラムの修正ですので、その部分だけを紹介します。
[Program VIII-6_1] クラスNetWorksのメソッドdef cross_entropy_error の修正
[Program VIII-6_2] 関数def batch_grad の追加
[Program VIII-6_3] メインプログラムの修正
[Program VIII-6 :付記]
6-1) メソッドcross_entropy_errorでは、処理する配列1次の数と、配列の全数と同じならバッチ処理ではないと判定。 違えば、バッチ処理で配列1次の数をバッチ数と見なします。
6-2) batch_grad関数がバッチ処理をします。 np.random.choice関数でバッチ数分の教師データの配列を作り、ゲート入力と教師データのバッチ配列 batch_x と batch_t を作ります。
6-3) batch_grad関数でbatch_x ,batch_t をnetwork.gradの引数にして引き渡せば、cross_entropy_errorの処理で平均化された偏微分値が戻ってきます。
6-4) MAINではinputxとANDgate をNumpy配列に変更しています。これまでのリストデータではバッチ処理に使う index配列が利用出来なくなるため。
[Program VIII-6:結果]
結果はほとんど同じですが、まだ、Fig4と比べて余裕が有る様です。 バッチ入力4つの結果を平均化しているので、1学習での変化量が少なく、収束も遅くなったようです。 右側は OR gate に変えた場合です。
4. 4っのゲートを比較する
今度は、少しプログラムの改良を行って、4っのロジックゲートのネットワークが学習する様子を比較できるようにしてみます。
1) 4ロジックゲートの並列化
同時に4つのNetworksオブジェクトを生成します。生成したオブジェクトはgatesリストの要素として順に AND, OR, NAND, XORとします。 ゲート名をnamesリストにして、teachersリストとしてゲート出力を順にリスト化しておきます。AND, OR, NAND, XORの順に呼び出して学習をします。 gatesリストに各ゲートをインスタンス化し、それぞれで学習をします。 同時に4っのlogicの学習をして、学習を1回(バッチ計算4個)すると、その結果を4つのエリアに分けてプロットします。 なお、実行してみるとXORの学習が大変遅いので、2000回学習にします。 偏微分の計算は 2000回学習×4ロジック×バッチ4で 合計32,000回となります。
[Program VIII-7] ロジックゲートの並列学習
後半の、plot関数をdef plot_4gates に変更して、 メインを修正します。 なお、クラスNetWorksはProgram VII-6のままですので、 w1,w2の初期値は固定値のままです。
修正した、def plot_4gates ~ MAIN の部分です。
[Program VIII-7 :付記]
7-1) プロット関数をplot_4gate関数に置き換え、 変数gで4つのロジックを切り替えて、プロットする。サブプロットの位置は[g//2,g%2]で指定しました。
7-2) plot_4gateの関数では、一つのプロットエリアが小さくなるので、赤●,青▲,(x1,x2),y,vの表示も少し小さくします。
7-3) MAINで、4っのロジックを順に名前、ネットワーク、教師データ(正解出力)のリストを作ります。
7-4) MAINの学習ループは、2000回の学習ループ、4っのロジックをループ 、バッチ数4の引数設定となります。
[Program VIII-7:結果]
少し時間が掛かりましたが、最後にはXORは正しい結果にたどり着きました。
2) 並列学習における初期設定
ここで、ロジック用のニューラルネットワークのw1,w2の初期値をテキストに合わせて乱数にしてみます。 class NetWorks の def __init部分のw1,w2設定の2行を
self.w1 = np.random.randn(2,2)*0.01 #8 第1層の重み2行2列の初期値を初期値をガウス分布とした。
self.w2 = np.random.randn(2,2)*0.01 #8 第2層の重み2行2列の初期値をガウス分布とした。
に変更するだけです。 ( [program VIII-8] ですが、記載は省略します。
[Program VIII-8:結果] (Fig.NoはそのままFig.7です)
この場合には、XORは収束しませんでした。初期値設定が重要になるということでしょう。
5. 隠れ層のノード数を変更可能に
最後に、隠れ層のノード数を変更できる様にします。 ここまでは、ロジックゲートの2入力1出力に対して、隠れ層のノード数を2にしていました。 このため、1層目と2層目の重みW1とW2は2×2の配列でした。隠れ層のノード数をクラスNetWorkの引数に設定出来るようにします。
[Program VIII-9] 隠れ層ノード数を可変にしたニューラルネットワーク
ロジックをXORとして、隠れ層のノード数を2、3、4,5で比較してみます。 グラフ表示はこれまでと同じものが使えます。 学習回数は2000回では不足するので4000回にしました。 W1,W2の初期設定値はガウシアン分布設定です。
[Program VIII-9_1] クラスNetworksの初期値設定 def __init__ の修正
[Program VIII-9_1] MAIN の修正
[Program VIII-9 :付記]
9-1) class NetWorks の初期設定のdef __init__で、引数で隠れ層のノード数a1_dimを設定。デフォルトは2にしておきます。
9-2) 隠れ層のノード数によって w1を2×a1_dimのマトリックス、b1をa1_dim要素の配列、w2をa1_dim×2マトリックスに設定します。
9-3) MAINでは、gateリストのNetWorksを隠れ層を2,3,4,5に設定して、教師テータは全てXORにしました。
[Program VIII-9:結果]
6. まとめ
ニューラルネットワークの実装が出来ました。 独自の問題を設定したことで、簡単な問題ではあるけれどもニューラルネットワークの特徴が解りました。 初期値にどう設定するかによって結果が異なります。 また、学習率の設定にもよります。、ネットワークの隠れ層の設定も影響します。 そして、乱数を使ったデータ選択の場合は、時によって異なる結果にはなりますが、どれも正解にはたどり着きました(4つのロジックの場合ですけれど)。
今回の記事はビデオにしました。 アニメーションはビデオで確認して下さい。
7. あとがき・感想
前の記事から、大変長い時間がかかってしまいました。 実は、他にも計算問題を考えて試したのですがなかなかいいものがなく、ここまでかなりの回り道をして来ました。 (例えば、画像判定にサイコロの目を判断するのも考えました。競馬の順位予測も考えたけれどすぐに難しいと思いあきらめました。。。など) 一方で色々と試行錯誤があり pythonの学習もかなりしました。 Numpy の機能もテキストのプログラムを参考にして、多くの機能についてはググりながら調べました。 従って、いろんな方々のネット記事に助けていただいたと思います。 この場で感謝します。
そして、今回の学習では ニューラルネットワークにはネットワークの組み方や初期設定によって結果が異なる場合が有ることや、学習に使う教師データのとり方によっても変わることわかりました。 すなわち、ニューラルネットワークにも、『生まれ』(初期の設計や設定)の「よい」と「悪い」がものあり、さらに『育ち』(学習データ)が「よい」と「悪い」ものがあると思った次第です。 最近のニュースでAIの予測を聞くと、これは『生まれ』と『育ち』が「よい」AIなのかどうかと思ってしまいます。
次は、テキスト的では逆伝搬関数による高速化なのですが、次回はもう少しpythonの機能を学習して、プログラミングのテクニックをUPさせるテーマにしたいと思っています。(変わるかもしれない。)
最後までお読みいただきありがとうございます。
8.参考文献 / 参考リンク
[※]madplotlibの3dプロットのチュートリアルサイトです。
https://matplotlib.org/stable/tutorials/toolkits/mplot3d.html#sphx-glr-tutorials-toolkits-mplot3d-py
[※] Texによる数式表示は、記事6『Matplotlib で図表と数式(TeX)』で紹介しています。 https://python-learnig.amebaownd.com/posts/7732623
[※] Pythonの日本語チュートリアルサイトはhttps://docs.python.org/ja/3/index.html
[※] このブログは教本として、『ゼロから作るDeep Learning』 [ 斎藤 康毅 ] を使っています。
[※] その他の参考書として、pythonの教本としてなんとかまとまった1冊を、ということで、 『独学プログラマー Python言語の基本から仕事のやり方まで [ コーリー・アルソフ ] 』 を参考にしています。 線形代数の行列については専門書ではないけれど、基本的で分かり易い本として 『高校数学でわかる線形代数 』(ブルーバックス) [ 竹内 淳 ] などを紹介します。
*🐭*🐍*🐭*🐍*🐭*🐍*🐭*🐍*🐭*
[※] プログラムの勉強は、独学、インターネット教師よりもプロの育成プログラムが格段に速くて確実です。 費用に余裕があって時間に余裕のない方にお勧めです。
0コメント