1616
1717Downloads and formats datasets from the official HF Open ASR Leaderboard ESB
1818test-only sorted dataset (hf-audio/esb-datasets-test-only-sorted). This is the
19- same data source used by the official leaderboard and the offline NeMo eval
20- pipeline, ensuring apples-to-apples WER comparison.
19+ same data source used by the official leaderboard, ensuring apples-to-apples
20+ WER comparison.
2121
2222Audio paths in JSONL: /dataset/asr-leaderboard/data/{dataset}/{sample_id}.flac
2323
2424Usage:
2525 ns prepare_data asr-leaderboard
2626 ns prepare_data asr-leaderboard --datasets librispeech_clean ami
27- ns prepare_data asr-leaderboard --datasets earnings22
28- ns prepare_data asr-leaderboard --no-audio # skip saving audio files
27+ ns prepare_data asr-leaderboard --no-audio
2928"""
3029
3130import argparse
3231import json
3332from pathlib import Path
3433
34+ import numpy as np
3535import soundfile as sf
36- from datasets import load_dataset
36+ from datasets import Audio , load_dataset
3737from tqdm import tqdm
3838
39+ HF_REPO = "hf-audio/esb-datasets-test-only-sorted"
3940SYSTEM_MESSAGE = "You are a helpful assistant. /no_think"
40- MIN_AUDIO_DURATION = 0.1 # Skip audio shorter than this (causes mel spectrogram errors)
41+ AUDIO_SAMPLE_RATE = 16000
4142
42- # (hf_repo, config, split, text_field, id_field)
43+ # (config, split, text_field, id_field)
4344DATASET_CONFIGS = {
44- "librispeech_clean" : ("hf-audio/esb-datasets-test-only-sorted" , " librispeech" , "test.clean" , "text" , "id" ),
45- "librispeech_other" : ("hf-audio/esb-datasets-test-only-sorted" , " librispeech" , "test.other" , "text" , "id" ),
46- "voxpopuli" : ("hf-audio/esb-datasets-test-only-sorted" , " voxpopuli" , "test" , "text" , "id" ),
47- "tedlium" : ("hf-audio/esb-datasets-test-only-sorted" , " tedlium" , "test" , "text" , "id" ),
48- "gigaspeech" : ("hf-audio/esb-datasets-test-only-sorted" , " gigaspeech" , "test" , "text" , "id" ),
49- "spgispeech" : ("hf-audio/esb-datasets-test-only-sorted" , " spgispeech" , "test" , "text" , "id" ),
50- "earnings22" : ("hf-audio/esb-datasets-test-only-sorted" , " earnings22" , "test" , "text" , "id" ),
51- "ami" : ("hf-audio/esb-datasets-test-only-sorted" , " ami" , "test" , "text" , "id" ),
45+ "librispeech_clean" : ("librispeech" , "test.clean" , "text" , "id" ),
46+ "librispeech_other" : ("librispeech" , "test.other" , "text" , "id" ),
47+ "voxpopuli" : ("voxpopuli" , "test" , "text" , "id" ),
48+ "tedlium" : ("tedlium" , "test" , "text" , "id" ),
49+ "gigaspeech" : ("gigaspeech" , "test" , "text" , "id" ),
50+ "spgispeech" : ("spgispeech" , "test" , "text" , "id" ),
51+ "earnings22" : ("earnings22" , "test" , "text" , "id" ),
52+ "ami" : ("ami" , "test" , "text" , "id" ),
5253}
5354
5455
55- def save_audio_and_format_entry (
56- entry , dataset_name , audio_dir , sample_idx , text_field = "text" , id_field = "id" , with_audio = True
57- ):
58- """Format a dataset entry and optionally save audio file."""
59- text = entry [text_field ].strip ()
56+ def extract_audio (audio_info ):
57+ """Extract audio array and sampling rate from a HF dataset audio entry.
6058
61- system_message = {"role" : "system" , "content" : SYSTEM_MESSAGE }
62- user_message = {"role" : "user" , "content" : "Transcribe the following audio." }
59+ Handles both the legacy dict format ({"array": ..., "sampling_rate": ...})
60+ and the newer AudioDecoder object from torchcodec-based datasets library.
61+ """
62+ if audio_info is None :
63+ return None , None
64+ try :
65+ audio_array = np .array (audio_info ["array" ])
66+ sampling_rate = int (audio_info ["sampling_rate" ])
67+ return audio_array , sampling_rate
68+ except (KeyError , TypeError , IndexError ):
69+ return None , None
70+
71+
72+ def format_entry (entry , dataset_name , audio_dir , text_field , id_field , with_audio ):
73+ """Format a dataset entry into JSONL and optionally save the audio file."""
74+ text = entry [text_field ].strip ()
75+ if not text :
76+ return None
6377
6478 sample_id = str (entry [id_field ]).replace ("/" , "_" )
6579 audio_filename = f"{ Path (sample_id ).stem } .flac"
6680
67- audio_info = entry .get ("audio" , {} )
81+ audio_array , sampling_rate = extract_audio ( entry .get ("audio" ) )
6882 duration = None
69- if isinstance (audio_info , dict ) and "array" in audio_info and "sampling_rate" in audio_info :
70- audio_array = audio_info ["array" ]
71- sampling_rate = audio_info ["sampling_rate" ]
72- duration = len (audio_array ) / sampling_rate
73-
74- if duration < MIN_AUDIO_DURATION :
75- return None
7683
84+ if audio_array is not None and sampling_rate is not None :
85+ duration = len (audio_array ) / sampling_rate
7786 if with_audio :
7887 sf .write (str (audio_dir / audio_filename ), audio_array , sampling_rate )
7988
89+ user_message = {"role" : "user" , "content" : "Transcribe the following audio." }
8090 audio_meta = {"path" : f"/dataset/asr-leaderboard/data/{ dataset_name } /{ audio_filename } " }
8191 if duration is not None :
8292 audio_meta ["duration" ] = float (duration )
8393 user_message ["audio" ] = audio_meta
8494
85- formatted_entry = {
95+ formatted = {
8696 "task_type" : "ASR" ,
8797 "expected_answer" : text ,
88- "messages" : [system_message , user_message ],
98+ "messages" : [{ "role" : "system" , "content" : SYSTEM_MESSAGE } , user_message ],
8999 "subset_for_metrics" : dataset_name ,
100+ "id" : entry [id_field ],
90101 }
91-
92- formatted_entry ["id" ] = entry [id_field ]
93102 if "speaker_id" in entry :
94- formatted_entry ["speaker_id" ] = entry ["speaker_id" ]
103+ formatted ["speaker_id" ] = entry ["speaker_id" ]
95104
96- return formatted_entry
105+ return formatted
97106
98107
99108def prepare_dataset (dataset_name , output_dir , with_audio = True ):
100- """Prepare a single ASR dataset."""
109+ """Download, decode, and write a single ASR dataset to JSONL + audio files ."""
101110 if dataset_name not in DATASET_CONFIGS :
102111 raise ValueError (f"Unknown dataset: { dataset_name } . Available: { list (DATASET_CONFIGS .keys ())} " )
103112
104- hf_repo , hf_config , hf_split , text_field , id_field = DATASET_CONFIGS [dataset_name ]
113+ hf_config , hf_split , text_field , id_field = DATASET_CONFIGS [dataset_name ]
105114
106- print (f"Loading { dataset_name } from { hf_repo } (config={ hf_config } , split={ hf_split } )..." )
107- dataset = load_dataset (hf_repo , hf_config , split = hf_split , trust_remote_code = True )
115+ print (f"Loading { dataset_name } from { HF_REPO } (config={ hf_config } , split={ hf_split } )..." )
116+ dataset = load_dataset (HF_REPO , hf_config , split = hf_split )
117+ if with_audio and "audio" in dataset .column_names :
118+ dataset = dataset .cast_column ("audio" , Audio (sampling_rate = AUDIO_SAMPLE_RATE ))
108119
109120 output_file = output_dir / f"{ dataset_name } .jsonl"
110121 audio_dir = output_dir / "data" / dataset_name
111122
112123 if with_audio :
113124 audio_dir .mkdir (parents = True , exist_ok = True )
114- print (f"Saving audio files to { audio_dir } " )
115125
116126 print (f"Processing { len (dataset )} samples from { dataset_name } ..." )
117-
118127 count = 0
119- skipped = 0
120128 with open (output_file , "w" , encoding = "utf-8" ) as fout :
121- for idx , entry in enumerate (tqdm (dataset , desc = dataset_name )):
122- formatted = save_audio_and_format_entry (
123- entry , dataset_name , audio_dir , idx , text_field = text_field , id_field = id_field , with_audio = with_audio
124- )
129+ for entry in tqdm (dataset , desc = dataset_name ):
130+ formatted = format_entry (entry , dataset_name , audio_dir , text_field , id_field , with_audio )
125131 if formatted is None :
126- skipped += 1
127132 continue
128- if formatted ["expected_answer" ]:
129- fout .write (json .dumps (formatted ) + "\n " )
130- count += 1
131-
132- if skipped > 0 :
133- print (f"Skipped { skipped } samples with audio < { MIN_AUDIO_DURATION } s" )
133+ fout .write (json .dumps (formatted ) + "\n " )
134+ count += 1
134135
135136 print (f"Saved { count } samples to { output_file } " )
136137 return count
@@ -157,25 +158,19 @@ def main():
157158 output_dir .mkdir (parents = True , exist_ok = True )
158159
159160 with_audio = not args .no_audio
160-
161- if args .no_audio :
161+ if not with_audio :
162162 print ("Running without saving audio files." )
163- else :
164- print ("Running with audio. Saving to data/{dataset}/" )
165163
166164 datasets_to_prepare = list (DATASET_CONFIGS .keys ()) if "all" in args .datasets else args .datasets
167165
168166 total_samples = 0
169167 for dataset_name in datasets_to_prepare :
170168 total_samples += prepare_dataset (dataset_name , output_dir , with_audio = with_audio )
171169
172- # Combine all dataset JSONLs into test.jsonl
173170 combined_file = output_dir / "test.jsonl"
174171 print (f"\n Creating combined file: { combined_file } " )
175172
176- all_jsonl_files = sorted (output_dir .glob ("*.jsonl" ))
177- dataset_files = [f for f in all_jsonl_files if f .name != "test.jsonl" ]
178-
173+ dataset_files = sorted (f for f in output_dir .glob ("*.jsonl" ) if f .name != "test.jsonl" )
179174 combined_count = 0
180175 with open (combined_file , "w" , encoding = "utf-8" ) as fout :
181176 for dataset_file in dataset_files :
0 commit comments