ゼロから作るDeep Learning ❸ ―フレームワーク編
- 作者:斎藤 康毅
- 発売日: 2020/04/20
- メディア: 単行本(ソフトカバー)
ゼロから作るDeep Learning ③のDezero実装、勉強のためRubyでの再実装に挑戦している。今回はステップ46。この付近から重たいので1章ずつ進めていくことにする。
- ステップ46:最適化ルーチンを一般化する。これまでの最適化は誤差を計算したうえで手動でパラメータを更新する記述をしていたが、これを一般化する。
model.params().each{|p|
p.data -= lr * p.grad.data
}
このような手動で記述していた部分を切り出し、Optimizer
クラスとして引き抜く。
class Optimizer def initialize() @target = nil @hooks = [] end def setup(target) @target = target end def update() params = @target.params().select{|p| p.grad != nil } @hooks.each{|f| f(params) } params.each{|param| self.update_one(param) } end def update_one(param) raise NotImplementedError() end def add_hook(f) @hook.append(f) end end
Optimizer
クラスを派生させてSGD
クラスを作ってみる。このクラスでは値の最適化ポリシは以下のようになる。
class SGD < Optimizer def initialize(lr=0.01) super() @lr = lr end def update_one(param) param.data -= @lr * param.grad.data end end
SGD
クラスを活用してパラメータの最適化を行うためには以下のように記述する。
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([hidden_size, 1]) optimizer = SGD.new(lr) optimizer.setup(model) for i in 0..(max_iter-1) do y_pred = model.call(x) loss = mean_squared_error(y, y_pred) model.cleargrads() loss.backward() optimizer.update() if i % 1000 == 0 then puts loss end end
これまでのパラメータ更新ルーチンがoptimizer.update
の1行で済むようになった。実行結果はこちら。
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)
さらに、SGD
以外のMomentumSGD
も用意しておく。以下のようなクラスを作成する。
class MomentumSGD < Optimizer def initialize(lr=0.01, momentum=0.9) super() @lr = lr @momentum = momentum @vs = Hash.new end def update_one(param) np = Numpy v_key = param.object_id if not @vs.include?(v_key) then @vs[v_key] = np.zeros_like(np.zeros_like(param.data)) end v = @vs[v_key] v *= @momentum v -= @lr * param.grad.data param.data += v end end
こちらを使って同様に最適化を行ったものがこちら。実行結果は一緒だった。
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([hidden_size, 1]) optimizer = MomentumSGD.new(lr) optimizer.setup(model) for i in 0..(max_iter-1) do y_pred = model.call(x) loss = mean_squared_error(y, y_pred) model.cleargrads() loss.backward() optimizer.update() 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)