TabFM资料技术细节
P11 Technical Highlights · 01 · TabFM code reading

一个数,为什么先绕进频率空间?

TabFM 没有让数值与 ordinal-encoded 类别值直接穿过普通线性层。它先让每个预处理后的 scalar 同时走过一组转速可学习的 sin / cos 钟面,再把这些周期坐标投影成 cell embedding。 精彩之处不是“用了傅里叶”六个字,而是它把一个贫瘠的一维入口,改造成了可训练、分类型、跨表共享的基函数接口。

对象TabFM v1.0.0Google Research 官方发布与代码,不是当前 P11 自有模型。
释放配置32 frequencies每个 slot 先得到 64 维 sin/cos basis,再投影到 256 维。
共享边界按类型分开,按列共享num, Wnum)cat, Wcat) 互不共享;同一类型的所有列复用同一套。
证据边界code-level highlight公开材料没有让这篇解读自动成为性能 ablation 或 P11 采用决定。
The shortest answer

普通仿射 embedding 让 scalar \(z\) 随数值变化时只能在高维空间里走一条直线; learned Fourier lift 先把 \(z\) 变成许多不同尺度的周期响应,再线性组合,于是第一层就能表达平滑趋势、局部变化与快速区分。

但:它没有创造新的语义,也没有严格消除 ordinal 编号的人为顺序。它是更好的 value tokenizer inductive bias,不是 categorical identity 或 permutation-invariance 的证明。

01 · Geometry

宽线性层,仍然只画出一条直线

把一个 scalar 送进 256 维线性层听起来很“宽”,但输入只有一个自由度。输出坐标再多,仍然全部跟着同一个 \(z\) 同步伸缩。

Direct affine embedding

每个维度都只是 \(z\) 的仿射函数

\[e_{\mathrm{lin}}(z)=z\,w+b\in\mathbb R^E\]

当 \(z\) 从小到大变化,\(e_{\mathrm{lin}}(z)\) 只能沿方向 \(w\) 前后移动。即使 \(E=256\),这个轨迹的仿射维数仍不超过 1。

线性 embedding 与 Fourier lift 的几何对比 左侧是 scalar 经过线性层形成直线,右侧是经过多个周期坐标形成弯曲轨迹。 direct: 一条 affine line Fourier lift: curved basis path
示意图,只表达几何直觉不是 checkpoint PCA

别把这个判断说得过头

后续 Transformer / MLP 本来就有非线性,所以 direct linear input 并不等于整个模型只能拟合线性函数。Fourier features 的作用,是把有用的非线性基函数提前放到入口,减少后续网络从一条直线表示中“重新发明”数值几何的负担。

02 · Intuition lab

把同一个数交给很多只转速不同的钟

一对 \((\sin(\omega z),\cos(\omega z))\) 就是单位圆上的一个点。低频钟缓慢转动,保留大尺度邻近;高频钟快速转动,对细小变化更敏感。

LOW CLOCKω₁ = 0.55sin = · · · / cos = · · ·
MID CLOCKω₂ = 1.35sin = · · · / cos = · · ·
HIGH CLOCKω₃ = 2.70sin = · · · / cos = · · ·
φ(z) = [ · · · ]

这三个频率只用于直觉演示,不是 TabFM checkpoint 参数。TabFM release 使用每个 group slot 32 个可学习频率;真实值由预训练得到。

\[\phi_{\Omega}(z)=\big[\sin(\omega_1z),\ldots,\sin(\omega_Fz),\cos(\omega_1z),\ldots,\cos(\omega_Fz)\big]\in\mathbb R^{2F}\]
\[\phi_{\Omega}(z)^\top\phi_{\Omega}(z')=\sum_{k=1}^{F}\cos\!\big(\omega_k(z-z')\big)\]
低频

看大尺度

\(z\) 变化一点,钟面只移动一点;适合保留连续数值的平滑邻近。

高频

看细变化

很小的输入变化也会带来明显相位变化;可让相邻 category codes 更容易去相关。

学习频率

让任务选尺度

\(\omega_k\) 不是固定手工网格,而是参与预训练更新;模型自己决定哪些尺度有用。

03 · Actual path

真实路径比“scalar → sin/cos”再多三层

官方 sklearn wrapper 先做类型识别、ordinal encoding、标准化与 ensemble preprocessing;release 默认还会把三个 feature slots 组成一组。Fourier 层看到的是 \(z\),不是原始字符串或未经处理的整数。

01 · RAW数值 / 字符串

37.2"北京",同时保留 column type。

02 · COERCEordinal code

类别按 appearance / frequency / alphabetical 变成整数;unknown / missing 为 −1。

03 · PREPROCESS\(z=P_{\mathcal D}(x)\)

所有列先标准化;ensemble 可再走 power 等 normalization,并处理 outliers。

04 · GROUP[h, h+1, h+3]

v1.0.0 默认把三个循环偏移的 feature slots 编组;每个 slot 保留自己的 type mask。

05 · FOURIER32 → 64

每个 slot 计算 32 个频率的 sin/cos,得到 64 维 basis。

06 · PROJECT64 → 256

numeric / categorical 走不同 linear head;三个 slot 的结果最后求和。

Numerical slot
\[e_{\mathrm{num}}(z)=W_{\mathrm{num}}\,\phi_{\Omega_{\mathrm{num}}}(z)+b_{\mathrm{num}}\]

数值路径可以学习较温和的频率,尽量保留 metric continuity;这是源码注释给出的设计动机,不是一个被公开定理约束的频率范围。

Categorical slot
\[e_{\mathrm{cat}}(z)=W_{\mathrm{cat}}\,\phi_{\Omega_{\mathrm{cat}}}(z)+b_{\mathrm{cat}}\]

类别路径可以学习更高频的响应,让相邻 integer codes 不必被迫拥有相近 embedding;但 code 本身依然来自当前数据集的 ordinal mapping。

The exact sharing rule

不是“一列一套参数”,也不是“所有 cell 共用一套参数”。TabFM 有两条彼此独立的类型通道:

\[\underbrace{(\Omega_{\mathrm{num}},W_{\mathrm{num}},b_{\mathrm{num}})}_{\text{all numerical columns share}}\quad\neq\quad\underbrace{(\Omega_{\mathrm{cat}},W_{\mathrm{cat}},b_{\mathrm{cat}})}_{\text{all categorical columns share}}\]

所有 numerical cells——无论来自哪张表、哪一行、哪一个 numerical column——共享 numerical bank 与 numerical projection;所有 categorical cells 同理共享另一套 categorical bank 与 categorical projection。频率张量的第一维 3 表示 group slot 的位置,不是 column identity。

release 配置 / 张量数值类别准确读法
frequency bank\(\Omega_{\mathrm{num}}\in\mathbb R^{3\times32}\)\(\Omega_{\mathrm{cat}}\in\mathbb R^{3\times32}\)两组独立 nnx.Param;各自在同类型所有列间共享
basis每 slot 64 维每 slot 64 维32 个 sin + 32 个 cos
projectionin_linearin_linear_cat两套不共享的 64 → 256 heads;各自在同类型所有列间共享
routingcat_mask=Falsecat_mask=True在 slot 级选择后,再沿 3-slot group 求和
输入标准化后的数值ordinal encode 后再标准化的数值都不是未经处理的 raw value
04 · Categorical reality

类别频率库在修补编号几何,而不是理解“北京”

OrdinalEncoder 必须给字符串分配整数才能进入统一数值接口。但 nominal categories 本来没有“0 比 1 更近”的天然语义。高频映射能削弱这种近邻暗示,却不能让编号选择消失。

Relabeling demo

当前 mapping:按首次出现顺序

一个更精确的说法

不要写“TabFM 用 Fourier features 解决了 categorical encoding”。更准确的是:TabFM 把 ordinal-coded categorical values 送入独立的可学习频谱通道,使模型不必沿用数值通道的平滑几何;但 categorical identity、语义与严格重编号不变性仍未由此得到。

05 · Implementation

最短伪代码,以及三处真正容易漏掉的实现事实

核心计算很短;严谨之处在 parameter / buffer、type routing、group reduction 和数值精度。

TabFM CellEmbedder · conceptual pseudocodeshapes follow v1.0.0 release
# z_grouped: [batch, rows, columns, group_slots=3]
omega_num = Parameter(shape=[3, 32])
omega_cat = Parameter(shape=[3, 32])

def fourier(z, omega):
    phase = z[..., None] * omega              # [..., 3, 32]
    return concat(sin(phase), cos(phase)) # [..., 3, 64]

num = Linear_num(fourier(z_grouped, omega_num)) # [..., 3, 256]
cat = Linear_cat(fourier(z_grouped, omega_cat)) # [..., 3, 256]
slot = where(cat_mask[..., None], cat, num)
cell_embedding = slot.sum(axis=group_slots)      # [..., 256]
Train vs serve

JAX 定义参数,PyTorch 装载值

JAX 源码把频率声明为 nnx.Param。PyTorch inference port 用 register_buffer 接收 checkpoint 中的频率;公开仓库未提供训练循环,因而不能独立审计 optimizer 对这些参数的更新轨迹。

Precision

sin / cos 在 float32 做

PyTorch 路径显式把 \(z\omega\) 与频率升到 float32 再做三角函数,随后才 cast 回 compute dtype;源码注释指出相位可到约 30。

Scope

Fourier 只覆盖 X cells

classification target \(y\) 仍用 lookup;regression target \(y\) 仍用 MLP。JAX 源码甚至留下“以后尝试 Fourier y embedding”的 TODO。

06 · Why it is exciting

它精彩在“入口接口”的重新设计

这不是用一个复杂模块取代整个网络,而是在最早、最窄、最容易低秩塌缩的位置,提供一组能随任务一起学习的 basis functions。

01

用 64 个响应描述 1 个数

单个 scalar 不再只控制一个方向,而是同时触发多尺度 phase signature。

02

尺度不是手工写死

frequency banks 参与预训练,让不同数据生成任务共同塑造 value geometry。

03

分类型,跨列共享

numeric 与 categorical 不共享 frequency bank 或 projection;但同类所有列共享参数,因此仍能跨不同 schema 复用。

04

仍保持统一张量接口

各种 feature values 最后都成为 256 维 cell embeddings,能继续进入统一的 row / column backbone。

Fourier basis:全局、周期

每个 basis 在整条数轴上振荡,天然适合表达多尺度变化;多个频率共同工作时,单频率的周期碰撞会更难同时发生。

邻近路线:局部 radial basis

LimiX-2M 的 RaBEL 从局部 RBF basis 出发缓解 scalar tokenizer 的低秩通道。两条路线都在回答“一个数怎样变成丰富 token”,但 inductive bias 不同,值得另开一篇比较。

07 · Boundaries

这项设计没有自动解决什么?

越喜欢一个设计,越要把它的 claim boundary 写清。以下边界不是挑刺,而是让亮点保持可信。

No semantics

没有从字符串中恢复类别语义

“北京”先变成当前数据集里的 integer code。Fourier 层只看预处理后的数,不读字符串,也不拥有跨表永久 identity。

No exact invariance

没有严格证明 category relabeling 不变

改变 code 会改变 phase。若要声称 invariance,需要专门的 architecture、symmetrization 或等式级测试,不能从“用了高频”推出。

No new information

没有凭空创造被输入丢掉的信息

Fourier lift 是可逆性未保证的 nonlinear feature map;它改善 basis,却无法恢复 preprocessing 已经抹去的语义、单位或测量过程。

No performance proof

没有公开 ablation 就不替作者补结果

本文确认的是官方 release code path。代码里的设计动机不等同于独立实验证据,也不证明这一步单独带来多少指标提升。

P11 的额外边界

P11 当前只冻结了“shared ValueEncoder”这个接口,具体采用 Fourier、RBF、piecewise-linear、type-specific adapters 还是其他方案仍未决定。本文把 TabFM 的做法变成一个高质量候选与实验问题,不把它偷偷写成 P11 已采用的架构

08 · Back to P11

真正值得带回 P11 的,是一组可证伪的 ValueEncoder 对照

如果我们要吸收这项亮点,下一步不是立刻把 “Fourier” 写进模型名,而是验证它是否改善 P11 真正在意的 typed response、跨 schema transfer 与 category robustness。

候选 encoder它提供的偏置最小必须测的量失败时说明什么
direct linear最小、平滑、低成本 baselinevalue sensitivity / effective rank / downstream accuracy后续 backbone 是否足以补回非线性
shared Fourier统一多尺度 basis数值任务收益与类别干扰类型冲突是否真实存在
split Fourier banksnumeric continuity 与 category decorrelation 分流category relabel variance + numeric calibration独立 bank 是否只是增加参数
local RBF / RaBEL-like局部 basis 与近邻响应低秩诊断、out-of-range、算力与内存global periodic 与 local radial 哪个更匹配任务
Test 1

重编号压力测试

同一 nominal column 做多次合法 code permutation,测单次 forward 与 ensemble prediction 的方差,而不是只看平均 accuracy。

Test 2

数值尺度压力测试

平移、缩放、heavy tail、稀疏区间与 out-of-range query 分开评估,确认 preprocessing 和 learned frequencies 的责任。

Test 3

跨 schema 压力测试

把 value geometry 与 P11 的 semantic feature token 分开 ablate,避免把列描述收益错记到 scalar tokenizer 上。

09 · Evidence trail

论文没有写出的细节,要回到官方代码

截至 2026-07-31,P11 catalog 记录 TabFM 为官方模型/代码发布,尚未定位到 archival paper。因此本文把“官方发布主张”和“源码可核对事实”分开。

Reading boundary

怎样引用这篇页面

可以引用它解释 TabFM release code 的 value-embedding path;不应把它当作 TabFM 作者论文、独立复现、性能归因或 P11 方法定义。