ゼロから作るDeep Learning(14日目)

こんにちは、筋肉めがねです。

 

12月も残り3週間というところで、ようやくお尻に火がついてきました。この調子でサクサクっと書いていきます。

本日、12月11日、イギリス下院でBrexitに関わる重要な投票が実施されますね。これは結果を見てから、記事に書いていきましょう。

 

先週、木曜日、12月6日は聖ニコラウスの日でしたね。ドイツ始め幾つかのヨーロッパの国では、子供達がこの日にプレゼントをもらう習慣があります。子供達は前日の夜に靴磨きをし、6日の朝に聖ニコラウスのおっちゃんが靴の中に残していったプレゼントを見つけ大喜び。僕らがよく知るサンタクロースのモデルとなる人物は、実は6日には既にドイツに来ていたんですね。

 

f:id:KinnikuMegane:20181210064447j:plain

 

それでは、本日も「ゼロから作るDeep Learning」を進めていきましょう。本日は第5章です。

 

5章のはじめは計算グラフについての説明です。就職活動時の1次試験で受けるSPIに出題されそうな内容ですね。

f:id:KinnikuMegane:20181211145604p:plain 

4章では数値微分を用いた勾配降下法により、ニューラルネットワークの学習を行っておりました。ただ、数値微分を用いて勾配を求めようとすると時間がかかるという欠点があります。ですので、5章では、より高速に、そして効率よくニューラルネットワークが学習を行えるように誤差逆伝播法を使おうという訳です。

ざっくり言うと、盆地を歩いている時に、歩いているその地点の傾きを求める方法がいろいろあって、その中でも手っ取り早い方法が誤差逆伝播法、という事です。そして、誤差逆伝播法を視覚的に理解するために計算グラフを使っているという訳です。

 

順方向の伝播については、上の示した図の通り、視覚的に理解できますが、逆伝播では、その入力(順方向の出力)を最初に受け取り、微分を計算して次のノードへと計算結果を渡していきます。そして、逆伝播において、特に1点気を付けるべきポイントがあります。それは、乗算の逆伝播では、順伝播のときの入力信号の値が必要になり、そのため、乗算ノードの実装時には、順伝播の入力信号を保持しておく必要がある、という事です。

 

乗算レイヤと加算レイヤのコードを書いて理解していきましょう。 

class MulLayer:
     def __init__(self):
         self.x = None
         self.y = None
     def forward(self, x, y):
         self.x = x
         self.y = y
         out = x * y
         return out
     def backward(self, dout):
         dx = dout * self.y
         dy = dout * self.x
         return dx, dy
 
class AddLayer:
     def __init__(self):
         pass
     def forward(self, x, y):
         out = x + y
         return out
    def backward(self, dout):
         dx = dout * 1
         dy = dout * 1
         return dx, dy
 
apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()
apple_price = mul_apple_layer.forward(apple, apple_num)
orange_price = mul_orange_layer.forward(orange, orange_num)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)
price = mul_tax_layer.forward(all_price, tax)
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print(price)
print(dapple_num, dapple, dorange, dorange_num, dtax)

一つ目のprintでの出力は、以下です。

715.0000000000001

上の図で示した通り、最終的な価格715が出力されている事が分かります。

そして、二つ目のprintでの出力は以下となります。

110.00000000000001 2.2 3.3000000000000003 165.0 650

これは、各エッジ(ノードとノードをつなぐ線のこと)における微分の値を示しています。つまり、各要素が1増加した時に、最終的なoutputに与える影響がどれぐらいになるかを示しています。例えば、dapple_num、りんごの個数について、りんごが1つ増えると、最終的なoutputのpriceは110増えるわけですね。(1 x 100 x 1.1) 

 

次回は、ニューラルネットワークで使われるレイヤを実装していきます。

 

それでは、本日は以上でございます。