TimeSeriesSplitで時系列交差検証する:gapを入れた12行の分割を読む
時間順に増えるデータを評価するなら、「過去から学び、その後のデータで確かめる」分割が必要です。TimeSeriesSplitはそのための行番号を返す道具です。今回は架空の機器センサー12時間分を使い、学習行・評価行・その間に空ける行を見える形にします。
予測時点で分からないデータを先に除く
毎時の始めに「次の1時間で機器に異常があるか」を予測する教材を考えます。同じ行の「1時間終了時の最高温度」は予測時点では分かりません。行を時間順に分けても、この列を使えば未来の情報が混ざります。分割を設定する前に、各列の記録時刻と利用可能時刻を書き出してください。
| 教材の列 | 予測時点に使えるか | 理由 |
|---|---|---|
| 直前1時間の平均温度 | 使える候補 | 直前の集計が確定し取得できた時刻を確認する |
| 次の1時間の最高温度 | 使えない | 予測対象の時間が終わってから確定する |
| 異常判定ラベル | 説明変数に使わない | 予測したい正解そのもの |
12行を3回に分け、1行のgapを設ける
行番号0〜11を時刻順に並べます。評価行を毎回2行、評価直前の1行を学習から外す教材です。これは実際の待機時間の推奨値ではありません。ラベルの確定に時間がかかる問題では、情報の利用可能時刻に合わせて間隔を決めます。
from sklearn.model_selection import TimeSeriesSplit
splitter = TimeSeriesSplit(n_splits=3, test_size=2, gap=1)
for train, test in splitter.split(range(12)):
print(train.tolist(), test.tolist())
| 分割 | 学習 | 直前の除外行 | 評価 |
|---|---|---|---|
| 1 | 0〜4 | 5 | 6、7 |
| 2 | 0〜6 | 7 | 8、9 |
| 3 | 0〜8 | 9 | 10、11 |
前の評価行が後の学習に入るのは、時間が進んだ後の学習を模擬するためです。最初の時点のモデルで全期間を評価する設計とは異なります。比較したい運用を先に決めましょう。
gapは行数であり、時間単位ではない
公式APIのgapは、評価直前に学習から除くサンプル数です。不規則な間隔のデータでgap=1にしても、1時間空けたことにはなりません。今回のような等間隔の教材と、イベントごとに記録される実データを区別してください。評価期間の長さをそろえて比較する前提も確認します。[出典1]
また、gapを入れればすべてのリークが防げるわけではありません。複数日にまたがる集計、重複した利用者、終了後に修正された列などは、別に点検します。
前処理も各分割の学習側で決める
全12行で平均を計算して欠損を埋め、その後で上の分割をすると、評価側の値が学習に影響します。前処理を学習行でfitし、同じ変換を評価行へ適用します。Pipelineなどで学習と変換の順を管理できます。[出典2]
分割表で確かめるチェック項目
- 行が時刻順で、同時刻の扱いが決まっている。
- 各分割で学習の最終利用可能時刻が、評価の予測時点より前である。
- gapに除かれる行と、必要な待機期間が一致する。
- 前処理のfitに評価行を入れていない。
- モデル同士を同じ行で比較している。
- 各期間の件数と指標を残し、平均値だけで隠していない。
個別適用や説明の拡張時に確認する事項:最初の提出物は性能の高い数値より、この分割表です。どの時点で何を知っていたかを説明できる形にすると、評価の意味を第三者が確認できます。上のコードは分割を表示する例で、機器データでの学習や性能測定を実施した結果ではありません。