bruAristimunha commited on
Commit
19eb9db
·
1 Parent(s): e42d567

feat: merge real (paper) results with new community results

Browse files

Previously the board showed HF contents OR the bundled sample_results.json
(fallback) — so the first worker write would have hidden the real paper
results. Now the leaderboard treats the bundled paper results as an immutable
base and OVERLAYS new results from braindecode/contents, deduped by
(model, adapter); a freshly evaluated result wins on a same-key clash, every
other paper result is preserved. Falls back to base-only when contents is
absent. Also alias oeb 'frozen' -> arena 'probe' in results mapping so new
linear-probe results align with the paper rows and the frontend filters.

Tests: leaderboard merge + frozen->probe alias (22 passing).

backend/app/services/leaderboard.py CHANGED
@@ -25,59 +25,70 @@ def _build_entry_id(data: Dict[str, Any]) -> str:
25
  )
26
 
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  class LeaderboardService:
29
  def __init__(self):
30
  pass
31
 
32
  async def fetch_raw_data(self) -> List[Dict[str, Any]]:
33
- """Fetch raw leaderboard data from HuggingFace dataset, with local fallback"""
 
 
 
 
 
 
34
  try:
35
  logger.info(LogFormatter.section("FETCHING LEADERBOARD DATA"))
36
 
37
- # Try HuggingFace dataset first
 
 
 
 
 
 
 
38
  try:
39
- logger.info(LogFormatter.info(f"Loading dataset from {HF_ORGANIZATION}/contents"))
40
  dataset = datasets.load_dataset(
41
  f"{HF_ORGANIZATION}/contents",
42
- cache_dir=cache_config.get_cache_path("datasets")
 
43
  )["train"]
44
-
45
- df = dataset.to_pandas()
46
- data = df.to_dict('records')
47
-
48
- stats = {
49
- "Total_Entries": len(data),
50
- "Dataset_Size": f"{df.memory_usage(deep=True).sum() / 1024 / 1024:.1f}MB",
51
- "Source": "HuggingFace Hub",
52
- }
53
- for line in LogFormatter.stats(stats, "Dataset Statistics"):
54
- logger.info(line)
55
-
56
- return data
57
-
58
  except Exception as hf_error:
59
  logger.warning(LogFormatter.warning(
60
- f"Could not load HF dataset: {hf_error}. Using local sample data."
61
  ))
62
 
63
- # Fallback to local sample data
64
- if SAMPLE_DATA_PATH.exists():
65
- with open(SAMPLE_DATA_PATH, "r") as f:
66
- data = json.load(f)
67
-
68
- stats = {
69
- "Total_Entries": len(data),
70
- "Source": "Local sample data",
71
- }
72
- for line in LogFormatter.stats(stats, "Dataset Statistics"):
73
- logger.info(line)
74
-
75
- return data
76
- else:
77
- raise HTTPException(
78
- status_code=500,
79
- detail="No data source available: HF dataset not found and no local data."
80
- )
81
 
82
  except HTTPException:
83
  raise
 
25
  )
26
 
27
 
28
+ def _merge_results(
29
+ base: List[Dict[str, Any]], overlay: List[Dict[str, Any]]
30
+ ) -> List[Dict[str, Any]]:
31
+ """Merge curated/base results with new ones, deduped by (model, adapter).
32
+
33
+ ``base`` is the bundled real/paper results; ``overlay`` is new community
34
+ results from the HF contents dataset. Overlay rows override base rows that
35
+ share the same (fullname, adapter) so a freshly evaluated result supersedes
36
+ the baseline, while every other base result is preserved.
37
+ """
38
+ merged: Dict[tuple, Dict[str, Any]] = {}
39
+ for row in list(base) + list(overlay): # overlay last => wins on conflict
40
+ merged[(row.get("fullname"), row.get("adapter"))] = row
41
+ return list(merged.values())
42
+
43
+
44
  class LeaderboardService:
45
  def __init__(self):
46
  pass
47
 
48
  async def fetch_raw_data(self) -> List[Dict[str, Any]]:
49
+ """Merge the bundled real/paper results with new community results.
50
+
51
+ ``sample_results.json`` (the paper results) is the immutable base. New
52
+ results from the HF ``contents`` dataset are overlaid on top so the board
53
+ shows BOTH; on a (model, adapter) clash the new result wins. Falls back
54
+ to base-only when the contents dataset isn't available yet.
55
+ """
56
  try:
57
  logger.info(LogFormatter.section("FETCHING LEADERBOARD DATA"))
58
 
59
+ # Base: bundled real/paper results (always present).
60
+ base: List[Dict[str, Any]] = []
61
+ if SAMPLE_DATA_PATH.exists():
62
+ with open(SAMPLE_DATA_PATH, "r") as f:
63
+ base = json.load(f)
64
+
65
+ # Overlay: new community results from the HF contents dataset.
66
+ overlay: List[Dict[str, Any]] = []
67
  try:
 
68
  dataset = datasets.load_dataset(
69
  f"{HF_ORGANIZATION}/contents",
70
+ cache_dir=cache_config.get_cache_path("datasets"),
71
+ download_mode="force_redownload",
72
  )["train"]
73
+ overlay = dataset.to_pandas().to_dict("records")
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  except Exception as hf_error:
75
  logger.warning(LogFormatter.warning(
76
+ f"No HF contents dataset yet ({hf_error}); showing bundled results only."
77
  ))
78
 
79
+ data = _merge_results(base, overlay)
80
+ if not data:
81
+ raise HTTPException(
82
+ status_code=500,
83
+ detail="No leaderboard data available: no bundled or HF results.",
84
+ )
85
+
86
+ for line in LogFormatter.stats(
87
+ {"Base": len(base), "New": len(overlay), "Merged": len(data)},
88
+ "Leaderboard Merge",
89
+ ):
90
+ logger.info(line)
91
+ return data
 
 
 
 
 
92
 
93
  except HTTPException:
94
  raise
backend/app/services/results_mapping.py CHANGED
@@ -8,6 +8,10 @@ from app.config.submission_config import DATASET_ID_TO_ACCURACY_FIELD
8
 
9
  AVERAGE_FIELD = "Average ⬆️" # "Average ⬆️"
10
 
 
 
 
 
11
 
12
  def _submitted_date(request: Dict[str, Any]) -> str:
13
  ts = str(request.get("submitted_time", ""))
@@ -56,7 +60,7 @@ def map_oeb_results_to_contents(df, request: Dict[str, Any]) -> List[Dict[str, A
56
 
57
  row: Dict[str, Any] = {
58
  "fullname": request.get("model_name", ""),
59
- "adapter": str(strategy),
60
  "Precision": "",
61
  "Model sha": None,
62
  "Architecture": backbone or arch_fallback,
 
8
 
9
  AVERAGE_FIELD = "Average ⬆️" # "Average ⬆️"
10
 
11
+ # Map oeb finetuning kinds to the arena's adapter vocabulary used by the existing
12
+ # (paper) results and the frontend filters — frozen linear probing == "probe".
13
+ _ADAPTER_ALIAS = {"frozen": "probe"}
14
+
15
 
16
  def _submitted_date(request: Dict[str, Any]) -> str:
17
  ts = str(request.get("submitted_time", ""))
 
60
 
61
  row: Dict[str, Any] = {
62
  "fullname": request.get("model_name", ""),
63
+ "adapter": _ADAPTER_ALIAS.get(str(strategy), str(strategy)),
64
  "Precision": "",
65
  "Model sha": None,
66
  "Architecture": backbone or arch_fallback,
backend/tests/test_leaderboard_merge.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.services.leaderboard import _merge_results
2
+
3
+
4
+ def test_merge_overlay_wins_on_conflict():
5
+ base = [
6
+ {"fullname": "BIOT", "adapter": "full_finetune", "Average ⬆️": 30.0},
7
+ {"fullname": "LaBraM", "adapter": "probe", "Average ⬆️": 12.0},
8
+ ]
9
+ overlay = [
10
+ {"fullname": "BIOT", "adapter": "full_finetune", "Average ⬆️": 40.0}, # overrides base
11
+ {"fullname": "MyModel", "adapter": "lora", "Average ⬆️": 50.0}, # brand new
12
+ ]
13
+ out = _merge_results(base, overlay)
14
+ by = {(r["fullname"], r["adapter"]): r for r in out}
15
+ assert len(out) == 3
16
+ assert by[("BIOT", "full_finetune")]["Average ⬆️"] == 40.0 # new result wins
17
+ assert by[("LaBraM", "probe")]["Average ⬆️"] == 12.0 # paper result preserved
18
+ assert ("MyModel", "lora") in by # new model added
19
+
20
+
21
+ def test_merge_empty_overlay_returns_base():
22
+ base = [{"fullname": "BIOT", "adapter": "probe"}]
23
+ assert len(_merge_results(base, [])) == 1
24
+
25
+
26
+ def test_merge_empty_base_returns_overlay():
27
+ assert len(_merge_results([], [{"fullname": "X", "adapter": "frozen"}])) == 1
28
+
29
+
30
+ def test_merge_distinct_adapters_both_kept():
31
+ # paper "probe" and a new "frozen" for the same model are different keys -> both shown
32
+ base = [{"fullname": "BIOT", "adapter": "probe"}]
33
+ overlay = [{"fullname": "BIOT", "adapter": "frozen"}]
34
+ assert len(_merge_results(base, overlay)) == 2
backend/tests/test_results_mapping.py CHANGED
@@ -30,7 +30,7 @@ def test_one_row_per_strategy():
30
  assert len(rows) == 1
31
  row = rows[0]
32
  assert row["fullname"] == "BIOT-frozen"
33
- assert row["adapter"] == "frozen"
34
  assert row["Architecture"] == "BIOT"
35
  # mean over seeds: bcic2a = (0.40+0.60)/2 = 0.50 (fraction)
36
  assert abs(row["bcic2a_accuracy"] - 0.50) < 1e-9
@@ -52,7 +52,7 @@ def test_multiple_strategies_multiple_rows():
52
  df = _df()
53
  extra = df.copy(); extra["finetuning"] = "lora"
54
  rows = map_oeb_results_to_contents(pd.concat([df, extra], ignore_index=True), _request())
55
- assert {r["adapter"] for r in rows} == {"frozen", "lora"}
56
 
57
  def test_all_failed_run_has_no_metric_column():
58
  # When every experiment fails, oeb's DataFrame has NO test_balanced_accuracy
 
30
  assert len(rows) == 1
31
  row = rows[0]
32
  assert row["fullname"] == "BIOT-frozen"
33
+ assert row["adapter"] == "probe" # oeb "frozen" linear probing -> arena "probe"
34
  assert row["Architecture"] == "BIOT"
35
  # mean over seeds: bcic2a = (0.40+0.60)/2 = 0.50 (fraction)
36
  assert abs(row["bcic2a_accuracy"] - 0.50) < 1e-9
 
52
  df = _df()
53
  extra = df.copy(); extra["finetuning"] = "lora"
54
  rows = map_oeb_results_to_contents(pd.concat([df, extra], ignore_index=True), _request())
55
+ assert {r["adapter"] for r in rows} == {"probe", "lora"} # frozen aliased to probe; lora unchanged
56
 
57
  def test_all_failed_run_has_no_metric_column():
58
  # When every experiment fails, oeb's DataFrame has NO test_balanced_accuracy