🧪 第16章 机器学习概要
📄 一句话概括

一个数据集就是两列数:你能问出来的叫特征,要答的叫标签。 三类任务的分界在标签:连续的数、类别、或者根本没有标签。 训练误差一路降,测试误差先降后升——这是过拟合,也是全章的锚点。

📝 本章和前面几章的关系

这一篇对应教材 16.1 到 16.3.2:机器学习的相关概念、处理的资料、处理的任务, 以及统计与机器学习的分野。 第 0 篇到第 15 章都在讲「怎么把服务跑起来」,从这一篇起换方向: 服务里跑的那个模型,是怎么来的。

一、一个数据集就是两列数

实测造了 200 条房屋数据:面积 40 到 200 平方米,价格按 3.2 倍面积加 25 万、再叠一点噪声算出来。

面积 X(特征) 价格 y(标签)
137.5 449.4
159.6 549.9
164.9 537.7
86.1 286.8
150.9 514.9

两列数:一列是你能问出来的(面积),一列是你要答的(价格)。 机器学习的全部内容,就是找前者的什么组合能算出后者。 实测形状:X 是 (200, 1),y 是 (200,)。 这个形状不是随便定的——scikit-learn 要求特征永远是二维的。 所以哪怕只有一列特征,也要写成 200 行 1 列,不能压成一维。 这是新手第一个报错来源:Expected 2D array, got 1D array instead, 看到了就加一句 reshape(-1, 1)。

二、三类任务,分界在标签

任务 标签长什么样 实测这条
回归 连续的数 3.219 * 面积 + 21.77
分类 类别(是/否) 1 有 111 条、0 有 89 条
聚类 没有标签 三个簇中心 70.5/123.4/173.3

回归那条的训练集均方根误差是 11.81 万元——平均每套房子猜偏十来万。 这个数跟房价四百多万比,大约 3%。 分类那组用了两个特征(面积、房龄),标签是「卖没卖掉」。 注意 111 比 89 这个比例——不平衡的数据集是常态,后面讲评估指标时会反复碰到它。 聚类根本没有标签。所以它没法算「对了几分」,效果只能靠人看,或者用簇内距离这类间接指标。 这也是它比前两类少见于生产的原因。

三类任务的算法、评估方式、甚至数据准备的套路都不一样。 动手之前先确定这是哪一类,比选什么算法重要得多。

三、切三份:训练、验证、测试

切法 训练集平均面积 全体平均面积
按面积排序后取前 120 条 95.7 126.3
随机打乱后取 120 条 121.1 126.3

同一份数据,只换切法,训练集的平均面积差了 25 平方米。 模型只见过小房子,拿去测大房子必然偏——而这个偏会被误读成「模型不行」。 这就是为什么切分之前必须打乱。 真实数据里排序的原因五花八门:按时间入库、按 ID 自增、 按地区导出……你以为的随机,往往一点都不随机。 三份各有各的活:训练集调参数,验证集挑模型,测试集只在前两步都定完之后用一次。

测试集用两次以上,它就变成了验证集。 你报出来的那个数,就成了「在这份数据上挑出来的最好成绩」,不再是「在没见过的数据上的真实水平」。

四、统计与机器学习:问的问题不一样

统计 机器学习
要回答的 这两个变量是什么关系 这条新数据该判成什么
看重 系数有没有意义、置信区间 在没见过的数据上有多准
数据 少而精,讲究怎么抽的 多,讲究怎么切
过拟合 靠模型假设约束 靠验证集发现

教材把这两块分开讲(16.3.1 与 16.3.2),差别不在数学,在目的:统计想解释,机器学习想预测。 同一份数据,统计学家会问「面积的系数显著吗」,工程师会问「换一批房子还准不准」。 两个问题都对,只是要的东西不同。 这条分野有个实际后果:统计报告里的 R² 高,不等于模型能用。R² 说的是「这份数据解释了多少」, 不是「新数据能猜多准」。

五、规则还是学习:那个系数从哪来

做法 公式 均方根误差
人定规则 3.5 * 面积 + 30 47.02 万元
从数据拟合 3.219 * 面积 + 21.77 11.81 万元

人定的那条不是瞎猜——3.5 和 30 都是行业里摸出来的经验值。可就差这么一点系数,误差翻了 4.0 倍。 这就是「机器学习」这个名字最容易误导人的地方: 它不是从零变出规律,是在人已经大致知道形状(一条直线)的前提下,把两个系数算准。 顺着这个理解,「特征工程」为什么重要就说得通了:你给的形状对不对,决定了模型的上限。 给一条直线去拟合曲线关系,系数算得再准也没用。

六、过拟合:训练误差一路降,测试误差先降后升

40 个点,随机切 20 个训练、20 个测试,用不同阶数的多项式去拟合同一条正弦曲线。

模型 参数个数 训练集误差 测试集误差
1 阶 2 个系数 0.5747 0.6504
3 阶 4 个系数 0.5482 0.6325
5 阶 6 个系数 0.1661 0.4040
9 阶 10 个系数 0.1190 0.4201
15 阶 16 个系数 0.0706 45.4164

训练误差这一列从头到尾在降:0.5747 一路降到 0.0706,降了八倍。如果只看这一列,你会选 15 阶。 测试误差先降到 5 阶的 0.4040,然后回升。15 阶那个 45.4164 比 1 阶还差七十倍。 20 个点、16 个系数,模型有足够的自由度穿过每一个点。 穿过了,也就没学到规律,学到的是这 20 个点本身。

这张表是整章最该记住的一张:训练误差不能用来判断模型好不好。它只会随着模型变复杂一路下降, 最后降到 0——只要参数够多,任何数据都能被完美拟合,包括纯噪声。

七、不打乱会怎样:那是外推,不是过拟合

同一批点,这次不随机切,按 x 排好序直接取前 20 个当训练集。 实测训练点的 x 在 0.04 到 5.48,测试点在 5.64 到 9.78——完全错开,没有重叠。

模型 训练集误差 测试集误差
1 阶 0.4762 1.6396
3 阶 0.2115 9.8539
15 阶 0.0805 一百亿量级

15 阶的测试误差从 45.4164 变成了百亿。这次的问题不在模型,在测试集。 测试点的 x 全落在训练范围之外,模型只能往外推。 多项式出了训练范围会疯长——这是它的数学性质,不是它没学好。 两种病要分清:过拟合是「记住训练点」,外推是「训练点没见过的地方硬猜」。 前者的药是简化模型,后者的药是打乱切分。

现实里第二种更常见也更隐蔽:按时间切分的销售预测、按地区切分的风控模型,上线后全线崩, 往往就是这个原因。

八、这一篇之后

到这里,机器学习的骨架就齐了:数据是两列,任务是三类,切分是三份,好坏看测试集。 这三句话撑起了后面所有的内容。第 4 篇剩下的部分,一是把这些公式写成代码, 二是把它们包成一个别人能调的服务。 下一篇就是前者:那些带 Σ 的式子,落到 Python 里到底长什么样。

九、动手 Demo:机器学习:数据、任务与那条分界线

这个 Demo 用一份自造的房屋数据走完整个流程:看数据、分任务、切三份、比规则与学习、看过拟合。 第六、七两节是同一个模型、同一批点的两种切法,15 阶那条的测试误差差了两亿多倍。

pm = rng.permutation(40)
xtr, ytr = xs[pm[:20]], ys[pm[:20]]
xte, yte = xs[pm[20:]], ys[pm[20:]]
for deg in (1, 3, 5, 9, 15):
    p = np.polynomial.Polynomial.fit(xtr, ytr, deg)
    e1 = np.sqrt(np.mean((ytr - p(xtr)) ** 2))
    e2 = np.sqrt(np.mean((yte - p(xte)) ** 2))
    print(deg, round(e1, 4), round(e2, 4))

实跑输出:

--------------------------------------------------------------------
机器学习:数据、任务与那条分界线
--------------------------------------------------------------------

  ① 一个数据集长什么样
  --------------------------------------------------------------------
  特征 X 的形状 -> (200, 1)
  标签 y 的形状 -> (200,)

| 面积 X(特征) | 价格 y(标签) |
|---:|---:|
| 137.5 | 449.4 |
| 159.6 | 549.9 |
| 164.9 | 537.7 |
| 86.1 | 286.8 |
| 150.9 | 514.9 |

  ! 两列数:一列是你能问出来的(面积),一列是你要答的(价格)。
  机器学习的全部内容,就是找前者的什么组合能算出后者。
  ! X 是二维的 (200, 1),y 是一维的 (200,)。
  这个形状不是随便定的:scikit-learn 要求特征永远二维——一列特征也要写成 200 行 1 列,
  不能压成一维。这是新手第一个报错来源。

  ② 三类任务:回归、分类、聚类
  --------------------------------------------------------------------
  回归(预测一个数)-> 拟合出 3.219 * 面积 + 21.77
                      训练集均方根误差 11.81 万元
  分类(答一个是/否)-> 两个特征,标签里 1 有 111 条、0 有 89 条
  聚类(没有标签,自己分组)-> 三个簇的中心面积 70.5/123.4/173.3
  ! 三类的差别只在「标签长什么样」:回归的标签是连续的数,分类的标签是类别,聚类根本没有标签。
  ! 最后一类要特别说一句:没有标签就没法算「对了几分」。
  聚类的效果只能靠人看,或者用簇内距离之类的间接指标。这也是它比前两类少见于生产的原因。

  ③ 切三份:训练、验证、测试
  --------------------------------------------------------------------
  数据按面积排好序 -> 前 120 条平均面积 95.7,后 80 条平均面积 172.0
  直接切前 120 条当训练集 -> 训练集平均面积 95.7,全体 126.3
  随机打乱后切 120/40/40 -> 三份平均面积 121.1、128.5、139.3
  ! 顺序切出来的训练集平均面积只有 95.7,而全体是 126.3。
  模型只见过小房子,拿去测大房子必然偏——而这个偏会被误读成「模型不行」。
  ! 三份各有各的活:训练集调参数,验证集挑模型,测试集只在前两步都定完之后用一次。
  测试集用两次以上,它就变成了验证集,你报出来的数就不能信了。

  ④ 统计与机器学习:问的问题不一样
  --------------------------------------------------------------------

|  | 统计 | 机器学习 |
|---|---|---|
| 要回答的 | 这两个变量是什么关系 | 这条新数据该判成什么 |
| 看重 | 系数有没有意义、置信区间 | 在没见过的数据上有多准 |
| 数据 | 少而精,讲究怎么抽的 | 多,讲究怎么切 |
| 过拟合 | 靠模型假设约束 | 靠验证集发现 |

  ! 教材把这两块分开讲(16.3.1 与 16.3.2),差别不在数学,在目的:统计想解释,机器学习想预测。
  ! 所以同一份数据,统计学家会问「面积的系数显著吗」,工程师会问「换一批房子还准不准」。
  两个问题都对,只是要的东西不同。

  ⑤ 规则还是学习:那个系数从哪来
  --------------------------------------------------------------------
  人定规则 3.5 * 面积 + 30  -> 均方根误差 47.02 万元
  从数据拟合 3.219 * 面积 + 21.77 -> 均方根误差 11.81 万元
  人定那条比拟合出来的差 4.0 倍
  ! 人定的那条也不是瞎猜,3.5 和 30 都是行业里摸出来的经验值。
  可就差这么一点系数,误差就翻了 4.0 倍。
  ! 这就是「机器学习」这个名字最容易误导人的地方:它不是从零变出规律,
  是在人已经大致知道形状(一条直线)的前提下,把两个系数算准。

  ⑥ 过拟合:训练误差一路降,测试误差先降后升
  --------------------------------------------------------------------
  40 个点,随机切 20 个训练、20 个测试

| 模型 | 参数个数 | 训练集误差 | 测试集误差 |
|---|---:|---:|---:|
| 1 阶 | 2 个系数 | 0.5747 | 0.6504 |
| 3 阶 | 4 个系数 | 0.5482 | 0.6325 |
| 5 阶 | 6 个系数 | 0.1661 | 0.4040 |
| 9 阶 | 10 个系数 | 0.1190 | 0.4201 |
| 15 阶 | 16 个系数 | 0.0706 | 45.4164 |

  测试集误差最低的是 5 阶(0.4040)
  ! 15 阶那条:训练集误差低到 0.0706,测试集误差反弹到 45.4164,比 1 阶还差。
  ! 20 个点、16 个系数,模型有足够的自由度穿过每一个点——穿过了,也就没学到规律,
  学到的是这 20 个点本身。
  ! 这张表是整章最该记住的一张:训练误差不能用来判断模型好不好。
  它只会随着模型变复杂一路下降,最后降到 0。

  ⑦ 不打乱会怎样:那是外推,不是过拟合
  --------------------------------------------------------------------
  按 x 排序后直接切前一半 -> 训练点 x 在 0.04~5.48,测试点 5.64~9.78

| 模型 | 训练集误差 | 测试集误差 |
|---|---:|---:|
| 1 阶 | 0.4762 | 1.6396 |
| 3 阶 | 0.2115 | 9.8539 |
| 15 阶 | 0.0805 | 10293408794.2207 |

  ! 同一批点,只是切法不同,15 阶的测试误差从 45.4164 变成 10293408794.2207。
  ! 这一次的问题不在模型,在测试集:测试点的 x 全落在训练范围之外,模型只能往外推。
  多项式出了训练范围会疯长,推出来的数没有意义。
  ! 两种病要分清:过拟合是「记住训练点」,外推是「训练点没见过的地方硬猜」。
  前者的药是简化模型,后者的药是打乱切分。

  ⑧ 这一篇的三个数
  --------------------------------------------------------------------
  200 条数据、2 个特征、1 个标签,切 120/40/40
  人定系数误差 47.02,学出来的 11.81
  过拟合那组:1 阶 0.6504,9 阶 0.4201,15 阶 45.4164
  ! 第 17 章要做的那件事,就是用这套流程造一个能对外服务的机器学习 API。
  模型本身不复杂,复杂的是把「切分、训练、评估」包成一个别人能调的东西。

第三节那个 95.7 与 126.3,就是「不打乱」的代价。 第六节 15 阶训练误差 0.0706、测试 45.4164,两边差六百多倍。 第七节换的只是切法,15 阶测试误差变成百亿——外推与过拟合不是一回事。

30 秒实验

  1. 把噪声调大(rng.normal(0, 40, 200)),看第五节那个人定规则的误差还差几倍。
  2. 把第六节的切分从随机改成顺序,自己复现一次百亿那个数。
  3. 想一想:为什么测试集只能用一次?用两次会怎样?

常见误区

  1. 把一列特征写成一维:(200,) 传进 sklearn 直接报错。
  2. 不打乱就切分:训练集和测试集分布不同,评估全废。
  3. 拿训练误差判断模型好坏:它只会一路下降,最后降到 0。
  4. 测试集反复用:它变成了验证集,你报的数是「挑出来的最好成绩」。
  5. 以为机器学习不需要人给形状:特征选错,系数算得再准也没用。
自测三问(点开对答案) 1. 特征和标签分别是哪一列? —— 能问出来的那列是特征(面积),要答的那列是标签(价格)。 2. 三类任务靠什么区分? —— 靠标签:连续的数(回归)、类别(分类)、没有标签(聚类)。 3. 过拟合和外推的区别是什么? —— 过拟合是记住了训练点,外推是测试点在训练范围之外硬猜;前者换小模型,后者打乱切分。

骨架齐了,下一篇把那些带 Σ 的公式翻成能跑的代码。

相关笔记