Fixing fine-tuning script issues#1068
Conversation
Two issues made `FinetunedTabPFN*` look worse than the base model: 1. Early stopping kept the last (degraded) epoch's weights when no epoch improved over the default model, because the best-model snapshot was only taken on an improvement. Seed it with the default weights so the original model is restored as a floor. 2. Fine-tuning was hardcoded to model version V2.5 while a default TabPFNClassifier/TabPFNRegressor now loads V3, so comparing them mixed model generations. Fine-tuning now defaults to the package default version (settings.tabpfn.model_version) and accepts an optional `model_version`. Adds regression tests for both; existing finetuning tests are pinned to V2.5 since they mock the V2.5 architecture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the fine-tuning wrappers to dynamically target the package default model version instead of a hardcoded older version, and adds support for an optional model_version override. It also ensures that if fine-tuning with early stopping does not yield any improvement over the default model, the original base weights are restored. The reviewer identified a potential synchronization issue in distributed data parallel (DDP) training where the default model state is only snapshot on the main process, which could lead to weight desynchronization across ranks if no epoch improves; they suggested seeding the snapshot on all ranks instead.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Move it out of the _fit closure so it's a reusable helper that takes the model explicitly, alongside the other module-level finetuning helpers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The finetuning tests mock the V2.5 architecture (`TabPFNV2p5`) for speed, so they must run on V2.5 now that fine-tuning defaults to the package default version (V3). Replace the scattered `model_version=ModelVersion.V2_5` literals with a single documented `FINETUNE_TEST_MODEL_VERSION` constant in tests/utils.py, and note the spots where the pin isn't strictly required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supersedes the V2.5-pin approach: instead of pinning the fine-tuned version to V2.5 to keep the V2.5-architecture mock valid, repoint the mocks to `TabPFNV3` and drop the pins, so the tests exercise the default version users actually get. Remove the FINETUNE_TEST_MODEL_VERSION constant. Also make the regressor checkpoint test deterministic by mocking `_evaluate_model` with a strictly-improving (decreasing-mse) side effect, the same way the classifier checkpoint tests do — it previously relied on the random mocked forward happening to beat the default model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…path Parametrize the model_version-tracks-default test over FinetunedTabPFNClassifier and FinetunedTabPFNRegressor (it checks shared FinetunedTabPFNBase behavior, so it should run for both). The no-improvement weight-restoration test exercises the same shared `_fit`; add a comment noting the regressor inherits the identical path rather than duplicating the (slow, model-loading) test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The classifier example fine-tuned with lr=2e-5 and the default 10k context, which on the large Higgs dataset improved validation but not the held-out test set. Lower the LR to 1e-5 (also matching the regressor example) and raise the fine-tuning context to 30k so each step sees more of Higgs's 90k train rows — this makes fine-tuned beat the base model on test, not just validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously the default-weights seed was gated on is_main_process, so when no epoch beat the default model, only rank 0 restored the base weights while other ranks kept the last (degraded) epoch — a silent cross-rank desync. best_metric and primary_metric are already broadcast and DDP keeps weights all-reduced, so seeding on every rank makes all ranks restore the same base weights. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix #1064 — fine-tuning silently worse than the base model
What & why
Users reported that
FinetunedTabPFN{Classifier,Regressor}made performance worse, not better. Root cause was two bugs that made fine-tuning look broken regardless of the actual training, plus example hyperparameters that no longer suit the current default model:TabPFNClassifier()/TabPFNRegressor()(the natural baseline, and the examples' "Default"print) loads V3. So users were comparing a fine-tuned older model against a newer base — worse by construction.
— i.e. it could hand back a model strictly worse than the base it started from.
After the fixes, fine-tuning is never silently worse than base, and the shipped examples now improve over base:
0.8211 → 0.8279, log-loss0.5157 → 0.50570.1388 → 0.1363Issue
#1064
Motivation and Context
Public API Changes
How Has This Been Tested?
The script now produces improved results, apart from some internal evaluation.
Checklist
changelog/README.md), or "no changelog needed" label requested.