最小二乘不管系数多大,岭回归在旁边加一项惩罚,把系数压小。 alpha 是惩罚力度,越大压得越狠;alpha 等于 0 就退回最小二乘。 不标准化就上岭回归,惩罚会被量纲带偏——这次面积系数反而变大了。
上一篇的模型很干净。但特征一多、彼此相关,最小二乘的系数就会乱跳。 这一篇用同一份房价数据,演示岭回归怎么「上缰绳」,以及一个必须先做的前置动作。
一、最小二乘的毛病
它只干一件事:让误差平方和最小。至于系数多大,它不管。 特征之间相关时(比如面积和房间数),系数会正负乱跳,甚至大得离谱。 系数一大,模型就脆:新数据稍微一动,预测就飞出去。这就是过拟合。
所以需要在「误差小」之外,再加一条约束:系数别太大。
二、加一项惩罚
目标函数从「误差平方和」,变成「误差平方和 加 alpha 乘 系数的平方和」。 alpha 越大,惩罚越重,系数被压得越小。 alpha 是你自己定的数,它不在数据里。这种「要人定、不在数据里」的参数叫超参数。
它用的是系数的平方,所以叫 L2 惩罚。它会把系数压小,但很少压到正好 0。
三、先踩一个坑:不标准化
面积 62 到 128,房龄 1 到 18,量纲差了十倍。直接喂原始数据: 最小二乘面积系数 1.0489;岭 alpha=50 时变成 1.3197,反而变大了。 为什么?因为惩罚加在「系数的平方」上。量纲大的特征本来系数就小,罚它几乎没作用。
于是 alpha 一涨,系数在两个特征之间重新分活:一个被压下去,另一个顶上来。 不标准化的岭回归,等于让量纲决定谁被罚。这一步不补,后面的结论都不算数。
四、标准化之后再上岭
标准化就是每个特征减去均值、除以标准差,把量纲拉平。 这回 alpha 从 0.1 加到 1000,两个系数的绝对值一路单调变小。 面积系数从 18.47 掉到 0.37,房龄从 -11.06 掉到 -0.36。
这才是岭回归该有的样子:alpha 越大,系数越被压向 0。 但压过头就废了:alpha=1000 时测试 R 方是 -0.1798,比直接猜平均值还差。
五、什么时候该想到它
特征多、样本少的时候,最小二乘容易过拟合,岭回归能稳住。 特征之间高度相关时,最小二乘系数会乱跳,岭回归能压住。 普通任务先跑最小二乘,发现系数离谱再上岭回归。不必一上来就用。
名字里带 Ridge 是「山脊」的意思,因为系数被压成了一条窄窄的山脊。
六、动手 Demo:岭回归:给系数上缰绳
先看不标准化时 alpha 变大系数反而变大,再标准化重跑一遍。 最后把 alpha 从 0.1 到 1000 扫一排,看系数和 R 方怎么走。
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
AREA = [62, 75, 88, 95, 105, 118, 128, 70, 82, 100, 112, 92,
66, 79, 91, 108, 122, 86]
AGE = [15, 12, 8, 5, 3, 2, 1, 18, 10, 6, 4, 9,
14, 11, 7, 5, 2, 9]
PRICE = [78, 92, 118, 128, 146, 163, 172, 76, 100, 134, 154, 118,
82, 98, 124, 148, 168, 112]
X = np.array([[AREA[i], AGE[i]] for i in range(18)], dtype=float)
y = np.array(PRICE, dtype=float)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42)
m0 = LinearRegression().fit(Xtr, ytr)
m1 = Ridge(alpha=50.0).fit(Xtr, ytr)
print("不标准化 最小二乘:面积 %.4f,房龄 %.4f"
% (m0.coef_[0], m0.coef_[1]))
print("不标准化 岭 a=50 :面积 %.4f,房龄 %.4f"
% (m1.coef_[0], m1.coef_[1]))
sc = StandardScaler().fit(Xtr)
Xs_tr, Xs_te = sc.transform(Xtr), sc.transform(Xte)
for a in (0.1, 1.0, 10.0, 100.0, 1000.0):
mm = Ridge(alpha=a).fit(Xs_tr, ytr)
print("alpha %-6g 面积 %8.4f 房龄 %8.4f R 方 %7.4f"
% (a, mm.coef_[0], mm.coef_[1],
r2_score(yte, mm.predict(Xs_te))))
实跑输出:
--------------------------------------------------------------------
岭回归:给系数上缰绳
--------------------------------------------------------------------
① 最小二乘的毛病
--------------------------------------------------------------------
最小二乘只干一件事:让误差平方和最小。它不管系数多大。
特征一多、特征之间还相关的时候,系数会变得很大,甚至正负乱跳。
系数一大,模型就脆:新数据稍微一动,预测就飞出去。这叫过拟合。
! 岭回归的思路很朴素:在目标函数里加一项,惩罚过大的系数。
② 加一个 alpha
--------------------------------------------------------------------
目标函数从「误差平方和」变成「误差平方和 + alpha 乘 系数的平方和」。
alpha 越大,惩罚越重,系数被压得越小。alpha 等于 0 就退回最小二乘。
alpha 是一个你要自己定的数,它不在数据里,得试。这叫超参数。
! 岭回归用的是系数的平方(L2),所以它会把系数压小,但很少压到正好 0。
③ 不标准化会出什么事
--------------------------------------------------------------------
面积 62 到 128,房龄 1 到 18,量纲差了十倍。直接喂原始数据:
最小二乘:面积 1.0489,房龄 -2.2789。
岭 alpha=1:面积 1.0631,房龄 -2.2193。
岭 alpha=50:面积 1.3197,房龄 -1.0970。
注意:alpha 加大之后,面积系数反而变大了。
惩罚加在「系数的平方」上。量纲大的特征本来系数就小,罚它几乎没作用,
于是 alpha 一涨,两个系数之间重新分活——一个被压,另一个就顶上去。
! 岭回归不标准化,等于让量纲决定谁被罚。这一步不补,后面的结论都不算数。
④ 标准化之后再上岭
--------------------------------------------------------------------
标准化之后,两个特征都变成均值 0、标准差 1,量纲拉平了。
| alpha | 面积系数 | 房龄系数 | 测试 R 方 |
|---:|---:|---:|---:|
| 0.1 | 18.4679 | -11.0552 | 0.9823 |
| 1 | 16.0851 | -12.4246 | 0.9767 |
| 10 | 10.9144 | -10.3105 | 0.8711 |
| 100 | 3.0173 | -2.9527 | 0.1956 |
| 1000 | 0.3678 | -0.3613 | -0.1798 |
这下对了:alpha 越大,两个系数的绝对值都单调变小。
面积系数始终为正、房龄始终为负,方向也没被搅乱。
但测试 R 方从 0.9823 一路掉到 -0.1798——alpha 太大,模型就被压得只会猜平均值了。
最后一行 R 方是负数,意思是「还不如直接拿训练集的均值去猜」。这就是惩罚过头的下场。
! 标准化之后系数可以直接比大小,因为两个特征的尺度一样了。这才是「谁更重要」的正确比法。
⑤ 什么时候该想到岭回归
--------------------------------------------------------------------
特征多、样本少的时候,最小二乘容易过拟合,岭回归能稳住。
特征之间高度相关(比如「面积」和「房间数」)时,最小二乘系数会乱跳,岭回归能压住。
普通预测任务,先跑最小二乘,发现系数离谱再上岭回归。不必一上来就用。
! 名字里带 Ridge 是「山脊」的意思,因为系数被压成了一条窄窄的山脊形状。
StandardScaler 用 fit 学训练集的均值和标准差,再用 transform 把它们用到训练集和测试集上。
测试集只能 transform,不能 fit。拿测试集学均值和标准差,等于提前偷看考卷。
alpha=1000 那一行 R 方是负的,不是 bug:模型比「直接猜平均值」还差,就会算负值。
30 秒实验
- 把 alpha 加到 100000,看系数是不是都接近 0,R 方掉到多少。
- 把 StandardScaler 去掉,只跑标准化后的表,对比两组系数。
- 想一想:为什么测试集不能参与 fit 标准化?偷看考卷会造成什么后果?
常见误区
- 不标准化直接上岭:惩罚被量纲带偏,系数会给出反直觉的结果。
- 在测试集上 fit 标准化器:数据泄漏,测试分虚高。
- alpha 越大越好:压过头 R 方变负,模型只会猜平均值。
- 以为岭回归能把系数压成 0:L2 只会压小,要压成 0 得用 Lasso。
自测三问(点开对答案)
1. 岭回归比最小二乘多加了什么? —— 目标函数里多一项 alpha 乘系数的平方和,用来惩罚过大的系数。 2. 为什么必须标准化? —— 惩罚加在系数平方上,量纲大的特征系数本来就小,罚它没作用,系数会重新分配。 3. alpha=1000 时 R 方是负的,说明了什么? —— 惩罚过重,模型比「直接猜平均值」还差。回归讲完了。下一篇换个任务:分类,用一条最宽的分界线把两类点分开。
- ☐ (本篇完,下一篇 8.4 支持向量机:画一条最宽的分界)