平均誤差の計算

平均誤差の計算

損失関数の平均値

 分類問題を学習するネットワークの場合、出力値 $\boldsymbol{y}$ の各成分が入力データの属するクラスを表します。たとえば下図にあるように、ネットワークに $\boldsymbol{x}$ を入力して
 
\[\boldsymbol{y}=\begin{bmatrix}y_0\\y_1\\y_2\end{bmatrix}
=\begin{bmatrix}0.05\\ 0.83\\ 0.12\end{bmatrix}\]
という値が出力されたとすると、入力データがクラス $0$ に属する確率は $0.05$、クラス $1$ に属する確率は $0.83$、クラス $2$ に属する確率は $0.12$ となります。

 [python] classify cost function(損失関数)

 正解値(目標変数)は
 
\[\boldsymbol{t}=\begin{bmatrix}t_0\\ t_1\\ t_2\end{bmatrix}
=\begin{bmatrix}0\\ 1\\ 0\end{bmatrix}\]
となっているので、実際に入力データが属するクラスは $1$ です。ネットワークは出力値と正解値(目標値)の誤差がなるべく少なくなるように学習を進めます。上の例では、$y_1$ が $1$ に、$y_0$ と $y_2$ が $0$ に近づくように学習します。

 一般に $m$ クラス分類において、正解値(目標値)
 
\[\boldsymbol{t}=\begin{bmatrix}t_0\\ t_1\\ t_2\\ \vdots \\ t_{m-1}\end{bmatrix}\]
は 1 of K で表されたクラスです。ネットワークに $\boldsymbol{x}$ を入力して
 
\[\boldsymbol{y}=\begin{bmatrix}y_0\\ y_1\\ y_2\\ \vdots \\ y_{m-1}\end{bmatrix}\]
が出力されたとき、$y_0,\ y_1,\ y_2,\ \cdots\ y_{m-1}$ はクラス $1,\ 2,\ \cdots \ m-1$ に属する確率です。
 
\[\begin{align*}&P(\boldsymbol{t}=[0,\ 1,\ 0,\ \cdots ,\ 0]\mid\boldsymbol{x})=y_1\\[6pt]
&P(\boldsymbol{t}=[0,\ 0,\ 1,\ \cdots ,\ 0]\mid\boldsymbol{x})=y_2\\[6pt]
&\qquad\qquad\qquad\vdots\\[6pt]
&P(\boldsymbol{t}=[0,\ 0,\ 0,\ \cdots ,\ 1]\mid\boldsymbol{x})=y_{m-1}\end{align*}\]
 これらの式をまとめて、
 
\[P(\boldsymbol{t}\mid\boldsymbol{x})=y_0^{t_0}y_1^{t_1}y_2^{t_2}\ \cdots\ y_{m-1}^{t_{m-1}}\]
と表すことができます。出力 $\boldsymbol{y}$ にはネットワークで使用されたすべての重みパラメータ $w_{ji}$ が含まれているので、$P(\boldsymbol{t}\mid\boldsymbol{x})$ が最大になるように重みを最適化することによって、クラス $\boldsymbol{t}$ が生成される確率が最も高いモデルを構築することができます(最尤推定)。ここで、$P$ の自然対数をとって符号を変えた損失関数
 
\[\begin{align*}
E&=-\log P(\boldsymbol{t}\mid\boldsymbol{x})\\[6pt]
&=-t_0\log y_0-t_0\log y_1-\ \cdots\ -t_{m-1}\log y_{m-1}\\[6pt]
&=-\sum_{k=1}^{m-1}t_k\log y_k\end{align*}\]
を (1個の入力データに対する) 交差エントロピー誤差関数と定義します。実際のコードでは、この交差エントロピー誤差が最小となるように重み $w_{ji}$ を最適化することになります。

 バッチサイズ $n$ で学習させる場合、$n$ 個のデータそれぞれについて交差エントロピー誤差を計算して平均をとります。出力値と正解値(目標変数)の右下にデータ番号を添えて $t_{k,l},\ y_{k,l}$ のように表すと、バッチ内の交差エントロピー誤差の総和は
 
\[-\sum_{k,l}t_{k,l}(\log y_{k,l})\]
によって計算できます。つまり、すべての $k,\ l$ の組合わせについて $t_{k,l}$ と $\log y_{k,l}$ の積を計算して合計すればよいことになります。そのために、$n$ 個のデータベクトルを横に並べて次のような行列をつくります (Python の実装では 1 次元配列を縦に並べます)。
 
\[\begin{align*}T&=\begin{bmatrix}
t_{0,0} & t_{0,1} & \cdots & t_{0,n-1}\\
t_{1,0} & t_{1,1} & \cdots & t_{1,n-1}\\
t_{2,0} & t_{2,1} & \cdots & t_{2,n-1}\\
\vdots & \vdots & \vdots & \vdots\\
t_{m-1,0} & t_{m-1,1} & \cdots & t_{m-1,n-1}
\end{bmatrix}\\[6pt]
\log Y&=\begin{bmatrix}
\log y_{0,0} & \log y_{0,1} & \cdots & \log y_{0,n-1}\\
\log y_{1,0} & \log y_{1,1} & \cdots & \log y_{1,n-1}\\
\log y_{2,0} & \log y_{2,1} & \cdots & \log y_{2,n-1}\\
\vdots & \vdots & \vdots & \vdots\\
\log y_{m-1,0} & \log y_{m-1,1} & \cdots & \log y_{m-1,n-1}
\end{bmatrix}\end{align*}\]
 行列 $A$ と $B$ の要素同士の積をとって作った行列をアダマール積とよび、$A\circ B$ で表します(行列積とは異なる演算です)。$T$ と $\log Y$ のアダマール積をとると、
 
\[T\circ (\log Y)=\begin{bmatrix}
t_{0,0}\log y_{0,0} & t_{0,1}\log y_{0,1} & \cdots & t_{0,n-1}\log y_{0,n-1}\\
t_{1,0}\log y_{1,0} & t_{1,1}\log y_{1,1} & \cdots & t_{1,n-1}\log y_{1,n-1}\\
t_{2,0}\log y_{2,0} & t_{2,1}\log y_{2,1} & \cdots & t_{2,n-1}\log y_{2,n-1}\\
\vdots & \vdots & \vdots & \vdots\\
t_{m-1,0}\log y_{m-1,0} & t_{m-1,0}\log y_{m-1,1} & \cdots & t_{m-1,n-1}\log y_{m-1,n-1}\\
\end{bmatrix}\]
となります。この行列のすべての成分について和をとってから $n$ で割れば、バッチ内の平均交差エントロピー誤差を求めることができます。

 NumPy の配列同士を「 * 」で演算するとアダマール積を返します。
 また、numpy.sum() は配列のすべての要素の和をとるので、平均交差エントロピー誤差関数の実装は以下のようになります。

# リストM10-D-1

# 平均交差エントロピー誤差関数
def cross_entropy(y, t):
    ce = - np.sum(t * np.log(y + 1e-8)) / y.shape[0]
    return ce

 y.shape[0] は配列の行数なので、データの個数、すなわちバッチサイズ $n$ です(1次元配列をベクトルに対応させているので、上の数式とはベクトルの並ぶ方向が逆になっています)。np.log() の引数に y + 1e-8 を渡しますが、これは y が 0 のときに -inf を返さないように極めて小さな値を付加しています (-inf は $-\infty$, 1e-8 は $10^{-8}$ を意味します)。cross_entropy() 関数によるエントロピー誤差の計算を試してみましょう。まず適当な出力データと正解値データ(目標変数)を用意しておきます。

# リストM10-D-2

np.random.seed(10)

# ソフトマックス関数
def softmax(x):
    f = np.exp(x)/np.sum(np.exp(x), axis = 1, keepdims = True)
    return f

# -2~2の乱数要素をもつ3×3配列
x = np.random.uniform(-2, 2, (3, 3))

# xをソフトマックス関数に通す
y = softmax(x)

# 入力データのクラス(正解値)
x_class = np.array([2, 1, 0])

# クラスを1-of-Kに変換
t = np.identity(3, dtype = "int8")[x_class]

print("出力配列y\n{}\n".format(y))
print("正解値t\n{}".format(t))
出力配列y
[[0.61492068 0.03054553 0.35453379]
 [0.67096688 0.24654198 0.08249113]
 [0.08789538 0.83382094 0.07828368]]

正解値t
[[0 0 1]
 [0 1 0]
 [1 0 0]]

 cross_entropy() に y と t を渡して平均交差エントロピー誤差を計算してみます。

# リストM10-D-3

# 1バッチの平均交差エントロピー誤差を計算
ce = cross_entropy(y, t)

print(ce)
1.6229274747438958