メタモデル貢献度(MMC)は、モデルの予測がメタモデルに中立化された後のターゲットとの共分散です。同様に、ベンチマークモデル貢献度(BMC)は、モデルの予測がステーク加重のベンチマークモデルに中立化された後のターゲットとの共分散です。
これらの指標は、モデルの独自の成分がメタモデルの相関(BMCの場合はベンチマークモデル)にどのように貢献するかを示しています。モデルの予測をメタモデルまたはベンチマークモデルで中立化することにより、残りの直交成分のターゲットとの共分散がそのモデルの貢献です。
# https://github.com/numerai/numerai-tools/blob/master/numerai_tools/scoring.py
import numpy as np
import pandas as pd
from scipy import stats
def contribution(predictions: pd.DataFrame, meta_model: pd.Series, live_targets: pd.Series) -> pd.Series:
"""与えられた予測に関する与えられたメタモデルとの貢献相関を計算します。
その後、次の手順で貢献相関を計算します:
1. 各予測とメタモデルを順位付けし、順位を保持します
2. 各予測とメタモデルをガウス化します
3. 各予測をメタモデルに関して直交化します
4. 直交化された予測とターゲットを乗算します
引数:
predictions: pd.DataFrame - 評価する予測
meta_model: pd.Series - 評価対象のメタモデル
live_targets: pd.Series - 評価対象のライブターゲット
戻り値:
pd.Series - 予測の各列の結果の貢献相関スコア
"""
def rank(df: pd.DataFrame, method: str = "average") -> pd.DataFrame:
return df.apply(lambda series: (series.rank(method=method).values - 0.5) / series.count())
def tie_kept_rank(df: pd.DataFrame) -> pd.DataFrame:
# rank columns, but keep ties
return rank(df, "average")
def filter_sort_index(s1, s2, max_filtered_ratio=0.2):
ids = s1.dropna().index.intersection(s2.dropna().index)
# ensure we didn't filter too many ids
assert len(ids) / len(s1) >= (1 - max_filtered_ratio)
assert len(ids) / len(s2) >= (1 - max_filtered_ratio)
return s1.loc[ids].sort_index(), s2.loc[ids].sort_index()
def gaussian(df: pd.DataFrame) -> pd.DataFrame:
assert np.array_equal(df.index.sort_values(), df.index)
return df.apply(lambda series: stats.norm.ppf(series))
def orthogonalize(v, u):
return v - np.outer(u, (v.T @ u) / (u.T @ u))
# 予測、メタモデル、ターゲットをお互いにフィルタリングして並び替える
meta_model, predictions = filter_sort_index(meta_model, predictions)
live_targets, predictions = filter_sort_index(live_targets, predictions)
live_targets, meta_model = filter_sort_index(live_targets, meta_model)
# メタモデルと予測を順位付けして正規化して平均=0、標準偏差=1にします
p = gaussian(tie_kept_rank(predictions)).values
m = gaussian(tie_kept_rank(meta_model.to_frame()))[meta_model.name].values
# 予測をメタモデルに関して直交化します
neutral_preds = orthogonalize(p, m)
# ターゲットを中心化します
live_targets -= live_targets.mean()
# ターゲットと中立化された予測を乗算します
# 平均=0なので、これは共分散に相当します
mmc = (live_targets @ neutral_preds) / len(live_targets)
return pd.Series(mmc, index=predictions.columns)