目的
この記事では、重みに対する勾配法(勾配降下法)の実装サンプルを記載する。
概念の説明と実装サンプル
重みに対する勾配法とは
重みは、正解に対して入力値がどれだけ影響するかを示す重要度を表す。
重みという言葉からは全くイメージできない意味を持つが、これは英語のWeighを直訳し、重みとなっているからであり、本来は大切さ、価値、重要性という意味で命名されている。
そして、この重み(重要度)は、一般的にWeighの \(w\) を取り、\(w_{0}\)、\(w_{1}\)、\(w_{2}\) …で表現され、評価的に具体的な数値を入れてみて損失結果を計るためのパラメータとなる。
ニューラルネットワークでは、この重みに対して、Python - AI : 勾配降下法の実装サンプル で解説した勾配降下法を実施し、最適な重みを求めていく。
また、下記に示す行列のよう色々なパターンの重みを行列計算で一斉に偏微分し、最適な重みを求めていく。
\(W\) は、一斉に偏微分しようとしている2行3列の重み達。
\(L\) は、対象となる損失関数で \(\displaystyle \frac{∂L}{∂W}\) の各要素で偏微分し、損失関数\(L\)
の勾配を求めている。
以降は、この勾配を求める実装サンプルについて記載する。
重みに対する勾配のPython実装サンプル
以下、参考文献『ゼロから作るDeep Learning』から提供されている ch04/gradient_simplenet.py
を用いたサンプル解説をしていく。
※ サンプルコードは、下記Gitからダウンロードする。
Git(deep-learning-from-scratch):
https://github.com/oreilly-japan/deep-learning-from-scratch
ここでは、より分かりやすく説明するため、ch04/gradient_simplenet.py
のsimpleNet
クラスを実装するにあたりimportされている下記3つの関数をあえてPython対話モードで定義する形で記載する。
-
ソフトマックス関数:common/functions.pyのsoftmax関数
※ ソフトマックス関数の一般的な定義は下記ページを参考。
Python - AI : 活性化関数の実装サンプルまとめ(ステップ、シグモイド、ReLU、恒等関数、ソフトマックス関数) > ソフトマックス関数$ python >>> import numpy as np >>> >>> def softmax(x): ... if x.ndim == 2: ... x = x.T ... x = x - np.max(x, axis=0) ... y = np.exp(x) / np.sum(np.exp(x), axis=0) ... return y.T ... x = x - np.max(x) ... return np.exp(x) / np.sum(np.exp(x)) ... >>>
-
交差エントロピー誤差:common/functions.py の cross_entropy_error関数
※ 交差エントロピー誤差の処理内容については、下記ページを参考。
Python - AI : 損失関数(2乗和誤差、交差エントロピー誤差)と実装サンプル > ソフトマックス関数 Python - AI : 交差エントロピー誤差のミニバッチ学習と実装サンプル>>> # 上記対話モードの続き >>> def cross_entropy_error(y, t): ... if y.ndim == 1: ... t = t.reshape(1, t.size) ... y = y.reshape(1, y.size) ... ... if t.size == y.size: ... t = t.argmax(axis=1) ... ... batch_size = y.shape[0] ... return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size ... >>>
-
勾配処理:common/gradient.pyのnumerical_gradient関数
※ 勾配の処理内容は下記ページの勾配関数(num_gradient)を参考。
Python - AI : 偏微分と勾配の実装サンプル>>> # 上記対話モードの続き >>> def numerical_gradient(f, x): ... h = 1e-4 ... grad = np.zeros_like(x) ... ... it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) ... ... while not it.finished: ... idx = it.multi_index ... tmp_val = x[idx] ... x[idx] = float(tmp_val) + h ... fxh1 = f(x) ... ... x[idx] = tmp_val - h ... fxh2 = f(x) ... grad[idx] = (fxh1 - fxh2) / (2*h) ... ... x[idx] = tmp_val ... it.iternext() ... ... return grad ... >>>
-
重みに対する勾配処理:3つの関数を呼び出した形で
ch04/gradient_simplenet.py
のsimpleNet
クラスを実装>>> # 上記対話モードの続き >>> import sys, os >>> >>> class simpleNet: ... def __init__(self): ... self.W = np.random.randn(2,3) ... ... def predict(self, x): ... return np.dot(x, self.W) ... ... def loss(self, x, t): ... z = self.predict(x) ... y = softmax(z) ... loss = cross_entropy_error(y, t) ... ... return loss ... >>>
\(x\) は、入力データで \(t\) が教師データ。
predict関数は、入力データ \(x\) と
__init__
で設定した仮(ランダム)の重みパラメータself.W
の評価結果(積)を返す。loss関数は、predict関数、softmax関数を実施した \(y\) と教師データ \(t\) の損失関数(交差エントロピー誤差:cross_entropy_error関数)を返す。
実装サンプルの実行確認
以降、上記simpleNet
の実行例を基に解説する。
-
インスタンスの結果確認
>>> # 上記対話モードの続き >>> net = simpleNet() >>> print(net.W) [[ 0.49236891 -1.2239298 -1.13722119] [ 0.05405365 -0.79897152 -0.08066587]] >>>
simpleNet()
により、2x3行列のランダムな数値が生成される。 -
評価結果(積)の確認
>>> # ↑↑↑ 上記対話モードの続き >>> x = np.array([0.6, 0.9]) >>> p = net.predict(x) >>> print(p) [ 1.05414809 0.63071653 1.1328074 ] >>> np.argmax(p) 2 >>>
predict(x)
により、入力データ \([0.6, 0.9]\) と重みパラメータnet.W
の評価結果(積)を算出している。 (最大インデックスは、2) -
損失関数の結果確認
>>> # 上記対話モードの続き >>> t = np.array([0, 0, 1]) >>> net.loss(x, t) 0.92806853663411326 >>>
上記で最大インデックスとなった\(2\)が正解ラベルとなる状態で損失関数の結果は、約\(0.93\)
-
勾配の結果確認
>>> # 上記対話モードの続き >>> def f(W): ... return net.loss(x, t) ... >>> dW = numerical_gradient(f, net.W) >>> print(dW) [[ 0.21924763 0.14356247 -0.36281009 ] [ 0.32887144 0.2153437 -0.544211514]] >>>
net.loss(x, t)
をf(W)
とし、勾配処理(numerical_gradient
)を実施している。※
f(W)
のW
は、勾配処理:common/gradient.pyのnumerical_gradient関数の\((A)\)、\((B)\)と整合性が取れるように定義したもの。 -
結果から見る結論
重みパラメータ \(w_{11}\) と重みパラメータ \(w_{23}\) にスポットを当てた結果を見る。
\(w_{11}\) が \(h\)分増加すると、\(\displaystyle \frac{∂L}{∂W}\) の \(\displaystyle \frac{∂L}{∂w_{11}}\) は、約\(0.2\)となり、\(0.2\)増加している。
一方、\(w_{23}\) は \(h\) 分増加すると、\(\displaystyle \frac{∂L}{∂W}\) の \(\displaystyle \frac{∂L}{∂w_{23}}\) が約\(-0.5\)となり、\(0.5\)減少している。
よって、 重みパラメータ \(w_{11}\) は、マイナス方向に。
重みパラメータ \(w_{23}\) は、プラス方向に更新すべきという結論となる。※ 以上の要領で重みパラメータをより損失が少ない重みパラメータへ更新していくことが目的。
参考文献
- 斎藤 康毅(2018)『ゼロから作るDeep Learning - Pythonで学ぶディープラーニングの理論と実装』株式会社オライリー・ジャパン