Numeraiへどのように取り組んでいるのか(安穏の場合)
はじめに
この記事では、機械学習やプログラミングの素養が無い筆者が、「世界で最も難しいデータサイエンスコンペ」を自称しているNumeraiへ如何にして取り組んでいるのかの概要を書きます。
Numeraiが何かご存知無い方は、以下の記事を最初に読まれることをお勧めします。
機械学習による株価予測 はじめようNumerai/UKIさん
この記事では、
・筆者がこれまでに試した中で、淘汰されずに生き残った取り組み方法
・TCについての考えや取り組み
・最近取り組んでいること
などを紹介します。
この記事で述べる方法は正統的な機械学習の方法ではありません。どれも筆者の無手勝流です。筆者は機械学習をやっているわけではなく、ある種のオカルトを武器に戦っているのです。知識やスキルがなくてもできる内容と思いますが、理論的な裏付けがあるわけではないことだけはご注意ください。
最も基本的な戦略
素人がNumeraiで戦うのに最も基本的かつ重要な戦略は、「試行錯誤による選りすぐり」(蟲毒的な方法)です。
Numeraiでは複数(記事執筆段階で最大50)のモデルの提出を受け付けており、他のデータサイエンスコンペと異なり毎週の提出がずっと続きます。
このような仕組みなので、参加者は、「多数のモデルをためしに提出し、良かったものは残し、そうでないものは捨てる」という操作を繰り返すことが可能です。
理屈上、この操作を機械的にずっと続けていれば、手持ちのモデル全体(ポートフォリオ)としての成績は上がっていきます。
筆者は、平均すると月に2、3個くらいのペースでモデルを入れ替えしています。このとき、どのような要因で成績が良くなるのかということが掴めている事は必ずしも必要ありません。適当にモデルのバリエーションを作ってどんどん投下します。当然、良かったモデルの派生モデルを作ることはしますが、それの何が良かったのかの確信は無いのです。というのは、それぞれのモデルは後述する色々な要素の組み合わせでできており、きちんと切り分けしないこともあり、どの要素が効いているのかが確かめにくいからです。しかし、この操作を続けていると、効いている要素を持ったモデルは自然に生き残り、生き残ったモデルは派生が作られるので、良い要素はやがて勢力を拡大して多くのモデルで採用されるようになります。
人によっては、このような「やたらめったら当てずっぽう」なやり方に抵抗があるかもしれません。実際、数個以下しかモデルを提出していない参加者の方がむしろ多数派と思います。しかし、このやり方には、内容やモデルを構成する要素について理解や評価がはっきりしていなくても(なんならどんなモデルなのかスッパリ忘れてしまっても)、機械的な作業に落とし込んでポートフォリオの改善を継続していけるという利点があります。
このようなやり方が成立するところが、Numeraiが実は素人でも結構戦えることの大きな要因です。
CorrとTCの推移 50モデル平均 2021年12月~2022年10月末まで
使用している手法
そんなに沢山のモデルをどうやって作るのか、という事を疑問に思われるかもしれません。様々な機械学習の手法を知っていなくてはできないのではないかと。実際、Numerai参加者の中にはモデル名に機械学習の様々な手法の名前をつけている方も多いです。しかし筆者の場合、使っている手法は9割方がGBDT(XGBoostがメイン)です。GBDT系は、数行のサンプルコードをコピペすれば動きます。これを素人ながらの方法でこねくり回してモデルのバリエーションを作ります。
レジュームを使う方法
モデルのバリエーションを増やすための大枠の1つとして、モデル学習時に数epoch毎に学習を一旦止めて、データや設定等を変更し、再度続きから(レジュームして)学習を進めるということを行っています。(自分の取り組みのわりと中核です)
レジュームを使った学習では、切り替えにより学習曲線が以下のようにギザギザになります。
図から、色々切り替えても全体としては学習が進んでいることが確認できます。
学習曲線の例(赤:train 青:test)
欠点として、学習にとても時間がかかります。筆者だから成立していますが、おそらく常人には耐えられないと思います。
この方法によるモデルのバリエーションについて、筆者が行っている例をいくつか挙げます。
レジュームを使う例その1 トレーニングデータ
数epoch毎にトレーニングデータを切り替えます(分割して与えたり、乱数でシャッフルしたり)
狙いは、汎化性能の向上です。データ切り替えをすることにより汎化性のある信号のみの学習を進め、ノイズ部分のキャンセル(過学習の防止)をするという目論見です。
例えば、分割データ1で過学習した部分(ノイズ)は、次の分割データ2の学習時にはデータが切り替わって優位性が無くなるのでキャンセルされて、両方のデータに共通の汎化性のある信号だけの学習が進む、といった効果を期待しています。ただ、それで本当にそのような効果があるかどうかは不明です。効果がわかりにくいというのはオカルトが共通して持つ特徴です。
レジュームを使う例その2 ターゲット
数epoch毎にターゲットを切り替えると、それぞれを睨んだ形で(両立させて)学習が進みます。
Numeraiではスコアリングに使うもの(Nomi20)以外にも、ターゲットのバリエーションがいくつか運営から与えられています。例えば、Nomi20とJerome60というターゲットをアンサンブルすることがTCに良いとして推奨されていますので、最近はこれらを両立させて学習させたりしています。自分で加工したターゲットを使うこともあります。アンサンブルするのと何が違うのかは不明です。
レジュームを使う例その3 目的関数
数epoch毎に目的関数を切り替えて、異なる目的関数を両立させて学習させたりしています。
NumeraiではTC、Corrといった評価指標があります。例えば、TCを上げるのが狙いの目的関数とCorrを上げる為の目的関数を数epoch毎に切り替えることで、両方に対して学習させたりしています。ただ、実際やってみると、TC狙いの学習を進めるとCorrが下がってしまう場合が多いようです。これはTCの直感的な理解とも合っています。学習時に両者が綱引き状態になってしまうと学習が進みにくいので、例えばTC2回につきCorr1回等、適用する回数でTC優勢になるように調整しています。主にはTC狙いだけれど、両立できるところではCorrも取ろうという狙いです。
レジュームを使う例その4 Weight
学習時に設定するWeightの値を数epoch毎に切り替えるということをやっています。
例えば、間違いの多い部分のWeightを大きくする、あるいは逆に正しい部分を伸ばすなど。効果はよくわかりませんが、多くのモデルで使っています。益にも害にもならないのかもしれません。
レジュームを使う例その5 Early Stopping
切り替えと少し異なりますが、レジュームを使って逐一学習を停止/再開しますので、適当なepoch毎にモデルを保存してバックアップしておきます。最適なEarly Stoppingのタイミングを見極めることは筆者には難しいので、いくつかの段階でのモデルを抜き出して複数提出してしまいます。後述するようなデータにリークがあるような学習においては、学習曲線のtestのピークで判断するよりも早く止めた方が成績がよくなるような気配を感じており、そのような場合に有効です。
特徴量の加工
特徴量を加工して新しい特徴量を作れば、モデルのバリエーションが増やせます。しかし、むやみに特徴量を足したり引いたりよくわからない計算をしても、取り出せる情報は変わらない(むしろ劣化する)気もします。
あまりにも意味が無い操作は労力の無駄になりそうなので、極力、何かしらの意味付けができる操作をやるようにしています。ちょっと主張が矛盾しているように思われるかもしれませんが、オカルトにも信念があるのです。
生き残っているモデルで使われているものの中からいくつか例を挙げます。
ターゲットエンコーディング的な特徴量
データの中の取り出しにくい形の信号が、取り出しやすい形になることを狙って、ターゲットと絡めた(カンニング的な)特徴量作りをしています。筆者はこれを、「ターゲットエンコーディング的な特徴量」と呼んでいます。やたらと独自用語を作るのもオカルトの特徴です。
なお、カンニングなので、特徴量作成時に隔離したデータを使わないとリークします。個人的には、リークに気を付けて作った場合とそうでない場合であまり成績に差を感じていませんが、リークしていると学習時のTrainのスコアがやたらと高くなったり、early stoppingの止めどころが判断しにくくなったりするので、それを踏まえて使うことは必要と考えています。
ターゲットエンコーディング的な特徴量の例その1 ターゲットの平均
Numeraiの特徴量は5bin(0, 0.25, 0.5, 0.75, 1.0)です。各特徴量のそれぞれのbinに対して、ターゲットの平均を計算し、対応表(カンニングシート)を予め作成します。新しい特徴量では、その対応表の値を新しい特徴量にします。
例えば、特徴量Aが0.25のデータではターゲットの平均が0.55であったとします。この対応に基づき、特徴量Aが0.25になっているデータの新しい特徴量A’の値は全て0.55を入れます。
このようにして作った特徴量は、ある種、カンニングしたターゲットの予測のようなものです。例えば、特徴量Aのターゲットに対する効き方が線形でない場合でも、特徴量A’ではその非線形な部分が取り出しやすいとか、そんなことがあったりするかもしれません。
ターゲットエンコーディング的な特徴量の例その2 ターゲットと特徴量の差分の予測
同じ考え方で色々作れるということを示すため、もう1つ例を紹介します。
最初に、
予測差分 =(ターゲット)- (ある特徴量の値)
というものを定義して、一時的なターゲットとして特徴量毎に学習させます。
すると、特徴量の数だけこの「予測差分」を予測する為のモデルができます。
次に、これらのモデルを使って、各データに対して上記の「予測差分」を計算し、
(その特徴量の値) + (予測差分) = 予測ターゲット
として、この「予測ターゲット」を新しい特徴量にします。(予測した差分を元の特徴量に足せば、間接的にターゲットを予測していることになります)
ターゲットを使ってターゲットの予測をした値を特徴量にしていますので、その1と同じ構造です。
筆者は他にも似たような考えに基づく特徴量をいくつか作って投入しています。
データの周期性を使った特徴量
以前別の記事で紹介したことがあるのですが、Numeraiのデータは近い内容の特徴量が5周期並んだ構造になっています。この5周期は時系列のズレ(例えば1週間の5営業日)ではないかという指摘があります。もしそうであるなら、各周期の同じ位置にある特徴量同士を比較することで、時系列の変化の情報を拾える可能性があります。こちらも色々作っているのですが、例えば単純に各周期同士の差を取ったものを新しい特徴量にしたりといったことをやっており、それらを使ったモデルがまあまあ生き残ってます。
なお、Numeraiでは、Round毎に銘柄が匿名化されるので、時系列方向で銘柄が同定できません。このため、過去のRound(過去のera)のデータを使った時系列の特徴量を作ることが難しくなっています。銘柄毎ではなくグルーピングした特徴量などについてであれば、それらしきものを作れるので、これまでに色々作ってきました。しかし、それらはあまり成績が振るわず、殆ど淘汰されました。
TCへの取り組み
TCの直感的な説明(筆者の理解)
Numeraiでは、(執筆時現在は)CorrとTCという2つの評価指標に対して報酬が支払われます。
Corrはターゲットと予測の順位相関です。予測がターゲットに近ければハイスコアになります。
一方、TCはそのモデルの予測がメタモデル(参加者の予測が運営によって統合されたもの)とアンサンブルされたときに、メタモデルへ与える影響の良し悪しに対する評価です。
TCはメタモデルへの影響の良し悪しの評価なので、必ずしも予測が正しくなくても(ターゲットと相関していなくても)ハイスコアになり得ます。
例えば、ある銘柄について、メタモデルの予測順位が5000銘柄の中の1位だったけれど、実際の結果は10位だったとします。この場合、メタモデルの予測順位は実際より(僅かなズレですが)高すぎです。ですので、これを正しい方向に修正するには、実際より低い順位(例えば最下位)の予測をしているモデルのTCが高くなります。
高々モデル1つの予測の影響力ではメタモデルをわずかしか修正することができないので、上記の例のように、実際は10位なのに最下位という極端な予測をしたとしても、ズレは完全には修正されないと考えられます。そのため、程度はわかりませんが、メタモデルの間違いを正す方向であれば、より極端な予測の方がTCが高くなると思われます。また、予測銘柄全部の中で、メタモデルの間違いを正す方向の予測ができている銘柄数の割合いが高いほどTCが高くなると考えられます。つまり、TCが高い予測が持つ要素は以下の2つと考えられます。
①メタモデルの間違い(偏り)を正す方向にズレた予測をしている銘柄の割合が多いこと。
②間違いを正す方向のズレが大きいこと。
TCとCorrの関係
リーダーボードの全モデルの成績について、CorrRepとTCRepは多少ですが正の相関があります。上記にて、「メタモデルの間違いを正す」と書きましたが、メタモデルが間違うところにて、メタモデルよりも正しい予測をしていれば、間違いを正す予測の割合は多くなります。このため、CorrとTCはある程度相関するのだと考えられます。
TCの源泉
直感的ですが、TCの源泉は2種類に分けられると思います。
① メタモデルの間違い(特定の傾向で間違える性質があればそこを捉える)
② メタモデルが未知の予測(メタモデルが未だ引き出せていない特徴量の情報を見つける)
提供されるデータに含まれる情報は有限ですので、上記の源泉は有限であり、メタモデルが向上すれば減っていきます。payout自体はpayout factorや係数を変更して増やすこともできますが、TCの源泉となるアルファが少なくなるほど、それを見つけること自体が困難になりますし、S/N比が悪くなることで統計変動が大きく不安定になるのではないかと考えます。つまり、年々TCの安定した攻略が困難になるだろうと思われます。この点はCorrと大きく違うところだと思います。
先日イキってフォーラムに投稿したふわっとした概念図
TCを上げる為の目的関数
上述の通り、原理的には、自分のモデルの予測がメタモデルにアンサンブルされたときにメタモデルの予測を改善できればTCが高くなるはずです。
メタモデルの予測は執筆時現在入手不可ですが、運営提供のexample modelなど、メタモデルと相関の高いモデルの予測は自分で用意できます。
筆者はその代替的なメタモデルを使って、TCを上げることを目的とした目的関数を作成して使っています。
手順は以下の通りです。
1.代替的なメタモデルの予測を準備する。(新旧exampleの平均等)
2. 目的関数の中で、代替メタモデルと自分の予測をアンサンブル(加重平均)する。
3. GBDTの場合は、アンサンブルした予測とターゲットとの間で
1次、2次の勾配を計算する。(この部分は元のままで変更必要無し)
つまり、通常の勾配計算をする直前に単に混ぜるだけです。ただし、メタモデルを模して1/10000などの小さな重みでの加重平均にすると、本来のモデルの予想が殆ど完全に代替メタモデルに置き換わってしまって学習が進みにくくなります。そのため、学習に破綻の無い程度にモデル自身の予測の比率を大きくして混ぜています(数分の1等)。
この方法はTCに対して結構手応えを感じていますが、アンサンブルの比率が実際と異なるところで損をしているような気もします。
なお、現状、代替メタモデルとしてexample modelを使用していますが、メタモデルとより相関が高いモデルが用意できれば、この方法の精度が改善すると考えられます。メタモデルとの相関はliveでの再現性がよいので、いくつかのモデルでテストしながら強化学習的に相関が高くなるように調整していく方法も考えられます。
そのような取り組みは今後の課題ですが、一方で、メタモデルの予測自体が近々公開されるという話があるので、様子見状態となっています。
TC改善の為のターゲット(失敗している取り組み)
TC導入当初、代替メタモデルを利用する方法として、ターゲットを加工することをはじめに考えました。ターゲットと代替メタモデルの予想を比較して、修正すべき方向(代替メタモデルが間違えている方向)にターゲットを偏らせたものを新しいターゲットにするという考えです。しかし、この考えで作ったモデルは今のところ全然結果が出ておりません。今後の課題です。
最近の取り組み
Feature Importanceのコントロール
少数の特定の特徴量への依存が大きいと、バリアンスが大きくなるので良くないという話をよく聞きます。そこで、前述のレジュームを使う方法で、数epoch毎にFeature Importance(以下FI)を確認し、FIが大きい特徴量にハンデをつけながら学習を進めることで、特定の特徴量のFIが高くなるのを防止するような方法を試みています。例えばexample modelの予想に対してのFeature Neutralization(こちらのyaakuさんのブログ参照)をかけるとか、係数を調整しながら乱数を一時的に混ぜる(最終的には外す)といった方法で、FIの高い特徴量に一時的にハンデをつけてFIを下げます。
普通に学習したモデルのFI(降順)の分布 (一部の特定の特徴量のFIが高くなる)
FIのコントロールをしたモデルのFI(降順)の分布 (FIがわりと均一になります)
いくつかの仕組みを試作したので、今後liveで試していく予定です。
TB200的な学習Weight
TB200というのは、モデルの予測のTOP200、Bottom200のことです。Numeraiのメタモデルでは予測の上位200と下位200が投資対象銘柄となるらしく、その部分のみを切り取った評価が運営提供の診断プログラムで提供されたりしています。つまり、運営としてはTB200の予測が一番重要ということになります。
ただ、TB200は運営にとって重要ではあるけれど、CorrやTCといった評価指標自体にそれが組み込まれているというようなことではないようです。もしTB200に限定したスコアで評価すると、銘柄数が少なすぎてスコアのバラつきがとても大きくなるだろうと思います。
それで、これまではTB200に特に興味が無かったのですが、TCが導入されて少し考えが変わりました。というのは、予想には(提出されたらランク化されますが)分布があり、例えば、予測の上下部分を重視してモデルを学習するといった操作は、TCに影響があるかもしれないと考えたからです。分布の大部分は0.5付近に集中していて、多くのモデルはその部分の学習にリソースを割いているのでメタモデルもその部分の予想にある程度注力しているけれど、それが故に上下の中央とは性質が異なる部分の予想が疎かになっている傾向があって、上下部分だけに注力して学習すればメタモデルがカバーできていない部分を効率良く拾えるのではないかとか、あるいは逆に真ん中だけ注力してみるとなんか良いかもとか、そんな感じの妄想です。
予測の上下部分(一部分)を重視した学習をするのは、なりふり構わなければ簡単です。やはりレジュームを使う方法で、例えば目的関数の中で予測の上下部分以外を0.5に上書きしてしまうとかすれば、上下部分のみが評価されて学習が進みます。TB200に限定するとデータが少なすぎるので、3分割などがよいかもしれません。こちらもいくつか試作モデルを作成済みで、今後liveで試していく予定です。
Stake量の最適化
少し前に、PyPortfolioOptというライブラリを使って、各モデルへのstake量の最適化(ポートフォリオの最適化)の計算をやってみました。Staking3.0というものが将来導入されると、stake量の迅速な調整ができるようになるらしいので、そのときはこれを使ってみようかと思っています。
以下は以前試しに実行した結果です。payout factorを入れ忘れたので、実際の利益は期待値の1/3くらいと思われます。
なお、現状はstake量の配分は適当です。
おわりに
NumeraiはNMRベースではexampleを提出するだけでもプラス収支になりますが、NMR自体が余裕で一時期の数分の1とかになってますし、これが投資のメインにはなりにくいと私は思ってます。ただ、Numeraiをやっていて身に付けることができた取り組み方法や考え方などは、他の投資に応用できそうだという実感があり、今後、それらを使って他の投資にも挑戦していきたいと考えています。Numeraiも、TCなどは取り組み始めてまだ半年程度ですので、これからまだまだ改善していけるものと思っていますし、運営も儲かっているようなので、未来は明るい気がします。最後に、普段ツイッターなどでいいねやコメントをくださる方々にお礼を申し上げます。ありがとうございます。
俺たちの戦いはこれからだ!
◇追記
NMRで投げ銭いただける場合は以下にお願いします。
0x00000000000000000000000000000000000219c3
Discussion