Anonumous commited on
Commit
3b7d44a
·
1 Parent(s): 21fdc42

Update benchmark code

Browse files
Files changed (5) hide show
  1. app.py +36 -0
  2. constants.py +114 -0
  3. requirements.txt +4 -0
  4. styles.py +23 -0
  5. utils.py +149 -0
app.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from constants import INTRODUCTION_TEXT, METRICS_TAB_TEXT, SUBMIT_TAB_TEXT
4
+ from utils import init_repo, load_data, process_submit, get_datasets_description
5
+ from styles import LEADERBOARD_CSS
6
+
7
+ init_repo()
8
+
9
+ with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo:
10
+ gr.HTML(
11
+ '<img src="https://cdn.donmai.us/original/67/9c/__taihou_and_surcouf_azur_lane_and_1_more_drawn_by_yunsang__679c42b017a91a2349b25acfc7935157.png" style="width: 100%; height: auto;">'
12
+ )
13
+ gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
14
+
15
+ with gr.Tabs():
16
+ with gr.Tab("🏅 Лидерборд"):
17
+ leaderboard_html = gr.HTML(value=load_data(), every=60)
18
+
19
+ with gr.Tab("📈 Метрики"):
20
+ gr.Markdown(METRICS_TAB_TEXT, elem_classes="markdown-text")
21
+
22
+ with gr.Tab("📊 Датасеты"):
23
+ gr.Markdown(get_datasets_description(), elem_classes="markdown-text")
24
+
25
+ with gr.Tab("✉️ Отправить результат"):
26
+ gr.Markdown(SUBMIT_TAB_TEXT, elem_classes="markdown-text")
27
+ json_input = gr.TextArea(label="JSON")
28
+ submit_btn = gr.Button("🚀 Отправить")
29
+ output_msg = gr.Textbox(label="Статус")
30
+ submit_btn.click(
31
+ process_submit,
32
+ inputs=json_input,
33
+ outputs=[leaderboard_html, output_msg],
34
+ )
35
+
36
+ demo.launch()
constants.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ INTRODUCTION_TEXT = """
4
+ # Русский ASR Лидерборд
5
+ Добро пожаловать в лидерборд для моделей автоматического распознавания речи (ASR) на русском языке.
6
+ Здесь вы можете сравнить производительность различных моделей по метрикам WER (Word Error Rate) и CER (Character Error Rate) на нескольких датасетах.
7
+ Лидерборд сортируется по среднему WER (⬇️ - чем ниже, тем лучше).
8
+ Наведите курсор на значение WER в колонке датасета, чтобы увидеть CER.
9
+ Все метрики указаны в процентах (%).
10
+ """
11
+
12
+ METRICS_TAB_TEXT = """
13
+ # Метрики
14
+ Метрики рассчитываются на текстах в нижнем регистре и без пунктуации.
15
+
16
+ - **WER (Word Error Rate)**: Ошибка на уровне слов. Рассчитывается как:
17
+
18
+ $$ WER = \\frac{S + D + I}{N} $$
19
+
20
+ где S - количество замен, D - удалений, I - вставок, N - количество слов в референсе.
21
+
22
+ - **CER (Character Error Rate)**: Ошибка на уровне символов. Аналогичная формула, но для символов:
23
+
24
+ $$ CER = \\frac{S + D + I}{N} $$
25
+
26
+ где S, D, I, N - соответственно замены, удаления, вставки и количество символов в референсе.
27
+
28
+ - **Средние значения**: Простое среднее по всем датасетам.
29
+ - Все метрики нормализованы и представлены в процентах для удобства сравнения.
30
+ """
31
+
32
+ SUBMIT_TAB_TEXT = """
33
+ # Отправить результат
34
+ Чтобы добавить вашу модель в лидерборд, отправьте JSON с результатами. Метрики должны быть в диапазоне [0, 1] (не в процентах).
35
+
36
+ Формат:
37
+ ```json
38
+ {
39
+ "model_name": "MyAwesomeASRModel",
40
+ "link": "https://huggingface.co/myusername/my-asr-model",
41
+ "license": "Apache-2.0",
42
+ "metrics": {
43
+ "Russian_LibriSpeech": {
44
+ "wer": 0.1234,
45
+ "cer": 0.0567
46
+ },
47
+ "Common_Voice_Corpus_22.0": {
48
+ "wer": 0.2345,
49
+ "cer": 0.0789
50
+ },
51
+ "Tone_Webinars": {
52
+ "wer": 0.3456,
53
+ "cer": 0.0987
54
+ },
55
+ "Tone_Books": {
56
+ "wer": 0.4567,
57
+ "cer": 0.1098
58
+ },
59
+ "Tone_Speak": {
60
+ "wer": 0.5678,
61
+ "cer": 0.1209
62
+ },
63
+ "Sova_RuDevices": {
64
+ "wer": 0.6789,
65
+ "cer": 0.1310
66
+ }
67
+ }
68
+ }
69
+ В отчёте обязательно должны быть все датасеты, а именно: Russian_LibriSpeech, Common_Voice_Corpus_22.0, Tone_Webinars, Tone_Books, Tone_Speak, Sova_RuDevices.
70
+ После отправки лидерборд обновится автоматически.
71
+ """
72
+ REPO_ID = "Vikhrmodels/russian-asr-leaderboard"
73
+ HF_TOKEN = os.getenv("HF_TOKEN")
74
+ DATASETS = [
75
+ "Russian_LibriSpeech",
76
+ "Common_Voice_Corpus_22.0",
77
+ "Tone_Webinars",
78
+ "Tone_Books",
79
+ "Tone_Speak",
80
+ "Sova_RuDevices",
81
+ ]
82
+ SHORT_DATASET_NAMES = ["RuLS", "CV 22.0", "Webinars", "Books", "Speak", "Sova"]
83
+ DATASET_DESCRIPTIONS = {
84
+ "RuLS": {
85
+ "full_name": "Russian_LibriSpeech",
86
+ "description": "Russian LibriSpeech (RuLS) — датасет на основе аудиокниг из общественного достояния от LibriVox, содержащий около 98 часов русской речи с транскрипциями.",
87
+ "num_rows": 1352,
88
+ },
89
+ "CV 22.0": {
90
+ "full_name": "Common_Voice_Corpus_22.0",
91
+ "description": "Common Voice — краудсорсинговый многоязычный корпус речи от Mozilla. Версия 22.0 включает данные русской речи с транскрипциями.",
92
+ "num_rows": 10244,
93
+ },
94
+ "Webinars": {
95
+ "full_name": "Tone_Webinars",
96
+ "description": "Tone_Webinars — датасет русской речи из вебинаров с транскрипциями.",
97
+ "num_rows": 21587,
98
+ },
99
+ "Books": {
100
+ "full_name": "Tone_Books",
101
+ "description": "Tone_Books — датасет русских аудиокниг с транскрипциями.",
102
+ "num_rows": 4938,
103
+ },
104
+ "Speak": {
105
+ "full_name": "Tone_Speak",
106
+ "description": "Tone_Speak — датасет синтетической русской речи с транскрипциями.",
107
+ "num_rows": 700,
108
+ },
109
+ "Sova": {
110
+ "full_name": "Sova_RuDevices",
111
+ "description": "SOVA RuDevices — акустический корпус примерно 100 часов 16kHz живой русской речи, записанной ��а устройствах, с ручной транскрипцией.",
112
+ "num_rows": 5799,
113
+ },
114
+ }
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ pandas
3
+ huggingface_hub
4
+ datasets
styles.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LEADERBOARD_CSS = """
2
+ .leaderboard-wrapper { overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none; margin-bottom: 40px; }
3
+ .leaderboard-wrapper::-webkit-scrollbar { display: none; }
4
+ .leaderboard-table { min-width: 100%; }
5
+ .leaderboard-table table { border-collapse: collapse; width: 100%; }
6
+ .leaderboard-table th, .leaderboard-table td { border: 1px solid #ddd; padding: 8px; text-align: center; }
7
+ .leaderboard-table th { background-color: #f2f2f2; font-weight: bold; }
8
+ .leaderboard-table tr:nth-child(even) { background-color: #f9f9f9; }
9
+ .leaderboard-table tr:hover { background-color: #f1f1f1; }
10
+ .leaderboard-table a { color: #0366d6; text-decoration: none; }
11
+ .leaderboard-table a:hover { text-decoration: underline; }
12
+ .leaderboard-table span { cursor: pointer; }
13
+ /* Dark mode */
14
+ .dark .leaderboard-table th, .dark .leaderboard-table td { border-color: #30363d; color: #e0e0e0; }
15
+ .dark .leaderboard-table th { background-color: #21262d; }
16
+ .dark .leaderboard-table tr:nth-child(even) { background-color: #161b22; }
17
+ .dark .leaderboard-table tr:hover { background-color: #0d1117; }
18
+ .dark .leaderboard-table a { color: #58a6ff; }
19
+ /* Other CSS */
20
+ .gradio-container { max-width: 1400px; margin: auto; padding: 20px; }
21
+ .markdown-text { color: #24292e; padding: 15px; border-radius: 6px; background-color: #f6f8fa; margin-bottom: 20px; }
22
+ .dark .markdown-text { color: #c9d1d9; background-color: #161b22; }
23
+ """
utils.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import pandas as pd
3
+ from statistics import mean
4
+ from huggingface_hub import HfApi, create_repo
5
+ from datasets import load_dataset, Dataset
6
+ from datasets.data_files import EmptyDatasetError
7
+
8
+ from constants import (
9
+ REPO_ID,
10
+ HF_TOKEN,
11
+ DATASETS,
12
+ SHORT_DATASET_NAMES,
13
+ DATASET_DESCRIPTIONS,
14
+ )
15
+
16
+ api = HfApi(token=HF_TOKEN)
17
+
18
+
19
+ def init_repo():
20
+ try:
21
+ api.repo_info(REPO_ID, repo_type="dataset")
22
+ except:
23
+ create_repo(REPO_ID, repo_type="dataset", private=True, token=HF_TOKEN)
24
+
25
+
26
+ def load_data():
27
+ columns = (
28
+ ["model_name", "link", "license", "overall_wer", "overall_cer"]
29
+ + [f"wer_{ds}" for ds in DATASETS]
30
+ + [f"cer_{ds}" for ds in DATASETS]
31
+ )
32
+ try:
33
+ dataset = load_dataset(REPO_ID, token=HF_TOKEN)
34
+ df = dataset["train"].to_pandas()
35
+ except EmptyDatasetError:
36
+ df = pd.DataFrame(columns=columns)
37
+
38
+ if not df.empty:
39
+ df = df.sort_values("overall_wer").reset_index(drop=True)
40
+ df.insert(0, "rank", df.index + 1)
41
+
42
+ df["overall_wer"] = (df["overall_wer"] * 100).round(2).apply(lambda x: f"{x}")
43
+ df["overall_cer"] = (df["overall_cer"] * 100).round(2).apply(lambda x: f"{x}")
44
+ for ds in DATASETS:
45
+ df[f"wer_{ds}"] = (df[f"wer_{ds}"] * 100).round(2)
46
+ df[f"cer_{ds}"] = (df[f"cer_{ds}"] * 100).round(2)
47
+
48
+ for short_ds, ds in zip(SHORT_DATASET_NAMES, DATASETS):
49
+ df[short_ds] = df.apply(
50
+ lambda row: f'<span title="CER: {row[f"cer_{ds}"]:.2f}" style="cursor: help;">{row[f"wer_{ds}"]:.2f}</span>',
51
+ axis=1,
52
+ )
53
+ df = df.drop(columns=[f"wer_{ds}", f"cer_{ds}"])
54
+
55
+ df["model_name"] = df.apply(
56
+ lambda row: f'<a href="{row["link"]}" target="_blank">{row["model_name"]}</a>',
57
+ axis=1,
58
+ )
59
+
60
+ df = df.drop(columns=["link"])
61
+
62
+ df["license"] = df["license"].apply(
63
+ lambda x: "Открытая"
64
+ if any(
65
+ term in x.lower() for term in ["mit", "apache", "bsd", "gpl", "open"]
66
+ )
67
+ else "Закрытая"
68
+ )
69
+
70
+ df.rename(
71
+ columns={
72
+ "overall_wer": "Средний WER ⬇️",
73
+ "overall_cer": "Средний CER ⬇️",
74
+ "license": "Тип модели",
75
+ "model_name": "Модель",
76
+ "rank": "Ранг",
77
+ },
78
+ inplace=True,
79
+ )
80
+
81
+ table_html = df.to_html(escape=False, index=False)
82
+ return f'<div class="leaderboard-wrapper"><div class="leaderboard-table">{table_html}</div></div>'
83
+ else:
84
+ return (
85
+ '<div class="leaderboard-wrapper"><div class="leaderboard-table"><table><thead><tr><th>Ранг</th><th>Модель</th><th>Тип модели</th><th>Средний WER ⬇️</th><th>Средний CER ⬇️</th>'
86
+ + "".join(f"<th>{short}</th>" for short in SHORT_DATASET_NAMES)
87
+ + "</tr></thead><tbody></tbody></table></div></div>"
88
+ )
89
+
90
+
91
+ def process_submit(json_str):
92
+ columns = (
93
+ ["model_name", "link", "license", "overall_wer", "overall_cer"]
94
+ + [f"wer_{ds}" for ds in DATASETS]
95
+ + [f"cer_{ds}" for ds in DATASETS]
96
+ )
97
+ try:
98
+ data = json.loads(json_str)
99
+
100
+ required_keys = ["model_name", "link", "license", "metrics"]
101
+ if not all(key in data for key in required_keys):
102
+ raise ValueError(
103
+ "Неверная структура JSON. Требуемые поля: model_name, link, license, metrics"
104
+ )
105
+
106
+ metrics = data["metrics"]
107
+ if set(metrics.keys()) != set(DATASETS):
108
+ raise ValueError(
109
+ f"Метрики должны быть для всех датасетов: {', '.join(DATASETS)}"
110
+ )
111
+
112
+ wers = []
113
+ cers = []
114
+ row = {
115
+ "model_name": data["model_name"],
116
+ "link": data["link"],
117
+ "license": data["license"],
118
+ }
119
+ for ds in DATASETS:
120
+ if "wer" not in metrics[ds] or "cer" not in metrics[ds]:
121
+ raise ValueError(f"Для {ds} требуются wer и cer")
122
+ row[f"wer_{ds}"] = metrics[ds]["wer"]
123
+ row[f"cer_{ds}"] = metrics[ds]["cer"]
124
+ wers.append(metrics[ds]["wer"])
125
+ cers.append(metrics[ds]["cer"])
126
+
127
+ row["overall_wer"] = mean(wers)
128
+ row["overall_cer"] = mean(cers)
129
+
130
+ try:
131
+ dataset = load_dataset(REPO_ID, token=HF_TOKEN)
132
+ df = dataset["train"].to_pandas()
133
+ except EmptyDatasetError:
134
+ df = pd.DataFrame(columns=columns)
135
+ new_df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
136
+ new_dataset = Dataset.from_pandas(new_df)
137
+ new_dataset.push_to_hub(REPO_ID, token=HF_TOKEN)
138
+
139
+ updated_html = load_data()
140
+ return updated_html, "Успешно добавлен��!"
141
+ except Exception as e:
142
+ return None, f"Ошибка: {str(e)}"
143
+
144
+
145
+ def get_datasets_description():
146
+ desc = "# Описание датасетов\n\n"
147
+ for short_ds, info in DATASET_DESCRIPTIONS.items():
148
+ desc += f"### {short_ds} ({info['full_name']})\n{info['description']}\n- Количество записей: {info['num_rows']}\n\n"
149
+ return desc