Skip to content

[BUG] A (n,1) column-vector y silently trains against a broadcast all-pairs loss (R2 0.996 -> 0.002) #441

Description

@ChrisW09

Describe the bug

fit() documents y : array-like, shape (n_samples,) or (n_samples, n_targets), but passing the
documented 2-D column vector silently destroys training for regression and hard-crashes binary
classification.

TabularDataModule.setup() calls .unsqueeze(dim=1) on the label tensor without first flattening y,
so a (n, 1) target becomes a (B, 1, 1) label tensor against (B, 1) predictions. MSELoss happily
broadcasts that to (B, B, 1) and optimises an all-pairs objective — the per-row pairing between
prediction and target is lost entirely. BCEWithLogitsLoss refuses to broadcast and raises.

I reproduced this independently: same data, same seed, only the y shape differs —
R2 = 0.9963 with 1-D y, R2 = 0.0023 with y.reshape(-1, 1).


y given as a (n,1) column vector produces 3-D labels: regression trains against a broadcast all-pairs loss (R² 0.998 -> 0.017); binary classification hard-crashes

Where:deeptab/data/datamodule.py (273-287)

TabularDataModule.setup() calls .unsqueeze(dim=1) on the label tensor without first flattening y, so a 2-D column-vector y — a shape the fit() docstring explicitly documents as accepted — yields labels of shape (B,1,1) against predictions of shape (B,1). MSELoss then broadcasts to (B,B,1) and optimises an all-pairs objective (the per-row pairing is lost), while BCEWithLogitsLoss refuses to broadcast and raises.

Observed: 1-D labels (64, 1) R2 = 0.998
2-D labels (64, 1, 1) R2 = 0.0165 (identical data, identical seed, only the y shape differs)
Binary classifier with y.reshape(-1,1): ValueError: Target size (torch.Size([8, 1, 1])) must be the same as input size (torch.Size([8, 1])) raised from lightning_module.py:372 during the sanity-check validation pass.
Direct confirmation of the broadcast: with preds p of shape (8,1) and labels p.unsqueeze(-1), F.mse_loss returns 1.3606 where the correct value is 0.0.

Expected: y is documented in fit() as array-like, shape (n_samples,) or (n_samples, n_targets). A (n,1) target should train identically to a 1-D target (R² ≈ 0.998) and must not silently change the objective, and binary classification must not crash. setup() should ravel/reshape y to 1-D before applying unsqueeze(dim=1) (or reshape(-1,1) instead of unsqueeze). The bad loss also corrupts val_loss, hence early stopping and checkpoint selection.

Repro
importwarnings; warnings.simplefilter('ignore')
importnumpyasnp, pandasaspdfromsklearn.metricsimportr2_scorefromdeeptab.modelsimportMLPRegressor, MLPClassifierfromdeeptab.configs.coreimportPreprocessingConfigQ=dict(accelerator='cpu', devices=1, enable_progress_bar=False, enable_model_summary=False, logger=False)
STD=PreprocessingConfig(numerical_preprocessing='standardization')
rng=np.random.default_rng(0); n=80X=pd.DataFrame({'a': rng.normal(size=n), 'b': rng.normal(size=n)})
y=3.0*X['a'].to_numpy() +0.1*rng.normal(size=n)
fortag, yyin (('1-D', y), ('2-D', y.reshape(-1,1))):
m=MLPRegressor(preprocessing_config=STD, random_state=0)
m.fit(X, yy, max_epochs=30, batch_size=16, lr=1e-2, patience=30, **Q)
m._data_module.setup('fit')
print(tag, 'labels', tuple(m._data_module.train_dataset.labels.shape),
'R2', round(r2_score(y, m.predict(X)), 4))
# binary classification: hard crashyb= (rng.random(40) >0.5).astype(int); yb[0]=0; yb[1]=1Xb=pd.DataFrame({'a': rng.normal(size=40), 'b': rng.normal(size=40)})
MLPClassifier().fit(Xb, yb.reshape(-1,1), max_epochs=1, batch_size=16, patience=1, **Q)

Expected behavior
A (n, 1) target must train identically to a 1-D target. setup() should ravel/reshape y to 1-D
before applying unsqueeze(dim=1) (or use reshape(-1, 1) instead of unsqueeze).

Note this also corrupts val_loss, so early stopping and checkpoint selection are driven by the wrong
number too.

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
torch 2.9.1, lightning 2.6.5, scikit-learn 1.9.0, numpy 2.4.6. Found in a second-pass review of v2.0.0
(seven independent lenses, each finding adversarially re-verified by a second reviewer, then re-run by
hand). Distinct from the already-filed #409-#426.

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