人事把销售部的 24 条也加进来。数据从 60 条涨到 84 条,误差却从 0.94 涨到 1.39。 原因是两批人不服从同一套规则:技术部每 10 点多 0.90 个点,销售部只多 0.55 个点。 按部门切开各算各的,总误差回落到 0.87。分割的目的不是把数据弄小,是让每段误差变小。
上一篇把误差压成了一个数。这一篇讲怎么让它变小:不是换算法,是换分组。 销售部 24 条是新加的数据,种子写死在脚本里,跑出来人人一样。
一、客户又给了 24 条
人事这次把销售部也纳进来了:24 条,分数 42 到 92。 销售部的加薪区间是 2.5% 到 6.2%,平均 4.37%;技术部是 3.4% 到 10.6%,平均 6.76%。 两个部门的加薪水平根本不在一条线上。这不是数据脏,是制度不同。
销售的钱一部分走提成,年度调薪那部分本来就压得低;技术走职级,绩效直接挂钩。 数据合并之前,先问一句:这两批人是同一套规则生成的产物吗?
二、合并之后,误差反而变大了
| 用哪些数据 | RMSE(百分点) |
|---|---|
| 只用技术部 60 条 | 0.94 |
| 只用销售部 24 条 | 0.67 |
| 合并 84 条 | 1.39 |
数据从 60 条涨到 84 条,误差从 0.94 涨到 1.39,涨了 48%。 数据更多不等于模型更好。多出来的这批人,根本不服从同一个方程。 这是「样本量越大越好」这句话最典型的反例。样本量只在同分布的前提下才有意义。
三、为什么会变差
合并后的方程:加薪 = 0.56 + 0.0818 × 分数。 技术部单独是 0.70 + 0.0898 × 分数,销售部单独是 0.66 + 0.0554 × 分数。 合并的斜率 0.0818 落在两者之间,是个折中——而折中在这种场合对谁都不合适。
给技术部的人算,普遍偏低;给销售部的人算,普遍偏高。两头都错。 误差不是来自噪声,是来自结构。噪声可以多测几次压下去,结构错只能靠分组解决。
四、分割:按部门切开,各算各的
把 84 条分成两组,各自拟合一条线,再把两组残差合起来算一个总 RMSE。 结果 0.87,比合并的 1.39 低了不少,也比技术部单独的 0.94 低——因为它两边都照顾到了。 两个斜率也说得清了:技术部每 10 点多 0.90 个点,销售部每 10 点多 0.55 个点。
这才是分割的真正目的:不是把数据切小,是让每一段的误差变小。 切完如果误差没降,说明切错了变量——那个变量本来就不该分。
五、解释和预测,这次同时在
预测上:RMSE 从 1.39 降到 0.87。 解释上:现在能说「技术部靠绩效,销售部靠业绩提成」——这句话合并模型给不出来。 优秀的回归分析两件事都要做:既能给出更准的数,又能说清为什么。
只准不说,客户不敢用;只说不准,客户没法做预算。 下一篇把这件事固定成一个模型:分区模型。
六、动手 Demo:分割是为了管理误差
技术部 60 条加销售部 24 条,先合并拟合,再分开拟合,比两个 RMSE。 顺便看看合并方程给两个部门各自的预测是偏高还是偏低。
def mk(seed, n, lo, hi, a, b0, noise):
S = [seed]
def rnd():
S[0] = (S[0] * 1103515245 + 12345) % 2147483648
return S[0] / 2147483648.0
out = []
for i in range(n):
x = lo + int(round(i * (hi - lo) / float(n - 1)))
y = a + b0 * x + (rnd() - 0.5) * noise
out.append((x, round(y, 1)))
return out
TECH = mk(20261102, 60, 40, 95, 1.2, 0.085, 3.2)
SALE = mk(88117, 24, 42, 92, 1.0, 0.055, 2.6)
def fit(d):
xs = [p[0] for p in d]
ys = [p[1] for p in d]
n = len(xs)
mx = sum(xs) / float(n)
my = sum(ys) / float(n)
k = sum((x - mx) * (y - my) for x, y in zip(xs, ys)) / sum((x - mx) ** 2 for x in xs)
return k, my - k * mx
def rmse(d, k, b):
return (sum((y - (b + k * x)) ** 2 for x, y in d) / float(len(d))) ** 0.5
def near(d, q):
sub = [y for x, y in d if abs(x - q) <= 2]
return sum(sub) / float(len(sub)), len(sub)
kt, bt = fit(TECH)
ks, bs = fit(SALE)
print("技术部 m=%.4f b=%.2f RMSE=%.2f" % (kt, bt, rmse(TECH, kt, bt)))
print("销售部 m=%.4f b=%.2f RMSE=%.2f" % (ks, bs, rmse(SALE, ks, bs)))
ALL = TECH + SALE
ka, ba = fit(ALL)
print("合并 m=%.4f b=%.2f RMSE=%.2f" % (ka, ba, rmse(ALL, ka, ba)))
res = [y - (bt + kt * x) for x, y in TECH] + [y - (bs + ks * x) for x, y in SALE]
print("分开后总 RMSE=%.2f" % ((sum(d * d for d in res) / len(res)) ** 0.5))
print()
mt, nt = near(TECH, 70)
ms, ns = near(SALE, 70)
print("合并方程给 70 分的人:%.2f%%" % (ba + ka * 70))
print("技术部 68~72 分 %d 人,实际平均 %.2f%%" % (nt, mt))
print("销售部 68~72 分 %d 人,实际平均 %.2f%%" % (ns, ms))
print("两边实际平均相差 %.2f 个点" % (mt - ms))
实跑输出:
--------------------------------------------------------------------
分割是为了管理误差
--------------------------------------------------------------------
① 客户又给了 24 条
--------------------------------------------------------------------
人事这次把销售部也纳进来了:24 条,分数 42 到 92。
销售部的加薪区间是 2.5% 到 6.2%,平均 4.37%。技术部是 3.4% 到 10.6%,平均 6.76%。
两个部门的加薪水平根本不在一条线上。
② 合并之后,误差反而变大了
--------------------------------------------------------------------
| 用哪些数据 | RMSE(百分点) |
|---|---:|
| 只用技术部 60 条 | 0.94 |
| 只用销售部 24 条 | 0.67 |
| 合并 84 条 | 1.39 |
数据从 60 条涨到 84 条,误差从 0.94 涨到 1.39,涨了 48%。
数据更多不等于模型更好。多出来的这批人,根本不服从同一套规则。
! 这是「样本量越大越好」这句话最典型的反例。
③ 为什么会变差
--------------------------------------------------------------------
合并后的方程是:加薪 = 0.56 + 0.082 × 分数。
技术部单独是 0.70 + 0.0898 × 分数,销售部单独是 0.66 + 0.0554 × 分数。
合并方程的斜率 0.0818 落在两者之间,对谁都不合适。
给技术部的人算,普遍偏低;给销售部的人算,普遍偏高。两头都错。
拿 70 分验一下:合并方程给 6.29%。
技术部 68~72 分 5 人实际平均 6.76%,销售部 3 人实际平均 4.73%。
同一个分数,两个部门实际差了 2.03 个点。合并方程只能给一个数,它给的是这两个的折中。
这叫把一个方程硬套在两套制度上。误差不是来自噪声,是来自结构。
④ 分割:按部门切开,各算各的
--------------------------------------------------------------------
切开之后,84 个点的总 RMSE 是 0.87,比合并的 1.39 低了不少。
两个部门各自的斜率也说得清了:技术部每 10 点多 0.90 个点,销售部每 10 点多 0.55 个点。
这才是分割的真正目的:不是把数据弄小,是让每一段的误差变小。
⑤ 解释和预测,这次同时在
--------------------------------------------------------------------
预测上:RMSE 从 1.39 降到 0.87。
解释上:现在能说「技术部靠绩效,销售部靠业绩提成」——这句话合并模型给不出来。
优秀的回归分析两件事都要做:既能给出更准的数,又能说清为什么。
只准不说,客户不敢用;只说个大概,客户没法做预算。
合并的 RMSE 1.39 比两个部门各自的都大——这不是「平均」,这是「都不合适」。 「拿 70 分验一下」那三行最能说服客户:同一个分数,两个部门实际差 2.03 个点, 合并方程却只能给一个数。
30 秒实验
- 如果按分数段切(40~65、65~95)而不是按部门切,总 RMSE 会降吗?为什么?
- 切完误差没降,说明什么?该怎么处理?
- 想一想:如果销售部只有 5 条记录,还该不该单独建一条线?
常见误区
- 数据越多越好:不同分布的数据掺在一起,只会把误差做大。
- 为了降误差而乱切:切到什么程度都能降一点,切到没有业务含义就是自欺。
- 切完不看样本量:一段只剩五六条,斜率完全靠不住。
- 只报更低那个数:切分后的 RMSE 是全样本合起来算的,不是各段里挑一个。
自测三问(点开对答案)
1. 数据从 60 条涨到 84 条,误差为什么反而变大? —— 因为销售部不服从技术部那套规则。合并方程是个折中,对两边都偏,残差自然更大。 2. 分割的真正目的是什么? —— 让每一段的误差变小,同时让每一段的参数能各自解释。不是单纯把数据切小。 3. 切完之后误差没下降,说明什么? —— 说明切错了变量。那个变量本来就不携带结构差异,这种切法只会浪费样本。把「先分组、每组一条线」固定下来,就叫分区模型。下一篇看它的威力和它的陷阱。
- ☐ (本篇完,下一篇 11.6 分区模型与回头的客户)