深層学習ことはじめ

# はじめに
この記事は「[長野高専 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nnct)」19日目に合わせて書いたものです．
はじめまして．Advent Calendar初参加のやじるしです．この記事では，僕が現在興味を持っている機械学習・深層学習について概要をまとめたものです．お見苦しい点もあると思いますが，これを読んで，機械学習・深層学習に興味を持ってもらえると幸いです．

# 学習
### 学習の定義
コンピュータの学習についてトム.M.ミッチェルさんの定義は次のとおりです．
> コンピュータプログラムがタスクのクラス$T$と性能指標$P$に関し経験$E$から学習するとは，$T$内のタスクの$P$で測った性能が経験$E$により改善されることを言う．
>

この定義に出てくる記号$T, P, E$は以下のような意味を持っています    
- タスク$T$　：プログラムが解くべき課題
- 性能指標$P$：タスクの性能を評価する指標．経験を積んでどれだけタスクが解けたか．
- 経験$E$　　：プログラムに与える何らかのデータ，学習データ    
定義をブロック線図で表してみましょう
![Learn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/8b2c8988-6265-ba03-8592-1a3df6080f71.png)

# 機械学習（教師あり学習）
### 教師あり学習とは
機械学習は機械に何らかのデータを反復的に与え，そのデータ集合からパターンを見つけ出す手法です．このパターンを見つけ出すことを私達は学習と呼んでいます．では，機械学習を形式的に説明してみます．
### 仮説
仮説$h$はコンピュータが学習する数理モデルです．特徴量$\boldsymbol{x}$とパラメータ$\boldsymbol{\theta}$で表現し，学習によりパラメータ$\boldsymbol{\theta}$を定めることで、仮説の特徴量$\boldsymbol{x}$に予測（または分類）したいものを入力すると予測結果（分類結果）が返されます。学習の目的はこの仮説のパラメータ$\boldsymbol{\theta}$の適切な値を見つけ出すことです．
### 誤差関数
誤差関数$J(\boldsymbol{\theta})$は，その名の通り仮説$h$と学習データとの誤差を出力する関数です．回帰問題の場合には誤差関数は最小二乗法，分類問題の場合には交差エントロピー誤差などが扱われます．結論を言うと，学習はこの誤差関数の最小値を見つけることです．誤差関数の最小値を見つけることは仮説hと学習データの誤差が一番小さなパラメータ$\boldsymbol{\theta}$を見つけることと同じです．これらについて数式で表すと，次式のようになります．
$$\newcommand{\argmin}{\mathop{\rm arg~min}\limits}
 \argmin_{\boldsymbol{\theta}} J(\boldsymbol{\theta}) $$

### 最適化アルゴリズム
誤差関数はわかりました．しかし、問題点はコンピュータはどのように上式を求めるのか，すなわち，どのように誤差関数Jの最小値を探し出すかです．ここで，微分積分学が役に立ちます．関数の極値を探してみましょう．定義域が開区間の場合，もしくは定義域が適切な閉区間の場合，関数の最小値は必ず極小値を取ります．ここでは分かりやすく特徴量が1変数（すなわち，仮説のパラメータが$\theta$（スカラー量）のとき）とし，最小値を求めたい誤差関数$J$を2次関数とします．$J-\theta$グラフをプロットしてみましょう．
![勾配降下法.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/f12cce72-f54d-ae7b-1934-071813805aea.png)
ここで，次の式を考えます．
$$\theta_{n+1} := \theta_{n} - \mu \frac{d}{d\theta}J(\theta_n) $$
この式を計算で計算を続けていくと，$\theta_n$は極小値に収束します．（実際に手計算してみると収束してることがわかります）このアルゴリズムを勾配降下法といい，最適化アルゴリズムで最も基本的なものです．一般化すると以下の式になります．
$$ \boldsymbol{\theta}_{n+1} := \boldsymbol{\theta}_n - \mu \frac{\partial}{\partial \boldsymbol{\theta}}J(\boldsymbol{\theta}_n) $$
ここで，$\mu$は学習率と呼ばれる学習の中で影響を受けないハイパーパラメータと呼ばれるものです．人間が任意で決めることができます．（他にもコンピュータがハイパーパラメータを決める手法も考え出されています）
勾配降下法は基本的なアルゴリズムですが，欠点として，局所的最小値を求めるアルゴリズムであるので，適切な値から始めないと最小値ではない極小値に収束する可能性があります．
### 組み合わせてみよう
以上の仮説$h$，誤差関数$J$，最適化アルゴリズムを組み合わせてみましょう．学習の章で書いたブロック線図と照らし合わせてみます．
![Learn (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/677a8099-6262-3119-8c72-6f8c5e169a97.png)

# ニューラルネットワーク
### ニューロン
人間の脳にはニューロンと呼ばれる細胞を持っています．これが相互に接続されることで，人間は記憶する，考えるなど知的活動を行うことができるのです．これをモデル化してみます．
### ニューロンをモデル化する
ニューロンを数式で形式化します．
![neuron.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/799a0629-bca6-e3c0-2ee4-0051f3a46fa0.png)
ここで，仮説$h$は各特徴量$x_i$﻿とパラメータ（重みと呼ばれます）$\theta_i$の積の和，$g$はこのあと説明する活性化関数と呼ばれるものです．すなわち，この図が表す数式は以下のようになります．（$b$﻿はバイアスです．$\theta_0 = 1$とすると$b$﻿を書かずに表現することができます．）

```math
h = \sum_i x_i \theta_i + b \\
y = g(h) 
```
ここで，シグマ記号で書かれたところが，内積と同じ形になっています．すなわち，この数式はベクトルで表現することができます．特徴量を$\boldsymbol{x} = [x_0, x_1, ...x_n]$，パラメータを$\boldsymbol{\theta} = [1, \theta_1, ...\theta_n]$としますと，次の数式で表されます．

```math
h = \boldsymbol{x}\cdot\boldsymbol{\theta}^\intercal\\
y = g(h)
```
とても簡潔にかけますね．これを単純ニューロンといいます．

# 活性化関数
### 活性化関数とは
ニューロンの章で，活性化関数という言葉が出てきました．活性化関数は，特徴量とパラメータの積の和（もしくは内積）hの値を変換する関数です．単純なものだと，hが0.5以上なら1を出力する，そうでなければ0を出力する関数です．

```math
g(h) = \left\{ \begin{array}{l}
\displaystyle 1\; ( h \geq 0.5  ) \\
0 \; ( x < 0.5 )
\end{array} \right.
```
### 線形関数から非線形関数へ
活性化関数が恒等関数だったとき，次式が成り立ちます．
$$ y = \sum_i x_i \theta_i $$
見て分かるとおり，これは特徴量とパラメータの積を求め，それを足し合わせているのでこれは線形関数になります．これでは，仮説が直線でしか表現することができず，複雑（非線形）な予測・分類ができません．そこで，この線形関数を非線形関数に通して，仮説を非線形な関数にして複雑な予測・分類ができるようにします．
### 活性化関数の微分
非線形関数なら，複雑な予測，分類ができるようになることがわかりました．それに加え，微分可能な関数であることが必要です．これは，学習（誤差逆伝播法）をするにあたり活性化関数の一階微分が必要になるからです．次に示す関数は非線形で一階微分可能です．
### Sigmoid関数
Sigmoid関数は次の式で定義される関数です．
$$ g(h) = \frac{1}{1+e^{-h}} $$
微分をすると，次の式になります．
$$ \frac{d}{dh}g(h) = g(h)(1-g(h)) $$
特徴は，微分可能でかつ非線形であることと，微分した式が簡潔に書けることです．また，0から1までの値を出力するため，確率を表現することができます．しかし，この活性化関数は，何回も微分をし続けると勾配が消失する勾配消失問題を持っています．そのため，複雑な仮説の学習（隠れ層が深いニューラルネットワークの学習）ができませんでした．そこで，ReLU関数が登場します．
### ReLU関数
ReLU関数は次の式で定義される関数です．

```math
g(h) = \left\{ \begin{array}{l}
\displaystyle 0\; ( h \leq 0  ) \\
h \; ( h > 0 )
\end{array} \right.
```

場合分けをして微分すると，次の式になります．

``` math
\frac{d}{dh}g(h) = \left\{ \begin{array}{l}
\displaystyle 0\; ( h < 0  ) \\
微分不可能 \; ( h = 0 )\\
1 \; (h > 0)
\end{array} \right.
```
この活性化関数の特徴は，微分をして最大値が1になることです．これにより，学習の際に勾配が消失しにくくなります．ただし問題点として，$h = 0$の点で微分不可能なため学習がうまく行かない場合があります．

# 順伝播ニューラルネットワーク
### ニューラルネットワークを整理しよう
では，上で説明したニューロンをつなげていきましょう．図は以下のようになります．
![DeepNeuralNetwork.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/db59c65a-5728-9d16-3aa7-8dc230a0cff3.png)
よく見る図ですね．これは，上で言った単純ニューロンを縦と横につなげたものです．
ここで，縦で区切ったとき，層のように積み重なって見えます．この層に3段階に分けて名前をつけます．左から右に信号が流れるとすると，一番左の入力に値する層を「入力層」，中間にある層を「隠れ層」，一番右の出力に値する層を「出力層」といいます．数式で表してみると，次式のようになります．入力層に入力する特徴量を$\boldsymbol{x} \in \mathbb{R}^i$，出力層に出力する結果を$\boldsymbol{y} \in \mathbb{R}^o$，隠れ層を$\boldsymbol{u}^{(0)} \in \mathbb{R}^{u0}$とし，パラメータを$\Theta^{(0)} \in \mathbb{R}^{i\times u0}$，$\Theta^{(1)} \in \mathbb{R}^{u0\times o}$とします．ただし，$i$は入力層のサイズ，$u0$は隠れ層のサイズ，$o$は出力層のサイズです．また，記号の肩に乗っている括弧でくくられた値は層の番号を指します．

```math
\boldsymbol{u}^{(0)} = g^{(0)}(\boldsymbol{x}\cdot \Theta^{(0)})\\
 \boldsymbol{u}^{(1)} = g^{(1)}(\boldsymbol{u}^{(0)}\cdot \Theta^{(1)})
```
### 隠れ層を「深く」する
上の式から分かるとおり，ニューラルネットワークは合成関数になっていることがわかります．合成関数により，関数がいくつも合成されていくと，仮説は複雑な表現が可能になります．そのために，隠れ層の数を増やしていきます．言い換えると，層を深くしていきます．ここから，ニューラルネットワークを使用した仮説の学習のことを深層学習と呼ばれているのです．ただし，層を深くしすぎると学習に時間がかかる，自由度が上がりすぎて学習データにフィッティングしすぎてしまう（過学習といいます）可能性があるので，それも考慮して適切な深さに設定する必要があります．

# 誤差逆伝播法
### ニューラルネットワークが学習するには
今までに説明したものは，ニューラルネットワークに順伝播で入力をしている，すなわち，関数の引数に値を入れて，分類や予測をする方法です．しかし，正しい分類，予測をするためには学習が必要です．ここで厄介になるのが，パラメータがかなり多いかつ複数のニューロンに接続されているので，一個一個計算するのは大変であるということです．そこで，ニューラルネットワークを学習する手法として誤差逆伝播法が考案されました．これにより，ニューラルネットワークは精度が高く，汎用性の高い数理モデルになれたのです．
### 誤差逆伝播法を導出する
誤差逆伝播法を導出してみましょう．誤差逆伝播法というからには，順伝播とは逆の方向をたどることになります．誤差逆伝播法のポイントは「誤差$\delta$を層に逆伝播させ続ける」です．
#### 出力層を考える
出力層の各ニューロンで誤差を考えてみます．ここで出てくる$L$は層の個数の最大値です．すなわち，$L$番目の層を考えることは出力層を考えることと同じです．
$$ \frac{\partial J}{\partial \Theta_{ij}^{(L)}} = \frac{\partial J}{\partial u_j^{(L)}}\frac{\partial u_j^{(L)}}{\partial \Theta_{ij}^{(L)}} $$
偏微分の連鎖律（合成関数の偏微分）を使って式を展開しました．図を書いてみると，分かりやすくなると思います．
![DeepNeuralNetwork (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/fba0a3c6-2a73-63b7-c52e-80ed29389ccc.png)
出力層での線形和$u_j^{(L)}$はパラメータ$\Theta_{ij}^{(L)}$を持っており，これを微分すると，$z_i^{(L-1)} = g(u_i^{(L-1)})$になるはずです．（$u_j^{(L)} = ... + z_i^{(L-1)}\Theta_{ij}^{(L)} + ...$という形になっています．パラメータで微分すれば，$z_i^{(L-1)}$です）
そして，誤差関数$J$をニューロンでの線形和（活性化関数を通す前の式）で偏微分したものを誤差$\delta$とします．そうすると，次の式で書けるのです．
$$ \frac{\partial J}{\partial \Theta_{ij}^{(L)}} = \delta_j^{(L)} z_i^{(L-1)} $$
また，$\delta_j^{(L)}$をもっと展開してみます．
$$ \delta_j^{(L)} = \frac{\partial J}{\partial u_j^{(L)}} = \frac{\partial J}{\partial y_j}\frac{\partial y_j}{\partial u_j^{(L)}} $$
この式なら，出力層とその出力を用いた誤差関数だけで誤差が計算できます．では，この誤差を隠れ層に逆伝播させてみましょう．
#### 隠れ層
隠れ層も出力層と同様に，誤差関数とパラメータの偏微分を考えます．
$$ \frac{\partial J}{\partial \Theta_{ij}^{(l)}} = \frac{\partial J}{\partial u_i^{(l)}} \frac{\partial u_i^{(l)}}{\partial \Theta_{ij}^{(l)}}  $$
ニューロンでの線形和$u_i^{(l)}$とパラメータ$\Theta_{ij}^{(l)}$の偏微分は出力層で考えたときと同様に，$z_i^{(l-1)}$です．式を書き直すと次のようになります．
$$ \frac{\partial J}{\partial \Theta_{ij}^{(l)}} = \delta_i^{(l)}z_i^{(l-1)} $$
誤差$\delta_i^{(l)}$はどうなるのでしょうか．隠れ層におけるニューラルネットワークの図を考えてみます．
![DeepNeuralNetwork2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680238/41b54229-34fb-4338-c165-6318cc9a4b76.png)

逆伝播を考えると，誤差$\delta_i^{(l)}$は複数のニューロン$u_l^{(l+1)}$の影響を受けます．これは，偏微分の連鎖律を用いることで解消できます．
$$ \delta_i^{(l)} = \frac{\partial J}{\partial u_i^{(l)}} = \sum_k \frac{\partial J}{\partial u_k^{(l+1)}} \frac{\partial u_k^{(l+1)}}{\partial u_i^{(l)}} $$
このとき，誤差関数と線形和の偏微分が右辺に存在しています．これは誤差ですね．誤差$\delta_k^{(l+1)}$を用いて，式を変形しましょう．
$$ \delta_i^{(l)} = \sum_k \delta_k^{(l+1)}\frac{\partial u_k^{(l+1)}}{\partial u_i^{(l)}} = \sum_k \delta_k^{(l+1)}\frac{\partial u_k^{(l+1)}}{\partial z_i^{(l)}}\frac{\partial z_i^{(l)}}{\partial u_i^{(l)}} $$
この式から分かるとおり，これは$\delta$の漸化式になっています．ということは，これを各層で用いると，$\delta^{(L)} \rightarrow \delta^{(L-1)} \rightarrow ... \rightarrow \delta^{(1)}$と誤差を逆伝播することになります．これで，出力層の誤差が計算できれば，その誤差を伝播して簡単にニューラルネットワークのパラメータを改善することができるようになります．
# 深層学習
### 特徴表現学習
以上が基本的なニューラルネットワークの仕組みと学習の方法になります．上で出てきたいろんなニューラルネットワークの図で分かるとおり，ニューラルネットワークは人間の脳の神経細胞を模したニューロンがいくつも結合されて，それが相互に影響しあい，精度の良い数理モデルを作っています．このような手法を深層学習（ディープラーニング）と呼びます．ニューラルネットワークが層が何個も積み重なった形をしていることから「深層」という名前がついています．この深層学習には特徴表現学習という大きな特徴を持っています．
特徴表現学習は，数理モデル（すなわち，ニューラルネットワーク）自身が特徴量を見つけ出すように学習をするということです．これは，ニューラルネットワークでいう隠れ層に該当します．なんども隠れ層に入力が入ることで，ニューラルネットワークはそこで学習を通して見つけた適切な特徴量を見つけ，それを予測に使用します．画像や音声分析などのパターンを見つけるものに使用されるのはこの特徴があることが理由です．画像などに特徴量をつけようとしたとき，どこを特徴量にすればいいか分からず，人間が手作業で行う（または，その専門家でないと）困難です．しかし，ニューラルネットワークにこの特徴があるおかげで，様々な分野で深層学習が使われるようになりました．すごいですね．
### いろんなニューラルネットワーク
今回は順伝播ニューラルネットワークで説明しましたが，他にもニューラルネットワークは結合の方法によって様々なものがあります．例えば，以下のようなものが挙げられます．

- 畳み込みニューラルネットワーク（CNN）：目の役割をするネットワーク．画像などで用いられる．
- 回帰型ニューラルネットワーク（RNN）: 時系列データ（自然言語など）に用いられる

### なぜGPUなのか
機械学習とくに深層学習では，GPUがプロセッサとしてよく使われます．これは，GPUが並列演算処理に特化しているからです．深層学習の理論を見てみると，ほとんどの演算はベクトル，行列（一般にテンソル）で書き直すことができ，また，単純な処理を何度も繰り返すので，このような処理が得意なGPUは学習にはうってつけなのです．

# 深層学習フレームワーク
深層学習フレームワークは，層（レイヤー）の概念を用いて，簡単にニューラルネットワークを構築できるようにしたものです．現在，大きく分けてDefine and RunとDefine by Runの2種類のネットワーク構築方法があり，フレームワークによって異なります．
### Define and Run
Define and Runはニューラルネットワークを先に定義してから，それに基づいて学習をするネットワーク構築方法です．ネットワークの形は変化しないので最適化は容易ですが，分岐処理などが入れられない，もしくは，フレームワークで用意されているものしか扱えないので複雑なネットワーク（RNNなど）の構築は難しいです．以下のフレームワークが挙げられれます．

- TensorFlow : Google製の深層学習フレームワーク．
- Keras : TensorFlowを学習に用いたラッパー．簡単に書ける．

### Define by Run
Define by Runはニューラルネットワークの定義と学習を同時に行うことでニューラルネットワークを構築する方法です．学習の手順も記述するため，分岐処理や繰り返し処理を入れられるので複雑なネットワークが構築しやすく（柔軟性がある），またデバッグもしやすいことが特徴です．しかし，ネットワークを途中で変化させることができるので最適化が難しいです．この手法を提唱したのは日本にある株式会社preferred networksで，ここで開発された深層学習フレームワークChainerがこの手法を初めて使用しました．現在はChainerの開発は終了し，Chainerと開発思想が似たPyTorchが使用されています．

- Chainer : 日本で開発されたフレームワーク．初めてDefine by Runが使われた．開発は終了している．
- PyTorch : Chainerの開発思想が影響されて作られたFacebook製の深層学習フレームワーク．論文でよく使われる．

# 応用
これは僕自身が興味がある深層学習の応用例です．
### CeVIO AI
CeVIO AIはCeVIOプロジェクトにより開発された音声合成ソフトウェアです．現在は可不が有名ですね．声を聞いてみると分かりますが，かなり人間に近い声なんです．これは，従来の手法（隠れマルコフ連鎖）とは異なり，深層学習（DNN，CNN）を用いているからだそうです．論文も公開されています．(["Singing voice synthesis based on convolutional neural networks"](https://arxiv.org/abs/1904.06868))

# 終わりに
この記事では機械学習の概要，深層学習の概要，フレームワークについて触れました．
僕は機械学習・深層学習についてを勉強している途中なので拙い箇所が何箇所かあったと思いますがすみません．この記事を書いていて今までよく分かっていなかったところ（特に誤差逆伝播法）や，更に理解が深められたところあったので，僕自身はこの記事を書く意味があり良かったと感じました．もしここまで読んでみて，機械学習・深層学習に興味を持ってもらえたら幸いです．

# 変更
- 2月9日 : 勾配降下法の数式を変更しました．誤差関数$J(\theta)$を最小化したときのパラメータ$\theta$を探すため，適切な記号は$\min$ではなく，$\argmin$です．