数学2B未履修でもわかるニューラルネットワークと深層学習

# はじめに
「深層学習やってみたいけど数学わからない。だけど数学的な説明しかない」と思ったので頑張って「微分以外」の数学的な要素を省いて説明したいと思います。素人なので間違ってることを言ってるかもしれないので優しくご指摘をおねがいします。わからないことがありましたら、気軽に質問してください。

# 機械学習・深層学習とは
## AIと機械学習
よく聞くAIと機械学習、いったい何が違うのでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/5c169ea1-b767-c23f-dc50-b8f7d9209d8d.png)

この図は機械学習の立ち位置を表したものです。AI（人工知能）は知能を持っているように見えれば人工知能として確立することができます。そもそも現在、知能を生殖以外の方法で作るということはできないので、人工知能の定義はそうなってしまうわけです。

この知能を再現する方法としてここでは2つ挙げました。1つは「ルールベース」です。`if`や`else`などを多用することでリクエストに対して的確なレスポンスを返すプログラムを作ればAIとして多くの人が認識することができます。

2つめは「機械学習」です。過去のデータや経験からリクエストに対して適切なレスポンスを数学的に導くことこそが機械学習になります。ここで大事になってくるのはプログラミングよりも理論や数学です。特に統計学は機械学習の発展そのものなので最重要の単元になっています。

以上から`AI=機械学習`ではなく、`AI⊃機械学習`[^*1]であるということがわかります。例えば、`Siri`や`Googleアシスタント`などのAIは両方が使われています。AIへ呼びかけや音声の選出と再生、検索や地図、天気などの出力はケースベースが使われていますが、ユーザーの声色の分析、入力された声を文章化し、内容を理解するのには機械学習が使われています。
[^*1]: 記号`A⊃B`は`AはBを含む`という意味です。
## 機械学習と深層学習
次に深層学習と機械学習の違いを見ていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/96aba4ee-bc6e-83c9-604d-0777613c39a2.png)

簡単に言えば、機械学習の一種として深層学習が存在します。

機械学習の構造は単純で、統計学に近いです。与えられたデータと予測の誤差が小さくなるように計算を行います。この機械学習の手順を「アルゴリズム」[^*2]といいます。このアルゴリズムを我々が指定してデータを読み込ませて学習させます。

[^*2]: これに限らず、プログラミングにおいて実行する手順、やり方のことをアルゴリズムといいます。
ここで大切なのはアルゴリズムを指定しなければならないということです。出てくるアルゴリズムは昔に発見や開発された計算方法ばかりなので新しいことを学習させたい場合やより精度をよくしたいときにはアルゴリズムを見つけるor自作しなければいけません。

そこで出てくるのが深層学習です。厳密に言えば深層学習は機械学習の一種の「ニューラルネットワーク」という枠組みの中に入ってるのですが、「ニューラルネットワーク=深層学習」として解説します。ニューラルネットワークとは人間の脳の構造を再現したものです。このニューラルネットワークを使って複数層にわたって学習するものが深層学習になります。

深層学習はアルゴリズムの指定が必要なく[^3]、データに合わせた学習をすることができます。人間の脳と同じなので、最初は精度が悪いですがだんだんパラメータを調整して精度をよくすることができます。人間でいう赤ちゃんからの成長と似ています。成長には経験が必要なのと同じように多くのデータが必要になります。
[^3]: 損失関数や活性化関数、モデルの層の指定は必要です。

# ニューラルネットワーク
深層学習で使われるニューラルネットワークについて説明します。
## ニューロン
ニューロンとは入力された値を活性化関数に入れてその結果を出力するものです。基本的に円で表されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/f761707a-c875-91c6-2d95-3eac7c5d5b58.png)


## 活性化関数
活性化関数はニューロンで使われる関数です。数字を活性化するのが目的なのでよく使われるもので数種類あります。いくつか紹介します。なお、関数の式は参考までに。
### ReLU関数（Rectified Linear Unit）
現在もっとも基本の関数です。

```math
y = \max \left( 0,x\right)
```
![ReLU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/3a1d014c-3a92-1a3a-214b-b164ba04edb7.png)

### Tanh関数（Hyperbolic Tangent Function）
[双曲線関数](https://ja.wikipedia.org/wiki/双曲線関数)の一種です。昔はよく使われたそうです。

```math
y = \dfrac {e^{x}-e^{-x}}{e^{x}+e^{-x}}
```
![Tanh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/54d131f3-1d5b-80cb-458d-ed0f357bc9c9.png)

### Sigmoid関数（Standard Sigmoid Function）
これもよく使われます。縮尺が同じなのでわかりづらいですが、Tanh関数を0~1の範囲に凝縮したかたちをしてます。

```math
y = \dfrac {1}{1+e^{-x}}
```
![Sigmoid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/48bdb752-5db7-9ce6-132e-2976cf4cbc61.png)

### STEP関数
活性化関数において基本中の基本です。

```math
y = \begin{cases}0 & (x < 0) \\ 1 & ( x\geq 0)\end{cases}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/24548846-4234-96f6-9ab4-dcbe22f1a1a1.png)

### Softmax（Softmax Function）
いままでは2次元上で表せていましたが、Softmax関数は複数の入力に対し複数の出力(0.0~1.0)を行い、その和が1.0となるという特性を持っています。複数の出力をするため、確率を表すことができます。この特性のため後述する出力層に使われることが多いです。

```math
y_{i}=\dfrac {e^{x_{i}}}{\sum^{n}_{k=1}e^{x_{k}}} \space [i = 1, 2, 3 ..., n]
```

$i$ が決まらない限りグラフで表すことができないのでグラフ省かせていただきます。

## モデル（全結合層）
ここで、何を以て深層学習の完成とするかということを話しておきたいと思います。それはずばり、ウェイト（重さ）とバイアスという2種類の値を最適化することです。これらの値が更新されるにつれ、深層学習はすすんでるということになります。このことをいったん特に考えず鵜呑みにしてモデルの説明をご覧ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/b20429fb-73dd-5d4c-c36e-c8d2503993ef.png)

こちらが深層学習のモデルといわれるものです。縦3列にならんでいる○がニューロンで、1列ずつのことを層といいます。モデルの一番初めの層を`入力層`といい、生のデータをそのまま各ニューロンに入力します。最後の層を除いたその他の層を`隠れ層`といい、この層があるので「Deep Learning」と呼ばれます。最後の層を`出力層`といいます。

層から層への値の受け渡しは1:1ではなくすべてのニューロンの値が次の層のすべてのニューロンにつながってます。こう言った層を全結合層といい、基本的にこれの連続です。

## ウェイト（重さ）
このままだと更新するものがないです。ウェイトは重さという日本語の通り、値の重さを表していて、上の図ではよくニューロン間の線の太さとしてあらわされることがあります。活性化関数に通されたデータは次の層に行く前にウェイトと掛け合わされます。

例えば`5`というデータを次の層に出力するとき、大事なデータであったらデータの重さは重いということになり2などの値を掛けて増幅されます。逆に大事ではないデータは0.4などを掛けられ減衰されます。

ウェイトはデータの重要度を決める値で、ニューロンのつながりの数だけ存在します。この値を更新して最も正解率の高いウェイトのパターンを決めます。

## いったんまとめ
ここで全結合層の動きももう一回見てみましょう。ここからやや頭を使うようになります。何回も読んで理解しちゃいましょう！

$n$層目と$n-1$層目を考えます。$n-1$層目のニューロンの数は3、$n$層目のニューロンの数を2とします。このときこの2層の間にウェイトは6個必要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/22a836d6-c8ff-4466-18b8-9206f5219f87.png)

各層のニューロンに数字を付けました。ここで、「$n-1$層目の$j$のニューロンと$n$層目の$i$のニューロンの間のウェイト」を$w_{ij}$とします。j→iをijと表したのは$n$層目に着目してるからです。また、「$n-1$層目の$j$のニューロンから出力される値」を$a_j$とし、「$n$層目の$i$のニューロンに入力される値」を適当に$a'_i$とします。$a$ではなく$a'$としたのは活性化関数に入れられる前だからです。

ニューロンから出力された値にはウェイトをかけて次の層のニューロンに入力されるので、これを1個目のニューロンについてみるとこうなります。

```math
a'_1 = a_1w_{11}+a_2w_{12}+a_3w_{13}
```

これを総和を用いて表すとこうなります。

```math
a'_1 = \sum^{3}_{j=1} a_jw_{1j}
```

$n$層目すべてのニューロンに一般化させて、

```math
a'_i = \sum^{3}_{j=1} a_jw_{ij}
```
こう表すことができました。

## バイアス
上の式

```math
a'_i = \sum^{3}_{j=1} a_jw_{ij}
```
では$a$がすべて0になったとき$a'$はすべて0になってしまいます。あとは永遠に0が繰り返されるだけです。そこで0ではないバイアス$b$を足します。

```math
a'_i = \left( \sum^{3}_{j=1} a_jw_{ij} \right) +b
```

このバイアスも適切な値まで更新していくことになります。次からはウェイトとバイアスの更新の仕方を開設していこうと思います。

## 損失関数
いろんな種類がありますが、「予測した値を正解の値の差」を求める関数です。この差、誤差を損失（loss）といい、パラメータ更新に使うことになります。

# 深層学習
## 最急勾配法（勾配降下法）
いったんニューロンやニューラルネットワークを離れて数学的な説明になってしまいます。
### 微分
微分とは1変数関数のある点においての接線の傾きを求める行為のことを言います。出てきた式を導関数といいます。導関数に傾きを求めたい$x$を代入すると傾きが出てきます。
> 変数関数...すべての変数が決定するために必要な変数の数がn個の関数。1変数関数だと$x$が決まれば$y$が決まる。$n+1$次元のグラフとなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/87e5ab57-6186-a083-1582-6d1fc243b750.png)
数学的な説明は読み飛ばしても構いません。


---
#### 数学的な説明
今回は$y = x^2$の導関数を求めてみようと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/8d52a2f9-167e-cda7-a4a8-caffb5965fc4.png)
この画像は曲線の一部です。適当な2点を決めてその間の傾きを求めます。2点をつなぐように引いた直線の傾きがこれに当たります。実際に求めてみましょう。
$y = f(x) = x^2$とする。また、左側の点の$x$座標を$a$とする。ここで2点間の傾きは、

```math

\dfrac {f(a+h)-f(a)}{h}

```

$(右の点のy座標) - (左の点のy座標)$で高さが出て$h$で割ることによって傾きがもとまってます。
しかし、現在は接線の傾きを求めようとしているので2点じゃダメです。そこで$h=0$として2点を1点にします。ただ、$h$で割ってる以上、0で割ることはできないので「$h$を0に極限まで近づける」という処置を行います。この処置を極限といいますが、こうすれば$h$は0と扱えるうえ、hで割ることができます。普通に計算して最後に$h$に0を代入するだけです。

```math
\begin{eqnarray}

\lim_{h \to 0} \dfrac {f(a+h)-f(a)}{h} & = & \lim_{h \to 0} \dfrac {(a^2+2ah+h^2)-(a^2)}{h}\\
& = &\lim_{h \to 0} \dfrac {2ah+h^2}{h}\\
& = &\lim_{h \to 0} 2a + h \\
& = &2a

\end{eqnarray}
```
これで傾きは$2a$と出ました。そして$a$は特に範囲が決まってるわけではないので$x$と置き換えることができます。（本来は最初から$x$で計算します。）以上より関数$y = x^2$の導関数は$2x$となります。$x$に任意の数字を代入すればその点の傾きがもとまります。

### 偏微分
微分は接点の傾きを求める行為でした。偏微分は1変数関数だけではなく2や3変数関数に使えます。手法としては$n$変数関数があったとき、$n-1$個の変数を固定させて微分を行うというものです。

ここで大事なのは1つだけ固定されない変数があるということです。こうすると1変数関数に落とし込むことができ、単純微分で解くことができます。例えば、$z = f(x, y)$を偏微分し、固定しない変数が$x$のとき、「$f(x, y)$を$x$に関して偏微分する」や「$z$を$x$について偏微分する」などという表現をして以下のように式で表します。

```math
\dfrac {\partial f}{\partial x} (x, y)　or　\dfrac {\partial z}{\partial x}
```

自分は前者を使うようにします。

よくわからない人は「変数を固定して微分する。（傾きを求める）」というのを理解しとくだけでも大丈夫です。

### 再急勾配法（本題）
損失関数を$E$とします。突然ですが、これを適当なウェイト$w_{ij}$で偏微分します。

```math
\dfrac {\partial E}{\partial w_{ij}}
```

これを行うことで傾きを求めることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/d6548dac-da66-38b9-b10d-aff00314fbc0.png)


左側の点のように傾きが負（マイナス）の場合は$w$を正（プラス）の向きに更新、つまり大きくします。また、右の点のように傾きが正のとき負の向きに更新、つまり小さくします。こうすることで損失関数$E$の損失が一番小さい☆マークのところまで$w$を更新できます。同様のことをバイアスでも行うことができます。

注意すべきなのはだれもこの曲線は分からないということです。我々がわかるのは現在の傾きだけなので、傾きを頼りに更新します。それこそが「再急勾配法」です。また、運悪く、✕マークのところに引っかかることがありますが、これはどうすることもできません。精度に限界がある理由の一つになっています。

### 学習率
ウェイトの更新幅（どれくらいずらすか）のことを学習率といいます。学習率を掛け算することでこの幅を更新しています。だいたい`0.001`ぐらいの値を設定することが多いです。

### 数学的なお話
数学的に表すとこうなります。

```math
w_{ij} := w_{ij} - \alpha \dfrac {\partial E}{\partial w_{ij}}
```

$:=$は右辺を左辺に代入するという意味です。$\alpha$は学習率です。絶えず自分（$w_{ij}$）を更新していることがわかると思います。

## 誤差逆伝播法
誤差逆伝播法は効率よくウェイトとバイアスを更新していく手法です。もちろん勾配法を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263233/57f84f45-e250-a72b-9235-f3331a105018.png)

1. 現状のウェイト・バイアスで予測を行う。（`Forward`）
2. 損失関数で損失を求める。（`Predict, Loss`）
3. その損失をウェイト・バイアスで後ろから偏微分、勾配法して更新。（`Backprop`）
4. 更新後に1に戻る。

このような手順で更新すると層の多さに関係なく更新できるらしいです。

#CNN（Convolutional Neural Network）
> 気がい向いたら更新します。

# さいごに
数学が苦手な人のために簡単に深層学習を説明してみました。自分はまだ数学2Bを履修し終わってないのですが理解できました。数学って楽しいですね。CNNも気が向いたら更新させてもらいます。