🏠 总目录📚 本教程 08 · 数据、实验、模型的飞轮 ← →
📑 本页目录(点开跳转)

08 · 数据、实验、模型的飞轮

⏱ 24 分钟 | ⭐ 贪心挑候选 8 次只成功 2 次,UCB 8 次全中


🎯 一句话

这里的数据不是给定的,是你决定去生产的。 所以模型的价值不在指标好看,在于它让下一批实验少做多少次 —— ⭐ 而「下一批做什么」这个问题,答案不是挑预测分最高的那些。


🔄 一、闭环才是这件事的形态

普通 ML 的数据集是给定的:拿到、切分、训练、评估,结束。 AI4S 不是。它是一个环,而且环上每一步都要花真钱:

训模型 → 挑候选 → 做实验 → 拿到新数据 → 回去训模型

⭐⭐ 这个环改变了「好模型」的定义。 一个 R² 0.9 但只会推荐你已经做过的东西的模型,价值是零; 一个 R² 0.6 但每次都能指出「这个方向你没试过、而且可能有戏」的模型,能省掉几个月。

⭐ 判据一句话:别问模型多准,问它让你少做了多少次实验。


🎯 二、为什么不能挑分数最高的

这是全章的题眼。直觉是「挑模型觉得最好的」—— ⚠️ 这会让你一直在已知区域打转: 模型对熟悉的区域预测得准、给分高;对没探过的区域没把握,于是给不出高分。 于是你永远不去那里,于是模型永远学不会那里。这个循环自己会锁死。

⭐ 解法是在打分里明确地给不确定性付钱:

采集分数 = 预测值 + κ × 不确定性

κ = 0 就是纯贪心;κ 越大越倾向于去没探过的地方。 这个式子叫 UCB(置信上界),⭐ 和强化学习里的探索与利用是同一件事, 站内有一整章:强化学习基础 07 · 探索与利用。


🧪 三、把差别跑出来

一个一维的目标函数,右边有个高的全局最优,左边有个诱人的局部最优。 两种策略从完全相同的三个初始点出发,各跑 25 轮:

import numpy as np


def f(x):
    """多峰目标函数:全局最优在右侧,左侧有个诱人的局部最优。"""
    return np.sin(3 * x) * np.exp(-0.3 * (x - 2) ** 2) + 1.6 * np.exp(-2.5 * (x - 7.2) ** 2)


grid = np.linspace(0, 10, 400)
best_x = grid[np.argmax(f(grid))]


def gp(Xtr, ytr, Xte, ls=0.6, noise=1e-4):
    """最小版高斯过程:RBF 核,返回后验均值和标准差。"""
    k = lambda a, b: np.exp(-0.5 * ((a[:, None] - b[None, :]) / ls) ** 2)
    K = k(Xtr, Xtr) + noise * np.eye(len(Xtr))
    Ki = np.linalg.inv(K)
    mu = k(Xte, Xtr) @ Ki @ ytr
    var = 1.0 - np.einsum("ij,jk,ik->i", k(Xte, Xtr), Ki, k(Xte, Xtr))
    return mu, np.sqrt(np.maximum(var, 1e-12))


def run(kappa, rounds=25, seed=0):
    r = np.random.default_rng(seed)
    X = r.uniform(0, 10, 3)                       # 三个初始点,两种策略完全相同
    y = f(X)
    for t in range(rounds):
        mu, sd = gp(X, y, grid)
        nxt = grid[np.argmax(mu + kappa * sd)]    # ⭐ kappa=0 就是纯贪心
        X, y = np.append(X, nxt), np.append(y, f(nxt))
        if abs(nxt - best_x) < 0.15:
            return t + 1
    return None


print("目标函数全局最优在 x = %.2f,f = %.3f" % (best_x, f(best_x)))
print()
print("%-22s %s" % ("策略", "第几轮找到全局最优(25 轮内)"))
for name, kap in [("greedy (kappa=0)", 0.0), ("UCB (kappa=2)", 2.0)]:
    res = [run(kap, seed=s) for s in range(8)]
    ok = [r for r in res if r is not None]
    print("%-22s %s" % (name, " ".join("%2s" % (r if r else "--") for r in res)))
    print("%-22s   found %d/8, avg round %.1f" % ("", len(ok), np.mean(ok) if ok else float("nan")))

真实输出:

目标函数全局最优在 x = 7.19,f = 1.600

策略                     第几轮找到全局最优(25 轮内)
greedy (kappa=0)       -- -- -- -- --  2 --  2
                         found 2/8, avg round 2.0
UCB (kappa=2)           5  7  8  7  9  7  8  5
                         found 8/8, avg round 7.0
局部最优全局最优 f=1.60贪心 25 次全在这里右半区 0 个12345UCB 第 5 步就到了三个初始点(两种策略完全相同)8 次重复:贪心命中 2/8,UCB 命中 8/8
看采样点散布,不看谁的点多。⭐ 贪心 25 次全部落在左边那个局部最优上,右半区一个都没去过 —— 而它每一轮都很自信。⚠️ 这正是「模型说没有更好的了」不可信的原因:「找不到」和「没找过」在数据上分不开。

⭐⭐ 这张表要横着读,不要只看平均轮数:

⚠️ 别被「贪心平均 2.0 轮」骗了 —— 那是只统计成功的那 2 次算出来的。 ⭐ 把失败的 6 次算进去,贪心的期望代价是无穷 —— 它根本到不了。 💀 这正是真实项目里最贵的失败模式:指标一直在缓慢改善,而你一直在错的区域里。


📦 四、一次挑一批的坑

实验很少一次做一个 —— 一块 96 孔板就是 96 个。 ⚠️ 直接取采集分数最高的 96 个是错的:它们会挤在同一个小区域里, 彼此几乎一样,你花了 96 次实验只买到 1 条信息。

⭐ 两个能直接用的办法:

  1. 挑一个就假装拿到了结果(用模型预测值代替),更新不确定性再挑下一个 —— 这样后选的点自然会避开已选的点。
  2. 加一个多样性下限:候选之间的距离/相似度不得超过某个阈值。

⭐ 站内已经有一整章讲这个问题的近亲: 数据这一关 12 · 标注预算与主动学习。 ⚠️ 两者的区别值得记:那一章是「标注一个已经存在的样本要花钱」, 候选池是给定的、有限的;⭐ 这一章是「做一次实验要花钱,而样本是你造出来的、 原本不存在」—— 搜索空间是开放的,这让「挑哪个」变得难得多。


⚠️ 五、实验结果回来之后,你的数据分布已经变了

💀 这一条最容易被忽略,而它会毁掉你的评估。

你专门挑了模型认为好的、或者不确定的那些去做实验。 于是新数据根本不是从原分布里随机抽的 —— 它是被你的模型筛过的。

后果有两个:

① 用新旧数据混在一起做的评估会失真。 测试集里塞满了「模型觉得有戏」的样本, 它在这些样本上的表现不代表它在全空间上的表现。

② 每一轮的指标不可比。 第 3 轮的测试集和第 1 轮的分布不同, ⚠️ 「R² 从 0.6 涨到 0.7」可能只是因为后来的候选更好预测。

⭐ 对策:留一个从原始分布随机抽的、从不参与挑选的固定测试集。 它是你唯一一把不会随着飞轮转动而变形的尺子。 ⚠️ 而它同样必须按同源家族划分 —— 见 07 章。


🏁 六、什么时候停

三个信号,⚠️ 但它们都不干净:

信号 ⚠️ 但是
预算用完了 最诚实的一个,也是最常见的真实原因
连续几轮没有改善 可能是收敛了,⭐ 也可能是 κ 太小、根本没在探索
模型说没有更好的了 💀 这句话不可信 —— 它只覆盖你定义的搜索空间

⭐⭐ 第三条要特别当心:「模型找不到更好的」和「更好的东西不在你的搜索空间里」 在数据上完全无法区分。第 3 节那个实验就是缩微版 —— 贪心那 6 次失败里,模型每一轮都很自信,而它从没去过右边。


🔗 这一章连到哪里

去哪 为什么
强化学习基础 07 · 探索与利用 ⭐ 第 2 节那个 预测值 + κ × 不确定性 就是 UCB,那一章讲它的完整形态和为什么这样配
数据这一关 12 · 标注预算与主动学习 ⭐ 同一个问题的近亲。区别:那边候选池是给定的(标注已有样本),这边样本是你造出来的、搜索空间开放
07 · 评测的坑 ⚠️ 第五节说的固定测试集,同样必须按同源家族划分,否则它也是虚的
06 · 小数据这一关 飞轮的第一圈就是小数据 —— 那一章讲头几十条标签怎么用
模型上线之后 · 附录A 速查 ⭐ 第五节那个「分布变了」在部署语境里叫数据漂移,那边有完整的检测方法清单

✅ 检查点

  1. 闭环怎么改变了「好模型」的定义?判据是什么?
  2. 为什么挑预测分最高的候选会锁死?说清那个循环。
  3. 实验里贪心和 UCB 各成功几次?「贪心平均 2.0 轮」为什么是个误导性的数字?
  4. 一次挑 96 个的坑是什么?两个解法各是什么?
  5. 实验结果回来后为什么数据分布变了?会造成哪两个后果?对策是什么?
  6. 「模型说没有更好的了」为什么不可信?
👀 答案
  1. 因为数据不是给定的,是你决定去生产的。⭐ 好模型 = 让你少做实验的模型,不是指标高的模型。一个 R² 0.9 但只推荐你做过的东西的模型价值是零。判据:别问模型多准,问它让你少做了多少次实验。
  2. 模型对熟悉区域预测准、给分高;对没探过的区域没把握所以给不出高分 → 你永远不去那里 → 模型永远学不会那里 → 继续给不出高分。这个循环自己会锁死。
  3. 贪心 2/8,UCB 8/8。 ⚠️「贪心平均 2.0 轮」只统计了成功的那 2 次,而那 2 次是初始点碰巧撞上;其余 6 次 25 轮全耗光。⭐ 把失败算进去,贪心的期望代价是无穷。
  4. 取分数最高的 96 个会挤在同一小区域、彼此几乎一样,96 次实验只买到 1 条信息。解法:①挑一个就用预测值假装拿到结果、更新不确定性再挑下一个 ②加多样性下限。
  5. 因为你专门挑了模型觉得好或不确定的去做实验,新数据不是从原分布随机抽的。后果:①混合数据的评估失真 ②每轮指标不可比(涨了可能只是因为后来的候选更好预测)。⭐ 对策:留一个从原始分布随机抽、从不参与挑选的固定测试集(且同样要按家族划分)。
  6. 因为它只覆盖你定义的搜索空间。💀「模型找不到更好的」和「更好的不在搜索空间里」在数据上无法区分 —— 第 3 节贪心失败的那 6 次里,模型每轮都很自信,而它从没去过右边。

🛑 可以停在这里

这是正文最后一章。⭐ 你现在有一条完整的链: 把科学问题变成建模问题 → 表示 → 结构与对称性 → 小数据 → 正确评估 → 决定下一批实验做什么。

⚠️ 什么时候回来:真正开始转飞轮的时候。第五节那条「分布已经变了」 在纸上看很显然,⭐ 在项目第三轮时几乎所有人都会忘。

⚡ 走神救援

先记住这几件事

下一节 👉 附录A-速查.md

打卡记录保存在你的浏览器里,首页能看到总进度