過去記事
Lua版 ゼロから作るDeep Learning その1[パーセプトロンの実装]
活性化関数の実装
今回は活性化関数(ステップ関数・シグモイド関数・ReLU 関数)の実装です。
スクリプトは以下の通りです。
require 'gnuplot'
---ステップ関数.
-- 入力の各成分が0を超えたときに1を返す
-- @param x 入力 (Type:torch.DoubleTensor)
-- @return 1 or 0 (Type:torch.ByteTensor)
function step_function(x)
local y = torch.gt(x, 0) -- '>' 演算子の代わり
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
--確認
print("ステップ関数")
print("step_function(torch.Tensor({-1.0, 1.0, 2.0})):")
print(step_function(torch.Tensor({-1.0, 1.0, 2.0})))
local x = torch.range(-5.0, 5.0, 0.1)
local y = step_function(x)
gnuplot.figure(1)
gnuplot.title('ステップ関数')
gnuplot.axis({torch.min(x)-1, torch.max(x)+1, -0.1, 1.1})
gnuplot.plot(x, y, '-')
print("シグモイド関数")
print("sigmoid(torch.Tensor({-1.0, 1.0, 2.0})):")
print(sigmoid(torch.Tensor({-1.0, 1.0, 2.0})))
local x = torch.range(-5.0, 5.0, 0.1)
local y = sigmoid(x)
gnuplot.figure(2)
gnuplot.title('シグモイド関数')
gnuplot.axis({torch.min(x)-1, torch.max(x)+1, -0.1, 1.1})
gnuplot.plot(x, y, '-')
local x = torch.range(-5.0, 5.0, 0.1)
local y1 = step_function(x)
local y2 = sigmoid(x)
gnuplot.figure(3)
gnuplot.title('ステップ関数とシグモイド関数')
gnuplot.axis({torch.min(x)-1, torch.max(x)+1, -0.1, 1.1})
gnuplot.plot({x, y1, '-'}, {x, y2, '-'})
print("ReLU(Rectified Linear Unit) 関数")
print("sigmoid(torch.Tensor({-1.0, 1.0, 2.0})):")
print(relu(torch.Tensor({-1.0, 1.0, 2.0})))
local x = torch.range(-5.0, 5.0, 0.1)
local y = relu(x)
gnuplot.figure(4)
gnuplot.title('ReLU 関数')
gnuplot.axis({torch.min(x)-1, torch.max(x)+1, -1, 5.5})
gnuplot.plot(x, y, '-')
python の numpy では各要素と数値との商は演算子 '/' で大丈夫でしたが、Torchでは同様のオーバーロードは実装されていないようです。
したがって、シグモイド関数は以下のように書かなくてはなりません。
function sigmoid(x)
--入力したTensorのコピーの全要素を1に書き換える
y = x:clone():fill(1)
--各要素同士の商を求める
return torch.cdiv(y , (1 + torch.exp(-x)))
end
これは numpy よりも少しまどろっこしいかもしれません。
ただ演算子のオーバーロードは、可読性は向上しますが、変数の型がわかりづらくなるためにうっかりミスに繋がりやすいです。
なのでこのあたりは一長一短ではないかと思います。
個人的にはこっちのほうが好きです。
また同じような例として、Torch のTensor では論理演算子が使えません。代わりに以下のように書きます。
function step_function(x)
local y = torch.gt(x, 0) -- '>' 演算子の代わり
return y
end
戻り値はintenger 型のTensor となります。
論理演算子の代替メソッド一覧は以下の通りです。
torch.lt(a, b) -- '<'
torch.le(a, b) -- '<='
torch.gt(a, b) -- '>'
torch.ge(a, b) -- '>='
torch.eq(a, b) -- '=='
torch.ne(a, b) -- '~='
詳しくは以下のマニュアルをご参照ください。
実行
では先ほどのスクリプトを実行します。
th activationFunc.lua
出力は以下の通りです。
ステップ関数
step_function(torch.Tensor({-1.0, 1.0, 2.0})):
0
1
1
[torch.ByteTensor of size 3]
シグモイド関数
sigmoid(torch.Tensor({-1.0, 1.0, 2.0})):
0.2689
0.7311
0.8808
[torch.DoubleTensor of size 3]
ReLU(Rectified Linear Unit) 関数
sigmoid(torch.Tensor({-1.0, 1.0, 2.0})):
0
1
2
[torch.DoubleTensor of size 3]
原書と同じ出力を得られていると思います。
gnuplot を標準でサポートしてくれているのはありがたいです。
Torch でのgnuplot の使い方は以下をご参照ください。
補足
今回シグモイド関数を実装していますが、Torch では標準でシグモイド関数が実装されています。
原書にしたがって自分で実装しましたが、本来はその必要はありません。
torch.sigmoid(x)
おわりに
今回は以上です。
numpy と多少の違いはあれど同じように実装できるので安心しました。
次回は3層ニューラルネットワークを実装していきます。ありがとうございました。