勾配降下法の計算方法

ニューラルネットワークの学習アルゴリズムのメモ

表記の定義

定義記号(左辺を右辺で定義)  :=
ベクトル: \boldsymbol{x} = \begin{pmatrix} x_1 \\ x_2 \\ \vdots \end{pmatrix}
ベクトルの第i成分:  x_i
ベクトルのスライス:  \boldsymbol{x}_{[i:j]} := \begin{pmatrix} x_i \\ \vdots \\ x_{j-1} \end{pmatrix} \quad (i < j)

行列:  W
行列の第i行目ベクトル : W_{i:}
行列の第j列目ベクトル : W_{: j}
行列の第i行第j列成分:  W_{ij}

ベクトルの内積:  \boldsymbol{x} \cdot \boldsymbol{y} := \sum_{i} x_{i} y_{i}

ベクトルの要素積:   \boldsymbol{x} * \boldsymbol{y} := \begin{pmatrix} x_1 y_1 \\ x_2 y_2\\ \vdots \end{pmatrix}

ベクトルの直積:  \boldsymbol{x} {\otimes} \boldsymbol{y} := \begin{pmatrix} x_1 y_1  & x_1 y_2 & \cdots \\ x_2 y_1 & \ddots \\ \vdots \end{pmatrix}

ベクトルと行列の積:  W \boldsymbol{x} := \begin{pmatrix} W_{1:} \cdot \boldsymbol{x} \\ W_{2:} \cdot \boldsymbol{x}\\ \vdots \end{pmatrix}

ベクトルで偏微分:  \frac{\partial}{\partial \boldsymbol{x}} := \begin{pmatrix} \frac{\partial}{\partial x_1} \\ \frac{\partial}{\partial x_2} \\ \vdots \end{pmatrix}
行列で偏微分:  \frac{\partial}{\partial W} :=  \begin{pmatrix} \frac{\partial}{\partial W_{11}} & \frac{\partial}{\partial W_{12}} & \cdots \\ \frac{\partial}{\partial W_{21}} & \ddots \\ \vdots \end{pmatrix}

ベクトル関数\boldsymbol{f}:  \boldsymbol{f}(\boldsymbol{x}) := \begin{pmatrix} f(x_1) \\ f(x_2) \\ \vdots \end{pmatrix} (各成分の関数自体は等しい)

ネットワークの構造

ネットワークは層構造を成していて、特に最初の層を「入力層」、最後の層を「出力層」、それ以外を「隠れ層」と呼ぶ。

計算するにあたり、今回は任意の層1つに注目して次の値を定義する。

層の入力ベクトル: \boldsymbol{x}
層のウェイト: W
層のバイアス: \boldsymbol{b}
層の中間生成ベクトル: \boldsymbol{y} := W\boldsymbol{x} + \boldsymbol{b}
層の活性化関数 : \boldsymbol{f}
層の出力ベクトル:  \boldsymbol{z} := \boldsymbol{f}(\boldsymbol{y})

ウェイト・バイアスの修正方法

今回提示する修正法は、ニューラルネットの出力が期待される値とどれくらいずれているかの指標である損失関数 Eが小さくなるようにウェイト・バイアスを修正する勾配降下法である。
すなわち、

 W \leftarrow W -\alpha\frac{\partial E}{\partial W}
 \boldsymbol{b}  \leftarrow  \boldsymbol{b} -\alpha\frac{\partial E}{\partial \boldsymbol{b}}

という式をもってウェイトWとバイアス\boldsymbol{b}を更新する。
ここで、\alphaは学習率であり、勾配をどれだけ下るかの”重み”である。

計算

求めたいものは \frac{\partial E}{\partial W}だが、微分合成がどうなっているのかよくわからないため、まずその成分での微分 \frac{\partial E}{\partial W_{ij}}を考える。


 \begin{align} 
\frac{\partial E}{\partial W_{ij}} &= \frac{\partial E}{\partial \boldsymbol{y}} \cdot \frac{\partial \boldsymbol{y}}{\partial W_{ij}} \\
&=\frac{\partial E}{\partial \boldsymbol{y}} \cdot \frac{\partial}{\partial W_{ij}} \begin{pmatrix} W_{1:} \cdot \boldsymbol{x} + b_1\\ W_{2:} \cdot \boldsymbol{x} + b_2 \\ \vdots \end{pmatrix}  \quad (\boldsymbol{y}を展開)\\
&=\frac{\partial E}{\partial \boldsymbol{y}} \cdot \begin{pmatrix} 0 \\ \vdots \\ x_j \\ \vdots \\ 0 \end{pmatrix} \quad (偏微分によって第i成分以外ゼロになる) \\
&= \frac{\partial E}{\partial y_i}x_j \\
&= \frac{\partial E}{\partial \boldsymbol{z}} \cdot \frac{\partial \boldsymbol{z}}{\partial y_i}x_j \\
&= \frac{\partial E}{\partial \boldsymbol{z}} \cdot \frac{\partial \boldsymbol{f}(\boldsymbol{y})}{\partial y_i}x_j \\
&= \frac{\partial E}{\partial \boldsymbol{z}} \cdot \begin{pmatrix} 0 \\ \vdots \\ f'(y_i) \\ \vdots \\ 0 \end{pmatrix} x_j  \quad (偏微分によって第i成分以外ゼロになる)\\
&= \frac{\partial E}{\partial z_i} f'(y_i) x_j \\
\end{align}

したがって、行列表示に直すと、


 \frac{\partial E}{\partial W} = (\frac{\partial E}{\partial \boldsymbol{z}} * \boldsymbol{f'}(\boldsymbol{y})) \otimes \boldsymbol{x}

(見づらいが\boldsymbol{z}はベクトルである。)

出力層である場合

この層が出力層である場合、損失関数Eは出力層の出力\boldsymbol{z}で計算されるため具体的に計算することができる。
簡単なニューラルネットでは\frac{\partial E}{\partial \boldsymbol{z}}を簡単にするために損失関数Eを、


 \frac{\partial E}{\partial \boldsymbol{z}} = \boldsymbol{z} - \boldsymbol{t}  \quad (\boldsymbol{t}:期待される出力)

となるように決められていたりする。
計算すると、その場合の損失関数は二乗誤差関数であるとわかる。

隠れ層である場合

この層の出力\boldsymbol{z}は次の層の入力ベクトルである。
そこで、次の層の種々の値をチルダ(\tilde{\cdot})を付けて表すことにする。
つまり、


 \boldsymbol{z} = \tilde{\boldsymbol{x}}

したがって、


 \begin{align}
\frac{\partial E}{\partial z_i} &= \frac{\partial E}{\partial \tilde{x}_i} \\
&= \frac{\partial E}{\partial \tilde{\boldsymbol{y}}} \cdot \frac{\partial \tilde{\boldsymbol{y}}}{\partial \tilde{x}_i} \\
&= \frac{\partial E}{\partial \tilde{\boldsymbol{y}}} \cdot \frac{\partial}{\partial \tilde{x}_i} \begin{pmatrix} \tilde{W}_{1:} \cdot \tilde{\boldsymbol{x}} + \tilde{b}_1 \\ \tilde{W}_{2:} \cdot \tilde{\boldsymbol{x}} + \tilde{b}_2 \\ \vdots \end{pmatrix} \\
& = \frac{\partial E}{\partial \tilde{\boldsymbol{y}}} \cdot \tilde{W}_{: i} \quad (全成分を偏微分した結果) \\
&= (\frac{\partial E}{\partial \tilde{\boldsymbol{z}}} * \tilde{\boldsymbol{f'}}(\tilde{\boldsymbol{y}})) \cdot \tilde{W}_{\cdot i} \quad 
\end{align}

最後の式変形は"計算"の途中式を追うと、\frac{\partial E}{\partial y_i} = \frac{\partial E}{\partial z_i} f'(y_i)であることから。

この層の次のレイヤーが出力層だとすると、\frac{\partial E}{\partial \tilde{\boldsymbol{z}}} * \tilde{\boldsymbol{f'}}(\tilde{\boldsymbol{y}}) は計算出来て、もちろん \tilde{W}も既知だから \frac{\partial E}{\partial z_i}が求まって、行列表示としてまとめると、


 \frac{\partial E}{\partial W} = (\frac{\partial E}{\partial \tilde{\boldsymbol{y}}} \tilde{W} * \boldsymbol{f'}(\boldsymbol{y})) \otimes \boldsymbol{x}

これを繰り返すことで全ての隠れ層について、その勾配 \frac{\partial E}{\partial W}を計算することができる。

バイアスについて


 \begin{align} 
\frac{\partial E}{\partial b_{i}} &= \frac{\partial E}{\partial \boldsymbol{y}} \cdot \frac{\partial \boldsymbol{y}}{\partial b_{i}} \\
&=\frac{\partial E}{\partial \boldsymbol{y}} \cdot \frac{\partial}{\partial b_{i}} \begin{pmatrix} W_{1:} \cdot \boldsymbol{x} + b_1\\ W_{2:} \cdot \boldsymbol{x} + b_2 \\ \vdots \end{pmatrix} \\
&=\frac{\partial E}{\partial \boldsymbol{y}} \cdot \begin{pmatrix} 0 \\ \vdots \\ 1  \\ \vdots \\ 0 \end{pmatrix} \quad (第i成分だけ残る)\\
&= \frac{\partial E}{\partial y_i} \\
&= \frac{\partial E}{\partial z_i} f'(y_i) \\
\end{align}

したがって、

 \frac{\partial E}{\partial \boldsymbol{b}} = \frac{\partial E}{\partial \boldsymbol{z}} * \boldsymbol{f'}(\boldsymbol{y})

ウェイトの場合と同様に \frac{\partial E}{\partial \boldsymbol{z}}を出力層から遡って計算すればよい。

numpyでの実装

import numpy as np

ベクトル: \boldsymbol{x} → 一次元配列x(例:np.array([...])
行列:W → 二次元配列W(例:np.array([[...] ... ])

ベクトルのスライス: x[k:l]
行列の第i行目ベクトル : W[i, :]
行列の第j列目ベクトル : W[:, j]
行列の第i行第j列成分: W[i, j]

ベクトルの内積: x @ y
ベクトルの要素積: x * y
ベクトルの直積: np.outer(x, y)
ベクトルと行列の積: W @ x