Lua版 ゼロから作るDeep Learning その３［３層ニューラルネットワークの実装］


過去記事
--
[Lua版 ゼロから作るDeep Learning その１[パーセプトロンの実装]](http://qiita.com/Kazuki-Nakamae/items/16c82b5ac8a85d4adee3)
[Lua版 ゼロから作るDeep Learning その２［活性化関数］](http://qiita.com/Kazuki-Nakamae/items/5199447a54642620139a)

活性化関数の実装
--
　今回は３層ニューラルネットワークの実装です。

　スクリプトは以下の通りです。

```lua:activationFunc.lua
---ステップ関数.
-- 入力の各成分が０を超えたときに１を返す
-- @param x 入力 (Type：torch.DoubleTensor)
-- @return 1 or 0 (Type：torch.ByteTensor)
function step_function(x)
    local y = torch.gt(x, 0)    -- '>' 演算子の代わり (greater -> gt )
    return y
end

---シグモイド関数.
-- 入力の各成分に対するゲイン1の標準シグモイド関数（1/(1+exp(x))）の値を返す
-- @param x 入力 (Type：torch.DoubleTensor)
-- @return torch.DoubleTensor
function sigmoid(x)
    y = x:clone():fill(1)
    return torch.cdiv(y , (1 + torch.exp(-x)))
end

---ReLU(Rectified Linear Unit)関数.
-- 入力の各成分が0以下ならば0、0よりも高いならばそのままの値を返す
-- @param x 入力 (Type：torch.DoubleTensor)
-- @return torch.DoubleTensor
function relu(x)
    y = x:clone():fill(0)
    return torch.cmax(y, x)
end
```


```lua:three-layeredNN.lua
require './activationFunc.lua'

---恒等関数.
-- 出力層の活性化関数。
-- @param network 入力
-- @return 出力
function identity_function(x)
    return x
end

---ネットワーク生成関数.
-- 重みとバイアスが決定した3層NNを返す。
-- @return 3層NN (Type：table)
function init_network(x)
    local network = {};
    network['W1'] = torch.Tensor({{0.1, 0.3, 0.5}, {0.2, 0.4, 0.6}})
    network['b1'] = torch.Tensor({{0.1, 0.2, 0.3}})
    network['W2'] = torch.Tensor({{0.1, 0.4}, {0.2, 0.5}, {0.3, 0.6}})
    network['b2'] = torch.Tensor({{0.1, 0.2}})
    network['W3'] = torch.Tensor({{0.1, 0.3}, {0.2, 0.4}})
    network['b3'] = torch.Tensor({{0.1, 0.2}})

    return network
end

---順方向伝達関数.
-- 入力から出力までの伝達処理を行う。
-- @param network 入力 (Type：torch.table)
-- @param x 入力
-- @return 出力 (Type：torch.DoubleTensor)
function forward(network, x)
    local W1, W2, W3 = network['W1'], network['W2'], network['W3']
    local b1, b2, b3 = network['b1'], network['b2'], network['b3']

    local a1 = torch.mm(x, W1) + b1 --a1 = x * W1 + b1 でもよい
    local z1 = sigmoid(a1)
    local a2 = torch.mm(z1,W2) + b2
    local z2 = sigmoid(a2)
    local a3 = torch.mm(z2,W3) + b3
    local y = identity_function(a3)

    return y

end

local network = init_network()
local x = torch.Tensor({{1.0, 0.5}}) --1×2 マトリックス
local y = forward(network, x)
print(y)
```

 実行例
 
```bash:bashでの実行例
$th three-layeredNN.lua
 0.3168  0.6963
[torch.DoubleTensor of size 1x2]
```

　ほとんどpython の場合と変わらないと思います。
　
　ただ一点だけ注意点としてはnumpy.dot(A,B) をtorchでは torch.mm(A,B)と書く点です。
　torch にもtorch.dot(A, B) が存在しますが、この場合のA、B は1次元ベクトルとみなされます。
　torch の積はベクトルとマトリックスを区別しており、
　1次元ベクトル×1次元ベクトルならば、torch.dot(A, B)
　マトリックス×1次元ベクトルならば、torch.mv(A, B)
　マトリックス×マトリックスならば、torch.mm(A, B)
　となっています。
　そして残念なことに1次元ベクトル×マトリックスの計算は標準ではサポートされていないようです。
　そのため上では1次元ベクトルではなく、1×2マトリックスとして計算しています。
　
　また、入力をベクトルとして計算したい場合は転置t()を使って以下のように書けば大丈夫です。


```lua:activationFunc2.lua
require './activationFunc.lua'

---恒等関数.
-- 出力層の活性化関数。
-- @param network 入力
-- @return 出力
function identity_function(x)
    return x
end

---ネットワーク生成関数.
-- 重みとバイアスが決定した3層NNを返す。
-- @return 3層NN (Type：table)
function init_network(x)
    local network = {};
    network['W1'] = torch.Tensor({{0.1, 0.3, 0.5}, {0.2, 0.4, 0.6}})
    network['b1'] = torch.Tensor({0.1, 0.2, 0.3})
    network['W2'] = torch.Tensor({{0.1, 0.4}, {0.2, 0.5}, {0.3, 0.6}})
    network['b2'] = torch.Tensor({0.1, 0.2})
    network['W3'] = torch.Tensor({{0.1, 0.3}, {0.2, 0.4}})
    network['b3'] = torch.Tensor({0.1, 0.2})

    return network
end

---順方向伝達関数.
-- 入力から出力までの伝達処理を行う。
-- @param network 入力 (Type：torch.table)
-- @param x 入力
-- @return 出力 (Type：torch.DoubleTensor)
function forward(network, x)
    local W1, W2, W3 = network['W1'], network['W2'], network['W3']
    local b1, b2, b3 = network['b1'], network['b2'], network['b3']

    local a1 = torch.mv(W1:t(), x) + b1 --a1 = W1:t() * x + b1 でも大丈夫です
    local z1 = sigmoid(a1)
    local a2 = torch.mv(W2:t(), z1) + b2
    local z2 = sigmoid(a2)
    local a3 = torch.mv(W3:t(), z2) + b3
    local y = identity_function(a3)

    return y

end

local network = init_network()
local x = torch.Tensor({1.0, 0.5})
local y = forward(network, x)
print(y)
```

```bash:bashでの実行例
$th three-layeredNN2.lua
 0.3168
 0.6963
[torch.DoubleTensor of size 2]
```



おわりに
--

　今回は以上です。

　最初に示したやり方は少し概念的な部分で違う計算かもしれませんが、目的どおりの出力を得ることができました。
　二番目のものは計算として忠実ですが、転置するせいで分かりづらい感じがします。転置を内部でカバーする関数を独自に定義したほうがいいかもしれません。

　次回はソフトマックス関数を実装していきます。ありがとうございました。