ファイナンス機械学習:ファイナンスにおける交差検証法 オーバーフィッティングとなる理由
機械学習のモデルの評価においては、訓練データとは交わらないデータでテストすることが重要である。交差検証法は訓練データセットを訓練データを検証データに分け、検証データ結果からパラメータを調整し、最後に未知のテストデータでの評価を可能にする方法である。
k-fold交差検証法(k-fold CV )は、訓練データセットをk個に分割し、データセット$${i}$$を検証データとして、残りの$${k-1}$$データセットを訓練データとする。
しかし、観測データがIIDに従わないファイナンスではこのk-foldCVは、うまく機能せず、時系列データからなるテストデータで同じデータが複数回現れることで、バイアスが高くなる。
これを避けるには、訓練データセットの結果$${Y_i}$$と$${Y_j}$$が決定される観測値/訓練データ$${X}$$がダブっている場合、どちらかを除外するか、もしくは観測値が$${Y_j}$$と重複する$${Y_i}$$を除外する(独自性)、冗長なサンプリングを抑制する、バイアスが低くなるように分類器を早期終了させる方法がある。
独自性を使ったサンプリングの方法(逐次ブートストラップ、max_sample=avgU)と、分類器の早期終了は、前節で扱った。よって、ここでは、訓練データとテストデータ間の情報のリーケージを減らすパージングと、データ使用に禁止(猶予)期間を設けるバージングの二つの方法を扱う。
訓練データのパージング
トリプルバリア法において、ラベル$${Y_j}$$は、$${[t_{j,0},t_{j,1}]}$$区間の価格のリターンの符号$${sgn[r_{t_{j,0},t_{j.1}}]}$$で与えられている。よって、$${Y_i}$$が与えられる区間が、以下の条件のどれか一つを満たしている判断された場合、同じ情報を共有していることから取り除かれる。
$${t_{j,0}\le t_{i,0} \le t_{j,1}}$$
$${t_{j,0}\le t_{i,1} \le t_{j,1}}$$
$${t_{j,0}\le t_{i,0} \le t_{j,1} \le t_{i,1}}$$
これは、スニペット7.1で実装されている。
def getTrainTimes(t1, testTimes):
trn = t1.copy(deep=True)
for i, j in testTimes.iteritems():
df0 = train[(i <= train.index) & (train.index <= j)].index # train starts within test
df1 = train[(i <= train) & (train <= i)].index # train ends within test
df2 = train[(train.index <= i) & (j <= train)].index # train envelops test
trn = trn.drop(df0.union(df1).union(df2))
return trn
エンバーゴ(猶予期間)
パージングによって全てのリーケージを防ぐことができない場合、エンバーゴを課す。
$${t_{i,1}\let_{j,0}}$$である訓練ラベル$${Y_i=f\bigl[[t_{i,0},t_{i,1}]\bigl]}$$は、$${Y_j}$$のテストデータが始まる前に終わっているから、このようなテストデータより前にある訓練データにはエンバーゴは作用しない。テストデータの直後、$${t_{j,1} \le t_{i,0} \le t_{j, 1}+h}$$内に入っている訓練ラベルがエンバーゴの対称である。
エンバーゴの期間$${h}$$は、$${h \simeq 0.001 T}$$(Tは全訓練期間)で十分であり、これより大きくしてもパフォーマンスには変わらない。
この実装はスニペット7.2で与えられている
def getEmbargoTimes(times, pctEmbargo= 0.0):
step = int(times.shape[0] * pctEmbargo)
if step == 0:
mbrg = pd.Series(times, index=times)
else:
mbrg = pd.Series(times[step:], index=times[:-step])
mbrg = mbrg.append(pd.Series(times[-1], index=times[-step:]))
return mbrg
## Examples including Embargo before Purging
# testtimes=pd.Series(mbrg[dt1],index=[dt0]) # include embargo before purge
# trainTimes=getTrainTimes(t1,testTimes)
# testTimes=t1.loc[dt0:dt1].index