-Deep Learning- バッチ処理

前回 までにMINSTデータセットを用いたニューラルネットワークの実装例を示しました.

今回は,まずはPythonインタプリタを使用して,ニューラルネットワークの各層の重み形状を出力してみます.

>>> x, _ = get_data()
>>> network = init_network()
>>> W1, W2, W3 = network['W1'], network['W2'], network['W3']
>>> 
>>> x.shape
(10000, 784)
>>> x[0].shape
(784,)
>>> W1.shape
(784, 50)
>>> W2.shape
(50, 100)
>>> W3.shape
(100, 10)
>>> 
 

この結果から,多次元配列の対応する次元の要素数が一致していることが確認できます(バイアスは省略).
Xは(1 x ) 784, W1は784 x 50, W2は50 x 100, W3は100 x 10で,Yは(1 x )10となっています.

ここで,画像を複数枚まとめて入力することを考えます.例として100枚の画像をまとめて1回のpredict()関数で処理することを考えます.
そのためには,Xの形状を100 x 784として,100枚分のデータをまとめて入力データとすることができ,各配列の形状は以下のようになります.
Xは100 x 784, W1は784 x 50, W2は50 x 100, W3は100 x 10で,Yは100 x 10
このようにすれば,100枚分の入力データの結果が一度に出力されます.このように,まとまりのある入力データはバッチ(batch)と呼ばれます.

数値計算ライブラリでは,大きな配列の計算を効率良く処理できるような最適化が行われていることから,コンピュータで計算する際にはバッチ処理を用いるとメリットがあります.

以下に,前回のコードを書き換えてバッチ処理を行うコードの例を示します.

 
# Import Module
import sys, os
sys.path.append(os.pardir)  # Setting of import from Parent Directory
import numpy as np
import pickle
from dataset.mnist import load_mnist
 
# Definition of Function
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
 
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T
 
def get_data():
    (x_train, t_train), (x_test, t_test) = \
        load_mnist(normalize = True, flatten = True, one_hot_label = False)
    return x_test, t_test
 
def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network
 
def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
 
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y  = sigmoid(a3)
    return y
 
# Execution part
x, t = get_data()
network = init_network()
 
batch_size = 100    # Number of batch
accuracy_cnt = 0
 
for i in range(0, len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p= np.argmax(y_batch, axis = 1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])
 
# Display results
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
 

実行結果は以下のようになります.

Accuracy:0.9352
 
上記のコードにおけるrange()関数は,range(start, endのように指定すると,startからend-1までの整数からなるリストを作成します.また,range(start, end, stepのように3つの整数を指定すると,リストの要素の次の値がstepで指定された値だけ増加するリストを作成します.
 
range()関数の例
>>> list (range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list (range(0, 10, 3))
[0, 3, 6, 9]
>>> 
 
このによって出力されるリストを元に,x[i:i+batch_size]のように入力データからバッチを抜き出します.x[i:i+batch_size]は,入力データのi番目から,i+batch_n 番目までのデータを取り出しますが,上記のコードではx[0:100]x[100:200],…といったように,先頭から100枚ずつバッチとして取り出しています.
 
argmax()では,最大値のインデックスを取得しています.上記の例では,axis = 1という引数を与えています.これは,100 x 10 の配列の中で1次元目の要素ごとに(1次元目を軸として)最大値のインデックスを見つけることを指定しています(0次元は最初の次元に対応する).以下に例を示します.
 
>>> import numpy as np
>>> x = np.array([[0.1, 0.8, 0.1], [0.3, 0.1, 0.6], [0.2, 0.5, 0.3], [0.8, 0.1, 0.1]])
>>> y = np.argmax(x, axis = 1)
>>> print(y)
[1 2 1 0]
>>> 
 
ここでの [1 2 1 0]は,[0.1, 0.8, 0.1]の中で(0番目の次の)1番目の要素が最大値であること,[0.3, 0.1, 0.6]の中で2番目の要素が最大値であること,[0.2, 0.5, 0.3]の中で1番目の要素が最大値であること,[0.8, 0.1, 0.1]の中で0番目の要素が最大値であることを示しています.
 
最後にバッチ単位で分類した結果と,実際の答えを比較しています.
そのために,NumPy配列同士で比較演算子==)によって,True / False からなるブーリアン配列を作成し,Trueの個数を算出しています.この処理手順の確認のために以下に例を示します.
 
>>> y = np.array([1, 2, 1, 0])
>>> t = np.array([1, 2, 0, 0])
>>> print(y == t)
[ True  True False  True]
>>> np.sum(y == t)
3
>>>