kazeia/kazeia-android/COMPILE_WHISPER_NPU.md

6.2 KiB
Raw Blame History

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

# 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 :
qai-hub configure --api_token <VOTRE_TOKEN>

La configuration est sauvée dans ~/.qai_hub/client.ini.

Compilation

Commande générale

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

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

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

# 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

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 :

# 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

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