kazeia/RAPPORT_TTS.md

254 lines
9.2 KiB
Markdown

# Rapport TTS - Qwen3-TTS sur OnePlus Pad 3
**Date** : 26 mars 2026
**Objectif** : Synthèse vocale avec clonage de voix en français pour le chatbot Kazeia
**Tablette** : OnePlus Pad 3 (Snapdragon 8 Elite, NPU Hexagon HTP v79)
---
## 1. Modèles TTS évalués
### 1.1 Chatterbox Multilingual (ONNX)
| Caractéristique | Détail |
|---|---|
| Repo | `onnx-community/chatterbox-multilingual-ONNX` |
| Architecture | LLM 0.5B (Llama) + speech encoder + conditional decoder |
| Français | Oui (23 langues) |
| Clonage vocal | Oui |
| Format | ONNX (FP32, FP16, Q4F16) |
| Taille totale | ~1.5 Go (Q4F16) |
**Résultat** : Le modèle charge et génère des tokens mais le **décodage audio ne fonctionne que sur la première seconde**. Les versions quantifiées (FP16, Q4F16) ne produisent pas le stop token, résultant en du silence après le début. Seul le FP32 (2 Go LM) fonctionne correctement mais uniquement pour des phrases très courtes.
**Verdict** : Non fiable pour la production.
### 1.2 Qwen3-TTS (PyTorch natif)
| Caractéristique | 0.6B Base | 1.7B CustomVoice | 1.7B Base |
|---|---|---|---|
| Clonage vocal | Oui (x_vector) | Non (voix intégrées) | Oui (x_vector) |
| Français | Oui | Oui | Oui |
| Taille FP32 | ~1.8 Go | ~4.5 Go | ~4.5 Go |
| Qualité clonage | Bonne | N/A | Bonne |
**Résultat** : Fonctionne parfaitement sur CPU avec les voix françaises du projet Kazeia.
---
## 2. Tests Qwen3-TTS sur CPU (PC)
**Phrase** : *"Bonjour, je comprends que vous soyez triste. Je suis là pour vous écouter."*
### 2.1 Résultats par modèle et voix
| Modèle | Voix | Audio | Temps | Vitesse | Qualité |
|---|---|---:|---:|---|---|
| **0.6B Base** | Damien (clone) | 4.0s | 18.4s | 0.22x RT | Bonne |
| **0.6B Base** | Élodie (clone) | 4.3s | 18.6s | 0.23x RT | Bonne |
| **1.7B CustomVoice** | Vivian (intégrée) | 6.1s | 37.8s | 0.16x RT | Très bonne |
| **1.7B CustomVoice** | Serena (intégrée) | 4.3s | 26.4s | 0.16x RT | Très bonne |
| **1.7B Base** | Damien (clone) | 5.0s | 30.9s | 0.16x RT | Très bonne |
| **1.7B Base** | Élodie (clone) | 3.8s | 23.7s | 0.16x RT | Très bonne |
### 2.2 Observations
- Le **0.6B est ~1.4x plus rapide** que le 1.7B (0.22x vs 0.16x RT)
- Le **clonage vocal** fonctionne bien avec les voix françaises (accent naturel)
- Les échantillons de référence doivent être **courts** (~3-10 secondes)
- L'audio est continu et complet sur toute la durée (RMS > 0 partout)
- Le 0.6B offre le **meilleur rapport qualité/vitesse** pour le clonage
---
## 3. Export du talker Qwen3-TTS pour le NPU
### 3.1 Architecture du modèle
Qwen3-TTS 0.6B est composé de 4 sous-modèles :
```
Texte + Voix ref → [Speaker Encoder] → x-vector
[Talker LM] ← 28 couches Qwen3, 1024 hidden
↓ (identique à Qwen3-0.6B LLM)
[Code Predictor] ← 5 couches, 15 codebooks
[Speech Decoder] ← ConvNet + Transformer
Audio WAV
```
| Composant | Params | Rôle |
|---|---:|---|
| **Talker (LM)** | 754.8M | Génère les speech tokens (autorégressif) |
| Code Predictor | 141.6M | Prédit les 15 codebooks parallèles |
| Speaker Encoder | 8.9M | Extrait l'empreinte vocale |
| Speech Decoder | 114.3M | Décode les tokens en audio |
### 3.2 Conversion pour ExecuTorch + QNN
Le talker est **architecturalement identique** à Qwen3-0.6B :
- 28 couches Transformer
- hidden_size = 1024
- 16 attention heads, 8 KV heads
- head_dim = 128
- QK norm + RoPE theta 1M
- Seule différence : vocab_size = 3072 (codec) au lieu de 151936 (texte)
**Étapes de conversion** :
1. Extraction des poids du talker depuis le modèle HuggingFace
2. Renommage HF → Meta format (wq/wk/wv/wo, feed_forward.w1/w2/w3)
3. Remplacement de `tok_embeddings` par `codec_embedding` (3072x1024)
4. Suppression de `text_projection` et `text_embedding`
5. Patch du tokenizer pour clamper les IDs de calibration à [0, 3071]
6. Export via le pipeline ExecuTorch Qwen3-0.6B existant
**Résultat** : `.pte` de **286 Mo** généré en ~20 minutes.
### 3.3 Test sur le NPU Hexagon
| Métrique | Valeur |
|---|---|
| **Débit decode** | **90.7 tok/s** |
| Prefill | 888 tok/s |
| Tokens générés | 503 en 5.5s |
| Taille .pte | 286 Mo |
| Time to first token | 9 ms |
**Comparaison** :
| Backend | Talker decode (tok/s) | vs CPU |
|---|---:|---|
| **NPU Hexagon (ExecuTorch)** | **90.7** | **+4.1x** |
| CPU PC (PyTorch) | ~22 | baseline |
### 3.4 Exportabilité des autres composants
| Composant | ExecuTorch | ONNX | Raison échec |
|---|---|---|---|
| **Talker** | **OK** (286 Mo .pte) | Échoue | torch.export incompatible |
| Code Predictor | Non | Non | Multi-tête (15 codebooks), non standard |
| Speaker Encoder | Non tenté | Non tenté | Petit (8.9M), rapide sur CPU |
| Speech Decoder | Non tenté | Échoue | ConvNet dynamique + boucles |
---
## 4. Pipeline TTS recommandé pour la tablette
### 4.1 Architecture hybride NPU + CPU
```
┌─────────────────────────────────────────────┐
│ TABLETTE │
│ │
│ [Speaker Encoder] ──→ x-vector │
│ (CPU, 8.9M) │
│ │
│ [Talker .pte] ──→ speech tokens │
│ (NPU, 90 tok/s, 286 Mo) │
│ │
│ [Code Predictor] ──→ 16 codebooks │
│ (CPU, 141M, ~5 couches) │
│ │
│ [Speech Decoder] ──→ audio WAV │
│ (CPU, 114M, ConvNet) │
└─────────────────────────────────────────────┘
```
### 4.2 Mesure réelle du pipeline (CPU PC) et estimation NPU
**Phrase** : *"Bonjour, je suis là pour vous accompagner."* → 2.3s d'audio
| Composant | % temps | CPU PC | NPU tablette (estimé) |
|---|---:|---:|---:|
| **Talker (LM autorégressif)** | **87%** | 9.9s | **0.3s** (90.7 tok/s) |
| Speaker Encoder + Code Predictor + Decoder | 13% | 1.5s | ~1.5s |
| **Total** | 100% | **11.5s** | **~1.9s** |
| **Ratio temps réel** | | 0.20x (5x trop lent) | **1.24x RT** |
Le NPU transforme un pipeline **5x trop lent** en un pipeline **temps réel** en accélérant le talker (le goulot d'étranglement) de 3 tok/s à 90.7 tok/s.
### 4.3 Prérequis pour le déploiement complet
1. **Termux** sur la tablette avec Python 3.10 + PyTorch CPU
2. Le `.pte` du talker déjà déployé
3. Les poids du code predictor + speech decoder en PyTorch
4. Un script d'orchestration qui :
- Encode la voix de référence (CPU)
- Génère les speech tokens via le talker .pte (NPU)
- Prédit les codebooks (CPU)
- Décode en audio (CPU)
---
## 5. Fichiers produits
### Sur le PC
| Chemin | Description |
|---|---|
| `models_qnn/qwen3-tts-executorch/hybrid_llama_qnn.pte` | Talker exporté pour NPU (286 Mo) |
| `models_qnn/qwen3-tts-export/qwen3_tts_talker.pth` | Poids talker format Meta (3.1 Go) |
| `models_qnn/qwen3-tts-export/config.json` | Config ExecuTorch du talker |
| `models_qnn/chatterbox-tts/` | Chatterbox ONNX (abandonné) |
| `tts_qwen3/` | Échantillons audio générés |
| `voix_clips/` | Voix françaises tronquées à 5s |
### Échantillons audio générés
| Fichier | Modèle | Voix |
|---|---|---|
| `tts_qwen3/qwen3tts_06b_damien.wav` | 0.6B Base | Damien (clone) |
| `tts_qwen3/qwen3tts_06b_elodie.wav` | 0.6B Base | Élodie (clone) |
| `tts_qwen3/qwen3tts_17b_Vivian.wav` | 1.7B CustomVoice | Vivian |
| `tts_qwen3/qwen3tts_17b_Serena.wav` | 1.7B CustomVoice | Serena |
| `tts_qwen3/qwen3tts_17b_base_damien.wav` | 1.7B Base | Damien (clone) |
| `tts_qwen3/qwen3tts_17b_base_elodie.wav` | 1.7B Base | Élodie (clone) |
### Voix de référence françaises
8 voix disponibles dans `/opt/Kazeia/voix/` (clips 5s dans `voix_clips/`) :
Amir, Damien, Didier, Élodie, Jérôme, Richard, Sid, Zelda
---
## 6. Commandes de référence
### Générer de la parole avec Qwen3-TTS (CPU PC)
```python
from qwen_tts import Qwen3TTSModel
model = Qwen3TTSModel.from_pretrained("Qwen/Qwen3-TTS-12Hz-0.6B-Base", device_map="cpu", dtype=torch.float32)
wavs, sr = model.generate_voice_clone(
text="Votre texte ici",
language="French",
ref_audio="voix_clips/damien.wav",
ref_text="Bonjour",
x_vector_only_mode=True,
)
soundfile.write("output.wav", wavs[0], sr)
```
### Exporter le talker TTS pour le NPU
```bash
# 1. Convertir les poids (voir script de conversion)
# 2. Exporter via ExecuTorch (avec patch tokenizer)
python3.10 examples/qualcomm/oss_scripts/llama/llama.py \
-m SM8750 -b build-android --decoder_model qwen3-0_6b \
--checkpoint qwen3_tts_talker.pth --params config.json \
-s DEVICE_ID --backend htp -c -a OUTPUT_DIR
```
### Tester le talker sur le NPU tablette
```bash
python3.10 examples/qualcomm/oss_scripts/llama/llama.py \
-m SM8750 -b build-android --decoder_model qwen3-0_6b \
-s DEVICE_ID --backend htp \
--pre_gen_pte OUTPUT_DIR -a OUTPUT_DIR --prompt "test"
```
---
*Rapport généré par Claude Code (Opus 4.6)*