160 个训练点、800 个测试点,二维两团高斯。 牛顿法 11 步收敛,测试准确率 95.12%,对数损失 0.1188。 概率范围 3.25e-09 到 1.000000——它是真算出来的,不是凑的。 校准五桶里预测 0.5343 的那一桶实际正类 0.5417,预测 0.9819 的那一桶实际 0.9700。 同一批 12 个错标点:Logistic 掉 0.62 个点,最小二乘掉 1.88 个点, 但两者的终点都是 93.75%——比较时必须连起点一起看。
第 7、8、9 章的分类器输出的全是正负一。判类只看符号,符号之外的信息一概丢掉。 可很多场合要的不是「哪一类」,是「有多确信」——比如「这个像素有 0.02 的概率是猫」, 后面才好做取舍。 这一章把输出从正负一换成一个 0 到 1 之间的数,损失也跟着换。 换完会发现两件事:一是训练可以快到 11 步就收, 二是它给出的概率真的能当频率用——这个性质叫校准。
一、从硬标签到概率
到第 9 章为止,所有分类器交出来的都是正负一。 第 7 章的最小二乘分类算出一个实数 f(x),看符号判类;f 是 0.3 还是 0.9,判类结果一模一样。 第 8 章的 SVM 输出「到边界的距离乘符号」,这个数有相对意义,但它可以是 2.7 也可以是 15.3, 没有上界,也不能当概率。 要把一个没有上界的实数压进 0 到 1,标准做法是过一层 sigmoid:
p = 1 / (1 + exp(-z)) # z = w 点乘 x + b
它的形状是单调上升的 S 形,z 等于 0 时 p 等于 0.5, 并且满足 p 关于 0.5 中心对称(p 的负 z 等于 1 减 p)。 两端很快就饱和:
| z | 负 4 | 负 2 | 0 | 2 | 4 |
|---|---|---|---|---|---|
| p | 0.01799 | 0.11920 | 0.50000 | 0.88080 | 0.98201 |
| 注意一件事:模型的形状一点没变。 | |||||
| 决策边界还是那条直线——p 等于 0.5 等价于 z 等于 0,还是 w 点乘 x 加 b 等于 0。 | |||||
| 变的只是「怎么解释这个数」:以前它是一个判别分数,现在它是一个概率。 | |||||
| 实测的系数是 w 等于 (3.2909, 3.4055)、b 等于 负 0.3783。 | |||||
| 两个系数几乎相等,因为两团点的中心在 (负 1.1, 负 1.1) 和 (1.1, 1.1) 上, | |||||
| 分界线正好垂直于那条对角线。 | |||||
| b 是负的,说明分界线从原点往正方向挪了一点点——这是两团点抽样不均匀造成的。 | |||||
| 概率范围那一行值得单独看:最小 3.25e-09,最大 0.999999 以上。 | |||||
| sigmoid 在两端是饱和的,输入差 15 以上就压到 1e-7 以下。 | |||||
| 所以「几乎肯定不是」这种判断它是真算出来的,不是把 0 硬塞进去。 |
二、损失换成对数似然
输出变成概率,损失就该跟着改:不再问「和正负一差多远」,而是问「给正确类留了多少概率」。 正确的写法是负的 log P(正确类)。 y 等于正一时正确概率是 p,y 等于负一时是 1 减 p,两者可以合成一个式子:
P(正确) = sigmoid(y * f) # y 是正负一,f 是线性输出
损失 = -log P(正确) = log(1 + exp(-y * f))
这个式子叫 logistic 损失,也叫二分类的交叉熵。 它和第 8 章的 hinge 损失是邻居——两者都在惩罚「间隔不够大」,区别在尾巴:
| 间隔 y 乘 f | hinge 损失 | logistic 损失 |
|---|---|---|
| 负 2 | 3.000 | 2.127 |
| 负 1 | 2.000 | 1.313 |
| 0 | 1.000 | 0.693 |
| +1 | 0.000 | 0.313 |
| +2 | 0.000 | 0.127 |
| hinge 在间隔到 1 之后精确变成 0,一分钱不罚;logistic 永远大于 0,只是很快变得很小。 | ||
| 这个差别在实践里意味着:hinge 只关心「够不够」,logistic 还关心「多够」。 | ||
| 所以 SVM 的支持向量只有边界上那几个, | ||
| 而 Logistic 回归每个点都在出力——只是远处的点出的力小到可以忽略。 | ||
| 再和平方损失比,差别更大。 | ||
| 平方损失是「让输出贴近正负一」,那是把分类当回归做;它罚的是(1 减 间隔)的平方。 | ||
| 间隔从负 1 走到负 6,平方损失从 4.000 涨到 49.000(涨 12 倍), | ||
| 交叉熵只从 1.313 涨到 6.002(涨 4.6 倍)。 | ||
| 一个二次增长,一个近似线性。 | ||
| 这句话的实际后果很硬:一个错得离谱的点,在平方损失下会拿到不成比例的话语权。 | ||
| 它会拽着整条边界往自己那边跑,就为了把自己那 49 的罚金降下来一点。 | ||
| 第 7 章那个「12 个错标点把准确率打掉 1.8 个点」的现象,根子就在这张表上。 |
三、牛顿法:11 步到机器精度
交叉熵是凸的、二阶连续可导,所以可以直接上牛顿法:每步解一个方程 H 乘 delta 等于 g, 然后 b 减去 delta。 梯度 g 是「设计矩阵转置 乘(预测概率减标签)」, Hessian H 是「设计矩阵转置 乘 权重 乘 设计矩阵」,权重是 p 乘 (1 减 p)。 这个 H 恰好就是加权最小二乘的法方程矩阵。 所以这套算法有个别名:迭代重加权最小二乘(IRLS)。 每一轮只是把权重换一遍,再解一次最小二乘——没有学习率要调。
| 轮次 | 平均对数似然 | 梯度最大值 | 步长最大值 |
|---|---|---|---|
| 1 | 负 0.25680 | 9.62e+01 | 7.34e-01 |
| 2 | 负 0.16146 | 2.63e+01 | 5.13e-01 |
| 3 | 负 0.12019 | 9.94e+00 | 5.33e-01 |
| 4 | 负 0.10231 | 3.84e+00 | 6.08e-01 |
| 5 | 负 0.09603 | 1.45e+00 | 6.06e-01 |
| 6 | 负 0.09477 | 4.66e-01 | 4.20e-01 |
| 7 | 负 0.09469 | 9.42e-02 | 1.34e-01 |
| 8 | 负 0.09469 | 5.98e-03 | 9.90e-03 |
| 9 | 负 0.09469 | 2.86e-05 | 4.83e-05 |
| 10 | 负 0.09469 | 6.72e-10 | 1.14e-09 |
| 11 | 负 0.09469 | 1.37e-15 | 1.39e-15 |
| 对数似然从 负 0.25680 单调涨到 负 0.09469,第 11 步步长掉到 1e-12 以下,停。 | |||
| 但真正该看的是「有效位数」:前 6 步就把似然从 负 0.25680 抬到 负 0.09477, | |||
| 后面 5 步只多走了 0.00008。 | |||
| 换句话说,90% 的进展在头 6 步里就完成了。 | |||
| 梯度那一列更能说明问题:9.62e+01、2.63e+01、9.94e+00、3.84e+00、1.45e+00、4.66e-01、 | |||
| 9.42e-02、5.98e-03、2.86e-05、6.72e-10。 | |||
| 后半段每步不只是变小,是「平方」——5.98e-03 一步变成 2.86e-05,那正是 0.00598 的平方量级。 | |||
| 这就是二次收敛:靠近最优点时,每步的有效位数翻倍。 | |||
| 对比一下:第 8 章的 SMO 要 71 轮,第 9 章的 boosting 要两百轮。 | |||
| 能这么快有两个前提,缺一不可:一是这个问题的维度只有 3(两个系数加一个截距), | |||
| 二是牛顿法用了二阶信息——它知道「往哪边走、走多远」。 | |||
| 再看「步长最大值」那一列,它前四步是 0.734、0.513、0.533、0.608——不是单调变小。 | |||
| 牛顿方向的长度不等于「离最优点还有多远」:方向对了,步子可能突然变大, | |||
| 因为 Hessian 在那一段变小了。 | |||
| 所以停止条件不能只看步长,配合梯度一起看才稳。 | |||
| 代价必须说清楚:每步要解一个 d 乘 d 的方程组,代价是 d 的三次方。 | |||
| d 等于 3 的时候无感;d 等于一万的时候一步都解不起。 | |||
| 所以高维问题上用的是 L-BFGS 或者随机梯度——它们只用一阶信息,步数多得多,但每步便宜。 | |||
| 「11 步收敛」是这个尺寸下的成绩,不能当成牛顿法的一般性能。 |
四、校准:概率真正的价值
准确率只回答「判对了几成」,概率还要回答「你说的几成算不算数」。 检验办法是把测试点按预测概率分桶,比较「桶里的平均预测」和「桶里实际的正类比例」。 两者贴合,就叫校准过。
| 预测概率区间 | 点数 | 平均预测 | 实际正类比例 |
|---|---|---|---|
| 0.0 到 0.2 | 370 | 0.0124 | 0.0189 |
| 0.2 到 0.4 | 16 | 0.2886 | 0.3750 |
| 0.4 到 0.6 | 24 | 0.5343 | 0.5417 |
| 0.6 到 0.8 | 23 | 0.7052 | 0.7826 |
| 0.8 到 1.0 | 367 | 0.9819 | 0.9700 |
| 两列几乎贴着走:预测 0.5343 的那一桶,实际正类占 0.5417; | |||
| 预测 0.9819 的那一桶,实际占 0.9700。 | |||
| 这个性质不是所有分类器都有的。 | |||
| 第 7 章的最小二乘分类给不出——它的输出范围是 负 1.68 到 1.72,既不是概率, | |||
| 也没有什么可校准的东西。 | |||
| 要理解校准的分量,可以想一个反例:一个模型可以对 95% 的点判对, | |||
| 但它对每个点都输出 0.95——准确率满分,校准完全失效。 | |||
| 这种模型在「按概率排序取前 100 个」的任务里一文不值,因为它排不出序。 | |||
| 概率的价值不在判类,在排序和取舍。 | |||
| 中间三桶的点很少(16、24、23 个),那几个数波动大,别抠小数点。 | |||
| 两头各 370 和 367 个点,那两个数才稳。 | |||
| 这也是看校准表的常规姿势:先看点多的桶,点少的桶只当参考。 |
五、对错标点的鲁棒性
把第 7 章那 12 个位置一模一样的错标点加进训练集,两种方法各跑一遍:
| 做法 | 干净数据上 | 加 12 个错标点后 | 掉了多少 |
|---|---|---|---|
| 最小二乘分类 | 95.62% | 93.75% | 1.88 个点 |
| Logistic 回归 | 94.38% | 93.75% | 0.62 个点 |
| Logistic 掉 0.62 个点,最小二乘掉 1.88 个点,前者只有后者的三分之一。 | |||
| 这和第 7 章的结论一致:交叉熵对离谱点的惩罚是线性的,平方损失是二次的。 | |||
| 但有一件事必须看清楚:两者的终点是一样的,都是 93.75%。 | |||
| Logistic 的起点低(94.38% 对 95.62%),所以它「掉得少」有一半是因为它本来就更差一点。 | |||
| 这就是比较两种方法时的纪律:不能只报「谁掉得多」,必须把起点一起摆出来。 | |||
| 只报掉幅的话,一个起点 60% 的差模型也能「掉得最少」。 | |||
| 为什么 Logistic 的起点会低一点?因为它优化的是对数似然,不是误分数。 | |||
| 这两个目标在有限样本上不完全一致:似然最大的那条线,未必是错得最少的那条。 | |||
| 差距 1.24 个百分点,在这个规模上属于正常的目标错位,不是实现错误。 | |||
| 要说明: | |||
| 这两团点是按高斯造的、标准差 0.95、中心距 2.2,重叠区不小,所以准确率天花板就在 95% 上下。 | |||
| 换成完全可分的数据,两者都会到 100%,那时比的就是谁的概率更可信——而那正是下一节要拆的东西。 |
六、动手 Demo:Logistic 回归:牛顿法 11 步收敛,测试准确率 95.12%,对数损失 0.1188
造 160 个训练点和 800 个测试点:两团二维高斯,中心 (负 1.1, 负 1.1) 与 (1.1, 1.1),标准差 0.95, 一类 80 个。 模型是 p 等于 sigmoid(w 点乘 x 加 b),用牛顿法最小化平均负对数似然,每步记录似然、梯度最大值、 步长最大值。 收敛后打印系数、训练与测试准确率、对数损失、概率范围,再把测试集按预测概率分五桶做校准, 最后加 12 个错标点重跑两种方法做对照。
def fit_logit(A, yy, iters=30):
b = np.zeros(A.shape[1])
for t in range(1, iters + 1):
p = sig(A @ b) # 当前的概率预测
g = A.T @ (p - (yy > 0) * 1.0) # 负对数似然的梯度
w = p * (1.0 - p) # 每个点的权重
H = A.T @ (A * w[:, None]) + 1e-10 * np.eye(len(b))
step = np.linalg.solve(H, g) # 牛顿方向
b = b - step
ll = np.mean(np.log(np.clip(sig(yy * (A @ b)), 1e-15, 1.0)))
if np.abs(step).max() < 1e-12: # 步长到机器精度就停
break
return b
实跑输出:
--------------------------------------------------------------------
Logistic 回归:牛顿法 11 步收敛,测试准确率 95.12%,对数损失 0.1188
--------------------------------------------------------------------
数据 160 个训练点、800 个测试点,二维,两团中心 (负 1.1, 负 1.1) 与 (1.1, 1.1),标准差 0.95
模型 p 等于 sigmoid(w 点乘 x 加 b),输出就是一个 0 到 1 之间的数
损失 交叉熵,等价于「负的对数似然」
求解 牛顿法:每步解一个 3 元线性方程组
牛顿法的迭代轨迹(对数似然是每个点的平均值,越大越好):
轮次 平均对数似然 梯度最大值 步长最大值
--------------------------------------------
1 -0.25680 9.62e+01 7.34e-01
2 -0.16146 2.63e+01 5.13e-01
3 -0.12019 9.94e+00 5.33e-01
4 -0.10231 3.84e+00 6.08e-01
5 -0.09603 1.45e+00 6.06e-01
6 -0.09477 4.66e-01 4.20e-01
7 -0.09469 9.42e-02 1.34e-01
8 -0.09469 5.98e-03 9.90e-03
9 -0.09469 2.86e-05 4.83e-05
10 -0.09469 6.72e-10 1.14e-09
11 -0.09469 1.37e-15 1.39e-15
**对数似然从 -0.25680 单调涨到 -0.09469,第 11 步步长掉到 1e-12 以下,停。**
**注意收敛速度:前 6 步就把似然从 -0.25680 抬到 -0.09477,后面 5 步只多走了 0.00008。**
**这是牛顿法的典型行为:二阶信息用上了,靠近最优点时每步的有效位数翻倍。**
**相比之下第 8 章的 SMO 要 71 轮,第 9 章的 boosting 要两百轮。**
**能这么快,是因为这个问题的维度只有 3(两个系数加一个截距),而且牛顿法用的是二阶信息——它知道「往哪边走、走多远」。**
系数 w 等于 (3.2909, 3.4055),b 等于 -0.3783
训练准确率 94.38%
测试准确率 95.12%
对数损失 0.1188(每个点平均)
概率范围 3.25e-09 到 1.000000
**概率范围这一行要看清楚:最小值是 3.2e-09,也就是「几乎肯定不是正类」。**
**sigmoid 在两端是饱和的,输入差 15 以上就压到 1e-7 以下。**
**这就是为什么它给得出「0.999999」这种数——是真算出来的,不是凑的。**
再看校准:把测试集的 800 个点按预测概率分成五桶,比较「平均预测」和「实际的正类比例」。
预测概率区间 点数 平均预测 实际正类比例
--------------------------------------------
0.0 到 0.2 370 0.0124 0.0189
0.2 到 0.4 16 0.2886 0.3750
0.4 到 0.6 24 0.5343 0.5417
0.6 到 0.8 23 0.7052 0.7826
0.8 到 1.0 367 0.9819 0.9700
**两列几乎贴着走:预测 0.5343 的那一桶,实际正类占 0.5417;预测 0.9819 的那一桶,实际占 0.9700。**
**这个性质叫「校准」:它说的 30% 就真的是 30%,不是随口一个数。**
**第 7 章的最小二乘分类给不出这个——它的输出范围是负 1.68 到 1.72,既不是概率也没有校准过。**
**注意中间三桶的点很少(16、24、23 个),所以那几个数波动大,别抠小数点。**
为什么值得费劲做概率?因为「差得远」和「差一点点」应该罚得不一样。
看两种损失在同一个间隔上罚多少:
间隔 平方损失 交叉熵
-----------------------
-1.0 4.000 1.313
-2.0 9.000 2.127
-4.0 25.000 4.018
-6.0 49.000 6.002
+0.0 1.000 0.693
+1.0 0.000 0.313
+2.0 1.000 0.127
**间隔从负 1 走到负 6,平方损失从 4 涨到 49(涨了 12 倍),交叉熵只从 1.313 涨到 6.002(涨了 4.6 倍)。**
**前者是二次增长,后者是线性增长。**
**所以一个错得离谱的点,在平方损失下会拿到不成比例的话语权,在交叉熵下就没那么夸张。**
**第 7 章那个「12 个错标点把准确率打掉 1.8 个点」的现象,根子就在这张表上。**
那就用同样的 12 个错标点试一遍:
做法 干净数据上 加 12 个错标点后 掉了多少
--------------------------------------------------------
最小二乘分类 95.62% 93.75% 1.88 个点
Logistic 回归 94.38% 93.75% 0.62 个点
**Logistic 掉了 0.62 个点,最小二乘掉了 1.88 个点,前者只有后者的三分之一。**
**但要看清楚一件事:两者的终点是一样的(93.75%)。**
**Logistic 起点低(94.38% 对 95.62%),所以它「掉得少」有一半是因为它本来就更差一点。**
**这就是为什么比较两种方法时不能只看「谁掉得多」——必须把起点一起摆出来。**
要说明:**这两团点是按高斯造的、有真实重叠,所以准确率天花板就在 95% 上下。**
**换成完全可分的数据,两者都会到 100%,那时比的就是谁的概率更可信。**
g = A.T @ (p - (yy > 0) * 1.0) 这一行值得记住:交叉熵配 sigmoid 的梯度,
就是「预测概率减标签」再乘设计矩阵。
形式极简,而且有直观含义——每个点的残差是「它说自己多大概率是正类」减去「它实际是不是正类」,
梯度就是这些残差按特征加权求和。
这也解释了为什么收敛后期梯度会掉得那么快:越靠近最优点,残差越接近互相抵消。
+ 1e-10 * np.eye(len(b)) 这个抖动不能省。
p 一旦饱和(比如 0.999999),权重 w 等于 p 乘 (1 减 p) 就趋近于 0,H 会退化成奇异矩阵,
np.linalg.solve 直接抛 LinAlgError。
加一个极小的对角项是最省事的补救,代价是稍微偏离真牛顿方向——在 1e-10 这个量级上,
偏得可以忽略。
停止条件用的是「步长最大值小于 1e-12」,不是「似然变化小于多少」。
因为似然后期变化极小(第 7 到 11 轮只动了 0.00008),拿它当判据会在第 7 轮就误判成收敛,
而那时梯度还有 9.42e-02。
判据要看那个真正会归零的量——步长或者梯度。
hist 里同时记梯度最大值和步长最大值,是为了看清两者不同步。
梯度是一直降的(26.3、9.94、3.84、1.45),步长却在中途回升(0.513、0.533、0.608)。
牛顿方向的长度不等于「离最优点还有多远」,因为 Hessian 本身也在变。
sig 里那个 np.clip(z, -60.0, 60.0) 是防溢出的。
不夹的话 exp(800) 直接变 inf,1 除以 inf 得 0,然后 log 里就会出现 log(0)。
夹到正负 60 之后,sigmoid 的下限是 1e-26 左右,双精度还兜得住。
要说明:这里的两团高斯有真实重叠,所以 95% 附近就是这份数据的天花板。
别把这个数当成「Logistic 回归的准确率」——换个分布它就是另一个数。
能跨数据比较的只有「同一份数据上两种方法的差」。
30 秒实验
- 把两团中心从 (正负 1.1) 拉近到 (正负 0.6),重跑。 牛顿法还要 11 步吗?对数损失涨到多少?校准那两列还贴合吗?
- 把牛顿法换成固定步长的梯度下降(步长取 0.5),一直跑到梯度小于 1e-6。 需要多少步?这 11 步牛顿法相当于多少步梯度下降?
- 把 12 个错标点加到 60 个,两种方法各重跑一遍。 终点还会一样吗?谁先崩?崩的时候是准确率掉得多还是对数损失涨得多?
常见误区
- 「Logistic 回归是个回归算法」。 名字里有「回归」,但它做的是分类:输出是类别概率,训练目标是分类似然。 「回归」这两个字来自它用的那个函数(logistic 函数),不来自任务类型。 同样容易误会的是「Logistic」和「log」没关系——它是「logistic」这个 S 形函数的专名。
- 「交叉熵和平方损失只是写法不同,效果差不多」。 形状完全不同:一个近似线性增长,一个是二次增长。 间隔走到负 6,交叉熵 6.002,平方损失 49.000,差 8 倍。 实测里 12 个错标点让平方损失掉了 1.88 个点,交叉熵只掉 0.62 个点。
- 「11 步收敛说明牛顿法永远比梯度下降快」。 每步要解一个 d 乘 d 的方程组,代价是 d 的三次方。 d 等于 3 的时候它快得惊人,d 等于十万的时候一步都解不起。 所以大规模问题上用的是 L-BFGS 或随机梯度——步数多,但每步便宜。 「步数」和「总代价」是两件事。
- 「有了概率就能当置信度用」。 得先校准过才行,而且校准是对训练分布而言的。 换个数据分布,原来贴合的校准可能立刻失效——这就是为什么部署后要重新画一张校准表, 而不是拿训练时那张交差。
自测三问(点开对答案)
1. 为什么 Logistic 回归用交叉熵,不用平方损失? —— **因为平方损失是「让输出贴近正负一」,那是把分类当回归做。** **交叉熵是「让正确类的概率尽量大」,这才是分类该问的问题。** **形状上的差别更实际:平方损失对离谱的点是二次惩罚,一个错标点能拽着边界跑很远;** **交叉熵近似线性,罚得没那么夸张。** **实测里同一批 12 个错标点,平方损失掉 1.88 个点,交叉熵只掉 0.62 个点。** 2. 牛顿法为什么 11 步就收敛,代价是什么? —— **它用了二阶信息(Hessian),靠近最优点时是二次收敛——每步的有效位数翻倍。** **看梯度那一列最清楚:5.98e-03 下一步就是 2.86e-05,正好是平方量级。** **代价是每步解一个 d 乘 d 的方程组,复杂度 d 的三次方。** **d 等于 3 时无感,d 上万时就一步都解不起,只能退回一阶方法。** 3. 校准表里两列贴合说明什么?如果不贴,说明什么? —— **贴合说明「它说的 30% 就真是 30%」,概率能直接当频率解释,可以拿去做排序、算期望、** **定阈值。** **不贴说明概率只是个「相对分数」:排序可能还对,但绝对值不能用。** **最典型的失效是一个永远输出 0.95 的模型——准确率可能很高,校准彻底崩。** **这也是为什么评估概率模型要同时看准确率和校准,缺一不可。**牛顿法要算 Hessian、要反复迭代,11 步虽然快,但每一步都是三次方代价。 下一节换一个更省事的做法——每类拟合一个最小二乘,解一次线性方程组就完事,完全不迭代。 代价是它交出来的东西严格来说不是概率:有几百个点上「概率」是负数,得先截断再归一化才能用。
- ☐ (本节完,下一节 10.2 10.2)