Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

Deep Learning勉強2 誤差逆伝播法

前回、Deep Learning基礎としてニューラルネットワークや順伝搬法についてまとめました。
elsammit-beginnerblg.hatenablog.com

今回もこちらの本を読み勉強した内容について引き続きまとめて行きたいと思います!!

ゼロから作るDeep Learning ?Pythonで学ぶディープラーニングの理論と実装

新品価格
¥3,740から
(2020/11/6 21:11時点)

内容は誤差逆伝搬についてです!!
ここから急に難しくなり進みが遅くなってきました😨
頑張って進めていきたいと思います!!



■誤差逆伝搬法とは

誤差逆伝搬法とは、
ニューラルネットワークを学習させる際に用いられるアルゴリズム
です。
バックプロパゲーション - Wikipedia


ニューラルネットワークの順方向とは逆方向に重みパラメータを変更しながら計算を行っていき、
最適なパラメータを算出方法になります。
f:id:Elsammit:20201106220455p:plain

順伝搬法は簡単に実装できますが、数値微分をして1つ1つ実行しながら進めるため、時間がかかってしまう欠点がありました。
この誤差逆伝搬法はパラメータの更新が効率よく実施できる方法となっております。

逆伝搬を行うにあたり、計算方法は順方向の微分に相当します。
例えば、こちらのように逆伝搬はEの微分になっております。
f:id:Elsammit:20201106222726p:plain

■足し算の誤差逆伝搬

足し算の誤差逆伝搬では、こちらのようになります。
z = x + yをx、yそれぞれで偏微分すれば確かにこちらのような結果になりますね。
f:id:Elsammit:20201106223159p:plain

pythonで実装するとこちらのようになります。

class AddLayer:
    def __init__(self):
        pass

    def forward(self,x,y):
        return x+y
    
    def backward(self, dout):
        dx = dout*1
        dy = dout*1
        return dx,dy

■乗算の逆伝搬

乗算の誤差逆伝搬では、こちらのようになります。
z = x × yをx、yそれぞれで偏微分すれば確かにこちらのような結果になりますね。
f:id:Elsammit:20201106223608p:plain

pythonで乗算の逆伝搬を実装するとこちらのようになります。

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

■Affine変換層の逆伝搬

そもそもAffine変換とは?ですが、幾何学の分野での行列の積のことを言います。
こちらのことですね。

O = np.dot(X,A)

例えば、Y = X・Wをニューラルネットワーク構築し、逆伝搬させるとこちらの通りになります。
f:id:Elsammit:20201106224227p:plain

このAffine変換層の逆伝搬をpythonで実装するとこちらになります。

class Affine:
    def __init__(self, W, b):
        self.W =W
        self.b = b
        
        self.x = None
        self.original_x_shape = None

        self.dW = None
        self.db = None

    def forward(self, x):
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x

        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        
        dx = dx.reshape(*self.original_x_shape)
        return dx

■ReLU層の逆伝搬

ReLUとは、活性化関数の1つで、
x>0でy=x、x<=0でy=0の式で表されます。
こちらの逆伝搬ですが、
それぞれ、

x > 0の場合
dy/dx

x<=0の場合
0

になります。

pythonでReLUの逆伝搬を実装すると、

class Relu:
    def __init__(self):
        self.mask = None
    
    def forward(self,x):
        self.mask = (x<=0)
        out = x.copy()
        out[self.mask] = 0
        return out
    
    def backward(self,dout):
        dout[self.mask] = 0
        dx = dout
        return dx

■誤差逆伝搬法に対応したニューラルネットワーク

最後にこちらのように2層構成のニューラルネットワークの順伝搬・逆伝搬の構成をpythonで実装します。
今回のニューラルネットワークはこちらのような構成です。
f:id:Elsammit:20201106231049p:plain

ここで、
 ・W1:第1層の重みパラメータ
 ・W2:第2層の重みパラメータ
 ・b1:第1層のバイアス
 ・b2:第2層のバイアス
を示しております。

class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # レイヤの生成
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = Softmax()

    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)
        
    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads


■最後に

今回は誤差逆伝搬についてまとめました。
まだまだ勉強中なので、素人レベルの記事になってしまっております😢
後、もっとわかりやすくまとめておきたいな。。。
頑張って勉強進めます!!