Spaces:
Running
Running
Alvaro Romo
commited on
Commit
·
c2f297a
1
Parent(s):
db0086f
Fixed logging messages and refactor code. Added log in private dataset
Browse files- .gitignore +17 -0
- app.py +115 -134
- src/__pycache__/__init__.cpython-310.pyc +0 -0
- src/__pycache__/__init__.cpython-38.pyc +0 -0
- src/__pycache__/check_validity.cpython-310.pyc +0 -0
- src/__pycache__/check_validity.cpython-38.pyc +0 -0
- src/__pycache__/submit.cpython-310.pyc +0 -0
- src/check_validity.py +200 -34
- src/submit.py +3 -1
.gitignore
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
venv/
|
2 |
+
.venv/
|
3 |
+
.mypy_cache/
|
4 |
+
.idea/
|
5 |
+
*/__pycache__/*
|
6 |
+
.env
|
7 |
+
.ipynb_checkpoints
|
8 |
+
*ipynb
|
9 |
+
.vscode/
|
10 |
+
.DS_Store
|
11 |
+
.ruff_cache/
|
12 |
+
.python-version
|
13 |
+
.profile_app.python
|
14 |
+
*pstats
|
15 |
+
*.lock
|
16 |
+
|
17 |
+
user_request/
|
app.py
CHANGED
@@ -1,122 +1,74 @@
|
|
1 |
-
import
|
2 |
-
import pandas as pd
|
3 |
-
import re
|
4 |
-
from datasets import load_dataset
|
5 |
-
import src.check_validity as cv
|
6 |
-
from src.submit import ModelSizeChecker
|
7 |
import os
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
|
|
|
|
|
|
|
|
|
12 |
|
13 |
-
|
14 |
-
"""
|
15 |
-
Validate model with some checkers to assure tha can be evaluated
|
16 |
-
:param model: hf model name
|
17 |
-
:param precision: model parameters data type
|
18 |
-
:param base_model: base model (if it is need it)
|
19 |
-
:param weight_type:
|
20 |
-
:param use_chat_template:
|
21 |
-
:return:
|
22 |
-
"""
|
23 |
-
API = HfApi()
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
except Exception as e:
|
28 |
-
return "Could not get your model information. Please fill it up properly."
|
29 |
-
|
30 |
-
# TODO: think if it makes sense. Maybe we need to allow upload sumissions more than once
|
31 |
-
# # Has it been submitted already?
|
32 |
-
# model_key = f"{model}_{model_info.sha}_{precision}"
|
33 |
-
# if model_key in requested_models:
|
34 |
-
# return st.error(
|
35 |
-
# f"The model '{model}' with revision '{model_info.sha}' and precision '{precision}' has already been submitted.")
|
36 |
-
|
37 |
-
# Check model size early
|
38 |
-
model_size, error_text = cv.get_model_size(model_info=model_info, precision=precision, base_model=base_model)
|
39 |
-
if model_size is None:
|
40 |
-
return error_text
|
41 |
-
|
42 |
-
# Absolute size limit for float16 and bfloat16
|
43 |
-
if precision in ["float16", "bfloat16"] and model_size > 100:
|
44 |
-
error_message = f"Sadly, models larger than 100B parameters cannot be submitted in {precision} precision at this time. " \
|
45 |
-
f"Your model size: {model_size:.2f}B parameters."
|
46 |
-
return error_message
|
47 |
-
|
48 |
-
# Precision-adjusted size limit for 8bit, 4bit, and GPTQ
|
49 |
-
if precision in ["8bit", "4bit", "GPTQ"]:
|
50 |
-
size_checker = ModelSizeChecker(model=model, precision=precision, model_size_in_b=model_size)
|
51 |
-
|
52 |
-
if not size_checker.can_evaluate():
|
53 |
-
precision_factor = size_checker.get_precision_factor()
|
54 |
-
max_size = 140 * precision_factor
|
55 |
-
error_message = f"Sadly, models this big ({model_size:.2f}B parameters) cannot be evaluated automatically " \
|
56 |
-
f"at the moment on our cluster. The maximum size for {precision} precision is {max_size:.2f}B parameters."
|
57 |
-
return error_message
|
58 |
-
|
59 |
-
architecture = "?"
|
60 |
-
# Is the model on the hub?
|
61 |
-
if weight_type in ["Delta", "Adapter"]:
|
62 |
-
base_model_on_hub, error, _ = cv.is_model_on_hub(
|
63 |
-
model_name=base_model, revision="main", token=None, test_tokenizer=True
|
64 |
-
)
|
65 |
-
if not base_model_on_hub:
|
66 |
-
return f'Base model "{base_model}" {error}'
|
67 |
-
if not weight_type == "Adapter":
|
68 |
-
model_on_hub, error, model_config = cv.is_model_on_hub(model_name=model, revision=model_info.sha,
|
69 |
-
test_tokenizer=True)
|
70 |
-
if not model_on_hub or model_config is None:
|
71 |
-
return f'Model "{model}" {error}'
|
72 |
-
if model_config is not None:
|
73 |
-
architectures = getattr(model_config, "architectures", None)
|
74 |
-
if architectures:
|
75 |
-
architecture = ";".join(architectures)
|
76 |
-
|
77 |
-
# Were the model card and license filled?
|
78 |
-
try:
|
79 |
-
model_info.cardData["license"]
|
80 |
-
except Exception:
|
81 |
-
return "Please select a license for your model"
|
82 |
-
|
83 |
-
modelcard_OK, error_msg, model_card = cv.check_model_card(model)
|
84 |
-
if not modelcard_OK:
|
85 |
-
return error_msg
|
86 |
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
return chat_template_error
|
92 |
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
-
# Function to send email
|
96 |
-
def log_submission(model_name, description, user_contact):
|
97 |
-
# todo: create email or log in dataset
|
98 |
-
...
|
99 |
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
|
103 |
-
def get_url(html_content):
|
104 |
match = re.search(r'href=["\'](https?://[^\s"\']+)', html_content)
|
105 |
if match:
|
106 |
url = match.group(1)
|
107 |
return url
|
108 |
-
|
109 |
-
|
110 |
|
111 |
|
112 |
@st.cache_data
|
113 |
-
def load_data():
|
114 |
try:
|
115 |
-
columns = [
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
data = data[columns]
|
119 |
-
# TODO: check if from submit this is neede it
|
120 |
data["Model"] = data["Model"].apply(get_url)
|
121 |
data.sort_values(by="Average ⬆️", ascending=False, inplace=True)
|
122 |
data.reset_index(drop=True, inplace=True)
|
@@ -126,11 +78,12 @@ def load_data():
|
|
126 |
return pd.DataFrame()
|
127 |
|
128 |
|
|
|
129 |
leaderboard_data = load_data()
|
130 |
-
tabs = st.tabs(["Leaderboard", "Submit model"])
|
131 |
|
132 |
with tabs[0]:
|
133 |
-
# logo
|
134 |
cols_logo = st.columns(5, vertical_alignment="center")
|
135 |
with cols_logo[2]:
|
136 |
st.image("assets/images/hf-logo.png", use_container_width=True)
|
@@ -148,18 +101,16 @@ with tabs[0]:
|
|
148 |
""",
|
149 |
unsafe_allow_html=True,
|
150 |
)
|
151 |
-
leaderboard_cols = st.columns([0.1, 0.8, 0.1], vertical_alignment="center")
|
152 |
-
with leaderboard_cols[1]:
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
else:
|
162 |
-
st.write("No data found to display on leaderboard.")
|
163 |
|
164 |
with tabs[1]:
|
165 |
st.header("Submit model")
|
@@ -189,54 +140,84 @@ with tabs[1]:
|
|
189 |
html_path = "assets/html"
|
190 |
for filename in os.listdir(html_path):
|
191 |
file_path = os.path.join(html_path, filename)
|
192 |
-
with open(file_path,
|
193 |
guide_info_list.append(file.read())
|
194 |
|
195 |
# display adding number id
|
196 |
for i, info_div in enumerate(guide_info_list):
|
197 |
-
st.markdown(get_id_number(i+1) + info_div, unsafe_allow_html=True)
|
198 |
|
199 |
with st.form("submit_model_form"):
|
200 |
-
model_name = st.text_input(
|
201 |
-
|
202 |
-
|
203 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
precision_option = st.selectbox(
|
205 |
"Choose precision format:",
|
206 |
help="Size limits vary by precision: • FP16/BF16: up to 100B parameters • 8-bit: up to 280B parameters (2x) • 4-bit: up to 560B parameters (4x) Choose carefully as incorrect precision can cause evaluation errors.",
|
207 |
options=["float16", "bfloat16", "8bit", "4bit", "GPTQ"],
|
208 |
-
index=0
|
209 |
)
|
210 |
weight_type_option = st.selectbox(
|
211 |
"Select what type of weights are being loaded from the checkpoint provided:",
|
212 |
help="Original: Complete model weights in safetensors format Delta: Weight differences from base model (requires base model for size calculation) Adapter: Lightweight fine-tuning layers (requires base model for size calculation)",
|
213 |
options=["Original", "Adapter", "Delta"],
|
214 |
-
index=0
|
|
|
|
|
|
|
|
|
|
|
215 |
)
|
216 |
-
base_model_name = st.text_input("Base model",
|
217 |
-
help="Required for delta weights or adapters. This information is used to identify the original model and calculate the total parameter count by combining base model and adapter/delta parameters.",
|
218 |
-
value="")
|
219 |
model_type = st.selectbox(
|
220 |
"Choose model type:",
|
221 |
help="🟢 Pretrained: Base models trained on text using masked modeling 🟩 Continuously Pretrained: Extended training on additional corpus 🔶 Fine-tuned: Domain-specific optimization 💬 Chat: Models using RLHF, DPO, or IFT for conversation 🤝 Merge: Combined weights without additional training",
|
222 |
-
options=[
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
)
|
224 |
submit_button = st.form_submit_button("Submit Request")
|
225 |
|
226 |
if submit_button:
|
227 |
# validate model size, license, chat_templates
|
228 |
use_chat_template = True if model_type == "💬 Chat" else False
|
229 |
-
validation_error = validate_model(
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
if validation_error is not None:
|
231 |
st.error(validation_error)
|
232 |
elif not re.match(r"[^@]+@[^@]+\.[^@]+", user_contact):
|
233 |
st.error("Invalid email address.")
|
234 |
else:
|
235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
st.success("Your request has been sent successfully.")
|
237 |
-
|
238 |
-
st.error(
|
239 |
-
|
240 |
-
|
241 |
-
# st.header("Vote for next model")
|
242 |
-
# st.write("Esta sección estará disponible próximamente.")
|
|
|
1 |
+
import json
|
|
|
|
|
|
|
|
|
|
|
2 |
import os
|
3 |
+
import re
|
4 |
+
import uuid
|
5 |
+
from pathlib import Path
|
6 |
|
7 |
+
import pandas as pd
|
8 |
+
import streamlit as st
|
9 |
+
from datasets import load_dataset
|
10 |
+
from huggingface_hub import CommitScheduler
|
11 |
|
12 |
+
from src.check_validity import validate_model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
# define page config
|
15 |
+
st.set_page_config(page_title="IVACE Leaderboard", layout="wide")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
+
# setup scheduler to upload user requests
|
18 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", None)
|
19 |
+
request_file = Path("user_request/") / f"data_{uuid.uuid4()}.json"
|
20 |
+
request_folder = request_file.parent
|
|
|
21 |
|
22 |
+
scheduler = CommitScheduler(
|
23 |
+
repo_id="iberbench/ivace-user-request",
|
24 |
+
repo_type="dataset",
|
25 |
+
private=True,
|
26 |
+
folder_path=request_folder,
|
27 |
+
token=HF_TOKEN,
|
28 |
+
path_in_repo="data",
|
29 |
+
every=10,
|
30 |
+
)
|
31 |
|
|
|
|
|
|
|
|
|
32 |
|
33 |
+
def log_submission(input_dict: dict) -> None:
|
34 |
+
"""
|
35 |
+
Append input/outputs and user feedback to a JSON Lines file using a thread lock to avoid concurrent writes from different users.
|
36 |
+
"""
|
37 |
+
with scheduler.lock:
|
38 |
+
with request_file.open("a") as f:
|
39 |
+
f.write(json.dumps(input_dict))
|
40 |
+
f.write("\n")
|
41 |
|
42 |
|
43 |
+
def get_url(html_content: str) -> str:
|
44 |
match = re.search(r'href=["\'](https?://[^\s"\']+)', html_content)
|
45 |
if match:
|
46 |
url = match.group(1)
|
47 |
return url
|
48 |
+
else:
|
49 |
+
raise ValueError("Url not found in the link")
|
50 |
|
51 |
|
52 |
@st.cache_data
|
53 |
+
def load_data() -> pd.DataFrame:
|
54 |
try:
|
55 |
+
columns = [
|
56 |
+
"eval_name",
|
57 |
+
"Model",
|
58 |
+
"Type",
|
59 |
+
"Average ⬆️",
|
60 |
+
"IFEval",
|
61 |
+
"MMLU-PRO",
|
62 |
+
"GPQA",
|
63 |
+
"MUSR",
|
64 |
+
"CO₂ cost (kg)",
|
65 |
+
]
|
66 |
+
data = (
|
67 |
+
load_dataset("open-llm-leaderboard/contents")["train"]
|
68 |
+
.to_pandas()
|
69 |
+
.head(10)
|
70 |
+
)
|
71 |
data = data[columns]
|
|
|
72 |
data["Model"] = data["Model"].apply(get_url)
|
73 |
data.sort_values(by="Average ⬆️", ascending=False, inplace=True)
|
74 |
data.reset_index(drop=True, inplace=True)
|
|
|
78 |
return pd.DataFrame()
|
79 |
|
80 |
|
81 |
+
# streamlit UI
|
82 |
leaderboard_data = load_data()
|
83 |
+
tabs = st.tabs(["Leaderboard", "Submit model"])
|
84 |
|
85 |
with tabs[0]:
|
86 |
+
# logo image
|
87 |
cols_logo = st.columns(5, vertical_alignment="center")
|
88 |
with cols_logo[2]:
|
89 |
st.image("assets/images/hf-logo.png", use_container_width=True)
|
|
|
101 |
""",
|
102 |
unsafe_allow_html=True,
|
103 |
)
|
104 |
+
# leaderboard_cols = st.columns([0.1, 0.8, 0.1], vertical_alignment="center")
|
105 |
+
# with leaderboard_cols[1]:
|
106 |
+
if not leaderboard_data.empty:
|
107 |
+
st.data_editor(
|
108 |
+
leaderboard_data,
|
109 |
+
column_config={"Model": st.column_config.LinkColumn("Model")},
|
110 |
+
hide_index=False,
|
111 |
+
)
|
112 |
+
else:
|
113 |
+
st.write("No data found to display on leaderboard.")
|
|
|
|
|
114 |
|
115 |
with tabs[1]:
|
116 |
st.header("Submit model")
|
|
|
140 |
html_path = "assets/html"
|
141 |
for filename in os.listdir(html_path):
|
142 |
file_path = os.path.join(html_path, filename)
|
143 |
+
with open(file_path, "r", encoding="utf-8") as file:
|
144 |
guide_info_list.append(file.read())
|
145 |
|
146 |
# display adding number id
|
147 |
for i, info_div in enumerate(guide_info_list):
|
148 |
+
st.markdown(get_id_number(i + 1) + info_div, unsafe_allow_html=True)
|
149 |
|
150 |
with st.form("submit_model_form"):
|
151 |
+
model_name = st.text_input(
|
152 |
+
"Model Name (format: user_name/model_name)",
|
153 |
+
help="Your model should be public on the Hub and follow the username/model-id format (e.g. mistralai/Mistral-7B-v0.1).",
|
154 |
+
)
|
155 |
+
description = st.text_area(
|
156 |
+
"Description",
|
157 |
+
help="Add a description of the proposed model for the evaluation to help prioritize its evaluation",
|
158 |
+
)
|
159 |
+
user_contact = st.text_input(
|
160 |
+
"Your Contact Email",
|
161 |
+
help="User e-mail to contact when there are updates",
|
162 |
+
)
|
163 |
precision_option = st.selectbox(
|
164 |
"Choose precision format:",
|
165 |
help="Size limits vary by precision: • FP16/BF16: up to 100B parameters • 8-bit: up to 280B parameters (2x) • 4-bit: up to 560B parameters (4x) Choose carefully as incorrect precision can cause evaluation errors.",
|
166 |
options=["float16", "bfloat16", "8bit", "4bit", "GPTQ"],
|
167 |
+
index=0,
|
168 |
)
|
169 |
weight_type_option = st.selectbox(
|
170 |
"Select what type of weights are being loaded from the checkpoint provided:",
|
171 |
help="Original: Complete model weights in safetensors format Delta: Weight differences from base model (requires base model for size calculation) Adapter: Lightweight fine-tuning layers (requires base model for size calculation)",
|
172 |
options=["Original", "Adapter", "Delta"],
|
173 |
+
index=0,
|
174 |
+
)
|
175 |
+
base_model_name = st.text_input(
|
176 |
+
"Base model",
|
177 |
+
help="Required for delta weights or adapters. This information is used to identify the original model and calculate the total parameter count by combining base model and adapter/delta parameters.",
|
178 |
+
value="",
|
179 |
)
|
|
|
|
|
|
|
180 |
model_type = st.selectbox(
|
181 |
"Choose model type:",
|
182 |
help="🟢 Pretrained: Base models trained on text using masked modeling 🟩 Continuously Pretrained: Extended training on additional corpus 🔶 Fine-tuned: Domain-specific optimization 💬 Chat: Models using RLHF, DPO, or IFT for conversation 🤝 Merge: Combined weights without additional training",
|
183 |
+
options=[
|
184 |
+
"🟢 Pretrained",
|
185 |
+
"🟩 Continuously Pretrained",
|
186 |
+
"🔶 Fine-tuned",
|
187 |
+
"💬 Chat",
|
188 |
+
"🤝 Merge",
|
189 |
+
],
|
190 |
)
|
191 |
submit_button = st.form_submit_button("Submit Request")
|
192 |
|
193 |
if submit_button:
|
194 |
# validate model size, license, chat_templates
|
195 |
use_chat_template = True if model_type == "💬 Chat" else False
|
196 |
+
validation_error = validate_model(
|
197 |
+
model_name,
|
198 |
+
precision_option,
|
199 |
+
base_model_name,
|
200 |
+
weight_type_option,
|
201 |
+
use_chat_template,
|
202 |
+
)
|
203 |
if validation_error is not None:
|
204 |
st.error(validation_error)
|
205 |
elif not re.match(r"[^@]+@[^@]+\.[^@]+", user_contact):
|
206 |
st.error("Invalid email address.")
|
207 |
else:
|
208 |
+
input_dict = {
|
209 |
+
"model_name": model_name,
|
210 |
+
"description": description,
|
211 |
+
"user_contact": user_contact,
|
212 |
+
"precision_option": precision_option,
|
213 |
+
"weight_type_option": weight_type_option,
|
214 |
+
"base_model_name": base_model_name,
|
215 |
+
"model_type": model_type,
|
216 |
+
}
|
217 |
+
try:
|
218 |
+
log_submission(input_dict)
|
219 |
st.success("Your request has been sent successfully.")
|
220 |
+
except Exception as e:
|
221 |
+
st.error(
|
222 |
+
f"Failed to send your request: {e}. Please try again later."
|
223 |
+
)
|
|
|
|
src/__pycache__/__init__.cpython-310.pyc
DELETED
Binary file (156 Bytes)
|
|
src/__pycache__/__init__.cpython-38.pyc
DELETED
Binary file (154 Bytes)
|
|
src/__pycache__/check_validity.cpython-310.pyc
DELETED
Binary file (5.85 kB)
|
|
src/__pycache__/check_validity.cpython-38.pyc
DELETED
Binary file (5.9 kB)
|
|
src/__pycache__/submit.cpython-310.pyc
DELETED
Binary file (1.22 kB)
|
|
src/check_validity.py
CHANGED
@@ -1,15 +1,19 @@
|
|
1 |
import json
|
2 |
-
import os
|
3 |
-
import re
|
4 |
import logging
|
5 |
-
|
6 |
-
from datetime import datetime, timedelta, timezone
|
7 |
|
8 |
import huggingface_hub
|
9 |
-
from huggingface_hub import ModelCard, hf_hub_download
|
10 |
-
from huggingface_hub.hf_api import
|
|
|
|
|
|
|
|
|
11 |
from transformers import AutoConfig, AutoTokenizer
|
12 |
|
|
|
|
|
|
|
13 |
# ht to @Wauplin, thank you for the snippet!
|
14 |
# See https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard/discussions/317
|
15 |
def check_model_card(repo_id: str) -> tuple[bool, str]:
|
@@ -17,10 +21,16 @@ def check_model_card(repo_id: str) -> tuple[bool, str]:
|
|
17 |
try:
|
18 |
card = ModelCard.load(repo_id)
|
19 |
except huggingface_hub.utils.EntryNotFoundError:
|
20 |
-
return
|
|
|
|
|
|
|
|
|
21 |
|
22 |
# Enforce license metadata
|
23 |
-
if card.data.license is None and not (
|
|
|
|
|
24 |
return (
|
25 |
False,
|
26 |
(
|
@@ -32,24 +42,44 @@ def check_model_card(repo_id: str) -> tuple[bool, str]:
|
|
32 |
|
33 |
# Enforce card content
|
34 |
if len(card.text) < 200:
|
35 |
-
return
|
|
|
|
|
|
|
|
|
36 |
|
37 |
return True, "", card
|
38 |
|
39 |
|
40 |
def is_model_on_hub(
|
41 |
-
model_name: str,
|
|
|
|
|
|
|
|
|
42 |
) -> tuple[bool, str, AutoConfig]:
|
43 |
try:
|
44 |
config = AutoConfig.from_pretrained(
|
45 |
-
model_name,
|
|
|
|
|
|
|
|
|
|
|
46 |
if test_tokenizer:
|
47 |
try:
|
48 |
-
AutoTokenizer.from_pretrained(
|
49 |
-
model_name,
|
|
|
|
|
|
|
50 |
)
|
51 |
except ValueError as e:
|
52 |
-
return (
|
|
|
|
|
|
|
|
|
53 |
except Exception:
|
54 |
return (
|
55 |
False,
|
@@ -74,27 +104,42 @@ def is_model_on_hub(
|
|
74 |
except Exception as e:
|
75 |
if "You are trying to access a gated repo." in str(e):
|
76 |
return True, "uses a gated model.", None
|
77 |
-
return
|
|
|
|
|
|
|
|
|
78 |
|
79 |
|
80 |
-
def get_model_size(
|
|
|
|
|
81 |
size_pattern = re.compile(r"(\d+\.)?\d+(b|m)")
|
82 |
safetensors = None
|
83 |
adapter_safetensors = None
|
84 |
# hack way to check that model is adapter
|
85 |
-
is_adapter = "adapter_config.json" in (
|
|
|
|
|
86 |
|
87 |
try:
|
88 |
if is_adapter:
|
89 |
if not base_model:
|
90 |
-
return
|
|
|
|
|
|
|
91 |
|
92 |
-
adapter_safetensors = parse_safetensors_file_metadata(
|
|
|
|
|
93 |
safetensors = get_safetensors_metadata(base_model)
|
94 |
else:
|
95 |
safetensors = get_safetensors_metadata(model_info.id)
|
96 |
except Exception as e:
|
97 |
-
logging.warning(
|
|
|
|
|
98 |
|
99 |
if safetensors is not None:
|
100 |
model_size = sum(safetensors.parameter_count.values())
|
@@ -106,21 +151,32 @@ def get_model_size(model_info: ModelInfo, precision: str, base_model: str| None)
|
|
106 |
size_match = re.search(size_pattern, model_info.id.lower())
|
107 |
if size_match:
|
108 |
model_size = size_match.group(0)
|
109 |
-
model_size = round(
|
|
|
|
|
|
|
|
|
|
|
110 |
else:
|
111 |
return None, "Unknown model size"
|
112 |
except AttributeError:
|
113 |
-
logging.warning(
|
|
|
|
|
114 |
return None, "Unknown model size"
|
115 |
|
116 |
-
size_factor =
|
|
|
|
|
117 |
model_size = size_factor * model_size
|
118 |
|
119 |
return model_size, ""
|
120 |
|
|
|
121 |
def get_model_arch(model_info: ModelInfo):
|
122 |
return model_info.config.get("architectures", "Unknown")
|
123 |
|
|
|
124 |
def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
|
125 |
try:
|
126 |
# Attempt to download only the tokenizer_config.json file
|
@@ -128,21 +184,28 @@ def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
|
|
128 |
repo_id=model,
|
129 |
filename="tokenizer_config.json",
|
130 |
revision=revision,
|
131 |
-
repo_type="model"
|
132 |
)
|
133 |
|
134 |
# Read and parse the tokenizer_config.json file
|
135 |
-
with open(config_file,
|
136 |
tokenizer_config = json.load(f)
|
137 |
|
138 |
# Check if chat_template exists in the tokenizer configuration
|
139 |
-
if
|
140 |
-
return
|
|
|
|
|
|
|
141 |
|
142 |
return True, ""
|
143 |
except Exception as e:
|
144 |
-
return
|
145 |
-
|
|
|
|
|
|
|
|
|
146 |
def get_model_tags(model_card, model: str):
|
147 |
is_merge_from_metadata = False
|
148 |
is_moe_from_metadata = False
|
@@ -152,22 +215,125 @@ def get_model_tags(model_card, model: str):
|
|
152 |
return tags
|
153 |
if model_card.data.tags:
|
154 |
is_merge_from_metadata = any(
|
155 |
-
[
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
)
|
157 |
-
is_moe_from_metadata = any([tag in model_card.data.tags for tag in ["moe", "moerge"]])
|
158 |
|
159 |
is_merge_from_model_card = any(
|
160 |
-
keyword in model_card.text.lower()
|
|
|
161 |
)
|
162 |
if is_merge_from_model_card or is_merge_from_metadata:
|
163 |
tags.append("merge")
|
164 |
-
is_moe_from_model_card = any(
|
|
|
|
|
165 |
# Hardcoding because of gating problem
|
166 |
if "Qwen/Qwen1.5-32B" in model:
|
167 |
is_moe_from_model_card = False
|
168 |
-
is_moe_from_name = "moe" in model.lower().replace("/", "-").replace(
|
|
|
|
|
169 |
if is_moe_from_model_card or is_moe_from_name or is_moe_from_metadata:
|
170 |
tags.append("moe")
|
171 |
|
172 |
return tags
|
173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import json
|
|
|
|
|
2 |
import logging
|
3 |
+
import re
|
|
|
4 |
|
5 |
import huggingface_hub
|
6 |
+
from huggingface_hub import HfApi, ModelCard, hf_hub_download
|
7 |
+
from huggingface_hub.hf_api import (
|
8 |
+
ModelInfo,
|
9 |
+
get_safetensors_metadata,
|
10 |
+
parse_safetensors_file_metadata,
|
11 |
+
)
|
12 |
from transformers import AutoConfig, AutoTokenizer
|
13 |
|
14 |
+
from src.submit import ModelSizeChecker
|
15 |
+
|
16 |
+
|
17 |
# ht to @Wauplin, thank you for the snippet!
|
18 |
# See https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard/discussions/317
|
19 |
def check_model_card(repo_id: str) -> tuple[bool, str]:
|
|
|
21 |
try:
|
22 |
card = ModelCard.load(repo_id)
|
23 |
except huggingface_hub.utils.EntryNotFoundError:
|
24 |
+
return (
|
25 |
+
False,
|
26 |
+
"Please add a model card to your model to explain how you trained/fine-tuned it.",
|
27 |
+
None,
|
28 |
+
)
|
29 |
|
30 |
# Enforce license metadata
|
31 |
+
if card.data.license is None and not (
|
32 |
+
"license_name" in card.data and "license_link" in card.data
|
33 |
+
):
|
34 |
return (
|
35 |
False,
|
36 |
(
|
|
|
42 |
|
43 |
# Enforce card content
|
44 |
if len(card.text) < 200:
|
45 |
+
return (
|
46 |
+
False,
|
47 |
+
"Please add a description to your model card, it is too short.",
|
48 |
+
None,
|
49 |
+
)
|
50 |
|
51 |
return True, "", card
|
52 |
|
53 |
|
54 |
def is_model_on_hub(
|
55 |
+
model_name: str,
|
56 |
+
revision: str,
|
57 |
+
token: str | None = None,
|
58 |
+
trust_remote_code: bool = False,
|
59 |
+
test_tokenizer: bool = False,
|
60 |
) -> tuple[bool, str, AutoConfig]:
|
61 |
try:
|
62 |
config = AutoConfig.from_pretrained(
|
63 |
+
model_name,
|
64 |
+
revision=revision,
|
65 |
+
trust_remote_code=trust_remote_code,
|
66 |
+
token=token,
|
67 |
+
force_download=True,
|
68 |
+
)
|
69 |
if test_tokenizer:
|
70 |
try:
|
71 |
+
_ = AutoTokenizer.from_pretrained(
|
72 |
+
model_name,
|
73 |
+
revision=revision,
|
74 |
+
trust_remote_code=trust_remote_code,
|
75 |
+
token=token,
|
76 |
)
|
77 |
except ValueError as e:
|
78 |
+
return (
|
79 |
+
False,
|
80 |
+
f"uses a tokenizer which is not in a transformers release: {e}",
|
81 |
+
None,
|
82 |
+
)
|
83 |
except Exception:
|
84 |
return (
|
85 |
False,
|
|
|
104 |
except Exception as e:
|
105 |
if "You are trying to access a gated repo." in str(e):
|
106 |
return True, "uses a gated model.", None
|
107 |
+
return (
|
108 |
+
False,
|
109 |
+
f"was not found or misconfigured on the hub! Error raised was {e.args[0]}",
|
110 |
+
None,
|
111 |
+
)
|
112 |
|
113 |
|
114 |
+
def get_model_size(
|
115 |
+
model_info: ModelInfo, precision: str, base_model: str | None
|
116 |
+
) -> tuple[float | None, str]:
|
117 |
size_pattern = re.compile(r"(\d+\.)?\d+(b|m)")
|
118 |
safetensors = None
|
119 |
adapter_safetensors = None
|
120 |
# hack way to check that model is adapter
|
121 |
+
is_adapter = "adapter_config.json" in (
|
122 |
+
s.rfilename for s in model_info.siblings
|
123 |
+
)
|
124 |
|
125 |
try:
|
126 |
if is_adapter:
|
127 |
if not base_model:
|
128 |
+
return (
|
129 |
+
None,
|
130 |
+
"Adapter model submission detected. Please ensure the base model information is provided.",
|
131 |
+
)
|
132 |
|
133 |
+
adapter_safetensors = parse_safetensors_file_metadata(
|
134 |
+
model_info.id, "adapter_model.safetensors"
|
135 |
+
)
|
136 |
safetensors = get_safetensors_metadata(base_model)
|
137 |
else:
|
138 |
safetensors = get_safetensors_metadata(model_info.id)
|
139 |
except Exception as e:
|
140 |
+
logging.warning(
|
141 |
+
f"Failed to get safetensors metadata for model {model_info.id}: {e!s}"
|
142 |
+
)
|
143 |
|
144 |
if safetensors is not None:
|
145 |
model_size = sum(safetensors.parameter_count.values())
|
|
|
151 |
size_match = re.search(size_pattern, model_info.id.lower())
|
152 |
if size_match:
|
153 |
model_size = size_match.group(0)
|
154 |
+
model_size = round(
|
155 |
+
float(model_size[:-1])
|
156 |
+
if model_size[-1] == "b"
|
157 |
+
else float(model_size[:-1]) / 1e3,
|
158 |
+
3,
|
159 |
+
)
|
160 |
else:
|
161 |
return None, "Unknown model size"
|
162 |
except AttributeError:
|
163 |
+
logging.warning(
|
164 |
+
f"Unable to parse model size from ID: {model_info.id}"
|
165 |
+
)
|
166 |
return None, "Unknown model size"
|
167 |
|
168 |
+
size_factor = (
|
169 |
+
8 if (precision == "GPTQ" or "gptq" in model_info.id.lower()) else 1
|
170 |
+
)
|
171 |
model_size = size_factor * model_size
|
172 |
|
173 |
return model_size, ""
|
174 |
|
175 |
+
|
176 |
def get_model_arch(model_info: ModelInfo):
|
177 |
return model_info.config.get("architectures", "Unknown")
|
178 |
|
179 |
+
|
180 |
def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
|
181 |
try:
|
182 |
# Attempt to download only the tokenizer_config.json file
|
|
|
184 |
repo_id=model,
|
185 |
filename="tokenizer_config.json",
|
186 |
revision=revision,
|
187 |
+
repo_type="model",
|
188 |
)
|
189 |
|
190 |
# Read and parse the tokenizer_config.json file
|
191 |
+
with open(config_file, "r") as f:
|
192 |
tokenizer_config = json.load(f)
|
193 |
|
194 |
# Check if chat_template exists in the tokenizer configuration
|
195 |
+
if "chat_template" not in tokenizer_config:
|
196 |
+
return (
|
197 |
+
False,
|
198 |
+
f"The model {model} doesn't have a chat_template in its tokenizer_config.json. Please add a chat_template before submitting or submit without it.",
|
199 |
+
)
|
200 |
|
201 |
return True, ""
|
202 |
except Exception as e:
|
203 |
+
return (
|
204 |
+
False,
|
205 |
+
f"Error checking chat_template for model {model}: {str(e)}",
|
206 |
+
)
|
207 |
+
|
208 |
+
|
209 |
def get_model_tags(model_card, model: str):
|
210 |
is_merge_from_metadata = False
|
211 |
is_moe_from_metadata = False
|
|
|
215 |
return tags
|
216 |
if model_card.data.tags:
|
217 |
is_merge_from_metadata = any(
|
218 |
+
[
|
219 |
+
tag in model_card.data.tags
|
220 |
+
for tag in ["merge", "moerge", "mergekit", "lazymergekit"]
|
221 |
+
]
|
222 |
+
)
|
223 |
+
is_moe_from_metadata = any(
|
224 |
+
[tag in model_card.data.tags for tag in ["moe", "moerge"]]
|
225 |
)
|
|
|
226 |
|
227 |
is_merge_from_model_card = any(
|
228 |
+
keyword in model_card.text.lower()
|
229 |
+
for keyword in ["merged model", "merge model", "moerge"]
|
230 |
)
|
231 |
if is_merge_from_model_card or is_merge_from_metadata:
|
232 |
tags.append("merge")
|
233 |
+
is_moe_from_model_card = any(
|
234 |
+
keyword in model_card.text.lower() for keyword in ["moe", "mixtral"]
|
235 |
+
)
|
236 |
# Hardcoding because of gating problem
|
237 |
if "Qwen/Qwen1.5-32B" in model:
|
238 |
is_moe_from_model_card = False
|
239 |
+
is_moe_from_name = "moe" in model.lower().replace("/", "-").replace(
|
240 |
+
"_", "-"
|
241 |
+
).split("-")
|
242 |
if is_moe_from_model_card or is_moe_from_name or is_moe_from_metadata:
|
243 |
tags.append("moe")
|
244 |
|
245 |
return tags
|
246 |
|
247 |
+
|
248 |
+
def validate_model(
|
249 |
+
model, precision, base_model, weight_type, use_chat_template
|
250 |
+
):
|
251 |
+
"""
|
252 |
+
Validate model with some checkers to assure tha can be evaluated
|
253 |
+
:param model: hf model name
|
254 |
+
:param precision: model parameters data type
|
255 |
+
:param base_model: base model (if it is need it)
|
256 |
+
:param weight_type:
|
257 |
+
:param use_chat_template:
|
258 |
+
:return:
|
259 |
+
"""
|
260 |
+
API = HfApi()
|
261 |
+
|
262 |
+
try:
|
263 |
+
model_info = API.model_info(repo_id=model, revision="main")
|
264 |
+
except:
|
265 |
+
return (
|
266 |
+
"Could not get your model information. Please fill it up properly."
|
267 |
+
)
|
268 |
+
|
269 |
+
# Check model size early
|
270 |
+
model_size, error_text = get_model_size(
|
271 |
+
model_info=model_info, precision=precision, base_model=base_model
|
272 |
+
)
|
273 |
+
if model_size is None:
|
274 |
+
return error_text
|
275 |
+
|
276 |
+
# Absolute size limit for float16 and bfloat16
|
277 |
+
if precision in ["float16", "bfloat16"] and model_size > 100:
|
278 |
+
error_message = (
|
279 |
+
f"Sadly, models larger than 100B parameters cannot be submitted in {precision} precision at this time. "
|
280 |
+
f"Your model size: {model_size:.2f}B parameters."
|
281 |
+
)
|
282 |
+
return error_message
|
283 |
+
|
284 |
+
# Precision-adjusted size limit for 8bit, 4bit, and GPTQ
|
285 |
+
if precision in ["8bit", "4bit", "GPTQ"]:
|
286 |
+
size_checker = ModelSizeChecker(
|
287 |
+
model=model, precision=precision, model_size_in_b=model_size
|
288 |
+
)
|
289 |
+
|
290 |
+
if not size_checker.can_evaluate():
|
291 |
+
precision_factor = size_checker.get_precision_factor()
|
292 |
+
max_size = 140 * precision_factor
|
293 |
+
error_message = (
|
294 |
+
f"Sadly, models this big ({model_size:.2f}B parameters) cannot be evaluated automatically "
|
295 |
+
f"at the moment on our cluster. The maximum size for {precision} precision is {max_size:.2f}B parameters."
|
296 |
+
)
|
297 |
+
return error_message
|
298 |
+
|
299 |
+
architecture = "?"
|
300 |
+
# Is the model on the hub?
|
301 |
+
if weight_type in ["Delta", "Adapter"]:
|
302 |
+
base_model_on_hub, error, _ = is_model_on_hub(
|
303 |
+
model_name=base_model,
|
304 |
+
revision="main",
|
305 |
+
token=None,
|
306 |
+
test_tokenizer=True,
|
307 |
+
)
|
308 |
+
if not base_model_on_hub:
|
309 |
+
return f'Base model "{base_model}" {error}'
|
310 |
+
if not weight_type == "Adapter":
|
311 |
+
model_on_hub, error, model_config = is_model_on_hub(
|
312 |
+
model_name=model, revision=model_info.sha, test_tokenizer=True
|
313 |
+
)
|
314 |
+
if not model_on_hub or model_config is None:
|
315 |
+
return f'Model "{model}" {error}'
|
316 |
+
if model_config is not None:
|
317 |
+
architectures = getattr(model_config, "architectures", None)
|
318 |
+
if architectures:
|
319 |
+
architecture = ";".join(architectures)
|
320 |
+
|
321 |
+
# Were the model card and license filled?
|
322 |
+
try:
|
323 |
+
_ = model_info.cardData["license"]
|
324 |
+
except Exception:
|
325 |
+
return "Please select a license for your model"
|
326 |
+
|
327 |
+
modelcard_OK, error_msg, model_card = check_model_card(model)
|
328 |
+
if not modelcard_OK:
|
329 |
+
return error_msg
|
330 |
+
|
331 |
+
# Check the chat template submission
|
332 |
+
if use_chat_template:
|
333 |
+
chat_template_valid, chat_template_error = check_chat_template(
|
334 |
+
model, "main"
|
335 |
+
)
|
336 |
+
if not chat_template_valid:
|
337 |
+
return chat_template_error
|
338 |
+
|
339 |
+
return None
|
src/submit.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
-
from transformers import AutoConfig
|
2 |
from dataclasses import dataclass
|
3 |
|
|
|
|
|
|
|
4 |
@dataclass
|
5 |
class ModelSizeChecker:
|
6 |
model: str
|
|
|
|
|
1 |
from dataclasses import dataclass
|
2 |
|
3 |
+
from transformers import AutoConfig
|
4 |
+
|
5 |
+
|
6 |
@dataclass
|
7 |
class ModelSizeChecker:
|
8 |
model: str
|