ラビットチャレンジ　レポート　深層学習day1　day2

#Section1 入力層～中間層

　その前に深層学習の概要

・識別モデル：分類。データがクラスに属する確率を出力
　生成モデル：クラスに分類されるデータを生成する
　識別関数：分類。確率ではなく、所属するクラスを直接求める
・万能近似定理：ニューラルネットワークはだいたいのものを近似できるので汎用性がある
・入力層、中間層、出力層からなり、特に中間層は複数層に分かれる。４層以上のニューラルネットワークを深層と呼ぶ。


入力層：$x_1,x_2,…,x_i$ のベクトル
　　　　動物の識別例でいう、体長、体重、ひげの本数、耳のおおきさ等のデータの集合（確認テストの内容）
　　　　＊入力は数値であることに留意。数値でないものは何らかの形で数値化する必要がある

中間層：入力に重み$w$との内積を求め、バイアス$b$を加えたものが中間層に渡される
　　　　$wX+b$
　　　　これに活性化関数で変換され、次の層への入力となる

・実習？（1_1_forward_propagation.ipynb）

　ダウンロードしたファイルはパス名を合わせる必要があるので注意


``` python
u = np.dot(x, W) + b
```
　入力から中間層へデータを渡す部分

#Section2 活性化関数

　セクション分け細かすぎません？全体が見えにくい気がします。

・入力層から中間層への入力までは線形の計算しかしないため、このままでは非線形の処理ができない。
　中間層において、活性化関数により非線形変換をすることで対応する。
　![nn1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/408d623e-b7f6-8b42-3cc1-c3c2e9f6398d.png)

　左が線形、右が非線形

・活性化関数の一例
　　　中間層用
　　　　　ReLU関数
　　　　　シグモイド関数
　　　　　ステップ関数
　　　出力層用
　　　　　ソフトマックス関数
　　　　　恒等写像
　　　　　シグモイド関数

・シグモイド関数
　　$f(u)=\frac{1}{1+e^{-u}}$
・ReLU関数
　　勾配消失問題をある程度低減

``` python
def relu(x)
  return np.maxmum(0,x)
```

・実習と確認テスト（1_1_forward_propagation.ipynb）

``` python
 z2 = functions.relu(u2)
```
　インポートしたfunctionsからrelu関数を呼んでいる部分

#Section3 出力層

・分類の例で考えれば、出力層は各クラスに分類されるそれぞれの確率が出力される

・誤差関数
　　出力層の結果が、正解からどれだけずれているかを表す関数
　　出力ではそれぞれのクラスに分類される（犬：0.7、猫：0.2、ネズミ：0.1等）。
　　　　この時正解が犬の場合、
　　　　　　　もっとも高いものと一致はしているが、確率としては1-0.7=0.3の分外れている。
　　　　　　　また、猫、ネズミもそれぞれ0.2、0.1それらしいと判断している分、外れていると考える。
　　　　　　　このように正解とのずれを誤差とする。
　誤差関数（残差平方和） $E(w)=\frac{1}{2}||(y-d)||^2 $
　　２乗しているのは、正と負の差をどちらも正にそろえるため
　　$\frac{1}{2}しているのは、計算上微分したときに係数が消えるため都合がよいから$


・代表的な誤差関数
　　平均二乗誤差（ＭＳＥ）：一般に回帰問題に使われる
　　

``` python
loss = functions.mean_squared_error(d, y)
```

　　クロス（交差）エントロピー誤差：一般に分類問題に使われる
　　$-\sum_{i=1}^Id_ilog{y_i}$

``` python
loss = functions.cross_entropy_error(d, y)

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

```
　最初のif内はミニバッチ処理、次のif内はone-hotベクトル用の処理で、式の本質的コードは次の部分
　　-np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) 
　　小さな数字を足しているのは、０になって対数が無限にマイナスにならないようにするための処理

・活性化関数
　出力層での活性化関数は、分類問題では確率とみなせる0～1の値で、各クラスの総和が１になる等の特性が求められる

・問題の種類と活性化関数
　　恒等写像：回帰
　　　　$f(u)=u$
　　シグモイド関数：２クラス分類
　　　　$f(u)=\frac{1}{1+e^-u}$
　　ソフトマックス関数：多クラス分類
　　　　$f(i,u)=\frac{e^{u_i}}{\sum_{k=1}^K e^{u_k}}$
　　　　このKはクラス数で、すべてのクラスを足して分母とすることで、総和を１にする

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

　ソフトマックス関数の本質的な式の右辺を表すコードは次の部分


``` python
    return np.exp(x) / np.sum(np.exp(x))
```

　if文内は、ミニバッチ処理の時のフォーマットへの対応で、どちらも本質的にはこの式となる

#Section4 勾配降下法

・ニューラルネットを学習させる方法
　　応用的な方法に、確率的勾配降下法やミニバッチ勾配降下法がある
・解析的に解くことは困難なため、誤差関数が小さくなるように繰り返し修正していく
　$$w^{(t+1)}=w^{(t)}-\epsilon\Delta E$$

　誤差関数の傾き$\epsilon\Delta E$によりパラメータを更新する。$\epsilon$は学習率を調整するハイパーパラメータ
　この式に該当するのが下記の最終行

``` python
#確率的勾配降下法
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]
```

・上記コードの確率的勾配降下法はランダムにデータを選択して学習を行う方法

・オンライン学習：データが入る都度、パラメータの更新（学習）を行う
　バッチ学習：まとまったデータを使ってパラメータの更新（学習）を行う

・ミニバッチ勾配降下法
　　大量のデータを学習する際に、データを小分け（ミニバッチ）に分割し、ミニバッチ単位で誤差を求め、各バッチの誤差の平均をとって学習する
　　これにより計算をバッチ単位に並列処理を行うことができる
　　このように同じ処理を並列して行うことをSIMD（Single Instruction Multi Data）並列化という

確認テストの　$w^{(t+1)}=w^{(t)}-\epsilon\Delta E$　の図表現では、学習を適用する単位（エポック）がどのタイミングかを正しく理解することが重要と考えられる。

#Section5 誤差逆伝搬法

・各パラメータの誤差の傾きをどのように計算するか
　各パラメータについて数値微分をしては、計算コストが高くなる。
　誤差逆伝搬法は、合成関数の微分により、出力側から順に微分することで、重みwやバイアスbの各パラメータの微分を解析的に求め、計算コストを下げる方法

・連鎖率
　パラメータの更新のため複雑な合成関数の微分を求める必要があるが、合成関数はそれぞれの関数の微分の積で表せる。
　$\frac{∂ f(g(x))}{∂ x}=\frac{∂ f(x)}{∂ g(x)}\frac{∂ g(x)}{∂ x}$
　これにより出力層から順番に微分を求めていくことで、そこまでの計算結果をもとに次の微分を計算することができる。

　その前の値を保存している部分をコードにより確認する

``` 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_sigmoid(z1)
```

　delta2 で出力層での微分計算を行い、その後 delta1　で中間層の計算を行う際、delta2を利用している

　ここでdelta2 は$\frac{∂E}{∂y}$、delta1は$\frac{∂E}{∂u}=\frac{∂E}{∂y}\frac{∂y}{∂u}$

　さらに$\frac{∂E}{∂w_{ji}^{(2)}}=\frac{∂E}{∂y}\frac{∂y}{∂u}\frac{∂u}{∂w_{ji}^{(2)}}$を求めている部分は次の部分

``` python
grad['W1'] = np.dot(x.T, delta1)
```

　計算は楽になるが、解析的に解くのはやはり大変。ライブラリは優秀

#Section1 勾配消失問題

　誤差逆伝搬法を使用する際に、伝搬を繰り返すたびに勾配が緩やかになり、パラメータの更新が十分に行われなくなる。
　大きな原因として、多くの場合活性化関数の微分が０から１の間をを取るため、それを掛け合わせるごとに小さくなることにある。
　sigmoid関数の場合、微分値は０から０．２５の間である。

・一般的な対策
　　勾配消失が起きにくい活性化関数の選択
　　パラメータの初期値の選択
　　バッチ正規化

・活性化関数の選択
　　ReLU関数：正の時は勾配が１であるため勾配消失が起きにくい。また、負のときは勾配が０であるため、不要なパラメータが使われなくなり必要な部分が残される（スパース化）特性がある。
　　Tanh：微分値が-1から1を取るため、勾配消失が起きにくい。コード上の定義は次の通り

``` python
def tanh(x):
  return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

#tanhの導関数
def der_tanh(x):
  return 1.0 - (tanh(x) ** 2)
```

・パラメータの初期値の選択
　各パラメータの初期値はランダムではあるが、実際にはある法則（正規分布等）をもって定めることが多い。その中で、勾配消失が起きにくい法則がある
　　Xavier：sigmoid関数等に使用する。正規分布を前のノードの平方根で除算することで分散を小さくする。
　　　　　　sigmoid関数のようなＳ字型の活性化関数では0から離れると勾配が小さくなることから、より０近くにすることで勾配消失を防ぐ
　　He：前のノード数ｎとして、正規分布に$\sqrt{\frac{2}{n}}$をかける
        ReLU関数のようなＳ字型ではない活性化関数に使用する

　重みの初期値を０やそれに近い数字にしすぎると各ノード特性に差がなくなり、学習がうまくいかなくなるため適度に分散させる

・バッチ正規化
　ミニバッチに分割する際、バッチ単位で正規化を行うことで、データの偏りを低減する
　これにより、学習が安定することによる学習速度の上昇や過学習の低減といったメリットも得られる

・演習（2_2_2_vanishing_gradient_modified.ipynb）
　sigmoid関数でガウス分布で初期化した場合
![vani1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/7e7bba71-4d0b-dce9-2e03-985d4441e782.png)

　学習が進んでいない。

　同じくsigmoid関数で、Xavierで初期化した場合
![vani2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/b559a43b-4126-11e2-b509-c2a17f0c6d12.png)

　安定して学習ができている。

　次にReLU関数でガウス分布で初期化した場合
![vani3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/03ace868-e57c-40b4-f967-cc18ca065fc9.png)

　ReLU関数は勾配消失を低減できるため、当初学習が進んでいないがある程度のところから学習ができている。

　同じくReLU関数でXavierで初期化した場合
![vani4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/8e4a35a8-7339-b5f7-b16c-336e375823a7.png)

 ガウス分布の初期化に比べ、当初から効率よく学習できている

・実習（2_3_batch_normalization.ipynb）バッチ正規化

　正規化のコード実装を確認

``` python
            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
```
 sigmoid関数を使用し、初期化はXavier

``` python
network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10,
                        activation='sigmoid', weight_init_std='Xavier', use_batchnorm=use_batchnorm)
```
![vani5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/68548a28-999e-ab87-0a2f-4005ed799b7e.png)

　学習できていることを確認

　ReLU関数、He初期化に変更して実施

``` python
network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10,
                        activation='relu', weight_init_std='he', use_batchnorm=use_batchnorm)
```

![vani69.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/92954186-09c2-53f9-c01f-880bdb1d3b33.png)

 sigmoid、Xavierの組み合わせより、学習がやや早く進んでいる

#Section2 学習率最適化手法

　学習率の調整を適切に行う手法であり、一般に当初は大きくし、学習が進むのに伴い小さくする
　モメンタム、Adagrad、RMSProp、Adamといった手法があり、現在主に使われているのはAdam

・モメンタム
　慣性という項目で、前回からの重みの変化に係数をかけて加えるもの
　局所最適解に陥りにくい

・Adagrad
　勾配（の２乗）を累積していき、学習率を累積したもの（のroot）で除算することで調整する。
　除算する値が単純に大きくなっていkため、学習率は単純に小さくなっていく
　局所解に陥りやすい欠点がある

・RMSProp
　Adagradの改良型。蓄積した勾配情報をどれだけ適用するかを調整するパラメータが追加されている。
　局所解に陥りにくく、ハイパーパラメータの調整が必要なケースが少ないというメリットがある

・Adam
　モメンタムとRMSPropのいいとこどり

・実習（2_4_optimizer.ipynb）
　
　当初、SDGの学習率0.01で学習を行った
　![op1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/beb4a6f3-503a-3046-aef6-14920a770a07.png)
　うまく学習できていない。
　学習率を1に変更した
![op2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/c9e7e4be-fc7d-b811-aabc-94e7d42eb2d9.png)
　学習が遅いながらも進んでいる。

　次のモメンタムで行ったところ、やはり初期の学習率が0.01では学習できなかったため、初期学習率を1とした。

![op3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/b47a30c0-ffdc-55d1-3ae2-6a26c09e4271.png)

 基本的なSDGより学習の速度が速くなっている

　次にモメンタムを修正しAdaGradを実装する

``` python
        # 変更しよう  
        # ===========================================
        if i == 0:
            h[key] = np.zeros_like(network.params[key])        
        h[key] += grad[key] * grad[key]
        network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key])+1e-4)

        # ===========================================
```

　初期学習率が0.01や1ではうまくいかなかったため、0.1に変更
![op4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/74e0b919-fd55-59f4-d964-e5bfbeb284e5.png)

　学習ができている

　次のRMSPropでは学習率0.01で学習することができた。

![op5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/b5c587ab-e29f-7a2e-f4dd-8b457cd18610.png)

　最後にAdamを使用したが学習率0.01で学習できた
![op6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/09570592-833b-d4bd-17ed-e3c0917f3935.png)

　学習率の初期値はアルゴリズムによって適切に設定する必要があると考えられる

#Section3 過学習

　過学習は、訓練データに必要以上に適応してしまい、テストデータ（未知のデータ）に適応できなくなる（汎化性能が下がる）こと
　対応策として、正則化とドロップアウトを扱う。

・正則化
　ネットワークの自由度を制限して過学習を抑制する
　過学習の要因に、重みｗが極端に大きくなり、ネットワークが複雑になることがある。評価にｗの大きさによる罰則（ペナルティ）を付加する
　$E_n(w)+\frac{1}{p}||x||_p$
　$||x||_p={(|x_1|^p+…+|x_n|^p)}^{\frac{1}{p}}$

　pはノルム（距離）
　p=1の時　Ｌ１正則化　Lasso回帰　マンハッタン距離、図で見るとひし形の拘束、エッジで最適化されるので重要度の低い特徴量を削減する効果がある
　p=2の時　Ｌ２正則化　Ridge回帰　ユークリッド距離、図で見ると２乗なので円の拘束

・ドロップアウト
　学習のたびに、ランダムでノード間の接続を削除して学習する
　データが同じでも、学習するネットワークが変化することで、データにノイズを加えて水増しするのと同様な効果が得られる。	

・実習（2_5_overfiting.ipynb）
 オーバーフィッティングした様子
　訓練データには１００％近い精度だが、テストデータは精度が上がっていない
![of1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/53ff03a9-7142-013f-2141-8e4395866108.png)

　Ｌ２正規化

![of2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/0df1b862-4ac1-7f52-f519-c8c32c22e684.png)

　訓練データへの過学習が抑えられている。テストデータへのスコアがあがっていないが、元データが過学習用にデータ数が限定されていることもあってか、パラメータの修正だけでは向上しなかった
　正規化の強度を調整すると、強くしすぎたときは学習が進まない様子が見られた

　Ｌ１正規化
![of3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/675ce24b-b23b-fcfb-bdb1-66cb179b9eb4.png)
　重みが削減される効果があるため、極端な変動がみられる

　ドロップアウト
![of4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/5cd58f69-cce7-4a9d-c5da-ae70388f6d37.png)
　初期値ではドロップアウト率が高く、学習が進まない様子であった
　ドロップアウト率を調整して、過学習を抑えて学習させることができた。

#Section4　畳み込みニューラルネットの概念

　次元的なつながりのあるデータ（画像や時系列的なつながりのある音声等）を扱うときの定番
　ＮＮは入力情報の順番や並びを考慮しないため、そのような関連性のある情報を抽出する

・一般的構造

　入力層　→（畳み込み層＊ｎ→プーリング層）＊ｍ　→　全結合層　→　出力層

・ＬｅＮｅｔ
　ＣＮＮの原型といわれるネットワーク。１０個のクラスの多クラス分類
　処理の巻枯れ
　　32*32の画像を入力
　　28*28の画像6枚に畳み込み（4*4の6種のフィルタ）
　　14*14の画像6枚にサブサンプリング
　　10*10の画像16枚に畳み込み（）
　　5*5の画像16枚にサブサンプリング（400）
　　全結合層（400→120→84）
　　出力層　10　（gausstan connections）

・畳み込み層
　入力（画像）にフィルター（重みのパターン）をずらしながら適用し、出力を得る
　　フィルターの特性にどれだけ適合するかの情報を得られる
　パディング：出力サイズを調整する等の目的のため、入力画像の周囲にパディングを設けることがある。パディング内のデータは０で埋めたり、近傍値で埋めたりする	
　スライド：フィルターをずらす量
　チャンネル：フィルターの種類

・実習（2_6_simple_convolution_network.ipynb）
　各層を設定している部分


``` python
        # レイヤの生成
        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'])
```
　image to column：畳み込み処理を高速に行う工夫
　　　　　　　　　 画像をフィルタを適用する単位で、ベクトルに並べなおしておく

・プーリング層
　畳み込みとの違いは、重みがないこと。フィルターのような範囲の平均またはＭａｘを取る
　微妙なずれなどを吸収できる

・実習（2_6_simple_convolution_network.ipynb）
　MNIST（手書きの数字のデータセット）を認識する

　![cm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/e0cf9334-508c-0ea8-ffd1-4c2d74c96f1d.png)

　手書きの数字を適切に分類できている

#Section5 最新のＣＮＮ

・ＡｌｅｘＮｅｔ
　２０１２年ＩＬＳＶＲＣ，畳み込みによる物体認識
　人間の手で特徴量を決めていたものを初めて自動化
　３つの畳み込み層、２つのプーリング層、３つの全結合層から構成
　入力：224×224　出力：1000クラス分類
　画像の上下を分けて学習し、最後に全結合する
　活性化関数ＲｅＬＵ関数、データ拡張、ドロップアウト等を導入
　Global Max Pooling　→　チャンネルごとに最大値の値１つに圧縮
　Global Avarage pooling　→　チャンネルごとに平均の値１つに圧縮
　　情報が大量に減るがなぜかうまく認識するらしい・・・

・実習（2_8_deep_convolution_net.ipynb）
　多層の畳み込みネットワーク

``` python
        # レイヤの生成===========
        self.layers = []
        self.layers.append(layers.Convolution(self.params['W1'], self.params['b1'], 
                           conv_param_1['stride'], conv_param_1['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Convolution(self.params['W2'], self.params['b2'], 
                           conv_param_2['stride'], conv_param_2['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(layers.Convolution(self.params['W3'], self.params['b3'], 
                           conv_param_3['stride'], conv_param_3['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Convolution(self.params['W4'], self.params['b4'],
                           conv_param_4['stride'], conv_param_4['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(layers.Convolution(self.params['W5'], self.params['b5'],
                           conv_param_5['stride'], conv_param_5['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Convolution(self.params['W6'], self.params['b6'],
                           conv_param_6['stride'], conv_param_6['pad']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(layers.Affine(self.params['W7'], self.params['b7']))
        self.layers.append(layers.Relu())
        self.layers.append(layers.Dropout(0.5))
        self.layers.append(layers.Affine(self.params['W8'], self.params['b8']))
        self.layers.append(layers.Dropout(0.5))
        
        self.last_layer = layers.SoftmaxWithLoss()
```
![cm2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155671/1893df0c-cbca-39b8-de45-12b0583db82f.png)


　ＭＮＩＳＴで９８％以上の正答率がでている