畳み込みネットワーク・モデルにおける最適化手法と過学習の改善策について



**********************************************************************************
勾配降下法と誤差逆伝播法(backward propagation)のPython 実装
**********************************************************************************


 深層学習の基礎的理論の一つである勾配降下法を説明します。そして、この最適化アルゴリズムをpythonコードで実装してみたいと思います。

 勾配降下法をpythonに実装すると、以下のようになります。

import numpy as np
import matplotlib.pylab as plt

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
   for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fx1 = f(x) 
        
        x[idx] = tmp_val - h 
        fx2 = f(x) 
        grad[idx] = (fx1 - fx2) / (2*h)
        
        
    return grad


def gradient_descent(f, init_x, eta=0.01, ite_num=100):
    x = init_x
    x_history = []

    for i in range(ite_num):
        x_history.append( x.copy() )

        grad = numerical_gradient(f, x)
        x -= eta * grad

    return x, np.array(x_history)


def function_2(x):
    return (x[0] - 1)**2 + (x[1] - 2)**2

init_x = np.array([0.0, 0.0])    

eta = 0.1
ite_num = 20
x, x_history = gradient_descent(function_2, init_x, eta, ite_num)

plt.plot( [-5, 5], [2,2], '--b')
plt.plot( [1,1], [-5, 5], '--b')
plt.plot(x_history[:,0], x_history[:,1], 'o')

plt.xlim(-3.5, 3.5)
plt.ylim(-4.5, 4.5)
plt.xlabel("X0")
plt.ylabel("X1")
plt.show()


このpythonコードを実行すると、探索過程が表示されて、点(1,2)に到達することが確認できます。

 CNNの学習は損失関数\( L(w) \)を最小化する重みパラメータ\( w \)の最小化点を見出す問題です。勾配降下法を用いた学習過程は \[ w^{(j+1)} = w^{(j)} - \eta \frac{\partial L(w)}{\partial w} \] と表現できます。損失関数の偏微分値を計算するために提案された手法の一つが逆伝播法(back propagation)と呼ばれる方法です。CNNの出力は入力サイドからネットワークの順方法に沿って、つまり、左側から右側方向に順次計算を積み上げていきます。偏微分値の計算でネットワークの右側から左側に向かって、つまり、逆方向に計算を積み上げて行く方法を逆伝播法と言います。以下の説明は、数学が不得意な人には難しいかもしれません。スキップして読んでください。

 逆伝播法の基本は合成関数の微分法での連鎖律です。例えば、 \[ z=f(u)=u^2\\ u=g(x,y)=x+y \] とします。このとき、連鎖律 \[ \frac{\partial z}{\partial x} = \frac{\partial f(u)}{\partial u} \frac{\partial g(x,y)}{\partial x} \] が成立します。その結果、 \[ \frac{\partial z}{\partial x} = 2(x+y) \] と計算されます。

 ニューラルネットワークにおける個別層での入力信号の重みを \[ w =(w_1, w_2 ..., w_m) \]とし、入力信号を \[ x =(x_1, x_2,..., x_m) \] として、活性化関数を\( \phi \)とすると、このネットワークの出力は \[ z = \phi(u) \\ u= w_1x_1 + w_2 x_2 + ... + w_m x_m \] と表現されます。このネットワークに上での連鎖率を利用すると、重みパラメータによる偏微分計算は \[ \frac{\partial z}{\partial w_i} = \frac{\partial \phi(u)}{\partial u} \frac{\partial u}{\partial w_i},  i=1,2,...,m \] となります。逆伝播法による偏微分の計算は、ネットワークの終端から上流(左側)に遡上して計算を積み上げること、つまり、ネットワークの終端にある層の活性化関数の偏微分\( \frac{\partial \phi(u)}{\partial u} \)を計算して、その結果を前の層の偏微分の結果\( \frac{\partial u}{\partial w_i} \)に乗じるという手順に従うことを意味します。具体的に、活性化関数を \[ \phi(u) =\frac{1}{1+ \exp (-u)} \] というsigmoid関数とすると、 \[ \frac{\partial \phi(u)}{\partial u} = \frac{\exp (-u)}{(1+ \exp (-u))^2} = (1- \phi(u))\phi(u) \] となることが知られているので、 \[ \frac{\partial z}{\partial w_i} = (1- \phi(u))\phi(u)・x_i,  i=1,2,...,m \] と計算されます。このような個々のネットワークでの計算をCNN全体に適用して、CNN全体での重みパラメータの偏微分値を計算します。 ネットワークの回路図を用いた説明は、スタンフォード大学のサイト http://cs231n.github.io/にあります。

 Sigmoid関数の偏微分の計算をpythonで実装すると、以下のようになります。
import numpy as np

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = 1/(1 + np.exp(-x))
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx


 また、全結合層の逆伝播法による偏微分計算はpythonコードで

class Dense_layer:
    def __init__(self, W, b):
        self.W =W
        self.b = b
        
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        
        return dx


と実装されます。これらのpythonコードの実装については、斎藤康毅著『ゼロから作るDeep Learning』(オライリー・ジャパン社)などの参考書を参照してください。

**** 続きをお楽しみに! ****

Deep Learningのページへ戻る


トップページへ