首页 / 资讯中心 / 文章详情

逻辑回归全解析:从sigmoid、交叉熵到工程落地

逻辑回归全解析:从sigmoid、交叉熵到工程落地 ★ FEATURED ARTICLE
1. 从一条分界线说起逻辑回归到底在解决什么问题线性回归画一条线去拟合连续值这件事直观、好理解。但现实里有大量问题不是预测一个数而是判断属于哪一类——邮件是不是垃圾邮件、用户会不会点击、交易是不是欺诈、零件是不是次品。这时候用线性回归硬套会遇到两个让人难受的地方输出值域是负无穷到正无穷没法解释成概率损失函数对错分的惩罚不对称模型会为了迁就少数极端样本把分界线推歪。逻辑回归干的事情很朴素先把线性组合的结果塞进一个把实数压到 (0,1) 区间的函数里再拿这个输出当属于正类的概率用。名字里带回归本质是个分类器这个命名错位劝退过不少人我第一次看的时候也愣了半天。它值得花时间啃透原因不在于它多强而在于它是整个监督学习的地基。梯度下降怎么推、正则化怎么起作用、特征工程为什么重要、类别不平衡怎么处理、概率校准是什么——这些问题的答案在逻辑回归里都最干净、最容易看清楚。后面上树模型、上神经网络遇到调参调不动的时候回头想想逻辑回归里的那些约束往往能定位到问题。这篇东西面向两类人一类是刚学完线性回归、正准备跨进分类门槛的另一类是用过sklearn.linear_model.LogisticRegression但只知道fit和predict、对里头发生了什么说不清楚的。全文会从假设函数一路推到梯度推导、正则化、阈值选择、工程落地中间穿插我自己踩过的坑。数学部分我会尽量把每一步的动机讲明白不搞显然可得。一个提醒逻辑回归的推导里有很多符号约定上的分歧比如用 w 还是 θ正类标 1 还是 1不同教材写得不一样。看的时候别纠结符号盯住形状和维度就行。2. 假设函数的构造为什么非得是 sigmoid2.1 从线性打分到概率输出先设样本是 $x \in \mathbb{R}^d$权重 $w \in \mathbb{R}^d$偏置 $b$。线性部分算的是 $z w^\top x b$这个 $z$ 就是一个打分越大越偏向正类。问题是 $z$ 的取值没有边界而概率必须卡在 0 到 1 之间。最直接的念头是做个硬截断$z0$ 输出 1否则输出 0。这个方案在感知机里用过毛病很致命——它不可导梯度下降根本没法用。而且在 $z0$ 附近样本稍微动一点输出就从 0 跳到 1模型对噪声毫无容忍度。换成 sigmoid$$\sigma(z) \frac{1}{1 e^{-z}}$$这个函数有几个恰好对症的性质。单调递增保证打分越高概率越大值域严格落在 (0,1)端点取不到但可以无限逼近处处可导而且导数形式极简$$\sigma(z) \sigma(z)\big(1 - \sigma(z)\big)$$最后这条是整个推导能收得干净的关键。导数可以用函数值本身表示意味着前向算完输出反向要导数时直接拿输出相乘就行不需要重算指数。工程实现上这一点省了很多事。2.2 换个视角它其实在对数几率上做线性拟合很多人把 sigmoid 当成一个拍脑袋选出来的压缩函数其实它有明确的统计来源。定义正类概率 $p P(y1 \mid x)$几率为 $p/(1-p)$取对数得对数几率log-odds$$\log\frac{p}{1-p} w^\top x b$$反解出 $p$$$p \frac{1}{1 e^{-(w^\top x b)}}$$所以逻辑回归的真正假设是特征在对数几率尺度上和标签呈线性关系。这个视角非常有用它告诉你什么时候该做特征变换——如果某个特征和 log-odds 明显不是线性比如呈 U 形那就该加平方项或者做分箱。我见过不少模型效果上不去不是调参没调好而是某个关键特征和 log-odds 的关系压根不是线性的硬塞进去模型学不动。2.3 决策边界是线性的但概率面不是一个常见误解是逻辑回归只能处理线性可分的问题所以很弱。这话对了一半。predict用的决策规则是 $p 0.5$等价于 $w^\top x b 0$确实是 $d$ 维空间里的一个超平面。但模型输出的是概率是一个连续曲面。决策边界是线性的不代表模型的表达能力只有线性——你可以手工构造多项式特征、交叉特征把它喂进去边界依然线性但已经是在变换后的空间里线性了。我在一个用户流失预测的场景里验证过这一点原始 20 来个特征直接上逻辑回归AUC 大概 0.78加了几个比值特征和最近 30 天活跃天数的分箱哑变量之后同样的模型涨到 0.84。模型没换换的是特征空间。这也是为什么我始终认为逻辑回归的上限由特征工程决定下限由数据质量决定超参数的作用排在后面。3. 损失函数极大似然、交叉熵、KL 散度其实是同一件事3.1 从似然函数推起设数据集 ${(x_i, y_i)}_{i1}^n$$y_i \in {0,1}$样本独立。单个样本的预测概率可以紧凑写成$$P(y_i \mid x_i) \hat{p}_i^{y_i}(1-\hat{p}_i)^{1-y_i}$$这个写法看着绕验算一下就明白$y_i1$ 时只剩 $\hat{p}_i$$y_i0$ 时只剩 $1-\hat{p}_i$。取负对数似然$$L(w,b) -\sum_{i1}^n \Big[ y_i \log \hat{p}_i (1-y_i)\log(1-\hat{p}_i) \Big]$$这就是交叉熵。记住这个结论逻辑回归的损失函数不是人为设计的是极大似然估计的直接产物。这也解释了为什么用平方损失训练逻辑回归效果差——平方损失对应的是高斯噪声假设和伯努利分布不匹配而且配合 sigmoid 会得到一个非凸的优化目标。3.2 损失函数长什么样为什么这很重要交叉熵作为 $\hat{p}$ 的函数在 $\hat{p} \to y$ 时趋近 0在 $\hat{p}$ 远离 $y$ 时急剧上升而且上升速度是无界的。预测100% 是正类结果实际是负类惩罚会非常重。这个性质带来一个实际影响逻辑回归对标注错误的样本异常敏感。一条被错误标注的样本如果特征特别典型模型本会给出极端概率它产生的梯度会极大把整个决策面拽偏。我曾经在一个工业质检数据集上遇到这个问题几千条样本里混了 3% 左右的错标模型 AUC 死活卡在 0.85 上不去。清理掉那批脏数据之后直接跳到 0.93。教训是上模型之前先花时间抽样核对标签比调参收益高一个数量级。3.3 交叉熵和平方损失的梯度差异单样本下交叉熵对打分的导数有个极其漂亮的形式$$\frac{\partial \ell}{\partial z} \hat{p} - y$$推导过程留到下一节这里先看它的含义。误差 预测概率 - 真实标签。预测完全正确时梯度为 0预测错了梯度正比于偏差程度。这个形式和线性回归里平方损失的梯度形式一样但来源截然不同。对比一下平方损失配 sigmoid 的情况$\ell \frac{1}{2}(\hat{p}-y)^2$对 $z$ 求导会多乘一个 $\sigma(z) \hat{p}(1-\hat{p})$。当预测错得很离谱时$\hat{p}$ 接近 0 而 $y1$$\sigma$ 趋近于 0梯度反而被压没了学得非常慢。这就是所谓的梯度饱和。这个现象在深层网络里是灾难在逻辑回归里虽然没那么致命但依然会拖慢收敛。两个因素叠加——非凸、梯度消失——让平方损失在分类问题上彻底失去竞争力。损失函数对 $z$ 的梯度凸性错分严重时的梯度大小交叉熵$\hat{p} - y$凸大收敛快平方损失$(\hat{p}-y)\cdot\hat{p}(1-\hat{p})$非凸小饱和4. 梯度推导把每一步都拆开算一遍4.1 从头到底的一次完整求导很多人会背梯度是 $(\hat{p}-y)x$但推不出来。我从最底层开始一步步走。设 $z w^\top x b$$\hat{p} \sigma(z) 1/(1e^{-z})$单样本损失 $\ell -[y\log\hat{p} (1-y)\log(1-\hat{p})]$。先对 $\hat{p}$ 求导$$\frac{\partial \ell}{\partial \hat{p}} -\frac{y}{\hat{p}} \frac{1-y}{1-\hat{p}} \frac{\hat{p}-y}{\hat{p}(1-\hat{p})}$$再对 $z$ 求导链条上还差 $\partial \hat{p}/\partial z \hat{p}(1-\hat{p})$。两者相乘$$\frac{\partial \ell}{\partial z} \frac{\hat{p}-y}{\hat{p}(1-\hat{p})} \cdot \hat{p}(1-\hat{p}) \hat{p} - y$$分母恰好被约掉了。这不是巧合正是选用 sigmoid 配交叉熵的收益也是工业实现里为什么这两个东西永远成对出现。最后对参数求导。$\partial z/\partial w x$$\partial z/\partial b 1$所以$$\nabla_w \ell (\hat{p}-y),x, \qquad \frac{\partial \ell}{\partial b} \hat{p} - y$$整批样本累加$$\nabla_w L \sum_{i} (\hat{p}_i - y_i),x_i X^\top(\hat{p} - y)$$矩阵形式最后落到 $X^\top(\hat{p}-y)$形状是 $d \times 1$和 $w$ 一致。这个表达式的工程意义很重要梯度计算可以完全向量化一次矩阵乘法搞定不需要 Python 循环遍历样本。这也是逻辑回归能在百万级样本上跑得飞快的根本原因。4.2 凸性为什么逻辑回归不怕局部最优交叉熵损失配合 sigmoid对 $w$ 是严格凸的在数据不退化的情况下。验证思路是看 Hessian 矩阵$$H X^\top S X, \qquad S \text{diag}\big(\hat{p}_i(1-\hat{p}_i)\big)$$因为 $\hat{p}_i \in (0,1)$所以 $\hat{p}_i(1-\hat{p}_i) 0$$S$ 正定$H$ 半正定当 $X$ 列满秩时正定。结论损失曲面是一个碗只有全局最小值没有局部坑。这个性质的实际价值在于你不需要操心初始化。全零初始化、随机初始化最后都会收敛到同一个解数值误差范围内。这和神经网络的处境完全不同。所以看到有人调逻辑回归的随机种子指望提升效果基本可以判定是在做无用功——除非用了liblinear之外的求解器并且数据本身有共线性问题。4.3 三种求解器的差异什么时候该换sklearn的LogisticRegression有若干个solver可选实际用起来差异不小solver适用场景关键特点备注lbfgs默认中小规模L2拟牛顿法收敛稳不支持 L1liblinear小样本需要 L1坐标下降单线程大数据慢不支持 multinomialnewton-cg中小规模L2牛顿法二阶信息内存开销高newton-cholesky样本数远大于特征数利用结构加速特征数大时不合适sag大样本L2随机平均梯度需标准化特征saga大样本支持 L1/弹性网改进版 SAG收敛可能需调max_iter我的经验是默认先lbfgs需要稀疏解换saga配 L1样本几万以内且特征也少的时候liblinear很快。用sag/saga的时候务必先把特征标准化否则各维尺度差异会让收敛极慢这一点在文档里写得比较轻描淡写实际坑很深。我见过有人用saga加未标准化的金额特征量级上万跑了十分钟没收敛标准化后 3 秒完事。5. 正则化不是可选项而是建模的一部分5.1 L2 为什么能治共线性加上 L2 惩罚后目标函数变为$$J(w) L(w) \frac{\lambda}{2}|w|_2^2$$对应梯度多一项 $\lambda w$。从优化角度看它把解往原点拉抑制权重绝对值。真正关键的作用在共线性场景。假设两个特征几乎完全相关比如月收入和年收入不加正则时最优解不唯一——把权重在两者之间随意分配损失函数值都一样数值求解器会在这些解之间乱跳权重符号甚至可能一正一负、大得离谱。加了 L2 之后$|w|^2$ 这一项打破了平局唯一解会倾向于把权重均摊解就稳定了。我在一个信用评分的项目里吃过这个亏模型离线 AUC 很好上线后每周重训同一个特征的系数在 0.8 和 -1.2 之间反复横跳业务方看报表直接懵了。后来加上适度的 L2 并统一做特征筛选系数才稳定下来。如果你需要向非技术方解释模型系数正则化和共线性处理不是优化项是可交付的前提。5.2 L1 的稀疏性和它的使用边界L1 惩罚 $\lambda|w|_1$ 的几何解释是菱形约束最优解容易落在坐标轴上从而产生精确为零的权重天然带特征选择功能。特征维度上万、明显只有少数有用时L1 是合理选择。但 L1 有两个坑。一是相关特征中它只会随机保留一个其余全压成零这在特征高度冗余的场景下会让结果极不稳定重训一次选出来的特征就换了一批。二是 L1 的解在零点不可导sklearn里必须用liblinear或sagalbfgs不支持。实践中我更常用的是弹性网penaltyelasticnet配saga用l1_ratio在 L1 和 L2 之间折中。它既保留一定的稀疏性又不会因为特征相关就只留一个稳定性好很多。5.3 C 参数别搞反了方向sklearn用的是 $C$ 而不是 $\lambda$关系是 $C 1/\lambda$。$C$ 越小正则越强。这个反直觉的设定坑过不少人——我见过有人觉得模型过拟合把 C 调大点压一压方向完全搞反了。调 C 的正确姿势是对数网格搜索比如[0.001, 0.01, 0.1, 1, 10, 100]配合交叉验证。观察训练集和验证集的指标差距来判断方向训练集远好于验证集 → 过拟合 → 减小 C两者都差 → 欠拟合 → 增大 C或者回头检查特征一个容易忽略的点如果你用的是Pipeline加标准化正则化的强度会受标准化影响。因为标准化改变了特征的尺度同样的 C 在不同标准化方案下效果不同。所以调 C 必须在标准化之后、且在同一个 Pipeline 里做交叉验证否则网格搜索出来的最优 C 换到生产环境会失效。6. 阈值不是 0.5概率输出到业务决策的那一段路6.1 默认阈值的隐含假设predict()默认用 0.5 切分这个默认值背后藏着一个假设两类错误的代价相等。现实中几乎从不成立。风控场景漏掉一个欺诈用户的损失可能是误伤一个正常用户的几十倍疾病筛查漏诊的代价远高于误诊带来的复查成本。这时候 0.5 就是个很糟糕的选择。正确做法是拿验证集的概率输出扫一遍候选阈值针对业务指标选点。核心工具是混淆矩阵的四个格子预测正预测负实际正TPFN漏实际负FP误伤TN然后按业务定义代价函数比如 $C c_{FN}\cdot FN c_{FP}\cdot FP$选让 $C$ 最小的阈值。如果拿不到精确的代价数值退一步用 F-beta 分数$\beta 1$ 偏向召回$\beta 1$ 偏向精确率。6.2 一个具体的阈值选择流程我在一个内容审核的模拟项目里是这么做的用 5 折交叉验证拿到每个样本的 out-of-fold 预测概率必须用 OOF不能在训练集上取概率否则严重乐观在 0 到 1 之间按 0.01 步长扫阈值对每个阈值算混淆矩阵画出召回率、精确率随阈值变化的曲线标出业务能接受的最低下限在满足下限的阈值区间里选让总代价最小的那个把这个阈值固化到推理代码里并且在配置文件中显式写明不要用魔法数字第 5 步很容易被忽略。我见过阈值直接写在代码里、注释都没有的项目半年后有人重构代码时删掉了那行模型行为整体偏移排查了两天才发现问题。6.3 概率校准模型输出的0.9真的是 90% 吗逻辑回归的输出常被认为天然校准良好这个说法在模型正确设定时成立但在以下情况下会失效用了强正则、特征做了复杂变换、训练集正负比例和真实分布不一致重采样之后。校准的检查方法是画可靠性曲线把预测概率分桶比如 10 个桶横轴是桶的平均预测概率纵轴是桶内的实际正例率。理想情况是一条对角线。如果曲线在对角线下方说明模型过于自信。修正手段有 Platt 校准本质就是在输出上再套一个一维逻辑回归和等渗回归非参数样本少时容易过拟合。sklearn里CalibratedClassifierCV直接可用。注意校准必须在独立的验证集上拟合且要防止数据泄漏。我见过把校准器在训练集上 fit 的写法校准后的概率看起来非常漂亮上线后完全不对。7. 类别不平衡与特征处理那些决定成败的工程细节7.1 不平衡不是先调class_weight就完事正负比 1:100 的时候模型倾向于全部预测为负类准确率还有 99%但毫无用处。常见的三板斧第一换评估指标。准确率在这里是废的看 AUC、PR-AUC、召回率固定精确率。PR-AUC 在极度不平衡时比 ROC-AUC 更敏感我通常两个都看。第二class_weightbalanced。它给少数类的损失乘上 $n/(2n_)$ 之类的权重等价于让模型更在意少数类。这个改动很便宜先试。第三调阈值而不是重采样。这是我更推荐的做法。重采样尤其是过采样会改变先验分布导致输出概率不再是真实概率需要额外校准而调阈值不改变模型只改变决策点副作用小得多。如果确实要用采样SMOTE 之类的插值方法要小心必须在交叉验证的每一折内部做不能在划分之前对全量数据做。这个泄漏极其隐蔽指标会虚高一大截。正确写法是把采样器放进imblearn的Pipeline里。7.2 标准化、缺失值、类别特征的实操顺序逻辑回归对特征尺度敏感尤其用梯度类求解器时标准化基本是必做项。但顺序有讲究原始数据 → 缺失值处理数值型中位数填充 缺失指示变量类别型单独一档 → 异常值处理缩尾 winsorize或对数变换 → 类别特征编码低基数 one-hot高基数用目标编码并做折内拟合 → 标准化StandardScaler 或 RobustScaler → 模型几个容易翻车的点缺失指示变量很重要。如果缺失本身有信息量比如收入未填写的人违约率显著更高只做填充会把这个信号抹掉。加一列 0/1 指示变量让模型自己学。类别特征用 one-hot 时注意维度爆炸。一个 500 个取值的类别变量 one-hot 之后 500 维样本才几万条必然过拟合。这种要么做频次编码要么把低频类别合并成 other。标准化必须只 fit 在训练集上。这个错误新手常犯——在全量数据上fit_transform然后划分测试集的统计量泄漏进了训练过程。放进Pipeline就自动避免了from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegressionCV pipe Pipeline([ (scaler, StandardScaler()), (clf, LogisticRegressionCV( Cs[0.001, 0.01, 0.1, 1, 10, 100], cv5, penaltyl2, solverlbfgs, scoringroc_auc, max_iter2000, class_weightbalanced, )), ]) pipe.fit(X_train, y_train)LogisticRegressionCV相比手写GridSearchCV有个好处它内部对每个 C 用 warm start 优化速度快不少。但要注意它的scoring参数必须显式指定默认用的是准确率不平衡数据下选出来的 C 会很离谱。7.3 特征筛选的实用策略逻辑回归的系数本身有解释性所以很多人想靠它筛特征。我的建议是不要只看系数绝对值排大小因为系数受特征尺度影响。要看标准化之后的系数或者直接看单变量检验的结果。一套我用得比较顺的流程先算每个特征和目标的相关性数值型用点二列相关类别型用卡方或 IV 值去掉几乎没有信号的检查特征间的 VIF方差膨胀因子超过 10 的考虑删掉或合并用 L1 逻辑回归做一遍粗筛保留非零系数最后用少量候选特征跑完整 CV看稳定性第 2 步的 VIF 检查经常被跳过但它能提前暴露共线性问题比模型上线后系数乱跳再回头查要省事得多。8. 多分类扩展与一个完整的落地示例8.1 一对多 vs 多项逻辑回归二分类扩展到 K 类有两条路。OvR一对多训练 K 个二分类器第 k 个把类 k当正类、其余全当负类预测时取打分最高的。简单K 个模型互相独立可并行。Softmax多项逻辑回归直接建模 $P(yk \mid x) e^{z_k} / \sum_j e^{z_j}$所有类别的参数联合优化输出的概率和为 1理论上更自洽。什么时候有差别类别互斥时 Softmax 更合适类别之间有重叠或层次结构时 OvR 更灵活。sklearn里multi_class参数控制新版本中部分已被弃用统一走 multinomial。类别数很多比如上百时 Softmax 的每轮计算开销明显更大OvR 反而更实用。8.2 一段可以直接抄的完整流程下面这段包括数据划分、Pipeline、交叉验证、阈值搜索和概率校准覆盖前面提到的关键点import numpy as np from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_predict 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 roc_auc_score, precision_recall_curve X_tr, X_te, y_tr, y_te train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) base Pipeline([ (scaler, StandardScaler()), (clf, LogisticRegression( C0.5, penaltyl2, solverlbfgs, class_weightbalanced, max_iter2000, )), ]) # 用 OOF 概率做阈值搜索避免在训练集上自欺欺人 cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) oof_prob cross_val_predict( base, X_tr, y_tr, cvcv, methodpredict_proba )[:, 1] prec, rec, thr precision_recall_curve(y_tr, oof_prob) f1 2 * prec * rec / np.clip(prec rec, 1e-12, None) best_thr thr[np.argmax(f1[:-1])] print(fOOF AUC: {roc_auc_score(y_tr, oof_prob):.4f}, 最佳阈值: {best_thr:.3f}) # 校准可选视可靠性曲线决定是否加 calibrated CalibratedClassifierCV(base, methodsigmoid, cv5) calibrated.fit(X_tr, y_tr) test_prob calibrated.predict_proba(X_te)[:, 1] print(f测试集 AUC: {roc_auc_score(y_te, test_prob):.4f})几个细节值得说。stratifyy保证划分后正负比例一致不平衡数据下不加这个某折可能几乎没有正例交叉验证结果完全不可信。cross_val_predict拿的是 OOF 概率用它搜阈值才可信。precision_recall_curve返回的thr长度比prec、rec少一个所以np.argmax之后要拿thr[mask[:-1]]的索引这个 off-by-one 我踩过报错信息不直观。8.3 上线之后要盯什么模型上线不是终点。逻辑回归虽然是白盒但在实时数据流里依然会漂。特征漂移是第一优先级。每个特征的分布均值、分位数、缺失率每周统计一次和训练时的基线对比超出阈值就告警。我用 PSI群体稳定性指数做这件事超过 0.1 就要关注超过 0.25 基本得重训。系数稳定性也要盯。如果某个特征的系数在两次重训之间变化超过 30%通常意味着共线性或者数据质量问题不是模型学到了新规律。输出分布是最灵敏的指标。预测概率的均值如果在一周内偏移超过 5 个百分点基本可以断定上游数据出问题了。这个信号比 AUC 下降出现得早得多——等 AUC 掉了再查往往已经影响业务很久了。最后分享一个我自己的习惯每次重训都存下完整的特征清单、标准化参数、系数、阈值和评估指标做成一个版本化的快照。逻辑回归的模型文件很小存起来几乎零成本。出问题时能快速对比上一版和这一版到底差在哪比事后凭记忆推测靠谱得多。我有过一次线上指标突降靠这个快照半小时就定位到是某个上游表的口径变了如果没有历史版本这个排查可能得耗上一整天。
阅读完成 · 觉得有帮助?
咨询建站