ファイナンス、情報通信技術のスキル・アグリゲーション・サイト

' . iseeit.jp Finance × ICT . '
 
機械学習の外れ方 ②

学習期間を変えると、答えが変わる
窓幅の選び方と、1行のリーク

同じモデル、同じデータ、同じ評価期間。 学習に使う期間の長さを変えるだけで、予測精度が動きます。 短い期間ほど成績が良く見えました——ところがそれは、 学習データに答えが1つ混じっていたからでした。 直したら、結論が逆になります。

学習窓 データリーク 自己回帰 ベースライン

[ 1 ]

どこまで遡って学習するか

前回は、 予測の評価が物差し次第で逆転する話でした。今回は学習の側を扱います。

予測モデルを作るとき、過去のどこまでを学習に使うかを決める必要があります。 直近20日か、1年か、5年ぶんか。この長さを「窓(ウィンドウ)」と呼びます。

今回のモデル

自己回帰(AR)です。直近3日の変化率から、翌日の変化率を線形回帰で予測します。 LSTMのような複雑なものではありませんが、 「過去から未来を当てる」という構造は同じです。 窓の影響を見るには、単純なほうが分かりやすい。

窓を長くすれば学習データは増えます。ただし古い時期の性質まで混ざります。 短くすれば直近に適応できますが、サンプルが足りません。 どちらが良いのか、測ってみます。

実装上の但し書き。 評価の初期は、長い窓ほど学習に必要な過去が足りません。 その場合は回帰を諦めて「前日と同じ」を返します。 400日窓の成績にはこの区間が含まれます—— 長い窓ほどナイーブに近づく理由のひとつでもあります。
[ 2 ]

短い窓ほど、成績が良かった

合成データ600日ぶんを用意し、後半400日を評価区間としました。 窓の長さだけを変えて比べます。

学習窓MAPERMSE方向一致率
20 日1.430 %64967.7 %
60 日1.542 %69755.4 %
120 日1.537 %70157.4 %
180 日1.546 %70455.1 %

20日窓が突出しています。MAPE も RMSE も最良、 方向一致率にいたっては 67.7%。 前回「コイン投げと変わらない」と書いた50%を、大きく上回っています。

解釈もつきました。相場は変わり続けるのだから、直近だけを見るほうが適応が速い—— もっともらしい説明です。実際、そう書きかけました。

ただ、前回の教訓が引っかかりました。 「思ったより良い数字が出たら、まず測り方を疑う」。 方向一致率67.7%は、相場予測としては出来すぎです。 確かめることにしました。
[ 3 ]

学習データに、答えが入っていた

学習サンプルを作るループを見直しました。

誤った実装
// endIdx 時点で、翌日 P[endIdx+1] を予測したい
for (let i = endIdx - W + 1; i <= endIdx; i++) {
  // 特徴量:i 時点までの変化率
  const feat = [ logRet(i), logRet(i-1), logRet(i-2) ];
  // 目的変数:翌日の変化率
    Y.push( logRet(i+1) );   // i=endIdx のとき logRet(endIdx+1)
}

ループの最後、i = endIdx のときを見てください。 目的変数が logRet(endIdx+1) になります。 これはまさに予測したい値そのものです。

答えを1つ、学習データに混ぜていました。 予測時点では知り得ない未来の値です。 窓が短いほど、この1サンプルの比重が大きくなる—— 20日窓なら全体の5%、180日窓なら0.6%。 「短い窓ほど成績が良い」の正体は、これでした
データリーク(leakage)

予測時点では入手できないはずの情報が、学習や特徴量に混入すること。 評価成績だけが良くなり、実運用では再現しません。 時系列では、未来の値を使ってしまう形で起きやすい。

正しい実装
// 最後のサンプルの目的変数が P[endIdx] になるようにする
const last = endIdx - 1;
for (let i = last - W + 1; i <= last; i++) {
  const feat = [ logRet(i), logRet(i-1), logRet(i-2) ];
    Y.push( logRet(i+1) );   // 最大でも logRet(endIdx) = 既知
}

変えたのはループの終端だけです。1行にも満たない差です。

[ 4 ]

直したら、結論が逆になった

学習窓方向一致率
(リークあり)
方向一致率
(修正後)
20 日67.7 %50.9 %−16.8
60 日55.4 %45.9 %−9.5
180 日55.1 %46.6 %−8.5

すべて50%前後に戻りました。前回の記事と同じ結論です。 そして20日窓の優位も消えました

修正後の全体像

学習窓MAPERMSE方向ナイーブ比
20 日1.813 %82150.9 %+16.0 %
40 日1.691 %76449.9 %+8.2 %
60 日1.681 %76245.9 %+7.5 %
90 日1.624 %74450.4 %+3.9 %
120 日1.619 %74247.6 %+3.6 %
180 日1.609 %73646.6 %+2.9 %
ナイーブ1.564 %719測定不能
どの窓も、ナイーブ予測に負けています。 「前日と同じ」と答えるだけの予測より、すべて成績が悪い。 窓を長くするほどマシになりますが、それは 予測が「前日と同じ」に近づいていくからです。 学習データが増えるほど、モデルは「明日は今日とだいたい同じ」と学ぶ。

リークがあったときは、窓の選び方が成績を左右するように見えました。 修正後はそもそも勝てていない窓を最適化する以前の問題でした。

[ 5 ]

窓とリークを、切り替えてみる

学習窓を動かし、リークの有無を切り替えられます。 合成データと実データのどちらでも試せます。

Interactive · window & leakage
学習窓を、動かしてみる
自己回帰モデル(直近3日→翌日)を、窓ごとに学習し直して評価します。実データは Yahoo Finance から取得します。
窓の長さ別の成績(横軸=学習窓)

リークありでは、短い窓が有利に見えます。 切り替えると、その優位が消えます。 点線はナイーブ予測の成績です。修正後は、どの窓もこれを超えられません。

[ 6 ]

なぜ気づきにくいのか

リークはエラーを出しません。計算は最後まで走り、 それらしい数値が返ります。むしろ期待どおりの、良い数値が。

起きること見え方
成績が良くなる「モデルがうまく学習した」
窓が短いほど顕著「直近への適応が効いている」
実運用では再現しない気づくのは、動かした後

しかも解釈がつきやすいのが厄介です。 「短い窓が良い」という結果には、もっともらしい説明を当てはめられます。 説明できてしまうと、疑わなくなります

時系列を扱うときの目安。 「その情報は、予測時点で手に入るか」を1つずつ確かめる。 標準化や正規化を全期間で行うのも同じ問題を起こします (未来の平均・分散を使うことになる)。 前処理も含めて、時間の前後を守る必要があります。
[ 7 ]

窓は、何で決めるべきか

今回は「どの窓もナイーブに勝てない」という結論でしたが、 勝てるモデルがある場合に窓をどう選ぶかは別の問題です。

考え方内容
サンプル数パラメータ数の10倍以上は欲しい。3変数なら最低30〜50点
性質の安定性窓の中で相場の性格が変わっていないか
検証で決める学習・検証・テストを時間順に分け、検証で窓を選ぶ
やってはいけないテスト成績が最も良い窓を選ぶ(それは調整であって評価ではない)
この記事の表も、厳密には「評価」ではありません。 同じ評価区間で複数の窓を比べて並べています。 ここから最良の窓を選べば、その数字はもう成績ではなくなります。 窓を選ぶための区間と、成績を測るための区間は、分ける必要があります。
[ 8 ]

まとめ

窓の長さを変える実験のつもりが、自分の実装を検算する話になりました。 前回書いた「良い数字が出たら測り方を疑う」が、そのまま今回に効いています。

この記事のまとめ

  • 学習窓を変えると、同じモデル・同じデータでも成績が動く。
  • 短い窓ほど良く見えたのは、学習データに答えが1つ混じっていたから。
  • 窓が短いほど、その1サンプルの比重が大きくなる(20日窓なら5%、180日窓なら0.6%)。
  • 修正すると方向一致率が 67.7% → 50.9%。20日窓の優位も消えた。
  • 修正後はどの窓もナイーブ予測に負ける。窓を最適化する以前の問題。
  • 長い窓ほどマシなのは、予測が「前日と同じ」に近づくから。
  • リークはエラーを出さず、良い数値を返す。しかも解釈がつきやすい。
  • 窓を選ぶ区間と、成績を測る区間は分ける。同じ区間で選べば、それは評価ではない。
機械学習の外れ方 — シリーズ
  1. 予測は、当たっているのか
    「精度」という言葉が隠すもの
  2. 学習期間を変えると、答えが変わる(この記事)
    窓幅の選び方と、1行のリーク
  3. 「異常」は、誰が決めたのか
    閾値の恣意性

関連記事

『学習期間を変えると、答えが変わる — 窓幅の選び方と、1行のリーク』を公開しました。

ファイナンシャル・プランニング
債券利回り計算(単利)

最終利回り計算(単利) : 債券を購入時点から、最終償還日まで保有していた場合に得られる収益の利回りを単利にて計算します。

所有期間利回り計算(単利) : 債券の購入時点から、最終償還日前の売却時点までの所有期間に得られる収益の利回りを単利にて計算します。