NB-ASR Qwen3 Lunde v07 mixed Norwegian — 100k longfix
This is a Norwegian Bokmål Qwen3-ASR-0.6B checkpoint from the NB-ASR project at the National Library of Norway. It starts from optimizer step 100,000 of the completed 100,000-step run and adds a selected 1,200-step long-context recovery phase. It is an experimental evaluation checkpoint, not 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 - Parent checkpoint: 100,000 / 100,000 optimizer steps
- Published recovery checkpoint: 1,200 / 2,000 recovery 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
Long-context recovery
The original 100k checkpoint worked well on short clips and streaming input, but could omit large portions of a long, single-pass transcript. We repaired this with a 2,000-step checkpoint sweep and selected step 1,200 using protected short-form and streaming quality gates plus long-form evaluation.
Recovery training used one NVIDIA H200 in bf16 with gradient checkpointing,
7e-6 peak learning rate and a 50/50 optimizer-step mixture of:
- ordinary short Norwegian ASR examples, replayed to preserve existing quality;
- synthetic long-form examples, balanced across approximately 300, 600, 1,200 and 1,700 seconds, with long examples trained at batch size 1.
All checkpoints from steps 200 through 2,000 were evaluated. Step 1,200 was selected because it passed every protected regression gate, had no failed extended or 1,800-second evaluation samples, and gave the best aggregate result over the 600, 1,200 and 1,700-second tests.
| Evaluation | WER at selected step 1,200 |
|---|---|
| Natural recordings near 300 seconds | 3.79% |
| Same natural cohort split into 30-second clips | 5.32% |
| Short-form protected evaluation | 27.66% |
| Streaming/self-prefix evaluation | 7.63% |
| Synthetic ~300 seconds | 16.32% |
| Synthetic ~600 seconds | 13.23% |
| Synthetic ~1,200 seconds | 19.78% |
| Synthetic ~1,700 seconds | 31.01% |
| Synthetic ~1,800 seconds | 48.07% |
The intended support target is recordings up to approximately five minutes. The much longer evaluations show improvement but remain experimental and degrade substantially at 1,700–1,800 seconds.
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 100,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.74% | 21.23% |
| NRK | 18.24% | 20.61% |
| NST | 8.94% | 8.94% |
| Stortinget | 17.36% | 17.20% |
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-100k-longfix"
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 the final checkpoint of this 100k-step run; production readiness has not been established.
- 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.
- Long-form support was evaluated most thoroughly around the five-minute target; substantially longer single-pass recordings remain unstable.
- The evaluation does not establish robustness, fairness or production readiness.
- 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.