Whisper large-v3, fine-tuned for Finnish dialects
0
22 commits
1 repos using this model
2 linked in READMEs
updated Oct 15, 2026
Speech-to-text for Finnish dialect speech: it hears dialect and writes standard Finnish with punctuation. A LoRA fine-tune of openai/whisper-large-v3, trained at Haaga-Helia University of Applied Sciences in the GAIK project (Generative AI-enhanced Knowledge Management in Business), a research project led by Haaga-Helia with the University of Helsinki and Tampere University, co-funded by the European Union (ERDF).
GAIK project · Haaga-Helia University of Applied Sciences · developed by Akseli Leskinen (laguagu)
📖 The story, with audio: suomeksi · in English · 🔊 listen and compare models · 🗂️ training data · ⏱️ timestamp variant
📊 code, method and all results: GAIK-project/finnish-asr · 🧰 gaik-toolkit · 💻 GAIK on GitHub · 🌐 gaik.ai
| Test | Whisper large-v3 | This model | Best paid service |
|---|---|---|---|
| 🗣️ Dialect interviews (SKN, 10 municipalities not in training), WER % | 40.4 | 21.1 | 34.7 (Gemini 3.5 Flash) |
| 🗣️ Another dialect corpus (LA-murre), meaning-error points per 100 words ¹ | 25.0 | 14.2 | 27.2 (Gemini 3.5 Flash) |
| 📖 Read sentences (FLEURS Finnish), WER % | 7.4 | 7.6 | 3.3 (ElevenLabs Scribe v2) |
| 🏛️ Parliament speech (VoxPopuli Finnish), WER % | 12.7 | 10.8 | 9.1 (ElevenLabs Scribe v2) |
Use it for dialect speech, the way people talk outside the big cities and older speakers above all: it makes about half the errors of Whisper large-v3 and fewer than every paid service tested (Gemini 3.5 Flash, Google Chirp 3, ElevenLabs Scribe v2, OpenAI gpt-4o-transcribe). On standard Finnish it stays at Whisper's level, and the paid services are better.
WER after lower-casing and removing punctuation. ¹ LA-murre's reference is written in dialect, so a blind LLM judge (Claude Opus 5.5) counted only errors that change the meaning (critical 10, major 5, minor 1), all six models of a segment judged together; GPT-6.1-sol and gpt-oss-120b as judges give the same order. Gemini was asked for standard Finnish on the dialect tests. 9.9 h of test audio in all; every model and result: GAIK-project/finnish-asr · 🔊 listen and compare.
main, tag v1.0: this model, the one to use by default. revision="v1.0" pins it.timestamps: the same recipe trained with Whisper's timestamp tokens on half of the SKN
segments. It writes usable sentence times (58 % of SKN sentence starts within 1 s; this model 1 %)
and transcribes long audio in one go with OpenAI's temperature fallback (18.1 % WER on ten 4-minute
dialect interviews, no loops; this model 19.8 %), at about the same dialect accuracy (SKN 21.4 %,
same LA-murre judge score) but slightly worse on VoxPopuli (11.2 %) and on the webinar in pieces
(15.2 %). Use it when you need sentence timestamps or long-form decoding without cutting the audio
yourself, always with its fallback settings: load it with revision="timestamps"; its card is on
that branch.all-skn: the same recipe trained on all 50 SKN municipalities (81 h of dialect speech
instead of 62). Fewer meaning errors on LA-murre (13.1 against this model's 14.2 in the same judging
round), FLEURS level (7.5 %), VoxPopuli worse (11.9 %). For audio that is mostly dialect: load it
with revision="all-skn"; its card is on
that branch.You need Python with transformers 5 or newer (older versions cannot read this repository's tokenizer files) and preferably a GPU:
pip install "transformers>=5" torch soundfile
The audio must be 16 kHz mono. Whisper listens to at most 30 seconds at a time, so cut longer audio into 15–25 s pieces at its quietest moments and transcribe each piece. The code below does that and prints each piece with the time it starts at:
import numpy as np
import soundfile as sf
from transformers import pipeline
SR = 16000
asr = pipeline(
"automatic-speech-recognition", model="GAIK-project/whisper-large-v3-fi-dialects", dtype="float16", device="cuda"
) # without a GPU: device="cpu" and no dtype (slow)
def quiet_chunks(audio, min_s=15, max_s=25, win_s=0.1):
"""Split at the quietest 100 ms between min_s and max_s into each piece."""
win, start, pieces = int(win_s * SR), 0, []
while len(audio) - start > max_s * SR:
lo, hi = start + int(min_s * SR), start + int(max_s * SR)
frames = (hi - lo) // win
energy = (audio[lo : lo + frames * win].reshape(frames, win) ** 2).mean(axis=1)
cut = lo + int(np.argmin(energy)) * win + win // 2
pieces.append(audio[start:cut])
start = cut
return pieces + [audio[start:]]
audio, sr = sf.read("interview.wav", dtype="float32") # 16 kHz mono; resample first if it is not
pieces = quiet_chunks(audio) # a clip of 25 s or less stays one piece
out = asr(
[{"raw": p, "sampling_rate": SR} for p in pieces],
batch_size=8,
generate_kwargs={"language": "fi", "task": "transcribe"},
)
start = 0.0
for piece, o in zip(pieces, out):
print(f"[{start:6.1f} s] {o['text'].strip()}")
start += len(piece) / SR
Measured on a 180 s Finnish webinar (402 words), with the model downloaded from this repository, against the University of Helsinki team's reference and against the version a listener checked at its 42 disputed spans:
| Decoding | WER % | WER %, checked reference | |
|---|---|---|---|
quiet_chunks above | 13.7 | 13.5 | recommended |
pipeline chunk_length_s=20 | 14.7 | 13.5 | works |
pipeline chunk_length_s=30 | 52.5 | 51.5 | repeats one phrase over and over: do not use |
pipeline chunk_length_s=30, no_repeat_ngram_size=6 | 14.4 | 14.2 | the guard stops the repeating |
Source: the six decodings in results/clip/decoding_check.json,
scored by score_asr.py --decoding into decoding_scores.json.
Do not use return_timestamps=True with this model either. In that mode Whisper keeps its place in
long audio by writing time marks as it goes. This model was trained on text without those time marks,
so its times are wrong and on long audio it can lose its place and repeat itself; the timestamps
revision above was trained with them. Measured on SKN's dialect test
(600 segments, 791 sentence starts), only 0.8 % of the sentence starts had a time mark within 0.5 s
(plain Whisper large-v3: 53.5 %), and on ten 4-minute dialect recordings Whisper's own long-form mode
gave 21.5 % WER with one recording repeating a phrase, against 17.3 % for the pieces above
(details). If you need
times, use the piece start times printed above, which are exact to the cut points, or the
timestamps revision.
The 13.9 % in the first table is the same model before upload, decoded one piece at a time like the
base model's row; this repository's copy with the code above (batch_size=8) gives 13.7 %, one word
of 402 apart.
"Ours, round 2 (published)" in the chart is this model. The model with LA-murre was trained on no-derivatives data and is not shared.
fi_fi
train (2 704 read sentences) and VoxPopuli
fi train (7 504 parliament segments) keep the model's standard Finnish in place. 25 250
training segments in all.q_proj and v_proj
(about 2 % of the weights); AdamW, learning rate 1e-4 with 5 % warm-up and linear decay,
batch 8, bf16. Three epochs, keeping the one with the lowest validation loss (epoch 2: 0.206,
base model 0.538). The adapter is merged into the weights published here.One training run and one seed. The main judge is one model, Claude Opus 5.5. Three open models on CSC's Aitta service, given the same blind task, agree: gpt-oss-120b on all 583 segments prefers the same model as Claude in 81 % of the pairs where both see a difference, and Gemma 4 31B and Qwen3.6 27B, on a sample, in 92–93 %. All four judges rank the internal LA-murre model first, this model and round 1 about level, and plain Whisper large-v3 clearly last; their point scales differ, so compare models within one judge. Code, method, all results and a comparison with 20 other set-ups: GAIK-project/finnish-asr; summaries in gaik-toolkit: finnish_dialect_asr and transcription_eval.
Whisper large-v3, fine-tuned for Finnish dialects
0
22 commits
1 repos using this model
2 linked in READMEs
updated Oct 15, 2026
Speech-to-text for Finnish dialect speech: it hears dialect and writes standard Finnish with punctuation. A LoRA fine-tune of openai/whisper-large-v3, trained at Haaga-Helia University of Applied Sciences in the GAIK project (Generative AI-enhanced Knowledge Management in Business), a research project led by Haaga-Helia with the University of Helsinki and Tampere University, co-funded by the European Union (ERDF).
GAIK project · Haaga-Helia University of Applied Sciences · developed by Akseli Leskinen (laguagu)
📖 The story, with audio: suomeksi · in English · 🔊 listen and compare models · 🗂️ training data · ⏱️ timestamp variant
📊 code, method and all results: GAIK-project/finnish-asr · 🧰 gaik-toolkit · 💻 GAIK on GitHub · 🌐 gaik.ai
| Test | Whisper large-v3 | This model | Best paid service |
|---|---|---|---|
| 🗣️ Dialect interviews (SKN, 10 municipalities not in training), WER % | 40.4 | 21.1 | 34.7 (Gemini 3.5 Flash) |
| 🗣️ Another dialect corpus (LA-murre), meaning-error points per 100 words ¹ | 25.0 | 14.2 | 27.2 (Gemini 3.5 Flash) |
| 📖 Read sentences (FLEURS Finnish), WER % | 7.4 | 7.6 | 3.3 (ElevenLabs Scribe v2) |
| 🏛️ Parliament speech (VoxPopuli Finnish), WER % | 12.7 | 10.8 | 9.1 (ElevenLabs Scribe v2) |
Use it for dialect speech, the way people talk outside the big cities and older speakers above all: it makes about half the errors of Whisper large-v3 and fewer than every paid service tested (Gemini 3.5 Flash, Google Chirp 3, ElevenLabs Scribe v2, OpenAI gpt-4o-transcribe). On standard Finnish it stays at Whisper's level, and the paid services are better.
WER after lower-casing and removing punctuation. ¹ LA-murre's reference is written in dialect, so a blind LLM judge (Claude Opus 5.5) counted only errors that change the meaning (critical 10, major 5, minor 1), all six models of a segment judged together; GPT-6.1-sol and gpt-oss-120b as judges give the same order. Gemini was asked for standard Finnish on the dialect tests. 9.9 h of test audio in all; every model and result: GAIK-project/finnish-asr · 🔊 listen and compare.
main, tag v1.0: this model, the one to use by default. revision="v1.0" pins it.timestamps: the same recipe trained with Whisper's timestamp tokens on half of the SKN
segments. It writes usable sentence times (58 % of SKN sentence starts within 1 s; this model 1 %)
and transcribes long audio in one go with OpenAI's temperature fallback (18.1 % WER on ten 4-minute
dialect interviews, no loops; this model 19.8 %), at about the same dialect accuracy (SKN 21.4 %,
same LA-murre judge score) but slightly worse on VoxPopuli (11.2 %) and on the webinar in pieces
(15.2 %). Use it when you need sentence timestamps or long-form decoding without cutting the audio
yourself, always with its fallback settings: load it with revision="timestamps"; its card is on
that branch.all-skn: the same recipe trained on all 50 SKN municipalities (81 h of dialect speech
instead of 62). Fewer meaning errors on LA-murre (13.1 against this model's 14.2 in the same judging
round), FLEURS level (7.5 %), VoxPopuli worse (11.9 %). For audio that is mostly dialect: load it
with revision="all-skn"; its card is on
that branch.You need Python with transformers 5 or newer (older versions cannot read this repository's tokenizer files) and preferably a GPU:
pip install "transformers>=5" torch soundfile
The audio must be 16 kHz mono. Whisper listens to at most 30 seconds at a time, so cut longer audio into 15–25 s pieces at its quietest moments and transcribe each piece. The code below does that and prints each piece with the time it starts at:
import numpy as np
import soundfile as sf
from transformers import pipeline
SR = 16000
asr = pipeline(
"automatic-speech-recognition", model="GAIK-project/whisper-large-v3-fi-dialects", dtype="float16", device="cuda"
) # without a GPU: device="cpu" and no dtype (slow)
def quiet_chunks(audio, min_s=15, max_s=25, win_s=0.1):
"""Split at the quietest 100 ms between min_s and max_s into each piece."""
win, start, pieces = int(win_s * SR), 0, []
while len(audio) - start > max_s * SR:
lo, hi = start + int(min_s * SR), start + int(max_s * SR)
frames = (hi - lo) // win
energy = (audio[lo : lo + frames * win].reshape(frames, win) ** 2).mean(axis=1)
cut = lo + int(np.argmin(energy)) * win + win // 2
pieces.append(audio[start:cut])
start = cut
return pieces + [audio[start:]]
audio, sr = sf.read("interview.wav", dtype="float32") # 16 kHz mono; resample first if it is not
pieces = quiet_chunks(audio) # a clip of 25 s or less stays one piece
out = asr(
[{"raw": p, "sampling_rate": SR} for p in pieces],
batch_size=8,
generate_kwargs={"language": "fi", "task": "transcribe"},
)
start = 0.0
for piece, o in zip(pieces, out):
print(f"[{start:6.1f} s] {o['text'].strip()}")
start += len(piece) / SR
Measured on a 180 s Finnish webinar (402 words), with the model downloaded from this repository, against the University of Helsinki team's reference and against the version a listener checked at its 42 disputed spans:
| Decoding | WER % | WER %, checked reference | |
|---|---|---|---|
quiet_chunks above | 13.7 | 13.5 | recommended |
pipeline chunk_length_s=20 | 14.7 | 13.5 | works |
pipeline chunk_length_s=30 | 52.5 | 51.5 | repeats one phrase over and over: do not use |
pipeline chunk_length_s=30, no_repeat_ngram_size=6 | 14.4 | 14.2 | the guard stops the repeating |
Source: the six decodings in results/clip/decoding_check.json,
scored by score_asr.py --decoding into decoding_scores.json.
Do not use return_timestamps=True with this model either. In that mode Whisper keeps its place in
long audio by writing time marks as it goes. This model was trained on text without those time marks,
so its times are wrong and on long audio it can lose its place and repeat itself; the timestamps
revision above was trained with them. Measured on SKN's dialect test
(600 segments, 791 sentence starts), only 0.8 % of the sentence starts had a time mark within 0.5 s
(plain Whisper large-v3: 53.5 %), and on ten 4-minute dialect recordings Whisper's own long-form mode
gave 21.5 % WER with one recording repeating a phrase, against 17.3 % for the pieces above
(details). If you need
times, use the piece start times printed above, which are exact to the cut points, or the
timestamps revision.
The 13.9 % in the first table is the same model before upload, decoded one piece at a time like the
base model's row; this repository's copy with the code above (batch_size=8) gives 13.7 %, one word
of 402 apart.
"Ours, round 2 (published)" in the chart is this model. The model with LA-murre was trained on no-derivatives data and is not shared.
fi_fi
train (2 704 read sentences) and VoxPopuli
fi train (7 504 parliament segments) keep the model's standard Finnish in place. 25 250
training segments in all.q_proj and v_proj
(about 2 % of the weights); AdamW, learning rate 1e-4 with 5 % warm-up and linear decay,
batch 8, bf16. Three epochs, keeping the one with the lowest validation loss (epoch 2: 0.206,
base model 0.538). The adapter is merged into the weights published here.One training run and one seed. The main judge is one model, Claude Opus 5.5. Three open models on CSC's Aitta service, given the same blind task, agree: gpt-oss-120b on all 583 segments prefers the same model as Claude in 81 % of the pairs where both see a difference, and Gemma 4 31B and Qwen3.6 27B, on a sample, in 92–93 %. All four judges rank the internal LA-murre model first, this model and round 1 about level, and plain Whisper large-v3 clearly last; their point scales differ, so compare models within one judge. Code, method, all results and a comparison with 20 other set-ups: GAIK-project/finnish-asr; summaries in gaik-toolkit: finnish_dialect_asr and transcription_eval.