📈

論文実装:In-Context Fine-Tuning (ICF) を用いた時系列

に公開

論文実装:小売業における売上予測精度向上

今回の論文

In-Context Fine-Tuning for Time-Series Foundation Modelsという論文を読みました。

まず、論文の内容ですが、ざっくり次のような感じです。(英語があまり得意ではないので、違っていたらご指摘ください。)

概要

本論文は、時系列データの基盤モデル(Time-Series Foundation Models)における新しい適応手法「In-Context Fine-Tuning(ICF)」を提案している。
従来の時系列モデルは学習済みモデルを特定データセットに再学習(fine-tuning)する必要があったが、ICFでは推論時に関連する複数の時系列例を「プロンプト」として入力するだけでモデルがドメイン適応を行える。
提案モデル「TimesFM-ICF」は、ベースモデルTimesFMを拡張し、関連時系列の情報をコンテキストウィンドウ内で活用できるように設計されている。
実験の結果、ICFは明示的なfine-tuningを行わずに、既存の最先端モデルや統計的手法を上回り、明示的fine-tuningと同等の性能を達成した。

目的・背景

従来の時系列予測モデル(DeepAR、N-BEATSなど)は、個別タスクごとに学習が必要で、転用性が低い。一方、NLP分野ではLLM(例:GPT-4)がfew-shotやin-context learningにより、追加学習なしでタスク適応を実現している。
本研究の目的は、同様の「in-context learning能力」を時系列基盤モデルにも導入し、明示的なfine-tuningを不要にすることで、汎用的で即時利用可能な時系列予測を可能にすることである。

調査・実験・検証内容

モデル構造

提案モデル「TimesFM-ICF」は、デコーダ専用構造(Transformerベース)を採用し、以下の要素で構成される。

  1. 共通埋め込み層:入力時系列をパッチ分割し、MLPでトークン化。

  2. セパレータトークン:複数の時系列例を区切るための学習可能トークンを導入。

  3. クロスエクザンプルアテンション:異なる時系列間で因果的に注意を向ける設計。

  4. No Positional Encoding (NoPE):絶対位置符号を排除し、文脈長の汎化を向上。

トレーニング方法

  1. 既存のTimesFM (base) モデルを初期重みとして使用。

  2. 約4000億タイムポイントの多様なデータセットで事前学習。

  3. 継続学習(continued pretraining)では、最大50のin-context例を組み合わせて訓練。

  4. コンテキスト例の選定は「直近履歴5例+ランダム45例」という単純戦略。

評価設定

  • OOD(Out-of-Domain)ベンチマーク:Chronosベンチマーク23データセット(学習未使用)

  • ETTデータセット:長期予測(96〜720ステップ)で検証

  • 評価指標:Scaled MASE(平均絶対スケール誤差)とMAE(平均絶対誤差)

主な結果

|比較対象|評価|特徴|
||||
|TimesFM (base)|基準|通常のzero-shotモデル|
|TimesFM-FT|fine-tuningあり|各データセットに再学習|
|TimesFM-ICF|本手法|推論時にin-context例を入力|

  • OODベンチマークでTimesFM-ICFはTimesFM(base)より6.8%精度向上。

  • TimesFM-FT(fine-tuned版)と同等の性能を、追加学習なしで達成。

  • 他の強力なベースライン(PatchTSTなど)よりも5%高精度。

  • 推論時間はTimesFM-FTの1/16(25分 vs 418分)と高速。

  • ETT長期予測でもMAEで既存モデルを上回り、特に短系列データで優位性。

実施内容

1. データ処理:パッチ化とコンテキスト組成

ICF では複数系列を1つの長い“文”に見立てるため、履歴長(history_len)+予測長(horizon_len)でウィンドウを切り出し、パッチ(連続値ベクトル)をトークンに埋め込みます。

def moving_windows(y: np.ndarray, total_len: int, step: int=1):
    out = []
    for i in range(0, len(y)-total_len+1, step):
        out.append(y[i:i+total_len])
    return out

def sample_context(series_pool, history_len, horizon_len, n_examples, in_series_examples):
    """
    return:
      examples: 形状 (N, history_len + horizon_len) の系列群
      target_window: 予測対象の (history_len + horizon_len) ウィンドウ
    """
    # 複数系列から N 個抽出し、推論対象の系列と連結する……といった最小ロジック

ポイント

  • 複数系列(N個)を「例(examples)」として束ね、予測対象の系列を最後に連結(= in-context learning の“ショット”に相当)。

  • INT/標準化/クリップなどの前処理ユーティリティもノート内に同梱。

2. バックボーン:軽量 TimesFM 風 Transformer

公式 TimesFM パッケージの代替として、TinyTimesFMBackbone を自作。
入出力レイヤを含む薄いラッパ TimesFMWithIO を介して、patch→token→Transformer→token→patch の流れをシンプルに再現しています。

class TinyTimesFMBackbone(nn.Module):
    def __init__(self, d_model=512, n_layers=8, n_heads=8, d_ff=1024, dropout=0.1):
        super().__init__()
        layer = nn.TransformerEncoderLayer(d_model=d_model,
                                           nhead=n_heads,
                                           dim_feedforward=d_ff,
                                           dropout=dropout,
                                           batch_first=True)
        self.enc = nn.TransformerEncoder(layer, num_layers=n_layers)

    def forward(self, x):
        return self.enc(x)

実戦投入では公式実装/チェックポイントの利用を推奨。本ノートではICF の検証に資源を割くため、構造は最小限です。

3. ICF アダプタ:区切りトークン & 要約トークン

ICF の要は例間の情報を“うまく混ぜる”こと。
そこで、[SEP](区切り)と[SUM](要約)の学習可能埋め込みを導入し、例ごとの要約トークンが周辺トークンへ注意できるようにします。

class ICFAdapter(nn.Module):
    def __init__(self, backbone: TimesFMWithIO, d_model=512, input_patch=64, output_patch=64):
        super().__init__()
        self.backbone = backbone
        self.patch_embed = nn.Linear(input_patch, d_model)  # patch→token
        self.sep_token = nn.Parameter(torch.randn(1, 1, d_model) * 0.02)
        self.sum_token = nn.Parameter(torch.randn(1, 1, d_model) * 0.02)
        self.head = nn.Linear(d_model, output_patch)        # token→patch(予測)

    def forward(self, examples):  # (B, N, L+H)
        # 例ごとにパッチ埋め込み → [SUM] を先頭に付与 → 例間を [SEP] で連結
        # 連結列をバックボーンに通し、末尾(予測対象ウィンドウ)の出力を復元
        ...
        return pred  # (B, H)

ポイント

  • [SUM] … 各例の情報を1トークンへ要約し、例間の比較や共通パターンの抽出を促す

  • [SEP] … 例と例の境界を明示して拡散注意を防ぎ、“どこまでが1例か”を学習しやすくする

4. 損失関数・評価指標:MAE と MASE

学習は単純な MAE(L1)、評価はスケール不変で系列比較に向く MASE を使います。

def train_step(batch):
    model.train()
    examples = batch['examples'].to(DEVICE)      # (B,N,L+H)
    target   = batch['target_future'].to(DEVICE) # (B,H)

    optimizer.zero_grad(set_to_none=True)
    with torch.cuda.amp.autocast(enabled=(DEVICE=='cuda')):
        pred = model(examples)                    # (B,H)
        loss = F.l1_loss(pred, target)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
    return loss.item()

MASE はナイーブ予測(1期ラグ)との誤差比で定義。系列横断の比較がしやすく、時系列ベンチマークで多用されます。

5. 学習フロー:Linear-Probe(既定)/ Full

  • Linear-Probe(推奨) … Transformer 本体は凍結し、ICFアダプタと入出力のみ学習

  • Full … バックボーンも含めて微調整(データ/計算資源に余裕がある場合)

小規模データでも ICF の寄与を観測しやすいのは LP で、過学習が起こりにくいのが利点です。

6. 推論:Nショット in-context 予測

任意の系列に対して、N 個の“例”(別系列の履歴+将来)をつけてコンテキストを作り、予測対象系列の horizonを出力します。

@torch.no_grad()
def predict(examples, horizon_len):
    model.eval()
    return model(examples).detach().cpu().numpy()  # (B,H)

可視化は matplotlib で、履歴・予測・実測を重ねて確認します

サマリ

今回の論文実装では、In-Context Fine-Tuning(ICF)の要点である「例間アテンション」の仕組みを、区切りトークンと要約トークンを導入したコンパクトな Transformer によって再現できました。

これにより、ICF の学習原理を小規模環境でも直感的に検証できる最小実装を実現したと言える。さらに、データの読み込みからパッチ化、学習(Linear-Probe/Full の両対応)、MASE 指標による評価、推論、そしてモデル保存までを一連の流れとして統合したことで、再現性の高い実験ワークフローを構築できた点も大きな成果といえるでしょう。

また、バックボーンを凍結した軽量な Linear-Probe 設定においても、単独学習モデルよりも安定かつ堅牢な性能改善を確認でき、小規模データ環境における ICF の有効性を示すことができました。

Discussion