[ゼロから作るDeep Learning]sigmoid関数を使用したニューラルネットワークの重みの初期値

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

# ニューラルネットワークの重みの初期値について
今までニューラルネットワークの重みの初期値は、randomメソッドを使って乱数を出していましたが、それだと学習の成功具合に幅ができてしまいます。

重みの初期値と、ニューラルネットワークの学習は非常に関係が強く、適切な初期値の場合は良い学習結果になるし、不適切な初期値だと悪い学習結果になってしまいます。

そこで今回はsigmoid関数を使用したニューラルネットワークに適切な重みの初期値を設定してあげる方法を実装していきたいと思います。

# Xavierの初期値
sigmoid関数を使ったニューラルネットワークに一番適しているとされる重みの初期値はXavierの初期値というものです。

```python
scale = np.sqrt(1.0 / all_size_list[idx - 1]) 
scale * np.random.randn(all_size_list[idx-1], all_size_list[idx])
```
Xavierの初期値は、1 ÷ 前の層のノード数にルートをつけたものを計算し、それをrandomの乱数にかけることで作成することができます。

下に、Heの初期値やXavierの初期値を使用したニューラルネットワークの見本を載せておきます。

```python
#重みの初期値応用・Weight decayを実装したニューラルネットワーク
class MutiLayerNet:
    def __init__(self,input_size,hiden_size_list,output_size,
                activation='relu',weight_init_std='relu',weight_decay_lambda=0):#weight_decay_lambdaが大きいほど強力
        self.input_size = input_size#入力層のニューロン数
        self.output_size = output_size#出力層のニューロン数
        self.hiden_size_list = hiden_size_list#中間層の各層のニューロン数
        self.hiden_layer_num = len(hiden_size_list)#中間層の層数
        self.weight_decay_lambda = weight_decay_lambda#Weight decayの強さ設定
        self.params = {}#パラメータを入れる
        
        #重みの初期化
        self.__init_weight(weight_init_std)
        
        #レイヤ作成
        activation_layer = {'sigmoid': Sigmoid,'relu': Relu}
        self.layers = OrderedDict()#レイヤを保存する順番付き辞書
        for idx in range(1, self.hiden_layer_num+1):#中間層の数だけ繰り返す
            self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
                                                      self.params['b' + str(idx)])
            self.layers['Activation_function' + str(idx)] = activation_layer[activation]()#Relu関数レイヤを選択

        idx = self.hiden_layer_num + 1#出力層前のAffineレイヤ作成
        self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
            self.params['b' + str(idx)])

        self.last_layer = SoftmaxWithLoss()#出力層から損失関数までのレイヤ
        
    def __init_weight(self, weight_init_std):#重み・バイアスの初期化をするメソッド
        all_size_list = [self.input_size] + self.hiden_size_list + [self.output_size]#全ての層のニューロン数を保持
        for idx in range(1, len(all_size_list)):
            scale = weight_init_std#ランダム重みにかける数が入る
            if str(weight_init_std).lower() in ('relu', 'he'):#relu関数を使う場合heの初期値を作成
                scale = np.sqrt(2.0 / all_size_list[idx - 1])  # ReLUを使う場合に推奨される初期値
            elif str(weight_init_std).lower() in ('sigmoid', 'xavier'):#sigmoid関数を使う場合xavierの初期値を作成
                scale = np.sqrt(1.0 / all_size_list[idx - 1])  # sigmoidを使う場合に推奨される初期値

            self.params['W' + str(idx)] = scale * np.random.randn(all_size_list[idx-1], all_size_list[idx])#重み初期化
            self.params['b' + str(idx)] = np.zeros(all_size_list[idx])#バイアス初期化
            
    def predict(self, x):#ニューラルネットワークの順伝播処理
        for layer in self.layers.values():
            x = layer.forward(x)

        return x
    
    def loss(self, x, t):#ニューラルネットワークから損失関数までの順伝播処理＋Weight decayの処理
        y = self.predict(x)

        weight_decay = 0
        for idx in range(1, self.hiden_layer_num + 2):#Weight decayの処理で各層の重みの二乗を総和して下の処理をして総和する。
            W = self.params['W' + str(idx)]
            weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)

        return self.last_layer.forward(y, t) + weight_decay

    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)

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

    def numerical_gradient(self, x, t):#数値微分
        loss_W = lambda W: self.loss(x, t)

        grads = {}
        for idx in range(1, self.hidden_layer_num+2):
            grads['W' + str(idx)] = slopeing_grad(loss_W, self.params['W' + str(idx)])
            grads['b' + str(idx)] = slopeing_grad(loss_W, self.params['b' + str(idx)])

        return grads

    def gradient(self, x, t):#誤差逆伝播法
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.last_layer.backward(dout)

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

        # 勾配の回収
        grads = {}
        for idx in range(1, self.hiden_layer_num+2):#weight decayの処理もする
            grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
            grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db

        return grads
```