Make the PyTorch model picklable (module-level gelu activation)#47
Merged
Conversation
`get_activation("gelu")` returned a lambda, stored as `MLP.act` on the
encode/decode heads, so pickling the model raised
`Can't pickle local object 'get_activation.<locals>.<lambda>'`.
AutoGluon / TabArena save the fitted estimator (which holds the model)
with stdlib pickle, so the model must be picklable. Promote the gelu
activation to a module-level `_gelu_tanh` (picklable by reference, and
numerically identical to the previous lambda).
Add pickle round-trip tests for the classifier and regressor models,
plus a forward-output equivalence check after unpickling.
weihaokong
requested review from
abhidas,
erzel,
rajatsen91,
siriuz42 and
tamannarayan
as code owners
July 4, 2026 05:07
siriuz42
approved these changes
Jul 6, 2026
weihaokong
pushed a commit
that referenced
this pull request
Jul 6, 2026
The first predict memoizes nnx.jit-compiled step functions on the estimator instance (_predict_step_compiled_with_cat / _predict_step_compiled_no_cat in _batch_forward). Those closures cannot be pickled, so saving a fitted TabFMClassifier/TabFMRegressor with stdlib pickle crashes with "Can't pickle local object '..._batch_forward.<locals>._predict_step_fn'" once predict has run. That is the AutoGluon / TabArena save path (they pickle the fitted estimator), same motivation as the PyTorch-side fix in #47. Drop the memoized functions from __getstate__ on both estimators: they are pure caches and are rebuilt lazily on the next predict. Restored estimators produce identical predictions.
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.
Problem
Pickling the PyTorch
TabFMmodel fails:get_activation("gelu")returns a lambda, which is stored asself.acton the geluMLPheads. The transformer FFNs use swiglu (F.silu, picklable), but every model carries at least one geluMLP— the y-encoder and decoder (the regression path also adds a y-embedder) — so the model as a whole is unpicklable.This matters because AutoGluon / TabArena save the fitted estimator (which holds the model) with stdlib pickle. TabArena currently works around it with a custom
__getstate__/__setstate__that drops and reloads the network (autogluon/tabarena#434); this fix lets that workaround be removed.Repro on
main:Fix
Promote the gelu activation to a module-level
_gelu_tanhfunction. Module-level functions pickle by reference; the computation is unchanged (F.gelu(x, approximate="tanh")), so weights and outputs are identical — this is purely a serialization fix.Testing
Adds
PyTorchModelPickleTest(torch-only): classifier and regressor model pickle round-trips, plus a forward-output equivalence check after unpickling. The regressor case exercises all three geluMLPheads.pytest tabfm/src/classifier_and_regressor_pytorch_test.pypasses (5 tests).