multabench 0.0.1__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {multabench-0.0.1 → multabench-0.0.2}/PKG-INFO +63 -26
- {multabench-0.0.1 → multabench-0.0.2}/README.md +53 -24
- multabench-0.0.2/multabench/__init__.py +7 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/abstract_model.py +10 -1
- multabench-0.0.2/multabench/baselines/causilo.py +26 -0
- multabench-0.0.2/multabench/baselines/exaone_tabular.py +41 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/kumo_tabular.py +7 -1
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/categorical.py +6 -2
- multabench-0.0.2/multabench/baselines/tabdpt13.py +45 -0
- multabench-0.0.2/multabench/baselines/tabfm.py +60 -0
- multabench-0.0.2/multabench/baselines/tabldm.py +37 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabstar_v1.py +44 -4
- multabench-0.0.2/multabench/baselines/tabstar_v1p1.py +14 -0
- multabench-0.0.2/multabench/baselines/training/metrics.py +24 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/load.py +4 -2
- multabench-0.0.2/multabench/benchmark/metrics.py +37 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/sweep.py +11 -3
- multabench-0.0.2/multabench/datasets/info.py +41 -0
- multabench-0.0.2/multabench/datasets/revisions.json +84 -0
- multabench-0.0.2/multabench/datasets/revisions.py +41 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/hub.py +4 -2
- {multabench-0.0.1 → multabench-0.0.2}/multabench/finetune/utils.py +1 -1
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/elo.py +32 -45
- multabench-0.0.2/multabench/leaderboard/export.py +86 -0
- multabench-0.0.2/multabench/leaderboard/models/CatBoost.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/Causilo.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/ConTextTab.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular-Medium.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular-Small.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/LightGBM.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/RandomForest.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/RealMLP.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabDPT.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabFM.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabICLv2.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabM.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.5-Fast.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.5.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabPFN-v2.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabPFN-v2p5.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/TabSTAR.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/models/XGBoost.csv +481 -0
- multabench-0.0.2/multabench/leaderboard/published.py +13 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/PKG-INFO +63 -26
- {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/SOURCES.txt +36 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/requires.txt +13 -1
- {multabench-0.0.1 → multabench-0.0.2}/pyproject.toml +15 -2
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_core_imports.py +2 -1
- multabench-0.0.2/tests/test_datasets_info.py +36 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_elo.py +25 -6
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_metrics.py +24 -2
- multabench-0.0.2/tests/test_published_results.py +20 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_registry.py +2 -2
- multabench-0.0.2/tests/test_space_data.py +28 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_sweep.py +18 -1
- multabench-0.0.2/tests/test_tabstar_mapping.py +14 -0
- multabench-0.0.1/multabench/__init__.py +0 -2
- multabench-0.0.1/multabench/baselines/training/metrics.py +0 -37
- {multabench-0.0.1 → multabench-0.0.2}/LICENSE +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/autogluon_mm.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/benchmarks/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/benchmarks/evaluate.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/catboost.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/contexttab.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/lgbm.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/dates.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/feature_types.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/image_embeddings.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/nulls.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/numerical.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/target.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/text_embeddings.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/validation.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/random_forest.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/realmlp.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabdpt.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabicl_v2.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabm.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabpfn.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/training/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/xgboost.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/collect.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/runs.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/splits.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/constants.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/curation.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/constants.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/all_datasets.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/hub.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/objects.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/constants.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/dino_finetune.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/image_loading.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/constants.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/e5_finetune.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/compute.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/embed.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/embed_sweep.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/jobs.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/finetune/train_args.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/keys.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/loading.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/large_results.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/paper_results.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/plots.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/utils.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/discretize.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/splits.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/result_keys.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/scripts/do_leaderboard.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/__init__.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/devices.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/encoders.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/hardware.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/logging.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/profiling.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/timing.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/warnings.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/dependency_links.txt +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/top_level.txt +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/setup.cfg +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_collect.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_embeddings.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_feature_types.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_hardware.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_hub.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_leaderboard_results.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_official_commit.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_profiling.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_splits.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_text_embeddings.py +0 -0
- {multabench-0.0.1 → multabench-0.0.2}/tests/test_timing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multabench
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Benchmarking multimodal tabular learning: tables whose columns include text or images
|
|
5
5
|
Author: Alan Arazi, Eilam Shapira, Shoham Grunblat, Mor Ventura, Elad Hoffer, Gioia Blayer, David Holzmüller, Lennart Purucker, Gaël Varoquaux, Frank Hutter, Roi Reichart
|
|
6
6
|
License-Expression: MIT
|
|
@@ -42,9 +42,17 @@ Requires-Dist: tabdpt>=1.1.9; extra == "tabdpt"
|
|
|
42
42
|
Provides-Extra: tabpfn
|
|
43
43
|
Requires-Dist: tabpfn>=9.2.0; extra == "tabpfn"
|
|
44
44
|
Provides-Extra: tabstar
|
|
45
|
-
Requires-Dist: tabstar>=1.1
|
|
45
|
+
Requires-Dist: tabstar>=1.2.1; extra == "tabstar"
|
|
46
|
+
Provides-Extra: tabfm
|
|
47
|
+
Requires-Dist: tabpfn-extensions[many_class]>=0.6.1; extra == "tabfm"
|
|
48
|
+
Provides-Extra: causilo
|
|
49
|
+
Requires-Dist: causilo==1.0.2; extra == "causilo"
|
|
50
|
+
Provides-Extra: tabdpt13
|
|
51
|
+
Requires-Dist: tabdpt==1.3.1; extra == "tabdpt13"
|
|
46
52
|
Provides-Extra: autogluon-mm
|
|
47
53
|
Requires-Dist: autogluon.multimodal; extra == "autogluon-mm"
|
|
54
|
+
Provides-Extra: leaderboard
|
|
55
|
+
Requires-Dist: bencheval==0.1.1.dev20261015010754; extra == "leaderboard"
|
|
48
56
|
Provides-Extra: dev
|
|
49
57
|
Requires-Dist: matplotlib; extra == "dev"
|
|
50
58
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -76,32 +84,51 @@ than used frozen.
|
|
|
76
84
|
|
|
77
85
|
## Leaderboard
|
|
78
86
|
|
|
87
|
+
The leaderboard lives at [huggingface.co/spaces/multabench/leaderboard](https://huggingface.co/spaces/multabench/leaderboard),
|
|
88
|
+
with per-dataset results and cost; `space/` holds its app and data.
|
|
89
|
+
|
|
79
90
|
Default hyperparameters, `--size 10k`, 6 folds, frozen `e5-small` text and `dino-small` image embeddings
|
|
80
|
-
(ConTextTab
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
(ConTextTab and TabSTAR read raw text themselves; TabSTAR loads, for each dataset it was pretrained on, the
|
|
92
|
+
checkpoint that held that dataset out). TabPFN-v2 and v2.5 take at most 10 classes, which leaves out 3 of the 80 datasets.
|
|
93
|
+
|
|
94
|
+
**How Elo is computed.** With TabArena's own evaluation package, [bencheval](https://pypi.org/project/bencheval/),
|
|
95
|
+
pinned in the `leaderboard` extra. Every fold is its own battle: on each of a dataset's 6 folds, each pair of models
|
|
96
|
+
plays one battle, won by the lower test error on that fold (a tie counts half). The battles are fit as a Bradley–Terry
|
|
97
|
+
model, with RandomForest anchored at 1000. The 95% intervals come from 1,000 bootstrap resamples of the datasets, each
|
|
98
|
+
drawn with all 6 of its folds, so the intervals reflect how many datasets there are rather than how many folds. They
|
|
99
|
+
are bootstrapped before anchoring, so each model's interval is its own, RandomForest's included, rather than relative
|
|
100
|
+
to RandomForest. A model with no result on a fold has no battles there; nothing is imputed. Win rate, average rank, normalized
|
|
101
|
+
error and best-on count are per fold as well. Until October 2026 the leaderboard, like the paper, played one battle
|
|
102
|
+
per dataset on the mean error over its folds. That makes each dataset winner-take-all, which stretches the scale: on
|
|
103
|
+
the then 17 models, Kumo-Tabular was at 2670 rather than 2347, with the same ranking.
|
|
83
104
|
|
|
84
105
|
| # | Model | Elo, 80 datasets | 95% CI | Text, 40 | Images, 40 | Datasets |
|
|
85
106
|
|---|---|---|---|---|---|---|
|
|
86
|
-
| 1 |
|
|
87
|
-
| 2 | Kumo-Tabular-
|
|
88
|
-
| 3 |
|
|
89
|
-
| 4 |
|
|
90
|
-
| 5 |
|
|
91
|
-
| 6 |
|
|
92
|
-
| 7 | TabPFN-
|
|
93
|
-
| 8 |
|
|
94
|
-
| 9 |
|
|
95
|
-
| 10 |
|
|
96
|
-
| 11 |
|
|
97
|
-
| 12 |
|
|
98
|
-
| 13 |
|
|
99
|
-
| 14 |
|
|
100
|
-
| 15 |
|
|
107
|
+
| 1 | Kumo-Tabular | 2279 | +108 / −84 | 2310 | 2334 | 80 |
|
|
108
|
+
| 2 | Kumo-Tabular-Medium | 2126 | +86 / −72 | 2141 | 2190 | 80 |
|
|
109
|
+
| 3 | TabFM | 2057 | +86 / −63 | 2044 | 2141 | 80 |
|
|
110
|
+
| 4 | TabPFN-3.5 | 1991 | +78 / −69 | 1913 | 2132 | 80 |
|
|
111
|
+
| 5 | Kumo-Tabular-Small | 1937 | +56 / −46 | 1915 | 2026 | 80 |
|
|
112
|
+
| 6 | Causilo | 1878 | +51 / −43 | 1826 | 1993 | 80 |
|
|
113
|
+
| 7 | TabPFN-3.5-Fast | 1861 | +61 / −54 | 1782 | 2002 | 80 |
|
|
114
|
+
| 8 | TabICLv2 | 1648 | +37 / −35 | 1564 | 1792 | 80 |
|
|
115
|
+
| 9 | TabPFN-3 | 1586 | +37 / −36 | 1488 | 1747 | 80 |
|
|
116
|
+
| 10 | TabPFN-v2p5 | 1586 | +34 / −38 | 1565 | 1660 | 77 |
|
|
117
|
+
| 11 | TabPFN-v2 | 1373 | +45 / −50 | 1291 | 1505 | 77 |
|
|
118
|
+
| 12 | ConTextTab | 1339 | +56 / −63 | 1270 | 1455 | 80 |
|
|
119
|
+
| 13 | TabM | 1328 | +41 / −45 | 1311 | 1378 | 80 |
|
|
120
|
+
| 14 | CatBoost | 1268 | +42 / −48 | 1274 | 1283 | 80 |
|
|
121
|
+
| 15 | TabDPT | 1231 | +62 / −65 | 1106 | 1400 | 80 |
|
|
122
|
+
| 16 | TabSTAR | 1201 | +68 / −71 | 1241 | 1165 | 80 |
|
|
123
|
+
| 17 | RealMLP | 1093 | +57 / −69 | 1100 | 1090 | 80 |
|
|
124
|
+
| 18 | LightGBM | 1052 | +64 / −73 | 1081 | 1016 | 80 |
|
|
125
|
+
| 19 | RandomForest | 1000 | +80 / −87 | 1000 | 1000 | 80 |
|
|
126
|
+
| 20 | XGBoost | 997 | +69 / −78 | 1029 | 953 | 80 |
|
|
101
127
|
|
|
102
128
|
Each model's runs are in `multabench/leaderboard/models/<model>.csv`, one row per (dataset, fold), failed
|
|
103
|
-
runs included. `python -m multabench.leaderboard.elo` prints the table, with win rate, average rank
|
|
104
|
-
|
|
129
|
+
runs included. `python -m multabench.leaderboard.elo` prints the table, with win rate, average rank, normalized
|
|
130
|
+
error and the number of folds each model is best on; `--modalities text` or `--modalities image,image-text` restricts
|
|
131
|
+
it to those datasets.
|
|
105
132
|
|
|
106
133
|
Leaderboard runs pass `--official`, which also requires a clean checkout of a commit on `master`, so the `git`
|
|
107
134
|
column of every row names code anyone can check out. The image runs of the first 11 models are the one exception: they
|
|
@@ -116,11 +143,18 @@ pip install multabench
|
|
|
116
143
|
```
|
|
117
144
|
|
|
118
145
|
```python
|
|
119
|
-
from multabench import Size, load_embeddings, load_split
|
|
146
|
+
from multabench import Size, list_datasets, load_embeddings, load_published_results, load_split, score
|
|
147
|
+
datasets = list_datasets(modality="text", task="regression", tier="core") # DatasetInfo: id, hf_repo, modality, ...
|
|
120
148
|
split = load_split("MUL_IMAGE_PETFINDER", fold=0, size=Size.TEN_K) # split.x_train, split.y_train, split.x_test, ...
|
|
121
149
|
embeddings = load_embeddings("MUL_TEXT_WINE_REVIEW", fold=0, size=Size.TEN_K, encoder="e5-small")
|
|
150
|
+
result = score(split.task_type, y_true=y_test, y_pred=proba) # metric, test_score, test_error, as the leaderboard
|
|
151
|
+
published = load_published_results() # every leaderboard run, in the columns of multabench/leaderboard/models/
|
|
122
152
|
```
|
|
123
153
|
|
|
154
|
+
Each package version reads every dataset and embeddings repo at the commit pinned in
|
|
155
|
+
`multabench/datasets/revisions.json`, and ships the leaderboard's runs as of its release. After uploading to one of
|
|
156
|
+
those repos, `python -m multabench.datasets.revisions` pins them all to their current `main`.
|
|
157
|
+
|
|
124
158
|
To run the benchmark:
|
|
125
159
|
|
|
126
160
|
```bash
|
|
@@ -132,8 +166,11 @@ python benchmark.py --model light --dataset_name MUL_IMAGE_PETFINDER --fold 0 --
|
|
|
132
166
|
|
|
133
167
|
`pyproject.toml` lists the core dependencies, a `bench` extra for `benchmark.py` and one extra per model package, so
|
|
134
168
|
`pip install -e ".[bench,catboost]"` installs what one model needs; each wrapper imports its package only when it runs.
|
|
135
|
-
ConTextTab and
|
|
136
|
-
`init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those
|
|
169
|
+
ConTextTab, Kumo and TabFM are pinned to a git commit, which PyPI doesn't allow in extras, so `requirements-git.txt`
|
|
170
|
+
lists them. `init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those
|
|
171
|
+
three. A model whose requirements conflict with that lock declares its own venv in its wrapper (`ENV`), locked in
|
|
172
|
+
`requirements-<ENV>.txt` and set up with `source init.sh <ENV>`: Causilo, which needs a newer torch, runs in
|
|
173
|
+
`.venv-causilo`. `sweep.py` writes each model's venv python into its jobs, so one sweep covers every model.
|
|
137
174
|
|
|
138
175
|
`benchmark.py` is the single entry point: it evaluates one model on one dataset fold and writes the
|
|
139
176
|
result to `runs/<model>_<dataset>_<text_encoder>_<image_encoder>_<size>_<fold>.json` (`--output_dir` to change), with
|
|
@@ -265,7 +302,7 @@ MulTaBench is not an official TabArena leaderboard.
|
|
|
265
302
|
| | TabArena | MulTaBench | Status |
|
|
266
303
|
|---|---|---|---|
|
|
267
304
|
| Metrics | ROC AUC (binary), log loss (multiclass), RMSE (regression), via AutoGluon's scorers | Same | ✅ |
|
|
268
|
-
| Leaderboard | Elo via `bencheval`, RandomForest (default) anchored at 1000, missing results imputed with RandomForest and flagged, bootstrapped confidence intervals | Elo
|
|
305
|
+
| Leaderboard | Elo via `bencheval`, one battle per task fold, RandomForest (default) anchored at 1000, missing results imputed with RandomForest and flagged, bootstrapped confidence intervals | Same Elo, one battle per dataset fold; missing results are left out, neither imputed nor dropping the model, so a model is ranked on the datasets it ran on, which its Datasets column shows | |
|
|
269
306
|
| Splits | Fixed and part of the task definition | Same: a seeded function of the data | ✅ |
|
|
270
307
|
| Group and time structure | Audited per dataset; group-aware or forward-in-time splits where needed | Not audited: every dataset is split IID. Some datasets may have group or time structure, in which case IID splits can overestimate performance | |
|
|
271
308
|
| Runtime | Train and inference time per 1K rows | Same, with the time spent embedding text and images reported separately | ✅ |
|
|
@@ -23,32 +23,51 @@ than used frozen.
|
|
|
23
23
|
|
|
24
24
|
## Leaderboard
|
|
25
25
|
|
|
26
|
+
The leaderboard lives at [huggingface.co/spaces/multabench/leaderboard](https://huggingface.co/spaces/multabench/leaderboard),
|
|
27
|
+
with per-dataset results and cost; `space/` holds its app and data.
|
|
28
|
+
|
|
26
29
|
Default hyperparameters, `--size 10k`, 6 folds, frozen `e5-small` text and `dino-small` image embeddings
|
|
27
|
-
(ConTextTab
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
(ConTextTab and TabSTAR read raw text themselves; TabSTAR loads, for each dataset it was pretrained on, the
|
|
31
|
+
checkpoint that held that dataset out). TabPFN-v2 and v2.5 take at most 10 classes, which leaves out 3 of the 80 datasets.
|
|
32
|
+
|
|
33
|
+
**How Elo is computed.** With TabArena's own evaluation package, [bencheval](https://pypi.org/project/bencheval/),
|
|
34
|
+
pinned in the `leaderboard` extra. Every fold is its own battle: on each of a dataset's 6 folds, each pair of models
|
|
35
|
+
plays one battle, won by the lower test error on that fold (a tie counts half). The battles are fit as a Bradley–Terry
|
|
36
|
+
model, with RandomForest anchored at 1000. The 95% intervals come from 1,000 bootstrap resamples of the datasets, each
|
|
37
|
+
drawn with all 6 of its folds, so the intervals reflect how many datasets there are rather than how many folds. They
|
|
38
|
+
are bootstrapped before anchoring, so each model's interval is its own, RandomForest's included, rather than relative
|
|
39
|
+
to RandomForest. A model with no result on a fold has no battles there; nothing is imputed. Win rate, average rank, normalized
|
|
40
|
+
error and best-on count are per fold as well. Until October 2026 the leaderboard, like the paper, played one battle
|
|
41
|
+
per dataset on the mean error over its folds. That makes each dataset winner-take-all, which stretches the scale: on
|
|
42
|
+
the then 17 models, Kumo-Tabular was at 2670 rather than 2347, with the same ranking.
|
|
30
43
|
|
|
31
44
|
| # | Model | Elo, 80 datasets | 95% CI | Text, 40 | Images, 40 | Datasets |
|
|
32
45
|
|---|---|---|---|---|---|---|
|
|
33
|
-
| 1 |
|
|
34
|
-
| 2 | Kumo-Tabular-
|
|
35
|
-
| 3 |
|
|
36
|
-
| 4 |
|
|
37
|
-
| 5 |
|
|
38
|
-
| 6 |
|
|
39
|
-
| 7 | TabPFN-
|
|
40
|
-
| 8 |
|
|
41
|
-
| 9 |
|
|
42
|
-
| 10 |
|
|
43
|
-
| 11 |
|
|
44
|
-
| 12 |
|
|
45
|
-
| 13 |
|
|
46
|
-
| 14 |
|
|
47
|
-
| 15 |
|
|
46
|
+
| 1 | Kumo-Tabular | 2279 | +108 / −84 | 2310 | 2334 | 80 |
|
|
47
|
+
| 2 | Kumo-Tabular-Medium | 2126 | +86 / −72 | 2141 | 2190 | 80 |
|
|
48
|
+
| 3 | TabFM | 2057 | +86 / −63 | 2044 | 2141 | 80 |
|
|
49
|
+
| 4 | TabPFN-3.5 | 1991 | +78 / −69 | 1913 | 2132 | 80 |
|
|
50
|
+
| 5 | Kumo-Tabular-Small | 1937 | +56 / −46 | 1915 | 2026 | 80 |
|
|
51
|
+
| 6 | Causilo | 1878 | +51 / −43 | 1826 | 1993 | 80 |
|
|
52
|
+
| 7 | TabPFN-3.5-Fast | 1861 | +61 / −54 | 1782 | 2002 | 80 |
|
|
53
|
+
| 8 | TabICLv2 | 1648 | +37 / −35 | 1564 | 1792 | 80 |
|
|
54
|
+
| 9 | TabPFN-3 | 1586 | +37 / −36 | 1488 | 1747 | 80 |
|
|
55
|
+
| 10 | TabPFN-v2p5 | 1586 | +34 / −38 | 1565 | 1660 | 77 |
|
|
56
|
+
| 11 | TabPFN-v2 | 1373 | +45 / −50 | 1291 | 1505 | 77 |
|
|
57
|
+
| 12 | ConTextTab | 1339 | +56 / −63 | 1270 | 1455 | 80 |
|
|
58
|
+
| 13 | TabM | 1328 | +41 / −45 | 1311 | 1378 | 80 |
|
|
59
|
+
| 14 | CatBoost | 1268 | +42 / −48 | 1274 | 1283 | 80 |
|
|
60
|
+
| 15 | TabDPT | 1231 | +62 / −65 | 1106 | 1400 | 80 |
|
|
61
|
+
| 16 | TabSTAR | 1201 | +68 / −71 | 1241 | 1165 | 80 |
|
|
62
|
+
| 17 | RealMLP | 1093 | +57 / −69 | 1100 | 1090 | 80 |
|
|
63
|
+
| 18 | LightGBM | 1052 | +64 / −73 | 1081 | 1016 | 80 |
|
|
64
|
+
| 19 | RandomForest | 1000 | +80 / −87 | 1000 | 1000 | 80 |
|
|
65
|
+
| 20 | XGBoost | 997 | +69 / −78 | 1029 | 953 | 80 |
|
|
48
66
|
|
|
49
67
|
Each model's runs are in `multabench/leaderboard/models/<model>.csv`, one row per (dataset, fold), failed
|
|
50
|
-
runs included. `python -m multabench.leaderboard.elo` prints the table, with win rate, average rank
|
|
51
|
-
|
|
68
|
+
runs included. `python -m multabench.leaderboard.elo` prints the table, with win rate, average rank, normalized
|
|
69
|
+
error and the number of folds each model is best on; `--modalities text` or `--modalities image,image-text` restricts
|
|
70
|
+
it to those datasets.
|
|
52
71
|
|
|
53
72
|
Leaderboard runs pass `--official`, which also requires a clean checkout of a commit on `master`, so the `git`
|
|
54
73
|
column of every row names code anyone can check out. The image runs of the first 11 models are the one exception: they
|
|
@@ -63,11 +82,18 @@ pip install multabench
|
|
|
63
82
|
```
|
|
64
83
|
|
|
65
84
|
```python
|
|
66
|
-
from multabench import Size, load_embeddings, load_split
|
|
85
|
+
from multabench import Size, list_datasets, load_embeddings, load_published_results, load_split, score
|
|
86
|
+
datasets = list_datasets(modality="text", task="regression", tier="core") # DatasetInfo: id, hf_repo, modality, ...
|
|
67
87
|
split = load_split("MUL_IMAGE_PETFINDER", fold=0, size=Size.TEN_K) # split.x_train, split.y_train, split.x_test, ...
|
|
68
88
|
embeddings = load_embeddings("MUL_TEXT_WINE_REVIEW", fold=0, size=Size.TEN_K, encoder="e5-small")
|
|
89
|
+
result = score(split.task_type, y_true=y_test, y_pred=proba) # metric, test_score, test_error, as the leaderboard
|
|
90
|
+
published = load_published_results() # every leaderboard run, in the columns of multabench/leaderboard/models/
|
|
69
91
|
```
|
|
70
92
|
|
|
93
|
+
Each package version reads every dataset and embeddings repo at the commit pinned in
|
|
94
|
+
`multabench/datasets/revisions.json`, and ships the leaderboard's runs as of its release. After uploading to one of
|
|
95
|
+
those repos, `python -m multabench.datasets.revisions` pins them all to their current `main`.
|
|
96
|
+
|
|
71
97
|
To run the benchmark:
|
|
72
98
|
|
|
73
99
|
```bash
|
|
@@ -79,8 +105,11 @@ python benchmark.py --model light --dataset_name MUL_IMAGE_PETFINDER --fold 0 --
|
|
|
79
105
|
|
|
80
106
|
`pyproject.toml` lists the core dependencies, a `bench` extra for `benchmark.py` and one extra per model package, so
|
|
81
107
|
`pip install -e ".[bench,catboost]"` installs what one model needs; each wrapper imports its package only when it runs.
|
|
82
|
-
ConTextTab and
|
|
83
|
-
`init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those
|
|
108
|
+
ConTextTab, Kumo and TabFM are pinned to a git commit, which PyPI doesn't allow in extras, so `requirements-git.txt`
|
|
109
|
+
lists them. `init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those
|
|
110
|
+
three. A model whose requirements conflict with that lock declares its own venv in its wrapper (`ENV`), locked in
|
|
111
|
+
`requirements-<ENV>.txt` and set up with `source init.sh <ENV>`: Causilo, which needs a newer torch, runs in
|
|
112
|
+
`.venv-causilo`. `sweep.py` writes each model's venv python into its jobs, so one sweep covers every model.
|
|
84
113
|
|
|
85
114
|
`benchmark.py` is the single entry point: it evaluates one model on one dataset fold and writes the
|
|
86
115
|
result to `runs/<model>_<dataset>_<text_encoder>_<image_encoder>_<size>_<fold>.json` (`--output_dir` to change), with
|
|
@@ -212,7 +241,7 @@ MulTaBench is not an official TabArena leaderboard.
|
|
|
212
241
|
| | TabArena | MulTaBench | Status |
|
|
213
242
|
|---|---|---|---|
|
|
214
243
|
| Metrics | ROC AUC (binary), log loss (multiclass), RMSE (regression), via AutoGluon's scorers | Same | ✅ |
|
|
215
|
-
| Leaderboard | Elo via `bencheval`, RandomForest (default) anchored at 1000, missing results imputed with RandomForest and flagged, bootstrapped confidence intervals | Elo
|
|
244
|
+
| Leaderboard | Elo via `bencheval`, one battle per task fold, RandomForest (default) anchored at 1000, missing results imputed with RandomForest and flagged, bootstrapped confidence intervals | Same Elo, one battle per dataset fold; missing results are left out, neither imputed nor dropping the model, so a model is ranked on the datasets it ran on, which its Datasets column shows | |
|
|
216
245
|
| Splits | Fixed and part of the task definition | Same: a seeded function of the data | ✅ |
|
|
217
246
|
| Group and time structure | Audited per dataset; group-aware or forward-in-time splits where needed | Not audited: every dataset is split IID. Some datasets may have group or time structure, in which case IID splits can overestimate performance | |
|
|
218
247
|
| Runtime | Train and inference time per 1K rows | Same, with the time spent embedding text and images reported separately | ✅ |
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from multabench.benchmark.load import load_embeddings, load_split
|
|
2
|
+
from multabench.benchmark.metrics import Score, score
|
|
3
|
+
from multabench.benchmark.splits import Size
|
|
4
|
+
from multabench.datasets.all_datasets import Modality
|
|
5
|
+
from multabench.datasets.info import DatasetInfo, Tier, list_datasets
|
|
6
|
+
from multabench.datasets.objects import SupervisedTask
|
|
7
|
+
from multabench.leaderboard.published import load_published_results
|
|
@@ -16,7 +16,8 @@ from multabench.baselines.preprocessing.image_embeddings import (
|
|
|
16
16
|
)
|
|
17
17
|
from multabench.baselines.training.metrics import calculate_metric
|
|
18
18
|
from multabench.datasets.all_datasets import MulTaBenchDatasetID
|
|
19
|
-
from multabench.baselines.preprocessing.categorical import fit_categorical_encoders,
|
|
19
|
+
from multabench.baselines.preprocessing.categorical import (fit_categorical_encoders, fit_category_dtypes,
|
|
20
|
+
transform_categorical_features)
|
|
20
21
|
from multabench.baselines.preprocessing.numerical import fit_numerical_median, transform_numerical_features
|
|
21
22
|
from multabench.baselines.preprocessing.target import transform_preprocess_y, fit_preprocess_y
|
|
22
23
|
from multabench.dino.constants import DINOV3_SMALL
|
|
@@ -35,7 +36,11 @@ class TabularModel:
|
|
|
35
36
|
USE_CATEGORICAL_ENCODING: bool
|
|
36
37
|
USE_TEXT_EMBEDDINGS: bool
|
|
37
38
|
USE_TARGET_ENCODER: bool
|
|
39
|
+
# Categorical columns as pandas' category dtype, with the training values as categories, for models that read it.
|
|
40
|
+
USE_CATEGORY_DTYPE = False
|
|
38
41
|
NEEDS_GPU = True
|
|
42
|
+
# A model whose requirements conflict with the shared .venv runs in its own, .venv-<ENV>, from requirements-<ENV>.txt.
|
|
43
|
+
ENV: Optional[str] = None
|
|
39
44
|
|
|
40
45
|
def __init__(self, problem_type: SupervisedTask, device: torch.device,
|
|
41
46
|
dataset: MulTaBenchDatasetID | None = None, verbose: bool = False, image_folder: str | None = None,
|
|
@@ -117,6 +122,8 @@ class TabularModel:
|
|
|
117
122
|
)
|
|
118
123
|
self._train_embeddings.update(text_embeddings)
|
|
119
124
|
self.vprint(f"📝 Detected {len(self.text_transformers)} text features: {sorted(self.text_transformers)}")
|
|
125
|
+
if self.USE_CATEGORY_DTYPE:
|
|
126
|
+
self.category_dtypes = fit_category_dtypes(x=x_train, categorical_features=self.categorical_features)
|
|
120
127
|
self.fit_internal_preprocessor(x=x_train, y=y_train)
|
|
121
128
|
|
|
122
129
|
def transform_preprocessor(self, x: DataFrame, y: Optional[Series],
|
|
@@ -149,6 +156,8 @@ class TabularModel:
|
|
|
149
156
|
device=self.device,
|
|
150
157
|
train_embeddings=train_embeddings,
|
|
151
158
|
)
|
|
159
|
+
if self.USE_CATEGORY_DTYPE:
|
|
160
|
+
x = x.astype(self.category_dtypes)
|
|
152
161
|
return self.transform_internal_preprocessor(x=x, y=y)
|
|
153
162
|
|
|
154
163
|
def fit_internal_preprocessor(self, x: DataFrame, y: Series) -> Tuple[DataFrame, Series]:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pandas import DataFrame, Series
|
|
2
|
+
|
|
3
|
+
from multabench.baselines.abstract_model import TabularModel
|
|
4
|
+
from multabench.constants import SEED
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Causilo(TabularModel):
|
|
8
|
+
"""Nums AI's Causilo at TabArena's default; it needs torch>=2.13, so it runs in its own venv."""
|
|
9
|
+
|
|
10
|
+
MODEL_NAME = "Causilo"
|
|
11
|
+
SHORT_NAME = "causilo"
|
|
12
|
+
ENV = "causilo"
|
|
13
|
+
USE_VAL_SPLIT = False
|
|
14
|
+
USE_MEDIAN_FILLING = False
|
|
15
|
+
USE_CATEGORICAL_ENCODING = False
|
|
16
|
+
USE_TEXT_EMBEDDINGS = True
|
|
17
|
+
USE_TARGET_ENCODER = True
|
|
18
|
+
USE_CATEGORY_DTYPE = True
|
|
19
|
+
|
|
20
|
+
def initialize_model(self):
|
|
21
|
+
from causilo import CausiloClassifier, CausiloRegressor
|
|
22
|
+
cls = CausiloClassifier if self.is_cls else CausiloRegressor
|
|
23
|
+
return cls(n_estimators=8, random_state=SEED, device=str(self.device))
|
|
24
|
+
|
|
25
|
+
def fit_model(self, x_train: DataFrame, y_train: Series, x_val: DataFrame, y_val: Series):
|
|
26
|
+
self.model_.fit(x_train, y_train)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pandas import DataFrame, Series
|
|
3
|
+
|
|
4
|
+
from multabench.baselines.abstract_model import TabularModel
|
|
5
|
+
from multabench.constants import SEED
|
|
6
|
+
|
|
7
|
+
# TabArena reads the weights at main; this is main as of TabArena's runs and ours.
|
|
8
|
+
REVISION = "093ad1c2613e1e95d26df8610451d2e02ed53cbe"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _float32(x: DataFrame) -> np.ndarray:
|
|
12
|
+
values = x.to_numpy(dtype=np.float32)
|
|
13
|
+
values[~np.isfinite(values)] = np.nan
|
|
14
|
+
return values
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EXAONETabular(TabularModel):
|
|
18
|
+
"""LG's EXAONE Tabular at TabArena's default: 8 ensemble members in float16, in its own venv
|
|
19
|
+
(requirements-exaone.in)."""
|
|
20
|
+
|
|
21
|
+
MODEL_NAME = "EXAONE-Tabular"
|
|
22
|
+
SHORT_NAME = "exaone"
|
|
23
|
+
ENV = "exaone"
|
|
24
|
+
USE_VAL_SPLIT = False
|
|
25
|
+
USE_MEDIAN_FILLING = False
|
|
26
|
+
USE_CATEGORICAL_ENCODING = True
|
|
27
|
+
USE_TEXT_EMBEDDINGS = True
|
|
28
|
+
USE_TARGET_ENCODER = True
|
|
29
|
+
|
|
30
|
+
def initialize_model(self) -> None:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
def fit_model(self, x_train: DataFrame, y_train: Series, x_val: DataFrame, y_val: Series):
|
|
34
|
+
from exaonetabular import EXAONETabularClassifier, EXAONETabularRegressor
|
|
35
|
+
model_cls = EXAONETabularClassifier if self.is_cls else EXAONETabularRegressor
|
|
36
|
+
self.model_ = model_cls.from_pretrained(device=str(self.device), revision=REVISION, ensemble_count=8,
|
|
37
|
+
compute_dtype="float16", seed=SEED)
|
|
38
|
+
self.model_.fit(_float32(x_train), y_train.to_numpy())
|
|
39
|
+
|
|
40
|
+
def predict_from_processed(self, x: DataFrame) -> np.ndarray:
|
|
41
|
+
return super().predict_from_processed(_float32(x))
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import torch
|
|
3
5
|
from pandas import DataFrame, Series
|
|
@@ -24,6 +26,7 @@ class KumoEstimator:
|
|
|
24
26
|
|
|
25
27
|
def fit(self, x: DataFrame, y: Series):
|
|
26
28
|
import sdm
|
|
29
|
+
from sdm.cache import Cache
|
|
27
30
|
self.model = sdm.models.KumoTabular(task=self.task, size=self.size, device=self.device)
|
|
28
31
|
self.stypes = sdm.infer_stypes(x, _low_cardinality="infer")
|
|
29
32
|
x_context = sdm.TableTensor.from_pandas(df=x, stypes=self.stypes, device=self.device)
|
|
@@ -34,7 +37,10 @@ class KumoEstimator:
|
|
|
34
37
|
# NVIDIA's adapter fits one ensemble member at a time above these sizes, to bound GPU memory.
|
|
35
38
|
batch_size = 1 if rows > 2_000 or rows * cols >= 50_000 else self.num_estimators
|
|
36
39
|
generator = torch.Generator(self.device).manual_seed(SEED)
|
|
37
|
-
|
|
40
|
+
# sdm copies each estimator's fitted cache to pinned CPU memory, up to 56 GB on Hearthstone, past the 32 GB of
|
|
41
|
+
# RAM; Cache._apply_tensor does only that copy in fit, so skipping it keeps the caches on the GPU, where predict
|
|
42
|
+
# reads them anyway.
|
|
43
|
+
with torch.inference_mode(), self._inference(), patch.object(Cache, "_apply_tensor", lambda c, fn: c):
|
|
38
44
|
self.model.fit(x=x_context, y=y_context, num_estimators=self.num_estimators,
|
|
39
45
|
estimator_batch_size=batch_size, generator=generator)
|
|
40
46
|
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Set, Dict
|
|
2
2
|
|
|
3
|
-
from pandas import Series, DataFrame
|
|
3
|
+
from pandas import CategoricalDtype, Series, DataFrame
|
|
4
4
|
from sklearn.preprocessing import LabelEncoder
|
|
5
5
|
|
|
6
6
|
from multabench.baselines.preprocessing.nulls import MISSING_VALUE
|
|
@@ -21,4 +21,8 @@ def transform_categorical_features(x: DataFrame, categorical_encoders: Dict[str,
|
|
|
21
21
|
s = x[col].apply(lambda v: v if v in encoder.classes_ else MISSING_VALUE)
|
|
22
22
|
s = encoder.transform(s).astype(int)
|
|
23
23
|
x[col] = s
|
|
24
|
-
return x
|
|
24
|
+
return x
|
|
25
|
+
|
|
26
|
+
def fit_category_dtypes(x: DataFrame, categorical_features: Set[str]) -> Dict[str, CategoricalDtype]:
|
|
27
|
+
"""The train values of each categorical column, for models that read pandas' category dtype; unseen ones are NaN."""
|
|
28
|
+
return {col: CategoricalDtype(sorted(x[col].unique())) for col in categorical_features}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import torch
|
|
3
|
+
from pandas import DataFrame, Series
|
|
4
|
+
|
|
5
|
+
from multabench.baselines.abstract_model import TabularModel
|
|
6
|
+
from multabench.constants import SEED
|
|
7
|
+
|
|
8
|
+
# The weights TabArena benchmarked; tabdpt 1.3 can't load the older TabDPT's, nor 1.1 these.
|
|
9
|
+
HF_REPO = "Layer6/TabDPT"
|
|
10
|
+
WEIGHTS = "tabdpt1_3.safetensors"
|
|
11
|
+
REVISION = "a5ca6e01c0fa09ec68c73e958e5199d1932abb3a"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TabDPT13(TabularModel):
|
|
15
|
+
"""TabDPT 1.3 at TabArena's default; tabdpt 1.3 conflicts with the 1.1 TabDPT needs, so it has its own venv."""
|
|
16
|
+
|
|
17
|
+
MODEL_NAME = "TabDPT-1.3"
|
|
18
|
+
SHORT_NAME = "dpt13"
|
|
19
|
+
ENV = "tabdpt13"
|
|
20
|
+
USE_VAL_SPLIT = False
|
|
21
|
+
USE_MEDIAN_FILLING = False
|
|
22
|
+
USE_CATEGORICAL_ENCODING = True
|
|
23
|
+
USE_TEXT_EMBEDDINGS = True
|
|
24
|
+
USE_TARGET_ENCODER = True
|
|
25
|
+
|
|
26
|
+
def initialize_model(self) -> None:
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
def fit_model(self, x_train: DataFrame, y_train: Series, x_val: DataFrame, y_val: Series):
|
|
30
|
+
from huggingface_hub import hf_hub_download
|
|
31
|
+
from tabdpt import TabDPTClassifier, TabDPTRegressor
|
|
32
|
+
model_cls = TabDPTClassifier if self.is_cls else TabDPTRegressor
|
|
33
|
+
self.model_ = model_cls(device=str(self.device), use_flash=True, compile=False, verbose=False,
|
|
34
|
+
model_weight_path=hf_hub_download(HF_REPO, WEIGHTS, revision=REVISION),
|
|
35
|
+
normalizer="standard", missing_indicators=False, clip_sigma=8,
|
|
36
|
+
feature_reduction="pca", context_reduction="subsample", faiss_metric="l2")
|
|
37
|
+
# tabdpt reduces wide tables with torch.pca_lowrank, which draws from torch's global generator.
|
|
38
|
+
torch.manual_seed(SEED)
|
|
39
|
+
self.model_.fit(x_train.to_numpy(), y_train.to_numpy())
|
|
40
|
+
|
|
41
|
+
def predict_from_processed(self, x: DataFrame) -> np.ndarray:
|
|
42
|
+
if not self.is_cls:
|
|
43
|
+
return self.model_.predict(x.to_numpy(), seed=SEED)
|
|
44
|
+
probs = self.model_.ensemble_predict_proba(x.to_numpy(), seed=SEED)
|
|
45
|
+
return probs[:, 1] if self.d_output == 2 else probs
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pandas import DataFrame, Series
|
|
3
|
+
|
|
4
|
+
from multabench.baselines.abstract_model import TabularModel
|
|
5
|
+
from multabench.constants import SEED
|
|
6
|
+
|
|
7
|
+
HEAD_CLASSES = 10
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TabFMCodeRow:
|
|
11
|
+
"""ManyClassClassifier's base estimator, as in TabArena: a TabFM classifier per codebook row, sharing the network
|
|
12
|
+
and rebuilding the frame from the arrays the codebook passes."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, network, dtypes: Series):
|
|
15
|
+
self.network = network
|
|
16
|
+
self.dtypes = dtypes
|
|
17
|
+
|
|
18
|
+
def __sklearn_clone__(self):
|
|
19
|
+
return TabFMCodeRow(network=self.network, dtypes=self.dtypes)
|
|
20
|
+
|
|
21
|
+
def _frame(self, x) -> DataFrame:
|
|
22
|
+
return DataFrame(x, columns=self.dtypes.index).astype(self.dtypes)
|
|
23
|
+
|
|
24
|
+
def fit(self, x, y):
|
|
25
|
+
from tabfm import TabFMClassifier
|
|
26
|
+
self.model = TabFMClassifier(model=self.network, random_state=SEED).fit(self._frame(x), np.asarray(y))
|
|
27
|
+
self.classes_ = self.model.classes_
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
def predict_proba(self, x) -> np.ndarray:
|
|
31
|
+
return self.model.predict_proba(self._frame(x))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TabFM(TabularModel):
|
|
35
|
+
"""Google's TabFM at TabArena's default, with tabpfn-extensions' output coding above its 10-class head."""
|
|
36
|
+
|
|
37
|
+
MODEL_NAME = "TabFM"
|
|
38
|
+
SHORT_NAME = "tabfm"
|
|
39
|
+
USE_VAL_SPLIT = False
|
|
40
|
+
USE_MEDIAN_FILLING = False
|
|
41
|
+
USE_CATEGORICAL_ENCODING = False
|
|
42
|
+
USE_TEXT_EMBEDDINGS = True
|
|
43
|
+
USE_TARGET_ENCODER = True
|
|
44
|
+
USE_CATEGORY_DTYPE = True
|
|
45
|
+
|
|
46
|
+
def initialize_model(self) -> None:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def fit_model(self, x_train: DataFrame, y_train: Series, x_val: DataFrame, y_val: Series):
|
|
50
|
+
from tabfm import TabFMClassifier, TabFMRegressor, tabfm_v1_0_0_pytorch
|
|
51
|
+
network = tabfm_v1_0_0_pytorch.load(model_type="classification" if self.is_cls else "regression",
|
|
52
|
+
device=str(self.device))
|
|
53
|
+
if self.d_output > HEAD_CLASSES:
|
|
54
|
+
from tabpfn_extensions.many_class import ManyClassClassifier
|
|
55
|
+
self.model_ = ManyClassClassifier(estimator=TabFMCodeRow(network=network, dtypes=x_train.dtypes),
|
|
56
|
+
alphabet_size=HEAD_CLASSES, random_state=SEED)
|
|
57
|
+
else:
|
|
58
|
+
cls = TabFMClassifier if self.is_cls else TabFMRegressor
|
|
59
|
+
self.model_ = cls(model=network, random_state=SEED)
|
|
60
|
+
self.model_.fit(x_train, y_train)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from pandas import DataFrame, Series
|
|
2
|
+
|
|
3
|
+
from multabench.baselines.abstract_model import TabularModel
|
|
4
|
+
from multabench.constants import SEED
|
|
5
|
+
from multabench.utils.devices import CPU_CORES
|
|
6
|
+
|
|
7
|
+
# The Hub revision of TabArena's results; the regression checkpoint was replaced after it.
|
|
8
|
+
HF_REPO = "occams/Xiaomi-TabLDM"
|
|
9
|
+
REVISION = "0e10427273bc862fb60fe7df84c89e5298e16cc7"
|
|
10
|
+
# The classifier defaults of the commit TabArena's results ran, which upstream changed since.
|
|
11
|
+
CLASSIFIER_DEFAULTS = {"n_estimators": 8, "feat_shuffle_method": "latin", "cat_random_encode": False, "use_amp": "auto"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class XiaomiTabLDM(TabularModel):
|
|
15
|
+
"""Xiaomi's TabLDM at TabArena's default: its plain ensemble, without the candidate enhancement, whose rank
|
|
16
|
+
normalizer fails when a predict batch has more rows than the training set."""
|
|
17
|
+
|
|
18
|
+
MODEL_NAME = "Xiaomi-TabLDM"
|
|
19
|
+
SHORT_NAME = "tabldm"
|
|
20
|
+
USE_VAL_SPLIT = False
|
|
21
|
+
USE_MEDIAN_FILLING = False
|
|
22
|
+
USE_CATEGORICAL_ENCODING = False
|
|
23
|
+
USE_TEXT_EMBEDDINGS = True
|
|
24
|
+
USE_TARGET_ENCODER = True
|
|
25
|
+
USE_CATEGORY_DTYPE = True
|
|
26
|
+
|
|
27
|
+
def initialize_model(self) -> None:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def fit_model(self, x_train: DataFrame, y_train: Series, x_val: DataFrame, y_val: Series):
|
|
31
|
+
from huggingface_hub import hf_hub_download
|
|
32
|
+
from tabldm import TabLDMClassifier, TabLDMRegressor
|
|
33
|
+
checkpoint = "checkpoints/clf_default.ckpt" if self.is_cls else "checkpoints/reg_default.ckpt"
|
|
34
|
+
model_cls, defaults = (TabLDMClassifier, CLASSIFIER_DEFAULTS) if self.is_cls else (TabLDMRegressor, {})
|
|
35
|
+
self.model_ = model_cls(device=str(self.device), n_jobs=CPU_CORES, random_state=SEED, enhance_candidates=False,
|
|
36
|
+
model_path=hf_hub_download(HF_REPO, checkpoint, revision=REVISION), **defaults)
|
|
37
|
+
self.model_.fit(x_train, y_train)
|