【kaggle】特徴量エンジニアリング - ターゲットエンコーディング【備忘翻訳】

# はじめに
当記事はkaggleのLearnの[Feature Engineering](https://www.kaggle.com/learn/feature-engineering)の[Target Encoding](https://www.kaggle.com/code/ryanholbrook/target-encoding)を翻訳して備忘としたものです。
拙い英語力なので間違い等あればご指摘いただけたらと思います。

まとめ:[【kaggle】翻訳記事まとめ【備忘翻訳】](https://qiita.com/leafeon00000/items/71e57af5b2d9b0b7c846)
前:[【kaggle】特徴量エンジニアリング - 主成分分析【備忘翻訳】](https://qiita.com/leafeon00000/private/8f35535b71e326ead856)
次:おわり

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

----

# ターゲットエンコーディング
この強力なテクニックを使用して、あらゆるカテゴリ特徴量を強化します。

## イントロダクション
このコースで見てきたテクニックのほとんどは数値的な特徴に関するものでした。このレッスンで取り上げる手法であるターゲットエンコーディングは、カテゴリ特徴量を対象としています。これは、one-hotエンコーディングやラベルエンコーディングのように、カテゴリを数値としてエンコードする方法ですが、ターゲットを使用してエンコーディングを作成するという違いがあります。これは、いわゆる教師ありエンジニアリングの手法です。

<details><summary>setup</summary>

```python
import pandas as pd

autos = pd.read_csv("../input/fe-course-data/autos.csv")
```

</details>

## ターゲットエンコーディング
**ターゲットエンコーディング**とは、特徴量のカテゴリをターゲットから派生した数値に置き換えるあらゆる種類のエンコーディングです。

シンプルで効果的なバージョンは、レッスン3で学んだ平均のようなグループ集計を適用することです。自動車データセットを使用して、各自動車メーカーの平均価格を計算します:

```python
autos["make_encoded"] = autos.groupby("make")["price"].transform("mean")

autos[["make", "price", "make_encoded"]].head(10)
```

| |make|price|make_encoded|
|-----|-----|-----|-----|
|0|alfa-romero|13495|15498.333333|
|1|alfa-romero|16500|15498.333333|
|2|alfa-romero|16500|15498.333333|
|3|audi|13950|17859.166667|
|4|audi|17450|17859.166667|
|5|audi|15250|17859.166667|
|6|audi|17710|17859.166667|
|7|audi|18920|17859.166667|
|8|audi|23875|17859.166667|
|9|bmw|16430|26118.750000|

この種のターゲットエンコーディングは、**平均エンコーディング**と呼ばれることもあります。バイナリターゲットに適用される場合、**ビンカウント**とも呼ばれます。

## 平滑化(Smoothing)
ただし、このようなエンコーディングにはいくつかの問題があります。まず、`未知のカテゴリー`です。ターゲットエンコーディングは、過剰学習の特別なリスクがあり、独立した「エンコーディング」スプリットでトレーニングする必要があります。エンコーディングを将来のスプリットに結合するとき、Pandasはエンコーディングのスプリットに存在しないカテゴリの欠損値を埋めます。これらの欠損値は、何らかの方法でインプットする必要があります。

2つ目は`珍しいカテゴリー`です。あらゆるカテゴリがデータセット内で数回しか出現しない場合、そのグループで計算される統計はそれほど正確でない可能性があります。自動車データセットでは、`mercury`という名前は1回だけ出現します。私たちが計算した「平均」価格は、その1台の車両価格に過ぎず、将来登場する可能性のあるすべてのマーキュリーをあまり代表していない可能性があります。ターゲットエンコーディングの希少なカテゴリにより、過剰学習が発生する可能性が高くなります。

これらの問題を解決するには、**平滑化**を追加します。アイデアは、カテゴリ内平均と全体平均を組み合わせることです。まれなカテゴリはカテゴリ平均にあまり重みづけされず、欠落しているカテゴリは全体の平均の身になります。

疑似コードでは:
> encoding = weight * in_category + (1 - weight) * overall

ここで、`weight`はカテゴリ頻度から計算された0~1の間の値です。

`weight`の値を決定する簡単な方法は、 **`m推定値(m-estimate)`** を計算することです。

> weight = n / (n + m)

ここで、`n`はデータ内でそのカテゴリが出現する合計回数です。パラメータ`m`は「平滑化係数」を決定します。`m`の値が大きいほど、全体的な推定値の重みが大きくなります。

![1uVtQEz.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547651/9b07dc21-6365-f8d7-0992-b3ed3abeace4.png)

自動車データセットには、`chevrolet`というメーカーの車が3台あります。`m=2.0`を選択した場合、`chevrolet`カテゴリーは、chevroletの平均価格の60%と全体の平均価格の40%でエンコードされます。

> chevrolet = 0.6 * 6000.00 + 0.4 * 13285.03

`m`の値を選択するときは、カテゴリにどの程度のノイズが含まれると予想されるかを考慮してください。車の価格はメーカーごとに大きく異なりますか？正確な見積もりを得るには大量のデータが必要ですか？もしそうなら、`m`にもっと大きな値を選択する方がいいかもしれません。各メーカーの平均価格が比較的安定している場合は、もっと小さな値でも問題ないかもしれません。

:::note info

#### ターゲットエンコーディングのユースケース
ターゲットエンコーディングは次の場合に最適です:

* **高カーディナリな特徴量**: カテゴリの数が多い特徴量はエンコーディングが困難になる場合があります。one-hotエンコーディングでは生成される特徴量が多すぎたり、ラベルエンコーディングなどの代替手段がその特徴量には適さない可能性があります。ターゲットエンコーディングは、特徴量の最も重要な特性(ターゲットとの関係)を使用して、カテゴリの数値を導出します。
* **ドメイン動機の特徴**: 過去の経験からたとえ特徴量メトリックでスコアが低くても、そのカテゴリカルナ特徴が重要であるはずだと思うかもしれません。ターゲットエンコーディングは、特徴量の真の情報価値を明らかにするのに役立ちます。

:::

## 例 - MovieLens1M
[MovieLens1M]()データセットには、MovieLens1Mウェブサイトのユーザーによる100万件の映画評価が含まれており、各ユーザーと映画を説明する特徴量があります。下の折り畳みで設定のコードがあります。

<details><summary>setup</summary>

```python
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import warnings

plt.style.use("seaborn-whitegrid")
plt.rc("figure", autolayout=True)
plt.rc(
    "axes",
    labelweight="bold",
    labelsize="large",
    titleweight="bold",
    titlesize=14,
    titlepad=10,
)
warnings.filterwarnings('ignore')


df = pd.read_csv("../input/fe-course-data/movielens1m.csv")
df = df.astype(np.uint8, errors='ignore') # reduce memory footprint
print("Number of Unique Zipcodes: {}".format(df["Zipcode"].nunique()))
```

</details>

```console:console
Number of Unique Zipcodes: 3439
```

3,000を超えるカテゴリを持つ`Zipcode`の特徴量は、ターゲットエンコーディングに適した候補であり、このデータセットのサイズ(100万桁以上)により、エンコーディングを作成するためにいくらかのデータを節約できます。

まず、ターゲットエンコーディングをトレーニングするために25%のsplitを作成します。

```python
X = df.copy()
y = X.pop('Rating')

X_encode = X.sample(frac=0.25)
y_encode = y[X_encode.index]
X_pretrain = X.drop(X_encode.index)
y_train = y[X_pretrain.index]
```

`scikit-learn-contrib`の`category_encoders`パッケージはm-estimateエンコーダーを実装しており、これを使用して`Zipcode`特徴量をエンコードします。

```python
from category_encoders import MEstimateEncoder

# Create the encoder instance. Choose m to control noise.
encoder = MEstimateEncoder(cols=["Zipcode"], m=5.0)

# Fit the encoder on the encoding split.
encoder.fit(X_encode, y_encode)

# Encode the Zipcode column to create the final training data
X_train = encoder.transform(X_pretrain)
```

エンコードされた値をターゲットと比較して、エンコードがどの程度有用であるかを確認してみましょう。

```python
plt.figure(dpi=90)
ax = sns.distplot(y, kde=False, norm_hist=True)
ax = sns.kdeplot(X_train.Zipcode, color='r', ax=ax)
ax.set_xlabel("Rating")
ax.legend(labels=['Zipcode', 'Rating']);
```

![__results___11_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547651/2ee68f15-a602-b8f3-4a5c-1034aa2a4374.png)

エンコードされた`Zipcode`の特徴量の分布は、実際の視聴率の分布にほぼ従っています。つまり、映画視聴者の視聴率は郵便番号ごとに十分に異なっており、これらのターゲットエンコーディングは有用な情報を捉えることができました。