Spaces:
Running
Running
import gradio as gr | |
import pandas as pd | |
import json | |
# --- 1. Загрузка и подготовка данных --- | |
def load_data(json_path='data.json'): | |
"""Загружает данные из JSON и преобразует их в pandas DataFrame.""" | |
with open(json_path, 'r', encoding='utf-8') as f: | |
data = json.load(f) | |
df = pd.DataFrame(data) | |
# --- ИСПРАВЛЕНИЕ: Преобразуем колонку с датой в формат datetime --- | |
# dayfirst=True указывает pandas, что в формате ДД/ММ/ГГГГ день идет первым. | |
df['test_date'] = pd.to_datetime(df['test_date'], dayfirst=True) | |
return df | |
# --- 2. Логика фильтрации и обновления таблицы --- | |
def update_leaderboard(data_df, filter_type, search_query, show_outdated): | |
"""Фильтрует DataFrame на основе выбранного типа, поискового запроса и флага устаревших данных.""" | |
# Шаг 1: Фильтрация по устаревшим данным | |
if not show_outdated: | |
# Скрываем модели с пометкой об устаревших данных | |
filtered_df = data_df[~data_df['notes'].str.contains('устаревших данных', na=False, case=False)].copy() | |
else: | |
filtered_df = data_df.copy() | |
# Шаг 2: Фильтрация по типу | |
if filter_type == "Только войсклонинг": | |
filtered_df = filtered_df[filtered_df['type'] == 'voice_cloning'].copy() | |
elif filter_type == "Без войсклонинга": | |
filtered_df = filtered_df[filtered_df['type'] == 'single_speaker'].copy() | |
# else: "Все модели" - оставляем как есть | |
# Шаг 3: Фильтрация по поисковому запросу | |
if search_query: | |
query = search_query.lower() | |
filtered_df = filtered_df[ | |
filtered_df['engine'].str.lower().str.contains(query) | | |
filtered_df['voice'].str.lower().str.contains(query) | |
] | |
# Шаг 4: Подготовка DataFrame для отображения | |
# --- ИСПРАВЛЕНИЕ: Форматируем дату в 'ГГГГ-ММ-ДД' для корректной сортировки в UI --- | |
# Создаем копию, чтобы не изменять оригинальный DataFrame в gr.State | |
display_df = filtered_df.copy() | |
display_df['test_date'] = display_df['test_date'].dt.strftime('%Y-%m-%d') | |
column_mapping = { | |
"engine": "Движок", "voice": "Голос", "test_date": "Дата", | |
"hardware": "Железо", "utmos": "UTMOS (↑)", "cer": "CER (↓)", | |
"encodec_fad": "FAD (↓)", "similarity_avg": "Похожесть Avg (↑)", | |
"xrt_gpu": "xRT GPU (↓)", "xrt_cpu": "xRT CPU (↓)", "notes": "Примечания" | |
} | |
display_df = display_df[column_mapping.keys()].rename(columns=column_mapping) | |
return display_df | |
# --- 3. Создание интерфейса Gradio --- | |
# Загружаем данные один раз при старте приложения | |
original_df = load_data() | |
# Сортируем по-умолчанию по дате (самые новые вверху), скрывая устаревшие | |
initial_display_df = update_leaderboard(original_df, "Все модели", "", show_outdated=False) | |
initial_display_df = initial_display_df.sort_values(by="Дата", ascending=False) | |
with gr.Blocks(theme=gr.themes.Soft(), css="footer {visibility: hidden}") as demo: | |
gr.Markdown("# 🏆 Лидерборд TTS моделей для русского языка") | |
gr.Markdown( | |
""" | |
Этот лидерборд предназначен для сравнения различных Text-to-Speech моделей. | |
### Описание метрик: | |
- **UTMOS (↑)**: Оценка качества речи, основанная на мнении слушателей (Mean Opinion Score). **Больше — лучше.** | |
- **CER (↓)**: Character Error Rate (коэффициент ошибок по символам). Показывает, насколько часто синтез делает ошибки в произношении. **Меньше — лучше.** | |
- **FAD (↓)**: Fréchet Audio Distance. Объективная метрика, измеряющая расстояние между распределениями реального и синтезированного аудио. **Меньше — лучше.** | |
- **Похожесть Avg (↑)**: Средняя оценка схожести голоса с оригиналом при клонировании. **Больше — лучше.** | |
- **xRT GPU/CPU (↓)**: Real-Time Factor. Во сколько раз синтез быстрее (если < 1) или медленнее (если > 1) реального времени на GPU/CPU. **Меньше — лучше.** | |
- **Железо**: Тип оборудования, на котором производился тест (Cloud - облачный сервис, Local GPU/CPU - локальное железо, RTX 4090 - конкретная видеокарта). | |
*Кликните на заголовок колонки для сортировки. По умолчанию отсортировано по дате (сначала новые).* | |
""" | |
) | |
gr.Markdown("✉️ Чтобы добавить свою модель, а также вопросы и предложения пишите в Telegram [@bceloss](https://t.me/bceloss)") | |
gr.Markdown('✉️ Добавляйтесь в чат "Распознавание и синтез речи" [@speech_recognition_ru](https://t.me/speech_recognition_ru)') | |
gr.Markdown('👥 Авторы: Nikolay Shmyrev [@nshmyrev](https://t.me/nshmyrev), Denis Petrov [@bceloss](https://t.me/bceloss)') | |
with gr.Row(): | |
with gr.Column(scale=3): | |
filter_radio = gr.Radio( | |
["Все модели", "Только войсклонинг", "Без войсклонинга"], | |
label="Тип модели", | |
value="Все модели" | |
) | |
with gr.Column(scale=3): | |
search_box = gr.Textbox( | |
label="Поиск по названию движка или голоса", | |
placeholder="Например, Silero, Vosk, Multi..." | |
) | |
with gr.Column(scale=2): | |
show_outdated_checkbox = gr.Checkbox( | |
label="Показать модели с устаревшими данными", | |
value=False, | |
info="⚠️ Данные этих моделей могут быть неточными" | |
) | |
# Информационное сообщение о скрытых моделях | |
outdated_info = gr.Markdown(visible=False) | |
leaderboard_df = gr.DataFrame( | |
value=initial_display_df, | |
interactive=True, | |
) | |
# Используем gr.State для передачи полного DataFrame с правильными типами данных | |
df_state = gr.State(original_df) | |
def on_change(filter_type, search_query, show_outdated, data_df): | |
"""Обновляет таблицу и показывает информацию о скрытых моделях.""" | |
updated_df = update_leaderboard(data_df, filter_type, search_query, show_outdated) | |
# Подсчитываем количество скрытых моделей с устаревшими данными | |
if not show_outdated: | |
outdated_count = data_df[data_df['notes'].str.contains('устаревших данных', na=False, case=False)].shape[0] | |
if outdated_count > 0: | |
info_text = f"ℹ️ **Скрыто моделей с устаревшими данными: {outdated_count}**. Включите опцию выше, чтобы показать их." | |
return updated_df, gr.update(value=info_text, visible=True) | |
return updated_df, gr.update(visible=False) | |
# Связываем все элементы управления с функцией обновления | |
filter_radio.change( | |
fn=on_change, | |
inputs=[filter_radio, search_box, show_outdated_checkbox, df_state], | |
outputs=[leaderboard_df, outdated_info] | |
) | |
search_box.change( | |
fn=on_change, | |
inputs=[filter_radio, search_box, show_outdated_checkbox, df_state], | |
outputs=[leaderboard_df, outdated_info] | |
) | |
show_outdated_checkbox.change( | |
fn=on_change, | |
inputs=[filter_radio, search_box, show_outdated_checkbox, df_state], | |
outputs=[leaderboard_df, outdated_info] | |
) | |
# Показываем информацию о скрытых моделях при загрузке | |
demo.load( | |
fn=lambda: f"ℹ️ **Скрыто моделей с устаревшими данными: {original_df[original_df['notes'].str.contains('устаревших данных', na=False, case=False)].shape[0]}**. Включите опцию выше, чтобы показать их.", | |
outputs=outdated_info | |
) | |
# --- 4. Запуск приложения --- | |
if __name__ == "__main__": | |
demo.launch() |