Numerai

Numerai新データにおける分類モデルによる順位予測

この記事はNumerai Advent Calendar 17日目の記事です。

2021年9月にNumeraiで新データがリリースされました。本記事では、新データの簡単な理解と、kaggleのコンペで使用されていた分類器を用いた順位予測をNumeraiのデータに適用した結果をまとめていきます。(ついでに新規モデルも作っちゃいます)

新規データの期間とターゲットについて

公式ディスカッションより

最初に新規データですが、roundが異なる同一時期のデータが別eraとして提供されるようになりました。これに対してどのようにモデルを作成するかは、一つの差別化ポイントになるかと思いますが、実験コストが大きくなる割りに、あまり情報量が増えていないのではないかと感じるので、一旦重複を許さずモデルを構築する方針です。

重複を許さずデータを取得する方法などはhabakanさんのこちらのnotebookが参考になりました(感謝)

検証データに対する予測性能は、Numeraiのdiagnosticsという機能が追加され、Webもしくはapiで提出することで、下記のような様々な指標の評価が可能になりました。下記は重複を許していますが、4era毎のフォーマットで提出しても同じように性能を確認できます。

フォワードの成績が一番良いTY_3のdiagnostics(NNとGBDTのアンサンブル)

ただ、何度もweb上で確認するのは面倒なので、手元で評価ができると実験がしやすくなります。公式でも公開されていると思いますが、katsuさんのnotebookだと必要な入力と出力のイメージが付きやすいかと思います(感謝)

続いて、ターゲットです。

新規データでは、スコアリングされないターゲットが追加され、計20種のターゲットが与えられています。大きく分けると、1カ月後(target_hoge_20)と3カ月後(target_hoge_60)の上下?を表す値で[0, 0.25, 0.50, 0.75, 1.0]の離散値で与えられています。

予測したいターゲットはtarget_nomi_20(=target)となります。

target_nomi_20とその他のターゲットの積率相関の推移を確認します。

学習データ期間におけるtarget nomi_20との相関推移

検証データ期間におけるtarget nomi_20との相関推移

diagnosticの結果と照らし合わせると、大体target20系の相関が下振れしている時に予測が難しくなっているように思います。一部の時期では、相関が逆に増減していたりするので、このあたりは複数ターゲットによる利点があるかもしれません。

また、欠損があるターゲットもあるので注意が必要です。下記は学習データ、検証データにおける欠損を持つレコード数ですが、target60系の方が欠損が多く、割合も検証データの方がやや多くなっています。

学習、検証データにおけるターゲット毎の欠損レコードの割合

スコアは、ターゲットと予測値を順位付けした値の積率相関で計算されます。

exampleコードを見ると、同率順位の場合、最初のものを低い順位とするランク処理を行われています。また、恐らくrank化せずに予測値を提出してもNumerai側で、下記の処理を実行されるため?、kaggleなどでよく行われる後処理によるスコア向上はできないように思います。

同一予測は意味なくランク付けされる

報酬が決まる相関係数は、予測値に上記のランク付け処理を行った配列と真値との積率相関で決まりますが、ターゲットの値の大小がスコアに与える影響(ターゲットが大きいものを当てた方が良いのか?など)は頭で考えただけではわかりません(数弱)

ということで、ターゲット0.25, 0.50, 0.75に対して、各値の予測値の精度を悪化させた場合に相関係数の値がどうなるかを確認します。

各予測値の誤差を変えた場合のスコアへの影響

0.5に対する分散を大きくした場合、相関係数が最も悪化していることがわかります。また、上記は各ターゲットの値の数量を同一にしていますが、実際は0.5のターゲットが全体の半分近くを占めるため、当然スコアへの寄与が大きくなりそうです。(誤っていれば教えてください)

上記が正しければ、これらを重点的に学習することでより良いモデルが構築できるかもしれません。

また、回帰タスクで一般的に使用される二乗誤差(MSE)で最適化する場合、誤差が大きい対象により影響を受けたモデルが作成されます。しかし、今回改善したいスコアは誤差が小さい対象でも、順番が正しくないとランク化した際に誤差が大きくなるため、単純にMSEで最適化するのが良いのかは疑問です。

MSEで最適化した場合の結果を確認

実験は下記の条件で行います。

・全データ4era毎に抽出(重複無し)

・early stopping用の検証データはNumeraiが提供しているもの

・評価は検証データで行う

・LightGBMを使う場合、feature fraction=0.5(適当)でモデルを構築

・MMCはexampleモデルの予測値で計算

まずは、iteartionの回数による各指標の値を検証します。

LightGBMで100 iterationの間にRMSEの改善が無ければ学習を打ち止める(early stoppingをした)場合のbest iterationと最適化したい指標(corr)がどのようになるかを確認します。

左図:corr vs best iteration, 右図:rmse vs best iteration

大体iterationが200回以内で終了していることがわかります。また、左図から、さらに大きなiterationでスコアを改善できるように思えます。

そこで、iterationを1000, 2000, 3000, 4000, 5000, 10000に設定して、異なる乱数で10回ずつ学習してみます。

左からcorr, mmc, sharpe,max_feature_exposure,RMSE, iteartionの値を示す

上記の結果からiterationの数を増加させることで、全体的に指標が改善していることがわかります。

しかし、iterationが大きくなるにつれてRMSEが悪化しており、検証データのRMSEの改善具合でモデルを構築しても最適化したい指標(corr, mmc)の改善に繋がらないことがわかります。

iterationを増やすということは、汎化性能を無視してひたすら学習データのMSEを改善させていることになりますが、上記の結果だとそれが最適化したい指標(corr, mmc)の改善に繋がっているということになります(何故…?)

余談ですが、LightGBMのdocumentのobjectiveの設定、RMSEとMSEが同じ引数として扱われるのは何故なのだろう・・・

LightGBMのdocument

話しは戻って、上記から、RMSEを用いたearly stoppingでは早期に学習が終了してしまい、アンダーフィットしたモデルになることがわかります。

exampleコードのようにiteration回数を決め打ちするのは、一つのやり方かもしれません。

しかし、例えば、iterationを固定して特徴量選択による予測精度への影響を確認したいケースを考えると、特徴量削減により必要なiteration数も減少していると思われるため、決め打ちで設定したiterationで評価を行うと、精度の変化がiterationの相対的な増加によるものなのか、特徴量削減によるものなのかを切り分けるのが難しくなりそうです。

毎回iterationを探索しても良いですが、実験コストが上がるため、なんとか適切に損失関数を定義して、データからiterationの回数を決めたくなります。

kaggleではどのように解かれているか?

kaggleでは、Google QUEST Q&A Labelingのコンペで順位相関が評価指標として使用されていました。

このコンペでは、同順位の予測値の後処理による工夫が一つのポイントであったようですが、Numeraiでは、上述したようにrank化されるため、後処理による改善は期待できなさそうです。

その他の上位の学習方法の工夫として、ターゲットの各値を、大小関係を表す二値分類に変換してloglossでモデルを構築後に、各予測値から最終的な予測値を復元する処理が行われていました。(すげぇ…)

2nd placeのターゲットを変換する工夫

Numeraiのターゲットも均等に与えられているため、各ターゲットをt>0, t>0.25, t>0.50, t>0.75で二値化して学習することで、上記と同様の枠組みで順位の最適化を行えそうです。

そこで、LightGBMでearly stoppingを300に設定し、各ターゲットに対して異なる乱数で10回モデルを構築した時の、best iterationとbinary loglossを確認します。

左からtarget(target>valueの二値)、best iteration、loglossの値を示す

best iterationの値は、学習ができているのかの目安として表示しています。RMSEを用いた時と比較して、ealry stoppingを使用しても、ある程度学習が行えていることがわかります。

解法通りに、最終予測値を算出して、各指標の値を計算してみます。

seed,corr,mmc,sharpe,max_drawdown,max_feature_exposureの値を示す

feature exposureが高いものの、回帰モデルと比較しても負けていない予測精度を実現できています。何よりデータからモデルの複雑度を決められる(early stoppingを使える)のは個人的に安心感があります。

下記は、NNのアーキテクチャの実験結果ですが、loglossの改善とcorrやsharpeの改善がある程度揃っており、損失関数の最適化により所望の評価指標の改善を実現できているように思います。

左からcorr, logloss, sharpeの値を表す

以上から、Numeraiデータを分類問題として解くことで、early stoppingを使用してモデルを構築できることがわかりました。上記手法は、Numeraiデータに限らず、その他金融データに幅広く適用できるかと思います。

なお、今回の結果は全て検証データに過学習している可能性もあるため、フォワードでは全然上手くいかないという結果もあり得ることには注意が必要です。

最後に

色々実験を行った結果、筆者の最終的なモデルは下記のようになりました。

記事を書く傍ら作成したモデル

MSEと異なり、全てのターゲットに対して同様に最適化が行えているため、MMCが高く出ている?のかと思います。

様々な条件で実験を行い、新規モデルを作成できましたが、結局exampleモデルに勝てないという悲しい現象は普通に起こり得るので、あまり期待せずにフォワードの結果を確認していこうと思います。

また、今回データに触れてみて、kaggleや仕事で扱うデータと比較すると、Numeraiのデータは乱数の違いによるバラツキが大きく、適切に評価をするのが難しく感じました。

乱数の違いだけでも各指標のバラツキが大きい

どう対処するのが正解かは、私もわかりませんが、乱数でたまたま結果が良いチャンピオンモデルを使用すると、手元の評価とギャップが生まれると予想されます。今回は、試行毎にseed averageした結果で評価を行っていましたが、フォワードの結果も確認する必要がありそうで、厳密に評価するのは結構大変そうです。

最後になりましたが、日々情報発信をしてくださる方には大変感謝しています。Numerai検定がボロボロの私でも、stakeするモデルを作成できているのは、完全に皆さんのおかげです・・・。

あまりコミュニティに貢献できていないと感じたので、今回書かせていただきましたが、記事を書くことがモチベーションとなり色々と実験ができたので、書いてみて良かったです。