# 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 ``` 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..export \ --device "Snapdragon 8 Elite QRD" \ --target-runtime precompiled_qnn_onnx \ --precision float \ --skip-profiling \ --skip-inferencing \ --output-dir /opt/Kazeia/models_qnn/ ``` ### 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="/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