深層学習

#Day1
## 入力層ー中間層
![IMG_1833 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e9776e8d-f819-c8c9-6cd7-1c953f83cfc9.jpeg)

```math:
x_i：入力\\
w_i：重み\\
b：バイアス\\
f：活性化関数\\
z：出力\\
```
### 配列式
```math:
\boldsymbol{W} = \begin{pmatrix} w_{1} \\ w_{2} \\ \vdots \\ w_{n} \end{pmatrix} \qquad \boldsymbol{x} = \begin{pmatrix} x_{1} \\ x_{2} \\ \vdots \\ x_{n} \end{pmatrix}\\
u = w_{1}x_{1} + w_{2}x_{2} +\dots + w_{n}x_{n} + b\\
u = \boldsymbol{W}\boldsymbol{x} + b
```
### コード
```python:
W = np.array([[0.1], [0.2]])
b = 0.5
x = np.array([2, 3])
u = np.dot(x, W) + b
z = functions.relu(u)
```
![スクリーンショット 2021-05-06 14.49.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/89191098-31e5-6804-968d-ad9113ebd581.png)

### 考察
重回帰分析と似ている。
異なるのが、活性化関数の導入

## 活性化関数
次の層への出力の大きさ・信号ON/OFFを決める非線形関数（関数の図が直線でない）
### 使い分け
#### 中間層用
- ReLU関数（主流）
- Sigmoidシグモイド関数
- Step ステップ関数

#### 出力層用
- Softmaxソフトマックス関数（クラス分類２つ以上の場合）
- Sigmoidシグモイド関数
- 恒等写像関数（f(x) = x)

### Step ステップ関数
![スクリーンショット 2021-05-06 15.20.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/f1d9ba36-cf88-372e-5b46-6bac01691882.png)
パーセプトロンで利用される関数
<strong>課題</strong>：０〜１表現できず、線形分離可能なものしか学習できない

### Sigmoidシグモイド関数
![スクリーンショット 2021-05-06 15.26.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/8c4a1df2-2520-4033-412c-7cd279cf4788.png)
ON/OFFしかできないステップ関数に対し、信号の強弱を調整できる関数
<strong>課題</strong>：大きな値では出力が微小のため、勾配消失問題が落ちやすい

### ReLU関数
![スクリーンショット 2021-05-06 15.31.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/fdc8b39a-c750-eecc-774a-70824093ea6e.png)
勾配消失問題を回避、スパース化（影響が少ない重みを0に）を貢献

### Softmaxソフトマックス関数
![スクリーンショット 2021-05-07 10.38.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/012f488d-14b8-d3d0-5a0e-9eda807b3a6a.png)
### コード
```python:
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))
```
## 出力層
### 誤差関数
出力層のアウトプット（d）とラベル（y）の間、どのぐらい差があるか計算する関数

####二乗誤差
![スクリーンショット 2021-05-07 10.27.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/18cc5159-71fd-cb07-dd10-303a62bb5adb.png)
※1/2あるのは、微分する際、綺麗な結果になるため
※二乗にするのは、マイナス距離を排除するため
##### コード
```python:
def mean_squared_error(d, y):
    return np.mean(np.square(d - y)) / 2
```
####交差エントロピー
![スクリーンショット 2021-05-07 10.35.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/65503802-e3f4-49a1-ace2-3e63ea497998.png)
##### コード
```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]
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
```
### 出力層の活性化関数
#### 中間層と出力層の違い
- 中間層：しきい値の前後で信号の強弱を調整
- 出力層：信号の大きさをそのままに変更したり、０〜１を限定し、総和を１にしたりする

#### 活性化関数＋誤差関数
![スクリーンショット 2021-05-07 10.52.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/81452681-e49d-7271-8c1f-5f5013868ead.png)

## 勾配降下法
### 深層学習の目的
学習を通して、誤差を最小にするネットワックを作る
いわば、誤差関数E(W)を最小化するパラメータWを探すこと

### 勾配降下法
![スクリーンショット 2021-05-07 11.06.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b127732a-92fe-d461-ac94-f08f6575b6ac.png)

```math:
{\epsilon}学習率
```
![スクリーンショット 2021-05-07 11.07.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/2366ac63-7eb7-f179-8bac-868c19d78b58.png)
#### 学習率が大きすぎる
最小値にいつまでも、たどり着かず、発散してしまう
![スクリーンショット 2021-05-07 11.11.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/3e83499b-96ef-76bc-20a1-2e29c8dca622.png)
#### 学習率が小さすぎる
学習する時間がかかる
![スクリーンショット 2021-05-07 11.13.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a544cfab-2a4d-fcb1-1d2b-c11c76356056.png)
#### 学習率の決定、収束性向上のためのアルゴリズム
- Momentum
- AdaGrad
- AdaDelta
- Adam

### 確率的勾配降下法
![スクリーンショット 2021-05-07 11.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/4d80d788-e21e-96de-5655-c81dd597f877.png)
データ全体の誤差の代わり、ランダムに抽出したデータの誤差で計算する
以下のメリットがある
- 計算コストを軽減
- オンライ学習できる
- 局所的な最小値に収束するリスクを軽減
※オンライ学習：データ全体代わりに、随時、個別のデータを学習する

### ミニバッチ勾配降下法
![スクリーンショット 2021-05-07 11.29.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/bf5ad907-596a-192d-9b75-6fdfe68a6ecb.png)
ランダムに分割したデータの集合（ミニバッチ）Dtに属するサンプルの平均誤差で計算する
並列計算を活用できるため、計算資源（CPU,GPU）を有効利用できる
## 数値微分
![スクリーンショット 2021-05-07 11.34.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/7f0dceca-0f05-e14b-cdd7-d85fa2b8341c.png)
![スクリーンショット 2021-05-07 11.34.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d7580086-08ee-85ee-210b-7a865ebfc8a6.png)
微小な数値を生成し擬似的に微分を計算する手法
どんな複雑な関数でも計算できる、
反面、各パラメータをそれぞれE(w+h)とE(w-h)を計算しないといけないため、
計算コストが高いすぎる
しかし、誤差逆伝播の結果を検証するのによく使われている

```python:
def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x + h)の計算
        x[idx] = tmp_val + h
        fxh1 = f(x)

        # f(x - h)の計算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        # 値を元に戻す
        x[idx] = tmp_val

    return grad
```
## 誤差逆伝播法
![スクリーンショット 2021-05-07 11.34.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/dc2e426b-734a-7f59-fb62-c1f592fee69d.png)
を計算するため、
算出された誤差を、出力層側から順に微分し、前の層へ、前の層へ伝播する
最小限の計算で各パラメータでの微分値を解析的に計算する手法

![スクリーンショット 2021-05-07 15.10.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a52bccda-ed6f-3b88-d20a-5e12c0e92b6d.png)
![IMG_1834 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d9f7fae5-535a-ee78-1c30-ece8a0779ca1.jpeg)

## 実装
### 初期設定

```python:
def init_network():
    # print("##### ネットワークの初期化 #####")
    network = {}
    nodesNum = 10
    network['W1'] = np.random.randn(2, nodesNum)
    network['W2'] = np.random.randn(nodesNum)
    network['b1'] = np.random.randn(nodesNum)
    network['b2'] = np.random.randn()

    return network
```
### 順伝播

```python:
def forward(network, x):
    # print("##### 順伝播開始 #####")
    
    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    u1 = np.dot(x, W1) + b1
    z1 = functions.relu(u1)
    
    u2 = np.dot(z1, W2) + b2
    y = u2
    
    return z1, y
```
### 逆伝播

```python:
# print("\n##### 誤差逆伝播開始 #####")    

    grad = {}
    
    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']

    # 出力層でのデルタ
    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 = delta1[np.newaxis, :]
    # b1の勾配
    grad['b1'] = np.sum(delta1, axis=0)
    x = x[np.newaxis, :]
    # W1の勾配
    grad['W1'] = np.dot(x.T, delta1)

    return grad
```
### b1、b2の計算について
>because the network is designed to process examples in (mini-)batches, and you therefore have gradients calculated for more than one example at a time. The sum is squashing the results down to a single update. This would be easier to confirm if you also showed update code for weights.

https://datascience.stackexchange.com/questions/20139/gradients-for-bias-terms-in-backpropagation

### 学習

```python:
def f(x):
    y = 3 * x[0] + 2 * x[1]
    return y
# サンプルデータを作成
data_sets_size = 100000
data_sets = [0 for i in range(data_sets_size)]

for i in range(data_sets_size):
    data_sets[i] = {}
    # ランダムな値を設定
    data_sets[i]['x'] = np.random.rand(2)
    
    ## 試してみよう_入力値の設定
    # data_sets[i]['x'] = np.random.rand(2) * 10 -5 # -5〜5のランダム数値
    
    # 目標出力を設定
    data_sets[i]['d'] = f(data_sets[i]['x'])
    
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)

print("##### 結果表示 #####")    
lists = range(epoch)


plt.plot(lists, losses, '.')
# グラフの表示
plt.show()
```
![スクリーンショット 2021-05-07 16.34.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/17479000-b801-82d9-f195-91b646e35231.png)

#Day2
## 勾配消失問題
層が重ねれば重ねるほど、勾配降下法を用いると、
下位層のパラメータを更新するための勾配が小さくなりすぎ、
やがて、下位層のパラメータが更新できなくる問題
![スクリーンショット 2021-05-14 16.45.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a7571bfb-f0f8-afcf-87d2-ea89ebab648c.png)
### シグモイド関数の問題
![スクリーンショット 2021-05-14 16.49.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/ea1f1c61-0c3b-878e-88a8-e1b3310ecdab.png)
シグモイド関数を微分した最大値がたった0.25
![スクリーンショット 2021-05-14 16.51.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/82f5ded3-4e10-9785-ee77-07f49aecf88e.png)
上記の式で勾配を計算すると何回も0.25を掛けたら、段々小さくなり、勾配消失が発生してしまう
### 勾配消失を解決する方法
#### 活性化関数の選択
![スクリーンショット 2021-05-14 17.00.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/7665c0e7-8100-83d5-5a5b-5753a11eed32.png)
ReLU活性化関数の最大値が1で、最小値が0により、
勾配消失問題を抑止でき、スパース化に貢献できる

#### 重みの初期値設定

y = w1x1 + w2x2 + ... + wNxN + b
重みw1,w2,...は普段、正規分布で初期化されているが、
yに対する活性関数の微分を大きすぎない（勾配爆発しない）、小さすぎない（勾配消失しない）ように、yの分散を重みの分散と近くように保つ手法。
##### Xavier
```math:
\frac{1}{\sqrt{n}}
```

```python:
network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / (np.sqrt(input_layer_size))
```
######利用する活性化関数
- ReLU
- Sigmoid
- Tanch(双曲線正接)

https://prateekvjoshi.com/2016/03/29/understanding-xavier-initialization-in-deep-neural-networks/
##### He
```math:
\frac{2}{\sqrt{n}}
```

```python:
network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / (np.sqrt(input_layer_size)) * np.sqrt(2)
```
######利用する活性化関数
- ReLU

##### 考察

重みの初期値が0にすると、勾配消失が発生
1にすると、勾配爆発が発生
どの場合でも、うまく学習できない

#### バッチの正規化
正規分布のように、入力値の偏りを調整する手法

##### 数式
![スクリーンショット 2021-05-17 13.59.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/624cd947-3254-eda1-f46f-ed3a2bda0d82.png)

![スクリーンショット 2021-05-17 13.59.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/e344da2c-fd21-b868-5270-eb36ab02cb0e.png)

![IMG_1848 (1).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/edf14cd6-e227-c5f3-b93c-afbadb6a4432.jpeg)

##### コード
###### Forward
```python:
def batchnorm_forward(x, gamma, beta, eps):

  N, D = x.shape

  #step1: calculate mean
  mu = 1./N * np.sum(x, axis = 0)

  #step2: subtract mean vector of every trainings example
  xmu = x - mu

  #step3: following the lower branch - calculation denominator
  sq = xmu ** 2

  #step4: calculate variance
  var = 1./N * np.sum(sq, axis = 0)

  #step5: add eps for numerical stability, then sqrt
  sqrtvar = np.sqrt(var + eps)

  #step6: invert sqrtwar
  ivar = 1./sqrtvar

  #step7: execute normalization
  xhat = xmu * ivar

  #step8: Nor the two transformation steps
  gammax = gamma * xhat

  #step9
  out = gammax + beta

  #store intermediate
  cache = (xhat,gamma,xmu,ivar,sqrtvar,var,eps)

  return out, cache
```
###### Backward
```python:
def batchnorm_backward(dout, cache):

  #unfold the variables stored in cache
  xhat,gamma,xmu,ivar,sqrtvar,var,eps = cache

  #get the dimensions of the input/output
  N,D = dout.shape

  #step9
  dbeta = np.sum(dout, axis=0)
  dgammax = dout #not necessary, but more understandable

  #step8
  dgamma = np.sum(dgammax*xhat, axis=0)
  dxhat = dgammax * gamma

  #step7
  divar = np.sum(dxhat*xmu, axis=0)
  dxmu1 = dxhat * ivar

  #step6
  dsqrtvar = -1. /(sqrtvar**2) * divar

  #step5
  dvar = 0.5 * 1. /np.sqrt(var+eps) * dsqrtvar

  #step4
  dsq = 1. /N * np.ones((N,D)) * dvar

  #step3
  dxmu2 = 2 * xmu * dsq

  #step2
  dx1 = (dxmu1 + dxmu2)
  dmu = -1 * np.sum(dxmu1+dxmu2, axis=0)

  #step1
  dx2 = 1. /N * np.ones((N,D)) * dmu

  #step0
  dx = dx1 + dx2

  return dx, dgamma, dbeta
```
https://kratzert.github.io/2016/02/12/understanding-the-gradient-flow-through-the-batch-normalization-layer.html

https://towardsdatascience.com/batch-normalisation-explained-5f4bd9de5feb
##### 効果
- 多少ノイズがInputに追加されたため、過学習の抑止を期待できる。
- 学習率を大きく設定しても、Inputが正規化されたため、勾配消失・爆発を抑えられる。

## 学習率最適化手法
### 勾配降下法

```math:
w^{(t+1)} = w^{(t)} - \epsilon\Delta E
```
### モメンタム

```math:
V_{t} = \mu V_{t-1}  - \epsilon\Delta E\\
w^{(t+1)} = w^{(t)} + V_{t}\\
\mu :慣性
```

```python:
class Momentum:
    def __init__(self, learning_rate=0.01, momentum=0.9):
        self.learning_rate = learning_rate
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grad):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.learning_rate * grad[key] 
            params[key] += self.v[key]
```
#### 特徴
- 局所的最適解にならず、大局的最適解となる
- 最適値に行くまでの時間が早い

### AdaGrad
```math:
h_{t} = h_{t-1}  + (\Delta E)^2\\
w^{(t+1)} = w^{(t)}  - \epsilon \frac{1}{\sqrt{h_{t}}} \Delta E\\
```

```python:
class AdaGrad:
    def __init__(self, learning_rate=0.01):
        self.learning_rate = learning_rate
        self.h = None
        
    def update(self, params, grad):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grad[key] * grad[key]
            params[key] -= self.learning_rate * grad[key] / (np.sqrt(self.h[key]) + 1e-7)
```
#### 特徴
- 勾配の穏やかな斜面に対して、最適値に近いづける
- 学習率が徐々に小さくなるため、鞍点問題が起こることもある

### RMSProp
```math:
h_{t} = \alpha h_{t-1}  + (1 -\alpha)(\Delta E)^2\\
w^{(t+1)} = w^{(t)}  - \epsilon \frac{1}{\sqrt{h_{t}} + \theta} \Delta E\\
```

```python:
class RMSprop:
    def __init__(self, learning_rate=0.01, decay_rate = 0.99):
        self.learning_rate = learning_rate
        self.decay_rate = decay_rate
        self.h = None
        
    def update(self, params, grad):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grad[key] * grad[key]
            params[key] -= self.learning_rate * grad[key] / (np.sqrt(self.h[key]) + 1e-7)
```
#### 特徴
- 局所的最適解にならず、大局的最適解となる
- AdaGradの進化系
- AdaGradに比べたら、ハイパーパラメーターの調整が少ない

### Adam
モメンタムの、過去の勾配の指数関数的減衰平均と
RMSPropの、過去の勾配の２乗の指数関数的減衰平均を
孕んだ最適化アルゴリズム

```math:
m_{t} = \beta_{1} m_{t-1}  + (1 -\beta_{1})\Delta E\\
v_{t} = \beta_{2} v_{t-1}  + (1 -\beta_{2})(\Delta E)^2\\
w^{(t+1)} = w^{(t)}  - \epsilon \frac{m_{t}}{\sqrt{v_{t}} + \theta} \\
```

```python:
class Adam:
    def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999):
        self.learning_rate = learning_rate
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grad):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.learning_rate * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)         
        
        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grad[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grad[key] ** 2 - self.v[key])
            
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
```
https://qiita.com/omiita/items/1735c1d048fe5f611f80

https://github.com/Jaewan-Yun/optimizer-visualization
## 過学習
### 原因
- 学習データに対して、パラメーター数が圧倒的に多い
- パラメーターの値が適切ではない
- ノード（層）が多い

### 正則化
過学習を抑止するため、ネットワークの（パラメーター、層など）を制約する

#### L1正則化-Lasso
```math:
\|W\|_1 = (| w_1| + | w_2| +...+ | w_n|)\\
E_n(W) + \lambda \|W\|_1\\
```
λ:weight_decay_lambda
![スクリーンショット 2021-05-19 11.15.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d18f388b-e697-50f8-b9ea-867170dd65b4.png)
##### ソース
```python:
# 正則化強度設定 ======================================
weight_decay_lambda = 0.005
# =================================================

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)
    weight_decay = 0
    
    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * np.sign(network.params['W' + str(idx)])
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)  
```
##### 解説
正則化付き勾配

> grad[W] = grad[w] + λ *np.sign(W)

```python:
grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * np.sign(network.params['W' + str(idx)])
```
損失計算

```python:
weight_decay = 0    
for idx in range(1, hidden_layer_num+1):
    ...       
    weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))
loss = network.loss(x_batch, d_batch) + weight_decay
```
#### L2正則化-Ridge
```math:
\|W\|_2 = \sqrt{(| w_1|^2 + | w_2|^2 +...+ | w_n|^2)}\\
E_n(W) + \frac{1}{2} \lambda \|W\|_2\\
```
λ:weight_decay_lambda
![スクリーンショット 2021-05-19 11.16.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/6ed7f1ea-e0b1-c881-566d-7614a075c95e.png)
##### ソース
```python:
# 正則化強度設定 ======================================
weight_decay_lambda = 0.1
# =================================================

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)
    weight_decay = 0
    
    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * network.params['W' + str(idx)]
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += 0.5 * weight_decay_lambda * np.sqrt(np.sum(network.params['W' + str(idx)] ** 2))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss) 
```
##### 解説
正則化付き勾配
> grad[W] = grad[w] + λ *W

```python:
grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * network.params['W' + str(idx)]
```
損失計算

```python:
weight_decay = 0
for idx in range(1, hidden_layer_num+1):
   ...       
   weight_decay += 0.5 * weight_decay_lambda * np.sqrt(np.sum(network.params['W' + str(idx)] ** 2))
loss = network.loss(x_batch, d_batch) + weight_decay
```
#### ドロップアウト
ランダムにノードを削除し、学習する
![スクリーンショット 2021-05-19 13.22.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1c428ff0-c25e-a1ca-27a0-1d35b3ca5871.png)

##### ソース
```python:
class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask
```
### 学習結果
#### 過学習
学習データに対する正解率が1.0に達しているが、
テストデータに対する正解率が0.65前後に止まっている。
![スクリーンショット 2021-05-19 14.19.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/d7cf3f04-9fd9-8790-8cb6-3b1530fe48a4.png)

#### L1正則化
スパース化の影響により、正解率の変動幅が激しくなり、
過学習が多少改善できている
![スクリーンショット 2021-05-19 14.21.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/35fdd629-f1e3-ee25-054a-9e9a45f8d5e3.png)

#### L2正則化
学習データに対する正解率が1.0に達していないが、
テストデータに対する正解率も0.65前後に止まっている。
![スクリーンショット 2021-05-19 14.25.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/928a839f-67c7-963c-901f-af7bb4d29902.png)

#### ドロップアウト
滑らかに学習できている
![スクリーンショット 2021-05-19 14.28.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/9e15b833-1389-c04d-ff2a-e8bd86c937ba.png)

#### ドロップアウト+L1正則化
![スクリーンショット 2021-05-19 14.31.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/f09b6672-9417-a604-d769-4168941048dd.png)

#### ドロップアウト+L2正則化
![スクリーンショット 2021-05-19 14.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/022e6635-ef64-257a-3838-f29ba007a478.png)


## 畳み込みニューラルネットワークの概念
### 畳み込み層
写真のデータだと、縦・横・チャネルが存在しているため、
１次元の配列に変化すると、左、上、右、下のピクセルの関係がなくなる。
畳み込み層を利用すると、そういう関係を保ちつつ、学習できる。
#### 演算概念
![スクリーンショット 2021-05-22 14.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/794eac99-991a-91eb-87f2-d758f84370aa.png)

#### バイアス
![スクリーンショット 2021-05-22 14.04.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/7401f6ca-8b77-c980-bf79-4a30cf3d97aa.png)

#### パディング
![スクリーンショット 2021-05-22 14.05.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/09543f45-d306-10dc-0dab-79a28fcf6d07.png)

#### ストライド
![スクリーンショット 2021-05-22 14.05.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/a4731639-cb1f-dde7-3ebe-920c3692e414.png)

#### チャンネル
![スクリーンショット 2021-05-22 14.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/eecb4d07-7e5f-88d1-470e-1d9a58d59f50.png)

#### 出力サイズの計算
# 出力値のheight, width
```python:
#入力
input_h
input_w

#フィルタ
filter_h
filter_w

#パディング
padding

#ストライド
stride

#出力
output_h
output_w

#算出
output_h = 1 + int((input_h + 2 * padding - filter_h) / stride)
output_w = 1 + int((input_w + 2 * padding - filter_w) / stride)
```
#### ソース
#### im2col
```python:
# 画像データを２次元配列に変換
'''
input_data: 入力値
filter_h: フィルターの高さ
filter_w: フィルターの横幅
stride: ストライド
pad: パディング
'''
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    # N: number, C: channel, H: height, W: width
    N, C, H, W = input_data.shape
    out_h = (H + 2 * pad - filter_h)//stride + 1
    out_w = (W + 2 * pad - filter_w)//stride + 1

    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
    for y in range(filter_h):
        y_start = y 
        y_max = y_start + stride * out_h
        for x in range(filter_w):
            x_start = x 
            x_max = x_start + stride * out_w
            col[:, :, y, x, :, :] = img[:, :, y_start:y_max:stride, x_start:x_max:stride]
    col = col.transpose(0, 4, 5, 1, 2, 3) # (N, C, filter_h, filter_w, out_h, out_w) -> (N, filter_w, out_h, out_w, C, filter_h)   
    col = col.reshape(N * out_h * out_w, -1)
    return col
```
##### 確認
```python:
input_data = np.random.rand(2, 1, 4, 4)*100//1 # number, channel, height, widthを表す
print('========== input_data ===========\n', input_data)
print('==============================')
filter_h = 2
filter_w = 2
stride = 2
pad = 0
col = im2col(input_data, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print('============= col ==============\n', col)
print('==============================')
```
![スクリーンショット 2021-05-24 9.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/3ed907d3-699a-d788-81e5-309f1b938888.png)

#### col2im
```python:
# ２次元配列を画像データに変換
def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    # N: number, C: channel, H: height, W: width
    N, C, H, W = input_shape
    # 切り捨て除算    
    out_h = (H + 2 * pad - filter_h)//stride + 1
    out_w = (W + 2 * pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2) # (N, filter_h, filter_w, out_h, out_w, C)
    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] = col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]
```
##### 確認
```python:
img = col2im(col, input_shape=(2, 1, 4, 4),filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print(img)
```
![スクリーンショット 2021-05-24 9.15.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/8bce482f-58cd-2b21-c89e-de8293635c8f.png)


https://qiita.com/kuroitu/items/7877c002e192955c7857

#### Convolution
```python:
class Convolution:
    # W: フィルター, b: バイアス
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad
        
        # 中間データ（backward時に使用）
        self.x = None   
        self.col = None
        self.col_W = None
        
        # フィルター・バイアスパラメータの勾配
        self.dW = None
        self.db = None

    def forward(self, x):
        # FN: filter_number, C: channel, FH: filter_height, FW: filter_width
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        # 出力値のheight, width
        out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)
        
        # xを行列に変換
        col = im2col(x, FH, FW, self.stride, self.pad)
        # フィルターをxに合わせた行列に変換
        col_W = self.W.reshape(FN, -1).T
        print('col',col.shape)
        print('col_W',col_W.shape)
        out = np.dot(col, col_W) + self.b
        # 計算のために変えた形式を戻す
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
        print('out',out.shape)
        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        # dcolを画像データに変換
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx
```
### プーリング層
![スクリーンショット 2021-05-22 14.22.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/b3284b47-592b-531d-f6ad-f28513219250.png)

#### ソース
```python:
class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad
        
        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)
        
        # xを行列に変換
        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        # プーリングのサイズに合わせてリサイズ
        col = col.reshape(-1, self.pool_h*self.pool_w)
        
        # 行ごとに最大値を求める
        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        # 整形
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)
        
        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,)) 
        
        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)
        
        return dx

```
![スクリーンショット 2021-05-22 16.35.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/4ff1f8f0-dc1c-afce-6e13-87899898021d.png)


https://qiita.com/eijian/items/c947fb6b5e7a49858fb4

https://medium.com/@_init_/an-illustrated-explanation-of-performing-2d-convolutions-using-matrix-multiplications-1e8de8cd2544
## 最新CNN
### AlexNet
####　概要
2012年、ヒントン 教授チームに発表される物体認識のモデル
初の深層学習概念を取り組むモデル
####　構造
input:224×224×3の写真
output:1000個の要素を持つ一次元の配列

畳み込み層：5
プーリング層：3
全結合層：３
![スクリーンショット 2021-05-22 16.41.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242761/1c48563a-e7d0-44d3-608e-7802c517a287.png)

####　ソース・Keras
```python:
#Alex net
model = Sequential()

# Layer 1
model.add(Conv2D(48, kernel_size=(11, 11), strides=(4,4), input_shape=(224, 224, 3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2,2), padding='same'))
model.add(BatchNormalization())

# Layer 2
model.add(Conv2D(128, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2,2), padding='same'))
model.add(BatchNormalization())

# Layer 3
model.add(Conv2D(192, kernel_size=(3, 3), activation='relu'))
model.add(BatchNormalization())

# Layer 4
model.add(Conv2D(192, kernel_size=(3, 3), activation='relu'))
model.add(BatchNormalization())

# Layer 5
model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2,2), padding='same'))
model.add(BatchNormalization())

model.add(Flatten())

# Layer 6
model.add(Dense(2048, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.01))

# Layer 7
model.add(Dense(2048, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.01))

# Layer 8
model.add(Dense(1000, activation="softmax"))

model.summary()
model.compile(optimizer=keras.optimizers.Adam(lr=0.001), 
                loss='categorical_crossentropy',
                metrics = ['accuracy'])
```
https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf