svale-600M-hf

3dio-ai/svale-600M converted from its NeMo archive to transformers format (ParakeetForTDT). Weights are unchanged — this is a format conversion only, produced with the conversion script that ships with transformers:

python src/transformers/models/parakeet/convert_nemo_to_hf.py \
  --hf_repo_id 3dio-ai/svale-600M --model_type tdt --output_dir .

Requirements

pip install "transformers>=5.12" librosa torch

Both are load-bearing, and neither failure is obvious from the traceback:

  • transformers>=5.12 is the floor for the parakeet_tdt architecture. Older versions raise ValueError: The checkpoint you are trying to load has model type parakeet_tdt but Transformers does not recognize this architecture.
  • librosa is imported by the feature extractor, not by transformers itself, so a plain transformers + torch environment fails at load with ImportError: ParakeetFeatureExtractor requires the librosa library.

Use

import numpy as np
import torch
from transformers import pipeline

asr = pipeline(
    "automatic-speech-recognition",
    model="kasper-cbx/svale-600M-hf",
    dtype=torch.bfloat16,   # fp32 weights; bf16 is what the conversion was checked in
    device="cuda",
)

# 16 kHz mono float32, one clip per call — see the batching note below.
asr({"raw": audio, "sampling_rate": 16000})

Output is lowercase and unpunctuated, like the original. Danish only — it takes no language or task argument, and passing Whisper-style generate_kwargs will fail.

Decode one clip at a time

batch_size > 1 corrupts output. Batched, the TDT decoder keeps emitting into the padding that makes a batch rectangular, so short clips come back with their tails repeated — "kø kø" decodes as "kø kø kø kø kø kø kø kø". Longer clips in the same batch pick up a few spurious trailing words.

On a 45-utterance fixture, batched decoding reproduced the NeMo original on 23/45 utterances; one at a time reproduced 42/45. Every number on this card was measured one at a time, for both checkpoints.

This is a property of the batched TDT decoding path, not of these weights: the same happens with the base model, and adding the eos_token_id below does not change it (EOS is not what stops a frame-synchronous decoder).

Expected warnings

  • Using the model-agnostic default max_length (=NNN) — once per call, harmless. The value is derived from encoder length and tracks clip length (observed 50→3490); a fixed max_new_tokens is what would truncate a long clip. That it does not truncate is measured — see the parity table.

Parity with the original

Both checkpoints were run over the full CoRal-v3 conversation test split (8,438 utterances, 8h 01m of audio) on one L4, decoding one clip at a time in both cases. Scoring uses the Danish ASR leaderboard's own normaliser and corpus-WER metric, so these numbers sit on the same scale as its published ones.

WER CER Throughput
3dio-ai/svale-600M (NeMo) 21.59 12.55 66.2x realtime
kasper-cbx/svale-600M-hf (this repo) 21.54 12.50 49.1x realtime
  • WER delta −0.05 pp, paired bootstrap 95% CI [−0.14, +0.03] pp — the interval includes zero, so the two are statistically indistinguishable on this set.
  • 87.3% of hypotheses (7,364 / 8,438) are byte-identical; the rest differ by a word or two, in both directions.
  • The NeMo run reproduces the leaderboard's published 21.52 to within 0.07 pp, which is the check that the harness here is measuring the same thing it does.

Accuracy is on parity. Throughput is not: unbatched, this conversion runs at about 0.74x the original's rate, the difference being per-call Python overhead rather than anything about the weights.

Differences from a plain conversion run

generation_config.json carries "eos_token_id": 3, which the conversion script does not emit but nvidia/parakeet-tdt-0.6b-v3 (the base this was fine-tuned from) does. The two tokenizers are byte-identical and id 3 is <|endoftext|> in both, so the value is the base model's, not an invention here. It was added for fidelity with the base config; decoding is unchanged by it, verified at batch_size 1 and 4.

Nothing else differs from what the script produces, and the weights are untouched.

Why convert at all

The original ships only as svale-600M.nemo, so using it requires nemo_toolkit. In a Hugging Face ZeroGPU Space that is a problem beyond dependency weight: NeMo resident in the main process breaks the fork() ZeroGPU uses to start GPU workers, and every request fails with "GPU task aborted" — including requests for unrelated models in the same app. Converting removes NeMo from the runtime entirely.

Licence

Inherits the NVIDIA Open Model License from the base model, and the CoRal OpenRAIL-D use restrictions that come with its training data: no speech synthesis, no biometric identification.

Downloads last month
24
Safetensors
Model size
0.6B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for kasper-cbx/svale-600M-hf

Finetuned
(1)
this model

Space using kasper-cbx/svale-600M-hf 1