LiteRT fornisce un'interfaccia unificata per utilizzare le unità di elaborazione neurali (NPU) senza richiedere di navigare tra compilatori, runtime o dipendenze di librerie specifici del fornitore. L'utilizzo di LiteRT per l'accelerazione della NPU migliora le prestazioni per l'inferenza in tempo reale e di modelli di grandi dimensioni e riduce al minimo le copie della memoria tramite l'utilizzo di buffer hardware senza copia.
Inizia
- Per i modelli ML classici, vedi le seguenti applicazioni demo.
- App Kotlin per la segmentazione delle immagini: fornisce esempi rispettivamente per la compilazione AOT e la compilazione (JIT) sul dispositivo.
- App C++ per la segmentazione asincrona: mostra la compilazione AOT e la compilazione sul dispositivo (JIT) nella stessa app.
- Per i modelli linguistici di grandi dimensioni (LLM), consulta la guida all'esecuzione di LLM su NPU utilizzando LiteRT-LM.
Fornitori di NPU
LiteRT supporta l'accelerazione NPU con i seguenti fornitori:
Qualcomm AI Engine Direct
- Supporta l'esecuzione della compilazione AOT e on-device tramite l'API
CompiledModel. - Per i dettagli della configurazione, consulta Qualcomm AI Engine Direct.
- Per gli ultimi aggiornamenti, consulta Unlocking Peak Performance on Qualcomm NPU with LiteRT.
MediaTek NeuroPilot
- Supporta l'esecuzione della compilazione AOT e on-device tramite l'API
CompiledModel. - Per i dettagli della configurazione, vedi MediaTek NeuroPilot.
- Consulta MediaTek NPU and LiteRT: Powering the next generation of on-device AI per gli ultimi aggiornamenti.
Google Tensor
L'SDK Google Tensor è in accesso sperimentale. Registrati qui.
Compilazione AOT e on-device
La NPU LiteRT supporta la compilazione AOT e on-device per soddisfare i tuoi requisiti di deployment specifici:
- Compilazione offline (AOT): questa è la soluzione migliore per modelli grandi e complessi in cui è noto il SoC di destinazione. La compilazione ahead-of-time riduce significativamente i costi di inizializzazione e l'utilizzo della memoria quando l'utente avvia la tua app.
- Compilazione online (sul dispositivo): nota anche come compilazione JIT. Questa opzione è ideale per la distribuzione di piccoli modelli indipendenti dalla piattaforma. Il modello viene compilato sul dispositivo dell'utente durante l'inizializzazione, senza richiedere ulteriori passaggi di preparazione, ma comportando un costo più elevato per la prima esecuzione.
La seguente guida mostra come eseguire il deployment per la compilazione AOT e on-device in tre passaggi.
Passaggio 1: compilazione AOT per i SoC NPU di destinazione
Puoi utilizzare il compilatore AOT (ahead-of-time) LiteRT per compilare il modello .tflite per i SoC supportati. Puoi anche scegliere come target più fornitori e versioni di SoC contemporaneamente in un unico processo di compilazione. Per maggiori dettagli, consulta questo notebook sulla compilazione AOT di LiteRT. Anche se facoltativa, la compilazione AOT è vivamente consigliata per i modelli più grandi per ridurre il tempo di inizializzazione sul dispositivo. Questo passaggio non è necessario per la compilazione sul dispositivo.
Passaggio 2: esegui il deployment con Google Play se utilizzi Android
Su Android, utilizza Google Play per l'AI on-device (PODAI) per eseguire il deployment del modello e delle librerie di runtime della NPU con la tua app.
- Per i modelli di compilazione sul dispositivo: aggiungi il file del modello .tflite originale
direttamente nella directory assets/ della tua app.
- Vedi l'implementazione di esempio nell'app Kotlin per la compilazione on-device della segmentazione.
- Per i modelli di compilazione AOT: utilizza LiteRT per esportare i modelli compilati
in un unico Play AI Pack di Google.
Dopodiché, carichi il pacchetto AI su Google Play per distribuire automaticamente i modelli compilati corretti ai dispositivi degli utenti.
- Consulta LiteRT AOT Compilation notebook per istruzioni sull'esportazione dei modelli compilati in un pacchetto Play AI.
- Consulta l'implementazione di esempio nell'app Kotlin di compilazione AOT della segmentazione.
- Per le librerie di runtime della NPU, utilizza Play Feature Delivery per distribuire le librerie di runtime corrette ai dispositivi degli utenti.
Consulta le sezioni seguenti su come eseguire il deployment con Play AI Pack e Play Feature Delivery.
Esegui il deployment di modelli AOT con Play AI Pack
I seguenti passaggi ti guidano nell'implementazione dei modelli compilati AOT utilizzando Play AI Packs.
Aggiungi AI Pack al progetto
Importa i pacchetti AI nel progetto Gradle copiandoli nella directory radice del progetto Gradle. Ad esempio:
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Aggiungi ogni AI Pack alla configurazione di build di 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
Aggiungere pacchetti AI alla configurazione di Gradle
Copia device_targeting_configuration.xml dai pacchetti AI generati nella
directory del modulo dell'app principale. Poi aggiorna settings.gradle.kts:
// my_app/setting.gradle.kts
...
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
Aggiornamento build.gradle.kts:
// my_app/build.gradle.kts
android {
...
defaultConfig {
...
// API level 31+ is required for NPU support.
minSdk = 31
}
// AI Packs
assetPacks.add(":ai_packs:my_model")
assetPacks.add(":ai_packs:my_model_mtk")
}
Configura AI Pack per la pubblicazione on demand
La pubblicazione on demand ti consente di richiedere il modello in fase di runtime, il che è utile se
il modello è necessario solo per determinati flussi utente. Il modello verrà scaricato
nello spazio di archiviazione interno dell'app. Con la funzionalità Android AI Pack
configurata nel file build.gradle.kts, controlla le funzionalità del dispositivo.
Vedi anche le istruzioni per la pubblicazione al momento dell'installazione e la pubblicazione rapida successiva da PODAI.
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,
)
Esegui il deployment delle librerie di runtime NPU con Play Feature Delivery
Play Feature Delivery supporta più opzioni di pubblicazione per ottimizzare le dimensioni di download iniziali, tra cui la pubblicazione al momento dell'installazione, la pubblicazione on demand, la pubblicazione condizionale e la pubblicazione istantanea. Qui mostriamo la guida di base alla pubblicazione al momento dell'installazione.
Aggiungi le librerie di runtime della NPU al progetto
Scarica litert_npu_runtime_libraries.zip per la compilazione AOT o litert_npu_runtime_libraries_jit.zip per la compilazione sul dispositivo e decomprimi il file nella directory principale del progetto:
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
Esegui lo script per scaricare le librerie di supporto della NPU. Ad esempio, esegui il seguente comando per le NPU Qualcomm:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Aggiungi le librerie di runtime NPU alla configurazione Gradle
Copia device_targeting_configuration.xml dai pacchetti AI generati nella
directory del modulo dell'app principale. Poi aggiorna settings.gradle.kts:
// my_app/setting.gradle.kts
...
// 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")
Aggiornamento 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
}
}
// 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"))
...
}
Passaggio 3: inferenza sulla NPU utilizzando LiteRT Runtime
LiteRT astrae la complessità dello sviluppo rispetto a versioni specifiche di SoC, consentendoti di eseguire il modello sulla NPU con poche righe di codice. Fornisce inoltre un meccanismo di fallback integrato e robusto: puoi specificare CPU, GPU o entrambe come opzioni e LiteRT le utilizzerà automaticamente se la NPU non è disponibile. La compilazione AOT supporta anche il fallback. Fornisce una delega parziale sulla NPU in cui i sottografi non supportati vengono eseguiti senza problemi sulla CPU o sulla GPU come specificato.
Esegui in Kotlin
Vedi l'esempio di implementazione nelle seguenti app demo:
- Compilazione della segmentazione on-device dell'app Kotlin
- Segmentazione della compilazione AOT dell'app Kotlin
Aggiungere dipendenze Android
Puoi aggiungere l'ultimo pacchetto Maven di LiteRT alle dipendenze di build.gradle:
dependencies {
...
implementation("com.google.ai.edge.litert:litert:+")
}
Integrazione di runtime
// 1. Load model and initialize runtime.
// If NPU is unavailable, inference will fallback to GPU.
val model =
CompiledModel.create(
context.assets,
"model/mymodel.tflite",
CompiledModel.Options(Accelerator.NPU, Accelerator.GPU)
)
// 2. Pre-allocate input/output buffers
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()
// 3. Fill the first input
inputBuffers[0].writeFloat(...)
// 4. Invoke
model.run(inputBuffers, outputBuffers)
// 5. Read the output
val outputFloatArray = outputBuffers[0].readFloat()
Esegui in C++ multipiattaforma
Vedi l'implementazione di esempio in Asynchronous segmentation C++ App.
Dipendenze build Bazel
Gli utenti C++ devono creare le dipendenze dell'applicazione con l'accelerazione LiteRT NPU. La regola cc_binary che raggruppa la logica principale dell'applicazione
(ad es. main.cc) richiede i seguenti componenti di runtime:
- Libreria condivisa dell'API LiteRT C: l'attributo
datadeve includere la libreria condivisa dell'API LiteRT C (//litert/c:litert_runtime_c_api_shared_lib) e l'oggetto condiviso di distribuzione specifico del fornitore per la NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so). - Librerie di backend specifiche per la NPU: ad esempio, le librerie Qualcomm AI RT (QAIRT)
per l'host Android (come
libQnnHtp.so,libQnnHtpPrepare.so) e la libreria DSP Hexagon corrispondente (libQnnHtpV79Skel.so). In questo modo, il runtime LiteRT può scaricare i calcoli sulla NPU. - Dipendenze degli attributi: l'attributo
depssi collega a dipendenze essenziali in fase di compilazione, come il buffer dei tensori di LiteRT (//litert/cc:litert_tensor_buffer) e l'API per il livello di distribuzione della NPU (//litert/vendors/qualcomm/dispatch:dispatch_api). In questo modo, il codice dell'applicazione può interagire con la NPU tramite LiteRT. - File del modello e altri asset: inclusi tramite l'attributo
data.
Questa configurazione consente al binario compilato di caricare e utilizzare dinamicamente la NPU per l'inferenza di machine learning accelerata.
Configurare un ambiente NPU
Alcuni backend NPU richiedono librerie o dipendenze di runtime. Quando utilizzi l'API del modello
compilato, LiteRT organizza questi requisiti tramite un oggetto Environment.
Utilizza il seguente codice per trovare le librerie o i driver NPU appropriati:
// 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)));
Integrazione di runtime
Il seguente snippet di codice mostra un'implementazione di base dell'intero processo in 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));
Copia zero con accelerazione NPU
L'utilizzo della copia zero consente a un'NPU di accedere direttamente ai dati nella propria memoria senza che la CPU debba copiarli esplicitamente. Non copiando i dati nella memoria della CPU e dalla memoria della CPU, la copia zero può ridurre significativamente la latenza end-to-end.
Il seguente codice è un'implementazione di esempio di Zero-Copy NPU con
AHardwareBuffer, che passa i dati direttamente alla NPU. Questa implementazione evita
costosi round trip alla memoria della CPU, riducendo significativamente l'overhead di inferenza.
// 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();
Concatenare più inferenze della NPU
Per pipeline complesse, puoi concatenare più inferenze della NPU. Poiché ogni passaggio utilizza un buffer compatibile con l'acceleratore, la pipeline rimane principalmente nella memoria gestita dalla 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);
Memorizzazione nella cache della compilazione on-device della NPU
LiteRT supporta la compilazione JIT (Just In Time) dei modelli .tflite sulla NPU del dispositivo. La compilazione JIT
può essere particolarmente utile nelle situazioni in cui la compilazione del modello
in anticipo non è fattibile.
La compilazione JIT, tuttavia, può comportare un sovraccarico di latenza e memoria per tradurre il modello fornito dall'utente in istruzioni bytecode NPU su richiesta. Per ridurre al minimo l'impatto sulle prestazioni, gli artefatti di compilazione della NPU possono essere memorizzati nella cache.
Quando la memorizzazione nella cache è abilitata, LiteRT attiverà la ricompilazione del modello solo quando necessario, ad esempio:
- La versione del plug-in del compilatore NPU del fornitore è cambiata.
- Il fingerprint build di Android è cambiato.
- Il modello fornito dall'utente è cambiato.
- Le opzioni di compilazione sono cambiate.
Per attivare la memorizzazione nella cache della compilazione della NPU, specifica il tag dell'ambiente CompilerCacheDir nelle opzioni dell'ambiente. Il valore deve essere impostato su un percorso scrivibile esistente dell'applicazione.
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));
Esempio di risparmio di latenza e memoria:
Il tempo e la memoria necessari per la compilazione della NPU possono variare in base a diversi fattori, come il chip NPU sottostante, la complessità del modello di input e così via.
La seguente tabella confronta il tempo di inizializzazione del runtime e il consumo di memoria quando è richiesta la compilazione della NPU rispetto a quando la compilazione può essere ignorata a causa della memorizzazione nella cache. Su un dispositivo di esempio otteniamo quanto segue:
| Modello TFLite | model init with NPU compilation | model init with cached compilation | init memory footprint with NPU compilation | init memory with cached compilation |
|---|---|---|---|---|
| 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 |
Su un altro dispositivo otteniamo quanto segue:
| Modello TFLite | model init with NPU compilation | model init with cached compilation | init memory footprint with NPU compilation | init memory with cached compilation |
|---|---|---|---|---|
| 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 |