本文是《LLM 时代的经典机器学习:只讲它在哪里重现》系列的第 2 篇(共十一篇)。上一篇:什么是学习——划分、泛化、过拟合与偏差-方差;下一篇:逻辑回归与奖励模型——每个分类头的原型。
线性回归是最简单的模型:用一条直线(高维时是一个平面)预测一个数。它简单到有闭式解——写出公式就能算出最优参数——但它同时是后面一切的原型:每一个神经网络的最后一层都是它(或它的分类版本),weight decay 就是它的 Ridge 正则,梯度下降、学习率、特征缩放、共线这些深度学习里天天遇到的问题,都可以在它身上用两个参数画出来看清楚。这一篇从 20 个点拟合一条直线开始,把这些概念一个一个建立起来。
全篇的核心问题是:
lstsq那一行到底解了什么方程,为什么它一步就能算出答案?1 梯度下降为什么有时候要几百步、有时候直接发散?2 LLM 训练配置里的weight_decay=0.1和一百年前的 Ridge 回归是什么关系?3
一、总览
1. 一条线、一个碗、一个惩罚项
本文按”从一个模型到一个训练过程”的顺序组织:先定义模型与损失(一条直线、残差的平方和),再看两种求解方式(闭式解一步到位、梯度下降一步一步走),再看梯度下降在什么地形上难走(特征量纲、共线),最后给损失加一个惩罚项(Ridge / Lasso)并证明它就是 weight decay。
| 概念 | 一句话 | 本文里的数字 |
|---|---|---|
| 模型 | \(\hat y = w^T x + b\),特征的加权和 | 20 个点:\(w = 1.525, b = 1.997\) |
| 损失 | 残差的平方和(MSE) | 最小二乘 38.7 vs 随手猜的直线 98 |
| 闭式解 | \((X^T X) w = X^T y\),解一个线性方程组 | 与梯度下降差 \(5 \times 10^{-10}\) |
| 梯度下降 | 沿损失下降最快的方向走 | 学习率上限 = 2 / 最大特征值 = 0.027 |
| 特征缩放 | 让碗变圆 | 条件数 540 万 → 1 |
| Ridge / Lasso | 惩罚大系数:\(L_2\) 压小、\(L_1\) 压到零 | 50 个特征,Lasso 归零 45 个 |
| weight decay | 每步把 \(w\) 乘 \((1 - 2\eta\lambda)\) | 与 Ridge 闭式解差 \(10^{-13}\) |
2. 本文的章节安排
| 章 | 主题 | 内容 |
|---|---|---|
| 二 | 模型与损失 | 一条直线、20 个点;残差;为什么用平方;多个特征就是矩阵乘 |
| 三 | 闭式解 | 一个参数时求导令为零;两个参数时是两个方程;写成矩阵就是正规方程;投影的几何 |
| 四 | 梯度下降 | 一个参数时用导数当指南针、手算四步;两个参数的碗与沟;学习率上限从一个数推到矩阵;GD vs SGD 到同一个答案 |
| 五 | 地形不好走的两种情形 | 特征量纲不同(沟太窄);两个特征共线(沟没有底) |
| 六 | Ridge 与 Lasso | 加惩罚项;系数路径;菱形与圆的几何;Lasso 做特征选择 |
| 七 | Ridge 就是 weight decay | 推导 + 数值对上;回到上一篇的 15 次多项式 |
| 八 | 为什么是平方 | 高斯噪声的最大似然;有离群点时换绝对值 |
| 九 | 本文小结 | |
| 十 | 自测 | 七道题 |
二、模型与损失
1. 一条直线、20 个点
20 个点,横坐标 \(x\) 在 0 到 10 之间,纵坐标 \(y\) 由 \(y = 1.5x + 2\) 加上标准差 2 的噪声生成——我们假装不知道 1.5 和 2,要从点里把它们找回来。模型是一条直线:
\[\hat y = w x + b\]- \(w\):斜率——\(x\) 每增加 1,\(\hat y\) 增加多少;
- \(b\):截距——\(x = 0\) 时的预测;
- \(\hat y\):预测值,戴帽子以区别于真实值 \(y\)。
哪条直线最好?先随手画两条,再看最小二乘选的那条:
2. 残差与平方和
每个点的预测值与真实值之差叫残差:\(r_i = y_i - \hat y_i\)。右图里每条竖线就是一个残差。评价一条直线好坏的标准是残差的平方和:
\[L(w, b) = \sum_{i=1}^{n} \big(y_i - (w x_i + b)\big)^2\]| 直线 | \(w\) | \(b\) | 残差平方和 |
|---|---|---|---|
| 直线 A | 1 | 5 | 97.6 |
| 直线 B | 2 | 0 | 89.2 |
| 最小二乘 | 1.525 | 1.997 | 38.7 |
最小二乘(least squares)就是找让这个平方和最小的 \((w, b)\)。找出来的 1.525 与 1.997 离真实的 1.5 与 2 很近——差的那点是 20 个点的噪声。平方和除以 \(n\) 就是上一篇的 MSE(这里 1.934)。
用 3 个点在纸上算一遍。20 个点的数字算不过来,把问题缩小到 3 个点:\((1, 3), (2, 5), (3, 8)\)。全文后面每个公式都会回到这 3 个点,所以先把它们记住。随手画一条 \(\hat y = 2x + 1\):
| \(x_i\) | \(y_i\) | 预测 \(2x_i + 1\) | 残差 \(r_i\) | \(r_i^2\) |
|---|---|---|---|---|
| 1 | 3 | 3 | 0 | 0 |
| 2 | 5 | 5 | 0 | 0 |
| 3 | 8 | 7 | 1 | 1 |
| 合计 | 1.0 |
这条线穿过前两个点、离第三个点差 1,平方和是 1.0——看起来已经不错。但最小二乘找到的是 \(w = 2.5, b = 1/3\)(第三章会算出它),三个残差是 \(0.167, -0.333, 0.167\),平方和 \(0.028 + 0.111 + 0.028 = \mathbf{0.167}\),比 1.0 小得多。它没有精确穿过任何一个点,却让三个点”都差一点”,而不是”两个点完全对、一个点差很多”——平方对大残差的惩罚是不成比例的(差 1 罚 1,差 0.333 只罚 0.111),所以最小二乘偏爱把误差摊平。第八章会说清为什么是平方而不是别的。
为什么要平方而不直接把残差加起来?残差有正有负,直接相加会互相抵消——一条离所有点都很远的线也可能残差之和为零。平方(或取绝对值)先把符号去掉,再相加才能衡量”总共差多少”。
3. 多个特征:矩阵写法
现实里输入通常不止一个数:预测房价要用面积、房龄、楼层……\(d\) 个特征 \(x = (x_1, \ldots, x_d)\),模型是它们的加权和:
\[\hat y = w_1 x_1 + w_2 x_2 + \cdots + w_d x_d + b = w^T x + b\]\(w^T x\) 读作”\(w\) 转置乘 \(x\)“,就是两个向量对应位置相乘再相加(点积):\(w = (1, 2), x = (3, 4)\) 时 \(w^T x = 1 \times 3 + 2 \times 4 = 11\)。上标 \(T\) 是转置——把行写成列、列写成行;写它只是为了让”一行乘一列”的矩阵乘法规则成立,不用想得更多。
把 \(n\) 个样本摞成一个 \(n \times d\) 的矩阵 \(X\)(每行一个样本),全部预测就是一次矩阵乘法 \(\hat y = X w + b\)——矩阵乘向量的规则是”\(X\) 的每一行与 \(w\) 做点积,得到一个数”,\(n\) 行就得到 \(n\) 个预测。再做一个常用的小动作:给 \(X\) 加一列全 1,\(b\) 就成了这一列的系数,可以并进 \(w\) 里——之后的公式都不再单独写 \(b\)。用 3 个点写出来:
\[X = \begin{pmatrix} 1 & 1 \\ 1 & 2 \\ 1 & 3 \end{pmatrix},\quad w = \begin{pmatrix} b \\ w \end{pmatrix},\quad X w = \begin{pmatrix} b + 1w \\ b + 2w \\ b + 3w \end{pmatrix},\quad y = \begin{pmatrix} 3 \\ 5 \\ 8 \end{pmatrix}\]\(Xw\) 的三行正好是三个点的预测值 \(w x_i + b\),只是排成了一列。代码里是一行:
def add_bias(X):
return np.c_[np.ones(len(X)), X] # 左边加一列 1,b 变成 w[0]
np.c_[...] 把几个数组按列拼起来;np.ones(len(X)) 是长度为 \(n\) 的全 1 向量。之后 X @ w 就是矩阵乘(@ 是 NumPy 的矩阵乘法运算符),一行算出全部预测。
三、闭式解
1. 先用一个参数看懂”求导令为零”
先把 \(b\) 拿掉,只留斜率 \(w\),模型是 \(\hat y = w x\)。损失 \(L(w) = \sum_i (y_i - w x_i)^2\) 是 \(w\) 的二次函数——展开后是 \(a w^2 + c w + e\) 的形式,\(a = \sum x_i^2 > 0\),图形是一个开口向上的抛物线。抛物线的最低点在哪里?斜率为零的地方:左边斜率为负(往右走会下降),右边斜率为正(往右走会上升),最低点恰好是斜率从负变正的那一点。函数在某点的斜率就是它的导数,所以”求最小值”变成”求导数、令它等于零、解方程”。
用 3 个点算:\(L(w) = (3 - w)^2 + (5 - 2w)^2 + (8 - 3w)^2\)。对每一项求导用链式法则——\((a - bw)^2\) 的导数是 \(2(a - bw) \cdot (-b)\):
\[L'(w) = -2 \cdot 1 \cdot (3 - w) - 2 \cdot 2 \cdot (5 - 2w) - 2 \cdot 3 \cdot (8 - 3w) = -2 \sum_i x_i (y_i - w x_i)\]令 \(L'(w) = 0\):\(\sum_i x_i y_i = w \sum_i x_i^2\),即 \(w = \frac{\sum x_i y_i}{\sum x_i^2} = \frac{3 + 10 + 24}{1 + 4 + 9} = \frac{37}{14} = 2.64\)。这是”过原点的最佳直线”的斜率。加回 \(b\) 后有两个未知数,就要对 \(w\) 和 \(b\) 各求一次导(对一个求导时把另一个当常数,这叫偏导数),得到两个方程:
\[\begin{aligned} \frac{\partial L}{\partial b} = 0 &\;\Longrightarrow\; n\, b + \Big(\sum x_i\Big) w = \sum y_i &&\Longrightarrow\; 3b + 6w = 16 \\ \frac{\partial L}{\partial w} = 0 &\;\Longrightarrow\; \Big(\sum x_i\Big) b + \Big(\sum x_i^2\Big) w = \sum x_i y_i &&\Longrightarrow\; 6b + 14w = 37 \end{aligned}\]两个一次方程、两个未知数,初中解法:第一式得 \(b = (16 - 6w)/3\),代入第二式 \(32 - 12w + 14w = 37\),\(w = 2.5\),\(b = 1/3\)。这就是第二章表里那条平方和 0.167 的直线。
2. 写成矩阵:正规方程
上面两个方程的系数——\(3, 6, 6, 14\) 和右边的 \(16, 37\)——不是凭空来的。用第二章的 \(X\) 算两个矩阵乘法:
\[X^T X = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 2 & 3 \end{pmatrix} \begin{pmatrix} 1 & 1 \\ 1 & 2 \\ 1 & 3 \end{pmatrix} = \begin{pmatrix} 3 & 6 \\ 6 & 14 \end{pmatrix}, \qquad X^T y = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 2 & 3 \end{pmatrix} \begin{pmatrix} 3 \\ 5 \\ 8 \end{pmatrix} = \begin{pmatrix} 16 \\ 37 \end{pmatrix}\](\(X^T X\) 左上角的 3 是”第一行 \((1,1,1)\) 点乘第一列 \((1,1,1)\)“;右下角的 14 是 \((1,2,3) \cdot (1,2,3) = 1 + 4 + 9\)。)两个手算的方程,正好就是 \(X^T X\, w = X^T y\) 这一个矩阵等式——矩阵只是把”\(d\) 个方程”打包成一行的记法。一般地,对 \(L(w) = \lVert X w - y \rVert^2\)(\(\lVert v \rVert^2\) 表示向量 \(v\) 各分量的平方和,这里就是残差平方和)求导:
\[\frac{\partial L}{\partial w} = 2 X^T (X w - y) = 0 \quad \Longrightarrow \quad X^T X\, w = X^T y\]这叫正规方程(normal equation)。它是一个 \(d \times d\) 的线性方程组——\(X^T X\) 是已知矩阵、\(X^T y\) 是已知向量、\(w\) 是未知数——解它就是最优解。写成”除过去”的形式(矩阵的”除”是乘逆矩阵):
\[w^* = (X^T X)^{-1} X^T y\]上一篇 fit_poly 里的 np.linalg.lstsq 解的就是这个(用更稳的数值方法,不真的求逆)。手写版是一行:
def fit_closed_form(X, y):
return np.linalg.solve(X.T @ X, X.T @ y) # 解 (XᵀX) w = Xᵀy
np.linalg.solve(A, c) 解线性方程组 \(A w = c\),对 3 个点它算的就是上面那个 \(2 \times 2\) 方程组,返回 [0.333, 2.5]。20 个点、1000 个特征,代码一个字不变——这是矩阵记法的全部价值。
3. 几何:投影
为什么导数为零就是最优?换个角度看。把 3 个点的 \(y = (3, 5, 8)\) 看成三维空间里的一个点(三个坐标各是一个样本的真实值);预测 \(Xw = b \cdot (1, 1, 1) + w \cdot (1, 2, 3)\) 是 \(X\) 两列的线性组合(各乘一个数再相加)。\(b, w\) 取遍所有值,\(Xw\) 扫出一个过原点的平面(列空间)——三维空间里两个方向能张出一个平面,但填不满整个空间。\(y\) 一般不在这个平面上(否则残差可以为零,三个点恰好共线)。离 \(y\) 最近的平面上的点,是 \(y\) 在平面上的垂直投影——就像你站在地面上方某点,离你最近的地面点是你正下方那个。”垂直”意味着残差 \(y - Xw\) 与平面里的每个方向(\(X\) 的每一列)都正交:\(X^T (y - Xw) = 0\)——这正是正规方程。用 3 个点验算:残差 \((0.167, -0.333, 0.167)\) 与 \((1, 1, 1)\) 的点积是 \(0.167 - 0.333 + 0.167 = 0\),与 \((1, 2, 3)\) 的点积是 \(0.167 - 0.667 + 0.5 = 0\)。最小二乘 = 把 \(y\) 投影到特征张成的平面上,残差是垂足到 \(y\) 的那段。
4. 什么时候解不出来
\(X^T X\) 要可逆,需要 \(X\) 的各列线性无关。两种情况会坏掉:特征比样本多(\(d > n\),\(X^T X\) 秩不足,有无穷多个解都能让残差为零——这就是上一篇”参数比样本多就能背下训练集”的代数版),以及两个特征几乎相同(第五章)。两者的解法都是第六章的 Ridge。
四、梯度下降
1. 一个参数的梯度下降:用导数当指南针
闭式解一步到位,但它要解一个 \(d \times d\) 的方程组,\(d\) 是几十亿时不可能;而且只有线性模型有闭式解。通用的办法是梯度下降:不解方程,而是从任意一点出发,每次朝”下坡”方向挪一小步,挪很多步到碗底。
“下坡方向”怎么知道?还是导数。一个参数时,导数是斜率:斜率为正说明右边更高,要往左走;斜率为负要往右走。所以每一步都是”减去导数乘一个小步长“——导数为正就减小 \(w\),为负就增大 \(w\),方向自动对。用最简单的碗 \(L(w) = (w - 3)^2\)(最低点在 3,导数 \(L'(w) = 2(w - 3)\))从 \(w = 0\) 出发,步长 \(\eta = 0.1\):
| 步 | 当前 \(w\) | 导数 \(2(w - 3)\) | 新 \(w = w - 0.1 \times\) 导数 |
|---|---|---|---|
| 1 | 0 | −6 | 0.6 |
| 2 | 0.6 | −4.8 | 1.08 |
| 3 | 1.08 | −3.84 | 1.464 |
| 4 | 1.464 | −3.07 | 1.771 |
每一步都朝 3 走,而且越靠近越慢(导数越小、步子越小),永远不会跨过去——这是学习率合适时的样子。步长 \(\eta\) 就是学习率。
多个参数时,”导数”变成梯度:对每个参数各求一次偏导,排成一个向量。梯度指向损失上升最快的方向,所以沿着负梯度走就是下降最快。更新公式与一个参数时完全一样,只是 \(w\) 和导数都成了向量:
\[w \leftarrow w - \eta \cdot \frac{\partial L}{\partial w}, \qquad \frac{\partial L}{\partial w} = \frac{2}{n} X^T (X w - y)\]梯度的表达式就是第三章正规方程左边那个 \(2X^T(Xw - y)\)(这里多除了 \(n\),用平均而不是求和,让学习率的量级不随样本数变)。\(Xw - y\) 是残差向量(预测减真实),\(X^T\) 乘它相当于”每个特征与残差做点积”——某个特征与残差正相关,说明这个特征的权重该调小,梯度在这一维就是正的。
2. 两个参数:碗与沟
对 20 个点的直线,损失只有两个参数 \((w, b)\),可以把整个”碗”画出来:横轴 \(w\)、纵轴 \(b\),每个位置的损失值用等高线表示——同一条线上的所有 \((w, b)\) 损失相同,像地图上的海拔线。再画上梯度下降走过的路:
怎么读这张图:橙星是第三章的闭式解,也就是碗底;椭圆越小损失越低;红点从右上角的起点出发,每一步是一个点。碗不是圆的,是一条斜的窄沟:椭圆很扁,沿短轴方向(大致是 \(b\) 方向)损失变化剧烈、沿长轴(沟底)方向变化缓慢。梯度总是垂直于等高线(等高线方向损失不变,垂直方向变化最快),所以第一步几乎垂直冲向沟底,之后只能在沟底沿着缓的方向一点点爬——GD 在窄沟里慢,是因为陡的方向限制了步长,缓的方向又要走很远。
为什么沟是斜的?因为 \(w\) 与 \(b\) 不独立:\(x\) 的均值是 5,把斜率 \(w\) 加大一点、同时把截距 \(b\) 减小 5 倍那么多,直线在 \(x = 5\) 附近几乎不动,损失几乎不变——这个”互相补偿”的方向就是沟底的方向。
3. 学习率的上限
步子太大会怎样?回到一个参数的碗 \(L(w) = (w - 3)^2\),把学习率改成 1.1:
| 步 | 当前 \(w\) | 导数 | 新 \(w = w - 1.1 \times\) 导数 |
|---|---|---|---|
| 1 | 0 | −6 | 6.6 |
| 2 | 6.6 | 7.2 | −1.32 |
| 3 | −1.32 | −8.64 | 8.184 |
| 4 | 8.184 | 10.37 | −3.22 |
一步跨过碗底落到对面更高的地方,再跨回来更高,在碗的两壁之间越跳越远——发散。到底多大算太大?每一步 \(w - 3\) 变成 \((1 - 2\eta)(w - 3)\):\(\eta = 0.1\) 时乘 0.8,越乘越小;\(\eta = 1.1\) 时乘 \(-1.2\),符号翻转、绝对值越乘越大。临界点是 \(\lvert 1 - 2\eta \rvert = 1\),即 \(\eta = 1\)。这里的 2 是这个碗的曲率——二阶导数 \(L''(w) = 2\),碗越陡(曲率越大)允许的学习率越小:一般地 \(\eta < 2 / L''\)。
多个参数时,碗在不同方向的曲率不同(窄沟:陡壁方向曲率大,沟底方向曲率小)。这些曲率由矩阵 \(H = \frac{2}{n} X^T X\)(损失的二阶导,叫 Hessian)的特征值给出——特征值就是碗沿各条主轴方向的曲率,最大的那个对应最陡的方向,记 \(\lambda_{\max}\)。步长必须让最陡的方向不发散,所以稳定条件是
\[\eta < \frac{2}{\lambda_{\max}}\]20 个点的直线:\(H\) 的特征值是 0.55 与 74.62,上限 \(2 / 74.62 = 0.027\)。上一节右图学习率 0.0265 贴着上限,所以震荡;超过 0.027 就飞出去。(3 个点的版本:\(H\) 的特征值是 0.24 与 11.09,上限 0.18。)两个特征值之比 \(\lambda_{\max} / \lambda_{\min} = 135\) 叫条件数(condition number),它就是”沟有多窄”:最陡与最缓方向的曲率之比。条件数 1 是圆碗,任何方向一样陡,一步到底;条件数越大,学习率被陡方向压得越小、缓方向要走的步数越多。
4. GD 与 SGD 到同一个答案
200 个样本、3 个特征的数据上,三种方法一起算:
def fit_gd(X, y, lr=0.1, steps=200, batch=None, seed=0):
r = np.random.default_rng(seed)
w = np.zeros(X.shape[1])
for _ in range(steps):
idx = slice(None) if batch is None else r.choice(len(y), batch, replace=False) # ① 全量,或随机抽一个小批
Xb, yb = X[idx], y[idx]
grad = 2 * Xb.T @ (Xb @ w - yb) / len(yb) # ② 这一批上的梯度
w -= lr * grad # ③ 走一步
return w
- ①
batch=None时每步用全部 200 个样本,是全量梯度下降(GD);给了batch=16就每步随机抽 16 个,是小批量随机梯度下降(SGD)——深度学习用的全是后者,因为全量数据算一次梯度太贵; - ② 小批上的梯度是全量梯度的一个有噪声的估计;
- ③ 同一条更新公式。
| 方法 | 系数 | 与闭式解的最大差 |
|---|---|---|
| 真实系数 | [24.66, 59.64, 56.82] | — |
| 闭式解 | [24.93, 59.98, 57.08] | 0 |
| GD 300 步 | [24.93, 59.98, 57.08] | \(4.9 \times 10^{-10}\) |
| SGD(16)300 步 | [24.37, 60.05, 56.92] | 0.56 |
sklearn.LinearRegression |
[24.93, 59.98, 57.08] | 0 |
怎么读这张图:纵轴是”当前损失比最优损失多多少”,用对数刻度(每格差 10 倍),所以越往下越接近最优。GD 走到闭式解,差 \(10^{-10}\)——同一个最优解。SGD 每步只算 16 个样本(算量是 GD 的 1/12),前 50 步与 GD 一样快,之后在最优点附近抖动、不再逼近——因为每步的梯度带噪声,噪声大小与学习率成正比、与 batch 大小成反比。深度学习里”学习率衰减”就是为了让这个抖动最后收小。线性回归是唯一一个能把闭式解、GD、SGD 放在一起对答案的地方,值得亲手跑一次。
五、地形不好走的两种情形
1. 特征量纲不同:沟太窄
两个特征,一个取值 0–1、一个 0–1000(比如”评分”与”字数”)。碗的形状由 \(X^T X\) 决定,大特征那个方向的曲率是小特征的 \(1000^2\) 倍:
| 特征 | 条件数 | 最大安全学习率 | 500 步后 MSE 比最优多 |
|---|---|---|---|
| 原始 | 5,421,535 | \(2.9 \times 10^{-6}\) | 1.18 |
| 标准化后 | 1 | 0.8 | 0.0000 |
原始特征下,安全学习率被大特征压到百万分之三,小特征那个方向 500 步几乎没动。标准化(standardization)——每个特征减去它的均值、再除以它的标准差:
\[x'_j = \frac{x_j - \mu_j}{\sigma_j}\]做完之后每个特征均值 0、标准差 1,”字数”和”评分”变成同一个量级。例如三篇文章字数 100、300、800:均值 400,标准差 \(\sqrt{((300)^2 + (100)^2 + (400)^2)/3} = 294\),标准化后是 \(-1.02, -0.34, 1.36\)。它让碗变圆,条件数变成 1,几十步收敛。注意均值和标准差要在训练集上算、再用同样的数去变换验证集与测试集——用全部数据算是上一篇说的信息泄漏。这就是为什么 scikit-learn 的例子总有一个 StandardScaler,也是深度学习里 BatchNorm / LayerNorm(L3 第二篇)要解决的同一个问题:让每个方向的曲率差不多。
2. 两个特征共线:沟没有底
第二种坏地形:两个特征几乎一样(\(x_2 \approx x_1\))。真实规律是 \(y = 2 x_1\),看最小二乘学到什么:
| \(x_2\) 与 \(x_1\) 的相关系数 | 无正则 \(w_1, w_2\) | Ridge \(\alpha = 1\) 的 \(w_1, w_2\) |
|---|---|---|
| 0.626 | 2.04, 0.00 | 2.00, 0.02 |
| 0.994 | 2.47, −0.45 | 1.46, 0.54 |
| 0.9999 | 7.88, −5.95 | 1.00, 0.92 |
两列几乎相同时,\(w_1 = 7.88, w_2 = -5.95\) 与 \(w_1 = 1, w_2 = 1\) 给出几乎一样的预测(只有它们的和 ≈ 2 是确定的),最小二乘就在这条”沟底”上随噪声乱选一个点——系数巨大、符号相反、换一批数据完全不同。这叫共线(collinearity),\(X^T X\) 接近不可逆。上一篇 17 次以上多项式曲线走平,就是 \(x^{17}\) 与 \(x^{18}\) 在 \([0,1]\) 上几乎共线。右列的 Ridge 把两个系数各分一半(1.00 与 0.92)——它是怎么做到的,下一章。
六、Ridge 与 Lasso
1. 加一个惩罚项
上一篇说正则化是”把模型拉向简单解”。对线性回归,”简单”就是系数小。在损失后面加一项惩罚大系数:
\[\text{Ridge:}\quad \min_w \lVert X w - y \rVert^2 + \alpha \lVert w \rVert_2^2 \qquad\qquad \text{Lasso:}\quad \min_w \lVert X w - y \rVert^2 + \alpha \lVert w \rVert_1\]- \(\lVert w \rVert_2^2 = \sum_j w_j^2\):系数的平方和(\(L_2\) 范数的平方);
- \(\lVert w \rVert_1 = \sum_j \lvert w_j \rvert\):系数绝对值之和(\(L_1\) 范数);
- \(\alpha\):惩罚的强度。\(\alpha = 0\) 回到普通最小二乘,\(\alpha \to \infty\) 所有系数趋于零。
Ridge 仍有闭式解:对新目标求导令为零,惩罚项 \(\alpha \sum_j w_j^2\) 的导数是 \(2\alpha w\),正规方程变成 \((X^T X + \alpha I) w = X^T y\)——\(I\) 是单位矩阵,所以只是在 \(X^T X\) 的对角线上各加了 \(\alpha\)。用 3 个点、\(\alpha = 1\),惯例上不惩罚截距 \(b\)(它只是把直线整体上下平移,不算”复杂”),所以只给 \(w\) 那一格加:
\[\begin{pmatrix} 3 & 6 \\ 6 & 14 \end{pmatrix} \;\to\; \begin{pmatrix} 3 & 6 \\ 6 & 15 \end{pmatrix}, \qquad \text{解得 } (b, w) = (2.0, 1.67)\]斜率从无正则的 2.5 被压到 1.67,截距则补上去变成 2.0——直线更平了,这就是”压小系数”(3 个点太少,\(\alpha = 1\) 相对而言很重;50 个特征的实验里 \(\alpha\) 的合适量级会用验证集选)。为什么加对角线就一定可逆?共线时 \(X^T X\) 在沟底方向的曲率是 0(那个方向的特征值为零,沟没有底),加 \(\alpha I\) 让每个方向的曲率都至少是 \(\alpha\)——沟底被抬起来变成了碗底,最低点于是唯一。这就是它救共线与 \(d > n\) 的原理。Lasso 的绝对值在零点不可导(左边斜率 \(-1\)、右边 \(+1\),没有唯一的斜率),没有闭式解,用坐标下降等算法。
2. 系数路径:压小 vs 压到零
50 个特征、只有 5 个真有用、100 个样本。把 \(\alpha\) 从小到大扫一遍,画每个系数怎么变:
| 模型 | 恰好为 0 的系数 | 系数绝对值均值 | 最大 |
|---|---|---|---|
| 无正则 | 0 / 50 | 4.03 | 67.3 |
| Ridge \(\alpha = 10\) | 0 / 50 | 3.74 | 56.8 |
| Lasso \(\alpha = 1\) | 36 / 50 | 2.84 | 65.7 |
| Lasso \(\alpha = 5\) | 45 / 50 | 2.31 | 61.4 |
- 无正则:45 个没用的特征也得到了非零系数(均值 4)——模型在用噪声特征拟合噪声,这是过拟合的样子;
- Ridge:所有系数一起压小,但没有一个恰好为零;
- Lasso:把 36–45 个没用的特征压到恰好为零,只留下真正有用的几个——稀疏。\(\alpha = 5\) 时 45/50 为零,恰好对应 5 个有用特征。Lasso 因此是一种特征选择。
3. 几何:为什么 \(L_1\) 能压到零
“加惩罚项 \(\alpha \lVert w \rVert\)“等价于”在约束 \(\lVert w \rVert \le c\) 下最小化损失”——惩罚越重(\(\alpha\) 越大),相当于允许的范围 \(c\) 越小;两种说法选出的是同一个 \(w\)。约束的说法好画图:把 \(w\) 限制在原点附近一个区域里,在区域里找损失最低的点。两个参数时能画出来:
\(L_1\) 的约束区 \(\lvert w_1 \rvert + \lvert w_2 \rvert \le c\) 是一个菱形,角在坐标轴上;损失的等高线从最优点向外扩,多数情况下先碰到的是角——角上某个坐标恰好为零。\(L_2\) 的约束区是圆,没有角,碰到哪里都行,两个坐标一般都不为零。高维时菱形变成有很多尖角的多面体,”碰到角”的概率更大,Lasso 归零的系数就更多。另一个说法:\(L_1\) 惩罚的导数在零附近仍是 \(\pm\alpha\)(不随 \(w\) 变小而减弱),一直有动力把系数推到零并停在零;\(L_2\) 的导数 \(2\alpha w\) 随 \(w\) 变小而变小,越接近零推力越弱,永远到不了。
七、Ridge 就是 weight decay
1. 推导
对 Ridge 的目标求梯度:
\[\frac{\partial}{\partial w}\Big[\frac{1}{n}\lVert X w - y \rVert^2 + \lambda \lVert w \rVert^2\Big] = \underbrace{\frac{2}{n} X^T (X w - y)}_{\text{普通梯度 } g} + 2 \lambda w\]代进梯度下降:
\[w \leftarrow w - \eta (g + 2\lambda w) = (1 - 2\eta\lambda)\, w - \eta g\]每步先把 \(w\) 乘一个略小于 1 的数(衰减),再走一步普通梯度。这就是 weight decay(权重衰减)——名字来自这个”先缩小”的动作。代码里是一行的差别:
w = (1 - 2 * lr * lam) * w - lr * grad # weight decay:先衰减,再走普通梯度
2. 数值对上
200 个样本、5 个标准化后的特征,\(\lambda = 0.5\):
| 方法 | 系数 |
|---|---|
| Ridge 闭式解 \((X^T X / n + \lambda I)^{-1} X^T y / n\) | [48.351, 58.067, 51.452, 4.740, 49.103] |
| GD + weight decay 2000 步 | [48.351, 58.067, 51.452, 4.740, 49.103] |
| 无正则闭式解 | [69.783, 88.874, 80.168, 10.382, 75.429] |
两者最大差 \(9 \times 10^{-14}\)。LLM 训练配置里的 weight_decay=0.1 就是 Ridge 在几十亿参数上的形态(AdamW 里它与 \(L_2\) 正则有一点区别——衰减不经过 Adam 的自适应缩放,L3 第三篇讲)。LLM 几乎不用 Lasso:稀疏性对稠密的 Transformer 权重没有直接用处,它的用武之地是特征工程与剪枝。
3. 回到上一篇的 15 次多项式
上一篇 15 次多项式过拟合,系数大到 2020。不减容量、只加 Ridge:
| \(\alpha\) | 训练 MSE | 验证 MSE | 最大系数 |
|---|---|---|---|
| 0 | 0.049 | 0.143 | 2020.5 |
| 0.01 | 0.071 | 0.108 | 4.6 |
| 10 | 0.196 | 0.222 | 0.3 |
正则项不改变模型容量(还是 15 次),只是不让高次系数变大——曲线于是平滑。\(\alpha\) 太大又变成欠拟合(右图)。\(\alpha\) 是一个超参数,用上一篇的验证集选。
八、为什么是平方
1. 高斯噪声的最大似然
为什么损失用残差的平方,不是绝对值、不是四次方?第二章给了一个朴素理由(去掉符号),但绝对值也能去掉符号。真正的理由是一个关于噪声的假设。
假设数据是”直线加高斯噪声”:\(y = w^T x + \varepsilon\),\(\varepsilon \sim \mathcal{N}(0, \sigma^2)\)——每个点的 \(y\) 是直线上的值加一个随机扰动 \(\varepsilon\),扰动服从均值 0、标准差 \(\sigma\) 的高斯分布(正态分布,钟形曲线:小扰动常见、大扰动罕见,离 0 越远概率越小,下降得很快)。那么观察到某个 \(y\) 的概率密度是
\[p(y \mid x) = \frac{1}{\sqrt{2\pi}\sigma} \exp\Big(-\frac{(y - w^T x)^2}{2\sigma^2}\Big)\]\(\exp(z)\) 就是 \(e^z\)(\(e \approx 2.718\));指数上是负的”残差平方除以 \(2\sigma^2\)“,残差为 0 时密度最大,残差越大密度按平方指数式衰减。现在反过来想:给定 20 个点,哪条直线最”可能”产生它们?每个点的密度相乘就是全部点一起出现的可能性(假设各点噪声独立),选让这个乘积最大的 \(w\)——这叫最大似然估计(maximum likelihood estimation,MLE)。
20 个很小的数相乘不好处理,取对数:\(\log(ab) = \log a + \log b\),乘积变成和;对数是单调增的,让乘积最大等价于让对数和最大。再加个负号变成”让某个东西最小”,好与损失函数的习惯一致。对上面的密度取负对数,\(\log \exp(z) = z\),得
\[-\log p(y \mid x) = \frac{(y - w^T x)^2}{2\sigma^2} + \underbrace{\log(\sqrt{2\pi}\sigma)}_{\text{与 } w \text{ 无关的常数}}\]对全部样本求和、丢掉常数和系数 \(1/2\sigma^2\),就是 \(\sum_i (y_i - w^T x_i)^2\)——最小二乘就是高斯噪声假设下的最大似然估计。平方来自高斯密度指数上的那个平方。换成拉普拉斯噪声(密度 \(\propto e^{-\lvert y - w^T x \rvert / b}\),指数上是绝对值)做同样的推导,就得到绝对值误差。
2. 离群点
这个假设的代价:高斯分布的尾巴很薄,它”不相信”会有很大的残差,所以一个很大的残差(平方后更大)会把直线狠狠拉过去。20 个点里改两个成离群点:
| 情形 | 斜率 \(w\) |
|---|---|
| 无离群点,平方误差 | 1.57 |
| 两个离群点,平方误差 | 0.82 |
| 两个离群点,绝对值误差 | 1.57 |
数据里有离群点(标注错误、爬虫抓到的乱码)时,平方误差会被少数点带偏,绝对值误差(或 Huber 损失——小残差用平方、大残差用绝对值)稳得多。这是”损失函数是对噪声分布的假设”的第一个实例:选损失,就是在说你相信误差长什么样。
九、本文小结
- 模型 \(\hat y = w^T x + b\),加一列 1 把 \(b\) 并进 \(w\);损失是残差平方和,20 个点的最小二乘直线 38.7 vs 随手画的 90 多。
- 闭式解:对损失求导令为零得正规方程 \(X^T X w = X^T y\),几何上是把 \(y\) 投影到特征张成的平面;\(d > n\) 或特征共线时 \(X^T X\) 不可逆。
- 梯度下降:损失是一个碗,GD 沿负梯度走;学习率上限 \(2 / \lambda_{\max}\)(20 个点的直线是 0.027),条件数 = 沟有多窄;GD 与闭式解差 \(10^{-10}\),SGD 用 1/12 的算量到同一邻域但在附近抖动(噪声 ∝ 学习率 / batch)。
- 两种坏地形:特征量纲不同(条件数 540 万,标准化后 1);特征共线(系数 7.88 / −5.95 乱跳,Ridge 各分一半)。
- Ridge \(+\alpha \lVert w \rVert_2^2\) 把所有系数一起压小、让 \(X^T X + \alpha I\) 可逆;Lasso \(+\alpha \lVert w \rVert_1\) 把无用系数压到恰好为零(50 个特征归零 45 个)——菱形的角在坐标轴上。
- Ridge = weight decay:\(w \leftarrow (1 - 2\eta\lambda) w - \eta g\),与闭式解差 \(10^{-13}\);15 次多项式加 \(\alpha = 0.01\) 的 Ridge,最大系数 2021 → 5,验证 MSE 0.143 → 0.108,容量不变、曲线平滑。
- 平方误差 = 高斯噪声的最大似然;有离群点时它被拉偏(斜率 1.57 → 0.82),绝对值误差不受影响。
配套代码:本文全部数字与图由 classical-ml/02_linear_regression.py 产生(line / surface / gd / scaling / collinear / paths / geometry / wd / smooth / robust 十个子实验),CPU 上十几秒跑完。
十、自测
-
三个点 \((0, 1), (1, 3), (2, 4)\),用第三章的两个方程手算最小二乘直线。
答案
\(n = 3, \sum x = 3, \sum x^2 = 5, \sum y = 8, \sum xy = 0 + 3 + 8 = 11\)。方程 \(3b + 3w = 8\)、\(3b + 5w = 11\),相减得 \(2w = 3\),\(w = 1.5\),\(b = (8 - 4.5)/3 = 1.17\)。直线 \(\hat y = 1.5x + 1.17\),残差 \(-0.17, 0.33, -0.17\)。
-
20 个点、一个特征,闭式解要解几元几次方程?1000 个特征呢?
答案
加偏置后 \(w\) 有 2 个未知数,解 2×2 的线性方程组;1000 个特征是 1001×1001。线性方程组,一次,一步解出。
-
梯度下降的损失曲线先降后突然变成 NaN。最可能的原因?怎么算一个安全的学习率?
答案
学习率超过 \(2 / \lambda_{\max}\) 发散了;算 \(\frac{2}{n} X^T X\) 的最大特征值取 2 除以它,或先标准化特征让条件数变小。
-
一个特征是”字数”(0–10000),一个是”评分”(1–5),不标准化直接梯度下降会怎样?
答案
碗变成极窄的沟,安全学习率被”字数”限制到极小,”评分”的系数几乎不动;标准化后条件数接近 1。
-
100 个样本、1000 个特征,无正则的线性回归会发生什么?Ridge 和 Lasso 各怎么救?
答案
\(X^T X\) 不可逆 / 无穷多解、严重过拟合;Ridge 加 \(\alpha I\) 让解唯一并压小系数,Lasso 选出少数特征。
-
为什么 Lasso 能把系数压到恰好为零而 Ridge 不能?用几何说一句。
答案
\(L_1\) 的约束区是菱形、角在坐标轴上,等高线先碰到角;\(L_2\) 是圆、没有角。(或:\(L_1\) 的推力在零附近不减弱。)
-
AdamW 里
weight_decay=0.1,从线性回归的角度看它在做什么?为什么 LLM 不用 Lasso?答案
每步把权重乘 \((1 - \eta \lambda)\)——Ridge 的梯度下降形态;Lasso 的稀疏性对稠密 Transformer 权重没有直接用处。
下一篇
下一篇把线性回归的输出过一个 sigmoid,变成预测概率的逻辑回归——每一个神经网络分类头的原型;然后证明奖励模型就是一个作用在”两个回答的特征差”上的逻辑回归,它的准确率上限由标注一致性决定。
-
它解的是正规方程 \(X^T X w = X^T y\)——对残差平方和求导令为零得到的一个 \(d \times d\) 线性方程组;几何上是把 \(y\) 投影到特征张成的平面。因为损失是二次的、碗只有一个底,所以一步能解出;\(d > n\) 或特征共线时 \(X^T X\) 不可逆,要加 Ridge。详见第三章。 ↩
-
损失是一个碗,但通常是一条斜的窄沟:陡的方向限制学习率(上限 \(2 / \lambda_{\max}\),20 个点的直线是 0.027),缓的方向要走很远——条件数越大越慢;超过上限就在沟壁间越跳越高、发散。特征量纲不同(条件数 540 万)与特征共线是两种典型的坏地形,标准化与 Ridge 分别修它们。详见第四章、第五章。 ↩
-
同一个东西。Ridge 在损失里加 \(\lambda \lVert w \rVert^2\),梯度多出 \(2\lambda w\),代进梯度下降就是 \(w \leftarrow (1 - 2\eta\lambda) w - \eta g\)——每步先把权重乘一个略小于 1 的数,这就是 weight decay;数值上与 Ridge 闭式解差 \(10^{-13}\)。详见第七章。 ↩
本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/linear-regression-least-squares-ridge-and-lasso.html)的前提下,欢迎各种形式的转载、翻译或商业引用。
COMMENTS
评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。