誤差逆伝播法 その3


AIって結局何なのかよく分からないので、とりあえず100日間勉強してみた Day59


経緯についてはこちらをご参照ください。



■本日の進捗

  • ソフトマックス層の逆伝播を理解


■はじめに

今回も「ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装(オライリー・ジャパン)」で、深層学習を学んでいきます。

前回まで、これまで学んできた層の逆伝播を考えてきましたが、今回は最後にソフトマックス関数について逆伝播を実装していきます。

■ソフトマックス層の逆伝播

まずはソフトマックス関数の復習ですが、これは下記のように記述される活性化関数で、主にクラス分類タスクの出力層で用いられ、ニューラルネットワークの出力を0から1の確率に変換してくれる関数でした。

$$ \sigma (z_i) = \frac{ e^{z_i}}{ \displaystyle \sum_{j=1}^n e^{z_j} } $$

ここで、ニューラルネットワークの損失として交差エントロピー誤差を用いた場合を考えます。

交差エントロピー誤差はニューラルネットワークの出力に対して用いられますが、ソフトマックス関数は出力層の活性化関数であることを考えれば、ソフトマックス関数の出力はニューラルネットワークの出力そのものを示すと考えることもできます。

$$ L = \ – \displaystyle \sum_{j} y_i \log (\sigma (z_j)) $$

各ソフトマックス関数に対する損失の微分は、

$$ \frac{\partial L}{\partial \sigma (z_k) } = \ – \frac{y_k}{\sigma (z_k)} \ \ \ \ \ \cdots (1) $$

また、この出力に対する損失の微分を考えると、連鎖律を用いれば、

$$ \frac{\partial L}{\partial z_j} = \sum_{k} \frac{\partial L}{\partial \sigma (z_k) } \cdot \frac{\partial \sigma (z_k)}{\partial \sigma z_j} \ \ \ \ \ \cdots (2) $$

ここで、i番目のソフトマックス関数の微分を考えます。

● 同じクラス( i = j )の時

$$ \frac{\partial \sigma(z_j) }{\partial z_j } = \frac{\partial}{\partial z_j} \left( \frac{ e^{z_j}}{ \displaystyle \sum_{k=1}^n e^{z_k} } \right) $$

商の微分法則を用いれば、

$$ \frac{\partial \sigma(z_j) }{\partial z_j } = \frac{ \left( \frac{\partial}{\partial z_j } e^{z_k} \right) \cdot \displaystyle \sum_{k=1}^n e^{z_k} – e^{z_j} \cdot \frac{\partial }{\partial z_j } \left( \displaystyle \sum_{k=1}^n e^{z_k} \right) }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = \frac{ e^{z_j} \cdot \displaystyle \sum_{k=1}^n e^{z_k} – e^{z_j} \cdot e^{z_j} }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = \frac{ e^{z_j} \left( \displaystyle \sum_{k=1}^n e^{z_k} – e^{z_j} \right) }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = \frac{e^{z_j }}{\displaystyle \sum_{k=1}^n e^{z_k} } \cdot \left( 1 – \frac{e^{z_j} }{ \displaystyle \sum_{k=1}^n e^{z_k}} \right) $$

以上から、ソフトマックス関数の微分は、

$$ \frac{\partial \sigma (z_j) }{\partial z_j } = \sigma(z_j) (1 – \sigma (z_j) ) \ \ \ \ \ \cdots (3) $$

● 異なるクラス( i ≠ j )の時

i ≠ j の場合は、(例えば i = i ならば)zi で微分すると、

$$ \frac{\partial \sigma(z_j) }{\partial z_i } = \frac{\partial}{\partial z_i} \left( \frac{ e^{z_j}}{ \displaystyle \sum_{k=1}^n e^{z_k} } \right) $$

同様に、

$$ \frac{\partial \sigma(z_j) }{\partial z_i } = \frac{ \left( \frac{\partial}{\partial z_i } e^{z_k} \right) \cdot \displaystyle \sum_{k=1}^n e^{z_k} – e^{z_j} \cdot \frac{\partial }{\partial z_i } \left( \displaystyle \sum_{k=1}^n e^{z_k} \right) }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = \frac{ 0 \cdot \displaystyle \sum_{k=1}^n e^{z_k} – e^{z_j} \cdot e^{z_i} }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = – \frac{ e^{z_j} \cdot e^{z_i} }{ \left( \displaystyle \sum_{k=1}^n e^{z_k} \right)^2 } $$

$$ = \frac{ e^{z_j}}{ \displaystyle \sum_{k=1}^n e^{z_k} } \cdot \frac{ e^{z_i}}{ \displaystyle \sum_{k=1}^n e^{z_k} } $$

以上から、ソフトマックス関数の微分は、

$$ \frac{\partial \sigma (z_j )}{\partial z_i} = \ – \sigma (z_j) \sigma (z_i) $$

● 出力zに対する損失の勾配

前述の損失の勾配に話を戻します。これは下記の通り(2)式で記述できました。

$$ \frac{\partial L}{\partial z_j} = \sum_{k} \frac{\partial L}{\partial \sigma (z_k) } \cdot \frac{\partial \sigma (z_k)}{\partial \sigma z_j} $$

交差エントロピー誤差の微分である(1)式を用いれば、

$$ \frac{\partial L}{\partial z_j} = \ – \frac{y_k}{\sigma (z_k)} \cdot \frac{\partial \sigma (z_k)}{\partial \sigma z_j} $$

i = j を仮定すれば、(3)式のソフトマックス関数の微分も代入できるので、

$$ \frac{\partial L}{\partial z_j} = \ – \frac{y_k}{\sigma (z_k)} \cdot \sigma (z_j) (1 – \sigma (z_j) ) $$

以上より、損失の勾配、つまりはソフトマックス層の逆伝播は下記のように記述できます。

$$ \frac{\partial L}{\partial z_j} = \sigma (z_j) – y_j $$

逆伝播がとても簡単な式で記述できていますが、これはニューラルネットワークの活性化関数として逆伝播がシンプルな形になるようにソフトマックス関数が設計されたからです。

早速、上記を参考にソフトマックス層を実装してみます。

class SoftmaxCrossEntropy:
    def __init__(self):
        self.output = None
        self.grad_input = None

    def forward(self, logits):
        exp_values = np.exp(logits - np.max(logits, axis=1, keepdims=True))
        self.output = exp_values / np.sum(exp_values, axis=1, keepdims=True)
        return self.output

    def backward(self, y_true):
        self.grad_input = self.output - y_true
        return self.grad_input



■おわりに

今回はソフトマックス層の逆伝播を1から導出してみました。途中でソフトマックス関数の微分に対して異なるクラス( i ≠ j )の場合も考慮しましたが、最終的な損失の逆伝播に対してはこの場合を考慮しませんでした。

これは、交差エントロピー誤差がOne-Hot表現を用いたニューラルネットワークの出力(y)と、ソフトマックス関数の出力に基づいて算出されるからです。

つまり損失関数は正解のクラス( i = j )に対してのみ影響を与えるので、異なるクラスの場合を無視しても実質的に違いがないということです。

ちなみに今回のアプローチももちろん参考文献とは違う導出方法ですが、ソフトマックスに関してはこの数式処理で考えた方が分かりやすいかなと個人的には思います。ソフトマックスから交差エントロピー誤差の流れを追う(もちろん順伝播にも逆伝播にも)のは(イメージしづらいという意味で)少々難しかったです。



■参考文献

  1. Andreas C. Muller, Sarah Guido. Pythonではじめる機械学習. 中田 秀基 訳. オライリー・ジャパン. 2017. 392p.
  2. 斎藤 康毅. ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装. オライリー・ジャパン. 2016. 320p.
  3. ChatGPT. 4o mini. OpenAI. 2024. https://chatgpt.com/
  4. API Reference. scikit-learn.org. https://scikit-learn.org/stable/api/index.html


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です