File size: 10,823 Bytes
9ba8fab
 
 
3769468
9ba8fab
 
3769468
6960dc6
da12542
 
f81f1e2
da12542
f81f1e2
 
 
 
da12542
 
ddc83ff
f81f1e2
74d890a
 
f81f1e2
 
 
 
74d890a
 
 
 
 
 
 
 
 
 
 
 
9ba8fab
f81f1e2
9ba8fab
 
d4aa692
ddc83ff
 
3769468
 
 
d4aa692
 
 
 
 
3769468
 
 
d415750
d4aa692
3769468
ddc83ff
f81f1e2
 
ddc83ff
f81f1e2
 
ddc83ff
3769468
 
 
d4aa692
 
f81f1e2
 
d415750
3769468
 
 
ddc83ff
3769468
d415750
3769468
 
d415750
d4aa692
 
 
 
 
 
 
3769468
ddc83ff
3769468
d415750
d4aa692
d415750
d4aa692
f81f1e2
 
d4aa692
ddc83ff
d4aa692
d415750
d4aa692
3769468
ddc83ff
3769468
 
ddc83ff
f81f1e2
 
 
 
 
 
 
 
d4aa692
ddc83ff
d4aa692
 
3769468
d415750
 
f81f1e2
 
3769468
 
 
d415750
 
 
d4aa692
d415750
d4aa692
f81f1e2
d4aa692
 
f81f1e2
 
d4aa692
 
f81f1e2
 
 
d4aa692
f81f1e2
d4aa692
9ba8fab
 
 
ddc83ff
9ba8fab
ddc83ff
d415750
3769468
d4aa692
d415750
29c8f24
d4aa692
d415750
9ba8fab
d415750
d4aa692
ddc83ff
 
3769468
 
d415750
3769468
d4aa692
d415750
3769468
d4aa692
f81f1e2
d4aa692
d415750
f81f1e2
d415750
ddc83ff
d415750
f81f1e2
d415750
 
ddc83ff
d4aa692
ddc83ff
d4aa692
d415750
 
ddc83ff
d4aa692
9ba8fab
ddc83ff
9ba8fab
 
 
d4aa692
 
9ba8fab
d4aa692
 
9ba8fab
d4aa692
3769468
9ba8fab
ddc83ff
d4aa692
c726970
ddc83ff
d415750
9ba8fab
 
d415750
f81f1e2
33f8987
 
9ba8fab
 
d415750
33f8987
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d4aa692
33f8987
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ba8fab
ddc83ff
d415750
 
ddc83ff
3769468
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import gradio as gr
import pandas as pd
from datasets import load_dataset
from jiwer import wer, cer
import os
from datetime import datetime
import re

from huggingface_hub import login

# Authentication setup
token = os.environ.get("HG_TOKEN")
print(f"Token exists: {token is not None}")
if token:
    print(f"Token length: {len(token)}")
    print(f"Token first few chars: {token[:4]}...")
login(token)

print("Loading dataset...")
try:
    # Try loading without use_auth_token parameter since it's not accepted
    dataset = load_dataset("sudoping01/bambara-speech-recognition-benchmark", name="default")["eval"]
    print(f"Successfully loaded dataset with {len(dataset)} samples")
    references = {row["id"]: row["text"] for row in dataset}
except Exception as e:
    print(f"Error loading dataset: {str(e)}")
    try:
        # Second attempt with token passed differently
        from huggingface_hub import HfApi
        api = HfApi(token=token)
        dataset = load_dataset("sudoping01/bambara-speech-recognition-benchmark", name="default")["eval"]
        print(f"Successfully loaded dataset with {len(dataset)} samples")
        references = {row["id"]: row["text"] for row in dataset}
    except Exception as e2:
        print(f"Second attempt error: {str(e2)}")
        # Fallback in case dataset can't be loaded
        references = {}
        print("WARNING: Using empty references dictionary due to dataset loading error")

# Initialize leaderboard file
leaderboard_file = "leaderboard.csv"
if not os.path.exists(leaderboard_file):
    pd.DataFrame(columns=["submitter", "WER", "CER", "timestamp"]).to_csv(leaderboard_file, index=False)
else:
    print(f"Loaded existing leaderboard with {len(pd.read_csv(leaderboard_file))} entries")

def normalize_text(text):
    """
    Normalize text for WER/CER calculation:
    - Convert to lowercase
    - Remove punctuation
    - Replace multiple spaces with single space
    - Strip leading/trailing spaces
    """
    if not isinstance(text, str):
        text = str(text)
    
    # Convert to lowercase
    text = text.lower()
    
    # Remove punctuation, keeping spaces
    text = re.sub(r'[^\w\s]', '', text)
    
    # Normalize whitespace
    text = re.sub(r'\s+', ' ', text).strip()
    
    return text

def calculate_metrics(predictions_df):
    """Calculate WER and CER for predictions."""
    results = []
    total_ref_words = 0
    total_ref_chars = 0

    for _, row in predictions_df.iterrows():
        id_val = row["id"]
        if id_val not in references:
            print(f"Warning: ID {id_val} not found in references")
            continue
            
        reference = normalize_text(references[id_val])
        hypothesis = normalize_text(row["text"])
        
        # Print detailed info for first few entries
        if len(results) < 5:
            print(f"ID: {id_val}")
            print(f"Reference: '{reference}'")
            print(f"Hypothesis: '{hypothesis}'")
        
        # Skip empty strings
        if not reference or not hypothesis:
            print(f"Warning: Empty reference or hypothesis for ID {id_val}")
            continue
            
        # Split into words for jiwer
        reference_words = reference.split()
        hypothesis_words = hypothesis.split()
        reference_chars = list(reference)
        
        if len(results) < 5:
            print(f"Reference words: {reference_words}")
            print(f"Hypothesis words: {hypothesis_words}")
        
        # Calculate metrics
        try:
            # Calculate WER and CER
            sample_wer = wer(reference, hypothesis)
            sample_cer = cer(reference, hypothesis)
            
            # Cap metrics at sensible values to prevent outliers
            sample_wer = min(sample_wer, 2.0)  # Cap at 200% WER
            sample_cer = min(sample_cer, 2.0)  # Cap at 200% CER
            
            # For weighted calculations
            total_ref_words += len(reference_words)
            total_ref_chars += len(reference_chars)
            
            if len(results) < 5:
                print(f"WER: {sample_wer}, CER: {sample_cer}")
                
            results.append({
                "id": id_val,
                "reference": reference,
                "hypothesis": hypothesis,
                "ref_word_count": len(reference_words),
                "ref_char_count": len(reference_chars),
                "wer": sample_wer,
                "cer": sample_cer
            })
        except Exception as e:
            print(f"Error calculating metrics for ID {id_val}: {str(e)}")
    
    if not results:
        raise ValueError("No valid samples for WER/CER calculation")
        
    # Calculate standard average metrics
    avg_wer = sum(item["wer"] for item in results) / len(results)
    avg_cer = sum(item["cer"] for item in results) / len(results)
    
    # Calculate weighted average metrics based on reference length
    weighted_wer = sum(item["wer"] * item["ref_word_count"] for item in results) / total_ref_words
    weighted_cer = sum(item["cer"] * item["ref_char_count"] for item in results) / total_ref_chars
    
    print(f"Simple average WER: {avg_wer:.4f}, CER: {avg_cer:.4f}")
    print(f"Weighted average WER: {weighted_wer:.4f}, CER: {weighted_cer:.4f}")
    print(f"Processed {len(results)} valid samples")
    
    return avg_wer, avg_cer, weighted_wer, weighted_cer, results

def process_submission(submitter_name, csv_file):
    try:
        # Read and validate the uploaded CSV
        df = pd.read_csv(csv_file)
        print(f"Processing submission from {submitter_name} with {len(df)} rows")
        
        if len(df) == 0:
            return "Error: Uploaded CSV is empty.", None
            
        if set(df.columns) != {"id", "text"}:
            return f"Error: CSV must contain exactly 'id' and 'text' columns. Found: {', '.join(df.columns)}", None
            
        if df["id"].duplicated().any():
            dup_ids = df[df["id"].duplicated()]["id"].unique()
            return f"Error: Duplicate IDs found: {', '.join(map(str, dup_ids[:5]))}", None

        # Check if IDs match the reference dataset
        missing_ids = set(references.keys()) - set(df["id"])
        extra_ids = set(df["id"]) - set(references.keys())
        
        if missing_ids:
            return f"Error: Missing {len(missing_ids)} IDs in submission. First few missing: {', '.join(map(str, list(missing_ids)[:5]))}", None
            
        if extra_ids:
            return f"Error: Found {len(extra_ids)} extra IDs not in reference dataset. First few extra: {', '.join(map(str, list(extra_ids)[:5]))}", None
        
        # Calculate WER and CER
        try:
            avg_wer, avg_cer, weighted_wer, weighted_cer, detailed_results = calculate_metrics(df)
            
            # Debug information
            print(f"Calculated metrics - WER: {avg_wer:.4f}, CER: {avg_cer:.4f}")
            print(f"Weighted metrics - WER: {weighted_wer:.4f}, CER: {weighted_cer:.4f}")
            print(f"Processed {len(detailed_results)} valid samples")
            
            # Check for suspiciously low values
            if avg_wer < 0.001:
                print("WARNING: WER is extremely low - likely an error")
                return "Error: WER calculation yielded suspicious results (near-zero). Please check your submission CSV.", None
                
        except Exception as e:
            print(f"Error in metrics calculation: {str(e)}")
            return f"Error calculating metrics: {str(e)}", None
        
        # Update the leaderboard
        leaderboard = pd.read_csv(leaderboard_file)
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        new_entry = pd.DataFrame(
            [[submitter_name, avg_wer, avg_cer, timestamp]],
            columns=["submitter", "WER", "CER", "timestamp"]
        )
        leaderboard = pd.concat([leaderboard, new_entry]).sort_values("WER")
        leaderboard.to_csv(leaderboard_file, index=False)
        
        return f"Submission processed successfully! WER: {avg_wer:.4f}, CER: {avg_cer:.4f}", leaderboard
        
    except Exception as e:
        print(f"Error processing submission: {str(e)}")
        return f"Error processing submission: {str(e)}", None

# Create the Gradio interface
with gr.Blocks(title="Bambara ASR Leaderboard") as demo:
    gr.Markdown(
        """
        # Bambara ASR Leaderboard
        
        This leaderboard ranks and evaluates speech recognition models for the Bambara language.
        Models are ranked based on their Word Error Rate (WER), from lowest to highest.
        """
    )
    
    # Load and display current leaderboard immediately
    with gr.Tabs() as tabs:
        with gr.TabItem("πŸ… Current Rankings"):
            # Show current leaderboard rankings
            current_leaderboard = pd.read_csv(leaderboard_file).sort_values("WER")
            
            gr.Markdown("### Current ASR Model Rankings")
            leaderboard_view = gr.DataFrame(
                value=current_leaderboard,
                interactive=False,
                label="Models are ranked by Word Error Rate (WER) - lower is better"
            )
            
            gr.Markdown(
                """
                ## Metrics Explanation
                - **WER**: Word Error Rate (lower is better) - measures word-level accuracy
                - **CER**: Character Error Rate (lower is better) - measures character-level accuracy
                """
            )
        
        with gr.TabItem("πŸ“Š Submit New Results"):
            gr.Markdown(
                """
                ### Submit a new model for evaluation
                
                Upload a CSV file with 'id' and 'text' columns to evaluate your ASR predictions.
                The 'id's must match those in the reference dataset.
                """
            )
            
            with gr.Row():
                submitter = gr.Textbox(label="Submitter Name or Model Name", placeholder="e.g., MALIBA-AI/asr")
                csv_upload = gr.File(label="Upload CSV File", file_types=[".csv"])
                
            submit_btn = gr.Button("Submit")
            output_msg = gr.Textbox(label="Status", interactive=False)
            leaderboard_display = gr.DataFrame(
                label="Updated Leaderboard",
                value=current_leaderboard,
                interactive=False
            )
            
            submit_btn.click(
                fn=process_submission,
                inputs=[submitter, csv_upload],
                outputs=[output_msg, leaderboard_display]
            )

# Print startup message
print("Starting Bambara ASR Leaderboard app...")

# Launch the app
if __name__ == "__main__":
    demo.launch(share=True)