Spaces:
Running
Running
jasonshaoshun
commited on
Commit
·
2fc77f5
1
Parent(s):
bf334c6
first commit
Browse files- Makefile +13 -0
- README.md +39 -8
- app.py +196 -0
- pyproject.toml +13 -0
- requirements.txt +17 -0
- src/about.py +89 -0
- src/display/css_html_js.py +105 -0
- src/display/formatting.py +27 -0
- src/display/utils.py +291 -0
- src/envs.py +30 -0
- src/leaderboard/read_evals.py +607 -0
- src/populate.py +135 -0
- src/submission/check_validity.py +167 -0
- src/submission/submit.py +111 -0
Makefile
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.PHONY: style format
|
2 |
+
|
3 |
+
|
4 |
+
style:
|
5 |
+
python -m black --line-length 119 .
|
6 |
+
python -m isort .
|
7 |
+
ruff check --fix .
|
8 |
+
|
9 |
+
|
10 |
+
quality:
|
11 |
+
python -m black --check --line-length 119 .
|
12 |
+
python -m isort --check-only .
|
13 |
+
ruff check .
|
README.md
CHANGED
@@ -1,14 +1,45 @@
|
|
1 |
---
|
2 |
-
title: Leaderboard
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 5.8.0
|
8 |
app_file: app.py
|
9 |
-
pinned:
|
10 |
license: apache-2.0
|
11 |
-
short_description:
|
12 |
---
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: Leaderboard 2024
|
3 |
+
emoji: 🥇
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: indigo
|
6 |
sdk: gradio
|
|
|
7 |
app_file: app.py
|
8 |
+
pinned: true
|
9 |
license: apache-2.0
|
10 |
+
short_description: Leaderboard for the 2024 BabyLM Challenge
|
11 |
---
|
12 |
|
13 |
+
# Start the configuration
|
14 |
+
|
15 |
+
Most of the variables to change for a default leaderboard are in `src/env.py` (replace the path for your leaderboard) and `src/about.py` (for tasks).
|
16 |
+
|
17 |
+
Results files should have the following format and be stored as json files:
|
18 |
+
```json
|
19 |
+
{
|
20 |
+
"config": {
|
21 |
+
"model_dtype": "torch.float16", # or torch.bfloat16 or 8bit or 4bit
|
22 |
+
"model_name": "path of the model on the hub: org/model",
|
23 |
+
"model_sha": "revision on the hub",
|
24 |
+
},
|
25 |
+
"results": {
|
26 |
+
"task_name": {
|
27 |
+
"metric_name": score,
|
28 |
+
},
|
29 |
+
"task_name2": {
|
30 |
+
"metric_name": score,
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
```
|
35 |
+
|
36 |
+
Request files are created automatically by this tool.
|
37 |
+
|
38 |
+
If you encounter problem on the space, don't hesitate to restart it to remove the create eval-queue, eval-queue-bk, eval-results and eval-results-bk created folder.
|
39 |
+
|
40 |
+
# Code logic for more complex edits
|
41 |
+
|
42 |
+
You'll find
|
43 |
+
- the main table' columns names and properties in `src/display/utils.py`
|
44 |
+
- the logic to read all results and request files, then convert them in dataframe lines, in `src/leaderboard/read_evals.py`, and `src/populate.py`
|
45 |
+
- the logic to allow or filter submissions in `src/submission/submit.py` and `src/submission/check_validity.py`
|
app.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import gzip
|
3 |
+
import gradio as gr
|
4 |
+
from gradio_leaderboard import Leaderboard, ColumnFilter, SelectColumns
|
5 |
+
import pandas as pd
|
6 |
+
from apscheduler.schedulers.background import BackgroundScheduler
|
7 |
+
from huggingface_hub import snapshot_download
|
8 |
+
from io import StringIO
|
9 |
+
|
10 |
+
from src.about import (
|
11 |
+
CITATION_BUTTON_LABEL,
|
12 |
+
CITATION_BUTTON_TEXT,
|
13 |
+
EVALUATION_QUEUE_TEXT,
|
14 |
+
INTRODUCTION_TEXT,
|
15 |
+
LLM_BENCHMARKS_TEXT,
|
16 |
+
TITLE,
|
17 |
+
)
|
18 |
+
from src.display.css_html_js import custom_css
|
19 |
+
from src.display.utils import (
|
20 |
+
BENCHMARK_COLS,
|
21 |
+
BENCHMARK_COLS_MULTIMODAL,
|
22 |
+
BENCHMARK_COLS_MIB,
|
23 |
+
COLS,
|
24 |
+
COLS_MIB,
|
25 |
+
COLS_MULTIMODAL,
|
26 |
+
EVAL_COLS,
|
27 |
+
EVAL_TYPES,
|
28 |
+
AutoEvalColumn,
|
29 |
+
AutoEvalColumn_mib,
|
30 |
+
fields,
|
31 |
+
)
|
32 |
+
from src.envs import API, EVAL_REQUESTS_PATH, EVAL_RESULTS_PATH, QUEUE_REPO, REPO_ID, RESULTS_REPO, TOKEN, RESULTS_REPO_MIB_SUBGRAPH, EVAL_RESULTS_MIB_SUBGRAPH_PATH
|
33 |
+
from src.populate import get_evaluation_queue_df, get_leaderboard_df, get_leaderboard_df_mib
|
34 |
+
from src.submission.submit import add_new_eval
|
35 |
+
|
36 |
+
print("restart_space ")
|
37 |
+
|
38 |
+
def restart_space():
|
39 |
+
API.restart_space(repo_id=REPO_ID)
|
40 |
+
|
41 |
+
print("end restart_space")
|
42 |
+
|
43 |
+
|
44 |
+
print("Space initialisation ")
|
45 |
+
### Space initialisation
|
46 |
+
print("EVAL_REQUESTS_PATH")
|
47 |
+
try:
|
48 |
+
print(EVAL_REQUESTS_PATH)
|
49 |
+
snapshot_download(
|
50 |
+
repo_id=QUEUE_REPO, local_dir=EVAL_REQUESTS_PATH, repo_type="dataset", tqdm_class=None, etag_timeout=30, token=TOKEN
|
51 |
+
)
|
52 |
+
except Exception:
|
53 |
+
restart_space()
|
54 |
+
|
55 |
+
print("EVAL_RESULTS_PATH")
|
56 |
+
try:
|
57 |
+
print(EVAL_RESULTS_PATH)
|
58 |
+
snapshot_download(
|
59 |
+
repo_id=RESULTS_REPO, local_dir=EVAL_RESULTS_PATH, repo_type="dataset", tqdm_class=None, etag_timeout=30, token=TOKEN
|
60 |
+
)
|
61 |
+
except Exception:
|
62 |
+
restart_space()
|
63 |
+
|
64 |
+
print("RESULTS_REPO_MIB_SUBGRAPH")
|
65 |
+
try:
|
66 |
+
print(RESULTS_REPO_MIB_SUBGRAPH)
|
67 |
+
snapshot_download(
|
68 |
+
repo_id=RESULTS_REPO_MIB_SUBGRAPH, local_dir=EVAL_RESULTS_MIB_SUBGRAPH_PATH, repo_type="dataset", tqdm_class=None, etag_timeout=30, token=TOKEN
|
69 |
+
)
|
70 |
+
except Exception:
|
71 |
+
restart_space()
|
72 |
+
|
73 |
+
print("End Space initialisation ")
|
74 |
+
|
75 |
+
|
76 |
+
LEADERBOARD_DF_MIB_SUBGRAPH = get_leaderboard_df_mib(EVAL_RESULTS_MIB_SUBGRAPH_PATH, EVAL_REQUESTS_PATH, COLS_MIB, BENCHMARK_COLS_MIB)
|
77 |
+
|
78 |
+
# LEADERBOARD_DF = get_leaderboard_df(EVAL_RESULTS_PATH, EVAL_REQUESTS_PATH, COLS, BENCHMARK_COLS)
|
79 |
+
# LEADERBOARD_DF_MULTIMODAL = get_leaderboard_df(EVAL_RESULTS_PATH, EVAL_REQUESTS_PATH, COLS_MULTIMODAL, BENCHMARK_COLS_MULTIMODAL)
|
80 |
+
|
81 |
+
(
|
82 |
+
finished_eval_queue_df,
|
83 |
+
running_eval_queue_df,
|
84 |
+
pending_eval_queue_df,
|
85 |
+
) = get_evaluation_queue_df(EVAL_REQUESTS_PATH, EVAL_COLS)
|
86 |
+
|
87 |
+
|
88 |
+
def init_leaderboard_mib(dataframe, track):
|
89 |
+
print(f"init_leaderboard_mib: dataframe head before loc is {dataframe.head()}\n")
|
90 |
+
|
91 |
+
if dataframe is None or dataframe.empty:
|
92 |
+
raise ValueError("Leaderboard DataFrame is empty or None.")
|
93 |
+
|
94 |
+
# filter for correct track
|
95 |
+
# dataframe = dataframe.loc[dataframe["Track"] == track]
|
96 |
+
|
97 |
+
print(f"init_leaderboard_mib: dataframe head after loc is {dataframe.head()}\n")
|
98 |
+
|
99 |
+
return Leaderboard(
|
100 |
+
value=dataframe,
|
101 |
+
datatype=[c.type for c in fields(AutoEvalColumn_mib)],
|
102 |
+
select_columns=SelectColumns(
|
103 |
+
default_selection=[c.name for c in fields(AutoEvalColumn_mib) if c.displayed_by_default],
|
104 |
+
cant_deselect=[c.name for c in fields(AutoEvalColumn_mib) if c.never_hidden],
|
105 |
+
label="Select Columns to Display:",
|
106 |
+
),
|
107 |
+
search_columns=["Method"], # Changed from AutoEvalColumn_mib.model.name to "Method"
|
108 |
+
hide_columns=[c.name for c in fields(AutoEvalColumn_mib) if c.hidden],
|
109 |
+
bool_checkboxgroup_label="Hide models",
|
110 |
+
interactive=False,
|
111 |
+
)
|
112 |
+
|
113 |
+
def init_leaderboard(dataframe, track):
|
114 |
+
if dataframe is None or dataframe.empty:
|
115 |
+
raise ValueError("Leaderboard DataFrame is empty or None.")
|
116 |
+
# filter for correct track
|
117 |
+
dataframe = dataframe.loc[dataframe["Track"] == track]
|
118 |
+
|
119 |
+
# print(f"\n\n\n dataframe is {dataframe}\n\n\n")
|
120 |
+
|
121 |
+
return Leaderboard(
|
122 |
+
value=dataframe,
|
123 |
+
datatype=[c.type for c in fields(AutoEvalColumn)],
|
124 |
+
select_columns=SelectColumns(
|
125 |
+
default_selection=[c.name for c in fields(AutoEvalColumn) if c.displayed_by_default],
|
126 |
+
cant_deselect=[c.name for c in fields(AutoEvalColumn) if c.never_hidden],
|
127 |
+
label="Select Columns to Display:",
|
128 |
+
),
|
129 |
+
search_columns=[AutoEvalColumn.model.name],
|
130 |
+
hide_columns=[c.name for c in fields(AutoEvalColumn) if c.hidden],
|
131 |
+
bool_checkboxgroup_label="Hide models",
|
132 |
+
interactive=False,
|
133 |
+
)
|
134 |
+
|
135 |
+
def process_json(temp_file):
|
136 |
+
if temp_file is None:
|
137 |
+
return {}
|
138 |
+
|
139 |
+
# Handle file upload
|
140 |
+
try:
|
141 |
+
file_path = temp_file.name
|
142 |
+
if file_path.endswith('.gz'):
|
143 |
+
with gzip.open(file_path, 'rt') as f:
|
144 |
+
data = json.load(f)
|
145 |
+
else:
|
146 |
+
with open(file_path, 'r') as f:
|
147 |
+
data = json.load(f)
|
148 |
+
except Exception as e:
|
149 |
+
raise gr.Error(f"Error processing file: {str(e)}")
|
150 |
+
|
151 |
+
gr.Markdown("Upload successful!")
|
152 |
+
return data
|
153 |
+
|
154 |
+
|
155 |
+
demo = gr.Blocks(css=custom_css)
|
156 |
+
with demo:
|
157 |
+
gr.HTML(TITLE)
|
158 |
+
gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
|
159 |
+
|
160 |
+
with gr.Tabs(elem_classes="tab-buttons") as tabs:
|
161 |
+
# with gr.TabItem("Strict", elem_id="strict-benchmark-tab-table", id=0):
|
162 |
+
# leaderboard = init_leaderboard(LEADERBOARD_DF, "strict")
|
163 |
+
# with gr.TabItem("Strict-small", elem_id="strict-small-benchmark-tab-table", id=1):
|
164 |
+
# leaderboard = init_leaderboard(LEADERBOARD_DF, "strict-small")
|
165 |
+
# with gr.TabItem("Multimodal", elem_id="multimodal-benchmark-tab-table", id=2):
|
166 |
+
# leaderboard = init_leaderboard(LEADERBOARD_DF_MULTIMODAL, "multimodal")
|
167 |
+
|
168 |
+
# with gr.TabItem("📝 About", elem_id="llm-benchmark-tab-table", id=4):
|
169 |
+
# gr.Markdown(LLM_BENCHMARKS_TEXT, elem_classes="markdown-text")
|
170 |
+
|
171 |
+
# with gr.TabItem("👶 Submit", elem_id="llm-benchmark-tab-table", id=5):
|
172 |
+
# with gr.Column():
|
173 |
+
# with gr.Row():
|
174 |
+
# gr.Markdown(EVALUATION_QUEUE_TEXT, elem_classes="markdown-text")
|
175 |
+
|
176 |
+
with gr.TabItem("Subgraph", elem_id="subgraph", id=0):
|
177 |
+
leaderboard = init_leaderboard_mib(LEADERBOARD_DF_MIB_SUBGRAPH, "Subgraph")
|
178 |
+
# leaderboard = init_leaderboard_mib(LEADERBOARD_DF, "mib")
|
179 |
+
|
180 |
+
with gr.TabItem("Causal Graph", elem_id="causalgraph", id=1):
|
181 |
+
leaderboard = init_leaderboard_mib(LEADERBOARD_DF_MIB_SUBGRAPH, "Causal Graph")
|
182 |
+
|
183 |
+
# with gr.Row():
|
184 |
+
# with gr.Accordion("📙 Citation", open=False):
|
185 |
+
# citation_button = gr.Textbox(
|
186 |
+
# value=CITATION_BUTTON_TEXT,
|
187 |
+
# label=CITATION_BUTTON_LABEL,
|
188 |
+
# lines=20,
|
189 |
+
# elem_id="citation-button",
|
190 |
+
# show_copy_button=True,
|
191 |
+
# )
|
192 |
+
|
193 |
+
scheduler = BackgroundScheduler()
|
194 |
+
scheduler.add_job(restart_space, "interval", seconds=1800)
|
195 |
+
scheduler.start()
|
196 |
+
demo.launch(share=True, ssr_mode=False)
|
pyproject.toml
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[tool.ruff]
|
2 |
+
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
3 |
+
select = ["E", "F"]
|
4 |
+
ignore = ["E501"] # line too long (black is taking care of this)
|
5 |
+
line-length = 119
|
6 |
+
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
7 |
+
|
8 |
+
[tool.isort]
|
9 |
+
profile = "black"
|
10 |
+
line_length = 119
|
11 |
+
|
12 |
+
[tool.black]
|
13 |
+
line-length = 119
|
requirements.txt
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
APScheduler
|
2 |
+
black
|
3 |
+
datasets
|
4 |
+
fastapi==0.112.2
|
5 |
+
gradio
|
6 |
+
gradio[oauth]
|
7 |
+
gradio_leaderboard==0.0.13
|
8 |
+
gradio_client
|
9 |
+
huggingface-hub>=0.18.0
|
10 |
+
matplotlib
|
11 |
+
numpy
|
12 |
+
pandas
|
13 |
+
python-dateutil
|
14 |
+
tqdm
|
15 |
+
transformers
|
16 |
+
tokenizers>=0.15.0
|
17 |
+
sentencepiece
|
src/about.py
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dataclasses import dataclass
|
2 |
+
from enum import Enum
|
3 |
+
|
4 |
+
@dataclass
|
5 |
+
class Task:
|
6 |
+
benchmark: str
|
7 |
+
metric: str
|
8 |
+
col_name: str
|
9 |
+
|
10 |
+
|
11 |
+
@dataclass
|
12 |
+
class TaskMIB:
|
13 |
+
benchmark: str # task name in json (ioi/arithmetic)
|
14 |
+
models: list[str] # list of models to show as sub-columns
|
15 |
+
col_name: str # display name in leaderboard
|
16 |
+
metrics: list[str] # metrics to store (edge_counts, faithfulness)
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
# Select your tasks here
|
21 |
+
# ---------------------------------------------------
|
22 |
+
class Tasks(Enum):
|
23 |
+
# task_key in the json file, metric_key in the json file, name to display in the leaderboard
|
24 |
+
task0 = Task("blimp", "acc", "BLiMP")
|
25 |
+
task1 = Task("blimp_supplement", "acc", "BLiMP Supplement")
|
26 |
+
task2 = Task("glue", "acc", "(Super)GLUE")
|
27 |
+
task3 = Task("ewok", "acc", "EWoK")
|
28 |
+
|
29 |
+
|
30 |
+
class TasksMIB(Enum):
|
31 |
+
task0 = TaskMIB("ioi", ["meta_llama", "qwen", "gpt2"], "ioi", ["edge_counts", "faithfulness"])
|
32 |
+
task1 = TaskMIB("mcqa", ["meta_llama", "qwen", "gpt2"], "mcqa", ["edge_counts", "faithfulness"])
|
33 |
+
|
34 |
+
|
35 |
+
class TasksMultimodal(Enum):
|
36 |
+
task0 = Task("blimp", "acc", "BLiMP")
|
37 |
+
task1 = Task("blimp_supplement", "acc", "BLiMP Supplement")
|
38 |
+
task2 = Task("glue", "acc", "(Super)GLUE")
|
39 |
+
task3 = Task("ewok", "acc", "EWoK")
|
40 |
+
task4 = Task("vqa", "acc", "VQA")
|
41 |
+
task5 = Task("winoground", "acc", "Winoground")
|
42 |
+
task6 = Task("devbench", "acc", "DevBench")
|
43 |
+
|
44 |
+
NUM_FEWSHOT = 0 # Change with your few shot
|
45 |
+
# ---------------------------------------------------
|
46 |
+
|
47 |
+
|
48 |
+
|
49 |
+
# Your leaderboard name
|
50 |
+
TITLE = """<h1 align="center" id="space-title"> Mechanistic Interpretability Benchmark 2024 Leaderboards</h1>"""
|
51 |
+
|
52 |
+
# What does your leaderboard evaluate?
|
53 |
+
INTRODUCTION_TEXT = """
|
54 |
+
The leaderboards for each track of the 2024 Mechanistic Interpretability Benchmark.
|
55 |
+
"""
|
56 |
+
|
57 |
+
# Which evaluations are you running? how can people reproduce what you have?
|
58 |
+
LLM_BENCHMARKS_TEXT = f"""
|
59 |
+
This leaderboard displays scores from the 2024 BabyLM Challenge. Each track has its own tab.
|
60 |
+
"""
|
61 |
+
|
62 |
+
EVALUATION_QUEUE_TEXT = """
|
63 |
+
## Some good practices before requesting a predictions upload:
|
64 |
+
|
65 |
+
Make sure you can get scores from your predictions file using the `score_predictions.py` script.
|
66 |
+
```bash
|
67 |
+
git clone https://github.com/babylm/evaluation-pipeline-2024/
|
68 |
+
cd evaluation-pipeline-2024
|
69 |
+
python score_predictions.py path/to/your/predictions.json.gz
|
70 |
+
```
|
71 |
+
If this step fails, follow the error messages to debug your predictions before getting in touch. It's likely that either (i) some results are missing, or (ii) the results are incorrectly formatted.
|
72 |
+
|
73 |
+
Make sure your model has an open license! This is a leaderboard that is meant to advance research on language modeling, and we'd love for as many people as possible to know they can use your model.
|
74 |
+
|
75 |
+
Once these steps have been followed, get in touch with the organizers with your predictions file(s), and the scores you've obtained.
|
76 |
+
We'll verify that we can match your scores, and then upload to the leaderboard. Optionally, you can give us your preferred model display name for the leaderboard, and a link to your model on HuggingFace.
|
77 |
+
"""
|
78 |
+
|
79 |
+
CITATION_BUTTON_LABEL = "If you would like to cite these results, please cite the 2024 BabyLM Findings paper, as well as the authors of the model(s) whose results you cite!"
|
80 |
+
CITATION_BUTTON_TEXT = r"""
|
81 |
+
@article{hu2024findingssecondbabylmchallenge,
|
82 |
+
title={Findings of the Second BabyLM Challenge: Sample-Efficient Pretraining on Developmentally Plausible Corpora},
|
83 |
+
author={Michael Y. Hu and Aaron Mueller and Candace Ross and Adina Williams and Tal Linzen and Chengxu Zhuang and Ryan Cotterell and Leshem Choshen and Alex Warstadt and Ethan Gotlieb Wilcox},
|
84 |
+
year={2024},
|
85 |
+
journal={Computing Research Repository},
|
86 |
+
volume={arXiv:2412.05149},
|
87 |
+
url={https://arxiv.org/abs/2412.05149},
|
88 |
+
}
|
89 |
+
"""
|
src/display/css_html_js.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
custom_css = """
|
2 |
+
|
3 |
+
.markdown-text {
|
4 |
+
font-size: 16px !important;
|
5 |
+
}
|
6 |
+
|
7 |
+
#models-to-add-text {
|
8 |
+
font-size: 18px !important;
|
9 |
+
}
|
10 |
+
|
11 |
+
#citation-button span {
|
12 |
+
font-size: 16px !important;
|
13 |
+
}
|
14 |
+
|
15 |
+
#citation-button textarea {
|
16 |
+
font-size: 16px !important;
|
17 |
+
}
|
18 |
+
|
19 |
+
#citation-button > label > button {
|
20 |
+
margin: 6px;
|
21 |
+
transform: scale(1.3);
|
22 |
+
}
|
23 |
+
|
24 |
+
#leaderboard-table {
|
25 |
+
margin-top: 15px
|
26 |
+
}
|
27 |
+
|
28 |
+
#leaderboard-table-lite {
|
29 |
+
margin-top: 15px
|
30 |
+
}
|
31 |
+
|
32 |
+
#search-bar-table-box > div:first-child {
|
33 |
+
background: none;
|
34 |
+
border: none;
|
35 |
+
}
|
36 |
+
|
37 |
+
#search-bar {
|
38 |
+
padding: 0px;
|
39 |
+
}
|
40 |
+
|
41 |
+
/* Limit the width of the first AutoEvalColumn so that names don't expand too much */
|
42 |
+
#leaderboard-table td:nth-child(2),
|
43 |
+
#leaderboard-table th:nth-child(2) {
|
44 |
+
max-width: 400px;
|
45 |
+
overflow: auto;
|
46 |
+
white-space: nowrap;
|
47 |
+
}
|
48 |
+
|
49 |
+
.tab-buttons button {
|
50 |
+
font-size: 20px;
|
51 |
+
}
|
52 |
+
|
53 |
+
#scale-logo {
|
54 |
+
border-style: none !important;
|
55 |
+
box-shadow: none;
|
56 |
+
display: block;
|
57 |
+
margin-left: auto;
|
58 |
+
margin-right: auto;
|
59 |
+
max-width: 600px;
|
60 |
+
}
|
61 |
+
|
62 |
+
#scale-logo .download {
|
63 |
+
display: none;
|
64 |
+
}
|
65 |
+
#filter_type{
|
66 |
+
border: 0;
|
67 |
+
padding-left: 0;
|
68 |
+
padding-top: 0;
|
69 |
+
}
|
70 |
+
#filter_type label {
|
71 |
+
display: flex;
|
72 |
+
}
|
73 |
+
#filter_type label > span{
|
74 |
+
margin-top: var(--spacing-lg);
|
75 |
+
margin-right: 0.5em;
|
76 |
+
}
|
77 |
+
#filter_type label > .wrap{
|
78 |
+
width: 103px;
|
79 |
+
}
|
80 |
+
#filter_type label > .wrap .wrap-inner{
|
81 |
+
padding: 2px;
|
82 |
+
}
|
83 |
+
#filter_type label > .wrap .wrap-inner input{
|
84 |
+
width: 1px
|
85 |
+
}
|
86 |
+
#filter-columns-type{
|
87 |
+
border:0;
|
88 |
+
padding:0.5;
|
89 |
+
}
|
90 |
+
#filter-columns-size{
|
91 |
+
border:0;
|
92 |
+
padding:0.5;
|
93 |
+
}
|
94 |
+
#box-filter > .form{
|
95 |
+
border: 0
|
96 |
+
}
|
97 |
+
"""
|
98 |
+
|
99 |
+
get_window_url_params = """
|
100 |
+
function(url_params) {
|
101 |
+
const params = new URLSearchParams(window.location.search);
|
102 |
+
url_params = Object.fromEntries(params);
|
103 |
+
return url_params;
|
104 |
+
}
|
105 |
+
"""
|
src/display/formatting.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def model_hyperlink(link, model_name):
|
2 |
+
return f'<a target="_blank" href="{link}" style="color: var(--link-text-color); text-decoration: underline;text-decoration-style: dotted;">{model_name}</a>'
|
3 |
+
|
4 |
+
|
5 |
+
def make_clickable_model(model_repo, model_name):
|
6 |
+
link = f"https://huggingface.co/{model_repo}"
|
7 |
+
return model_hyperlink(link, model_name)
|
8 |
+
|
9 |
+
|
10 |
+
def styled_error(error):
|
11 |
+
return f"<p style='color: red; font-size: 20px; text-align: center;'>{error}</p>"
|
12 |
+
|
13 |
+
|
14 |
+
def styled_warning(warn):
|
15 |
+
return f"<p style='color: orange; font-size: 20px; text-align: center;'>{warn}</p>"
|
16 |
+
|
17 |
+
|
18 |
+
def styled_message(message):
|
19 |
+
return f"<p style='color: green; font-size: 20px; text-align: center;'>{message}</p>"
|
20 |
+
|
21 |
+
|
22 |
+
def has_no_nan_values(df, columns):
|
23 |
+
return df[columns].notna().all(axis=1)
|
24 |
+
|
25 |
+
|
26 |
+
def has_nan_values(df, columns):
|
27 |
+
return df[columns].isna().any(axis=1)
|
src/display/utils.py
ADDED
@@ -0,0 +1,291 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dataclasses import dataclass, make_dataclass
|
2 |
+
from enum import Enum
|
3 |
+
|
4 |
+
import pandas as pd
|
5 |
+
|
6 |
+
from src.about import Tasks, TasksMultimodal, TasksMIB
|
7 |
+
|
8 |
+
def fields(raw_class):
|
9 |
+
return [v for k, v in raw_class.__dict__.items() if k[:2] != "__" and k[-2:] != "__"]
|
10 |
+
|
11 |
+
|
12 |
+
# These classes are for user facing column names,
|
13 |
+
# to avoid having to change them all around the code
|
14 |
+
# when a modif is needed
|
15 |
+
@dataclass
|
16 |
+
class ColumnContent:
|
17 |
+
name: str
|
18 |
+
type: str
|
19 |
+
displayed_by_default: bool
|
20 |
+
hidden: bool = False
|
21 |
+
never_hidden: bool = False
|
22 |
+
|
23 |
+
## Leaderboard columns
|
24 |
+
auto_eval_column_dict_mib = []
|
25 |
+
auto_eval_column_dict = []
|
26 |
+
auto_eval_column_dict_multimodal = []
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
auto_eval_column_dict_mib = []
|
33 |
+
|
34 |
+
# Method name column
|
35 |
+
auto_eval_column_dict_mib.append(["method", ColumnContent, ColumnContent("Method", "markdown", True, never_hidden=True)])
|
36 |
+
|
37 |
+
# For each task and model combination
|
38 |
+
for task in TasksMIB:
|
39 |
+
for model in task.value.models:
|
40 |
+
col_name = f"{task.value.benchmark}_{model}" # ioi_meta_llama, mcqa_qwen, etc.
|
41 |
+
auto_eval_column_dict_mib.append([
|
42 |
+
col_name,
|
43 |
+
ColumnContent,
|
44 |
+
ColumnContent(col_name, "number", True)
|
45 |
+
])
|
46 |
+
|
47 |
+
# Average column
|
48 |
+
auto_eval_column_dict_mib.append(["average", ColumnContent, ColumnContent("Average", "number", True)])
|
49 |
+
|
50 |
+
|
51 |
+
# Create the dataclass for MIB columns
|
52 |
+
AutoEvalColumn_mib = make_dataclass("AutoEvalColumn_mib", auto_eval_column_dict_mib, frozen=True)
|
53 |
+
|
54 |
+
# Column selection for display
|
55 |
+
COLS_MIB = [c.name for c in fields(AutoEvalColumn_mib) if not c.hidden]
|
56 |
+
|
57 |
+
# BENCHMARK_COLS_MIB = [t.value.col_name for t in TasksMIB]
|
58 |
+
BENCHMARK_COLS_MIB = []
|
59 |
+
for task in TasksMIB:
|
60 |
+
for model in task.value.models:
|
61 |
+
col_name = f"{task.value.col_name}_{model.replace('-', '_')}"
|
62 |
+
BENCHMARK_COLS_MIB.append(col_name)
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
+
|
71 |
+
|
72 |
+
|
73 |
+
|
74 |
+
|
75 |
+
# Init
|
76 |
+
|
77 |
+
auto_eval_column_dict_mib.append(["model", ColumnContent, ColumnContent("Model", "markdown", True, never_hidden=True)])
|
78 |
+
# auto_eval_column_dict_mib.append(["hf_repo", ColumnContent, ColumnContent("HF Repo", "str", False)])
|
79 |
+
# auto_eval_column_dict_mib.append(["track", ColumnContent, ColumnContent("Track", "markdown", False)])
|
80 |
+
|
81 |
+
#Scores
|
82 |
+
for task in TasksMIB:
|
83 |
+
auto_eval_column_dict_mib.append([task.name, ColumnContent, ColumnContent(task.value.col_name, "number", True)])
|
84 |
+
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
auto_eval_column_dict.append(["model", ColumnContent, ColumnContent("Model", "markdown", True, never_hidden=True)])
|
89 |
+
auto_eval_column_dict.append(["hf_repo", ColumnContent, ColumnContent("HF Repo", "str", False)])
|
90 |
+
auto_eval_column_dict.append(["track", ColumnContent, ColumnContent("Track", "markdown", False)])
|
91 |
+
#Scores
|
92 |
+
for task in Tasks:
|
93 |
+
auto_eval_column_dict.append([task.name, ColumnContent, ColumnContent(task.value.col_name, "number", True)])
|
94 |
+
# Model information
|
95 |
+
auto_eval_column_dict.append(["text_average", ColumnContent, ColumnContent("Text Average", "number", True)])
|
96 |
+
auto_eval_column_dict.append(["still_on_hub", ColumnContent, ColumnContent("Available on the hub", "bool", False)])
|
97 |
+
auto_eval_column_dict.append(["revision", ColumnContent, ColumnContent("Model sha", "str", False, False)])
|
98 |
+
|
99 |
+
auto_eval_column_dict_multimodal.append(["model", ColumnContent, ColumnContent("Model", "markdown", True, never_hidden=True)])
|
100 |
+
auto_eval_column_dict_multimodal.append(["hf_repo", ColumnContent, ColumnContent("HF Repo", "str", False)])
|
101 |
+
auto_eval_column_dict_multimodal.append(["track", ColumnContent, ColumnContent("Track", "markdown", False)])
|
102 |
+
for task in TasksMultimodal:
|
103 |
+
auto_eval_column_dict_multimodal.append([task.name, ColumnContent, ColumnContent(task.value.col_name, "number", True)])
|
104 |
+
if task.value.col_name in ("ewok", "EWoK"): # make sure this appears in the right order
|
105 |
+
auto_eval_column_dict_multimodal.append(["text_average", ColumnContent, ColumnContent("Text Average", "number", True)])
|
106 |
+
auto_eval_column_dict_multimodal.append(["vision_average", ColumnContent, ColumnContent("Vision Average", "number", True)])
|
107 |
+
auto_eval_column_dict_multimodal.append(["still_on_hub", ColumnContent, ColumnContent("Available on the hub", "bool", False)])
|
108 |
+
auto_eval_column_dict_multimodal.append(["revision", ColumnContent, ColumnContent("Model sha", "str", False, False)])
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
AutoEvalColumn = make_dataclass("AutoEvalColumn", auto_eval_column_dict, frozen=True)
|
113 |
+
AutoEvalColumnMultimodal = make_dataclass("AutoEvalColumnMultimodal", auto_eval_column_dict_multimodal, frozen=True)
|
114 |
+
|
115 |
+
## For the queue columns in the submission tab
|
116 |
+
@dataclass(frozen=True)
|
117 |
+
class EvalQueueColumn: # Queue column
|
118 |
+
model = ColumnContent("model", "markdown", True)
|
119 |
+
track = ColumnContent("track", "str", True)
|
120 |
+
revision = ColumnContent("revision", "str", True)
|
121 |
+
private = ColumnContent("private", "bool", True)
|
122 |
+
status = ColumnContent("status", "str", True)
|
123 |
+
|
124 |
+
## All the model information that we might need
|
125 |
+
@dataclass
|
126 |
+
class ModelDetails:
|
127 |
+
name: str
|
128 |
+
display_name: str = ""
|
129 |
+
symbol: str = "" # emoji
|
130 |
+
|
131 |
+
# Column selection
|
132 |
+
|
133 |
+
COLS = [c.name for c in fields(AutoEvalColumn) if not c.hidden]
|
134 |
+
COLS_MULTIMODAL = [c.name for c in fields(AutoEvalColumnMultimodal) if not c.hidden]
|
135 |
+
|
136 |
+
EVAL_COLS = [c.name for c in fields(EvalQueueColumn)]
|
137 |
+
EVAL_TYPES = [c.type for c in fields(EvalQueueColumn)]
|
138 |
+
|
139 |
+
BENCHMARK_COLS = [t.value.col_name for t in Tasks]
|
140 |
+
BENCHMARK_COLS_MULTIMODAL = [t.value.col_name for t in TasksMultimodal]
|
141 |
+
|
142 |
+
TEXT_TASKS = {
|
143 |
+
"glue": ["cola", "sst2", "mrpc", "qqp", "mnli", "mnli-mm", "qnli", "rte",
|
144 |
+
"boolq", "multirc", "wsc"],
|
145 |
+
# Lots of BLiMP tasks – use verifier function below to see if you've included everything.
|
146 |
+
"blimp": ["adjunct_island","anaphor_gender_agreement","anaphor_number_agreement","animate_subject_passive","animate_subject_trans",
|
147 |
+
"causative","complex_NP_island","coordinate_structure_constraint_complex_left_branch","coordinate_structure_constraint_object_extraction","determiner_noun_agreement_1",
|
148 |
+
"determiner_noun_agreement_2","determiner_noun_agreement_irregular_1","determiner_noun_agreement_irregular_2","determiner_noun_agreement_with_adjective_1",
|
149 |
+
"determiner_noun_agreement_with_adj_2","determiner_noun_agreement_with_adj_irregular_1","determiner_noun_agreement_with_adj_irregular_2","distractor_agreement_relational_noun",
|
150 |
+
"distractor_agreement_relative_clause","drop_argument","ellipsis_n_bar_1","ellipsis_n_bar_2",
|
151 |
+
"existential_there_object_raising", "existential_there_quantifiers_1",
|
152 |
+
"existential_there_quantifiers_2", "existential_there_subject_raising", "expletive_it_object_raising",
|
153 |
+
"inchoative", "intransitive","irregular_past_participle_adjectives", "irregular_past_participle_verbs",
|
154 |
+
"irregular_plural_subject_verb_agreement_1", "irregular_plural_subject_verb_agreement_2", "left_branch_island_echo_question", "left_branch_island_simple_question",
|
155 |
+
"matrix_question_npi_licensor_present", "npi_present_1", "npi_present_2", "only_npi_licensor_present", "only_npi_scope", "passive_1", "passive_2",
|
156 |
+
"principle_A_case_1", "principle_A_case_2", "principle_A_c_command", "principle_A_domain_1",
|
157 |
+
"principle_A_domain_2", "principle_A_domain_3", "principle_A_reconstruction", "regular_plural_subject_verb_agreement_1",
|
158 |
+
"regular_plural_subject_verb_agreement_2", "sentential_negation_npi_licensor_present", "sentential_negation_npi_scope", "sentential_subject_island",
|
159 |
+
"superlative_quantifiers_1", "superlative_quantifiers_2", "tough_vs_raising_1", "tough_vs_raising_2",
|
160 |
+
"transitive", "wh_island", "wh_questions_object_gap", "wh_questions_subject_gap",
|
161 |
+
"wh_questions_subject_gap_long_distance", "wh_vs_that_no_gap", "wh_vs_that_no_gap_long_distance", "wh_vs_that_with_gap",
|
162 |
+
"wh_vs_that_with_gap_long_distance"
|
163 |
+
],
|
164 |
+
"blimp_supplement": ["hypernym", "qa_congruence_easy", "qa_congruence_tricky",
|
165 |
+
"subject_aux_inversion", "turn_taking"],
|
166 |
+
"ewok": ["agent-properties", "material-dynamics", "material-properties", "physical-dynamics",
|
167 |
+
"physical-interactions", "physical-relations", "quantitative-properties",
|
168 |
+
"social-interactions", "social-properties", "social-relations", "spatial-relations"]
|
169 |
+
}
|
170 |
+
|
171 |
+
VISION_TASKS = {
|
172 |
+
"vqa": ["vqa"],
|
173 |
+
"winoground": ["winoground"],
|
174 |
+
"devbench": ["lex-viz_vocab", "gram-trog", "sem-things"]
|
175 |
+
}
|
176 |
+
|
177 |
+
NUM_EXPECTED_EXAMPLES = {
|
178 |
+
"glue": {
|
179 |
+
"cola": 522,
|
180 |
+
"sst2": 436,
|
181 |
+
"mrpc": 204,
|
182 |
+
"qqp": 20215,
|
183 |
+
"mnli": 4908,
|
184 |
+
"mnli-mm": 4916,
|
185 |
+
"qnli": 2732,
|
186 |
+
"rte": 139,
|
187 |
+
"boolq": 1635,
|
188 |
+
"multirc": 2424,
|
189 |
+
"wsc": 52
|
190 |
+
},
|
191 |
+
"blimp": {
|
192 |
+
"adjunct_island": 928,
|
193 |
+
"anaphor_gender_agreement": 971,
|
194 |
+
"anaphor_number_agreement": 931,
|
195 |
+
"animate_subject_passive": 895,
|
196 |
+
"animate_subject_trans": 923,
|
197 |
+
"causative": 818,
|
198 |
+
"complex_NP_island": 846,
|
199 |
+
"coordinate_structure_constraint_complex_left_branch": 906,
|
200 |
+
"coordinate_structure_constraint_object_extraction": 949,
|
201 |
+
"determiner_noun_agreement_1": 929,
|
202 |
+
"determiner_noun_agreement_2": 931,
|
203 |
+
"determiner_noun_agreement_irregular_1": 681,
|
204 |
+
"determiner_noun_agreement_irregular_2": 820,
|
205 |
+
"determiner_noun_agreement_with_adjective_1": 933,
|
206 |
+
"determiner_noun_agreement_with_adj_2": 941,
|
207 |
+
"determiner_noun_agreement_with_adj_irregular_1": 718,
|
208 |
+
"determiner_noun_agreement_with_adj_irregular_2": 840,
|
209 |
+
"distractor_agreement_relational_noun": 788,
|
210 |
+
"distractor_agreement_relative_clause": 871,
|
211 |
+
"drop_argument": 920,
|
212 |
+
"ellipsis_n_bar_1": 802,
|
213 |
+
"ellipsis_n_bar_2": 828,
|
214 |
+
"existential_there_object_raising": 812,
|
215 |
+
"existential_there_quantifiers_1": 930,
|
216 |
+
"existential_there_quantifiers_2": 911,
|
217 |
+
"existential_there_subject_raising": 924,
|
218 |
+
"expletive_it_object_raising": 759,
|
219 |
+
"inchoative": 855,
|
220 |
+
"intransitive": 868,
|
221 |
+
"irregular_past_participle_adjectives": 961,
|
222 |
+
"irregular_past_participle_verbs": 942,
|
223 |
+
"irregular_plural_subject_verb_agreement_1": 804,
|
224 |
+
"irregular_plural_subject_verb_agreement_2": 892,
|
225 |
+
"left_branch_island_echo_question": 947,
|
226 |
+
"left_branch_island_simple_question": 951,
|
227 |
+
"matrix_question_npi_licensor_present": 929,
|
228 |
+
"npi_present_1": 909,
|
229 |
+
"npi_present_2": 914,
|
230 |
+
"only_npi_licensor_present": 882,
|
231 |
+
"only_npi_scope": 837,
|
232 |
+
"passive_1": 840,
|
233 |
+
"passive_2": 903,
|
234 |
+
"principle_A_case_1": 912,
|
235 |
+
"principle_A_case_2": 915,
|
236 |
+
"principle_A_c_command": 946,
|
237 |
+
"principle_A_domain_1": 914,
|
238 |
+
"principle_A_domain_2": 915,
|
239 |
+
"principle_A_domain_3": 941,
|
240 |
+
"principle_A_reconstruction": 967,
|
241 |
+
"regular_plural_subject_verb_agreement_1": 890,
|
242 |
+
"regular_plural_subject_verb_agreement_2": 945,
|
243 |
+
"sentential_negation_npi_licensor_present": 919,
|
244 |
+
"sentential_negation_npi_scope": 871,
|
245 |
+
"sentential_subject_island": 961,
|
246 |
+
"superlative_quantifiers_1": 979,
|
247 |
+
"superlative_quantifiers_2": 986,
|
248 |
+
"tough_vs_raising_1": 948,
|
249 |
+
"tough_vs_raising_2": 920,
|
250 |
+
"transitive": 868,
|
251 |
+
"wh_island": 960,
|
252 |
+
"wh_questions_object_gap": 859,
|
253 |
+
"wh_questions_subject_gap": 898,
|
254 |
+
"wh_questions_subject_gap_long_distance": 857,
|
255 |
+
"wh_vs_that_no_gap": 861,
|
256 |
+
"wh_vs_that_no_gap_long_distance": 875,
|
257 |
+
"wh_vs_that_with_gap": 919,
|
258 |
+
"wh_vs_that_with_gap_long_distance": 910
|
259 |
+
},
|
260 |
+
"blimp_supplement": {
|
261 |
+
"hypernym": 842,
|
262 |
+
"qa_congruence_easy": 64,
|
263 |
+
"qa_congruence_tricky": 165,
|
264 |
+
"subject_aux_inversion": 3867,
|
265 |
+
"turn_taking": 280
|
266 |
+
},
|
267 |
+
"ewok": {
|
268 |
+
"agent-properties": 2210,
|
269 |
+
"material-dynamics": 770,
|
270 |
+
"material-properties": 170,
|
271 |
+
"physical-dynamics": 120,
|
272 |
+
"physical-interactions": 556,
|
273 |
+
"physical-relations": 818,
|
274 |
+
"quantitative-properties": 314,
|
275 |
+
"social-interactions": 294,
|
276 |
+
"social-properties": 328,
|
277 |
+
"social-relations": 1548,
|
278 |
+
"spatial-relations": 490
|
279 |
+
},
|
280 |
+
"vqa": {
|
281 |
+
"vqa": 25230
|
282 |
+
},
|
283 |
+
"winoground": {
|
284 |
+
"winoground": 746
|
285 |
+
},
|
286 |
+
"devbench": {
|
287 |
+
"lex-viz_vocab": 119,
|
288 |
+
"gram-trog": 76,
|
289 |
+
"sem-things": 1854
|
290 |
+
}
|
291 |
+
}
|
src/envs.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
from huggingface_hub import HfApi
|
4 |
+
|
5 |
+
# Info to change for your repository
|
6 |
+
# ----------------------------------
|
7 |
+
TOKEN = os.environ.get("HF_TOKEN") # A read/write token for your org
|
8 |
+
|
9 |
+
OWNER = "shunshao" # Change to your org - don't forget to create a results and request dataset, with the correct format!
|
10 |
+
# ----------------------------------
|
11 |
+
|
12 |
+
REPO_ID = f"{OWNER}/mib-test"
|
13 |
+
QUEUE_REPO = f"{OWNER}/requests-mib-test"
|
14 |
+
RESULTS_REPO = f"{OWNER}/results-mib-test"
|
15 |
+
RESULTS_REPO_MIB_SUBGRAPH = f"{OWNER}/results-mib-subgraph"
|
16 |
+
|
17 |
+
# If you setup a cache later, just change HF_HOME
|
18 |
+
CACHE_PATH=os.getenv("HF_HOME", ".")
|
19 |
+
|
20 |
+
# Local caches
|
21 |
+
EVAL_REQUESTS_PATH = os.path.join(CACHE_PATH, "eval-queue")
|
22 |
+
EVAL_RESULTS_PATH = os.path.join(CACHE_PATH, "eval-results")
|
23 |
+
EVAL_RESULTS_MIB_SUBGRAPH_PATH = os.path.join(CACHE_PATH, "eval-results-mib-subgraph")
|
24 |
+
|
25 |
+
|
26 |
+
|
27 |
+
EVAL_REQUESTS_PATH_BACKEND = os.path.join(CACHE_PATH, "eval-queue-bk")
|
28 |
+
EVAL_RESULTS_PATH_BACKEND = os.path.join(CACHE_PATH, "eval-results-bk")
|
29 |
+
|
30 |
+
API = HfApi(token=TOKEN)
|
src/leaderboard/read_evals.py
ADDED
@@ -0,0 +1,607 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import glob
|
2 |
+
import json
|
3 |
+
import math
|
4 |
+
import os
|
5 |
+
from dataclasses import dataclass
|
6 |
+
|
7 |
+
import dateutil
|
8 |
+
import numpy as np
|
9 |
+
|
10 |
+
from src.display.formatting import make_clickable_model
|
11 |
+
from src.display.utils import AutoEvalColumn, AutoEvalColumnMultimodal, Tasks, TasksMultimodal
|
12 |
+
from src.submission.check_validity import is_model_on_hub
|
13 |
+
|
14 |
+
|
15 |
+
from typing import List, Dict
|
16 |
+
from src.about import TasksMIB
|
17 |
+
|
18 |
+
|
19 |
+
def compute_area(edge_counts, faithfulnesses, log_scale=True):
|
20 |
+
percentages = [e / max(edge_counts) for e in edge_counts]
|
21 |
+
area_under = 0.
|
22 |
+
area_from_100 = 0.
|
23 |
+
for i in range(len(faithfulnesses) - 1):
|
24 |
+
i_1, i_2 = i, i+1
|
25 |
+
x_1 = percentages[i_1]
|
26 |
+
x_2 = percentages[i_2]
|
27 |
+
# area from point to 100
|
28 |
+
if log_scale:
|
29 |
+
x_1 = math.log(x_1)
|
30 |
+
x_2 = math.log(x_2)
|
31 |
+
trapezoidal = (percentages[i_2] - percentages[i_1]) * \
|
32 |
+
(((abs(1. - faithfulnesses[i_1])) + (abs(1. - faithfulnesses[i_2]))) / 2)
|
33 |
+
area_from_100 += trapezoidal
|
34 |
+
|
35 |
+
trapezoidal = (percentages[i_2] - percentages[i_1]) * ((faithfulnesses[i_1] + faithfulnesses[i_2]) / 2)
|
36 |
+
area_under += trapezoidal
|
37 |
+
average = sum(faithfulnesses) / len(faithfulnesses)
|
38 |
+
return (area_under, area_from_100, average)
|
39 |
+
|
40 |
+
@dataclass
|
41 |
+
class EvalResult_MIB:
|
42 |
+
"""Represents one full evaluation for a method across all models in MIB."""
|
43 |
+
eval_name: str # method name as identifier
|
44 |
+
method_name: str # name of the interpretation method
|
45 |
+
results: Dict # nested dict of results {task: {model: {metric: scores}}}
|
46 |
+
|
47 |
+
# def init_from_json_file(self, json_filepath):
|
48 |
+
# """Inits results from the method result file"""
|
49 |
+
# with open(json_filepath) as fp:
|
50 |
+
# data = json.load(fp)
|
51 |
+
|
52 |
+
# method_name = data.get("method_name")
|
53 |
+
|
54 |
+
# def _get_task_metrics(scores, task_name):
|
55 |
+
# """Extract both edge_counts and faithfulness scores"""
|
56 |
+
# task_scores = scores.get(task_name, {})
|
57 |
+
# if not task_scores:
|
58 |
+
# return None
|
59 |
+
|
60 |
+
# edge_counts = task_scores.get("edge_counts", [])
|
61 |
+
# faithfulness = task_scores.get("faithfulness", [])
|
62 |
+
|
63 |
+
# if not edge_counts or not faithfulness:
|
64 |
+
# return None
|
65 |
+
|
66 |
+
# # Handle case where faithfulness is a list of lists
|
67 |
+
# if isinstance(faithfulness[0], list):
|
68 |
+
# faithfulness = faithfulness[0]
|
69 |
+
|
70 |
+
# return {
|
71 |
+
# "edge_counts": edge_counts,
|
72 |
+
# "faithfulness": faithfulness
|
73 |
+
# }
|
74 |
+
|
75 |
+
# # Process results for each model
|
76 |
+
# results = {}
|
77 |
+
# for task in TasksMIB:
|
78 |
+
# results[task.value.benchmark] = {}
|
79 |
+
# for model_result in data.get("results", []):
|
80 |
+
# # model_id = model_result.get("model_id", "").split('/')[-1] # Get last part of model path
|
81 |
+
# model_id = model_result.get("model_id", "").split('/')[0]
|
82 |
+
# scores = model_result.get("scores", {})
|
83 |
+
# metrics = _get_task_metrics(scores, task.value.benchmark)
|
84 |
+
# if metrics is not None:
|
85 |
+
# results[task.value.benchmark][model_id] = metrics
|
86 |
+
|
87 |
+
# return EvalResult_MIB(
|
88 |
+
# eval_name=method_name,
|
89 |
+
# method_name=method_name,
|
90 |
+
# results=results
|
91 |
+
# )
|
92 |
+
def init_from_json_file(self, json_filepath):
|
93 |
+
"""Inits results from the method result file"""
|
94 |
+
with open(json_filepath) as fp:
|
95 |
+
data = json.load(fp)
|
96 |
+
|
97 |
+
method_name = data.get("method_name")
|
98 |
+
|
99 |
+
# Initialize results dictionary with the exact structure from JSON
|
100 |
+
results = {}
|
101 |
+
for task in ["ioi", "mcqa"]: # Use exact task names from JSON
|
102 |
+
results[task] = {}
|
103 |
+
|
104 |
+
# Process each model's results maintaining original structure
|
105 |
+
for model_result in data.get("results", []):
|
106 |
+
model_id = model_result.get("model_id", "")
|
107 |
+
if "/" in model_id:
|
108 |
+
org = model_id.split("/")[0]
|
109 |
+
if org == "meta-llama":
|
110 |
+
model_name = "meta_llama"
|
111 |
+
elif org == "Qwen":
|
112 |
+
model_name = "qwen"
|
113 |
+
elif "gpt" in model_id.lower():
|
114 |
+
model_name = "gpt2"
|
115 |
+
else:
|
116 |
+
model_name = model_id
|
117 |
+
|
118 |
+
# Keep exact scores structure from JSON
|
119 |
+
scores = model_result.get("scores", {})
|
120 |
+
for task in ["ioi", "mcqa"]:
|
121 |
+
if task in scores:
|
122 |
+
results[task][model_name] = {
|
123 |
+
"edge_counts": scores[task]["edge_counts"],
|
124 |
+
"faithfulness": scores[task]["faithfulness"]
|
125 |
+
}
|
126 |
+
|
127 |
+
return EvalResult_MIB(
|
128 |
+
eval_name=method_name,
|
129 |
+
method_name=method_name,
|
130 |
+
results=results
|
131 |
+
)
|
132 |
+
|
133 |
+
|
134 |
+
# def to_dict(self):
|
135 |
+
# """Converts the Eval Result to a dict for dataframe display"""
|
136 |
+
# data_dict = {
|
137 |
+
# "eval_name": self.eval_name,
|
138 |
+
# "Method": self.method_name,
|
139 |
+
# }
|
140 |
+
|
141 |
+
# all_scores = []
|
142 |
+
# expected_entries = 0 # Count how many entries we expect
|
143 |
+
# actual_entries = 0 # Count how many entries we actually got
|
144 |
+
|
145 |
+
# # For each task (ioi, mcqa)
|
146 |
+
# for task, task_results in self.results.items():
|
147 |
+
# # Get the models that have results for this task
|
148 |
+
# models = task_results.keys()
|
149 |
+
|
150 |
+
# for model in models:
|
151 |
+
# expected_entries += 1
|
152 |
+
# col_name = f"{task}_{model}"
|
153 |
+
# metrics = task_results[model]
|
154 |
+
# if metrics:
|
155 |
+
# edge_counts = metrics["edge_counts"]
|
156 |
+
# faithfulness = metrics["faithfulness"]
|
157 |
+
# if isinstance(faithfulness[0], list):
|
158 |
+
# faithfulness = faithfulness[0]
|
159 |
+
|
160 |
+
# # Use compute_area instead of simple averaging
|
161 |
+
# area_under, area_from_100, avg = compute_area(edge_counts, faithfulness)
|
162 |
+
# score = area_under * 100 # Scale up for readability
|
163 |
+
# data_dict[col_name] = round(score, 2)
|
164 |
+
# all_scores.append(score)
|
165 |
+
# actual_entries += 1
|
166 |
+
# else:
|
167 |
+
# data_dict[col_name] = '-'
|
168 |
+
|
169 |
+
# # Only show average if all entries are present
|
170 |
+
# if actual_entries == expected_entries:
|
171 |
+
# data_dict["Average"] = round(np.mean(all_scores), 2)
|
172 |
+
# else:
|
173 |
+
# data_dict["Average"] = '-'
|
174 |
+
|
175 |
+
# return data_dict
|
176 |
+
def to_dict(self):
|
177 |
+
"""Converts the Eval Result to a dict for dataframe display"""
|
178 |
+
data_dict = {
|
179 |
+
"eval_name": self.eval_name,
|
180 |
+
"Method": self.method_name,
|
181 |
+
}
|
182 |
+
|
183 |
+
all_scores = []
|
184 |
+
required_entries = {
|
185 |
+
'ioi_meta_llama': False,
|
186 |
+
'ioi_qwen': False,
|
187 |
+
'ioi_gpt2': False,
|
188 |
+
'mcqa_meta_llama': False,
|
189 |
+
'mcqa_qwen': False,
|
190 |
+
'mcqa_gpt2': False
|
191 |
+
}
|
192 |
+
|
193 |
+
# For each task (ioi, mcqa)
|
194 |
+
for task, task_results in self.results.items():
|
195 |
+
# Get the models that have results for this task
|
196 |
+
models = task_results.keys()
|
197 |
+
|
198 |
+
for model in models:
|
199 |
+
col_name = f"{task}_{model}"
|
200 |
+
metrics = task_results[model]
|
201 |
+
if metrics:
|
202 |
+
edge_counts = metrics["edge_counts"]
|
203 |
+
faithfulness = metrics["faithfulness"]
|
204 |
+
if isinstance(faithfulness[0], list):
|
205 |
+
faithfulness = faithfulness[0]
|
206 |
+
|
207 |
+
# Use compute_area
|
208 |
+
area_under, area_from_100, avg = compute_area(edge_counts, faithfulness)
|
209 |
+
score = area_under * 100
|
210 |
+
data_dict[col_name] = round(score, 2)
|
211 |
+
all_scores.append(score)
|
212 |
+
required_entries[col_name] = True
|
213 |
+
else:
|
214 |
+
data_dict[col_name] = '-'
|
215 |
+
|
216 |
+
# Only show average if all six required entries are present
|
217 |
+
if all(required_entries.values()):
|
218 |
+
data_dict["Average"] = round(np.mean(all_scores), 2)
|
219 |
+
else:
|
220 |
+
data_dict["Average"] = '-'
|
221 |
+
|
222 |
+
return data_dict
|
223 |
+
|
224 |
+
|
225 |
+
|
226 |
+
|
227 |
+
|
228 |
+
@dataclass
|
229 |
+
class EvalResult:
|
230 |
+
"""Represents one full evaluation. Built from a combination of the result and request file for a given run.
|
231 |
+
"""
|
232 |
+
eval_name: str # org_model_track (uid)
|
233 |
+
full_model: str # org/model (name of model)
|
234 |
+
repo_id: str # org/model (path to model on HF)
|
235 |
+
track: str
|
236 |
+
org: str
|
237 |
+
model: str
|
238 |
+
revision: str # commit hash, "" if main
|
239 |
+
results: dict
|
240 |
+
date: str = "" # submission date of request file
|
241 |
+
still_on_hub: bool = False
|
242 |
+
|
243 |
+
@classmethod
|
244 |
+
def init_from_json_file(self, json_filepath):
|
245 |
+
"""Inits the result from the specific model result file"""
|
246 |
+
with open(json_filepath) as fp:
|
247 |
+
data = json.load(fp)
|
248 |
+
|
249 |
+
config = data.get("config")
|
250 |
+
track = data.get("track")
|
251 |
+
|
252 |
+
# Get model and org
|
253 |
+
org_and_model = config.get("model_name", config.get("model_args", None))
|
254 |
+
repo_id = config.get("hf_repo", config.get("hf_repo", None))
|
255 |
+
org_and_model = org_and_model.split("/", 1)
|
256 |
+
|
257 |
+
if len(org_and_model) == 1:
|
258 |
+
org = None
|
259 |
+
model = org_and_model[0]
|
260 |
+
else:
|
261 |
+
org = org_and_model[0]
|
262 |
+
model = org_and_model[1]
|
263 |
+
full_model = "/".join(org_and_model)
|
264 |
+
eval_name = "_".join(org_and_model) + f"_{track}"
|
265 |
+
|
266 |
+
still_on_hub, _, model_config = is_model_on_hub(
|
267 |
+
repo_id, config.get("model_sha", "main"), trust_remote_code=True, test_tokenizer=False
|
268 |
+
)
|
269 |
+
|
270 |
+
def _get_task_results(task):
|
271 |
+
# We average all scores of a given metric (not all metrics are present in all files)
|
272 |
+
accs = np.array([v.get(task.metric, None) for k, v in data["results"].items() if task.benchmark == k])
|
273 |
+
if accs.size == 0 or any([acc is None for acc in accs]):
|
274 |
+
return None
|
275 |
+
|
276 |
+
mean_acc = np.mean(accs) * 100.0
|
277 |
+
return mean_acc
|
278 |
+
|
279 |
+
# Extract results available in this file (some results are split in several files)
|
280 |
+
results = {}
|
281 |
+
if track.lower() == "multimodal":
|
282 |
+
for task in TasksMultimodal:
|
283 |
+
task = task.value
|
284 |
+
task_result = _get_task_results(task)
|
285 |
+
if task_result is not None:
|
286 |
+
results[task.benchmark] = task_result
|
287 |
+
else:
|
288 |
+
for task in Tasks:
|
289 |
+
task = task.value
|
290 |
+
task_result = _get_task_results(task)
|
291 |
+
if task_result is not None:
|
292 |
+
results[task.benchmark] = task_result
|
293 |
+
|
294 |
+
return self(
|
295 |
+
eval_name=eval_name,
|
296 |
+
full_model=full_model,
|
297 |
+
repo_id=repo_id,
|
298 |
+
track=track,
|
299 |
+
org=org,
|
300 |
+
model=model,
|
301 |
+
results=results,
|
302 |
+
revision=config.get("model_sha", ""),
|
303 |
+
still_on_hub=still_on_hub,
|
304 |
+
)
|
305 |
+
|
306 |
+
# Q: not sure what to do with this
|
307 |
+
def update_with_request_file(self, requests_path):
|
308 |
+
"""Finds the relevant request file for the current model and updates info with it"""
|
309 |
+
request_file = get_request_file_for_model(requests_path, self.full_model, self.track)
|
310 |
+
|
311 |
+
try:
|
312 |
+
with open(request_file, "r") as f:
|
313 |
+
request = json.load(f)
|
314 |
+
self.date = request.get("submitted_time", "")
|
315 |
+
except Exception:
|
316 |
+
print(f"Could not find request file for {self.org}/{self.model}")
|
317 |
+
|
318 |
+
def to_dict(self):
|
319 |
+
"""Converts the Eval Result to a dict compatible with our dataframe display"""
|
320 |
+
eval_column = AutoEvalColumnMultimodal if self.track.lower() == "multimodal" else AutoEvalColumn
|
321 |
+
vision_tasks = ("VQA", "Winoground", "DevBench", "vqa", "winoground", "devbench")
|
322 |
+
num_text_tasks = len(Tasks)
|
323 |
+
text_average = sum([v for k, v in self.results.items() if v is not None and k not in vision_tasks]) / num_text_tasks
|
324 |
+
if self.still_on_hub:
|
325 |
+
model_display_name = make_clickable_model(self.repo_id, self.full_model)
|
326 |
+
else:
|
327 |
+
model_display_name = self.full_model
|
328 |
+
data_dict = {
|
329 |
+
"eval_name": self.eval_name, # not a column, just a save name,
|
330 |
+
eval_column.model.name: model_display_name,
|
331 |
+
eval_column.hf_repo.name: self.repo_id,
|
332 |
+
eval_column.revision.name: self.revision,
|
333 |
+
eval_column.text_average.name: text_average,
|
334 |
+
eval_column.still_on_hub.name: self.still_on_hub,
|
335 |
+
}
|
336 |
+
|
337 |
+
if self.track.lower() == "multimodal":
|
338 |
+
taskset = TasksMultimodal
|
339 |
+
num_vision_tasks = len(TasksMultimodal) - len(Tasks)
|
340 |
+
vision_average = sum([v for k, v in self.results.items() if v is not None and k in vision_tasks]) / num_vision_tasks
|
341 |
+
data_dict[eval_column.vision_average.name] = vision_average
|
342 |
+
else:
|
343 |
+
taskset = Tasks
|
344 |
+
for task in taskset:
|
345 |
+
data_dict[task.value.col_name] = self.results[task.value.benchmark]
|
346 |
+
|
347 |
+
return data_dict
|
348 |
+
|
349 |
+
|
350 |
+
|
351 |
+
def get_request_file_for_model(requests_path, model_name, track):
|
352 |
+
"""Selects the correct request file for a given model. Only keeps runs tagged as FINISHED"""
|
353 |
+
request_files = os.path.join(
|
354 |
+
requests_path,
|
355 |
+
f"{model_name}_eval_request_*.json",
|
356 |
+
)
|
357 |
+
request_files = glob.glob(request_files)
|
358 |
+
|
359 |
+
# Select correct request file (track)
|
360 |
+
request_file = ""
|
361 |
+
request_files = sorted(request_files, reverse=True)
|
362 |
+
for tmp_request_file in request_files:
|
363 |
+
with open(tmp_request_file, "r") as f:
|
364 |
+
req_content = json.load(f)
|
365 |
+
if (
|
366 |
+
req_content["status"] in ["FINISHED"]
|
367 |
+
):
|
368 |
+
request_file = tmp_request_file
|
369 |
+
return request_file
|
370 |
+
|
371 |
+
|
372 |
+
|
373 |
+
def get_raw_eval_results(results_path: str, requests_path: str) -> list[EvalResult]:
|
374 |
+
"""From the path of the results folder root, extract all needed info for results"""
|
375 |
+
model_result_filepaths = []
|
376 |
+
|
377 |
+
print(f"results_path is {results_path}")
|
378 |
+
|
379 |
+
for root, dirnames, files in os.walk(results_path):
|
380 |
+
print(f"root is {root}, dirnames is {dirnames}, files is {files}")
|
381 |
+
# We should only have json files in model results
|
382 |
+
if len(files) == 0 or any([not f.endswith(".json") for f in files]):
|
383 |
+
continue
|
384 |
+
|
385 |
+
# Sort the files by date
|
386 |
+
try:
|
387 |
+
files.sort(key=lambda x: x.removesuffix(".json").removeprefix("results_")[:-7])
|
388 |
+
except dateutil.parser._parser.ParserError:
|
389 |
+
files = [files[-1]]
|
390 |
+
|
391 |
+
for file in files:
|
392 |
+
model_result_filepaths.append(os.path.join(root, file))
|
393 |
+
|
394 |
+
print(f"model_result_filepaths is {model_result_filepaths}")
|
395 |
+
|
396 |
+
eval_results = {}
|
397 |
+
for model_result_filepath in model_result_filepaths:
|
398 |
+
# Creation of result
|
399 |
+
eval_result = EvalResult.init_from_json_file(model_result_filepath)
|
400 |
+
eval_result.update_with_request_file(requests_path)
|
401 |
+
|
402 |
+
# Store results of same eval together
|
403 |
+
eval_name = eval_result.eval_name
|
404 |
+
if eval_name in eval_results.keys():
|
405 |
+
eval_results[eval_name].results.update({k: v for k, v in eval_result.results.items() if v is not None})
|
406 |
+
else:
|
407 |
+
eval_results[eval_name] = eval_result
|
408 |
+
|
409 |
+
results = []
|
410 |
+
for v in eval_results.values():
|
411 |
+
try:
|
412 |
+
v.to_dict() # we test if the dict version is complete
|
413 |
+
results.append(v)
|
414 |
+
except KeyError: # not all eval values present
|
415 |
+
continue
|
416 |
+
|
417 |
+
return results
|
418 |
+
|
419 |
+
|
420 |
+
|
421 |
+
|
422 |
+
|
423 |
+
|
424 |
+
# def get_raw_eval_results_mib(results_path: str) -> List[EvalResult_MIB]:
|
425 |
+
# """Extract all evaluation results from the results folder"""
|
426 |
+
# model_result_filepaths = []
|
427 |
+
|
428 |
+
# print(f"results_path is {results_path}")
|
429 |
+
|
430 |
+
# for root, dirnames, files in os.walk(results_path):
|
431 |
+
# print(f"root is {root}, dirnames is {dirnames}, files is {files}")
|
432 |
+
# if len(files) == 0 or any([not f.endswith(".json") for f in files]):
|
433 |
+
# continue
|
434 |
+
|
435 |
+
# files.sort()
|
436 |
+
# for file in files:
|
437 |
+
# model_result_filepaths.append(os.path.join(root, file))
|
438 |
+
|
439 |
+
# print(f"model_result_filepaths is {model_result_filepaths}")
|
440 |
+
|
441 |
+
# eval_results = []
|
442 |
+
# for model_result_filepath in model_result_filepaths:
|
443 |
+
# try:
|
444 |
+
# eval_result = EvalResult_MIB("", "", {}) # Create empty instance
|
445 |
+
# result = eval_result.init_from_json_file(model_result_filepath)
|
446 |
+
# # Verify the result can be converted to dict format
|
447 |
+
# result.to_dict()
|
448 |
+
# eval_results.append(result)
|
449 |
+
# except Exception as e:
|
450 |
+
# print(f"Error processing {model_result_filepath}: {e}")
|
451 |
+
# continue
|
452 |
+
|
453 |
+
# return eval_results
|
454 |
+
|
455 |
+
def get_raw_eval_results_mib(results_path: str, requests_path: str) -> List[EvalResult_MIB]:
|
456 |
+
"""From the path of the results folder root, extract all needed info for MIB results"""
|
457 |
+
model_result_filepaths = []
|
458 |
+
|
459 |
+
print(f"results_path is {results_path}")
|
460 |
+
|
461 |
+
for root, dirnames, files in os.walk(results_path):
|
462 |
+
print(f"root is {root}, dirnames is {dirnames}, files is {files}")
|
463 |
+
# We should only have json files in model results
|
464 |
+
if len(files) == 0 or any([not f.endswith(".json") for f in files]):
|
465 |
+
continue
|
466 |
+
|
467 |
+
# Sort the files by date - keeping original sorting logic
|
468 |
+
try:
|
469 |
+
files.sort(key=lambda x: x.removesuffix(".json").removeprefix("results_")[:-7])
|
470 |
+
except dateutil.parser._parser.ParserError:
|
471 |
+
files = [files[-1]]
|
472 |
+
|
473 |
+
for file in files:
|
474 |
+
model_result_filepaths.append(os.path.join(root, file))
|
475 |
+
|
476 |
+
print(f"model_result_filepaths is {model_result_filepaths}")
|
477 |
+
|
478 |
+
eval_results = []
|
479 |
+
for model_result_filepath in model_result_filepaths:
|
480 |
+
try:
|
481 |
+
eval_result = EvalResult_MIB("", "", {}) # Create empty instance
|
482 |
+
result = eval_result.init_from_json_file(model_result_filepath)
|
483 |
+
print(f"eval_result.init_from_json_file(model_result_filepath) is {result}")
|
484 |
+
# Verify the result can be converted to dict format
|
485 |
+
result.to_dict()
|
486 |
+
eval_results.append(result)
|
487 |
+
except Exception as e:
|
488 |
+
print(f"Error processing {model_result_filepath}: {e}")
|
489 |
+
continue
|
490 |
+
|
491 |
+
return eval_results
|
492 |
+
|
493 |
+
|
494 |
+
|
495 |
+
|
496 |
+
|
497 |
+
|
498 |
+
|
499 |
+
|
500 |
+
|
501 |
+
|
502 |
+
|
503 |
+
|
504 |
+
|
505 |
+
|
506 |
+
|
507 |
+
|
508 |
+
|
509 |
+
|
510 |
+
# from dataclasses import dataclass
|
511 |
+
# from enum import Enum
|
512 |
+
# from typing import Dict, List, Any
|
513 |
+
|
514 |
+
# @dataclass
|
515 |
+
# class Task:
|
516 |
+
# benchmark: str
|
517 |
+
# metrics: list[str]
|
518 |
+
# col_name: str
|
519 |
+
|
520 |
+
# def get_model_ids(self, results: Dict) -> List[str]:
|
521 |
+
# """Extract model IDs from results"""
|
522 |
+
# try:
|
523 |
+
# return [result["model_id"] for result in results["results"]]
|
524 |
+
# except (KeyError, TypeError):
|
525 |
+
# return []
|
526 |
+
|
527 |
+
# class TasksMIB(Enum):
|
528 |
+
# task0 = Task("ioi", ["edge_counts", "faithfulness"], "Indirect Object Identification")
|
529 |
+
# task1 = Task("mcqa", ["edge_counts", "faithfulness"], "Multiple Choice QA")
|
530 |
+
|
531 |
+
# @classmethod
|
532 |
+
# def get_models(cls, results: Dict) -> List[str]:
|
533 |
+
# """Class method to get model IDs using any task"""
|
534 |
+
# # Since model IDs are common across tasks, we can use any task to extract them
|
535 |
+
# return cls.task0.value.get_model_ids(results)
|
536 |
+
|
537 |
+
# # Example usage:
|
538 |
+
# results = {
|
539 |
+
# "method_name": "EAP-IG (mean)",
|
540 |
+
# "results": [
|
541 |
+
# {"model_id": "meta-llama/Llama-3.1-8B", "scores": {}},
|
542 |
+
# {"model_id": "Qwen/Qwen2-1.5B", "scores": {}}
|
543 |
+
# ]
|
544 |
+
# }
|
545 |
+
|
546 |
+
# # Get models using TasksMIB
|
547 |
+
# model_ids = TasksMIB.get_models(results)
|
548 |
+
# print(model_ids) # ['meta-llama/Llama-3.1-8B', 'Qwen/Qwen2-1.5B']
|
549 |
+
|
550 |
+
|
551 |
+
from dataclasses import dataclass
|
552 |
+
from enum import Enum
|
553 |
+
from typing import Dict, List, Tuple
|
554 |
+
|
555 |
+
@dataclass
|
556 |
+
class Task:
|
557 |
+
benchmark: str
|
558 |
+
metrics: list[str]
|
559 |
+
col_name: str
|
560 |
+
|
561 |
+
def get_method_results(self, results: Dict) -> List[Tuple[str, str, Dict]]:
|
562 |
+
"""
|
563 |
+
Extract (method_name, model_id, scores) tuples from results
|
564 |
+
|
565 |
+
Args:
|
566 |
+
results (Dict): Results dictionary containing method_name and results
|
567 |
+
|
568 |
+
Returns:
|
569 |
+
List[Tuple[str, str, Dict]]: List of (method_name, model_id, scores) tuples
|
570 |
+
"""
|
571 |
+
method_name = results.get("method_name", "unknown")
|
572 |
+
try:
|
573 |
+
return [
|
574 |
+
(method_name, result["model_id"], result["scores"])
|
575 |
+
for result in results["results"]
|
576 |
+
]
|
577 |
+
except (KeyError, TypeError):
|
578 |
+
return []
|
579 |
+
|
580 |
+
class TasksMIB(Enum):
|
581 |
+
task0 = Task("ioi", ["edge_counts", "faithfulness"], "Indirect Object Identification")
|
582 |
+
task1 = Task("mcqa", ["edge_counts", "faithfulness"], "Multiple Choice QA")
|
583 |
+
|
584 |
+
@classmethod
|
585 |
+
def get_method_model_pairs(cls, results: Dict) -> List[Tuple[str, str]]:
|
586 |
+
"""Get all (method_name, model_id) pairs from results"""
|
587 |
+
return [(pair[0], pair[1]) for pair in cls.task0.value.get_method_results(results)]
|
588 |
+
|
589 |
+
# Example usage:
|
590 |
+
results = {
|
591 |
+
"method_name": "EAP-IG (mean)",
|
592 |
+
"results": [
|
593 |
+
{"model_id": "meta-llama/Llama-3.1-8B", "scores": {}},
|
594 |
+
{"model_id": "Qwen/Qwen2-1.5B", "scores": {}}
|
595 |
+
]
|
596 |
+
}
|
597 |
+
|
598 |
+
# Get method-model pairs
|
599 |
+
method_model_pairs = TasksMIB.get_method_model_pairs(results)
|
600 |
+
print(method_model_pairs)
|
601 |
+
# [('EAP-IG (mean)', 'meta-llama/Llama-3.1-8B'), ('EAP-IG (mean)', 'Qwen/Qwen2-1.5B')]
|
602 |
+
|
603 |
+
# Get full results including scores
|
604 |
+
full_results = TasksMIB.task0.value.get_method_results(results)
|
605 |
+
for method_name, model_id, scores in full_results:
|
606 |
+
print(f"Method: {method_name}, Model: {model_id}")
|
607 |
+
print(f"Scores: {scores}")
|
src/populate.py
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
|
4 |
+
import pandas as pd
|
5 |
+
|
6 |
+
from src.display.formatting import has_no_nan_values, make_clickable_model
|
7 |
+
from src.display.utils import AutoEvalColumn, AutoEvalColumnMultimodal, EvalQueueColumn
|
8 |
+
from src.leaderboard.read_evals import get_raw_eval_results, get_raw_eval_results_mib
|
9 |
+
|
10 |
+
|
11 |
+
def get_leaderboard_df(results_path: str, requests_path: str, cols: list, benchmark_cols: list) -> pd.DataFrame:
|
12 |
+
"""Creates a dataframe from all the individual experiment results"""
|
13 |
+
print(f"results_path is {results_path}, requests_path is {requests_path}")
|
14 |
+
raw_data = get_raw_eval_results(results_path, requests_path)
|
15 |
+
print(f"raw_data is {raw_data}")
|
16 |
+
all_data_json = [v.to_dict() for v in raw_data]
|
17 |
+
print(f"all_data_json is {pd.DataFrame.from_records(all_data_json)}")
|
18 |
+
all_data_json_filtered = []
|
19 |
+
for item in all_data_json:
|
20 |
+
item["Track"] = item["eval_name"].split("_")[-1]
|
21 |
+
item["ioi"] = 0
|
22 |
+
item["mcqa"] = 0
|
23 |
+
if "VQA" in benchmark_cols and "VQA" in item:
|
24 |
+
all_data_json_filtered.append(item)
|
25 |
+
if "VQA" not in benchmark_cols and "VQA" not in item:
|
26 |
+
all_data_json_filtered.append(item)
|
27 |
+
|
28 |
+
all_data_json = all_data_json_filtered
|
29 |
+
|
30 |
+
df = pd.DataFrame.from_records(all_data_json)
|
31 |
+
df = df.sort_values(by=[AutoEvalColumn.text_average.name], ascending=False)
|
32 |
+
# df = df.sort_values(by=[Tasks.task0.value.col_name], ascending=False)
|
33 |
+
# df = df.sort_values(by=[AutoEvalColumn.track.name], ascending=False)
|
34 |
+
|
35 |
+
print(f"df is {df}")
|
36 |
+
|
37 |
+
# df = df[cols].round(decimals=1)
|
38 |
+
|
39 |
+
# filter out if any of the benchmarks have not been produced
|
40 |
+
df = df[has_no_nan_values(df, benchmark_cols)]
|
41 |
+
return df
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
# def get_leaderboard_df_mib(results_path: str, requests_path: str, cols: list, benchmark_cols: list) -> pd.DataFrame:
|
46 |
+
# """Creates a dataframe from all the individual experiment results"""
|
47 |
+
# print(f"results_path is {results_path}, requests_path is {requests_path}")
|
48 |
+
# raw_data = get_raw_eval_results(results_path, requests_path)
|
49 |
+
# print(f"raw_data is {raw_data}")
|
50 |
+
# all_data_json = [v.to_dict() for v in raw_data]
|
51 |
+
# print(f"all_data_json is {pd.DataFrame.from_records(all_data_json)}")
|
52 |
+
# all_data_json_filtered = []
|
53 |
+
# for item in all_data_json:
|
54 |
+
# item["Track"] = item["eval_name"].split("_")[-1]
|
55 |
+
# if "VQA" in benchmark_cols and "VQA" in item:
|
56 |
+
# all_data_json_filtered.append(item)
|
57 |
+
# if "VQA" not in benchmark_cols and "VQA" not in item:
|
58 |
+
# all_data_json_filtered.append(item)
|
59 |
+
# all_data_json_filtered.append(item)
|
60 |
+
|
61 |
+
# all_data_json = all_data_json_filtered
|
62 |
+
|
63 |
+
# df = pd.DataFrame.from_records(all_data_json)
|
64 |
+
# df = df.sort_values(by=[AutoEvalColumn.text_average.name], ascending=False)
|
65 |
+
|
66 |
+
# print(f"df is {df}")
|
67 |
+
|
68 |
+
# df = df[cols].round(decimals=1)
|
69 |
+
|
70 |
+
# # filter out if any of the benchmarks have not been produced
|
71 |
+
# df = df[has_no_nan_values(df, benchmark_cols)]
|
72 |
+
# return df
|
73 |
+
|
74 |
+
def get_leaderboard_df_mib(results_path: str, requests_path: str, cols: list, benchmark_cols: list) -> pd.DataFrame:
|
75 |
+
"""Creates a dataframe from all the MIB experiment results"""
|
76 |
+
print(f"results_path is {results_path}, requests_path is {requests_path}")
|
77 |
+
raw_data = get_raw_eval_results_mib(results_path, requests_path)
|
78 |
+
print(f"raw_data is {raw_data}")
|
79 |
+
|
80 |
+
# Convert each result to dict format
|
81 |
+
all_data_json = [v.to_dict() for v in raw_data]
|
82 |
+
print(f"all_data_json is {pd.DataFrame.from_records(all_data_json)}")
|
83 |
+
|
84 |
+
# Convert to dataframe
|
85 |
+
df = pd.DataFrame.from_records(all_data_json)
|
86 |
+
|
87 |
+
# Sort by Average score descending
|
88 |
+
if 'Average' in df.columns:
|
89 |
+
# Convert '-' to NaN for sorting purposes
|
90 |
+
df['Average'] = pd.to_numeric(df['Average'], errors='coerce')
|
91 |
+
df = df.sort_values(by=['Average'], ascending=False, na_position='last')
|
92 |
+
# Convert NaN back to '-'
|
93 |
+
df['Average'] = df['Average'].fillna('-')
|
94 |
+
|
95 |
+
return df
|
96 |
+
|
97 |
+
|
98 |
+
def get_evaluation_queue_df(save_path: str, cols: list) -> list[pd.DataFrame]:
|
99 |
+
"""Creates the different dataframes for the evaluation queues requests"""
|
100 |
+
entries = [entry for entry in os.listdir(save_path) if not entry.startswith(".")]
|
101 |
+
all_evals = []
|
102 |
+
|
103 |
+
for entry in entries:
|
104 |
+
if ".json" in entry:
|
105 |
+
file_path = os.path.join(save_path, entry)
|
106 |
+
with open(file_path) as fp:
|
107 |
+
data = json.load(fp)
|
108 |
+
|
109 |
+
if "still_on_hub" in data and data["still_on_hub"]:
|
110 |
+
data[EvalQueueColumn.model.name] = make_clickable_model(data["hf_repo"], data["model"])
|
111 |
+
data[EvalQueueColumn.revision.name] = data.get("revision", "main")
|
112 |
+
else:
|
113 |
+
data[EvalQueueColumn.model.name] = data["model"]
|
114 |
+
data[EvalQueueColumn.revision.name] = "N/A"
|
115 |
+
|
116 |
+
all_evals.append(data)
|
117 |
+
elif ".md" not in entry:
|
118 |
+
# this is a folder
|
119 |
+
sub_entries = [e for e in os.listdir(f"{save_path}/{entry}") if os.path.isfile(e) and not e.startswith(".")]
|
120 |
+
for sub_entry in sub_entries:
|
121 |
+
file_path = os.path.join(save_path, entry, sub_entry)
|
122 |
+
with open(file_path) as fp:
|
123 |
+
data = json.load(fp)
|
124 |
+
|
125 |
+
data[EvalQueueColumn.model.name] = make_clickable_model(data["model"])
|
126 |
+
data[EvalQueueColumn.revision.name] = data.get("revision", "main")
|
127 |
+
all_evals.append(data)
|
128 |
+
|
129 |
+
pending_list = [e for e in all_evals if e["status"] in ["PENDING", "RERUN"]]
|
130 |
+
running_list = [e for e in all_evals if e["status"] == "RUNNING"]
|
131 |
+
finished_list = [e for e in all_evals if e["status"].startswith("FINISHED") or e["status"] == "PENDING_NEW_EVAL"]
|
132 |
+
df_pending = pd.DataFrame.from_records(pending_list, columns=cols)
|
133 |
+
df_running = pd.DataFrame.from_records(running_list, columns=cols)
|
134 |
+
df_finished = pd.DataFrame.from_records(finished_list, columns=cols)
|
135 |
+
return df_finished[cols], df_running[cols], df_pending[cols]
|
src/submission/check_validity.py
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
import re
|
4 |
+
import numpy as np
|
5 |
+
from collections import defaultdict
|
6 |
+
from datetime import datetime, timedelta, timezone
|
7 |
+
|
8 |
+
import huggingface_hub
|
9 |
+
from huggingface_hub import ModelCard
|
10 |
+
from huggingface_hub.hf_api import ModelInfo
|
11 |
+
from transformers import AutoConfig
|
12 |
+
from transformers.models.auto.tokenization_auto import AutoTokenizer
|
13 |
+
|
14 |
+
from src.display.utils import TEXT_TASKS, VISION_TASKS, NUM_EXPECTED_EXAMPLES
|
15 |
+
|
16 |
+
def check_model_card(repo_id: str) -> tuple[bool, str]:
|
17 |
+
"""Checks if the model card and license exist and have been filled"""
|
18 |
+
try:
|
19 |
+
card = ModelCard.load(repo_id)
|
20 |
+
except huggingface_hub.utils.EntryNotFoundError:
|
21 |
+
return False, "Please add a model card to your model to explain how you trained/fine-tuned it."
|
22 |
+
|
23 |
+
# Enforce license metadata
|
24 |
+
if card.data.license is None:
|
25 |
+
if not ("license_name" in card.data and "license_link" in card.data):
|
26 |
+
return False, (
|
27 |
+
"License not found. Please add a license to your model card using the `license` metadata or a"
|
28 |
+
" `license_name`/`license_link` pair."
|
29 |
+
)
|
30 |
+
|
31 |
+
# Enforce card content
|
32 |
+
if len(card.text) < 200:
|
33 |
+
return False, "Please add a description to your model card, it is too short."
|
34 |
+
|
35 |
+
return True, ""
|
36 |
+
|
37 |
+
def is_model_on_hub(model_name: str, revision: str, token: str = None, trust_remote_code=False, test_tokenizer=False) -> tuple[bool, str]:
|
38 |
+
"""Checks if the model model_name is on the hub, and whether it (and its tokenizer) can be loaded with AutoClasses."""
|
39 |
+
try:
|
40 |
+
config = AutoConfig.from_pretrained(model_name, revision=revision, trust_remote_code=trust_remote_code, token=token)
|
41 |
+
if test_tokenizer:
|
42 |
+
try:
|
43 |
+
tk = AutoTokenizer.from_pretrained(model_name, revision=revision, trust_remote_code=trust_remote_code, token=token)
|
44 |
+
except ValueError as e:
|
45 |
+
return (
|
46 |
+
False,
|
47 |
+
f"uses a tokenizer which is not in a transformers release: {e}",
|
48 |
+
None
|
49 |
+
)
|
50 |
+
except Exception as e:
|
51 |
+
return (False, "'s tokenizer cannot be loaded. Is your tokenizer class in a stable transformers release, and correctly configured?", None)
|
52 |
+
return True, None, config
|
53 |
+
|
54 |
+
except ValueError:
|
55 |
+
return (
|
56 |
+
False,
|
57 |
+
"needs to be launched with `trust_remote_code=True`. For safety reason, we do not allow these models to be automatically submitted to the leaderboard.",
|
58 |
+
None
|
59 |
+
)
|
60 |
+
|
61 |
+
except Exception as e:
|
62 |
+
return False, "was not found on hub!", None
|
63 |
+
|
64 |
+
|
65 |
+
def get_model_size(model_info: ModelInfo, precision: str):
|
66 |
+
"""Gets the model size from the configuration, or the model name if the configuration does not contain the information."""
|
67 |
+
try:
|
68 |
+
model_size = round(model_info.safetensors["total"] / 1e9, 3)
|
69 |
+
except (AttributeError, TypeError):
|
70 |
+
return 0 # Unknown model sizes are indicated as 0, see NUMERIC_INTERVALS in app.py
|
71 |
+
|
72 |
+
size_factor = 8 if (precision == "GPTQ" or "gptq" in model_info.modelId.lower()) else 1
|
73 |
+
model_size = size_factor * model_size
|
74 |
+
return model_size
|
75 |
+
|
76 |
+
def get_model_arch(model_info: ModelInfo):
|
77 |
+
"""Gets the model architecture from the configuration"""
|
78 |
+
return model_info.config.get("architectures", "Unknown")
|
79 |
+
|
80 |
+
def already_submitted_models(requested_models_dir: str) -> set[str]:
|
81 |
+
"""Gather a list of already submitted models to avoid duplicates"""
|
82 |
+
depth = 1
|
83 |
+
file_names = []
|
84 |
+
users_to_submission_dates = defaultdict(list)
|
85 |
+
|
86 |
+
for root, _, files in os.walk(requested_models_dir):
|
87 |
+
current_depth = root.count(os.sep) - requested_models_dir.count(os.sep)
|
88 |
+
if current_depth == depth:
|
89 |
+
for file in files:
|
90 |
+
if not file.endswith(".json"):
|
91 |
+
continue
|
92 |
+
with open(os.path.join(root, file), "r") as f:
|
93 |
+
info = json.load(f)
|
94 |
+
file_names.append(f"{info['model']}_{info['revision']}_{info['track']}")
|
95 |
+
|
96 |
+
# Select organisation
|
97 |
+
if info["model"].count("/") == 0 or "submitted_time" not in info:
|
98 |
+
continue
|
99 |
+
organisation, _ = info["model"].split("/")
|
100 |
+
users_to_submission_dates[organisation].append(info["submitted_time"])
|
101 |
+
|
102 |
+
return set(file_names), users_to_submission_dates
|
103 |
+
|
104 |
+
def is_valid_predictions(predictions: dict) -> tuple[bool, str]:
|
105 |
+
out_msg = ""
|
106 |
+
for task in TEXT_TASKS:
|
107 |
+
if task not in predictions:
|
108 |
+
out_msg = f"Error: {task} not present"
|
109 |
+
break
|
110 |
+
for subtask in TEXT_TASKS[task]:
|
111 |
+
if subtask not in predictions[task]:
|
112 |
+
out_msg = f"Error: {subtask} not present under {task}"
|
113 |
+
break
|
114 |
+
if out_msg != "":
|
115 |
+
break
|
116 |
+
if "vqa" in predictions or "winoground" in predictions or "devbench" in predictions:
|
117 |
+
for task in VISION_TASKS:
|
118 |
+
if task not in predictions:
|
119 |
+
out_msg = f"Error: {task} not present"
|
120 |
+
break
|
121 |
+
for subtask in VISION_TASKS[task]:
|
122 |
+
if subtask not in predictions[task]:
|
123 |
+
out_msg = f"Error: {subtask} not present under {task}"
|
124 |
+
break
|
125 |
+
if out_msg != "":
|
126 |
+
break
|
127 |
+
|
128 |
+
# Make sure all examples have predictions, and that predictions are the correct type
|
129 |
+
for task in predictions:
|
130 |
+
for subtask in predictions[task]:
|
131 |
+
if task == "devbench":
|
132 |
+
a = np.array(predictions[task][subtask]["predictions"])
|
133 |
+
if subtask == "sem-things":
|
134 |
+
required_shape = (1854, 1854)
|
135 |
+
elif subtask == "gram-trog":
|
136 |
+
required_shape = (76, 4, 1)
|
137 |
+
elif subtask == "lex-viz_vocab":
|
138 |
+
required_shape = (119, 4, 1)
|
139 |
+
if a.shape[0] != required_shape[0] or a.shape[1] != required_shape[1]:
|
140 |
+
out_msg = f"Error: Wrong shape for results for `{subtask}` in `{task}`."
|
141 |
+
break
|
142 |
+
if not str(a.dtype).startswith("float"):
|
143 |
+
out_msg = f"Error: Results for `{subtask}` ({task}) \
|
144 |
+
should be floats but aren't."
|
145 |
+
break
|
146 |
+
continue
|
147 |
+
|
148 |
+
num_expected_examples = NUM_EXPECTED_EXAMPLES[task][subtask]
|
149 |
+
if len(predictions[task][subtask]["predictions"]) != num_expected_examples:
|
150 |
+
out_msg = f"Error: {subtask} has the wrong number of examples."
|
151 |
+
break
|
152 |
+
|
153 |
+
if task == "glue":
|
154 |
+
if type(predictions[task][subtask]["predictions"][0]["pred"]) != int:
|
155 |
+
out_msg = f"Error: results for `{subtask}` (`{task}`) should be integers but aren't."
|
156 |
+
break
|
157 |
+
else:
|
158 |
+
if type(predictions[task][subtask]["predictions"][0]["pred"]) != str:
|
159 |
+
out_msg = f"Error: results for `{subtask}` (`{task}`) should be strings but aren't."
|
160 |
+
break
|
161 |
+
|
162 |
+
if out_msg != "":
|
163 |
+
break
|
164 |
+
|
165 |
+
if out_msg != "":
|
166 |
+
return False, out_msg
|
167 |
+
return True, "Upload successful."
|
src/submission/submit.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
from datetime import datetime, timezone
|
4 |
+
|
5 |
+
from src.display.formatting import styled_error, styled_message, styled_warning
|
6 |
+
from src.envs import API, EVAL_REQUESTS_PATH, TOKEN, QUEUE_REPO
|
7 |
+
from src.submission.check_validity import (
|
8 |
+
already_submitted_models,
|
9 |
+
check_model_card,
|
10 |
+
get_model_size,
|
11 |
+
is_model_on_hub,
|
12 |
+
is_valid_predictions,
|
13 |
+
)
|
14 |
+
|
15 |
+
REQUESTED_MODELS = None
|
16 |
+
USERS_TO_SUBMISSION_DATES = None
|
17 |
+
|
18 |
+
def add_new_eval(
|
19 |
+
model_name: str,
|
20 |
+
model_id: str,
|
21 |
+
revision: str,
|
22 |
+
track: str,
|
23 |
+
predictions: dict,
|
24 |
+
):
|
25 |
+
global REQUESTED_MODELS
|
26 |
+
global USERS_TO_SUBMISSION_DATES
|
27 |
+
if not REQUESTED_MODELS:
|
28 |
+
REQUESTED_MODELS, USERS_TO_SUBMISSION_DATES = already_submitted_models(EVAL_REQUESTS_PATH)
|
29 |
+
|
30 |
+
out_message = ""
|
31 |
+
|
32 |
+
user_name = ""
|
33 |
+
model_path = model_name
|
34 |
+
if "/" in model_name:
|
35 |
+
user_name = model_name.split("/")[0]
|
36 |
+
model_path = model_name.split("/")[1]
|
37 |
+
|
38 |
+
current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
39 |
+
|
40 |
+
if track is None:
|
41 |
+
return styled_error("Please select a track.")
|
42 |
+
|
43 |
+
# Does the model actually exist?
|
44 |
+
if revision == "":
|
45 |
+
revision = "main"
|
46 |
+
|
47 |
+
out_message = ""
|
48 |
+
|
49 |
+
# Is the model info correctly filled?
|
50 |
+
print("Made it before 1")
|
51 |
+
try:
|
52 |
+
model_info = API.model_info(repo_id=model_id, revision=revision)
|
53 |
+
except Exception:
|
54 |
+
out_message += styled_warning("Could not get your model information. The leaderboard entry will not have a link to its HF repo.") + "<br>"
|
55 |
+
print("Made it after 1")
|
56 |
+
|
57 |
+
try:
|
58 |
+
predictions_OK, error_msg = is_valid_predictions(predictions)
|
59 |
+
if not predictions_OK:
|
60 |
+
return styled_error(error_msg) + "<br>"
|
61 |
+
except:
|
62 |
+
return styled_error(error_msg) + "<br>"
|
63 |
+
|
64 |
+
print("Made it after 3")
|
65 |
+
|
66 |
+
# Seems good, creating the eval
|
67 |
+
print("Adding new eval")
|
68 |
+
|
69 |
+
eval_entry = {
|
70 |
+
"model_name": model_name,
|
71 |
+
"hf_repo": model_id,
|
72 |
+
"revision": revision,
|
73 |
+
"track": track,
|
74 |
+
"predictions": predictions,
|
75 |
+
"status": "PENDING",
|
76 |
+
"submitted_time": current_time,
|
77 |
+
}
|
78 |
+
|
79 |
+
print("Made it after 4")
|
80 |
+
|
81 |
+
# Check for duplicate submission
|
82 |
+
if f"{model_name}_{revision}_{track}" in REQUESTED_MODELS:
|
83 |
+
return styled_error("A model with this name has been already submitted.")
|
84 |
+
|
85 |
+
print("Creating eval file")
|
86 |
+
OUT_DIR = f"{EVAL_REQUESTS_PATH}/{user_name}"
|
87 |
+
os.makedirs(OUT_DIR, exist_ok=True)
|
88 |
+
out_path = f"{OUT_DIR}/{model_path}_{revision}_eval_request_False_{track}.json"
|
89 |
+
|
90 |
+
print("Made it after 5")
|
91 |
+
|
92 |
+
with open(out_path, "w") as f:
|
93 |
+
f.write(json.dumps(eval_entry))
|
94 |
+
|
95 |
+
print("Uploading eval file")
|
96 |
+
API.upload_file(
|
97 |
+
path_or_fileobj=out_path,
|
98 |
+
path_in_repo=out_path.split("eval-queue/")[1],
|
99 |
+
repo_id=QUEUE_REPO,
|
100 |
+
repo_type="dataset",
|
101 |
+
commit_message=f"Add {model_name} to eval queue",
|
102 |
+
)
|
103 |
+
|
104 |
+
print("Made it after 6")
|
105 |
+
|
106 |
+
# Remove the local file
|
107 |
+
os.remove(out_path)
|
108 |
+
|
109 |
+
return styled_message(
|
110 |
+
"Your request has been submitted to the evaluation queue!\nPlease wait for up to an hour for the request to show in the PENDING list."
|
111 |
+
)
|