qnn-npu-compile

Compile des modèles ONNX pour les exécuter sur les NPU Qualcomm (Hexagon HTP, via le SDK QNN / QAIRT) dans des applications Android, et les utiliser avec sherpa-onnx ou ONNX Runtime. Utilisez-le lorsque l'utilisateur souhaite exécuter un modèle (parole, audio, vision, petit transformeur) sur le NPU d'un téléphone Snapdragon pour gagner en vitesse ou en autonomie, ou lorsqu'il demande comment compiler/convertir/quantifier un modèle pour QNN, HTP, Hexagon, Snapdragon ou « le NPU ».

Qu'est-ce que qnn-npu-compile ?

Ce skill fournit une procédure détaillée, étape par étape, pour compiler des modèles ONNX afin qu'ils s'exécutent sur les unités de traitement neuronal (NPU) Qualcomm Snapdragon, en particulier le Hexagon HTP via le SDK QAIRT. Il couvre l'ensemble du pipeline : de l'export initial du modèle depuis PyTorch (en veillant à ce que les formes soient statiques et que les entrées soient en int32) jusqu'à la conversion à l'aide de `qnn-onnx-converter`, la génération de bibliothèques de modèles et de binaires de contexte spécifiques à une puce, puis le déploiement et l'exécution sur un appareil Android. Il explique pourquoi l'utilisation du NPU est bénéfique (vitesse et économie d'énergie pour les opérations matricielles lourdes et régulières) et ses limites (formes statiques, limites de taille de modèle, binaires de contexte spécifiques à une architecture). Le skill est basé sur le flux de travail utilisé par le projet sherpa-onnx pour publier ses propres modèles compatibles NPU ; il inclut des vérifications de validation (précision, rapidité, puissance) et des conseils de dépannage pour les problèmes courants (formes dynamiques, repli silencieux sur le CPU, dépendances de version du SDK). Il s'adresse aux développeurs qui souhaitent accélérer leurs applications Android et améliorer leur autonomie en déchargeant les modèles d'IA sur le matériel dédié.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/chencohensolutions/claude-skills/tree/HEAD/plugins/qnn-npu-compile/skills/qnn-npu-compile

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Compilation de modèles pour les NPU Qualcomm (QNN HTP)

Ceci est une procédure, pas un script : chaque modèle nécessite son propre export et ses propres vérifications. Elle suit la façon dont sherpa-onnx compile ses modèles NPU publiés (k2-fsa/sherpa-onnx, .github/workflows/export-*-qnn.yaml, .github/scripts/export-qnn/, scripts/qnn/), qui sont connus pour fonctionner. Lisez le workflow correspondant avant de commencer ; adaptez, n'inventez pas.

0. Déterminer si le NPU en vaut la peine

  • Mesurez d'abord sur le téléphone cible : utilisation CPU (%) et temps réel du modèle tel qu'il s'exécute actuellement (/proc/<pid>/stat toutes les 2 s, top -H -p <pid> par thread), et autonomie de la batterie sur une tâche fixe si l'objectif est l'économie d'énergie. Le NPU est rentable pour les gros travaux matriciels stables (encodeurs, longs passages par lots) ; pour un petit modèle sur 1 à 2 threads, le gain peut être faible.
  • Formes statiques uniquement. Le NPU exécute des graphes de taille fixe : lot fixe (1), longueur d'entrée fixe (taille de segment, ou 30 s pour l'encodeur de Whisper). Un modèle dont la longueur d'entrée varie nécessite un segment fixe, du remplissage ou plusieurs graphes.
  • Une famille de puces par compilation. Un binaire de contexte est compilé pour une architecture Hexagon (v73, v75, v79, v81…) et ne fonctionne que sur celle-ci ; les autres téléphones (et Exynos / Tensor / MediaTek) conservent le chemin CPU. Prévoyez toujours une solution de repli vers le CPU.
  • Limites de taille. Les très grands modèles (plus d'environ 1 milliard de paramètres en fp16) peuvent ne pas tenir dans un contexte : ils nécessitent des poids 8 bits (w8a16) et/ou une division en plusieurs graphes. Les propres versions Whisper de sherpa-onnx s'arrêtent à la taille moyenne (769 M).

Puces (d'après scripts/qnn/device_info.py de sherpa-onnx) : SM8550 Snapdragon 8 Gen 2 (v73), SM8650 8 Gen 3 (v75), SM8750 8 Elite (v79), SM8850 (v81), QCS9100 (v73). SM8350 (v68) n'a pas de fp16 : ignorez-le. Trouvez la puce d'un téléphone avec adb shell getprop ro.soc.model.

1. Outils (Linux x86-64 uniquement)

Le SDK QNN (QAIRT) fonctionne sur Linux x86-64 (ou Windows), pas sur macOS. Sur Mac : une VM Linux/une instance cloud, ou Docker avec --platform linux/amd64 (lent : émulé). sherpa-onnx construit une image Docker pour cela (Dockerfile-2.40, qnn-sdk-docker-build.yaml).

  • SDK QAIRT, par exemple 2.40.0.251030 : depuis Qualcomm (compte gratuit), ou la copie téléchargée par le CI de sherpa-onnx (huggingface.co/csukuangfj/qnn-toolkit, v2.40.0.251030.zip). Vérifiez que sa licence autorise votre utilisation.
  • source qairt/<version>/bin/envsetup.sh (définit QNN_SDK_ROOT), puis sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh et python3 ${QNN_SDK_ROOT}/bin/check-python-dependency.
  • Environnement virtuel Python 3.10 avec numpy<2 onnx==1.17.0 onnxruntime onnxsim plus les dépendances d'export du modèle lui-même.
  • Android NDK dans le PATH (ndk-build), pour les bibliothèques de modèles aarch64 ; LDFLAGS="-Wl,-z,max-page-size=16384" (pages de 16 Ko, requis sous Android 15+).

2. Exporter ONNX pour le NPU

Ré-exporter à partir du point de contrôle original (PyTorch), pas à partir d'un ONNX fabriqué pour CPU :

  • --dynamic-batch 0 (lot 1), pas de quantification int8 dans l'ONNX (--enable-int8-quantization 0 : quantifiez dans QNN à la place), --use-int32-inputs 1 (pas de tenseurs int64), segment/longueur fixe.
  • Simplifiez (onnxsim) et vérifiez que chaque entrée/sortie a une forme fixe (scripts/pyannote/segmentation/show-onnx.py de sherpa-onnx).
  • Exécutez l'ONNX sur CPU (onnxruntime) par rapport à l'original sur quelques entrées réelles : même sortie (ou même texte).

Exemples : Zipformer2 en streaming (icefall zipformer/export-onnx-streaming.py, voir scripts/zipformer-transducer/x-asr/qnn/export-streaming.sh de sherpa-onnx) ; Whisper (scripts/whisper/qnn/export_onnx.py de sherpa-onnx, encodeur et décodeur en tant que graphes séparés).

3. Convertir, construire, compiler (par graphe, par puce)

Pour chaque graphe ONNX $m (par exemple encodeur, décodeur, joncteur) :

# a) ONNX → graphe C++ QNN + poids. Activations fp16 : --act_bitwidth 16 --bias_bitwidth 32.
#    Pour les poids 8 bits (grands modèles) : ajoutez une quantification avec un --input_list d'entrées réelles.
qnn-onnx-converter --input_network $m.onnx --output_path out/$m.cpp --act_bitwidth 16 --bias_bitwidth 32

# b) Configuration HTP pour la puce (generate_config.py de sherpa-onnx : soc_id, dsp_arch, vtcm).
python3 generate_config.py --soc SM8750 --graph-name $m --output-dir out/config-$m --qnn-sdk-root $QNN_SDK_ROOT

# c) Bibliothèques de modèles : x86_64 (pour compiler le contexte sur l'hôte) et aarch64-android.
python3 $QNN_SDK_ROOT/bin/x86_64-linux-clang/qnn-model-lib-generator -c out/$m.cpp -b out/$m.bin -o out/model_libs

# d) Binaire de contexte pour la puce : ce que le téléphone charge.
$QNN_SDK_ROOT/bin/x86_64-linux-clang/qnn-context-binary-generator \
  --backend $QNN_SDK_ROOT/lib/x86_64-linux-clang/libQnnHtp.so \
  --model out/model_libs/x86_64-linux-clang/lib$m.so \
  --output_dir out/binary --binary_file $m \
  --config_file out/config-$m/htp_backend_extensions.json

Conservez les journaux du convertisseur et du générateur : les opérations non prises en charge et les avertissements de quantification y apparaissent.

4. Exécution sur le téléphone

  • Expédiez les bibliothèques d'exécution QNN pour Android aarch64 (libQnnHtp.so, libQnnHtpV79Skel.so et stub pour la puce, libQnnSystem.so ; sherpa-onnx les publie sous forme de qnn-libs-<version>.tar.bz2). La bibliothèque Skel s'exécute sur le DSP : OnlineRecognizer.prependAdspLibraryPath(dir) de sherpa-onnx (ou ADSP_LIBRARY_PATH) doit pointer vers son dossier.
  • sherpa-onnx : QnnConfig(backendLib = ".../libQnnHtp.so", contextBinary = ".../$m.bin", systemLib = ".../libQnnSystem.so") sur la configuration du modèle (par exemple OnlineTransducerModelConfig(..., qnnConfig = ...)), provider = "qnn".
  • Alternative ONNX Runtime : le fournisseur d'exécution QNN (QNNExecutionProvider, backend_path=libQnnHtp.so), qui peut également mettre en cache lui-même les binaires de contexte.
  • Choisissez par appareil : si ro.soc.model correspond à une puce compilée et que les bibliothèques se chargent, NPU ; sinon CPU. Ne présumez jamais.

5. Vérification

  1. Même sortie : exécutez la même entrée/audio sur CPU et NPU ; pour la parole, le taux d'erreur sur 10+ extraits réels. Les activations 16 bits sont généralement sans perte ; les poids 8 bits doivent être vérifiés.
  2. Vitesse : facteur temps réel et latence de la première sortie sur le téléphone.
  3. Puissance : utilisation CPU du processus et baisse de la batterie sur une tâche fixe de 30 à 60 minutes, CPU vs NPU, écran éteint, même luminosité et réseau. Répétez ; l'état thermique modifie les résultats.
  4. Enregistrez les résultats (puce, version du SDK, commit du modèle, quantification, chiffres) à côté du modèle.

Pièges

  • Formes dynamiques, entrées int64, opérations non prises en charge (certaines opérations d'attention ou de contrôle de flux) : corrigez-les dans l'export (encapsulez le modèle, corrigez les longueurs), pas dans QNN.
  • Les binaires de contexte sont liés à la famille de versions du SDK : construisez avec la même version du SDK QAIRT que les bibliothèques d'exécution.
  • Un modèle qui revient silencieusement au CPU pour certaines opérations perd la majeure partie du gain : vérifiez les journaux pour les partitions CPU.
  • Licences : celle du modèle (redistribuer une forme compilée revient à distribuer le modèle) et celle du SDK QNN.
  • Alternative sans Linux local : Qualcomm AI Hub (compilation et profilage dans le cloud sur des appareils réels ; nécessite un compte ; vérifiez ses conditions pour votre application).

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills associés