jiaxie's picture
Align leaderboard with SpectrumWorld paper Table 3
317c6d4 verified
Raw
History Blame Contribute Delete
19.1 kB
import gradio as gr
import pandas as pd
import json
from pathlib import Path
from typing import Dict, Optional
class SpectralLeaderboard:
def __init__(self, data_file: str = "./leaderboard_v_1.0.json"):
self.data_file = Path(data_file)
self.data = self._load_data()
def _load_data(self) -> Dict:
"""ๅŠ ่ฝฝๆŽ’่กŒๆฆœๆ•ฐๆฎ"""
try:
with open(self.data_file, "r", encoding="utf-8") as f:
data = json.load(f)
print(f"โœ… Successfully loaded {data['leaderboard_info']['total_models']} models from {self.data_file}")
return data
except FileNotFoundError:
print(f"โŒ Data file {self.data_file} not found. Creating empty leaderboard.")
return {"leaderboard_info": {"total_models": 0}, "models": []}
except Exception as e:
print(f"โŒ Error loading data: {e}")
return {"leaderboard_info": {"total_models": 0}, "models": []}
def _format_accuracy(self, accuracy: Optional[float]) -> str:
"""ๆ ผๅผๅŒ–ๅ‡†็กฎ็އๆ˜พ็คบ"""
if accuracy is None:
return "-"
return f"{accuracy:.2f}"
def _calculate_average(self, results: Dict) -> Optional[float]:
"""Return Avg. Perf from Table 3: arithmetic mean across 14 subtasks."""
return results.get("overall_accuracy")
def _get_model_type_icon(self, model_type: str) -> str:
"""่Žทๅ–ๆจกๅž‹็ฑปๅž‹ๅ›พๆ ‡"""
icons = {"open_source": "๐Ÿ”“", "proprietary": "๐Ÿ”’", "baseline": "๐Ÿ“Š"}
return icons.get(model_type, "โ“")
def _get_multimodal_icon(self, is_multimodal: bool) -> str:
"""่Žทๅ–ๅคšๆจกๆ€ๅ›พๆ ‡"""
return "๐Ÿ‘๏ธ" if is_multimodal else "๐Ÿ“"
def _get_rank_display(self, rank: int) -> str:
"""่Žทๅ–ๆŽ’ๅๆ˜พ็คบ๏ผŒๅ‰ไธ‰ๅๆ˜พ็คบๅฅ–็‰Œ"""
medals = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}
return medals.get(rank, str(rank))
def _create_link(self, text: str, url: str) -> str:
"""ๅˆ›ๅปบHTML้“พๆŽฅ"""
if url and url.strip():
return f'<a href="{url}" target="_blank" style="text-decoration: none; color: inherit;">{text}</a>'
return text
def get_leaderboard_df(
self,
model_type_filter: str = "All",
multimodal_filter: str = "All",
sort_by: str = "Overall",
ascending: bool = False,
) -> pd.DataFrame:
"""็”ŸๆˆๆŽ’่กŒๆฆœDataFrame"""
models = self.data.get("models", [])
print(f"๐Ÿ“Š Processing {len(models)} models")
# ็ญ›้€‰ๆจกๅž‹
filtered_models = []
for model in models:
# ๆจกๅž‹็ฑปๅž‹็ญ›้€‰
if model_type_filter != "All" and model.get("model_type", "") != model_type_filter:
continue
# ๅคšๆจกๆ€็ญ›้€‰
if multimodal_filter == "Multimodal Only" and not model.get("is_multimodal", False):
continue
elif multimodal_filter == "Text Only" and model.get("is_multimodal", False):
continue
filtered_models.append(model)
print(f"๐Ÿ” After filtering: {len(filtered_models)} models")
# ๆž„ๅปบDataFrameๆ•ฐๆฎ
data = []
for model in filtered_models:
try:
results = model.get("results", {})
# ่Žทๅ–ๅ„้กนๅ‡†็กฎ็އ
overall_accuracy = self._calculate_average(results)
signal_acc = results.get("Signal", {}).get("accuracy")
perception_acc = results.get("Perception", {}).get("accuracy")
semantic_acc = results.get("Semantic", {}).get("accuracy")
generation_acc = results.get("Generation", {}).get("accuracy")
# ๅˆ›ๅปบๅธฆ้“พๆŽฅ็š„ๆจกๅž‹ๅๅ’Œๆไบค่€…
model_name_display = self._create_link(model.get("name", "Unknown"), model.get("name_link", ""))
submitter_display = self._create_link(
model.get("submitter", "Unknown"), model.get("submitter_link", "")
)
row = {
"Type": self._get_model_type_icon(model.get("model_type", "unknown")),
"Model": model_name_display,
"Size": model.get("model_size", "Unknown"),
"MM": self._get_multimodal_icon(model.get("is_multimodal", False)),
"Overall": self._format_accuracy(overall_accuracy),
"Signal": self._format_accuracy(signal_acc),
"Perception": self._format_accuracy(perception_acc),
"Semantic": self._format_accuracy(semantic_acc),
"Generation": self._format_accuracy(generation_acc),
"Submitter": submitter_display,
"Date": (model.get("submission_time", "")[:10] if model.get("submission_time") else "-"),
# ็”จไบŽๆŽ’ๅบ็š„ๆ•ฐๅ€ผๅˆ—
"overall_val": overall_accuracy or 0,
"signal_val": signal_acc or 0,
"perception_val": perception_acc or 0,
"semantic_val": semantic_acc or 0,
"generation_val": generation_acc or 0,
}
data.append(row)
except Exception as e:
print(f"โš ๏ธ Error processing model {model.get('name', 'Unknown')}: {e}")
continue
df = pd.DataFrame(data)
print(f"๐Ÿ“‹ Created DataFrame with {len(df)} rows")
if len(df) == 0:
print("๐Ÿ“‹ Empty DataFrame, returning empty table")
return pd.DataFrame(
columns=[
"Rank",
"Type",
"Model",
"Size",
"MM",
"Overall",
"Signal",
"Perception",
"Semantic",
"Generation",
"Submitter",
"Date",
]
)
# ๆŽ’ๅบ
sort_mapping = {
"Overall": "overall_val",
"Signal": "signal_val",
"Perception": "perception_val",
"Semantic": "semantic_val",
"Generation": "generation_val",
"Model": "Model",
"Date": "Date",
}
sort_col = sort_mapping.get(sort_by, "overall_val")
df = df.sort_values(by=sort_col, ascending=ascending)
# ๆทปๅŠ ๅธฆๅฅ–็‰Œ็š„ๆŽ’ๅ
ranks = []
for i in range(len(df)):
rank_num = i + 1
ranks.append(self._get_rank_display(rank_num))
df.insert(0, "Rank", ranks)
# ็งป้™ค็”จไบŽๆŽ’ๅบ็š„่พ…ๅŠฉๅˆ—
display_columns = [
"Rank",
"Type",
"Model",
"Size",
"MM",
"Overall",
"Signal",
"Perception",
"Semantic",
"Generation",
"Submitter",
"Date",
]
result_df = df[display_columns]
print(f"โœ… Returning DataFrame with {len(result_df)} rows")
return result_df
def get_subcategory_details(self, model_name: str) -> pd.DataFrame:
"""่Žทๅ–ๆจกๅž‹็š„ๅญ็ฑปๅˆซ่ฏฆ็ป†็ป“ๆžœ"""
# ็งป้™คHTMLๆ ‡็ญพ่ฟ›่กŒๅŒน้…
clean_model_name = model_name
if "<a href=" in model_name:
# ๆๅ–้“พๆŽฅไธญ็š„ๆ–‡ๆœฌ
import re
match = re.search(r">([^<]+)<", model_name)
if match:
clean_model_name = match.group(1)
for model in self.data.get("models", []):
if model.get("name") == clean_model_name:
data = []
results = model.get("results", {})
for level, level_data in results.items():
if level == "overall_accuracy": # ่ทณ่ฟ‡ๆ€ปไฝ“ๅ‡†็กฎ็އๅญ—ๆฎต
continue
subcategories = level_data.get("subcategories", {})
for subcat, subcat_data in subcategories.items():
data.append(
{
"Level": level,
"Subcategory": subcat,
"Accuracy": self._format_accuracy(subcat_data.get("accuracy")),
}
)
return pd.DataFrame(data)
return pd.DataFrame()
def create_leaderboard():
"""ๅˆ›ๅปบๆŽ’่กŒๆฆœGradio็•Œ้ข"""
leaderboard = SpectralLeaderboard()
with gr.Blocks(
title="๐Ÿ”ฌ SpectrumLab Leaderboard",
theme=gr.themes.Default(),
css="""
.gradio-container {
max-width: 1400px !important;
}
.dataframe table {
border-collapse: collapse !important;
}
.dataframe td, .dataframe th {
padding: 8px 12px !important;
border: 1px solid #e1e5e9 !important;
}
.dataframe th {
background-color: #f8f9fa !important;
font-weight: 600 !important;
}
.dataframe tr:nth-child(even) {
background-color: #f8f9fa !important;
}
.dataframe tr:hover {
background-color: #e8f4f8 !important;
}
""",
) as demo:
gr.Markdown(
"""
# ๐Ÿ† SpectrumLab Leaderboard
A comprehensive benchmark for evaluating large language models on **spectroscopic analysis tasks**.
๐Ÿ“Š **Evaluation Levels**: Signal Processing, Perception, Semantic Understanding, Generation
๐Ÿ”ฌ **Domains**: IR, NMR, UV-Vis, Mass Spectrometry and more
๐ŸŒŸ **Multimodal**: Support for both text-only and vision-language models
"""
)
with gr.Row():
info = leaderboard.data.get("leaderboard_info", {"total_models": 0})
gr.Markdown(
f"""
**๐Ÿ“ˆ Stats**: {info["total_models"]} models evaluated
**๐Ÿ… Rankings**: ๐Ÿฅ‡๐Ÿฅˆ๐Ÿฅ‰ medals for top performers
**๐Ÿ”— Submit**: Send evaluation results to contribute your model!
"""
)
with gr.Row():
with gr.Column(scale=2):
model_type_filter = gr.Dropdown(
choices=["All", "open_source", "proprietary", "baseline"],
value="All",
label="๐Ÿท๏ธ Model Type",
)
with gr.Column(scale=2):
multimodal_filter = gr.Dropdown(
choices=["All", "Multimodal Only", "Text Only"],
value="All",
label="๐Ÿ‘๏ธ Modality",
)
with gr.Column(scale=2):
sort_by = gr.Dropdown(
choices=[
"Overall",
"Signal",
"Perception",
"Semantic",
"Generation",
"Model",
"Date",
],
value="Overall",
label="๐Ÿ“Š Sort By",
)
with gr.Column(scale=1):
ascending = gr.Checkbox(value=False, label="โฌ†๏ธ Ascending")
with gr.Column(scale=1):
refresh_btn = gr.Button("๐Ÿ”„ Refresh", variant="secondary")
# ไธปๆŽ’่กŒๆฆœ่กจๆ ผ
initial_df = leaderboard.get_leaderboard_df()
leaderboard_table = gr.Dataframe(
value=initial_df,
interactive=False,
wrap=True,
datatype=["html"] * len(initial_df.columns) if len(initial_df.columns) > 0 else ["html"] * 12,
column_widths=(
[
"6%",
"5%",
"18%",
"8%",
"5%",
"10%",
"10%",
"10%",
"10%",
"10%",
"16%",
"10%",
]
if len(initial_df.columns) > 0
else None
),
label="๐Ÿ† Model Rankings",
)
# ๆจกๅž‹่ฏฆ็ป†ไฟกๆฏ
with gr.Accordion("๐Ÿ“‹ Model Details", open=False):
model_choices = [model.get("name", "Unknown") for model in leaderboard.data.get("models", [])]
model_select = gr.Dropdown(
choices=model_choices,
label="Select Model for Details",
)
with gr.Row():
with gr.Column():
subcategory_table = gr.Dataframe(label="๐Ÿ“Š Subcategory Results")
with gr.Column():
model_info = gr.Markdown(label="โ„น๏ธ Model Information")
# ๅ›พไพ‹่ฏดๆ˜Ž
with gr.Accordion("๐Ÿ“– Legend & Info", open=False):
gr.Markdown(
"""
### ๐Ÿ” Column Explanations
- **Rank**: ๐Ÿฅ‡ 1st place, ๐Ÿฅˆ 2nd place, ๐Ÿฅ‰ 3rd place, then numbers
- **Type**: ๐Ÿ”“ Open Source, ๐Ÿ”’ Proprietary, ๐Ÿ“Š Baseline
- **MM**: ๐Ÿ‘๏ธ Multimodal, ๐Ÿ“ Text-only
- **Overall**: Average accuracy across all 14 evaluated subtasks, matching Table 3 in the paper
- **Signal**: Low-level signal processing tasks
- **Perception**: Mid-level feature extraction tasks
- **Semantic**: High-level understanding tasks
- **Generation**: Spectrum generation tasks
### ๐Ÿ“ Notes
- "-" indicates the model was not evaluated on that benchmark
- Rankings are based on overall performance across all evaluated tasks
- Multimodal models can process both text and spectroscopic images
- Click on model names and submitters to visit their pages
### ๐Ÿ“Š Task Categories
**Signal Level:**
- Spectrum Type Classification (TC)
- Spectrum Quality Assessment (QE)
- Basic Feature Extraction (FE)
- Impurity Peak Detection (ID)
**Perception Level:**
- Functional Group Recognition (GR)
- Elemental Compositional Prediction (EP)
- Peak Assignment (PA)
- Basic Property Prediction (PP)
**Semantic Level:**
- Molecular Structure Elucidation (SE)
- Fusing Spectroscopic Modalities (FM)
- Multimodal Molecular Reasoning (MR)
**Generation Level:**
- Forward Problems (FP)
- Inverse Problems (IP)
- De Novo Generation (DnG)
"""
)
def update_leaderboard(model_type, multimodal, sort_by_val, asc):
"""ๆ›ดๆ–ฐๆŽ’่กŒๆฆœ"""
print(f"๐Ÿ”„ Updating leaderboard with filters: {model_type}, {multimodal}, {sort_by_val}, {asc}")
return leaderboard.get_leaderboard_df(
model_type_filter=model_type,
multimodal_filter=multimodal,
sort_by=sort_by_val,
ascending=asc,
)
def update_model_details(model_name):
"""ๆ›ดๆ–ฐๆจกๅž‹่ฏฆ็ป†ไฟกๆฏ"""
if not model_name:
return pd.DataFrame(), ""
# ่Žทๅ–ๅญ็ฑปๅˆซ่ฏฆๆƒ…
subcategory_df = leaderboard.get_subcategory_details(model_name)
# ่Žทๅ–ๆจกๅž‹ๅŸบๆœฌไฟกๆฏ
for model in leaderboard.data.get("models", []):
if model.get("name") == model_name:
# ๅค„็†้“พๆŽฅๆ˜พ็คบ
def format_link(name, url):
if url and url.strip():
return f"[{name}]({url})"
return "Not provided"
model_info_dict = model.get("model_info", {})
results = model.get("results", {})
info_md = f"""
### {model.get("name", "Unknown")}
**๐Ÿ‘ค Submitter**: {model.get("submitter", "Unknown")}
**๐Ÿ“… Submission**: {model.get("submission_time", "")[:10] if model.get("submission_time") else "Unknown"}
**๐Ÿท๏ธ Type**: {model.get("model_type", "Unknown")}
**๐Ÿ“ Size**: {model.get("model_size", "Unknown")}
**๐Ÿ‘๏ธ Multimodal**: {"Yes" if model.get("is_multimodal", False) else "No"}
**๐Ÿ“ Description**: {model_info_dict.get("description", "") or "No description provided"}
**๐Ÿ”— Links**:
- **Homepage**: {format_link("Visit", model_info_dict.get("homepage", ""))}
- **Paper**: {format_link("Read", model_info_dict.get("paper", ""))}
- **Code**: {format_link("View", model_info_dict.get("code", ""))}
**๐Ÿ“Š Performance Summary**:
- **Overall**: {leaderboard._format_accuracy(results.get("overall_accuracy"))}%
- **Signal**: {leaderboard._format_accuracy(results.get("Signal", {}).get("accuracy"))}%
- **Perception**: {leaderboard._format_accuracy(results.get("Perception", {}).get("accuracy"))}%
- **Semantic**: {leaderboard._format_accuracy(results.get("Semantic", {}).get("accuracy"))}%
- **Generation**: {leaderboard._format_accuracy(results.get("Generation", {}).get("accuracy"))}%
"""
return subcategory_df, info_md
return pd.DataFrame(), ""
# ไบ‹ไปถ็ป‘ๅฎš
for component in [model_type_filter, multimodal_filter, sort_by, ascending]:
component.change(
fn=update_leaderboard,
inputs=[model_type_filter, multimodal_filter, sort_by, ascending],
outputs=[leaderboard_table],
)
refresh_btn.click(
fn=update_leaderboard,
inputs=[model_type_filter, multimodal_filter, sort_by, ascending],
outputs=[leaderboard_table],
)
model_select.change(
fn=update_model_details,
inputs=[model_select],
outputs=[subcategory_table, model_info],
)
return demo
if __name__ == "__main__":
app = create_leaderboard()
print("๐Ÿš€ Starting SpectrumLab Leaderboard...")
app.launch(
server_name="0.0.0.0",
show_api=False,
)