[ゼロから作るDeep Learning]誤差逆伝播法でニューラルネットワークに逆伝播処理を実装


はじめに

この記事はゼロから作るディープラーニング 6章誤差逆伝播法を自分なりに理解して分かりやすくアウトプットしたものです。
文系の自分でも理解することが出来たので、気持ちを楽にして読んでいただけたら幸いです。
また、本書を学習する際に参考にしていただけたらもっと嬉しいです。

ニューラルネットワークに逆伝播処理を実装

前回の記事で、誤差逆伝播法をするために必要なパーツは全て実装することができました。そこで今回はそのパーツを実際に使って誤差逆伝播法を行ってニューラルネットワークに逆伝播処理を実装したいと思います。

1, パラメータの初期化・レイヤの作成

from collections import OrderedDict
class LayerNet:
    def __init__(self, input_size, hiden_size, output_size, weight_init_std = 0.01):
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hiden_size)
        self.params['b1'] = np.zeros(hiden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hiden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

        #レイヤ生成
        self.layers = OrderedDict()#順番付きの辞書
        self.layers['Affine1'] = Affine(self.params['W1'],self.params['b1']) # 中間層一層目のAffineレイヤ全て作成
        self.layers['Relu1'] = Relu()#中間層一層目の全てのReluレイヤ作成
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) # 中間層2層目の全てのAffineレイヤ作成
        self.layers['Relu2'] = Relu() # 中間層二層目の全てのReluレイヤ作成
        self.lastLayer = SoftmaxWithLoss() # 出力層から損失関数までのレイヤ作成

パラメータの初期化については、数値微分のニューラルネットワークの際と同じなのでそちらを参照してください。

次のレイヤ生成の部分では、まず最初にレイヤを保管する順番付きの辞書OrderDictを作成して、それをインスタンス変数に保存します。

次に作成したOrderDictのなかにレイヤをニューラルネットワークの左側から順番に入れていきます。
OrderDictの中に入れることで、レイヤは順番で管理されます。そうなることで、for文を使いレイヤを一つずつ取り出していけば順伝播処理を、順番を逆にしてfor文を使うことで逆伝播処理を簡単に行うことができます。

2, predictメソッド・lossメソッド・accuracyメソッド・レイヤを使った勾配式を実装する

from collections import OrderedDict
class LayerNet:
    def __init__(self, input_size, hiden_size, output_size, weight_init_std = 0.01):
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hiden_size)
        self.params['b1'] = np.zeros(hiden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hiden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

        #レイヤ生成
        self.layers = OrderedDict()#順番付きの辞書
        self.layers['Affine1'] = Affine(self.params['W1'],self.params['b1']) # 中間層一層目のAffineレイヤ全て作成
        self.layers['Relu1'] = Relu()#中間層一層目の全てのReluレイヤ作成
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) # 中間層2層目の全てのAffineレイヤ作成
        self.layers['Relu2'] = Relu() # 中間層二層目の全てのReluレイヤ作成
        self.lastLayer = SoftmaxWithLoss() # 出力層から損失関数までのレイヤ作成

    def predict(self, x): # ニューラルネットワークの中間層までの順伝播の処理を行う
        for layer in self.layers.values():
            x = layer.forward(x) # 順番付けされたレイヤたちをfor文で順番ごとに処理を行う

        return x

    def loss(self, x, t): # ニューラルネットワークから損失関数までの順伝播処理を行う
        y = self.predict(x) # 予測データを出す

        return self.lastLayer.forward(y,t) # 出力層から損失関数までの順伝播処理を行う

    def accuracy(self,x, t): # 正答率を出す
        y = self.predict(x)
        y = np.argmax(y, axis=1) # 予測データから正解予想のラベルを出す
        if t.ndim != 1 : t = np.argmax(t, axis=1) # 正解データがone_hotだった場合、1次元配列に直す

        accuracy = np.sum(y == t) /float(x.shape[0]) # 正答率を出す

        return accuracy

    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'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db

        return grads

predictメソッドはニューラルネットワークの順伝播処理を行います。
先ほども少し書きましたが、順伝播処理はOrderDictにfor文を使い順番にforwardメソッドを行い、returnで出力値を返すことで行うことができます。

lossメソッドではpredictメソッドプラス出力層の活性化関数と損失関数を行うので、predictメソッドをした後に、その結果をlastLayerのforwardに使用することで実装が可能です。

accuracyは数値微分の時と同じなのでそちらを参照してください。

レイヤを使用した勾配式はニューラルネットワークの逆伝播処理を行えばいいので、まずOrderDictの順番をreverseメソッドで反対にした後順番にfor文でbackwardを行い、returnで結果を返していくことで実装することができます。