Describe the bug
NegativeBinomialDistribution uses the scipy success-probability convention with torch's NegativeBinomial, inverting the parameterization (deeptab/distributions/negative_binomial.py:41-44):
r = torch.tensor(1.0) / dispersion
p = r / (r + mean)
dist.NegativeBinomial(total_count=r, probs=p)
torch defines mean = total_count * probs / (1 - probs), so probs must be mean / (r + mean). With the code as written, the constructed distribution's mean is r²/µ instead of µ (verified: µ=10, r=1 gives distribution mean 0.1).
Training still converges — the network simply learns whatever head output makes softplus(head) equal r²/µ — but the "mean" column is then not the mean, so every downstream consumer silently gets garbage: predict() output, mean-based metrics (RMSE/MAE via _extract_mean), and NegativeBinomialDeviance which reads column 0 as µ.
To Reproduce
import torch
import torch.distributions as dist
mu, r = torch.tensor(10.0), torch.tensor(1.0)
print(dist.NegativeBinomial(total_count=r, probs=r / (r + mu)).mean) # 0.1 (= r^2/mu)
print(dist.NegativeBinomial(total_count=r, probs=mu / (r + mu)).mean) # 10.0 (correct)
Expected behavior
probs = mean / (r + mean) (or equivalently logits = log(mean) - log(r)), so that the head column trained as the mean actually parameterizes the mean.
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
The in-code comment # variance = mean + mean^2 / dispersion is also inconsistent with r = 1/dispersion (that choice yields var = µ + µ²·dispersion) — worth clarifying which convention dispersion is meant to follow. NegativeBinomialDeviance in deeptab/metrics/distributional.py interprets its alpha as the size r, while the model's column 1 is dispersion = 1/r under the training code — the two should be reconciled together with this fix.
Describe the bug
NegativeBinomialDistributionuses the scipy success-probability convention with torch'sNegativeBinomial, inverting the parameterization (deeptab/distributions/negative_binomial.py:41-44):torch defines
mean = total_count * probs / (1 - probs), soprobsmust bemean / (r + mean). With the code as written, the constructed distribution's mean is r²/µ instead of µ (verified: µ=10, r=1 gives distribution mean 0.1).Training still converges — the network simply learns whatever head output makes
softplus(head)equalr²/µ— but the "mean" column is then not the mean, so every downstream consumer silently gets garbage:predict()output, mean-based metrics (RMSE/MAE via_extract_mean), andNegativeBinomialDeviancewhich reads column 0 as µ.To Reproduce
Expected behavior
probs = mean / (r + mean)(or equivalentlylogits = log(mean) - log(r)), so that the head column trained as the mean actually parameterizes the mean.Screenshots
n/a
Desktop (please complete the following information):
Additional context
The in-code comment
# variance = mean + mean^2 / dispersionis also inconsistent withr = 1/dispersion(that choice yieldsvar = µ + µ²·dispersion) — worth clarifying which conventiondispersionis meant to follow.NegativeBinomialDevianceindeeptab/metrics/distributional.pyinterprets itsalphaas the size r, while the model's column 1 isdispersion = 1/runder the training code — the two should be reconciled together with this fix.