過去記事まとめ
使用スクリプトのダウンロード
git clone https://github.com/Kazuki-Nakamae/Lua_DLfromScratch
はじめに
前回は原書4章の二層ニューラルネットワークを実装し、それによるMNISTの学習処理を実装することになっていましたが、現状の数値微分による勾配算出では遅すぎるため、まず誤差逆伝搬法の実装をしてから学習実装したいと思います。
今回はまず単純な加算レイヤと乗算レイヤ実装です。スクリプトは以下の通りです。
layer_naive.lua
--Copyright (C) 2017 Kazuki Nakamae
--Released under MIT License
--license available in LICENSE file
--- MulLayerクラス(オブジェクト)
-- 乗算レイヤを実装
MulLayer={}
MulLayer.new = function()
local obj = {}
--メンバ変数
obj.x = nil
obj.y = nil
-- @function self.forward()
-- 順伝搬
-- @param x 入力1{Type:number}
-- @param x 入力2{Type:number}
-- @return 乗算結果{Type:number}
obj.forward = function(self, x, y)
self.x = x
self.y = y
local out = x * y
return out
end
-- @function self.backward()
-- 逆伝搬
-- @param dout 層の出力に対する微分{Type:number}
-- @return 層の各入力に対する微分{Type:number}
obj.backward = function(self, dout)
local dx = dout * self.y
local dy = dout * self.x
return dx, dy
end
return obj
end
--- AddLayerクラス(オブジェクト)
-- 加算レイヤを実装
AddLayer={}
AddLayer.new = function()
local obj = {}
-- @function self.forward()
-- 順伝搬
-- @param x 入力1{Type:number}
-- @param x 入力2{Type:number}
-- @return 加算結果{Type:number}
obj.forward = function(self, x, y)
local out = x + y
return out
end
-- @function self.backward()
-- 逆伝搬
-- @param dout 層の出力に対する微分{Type:number}
-- @return 層の各入力に対する微分{Type:number}
obj.backward = function(self, dout)
local dx = dout * 1
local dy = dout * 1
return dx, dy
end
return obj
end
順伝搬は入力をそのまま加算したり乗算したりしますが、逆伝搬では層の出力(z(x,y))に対する微分dL/dzから層の出力(x,y)に対する微分dL/dx, dL/dy を求めます。合成関数の知識がいるので忘れている方は思い出していただけるとよいかと思います。
これを用いた原書p140での計算は以下の通りになります。
buy_apple_orange.lua
--Copyright (C) 2017 Kazuki Nakamae
--Released under MIT License
--license available in LICENSE file
require './layer_naive'
local apple = 100
local apple_num = 2
local orange = 150
local orange_num = 3
local tax = 1.1
-- layerの宣言
local mul_apple_layer = MulLayer.new()
local mul_orange_layer = MulLayer.new()
local add_apple_orange_layer = AddLayer.new()
local mul_tax_layer = MulLayer.new()
-- forward
local apple_price = mul_apple_layer:forward(apple, apple_num) -- (1層)
local orange_price = mul_orange_layer:forward(orange, orange_num) -- (2層)
local all_price = add_apple_orange_layer:forward(apple_price, orange_price) -- (3層)
local price = mul_tax_layer:forward(all_price, tax) -- (4層)
-- backward
local dprice = 1
local dall_price, dtax = mul_tax_layer:backward(dprice) -- (4層)
local dapple_price, dorange_price = add_apple_orange_layer:backward(dall_price) -- (3層)
local dorange, dorange_num = mul_orange_layer:backward(dorange_price) -- (2層)
local dapple, dapple_num = mul_apple_layer:backward(dapple_price) -- (1層)
-- 結果
print("price:"..price)
print("dApple:"..dapple)
print("dApple_num:"..dapple_num)
print("dOrange:"..dorange)
print("dOrange_num:"..dorange_num)
print("dTax:"..dtax)
実行結果
$ th buy_apple_orange.lua
price:715
dApple:2.2
dApple_num:110
dOrange:3.3
dOrange_num:165
dTax:650
おわりに
今回は以上です。
次回からは続いて活性化レイヤを実装していきましょう。
ありがとうございました。