What it is
- Base model
openai/whisper-large-v3. The adapter does nothing on its own.
- LoRA r=32, alpha=64, on
q_proj and v_proj only, PEFT 0.19.1.
- 619 optimizer steps, effective batch 8, lr 1e-4, seed 47, one RTX A4000.
- Trained on 2,475 packed examples of Greek council audio. Two councils, Argos and
Orestiada, were held out of training and used for validation.
Load it
from transformers import WhisperForConditionalGeneration, WhisperProcessor
from peft import PeftModel
base = WhisperForConditionalGeneration.from_pretrained("openai/whisper-large-v3")
model = PeftModel.from_pretrained(base, "opencouncil/whisper-large-v3-el-council-lora")
model = model.merge_and_unload()
processor = WhisperProcessor.from_pretrained("openai/whisper-large-v3",
language="greek", task="transcribe")
For v1, pass revision="v1" to from_pretrained.
The decode configuration these numbers were measured with
Every number below comes from this configuration through faster-whisper on a
CTranslate2 float16 conversion of the merged model. A different beam size or a different
temperature ladder gives a different number, so quote these figures only alongside this
configuration.
dict(language="el", beam_size=5, condition_on_previous_text=False,
temperature=[0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
no_speech_threshold=0.6, log_prob_threshold=-1.0,
compression_ratio_threshold=2.4, vad_filter=False)
What changed between v1 and v2
The training data changed. The recipe did not.
v1 treated each corrected utterance as one training example. Those average 3.55 seconds,
and Whisper always reads a 30-second window, so most of every example was padding the
model never meets at inference.
For v2 I dropped every clip where two people speak at once, then packed continuous spans
of a single speaker until each window carried about 22 seconds of speech.
v1 28,967 clips [ utterance ]............................... 3.55 s speech
v2 2,475 packs [ one speaker, one continuous span ] ~22 s speech
|<------- 30-second Whisper window ------->|
Two things moved at the same time, overlap filtering and window occupancy, so this
repository cannot tell you which of them did the work.
Results
Measured on 39 frozen validation windows from Argos and Orestiada, two councils absent
from all training data, and on 391 held-out windows from 117 meetings scored inside
OpenCouncil's own benchmark. Every row below was decoded on one machine with the
configuration above.
Table with columns: validation WER, held-out test WER, deletion rate | validation WER | held-out test WER | deletion rate |
|---|
| v2 (this revision) | 0.1390 | 0.1795 | 0.0313 |
v1 (revision="v1") | 0.1600 | 0.1867 | 0.0525 |
whisper-large-v3, not fine-tuned | | 0.1988 | 0.0335 |
v2 beats v1 on validation across all three random seeds I trained. On the held-out test
set the WER difference is -0.0040 with a 95% meeting-clustered interval of
[-0.0078, +0.0002], which crosses zero.
The difference you can rely on is the deletion rate. v2 drops 0.0313 of the reference
against v1's 0.0525, interval [-0.0251, -0.0174]. It leaves out about 40% less of what
was said. For a workflow where a person reviews the transcript, a wrong word is easier to
catch than a missing sentence.
Domain terms
On 250 occurrences of councillor surnames and place names in the same 39 windows:
Table with columns: DS-WER | DS-WER |
|---|
| Soniox | 0.3280 |
| ElevenLabs Scribe v2 | 0.3720 |
| v2, seed 47 (this revision) | 0.4360 |
| v2, seed 29 | 0.4640 |
| v1 | 0.4800 |
| v2, seed 13 | 0.4840 |
whisper-large-v3, not fine-tuned | 0.5400 |
The three v2 rows differ only in the random seed and span 0.0480, which is wider than the
0.0187 separating the v2 average from v1. Treat the domain-term ordering between v1 and
v2 as unresolved.
What this model does not do
It loses to ElevenLabs Scribe v2 and to Soniox on our own benchmark, on overall WER and
on domain terms both. If you need the best available Greek council transcription and can
pay for an API, use one of those. This adapter is useful when you want a self-hosted
model with no per-minute cost, and it is a clear improvement on the base model it starts
from.
Intended use
Greek municipal council meetings: long-form, multi-speaker, procedural vocabulary,
variable room acoustics. It was fine-tuned on that and measured on that.
Out-of-scope use
- Any language other than Greek.
- Domains other than council or committee proceedings. Generalisation outside this
domain is untested.
- Automated decisions about people. The output contains recognition errors on names at
a rate you can read above.
- Speaker identification. This model does not do it.
Limitations
- Overall WER on held-out council audio is 0.1795, so roughly one word in six differs
from the published transcript.
- Names are the weak point. Errors on them are mostly substitutions rather than
omissions, so a wrong name reads as a plausible name.
- Numbers, dates and amounts are not separately validated.
- The measurements compare against OpenCouncil's published transcripts, which are
themselves human-corrected and carry their own conventions.
Data
This repository contains the adapter weights and configuration. No dataset, no audio,
no transcripts.
License
Apache 2.0, matching the base model.
Provenance
Trained and evaluated in the OpenCouncil GSoC 2026 project. Method, experiment records
and every number above:
github.com/eellak/gsoc2026-opencouncil-stt,
report at
FINAL_REPORT.md.
Which version to use
Use v2, the default. It leaves out far less of the meeting and matches or beats v1
everywhere else. Load revision="v1" if you need to reproduce a result published before
2026-08-23.