DOoM-lb / generate_initial_leaderboard.py
Anonumous's picture
Update code base and add ruff format
2d440ee
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Скрипт для генерации первоначального лидерборда DeathMath и загрузки данных в HuggingFace.
Использует результаты из директории results и загружает их в репозиторий Vikhrmodels/DeathMath-leaderboard-data.
"""
import os
import json
import logging
import pandas as pd
import argparse
from pathlib import Path
from huggingface_hub import HfApi, create_repo
from datetime import datetime
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("leaderboard_generation.log"), logging.StreamHandler()],
)
logger = logging.getLogger(__name__)
# Константы
REPO_ID = "Vikhrmodels/DeathMath-leaderboard-data"
METAINFO_REPO_ID = "Vikhrmodels/DeathMath-leaderboard-metainfo"
def setup_repositories(token):
"""
Создает необходимые репозитории на HuggingFace Hub, если они еще не существуют.
Args:
token (str): Токен для доступа к HuggingFace Hub
"""
api = HfApi(token=token)
try:
# Проверка и создание репозитория для данных лидерборда
try:
api.repo_info(repo_id=REPO_ID, repo_type="dataset")
logger.info(f"Репозиторий {REPO_ID} уже существует")
except Exception:
logger.info(f"Создание репозитория для данных лидерборда: {REPO_ID}")
create_repo(repo_id=REPO_ID, repo_type="dataset", token=token)
# Проверка и создание репозитория для метаданных лидерборда
try:
api.repo_info(repo_id=METAINFO_REPO_ID, repo_type="dataset")
logger.info(f"Репозиторий {METAINFO_REPO_ID} уже существует")
except Exception:
logger.info(f"Создание репозитория для метаданных лидерборда: {METAINFO_REPO_ID}")
create_repo(repo_id=METAINFO_REPO_ID, repo_type="dataset", token=token)
return api
except Exception as e:
logger.error(f"Ошибка при создании репозиториев: {e}")
raise
def load_results(results_file):
"""
Загружает результаты из JSON файла и удаляет дубликаты.
Args:
results_file (str): Путь к файлу с результатами
Returns:
list: Список записей для лидерборда без дубликатов
"""
try:
with open(results_file, "r", encoding="utf-8") as f:
data = json.load(f)
leaderboard_entries = []
seen_models = set() # Множество для отслеживания уже обработанных моделей
for key, value in data.items():
if "_Combined_" in key: # берем только комбинированные результаты
model_name = value["model_name"]
# Пропускаем модель, если она уже была добавлена
if model_name in seen_models:
logger.info(f"Пропускаем дублирующуюся модель: {model_name}")
continue
# Добавляем модель во множество обработанных
seen_models.add(model_name)
leaderboard_entry = {
"model_name": model_name,
"score": value["score"],
"math_score": value["math_score"],
"physics_score": value["physics_score"],
"total_tokens": value["total_tokens"],
"evaluation_time": value["evaluation_time"],
"system_prompt": value.get(
"system_prompt", "Вы - полезный помощник по математике и физике. Ответьте на русском языке."
),
}
leaderboard_entries.append(leaderboard_entry)
# Сортировка по общему баллу
leaderboard_entries.sort(key=lambda x: x["score"], reverse=True)
logger.info(f"Загружено {len(leaderboard_entries)} уникальных моделей после удаления дубликатов")
return leaderboard_entries
except Exception as e:
logger.error(f"Ошибка при загрузке результатов: {e}")
raise
def prepare_directory_structure():
"""
Создает необходимую структуру директорий для внешних моделей.
Returns:
str: Путь к временной директории с подготовленной структурой
"""
temp_dir = Path("./temp_leaderboard")
model_data_dir = temp_dir / "model_data" / "external"
# Очистка и создание директорий
if temp_dir.exists():
import shutil
shutil.rmtree(temp_dir)
model_data_dir.mkdir(parents=True, exist_ok=True)
return str(temp_dir)
def upload_model_files(api, leaderboard_entries, temp_dir):
"""
Загружает файлы моделей в репозиторий данных лидерборда.
Args:
api (HfApi): Экземпляр API для взаимодействия с HuggingFace
leaderboard_entries (list): Список записей для лидерборда
temp_dir (str): Путь к временной директории
"""
model_data_dir = os.path.join(temp_dir, "model_data", "external")
for entry in leaderboard_entries:
model_name = entry["model_name"]
safe_filename = model_name.replace("/", "_").replace(" ", "_")
file_path = os.path.join(model_data_dir, f"{safe_filename}.json")
with open(file_path, "w", encoding="utf-8") as f:
json.dump(entry, f, ensure_ascii=False, indent=2)
# Загрузка файла модели в репозиторий
api.upload_file(
path_or_fileobj=file_path,
path_in_repo=f"model_data/external/{safe_filename}.json",
repo_id=REPO_ID,
repo_type="dataset",
)
logger.info(f"Загружен файл модели: {safe_filename}.json")
def generate_leaderboard_json(leaderboard_entries):
"""
Создает JSON файл с данными лидерборда.
Args:
leaderboard_entries (list): Список записей для лидерборда
Returns:
str: Путь к созданному JSON файлу
"""
leaderboard_file = "leaderboard.json"
with open(leaderboard_file, "w", encoding="utf-8") as f:
json.dump(leaderboard_entries, f, ensure_ascii=False, indent=2)
return leaderboard_file
def generate_readme(leaderboard_entries):
"""
Генерирует README.md с информацией о лидерборде.
Args:
leaderboard_entries (list): Список записей для лидерборда
Returns:
str: Путь к созданному README файлу
"""
readme_file = "README.md"
# Создаем DataFrame для удобного форматирования таблицы
df = pd.DataFrame(leaderboard_entries)
# Форматируем числовые колонки
for col in ["score", "math_score", "physics_score"]:
if col in df.columns:
df[col] = df[col].apply(lambda x: f"{x:.3f}")
if "total_tokens" in df.columns:
df["total_tokens"] = df["total_tokens"].apply(lambda x: f"{int(x):,}")
if "evaluation_time" in df.columns:
df["evaluation_time"] = df["evaluation_time"].apply(lambda x: f"{x:.1f}s")
# Создаем содержимое README
current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
readme_content = f"""# DeathMath Leaderboard
DeathMath - это бенчмарк для оценки способности моделей решать сложные математические и физические задачи на русском языке.
## Текущий лидерборд
Последнее обновление: {current_date}
| Модель | Общий балл | Математика | Физика | Токены | Время оценки |
|--------|------------|------------|---------|---------|--------------|
"""
# Добавляем строки таблицы
for _, row in df.iterrows():
readme_content += f"| {row['model_name']} | {row['score']} | {row['math_score']} | {row['physics_score']} | {row.get('total_tokens', 'N/A')} | {row.get('evaluation_time', 'N/A')} |\n"
readme_content += """
## Как принять участие в бенчмарке
Для участия в бенчмарке DeathMath:
1. Клонируйте репозиторий и запустите тесты вашей модели
2. Загрузите результаты через [HuggingFace Space](https://huggingface.co/spaces/Vikhrmodels/DeathMath-leaderboard)
3. Дождитесь проверки и добавления результатов в лидерборд
## Формат результатов
Результаты должны быть в формате JSON со следующей структурой:
```json
{
"score": 0.586,
"math_score": 0.8,
"physics_score": 0.373,
"total_tokens": 1394299,
"evaluation_time": 4533.2,
"system_prompt": "Вы - полезный помощник по математике и физике. Ответьте на русском языке."
}
```
## Лицензия
Бенчмарк распространяется под лицензией Apache 2.0
"""
with open(readme_file, "w", encoding="utf-8") as f:
f.write(readme_content)
return readme_file
def upload_leaderboard_files(api, leaderboard_file, readme_file):
"""
Загружает файлы лидерборда в репозиторий метаданных.
Args:
api (HfApi): Экземпляр API для взаимодействия с HuggingFace
leaderboard_file (str): Путь к JSON файлу лидерборда
readme_file (str): Путь к README файлу
"""
# Загрузка JSON лидерборда
api.upload_file(
path_or_fileobj=leaderboard_file, path_in_repo="leaderboard.json", repo_id=METAINFO_REPO_ID, repo_type="dataset"
)
logger.info(f"Загружен файл лидерборда: leaderboard.json в {METAINFO_REPO_ID}")
# Загрузка README
api.upload_file(
path_or_fileobj=readme_file, path_in_repo="README.md", repo_id=METAINFO_REPO_ID, repo_type="dataset"
)
logger.info(f"Загружен README: README.md в {METAINFO_REPO_ID}")
def main():
# Парсинг аргументов командной строки
parser = argparse.ArgumentParser(description="Генерация первоначального лидерборда DeathMath")
parser.add_argument(
"--results",
default="../results/leaderboard_results.json",
help="Путь к файлу с результатами (по умолчанию: ../results/leaderboard_results.json)",
)
parser.add_argument("--token", required=True, help="Токен для доступа к HuggingFace Hub")
args = parser.parse_args()
try:
logger.info("Начинаем генерацию лидерборда DeathMath")
# Настраиваем репозитории
api = setup_repositories(args.token)
logger.info("Репозитории успешно настроены")
# Загружаем результаты
leaderboard_entries = load_results(args.results)
logger.info(f"Загружено {len(leaderboard_entries)} записей для лидерборда")
# Подготавливаем структуру директорий
temp_dir = prepare_directory_structure()
logger.info(f"Создана временная директория: {temp_dir}")
# Загружаем файлы моделей
upload_model_files(api, leaderboard_entries, temp_dir)
logger.info("Файлы моделей успешно загружены")
# Генерируем JSON лидерборда
leaderboard_file = generate_leaderboard_json(leaderboard_entries)
logger.info(f"Создан файл лидерборда: {leaderboard_file}")
# Генерируем README
readme_file = generate_readme(leaderboard_entries)
logger.info(f"Создан README: {readme_file}")
# Загружаем файлы лидерборда
upload_leaderboard_files(api, leaderboard_file, readme_file)
logger.info("Файлы лидерборда успешно загружены")
logger.info("Генерация лидерборда успешно завершена!")
except Exception as e:
logger.error(f"Ошибка при генерации лидерборда: {e}")
raise
if __name__ == "__main__":
main()