scikit-learnによる機械学習実装 ~ diabetes編 ~

# 初めに
## 内容
scikit-learnを用いた機械学習を行います。
データセットは[scikit-learnから提供されるデータセット](https://pythondatascience.plavox.info/scikit-learn/scikit-learn%E3%81%AB%E4%BB%98%E5%B1%9E%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88)を使用します。

本記事では「**Diabetes**」（糖尿病患者の診断データ）を取り扱います。

[irisデータセットの記事はこちらです。](https://qiita.com/RiRyCasals/items/8515739d8a3cf8cf7891)
[Bostonデータセットの記事はこちらです。](https://qiita.com/RiRyCasals/items/bcf04638245a01fa25f6)
[digitsデータセットの記事はこちらです。](https://qiita.com/RiRyCasals/items/81c1de806fdae0a0e0c4)
その他のデータセットは別の記事にて更新しようと思います。

## 対象
主に**初心者向け**です。が、入門向けではないです。

本記事は友人に向けて作成してますが、
**「機械学習したい」と思っている方**にも読んでもらえるように書いています。

実装に関しての解説は記載しますが、アルゴリズムなどの解説は（基本的に）しませんのでご理解のほどを。

## 環境
- Google Colaboratory
- Python 3.6.9
- sklearn 0.22.2
- pandas 1.1.5
- matplotlib 3.2.2

ローカルで環境をそろえるのは難しいので（Dockerやクラウドを使えばできますが）
今回はColaboratoryを用います。
上記のバージョンに合わせるとローカル環境でも実行できます。

---
以降は実際にpythonを用いながらになるのでColaboratoryの用意をお願いします。

# Diabetes について
Diabetsは糖尿病患者442人の検査数値と1年後の疾患進行状況についてのデータセットです。

## 目的変数
まず、目的変数について確認しましょう。

```python:
from sklearn.datasets import load_diabetes
import pandas as pd

diabetes = load_diabetes()
target = diabetes.target

df_target = pd.DataFrame(target, columns=['progression'])
df_target.head()
```
次のような表が生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/0621708e-0f22-9d59-2163-d0553d7e6e8a.png)
これは1年後に糖尿病がどれくらい進行しているかを定量的に表したものだそうです。

<details><summary>友人Tへ</summary>
「151.0ってどれくらい進行してるの？」と気になると思います。（私も気になります）
結論を言うと「わっかんねぇ！」ですね。
（詳細を探すのもいいですが、これに時間を使いすぎて無駄にしないように！）
「データを提供してくれた側が前処理をしておいてくれた」という状況だと思えばあとの学習がはかどるかと思います。
「データの収集」や「データの分析」を行ってデータセットを作成することは、今後の勉強課題となりそうですね。
</ditails>

ちなみに、以下のようにすることでデータセットの説明を表示することもできます。
（英語ですが...）

```python:
print(diabetes.DESCR)
```

## 説明変数
次に、説明変数について確認しましょう。

```python:
data = diabetes.data
feature_names = diabetes.feature_names

df_data = pd.DataFrame(data, columns=feature_names)
df_data.head()
```
次のような表が生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/ece4e2b3-8c84-bc34-8c1b-bb088950b6e6.png)

ageやsexまでもがfloatなのは、datasetを用意した側ですでに正規化を行ったからです。
正規化に関して[diabetes.sdata.txt](http://web.stanford.edu/~hastie/Papers/LARS/diabetes.sdata.txt)には次のように書かれてます。（一部抜粋）
> first 10 columns have been normalized to have mean 0 and Euclidean
norm 1 and the last column y has been centered (mean 0).

各説明変数の内容は以下の通りです。

|変数名|内容|
|:---:|:---|
|age|年齢|
|sex|性別|
|bmi|肥満度指数|
|bp|平均血液圧力|
|s1~s6|血清測定値（詳細不明？）|
s1~s6についてはあまり情報がありませんでした。
[こちらサイト](http://taustation.com/diabetes-dataset/)ではs1~s6を次のようにとらえているようです。

|変数名|内容|
|:---:|:---|
|s1|総コレステロール値|
|s2|悪玉コレステロール値|
|s3|善玉コレステロール値|
|s4|総コレステロール値|
|s5|ラモトリギン|
|s6|グルコース|
s1とs4の違いがわからんですね...
もっと詳しく知りたい人は[こちらの論文を]((http://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf))ご覧ください。
Google先生に頼めば論文の翻訳もしてもらえますので頑張って...
（ちなみに、上記6項目は「Serum Measurements」だそうです。詳細がわからん...）

```python:
df_data.shape # => (442, 10)
```
レコード数は442件だそうです。

### 相関関係を見る
pandasのscatter_matrixを用いて、すべての目的変数のペアプロットを作成します。

```python:
import matplotlib.pyplot as plt

pd.plotting.scatter_matrix(pd.concat([df_data, df_target], axis=1), figsize=(20, 20))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/fd186f9b-c2cf-427e-1c6f-d4419e2938a3.png)
s1とs2に強い相関がありますね。（s系同士は相関が見れますね）

# 機械学習しよう
## 前処理
データそのものはすでに正規化処理が行われているので、
欠損値の確認及び補完とデータの分割を行います。

まず欠損値についてです。

```python:
df_data.isnull().any()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/7c961e1a-646a-2a67-fdd5-e5cab421832c.png)
データに欠損値はないようです。

次に分割作業です。

```python:
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(df_data, df_target, test_size=0.2, random_state=141)

print('x_train :', x_train.shape) # => x_train : (353, 10)
print('y_train :', y_train.shape) # => y_train : (353, 1)
print('x_test :', x_test.shape) # => x_test : (89, 10)
print('y_test :', y_test.shape) # => y_test : (89, 1)
```

## 学習
今回の目的変数の予測には回帰モデルを使用します。
bostonデータセットではlassoを使用したので、今回は**ARDRegression**を使用してみましょう。
他のモデルを使用したいのであれば[公式リファレンス](https://scikit-learn.org/stable/modules/classes.html)やほかの方のQiita記事をご覧ください。

**データセットの特徴に合わせて、適切なモデルを選ぶ**ことが重要になりますので、各モデルについての勉強をしたほうが良いですね。

```python:
from sklearn.linear_model import ARDRegression

model = ARDRegression()
model.fit(x_train, y_train)
```
## 推論
モデルの評価には$R^2$, RMSE, MAEの3つとも確認してみましょう。
$R^2$は1に近いほど、RMSE,MAEは小さければよいモデルと判断します。

```python:
from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
import numpy as np

pred = model.predict(x_test)

r2 = r2_score(y_test, pred)
rmse = np.sqrt(mean_squared_error(y_test, pred))
mae = mean_absolute_error(y_test, pred)

print('R2 :', r2) # => 0.601853388737597
print('RMSE :', rmse) # => RMSE : 50.73687625628082
print('MAE :', mae) # => MAE : 41.407855314776924
```
random_stateの値によっては0.4を下回ったりするので、考え物ですね...
評価指数については[こちらのサイト](https://funatsu-lab.github.io/open-course-ware/basic-theory/accuracy-index/)が参考になるやもしれません。

実測値と予測値を折れ線グラフで可視化し、確認してみましょう。

```python:
x = np.arange(y_test.shape[0])
plt.title('Comparison of measured and predicted values')
plt.ylabel('progression')
plt.plot(x, y_test, label='y_test')
plt.plot(x, pred, label='pred')
plt.legend()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690399/bcc008a9-d9b8-a855-1e46-60a09122f100.png)
縦軸が疾患の進行度を表す値で、横軸がデータ89件分（train_test_splitの分割数により変化する）を表します。
凡例の通り青線がy_test、オレンジ線がpredです。

たしかに大まかに表現できてはいますが、いまいちなモデル、そんな印象を見受けられますね。

# 最後に
モデルのインスタンス作成時にハイパーパラメータ（人が調整するパラメータ）を設定できます。
[公式リファレンス](https://scikit-learn.org/stable/modules/classes.html)に設定できるハイパーパラメータの詳細が載っているので参考になるかと。

model_selection.GridSearchCV などで最適なハイパーパラメータの探索などができますので、使用してみてください。

次回は定番の手書き文字の認識を行います。