ラビットチャレンジレポート＿深層学習①-1

**深層学習day1**
**Section1: 入力層〜中間層**

識別モデルと生成モデル

識別モデル：データを目的のクラスに分類する
決定木、ロジスティック回帰、サポートベクターマシン(SVM)、ニューラルネットワーク　など

生成モデル：特定のクラスのデータを生成する
隠れマルコフモデル、ベイジアンネットワーク、変分オートエンコーダ(VAE)、敵対的生成ネットワーク(GAN)など

ニューラルネットワーク

入力層、中間層、出力層で構成される。

入力層：説明変数を入力
中間層：入力層をもとに重みとバイアスを学習
出力層：結果を出力


回帰、分類が可能。

回帰：連続する実数値を取る関数の近似
線形回帰、回帰木、ランダムフォレスト　など

分類：離散的な結果を予想
ベイズ分類、ロジスティック回帰、決定木、ランダムフォレスト　など

入力層〜中間層
説明変数と重みの内積+バイアスの結果を、活性化関数に入力して出力する。
ここでは中間層の出力で、次の層への入力となる。
学習によって、重みとバイアスを最適化していく。

$$u=w_{1}x_{1}+w_{2}x_{2}+w_{3}x_{3}+w_{4}x_{4}+b
=Wx+b$$


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




**Section2: 活性化関数**

活性化関数：
ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。
代表的な活性化関数として、以下を挙げる。

ステップ関数

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


```
def step(x):
    return np.array(x>0).astype(np.int)
```

シグモイド関数
0～1の間を緩やかに変化する関数
勾配消失問題が起きることがある


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


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

RELU関数
入力が0より大きければそのまま出力、0より小さければ0とする
勾配消失問題の回避とスパース化に貢献することから、近年よく使われている

```math
f(x) = \left\{
\begin{array}{ll}
x & (x \gt 0) \\
0 & (x \le 0)
\end{array}
\right.
```

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

tanh関数
1～1の間で出力

```
def tanh(x):
    return np.tanh(x)
#以下のようにも書き換えられる
#   return np.sinh(x)/np.cosh(x)
#   return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
```



**Section3: 出力層**

出力層の役割
最終的な結果を出力する。
出力と教師データを比較して誤差を定量化する。

誤差関数
回帰には平均二乗誤差、分類には交差エントロピー誤差が用いられる。


平均二乗誤差

$$E=\frac{1}{2}\sum_{k}(y_k-t_k)^2$$

```
def mean_squared_error(y, d):
  return np.mean(np.square(y-d)) / 2
```

＊正の数と負の数が混ざっていると正しく誤差が合計できないため、必ず正の数にするために2乗している
＊微分した際の式を簡単にするため、あらかじめ２で割っている

交差エントロピー関数
$$E_{n}(w)=-\sum_{i=1}^{I}{d_{i}log\ y_{i}}$$

```
def cross_entropy_error(d, y):
# 1次元の場合
  if y.ndim == 1:
# (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)

# バッチサイズ分取り出して対数関数に付与
# 対数関数の結果が0になることを回避するために極めて小さい値「1e-7」を付与
  batch_size = y.shape[0]
  return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7) / batch_size
```

出力層の活性化関数

ソフトマックス関数
各クラスに該当する確率を出力する。
他クラス分類(3クラス以上)に用いられる。

$$f(i,u)=\frac{e^{u_{i}}}{\sum_{k=1}^{K}{e^{u_{k}}}}$$

```
def softmax(x):
# 2次元の場合
  if x.ndim == 2:
# xを転置
    x = x.T
# スケーリング（xから列方向(縦に見て)取った最大値を引く）
    x = x - np.max(x, axis=0)
# xの指数関数の値(数式の分子)を指数関数の総和
    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))
```

＊オーバーフロー対策：
指数関数を扱うため、入力によって値が極端に大きくなってしまうことを防ぐ

恒等写像

シグモイド関数（ロジステック関数）


**Section4: 勾配降下法**

勾配降下法
深層学習では、誤差関数を最小化するパラメータを求めて、精度のよい予測モデルを得ることが目的。
パラメータをどの方向に更新したら誤差を小さくできるか、勾配（傾き）を利用して誤差関数の値を徐々に減らしていく方法を勾配降下法と言う。

$${g}^{(t)}=\nabla E(\mathbf{w}^{(t)})$$$$Δ{w}^{(t)}=-\eta\mathbf{g}^{(t)}$$                                              $${w}^{(t+1)}=\mathbf{w}^{(t)}+\Delta\mathbf{w}^{(t)}$$



η は学習率（learning rate）を意味する

確率的勾配降下法
パラメータの1回の更新に全データではなく、ランダムに選び出された1つのデータを使う方法。

メリット
データが冗⻑な場合の計算コストの軽減
望まない局所極小解に収束するリスクの軽減
オンライン学習が可能

＊オンライン学習：データを都度与えて学習させる手法
（バッチ学習はデータを一通り用意してから一度に与えて学習させる手法。）


ミニバッチ勾配降下法
1回の更新に一定数のデータを使う方法。
1回の更新で用いるデータの塊をミニバッチと言う。
ミニバッチのサイズもハイパーパラメータである。

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

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

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

エポック：データセット全てを一度与えることを1エポックと言う




**Section5: 誤差逆伝播法**

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


実装例

```
#  誤差逆伝播
def backward(x, d, z1, y):
  grad = {}

  W1, W2 = network['W1'], network['W2']
  b1, b2 = network['b1'], network['b2']
  #  出力層でのデルタ
  delta2 = d_softmax_with_loss(t, y)
  #  b2の勾配
  grad['b2'] = np.sum(delta2, axis=0)
  #  W2の勾配
  grad['W2'] = np.dot(z1.T, delta2)
  #  中間層でのデルタ
  delta1 = np.dot(delta2, W2.T) * d_relu(z1)
  # b1の勾配
  grad['b1'] = np.sum(delta1, axis=0)
  #  W1の勾配
  grad['W1'] = np.dot(x.T, delta1)

  return grad
```


**この記事の参考図書**
「ゼロから作るDeepLearning」 斎藤康毅
「機械学習のエッセンス」加藤公一

~Fin~