ML · 章节 3
第三章 监督分类
引言
在第二章里,我们学习了如何用线性回归和 k 近邻去预测一个连续的目标变量——例如鲍鱼的年龄或房屋的价格。然而在工程实践中,更常见的需求并不是估计一个数值,而是回答一个是非题:这位乘客能否在沉船中幸存?这个肿瘤是良性还是恶性?这封邮件是垃圾邮件吗?这类问题统称为分类问题 (classification),其中目标变量 取离散的、有限多个值。当 时,问题被称作二元分类 (binary classification)。
本章将围绕两个迷你数据集展开:titanic_mini(泰坦尼克号生还预测)与 cancer_mini(威斯康星州乳腺癌诊断)。我们将依次介绍朴素贝叶斯、分类指标、概率分数、ROC 曲线、决策树、随机森林、梯度提升以及 LightGBM 与 XGBoost 等业界标杆。
本章的核心问题:当我们说一个分类器"准确率 80%"时,这句话到底有多少信息量?如何区分两个准确率相同但行为迥异的模型?
1. titanic_mini 数据集与经验概率
1.1 数据集结构
titanic_mini 是泰坦尼克号原始数据集的极简版本,仅保留四个二元变量:
| 列名 | 含义 | 取值 |
|---|---|---|
Sex | 性别 | 0 = 男,1 = 女 |
Children | 是否儿童 | 0 = 成人,1 = 儿童 |
FirstClass | 头等舱标志 | 0 = 否,1 = 是 |
Survived | 目标变量:是否幸存 | 0 = 罹难,1 = 幸存 |
所有变量都已经过编码并取布尔值,这让我们可以专注于分类算法本身,而不必处理缺失值或类别编码。
1.2 用布尔条件过滤 DataFrame
pandas 通过逐元素的逻辑运算允许我们提取数据子集。三个核心算子分别是按位与 &、按位或 |、按位非 ~,每个条件都必须放在括号里:
# 一等舱的女性乘客 df[(df["Sex"] == 1) & (df["FirstClass"] == 1)] # 不在一等舱的男性乘客 df[(df["Sex"] == 0) & (df["FirstClass"] == 0)]
由于 Survived 是 0/1 编码的,该列的均值正好等于幸存比例——这是一个非常便利的小技巧:
df[(df["Sex"] == 1) & (df["FirstClass"] == 1)]["Survived"].mean()
关键观察:当一个二元变量取值 0/1 时,
.mean()给出经验概率 。这正是后续朴素贝叶斯估计参数的方法。
1.3 经验生还概率
通过手工计算两组乘客的生还率,我们立刻能感受到性别和舱位的巨大影响:一等舱女性的生还率往往超过 95%,而非一等舱男性则不到 20%。这种差异告诉我们:变量 Sex 与 FirstClass 携带了大量预测信息——它们正是机器学习模型应该利用的特征。
2. 朴素贝叶斯分类器
2.1 贝叶斯定理
给定观测 ,分类的核心目标是估计后验概率:
由于分母 与类别 无关,预测时可以省略,于是:
2.2 朴素假设
直接估计联合分布 在维度增加时迅速变得不可行。朴素贝叶斯通过一个强假设大幅简化问题:
也就是说,给定类别 ,各特征之间相互独立。
关键点 — 朴素假设:这个假设几乎在现实中从未严格成立(一等舱里的女性比例自然偏高,性别与舱位并不独立)。然而即使假设被违反,朴素贝叶斯往往依然给出令人惊讶的好结果,是一个非常稳健、训练极快的基线模型。
2.3 最大后验决策
预测时,我们选择使后验最大的类别:
这被称作 MAP 估计 (Maximum A Posteriori)。
2.4 伯努利朴素贝叶斯
当所有特征都是二元变量时(正如 titanic_mini),合适的条件分布是伯努利分布:
对于一个观测 :
参数 通过频率估计,并使用拉普拉斯平滑避免零概率:
其中 即 sklearn 中的 alpha 参数,默认为 1。
2.5 在 sklearn 中的实现
from sklearn.naive_bayes import BernoulliNB model = BernoulliNB(alpha=1.0) model.fit(X_train, y_train) y_hat = model.predict(X_test)
完整的训练流水线只需四行:拆分、构造、训练、预测。如此简单的接口正是 sklearn 设计哲学的体现——所有模型共享 .fit() / .predict() API。
3. 分类评价指标
3.1 为什么 MAE/RMSE 不再适用?
第二章的回归指标(MAE、RMSE、)在分类中失去了语义。把"罹难"与"幸存"分别编码为 0 和 1 时,两者之间的"距离"并无物理意义;MAPE 在 时还会出现除零错误。我们需要全新的、基于计数的指标。
3.2 准确率
准确率 (accuracy) 是最直观的指标:
在 0/1 编码下,可以等价地写成:
from sklearn.metrics import accuracy_score accuracy_score(y_test, y_hat)
但准确率有一个致命缺陷:在类别失衡的数据上极具误导性。如果 95% 的邮件不是垃圾邮件,那么"全部预测为非垃圾"的模型也能取得 95% 的准确率,却毫无实用价值。
3.3 混淆矩阵
为了细化分析,我们引入混淆矩阵 (confusion matrix):
四个量分别是:真阴性 、假阳性 、假阴性 、真阳性 。所有后续的指标都建立在它们之上。
from sklearn.metrics import confusion_matrix confusion_matrix(y_test, y_hat)
3.4 精确率、召回率与 F1
精确率 (precision) 衡量"阳性预测的可靠性":
召回率 (recall) 衡量"对真实阳性的覆盖程度":
F1 分数 是两者的调和平均:
如何选择? 当假阳性代价高(比如把好邮件误判为垃圾邮件)时,关注 precision;当假阴性代价高(比如漏诊癌症)时,关注 recall;如果两者同等重要,使用 F1。
3.5 分类报告
classification_report 一次性给出每个类别的 precision、recall、F1 和支持样本数:
from sklearn.metrics import classification_report print(classification_report(y_test, y_hat))
这是面试与项目汇报中最常用的诊断工具。
4. 概率分数与决策阈值
4.1 predict_proba
许多分类器(朴素贝叶斯、逻辑回归、随机森林……)实际上输出的是正类的概率,而非直接的类标签:
probas = model.predict_proba(X_test) # shape (n, 2) y_score = probas[:, 1] # 正类概率
4.2 决策阈值
从概率到类别需要一个阈值 :
sklearn 默认 ,但这并非神圣不可侵犯。改变阈值会重新平衡 precision 与 recall:
- 提高阈值 → 阳性预测变少 → precision 上升、recall 下降;
- 降低阈值 → 阳性预测变多 → recall 上升、precision 下降。
t = 0.7 y_hat = (y_score >= t).astype(int)
阈值的选择应由业务成本驱动。例如在癌症筛查中,宁可让一些良性肿瘤进入复检,也不能漏掉一个恶性病例——此时合适的阈值通常远低于 0.5。
5. ROC 曲线与 AUC
5.1 阈值无关的评估
固定阈值评估只能反映模型在某个工作点的表现。ROC 曲线 通过遍历所有可能的阈值,给出与阈值选择无关的整体评价。
5.2 TPR 与 FPR
对每个阈值 ,定义:
ROC 曲线把 作为点画出来,阈值从 1 扫到 0。
5.3 解读
- 点 :阈值极高,没有阳性预测;
- 点 :阈值极低,全部预测为阳性;
- 对角线 :随机分类器;
- 曲线越靠近左上角,模型越好。
5.4 AUC
AUC (Area Under Curve) 是 ROC 曲线下方的面积,取值于 。
AUC 的概率解释:随机抽取一个正样本和一个负样本,AUC 等于"模型给正样本打分高于负样本的概率"。
经验性的判读标尺:
- :随机;
- :可用;
- :良好;
- :优秀。
from sklearn.metrics import roc_curve, roc_auc_score y_score = model.predict_proba(X_test)[:, 1] fpr, tpr, thresholds = roc_curve(y_test, y_score) auc = roc_auc_score(y_test, y_score)
注意:ROC 与 AUC 必须以概率分数计算,而不是以离散的预测标签计算。
6. 决策树
6.1 直观图景
决策树通过一连串关于特征的"是非问题"将样本路由到叶子节点。每一次分裂都试图让子节点变得更纯——理想情况下每个叶子只包含一个类别。
6.2 基尼指数
对于二元目标 ,基尼不纯度 (Gini impurity) 定义为:
其性质:
- 当 时 取最大值 (最不纯);
- 当 或 时 (完全纯)。
6.3 选择切分变量
在节点 上,对每个候选变量 :
- 按 的取值划分 为子集 ;
- 计算每个子集的基尼 ;
- 计算加权平均:
- 选择使 最小的变量进行分裂。
6.4 数值变量的切分
对于 cancer_mini 中的连续变量 texture、radius 等,分裂规则形如 。算法会枚举所有可能的阈值(通常取相邻取值的中点),选择令子节点不纯度最小化的 。这就是为什么决策树 对连续变量自动进行离散化,无需用户手动分箱。
6.5 停止准则
- 完全纯净:,节点变成叶子;
- 无增益:所有候选切分都不能降低基尼;
- 结构限制:
max_depth(最大深度)、min_samples_split(分裂最小样本数)、min_samples_leaf(叶子最小样本数)。
这些超参数控制过拟合:树越深越容易记忆训练集中的噪声。
6.6 用 sklearn 训练与可视化
from sklearn.tree import DecisionTreeClassifier, plot_tree, export_text model = DecisionTreeClassifier(max_depth=3) model.fit(X_train, y_train) plot_tree(model, feature_names=X.columns, class_names=["0", "1"], filled=True, rounded=True, impurity=True) print(export_text(model, feature_names=list(X.columns)))
plot_tree 给出图形树,每个节点显示分裂规则、基尼值、样本数和类别分布;export_text 输出文本规则。
决策树的最大优势:可解释性。一棵深度为 3 的树可以直接讲给业务方听:"如果是儿童 → 幸存;否则如果是女性 → 幸存……"。
7. 交叉验证
单次 train/test 划分的评估结果会受到划分随机性的影响。 折交叉验证 (cross-validation) 把数据切成 份,轮流让每一份充当测试集,最后取平均:
from sklearn.model_selection import cross_val_score scores = cross_val_score(model, X, y, cv=10, scoring="accuracy") print(scores.mean(), scores.std())
通过遍历 max_depth,配合 cross_val_score,可以画出"验证准确率—深度"曲线,找到偏差—方差权衡的最优点。
8. 集成方法
单棵决策树的方差大、对噪声敏感。集成学习 (ensemble learning) 通过组合多棵树解决这一问题。
8.1 随机森林
随机森林在两个层面引入随机性:
- Bootstrap 采样:每棵树用一个有放回的随机样本训练;
- 特征子采样:每个节点只在随机选出的一部分变量中找最佳切分。
预测时分类取多数投票,回归取平均。
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(n_estimators=200, max_depth=5) model.fit(X_train, y_train)
主要超参数:n_estimators(树的数量)、max_depth、max_features(每节点候选变量数)、min_samples_leaf、bootstrap。增大 n_estimators 通常稳定模型而不会引发过拟合。
8.2 梯度提升
梯度提升 (Gradient Boosting) 与随机森林截然不同。它顺序地构造一系列浅树,每棵新树修正前面所有树的累计残差:
最终预测 = 累加的逐步修正,每步修正本身是一棵简单的树。
学习率 (learning_rate) 控制每次修正的强度,通常推荐"小学习率 + 多树"的组合。
from sklearn.ensemble import GradientBoostingClassifier model = GradientBoostingClassifier( n_estimators=200, learning_rate=0.05, max_depth=5 )
| 对比维度 | 随机森林 | 梯度提升 |
|---|---|---|
| 树间关系 | 独立 | 序列依赖 |
| 训练 | 可并行 | 必须顺序 |
| 聚合 | 投票/平均 | 累加修正 |
| 过拟合风险 | 低 | 较高(需调 learning rate) |
8.3 LightGBM 与 XGBoost
LightGBM 与 XGBoost 是梯度提升的工业级实现,在 Kaggle 表格数据竞赛中长期占据榜首。
LightGBM 的关键创新是按叶生长 (leaf-wise):每次只展开能最大降低损失的那片叶子,而非按深度逐层扩张。这让它训练更快、树更紧凑。
from lightgbm import LGBMClassifier model = LGBMClassifier(n_estimators=300, learning_rate=0.05, verbosity=-1)
XGBoost 在标准梯度提升基础上加入 L1/L2 显式正则、近似切分算法、二阶 Taylor 展开等多项优化,对过拟合具有更强的控制力。
from xgboost import XGBClassifier model = XGBClassifier(n_estimators=300, learning_rate=0.05, max_depth=3)
XGBoost 还支持 GPU 加速,只需设置 tree_method="gpu_hist"。
在 cancer_mini 上对四种集成模型作 10 折交叉验证比较,通常会得到非常接近的高准确率(0.95 左右)。这说明在干净的小数据集上,模型选择不是瓶颈;而在大型、含噪声的真实数据上,LightGBM/XGBoost 的工程优化才会真正体现价值。
练习
- 经验概率:在
titanic_mini上,分别计算"一等舱女性"和"非一等舱男性"两个子群体的生还率,比较两个值。 - 朴素贝叶斯基线:用
BernoulliNB训练titanic_mini分类器,输出accuracy_score与classification_report,并解释回归指标(MAE/RMSE)为何不适用。 - 手算 TPR(0.7):给定
y_test与y_score,在阈值 下手动计算 、、、,由此得到 与 ;用roc_curve画出完整 ROC 曲线,并把上述点叠加到图上。 - 基尼计算:在
titanic_mini上分别按Sex、Children、FirstClass切分,计算切分后的加权基尼,找出基尼最小的变量,验证它就是DecisionTreeClassifier选作根节点的变量。 - 可视化决策树:在
titanic_mini与cancer_mini上各训练一棵max_depth=3的决策树,用plot_tree与export_text显示结构与规则。 - 深度调优:遍历
max_depth ∈ {1, 2, …, 29},用 10 折交叉验证记录平均准确率,画出曲线并找出最优深度。讨论欠拟合与过拟合区间。 - 集成大比拼:在
cancer_mini上以 10 折交叉验证比较 RandomForest、GradientBoosting、LightGBM、XGBoost 四种模型的准确率均值与标准差。 - 阈值调优:从
BernoulliNB取predict_proba,在 下分别评估 precision、recall、F1,讨论业务场景(漏判恶性肿瘤 vs 误判良性肿瘤)下应如何取舍阈值。
拓展阅读
sklearn.naive_bayes.BernoulliNB— scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.BernoulliNB.html- 分类指标用户指南 — scikit-learn.org/stable/modules/model_evaluation.html#classification-metrics
sklearn.metrics.roc_curve与roc_auc_score— scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_curve.htmlsklearn.tree.DecisionTreeClassifier— scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html- 决策树用户指南(包含 Gini 与 Entropy 推导) — scikit-learn.org/stable/modules/tree.html
sklearn.ensemble.RandomForestClassifier— scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.htmlsklearn.ensemble.GradientBoostingClassifier— scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html- LightGBM 官方文档 — lightgbm.readthedocs.io
- XGBoost 官方文档 — xgboost.readthedocs.io
sklearn.model_selection.cross_val_score— scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_val_score.html- 经典参考:Hastie, Tibshirani, Friedman, The Elements of Statistical Learning,第 9 章(决策树)与第 10 章(Boosting)。
- 工业实践:Chen & Guestrin, XGBoost: A Scalable Tree Boosting System, KDD 2016。