Token Classification
Transformers
ONNX
Safetensors
Danish
electra
punctuation-restoration
sentence-boundary-detection
truecasing
danish
Instructions to use RyeAI/ekko-pnc with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use RyeAI/ekko-pnc with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="RyeAI/ekko-pnc")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("RyeAI/ekko-pnc") model = AutoModelForTokenClassification.from_pretrained("RyeAI/ekko-pnc", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 8,028 Bytes
c50b158 d4cb542 5b90a6e c50b158 13f8026 539e399 c50b158 313b5f6 13f8026 c50b158 7e2069c c50b158 13f8026 c50b158 2c3f393 eb65df1 2c3f393 eb65df1 2c3f393 fde1765 2c3f393 eb65df1 fde1765 c50b158 28bdfae 0ad1fd7 de465d1 0ad1fd7 de465d1 0ad1fd7 df2c1c4 de465d1 28bdfae 1d05f98 d1372b2 fde1765 c50b158 7e2069c c50b158 fde1765 c50b158 fde1765 7e2069c fde1765 9b0372a fde1765 7e2069c fde1765 80d2347 bbdb023 7e2069c fde1765 c50b158 5bb210a a6fa157 70becac 5bb210a fde1765 c50b158 fde1765 c50b158 fde1765 c50b158 fde1765 c50b158 fde1765 5b90a6e 7e2069c fde1765 80d2347 fde1765 d4cb542 5b90a6e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | ---
language:
- da
license: cc-by-4.0
license_link: https://huggingface.co/RyeAI/ekko-pnc/blob/main/MODEL_LICENSE.md
thumbnail: https://huggingface.co/RyeAI/ekko-pnc/resolve/d3129544e4e0155093e192cac58a9e69a4dea1ff/cover.png
library_name: transformers
pipeline_tag: token-classification
base_model: jonfd/electra-small-nordic
base_model_relation: finetune
datasets:
- alexandrainst/nst-da
- alexandrainst/ftspeech
- CoRal-project/coral-v3
- wikimedia/wikipedia
tags:
- punctuation-restoration
- sentence-boundary-detection
- truecasing
- danish
- onnx
metrics:
- f1
---

# Ekko PnC v2 — Danish punctuation restoration and truecasing
Ekko PnC v2 restores punctuation and capitalization in Danish speech transcripts.
It adds commas, full stops, question marks, and exclamation marks while
keeping the original words in the same order. This makes it suitable for
transcripts with word timestamps. The int8 ONNX model runs locally. Ekko v1 Tiny
is a separate speech recognizer; PnC v2 can process its output.
> **Input:** hej mit navn er emil hvordan går det i dag jeg har været på kontoret og bagefter skal jeg hjem
>
> **Output:** Hej, mit navn er Emil. Hvordan går det i dag? Jeg har været på kontoret, og bagefter skal jeg hjem.
## Quickstart
With [uv](https://docs.astral.sh/uv/) and Python 3.11+:
```bash
uvx --from 'ekko-stt[pnc]' ekko pnc "hej mit navn er emil hvordan går det i dag"
```
Expected output: `Hej, mit navn er Emil. Hvordan går det i dag?`
The command installs the [Ekko STT package](https://pypi.org/project/ekko-stt/).
The first PnC run downloads the pinned int8 model from Hugging Face; later runs use the cache.
For longer transcripts, the runtime uses 128-token windows with 12 words of overlap
and keeps every input word in order. Pass continuous text when possible so PnC can
use context across speech recognition chunks.
### Python with ONNX
Install `ekko-stt[pnc]` in your Python environment. This loads the int8 ONNX
export and applies the same windowing as the command above:
```python
from ekko import Punctuator
from ekko.artifacts import ensure_pnc_directory
pnc = Punctuator(ensure_pnc_directory())
print(pnc("hej mit navn er emil hvordan går det i dag"))
```
<details>
<summary>Python with Transformers (PyTorch)</summary>
Install `transformers` and `torch` to use the PyTorch weights directly. This
short-input example maps predictions from the first token of each word back to
the original words:
```python
import torch
from transformers import AutoModelForTokenClassification, AutoTokenizer
repo = "RyeAI/ekko-pnc"
tokenizer = AutoTokenizer.from_pretrained(repo)
model = AutoModelForTokenClassification.from_pretrained(repo).eval()
words = "hej mit navn er emil hvordan går det i dag".split()
inputs = tokenizer(words, is_split_into_words=True, return_tensors="pt")
with torch.inference_mode():
predictions = model(**inputs).logits.argmax(dim=-1)[0].tolist()
output, seen, capitalize_next = [], set(), True
for word_id, prediction in zip(inputs.word_ids(), predictions):
if word_id is None or word_id in seen:
continue
seen.add(word_id)
label = model.config.id2label[prediction]
mark = label.removesuffix("|U")
word = words[word_id]
if capitalize_next or label.endswith("|U"):
word = word[:1].upper() + word[1:]
output.append(word + ("" if mark == "O" else mark))
capitalize_next = mark in {".", "?", "!"}
print(" ".join(output))
```
</details>
For longer transcripts, use the ONNX example above; it preserves all words
across overlapping windows.
Version 2 is tuned for **continuous speech** such as dictation and meetings.
The earlier version often missed sentence endings when given a long transcript;
it had mainly learned from short, isolated utterances. Version 2 was trained
on longer stretches of conversation and is substantially better at finding
sentence boundaries in that setting.
## Results
The main evaluation used 88 clips from 8 meetings (4,322 words), with
punctuation reviewed by a person. Both versions received the *same correct
words*, so these scores measure punctuation and casing rather than speech
recognition. The numbers below use the FP32 ONNX model.
F1 combines precision and recall; higher is better.
| Model | Sentence boundary F1 | Boundary recall | Boundary precision | Full stop F1 | Question F1 | Casing F1 |
|---|---:|---:|---:|---:|---:|---:|
| v1 | 0.402 | 0.264 | 0.849 | 0.350 | 0.333 | 0.523 |
| **v2** | **0.698** | **0.619** | 0.801 | **0.654** | **0.462** | **0.736** |
The v2 casing score includes a simple rule that capitalizes the word after a
predicted sentence ending. Without that rule, its casing F1 was 0.653.
On 55 held-out CoRal conversations, v2 improved sentence boundary F1 from
0.095 to 0.474. The punctuation reference for this set was machine-generated
and checked to preserve every word, so this result measures agreement with
that reference. This earlier comparison used the settings
`max_length=256, overlap_words=24`. In a later runtime comparison on the same
conversations, the same v2 int8 weights scored 0.471 with 256/24 and **0.599
with the recommended 128/12**.
The set has been used in earlier experiments, so this is guidance for runtime
configuration rather than an independent evaluation of new model weights.
Very short clips are a trade-off: on isolated 3–4 second utterances, boundary
F1 decreased from 0.865 (v1) to 0.818 (v2). Version 2 may add extra full stops
on such inputs.
## Speed
**1,000 Danish words in about 0.09 seconds** on an Apple M1 Pro CPU, after model
loading. The int8 ONNX benchmark includes tokenization, inference, and rendering;
it excludes download, speech recognition, and browser WASM. Speed varies by device
and input length.
## Files and use
| Format | Files | When to use |
|---|---|---|
| int8 ONNX | `pnc.int8.onnx`, `tokenizer.json`, `config.json` | Local and browser inference; about 22.4 MB for the model file |
| FP32 ONNX | `pnc.onnx`, `pnc.onnx.data`, `tokenizer.json`, `config.json` | Best measured quality when memory is less constrained |
On the meeting evaluation, int8 was behind FP32 by 0.006 boundary F1,
0.022 question F1, and 0.016 casing F1.
<details>
<summary>Exact revision and checksums</summary>
The int8 runtime pins revision
`d3129544e4e0155093e192cac58a9e69a4dea1ff`. SHA-256 checksums:
```text
pnc.int8.onnx 46bb979d409731872f10b6cb71298594595f16368d2aec2fcaf94d87b4266527
tokenizer.json a1e0d43ee307ad7a1ccc23e6e5398d877a68ca7f576067ac507625a0d50e9502
config.json dc655c46f69eeb15dd43786ad58fb5862cc794b36cc9d2c426ab5bc4b01f23ca
```
The files in this repository also have checksums in `SHA256SUMS`. Version 1
remains available through the `v1` tag.
</details>
## Training and limitations
Ekko PnC builds on
[`jonfd/electra-small-nordic`](https://huggingface.co/jonfd/electra-small-nordic).
Its Danish training text included Wikipedia, NST, CoRal, and FT Speech.
Training combined human-punctuated source text with machine-generated
punctuation labels. The main meeting evaluation above uses human-reviewed
punctuation; the CoRal conversation evaluation uses machine-generated
punctuation references.
- Punctuation comes from text alone; the model cannot hear pauses or intonation.
Question marks remain its weakest common punctuation class.
- It handles capitalization as an initial uppercase letter. It cannot create
mixed-case words such as `iPhone` from lowercase input; existing capitals
are preserved by the Ekko runtimes.
- The human-reviewed meeting set is small: 8 meetings and 324 reference full
stops. Results on other speaking styles may differ.
## License and data terms
Ekko PnC v2 is released under CC BY 4.0. See
[`MODEL_LICENSE.md`](MODEL_LICENSE.md), [`NOTICE`](NOTICE), and
[`LICENSES/`](LICENSES/) for the terms and source notices. CoRal's use
restrictions also apply to this trained model. The FT Speech and NST notices
are included in the license bundle.
|