4
逻辑回归与分类指标
Logistic Regression & Classification Metrics
逻辑回归名字叫"回归",干的却是分类的活。它把线性回归的输出(负无穷到正无穷)用 Sigmoid 压到 0~1 之间,当成"是正例的概率"。二分类的 baseline 之王。
数学原理:Sigmoid + 交叉熵
Sigmoid:σ(z) = 1 / (1 + e-z),把任意实数压到 (0,1)
假设函数:p = σ(θᵀx);预测:p > 0.5 → 正例,否则负例
论为什么不用 MSE
分类用交叉熵损失(Cross Entropy)而不是 MSE——因为 MSE 在 Sigmoid 下是非凸的,梯度下降容易卡局部最优;交叉熵对预测错误的惩罚很大,且梯度形状漂亮。J(θ) = -Σ[y·log(p) + (1-y)·log(1-p)]。
多分类:OvR vs Softmax
| 方法 | 思路 |
|---|---|
| OvR(One-vs-Rest) | 有 K 类就训 K 个二分类器,每个判"是不是这一类",选概率最高的。sklearn 的 LogisticRegression 默认。 |
| Softmax(多项逻辑回归) | 一次输出 K 类的概率,所有概率和为 1。 |
分类指标全家桶
| 指标 | 公式 / 含义 |
|---|---|
| 混淆矩阵 | TP / FP / FN / TN 四格表。 |
| 准确率 Accuracy | (TP+TN) / 全部。类别均衡时用,不均衡会骗人。 |
| 精确率 Precision | TP / (TP+FP)。"预测为正的里面真的正"。 |
| 召回率 Recall | TP / (TP+FN)。"所有真的正例被找出来多少"。 |
| F1 | 2·P·R / (P+R),调和平均,平衡两者。 |
| ROC-AUC | ROC 曲线下面积,0.5=瞎猜,1=完美,类别不平衡时首选。 |
论怎么记
看病看重 recall(漏诊代价高,宁可错杀);抓坏人看重 precision(误伤代价高,宁可放过)。
Sigmoid 函数图像与性质
import numpy as np
import matplotlib.pyplot as plt
z = np.linspace(-10, 10, 100)
sigmoid = lambda z: 1 / (1 + np.exp(-z))
plt.plot(z, sigmoid(z), label="Sigmoid")
plt.axhline(0.5, color="gray", ls="--")
plt.axvline(0, color="gray", ls="--")
plt.title("Sigmoid: 把实数压到 (0,1)")
plt.grid(True)
plt.show()
# 性质:z=0 时输出 0.5(决策边界)
# z 很大时趋近 1,z 很小时趋近 0
# 导数最大在 z=0 处,两端饱和(导数趋近 0)→ 梯度消失
完整案例:信用卡欺诈检测
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (confusion_matrix, classification_report,
roc_auc_score, f1_score)
# 构造极度不平衡数据:99% 正常,1% 欺诈
X, y = make_classification(
n_samples=10000, n_features=20,
weights=[0.99], flip_y=0, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42
)
# stratify=y:保证训练集和测试集类别比例一致
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
# baseline:不处理不平衡
lr = LogisticRegression(max_iter=1000, random_state=42)
lr.fit(X_train_s, y_train)
pred = lr.predict(X_test_s)
print("准确率:", np.mean(pred == y_test))
# 准确率:0.99(看起来很美)
print("F1:", f1_score(y_test, pred))
# F1:0.0(欺诈全没抓到!)
# 修复:class_weight="balanced" 自动按类别频率加权
lr_bal = LogisticRegression(max_iter=1000, class_weight="balanced", random_state=42)
lr_bal.fit(X_train_s, y_train)
pred2 = lr_bal.predict(X_test_s)
proba = lr_bal.predict_proba(X_test_s)[:, 1]
print("\n=== 平衡后的混淆矩阵 ===")
print(confusion_matrix(y_test, pred2))
# [[1850 130]
# [ 8 12]]
print("\n=== 分类报告 ===")
print(classification_report(y_test, pred2, target_names=["正常", "欺诈"]))
print("ROC-AUC:", roc_auc_score(y_test, proba))
# ROC-AUC:0.92
本章面试题
面试 · 逻辑回归
Q1. 逻辑回归为什么用交叉熵不用 MSE?
查看答案
Sigmoid + MSE 是非凸损失,梯度下降容易卡局部最优;交叉熵在 Sigmoid 下梯度形状好(误差大梯度大),且是 MLE 推导出来的。
Q2. 类别不平衡怎么办?
查看答案
① class_weight="balanced";② 重采样(SMOTE 过采样 / 欠采样);③ 换指标(F1 / ROC-AUC / PR-AUC);④ 调整分类阈值(不一定要 0.5)。
Q3. precision 和 recall 怎么权衡?
查看答案
移动分类阈值:阈值高 precision 高 recall 低;阈值低 recall 高 precision 低。画 PR 曲线选工作点。
Q4. solver 怎么选?
查看答案
小数据集 liblinear;大且多分类 lbfgs / saga;带 L1 正则用 saga。