Repository navigation
Conversation
Loading TimesFM 3 from a local .safetensors or .pth file goes through _make_torch_model, which built the default transformer with use_rope_var=True. The published google/timesfm-3.0-pytorch config.json, TimesFM3Torch's own defaults and the MLX backend all use use_rope_var=False. RoPE has no weights, so load_state_dict accepts the checkpoint and the model then rotates variate-attention queries and keys by the variate index, which the weights were never trained with. Univariate forecasts are unaffected (one variate sits at RoPE position 0), but multivariate and covariate forecasts diverge from the Hub-loaded model: with 4 to 16 past-future covariates the max deviation reaches 5 to 16 on a base-level-20 series and MAE is 2.2x to 2.8x worse. Use use_rope_var=False and add a regression test that pins the local-file architecture to the published config.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Loading TimesFM 3 from a local weights file (
checkpoint_path="/path/model.safetensors"or a.pth) builds a model whose variate attention applies RoPE. The published checkpoint was trained without it. The weights load without any error, univariate forecasts are unchanged, and multivariate and covariate forecasts silently drift from the Hub-loaded model.Problem
_init_modelsends Hub ids and local directories throughTimesFM3Torch.from_pretrained, which readsconfig.json. A local.safetensors/.pthfile has no config, so_make_torch_modelsupplies the architecture, and its defaultTransformerConfighasuse_rope_var=True. Everything else usesFalse:google/timesfm-3.0-pytorch'sconfig.json("use_rope_var": false), theTimesFM3Torch.__init__defaults and the MLX backend (mlx/configs.py). RoPE has no weights, soload_state_dictsucceeds. With one variate, RoPE sits at position 0, where it is the identity, which is why univariate checks did not catch this.flowchart LR A["checkpoint_path"] --> B{"local file?"} B -- "Hub id / directory" --> C["TimesFM3Torch.from_pretrained<br/>reads config.json<br/>use_rope_var=False"] B -- ".safetensors / .pth" --> D["_make_torch_model defaults"] D -- "before" --> E["use_rope_var=True<br/>variate attention rotated by variate index"] D -- "after" --> F["use_rope_var=False<br/>same model as the Hub path"]Reproduction (CPU, real weights)
The same
model.safetensorsis loaded twice: once from the Hub id and once from the local file path, and the two forecasts are compared. The 4/8/16-covariate cases are 256-step contexts at base level 20 plus K step covariates, horizon 48.Before (master e51928e):
After (this PR):
Using the Hub model as the reference, the local-file model was 2.2x to 2.8x worse in MAE once a few covariates were present. That is consistent with the weights having been trained without variate RoPE. The benchmark scripts take
checkpoint_pathfromTIMESFM3_CHECKPOINT(for exampletimesfm3-usage/benchmarks/gift_eval/evaluate_all_gift.py), so pointing that variable at a downloaded weights file hits this path.The issue has been there since the initial torch code (0548f53).
Change
src/timesfm3/torch/timesfm3_forecaster.py:use_rope_var=Falsein_make_torch_model's defaultTransformerConfig, with a comment tying it to the published config.src/timesfm3/torch/timesfm3_forecaster_test.py:test_local_checkpoint_file_uses_published_architecturemocksTimesFM3Torchand asserts the default local-file architecture hasuse_rope_seq=Trueanduse_rope_var=False. It needs no weights. It fails on master withAssertionError: True is not falseand passes with the fix.A checkpoint trained with variate RoPE can still be loaded by passing
transformer_configexplicitly, as any non-default architecture requires today.Testing
Run on Windows 11, CPU only, with Python 3.11.9 and torch 2.15.0+cpu:
python -m pytest -q src/timesfm3/torch: 50 passedruff checkon both files: clean.ruff format --diffreports no changes in the lines this PR touches.google/timesfm-3.0-pytorchon CPU.Not run: the MLX tests (macOS only) and anything on GPU.