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.
Files changed (145) hide show
  1. {multabench-0.0.1 → multabench-0.0.2}/PKG-INFO +63 -26
  2. {multabench-0.0.1 → multabench-0.0.2}/README.md +53 -24
  3. multabench-0.0.2/multabench/__init__.py +7 -0
  4. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/abstract_model.py +10 -1
  5. multabench-0.0.2/multabench/baselines/causilo.py +26 -0
  6. multabench-0.0.2/multabench/baselines/exaone_tabular.py +41 -0
  7. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/kumo_tabular.py +7 -1
  8. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/categorical.py +6 -2
  9. multabench-0.0.2/multabench/baselines/tabdpt13.py +45 -0
  10. multabench-0.0.2/multabench/baselines/tabfm.py +60 -0
  11. multabench-0.0.2/multabench/baselines/tabldm.py +37 -0
  12. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabstar_v1.py +44 -4
  13. multabench-0.0.2/multabench/baselines/tabstar_v1p1.py +14 -0
  14. multabench-0.0.2/multabench/baselines/training/metrics.py +24 -0
  15. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/load.py +4 -2
  16. multabench-0.0.2/multabench/benchmark/metrics.py +37 -0
  17. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/sweep.py +11 -3
  18. multabench-0.0.2/multabench/datasets/info.py +41 -0
  19. multabench-0.0.2/multabench/datasets/revisions.json +84 -0
  20. multabench-0.0.2/multabench/datasets/revisions.py +41 -0
  21. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/hub.py +4 -2
  22. {multabench-0.0.1 → multabench-0.0.2}/multabench/finetune/utils.py +1 -1
  23. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/elo.py +32 -45
  24. multabench-0.0.2/multabench/leaderboard/export.py +86 -0
  25. multabench-0.0.2/multabench/leaderboard/models/CatBoost.csv +481 -0
  26. multabench-0.0.2/multabench/leaderboard/models/Causilo.csv +481 -0
  27. multabench-0.0.2/multabench/leaderboard/models/ConTextTab.csv +481 -0
  28. multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular-Medium.csv +481 -0
  29. multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular-Small.csv +481 -0
  30. multabench-0.0.2/multabench/leaderboard/models/Kumo-Tabular.csv +481 -0
  31. multabench-0.0.2/multabench/leaderboard/models/LightGBM.csv +481 -0
  32. multabench-0.0.2/multabench/leaderboard/models/RandomForest.csv +481 -0
  33. multabench-0.0.2/multabench/leaderboard/models/RealMLP.csv +481 -0
  34. multabench-0.0.2/multabench/leaderboard/models/TabDPT.csv +481 -0
  35. multabench-0.0.2/multabench/leaderboard/models/TabFM.csv +481 -0
  36. multabench-0.0.2/multabench/leaderboard/models/TabICLv2.csv +481 -0
  37. multabench-0.0.2/multabench/leaderboard/models/TabM.csv +481 -0
  38. multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.5-Fast.csv +481 -0
  39. multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.5.csv +481 -0
  40. multabench-0.0.2/multabench/leaderboard/models/TabPFN-3.csv +481 -0
  41. multabench-0.0.2/multabench/leaderboard/models/TabPFN-v2.csv +481 -0
  42. multabench-0.0.2/multabench/leaderboard/models/TabPFN-v2p5.csv +481 -0
  43. multabench-0.0.2/multabench/leaderboard/models/TabSTAR.csv +481 -0
  44. multabench-0.0.2/multabench/leaderboard/models/XGBoost.csv +481 -0
  45. multabench-0.0.2/multabench/leaderboard/published.py +13 -0
  46. {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/PKG-INFO +63 -26
  47. {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/SOURCES.txt +36 -0
  48. {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/requires.txt +13 -1
  49. {multabench-0.0.1 → multabench-0.0.2}/pyproject.toml +15 -2
  50. {multabench-0.0.1 → multabench-0.0.2}/tests/test_core_imports.py +2 -1
  51. multabench-0.0.2/tests/test_datasets_info.py +36 -0
  52. {multabench-0.0.1 → multabench-0.0.2}/tests/test_elo.py +25 -6
  53. {multabench-0.0.1 → multabench-0.0.2}/tests/test_metrics.py +24 -2
  54. multabench-0.0.2/tests/test_published_results.py +20 -0
  55. {multabench-0.0.1 → multabench-0.0.2}/tests/test_registry.py +2 -2
  56. multabench-0.0.2/tests/test_space_data.py +28 -0
  57. {multabench-0.0.1 → multabench-0.0.2}/tests/test_sweep.py +18 -1
  58. multabench-0.0.2/tests/test_tabstar_mapping.py +14 -0
  59. multabench-0.0.1/multabench/__init__.py +0 -2
  60. multabench-0.0.1/multabench/baselines/training/metrics.py +0 -37
  61. {multabench-0.0.1 → multabench-0.0.2}/LICENSE +0 -0
  62. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/__init__.py +0 -0
  63. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/autogluon_mm.py +0 -0
  64. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/benchmarks/__init__.py +0 -0
  65. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/benchmarks/evaluate.py +0 -0
  66. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/catboost.py +0 -0
  67. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/contexttab.py +0 -0
  68. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/lgbm.py +0 -0
  69. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/__init__.py +0 -0
  70. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/dates.py +0 -0
  71. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/feature_types.py +0 -0
  72. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/image_embeddings.py +0 -0
  73. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/nulls.py +0 -0
  74. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/numerical.py +0 -0
  75. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/target.py +0 -0
  76. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/text_embeddings.py +0 -0
  77. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/preprocessing/validation.py +0 -0
  78. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/random_forest.py +0 -0
  79. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/realmlp.py +0 -0
  80. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabdpt.py +0 -0
  81. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabicl_v2.py +0 -0
  82. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabm.py +0 -0
  83. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/tabpfn.py +0 -0
  84. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/training/__init__.py +0 -0
  85. {multabench-0.0.1 → multabench-0.0.2}/multabench/baselines/xgboost.py +0 -0
  86. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/__init__.py +0 -0
  87. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/collect.py +0 -0
  88. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/runs.py +0 -0
  89. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/splits.py +0 -0
  90. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/__init__.py +0 -0
  91. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/constants.py +0 -0
  92. {multabench-0.0.1 → multabench-0.0.2}/multabench/benchmark/utils/curation.py +0 -0
  93. {multabench-0.0.1 → multabench-0.0.2}/multabench/constants.py +0 -0
  94. {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/__init__.py +0 -0
  95. {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/all_datasets.py +0 -0
  96. {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/hub.py +0 -0
  97. {multabench-0.0.1 → multabench-0.0.2}/multabench/datasets/objects.py +0 -0
  98. {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/__init__.py +0 -0
  99. {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/constants.py +0 -0
  100. {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/dino_finetune.py +0 -0
  101. {multabench-0.0.1 → multabench-0.0.2}/multabench/dino/image_loading.py +0 -0
  102. {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/__init__.py +0 -0
  103. {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/constants.py +0 -0
  104. {multabench-0.0.1 → multabench-0.0.2}/multabench/e5/e5_finetune.py +0 -0
  105. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/__init__.py +0 -0
  106. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/compute.py +0 -0
  107. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/embed.py +0 -0
  108. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/embed_sweep.py +0 -0
  109. {multabench-0.0.1 → multabench-0.0.2}/multabench/embeddings/jobs.py +0 -0
  110. {multabench-0.0.1 → multabench-0.0.2}/multabench/finetune/train_args.py +0 -0
  111. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/__init__.py +0 -0
  112. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/__init__.py +0 -0
  113. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/keys.py +0 -0
  114. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/data/loading.py +0 -0
  115. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/large_results.py +0 -0
  116. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/paper_results.py +0 -0
  117. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/plots.py +0 -0
  118. {multabench-0.0.1 → multabench-0.0.2}/multabench/leaderboard/utils.py +0 -0
  119. {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/__init__.py +0 -0
  120. {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/discretize.py +0 -0
  121. {multabench-0.0.1 → multabench-0.0.2}/multabench/preprocessing/splits.py +0 -0
  122. {multabench-0.0.1 → multabench-0.0.2}/multabench/result_keys.py +0 -0
  123. {multabench-0.0.1 → multabench-0.0.2}/multabench/scripts/do_leaderboard.py +0 -0
  124. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/__init__.py +0 -0
  125. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/devices.py +0 -0
  126. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/encoders.py +0 -0
  127. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/hardware.py +0 -0
  128. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/logging.py +0 -0
  129. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/profiling.py +0 -0
  130. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/timing.py +0 -0
  131. {multabench-0.0.1 → multabench-0.0.2}/multabench/utils/warnings.py +0 -0
  132. {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/dependency_links.txt +0 -0
  133. {multabench-0.0.1 → multabench-0.0.2}/multabench.egg-info/top_level.txt +0 -0
  134. {multabench-0.0.1 → multabench-0.0.2}/setup.cfg +0 -0
  135. {multabench-0.0.1 → multabench-0.0.2}/tests/test_collect.py +0 -0
  136. {multabench-0.0.1 → multabench-0.0.2}/tests/test_embeddings.py +0 -0
  137. {multabench-0.0.1 → multabench-0.0.2}/tests/test_feature_types.py +0 -0
  138. {multabench-0.0.1 → multabench-0.0.2}/tests/test_hardware.py +0 -0
  139. {multabench-0.0.1 → multabench-0.0.2}/tests/test_hub.py +0 -0
  140. {multabench-0.0.1 → multabench-0.0.2}/tests/test_leaderboard_results.py +0 -0
  141. {multabench-0.0.1 → multabench-0.0.2}/tests/test_official_commit.py +0 -0
  142. {multabench-0.0.1 → multabench-0.0.2}/tests/test_profiling.py +0 -0
  143. {multabench-0.0.1 → multabench-0.0.2}/tests/test_splits.py +0 -0
  144. {multabench-0.0.1 → multabench-0.0.2}/tests/test_text_embeddings.py +0 -0
  145. {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.1
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.15; extra == "tabstar"
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 reads raw text itself). Elo is fit on one battle per dataset and model pair, with RandomForest
81
- at 1000 and 95% intervals from bootstrapping the datasets; a model with no result on a dataset has no
82
- battles there. TabPFN-v2 and v2.5 take at most 10 classes, which leaves out 3 of the 80 datasets.
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 | TabPFN-3.5 | 2221 | +230 / −176 | 2080 | 2458 | 80 |
87
- | 2 | Kumo-Tabular-Small | 2139 | +190 / −151 | 2094 | 2268 | 80 |
88
- | 3 | TabPFN-3.5-Fast | 2010 | +195 / −149 | 1906 | 2199 | 80 |
89
- | 4 | TabICLv2 | 1745 | +137 / −115 | 1637 | 1941 | 80 |
90
- | 5 | TabPFN-3 | 1666 | +145 / −117 | 1554 | 1866 | 80 |
91
- | 6 | TabPFN-v2p5 | 1661 | +136 / −114 | 1647 | 1743 | 77 |
92
- | 7 | TabPFN-v2 | 1407 | +117 / −97 | 1322 | 1559 | 77 |
93
- | 8 | TabM | 1366 | +98 / −89 | 1348 | 1432 | 80 |
94
- | 9 | ConTextTab | 1353 | +113 / −102 | 1279 | 1490 | 80 |
95
- | 10 | CatBoost | 1298 | +66 / −55 | 1307 | 1321 | 80 |
96
- | 11 | TabDPT | 1251 | +112 / −93 | 1116 | 1455 | 80 |
97
- | 12 | RealMLP | 1082 | +81 / −71 | 1103 | 1059 | 80 |
98
- | 13 | LightGBM | 1058 | +63 / −61 | 1093 | 1013 | 80 |
99
- | 14 | RandomForest | 1000 | anchor | 1000 | 1000 | 80 |
100
- | 15 | XGBoost | 988 | +67 / −65 | 1024 | 936 | 80 |
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 and
104
- normalized error; `--modalities text` or `--modalities image,image-text` restricts it to those datasets.
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 Kumo are pinned to a git commit, which PyPI doesn't allow in extras, so `requirements-git.txt` lists them.
136
- `init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those two.
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 as in the paper, RandomForest anchored at 1000, bootstrapped confidence intervals; 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 | |
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 reads raw text itself). Elo is fit on one battle per dataset and model pair, with RandomForest
28
- at 1000 and 95% intervals from bootstrapping the datasets; a model with no result on a dataset has no
29
- battles there. TabPFN-v2 and v2.5 take at most 10 classes, which leaves out 3 of the 80 datasets.
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 | TabPFN-3.5 | 2221 | +230 / −176 | 2080 | 2458 | 80 |
34
- | 2 | Kumo-Tabular-Small | 2139 | +190 / −151 | 2094 | 2268 | 80 |
35
- | 3 | TabPFN-3.5-Fast | 2010 | +195 / −149 | 1906 | 2199 | 80 |
36
- | 4 | TabICLv2 | 1745 | +137 / −115 | 1637 | 1941 | 80 |
37
- | 5 | TabPFN-3 | 1666 | +145 / −117 | 1554 | 1866 | 80 |
38
- | 6 | TabPFN-v2p5 | 1661 | +136 / −114 | 1647 | 1743 | 77 |
39
- | 7 | TabPFN-v2 | 1407 | +117 / −97 | 1322 | 1559 | 77 |
40
- | 8 | TabM | 1366 | +98 / −89 | 1348 | 1432 | 80 |
41
- | 9 | ConTextTab | 1353 | +113 / −102 | 1279 | 1490 | 80 |
42
- | 10 | CatBoost | 1298 | +66 / −55 | 1307 | 1321 | 80 |
43
- | 11 | TabDPT | 1251 | +112 / −93 | 1116 | 1455 | 80 |
44
- | 12 | RealMLP | 1082 | +81 / −71 | 1103 | 1059 | 80 |
45
- | 13 | LightGBM | 1058 | +63 / −61 | 1093 | 1013 | 80 |
46
- | 14 | RandomForest | 1000 | anchor | 1000 | 1000 | 80 |
47
- | 15 | XGBoost | 988 | +67 / −65 | 1024 | 936 | 80 |
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 and
51
- normalized error; `--modalities text` or `--modalities image,image-text` restricts it to those datasets.
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 Kumo are pinned to a git commit, which PyPI doesn't allow in extras, so `requirements-git.txt` lists them.
83
- `init.sh` sets up the shared `.venv` from `requirements.txt`, which locks the core, every extra and those two.
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 as in the paper, RandomForest anchored at 1000, bootstrapped confidence intervals; 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 | |
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, transform_categorical_features
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
- with torch.inference_mode(), self._inference():
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)