【kaggle】中級機械学習 - 交差検証【備忘翻訳】

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

まとめ:[【kaggle】翻訳記事まとめ【備忘翻訳】](https://qiita.com/leafeon00000/items/71e57af5b2d9b0b7c846)
前:[【kaggle】中級機械学習 - パイプライン【備忘翻訳】](https://qiita.com/leafeon00000/private/5c7f854304a19b9b6de3)
次:[【kaggle】中級機械学習 - XGBoost【備忘翻訳】](https://qiita.com/leafeon00000/private/334e84e10e0aaccc02ea)

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

----

# 交差検証(cross-validation)
モデルをテストするためのより良い方法

このチュートリアルでは、交差検証(cross-validation)を使用してモデルのパフォーマンスをより適切に測定する方法を学習します。

## イントロ
機械学習は反復的なプロセスです。

どのような予測変数を使用するか、どのようなタイプのモデルを死闘するか、それらのモデルにどのような引数を利用するかなど、様々な選択に直面することとなります。これまで、検証セットを使用してモデルの品質を測定することにより、データに基づいてこれらの選択を行ってきました。これまでは検証セットでモデルの品質を測定することによって、データ駆動でこれらの選択を行ってきました。

しかしこれらのアプローチにはいくつかの欠点があります。例えば、5,000行のデータセットがあると想像してください。通常、データの約20%、つまり1,000行を検証データセットとして保持します。しかし、この1,000行を使ってモデルのスコアを決定するにはランダムに偏っている可能性が残ります。つまり、このモデルは1,000行のセットでは正しく機能しても、別の1,000行では正しく機能しない可能性があります。

極端な場合、検証セットにほんの１行のデータしかないことも考えられます。複数のモデルを比較した場合、単一のデータポイントでどのモデルが最適な予測を行うかはほとんど運次第です。

一般的に、検証セットが大きいほど、モデル品質を測定した際におけるランダム性(いわゆる「ノイズ」)が小さくなり、より信頼性が高まります。残念ながら、大規模な検証セットを取得するにはトレーニングデータから行を削除する必要があります。トレーニングデータセットが小さいほど、モデルの品質が低下します!

## 交差検証(cross-validation)とは？
**交差検証(cross-calidation)** では、データの様々なサブセットに対してモデルプロセスを実行し、モデルの品質を複数の尺度で測定します。

例えば、まずデータを全体の20%ずつ、5つに分割します。この場合、データを5つの**Fold**と呼びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547651/f44e12cf-7da7-6c04-1abe-a5bd231bcd22.png)

次に、各Foldごとに1つずつ実験をします。
* 実験1では、最初のFoldを検証セットとして使用し、その他の全てをトレーニングデータとして使用します。これにより、20%のホールドアウト[^1]セットに基づいてモデルの品質を測定できます。
* 実験2では、2番目のFoldをホールドアウトします(2番目のFold以外の全てをモデルのトレーニングに使用します)。そして2番目のFoldを検証データとしてモデルの品質を測定します。
* このプロセスを繰り返し、各Foldをホールドセットとして使用していきます。これらをまとめると、データの100%をホールドアウトとして使用した時点で、データセットの全ての行に基づいたモデルの品質が得られます(全ての行を同時に使用しない場合でも)。

[^1]:今まで見てきたようにトレーニングデータと検証データを固定して予測する様データセットの分割方法

## どんな時にcross-validationを使うべきなのか？
cross-validationはモデルの品質をより正確に測定することができます。これはモデリングに関する多kの決定を行う場合に特に重要です。ただし、複数回モデルを実行するため(各Foldごとに1回ずつ)、実行に時間がかかる場合があります。

では、これらのトレードオフを考慮すると、それぞれのアプローチはいつ使用すればいいのでしょうか？

* 追加の計算負荷が大きな障害にならない*小さなデータセット*の場合は、cross-validationを実行するべきでしょう。
* *より大きなデータセット*の場合は、単一の検証セットで十分です。コードの実行速度が速くなり、ホールドアウトのために一部のデータセットを再利用する必要はほとんどない可能性があるからです。

大規模なデータセットと小規模なデータセットを構成する単純な基準はありません。ただし、モデルの実行に数分かそれ以下しかかからない場合は、cross-validationに切り替える価値があるでしょう。

あるいは、cross-validationを実行して、各実験のスコアが近いかどうかを確認することもできます。各実験で同じような結果が得られる場合は、単一の検証セットで十分であると考えられます。

## 例
前回のチュートリアルと同じデータを使用します。入力データを`X`に読み込み、出力データを`y`に読み込みます。

<details><summary>データの読み込み</summary>

```python
import pandas as pd

# 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
```

</details>

次に、欠損値を補完しランダムフォレストモデルを使用するパイプラインを定義します。

パイプラインを使用せずにcross-validationを行うことは*可能ですが*非常に困難です。パイプラインを使用すると、コードが驚くほど簡単になります。

```python
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer

my_pipeline = Pipeline(steps=[('preprocessor', SimpleImputer()),
                              ('model', RandomForestRegressor(n_estimators=50,
                                                              random_state=0))
                             ])
```

sciikit-learnの`cross_val_score()`関数を使用してcross-validationスコアを取得します。foldの個数を`cv`パラメータにセットします。

```python
from sklearn.model_selection import cross_val_score

# Multiply by -1 since sklearn calculates *negative* MAE
scores = -1 * cross_val_score(my_pipeline, X, y,
                              cv=5,
                              scoring='neg_mean_absolute_error')

print("MAE scores:\n", scores)
```

```console:console
MAE scores:
 [301628.7893587  303164.4782723  287298.331666   236061.84754543
 260383.45111427]
```

`scoring`パラメータは、モデルの品質の尺度を選択します。この場合は負の平均絶対誤差(negative MAE)を選択しました。[scikit-learnのドキュメント](http://scikit-learn.org/stable/modules/model_evaluation.html)にはオプションの一覧が記載してあります。

*negative MAE*を指定するのは少し驚きです。scikit-learnでは測定値が高い数値の方が良いように定義されるのが慣習です。ここでnegativeの値を使用すると、その慣習との一貫性を保つことができますが、negative MAEは他の場所ではほとんんど聞いたことがありません。

通常、代替モデルを比較するには、モデルの品質を測る単一の尺度が必要です。したがって実験全体の平均をとります。

```python
print("Average MAE score (across experiments):")
print(scores.mean())
```

```console:console
Average MAE score (across experiments):
277707.3795913405
```

## まとめ
cross-validationを使用すると、モデルの品質をより正確に測定でき、コードを綺麗に保てるという利点もあります: トレーニングセットと検証セットを別々に管理する必要がなくなったことに注意してください。したがって、小さなデータセットの場合は特に、良い改善となります!