O LiteRT oferece uma interface unificada para usar unidades de processamento neural (NPUs) sem forçar você a navegar por compiladores, tempos de execução ou dependências de biblioteca específicos do fornecedor. Usar o LiteRT para aceleração de NPU aumenta o desempenho da inferência em tempo real e de modelos grandes e minimiza as cópias de memória com o uso de buffers de hardware de cópia zero.
Começar
Para começar, consulte o guia de visão geral da NPU:
- Para modelos de ML clássicos, consulte as seções a seguir para etapas de conversão, compilação e implantação.
- Para modelos de linguagem grandes (LLMs), use a estrutura LiteRT-LM:
Para exemplos de implementações do LiteRT com suporte a NPU, consulte os seguintes aplicativos de demonstração:
Fornecedores de NPU
O LiteRT oferece suporte à aceleração de NPU com os seguintes fornecedores:
Qualcomm AI Engine Direct
- Os caminhos de execução de compilação AOT e no dispositivo são compatíveis com a API Compiled Model.
- Consulte Qualcomm AI Engine Direct para mais detalhes sobre a configuração.
MediaTek NeuroPilot
- Os caminhos de execução AOT e JIT são compatíveis com a API Compiled Model.
- Consulte MediaTek NeuroPilot para mais detalhes sobre a configuração.
Converter e compilar modelos para NPU
Para usar a aceleração de NPU com o LiteRT, os modelos precisam ser convertidos para o formato de arquivo LiteRT e compilados para uso de NPU no dispositivo. É possível usar o compilador LiteRT AOT (antecipado) para compilar modelos em um pacote de IA, que agrupa seus modelos compilados com configurações de segmentação por dispositivo. Isso verifica se os modelos são veiculados corretamente aos dispositivos, dependendo de estarem equipados ou otimizados para SoCs específicos.
Depois de converter e compilar os modelos, use o Play para IA no dispositivo (PODAI) para fazer upload de modelos para o Google Play e entregar modelos aos dispositivos usando a estrutura de IA sob demanda.
Use o notebook de compilação AOT do LiteRT para um guia completo sobre como converter e compilar modelos para NPU.
[Somente AOT] Implantar com o pacote de IA do Google Play
Depois de converter o modelo e compilar um pacote de IA, siga estas etapas para implantar o pacote de IA com o Google Play.
Importar pacotes de IA para o projeto do Gradle
Copie os pacotes de IA para o diretório raiz do projeto Gradle. Exemplo:
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Adicione cada pacote de IA à configuração de build do Gradle:
// my_app/ai_packs/my_model/build.gradle.kts
plugins { id("com.android.ai-pack") }
aiPack {
packName = "my_model" // ai pack dir name
dynamicDelivery { deliveryType = "on-demand" }
}
// Add another build.gradle.kts for my_model_mtk/ as well
Adicionar bibliotecas de ambiente de execução da NPU ao projeto
Faça o download de litert_npu_runtime_libraries.zip para AOT ou litert_npu_runtime_libraries_jit.zip para JIT e descompacte no diretório raiz do projeto:
my_app/
...
litert_npu_runtime_libraries/
mediatek_runtime/...
qualcomm_runtime_v69/...
qualcomm_runtime_v73/...
qualcomm_runtime_v75/...
qualcomm_runtime_v79/...
qualcomm_runtime_v81/...
fetch_qualcomm_library.sh
Execute o script para fazer o download das bibliotecas de suporte da NPU. Por exemplo, execute o seguinte para NPUs da Qualcomm:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Adicionar pacotes de IA e bibliotecas de tempo de execução da NPU à configuração do Gradle
Copie device_targeting_configuration.xml dos pacotes de IA gerados para o
diretório do módulo principal do app. Em seguida, atualize settings.gradle.kts:
// my_app/setting.gradle.kts
...
// [AOT only]
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
// NPU runtime libraries
include(":litert_npu_runtime_libraries:runtime_strings")
include(":litert_npu_runtime_libraries:mediatek_runtime")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v69")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v73")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v75")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v79")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v81")
Atualize build.gradle.kts:
// my_app/build.gradle.kts
android {
...
defaultConfig {
...
// API level 31+ is required for NPU support.
minSdk = 31
// NPU only supports arm64-v8a
ndk { abiFilters.add("arm64-v8a") }
// Needed for Qualcomm NPU runtime libraries
packaging { jniLibs { useLegacyPackaging = true } }
}
// Device targeting
bundle {
deviceTargetingConfig = file("device_targeting_configuration.xml")
deviceGroup {
enableSplit = true // split bundle by #group
defaultGroup = "other" // group used for standalone APKs
}
}
// [AOT Only]
// AI Packs
assetPacks.add(":ai_packs:my_model")
assetPacks.add(":ai_packs:my_model_mtk")
// NPU runtime libraries
dynamicFeatures.add(":litert_npu_runtime_libraries:mediatek_runtime")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v69")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v73")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v75")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v79")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v81")
}
dependencies {
// Dependencies for strings used in the runtime library modules.
implementation(project(":litert_npu_runtime_libraries:runtime_strings"))
...
}
[Somente AOT] Usar a implantação sob demanda
Com o recurso Android AI Pack configurado no arquivo build.gradle.kts,
verifique os recursos do dispositivo e use a NPU em dispositivos compatíveis, usando GPU e CPU
como substitutos:
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val modelProvider = AiPackModelProvider(
context, "my_model", "model/my_model.tflite") {
if (NpuCompatibilityChecker.Qualcomm.isDeviceSupported())
setOf(Accelerator.NPU) else setOf(Accelerator.CPU, Accelerator.GPU)
}
val mtkModelProvider = AiPackModelProvider(
context, "my_model_mtk", "model/my_model_mtk.tflite") {
if (NpuCompatibilityChecker.Mediatek.isDeviceSupported())
setOf(Accelerator.NPU) else setOf()
}
val modelSelector = ModelSelector(modelProvider, mtkModelProvider)
val model = modelSelector.selectModel(env)
val compiledModel = CompiledModel.create(
model.getPath(),
CompiledModel.Options(model.getCompatibleAccelerators()),
env,
)
Criar CompiledModel para o modo JIT
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val compiledModel = CompiledModel.create(
"model/my_model.tflite",
CompiledModel.Options(Accelerator.NPU),
env,
)
Inferência na NPU usando o LiteRT em Kotlin
Para começar a usar o acelerador de NPU, transmita o parâmetro de NPU ao criar o modelo compilado (CompiledModel).
O snippet de código a seguir mostra uma implementação básica de todo o processo em Kotlin:
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()
inputBuffers[0].writeFloat(FloatArray(data_size) { data_value })
model.run(inputBuffers, outputBuffers)
val outputFloatArray = outputBuffers[0].readFloat()
inputBuffers.forEach { it.close() }
outputBuffers.forEach { it.close() }
model.close()
Inferência em NPU usando LiteRT em C++
Dependências do build
Os usuários de C++ precisam criar as dependências do aplicativo com aceleração de NPU do LiteRT. A regra cc_binary que empacota a lógica principal do aplicativo
(por exemplo, main.cc) requer os seguintes componentes de tempo de execução:
- Biblioteca compartilhada da API LiteRT C: o atributo
dataprecisa incluir a biblioteca compartilhada da API LiteRT C (//litert/c:litert_runtime_c_api_shared_lib) e o objeto compartilhado de envio específico do fornecedor para a NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so). - Bibliotecas de back-end específicas da NPU: por exemplo, as bibliotecas Qualcomm AI RT (QAIRT) para o host Android (como
libQnnHtp.so,libQnnHtpPrepare.so) e a biblioteca Hexagon DSP correspondente (libQnnHtpV79Skel.so). Isso garante que o tempo de execução do LiteRT possa descarregar computações para a NPU. - Dependências de atributos: o atributo
depsvincula dependências essenciais de tempo de compilação, como o buffer de tensor do LiteRT (//litert/cc:litert_tensor_buffer) e a API para a camada de envio da NPU (//litert/vendors/qualcomm/dispatch:dispatch_api). Isso permite que o código do aplicativo interaja com a NPU usando o LiteRT. - Arquivos de modelo e outros recursos: incluídos pelo atributo
data.
Essa configuração permite que o binário compilado carregue e use dinamicamente a NPU para inferência acelerada de machine learning.
Configurar um ambiente de NPU
Alguns back-ends de NPU exigem dependências ou bibliotecas de tempo de execução. Ao usar a API de modelo compilado, o LiteRT organiza esses requisitos em um objeto Environment.
Use o código a seguir para encontrar as bibliotecas ou drivers de NPU adequados:
// Provide a dispatch library directory (following is a hypothetical path) for the NPU
std::vector<Environment::Option> environment_options = {
{
Environment::OptionTag::DispatchLibraryDir,
"/usr/lib64/npu_dispatch/"
}
};
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create(absl::MakeConstSpan(environment_options)));
Integração do ambiente de execução
O snippet de código a seguir mostra uma implementação básica de todo o processo em C++:
// 1. Load the model that has NPU-compatible ops
LITERT_ASSIGN_OR_RETURN(auto model, Model::Load("mymodel_npu.tflite"));
// 2. Create a compiled model with NPU acceleration
// See following section on how to set up NPU environment
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorNpu));
// 3. Allocate I/O buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// 4. Fill model inputs (CPU array -> NPU buffers)
float input_data[] = { /* your input data */ };
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, /*size*/));
// 5. Run inference
compiled_model.Run(input_buffers, output_buffers);
// 6. Access model output
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
Cópia zero com aceleração de NPU
O uso de cópia zero permite que uma NPU acesse dados diretamente na própria memória sem que a CPU precise copiar esses dados explicitamente. Ao não copiar dados para e da memória da CPU, a cópia zero pode reduzir significativamente a latência de ponta a ponta.
O código a seguir é um exemplo de implementação de NPU de cópia zero com
AHardwareBuffer, transmitindo dados diretamente para a NPU. Essa implementação evita viagens de ida e volta caras para a memória da CPU, reduzindo significativamente a sobrecarga de inferência.
// Suppose you have AHardwareBuffer* ahw_buffer
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_tensor"));
LITERT_ASSIGN_OR_RETURN(auto npu_input_buffer, TensorBuffer::CreateFromAhwb(
env,
tensor_type,
ahw_buffer,
/* offset = */ 0
));
std::vector<TensorBuffer> input_buffers{npu_input_buffer};
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// Execute the model
compiled_model.Run(input_buffers, output_buffers);
// Retrieve the output (possibly also an AHWB or other specialized buffer)
auto ahwb_output = output_buffers[0].GetAhwb();
Encadeie várias inferências de NPU
Para pipelines complexos, é possível encadear várias inferências de NPU. Como cada etapa usa um buffer compatível com acelerador, seu pipeline permanece principalmente na memória gerenciada pela NPU:
// compiled_model1 outputs into an AHWB
compiled_model1.Run(input_buffers, intermediate_buffers);
// compiled_model2 consumes that same AHWB
compiled_model2.Run(intermediate_buffers, final_outputs);
Armazenamento em cache de compilação just-in-time da NPU
O LiteRT oferece suporte à compilação just-in-time (JIT) de NPU de modelos .tflite. A compilação JIT pode ser especialmente útil em situações em que não é possível compilar o modelo com antecedência.
No entanto, a compilação JIT pode ter alguma latência e sobrecarga de memória para traduzir o modelo fornecido pelo usuário em instruções de bytecode da NPU sob demanda. Para minimizar o impacto no desempenho, os artefatos de compilação da NPU podem ser armazenados em cache.
Quando o cache está ativado, o LiteRT só aciona a recompilação do modelo quando necessário, por exemplo:
- A versão do plug-in do compilador de NPU do fornecedor mudou.
- A impressão digital do build do Android mudou;
- O modelo fornecido pelo usuário mudou;
- As opções de compilação mudaram.
Para ativar o cache de compilação da NPU, especifique a tag de ambiente CompilerCacheDir nas opções de ambiente. O valor precisa ser definido como um caminho gravável existente do aplicativo.
const std::array environment_options = {
litert::Environment::Option{
/*.tag=*/litert::Environment::OptionTag::CompilerPluginLibraryDir,
/*.value=*/kCompilerPluginLibSearchPath,
},
litert::Environment::Option{
litert::Environment::OptionTag::DispatchLibraryDir,
kDispatchLibraryDir,
},
// 'kCompilerCacheDir' will be used to store NPU-compiled model
// artifacts.
litert::Environment::Option{
litert::Environment::OptionTag::CompilerCacheDir,
kCompilerCacheDir,
},
};
// Create an environment.
LITERT_ASSERT_OK_AND_ASSIGN(
auto environment, litert::Environment::Create(environment_options));
// Load a model.
auto model_path = litert::testing::GetTestFilePath(kModelFileName);
LITERT_ASSERT_OK_AND_ASSIGN(auto model,
litert::Model::CreateFromFile(model_path));
// Create a compiled model, which only triggers NPU compilation if
// required.
LITERT_ASSERT_OK_AND_ASSIGN(
auto compiled_model, litert::CompiledModel::Create(
environment, model, kLiteRtHwAcceleratorNpu));
Exemplo de economia de latência e memória:
O tempo e a memória necessários para a compilação da NPU podem variar com base em vários fatores, como o chip da NPU, a complexidade do modelo de entrada etc.
A tabela a seguir compara o tempo de inicialização do ambiente de execução e o consumo de memória quando a compilação da NPU é necessária e quando ela pode ser ignorada devido ao armazenamento em cache. Em um dispositivo de amostra, obtemos o seguinte:
| Modelo do TFLite | Inicialização do modelo com compilação de NPU | model init com compilação em cache | consumo de memória de inicialização com compilação de NPU | inicializar a memória com a compilação em cache |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7465,22 ms | 198,34 ms | 1525,24 MB | 355,07 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592,54 ms | 166,47 ms | 254,90 MB | 33,78 MB |
Em outro dispositivo, coletamos o seguinte:
| Modelo do TFLite | Inicialização do modelo com compilação de NPU | model init com compilação em cache | consumo de memória de inicialização com compilação de NPU | inicializar a memória com a compilação em cache |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2766,44 ms | 379,86 ms | 653,54 MB | 501,21 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784,14 ms | 231,76 ms | 113,14 MB | 67,49 MB |