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.
Describe the bug
fit()documentsy : array-like, shape (n_samples,) or (n_samples, n_targets), but passing thedocumented 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 flatteningy,so a
(n, 1)target becomes a(B, 1, 1)label tensor against(B, 1)predictions.MSELosshappilybroadcasts that to
(B, B, 1)and optimises an all-pairs objective — the per-row pairing betweenprediction and target is lost entirely.
BCEWithLogitsLossrefuses to broadcast and raises.I reproduced this independently: same data, same seed, only the
yshape differs —R2 = 0.9963 with 1-D
y, R2 = 0.0023 withy.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
Expected behavior
A
(n, 1)target must train identically to a 1-D target.setup()should ravel/reshapeyto 1-Dbefore applying
unsqueeze(dim=1)(or usereshape(-1, 1)instead ofunsqueeze).Note this also corrupts
val_loss, so early stopping and checkpoint selection are driven by the wrongnumber too.
Screenshots
n/a
Desktop (please complete the following information):
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.