新規参加者向けのTips
2015年に設立されたNumeraiは、毎週データサイエンスのトーナメントを開催しています。世界中のデータサイエンティストが、難読化されたデータを無料でダウンロードし、株式市場の予測モデルを構築することでトーナメントに参加できます。ダウンロードできるデータは難読化されたいるため、金融の知識がなくても参加することができます。
参加者は、自分の予測に賭け金をかけることでトーナメントに参加できます。良い予測を提出すると、より多くの仮想通貨(NMR)を得ることができ、悪い予測を提出すると、NMRを没収されます。
Numeraiは、メタモデルと呼ばれる参加者のモデルを統合したモデルを構築し、得られたデータを自社のグローバル・エクイティ・ヘッジファンドの取引に反映させています。
参加者は、賭け金の有無にかかわらずトーナメントに参加することができますが、NumeraiはNMRがステークされたモデルのみを使用します。参加者は自分でデータを用意する必要はありませんが、チームから与えられたデータを最適化し、自分の予測を提出することが求められます。
- 1.Numeraiのホームページと用語集の読み方
- 2.Numeraiに予測を提出する方法
- 3.モデル診断の読み方
- 4.Numeraiとコミュニティに関連する便利なリンク
NumeraiのホームページのURLは、https://numer.ai/tournament です。
Numeraire(NMR)トークン: NumeraireはERC-20ベースのトークンで、Numeraiへのステーキングに使用されます。
NMRは、Numeraiのトーナメントに参加したり、Numeraiに技術的な貢献をすることで獲得できます。NMRトークンは、Numeraiが構築されているErasure Protocolで使用することができます。現在、Erasureプロトコル上のアプリは、Numerai、Numerai Signals、Erasure Bayですが、プロトコルは誰でも構築できるようになっています。
Corr: 提出された予測データとターゲットと の相関係数を表します。
TC: 真の貢献度(TC)は、モデルの予測がNumeraiのメタモデルにとってどれだけ価値があるかを示します。
FNC: Feature neutral correlation (FNC)は、提出した予測データをNumeraiのすべての特徴量に対して中和した後の、ターゲットとの相関係数を表します。
Corr / TC / FNC Rep: Rep(評判)は、過去20ラウンドにおけるその指標の加重平均であり、リーダーボードでユーザーをランク付けするために使用されるものです。Repについての詳細はこちら
ステーク 自分のモデルの予測にどれだけ自信があるかを示すために、NMRをステーク(預入れ+ロック)をします。良い予測を提出した場合、NMRを得ることができます。一方、悪い予測を提出した場合はNMRが没収されます。賭け金の最小値は3NMRです。ステークせずに参加して、自分のモデルがどのようなパフォーマンスを示すかを知ってから、ステークするかどうかを決めることができます。
ペイアウト 賭けたNMRに応じて受け取る報酬。毎週(1ラウンドは4週間)、賭け金の最大25%を獲得または没収されます。ステーキングとペイアウトについての詳細はこちらをご覧ください。
DOCS: ルール、FAQ、新規ユーザーセクションを含むNumeraiのドキュメントへのリンクです。
CHAT: 告知、一般的な議論、データサイエンスなどのチャンネルを含むチャットスペースです。NumeraiチームはRocketChatに積極的に参加しており、プロフィールには
モデル このページでは、Numeraiに投稿するモデルを追加/削除できます。モデルの追加/削除をするには、「新しいモデルの追加」/「既存のアカウントの吸収」を押してください。 複数モデルのアカウントやアカウントの吸収についてはこちらをご覧ください。
設定: Eメール、パスワード、2段階認証(2FA)、APIキーの設定を行います。
アカウントがNMRを保持しているため、2FAによるセキュリティ強化を推奨します。2FAを使用する場合は、リカバリーコードを保存する必要があります。2FAのデバイスにアクセスできなくなっても、Numeraiはアカウントをリセットしません。

team
タグが付いています。チームメンバーは主に太平洋時間で活動しています。参加はこちらから。
FORUM: Numerai、データサイエンス、ステイク戦略などに関連する議論のためのスペースです。主にコミュニティ内での情報共有を目的として います。リンクはこちらです。
LEADERBOARD: Numeraiに投稿された予測結果のランキングです。ステーク量、Rep、TCなどでソート可能です。
アカウント: 「ウォレット」「モデル」「設定」「ログアウト」の4つのリンクがあります。
・ウォレット:NMRトークンの入金と出金が行えます。
入金(deposit):ウォレットに登録されているアドレスにNMRトークンを送ることで入金できます。このアドレスに他のトークンを送ることはできません。
出金(withdraw):NMRトークンを自分のNumeraiウォレット以外のアドレスに出金することができます。出金を依頼する前に、出金先の外部アドレスがNMR受け取りに適していることを確認してください。間違ったアドレスに送ったNMRは回収できません。



モデル情報: モデルのランキングやCorr・TCのRepなどの情報を掲載しています。ˇのボタンでモデルを切り替えることができます。
データ情報: 最新ラウンドのデータダウンロードリンクと、予測結果のアップロードリンクです。
ステーク: NMRのステーク量を調整できます。ステーク方法には、CorrやTCがあります。Corrではターゲットとの相関関係のみにNMRを賭けることができ、TCでは真の貢献にNMRを賭けることができます。

すぐにでもNumeraiに投稿したいという方は、大会参加者のkatsu1110さんとCarlo LepelaarsさんのKaggle Notebooksがとても参考になります。
今回の記事では、上記の記事や公式のサンプルモデルから一歩踏み込んだ説明(コードでどこを改善するかなど)をします。本説明により提出プロセスがわかりやすくなり、大会の出場者数が増えることを願っています。
今回紹介したコードは、Google Colab上で実行できます。Runボタンを押すと投稿ファイルが作成されますので、ぜひ使ってみてください。
Colabへのリンク
データセットはダウンロードリンクからダウンロードできます。ファイルを解凍すると、numerai_training_data.csv、numerai_tournament_data.csvなどのファイルがあることがわかります。numerai_training_data.csvはトレーニングデータを格納したcsvファイル、numerai_tournament_data.csvは検証用データを格納したcsvファイルです。
id: 暗号化された株のラベル
era: データが収集された期間のことです。eraが同じであれば、同じ期間にデータが収集されたことを意味します。
data_type: データの種類。trainはトレーニング用のデータ、validationは検証用のデータ、testはテスト用のデータ、liveは現在のラウンドのデータです。
feature: ビン化した特徴量。特徴量は0,0.25,0.5,0.75,1の5段階にビン化されています。また、特徴量は、次のようなラベルの付いたグループに分かれています。
"feature_intelligence"、"feature_wisdom"、"feature_charisma"、"feature_dexterity"、"feature_strength"、"feature_constitution"
target: ビン化された教師データ。また、ターゲットも0,0.25,0.5,0.75,1の5段階にビン化されています。numerai_training_data.csvではターゲットデータが与えられていますが、numerai_tournament_data.csvのテストデータとライブデータではNaNとなっています。

- 1.データ読み込み
- 2.特徴量エンジニアリング
- 3.機械学習
- 4.モデルの強さについて
- 5.予測結果が書き込まれたcsvファイルの作成
- 6.中和の方法
Carlo Lepelaars氏の記事から、データ読み込みの部分を引用(一部編集)します。
download_current_data (DIR)を呼び出し、最新のデータをDIRで指定したディレクトリにダウンロード後、train, val, test = load_data (DIR, reduce_memory = True)を呼び出すと、train, val, testのデータを別々に保存できます。
!pip install numerapi
import numerapi
NAPI = numerapi.NumerAPI(verbosity="info")
import numpy as np
import random as rn
import pandas as pd
import seaborn as sns
import lightgbm as lgb
import matplotlib.pyplot as plt
from scipy.stats import spearmanr, pearsonr
from sklearn.metrics import mean_absolute_error
import os
DIR = "/kaggle/working"
def download_current_data(directory: str):
"""
Downloads the data for the current round
:param directory: The path to the directory where the data needs to be saved
"""
current_round = NAPI.get_current_round()
if os.path.isdir(f'{directory}/numerai_dataset_{current_round}/'):
print(f"You already have the newest data! Current round is: {current_round}")
else:
print(f"Downloading new data for round: {current_round}!")
NAPI.download_current_dataset(dest_path=directory, unzip=True)
def load_data(directory: str, reduce_memory: bool=True) -> tuple:
"""
Get data for current round
:param directory: The path to the directory where the data needs to be saved
:return: A tuple containing the datasets
"""
print('Loading the data')
full_path = f'{directory}/numerai_dataset_{NAPI.get_current_round()}/'
train_path = full_path + 'numerai_training_data.csv'
test_path = full_path + 'numerai_tournament_data.csv'
train = pd.read_csv(train_path)
test = pd.read_csv(test_path)
# Reduce all features to 32-bit floats
if reduce_memory:
num_features = [f for f in train.columns if f.startswith("feature")]
train[num_features] = train[num_features].astype(np.float32)
test[num_features] = test[num_features].astype(np.float32)
val = test[test['data_type'] == 'validation']
test = test[test['data_type'] != 'validation']
return train, val, test
# Download, unzip and load data
download_current_data(DIR)
train, val, test = load_data(DIR, reduce_memory=True)
Numeraiデータセットの特徴量は互いに相関が低く、特徴量エンジニアリングを行わなくてもある程度の結果が得られます。
まず、訓練データを見てみると、大まかに6種類に分かれていることがわかります。
("feature_intelligence"、"feature_wisdom"、"feature_charisma"、"feature_dexterity"、"feature_strength"、"feature_constitution ") Carlo Lepelaars氏の論文からコードを引用しましたが、これらの特徴の平均値、偏差値、歪度などは有用な特徴です。
したがって、train = get_group_stats (train)を呼び出して、これらの特徴をtrainデータに追加します。
def get_group_stats(df: pd.DataFrame) -> pd.DataFrame:
for group in ["intelligence", "wisdom", "charisma", "dexterity", "strength", "constitution"]:
cols = [col for col in df.columns if group in col]
df[f"feature_{group}_mean"] = df[cols].mean(axis=1)
df[f"feature_{group}_std"] = df[cols].std(axis=1)
df[f"feature_{group}_skew"] = df[cols].skew(axis=1)
return df
train = get_group_stats(train)
val = get_group_stats(val)
test = get_group_stats(test)
メモリに余裕のあるPCであれば、特徴量の差分データや多項式特徴量などを含めると、良い結果が得られます。Google Colabで実行するとクラッシュしてしまうので、コードのみ掲載します。
from sklearn import preprocessing
ft_corr_list=['feature_dexterity7', 'feature_charisma18', 'feature_charisma63', 'feature_dexterity14']#Please try other features!
interactions = preprocessing.PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)
interactions.fit(train[ft_corr_list], train["target"])
X_train_interact = pd.DataFrame(interactions.transform(train[ft_corr_list]))
X_best_val_inter =pd.DataFrame(interactions.transform(val[ft_corr_list]))
X_best_test_inter =pd.DataFrame(interactions.transform(test[ft_corr_list]))
train=pd.concat([train,X_train_interact],axis=1)
val=val.reset_index().drop(columns='index')
val=pd.concat([val,X_best_val_inter],axis=1)
test=test.reset_index().drop(columns='index')
test=pd.concat([test,X_best_test_inter],axis=1)
Kaggleで使われているような特徴量エンジニアリングがNumeraiでもそのまま使えるので、train、val、testのデータを加工ことで、良いCorrやSharpe ratioが得られると思います。Numeraiで良い結果を得るために必要な作業の一つが特徴量エンジニアリングであることに間違いはありません。
Numeraiデータセットに機械学習を適用する際に考慮しなければならないのは
i) どのような機械学習手法を使用するか(LightGBM、XGBoost、ニューラルネットワークなど)
ii) どのようなハイパーパラメータを使用するか
iii) 予測結果をスタックするかどうか などです。
今回は、計算時間を考慮してLightGBMを使用します。訓練データ以外のid、era、data_typeは機械学習には必要ありません。残ったfeature_ ○○を説明変数として、targetを教師データとして学習します。学習したデータを用いて、valに含まれるValidationデータとLiveデータについても予測データを作成します。
i)~iii)を考慮すれば、Corr等の値が改善されるので、この部分もやりこみ要素の要素の一つです。
feature_list = train.columns.drop(['id','era','data_type','target'])
dtrain = lgb.Dataset(train[feature_list].fillna(0), label=train["target"])
dvalid = lgb.Dataset(val[feature_list].fillna(0), label=val["target"])
best_config ={"objective":"regression", "num_leaves":31,"learning_rate":0.01,"n_estimators":2000,"max_depth":5,"metric":"mse","verbosity": 10, "random_state": 0}
model = lgb.train(best_config, dtrain)
train.loc[:, "prediction"] = model.predict(train[feature_list])
val.loc[:,"prediction"]=val["target"]
val.loc[:,"prediction"] = model.predict(val[feature_list])
spearman, payout, numerai_sharpe, maeを計算して、Validationデータのモデルの強さを推定することができます。
spearman,payout,numerai_sharpeは大きいほど良いです。
この中でも特にspearmanの値が大きい(0.025以上が目安)と良いモデルであるとみなせます。
(※Corrだけに注目すると、いろいろな問題が発生する可能性があります。Numeraiに詳しい方とは意見が分かれるところだと思いますが、初めて予測結果を提出する方向けの記事なので、このように表現させてください)
なお、用語の説明は以下の通りです。
spearman: Correlationの平均値。高ければ高いほど良い(参考は0.022~0.04)
ペイアウト 平均リターン
numerai_sharpe: 平均リターンを標準偏差で割った比率。高ければ高いほど良い(目安は1以上)
mae: 平均絶対誤差
def sharpe_ratio(corrs: pd.Series) -> np.float32:
"""
Calculate the Sharpe ratio for Numerai by using grouped per-era data
:param corrs: A Pandas Series containing the Spearman correlations for each era
:return: A float denoting the Sharpe ratio of your predictions.
"""
return corrs.mean() / corrs.std()
def evaluate(df: pd.DataFrame) -> tuple:
"""
Evaluate and display relevant metrics for Numerai
:param df: A Pandas DataFrame containing the columns "era", "target" and a column for predictions
:param pred_col: The column where the predictions are stored
:return: A tuple of float containing the metrics
"""
def _score(sub_df: pd.DataFrame) -> np.float32:
"""Calculates Spearman correlation"""
return spearmanr(sub_df["target"], sub_df["prediction"])[0]
# Calculate metrics
corrs = df.groupby("era").apply(_score)
print(corrs)
payout_raw = (corrs / 0.2).clip(-1, 1)
spearman = round(corrs.mean(), 4)
payout = round(payout_raw.mean(), 4)
numerai_sharpe = round(sharpe_ratio(corrs), 4)
mae = mean_absolute_error(df["target"], df["prediction"]).round(4)
# Display metrics
print(f"Spearman Correlation: {spearman}")
print(f"Average Payout: {payout}")
print(f"Sharpe Ratio: {numerai_sharpe}")
print(f"Mean Absolute Error (MAE): {mae}")
return spearman, payout, numerai_sharpe, mae
feature_spearman_val = [spearmanr(val["prediction"], val[f])[0] for f in feature_list]
feature_exposure_val = np.std(feature_spearman_val).round(4)
spearman, payout, numerai_sharpe, mae = evaluate(val)
中和用のファイルをsubmission_file.csvに書き込みます。このファイルにはidとpredictionのカラムが必要です。また、idはValidationデータ、testデータ(+Liveデータ)の順であることが必要です。順番が違うとNumerai側でリジェクトされますのでご注意ください。
test.loc[:, "prediction"] =0
test.loc[:, "prediction"] = model.predict(test[feature_list])
test[['id', "prediction"]].to_csv("submission_test.csv", index=False)
val[['id', "prediction"]].to_csv("submission_val.csv", index=False)
test=0
val=0
directory = "/kaggle/working"
full_path = f'{directory}/numerai_dataset_{NAPI.get_current_round()}/'
test_path = full_path + 'numerai_tournament_data.csv'
tournament_data = pd.read_csv(test_path)
tournament_data_id=tournament_data['id']
tournament_data_id2=tournament_data['feature_dexterity7']
tournament_data_id=pd.concat([tournament_data_id,tournament_data_id2],axis=1)
val=pd.read_csv("submission_val.csv")
test=pd.read_csv("submission_test.csv")
test_val_concat=pd.concat([val[['id', "prediction"]],test[['id', "prediction"]]],axis=0).set_index('id')
tournament_data_id=tournament_data_id.set_index('id')
conc_submit=pd.concat([tournament_data_id,test_val_concat],axis=1).drop(columns='feature_dexterity7').reset_index()
conc_submit=conc_submit.rename(columns={'index': 'id'})
conc_submit.to_csv("submission_file"+".csv", index=False)
Example_model(Numeraiが公式に配布しているサンプルモデル)と自分のモデルを線形回帰させることで、それぞれの特徴量と予測結果の相関を下げつつ、シャープ比を向上させることができます。ただし、やりすぎるとCorrが大きく下がってしまうので、0.3~0.5くらいがいいと思います。どのようなモデルをどれだけ中和するかも一つの検討要素となります。(*中和をしない、というのも選択肢の一つです) 得られたneutralized_submission_file.csvを、NumeraiのホームページのUpload predictionsから提出すれば完了です。
def neutralize(series,by, proportion):
scores = series.values.reshape(-1, 1)
exposures = by.values.reshape(-1, 1)
exposures = np.hstack((exposures, np.array([np.mean(series)] * len(exposures)).reshape(-1, 1)))
correction = proportion * (exposures.dot(np.linalg.lstsq(exposures, scores)[0]))
corrected_scores = scores - correction
neutralized = pd.Series(corrected_scores.ravel(), index=series.index)
return neutralized
by=pd.read_csv('/kaggle/working/numerai_dataset_'+str(NAPI.get_current_round())+'/example_predictions.csv')
neut=pd.read_csv("submission_file.csv")
neut=pd.DataFrame({'prediction':neutralize(neut['prediction'],by['prediction'], 0.3)})
conc=pd.concat([by.drop(columns="prediction"),neut],axis=1)
conc.to_csv("neutralized_submission_file.csv", index=False)#submission file
以下に示す目安はあくまでも一例です。参考程度にとどめてください。事実、以下の指標が悪いものでも上位にランクされるモデルも存在します。
Validation Sharpe: Validationデータのシャープレシオは1以上が良い結果を得やすいです。
Validation Mean: ValidationデータのCorr平均値が0.025~程度であると良いです。
Feature Neutral Mean: 全ての特徴量で中和した場合のCorr平均値(あまり参考になりません)
Validation SD: 各Eraの予測値とValidationデータの相関の標準偏差(あまり参考になりません)
Feature Exposure: 特徴量の露出度。特徴量と予測結果のバランスの良さを示す指標です。小さければ小さいほど良いです。
Max Drawdown -0.05以下を目安としましょう。
Corr with Example Preds: サンプルモデルとの相関性 0.5~0.8が目安です。
Numeraiとコミュニティに関連する便利なリンク
【Numeraiに関する説明・HPの見方等】
Numerai公式:Numerai 日本語ドキュメント
@blog_UKI:機械学習による株価予測 はじめようNumerai
@blog_UKI:Numeraiトーナメント -伝統的クオンツと機械学習の融合-
@blog_UKI:機械学習による株価予測 さがそうNumerai Signals
@blog_UKI:機械学習による株価予測 さがそう 真のNumerai Signals
@katsu1110:KagglerへのNumeraiのススメ
@developer_quant:【ファイナンス機械学習】著者によるNumerai解説スライドを日本語でまとめてみる @Y_oHr_N:Numeraiはいいぞ
Numeraiに関するチュートリアル (*英語版)
Last modified 3mo ago