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

## 0. ニューラルネットワークの全体像

確認テスト1
・ディープラーニングは、結局何をやろうとしているか2行以内で述べよ。
入力値から出力値に変換する数学モデルについて、人間が特定のモデルのパラメーターを指定するのではなく、中間層を用いることでモデルを構築する。
・次の中のどの値の最適化が最終目的か
重み[W], バイアス[b]

確認テスト2
以下のニューラルネットワークを紙に書け。
入力層：2ノード1層
中間層：3ノード2層
出力層：1ノード1層
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043122/5f35feca-de1c-ea72-0641-e7e556757f1c.png)

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

ニューラルネットワーク︓回帰
　連続する実数値を取る関数の近似
ニューラルネットワーク︓分類
　性別（男あるいは女）や動物の種類など離散的な結果を予想するための分析

深層学習の実用例
自動売買（トレード）、チャットボット、翻訳、音声解釈、囲碁・将棋AI

## 1. 入力層～中間層
確認テスト1
この図式に動物分類の実例を入れてみよう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043122/1cc97ed4-8cd7-e57b-c015-b979c0565412.png)

確認テスト2
この数式をPythonで書け。
import numpy as np
u = np.dot(x, W) + b

確認テスト3
1-1のファイルから中間層の出力を定義しているソースを抜き出せ。
z = functions.sigmoid(u)
print_vec("中間層出力", z)

## 2. 活性化関数
ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。入力値の値によって、次の層への信号のON/OFFや強弱を定める働きをもつ。
中間層用の活性化関数
　ReLU関数
　シグモイド（ロジスティック）関数
　ステップ関数
出力層用の活性化関数
　ソフトマックス関数
　恒等写像
　シグモイド（ロジスティック）関数

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

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

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

確認テスト1
配布されたソースコードより該当する箇所を抜き出せ。
z1 = functions.relu(u1)
z2 = functions.relu(u2)


## 3. 出力層
分類では、例えば、犬の確率、猫の確率、ネズミの確率のように各クラスに属する確率を出力。
実際にニューラルネットワークの学習において、入力データと訓練データ（正解値）を用意する。
そして出力層の結果と目的変数の正解値を比較し、どのくらい合っているかを表現するのが誤差関数。

確認テスト1
・なぜ、引き算でなく二乗するか述べよ
各ラベルでの誤差は正負の両方の値が発生し、総和をとると、正負が打ち消しあってしまい、全体の誤差を正しく評価できなくなる。2乗して総和をとることで全てのラベルでの誤差が正の値になるようにしている。
・下式の1/2はどういう意味を持つか述べよ
誤差逆伝播するときに微分計算が必要となるが、その際に、この1/2があることで、計算が簡単になる。本質的な意味はない。

平均二乗誤差（MSE：Mean Squared Error）
回帰問題の誤差関数

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

クロスエントロピー誤差（交差エントロピー誤差）
分類問題の誤差関数

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

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

確認テスト2
①~③の数式に該当するソースコードを示し、一行づつ処理の説明をせよ。
①　return y.T
②　np.exp(x)
③　np.sum(np.exp(x),axis=0)

```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) #総和（全体）np.exp(x)で割ることで、確率としている。
    return y.T

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

確認テスト3
①~②の数式に該当するソースコードを示し、一行づつ処理の説明をせよ。
①　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:    # 1次元の場合。
    d = d.reshape(1, d.size)    # (1, 全要素数)のベクトルに変形する
    y = y.reshape(1, y.size)    # (1, 全要素数)のベクトルに変形する

  # 教師データが one-hot-vector の場合、正解ラベルのインデックスに変換
  if d.size == y.size:
    d = d.argmax(axis=1)  # argmaxで最大値のインデックスを取得

  batch_size = y.shape[0] # 行の形状
  return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7) / batch_size 
  # np.arangeでバッチサイズ分取り出して対数関数に与えている。
```

## 4. 勾配降下法
勾配降下法は、ニューラルネットワークを学習させる手法で、以下の3種類などがある。
・勾配降下法
・確率的勾配降下法
・ミニバッチ勾配降下法

深層学習の目的
→学習を通して誤差を最小にするネットワークを作成すること
→勾配降下法を利用してパラメーターを最適化

確認テスト1
該当するソースコードを探してみよう。
network[key] -= learning_rate * grad[key]
grad = backward(x, d, z1, y)

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

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

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

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

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


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

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

```python
    # 出力層でのデルタ
    # functions.d_mean_squared_errorが誤差関数を微分したもの
    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)