📘 第4章 权重和寻找最优解
📄 一句话概括

回归分析是把「找一条线」变成一套能报数字的流程:系数、标准误、t 值、R²。四个数各管一头, 缺一个就会误判。 这一节的重点不是公式,是三件常被忽略的事: R² 只涨不跌、共线性让解释失效、参数个数追上样本个数就是灾难。

📝 本节和前面几节的关系

上一节说清了一条线的能力边界。这一节把「怎么用这条线」讲成一套能报数字的流程。 标准误这一套后面会反复用:第 7 章比两个模型谁更好,第 8 章判断策略差异是不是噪声, 用的都是同一个思路 —— 先算不确定度,再比大小。

一、四个数字,各管一头

一份正经的回归结果至少报四样东西,它们回答的是四个不同的问题:

数字 回答的问题
系数 自变量每变一个单位,y 平均变多少
标准误 换一批数据重来,这个系数会晃多少
t 值 系数除以标准误,信号比噪声
R² 这条线解释了多少波动
标准误不是估的,是算的:s²(X'X)⁻¹ 的对角线开根号。其中 s² 是残差方差。
这个式子里 (X'X)⁻¹ 那一项,后面会变成共线性的全部麻烦来源。
t 值的用法很朴素:绝对值大于 2 就大致算「显著」。这条经验法则来自正态分布 ——
两倍标准差以外的概率约 5%。它不是金标准,但对大多数样本量够的场合够用。

二、标准误和 R² 是两件事

Demo 第一段:40 个点,真规律 y = 3 + 1.8x。 拟合出斜率 1.7880、标准误 0.0305、t 值 58.59、R² 0.9891。 斜率比真值小 0.0120,看着像有偏差。 可把它除以标准误,只有 0.39 —— 这点偏差完全在噪声能解释的范围里。 R² 0.9891 说的是「这条线解释了 98.9% 的波动」; 标准误 0.0305 说的是「换一批数据重来,斜率会在 1.79 上下晃 0.03」。一个讲拟合得好不好, 一个讲结论稳不稳。两个都重要,但用途不同。 做预测时看 R²,做决策时看标准误。「这个广告投下去能涨多少销量」要的是系数加标准误, 「这条线能不能替代实验」才看 R²。

三、加一个没用的特征,R² 一定涨

Demo 第二段往模型里塞了一串纯随机数。结果:

原模型 新模型
R² 0.9891 0.9894
x1 的斜率 1.7880 1.7864
随机数的 t 值 — +1.11
R² 涨了 0.0003 —— 涨得少,但它只会涨不会跌。这是 R² 的结构决定的:多加一列,
残差平方和一定不会变大。所以拿 R² 比模型,永远偏向更复杂的那个。
真正的判据是那个 t 值:1.11。约等于 1 的 t 值说明这个系数跟零分不出来 ——
这一列没有携带信息。
这就是为什么正经的回归表格从不只报 R²。想比模型好坏,得用「调整 R²」(按参数个数打折)、AIC、
或者干脆留出测试集。这三个办法做的是同一件事:惩罚复杂度。

四、共线性:预测还挺准,解释全废

Demo 第三段造了一个 x2 = 0.98·x1,跟 x1 的相关系数 0.9990。 x2 一点用都没有,规律还是 y = 3 + 1.8x1。

模型 x1 系数 标准误 t 值
只放 x1 1.7880 0.0305 58.59
x1 和 x2 一起 0.2342 0.7128 0.33
R² 从 0.9891 变成 0.9888,几乎没动 —— 模型没变强也没变弱。
可 x1 的系数从 1.7880 掉到 0.2342,标准误从 0.0305 涨到 0.7128,涨了 23 倍,
t 值从 58.59 掉到 0.33。
要两个系数一起看才明白:x2 的系数是 1.5853,两个数「合用」一个 1.8。
最小二乘不知道该把功劳分给谁,于是给出一个随机的分法。换个随机种子,这两个数会跑到别的地方去。
这就是共线性。它的后果不是预测变差(R² 还在 0.99),而是解释变废:
你说不出到底是 x1 在起作用还是 x2。
而且两个 t 值都变得不可信 —— 明明两个变量合起来非常显著,单独看却都不显著。
判断办法很土但很准:把两个几乎一样的特征分别单独放进模型,
如果系数差得离谱,它们就共线了。真实数据里共线性极常见(身高和体重、气温和用电量、
同一种指标的不同测法),看到系数符号跟常识反着走,第一个要怀疑的就是它。

五、参数个数追上样本个数

Demo 第四段:10 个点,来自 sin(2πx) 加噪声,用 9 次多项式去配。 训练点上最大误差 4.25e-11,等于零。新点上最大误差 1.10 —— sin 的取值本来只在 −1 到 1 之间。 Demo 第五段逐次加高,看两条误差怎么分岔:

次数 训练误差 新点误差
1 0.6263 0.7554
3 0.1913 0.3341
5 0.1756 0.3536
7 0.1600 0.3410
9 0.0000 1.1028
左边一列整体往下,最后落到 0;右边一列最低在 3 次(0.3341),之后一路涨回 1.1028。
两条曲线分岔的地方,就是「开始记噪声」的地方。
于是有一句反直觉的话:训练误差最小的模型,一定不是最好的模型。因为参数个数追上了样本个数,
模型就有足够的自由度把每个点都穿过去 —— 包括那些本来就是噪声的点。
真实的数据分析里没有 sin 可以对照,所以靠的是另一套办法:留出一部分数据不看,
用没看过的数据来挑次数。这就是训练集/验证集/测试集的来历,第 7 章会把它写成固定流程。

六、「回归」这个词是怎么来的

1886 年,高尔顿量了一千多对父子的身高,画成散点图,发现一件事: 父亲很高,儿子也高,但没有父亲那么高;父亲很矮,儿子也矮,但没那么矮。整体在往中间收。 他把这个现象叫「向平均回归」。后来的统计学家把这个词借过来, 专指「用一条线去拟合条件平均」—— 名字里那个「回归」早就不指身高了,可它留了下来。 顺带说一句:高尔顿当年的解释是错的。不是「高个子的儿子会变矮」,而是身高由很多因素共同决定, 极端值必然是「很多因素同时往一个方向偏」的结果,下一代这些因素重新抽一遍,自然就回到中间。 这叫「回归到均值」,是统计里最容易误解的一句话 —— 它说的是群体趋势,不是任何一个人的命运。

七、动手 Demo:四个数字、一个离群点、一条九次曲线

ols 这个函数把最小二乘的「附带产物」一次算全了:系数、系数标准误、R²。 核心只有三行 —— 解方程、算残差、用 s²(X'X)⁻¹ 求标准误。 后面几段共用同一批数据:40 个点、一条真直线,然后分别往里塞随机数、塞一个几乎重复的列、 把次数一路加高。每一段只改一个东西,这样差别才归因得清楚。

def ols(X, y):
    """最小二乘,顺带把标准误和 R² 一起算出来。"""
    n, k = X.shape
    w, *_ = np.linalg.lstsq(X, y, rcond=None)
    res = y - X.dot(w)
    sse = float(res.dot(res))
    s2 = sse / (n - k)                    # 残差方差
    cov = s2 * np.linalg.pinv(X.T.dot(X)) # 系数协方差阵
    se = np.sqrt(np.diag(cov))            # 对角线开根号就是标准误
    r2 = 1.0 - sse / float(((y - y.mean()) ** 2).sum())
    return w, se, r2

# 逐次加高,看训练误差和新点误差怎么分岔
for d in range(1, 10):
    X = np.column_stack([xs ** p for p in range(d + 1)])
    w, *_ = np.linalg.lstsq(X, ys, rcond=None)
    e_in = np.abs(X.dot(w) - ys).max()      # 训练点上
    e_out = np.abs(Xt.dot(w) - truth).max() # 新点上

实跑输出:

--------------------------------------------------------------------
把一堆点压成一条线
--------------------------------------------------------------------
==============================================================

[1] 一元回归:斜率、截距,还有「这个斜率有多准」

  40 个点,y = 3 + 1.8x 再加一点噪声。最小二乘给出的结果:

                  估计值        标准误        t 值
    ----------------------------------------------------------
    截距          3.0616        0.1773        17.27
    斜率          1.7880        0.0305        58.59

  R² = 0.9891,残差标准误 = 0.5713,自由度 = 38

  斜率 1.7880,真值 1.8,差 0.0120;它的标准误是 0.0305。
  差除以标准误得 0.39 —— 这点偏差完全在噪声能解释的范围里。

  R² 和标准误说的不是一件事:
  R² 说「这条线解释了多少波动」,标准误说「换一批数据重来,
  这个系数会晃多少」。前者是拟合得好不好,后者是结论稳不稳。

  标准误是算出来的,不是估的:s²(X'X)⁻¹ 的对角线开根号。
  这个式子后面还要用——[3] 段那个「涨了二十多倍」就是从它来的。

[2] 加一个跟 y 没关系的特征

  原模型:y ~ x1
    R² = 0.9891,残差标准误 0.5713,斜率标准误 0.0305
  新模型:y ~ x1 + 一串纯随机数
    R² = 0.9894,残差标准误 0.5696,斜率标准误 0.0305

                    原模型          新模型
    ----------------------------------------------
    x1 的斜率       1.7880          1.7864
    随机数的斜率    —               +0.1361
    随机数的 t 值   —               +1.11

  R² 从 0.9891 涨到 0.9894,涨了 0.0004。看着是进步,其实是假的:
  随机数总能蹭到一点点相关性,R² 只会涨不会跌。
  这就是为什么报回归结果一定要连标准误和 t 值一起报——
  那个随机数的 t 值只有 1.11,一眼就看得出它没用。

[3] 两个自变量高度相关会怎样

  造一个 x2 = 0.98·x1 再加微小扰动,跟 x1 的相关系数 0.9990。
  真实规律还是 y = 3 + 1.8·x1,x2 一点用都没有。

    模型              x1 系数     标准误      t 值
    ------------------------------------------------------
    只放 x1           1.7880      0.0305      58.59
    x1 和 x2 一起     0.2342      0.7128      0.33

  R² 从 0.9891 变成 0.9888,几乎没动——模型没变强。
  可 x1 的系数从 1.7880 掉到 0.2342,标准误从 0.0305 涨到 0.7128,涨了 23 倍,
  t 值从 58.6 掉到 0.3。
  同一模型里 x2 的系数 1.5853、标准误 0.7262、t 值 2.18。

  两个系数一起看才明白:x2 其实就是 x1,它们只能「合用」一个 1.8。
  最小二乘不知道该把功劳分给谁,于是给出一个随机的分法——
  换个随机种子,这两个数会跑到别的地方去,而 R² 一动不动。

  这叫共线性。后果不是预测变差(R² 还在 0.99),而是解释变废:
  你说不出到底是 x1 在起作用还是 x2,两个 t 值也都不可信了。
  判断办法很土但很准:把两个几乎一样的特征分别单独放进模型,
  如果系数差得离谱,它们就共线了。

[4] 给 10 个点配一条 9 次曲线

  10 个点,来自 sin(2πx) 加一点噪声。用 9 次多项式去配:

    点数 10,待定系数 10 个
    训练点上最大误差 4.25e-11
    60 个新点上最大误差 1.1028

  训练误差 4.25e-11,等于零——10 个点配 10 个系数,穿过它们是做得到的。
  可在新点上它错到 1.10。sin 的取值本来只在 -1 到 1 之间。

  看几个位置(新点都是没参与拟合的):

    x = 0.03:真值 +0.166,9 次曲线 -0.181,差 -0.347
    x = 0.26:真值 +0.998,9 次曲线 +0.860,差 -0.138
    x = 0.51:真值 -0.052,9 次曲线 +0.075,差 +0.128
    x = 0.74:真值 -0.998,9 次曲线 -0.659,差 +0.339
    x = 0.97:真值 -0.166,9 次曲线 +0.936,差 +1.103

  这不是「拟合得不够好」,是「拟合得太好了」。
  参数个数追上样本个数,模型有了足够的自由度把噪声也当成规律记住。
  于是有一句反直觉的话:训练误差最小的模型,一定不是最好的模型。

[5] 逐次加高:训练误差一路降,新点误差先降后升

    次数        训练误差    新点误差
    ----------------------------------------
    1           0.6263      0.7554
    2           0.6800      0.7944
    3           0.1913      0.3341
    4           0.1873      0.3854
    5           0.1756      0.3536
    6           0.1539      0.3507
    7           0.1600      0.3410
    8           0.0768      0.5259
    9           0.0000      1.1028

  左边一列整体往下,最后落到 0(中间有两处小反弹),
  右边一列最低出现在 3 次(0.3341),之后一路涨回 1.1028。
  两条曲线分岔的地方,就是「开始记噪声」的地方。

  真实的数据分析里没有 sin 可以对照,所以靠的是另一套办法:
  留出一部分数据不看(训练/验证/测试),用没看过的数据来挑次数。
  这也是第 7 章讲有监督学习时反复要用的那套流程。

[6] 「回归」这个词是怎么来的

  1886 年,高尔顿量了一千多对父子的身高,画成散点图,发现一件事:
  父亲很高,儿子也高,但没有父亲那么高;父亲很矮,儿子也矮,
  但没那么矮。整体在往中间收。

  他把这个现象叫「向平均回归」(regression toward mediocrity)。
  后来的统计学家把这个词借过来,专指「用一条线去拟合条件平均」。
  名字里那个「回归」早就不指身高了,可它留了下来。

  顺带说一句:高尔顿当年的解释是错的。
  不是「高个子的儿子会变矮」,而是身高由很多因素共同决定,
  极端值必然是「很多因素同时往一个方向偏」的结果,
  下一代这些因素重新抽一遍,自然就回到中间。
  这叫「回归到均值」,是统计里最容易误解的一句话——
  它说的是群体趋势,不是任何一个人的命运。

第一段那张表要连 t 值一起读。截距 3.0616、斜率 1.7880,真值是 3 和 1.8, 差得都不多,而且标准误很小 —— 这叫「估得准」。如果标准误很大,同样的系数就只能叫「估了一下」。 第二段是 R² 的软肋。塞一串随机数,R² 从 0.9891 涨到 0.9894。 涨得少,可它只涨不跌 —— 这个方向性是结构性的,不是这次运气好。 第三段那张两行表,是全节最该记住的。同一个变量 x1,单独放进去系数 1.7880、t 值 58.59; 再加一个几乎一样的 x2,系数变成 0.2342、t 值 0.33。R² 却几乎没变。 这一行数字把「预测」和「解释」的区别说尽了。 第四段那个 4.25e-11 不是精度问题,是数学上必然。10 个点配 10 个系数,方程组恰好有唯一解, 穿过去是能做到的。所以「训练误差为零」这件事本身不提供任何信息 —— 它只是说「参数够多」。 第五段那张表要看两列的走势,不是看具体数字。左列单调向下、右列先降后升, 这个 X 形是过拟合的标准长相,换任何数据、任何模型都会再见到它。 第六段是历史,别跳过。「回归到均值」这个说法被误用的次数, 比这一节其他所有概念加起来都多 —— 它描述群体,不预测个人。

30 秒实验

  1. 把第一段的 n 从 40 改成 400,重跑。标准误会缩小到原来的三分之一左右(跟根号 n 成反比), 系数的估计值会更贴近真值 —— 这是「多测几个点」的收益。
  2. 第三段把 x2 的扰动从 0.15 改成 3.0(相关性降到 0.9 左右),重跑。 共线性变弱,x1 的系数应该明显往 1.7880 回,标准误也应该降下来。
  3. 第五段把新点改成「训练点本身」,也就是用训练误差当判据。 你会发现 9 次永远最好 —— 这就是只看训练误差会选错模型的最小例子。

常见误区

  1. 以为 R² 越高模型越好。加一个纯随机列,R² 也会涨。 比模型要么用调整 R²,要么用测试集,不能只看 R² 本身。
  2. 以为系数大就重要、系数小就不重要。共线性下系数会乱跑, 一个 0.23 和一个 1.59 可能只是同一个 1.8 被劈成两半。看重要性要看标准化的系数, 或者看拿掉它之后 R² 掉多少。
  3. 以为标准误只是「误差范围」,不影响结论。标准误涨 23 倍,t 值就从 58 掉到 0.33, 同一个变量从「铁证」变成「看不出来」。它改的不是精度,是结论。
  4. 以为「回归到均值」是说极端会变正常。它说的是群体趋势: 极端值里「运气成分」大的那部分,下一次不会再出现。 这跟「惩罚优秀」「向平庸看齐」完全是两回事。
自测三问(点开对答案) 1. 为什么加一个纯随机特征,R² 也会涨? —— 因为 R² 的定义是 `1 − 残差平方和/总平方和`, 而**多一列自变量,残差平方和一定不会变大**(最坏情况是这列系数取 0,等于没用)。 **所以 R² 只会涨不会跌,它天然偏向复杂模型。**要修正这一点, 就得用调整 R²(按参数个数打折)或者留出测试集。 2. 共线性下 R² 还挺高,为什么说模型「废了」? —— **因为废的不是预测,是解释。**R² 高只说明「这几个变量合起来能拟合」, 可具体每一个起了多大作用,模型说不出来 —— 系数 0.2342 和 1.5853 只是 1.8 的一个随机劈法, 换个种子就变。 **而且两个 t 值都掉到不显著,明明合起来非常显著。**做决策要的是「动哪个变量有用」, 这一问被共线性毁了。 3. 训练误差降到零,为什么反而是坏消息? —— 因为**参数个数追上样本个数时,模型可以把每个点都穿过去,包括噪声点。** Demo 里 10 个点配 9 次多项式,训练误差 4.25e-11, 新点误差 1.1028 —— **它记住的是这一批数据,不是背后的规律。**判据只有一个: 拿没见过的数据去试。

最小二乘对每个点一视同仁。 下一节给每个点配一个权重 —— 一个离群点、一段测不准的数据,都能靠这一个改动处理掉。

相关笔记