qnn-npu-compile

Compile modelos ONNX para execução em NPUs Qualcomm (Hexagon HTP, via SDK QNN / QAIRT) em aplicativos Android, e executá-los com sherpa-onnx ou ONNX Runtime. Use quando o usuário quiser um modelo (fala, áudio, visão, pequeno transformer) em um NPU de celular Snapdragon para velocidade ou bateria, ou perguntar como compilar/converter/quantizar um modelo para QNN, HTP, Hexagon, Snapdragon ou 'o NPU'.

O que é qnn-npu-compile?

Este guia detalha o processo de compilação de modelos ONNX para execução em Unidades de Processamento Neural (NPU) da Qualcomm, especificamente nos núcleos Hexagon HTP, utilizando o SDK QNN/QAIRT. É útil para desenvolvedores de aplicativos Android que desejam acelerar modelos de fala, áudio, visão ou transformers pequenos em celulares Snapdragon, obtendo maior velocidade ou economia de bateria. O fluxo de trabalho segue as práticas adotadas pelo sherpa-onnx e cobre desde a avaliação da viabilidade do NPU até a verificação de desempenho no dispositivo final. Ele suporta as plataformas Linux x86-64 (para as ferramentas) e Android (para execução), com alternativas via Docker ou Qualcomm AI Hub. A metodologia envolve exportar modelos do PyTorch para ONNX com shapes estáticos, converter para código C++ do QNN, gerar bibliotecas e binários de contexto específicos por chip (como Snapdragon 8 Elite), e integrar via sherpa-onnx ou ONNX Runtime. O guia também alerta sobre limitações importantes, como dependência de shapes fixos, incompatibilidade entre famílias de chips e a necessidade de fallback para CPU.

Funciona com~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 Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Compilando modelos para NPUs Qualcomm (QNN HTP)

Um procedimento, não um script: cada modelo precisa de sua própria exportação e verificações. Ele segue como o sherpa-onnx compila seus modelos NPU publicados (k2-fsa/sherpa-onnx, .github/workflows/export-*-qnn.yaml, .github/scripts/export-qnn/, scripts/qnn/), que são conhecidos por funcionar. Leia o workflow correspondente lá antes de começar; adapte, não invente.

0. Decida se o NPU vale a pena

  • Meça primeiro no telefone alvo: % de CPU e tempo real do modelo enquanto executa atualmente (ticks /proc/<pid>/stat a cada 2 s, top -H -p <pid> por thread), e bateria durante uma tarefa fixa se o objetivo for economia de energia. O NPU compensa para trabalhos grandes e contínuos de matriz (codificadores, passes longos em lote); para um modelo pequeno em 1–2 threads o ganho pode ser pequeno.
  • Apenas shapes estáticos. O NPU executa gráficos de tamanho fixo: batch fixo (1), comprimento de entrada fixo (tamanho do chunk, ou 30 s para o codificador do Whisper). Um modelo cujo comprimento de entrada varia precisa de um chunk fixo, padding ou vários gráficos.
  • Uma família de chip por build. Um binário de contexto é compilado para uma arquitetura Hexagon (v73, v75, v79, v81…) e funciona apenas nela; outros telefones (e Exynos / Tensor / MediaTek) mantêm o caminho da CPU. Planeje sempre um fallback para CPU.
  • Limites de tamanho. Modelos muito grandes (acima de ~1B parâmetros em fp16) podem não caber em um contexto: eles precisam de pesos de 8 bits (w8a16) e/ou divisão em vários gráficos. As próprias builds do Whisper do sherpa-onnx param no medium (769M).

Chips (do scripts/qnn/device_info.py do sherpa-onnx): SM8550 Snapdragon 8 Gen 2 (v73), SM8650 8 Gen 3 (v75), SM8750 8 Elite (v79), SM8850 (v81), QCS9100 (v73). SM8350 (v68) não tem fp16: pule-o. Descubra o chip de um telefone com adb shell getprop ro.soc.model.

1. Ferramentas (apenas Linux x86-64)

O SDK QNN (QAIRT) roda em Linux x86-64 (ou Windows), não em macOS. Em um Mac: VM/cloud box Linux, ou Docker com --platform linux/amd64 (lento: emulado). O sherpa-onnx constrói uma imagem Docker para isso (Dockerfile-2.40, qnn-sdk-docker-build.yaml).

  • QAIRT SDK, ex. 2.40.0.251030: da Qualcomm (conta gratuita), ou a cópia que o CI do sherpa-onnx baixa (huggingface.co/csukuangfj/qnn-toolkit, v2.40.0.251030.zip). Verifique se a licença permite seu uso.
  • source qairt/<version>/bin/envsetup.sh (define QNN_SDK_ROOT), então sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh e python3 ${QNN_SDK_ROOT}/bin/check-python-dependency.
  • Ambiente virtual Python 3.10 com numpy<2 onnx==1.17.0 onnxruntime onnxsim mais as dependências de exportação do próprio modelo.
  • Android NDK no PATH (ndk-build), para as bibliotecas de modelo aarch64; LDFLAGS="-Wl,-z,max-page-size=16384" (páginas de 16 KB, exigido no Android 15+).

2. Exporte ONNX para o NPU

Re-exporte a partir do checkpoint original (PyTorch), não de um ONNX feito para CPUs:

  • --dynamic-batch 0 (batch 1), sem quantização int8 no ONNX (--enable-int8-quantization 0: quantize no QNN), --use-int32-inputs 1 (sem tensores int64), chunk/comprimento fixo.
  • Simplifique (onnxsim) e verifique se cada entrada/saída tem um shape fixo (scripts/pyannote/segmentation/show-onnx.py do sherpa-onnx).
  • Execute o ONNX na CPU (onnxruntime) contra o original em algumas entradas reais: mesma saída (ou mesmo texto).

Exemplos: Zipformer2 streaming (zipformer/export-onnx-streaming.py do icefall, veja scripts/zipformer-transducer/x-asr/qnn/export-streaming.sh do sherpa-onnx); Whisper (scripts/whisper/qnn/export_onnx.py do sherpa-onnx, codificador e decodificador como gráficos separados).

3. Converta, construa, compile (por gráfico, por chip)

Para cada gráfico ONNX $m (ex. encoder, decoder, joiner):

# a) ONNX → gráfico C++ + pesos do QNN. Ativações fp16: --act_bitwidth 16 --bias_bitwidth 32.
#    Para pesos de 8 bits (modelos grandes): adicione quantização com um --input_list de entradas reais.
qnn-onnx-converter --input_network $m.onnx --output_path out/$m.cpp --act_bitwidth 16 --bias_bitwidth 32

# b) Configuração HTP para o chip (scripts/qnn/generate_config.py do 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) Bibliotecas de modelo: x86_64 (para compilar o contexto no host) e 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) Binário de contexto para o chip: o que o telefone carrega.
$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

Mantenha os logs do conversor e do gerador: operações não suportadas e avisos de quantização aparecem lá.

4. Execute no telefone

  • Envie as bibliotecas de runtime QNN para Android aarch64 (libQnnHtp.so, libQnnHtpV79Skel.so e stub para o chip, libQnnSystem.so; o sherpa-onnx as publica como qnn-libs-<version>.tar.bz2). A biblioteca Skel roda no DSP: o OnlineRecognizer.prependAdspLibraryPath(dir) do sherpa-onnx (ou ADSP_LIBRARY_PATH) deve apontar para sua pasta.
  • sherpa-onnx: QnnConfig(backendLib = ".../libQnnHtp.so", contextBinary = ".../$m.bin", systemLib = ".../libQnnSystem.so") na configuração do modelo (ex. OnlineTransducerModelConfig(..., qnnConfig = ...)), provider = "qnn".
  • Alternativa ONNX Runtime: o provedor de execução QNN (QNNExecutionProvider, backend_path=libQnnHtp.so), que também pode armazenar em cache os binários de contexto.
  • Escolha por dispositivo: se ro.soc.model corresponder a um chip compilado e as bibliotecas carregarem, NPU; senão, CPU. Nunca presuma.

5. Verifique

  1. Mesma saída: execute a mesma entrada/áudio na CPU e NPU; para fala, taxa de erro de palavras em 10+ clipes reais. Ativações de 16 bits geralmente são sem perdas; pesos de 8 bits devem ser verificados.
  2. Velocidade: fator de tempo real e latência da primeira saída no telefone.
  3. Energia: % de CPU do processo e queda de bateria durante uma tarefa fixa de 30–60 min, CPU vs NPU, tela desligada, mesmo brilho e rede. Repita; o estado térmico altera os resultados.
  4. Registre resultados (chip, versão do SDK, commit do modelo, quantização, números) junto ao modelo.

Problemas comuns

  • Shapes dinâmicos, entradas int64, operações não suportadas (algumas operações de atenção ou controle de fluxo): corrija na exportação (enrole o modelo, corrija comprimentos), não no QNN.
  • Binários de contexto estão vinculados à família da versão do SDK: construa com a mesma versão do QAIRT que as bibliotecas de runtime.
  • Um modelo que cai em CPU para algumas operações silenciosamente perde a maior parte do ganho: verifique os logs para partições de CPU.
  • Licenças: a do modelo (redistribuir uma forma compilada é distribuir o modelo) e a do SDK QNN.
  • Rota alternativa sem Linux local: Qualcomm AI Hub (compilação em nuvem e perfilamento em dispositivos reais; precisa de conta; verifique os termos para seu aplicativo).

Individual skills in this repo

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

Habilidades Relacionadas