end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

ニューラルネットワークにおける損失関数と勾配(偏微分)

GitHub - oreilly-japan/deep-learning-from-scratch: 『ゼロから作る Deep Learning』(O'Reilly Japan, 2016)

「ゼロから作るDeep Learning ① (Pythonで学ぶディープラーニングの理論と実装)」 p.89~108 の写経です。

目次

損失関数としての 交差エントロピー誤差

 \large{ E = - \sum_k t_k \log y_k }
ただし、  \large{ t_k : 正解データ、y_k : 実際の出力データ}

交差エントロピー誤差は、上記の通りですが、 複数データの「ミニバッチ学習」を行う場合、以下のようになります。

「ミニバッチ学習」とは、データの1つ1つでパラメータを更新するのではなく、 いくつかのデータをまとめて入力し、それぞれの勾配を計算し、 その勾配の平均値を用いてパラメータを更新する方法です。

 \large{ E = - \frac{1}{N} \sum_n \sum_k t_{nk} \log y_{nk} }
ただし、  \large{ t_{nk} : n番目のデータのk番目要素の正解データ \\\
                       y_{nk} : n番目のデータのk番目要素の実際の出力データ}

上記を numpy for pythonで実装する場合、p94に記載の以下のようになります。

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    # 正解dataがone-hot-vectorの場合の変換
    if t.size == y.size:
        t = t.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

損失関数を数値微分し、勾配算出

単純?な数値に対しての数値微分と、python実装は以下の通りです

 \large{
  \frac{ d (x) }{ dx } =  \lim_{h \to 0} \frac{ f(x+h)-f(x)}{h}
}

def numerical_diff(f, x):
    h = 1e-4 # 0.0001
    return (f(x+h) - f(x-h)) / (2*h)

※ fは損失関数、xは入力値

更には、 \large{x_0、x_1}のように、 複数の値を持つベクトルに対し、  \large{ \frac{\partial f}{\partial x_0}、\frac{\partial f}{\partial x_1} } のような偏微分を行う場合、p104にあるように実装します。

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 値を元に戻す
        it.iternext()   
        
    return grad

勾配降下法による最小値算出

勾配降下法では、以下の数式で、次の \large{x_0、x_1}を算出します。

 \large{
  x_0 ← x_0 - \eta \frac{\partial f}{\partial x_0} \\\
  x_1 ← x_1 - \eta \frac{\partial f}{\partial x_1}
}

※ ηは、前もって決めておく学習率で、例えば、0.01や0.001

これをpythonで実装する場合、p.107の以下のようになります

def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    x_history = []

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

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

    return x, np.array(x_history)