Describe the bug
Second-pass umbrella from the same full-package review as #409–#424: remaining verified findings that didn't fit the earlier issues.
TaskModel.train_features is never initialized in __init__ (deeptab/training/lightning_module.py — only assigned in setup(stage="fit") at line 284). The guards at lines 449, 504, 538 read ... and self.train_features is not None, which presumes a None default — instead they raise AttributeError: 'TaskModel' object has no attribute 'train_features' on any TabR/ModernNCA TaskModel that runs validate/predict without a prior in-process fit (e.g. restored from a checkpoint). Fix: self.train_features = self.train_targets = None in __init__.
_validate_fit_inputs checks phantom distribution-family keys (deeptab/models/base.py:99-103): it gates on "inversegaussian" and "binomial", neither of which exists in DISTRIBUTION_REGISTRY (deeptab/distributions/registry.py:19-34 — the real key is "inversegamma", and there is no binomial family). Consequently the strictly-positive target check never fires for inversegamma (or lognormal, which also needs it), and the binary check is dead code.
- Generic-family CRPS fallback derives sigma from the test labels (
deeptab/metrics/distributional.py:231): sig=np.std(y_true - loc) uses the ground truth to set the predictive spread, making scores incomparable across test sets; with perfect predictions sig=0 degenerates inside properscoring. The fallback should use a model-predicted scale or be explicit (e.g. report MAE like the ImportError branch).
sparsemax mutates its input in place (deeptab/nn/blocks/common.py:86, input_ -= max_val). Via ODST (deeptab/nn/blocks/node.py:321-322) the input is the feature_selection_logits Parameter itself, so the stored parameter values get shifted by -max on first use — output is shift-invariant so predictions stay correct, but weight decay operates on corrupted values and checkpoints differ from what was trained. Fix: input_ = input_ - max_val.
MambaOriginal.allocate_inference_cache iterates a nonexistent attribute (deeptab/nn/blocks/mamba.py:729-733): loops self.layers, but the class defines only fwd_layers/bckwd_layers → guaranteed AttributeError if ever called.
- TabTransformer crashes on datasets with zero numerical features (
deeptab/architectures/tabtransformer.py:135): torch.cat(num_features, dim=1) on an empty list raises RuntimeError: torch.cat(): expected a non-empty list of Tensors — an all-categorical dataset (TabTransformer's home turf) needs a guard.
fit(rebuild=False) silently discards loss/sampler settings: SklearnBaseClassifier.fit recomputes loss_fct/sampler from class_weight/balanced_sampler/sample_weight (classifier_base.py:297-299), but the fit mixin passes them only into _build_model(...), which is skipped when rebuild=False (_mixins/fit.py:445-469) — the freshly requested class weights/sampling are ignored without warning.
To Reproduce
Each item lists its file/line; representative snippets:
# (1)
from deeptab.training.lightning_module import TaskModel
# restore a TabR TaskModel from checkpoint, then trainer.validate(...) -> AttributeError
# (2)
from deeptab.distributions.registry import DISTRIBUTION_REGISTRY
print("inversegaussian" in DISTRIBUTION_REGISTRY, "binomial" in DISTRIBUTION_REGISTRY) # False False
# (5)
from deeptab.nn.blocks.mamba import MambaOriginal # has fwd_layers/bckwd_layers, no .layers
Expected behavior
Guards should compare against initialized attributes; validation key sets should match the registry; metrics must not read the ground truth to parameterize themselves; functional ops must not mutate parameters in place; public methods should reference existing attributes; architectures should validate unsupported feature combinations at build time; ignored fit arguments should raise or warn.
Screenshots
n/a
Desktop (please complete the following information):
- OS: macOS (Darwin 25.5.0, arm64)
- Python version: 3.11.15
- deeptab Version: 2.0.0 (main @ 4e6a359)
Additional context
Doc-only nits noticed on the way (not bugs, listing for a docs sweep rather than separate tickets): MLPConfig docstring documents a nonexistent skip_layers field; several config docstrings contain "default=field(default_factory=list" copy-paste artifacts; NormalDistribution's second parameter is named "variance" but is used as the std/scale in dist.Normal(mean, variance) — consistent internally, but the name invites mis-setting by users.
Describe the bug
Second-pass umbrella from the same full-package review as #409–#424: remaining verified findings that didn't fit the earlier issues.
TaskModel.train_featuresis never initialized in__init__(deeptab/training/lightning_module.py— only assigned insetup(stage="fit")at line 284). The guards at lines 449, 504, 538 read... and self.train_features is not None, which presumes aNonedefault — instead they raiseAttributeError: 'TaskModel' object has no attribute 'train_features'on any TabR/ModernNCA TaskModel that runsvalidate/predictwithout a prior in-processfit(e.g. restored from a checkpoint). Fix:self.train_features = self.train_targets = Nonein__init__._validate_fit_inputschecks phantom distribution-family keys (deeptab/models/base.py:99-103): it gates on"inversegaussian"and"binomial", neither of which exists inDISTRIBUTION_REGISTRY(deeptab/distributions/registry.py:19-34— the real key is"inversegamma", and there is no binomial family). Consequently the strictly-positive target check never fires forinversegamma(orlognormal, which also needs it), and the binary check is dead code.deeptab/metrics/distributional.py:231):sig=np.std(y_true - loc)uses the ground truth to set the predictive spread, making scores incomparable across test sets; with perfect predictionssig=0degenerates insideproperscoring. The fallback should use a model-predicted scale or be explicit (e.g. report MAE like the ImportError branch).sparsemaxmutates its input in place (deeptab/nn/blocks/common.py:86,input_ -= max_val). Via ODST (deeptab/nn/blocks/node.py:321-322) the input is thefeature_selection_logitsParameter itself, so the stored parameter values get shifted by-maxon first use — output is shift-invariant so predictions stay correct, but weight decay operates on corrupted values and checkpoints differ from what was trained. Fix:input_ = input_ - max_val.MambaOriginal.allocate_inference_cacheiterates a nonexistent attribute (deeptab/nn/blocks/mamba.py:729-733): loopsself.layers, but the class defines onlyfwd_layers/bckwd_layers→ guaranteedAttributeErrorif ever called.deeptab/architectures/tabtransformer.py:135):torch.cat(num_features, dim=1)on an empty list raisesRuntimeError: torch.cat(): expected a non-empty list of Tensors— an all-categorical dataset (TabTransformer's home turf) needs a guard.fit(rebuild=False)silently discards loss/sampler settings:SklearnBaseClassifier.fitrecomputesloss_fct/samplerfromclass_weight/balanced_sampler/sample_weight(classifier_base.py:297-299), but the fit mixin passes them only into_build_model(...), which is skipped whenrebuild=False(_mixins/fit.py:445-469) — the freshly requested class weights/sampling are ignored without warning.To Reproduce
Each item lists its file/line; representative snippets:
Expected behavior
Guards should compare against initialized attributes; validation key sets should match the registry; metrics must not read the ground truth to parameterize themselves; functional ops must not mutate parameters in place; public methods should reference existing attributes; architectures should validate unsupported feature combinations at build time; ignored fit arguments should raise or warn.
Screenshots
n/a
Desktop (please complete the following information):
Additional context
Doc-only nits noticed on the way (not bugs, listing for a docs sweep rather than separate tickets):
MLPConfigdocstring documents a nonexistentskip_layersfield; several config docstrings contain "default=field(default_factory=list" copy-paste artifacts;NormalDistribution's second parameter is named "variance" but is used as the std/scale indist.Normal(mean, variance)— consistent internally, but the name invites mis-setting by users.