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

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


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

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

```python
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']) # 中間層２層目の全てのAffineレイヤ作成
        self.layers['Relu2'] = Relu() # 中間層二層目の全てのReluレイヤ作成
        self.lastLayer = SoftmaxWithLoss() # 出力層から損失関数までのレイヤ作成
    
```

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

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

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


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

```python
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']) # 中間層２層目の全ての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だった場合、１次元配列に直す
        
        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で結果を返していくことで実装することができます。