一条直线怎样输出分类概率?从对数几率到逻辑回归决策边界
从线性回归阈值化的失败出发,推导逻辑回归的 logit、Sigmoid、决策边界与梯度,并用 NumPy 和当前 sklearn API 完成可调试实现。
上一篇用 L2 正则化约束了连续预测模型的权重。现在进入经典机器学习的第一个分类模型:面对“客户是否流失”“肿瘤是否恶性”这类二分类问题,我们既要一个类别,也常常需要一个可比较的风险分数。
最直接的想法是先用线性回归预测 0 或 1,再以 0.5 为阈值。但线性回归输出没有上下界,加入一个极端样本就可能把整条拟合直线拉偏;1.7 或 -0.4 也不能直接解释为概率。
逻辑回归(Logistic Regression)保留了线性模型清晰的几何结构,却改变了输出的含义。本文只回答三个紧密问题:线性分数如何变成概率、概率阈值如何变成决策边界,以及交叉熵如何学习这条边界。
01 为什么“线性回归后截断”不是好分类器?#
设一维输入 表示某项风险指标,标签 。线性回归拟合:
若训练点是 (1,0)、(2,0)、(3,1)、(4,1),一条直线也许能让 的样本超过 0.5。但加入一个很远的正类点 (20,1) 后,平方误差会强烈关注这个点的残差,斜率和截距都可能改变,原来的分类阈值随之移动。
y
1.0 │ ● ● ● 极端正类点
│ ╱ _______╱
0.5 │──────╳──── 分类阈值 ───╳──────── 新交点
│ ╱
0.0 │ ● ●
└────────────────────────────────────────► xtext问题不是“输出后裁剪到 ”就能修复的:裁剪区间外梯度为 0,而且平方误差仍在拟合连续数值 0 和 1,没有直接刻画类别概率的似然。
我们需要一个模型满足:
- 输出严格位于 ,可解释为正类概率;
- 参数仍通过可导目标学习;
- 最终边界仍能看出每个特征怎样推动预测。
02 先线性打分,再映射为概率#
对一批 个样本、每个样本 个特征,逻辑回归先计算线性分数:
再通过 S 形函数(Sigmoid Function):
张量形状沿数据流变化如下:
X [N,D] ──矩阵乘──► z=Xw+b [N]
w [D] │
b [] ▼ sigmoid
p=P(y=1|x) [N]
│ threshold τ
▼
y_pred [N]text- :设计矩阵,每行一个样本;
- :特征权重;
- :截距;
- :对数几率分数(Logit);
- :每个样本属于正类的估计概率。
Sigmoid 单调递增,因此 越大,正类概率越高:
| 模型含义 | ||
|---|---|---|
| 负类几率约为正类的 9 倍 | ||
| 两类等可能 | ||
| 正类几率约为负类的 9 倍 |
03 “对数几率”为什么会变成线性函数?#
概率 对应的几率(Odds)为:
它表示“正类概率是负类概率的多少倍”。对几率取自然对数,得到对数几率(Log-Odds):
把上式对 求解,正好得到 Sigmoid。因此逻辑回归的核心假设不是“概率与特征线性”,而是:对数几率与特征线性。
若某个特征 增加 1,而其他特征不变,对数几率增加 ;几率则乘以 。例如 ,该特征每增加一个单位,预测正类几率乘以 2。
特征尺度同样影响系数含义:年龄增加 1 岁与收入增加 1 元不是可比较的变化。解释系数前必须记录单位;若加入正则化,通常还应在防泄漏 Pipeline 中标准化。
04 决策边界究竟在哪里?#
默认阈值 时:
所以决策边界是:
二维情况下,,边界为一条直线;更高维时是超平面(Hyperplane)。向量 垂直于边界,并指向正类分数增大的方向。
x₂
▲ + + 正类:wᵀx+b > 0
│ + +
│ - - - - - - - - - 边界:wᵀx+b = 0
│ ○ ○
│ ○ ○ 负类:wᵀx+b < 0
└──────────────────────► x₁
↗ w(边界法向量)text若业务阈值改成任意 ,边界不再是 ,而是:
例如要求 才触发人工复核,则 logit 至少为 。这会沿法向量平移边界,却不重新训练模型。阈值必须在验证集上按漏报和误报成本选择,不能用测试集调。
05 用三个点手算概率、损失与一步更新#
只用一个特征且设 :
x = [1, 2, 3]
y = [0, 0, 1]
w = 0text此时三个 logit 都是 0,概率都是 0.5。平均二元交叉熵(Binary Cross-Entropy,BCE)为:
上一篇分类损失文章已经推导过单样本 。结合 :
代入数值:
梯度竟然为 0。不是模型已经学好,而是“两个较小的负类点”和“一个较大的正类点”在当前坐标中刚好抵消。若同时学习截距:
截距仍会向负方向更新。这个极小例子提醒我们:截距不是装饰;中心化、样本分布和特征相关性都会影响梯度。
再看单个正样本 。梯度为:
取学习率 :
新 logit 为 ,新概率为 ,损失从 降到 。
06 不调用 fit,先写出训练本体#
下面用 NumPy 明确完成前向、稳定交叉熵、梯度和更新。np.logaddexp(0, z)-yz 等价于二分类负对数似然,能避免直接计算 log(sigmoid(z)) 的溢出问题。
import numpy as np
X = np.array([
[0.0, 0.0],
[0.0, 1.0],
[1.0, 0.0],
[1.0, 1.0],
[2.0, 1.0],
], dtype=np.float64) # [N=5, D=2]
y = np.array([0.0, 0.0, 0.0, 1.0, 1.0]) # [5]
w = np.zeros(X.shape[1], dtype=np.float64) # [D=2]
b = 0.0 # []
learning_rate = 0.1
l2 = 0.01
for step in range(2000):
logits = X @ w + b # [5]
probabilities = 1.0 / (1.0 + np.exp(-logits))
data_loss = np.mean(np.logaddexp(0.0, logits) - y * logits)
objective = data_loss + 0.5 * l2 * np.dot(w, w)
residual = probabilities - y # [5]
grad_w = X.T @ residual / X.shape[0] + l2 * w # [2]
grad_b = residual.mean() # []
w -= learning_rate * grad_w
b -= learning_rate * grad_b
new_X = np.array([[1.5, 0.5]]) # [num_queries=1, D=2]
new_logit = new_X @ w + b # [1]
new_probability = 1.0 / (1.0 + np.exp(-new_logit))
new_prediction = (new_probability >= 0.5).astype(np.int64)python需要调试的最小不变量:
assert X.ndim == 2 and y.shape == (X.shape[0],)
assert w.shape == (X.shape[1],)
assert logits.shape == probabilities.shape == y.shape
assert np.isfinite(objective)
assert np.isfinite(grad_w).all() and np.isfinite(grad_b)python07 用当前 scikit-learn API 落地#
截至本文写作时,scikit-learn 1.9 的 LogisticRegression 默认执行正则化逻辑回归;C 是正则化强度的倒数,越小约束越强。1.8 起 penalty 参数已经弃用,因此新代码用 l1_ratio 和 C 表达正则化类型。
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# X_train: [num_train, num_features]
# y_train: [num_train],元素是类别标签 0/1
pipeline = make_pipeline(
StandardScaler(),
LogisticRegression(
C=1.0,
l1_ratio=0.0, # 0.0 表示纯 L2;不再显式传已弃用的 penalty
solver='lbfgs',
max_iter=1000,
),
)
search = GridSearchCV(
estimator=pipeline,
param_grid={'logisticregression__C': np.logspace(-3, 3, 13)},
scoring='neg_log_loss',
cv=StratifiedKFold(n_splits=5, shuffle=True, random_state=42),
n_jobs=-1,
refit=True,
)
search.fit(X_train, y_train)
logits = search.decision_function(X_val) # [num_val]
probabilities = search.predict_proba(X_val) # [num_val, 2]
predictions = search.predict(X_val) # [num_val]
positive_column = np.flatnonzero(search.classes_ == 1).item()
positive_probability = probabilities[:, positive_column]python重要接口语义:
decision_function(X)返回二分类中classes_[1]的有符号分数;大于 0 时默认预测该类;predict_proba(X)的列顺序由classes_决定,不能永远假设第二列就是业务正类;coef_在二分类中形状为[1,D],intercept_为[1];C=np.inf表示无正则化,但通常应通过交叉验证选择有限C;max_iter是求解器迭代上限,不是小批量训练的 epoch 数;若出现ConvergenceWarning,先标准化并检查尺度,再考虑增加它。
标准化必须在 Pipeline 内部,让每个交叉验证折只用本折训练数据计算均值和方差。
08 训练、阈值选择与推理不要混成一步#
训练集
└── 学 w,b 与预处理统计量
│
验证集 ▼
├── 选 C、特征与模型
└── 按成本选概率阈值 τ
│ 全部冻结
测试集 ▼
└── 一次最终验收
│
线上请求 ──► 同一预处理 ─► p ─► τ ─► 动作text模型输出 0.7 并不自动意味着“必须判正类”。若漏掉恶性病例代价远大于误报,可以降低阈值;若人工复核资源紧张,可以提高阈值。阈值改变的是决策规则,不改变模型已经学到的概率排序。
还要区分 区分能力(Discrimination) 与 概率校准(Calibration):模型可能把正样本普遍排在负样本前面,却让“预测 0.8”的样本只有 60% 真为正类。交叉熵训练有概率语义,但有限数据、正则化和分布漂移仍会破坏校准。
09 常见错误与调试路径#
- 把类别编码顺序想当然。 总是打印
classes_,按业务正类定位概率列。 - 把 logit 当概率。
decision_function可为任意实数;只有predict_proba位于 。 - 忽略标准化。 不同尺度会让求解器收敛变慢,也让 L2 对各系数的约束失衡。
- 误解
C。C越大正则化越弱,与 Ridge 的alpha方向相反。 - 盲目提高
max_iter。 若损失不收敛,先查非有限值、尺度、完全分离和重复特征。 - 在测试集选阈值。 这会把测试信息带入开发;阈值是超参数,应在验证集确定。
- 只看准确率。 类别失衡时,同时记录对数损失、精确率、召回率和混淆矩阵。
- 把系数当因果效应。 相关特征、选择偏差和遗漏变量会改变系数;预测关联不等于干预因果。
可先检查:
model = search.best_estimator_.named_steps['logisticregression']
assert np.isfinite(model.coef_).all()
assert np.isfinite(model.intercept_).all()
assert np.allclose(probabilities.sum(axis=1), 1.0)
print('classes:', search.classes_)
print('iterations:', model.n_iter_)
print('logit range:', logits.min(), logits.max())python若训练和验证都接近随机,检查特征是否真的含信号、标签是否错位;若训练很好而验证很差,回到学习曲线与正则化诊断;若线上概率整体偏高或偏低,检查分布漂移与校准,而不是只移动阈值掩盖问题。
10 它何时会失败?与相近方法怎样区分?#
逻辑回归默认只有线性决策边界。对“同心圆”或异或(XOR)结构,原始特征空间中不存在一条直线分开两类;必须构造非线性特征、使用核方法、树模型或神经网络。
完全分离时,未正则化最大似然会不断放大系数,让训练概率逼近 0 和 1,却没有有限最优参数;默认正则化能缓解,但不能创造新信息。
| 方法 | 学到什么 | 概率输出 | 决策边界 | 主要区别 |
|---|---|---|---|---|
| 逻辑回归 | 一组全局线性权重 | 原生 Sigmoid / Softmax | 线性 | 可解释、训练与推理快 |
| 线性回归阈值化 | 连续目标的平方误差拟合 | 无可靠概率含义 | 线性 | 目标与分类不匹配 |
| 线性 SVM | 最大间隔超平面 | 默认不是概率 | 线性 | 更关注边界附近样本与间隔 |
| K 近邻 | 保存训练实例并局部投票 | 邻域票数比例 | 高度非线性 | 几乎不训练,推理成本高 |
| 决策树 | 特征阈值规则 | 叶节点频率 | 轴对齐分段 | 自动表示非线性交互但易高方差 |
它还会在标签噪声严重、极端类别失衡、预测人群与训练人群不同、关键交互未进入特征时失败。清晰的线性边界是优点,也是表达能力的上限。
11 今天真正需要记住什么?#
- 逻辑回归先计算线性 logit,再用 Sigmoid 得到正类概率;线性的是对数几率,不是概率本身。
- 默认 0.5 阈值对应 ,所以二分类边界是超平面, 是其法向量。
- BCE 梯度通过 调整权重;稳定实现应直接在 logits 上计算损失。
- scikit-learn 的
C越小正则化越强,概率列顺序必须按classes_读取。 - 训练参数、选择正则化和选择业务阈值属于不同阶段,测试集只做最后验收。
12 思考题与小练习#
练习 1:从概率还原 logit
若模型输出正类概率 0.8,则 。若阈值也是 0.8,这个样本恰好位于新决策边界上。
练习 2:画出一条二维边界
给定 、,边界为 ,即 。判断点 的类别:logit 为 1,默认阈值下预测正类,概率约为 0.731。
练习 3:验证正则化方向
把网格改为 C=[0.001, 1, 1000],记录训练对数损失、验证对数损失和系数范数。解释为什么小 C 往往让系数更小,而最大 C 不一定给出最好验证结果。
相关工作#
- Cox: The Regression Analysis of Binary Sequences ↗:逻辑模型用于二元响应分析的经典论文。
- Nelder & Wedderburn: Generalized Linear Models ↗:将逻辑回归放入广义线性模型统一框架的奠基工作。
- Platt: Probabilistic Outputs for Support Vector Machines ↗:从概率校准角度比较间隔模型与正则化似然方法。
- Niculescu-Mizil & Caruana: Predicting Good Probabilities ↗:比较分类器概率质量与校准方法的代表性研究。
- scikit-learn: LogisticRegression ↗:当前参数、求解器、正则化与输入输出形状说明。
13 下一篇预告#
逻辑回归用一组全局权重画出一条线性边界。如果类别边界弯弯曲曲,但相似样本往往拥有相同标签,能否不假设全局公式,直接询问“离新样本最近的训练点是谁”?下一篇将进入 K 近邻,追踪距离计算、局部投票、特征尺度与维度灾难。