01.4.2
Target / Mean Encoding
KFold(n_splits=5) で分けて自分の所属しない fold のデータだけで平均を計算する Out-of-Fold(OOF)の実装が必須です。このページでは category_encoders の TargetEncoder(smoothing=1.0) を KFold のループで回す書き方と、出現回数の少ないカテゴリを全体平均に寄せる平滑化、微小ノイズの注入という 3 つのリーク対策を扱います。概要 #
店舗ごとの売上を予測するとき、「その店の過去の平均売上」は非常に強い特徴量ですが、今日の売上を含めて平均を取ったら答えを写すことになります。Target Encoding はこの「カテゴリごとの目的変数の平均」を特徴量にする手法です。強力であるぶん、平均の計算に自分自身を入れないための KFold の工夫が結果を左右します。高次元なカテゴリを 1 列に落とし込みながら目的変数との結び付きを直接反映できる一方で、リーク(過大評価)との戦い が最大のテーマになります。
Pipeline 例(KFold + TargetEncoder) #
次のコードは、5 分割の交差検証の形で「元号」列を Target Encoding した 元号_te 列を作り、最後に推論用の encoder を全データで学習し直します。
| |
実装のポイント #
一つ目は Out-of-Fold で生成することです。学習に使った行で同じ統計値を使うとリークするため、KFold で分割し、検証側の行を「見ていないデータ」として推測する形にします。二つ目は平滑化(smoothing)を入れることで、出現回数が少ないカテゴリは全体平均に寄せます。smoothing=1.0 などを指定すると count / (count + smoothing) を重みとして混ぜてくれます。三つ目はノイズを加えることで、np.random.normal(0, 0.01) のような微小なノイズを 元号_te に足すと、木モデル+小規模データで「この値ならこのカテゴリ」と暗記する過学習を軽減できます。
リーク対策の比較 #
| 手段 | 目的 | 実装 Tip |
|---|---|---|
| KFold Out-of-Fold 生成 | 訓練行でのリーク防止 | n_splits=5 以上で安定化 |
| 時系列順の蓄積平均 | 未来情報を使わない | Ordered Target Statistics との比較を推奨 |
| 事前分布との平滑化 | 頻度の少ないカテゴリでの極端値を抑える | smoothing を count に応じて増減 |
| ノイズ注入 | 過学習の抑制 | np.random.normal(scale=0.01) などを加算 |
モデル投入時の検証ポイント #
最初に見るのはバリデーションスキームとの整合性で、Hold-Out / KFold / 時系列など、本番運用と同じスキームで Out-of-Fold を作成したかを確認します。時系列で予測するのに shuffle=True の KFold で OOF を作ると、未来の行の平均が過去の行に混ざります。二つ目は重要度のモニタリングで、元号_te の重要度(Gain / SHAP)が他の列より極端に高い場合はリークの可能性があり、交差検証スコアと Public/Private LB の差分を定期チェックします。三つ目はカテゴリごとの値域の確認で、df.groupby(cat_col)[f"{cat_col}_te"].agg(["count", "mean", "std"]) を出し、count が 1 桁のカテゴリで mean が全体平均から大きく外れていないかを見ます。
チェックリスト #
最初に確認したいのは、学習データと検証データが完全に分離された状態でエンコードしているかです。二つ目は、平滑化・ノイズを入れたバージョンと入れていないバージョンを交差検証で比較したかで、スコア差が小さければ smoothing=1.0 の既定で十分です。三つ目は推論時に使う TargetEncoder を保存し、再現性を担保したかで、OOF 用ではなく全データで fit した方を保存します。最後に、需要予測・行動予測など時系列依存が強い場合は Ordered Target Statistics も検討したかを見ます。
参考文献 #
- Micci-Barreca, Daniele. “A Preprocessing Scheme for High-Cardinality Categorical Attributes in Classification and Prediction Problems.” SIGKDD (2001).
- category_encoders.target_encoder.TargetEncoder