📈 第11章 误差
📄 一句话概括

把整个残差分布压成一个数:先平方、再求和、除以个数、开方。这就是均方根误差。 60 条数据算出来 RMSE = 0.939。它不是最大偏差(1.69),也不是中位数(0.83)。 用 ±1 倍 RMSE 造区间只盖住 60% 的人,±2 倍才盖满。所以区间必须连着覆盖率一起说。

📝 本章和前面几章的关系

上一篇看了残差的样子。这一篇把它压成一个数,好写进制度、好对外承诺。 书里这一步是在 R 的 summary() 里读出来的。这里手算一遍,再和 R 的口径对照。

一、客户要一个数

客户说:别跟我说「大概」,给我一个能写进制度里的数。 这就是要把整个残差分布压成一个数,而且这个数要能解释、能复现。 候选有不少:平均绝对偏差、最大偏差、九十分位偏差、均方根误差。

书里选的是均方根误差。原因是它和最小二乘用的是同一套惩罚逻辑,前后一致。 选哪个都能用,但必须说清选的是哪个。不同口径给出的数能差一倍。

二、均方根误差怎么算

第一步,每个残差平方:−1.25 的平方是 1.56,符号没了,数值也被放大了。 第二步,全部加起来:52.93。 第三步,除以个数 60:0.882。

第四步,开方:0.939。 四步缺一不可。少开方,单位就变成「百分点的平方」,没法跟人说话。 「均方根」三个字就是这四步的缩写:均(除以个数)、方(平方)、根(开方)。

三、它的读法:不是最大偏差

RMSE = 0.94,意思是「平均而言偏这么多」,不是「最多偏这么多」。 实际最大偏差是 1.69,比 RMSE 大得多。拿 RMSE 当上限去承诺,一定翻车。 它也不是中位数。残差绝对值的中位数是 0.83,比 RMSE 小——因为平方放大了大偏差的权重。

三个数摆在一起:中位数 0.83、RMSE 0.94、最大值 1.69。数值一路变大,含义一路不同。 报哪个数,取决于你要回答哪个问题。

四、用 RMSE 造一个区间

客户拿到 0.94 还是不知道该干什么。要变成区间,得乘一个倍数。 ±1 倍(0.94 个点)盖住 60% 的人;±1.5 倍(1.41 个点)盖住 87%;±2 倍(1.88 个点)盖满 100%。 这三个覆盖率是数出来的,不是查表来的。样本只有 60 个,所以数字会跳。

所以对外只能说:±0.94 个点,十次里有六次。不能说「误差不超过一个点」。 区间和覆盖率必须成对出现。只说区间不说覆盖率,等于什么都没说。

五、R 的汇总里也有这一行

R 的 summary() 会打一行:Residual standard error: 0.955 on 58 degrees of freedom。 0.955 比这里的 0.939 大一点,差别来自分母:R 用 n−2 = 58,这里用 n = 60。 两个参数用掉了两个自由度,所以除 58。同一份数据、两个口径、两个数,都不算错。

对外报数时必须说清用的是哪个分母,否则别人复算不出来,就会怀疑你在挑数。

六、动手 Demo:把一个误差数定量地指定下来

按四步手算 RMSE:平方、求和、平均、开方,每一步都把中间值打出来。 再用 1 倍、1.5 倍、2 倍 RMSE 造三个区间,实测各盖住多少人。

S = [20261102]

def rnd():
    S[0] = (S[0] * 1103515245 + 12345) % 2147483648
    return S[0] / 2147483648.0

DATA = []
for i in range(60):
    x = 40 + int(round(i * 55 / 59.0))
    y = 1.2 + 0.085 * x + (rnd() - 0.5) * 3.2
    DATA.append((x, round(y, 1)))

XS = [d[0] for d in DATA]
YS = [d[1] for d in DATA]
n = len(XS)
mx = sum(XS) / float(n)
my = sum(YS) / float(n)
m = sum((x - mx) * (y - my) for x, y in zip(XS, YS)) / sum((x - mx) ** 2 for x in XS)
b = my - m * mx
res = [y - (b + m * x) for x, y in DATA]

ss = sum(d * d for d in res)
r = (ss / n) ** 0.5
print("残差平方和 %.2f" % ss)
print("除以 %d 得 %.3f" % (n, ss / n))
print("开方得 RMSE %.3f" % r)
print("除以 n-2 得 %.3f(R 的口径)" % ((ss / (n - 2)) ** 0.5))

print()
for k in (1.0, 1.5, 2.0):
    cov = sum(1 for d in res if abs(d) <= k * r) / float(n) * 100
    print("±%.1f 倍 = %.2f 个点,盖住 %.0f%%" % (k, k * r, cov))

实跑输出:

--------------------------------------------------------------------
把一个误差数定量地指定下来
--------------------------------------------------------------------

  ① 客户要一个数
  --------------------------------------------------------------------
  客户说:别跟我说「大概」,给我一个能写进制度里的数。
  这就是要把整个残差分布压成一个数,而且这个数要能解释、能复现。
  候选有很多:平均绝对偏差、最大偏差、九十分位偏差。书里选的是均方根误差。

  ② 均方根误差怎么算
  --------------------------------------------------------------------
  第一步,每个残差平方:0.61 的平方是 0.37,负号没了。
  第二步,全部加起来:52.93。
  第三步,除以个数 60:0.882。
  第四步,开方:0.939。这就是 RMSE。
  四步缺一不可。少开方,单位就变成「百分点的平方」,没法跟人说话。

  ③ 它的读法:不是最大偏差
  --------------------------------------------------------------------
  RMSE = 0.94,意思是「平均而言偏这么多」,不是「最多偏这么多」。
  实际最大偏差是 1.69,比 RMSE 大得多。拿 RMSE 当上限去承诺,一定翻车。
  它也不是中位数。残差绝对值的中位数是 0.83,比 RMSE 小,因为平方放大了大偏差的权重。

  ④ 用 RMSE 造一个区间,覆盖率实测
  --------------------------------------------------------------------

| 区间宽度 | 折成百分点 | 实际覆盖率 |
|:---:|---:|---:|
| ±1.0 倍 | 0.94 个百分点 | 60% |
| ±1.5 倍 | 1.41 个百分点 | 87% |
| ±2.0 倍 | 1.88 个百分点 | 100% |

  一倍 RMSE 只盖住六成,两倍才盖满。所以「±1 个点」这句话,只对六成人成立。
  要对外承诺,就得说清覆盖率:±0.94 个点,十次里有六次。
  ! 区间和覆盖率必须成对出现。只说区间不说覆盖率,等于没说。

  ⑤ R 的汇总里也有这一行
  --------------------------------------------------------------------

  [平台原样 · 本沙箱不执行]
  | Residual standard error: 0.955 on 58 degrees of freedom
  | Multiple R-squared:  0.703, Adjusted R-squared: 0.698
  这是 R 的 summary() 里那两行。第一行就是残差标准误。

  注意 R 报的是 0.955,比这里手算的 0.939 大一点。差别来自分母:R 用 n−2 = 58,这里用 n = 60。
  两个参数用掉了两个自由度,所以要除 58。同一份数据,两个口径,两个数,都不算错。
  对外报数时必须说清用的是哪个分母,否则别人复算不出来,就会怀疑你在挑数。

同一份残差,除 60 得 0.939、除 58 得 0.955。两个数都对,前提是说清楚除的是谁。 覆盖率 60/87/100 是这 60 个人实测出来的。 换一批人,比例会变,但「一倍不够、两倍才满」的格局通常不变。

30 秒实验

  1. 把倍数改成 0.5,覆盖率会是多少?这个区间有实用价值吗?
  2. 如果客户只关心「别高估」,应该看残差的哪个统计量?为什么不是 RMSE?
  3. 想一想:样本只有 60 个时,实测覆盖率和理论覆盖率会差多少?样本变大呢?

常见误区

  1. 把 RMSE 当最大偏差:它是平均意义上的典型偏差,最大值通常比它大一倍。
  2. 不同口径混着用:一会除 n 一会除 n−2,同一份数据报出两个数,别人没法复算。
  3. 只报区间不报覆盖率:「±1 个点」这句话在覆盖率 60% 和 95% 时完全是两回事。
  4. 以为覆盖率是固定值:它是样本里数出来的,样本换了就会变。
自测三问(点开对答案) 1. 均方根误差的四步是什么? —— 每个残差平方、全部求和、除以个数、最后开方。少任何一步,量纲或含义就错了。 2. RMSE 0.94 能不能说成「误差不超过 0.94」? —— 不能。实际最大偏差是 1.69。0.94 是平均意义上的典型偏差,不是上限。 3. 为什么 R 报的 0.955 和这里算的 0.939 不一样? —— 分母不同:R 用 n−2 = 58(扣掉两个参数的自由度),这里用 n = 60。两个都是常见口径,说清就行。

有了误差数,下一步是让它变小。办法不是换算法,是把数据切开——按部门分。

相关笔记