【kaggle】中級機械学習 - XGBoost【備忘翻訳】

# はじめに
当記事はkaggleのLearnの[Intro to Machine Learning](https://www.kaggle.com/learn/intro-to-machine-learning)の[XGBoost](https://www.kaggle.com/code/alexisbcook/xgboost)を翻訳して備忘としたものです。
拙い英語力なので間違い等あればご指摘いただけたらと思います。

まとめ:[【kaggle】翻訳記事まとめ【備忘翻訳】](https://qiita.com/leafeon00000/items/71e57af5b2d9b0b7c846)
前:[【kaggle】中級機械学習 - 交差検証【備忘翻訳】](https://qiita.com/leafeon00000/private/d33102acb7784c6ba1e3)
次:[【kaggle】中級機械学習 - データリーク【備忘翻訳】](https://qiita.com/leafeon00000/private/5745f0a4678469cd8dca)

:::note warn
当記事に含まれるコードはkaggle内のnotebook内で正常に動作します。動作を試したい場合はkaggleのnotebookで試してください。
:::

----

# XGBoost
構造化データに対する最も正確なモデリング手法

このチュートリアルでは、**gradient boosting(勾配ブースティング)** を用いてモデルを構築および最適化する方法を学びます。この方法はKaglleコンペティションで多く採用されており、様々なデータセットで最先端の結果を残しています。

## イントロ
このコースの大部分では、多くの決定木の予測を平均化するだけで、単一の決定木よりも優れたパフォーマンスを出すランダムフォレスト法で予測を行ってきました。

ランダムフォレスト法を「アンサンブル手法」と呼びます。定義上、アンサンブル手法は複数のモデルの予測を組み合わせます(例えば、ランダムフォレストの場合は複数の決定木を用いています)。

今回は、勾配ブースティングと呼ばれる別のアンサンブル手法を学びます。

# Gradient Boosting (勾配ブースティング)
**Gradient Boosting(勾配ブースティング)** は、サイクルを経てモデルをアンサンブルに反復的に追加する方法です。

まず、単一のモデルでアンサンブルを初期化します。しかし、その予測は非常に単純なものになる可能性があります(たとえその予測が極めて不正確であったとしても、その後のアンサンブルへの追加によってそれらの問題は修正されるでしょう)。

サイクルは次のように進みます:
* まず、現在のアンサンブルを使用して、データセット内の各観測値の予測を生成します。予測を行うには、アンサンブル内のすべてのモデルからの予測を追加します
* これらの予測は損失関数を計算するために使用されます(例:[平均二乗誤差](https://en.wikipedia.org/wiki/Mean_squared_error)など)
* 次に、損失関数を使用して、アンサンブルに追加される新しいモデルを適合させます。具体的には、この新しいモデルにアンサンブルを追加することで損失が減少するようにモデルパラメータを決定します。(補足:「勾配ブースティング」の「勾配」とは、損失関数に[勾配下降法](https://en.wikipedia.org/wiki/Gradient_descent)を使用してこの新しいモデルのパラメータを決定することを意味します。)
* 最後に、新しいモデルをアンサンブルに追加して...
* ...繰り返す!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547651/0b20e844-d192-f3a4-1ba7-a2f34dd0fc4f.png)

## 例
まず、`X_train`、`X_valid`、`y_train`、`y_valid`にトレーニングデータと検証データを読み込みます。

<details><summary>読み込みコード</summary>

```python
import pandas as pd
from sklearn.model_selection import train_test_split

# Read the data
data = pd.read_csv('../input/melbourne-housing-snapshot/melb_data.csv')

# Select subset of predictors
cols_to_use = ['Rooms', 'Distance', 'Landsize', 'BuildingArea', 'YearBuilt']
X = data[cols_to_use]

# Select target
y = data.Price

# Separate data into training and validation sets
X_train, X_valid, y_train, y_valid = train_test_split(X, y)
```

</details>

この例では**XGBoost**ライブラリを使用します。**XGBoost**は**extreme gradient boosting**の略で、パフォーマンスと速度に重点を置き、いくつかの追加機能を備えた勾配ブースティングです。(scikit-learnには勾配ブースティングの別のメソッドがありますが、XGBoostには技術的ないくつかの利点があります。)

次のコードではscikit-learn APIのXGBoostをインポートします(**[xgboost.XGBRegressor](https://xgboost.readthedocs.io/en/latest/python/python_api.html#module-xgboost.sklearn)**)。これにより、scikit-learnの場合と同じようにモデルを構築して適合できる様になります。出力を見るとわかるように、`XGBRegressor`クラスには多くの調整可能なパラメータがあります。これについては後ほど説明します。

```python
from xgboost import XGBRegressor

my_model = XGBRegressor()
my_model.fit(X_train, y_train)
```

<details><summary>console</summary>

```console:console
XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.300000012, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=100, n_jobs=0,
             num_parallel_tree=1, predictor='auto', random_state=0, reg_alpha=0,
             reg_lambda=1, ...)
```

</details>

そして、予測を行い、モデルを評価します。

```python
from sklearn.metrics import mean_absolute_error

predictions = my_model.predict(X_valid)
print("Mean Absolute Error: " + str(mean_absolute_error(predictions, y_valid)))
```

```console:console
Mean Absolute Error: 241041.5160392121
```

## パラメータ調整
XGBoostには、精度とトレーニング速度に大きな影響を与える可能性のあるパラメータがいくつかあります。最初に理解しておくべきパラメータは次のとおりです:

#### n_estimators
`n_estimators`は上記で説明したサイクルを何回繰り返すかを指定します。これはアンサンブルに含めるモデルの数と等しくなります。

* 値が小さすぎると過小学習が発生し、トレーニングデータとテストデータの両方で予測が不正確になります
* 値が大きすぎると過剰学習が発生し、トレーニングデータに対する予測値は正確になりますが、テストデータに対する予測値は不正確になります(これが私たちが気にしている点です)

一般的な値の範囲は100~1000ですが、これは以下で説明する`learning_rate`パラメータに大きく依存します。

次のコードはアンサンブル内のモデルの数を設定するものです:

```python
my_model = XGBRegressor(n_estimators=500)
my_model.fit(X_train, y_train)
```

<details><summary>console</summary>

```console:console
XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.300000012, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=500, n_jobs=0,
             num_parallel_tree=1, predictor='auto', random_state=0, reg_alpha=0,
             reg_lambda=1, ...)
```

</details>

#### early_stopping_rounds
`early_stopping_rounds`は`n_estimators`の理想的な値を自動的に見つける方法を提供します。早期停止により、`n_estimators`が最後まで達してなくても、検証スコアの改善が止まると、モデルは反復を停止します。`n_estimators`に高い値を設定し、`early_stopping_rounds`を使用して反復を停止する最適なタイミングを見つけるのが賢明です。

ランダムな偏りによって、検証スコアが改善されないラウンドが発生することがあるため、停止する前に、それを許容するラウンド数を指定する必要があります。`early_stopping_rounds=5`に設定するのが妥当です。この場合、検証スコアが５ラウンド連続して低下した後に停止します。

`early_stopping_rounds`を使用する場合は、検証スコアを計算するためにいくつかのデータを確保しておく必要もあります。これは`eval_set`パラメータを設定することによって行われます。

上記の例コードを修正して、early_stopping_roundsを含めたコードは次の様に書けます:

```python
my_model = XGBRegressor(n_estimators=500)
my_model.fit(X_train, y_train, 
             early_stopping_rounds=5, 
             eval_set=[(X_valid, y_valid)],
             verbose=False)
```

<details><summary>console</summary>

```console:console
/opt/conda/lib/python3.7/site-packages/xgboost/sklearn.py:797: UserWarning: `early_stopping_rounds` in `fit` method is deprecated for better compatibility with scikit-learn, use `early_stopping_rounds` in constructor or`set_params` instead.
  UserWarning,
XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.300000012, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=500, n_jobs=0,
             num_parallel_tree=1, predictor='auto', random_state=0, reg_alpha=0,
             reg_lambda=1, ...)
```

</details>


後で全てのデータを使用してモデルを適合させたい場合は、早期停止で実行したいときに最適と思われる値を`n_estimators`を設定します。

#### learning_rate
各構成モデルからの予測を単純に加算して予測を得るのではなく、各モデルからの予測値を小さな数値(**learning rate(学習率)** と呼ばれる)で乗算してから追加することができます。

つまり、アンサンブルに追加する各ツリーが与える影響を小さくすることができます。したがって、過剰適合することなく、`n_estimators`に高い値を設定できます。早期停止を用いることで、適切なツリーの数が自動的に決定されます。

一般的に、学習率(learning rate)が小さく、推定子の数(n_estimatorsで指定した値)が多いと、より正確なXGBoostモデルが生成されますが、サイクルを通じて反復回数が増えるため、モデルのトレーニングにかかる時間も長くなります。XGBoostはデフォルトでは`learning_rate=0.1`となっています。

上記の例コードを修正して学習率を変更すると、次のコードの様に書けます:

```python
my_model = XGBRegressor(n_estimators=1000, learning_rate=0.05)
my_model.fit(X_train, y_train, 
             early_stopping_rounds=5, 
             eval_set=[(X_valid, y_valid)], 
             verbose=False)
```

<details><summary>console</summary>

```console:console
XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.05, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=1000,
             n_jobs=0, num_parallel_tree=1, predictor='auto', random_state=0,
             reg_alpha=0, reg_lambda=1, ...)
```

</details>

#### n_jobs
実行時間が考慮されるような大規模なデータセットでは、並列処理を使用してモデルをより早く構築できます。一般的には、パラメータ`n_jobs`をマシンのコア数と同じ値に設定します。データセットが小さい場合、これは役に立ちません。

結果として得られるモデルはそれほど改善されないため、フィッティング時間を微調整することは通常は無駄になるだけです。しかし、大規模なデータセットで、fitコマンドの実行中に長い時間待機しなければならない場合に便利です。

変更された例コードは次の通りです。

```python
my_model = XGBRegressor(n_estimators=1000, learning_rate=0.05, n_jobs=4)
my_model.fit(X_train, y_train, 
             early_stopping_rounds=5, 
             eval_set=[(X_valid, y_valid)], 
             verbose=False)
```

<details><summary>console</summary>

```console:console
XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.05, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=6, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=1000,
             n_jobs=4, num_parallel_tree=1, predictor='auto', random_state=0,
             reg_alpha=0, reg_lambda=1, ...)
```

</details>

## まとめ
[XGBoost](https://xgboost.readthedocs.io/en/latest/)は標準的な表形式のデータ(画像や動画などの特殊なタイプのデータではなく、Pandas DataFramesに保存できるタイプのデータ)を扱うための代表的なソフトウェアライブラリです。慎重にパラメータを調整することで、非常に正確なモデルをトレーニングできます。