CommunityCodierung & Entwicklunggithub.com

qnn-npu-compile

Kompiliere ONNX-Modelle für die Ausführung auf Qualcomm NPUs (Hexagon HTP, via QNN / QAIRT SDK) für Android-Apps und führe sie mit sherpa-onnx oder ONNX Runtime aus. Verwende es, wenn der Benutzer ein Modell (Sprache, Audio, Vision, kleiner Transformer) auf dem NPU eines Snapdragon-Handys für Geschwindigkeit oder Akku haben möchte, oder fragt, wie man ein Modell für QNN, HTP, Hexagon, Snapdragon oder 'die NPU' kompiliert/konvertiert/quantisiert.

Was ist qnn-npu-compile?

Diese Anleitung beschreibt den schrittweisen Prozess zum Kompilieren von ONNX-Modellen für die Ausführung auf Qualcomm NPUs (Hexagon HTP) über das QNN/QAIRT SDK. Sie ist speziell für Android-Apps mit sherpa-onnx oder ONNX Runtime als Inferenz-Frameworks gedacht. Der Nutzen liegt in erheblich höherer Geschwindigkeit und geringerem Stromverbrauch für rechenintensive Modelle (Sprache, Audio, Vision, kleine Transformer) auf Snapdragon-Smartphones. Die Unterstützung umfasst Snapdragon 8 Gen 2 bis 8 Elite und andere QCOM-Chips. Der Workflow erfordert Linux x86-64, das QAIRT SDK, Python 3.10 und das Android NDK. Wichtige Schritte sind: ONNX-Export mit statischen Shapes (kein dynamisches Batching), Konvertierung in QNN-C++-Graphen, Erstellung von Modellbibliotheken für x86_64 und aarch64-android, Generierung von Kontext-Binaries für den Ziel-Chip und schließlich die Ausführung auf dem Gerät mit entsprechenden Laufzeitbibliotheken. Die Anleitung betont die Notwendigkeit von CPU-Fallbacks, die Überprüfung auf Ausgabeidentität zwischen CPU und NPU, Geschwindigkeits- und Energietests sowie die Beachtung von Lizenzbedingungen.

Funktioniert mit~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 Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Modelle für Qualcomm NPUs kompilieren (QNN HTP)

Eine Anleitung, kein Skript: Jedes Modell benötigt seinen eigenen Export und seine eigenen Überprüfungen. Sie folgt der Vorgehensweise, die sherpa-onnx für seine veröffentlichten NPU-Modelle verwendet (k2-fsa/sherpa-onnx, .github/workflows/export-*-qnn.yaml, .github/scripts/export-qnn/, scripts/qnn/), welche bekanntermaßen funktionieren. Lies den passenden Workflow dort, bevor du beginnst; passe an, erfinde nicht.

0. Entscheide, ob sich die NPU lohnt

  • Messe zuerst auf dem Ziel-Handy: CPU-Auslastung und Wandzeit des Modells im aktuellen Betrieb (/proc/<pid>/stat Ticks alle 2 s, top -H -p <pid> pro Thread) und Akkuverbrauch über eine festgelegte Aufgabe, wenn Energie das Ziel ist. Die NPU lohnt sich für große, stabile Matrix-Arbeiten (Encoder, lange Batch-Durchläufe); für ein kleines Modell auf 1–2 Threads kann der Gewinn gering sein.
  • Nur statische Shapes. Die NPU führt Graphen mit fester Größe aus: feste Batch-Größe (1), feste Eingabelänge (Chunk-Größe oder 30 s für Whisper-Encoder). Ein Modell mit variabler Eingabelänge benötigt einen festen Chunk, Padding oder mehrere Graphen.
  • Eine Chip-Familie pro Build. Ein Kontext-Binary wird für eine Hexagon-Architektur (v73, v75, v79, v81…) kompiliert und läuft nur dort; andere Handys (und Exynos / Tensor / MediaTek) behalten den CPU-Pfad. Plane immer einen CPU-Fallback ein.
  • Größenbeschränkungen. Sehr große Modelle (über etwa 1B Parameter in fp16) passen möglicherweise nicht in einen Kontext: Sie benötigen 8-Bit-Gewichte (w8a16) und/oder Aufteilung in mehrere Graphen. sherpa-onnxs eigene Whisper-Builds enden bei medium (769M).

Chips (aus sherpa-onnx scripts/qnn/device_info.py): SM8550 Snapdragon 8 Gen 2 (v73), SM8650 8 Gen 3 (v75), SM8750 8 Elite (v79), SM8850 (v81), QCS9100 (v73). SM8350 (v68) hat kein fp16: überspringe es. Finde den Chip eines Handys mit adb shell getprop ro.soc.model.

1. Werkzeuge (nur Linux x86-64)

Das QNN SDK (QAIRT) läuft auf Linux x86-64 (oder Windows), nicht auf macOS. Auf einem Mac: Eine Linux-VM/Cloud-Box oder Docker mit --platform linux/amd64 (langsam: emuliert). sherpa-onnx baut ein Docker-Image dafür (Dockerfile-2.40, qnn-sdk-docker-build.yaml).

  • QAIRT SDK, z. B. 2.40.0.251030: von Qualcomm (kostenloses Konto) oder die Kopie, die sherpa-onnxs CI herunterlädt (huggingface.co/csukuangfj/qnn-toolkit, v2.40.0.251030.zip). Überprüfe, ob seine Lizenz deine Nutzung erlaubt.
  • source qairt/<version>/bin/envsetup.sh (setzt QNN_SDK_ROOT), dann sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh und python3 ${QNN_SDK_ROOT}/bin/check-python-dependency.
  • Python 3.10 venv mit numpy<2 onnx==1.17.0 onnxruntime onnxsim plus die eigenen Export-Abhängigkeiten des Modells.
  • Android NDK im PATH (ndk-build), für die aarch64-Modellbibliotheken; LDFLAGS="-Wl,-z,max-page-size=16384" (16-KB-Seiten, erforderlich ab Android 15+).

2. Exportiere ONNX für die NPU

Exportiere neu aus dem originalen Checkpoint (PyTorch), nicht aus einem für CPUs erstellten ONNX:

  • --dynamic-batch 0 (Batch-Größe 1), keine int8-Quantisierung im ONNX (--enable-int8-quantization 0: quantisiere in QNN stattdessen), --use-int32-inputs 1 (keine int64-Tensoren), fester Chunk/Länge.
  • Vereinfache (onnxsim) und überprüfe, ob jede Ein-/Ausgabe eine feste Form hat (sherpa-onnx scripts/pyannote/segmentation/show-onnx.py).
  • Führe das ONNX auf der CPU (onnxruntime) gegen das Original mit einigen realen Eingaben aus: gleiche Ausgabe (oder gleicher Text).

Beispiele: Streaming Zipformer2 (icefall zipformer/export-onnx-streaming.py, siehe sherpa-onnx scripts/zipformer-transducer/x-asr/qnn/export-streaming.sh); Whisper (sherpa-onnx scripts/whisper/qnn/export_onnx.py, Encoder und Decoder als separate Graphen).

3. Konvertieren, bauen, kompilieren (pro Graph, pro Chip)

Für jeden ONNX-Graphen $m (z. B. encoder, decoder, joiner):

# a) ONNX → QNN C++ Graph + Gewichte. fp16-Aktivierungen: --act_bitwidth 16 --bias_bitwidth 32.
#    Für 8-Bit-Gewichte (große Modelle): füge Quantisierung mit einer Kalibrierung --input_list mit realen Eingaben hinzu.
qnn-onnx-converter --input_network $m.onnx --output_path out/$m.cpp --act_bitwidth 16 --bias_bitwidth 32

# b) HTP-Konfiguration für den Chip (sherpa-onnx scripts/qnn/generate_config.py: 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) Modellbibliotheken: x86_64 (um den Kontext auf dem Host zu kompilieren) und 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) Kontext-Binary für den Chip: das, was das Handy lädt.
$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

Behalte die Konverter- und Generator-Logs: Nicht unterstützte Operationen und Quantisierungswarnungen werden dort angezeigt.

4. Auf dem Handy ausführen

  • Übertrage die QNN-Laufzeitbibliotheken für Android aarch64 (libQnnHtp.so, libQnnHtpV79Skel.so und Stub für den Chip, libQnnSystem.so; sherpa-onnx veröffentlicht sie als qnn-libs-<version>.tar.bz2). Die Skel-Bibliothek läuft auf dem DSP: sherpa-onnxs OnlineRecognizer.prependAdspLibraryPath(dir) (oder ADSP_LIBRARY_PATH) muss auf ihren Ordner zeigen.
  • sherpa-onnx: QnnConfig(backendLib = ".../libQnnHtp.so", contextBinary = ".../$m.bin", systemLib = ".../libQnnSystem.so") in der Modellkonfiguration (z. B. OnlineTransducerModelConfig(..., qnnConfig = ...)), provider = "qnn".
  • ONNX Runtime Alternative: der QNN Execution Provider (QNNExecutionProvider, backend_path=libQnnHtp.so), welcher auch selbst Kontext-Binaries cachen kann.
  • Wähle pro Gerät: Wenn ro.soc.model zu einem kompilierten Chip passt und die Bibliotheken laden, NPU; sonst CPU. Gehe niemals davon aus.

5. Überprüfen

  1. Gleiche Ausgabe: Führe denselben Audio-/Eingabedatensatz auf CPU und NPU aus; für Sprache, Wortfehlerrate auf 10+ echten Clips. 16-Bit- Aktivierungen sind normalerweise verlustfrei; 8-Bit-Gewichte müssen überprüft werden.
  2. Geschwindigkeit: Echtzeitfaktor und Latenz der ersten Ausgabe auf dem Handy.
  3. Energie: CPU-Auslastung des Prozesses und Akkuverbrauch über eine feste Aufgabe von 30–60 min, CPU vs. NPU, Bildschirm aus, gleiche Helligkeit und Netzwerk. Wiederhole; der thermische Zustand beeinflusst die Ergebnisse.
  4. Zeichne Ergebnisse (Chip, SDK-Version, Modell-Commit, Quantisierung, Zahlen) neben dem Modell auf.

Fallstricke

  • Dynamische Shapes, int64-Eingaben, nicht unterstützte Operationen (einige Attention- oder Kontrollfluss-Ops): Behebe sie im Export (umwickele das Modell, korrigiere Längen), nicht in QNN.
  • Kontext-Binaries sind an die SDK-Versionsfamilie gebunden: Baue mit derselben QAIRT-Version wie die Laufzeitbibliotheken.
  • Ein Modell, das für einige Operationen auf die CPU zurückfällt, verliert still den Großteil des Gewinns: Überprüfe die Logs auf CPU-Partitionen.
  • Lizenzen: Die des Modells (das Verteilen einer kompilierten Form ist das Verteilen des Modells) und die des QNN SDKs.
  • Alternativer Weg ohne lokales Linux: Qualcomm AI Hub (Cloud-Kompilierung und Profilerstellung auf echten Geräten; benötigt ein Konto; überprüfe seine Bedingungen für deine App).

Individual skills in this repo

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

Verwandte Skills