最小二乘默认每个点一样可信。加权回归只改一件事:每个点带一个权重,残差平方按权重加起来。
这个改动小到只是把 X'X 换成 X'WX,
可它能处理两件很实际的事:一个离谱的离群点,和「越远测得越不准」的测量。
上一节讲的共线性是「自变量之间打架」。 这一节讲的是另一种不平等:样本之间不等价 —— 有的点该多信,有的点不该信。 加权这个念头后面还会出现好几次:第 6 章 MCMC 里的重要性采样、第 9 章注意力机制的加权求和, 底下的想法都是同一个 —— 让重要的那部分多说几句。
一、一句话的改动
普通最小二乘最小化 Σ(yᵢ − ŷᵢ)²。
加权版本最小化 Σ wᵢ(yᵢ − ŷᵢ)²,就是每项的平方先乘一个权重。
推导过程一模一样(对 w 求导、令其为零),只是每个求和里多乘了一个 wᵢ,
结果变成正规方程 X'WX w = X'Wy,W 是对角线为 wᵢ 的对角矩阵。
两个极端情况值得记住:
| 权重取法 | 等价于 |
|---|---|
| 全部等于 1 | 普通最小二乘 |
| 某一项等于 0 | 把那一行数据删掉 |
| 等于 1/方差 | 噪声大的点少信,统计上最优 |
| 中间那条最有用:把权重设成 0 和删数据是一回事,所以加权不是新算法, | |
| 是最小二乘本来就允许的事 —— 只是默认值一直是 1,大家忘了它能改。 |
二、一个离群点能拉多歪
Demo 第一段:30 个点,真规律 y = 2 + 1.5x。
然后在 x = 9.0 处塞一个 y = −20 的点(其他点的 y 都在 5 到 17 之间)。
| 截距 | 斜率 | |
|---|---|---|
| 30 个点 | 2.0636 | 1.4797 |
| 加那 1 个点 | 3.4069 | 0.9953 |
| 真值 | 2.0000 | 1.5000 |
| 31 个点里只有 1 个不对劲,斜率掉了 32.7%,截距涨了六成。一个点比其余三十个点加起来还有话语权。 | ||
| 原因就是那个平方。这一点的残差是 −32.4,平方之后 1047, | ||
| 而其余 30 个点的残差平方和只有个位数。最小二乘要压住这一个大数,只能把整条线往它那边掰。 | ||
| 这也是「为什么不用绝对值」这条问题的另一半答案: | ||
| 平方让解好算,也让离群点的破坏力按平方放大。想两头都要, | ||
| 得用 Huber 损失这类折中方案 —— 小残差按平方、大残差按绝对值。 |
三、权重是连续可调的开关
Demo 第二段把那个离群点的权重从 1 一路调到 0:
| 该点权重 | 斜率 |
|---|---|
| 1.00 | 0.9953 |
| 0.30 | 1.3252 |
| 0.10 | 1.4273 |
| 0.01 | 1.4744 |
| 0.00 | 1.4797 |
| 权重 0.10 的时候斜率 1.4273,已经很接近「完全不信」的 1.4797。因为权重是乘在平方上的, | |
| 0.1 的权重相当于把这点的影响力压到十分之一 —— | |
| 平方根意义上,等于把它的「等效距离」缩到三分之一。 | |
| 这个连续可调的性质,才是加权比「删数据」好用的地方。删数据是个不可逆的决定,删错了没法救; | |
| 给个 0.1 是「半信半疑」,既不让它主导,也不假装它不存在。真实数据里, | |
| 标注可疑但不敢删的样本太多了,这个中间档正好用得上。 |
四、异方差:越远越不准
Demo 第三段造了 50 个点,噪声幅度跟 x 成正比 —— x 越大测得越不准。 这是实测里最常见的情形:远处的、慢的、贵的目标,都测不准。
| 权重 | 斜率 | 标准误 |
|---|---|---|
| 全部等权 | 1.4556 | 0.3442 |
| 1/x² | 1.4234 | 0.2344 |
| 真值 | 1.5000 | — |
| 标准误从 0.3442 降到 0.2344,降了 32%。可系数反而从 1.4556 挪到 1.4234, | ||
| 离真值 1.5 远了 0.0322。 | ||
| 这两件事不矛盾,而且必须放在一起说:加权不是为了「这一次更准」,是为了「重复很多次时更稳」。 | ||
| 标准误小,意思是换一批同样条件的数据重来,估计值不会乱跑。 | ||
| 单次实现偏一点很正常,这一次就偏了一点。 | ||
| 为什么 1/x² 是对的权重?因为加权最小二乘的最优权重是 1/方差,而这里的噪声标准差正比于 x, | ||
| 方差就正比于 x²。记住这句就够了:方差大的点,权重应该小。 | ||
| 但权重从哪来,这才是真问题。它要么来自已知的物理规律(比如计数器的读数误差正比于读数本身), | ||
| 要么来自残差图(把残差对 x 画出来,看喇叭口往哪边张)。拍脑袋给的权重,等于把偏见写进了公式。 |
五、局部加权:每个预测点自己拟一条线
Demo 第四段换了个用法。
数据是 y = 3·sin(x) + 0.3x,
一条全局直线拟合出来最大误差 2.3744 —— 一条直线当然压不住一个弯。
改成局部加权:对每一个要预测的 x,按「离 x 越近权重越大」重新拟一条线,只取 x 处那一个值。
权重用高斯核 exp(−(xᵢ−x)²/2h²),h 叫带宽,控制「近邻」有多宽。
| 带宽 h | 最大误差 |
|---|---|
| 0.10 | 0.8580 |
| 0.20 | 0.5735 |
| 0.30 | 0.5012 |
| 0.50 | 0.5161 |
| 1.00 | 1.0803 |
| 2.00 | 1.8672 |
| 5.00 | 2.3807 |
| 最小误差出现在 h = 0.3(0.5012),比全局直线的 2.3744 好四倍多。h 调小,曲线一个点一个点地跟, | |
| 把噪声也拟合进去;h 调大,权重摊平,退化成一条直线。两头都差,中间有个谷。 | |
| 这个办法有个正式名字:LOESS(局部加权散点平滑)。它不产出「一个模型」, | |
| 只产出「一条曲线」—— | |
| 没有系数可以解释,想知道 x=2 处的斜率是多少,得现场重算一遍。这是它的代价,也是它灵活的原因。 |
六、带宽和次数是同一个旋钮
上一节用「多项式的次数」控制复杂度,这一节用「带宽」控制。 两件事是同一个东西:模型能弯到什么程度。
| 旋钮 | 往哪边拧 | 后果 |
|---|---|---|
| 多项式次数 | 调高 | 能弯,也容易记噪声 |
| 带宽 h | 调小 | 能弯,也容易记噪声 |
| kNN 的 k | 调小 | 能弯,也容易记噪声 |
| 决策树深度 | 调高 | 能弯,也容易记噪声 |
| 四个名字、四种算法,拧的都是同一个旋钮。所以「怎么选参数」这个问题,在每一章都会重新出现一次, | ||
| 解法也总是同一套:留出没看过的数据,拿它来试。 | ||
| 最后提醒一句加权最容易被误用的地方:权重是你给的,不是数据给的。给错了,结果可能比不加权更糟。 | ||
| 上面 1/x² 敢用,是因为「噪声正比于 x」这个假设有物理依据; | ||
| 要是凭感觉拍一个权重,那就只是把偏见写进了公式。 |
七、动手 Demo:一个离群点、一段测不准的数据、一条自己弯的曲线
wls 就是普通最小二乘多了一行:把 X'X 换成 X'WX,把 X'y 换成 X'Wy。
W 是对角矩阵,对角线就是权重。
前三段用的是同一批 30 个点的数据,只改权重;
第四段把权重换成「离预测点有多近」,同一个 wls 函数就变成了 LOESS。
def wls(X, y, wt):
"""加权最小二乘:把 X'X 换成 X'WX,X'y 换成 X'Wy。"""
W = np.diag(wt)
A = X.T.dot(W).dot(X)
b = X.T.dot(W).dot(y)
w, *_ = np.linalg.lstsq(A, b, rcond=None)
return w
# 一个离群点:先等权,再把它的权重设成 0
w0 = wls(X, y, np.ones(30)) # 斜率 1.4797
w1 = wls(Xo, yo, np.ones(31)) # 斜率 0.9953,被那个点拉歪
wt = np.ones(31); wt[-1] = 0.0
w2 = wls(Xo, yo, wt) # 斜率 1.4797,回来了
# 局部加权:对每个预测点,按高斯核重新拟一条线
for t in xt:
k = np.exp(-((xs - t) ** 2) / (2.0 * h * h))
wh = wls(Xg, ys, k)
pred.append(wh[0] + wh[1] * t)
实跑输出:
--------------------------------------------------------------------
给每个点配一个权重
--------------------------------------------------------------------
==============================================================
[1] 一个离群点能把整条线拉歪多少
30 个点,真实规律 y = 2 + 1.5x。
然后在 x = 9.0 处塞进一个 y = -20 的点(其他点的 y 都在 5 到 17 之间)。
截距 斜率
----------------------------------------
30 个点 2.0636 1.4797
加那 1 个点 3.4069 0.9953
真值 2.0000 1.5000
31 个点里只有 1 个不对劲,斜率就从 1.4797 掉到 0.9953,
差了 32.7%;截距从 2.0636 涨到 3.4069。
为什么一个点有这么大劲?因为最小二乘最小化的是平方和,
这一点的残差是 -32.4,平方之后是 1047,
比其余 30 个点的平方和加起来还大。它一个人说了算。
[2] 加权:给这个点一个 0 的权重
加权最小二乘最小化的是 Σ wᵢ(yᵢ − ŷᵢ)²。
把最后那个点的权重设成 0,等于把它从平方和里删掉。
权重方案 截距 斜率
----------------------------------------------
全部为 1 3.4069 0.9953
最后一点为 0 2.0636 1.4797
斜率回到 1.4797,跟没加那个点时(1.4797)几乎一样。
把权重设成 0 和「把这行数据删掉」是同一件事——
所以加权回归不是新算法,是最小二乘本来就允许的事。
权重还可以取 0 到 1 之间的数。比如取 0.1,意思是「信一成」:
最后一点权重 1.00:斜率 0.9953
最后一点权重 0.30:斜率 1.3252
最后一点权重 0.10:斜率 1.4273
最后一点权重 0.01:斜率 1.4744
最后一点权重 0.00:斜率 1.4797
中间那几档就是「半信半疑」——不想删掉它,又不想让它主导。
这个连续可调的性质,才是加权比「删数据」好用的地方。
[3] 异方差:越是后面的点越不准
50 个点,噪声幅度跟 x 成正比:x 越大,测得越不准。
这是实测里最常见的情形——远处的、慢的、贵的,都测不准。
斜率 标准误
----------------------------------------------
全部等权 1.4556 0.3442
权重 1/x² 1.4234 0.2344
真值 1.5000 —
斜率从 1.4556 挪到 1.4234 —— 离真值 1.5 反而远了 0.0322。
但标准误从 0.3442 降到 0.2344,降了 32%。
加权不是为了「这一次更准」,是为了「重复很多次时更稳」。
标准误小,意思是换一批同样条件的数据重来,估计值不会乱跑。
单次实现偏一点很正常,这一次就偏了一点。
为什么 1/x² 是对的权重?因为加权最小二乘的最优权重是 1/方差,
而这里的噪声标准差正比于 x,方差就正比于 x²。
记住这句就够了:方差大的点,权重应该小。
[4] 局部加权:每个预测点自己拟一条线
40 个点,规律是 y = 3·sin(x) + 0.3x。全局直线拟合出来:
斜率 -0.6203,截距 2.7660
在 60 个新点上最大误差 2.3744
一条直线当然压不住一个弯。改成局部加权:对每个预测点 x,
按「离 x 越近权重越大」重新拟一条线,只取 x 处的那个值。
权重用高斯核 exp(−(xᵢ−x)²/2h²),h 是带宽。
带宽 h 最大误差 说明
--------------------------------------------------------
0.10 0.8580 太窄,跟着噪声抖
0.20 0.5735 偏窄
0.30 0.5012 刚好
0.50 0.5161 偏宽
1.00 1.0803 更宽,弯处跟不上
2.00 1.8672 快成直线了
5.00 2.3807 全局平均
误差最小出现在带宽 0.3 附近(0.5012)。h 调小,曲线一个点一个点地跟,
把噪声也拟合进去;h 调大,权重摊平,退化成一条直线。
两头都差,中间有个谷 —— 跟上一节那个多项式次数的形状一模一样。
[5] 带宽和次数是同一个旋钮的两种转法
上一节用「多项式的次数」控制复杂度,这一节用「带宽」控制,
两件事是同一个东西:模型能弯到什么程度。
旋钮 往哪边拧 后果
--------------------------------------------------------
多项式次数 调高 能弯,也容易记噪声
带宽 h 调小 能弯,也容易记噪声
邻域大小 k(kNN) 调小 能弯,也容易记噪声
决策树深度 调高 能弯,也容易记噪声
四个名字、四种算法,拧的都是同一个旋钮。
所以「怎么选参数」这个问题,在每一章都会重新出现一次,
解法也总是同一套:留出没看过的数据,拿它来试。
最后提醒一句加权最容易被误用的地方:
权重是你给的,不是数据给的。给错了,结果可能比不加权更糟。
上面 1/x² 敢用,是因为「噪声正比于 x」这个假设有物理依据;
要是凭感觉拍一个权重,那就只是把偏见写进了公式。
第一段两组系数摆在一起看最直观。斜率 1.4797 对 0.9953,
这 0.48 的差距全来自一个点。注意截距也跟着从 2.0636 涨到 3.4069 —— 拉歪的是整条线,不只一头。
第二段那张权重表是连续变化,不是开关。从 0.30 到 0.10,斜率从 1.3252 跳到 1.4273,
大部分「修正」发生在前两档 —— 这也是「半信半疑」这个档位有用的原因。
第三段最容易读错。标准误降了 32%,可系数离真值更远了。
这两件事必须一起读:加权优化的是「重复实验时的稳定性」,不是「这一次的准确度」。
单次实现偏一点,完全在预期之内。
第四段是这一节的用法升级。前三段是「给样本加权」,
这一段是「给距离加权」——
同一个函数,换个权重就换了用途。那一行 k = exp(-((xs - t)²)/(2·h·h)) 就是全部机关。
最后那张带宽表要看形状,不看某个数。0.10 太窄(误差 0.8580)、0.30 最好(0.5012)、
5.00 退化成直线(2.3807),这个 U 形跟上一节「多项式次数」的 X 形是同一个东西的两种表现。
30 秒实验
- 把第一段那个离群点从
y = −20改成y = 40(往另一头离谱),重跑。 斜率应该往 1.8 以上冲,方向反过来,幅度差不多 —— 平方不分正负。 - 第三段把噪声从
1.0 * u2改成1.0(等方差),再用 1/x² 加权。 这时候加权是错的做法,标准误应该反而变大 —— 这就是「权重给错了更糟」。 - 第四段把带宽 h 固定成 0.3,把核函数从高斯换成「距离小于 h 取 1、否则取 0」。 结果会有点像,但在预测点附近会跳 —— 权重不连续,拟合出的曲线也不连续。
常见误区
- 以为加权能「修复」离群点。它只是降低这个点的影响力, 点本身还在数据里,残差还在那 —— 你仍然需要回答「这个点为什么这么离谱」。 加权是缓冲,不是诊断。
- 以为权重越大越好、越信越好。权重的作用是分配「信任」, 总量有限。给一个点加权重,等于给其他点减权重。全给同一个点时,模型就只拟合那一个点。
- 以为加权一定让结果更准。它优化的是方差(稳定性), 单次实现照样可能偏。Demo 里加权之后系数离真值反而远了 0.0322,这不是失败, 是这类估计的正常表现。
- 以为「越近权重越大」是常识,不用解释。它其实是一个很强的假设: 「函数在局部可以用直线近似」。数据变化太剧烈、或者预测点落在数据空档里, 这个假设就不成立 —— 那时 LOESS 会给出很难看的结果。
自测三问(点开对答案)
1. 为什么一个离群点能顶过三十个正常点? —— 因为**最小二乘最小化的是残差平方和,一点的大残差按平方放大**。 Demo 里那个点残差 −32.4,平方 1047,而其余 30 个点的残差平方和只有个位数。 **要压住这一个大数,只能把整条线往它那边掰。**换成绝对值损失就没这么夸张, 但那样就没有闭式解了。 2. 加权之后标准误降了 32%,系数却离真值更远了,这算成功还是失败? —— 算成功,但要理解它成功在哪。 **加权优化的目标是「估计量的方差最小」,不是「这一次的结果最接近真值」。** 标准误小意味着换一批数据重来、 结论不会乱跑 —— **这是可以重复的性质,而「这一次准不准」是一次性的运气。**单次偏 0.0322 完全在预期之内。 3. 带宽 h 调小和多项式次数调高,为什么会出同样的问题? —— 因为它们拧的是同一个旋钮:**模型能弯到什么程度**。 h 小意味着只用很近的点、直线能快速改变方向;次数高意味着曲线能剧烈起伏。 **两者都会让模型跟着噪声跑。**所以调参的办法也一样 —— 留出没看过的数据,在那个上面挑。到这儿,模型、误差、权重都齐了。 下一节换一个问题:两个东西像不像,这件事该怎么算?它看着更简单, 其实坑更多 —— 五种算法给五个答案。
- ☐ (本节完,下一节 4.4 相似度的计算)