実装で学ぶ深層学習1(回帰編)

#はじめての深層学習
##はじめに
自分が初めて深層学習に触れたのは学部3年の時で学部の授業の中で深層学習について触れる講義は少なかったように思える。
その分深層学習は独学でいろいろ調べた物が多いが、深層学習の内部構造の把握と実装を行う上で自分が踏んだステップのうちこれは必要だったな、というものをピックアップしてブログに載せる。また、ブログを書くのも初めてで練習としての記事なので読みづらいと思われます。ご容赦ください。

##深層学習とは
まず実装の前に深層学習が何をやっているかを説明する。
深層学習とは一言で言えば関数の最適化を用いた汎化である。
画像を入力として、画像が猫である確率を出す関数を最適化すれば、それは画像を猫とそれ以外で分類する分類器になり、$x$を入力として$sin(x)$を出す関数を最適化すればそれは回帰モデルと呼ばれる。
まず、深層学習が関数の最適化であることを実感するために回帰を実装していく。
##環境整備
パソコンがあってネット環境があれば深層学習をする上で必要なものは揃っている。
google が提供するgoogle colaboratoryを使用すれば,必要なライブラリは揃っているのでこれを使う(便利な世の中になった)。
google colaboratoryはgoogle driveから新規のボタンを押して,その他から選べる。開くとjupiter-notebookが開かれてすぐに対話式の環境が構成される。

##回帰の実装(y=sin(x)を予測)
実装して動くものをみながら議論した方が理解しやすいと思うのでまず回帰モデルを実装する。
まず、以下のコードをいれてみよう。

```python
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,20,0.1)
y = np.sin(x)
plt.plot(x,y)
```


これを実行するとsinカーブがグラフとして表示されたと思う。
matplotlibが表を描画するために使ったライブラリで、これからよくこのライブラリを利用する。
ここまでで訓練データを生成した。
0から20までの0.1刻みの実数とそれのsinである。

次にモデルを定義する。

```python

from keras import layers
from keras import models
from keras import optimizers
model = models.Sequential()
model.add(layers.Dense(256,activation = "relu",input_shape=(1,)))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(256,activation = "relu"))
model.add(layers.Dense(1))
model.compile(loss = "mse",optimizer="adam")
```
ここでkerasのライブラリから必要なものをimportした。
モデルとは今回どういう設計で関数を作るかというもので、様々な記述の仕方があるがわかりやすくSequential()を使う。
ここでモデルにたくさんの層をaddしていることがわかると思う。
今回はlayers.Denseを大量に使っているがこれがなんなのかを述べる。
####全結合層
小見出しの通りlayers.Denseは全結合層でベクトルを受け取りベクトルを返すものである。
引数に256を取っている物がたくさんあると思うが、これは出力を何次元にするかというものである。
例えば入力がn次元のx, 出力がm次元のyで全結合層は以下の式で表される。

```math
y = Ax+b
```

ここで, Aはn*m行列でbはm次元ベクトルである。
この行列とベクトルはどこから出てくるかというと、全て変数として持ってしまって、あとで最適化するのである。
つまり、これで任意の線形変換を再現できるのである。

####活性化関数
上のモデルは線形変換を何度も適用して$y$を得ようとしていることがわかったが、複数の線形変換はひとつの線形変換で再現できてしまう。
これでは全結合層をたくさん重ねても意味がなくなってしまう。
そこで出てくるのが活性化関数(Activation Function)で非線形な写像である。
ベクトルの全ての値に対して非線形な写像を行うのである。
全結合層と全結合層の間にこれを噛ませることで層を重ねた時にモデル全体での表現能力が上がるのである。
今回のコードではすべてReluを用いている。これは$max(0,x)$であり、確かに非線形である。深層学習の分野ではこのReluがよく用いられる。
####最適化
全結合層の中には任意の線形変換を表現するために大量の内部変数($Aとb$)が使われるが、これをどう最適化するかをmodel.compileで記述する。
lossというのが、最適になればなるほど値が小さくなる指標で、今回はmse(mean squared error)を用いている。つまり、予測値と正解の値の差の平方である。
今のモデルのlossを計算するだけではパラメータは最適化できない。
パラメータをどの方向にどれだけ動かせばlossが減るかを計算しなくてはならない。
基本的にはパラメータの勾配を降れば良いのだが、安定化や高速化の観点から単純な勾配でなく二次微分や手前のステップでの勾配などをつかったadamというのがかなり深層学習の最適化には良いとされている。

####訓練
それでは実際に訓練してみよう。

```python
hist=model.fit(x,y,steps_per_epoch=10,epochs = 10)
```
学習では訓練データが巨大であることが多いので一回の学習（パラメータ調整）で訓練データ全部を用いた勾配を下ることができないことが多いので訓練データを小さくしたバッチというものを使う。
今回は学習データ全体を10こに分けている(setps_per_epoch=10)。
つまり2このデータから勾配を下るということを10回行って一つのepoch(学習の単位で訓練データ全体を舐めた回数に相当する物)ということになり、今回は10回全体を舐めるように訓練させた。

####予測
では、予測してみよう。

```python
test_x = x + 0.05
acc_y = np.sin(test_x)
pre_y = model.predict(test_x)
plt.plot(test_x,acc_y)
plt.plot(test_x,pre_y)
plt.show()
```

これで訓練データから0.05だけx方向にずれたデータに対してどの程度モデルがsin(x)とおなじ振る舞いをしているかがわかると思う。
そのまま動かすとこのようになる。
![10_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625181/ad45394b-4649-0aaf-6b5c-418087d4a5b2.png)

$x>10$で値が大きく外れてしまっている。

モデル自体を改善したり、訓練をもっと行ったりすることで, 下のように改善することができるので、ぜひやってみよう。

![30_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625181/a7770c7b-1392-29d1-670d-c53f15c3ecc8.png)


###GPU
学習に時間がかかるようなモデル、訓練の長さが必要な時はgoogle colaboratoryでは編集->ノートブックの設定からハードウェアアクセラレータをNone からGPUにしよう。
GPUの力で訓練が早く終わるはずだ。


##おわりに
今回は線形変換とreluだけでsinカーブを表現した。
sinカーブだけでなく深層学習は画像認識や画像生成まで任意の関数を近似することができるので可能性は無限大である。
次回は画像認識、ベタだがmnistの手書き文字認識をできるだけ短い行で実装して畳み込み層についての説明をしたいと思う。