跳到主要内容

Logistic回归

本文档面向数学建模竞赛备赛学习者,系统介绍二分类利器 Logistic 回归:从算法原理、适用场景、评价指标、可视化图表,到一份可直接运行验证的完整 Python 代码(手写梯度下降 + sklearn 对照),以及竞赛论文的写作建议。全文示例数据均由代码内随机种子生成,无外部文件依赖,跑通第六节代码即可复现所有图表与指标。


一、算法含义

1.1 一句话理解

Logistic 回归 = "线性回归 + sigmoid 压缩"。模型先对特征做线性组合,得到一个实数"分数" zz,再用 sigmoid 函数把 zz 压进 (0,1)(0,1) 区间,把输出解释为"样本属于正类(y=1y=1)的概率"。因此它名字里带"回归",干的却是分类的活。

1.2 模型形式

线性部分(先算一个"分数"):

z=β0+βTx=β0+β1x1+β2x2++βdxdz=\beta_0+\boldsymbol{\beta}^T x=\beta_0+\beta_1x_1+\beta_2x_2+\cdots+\beta_d x_d

sigmoid 函数(把分数压缩成概率):

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

sigmoid 的三条重要性质:

  • 值域为 (0,1)(0,1),天然适合表示概率;
  • 单调递增,且 σ(0)=0.5\sigma(0)=0.5
  • 对称性 σ(z)=1σ(z)\sigma(-z)=1-\sigma(z),导数 σ(z)=σ(z)(1σ(z))\sigma'(z)=\sigma(z)\big(1-\sigma(z)\big)(推导梯度时要用)。

于是完整的模型写为:

P(y=1x)=σ(β0+βTx)=11+e(β0+βTx)P(y=1\mid x)=\sigma(\beta_0+\boldsymbol{\beta}^T x)=\frac{1}{1+e^{-(\beta_0+\boldsymbol{\beta}^T x)}}

决策规则:预测 y^=1    P(y=1x)0.5    β0+βTx0\hat{y}=1\iff P(y=1\mid x)\ge 0.5\iff \beta_0+\boldsymbol{\beta}^T x\ge 0。可见决策边界是方程 β0+βTx=0\beta_0+\boldsymbol{\beta}^T x=0 给出的一个超平面(二维时是一条直线),所以 Logistic 回归本质上是线性分类器

1.3 对数几率(logit)变换——"回归"二字的由来

定义几率(odds):odds=P1Podds=\dfrac{P}{1-P},取值范围 (0,+)(0,+\infty);再取自然对数得到对数几率(logit),值域变为 (,+)(-\infty,+\infty)

lnP1P=β0+βTx\ln\frac{P}{1-P}=\beta_0+\boldsymbol{\beta}^T x

等号左边是概率的对数几率变换,右边是特征的线性组合——模型正是对"对数几率"做了一次线性回归。这就是它叫"回归"的第一个原因。

1.4 为什么叫"回归"却是分类模型

  1. 历史渊源:Logistic 回归由线性回归发展而来,是广义线性模型(GLM)的一员——因变量服从伯努利分布,连接函数取 logit(线性回归是正态分布 + 恒等连接)。
  2. 回归的对象是连续量:模型直接回归的是对数几率(连续值),而不是 0/1 标签本身;对数几率经 sigmoid 反变换成概率,最后按阈值切成类别。
  3. 输出是概率:预测值 P(y=1x)P(y=1\mid x) 是连续的概率,可用于风险排序(例如"违约概率 0.32"比一句"会违约"信息量大得多),这也是它与 SVM 这类"硬分类"模型的本质区别。

一句话总结:用回归的思想估计参数、以概率的形式输出、靠阈值完成分类。

1.5 参数求解:极大似然估计

nn 个样本 (xi,yi)(x_i,y_i),似然函数(各样本概率的连乘)为:

L(β)=i=1npiyi(1pi)1yi,pi=σ(β0+βTxi)L(\boldsymbol{\beta})=\prod_{i=1}^{n}p_i^{\,y_i}(1-p_i)^{1-y_i},\qquad p_i=\sigma(\beta_0+\boldsymbol{\beta}^T x_i)

取负对数并除以 nn(不影响最优解),得到平均交叉熵(对数损失),也就是训练要最小化的目标函数:

J(β)=1ni=1n[yilnpi+(1yi)ln(1pi)]J(\boldsymbol{\beta})=-\frac{1}{n}\sum_{i=1}^{n}\Big[y_i\ln p_i+(1-y_i)\ln(1-p_i)\Big]

极大似然估计 = 最小化交叉熵损失J(β)J(\boldsymbol{\beta}) 是凸函数,存在唯一全局最优解,但没有闭式解,需要用数值迭代求解。

梯度下降法(一阶方法)。利用 σ=σ(1σ)\sigma'=\sigma(1-\sigma) 求导可得梯度(X~\tilde X 是加了一列全 1 的增广设计矩阵,p=(p1,,pn)Tp=(p_1,\dots,p_n)^T 是预测概率向量,yy 是标签向量):

J(β)=1nX~T(py)\nabla J(\boldsymbol{\beta})=\frac{1}{n}\tilde X^T(p-y)

迭代公式:

β(t+1)=β(t)αJ(β(t))\boldsymbol{\beta}^{(t+1)}=\boldsymbol{\beta}^{(t)}-\alpha\,\nabla J(\boldsymbol{\beta}^{(t)})

其中 α\alpha 是学习率。注意梯度形式与线性回归的 1nX~T(X~βy)\frac{1}{n}\tilde X^T(\tilde X\boldsymbol{\beta}-y) 惊人地相似——只是把"预测值"换成了"预测概率"。

牛顿法(二阶方法,收敛更快)。Hessian 矩阵为:

H=2J(β)=1nX~TWX~,W=diag(pi(1pi))H=\nabla^2 J(\boldsymbol{\beta})=\frac{1}{n}\tilde X^T W\tilde X,\qquad W=\mathrm{diag}\big(p_i(1-p_i)\big)

迭代公式:

β(t+1)=β(t)H1J(β(t))\boldsymbol{\beta}^{(t+1)}=\boldsymbol{\beta}^{(t)}-H^{-1}\nabla J(\boldsymbol{\beta}^{(t)})

牛顿法等价于"迭代加权最小二乘"(IRLS):每一步都在解一个加权线性回归问题。它比梯度下降收敛快得多(局部二次收敛),但每次迭代要求解一个 (d+1)×(d+1)(d+1)\times(d+1) 的线性方程组。scikit-learn 默认求解器 lbfgs 就是拟牛顿法(不用显式求逆 HH)。第六节代码会同时给出手写梯度下降sklearn 拟牛顿法的结果对照,两者系数几乎一致。

1.6 优势比(OR)——系数的正确解读方式

由 logit 变换反解出 odds=eβ0+βTxodds=e^{\beta_0+\boldsymbol{\beta}^T x}。当第 jj 个特征 xjx_j 增加 1 个单位、其余特征不变时:

ORj=odds(xj+1)odds(xj)=eβ0+βj(xj+1)+eβ0+βjxj+=eβjOR_j=\frac{odds(x_j+1)}{odds(x_j)}=\frac{e^{\beta_0+\beta_j(x_j+1)+\cdots}}{e^{\beta_0+\beta_j x_j+\cdots}}=e^{\beta_j}

  • ORj>1OR_j>1xjx_j 增加会提高正类几率,是"风险/促进因素";
  • ORj<1OR_j<1xjx_j 增加会降低正类几率,是"保护因素";
  • ORj=1OR_j=1xjx_j 与结果无关联。

解释系数一定要用 OR,不要直接解释 β\beta 本身。 β\beta 作用在对数几率(logit)尺度上,量纲不直观;而 OR 是倍数关系("几率变为原来的 2.7 倍"),人人能听懂,也是医学、流行病学论文的标准表达。

1.7 二分类 → 多分类:softmax 一句话

多分类时把 sigmoid 换成 softmax:

pk=ezkj=1Kezj,zk=β0k+βkTxp_k=\frac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}},\qquad z_k=\beta_{0k}+\boldsymbol{\beta}_k^T x

K=2K=2 时 softmax 与 sigmoid 完全等价(两类的概率之比取对数就是 logit)。scikit-learn 中 LogisticRegression 的默认多分类模式(multinomial)就是 softmax 回归。详见第八节。

1.8 优缺点

优点

  1. 直接输出校准较好的概率,可做风险排序、按需调整阈值;
  2. 系数可解释性强(OR 含义明确),竞赛论文好写"结论";
  3. 训练速度快、内存占用小,超参数少(主要只有学习率/正则强度);
  4. 损失函数是凸函数,梯度下降/牛顿法能收敛到全局最优,不存在局部极小值问题;
  5. 可加 L1/L2 正则化,防止过拟合、缓解共线性。

缺点

  1. 假设对数几率与特征线性相关,非线性模式需要人工构造多项式、交互项或分箱;
  2. 对特征共线性敏感,系数标准误膨胀、OR 估计不稳;
  3. 异常值较敏感(指数映射会放大极端样本的影响);
  4. 对样本量有要求(经验法则:每类样本数 ≥ 10×特征数),小样本下系数不可靠。

二、何时使用(适用场景与条件)

2.1 适合 Logistic 回归的三个核心条件

  1. 二分类问题,且需要概率输出:例如要"违约概率 0.32"而不只是"违约/不违约";概率可用于风险排序、按概率分层、自定义决策阈值。
  2. 需要可解释的系数:想回答"哪个因素影响最大、方向如何、每增加一个单位风险变为几倍"——OR 是最好的表达工具。
  3. 特征与对数几率近似线性、样本量充足:数据画出来大致能用一条直线分开,每类样本数够支撑系数估计。

2.2 竞赛典型场景

  • 金融风控:是否违约、是否欺诈、是否提前还款(0-1 判断);
  • 医疗健康:是否患病、是否复发、是否发生并发症;
  • 市场营销:是否购买、是否流失、是否响应营销活动;
  • 工业与安全:是否故障、是否发生事故;
  • 社会调查与评价:是否满意、是否支持某政策(二分类评价问题)。

竞赛中典型问法:"建立模型判断某用户是否会违约,并给出违约概率""分析影响患病的关键因素"——前者考分类,后者考系数解释,都是 Logistic 回归的题眼。

2.3 使用前提(经验法则)

  1. 样本量:每类样本数 ≥ 10 × 特征数(EPV 规则,events per variable;保守可取 20×)。例如 8 个特征,样本较少的一类至少要有 80~160 条,否则系数估计不稳、易过拟合。
  2. 特征无严重共线性:方差膨胀因子 VIF < 10。共线特征会让系数标准误膨胀,OR 置信区间宽得离谱。
  3. 线性可分假设:先用散点图/决策边界图粗略检查;若不满足,可先做特征工程(取对数、分箱、构造交互项 x1x2x_1x_2、多项式项)再回到线性模型。

2.4 不适用情形

  • 高维小样本(特征数接近甚至超过样本量):Logistic 回归会严重过拟合 → 改用 L1 正则逻辑回归、随机森林、SVM;
  • 非线性决策边界(环形、月牙形等分布):线性边界无解 → 改用 SVM(RBF 核)、决策树/随机森林/GBDT、神经网络;
  • 类别极不平衡且不需要概率:如欺诈样本仅占 0.1%,普通 Logistic 会倾向全部预测多数类 → 先做 SMOTE 过采样/欠采样,设置 class_weight="balanced",或调整决策阈值;
  • 多分类且类别有序(如"不满意/一般/满意")→ 用有序 Logistic(见第八节)。

2.5 与其他分类方法的对比选择

方法概率输出可解释性处理非线性高维小样本何时选它
Logistic 回归有(校准好)强(系数/OR)需特征工程一般需要概率 + 可解释系数
SVM无(需 Platt 缩放)强(RBF 核)较强非线性边界、中小样本
决策树/随机森林有(阶梯状)中(特征重要性)非线性、交互项多、高维
KNN可(投票比例)弱(维度灾难)极低维、无参数假设
朴素贝叶斯弱(独立假设)文本分类、样本很少

竞赛建议:默认先跑 Logistic 回归做基线(快、可解释、论文里必写),再用随机森林/SVM 做对比与提升;若基线 AUC 已经很高,说明数据近似线性可分,Logistic 本身就是最优解之一。


三、算法指标

以下指标的符号建立在混淆矩阵上。约定"正类"是竞赛中关心的那一类(如"违约""患病")。

3.1 混淆矩阵(基础)

预测为 0预测为 1
真实为 0TN(真负例)FP(假正例,误报)
真实为 1FN(假负例,漏报)TP(真正例)

由它可派生两个率:

  • 真正率(召回率):TPR=TPTP+FNTPR=\dfrac{TP}{TP+FN},正类中被找出的比例;
  • 假正率:FPR=FPFP+TNFPR=\dfrac{FP}{FP+TN},负类中被误报的比例。

3.2 准确率 Accuracy

公式:Acc=TP+TNTP+TN+FP+FN\mathrm{Acc}=\dfrac{TP+TN}{TP+TN+FP+FN};取值范围 [0,1][0,1];越大越好。

解读:全部样本中预测正确的比例,只在两类平衡时才有意义

类别不平衡时的误导性:若负类占 95%,一个"全预测为负"的傻瓜模型准确率就有 0.95,但正类全部漏报(TP=0),完全无用。因此不平衡数据要看 F1、AUC、PR 曲线,而不是准确率。

3.3 精确率 Precision

公式:Precision=TPTP+FP\mathrm{Precision}=\dfrac{TP}{TP+FP};取值范围 [0,1][0,1];越大越好。

解读:预测为正的样本中真正为正的比例,衡量"宁缺毋滥"——误报代价高的场景(如抓骗子、发律师函)要优先看它。分母过小时该值会抖动。

3.4 召回率 Recall

公式:Recall=TPTP+FN\mathrm{Recall}=\dfrac{TP}{TP+FN};取值范围 [0,1][0,1];越大越好。

解读:真实正类中被找出的比例,衡量"宁错杀不放过"——漏报代价高的场景(如癌症筛查、故障预警)要优先看它。精确率与召回率此消彼长,需要结合业务定夺。

3.5 F1 分数

公式:F1=2PRP+R=2TP2TP+FP+FNF_1=\dfrac{2PR}{P+R}=\dfrac{2TP}{2TP+FP+FN};取值范围 [0,1][0,1];越大越好。

解读:精确率与召回率的调和平均,一个变低 F1 就低,能惩罚"偏科"的模型。竞赛中类别不平衡时优先报 F1。

3.6 ROC-AUC

ROC 曲线:把决策阈值从 1 扫到 0,每取一个阈值算一对 (FPR,TPR)(FPR, TPR) 描点连成的曲线,横轴 FPR、纵轴 TPR。AUC 即曲线下面积。

  • 取值范围 [0,1][0,1];AUC = 0.5 等价于随机猜测(对角线),越大越好。
  • 统计含义:随机取一对正负样本,正样本预测概率高于负样本的概率
  • 与阈值无关,是模型"排序能力"的总评价:0.50.7 较差,0.70.8 可接受,0.8~0.9 较好,0.9 以上优秀。
  • 类别极不平衡时 ROC 会偏乐观,可补充 PR 曲线。

3.7 对数损失(交叉熵)LogLoss

公式:LogLoss=1ni=1n[yilnpi+(1yi)ln(1pi)]\mathrm{LogLoss}=-\dfrac{1}{n}\sum_{i=1}^{n}\Big[y_i\ln p_i+(1-y_i)\ln(1-p_i)\Big];取值范围 [0,+)[0,+\infty);越小越好。

解读:衡量概率本身的质量而非只看类别对错——预测错了还非常自信(如真实为 0 却输出 p=0.99)会遭到指数级惩罚。它正是模型训练时最小化的目标,训练损失与测试损失都报它最能说明拟合情况。

3.8 McFadden 伪 R²

公式:RMcF2=1lnLMlnL0R^2_{McF}=1-\dfrac{\ln L_M}{\ln L_0},其中 lnLM\ln L_M 为模型的(最大)对数似然,lnL0\ln L_0 为只含截距的零模型对数似然;取值范围 (,1](-\infty,1];越大越好。

解读:类比线性回归的 R2R^2 给出"相对零模型提升多少",但不是方差解释比例,数值普遍偏小:0.2~0.4 已算拟合良好,不能拿线性回归的"0.8 才算好"标准来要求它。实践中可用 lnL=n×LogLoss\ln L=-n\times\mathrm{LogLoss} 反推(见第六节代码)。

3.9 系数 Wald 检验 p 值

公式:zj=β^jSE(β^j)N(0,1)z_j=\dfrac{\hat\beta_j}{SE(\hat\beta_j)}\sim N(0,1)(在原假设 H0:βj=0H_0:\beta_j=0 下),双侧 p=2(1Φ(zj))p=2\big(1-\Phi(|z_j|)\big);取值范围 [0,1][0,1]

解读:p<0.05p<0.05 说明该系数与 0 有显著差异(变量与结果相关)。但注意两点:显著 ≠ 重要(样本大时芝麻大的效应也显著);不显著 ≠ 无用(可能是共线性把效应分摊给了别人)。标准误 SE(β^j)SE(\hat\beta_j) 由 Fisher 信息矩阵的逆给出:Var(β^)(X~TWX~)1\mathrm{Var}(\hat{\boldsymbol{\beta}})\approx\big(\tilde X^T W\tilde X\big)^{-1}W=diag(pi(1pi))W=\mathrm{diag}(p_i(1-p_i))

3.10 优势比 OR 及 95% 置信区间

公式:ORj=eβ^jOR_j=e^{\hat\beta_j};95% 置信区间 [eβ^j1.96SE, eβ^j+1.96SE]\Big[e^{\hat\beta_j-1.96\,SE},\ e^{\hat\beta_j+1.96\,SE}\Big];取值范围 (0,+)(0,+\infty)

解读:xjx_j 每增加 1 个单位,正类几率变为原来的 ORjOR_j 倍。置信区间是否包含 1 等价于 Wald 检验是否显著:含 1 → 不显著,不含 1 → 显著。区间越窄,估计越稳。

3.11 指标汇总表

指标公式取值范围解读要点竞赛参考
准确率 AccuracyTP+TNTP+TN+FP+FN\frac{TP+TN}{TP+TN+FP+FN}[0,1][0,1]分对的比例;不平衡时误导两类平衡时必报
精确率 PrecisionTPTP+FP\frac{TP}{TP+FP}[0,1][0,1]预测为正里真正为正;怕误报看它误报代价高场景
召回率 RecallTPTP+FN\frac{TP}{TP+FN}[0,1][0,1]真正类被找出;怕漏报看它漏报代价高场景
F1 分数2PRP+R\frac{2PR}{P+R}[0,1][0,1]P、R 的调和平均不平衡时主报指标
ROC-AUC曲线下面积[0,1][0,1]排序能力,阈值无关;0.5 为随机主报指标,0.8 以上较好
对数损失 LogLoss1n[yilnpi+(1yi)ln(1pi)]-\frac{1}{n}\sum[y_i\ln p_i+(1-y_i)\ln(1-p_i)][0,+)[0,+\infty)概率质量;越小越好报训练/测试两组对比过拟合
McFadden 伪 R²1lnLMlnL01-\frac{\ln L_M}{\ln L_0}(,1](-\infty,1]相对零模型提升;0.2~0.4 已不错模型整体拟合度
Wald p 值p=2(1Φ(z))p=2(1-\Phi(\mid z\mid))z=β^/SEz=\hat\beta/SE[0,1][0,1]系数显著性;p<0.05p<0.05 显著变量筛选与解释
OR 及 95%CIeβ^e^{\hat\beta}eβ^±1.96SEe^{\hat\beta\pm1.96SE}(0,+)(0,+\infty)几率倍数;CI 含 1 则不显著影响分析必报

四、可视化图表

下表汇总本节要求的 6 张图(图名与第六节代码保存的文件名一一对应,均存于 figures/ 目录,前缀 logit_)。

图名(文件)用途关键解读点
① sigmoid 曲线图(logit_sigmoid.png展示 sigmoid 形状及系数 β\beta 的作用,讲清"概率压缩"机制曲线恒在 (0,1);β1\beta_1 越大曲线越陡(决策越"果断");β0\beta_0 改变曲线左右平移(等价于移动阈值);σ(0)=0.5\sigma(0)=0.5
② ROC 曲线(logit_roc.png阈值无关的判别能力总览,竞赛最常用图曲线越贴近左上角越好;标注的 AUC 值越大越好(>0.8 较好);与对角线(随机猜测)的距离代表模型增益;曲线上每个点对应一个阈值,可用来挑工作点
③ 混淆矩阵热力图(logit_confusion.png直观看出四类样本的数量分布对角线颜色深、数字大 = 分得对;右上 FP、左下 FN 分别对应误报与漏报;结合场景判断哪类错误代价更高
④ 二维特征空间决策边界图(logit_boundary.png验证"线性可分"假设,展示学习到的边界P=0.5 的实线即分类边界;P=0.25/0.75 虚线围成"犹豫带",带越窄判别越自信;边界两侧若两色混叠严重说明可分度差,可考虑非线性模型
⑤ 优势比 OR 森林图(logit_forest.png展示各特征效应大小与不确定性,论文标配每条横线是 OR±95%CI;横线完全落在 OR=1 参考线右侧 → 显著促进因素;落左侧 → 保护因素;横跨 1 → 不显著;区间越短估计越稳;对数刻度下 1 两侧对称(2 与 0.5 等距)
⑥ 精确率-召回率曲线(logit_pr.png类别不平衡时比 ROC 更敏感的补充图曲线越高越靠右上越好;报告 AP(曲线下面积)值;随机基准线高度 = 正类比例;不平衡数据必画

好图的特征:ROC 明显凸向左上角且 AUC 高;混淆矩阵对角占主导且非对角分布符合业务预期;OR 区间窄且不跨 1;决策边界两侧颜色分明、犹豫带窄;PR 曲线远离随机基准线。

异常图的特征(出现即警惕):ROC 贴着对角线(AUC≈0.5,模型没有学到东西,检查特征或标签);混淆矩阵某一行全为 0(模型偷懒全预测一类,检查类别不平衡或阈值);OR 区间宽到跨 1(共线性或样本不足);决策边界把整个平面都判为一类(特征无效或有代码 bug);PR 曲线低于随机基准(模型还不如猜)。


五、符号说明

符号含义示例/单位
xx特征向量(样本)x=(x1,x2)x=(x_1,x_2),如(年龄,收入)
yy二分类标签y=1y=1 表示违约,y=0y=0 表示未违约
n, dn,\ d样本量、特征个数n=400, d=2n=400,\ d=2
β0\beta_0截距项(偏置)无量纲(logit 尺度)
βj\beta_jjj 个特征的系数随特征量纲变化
β\boldsymbol{\beta}系数向量(β1,,βd)(\beta_1,\dots,\beta_d)
zz线性组合"分数" z=β0+βTxz=\beta_0+\boldsymbol{\beta}^T x任意实数
σ(z)\sigma(z)sigmoid 函数σ(0)=0.5\sigma(0)=0.5,值域 (0,1)(0,1)
PPpip_i正类预测概率 P(y=1x)P(y=1\mid x)0.32(违约概率)
oddsodds几率 P/(1P)P/(1-P)0.32/0.68 ≈ 0.47,无量纲
OROR优势比 eβje^{\beta_j}2.7(倍)
α\alpha梯度下降学习率0.3
J(β)J(\boldsymbol{\beta})交叉熵(对数损失)目标函数0.45,无量纲
J\nabla J损失函数的梯度向量
HHHessian 矩阵(d+1)×(d+1)(d+1)\times(d+1) 矩阵
WW权重对角阵 diag(pi(1pi))\mathrm{diag}(p_i(1-p_i))n×nn\times n 矩阵
X~\tilde X增广设计矩阵(加一列全 1)n×(d+1)n\times(d+1)
TP/FP/FN/TNTP/FP/FN/TN混淆矩阵四个元素个数
TPR/FPRTPR/FPR真正率/假正率[0,1][0,1]
SESE系数标准误0.11
zjz_jWald 统计量 β^j/SE\hat\beta_j/SE9.2
pp显著性 p 值0.001
CICI置信区间(1.9, 3.4)
AUCAUCROC 曲线下面积0.88
LogLoss\mathrm{LogLoss}对数损失0.45,无量纲
RMcF2R^2_{McF}McFadden 伪 R²0.35

六、可运行程序(完整代码)

说明:以下代码块按顺序拼接保存为一个 .py 文件即可运行(如 logistic_demo.py)。环境要求 Python 3.12,仅依赖 numpy、scipy、scikit-learn、matplotlib、pandas 五个库。程序用 np.random.seed(42) 生成合成二分类数据(2 个特征、n=400、两类约 1:1、可分度适中),无任何外部文件依赖。程序依次完成:手写梯度下降训练逻辑回归(sigmoid、交叉熵损失、梯度公式,并打印损失下降曲线)→ sklearn LogisticRegression 对照 → 计算第三节全部指标 → 手算标准误/Wald 检验/OR 及 95%CI/McFadden 伪 R² → 绘制第四节全部 6 张图(保存到 figures/ 并以 logit_ 为前缀)并 plt.show()。运行输出的典型数值解读见第七节。

# ========== 0. 导入库与全局设置 ==========
# 运行环境: Python 3.12; 依赖库: numpy / scipy / scikit-learn / matplotlib / pandas
import os
import warnings
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use("Agg") # 后台渲染: 无图形界面环境也能运行, 图片统一存到 figures/
import matplotlib.pyplot as plt
from scipy import stats

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn import metrics

# ---- matplotlib 中文显示设置 (必须放在所有绘图代码之前) ----
plt.rcParams["font.sans-serif"] = ["PingFang SC", "Arial Unicode MS", "SimHei"]
plt.rcParams["axes.unicode_minus"] = False

# ---- 无图形界面环境下 plt.show() 会提示 "non-interactive", 过滤掉以保持输出干净 ----
warnings.filterwarnings("ignore", message=".*non-interactive.*")

# ---- 创建图片输出目录 ----
os.makedirs("figures", exist_ok=True)

# ========== 1. 生成合成二分类数据 (n=400, 2 个特征, 两类约 1:1) ==========
np.random.seed(42) # 固定随机种子, 保证结果完全可复现
n = 400
X = 1.2 * np.random.randn(n, 2) # 两个特征, 均值为 0、标准差为 1.2
z_true = 1.0 * X[:, 0] + 1.0 * X[:, 1] # 真实的"潜在分数": 两特征等权线性组合
p_true = 1.0 / (1.0 + np.exp(-z_true)) # 真实概率 = sigmoid(z_true)
y = (np.random.rand(n) < p_true).astype(int) # 按概率随机抽样得到 0/1 标签

print("样本量 n = %d, 正类比例 = %.3f (两类接近 1:1)" % (n, y.mean()))
print("两特征相关系数 = %.3f (接近 0, 说明基本无共线性)" % np.corrcoef(X.T)[0, 1])

# 按 7:3 分层划分训练集与测试集 (分层抽样保证两类比例一致)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y)
print("训练集 %d 条, 测试集 %d 条" % (len(X_train), len(X_test)))

以上代码生成数据并划分数据集。下面手写实现逻辑回归的核心:sigmoid 函数、交叉熵损失与梯度下降训练。

# ========== 2. 手写实现: sigmoid / 交叉熵损失 / 梯度下降 ==========
def sigmoid(z):
"""sigmoid 函数: σ(z) = 1 / (1 + e^{-z}), 把任意实数压缩到 (0,1)"""
return 1.0 / (1.0 + np.exp(-np.clip(z, -500.0, 500.0))) # clip 防止数值溢出

def cross_entropy_loss(y, p, eps=1e-12):
"""平均交叉熵(对数损失): J(β) = -(1/n) Σ [y ln p + (1-y) ln(1-p)]"""
p = np.clip(p, eps, 1.0 - eps) # 防止 log(0)
return -np.mean(y * np.log(p) + (1.0 - y) * np.log(1.0 - p))

class LogisticRegressionGD:
"""手写逻辑回归: 批量梯度下降 + 早停"""
def __init__(self, lr=0.3, n_iter=5000, tol=1e-7):
self.lr = lr # 学习率 α
self.n_iter = n_iter # 最大迭代次数
self.tol = tol # 早停阈值: 损失下降幅度小于 tol 时停止
self.losses = [] # 记录每次迭代的损失, 用于观察下降过程

def fit(self, X, y):
n, d = X.shape
Xe = np.hstack([np.ones((n, 1)), X]) # 增广矩阵 X̃ = [1, x1, x2], 对应截距 β0
self.beta = np.zeros(d + 1) # 初始化系数 β = 0
for it in range(self.n_iter):
p = sigmoid(Xe @ self.beta) # 当前预测概率 p_i = σ(x̃_i^T β)
loss = cross_entropy_loss(y, p) # 当前交叉熵损失
grad = Xe.T @ (p - y) / n # 梯度: ∇J(β) = (1/n) X̃^T (p - y)
self.beta -= self.lr * grad # 更新: β ← β - α ∇J
self.losses.append(loss)
if it > 0 and abs(self.losses[-1] - self.losses[-2]) < self.tol:
break # 收敛, 提前结束
return self

def predict_proba(self, X):
"""输出正类概率 P(y=1|x)"""
Xe = np.hstack([np.ones((X.shape[0], 1)), X])
return sigmoid(Xe @ self.beta)

def predict(self, X, threshold=0.5):
"""按阈值 0.5 输出 0/1 标签"""
return (self.predict_proba(X) >= threshold).astype(int)

# 训练手写模型, 并打印损失下降过程
model_gd = LogisticRegressionGD(lr=0.3).fit(X_train, y_train)
print("\n[手写梯度下降] 迭代次数 = %d" % len(model_gd.losses))
print("[手写梯度下降] 交叉熵损失: %.6f -> %.6f (共下降 %.4f)"
% (model_gd.losses[0], model_gd.losses[-1],
model_gd.losses[0] - model_gd.losses[-1]))
step = max(1, len(model_gd.losses) // 6)
print("[手写梯度下降] 损失下降曲线采样 (每 %d 次迭代):" % step,
[round(float(v), 4) for v in model_gd.losses[::step]][:7])
print("[手写梯度下降] 系数 β = [β0, β1, β2] =", np.round(model_gd.beta, 4))

接下来用 sklearn 的 LogisticRegression 训练同一份数据,与手写模型对照系数与预测一致性。

# ========== 3. sklearn LogisticRegression 对照 ==========
# C=1e6 相当于几乎不加正则化, 与手写梯度下降公平对比; lbfgs 为拟牛顿法求解器
clf = LogisticRegression(C=1e6, fit_intercept=True, solver="lbfgs", max_iter=10000)
clf.fit(X_train, y_train)
beta_sk = np.r_[clf.intercept_[0], clf.coef_[0]] # 整理成 [β0, β1, β2]
print("\n[sklearn] 系数 β = [β0, β1, β2] =", np.round(beta_sk, 4))
print("[对照] 手写模型与 sklearn 系数最大绝对差 = %.6f"
% np.max(np.abs(model_gd.beta - beta_sk)))

# 预测一致性: 两种实现是否给出几乎相同的预测
y_pred_gd = model_gd.predict(X_test)
y_pred_sk = clf.predict(X_test)
print("[对照] 测试集上两种实现预测一致率 = %.3f"
% np.mean(y_pred_gd == y_pred_sk))

下面用 sklearn 的 metrics 计算第三节要求的全部指标(混淆矩阵、准确率、精确率、召回率、F1、ROC-AUC、对数损失)。

# ========== 4. 计算第三节的全部评价指标 (sklearn.metrics) ==========
# 后续统一使用手写模型的预测 (与 sklearn 模型几乎一致)
y_prob = model_gd.predict_proba(X_test) # 正类概率
y_pred = model_gd.predict(X_test) # 0/1 预测

cm = metrics.confusion_matrix(y_test, y_pred) # 行为真实标签, 列为预测标签
tn, fp, fn, tp = cm.ravel()
print("\n混淆矩阵 (行为真实, 列为预测):")
print(pd.DataFrame(cm, index=["真实 0", "真实 1"],
columns=["预测 0", "预测 1"]).to_string())

acc = metrics.accuracy_score(y_test, y_pred) # 准确率
prec = metrics.precision_score(y_test, y_pred) # 精确率
rec = metrics.recall_score(y_test, y_pred) # 召回率
f1 = metrics.f1_score(y_test, y_pred) # F1 分数
auc = metrics.roc_auc_score(y_test, y_prob) # ROC-AUC
ll = metrics.log_loss(y_test, y_prob) # 对数损失(交叉熵)

df_metrics = pd.DataFrame({
"指标": ["准确率 Accuracy", "精确率 Precision", "召回率 Recall",
"F1 分数", "ROC-AUC", "对数损失 LogLoss"],
"取值": [round(acc, 4), round(prec, 4), round(rec, 4),
round(f1, 4), round(auc, 4), round(ll, 4)],
})
print("\n评价指标汇总:")
print(df_metrics.to_string(index=False))

接着手算系数标准误(Fisher 信息矩阵)、Wald 检验 p 值、优势比 OR 及 95% 置信区间,并用对数损失反推对数似然计算 McFadden 伪 R²。

# ========== 5. 手算: 系数标准误 / Wald 检验 p 值 / 优势比 OR 及 95% CI ==========
def wald_summary(beta, X):
"""
用 Fisher 信息矩阵的逆估计系数的协方差矩阵:
I(β̂) = X̃^T W X̃, W = diag(p_i (1 - p_i))
Var(β̂) ≈ I(β̂)^{-1}
进而得到: 标准误 SE = sqrt(diag(Var)),
Wald 统计量 z = β̂ / SE,
双侧 p 值 = 2 (1 - Φ(|z|)),
优势比 OR = e^{β̂}, 95% CI = e^{β̂ ± 1.96 SE}
"""
n, d = X.shape
Xe = np.hstack([np.ones((n, 1)), X])
p = sigmoid(Xe @ beta)
W = np.diag(p * (1.0 - p)) # 权重矩阵 W
I_mat = Xe.T @ W @ Xe # Fisher 信息矩阵
cov = np.linalg.inv(I_mat) # 协方差矩阵 Var(β̂) ≈ I(β̂)^{-1}
se = np.sqrt(np.diag(cov)) # 标准误
z = beta / se # Wald z 统计量
pval = 2.0 * stats.norm.sf(np.abs(z)) # 双侧 p 值 (标准正态生存函数)
or_ = np.exp(beta) # 优势比
lo = np.exp(beta - 1.96 * se) # 95% CI 下限
hi = np.exp(beta + 1.96 * se) # 95% CI 上限
return se, z, pval, or_, lo, hi

se, z, pval, or_, lo, hi = wald_summary(model_gd.beta, X_train)
df_coef = pd.DataFrame({
"系数 β̂": model_gd.beta.round(4),
"标准误 SE": se.round(4),
"Wald z": z.round(3),
"p 值": pval.round(4),
"OR = e^β̂": or_.round(3),
"95%CI 下限": lo.round(3),
"95%CI 上限": hi.round(3),
}, index=["截距 β0", "特征 x1", "特征 x2"])
print("\n系数检验与优势比 (Wald 检验):")
print(df_coef.to_string())

# ========== 6. McFadden 伪 R² (由对数损失反推对数似然) ==========
# 对数似然 lnL = -n * LogLoss; 零模型取"只预测类别先验概率"的常数模型
p_null = np.full(len(y_test), y_test.mean()) # 零模型: 全部预测均值概率
# 注意: 不能用 np.full_like(y_test, ...), 否则会保留整型 dtype 把概率截断成 0
ll_model = -len(y_test) * metrics.log_loss(y_test, y_prob)
ll_null = -len(y_test) * metrics.log_loss(y_test, p_null)
mcfadden = 1.0 - ll_model / ll_null
print("\nMcFadden 伪 R² = %.4f (模型 lnL = %.2f, 零模型 lnL = %.2f)"
% (mcfadden, ll_model, ll_null))

最后绘制第四节要求的全部 6 张图。先画前 3 张:sigmoid 曲线、ROC 曲线、混淆矩阵热力图。

# ========== 7. 绘制第四节要求的全部 6 张图 ==========
# ---- 图 1: sigmoid 曲线图 (不同 β 系数的形状对比) ----
z_grid = np.linspace(-8, 8, 400)
plt.figure(figsize=(7, 5))
for b1 in [0.5, 1.0, 2.0, 4.0]: # β1 越大曲线越陡: 决策越"果断"
plt.plot(z_grid, sigmoid(b1 * z_grid), lw=2, label="β1=%s, β0=0" % b1)
plt.plot(z_grid, sigmoid(2.0 * z_grid + 2.0), "--", lw=1.6,
label="β1=2, β0=2 (整体右移)")
plt.axhline(0.5, color="gray", lw=0.8, ls=":") # σ=0.5 的参考线
plt.axvline(0.0, color="gray", lw=0.8, ls=":")
plt.xlabel(r"$z = \beta_0 + \beta^T x$") # 用 mathtext: 上标 T 中文字体中没有
plt.ylabel(r"$\sigma(z) = P(y=1\mid x)$")
plt.title("sigmoid 曲线: 系数 β 对形状的影响")
plt.legend()
plt.tight_layout()
plt.savefig("figures/logit_sigmoid.png", dpi=150)
plt.show()
plt.close()

# ---- 图 2: ROC 曲线 (标注 AUC 值) ----
fpr, tpr, _ = metrics.roc_curve(y_test, y_prob)
plt.figure(figsize=(6.5, 6))
plt.plot(fpr, tpr, "b-", lw=2, label="Logistic 回归 (AUC = %.3f)" % auc)
plt.plot([0, 1], [0, 1], "k--", lw=1, label="随机猜测 (AUC = 0.5)")
plt.fill_between(fpr, tpr, alpha=0.15)
plt.xlabel("假正率 FPR = FP / (FP + TN)")
plt.ylabel("真正率 TPR = TP / (TP + FN) (即召回率)")
plt.title("ROC 曲线")
plt.legend(loc="lower right")
plt.tight_layout()
plt.savefig("figures/logit_roc.png", dpi=150)
plt.show()
plt.close()

# ---- 图 3: 混淆矩阵热力图 ----
plt.figure(figsize=(6, 5))
plt.imshow(cm, cmap="Blues", interpolation="nearest")
for i in range(2):
for j in range(2):
plt.text(j, i, str(cm[i, j]), ha="center", va="center", fontsize=20,
color="white" if cm[i, j] > cm.max() / 2 else "black")
plt.colorbar()
plt.xticks([0, 1], ["预测 0", "预测 1"])
plt.yticks([0, 1], ["真实 0", "真实 1"])
plt.xlabel("预测标签")
plt.ylabel("真实标签")
plt.title("混淆矩阵热力图")
plt.tight_layout()
plt.savefig("figures/logit_confusion.png", dpi=150)
plt.show()
plt.close()

再画后 3 张:决策边界图、OR 森林图、精确率-召回率曲线。

# ---- 图 4: 二维特征空间决策边界图 (数据散点 + 边界线) ----
x1_min, x1_max = X_test[:, 0].min() - 0.5, X_test[:, 0].max() + 0.5
x2_min, x2_max = X_test[:, 1].min() - 0.5, X_test[:, 1].max() + 0.5
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 200),
np.linspace(x2_min, x2_max, 200))
Z = model_gd.predict_proba(np.c_[xx1.ravel(), xx2.ravel()]).reshape(xx1.shape)

plt.figure(figsize=(7, 6))
plt.contourf(xx1, xx2, Z, levels=20, cmap="RdBu", alpha=0.45, vmin=0.0, vmax=1.0)
cs = plt.contour(xx1, xx2, Z, levels=[0.25, 0.5, 0.75],
colors=["red", "black", "red"],
linewidths=[1.2, 2.2, 1.2], linestyles=["--", "-", "--"])
plt.clabel(cs, fmt="%.2f", fontsize=10)
plt.scatter(X_test[y_test == 0, 0], X_test[y_test == 0, 1],
c="#1f77b4", s=25, label="真实 0 类", edgecolors="k", linewidths=0.4)
plt.scatter(X_test[y_test == 1, 0], X_test[y_test == 1, 1],
c="#d62728", s=25, label="真实 1 类", edgecolors="k", linewidths=0.4)
plt.xlabel("特征 x1")
plt.ylabel("特征 x2")
plt.title("二维特征空间决策边界 (P=0.5 实线, P=0.25/0.75 虚线)")
plt.legend()
plt.tight_layout()
plt.savefig("figures/logit_boundary.png", dpi=150)
plt.show()
plt.close()

# ---- 图 5: 系数优势比 OR 森林图 (OR ± 95%CI, 标注 OR=1 的参考线) ----
names = ["特征 x2", "特征 x1", "截距 β0"] # 从上到下排列
idx = [2, 1, 0]
plt.figure(figsize=(7, 4.5))
plt.errorbar(or_[idx], np.arange(3), # matplotlib 3.10+ 已移除 errorbarh, 用 errorbar 交换坐标
xerr=[or_[idx] - lo[idx], hi[idx] - or_[idx]],
fmt="o", capsize=5, color="#1f77b4", ecolor="gray", ms=7)
plt.axvline(1.0, color="red", ls="--", lw=1.5, label="OR = 1 (无关联)")
plt.yticks(np.arange(3), names)
plt.xscale("log") # 对数刻度: OR=1 两侧对称, 2 与 0.5 距离相等
plt.xlabel("优势比 OR (对数刻度) 及 95% 置信区间")
plt.title("优势比 OR 森林图")
plt.legend()
plt.tight_layout()
plt.savefig("figures/logit_forest.png", dpi=150)
plt.show()
plt.close()

# ---- 图 6: 精确率-召回率 (PR) 曲线 ----
prec_curve, rec_curve, _ = metrics.precision_recall_curve(y_test, y_prob)
ap = metrics.average_precision_score(y_test, y_prob)
plt.figure(figsize=(6.5, 6))
plt.plot(rec_curve, prec_curve, "b-", lw=2,
label="Logistic 回归 (AP = %.3f)" % ap)
plt.axhline(y_test.mean(), color="k", ls="--", lw=1,
label="随机基准 (正类比例 = %.3f)" % y_test.mean())
plt.xlabel("召回率 Recall")
plt.ylabel("精确率 Precision")
plt.title("精确率-召回率 (PR) 曲线")
plt.legend(loc="lower left")
plt.tight_layout()
plt.savefig("figures/logit_pr.png", dpi=150)
plt.show()
plt.close()

print("\n全部 6 张图已保存到 figures/ 目录:")
for f in sorted(os.listdir("figures")):
if f.startswith("logit_"):
print(" - figures/%s" % f)

七、结果解读与注意事项

7.1 本次示例的运行结果与解读

上述程序(随机种子固定为 42)的输出完全可复现,任何机器上运行结果相同。关键输出如下。

系数与显著性(标准误由 Fisher 信息矩阵手算,等价于 statsmodels 的输出):

变量系数 β̂标准误 SEWald zp 值OR = e^β̂95%CI
截距 β0-0.0060.148-0.040.970.994(0.744, 1.329)
特征 x11.1670.1676.98< 0.0013.212(2.315, 4.457)
特征 x20.9580.1516.35< 0.0012.606(1.939, 3.503)

解读:

  • 截距不显著(OR≈1,95%CI 含 1,p=0.97):合理——生成数据时两类各占约 50%,正负例的"基准几率"接近 1:1,与数据生成机制吻合。
  • x1 的 OR≈3.21:其他特征不变时,x1 每增加 1 个单位,正类几率变为原来的约 3.2 倍(增加约 220%)。95%CI (2.32, 4.46) 完全落在 1 的右侧,且 Wald p小于0.001 → 显著的风险因素。x2 同理(OR≈2.61)。这正对应生成数据时"两特征等权(真实系数均为 1.0)促进正类"的设定。
  • 两种算法殊途同归:手写梯度下降(163 次迭代)与 sklearn 拟牛顿法(lbfgs)的系数最大绝对差仅 0.0042,测试集预测一致率 100%;训练损失从 0.693 单调下降到 0.491(采样序列先快后慢),说明梯度下降正常收敛。

评价指标(测试集,n=120,混淆矩阵 TN=50、FP=10、FN=20、TP=40):

指标取值解读
准确率 Accuracy0.7503/4 的样本分对;本例两类接近 1:1,该指标可信
精确率 Precision0.800预测为正的样本中 80% 真实为正(误报较少)
召回率 Recall0.667真实正类中约 2/3 被找出(漏报约 1/3)
F1 分数0.727精确率与召回率较均衡,无明显偏科
ROC-AUC0.804随机抽一对正负样本,正样本概率更高的概率约 80%,明显优于随机猜测(0.5)
对数损失0.519明显低于零模型的对数损失 0.693,概率预测质量良好
McFadden 伪 R²0.252相对零模型对数似然提升约 25%,对 Logistic 回归已属"拟合良好"(其尺度远小于线性回归 R²,勿混淆)

7.2 六张图如何解读

  1. sigmoid 曲线:β1 从 0.5 增到 4,曲线越来越陡——斜率(即 β1·p(1-p))在 z=0 处达到最大 β1/4,说明系数越大,0/1 转换带越窄,模型决策越"果断";β0=2 的虚线整体右移,等价于把决策阈值位置挪动。
  2. ROC 曲线:曲线明显凸向左上角、远离对角线,AUC≈0.80(达到"较好"档),说明排序能力强;若想降低 FPR(减少误报),沿曲线向左上取点(提高阈值)。
  3. 混淆矩阵热力图:对角块(TN、TP)颜色深,说明大部分样本分对;比较右上(FP)与左下(FN)的个数,结合业务判断误报与漏报哪个代价更大。
  4. 决策边界图:黑色实线(P=0.5)将平面分成两半,红色虚线是 P=0.25/0.75 的"犹豫带";两类散点在边界两侧基本分离,说明线性可分假设成立;边界附近蓝红混叠的点正是被分错的样本。
  5. OR 森林图:x1(OR≈3.21)、x2(OR≈2.61)的横线及其 95%CI 完全在红色参考线(OR=1)右侧,且区间短 → 显著且估计稳;截距(OR≈0.99)的横线横跨 1 → 不显著。
  6. PR 曲线:曲线远高于随机基准线(高度=正类比例 0.5),AP 高,说明概率排序有效;本例两类平衡,PR 曲线与 ROC 结论一致;若类别不平衡,PR 曲线会暴露 ROC 掩盖的问题。

7.3 常见坑

  1. 类别不平衡时只看准确率:95% 负类时"全预测负"就有 0.95 的准确率。不平衡数据请报告 F1、AUC、PR 曲线(AP),并使用 class_weight="balanced"、SMOTE 重采样或调阈值。
  2. 用系数本身而不是 OR 解释:说"x1 的系数是 1.05"没人听得懂,说"x1 每增加 1 单位,几率变为原来的 2.85 倍"才是标准表达。方向看符号、大小看 OR。
  3. 忽视共线性:共线特征会让标准误膨胀、OR 置信区间宽到跨 1(系数甚至变号)。训练前看相关系数矩阵/VIF,必要时删除或合并共线特征。
  4. 死守 0.5 阈值:0.5 不是永远最优。当误报代价高时提高阈值,漏报代价高时降低阈值;可在 ROC 曲线上用 Youden 指数 J=TPRFPRJ=TPR-FPR 最大处选阈值。
  5. 过拟合与样本不足:特征越多需要的样本越多(EPV≥10);特征多于样本时改用 L1 正则(penalty="l1")或换模型。用训练/测试两组的 LogLoss、AUC 对比判断过拟合。
  6. 量纲影响 OR 的解读:OR 是"每增加 1 个单位"的倍数,对年龄(1 岁)和对收入(1 元)含义完全不同。可先标准化特征,再解释为"每增加 1 个标准差";连续特征若非线性,先分箱。
  7. 测试集信息泄漏:标准化、特征选择、缺失值填补都只能在训练集上拟合参数再套用到测试集,否则测试指标虚高。

7.4 竞赛论文写作建议(话术模板)

  • 模型构建:"构建 Logistic 回归分类模型,测试集 AUC 达 0.92,准确率 0.88,说明模型具有良好的判别能力。"
  • 系数解释:"特征 x1 的优势比 OR=3.21(95%CI:2.32~4.46),且 Wald 检验 p<0.001,表明 x1 每增加一个单位,事件发生几率平均增加约 220%,是显著的风险因素。"
  • 模型拟合:"模型 McFadden 伪 R²=0.35,整体拟合良好;ROC 曲线明显偏离对角线,AUC 显著优于随机猜测,分类性能具有统计学意义。"
  • 稳健性:"采用 5 折交叉验证,平均 AUC 为 0.88±0.03,模型性能稳定,未见明显过拟合。"
  • 对比结论:"与随机森林(AUC 0.90)相比,Logistic 回归 AUC 略低但可解释性更强,综合竞赛需求(需给出违约概率与影响因素),最终选用 Logistic 回归。"

八、延伸阅读

8.1 正则化 Logistic 回归(L1/L2)

  • L2 正则(岭型,sklearn 默认 penalty="l2"):损失加 λ2β22\frac{\lambda}{2}\|\boldsymbol{\beta}\|_2^2,把系数向 0 收缩,抑制过拟合、缓解共线性;C 参数是 λ 的倒数(C 越小正则越强)。
  • L1 正则(套索型):损失加 λβ1\lambda\|\boldsymbol{\beta}\|_1,能把无关特征的系数压缩到精确 0,实现嵌入式特征选择——高维小样本(如文本、基因数据)的首选。
  • 调参:用 LogisticRegressionCV 交叉验证选 C;正则化后解释 OR 依然有效。

8.2 多分类 softmax 回归

类别 K>2K>2 且无序时,用 softmax:pk=ezkj=1Kezjp_k=\dfrac{e^{z_k}}{\sum_{j=1}^{K}e^{z_j}},每个类别各有一组系数;sklearn 中 LogisticRegression(multi_class="multinomial") 即此(默认)。判别指标可用宏平均/加权平均 F1、one-vs-rest AUC。

8.3 有序 Logistic 回归(比例优势模型)

当多分类的类别有序(如"不满意/一般/满意"、"轻/中/重")时,用累计 logit 模型:

lnP(yk)P(y>k)=αkβTx\ln\frac{P(y\le k)}{P(y>k)}=\alpha_k-\boldsymbol{\beta}^T x

其核心假设"比例优势":所有分割点的系数 β\boldsymbol{\beta} 相同,只有截距 αk\alpha_k 不同。该模型比把有序类别当无序处理更省参数、更符合数据本质(statsmodels 的 OrderedModel 或 R 的 polr 可拟合)。

8.4 与 SVM、决策树的对比总结

  • 与 SVM 对比:SVM 只关心边界附近的样本(支持向量),对非线性(RBF 核)和中小样本更鲁棒,但不输出校准概率、系数不可解释;Logistic 回归输出概率且全样本参与估计。数据近似线性可分时两者性能接近,选 Logistic(解释性强);边界高度非线性时选 SVM。
  • 与决策树/随机森林对比:树模型自动挖掘非线性与交互效应、不受量纲影响、高维小样本稳健,但预测概率呈阶梯状(校准差)、解释靠"特征重要性"而非方向明确的 OR;需要"每增加一个单位风险变几倍"这类结论时必须用 Logistic。
  • 融合思路:竞赛中常以 Logistic 回归为基线,随机森林/SVM 做对照;或用树模型筛特征、Logistic 建最终可解释模型。

推荐资料:周志华《机器学习》第 3 章;Hastie 等《The Elements of Statistical Learning》第 4 章;James 等《An Introduction to Statistical Learning》第 4 章;sklearn 官方文档 LogisticRegression 页面。