juanquivilla commited on
Commit
27c16fc
·
verified ·
1 Parent(s): 1462723

v15: ROUGE-L 0.960, 70% exact match — LR 2.5e-5 breakthrough

Browse files
Files changed (4) hide show
  1. README.md +126 -84
  2. generation_config.json +9 -0
  3. model.safetensors +1 -1
  4. training_args.bin +3 -0
README.md CHANGED
@@ -1,93 +1,89 @@
1
  ---
2
- license: other
3
- license_name: lfm1.0
4
- license_link: https://www.liquid.ai/license
5
  base_model: LiquidAI/LFM2.5-350M-Base
6
- datasets:
7
- - juanquivilla/sotto-transcript-cleanup
8
  tags:
9
- - speech-to-text
10
- - transcript-cleanup
11
- - disfluency-correction
12
- - sotto-asr
13
- - lfm2
14
- - liquid-ai
15
- - text2text-generation
16
- library_name: transformers
17
  pipeline_tag: text-generation
18
- language:
19
- - en
20
  ---
21
 
22
  # SottoASR Transcript Cleanup — LFM2.5-350M (bf16)
23
 
24
- <p align="center">
25
- <a href="https://sotto.app">sotto.app</a> ·
26
- <a href="https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-5bit">MLX 5-bit (recommended for deployment)</a> ·
27
- <a href="https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-4bit">MLX 4-bit</a> ·
28
- <a href="https://huggingface.co/datasets/juanquivilla/sotto-transcript-cleanup">Training Dataset</a>
29
- </p>
30
 
31
- ## Overview
32
 
33
- This is the **full-precision (bf16) fine-tuned** [LiquidAI/LFM2.5-350M-Base](https://huggingface.co/LiquidAI/LFM2.5-350M-Base) model for cleaning speech-to-text transcripts. It is the fine-tuned SLM (Small Language Model) powering on-device transcript cleanup in [**SottoASR**](https://sotto.app) — a local, privacy-first speech-to-text application for macOS.
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- **For on-device deployment, use the [MLX 5-bit quantized version](https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-5bit) (233MB, <0.5% quality loss).**
36
 
37
- ## What It Does
38
 
39
- Takes raw, unpunctuated ASR output and produces clean, properly formatted text:
 
 
 
 
 
40
 
41
- | Input (raw ASR) | Output (cleaned) |
42
- |---|---|
43
- | `uh the server is uh running low on memory` | The server is running low on memory. |
44
- | `use redis wait no memcached is better` | Use Memcached. |
45
- | `so basically the the api is um throttling our requests` | The API is throttling our requests. |
46
- | `lets go ahead and really focus on the performance issue` | Let's go ahead and really focus on the performance issue. |
47
- | `send the email to john period` | Send the email to John. |
48
- | `me and the team is working on fixing it` | The team and I are working on fixing it. |
49
 
50
- Handles: filler removal, crutch word removal, self-corrections, false starts, grammar fixes, misheard word correction, dictation commands (period→., comma→,, slash→/), list formatting, and wording preservation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- ## Performance
53
 
54
- | Metric | This Model (350M) | Prompted Qwen3.5-2B | Improvement |
55
- |--------|-------------------|---------------------|-------------|
56
- | **ROUGE-L** | **0.931** | 0.891 | **+4.5%** |
57
- | **Exact Match** | **56%** | 37% | **+51% relative** |
58
- | **Self-Correction** | **0.869** | 0.742 | **+17.1%** |
59
- | **Zero-Filler Rate** | **90%** | 82% | **+9.8% relative** |
60
- | **Inference** | **0.12s** | 1.0s | **8.3x faster** |
61
- | **Model Size** | **354M params** | 2B params | **5.7x smaller** |
62
 
63
- ### Per-Category Scores
 
 
64
 
65
- | Category | ROUGE-L | Description |
66
- |----------|---------|-------------|
67
- | preserve_wording | 0.987 | Clean input passes through unchanged |
68
- | list_formatting | 0.972 | Spoken lists → numbered format |
69
- | filler_removal | 0.955 | uh, um, uhm, er, ah |
70
- | short | 0.940 | Brief utterances (2-10 words) |
71
- | false_start | 0.926 | Stutters and restarts |
72
- | dictation_commands | 0.971 | period→., comma→,, slash→/ |
73
- | mixed | 0.928 | Multiple overlapping disfluencies |
74
- | long_dictation | 0.918 | 100+ word passages |
75
- | misheard_words | 0.913 | ASR errors (post gress→Postgres) |
76
- | grammar | 0.906 | gonna→going to, me and him→he and I |
77
- | crutch_words | 0.892 | basically, you know, I mean |
78
- | self_correction | 0.869 | Speaker changes mind mid-sentence |
79
-
80
- ## Training
81
-
82
- - **Base model:** [LiquidAI/LFM2.5-350M-Base](https://huggingface.co/LiquidAI/LFM2.5-350M-Base) (hybrid convolution + attention, 32K context)
83
- - **Dataset:** [juanquivilla/sotto-transcript-cleanup](https://huggingface.co/datasets/juanquivilla/sotto-transcript-cleanup) — 124K synthetic pairs
84
- - **Method:** Two-stage full fine-tuning
85
- 1. **Stage 1:** Full FT on 124K dataset (LR 1e-5, 3 epochs, ~22 min on RTX 4090)
86
- 2. **Stage 2:** Concentrated hard-pattern FT on 14K examples (LR 2e-6, 1 epoch, 27 seconds)
87
- - **Data sources:** Qwen3.5-35B (95K), Grok 4.20 (29K), hand-crafted (235)
88
- - **Key finding:** Full fine-tune dramatically outperforms LoRA for small models (+7% ROUGE-L on same data)
89
 
90
- ## Usage
91
 
92
  ```python
93
  from transformers import AutoModelForCausalLM, AutoTokenizer
@@ -95,31 +91,77 @@ import torch
95
 
96
  model = AutoModelForCausalLM.from_pretrained(
97
  "juanquivilla/sotto-cleanup-lfm25-350m",
98
- dtype=torch.bfloat16, device_map="auto", trust_remote_code=True,
 
99
  )
100
  tokenizer = AutoTokenizer.from_pretrained("juanquivilla/sotto-cleanup-lfm25-350m")
101
 
102
- raw = "uh the server is uh running low on memory"
103
- prompt = f"### Input:\n{raw}\n\n### Output:\n"
 
104
  inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
105
  with torch.no_grad():
106
  out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
107
- print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
108
- #"The server is running low on memory."
 
109
  ```
110
 
111
- ## Quantized Variants
112
 
113
- | Variant | Size | ROUGE-L | Filler-Free | Link |
114
- |---------|------|---------|-------------|------|
115
- | **bf16 (this model)** | 676MB | 0.931 | 90% | — |
116
- | **MLX 5-bit (recommended)** | 233MB | 0.926 | 99% | [sotto-cleanup-lfm25-350m-mlx-5bit](https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-5bit) |
117
- | MLX 4-bit | 190MB | 0.897 | 99% | [sotto-cleanup-lfm25-350m-mlx-4bit](https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-4bit) |
118
 
119
- ## Part of SottoASR
 
 
 
120
 
121
- [**SottoASR**](https://sotto.app) is a local, privacy-first speech-to-text application for macOS. Press a hotkey, speak, and clean text appears at your cursor. All processing happens on-device — no audio or text is ever sent to a cloud service. This model powers the transcript cleanup step.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  ## License
124
 
125
- This model inherits the [LFM 1.0 license](https://www.liquid.ai/license) from the base model.
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ license: mit
3
+ language:
4
+ - en
5
  base_model: LiquidAI/LFM2.5-350M-Base
 
 
6
  tags:
7
+ - speech-to-text
8
+ - transcript-cleanup
9
+ - text-correction
10
+ - asr-post-processing
11
+ - LFM
12
+ - LiquidAI
 
 
13
  pipeline_tag: text-generation
14
+ datasets:
15
+ - juanquivilla/sotto-transcript-cleanup
16
  ---
17
 
18
  # SottoASR Transcript Cleanup — LFM2.5-350M (bf16)
19
 
20
+ Fine-tuned [LiquidAI/LFM2.5-350M-Base](https://huggingface.co/LiquidAI/LFM2.5-350M-Base) for on-device speech-to-text transcript cleanup. Removes filler words, corrects grammar, formats punctuation, and handles false starts and self-corrections — all locally, with zero cloud dependency.
 
 
 
 
 
21
 
22
+ ## Model Details
23
 
24
+ | Property | Value |
25
+ |----------|-------|
26
+ | **Base model** | LiquidAI/LFM2.5-350M-Base |
27
+ | **Parameters** | 354M (all trainable, no LoRA) |
28
+ | **Precision** | bf16 |
29
+ | **Size on disk** | ~676 MB |
30
+ | **Architecture** | Hybrid: 10 conv layers + 6 GQA attention layers |
31
+ | **Context window** | 32,768 tokens (trained with 4,096 packed) |
32
+ | **Training method** | Full fine-tune (SFT) with TRL |
33
+ | **Training data** | 143K samples (131K base + 12K targeted patterns) |
34
+ | **Learning rate** | 2.5e-5 (cosine schedule) |
35
+ | **Epochs** | 3 |
36
+ | **Hardware** | 1x RTX 4090 (24GB), ~25 min training |
37
 
38
+ ## Benchmark Results
39
 
40
+ Evaluated on 135-sample SottoASR benchmark (diverse transcript cleanup scenarios):
41
 
42
+ | Metric | Score |
43
+ |--------|-------|
44
+ | **ROUGE-L** | **0.960** |
45
+ | **Exact Match** | **69.6%** |
46
+ | **Zero-Filler Rate** | **88.1%** |
47
+ | **Avg Latency** | 0.116s (RTX 4090) |
48
 
49
+ ### Per-Category Breakdown
 
 
 
 
 
 
 
50
 
51
+ | Category | ROUGE-L | Exact Match |
52
+ |----------|---------|-------------|
53
+ | crutch_words | 0.916 | 60% |
54
+ | dictation_commands | 0.989 | 80% |
55
+ | false_start | 0.957 | 80% |
56
+ | filler_removal | 0.951 | 73% |
57
+ | grammar | 0.973 | 80% |
58
+ | list_formatting | 0.990 | 80% |
59
+ | long_dictation | 0.934 | 13% |
60
+ | misheard_words | 0.938 | 70% |
61
+ | mixed | 0.946 | 60% |
62
+ | preserve_wording | 0.995 | 75% |
63
+ | self_correction | 0.974 | 80% |
64
+ | short | 0.947 | 70% |
65
+
66
+ ### Comparison with Prompted 2B Model
67
+
68
+ | Metric | This model (350M) | Prompted Qwen 2B | Delta |
69
+ |--------|-------------------|-------------------|-------|
70
+ | ROUGE-L | **0.960** | 0.891 | **+0.069** |
71
+ | Exact Match | **70%** | 37% | **+33pts** |
72
+ | Inference speed | **0.12s** | 1.0s | **8x faster** |
73
 
74
+ ## Usage
75
 
76
+ ### Prompt Format
 
 
 
 
 
 
 
77
 
78
+ ```
79
+ ### Input:
80
+ {raw transcript, lowercase, no punctuation}
81
 
82
+ ### Output:
83
+ {model generates cleaned text}
84
+ ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ ### Example
87
 
88
  ```python
89
  from transformers import AutoModelForCausalLM, AutoTokenizer
 
91
 
92
  model = AutoModelForCausalLM.from_pretrained(
93
  "juanquivilla/sotto-cleanup-lfm25-350m",
94
+ dtype=torch.bfloat16,
95
+ trust_remote_code=True,
96
  )
97
  tokenizer = AutoTokenizer.from_pretrained("juanquivilla/sotto-cleanup-lfm25-350m")
98
 
99
+ text = "so uh basically the thing is we need to uh fix the deployment pipeline"
100
+ prompt = f"### Input:\n{text}\n\n### Output:\n"
101
+
102
  inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
103
  with torch.no_grad():
104
  out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
105
+ output = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
106
+ print(output.split("###")[0].strip())
107
+ # → "We need to fix the deployment pipeline."
108
  ```
109
 
110
+ ## MLX Quantized Versions
111
 
112
+ For on-device deployment on Apple Silicon:
 
 
 
 
113
 
114
+ | Variant | Size | ROUGE-L | Repo |
115
+ |---------|------|---------|------|
116
+ | **5-bit (recommended)** | ~233 MB | ~0.955 | [sotto-cleanup-lfm25-350m-mlx-5bit](https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-5bit) |
117
+ | 4-bit | ~190 MB | ~0.945 | [sotto-cleanup-lfm25-350m-mlx-4bit](https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m-mlx-4bit) |
118
 
119
+ ## Training Data
120
+
121
+ Trained on [juanquivilla/sotto-transcript-cleanup](https://huggingface.co/datasets/juanquivilla/sotto-transcript-cleanup) — 143K input/output pairs covering:
122
+
123
+ - Filler word removal (uh, um, like, you know)
124
+ - Crutch phrase stripping (okay so basically, the thing is)
125
+ - Self-correction resolution (X, no wait, Y → Y)
126
+ - False start cleanup
127
+ - Grammar and punctuation correction
128
+ - Dictation command interpretation
129
+ - Short input handling (heavy filler, minimal content)
130
+ - Long-form transcript cleanup (500+ words)
131
+
132
+ ## Training Progression
133
+
134
+ This model is the result of 15+ iterative experiments:
135
+
136
+ | Version | ROUGE-L | Key Innovation |
137
+ |---------|---------|----------------|
138
+ | v1: LoRA SFT 15K | 0.771 | Baseline |
139
+ | v3: LoRA SFT 100K | 0.863 | Scale breakthrough |
140
+ | v4: + GRPO | 0.891 | Matched prompted 2B |
141
+ | v5: Full FT | 0.907 | LoRA was bottleneck |
142
+ | v7: Higher LR (2e-5) | 0.943 | LR breakthrough #2 |
143
+ | v11: + Targeted data | 0.950 | Pattern-specific fix |
144
+ | **v15: LR 2.5e-5** | **0.960** | **LR breakthrough #3** |
145
+
146
+ ## Limitations
147
+
148
+ - Optimized for English transcripts
149
+ - Best on conversational/meeting-style speech
150
+ - May not handle domain-specific jargon (medical, legal) without additional fine-tuning
151
+ - Long dictation (>500 words) has lowest exact match rate
152
 
153
  ## License
154
 
155
+ MIT same as the base model.
156
+
157
+ ## Citation
158
+
159
+ ```bibtex
160
+ @misc{sotto-cleanup-2026,
161
+ title={SottoASR Transcript Cleanup Model},
162
+ author={Juan Villa},
163
+ year={2026},
164
+ publisher={Hugging Face},
165
+ url={https://huggingface.co/juanquivilla/sotto-cleanup-lfm25-350m}
166
+ }
167
+ ```
generation_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 1,
4
+ "eos_token_id": [
5
+ 7
6
+ ],
7
+ "pad_token_id": 0,
8
+ "transformers_version": "5.3.0"
9
+ }
model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:971073c33ec2b3309d6e05904678a0037c1e985a39fd27d03c1fbf9e157955cc
3
  size 708984464
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5969b0e3bd9e0c7b0eb793b3406d42734e8e4c481ee80511e089619e1520d3c7
3
  size 708984464
training_args.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:052ea156c1f5f51905925b2767166bc4c4e3956dc39679724ae0bc32d732a0c3
3
+ size 5713