Lua版 ゼロから作るDeep Learning その７［バッチ処理］


過去記事
--
[Lua版 ゼロから作るDeep Learning その１[パーセプトロンの実装]](http://qiita.com/Kazuki-Nakamae/items/16c82b5ac8a85d4adee3)
[Lua版 ゼロから作るDeep Learning その２［活性化関数］](http://qiita.com/Kazuki-Nakamae/items/5199447a54642620139a)
[Lua版 ゼロから作るDeep Learning その３［３層ニューラルネットワークの実装］](http://qiita.com/Kazuki-Nakamae/items/57c2977d0a9a38bdf6b8)
[Lua版 ゼロから作るDeep Learning その４［ソフトマックス関数の実装］]
(http://qiita.com/Kazuki-Nakamae/items/20e53a02a8b759583d31)
[Lua版 ゼロから作るDeep Learning その５［MNIST画像の表示］](http://qiita.com/Kazuki-Nakamae/items/6373443510f94a67e910)
[Lua版 ゼロから作るDeep Learning その5.5［pklファイルをLua Torchで使えるようにする］](http://qiita.com/Kazuki-Nakamae/items/b56c3d23fc21fdc260b0)
[Lua版 ゼロから作るDeep Learning その６［ニューラルネットワークの推論処理］](http://qiita.com/Kazuki-Nakamae/items/4a8198ec56c8a9658bd3)



バッチ処理
--
　今回は前回のMNIST画像データのNN推論処理にバッチ処理を実装します。
　
　
　スクリプトは以下のようになります。

```lua:neuralnet_mnist_batch.lua
require './activationFunc.lua'
require './softmax.lua'
require './exTorch.lua'
npy4th = require 'npy4th' --https://github.com/htwaijry/npy4th (Author:Hani Altwaijry)

---データ取得関数
-- MNISTのデータ取得する。
-- @return テストデータのイメージ ,テストデータのラベル {Type:ByteTensor}
function get_data()
    --画像データ
    local x_test = npy4th.loadnpy('x_test.npy')
    --ラベルデータ
    local t_test = npy4th.loadnpy('t_test.npy')

    return x_test, t_test
end

---ネットワーク生成関数.
-- 重みとバイアスが決定した3層NNを返す。今回はpklで記述された重みを用いる
-- @return 3層NN (Type：table)
function init_network()
    local network = npy4th.loadnpz('sample_weight.npz')

    return network
end

---分類関数.
-- ネットワークにしたがって分類計算を行う。
-- @param network 入力 (Type：torch.table)
-- @param x 入力
-- @return 出力 (Type：torch.DoubleTensor)
function predict(network, x)
    local W1, W2, W3 = network['W1'], network['W2'], network['W3']
    local b1, b2, b3 = network['b1'], network['b2'], network['b3']

    local a1 = mulTensor(x, W1) + makeIterTensor(b1,mulTensor(x, W1):size()[1])
    local z1 = sigmoid(a1)
    local a2 = mulTensor(z1,W2) + makeIterTensor(b2,mulTensor(z1,W2):size()[1])
    local z2 = sigmoid(a2)
    local a3 = mulTensor(z2,W3) + makeIterTensor(b3,mulTensor(z2,W3):size()[1])
    local y = softmax(a3)

    return y

end

local x, t = get_data()
local network = init_network()

local batch_size = 100 --バッチ数
local accuracy_cnt = 0
for i = 1, x:size()[1], batch_size do
    local x_batch = x:sub(i,i+batch_size-1)
    local y_batch = predict(network, x_batch)
    local value, indices = torch.max(y_batch, 2)
    local p = indices - 1
    --要素それぞれを比較して合計を計算
    accuracy_cnt = accuracy_cnt + torch.sum(torch.eq(p:byte(), t:sub(i,i+batch_size-1):byte()))
end

print("Accuracy:"..(accuracy_cnt/x:size()[1]))
```
```bash:出力結果
$ th neuralnet_mnist_batch.lua
Accuracy:0.9352	
```


　それほど変わりばえはしませんが、細かな部分で工夫が必要です。
　
　まずバッチで指定した分の配列の取得ですが、Tensorはコロン表記```i:i+batch_size```で要素を範囲取得することはできません。代わりに```:sub(i,i+batch_size-1)```メソッドで部分行列として取得します。また、配列の始まりが1からなので１から始まるならば最後の要素は```i+batch_size-1```となります。
　
　またpredictの部分では加算をするためにbatch指定した分だけバイアスのベクトルを行方向に重ねた行列が必要になります。numpyのように自動的にはやってくれないので、そのための関数```makeIterTensor()```を定義しましょう。

```lua:makeIterTensor関数(exTorch.lua内に定義)
---行反復行列生成関数
-- 入力されたベクトルをN行反復する行列を生成する
-- @param vector 一次元ベクトル (Type：Tensor)
-- @param iter 反復数 (Type：byte)
-- @return 行反復行列 (Type：DoubleTensor)
function makeIterTensor(vector,iter)
    local iterTensor = torch.DoubleTensor(vector:size()[1],iter)
    local i = 0
    iterTensor:apply(function() --applyで各要素へ値を代入
        i = i + 1
        if vector:size()[1]<i then --ベクトルのインデックスを超えたら初期化
            i = 1
        end
        return vector[i]
    end)

    return iterTensor
end
```

　これでpredict内の計算が可能となります。
　
　ちなみにTensor対スカラーで演算する場合は演算子で一括で計算してくれます。```local p = indices - 1```の部分がそれですね。
　
　最後の等価の評価ですが、各要素に対して比較結果をだす場合は```torch.eq()```で行う必要があります。型にも厳密ですので```:byte()```などでキャストしましょう。
　

おわりに
--

　今回は以上です。簡単な自作関数を定義していくことで冗長にならず意外とまとまった記述になった気がします。

　次回は原書の４章であるNN学習の実装に入っていきます。
　
　ありがとうございました。