機械学習という人工知能の技術

# はじめに
機械学習は面白く、夢の広がる技術であるにも関わらず、手を出そうとしないエンジニアがとても多いと感じていました。私もはじめは「機械学習？取り敢えず学習はするんだろうけど全然イメージが付かないし使えるかも分からない」とこの技術に関してあまり興味を持っていませんでしたが、ある時なんとなく「機械学習でも少し調べてみるか」と**Coursera内にあるAndrew Ng氏によるMachine Learningのコース**の動画を見ていて「これは滅茶苦茶夢のある技術だ」とみるみるハマっていき、そこで本格的に「勉強してみよう」という気になりました。

> アンドリュー・ン（英語: Andrew Ng、中国語: 吳恩達、1976年4月18日 - ）は、中国系アメリカ人の計算機科学者、人工知能研究者、投資家、起業家、**Google Brain（英語版）の共同設立者、 Baiduの元副社長兼チーフサイエンティスト、スタンフォード大学 兼担教授**である。Eラーニングサービスの**Courseraとdeeplearning.aiとAI Fundの創始者**でもある[2]。
>
> (出典：[ウィキペディア (Wikipedia): フリー百科事典「アンドリュー・ン」](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%89%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%BB%E3%83%B3))

機械学習は面白い、しかしそれを学ぼうとするエンジニアは少ない。私が思うに機械学習をやろうとしないのは「処理内容が全くイメージが付かないから」なんだと思います。だから、この記事によって最低限「機械学習完全に理解した」という状態に持っていけるくらいに分かりやすくかつ十分な情報を持ってして機械学習というものを説明しようと思います。

# この記事で得られること
- 機械学習のアルゴリズムが分かる
- 機械学習を一から実装出来るようになる

# 機械学習を理解する
## 機械学習の定義
> A computer program is said to *learn* from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.

> コンピュータプログラムがタスクのクラスTと性能指標Pに関し経験Eから**学習**するとは、T内のタスクのPで測った性能が経験Eにより改善される事を言う

> （出典：[ウィキペディア (Wikipedia): フリー百科事典「機械学習」](https://ja.wikipedia.org/wiki/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92)）

分かりやすくいうと、あるタスクとタスクの性能指標があるとして、経験（例えば、訓練データ）から学習して、その性能指標で測った時に性能が上がっていればそれは機械学習と言いますよ、ということです。

## 二つの実装方法
機械学習と聞いても「学習ってなんだろう。そもそも学習の結果として何があるのだろう」と、情報のヒントなしでは中々イメージがし辛いです。イメージを持ってもらうために以下で簡単に説明します。


![スクリーンショット 2020-12-01 7.33.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/9c9d3784-968f-db8d-dad5-d835d652346c.png)

※ Courseraで説明される図とは若干異なり、理解しやすさ重視でより簡単な図にしています。

関数実装に関して、二つの実装方法があると考えると分かりやすいです。

一つは**エンジニアが自分達の手で実装**
一つは**訓練データで実装**

訓練データによる実装は「CSVを挿入すれば自動的にアウトプットの値が正しい値に近付く」というイメージです（反感を買いそうな表現ではありますが）

## 具体例で理解する

|x1|x2|x3|AND|
|:---:|:---:|:---:|:---:|
|0|0|0|0|
|0|0|1|0|
|0|1|0|0|
|0|1|1|0|
|1|0|0|0|
|1|0|1|0|
|1|1|0|0|
|1|1|1|1|

AND関数というものを実装したいとします。
引数としてx1, x2, x3をとり、上記の真理値表の通り入力が全て`1`である場合にのみ`1`（真）を出力するというものです。

以下のように手元に用意しておいた正解ラベル付き訓練データ`training_data.csv`で学習させます。
（※ 以下の`y`の列が正解ラベル）

![スクリーンショット 2020-12-01 8.32.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/48d7a998-d983-91e4-2ef2-23dd759bee43.png)

学習が進むと、内部で自動的に**機械が持つパラメーター（後述）**が正しい値にチューニングされ、ANDモデルが構築されます。こうして出来上がった仮設関数`h(x)`を、後は以下のように値を入れて使うだけです。

```python
result = h(1, 0, 1)
print(result) # <--- 0　（正解）

result = h(1, 1, 1)
print(result) # <--- 1　（正解）

result = h(0, 1, 1)
print(result) # <--- 1　（不正解）
```

# 理論
## 概要
機械学習の数式を理解します。これを理解すれば機械学習がどのように処理しているのかが分かるようになり、機械学習というものをよりイメージしやすくなります。

## 知識的準備
### シグマ
```math
n = 繰り返す回数 \\
i = スタートの値 \\

\sum_{i=1}^n\bigl(i + 2\bigl) \\
```

機械学習では上記のような数式が登場します（シグマと言います）。**はじめは拒否反応を起こす**かもしれませんが、実際には、開発経験者かつ足し算引き算などの**四則演算が分かっていればそんなに難しくはない**です。なぜならforループと考え方がほぼ同じだからです（機械学習の数式についても一つ一つ紐解くので安心してください）

```math
n = 3 である時 \\
\sum_{i=1}^n\bigl(i + 2\bigl) = (1 + 2) + (2 + 2) + (3 + 2) = 12
```

シグマではイテレーション毎に出た値を**足し**ます。
念のため伝えると、下記のように**掛け算で繋げる**ものもあり、π（パイ）の大文字が記号として使われます。

```math
n = 3 である時 \\
\prod_{i=1}^n\bigl(i + 2\bigl) = (1 + 2) \times (2 + 2) \times (3 + 2) = 60
```

これで準備は以上です。

## 機械学習の数式・用語
機械学習では、以下の数式が登場します。
後で一つ一つ分解して説明するので、今は軽く眺める程度で大丈夫です。

### 重み（Weight）
```math
\theta \\
(書籍によってはWで表される）
```

### 損失関数(Loss Function)
```math
J(\theta)
```

### 平均二乗誤差(Mean Squared Error)
```math
J(\theta_j) = \frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```

```math
\frac{d}{d \theta_j} J(\theta_j) = \frac{1}{n} \sum_{i = 1}^{n} \Bigl( h(x^{(i)}) - y^{(i)} \Bigl)X^{(i)}_j
```

### 仮設関数(Hypothesis Function)
```math
h(x) = \theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2 + \cdots + \theta_j x_j = 予測した値
```

### 最急降下法 (勾配降下法, Gradient Descent)
```math
\theta_j = \theta_j - a \frac{d}{d \theta_j}J(\theta_j)
```

## 機械学習アルゴリズムを理解する
例として「家の大きさで価格を予測するモデル」で流れを説明しようと思いますが、
その前にグラフについて説明します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/5eb90955-ec35-77b1-8447-325711a60d05.png)

### グラフの説明
左図は`J(θ)`と`θ`の関係性を表し、右図は実際のデータ、`x(size)`と`y(price)`の関係性を表しています。

#### x: size
この例では家の大きさを表します。

#### y: price
家の値段を表します。

#### θ： シータ
`θ`は「シータ」と読み、学習をするたびに予測するに最適な値へと自動的に変化します。**機械が持つ変数**だと考えると分かりやすいかもしれません。機械学習において非常に重要なパラメーターです。

#### J(θ): 損失関数
`J（θ）`は**損失関数**（または**誤差関数**、**目的関数**でも良い）と言い、`θ`を`J`関数のインプットといて入れた場合「実際のデータとどのくらい誤差があったか」を示し、この値が低ければ低い程誤差が少なくて良いということになります。

#### h(x): 仮設関数
`h(x)`は**仮設関数**と呼ばれるもので、前述の通り、学習後はこれを用いて予測をすることになります。

### 学習の目的
学習が進むと`J(θ)`上にある点が段々と下がっていき、最終的には一番下まで行き、収束します。その時点で`h(x)`の「予測線」も`θ`の変化の影響を受けて実際のデータに近い位置まで移動しています。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/e3aa3bd1-bf51-4814-bf7d-b6a1d923b31b.png)

この「**`J(θ)`の軌道上にある点を一番下まで移動させ、モデルの予測精度を上げること**」が機械学習の目的になります。

### 数式の説明
それでは、前述した数式について分解して説明します。

```math
\begin{align}
J(\theta_j) = \frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2　・・・・・・・・・・・・ 1 \\

\newline
\newline

\frac{d}{d \theta_j} J(\theta_j) = \frac{1}{n} \sum_{i = 1}^{n} \Bigl( h(x^{(i)}) - y^{(i)} 
 \Bigl)X^{(i)}_j ・・・・・・・・・・・ 2 \\

\newline
\newline

h(x) = \theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2 + \cdots + \theta_j x_j ・・・・・・・・・・ 3  \\

\newline
\newline

\theta_j = \theta_j - \frac{d}{d \theta_j}J(\theta_j) ・・・・・・・・・・・・・・・・・・・ 4 \\
\end{align}
```

#### 平均二乗誤差
数式と名前は物々しいですが、やっていることは単純です。

```math
J(\theta_j) = \frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```

この式を軽く説明すると「予測した家の値段（`h(x)`）と実際の家の値段（`y`）の差を二乗してその誤差を平均する」となります。

これを分解して見ましょう。

```math
h(x) - y
```

x（家の大きさ）をh関数のインプットとして入力し、出力として予測した値（家の値段）が返って来ます。
予測した値と実際のデータと**どのくらい誤差があったか**を計算しています。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/dc990a05-9d9d-6fd6-371e-0b0f9bfbd4f0.png)


```math
\sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)
```

シグマは前述した通り「forループ」のようなもので、**算出した誤差を合算**しています。
（上図の赤線が予測線との誤差）

```math
\sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```

誤差の合計に二乗している理由は、誤差を合算させた結果を「**マイナスの値にしないため**」です。
二乗すれば例え誤差合計が`-3`だとしても`(-3) * (-3) = 9`となり、マイナスの値にはなりません。

```math
\frac{1}{n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```

N（データの数）で割り、**平均を算出**しています。

```math
\frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```
1/2nとなっている理由は、「**2を付けておけば微分をした際に打ち消されて綺麗な数式になるから**」です。
（実際に使用する式はこれに微分した式であるため）


```math
J(\theta) = \frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2
```

これが**平均二乗誤差**です。

```math
\begin{align}
\newline
\frac{d}{d \theta} ・・・・・・・・・・・・・・・・・・ 1 \\
\frac{d}{d \theta} J(\theta)　・・・・・・・・・・・・・・・ 2
\newline
\newline
\end{align}
```

2は「**J(θ)をθで微分する**」と読みます。
1は難しそうな記号に見えますが、機械学習のために微分を覚える必要はなく「微分をすると**接線の傾き**を求められるんだな」程度の理解で問題ありません。（傾きを求めるのは「θを更新する際の方向」が分かるため非常に重要です）


![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/6f43add5-75ad-58d5-9456-996f24d27f3d.png)


```math
J(\theta) = \frac{1}{2n} \sum_{i=1}^{n} \Bigl(h(x^{(i)}) - y^{(i)} \Bigl)^2 ・・・・・・ before\\


\frac{d}{d \theta} J(\theta) = \frac{1}{n} \sum_{i = 1}^{n} \Bigl( h(x^{(i)}) - y^{(i)} 
 \Bigl)X^{(i)}　・・・ after
```

`J(θ)`を`θ`で微分すると上のような式になります。

#### 仮設関数
仮設関数について説明します。

```math
\begin{align}
\newline
h(x) = \theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2 + \cdots + \theta_j x_j
\newline
\newline
\end{align}
```

「家の大きさ」などの特徴が`j`個ある場合はこのような

```math
\begin{align}
\newline
h(x) = \theta_0 + \theta_1 x_1
\newline
\newline
\end{align}
```

今回、特徴の数が「家の大きさ」の一つだけなので、`y = ab + c`と同じような式になります（`Xo`は式を同じような形にする目的で付けられており、実際に値は`Xo = 1`となっており影響を与えないので分かりやすさのため一旦記述していません）

`ab`で「どのくらい線を傾けるか」が決まり、`c`で「どのくらい線を高くするか」が決まります。
何故`c`を入れる必要があるかは以下の図で示します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/814fa703-6eff-22bd-a6e4-f607ce2b85d4.png)

もし、入力として特徴を三つ入れる場合、仮設関数は以下のようになります。

```math
\begin{align}
\newline
h(x) = \theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2 + \theta_3 x_3 \\
\newline
x_1 ・・・・・・・・・・ 家の大きさ \\
x_2 ・・・・・・・・・・ ベッドの数 \\
x_3 ・・・・・・・・・・・・ 築年数 \\
\newline

\end{align}
```

`x`は実際のデータです。
`θ`は自動で調節される値で、今回に関して言えば初期値は`0`でも良い。

#### 最急降下法
`θ`の更新方法です。

```math
\theta_j = \theta_j - a \frac{d}{d \theta_j}J(\theta_j)
```

一見複雑そうに見える式ですが、一旦分かりやすさのためより簡単な式に直してみます。

```math
\theta = \theta - \frac{d}{d \theta}J(\theta)
```

学習率`a`と特徴の添字`j`を式から省きました。

```math
\frac{d}{d \theta}J(\theta)
```

前述で説明したことを思い出してみてください。
これは`J(θ)`をθで微分するという意味で、「**接線の傾き**」を求めるための式です。
接線の傾きを何故求めているかというと、「傾き」がわかれば

「**θを`+`に更新するべきか、`-`に更新するべきか方向が分かる**」からです。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/1dc3f51a-c5cb-dd20-9d97-941d4f75fbcf.png)

左図は`θ = 7`だった場合で、微分をすると`+`の値が返ってきます。
右図は`θ = 2`だった場合で、微分をすると`-`の値が返ってきます。

つまり、`θ = 6`だった場合、

```math
\frac{d}{d \theta}J(\theta) = (+)の値
```

このようになり、

```math
\theta = \theta - \frac{d}{d \theta}J(\theta)
```

これに（＋）の値を代入すると

```math
\theta = \theta -(+) \\
\theta = \theta * (ー)の値
```

となります。
つまり、「**マイナス方向にθの値が更新**」されることになります

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/e98cf389-542d-3436-ed57-e354e236a552.png)

逆もまた然り、今度は`θ = 2`の場合

```math
\frac{d}{d \theta}J(\theta) = (-)の値
```

このようになり、

```math
\theta = \theta - \frac{d}{d \theta}J(\theta)
```

これに（ー）の値を代入すると

```math
\theta = \theta -(-) \\
\theta = \theta * (＋)の値
```

となります。
つまり、「**プラスの方向にθの値が更新**」されることになります。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/c6db3757-bc01-11ac-775f-9f9323452cf8.png)

これを、**収束**するまで学習を繰り返します。

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176882/5b6fa11c-267c-7336-9821-58f17388fc92.png)

これで「家の値段を家の大きさで予測するモデル」が完成しました。
長くなってしまったため、ここまでとします（本当はSigmoid Functionあたりの話もするつもりでしたが）

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

# GitHub
## Perceptron
- Octaveで自作したコードで、機械学習学び始めの時に書いたコードなのであまり綺麗とは言えませんが参考までに。
    - https://github.com/RyoAtsuta/Perceptron

## NeuralNetwork
- こちらは個人的には自信作なので是非見てやって下さい m（_ _）m
    - https://github.com/RyoAtsuta/NeuralNetwork

# 次回
次回はニューラルネットワークについて説明します。