Kaggleの臨床検査データセットを使ってみた③　～アンサンブル学習をしてみた～

## 概要
Kaggleの血液検査データセットを使ってデータ分析をしてみた。
いろいろ試してみた結果、量が多くなったので分割します。
今回はその③ (全5回)

**他の回はこちらから**
[①～モデルの性能比較をしてみた～](https://qiita.com/tuk19/items/b0df794d4d827511adf1)
[②～特徴選択をして、重要度を可視化してみた～](https://qiita.com/tuk19/items/dd759caded18d0aeeb20)
[④～外れ値の取り扱い～](https://qiita.com/tuk19/items/7c1f8090a3c93aa8ff49)
[⑤～不均衡データの取り扱い～](https://qiita.com/tuk19/items/ade9a23ce80988aa6a30)

使用したデータセット：[Patient Treatment Classification (Electronic Health Record Dataset)](https://www.kaggle.com/datasets/saurabhshahane/patient-treatment-classification)
インドネシアの病院で集められた血液検査の結果から、患者に治療が必要かどうかを判定する

## モデル
今回使用したモデルは以下の6種類

1. XGBoost
2. ニューラルネットワーク
3. ランダムフォレスト
4. ロジスティック回帰
5. 決定木
6. k－近傍法

## データ確認
データは血液検査の結果。
データの検査方法が分からなかったため、正常値は[2016年度国立がん研究センターのデータ](https://www.ncc.go.jp/jp/ncch/division/clinical_laboratory/kensa.pdf)をお借りしました。（※ 正常値は測定方法などにより若干のばらつきあり）

| 列No   | 検査値       | 和訳                      |　正常値                                               |高いと|低いと|
|:-: |:-:           |:-:                       |:-:                                                       |:-:                                                       |:-:                                                       |
|  1 | HAEMATOCRIT  | ヘマトクリット            | 男性：40.7～50.1 ％, <BR>女性：35.1～44.4 ％                      |多血症など|貧血など|
|  2 | HAEMOGLOBINS | ヘモグロビン              | 男性：13.7～16.8 g/dl, <BR>女性：11.6～14.8 g/dl                 |多血症など|貧血など|
|  3 | ERYTHROCYTE  | 赤血球                    | 男性：4.35～5.55 x 10<sup>6</sup> /μＬ, <BR>女性：3.86～4.92 x 10<sup>6</sup> /μＬ |多血症など|貧血など|
|  4 | LEUCOCYTE    | 白血球                    | 3.3～8.6 x 10<sup>3</sup>/μL                            |感染症・白血病など|一部感染症・膠原病・貧血など|
|  5 | THROMBOCYTE	| 血小板                    | 158～348 x 10<sup>3</sup>/μL                            |血小板血症・白血病・多血症など|貧血・紫斑病など|
|  6 | MCH          | 平均赤血球ヘモグロビン量   | 27.5～33.2 pg                                            |巨赤芽球性貧血など|鉄欠乏性貧血など|
|  7 | MCHC	        | 平均赤血球ヘモグロビン濃度 | 31.7～35.3 g/dL                                          |脱水・多血症など|貧血など|
|  8 | MCV          | 平均赤血球容積            | 83.6～98.2 fL　                                          |巨赤芽球性貧血など|鉄欠乏性貧血など|
|  9 | AGE          | 年齢                     | ―                                                        |―                                                        |―                                                        |
| 10 | SEX          | 性別                     | ―                                                        |―                                                        |―                                                        |
| 11 | SOURCE       | 治療が必要か              | out: 治療不要, in: 治療が必要                             |―                                                        |―                                                        |



前処理は[①と同じ](https://qiita.com/tuk19/items/b0df794d4d827511adf1#%E5%89%8D%E5%87%A6%E7%90%86)なので割愛

## アンサンブル学習
複数のモデルの結果を融合させて精度を向上させる手法。
今回はランダムフォレスト、ロジスティック回帰、決定木を使ってアンサンブル学習
前回までの結果

|ROC_AUC|特異度|感度|f1値|
|:-:|:-:|:-:|:-:|
|['AUC_RF', 0.755]|['specificity_RF', 0.859]|['recall_RF_S', 0.658]|['f1_RF', 0.707]|
|['AUC_RF_S', 0.75]|['specificity_KN_S', 0.853]|['recall_RF', 0.652]|['f1_RF_S', 0.703]|
|['AUC_LR', 0.723]|['specificity_RF_S', 0.843]|['recall_DT', 0.618]|['f1_LR', 0.666]|
|['AUC_DT_S', 0.721]|['specificity_LR', 0.837]|['recall_LR', 0.61]|['f1_DT_S', 0.664]|
|['AUC_LR_S', 0.719]|['specificity_DT_S', 0.833]|['recall_DT_S', 0.61]|['f1_LR_S', 0.661]|
|['AUC_KN', 0.702]|['specificity_LR_S', 0.831]|['recall_LR_S', 0.607]|['f1_KN', 0.641]|
|['AUC_KN_S', 0.69]|['specificity_KN', 0.813]|['recall_KN', 0.591]|['f1_DT', 0.625]|
|['AUC_DT', 0.677]|['specificity_DT', 0.737]|['recall_KN_S', 0.527]|['f1_KN_S', 0.61]|

```python:学習準備
X_ENS, y_ENS, df_ENS = dframe_copy(df_samp, "SOURCE")
train_X, test_X, train_y, test_y = train_test_split(X_ENS, y_ENS, test_size=0.2, random_state=42)
train_X, val_X, train_y, val_y = train_test_split(train_X, train_y, test_size=0.1, random_state=42)
train_X = np.asarray(train_X).astype(np.float32)
train_y = np.asarray(train_y).astype(np.float32)
val_X = np.asarray(val_X).astype(np.float32)
val_y = np.asarray(val_y).astype(np.float32)
test_X = np.asarray(test_X).astype(np.float32)
test_y = np.asarray(test_y).astype(np.float32)
```

### 重み付きVoting
```python:Weight_Average_Voting
dtc = DecisionTreeClassifier()
estimators = [('DT', DT_model), ('RF', RF_model), ('LR', LR_model)]
vote_clf = VotingClassifier(estimators=estimators, voting='soft', weights=[1, 3, 2])
vote_clf.fit(train_X, train_y)
vote_clf.score(test_X, test_y)
pred_y = vote_clf.predict(test_X)

# モデル評価
AUC_WAV = create_ROCcurve(test_y, pred_y)
tn_WAV, fp_WAV, fn_WAV, tp_WAV, specificity_WAV, recall_WAV, f1_WAV = create_cm(test_y, pred_y, True)
AUC_list.append(["AUC_WeightAvarageVoting", np.round(AUC_WAV, 3)])
specificity_list.append(["specificity_WeightAvarageVoting", np.round(specificity_WAV, 3)])
recall_list.append(["recall_WeightAvarageVoting", np.round(recall_WAV, 3)])
f1_score_list.append(["f1_WeightAvarageVoting", np.round(f1_WAV, 3)])
```
#### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/ec8555e3-4a79-09f4-92b1-aa65afee8dea.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/4ef0ea9e-39a9-27b8-50de-813db71dcf4c.png)

### Bagging
```python:バギング
bag_model = BaggingClassifier(base_estimator=DT_model, n_estimators=100)
bag_model.fit(train_X, train_y)
bag_model.score(test_X, test_y)
pred_y = bag_model.predict(test_X)

# モデル評価
AUC_BAG = create_ROCcurve(test_y, pred_y)
tn_BAG, fp_BAG, fn_BAG, tp_BAG, specificity_BAG, recall_BAG, f1_BAG = create_cm(test_y, pred_y, True)
AUC_list.append(["AUC_Bagging", np.round(AUC_BAG, 3)])
specificity_list.append(["specificity_Bagging", np.round(specificity_BAG, 3)])
recall_list.append(["recall_Bagging", np.round(recall_BAG, 3)])
f1_score_list.append(["f1_Bagging", np.round(f1_BAG, 3)])
```
#### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/c8af77d0-8782-91b8-2567-43fefac9f2dd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/f230df50-3518-b1ad-6642-218f2c945214.png)

### Stacking
```python:スタッキング　
estimators = [('DT', DT_model), ('RF', RF_model), ('LR', LR_model)]
stk_clf = StackingClassifier(estimators=estimators, final_estimator=LogisticRegression(solver='liblinear'))
stk_clf.fit(train_X, train_y)
stk_clf.score(test_X, test_y)
pred_y = stk_clf.predict(test_X)

# モデル評価
AUC_STK = create_ROCcurve(test_y, pred_y)
tn_STK, fp_STK, fn_STK, tp_STK, specificity_STK, recall_STK, f1_STK = create_cm(test_y, pred_y, True)
AUC_list.append(["AUC_Stacking", np.round(AUC_STK, 3)])
specificity_list.append(["specificity_Stacking", np.round(specificity_STK, 3)])
recall_list.append(["recall_Stacking", np.round(recall_STK, 3)])
f1_score_list.append(["f1_Stacking", np.round(f1_STK, 3)])
```
#### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/56a4c39b-ff14-46c7-dc4d-6ea6ab28c81f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683664/6062a12e-ff09-4761-1c88-69786ea6a159.png)

### 考察
|ROC_AUC|特異度|感度|f1値|
|:-:|:-:|:-:|:-:|
|['AUC_RF', 0.755]|['specificity_Bagging', 0.862]|['recall_RF_S', 0.658]|['f1_Stacking', 0.708]|
|['AUC_Stacking', 0.755]|['specificity_RF', 0.859]|['recall_Stacking', 0.658]|['f1_RF', 0.707]|
|['AUC_RF_S', 0.75]|['specificity_KN_S', 0.853]|['recall_RF', 0.652]|['f1_RF_S', 0.703]|
|['AUC_WeightAvarageVoting', 0.745]|['specificity_Stacking', 0.853]|['recall_WeightAvarageVoting', 0.639]|['f1_WeightAvarageVoting', 0.694]|
|['AUC_LR', 0.723]|['specificity_WeightAvarageVoting', 0.851]|['recall_DT', 0.618]|['f1_LR', 0.666]|
|['AUC_DT_S', 0.721]|['specificity_RF_S', 0.843]|['recall_LR', 0.61]|['f1_DT_S', 0.664]|
|['AUC_LR_S', 0.719]|['specificity_LR', 0.837]|['recall_DT_S', 0.61]|['f1_LR_S', 0.661]|
|['AUC_Bagging', 0.717]|['specificity_DT_S', 0.833]|['recall_LR_S', 0.607]|['f1_Bagging', 0.65]|
|['AUC_KN', 0.702]|['specificity_LR_S', 0.831]|['recall_KN', 0.591]|['f1_KN', 0.641]|
|['AUC_KN_S', 0.69]|['specificity_KN', 0.813]|['recall_Bagging', 0.572]|['f1_DT', 0.625]|
|['AUC_DT', 0.677]|['specificity_DT', 0.737]|['recall_KN_S', 0.527]|['f1_KN_S', 0.61]|


ランダムフォレスト、ロジスティック回帰、決定木でアンサンブル学習を実施したが、元のランダムフォレストによる学習と大きな差はなかった。
一方で、ロジスティック回帰や決定木と比べると重み付きVoteingやStackingで改善がみられることから、精度の高いモデルを使用すれば全体的な改善につながると考えられる。

## 参考資料
[機械学習上級者は皆使ってる？！アンサンブル学習の仕組みと3つの種類について解説します](https://www.codexa.net/what-is-ensemble-learning/)
[アンサンブル学習](https://ai-kenkyujo.com/term/ensemble-learning/)
[アンサンブル学習 ～三人寄れば文殊の知恵～ たくさんモデルを作って推定性能を上げよう！](https://datachemeng.com/ensemblelearning/)
[アンサンブル学習（Ensemble learning）解説と実験](https://data-analysis-stats.jp/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92/%E3%82%A2%E3%83%B3%E3%82%B5%E3%83%B3%E3%83%96%E3%83%AB%E5%AD%A6%E7%BF%92%EF%BC%88ensemble-learning%EF%BC%89%E8%A7%A3%E8%AA%AC%E3%81%A8%E5%AE%9F%E9%A8%93/)
[アンサンブル学習](https://axa.biopapyrus.jp/machine-learning/ensemble-learning.html)