LiteRT bietet eine einheitliche Schnittstelle für die Verwendung von Neural Processing Units (NPUs), ohne dass Sie anbieterspezifische Compiler, Runtimes oder Bibliotheksabhängigkeiten benötigen. Die Verwendung von LiteRT für die NPU-Beschleunigung steigert die Leistung für Echtzeit- und Large-Model-Inferenz und minimiert Speicherkopien durch die Verwendung von Hardwarepuffern ohne Kopieren.
Jetzt starten
- Klassische ML-Modelle: Die folgenden Demoanwendungen sind verfügbar.
- Kotlin-App zur Bildsegmentierung: Hier finden Sie Beispiele für die AOT-Kompilierung bzw. die On-Device-Kompilierung (JIT).
- Asynchronous segmentation C++ App (Asynchrone Segmentierungs-C++-App): Hier werden sowohl die AOT-Kompilierung als auch die On-Device-Kompilierung (JIT) in derselben App demonstriert.
- Für Large Language Models (LLMs) finden Sie im Leitfaden Informationen zum Ausführen von LLMs auf der NPU mit LiteRT-LM.
NPU-Anbieter
LiteRT unterstützt die NPU-Beschleunigung mit den folgenden Anbietern:
Qualcomm AI Engine Direct
- Unterstützung der AOT- und On-Device-Kompilierung über die
CompiledModel-API. - Weitere Informationen zur Einrichtung finden Sie unter Qualcomm AI Engine Direct.
- Hier finden Sie die neuesten Informationen.
MediaTek NeuroPilot
- Unterstützung der AOT- und On-Device-Kompilierung über die
CompiledModel-API. - Weitere Informationen zur Einrichtung finden Sie unter MediaTek NeuroPilot.
- MediaTek NPU and LiteRT: Powering the next generation of on-device AI
Google Tensor
Das Google Tensor SDK ist in der experimentellen Zugriffsphase. Hier kannst du dich registrieren.
AOT- und On-Device-Kompilierung
LiteRT NPU unterstützt sowohl AOT- als auch On-Device-Kompilierung, um Ihre spezifischen Bereitstellungsanforderungen zu erfüllen:
- Offline-Kompilierung (AOT): Diese Methode eignet sich am besten für große, komplexe Modelle, bei denen der Ziel-SoC bekannt ist. Durch die Ahead-of-Time-Kompilierung werden die Initialisierungskosten erheblich gesenkt und der Speicherverbrauch beim Starten der App durch den Nutzer reduziert.
- Online-Kompilierung (auf dem Gerät): Auch als JIT-Kompilierung bezeichnet. Dies ist ideal für die plattformunabhängige Verteilung kleiner Modelle. Das Modell wird während der Initialisierung auf dem Gerät des Nutzers kompiliert. Das erfordert keinen zusätzlichen Vorbereitungsschritt, verursacht aber höhere Kosten beim ersten Ausführen.
In der folgenden Anleitung wird in drei Schritten beschrieben, wie Sie die Bereitstellung für die AOT- und die On-Device-Kompilierung vornehmen.
Schritt 1: AOT-Kompilierung für die Ziel-NPU-SoCs
Mit dem LiteRT AOT-Compiler (Ahead-of-Time) können Sie Ihr .tflite-Modell für die unterstützten SoCs kompilieren. Sie können auch mehrere SoC-Anbieter und ‑Versionen gleichzeitig in einem einzigen Kompilierungsprozess anvisieren. Weitere Informationen finden Sie in diesem LiteRT AOT Compilation-Notebook. Die AOT-Kompilierung ist zwar optional, wird aber für größere Modelle dringend empfohlen, um die Initialisierungszeit auf dem Gerät zu verkürzen. Dieser Schritt ist für die On-Device-Kompilierung nicht erforderlich.
Schritt 2: Bereitstellung über Google Play (Android)
Verwenden Sie auf Android-Geräten Google Play for On-device AI (PODAI), um das Modell und die NPU-Laufzeitbibliotheken mit Ihrer App bereitzustellen.
- Für Modelle der On-Device-Kompilierung: Fügen Sie die ursprüngliche .tflite-Modelldatei direkt in das Verzeichnis „assets/“ Ihrer App ein.
- Eine Beispielimplementierung finden Sie in der Kotlin-App für die On-Device-Kompilierung von Segmentierung.
- Für Modelle der AOT-Kompilierung: Verwenden Sie LiteRT, um Ihre kompilierten Modelle in ein einzelnes Google Play AI Pack zu exportieren.
Anschließend laden Sie das KI-Paket in Google Play hoch, damit die richtigen kompilierten Modelle automatisch auf die Geräte der Nutzer übertragen werden.
- Eine Anleitung zum Exportieren kompilierter Modelle in ein Play AI Pack finden Sie im LiteRT AOT Compilation-Notebook.
- Eine Beispielimplementierung finden Sie unter Segmentation AOT compilation Kotlin App.
- NPU-Laufzeitbibliotheken werden über Play Feature Delivery auf die Geräte der Nutzer verteilt.
In den folgenden Abschnitten erfahren Sie, wie Sie Play AI Pack und Play Feature Delivery für die Bereitstellung verwenden.
AOT-Modelle mit dem Play AI Pack bereitstellen
In den folgenden Schritten wird beschrieben, wie Sie Ihre AOT-kompilierten Modelle mit Play AI Packs bereitstellen.
AI Pack zum Projekt hinzufügen
Importieren Sie KI-Pakete in das Gradle-Projekt, indem Sie die KI-Pakete in das Stammverzeichnis des Gradle-Projekts kopieren. Beispiel:
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Fügen Sie jedes AI Pack der Gradle-Build-Konfiguration hinzu:
// 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
AI Packs der Gradle-Konfiguration hinzufügen
Kopieren Sie device_targeting_configuration.xml aus den generierten KI-Paketen in das Verzeichnis des Haupt-App-Moduls. Aktualisieren Sie dann settings.gradle.kts:
// my_app/setting.gradle.kts
...
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
Aktualisieren Sie 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")
}
AI Pack für die On-Demand-Bereitstellung konfigurieren
Mit der On-Demand-Bereitstellung können Sie das Modell zur Laufzeit anfordern. Das ist nützlich, wenn das Modell nur für bestimmte Nutzerabläufe erforderlich ist. Ihr Modell wird in den internen Speicherplatz Ihrer App heruntergeladen. Prüfen Sie die Gerätefunktionen, wenn die Funktion „Android AI Pack“ in der Datei build.gradle.kts konfiguriert ist.
Weitere Informationen finden Sie in der Anleitung für die Bereitstellung bei der Installation und die Bereitstellung nach der Installation von 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,
)
NPU-Laufzeitbibliotheken mit Play Feature Delivery bereitstellen
Play Feature Delivery unterstützt mehrere Bereitstellungsoptionen zur Optimierung der anfänglichen Downloadgröße, darunter die Bereitstellung bei der Installation, die On-Demand-Bereitstellung, die bedingte Bereitstellung und die Sofortbereitstellung. Hier sehen Sie die grundlegende Anleitung zur Bereitstellung bei der Installation.
NPU-Laufzeitbibliotheken zum Projekt hinzufügen
Laden Sie litert_npu_runtime_libraries.zip für die AOT-Kompilierung oder litert_npu_runtime_libraries_jit.zip für die On-Device-Kompilierung herunter und entpacken Sie die Datei im Stammverzeichnis des Projekts:
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
Führen Sie das Skript aus, um die NPU-Supportbibliotheken herunterzuladen. Führen Sie beispielsweise Folgendes für Qualcomm-NPUs aus:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
NPU-Laufzeitbibliotheken zur Gradle-Konfiguration hinzufügen
Kopieren Sie device_targeting_configuration.xml aus den generierten KI-Paketen in das Verzeichnis des Haupt-App-Moduls. Aktualisieren Sie dann 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")
Aktualisieren Sie 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"))
...
}
Schritt 3: Inferenz auf der NPU mit der LiteRT-Laufzeit
LiteRT abstrahiert die Komplexität der Entwicklung für bestimmte SoC-Versionen, sodass Sie Ihr Modell mit nur wenigen Codezeilen auf der NPU ausführen können. Außerdem bietet sie einen robusten, integrierten Fallback-Mechanismus: Sie können CPU, GPU oder beides als Optionen angeben. LiteRT verwendet diese dann automatisch, wenn die NPU nicht verfügbar ist. Die AOT-Kompilierung unterstützt auch Fallback. Sie bietet eine teilweise Delegation an die NPU, wobei nicht unterstützte Teilgraphen nahtlos auf der CPU oder GPU ausgeführt werden, wie angegeben.
In Kotlin ausführen
Beispielimplementierungen finden Sie in den folgenden Demo-Apps:
- Segmentierung der On-Device-Kompilierung von Kotlin-Apps
- Segmentierung der AOT-Kompilierung von Kotlin-Apps
Android-Abhängigkeiten hinzufügen
Sie können das aktuelle LiteRT-Maven-Paket zu Ihren build.gradle-Abhängigkeiten hinzufügen:
dependencies {
...
implementation("com.google.ai.edge.litert:litert:+")
}
Laufzeitintegration
// 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()
Plattformübergreifende Ausführung in C++
Eine Beispielimplementierung finden Sie in der C++-App für die asynchrone Segmentierung.
Bazel-Build-Abhängigkeiten
C++-Nutzer müssen die Abhängigkeiten der Anwendung mit LiteRT-NPU-Beschleunigung erstellen. Die cc_binary-Regel, die die Kernanwendungslogik verpackt (z.B. main.cc) sind die folgenden Laufzeitkomponenten erforderlich:
- Gemeinsame Bibliothek der LiteRT C API: Das Attribut
datamuss die gemeinsame Bibliothek der LiteRT C API (//litert/c:litert_runtime_c_api_shared_lib) und das anbieterspezifische Dispatch-Shared-Object für die NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so) enthalten. - NPU-spezifische Backend-Bibliotheken: Zum Beispiel die Qualcomm AI RT (QAIRT)-Bibliotheken für den Android-Host (z. B.
libQnnHtp.so,libQnnHtpPrepare.so) und die entsprechende Hexagon DSP-Bibliothek (libQnnHtpV79Skel.so). So kann die LiteRT-Laufzeit Berechnungen an die NPU auslagern. - Attributabhängigkeiten: Das Attribut
depsverweist auf wichtige Compile-Zeit-Abhängigkeiten wie den Tensorpuffer von LiteRT (//litert/cc:litert_tensor_buffer) und die API für die NPU-Dispatch-Ebene (//litert/vendors/qualcomm/dispatch:dispatch_api). Dadurch kann Ihr Anwendungscode über LiteRT mit der NPU interagieren. - Modelldateien und andere Assets: Über das Attribut
dataeingebunden.
Mit dieser Einrichtung kann Ihre kompilierte Binärdatei die NPU dynamisch laden und für die beschleunigte Inferenz von maschinellem Lernen verwenden.
NPU-Umgebung einrichten
Für einige NPU-Back-Ends sind Laufzeitabhängigkeiten oder ‑bibliotheken erforderlich. Bei Verwendung der API für kompilierte Modelle werden diese Anforderungen von LiteRT über ein Environment-Objekt organisiert.
Mit dem folgenden Code können Sie die entsprechenden NPU-Bibliotheken oder ‑Treiber finden:
// 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)));
Laufzeitintegration
Das folgende Code-Snippet zeigt eine einfache Implementierung des gesamten Prozesses 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));
Zero-Copy mit NPU-Beschleunigung
Durch die Verwendung von Zero-Copy kann eine NPU direkt auf Daten im eigenen Speicher zugreifen, ohne dass die CPU diese Daten explizit kopieren muss. Da keine Daten in den und aus dem CPU-Arbeitsspeicher kopiert werden, kann die End-to-End-Latenz durch Zero-Copy deutlich reduziert werden.
Der folgende Code ist eine Beispielimplementierung von Zero-Copy-NPU mit AHardwareBuffer, bei der Daten direkt an die NPU übergeben werden. Bei dieser Implementierung werden teure Roundtrips zum CPU-Speicher vermieden, wodurch der Inferenz-Overhead erheblich reduziert wird.
// 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();
Mehrere NPU-Inferenzvorgänge verketten
Bei komplexen Pipelines können Sie mehrere NPU-Inferenzvorgänge verketten. Da in jedem Schritt ein accelerator-freundlicher Puffer verwendet wird, bleibt Ihre Pipeline größtenteils im NPU-verwalteten Speicher:
// 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);
NPU-Caching für die On-Device-Kompilierung
LiteRT unterstützt die NPU-basierte On-Device-Kompilierung (JIT) von .tflite-Modellen. Die JIT-Kompilierung kann besonders nützlich sein, wenn es nicht möglich ist, das Modell vorab zu kompilieren.
Die JIT-Kompilierung kann jedoch zu Wartezeiten und Speicher-Overhead führen, da das vom Nutzer bereitgestellte Modell bei Bedarf in NPU-Bytecode-Anweisungen übersetzt werden muss. Um die Auswirkungen auf die Leistung zu minimieren, können NPU-Kompilierungsartefakte im Cache gespeichert werden.
Wenn das Caching aktiviert ist, löst LiteRT die Neukompilierung des Modells nur bei Bedarf aus, z.B.:
- Die Version des NPU-Compiler-Plug-ins des Anbieters hat sich geändert.
- Der Android-Build-Fingerprint hat sich geändert.
- Das vom Nutzer bereitgestellte Modell wurde geändert.
- Die Kompilierungsoptionen wurden geändert.
Wenn Sie das NPU-Kompilierungs-Caching aktivieren möchten, geben Sie in den Umgebungsoptionen das Umgebungstag CompilerCacheDir an. Der Wert muss auf einen vorhandenen beschreibbaren Pfad der Anwendung festgelegt werden.
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));
Beispiele für Latenz- und Arbeitsspeichereinsparungen:
Die für die NPU-Kompilierung erforderliche Zeit und der erforderliche Arbeitsspeicher können je nach verschiedenen Faktoren variieren, z. B. dem zugrunde liegenden NPU-Chip und der Komplexität des Eingabemodells.
In der folgenden Tabelle werden die Laufzeitinitialisierungszeit und der Arbeitsspeicherverbrauch verglichen, wenn eine NPU-Kompilierung erforderlich ist und wenn die Kompilierung aufgrund von Caching übersprungen werden kann. Auf einem Beispielgerät erhalten wir Folgendes:
| TFLite-Modell | Modellinitialisierung mit NPU-Kompilierung | Modellinitialisierung mit zwischengespeicherter Kompilierung | Speicherbedarf bei der Initialisierung mit NPU-Kompilierung | Arbeitsspeicher mit zwischengespeicherter Kompilierung initialisieren |
|---|---|---|---|---|
| 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 |
Auf einem anderen Gerät erhalten wir Folgendes:
| TFLite-Modell | Modellinitialisierung mit NPU-Kompilierung | Modellinitialisierung mit zwischengespeicherter Kompilierung | Speicherbedarf bei der Initialisierung mit NPU-Kompilierung | Arbeitsspeicher mit zwischengespeicherter Kompilierung initialisieren |
|---|---|---|---|---|
| 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 |