Skip to content

How a tabular foundation model predicts, step by step

Published:
•33 min read

Tabular foundation models

Tabular foundation models (TFMs) are pretrained networks that predict on a table they have never seen. The best known are TabPFN (PFN for prior-data fitted network, by Prior Labs), TabICL (ICL for in-context learning, by the SODA team at Inria) and Google’s TabFM. This post explains how they predict; the companion post tests them on a real sales forecast.

With LightGBM, fit() is where the work happens: the model looks at your rows and builds trees. With a TFM, fit() mostly stores your table (plus light preprocessing, and optionally a cache; see How the cost grows). The work happens in predict(): the stored rows (the context) and the rows you want answered (the test rows) go through the network together in one forward pass (in practice a few passes on shuffled copies of the table, averaged; see How the cost grows). The weights never change. This is called in-context learning: no training on your data.

The running churn table, three labelled customers and Dan, goes into a pretrained model with frozen weights; one forward pass returns Dan's churn probabilities.

The labelled rows are the context; Dan is the test row.

The running example

One small churn table is used all the way through: three customers have a known answer, and we predict the fourth.

Customerageincome (k€)tenure (years)churned?
Alice35502yes
Bob623010no
Carla28901yes
Dan45603?

Real tables have thousands of rows and dozens of columns. The steps are exactly the same.

The model described is TabICL v1, from the 2025 paper. The current tabicl package ships TabICLv2; its main changes are listed after the three stages. Sizes such as 128 are TabICL’s; other models use other sizes.

How it predicts: three stages

The big picture

A transformer works on tokens, the units it compares with each other. For text, a token is a piece of a word. For a table, there are three choices:

Three ways to turn a table into tokens: one token per row, one token per cell, or one token per cell that is then compressed into one vector per row.

TabPFN v2, the cell-per-token design here, actually puts two cells of a row in each token by default.

One token per cell keeps each value’s identity, but the number of tokens is rows times columns, so it is expensive. The newer designs start with cells and then compress each row into one vector before the step where rows are compared with each other, the expensive one.

TabICL is one of those newer designs. It runs the same kind of transformer block in three stages; each stage lets a different set of things look at each other.

TabICL-style pipeline: input table 4 × 3, stage 1 attends down each column (4 × 3 × 128), stage 2 attends across each row (4 × 512), stage 3 lets Dan read the labelled rows (1 × 512), and a 2-layer MLP head gives 1 × 2 churn probabilities.

TabICL-style pipeline, with the shapes for the example table

Stage 1 turns each number into a vector, stage 2 turns each row into one vector, and stage 3 lets Dan’s row read the labelled rows to predict his answer. Each stage answers one question:

Attention in one picture

Every stage uses the same block (stage 1 runs it through inducing points, below). Only two things change: what one token is (a cell, then a row) and who may look at whom.

One attention step: the asking token's query is scored against each other token's key; a softmax turns the scores into weights 0.6, 0.3 and 0.1; the new vector is 0.6 v1 + 0.3 v2 + 0.1 v3.

One attention step. d_k is the length of one head’s slice, defined below.

Each token carries three vectors, made by multiplying it with three learned matrices, W_Q, W_K and W_V:

Each of these vectors has the length of the token itself, e = 128 numbers in stages 1 and 2, 4e = 512 in stage 3. The block splits them into several slices, called heads, and runs the same attention on each slice side by side; d_k is the length of one slice. The heads’ outputs are glued back together and projected to the token’s length.

In each head, the token asking takes the dot product of its q with the k of every token it is allowed to look at. Each dot product is a single number, a score. A softmax turns the scores into weights that sum to 1, weight = softmax(q·k/√d_k). The token’s new vector is the weighted sum of the values: in the picture, 0.6 v1 + 0.3 v2 + 0.1 v3. q and k decide how much, v decides what.

After the attention, a small feed-forward network (FFN) runs on every token. Each of the two steps normalises its input, applies the step, and adds the result back to the input (a residual connection).

Stage 1: attention on the columns

Each cell becomes a vector that knows its column. 35 alone means nothing; 35 among ages 28 to 62 means “on the young side”. Stage 1 encodes that.

The age column goes through the column encoder: each cell becomes h = x·w + b, 128 inducing points attend to the context cells, every cell attends to them, and the embedding is W ⊙ x + B. Each age comes out as a vector of 128 numbers. Dan's cell only reads the summary.

Stage 1 on the age column

It works on one column at a time, with the same weights for every column:

  1. Each number becomes a vector, h = x·w + b, with w and b learned: x is one number, h is that number as a vector of length e.
  2. A Set Transformer (a transformer that treats the column as an unordered set) reads the column. Letting every cell attend to every other cell would cost rows², so it uses m = 128 inducing points instead: a fixed set of learned vectors that first read the context cells of the column, then are read by every cell. Cost grows linearly with rows instead of quadratically. TabICL stacks 3 of these blocks.
  3. The Set Transformer outputs, for each cell, a weight vector W and a bias vector B. The cell’s embedding is W ⊙ x + B (each of W’s 128 numbers multiplied by x, plus B): the raw number again, scaled and shifted by what the column looks like.

Like a normalisation, this makes values comparable across columns, but it is learned, and the output is 128 numbers per cell instead of one. In practice, you don’t have to scale your columns: stage 1 does it. Only context cells shape the summary: Dan’s cell reads it but does not write to it. That is the first, small way a test row uses the context rows.

Stage 2: attention on the rows

Stage 3 compares rows, so every row must become one vector of the same size, whether the table has 3 columns or 600.

Alice's row: her three cell vectors and four CLS tokens attend to each other across the row; the four CLS outputs are glued into one vector of 512 numbers.

Stage 2 on Alice’s row. Here h(35) means Alice’s age after all of stage 1 (its W ⊙ x + B embedding), not the x·w + b vector of step 1; W in the box stands for the attention matrices W_Q, W_K, W_V.

Stage 2 is a 3-layer transformer that runs on each row separately. Its tokens are the row’s cell vectors plus 4 CLS tokens (CLS for classification token, a name from BERT).

Stage 3: in-context attention

Dan learns from the labelled rows. Now each token is a whole row, the 512-number vector from stage 2. Labelled rows get their answer added as a learned vector (one-hot class × a learned matrix); Dan asks, they answer. Stage 3 is a 12-layer transformer, the deepest of the three.

Dan's row attends to the three labelled rows: Alice (churned yes) with weight 0.45, Carla (yes) 0.38, Bob (no) 0.17. After 12 layers, the head gives P(yes) = 0.80, P(no) = 0.20.

Stage 3: Dan attends to the labelled rows. Alice is closest to Dan on every column, and Carla is closer than Bob on tenure, so here they weigh most. In the real model, 12 layers of this and the MLP head produce the probabilities; they are not a sum of weights.

Stage 3 attention grid for Alice, Bob, Carla (labelled) and Dan, Eve (test rows): every row may attend to the three labelled rows; no row attends to Dan or Eve.

Who looks at whom in stage 3

Then the head turns Dan’s final vector into probabilities:

Shapes at each step

The table shrinks from one number per cell to one probability per class for each test row.

Tensor shapes drawn as boxes: input 4 × 3, after stage 1 4 × 3 × 128, after stage 2 4 × 4 × 128 (4 CLS vectors per row), glued 4 × 512, after stage 3 1 × 512 for Dan, after the head 1 × 2. Above the arrows, the question each stage answers.

Tensor shapes for the example table, drawn AlexNet-style

C = number of classes. In the example, 4e = 4 × 128 = 512, so n × 4e in the table is the 4 × 512 above.

StepOne token isAttention runsOur exampleGeneral
Inputnonenowhere4 × 3n × d
Stage 1one celldown each column4 × 3 × 128n × d × e
Stage 2one cell or one CLS tokenacross each row4 × 7 × 128 in, CLS out 4 × 4 × 128, glued → 4 × 512n × (d + 4) × e → n × 4e
Stage 3one rowacross rows: every row reads the labelled rows, never a test row4 × 512 in, Dan’s 1 × 512 keptn × 4e in, n_test × 4e kept
Headnonenowhere (an MLP on each test row)1 × 2n_test × C

What TabICLv2 changes, and regression

TabICLv2 keeps the three stages, with three main changes: stage 1 embeds each cell together with two other cells of its row; the labels also come in at stage 1, not only at stage 3, so every stage knows the task; and each query is rescaled according to the context length and the query itself (QASSMax, query-aware scalable softmax), in stage 1’s inducing points and in stage 3.

It also adds regression, which changes only the head and the label vector. TabICLv2 predicts 999 quantiles, trained with the pinball loss used to score quantile forecasts, and its label goes in as y·w + b instead of a one-hot class vector. TabPFN does it differently: it cuts the range of targets into bins and outputs a probability per bin, a histogram with two thin tails, so training is a classification over bins and values beyond the outermost bins can only come from the tails. Either way, every prediction comes with a full distribution, so quantiles come for free.

How the cost grows

Stage 3 is the expensive one as the context grows: its attention grows with the square of the labelled rows, and linearly with the test rows. More columns never touch stage 3, because stage 2 has already squeezed each row into one vector.

With n_ctx, n_test, d and e as above, and m = 128 inducing points: attention-score cost = number of query-key pairs × the vector size of each dot product.

StageAttention-score cost per layerRowsColumns
Stage 1, with inducing points (TabICL), per block(2·n_ctx + n_test) × d × m × elinearlinear
Stage 1, hypothetical without inducing points (not what TabICL does)d × (n_ctx + n_test) × n_ctx × equadratic in n_ctx, linear in n_testlinear
Stage 2(n_ctx + n_test) × (d + 4)² × elinearquadratic
Stage 3(n_ctx² + n_test × n_ctx) × 4equadratic in n_ctx, linear in n_testnone

Attention is not the whole bill. Every layer also projects each token into q, k and v, projects the result back, and runs an FFN: about 8 × width² multiply-adds per token, with width 128 in stages 1 and 2 and 512 in stage 3. And the stages have different depths: 3 inducing-point blocks, 3 row layers, 12 in-context layers. The two charts count all of it.

Whole-model multiply-adds per stage against labelled rows, log scales. Stage 3 grows with the square of the labelled rows, passes stage 1 near 290 rows, and is 43 times stage 1 at 100,000 rows.

Computed by scripts/generate-tfm-prequel-figures.mjs from TabICL v1 sizes; the other sizes are in the subtitle

With 50 columns and 1,000 test rows, stage 3 is the largest stage from about 290 labelled rows, because it is the widest and the deepest. Past a few thousand rows its rows² term takes over: at 100,000 labelled rows it does 96% of the work.

Whole-model multiply-adds per stage against the number of columns, log scales, with 10,000 labelled rows. Stage 3 is flat; stage 1 grows linearly and passes it near 256 columns; stage 2 grows faster and passes it near 248 columns.

Computed by scripts/generate-tfm-prequel-figures.mjs from TabICL v1 sizes; the other sizes are in the subtitle

With 10,000 labelled rows fixed, stage 3 stays flat as columns grow (dashed line). Stages 1 and 2 both grow with the columns and pass stage 3 at about 250. Stage 2’s columns² attention only dominates its own cost past about 500 columns, so on very wide tables stage 2 pulls ahead of stage 1.

What doubling each input does to the attention-score cost (approximately; the projections and FFNs grow only linearly, so stage 2’s total is still about ×3 when doubling from 500 columns, and nears ×4 only on far wider tables):

Double the…Stage 1Stage 2Stage 3Overall
labelled rows (context)×2 when context rows dominate×2 when context rows dominate×4 when context rows dominate×4 once context is large
test rows×1 to ×2×1 to ×2×1 to ×2: only the test × context part doublesat most ×2, close to ×1 when context rows dominate
columns×2×4 for wide tables×1×2 to ×4, stage 3 unaffected
vector size e×2×2×2×2 in attention, ×4 in the projections and FFNs

In practice, labelled rows never look at test rows, so their vectors can be computed once and reused (a KV cache; kv_cache=True in TabICL). Each new test row then pays stages 1 and 2 for its own cells and, in stage 3, about n_ctx × 4e per layer for attention plus its own projections and FFN, and test rows can be scored in parallel. Ensembling goes the other way: with n_estimators, the same frozen model runs on several views of the data and the predictions are averaged, so the default 8 estimators cost about 8 times one pass.

Training

Everything learned (all three stages, the CLS tokens, the label vectors and the head) was fixed during pretraining, before your data ever arrives.

The data is synthetic: tables nobody collected. Most come from a structural causal model, a random graph of causes and effects where some variables drive others and noise is added. The generator is highly parametrised, so it simulates simple and complex causal relationships alike. Sampling from one graph gives a small table, with one column as the target. In about 30% of TabICL’s tables, the cause-to-effect functions are tree models (XGBoost) instead of small neural networks, and TabPFN’s prior adds uninformative features, so the model learns to ignore them. The scale is large: about 130 million tables for TabPFN v2, about 83 million for TabICL, about 35 million for TabICLv2.

Random cause-and-effect graphs each generate a small table. The model sees each table with some targets hidden and outputs, for each, a probability for every range of y values. The loss is how little probability landed on the true range; the weights are updated and this repeats over millions of synthetic datasets.

Shown for TabPFN-style regression, where the outputs are bins of y; for classification the bins are the classes.

The task is exactly the prediction setup: in each synthetic table, some rows are labelled and the rest are held out, and the model must predict the held-out labels. So each training example is a whole dataset, not a row. For classification the loss is cross-entropy: how little probability the model put on the true class. Tables usually grow during training (a curriculum): TabICLv2 trains on tables of 1,024 rows, then 400 to 10,240, then up to 60,000. What the model learns is a general skill, “given labelled rows, predict new rows”, rather than facts about any one dataset.

One caveat from training only on synthetic data: the model has never seen a real retail table. Promotions, sudden shocks and changes of regime are covered only as far as the generator produced something like them.

An LLM goes through pretraining, instruction fine-tuning, preference tuning (RLHF or DPO) and often RL for reasoning. A TFM is essentially one stage: pretraining on synthetic tables. Optional extras are continued training on real tables or fine-tuning on one client’s data. There is no preference tuning: the label already scores the prediction. The prior (the synthetic-data generator) does the job that data curation and alignment do for an LLM.

Why it works: prior-data fitted networks

TFMs are based on PFNs. A PFN approximates Bayesian model averaging. An example: a new product sold 0, 0, 5, 0, 0 and 6 units in its first six weeks. Is it bike-like (lumpy demand, most weeks zero) or sock-like (steady sales)? Say bike-like predicts 1 unit next week (most likely a zero week, with some chance of a spike) and sock-like 2 (its steady average).

You do not have to pick one. Score how well each explanation fits what you saw, turn the scores into weights, and average the two forecasts (both start equally likely):

Two explanations for a new product that sold 0, 0, 5, 0, 0, 6 units: bike-like lumpy demand fits the data with 0.50, sock-like steady demand with 0.20. After normalising, their weights are 0.71 and 0.29. Their forecasts of 1 and 2 units average to about 1.3 units.

That is Bayesian model averaging. In symbols, with D the context (the labelled rows; not d, the column count), x the new row and φ one candidate task (one data-generating rule, such as one causal graph with its parameters): p(y | x, D) = ∫ p(y | x, φ) p(φ | D) dφ. For each candidate task, take its prediction at the new row, then weight it by how credible that task became after seeing the context.

The equation is exact under two assumptions: once you know the true task, the context tells you nothing more about the new answer; and the new row’s features, on their own, do not change which task is likely.

Doing this by hand means listing every possible task, which is impossible for real tables. A PFN learns to do it: trained on millions of tables from the synthetic generator, one forward pass approximates the average over every task that generator could produce. The generator is the “prior” in the name.

Comparison with the traditional transformer architecture

With “Attention Is All You Need”

Both use the same block: multi-head attention, then a feed-forward network (FFN), each with a residual connection and normalisation. What differs is how the blocks are wired and what a token is.

The original encoder-decoder transformer next to the TabICL-style TFM: the same attention, add and norm, and feed-forward blocks, wired as three encoder stacks along different axes.

Original transformer vs TabICL-style TFM. In stage 3, context rows also attend to each other, not only test rows to context rows. Drawn with the 2017 paper’s Add & Norm after each step. Stage 1 outputs W ⊙ x + B, not just x·w + b. In TabICL v1, N₁ = 3 inducing-point blocks (each two attentions: points read cells, then cells read points), N₂ = 3 layers and N₃ = 12 layers.

The wiring differs: no decoder and no output fed back in; three encoder stacks run once, each along a different axis. The cross-attention that remains is inside those stacks (see the table), not from a decoder to an encoder. One detail differs too: the 2017 model normalises after each step, TabICL before it.

PartAttention Is All You Need (2017)TabICL-style TFM
Tasktranslate a sentencepredict the labels of test rows
Familyencoder + decoderencoder-style: no causal mask, one pass
One tokena word piecea cell (stages 1 and 2), then a row (stage 3)
Embeddinga lookup table: one learned vector per wordx·w + b: a number times a learned vector
Position signalon every token (word order matters)none across rows (row order doesn’t matter); rotary positions across columns in stage 2
Blockmulti-head attention + FFN, add & normthe same block
Stacksencoder, then decoderthree stacks, one per axis
Masksdecoder: causal (a word sees only earlier words)stage 1: inducing points read labelled cells only; stage 3: every row reads labelled rows only, never a test row
Cross-attentiondecoder attends to the encoder’s outputstage 3: test rows (as queries) read labelled rows (as keys and values); also stage 1: inducing points read context cells
Outputone word per pass, repeated until the sentence endsall class probabilities in one pass

Since rows carry no position, the model has no idea which row is older: for time series, time has to be given as features (week of year, month, and so on).

With a modern LLM

Both are transformers that learn in context. They differ in what they were trained on, what a token is, and how they produce output. In practice an LLM is often the interface and orchestrator, and the TFM the prediction engine it calls.

An LLM loops, producing one token per pass; the TFM produces all predictions in one pass.

LLM vs TFM: loop vs one pass

LLMTabICL-style TFM
Pretraining datainternet text and codemillions of synthetic tables from random causal models and tree-based generators
Pretraining taskpredict the next tokenpredict held-out labels from labelled rows
What it learnslanguage and world knowledgehow to predict from examples
Inputa sequence; order mattersa table; row order doesn’t matter
Numberssplit into text tokensfed in as numbers (x·w + b)
Wiringdecoder-only, causal maskthree attention stages; in stage 3 every row reads only labelled rows
Outputone token per pass, repeatedall predictions in one pass
Forecasting a horizonstep by step, each output fed back ineach future date is a test row; the whole horizon in one pass
In-context exampleswritten into the promptthe labelled rows
Typical sizebillions of parameterstypically a few million to tens of millions (TabPFN v2 ≈ 7M, TabICL ≈ 27 million; newest up to ≈ 215M)

TabPFN-TS (TabPFN applied to time series, with calendar features as columns) forecasts this way, with no lags. A TFM forecast becomes recursive only with lag features shorter than the horizon. Time-series foundation models differ here: the original Chronos and Lag-Llama are autoregressive by design, while Chronos-Bolt and TimesFM predict a chunk of steps per pass and loop beyond it.

Other tabular foundation models: TabPFN and Kumo

This post follows the TabICL design; other tabular foundation models organise the same ideas differently.

TabPFN v2 and TabPFN-3

TabPFN v2 is a 12-layer transformer that never squeezes a row into one vector. Every cell is a token (in practice a pair of cells; see the token figure), and each of the 12 layers looks at the table in two directions. Down a column, a value is compared with the other values of the same feature: this is how the model knows whether 3.2 is high or low (like Dan’s age of 45 against 28 to 62). Across a row, the values of one example are combined.

Column attention compares a value with the other values in its column. Row attention compares the values within one row.

The catch is cost: every layer pays for rows² on every column. TabICL’s expensive in-context stage sees one vector per row instead of one per cell, which is what lets it take far more context rows. TabPFN-3 moved to the same column-then-row design: inducing-point column embedding, row compression with 4 CLS tokens, then 24 in-context layers; its report says the architecture is adapted from TabICLv2.

Kumo

Kumo, the start-up whose purchase by NVIDIA Fortune reported in June 2026, has two different models. Kumo Tabular (open weights, Sep 2026) works on one table and combines the two designs: TabPFN v2’s alternating column and row attention, then this post’s row compression and in-context stage. KumoRFM-2 (Apr 2026) works on a whole relational database and adds a stage that passes information between tables instead of flattening them.

Three rows of stages. TabICL v1: cells as x·w + b, column attention via inducing points, row attention with 4 CLS then labels, test rows read context rows. Kumo Tabular: Fourier features with label embedding, alternating column and row attention then 4 CLS, cached in-context stage with Test-GQA, output head for classes or 999 quantiles. KumoRFM-2: temporal subgraph built by PQL, per-table column and row attention with early labels, foreign-key attention along PK-FK links, query and context attending to each other.

From NVIDIA’s Kumo Tabular blog and the KumoRFM / KumoRFM-2 papers, as of Nov 2026

TabICL v1Kumo TabularKumoRFM-2
Inputone tableone tablea database as a temporal graph: records = nodes, primary key to foreign key (PK-FK) links = edges
Cell encodingx·w + b, then W ⊙ x + BFourier features (learned sines and cosines); missing values handled nativelylightweight per-table encoder (few details published)
Who builds the labelsyou supply X and yyou supply X and yPQL (Kumo’s predictive query language) computes them at past timestamps, avoiding leakage
OutputMLP head, 10 classes per pass (more via a hierarchy)≤ 10 classes per pass (more via error-correcting codes); regression = 999 quantilesclassification, regression, multi-class, recommendation
Pretrainingsynthetic (causal + tree priors)synthetic only; 35M / 71M / 137M tables by size; curriculum: 1,024 rows, then 400 to 10,240, then up to 60,000synthetic + public real relational databases; single tables first, then relational
Size≈ 27 million parameters28M to 215M (3 sizes)not stated
Licenseopen (BSD)OpenMDW-1.1, commercial use allowedcommercial product

What Kumo changes:

Limits as published: Kumo Tabular was trained on up to 60,000 rows and 100 columns of context, and takes numerical and categorical columns natively; text, images and timestamps go through built-in preprocessing recipes. All benchmark results are self-reported: Kumo Tabular claims #1 on TabArena (Elo 1950); KumoRFM-2 claims up to 8% over baselines on relational benchmarks.

Much of Kumo Tabular’s recipe is also in the TabICLv2 paper: the same kind of synthetic prior, the same three-step curriculum, 999 regression quantiles, and a similar attention scaling with context length (QASSMax there, one learned coefficient per head in Kumo). What TabICLv2 does not use: Fourier features, Test-GQA, and alternating column and row attention. NVIDIA’s blog does not say which of these explains Kumo Tabular’s first place.

How inputs are handled, per model

Each model cleans data differently, and much of it happens in a preprocessing layer outside the network. The TabICL column describes the current tabicl package.

InputTabICLTabPFN (3.5, the package default)Kumo TabularWhat you should do
Missing numbersmean imputation, no indicatormean imputation plus a NaN/Inf indicator inside the model (TabPFN-3 report)handled natively, no imputationleave as NaN
Missing categoriescode −1, the same as an unseen categoryhandled automaticallyas aboveleave as NaN
Categoriesordinal encodingordinal encoding (shuffled per view); any cardinalityseparate Fourier-feature weightspass as category; group if many values
Unseen categorycode −1not documentednot documentedmap to unknown, monitor the rate
IDs, free texttreated as categories (exact matches only); README recommends skrubbuilt-in text transformer; keep IDs as categoricalbuilt-in preprocessing recipesembed or extract features
Datesnot native: use skrub’s DatetimeEncoderexpanded into calendar features by the package; on by default with TabPFN-3.5 (TabPFN-3 and older refuse raw datetimes)built-in preprocessing recipesderive calendar features
Outliersvalues beyond 4 std left out of the statistics, then soft log clippingvalues beyond 12 std clipped (classification; none for regression)not documented (the generator clips outliers)usually nothing
Ensemble views8 by default (was 32): column shuffles, class shifts, normalisations8 by default: feature and class shuffles; at most 768 features per viewnot detailedkeep the default

On the churn table, that means:

TabICL’s tutorial, “Handling strings and dates with skrub”, puts these pieces together:

TableVectorizer(
    low_cardinality="passthrough",
    cardinality_threshold=10,
    high_cardinality=StringEncoder(n_components=10),
    datetime=DatetimeEncoder(add_weekday=True, add_day_of_year=True, periodic_encoding="circular"),
)

or simply skrub.tabular_pipeline(TabICLClassifier()), which builds a similar TableVectorizer (30 string components and spline date features, without the weekday and day-of-year extras) followed by the estimator. On the tutorial’s small example (600 rows, 2-fold, n_estimators=1, so noisy: the gap is about one standard deviation), the explicit TableVectorizer above raises ROC AUC by about 13% relative to the raw table.

Sources


My contributions on this path:

Newsletter subscriptions are not currently available.