「[[.NET 開発基盤部会 Wiki>http://dotnetdevelopmentinfrastructure.osscons.jp]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。 -[[戻る>深層学習(deep learning)]] --[[パーセプトロン]] --[[ニューラルネットワーク]] ---ニューラルネットワーク(推論) ---[[ニューラルネットワーク(学習)]] *目次 [#x49ef019] #contents *概要 [#rfb3c4eb] 推論フェーズでは、信号は順方向に伝播する。 *行列の積による実装 [#laa37ea3] **簡単なニューラルネットワークの実装 [#u375e15b] ***対象 [#m553499c] バイアスと活性化関数を省略し重みだけとする。 >X行列 * W行列=Y行列 ┌ ┐ ┌ ┐ ┌ ┐ │a1 b1│ │a2 b2 c2│ = │a1a2+b1d2 a1b2+b1e2 a1c2+b1f2│ └ ┘ │ │ └ ┘ │d2 e2 f2│ └ ┘ X行列 W行列 Y行列 1行2列 2行3列 1行3列 ─ ─ └────┘ #ref(simple.png,left,nowrap,簡単なニューラルネットワークの実装,60%) ┌ ┐ ┌ ┐ ┌ ┐ │a b│ │1 3 5│ = │1a+2b 3a+4b 5a+6b│ └ ┘ │ │ └ ┘ │2 4 6│ └ ┘ X行列 W行列 Y行列 1行2列 2行3列 1行3列 ─ ─ └───┘ ***実装 [#qbef5163] >>> x=np.array([1,2]) >>> x array([1, 2]) >>> w=np.array([[1,3,5],[2,4,6]]) >>> w array([[1, 3, 5], [2, 4, 6]]) >>> y=np.dot(x,w) >>> y array([ 5, 11, 17]) **3層のニューラルネットワークの実装 [#pbae7d55] ***対象 [#j4d0a806] 前述の「[[簡単なニューラルネットワーク>#m553499c]]」を3層化する。 #ref(3tier.png,left,nowrap,3層のニューラルネットワークの実装,60%) -第一層目の計算の例~ 入力が1行2列、出力が1行3列 --重み付き信号とバイアスの和 ---a(1)1 = w(1)11 x1 + w(1)12 x2 + b(1)1 ---a(1)2 = w(1)21 x1 + w(1)22 x2 + b(1)2 ---a(1)3 = w(1)31 x1 + w(1)32 x2 + b(1)3 --これを行列で表すと、~ 入力が1行2列、出力が1行3列だから、 A(1) = x(1) W(1) + B(1) ---X(1) = [x1 x2] ---A(1) = [a(1)1 a(1)2 a(1)3] ---W(1) = W(1)は、2行3列 ┌ ┐ │w(1)11 w(1)21 w(1)31│ │w(1)12 w(1)22 w(1)32│ └ ┘ ---B(1) = 出力が1行3列だから、 [b(1)1 b(1)2 b(1)3] ---活性化関数~ [[Sigmoid関数>ニューラルネットワーク#u0ac8472]]を使用する。 h(A(1)) = Z(1) -第二層目の計算の例~ 入力が1行3列、出力が1行2列だから、 --第一層目と大方、同じ。 ---X(2) = 1行3列 ---A(2) = 1行2列 ---W(2) = 入力が1行3列、出力が1行2列だから、W(2)は、3行2列 ---b(2) = 出力と同じ、1行2列 -第三層目の計算の例~ 入力が1行2列、出力が1行2列だから、 --第一層目と大方、同じ。 ---X(3) = 1行2列 ---A(3) = 1行2列 ---W(2) = 入力が1行2列、出力が1行2列だから、W(2)は、2行2列 ---b(3) = 出力と同じ、1行2列 --活性化関数だけ異なる。 ---[[出力層の活性化関数>#w9eba652]]は問題によって異なる。 ---ここでは、[[恒等関数>#u6563984]]を使用する。 σ(A(3)) = Z(3) ***実装 [#sbb7f972] そのまんま実装した。 """This is a test program.""" import numpy as np def sigmoid(x_1): """sigmoid.""" return 1 / (1 + np.exp(-x_1)) def identity_function(y_1): """出力層の活性化関数""" return y_1 # 第一層 X1 = np.array([1.0, 0.5]) W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]) B1 = np.array([0.1, 0.2, 0.3]) print("第一層") A1 = np.dot(X1, W1) + B1 print("A1:" + str(A1)) Z1 = sigmoid(A1) print("Z1:" + str(Z1)) # 第二層 X2 = Z1 W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]) B2 = np.array([0.1, 0.2]) print("第二層") A2 = np.dot(X2, W2) + B2 print("A2:" + str(A2)) Z2 = sigmoid(A2) print("Z2:" + str(Z2)) # 第三層 X3 = Z2 W3 = np.array([[0.1, 0.3], [0.2, 0.4]]) B3 = np.array([0.1, 0.2]) print("第三層") A3 = np.dot(X3, W3) + B3 print("A3:" + str(A3)) Z3 = identity_function(A3) print("Z3:" + str(Z3)) ***慣例的実装 [#r9753caf] -init_networkで、ニューラルネットワークを定義/初期化し、 -forwardで入力から出力方向への伝達処理を行う。 """This is a test program.""" import numpy as np def sigmoid(x_1): """sigmoid.""" return 1 / (1 + np.exp(-x_1)) def identity_function(y_1): """出力層の活性化関数""" return y_1 def init_network(): """ニューラルネットワーク""" network = {} networkw = {} networkw[0] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]) networkw[1] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]) networkw[2] = np.array([[0.1, 0.3], [0.2, 0.4]]) networkb = {} networkb[0] = np.array([0.1, 0.2, 0.3]) networkb[1] = np.array([0.1, 0.2]) networkb[2] = np.array([0.1, 0.2]) network["W"] = networkw network["b"] = networkb return network def forward(network, zzz): """ニューラルネットワークの実行""" tier = len(network["W"]) for num in range(tier - 1): print("第" + str(num + 1) + "層") xxx = zzz aaa = np.dot(xxx, network["W"][num]) + network["b"][num] print("A" + str(num + 1) + ":" + str(aaa)) zzz = sigmoid(aaa) print("Z" + str(num + 1) + ":" + str(zzz)) print("第" + str(tier) + "層") xxx = zzz aaa = np.dot(xxx, network["W"][tier -1]) + network["b"][tier -1] print("A" + str(tier) + ":" + str(aaa)) zzz = identity_function(aaa) # 出力層の活性化関数 print("Z" + str(tier) + ":" + str(zzz)) return zzz print(forward(init_network(), np.array([1.0, 0.5]))) *出力層の設計 [#p221ffd8] **活性化関数 [#w9eba652] -ニューラルネットワークは --分類問題と ---入力データがどのクラスに属するか? ---例:写真から男性・女性を識別する。 --回帰問題に ---入力データから連続的な数値を予測する。 ---例:写真からその人の体重を予測する。 >用いることができる。 -一般的に、出力層の活性関数は、 --回帰問題 : [[恒等関数>#u6563984]] --分類問題 : ---[[Sigmoid関数>#qc025e6f]] ---[[Softmax関数>#b77bdfd7]] >を利用する。 **恒等関数 [#u6563984] -回帰問題で使用。 -入力をそのまま返す。 -式 f(x) = x -特徴 --回帰問題で使用。 --入力をそのまま返す。 -グラフ #ref(identity.png,left,nowrap,恒等関数,60%) **Sigmoid関数 [#qc025e6f] -2クラス分類の機械学習で使用される。 -xで条件付けされたyの[[ベルヌーイ分布>統計解析#aa908967]]を推定。 -[[オッズの対数を取ったロジット関数の逆関数>統計解析#ge055200]]らしい。 -実装などについては[[コチラ>ニューラルネットワーク#u0ac8472]] -中間層の活性化関数として既出なので、実装など詳細については[[コチラ>ニューラルネットワーク#u0ac8472]] **Softmax関数 [#b77bdfd7] -多クラス分類の機械学習で使用される。 -xで条件付けされたyの[[マルチヌーイ分布>統計解析#t1a00fea]]を推定。 -[[Sigmoid関数>#qc025e6f]]を多クラスに拡張、一般化したもの。 -Softmax関数の結果は --0 - 1 の実数になり、その総和は 1.0 になる。 --従って、Softmax関数の結果は「確率」として解釈できる。 --これは多クラス分類問題で使用できる。 --分類では、一番大きい値のみを使用する。 --Softmax関数を使用しても値の大小関係は変わらないので~ 順方向の推論フェーズにおいては、Softmax関数は省略可能である。 ***対象 [#ga31b4f9] exp(Ak) Yk = ─────────── n Σexp(Ai) i=1 [[Sigmoid関数>ニューラルネットワーク#u0ac8472]]の項で説明した通り、 -exp(x)とは、eのx乗を意味する。 -eは[[ネイピア数>DS:数学的基礎 - 微分・偏微分#adabdb73]](2.7182)の実数を表す。 #ref(softmax.png,left,nowrap,Softmax関数,60%) ***実装 [#z0de2e5f] -そのまま実装 >>> a=np.array([0.3, 2.9, 4.0]) >>> a array([ 0.3, 2.9, 4. ]) >>> exp_a = np.exp(a) >>> exp_a array([ 1.34985881, 18.17414537, 54.59815003]) >>> sum_exp_a = np.sum(exp_a) >>> sum_exp_a 74.122154210163302 >>> y = exp_a / sum_exp_a >>> y array([ 0.01821127, 0.24519181, 0.73659691]) >>> -関数化 """This is a test program.""" import numpy as np def softmax(aaa): """Softmax関数""" exp_a = np.exp(aaa) sum_exp_a = np.sum(exp_a) return exp_a / sum_exp_a print(softmax(np.array([0.3, 2.9, 4.0]))) ***オーバーフロー対策の実装 [#id0a8627] Softmax関数は指数関数を使用するため、オーバーフローし易い。~ 従って、ここでは、オーバーフロー対策について考える。 -以下のように変形できる。 exp(Ak) Yk = ─────────── n Σexp(Ai) i=1 > C exp(Ak) = ─────────── n C Σexp(Ai) i=1 > exp(Ak + logeC) = ─────────── n Σexp(Ai + logeC) i=1 --例えば、 ---10の3乗は、10の1乗*10の2乗 = 1,000 ---10の4乗は、10の2乗*10の2乗 = 10,000 ---なので、下のような変形ができるということになる。 (a+b) a b X = X * X --Xb=Cは、b=logxCであるから、下のようになる。 a a logxC (a+logxC) C X = X * X = X exp(Ak + C') = ─────────── n Σexp(Ai + C') i=1 -Cは、入力信号中の最大値を使用する。~ これによって、オーバーフローが発生し難くなる。以下に実装を示す。 """This is a test program.""" import numpy as np def softmax(aaa): """Softmax関数""" exp_a = np.exp(aaa - np.max(aaa)) sum_exp_a = np.sum(exp_a) return exp_a / sum_exp_a # print(softmax(np.array([0.3, 2.9, 4.0]))) print(softmax(np.array([1010, 1000, 990]))) **ニューロンの数 [#o46593dd] 出力層のニューロンの数は、問題に合わせて決定する。 -分類問題 : 分類する数に合わせる。 -回帰問題 : 多分、1つにする。 *手書き数字画像の認識 [#zf703d94] -実践的な手書き数字画像の認識を行う。 -ここでは、学習済みのパラメタを使用して実装をする。 **MNISTデータセット [#c18ded2a] 機械学習分野で最も有名なデータセット ***概要 [#l74156dc] -手書き数字の画像セット --数字の0-9で構成され、以下の画像を使用して学習/推論を行う。 ---学習用の訓練画像 :60,000枚 ---推論用のテスト画像:10,000枚 -28*28の単一チャンネル、グレー画像 --画像の1つのピクセルは、0-255の8ビット --画像には、正解を意味するラベルが与えられている。 --白黒画像は行列で表現でき、カラー画像は[[テンソル>NumPy#h768806a]](行列 * RGB)で表現できる。 ***データの取得 [#y26372f0] -以下にアクセスして[Download ZIP]する。 --oreilly-japan/deep-learning-from-scratch:~ 『ゼロから作る Deep Learning』のリポジトリ~ https://github.com/oreilly-japan/deep-learning-from-scratch -Cドラ直下にZIP解凍したら、CMDでch03ディレクトリに移動する。 -ここでpythonで[[mnist_show.py>https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch03/mnist_show.py]]を実行し、[[mnist.py>https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/dataset/mnist.py]]経由でデータセットを取得する。 c:\deep-learning-from-scratch-master\ch03>python mnist_show.py Downloading train-images-idx3-ubyte.gz ... Done Downloading train-labels-idx1-ubyte.gz ... Done Downloading t10k-images-idx3-ubyte.gz ... Done Downloading t10k-labels-idx1-ubyte.gz ... Done Converting train-images-idx3-ubyte.gz to NumPy Array ... Done Converting train-labels-idx1-ubyte.gz to NumPy Array ... Done Converting t10k-images-idx3-ubyte.gz to NumPy Array ... Done Converting t10k-labels-idx1-ubyte.gz to NumPy Array ... Done Creating pickle file ... Done! 5 (784,) (28, 28) c:\deep-learning-from-scratch-master\ch03>python mnist_show.py 5 (784,) (28, 28) -一回目はWebからのダウンロードは成功したが、画像表示ができなかった。~ 二回目以降は、ローカル保存した*.pklからデータを読み込むようで、画像表示できた。~ なお、*.pklファイルは、pythonのbinaryserialize的なpickleによって生成されている。 --\dataset\mnist.pkl は、MNISTデータ --\ch03\sample_weight.pkl は、重みパラメタ **推論処理 [#r85fa43b] ***ニューロン構成 [#g604f173] -入力層が784個、出力層が10個のする。 --入力層が784個 = 28 * 28 のピクセル数 --出力層が10個 = 分類問題なので、0 - 9 のクラス -中間層(隠れ層)の数は任意だが、ココでは以下の値に設定。 --第一層:50個 --第二層:100個 ***実行 [#oda0dcb6] 以下のように[[neuralnet_mnist.py>https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch03/neuralnet_mnist.py]]を実行すると、 >python neuralnet_mnist.py -推論用のテスト画像:10,000枚の分類結果の正解率が表示される。 -なお、ここでは、画像データに正規化(normalize)の前処理(preprocessing)を行っている。 **[[バッチ>ニューラルネットワーク(学習)#h15bc15d]]化 [#c7726cc3] ***ニューロン構成 [#u17966f5] -上記の[[neuralnet_mnist.py>https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch03/neuralnet_mnist.py]]処理中の重みパラメタを確認すると、~ この、ニューラルネットワークが以下の様なものであることを確認できる。 #ref(batch1.png,left,nowrap,batch1) -バッチ化は、X行列、Y行列の行を増やして~ データを纏めて投入できるようにするだけで良い。~ ソレに合わせてW行列も変わるがbに変更はない(1行n列)。 #ref(batch2.png,left,nowrap,batch2) ***実行 [#p03f2958] 以下のように[[neuralnet_mnist_batch.py>https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch03/neuralnet_mnist_batch.py]]を実行すると、 >python neuralnet_mnist_batch.py -neuralnet_mnistと比べ、100枚づつのバッチ処理が行われ、高速化される。 -正解の判定処理は、true値をsumするという方法を使用している。 -なお、np.argmaxの引数、axis=1は行方向の最大値となる要素のインデックスを求める。