第 3 章 · 第 7 讲 / 共 7 讲
类别不平衡,以及往哪走
拆掉全章一直默认的那个前提,然后交代第 3 章剩下的阅读材料和全章自查。
能用一个数字例子说明「精度会骗人」;能从决策门槛的角度解释不平衡为什么伤害模型;能推出再缩放公式并说出它的前提;能分清欠采样、过采样、阈值移动三条路线各自的代价;知道 3.7 的五个方向分别指向哪一章。
前面六讲,包括 3.3、3.4、3.5,全都默默假设了一件事:各类样本数量差不多。这一讲把这个前提拆掉,看看会塌成什么样,以及怎么补。
3.6 类别不平衡问题给 3.5 打的补丁
这一节的位置很讲究。它放在多分类学习之后,因为多分类的拆解过程会自己制造不平衡问题。但更根本的原因是:前面所有方法都建立在一个从未明说的假设上。
"前面介绍的分类学习方法都有一个共同的基本假设,即不同类别的训练样例数目相当。"
注意"相当"不是"相等",是"差不多"。书里也说:"如果不同类别的训练样例数目稍有差别,通常影响不大。"
问题出在"差别很大"的时候。"类别不平衡"(class-imbalance)指的就是这种情况。本节假设正类样例较少、反类样例较多(这是最常见的场景,比如"患病/健康""欺诈/正常")。
先看清问题有多严重
书里用了一个极端的例子,非常值得慢慢读:
998 个反例,2 个正例。现在有一个"聪明"的学习器,它的策略是——永远预测"反例"。
它的精度是多少?
99.8% 的精度!听起来是个近乎完美的模型。
但它一个正例都找不出来。书里的评价是:"然而这样的学习器往往没有价值,因为它不能预测出任何正例。"
在类别不平衡的数据上,"精度"这个指标会骗人。
想想看:如果任务是"从 1000 个人里找出 2 个病人",一个"谁都说健康"的模型精度 99.8%,但它漏掉了全部病人——在这种任务里,这 99.8% 是毫无意义的。
所以看完这一节,你应该记住的不只是"再缩放"这个公式,还有"选对评价指标"这件事(第 2 章的查准率、查全率、F1、AUC 就是干这个的)。
从"决策门槛"看这个问题(本节最漂亮的一段)
书里说"从线性分类器的角度讨论容易理解"。我们就顺着这个角度走一遍。
用 y = wTx + b 对新样本分类时,本质上是在拿预测出的 y 和一个阈值比较:通常 y > 0.5 判正例,否则判反例。
为什么是 0.5?因为 y 表达的是"正例的可能性",那么 y/(1−y) 就是"正例可能性与反例可能性的比值"。阈值设成 0.5,等价于要求这个比值大于 1:
而 "> 1" 的含义是:分类器认为真实正、反例的可能性相同——这是一个 1:1 的赌注。
但训练集里的比例不是 1:1 啊!令 m+ 是正例数,m− 是反例数,那么观测几率是 m+/m−。
书里用了一个假设(注意,这个假设后来被证明很关键):"由于我们通常假设训练集是真实样本总体的无偏采样,因此观测几率就代表了真实几率。"
于是正确的决策规则应该改成:只要预测几率高于观测几率,就判为正例:
还是 998 反例、2 正例,m+/m− = 2/998 ≈ 0.002。
意思是:在这个数据集里,随便抓一个样本,它是正例的赔率本来就只有 1:499。所以模型不该等到"预测几率 > 1"才判正例——只要预测几率超过 0.002,就已经比瞎猜更值得押注了。
如果还死守 0.5 这个门槛,模型就会变得极度保守,正例几乎全被漏掉。这就是"不平衡"真正伤害模型的方式——不是训练不出来,而是决策门槛站错了位置。
再缩放:把门槛"挪"到正确的地方
问题是:我们的分类器是按式 (3.46)(门槛 1)训练出来的。要让它执行式 (3.47)(门槛 m+/m−),怎么办?
书里的做法极简洁:"只需令":
这叫再缩放(rescaling),是类别不平衡学习的一个基本策略。
书里说"要做到这一点很容易",我们检查一遍逻辑闭环:
假设原始预测满足式 (3.47),也就是 y/(1−y) > m+/m−。两边同乘 m−/m+:
y/(1−y) × m−/m+ > 1
而左边正是 y′/(1−y′)。所以 y′/(1−y′) > 1——这正是原来那个 0.5 的门槛!
也就是说:把预测几率乘上补偿系数之后,用老门槛(0.5)决策,效果就等于用新门槛(m+/m−)决策。完美衔接。
把这个系数的意义说白:m−/m+ 是一个"补偿倍数"。正例越少(m+ 越小),这个倍数越大,模型就越倾向于判正例——正好抵消了训练集里正例稀缺带来的偏见。
"再缩放的思想虽简单,但实际操作却并不平凡,主要因为'训练集是真实样本总体的无偏采样'这个假设往往并不成立,也就是说,我们未必能有效地基于训练集观测几率来推断出真实几率。"
翻译:如果训练集本身是"偏"的(比如数据是人工挑出来标注的,或者采集方式有偏),那 m+/m− 根本不代表真实世界的比例,用它做补偿就是错的。
这是本节最容易被跳过、但实践中最要命的一句话。
三类实际做法
因为"改公式"这条路受假设限制,所以实践中主要靠"改数据"或"改决策"。书里归成三类:
| 路线 | 做法 | 优点 | 风险 |
|---|---|---|---|
| 欠采样 undersampling |
去掉一些反例,让正反例数目接近,再学习 | 时间开销通常远小于过采样(训练集变小了) | 随机丢弃反例"可能丢失一些重要信息" |
| 过采样 oversampling |
增加一些正例,让正反例数目接近,再学习 | 不丢信息 | 训练集变大,开销大;简单重复采样会招致严重过拟合 |
| 阈值移动 threshold-moving |
不动数据,直接基于原始训练集学习,但预测时把式 (3.48) 嵌进决策过程 | 不动数据,最简单 | 依赖上面那个"无偏采样"的假设 |
两个代表算法,值得记住名字
书里各给了一个,思路都很聪明:
它解决的问题:过采样不能简单地"把正例复制一遍",否则模型会把这几个样本背下来,严重过拟合。
它的做法:不对训练集里的正例做重复采样,而是通过对正例进行"插值"来产生额外的正例。
"插值"是什么意思:找两个正例样本,在它们之间的连线上随机取一个点,造出一个"不存在但合理"的新正例。这样新样本既不是原样本的复制,又落在正例该在的区域里。
它解决的问题:欠采样"随机丢弃反例可能丢失重要信息"——万一丢掉的那批反例里正好有关键样本呢?
它的做法:"利用集成学习机制,将反例划分为若干个集合供不同学习器使用,这样对每个学习器来看都进行了欠采样,但在全局来看却不会丢失重要信息。"
为什么这招有效:假设 998 个反例,分成 10 份,每份约 100 个,配 2 个正例训一个学习器,一共训练 10 个。每个学习器看到的是"欠采样"后的平衡数据(不会偏向反例),但把 10 个学习器的结果集成起来,998 个反例的信息全都用上了。等于"既要平衡、又要信息",用一个巧妙的切分同时满足了。(第 8 章会正式讲集成学习。)
顺便:这跟"代价敏感学习"是什么关系
书里最后一段把两个概念连了起来:"'再缩放'也是'代价敏感学习'(cost-sensitive learning)的基础。在代价敏感学习中将式 (3.48) 中的 m−/m+ 用 cost+/cost− 代替即可。"
cost+:把正例误分为反例的代价。(比如"病人被判健康"——漏诊,可能是致命的)
cost−:把反例误分为正例的代价。(比如"健康人被说有病"——误诊,多花点检查费)
如果 cost+ ≫ cost−,比值很大,模型就更倾向判正例——和"正例太少时更倾向判正例"在数学上是同一个动作。
不过书里也强调了一个区分(这里对应 Zhou & Liu, 2006b):"非均等代价和类别不平衡性虽然都可借助'再缩放'技术,但两者本质不同。"
不同在哪?类别不平衡是"数据里样本多寡"的问题;非均等代价是"犯错后果轻重"的问题。前者是客观的分布,后者是人为的定价。两者恰好都能用同一个公式处理,但不该混为一谈。书里还补了一句:类别不平衡学习中通常是"较小类的代价更高,否则无需进行特殊处理"。
"对 OvR、MvM 来说,由于对每个类进行了相同的处理,其拆解出的二分类任务中类别不平衡的影响会相互抵消,因此通常不需专门处理。"
为什么?因为 OvR 里每一类都轮流当一次正例。A 类当正例时它是少数,B 类当正例时 A 类又成了多数——每个类都被"少数"和"多数"各体验一遍,偏差互相抵消了。
但 OvO 不一样:两两配对后,某些配对可能仍然严重不平衡,那就得管。所以书里只说了 OvR 和 MvM。
随堂自测 · 3.6
训练集有 998 个反例、2 个正例。按再缩放公式,预测几率 y/(1−y) 前面的补偿系数 m−/m+ 约等于多少?
m−/m+ = 998/2 = 499。注意分子分母的位置:反例数在分子。它是个"放大倍数"——正例越稀缺,放大倍数越大,模型就越容易判正例。如果搞反成 m+/m−=0.002,那就等于把正例压得更少了,方向完全相反。
3.7 阅读材料每一条都是一个岔路口
这一节篇幅很短,但每段都在指向后面的章节或一个研究方向。我们按原书顺序逐段过,不跳过。
① 稀疏表示与 LASSO
"稀疏表示"近年来很受关注,但即便对多元线性回归这样简单的模型,获得具有最优"稀疏性"的解也并不容易。稀疏性问题本质上对应了 L0 范数的优化,这在通常条件下是 NP 难问题。LASSO 通过 L1 范数来近似 L0 范数,是求取稀疏解的重要技术。
什么是"稀疏"?就是让学到的 w 里大部分分量恰好等于 0。好处有两个:模型只依赖少数几个特征(简单、不容易过拟合),而且你能直接看出"到底哪几个特征有用"(可解释)。
为什么难?"最优稀疏"要数一数 w 里非零元素的个数,这就是 L0 范数。而"数个数"这个操作是跳变的(从 0 个变 1 个),没法求导,也没法用梯度方法优化——事实上它是 NP 难的(组合爆炸)。
LASSO 的巧劲:用 L1 范数(各分量绝对值之和)来近似 L0。L1 是凸的、可优化的,而且它天然会把一些系数"压到恰好 0"——这个性质很神奇,第 11 章会讲清楚原理。Tibshirani 1996 那篇论文就是它的出处。
② ECOC 的理论进展
这一段有四层信息:
- OvO 和 OvR 都是 ECOC 的特例(Allwein et al., 2000)。这个结论挺漂亮——说明 3.5 讲的三种策略其实是同一框架下的不同选择。
- 人们以往希望设计"通用"的编码法。但 Crammer & Singer (2002) 提出要考虑问题本身的特点,设计"问题依赖"的编码法,并证明寻找最优的离散编码矩阵是一个 NP 完全问题。
- 此后出现了多种问题依赖的 ECOC 编码法,通常思路是"找出具有代表性的二分类问题来进行编码"(Pujol et al., 2006, 2008)。也就是说,不是随便拆,而是优先拆那些"最容易分"的二分类问题。
- Escalera et al. (2010) 开发了一个开源 ECOC 库,可以直接用。
③ MvM 的其他实现
"MvM 除了 ECOC 还可有其他实现方式,例如 DAG 拆分法(Platt et al., 2000)将类别划分表达成树形结构,每个结点对应于一个二类分类器。"
DAG 是"有向无环图"。用大白话说:不是让所有分类器同时投票,而是像走迷宫一样一层层排除——第一层的分类器先把候选缩小,第二层再缩小,直到只剩一个。好处是测试时不用跑所有分类器。
另外还有一批工作"致力于直接求解多分类问题",比如多类支持向量机(Crammer & Singer, 2001;Lee et al., 2004)——不去拆,直接改算法本身。
④ 代价敏感学习的边界
这一段信息量很大,逐句看:
- "代价敏感学习中研究得最多的是基于类别的'误分类代价',代价矩阵如表 2.2 所示;本书在提及代价敏感学习时,默认指此类情形。"
- "已经证明,对二分类任务可通过'再缩放'获得理论最优解(Elkan, 2001)"——所以 3.6 那个公式在二分类下不只是"启发式",是有理论保证的。
- "但对多分类任务,仅在某些特殊情形下存在闭式解(Zhou & Liu, 2006a)"——多分类的代价敏感问题要难得多。
- "非均等代价和类别不平衡性虽然都可借助'再缩放'技术,但两者本质不同(Zhou & Liu, 2006b)。"
- "需注意的是,类别不平衡学习中通常是较小类的代价更高,否则无需进行特殊处理。"
⑤ 多标记学习:一个容易混淆的邻居
最后一段澄清了一个概念区别,非常值得记:
多分类:有很多类别,但每个样本只属于其中一个。(这个瓜是西瓜,就不能是南瓜。)
多标记(multi-label):一个样本可以同时有多个标签。书里的例子:一幅图像可同时标注为"蓝天""白云""羊群""自然场景"。
所以多标记不是多分类的一种,而是另一个任务类型(Zhang & Zhou, 2014 有综述)。如果你要做"一张图打多个标签"的任务,别往 3.5 的框架里硬套。
附:书里那个小故事——最小二乘法是谁发明的
这一段看着像闲笔,其实讲的是"一个方法为什么会被记住"。
1801 年,意大利天文学家皮亚齐发现了 1 号小行星"谷神星"。但跟踪观测 40 天后,谷神星转到太阳背后,他失去了它的位置。许多天文学家试图重新找到它,都徒劳无获。
这事引起了高斯(1777—1855)的注意。他发明了一种方法,根据皮亚齐的观测数据计算出了谷神星的轨道。后来德国天文学家奥伯斯在高斯预言的时间和星空领域,重新找到了谷神星。
1809 年,高斯在《天体运动论》中发表了这种方法——即最小二乘法。
1805 年,法国数学家勒让德(1752—1833)发表了《计算彗星轨道的新方法》,其附录中描述了最小二乘法。但勒让德的书里没有涉及最小二乘法的误差分析。
而高斯 1809 年的著作包括了这方面的内容——书里的评价是:"这对最小二乘法用于数理统计、乃至今天的机器学习有极为重要的意义。"
由于这一重大贡献,加上高斯声称自己 1799 年就已开始使用这个方法,很多人将发明优先权归之于高斯。两位大数学家发生了著名的优先权之争,此后有许多数学史家专门研究,但至今也没弄清到底是谁最先发明了最小二乘法。
勒让德先发表,高斯后发表,但历史把功劳更多记在高斯头上。差别不在"谁先想到那个公式",而在高斯补上了误差分析——他解释了"为什么这样做是合理的、误差有多大"。
这对学机器学习的人是个很好的提醒:一个方法的价值,往往不在于公式本身,而在于你是否理解它成立的条件和它的误差边界。这也是为什么这份讲义在每节都要单独讲"常见坑"。
章末自查合上书,看能不能讲出来
读完不等于学会。下面这 12 条是这一章的"骨架",建议你合上文档,逐条口头讲一遍。讲不出来的,就是还没真正拿下的地方——回去看对应小节。
| # | 对应节 | 你应该能讲清楚的事 |
|---|---|---|
| 1 | 3.1 | 线性模型的公式长什么样,w 和 b 分别控制什么,为什么它可解释 |
| 2 | 3.2 | 为什么无序属性不能直接编号 1、2、3,而要用 k 维向量 |
| 3 | 3.2 | 为什么用"平方"而不是绝对值,以及为什么"求导置零"就一定是全局最优 |
| 4 | 3.2 | 闭式解 w = 协方差 ÷ 方差;以及 b = ȳ − w x̄ 意味着直线过重心 |
| 5 | 3.2 | 为什么要把 b 吸收进 ŵ,矩阵 X 为什么要补一列 1 |
| 6 | 3.2 | 什么时候 XTX 不满秩,后果是什么,正则化怎么救 |
| 7 | 3.3 | 为什么不能用阶跃函数,sigmoid 好在哪;"几率"和"对数几率"各是什么 |
| 8 | 3.3 | 极大似然法的直觉;式 (3.26) 那个"开关"技巧;式 (3.27) 为什么是凸的 |
| 9 | 3.4 | LDA 的两个愿望怎么变成一个分式;Sw、Sb 各量的是什么 |
| 10 | 3.4 | 为什么 w 只能定方向不能定长度;Sw−1 的"白化"直觉;为什么是 N−1 维 |
| 11 | 3.5 | OvO / OvR 的分类器数量与开销对比;ECOC 的编码与解码流程,以及"纠错"是怎么发生的 |
| 12 | 3.6 | 998/2 那个例子说明了什么;再缩放公式为什么成立;三类做法各自的代价 |
第 3 章的所有公式,只有写一遍代码才会真的落地。最小的三个练习:
① 用 numpy 实现式 (3.12) 的多元线性回归,和 sklearn.linear_model.LinearRegression 的结果对比一下(会发现几乎一样)。
② 用梯度下降手写对率回归(目标是式 3.27),观察损失怎么下降。再试一次牛顿法,比较收敛速度。这一条对应习题 3.3。
③ 实现式 (3.39) 的 LDA,在二维数据上把投影方向画出来。这一条对应习题 3.5。
习题里还有两道理沦题值得做:3.1(什么情形下不必考虑偏置 b——提示:想想数据是否已经中心化)、3.2(证明 3.18 非凸而 3.27 凸)。
本讲小结
- 不平衡数据上 「精度」会骗人:全猜反例就能 99.8%,但毫无价值。
- 问题的本质是决策门槛站错了位置:模型按「正反一样多」设计(门槛 1),而真实比例是 m+/m−。
- 再缩放:把几率乘上 m−/m+,老门槛就等效于新门槛。
- 但「训练集是无偏采样」这个假设常不成立 —— 所以还有欠采样、过采样、阈值移动三条路。
- 代价敏感学习把 m−/m+ 换成 cost+/cost−,两者数学同形,但本质不同。
第 3 章到此结束。建议回到 课程目录,从第 1 讲开始复盘一遍「章末自查」;或者直接进第 4 章决策树——那是另一条思路,和本章几乎不重叠。
卡住了就直接问我 —— 把没看懂的那一句原样抄给我,我单独展开讲,不用客气。