diff --git a/timesfm-forecasting/examples/global-temperature/output/forecast_output.csv b/timesfm-forecasting/examples/global-temperature/output/forecast_output.csv index c24104c..0a4de7d 100644 --- a/timesfm-forecasting/examples/global-temperature/output/forecast_output.csv +++ b/timesfm-forecasting/examples/global-temperature/output/forecast_output.csv @@ -1,13 +1,13 @@ -date,point_forecast,q10,q20,q30,q40,q50,q60,q70,q80,q90,q99 -2025-01-01,1.2593384,1.248188,1.140702,1.1880752,1.2137158,1.2394564,1.2593384,1.2767732,1.297132,1.32396,1.367888 -2025-02-01,1.2856668,1.2773758,1.1406044,1.1960833,1.2322671,1.2593892,1.2856668,1.3110137,1.3400218,1.3751202,1.4253658 -2025-03-01,1.2950127,1.2869918,1.126852,1.1876173,1.234988,1.2675052,1.2950127,1.328448,1.354729,1.4035482,1.4642649 -2025-04-01,1.2207624,1.2084007,1.0352504,1.1041918,1.151865,1.1853008,1.2207624,1.256663,1.2898555,1.3310349,1.4016538 -2025-05-01,1.1702554,1.153313,0.9691495,1.0431063,1.0932612,1.1276176,1.1702554,1.201966,1.2390311,1.2891905,1.3632389 -2025-06-01,1.1455553,1.1275499,0.94203794,1.0110554,1.0658777,1.1061188,1.1455553,1.1806211,1.2180579,1.2702757,1.345366 -2025-07-01,1.1702348,1.1510556,0.9503718,1.0347577,1.0847733,1.1287677,1.1702348,1.2114835,1.2482276,1.2997853,1.3807325 -2025-08-01,1.2026825,1.1859496,0.9709255,1.0594383,1.1106675,1.1579902,1.2026825,1.2399211,1.2842004,1.3408126,1.419526 -2025-09-01,1.1909748,1.1784849,0.95943713,1.0403702,1.103606,1.1511956,1.1909748,1.2390201,1.2832941,1.3354731,1.416972 -2025-10-01,1.1490841,1.1264795,0.9079477,0.99529266,1.0548235,1.1052223,1.1490841,1.1897774,1.240414,1.2868769,1.3775467 -2025-11-01,1.0804785,1.0624356,0.8361266,0.9259792,0.9882403,1.0386353,1.0804785,1.1281581,1.1759715,1.228377,1.3122478 -2025-12-01,1.0613453,1.0366092,0.80220693,0.89521873,0.9593707,1.0152239,1.0613453,1.1032857,1.15315,1.216908,1.2959521 +date,point_forecast,mean,q10,q20,q30,q40,q50,q60,q70,q80,q90 +2025-01-01,1.2223774,1.2215943,1.1230627,1.1613995,1.1832488,1.2030286,1.2223774,1.240989,1.2626991,1.2927711,1.339603 +2025-02-01,1.2563584,1.2502017,1.1482248,1.1891649,1.2134029,1.2355918,1.2563584,1.2787917,1.3061507,1.3358022,1.3880283 +2025-03-01,1.286477,1.2816916,1.1694773,1.2141376,1.2430503,1.2636719,1.286477,1.3101407,1.3365126,1.3725822,1.4265702 +2025-04-01,1.240488,1.2406754,1.119298,1.1689228,1.1957527,1.2162383,1.240488,1.265359,1.2880774,1.3237736,1.3806401 +2025-05-01,1.2026378,1.1969143,1.0776879,1.1280149,1.1554759,1.1801765,1.2026378,1.2274072,1.2546852,1.2890899,1.3469675 +2025-06-01,1.21002,1.1963896,1.0811386,1.1352499,1.1687461,1.1853771,1.21002,1.2333429,1.255994,1.2938039,1.3532466 +2025-07-01,1.2253109,1.2151253,1.0917634,1.1474797,1.176413,1.2029413,1.2253109,1.250956,1.2764856,1.3113455,1.3728349 +2025-08-01,1.2421811,1.2292916,1.1043029,1.1598811,1.1929151,1.2164187,1.2421811,1.266998,1.2924675,1.3304923,1.394975 +2025-09-01,1.269735,1.2603163,1.1239274,1.1872942,1.2162873,1.242951,1.269735,1.2976494,1.3236798,1.3580028,1.4252181 +2025-10-01,1.2496669,1.2436218,1.0962446,1.1630417,1.1938806,1.221712,1.2496669,1.2757387,1.3041382,1.3376691,1.409816 +2025-11-01,1.2135266,1.2031629,1.0545524,1.1223581,1.157211,1.1864623,1.2135266,1.2447275,1.2706528,1.3091388,1.3801678 +2025-12-01,1.2034141,1.1867243,1.0412307,1.1113251,1.1433371,1.1752325,1.2034141,1.2308054,1.2621957,1.2912495,1.370078 diff --git a/timesfm-forecasting/examples/global-temperature/output/forecast_output.json b/timesfm-forecasting/examples/global-temperature/output/forecast_output.json index d1bd036..4919e17 100644 --- a/timesfm-forecasting/examples/global-temperature/output/forecast_output.json +++ b/timesfm-forecasting/examples/global-temperature/output/forecast_output.json @@ -1,5 +1,5 @@ { - "model": "TimesFM 1.0 (200M) PyTorch", + "model": "TimesFM 2.5 (200M) PyTorch", "input": { "source": "NOAA GISTEMP Global Temperature Anomaly", "n_observations": 36, @@ -23,166 +23,166 @@ "2025-12" ], "point": [ - 1.25933837890625, - 1.285666823387146, - 1.2950127124786377, - 1.2207623720169067, - 1.170255422592163, - 1.1455552577972412, - 1.1702347993850708, - 1.2026824951171875, - 1.1909748315811157, - 1.1490840911865234, - 1.080478549003601, - 1.0613453388214111 + 1.2223774194717407, + 1.2563583850860596, + 1.286476969718933, + 1.240488052368164, + 1.202637791633606, + 1.2100199460983276, + 1.2253109216690063, + 1.2421810626983643, + 1.2697349786758423, + 1.2496669292449951, + 1.2135266065597534, + 1.2034140825271606 ], "quantiles": { + "mean": [ + 1.2215943336486816, + 1.25020170211792, + 1.281691551208496, + 1.240675449371338, + 1.1969143152236938, + 1.1963895559310913, + 1.215125322341919, + 1.229291558265686, + 1.260316252708435, + 1.243621826171875, + 1.2031629085540771, + 1.186724305152893 + ], "10%": [ - 1.2481880187988281, - 1.2773758172988892, - 1.286991834640503, - 1.2084007263183594, - 1.1533130407333374, - 1.1275498867034912, - 1.1510555744171143, - 1.1859495639801025, - 1.1784849166870117, - 1.1264795064926147, - 1.0624356269836426, - 1.036609172821045 + 1.1230627298355103, + 1.1482248306274414, + 1.1694773435592651, + 1.119297981262207, + 1.0776878595352173, + 1.0811386108398438, + 1.0917633771896362, + 1.1043028831481934, + 1.123927354812622, + 1.0962445735931396, + 1.054552435874939, + 1.0412306785583496 ], "20%": [ - 1.1407020092010498, - 1.1406043767929077, - 1.126852035522461, - 1.0352504253387451, - 0.9691494703292847, - 0.9420379400253296, - 0.9503718018531799, - 0.970925509929657, - 0.9594371318817139, - 0.9079477190971375, - 0.8361266255378723, - 0.8022069334983826 + 1.161399483680725, + 1.1891648769378662, + 1.2141375541687012, + 1.168922781944275, + 1.1280149221420288, + 1.1352498531341553, + 1.1474796533584595, + 1.1598811149597168, + 1.1872942447662354, + 1.1630417108535767, + 1.1223580837249756, + 1.1113251447677612 ], "30%": [ - 1.1880751848220825, - 1.1960833072662354, - 1.187617301940918, - 1.104191780090332, - 1.0431063175201416, - 1.01105535030365, - 1.0347577333450317, - 1.0594383478164673, - 1.040370225906372, - 0.9952926635742188, - 0.9259791970252991, - 0.8952187299728394 + 1.18324875831604, + 1.2134028673171997, + 1.2430503368377686, + 1.195752739906311, + 1.1554758548736572, + 1.1687461137771606, + 1.1764130592346191, + 1.1929150819778442, + 1.2162872552871704, + 1.193880558013916, + 1.1572109460830688, + 1.1433371305465698 ], "40%": [ - 1.2137157917022705, - 1.232267141342163, - 1.2349879741668701, - 1.151865005493164, - 1.0932612419128418, - 1.0658776760101318, - 1.084773302078247, - 1.1106674671173096, - 1.1036059856414795, - 1.0548235177993774, - 0.9882403016090393, - 0.9593706727027893 + 1.2030285596847534, + 1.2355917692184448, + 1.263671875, + 1.216238260269165, + 1.1801764965057373, + 1.1853771209716797, + 1.2029412984848022, + 1.216418743133545, + 1.2429510354995728, + 1.2217119932174683, + 1.1864622831344604, + 1.1752325296401978 ], "50%": [ - 1.2394564151763916, - 1.2593891620635986, - 1.267505168914795, - 1.1853008270263672, - 1.127617597579956, - 1.1061187982559204, - 1.128767728805542, - 1.1579902172088623, - 1.1511956453323364, - 1.1052223443984985, - 1.03863525390625, - 1.0152238607406616 + 1.2223774194717407, + 1.2563583850860596, + 1.286476969718933, + 1.240488052368164, + 1.202637791633606, + 1.2100199460983276, + 1.2253109216690063, + 1.2421810626983643, + 1.2697349786758423, + 1.2496669292449951, + 1.2135266065597534, + 1.2034140825271606 ], "60%": [ - 1.25933837890625, - 1.285666823387146, - 1.2950127124786377, - 1.2207623720169067, - 1.170255422592163, - 1.1455552577972412, - 1.1702347993850708, - 1.2026824951171875, - 1.1909748315811157, - 1.1490840911865234, - 1.080478549003601, - 1.0613453388214111 + 1.2409889698028564, + 1.2787916660308838, + 1.3101407289505005, + 1.2653590440750122, + 1.2274072170257568, + 1.2333428859710693, + 1.2509560585021973, + 1.266998052597046, + 1.2976493835449219, + 1.2757387161254883, + 1.2447274923324585, + 1.2308053970336914 ], "70%": [ - 1.27677321434021, - 1.3110136985778809, - 1.3284480571746826, - 1.2566629648208618, - 1.2019660472869873, - 1.1806211471557617, - 1.2114834785461426, - 1.2399210929870605, - 1.2390201091766357, - 1.1897773742675781, - 1.1281580924987793, - 1.1032856702804565 + 1.2626991271972656, + 1.3061506748199463, + 1.336512565612793, + 1.2880773544311523, + 1.2546851634979248, + 1.2559939622879028, + 1.276485562324524, + 1.292467474937439, + 1.323679804801941, + 1.30413818359375, + 1.2706527709960938, + 1.2621957063674927 ], "80%": [ - 1.2971320152282715, - 1.3400218486785889, - 1.3547290563583374, - 1.2898554801940918, - 1.2390310764312744, - 1.2180578708648682, - 1.248227596282959, - 1.2842004299163818, - 1.2832940816879272, - 1.240414023399353, - 1.175971508026123, - 1.153149962425232 + 1.2927711009979248, + 1.3358021974563599, + 1.372582197189331, + 1.3237736225128174, + 1.2890899181365967, + 1.2938039302825928, + 1.3113454580307007, + 1.3304922580718994, + 1.358002781867981, + 1.3376691341400146, + 1.3091387748718262, + 1.2912495136260986 ], "90%": [ - 1.3239599466323853, - 1.3751201629638672, - 1.403548240661621, - 1.3310348987579346, - 1.2891905307769775, - 1.2702757120132446, - 1.2997852563858032, - 1.3408125638961792, - 1.3354730606079102, - 1.286876916885376, - 1.2283769845962524, - 1.2169079780578613 - ], - "99%": [ - 1.3678879737854004, - 1.4253658056259155, - 1.4642648696899414, - 1.40165376663208, - 1.3632389307022095, - 1.3453660011291504, - 1.380732536315918, - 1.4195259809494019, - 1.416972041130066, - 1.3775466680526733, - 1.3122477531433105, - 1.2959520816802979 + 1.3396029472351074, + 1.3880282640457153, + 1.426570177078247, + 1.3806401491165161, + 1.3469674587249756, + 1.3532465696334839, + 1.3728349208831787, + 1.394974946975708, + 1.425218105316162, + 1.409816026687622, + 1.380167841911316, + 1.3700779676437378 ] } }, "summary": { - "forecast_mean_c": 1.186, - "forecast_max_c": 1.295, - "forecast_min_c": 1.061, - "vs_last_year_mean": -0.067 + "forecast_mean_c": 1.235, + "forecast_max_c": 1.286, + "forecast_min_c": 1.203, + "vs_last_year_mean": -0.017 } } \ No newline at end of file diff --git a/timesfm-forecasting/examples/global-temperature/output/forecast_visualization.png b/timesfm-forecasting/examples/global-temperature/output/forecast_visualization.png index 0124703..f965a5a 100644 Binary files a/timesfm-forecasting/examples/global-temperature/output/forecast_visualization.png and b/timesfm-forecasting/examples/global-temperature/output/forecast_visualization.png differ diff --git a/timesfm-forecasting/examples/global-temperature/run_forecast.py b/timesfm-forecasting/examples/global-temperature/run_forecast.py index 8e83587..abd8b6e 100644 --- a/timesfm-forecasting/examples/global-temperature/run_forecast.py +++ b/timesfm-forecasting/examples/global-temperature/run_forecast.py @@ -11,6 +11,7 @@ from pathlib import Path import numpy as np import pandas as pd +import timesfm # Preflight check print("=" * 60) @@ -35,27 +36,28 @@ print( # TimesFM expects a list of 1D numpy arrays input_series = df["anomaly_c"].values.astype(np.float32) -# Load TimesFM 1.0 (PyTorch) -# NOTE: TimesFM 2.5 PyTorch checkpoint has a file format issue at time of writing. -# The model.safetensors file is not loadable via torch.load(). -# Using TimesFM 1.0 PyTorch which works correctly. -print("\n🤖 Loading TimesFM 1.0 (200M) PyTorch...") -import timesfm +# Load TimesFM 2.5 (PyTorch) +print("\n🤖 Loading TimesFM 2.5 (200M) PyTorch...") -hparams = timesfm.TimesFmHparams(horizon_len=12) -checkpoint = timesfm.TimesFmCheckpoint( - huggingface_repo_id="google/timesfm-1.0-200m-pytorch" +model = timesfm.TimesFM_2p5_200M_torch.from_pretrained( + "google/timesfm-2.5-200m-pytorch", + torch_compile=False, ) -model = timesfm.TimesFm(hparams=hparams, checkpoint=checkpoint) +model.compile(timesfm.ForecastConfig( + max_context=512, + max_horizon=12, + normalize_inputs=True, + use_continuous_quantile_head=True, + fix_quantile_crossing=True, +)) # Forecast print("\n📈 Running forecast (12 months ahead)...") forecast_input = [input_series] -frequency_input = [0] # Monthly data point_forecast, experimental_quantile_forecast = model.forecast( - forecast_input, - freq=frequency_input, + horizon=12, + inputs=forecast_input, ) print(f" Point forecast shape: {point_forecast.shape}") @@ -65,9 +67,8 @@ print(f" Quantile forecast shape: {experimental_quantile_forecast.shape}") point = point_forecast[0] # Shape: (horizon,) quantiles = experimental_quantile_forecast[0] # Shape: (horizon, num_quantiles) -# TimesFM quantiles: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.99] -# Index mapping: 0=10%, 1=20%, ..., 4=50% (median), ..., 9=99% -quantile_labels = ["10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "99%"] +# TimesFM 2.5 columns: 0=mean, 1=10%, 2=20%, ..., 5=50% (median), ..., 9=90% +quantile_labels = ["mean", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%"] # Create forecast dates (2025 monthly) last_date = df["date"].max() @@ -80,16 +81,16 @@ output_df = pd.DataFrame( { "date": forecast_dates.strftime("%Y-%m-%d"), "point_forecast": point, - "q10": quantiles[:, 0], - "q20": quantiles[:, 1], - "q30": quantiles[:, 2], - "q40": quantiles[:, 3], - "q50": quantiles[:, 4], # Median - "q60": quantiles[:, 5], - "q70": quantiles[:, 6], - "q80": quantiles[:, 7], - "q90": quantiles[:, 8], - "q99": quantiles[:, 9], + "mean": quantiles[:, 0], + "q10": quantiles[:, 1], + "q20": quantiles[:, 2], + "q30": quantiles[:, 3], + "q40": quantiles[:, 4], + "q50": quantiles[:, 5], # Median + "q60": quantiles[:, 6], + "q70": quantiles[:, 7], + "q80": quantiles[:, 8], + "q90": quantiles[:, 9], } ) @@ -100,7 +101,7 @@ output_df.to_csv(output_dir / "forecast_output.csv", index=False) # JSON output for the report output_json = { - "model": "TimesFM 1.0 (200M) PyTorch", + "model": "TimesFM 2.5 (200M) PyTorch", "input": { "source": "NOAA GISTEMP Global Temperature Anomaly", "n_observations": len(df), @@ -135,24 +136,24 @@ print("=" * 60) print( f"\n📅 Forecast period: {forecast_dates[0].strftime('%Y-%m')} to {forecast_dates[-1].strftime('%Y-%m')}" ) -print(f"\n🌡️ Temperature Anomaly Forecast (°C above 1951-1980 baseline):") -print(f"\n {'Month':<10} {'Point':>8} {'80% CI':>15} {'90% CI':>15}") +print("\n🌡️ Temperature Anomaly Forecast (°C above 1951-1980 baseline):") +print(f"\n {'Month':<10} {'Point':>8} {'60% CI':>15} {'80% CI':>15}") print(f" {'-' * 10} {'-' * 8} {'-' * 15} {'-' * 15}") -for i, (date, pt, q10, q90, q05, q95) in enumerate( +for i, (date, pt, q20, q80, q10, q90) in enumerate( zip( forecast_dates.strftime("%Y-%m"), point, - quantiles[:, 1], # 20% - quantiles[:, 7], # 80% - quantiles[:, 0], # 10% - quantiles[:, 8], # 90% + quantiles[:, 2], # 20% + quantiles[:, 8], # 80% + quantiles[:, 1], # 10% + quantiles[:, 9], # 90% ) ): print( - f" {date:<10} {pt:>8.3f} [{q10:>6.3f}, {q90:>6.3f}] [{q05:>6.3f}, {q95:>6.3f}]" + f" {date:<10} {pt:>8.3f} [{q20:>6.3f}, {q80:>6.3f}] [{q10:>6.3f}, {q90:>6.3f}]" ) -print(f"\n📊 Summary Statistics:") +print("\n📊 Summary Statistics:") print(f" Mean forecast: {point.mean():.3f}°C") print( f" Max forecast: {point.max():.3f}°C (Month: {forecast_dates[point.argmax()].strftime('%Y-%m')})" @@ -162,6 +163,6 @@ print( ) print(f" vs 2024 mean: {point.mean() - df['anomaly_c'].iloc[-12:].mean():+.3f}°C") -print(f"\n✅ Output saved to:") +print("\n✅ Output saved to:") print(f" {output_dir / 'forecast_output.csv'}") print(f" {output_dir / 'forecast_output.json'}") diff --git a/timesfm-forecasting/examples/global-temperature/visualize_forecast.py b/timesfm-forecasting/examples/global-temperature/visualize_forecast.py index 86e7d72..500badc 100644 --- a/timesfm-forecasting/examples/global-temperature/visualize_forecast.py +++ b/timesfm-forecasting/examples/global-temperature/visualize_forecast.py @@ -16,6 +16,8 @@ from __future__ import annotations import json from pathlib import Path +import matplotlib +matplotlib.use("Agg") import matplotlib.pyplot as plt import numpy as np import pandas as pd @@ -57,11 +59,11 @@ def main() -> None: label="Historical (NOAA GISTEMP)", ) - # Plot 90% CI (outer band) - ax.fill_between(dates, q10, q90, alpha=0.2, color="#dc2626", label="90% CI") + # Plot 80% CI (outer band) + ax.fill_between(dates, q10, q90, alpha=0.2, color="#dc2626", label="80% CI") - # Plot 80% CI (inner band) - ax.fill_between(dates, q20, q80, alpha=0.3, color="#dc2626", label="80% CI") + # Plot 60% CI (inner band) + ax.fill_between(dates, q20, q80, alpha=0.3, color="#dc2626", label="60% CI") # Plot point forecast ax.plot(