学習者言語の分析(応用)2(第5回)
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__ () 必要な引数: 入力のサイズ、中間層のサイズ、出力層のサイズ
- 重みを保存するディクショナリを作成する。
- W1、W2をkeyとしてランダムな値を保存する(weight_init_stdは重みが大きくならないように調整する定数)。
- バイアス(b1、b2)の初期値は0を初期値とする。
predict() 必要な引数: 入力データ
- 重み、バイアスを呼び出し、それぞれW1、W2、b1、b2に保存する。
- 推論(掛け算)する。W1を掛けてb1を足してシグモイド変換して、W1を掛けてb2を足して、ソフトマックスする。
loss() 必要な引数: 入力データ、教師ラベル(正解データ)
- predict()した値をyに保存する。
- 交差エントロピーを計算して出力する。
accuracy() 必要な引数: 入力データ、教師ラベル(正解データ)
- predict()を利用して推論(掛け算)した値をyに保存
- y(予測値)とt(正解)はone hot表現なのでnp.argmax()で最大値(1)の要素番号を取得。
- yとtで最大値(1)の要素番号が合っているデータを足して総数で割る(平均値の算出)
numerical_gradient() 必要な引数: 入力データ、教師ラベル(正解データ)
- 損失関数を関数として保存
- 勾配を保存するディクショナリを作成
- 重み、バイアスの勾配を計算して出力
このクラスの変数とメソッドを以下にまとめます。
| 変数 | 説明 |
|---|---|
| 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)
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>]
- 学習済みのニューラルネットワークを利用してテストデータで正解率を算出します。
In [16]:
net.accuracy(x_test,t_test)
Out[16]:
0.9462
