ニューラルネッワークのハイパーパラメータ最適化方法について Part1〜学習率〜

ニューラルネットのハイパーパラメータを最適化する方法について、Pythonモデルで試行錯誤しながら私なりの考えをまとめていこうと思っています。動作理解もかねているのでライブラリを使わずPythonだけで書いています。ハイパーパラメータの種類によっては層を深くしたディープニューラルネットモデルを扱うパートもあります。

使用した[Pythonモデル](https://github.com/MIPPS/dnn_hp)は公開しているのでアドバイスや間違いがあれば教えてください。

## ハイパーパラメータとは
学習前に設定する以下のようなもので、学習で自動調整するパラメータ（重み、バイアス）とは区別しています。

- 学習率　**← Part1**
- 重み初期値（±1, Xavier, Gaussian, He）**← Part2**
- 活性化関数（Sigmoid，Tanh, ReLU）**← Part3**
- 一括重み更新、逐次的重み更新、ミニバッチ処理 **← Part4**
- レイヤー数、ニューロン数 **← Part5**
- パラメータの更新方法（SGD、Adam、AdGard、Momentam、SMPROS、RMSProp） **← Part6**
- 正規化  **← Part7**

各ハイパーパラメータは一つづつPartに分けて扱っていく予定です。本Partは前置きと学習率の話ということで。

ニューラルネットの性能は入力データと上述のパイパーパラメータに依存しており、後者はそれなりの試行錯誤が必要だと思います。今回以下のようなネットワーク構成で、そのハイパーパラメータ（今回は学習率）をチューニングし学習回数や誤差が改善されるところを見ていきます。

**・ニューラルネットワーク図**
<img width="360" alt="ファイル名" src="https://qiita-image-store.s3.amazonaws.com/0/164459/27a8b18f-fbac-8b1a-9dbd-8daa855942f1.png" border="5" >

**・学習回数と誤差の遷移**
<img width="700" alt="サンプルグラフ" src="https://qiita-image-store.s3.amazonaws.com/0/164459/c3340845-bf84-090c-d394-9a4c9648c115.png">

## Pythonサンプルコード
TensorFlowやCaffeなどのニューラルネットライブラリは使わず、Pythonで書いています。
動作確認したのは以下の環境ですが、Python2.7でも少しだけ動くことをみています。

- Ubuntu 14.04.5 LTS
- Python Version 3.6.0 |Anaconda 4.3.0 (64-bit)| 
    - numpy==1.11.3
    - matplotlib==2.0.0


モデル作成時の参考書籍は下記になります。
[学習とニューラルネットワーク 熊沢 逸夫(著)／森北出版](https://www.amazon.co.jp/%E5%AD%A6%E7%BF%92%E3%81%A8%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF-%E9%9B%BB%E5%AD%90%E6%83%85%E5%A0%B1%E9%80%9A%E4%BF%A1%E5%B7%A5%E5%AD%A6%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E7%86%8A%E6%B2%A2-%E9%80%B8%E5%A4%AB/dp/4627702914/ref=sr_1_1?ie=UTF8&qid=1486859328&sr=8-1&keywords=%E5%AD%A6%E7%BF%92%E3%81%A8%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88)
<img width="120" alt="ファイル名" src="https://qiita-image-store.s3.amazonaws.com/0/164459/2a7dcddb-468e-4c7c-6bef-e2cb644dad6b.png">
この第6章でニューラルネットワークの仕組みや導出方法が詳しく説明してあります。サンプルの[Cプログラム](https://www.morikita.co.jp/exclusive/download/736)も載っていますが、[Amazonの評価](https://www.amazon.co.jp/%E5%AD%A6%E7%BF%92%E3%81%A8%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF-%E9%9B%BB%E5%AD%90%E6%83%85%E5%A0%B1%E9%80%9A%E4%BF%A1%E5%B7%A5%E5%AD%A6%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E7%86%8A%E6%B2%A2-%E9%80%B8%E5%A4%AB/dp/4627702914/ref=sr_1_1?ie=UTF8&qid=1486859328&sr=8-1&keywords=%E5%AD%A6%E7%BF%92%E3%81%A8%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88)にもあるように書籍に記載の入力データで実行すると収束しません。ただ以下のように学習回数20万回まで増やすと17万回程度で収束しますので、この本通りに実行したのにうまくいかない方がいれば試してみてください。

```c:appendix.txt
#define NUM_LEARN 200000   // 学習の繰り返し回数をここで指定する
```

Pythonモデルで上記書籍と同様のハイパーパラメータ設定をすると収束するまで約11万回となりある程度再現できています。ここ出発点として最適化をしていきたいと思います。

## 本稿の対象範囲について
本稿が対象にしているのは、古典的な層の浅いニューラルネットワークや５層程度のディープニューラルネットワークのハイパーパラメータ探索で、畳み込みニューラルネットワークについての試行はしていないです（今後試したいですが）。また、ニューラルネットワークの動作や学習方法といった動作説明もしていません。こちらは上記であげた書籍や以下で詳しく載っています。特に下記書籍の誤差逆伝播法を図をつかって説明する方法はとてもわかりやすかったです。

[ゼロから作るDeep Learning
――Pythonで学ぶディープラーニングの理論と実装　斎藤 康毅(著)／O'Reilly Japan](https://www.oreilly.co.jp/books/9784873117584/)
<img width="120" alt="ファイル名" src="https://qiita-image-store.s3.amazonaws.com/0/164459/57de0a34-f665-d705-7326-a45be7e39c6e.jpeg">




## 本題の学習率について
下記ハイパーパラメータを初期値＝改善の余地がある値としてまずこの状態を確認していきたいと思います。

- 学習率 0.05
- 重み初期値 -1 と 1 の交互
- 活性化関数：Sigmoid
- 逐次的重み更新方法
- レイヤー、ニューロン数　入力：３／中間：５／出力：１
- パラメータ更新方法：SGD
- 正規化：なし


図にすると以下のようになります。
![chap01_2 (5).png](https://qiita-image-store.s3.amazonaws.com/0/164459/7ec4c0a8-37f4-81e4-0510-817b9a4229bb.png)

３層のネットワーク構成で、活性化関数はSigmoidをつかっています。重みの初期値は+1と-1の交互。訓練データは６つしかなく、XDATA３要素中の１の個数が偶数であればYDATAを１としています。変数（XDATA,YDATA,TDATA、W[]）はソースコードと対応しているので追うときの参考にしてください。

上記の図はフィードフォワード時の図なのでハイパーパラメータのいくつかは図中に出てきませんがそのうちバックフォワードの図も書くつもりなのでそこで再度確認させてください。

実行結果です。６つの訓練データすべてで誤差が0.01未満になるまで訓練しました。
横軸は学習回数、縦軸は上段のグラフで誤差、残りの２つはn各層の重みW[0],W[1]の推移です。
<img width="700" alt="0.05" src="https://qiita-image-store.s3.amazonaws.com/0/164459/7fe6f03a-0d5c-1645-5487-f425254c02a3.png">


学習の最初と最後あたりは値が大きく変化していますが、それ以外のほとんどのところ（100000〜600000回あたり）は誤差がある状態にも関わらずあまり変化がなく学習がすすんでいないことを見ることができます。この期間の学習を加速するため、学習率0.05→0.5→5→50と変更してみました。ソースコードは以下を修正してください。初期値は５にしてあります。

```python:neuralnet.py
EPSILON = 5.0             # Learning Rate
```

学習率を増やした時の学習回数結果を対数グラフにしました。４サンプルは少しもの足りない気がしたので、その中間値も計測しています。グラフをみると横軸：学習率に半比例して縦軸：学習回数が減っているのがわかります。ただ学習率50は大きすぎるようで、誤差が収束しなかったためグラフにのせていません。
![figure_1.png](https://qiita-image-store.s3.amazonaws.com/0/164459/781bc077-7c60-6fd2-cd62-21a8fbe9247f.png)


上記グラフの右から２番めにあたる学習率 5.0、学習回数 564の時の学習遷移グラフです。学習率0.05のときと比較すると学習全体を通して誤差が変化しているのがわかり、効率よく？学習しているように見えます。
<img width="700" alt="0.05" src="https://qiita-image-store.s3.amazonaws.com/0/164459/3fbf4652-bd87-8430-3ba8-db497ad0769b.png">


## まとめ
最初に試した学習率0.05では、特定のトレーニングデータ（オレンジ色のtr:1）で誤差が長期間残っていますが、最終的には収束しています。そのことから学習率を大きくしてこの勾配を早く下ったとしても最終的にはきちんと収束する可能性があり、実際大きくしたところより早く収束し学習回数も減ったといった形でしょうか。この点についてはモーメンタム※等を導入したほうがよりスマートに学習回数は減るかもしれません。※モーメンタムについてはPart7で考察したいと思います。

学習率が過大(今回は50)の場合はそもそも収束しませんが、それ以下であればどの値でも学習回数と引き換えに収束しているので、その点はハイパーパラメータの調整難易度でいうとの低い方なのかもしれません。

## 次回
次回取り扱うハイパーパラメータは"重みの初期値"です。中間層と出力層の間の結線は６本なので、上記グラフのW[1]には、ラインが６つあるべきところ３つしかありません。この点について重みの初期値を変更することで対応してきます。また今回のモデルより層を２つ増やし、５層にしたディープニューラルネットも扱います。