Rabbit Challenge レポート(深層学習 DAY1)





# 1. 入力層~中間層

入力: $x_i$

重み: $w_i$

バイアス: $b$

総入力: $u = w_1x_1 + w_2x_2 + ... w_nx_n + b = Wx + b$

活性化関数: $f$

# 2. 活性化関数

活性化関数: $ f^{(l)}(u^{(l)}) = [f^{(l)}(u_1^{(l)}...u_j^{(l)})]$

## 中間層で用いられる活性化関数

### ステップ関数

```
def step_function(x):
  if x > 0:
    return 1
  else:
    return 0
```

0~1の間を表現できず、線形分離可能なものしか学習しなかった

### シグモイド関数

```
def sigmoid(x):
  return 1/1(+np.exp(-x))
```

ステップ関数では表現できない0~1の値を表すことができ、NN普及のひっかけとなった

大きな値では出力の変化が微弱なため、勾配消失問題を引き起こすことがあった

### RELU関数

```
def relu(x):
  return np.maximum(0,x)
```

昨今最も使われている活性化関数

勾配消失問題の回避とスパース化に貢献することで良い結果をもたらしてくれる


# 3. 出力層

## 誤差関数

### 平均二乗誤差

$En(w) = \frac{1}{2}\sum_{j=1}^l(y_j-d_j)^2 = \frac{1}{2}||(y-d)||^2$

```
# 平均二乗誤差
def mean_squared_error(d, y):
    return np.mean(np.square(d - y)) / 2
```

### 交差エントロピー

$ En(w) = \ \sum_{i=1}^l d_i\log y_i$

```
# クロスエントロピー
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
```

## 活性化関数

出力層と中間層との違い

- 値の強弱

  - 中間層：閾値の前後で信号の強弱を調整
  - 出力層: 信号の大きさ(比率)はそのままに変換
- 確率出力

  - 分類問題の場合は出力層の出力は0~1の範囲に限定し、総和を1にする

### ソフトマックス関数

使い分け

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/156aeefb-91bd-70b0-f5d4-a125c93bc024.png)


```
# ソフトマックス関数
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))
```


# 4. 勾配降下法

## 勾配降下法

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

$\nabla E=\frac{\partial E}{\partial w} = [ \frac{ \partial E}{ \partial w_1}...\frac{ \partial E}{ \partial w_M}]$

### 学習率

学習が小さい場合発散することは無いが、小さすぎると収束するまでに時間がかかる

以下のようなアルゴリズムがある

- Momentum
- AdaGrda
- Adadelta
- Adam ←よく利用される

## 確率的勾配降下法

ランダムに抽出したサンプルの誤差を求める

$w^{(t+1)} = w^{(t)} - \epsilon \nabla E_n$

メリット

- データが冗長な場合の計算コストの削減
- 望まない局所極小解に収束するリスク削減
- オンライン学習ができる

## ミニバッチ勾配降下法

オンライン学習の考えを取り入れ、分割してバッチ学習を行う

$w^{(t+1)} = w^{(t)} - \epsilon \nabla E_n$

$E_t = \frac{1}{N_t} \sum_{n \in D_t}^{} E_n$

$N_t = |D_t|$

ランダムに分割したデータの集合(ミニバッチ) Dtに属するサンプルの平均誤差を求める

メリット

- 確率的勾配降下法のメリットを損なわず、計算機の計算資源を有効活用できる
- 並列での学習が可能

## 誤差勾配の計算

どう計算する？

$ \nabla E = \frac{\partial E}{\partial w} = [ \frac{\partial E}{\partial w_1} ... \frac{\partial E}{\partial w_M}]$

### 数値微分

プログラムで微小な数値を生成し、疑似的に微分を計算する一般的な手法

$\frac{\partial E }{\partial w_m} \approx \frac{E(w_m+h)-E(w_m -h)}{2h}$


# 5. 誤差逆伝播法

### 誤差逆伝播法

算出された誤差を、出力層側から順に微分し、前の層前の層へと伝播。最小限の計算で各パラメータの微分値を解析的に計算する手法

誤差関数: $ E(y) = \frac{1}{2} \sum_{j=1}^l(y_j-d_j)^2 = \frac{1}{2}||y-d||^2$

出力層の活性化関数: $y=u^{(t)}$

総入力の計算: $u^{(t)} = w^{(l)}z^{(l-1)}+b^{(l)}$

$\frac{\partial E}{\partial w_{ji}^{(2)}} = \frac{\partial E}{\partial y} \frac{\partial y}{\partial u} \frac{\partial u}{\partial w_{ji}^{(2)}} = (y_j-d_j)z_j$

$\frac{\partial E(y)}{\partial y} = \frac{\partial}{\partial y} \frac{1}{2} || y -d ||^2 = y-d $

$ \frac{\partial y(u)}{\partial u} = \frac{\partial u}{\partial u} = 1$

```
# 誤差逆伝播
def backward(x, d, z1, y):
    # 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 = 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
```

# 確認テスト

## 確認テスト1-1
Q. ディープラーニングはなにをしようとしているか。

A. プログラムの代わりにニューラルネットワークを利用し、入力値から目的値を求めるモデルを作成する

補足: 重み[w]やバイアス[b]を求める


## 確認テスト1-2
Q. 次のネットワークを紙にかけ

入力層: 2ノード1層

中間層: 3ノード2層

出力層: 1ノード1層

A.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/c88e7380-a39f-1cdd-83cc-f397e80dee58.png)


## 確認テスト1-3
Q. この図式に動物分類の実例を入れてみよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/c1508da0-90c7-290f-594e-5be1b1b15df4.png)


A.
x1: 体の大きさ

x2: 体の長さ

x3: 耳の大きさ

x4:しっぽの大きさ


## 確認テスト1-4
Q. この数式をPythonで書け

$ u = w_1x_1+w_2x_2+w_3x_3 = b$

A. ```u = np.dot(x, W) + b```

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

```
    # 2層の総入力
    u2 = np.dot(z1, W2) + b2
  
    # 2層の総出力
    z2 = functions.relu(u2)
```

補足: 中間層が今回は1つ

## 確認テスト2-1
Q.線形と非線形の違いを図にかいて簡易に説明せよ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/5e294577-273d-4603-9473-24b898e1d2c4.png)


左図 線形: 直線で表せることができるもの

右図 非線形: 表せないもの

[補足]

線形な関数は以下を満たす

- 加法性: f(x+y) = f(x) + f(y)
- 斉次性: f(kx) = kf(x)

## 確認テスト2-2
Q.
$z = f(u) ... (1.5)$

配布されたソースコードより該当する箇所を抜き出せ

```
# 1層の総出力
z1 = functions.relu(u1)
```

[補足]
```from common import functions```という形でimportされているので関数自体は別ファイルに定義されている

```
def relu(x):
return np.maximum(0, x)
```

## 確認テスト3-1

$ En(w) = \frac{1}{2}\sum_{j=1}^l(y_j-d_j)^2 = \frac{1}{2}||(y-d)||^2$

Q.なぜ引き算ではなく二乗するのか

A.必ず正の値になるように調整するため

Q.1/2はどういう意味をもつのか

A.誤差逆伝搬の計算で微分を行う際に計算が簡単になる

補足：1/2に本質的な意味はない

## 確認テスト3-2

ソフトマックス関数の①~③に該当するソースコードを示し、説明せよ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/b9a72987-68b2-ccda-19b3-a2a018ec8b02.png)


```
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)③) ・・・①
```

9行目 return の箇所が実際の数式に該当する箇所である

補足: 3行目からの分岐はミニバッチで利用する処理

その他の処理はプログラムを安定させるための記述

## 確認テスト3-3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/f0764a79-6512-575f-8512-af64869eb958.png)

Q. ①~②の数式に該当するソースコードを示し、一行ずつ処理の説明をせよ。

```
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 ・・・①
```

A. 11行目 returnの文が該当

補足: 1e-7は対数関数を∞に飛ばした時、0になってしまうことを防ぐために利用している

## 確認テスト4-1

Q.オンライン学習とは何か

A.データをリアルタイムで読み取り、パラメータを更新する方法

補足: バッチ学習は一度にすべての学習データを使ってパラメータ更新を行う

## 確認テスト4-2

Q.この数式の意味を図に書いて説明せよ

$ w^ {(t+1)} = w^ {(t)} - \epsilon \nabla E_n$

A.
〇 -> 〇 -> 〇
t -> t+1 -> t+2
重みが $-\epsilon \nabla En $ずつ加えられていくイメージ

\[参考]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960917/54de9846-4753-65b6-f677-ecc25587af3e.png)



## 確認テスト5-1

Q.誤差逆伝播法では不要な再帰的処理を割けることができる。既に行った計算結果を保持しているソースコードを抽出せよ

A.

```
# 出力層でのデルタ
delta2 = functions.d_mean_squared_error(d, y)
```