ゼロから作るDeep Learning ❸ ―フレームワーク編
- 作者:斎藤 康毅
- 発売日: 2020/04/20
- メディア: 単行本(ソフトカバー)
ゼロから作るDeep Learning ③を買った。DezeroのPython実装をRubyに移植する形で独自に勉強している。次はステップ13とステップ14。
- ステップ13:逆伝搬における可変長引数のサポート
前回に続いて可変長引数時の逆伝搬をサポートする。backward()
に以下の修正を加える。
step13.rb
class Variable ... def backward() if @grad == nil then @grad = @data.clone.fill(1.0) end funcs = [@creator] while funcs != [] do f = funcs.pop gys = f.outputs.map{|x| x.grad} gxs = f.backward(*gys) if not gxs.is_a?(Array) then gxs = [gxs] end f.inputs.zip(gxs).each{|x, gx| x.grad = gx if x.creator != nil then funcs.push(x.creator) end } end end
要点としてはf.outputs
とf.inputs
が可変長引数を受け取ることができるように拡張されている。f.outputs
に対してx.grad
の要素を取り出すためにmap
を使ったりして、backward(gys)
に対して任意の長さの配列が受け付けられるようになっている。
これに伴ってSquare
クラスの実装を変更した。
step13.rb
class Square < Function ... def backward(gy) x = @inputs[0].data gx = x.zip(gy).map{|i0, i1| i0 * i1 * 2.0} return gx end
inputs
が配列へと変更されたため@inputs[0]
として先頭の値を取り出すように変更されている。
これまでと同じようにテストを作って実行してみる。
def add(x0, x1) return Add.new().call(x0, x1) end x = Variable.new([2.0]) y = Variable.new([3.0]) z = add(square(x), square(y)) z.backward() puts(z.data) puts(x.grad) puts(y.grad)
13.0 4.0 6.0
上手くできているようだ。
- ステップ14:同じ変数を繰り返し使う場合の考慮
現状の実装では同じ変数を売り返して使う場合の考慮がなされていない。複数の場所で同じ変数を使用した場合には、backward
において微分した値を累積する必要がある。
step14.rb
class Variable ... def backward() if @grad == nil then @grad = @data.clone.fill(1.0) end funcs = [@creator] while funcs != [] do ... f.inputs.zip(gxs).each{|x, gx| if x.grad === nil then x.grad = gx else x.grad = [(x.grad + gx).sum] end if x.creator != nil then funcs.push(x.creator) end } ...
テストを行う。
begin x = Variable.new([3.0]) y = add(x, x) y.backward() puts(x.grad) end begin x = Variable.new([3.0]) y = add(add(x, x),x) y.backward() puts(x.grad) end
2.0 3.0
それともう一つ、微分の値をリセットするためのメソッドを定義しておいた。
class Variable ... def cleargrad() @grad = nil end ...
x = Variable.new([3.0]) y = add(x, x) y.backward() puts(x.grad) x = Variable.new([3.0]) y = add(add(x, x),x) y.backward() puts(x.grad)
2.0 3.0
こちら問題なさそう。