【kaggle】中級機械学習 - パイプライン【備忘翻訳】

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

まとめ:[【kaggle】翻訳記事まとめ【備忘翻訳】](https://qiita.com/leafeon00000/items/71e57af5b2d9b0b7c846)
前:[【kaggle】中級機械学習 - カテゴリ変数【備忘翻訳】](https://qiita.com/leafeon00000/private/e23aca220cb83168db4f)
次:[【kaggle】中級機械学習 - 交差検証【備忘翻訳】](https://qiita.com/leafeon00000/private/d33102acb7784c6ba1e3)

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

----

# パイプライン
前処理を伴う複雑なモデルをデプロイ(さらにはテスト)するには不可欠なスキルです。

このチュートリアルでは**パイプライン**を使ってモデルを綺麗に作成する方法を学びます。

## イントロ
**パイプライン**は簡単にデータの前処理とモデルのコーディングを整理することができます。具体的には、パイプラインは前処理とモデリングのステップをまとめるため、全体を1つのステップのように使用できます。

多くのデータサイエンティストはパイプラインを使用せずにモデルを組み立てますが、パイプラインには以下のような重要な利点があります。

1. **綺麗なコード**: 前処理の各ステップでデータを記録するのは面倒な作業になることがあります。パイプラインを使用すると、各ステップでトレーニングデータと検証データを手動で追跡する必要がなくなります。
1. **バグの減少**: ステップを誤って適用したり、前処理のステップを忘れたりする機会が少なくなります。
1. **本運用化が容易**: モデルをプロトタイプから大規模に展開可能なものへ移行するのは、とても難しい場合があります。ここでは関連する多くの懸念事項については説明しませんが、パイプラインが役立ちます。
1. **モデル検証のその他のオプション**: 次のチュートリアルでは、相互検証の例を紹介します。

## 例
前回のチュートリアルと同様に、メルボルンの住宅データセットを使用します。

データの取り込み手順については特に触れません。`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')

# Separate target from predictors
y = data.Price
X = data.drop(['Price'], axis=1)

# Divide data into training and validation subsets
X_train_full, X_valid_full, y_train, y_valid = train_test_split(X, y, train_size=0.8, test_size=0.2,
                                                                random_state=0)

# "Cardinality" means the number of unique values in a column
# Select categorical columns with relatively low cardinality (convenient but arbitrary)
categorical_cols = [cname for cname in X_train_full.columns if X_train_full[cname].nunique() < 10 and 
                        X_train_full[cname].dtype == "object"]

# Select numerical columns
numerical_cols = [cname for cname in X_train_full.columns if X_train_full[cname].dtype in ['int64', 'float64']]

# Keep selected columns only
my_cols = categorical_cols + numerical_cols
X_train = X_train_full[my_cols].copy()
X_valid = X_valid_full[my_cols].copy()
```

</details>

`head()`メソッドで、トレーニングデータを見てみましょう。データにはカテゴリデータと欠損値のある列両方が含まれていることに注意してください。パイプラインを使用すると、どちらも簡単に処理できます。

```python
X_train.head()
```

| |Type|Method|Regionname|Rooms|Distance|Postcode|Bedroom2|Bathroom|Car|Landsize|BuildingArea|YearBuilt|Lattitude|Longtitude|Propertycount|
|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|
|12167|u|S|Southern Metropolitan|1|5.0|3182.0|1.0|1.0|1.0|0.0|NaN|1940.0|-37.85984|144.9867|13240.0|
|6524|h|SA|Western Metropolitan|2|8.0|3016.0|2.0|2.0|1.0|193.0|NaN|NaN|-37.85800|144.9005|6380.0|
|8413|h|S|Western Metropolitan|3|12.6|3020.0|3.0|1.0|1.0|555.0|NaN|NaN|-37.79880|144.8220|3755.0|
|2919|u|SP|Northern Metropolitan|3|13.0|3046.0|3.0|1.0|1.0|265.0|NaN|1995.0|-37.70830|144.9158|8870.0|
|6043|h|S|Western Metropolitan|3|13.3|3020.0|3.0|1.0|2.0|673.0|673.0|1970.0|-37.76230|144.8272|4217.0|

それでは3つのステップでパイプラインを構築していきます。

### ステップ1: 前処理を定義する
パイプラインが前処理とモデリングのステップをまとめるのと同様に、`ColumnTransformer`クラスは次の様な様々な前処理のステップをまとめます。
* 欠損値に数値データを補完し、
* カテゴリデータの欠損値をone-hotエンコーディングを適用して補完します

```python
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder

# Preprocessing for numerical data
numerical_transformer = SimpleImputer(strategy='constant')

# Preprocessing for categorical data
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

# Bundle preprocessing for numerical and categorical data
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numerical_transformer, numerical_cols),
        ('cat', categorical_transformer, categorical_cols)
    ])
```

### ステップ2: モデルの定義
次に、おなじみ`RandomForestRegressor`クラスでランダムフォレストモデルを定義します。

```python
from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor(n_estimators=100, random_state=0)
```

### ステップ3: パイプラインの作成と評価
最後に、`Pipeline`クラスを使って、前処理とモデルのプロセスをまとめます。注意点がいくつかあります:
* パイプラインを使用すると、トレーニングデータを前処理し、1行のコードでモデルを適合させることができます。(パイプラインを使わない場合は、補完、one-hotエンコーディング、モデルトレーニングを別々のステップで実行する必要があります。数値の変数とカテゴリ変数の両方を扱う場合は、これは特に面倒になります!)
* パイプラインでは、`X_valid`内の未処理の特徴量を`predict()`コマンドで処理し、予測を生成する前に特徴量を自動的に前処理します。(パイプラインがなければ、予測を行う前に検証データを前処理する必要があることを覚えておく必要があります。)

```python
from sklearn.metrics import mean_absolute_error

# Bundle preprocessing and modeling code in a pipeline
my_pipeline = Pipeline(steps=[('preprocessor', preprocessor),
                              ('model', model)
                             ])

# Preprocessing of training data, fit model 
my_pipeline.fit(X_train, y_train)

# Preprocessing of validation data, get predictions
preds = my_pipeline.predict(X_valid)

# Evaluate the model
score = mean_absolute_error(y_valid, preds)
print('MAE:', score)
```

```console:console
MAE: 160679.18917034855
```

## まとめ
パイプラインは、機械学習のコードを綺麗に保ち、エラーを回避するのに役立ち、高度な前処理を伴うワークフローで特に役立ちます。