SIGMA-SE Tech Blog

SIGMA-SE Tech Blog

Python - AI : 損失関数(2乗和誤差、交差エントロピー誤差)と実装サンプル

目的

この記事では、損失関数の紹介と簡単な実装サンプルについて記載する。

概念の説明と実装サンプル

ニューラルネットワークで使用する損失関数

ニューラルネットワークの学習とは、訓練データを基に意図した結果となる最適な重みパラメータを判別することを指す。

学習するためには、損失関数というニューラルネットワークの性能の悪さを表す指標を基準に、その値が最も小さくなる重みとなるように自己探索(最適な重みパラメータを探す)していくことになる。

ここでは、有名な損失関数である2乗和誤差交差エントロピー誤差について、簡単なPython実装サンプルを使って解説する。

2乗和誤差の定義

2乗和誤差(mean squared error)は、残差平方和とも呼ばれ、ターゲットとなる2つの変数差の2乗を総和し、2で割った値を取る。

この2つの変数のうち \(y_{k}\) をニューラルネットワークの出力、もう1つの \(t_{k}\) を教師データ(訓練データ)と置く。
※ \(k\) はデータの次元数

\[ E = \frac{1}{2}\sum_{i=1}^{n} (y_{k}-t_{k})^2\hspace{5mm}・・・(A) \]

2乗和誤差の実装サンプル

Python - AI : MNISTのダウンロード方法(手書き数字画像セットを取込む) で触れた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\) は、ニューラルネットワークの出力でソフトマックス関数出力値(確率)
    ※ この出力値の算出処理については、Python - AI : MNISTを使ったニューラルネットワークの推論処理と実装サンプル> 推論処理のニューロン構成と関数定義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

交差エントロピー誤差の定義

交差エントロピー誤差(cross entropy error)は、クロスエントロピーとも呼ばれ、ターゲットとなる2つの変数のうち、一方を自然対数(底は \(e\))として双方の積総和にマイナスをかけた値を取る。

この2つの変数のうち \(y_{k}\) をニューラルネットワークの出力、もう1つの \(t_{k}\) を教師データ(訓練データ)とする。
※ \(k\) はデータの次元数

\[ E = -\sum_{i=1}^{n} t_{k} \log y_{k}\hspace{5mm}・・・(B) \]

交差エントロピー誤差と実装サンプル

前項と同様に、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の自然対数にマイナスをかけた値である

\[ -\log 0.6 = 0.51 \]
のみとなる。

前項でわざとはずした下記の結果であれば

\[ -\log 0.1 = 2.30 \]

となるため、結果(損失値)が高くなっていることが分かる。

これをPythonで書くと

delta = 1e-7
-np.sum(t * np.log(y + delta))

となる。

※ \(delta = 1e-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 cross_entropy_error(y, t):
...     delta = 1e-7
...     return -np.sum(t * np.log(y + delta))
...
>>> 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倍)まで大きくなり、前項と同様に妥当な損失値が得られていることが分かる。

参考文献

  • 斎藤 康毅(2018)『ゼロから作るDeep Learning - Pythonで学ぶディープラーニングの理論と実装』株式会社オライリー・ジャパン


Copyright SIGMA-SE All Rights Reserved.
s-hama@sigma-se.jp