一次划分的成绩里混着运气;交叉验证把数据切成 k 份轮流当考卷——实测五折从 62.5% 到 87.5%,波动 25 个百分点,这就是为什么不能只看一次。
12.6 跑出了 87.5%,这一节回答一个更根本的问题:那个数字到底能信几分。
一、一次划分的毛病
80/20 划分有个明显的问题:切得好就分数高,切得差就分数低。 同一份数据、同一个算法,只因为把哪几条分到了测试集,结果就能差一大截。 说白了,这份成绩里混着【运气】。 这不是算法不稳,是【一次抽样的样本太小】。
二、交叉验证怎么做
解决办法叫交叉验证: 把数据切成 k 份 → 每次都拿其中 1 份当测试、剩下 k-1 份当训练 → 轮流 k 次 → 最后把 k 个成绩平均。 每一份都当过一次考卷——所以运气被摊平了。 实测做 5 折交叉验证,5 个数字的平均值比任何单个数字都可靠。 它没有用到新数据,只是把手里这点数据用得更充分。
三、实测的波动有多大
看实测的五个数字:75%、75%、75%、62.5%、87.5%。 五折平均 75%,但最好的一折和最差的一折差了 25 个百分点。 这就是关键的一句:同一个算法、同一份数据,只因为切法不同就能差这么多。 所以单次成绩只能当参考——要说【这个算法好不好】,得看多次平均。 它还有个好处:平均下来 75% 比单次的 87.5% 低——这才是更接近真相的数字。
四、由此得到两条经验
第一:单次得分只能参考,多看几次才敢下结论。 第二:拿测试集反复调参数,等于变相把答案背下来。 第二条尤其重要:如果你试了二十组参数、挑了在测试集上最好的那组, 那你其实是【让测试集参与了训练】——这个分数就不再是对新数据的承诺了。 所以正规做法是再留一份【从不参与任何选择】的数据——它才是最后那张真正的考卷。
五、动手 Demo:尝试交叉验证
一次划分的成绩里混着运气,交叉验证把每一份都轮流当一次考卷。 实测做 5 折,看分数的波动有多大。
xs, ys = make_dataset(per_digit=8)
n, kf, size = len(xs), 5, len(xs) // 5
for i in range(kf):
s, e = i * size, (i + 1) * size
rx, ry = xs[:s] + xs[e:], ys[:s] + ys[e:]
right = sum(1 for q, t in zip(xs[s:e], ys[s:e])
if knn_predict(rx, ry, q, 3) == t)
print("第 %d 折 %.1f%%" % (i + 1, right / (e - s) * 100))
实跑输出:
--------------------------------------------------------------------
尝试交叉验证
--------------------------------------------------------------------
**上一节的 80/20 划分有个毛病:切得好就分数高、切得差就分数低。**
说白了,**这份成绩里混着【运气】。**
解决办法叫**交叉验证**:
把数据切成 k 份
每次都拿其中 1 份当测试、剩下 k-1 份当训练
轮流 k 次,最后把 k 个成绩平均一下
**这样一来,每一份都当过测试,运气被摊平了。**
实测做 5 折交叉验证:
第几折 训练 测试 准确率
------------------------------------
1 32 8 75.0%
2 32 8 75.0%
3 32 8 75.0%
4 32 8 62.5%
5 32 8 87.5%
五折平均准确率 75.0%
最好的一折 87.5%
最差的一折 62.5%
波动 25.0 个百分点
**看那个波动**——**同一个人、同一份数据,只因为切法不同就能差这么多。**
所以那两条经验就有了出处:
**单次得分只能参考,多看几次才敢下结论。**
**拿测试集反复调参数,等于变相把答案背下来——那是作弊。**
实测五折:75%、75%、75%、62.5%、87.5%——平均 75%,最好与最差相差 25 个百分点。 同一个算法、同一份数据,只因为切法不同就能差这么多——所以单次成绩只能参考。
30 秒实验
- 把折数从 5 改成 8,看平均值会不会更稳。
- 在纸上算一算:8 条测试样本里错 1 条,分数会掉多少个百分点。
常见误区
- 以为单次分数就是算法的真实水平。它里面混着抽样的运气。
- 以为可以反复拿测试集挑参数。那等于把答案背下来,分数不再可信。
自测三问(点开对答案)
1. 交叉验证是怎么做的? —— 把数据切成 k 份,轮流拿一份当测试、其余当训练,跑 k 次后取平均。 2. 这次五折实测的波动有多大? —— 从 62.5% 到 87.5%,相差 25 个百分点,平均 75%。 3. 为什么不能拿测试集反复调参数? —— 因为那等于让测试集参与训练,分数不再代表对没见过的数据的表现。- ☐ 下一节(亲手尝试C语言)
相关笔记
- 通过机器学习识别手写数字 — 那个 87.5% 是怎么来的
- 准备学习数据 — 训练与测试为什么要分开
- 什么是机器学习 — 回到这一章的起点
- ⌨️ 编程原理 · 索引 — 把编程全貌理一遍
- ⚙️ 程序是怎样跑起来的 · 索引