0から始める機械学習メモ

# 機械学習メモ

- 初心者向け本・講義などを受講した結果重要な内容をメモ
- （序盤）開発者目線、企画者目線など関係なく、機械学習に携わる人は知っておくべき汎用的な内容を記載
    - 知っておくべき単語
    - 最低限必要な数学知識
    - 一般的な機械学習導入における開発フロー
- （中盤）数学的な知識も踏まえつつ、簡単なモデル作成の例を記載
    - 「単回帰分析」から、モデル作成のフローを具体的に学ぶ
    - 「重回帰分析」のようにパラメータが増えても、フローは「単回帰分析」と変わらないことを学ぶ
- （後半）機械学習エンジニア向けのプログラミング寄りの話を記載
    - 機械学習で利用される基本的なPythonライブラリ紹介
    - データの前処理手順を紹介
    - ディープラーニング用フレームワークChainerの紹介

## 知っておくべき単語

### ●機械学習でよく聞くワードの位置づけ

ディープラーニングは機械学習の一部であり、機械学習は人工知能（AI）の一部である。

```math
人工知能（AI）\supset　機械学習　\supset　ディープラーニング、ニューラルネットワーク
```

#### 人工知能（AI）

人間で言う「目、口、耳」から入る情報を、「画像、自然言語、音声」としてインプット、数値化し、何らかの判断をくだす一連のフローを指す。

#### 機械学習

「画像、自然言語、音声」から、判断をくだすために利用するアルゴリズムの総称を指す。

#### ディープラーニング

「画像、自然言語、音声」から、判断をくだすために利用するアルゴリズムの一部を指す。その他のアルゴリズムとしては、SVM（サポートベクターマシン）、単回帰分析、重回帰分析などがある。

ディープラーニングの一種として、CNN（畳み込みニューラルネットワーク）などが存在する。

#### ニューラルネットワーク
入力値から出力値を一発で計算されるわけではなく、複数の層で相互に計算を行うことで出力値が計算される。まず、入力データに対する予想を出力し、その予想と正解の誤差(loss)を算出、lossを逆伝播することによりモデルの更新を行うことでモデルのパラメータをチューニングしていく。回帰や分類といったモチベーションに対する解決手法となりうる。

### ●ニューラルネットワークにおける専門用語
#### 順伝播

入力値と出力値からモデルを作成する際に、入力値を元に徐々に出力値が求まっていく流れ

#### 逆伝播

入力値と出力値からモデルを作成する際に、各入力値の重みを決定するために、出力値から入力値に遡りながら計算していく流れ

#### 線形変換

ニューラルネットワークにおける最初の層での変換を「線形変換」と呼ぶ

#### 非線形変換

線形変換によって変換された線形関数をを、再度線形ではない状態に変換する

#### 活性化関数

伝達関数とも呼ぶ。線形変換によって得られた線形関数を、再度非線形変換を行う際に利用する関数。例としてReLU（ランプ関数）等がある

0以下である場合は0を、そうでない場合はそのままの値を返却

```math
\varphi(x) = x_+ = \max(0, x)
```

#### 損失関数

ラベルと出力の誤差を計算する関数。それぞれのモデルの手法によって、損失関数は異なる。

##### 回帰
平均二条誤差で表される。

※Nは教師データの数とする

```math
L = \frac{1}{N}\sum_{n=1}^{N}(y_{n}-t_{n})^{2}
```

##### 分類

クロスエントロピーという手法を用いるが割愛

### ●機械学習の精度に関する単語

#### Accuracy

Accuracyは正解率の意味。分類問題の時に登場し、回帰では登場しない。Accuracyには以下の3種類がある。

- training accuracy
- validation accuracy
- test accuracy

Accuracyを説明するために、ニューラルネットワークのより詳細な流れを説明する。

1. 訓練データに対してaccuracyとlossを計算する
    - この時のaccuracyを**training accuracy**と呼ぶ
2. lossを使ってモデルを更新する
3. 訓練データ数分1と2を繰り返す
4. 学習されたモデルに対して検証データで検証す る(この際出たlossでモデルの更新は行わない)
    - この時のaccuracyを**validation accuracy**と呼ぶ
5. 上記の1～4の流れを1epochと呼び、何度も繰り返す
6. 最後にできたモデルに対してテストデータで最終的な性能を測る
    - この時のaccuracyを**test accuracy**と呼ぶ

#### BatchNormalization
Accuracyの精度が低い場合、精度向上のために、BatchNormalizationという手法がある。以下の数式で各要素が標準化されることで、パラメータごとの結果に与える影響度の差を小さくすることができる。

```math

\frac{x-\bar{x}}{\rho}

```
入力変数Xごとに処理をかけるため、引数は入力変数Xの数となる。

#### 誤差

入力値Xに対して、以下の差分を誤差と呼ぶ

 - 実測値Y
 - モデルに入力値Xを与えた時の、出力値

### ●画像処理におけるワード

#### コンボリューション（convolution）
CNN（畳み込みニューラルネットワーク）の語源となった、Cの部分。カーネルと呼ばれる行列のフィルターに対し、元画像を通すことで、元画像の各画素に対して処理する。特徴量が際立つように、不要な特徴量ピクセルを削ることが多い。

#### カーネル（kernel）
LinuxのOSの核となる部分の話ではなく、画像処理においてはコンボリューションを行う際のフィルタを指す。フィルタのサイズは、ksizeで表す。

#### プーリング（pooling）
ピクセル数を減らし、画像の縮小を行う。手法としてはいくつかあり、例としてMax_poolingがある。Max_poolingは、たとえば2×2のピクセル範囲の中で、一番特徴量が大きい値のピクセル以外を削除する手法。

#### ストライド（stride）
フィルタを適応する際に、何ピクセルごとに適応するかを指定。

#### パッド（pad)
画像の外周に0埋めする範囲。ここを埋めておかないと、適応していく中で隅にカーネルをかけることができない。


### ●より専門的な用語

#### Optimizer
パラメータを更新する最後の段階で実行される、**最適化アルゴリズム**のことを指す。SGDやAdam等さまざまな最適化手法がある。

[[最新論文] 新しい最適化手法誕生! AdaBound & AMSBound](https://qiita.com/Phoeboooo/items/f610affdcaaae0a28f34)

#### Iterator
ニューラルネットワーク上のパラメータを、1回更新することをIteratorと呼ぶ。訓練データいくつ読み込んだらIteratorを回すかを決めるために、バッチサイズという概念が存在する。

```math
Iterator = \frac{訓練データ}{バッチサイズ}
```

例）訓練データが10万件で、バッチサイズを100と決めた場合、1,000回のパラメータ更新が行われる。

#### epoch
バッチをすべて実行しきった状態を、1epochと呼ぶ。要するに、**1つの訓練データを利用して何回学習させるか**がepoch数となる。

#### Updater
パラメータを更新する際のさまざまな情報をUpdaterで定義する。これまでに定義してきたOptimizer、Iterator、そして実際に利用する**処理装置**を指定する。

Chainer（後述）では以下のように記載する

```python
from chainer import training

# 事前に定義しておいたiterator
# 事前に定義しておいたoptimizer
# 利用する処理装置（-1はCPU、0はGPU）
updater = training.StandardUpdater(train_iter, optimizer, device=-1)
```

#### Trainer, extensions

訓練の手法を指定する。利用するupdaterや、epoch数、出力フォルダなどを指定する。

Chainer（後述）では以下のように記載する

```python
from chainer.training import extensions

epoch = 50

# 事前に定義しておいたupdater
# 事前に定義しておいたepoch数
# アウトプットフォルダ
trainer = training.Trainer(updater, (epoch, 'epoch'), out='result/wine')
```

### ●機械学習の３大トピック

#### 教師あり学習

入力値X及び出力値Y（答え）が存在する
ビジネス課題については主に「入力値、出力値」の関係性を分析するため、教師あり学習が使われることが多い

- 回帰
    - 数値（連続値）を予測する
    - 例として、「X：駅からの距離と、Y：車の通行量」、「X：一日のうち晴れた時間と、Y：とあるコンビニの来店客数」等
- 分類
    - カテゴリを予測する
    - 例として、「X：アルコール度数から、Y：白ワインであるか、Y：赤ワインであるか」、「X：色合いから、Y：吉野家の牛丼であるか、Y：松屋の牛丼であるか」

#### 教師なし学習

入力値のみしか存在せず、出力値（答え）は存在しない
マーケティングのように、不明瞭な母体を対象とした分析によく使われる

- クラスタリング
    - 教師あり学習の「分類」とは異なり、あらかじめ決められたクラスではなく、今回のデータパターンについてのみ考える
    - 教師あり学習のようなYがなく、「いくつにクラスタを分けるか」のような指定ができる
- 次元削減

#### 強化学習

データがない、データがほとんどない場合に利用する

### ●内挿・外挿

機械学習においては、内挿を求めることはできるが、外挿を求められるかの保証は無い

#### 内挿
入力・出力のデータセットに対して、最大値と最小値の間を取りえる値の予測をする。

#### 外挿
入力・出力のデータセットに対してデータ範囲の外の値を予測する。




## 最低限必要な数学知識

- 微分積分
    - 曲線に対する、接線の傾きを求めるために利用
    - 全データセットに対する誤差の合計値が曲線で表される場合、その曲線の最小値（接線の傾き = 0）を求めることで理想的なモデルに対するパラメータを算出できる
- 線形台数
    - スカラー
        - ただ一つの変数
    - ベクトル
        - スカラーの集合。１つのスカラーの大きさが距離を表し、スカラーの数が方向を表す
        - 転置ベクトル × 変数ベクトルを微分すると、転置前のベクトルとなる
    - 行列
        - ベクトルの集合
        - 行列A × 行列Aの逆行列 = I
    - ベクトル × 転置ベクトル ＝ スカラー
    - 行列 × ベクトル ＝ ベクトル
    - 転置ベクトル × 行列 × ベクトル ＝ スカラー

**覚えたほうがいい公式**

定数の微分は0ベクトル

```math
\frac{d}{d\vec{x}}{C} = \vec{0}
```

転置ベクトルと変数ベクトルの積の微分は転置前のベクトル

```math
\frac{d}{d\vec{x}}(\vec{b}^Tx) = \vec{b}

```

転置ベクトル、定数、転置前ベクトルの積の公式

```math
\frac{d}{d\vec{x}}(\vec{x}^TA\vec{x}) = (A+A^T)\vec{x}

```

- 確率統計


## 一般的な機械学習導入における開発フロー

1. 企画、ヒアリング
     - 業務を知っている人からヒアリングを行い、方針を決める
2. 環境構築
     - モデルを作成できるようなGPUを搭載した環境（パブリッククラウドが多い）を構築
3. 教師データ作成
     - 教師あり学習の場合、入力値・出力値のセットを教師データとして用意する
4. 前処理構造化
     - 非構造化データ（言語、画像のような）について、構造化データ（数値、ベクトル表現）に変換する
5. モデル構築（機械学習）
     - 用意した教師データから、モデルを作成する
     - 失敗する場合の原因は教師データかモデルのアルゴリズムにある
6. 仮運用、検証
7. システム統合
     - 検証の終わったモデルを実際のアプリケーションに組み込む


## 「単回帰分析」から、モデル作成のフローを具体的に学ぶ

問題設定　：　**「部屋の広さから、家賃を予測しよう」**

### Step1　学習させる

「学習させる」　＝　「モデルを作成する」と同義

インプット（入力変数）とアウトプット（出力変数）は何かを決定する。今回の例では、**入力変数＝部屋の広さ**、**出力変数＝家賃**となる。
学習用のデータとして、「（部屋の広さ：家賃）　＝　（２０：５）、（３０：７）、（４０：１０）」のようなデータを用意することになる。

### Step2 推論

インプットである**部屋の広さ**を、学習済みのモデルに投入することで、家賃が予測される。


## 単回帰分析のモデル作成の手順

### Step1 モデルの形を「人間が」決める

ここで言うモデルの形というのは、数式のことである。
たとえば、「このモデルは直線でいけそうだ！」と判断した場合は、以下の数式を「モデルの形」と定義する。

**x,yのようなグラフ上軸となる値を変数と呼び、グラフの形を決定付けるa,bをパラメータと呼ぶ**

```math
\hat{y} = ax + b
```

つまりモデルを作成するとは、直線を例にすると、「x,yのデータに基づいて、適切なa,bのパラメータを決定する」と同義である。

#### テクニック

- x,yのデータ群について、中心化（センタリング）を行う
    - パラメータの一部である切片bを求める必要が無くなる
    - 中心化の方法としては、x,yのプロットされた値から、それぞれの平均値を引くことでプロットしなおす

データが中心化済みの場合、モデルの形は以下で表すことが可能となる

```math
\hat{y} = ax
```

### Step2 評価関数を決める

評価関数とは、損失関数と同義。モデルから算出した値と、実際のプロット値の差分について、2乗誤差を求めた式。Lで表す。

モデルとして利用したい関数が、今回はセンタリング済みの単回帰直線であるため以下となる。

```math
\hat{y} = ax
```

実際にプロットされる値と、モデルから算出された値の2乗誤差は以下のように表される

```math
L = \sum_{n=1}^{N}({y}_{n} - \hat{y}_{n})^{2}
```

### Step3 評価関数を最小化する

**評価関数を最小化する目的は、評価関数が最小となるようなパラメータ値（今回の場合はa）を算出することで、モデルを変数のみ（x,yのみ）で表すこと**

```math
L = \sum_{n=1}^{N}({y}_{n} - \hat{y}_{n})^{2}
```

```math
\hat{y}_{n}はモデルから計算した予測値、y_{n}は実際のプロット値
```

評価関数であるLを最小化すると以下のようになる。

①予測値を代入する

```math
L = \sum_{n=1}^{N}({y}_{n} - ax_{n})^{2}

```

②展開する

```math
L = \sum_{n=1}^{N}y_{n}^{2}-2(\sum_{n=1}^{N}x_{n}y_{n})a + (\sum_{n=1}^{N}x_{n}^{2})a^{2}

```

③最小化 = 評価関数の最小値を求めることなので、微分した値が0になる（傾きが0になる）点を探す

```math
L'(a) = \frac{d}{da}(\sum_{n=1}^{N}y_{n}^{2}-2(\sum_{n=1}^{N}x_{n}y_{n})a + (\sum_{n=1}^{N}x_{n}^{2})a^{2}) = 0

```

④3の数式をaについて解く

```math
a = \frac{\sum_{n=1}^{N}x_{n}y_{n}}{\sum_{n=1}^{N}x_{n}^{2}}
```

つまり、単回帰直線のモデルは、

```math
y = \frac{\sum_{n=1}^{N}x_{n}y_{n}}{\sum_{n=1}^{N}x_{n}^{2}}x
```

で表記され、入力値と出力値のデータセットを利用することでパラメータaが置き換わり、モデルが作成できることが分かる。

**評価関数が最小になるポイントを探したら、既知のプロット用データのみで傾きが求まることが判明！！**


## 「重回帰分析」のようにパラメータが増えても、フローは「単回帰分析」と変わらないことを学ぶ

単回帰分析と異なる点としては、インプット（入力変数）の数である。重回帰分析では、インプットとなる値は1つの変数ではなく、複数の変数となる。まずはインプット（入力変数）とアウトプット（出力変数）は何かを決定する。今回の例では、**入力変数（x1～xn）＝部屋の広さ、距離、治安・・・**、**出力変数（y）＝家賃**とする。

## 重回帰分析のモデル作成の手順

### Step1 モデルの形を「人間が」決める

各入力値（部屋の広さ、距離、治安、・・・）に対して、それぞれ重み付けのwを掛け合わせて以下のようなモデルが考えられる。

```math
\hat{y} = w_{0}x_{0} + w_{1}x_{1} + w_{2}x_{2}
 + ・・・ + w_{m}x_{m}
```

書き換えると、ベクトル表記をすることも可能
**※要素数の等しい転置ベクトルと通常のベクトルの掛け算なので、結果は上記のスカラと同様になる**

```math
\hat{y} = \vec{w}\vec{x}^T
```

### Step2 評価関数を決める

（センタリング済みデータの）単回帰分析におけるパラメータがaのみだったのに対して、重回帰分析のモデルにおけるパラメータはwがM個ある。

単回帰分析時点の評価関数と同様に、以下で表すことができる。

```math
L = \sum_{n=1}^{N}({y}_{n} - \hat{y}_{n})^{2}
```

再度シグマを展開すると、以下のようになる。

```math
L = ({y}_{1} - \hat{y}_{1})^{2} + ({y}_{2} - \hat{y}_{2})^{2} + ・・・ + ({y}_{n} - \hat{y}_{n})^{2}
```

こちらをベクトル表記で表すと以下となる。

```math
L = \begin{bmatrix}
{y}_{1} - \hat{y}_{1} & 
{y}_{2} - \hat{y}_{2} & 
・・・ & 
{y}_{n} - \hat{y}_{n}
\end{bmatrix}
\begin{bmatrix}
{y}_{1} - \hat{y}_{1} \\
 {y}_{2} - \hat{y}_{2} \\
 ・・・\\
 {y}_{n} - \hat{y}_{n}
\end{bmatrix}
= (\vec{y} - \hat{\vec{y}})^T(\vec{y} - \hat{\vec{y}})
```

### Step3 評価関数を最小化する

中間の数式は省き、Lの数式を微分しやすいように変形する
Xは、行方向がデータ件数、列方向が入力変数の数（広さ、距離、治安など）の行列となる。

```math
L = (\vec{y} - \hat{\vec{y}})^T(\vec{y} - \hat{\vec{y}}) = \vec{y}^T\vec{y} - 2\vec{y}^TX\vec{w}+\vec{w}^TX^TX\vec{w}
```

上記Lをベクトル微分し、Lが最小となる場合のwを算出すると、以下の結果が得られる

```math

\frac{d}{d\vec{w}}L = -2X^T\vec{y} + 2X^TX\vec{w} = \vec{0}

```
wについて解くと、

```math
\vec{w} = (X^TX)^{-1}X^T\vec{y}

```

参考として、上記の式をベクトル微分するというのは、以下のようにwのそれぞれの値について偏微分することと同義である。

```math

\begin{bmatrix}
\frac{d}{dw_{0}}(L) \\
\frac{d}{dw_{1}}(L)  \\
 ・・・\\
\frac{d}{dw_{m}}(L) 
\end{bmatrix}
= 
\begin{bmatrix}
0 \\
 0 \\
 0 \\
 0
\end{bmatrix}
```

つまり今回のモデルは、以下で表現できる。

```math
\hat{y} = ((X^TX)^{-1}X^T\vec{y})\vec{x}^T
```

## 機械学習で利用される基本的なPythonライブラリ紹介

- numpy
    - 数学計算用ライブラリ
    - 通常のリストをnumpy用arrayに格納することで、行列計算やベクトル計算が可能となる

```python
import numpy as np


# リストをnumpyで利用できるように定義、2つ目の引数は型の指定
np.array([[1, 2, 3]], 'f')

# ランダム要素が発生する場合に、固定値にする
np.random.seed(3)

```


- pandas
    - データ操作系ライブラリ
    - 各データをデータフレームという独自オブジェクトに変換することで、さまざまな操作が行える

```python
import pandas as pd

# CSVの読み込み
df = pd.read(`xxx.csv`)

# 読み込んだデータの上から3列を抽出
print(df.head(3))
# Class Kind Min Max
# 0	1	14.23	2.43	15.6
# 1	1	13.20	2.14	11.2
# 2	1	13.16	2.67	18.6

# 行、列を指定して抽出
# 全行、1列目のみ
print(df.iloc[:, 0].head(1))
# Class
# 0	1

# 全行、2列目以降すべて
print(df.iloc[:, 1:].head(1))
# Kind Min Max
# 0　14.23	2.43	15.6

# 2行目以降すべて、全列
print(df.iloc[1:, :].head(1))
# Class Kind Min Max
# 1	1	13.20	2.14	11.2

```
- OpenCV
    - 画像操作系ライブラリ
    - 読み込んだ瞬間にnumpyの形式になっている
    - 色合いが元画像と異なっており、BGRが逆になっている

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import cv2

img = cv2.imread('data/sample.png')
print(type(img))
# numpy.ndarray

```

- Pillow
    - 画像操作系ライブラリ
    - 色合いが元画像と同じ
    - 読み込んだ瞬間は画像形式であり、機械学習のためにはnumpyに変換する必要がある

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from PIL import Image

img = Image.open('data/sample.png')
print(type(img))
# PIL.PngImagePlugin.PngImageFile

```

## データの前処理手順を紹介

モデルを作成する際には、データを用意する必要がある。用意しなければいけないデータは以下のとおり。

- 訓練用データにおける説明変数X
- 訓練用データにおける目的変数T
- 訓練後モデルを評価する際の説明変数X
- 訓練後モデルを評価する際の目的変数T

```python
import numpy as np
import pandas as pd
import chainer

# あるデータの読み込み
df = pd.read_csv("XXX.csv")

# 教師データとテストデータの分割
# データの一番右が目的変数Tの場合、以下のように分割
# xは一番右を除いたすべての列、tは一番右の列
# またこのタイミングでデータの型をdfからfloat32のnumpyリストに変換
x = df.iloc[:, :-1].values.astype('f')
t = df.iloc[:, -1].values.astype('f')

# x, tを統合したリストを作成
dataset = list(zip(x, t))

# 訓練用データ70パーセント、テスト用データ30パーセントに分割
# chainer（後述）の中にある、ランダム分割用のメソッドを利用
train_data, test_data = chainer.datasets.split_dataset_random(dataset, int( len(dataset) * 0.7), seed=0)

# train_data = 訓練用データの説明変数X、目的変数Tのリスト
# test_data = テスト用データの説明変数X、目的変数Tのリスト
```


## ディープラーニング用フレームワークChainerの紹介

### Chainerとは？

Chainerは、Preferred Networks社が開発を進めている日本製のフレームワークです。 機械学習を行うための機能が提供され、Pythonで使用することができる。内部ではnumpyというPythonライブラリが動作しており、さらに内部ではC言語で記載されている。

### 何ができるの？
- ある層からある層へのネットワークを構築し、組み合わせることでニューラルネットワークを構築することができる
- ニューラルネットワークを構築して解決できる問題として、主に**回帰**と**分類**を解くことができる
- CNN（畳み込みニューラルネットワーク）を構築することができる

#### 回帰について解くモデル

```python

# 3層のニューラルネットワークを構築する
# chainerの中にあるChainクラスを継承する
class NN(chainer.Chain):

    # インスタンス化する際に、
    # n_mid_unitsで2層目ノード数を指定
    # n_outの最終層は回帰の場合は1つ
    def __init__(self, n_mid_units=10, n_out=1):
        # Chainクラスのコンストラクタ呼び出し
        super().__init__()
        # Chainクラスのwithコンテキストを呼び出す
        # with句のスタートエンドの間に定義されたLinkが
        # Optimizerの対象となるためここはおまじない
        with self.init_scope():

            # 1層目⇒2層目のネットワーク
            self.fc1 = L.Linear(None, n_mid_units)
            # 2層目⇒3層目のネットワーク
            self.fc2 = L.Linear(None, n_out)

    # 順伝播を定義
    # インスタンスが宣言された際に自動で実施される
    def __call__(self, x):
        h = self.fc1(x)
        h = F.relu(h)
        h = self.fc2(h)
        return h

# ここからモデルに値を渡し、学習させる
# 再現性のためにnpのrandomモジュールにて固定にする
np.random.seed(0)

# モデルのインスタンス化
nn = NN()

# 損失関数の別定義や可視化のためにモデルをオーバーラップ
# 損失関数lossfunとしては、平均2乗誤差を利用
model = L.Classifier(nn, lossfun=F.mean_squared_error)

# 回帰に明確な答えは無いので、正答率計算をオフにする
model.compute_accuracy = False

# optimizerの定義、セット
# 今回はアルゴリズムとしてSGDを採用
optimizer = chainer.optimizers.SGD()
optimizer.setup(model)

# iteratorの定義
# batchsizeは訓練データ数にもよるが、任意
batchsize = 10
train_iter = chainer.iterators.SerialIterator(train_data, batchsize)
test_iter  = chainer.iterators.SerialIterator(test_data,  batchsize, repeat=False, shuffle=False)





```