学習者言語の分析(応用)2(第5回)

  • 5.1 2層のニューラルネトワーク
  • 5.2 学習アルゴリズムの実装
    • 5.2.1 MNIST
    • 5.2.2 ミニバッチ処理
    • 5.2.3 ニューラルネットワークの実装

5.1 2層のニューラルネトワーク¶

  • ここまででニューラルネットワークの学習に関する基本的な知識はすべて扱いました。
  • ここでは、2層のニューラルネットワークを1つのクラスとして実装します。
  • ここまで定義した関数をcommonというフォルダにまとめたので以下のようにimportしてください。
In [1]:
import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
import numpy as np
  • それでは2層のニューラルネットワークのクラスを見ていきましょう。
In [2]:
class TwoLayerNet:
    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        
        # 1
        self.params = {} 
        
        # 2
        self.params["W1"] = weight_init_std * np.random.randn(input_size,hidden_size)
        self.params ["W2"] = weight_init_std * np.random.randn(hidden_size,output_size)
        
        # 3
        self.params["b1"] = np.zeros(hidden_size)
        self.params["b2"] = np.zeros(output_size)
        
    def predict(self,x):
        
        # 1
        W1,W2 = self.params["W1"], self.params["W2"]
        b1, b2 = self.params["b1"],self.params["b2"]
        
        # 2
        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):
        # 1
        y = self.predict(x)
        
        # 2
        return cross_entropy_error(y,t)

    def accuracy(self,x,t):
        # 1
        y = self.predict(x)
        
        # 2
        y = np.argmax(y,axis=1)
        t = np.argmax(t,axis=1)
        
        # 3
        accuracy = np.sum(y ==t) / float(x.shape[0])
        return accuracy
    
    def numerical_gradient(self,x,t):
        # 1
        loss_W = lambda W: self.loss(x,t)
        
        # 2
        grads = {}
        
        # 3
        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
  • __init__ () 必要な引数: 入力のサイズ、中間層のサイズ、出力層のサイズ

    1. 重みを保存するディクショナリを作成する。
    2. W1、W2をkeyとしてランダムな値を保存する(weight_init_stdは重みが大きくならないように調整する定数)。
    3. バイアス(b1、b2)の初期値は0を初期値とする。
  • predict() 必要な引数: 入力データ

    1. 重み、バイアスを呼び出し、それぞれW1、W2、b1、b2に保存する。
    2. 推論(掛け算)する。W1を掛けてb1を足してシグモイド変換して、W1を掛けてb2を足して、ソフトマックスする。
  • loss() 必要な引数: 入力データ、教師ラベル(正解データ)

    1. predict()した値をyに保存する。
    2. 交差エントロピーを計算して出力する。
  • accuracy() 必要な引数: 入力データ、教師ラベル(正解データ)

    1. predict()を利用して推論(掛け算)した値をyに保存
    2. y(予測値)とt(正解)はone hot表現なのでnp.argmax()で最大値(1)の要素番号を取得。
    3. yとtで最大値(1)の要素番号が合っているデータを足して総数で割る(平均値の算出)
  • numerical_gradient() 必要な引数: 入力データ、教師ラベル(正解データ)

    1. 損失関数を関数として保存
    2. 勾配を保存するディクショナリを作成
    3. 重み、バイアスの勾配を計算して出力
  • このクラスの変数とメソッドを以下にまとめます。

変数 説明
params 重みを保存するディクショナリ
grads 勾配を保存するディクショナリ
メソッド 説明
__init__(self,input_size,hidde_size,output_size) 初期化を行う(入力するデータの数、中間層のニューロンの数、出力層の数を設定する)
predict(self,x) 推論(掛け算)を行う。xはデータ
loss(self,x,t) 損失を求める。xはデータ、tは正解ラベル
accuracy(self,x,t) 予測の精度を求める。
  • それでは、このニューラルネットワークの動作を見ていきましょう。
  • 以下では入力層が3、中間層が4、出力層が2のニューラルネットワークを生成します。
In [8]:
# インスタンスの生成
net = TwoLayerNet(input_size=3,hidden_size=4,output_size=2)
In [9]:
# 初期化された(ランダムな)重み
net.params["W1"].shape
Out[9]:
(3, 4)
In [10]:
# ランダムなデータを発生させ、予測
x = np.random.rand(10,3)
y = net.predict(x)
In [11]:
y.shape
Out[11]:
(10, 2)
In [12]:
# ランダムに正解ラベルを発生させて、勾配を計算
t = np.random.rand(10,2)
grads = net.numerical_gradient(x,t)

5.2 学習アルゴリズムの実装¶

5.2.1 MNIST¶

  • ここでは、MNISTという手書き画像セットを用いて学習アルゴリズムを実装します。
  • MNISTには0から9までの数字画像があり、以下のこの画像データは$縦28 \times 横28$のグレー画像で各ピクセルは0(白)から255(黒)までの値をとり、それぞれの画像データには正解ラベルが付与されています。
  • 本授業では以下のようにMNISTのデータをダウンロードします。

MNIST

In [14]:
import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist

(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label=True)

5.2.2 ミニバッチ処理¶

  • 大量のデータを対象にして損失関数を求めるにはかなり時間がかかります。
  • そのため、ニューラルネットワークでは、ランダムにデータの一部を取り出して学習をし、また次の学習でもランダムにデータの一部を取り出して学習するというプロセスを繰り返します。
  • これをミニバッチ学習と言います。
  • MNISTは訓練データが60000あります。
In [9]:
print(x_train.shape)
(60000, 784)
  • このデータからランダムに100個のデータを抜き出すには以下のようにします。
In [10]:
train_size = x_train.shape[0]
batch_size = 100
batch_mask = np.random.choice(train_size,batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

5.2.3 ニューラルネットワークの実装¶

  • 以下は上記のTwoLayerNetにgradientというメソッドを加えたものです。
  • 授業で扱った勾配の計算方法を実装するnumerical_gradientでは計算時間が膨大になってしまいます。そのため、授業では扱っていない方法で実装しています。
In [11]:
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']
    
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        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 num_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
        
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}
        
        batch_num = x.shape[0]
        
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        
        dz1 = np.dot(dy, W2.T)
        da1 = sigmoid_grad(a1) * dz1
        grads['W1'] = np.dot(x.T, da1)
        grads['b1'] = np.sum(da1, axis=0)

        return grads
  • それはこのTwoLayerNetでMNISTのデータを扱ってみましょう。
In [12]:
# インスタンスの生成
net = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
In [13]:
# ハイパーパラメター
iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
In [14]:
# ミニバッチ学習の実装

# 記録保存のためのリスト
loss_list = []

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 = net.gradient(x_batch,t_batch)
    
    # パラメータの更新
    for key in ("W1","b1","W2","b2"):
        net.params[key] -= learning_rate * grad[key]
        
    # 経過の記録
    loss = net.loss(x_batch,t_batch)
    loss_list.append(loss)
In [15]:
# 損失関数の可視化
import matplotlib.pyplot as plt
%matplotlib inline

right = np.arange(1,10001,1)
plt.plot(right,loss_list)
Out[15]:
[<matplotlib.lines.Line2D at 0x1152fc790>]
No description has been provided for this image
  • 学習済みのニューラルネットワークを利用してテストデータで正解率を算出します。
In [16]:
net.accuracy(x_test,t_test)
Out[16]:
0.9462