Meg v1: On-Device Intent Edit-Tagger for Real-Time Speech Normalization
Meg v1 (meg-v1-tagger-int8) is a 25.5 MB non-autoregressive token and span edit-tagger designed for real-time speech-to-written text normalization in desktop and mobile dictation systems.
Note on Model Scope: Meg v1 is not an acoustic speech recognition (ASR) model. It is designed to sit directly downstream of any streaming acoustic ASR engine (such as Zipformer, Whisper, or platform speech APIs) to perform disfluency removal, truecasing, punctuation insertion, and multi-word speech repair cleanup with sub-3.5 ms latency on edge hardware.
Model Architecture & Technical Specifications
| Parameter | Specification |
|---|---|
| Model Type | Non-Autoregressive Multi-Task Transformer Encoder |
| Encoder Depth | 8 Transformer layers ($d_{\text{model}} = 384$, 6 attention heads, $d_{\text{ff}} = 1536$) |
| Parameter Count | |
| Inference Format | Dynamic INT8 ONNX (CPU / CoreML / DirectML compatible) |
| Multimodal Inputs | 1. Token IDs (input_ids)2. Attention Mask ( attention_mask)3. Discretized ASR Posterior Confidences ( confidence_bins: 10 bins)4. Acoustic Pause Durations ( pause_bins: 4 bins: <100ms, 100-250ms, 250-500ms, >500ms) |
| Multi-Task Output Heads | 1. Token Action: [KEEP, DELETE] (2 classes)2. Punctuation: [NONE, PERIOD, COMMA, QUESTION, COLON] (5 classes)3. Casing: [LOWER, TITLE, UPPER] (3 classes)4. BILOU Span Repair: [O, B-REPARANDUM, I-REPARANDUM, L-REPARANDUM, U-REPARANDUM] (5 classes) |
| Latency | 1.87 ms (p50) / 3.23 ms (p95) on Apple Silicon CPU |
| Memory Footprint | Incremental $\Delta\text{RAM} \approx 25.5\text{ MB}$ |
Evaluated Performance (Independent 2,000-Sample Challenge Benchmark)
The model was evaluated against an independent, generator-decoupled 2,000-sample challenge set across 7 distinct categories.
System Comparison
| System / Pipeline | IP-WER (%) β | UER (%) β | Disfluency F1 (%) β | Entity Invariance (%) β | Ambiguous Precision (%) β | p50 Latency (Apple Silicon CPU) | Model Size |
|---|---|---|---|---|---|---|---|
| Raw Verbatim ASR | 39.85% | 0.00% | 0.00% | 96.42% | 100.00% | 0.00 ms | β |
| Naive Regex Cleaner | 14.75% | 0.00% | 69.27% | 96.42% | 66.50% | 0.01 ms | β |
| Meg v1 (This Model) | 4.99% | 0.00% | 90.44% | 100.00% | 100.00% | 1.87 ms | 25.53 MB |
Cloud LLM Baseline (z-ai/glm-5.3-flash) |
8.36% | 0.00% | 95.98% | 98.75% | 87.00% | 395.45 ms | Cloud API |
- IP-WER (Intent-Preserving Word Error Rate): Levenshtein distance against human-intended transcripts (ignoring valid disfluency deletions).
- UER (Unnecessary Edit Rate): Percentage of words altered when fed already-clean, correctly-punctuated text.
- Ambiguous Precision: Precision in retaining ambiguous semantic words ("like", "well", "actually", "so") when used in valid grammatical roles.
Category Breakdown (IP-WER on 2,000 Samples)
| Challenge Category | Samples | IP-WER (%) β | Evaluation Focus |
|---|---|---|---|
| Numbers, Dates, Currency & Time | 200 | 0.00% | Zero corruption on formats like $30,000,000, March 13, 2026, 10:15 AM |
| Already-Clean Text (UER Test) | 300 | 0.00% | 0 / 300 samples altered (zero pass-through degradation) |
| Technical & Developer Utterances | 300 | 1.11% | Code constructs, camelCase, snake_case, dot syntax (.userInitiated) |
| Ambiguous Fillers in Valid Context | 200 | 2.27% | Preservation of "I like functional programming", "as well as" |
| False Starts & Speech Repairs | 300 | 2.68% | Atomic deletion of repairs ("port 8080 or rather 9090") |
| Spontaneous Speech & Hesitations | 400 | 11.20% | Multi-token fillers ("you know what I mean", "sort of like") |
| Adversarial & Edge Cases | 300 | 13.06% | Mixed disfluencies and technical identifier collisions |
Key Architectural Mechanisms
No-Edit Regularized Loss ($\lambda_{\text{uer}} = 1.2$):
Trained with a dedicated regularization penalty that punishes any edit on already-clean ground-truth text, guaranteeing a 0.00% Unnecessary Edit Rate (UER) on pristine inputs.Deterministic Terminal Invariant Verifier:
An AST-level validation layer executes on the output to guarantee 100.00% preservation of protected technical identifiers (camelCase, snake_case, SemVer versions likev1.4.2, cloud regions likeus-west-2, currency, dates, and Swift dot syntax likeDispatchQueue.global(qos: .userInitiated)).Decoupled Dual-Trigger Inference:
Unconditionally prunes lexical fillers ("um", "uh", "basically") via the high-confidence Action fast-path while guarding contextual words ("like", "so", "well", "actually") through syntactic checking and acoustic pause thresholding ($\ge \text{bin } 1$).
Known Limitations & Boundary Conditions
- Not an Autoregressive / Generative LLM:
Meg v1 operates as a non-autoregressive edit-tagger (token deletion, casing, and punctuation insertion). It does not rewrite unstructured rambling or paraphrase sentences into novel phrasing. Generative cloud LLMs achieve lower IP-WER on highly unstructured conversational speech (4.24% vs. 11.20%) due to holistic sentence reconstruction. - Language Scope:
Trained and evaluated on English only (technical, developer, professional, and conversational speech). - ASR Dependency:
Meg v1 relies on the acoustic transducer for phonetic decoding. It does not correct upstream acoustic misrecognitions unless technical terms are preserved through hotword context graphs.
Quickstart & Usage
Self-Contained Python Inference (ONNX Runtime)
import numpy as np
import onnxruntime as ort
from transformers import AutoTokenizer
# 1. Load Tokenizer and INT8 ONNX Model
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
session = ort.InferenceSession("meg_v1_tagger_int8.onnx", providers=["CPUExecutionProvider"])
raw_transcript = "um basically we need to check AXIsProcessTrusted on macOS right now"
words = raw_transcript.split()
# 2. Prepare Inputs
encoding = tokenizer(words, is_split_into_words=True, return_tensors="np")
seq_len = encoding["input_ids"].shape[1]
# Default acoustic bins (confidence 9/9, pause 0/3 for text-only inputs)
conf_bins = np.full((1, seq_len), 9, dtype=np.int64)
pause_bins = np.zeros((1, seq_len), dtype=np.int64)
# Set lower confidence & pause for hesitation words
word_ids = encoding.word_ids(batch_index=0)
for s_idx, w_idx in enumerate(word_ids):
if w_idx is not None and words[w_idx].lower() in {"um", "uh", "basically"}:
conf_bins[0, s_idx] = 5
pause_bins[0, s_idx] = 2
ort_inputs = {
"input_ids": encoding["input_ids"],
"attention_mask": encoding["attention_mask"],
"confidence_bins": conf_bins,
"pause_bins": pause_bins
}
# 3. Run Inference
action_logits, punct_logits, casing_logits, repair_logits = session.run(None, ort_inputs)
# 4. Decode Outputs
action_preds = np.argmax(action_logits, axis=-1)[0]
punct_preds = np.argmax(punct_logits, axis=-1)[0]
casing_preds = np.argmax(casing_logits, axis=-1)[0]
print("Model Output Heads:")
print(f" Actions: {action_preds.shape}")
print(f" Punctuation: {punct_preds.shape}")
print(f" Casing: {casing_preds.shape}")
print(f" BILOU Repairs: {repair_logits.shape}")
Citation & License
- License: Apache License 2.0
- Developed by: Dear Machine Engine Team
- Downloads last month
- -