しいたげられたしいたけ

コロナ対策なおざりの河村たかし現名古屋市長は、隣街住人として落としてほしい。

排他的論理和EORを機械学習で実現しようとしたらバタフライ効果が発生した?(その7)

わけのわからないつか未調査のことは他にもたくさんあって、一例だが使用するパソコンによっても計算結果が違うのだ。同じパソコンを使っている限り再現性は揺るぎもしないのだが、現在保有しているDELL17インチノーパソとLenovo15インチノーパソで結果に差が出たりする。いずれも 64bit Windows10 Home なのだが、内部表現が違うのか Anaconda のバージョンが違うのか…これは調査すればわかることだと思いたい。

重みの初期値もいろいろ変えて試している。マッピングするべきだろうが、変数が多いし範囲と刻みをどうするか等の方針がまだ決まっていない。

ガウス分布乱数でいくつか試して、とりわけヘンな結果が出た数値例を、今回は貼っていく。

「その4」の「#コード4-0」の1層重み W1 と2層重み W2 を、次のように変更した。numpy のガウス分布乱数メソッド random.randn() で生成した直後にダンプした値を、そのままコードに置き換えたのだ。

W1 = np.array([
[-0.00420782, -0.04331233, 0.12136818],
[-0.00953556, 0.01987206, 0.02593997]])
W2 = np.array([
[ 0.08212653, -0.04860534],
[-0.17150817, -0.30326033],
[-0.003902 , -0.0539731 ]])

この初期値を使用すると、正答率が100%にならない。損失関数の値もゼロ近くに収束しない。つまり排他的論理和EORを実現するのに失敗したということだ。ときどきそういうことがある。

「その4」「#コード4-2」で作成した正解率accと損失関数の値lossのグラフは、こんな感じになる。

f:id:watto:20210323010447p:plain

 

以下、いつものように「その4」に載せたコードで作成したグラフを貼っていく。

左:「#コード4-3」によるW1の3D折れ線グラフ。

右:「#コード4-4」によるW1の3面2D展開図風グラフ。

f:id:watto:20210323010521p:plain
f:id:watto:20210323010510p:plain

 

左:「#コード4-5」によるb1の3D折れ線グラフ。

右:「#コード4-6」によるb1の3面2D展開図風グラフ。 

f:id:watto:20210323010459p:plain
f:id:watto:20210323010452p:plain

 

左:「#コード4-7」によるW2の3D折れ線グラフ。

右:「#コード4-8」によるW2の3面2D展開図風グラフ。 

f:id:watto:20210323010441p:plain
f:id:watto:20210323010516p:plain

 

左:「#コード4-9」によるバイアスb2の2D折れ線グラフ。

右:「#コード4-2」による正解率と損失関数の値のグラフを再掲(グラフの大きさを合わせるため)。

f:id:watto:20210323010505p:plain
f:id:watto:20210323010447p:plain

 

前回記事「その6」でよくわかっていないのに「アトラクター」という用語を使いたくなったのは、上掲グラフとりわけW1の3D折れ線グラフが周回軌道に似た動きを示していたことも一因である。ただし正解率100%にならないグラフが必ずしもこのような周回軌道もどきの動きをするわけではなく、むしろ単調な動きを示すことも多い。実例は後日示すかも。

 

グラフ描画後すなわち学習を100回繰り返した後でダンプしたW1、b1、W2、b2の値は、次の通りだった。

>>> W1
array([[1.71176211, 1.40439772, 5.15937839],
[1.71143204, 1.40486592, 5.15880418]])
>>> b1
array([5.35940066, 5.61089252, 1.85192825])
>>> W2
array([[-17.59943793, 17.63295912],
[-18.21478118, 17.74001268],
[-20.42315653, 20.36528143]])
>>> b2
array([ 55.77585749, -55.77585749])

ナマの数字だけでは意味がとりづらいので、前回「その6」と同様の検算を試みる。ただし Microsoft Mathematics はビジュアルがよくても操作が面倒なので、Python 対話モードに数式を入力して計算させることにした。

 

ニューラルネットワーク1層目の演算結果。ニューラルネットワークに関しては、いつものタネ本 斎藤康毅『ゼロから作るDeep Learning』(O'REILLY) 3章、4章参照。

>>> np.dot(x_e,W1) + b1
array([[ 5.35940066, 5.61089252, 1.85192825],
[ 7.07116277, 7.01529024, 7.01130664],
[ 7.0708327 , 7.01575844, 7.01073243],
[ 8.78259481, 8.42015616, 12.17011081]])

これでもわかりにくいので、シグモイド関数を噛ませて小数点以下1桁まで丸めてみた。シグモイド関数は『ゼロから作るDeep Learning』P45参照。

>>> Z1 = sigmoid(np.dot(x_e,W1) + b1)
>>> Z1
array([[0.9953183 , 0.99635553, 0.86435334],
[0.99915148, 0.99910276, 0.99909918],
[0.9991512 , 0.99910318, 0.99909867],
[0.99984664, 0.99977967, 0.99999482]])
>>> np.round(Z1,decimals=1)
array([[1. , 1. , 0.9],
[1. , 1. , 1. ],
[1. , 1. , 1. ],
[1. , 1. , 1. ]])

この1層出力を、どう捏ね回してもEORは実現できそうにない。

 

グラフを描画したプログラムでは、Z1をニューラルネットワーク2層目に入力してさらにソフトマックス関数を噛ませている。ソフトマックス関数は『ゼロから作るDeep Learning』P66参照。

これらの演算結果も示す。

>>> np.dot(Z1,W2) + b2
array([[ 2.45759337, -2.94729187],
[-0.41184405, -0.08682869],
[-0.41183621, -0.08683671],
[-0.45470003, -0.04432262]])
>>> softmax(np.dot(Z1,W2) + b2)
array([[0.99552554, 0.00447446],
[0.41945396, 0.58054604],
[0.41945782, 0.58054218],
[0.39882163, 0.60117837]])

一度くらい argmax() を噛ませた結果を表示してみよう。ちょっとはわかりやすくならないだろうか?

>>> y = softmax(np.dot(Z1,W2) + b2)
>>> np.argmax(y, axis=1)
array([0, 1, 1, 1], dtype=int64)

正しくは [0, 1, 1, 0] と表示されなければならない。

今回は収束しなかったから結果もワケわかんないが、次からはわからないでもない結果が出てくるので、長々と書いたのは比較用である。 

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

  • 作者:斎藤 康毅
  • 発売日: 2016/09/24
  • メディア: 単行本(ソフトカバー)
 

 続いて、この初期値を小数点以下7桁に丸めてみる。

>>> W1 = np.round(W1, decimals=7)
>>> W2 = np.round(W2, decimals=7)
>>>
>>> W1
array([[-0.0042078, -0.0433123, 0.1213682],
[-0.0095356, 0.0198721, 0.02594 ]])
>>> W2
array([[ 0.0821265, -0.0486053],
[-0.1715082, -0.3032603],
[-0.003902 , -0.0539731]])

そうすると、なんと正解率100%、評価関数の値ゼロ付近に収束してしまうのだ!

 

左:「#コード4-3」によるW1の3D折れ線グラフ。

右:「#コード4-4」によるW1の3面2D展開図風グラフ。

f:id:watto:20210323104751p:plain
f:id:watto:20210323104741p:plain

 

左:「#コード4-5」によるb1の3D折れ線グラフ。

右:「#コード4-6」によるb1の3面2D展開図風グラフ。

f:id:watto:20210323104730p:plain
f:id:watto:20210323104725p:plain

 

左:「#コード4-7」によるW2の3D折れ線グラフ。

右:「#コード4-8」によるW2の3面2D展開図風グラフ。

f:id:watto:20210323104721p:plain
f:id:watto:20210323104746p:plain

 

左:「#コード4-9」によるバイアスb2の2D折れ線グラフ。

右:「#コード4-2」による正解率と損失関数の値のグラフ。

f:id:watto:20210323104735p:plain
f:id:watto:20210323104712p:plain

ご覧の通り初期値の小数点以下8桁目を丸めただけで、正解率100%損失関数の値ゼロ付近に収束するという劇的な変化を見せた。丸めなかった場合との差は一千万分の一以下である!

グラフ描画後のW1、b1、W2、b2のダンプ。

>>> W1
array([[ 0.15566959, -3.62609222, 4.41335946],
[ 0.15508861, -3.62582171, 4.41213812]])
>>> b1
array([ 4.61571949, 6.2275166 , -0.49241919])
>>> W2
array([[ -8.68490772, 8.71842892],
[ -9.37837986, 8.90361136],
[-10.95008053, 10.89220543]])
>>> b2
array([ 25.00004968, -25.00004968])

 

上掲値を用いた1層出力 Z1 = sigmoid(np.dot(x_e,W1) + b1) の検算。

>>> np.dot(x_e,W1) + b1
array([[ 4.61571949, 6.2275166 , -0.49241919],
[ 4.77138908, 2.60142438, 3.92094027],
[ 4.77080811, 2.60169489, 3.91971893],
[ 4.92647769, -1.02439733, 8.33307838]])
>>> Z1 = sigmoid(np.dot(x_e,W1) + b1)
>>> Z1
array([[0.99020189, 0.99802954, 0.37932383],
[0.99160251, 0.93095319, 0.98056284],
[0.99159767, 0.93097058, 0.98053955],
[0.99280021, 0.26417174, 0.99975963]])

 

Z1 を小数点以下1桁表示してみる。

>>> np.round(Z1,decimals=1)
array([[1. , 1. , 0.4],
[1. , 0.9, 1. ],
[1. , 0.9, 1. ],
[1. , 0.3, 1. ]])

1列目が余剰次元で、2列目と3列目で EOR を実現しようとしていることが推察される。「余剰次元」という言葉は「次層への影響が小さそうな列」というほどの意味で使っています。

 

念のため2層出力および出力に argmax() を噛ませた結果を。

>>> np.dot(Z1,W2) + b2
array([[ 2.88671093, -3.34930457],
[-3.0800014 , 2.61450368],
[-3.07986738, 2.61436259],
[ 2.95272011, -3.10272186]])
>>> softmax(np.dot(Z1,W2) + b2)
array([[0.99804618, 0.00195382],
[0.00335312, 0.99664688],
[0.00335404, 0.99664596],
[0.99766042, 0.00233958]])
>>> y = softmax(np.dot(Z1,W2) + b2)
>>> np.argmax(y, axis=1)
array([0, 1, 1, 0], dtype=int64) 

[0, 1, 1, 0]、正解している。

ゼロから作るDeep Learning ❷ ―自然言語処理編

ゼロから作るDeep Learning ❷ ―自然言語処理編

  • 作者:斎藤 康毅
  • 発売日: 2018/07/21
  • メディア: 単行本(ソフトカバー)
 

初期値6桁丸めと5桁丸めのデータも採ったが、7桁のケースと似通っていたので省略する。結果が激しく変化したのが4桁丸めと3桁丸めのケースだった。差が大きいから変化して当然という見方もできようが、それでも4桁で1万分の1、3桁で千分の1以下の差である。

4桁で丸めたケース。

>>> W1 = np.round(W1, decimals=4)
>>> W2 = np.round(W2, decimals=4)

>>> W1
array([[-0.0042, -0.0433, 0.1214],
[-0.0095, 0.0199, 0.0259]])
>>> W2
array([[ 0.0821, -0.0486],
[-0.1715, -0.3033],
[-0.0039, -0.054 ]])

 

左:「#コード4-3」によるW1の3D折れ線グラフ。

右:「#コード4-4」によるW1の3面2D展開図風グラフ。

f:id:watto:20210323121826p:plain
f:id:watto:20210323121814p:plain

 

左:「#コード4-5」によるb1の3D折れ線グラフ。

右:「#コード4-6」によるb1の3面2D展開図風グラフ。

f:id:watto:20210323121804p:plain
f:id:watto:20210323121758p:plain

 

左:「#コード4-7」によるW2の3D折れ線グラフ。

右:「#コード4-8」によるW2の3面2D展開図風グラフ。

f:id:watto:20210323121753p:plain
f:id:watto:20210323121820p:plain

 

左:「#コード4-9」によるバイアスb2の2D折れ線グラフ。

右:「#コード4-2」による正解率と損失関数の値のグラフ。

f:id:watto:20210323121809p:plain
f:id:watto:20210323121738p:plain

前々回「その5」あたりでは地味だったb1やW2のグラフの動きが激しい。今回はどれもそうか。

グラフ描画後のW1、b1、W2、b2のダンプ。

>>> W1
array([[-0.5733437 , -3.00341978, 3.54293311],
[ 0.58475128, 2.82789074, -3.73600438]])
>>> b1
array([3.18836141, 2.22843679, 2.34602933])
>>> W2
array([[ 5.54385952, -5.51035952],
[ 5.69392689, -6.16872689],
[ 5.15003454, -5.20793454]])
>>> b2
array([-13.74932189, 13.74932189])

 

1層出力 Z1 = sigmoid(np.dot(x_e,W1) + b1) の小数点以下1桁表示。

>>> Z1 = sigmoid(np.dot(x_e,W1) + b1)
>>> np.round(Z1,decimals=1)
array([[1. , 0.9, 0.9],
[0.9, 0.3, 1. ],
[1. , 1. , 0.2],
[1. , 0.9, 0.9]])

1行目が余剰次元のようなのはいいとして、2行目と3行目でEORを実現しようとする方法が、7桁丸めのときと異なっている。

すなわち7桁丸めのときは2列目と3列目をAND演算しているかのようだが、この4桁丸めではNAND演算しているように見える。

4桁丸めと7桁丸めでは異なるアトラクターに引き寄せられていると表現していいのではないだろうか。アトラクターも不正確な言い方なので「目標とする収束値」などと言い換えた方が多少はマシかもだが。

 

2層目出力と argmax()。結果は同じである。

>>> np.dot(Z1,W2) + b2
array([[ 1.41531244, -1.86461704],
[-1.65174635, 1.47546936],
[-1.64513101, 1.19453733],
[ 1.23769924, -1.67817407]])
>>> softmax(np.dot(Z1,W2) + b2)
array([[0.96373382, 0.03626618],
[0.04199849, 0.95800151],
[0.05521784, 0.94478216],
[0.94862556, 0.05137444]])
>>> y = softmax(np.dot(Z1,W2) + b2)
>>> np.argmax(y, axis=1)
array([0, 1, 1, 0], dtype=int64)

ゼロから作るDeep Learning ❸ ―フレームワーク編

ゼロから作るDeep Learning ❸ ―フレームワーク編

  • 作者:斎藤 康毅
  • 発売日: 2020/04/20
  • メディア: 単行本(ソフトカバー)
 

 3桁で丸めたケース。

>>> W1 = np.round(W1, decimals=3)
>>> W2 = np.round(W2, decimals=3)

>>> W1
array([[-0.004, -0.043, 0.121],
[-0.01 , 0.02 , 0.026]])
>>> W2
array([[ 0.082, -0.049],
[-0.172, -0.303],
[-0.004, -0.054]])

  

左:「#コード4-3」によるW1の3D折れ線グラフ。

右:「#コード4-4」によるW1の3面2D展開図風グラフ。

f:id:watto:20210323125341p:plain
f:id:watto:20210323125331p:plain

 

左:「#コード4-5」によるb1の3D折れ線グラフ。

右:「#コード4-6」によるb1の3面2D展開図風グラフ。

f:id:watto:20210323125320p:plain
f:id:watto:20210323125315p:plain

 

左:「#コード4-7」によるW2の3D折れ線グラフ。

右:「#コード4-8」によるW2の3面2D展開図風グラフ。

f:id:watto:20210323125309p:plain
f:id:watto:20210323125336p:plain

 

左:「#コード4-9」によるバイアスb2の2D折れ線グラフ。

右:「#コード4-2」による正解率と損失関数の値のグラフ。

f:id:watto:20210323125326p:plain
f:id:watto:20210323125257p:plain

 

グラフ描画後のW1、b1、W2、b2のダンプ。

>>> W1
array([[ 3.76666159, -2.17278522, -4.22211443],
[-2.11128289, 3.81356837, -4.28411122]])
>>> b1
array([2.13895947, 2.19372889, 1.55502881])
>>> W2
array([[ 13.5655765 , -13.5325765 ],
[ 13.31123554, -13.78623554],
[ 4.06164038, -4.11964038]])
>>> b2
array([-23.99379003, 23.99379003])

 

1層出力 Z1 の小数点以下1桁表示。

>>> np.round(Z1,decimals=1)
array([[0.9, 0.9, 0.8],
[1. , 0.5, 0.1],
[0.5, 1. , 0.1],
[1. , 1. , 0. ]])

これは何をしているんだろう? 1列目と2列目のNANDを使って3列目は余剰次元? それにしてはフリーダム。

むしろ余剰次元は2層目の行列演算で無視できればいいのだから、もともと 0 または 1 に揃える必要性はなさそうに思われる。

 

2層出力と argmax()。結果は合っているのだが。

>>> np.dot(Z1,W2) + b2
array([[ 3.47178286, -3.91749739],
[-3.47597717, 3.26513381],
[-3.58969204, 3.12903216],
[ 2.30683187, -2.73956496]])
>>> softmax(np.dot(Z1,W2) + b2)
array([[9.99382541e-01, 6.17458987e-04],
[1.17994009e-03, 9.98820060e-01],
[1.20662080e-03, 9.98793379e-01],
[9.93608643e-01, 6.39135684e-03]])
>>> y = softmax(np.dot(Z1,W2) + b2)
>>> np.argmax(y, axis=1)
array([0, 1, 1, 0], dtype=int64)

スポンサーリンク