TabPFN for NIRS — fixed recipe (no HPO)
TL;DR
from nirs4all.operators.models import TabPFNNIRSRegressor
est = TabPFNNIRSRegressor(n_estimators=16)
est.fit(X_train, y_train)
y_pred = est.predict(X_test)
One TabPFN training per dataset. No hyperparameter tuning, no preprocessing CV, no stacking. The preprocessing pipeline is fixed and was selected empirically on a 57-dataset NIRS regression cohort.
On that cohort the median test-set RMSE lands at ~+2.5 % relative to
the TabPFN paper’s 72-chain per-dataset HPO baseline TabPFN_opt —
making this the simplest recipe that gets close to the paper without
the HPO cost.
What does it do?
TabPFNNIRSRegressor chains the following steps inside fit:
Step |
Operator |
Why |
|---|---|---|
1 |
|
Smoothing + first derivative — the most universally robust SG kernel in the TabPFN-paper winners |
2 |
|
Removes one orthogonal-to-y signal direction. Net win on the cohort (helps more than it hurts) |
3 |
|
TabPFN v3 has a 2000-feature hard limit. Uniformly-spaced column indices keep spectral structure |
4 |
|
Center, do not scale (TabPFN is scale-aware) |
5 |
|
TabPFN ensemble. |
At predict time the same pipeline is applied to X_test (re-using the
fitted SG / OSC / scaler / subsample indices).
When to use this vs the alternatives
Recipe |
When |
Cost per dataset |
|---|---|---|
|
Default choice. No HPO available, want to match |
1× TabPFN |
|
Need absolute best test RMSE, willing to pay the HPO cost |
~72× TabPFN |
Mini-HPO over 5 chains via TabPFN-OOF |
More accurate than fixed recipe on some datasets, but introduces new catastrophes on SPXY / Kennard-Stone splits (OOF picks the wrong chain when train/test distributions diverge) |
~26× TabPFN |
|
Pure linear baselines, no GPU needed, interpretable |
~1× PLS |
|
Lots of data (n > 1000), heavy non-linearity |
Long training |
Hyperparameter knobs
Parameter |
Default |
What it does |
|---|---|---|
|
|
TabPFN ensemble size at fit time. 4 is faster but noisier; 16 is the empirical sweet spot |
|
|
TabPFN v3 cap. Wider spectra get uniformly subsampled |
|
|
SG smoothing window |
|
|
SG polynomial order |
|
|
SG derivative order |
|
|
Number of OSC directions to remove |
|
|
Seed for TabPFN |
|
|
TabPFN device. |
|
|
TabPFN checkpoint. |
After .fit, three book-keeping attributes are populated:
n_features_in_— number of input features seen at fit timen_features_used_— number of features actually fed to TabPFN (after themax_featurescap)subsample_idx_— column indices used (orNonewhen no subsample)
Performance on a 57-dataset NIRS cohort
The recipe was validated on the full TabPFN-paper regression cohort (60 datasets minus a few too-large ones for HPO comparisons):
Metric |
Value |
|---|---|
Median test RMSE Δ vs |
~+2.5 % |
Wins vs |
~21 / 57 datasets |
Worst case |
+47 % on |
Datasets within ±10 % of |
majority |
The full sweep is in bench/nicon_v2/ (see test_full_50_results.csv
and test_minihpo5_untested_results.csv for raw numbers).
Use in a nirs4all.run pipeline
import nirs4all
from sklearn.model_selection import ShuffleSplit
from nirs4all.operators.models import TabPFNNIRSRegressor
result = nirs4all.run(pipeline=[
ShuffleSplit(n_splits=3, test_size=0.2, random_state=42),
{"model": TabPFNNIRSRegressor(n_estimators=16)},
], dataset="path/to/data", verbose=1)
print(f"best RMSE: {result.best_rmse:.4f}")
The estimator is sklearn-compatible (get_params, set_params,
clone), so it integrates with nirs4all’s cross-validation,
hyperparameter search, and result-tracking machinery the same way as
any other regressor.
Requirements
Install the optional dependency:
pip install tabpfnA GPU is strongly recommended for
n_train > 200. TabPFN runs on CPU but is much slower (the import is done lazily insidefit, so the module itself imports without the dependency).
Known limitations
On a handful of datasets (Brix_spxy70, Corn_Oil_80, Biscuit_Sucrose, TIC_spxy70, DIESEL_hlb-a) the fixed recipe is +20 to +47 % above
TabPFN_opt. These are typicallyn_train < 100with very specific per-dataset preprocessing requirements that no fixed recipe can match.On SPXY / Kennard-Stone test splits where the test set is designed to be far from the train set, cheap CV-based preprocessing selectors (the alternative to this fixed recipe) frequently mis-rank chains. Sticking with the fixed recipe is safer on those splits.
See also
Hyperparameter Tuning — when you want per-dataset HPO instead
Preprocessing — building custom chains
Operator catalog: choose a job, then inspect the method — all available transforms
examples/user/04_models/U06_tabpfn_nirs.py— runnable walkthrough