【ラビットチャレンジ（E資格）】機械学習

# はじめに
本記事はJDLA E資格の認定プログラム「ラビット・チャレンジ」における応用数学のレポート記事です。
本記事では以下の7つの項目について、要点をまとめています。

 - 線形回帰モデル
 - 非線形回帰モデル
 - ロジスティック回帰モデル
 - 主成分分析(PCA)
 - k近傍法(KNN)
 - k-平均法(k-means)
 - サポートベクトルマシン(SVM)

機械学習モデルってたくさんあり、「いざ分析！」となったときに何のモデル使ったら。。とオロオロしてしまいます。
以下のscikit-learnのアルゴリズムチートシートは、機械学習モデル選定の参考になるなと思いました。
![ml_map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/5d16bc59-9cea-43c6-c8e1-ac9254a7c415.png "scikit-learnアルゴリズムチートシート")
引用：[scikit-learn アルゴリズム・チートシート](https://scikit-learn.org/stable/tutorial/machine_learning_map/ "scikit-learnアルゴリズムチートシート")


# 1. 線形回帰モデル
線形回帰モデルは、教師あり学習であり回帰問題の手法の中で最も基本的なモデルです。
入力と出力の関係が1次式によっておおよそ表現可能な場合に有効なモデルであり、2乗損失と呼ばれる損失を最小化するような1次式を予測器として学習します。

 - 仮説：入力変数による1次式
$$f(x;w)=w_0+w_1x_1+\cdots+w_dx_d$$
 - 損失関数：2乗損失
$$L(w)=(y-f(x;w))^2$$
学習時には、各データポイントに対して2乗損失を計算し、その平均値（平均2乗損失）
$$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i-f(x_i;w))^2$$
が最も小さくなるような1次式の仮説 $f(x;w)=w_0+w_1x_1+\cdots+w_dx_d$ を予測器として求めます。

 - 線形回帰モデルの性質
 ①　入力と出力の関係がおよそ1次式といえない場合には不適切なモデルとなる。
 ②　外れ値の影響を強く受ける。
　　損失関数として2乗損失を用いるので、外れ値ではその値がとても大きくなってしまいます。
　　データを確認して、問題無いようであれば削除するのが良いでしょう。
 ③　多重共線性に注意する必要がある。
　　2つ以上の変数に高い相関があると、得られたデータに対する学習結果が不安定になってしまいます。
　　2つの変数の内1つを削除する、1つの変数に変換する（平均値をとるなど）の対策を取る必要があります。
　　また、正則化を加えた線形回帰モデルを使用するといった対策もあります。

## 線形回帰モデルのコード実装
sklearnのボストンデータセットを題材に、モデル実装をしてみました。
コード詳細はgithubをご覧ください。通常の線形回帰モデルのほかに、正則化を加えたモデルである、Ridge回帰やLasso回帰についてもコードの実装をしています。

[github：線形回帰モデルのコード実装](https://github.com/kimurazzz/rabbit/blob/main/regression.ipynb)

### 実装内容と結果
 - 特徴量を標準化（今回は全特徴量をモデルの学習に使用しました。）
 - データを7:3で学習用データと検証用データに分割（ホールドアウト法）
 - 線形回帰
   [MSE] Train : 22.468, Test : 21.682
[R^2] Train : 0.757, Test : 0.666

 - Ride回帰
   [MSE] Train : 22.678, Test : 22.002
[R^2] Train : 0.755, Test : 0.661

 - Lasso回帰
   [MSE] Train : 27.390, Test : 24.980
[R^2] Train : 0.704, Test : 0.616

「平均2乗誤差MSEは0に近く、決定係数$R^2$は1に近いほど良い」ということから、正則化を用いない基本の線形回帰が一番良い結果であることが分かります。
今回はハイパーパラメータのチューニングは特に行わなかったので、グリッドサーチやランダムサーチを行うことで、精度の改善ができるかもしれません。

# 2. 非線形回帰モデル
下図のように線形回帰モデルでは特徴を捉えられないような、複雑な非線形構造をもつデータに対しては非線形回帰モデルを使用します。こちらも教師あり学習です。
![非線形データ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/fd096f04-c0da-82ac-c9c8-0af5adccea3e.png "非線形構造を持つデータ例")
非線形な回帰曲線を引くためには、基底展開法という方法を使ってモデリングを行います。

 - 基底展開法
    - 回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線形結合を利用する。
    - 未知パラメータは、線形回帰モデルと同様に最二乗法や最尤法による推定する。  
    - よく使われる基底関数
        - 多項式関数
        - ガウス型基底関数
        - スプライン関数 / Bスプライン関数  
     
基底展開法を用いた非線形回帰モデルの式は以下のようになります。

```math
y_{i}=w_{0}+\sum^{m}_{i=1}{w_{j}\phi_{j}(x_{i})}+\epsilon_{i}
```
$\phi_{j}(x_{i})$が基底関数です。

## 非線形回帰モデルのコード実装
コード詳細はgithubをご覧ください。RBFカーネルを用いたKernelRidge回帰や、多項式関数を用いた非線形回帰についてコードの実装をしています。

[github：非線形回帰モデルのコード実装](https://github.com/kimurazzz/rabbit/blob/7309264d4906360fc791b4486261636ee6820110/nonlinear%20regression.ipynb)

### 実装内容と結果
以下のようなデータを作成
![非線形回帰用データ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/a98731e2-1533-bc59-0bfc-317ee8709a65.png "非線形構造を持つデータ")

 - 線形回帰  
線形回帰では非線形データについてうまく表現できていないことが分かります。
![線形回帰.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/6c81c3b2-a675-71d3-3d3a-8a5325561bd2.png "線形回帰")
 - KernelRide回帰  
一方で、カーネルリッジ回帰では非線形データについて良く表現できていることが分かります。  
![カーネルリッジ回帰.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/93f1ea91-5a01-036b-96e2-e85d0c6996ef.png "カーネルリッジ回帰")

# 3. ロジスティック回帰モデル
ロジスティック回帰は分類モデルにおいて最も基本的なモデルです。こちらも教師あり学習です。
線形な境界でおおよそ分離できるようなデータに対して有効なモデルで、交差エントロピー損失を最小化するような1次式を予測器として学習します。
線形回帰と同様に、外れ値と多重共線性には注意する必要があります。

 - 仮説：
    - 出力ラベル$y=1$の確率の推定モデル：
    $p(x;w)=\sigma(w_0+w_1x_1+\cdots+w_dx_d)$、$\sigma(z)$はsigmoid関数とする。
    - 確率の推定値が0.5より大きければ$f(x;w)=1$, 小さければ$f(x;w)=0$と予測する。

 - 損失関数：交差エントロピー損失

``` math
\begin{eqnarray*}
L(w)=\begin{cases}
-\log(p(x;w))\text{ if }y = 1\\
-\log(1-p(x;w))\text{ if }y=0
\end{cases}
\end{eqnarray*}
```
学習時には、各データポイントに対して交差エントロピー損失を計算し、その平均値が最も小さくなるような1次式を予測器として求めます。

ちなみに、sigmoid関数は以下の式で表せれる関数です。

``` math
\sigma(z)=\frac{1}{1+\exp(-z)}
```

 - 逆正則化ハイパーパラメータ  
線形回帰モデルと同様に、ロジスティック回帰にも正則化項による罰則を加えることができます。目的関数は、

``` math
J(w) = \frac{1}{n}\sum_{i=1}^{n} -\{y_i \log(p(x_i;w))+(1-y_i)\log(1-p(x_i;w))\}+\lambda||w||_2^2
```

となり、学習時にこの目的関数が最も小さくなるような1次式を予測器として求めます。
$\lambda$は正則化項を損失に比べてどれだけ重要視するか調整するハイパーパラメータで、学習時には適切な値を自分で決める必要があります。

なお、罰則は$||w||_1=|w_0|+|w_1|+\cdots+|w_d|$でもよく、$||w||_w^2$による正則化を$L^2$正則化、$||w||_1$による正則化を$L^1$正則化といいます。

## ロジスティック回帰モデルのコード実装
sklearnのIrisデータセットを題材に、モデル実装をしてみました。
コード詳細はgithubをご覧ください。

[github：ロジスティック回帰モデルのコード実装](https://github.com/kimurazzz/rabbit/blob/main/logistic_regression.ipynb)

### 実装内容と結果
 - 特徴量を標準化
 - データを7:3で学習用データと検証用データに分割（ホールドアウト法）
 - ロジスティック回帰で分類  
[F1スコア] 0.911

高い精度で分類できていることが分かります。F値は適合率と再現率の調和平均によって計算されます。高ければ高いほどRecallとPrecisionの値がともに高くなります。以下に混同行列の結果も示します。0は完璧に分類できていて、2,3は誤分類がみられることが分かります。
![ロジスティック回帰混同行列.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/fda64ff1-e74d-e4e5-f294-59cab428909c.png "混同行列")

# 4. 主成分分析
変数の個数が多く複雑なモデルは過学習に陥りやすいため、予測精度への寄与が少ない変数は削除するなどの工夫が必要です。
より少ない変数で元のデータの情報を表現できるような新しい変数を作り出すことを次元削減といい、主成分分析はその中の基本的な手法となります。

考え方としては、情報量を分散の大きさと捉え、線形変換後の変数の分散が最大となる射影軸を探索します。
例えば以下のようなデータがあるとき、分散の大きさが最大となるような射影軸は緑ラインでも黄色ラインでもなく、赤色ラインとなります。
それぞれのラインにデータプロットを射影したとき、赤色が一番分散が大きそう（データ情報を多く保持できそう）ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/6b99f3b5-a7bb-dcae-8351-19dfe82ade59.png "射影軸の探索")

では、分散が最大となる射影軸の求め方について説明します。
次のような学習データが与えられてるとき、
$$x_i=(x_{i1},x_{i2},\dots,x_{im})\in\mathbb{R}^m（m次元のベクトル）$$
平均ベクトル、データ行列、分散共分散行列、線形変換後のベクトルは以下のように求められます。

- 平均ベクトル：　$\bar{x}= \frac{1}{n}\sum_{i=1}^{n}x_i$

- データ行列：　$\bar{X}= (x_1-\bar{x},x_2-\bar{x},\dots,x_n-\bar{x})^T$

- 分散共分散行列：　$\sum=Var(\bar{X})=\frac{1}{n}\bar{X}^T\bar{X}$

- 線形変換後のベクトル：　$s_j=(s_{1j},\dots,s_{nj})^T=\bar{X}a_j$


また、線形変換後の分散は以下の式で表されます。
$$
Var(s_j)=\frac{1}{n}s_j^Ts_j=\frac{1}{n}(\bar{X}a_j)^T(\bar{X}a_j)=\frac{1}{n}a_j^T\bar{X}^T\bar{X}a_j=a_j^TVar(\bar{X})a_j
$$

そして

 - 目的関数：　$a_j^TVar(\bar{X})a_j$
 - 制約条件：　$a_j^Ta_j=1$

とする制約付きの最適化問題として、ラグランジュ関数を最大にする係数ベクトル（微分して0になる点）を探索することにより、分散が最大となる射影軸を求めます。

 - ラグランジュ関数：　$E(a_j)=a_j^TVar(\bar{X})a_j-\lambda(a_j^Ta_j-1)$

ラグランジュ関数を微分して最適解を求めると
$$
Var(\bar{X})a_j=\lambda a_j
$$
となり、元のデータの分散共分散行列の固有値と固有ベクトルを求めれば良いことがわかります。
主成分分析の特徴についてもまとめます。
　・主成分は元の次元分ある
　・各主成分は直交する
　・分散が大きいほうから第1,第2,第3,...と番号が振られている
　・分散が大きいほうからいくつかの主成分のみを採用することで次元削減をする
　・第k主成分の分散の全分散に対する割合を寄与率という

## 主成分分析のコード実装
sklearnの乳がん検査データセットを題材に、モデル実装をしてみました。
コード詳細はgithubをご覧ください。

[github：主成分分析のコード実装](https://github.com/kimurazzz/rabbit/blob/main/pca.ipynb)

### 実装内容と結果
 - 特徴量を標準化した後、主成分分析
 - 2次元に次元削減したときの合計寄与率は0.632

2次元に削減したときの散布図は以下のようになりました。正解ラベルで色分けしています。
logistic回帰のような線形に分解境界を決める予測器を用いることで、悪性と良性の分類を行えそう。という事が分かります。
![主成分分析結果.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/4de44917-7327-8669-60d0-18c959ed61f0.png "2次元に削減したプロット")

# 5. K近傍法
教師あり学習の分類問題を予測する機械学習手法です。
最近傍のデータをk個とってきて、それらが最も多く所属するクラスに識別していきます。
kを変化させると結果も変わります。
![k-nn.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/9eacf303-e919-25df-00a2-359d639c49c3.png "k近傍法のイメージ")
また、kを大きくするほど決定境界が滑らかになります。
![k-nn2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/ddccbdf7-44e3-49b4-10ea-80723cecaea4.png "2決定境界の変化")

## k近傍法のコード実装
コード詳細はgithubをご覧ください。
kの値による決定境界の変化を確認できます。
また、sklearnのライブラリーを使わない方法についても実装をしています。

[github：k近傍法のコード実装](https://github.com/kimurazzz/rabbit/blob/main/knn.ipynb)

# 6. K-平均法
似ているデータポイントをまとめて組にしていく教師なし学習をクラスタリングといいます。
クラスタリングの中でも、デンドログラムを作るものを階層クラスタリング、デンドログラムを作らないものを非階層クラスタリングといいます。
ちなみにデンドログラムは以下のようなものです。
![デンドログラム.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/32e10655-7b90-4d6b-b712-a5a67a12bc11.png "デンドログラム")

そして、k-平均法は非階層クラスタリングの一種であり、クラスタ数Kを事前に決めておき、データポイント全体をK個のクラスターに分割する手法です。
![k-平均法イメージ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/385e1833-29e3-6dbc-949a-db980b84673b.png "k-平均法のイメージ")

- k-平均法のアルゴリズム  
　①　各クラスター中心の初期値を設定する。  
　②　各データ点に対して、各クラスター中心との距離を計算し、最も距離が近いクラスターを割り当てる。  
　③　各クラスタの平均ベクトル（クラスターに割り当てられたデータ点の中心）を計算する。
　④　収束するまで②,③の処理を繰り返す。

中々、アルゴリズムのイメージが付きにくいと思います。
以下のサイトがとても分かりやすかったです。k-means法デモツールから、サンプル数とクラスタ数を設定しk-平均法のシミュレーションができます。上記4つのアルゴリズムを一つずつ追うことができます。

URL：https://www.albert2005.co.jp/knowledge/data_mining/cluster/non-hierarchical_clustering

## k-平均法のコード実装
sklearnのワインデータセットを題材に、モデル実装をしてみました。
コード詳細はgithubをご覧ください。

[github：k-平均法のコード実装](https://github.com/kimurazzz/rabbit/blob/main/k-means.ipynb)


### 実装内容と結果
 - 特徴量を標準化
 - 主成分分析をして寄与率の高い2変数に次元削減（もともと13カラム）
 - k-平均法でクラスタリング（正解ラベル数は3なのでk=3とした）

以下のような結果を得ることができました。
![k-平均法結果.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/a70bb9e0-1f7b-8693-2ea8-698e8f9e51ed.png "k-平均法によるクラスタリング結果")
妥当と思われる3つのクラスターに分類できていることが分かります。
また、このクラスターは、正解ラベルで色分けしたplotとともほぼ一致していました。
つまり精度高くクラスタリングできているということが言えます。

# 7. サポートベクトルマシン
サポートベクトルマシン（SVM）は、線形二値分類の手法です。カーネル法を用いることで非線形分類も可能です。同じ線形手法であるロジスティック回帰との違いは、データがうまく分離されていることを、マージンと呼ばれる量によって測っていることです。また、外れ値に影響されにくいという特徴もあります。

 - SVMの仮定
説明変数を$x_1, x_2$としたとき、SVMでは次のように問題を設定することで、正しく判別できていることを$y(w_1x_1 + w_2x_2 + b) \geqq 0$と表すことができます。  
    - 目的変数$y$は、1または-1とする（0または1、1または2などにはしない）。  
    - $f(x_1, x_2) = w_1x_1 + w_2x_2 + b$とおいて、$f(x_1, x_2)\geqq 0$のとき$y=1$，$f(x_1, x_2)<0$のとき$y=-1$と判別する。 

 - ハードマージンSVM
SVMではマージン最大化という基準で、訓練データを最もよく判別できるように$w_1, w_2, b$の値を求めます。
    - マージン :  分類境界を挟んで2つのクラスがどのくらい離れているか
    - サポートベクトル : マージンの境界上のデータポイント（分類境界に最も近いデータのこと）  
<img width="350" alt="SVMのイメージ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/f4418afc-f11c-8531-bef7-23df6e10adf2.png">

SVMはマージンを大きくすることで、訓練データへの当てはまりを防ぎ、未知データへの予測を良くしようとしています。まさに、正則化の考え方といえるでしょう。

 - ソフトマージンSVM  
ハードマージンを採用すると、線形分離不可能な訓練データについては解なしとなってしまいます。
線形分離不可能な場合でも、不能なりになるべく良い直線を引くための工夫がスラック変数です。
各データ点に対して、$\xi = \max\{0, 1-y(w_1x_1+w_2x_2+b)\}$をスラック変数といいます。
これはデータ点がマージン内に存在しないという条件をどれだけ違反しているかを測っているといえる量です。
スラック変数を導入し、マージン内へのデータ侵入や誤判定を許したマージンをソフトマージンといいます。
通常、SVMと言ったらソフトマージンを用いたSVMを指します。

 - カーネルSVM
SVMはカーネル法を用いることで非線形の分類を行うこともできます。
基本的なアイディアとしては「データを高次元に送って線形分離し元の空間に引き戻す」というものです。
![高次元空間での分類.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/100088b0-9b0e-d4b9-8ba5-4ea62c6b8477.png "高次元空間での分類")
しかし、高次元のデータは一般に計算量が膨大で扱いが困難なため、カーネル法という工夫が登場します。
これはカーネルトリックとも呼ばれ、「データを高次元に送ることなしに、高次元に送ったものと同じ結果を得る」ための工夫です。
カーネルにはさまざまな種類がありますが、主に次の2種類が良く使われます。
    - 多項式カーネル：$\phi(x)=(1,x,x^2,\cdots,x^d)$
    - Gaussカーネル：$\phi(x)=\left(1,\gamma x,\frac{1}{\sqrt{2!}}\gamma^2 x^2,\frac{1}{\sqrt{3!}}\gamma^3 x^3,\cdots\right)$

## SVMのコード実装
線形分離可能な場合、線形分離不可能でカーネル法の適用が必要な場合、それぞれ実装をしました。
コード詳細はgithubをご覧ください。

[github：SVMのコード実装](https://github.com/kimurazzz/rabbit/blob/main/SVM.ipynb)

### 実装内容と結果
以下は、線形分離不可能な場合においてカーネル法を適用して分類を行った結果です。
おおよそ分類できていることが分かりました。
![SVM結果.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567346/036add39-8f73-038a-c232-c798a0cd1df1.png "カーネル法を用いた分類結果")