内容与七讲完全相同,只是没有拆分,一次读完约 90 分钟。建议按讲分开读——每讲 15–25 分钟,读完做一道自测:课程目录。
机器学习 · 西瓜书 · 第 3 章
线性模型
从一条直线开始,走到分类、降维和不平衡数据。逐节拆解 3.1–3.7,书里跳过的推导在这里补上。
这一章是西瓜书里最"友好"的一章,也是最容易被读快的一章。原因很矛盾:公式看起来都认识,但每一步之间的跳跃其实不小——比如 3.2 里两个偏导数是怎么变成闭式解的,3.3 里似然函数怎么化简成那个样子,3.4 里为什么 w = Sw-1(μ₀-μ₁) 就冒出来了。
所以这份讲义的做法是:先把每段话翻译成人话,再逐个符号解释,最后补上被省略的中间步骤。你可以把它当成"带旁白的西瓜书第 3 章"。
公式编号沿用你本地这份教材(如 式 (3.27)),每一节都能在原文件里找到对应段落。讲义只做三件事:把书上的话翻译成人话、把符号逐个拆开、把书里跳过的中间步骤补回来——不引入教材以外的内容,也不需要联网查任何东西。
本章地图先看全局,再进细节
第 3 章从头到尾只在回答一个问题:怎么用"加权求和"这一个动作,去干完机器学习里几乎所有的基础任务?
所有内容的地基
怎么解 w 和 b
套一个 sigmoid
换个思路:找投影方向
拆开来用
怎么办
看这张图的顺序很重要:3.2 解决"回归",3.3 和 3.4 解决"二分类"(两条完全不同的路线),3.5 把二分类拼成多分类,3.6 修补 3.5 留下的一个漏洞。这也是原书那句"我们先从回归任务开始,然后讨论二分类和多分类任务"的意思。
3.1 基本形式全章的地基
先看定义。给定一个样本 x,它有 d 个属性(比如西瓜的色泽、根蒂、敲声……一共 d 个),那么:
这句话的人话版:每个属性乘上它自己的分量(权重),全部加起来,再补一个底数。就这么简单。
举个具体的:假设判断一个西瓜甜不甜,只看三个属性——糖度 12、水分 80、重量 5。模型是 0.8×糖度 + 0.1×水分 − 0.3×重量 + 2,那预测值就是 9.6 + 8 − 1.5 + 2 = 18.1。模型干的全部事情,就是这一行加法。
三个符号,逐个说清
| 符号 | 是什么 | 怎么读 |
|---|---|---|
| xi | 样本在第 i 个属性上的取值 | "第 i 个特征的值"。它是一个数。 |
| wi | 第 i 个属性对应的权重,是要学的东西 | "第 i 个特征有多重要"。也是个数字,可以是负的。 |
| b | 偏置项(bias),也叫截距,同样是要学的东西 | "不管特征是什么,先垫一个底"。没有它,模型就被迫穿过原点。 |
然后是向量写法。把 w 和 x 各自打包成一个有序的列表(这就是"向量"),式 (3.1) 那串加法就能写成:
wTx 念作"w 转置乘以 x",意思是把两个列表对应位置的数相乘,再把所有乘积加起来——也就是所谓的点积。它和式 (3.1) 那串长加法是一模一样的东西,只是写起来短。
为什么要换成这个写法?因为式 (3.1) 里有个 d,d 可能是 3 也可能是 30000。写成 wTx 之后,不管 d 是多少,式子的样子都不变。后面推公式、写代码,全靠这个便利。
为什么这么简单的东西值得单独一节?
书给了三条理由,值得一条条体会:
- 它是很多强模型的骨架。原书说"许多功能更为强大的非线性模型可在线性模型的基础上通过引入层级结构或高维映射而得"。翻译一下:神经网络的一层,本质就是 wTx+b 再套一个非线性函数(第 5 章);支持向量机能处理非线性,靠的是先把数据映射到高维再在这个高维空间里做线性划分(第 6 章)。所以这一章没读懂,第 5、6 章会很痛苦。
- 它可解释。这是线性模型独有的优势。书里的例子:若学得
f好瓜(x) = 0.2·色泽 + 0.5·根蒂 + 0.3·敲声 + 1,
因为根蒂的权重 0.5 最大,敲声 0.3 次之,色泽 0.2 最小,所以可以读成:"根蒂最要紧,敲声比色泽更重要。"权重的大小直接就是重要性排序,这在深度神经网络里是拿不到的。 - 它形式简单、易于建模。参数只有 d+1 个,训练快,不容易过拟合,出了问题也好排查。
上面"权重越大越重要"这句话有个前提:各个特征的量纲(尺度)差不多。如果"糖度"的取值范围是 0–15,而"重量"是 0–5000 克,那重量的权重天然会很小,你没法通过比权重来判断谁重要。所以真要比权重,得先把特征标准化(都缩放到同一尺度)。书里没写这一句,但这是实践里必须知道的。
随堂自测 · 3.1
若学得 f好瓜(x) = 0.2·色泽 + 0.5·根蒂 + 0.3·敲声 + 1,下面哪种解读是对的?
权重衡量的是"这个属性每变一个单位,预测值变多少",所以 0.5 > 0.3 > 0.2 就是重要性排序。另外三个都错在细节上:权重之和不必为 1(也可以为负、可以很大);0.2 不是 0,色泽仍在起作用;而"取值范围宽"恰恰应该配更小的权重,否则一个量纲很大的特征会主导整个模型——这正是上一段"常见坑"说的事。
3.2 线性回归让模型输出一个实数
回归任务的意思是:要预测的东西是连续的实数——房价、温度、西瓜的含糖量。这类任务里,"线性模型"就叫"线性回归"(linear regression)。
数据集的写法先熟悉一下:
读作:一共有 m 个样本,第 i 个样本有 d 个属性(记作向量 xi),它的正确答案是实数 yi。m 是样本个数,d 是属性个数,这两个数在这一章里反复出现,别搞混。
动笔之前:离散属性怎么变成数字
模型只会做加减乘,所以属性必须是数。但现实里的属性常常是"高/中/低""西瓜/南瓜/黄瓜"。书里给了两条规则,分界线只有一条:这些取值之间有没有"序"(大小顺序)。
| 属性类型 | 处理方式 | 例子 |
|---|---|---|
| 有序 (取值之间有大小) |
连续化:按顺序映射成一串等距的数 | 二值属性"身高":高→1.0,矮→0.0 三值属性"高度":高→1.0,中→0.5,低→0.0 |
| 无序 (取值之间没大小) |
转成 k 维向量(一个 1,其余 0,即 one-hot 编码) | 三值属性"瓜类":西瓜→(0,0,1),南瓜→(0,1,0),黄瓜→(1,0,0) |
因为那等于凭空造出一个不存在的大小关系。如果硬把"西瓜=1、南瓜=2、黄瓜=3",模型就会以为"黄瓜是西瓜的 3 倍"、"南瓜正好在两者中间"。这些关系全是假的,后面算距离、算相关系数时都会被误导。原书在这里留了个指向:"参见 9.3 节"——第 9 章讲聚类时会再吃一次这个亏。
用 (0,0,1)、(0,1,0)、(1,0,0) 就安全了:任意两个之间的距离都一样(都是 √2),谁也不比谁"大"。
先解最简单的情况:只有一个属性
这时候样本就是一个数 xi,模型是一条直线:
那个 ≃ 是"约等于",很关键——它承认了不可能全部拟合准,能接近就行。
第一步:先定义什么叫"差"
要挑最好的 w 和 b,得先能比较"谁更好"。书里用的是均方误差(第 2.2 节讲过,回归任务最常用的性能度量),也就是把所有样本的误差平方加起来:
符号解释:arg min 的意思是"让后面那坨东西取到最小值的那个参数"。所以整句读作:找一组 (w, b),使所有样本的误差平方和最小。那个带星号的 w*、b* 就是最优解。
把这个误差和记作 E(w,b)。基于"最小化均方误差"来求解的方法,就叫最小二乘法(least square method)。
三个原因,从实用到理论:
① 误差有正有负,直接加会互相抵消(+5 和 −5 加起来是 0,看起来完美,其实糟透了)。平方能把负的也变成正的。
② 平方会放大大的误差。差 10 的样本罚 100,差 1 的只罚 1。于是模型会优先去修正那些错得最离谱的点。
③ 平方处处可导,后面求导找最小值才方便。绝对值在 0 点不可导,会添麻烦。
书里还说了一句几何意义:均方误差"对应了常用的欧几里得距离"。这里要稍微抠一下——严格说,它对应的是每个样本到直线的竖直方向距离(也就是残差)的平方和,不是垂直距离。垂直距离要除以 √(1+w²),那会变成另一个问题(叫"正交回归")。做作业和考试时按"竖直距离"理解就不会错。
第二步:为什么可以"求导置零"就完事
因为 E(w,b) 是个凸函数。凸函数你可以想成一个碗:只有一个最低点,没有别的小坑。所以只要找到"斜率为 0"的地方,那一定是全局最低点,不用怕掉进局部最优。
书里给的严格定义是:对区间上任意两点 x1、x2,若
则称 f 为凸函数。人话:取两点的中点,函数在这个中点的高度,不超过"两点连线的中点高度"。碗的形状正是这样——碗底比弦低。
怎么判断?书给了实用判据(一元函数):二阶导数 ≥ 0 就是凸函数;如果二阶导恒大于 0,叫严格凸函数。f(x) = x2 的二阶导是 2 > 0,所以它是典型的凸函数,形状是 U 型。
第三步:动手求导(书里跳过的中间步骤在这里)
书直接给出了两个偏导数。我们把它补全。误差和是:
对 w 求偏导——把 b 当常数,用链式法则(外面平方的导数 × 里面式子的导数):
= −2 ∑i xi(yi − w xi − b)
= −2 ( ∑i xiyi − w ∑i xi2 − b ∑i xi ) 式 (3.5)
对 b 求偏导——把 w 当常数,里面式子的导数是 −1:
= −2 ∑i (yi − w xi − b) 式 (3.6)
令两个偏导都等于 0,得到两个方程。先看第二个(对 b 的那个),除以 −2:
这里用了 ∑i b = m b(b 是常数,加了 m 次)。再把 x̄ = (1/m)∑xi、ȳ = (1/m)∑yi 代进去,就得到一句非常好记的话:
b = ȳ − w x̄ 意味着:回归直线一定穿过样本的重心 (x̄, ȳ)。这是一个常考的小结论,也解释了为什么"移动整体数据"时直线会跟着平移。
现在把 b = ȳ − w x̄ 代回第一个方程,解 w:
⟹ ∑ xiyi − ȳ ∑ xi = w ( ∑ xi2 − x̄ ∑ xi )
⟹ 左边 = ∑ (xi−x̄)(yi−ȳ),右边括号 = ∑ (xi−x̄)2
于是得到闭式解(闭式解 = 能直接用公式算出来,不用一步步迭代试):
分母那坨 ∑xi2 − (1/m)(∑xi)2,其实就是 ∑(xi−x̄)2,也就是 m × x 的方差。
分子 ∑ yi(xi−x̄) 等于 ∑(xi−x̄)(yi−ȳ),也就是 m × x 与 y 的协方差。
所以这个看起来很凶的公式,本质是一句初中就学过的话:w = x 和 y 的协方差 ÷ x 的方差。斜率就是"x 每变动一个单位,y 平均跟着变多少"。
把这句话画出来
动手试试
更一般的情况:有 d 个属性
现在样本是一个向量了,模型变成:
这叫多元线性回归(multivariate linear regression)。思路和一元完全一样,只是记号要升一级。
关键技巧:把 b 藏进 w 里
每处理一次 b 都很啰嗦。有个惯用招式:给每个样本人为补一个恒等于 1 的属性,同时给 w 补上 b 这一项。于是:
验证一下:把最后那个乘 1 的项展开,就是 … + b·1,正好是 b。所以b 就这样被"吸收"进去了,之后不用单独管它。代价是参数从 d 个变成 d+1 个。
把整个数据集写成矩阵 X,大小是 m × (d+1)——每一行是一个样本,前 d 列是 d 个属性,最后一列全是 1:
| 属性 1 | 属性 2 | … | 属性 d | 补的 1 | |
|---|---|---|---|---|---|
| 样本 1 | x11 | x12 | … | x1d | 1 |
| 样本 2 | x21 | x22 | … | x2d | 1 |
| ⋮ | ⋮ | ⋮ | ⋱ | ⋮ | ⋮ |
| 样本 m | xm1 | xm2 | … | xmd | 1 |
再把所有答案摞成一个列向量 y = (y1; y2; …; ym)。于是目标变成:
人话:Xŵ 是所有样本的预测值(一列),y 是真实值(一列),两者相减就是每个样本的误差(一列)。(…)T(…) 就是把这一列误差各自平方再求和——和式 (3.4) 的平方和是一回事,只是写成矩阵形式。
对 ŵ 求导(这一步书里也是直接给结果):
令它为零:XTXŵ = XTy。两边同时左乘 (XTX)-1,得到那个必须记住的公式:
最后把 ŵ* 拆回 w 和 b,模型就学完了:
别把它当咒语背。它的形状很好认:这就是"一元线性回归求斜率"的矩阵版。
回看一元的结论 w = 协方差 ÷ 方差。写成矩阵语言,"方差"对应 XTX(特征之间的散开程度),"协方差"对应 XTy(特征和答案之间的关系)。所以 (XTX)-1 就是在"除掉尺度",XTy 就是在"量关系"。除一下,就是系数。
但是:那个逆矩阵不一定存在
式 (3.12) 里有一步"求逆"。求逆的前提是 XTX 满秩(full-rank)或正定(positive definite)。书上接着就说了:现实任务中它往往不满秩。
什么时候不满秩?两种典型情况:
- 属性比样本多(d+1 > m)。书里的例子很生动:生物信息学的基因芯片数据,动辄上万个属性(基因),但样本只有几十上百个。
- 属性之间有线性关系。比如你同时把"身高(厘米)"和"身高(米)"放进模型——信息完全重复,矩阵必然不满秩。
后果是什么?会解出无穷多组 ŵ,它们让均方误差同样小(都等于最小值)。就像方程 x + y = 1 有无数个解一样,条件不够,定不下来唯一解。
这时候要选哪一个?原书说:"将由学习算法的归纳偏好决定,常见的做法是引入正则化项"。
"归纳偏好"是第 1.4 节的概念,在这里的意思是:面对多个同样好的答案,算法得有自己的偏好才能挑出一个。比如"偏好系数尽量小"或者"偏好稀疏(大部分系数是 0)"。
"正则化"就是把这个偏好写进目标函数里。最常用的一招叫岭回归(第 6、11 章会正式讲),做法是给 XTX 的对角线加一个小正数 λ:
加了这个 λ 之后,矩阵一定可逆了(所以叫"岭")。而且 λ 会让解整体变小、更稳。你不需要现在推它,只要知道"不满秩 → 加个小尾巴让它可逆"这个套路。
最后一步:从"线性回归"到"广义线性模型"
到这里 y = wTx + b 已经能拟合了。但书在这里拐了个弯,问了一个很好的问题:
"可否令模型预测值逼近 y 的衍生物呢?"
举个场景:某个量是指数增长的(人口、复利、细菌繁殖)。它长得像 ex 那种曲线,用一条直线去拟合,怎么都不像。但如果把它取对数,指数就变成了直线。
于是不拟合 y,改拟合 ln y:
这叫对数线性回归(log-linear regression)。它想让 ewTx+b 去逼近 y——注意,是对 e(…) 逼近 y,而不是直线逼近 y。
形式上,式 (3.14) 还是个线性回归(左边是 ln y,右边是 w 的线性组合,参数仍然只以一次方出现)。但实质上,它已经在做"输入空间到输出空间的非线性映射"了:x 变一点,y 的变化量不是常数,而是跟着 y 本身在放大。
书里那个词用得很准:这里的对数函数起到了"将线性回归模型的预测值与真实标记联系起来"的作用。记住这句话,下一句就顺理成章了。
把这个"联系"抽象出来:取一个单调可微的函数 g(·),令
这就叫广义线性模型(generalized linear model),g(·) 叫联系函数(link function)。
- 为什么 g 要单调?因为要能反解回去(单调才有一一对应,才有反函数 g-1),而且要保持"顺序不乱"。
- 为什么要可微?因为要能求导、能用优化算法(下一节全靠这个)。
- 对照一下:对数线性回归就是 g(·) = ln(·) 的特例。因为 ln y = wTx+b 两边取 exp 就是 y = ewTx+b,即 y = g-1(wTx+b)。
式 (3.15) 是整章的枢纽。下一节 3.3 的全部内容,就是"给分类任务挑一个合适的 g"。所以如果只记一句话:3.3 是 3.2 的特例,只不过 g 换成了 sigmoid。
另外书里提了一句"广义线性模型的参数估计常通过加权最小二乘法或极大似然法进行"——这句是伏笔,3.3 用的正是极大似然法。
随堂自测 · 3.2
一元线性回归的闭式解 w = ∑yi(xi−x̄) / [∑xi2 − (1/m)(∑xi)2],它其实等于下面哪个比值?
分母 ∑xi2 − (∑xi)2/m 化简就是 ∑(xi−x̄)2,等于 m·Var(x);分子 ∑yi(xi−x̄) 等于 ∑(xi−x̄)(yi−ȳ),等于 m·Cov(x,y)。两个 m 约掉,就是 Cov/Var。这正是"斜率 = 协方差 ÷ 方差"。
3.3 对数几率回归名字叫回归,干的是分类
上一节解决的是"输出一个实数"。可如果任务是分类呢?比如判断"是不是好瓜",答案是 0 或 1。
原书在这里只用了一句话转折:"答案蕴涵在式 (3.15) 的广义线性模型中:只需找一个单调可微函数将分类任务的真实标记 y 与线性回归模型的预测值联系起来。"
把这句话拆开:线性模型算出来的 z = wTx + b 是个实数(可能是 −7.3,可能是 0.02,也可能是 1.4),而我们要的 y 只能是 0 或 1。所以缺的不是模型,缺的是一个"翻译器"——也就是式 (3.15) 里那个 g-1。
先想想"最理想的翻译器"长什么样
最符合直觉的翻译显然是:z 是正的,就判 1;z 是负的,就判 0。这就是单位阶跃函数(unit-step function,也叫 Heaviside 函数):
0.5, 若 z = 0;
1, 若 z > 0 式 (3.16)
书里的说法:预测值 z 大于零就判为正例,小于零判为反例,恰好等于临界值(0)时可以任意判别。看着很完美,对吧?
但它有个致命问题:不连续。在 z = 0 这一点上,函数值"啪"地一下从 0 跳到 1,中间没有过渡。而广义线性模型要求 g 单调可微——不可导,就没法求梯度,没法用优化算法去训练它。
所以原书说:希望找一个"替代函数"(surrogate function)——能在一定程度上近似阶跃函数,同时单调可微。
对数几率函数:一个"圆滑版"的阶跃
这个替代品就是对数几率函数(logistic function):
它就是深度学习里那个 sigmoid。书里对 sigmoid 的解释是"形似 S 的函数",对数几率函数是其中最重要的代表(第 5 章讲神经网络时会再遇到它)。
动手试试
注意最后两个数永远相等——这就是式 (3.19):模型把"对数几率"直接等于了 z。
它有三个性质,正好对上需求:
- z = 0 时 y = 0.5(和阶跃函数在临界点的 0.5 对上了);
- z → +∞ 时 y → 1;z → −∞ 时 y → 0(两头饱和);
- 在 z = 0 附近变化最陡——"最陡"意味着最敏感,不确定性最大的地方它给出最干脆的判断。
把它当作 g-1 代进式 (3.15):
"对数几率回归"这个名字是怎么来的
把式 (3.18) 变形一下(两边取倒数、移项、再取对数)。这一步书里是"类似于式 (3.14),式 (3.18) 可变化为",我们来真的变一次:
⟹ 1 + e−z = 1/y
⟹ e−z = (1−y)/y
⟹ ez = y/(1−y)
⟹ z = ln( y/(1−y) )
而 z 就是 wTx + b,所以:
接着把三个术语串起来(这三个名字务必分清):
| 名字 | 式子 | 人话 |
|---|---|---|
| 几率(odds) | y / (1−y) | 把 y 看作"样本是正例的可能性",那 1−y 就是反例的可能性。这个比值就是"正例的可能性是反例的几倍"。若 y=0.8,几率就是 4 倍。 |
| 对数几率 (log odds / logit) |
ln[ y/(1−y) ] | 对几率再取对数。取对数是为了把 (0, +∞) 这个区间"拉直"成整个实数轴,这样才能用 wTx+b 去拟合它。 |
| 对数几率回归 (logistic regression) |
式 (3.18) | 因为式 (3.18) 实际上是在用线性回归的预测结果去逼近真实标记的对数几率,所以得名。 |
① 它不是"回归"。虽然名字里有回归,但它是一种分类学习方法。原书特意强调了这一点。别被名字骗了。
② "对数几率函数" ≠ "对数函数"。书里专门加了个注:注意对数几率函数与 ln(·) 不同。前者是 1/(1+e−z) 这个 S 形曲线,后者是我们熟悉的 ln。名字像,东西完全两回事。
另外书里解释了这个译名的来历:有文献译作"逻辑回归",但中文"逻辑"和 logistic / logit 的本义相去甚远,所以本书意译为"对数几率回归",简称对率回归。
为什么它好用:书里给了三条
- 直接对分类可能性建模,无需事先假设数据分布。对比一下:朴素贝叶斯(第 7 章)要先假设"数据服从某种分布",如果假设错了,模型就跟着错。对率回归不假设分布,所以"避免了假设分布不准确所带来的问题"。
- 得到的是近似概率预测,不是硬邦邦的类别。它输出 0.87,你就知道"这个样本有 87% 的可能是好瓜"。这对很多需要概率辅助决策的任务很有用——比如医疗诊断里,0.51 和 0.99 虽然都判"有病",但处置方式完全不同。
- 目标函数是任意阶可导的凸函数。数学性质好,现有的数值优化算法都能直接用。
怎么把 w 和 b 求出来:极大似然法
先把式 (3.18) 里的 y 明确成"类后验概率估计",也就是 p(y=1 | x)——"在已知 x 的条件下,它是正例的概率"。于是式 (3.19) 可以重写为:
由于两者加起来必须等于 1,解一下就能分别写出它们:
注意这两个式子和 (3.18) 长得几乎一样,只是分母的指数上少了个负号——因为 (3.18) 那个式子是给"y=1"用的,(3.24) 是 1 减掉它。
想象你在猜一枚硬币的重量分布。你已经掷了 10 次,结果是 7 正 3 反。极大似然法的思路是:既然这事已经发生了,那就找一个参数,让"发生这件事"的概率最大。
放到这里:训练集里每个样本的真实标签 yi 是我们已经看到的既成事实。我们要调的 w、b,应该让"看到这批标签"这件事的概率最大。
所以目标就是最大化"每个样本属于它真实类别的概率"的连乘(独立事件同时发生的概率 = 各自概率相乘)。取对数把连乘变成连加(好算,且不改变最大值的位置),就得到下式。
书里一句话点明:"即令每个样本属于其真实标记的概率越大越好。" 这就是式 (3.25) 的全部含义。
一个式子同时写两种情况的技巧
麻烦在于:yi 有时候是 1,有时候是 0,难道要分两种情况写?书里用一个很巧的办法绕过去了。先做记号简化:
又是"把 b 吸收进去"那一招(和 3.2 一模一样)。再令 p1(x̂; β) 表示判为正例的概率,p0 = 1 − p1 表示反例的概率。于是:
当 yi = 1 时:第二项被 (1−1)=0 干掉,只剩 1 · p1 = p1。✓
当 yi = 0 时:第一项被 0 干掉,只剩 1 · p0 = p0。✓
所以这一个式子自动"选"了正确的那一项。为什么要这么写?因为后面求导时,一个有统一表达式的函数才好求导;分情况讨论就得写两遍,非常难看。这个技巧在机器学习里到处都是,值得记住。
化简成最终形状(书里跳了,这里补上)
把式 (3.26) 代入式 (3.25),用式 (3.23)(3.24) 展开。分两种情况看 ln p 长什么样:
若 yi = 0: ln p0 = ln 11 + eβTx̂ = 0 − ln(1 + eβTx̂i)
看这两行——它们的区别只在于前面那一项在 yi=1 时是 βTx̂i,在 yi=0 时是 0。那正好就是 yi · βTx̂i。所以两种情况可以合并成一行:
最大化这个和,等于最小化它的相反数(加个负号、翻个个儿)。于是得到:
式 (3.27) 就是深度学习里天天出现的 binary cross-entropy(二元交叉熵),也叫 log loss(对数损失)。很多人学到这里会有"啊,原来就是它"的感觉。
所以以后看到 PyTorch 里的 BCEWithLogitsLoss,可以放心地理解成:这就是西瓜书的式 (3.27)。
怎么解:没有闭式解,只能迭代
注意一个本质区别:3.2 的线性回归有闭式解(代公式一步算出),而式 (3.27) 因为那个 ln(1+e(·)) 的存在,没法直接解出 β。只能一步步逼近。
好消息是它是"关于 β 的高阶可导连续凸函数",所以梯度下降法、牛顿法这些经典数值优化算法都能用。原书以牛顿法为例:
这个公式别硬背,看它的结构:新位置 = 老位置 − (曲率)-1 × (斜率)。
梯度下降是"跟着坡度往低处挪一小步"。牛顿法更聪明:它在当前点用一个二次函数(一个碗)去近似目标函数,然后一步跳到这个碗的碗底。因为用了二阶信息(曲率),所以步子更准、收敛更快。
代价是每轮都要算二阶导矩阵(Hessian)并求逆,计算量大。样本多、维度高时就不划算了。
书里给了一阶导和二阶导:
一阶导那个式子有个特别漂亮的读法:"误差 × 特征"。括号里 yi − p1 就是"真实标签减去预测概率",也就是这一条样本的误差;乘以 x̂i 就是"这条样本的特征"。所有样本的"误差×特征"加起来,就是梯度。误差大的样本对参数的拉动更大——这和直觉完全一致。
二阶导那个式子也值得看一眼:因为 p1(1−p1) 永远 ≥ 0(概率在 0 和 1 之间),所以这个 Hessian 是正定的。正定意味着什么?意味着这个碗是"开口朝上"的——确实是个凸问题,牛顿法不会跑偏。
书里有一道习题:"试证明,对于参数 w,对率回归的目标函数 (3.18) 是非凸的,但其对数似然函数 (3.27) 是凸的。"
这句话的意思要拎清:式 (3.18) 那个 y = 1/(1+e−(wTx+b)) 本身,作为 w 的函数是"非凸"的(它是 S 形,有上有下)。但我们真正优化的不是它,而是式 (3.27) 那个对数似然——后者是凸的。
所以"对率回归是凸优化问题"这句话,必须限定在"用对数似然做目标"这个前提下。说漏了就不严谨。
随堂自测 · 3.3
为什么不能直接把单位阶跃函数当作广义线性模型里的 g-1?
式 (3.15) 要求 g 单调可微。阶跃函数在 z=0 处直接跳变,导数不存在,因此无法求梯度、无法优化。输出只有 0 和 1 恰恰是它的优点(正是我们要的),不是问题;而且它在 0 点附近不是"平缓",是"剧烈跳变"。
3.4 线性判别分析换一条完全不同的路
3.3 的思路是"算概率"。3.4 的思路完全不同:不去算概率,而是去找一个"最好的观察角度"。
线性判别分析(Linear Discriminant Analysis,LDA)在二分类问题上最早由 Fisher 在 1936 年提出,所以也叫"Fisher 判别分析"。
原书说它的思想"非常朴素",确实朴素,但要想透需要一点空间想象力:
把高维的数据"投影"到一条直线上,投影完之后,同类的点挤在一起,异类的点离得很远。然后在这条直线上切一刀就分开了。
为什么叫"投影"?想象你手里有一团三维的点(比如橘子和苹果混在一起)。你从某个角度看过去,它们在墙上留下影子。换个角度看,影子可能重叠在一起分不清;再换个角度,影子可能恰好分成两堆。LDA 要找的就是"分得最开的那一个角度"。
动手试试 · 这是本节的核心
这里有个特别值得注意的细节:滑块默认停在 −37°,那正好是两类中心连线的方向,此时 J ≈ 74。但最佳方向在 −47° 附近,J ≈ 240——差了整整 10 度,效果却好了三倍多。这 10 度的偏差,就是 Sw−1 干的活:因为两簇是沿"右上"方向被拉长的,直接沿中心连线看会被拉长的部分糊住,得先把那个方向压扁一点才行。
把"投影"写成数学
先摆记号。数据 D = {(xi, yi)},yi ∈ {0, 1}。对第 i 类(i 取 0 或 1),记:
| 符号 | 含义 |
|---|---|
| Xi | 第 i 类所有样本组成的集合 |
| μi | 第 i 类的均值向量(该类所有样本的中心点) |
| Σi | 第 i 类的协方差矩阵(描述该类样本散得多开、朝哪个方向散) |
现在把点投影到方向 w 上。投影的数学操作就是点积(内积)。所以:
- 类 0 的中心投影后,变成一个数:wTμ0;
- 类 1 的中心投影后,变成 wTμ1;
- 类 0 的"散开程度"投影后,变成 wTΣ0w;
- 类 1 的"散开程度"投影后,变成 wTΣ1w。
书上有一句容易被匆匆扫过的话:"由于直线是一维空间,因此 wTμ0、wTμ1、wTΣ0w 和 wTΣ1w 均为实数。"
这句话看着像废话,其实是提醒你别在后面的式子推导中把标量当向量处理。二维向量点积一个方向向量,结果是一个数(在直线上的坐标);矩阵夹在两个 w 中间变成二次型,结果也是一个数(散开的程度)。
把两个愿望写成一个分数
LDA 有两个"愿望",恰好是一对矛盾:
| 愿望 | 数学表达 | 想让它 |
|---|---|---|
| 同类要挤 | wTΣ0w + wTΣ1w | 尽可能小 |
| 异类要分 | ‖wTμ0 − wTμ1‖22 | 尽可能大 |
一个要小一个要大,怎么同时优化?让它们相除:分子要大、分母要小,整体就大。于是得到目标函数:
关键在一个很小但很容易卡住的地方:‖a‖22 的意思是"向量 a 的模长平方",而模长平方等于自己点乘自己:‖a‖2 = aTa。
这里的 a 是 wTμ0 − wTμ1,它是个标量(一个数),而标量等于它自己的转置。所以可以写成向量差的形式:a = (μ0−μ1)Tw。
那么 a2 = a·a = [(μ0−μ1)Tw]T · [(μ0−μ1)Tw] = wT(μ0−μ1)(μ0−μ1)Tw。
中间那个 (μ0−μ1)(μ0−μ1)T——一个列向量乘一个行向量——是个矩阵。记住这个形状,下面马上要用。
两个"散度矩阵"
为了写得清爽,书里定义了两个矩阵。它们是这一节的主角,一定要认识:
Sw = Σ0 + Σ1 = ∑x∈X0 (x−μ0)(x−μ0)T + ∑x∈X1 (x−μ1)(x−μ1)T 式 (3.33)
Sb = (μ0 − μ1)(μ0 − μ1)T 式 (3.34)
用人话说这两个东西:
- Sw(类内):把每一类的样本都减去自己类的中心,然后"自己乘自己的转置"再加起来。它在量的是"每一类自己有多散"。
顺便说一句:如果你把 ∑(x−μ)(x−μ)T 除以样本数 m,得到的就是协方差矩阵。所以散度矩阵和协方差矩阵只差一个常数倍。 - Sb(类间):只用两个类的中心,量的是"两个类的中心离多远"。注意它是一个"秩为 1"的矩阵——因为它只是同一个方向向量的外积。
于是式 (3.32) 可以重写成非常漂亮的样子:
这个形式的学名叫广义瑞利商(generalized Rayleigh quotient)。名字很唬人,但你只要记住它是"类间 ÷ 类内"就行,越大越好。
怎么解这个最大化问题
先注意一个性质,书里用了一个脚注来提:"若 w 是一个解,则对于任意常数 α,αw 也是式 (3.35) 的解。"
分子分母里 w 都出现两次。你把 w 换成 αw,分子变成 α2wTSbw,分母变成 α2wTSww,α2 上下正好约掉,比值完全不变。
这个性质非常重要,它告诉我们:我们只能定出 w 的"方向",定不出它的"长度"。方向才是关键——因为投影方向才决定分不分得开,长度只是缩放。
而且它带来了一个解题的自由:既然长度随便,那就人为规定一个长度,把问题变简单。
书里的做法是:"不失一般性,令 wTSww = 1"。这样分母就固定成 1 了,最大化 J 就等价于最大化分子,而最大化分子又等价于最小化负的分子:
s.t. wTSww = 1 式 (3.36)
这是一个"带约束的优化",标准解法是拉格朗日乘子法(附录 B.1)。做法是:把约束乘上一个系数 λ 加进目标里,变成无约束问题,再求导置零。
这个式子的形式叫"广义特征值问题"。如果 Sw 是单位矩阵,它就退化成普通的求特征值 Sbw = λw。
最后一步:从 λ 到 Sw-1(μ0−μ1)
这一步是全书最像"变魔术"的地方,但其实只用了两个观察。
观察一:Sbw 的方向是固定的。把 Sb = (μ0−μ1)(μ0−μ1)T 代进去:
下面画线的那一坨 (μ0−μ1)Tw 是一个数(两个向量点乘)。一个数乘上一个向量,结果还是那个方向的向量。所以 Sbw 的方向永远等于 (μ0−μ1) 的方向,不管 w 取什么。书里那句"注意到 Sbw 的方向恒为 μ0−μ1",就是这个意思。
观察二:既然方向一样,就可以把那个标量整体记作 λ。于是令:
把这个代回式 (3.37):
两边约掉 λ,再左乘 Sw−1:
⟹ w = Sw−1(μ0 − μ1) 式 (3.39)
别把它当成一个"天降公式"。它的含义是:先乘 Sw-1,再沿着两类中心的连线方向投影。
为什么不能直接沿着"中心连线"投影?因为两类的散开方向可能不一样。假设橙色那类在水平方向散得很长,绿色那类在竖直方向散得很长——如果你直接沿中心连线看过去,两个类的影子会糊在一起。
乘上 Sw−1 做的事情,叫"白化":它先把数据按类内散开的形状压扁、拉长,让两个类都变成差不多圆的球。球化之后再沿中心连线投影,就不会互相侵入了。
一句话记法:Sw-1 负责"把散开的方向压平",(μ0−μ1) 负责"指出该往哪边看"。
理论上你只要算 Sw−1 就行。但实际计算时,直接求逆数值上不稳定(尤其当 Sw 接近奇异时,一点点误差就会被放大成巨大的偏差)。
所以书里说:"考虑到数值解的稳定性,在实践中通常是对 Sw 进行奇异值分解"。做法是 Sw = UΣVT(Σ 是对角矩阵,对角线上是奇异值),然后 Sw−1 = VΣ−1UT。
你不需要现在会推 SVD(附录 A.3 有),只要知道:这是"绕过直接求逆"的标准手段。用 numpy 的话就是 np.linalg.pinv(伪逆),它内部就是干这个的。
书里提了一句:"LDA 可从贝叶斯决策理论的角度来阐释,并可证明,当两类数据同先验、满足高斯分布且协方差相等时,LDA 可达到最优分类。"
这句话是在说 LDA 的"适用条件":两类先验概率差不多、各自是正态分布、并且两个正态分布的"胖瘦形状"一样(协方差相等)。满足这三条时,LDA 就是最优的。反过来说,如果两类的协方差差得很多(一个很扁一个很圆),LDA 就不是最优了——它仍在假设它们一样。
(书里还有个注:严格说 LDA 与 Fisher 判别分析稍有不同,前者假设了各类样本的协方差矩阵相同且满秩。)
推广到多分类
二分类时是把数据投到一条线上。多分类时,一条线不够了——N 个类挤在一条线上会重叠。所以要投到一个低维空间里去。思路完全一样,只是"数"变成了"矩阵"。
先定义全局散度矩阵(所有样本相对于总均值的散度):
其中 μ 是所有样本的均值向量(不分类别)。这个等式 St = Sb + Sw 很有意思:总散度 = 类间散度 + 类内散度。就像"总方差 = 组间方差 + 组内方差",是很自然的一个分解。
然后把类内散度矩阵重新定义成"每一类的散度加起来":
再借 St = Sb + Sw 反推出类间散度矩阵:
(mi 是第 i 类的样本数。为什么这里要乘 mi?因为类间散度要把每类的"中心偏移"按该类的样本数量加权——大类的影响应该更大。这个 mi 是推出来的,书里跳了中间步骤。)
书里说:"显然,多分类 LDA 可以有多种实现方法:使用 Sb、Sw、St 三者中的任何两个即可。"理由是它们满足 St = Sb + Sw,知道两个就能算出第三个。
常见的一种实现是:
符号:W ∈ ℝd×(N−1) 是投影矩阵(把 d 维投到 N−1 维),tr(·) 是迹——矩阵对角线元素之和。为什么用迹?因为二分类时分子分母都是标量,直接除就行;多分类时它们是矩阵,得想办法变成一个数来比较。迹正好是"所有特征值之和",可以理解为"总能量",是标量除法的自然推广。
解法还是广义特征值问题:
而 W 的闭式解是:Sw−1Sb 的 N−1 个最大广义特征值所对应的特征向量组成的矩阵。
这个 N−1 经常被当成要背的结论,其实理由很直观:N 个类的"中心"是 N 个点,而 N 个点最多只能张成一个 N−1 维的空间。
类比一下:平面上 3 个点(3 个类中心),最多构成一个二维三角形——你不可能用 3 个点撑出三维空间。所以再多投几个维度也没有新信息了。这也说明 LDA 能保留的判别信息上限是 N−1 维。
最后,原书点出了 LDA 的第二个身份:"若将 W 视为一个投影矩阵,则多分类 LDA 将样本投影到 N−1 维空间,N−1 通常远小于数据原有的属性数。于是,可通过这个投影来减小样本点的维数,且投影过程中使用了类别信息,因此 LDA 也常被视为一种经典的监督降维技术。"
LDA 有两个身份:分类器(投到一维/低维之后切一刀)和降维方法(把 d 维压到 N−1 维)。
它和 PCA(第 10 章)的区别,一句话就能说清:PCA 是无监督的(只看数据散得最开的方向,不管标签),LDA 是监督的(它知道类别,专门找"分得开"的方向)。所以同样做降维,用于分类任务时 LDA 通常更好。
随堂自测 · 3.4
LDA 的解 w = Sw−1(μ0−μ1) 里,乘上 Sw−1 这一步起的作用是?
压缩维度是投影本身干的事,不是 Sw−1 干的。"让类间距离最大"只描述了目标,没说明为什么会出现 Sw−1。而"两类投影后均值相等"恰恰是灾难——那就完全分不开了。Sw−1 的真实作用是白化:先按类内散开的形状把数据压扁拉长,让两类变成球形,再沿 (μ0−μ1) 方向投影。
3.5 多分类学习只会二分类怎么办
前面两节(3.3、3.4)解决的都是二分类:只有"是/不是"两种答案。但现实中经常是 N 类:这个瓜是西瓜、南瓜、冬瓜还是黄瓜?
书里说得很实在:"有些二分类学习方法可直接推广到多分类,但在更多情形下,我们是基于一些基本策略,利用二分类学习器来解决多分类问题。"
这一节讲的就是这些"基本策略"。核心思路只有两个字:拆解。
① 拆:把多分类任务拆成若干个二分类任务;
② 训:为拆出来的每一个二分类任务,训练一个分类器;
③ 合:测试时,把这些分类器的预测结果集成起来,得到最终答案。
所以这一节真正要回答的只有两个问题:怎么拆?怎么合? 不同的拆法和合法,就是不同的策略。
策略一:OvO(一对一)
One vs. One,读作"一对其一"。数据集是 D = {(x1,y1), …},yi ∈ {C1, C2, …, CN}。
怎么拆:把 N 个类别两两配对。每配一对,就造一个二分类任务——"是 Ci 还是 Cj?",其中 Ci 的样例当正例,Cj 的样例当反例。
一共多少个?从 N 个里挑 2 个的组合数:
比如 4 个类,就是 4×3/2 = 6 个分类器:{C₁C₂}、{C₁C₃}、{C₁C₄}、{C₂C₃}、{C₂C₄}、{C₃C₄}。
怎么合:测试时,新样本同时喂给所有分类器,得到 N(N−1)/2 个预测。然后投票——被预测得最多的类别就是最终结果。
4 个类、6 个分类器。假设某个测试样本的结果是:{C₁C₂}→C₁,{C₁C₃}→C₁,{C₁C₄}→C₁,{C₂C₃}→C₃,{C₂C₄}→C₂,{C₃C₄}→C₃。
数票:C₁ 得 3 票,C₂ 得 1 票,C₃ 得 2 票,C₄ 得 0 票。所以判为 C₁。
一个有意思的细节:每个类被投票的次数上限是 N−1(因为它只出现在 N−1 对里)。所以 C₁ 得 3 票就是满分——全票通过。
策略二:OvR(一对其余)
One vs. Rest,读作"一对其余"(书里也提了另一个名字 OvA = One vs. All,但原书说 OvA 这个说法不严格,因为不可能把"所有类"都当反类)。
怎么拆:每次把一个类的样例当正例,其余所有类的样例当反例。这样每个类造一个任务,共 N 个分类器。
怎么合:书里分了两种情况:
- 只有一个分类器预测为正 → 那个类就是答案。
- 有多个分类器都预测为正 → "通常考虑各分类器的预测置信度,选择置信度最大的类别标记作为分类结果"。
因为每个分类器都是独立训练的,各自给了"是"的答案,没有互相协商。比如"苹果"和"番茄"都可能被判为"圆的红色物体",两个分类器都说"是"。
这时就只能比"谁更确定"。置信度就是概率输出的高低——对率回归天然就有这个(3.3 节那个 p1),所以用它做 OvR 很自然。
OvO 和 OvR 到底怎么选
书里这一段信息密度很高,几乎每一句都是考点,我们拆成表格看:
| 维度 | OvO | OvR | 结论 |
|---|---|---|---|
| 分类器数量 | N(N−1)/2 | N | OvR 少得多 |
| 每个分类器用的样本 | 只用到两个类的样例 | 用到全部样例 | — |
| 训练时间 | 类别很多时反而更小 | 类别很多时更大 | OvO 占优(N 大时) |
| 存储开销 | 更大(模型多) | 更小 | OvR 占优 |
| 测试时间 | 更大(要跑 N(N−1)/2 个模型) | 更小 | OvR 占优 |
| 预测性能 | 取决于具体的数据分布,多数情形下两者差不多 | 打平 | |
这个表格里有个反直觉的地方,值得停下来想一下:分类器多的 OvO,训练时间居然可能更小?
原因就在"每个分类器用多少样本"这一行。OvO 的每个分类器只看两类样本(比如 m=10000 个样本、10 个类,每个分类器可能只看 2000 个),而 OvR 的每个分类器要看全部 10000 个。虽然 OvO 要训 45 个模型,但每个都小得多。类别越多、每类样本越少,这个差距越明显。
类别很多、训练时间吃紧 → OvO;模型要小、预测要快(比如线上服务)→ OvR。
再用 sklearn 的话:OneVsOneClassifier 和 OneVsRestClassifier 就是这两个,多分类器默认的行为在文档里写得很清楚。
策略三:MvM(多对多)
Many vs. Many:每次把若干个类当正类、另外若干个类当反类。
显然 OvO 和 OvR 都是 MvM 的特例(OvO 是"1 个对 1 个",OvR 是"1 个对其余")。
但书里紧接着说了一句关键的限定:"MvM 的正、反类构造必须有特殊的设计,不能随意选取。"为什么不能随便?
因为如果随便挑,你可能造出一堆"几乎没法分"的二分类问题。比如把"西瓜、南瓜"当正类、"黄瓜、冬瓜"当反类——如果这些类的样本在特征空间里混在一起,这个二分类器根本学不好,白白浪费时间。
更重要的原因藏在后面:不同的划分方式,导致二分类问题本身的难度不同。所以划分得好不好,直接决定最终模型好不好。这也是下面 ECOC 那两段的伏笔。
ECOC:把"纠错码"的思想搬过来
纠错输出码(Error Correcting Output Codes,ECOC)是最常用的 MvM 技术,由 Dietterich 和 Bakiri 在 1995 年提出。
书里一句话点明了它的野心:"将编码的思想引入类别拆分,并尽可能在解码过程中具有容错性。"
它的工作过程分两步,这两步一定要分清:
第一步:编码(训练阶段)
对 N 个类别做 M 次划分。每次划分把一部分类划为正类、一部分划为反类,形成一个二分类训练集。M 次划分 → M 个训练集 → 训练出 M 个分类器。
这些划分用一个编码矩阵记录下来:每一行是一个类别,每一列是一个分类器。格子里的值表示"这个分类器把这个类当正例还是反例"。
| 类别 | f1 | f2 | f3 | f4 | f5 |
|---|---|---|---|---|---|
| C1 | −1 | +1 | −1 | +1 | −1 |
| C2 | +1 | −1 | −1 | +1 | +1 |
| C3 | −1 | +1 | +1 | −1 | +1 |
| C4 | +1 | −1 | +1 | −1 | −1 |
读法:"+1" 表示该分类器把这个类的样本当正例,"−1" 表示当反例。
按这张表,f2 那一列是 (+1, −1, +1, −1),意思是:分类器 f₂ 把 C₁ 和 C₃ 的样例作为正例,C₂ 和 C₄ 的样例作为反例——这正是原书图 3.5(a) 里的描述。每一行则是一个类别的"身份证":C₃ 的编码是 (−1, +1, +1, −1, +1)。
编码矩阵有两种常见形式:
| 类型 | 每个格子的取值 | 特点 |
|---|---|---|
| 二元码 (Dietterich & Bakiri, 1995) |
只有 +1 / −1 | 每个分类器都必须对每一类给出明确的正/反判断 |
| 三元码 (Allwein et al., 2000) |
+1 / −1 / 0 | 0 表示"停用类"——这个分类器不使用该类的样本,既不参与训练也不参与判断。更灵活 |
书里举了三元码的例子:"在图 3.5(b) 中,分类器 f₄ 将 C₁ 类和 C₄ 类的样例作为正例,C₃ 类的样例作为反例"——剩下的 C₂ 就是被"停用"了(矩阵里对应位置是 0)。
第二步:解码(测试阶段)
M 个分类器各自对测试样本给出一个预测(是/否),把这些预测按顺序排成一个长度 M 的编码。然后拿这个"预测编码"和每个类别自己的编码逐一比较距离,距离最小的那个类别就是答案。
距离怎么算?常用欧氏距离或海明距离(数不同位置的个数)。书里的例子说"若基于欧氏距离,预测结果将是 C₃"。
为什么叫"纠错"?一个具体例子
这是本节最精彩的部分,一定要跟着算一遍。
假设测试样本真实的类别是 C3,那它的正确编码应该是 (−1, +1, +1, −1, +1)(看表里 C₃ 那一行)。
现在假设分类器 f2 判错了——它本该输出 +1,却输出了 −1。于是我们拿到的编码变成:
| f1 | f2 | f3 | f4 | f5 | 与预测码的距离 | |
|---|---|---|---|---|---|---|
| 预测得到的编码 | −1 | −1 | +1 | −1 | +1 | — |
| C1 | −1 | +1 | −1 | +1 | −1 | 4 |
| C2 | +1 | −1 | −1 | +1 | +1 | 3 |
| C3 | −1 | +1 | +1 | −1 | +1 | 1 ← 最小 |
| C4 | +1 | −1 | +1 | −1 | −1 | 2 |
距离为 1(只有 f₂ 那一位不同),是所有类别里最小的——所以最终仍然判为 C₃,正确答案。
一个分类器错了,但它的错被其他分类器"投票"抵消了。因为每个类的编码都足够"独特",一位出错不足以把它认成别的类。
这跟通信里的纠错码是一个道理:发一串 1011,路上翻了 1 位变成 1001,接收方还是能靠"离哪个合法码最近"还原出 1011。ECOC 就是把这个思路搬到了分类上。
码长 M 的取舍
书里给了两条权衡,方向相反:
- M 越大,纠错能力越强。因为编码之间的差异位更多,能容忍更多分类器同时出错。
- 但 M 越大,代价也越大。"编码越长,意味着所需训练的分类器越多,计算、存储开销都会增大。"
还有一条更微妙的限制:"对有限类别数,可能的组合数目是有限的,码长超过一定范围后就失去了意义。"
因为每一列(每个分类器)只能把类别分成"正类组"和"反类组"。N 个类别,可能的划分方式是有限的。如果把同一列重复写两遍,那多出来的那个分类器提供不了任何新信息——码变长了,但"有效信息"没增加。
所以码长不是越长越好,而是有个上限。
两个容易忽略的深层提醒
"对同等长度的编码,理论上来说,任意两个类别之间的编码距离越远,则纠错能力越强。因此,在码长较小时可根据这个原则计算出理论最优编码。"
但——"码长稍大一些就难以有效地确定最优编码,事实上这是 NP 难问题"。
好消息是:"通常我们并不需获得理论最优编码,因为非最优编码在实践中往往已能产生足够好的分类器。"
这一段原书写得很克制,但意思很锋利:"并不是编码的理论性质越好,分类性能就越好,因为机器学习问题涉及很多因素。"
具体来说:把多个类拆成两个"类别子集",不同拆解方式所形成的两个类别子集的区分难度往往不同。于是可能出现这种情况——
编码 A:理论纠错性质很好,但它造出来的二分类问题都很难;
编码 B:理论纠错性质差一些,但它造出来的二分类问题都很简单。
"最终产生的模型性能孰强孰弱很难说。"
这背后是机器学习里一个反复出现的道理:理论上的好性质,只有在问题本身"难度相同"的前提下才等价于实际的好效果。
随堂自测 · 3.5
在 ECOC 的三元码编码矩阵中,格子里的 "0" 表示什么?
原书说得很明确:三元码中"0"表示"停用类",即 fi 不使用该类的样本。三元码的灵活性正来自这里——允许某些分类器"弃权",而不是强迫它在每一类上都站队。这也解释了为什么书上举例 f₄ 时只说了正例和反例,剩下的 C₂ 就是被停用的。
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 凸)。