185 lines
6.2 KiB
Markdown
185 lines
6.2 KiB
Markdown
# Compiling Whisper for Qualcomm NPU (AI Hub)
|
||
|
||
Guide pour compiler les modèles Whisper (Base/Small/Medium) via Qualcomm AI Hub
|
||
pour déploiement NPU sur Snapdragon 8 Elite (SM8750).
|
||
|
||
## Prérequis
|
||
|
||
### Environnement Python
|
||
```bash
|
||
# Créer un venv dédié (Python 3.10 recommandé)
|
||
python3 -m venv /opt/Kazeia/qnn_venv
|
||
source /opt/Kazeia/qnn_venv/bin/activate
|
||
|
||
# Installer qai-hub et les modèles
|
||
pip install qai-hub qai-hub-models
|
||
```
|
||
|
||
### Compte Qualcomm AI Hub
|
||
1. Créer un compte sur https://aihub.qualcomm.com
|
||
2. Générer un API token dans les paramètres du compte
|
||
3. Configurer le token :
|
||
```bash
|
||
qai-hub configure --api_token <VOTRE_TOKEN>
|
||
```
|
||
|
||
La configuration est sauvée dans `~/.qai_hub/client.ini`.
|
||
|
||
## Compilation
|
||
|
||
### Commande générale
|
||
```bash
|
||
source /opt/Kazeia/qnn_venv/bin/activate
|
||
|
||
python3 -m qai_hub_models.models.<MODEL_NAME>.export \
|
||
--device "Snapdragon 8 Elite QRD" \
|
||
--target-runtime precompiled_qnn_onnx \
|
||
--precision float \
|
||
--skip-profiling \
|
||
--skip-inferencing \
|
||
--output-dir /opt/Kazeia/models_qnn/<OUTPUT_DIR>
|
||
```
|
||
|
||
### Modèles disponibles
|
||
|
||
| Modèle | Module | Paramètres | Layers | Heads |
|
||
|--------|--------|------------|--------|-------|
|
||
| Whisper-Base | `whisper_base` | 77M | 6 | 8 |
|
||
| Whisper-Small | `whisper_small` | 244M | 12 | 12 |
|
||
| Whisper-Medium | `whisper_medium` | 769M | 24 | 16 |
|
||
|
||
### Exemple : Whisper-Small
|
||
```bash
|
||
python3 -m qai_hub_models.models.whisper_small.export \
|
||
--device "Snapdragon 8 Elite QRD" \
|
||
--target-runtime precompiled_qnn_onnx \
|
||
--precision float \
|
||
--skip-profiling \
|
||
--skip-inferencing \
|
||
--output-dir /opt/Kazeia/models_qnn/whisper-small-sm8750
|
||
```
|
||
|
||
**Temps de compilation** : ~5-10 minutes (upload modèle + compilation cloud).
|
||
|
||
### Exemple : Whisper-Base
|
||
```bash
|
||
python3 -m qai_hub_models.models.whisper_base.export \
|
||
--device "Snapdragon 8 Elite QRD" \
|
||
--target-runtime precompiled_qnn_onnx \
|
||
--precision float \
|
||
--skip-profiling \
|
||
--skip-inferencing \
|
||
--output-dir /opt/Kazeia/models_qnn/whisper-base-sm8750
|
||
```
|
||
|
||
### Options de device
|
||
```bash
|
||
# Lister les devices disponibles
|
||
qai-hub list-devices | grep -i "8 elite\|sm8750"
|
||
|
||
# Devices SM8750 (Snapdragon 8 Elite) :
|
||
# "Snapdragon 8 Elite QRD" → chipset: qualcomm-snapdragon-8-elite, sm8750
|
||
# "Samsung Galaxy S25 (Family)" → chipset: qualcomm-snapdragon-8-elite-for-galaxy, sm8750-ac
|
||
#
|
||
# Note: OnePlus Pad 2/3 utilise sm8750 standard (pas "for-galaxy")
|
||
```
|
||
|
||
## Fichiers générés
|
||
|
||
La compilation produit un sous-dossier avec :
|
||
|
||
```
|
||
whisper_small-precompiled_qnn_onnx-float-qualcomm_snapdragon_8_elite/
|
||
├── HfWhisperEncoder.onnx # Stub ONNX (~3KB)
|
||
├── HfWhisperEncoder_qairt_context.bin # Context binaire QNN (encoder)
|
||
├── HfWhisperDecoder.onnx # Stub ONNX (~8KB)
|
||
├── HfWhisperDecoder_qairt_context.bin # Context binaire QNN (decoder)
|
||
├── metadata.yaml # Métadonnées de compilation
|
||
└── vocab.bin # Vocabulaire (non utilisé)
|
||
```
|
||
|
||
### Architecture des modèles
|
||
|
||
**Encoder** (HfWhisperEncoder) :
|
||
- Input : `input_features` [1, 80, 3000] fp16
|
||
- Output : Cross-attention KV caches (N layers × k,v)
|
||
- `k_cache_cross_N` : [num_heads, 1, 64, 1500] fp16
|
||
- `v_cache_cross_N` : [num_heads, 1, 1500, 64] fp16
|
||
|
||
**Decoder** (HfWhisperDecoder) — KV-cache autorégressif :
|
||
- Inputs :
|
||
- `input_ids` : [1, 1] int32 (un token à la fois)
|
||
- `attention_mask` : [1, 1, 1, 200] fp16 (right-aligned, -100 pour masqué)
|
||
- `k_cache_self_N_in` / `v_cache_self_N_in` : Self KV caches (199 slots, init zeros)
|
||
- `k_cache_cross_N` / `v_cache_cross_N` : Cross KV caches (depuis encoder)
|
||
- `position_ids` : [1] int32
|
||
- Outputs :
|
||
- `logits` : [1, 51865, 1, 1] fp16
|
||
- `k_cache_self_N_out` / `v_cache_self_N_out` : Self KV caches mis à jour
|
||
|
||
## Déploiement sur tablette
|
||
|
||
### 1. Copier les modèles
|
||
```bash
|
||
MODEL_DIR="/data/local/tmp/kazeia/models/whisper-small-sm8750"
|
||
SRC="<chemin_local>/whisper_small-precompiled_qnn_onnx-float-qualcomm_snapdragon_8_elite"
|
||
|
||
adb shell "mkdir -p $MODEL_DIR"
|
||
adb push $SRC/HfWhisperEncoder.onnx $MODEL_DIR/
|
||
adb push $SRC/HfWhisperEncoder_qairt_context.bin $MODEL_DIR/
|
||
adb push $SRC/HfWhisperDecoder.onnx $MODEL_DIR/
|
||
adb push $SRC/HfWhisperDecoder_qairt_context.bin $MODEL_DIR/
|
||
```
|
||
|
||
### 2. Copier les assets partagés
|
||
Les fichiers `mel_filters.json` et `vocab.json` sont communs à toutes les variantes Whisper :
|
||
```bash
|
||
# Si déjà présents depuis une autre variante :
|
||
adb shell "cp /data/local/tmp/kazeia/models/whisper-sm8750/mel_filters.json $MODEL_DIR/"
|
||
adb shell "cp /data/local/tmp/kazeia/models/whisper-sm8750/vocab.json $MODEL_DIR/"
|
||
```
|
||
|
||
### 3. Configurer le chemin dans KazeiaService.kt
|
||
```kotlin
|
||
npuStt.load("$modelsDir/whisper-small-sm8750")
|
||
```
|
||
|
||
## Tailles des modèles compilés
|
||
|
||
| Modèle | Encoder (bin) | Decoder (bin) | Total |
|
||
|--------|--------------|---------------|-------|
|
||
| Whisper-Base | 47 MB | 145 MB | ~192 MB |
|
||
| Whisper-Small | 201 MB | 345 MB | ~546 MB |
|
||
|
||
## Performances sur OnePlus Pad (SM8750)
|
||
|
||
| Étape | Whisper-Base | Whisper-Small |
|
||
|-------|-------------|---------------|
|
||
| Mel (C++ natif) | ~220ms | ~220ms |
|
||
| Encoder NPU | ~140ms | ~270ms* |
|
||
| Decoder NPU (par step) | ~13ms | TBD |
|
||
| Load encoder | ~150ms | ~270ms |
|
||
| Load decoder | ~150ms | ~250ms |
|
||
|
||
*À confirmer avec des benchmarks réels sur audio.
|
||
|
||
## Dépannage
|
||
|
||
### "resource failed to call close"
|
||
Warnings bénins de l'ONNX Runtime. Les sessions ORT non fermées proprement génèrent ces messages lors du GC.
|
||
|
||
### Modèle ne se charge pas
|
||
- Vérifier que `HfWhisperEncoder_qairt_context.bin` est dans le MÊME répertoire que `HfWhisperEncoder.onnx`
|
||
- Vérifier que `libQnnHtp.so` est accessible via `nativeLibDir`
|
||
- Le modèle doit être compilé pour le bon chipset (sm8750, pas sm8750-ac ni sm8850)
|
||
|
||
### Mauvaise détection de langue
|
||
Whisper-Base est peu fiable pour la détection de langue sur segments courts. Whisper-Small est nettement meilleur. Si besoin, on peut forcer le token de langue après SOT.
|
||
|
||
## Référence
|
||
|
||
- Qualcomm AI Hub : https://aihub.qualcomm.com
|
||
- Code de référence : `qai_hub_models/models/_shared/hf_whisper/app.py`
|
||
- HuggingFace Whisper-Base : https://huggingface.co/qualcomm/Whisper-Base
|
||
- HuggingFace Whisper-Small : https://huggingface.co/qualcomm/Whisper-Small
|