CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: 広義の Target Encoding と Stacking は同じもの (と解釈できる)

おそらく、既に分かっている人には「知らなかったの?」とびっくりされる系の話なんだろうけど、今さら理解したので備忘録として残しておく。 結論から書くと、目的変数を用いた特徴量生成を広義の Target Encoding と定義した場合、Target Encoding と Stacking は同じものと解釈できる。 例えば、Target Mean Encoding は多項分布を仮定したナイーブベイズ分類器を用いた Stacking とやっていることは同じになる。 また、Target Encoding と Stacking が同じものであると解釈することで、周辺の知識についても理解しやすくなる。

Target Encoding について

Target Encoding は、データ分析コンペで用いられることがある特徴量生成 (Feature Extraction) の手法のこと。 一般的にはカテゴリ変数と目的変数について統計量を計算して、それを新たな特徴量として用いる。 統計量には平均値が使われることが多く、この点から平均値を使うものを Target Mean Encoding と限定して呼ぶこともある。

このエントリでは、上記のようにカテゴリ変数と目的変数、および関連する特徴量について統計量を扱うものを狭義の Target Encoding と定義する。 それに対し、目的変数を使った何らか (任意) の特徴量生成の手法を広義の Target Encoding と定義する。

きっかけについて

久しぶりにオライリーの「機械学習のための特徴量エンジニアリング」を読み返していたところ、以下のような記述があった。

5.2.2 ビンカウンティング

ビンカウンティングの考え方はとても簡単です。カテゴリ値をエンコードして特徴量として使用する代わりに、カテゴリごとに何らかの値を集計した統計量を利用します。カテゴリごとにター ゲットの値を集計して算出した条件付き確率は、そのような統計量の一例です。ナイーブベイズ分類器に精通している人はピンとくるはずです。なぜなら、ナイーブベイズ分類器では特徴量が互いに独立と考えてクラスの条件付き確率を求めたからです。

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

  • 作者: Alice Zheng,Amanda Casari,株式会社ホクソエム
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2019/02/23
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

上記でビンカウンティングの一例として挙げられている処理は Target (Mean) Encoding を指している。 そして、やっていることはナイーブベイズ分類器を使って計算した条件付き確率と同じ、とある。 これは、Target Mean Encoding がナイーブベイズ分類器を使った Stacking である、とも解釈できる。

確かめてみよう

念のため、実際にコードで確認してみよう。

使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.6
BuildVersion:   18G103
$ python -V                      
Python 3.7.5

下準備

下準備として必要なパッケージをインストールしておく。

$ pip install scikit-learn pandas

Python のインタプリタを起動する。

$ python

次のようなサンプルデータを用意する。 とあるフルーツの名前をカテゴリ変数の特徴量として、それが美味しいかについて二値のラベルがついていると解釈してもらえれば。

>>> import pandas as pd
>>> 
>>> data = {
...     'category': ['apple', 'apple',
...                  'banana', 'banana', 'banana',
...                  'cherry', 'cherry', 'cherry', 'cherry',
...                  'durian'],
...     'label': [0, 1,
...               0, 0, 1,
...               0, 1, 1, 1,
...               1],
... }
>>> 
>>> df = pd.DataFrame(data=data)
>>> df
  category  label
0    apple      0
1    apple      1
2   banana      0
3   banana      0
4   banana      1
5   cherry      0
6   cherry      1
7   cherry      1
8   cherry      1
9   durian      1

Target Mean Encoding の計算

単純な Target Mean Encoding では、カテゴリ変数ごとの目的変数の平均値を計算する。 つまり、以下のようになる。

>>> greedy_ts = df.groupby('category').agg({'label': 'mean'})
>>> pd.merge(df, greedy_ts, on='category', right_index=True)
  category  label_x   label_y
0    apple        0  0.500000
1    apple        1  0.500000
2   banana        0  0.333333
3   banana        0  0.333333
4   banana        1  0.333333
5   cherry        0  0.750000
6   cherry        1  0.750000
7   cherry        1  0.750000
8   cherry        1  0.750000
9   durian        1  1.000000

なお、上記のように学習データ全体を使った計算方法を Greedy TS と呼ぶ。 Greedy TS はリークが生じるため、本来は Target Encoding するときには避けた方が良い。 ただし、今回はリークの説明がしたいわけではないので気にしない。 気になる人は末尾の参考文献のブログエントリを読んでもらえれば。

多項分布を仮定したナイーブベイズ分類器を用いた Stacking

続いては多項分布を仮定したナイーブベイズ分類器を使って Stacking してみる。

まずは scikit-learn のモデルから使いやすいように、特徴量を One-Hot エンコードしておく。

>>> from sklearn.preprocessing import OneHotEncoder
>>> 
>>> encoder = OneHotEncoder(sparse=False)
>>> X = encoder.fit_transform(df[['category']])
>>> y = df.label.values

それぞれのフルーツごとに対応した次元ができる。

>>> X
array([[1., 0., 0., 0.],
       [1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
>>> y
array([0, 1, 0, 0, 1, 0, 1, 1, 1, 1])

多項分布を仮定したナイーブベイズ分類器を用意する。 Smoothing しないので alpha オプションには 0 を指定する。

>>> from sklearn.naive_bayes import MultinomialNB
>>> clf = MultinomialNB(alpha=0)

データ全体を学習させたら predict_proba() メソッドで推論する。

>>> clf.fit(X, y)
>>> y_pred_proba = clf.predict_proba(X)

得られた結果を、元のデータと連結してみよう。

>>> df.join(pd.Series(y_pred_proba[:, 1], name='y_pred_proba'))
  category  label  y_pred_proba
0    apple      0      0.500000
1    apple      1      0.500000
2   banana      0      0.333333
3   banana      0      0.333333
4   banana      1      0.333333
5   cherry      0      0.750000
6   cherry      1      0.750000
7   cherry      1      0.750000
8   cherry      1      0.750000
9   durian      1      1.000000

多項分布ナイーブベイズ分類器から得れた特徴量は、先ほど手作業で作った Target Mean Encoding の特徴量と一致している。

上記から、Target (Mean) Encoding と Stacking のつながりが見えてくる。 GBDT や NN などを用いた Stacking も、既存の特徴量と目的変数から新たな (メタ) 特徴量を作るという点で、広義の Target Encoding とやっていることは変わらない。 この点を理解することで、次のようなことを考えた。

Stacking で OOF Prediction する理由を説明しやすい

学習データ全体を使って Stacking するとリークが生じることが知られている。 この原理は、Target Encoding がリークを起こす仕組みと変わらない。 特徴量を付与する対象の行をモデルの学習データに含めることは、Target Mean Encoding で Greedy TS を計算するのと同じことになる。 もし Stacking でリークする理由がイメージしにくかったとしても、より単純な Target Mean Encoding を例に挙げれば理解しやすい。 それを防ぐ方法として Holdout TS (OOF Prediction) がある理由も分かりやすいはず。 これは Stacking が何段になっても、Target Encoding を複数回やっているのと同じことなので分割方法を使い回さなければいけない理由も直感的に理解できる。

コードを共通化できる可能性がある

これは Target Encoding で、こっちは Stacking というように、別々の概念としてコードを書く必要がなくなるかもしれない。 例えば目的変数を使う特徴量生成と、使わない特徴量生成くらいのざっくりした概念として扱えるとうれしい。 もしコードが共通化できるのであれば、パイプラインを作る観点で有用と考えられる。

それぞれで用いられている手法のお互いへの応用も可能では

両者が同じものだとすると、それぞれで用いられている手法を互いに応用できる可能性が出てくる。

例えば Target Encoding のリークを防ぐ手法として Ordered TS という計算方法が提案されている。 Target Encoding と Stacking が同一だとすれば、Ordered TS の計算方法を Stacking にも応用できるのではないか。 Ordered TS を用いると、Holdout TS よりもリークしにくいのに加えて、計算量の削減にもなると考えられる。

Holdout TS では、分割数を  k とした場合、計算量は  O((k - 1) N) になる。 それに対し、Ordered TS では  O(N) になるはずなので。

ただ Ordered TS は履歴が十分に貯まるまでポンコツな結果が出てしまう問題があるので、実用的かどうかは分からない。

Stacking と Target Mean Encoding で上位にくるモデルの違いについて

Stacking では、一般的に下位の層には GBDT や NN といった表現力の高いモデルを用いる。 そして、上位の層では過学習を防ぐために線形モデルなど単純なモデルを使われることが多い。

それに対し、Target Mean Encoding を Stacking と解釈した場合、下位の層がナイーブベイズ分類器という単純なモデルになっている。 そのため上位には表現力の高い GBDT や NN が使われることになる。

このように、両者を同一視した場合、表現力によって上位と下位のモデルが組み合わせになっていることも納得できる。

参考文献

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

  • 作者: Alice Zheng,Amanda Casari,株式会社ホクソエム
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2019/02/23
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

Kaggleで勝つデータ分析の技術

Kaggleで勝つデータ分析の技術

  • 作者: 門脇大輔,阪田隆司,保坂桂佑,平松雄司
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/10/09
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

blog.amedama.jp

CatBoost: unbiased boosting with categorical features (PDF)