[ゼロから作るDeep Learning]学習機能を実装したニューラルネットワークの実装

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

#学習機能を実装したニューラルネットワーク
今回は分類問題を解く専用の学習機能付きニューラルネットワークのクラスを実装して行きたいと思います。

今回の実装の流れを簡単に説明します。

**1,**　インスタンス変数でパラメータとハイパーパラメータを実装

**2,**　予測データを出力するニューラルネットワークの処理を行うクラスメソッド**predict**の実装

**3,**　ニューラルネットワークから損失関数までの処理を行うクラスメソッド**loss**の実装

**4,**　予測データの正答率を出すメソッド**accuracy**の実装

**5,**　学習を行うための**勾配式**を実装


#1, インスタンス変数でパラメータとハイパーパラメータを実装

```python
class MNST_net:# 中間層は今回一層
    
    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)
```

最初のクラスメソッドでハイパーパラメータとパラメータを実装します。

**__init__(self):**のカッコの中には、input_size(入力層のニューロン数),hiden_size(中間層のニューロン数),output_size(出力層のニューロン数)、weight_init_std(重みを小さなな値にして過学習を抑える)などの人間が設定しないといけないハイパーパラメータを記述します。

重みやバイアスなどのパラメータの実装(初期化)はparamsと言う辞書をインスタンス変数として作成し、その中に各層ごと各パラメータの種類ごとに分けて実装していきます。

重みは、np.random.randnを使って(入力信号✖︎ニューロン数)のランダムな整数が入った配列を作成し、weight_init_stdをかけることで実装(初期化)できます。

バイアスは初期値をゼロにしないといけないので、np.zerosでニューロン数のゼロが入った一次元配列を作成することで実装（初期化）できます。


#2, 予測データを出力するニューラルネットワークの処理を行うクラスメソッドpredictの実装

```python
class MNST_net:# 中間層は今回一層
    
    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)
    
    def predict(self, x):# ニューラルネットワークの処理
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        
        a1 = np.dot(x, W1) + b1# 入力層から隠れ層１段の処理
        z1 = sigmoid_function(a1)
        
        a2 = np.dot(z1, W2) + b2
        y = softmax_function_pro(a2)
        return y
```

初期化されたパラメータをそれぞれ変数に入れて、各層のニューラルネットワークのニューロンの処理を実装していきます。

#3, ニューラルネットワークから損失関数までの処理を行うクラスメソッドlossの実装

```python
class MNST_net:# 中間層は今回一層

    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)

    def predict(self, x):# ニューラルネットワークの処理
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']

        a1 = np.dot(x, W1) + b1# 入力層から隠れ層１段の処理
        z1 = sigmoid_function(a1)

        a2 = np.dot(z1, W2) + b2
        y = softmax_function_pro(a2)
        return y

    def loss(self, x, t):#  NNから損失関数までの処理
        y = self.predict(x)
        return cross_entropy_errors_label(t, y)
```

predictメソッドで予測データを取得して変数yに入れ、それを交差エントロピー誤差の引数に入れてreturnで返します。

#4, 予測データの正答率を出すメソッドaccuracyの実装

```python
class MNST_net:# 中間層は今回一層

    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)

    def predict(self, x):# ニューラルネットワークの処理
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']

        a1 = np.dot(x, W1) + b1# 入力層から隠れ層１段の処理
        z1 = sigmoid_function(a1)

        a2 = np.dot(z1, W2) + b2
        y = softmax_function_pro(a2)
        return y

    def loss(self, x, t):#  NNから損失関数までの処理
        y = self.predict(x)
        return cross_entropy_errors_label(t, y)

    def accuracy(self, x, t):# 正答率
        y = self.predict(x)
        y = np.argmax(y, axis = 1)# 一番確率の高いラベルのインデックスを出す
#         t = np.argmax(t, axis = 1)# 正解ラベルのインデックスを出す one-hot法
        
        accuracy = (y == t).sum() / float(x.shape[0])
        return accuracy
```

predictで予測データを出して、一番値が大きいindexをnp.argmaxで出し、それと正解データを比べて正解したものの合計 / 全体数　で正答率を出します。

#5, 学習を行うための勾配式を実装

```python
class MNST_net:# 中間層は今回一層

    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)

    def predict(self, x):# ニューラルネットワークの処理
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']

        a1 = np.dot(x, W1) + b1# 入力層から隠れ層１段の処理
        z1 = sigmoid_function(a1)

        a2 = np.dot(z1, W2) + b2
        y = softmax_function_pro(a2)
        return y

    def loss(self, x, t):#  NNから損失関数までの処理
        y = self.predict(x)
        return cross_entropy_errors_label(t, y)

    def accuracy(self, x, t):# 正答率
        y = self.predict(x)
        y = np.argmax(y, axis = 1)# 一番確率の高いラベルのインデックスを出す
#         t = np.argmax(t, axis = 1)# 正解ラベルのインデックスを出す one-hot法

        accuracy = (y == t).sum() / float(x.shape[0])
        return accuracy

     def slopeing_grad_net(self, x, t):# 初期パラメータ勾配式
        loss_c = lambda W: self.loss(x, t) # 関数化させないといけなかった 引数Wはダミー
        
        grads = {}
        grads['W1'] = slopeing_grad(loss_c,self.params['W1'])
        grads['b1'] = slopeing_grad(loss_c,self.params['b1'])
        grads['W2'] = slopeing_grad(loss_c,self.params['W2'])
        grads['b2'] = slopeing_grad(loss_c,self.params['b2'])
        
        return grads
```

ニューラルネットワークから損失関数までの処理を変数loss_cに入れて、それと初期化されたパラメータを各層・各種類ごとに前回作成したslopeing_gradで勾配を求めて、それをgradsと言う辞書にまとめてreturnで返します。