1. 生存分析里的建模目标:为什么偏要造一个"Cox loss"
我最早接触 Cox loss,是在做客户流失预测的时候。当时团队要做的不只是"判断哪些用户会走",而是"预测用户多久之后会走",因为续费提醒、挽留策略的投放时机,全依赖那个时间点。试了一圈常规分类模型,发现都不对劲:训练数据里有一大批用户,观察期结束还没流失,你没法给他们标一个确切的流失时间,但直接丢掉又太浪费。查了一圈资料,才发现这类问题有个正经名字叫生存分析,而 Cox 比例风险模型就是其中最常用的武器,所谓的 Cox loss,正是训练这个模型时用的损失函数。
先说清楚一个容易混淆的点:Cox loss 不是一个"损失函数"的名字,而是Cox 比例风险模型采用负对数偏似然(negative log partial likelihood)作为优化目标时的叫法。在传统统计软件里,你可能没见过"loss"这种说法,大家习惯叫"偏似然最大化";但到了深度学习时代,所有东西都要变成可微分的损失函数,Cox loss 这个叫法就自然流传开了。
它的核心用途,是拟合下面这个风险函数:
$$h(t|x) = h_0(t) \cdot \exp(f(x))$$
其中 $h(t|x)$ 表示特征为 $x$ 的个体在时刻 $t$ 的瞬时风险,$h_0(t)$ 是基线风险函数,与特征无关,只随时间变化;而 $f(x)$ 是我们真正关心的部分——它把原始特征映射成一个线性组合或者神经网络输出。Cox 模型的巧妙之处在于,它不关心 $h_0(t)$ 长什么样,只估计 $f(x)$ 那一部分,所以它属于半参数模型。
这种设计带来的直接好处是:你不需要对生存时间分布做任何假设(不需要猜它是指数分布还是 Weibull 分布),只需要假设不同个体之间的风险是成比例的,也就是任意两个个体 $i$ 和 $j$,在任意时刻 $t$,它们的风险比 $\frac{h(t|x_i)}{h(t|x_j)}$ 是常数,不随时间变化。这个假设是所有 Cox 模型应用的前提,模型效果崩了的时候,第一个要怀疑的就是它。
那损失函数怎么构造呢?核心思路是:我们不知道基线风险,但可以绕过它,通过比较同一时刻"谁先出事、谁还没出事"来估计参数。这就是偏似然的核心思想——只用"事件发生的顺序"来拟合,不需要精确的时间分布。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 偏似然损失公式拆解:从风险集到负对数
Cox loss 的公式看起来不长,但每个符号背后都有具体的统计含义。先把完整的负对数偏似然写出来:
$$L(\theta) = -\sum_{i: \delta_i = 1} \left[ f(x_i) - \log \sum_{j \in R(t_i)} \exp(f(x_j)) \right]$$
看着有点吓人,拆开就清楚了。这里的符号约定是:
- $x_i$:第 $i$ 个样本的特征向量
- $t_i$:第 $i$ 个样本的观测时间(可能是事件发生时间,也可能是删失时间)
- $\delta_i$:事件指示符,$\delta_i = 1$ 表示观测到了事件发生,$\delta_i = 0$ 表示右删失(只观测到"到某个时间还没发生")
- $f(x_i)$:模型输出的风险评分(log-risk),可以是线性回归的结果,也可以是神经网络最后一层的输出
- $R(t_i)$:风险集(risk set),指在时间 $t_i$ 时刻仍然"存活"(未被删失、未发生事件)的所有样本的集合
2.1 为什么只看"发生事件"的样本
注意求和符号下面的条件 $\delta_i = 1$——只有真正发生了事件的样本才会进入损失计算。删失样本不直接参与分子,但它们会出现在分母的风险集里。
举个例子,假设有 5 个病人,观察期内 3 人死亡、2 人失访(删失)。第一个人在第 10 天死亡,那么在他死亡的那一刻,风险集里包含所有在第 10 天还没死的病人——包括后来在第 20 天、第 30 天死亡的那两位,也包括到最后都没死的删失样本。这个时刻的信息是:"第 10 天,有一堆人还活着,但偏偏是这个人死了,为什么?"
这就是 Cox 模型的建模逻辑——它不预测绝对风险,而是预测相对风险排序。谁会在哪个时刻出事不重要,重要的是"在某个时刻,出事的是不是你"。
2.2 分子分母的含义:个体风险 vs 群体风险
分子 $\exp(f(x_i))$ 表示第 $i$ 个样本在当前时刻的"风险强度";分母 $\sum_{j \in R(t_i)} \exp(f(x_j))$ 表示风险集里所有样本的风险强度之和。两者相除,得到的是"在 $t_i$ 时刻,第 $i$ 个样本出事在所有可能出事的人当中所占的比例"。
模型的优化目标很直接:让这个比例尽量大。也就是说,发生在 $t_i$ 时刻的事件,我们希望模型预测"就是这个人最该出事",而不是别人。把所有事件时刻的这个比例乘起来,就是偏似然函数;取负对数,就变成求和形式的损失,方便梯度下降。
2.3 为什么取负对数
取负对数有三个实际理由。第一,事件概率是大量小数连乘,数值下溢到零是家常便饭,取对数能把乘法变加法;第二,优化器习惯最小化损失,加个负号把最大化问题转成最小化;第三,对数里恰好出现"log-sum-exp"结构,这在数值计算上有稳定的实现方式。
如果你手推一下梯度就会发现,这个损失函数的梯度有一个很优雅的解释:损失对 $f(x_i)$ 的梯度,等于"预测的风险比例"减去"实际的事件指示":
$$\frac{\partial L}{\partial f(x_i)} = \frac{\exp(f(x_i))}{\sum_{j \in R(t_i)} \exp(f(x_j))} - \delta_i$$
这个式子(对事件样本)的含义是:如果模型认为这个样本出事概率占风险集的 40%(预测),但实际它的 $\delta_i = 1$(事件确实发生了),说明模型给低了,梯度为正,会往提高 $f(x_i)$ 的方向更新。反过来,如果一个删失样本 $\delta_i = 0$ 但被预测了很高的风险,梯度会让它的分数下降。所以这个损失在隐式地做"排序学习",不是在做分类,也不是在做回归。
3. 从传统统计到深度学习:Cox loss 的两种落地姿势
Cox 比例风险模型最早是统计学家 David Cox 在 1972 年提出的,当时求解靠 Newton-Raphson 迭代。现在深度学习流行起来之后,Cox loss 被直接接进了神经网络——把 $f(x)$ 从简单的线性组合 $\beta^T x$ 换成多层感知机的输出,就得到了深度生存模型。但这中间的配方差异值得展开说说,因为你用传统工具包习惯了,转到深度框架时很容易踩坑。
3.1 传统统计方式:R 的 survival 包
在 R 里拟合 Cox 模型,核心代码就那么几行:
r复制libr
