MINI-TABPFN COURSE · LESSON 07

第 7 课讲义:Synthetic Prior 与预训练——模型怎样在许多任务上学会预测算法?

这是一节可直接阅读、实现和验收的课程单元。页面正文由 canonical Markdown lesson source 投影生成。

第 07 / 08 课course-v0.5 / owner-review完整讲义 + 动手验收包
课程讲义 · 基础知识层canonical source: course/lectures/07-synthetic-prior-training.md · 先建立概念、符号、公式与边界,再进入工程实现。

讲义定位

到第六课,我们已经有从 context/query 到 prediction 的完整计算图(cell → column → row → ICL → head)。 但它还没有回答:共享参数 \(\theta\) 从哪里来?

PFN 的核心训练观念不是在一张固定表上拟合一个长期模型,而是在许多由 task prior 生成的 episode 上做 outer-loop training,让 \(\theta\) 学习一套可迁移的 task-conditioned prediction procedure。测试时通常 不更新 \(\theta\),而是在 forward 中读取当前 context(第 5 课的 ICL)。

本讲义解释 synthetic prior、meta-learning 两层时间尺度、简化 SCM generator、seed hierarchy、 query-only loss 和 OOD prior split。它也明确说明:synthetic SCM 是可控的数据生成工具,不自动证明 模型恢复了真实因果结构或具备 intervention / counterfactual ability。

Mini 简化:课程用可配置、可 replay 的小 prior 做端到端训练。官方 TabPFN-3(02_tabpfn3 §Synthetic Prior) 在更大、更丰富的 SCM prior 上预训练(报告称超过 8 trillion tokens);课程不复现该规模与完整 recipe。

1. 学习目标与先修知识

学完后你应该能够:

  1. 区分固定数据集训练、task-distribution training 和单个 episode 的 inner computation;
  2. 用 outer loop 描述 PFN 风格预训练,并写出 canon §3.8 / §3.1 的 loss 边界;
  3. 设计可 replay 的 synthetic prior 与 run/task/episode/model seed hierarchy;
  4. 解释为什么 query-only loss 是 prediction boundary 的一部分(衔接第 1、5 课);
  5. 对照官方 prior 扩展维,并设计课程版 in-distribution / OOD prior split;
  6. 把 train loss、checkpoint 与 held-out / OOD task 泛化分开报告。

先修:第 1–6 课的 episode 契约、ICL mask、classification/regression head。本课不改模型内部公式, 只把它们接到可采样的任务分布上。

2. 什么是 prior?

在这里,prior 不是一张固定训练表,而是生成 task 的分布(canon §3.8):

\[\mathrm{task\_spec}\sim p(\mathrm{task\_spec}), \qquad \mathrm{episode}\sim p(\mathrm{episode}\mid \mathrm{task\_spec})\]

task_spec 可包含特征数 \(P\)、context/query 大小 \(N_c,N_q\)、函数族、噪声、类别数 \(C\) 和 生成机制。每次 outer-loop step 先从 prior 采样一个或一批 task,再生成 context/query,最后只用 query target 计算 loss。

普通监督学习通常优化:

\[\text{parameters} \longrightarrow \text{loss on rows from a fixed dataset}\]

PFN 风格训练更接近:

\[\text{parameters} \longrightarrow \text{loss averaged over episodes sampled from a task distribution}\]

参数要学的是跨 task 的预测策略,而不只是某张表的固定决策边界。这与第 1 课的 “学程序,不是学一张表”同一条主线。

3. Meta-learning 的两层时间尺度

Outer loop

更新共享参数 \(\theta\):

\[\text{sample task} \longrightarrow\text{sample context/query} \longrightarrow\text{predict} \longrightarrow\text{query loss} \longrightarrow\text{update }\theta\]

合法的 outer-loop 目标(与 canon §3.1 一致)是:

\[\widehat{Y}_{\mathrm{query}} =f_\theta(X_{\mathrm{context}},Y_{\mathrm{context}},X_{\mathrm{query}},\mathrm{masks}), \qquad \mathcal{L} =\operatorname{loss\_fn}\!\bigl(\widehat{Y}_{\mathrm{query}},\,Y_{\mathrm{query}}\bigr)\]

Inner computation

测试时模型通常不更新 \(\theta\),而是在 forward 中把 context evidence 读入 row-level ICL,形成对 当前 task 的条件预测(第 5 课)。这就是 in-context adaptation:适应发生在输入与激活路径中,而不是 为每个 task 重新训练一套参数。

4. 官方 prior 维对照与 Mini 实现范围

官方 TabPFN-3 在 Synthetic Prior 中强调最大化数据广度,并列出若干扩展维。课程讲义应列名对齐、 不声称实现全部

官方 prior 维(02_tabpfn3直觉Mini 课程
Graph generation更丰富的 DAG / 结构多样性实现:chain / star / sparse random DAG
Combiner mechanisms父节点 → 子节点的多种函数合成实现:linear / threshold / sinusoid / interaction
Categorical variables更表达力的类别生成可选简化;默认数值为主
High-frequency oscillators全频谱正弦结构用 sinusoid family 近似,不追求官方频谱覆盖
Spatial prior空间相关协变量不实现;可作 OOD 叙事
Many-class prior与 \(C_{\max}=160\) 解码器匹配课程限制较小 \(C\);提醒官方 ceiling
Temporal prior (DSCM)离散时间动态 SCM不实现;列为论文扩展
Out-of-distribution prior分布偏移与外推必须在 task 生成层做 OOD split

官方流程图(Step 1–5)可记为:采样超参 → 采样 DAG → 计算 SCM → 抽取数据集 → 后处理。课程至少要覆盖 前四步的可审计简化版。

5. 一个简化 synthetic prior

课程从一个小而透明的 task family 开始:

\[\begin{aligned} \mathrm{family}&\sim\{\mathrm{linear},\mathrm{nonlinear},\mathrm{threshold},\mathrm{heteroskedastic}\},\\ P&\sim\operatorname{Uniform}(\text{feature-width range}),& N_c&\sim\operatorname{Uniform}(\text{context-size range}),\\ N_q&\sim\operatorname{Uniform}(\text{query-size range}),& \mathrm{noise}&\sim\text{configured distribution}. \end{aligned}\]

对每个 task 先采样 latent state,再生成一张 episode。将 TaskSpecEpisode 分开很重要:

  • TaskSpec 描述出题配置与 provenance(含 prior_split);
  • Episode 是模型实际允许看到的 tensor 与合法 metadata;
  • 未观察的 latent state(真 DAG、父节点集合)不能通过 shortcut 进入 model input。

6. SCM 的最小基础

一个简化 structural causal model 可写成一组结构方程:

\[Z_1=f_1(U_1),\qquad Z_2=f_2(Z_1,U_2),\qquad Y=f_y(Z_1,Z_2,U_y)\]

其中 \(U\) 是噪声或 exogenous variables,\(f\) 是生成函数。它提供可控的相关结构、非线性与噪声机制, 适合作为 synthetic task prior 的一个 family。

按拓扑序生成特征时,也可写成:

\[X_j=f_j\!\left(X_{\operatorname{pa}(j)}\right)+\varepsilon_j, \qquad Y=g(X_{\mathrm{target}},\varepsilon_y)\]

但要保持边界:

  • 生成器有已知 DAG,不代表模型能从 observational episode 恢复 DAG;
  • 在 synthetic SCM 上 prediction 变好,不代表现实世界 causal identification 成立;
  • 变量名或结构方程是 generator provenance,不是模型输入中的“真相标签”;
  • counterfactual 能力需要单独定义 intervention、潜在结果与评估 protocol(本课不做)。

7. Seed hierarchy:可复现不是只记一个 seed

至少区分(canon §3.8):

\[\begin{array}{ll} \mathrm{run\_seed} & \text{整个训练 run}\\ \mathrm{task\_seed} & \text{一个 task 的 latent state}\\ \mathrm{episode\_seed} & \text{context/query 采样与 row split}\\ \mathrm{model\_seed} & \text{参数初始化与 dropout} \end{array}\]

如果所有随机过程共用一个隐式 global seed,出现差异时无法判断是 task 变了、batch 变了还是模型初始化 变了。seed hierarchy 让 same-seed replay 和 controlled intervention 成为可能。

8. Query-only loss

合法形式:

\[\widehat{Y}_{\mathrm{query}} =f_\theta(X_{\mathrm{context}},Y_{\mathrm{context}},X_{\mathrm{query}},\mathrm{masks}), \qquad \mathcal{L} =\operatorname{loss\_fn}\!\left(\widehat{Y}_{\mathrm{query}},Y_{\mathrm{query}}\right)\]

不能这样做:

\[\widehat{Y}_{\mathrm{query}} =f_\theta(X_{\mathrm{context}},Y_{\mathrm{context}},X_{\mathrm{query}},Y_{\mathrm{query}})\]

也不能把 \(Y_{\mathrm{query}}\) 放进 batch normalization、统一 label tensor、padding statistics 或 cache。query target 只能作为 prediction 之后的监督信号。这与第 5 课的 leakage hard gate 同一条边界: 训练监督与 prediction input 必须分离。

9. Trainer 的最小契约

for step in range(num_steps):
    batch = prior.sample_batch(generator)
    pred = model(
        batch.x_context,
        batch.y_context,
        batch.x_query,
        context_mask=batch.context_mask,
        query_mask=batch.query_mask,
    )
    loss = criterion(pred, batch.y_query)
    optimizer.zero_grad(set_to_none=True)
    loss.backward()
    optimizer.step()

训练记录至少包括:run/task/episode seed、prior configuration、checkpoint step、train query loss、 validation held-out task loss、资源信息与 wall-clock。只有 loss、没有 seed/split/prior 版本,实验 不可复盘。

10. OOD prior split

如果 train 和 test 使用完全相同的 task family、feature width、noise 和 context size,结果只能说明 in-distribution generalization。OOD 必须在 task generation 层定义,而不是生成全部数据后随机切 row。一个合法例子:

\[\begin{aligned} \mathrm{train:}\quad&P\in[2,5],\quad \mathrm{noise}=\text{low},\quad \text{linear}+\text{mild nonlinear},\\ \mathrm{test:}\quad&P\in[6,8],\quad \mathrm{noise}=\text{higher},\quad \text{stronger nonlinear}. \end{aligned}\]

报告中要写清楚 OOD 改变了哪些 prior dimension,不能只说“测试集更难”。官方也单独加入 out-of-distribution prior;课程用更小的可控轴变化即可,但语义必须是 task-level。

11. Loss、泛化与 checkpoint 的关系

训练 loss 下降说明模型在当前采样分布的 query targets 上优化成功;它不自动说明:

  • held-out task 表现更好;
  • OOD prior 上仍然有效;
  • 输出概率校准;
  • 模型使用了 context,而不是 shortcut;
  • 架构组件的解释与论文一致。

因此每个 checkpoint 都应在固定 validation episodes 和 held-out task episodes 上评估,避免只挑最好看的 train curve。第 8 课会把这些数字挂到明确的 claim 上。

12. 计算预算与 prior 设计

扩大 prior 的 family、\(N_c\) 和 \(P\) 会提升覆盖范围,也会增加计算量。合理的课程实验先固定一个小 prior,建立端到端证据,再一次只改变一个维度:

  1. 增加 task family;
  2. 增加 context size;
  3. 增加 feature width;
  4. 增加 noise 或 missingness;
  5. 增加 OOD distance。

否则一次改太多,loss 变化没有可解释归因。

13. 四个必做实验

实验 A:same-seed replay

同一个 task/episode seed 产生逐元素相同的 tensor、label 和 mask。

实验 B:query-only loss audit

用不同 \(Y_{\mathrm{query}}\) 的 evaluation copy 进行 prediction,输出必须相同;用真实 target 计算 loss 后,loss 才可以不同。

实验 C:OOD prior split

固定训练 budget,比较 IID 与 OOD task performance,并描述 prior 改变的维度。

实验 D:loss vs generalization

画 train query loss、validation task loss、OOD loss 三条曲线,说明它们何时分叉。

14. 知识检查

  1. prior 生成的对象是 row、dataset 还是 task?为什么?
  2. outer loop 与 inner computation 的时间尺度有什么区别?
  3. 为什么 synthetic SCM 不能自动支持 causal claim?
  4. 为什么需要多个 seed,而不是只保存一个 global seed?
  5. query-only loss 如何同时做到训练监督与 prediction boundary 分离?
  6. OOD split 为什么必须在 task generation 层定义?
  7. 官方 prior 的哪几维是课程明确“列名对齐、不实现”的?

15. 本课的硬结论

Synthetic prior training 的目标是让共享参数跨 episode 学习一种预测程序。它的可信度依赖 prior 定义、 seed hierarchy、query-only loss、held-out task / OOD split 和 checkpoint evidence;synthetic generator 本身不是现实因果能力的证明。官方 prior 维可作为对照地图;课程只对已实现轴负责。

HAND-ON LAB · 工程实现与验收包

第 7 课:Synthetic Prior 与预训练

项目问题

PFN 不在一张固定表上把参数拟合到终点,而是在不断生成的新任务上训练一个跨任务预测算法。本课把 “生成任务”和“训练模型”接起来,并与官方 TabPFN-3 synthetic prior 维做对照(实现范围见下)。

简化 SCM prior

\[X_j=f_j\!\left(X_{\operatorname{pa}(j)}\right)+\varepsilon_j, \qquad Y=g(X_{\mathrm{target}},\varepsilon_y)\]

每个 episode 随机化特征数 \(P\)、样本数 \(N_c,N_q\)、图结构、线性/非线性关系、交互、噪声、目标类型 和类别数 \(C\)。

官方 prior 维(列名对齐)

官方维Mini 是否实现
Graph generation是(简化族)
Combiner mechanisms是(简化函数族)
Categorical / oscillators / spatial / temporal否或极简;报告中标明
Many-class prior小 \(C\);提醒官方 \(C_{\max}=160\)
OOD prior是(task 生成层 split)

实现任务

  • data/scm.py
  • data/functions.py:线性、阈值、周期、异方差函数;
  • data/prior_sampler.py
  • training/trainer.pylosses.pycheckpoint.py
  • 分类与回归统一 episode API。

验证实验

  1. 每个 seed 能重放同一批任务;
  2. 训练 loss 下降不等于新任务能力,必须保留 held-out task;
  3. 记录任务分布、模型配置、环境和 checkpoint;
  4. 对 prior 中未出现的组合做一轮 OOD 任务测试。

边界

synthetic SCM prior 是任务生成器与 inductive bias,不是现实数据上的因果识别证明;课程也不声称复现 官方 TabPFN-3 的完整训练 recipe 或 8T-token 规模。

交付

scm.pyprior_sampler.py、训练脚本、checkpoint、held-out task report。

本课的具体教学包

本课把 task generator 接到 end-to-end trainer。重点不是造一个“看起来像真实数据”的随机表,而是 定义一个可记录、可重放、可做 OOD split 的 task distribution。

时间内容证据
00:00–00:25prior 是 task distribution,不是数据增强画出 outer/inner loop
00:25–00:55DAG/SCM 与 function family;官方维对照表采样一张小图
00:55–01:30prior sampler implementationtask manifest
01:30–01:45休息检查 seed hierarchy
01:45–02:20query-only loss trainerloss trace + checkpoint
02:20–02:50held-out/OOD task evaluationtrain vs held-out 曲线
02:50–03:00reviewprior boundary 与交付

1. Prior 的对象和 outer loop

PFN 训练的抽象形式(对齐 canon §3.8 / §3.1)是:

\[\begin{aligned} \mathrm{task\_spec}&\sim p(\mathrm{task\_spec}),\\ \mathrm{episode}&\sim p(\mathrm{episode}\mid\mathrm{task\_spec}),\\ (\mathrm{context},\mathrm{query})&=\operatorname{sample\_episode}(\mathrm{task\_spec}),\\ \widehat{Y}_{\mathrm{query}}&=f_\theta(X_{\mathrm{context}},Y_{\mathrm{context}},X_{\mathrm{query}},\mathrm{masks}),\\ \mathcal{L}&=\operatorname{loss\_fn}\!\bigl(\widehat{Y}_{\mathrm{query}},Y_{\mathrm{query}}\bigr),\qquad \theta\leftarrow\operatorname{Update}(\theta,\nabla_\theta\mathcal{L}). \end{aligned}\]

p(task_spec) 是课程的 synthetic prior。每个 task 可以改变 \(P\)、\(N_c\)、噪声、函数族、图结构和 target kind。训练 loss 只使用 query labels;context labels 作为输入条件。不能把全表 target 先喂给 encoder 再称为 ICL。

2. 简化 SCM prior 的实现拆解

先采样有向无环图,再按 topological order 生成 feature:

\[X_j=f_j\!\left(X_{\operatorname{pa}(j)}\right)+\varepsilon_j, \qquad Y=g(X_{\mathrm{target}},\varepsilon_y)\]

课程需要至少支持以下离散选择:

graph: chain / star / sparse random DAG
function: linear / threshold / sinusoid / pairwise interaction
noise: gaussian / laplace / heteroskedastic
target: binary / multiclass / scalar regression

推荐的 TaskSpec

@dataclass(frozen=True)
class TaskSpec:
    task_id: int
    kind: Literal["classification", "regression"]
    n_features: int
    n_classes: int | None
    n_context: int
    n_query: int
    graph_family: str
    function_family: str
    noise_family: str
    prior_split: Literal["train", "heldout", "ood"]

TaskSpec 是 provenance metadata;模型不应收到 graph_family、latent adjacency 或 true parents。

3. 采样器与 seed hierarchy

把一个 global seed 拆成可定位的子 seed(canon §3.8):

\[\mathrm{run\_seed} \longrightarrow\mathrm{epoch\_seed} \longrightarrow\mathrm{task\_seed} \longrightarrow \{\mathrm{feature\_noise\_seed},\mathrm{target\_noise\_seed}\}\]

并与讲义中的 episode_seed / model_seed 一并写入 manifest。当一个 episode 失败时,可以用 task_id 和 seed 逐项 replay,而不需要保存完整 tensor。每个 checkpoint 必须保存:

model_config, optimizer_config, run_seed, prior_config,
git/source identifier, torch version, device, global_step

不要只保存 model.state_dict();否则无法判断某个结果来自哪一种 task distribution。

4. Trainer 的 exact contract

class EpisodeTrainer:
    def train_step(self, batch: list[Episode]) -> dict[str, float]:
        self.optimizer.zero_grad(set_to_none=True)
        losses = []
        for episode in batch:
            pred = self.model(
                episode.x_context, episode.y_context, episode.x_query
            )
            losses.append(self.loss_fn(pred, episode.y_query, episode.task_kind))
        loss = torch.stack(losses).mean()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
        self.optimizer.step()
        return {"loss": float(loss.detach())}

真实实现可以 vectorize;课堂先要求读清楚“每个 episode 的 query-only loss”,再做 batch 优化。 classification 与 regression 若共享模型,head dispatch 必须由 task_kind 控制,不能让 target dtype 暗示任务类型。

5. Reference training run

为了让第 8 课有稳定输入,课程参考 run 可以先用一个小预算:

train tasks sampled on the fly: 2,000 episodes
batch size: 8 episodes
optimizer: AdamW, lr=3e-4
gradient clip: 1.0
validation: every 100 steps, 100 fresh held-out tasks
checkpoint: best validation + final step

这些数是课程 smoke run,不是官方 TabPFN-3 recipe,也不构成性能承诺。若设备不足,减少 steps,但 必须保留完整 manifest 和 held-out evaluation。

6. 必做验证

实验 A:same-seed replay

同一 TaskSpec/task_seed 必须生成相同的 X/Y。如果使用 nondeterministic GPU kernel,记录这一事实, 并在 CPU smoke test 上完成逐元素 replay。

实验 B:query-only loss audit

在 trainer 中临时把 y_query 替换为随机值,确认只有 loss/metrics 改变,model input、context hidden 和 prediction 不应因 evaluation-only label 改变。这个审计要和第 5 课的 forward leakage test 区分开: 第 5 课查 ICL 输入路径;本课查 trainer / collate / loss 路径。

实验 C:OOD prior split

训练只使用 linear + chain,held-out 至少包含:

nonlinear + chain
linear + sparse random DAG
heteroskedastic regression

OOD 任务结果可以变差;课程要报告变化而不是挑最好的一组。

实验 D:loss vs generalization

同时画 train episode loss、held-out task loss、classification calibration 或 regression coverage。 训练 loss 下降不是“学会跨任务算法”的充分证据。

常见失败

  • 先生成一张固定 synthetic table,再反复 shuffle rows;
  • 每个 task 都使用同一 graph/function/noise,prior 实际没有 task diversity;
  • query labels 在 collate 阶段进入 model input;
  • checkpoint 没有 prior/config/seed,结果无法 replay;
  • 把 synthetic SCM 的生成结构写成模型已经识别现实因果结构;
  • 只看 train loss,不做 fresh held-out tasks;
  • 把官方 prior 的未实现维写成“课程已覆盖”。

退出条件与评分

通过条件:

  1. 至少 4 个 prior axes 可配置并记录;
  2. 同一 seed 可以 replay task;
  3. trainer 的 loss 只在 query target 上计算;
  4. checkpoint 能恢复 model、optimizer、prior config 和 step;
  5. 至少一组 OOD prior split 有独立报告;
  6. 明确 synthetic prior 是 inductive bias,不是 causal identification evidence;
  7. 报告中有官方 prior 维对照表(实现 / 未实现)。

评分建议:prior design 25%,trainer/loss 30%,replay/checkpoint 20%,OOD evidence 15%,边界 10%。

课后作业

加入一个新 function family,但先写 task-level test 证明它真的改变了生成分布,再把它加入 train 或 OOD split。不能只改函数名而不改变 episode statistics。