ゼロから作るDeep Learning ❸ ―フレームワーク編
- 作者:斎藤 康毅
- 発売日: 2020/04/20
- メディア: 単行本(ソフトカバー)
ゼロから作るDeep Learning ③のDezero実装、勉強のためRubyでの再実装に挑戦している。今回はステップ45。この付近から重たいので1章ずつ進めていくことにする。特にステップ45は2つのセクションに分かれており重たい。
- ステップ45:
TwoLayerNet
クラスを作ってレイヤのパラメータをまとめげる。2つのLinearLayer
とsigmoid
クラスをまとめ上げてパラメータを一括管理する。
class TwoLayerNet < Model def initialize(hidden_size, out_size) super() instance_variable_set(:@l1, LinearLayer.new(hidden_size)) instance_variable_set(:@l2, LinearLayer.new(out_size)) end def forward(x) y = sigmoid_simple(@l1.call(x)) y = @l2.call(y) return y end end
モデルクラスはplot
の機能をも持っており、Graphvizで表示することができる。
次にTwoLayerNet
を使って同様にニューラルネットワークを構築する。同じように学習できるかどうかを確認している。
np.random.seed(0) x = np.random.rand(100, 1) y = np.sin(2 * np.pi * x) + np.random.rand(100, 1) x = Variable.new(x) y = Variable.new(y) lr = 0.2 max_iter = 10000 hidden_size = 10 model = TwoLayerNet.new(hidden_size, 1) for i in 0..(max_iter-1) do y_pred = model.call(x) loss = mean_squared_error(y, y_pred) model.cleargrads() loss.backward() model.params().each{|p| p.data -= lr * p.grad.data } if i % 1000 == 0 then puts loss end end
実行結果。無事に学習に成功した。
variable(0.8165178492839196) variable(0.2499028013724818) variable(0.24609873705372717) variable(0.23721585190665512) variable(0.2079321578201881) variable(0.1231191944394262) variable(0.07888168068357643) variable(0.07666129175490086) variable(0.0763503210507124) variable(0.07616987350656905)
最後にMLP
クラスを作成する。MLP
クラスはTwoLayerNetクラスを一般化したもので、多層パーセプトロンを定義できる。
class MLP < Model def initialize(fc_output_sizes, activation=method(:sigmoid_simple)) super() @activation = activation @layers = [] fc_output_sizes.each_with_index{|out_size, i| layer = LinearLayer.new(out_size) layer_name = '@l' + i.to_s instance_variable_set(layer_name.intern, layer) @layers.append(layer) } end def forward(x) @layers.first(@layers.size-1).each{|l| x = @activation.call(l.call(x)) } return @layers.last().call(x) end end
色々と苦労したのだが、まずシンボルを定義するのに、文字列からシンボルを作り上げるための.intern
メソッドを使用した。これによりレイヤ名を定義できる。
layer_name = '@l' + i.to_s instance_variable_set(layer_name.intern, layer)
forward()
の中は、最後の1レイヤを除いてLinearLayer
を実行し、アクティベーションを実行する。最後のレイヤは単独で実行して終了する。
先ほどのTwoLayerNet
と全く同じネットワークを作って同じ動きをするか確認する。
begin np.random.seed(0) x = np.random.rand(100, 1) y = np.sin(2 * np.pi * x) + np.random.rand(100, 1) x = Variable.new(x) y = Variable.new(y) lr = 0.2 max_iter = 10000 hidden_size = 10 model = MLP.new([10, 1]) for i in 0..(max_iter-1) do y_pred = model.call(x) loss = mean_squared_error(y, y_pred) model.cleargrads() loss.backward() model.params().each{|p| p.data -= lr * p.grad.data } if i % 1000 == 0 then puts loss end end end
全く同じ動きが得られた。成功だ。
variable(0.8165178492839196) variable(0.253922549463463) variable(0.253893268388255) variable(0.2538649159758041) variable(0.2538374605947276) variable(0.25381087169107486) variable(0.25378511975162776) variable(0.2537601762684507)