线性回归要找一条直线(高维时是一个超平面),让所有点到它的垂直距离的平方和最小。 它有两个解法:正规方程一步解出来,梯度下降一步一步挪过去。 实测 100 个样本、3 个参数,正规方程解出 (3.0092, 2.0141, −1.4883),真值是 (3, 2, −1.5), MSE 0.0727、R² 0.9863。
这是第 2 章的第一个算法,也是全部十七个里最基础的一个: 它把「学习」这件事还原成了一次曲线拟合。 后面讲正则化(2.2)就是在它的损失函数上再加一项,讲逻辑回归(2.3)就是把输出套一个挤压函数。 1.2 节那条七步流水线,这一节开始只换第四步。
一、它要解决什么:给一堆点,找一条最贴合的线
教材这一节的开场是一张散点图:横轴是某个特征,纵轴是要预测的值,点撒得到处都是, 但明显有个往上走的趋势。线性回归做的事,就是在这堆点中间画一条直线,让「整体上离得最近」。 两个特征时是平面上的直线,三个特征时是空间里的平面,再多就没法画了——但数学上它是同一个东西: 一个加权和加一个截距。 写成公式就是 y = w0 + w1 x1 + w2 x2 + … + wp xp。这里 w0 叫截距(偏置),w1 到 wp 叫系数。 所谓「训练」,就是把这 p + 1 个数定下来。 要提醒一句:线性回归的「线性」指的是对系数线性,不是对特征线性。 你把 x 换成 x²、log x、x1 乘 x2,只要系数还是一次,它仍然叫线性回归。 这条性质在做特征工程时很好用。
二、为什么罚平方,不罚绝对值
「离得最近」需要一个精确的定义。 教材选的是残差平方和:每个点的预测值和真实值之差叫残差,把所有残差平方起来加总,让它最小。 为什么不直接用残差的和?因为正负会抵消,一条穿过数据正中间的烂线也能让总和为零。为什么不罚绝对值? 因为绝对值在 0 点不可导,求最优解时麻烦;平方处处可导,求导之后是线性的,能直接解方程。 平方还有一个副作用:它放大离群点。一个偏了 10 的点,平方之后贡献 100, 而十个各偏 1 的点只贡献 10。 所以线性回归对离群点极其敏感——这不是缺点,是特性:它明说了「我更在乎那些偏得离谱的点」。 真要抗离群点,得换损失函数,那是 40 课 里「鲁棒学习」那一章的事。
三、两种解法:解方程,或者走下山
把残差平方和对每个系数求偏导、令其为零,会得到一个线性方程组,叫正规方程:w = (XᵀX)⁻¹Xᵀy。 样本数和特征数都不大时,这一步用矩阵运算一次算完,没有迭代、没有学习率、不用担心收敛。 另一条路是梯度下降:先随便给一组系数,算出当前的梯度(往哪个方向调能让损失降得最快), 朝着反方向挪一小步,重复几千次。 两条路在数学上收敛到同一个点。 Demo 里两个解法给出的系数只差小数点后四位,差的那点来自梯度下降没有完全收敛。 该选哪个? 特征几十个以内,直接解方程;特征上万(图像、文本),只能用梯度下降。 因为正规方程里的矩阵求逆是 O(p³),p 是一万时根本算不动;而梯度下降每轮只过一遍数据,是 O(np)。 还有一个容易被忽略的差别: 正规方程不需要标准化,梯度下降需要。 Demo 里把第二个特征放大 100 倍之后, 梯度下降能用的学习率从 0.1 掉到十万分之一,200 轮几乎没挪动。原因下面 Demo 里会量。
四、怎么判断拟合得好:MSE 与 R²
训练完总要看一眼分数。最直接的是均方误差 MSE:残差平方的平均。 它的单位和目标值一样,但因为平方过,数值会比直觉大一些——残差平均 0.3,MSE 大约是 0.09。 另一个更常报的是决定系数 R²:1 减去「残差平方和」除以「目标值相对均值的平方和」。 它的含义是「模型解释了目标值多少比例的波动」。 R² 等于 1 是完美,等于 0 是「和你直接用平均值去猜一样烂」,还可以是负数(比平均数还差)。 Demo 里 R² = 0.9863,意思是这 100 个点的起伏里,98.63% 被模型解释掉了。 剩下的 1.37% 就是那层标准差 0.3 的噪声——这部分永远解释不掉,因为它本来就不由特征决定。 这里有个新手常犯的错:把 R² 高当成模型好。特征越多,R² 只会越高,哪怕加的是纯噪声。 所以比较模型时要用「调整后的 R²」或者干脆看测试集误差,这也是 2.2 节要讲正则化的动机之一。
五、动手 Demo:一条方程解出来,另一条一路走下来
这一段把两种解法都跑一遍,再量一个容易被忽略的事:特征尺度对梯度下降的影响。 数据是 100 个样本、2 个特征,真实关系 y = 3 + 2x1 − 1.5x2 加标准差 0.3 的噪声。 噪声是故意加的——没有噪声的数据,任何方法都能把系数还原得一模一样, 就看不出「估计」和「真值」的差别了。
import numpy as np
X = np.column_stack([np.ones(100), x1, x2]) # 第一列全 1,对应截距
# 解法一:正规方程,一步到位
w = np.linalg.solve(X.T @ X, X.T @ y)
print("正规方程:", np.round(w, 4))
# 解法二:梯度下降,一步一步挪
w = np.zeros(3)
for it in range(200):
grad = X.T @ (X @ w - y) / len(y)
w -= 0.1 * grad
print("梯度下降:", np.round(w, 4))
# 拟合得好不好:MSE 与 R²
res = y - X @ w
print("MSE %.4f" % (res * res).mean())
dy = y - y.mean()
print("R² %.4f" % (1.0 - (res * res).sum() / (dy * dy).sum()))
实跑输出:
--------------------------------------------------------------------
线性回归:一条方程解出来,另一条一路走下来
--------------------------------------------------------------------
造 100 个样本,真实关系是 y = 3 + 2 x1 − 1.5 x2 + 噪声。噪声标准差 0.3,
所以任何方法都不可能把系数还原得一模一样——目标是**接近**,不是**等于**。
【一】正规方程:一步解出来
把「误差平方和最小」对每个系数求导令其为零,会得到一个线性方程组,解出来就是最优系数。
这个解叫正规方程:w = (X^T X)^{-1} X^T y。
项 真值 估计值 差
--------------------------------------
截距 w0 3.0000 3.0092 +0.0092
w1 2.0000 2.0141 +0.0141
w2 -1.5000 -1.4883 +0.0117
MSE 0.0727,决定系数 R² 0.9863。R² 的意思是「模型解释了目标值多少比例的波动」,
1.0 是完美,0.0 是等于只用平均值去猜。
【二】梯度下降:一步一步挪下去
学习率 第 1 轮 第 11 轮 第 51 轮 第 200 轮
--------------------------------------------------------
0.01 14.3187 11.8348 5.5519 0.4030
0.10 11.9565 1.7000 0.0735 0.0727
0.50 4.0654 0.0727 0.0727 0.0727
三个学习率都在往下走,但快慢差很多。学习率太小时走得稳却慢;太大时可能一步跨过头,
损失反而弹上去。**这是梯度下降唯一的调参难题。**
【三】特征缩放对梯度下降的影响:把 x2 放大 100 倍
输入 能用的学习率 第 200 轮 MSE
--------------------------------------------------------
x2 放大 100 倍,不缩放 0.00001 12.7825
标准化后再算 0.1 0.0727
不缩放时学习率只能压到十万分之一,200 轮从 14.3 走到 12.7825,还在半路上;
再往上提就直接发散。原因在等高线的形状:一个方向陡、一个方向平,
梯度下降会在窄谷底下来回横跳。
**标准化把等高线捏成接近正圆,学习率才敢放大,下降方向才顺手。**
【四】正规方程和梯度下降,选哪个
做法 要不要调学习率 要不要标准化 特征很多时
----------------------------------------------------------------------
正规方程 不用 不用 矩阵求逆很贵
梯度下降 要 要 每轮只过一遍数据
特征几十个,直接解方程;特征几万个(比如图像、文本),只能用梯度下降。
**这不是两条互斥的路,是同一件事的两种算法。**
正规方程的结果:3.0092、2.0141、−1.4883,真值是 3、2、−1.5。三个都差不到 0.02, 这就是噪声带来的估计误差。 MSE 0.0727,而噪声方差是 0.09——残差比噪声还小一点,说明模型没有额外引入误差,拟合得刚刚好。 梯度下降那一栏,三个学习率差了十倍速。lr = 0.01 走了 200 轮才到 0.4030; lr = 0.5 第 11 轮就到 0.0727 了。学习率小是稳但慢,大是快但可能一步跨过头。 这是梯度下降唯一的调参难题,没有通用答案,只能试。 第三段是这一节最值得记住的实验。把第二个特征乘 100,不缩放时学习率只能压到 0.00001, 200 轮下来 MSE 还有 12.7825;换成标准化,同样 200 轮、学习率 0.1,直接到 0.0727。 原因是等高线的形状。 两个特征尺度差 100 倍,损失函数的等高线是极扁的椭圆,梯度方向几乎垂直于「下山最快的方向」, 于是就横着来回跳,每一步都在正确的谷里,但走的是锯齿。 标准化把椭圆捏成接近正圆, 梯度就指向谷底了。 最后那张对比表回答了「选哪个」:正规方程不用调参、不用标准化,但特征一多就算不动; 梯度下降正好相反。这不是两条互斥的路,是同一件事的两种算法。
30 秒实验
- 把噪声标准差从 0.3 改成 1.0,正规方程解出的三个系数会离真值更远还是更近?先猜再改。
- 把学习率从 0.5 提到 1.0,200 轮之后 MSE 会变成多少?会不会发散?
- 给数据加一个离群点(比如把 y 的第 1 个值加上 20),正规方程解出的系数会怎么变?这说明了什么?
常见误区
- 以为系数的大小能直接比重要性。特征 1 的单位是米、特征 2 的单位是毫米, 系数差一千倍不代表特征 2 不重要。要比重要性必须先标准化,或者看别的指标。
- 用训练集上的 R² 报成绩。R² 在训练集上只会虚高,特征加得越多越高。 必须报测试集的 MSE 或 R²,这是 1.2 节那条纪律。
- 拿线性回归去做外推。数据只覆盖 x 在 0 到 10 的范围,你让它预测 x = 100,它照样给你一个数, 还很自信。线性回归没有任何「超出范围就不知道」的机制,这是它的死穴。
自测三问(点开对答案)
1. 为什么说「线性回归的线性是对系数线性,不是对特征线性」? —— 因为模型仍然是 y = w0 + w1 f1(x) + w2 f2(x),系数 w 只以一次方出现。 把 x 换成 x²、log x、x1x2 都算,只要系数还是一次的,求解方法完全一样。 2. 特征 2 的尺度是特征 1 的 100 倍,对正规方程和梯度下降分别有什么影响? —— 正规方程没影响,它一步解出来,跟尺度无关; 梯度下降影响很大,等高线变成极扁的椭圆,可用学习率要缩小两个数量级,收敛极慢。 3. R² = 0.9863 说明了什么?剩下的 1.37% 是什么? —— 说明模型解释了目标值 98.63% 的波动。 剩下的 1.37% 是噪声——这部分不由特征决定,任何模型都解释不掉。线性回归有一个毛病:特征一多、彼此相关,系数就会乱跳。2.2 节给它加一道「罚款」,这就是正则化。
- ☐ (本节完,下一节 2.2 算法 2:正则化)