Deep Learning

ニューラルネットワークの学習


データからの学習

「学習」とは「訓練データから最適な重みパラメータの値を自動で獲得すること」。 「損失関数」の値が最も小さくなる重みパラメータを探すために「勾配法」を用いる。

機械学習では2通りのアプローチがある。ディープラーニングは後者の例である。

損失関数

損失関数 (loss function) はニューラルネットワークの「性能の悪さ」を表す基準である。 任意の関数を用いることができるが、一般には2乗和誤差や交差エントロピー誤差を用いる。

2乗和誤差

2乗和誤差 (mean squared error) の式は、$k$ をデータの次元数、 $y_k$ をニューラルネットワークの出力、 $t_k$ を教師データとして次の通り。

$\displaystyle \begin{equation} E = \frac{1}{2} \Sigma_{k} (y_k - t_k)^2 \end{equation}$
mean_squared_error.py
def mean_squared_error(y, t):
  return 0.5 * np.sum((y - t)**2)

交差エントロピー誤差

交差エントロピー誤差 (cross entropy error) の式は、$k$ をデータの次元数、 $y_k$ をニューラルネットワークの出力、 $t_k$ を正解ラベルとして次の通り。$t_k$ は one-hot 表現(正解だけが1で他は0の値をとる)されているものとする。

$\displaystyle \begin{equation} E = - \Sigma_{k} t_k \log y_k \end{equation}$
cross_entropy_error.py
def cross_entropy_error(y, t):
  delta = 1e-7
  return -np.sum(t * np.log(y + delta))

ミニバッチ学習

損失関数は、すべての訓練データを対象として求める必要がある。 たとえば、訓練データが100個ある場合は、その100個の損失関数の和が指標となる。

データが $N$ 個あり、$t_{nk}$ を $n$ 個目のデータの $k$ 次元目の値を表すとすると、 交差エントロピー誤差 (cross entropy error) の式は次の通り。 データの個数 $N$ で割って正規化している。

$\displaystyle \begin{equation} E = - \frac{1}{N} \Sigma_{n} \Sigma_{k} t_{nk} \log y_{nk} \end{equation}$

勾配法

勾配法 (gradient method) には 勾配降下法 ()gradient descent method) と 勾配上昇法 (gradient ascent method) がある。 損失関数の符号を逆転させれば同じになるので大きな問題ではない。

数値が2次元 ( $x_0$, $x_1$ )の場合の勾配法は次の式であたえられる。

$\displaystyle \quad\quad x_0 = x_0 - \eta \frac{\partial f}{\partial x_0} \quad\quad\quad (4.7) \\ \displaystyle \quad\quad x_1 = x_1 - \eta \frac{\partial f}{\partial x_1} $

式 (4.7) の $\eta$ は更新の量を表し、学習率 (learning rate) と呼ばれる。

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

gradient_descent.py
def gradient_descent(f, init_x, lr=0.01, step_num = 100):
  x = init_x
  for i in range(step_num):
    grad = numerical_gradient(f, x)
    x -= lr * grad
  return x

ニューラルネットワークに対する勾配

2x3の重み $W$ だけを持つニューアッルネットワークを考え、損失関数を $L$ とする。

$\displaystyle \begin{equation} W = \left( \begin{matrix} w_{11} & w_{21} & w_{31} \\ w_{12} & w_{22} & w_{32} \\ \end{matrix} \right) \\ \frac{\partial L}{\partial W} = \left( \begin{matrix} \frac{\partial L}{\partial w_{11}} & \frac{\partial L}{\partial w_{21}} & \frac{\partial L}{\partial w_{31}} \\ \frac{\partial L}{\partial w_{12}} & \frac{\partial L}{\partial w_{22}} & \frac{\partial L}{\partial w_{32}} \end{matrix} \right) \end{equation}$
gradient_simplenet.py
import sys, os
sys.path.append(pardir)
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient

class simpleNet:
  def __init__(self):
    self.W = np.random.randn(2,3)

  def predict(self, x):
    return np.dot(x, self.W)

 def loss(self, x, t):
    z = self.predict(x)
    y = sotmax(z)
    loss = cross_entropy_error(y, t)
    return loss


学習アルゴリズムの実装

ニューラルネットワークには適応可能な重みとバイアスがあり、 この重みとバイアスを訓練データに適するように調整することを「学習」と呼ぶ。 ミニバッチ(訓練データからランダムにデータの一部をミニバッチとして選ぶ。) -> 勾配の算出(ミニバッチに対して損失関数を最も現象させる勾配を求める。) -> パラメータの更新(重みパラメータを勾配方向に微小量だけ更新する) を繰返すことで学習が行われる。

ミニバッチとしてランダムに選択されたデータを使用することから、確率的勾配降下法 (Stochastic Gradient Descent, SGD) と呼ばれる。

2層ニューラルネットワークのクラス

two_layer_net.py
import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient

class TwoLayerNet:
  def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
    self.params = {}
    self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
    self.params['b1'] = np.zeros(hidden_size)
    self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
    self.params['b2'] = np.zeros(output_size)

  def predict(self, x):
    W1, W2 = self.param['W1'], self.param['W2']
    b1, b2 = self.param['b1'], self.param['b2']
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    y = softmax(a2)
    return y

  def loss(self, x, t):   # x: input data, t: training data
    y = self.predict(x)
    return cross_entropy_error(y, t)

  def accuracy(self, x, t):
    y = predict(x)
    y = np.argmax(y, axis=1)
    t = np.argmax(t, axis=1)
    accuracy = np.sum(y == t) / float(x.shape[0])
    return accuracy

  def numerical_gradient(self, x, t):
    loss_W = lambda W: self.loss(x,t)
    grads = {}
    grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
    grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
    grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
    grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
    return grads




Yoshihisa Nitta

http://nw.tsuda.ac.jp/