初めに
今回は、Google Researchが公開した時系列基盤モデル TimesFM 2.5 を使って、東京の日次気温(最低・最高)とVTI(米国株ETF)の終値をゼロショットで予測する方法を紹介します。
TimesFMはLLMと同じデコーダーオンリーのTransformerアーキテクチャを時系列に適用したモデルで、事前学習済みの重みをそのまま使い、学習データなしで任意の時系列を予測できます。2.5では200Mパラメータに軽量化されつつコンテキスト長が16,384に拡張され、連続分位ヘッド(continuous quantile head)による確率的予測にも対応しています。
今回の実験では、気温のような強い季節性を持つデータと、株価のようなランダムウォーク的なデータの両方を予測し、モデルの予測精度や不確実性の表現がどう変わるかを確認します。
開発環境
| 項目 | バージョン |
|---|---|
| OS | Windows 11 |
| Python | 3.12.7 |
| パッケージマネージャ | uv 0.9.2 |
| PyTorch | 2.11.0+cpu |
| timesfm | 2.0.0 (editable install) |
| GPU | なし(CPU推論) |
環境構築
Python バージョンの注意
PyTorch 2.11.0はPython 3.13.8のASTパーサとの間に非互換があり、import torch時に以下のエラーが発生します。
IndentationError: expected an indented block after function definition on line 4
Python 3.12系を明示指定する必要があります。
仮想環境の作成
cd timesfm uv venv --python 3.12 source .venv/Scripts/activate
パッケージのインストール
TimesFM本体をPyTorchバックエンドでeditable installし、データ取得・可視化用のパッケージも合わせてインストールします。
# TimesFM本体 + PyTorchバックエンド uv pip install -e ".[torch]" # データ取得・可視化 uv pip install meteostat yfinance matplotlib pandas
インストールの確認
python -c "import torch; print('torch', torch.__version__); import timesfm; print('timesfm ok')"
torch 2.11.0+cpu timesfm ok
データの取得
東京の気温(meteostat)
meteostatを使って東京の気象ステーション(WMO ID: 47662)から過去6年分の日次データを取得します。
なお、meteostat 2.xでは旧バージョンのfrom meteostat import Daily, Pointが使えなくなっており、from meteostat import daily(小文字の関数)を使用します。また、Point(lat, lon)での地理検索はProviderの対応状況によって空になることがあるため、ステーションIDを直接指定するのが確実です。
from datetime import datetime, timedelta from meteostat import daily TOKYO_STATION = "47662" end = datetime(2026, 4, 12) start = end - timedelta(days=365 * 6) df = daily(TOKYO_STATION, start, end).fetch(fill=True) temps = df[["tmin", "tmax"]].interpolate(method="linear").ffill().bfill()
2191 daily rows, 2020-04-13 -> 2026-04-12
VTI株価(yfinance)
yfinanceで過去6年分のVTI終値を取得します。auto_adjust=Trueで調整済み終値を使います。
import yfinance as yf df = yf.download("VTI", start=start, end=end, auto_adjust=True, progress=False) # yfinanceはMultiIndexを返すことがあるのでフラットにする if isinstance(df.columns, pd.MultiIndex): df.columns = df.columns.get_level_values(0) vti = df[["Close"]].rename(columns={"Close": "close"})
1507 trading days, 2020-04-13 -> 2026-04-10
モデルの読み込みとコンパイル
重みのダウンロード
from_pretrainedでHugging Face Hubからmodel.safetensors(約800MB)が自動ダウンロードされます。2回目以降は~/.cache/huggingface/のキャッシュから読み込まれます。
import torch import timesfm torch.set_float32_matmul_precision("high") model = timesfm.TimesFM_2p5_200M_torch.from_pretrained( "google/timesfm-2.5-200m-pytorch" )
ForecastConfigによるコンパイル
model.compile()にForecastConfigを渡して推論の設定を行います。max_contextとmax_horizonは内部でパッチサイズの倍数に自動調整されます。
model.compile(
timesfm.ForecastConfig(
max_context=2048, # 入力系列の最大長
max_horizon=60, # 予測ステップ数(約2ヶ月)
normalize_inputs=True, # 入力のスケール正規化
use_continuous_quantile_head=True, # 分位予測ヘッドを有効化
force_flip_invariance=True, # 負のスケーリングにも線形不変性を保証
infer_is_positive=True, # 入力が非負なら出力も非負にクランプ
fix_quantile_crossing=True, # 分位の交差を修正
)
)
主要パラメータの解説です。
| パラメータ | 説明 |
|---|---|
max_context |
モデルに渡す過去データの最大長。短い系列はゼロパディング、長い系列は切り詰め |
max_horizon |
予測する未来のステップ数 |
use_continuous_quantile_head |
Trueにすると点予測に加えて分位予測(mean, q10〜q90)が返る |
force_flip_invariance |
TimesFMはf(aX+b) = a*f(X)+bをa≥0で保証。このフラグでa<0にも拡張 |
infer_is_positive |
株価のように常に正の系列で負の予測を防ぐ |
推論の実行
各系列を1次元のnumpy arrayとしてリストで渡し、model.forecast()を呼びます。
import numpy as np inputs = [ temps["tmin"].to_numpy(dtype=np.float32), temps["tmax"].to_numpy(dtype=np.float32), vti["close"].to_numpy(dtype=np.float32), ] point_forecast, quantile_forecast = model.forecast(horizon=60, inputs=inputs) # point_forecast.shape = (3, 60) — 3系列 × 60ステップ # quantile_forecast.shape = (3, 60, 10) — [mean, q10, q20, ..., q90]
3系列を一括で予測しています。quantile_forecastの10列はそれぞれmean, q10, q20, ..., q90に対応しています。
可視化
matplotlibで日本語ラベルを文字化けなく描画するため、Windows標準のMeiryoフォントを指定し、マイナス記号の文字化けも防止します。
import matplotlib as mpl import matplotlib.pyplot as plt mpl.rcParams["font.family"] = "Meiryo" mpl.rcParams["axes.unicode_minus"] = False
描画では過去1年分の実績と予測線・予測区間を重ねてプロットしています。以下は最低気温の例です。
fig, ax = plt.subplots(figsize=(12, 5)) # 過去1年分の実績 hist = temps["tmin"].iloc[-365:] ax.plot(hist.index, hist.values, color="#1f77b4", label="実績") # 予測(平均) future_idx = pd.date_range(temps.index[-1] + pd.Timedelta("1D"), periods=60, freq="D") ax.plot(future_idx, point_forecast[0], color="#d62728", label="予測(平均)") # 予測区間 q10-q90 q10 = quantile_forecast[0, :, 1] q90 = quantile_forecast[0, :, 9] ax.fill_between(future_idx, q10, q90, alpha=0.15, color="#d62728", label="予測区間 q10-q90") ax.set_title("東京 日次最低気温 — TimesFM 2.5 予測(今後60日間)") ax.set_ylabel("最低気温 (℃)") ax.set_xlabel("日付") ax.legend() fig.savefig("tokyo_tmin_forecast.png", dpi=140)
実行結果
2026年4月12日を基準に60日先を予測した結果です。
| 系列 | 予測初日 → 60日後(平均) | 60日後の不確実性(q10-q90) |
|---|---|---|
| 東京 最低気温 | 12.9℃ → 20.8℃ | 17.5 - 24.2 ℃ |
| 東京 最高気温 | 20.7℃ → 27.5℃ | 23.0 - 31.4 ℃ |
| VTI 終値 | $334.8 → $340.8 | $315.8 - $357.5 |
東京 最低気温

4月中旬から6月中旬にかけて滑らかに上昇するカーブが出ており、梅雨入り前の昇温を再現しています。予測区間は±3℃程度と狭く、モデルが高い確信度を持っていることが分かります。
東京 最高気温

最低気温と同様に季節性を正しく捉えています。60日後の予測平均は27.5℃で、6月上旬の東京としては妥当な値です。
VTI 終値

予測平均はほぼ横ばい(+1.8%)ですが、予測区間は$315〜$358と気温の5倍以上に広くなっています。株価のランダムウォーク的な性質を分位帯の広さで正直に表現しており、点予測だけでは意味がなく、予測区間を見て初めて「方向予測が困難であること」をモデルが示していると読み取れます。
トラブルシューティング
今回の実験でハマったポイントを3つ記録しておきます。
Python 3.13でimport torchが失敗する
PyTorch 2.11.0のJITコンパイラがPython 3.13.8のASTパーサと非互換です。uv venv --python 3.12でPython 3.12系を使います。
meteostatでImportError: cannot import name 'Daily'
meteostat 2.xではAPIが変更されています。
# 旧 (1.x) from meteostat import Daily, Point # 新 (2.x) from meteostat import daily
dailyは関数として呼び出し、.fetch()でDataFrameを取得します。
meteostatのPoint()でデータが0件になる
Point(lat, lon)での地理検索はProvider依存で空になることがあります。ステーションIDを直接指定するのが確実です。
# 空になることがある daily(Point(35.6762, 139.6503, 44), start, end) # 確実に取得できる daily("47662", start, end) # 47662 = 東京 WMO ID


