第3回：ニューラルネットワーク概要②

## <font color="ForestGreen">**前回では**</font>
機械学習における学習とは一言で言うと
**「重みを訓練データから自動的に決定する」**ことです。
前回示した通り、ステップで示すと以下のようになります。

**ステップ①:**ある重みからなるニューラルネットワークに**訓練データ[入力]**を入力する。
**ステップ②:**ニューラルネットワークは**順伝播**を行い、演算結果を出力する。
**ステップ③:**出力したデータと**教師データ（訓練データ[出力]）**を比較し、**損失関数**を計算する。
**ステップ④:****損失関数**を基に**逆伝播**を行い、**勾配**を計算する。
**ステップ⑤:****勾配方向**に重みを少しだけ更新する。
**①～⑤を繰り返す**
繰り返した結果、訓練データの入力と出力の関係をうまく説明できる重みをもつニューラルネットワークが構築される。

さあ、それではステップごとにどのような仕組みになっているのか見ていきましょう。
前回の専門用語たちの説明も登場します。
## <font color="ForestGreen">**ステップ①**</font>
まずはステップ①から見てみましょう。ステップ①は以下のようでしたね。
>**ステップ①:**ある重みからなるニューラルネットワークに**訓練データ[入力]**を入力する。

最初に、ある重みからなるニューラルネットワークを定義する必要があります。
まずニューラルネットワークのモデルを決めます。形を決めます。
つまり y = ax + b みたいな式を決めたのと同じ意味合いを持ちます。

次に、重みの初期値を決めます。
つまり y = ax + b の a と b の初期値を決めて、y = 2x + 5 とするのと同じです。
重みの初期値の決め方はかなり大きなテーマですが今回は割愛します。
これで、ある重みからなるニューラルネットワークができました。

ここに**訓練データ[入力]**を入力します。
実際に完成したＡＩに食わせるデータです。y = ax + b でいう x にあたります。
この例では分かりやすくするために変数 x は1つですが、実践ではかなり多くの変数が必要です。
つまりＡＩに食わせるデータの種類の数だけ必要になるということです。

ＡＩに食わせるデータの種類は競馬を例にとると、馬体重や騎手の体重、
最速タイムなどが考えられます。
馬体重だけからでは勝ち馬は予想できなさそうですよね？
馬体重と騎手の体重や最速タイムなどの情報を全部変数として使えたほうが予想精度があがりそうですよね？
こういったことを考えながらニューラルネットワークのモデルを決めるのです。
モデルが決まると必然的に**訓練データ[入力]**のデータ構造も決まります。
:eye:入力データは馬体重と騎手の体重の2種類にしよう！みたいな感じです。

さあ、設計したニューラルネットワークに**訓練データ[入力]**をぶち込んで次のステップに進みましょう。

## <font color="ForestGreen">**ステップ②**</font>
>**ステップ②:**ニューラルネットワークは**順伝播**を行い、演算結果を出力する。

**訓練データ[入力]**をぶち込まれたニューラルネットワークはそれを基に計算し、
結果を出力します。
y = 2x + 5 に x = 7 をぶち込んで計算し、y = 19 を出力するのと同様です。
図に示すと以下のようになります。
![fwd_exe.png](https://qiita-image-store.s3.amazonaws.com/0/244596/084a7c74-4f99-8f3e-34da-07a8ec728385.png)
fig.1 【 y = 2x + 5 】を表すニューラルネットワーク

**訓練データ[入力]**をもとにニューラルネットワークが計算し、
演算結果を出力することを**順伝播**といいます。
順方向に計算結果が伝播していくからです。
以下に**順伝播**のイメージを示します。
![順伝播.png](https://qiita-image-store.s3.amazonaws.com/0/244596/a279c91f-0813-8111-8581-2bdb471d341f.png)
fig.2 順伝播

あなたのニューラルネットワークが**訓練データ[入力]**から出力結果をはじきだしたところで
次のステップです。

## <font color="ForestGreen">**ステップ③**</font>
>**ステップ③:**出力したデータと**教師データ（訓練データ[出力]）**を比較し、**損失関数**を計算する。

あなたが設計したニューラルネットワークはぶち込まれた**訓練データ[入力]**を基に計算し、
結果を出力しました。

この結果はどうなんでしょうか。
<font color="OrangeRed">**イイ**</font>のでしょうか？
それとも<font color="OrangeRed">**わるい**</font>のでしょうか？
それがわからないとニューラルネットワークは学習をすすめることができません。
答え合わせなしで問題を解き続けさせられているようなものです。
これでは勉強ができるようになりませんよね？

そこで<font color="OrangeRed">**イイ**</font>か<font color="OrangeRed">**わるい**</font>かの指標として**損失関数**を考えます。
**損失関数**は、出力したデータと**教師データ（訓練データ[出力]）**から計算される指標です。
あえて乱暴に分かりやすく言ってしまえば**損失関数**とは**出力と正解との誤差**のことです。

**損失関数**として計算される誤差としては、一般的に**２乗和誤差**や**交差エントロピー誤差**が使われます。

**２乗和誤差**は以下の式で計算されます。

```math
E = \frac{1}{2}\sum_{k} (y_k - t_k)^2
```
E : 損失関数
k : データ数
y : ニューラルネットワークの出力
t : 正解
:eye:式についてはこんなもんなんだなーくらいでいいです。原理は重要ではありません。

**交差エントロピー誤差**は以下の式です。

```math
E = -\sum_{k}t_k\log y_k
```
E : 損失関数
k : データ数
y : ニューラルネットワークの出力
t : 正解**(one-hot表現)**
:eye:**one-hot表現**とは正解だけ1で他は0となる表現です。重要ではありません。
:eye:logの底はeの自然対数ですが別に重要ではありません（モノを作るためには）

出力結果が<font color="OrangeRed">**イイ**</font>か<font color="OrangeRed">**わるい**</font>かを示す**損失関数**が計算できたところで次のステップです。

## <font color="ForestGreen">**ステップ④**</font>
>**ステップ④:****損失関数**を基に**逆伝播**を行い、**勾配**を計算する。

さて、**損失関数**が計算できたところで次は何をすればいいのでしょうか？
我々の目的は**訓練データの入力と出力の関係をうまく説明できる重みをもつニューラルネットワークを構築すること**でしたね。
**損失関数**は**誤差**でしたから、これが小さい方がより優秀なニューラルネットワークということになります。

つまり次にやることは**損失関数を小さくすること**です。

どうすれば小さくなるのでしょうか。
とは言っても、我々にできることはニューラルネットワークの重みを更新することだけです。
**損失関数**が小さく小さくなるように（ゼロになるのが理想）ニューラルネットワークの重みを更新すればいいのです。

では重みをどのように更新していけばいいのでしょうか。

**損失関数**を一番大きく減らすように重みを更新すればいいのです。
それを繰り返すことがすなわち**学習**です。

では**損失関数**を一番大きく減らすにはどうすればいいのでしょうか。

**勾配**を使えばいいのです。
**勾配**とは、**損失関数**の値を最も減らす方向を示すものです。

**勾配**は**数値微分**を用いて計算することができます。
**微分**とは関数上のある点における傾きを求めるアレでしたね。
**数値微分**は**微分**ほど厳密ではないが実装が簡単であるという理由で
機械学習でも**勾配**を求めるのに使われています。

具体的には、対象となる関数の上の非常に近い二点を結び，
その直線の傾きを**微分**の近似値とします。
ただし、**数値微分**は数値解析的なアプローチであるため、プログラムにやらせるには
かなり重い時間のかかる処理となります。

そこで**逆伝播**という手法を用いることで、軽い時間のかからない算出が行えるのです。
**逆伝播**の詳しい説明は割愛しますが、**数値微分**の代わりに軽い処理で**勾配**を算出してくれるんだなくらいの理解でいいと思います。

現在の**損失関数**に対しての**勾配**が計算できたところで次のステップです。
>:eye:ちなみに**数値微分**も実際に使われます。
**逆伝播**は軽い処理ですが実装が複雑になることが多いため、ミスも多くなります。
一方**数値微分**は重い処理ですが実装は簡単です。つまり**逆伝播**で算出した
**勾配**が正しい結果になっているかの答え合わせに**数値微分**が使えます。
**逆伝播**と**数値微分**でそれぞれ算出した**勾配**がほぼ似たような値であれば
**逆伝播**がうまいこと実装できてそうだなと判断がつきます。
これを**勾配確認**といいます。

## <font color="ForestGreen">**ステップ⑤**</font>
>**ステップ⑤:****勾配方向**に重みを少しだけ更新する。

現在の**損失関数**に対しての**勾配**が計算できました。
その**勾配**に従ってニューラルネットワークの重みを**少しだけ**更新すれば、
精度が向上することでしょう。

しかしこの**少しだけ**というのがクセモノです。
めっちゃくちゃ**少しだけ**だったらニューラルネットワークが学習を終えるのに１００年くらいかかるかもしれません。
一方、大きく更新してしまった場合、**損失関数**をうまく減らすことができません。
以下のように、ちょうどいいところを通り過ぎて更新されてしまうからです。
![learningRateGoaway.png](https://qiita-image-store.s3.amazonaws.com/0/244596/bb7e5391-d62d-8316-9083-7941db4ee204.png)
fig.3 大きすぎる重みの更新が損失関数に与える結果

この更新の度合いのことを、**学習率**といい、これは人間が決めねばなりません。
上述の通り、**学習率**は一般に、大きすぎても小さすぎても学習はうまくいきません。
**学習率の決め方**も**重みの初期値の決め方**と同様、かなり大きなテーマですが割愛します。
妥当な**学習率**で重みを更新し、学習が進みました。

:eye:**学習率**のように、機械学習において人間が決めなければならないものを**ハイパーパラメータ**といいます。
:eye:**ハイパーパラメータ**をいろいろ変えて学習させ、最も精度のよいニューラルネットワークのモデルを探すのです。

## <font color="ForestGreen">**ステップ①～⑤を繰り返す**</font>
以上見てきたステップ①～⑤を繰り返すことでこのニューラルネットワークは学習を重ねます。
そしていい感じの重みをもったニューラルネットワークになれば学習完了です。

いい感じとは何かはご自分で判断してください。
ふつうは推測精度がある値を超えたらオッケーとします。
精度が 70 % 超えるレベルでも使えるのか、 98 % 以上ないといけないものなのかは要件次第です。

競馬を例に言うと、最終的な的中率をどのくらいにしたいのか次第です。
あなたが望む精度を超えたとき、このニューラルネットワークはデータを与えれば勝ち馬を予想するＡＩに成長を遂げたのです。
あとはあなたが発明したそれを使って**バシっと稼ぐ**だけですね。
:eye:学びに必要なのはモチベーションコントロールのみです。自己コントロール。まさに:eye:ヨガ:eye:

## <font color="ForestGreen">**まとめ**</font>
今回はニューラルネットワークの詳細を説明しました。
機械学習が何をやっているかスッキリと見えてきたのではないでしょうか。

次回はいよいよ簡単なニューラルネットワークを実装していきます。
次回を終えた時、みなさんは<font color="OrangeRed">**オリジナルのニューラルネットワーク**</font>を作れるようになっているはずです。
:eye:簡単なものではありますが。。。でも、とてもとても大きな一歩です。

せーの！:eye:サハスラーラ:eye: