PyCaretのLassoとLightGBMで特徴量選択

# 概要

PyCaretのLassoとLightGBMで学習したモデルの重みを用いて、特徴量選択を実施した。

# 実装

実行環境は以下の通り

- Python 3.9.0
- PyCaret 2.3.0

## 前準備

データは、pycaretに用意されているサンプルデータの`insuarance`を利用する。

[Build and deploy your first machine learning web app - PyCaret Official](https://pycaret.gitbook.io/docs/learn-pycaret/official-blog/build-and-deploy-your-first-machine-learning-web-app#business-problem)


このデータは、保険会社が顧客の保険料を決める際に、貸し倒れしないような保険料を設定すべく、患者がWebに入力したフォームの内容から、将来の疾患や起こりうる健康リスクを予測し、患者ごとの適切な料金を設定するというタスクを解くために用意されたもの。

データの中身を見ると、顧客の性別や年齢、喫煙の有無といった情報が格納されている。

```python 
from pycaret.datasets import get_data
insurance = get_data('insurance')
```

```sh:
age	sex	bmi	children	smoker	region	charges
0	19	female	27.900	0	yes	southwest	16884.92400
1	18	male	33.770	1	no	southeast	1725.55230
2	28	male	33.000	3	no	southeast	4449.46200
3	33	male	22.705	0	no	northwest	21984.47061
4	32	male	28.880	0	no	northwest	3866.85520
```

データを読み込み後`setup`を実行し、データをPycaretのパイプラインに載せる。`silent=True`とすることで、setup実行時に「このデータで問題ない？」のYes/Noの入力の手間が省ける。


```python:
from pycaret.regression import *
exp_name = setup(data = insurance,  target = 'charges', silent = True)
```

後の可視化に利用するので、カラム名を取得する。setup処理で、子供の人数(`children`)や喫煙の有無(`smoker_yes`)がワンホットベクトルとして作成されたことが確認できる。

```python:
columns = get_config("X").columns
print(columns)
```

```sh:
Index(['age', 'bmi', 'sex_male', 'children_0', 'children_1', 'children_2',
       'children_3', 'children_4', 'children_5', 'smoker_yes',
       'region_northeast', 'region_northwest', 'region_southeast',
       'region_southwest'],
      dtype='object')
```

全モデルで探索を実行すると、以下の出力を得られる。gbrのR2の最大値が0.8613なので、後の特徴量選択でどのように変化するか確認する。

```python:
compare_models()
```

![3b2b5447.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/eb8a96b7-ae39-b3de-117b-225cda25c395.png)




### Lassoによる特徴量の重要度の確認

Lassoのモデルの作成と、作成したモデルのチューニングをおこなう。

```python:
lasso = create_model('lasso')
tuned_lasso = tune_model(lasso)
```

作成された`lasso`、`tuned_lasso`の型を出力すると、`sklearn.linear_model._coordinate_descent.Lasso`であることが確認できる。

```python:
type(tuned_lasso)
```

```sh:
sklearn.linear_model._coordinate_descent.Lasso
```

つまり作成されたモデルに対して、以下のscikit-learnのlassoと同じ処理が行える。

[sklearn.linear_model.Lasso — scikit-learn 1.0.2 documentation](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Lasso.html
)

`sklearn.linear_model._coordinate_descent.Lasso`は、`coef_`のメンバ変数に各特徴量の重みが格納されている。なので`coef_`を利用して、各特徴量が予測結果にどれだけ影響を与えているかを確認する。


```python:
feature_imp = pd.DataFrame(sorted(zip(tuned_lasso.coef_, columns)), columns=['Value','Feature'])
feature_imp.plot.barh(x='Feature',)
```

![lasso_coef.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/01c74033-06d3-db53-83e4-39b4b6be91f1.png)


結果から`smoker_yes`の喫煙者である、という要素が保険料の増額に大きな影響を与えることが確認できる。直感的にも喫煙＝健康に悪いという点から、喫煙者であることが保険料を値上げする要因であることは納得できる。値を見ると年額$23,000程度値上がりするようなので、禁煙は健康にも金銭的にもお勧めできる。

次に`重み=0`の推論に全く利用されていない特徴量を確認すると`children_1`、`children_5`の2つであることが確認できた。


```python:
print(columns[tuned_lasso.coef_ == 0 ])
```
```sh:
Index(['children_1', 'children_5'], dtype='object')
```

### LightGBMによる特徴量の重要度の確認

Lassoの場合と同様に、モデルの作成とチューニングを実施する。

```python:
lightgbm = create_model('lightgbm')
tuned_lightgbm = tune_model(lightgbm)
```

モデルの型を出力すると、`lightgbm.sklearn.LGBMRegressor`であることが確認できた。

```python:
type(tuned_lightgbm)
```

```sh:
lightgbm.sklearn.LGBMRegressor
```

https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMRegressor.html

`lightgbm.sklearn.LGBMRegressor`は、`feature_importances_`に各特徴量の重要度が格納されているので、Lassoと同じ手順で特徴量の重要度を確認する。

```python:
feature_imp = pd.DataFrame(sorted(zip(tuned_lightgbm.feature_importances_, columns)), columns=['Value','Feature'])

feature_imp.plot.barh(x='Feature',)
```

![lightgbm_importance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/6ada3bee-d210-208b-9fa3-d9da8943d688.png)


こちらも`feature_importances_`が0の特徴量を出力すると、`children_4`, `children_5`の2つを得られた。

```python:
print(columns[tuned_lightgbm.feature_importances_ == 0 ])
```

```sh:
Index(['children_4', 'children_5'], dtype='object')
```

## 選択した特徴量で再学習

LassoとLightGMBで利用されなかった特徴量を取り除いて、再度学習を行う。Pycaretのパイプラインからデータを取り出す場合は`get_config`を利用する。


```python:
drop_columns = ['children_1', 'children_4', 'children_5']

# PycaretのXを取得し、不要なカラムを削除
pycaret_X = get_config('X')
feature_selected_X = pycaret_X.drop(drop_columns, axis=1)

print(feature_selected_X.columns)
```

```sh:
Index(['age', 'bmi', 'sex_male', 'children_0', 'children_2', 'children_3',
       'smoker_yes', 'region_northeast', 'region_northwest',
       'region_southeast', 'region_southwest'],
      dtype='object')
```

その後`set_config`を利用し、作成したデータをパイプラインにセットする。

```python:
set_config('X', feature_selected_X)
```

その後、再び学習を実行する。

```python:
compare_models()
```

![988d35ee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/eff12975-3e92-577b-7104-efa8802530da.png)


出力結果を見るとgbrのR2のスコアが最初の`0.8613`から`0.8347`に下がってしまった。今回は特徴量が13と少ないデータだったため、削減する必要もなかったのかも。

Kaggleなどの実際のデータで特徴量が大量の場合に利用すると効果的かもしれない。