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)

ニューラルネットワークの推論処理
--
　今回はMNIST画像データのNN推論処理を行います。
　
　原書ではあらかじめ重みとバイアスを決定したpklファイルを読み込んで行なっていますので、今回はこのpklファイルを用いてTorchで同様の処理を行いました。
　pklファイルの読み込みについては[Lua版 ゼロから作るDeep Learning その５．５［pklファイルをLua Torchで使えるようにする］](http://qiita.com/Kazuki-Nakamae/items/b56c3d23fc21fdc260b0)をご参照ください。
　
　スクリプトは以下のようになります。

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

---データ取得関数
-- MNISTのデータ取得する。
-- @return テストデータのイメージ ,テストデータのラベル {Type:ByteTensor}
function get_data()
    --download
    local tar = 'http://torch7.s3-website-us-east-1.amazonaws.com/data/mnist.t7.tgz'
    if not paths.dirp('mnist.t7') then
    os.execute('wget ' .. tar)
    os.execute('tar xvf ' .. paths.basename(tar))
    end
    --get data
    local test_file = 'mnist.t7/test_32x32.t7'
    local testData = torch.load(test_file,'ascii')

    return testData['data'], testData['labels']
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']

    --最初の入力ではnumpy形式に合わせるためにテンソルを一次元化
    local a1 = mulTensor(x:resize(1,W1:size()[1]), W1) + b1:double()
    local z1 = sigmoid(a1)
    local a2 = mulTensor(z1,W2) + b2:double()
    local z2 = sigmoid(a2)
    local a3 = mulTensor(z2,W3) + b3:double()
    local y = softmax(a3)

    return y

end

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

local accuracy_cnt = 0
for i = 1, x:size()[1] do
    scaledx = image.scale(x[i],28,28) --32x32 を 28x28 に縮小
    local y = predict(network, scaledx)
    local value, indices = torch.max(y, 2) -- torch.max( , 2) でrow 方向での最大値を取得
    local p = tensor2scalar(indices) --テンソルなのでスカラーへ変換
    if p == t[i] then
        accuracy_cnt = accuracy_cnt + 1
    end
end

print("Accuracy:"..(accuracy_cnt/x:size()[1]))
```

　原書では28x28 のMMISTデータでしたが、今回使う画像は32x32 となっています。そのためimage.scale(画像データ,28,28) というように縮小させたデータを推論処理にかけています。このため結果は原書とは異なるものになる点に注意しましょう。
　
　[activationFunc.lua](http://qiita.com/Kazuki-Nakamae/items/5199447a54642620139a)、[softmax.lua](http://qiita.com/Kazuki-Nakamae/items/20e53a02a8b759583d31)は過去の記事を参照してください。
　exTorch.lua では可読性の向上のために補助的に定義した関数が入っています。

```lua:exTorch.lua
---テンソル間の積の計算関数
-- 各次元に対応したテンソルの積ABを行う。
-- @param A A (Type：Tensor)
-- @param B B (Type：Tensor)
-- @return AB (Type：torch.DoubleTensor)
function mulTensor(A, B)
    A = A:double()
    B = B:double()
    local AB = nil;
    if (A:dim() == 1 and B:dim() ~= 1) then
        --1Dvector・matrix
        AB = torch.mv(B:t(), A)
    else
        --others
        AB = A*B
    end
    return AB
end

---テンソルをスカラー変換関数
-- 1x1テンソルをスカラーへ変換する。
-- @param tensor 1x1テンソル (Type：Tensor)
-- @return スカラー (Type：number)
function tensor2scalar(tensor)
    return tensor[1]
end
```



```bash:実行例
$ th neuralnet_mnist.lua
Accuracy:0.8616	

```

　縮小しているのでpythonでの結果とは異なっていますが、ちゃんと推論自体はできていそうです。
　
　今回はpythonのデータに無理やり合わせた処理が多くなっていますが、それがなければ推論処理は原書と同様の手間で行えると思います。ただ一つ注意するならば、型の扱いはpythonよりも気をつけるべきかもしれません。pythonでは暗黙の型変換をわりとしてくれますが、Luaの型の扱い方はわりと標準的なので違っていればエラーが返ってきます。
　
　型は整数での計算であることがわかっているならば、byteやlongなどの整数型、小数を交えた計算ならばdouble型を使いましょう。
　基本的なことではありますがコンピュータの小数計算には常に誤差が伴うのでfloat型はできる限り避けた方が良いです。ご存知ないという方は以下のコードを試してみてください。


```lua:cancellation.lua
local floatT = torch.Tensor(1):fill(0):float()
for i = 1, 1000000 do
    floatT = floatT + torch.Tensor(1):fill(0.000001):float()
end
local doubleT = torch.Tensor(1):fill(0):double()
for i = 1, 1000000 do
    doubleT = doubleT + torch.Tensor(1):fill(0.000001):double()
end
print("float : ")
print(floatT)
print("double : ")
print(doubleT)
```

```bash:実行例
$ th cancellation.lua
float : 	
 1.0090
[torch.FloatTensor of size 1]

double : 	
 1.0000
[torch.DoubleTensor of size 1]
```
 これは本来はどちらも１となるのが正しい計算ですが、floatでは誤差が生じるため結果がずれてしまいます。doubleでもこのような誤差は生じますがfloatよりは安全です。
　

おわりに
--

　今回は以上です。

　次回はこの推論処理にバッチ処理を実装してみようと思います。
　
　ありがとうございました。
　
追記(2017/06/24)
--

　縮小した結果で行うのは気持ち悪い気もするので、いっそのことMMISTデータもpythonから取り出してしまいましょう。
　原書付録のファイルの中からdatasetディレクトリをカレントディレクトリにコピーし、以下のスクリプトを実行します。

```python3:saveMMISTtest.py
# coding: utf-8
import sys, os
import numpy as np
import pickle
from dataset.mnist import load_mnist

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

np.save("x_test",x_test)
np.save("t_test",t_test)
```
```bash:実行例
$ python3 saveMMISTtest.py
```

x_test.npy とt_test.npy が出力されるのでこれをget_data()で読み込む形にします。

```python3:neuralnet_mnist2.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']

    --最初の入力ではnumpy形式に合わせるためにテンソルを一次元化
    local a1 = mulTensor(x, W1) + b1:double()
    local z1 = sigmoid(a1)
    local a2 = mulTensor(z1,W2) + b2:double()
    local z2 = sigmoid(a2)
    local a3 = mulTensor(z2,W3) + b3:double()
    local y = softmax(a3)

    return y

end

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

local accuracy_cnt = 0
for i = 1, x:size()[1] do
    local y = predict(network, x[i])
    local value, indices = torch.max(y, 1) -- torch.max( , 1) でcol 方向での最大値を取得
    local p = tensor2scalar(indices) -1 --python出力なので配列の開始が0、Luaの配列(table型)は1から開始なので1だけずらす
    if p == t[i] then
        accuracy_cnt = accuracy_cnt + 1
    end
end

print("Accuracy:"..(accuracy_cnt/x:size()[1]))
```
```bash:実行例
$ th neuralnet_mnist2.lua
Accuracy:0.9352
```

 これで原書と同じ出力を出すことができました。