Skip to content

[BUG] Second-pass findings: uninitialized train_features guard, phantom family keys in validation, CRPS fallback uses test labels, sparsemax in-place mutation, and more #426

Description

@ChrisW09

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.

  1. 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__.
  2. _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.
  3. 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).
  4. 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.
  5. 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.
  6. 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.
  7. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions