ラビットチャレンジ （深層学習 day1 & day2）

# ◆深層学習 day1

## 0. プロローグ
**＜概要＞**

**識別と生成**
・識別
　　データを目的のクラスに分類する
　　例）犬や猫の画像のデータを分類する
　　p(Ck|x)
　　条件”あるデータxが与えられた”という条件の下でクラスCkである確率
・生成
　　特定のクラスのデータを生成する
　　例）犬らしい画像を生成する
　　p(x|Ck)
　　条件”あるクラスCkが与えられた”という条件の下でデータxの分布

**ニューラルネットワーク（NN）でできること**
　　回帰
　　　　結果予想　（売上予想、株価予想）
　　　　ランキング（競馬順位予想、人気順位予想）
　　分類　
　　　　猫写真の判別
　　　　手書き文字認識
　　　　花の種類分類

**深層学習の実用例**
　　自動売買（トレード）
　　チャットボット
　　翻訳
　　音声解釈
　　囲碁・将棋AI
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/dffce49c-1bac-4416-49c7-c2fa60b4368a.png)


## 1. 入力層～中間層

**＜概要＞**
ニューラルネットワークの全体像
　　「入力層」「中間層」「出力層」の3つに分かれる。

ディープラーニングとは
　　多数の中間層を持つニューラルネットワークを用いて、入力値から目的とする出力値に変換する数学モデルを構築すること。

#### 確認テスト
＜確認テスト1＞
・ディープラーニングは、結局何をやろうとしているか2行以内で述べよ。
　　ルールベースではなくデータから学習して識別や生成等といったタスクを行う。
・次の中のどの値の最適化が最終目的か
　　重み[W], バイアス[b]

＜確認テスト2＞
・以下のニューラルネットワークを紙に書け。
　　入力層：2ノード1層
　　中間層：3ノード2層
　　出力層：1ノード1層

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/f0667dcd-b269-a6f9-e4cf-581b1ba7a7e5.png)

＜確認テスト3＞
・この図式に動物分類の実例を入れてみよう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/1e60d786-78f9-2d0e-9808-c7db28f98e38.png)

＜確認テスト4＞
・この数式をPythonで書け。

　　　　import numpy as np
　　　　u = np.dot(x, W) + b

＜確認テスト5＞
・1-1のファイルから中間層の出力を定義しているソースを抜き出せ。

　　　　z = functions.sigmoid(u)
　　　　print_vec("中間層出力", z)

####演習
コードの確認
＜確認テスト4＞、＜確認テスト5＞で演習

・順伝播のコードの書き方

```python
def forward(network, x):
    
    print("##### 順伝播開始 #####")

    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    # 1層の総入力
    u1 = np.dot(x, W1) + b1
    
    # 1層の総出力
    z1 = functions.relu(u1)
    
    # 2層の総入力
    u2 = np.dot(z1, W2) + b2
    
    # 2層の総出力
    z2 = functions.relu(u2)

    # 出力層の総入力
    u3 = np.dot(z2, W3) + b3
    
    # 出力層の総出力
    y = u3
    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("総入力2", u2)
    print_vec("出力1", z1)
    print("出力合計: " + str(np.sum(z1)))

    return y, z1, z2
```

## 2. 活性化関数
**＜概要＞**
ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。
入力値の値によって、次の層への信号のON/OFFや強弱を定める働きをもつ。

中間層用の活性化関数
　　ReLU関数
　　シグモイド（ロジスティック）関数
　　ステップ関数
出力層用の活性化関数
　　ソフトマックス関数
　　恒等写像
　　シグモイド（ロジスティック）関数


**ステップ関数**
　　しきい値を超えたら発火する関数であり、出力は常に1か0。
　　パーセプトロン（ニューラルネットワークの前身）で利用された関数。
　　0-1間の間を表現できず、線形分離可能なものしか学習できなかった。

```math
f(x) = \left\{ \begin{array}{ll}1 & (x \geq 0) \\0 & (x \lt 0)　\end{array} \right.
```

**シグモイド関数**
　　0-1の間を緩やかに変化する関数で、ステップ関数ではON/OFFしかない状態に対し、信号の強弱を伝えられるようになり、
　　予想ニューラルネットワーク普及のきっかけとなった。
　　大きな値では出力の変化が微小なため、勾配消失問題を引き起こす事があった。

```math
{f(x)=\frac{1}{1+e^{-x}}}
```

**ReLU関数**
　　今最も使われている活性化関数
　　勾配消失問題の回避とスパース化に貢献することで良い成果をもたらしている。

```math
f(x) = \left\{ \begin{array}{ll}x & (x \geq 0) \\0 & (x \lt 0)　\end{array} \right.
```
#### 確認テスト
＜確認テスト1＞
・線形と非線形の違いを簡潔に説明せよ。
　　線形は直線で表される関数(1次関数)
　　非線形は曲線で表される関数(2次以上の関数)

＜確認テスト2＞
・配布されたソースコードより該当する箇所を抜き出せ。

　　z1 = functions.relu(u1)
　　z2 = functions.relu(u2)

####演習
＜確認テスト2＞で演習

・Commonの中にある活性化関数を確認する。

```python
# 中間層の活性化関数
# シグモイド関数（ロジスティック関数）
def sigmoid(x):
    return 1/(1 + np.exp(-x))

# ReLU関数
def relu(x):
    return np.maximum(0, x)

# ステップ関数（閾値0）
def step_function(x):
    return np.where( x > 0, 1, 0) 

# 出力層の活性化関数
# ソフトマックス関数
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x) # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))

# ソフトマックスとクロスエントロピーの複合関数
def softmax_with_loss(d, x):
    y = softmax(x)
    return cross_entropy_error(d, y)
```

## 3. 出力層
**＜概要＞**
・分類では、例えば、犬の確率、猫の確率、ネズミの確率のように各クラスに属する確率を出力。
・ニューラルネットワークの学習において、入力データと訓練データ（正解値）を用意する。
　誤差関数は、出力層の結果と目的変数の正解値を比較し、どのくらい合っているかずれているかを表現する。
　ズレているほど値は大きくなる。
・2値分類ならクロスエントロピー関数、他クラス分類ならカテゴリカルクロスエントロピー関数、
　回帰なら平均二乗誤差や平均絶対誤差等が用いられる。

**誤差関数**
・平均二乗誤差（MSE：Mean Squared Error）
　回帰問題の誤差関数
　　　　${\frac{1}{2}\sum_{n=1}^N(y_n-t_n)^2}$

```python
def mean_squared_error(y, d):
  return np.mean(np.square(y-d)) / 2
```
・クロスエントロピー誤差（交差エントロピー誤差）
　分類問題の誤差関数
　　　　${-\sum_{n=1}^Nt_n\mathbb{log}y_n}$　
　　　　非常によく使う関数

```python
def cross_entropy_error(d, y):
  if y.ndim == 1:
    d = d.reshape(1, d.size)
    y = y.reshape(1, y.size)

  # 
  if d.size == y.size:
    d = d.argmax(axis=1)

  batch_size = y.shape[0]
  return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7) / batch_size

```




**出力層の活性化関数**
・回帰　　　　　→　恒等写像
・ニ値分類　　　→　シグモイド関数
・多クラス分類　→　ソフトマックス関数

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/ce99124a-95c7-b8a8-5c25-24cf312e48c8.png)

#### 確認テスト
＜確認テスト1＞
・なぜ、引き算でなく二乗するか述べよ
　　引き算を行うだけでは、各ラベルでの誤差で正負両方の値が発生し、全体の誤差を正しくあらわすのに都合が悪い
　　2乗してそれぞれのラベルでの誤差を正の値になるようにする。

・下式の1/2はどういう意味を持つか述べよ
　　誤差逆伝播の計算で誤差関数の微分を用いるが、この1/2があることで計算が簡単になる。本質的な意味はない。

＜確認テスト2＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/c368bc55-9ce5-da48-0e81-fd4c4ab6d47f.png)
・①~③の数式に該当するソースコードを示し、一行づつ処理の説明をせよ。
　　①　softmax(x)
　　②　np.exp(x)
　　③　np.sum(np.exp(x)) 

```python
# ソフトマックス関数
def softmax(x):
    if x.ndim == 2:                         ### このifの中はミニバッチで使われる
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x) # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))    ### 本質的な処理はこの部分　②/③
```
＜確認テスト3＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/e9d44d71-a408-b721-5091-2a8fe3aaf8b9.png)
・①~②の数式に該当するソースコードを示し、一行づつ処理の説明をせよ。
　　①　cross_entropy_error(d, y)
　　②　-np.sum(np.log(y[np.arange(batch_size), d] + 1e-7))

```python
# クロスエントロピー
def cross_entropy_error(d, y):
    if y.ndim == 1:
        d = d.reshape(1, d.size)
        y = y.reshape(1, y.size)
        
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if d.size == y.size:
        d = d.argmax(axis=1)
             
    batch_size = y.shape[0]

    ###本質的な処理は最後の1行
    ### 1e-7は対数関数で0の時-∞になるのを防ぐため加算
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
```
####演習
＜確認テスト1＞＜確認テスト2＞で演習


## 4. 勾配降下法
**＜概要＞**
勾配降下法は、ニューラルネットワークを学習させる手法で、以下の3種類などがある。
（勾配降下法を利用してパラメーターを最適化する）
　　①勾配降下法
　　②確率的勾配降下法
　　③ミニバッチ勾配降下法

**①勾配降下法**
数式：
　　$w^{t+1} = w^{(t)} - \epsilon \Delta E$
　　　　ΔE 誤差勾配
　　　　ϵ 学習率


**②確率的勾配降下法**
メリット
　　・データが冗長な場合は計算コストを軽減
　　・毎回毎回違いデータを使って学習するため、望まない局所最適解に収束するリスクを軽減
　　・オンライン学習できる

　　数式：
　　　　$w^{t+1} = w^{(t)} - \epsilon \Delta E_n$

**③ミニバッチ勾配降下法**
メリット
　　確率的勾配降下法のメリットを損なわず、計算機の計算資源を有効利用できる
　　→CPUを利用したスレッド並列化やGPUを利用したSIMD並列化

　　数式：
　　　　$w^{t+1} = w^{(t)} - \epsilon \Delta E_t$
　　　　$E_t = \frac{1}{N_t} \sum_{n \in D_t} E_n$
　　　　$N_t = |D_t|$

**学習率**
学習率が大きいと、最適解を飛び越えて値が大きくなり、学習がうまく進まない。このことを「発散」という。
逆に、学習率が小さいと発散する確率が減るが、学習が終わるまでの時間がかかる。
また、学習率が小さいと、局所最適解（極小値）を求めてしまう可能性がある。

学習率を決定する手法は以下のような方法がある。
　　・Momentum
　　・AdaGrad
　　・Adadelta
　　・Adam

#### 確認テスト
＜確認テスト1＞

・該当するソースコードを探してみよう。
　$w^{t+1} = w^{(t)} - \epsilon \Delta E$

　　network[key] -= learning_rate * grad[key]
　　grad = backward(x, d, z1, y)

＜確認テスト2＞
・オンライン学習とはなにか2行でまとめよ。
　　モデルにその都度データ与えて学習させる学習手法。
　　学習データが入ってくる都度パラメータを更新し、学習を進めていく。

＜確認テスト3＞
・この数式の意味を図に書いて説明せよ。
 　　$w^{t+1} = w^{(t)} - \epsilon \Delta E_t$
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/363be0d2-aec2-45b0-dea8-ff636b3b83b5.png)

####演習
勾配降下法を行っている部分のコードを確認

```python
losses = []
# 学習率
learning_rate = 0.07

# 抽出数
epoch = 1000

# パラメータの初期化
network = init_network()
# データのランダム抽出
random_datasets = np.random.choice(data_sets, epoch)

# 勾配降下の繰り返し
for dataset in random_datasets:
    x, d = dataset['x'], dataset['d']
    z1, y = forward(network, x)
    grad = backward(x, d, z1, y)
    # パラメータに勾配適用
    for key in ('W1', 'W2', 'b1', 'b2'):
        network[key]  -= learning_rate * grad[key]

    # 誤差
    loss = functions.mean_squared_error(d, y)
    losses.append(loss)

```

## 5. 誤差逆伝播法
＜概要＞
直接微分計算をすると、計算量が非常に多くなってしまう。そこで、誤差逆伝播法を利用する。
算出された誤差を、出力層側から順に微分し、前の層前の層へと伝播。最小限の計算で各パラメータでの微分値を解析的に計算する手法。

#### 確認テスト
＜確認テスト1＞
誤差逆伝播法では不要な再帰的処理を避ける事が出来る。既に行った計算結果を保持しているソースコードを抽出せよ。

```python
    # 出力層でのデルタ
    delta2 = functions.d_mean_squared_error(d, y)
    # b2の勾配
    grad['b2'] = np.sum(delta2, axis=0)
    # W2の勾配
    grad['W2'] = np.dot(z1.T, delta2)
    # 中間層でのデルタ
    #delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)

    ## 試してみよう
    delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)

    delta1 = delta1[np.newaxis, :]
    # b1の勾配
    grad['b1'] = np.sum(delta1, axis=0)
    x = x[np.newaxis, :]
    # W1の勾配
    grad['W1'] = np.dot(x.T, delta1)
    
    # print_vec("偏微分_重み1", grad["W1"])
    # print_vec("偏微分_重み2", grad["W2"])
    # print_vec("偏微分_バイアス1", grad["b1"])
    # print_vec("偏微分_バイアス2", grad["b2"])

    return grad
```
＜確認テスト2＞
・2つの空欄に該当するソースコードを探せ。
　　delta2 = functions.d_mean_squared_error(d, y)
　　grad['W2'] = np.dot(z1.T, delta2)

####演習
　　＜確認テスト1＞、＜確認テスト2＞で演習

#◆深層学習 day2

## 0. 復習
＜概要＞
全体像
　　1.入力層に値を入力
　　2. 重み、バイアス、活性化関数で計算しながら値が伝わる
　　3. 出力層から値が伝わる
　　4. 出力層から出た値と正解値から、誤差関数を使って誤差を求める
　　5.誤差を小さくするために重みやバイアスを更新する
　　6. 1〜5の操作を繰り返すことにより、出力値を正解値に近づけていく



####確認テスト
・連鎖律の原理を使い、dz/dxを求めよ。
　　　　z = t$^2$
　　　　t = x + y

　　$\frac{dz}{dx}=\frac{dz}{dt}\times\frac{dt}{dx}=2t\times 1=2(x+y)$

## 1. 勾配消失問題
＜概要＞
誤差逆伝播法が下位層に進んでいくに連れて、勾配がどんどん緩やかになっていく。
そのため、勾配降下法による、更新では下位層のパラメータはほとんど変わらず、訓練は最適値に収束しなくなる。

勾配消失の解決方法
・活性化関数の選択
　　→ReLU関数、微分した結果がx≦0x≦0 の時は 0、x>0の時は、1となる。そのため、勾配消失問題が解消できる。
・重みの初期値設定
　　→Xavierの初期値、Heの初期値
・バッチ正規化
　　→ミニバッチ単位で入力値のデータの偏りを抑制する

####確認テスト
＜確認テスト1＞
・シグモイド関数を微分した時、入力値が0の時に最大値をとる。その値として正しいものを選択肢から選べ。
　　0.25

＜確認テスト2＞
・重みの初期値に0を設定すると、どのような問題が発生するか。簡潔に説明せよ。
　　すべての重みの値が均一に更新されることになるため、多数の重みをもつ意味がなくなり、正しい学習が行えない。

＜確認テスト3＞
・一般的に考えられるバッチ正規化の効果を2点挙げよ。
　　計算が高速で、過学習を抑えることができる。
　　中間層の重みの安定度が増し、勾配消失が起こりづらい。

####演習
「2_2_2_vanishing_gradient_modified.ipynb」を使用して演習

・ガウス分布に基づいて重みを初期化
　活性化関数はシグモイド関数を使用
　※学習が失敗するパターンらしい
　　（ソースコード省略）

　＜結果＞
　　　　学習が行われていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/1dd0d55f-6d97-5e7f-63af-e93f4b8a7ae1.png)

・ガウス分布に基づいて重みを初期化
　活性化関数はRelu関数を使用
　＜結果＞
　　　　あるところまでは学習が進まないがその後急に進む。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/32fc77fe-4fa7-db74-b6c7-152b53a6aae4.png)

・Xavier初期化
　活性化関数はシグモイド関数を使用
　＜結果＞
　　　　	学習がきれいに進んでいる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/e7f0a087-a276-d8af-abb5-dbe8530cba06.png)

・He初期化
　活性化関数はRelu関数を使用
　＜結果＞
　　　　	高速に学習がすすんでいる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/69004dcc-13ea-114e-8fb9-b1fe5644bae6.png)

・バッチ正則化

```python
import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer

# バッチ正則化 layer
class BatchNormalization:
    '''
    gamma: スケール係数
    beta: オフセット
    momentum: 慣性
    running_mean: テスト時に使用する平均
    running_var: テスト時に使用する分散
    '''
    def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
        self.gamma = gamma
        self.beta = beta
        self.momentum = momentum
        self.input_shape = None

        self.running_mean = running_mean
        self.running_var = running_var  
        
        # backward時に使用する中間データ
        self.batch_size = None
        self.xc = None
        self.std = None
        self.dgamma = None
        self.dbeta = None

    def forward(self, x, train_flg=True):
        if self.running_mean is None:
            N, D = x.shape
            self.running_mean = np.zeros(D)
            self.running_var = np.zeros(D)
                        
        if train_flg:
            mu = x.mean(axis=0) # 平均
            xc = x - mu # xをセンタリング
            var = np.mean(xc**2, axis=0) # 分散
            std = np.sqrt(var + 10e-7) # スケーリング
            xn = xc / std
            
            self.batch_size = x.shape[0]
            self.xc = xc
            self.xn = xn
            self.std = std
            self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu # 平均値の加重平均
            self.running_var = self.momentum * self.running_var + (1-self.momentum) * var #分散値の加重平均
        else:
            xc = x - self.running_mean
            xn = xc / ((np.sqrt(self.running_var + 10e-7)))
            
        out = self.gamma * xn + self.beta 
        
        return out

    def backward(self, dout):
        dbeta = dout.sum(axis=0)
        dgamma = np.sum(self.xn * dout, axis=0)
        dxn = self.gamma * dout
        dxc = dxn / self.std
        dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0)
        dvar = 0.5 * dstd / self.std
        dxc += (2.0 / self.batch_size) * self.xc * dvar
        dmu = np.sum(dxc, axis=0)
        dx = dxc - dmu / self.batch_size
        
        self.dgamma = dgamma
        self.dbeta = dbeta

        return dx    
```
＜結果＞
　　　　学習がうまくすすんでいる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/df9bed03-a454-b625-4f39-0a1c4cba2be0.png)

## 2. 学習率最適化手法
＜概要＞
初期の学習率設定方法の指針
　・初期の学習率を大きく設定し、徐々に学習率を小さくしていく
　・パラメータ毎に学習率を可変させる
　　　→学習率最適化手法を利用して、学習率を最適化

学習率最適化手法
　・モメンタム
　・AdaGrad
　・RMSprop
　・Adam

**モメンタム**
誤差をパラメータで微分したものと学習率の積を減算した後、現在の重みに前回の重みを減算した値と慣性の積を加算する

　　　　$V_{t} = \mu V_{t-1} - \epsilon \nabla E$
　　　　$\boldsymbol{w}^{(t+1)}=\boldsymbol{w}^{(t)} + V_{t}$


モメンタムのメリット
局所的最適解にはならず、比較的大域的最適解になりやすい。
谷間についてから最も低い位置（最適値）にいくまでの時間が早い

**AdaGrad**
誤差をパラメータで微分したものと再定義した学習率の積を減算する

　　　　$h_0 = \theta$
　　　　$h_t = h_{t-1}+(\nabla E)^2$
　　　　$\boldsymbol{w}^{(t+1)}=\boldsymbol{w}^{(t)} - \epsilon \frac{1}{\sqrt{h_t}+\theta}\nabla E$




勾配の緩やかな斜面に対して、最小値に近づける。
学習率が徐々に小さくなるので、鞍点問題を引き起こす事があった。

**RMSprop**
AdaGradの改良版
誤差をパラメータで微分したものと再定義した学習率の積を減算する

　　　　$h_t = \alpha h_{t-1}+(1-\alpha)(\nabla E)^2$
　　　　$\boldsymbol{w}^{(t+1)}=\boldsymbol{w}^{(t)} - \epsilon \frac{1}{\sqrt{h_t}+\theta}\nabla E$


局所的最適解にはならず、大域的最適解となる。
ハイパーパラメータの調整が必要な場合が少ない。

**Adam**
・モメンタムの、過去の勾配の指数関数的減衰平均
・RMSPropの、過去の勾配の2乗の指数関数的減衰平均
上記をそれぞれ孕んだ最適化アルゴリズムである。


####確認テスト
＜確認テスト＞
・モメンタム、AdaGrad、RMSpropの特徴をそれぞれ簡潔に説明せよ。
　　モメンタム：大局的局所解になりやすい。
　　AdaGrad：勾配が緩やな場合でも最適解が見つけられる
　　RMSprop：AdaGradの改良版。局所最適解にならず大域的最適解になりやすい。
　　　　　　　ハイパーパラメータの調整が必要な場合が少ない

####演習
「2_4_optimizer_after.ipynb」を使用して演習

・SGD（確率的勾配降下法）
　　　（ソースコード省略）
　＜結果＞
　　　　学習がうまくいっていない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/1d4381ef-968d-e15d-0fc4-7cee024583b5.png)

・Momentum
　　コードの確認

```python
# 慣性
momentum = 0.9

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        v = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            v[key] = np.zeros_like(network.params[key])

        ### モメンタムの更新量の計算 ###################
        v[key] = momentum * v[key] - learning_rate * grad[key]
        network.params[key] += v[key]

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)
```
＜結果＞
　　　　学習がうまくいっていない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/49db70db-ed74-58cb-afce-35c64dea1648.png)

・AdaGrad
　　コードの確認

```python
for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        h = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
            
        # 変更しよう  
        # ===========================================
        if i == 0:
            h[key] = np.zeros_like(network.params[key]) 

        ### AdaGradの更新量の計算 ###########################       
        h[key] = momentum * h[key] - learning_rate * grad[key]
        network.params[key] += h[key]
        # ===========================================
        
        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)
```
＜結果＞
　　　　学習がうまくいっていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/f77f5f2b-345d-09aa-a2df-734ffff3fb96.png)

・RSMprop
　　コードの確認

```python
iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01 
decay_rate = 0.99    ### ハイパーパラメータ##########

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        h = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            h[key] = np.zeros_like(network.params[key])

        ### 更新量の計算 ###########################   
        h[key] *= decay_rate
        h[key] += (1 - decay_rate) * np.square(grad[key])
        network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key]) + 1e-7)

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)                
```
＜結果＞
　　　　学習がうまくいった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/c275073c-7f59-1bc7-7c22-e213ae9c3e3d.png)

・Adam
　　コードの確認

```python
for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        m = {}
        v = {}
    learning_rate_t  = learning_rate * np.sqrt(1.0 - beta2 ** (i + 1)) / (1.0 - beta1 ** (i + 1))    
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            m[key] = np.zeros_like(network.params[key])
            v[key] = np.zeros_like(network.params[key])

        ### 更新量の計算 ###########################               
        m[key] += (1 - beta1) * (grad[key] - m[key])
        v[key] += (1 - beta2) * (grad[key] ** 2 - v[key])            
        network.params[key] -= learning_rate_t * m[key] / (np.sqrt(v[key]) + 1e-7) 
```
＜結果＞
　　　　学習がうまくいった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/141ac81c-1547-8b31-e479-cc1f017a1a22.png)



## 3. 過学習
＜概要＞
過学習の原因
　　重みが大きい値をとることで、過学習が発生することがある。
　　学習させていくと、重みにばらつきが発生する。
　　重みが大きい値は、学習において重要な値であり、重みが大きいと過学習が起こる。

過学習の解決策
　　誤差に対して、正則化項を加算することで、重みを抑制する。
　　過学習がおこりそうな重みの大きさ以下で重みをコントロールし、かつ重みの大きさにばらつきを出す必要がある。

```math
{E_n(\boldsymbol{w}) + \frac{1}{p}\lambda \|\boldsymbol{w}\|_p\\
\|\boldsymbol{w}\|_p = (|w_1|^p+|w_2|^p+\cdots+|w_n|^p)^{\frac{1}{p}}\\
}
　　
　　\\
p=1のとき、L1正則化\\
p=2のとき、L2正則化

```

ドロップアウト
　　過学習の課題はノードの数が多いこと。
　　ドロップアウトとは？
　　ランダムにノードを削除して学習させること
　　メリットとして、データ量を変化させずに、異なるモデルを学習させていると解釈できる。

####確認テスト
＜確認テスト1＞
・機械学習で使われる線形モデル（線形回帰、主成分分析…etc）の正則化は、モデルの重みを制限することで可能となる。
前述の線形モデルの正則化手法の中にリッジ回帰という手法があり、その特徴として正しいものを選択しなさい
（a）ハイパーパラメータを大きな値に設定すると、すべての重みが限りなく0に近づく
（b）ハイパーパラメータを0に設定すると、非線形回帰となる
（c）バイアス項についても、正則化される
（d）リッジ回帰の場合、隠れ層に対して正則化項を加える

　　→(a)

＜確認テスト2＞
・下図について、L1正則化を表しているグラフはどちらか答えよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/988396be-f17e-70af-2649-5a5ccd29d409.png)
　　→右の図（Lasso）

####演習
「2_5_overfiting.ipynb」を使用して演習

訓練データに対する正解率とテストデータに対する正解率に大きな乖離があり過学習が起きている。

```python
import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer


(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)
optimizer = optimizer.SGD(learning_rate=0.01)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)
        
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/aae1adab-81e9-6d3e-6f6c-7b2bb409c6a3.png)

・L2正則化
　＜結果＞
　　　　上記に比べて訓練データの正解率とテストデータの正解率の乖離が小さくなっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/6d43c2d5-9178-b018-3d11-fabdb5fb61d2.png)

・L1正則化
　＜結果＞
　　　　上記２つとはずいぶん雰囲気が異なる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/b1eefa82-e1f9-78a8-f531-27ad688455f6.png)

・ドロップアウト
　＜結果＞
　　　　過学習を抑制しながら学習が進んでいる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/a50f6ee7-a518-363c-daa9-a93043f25820.png)


## 4. 畳み込みニューラルネットワークの概念
＜概要＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/d35f350b-831c-4803-f6a6-325d4a1a4c46.png)

畳み込み層
　　画像とフィルタ(カーネル)の積の総和にバイアスを加えることで算出することで特徴を抽出する。
　　画像の場合、縦、横、チャンネルの3次元のデータをそのまま学習し、次に伝えることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/83ad2cf6-2902-6fd1-6592-7c4d2c40c119.png)

パディング
　　画像の周囲に固定値(例えば0)を埋め込むこと。
　　パディングなしで畳み込みをすると元の画像より小さくなるが、パディングをすることでサイズを維持することが出来る。
　　また、パディングなしの場合、画像の端の方は他の部分と比べて畳み込みに使われる回数が少なくなり、特徴として抽出されにくいが、
　　パディングをすることによってより端の方も特徴を抽出できるようになる。

ストライド
　　フィルタをどれだけずらして畳み込みを行うかということ。

チャンネル
　　空間的な奥行き。例えばカラー画像の場合はRGBの3チャンネルに分けて畳み込みを行う。

プーリング層
　　対象領域の中から1つの値を取得する層。
　　畳み込んだ後に行うことでそれらしい特徴を持った値のみを抽出できる。
　　最大値プーリング、平均値プーリング、など
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/7f21c657-9734-818d-61e0-17e860d8baca.png)

####確認テスト
＜確認テスト＞
・サイズ6×6の入力画像を、サイズ2×2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。なおストライドとパディングは1とする

 　　→7x7

####演習
コードの確認
「2_6_simple_convolution_network.ipynb」を使用して演習

・畳み込み処理

```python
class SimpleConvNet:
    # conv - relu - pool - affine - relu - affine - softmax
    def __init__(self, input_dim=(1, 28, 28), conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        filter_num = conv_param['filter_num']        
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2 * filter_pad) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size / 2) * (conv_output_size / 2))

        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        # レイヤの生成　↓ここで畳み込み処理を行っている##############
        self.layers = OrderedDict()
        self.layers['Conv1'] = layers.Convolution(self.params['W1'], self.params['b1'], conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = layers.Relu()
        self.layers['Pool1'] = layers.Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = layers.Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = layers.Relu()
        self.layers['Affine2'] = layers.Affine(self.params['W3'], self.params['b3'])

        self.last_layer = layers.SoftmaxWithLoss()

```
・高速で演算するための工夫
 　　入力の行列を並び替えている。

```python
# im2colの処理確認
input_data = np.random.rand(2, 1, 4, 4)*100//1 # number, channel, height, widthを表す
print('========== input_data ===========\n', input_data)
print('==============================')
filter_h = 3
filter_w = 3
stride = 1
pad = 0
col = im2col(input_data, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print('============= col ==============\n', col)
print('==============================')
```
========== input_data ===========
 [[[[42. 75. 86. 66.]
   [65. 31. 30. 28.]
   [46. 19. 86. 22.]
   [13. 59. 28. 60.]]]

 [[[15. 79. 46. 36.]
   [22. 26. 45. 75.]
   [54. 25.  9. 12.]
   [67. 56. 18. 87.]]]]

============= col ==============
 [[42. 75. 86. 65. 31. 30. 46. 19. 86.]
 [75. 86. 66. 31. 30. 28. 19. 86. 22.]
 [65. 31. 30. 46. 19. 86. 13. 59. 28.]
 [31. 30. 28. 19. 86. 22. 59. 28. 60.]
 [15. 79. 46. 22. 26. 45. 54. 25.  9.]
 [79. 46. 36. 26. 45. 75. 25.  9. 12.]
 [22. 26. 45. 54. 25.  9. 67. 56. 18.]
 [26. 45. 75. 25.  9. 12. 56. 18. 87.]]


## 5. 最新のCNN
＜概要＞
AlexNet
　　・2012年にILSVRC（ImageNet Large Scale Visual Recognition Challenge）で優勝したモデル。
　　・5層の畳み込み層およびプーリング層、それに続いて2層の全結合層という構造。
　　・過学習を防ぐ施策としてサイズ4096の全結合層の出力にドロップアウトを使用している
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593495/a689442c-f873-8e5b-5ab1-23d2cfd151f6.png)

####確認テスト
　　なし
####演習
　　なし