機械学習を学んでみたのでそのまとめ③分類

　前回に引き続き、Udemy[【世界で74万人が受講】基礎から理解し、Pythonで実装！機械学習26のアルゴリズムを理論と実践を通じてマスターしよう](https://www.udemy.com/course/ml-with-python/)で学んだ内容をアウトプットしていきたいと思います。

(※この記事は勉強した内容のアウトプットを目的としてざっくりと書かれているので、もともと知っていて復習の為に読んだりするにはいいと思いますが、正しく知りたい方は別の記事を読んだほうが良いとおもわれます。コードに関してはライブラリが入っていれば、jupyterなどをコピペでだいたい動くはずです。)

##分類(Classifier)とは
分類モデルは、あるデータ（独立変数）からカテゴリ分けされた値（従属変数）を予測していきます。

例：独立変数の身長、体重、血圧から従属変数である生活習慣病にかかっているかを予測する。

ここでは、ロジスティック回帰、K近傍法、サポートベクトルマシン、カーネルSVM、ナイーブベイズ、分類木、ランダムフォレストの手法を学んだ。

##ロジスティック回帰
ロジスティック回帰は回帰と名前がついていますが、分類の手法です。もともとは統計学の中で確立された考え方なので直感的に理解するのが難しいのでまたざっくりと説明します。（正しく理解したい方は別の方の記事を読んで下さい。）

独立変数を$x_1$, $x_2$から従属変数$y$をを確率(0%~100%)として求めます。


```math
y = b_0 × b_1^{x_1} × b_2^{x_2}・・・b_n^{x_n}
```

上記の式の両辺を変形して下記の式を作ります。

```math
y' =  \frac{1}{1 + e^{-(b_0+b_1x_1+b_2x_2・・・b_nx_n)}}
```

この式はシグモイド関数と呼ばれグラフにすると以下のようになります。ちなみに$b_0+b_1x_1+b_2x_2・・・b_nx_n$の部分は前の記事の重回帰で出てきたものですね。

![qiita_20210515_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/c5131347-b0f9-f642-37f4-e95bbdd7e877.png)


これにより$b_0+b_1x_1+b_2x_2・・・b_nx_n$から$y'$を求めることができるようになります。

とりあえず、学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてロジスティック回帰を使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
```



```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[45  2]
 [ 2 65]]
0.9649122807017544

##K近傍法

ここでもざっくり言うと、対象とするデータの周辺から選択する数(K)を決めその多数決で対象とするデータがどの分類に属するかを決める方式です。詳しくは下の図で説明します。

![qiita_20210508_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/252f7604-11d7-60fb-d244-c1d47792a5fb.png)

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてK近傍法を使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = KNeighborsClassifier(n_neighbors=5, metric='minkowski', p=2)
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[42  5]
 [ 0 67]]
0.956140350877193




##サポートベクトルマシン

サポートベクトルマシンでは下図のように、似ているデータを持っている2つのデータ（サポートベクトル）が2つカテゴリを分ける境界となり、サポートベクトルの距離(マージン)が最大となるように境界線を引きます。

![qiita_20210508_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/aafedc92-82f0-bb95-9eb9-fd6405b09556.png)

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてサポートベクトルマシンを使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = SVC(kernel='linear')
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[46  1]
 [ 1 66]]
0.9824561403508771


##カーネルSVM

カーネルSVMについてもざっくりと説明します。カーネルSVNは下図のように線形で分類できないような時に使用します。

![qiita_20210509_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/37ff2a90-766e-f425-7909-d61855774fcd.png)

どのように分類するかというと、カーネル関数というものを使用して下図のように高次元にプロットして、超平面を引くことで分類します。

![qiita_20210509_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/9a26fc7f-64d3-b785-f5aa-71520f399ac9.png)

式にすると下のようになり$\sigma$はハイパーパラメータでこちらで指定する必要があります。ここでもざっくりというと$\sigma$の値が大きくなると変換後のZ軸の高さが高くなる傾向にあります。

```math
K(\overrightarrow{x},\overrightarrow{l^i}) = e^-\frac{||\overrightarrow{x}-\overrightarrow{l^i}||^2}{2\sigma^2}
```

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてカーネルSVMを使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = SVC(kernel='rbf')
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[46  2]
 [ 0 66]]
0.9824561403508771

##ナイーブベイズ

ナイーブベイズは下記のベイズの定理を用いた条件確率を用いて分類する手法です。

ベイズの定理

```math
P(B|A) = \frac{P(A|B) P(B)}{P(A)}
```

$P(B|A)$ :事後確率
$P(A|B)$ :尤度
$P(B)$ :事前確率
$P(A)$ :周辺尤度

![qiita_20210515_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/ca573344-6394-a9ce-fe36-bf0f21d235c1.png)

また、ナイーブベイズを使うには前提条件が厳しくてそれぞれの独立変数が相関しあっていない場合でしか使えない等があります。

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてナイーブベイズを使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = GaussianNB()
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[42  5]
 [ 6 61]]
0.9035087719298246


##分類木

分類木では情報エントロピー（煩雑さ）を定量的に使うことで分類するための仕組みを使っていきます。
情報エントロピーは下図の示すように0か1に近づくほど小さくなり、ちょうど半々になる0.5で一番高くなります。

![qiita_20210515_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/56ae1b46-ccf4-9121-ec8c-bcc684c5d592.png)


![qiita_20210515_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/31614e42-f13f-43a4-22ad-fa12bdc6210f.png)

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数として分類木を使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング(分類木に関してはしなくても良い)

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = DecisionTreeClassifier(criterion='entropy')
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [0 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

`出力結果`
[[44  3]
 [ 4 63]]
0.9385964912280702


##ランダムフォレスト

ランダムフォレスト（分類）の考え方は、回帰のところで学んだ考え方と同じなので説明は省略します。

とりあえず、ここでも学んだ内容でScikit-learnのデータセットの乳がん患者を使って、target以外のデータを独立変数、target（悪性or良性）を従属変数としてランダムフォレスト（分類）を使った分類をしたいと思います。

ライブラリのインポート

```python
import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import  RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.preprocessing import StandardScaler
```

データセットの読み込み

```python
cancer = load_breast_cancer()
cancer_data = pd.DataFrame(cancer.data, columns = cancer.feature_names).assign(malignant=np.array(cancer.target))
X = cancer_data.iloc[:, :-1].values
y = cancer_data.iloc[:, -1].values
```

訓練用データセットとテスト用データセットへの分割(8:2)

```python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
```

フィーチャースケーリング(分類木と同様にしなくても良い)

```python
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```

訓練用データセットを使った学習

```python
classifier = RandomForestClassifier(n_estimators=10, criterion='entropy')
classifier.fit(X_train, y_train)
```

テスト用データセットを使った結果の予測

```python
y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
```

`出力結果`
[[0 0]
 [1 1]
 [1 1]
・・・
（省略）
・・・
 [0 0]
 [0 0]
 [1 1]]

混同行列と精度スコアでモデルの評価

```python
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)
```

[[44  3]
 [ 2 65]]
0.956140350877193

## おまけ（混同行列の可視化）
混同行列はseabornをつかって可視化すると見やすいです。

```python
import seaborn as sns
sns.heatmap(cm, annot=True)
```

![confusion_matrix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/6647ca04-ff77-f126-d6aa-12e314b9a081.png)




## 分類分析まとめ

| モデル | 長所 | 短所 |
|:-:|:-:|:-:|
| ロジスティック回帰  | 変数が結果に対してどの程度影響を与えるかがわかる  | ロジスティック回帰の前提条件を満たさないと使えない  |
| K近傍法 | 理解しやすい  | kを決める必要がある |
| サポートベクトルマシン  | 適用が簡単で過学習しにくい  | 非線形には適さない  |
| カーネルSVN | 非線形に対して高い精度で過学習もしにくい | 計算量が多くなり特徴量が多い場合は最適ではない  |
| ナイーブベイズ | 非線形にも対応できる確率的な考え方 | 変数同士が相関を持っていないことが条件  |
| 分類木 | 線形・非線形のどちらにも対応可能 | データが少ないと精度が低く、過学習しやすい  |
| ランダムフォレスト | 線形、非線形どちらでも使え、精度も高い  | 使用する木の数を指定する必要があり、多くすると精度が高くなるが学習時間や予測時間も増加する  |