0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

softmax with loss Layerの実装 (E資格対策)

Posted at

前回のE検定で出題されたらしいので書きます

Softmax関数とは

y_k=\frac{exp(x_k)}{\sum_{j=1}^{N}exp(x_j)} \\
(y_1+y_2+\cdots + y_N=1)

で表される関数、活性化関数で用いられます。

Softmax関数の実装

import numpy as np

def softmax(x):
  sum_x=sum(np.exp(x))
  ex_x=np.exp(x)
  ex_x/=sum_x
  return ex_x

a=np.array([1,3,5])
soft_a=softmax(a)
print(soft_a) #[0.01587624 0.11731043 0.86681333]

できました。
理論上はこれでいいのですが、大きい値が入ってくると正しく計算ができない場合があります。
例えば

b=np.array([1000,1030,800])
soft_b=softmax(b)
print(soft_b)

とすると

[nan nan nan]

と計算ができなくなってしまいます。
なので先ほどの式を少しいじってみます。

\begin{align}
y_k&=\frac{exp(x_k)}{\sum_{j=1}^{N}exp(x_j)} \\
~\\
&=\frac{C~exp(x_k)}{C~\sum_{j=1}^{N}exp(x_j)} \\
~\\
&=\frac{exp(x_k+log~C)}{\sum_{j=1}^{N}exp(x_j+log~C)} \\
~\\
&=\frac{exp(x_k-C')}{\sum_{j=1}^{N}exp(x_j-C')} \\

\end{align}

分母と分子に$C$をかけ、$C=exp(log~C)$、さらに$log~C$を新たな定数$-C'$とすれば上記の式が成立します。
今回研鑽できなかった理由は、数値が大きすぎたからです。
そこで渡された配列の最大値を$C'$に代入し、計算できるようにします。

import numpy as np

def softmax(x):
  max_x=max(x)#最大値を求める
  sum_x=sum(np.exp(x-max_x))#-max_xを追加
  ex_x=np.exp(x-max_x)#-max_xを追加
  ex_x/=sum_x
  return ex_x

b=np.array([1000,1030,800])
soft_b=softmax(b)
print(soft_b)

とすると

[9.35762297e-014 1.00000000e+000 1.29499819e-100]

無事計算ができました。

CrossEntropyとは

Softmax関数は出力層に多く用いられるので、損失関数とくっついて出力されます。ここの損失関数として使わているのは交差エントロピーです。
ここでの交差エントロピーEは

$$
E=- \sum_k t_{k}log~y_{k}
$$

と表せます。$t_{k}$は教師データ、$y_{k}$はNNからの出力です。

CrossEntropyの実装

import numpy as np

def cross_entropy(t,y):
  delta=1e-7
  return -sum(t*np.log(y+delta)) #log0となるとinfで計算できなくなるため対策

t=[0,1,0]
y=[0.1,0.3,0.6]
print(cross_entropy(np.array(t), np.array(y)))

結果

1.2039724709926583
{\begin{align}
E &= -\sum_k t_{k}log~y_{k} \\
&=-\sum_k t_k \log \biggl (\frac{\exp(x_k)}{\sum_{j=1} ^n \exp(x_j)} \biggr ) \\
&= -\sum_k t_k x_k + \sum_k t_k \log \biggl (\sum_{j=1} ^n  \exp(x_j) \biggr ) \\
&= -\sum_k t_k x_k + \log \biggl (\sum_{j=1} ^n  \exp(x_j) \biggr ) \\
\end{align}
}

$\sum_k t_k=1,\sum_{j=1} ^n \exp(x_j)$はkに依存しないため、このような式に変形できます。
そして逆伝播を求めると

{\begin{align}
\frac{\partial E}{\partial x_k}
&= -t_k   +\frac{\exp(x_k)}{\sum_{j=1} ^n \exp(x_j)}  \\
&= -t_k   + y_k  \\
\end{align}
}

めっちゃきれいな形になりました。

合体

これまでを踏まえて合体していきます。

import numpy as np

def softmax(x):
  max_x=max(x)
  sum_x=sum(np.exp(x-max_x))
  ex_x=np.exp(x-max_x)
  ex_x/=sum_x
  return ex_x

def cross_entropy(t,y):
  delta=1e-7
  return -sum(t*np.log(y+delta))


class softmax_with_loss:
  def __init__(self):
    self.loss=None#損失
    self.y=None #softmaxの出力
    self.t=None #教師データ

  def forward(self,x,t):
    self.t=t
    self.y=softmax(x)
    self.loss=cross_entropy(self.t,self.y)

    return self.loss

  def backward(self, dout=1):
    batch_size=self.t.shape[0]
    dx=(self.y-self.t)/batch_size

    return dx

t=np.array([0,1,0])
x=np.array([0.1,0.3,0.6])

sofmax=softmax_with_loss()
print(sofmax.forward(x,t))
print(sofmax.backward())

結果

1.1532862384703433
[ 0.08612988 -0.22813406  0.14200417]

無事計算できました!

参考文献
ゼロから作るDeep Learning: Pythonで学ぶディープラーニングの理論と実装
https://qiita.com/Yoko303/items/09efd10161d0a764833d

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?