【AutoMLで分類（3分類）】Pycaretで機械学習モデルの比較

# はじめに
[Pycaret](https://pycaret.readthedocs.io/en/latest/index.html)とは数行のコードで機械学習モデルを構築・比較してくれるAutoMLライブラリです。  
  
この投稿では分類問題(3分類)を取り扱い、結果の解釈やコードの詳細を説明します。

# 分類問題用のデータセットの作成
分類問題用にデータセットにラベルづけを行います。今回は[こちら](https://qiita.com/oki_kosuke/items/debe4883505cb2d9809d)のページで紹介した"df.csv"というデータを用いていきます。csvファイルは[GitHun](https://github.com/okikosuke/sample/tree/main/sample_data)にも保存しております。

```python
#　データの読み込み
import pandas as pd
df = pd.read_csv("df.csv")
print("Datasize: " + str(df.shape))
df.tail(15)
```

```:出力
Datasize: (707, 10)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/6de4a419-e405-4235-a236-039f4676fca6.png)

このデータセットのyの値に対して、以下のルールに基づきラベル付を行います。  
- y≦150は"L"
- y≦250は"M"
- y>250は"H"

```python
# y列をラベルに置き換えます
df.loc[df["y"] > 250, "y_label"] = "H"
df.loc[df["y"] <= 250, "y_label"] = "M"
df.loc[df["y"] <= 150, "y_label"] = "L"
df.tail(15)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/5c6547c7-6b0d-3e0c-b434-0d515db3bc6d.png)

yとy_labelを見比べると、定義した通りにラベル付されていることが分かります。

# 0. データの読込みと前処理

```python
# ライブラリのインポート
# 不要な警告文を非表示にする
import warnings
warnings.filterwarnings("ignore")
from pycaret.classification import *
```
```python
# pycaretのバージョンを確認
import pycaret
print("pycaret version: ", pycaret.__version__)
```
```:出力
pycaret version:  3.0.4
```

次にデータの前処理を行っていきます。今回はyを無視して、x1～x9を説明変数としてy_labelを予測していくことにします。

```python
# データの前処理
clf = setup(df, target ="y_label", fold = 5, 
            ignore_features = ["y"],
            fold_shuffle=True, session_id = 123)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/3e294d7e-8030-acad-cc73-6ddda4dc2b04.png)


3.Label Encoded にラベル付の方法が記載されている。

参考サイト（引数の参考）

https://torch.classcat.com/2020/12/17/pycaret-2-2-tutorials-multiclass-classification-mclf101/

# 1. モデルの比較
この段階ではハイパーパラメータの最適化までは実行されていない。

```python
# 各種モデルの比較
best_model = compare_models()
best_model
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/96f6c886-e3be-de61-b22c-d271963e607e.png)

```:出力
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
                   intercept_scaling=1, l1_ratio=None, max_iter=1000,
                   multi_class='auto', n_jobs=None, penalty='l2',
                   random_state=123, solver='lbfgs', tol=0.0001, verbose=0,
                   warm_start=False)
```

- Accuracy：正解率（正例、負例を問わず正解した割合）
- AUC：ROC曲線で囲まれた面積
- Recall：再現率、実際に陽性であるもののうち陽性と予測できた割合
- Prec.：適合率、陽性と予測したうちで実際に陽性だった割合
- F1：F値、再現率と適合率の調和平均

# 2. モデルの確認
```python
# create_modelの引数に上記で最適化したモデル名を記載
model = create_model('lightgbm')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/27a0d856-bd9c-8f1a-4db2-bdac75703f9b.png)

このMeanの値が上記のモデル比較のスコアとなっている

# 3. ハイパーパラメータの最適化
選択したモデルのハイパーパラメータの最適化を行う（問題なければこれが最終モデルとなる）。今回はlightGBMのモデルを最適化していきます。


```python
# ハイパーパラメータの最適化を行う
final_model = tune_model(model, n_iter = 500, optimize = 'Accuracy')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/f0d10e31-f731-2437-83b8-d3261f019d21.png)

モデルのチューニングが上手くいっていれば、Accuracyなどのスコアが上がる。

次に最適化したモデルで結果を可視化していく（公式： https://pycaret.readthedocs.io/en/latest/api/classification.html)

# 4. モデルのハイパーパラメータの確認

```python
plot_model(final_model, plot = 'parameter')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/7acb61de-388e-5e0f-30bb-068752487da5.png)

# 5. ROC曲線

```# 可視化　ROC曲線
plot_model(final_model, plot="auc")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/ed711157-1264-b4f4-ab95-70c8a6c86f45.png)

真横軸に偽陽性率、縦軸に真陽性率をプロットした曲線がROC(Receiver Operating Characteristic)）曲線である。ROC曲線の下の面積をAUC(Area Under the Curve)と呼び、最大で1で完全にあてずっぽうの時で0.5になる。

https://blog.kikagaku.co.jp/roc-auc

# 6. 混同行列

```python
plot_model(final_model, plot="confusion_matrix")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/37e95005-7b2b-cfe7-7e5b-1066cd7e2b9c.png)

ラベルのつけ方はデータ読み込み時の"Label Encoded"に記載あり。

# 7. 予測エラー

```python
plot_model(final_model, plot = 'error')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/37182473-3516-13d8-3af7-bb6120a397eb.png)

8. クラスレポート

```python
plot_model(final_model, plot = "class_report")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/2cfa34a4-2f0c-2b38-276d-6eae866705d5.png)

それぞれのクラスのスコアが表示される。

# 12. 学習曲線
```python
# 可視化　引数plot = learning　で　学習曲線
plot_model(final_model, plot = 'learning')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/f4094ebd-ea08-c0d4-6e62-f35a4aef0487.png)

横軸に訓練データのデータ数、縦軸に予測精度をプロットしている。理想的なモデルではサンプルサイズを大きくした時にTrainig Scoreが下がり、CV Scoreが上がっていき、両者が漸近する。漸近する値があらかじめ設定した値よりも大きいとモデルが上手く作られていると考えられる。両者ともにスコアが低い場合は学習不足なのでパラメータを増やす、学習データのスコアだけが高い場合は過学習気味なのでパラメータ数を減らすなどの対策が必要である。両者の差が大きい場合はデータ数を増やしてモデルの精度を上げるなどの対策が必要。

https://axa.biopapyrus.jp/machine-learning/model-evaluation/learning-curves.html

# 14. 検証曲線

```python
# 可視化　引数plot = vc　で 検証曲線
plot_model(final_model, plot = 'vc')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/0c36e94b-299c-5441-25bf-4d65ee68943f.png)

検証曲線は横軸にハイパーパラメータ、縦軸に予測精度をプロットしている。適切なハイパーパラメータ（例：max_depth：木の深さ）の値はどこかを確認でき、訓練精度と検証制度の差が小さく、精度が高いものを選ぶようにする。（学習不足や過学習を確認することができます。）横軸のハイパーパラメータはアルゴリズムによって異なる。

https://axa.biopapyrus.jp/machine-learning/model-evaluation/validation-curve.html

# 15. ディメンジョン

```python
plot_model(final_model, plot = 'dimension')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/97e94b9e-fcda-6487-1c0a-57eae181a1a7.png)

多変量データ可視化アルゴリズム。円周上に各特徴次元を一様に配置し、各特徴量を正規化して、中心から各円弧までの軸上に点をプロットします。このメカニズムにより、たくさんの次元を持つデータを円内に簡単に描画しすることができます。（特徴量の選択に関しては要調査）

https://www.scikit-yb.org/en/latest/api/features/radviz.html

# 16. 特徴量の重要度

```python
# 可視化　引数plot = feature　で特徴重要度プロット
plot_model(final_model, plot = 'feature')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/d4501753-ea27-6606-32dc-bae7e6018817.png)

```python
# 以下のコードで全ての特徴量に対して重要度を算出することも可能
plot_model(final_model, plot = 'feature_all')
```

# 17. 決定境界

```python
plot_model(final_model, plot = 'boundary')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/856912ab-23f7-1a4a-39f0-11c546dc65d6.png)


クラスを分類する決定境界

# 18. 決定木

```python
# 小さな決定木のモデルを作って、特徴量の関係を可視化する
dt = create_model('dt', max_depth=3)
plot_model(dt, plot = 'tree')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/88f845bc-b187-ce12-d47b-da6e01df363c.png)

# 19. plot_model一覧

```python
# これまでに出力したplot_modelを確認できる
evaluate_model(final_model)
```

# 20. モデルの確定

```python
final_model = finalize_model(final_model)
print(final_model)
```

```:出力
Pipeline(memory=FastMemory(location=C:\Users\*ユーザーネームに紐づく階層*\AppData\Local\Temp\joblib),
         steps=[('label_encoding',
                 TransformerWrapperWithInverse(exclude=None, include=None,
                                               transformer=LabelEncoder())),
                ('numerical_imputer',
                 TransformerWrapper(exclude=None,
                                    include=['x1', 'x2', 'x3', 'x4', 'x5', 'x6',
                                             'x7', 'x8', 'x9'],
                                    transformer=SimpleImputer(add_indicator=False,
                                                              copy...
                                colsample_bytree=1.0, feature_fraction=0.8,
                                importance_type='split', learning_rate=0.2,
                                max_depth=-1, min_child_samples=11,
                                min_child_weight=0.001, min_split_gain=0.2,
                                n_estimators=170, n_jobs=-1, num_leaves=8,
                                objective=None, random_state=123, reg_alpha=0.3,
                                reg_lambda=1e-07, silent='warn', subsample=1.0,
                                subsample_for_bin=200000, subsample_freq=0))],
         verbose=False)
```

```python
#　モデルの保存（　''の中は保存したいファイル名）
save_model(final_model,"Final_Model")
```


## 参考

- [PyCaret入門 Pythonで機械学習を自動化しよう！【AutoML】](https://aiacademy.jp/media/?p=954)

- [PyTorch 2.0, Hugging Face & Stable Diffusion](https://torch.classcat.com/2020/12/17/pycaret-2-2-tutorials-multiclass-classification-mclf101/)

- [low-code機械学習ライブラリ「PyCaret」でモデルを可視化してみた](https://zenn.dev/shnchr/articles/a7917a7e2e4f6b)

- [PyCaretの初心者向けまとめ（分類編）](https://qiita.com/shuhigashi/items/cb6816a1da1d347bbdc2)

- [low-code機械学習ライブラリ「PyCaret」でモデルを可視化してみた](https://qiita.com/shnchr/items/bdd4810b3477aa977413)