前回の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