1. 前提条件
このページでは、損失関数の紹介と簡単な実装サンプルについて記載する。
▼ Python3.6、NumPyがインストールされていること。
このページでは、venvの仮想環境(Python3.6)上にNumPyをインストールした環境で、Python対話モード(Pythonインタプリタ)にて実装サンプルを記載している。
※ Python対話モード、NumPyについては下記を参考。
Python対話モード:[Python] 対話モード (インタプリタ) の使用方法
NumPy:[Python] [NumPy] インストールとnumpy.ndarrayの使用方法
2. ニューラルネットワークで使用する損失関数
ニューラルネットワークの学習とは、訓練データを基に意図した結果となる最適な重みパラメータを判別する ことを指す。
学習するためには、損失関数 という ニューラルネットワークの性能の悪さ表す指標 を基準にして、その結果が 最も小さい値となる重み となるように 自己探索(最適な重みパラメータを探す) していくことになる。
以下、有名どころの損失関数である 2 乗和誤差、交差エントロピー誤差 について、簡単なPython実装サンプルを使って解説する。
3. 2 乗和誤差の定義と実装サンプル
2 乗和誤差 (mean squared error) は、残差平方和とも呼ばれ、ターゲットとなる 2 つの変数差の2 乗を総和し、2 で割った値 を取る。
▼ 2 乗和誤差の定義
この 2 つの変数のうち \(y_{k}\) をニューラルネットワークの出力、もう 1 つの \(t_{k}\) を教師データ (訓練データ) と置く。
※ \(k\) は、データの次元数
\[
{\normalsize
E = \frac{1}{2}\sum_{i=1}^{n} (y_{k}-t_{k})^2…(A)
}
\]
▼ 2 乗和誤差の実装サンプル
下記で触れたMNISTデータセットを用いたと想定して、10 個の要素からなるデータを例に解説する。
$ python
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
・\(k=10\) は、10 個の要素からなるデータでMNISTで扱うデータ数 (0 ~ 9 の数字)
・\(y_{k}\) にあたる \(y\) は、ニューラルネットワークの出力でソフトマックス関数の出力値 (確率)
※ この出力値の算出処理については、下記ページの「predict 関数」を参考。
・\(t_{k}\) にあたる \(t\) は、教師データでMNISTであらかじめ準備されている正解を表す配列(1 が 正解)
そして、\(y\) と \(t\) は、それぞれの要素同士が対となっていて、下記結果を表している。
・要素 1:数字画像が 0 の確率が 10 % (0.1) ⇒ 不正解 (0)
・要素 2:数字画像が 1 の確率が 5 % (0.05) ⇒ 不正解 (0)
・要素 3:数字画像が 2 の確率が 60 % (0.6) ⇒ 正解 (1)
・要素 4:数字画像が 3 の確率が 0 % (0.0) ⇒ 不正解 (0)
・要素 5:数字画像が 4 の確率が 5 % (0.05) ⇒ 不正解 (0)
・要素 6:数字画像が 5 の確率が 10 % (0.1) ⇒ 不正解 (0)
・要素 7:数字画像が 6 の確率が 0 % (0.0) ⇒ 不正解 (0)
・要素 8:数字画像が 7 の確率が 10 % (0.1) ⇒ 不正解 (0)
・要素 9:数字画像が 8 の確率が 0 % (0.0) ⇒ 不正解 (0)
・要素 10:数字画像が 9 の確率が 0 % (0.0) ⇒ 不正解 (0)
上記 \((A)\) は、この要素別の \(y\) と \(t\) の差を2乗した総和を 2 で割ったもの。
これをPythonで書くと
0.5 * np.sum((y-t)**2)
となる。
これを関数で定義し、上記の \(y\) と \(t\) で実行してみる。
$ python
>>> import numpy as np
>>> def mean_squared_error(y, t):
... return 0.5 * np.sum((y-t)**2)
...
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
>>>
>>> mean_squared_error(np.array(y), np.array(t))
0.09750000000000003
>>>
正解である 2 が 60 %となる上記 mean_squared_error の結果は、約 0.0975 となった。
試しにわざとはずして 不正解である 7 が 60 %となる \(y\) で結果を見てみると
$ python
>>> import numpy as np
>>> def mean_squared_error(y, t):
... return 0.5 * np.sum((y-t)**2)
...
>>> y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
>>>
>>> mean_squared_error(np.array(y), np.array(t))
0.5975
>>>
結果は、0.5975 となり 約6倍 (損失が6倍) まで大きくなってしまう。
ちなみに正解である 2 が 100 %となる下記 \(y\) だと 結果は 0 となり、損失が0(間違った予測なし) ということになる。
>>> y = [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
>>> mean_squared_error(np.array(y), np.array(t))
0.0
>>>
4. 交差エントロピー誤差と実装サンプル
交差エントロピー誤差 (cross entropy error) は、クロスエントロピーとも呼ばれ、ターゲットとなる 2 つの変数のうち、一方を自然対数(低は \(e\))として双方の積総和にマイナスをかけた値 を取る。
▼ 交差エントロピー誤差の定義
この 2 つの変数のうち \(y_{k}\) を ニューラルネットワークの出力、もう 1 つの \(t_{k}\) を教師データ (訓練データ)と置く。
※ \(k\) は、データの次元数
\[
{\normalsize
E = -\sum_{i=1}^{n} t_{k} \log \ y_{k}…(B)
}
\]
▼ 交差エントロピー誤差の実装サンプル
前項で触れた 3. 2 乗和誤差の定義と実装サンプル と同様に、10 個の要素からなるデータを例に解説する。
$ python
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
\(y_{k}\) 、\(t_{k}\) と同様に \(k=10\) となる \(y\) は、ソフトマックス関数の出力値で \(t\) は、正解のみ 1、不正解は 0 を取る。
そして、上記 \((B)\) の \(t_{k}\) は、不正解が 0 となる 9つあり積も必ず 0 となる。
よって、結局は正解 1 である \(y_{k}\) 、つまり 0.6 の自然対数にマイナスをかけた値である
\[
{\normalsize
-\log \ 0.6=0.51
}
\]
のみとなる。
前項「3. 2 乗和誤差の定義と実装サンプル」でわざとはずした下記の結果であれば
\[
{\normalsize
-\log \ 0.1=2.30
}
\]
となるため、結果(損失値)が高くなっていることが分かる。
$ python
>>> y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
これをPythonで書くと
delta = le-7
-np.sum(t * np.log(y + delta))
となります。
※ delta = le - 7 は、\(y_{k}\) が 0 となる \(log(0)\) よってマイナスの無限大に発散しないよう 微量な delta を足込んでいます。
これを関数で定義し、上記の \(y\) と \(t\) で実行してみる。
$ python
>>> import numpy as np
>>> def cross_entropy_error(y, t):
... delta = 1e-7
... return -np.sum(t * np.log(y + delta))
...
>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
>>> cross_entropy_error(np.array(y), np.array(t))
0.510825457099338
>>>
正解である 2 が 60 %となる上記 cross_entropy_error の結果は、約 0.510 となった。
わざとはずして 不正解である 2 が 10 %となる \(y\) だと
$ python
>>> import numpy as np
>>> def mean_squared_error(y, t):
... return 0.5 * np.sum((y-t)**2)
...
>>> y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ]
>>>
>>> cross_entropy_error(np.array(y), np.array(t))
2.302584092994546
>>>
結果は、約2.302 となり、やはり 約4倍 (損失が4倍) まで大きくなり、前項「3. 2 乗和誤差の定義と実装サンプル」と同様に妥当な 損失値が得られている ことが分かる。
以上。
【参考文献】
斎藤 康毅 (2018) 『ゼロから作るDeep Learning - Pythonで学ぶディープラーニングの理論と実装』株式会社オライリー・ジャパン