Skip to main content

Why Standard AI Fails in the Real World: Survival Analysis

Why Standard AI Fails in the Real World: Survival Analysis

Topic Overview

What is Deep Survival Analysis? It is a specialized subfield of machine learning designed to predict the time until a specific event occurs, while explicitly handling data where the event’s ultimate time is unknown.

Why does it exist? Standard regression models (like MSE) require a definite target number for every input. In the real world, we frequently run into “censored” data. If you are building a predictive maintenance model for an industrial robot, and the robot is still running perfectly when your study ends, you do not know its time-to-failure. You only know it is greater than the current time. If you throw out this data, your model is biased. If you label the failure time as “now,” your model is catastrophically wrong.

What problem does it solve? It solves the mathematical problem of calculating gradients and updating neural network weights when your training labels are inequalities (\(T > t\)) rather than exact values (\(T = t\)).

Why should engineers care? Any ML system dealing with time-to-event—patient survival in healthcare, customer churn in marketing, equipment failure in manufacturing, or loan default in finance—will fail with standard losses. Deep Survival Analysis provides the exact mathematical and computational frameworks to train neural networks on these realities.


Prerequisites

1. Likelihood Functions * Why it is needed: Standard losses (MSE, Cross-Entropy) measure error. Survival analysis relies on Partial Likelihood, which measures the relative ranking of risks. You must understand how likelihood connects model parameters to observed data probabilities. * Where it appears: It is the exact mathematical engine of the Cox loss function.

2. Conditional Probability * Why it is needed: The core question in survival analysis is not “What is the absolute probability of failing at time \(t\)?” but rather “Given that a subject has survived up to time \(t\), what is their instantaneous risk of failing right now?” This is purely conditional probability. * Where it appears: In the definition of the Hazard Function.


Historical Motivation

In the 1970s, statisticians were trying to model patient survival times. They used parametric models (like assuming survival times followed a Weibull or Exponential distribution). This failed in practice because the underlying biological distribution of cancer progression was too complex and unknown to force into a neat mathematical shape.

In 1972, David Cox published a paper introducing the Proportional Hazards model. His engineering insight was brilliant: What if we just don’t model the baseline shape of time at all?

Instead of trying to predict the absolute time, Cox proposed a model that only predicts the relative risk between patients. By taking the ratio of risks between patients, the messy, unknown baseline time component cancels out entirely. Deep Learning researchers later realized that Cox’s mathematical trick—which relies only on the ranking of a neural network’s output—could be plugged directly into modern architectures, creating Deep Survival Analysis.


Intuitive Explanation

Imagine you are a race commentator at a marathon, but you are blindfolded. You can’t see the runners, but a sensor beeps every time a runner crosses the finish line.

Your job is to evaluate a “Speed Coach” AI. The Speed Coach looks at the runners’ biometrics and outputs a single “Speed Score” for each runner. Higher score = faster runner.

Runner A crosses the finish line at 2 hours. At this exact moment, Runners B, C, and D are still running (they are in the “risk set”). * If the Speed Coach gave Runner A the highest score out of A, B, C, and D, the coach is doing a great job. The person who actually finished first was predicted to be the most likely to finish first. * If the Speed Coach gave Runner B a much higher score than Runner A, the coach is failing.

Now, what about Runner E? Runner E got a flat tire at mile 10 and left the race. We don’t know when Runner E would have finished. We cannot use Runner E to evaluate the coach at the finish line, because Runner E wasn’t in the race at the end. But we do know that Runner E hadn’t finished before mile 10.

This is exactly how Deep Survival Analysis works. The neural network is the Speed Coach. The loss function (Cox Partial Likelihood) only penalizes the model when a runner finishes, and it checks: “Did the model give the highest risk score to the person who actually had the event, compared to everyone else still in the race?”


Visual Understanding

Draw a 2D graph. * X-axis: Time (0 to 10 years). * Y-axis: Neural Network Risk Score (0 to 10).

Plot 5 patients as dots: 1. Patient 1: (Time = 2, Score = 8). Event happened (Red dot). 2. Patient 2: (Time = 2, Score = 4). Censored (Blue circle - left the study at year 2). 3. Patient 3: (Time = 5, Score = 9). Event happened (Red dot). 4. Patient 4: (Time = 6, Score = 2). Censored (Blue circle). 5. Patient 5: (Time = 10, Score = 7). Event happened (Red dot).

Now, draw the mechanics of the loss function: * Draw a vertical line at Time = 2. Everyone at Time \(\ge 2\) (Patients 1, 2, 3, 4, 5) is the “Risk Set.” Patient 1 had the event. The loss checks if Patient 1’s score (8) is higher than the sum of exponentiated scores of everyone else. It is. * Crucial visual: Patient 2 is also at Time = 2, but is censored. The loss function ignores Patient 2. No vertical line is evaluated for Patient 2. * Draw a vertical line at Time = 5. The Risk Set is Patients 3, 4, 5. Patient 3 had the event (Score 9). The loss checks if 9 is the dominant score among 3, 4, and 5. * The model never has to predict the X-axis (Time). It only has to ensure the Y-axis (Risk Score) correctly ranks the patients who had events above the patients who survived past them.


Mathematical Foundations

Let us formalize the “race track” intuition.

1. The Fundamental Variables * \(T\): The true, unobserved time until the event. * \(C\): The censoring time (when the subject left the study). * \(Y = \min(T, C)\): The observed time. This is the number we actually have in our dataset. * \(\delta\): The event indicator. \(\delta = 1\) if the event was observed (\(T \le C\)). \(\delta = 0\) if censored (\(T > C\)).

2. The Core Functions * Survival Function \(S(t|\mathbf{x})\): The probability that the subject survives past time \(t\). \(S(t) = P(T > t)\). * Hazard Function \(h(t|\mathbf{x})\): The instantaneous rate of failure at time \(t\), given they survived up to \(t\). \[h(t|\mathbf{x}) = \frac{f(t|\mathbf{x})}{S(t|\mathbf{x})}\] (Where \(f(t)\) is the probability density function of failing at exactly \(t\)).

3. The Cox Proportional Hazards Model We pass input features \(\mathbf{x}\) into a neural network. The network outputs a single scalar log-risk score, \(r_\theta(\mathbf{x})\). The Cox model defines the hazard as: \[h(t|\mathbf{x}) = h_0(t) \exp(r_\theta(\mathbf{x}))\] * \(h_0(t)\): The baseline hazard. It is an unknown, messy function of time. We never calculate this. * \(\exp(r_\theta(\mathbf{x}))\): The multiplicative risk factor from the neural network. Notice it does not depend on \(t\). This is the “Proportional” assumption: if patient A has twice the risk of patient B at day 1, they have twice the risk at day 100.

4. The Cox Partial Likelihood (The Loss Function) Because \(h_0(t)\) is unknown, standard maximum likelihood estimation fails. Cox’s breakthrough was realizing that if you take the ratio of the hazard of the person who had the event, divided by the hazard of everyone else in the risk set, \(h_0(t)\) completely cancels out.

For a single event \(i\) that happened at time \(Y_i\), the partial likelihood is: \[L_i = \frac{\exp(r_\theta(\mathbf{x}_i))}{\sum_{j \in \mathcal{R}(Y_i)} \exp(r_\theta(\mathbf{x}_j))}\]

  • \(\mathcal{R}(Y_i)\): The “Risk Set.” This is the set of all patients \(j\) whose observed time \(Y_j \ge Y_i\). (Everyone still in the race).
  • Why this works: If patient \(i\) has the highest risk score in the risk set, the numerator is large, the denominator is just slightly larger, and \(L_i\) is close to 1 (good). If patient \(i\) has a low score, the numerator is small, the denominator is huge, and \(L_i\) approaches 0 (bad).

5. The Final Loss Function We maximize the product of \(L_i\) for all patients who had an event (\(\delta_i = 1\)). In deep learning, we minimize the Negative Log Partial Likelihood: \[\mathcal{L} = - \sum_{i : \delta_i = 1} \left[ r_\theta(\mathbf{x}_i) - \log \left( \sum_{j \in \mathcal{R}(Y_i)} \exp(r_\theta(\mathbf{x}_j)) \right) \right]\] Notice the censored patients (\(\delta_i = 0\)) do not appear in the outer sum at all.


Worked Numerical Example

Let’s compute the loss for a mini-batch of 3 patients. We will use raw neural network outputs (log-risk scores).

  • Patient 1: \(Y_1 = 2\), \(\delta_1 = 1\) (Event at t=2), \(r_1 = 1.0\)
  • Patient 2: \(Y_2 = 5\), \(\delta_2 = 0\) (Censored at t=5), \(r_2 = 2.0\)
  • Patient 3: \(Y_3 = 10\), \(\delta_3 = 1\) (Event at t=10), \(r_3 = 0.0\)

Step 1: Identify events and risk sets * Only Patient 1 and Patient 3 have \(\delta = 1\). Patient 2 is ignored in the outer sum. * For Patient 1 (Event at t=2): Who is in the risk set \(\mathcal{R}(2)\)? Everyone with \(Y \ge 2\). That is Patients 1, 2, and 3. * For Patient 3 (Event at t=10): Who is in the risk set \(\mathcal{R}(10)\)? Everyone with \(Y \ge 10\). That is only Patient 3.

Step 2: Calculate Loss for Patient 1 * Numerator: \(\exp(r_1) = \exp(1.0) \approx 2.718\) * Denominator: \(\sum \exp(r) = \exp(1.0) + \exp(2.0) + \exp(0.0) = 2.718 + 7.389 + 1.0 = 11.107\) * \(L_1 = 2.718 / 11.107 \approx 0.2447\) * \(-\log(L_1) = -(\log(2.718) - \log(11.107)) = -(1.0 - 2.407) = 1.407\)

Step 3: Calculate Loss for Patient 3 * Numerator: \(\exp(r_3) = \exp(0.0) = 1.0\) * Denominator: \(\sum \exp(r) = \exp(0.0) = 1.0\) * \(L_3 = 1.0 / 1.0 = 1.0\) * \(-\log(L_3) = -(\log(1.0) - \log(1.0)) = 0.0\)

Step 4: Total Loss * \(\mathcal{L} = 1.407 + 0.0 = 1.407\)

Interpretation: The model suffers a loss because Patient 1 had the event at t=2, but Patient 2 (who survived past t=2) had a higher risk score (2.0 vs 1.0). The model is penalized for this incorrect ranking. Patient 3 was the only one left at t=10, so the model cannot be penalized for its ranking at that time.


Computational Interpretation

What happens when we pass a batch of 10,000 patients through this loss function?

The Naive Implementation Bottleneck: If you write the loss exactly as the math shows, for every event, you must loop through the entire dataset to find who is in the risk set. This is \(\mathcal{O}(N^2)\). For 10,000 patients, that is 100 million operations per training step. On a GPU, this is catastrophic.

The Sorted Efficient Implementation: Engineers solve this by sorting the batch by descending time before computing the loss. If sorted by descending time, the risk set \(\mathcal{R}(t_i)\) for any event \(i\) is simply “myself, and everyone below me in the sorted array.” We can compute the denominator for all patients simultaneously using a cumulative sum from the bottom up. This reduces the complexity from \(\mathcal{O}(N^2)\) to \(\mathcal{O}(N \log N)\) (due to the sort) plus \(\mathcal{O}(N)\) for the cumulative sum.

Memory Representation: * Input X: [Batch, Features] * NN Output risk_scores: [Batch, 1] * Time Y: [Batch, 1] * Event E: [Batch, 1] (boolean or 0/1 integer)


Implementation Perspective

Here is the production-ready PyTorch implementation using the sorting trick. Do not implement the naive \(\mathcal{O}(N^2)\) version.

import torch
import torch.nn.functional as F

def cox_partial_likelihood_loss(risk_scores, times, events):
    """
    risk_scores: [Batch, 1] - Raw neural network outputs (log-risk)
    times:       [Batch, 1] - Observed times (Y)
    events:      [Batch, 1] - Event indicators (1 if event, 0 if censored)
    """
    # Squeeze to 1D tensors for easier manipulation
    risk_scores = risk_scores.squeeze() # [Batch]
    times = times.squeeze()             # [Batch]
    events = events.squeeze()           # [Batch]
    
    # 1. Sort by descending time
    # This is the critical engineering step. 
    # indices[i] is the original batch index of the i-th largest time.
    sorted_times, indices = torch.sort(times, descending=True)
    
    # Reorder risk scores and event indicators to match the sorted times
    sorted_risk = risk_scores[indices]
    sorted_events = events[indices]
    
    # 2. Compute the denominator: Log-Cumulative-Sum of Exp(risk)
    # We need log(sum(exp(risk_j))) for all j in the risk set.
    # The risk set for element i is elements 0 through i (since sorted descending).
    # Using logsumexp is CRITICAL here to prevent floating point overflow 
    # if risk scores are large (e.g., exp(100) = NaN).
    log_cumsum_exp = torch.logcumsumexp(sorted_risk, dim=0)
    
    # 3. Calculate the per-patient loss
    # Loss = risk_i - log(sum(exp(risk_j)))
    per_patient_loss = sorted_risk - log_cumsum_exp
    
    # 4. Zero out censored patients
    # We only calculate loss for patients where events == 1
    # Multiplying by the boolean tensor (cast to float) achieves this.
    masked_loss = per_patient_loss * sorted_events
    
    # 5. Average over the batch
    # Note: We divide by the TOTAL number of events in the batch, 
    # not the batch size, to keep gradient magnitude consistent.
    num_events = sorted_events.sum()
    if num_events == 0:
        return torch.tensor(0.0, requires_grad=True) # Edge case: batch has no events
    return -masked_loss.sum() / num_events

Why logcumsumexp? If risk_scores contain a value like 20, \(\exp(20)\) is roughly 485 million. Summing many of these quickly exceeds the limit of float32 (max \(\sim 3.4 \times 10^{38}\)), resulting in NaN. logcumsumexp applies the mathematical identity \(\log(\sum \exp(x)) = x_{max} + \log(\sum \exp(x - x_{max}))\), keeping the numbers small and stable.


Where It Appears In AI

AI Component Exact Usage
Transformer (EHR Models) Processes longitudinal patient data (visits, lab tests). The [CLS] token is passed to a linear layer to output the Cox risk score, trained via Partial Likelihood.
CNN (Sensor Data) 1D CNNs process vibration waveforms from jet engines. The output vector is mapped to a single Cox risk score predicting time until bearing failure.
Embedding Layers (Churn) Categorical customer features are embedded, concatenated, and passed through an MLP to output a churn risk score evaluated by Cox loss.
GNN (Drug Discovery) Graph networks predict molecule toxicity. Node embeddings are pooled to predict patient survival probability when given a specific drug regimen.

Deep Dive Into Real Models

DeepHit (Discrete-Time Survival) The Cox model assumes the risk ratio between two patients stays constant forever (Proportional Hazards). In reality, a patient might have high short-term risk (recovery from surgery) but low long-term risk. DeepHit solves this by abandoning the Cox model entirely. * Instead of one risk score, the neural network outputs a vector of size \(T_{max}\) (e.g., 100 time bins). * It predicts a probability distribution: “Probability of event in bin 1, bin 2… bin 100.” * Loss Function: A modified Cross-Entropy loss that accounts for censoring. If censored at bin 30, the true label is a vector of 1s up to bin 30 (they survived those bins) and unknown after. * Advantage: Captures non-proportional hazards. Discrete logits are also much easier to calibrate into actual survival curves than Cox scores.

Transformers in Clinical AI (e.g., Med-BERT) In models predicting patient survival from Electronic Health Records (EHR): 1. Every doctor’s visit is a token. 2. A Transformer encoder processes the sequence of visits. 3. The final hidden state \(h \in \mathbb{R}^d\) is extracted. 4. A linear layer \(W \in \mathbb{R}^{1 \times d}\) computes the risk score: \(r = Wh + b\). 5. This single scalar \(r\) is passed to our cox_partial_likelihood_loss function. 6. Why not MSE to predict exact days? Because 40% of patients in the dataset are still alive (censored). MSE would severely underpredict their survival times, pulling the model’s weights toward incorrect targets.


Failure Modes

1. Tied Event Times (The Breslow Approximation) * Cause: The mathematical derivation of Cox loss assumes events happen one at a time. In real datasets, multiple patients often have events at the exact same observed time (e.g., two machines fail on day 50). * Symptoms: The loss mathematically “double counts” these patients in each other’s risk sets, slightly biasing the gradients. * Solution: Use the Efron approximation instead of the standard Breslow approximation in your loss function, which handles ties correctly. (Most libraries like lifelines or pycox handle this under the hood).

2. Violation of Proportional Hazards * Cause: Using standard Cox loss when the true risk rankings cross over time. (e.g., Treatment A is better than B for the first year, but worse after year 2). * Symptoms: The model converges, but when you plot the predicted survival curves, they look mathematically impossible (they cross or don’t decrease monotonically). * Solution: Switch to a discrete-time model like DeepHit, or add time-dependent features to the input \(\mathbf{x}\).

3. Unsorted Batches * Cause: Writing the loss function without the torch.sort descending step, or sorting ascending instead of descending. * Symptoms: The cumulative sum computes the risk set of the past, not the future. The model learns to predict backwards (giving higher scores to people who survived longer), completely inverting the meaning of the network output. * Solution: Strictly enforce descending=True sorting on observed times.


Engineering Insights

Calibration vs. Discrimination The Cox loss function is purely a ranking loss (like Contrastive Learning). It cares only that Patient A is ranked higher than Patient B. It does not care about the absolute value of the risk score. * Implication: You cannot take the output of a Cox neural network and say “The patient has a 75% chance of dying in 5 years.” The scores are uncalibrated. * Engineering Fix: To get actual probabilities, you must pass the dataset’s risk scores through a post-hoc calibration step, like fitting a Kaplan-Meier estimator to the binned risk scores, or using isotonic regression.

GPU Underutilization Unlike image classification (which is easily parallelized into massive batches), Deep Survival Analysis often deals with tabular clinical data (tensors of shape [Batch, 50]). The forward pass is incredibly fast, but the sorting operation in the Cox loss is memory-bound and harder to parallelize. You will often find your GPU utilization sits at 20-40% when training survival models, bottlenecked by the torch.sort and logcumsumexp operations, not the neural network weights.


Interview Questions

Beginner: Question: Why can’t we just use Mean Squared Error to predict the exact number of days a patient will survive? Answer: Because of censoring. If a patient is still alive after 5 years, their true survival time is unknown (it is \(> 5\) years). If we use MSE, we have to either throw that valuable data away, or incorrectly label their survival time as exactly 5 years. Both options severely bias the model. Cox loss elegantly includes censored patients in the risk sets without requiring a definitive target time for them.

Intermediate: Question: In the Cox Partial Likelihood formula, what is the “Risk Set,” and how does it change as time progresses? Answer: The risk set at time \(t\) is the group of individuals who are still being observed and have not yet experienced the event just before time \(t\). As time progresses, the risk set can only shrink, as people either experience the event (and leave the risk set) or are censored (and leave the risk set).

Advanced: Question: Our naive PyTorch implementation of the Cox loss caused an Out-Of-Memory (OOM) error on the GPU when we increased the batch size to 50,000. How did you fix it? Answer: The naive implementation materializes an \(N \times N\) matrix to compute the risk set denominator, resulting in \(\mathcal{O}(N^2)\) memory. I fixed it by sorting the batch by descending time and using torch.logcumsumexp. Because the risk set for any event is simply “myself and everyone after me in the sorted array,” the cumulative sum reduces the memory footprint to \(\mathcal{O}(N)\) and computes the exact same mathematical value.

Research-Level: Question: Standard Cox models assume Proportional Hazards. How do modern deep learning architectures like DeepHit bypass this assumption mathematically? Answer: Instead of outputting a single scalar risk score that multiplies a baseline hazard, DeepHit formulates survival as a discrete-time classification problem. The network outputs a vector of logits corresponding to discrete time bins. The loss function maximizes the log-likelihood of the observed event occurring in its specific bin, while applying a ranking loss (like a modified pairwise hinge loss) to ensure the predicted survival functions of different patients do not cross, entirely removing the proportional hazards constraint.


Research Perspective

Current Limitations: * Competing Risks: Standard survival analysis assumes there is only one type of event (e.g., death). In reality, a patient might die from heart disease, cancer, or a car crash. Standard Cox loss gets confused by these competing risks. * Data Scarcity: Healthcare data is highly regulated (HIPAA). Survival models often overfit because datasets have thousands of features but only a few hundred patients.

Modern Improvements & Research Directions: 1. Multi-Task Survival (Cause-Specific Nets): Neural networks now output \(K\) different risk scores, one for each competing risk, trained with specialized competing-risk losses. 2. Counterfactual Survival: Using causal inference combined with survival analysis to answer “What would this patient’s survival be if we had given them Treatment B instead of Treatment A?” 3. Survival Foundation Models: Projects like MAMBASURV are attempting to train massive transformer models on millions of unlabelled longitudinal patient records, using self-supervised learning, to create foundational representations that can be fine-tuned for downstream Cox or DeepHit tasks with very little labeled data.


Connections

Prerequisites: * Likelihood Functions (Basis of the Partial Likelihood) * Conditional Probability (Basis of the Hazard Function) * LogSumExp Trick (Required for numerical stability)

Enables: * Predictive Maintenance Systems * Clinical Trial Analysis * Customer Churn Modeling

Used by: * DeepHit (Discrete alternative) * Cox-nnet (Early MLP adaptation) * Med-BERT / Clinical Transformers

Extended by: * Competing Risks Models * Multi-State Survival Models * Causal Survival Analysis

Related Concepts: * Kaplan-Meier Estimator (Non-parametric way to draw the survival curve \(S(t)\)) * Log-Rank Test (Statistical test to compare two groups using Cox assumptions) * Receiver Operating Characteristic (ROC) - specifically Time-Dependent AUC, which is the standard metric to evaluate a trained Survival model.


Final Mental Model

Deep Survival Analysis is a blindfolded race referee.

You do not have a stopwatch (you don’t predict absolute time). You only have a leaderboard of “Speed Scores” provided by a neural network. Every time a runner crosses the finish line (an event occurs), you look at the leaderboard. If the person who crossed has the highest score among everyone still running, you do nothing (zero loss). If someone still running has a much higher score than the person who just crossed, you blow a whistle and penalize the network (high loss). You completely ignore people who left the track early (censored), except to acknowledge they were in the race up until the moment they left.

Comments

Popular posts from this blog

Why Every AI Model is Actually Bayesian

Why Every AI Model is Actually Bayesian Topic Overview Standard probability asks: “What is the chance of this event happening in the entire universe?” Conditional probability asks: “Given that I already know this to be true, what is the chance of that happening?” Bayes’ Theorem is the engineering tool that flips this condition around. It answers the most critical question in machine learning: “Given the data I just observed, what is the probability that my hypothesis is true?” Why does it exist? Because in the real world, we usually know \(P(\text{Data}|\text{Hypothesis})\) —how likely a certain observation is under a specific model—but what we actually want is \(P(\text{Hypothesis}|\text{Data})\) —how likely our model is to be correct given the observation. Bayes’ Theorem is the mathematical bridge between the data we generate and the truths we want to infer. What problem does it solve? It provides a rigorous, mathematically sound mechani...

Why Everything in AI is a Vector ?

Why Everything in AI is a Vector ? Topic Overview In the previous lesson, you learned about the scalar: a single number representing magnitude. But reality is rarely one-dimensional. A single pixel has three color channels (red, green, blue). A word’s meaning has dozens of grammatical and semantic features. A model’s parameters number in the millions. To represent entities that possess multiple attributes simultaneously, we need a mathematical object that holds multiple scalars in a specific order. That object is a vector. A vector \(\mathbf{v} \in \mathbb{R}^n\) is an ordered list of \(n\) scalars. It represents a point, or an arrow from the origin, in \(n\) -dimensional space. Why does it exist? Because we need a mathematical language for things that have more than one degree of freedom. A scalar tells you “how hot.” A vector tells you “where you are” and “which way you’re going.” What problem does it solve? It provides the fundamental d...

Why Your PyTorch Code Keeps Breaking (It’s the Scalars)

Why Your PyTorch Code Keeps Breaking (It’s the Scalars) Topic Overview You are about to learn about the most fundamental object in all of mathematics and machine learning: the scalar. A scalar is a single number. That is the entire definition. But do not confuse simplicity with insignificance. Every neural network, no matter how many billions of parameters it contains, is ultimately optimized by reading and writing scalars. The loss value is a scalar. The learning rate is a scalar. The temperature parameter in softmax is a scalar. The scale factor in attention is a scalar. Every gradient clipping decision is based on a scalar. The final prediction of a classification model is a scalar probability. Why does the scalar exist as a named concept? Because not all numbers are created equal. Some numbers exist as components of larger structures—a coordinate in a vector, an entry in a matrix, a pixel in an image. A scalar is a number that stands alone...