A reproducible pipeline for building a speech‑to‑text dataset from Indian Supreme Court proceedings and fine‑tuning Whisper models. It captures what we did in order: data ingestion → alignment → cleaning → dataset export → fine‑tuning → evaluation.
Prereqs: pip install -r requirements.txt
Repository Map
download.py: Download PDFs/audio from data.csv (Dropbox mp3s)audio_files/, transcripts/, meta/: Downloaded dataaligner.py + config.py: Extract PDF text, run NeMo Forced Aligner (NFA), chunk to 10–30s with punctuation/gap constraints paired .wav/.txt files.clean.py: Duration and word count gates, writes filter_report.csv.wer_filtering.py: Builds curation_table.csv with WER-based tags (clean, fair, hard, reject) for optional filtering during fine-tuning.upload_data_to_hf.py: Builds train/test JSON and pushes dataset to HF (features include audio, sentence, metadata) and curation_table.csv for optional filtering.train.py: Minimal fine‑tuning for Whisper (small/medium), optional curation by curation_table.csv tags.eval.py: Batched inference with WER/CER + normalized metrics; OOM‑safe batch reductions.baseline_wer_v2.py, format_results.py, duration.py: Baselines, result formatting, and duration stats.Input spreadsheet: data.csv
python download.py --csv data.csvaudio_files/audio_<idx>.wav - PDFs: transcripts/transcript_<idx>.pdf - Metadata: meta/metadata_<idx>.jsonNEMO_ROOT points to a NeMo checkout that contains tools/nemo_forced_aligner/align.py.|) to encourage denser NFA segments. - Aligns text to audio with NFA; chunks to 10–30s, respecting hard gaps; falls back to word‑CTM to guarantee max length.python aligner.py --nemo_root "$NEMO_ROOT" --audio_dir audio_files --transcript_dir transcripts --output_dir aligned_output_30s_finalaligned_output_30s_final/audio_<idx>_<chunk>.wav/.txtmin_duration=2.0s, min_words=3, min_alnum_chars=6 (no VAD).python clean.py --in_dir aligned_output_30s_final --out_dir aligned_output_30s_final_clean --reject_dir final_data_rejected --report_csv filter_report.csvaligned_output_30s_final_clean/ and Rejected: final_data_rejected/filter_report.csv with drop = 0|1 for each sample.filter_report.csv (keeps only samples where drop=0).wer_min, wer_med, wer_mean, wer_std.clean: wer_min ≤ 0.25 (at least one model performs well)fair: median WER in 45th–85th percentile range AND wer_min ≤ 0.60ambiguous: high disagreement (wer_std > 0.35)drop: wer_min > 0.55 AND median WER above 95th percentile of hard samplesno_pred: no model produced predictionspython wer_filtering.pycuration_table.csv with columns: basename, path, duration, wer_<model> (per model), ref, no_pred, wer_min, wer_med, wer_mean, wer_std, tagmissing_no_predictions.csv (samples with no predictions from any model)train.json/test.json from aligned pairs and splits test by fixed sr_no set.python upload_data_to_hf.py --push-to-hub --hf-username <your_hf_user> --hf-token $HF_TOKENsc_stt_export/train.json, sc_stt_export/test.jsonkarynaur/court_proceedings_stt_dataset (contains train/test splits and curation_table.csv) https://huggingface.co/datasets/karynaur/court_proceedings_stt_datasetdatasets (local or Hub), simple collator, and computes WER/CER + normalized metrics.python train.py --model_id openai/whisper-small --output_dir whisper_small_ft --fp16curation_table.csv has tag and basename|path columns, then set --include_tags clean,fair as needed.whisper_small_ft/ (or whisper_medium_ft/).python eval.py --model_path openai/whisper-small --dataset karynaur/court_proceedings_stt_dataset --split test --batch_size 16 --fp16 --out_file eval/openai_whisper-small_eval.txtpython eval.py --model_path whisper_small_ft --dataset karynaur/court_proceedings_stt_dataset --split test --batch_size 16 --fp16 --out_file eval/resport_whisper_small_ft.txteval/.eval/ files):
openai/whisper-small: WER 44.19, CER 27.50, NORM WER 32.82, NORM CER 24.55openai/whisper-medium: WER 40.87, CER 25.86, NORM WER 29.58, NORM CER 22.99openai/whisper-large-v3: WER 40.77, CER 23.50, NORM WER 26.18, NORM CER 19.97nvidia/parakeet-rnnt-1.1b: WER 47.37, CER 24.90, NORM WER 25.98, NORM CER 20.14nvidia/stt_en_conformer_ctc_large: WER 50.74, CER 27.79, NORM WER 32.57, NORM CER 23.39whisper-small-ft: WER 33.08, CER 19.32, NORM WER 23.78, NORM CER 16.90 https://huggingface.co/karynaur/whisper-small-ft-court-proceedingswhisper-medium-ft: WER 31.27, CER 18.99, NORM WER 22.53, NORM CER 16.61 https://huggingface.co/karynaur/whisper-medium-ft-court-proceedingspython download.py --csv data.csvpython aligner.py --nemo_root "$NEMO_ROOT" --audio_dir audio_files --transcript_dir transcripts --output_dir aligned_output_30s_finalpython clean.py --in_dir aligned_output_30s_final --out_dir aligned_output_30s_final_clean --reject_dir final_data_rejected --report_csv filter_report.csvpython wer_filtering.pypython upload_data_to_hf.py --push-to-hub --hf-username <you> --hf-token $HF_TOKENpython train.py --model_id openai/whisper-small --output_dir whisper_small_ft --fp16python eval.py --model_path whisper_small_ft --out_file eval/resport_whisper_small_ft.txt2 commits
Jupyter Notebook
92.6%
Python
7.4%
A reproducible pipeline for building a speech‑to‑text dataset from Indian Supreme Court proceedings and fine‑tuning Whisper models. It captures what we did in order: data ingestion → alignment → cleaning → dataset export → fine‑tuning → evaluation.
Prereqs: pip install -r requirements.txt
Repository Map
download.py: Download PDFs/audio from data.csv (Dropbox mp3s)audio_files/, transcripts/, meta/: Downloaded dataaligner.py + config.py: Extract PDF text, run NeMo Forced Aligner (NFA), chunk to 10–30s with punctuation/gap constraints paired .wav/.txt files.clean.py: Duration and word count gates, writes filter_report.csv.wer_filtering.py: Builds curation_table.csv with WER-based tags (clean, fair, hard, reject) for optional filtering during fine-tuning.upload_data_to_hf.py: Builds train/test JSON and pushes dataset to HF (features include audio, sentence, metadata) and curation_table.csv for optional filtering.train.py: Minimal fine‑tuning for Whisper (small/medium), optional curation by curation_table.csv tags.eval.py: Batched inference with WER/CER + normalized metrics; OOM‑safe batch reductions.baseline_wer_v2.py, format_results.py, duration.py: Baselines, result formatting, and duration stats.Input spreadsheet: data.csv
python download.py --csv data.csvaudio_files/audio_<idx>.wav - PDFs: transcripts/transcript_<idx>.pdf - Metadata: meta/metadata_<idx>.jsonNEMO_ROOT points to a NeMo checkout that contains tools/nemo_forced_aligner/align.py.|) to encourage denser NFA segments. - Aligns text to audio with NFA; chunks to 10–30s, respecting hard gaps; falls back to word‑CTM to guarantee max length.python aligner.py --nemo_root "$NEMO_ROOT" --audio_dir audio_files --transcript_dir transcripts --output_dir aligned_output_30s_finalaligned_output_30s_final/audio_<idx>_<chunk>.wav/.txtmin_duration=2.0s, min_words=3, min_alnum_chars=6 (no VAD).python clean.py --in_dir aligned_output_30s_final --out_dir aligned_output_30s_final_clean --reject_dir final_data_rejected --report_csv filter_report.csvaligned_output_30s_final_clean/ and Rejected: final_data_rejected/filter_report.csv with drop = 0|1 for each sample.filter_report.csv (keeps only samples where drop=0).wer_min, wer_med, wer_mean, wer_std.clean: wer_min ≤ 0.25 (at least one model performs well)fair: median WER in 45th–85th percentile range AND wer_min ≤ 0.60ambiguous: high disagreement (wer_std > 0.35)drop: wer_min > 0.55 AND median WER above 95th percentile of hard samplesno_pred: no model produced predictionspython wer_filtering.pycuration_table.csv with columns: basename, path, duration, wer_<model> (per model), ref, no_pred, wer_min, wer_med, wer_mean, wer_std, tagmissing_no_predictions.csv (samples with no predictions from any model)train.json/test.json from aligned pairs and splits test by fixed sr_no set.python upload_data_to_hf.py --push-to-hub --hf-username <your_hf_user> --hf-token $HF_TOKENsc_stt_export/train.json, sc_stt_export/test.jsonkarynaur/court_proceedings_stt_dataset (contains train/test splits and curation_table.csv) https://huggingface.co/datasets/karynaur/court_proceedings_stt_datasetdatasets (local or Hub), simple collator, and computes WER/CER + normalized metrics.python train.py --model_id openai/whisper-small --output_dir whisper_small_ft --fp16curation_table.csv has tag and basename|path columns, then set --include_tags clean,fair as needed.whisper_small_ft/ (or whisper_medium_ft/).python eval.py --model_path openai/whisper-small --dataset karynaur/court_proceedings_stt_dataset --split test --batch_size 16 --fp16 --out_file eval/openai_whisper-small_eval.txtpython eval.py --model_path whisper_small_ft --dataset karynaur/court_proceedings_stt_dataset --split test --batch_size 16 --fp16 --out_file eval/resport_whisper_small_ft.txteval/.eval/ files):
openai/whisper-small: WER 44.19, CER 27.50, NORM WER 32.82, NORM CER 24.55openai/whisper-medium: WER 40.87, CER 25.86, NORM WER 29.58, NORM CER 22.99openai/whisper-large-v3: WER 40.77, CER 23.50, NORM WER 26.18, NORM CER 19.97nvidia/parakeet-rnnt-1.1b: WER 47.37, CER 24.90, NORM WER 25.98, NORM CER 20.14nvidia/stt_en_conformer_ctc_large: WER 50.74, CER 27.79, NORM WER 32.57, NORM CER 23.39whisper-small-ft: WER 33.08, CER 19.32, NORM WER 23.78, NORM CER 16.90 https://huggingface.co/karynaur/whisper-small-ft-court-proceedingswhisper-medium-ft: WER 31.27, CER 18.99, NORM WER 22.53, NORM CER 16.61 https://huggingface.co/karynaur/whisper-medium-ft-court-proceedingspython download.py --csv data.csvpython aligner.py --nemo_root "$NEMO_ROOT" --audio_dir audio_files --transcript_dir transcripts --output_dir aligned_output_30s_finalpython clean.py --in_dir aligned_output_30s_final --out_dir aligned_output_30s_final_clean --reject_dir final_data_rejected --report_csv filter_report.csvpython wer_filtering.pypython upload_data_to_hf.py --push-to-hub --hf-username <you> --hf-token $HF_TOKENpython train.py --model_id openai/whisper-small --output_dir whisper_small_ft --fp16python eval.py --model_path whisper_small_ft --out_file eval/resport_whisper_small_ft.txt2 commits
Jupyter Notebook
92.6%
Python
7.4%