NB-ASR Qwen3 Lunde v07 mixed Norwegian — checkpoint 55k
This is a private, intermediate checkpoint of a Norwegian Bokmål Qwen3-ASR-0.6B training run from the NB-ASR project at the National Library of Norway. It was saved at optimizer step 55,000 of a planned 100,000-step run. It is published as a reproducible snapshot and must not be confused with the final checkpoint or a production release.
The model supports two transcription styles:
- No text prompt: reading-optimised Norwegian Bokmål.
<verbatim>: closer reproduction of the spoken form.
<summary> exists in the bundled custom tokenizer but this run did not train
on summary targets. Summary-prompt evaluation is included as a diagnostic, not
evidence of summary capability.
Model lineage
- Architecture:
Qwen/Qwen3-ASR-0.6B - Direct initialization:
NbAiLab/Qwen3-ASR-0.6B-custom-tokenizer - Published checkpoint: 55,000 / 100,000 optimizer steps
- Training precision: bf16
- Hardware: 32 NVIDIA H200 GPUs across 8 Olivia nodes
- Per-GPU batch size: 24; global batch size: 768; no gradient accumulation
- Optimizer: fused AdamW; maximum learning rate
6e-5 - Schedule: 2% warmup followed by cosine decay
- Maximum training audio duration: 30 seconds
- All model parameters were trainable
Training mixed two private NB-ASR datasets:
NbAiLab/lunde-nor-nob-verbatim-v07, prompted with<verbatim>NbAiLab/lunde-nor-nob-reading-optimised-v07, with no style prompt
The compiled training manifests contained 7,740,607 rows: approximately 4.12M verbatim rows and 3.62M reading-optimised rows. Training used direct 16 kHz mono WAV files. The exact training-dataset revisions were not pinned in the run configuration, so the dataset names alone are not a complete immutable data provenance record.
Evaluation at step 55,000
Evaluation used greedy autoregressive generation without labels, decoder
targets, or teacher forcing. The private evaluation dataset was
NbAiLab/nb-asr-evaluering-sprakradet at immutable revision
accd2c5165dfa146cd631e76d1447c80923e5f69: 207 unique audio clips, 414
reference rows and 1,242 scored prompt/reference combinations.
Corpus-level WER by source domain and matched transcription style:
| Domain | Reading-optimised: no prompt | Verbatim: <verbatim> |
|---|---|---|
| Audiobooks | 20.93% | 21.97% |
| NRK | 18.68% | 19.23% |
| NST | 8.94% | 8.45% |
| Stortinget | 16.80% | 17.05% |
The reading-optimised column scores no-prompt generations against
reading-optimised references. The verbatim column scores <verbatim>-prompted
generations against verbatim references. These are corpus-level WERs within
each domain, not averages of per-utterance WER. Fleurs and nb_tale are part
of the full evaluation but intentionally omitted from this summary table.
Inference
Use the processor, tokenizer and chat_template.jinja bundled with this
repository. Audio should be mono 16 kHz floating-point samples. The following
low-level Transformers path matches the prompt contract used in evaluation and
avoids wrapper-side language validation for project-added Norwegian:
import numpy as np
import soundfile as sf
import torch
from qwen_asr import Qwen3ASRModel
from qwen_asr.core.transformers_backend import Qwen3ASRProcessor
model_id = "NbAiLab/nb-asr-qwen3-lunde07-mixed-nob-55k"
wrapper = Qwen3ASRModel.from_pretrained(
model_id,
dtype=torch.bfloat16,
device_map="cuda",
)
model = wrapper.model.eval()
processor = Qwen3ASRProcessor.from_pretrained(
model_id,
fix_mistral_regex=True,
)
audio, sample_rate = sf.read("audio.wav", dtype="float32")
if audio.ndim == 2:
audio = audio.mean(axis=1)
if sample_rate != 16000:
raise ValueError("Resample audio to 16 kHz before inference")
audio = np.asarray(audio, dtype=np.float32)
# Use "" for reading-optimised output or "<verbatim>" for verbatim output.
style_prompt = "<verbatim>"
user_content = [{"type": "audio", "audio": None}]
if style_prompt:
user_content.append({"type": "text", "text": "\n" + style_prompt})
messages = [
{"role": "system", "content": ""},
{"role": "user", "content": user_content},
]
prompt = processor.apply_chat_template(
[messages], add_generation_prompt=True, tokenize=False
)[0]
prompt += "language Norwegian<asr_text>"
inputs = processor(
text=[prompt],
audio=[audio],
return_tensors="pt",
padding=True,
truncation=False,
)
prompt_length = inputs["input_ids"].shape[1]
generation_inputs = {
"input_ids": inputs["input_ids"].to(model.device),
"attention_mask": inputs["attention_mask"].to(model.device),
"max_new_tokens": 256,
"do_sample": False,
"pad_token_id": processor.tokenizer.pad_token_id,
"use_cache": True,
}
if "input_features" in inputs:
generation_inputs["input_features"] = inputs["input_features"].to(
model.device, dtype=next(model.parameters()).dtype
)
if "feature_attention_mask" in inputs:
generation_inputs["feature_attention_mask"] = inputs[
"feature_attention_mask"
].to(model.device)
with torch.inference_mode():
generated = model.generate(**generation_inputs)
sequences = generated.sequences if hasattr(generated, "sequences") else generated
text = processor.tokenizer.decode(
sequences[0, prompt_length:].cpu(),
skip_special_tokens=True,
clean_up_tokenization_spaces=False,
)
print(text.strip())
The raw assistant prefix used in training is
language Norwegian<asr_text>. Do not replace it with
language Norwegian Bokmål<asr_text> or place style prompts in the system
message. The tokenizer uses left padding, EOS token 151645 and pad token
151643.
Custom tokenizer
The direct parent remaps reserved Qwen tokenizer slots without changing model dimensions. Relevant project tokens include:
| Token | ID | Intended use |
|---|---|---|
<verbatim> | 151677 | Verbatim transcription prompt |
<reading_optimised> | 151678 | Reserved explicit reading-optimised prompt; this run used no prompt instead |
<summary> | 151679 | Summary prompt; not trained by this run |
<inaudible> | 151680 | Inaudible speech event |
<laughter> | 151681 | Laughter event |
<applause> | 151682 | Applause event |
<cough> | 151683 | Cough event |
Limitations
- This is an intermediate 55k checkpoint from an unfinished 100k-step run.
- The model is intended for Norwegian Bokmål and has not been established as a multilingual checkpoint.
- Training data is private and may overlap public or internal evaluation sources at the recording level; no comprehensive contamination claim is made.
- Evaluation covers short-form ASR and does not establish long-form, streaming, robustness, fairness, or production-readiness properties.
- The run used an internal custom prompt and tokenizer contract. Generic ASR pipelines that ignore the bundled template or style prompt may produce different results.
- Generated transcripts may contain omissions, substitutions, hallucinations, offensive text, names or numbers rendered incorrectly, and other errors. Human review is required for consequential use.
Published files
This repository contains inference weights and exact configuration/processor
files only. Optimizer, scheduler, RNG, Trainer state, training arguments,
audio, transcripts, manifests, logs and credentials are intentionally excluded.
A compact release_provenance.json records file hashes and source-run details.