每个维度都只是 \(z\) 的仿射函数
当 \(z\) 从小到大变化,\(e_{\mathrm{lin}}(z)\) 只能沿方向 \(w\) 前后移动。即使 \(E=256\),这个轨迹的仿射维数仍不超过 1。
TabFM 没有让数值与 ordinal-encoded 类别值直接穿过普通线性层。它先让每个预处理后的 scalar 同时走过一组转速可学习的 sin / cos 钟面,再把这些周期坐标投影成 cell embedding。 精彩之处不是“用了傅里叶”六个字,而是它把一个贫瘠的一维入口,改造成了可训练、分类型、跨表共享的基函数接口。
(Ωnum, Wnum) 与 (Ωcat, Wcat) 互不共享;同一类型的所有列复用同一套。普通仿射 embedding 让 scalar \(z\) 随数值变化时只能在高维空间里走一条直线; learned Fourier lift 先把 \(z\) 变成许多不同尺度的周期响应,再线性组合,于是第一层就能表达平滑趋势、局部变化与快速区分。
但:它没有创造新的语义,也没有严格消除 ordinal 编号的人为顺序。它是更好的 value tokenizer inductive bias,不是 categorical identity 或 permutation-invariance 的证明。
把一个 scalar 送进 256 维线性层听起来很“宽”,但输入只有一个自由度。输出坐标再多,仍然全部跟着同一个 \(z\) 同步伸缩。
当 \(z\) 从小到大变化,\(e_{\mathrm{lin}}(z)\) 只能沿方向 \(w\) 前后移动。即使 \(E=256\),这个轨迹的仿射维数仍不超过 1。
后续 Transformer / MLP 本来就有非线性,所以 direct linear input 并不等于整个模型只能拟合线性函数。Fourier features 的作用,是把有用的非线性基函数提前放到入口,减少后续网络从一条直线表示中“重新发明”数值几何的负担。
一对 \((\sin(\omega z),\cos(\omega z))\) 就是单位圆上的一个点。低频钟缓慢转动,保留大尺度邻近;高频钟快速转动,对细小变化更敏感。
sin = · · · / cos = · · ·
sin = · · · / cos = · · ·
sin = · · · / cos = · · ·
这三个频率只用于直觉演示,不是 TabFM checkpoint 参数。TabFM release 使用每个 group slot 32 个可学习频率;真实值由预训练得到。
\(z\) 变化一点,钟面只移动一点;适合保留连续数值的平滑邻近。
很小的输入变化也会带来明显相位变化;可让相邻 category codes 更容易去相关。
\(\omega_k\) 不是固定手工网格,而是参与预训练更新;模型自己决定哪些尺度有用。
官方 sklearn wrapper 先做类型识别、ordinal encoding、标准化与 ensemble preprocessing;release 默认还会把三个 feature slots 组成一组。Fourier 层看到的是 \(z\),不是原始字符串或未经处理的整数。
37.2 或 "北京",同时保留 column type。
类别按 appearance / frequency / alphabetical 变成整数;unknown / missing 为 −1。
所有列先标准化;ensemble 可再走 power 等 normalization,并处理 outliers。
v1.0.0 默认把三个循环偏移的 feature slots 编组;每个 slot 保留自己的 type mask。
每个 slot 计算 32 个频率的 sin/cos,得到 64 维 basis。
numeric / categorical 走不同 linear head;三个 slot 的结果最后求和。
数值路径可以学习较温和的频率,尽量保留 metric continuity;这是源码注释给出的设计动机,不是一个被公开定理约束的频率范围。
类别路径可以学习更高频的响应,让相邻 integer codes 不必被迫拥有相近 embedding;但 code 本身依然来自当前数据集的 ordinal mapping。
不是“一列一套参数”,也不是“所有 cell 共用一套参数”。TabFM 有两条彼此独立的类型通道:
所有 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 |
| projection | in_linear | in_linear_cat | 两套不共享的 64 → 256 heads;各自在同类型所有列间共享 |
| routing | cat_mask=False | cat_mask=True | 在 slot 级选择后,再沿 3-slot group 求和 |
| 输入 | 标准化后的数值 | ordinal encode 后再标准化的数值 | 都不是未经处理的 raw value |
OrdinalEncoder 必须给字符串分配整数才能进入统一数值接口。但 nominal categories 本来没有“0 比 1 更近”的天然语义。高频映射能削弱这种近邻暗示,却不能让编号选择消失。
不要写“TabFM 用 Fourier features 解决了 categorical encoding”。更准确的是:TabFM 把 ordinal-coded categorical values 送入独立的可学习频谱通道,使模型不必沿用数值通道的平滑几何;但 categorical identity、语义与严格重编号不变性仍未由此得到。
核心计算很短;严谨之处在 parameter / buffer、type routing、group reduction 和数值精度。
# 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]
JAX 源码把频率声明为 nnx.Param。PyTorch inference port 用 register_buffer 接收 checkpoint 中的频率;公开仓库未提供训练循环,因而不能独立审计 optimizer 对这些参数的更新轨迹。
PyTorch 路径显式把 \(z\omega\) 与频率升到 float32 再做三角函数,随后才 cast 回 compute dtype;源码注释指出相位可到约 30。
classification target \(y\) 仍用 lookup;regression target \(y\) 仍用 MLP。JAX 源码甚至留下“以后尝试 Fourier y embedding”的 TODO。
这不是用一个复杂模块取代整个网络,而是在最早、最窄、最容易低秩塌缩的位置,提供一组能随任务一起学习的 basis functions。
单个 scalar 不再只控制一个方向,而是同时触发多尺度 phase signature。
frequency banks 参与预训练,让不同数据生成任务共同塑造 value geometry。
numeric 与 categorical 不共享 frequency bank 或 projection;但同类所有列共享参数,因此仍能跨不同 schema 复用。
各种 feature values 最后都成为 256 维 cell embeddings,能继续进入统一的 row / column backbone。
每个 basis 在整条数轴上振荡,天然适合表达多尺度变化;多个频率共同工作时,单频率的周期碰撞会更难同时发生。
LimiX-2M 的 RaBEL 从局部 RBF basis 出发缓解 scalar tokenizer 的低秩通道。两条路线都在回答“一个数怎样变成丰富 token”,但 inductive bias 不同,值得另开一篇比较。
越喜欢一个设计,越要把它的 claim boundary 写清。以下边界不是挑刺,而是让亮点保持可信。
“北京”先变成当前数据集里的 integer code。Fourier 层只看预处理后的数,不读字符串,也不拥有跨表永久 identity。
改变 code 会改变 phase。若要声称 invariance,需要专门的 architecture、symmetrization 或等式级测试,不能从“用了高频”推出。
Fourier lift 是可逆性未保证的 nonlinear feature map;它改善 basis,却无法恢复 preprocessing 已经抹去的语义、单位或测量过程。
本文确认的是官方 release code path。代码里的设计动机不等同于独立实验证据,也不证明这一步单独带来多少指标提升。
P11 当前只冻结了“shared ValueEncoder”这个接口,具体采用 Fourier、RBF、piecewise-linear、type-specific adapters 还是其他方案仍未决定。本文把 TabFM 的做法变成一个高质量候选与实验问题,不把它偷偷写成 P11 已采用的架构。
如果我们要吸收这项亮点,下一步不是立刻把 “Fourier” 写进模型名,而是验证它是否改善 P11 真正在意的 typed response、跨 schema transfer 与 category robustness。
| 候选 encoder | 它提供的偏置 | 最小必须测的量 | 失败时说明什么 |
|---|---|---|---|
| direct linear | 最小、平滑、低成本 baseline | value sensitivity / effective rank / downstream accuracy | 后续 backbone 是否足以补回非线性 |
| shared Fourier | 统一多尺度 basis | 数值任务收益与类别干扰 | 类型冲突是否真实存在 |
| split Fourier banks | numeric continuity 与 category decorrelation 分流 | category relabel variance + numeric calibration | 独立 bank 是否只是增加参数 |
| local RBF / RaBEL-like | 局部 basis 与近邻响应 | 低秩诊断、out-of-range、算力与内存 | global periodic 与 local radial 哪个更匹配任务 |
同一 nominal column 做多次合法 code permutation,测单次 forward 与 ensemble prediction 的方差,而不是只看平均 accuracy。
平移、缩放、heavy tail、稀疏区间与 out-of-range query 分开评估,确认 preprocessing 和 learned frequencies 的责任。
把 value geometry 与 P11 的 semantic feature token 分开 ablate,避免把列描述收益错记到 scalar tokenizer 上。
截至 2026-07-31,P11 catalog 记录 TabFM 为官方模型/代码发布,尚未定位到 archival paper。因此本文把“官方发布主张”和“源码可核对事实”分开。
b8a8b090…可以引用它解释 TabFM release code 的 value-embedding path;不应把它当作 TabFM 作者论文、独立复现、性能归因或 P11 方法定义。