leaderboard / index.html
alana89's picture
TAR-L: the large encoders fine-tuned (multabench 0.1.2, de67998)
d8c439a verified
Raw History Blame Contribute Delete
55.4 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MulTaBench Leaderboard</title>
<link rel="icon" type="image/svg+xml" href="assets/mark.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo:wght@500;700;800&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
<style>
:root {
--bg: #F3F5F2; --surface: #FFFFFF; --ink: #1C2228; --muted: #5C666F; --line: #D9DED7; --soft: #EBEEE9;
--accent: #2C5C8A; --text-mod: #A4581B; --image-mod: #2D7653; --best: #2C5C8A; --warn: #A33A2A;
--frozen: #7E8A94; --tar: #2C5C8A; --large: #2F7F8C; --large-tar: #B0466E; --e2e: #8A5FA8;
--display: "Archivo", "Helvetica Neue", Arial, sans-serif;
--body: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
--mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #111519; --surface: #181E24; --ink: #E4E9E3; --muted: #97A1A9; --line: #2B333A; --soft: #20272E;
--accent: #84B3E0; --text-mod: #E39B5C; --image-mod: #6FC79D; --best: #5E97CF; --warn: #E07A68;
--frozen: #8F9BA5; --tar: #84B3E0; --large: #74C2CE; --large-tar: #E68AAE; --e2e: #C29BDF;
color-scheme: dark;
}
}
:root[data-theme="dark"] {
--bg: #111519; --surface: #181E24; --ink: #E4E9E3; --muted: #97A1A9; --line: #2B333A; --soft: #20272E;
--accent: #84B3E0; --text-mod: #E39B5C; --image-mod: #6FC79D; --best: #5E97CF; --warn: #E07A68;
--frozen: #8F9BA5; --tar: #84B3E0; --large: #74C2CE; --large-tar: #E68AAE; --e2e: #C29BDF;
color-scheme: dark;
}
* { box-sizing: border-box; }
body { background: var(--bg); color: var(--ink); font: 15px/1.55 var(--body); padding-inline: 16px; padding-block: 28px 64px; }
.wrap { max-width: 1120px; margin: 0 auto; display: grid; gap: 40px; }
.wrap > *, section > * { min-width: 0; }
h2 { font-family: var(--display); text-wrap: balance; margin: 0; letter-spacing: -0.01em; }
h2 { font-size: 21px; font-weight: 700; }
p { margin: 0; max-width: 68ch; }
.muted { color: var(--muted); }
.mono, td.num, th.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
header { display: grid; gap: 14px; }
.logo { display: block; width: min(640px, 100%); }
.logo img { display: block; width: 100%; height: auto; }
.logo .dark { display: none; }
:root[data-theme="dark"] .logo .light { display: none; }
:root[data-theme="dark"] .logo .dark { display: block; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .logo .light { display: none; }
:root:not([data-theme="light"]) .logo .dark { display: block; }
}
section { display: grid; gap: 16px; }
.head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; }
.tabs { display: flex; flex-wrap: wrap; gap: 6px; }
.tabs button { font: 500 13px/1 var(--body); color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 8px 13px; cursor: pointer; }
.tabs button[aria-pressed="true"] { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.tabs button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; }
.scroll { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: 14px; }
th { font: 500 11.5px/1.2 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); text-align: left; padding: 12px 12px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
td { padding: 9px 12px; border-bottom: 1px solid var(--soft); white-space: nowrap; }
tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; }
.rank { color: var(--muted); font-family: var(--mono); width: 2.5em; }
.model { font-weight: 600; white-space: nowrap; }
.model a { color: inherit; text-decoration: none; }
.model a:hover { text-decoration: underline; }
.mlogo { width: 18px; height: 18px; margin-right: 8px; vertical-align: -4px; border-radius: 4px; object-fit: contain; background: #fff; }
.dev { font: 500 11px/1 var(--mono); border-radius: 4px; padding: 3px 6px; background: var(--soft); color: var(--muted); }
.ci { color: var(--muted); font-size: 12.5px; }
.chart svg { display: block; width: 100%; height: auto; }
.chart text { font-family: var(--mono); font-size: 11px; fill: var(--muted); }
.chart .lbl { font-family: var(--body); font-size: 13px; fill: var(--ink); font-weight: 500; }
.chart.wide svg { min-width: 760px; }
.narrow { max-width: 640px; }
#boardTable th, #boardTable td { padding-inline: 9px; }
.controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
select { font: 13px var(--body); color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 7px 10px; }
.bar { display: inline-block; height: 8px; border-radius: 2px; background: var(--accent); vertical-align: middle; margin-right: 8px; opacity: .75; }
.flag { color: var(--warn); }
.chip { display: inline-block; font: 600 11.5px/1 var(--mono); letter-spacing: 0.03em; padding: 4px 8px; border-radius: 999px;
color: var(--c); background: color-mix(in oklab, var(--c) 14%, var(--surface)); }
.frozen { --c: var(--frozen); } .tar { --c: var(--tar); } .large { --c: var(--large); } .large-tar { --c: var(--large-tar); } .end-to-end { --c: var(--e2e); }
b.enc { color: var(--c); }
.selectors { border-bottom: 1px solid var(--line); }
.pagetabs { display: flex; gap: 4px; overflow-x: auto; border-bottom: 1px solid var(--line); scrollbar-width: none; }
.pagetabs a { font: 500 14px/1 var(--body); color: var(--muted); text-decoration: none; padding: 14px 14px 12px; white-space: nowrap;
border-bottom: 2px solid transparent; margin-bottom: -1px; }
.pagetabs a:hover { color: var(--ink); }
@media (max-width: 700px) {
.filters { gap: 8px 16px; }
.filters .hint { display: none; }
.tabs button { font-size: 12px; padding: 6px 10px; }
.encnote { font-size: 14px; line-height: 1.5; }
}
.pagetabs a[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.pagetabs a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tabpanel { display: grid; gap: 40px; }
.tabpanel[hidden] { display: none; }
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 22px; padding: 12px 0; }
.filters .hint { flex-basis: 100%; max-width: none; font-size: 13px; color: var(--muted); }
.filters[hidden], .filters .group[hidden] { display: none; }
.filters .group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.encrow { flex-basis: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.encnote { max-width: 84ch; font-size: 16px; line-height: 1.6; margin: 1em 0; }
.encnote ul { margin: 0.6em 0 0; padding-left: 1.2em; }
#intro { font-size: 17px; line-height: 1.6; max-width: 84ch; }
.filters .label { font: 500 11.5px/1 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); margin-right: 4px; }
.tabs button .n { font-family: var(--mono); opacity: .6; margin-left: 4px; }
.links { display: flex; flex-wrap: wrap; gap: 6px 24px; }
.links a, .encnote a { color: var(--accent); white-space: nowrap; text-decoration: none; font-weight: 500; }
.links a:hover, .encnote a:hover { text-decoration: underline; }
.links .ico { display: inline-block; width: 1.05em; height: 1.05em; margin-right: 5px; vertical-align: -0.15em; line-height: 1; }
code { font-family: var(--mono); font-size: 0.92em; overflow-wrap: anywhere; }
.topbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 20px; }
.topbar .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.btn { font: 500 13px/1 var(--body); color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 8px 14px; cursor: pointer; text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.version { margin: 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; font: 12px/1 var(--mono); color: var(--muted); }
.version .tag { background: var(--accent); color: var(--surface); border-radius: 999px; padding: 4px 9px; font-weight: 500; }
.version a { color: var(--accent); }
.star { color: var(--warn); }
#boardNote { line-height: 1.8; }
#boardNote .label { display: inline-block; min-width: 92px; font: 500 11.5px/1 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; }
.pf { text-align: center; width: 1%; white-space: nowrap; }
tr.front td { background: color-mix(in oklab, var(--accent) 7%, var(--surface)); }
#boardTable tr.groupEnd td, #boardTable td.grouped { border-bottom-color: var(--line); }
#boardTable td.grouped { vertical-align: top; background: var(--surface); }
.pareto text.name { font-family: var(--body); font-size: 12px; fill: var(--ink); font-weight: 500; }
.dl { display: grid; }
.dl > div { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--soft); }
.dl > div:last-child { border-bottom: 0; }
.dl .label { font: 500 11.5px/1 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); min-width: 190px; }
.bibs { max-height: 420px; overflow-y: auto; }
pre.bib { margin: 0; padding: 16px 18px; font: 12.5px/1.5 var(--mono); white-space: pre-wrap; word-break: break-word; }
.cta { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px 32px; align-items: center; padding: 26px 28px; background: var(--ink); color: var(--bg); border-radius: 12px; }
.cta h2 { color: var(--bg); font-size: 24px; }
.cta .btn { background: var(--bg); color: var(--ink); border-color: var(--bg); }
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="topbar">
<a class="logo" href="https://github.com/alanarazi7/MulTaBench"><img class="light" src="assets/logo.svg" alt="MulTaBench: Benchmarking Multimodal Tabular Learning"><img class="dark" src="assets/logo-dark.svg" alt="MulTaBench: Benchmarking Multimodal Tabular Learning"></a>
<div class="version" id="version"></div>
<div class="actions">
<div class="tabs" id="themeTabs" role="group" aria-label="Theme"></div>
</div>
</div>
<p class="muted" id="intro">MulTaBench is a benchmark for multimodal tabular learning with text and images. Its purpose is to drive smarter encoding methods, towards our vision of truly Multimodal Tabular Foundation Models. Its 80 datasets balance image and text modalities, classification and regression, and how hard the text or images are to encode, with the Core slice focusing on the 40 datasets where adapting the encoders to the task pays off.</p>
<p class="links"><a href="https://github.com/alanarazi7/MulTaBench"><svg class="ico" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>Code</a><a href="https://huggingface.co/multabench"><img class="ico" src="assets/huggingface.svg" alt="">Datasets</a><a href="https://arxiv.org/abs/2605.10616"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path fill="#B31B1B" d="M3.8423 0a1.0037 1.0037 0 0 0-.922.6078c-.1536.3687-.0438.6275.2938 1.1113l6.9185 8.3597-1.0223 1.1058a1.0393 1.0393 0 0 0 .003 1.4229l1.2292 1.3135-5.4391 6.4444c-.2803.299-.4538.823-.2971 1.1986a1.0253 1.0253 0 0 0 .9585.635.9133.9133 0 0 0 .6891-.3405l5.783-6.126 7.4902 8.0051a.8527.8527 0 0 0 .6835.2597.9575.9575 0 0 0 .8777-.6138c.1577-.377-.017-.7502-.306-1.1407l-7.0518-8.3418 1.0632-1.13a.9626.9626 0 0 0 .0089-1.3165L4.6336.4639s-.3733-.4535-.768-.463zm0 .272h.0166c.2179.0052.4874.2715.5644.3639l.005.006.0052.0055 10.169 10.9905a.6915.6915 0 0 1-.0072.945l-1.0666 1.133-1.4982-1.7724-8.5994-10.39c-.3286-.472-.352-.6183-.2592-.841a.7307.7307 0 0 1 .6704-.4401Zm14.341 1.5701a.877.877 0 0 0-.6554.2418l-5.6962 6.1584 1.6944 1.8319 5.3089-6.5138c.3251-.4335.479-.6603.3247-1.0292a1.1205 1.1205 0 0 0-.9763-.689zm-7.6557 12.2823 1.3186 1.4135-5.7864 6.1295a.6494.6494 0 0 1-.4959.26.7516.7516 0 0 1-.706-.4669c-.1119-.2682.0359-.6864.2442-.9083l.0051-.0055.0047-.0055z"/></svg>Paper</a></p>
</header>
<div class="selectors">
<nav class="pagetabs" id="pageTabs" role="tablist">
<a role="tab" href="#board" data-tab="board">Leaderboard</a>
<a role="tab" href="#encodings" data-tab="encodings">Encoders</a>
<a role="tab" href="#cost" data-tab="cost">Pareto</a>
<a role="tab" href="#paper" data-tab="paper">Cite & Data</a>
</nav>
<div class="filters">
<p class="hint">Press the buttons to focus on a slice of the benchmark.</p>
<div class="group tabs" id="tierTabs" role="group" aria-label="Datasets"><span class="label">Datasets</span></div>
<div class="group tabs" id="taskTabs" role="group" aria-label="Task"><span class="label">Task</span></div>
<div class="group tabs" id="modTabs" role="group" aria-label="Modality"><span class="label">Modality</span></div>
<div class="encrow">
<div class="group tabs" id="encodingTabs" role="group" aria-label="Encoders"><span class="label">Encoders</span></div>
<div class="encnote">For models that are not E2E, we embed texts with e5 (<a href="https://huggingface.co/intfloat/e5-small-v2">small</a>, <a href="https://huggingface.co/intfloat/e5-large-v2">large</a>) and images with DINOv3 (<a href="https://huggingface.co/facebook/dinov3-vits16-pretrain-lvd1689m">small</a>, <a href="https://huggingface.co/facebook/dinov3-vitl16-pretrain-lvd1689m">large</a>), and then project each column to 30 dimensions with PCA. From there on, they are treated as any other numeric features.
<ul>
<li><b class="enc frozen">Frozen-S</b> uses the small encoders as they are.</li>
<li><b class="enc tar">TAR-S</b> (Target-Aware Representations) first fine-tunes the small encoders on the training labels with default hyperparameters.</li>
<li><b class="enc large">Frozen-L</b> uses the large encoders as they are.</li>
<li><b class="enc large-tar">TAR-L</b> fine-tunes the large encoders as TAR-S does the small ones.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="tabpanel" id="tab-board" role="tabpanel">
<section>
<div class="head">
<h2>Elo rating</h2>
<div class="group tabs" id="groupTabs" role="group" aria-label="Rows"></div>
</div>
<p class="muted" id="boardNote"></p>
<div class="panel scroll chart wide" id="eloChart"></div>
<div class="panel scroll"><table id="boardTable"></table></div>
</section>
<section class="cta">
<h2>Have a model or an encoding method that should be on the board?</h2>
<a class="btn" href="https://github.com/alanarazi7/MulTaBench/issues/new">Open an issue</a>
</section>
</div>
<div class="tabpanel" id="tab-encodings" role="tabpanel">
<section>
<div class="panel scroll narrow"><table id="encodingTable"></table></div>
</section>
</div>
<div class="tabpanel" id="tab-cost" role="tabpanel">
<section>
<div class="head">
<h2>Pareto Frontier</h2>
<div class="group tabs" id="encTabs" role="group" aria-label="Encoding time"></div>
</div>
<p class="muted">Each entry's Elo, under the filters above, against its median train + predict time per 1,000 rows (log scale), TAR's fine-tuning included; <b>With encoding time</b> adds an estimate of the encoders' time on those rows. The times come from runs and caches on different machines, so the comparison is an approximation, but where encoding counts it dominates the cost. <span class="star">★</span> marks the Pareto front: no other model is both faster and stronger.</p>
<div class="panel chart pareto" id="paretoChart"></div>
</section>
<section>
<h2>Cost</h2>
<p class="muted">Median time per 1,000 rows, as a ballpark: models run on different hardware, so their times are not directly comparable, and some encoders were timed on different GPUs, which we plan to fix. Most of the cost is encoding the text and images, which could be optimized.</p>
<div class="panel scroll"><table id="costTable"></table></div>
</section>
</div>
<div class="tabpanel" id="tab-paper" role="tabpanel">
<section>
<h2>All results</h2>
<p class="muted">Every run on the board, one row per model, encoding, dataset and fold, with its test score and error and its train and predict time, compiled into one CSV:</p>
<div class="panel"><pre class="bib">pip install multabench
python -m multabench.leaderboard.results multabench_results.csv</pre></div>
</section>
<section id="cite">
<div class="head"><h2>Cite MulTaBench</h2><button class="btn" id="copyBib" type="button">Copy BibTeX</button></div>
<div class="panel"><pre class="bib" id="bib">@misc{arazi2026multabenchbenchmarkingmultimodaltabular,
title={MulTaBench: Benchmarking Multimodal Tabular Learning with Text and Image},
author={Alan Arazi and Eilam Shapira and Shoham Grunblat and Mor Ventura and Elad Hoffer and Gioia Blayer and David Holzmüller and Lennart Purucker and Gaël Varoquaux and Frank Hutter and Roi Reichart},
year={2026},
eprint={2605.10616},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2605.10616},
}</pre></div>
</section>
<section id="cite-models">
<div class="head"><h2>Cite the models and encoders</h2><button class="btn" id="copyModelsBib" type="button">Copy BibTeX</button></div>
<p class="muted">Every model on the board, then the text and image encoders, e5 and DINOv3.</p>
<div class="panel scroll bibs"><pre class="bib" id="modelsBib">@misc{prokhorenkova2019catboostunbiasedboostingcategorical,
title={CatBoost: unbiased boosting with categorical features},
author={Liudmila Prokhorenkova and Gleb Gusev and Aleksandr Vorobev and Anna Veronika Dorogush and Andrey Gulin},
year={2019},
eprint={1706.09516},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/1706.09516},
}
@inproceedings{ke2017lightgbm,
author={Ke, Guolin and Meng, Qi and Finley, Thomas and Wang, Taifeng and Chen, Wei and Ma, Weidong and Ye, Qiwei and Liu, Tie-Yan},
booktitle={Advances in Neural Information Processing Systems},
publisher={Curran Associates, Inc.},
title={LightGBM: A Highly Efficient Gradient Boosting Decision Tree},
url={https://proceedings.neurips.cc/paper_files/paper/2017/file/6449f44a102fde848669bdd9eb6b76fa-Paper.pdf},
volume={30},
year={2017},
}
@misc{chen2016xgboostscalabletreeboosting,
title={XGBoost: A Scalable Tree Boosting System},
author={Tianqi Chen and Carlos Guestrin},
year={2016},
eprint={1603.02754},
archivePrefix={arXiv},
primaryClass={cs.LG},
doi={https://doi.org/10.1145/2939672.2939785},
url={https://arxiv.org/abs/1603.02754},
}
@article{breiman2001randomforests,
title={Random Forests},
author={Breiman, Leo},
journal={Machine Learning},
volume={45},
number={1},
pages={5--32},
year={2001},
publisher={Springer},
doi={10.1023/A:1010933404324},
}
@misc{holzmuller2025betterdefaultstrongpretuned,
title={Better by Default: Strong Pre-Tuned MLPs and Boosted Trees on Tabular Data},
author={David Holzmüller and Léo Grinsztajn and Ingo Steinwart},
year={2025},
eprint={2407.04491},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2407.04491},
}
@misc{gorishniy2025tabmadvancingtabulardeep,
title={TabM: Advancing Tabular Deep Learning with Parameter-Efficient Ensembling},
author={Yury Gorishniy and Akim Kotelnikov and Artem Babenko},
year={2025},
eprint={2410.24210},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2410.24210},
}
@article{hollmann2025accurate,
title={Accurate predictions on small data with a tabular foundation model},
author={Hollmann, Noah and M{\"u}ller, Samuel and Purucker, Lennart and Krishnakumar, Arjun and K{\"o}rfer, Max and Hoo, Shi Bin and Schirrmeister, Robin Tibor and Hutter, Frank},
journal={Nature},
volume={637},
number={8045},
pages={319--326},
year={2025},
doi={10.1038/s41586-024-08328-6},
}
@misc{grinsztajn2026tabpfn25advancingstateart,
title={TabPFN-2.5: Advancing the State of the Art in Tabular Foundation Models},
author={Léo Grinsztajn and Klemens Flöge and Oscar Key and Felix Birkel and Philipp Jund and Brendan Roof and Benjamin Jäger and Dominik Safaric and Simone Alessi and Adrian Hayler and Mihir Manium and Rosen Yu and Felix Jablonski and Shi Bin Hoo and Anurag Garg and Jake Robertson and Magnus Bühler and Vladyslav Moroshan and Lennart Purucker and Clara Cornu and Lilly Charlotte Wehrhahn and Alessandro Bonetto and Bernhard Schölkopf and Sauraj Gambhir and Noah Hollmann and Frank Hutter},
year={2026},
eprint={2511.08667},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2511.08667},
}
@misc{grinsztajn2026tabpfn3technicalreport,
title={TabPFN-3: Technical Report},
author={Léo Grinsztajn and Klemens Flöge and Oscar Key and Felix Birkel and Philipp Jund and Brendan Roof and Mihir Manium and Shi Bin Hoo and Magnus Bühler and Anurag Garg and Dominik Safaric and Jake Robertson and Benjamin Jäger and Simone Alessi and Adrian Hayler and Vladyslav Moroshan and Lennart Purucker and Philipp Singer and Alan Arazi and Julien Siems and Jan Hendrik Metzen and Georg Grab and Nick Erickson and Siyuan Guo and Eliott Kalfon and Simon Bing and David Salinas and Clara Cornu and Lilly Charlotte Wehrhahn and Diana Kriuchkova and Kursat Kaya and Lydia Sidhoum and Marie Salmon and Jerry Chen and Madelon Hulsebos and Yann LeCun and Samuel Müller and Bernhard Schölkopf and Sauraj Gambhir and Noah Hollmann and Frank Hutter},
year={2026},
eprint={2605.13986},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2605.13986},
}
@misc{jager2026tabpfn35technicalreport,
title={TabPFN-3.5: Technical Report},
author={Benjamin Jäger and Nick Erickson and Léo Grinsztajn and Felix Birkel and Klemens Flöge and Oscar Key and Kürşat Kaya and Jonas Kübler and Adèle Frankel and Tobias Schröder and Anurag Garg and Jan Hendrik Metzen and David Salinas and Simon Bing and Kristina Collins and Tuana Çelik and Vahid Balazadeh and Lydia Sidhoum and Tomás Pereda and Brendan Roof and Andrej Tschalzev and Siyuan Guo and Philipp Singer and Lennart Purucker and Jake Robertson and Marie Salmon and Philipp Jund and Jerry Chen and Diana Kriuchkova and Arthur Cahu and Eliott Kalfon and Adrian Hayler and Georg Grab and Vitor Monteiro and Lilly Wehrhahn and Dominik Safaric and Clara Cornu and Alan Arazi and Rylee Grace and Simone Alessi and Mihir Manium and Bernhard Schölkopf and Yann LeCun and Madelon Hulsebos and Sauraj Gambhir and Noah Hollmann and Frank Hutter},
year={2026},
eprint={2609.17895},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2609.17895},
}
@misc{ma2026tabdptscalingtabularfoundation,
title={TabDPT: Scaling Tabular Foundation Models on Real Data},
author={Junwei Ma and Valentin Thomas and Rasa Hosseinzadeh and Alex Labach and Hamidreza Kamkari and Jesse C. Cresswell and Keyvan Golestan and Guangwei Yu and Anthony L. Caterini and Maksims Volkovs},
year={2026},
eprint={2410.18164},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2410.18164},
}
@misc{qu2026tabiclv2betterfasterscalable,
title={TabICLv2: A better, faster, scalable, and open tabular foundation model},
author={Jingang Qu and David Holzmüller and Gaël Varoquaux and Marine Le Morvan},
year={2026},
eprint={2602.11139},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2602.11139},
}
@misc{spinaci2025contexttabsemanticsawaretabularincontext,
title={ConTextTab: A Semantics-Aware Tabular In-Context Learner},
author={Marco Spinaci and Marek Polewczyk and Maximilian Schambach and Sam Thelin},
year={2025},
eprint={2506.10707},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2506.10707},
}
@misc{arazi2025tabstartabularfoundationmodel,
title={TabSTAR: A Tabular Foundation Model for Tabular Data with Text Fields},
author={Alan Arazi and Eilam Shapira and Roi Reichart},
year={2025},
eprint={2505.18125},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2505.18125},
}
@misc{tao2026mitrav2technicalreport,
title={Mitra-v2 Technical Report},
author={Yefan Tao and Xiyuan Zhang and Xinyi Liu and Boran Han and Danielle Maddix and Haoyang Fang and Zhen Han and Jiading Gai and Xuanqing Liu and Michael Bohlke-Schneider and Yuyang and Wang and Gerald Friedland and Kevan Mah and Chris Lee and Chris Kong},
year={2026},
eprint={2609.04540},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2609.04540},
}
@misc{zhang2026limix2contextualmechanismnetwork,
title={LimiX-2: A Contextual Mechanism Network Towards General Structured-Data Intelligence},
author={Xingxuan Zhang and Gang Ren and Hao Yuan and Hao Zou and Hongze Tan and Hui Wang and Jianhao Song and Jiansheng Li and Jiayao Zhang and Jinghan Zhang and Kaifang Li and Lang Mo and Li Mao and Mingchao Hao and Nuo Xu and Rui Ding and Ruiji Zhang and Shuyang Li and Siyu Mei and Tianyang Zhang and Weiyang Mu and Yancheng Dong and Yongxian Wei and Yuan Xue and Yuanrui Wang and Yue He and Zijia Yang and Ziyun Li and Dongzhe Li and Fuqiang Wang and Jiandong Liu and Jiawei Chen and Jiaxin Du and Kaijie Cheng and Kehan Li and Lei Sun and Linjun Zhou and Ningbo Dai and Qi Wang and Renzhe Xu and Shaoxing Du and Shumeng Yang and Wang Lu and Wenjing Chu and Xiannan Huang and Xiaoyu Lin and Xing Ai and Xinyan Han and Xuanyue Li and Xuanyue Su and Xukun Zhang and Yan Lu and Yaxin Zhang and Yi Qin and Yifei Huang and Yihan Xu and Yongle Lv and Yuanyuan Jiang and Yushan Han and Peng Cui},
year={2026},
eprint={2609.17488},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2609.17488},
}
@misc{nvidia2026kumotabular,
title={NVIDIA Kumo Tabular Sets a New Accuracy-Efficiency Frontier for Tabular Prediction},
author={{NVIDIA}},
year={2026},
howpublished={Hugging Face blog},
url={https://huggingface.co/blog/nvidia/kumo-tabular},
}
@misc{eo2026exaonetabular10,
title={EXAONE Tabular 1.0 : Technical Report},
author={Moonjung Eo and Min-Kook Suh and Hye-Seung Cho and Jiwon Kim and Seoyoon Kim and Sangjun Nam and Soonyoung Lee},
year={2026},
eprint={2608.25774},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2608.25774},
}
@misc{cho2026causilotechnicalreport,
title={Causilo Technical Report},
author={Minyong Cho and Minho Jeong and Dooho Lee and Jinmo Lee and Jaemin Yoo},
year={2026},
eprint={2609.22866},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2609.22866},
}
@misc{kong2026tabfmzeroshotfoundationmodel,
title={TabFM: A Zero-Shot Foundation Model for Tabular Data},
author={Weihao Kong and Erez Louidor Ilan and Shuxin Nie and Taman Narayan and Rajat Sen and Yichen Zhou and Deqing Fu and Samet Oymak and Abhimanyu Das},
year={2026},
eprint={2609.37959},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2609.37959},
}
@misc{tabldmteam2026xiaomitabldmtabularfoundationmodel,
title={Xiaomi-TabLDM: A Tabular Foundation Model Technical Report},
author={TabLDM Team and Penghui Wang and Wei Liu and Hong Wang and Chengyue Huang and Yuxi Sun and Zirui Wang and Hongming Huang and Quan Wang and Zhenwei Xin and Ping Hou and Jie Yu and Chunxiao Liu and Erli Meng and Bin Wang},
year={2026},
eprint={2609.03880},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2609.03880},
}
@misc{wang2024textembeddingsweaklysupervisedcontrastive,
title={Text Embeddings by Weakly-Supervised Contrastive Pre-training},
author={Liang Wang and Nan Yang and Xiaolong Huang and Binxing Jiao and Linjun Yang and Daxin Jiang and Rangan Majumder and Furu Wei},
year={2024},
eprint={2212.03533},
archivePrefix={arXiv},
primaryClass={cs.CL},
url={https://arxiv.org/abs/2212.03533},
}
@misc{simeoni2025dinov3,
title={DINOv3},
author={Oriane Siméoni and Huy V. Vo and Maximilian Seitzer and Federico Baldassarre and Maxime Oquab and Cijo Jose and Vasil Khalidov and Marc Szafraniec and Seungeun Yi and Michaël Ramamonjisoa and Francisco Massa and Daniel Haziza and Luca Wehrstedt and Jianyuan Wang and Timothée Darcet and Théo Moutakanni and Leonel Sentana and Claire Roberts and Andrea Vedaldi and Jamie Tolan and John Brandt and Camille Couprie and Julien Mairal and Hervé Jégou and Patrick Labatut and Piotr Bojanowski},
year={2025},
eprint={2508.10104},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2508.10104},
}</pre></div>
</section>
</div>
</div>
<script>
function parseCSV(text) {
const rows = [];
let row = [], field = '', quoted = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (quoted) {
if (c === '"' && text[i + 1] === '"') { field += '"'; i++; }
else if (c === '"') quoted = false;
else field += c;
} else if (c === '"') quoted = true;
else if (c === ',') { row.push(field); field = ''; }
else if (c === '\n') { row.push(field); rows.push(row); row = []; field = ''; }
else if (c !== '\r') field += c;
}
if (field || row.length) { row.push(field); rows.push(row); }
const [header, ...body] = rows;
return body.map(r => Object.fromEntries(header.map((h, i) => {
const v = r[i];
return [h, v === '' || v === undefined ? null : isNaN(Number(v)) ? v : Number(v)];
})));
}
// The Space runs in an iframe that most sites refuse to load in, so external links open in a new tab.
document.addEventListener('click', e => {
const a = e.target.closest('a[href^="http"]');
if (a) Object.assign(a, { target: '_blank', rel: 'noopener' });
});
async function load() {
const get = async name => parseCSV(await (await fetch(`data/${name}.csv`)).text());
const boards = {}, encodingBoards = {};
(await get('leaderboards')).forEach(r => {
(boards[[r.tier, r.task, r.modality].join('|')] ||= []).push({ ...r, lo: r.elo_low, hi: r.elo_high, norm_error: r.normalized_error, champions: r.best_on });
});
(await get('encodings')).forEach(r => {
(encodingBoards[[r.tier, r.task, r.modality].join('|')] ||= []).push({ ...r, entry: r.encoding, lo: r.elo_low, hi: r.elo_high, champions: r.best_on });
});
const per_dataset = (await get('per_dataset')).map(r => ({ ...r, task_type: r.dataset.slice(0, 3) }));
const cost = await get('cost');
const res = await fetch('data/leaderboards.csv', { method: 'HEAD' });
const updated = res.headers.get('last-modified') && new Date(res.headers.get('last-modified')).toISOString().slice(0, 10);
const info = Object.fromEntries((await get('models')).map(r => [r.model, r]));
const entries = boards['all|all|all'].map(r => r.entry).sort();
const encodingOf = Object.fromEntries(boards['all|all|all'].map(r => [r.entry, r.encoding]));
return { boards, encodingBoards, per_dataset, cost, updated, info, encodingOf, meta: { entries } };
}
load().then(start).catch(err => {
document.querySelector('.wrap').insertAdjacentHTML('afterbegin', `<p class="flag">Could not load the leaderboard data: ${err.message}</p>`);
});
function start(D) {
const modelName = m => { const i = D.info[m]; return i ? `<img class="mlogo" src="assets/logos/${i.logo}" alt="" title="${i.developer}"><a href="${i.link}" title="${i.developer}: paper or report">${m}</a>` : m; };
const ENC_LABEL = { frozen: 'Frozen-S', tar: 'TAR-S', large: 'Frozen-L', 'large-tar': 'TAR-L', 'end-to-end': 'E2E' };
const ENC_TITLE = { frozen: 'Frozen small encoders', tar: 'Small encoders fine-tuned on each fold', large: 'Frozen large encoders', 'large-tar': 'Large encoders fine-tuned on each fold', 'end-to-end': 'Reads raw text itself' };
const encColor = e => `var(--${e === 'end-to-end' ? 'e2e' : e})`;
// A short mark for the encoding, in place of the "(Frozen-S)" or "(TAR-S)" of an entry's name.
const ENC_MARK = { frozen: '❄️', tar: '🎯', large: '🧊', 'large-tar': '🏹', 'end-to-end': '🔚' };
const encodingCell = e => `<td><span class="chip ${e}" title="${ENC_TITLE[e]}">${ENC_LABEL[e]}</span></td>`;
const TIERS = [['core', 'Core'], ['extended', 'Extended']];
const TASKS = [['classification', 'Classification'], ['regression', 'Regression']];
const MODS = [['text', 'Text'], ['image', 'Image']];
const ENCS = [['frozen', 'Frozen-S'], ['tar', 'TAR-S'], ['large', 'Frozen-L'], ['large-tar', 'TAR-L']];
const encoderCells = (e5, dino) => `<td class="model"><a href="https://huggingface.co/intfloat/${e5}-v2">${e5}</a></td>`
+ `<td class="model"><a href="https://huggingface.co/facebook/dinov3-vit${dino[0]}16-pretrain-lvd1689m">DINOv3-${dino[1]}</a></td>`;
const ENCODERS = { frozen: encoderCells('e5-small', ['s', 'small']), tar: encoderCells('e5-small', ['s', 'small']),
large: encoderCells('e5-large', ['l', 'large']), 'large-tar': encoderCells('e5-large', ['l', 'large']) };
// The encodings board names each encoding by its label.
const ENC_KEY = Object.fromEntries(Object.entries(ENC_LABEL).map(([k, v]) => [v, k]));
const taskOf = d => d.startsWith('REG_') ? 'regression' : 'classification';
const store = {
get(k) { try { return localStorage.getItem(k); } catch (e) { return null; } },
set(k, v) { try { localStorage.setItem(k, v); } catch (e) {} }
};
const fmt = (x, d = 0) => x == null ? '—' : Number(x).toLocaleString('en-US', { minimumFractionDigits: d, maximumFractionDigits: d });
const el = (tag, attrs = {}, html = '') => Object.assign(document.createElement(tag), attrs, html ? { innerHTML: html } : {});
// Every visit opens in the system's theme; the buttons override it until the next visit.
const THEMES = [['light', 'Light'], ['dark', 'Dark']];
function setTheme(t) {
document.documentElement.setAttribute('data-theme', t);
const box = document.getElementById('themeTabs');
box.innerHTML = '';
THEMES.forEach(([k, label]) => {
const b = el('button', { type: 'button', onclick: () => { setTheme(k); render(); } }, label);
b.setAttribute('aria-pressed', k === t);
box.appendChild(b);
});
}
setTheme(matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
// The multabench package version the board is built with.
const LATEST = { version: 'v0.1.2', date: D.updated };
document.getElementById('version').innerHTML = `<span class="tag">${LATEST.version}</span>`;
// Page tabs follow the URL hash, so each can be linked to; the filters only show where they apply.
const TABS = [...document.querySelectorAll('#pageTabs a')].map(a => a.dataset.tab);
function showTab() {
const k = TABS.includes(location.hash.slice(1)) ? location.hash.slice(1) : 'board';
TABS.forEach(t => { document.getElementById('tab-' + t).hidden = t !== k; });
document.querySelectorAll('#pageTabs a').forEach(a => a.setAttribute('aria-selected', a.dataset.tab === k));
document.querySelector('.filters').hidden = k === 'paper';
document.getElementById('encodingTabs').hidden = k === 'encodings';
}
window.addEventListener('hashchange', () => { showTab(); document.getElementById('pageTabs').scrollIntoView(); });
showTab();
[['copyBib', 'bib'], ['copyModelsBib', 'modelsBib']].forEach(([button, pre]) => {
document.getElementById(button).onclick = e => {
navigator.clipboard?.writeText(document.getElementById(pre).textContent).then(() => { e.target.textContent = 'Copied'; });
};
});
// Encoding counts twice: on the 1K training rows and on the 1K predicted rows.
let withEnc = store.get('enc_time') === '1';
const encodeOf = r => withEnc ? 2 * (r.text_encode_s_per_1k + r.image_encode_s_per_1k) : 0;
let timeOf = {};
// TAR's fine-tuning is part of training, so it always counts; encoding the rows is optional, as for frozen encoders.
const computeTime = () => { timeOf = Object.fromEntries(D.cost.map(r => [r.entry, r.train_s_per_1k + r.finetune_s_per_1k + r.inference_s_per_1k + encodeOf(r)])); };
computeTime();
let showNames = store.get('pareto_names') !== '0', withEmojis = store.get('pareto_emojis') !== '0';
function drawEncTabs() {
const box = document.getElementById('encTabs');
const b = el('button', { type: 'button', title: "Estimate, from the cached encoders' own time", onclick: () => {
withEnc = !withEnc; store.set('enc_time', withEnc ? '1' : '0'); computeTime(); drawEncTabs(); render();
} }, 'With encoding time');
b.setAttribute('aria-pressed', withEnc);
const n = el('button', { type: 'button', title: 'Each name is also in its point\'s tooltip', onclick: () => {
showNames = !showNames; store.set('pareto_names', showNames ? '1' : '0'); drawEncTabs(); render();
} }, 'Show names');
n.setAttribute('aria-pressed', showNames);
const m = el('button', { type: 'button', title: '❄️ for Frozen-S, 🎯 for TAR-S, 🧊 for Frozen-L, 🏹 for TAR-L and 🔚 for E2E, as the points', onclick: () => {
withEmojis = !withEmojis; store.set('pareto_emojis', withEmojis ? '1' : '0'); drawEncTabs(); render();
} }, 'With emojis');
m.setAttribute('aria-pressed', withEmojis);
box.replaceChildren(b, n, m);
}
drawEncTabs();
function paretoFront(board) {
const front = new Set();
let best = -Infinity;
[...board].filter(r => timeOf[r.entry] != null).sort((a, b) => timeOf[a.entry] - timeOf[b.entry] || b.elo - a.elo)
.forEach(r => { if (r.elo > best) { front.add(r.entry); best = r.elo; } });
return front;
}
function drawPareto(board, front) {
const rows = board.filter(r => timeOf[r.entry] != null);
const W = 1080, H = 500, left = 64, right = 24, top = 60, bottom = 52;
const lt = rows.map(r => Math.log10(timeOf[r.entry]));
const tMin = Math.floor(Math.min(...lt)), tMax = Math.ceil(Math.max(...lt));
const eMin = Math.floor((Math.min(...rows.map(r => r.elo)) - 60) / 200) * 200;
const eMax = Math.ceil((Math.max(...rows.map(r => r.elo)) + 60) / 200) * 200;
const x = t => left + (Math.log10(t) - tMin) / (tMax - tMin) * (W - left - right);
const y = e => top + (eMax - e) / (eMax - eMin) * (H - top - bottom);
let s = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" role="img" aria-label="Elo against run time, with the Pareto front">`;
for (let p = tMin; p <= tMax; p++) {
const v = 10 ** p;
s += `<line x1="${x(v)}" x2="${x(v)}" y1="${top}" y2="${H - bottom}" stroke="var(--soft)"/>`;
s += `<text x="${x(v)}" y="${H - bottom + 18}" text-anchor="middle">${v >= 1 ? fmt(v) : v}</text>`;
}
for (let e = eMin; e <= eMax; e += 200) {
s += `<line x1="${left}" x2="${W - right}" y1="${y(e)}" y2="${y(e)}" stroke="var(--soft)"/>`;
s += `<text x="${left - 10}" y="${y(e) + 4}" text-anchor="end">${e}</text>`;
}
s += `<text x="${(left + W - right) / 2}" y="${H - 10}" text-anchor="middle">Train (with TAR fine-tuning) + predict${withEnc ? ' + encoding (estimate)' : ''} s per 1K rows, log scale</text>`;
s += `<text transform="translate(16 ${(top + H - bottom) / 2}) rotate(-90)" text-anchor="middle">Elo</text>`;
const fr = rows.filter(r => front.has(r.entry)).sort((a, b) => timeOf[a.entry] - timeOf[b.entry]);
let path = '';
fr.forEach((r, i) => {
const px = x(timeOf[r.entry]), py = y(r.elo);
path += i === 0 ? `M${px} ${H - bottom} L${px} ${py}` : ` L${px} ${y(fr[i - 1].elo)} L${px} ${py}`;
});
if (fr.length) path += ` L${W - right} ${y(fr[fr.length - 1].elo)}`;
s += `<path d="${path}" fill="none" stroke="var(--warn)" stroke-width="1.6"/>`;
// Front labels first, then the others wherever they fit; a label that fits nowhere is left to the tooltip.
const boxes = [], hit = (b) => boxes.some(o => b.x0 < o.x1 && o.x0 < b.x1 && b.y0 < o.y1 && o.y0 < b.y1);
// With emojis the point carries the encoding; without, a name does once both encodings are on the board.
const name = r => withEmojis || encSel.length < 2 ? r.model : r.entry;
const half = 6;
const place = (r, onFront) => {
const px = x(timeOf[r.entry]), py = y(r.elo), w = name(r).length * (onFront ? 7 : 6.4), h = 12;
const spots = onFront ? [[px - half - 4 - w, py - half - 3 - h]] : [[px + half + 3, py - 4], [px - half - 3 - w, py - 4], [px - w / 2, py - half - 13], [px - w / 2, py + half + 2]];
for (const [x0, y0] of spots) {
const box = { x0, y0, x1: x0 + w, y1: y0 + h };
if (onFront || !hit(box)) { boxes.push(box); return `<text${onFront ? ' class="name"' : ' font-size="10.5"'} x="${x0}" y="${y0 + 10}">${name(r)}</text>`; }
}
return '';
};
rows.forEach(r => boxes.push({ x0: x(timeOf[r.entry]) - half, x1: x(timeOf[r.entry]) + half, y0: y(r.elo) - half, y1: y(r.elo) + half }));
const tip = r => `<title>${r.entry}: Elo ${fmt(r.elo)}, ${fmt(timeOf[r.entry], 2)} s per 1K rows</title>`;
// A point is its encoding's emoji, or a colored square outlined on the front; both the size of the old squares.
const point = (px, py, e, onFront, title = '') => {
const h = onFront ? 5.5 : 4;
if (withEmojis) return `<text x="${px}" y="${py + h}" text-anchor="middle" font-size="${2 * h + 2}">${title}${ENC_MARK[e]}</text>`;
const edge = onFront ? ' stroke="var(--warn)" stroke-width="2"' : ' opacity=".7"';
return `<rect x="${px - h}" y="${py - h}" width="${2 * h}" height="${2 * h}" fill="${encColor(e)}"${edge}>${title}</rect>`;
};
rows.filter(r => !front.has(r.entry)).forEach(r => { s += point(x(timeOf[r.entry]), y(r.elo), r.encoding, false, tip(r)); });
fr.forEach(r => { s += point(x(timeOf[r.entry]), y(r.elo), r.encoding, true, tip(r)); });
// The front keeps its names; Show names adds the others.
fr.forEach(r => { s += place(r, true); });
if (showNames) [...rows].filter(r => !front.has(r.entry)).sort((a, b) => b.elo - a.elo).forEach(r => { s += place(r, false); });
// The legend sits above the plot, boxed, so it is read before the points.
const encs = [...ENCS.map(e => e[0]), 'end-to-end'].filter(e => rows.some(r => r.encoding === e));
const items = [...encs.map(e => [point(0, 0, e, false), ENC_LABEL[e]]), ['<line x1="-8" x2="6" stroke="var(--warn)" stroke-width="2"/>', 'Pareto front']];
const widths = items.map(([, t]) => 30 + t.length * 8), legendW = widths.reduce((a, b) => a + b, 0) + 16;
s += `<rect x="${left}" y="8" width="${legendW}" height="30" rx="15" fill="var(--bg)" stroke="var(--line)"/>`;
let lx = left + 16;
items.forEach(([mark, t], i) => {
s += `<g transform="translate(${lx + 6} 23) scale(1.3)">${mark}</g><text class="lbl" x="${lx + 18}" y="28">${t}</text>`;
lx += widths[i];
});
s += '</svg>';
document.getElementById('paretoChart').innerHTML = `<div style="padding:12px 8px 4px">${s}</div>`;
}
const dsInfo = {};
D.per_dataset.forEach(r => { dsInfo[r.dataset] = { tier: r.tier, task: taskOf(r.dataset), modality: r.modality }; });
const restore = (k, all, dflt = all) => { try { const v = JSON.parse(store.get(k)); return v.length && v.every(x => all.includes(x)) ? v : dflt; } catch (e) { return dflt; } };
const sel = { tier: restore('tiers_v2', TIERS.map(t => t[0]), ['core']), task: restore('tasks', TASKS.map(t => t[0])),
modality: restore('mods', MODS.map(m => m[0])) };
const FIELDS = Object.keys(sel);
let encSel = restore('encodings', ENCS.map(e => e[0]));
// Each board rates every entry; the encodings picked only choose which of them are shown.
const slice = board => board.filter(r => r.encoding === 'end-to-end' || encSel.includes(r.encoding));
const keyOf = (picked, all) => picked.length === all.length ? 'all' : all.map(a => a[0]).filter(a => picked.includes(a)).join('+');
const inFilter = d => FIELDS.every(f => sel[f].includes(dsInfo[d][f]));
// One choice per row: All, or a single slice; count(keys) is how many datasets or entries that choice keeps.
function choices(container, items, picked, count, pick) {
container.querySelectorAll('button').forEach(b => b.remove());
[['all', 'All'], ...items].forEach(([key, label]) => {
const keys = key === 'all' ? items.map(i => i[0]) : [key];
const b = el('button', { type: 'button' }, `${label}<span class="n">${count(keys)}</span>`);
b.setAttribute('aria-pressed', keys.length === picked.length && keys.every(k => picked.includes(k)));
b.onclick = () => { pick(keys); render(); };
container.appendChild(b);
});
}
function toggles(container, items, field, storeKey) {
const count = keys => Object.values(dsInfo).filter(i => keys.includes(i[field]) && FIELDS.every(f => f === field || sel[f].includes(i[f]))).length;
choices(container, items, sel[field], count, keys => { sel[field] = keys; store.set(storeKey, JSON.stringify(keys)); });
}
// Encodings combine: from All a click keeps just that one, else it toggles; none or every one is All again.
function encodingToggles() {
const box = document.getElementById('encodingTabs'), all = ENCS.map(e => e[0]), isAll = encSel.length === all.length;
const count = keys => D.meta.entries.filter(m => keys.includes(D.encodingOf[m])).length;
const pick = keys => { encSel = keys.length ? all.filter(k => keys.includes(k)) : all; store.set('encodings', JSON.stringify(encSel)); render(); };
box.querySelectorAll('button').forEach(b => b.remove());
[['all', 'All'], ...ENCS].forEach(([key, label]) => {
const keys = key === 'all' ? all : [key];
const b = el('button', { type: 'button' }, `${label}<span class="n">${count(keys)}</span>`);
b.setAttribute('aria-pressed', key === 'all' ? isAll : !isAll && encSel.includes(key));
b.onclick = () => pick(key === 'all' || isAll ? keys : encSel.includes(key) ? encSel.filter(k => k !== key) : [...encSel, key]);
box.appendChild(b);
});
}
// A banded column per model, its encodings side by side within it.
function drawElo(board) {
const groups = [];
board.forEach(r => { let g = groups.find(g => g.model === r.model); if (!g) groups.push(g = { model: r.model, rows: [] }); g.rows.push(r); });
const ORDER = ['frozen', 'large', 'tar', 'large-tar', 'end-to-end'];
groups.forEach(g => g.rows.sort((a, b) => ORDER.indexOf(a.encoding) - ORDER.indexOf(b.encoding)));
const W = 1080, H = 460, left = 56, right = 16, top = 40, bottom = 116, slot = (W - left - right) / groups.length;
const lo = Math.min(1000, ...board.map(r => r.lo)), hi = Math.max(...board.map(r => r.hi));
const step = hi - lo > 1200 ? 400 : 200;
const min = Math.floor((lo - 50) / step) * step, max = Math.ceil((hi + 50) / step) * step;
const y = v => top + (max - v) / (max - min) * (H - top - bottom);
let s = `<svg viewBox="0 0 ${W} ${H}" role="img" aria-label="Elo ratings with 95% intervals, by model and encoding">`;
// Every other model gets a band, so each model's entries read as one group.
groups.forEach((g, i) => { if (i % 2 === 0) s += `<rect x="${left + i * slot}" y="${top}" width="${slot}" height="${H - top - bottom}" fill="var(--bg)"/>`; });
for (let t = min; t <= max; t += step) {
s += `<line x1="${left}" x2="${W - right}" y1="${y(t)}" y2="${y(t)}" stroke="var(--soft)"/>`;
s += `<text x="${left - 10}" y="${y(t) + 4}" text-anchor="end">${t}</text>`;
}
s += `<line x1="${left}" x2="${W - right}" y1="${y(1000)}" y2="${y(1000)}" stroke="var(--muted)" stroke-dasharray="3 3"/>`;
let lx = left;
ORDER.filter(e => board.some(r => r.encoding === e)).forEach(e => {
s += `<circle cx="${lx + 6}" cy="16" r="5" fill="${encColor(e)}"/><text x="${lx + 16}" y="20">${ENC_LABEL[e]}</text>`;
lx += 26 + ENC_LABEL[e].length * 7 + 18;
});
groups.forEach((g, i) => {
const cx = left + (i + 0.5) * slot, gap = Math.min(12, slot * 0.7 / g.rows.length);
const xs = g.rows.map((r, j) => cx + (j - (g.rows.length - 1) / 2) * gap);
g.rows.forEach((r, j) => {
const c = encColor(r.encoding);
const tip = `<title>${r.entry}: Elo ${fmt(r.elo)} (${fmt(r.lo)}–${fmt(r.hi)})</title>`;
if (r.hi > r.lo) s += `<line x1="${xs[j]}" x2="${xs[j]}" y1="${y(r.hi)}" y2="${y(r.lo)}" stroke="${c}" stroke-opacity=".35" stroke-width="4" stroke-linecap="round"/>`;
s += `<circle cx="${xs[j]}" cy="${y(r.elo)}" r="4" fill="${c}">${tip}</circle>`;
});
s += `<text class="lbl" text-anchor="end" transform="translate(${cx + 4} ${H - bottom + 16}) rotate(-40)">${g.model}</text>`;
});
s += '</svg>';
document.getElementById('eloChart').innerHTML = `<div style="padding:8px 8px 0">${s}</div>`;
}
// Grouped, a model's entries sit together, ranked by its best one; on by default.
let byModel = store.get('group_by_model') !== '0';
function drawGroupTabs() {
const b = el('button', { type: 'button', title: "Each model's entries together, ranked by its best one", onclick: () => {
byModel = !byModel; store.set('group_by_model', byModel ? '1' : '0'); drawGroupTabs(); render();
} }, 'Group by model');
b.setAttribute('aria-pressed', byModel);
document.getElementById('groupTabs').replaceChildren(b);
}
drawGroupTabs();
function drawBoard(board, front) {
const t = document.getElementById('boardTable');
// The board is sorted by Elo, so a stable sort on each model's first row keeps its entries in Elo order.
const first = {}, size = {};
board.forEach((r, i) => { first[r.model] ??= i; size[r.model] = (size[r.model] || 0) + 1; });
const models = Object.keys(first);
const rows = byModel ? [...board].sort((a, b) => first[a.model] - first[b.model]) : board;
const maxN = Math.max(...board.map(r => r.datasets));
t.innerHTML = `<thead><tr><th></th><th class="pf" title="No other entry is both faster and stronger">Pareto Frontier</th><th>Encoding</th><th>Model</th><th class="num">Elo</th><th class="num">95% CI</th><th class="num" title="Share of its battles the entry wins">Win rate</th></tr></thead>`;
const tb = el('tbody');
rows.forEach((r, i) => {
const ci = `<span class="ci">+${fmt(r.hi - r.elo)} / −${fmt(r.elo - r.lo)}</span>`;
const n = r.datasets < maxN ? ` <span class="flag" title="Rated on ${r.datasets} of ${maxN} datasets">${r.datasets}/${maxN}</span>` : '';
const star = front.has(r.entry) ? '<span class="star" title="On the Pareto front of Elo and run time">★</span>' : '';
const lead = !byModel || first[r.model] === board.indexOf(r), span = byModel ? size[r.model] : 1;
const rank = byModel ? (lead ? models.indexOf(r.model) + 1 : '') : i + 1;
const model = lead ? `<td class="model${span > 1 ? ' grouped' : ''}" rowspan="${span}">${modelName(r.model)}${n}</td>` : '';
const end = byModel && (i === rows.length - 1 || rows[i + 1].model !== r.model);
const cls = [front.has(r.entry) ? 'front' : '', end ? 'groupEnd' : ''].filter(Boolean).join(' ');
tb.appendChild(el('tr', { className: cls }, `<td class="rank">${rank}</td><td class="pf">${star}</td>${encodingCell(r.encoding)}${model}<td class="num"><b>${fmt(r.elo)}</b></td><td class="num">${ci}</td><td class="num">${fmt(r.win_rate * 100, 1)}%</td>`));
});
t.appendChild(tb);
}
const filterKey = () => keyOf(sel.tier, TIERS) + '|' + keyOf(sel.task, TASKS) + '|' + keyOf(sel.modality, MODS);
function renderBoard() {
const board = slice(D.boards[filterKey()]);
const names = Object.keys(dsInfo).filter(inFilter);
const count = (field, key) => names.filter(d => dsInfo[d][field] === key).length;
const line = (title, field, all) => `<span class="label">${title}</span> ` + all.filter(([k]) => count(field, k))
.map(([k, label]) => `${count(field, k)} ${label}`).join(' · ');
document.getElementById('boardNote').innerHTML = [`<b>${names.length} datasets</b>` + (names.length < 10 ? ' (few, so wide intervals)' : ''),
line('Datasets', 'tier', TIERS), line('Task', 'task', TASKS), line('Modality', 'modality', MODS)].join('<br>');
const front = paretoFront(board);
drawElo(board);
drawBoard(board, front);
drawPareto(board, front);
}
const orderOf = () => slice(D.boards['all|all|all']).map(r => r.entry);
function render() {
toggles(document.getElementById('tierTabs'), TIERS, 'tier', 'tiers');
toggles(document.getElementById('taskTabs'), TASKS, 'task', 'tasks');
toggles(document.getElementById('modTabs'), MODS, 'modality', 'mods');
encodingToggles();
renderEncodings(filterKey());
renderBoard();
renderCost();
}
render();
function renderCost() {
const order = orderOf();
const rows = D.cost.filter(r => order.includes(r.entry)).sort((a, b) => order.indexOf(a.entry) - order.indexOf(b.entry));
const maxT = Math.max(...rows.map(r => timeOf[r.entry]));
const what = 'Train + finetune' + (withEnc ? ' + encode' : '') + ' + predict';
const t = document.getElementById('costTable');
t.innerHTML = `<thead><tr><th>Encoding</th><th>Model</th><th>Device</th><th>${what} s / 1K rows</th><th class="num">Train</th><th class="num" title="TAR's LoRA fine-tuning, per 1K training rows">Finetune</th><th class="num" title="Text and image encoding, estimate per 1K rows">Encode</th><th class="num">Predict</th></tr></thead>`;
const tb = el('tbody');
rows.forEach(r => {
const total = timeOf[r.entry];
tb.appendChild(el('tr', {}, `${encodingCell(r.encoding)}<td class="model">${modelName(r.model)}</td><td><span class="dev">${r.gpu ? 'H100' : 'CPU'}</span></td><td class="mono"><span class="bar" style="width:${Math.max(2, total / maxT * 140)}px"></span>${fmt(total, 2)}</td><td class="num">${fmt(r.train_s_per_1k, 2)}</td><td class="num">${r.finetune_s_per_1k ? fmt(r.finetune_s_per_1k, 2) : '—'}</td><td class="num">${fmt(r.text_encode_s_per_1k + r.image_encode_s_per_1k, 2)}</td><td class="num">${fmt(r.inference_s_per_1k, 2)}</td>`));
});
t.appendChild(tb);
}
function renderEncodings(filterKey) {
const board = D.encodingBoards[filterKey];
const t = document.getElementById('encodingTable');
t.innerHTML = `<thead><tr><th></th><th>Encoding</th><th>Text encoder</th><th>Image encoder</th><th class="num">Elo</th><th class="num">95% CI</th><th class="num" title="Share of the battles these encoders win">Win rate</th></tr></thead>`;
const tb = el('tbody');
board.forEach((r, i) => {
const ci = r.hi > r.lo ? `<span class="ci">+${fmt(r.hi - r.elo)} / −${fmt(r.elo - r.lo)}</span>` : '<span class="ci">anchor</span>';
tb.appendChild(el('tr', {}, `<td class="rank">${i + 1}</td><td><span class="chip ${ENC_KEY[r.encoding]}">${r.encoding}</span></td>${ENCODERS[ENC_KEY[r.encoding]]}<td class="num"><b>${fmt(r.elo)}</b></td><td class="num">${ci}</td><td class="num">${fmt(r.win_rate * 100, 1)}%</td>`));
});
t.appendChild(tb);
}
}
</script>
</body>
</html>