初心者データサイエンティストの備忘録

調べたことは全部ここに書いて自分の辞書を作る

【読書記録】なっとく!ディープラーニング_第6章

目次

はじめに

 現在ニューラルネットワークは多くの分野で使われています。私の場合、セキュリティ上の課題を解決する手段としてニューラルネットワークを用いています。
 このニューラルネットワークですが、モデルの表現性を高めるために層を重ねることが必要不可欠となっています。層を重ねたニューラルネットワークは、推定対象のパラメータが多く、前回記事で用いた方法でパラメータを推定することができません。
 以上のような背景のもとで生まれた技術が誤差逆伝播法です。誤差逆伝播法は合成関数の微分の公式を用いることで、全ての層のパラメータの推定を可能にします。
 本記事では、3層構造のニューラルネットワークを題材に、誤差逆伝播法がどのような仕組みになっているのか、数式とJuliaのコードを示すことで説明しようと思います。
 なお、本記事はなっとく!ディープラーニングの第6章に沿って書いております。問題設定等をより詳しく知りたい方は本書をご覧ください。

勾配降下法のおさらい

 勾配降下法の基本式は、


\omega _ {(n+1)} = \omega _ {(n)} - \eta \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )

です。ただし、\omega _ {(n+1)}は更新後のパラメータ、\omega _ {(n)}は更新前のパラメータ、\etaは学習率、Lは損失関数です。 \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )によって更新の方向が、 \etaによって更新の大きさが決まります。なお、損失関数は二乗誤差とします。

複数層のニューラルネットワーク

 図1のような3層構造のニューラルネットワークを考えます。

図1:3層構造のニューラルネットワーク

このとき、入力層のベクトルを横ベクトル \boldsymbol{x} = (x_1, x_2, x_3)とします。また、入力層から中間層の変換を


\boldsymbol{u} = (u_1, u_2, u_3, u_4) = \boldsymbol{x} W_{01}

とします。\boldsymbol{x}1\times3のベクトル、W_{01}3\times 4の行列です。
 次に、\boldsymbol{u}の各成分を活性化関数Reluに入力し、


\boldsymbol{z} = ({\rm Relu}(u_1), {\rm Relu}(u_2), {\rm Relu}(u_3), {\rm Relu}(u_4))

と変換します。ただし、


{\rm Relu}(x) = \left\{
\begin{array}{l}
x &(x>0) \\
0 & (x \leq 0)
\end{array}
\right.

とします。
 最後に、中間層から出力層への変換を


\hat{y} = \boldsymbol{z} W_{12}

とします。\hat{y}スカラーW_{12}4 \times 1の行列です。

誤差逆伝播

 ここでは、勾配降下法を使いW _ {01}を推定する方法を紹介します。W_{12}を推定する方法については、過去の記事 aisinkakura-datascientist.hatenablog.com で紹介しているので、興味があればご覧ください。

一つの成分に対する更新式

 「勾配降下法のおさらい」に書いた通り、パラメータを推定するためには損失関数をパラメータで微分した式が必要となります。今回はわかりやすいように、


W_{01} = \left(\begin{array}{cccc}
w_{11} & w_{12} & w_{13} & w_{14} \\
w_{21} & w_{22} & w_{23} & w_{24} \\
w_{31} & w_{32} & w_{33} & w_{34} 
\end{array}\right)

とし、代表として w _ {23}を推定するための更新式を導こうと思います。
 更新式のポイントは、\dfrac{\partial L}{\partial w _ {23}}を計算することです。この式は、合成関数の微分の公式を使うことで次の様に分解できます。


\dfrac{\partial L}{\partial w_{23}} = \dfrac{\partial u_3}{\partial w_{23}} \dfrac{\partial z_3}{\partial u_{3}} \dfrac{\partial \hat{y}}{\partial z_{3}}\dfrac{\partial L}{\partial \hat{y}}

 分解した式を一つずつ計算していきます。まず、 \dfrac{\partial u _ 3}{\partial w _ {23}}を計算します。\boldsymbol{u} = \boldsymbol{x} W_{01}より、


u_3 = w_{13}x_1+w_{23}x_2+w_{33}x_3

となることから、


\dfrac{\partial u _ 3}{\partial w _ {23}} = x_2

となります。次に\dfrac{\partial z _ 3}{\partial u _ {3}}について、 z_3 = {\rm Relu}(u_3)より、


\dfrac{\partial z_3}{\partial u_3} = \left\{
\begin{array}{l}
1 &(u_3>0) \\
0 & (u_3 \leq 0)
\end{array}
\right.

となります。なお、この右辺の式を今後は{\rm ReluDeriv}(\cdot)と呼びます。
 さらに、\dfrac{\partial \hat{y}}{\partial z _ 3}を計算します。


W_{12} = \left(\begin{array}{cccc}
w_1 & w_2 & w_3 & w_4
\end{array}\right)

とすると、\hat{y} = \boldsymbol{z} W _ {12}より、


\hat{y} = z_1 w_1 + z_2 w_2 + z_3 w_3 + z_4 w_4

となります。したがって、


\dfrac{\partial \hat{y}}{\partial z _ 3} = w_3

となります。
 最後に、\dfrac{\partial L}{\partial \hat{y}}を計算します。 L = (\hat{y}-y) ^ 2なので、


\dfrac{\partial L}{\partial \hat{y}} = 2(\hat{y}-y)

となります。
 以上をまとめると、


\dfrac{\partial L}{\partial w_{23}} \propto x_2 {\rm ReluDeriv}(u_3)w_3(\hat{y}-y)

となります。なお、\dfrac{\partial L}{\partial \hat{y}}の係数の2は学習率に吸収すれば良いです。そのため、係数の2は消して等号=を比例記号\proptoに置き換えています。
 上記を踏まえ、一般の w _ {ij}によるL微分を計算すると下記のようになります。


\dfrac{\partial L}{\partial w_{ij}} \propto x_i {\rm ReluDeriv}(u_j)w_j (\hat{y}-y)

全ての成分に対する更新式

 前節で


\dfrac{\partial L}{\partial w_{ij}} \propto x_i {\rm ReluDeriv}(u_j)w_j (\hat{y}-y)

となると書きました。これを基に \dfrac{\partial L}{\partial W _ {01}}を計算すると、下記のようになります。


\begin{eqnarray}
\dfrac{\partial L}{\partial W _ {01}} &\propto& (\hat{y}-y) \left(\begin{array}{cccc}
x_1 {\rm RD}(u_1)w_1 & x_1 {\rm RD}(u_2)w_2 & x_1 {\rm RD}(u_3)w_3 & x_1 {\rm RD}(u_4)w_4 \\
x_2 {\rm RD}(u_1)w_1 & x_2 {\rm RD}(u_2)w_2 & x_2 {\rm RD}(u_3)w_3 & x_2 {\rm RD}(u_4)w_4 \\
x_3 {\rm RD}(u_1)w_1 & x_3 {\rm RD}(u_2)w_2 & x_3 {\rm RD}(u_3)w_3 & x_3 {\rm RD}(u_4)w_4 \\
\end{array}\right) \\
&=& (\hat{y}-y)\boldsymbol{x}^\top \left\{ W_{12}^\top \times ({\rm RD(u_1), \rm RD(u_2), \rm RD(u_3), \rm RD(u_4)})\right\}
\end{eqnarray}

 なお、RDとはReluDerivのこと、行列間の\timesは各成分の積を表します。

コードに落とし込む

 以上のことをJuliaで書いたコードが下記です。W_0_1_delta \dfrac{\partial L}{\partial W _ {01}}です。

using LinearAlgebra, Statistics, Random
"""
データ
"""
# 特徴量
streetlights = [
    1 0 1
    0 1 1
    0 0 1
    1 1 1
]

# 目的変数
walk_vs_stop = [
    1
    1
    0
    0
]

"""
パラメータ
"""
# 学習率
α = 0.2

# 入力層のサイズ
input_size = size(streetlights[1, :])[1]

# 隠れ層のサイズ
hidden_size = 4

# 出力層のサイズ
output_size = 1

# ランダムに初期化された3つの層を結合するパラメータの初期値
#W_0_1 = 2*rand(input_size, hidden_size).-1
#W_1_2 = 2*rand(hidden_size, output_size).-1
Random.seed!(1)
W_0_1 = 2*rand(input_size, hidden_size).-1
W_1_2 = 2*rand(hidden_size).-1

"""
関数
"""
relu(x) = (x>0)*x

relu2deriv(output) = output>0


"""
誤差逆伝播法
"""
# MSEを保存するためのリストを作成
mse_list = []

for iteration=1:60
    for index=1:size(walk_vs_stop)[1]
        # 順伝播
        layer_0 = streetlights[index, :]'
        layer_1_1 = layer_0*W_0_1 # 中間層の活性化関数に入れる前
        layer_1_2 = relu.(layer_1_1) # 中間層の活性化関数に入れた後
        layer_2 = layer_1_2*W_1_2
        
        

        # 更新式の差分
        ## layer_2のパラメータの更新式の差分
        ### layer_2ではなく、layer_2[1]としているのは、layer2が(1, 1)の行列になっているから
        layer_2_delta = layer_2[1]-walk_vs_stop[index]
        W_1_2_delta = α*layer_2_delta*layer_1_2'
        ## layer_1のパラメータの更新式の差分
        W_0_1_delta = α*layer_2_delta*layer_0'*(W_1_2'.*relu2deriv.(layer_1_1))
        

        # 更新
        W_1_2 = W_1_2-W_1_2_delta
        W_0_1 = W_0_1-W_0_1_delta
    end
    
    # MSE(Mean Square Error)の計算
    ## 全てのデータに対して順伝播を行う
    Layer_1_1 = streetlights*W_0_1 # 中間層の活性化関数に入れる前
    Layer_1_2 = relu.(Layer_1_1) # 中間層の活性化関数に入れた後
    pred_list = Layer_1_2*W_1_2
    

    mse = mean((pred_list-walk_vs_stop).^2)
    push!(mse_list, mse)
    println("Error:", mse)
    println("pred_list:", pred_list)
    
end

 図2は学習が進むにつれ平均二乗誤差(Mean Squared Error:MSE)がどのように減少するのかを示した図です。学習が進むにつれ、MSEが減少していることがわかります。このように、誤差逆伝播法によって正しい予測ができるニューラルネットワークを構築できることがわかります。

図2:イテレーションごとのMSEの変化

 なお、図2を描画するのに用いたコードは下記の通りです。

import Pkg
Pkg.add("PyPlot")
using PyPlot

# 折れ線グラフを描画
plot(1:size(mse_list)[1], mse_list)
xlabel("iteration")
ylabel("MSE")

まとめ

 本記事では、3層構造のニューラルネットワークを題材にして誤差逆伝播法について説明しました。また、Juliaを用いて誤差逆伝播法を実装し、ニューラルネットワークに含まれるパラメータを推定しました。
 本記事が誤差逆伝播法の理解促進につながれば幸いです。