CommunityProgramación y desarrollogithub.com

qnn-npu-compile

Compila modelos ONNX para ejecutarlos en NPUs de Qualcomm (Hexagon HTP, mediante el SDK QNN / QAIRT) para aplicaciones Android, y ejecútalos con sherpa-onnx o ONNX Runtime. Úsalo cuando el usuario quiera un modelo (voz, audio, visión, transformer pequeño) en la NPU de un teléfono Snapdragon para velocidad o batería, o pregunte cómo compilar/convertir/cuantizar un modelo para QNN, HTP, Hexagon, Snapdragon o "la NPU".

¿Qué es qnn-npu-compile?

Esta habilidad guía a los desarrolladores en la compilación de modelos ONNX para ejecutarlos en las Unidades de Procesamiento Neuronal (NPU) de Qualcomm, específicamente los núcleos Hexagon HTP (por ejemplo, en chips Snapdragon 8 Gen 2, 8 Gen 3, 8 Elite). El proceso abarca desde decidir si la NPU es beneficiosa—midiendo el rendimiento actual en la CPU y considerando limitaciones como formas estáticas, una familia de chips por compilación, y límites de tamaño—hasta la exportación del modelo desde su punto de control original (por ejemplo, PyTorch), omitiendo la cuantización int8 para realizarla en QNN. Luego, mediante herramientas del SDK QAIRT en Linux x86-64, se convierte el ONNX a código C++ de QNN, se generan bibliotecas de modelo para el host y Android, y se produce un binario de contexto específico para la arquitectura Hexagon de destino. Finalmente, se detalla cómo ejecutar el modelo en el teléfono con sherpa-onnx u ONNX Runtime, verificando la equivalencia de salidas, velocidad y consumo energético. La habilidad es esencial para desarrolladores de aplicaciones Android que necesitan acelerar modelos de IA (reconocimiento de voz, audio, visión, transformers pequeños) en dispositivos Snapdragon, maximizando el rendimiento y la eficiencia energética.

Compatible con~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 Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Compilando modelos para NPUs de Qualcomm (QNN HTP)

Un procedimiento, no un script: cada modelo necesita su propia exportación y comprobaciones. Sigue cómo sherpa-onnx compila sus modelos NPU publicados (k2-fsa/sherpa-onnx, .github/workflows/export-*-qnn.yaml, .github/scripts/export-qnn/, scripts/qnn/), que se sabe que funcionan. Lee el workflow correspondiente allí antes de empezar; adáptalo, no inventes.

0. Decide si vale la pena la NPU

  • Mide primero en el teléfono objetivo: % de CPU y tiempo real del modelo tal como se ejecuta ahora (tics de /proc/<pid>/stat cada 2 s, top -H -p <pid> por hilo), y batería durante una tarea fija si el objetivo es la energía. La NPU compensa para trabajo grande y constante de matrices (codificadores, pases de lotes largos); para un modelo pequeño en 1-2 hilos la ganancia puede ser pequeña.
  • Solo formas estáticas. La NPU ejecuta grafos de tamaño fijo: lote fijo (1), longitud de entrada fija (tamaño de fragmento, o 30 s para el codificador de Whisper). Un modelo cuya longitud de entrada varía necesita un fragmento fijo, relleno o varios grafos.
  • Una familia de chips por compilación. Un binario de contexto se compila para una arquitectura Hexagon (v73, v75, v79, v81…) y solo se ejecuta allí; otros teléfonos (y Exynos / Tensor / MediaTek) mantienen la ruta de CPU. Siempre planea un fallback a CPU.
  • Límites de tamaño. Los modelos muy grandes (más de ~1B parámetros en fp16) pueden no caber en un contexto: necesitan pesos de 8 bits (w8a16) y/o dividirse en varios grafos. Las propias compilaciones de Whisper de sherpa-onnx se detienen en medium (769M).

Chips (de 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) no tiene fp16: omítelo. Encuentra el chip de un teléfono con adb shell getprop ro.soc.model.

1. Herramientas (solo Linux x86-64)

El SDK de QNN (QAIRT) se ejecuta en Linux x86-64 (o Windows), no en macOS. En una Mac: una VM Linux / caja en la nube, o Docker con --platform linux/amd64 (lento: emulado). sherpa-onnx construye una imagen Docker para ello (Dockerfile-2.40, qnn-sdk-docker-build.yaml).

  • SDK QAIRT, ej. 2.40.0.251030: de Qualcomm (cuenta gratuita), o la copia que descarga el CI de sherpa-onnx (huggingface.co/csukuangfj/qnn-toolkit, v2.40.0.251030.zip). Verifica que su licencia permita tu uso.
  • source qairt/<version>/bin/envsetup.sh (establece QNN_SDK_ROOT), luego sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh y python3 ${QNN_SDK_ROOT}/bin/check-python-dependency.
  • Entorno virtual Python 3.10 con numpy<2 onnx==1.17.0 onnxruntime onnxsim más las dependencias de exportación del modelo.
  • Android NDK en PATH (ndk-build), para las bibliotecas de modelo aarch64; LDFLAGS="-Wl,-z,max-page-size=16384" (páginas de 16 KB, requerido en Android 15+).

2. Exporta ONNX para la NPU

Re-exporta desde el punto de control original (PyTorch), no desde un ONNX hecho para CPU:

  • --dynamic-batch 0 (lote 1), sin cuantización int8 en el ONNX (--enable-int8-quantization 0: cuantiza en QNN en su lugar), --use-int32-inputs 1 (sin tensores int64), fragmento/longitud fijos.
  • Simplifica (onnxsim) y verifica que cada entrada/salida tenga una forma fija (sherpa-onnx scripts/pyannote/segmentation/show-onnx.py).
  • Ejecuta el ONNX en CPU (onnxruntime) contra el original en algunas entradas reales: misma salida (o mismo texto).

Ejemplos: Zipformer2 en streaming (icefall zipformer/export-onnx-streaming.py, ver sherpa-onnx scripts/zipformer-transducer/x-asr/qnn/export-streaming.sh); Whisper (sherpa-onnx scripts/whisper/qnn/export_onnx.py, codificador y decodificador como grafos separados).

3. Convierte, construye, compila (por grafo, por chip)

Para cada grafo ONNX $m (ej. codificador, decodificador, unidor):

# a) ONNX → grafo C++ de QNN + pesos. Activaciones fp16: --act_bitwidth 16 --bias_bitwidth 32.
#    Para pesos de 8 bits (modelos grandes): añade cuantización con una --input_list de entradas reales.
qnn-onnx-converter --input_network $m.onnx --output_path out/$m.cpp --act_bitwidth 16 --bias_bitwidth 32

# b) Configuración HTP para el 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) Bibliotecas de modelo: x86_64 (para compilar el contexto en el host) y 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) Binario de contexto para el chip: lo que carga el teléfono.
$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

Guarda los registros del convertidor y generador: las operaciones no soportadas y las advertencias de cuantización aparecen allí.

4. Ejecuta en el teléfono

  • Entrega las bibliotecas de ejecución de QNN para Android aarch64 (libQnnHtp.so, libQnnHtpV79Skel.so y el stub para el chip, libQnnSystem.so; sherpa-onnx las publica como qnn-libs-<version>.tar.bz2). La biblioteca Skel se ejecuta en el DSP: OnlineRecognizer.prependAdspLibraryPath(dir) de sherpa-onnx (o ADSP_LIBRARY_PATH) debe apuntar a su carpeta.
  • sherpa-onnx: QnnConfig(backendLib = ".../libQnnHtp.so", contextBinary = ".../$m.bin", systemLib = ".../libQnnSystem.so") en la configuración del modelo (ej. OnlineTransducerModelConfig(..., qnnConfig = ...)), provider = "qnn".
  • Alternativa con ONNX Runtime: el proveedor de ejecución QNN (QNNExecutionProvider, backend_path=libQnnHtp.so), que también puede almacenar en caché binarios de contexto por sí mismo.
  • Elige por dispositivo: si ro.soc.model coincide con un chip compilado y las bibliotecas cargan, NPU; si no, CPU. Nunca asumas.

5. Verifica

  1. Misma salida: ejecuta la misma entrada/audio en CPU y NPU; para voz, tasa de error de palabras en 10+ clips reales. Las activaciones de 16 bits suelen ser sin pérdida; los pesos de 8 bits deben comprobarse.
  2. Velocidad: factor de tiempo real y latencia de primera salida en el teléfono.
  3. Energía: % de CPU del proceso, y caída de batería durante una tarea fija de 30-60 min, CPU vs NPU, pantalla apagada, mismo brillo y red. Repite; el estado térmico cambia los resultados.
  4. Registra los resultados (chip, versión del SDK, commit del modelo, cuantización, números) junto al modelo.

Dificultades

  • Formas dinámicas, entradas int64, operaciones no soportadas (algunas operaciones de atención o control de flujo): corrígelas en la exportación (envuelve el modelo, corrige longitudes), no en QNN.
  • Los binarios de contexto están vinculados a la familia de versiones del SDK: compila con la misma versión de QAIRT que las bibliotecas de ejecución.
  • Un modelo que cae a CPU para algunas operaciones pierde silenciosamente la mayor parte de la ganancia: revisa los registros en busca de particiones de CPU.
  • Licencias: la del modelo (redistribuir una forma compilada es distribuir el modelo) y la del SDK de QNN.
  • Ruta alternativa sin Linux local: Qualcomm AI Hub (compilación y perfilado en la nube en dispositivos reales; necesita una cuenta; revisa sus términos para tu aplicación).

Individual skills in this repo

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

Skills relacionados