AI 工程基础体系 · 第 43/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。

逻辑回归完整原理:对数几率、损失、阈值、校准与解释

逻辑回归(Logistic Regression)是一种用于分类的统计学习模型。它的核心做法不是直接把输入映射为类别,而是:

  1. 先用线性函数计算一个分数;
  2. 把分数解释为对数几率
  3. 通过 Sigmoid 函数把分数转换为概率;
  4. 用对数损失训练参数;
  5. 在推理阶段根据业务代价选择分类阈值;
  6. 检查输出概率是否经过校准
  7. 结合模型结构和数据条件解释特征对结果的影响。

因此,“逻辑回归输出 0 或 1”并不准确。模型本身主要输出概率,0/1 是应用层根据阈值做出的决策。


一、先从二分类问题定义开始

设每个样本有特征向量:

x=(x1,x2,,xd)x=(x_1,x_2,\dots,x_d)

标签为:

y{0,1}y\in\{0,1\}

例如:

  • y=1y=1:用户会流失;
  • y=0y=0:用户不会流失。

逻辑回归要估计的是条件概率:

P(y=1x)P(y=1\mid x)

记这个概率为 p(x)p(x)。因为概率必须位于 (0,1)(0,1) 之间,不能直接使用普通线性回归:

p(x)=β0+β1x1++βdxdp(x)=\beta_0+\beta_1x_1+\cdots+\beta_dx_d

线性函数的输出可能小于 0 或大于 1,所以逻辑回归先定义一个没有范围限制的线性分数:

z=β0+β1x1++βdxdz=\beta_0+\beta_1x_1+\cdots+\beta_dx_d

其中:

  • β0\beta_0 是截距;
  • βj\beta_j 是第 jj 个特征的系数;
  • zz 可以取任意实数。

随后,逻辑回归把 zz 转换为概率。


二、对数几率:为什么线性函数可以用于概率建模

2.1 几率与对数几率

对于事件概率 pp,它的几率(odds)定义为:

odds(p)=p1p\operatorname{odds}(p)=\frac{p}{1-p}

几率表示“事件发生”与“不发生”的相对可能性:

  • p=0.5p=0.5 时,odds 为 11
  • p=0.8p=0.8 时,odds 为 44
  • p=0.2p=0.2 时,odds 为 0.250.25

几率仍然只能取正数。对几率取自然对数,得到对数几率(log-odds,也叫 logit):

logit(p)=logp1p\operatorname{logit}(p) = \log\frac{p}{1-p}

pp 从 0 接近 1 变化时,logit 从负无穷增加到正无穷,因此可以用任意实数的线性函数表示:

logp(x)1p(x)=β0+β1x1++βdxd\log\frac{p(x)}{1-p(x)} = \beta_0+\beta_1x_1+\cdots+\beta_dx_d

这就是逻辑回归最核心的建模假设:

特征与目标概率的对数几率之间是线性关系。

注意,这不是说概率 p(x)p(x) 与特征之间是线性的。概率经过逆变换后通常是 S 形曲线。

2.2 从对数几率推导 Sigmoid

令:

z=β0+βTxz=\beta_0+\beta^Tx

根据逻辑回归假设:

logp1p=z\log\frac{p}{1-p}=z

两边取指数:

p1p=ez\frac{p}{1-p}=e^z

整理:

p=ez(1p)p=e^z(1-p)

p+pez=ezp+pe^z=e^z

p(1+ez)=ezp(1+e^z)=e^z

p=ez1+ez=11+ezp=\frac{e^z}{1+e^z} = \frac{1}{1+e^{-z}}

于是得到 Sigmoid 函数:

σ(z)=11+ez\sigma(z)=\frac{1}{1+e^{-z}}

逻辑回归的概率输出为:

P(y=1x)=σ(β0+βTx)P(y=1\mid x) = \sigma(\beta_0+\beta^Tx)

z=0z=0 时,p=0.5p=0.5;当 z>0z>0 时,p>0.5p>0.5;当 z<0z<0 时,p<0.5p<0.5

2.3 线性决策边界

如果使用默认阈值 0.50.5,则预测正类的条件为:

p0.5p\geq 0.5

由于 Sigmoid 是单调递增函数:

p0.5    z0p\geq 0.5 \iff z\geq 0

所以决策边界是:

β0+βTx=0\beta_0+\beta^Tx=0

在二维特征空间中,这是直线;在更高维空间中,这是超平面。

这说明逻辑回归是一个线性分类器。它可以输出非线性的概率曲线,但默认的分类边界仍然是线性的。


三、一个数值算例:从线性分数到概率和解释

假设只有一个特征 xx,模型为:

z=2+0.8xz=-2+0.8x

p=σ(2+0.8x)p=\sigma(-2+0.8x)

x=3x=3 时:

z=2+0.8×3=0.4z=-2+0.8\times3=0.4

因此:

p=11+e0.40.5987p=\frac{1}{1+e^{-0.4}}\approx0.5987

模型认为该样本属于正类的概率约为 0.5990.599

如果阈值是 0.50.5,则预测为正类。决策边界满足:

2+0.8x=0-2+0.8x=0

所以:

x=2.5x=2.5

x>2.5x>2.5 时,预测正类;当 x<2.5x<2.5 时,预测负类。

系数 0.80.8 的含义不是“xx 每增加 1,概率增加 0.8”。概率变化取决于当前 xx 所在的位置。它真正表示的是:

logp1p\log\frac{p}{1-p}

xx 增加 1 时增加 0.80.8

换算为几率:

odds(x+1)odds(x)=e0.82.2255\frac{\operatorname{odds}(x+1)}{\operatorname{odds}(x)} = e^{0.8} \approx2.2255

也就是说,在其他条件不变时,xx 增加 1,会使正类几率乘以约 2.232.23


四、损失函数:为什么训练逻辑回归使用对数损失

4.1 伯努利分布与似然

二分类标签可以看作服从伯努利分布:

P(yx)=py(1p)1yP(y\mid x)=p^y(1-p)^{1-y}

y=1y=1 时,该式为 pp;当 y=0y=0 时,该式为 1p1-p

对于 nn 个独立样本,似然函数为:

L(β)=i=1npiyi(1pi)1yiL(\beta) = \prod_{i=1}^{n} p_i^{y_i}(1-p_i)^{1-y_i}

其中:

pi=σ(β0+βTxi)p_i=\sigma(\beta_0+\beta^Tx_i)

直接最大化乘积不便于计算,因此取对数:

logL(β)=i=1n[yilogpi+(1yi)log(1pi)]\log L(\beta) = \sum_{i=1}^{n} \left[ y_i\log p_i+(1-y_i)\log(1-p_i) \right]

最大化对数似然,等价于最小化负对数似然:

J(β)=i=1n[yilogpi+(1yi)log(1pi)]J(\beta) = -\sum_{i=1}^{n} \left[ y_i\log p_i+(1-y_i)\log(1-p_i) \right]

通常使用平均损失:

L(β)=1ni=1n[yilogpi+(1yi)log(1pi)]\mathcal{L}(\beta) = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i\log p_i+(1-y_i)\log(1-p_i) \right]

这就是二分类交叉熵,也常被称为对数损失(log loss)。

4.2 单个样本的损失

对于一个样本:

(y,p)=[ylogp+(1y)log(1p)]\ell(y,p) = -\left[ y\log p+(1-y)\log(1-p) \right]

y=1y=1 时:

(1,p)=logp\ell(1,p)=-\log p

模型给出 p=0.9p=0.9,损失较小;给出 p=0.01p=0.01,损失很大。

y=0y=0 时:

(0,p)=log(1p)\ell(0,p)=-\log(1-p)

模型给出 p=0.1p=0.1,损失较小;给出 p=0.99p=0.99,损失很大。

因此,对数损失会严厉惩罚“非常自信但预测错误”的结果。这也是它适合概率建模的原因。

4.3 数值算例:三条样本

考虑三个样本:

x=[0,1,2],y=[0,1,1]x=[0,1,2],\qquad y=[0,1,1]

暂时取:

β0=1,β1=1\beta_0=-1,\qquad \beta_1=1

三个样本的线性分数为:

z=[1,0,1]z=[-1,0,1]

对应概率约为:

p=[0.2689,0.5,0.7311]p=[0.2689,0.5,0.7311]

逐个计算损失:

  • 第一个样本 y=0y=0

log(10.2689)0.3133-\log(1-0.2689)\approx0.3133

  • 第二个样本 y=1y=1

log(0.5)0.6931-\log(0.5)\approx0.6931

  • 第三个样本 y=1y=1

log(0.7311)0.3133-\log(0.7311)\approx0.3133

平均损失为:

L=0.3133+0.6931+0.313330.4399\mathcal{L} = \frac{0.3133+0.6931+0.3133}{3} \approx0.4399

第二个样本的预测概率只有 0.50.5,所以它对损失贡献较大。


五、梯度、Hessian 与参数优化

逻辑回归一般没有像普通最小二乘那样的简单闭式解,需要通过数值优化求参数。

令设计矩阵包含截距列:

X=[1x11x1d1x21x2d1xn1xnd]X= \begin{bmatrix} 1 & x_{11} & \cdots & x_{1d}\\ 1 & x_{21} & \cdots & x_{2d}\\ \vdots & \vdots & & \vdots\\ 1 & x_{n1} & \cdots & x_{nd} \end{bmatrix}

令:

p=σ(Xβ)p=\sigma(X\beta)

二分类交叉熵的梯度为:

L=1nXT(py)\nabla\mathcal{L} = \frac{1}{n}X^T(p-y)

其中 pyp-y 是每个样本的预测误差。

对上面的三条样本:

py=[0.2689,0.5,0.2689]p-y=[0.2689,-0.5,-0.2689]

截距方向的梯度为:

0.26890.50.268930.1667\frac{0.2689-0.5-0.2689}{3} \approx -0.1667

特征方向的梯度为:

0×0.2689+1×(0.5)+2×(0.2689)30.3460\frac{0\times0.2689+1\times(-0.5)+2\times(-0.2689)}{3} \approx -0.3460

如果学习率为 0.50.5,梯度下降更新为:

βnew=βηL\beta_{\text{new}} = \beta-\eta\nabla\mathcal{L}

因此:

β0new=10.5(0.1667)0.9167\beta_0^{\text{new}} = -1-0.5(-0.1667) \approx-0.9167

β1new=10.5(0.3460)1.1730\beta_1^{\text{new}} = 1-0.5(-0.3460) \approx1.1730

特征系数变大,是因为当前模型对正类样本的概率仍然不够高,梯度推动模型提高 xx 较大时的分数。

逻辑回归损失的 Hessian 为:

H=1nXTWXH=\frac{1}{n}X^TWX

其中 WW 是对角矩阵:

Wii=pi(1pi)W_{ii}=p_i(1-p_i)

因为 pi(1pi)0p_i(1-p_i)\geq0,所以 Hessian 是半正定矩阵。未加正则化时,逻辑回归的对数损失是凸函数;加上常见的 L2 正则化后,仍然是凸优化问题。

这意味着在满足适当条件时,优化器不会陷入深度神经网络中常见的局部极小值问题。但“凸”不等于“任何实现都一定稳定”:特征尺度、停止条件、数值溢出和数据分离仍会影响训练。


六、正则化:防止系数过大,但会影响解释和概率

实际训练通常最小化:

Lreg=L+λR(β)\mathcal{L}_{\text{reg}} = \mathcal{L} + \lambda R(\beta)

6.1 L2 正则化

L2 正则化通常写为:

R(β)=12j=1dβj2R(\beta)=\frac{1}{2}\sum_{j=1}^{d}\beta_j^2

目标函数为:

Lreg=L+λ2β22\mathcal{L}_{\text{reg}} = \mathcal{L} + \frac{\lambda}{2}\|\beta\|_2^2

它倾向于让系数变小,但通常不会将系数精确压到 0。

6.2 L1 正则化

L1 正则化为:

R(β)=j=1dβjR(\beta)=\sum_{j=1}^{d}|\beta_j|

它可以把部分系数压到 0,因此具有一定的特征选择效果。但在高度相关的特征之间,L1 选择哪一个特征可能不稳定。

6.3 截距是否正则化

常见实现会默认不对截距正则化,或者使用不同方式处理截距。具体行为取决于库和参数配置,不能只根据公式假定实现细节。

在 scikit-learn 中,LogisticRegressionC 通常表示正则化强度的倒数:

  • C 越小,正则化越强;
  • C 越大,正则化越弱。

这与直接使用 λ\lambda 的教材记号相反,调参时必须确认参数语义。


七、完全分离:逻辑回归可能没有有限的最大似然解

考虑一维数据:

x=[0,1,2,3]x=[0,1,2,3]

y=[0,0,1,1]y=[0,0,1,1]

存在一条边界可以完全分开两类,例如 x=1.5x=1.5。如果不断增大斜率并调整截距,模型可以让正类概率无限接近 1,让负类概率无限接近 0。

但是要达到概率恰好为 0 或 1,需要系数趋向正无穷或负无穷。于是未正则化的最大似然估计可能不存在有限解,这叫完全分离(complete separation)。

表现包括:

  • 系数异常巨大;
  • 优化器达到迭代上限;
  • 不同实现给出差异很大的参数;
  • 训练集损失接近 0,但测试集表现很差;
  • 概率极端且不稳定。

L2 正则化通常可以使问题重新具有有限解,但它改变了估计目标。工程上还应检查:

  • 是否有某个字段直接泄露标签;
  • 是否存在只在训练期出现的状态字段;
  • 类别特征是否过于稀疏;
  • 数据量是否不足以支持当前特征维度。

八、阈值:概率输出如何变成业务决策

8.1 阈值不是模型参数本身

假设模型输出:

p=P(y=1x)p=P(y=1\mid x)

分类器还需要一个阈值 tt

y^={1,pt0,p<t\hat y= \begin{cases} 1,&p\geq t\\ 0,&p<t \end{cases}

默认阈值通常是 0.50.5,但它不是自然法则,也不一定适合业务。

改变阈值不会改变已经输出的概率,也不会改变 ROC 曲线上的模型排序;它改变的是最终决策。

8.2 从错误代价推导阈值

设:

  • 误报(False Positive,FP)的代价为 CFPC_{FP}
  • 漏报(False Negative,FN)的代价为 CFNC_{FN}
  • 模型输出的概率 pp 已经是可靠的条件概率。

如果预测正类,期望代价为:

CFP(1p)C_{FP}(1-p)

如果预测负类,期望代价为:

CFNpC_{FN}p

选择正类的条件是:

CFP(1p)<CFNpC_{FP}(1-p)<C_{FN}p

整理得到:

p>CFPCFP+CFNp>\frac{C_{FP}}{C_{FP}+C_{FN}}

例如:

  • 误报代价为 1;
  • 漏报代价为 4。

则阈值为:

t=11+4=0.2t=\frac{1}{1+4}=0.2

这意味着只要正类概率超过 0.20.2,漏报的风险就可能高于误报。

这个推导依赖两个条件:

  1. 概率确实经过校准;
  2. 不考虑资源容量、延迟、批处理等额外约束。

如果每天只能处理 1 万个样本,阈值还必须结合处理容量决定,而不能只使用代价公式。

8.3 混淆矩阵中的四种结果

二分类决策得到四类结果:

实际 预测 名称
1 1 TP,真正例
0 1 FP,假正例
1 0 FN,假负例
0 0 TN,真负例

由此定义:

Precision=TPTP+FP\text{Precision}=\frac{TP}{TP+FP}

Recall=TPTP+FN\text{Recall}=\frac{TP}{TP+FN}

FPR=FPFP+TN\text{FPR}=\frac{FP}{FP+TN}

降低阈值通常会增加预测正类的数量:

  • Recall 往往上升;
  • Precision 可能下降;
  • FPR 往往上升。

但这些不是绝对单调保证,有限样本和并列分数可能造成局部变化。

8.4 ROC 与 PR 的适用边界

ROC 曲线以 FPR 为横轴、TPR(Recall)为纵轴,主要反映模型对正负样本的排序能力。

当正类非常稀少时,ROC-AUC 可能看起来不错,但实际正类预测中包含大量误报。这时 Precision-Recall 曲线通常更能反映业务质量。

例如在万分之一的欺诈率下,即使模型有较高 Recall,只要误报稍多,Precision 也可能很低。因此评估阈值时应直接观察:

  • 给定告警量下的 Precision;
  • 给定 Recall 下的告警量;
  • 每天需要人工处理的样本数;
  • FP 与 FN 的真实业务代价。

九、校准:概率值是否具有频率意义

9.1 什么是校准

一个分类器如果输出 0.70.7,理想含义是:

在所有输出约为 0.70.7 的样本中,约 70% 实际属于正类。

这叫概率校准(calibration)。

形式化地说,对于预测概率 qq

P(y=1p^=q)=qP(y=1\mid \hat p=q)=q

实际数据中不能对每个精确的 qq 估计,因此通常把预测概率分箱,比较每个箱子的:

  • 平均预测概率;
  • 实际正类比例。

校准与区分能力不同:

  • 区分能力关注正类是否排在负类前面;
  • 校准关注概率数值是否可信。

一个模型可以排序很好但校准很差。例如所有正类都排在前面,但输出概率普遍过高。

9.2 Brier 分数

Brier 分数是概率预测的均方误差:

Brier=1ni=1n(p^iyi)2\text{Brier} = \frac{1}{n} \sum_{i=1}^{n} (\hat p_i-y_i)^2

越低越好。

它同时受到概率准确性和类别分布影响,不应脱离数据集的正类比例单独解读。

9.3 校准曲线与 ECE

将概率分为 KK 个区间,第 kk 个区间中:

  • 样本数为 nkn_k
  • 平均预测概率为 confk\operatorname{conf}_k
  • 实际正类比例为 acck\operatorname{acc}_k

Expected Calibration Error(ECE)常写为:

ECE=k=1Knknacckconfk\operatorname{ECE} = \sum_{k=1}^{K} \frac{n_k}{n} \left| \operatorname{acc}_k-\operatorname{conf}_k \right|

ECE 依赖分箱方式,不是完全无偏的统一标准。样本很少时,某些箱子的实际比例波动很大,不能把一次校准曲线当作确定事实。

9.4 Platt Scaling 与 Isotonic Regression

常见的后处理方法包括:

Platt Scaling

在模型原始分数 zz 上再训练一个逻辑函数:

P(y=1z)=11+exp(Az+B)P(y=1\mid z) = \frac{1}{1+\exp(Az+B)}

它参数少,数据量较小时通常比非参数方法稳定。

Isotonic Regression

学习一个单调非递减函数,把原始分数映射到概率。它表达能力更强,但样本较少时更容易过拟合。

校准模型必须使用独立的校准数据,或者使用交叉验证产生没有见过训练标签的预测。不能在同一批训练预测上校准,否则模型会利用已经见过的标签,得到虚假的校准效果。

9.5 阈值选择与校准的先后关系

一个合理的流程是:

  1. 在训练数据上训练基础模型;
  2. 在独立校准数据上校准概率;
  3. 在另一个验证集上选择阈值;
  4. 在最终测试集上只评估一次。

如果先用未校准概率选阈值,再改变概率映射,原来的阈值通常就不再适用。

校准也可能改变排序,因此可能影响 AUC;尤其是复杂的非参数校准方法不应被视为“只改变概率、不影响排序”的绝对保证。


十、逻辑回归系数如何解释

10.1 系数对数几率解释

模型为:

logp1p=β0+β1x1++βdxd\log\frac{p}{1-p} = \beta_0+\beta_1x_1+\cdots+\beta_dx_d

在其他特征保持不变时,xjx_j 增加 1,会使对数几率增加 βj\beta_j

  • βj>0\beta_j>0:提高正类几率;
  • βj<0\beta_j<0:降低正类几率;
  • βj=0\beta_j=0:在模型中没有线性贡献。

10.2 系数转为几率比

对系数取指数:

exp(βj)\exp(\beta_j)

得到几率比(odds ratio)。

如果 βj=0.8\beta_j=0.8,则:

e0.82.23e^{0.8}\approx2.23

含义是:在其他特征不变时,xjx_j 增加一个单位,正类几率乘以约 2.232.23

如果 βj=0.7\beta_j=-0.7,则:

e0.70.497e^{-0.7}\approx0.497

表示正类几率约变为原来的 49.7%49.7\%

几率比不是概率比。例如,原概率为 0.10.1,几率为 0.1110.111;乘以 2.23 后几率约为 0.2480.248,对应概率:

0.2481+0.2480.199\frac{0.248}{1+0.248}\approx0.199

概率从 10%10\% 变成约 19.9%19.9\%,并不是增加 223%223\% 个百分点。

10.3 特征尺度决定解释单位

如果年龄以“岁”为单位,系数表示年龄增加 1 岁的影响;如果年龄先标准化为:

x=xμσx'=\frac{x-\mu}{\sigma}

那么系数表示特征增加一个标准差时的对数几率变化。

标准化有两个作用:

  1. 让不同尺度的系数更容易比较;
  2. 改善优化过程的数值条件。

但标准化后的系数不能直接解释为“原始单位增加 1”的效果,解释时必须还原单位。

10.4 截距的解释

当所有特征都为 0 时:

β0=logp01p0\beta_0=\log\frac{p_0}{1-p_0}

其中 p0p_0 是基准样本的正类概率。

如果“所有特征为 0”不是实际存在或有意义的样本,截距通常不具备直观业务含义。对连续特征做中心化,往往可以让截距代表“平均特征样本”的基准对数几率。

10.5 相关特征和因果误读

系数是“控制模型中其他特征后的条件关联”,不是因果效应。

当两个特征高度相关时:

  • 系数可能不稳定;
  • 单个系数的符号可能反转;
  • 不同训练样本会产生不同的系数分配;
  • 预测仍然可能很好,但单个特征解释不可靠。

如果目标是因果结论,需要额外的实验设计、混杂控制和因果假设,不能仅凭逻辑回归系数得出“改变该特征就会导致结果改变”的结论。

10.6 非线性与交互项

如果真实关系不是线性的,可以显式加入特征变换:

x,x2,log(x),分箱(x)x,\quad x^2,\quad \log(x),\quad \text{分箱}(x)

也可以加入交互项:

x1x2x_1x_2

例如:

logp1p=β0+β1x1+β2x2+β3x1x2\log\frac{p}{1-p} = \beta_0+\beta_1x_1+\beta_2x_2+\beta_3x_1x_2

此时 x1x_1 的作用取决于 x2x_2

zx1=β1+β3x2\frac{\partial z}{\partial x_1} = \beta_1+\beta_3x_2

不能再孤立地把 eβ1e^{\beta_1} 当作所有样本上的固定几率比。


十一、从概率到概率变化:边际效应不是固定的

逻辑回归中:

p=σ(z)p=\sigma(z)

Sigmoid 的导数为:

dpdz=p(1p)\frac{dp}{dz}=p(1-p)

因此,对连续特征 xjx_j

pxj=βjp(1p)\frac{\partial p}{\partial x_j} = \beta_jp(1-p)

这解释了为什么同一个系数在不同样本上的概率影响不同:

  • pp 接近 0.50.5 时,p(1p)p(1-p) 最大;
  • pp 接近 0 或 1 时,概率曲线饱和,边际变化很小。

仍以 β1=0.8\beta_1=0.8 为例:

p=0.5p=0.5 时:

px=0.8×0.5×0.5=0.2\frac{\partial p}{\partial x} = 0.8\times0.5\times0.5=0.2

此时 xx 增加一个单位,局部概率变化约为 0.20.2

但当 p=0.99p=0.99 时:

px=0.8×0.99×0.010.0079\frac{\partial p}{\partial x} = 0.8\times0.99\times0.01 \approx0.0079

概率已经接近 1,继续增加 xx 的概率影响很小。


十二、二分类之外:多分类逻辑回归

多分类逻辑回归通常使用 Softmax:

P(y=kx)=ezkj=1KezjP(y=k\mid x) = \frac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}}

其中:

zk=βk0+βkTxz_k=\beta_{k0}+\beta_k^Tx

KK 是类别数。所有类别概率之和为 1。

训练损失是多分类交叉熵:

L=1ni=1nk=1K1(yi=k)logpik\mathcal{L} = -\frac{1}{n} \sum_{i=1}^{n} \sum_{k=1}^{K} \mathbf{1}(y_i=k)\log p_{ik}

常见实现有两种思路:

  • OvR(One-vs-Rest):为每个类别训练一个“该类 vs 其他类”的二分类器;
  • Multinomial/Softmax:一次性建模所有类别的联合概率。

二分类时,Softmax 在数学上可以退化为 Sigmoid;但多分类中不能把每个类别的独立 Sigmoid 输出直接当作互斥类别概率,除非任务本身是多标签分类。


十三、可运行的 scikit-learn 示例

下面示例完成一个相对完整的流程:

  • 生成二分类数据;
  • 使用标准化和逻辑回归组成 Pipeline;
  • 在训练集内部进行概率校准;
  • 使用独立验证集选择阈值;
  • 在测试集上计算概率指标和分类指标。
import numpy as np

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import (
    log_loss,
    brier_score_loss,
    roc_auc_score,
    average_precision_score,
    confusion_matrix,
    classification_report,
)

# 1. 构造数据
X, y = make_classification(
    n_samples=5000,
    n_features=10,
    n_informative=5,
    n_redundant=2,
    weights=[0.85, 0.15],
    random_state=42,
)

# 2. 划分训练集、验证集、测试集
X_train, X_tmp, y_train, y_tmp = train_test_split(
    X,
    y,
    test_size=0.4,
    stratify=y,
    random_state=42,
)

X_valid, X_test, y_valid, y_test = train_test_split(
    X_tmp,
    y_tmp,
    test_size=0.5,
    stratify=y_tmp,
    random_state=42,
)

# 3. 基础模型:标准化 + L2 逻辑回归
base_model = Pipeline(
    steps=[
        ("scaler", StandardScaler()),
        (
            "logreg",
            LogisticRegression(
                C=1.0,
                penalty="l2",
                solver="lbfgs",
                max_iter=1000,
                random_state=42,
            ),
        ),
    ]
)

# 4. 交叉验证校准概率
# estimator 参数适用于较新的 scikit-learn 版本。
calibrated_model = CalibratedClassifierCV(
    estimator=base_model,
    method="sigmoid",
    cv=5,
)

calibrated_model.fit(X_train, y_train)

# 5. 在验证集上根据业务目标选择阈值
valid_prob = calibrated_model.predict_proba(X_valid)[:, 1]

# 示例:假设漏报代价约为误报的 4 倍
threshold = 1.0 / (1.0 + 4.0)

valid_pred = (valid_prob >= threshold).astype(int)

print("chosen threshold:", threshold)
print("validation confusion matrix:")
print(confusion_matrix(y_valid, valid_pred))

# 6. 只在测试集上做最终评估
test_prob = calibrated_model.predict_proba(X_test)[:, 1]
test_pred = (test_prob >= threshold).astype(int)

print("test log loss:", log_loss(y_test, test_prob))
print("test brier score:", brier_score_loss(y_test, test_prob))
print("test roc auc:", roc_auc_score(y_test, test_prob))
print("test average precision:", average_precision_score(y_test, test_prob))
print("test confusion matrix:")
print(confusion_matrix(y_test, test_pred))
print(classification_report(y_test, test_pred, digits=4))

代码中的数据流

  • StandardScaler 只应在训练数据上拟合均值和标准差;
  • Pipeline 保证标准化参数与分类器绑定,避免手工预处理不一致;
  • CalibratedClassifierCV 使用交叉验证生成校准所需的预测,避免直接使用训练集内预测;
  • 阈值在验证集上选择;
  • 测试集只用于最终报告,不能反复用它调阈值。

CalibratedClassifierCV 的参数名和部分行为会随 scikit-learn 版本变化。较新版本使用 estimator,旧版本曾使用 base_estimator。部署前应以目标环境的官方 User Guide 和 API 文档为准,而不是把不同版本的示例混用。


十四、类别不平衡:训练目标、概率和阈值会同时受影响

当正类比例很低时,直接优化普通平均损失可能让模型偏向负类。常见措施包括:

  • 使用 class_weight="balanced"
  • 使用样本权重;
  • 重新采样;
  • 调整决策阈值;
  • 使用 PR-AUC、Recall、Precision 等更适合任务的指标。

但这些措施含义不同。

如果训练时给正类更高权重,模型优化的实际上是加权损失:

L=1niwi[yilogpi+(1yi)log(1pi)]\mathcal{L} = -\frac{1}{n} \sum_i w_i \left[ y_i\log p_i+(1-y_i)\log(1-p_i) \right]

此时模型分数可能更适合排序或召回,但未经额外处理的输出不一定仍然是原始数据分布下的真实概率。

因此:

类别权重、过采样和阈值调整不能互相替代。

如果业务需要可靠概率,应在接近线上真实分布的数据上重新校准。过采样后的训练集分布与线上分布不同,直接把其输出当作线上概率是常见错误。


十五、数据泄露、时间切分与概率漂移

15.1 数据泄露会制造虚假的高分

如果特征在预测时点实际上不可获得,或者由未来结果计算得到,逻辑回归可能获得极高的 AUC 和极低的损失,但上线后立即失效。

典型例子:

  • 用“是否已取消订单”预测订单是否会取消;
  • 用事件发生后的人工处理结果预测事件是否发生;
  • 用全量数据计算标准化参数,再评估历史测试集;
  • 用测试集反复选择阈值和正则化强度。

评估必须严格模拟线上时间点。

15.2 时间切分通常比随机切分更接近生产

如果任务存在时间变化,例如:

  • 欺诈策略变化;
  • 用户行为变化;
  • 产品策略变化;
  • 生成式 AI 的提示词和攻击方式变化;

随机切分可能把未来模式泄露到训练集。应考虑按时间切分,并在后续时间窗口评估。

15.3 基准率变化会破坏校准

即使条件分布相对稳定,正类先验概率变化也可能影响输出概率。例如:

  • 训练数据中的欺诈率为 1%;
  • 线上某段时间欺诈率变为 3%。

此时模型排序能力可能仍然不错,但概率的绝对含义可能发生偏移。需要监控:

  • 正类比例;
  • 概率分布;
  • 分箱后的实际命中率;
  • Brier 分数和校准曲线;
  • 业务阈值下的 Precision、Recall 和告警量。

十六、逻辑回归的失败表现与诊断方法

16.1 训练损失低,测试损失高

这通常说明:

  • 特征过多;
  • 正则化过弱;
  • 数据量不足;
  • 训练与测试分布不同;
  • 存在重复或近重复样本;
  • 特征中有泄露。

诊断时应同时查看训练和测试的:

  • log loss;
  • ROC-AUC;
  • PR-AUC;
  • 校准曲线;
  • 概率分布。

只看 Accuracy 很容易漏掉概率失真和少数类失败。

16.2 AUC 很高,但 Precision 很低

这通常发生在正类稀少时。AUC 评价整体排序,不能回答“在当前告警量下有多少告警是真正例”。

应固定一个可执行的业务条件,例如:

  • 每天最多处理 5000 条;
  • Recall 至少达到 80%;
  • Precision 不低于某个下限;

然后在验证集上选择阈值,并在后续时间窗口验证。

16.3 概率大量接近 0 或 1

可能原因包括:

  • 完全分离或近完全分离;
  • 正则化太弱;
  • 训练数据过少;
  • 特征泄露;
  • 模型没有经过校准;
  • 训练分布与线上分布不同。

不要因为概率“更自信”就认为模型更好。自信错误的预测会受到对数损失的强烈惩罚,并可能导致危险的自动决策。

16.4 系数符号反常

可能原因包括:

  • 特征之间存在强共线性;
  • 存在抑制效应;
  • 使用了交互项但忽略其含义;
  • 类别编码或参考类别理解错误;
  • 正则化改变了系数分配;
  • 训练数据与业务直觉不一致。

系数异常时应检查特征相关矩阵、编码方式、样本分层和数据生成过程,而不是立即删除该特征。


十七、阈值、拒答与权限控制不是一回事

在推荐、风控、内容审核和生成式 AI 安全场景中,逻辑回归可能用于:

  • 内容是否需要人工审核;
  • 请求是否疑似违规;
  • 用户是否可能流失;
  • 文档是否属于某一类别;
  • 检索结果是否相关;
  • 生成内容是否需要二次检查。

但分类模型的预测结果不能自动等同于权限决定。

例如,模型输出“用户可能有管理员风险”的概率,只能作为风险信号;真正的权限控制仍应由:

  • 身份认证;
  • 角色与策略;
  • 资源访问检查;
  • 审计日志;
  • 明确的拒绝路径;

共同完成。不能用一个概率阈值替代确定性的授权规则。

对于高风险自动化决策,可以设置拒答或人工复核区间:

p<tlow自动判为负类p<t_{\text{low}} \Rightarrow \text{自动判为负类}

p>thigh自动判为正类p>t_{\text{high}} \Rightarrow \text{自动判为正类}

tlowpthigh人工复核t_{\text{low}}\leq p\leq t_{\text{high}} \Rightarrow \text{人工复核}

这样做会增加延迟和人工成本,但可以减少边界样本上的高风险错误。阈值设计必须把模型质量、人工容量、响应时延和错误代价放在同一个生产系统中衡量。


十八、与深度学习和生成式 AI 的关系

逻辑回归的最后一层经常出现在更复杂的模型中:

  • 二分类神经网络通常使用一个线性输出层加 Sigmoid;
  • 多分类神经网络通常使用线性输出层加 Softmax;
  • 训练时同样常用二分类或多分类交叉熵;
  • 训练目标仍然是最大化标签的条件似然。

区别在于,逻辑回归直接对原始特征使用线性函数,而深度网络先通过多层非线性变换学习表示:

h=fθ(x)h=f_{\theta}(x)

最后再计算:

z=wTh+bz=w^Th+b

p=σ(z)p=\sigma(z)

所以可以把逻辑回归理解为:

在给定特征表示上的线性概率模型。

如果深度模型的最后一层输出概率,在部署时同样需要考虑:

  • 概率是否校准;
  • 阈值是否符合代价;
  • 训练分布是否代表线上分布;
  • 推理延迟和成本;
  • 误判后的权限、审核和恢复流程。

“用了深度模型”并不会自动解决阈值和校准问题。


十九、如何选择评估方式

根据任务目标,评估重点不同:

  • 需要可靠概率:关注 log loss、Brier 分数、校准曲线;
  • 关注排序能力:关注 ROC-AUC 或 PR-AUC;
  • 正类稀少:优先查看 PR 曲线和阈值下的 Precision;
  • 漏报代价高:关注 Recall、FN 数量和召回下限;
  • 误报代价高:关注 Precision、人工处理量和 FP 成本;
  • 资源有限:直接优化固定容量下的收益或代价;
  • 高风险场景:增加拒答区间、人工复核和审计指标。

没有一个指标能同时完整描述概率质量、排序能力和业务决策质量。尤其不能用 Accuracy 代替所有评估:当正类占比为 1% 时,全部预测为负类也可能得到 99% 的 Accuracy,但模型没有任何实际识别能力。


二十、核心边界

逻辑回归适合以下条件:

  • 标签定义清楚;
  • 特征在预测时点可获得;
  • 对数几率与特征关系可以近似建模;
  • 需要较强的可解释性;
  • 需要稳定、低成本、低延迟的推理;
  • 可以通过特征变换表达必要的非线性。

它的主要边界包括:

  1. 默认决策边界是线性的;
  2. 系数解释依赖尺度、编码和其他特征;
  3. 相关性不等于因果关系;
  4. 类别权重和采样可能破坏原始概率含义;
  5. 高 AUC 不代表概率已校准;
  6. 默认阈值 0.50.5 不代表业务最优;
  7. 完全分离可能导致未正则化参数发散;
  8. 训练集上的概率和指标不能代表线上表现;
  9. 预测概率不能替代认证、授权和审计系统。

从原理上看,逻辑回归连接了几个重要概念:线性模型通过 logit 建模概率,最大似然自然导出交叉熵,系数指数化后得到几率比,阈值把概率转换为代价相关的决策,校准则验证概率是否具有频率意义。理解这条链路,才能避免把“分数”“概率”“类别”和“业务动作”混为一谈。


系列导航与关联阅读

官方资料

本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。