Luigi commited on
Commit
44fa0ce
·
verified ·
1 Parent(s): 806aa61

README: document VoxCPM2-TW teacher, v1 warm-start, ASR gate, GAN recipe + real metrics/data

Browse files
Files changed (1) hide show
  1. README.md +90 -56
README.md CHANGED
@@ -22,19 +22,35 @@ pipeline_tag: text-to-speech
22
 
23
  A **4.63M‑parameter** Mandarin (Taiwan) + English text‑to‑speech model that runs **entirely on CPU**
24
  and emits **8 kHz** audio — sized for **G.711 telephony** and **on‑device (Jetson‑class)** use.
25
- One model, one voice: Chinese, English, and code‑mix through a single frontend (no language routing).
 
26
 
27
- > 🔊 **Live demo:** https://huggingface.co/spaces/Luigi/PrimeTTS-vs-Inflect-Nano-v1 · 🧩 **Base:** [`owensong/Inflect-Nano-v1`](https://huggingface.co/owensong/Inflect-Nano-v1) (fine‑tune, **same frozen architecture**)
28
 
29
  | | |
30
  |---|---|
31
  | **Parameters** | 4.63M (3.47M acoustic + 1.17M vocoder) |
32
  | **Sample rate** | 8 kHz (telephony‑band) |
33
  | **Runtime** | `onnxruntime`, **CPU‑only**, torch‑free at inference |
34
- | **Languages** | zh‑TW (Traditional) + English + code‑mix |
 
35
  | **Architecture** | FastSpeech‑style (no attention) + Snake‑HiFiGAN — **frozen**, no NAS |
36
  | **License** | Apache‑2.0 |
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  ---
39
 
40
  ## Quickstart (inference, CPU)
@@ -70,22 +86,49 @@ torch‑free and runs as‑is on a Jetson Nano CPU. See `scripts/synth_from_text
70
 
71
  ---
72
 
73
- ## Why it works — the two levers
 
 
74
 
75
- Inflect‑Nano‑v1's 4.63M architecture is **not capacity‑limited** for this task (the original English
76
- checkpoint already scores ~0.05 CER). Our first retrains were still unintelligible **not** because of
77
- size, but because of two fixable things. Both fixes keep the **architecture frozen**:
 
 
 
 
 
 
 
 
78
 
79
- | Lever | What went wrong / the fix | Held‑out Mandarin CER |
80
  |---|---|---|
81
- | **1. Phone‑level alignment** | Crude char/letter‑CTC → wrong per‑phone durations → over‑smoothed, garbled mel. Replace with true phone‑level forced alignment (`align_durations_v4.py`). | 0.88 → **0.40** |
82
- | **2. Diverse training text** | A narrow corpus (~234 Han chars) leaves ~39% of heldout characters unseen — the model can't pronounce what it never saw. Expand coverage (`select_diverse_text.py`). | 0.40 **0.06** |
 
 
83
 
84
- Applied to **both** languages in one single‑voice corpus, the same recipe yields a genuinely bilingual
85
- model (zh ≈ 0.07, English ≈ 0.04–0.12 WER) — no routing. Everything else is Inflect‑Nano‑v1's defaults.
 
 
 
 
86
 
87
- **Takeaway for your own finetune:** *alignment quality* and *vocabulary coverage* dominate. Get those
88
- two right and a tiny frozen model is enough.
 
 
 
 
 
 
 
 
 
 
 
89
 
90
  ---
91
 
@@ -93,89 +136,80 @@ two right and a tiny frozen model is enough.
93
 
94
  - **Acoustic** — `MicroFastSpeech` (~3.47M): depthwise Conv‑FFN, **no attention**, external durations +
95
  length regulator, frame‑pitch, BiGRU, postnet.
96
- - **Vocoder** — Snake‑HiFiGAN (~1.17M), **8 kHz variant** `snake_8k` (sr 8000, n_fft 512, hop 128, 80 mels).
 
97
  - **Frontend** — `g2pw` (Taiwan bopomofo + polyphone disambiguation) + `g2p_en` (arpabet), merged into
98
  one phone sequence with per‑phone language ids → handles zh, en, and code‑mix in a single pass.
99
 
100
  ---
101
 
102
- ## Train / fine‑tune your own (voice or language)
103
 
104
- The pipeline is 5 steps: **data → align → train acoustic → train vocoder → export**. Repo layout:
 
105
 
106
  ```
107
  acoustic_encoder.onnx acoustic_decoder.onnx vocoder.onnx meta.json symbol_table.json ← deployable weights
108
- acoustic_zh_v2_35k.pt ← checkpoint (resume/fine‑tune)
109
  scripts/ frontend, aligner, corpus‑gen, diverse‑text, train, export, eval
110
  inflect_nano/ the trainer (acoustic.py + vocoder.py), forked from Inflect‑Nano‑v1 (LICENSE included)
111
  ```
112
 
113
  **Prerequisites:** Python 3.12, a GPU for training; `pip install torch torchaudio transformers
114
- onnxruntime soundfile librosa g2pw g2p_en cn2an opencc`. A single‑speaker teacher TTS (or clean
115
- recordings) for the audio. Put the trainer on your path: `PYTHONPATH=. python -m inflect_nano.acoustic …`.
116
 
117
- ### 1 · Diverse text → `train_zh.tsv`, `train_en.tsv`
118
  ```bash
119
- python scripts/select_diverse_text.py --lang zh --n 6000 --out train_zh.tsv
120
- python scripts/select_diverse_text.py --lang en --n 6000 --out train_en.tsv
 
 
121
  ```
122
- Tatoeba → OpenCC `s2twp` (zh) → greedy char/word‑coverage selection. **Coverage is the #1 driver of
123
- held‑out quality** — don't skimp here.
124
 
125
- ### 2 · Teacher corpus → `corpus/{*.wav, manifest.jsonl}`
126
  ```bash
127
- python scripts/gen_breezy_corpus.py --corpus train_zh.tsv --out-dir corpus --cer-thresh 0.30
 
128
  ```
129
- Synthesizes each line in **one voice**, keeping a clip only if an ASR transcript matches the text
130
- (here BreezyVoice + Breeze‑ASR‑25, t2s‑normalized). Any clean single‑speaker source works — for native
131
- code‑mix, use the **same voice** for zh and en.
132
 
133
- ### 3 · Phone‑level alignment → `align.jsonl` ⭐ *the key step*
134
  ```bash
135
- python scripts/align_durations_v4.py --manifest corpus/manifest.jsonl --out align.jsonl
136
  ```
137
- Per‑phone durations from the real audio via espeak phoneme‑CTC + `torchaudio.forced_align`.
138
- **Skipping or approximating this is what makes tiny TTS sound garbled.**
139
 
140
- ### 4 · Train the acoustic model → `acoustic_8k/…pt`
141
- ```bash
142
- PYTHONPATH=. python -m inflect_nano.acoustic --durations-jsonl align.jsonl \
143
- --out-dir acoustic_8k --vocoder-variant snake_8k --sample-rate 8000 \
144
- --steps 60000 --batch-size 16 --vocoder-checkpoint <vocoder.pt> --vocoder-mel-weight 1.0
145
- ```
146
- Mix languages in one corpus, single speaker (see `scripts/run_bilingual.sh`).
147
-
148
- ### 5 · Train the 8 kHz vocoder → `vocoder_8k/…pt`
149
  ```bash
150
  PYTHONPATH=. python -m inflect_nano.vocoder --train-jsonl voc_rows.jsonl \
151
- --out-dir vocoder_8k --variant snake_8k --steps 40000 --stft-weight 2.5
152
  ```
153
- Train on the same diverse audio. Higher `--stft-weight` → crisper waveform (see `scripts/run_voc_retrain.sh`).
154
 
155
- ### 6 · Export to ONNX → the deployable weights
156
  ```bash
157
- python scripts/export_8k.py --acoustic-ckpt acoustic_8k/…pt --vocoder-ckpt vocoder_8k/…pt --out-dir onnx/
 
 
 
 
 
158
  ```
159
 
160
- ### 7 · Evaluate
161
  ```bash
 
162
  python scripts/synth_from_text.py --onnx-dir onnx --out-dir syn --texts eval.jsonl
163
- python scripts/assess_big.py --synth-dir syn # offline X‑ASR CER/WER
164
  ```
165
- Use **≥30 held‑out sentences** — small eval sets are too noisy to trust.
166
-
167
- > **Note on the published weights:** these are the **zh‑focused** checkpoint (strong Mandarin). The
168
- > more balanced **bilingual** checkpoint (zh ≈ 0.07 / English ≈ 0.04 WER) replaces them as training
169
- > completes; the demo always loads whatever is here.
170
 
171
  ---
172
 
173
  ## Credits & licenses
174
 
175
  - **Base model / trainer:** [`owensong/Inflect-Nano-v1`](https://huggingface.co/owensong/Inflect-Nano-v1) (Apache‑2.0; see `inflect_nano/LICENSE.inflect-nano`)
176
- - **Teacher / gate ASR:** BreezyVoice · `Breeze-ASR-25` (MediaTek Research)
 
177
  - **Aligner:** `facebook/wav2vec2-lv-60-espeak-cv-ft` + `torchaudio.forced_align`
178
  - **Frontend:** `g2pw` (Taiwan readings) + `g2p_en` · **Eval ASR:** sherpa‑onnx X‑ASR (zh‑en Zipformer)
179
- - **Text:** Tatoeba (CC‑BY 2.0 FR)
180
 
181
  This repository: **Apache‑2.0**.
 
22
 
23
  A **4.63M‑parameter** Mandarin (Taiwan) + English text‑to‑speech model that runs **entirely on CPU**
24
  and emits **8 kHz** audio — sized for **G.711 telephony** and **on‑device (Jetson‑class)** use.
25
+ One model, **one young‑female voice**: Chinese, English, and code‑mix through a single frontend (no
26
+ language routing).
27
 
28
+ > 🔊 **Live demo:** https://huggingface.co/spaces/Luigi/PrimeTTS-vs-Inflect-Nano-v1 · 🧩 **Base:** [`owensong/Inflect-Nano-v1`](https://huggingface.co/owensong/Inflect-Nano-v1) (warm‑started fine‑tune, **same frozen architecture**)
29
 
30
  | | |
31
  |---|---|
32
  | **Parameters** | 4.63M (3.47M acoustic + 1.17M vocoder) |
33
  | **Sample rate** | 8 kHz (telephony‑band) |
34
  | **Runtime** | `onnxruntime`, **CPU‑only**, torch‑free at inference |
35
+ | **Languages** | zh‑TW (Traditional) + English + code‑mix, single voice |
36
+ | **Voice** | young female, **Taiwan‑Mandarin accent** |
37
  | **Architecture** | FastSpeech‑style (no attention) + Snake‑HiFiGAN — **frozen**, no NAS |
38
  | **License** | Apache‑2.0 |
39
 
40
+ ### Held‑out quality (eval_big, 36 unseen phone‑attendant sentences)
41
+
42
+ | metric | this model | prior 8k release |
43
+ |---|---|---|
44
+ | zh‑CER (Breeze‑ASR‑25) | **0.090** | 0.132 |
45
+ | code‑mix CER | **0.178** | 0.254 |
46
+ | en‑WER (Whisper) | **0.083** | 0.092 |
47
+ | Taiwan‑accent gap¹ | **+0.088** | +0.017 |
48
+ | SQUIM PESQ | **3.31** | 2.07 |
49
+ | SQUIM MOS | **4.24** | 3.30 |
50
+
51
+ ¹ `CER(generic ASR) − CER(Taiwan‑tuned Breeze‑ASR‑25)` per zh clip; `>0` ⇒ a Taiwan‑tuned recognizer
52
+ understands it better ⇒ genuine Taiwan accent present.
53
+
54
  ---
55
 
56
  ## Quickstart (inference, CPU)
 
86
 
87
  ---
88
 
89
+ ## Training data
90
+
91
+ Everything is **distilled from a single teacher voice** so zh / en / code‑mix share one timbre and accent.
92
 
93
+ - **Reference voice** a young Taiwan‑female sample synthesized with **Edge‑TTS `zh‑TW‑HsiaoChenNeural`**
94
+ (a genuine Taiwan‑Mandarin voice that also reads English). This is what fixes the accent: Taiwan
95
+ Mandarin can't be prompted out of a generic model, so we *clone a Taiwan reference* instead.
96
+ - **Teacher** — **VoxCPM2** (`openbmb/VoxCPM2`) voice‑clones that one reference for every line, giving a
97
+ consistent young‑female voice across all three languages (48 kHz, resampled to 8 kHz for training).
98
+ - **Text** — Taiwan office / phone‑attendant register: diverse Mandarin, general + domain English, and
99
+ frame‑bank code‑mix (`gen_codemix.py`) with English in varied positions.
100
+ - **ASR quality gate** — every clip is transcribed and kept only if it matches its text, using a
101
+ **Taiwan‑tuned recognizer** so the gate never penalizes the accent we want:
102
+ - zh & code‑mix → **Breeze‑ASR‑25** Han‑level CER (≤ 0.12 zh, ≤ 0.15 mix)
103
+ - English → **Whisper‑medium** WER (≤ 0.20)
104
 
105
+ | split | clean clips | dropped by gate |
106
  |---|---|---|
107
+ | pure Chinese | 2,077 | 16.9% |
108
+ | codemix (zh+en) | 2,571 | 8.2% |
109
+ | pure English | 1,975 | 1.2% |
110
+ | **total** | **6,623** | 9.3% |
111
 
112
+ ---
113
+
114
+ ## How it was trained — the levers
115
+
116
+ Inflect‑Nano‑v1's 4.63M architecture is **not capacity‑limited** for this task. Quality came from four
117
+ fixable things, all keeping the **architecture frozen** (no NAS, no param changes):
118
 
119
+ 1. **Phonelevel alignment** (`align_durations_v4.py`) true per‑phone durations (espeak phoneme‑CTC +
120
+ `torchaudio.forced_align`) instead of crude char/letter CTC. Skipping this is what makes tiny TTS garble.
121
+ 2. **Vocabulary coverage + diverse code‑mix** — broad character coverage and a code‑mix **frame bank**
122
+ (varied syntax, English in varied positions) so the model isn't overfit to a few templates.
123
+ 3. **Teacher choice** — the English a tiny model learns is only as native as the teacher's. A Taiwan‑
124
+ biased teacher gave flat, accented English; **VoxCPM2** gives clean, natural zh *and* en in one voice.
125
+ 4. **Warm‑start from Inflect‑Nano‑v1** — the acoustic model is initialized from the English‑native v1
126
+ checkpoint (**199/199 tensors copied, 0 skipped** — the bilingual symbol table already matches), so
127
+ v1's English transfers directly; the corpus then teaches Taiwan Mandarin on top.
128
+
129
+ A **2D mel‑GAN** discriminator (training‑only; ONNX is unchanged) sharpens the mel after a 25k pure‑
130
+ reconstruction warmup, lifting PESQ/MOS. The shipped checkpoint is the **35k step** — the held‑out sweet
131
+ spot, *before* the GAN starts over‑sharpening held‑out intelligibility.
132
 
133
  ---
134
 
 
136
 
137
  - **Acoustic** — `MicroFastSpeech` (~3.47M): depthwise Conv‑FFN, **no attention**, external durations +
138
  length regulator, frame‑pitch, BiGRU, postnet.
139
+ - **Vocoder** — Snake‑HiFiGAN (~1.17M), **8 kHz variant** `snake_8k` (sr 8000, n_fft 512, hop 128, 80 mels),
140
+ retrained on the teacher corpus.
141
  - **Frontend** — `g2pw` (Taiwan bopomofo + polyphone disambiguation) + `g2p_en` (arpabet), merged into
142
  one phone sequence with per‑phone language ids → handles zh, en, and code‑mix in a single pass.
143
 
144
  ---
145
 
146
+ ## Reproduce / fine‑tune your own
147
 
148
+ Pipeline: **teacher corpus ASR gate → align → train vocoderwarm‑start + train acoustic → export**.
149
+ Repo layout:
150
 
151
  ```
152
  acoustic_encoder.onnx acoustic_decoder.onnx vocoder.onnx meta.json symbol_table.json ← deployable weights
153
+ acoustic_tw_8k_35k.pt ← shipped checkpoint
154
  scripts/ frontend, aligner, corpus‑gen, diverse‑text, train, export, eval
155
  inflect_nano/ the trainer (acoustic.py + vocoder.py), forked from Inflect‑Nano‑v1 (LICENSE included)
156
  ```
157
 
158
  **Prerequisites:** Python 3.12, a GPU for training; `pip install torch torchaudio transformers
159
+ onnxruntime soundfile librosa g2pw g2p_en cn2an opencc faster-whisper edge-tts`.
 
160
 
161
+ ### 1 · Teacher corpus (one cloned voice)
162
  ```bash
163
+ # make a Taiwan‑female reference, then VoxCPM2‑clone every line in that voice
164
+ edge-tts --voice zh-TW-HsiaoChenNeural --text "<ref sentence>" --write-media ref.mp3
165
+ python gen_voxcpm_corpus.py --texts texts.jsonl --ref ref.wav --ref-text ref.txt \
166
+ --out-dir corpus --manifest manifest.jsonl
167
  ```
 
 
168
 
169
+ ### 2 · ASR quality gate (Taiwan‑tuned)
170
  ```bash
171
+ python asr_filter.py --manifest manifest.jsonl --out manifest \
172
+ --device cuda # Breeze‑ASR‑25 (zh/mix) + Whisper‑medium (en) → manifest.clean.jsonl
173
  ```
 
 
 
174
 
175
+ ### 3 · Phone‑level alignment ⭐ *the key step*
176
  ```bash
177
+ python scripts/align_durations_v4.py --manifest manifest.clean.jsonl --out align.jsonl
178
  ```
 
 
179
 
180
+ ### 4 · Train the 8 kHz vocoder
 
 
 
 
 
 
 
 
181
  ```bash
182
  PYTHONPATH=. python -m inflect_nano.vocoder --train-jsonl voc_rows.jsonl \
183
+ --out-dir vocoder_8k --variant snake_8k --steps 40000 --stft-weight 2.0
184
  ```
 
185
 
186
+ ### 5 · Warm‑start + train the acoustic (GAN recipe)
187
  ```bash
188
+ PYTHONPATH=. python -m inflect_nano.acoustic --durations-jsonl align.jsonl \
189
+ --out-dir acoustic_8k --vocoder-variant snake_8k --sample-rate 8000 \
190
+ --vocoder-checkpoint vocoder_8k/hifigan-snake_8k-final.pt --vocoder-mel-weight 1.0 \
191
+ --init-checkpoint inflect_nano_v1_acoustic.pt \
192
+ --mel-gan-weight 0.1 --gan-2d --gan-fm-auto --gan-r1-gamma 1.0 --gan-crop 128 --gan-warmup-steps 25000 \
193
+ --steps 60000 --batch-size 16 --en-upsample 1
194
  ```
195
 
196
+ ### 6 · Export to ONNX + evaluate
197
  ```bash
198
+ python scripts/export_8k.py --acoustic-ckpt acoustic_8k/…pt --vocoder-ckpt vocoder_8k/…pt --out-dir onnx/
199
  python scripts/synth_from_text.py --onnx-dir onnx --out-dir syn --texts eval.jsonl
200
+ python scripts/assess_big.py --synth-dir syn # offline CER/WER
201
  ```
202
+ Evaluate on **≥30 held‑out sentences** — small eval sets are too noisy to trust. Sweep checkpoints and
203
+ pick the **held‑out** sweet spot (the GAN keeps improving train‑set sharpness past the held‑out optimum).
 
 
 
204
 
205
  ---
206
 
207
  ## Credits & licenses
208
 
209
  - **Base model / trainer:** [`owensong/Inflect-Nano-v1`](https://huggingface.co/owensong/Inflect-Nano-v1) (Apache‑2.0; see `inflect_nano/LICENSE.inflect-nano`)
210
+ - **Teacher TTS:** [`openbmb/VoxCPM2`](https://huggingface.co/openbmb/VoxCPM2) · **Reference voice:** Microsoft **Edge‑TTS** `zh‑TW‑HsiaoChenNeural`
211
+ - **Gate ASR:** `Breeze-ASR-25` (MediaTek Research, Taiwan Mandarin + code‑switch) · OpenAI **Whisper‑medium**
212
  - **Aligner:** `facebook/wav2vec2-lv-60-espeak-cv-ft` + `torchaudio.forced_align`
213
  - **Frontend:** `g2pw` (Taiwan readings) + `g2p_en` · **Eval ASR:** sherpa‑onnx X‑ASR (zh‑en Zipformer)
 
214
 
215
  This repository: **Apache‑2.0**.