【機械学習】誤差逆伝播法による速度改善（その2）

前回の[ブログ](http://qiita.com/m-hayashi/items/c5ace811333364d887f5)では、手書き文字データの速度改善を行うにあたって、誤差逆伝播法に触れました。

その中では計算グラフや連鎖率のお話、また加算ノードや乗算ノードでの逆伝播がどのようなオペレーションになるのか確認しました。

今回はその続きで、ニューラルネットワークでの以下の演算ノードが、逆伝播でどのような処理になるのかを確認します。

・シグモイド関数（活性化関数）
・Softmax関数
・交差エントロピー誤差（損失関数）
・Affine変換（行列の内積とバイアスの和）

```math
\def\textlarge#1{%
  {\rm\Large #1}
}
\def\textsmall#1{%
  {\rm\scriptsize #1}
}
```

# シグモイド関数の逆伝播
シグモイド関数は以下のような形の関数でした。

```math
y = \frac{1}{1+e^{-x}}
```

これを計算グラフで表現してみます。
シグモイド関数自体が、加算、除算、指数計算の複合計算なので、計算ノードが複数組み合わさったものになります。
順伝播は以下のような形です。

![sigmoid_graph.png](https://qiita-image-store.s3.amazonaws.com/0/134453/6efaaa0f-aeb2-57a4-7205-57054ace6097.png)

逆伝播ですが、まずは最後の除算ノードに着目します。
除算は、y=1/x という演算処理なので、偏微分は以下の計算になります。

```math
\frac{\partial y}{\partial x} = -\frac{1}{x^2} = -y^2
```

その手間の3つめの加算ノードは前回お話ししたとおり、そのまま値を流すだけです。

その次の指数計算「exp」ノードについては、偏微分計算は以下の通りです。

```math
\frac{\partial y}{\partial x} = e^x
```

つまり、この場合はexp(-x)をかけて逆伝播します。

最後の逆伝播は一番手前の乗算ノードになりますが、前回お話しした通り、
入力ノードをひっくり返して掛ければ良いので、この場合は-1をかけてやれば良いことになります。

整理すると以下のような計算グラフになります。
![sigmoid_graph2.png](https://qiita-image-store.s3.amazonaws.com/0/134453/b0b4c8e1-398a-9fcf-6a0d-8862d304b02a.png)

逆伝播の最後の出力は、以下のように表現を変えることができます。

```math
y^2e^{-x}\frac{\partial E}{\partial y}=y\frac{e^{-x}}{1+e^{-x}}\frac{\partial E}{\partial y}= y(1-y)\frac{\partial E}{\partial y} 
```

なので、シグモイド関数のノードでは、順伝播で出力された値yに対して、y（1-y）倍した値を
逆伝播で渡してやれば良いことになります。
![sigmoid_graph3.png](https://qiita-image-store.s3.amazonaws.com/0/134453/6b7e32ce-884f-a01f-6729-96e465b67384.png)

# Softmax関数の逆伝播
Softmax関数はこのような形をしていました。

```math

y_{k}=σ(k)=\frac{e^{x_{k}}}{\sum_{i=0}^9(e^{x_{i}})}

```

計算グラフは、このような形になります。
![softmax_graph5.png](https://qiita-image-store.s3.amazonaws.com/0/134453/df1628a2-c2da-8c03-c231-1d7bc5adfeb6.png)

逆伝播については、若干複雑です。
今まで出てきた加算/乗算/除算/指数のノードを組み合わせるとできるわけですが、結果から先に記載すると、インデックスk番目に着目した計算結果は以下の通りです。

```math
y_{k}(\frac{\partial E}{\partial y_{k}}-{\sum_{i=0}^9 y_{i}\frac{\partial E}{\partial y_{i}}})
```

逆伝播のフローを赤線で記載すると以下のような計算結果になります。
![softmax_graph6.png](https://qiita-image-store.s3.amazonaws.com/0/134453/3efafef7-ab18-e5cb-118a-22527a5eea45.png)

入出力だけに着目して表現するとSoftmaxノードは以下のような表現になります。
![softmax_graph4.png](https://qiita-image-store.s3.amazonaws.com/0/134453/011089b2-87ce-ac16-4649-cfada739d13d.png)


Softmaxの逆伝播の形は、この後に説明する「交差エントロピー誤差」の逆伝播と組み合わせると、実は非常にスッキリした形になります。
話を進めていきます。

# 交差エントロピー誤差関数の逆伝播
交差エントロピー誤差の関数はこのような形をしていました。

```math

E=\sum_{n=1}^N(-log(y_{n,t_n}))

```

この表現は、n番目の入力データをx$\textsmall{n}$とし、それに対する正解データをt$\textsmall{n}$（=0,1,2,...,9）、
そしてその時に出力層からの10個の数値y$\textsmall{n,k}$（k=0,1,2,...9）で定義したときの形でした。

上記はN個のバッチデータを扱う表現ですが、逆伝播を考えるときは、1つの入力データを入力したケースを考えます。
つまり、入力データをx（ベクトル）、 出力データをy（ベクトル）、正解データをt（スカラー）としたときの表現は以下のようになります。

```math

E=-log(y_{t})

```

ここでの正解データtはスカラー値ですが、正解のインデックスが1で他は0という「one-hot表現」ベクトルを用いてみます。

たとえば、正解が3である場合には、正解データtは以下のようなベクトル値で表現します。

```math

t=(0,0,0,1,0,0,0,0,0,0)

```

one-hot表現を用いた場合の、交差エントロピー誤差は以下のような表現となります。

```math
E=\sum_{k=0}^9(-t_{k}log(y_{k}))
```

対数関数の微分は以下の通りです。

```math

\frac{d}{d x} ({log(x)}) = \frac{1}{x}

```

計算グラフは以下のようなフローになります。
![cross-entropy-error_graph3.png](https://qiita-image-store.s3.amazonaws.com/0/134453/e6169718-8836-9f33-65e6-5881fa15fceb.png)



つまり、インデックスkでの逆伝播の出力は以下の通りです。

```math

-\frac{t_{k}}{y_{k}}

```

「交差エントロピー誤差」ノードという括りで見たときには、インデックスkの入出力は以下のような表現になります。
![cross-entropy-error_graph2.png](https://qiita-image-store.s3.amazonaws.com/0/134453/473505a3-47e0-2fff-7a35-a3b17f119c94.png)

#「Softmaxノード」と「交差エントロピー誤差ノード」の逆伝播結合
上記で計算した、「Softmaxノード」と「交差エントロピー誤差ノード」の逆伝播を結合してみます。

つまり、計算としては「Softmaxノード」の逆伝播の以下の結果に対して、

```math
y_{k}(\frac{\partial E}{\partial y_{k}}-{\sum_{i=0}^9 y_{i}\frac{\partial E}{\partial y_{i}}})
```
これを以下の置換を行うことで計算できます。

```math
\frac{\partial E}{\partial y_{k}}　→　-(\frac{t_{k}}{y_{k}})\frac{\partial E}{\partial y_{k}}\\
\frac{\partial E}{\partial y_{i}}　→　-(\frac{t_{i}}{y_{i}})\frac{\partial E}{\partial y_{i}}
```

つまり、「交差エントロピー誤差ノード」の逆伝播の出力を、「Softmaxノード」の入力値に与えてやります。

あと、one-hot表現している正解データtについては、k番目の要素が1で他は
0であることを注意して、計算してみると以下のようになります。

```math
y_{k}\bigl((-\frac{t_{k}}{y_{k}})\frac{\partial E}{\partial y_{k}}-{\sum_{i=0}^9 y_{i}(-\frac{t_{i}}{y_{i}})\frac{\partial E}{\partial y_{i}}}\bigl) \\
=-t_{k}\frac{\partial E}{\partial y_{k}}+y_{k}\sum_{i=0}^9 t_{i}\frac{\partial E}{\partial y_{i}} \\
=-t_{k}\frac{\partial E}{\partial y_{k}}+y_{k}\frac{\partial E}{\partial y_{k}} \\
=(y_{k}-t_{k})\frac{\partial E}{\partial y_{k}}

```

上記の結果のとおり、「Softmaxノード」と「交差エントロピー誤差ノード」を結合した場合の、逆伝播は(y$\textsmall{k}$-t$\textsmall{k}$)倍して渡してやるだけという非常にシンプルな結果になりました。

実は、このような逆伝播の結果がシンプルな形になるように、Softmax関数、交差エントロピー誤差関数は設計されています。

プログラムを組む際のアルゴリズムも、この2つを1つのノードとして扱った方がシンプルになります。

ノードのイメージはこのような形になります。
![cross-entropy-error_graph5.png](https://qiita-image-store.s3.amazonaws.com/0/134453/d2af7337-4434-af02-bc99-144443a44b7a.png)

#Affine変換の逆伝播
最後にAffine変換（行列の内積とバイアスの和）のノードの逆伝播を紹介します。

入力層をX、重み付けパラメータをW、バイアスをBとしたとき、出力Yは以下のような
演算であることを[以前のブログ](http://qiita.com/m-hayashi/items/3f2b83a31961c4c8fe8a)で説明いたしました。

```math
Y=X・W+B
```

上記のような行列の内積と和から成る演算がAffine変換ですが、これを計算グラフで表すと以下のようになります。
![affine_graph.png](https://qiita-image-store.s3.amazonaws.com/0/134453/58740fc9-4e11-0e4f-a25e-24aa678e1342.png)

内積計算は「dot」ノードで表現しています。

入力層X、重み付けパラメータWに対応する逆伝播の計算ですが、計算の詳細は割愛しますが、
結果は以下のような形となります。

```math
\frac{\partial E}{\partial X} = \frac{\partial E}{\partial Y}・W^T \\
\frac{\partial E}{\partial W} = X^T・\frac{\partial E}{\partial Y}
```

形としてはこちらも非常にシンプルな演算式となっています。

XとWの右上の「T」は転置行列であることを意味しています。
また、ここで∂E/∂X、∂E/∂W、∂E/∂Yなどは要素での偏微分の行列を表しています。
たとえば、∂E/∂Xについては以下のような形の勾配ベクトルになります。

```math
\frac{\partial E}{\partial X}=(\frac{\partial E}{\partial X_{0}},\frac{\partial E}{\partial X_{1}},...)

```

逆伝播を加筆した計算グラフは以下の通りです。
![affine_graph2.png](https://qiita-image-store.s3.amazonaws.com/0/134453/f7506c58-a5fa-81e5-0f35-bfebed0eb185.png)


***
前回と今回のブログで、計算グラフという考えかたを導入して、
ニューラルネットワークの入力層から損失関数計算までの
各フェーズでの逆伝播を確認しました。

この議論の最初に、アルゴリズムの速度改善のためには、損失関数Eの最小値を、如何に早く計算するかが肝になってとお話ししましたが、
実は、損失関数として導入している「交差エントロピー誤差」自体が、
逆伝播で最小値を早く計算するために設計されています。

また、各計算ノードでの逆伝播はシンプルな局所計算で実現でき、
それらを組み合わせることで、速度改善につながるところも垣間見えます。

-----
参考文献：[『ゼロから作るDeep Learning』](https://www.oreilly.co.jp/books/9784873117584/)