metadata
license: mit
language:
- en
base_model: LiquidAI/LFM2.5-350M-Base
tags:
- speech-to-text
- transcript-cleanup
- text-correction
- asr-post-processing
- LFM
- LiquidAI
- grpo
- full-fine-tune
- inverse-text-normalization
pipeline_tag: text-generation
datasets:
- juanquivilla/sotto-transcript-cleanup
SottoASR Transcript Cleanup — LFM2.5-350M (Full Precision, soup_30)
sottoasr.app · MLX 5-bit (recommended) · MLX 4-bit (smaller) · Training Dataset
Overview
Full-precision bf16 fine-tune of LiquidAI/LFM2.5-350M-Base for on-device speech-to-text transcript cleanup. This is the training artifact — for on-device deployment on Apple Silicon, use the 5-bit MLX variant.
What's new in soup_30
soup_30 extends v45 with targeted training data covering five failure modes observed on real production transcripts:
- Multi-number sentences ("the score was 21 to 14")
- Year-with-context drift ("twenty eighteen and twenty twenty seven" → preserve both years)
- Disconnected number lists (no phone-number hallucination on "one two three eleven forty six")
- Within-input duplicates (same number stated twice — preserve consistency)
- Long-form preservation (multi-paragraph dictations with embedded numbers)
The v37+v47 training data (~30K examples) was re-audited end-to-end with a Qwen3.6-27B judge and bad rows were dropped. A new 66-case adversarial benchmark drives objective evaluation.
Headline numbers
| Capability | v36 | v45 | soup_30 (this model) |
|---|---|---|---|
| Number accuracy (171-sample stratified val) | 12.9% | 95.9% | 96.5% |
| 66-case adversarial benchmark (greedy) | n/a | 76% | 86% |
| 66-case adversarial benchmark (temp 0.7 × 4) | n/a | 77% | 85% |
| Loops on 264 sampling-mode probes | n/a | 0 | 2 |
| Filler-free on 241 long inputs | 67.2% | 68.0% | 73.0% |
| Sub-deletion >15% on 241 long inputs | 13.3% | 13.7% | 4.6% |
Composite score (0.35×num + 0.30×bench_greedy + 0.15×bench_sample + 0.10×filler_long + 0.05×(1-sub15) + 0.05×(1-loops/N)): 89.45
Training pipeline
LiquidAI/LFM2.5-350M-Base
→ SFT v23 → GRPO v23 (paragraph emission)
→ GRPO v36: full FT with substantive-deletion-aware reward
→ SFT v39: + 12.7K augmented number examples (ITN)
→ GRPO v40-v45: chained refinement, fixed reward + amplified filler penalty
→ SFT/GRPO soup_30: + 5.5K gap-filling examples (multi-number, year-context,
disconnected, duplicates, long-form), Qwen-audited and cleaned
Usage
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model = AutoModelForCausalLM.from_pretrained(
"juanquivilla/sotto-cleanup-lfm25-350m",
dtype=torch.bfloat16, trust_remote_code=True,
)
tokenizer = AutoTokenizer.from_pretrained("juanquivilla/sotto-cleanup-lfm25-350m")
text = "talk about server three sixty"
prompt = f"### Input:\n{text}\n\n### Output:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
out = model.generate(**inputs, max_new_tokens=512, do_sample=False)
output = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
if "###" in output:
output = output[:output.index("###")]
print(output.strip())
All Variants
| Variant | Size | Use Case |
|---|---|---|
| Full precision (this) | 676 MB | Training, GPU inference |
| MLX 5-bit | ~237 MB | Recommended for Apple Silicon |
| MLX 4-bit | ~195 MB | Smallest |
License
MIT