[UMP] Ubiquant Market Prediction コンペ振り返りと上位解法まとめ
どういうコンペ?
コンペのURLはココ
匿名特徴量を元に株価を予測するコンペです。データ量は大きいですが、シングルテーブルで比較的やりやすいコンペでした。Numerai Tournamentで使えるネタ拾いのために参加 → 39位(🥈)でした。
どういうデータ?
時系列のテーブルデータでした。
time_idがタイムポイント、investment_idが株のIDで、f0..300が匿名特徴量です。
評価関数は各time_IDのピアソン相関係数の平均値。
Baseline解法
LightGBMとKerasがほとんどでした。1位はTabNetを使用していましたが、自分はうまく扱えず挫折…
LightGBM
Keras
上位解法
1位と8位が勉強になる。
# 特徴量エンジニアリング
今回、メモリがカツカツのため、多くても100程度しか入りません。ただ、上位勢はカツカツになるまでfeatureを入れているようです。
- 各time_idごとの平均値(1位, 8位)
optiverではtargetの値でクラスタリングした際のいくつかの指標の平均値が使われていた(参考)が、今回はtargetと相関のある特徴について、各time_idごとの全体平均が使われていた。上位2人がやってるので、コレは結構聞いてそう。
→ time_idの平均値はマーケット全体の値(相場全体が上げ相場、下げ相場)を反映している、と解釈することができそうです。
1位のコードです。
features = [f'f_{i}' for i in range(300)]
corr = train_df[features[:] + ['target']].corr()['target'].reset_index()
corr['target'] = abs(corr['target'])
corr.sort_values('target', ascending = False, inplace = True)
best_corr = corr.iloc[3:103, 0].to_list()
time_id_mean_features = []
for col in tqdm(best_corr):
mapper = train_df.groupby(['time_id'])[col].mean().to_dict()
train_df[f'time_id_{col}'] = train_df['time_id'].map(mapper)
train_df[f'time_id_{col}'] = train_df[f'time_id_{col}'].astype(np.float16)
time_id_mean_features.append(f'time_id_{col}')
features += time_id_mean_features
- 移動平均線からの差分値(8位)
# 機械学習モデル
Publicノートと上位を見たところ、NNが4割、LGBが4割、TabNetが少し。
個人的にはTabNetはとても使いづらかった。。また、3rdはTransformerのみでブチ取ってます。
# アンサンブル、後処理
特に書いている人はいない。Target値が正規化されたものだったので、正規化してから平均値を取るとちょっとスコアがあがりました。
8位はブル相場(下げ相場)に対応して、正の値は少し小さく、負の値はより小さくするという後処理をしていた様子。
# その他(今回のコンペではあまり差がつかなかった要因)
- スケーリング
- データの水増し
- リーク
- 問題設定
- 過去コンペではランク学習を用いているものもありましたが、今回のコンペの上位では見当たらない。みんな普通に回帰していたと思います。
- クロスバリデーション
- 時系列なのですが、過去データからのLeakがないようで、個人的にはどのSplitでもOKでした。上位陣もCVの切り方は結構多様です(KFold, GroupKFold, Purged KFold, TimeseriesSplit)。
# その他(次回試してみたい)
-
lightgbmの
extra_trees=True
は次回試してみたい
# その他(個人的な改善ポイント)
-
アンサンブル用の後処理
- Target値が正規化されたものだったので、正規化してから平均値を取るとちょっとスコアがあがりました
-
外れ値の除外
- いくつか標準偏差×70倍みたいな外れ値があったので、それを除くとLightGBMもスコアがあがりました
-
ロバストにする仕組み
- investment_id, time_idはロバストさがなくなるのでは?と考え、全部の学習から除きました。
後は、色々試行錯誤した結果、time_idは全てを使わないとなぜかスコアが上がらないということに気が付きました。
→ 最初はCPCVとかでSplitしてたけど、結局KFoldに落ち着いた。Leakしてない特徴量を使っていたと思われます。
感想
-
テーブルデータでもNNがお強い。
- 一昔のNNがTransformerみたいな扱いになってる。
- ただ、NumeraiにNNモデルを投入してますが、LGBモデルに勝てておらず、難しさを感じます。。
-
特徴量エンジニアリングはやるべきであった。
- 思いつきはしたが、コンペに取り組んでるときは理解ができていなかったために取り込めてなかったですね。思いついたらやるべきだったかな。
-
PublicのLBに過学習した人が多かったというのもあり、シンプルなモデルで上位を取ってる人が多い印象でした。
- 自分もinvestment_idとtime_id落としてLGBとNNにぶっこんでアンサンブルして銀メダルだったので、かなり過学習してた人が多かったと思います(後は運もよかった。笑)
Discussion