SIGMA-SE Math & Tech Library

SIGMA-SE Math & Tech Library


数学と情報技術をテーマに、書籍や教材だけではつかみにくい考え方を具体例とともに簡潔にわかりやすく伝える解説サイトです。
技術の歴史や背景、関連知識の整理、学習のための覚書や要約記事も掲載しています。

Python - ニューラルネットワーク:8/14 損失関数の役割と実装

概要

ニューラルネットワークの学習で使う損失関数について、2乗和誤差と交差エントロピー誤差を中心に整理する。

損失関数は、モデルの予測が正解からどれくらい外れているかを数値で表す関数となる。

ここでは、予測値と正解ラベルを使って損失を計算し、学習で何を小さくしようとしているのかを確認する。

この記事の構成

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

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

  • 損失関数の役割

    ニューラルネットワークの学習とは、訓練データに対する損失が小さくなるように重みパラメータを更新することを指す。

    学習では、ニューラルネットワークの予測と正解のずれを表す損失関数を基準に、その値が小さくなる方向へ重みを更新する。一般のニューラルネットワークでは、大域的に最小の値へ到達するとは限らない。

    ここでは、代表的な損失関数である2乗和誤差交差エントロピー誤差をPython実装サンプルで確認する。

2乗和誤差の定義

  • 数式と変数の意味

    ここで扱う2乗和誤差は、予測値と正解値の差を2乗して合計し、微分を簡潔にするため全体へ\(\frac{1}{2}\)を掛けた値である。全要素の平均を取る平均二乗誤差(MSE)とは区別する。

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

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

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

  • 2乗和誤差の計算確認

    Python - ニューラルネットワーク: 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 - ニューラルネットワーク: 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 half_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]
    >>> half_squared_error(np.array(y), np.array(t))
    0.09750000000000003
    

    正解である2が60%となり、上記half_squared_errorの結果は、約0.0975となった。

    試しにわざとはずして不正解である7が60%となる \(y\) で結果を見てみると

    $ python
    >>> import numpy as np
    >>> def half_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]
    >>> half_squared_error(np.array(y), np.array(t))
    0.5975
    

    結果は0.5975となり、約6倍(損失が6倍)まで大きくなってしまう。

    ちなみに正解である2が100%となる下記 \(y\) だと、結果は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]
    >>> half_squared_error(np.array(y), np.array(t))
    0.0
    

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

  • 数式と変数の意味

    交差エントロピー誤差(cross entropy error)は、各クラスの教師データに予測確率の自然対数(底は \(e\))を掛け、その総和へマイナスを付けた値を取る。

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

    \[ E = -\sum_{k=1}^{K} 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倍)まで大きくなり、前項と同様に妥当な損失値が得られていることが分かる。

まとめ

  • 損失は予測の悪さを連続値で表し、精度は正解した割合を表すため、役割が異なる。
  • 2乗和誤差は予測値と正解値の差に、交差エントロピー誤差は正解クラスの予測確率に注目する。one-hot表現では正解クラスだけが1になる。
  • 交差エントロピー誤差ではlog(0)を避けるため微小値を加え、学習では損失が小さくなる方向へ重みを更新。

参考文献

この記事を共有
Xで共有 Facebookで共有 LINEで共有



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