Skip to content

[BUG] Config-gated crashes and broken public nn blocks across architectures #423

Description

@ChrisW09

Describe the bug
Umbrella issue: non-default config options and importable nn blocks that crash or are silently broken. None of these fire with default configs, which is why the test suite is green — but each is reachable through documented options or public imports.

Config-gated, in shipped architectures:

  1. TabM norm=... crashes at construction (deeptab/architectures/tabm.py:102-104): get_normalization_layer(config) returns an instance (deeptab/nn/normalization.py:28-38), then self.norm_f(self.hparams.layer_sizes[0]) calls its forward with an int.
  2. TabM batch_norm=True applies BatchNorm1d(layer_sizes[0]) to the 3-D (B, ensemble, F) output of LinearBatchEnsembleLayer (tabm.py:99-100) → shape error (or wrong-axis normalization if ensemble_size == layer_sizes[0]). Similar latent mismatch for use_glu=True (halved feature dim vs next layer's in_features; also in architectures/mlp.py/tangos.py).
  3. norm_first=True is silently ignored: CustomTransformerEncoderLayer.forward hardcodes post-norm ordering (deeptab/nn/blocks/transformer.py:61-74). FTTransformer additionally applies the same LayerNorm instance twice (as the encoder's final norm and again after pooling, architectures/ft_transformer.py:73-113).
  4. Custom BatchNorm crashes on 3-D input in training mode (deeptab/nn/blocks/common.py:220-233, verified: RuntimeError: output with shape [16] doesn't match the broadcast shape [6, 16]); InstanceNorm/GroupNorm require 4-D input — none usable inside sequence-shaped residual blocks despite being offered via norm=.
  5. shuffle_embeddings=True breaks checkpoint reload (architectures/mambular.py:83, mambattention.py:102): self.perm = torch.randperm(...) is not a registered buffer, so a reloaded model draws a new permutation and predicts garbage with no error.
  6. Trompt's init_rec is uninitialized memory (architectures/experimental/trompt.py:63): nn.Parameter(torch.empty(P, d_model)) with no nn.init — can start as huge values/NaNs; unreproducible under any seed.
  7. Single-class y silently builds an MSE-regression TaskModel (num_classes=1 falls into the regression loss branch) and predict then does classes_[1] on a length-1 array → IndexError. A clear "need at least 2 classes" error at fit would be right.
  8. NODE tree_dim > 1: ODSTE's response einsum sums over the response channel (nn/blocks/node.py:685) and architectures/node.py:86-87 sizes the head ignoring tree_dim (crash if ≠ 1).

Importable-but-broken blocks (no shipped architecture uses them, but they're public):
9. RotaryTransformerEncoderLayer creates fresh random QKV/out projections on every forward (transformer.py:684, 701) — untrained, unreproducible eval, while the parent's self_attn weights sit unused in the optimizer.
10. MultiHeadAttentionBatchEnsemble.process_projection scrambles sequence/ensemble axes for non-ensembled projections (common.py:1392-1398): view(N*E, S, D) on an (N, S, E, D) tensor + view(N, E, S, D).permute(...) reinterprets row order (verified numerically, max abs deviation 2.40 from the correct projection). Default batch_ensemble_projections=["query"] sends key/value/out through this path.
11. PositionalInvariance sub-modules crash on construction/forward (common.py:343-367): in-place assignment on a grad-leaf Parameter, shape-incompatible broadcasts unless seq_len == d_model, and the "conv" variant changes sequence length.
12. mLSTM/sLSTM blocks (common.py:1578-1620, 1743-1780, reachable via TabulaRNN(model_type="mLSTM"/"sLSTM")): the recurrent state is re-zeroed every call (stored shape (1,1,H) never matches B), and batch-global reductions (torch.max(nt * q), mean over batch) make each sample's output depend on the other rows in its inference batch.
13. AutoInt: kv_compression builds parameters that forward never uses (silent no-op, dead parameters), and the interaction layer lacks the reference implementation's ReLU (architectures/autoint.py:84-174).
14. BaseModel.encode() / pool_sequence("cls"): encode's no-grad branch has a stray embeddings = self.encoder(x) line (core/base_model.py:269-271) — AttributeError for Mamba models (no encoder attribute), double compute for the rest; CLS pooling always takes token 0 while EmbeddingLayer supports cls_position=1 (append at end) — silently pools an ordinary feature token.

To Reproduce
Each item lists its file/line; the config-gated ones reproduce by constructing the model with the named option, e.g.:

from deeptab.configs.models import TabMConfig
from deeptab.models import TabMRegressor
TabMRegressor(model_config=TabMConfig(norm="LayerNorm")).build_model(X, y)  # item 1 crash

Expected behavior
Options exposed in configs should either work or be rejected at construction; public blocks should be functional or moved out of the public namespace. A parametrized smoke test sweeping the config-gated options (norm, batch_norm, use_glu, norm_first, shuffle_embeddings + save/load, tree_dim) would pin all of these.

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
Items 4, 10, 11 verified by execution; the rest by close reading during a full-package review. Happy to split any of these out into standalone issues if they're being picked up separately.

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