Censored Regression Losses¶
Censored regression handles data where the true target value is only partially observed. The model knows that the true value exceeds (or falls below) a threshold, but not the exact value.
When Does Censoring Arise?¶
Common scenarios
- Survival analysis — time-to-event is right-censored when a subject is still alive at study end
- Sensor saturation — detector readings clamped at hardware limits
- Detection limits — flux below sensitivity → only an upper bound is known
- Administrative — study ends before all events are observed
Censoring Convention¶
torchregress uses an integer encoding for the censoring indicator \(c_i\):
| \(c_i\) | Meaning | Interpretation |
|---|---|---|
| \(0\) | Observed | Target \(y_i\) is exact |
| \(+1\) | Right-censored | True value \(y_i^* \geq y_i\) |
| \(-1\) | Left-censored | True value \(y_i^* \leq y_i\) |
Mathematical Background¶
Gaussian Censored NLL¶
For a Gaussian with predicted mean \(\mu\) and variance \(\sigma^2\), the censored log-likelihood uses the normal CDF \(\Phi\) to model the probability of the true value lying beyond the observed threshold:
where \(\phi(\cdot)\) is the standard normal PDF and \(\Phi(\cdot)\) is the standard normal CDF.
Intuition
For observed points, the loss is the usual Gaussian NLL. For censored points, the loss is the log-probability of the true value being beyond the observed threshold — a softer constraint that prevents the model from fitting to boundary artefacts.
Accelerated Failure Time (AFT)¶
The AFT model assumes the log of survival time is linear:
The loss is the negative log-likelihood under this log-normal model, with censoring handled through \(\Phi\).
Available Losses¶
CensoredGaussianNLLLoss¶
Full Gaussian NLL with right/left censoring support. See CensoredGaussianNLLLoss API.
import torch
from torchregress.losses import CensoredGaussianNLLLoss
loss_fn = CensoredGaussianNLLLoss()
# Model predicts (mean, log_var d)
mean = torch.randn(64, 1)
log_var = torch.randn(64, 1)
target = torch.rand(64, 1) * 5.0
# Censoring: 0 = observed, +1 = right, -1 = left
censoring = torch.randint(-1, 2, (64, 1))
loss = loss_fn((mean, log_var), target, censoring=censoring)
CensoredQuantileLoss¶
Quantile regression adapted for censored data — provides non-parametric intervals without assuming Gaussian noise. See CensoredQuantileLoss API.
from torchregress.losses import CensoredQuantileLoss
loss_fn = CensoredQuantileLoss(quantile=0.5)
loss = loss_fn(y_pred, target, censoring=censoring)
AFTLoss¶
Log-normal Accelerated Failure Time model for survival analysis. See AFTLoss API.
from torchregress.losses import AFTLoss
loss_fn = AFTLoss()
# y_pred: (loc, log_scale) for log-normal AFT (log_scale = log σ, not log variance)
# censoring: 0 = observed, +1 = right-censored, -1 = left-censored
# (not sklearn's event_indicator where 1 = event)
loss = loss_fn((loc, log_scale), survival_time, censoring=censoring)
Complete Example: Survival Analysis¶
import torch
import torch.nn as nn
from torchregress.losses import CensoredGaussianNLLLoss
# Simulate survival data with right censoring
torch.manual_seed(42)
n = 500
x = torch.randn(n, 5)
true_time = torch.exp(x @ torch.randn(5, 1) + 0.5 * torch.randn(n, 1))
# 30% of subjects are right-censored (study ended before event)
censor_time = torch.rand(n, 1) * true_time.median() * 3
observed_time = torch.min(true_time, censor_time)
censoring = (censor_time < true_time).long() # 1 = right-censored, 0 = observed
# Model predicts (mean, log_variance)
class SurvivalModel(nn.Module):
def __init__(self):
super().__init__()
self.shared = nn.Sequential(nn.Linear(5, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU())
self.mean_head = nn.Linear(32, 1)
self.logvar_head = nn.Linear(32, 1)
def forward(self, x):
h = self.shared(x)
return self.mean_head(h), self.logvar_head(h)
model = SurvivalModel()
loss_fn = CensoredGaussianNLLLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(200):
mean, logvar = model(x)
loss = loss_fn((mean, logvar), observed_time, censoring=censoring)
optimizer.zero_grad(); loss.backward(); optimizer.step()
Limitations¶
- Censoring indicator convention: Uses
0= observed,+1= right-censored,-1= left-censored. This is the opposite of survival analysis conventions where1= event observed. Double-check your indicator encoding before training. - Gaussian assumption:
CensoredGaussianNLLLossassumes the uncensored target follows a Gaussian distribution. For heavy-tailed or multimodal survival times, considerCensoredQuantileLoss(distribution-free) or log-normal AFT models (AFTLoss). - Boundary sensitivity: When a large fraction of targets are censored near the same threshold, the CDF term \(\Phi((\mu - y)/\sigma)\) dominates the loss and can pull \(\mu\) toward the boundary. Monitor prediction distributions for boundary artefacts.
- No built-in non-informative censoring test: The loss assumes censoring is non-informative (independent of the true target value given \(X\)). If censoring depends on the unobserved target (informative censoring), estimates will be biased. Validate this assumption on your data before deploying.
Practical Tips¶
Creating the censoring indicator
Use 0 for fully observed, +1 for right-censored, -1 for left-censored. For survival analysis, censoring = (event_not_observed).long().
Combining with conformal prediction
Wrap censored quantile predictions with CQR for coverage-guaranteed survival intervals.
Next steps¶
- Censored metrics — concordance index, Brier score, observed MAE
- Conformal + Censored — wrap censored quantile predictions with CQR for coverage-guaranteed survival intervals
- Censored comparison — benchmark Gaussian vs quantile vs AFT
- Ordinal losses — ordered discrete targets (another type of partial observation)
References¶
| # | Reference |
|---|---|
| 1 | J. Tobin. "Estimation of Relationships for Limited Dependent Variables." Econometrica, 26(1):24–36, 1958. |
| 2 | D.R. Cox. "Regression Models and Life-Tables." JRSS B, 34(2):187–220, 1972. |
| 3 | J.L. Buckley, I.R. James. "Linear Regression with Censored Data." Biometrika, 66(3):429–436, 1979. |