備忘録_ニューラルネットワークの学習

備忘録_ニューラルネットワークの学習

ニューラルネットワークの学習の手順(確率的勾配法(SGD))

確率的とは、「確率的に無造作に選び出した」という意味である。

ステップ1 ミニバッチ
訓練データの中からランダムに一部のデータを選び出す。その選ばれたデータをミニバッチと言い、ここでは、そのミニバッチの損失関数の値を減らすことを目的とする。

ステップ2 勾配の算出
ミニバッチの損失関数を減らすために、各重みパラメータの勾配を求める。勾配は、損失関数の値を最も減らす方向を示す。

ステップ3 パラメータの更新
重みパラメータを勾配方向に微少量だけ更新する。

ステップ4 繰り返す
ステップ1~ステップ3を繰り返す

手書き数字を学習するニューラルネットワークの実装

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

# coding: utf-8
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.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        #1層目
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        #2層目
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        return y
        
    # x:入力データ, t:教師データ
    def loss(self, x, t):
        y = self.predict(x)
        
        return cross_entropy_error(y, t)
    
    def accuracy(self, x, t):
        y = self.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
        
    # x:入力データ, t:教師データ
    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      
  • input_size
    入力層のニューロンの数。
    今回は784(28×28)。
  • hidden_size
    隠れ層のニューロンの数。
  • output_size
    出力層のニューロンの数。
    今回は10(0から9までの数字の為)。
  • __init__(self, input_size, hidden_size, output_size)
    初期化を行う。
    今回の重みパラメータはガウス分布に従う乱数で初期化、バイアスは0で初期化する。
  • predict(self, x)
    認識(推論)を行う。引数のxは画像データ。
  • loss(self, x, t)
    交差エントロピー誤差で損失関数の値を求める。
  • accuracy(self, x, t)
    認識制度を求める。
  • numerical_gradient(self, x, t)
    重みパラメータに対する勾配を求める。
  • grads
    勾配を保持するディクショナリ変数
    grads[‘W1’]は1層目の重みの勾配、grads[‘b2’]は2層目のバイアスの勾配

ミニバッチ学習の実装

ミニバッチ学習とは訓練データから無作為に一部のデータを取り出してそのミニバッチを対象に、勾配法によりパラメータを更新する。

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# データの読み込み
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000  # 繰り返しの回数を適宜設定する
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 勾配の計算
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)
    
    # パラメータの更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    # 訓練データとテストデータの認識制度の記録(1エポックごと)
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

# グラフの描画
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
  • load_mnist()
    MNISTデータセットを読み込む
  •  ミニバッチの取得
    ミニバッチのサイズを100とし、毎回60000個の訓練データからランダムに100個のデータ(画像データと正解ラベルデータ)を抜き出す
  • 勾配の計算、パラメータの更新
    100個のミニバッチを対象に勾配を求め、確率的勾配下降法でパラメータを更新する。
    パラメータの更新回数(iters_num)は10000回。
    更新するごとに訓練データに対する損失関数を計算し、その結果をtrain_loss_listに追加する。
  • 訓練データとテストデータの認識制度の記録(1エポックごと)
    1エポックとは学習において訓練データをすべて使いきったときの回数に対応する。

上記のコードで得られた訓練データとテストデータに対する認識精度の推移

  • 学習(エポック)が進むにつれて訓練データとテストデータを使って評価した認識制度は両方とも向上していることが分かる。
  • 2つの認識制度には差がないことが分かる(過学習が起きていない)。