よくわからないけど、分かった気になれるDeep Learning（ゼロから作るDeep Learningをやってみた）


# はじめに
GWの連休中ふと思い立って[「ゼロから作るDeep LearningーーPythonで学ぶディープラーニングの理論と実装」](https://www.oreilly.co.jp/books/9784873117584/)を手に取り勉強&実装してみたので、感想と自分なりの理解をメモ。
これから本書を手に取って勉強し始める人にとって、理解の手助けや学習コストをどの程度見込めばいいのかの参考になればなと思いますが、時間たった時に自分が見返して思い出すためのメモなので読みづらい部分もあるかもしれません。。。
細かい数式は参考書を見て詳しく追えばいいと思うので、ふわっと「Deep Learningってそういう仕組みなんだ」っていうふうに概要を分かった気になれればいいなと思いながら書いています。

自分は本書を一通り理解し、付属のサンプルコードを少し拡張することで、
"ペイント"で手書きした数字の画像ファイルの数字判別を行うシステムを作ることができました。
（※著作権の問題が絡んできそうなので、ソースコードは公開していません。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602075/8beb625b-57a0-65b3-766a-f2c8cd941679.gif" width=75%>


# ざっくりDeep Learningについて
Deep Learningは「識別」と「学習」の機能のミックス。
それぞれを理解するためには、「<font color="Blue">入力値</font>」、「<font color="Green">ニューラルネットワークの関数</font>」、「<font color="Red">出力値</font>」の3つの関係が重要。

* 「識別」とは、<font color="Blue">入力値</font>と既知の<font color="Green">ニューラルネットワークの関数</font>から<font color="Red">出力値</font>を求めること。（例: 何の数字かはわからない画像を読み込んで5だと認識する）

* 「学習」とは、正解（<font color="Red">出力値</font>）が既知であるテストデータ（<font color="Blue">入力値</font>）から<font color="Green">ニューラルネットワークの関数</font>を求めること。（例: 様々な手書きの数字の5の画像を読み込ませて、認識結果が5となるように<font color="Green">ニューラルネットワークの関数</font>をアップデートする）

なお、
「識別」の機能では__活性化関数__（2, 3章）、
「学習」の機能では__損失関数__、__勾配降下法__、__誤差逆伝播法__が重要であり（4, 5章）、
学習精度を高めるための工夫も様々ある（6, 7, 8章）。

# 「識別」機能について（2,3章）

前述した通り「識別」とは、<font color="Blue">入力値</font>と既知の<font color="Green">ニューラルネットワークの関数</font>から<font color="Red">出力値</font>を求めること。
例として、何の数字かはわからない画像を読み込んで5だと認識するするフローについてはこんな感じ（下図も参照）。

1. 何の数字かわからないけど読み込む画像について20×20のメッシュに切る（<font color="Blue">入力値</font>）。
1. 「ここのメッシュが黒塗りだったら数字XXの可能性が高い。」といった判断を下すような既知の<font color="Green">ニューラルネットワークの関数</font>（所謂学習済データ）を用いる。（どうやって関数が定義されているかは「学習」で後述）
1. 読み込んだ画像が数字0~9である確率をそれぞれ出す（<font color="Red">出力値</font>）。

|<img alt="識別処理フロー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602075/e52208fa-b8af-2d19-bbce-e16927c013d2.png">
|:-:|

ここで大切なのは、この画像は「5だ！」のように断定しているのではなく、「5である確率が70%。なので5と思われる。」のように判断していて、出力値は1行10列の行列となるようにしていることです。
また、__活性化関数__を用いて出力された行列Yの「各要素が正の値になる」かつ「各要素の合計が"1"となる」とするよう工夫しており、代表的な関数としては[sigmoid関数やsoftmax関数](https://qiita.com/SabanoMizuni/items/ab4b73cd9b8e733da11a)がある。


# 「学習」機能について（4, 5章）

前述した通り「学習」とは、正解（<font color="Red">出力値</font>）が既知であるテストデータ（<font color="Blue">入力値</font>）から<font color="Green">ニューラルネットワークの関数</font>を求めること。
この<font color="Green">ニューラルネットワークの関数</font>Wを__損失関数__、__勾配降下法__、__誤差逆伝播法__を用いて求めます。

|<img alt="学習" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602075/79d1cb10-1e6b-d181-b524-ad21be90bf8c.png">
|:-:|


> __脱線：懐かしい高校数学__
`y = f(x)`という関数において、入力値xと出力値yがわかっていれば、関数f(x)がわかるはず。

> ```math
f(x) = ax^2 + bx + c
```
> 例えば2次関数のf(x)であれば、(x,y)の組み合わせが3通りあれば、a,b,cが求まり、f(x)が求まります。
それのちょっと難しくて行列のバージョンを「学習」でやっていると考えればよいわけです。
（高校数学が懐かしい:relaxed:）。


__損失関数__
上の「Deep Learningの流れ（学習）」の図について、

- ニューラルネットワークの関数Aを用いると数字"5"である確率が0.7であった
- ニューラルネットワークの関数Bを用いると数字"5"である確率が0.85であった

といった場合に、どちらのニューラルネットワークの関数の方が__どの程度__優秀なのかを評価するための指標が損失関数Lです。
損失関数Lはエントロピー誤差とか2乗和誤差とかいろいろありますが、2乗和誤差の数式はコレ↓。

```math
L = \sum (X_{出力値}-X_{正解値})^2\\
```

ここで例えば、数字の画像を読み込んだ時の2~5の確率（出力値）が以下だった場合、それぞれのニューラルネットワークA,Bの損失関数Lは0.34, 0.14でニューラルネットワークの関数Bが優秀だとわかる。

<img alt="損失関数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602075/ae6e7cf4-a4d8-9df4-1350-9a11897c562c.png" width="600px">

また、当たり前ですが損失関数Lが0に近ければ近いほど正確に識別できているということであり、そのニューラルネットワーク関数Wを求めることが「学習」において重要ということです。
で、損失関数Lの値を小さくさせて（__勾配降下法__）関数Wを再計算します。

__勾配降下法__
現在の場所から勾配方向に一定の距離だけ進んだ場所のf(x)を求め、またその点から勾配方向に一定の距離ΔXだけ進んだ・・・というのを繰り返すことで極小値を求める手法。

2次元(下左図)であれば、X>極大値の任意の点(x,f(x))から始まれば、最終的に勾配法により極小値に落ち着く（勾配が0となる点が極小値）。
二乗和誤差の損失関数Lが3次元の場合(下左図)でも同様で、任意の点での勾配を求めて、勾配方向にΔXだけ進めるという操作を繰り返せば極小値にたどり着く。

<img alt="勾配降下法" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602075/befb0309-6623-8f9a-6391-e6330f841561.png" width="600px">

n次元行列であっても偏微分することで極小値に向かう勾配が求まるので、勾配方向Δxだけずらしたものを出力値の行列Yとし、元の行列Xと子の行列Yからニューラルネットワークの関数Wを計算しなおすことで「学習」できる。

__誤差逆伝播法__
割愛。
まぁ偏微分と行列の特徴を抑えた計算が楽な手法。


# 学習精度を高めるための工夫（6, 7, 8章）
割愛。
減衰振動微分方程式とかも出てきて、ここから先はディープな世界です。。。


# まとめ
今までの数学人生で遭遇したステージボスが全員集合して襲い掛かってきた感が凄いです。
どういうことかというと、

- 偏微分方程式（数ⅢC）
- n次元行列の行列積や転置（大学数学）
- 減衰振動微分方程式（院試）

など昔大学受験や大学在学中に苦労して勉強したなぁという内容のオンパレードで、Deep Learningについて学習する前にこれらを軽く思い出す必要がありました。
なのでプログラミングは強いけど、行列計算とか偏微分方程式はちょっと・・・という人には、やや学習コストが高くつく印象です。

ここまで読んでいただきありがとうございました。:bow: