Skip to content

fix(classifier, regressor): build the feature schema from the column count - #1349

Open
aniruddhaadak80 wants to merge 3 commits into
PriorLabs:mainfrom
aniruddhaadak80:fix/num-features-schema-width
Open

aniruddhaadak80 wants to merge 3 commits into
PriorLabs:mainfrom
aniruddhaadak80:fix/num-features-schema-width

Conversation

@aniruddhaadak80

Copy link
Copy Markdown
Contributor

Issue

Fixes #1344

Motivation and Context

it_from_preprocessed builds the categorical feature schema from the row count:

python feature_schema = convert_batch_of_cat_ix_to_schema( batch_of_cat_indices=cat_ix, num_features=X_preprocessed[0].shape[1], )

X_preprocessed[0] is the collated batch of shape (n_datasets, n_train_rows, n_columns) - _collate_list_field ( inetuning/data_util.py) stacks the 2-D (rows, cols) tensors. shape[1] is therefore
_train_rows, while the column count is shape[-1].

convert_batch_of_cat_ix_to_schema (utils.py) builds
ange(num_features) Features and marks index i categorical iff i in cat_indices. With the wrong width, a categorical column at an index >= n_train_rows is absent from the schema, so indices_for(FeatureModality.CATEGORICAL) silently omits it and the model is told the column is numerical.

This affected both estimators: TabPFNClassifier.fit_from_preprocessed and TabPFNRegressor.fit_from_preprocessed.

On the observable impact, stated honestly: this is currently latent rather than numerically visible. Every architecture opens its orward with del categorical_inds, so the argument is ignored on today's models and the two public prediction paths do not disagree on probabilities. What is wrong today is the metadata handed to the model, and that is exactly what an architecture honouring categorical_inds would depend on. A secondary cost is allocation:
um_features = n_train_rows allocates one Feature per training row per estimator per dataset, so fine-tuning a 50k-row chunk built 50k dataclasses where ~10 were needed.

Public API Changes

  • No Public API changes
  • Yes, Public API changes (Details below)

No signature changes. The categorical metadata the batched engine passes to the model becomes correct.

How Has This Been Tested?

New tests in ests/test_classifier_interface.py and ests/test_regressor_interface.py:

  • est__fit_from_preprocessed__feature_schema_spans_the_columns - asserts the schema width equals the column count (8) and not the row count (5), for both estimators.
  • est__predict_proba_batched__tells_the_model_about_high_categorical_columns - drives the real predict_proba_batched with a randomly-initialised model (the pattern already used in ests/test_batched_members.py, so no checkpoint download) and asserts the categorical_inds reaching the model match those the standard predict_proba path sends.

Both fail on the unfixed source and pass with the fix:

`

with the two source lines reverted

E AssertionError: assert 5 == 8
E AssertionError: assert [[[]]] == [[[226]]]
2 failed, 181 deselected
`

With the fix:

$ python -m pytest tests/test_classifier_interface.py tests/test_regressor_interface.py \ -k "feature_schema_spans_the_columns or high_categorical_columns" -q 4 passed, 365 deselected in 32.47s

uff check and
uff format --check are clean on both changed files. Several pre-existing tests in these two files fail on this machine with TabPFNLicenseError (no TABPFN_TOKEN, no cached checkpoint); the identical failures occur on the base commit, so they are environmental and unrelated.

Checklist

  • The changes have been tested locally.
  • Documentation has been updated (if the public API or usage changes).
  • A changelog entry has been added (see changelog/README.md), or "no changelog needed" label requested.
  • The code follows the project's style guidelines.
  • I have considered the impact of these changes on the public API.

…count

X_preprocessed is the collated batch of shape (n_datasets, n_train_rows,
n_columns), so the column count is the last dimension. Both estimators passed
the row count, which built a schema of the wrong width and dropped every
categorical column whose index was >= the number of training rows.
Copilot AI balanced review requested due to automatic review settings October 2, 2026 06:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8d03324. Configure here.

Comment thread changelog/1349.fixed.md Outdated
Comment thread src/tabpfn/classifier.py Outdated
@aniruddhaadak80

Copy link
Copy Markdown
Contributor Author

Thanks - both points taken. Fixed in d0c85c0.

  • changelog/1349.fixed.md now describes the change from the user's perspective (schema built from the number of features) and no longer narrates the incident or the old symbols.
  • The comments in classifier.py and
    egressor.py are trimmed to the factual note about the collated batch shape; the paragraph describing the previous behaviour is gone.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fit_from_preprocessed builds the feature schema from the row count, not the column count

2 participants