Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Python DeepLearningに再挑戦 14 誤差逆伝播法 活性化関数レイヤの実装

概要

Python DeepLearningに再挑戦 14 誤差逆伝播法 活性化関数レイヤの実装

参考書籍

活性化関数レイヤの実装

ReLUレイヤ

y = x (x>0), 0(x<=0)

上記の式のxに関する微分は、以下の通り

∂y / ∂x = 1 (x >0), 0 (x<=)

計算グラフで書くと以下のような感じ。

f:id:riikku:20161224080922p:plain

python で実装すると以下のようなコードになる。

class Relu:
    def __init__(self):# インスタンス変数 maskの初期化
        self.mask = None
        
    def forward(self,x):#信号の大きさをxとして引数として渡す
        self.mask = (x <=0 ) #  x <=0 の場合、True, それ以外はFalse を渡す
        out = x.copy() #  xの値(配列)をコピーする
        out[self.mask] = 0 # True の要素の値のみを0のに変換する
        
        return out
    
    def backward(self, dout):
        dout[self.mask] = 0 # x<=0がtrue のものは、逆伝播の微分のdoutも0で流す。それ以外はそのまま流す。
        dx = dout
        
        return dx

# forwardはわかりづらい。以下のような具体例で見ると理解できる。

import numpy as np
x = np.array([[1.0, -0.5], [-2.0, 3.0]])
print(x)
[[ 1.  -0.5]
 [-2.   3. ]]

mask = (x<=0)
mask
array([[False,  True],
       [ True, False]], dtype=bool)

out = x.copy()
out
array([[ 1. , -0.5],
       [-2. ,  3. ]])

out[mask] = 0
out
array([[ 1.,  0.],
       [ 0.,  3.]])

Sigmoidレイヤ

y = 1 / 1+exp(-x)

計算グラフで表すと以下の通り。

f:id:riikku:20161224085224p:plain

逆伝播も同様に計算グラフで記載していく。

ステップ1

/ ノードの微分は、∂y/∂x = -1/ x**2 = -y**2となるらしい。ここは途中の計算式がさっぱりわからん( ´Д`)y━・~~

ステップ2

合算ノードは、上流の値をそのまま流すだけ。

ステップ3

exp ノードは、y = exp(x)で、その微分は、以下の通りの式になる。

∂y / ∂x = exp(x) # 変わらないのかな〜

ステップ4

x ノードは準伝播時の値を、ひっくり返して乗算する。ここでは、-1を乗算するする。

f:id:riikku:20161224090606p:plain

なるほど〜。シグモイド関数の中身は変更しないから、sigmoidノードとして、最初と最後のみ実装すればOK!

f:id:riikku:20161224091139p:plain

また、さらに整理すると以下のようになるらしい。

∂L/∂y * (y**2) * exp(-x) 
= ∂L/∂y * ( 1/ (1+exp(-x))**2 ) * exp(-x) # y= 1/1+exp(-x) を代入している。
= ∂L/∂y * ( 1/ 1+exp(-x) ) * ( 1/ 1+exp(-x) ) * exp(-x) 
= ∂L/∂y * ( 1/ 1+exp(-x) ) * ( exp(-x) / 1+exp(-x) ) 
= ∂L/∂y * y(1-y) # exp(-x) / 1+exp(-x)を 1-y に変換している。

お〜〜〜凄いこと考える人いるなぁ(^ ^;) sigmoidレイヤの逆伝播は、順伝播の出力(y)だけから計算可能という理屈になる。

f:id:riikku:20161224091902p:plain

Sigmoidレイヤをpythonで実装すると以下の通り。

class Sigmoid:
    def __init__(self): # インスタンス変数の初期化, outは、backwardでの計算でも使う。
        self.out = None
        
    def forwad(self, x):
        out = 1/ (1+np.exp(-x))#シグモイド関数で出力を計算
        self.out = out
        
        return out
    
    def backward(self,dout):
        dx = dout * (1.0 - self.out) * self.out #出力から、逆伝播の微分を計算する
        
        return dx

シンプル!

次の記事は、Affine / Softmax レイヤか〜。