LiteRT fournit une interface unifiée pour utiliser les unités de traitement neuronal (NPU, Neural Processing Units) sans avoir à parcourir des compilateurs, des environnements d'exécution ou des dépendances de bibliothèque spécifiques à un fournisseur. L'utilisation de LiteRT pour l'accélération NPU améliore les performances pour l'inférence en temps réel et les modèles volumineux, et minimise les copies de mémoire grâce à l'utilisation de tampons matériels sans copie.
Premiers pas
Modèles de ML classiques
Pour les modèles de ML classiques, consultez les applications de démonstration suivantes.
- Application Kotlin de segmentation d'images: compilation AOT et sur l'appareil(JIT).
- Application C++ de segmentation d'images : compilation AOT et sur l'appareil (JIT) dans la même application.
Modèles d'IA générative
Pour les modèles d'IA générative, consultez les démonstrations et le guide suivants :
- Application C++ de similarité sémantique EmbeddingGemma: inférence CPU/GPU/NPU.
- Guide sur l'exécution de LLM à l'aide de LiteRT-LM.
Fournisseurs de NPU
LiteRT est compatible avec l'accélération NPU des fournisseurs suivants :
Google Tensor
- Prise en charge de l'exécution AOT via l'API
CompiledModel - Pour en savoir plus sur la configuration, consultez Google Tensor.
Qualcomm AI Engine Direct
- Prise en charge de l'exécution de la compilation AOT et sur l'appareil via l'API
CompiledModel - Pour en savoir plus sur la configuration, consultez Qualcomm AI Engine Direct.
- Pour obtenir les dernières informations, consultez Débloquer des performances optimales sur le NPU Qualcomm avec LiteRT.
MediaTek NeuroPilot
- Prise en charge de l'exécution de la compilation AOT et sur l'appareil via l'API
CompiledModel - Pour en savoir plus sur la configuration, consultez MediaTek NeuroPilot.
- Pour obtenir les dernières informations, consultez MediaTek NPU et LiteRT : alimenter la prochaine génération d'IA sur l'appareil.
Intel OpenVino
- Prise en charge de l'exécution de la compilation AOT et sur l'appareil via l'API
CompiledModel - Pour en savoir plus sur la configuration, consultez Intel OpenVino.
Compilation AOT et sur l'appareil
LiteRT NPU est compatible avec la compilation AOT et sur l'appareil pour répondre à vos exigences de déploiement spécifiques :
- Compilation hors connexion (AOT) : elle est idéale pour les modèles volumineux et complexes dont le SoC cible est connu. La compilation anticipée réduit considérablement les coûts d'initialisation et l'utilisation de la mémoire lorsque l'utilisateur lance votre application.
- Compilation en ligne (sur l'appareil) : également appelée compilation JIT. Elle est idéale pour la distribution de petits modèles indépendants de la plate-forme. Le modèle est compilé sur l'appareil de l'utilisateur lors de l'initialisation, ce qui ne nécessite aucune étape de préparation supplémentaire, mais entraîne un coût plus élevé lors de la première exécution.
Voici comment déployer votre modèle à l'aide des options de compilation AOT ou sur l'appareil :
Étape 1 : Compilation AOT pour les SoC NPU cibles
Vous pouvez utiliser le compilateur LiteRT AOT (anticipé) pour compiler votre modèle .tflite sur les SoC compatibles. Vous pouvez également cibler simultanément plusieurs fournisseurs et versions de SoC dans un seul processus de compilation. Pour en savoir plus, consultez ce notebook de compilation LiteRT AOT. Bien que facultative, la compilation AOT est fortement recommandée pour les modèles plus volumineux afin de réduire le temps d'initialisation sur l'appareil. Cette étape n'est pas requise pour la compilation sur l'appareil.
Étape 2 : Déployer avec Google Play sur Android
Sur Android, utilisez Google Play pour l'IA sur l'appareil (PODAI) afin de déployer le modèle et les bibliothèques d'exécution NPU avec votre application.
- Pour les modèles de compilation sur l'appareil : ajoutez le fichier de modèle .tflite d'origine
directement dans le répertoire assets/ de votre application.
- Pour obtenir un exemple d'implémentation, consultez l' application Kotlin de compilation sur l'appareil de segmentation.
- Pour les modèles de compilation AOT : utilisez LiteRT pour exporter vos modèles compilés
dans un seul pack d'IA Google Play.
Vous pouvez ensuite importer le pack d'IA dans Google Play pour diffuser automatiquement les modèles compilés corrects sur les appareils des utilisateurs.
- Pour obtenir des instructions sur l'exportation de modèles compilés dans un pack d'IA Play, consultez le notebook de compilation LiteRT AOT.
- Pour obtenir un exemple d'implémentation, consultez l' application Kotlin de compilation AOT de segmentation.
- Pour les bibliothèques d'exécution NPU, utilisez Play Feature Delivery pour distribuer les bibliothèques d'exécution appropriées sur les appareils des utilisateurs.
Consultez les sections suivantes pour savoir comment déployer avec Play AI Pack et Play Feature Delivery.
Déployer des modèles AOT avec Play AI Pack
Les étapes suivantes vous guident dans le déploiement de vos modèles compilés AOT à l'aide de Play AI Packs.
Ajouter un pack d'IA au projet
Importez des packs d'IA dans le projet Gradle en copiant le ou les packs d'IA dans le répertoire racine du projet Gradle. Exemple :
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Ajoutez chaque pack d'IA à la configuration de compilation 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
Ajouter des packs d'IA à la configuration Gradle
Copiez device_targeting_configuration.xml des packs d'IA générés dans le répertoire du module d'application principal. Mettez ensuite à jour settings.gradle.kts :
// my_app/setting.gradle.kts
...
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
Mettez à jour 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")
}
Configurer un pack d'IA pour la distribution à la demande
La distribution à la demande vous permet de demander le modèle au moment de l'exécution, ce qui est utile si le modèle n'est requis que pour certains flux utilisateur. Votre modèle sera téléchargé dans l'espace de stockage interne de votre application. Une fois la fonctionnalité Android AI Pack configurée dans le fichier build.gradle.kts, vérifiez les capacités de l'appareil.
Consultez également
les instructions
pour la distribution au moment de l'installation et la distribution rapide à partir de PODAI.
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val cpuGpuModelProvider =
ModelProvider.staticModel(
ModelProvider.Type.ASSET,
"model/my_model_cpu_gpu.tflite",
if (accelerator != Accelerator.NPU) accelerator else Accelerator.CPU,
)
val qualcommNpuModelProvider =
AiPackModelProvider(context, "my_model", "model/my_model.tflite")
{
buildSet {
if (
accelerator == Accelerator.NPU && NpuCompatibilityChecker.Qualcomm.isDeviceSupported()
)
add(Accelerator.NPU)
}
}
val mtkNpuModelProvider =
AiPackModelProvider(context, "my_model_mtk", "model/my_model.tflite")
{
buildSet {
if (
accelerator == Accelerator.NPU && NpuCompatibilityChecker.Mediatek.isDeviceSupported()
)
add(Accelerator.NPU)
}
}
val googleTensorTpuModelProvider =
AiPackModelProvider(context, "my_model", "model/my_model.tflite")
{
buildSet {
if (accelerator == Accelerator.NPU &&
NpuCompatibilityChecker.GoogleTensor.isDeviceSupported()
)
add(Accelerator.NPU)
}
}
val aiPackModelProvider =
ModelSelector(cpuGpuModelProvider, mtkNpuModelProvider, qualcommNpuModelProvider, googleTensorTpuModelProvider)
.selectModel(env)
val compiledModel = CompiledModel.create(
model.getPath(),
CompiledModel.Options(model.getCompatibleAccelerators()),
env,
)
Déployer des bibliothèques d'exécution NPU avec Play Feature Delivery
Play Feature Delivery est compatible avec plusieurs options de distribution pour optimiser la taille du téléchargement initial, y compris la distribution au moment de l'installation, la distribution à la demande, la distribution conditionnelle et la distribution instantanée. Nous présentons ici le guide de base pour la distribution au moment de l'installation.
Ajouter des bibliothèques d'exécution NPU au projet
Téléchargez
litert_npu_runtime_libraries.zip à partir de la dernière version
pour la compilation AOT ou
litert_npu_runtime_libraries_jit.zip à partir de la dernière version
pour la compilation sur l'appareil, puis décompressez-le dans le répertoire racine du projet :
my_app/
...
litert_npu_runtime_libraries/
google_tensor_runtime/...
mediatek_runtime/...
qualcomm_runtime_v69/...
qualcomm_runtime_v73/...
qualcomm_runtime_v75/...
qualcomm_runtime_v79/...
qualcomm_runtime_v81/...
fetch_qualcomm_library.sh
Exécutez le script pour télécharger les bibliothèques de prise en charge NPU. Par exemple, exécutez la commande suivante pour les NPU Qualcomm :
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Ajouter des bibliothèques d'exécution NPU à la configuration Gradle
Copiez device_targeting_configuration.xml des packs d'IA générés dans le répertoire du module d'application principal. Mettez ensuite à jour settings.gradle.kts :
// my_app/setting.gradle.kts
...
// NPU runtime libraries
include(":litert_npu_runtime_libraries:runtime_strings")
include(":litert_npu_runtime_libraries:google_tensor_runtime")
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")
Mettez à jour 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:google_tensor_runtime")
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"))
...
}
Étape 3 : Inférence sur NPU à l'aide de LiteRT Runtime
LiteRT élimine la complexité du développement par rapport à des versions spécifiques de SoC, ce qui vous permet d'exécuter votre modèle sur le NPU en quelques lignes de code. Il fournit également un mécanisme de secours intégré et robuste : vous pouvez spécifier le processeur, le GPU ou les deux comme options, et LiteRT les utilisera automatiquement si le NPU n'est pas disponible. La compilation AOT prend également en charge le secours. Elle fournit une délégation partielle sur le NPU où les sous-graphes non compatibles s'exécutent de manière transparente sur le processeur ou le GPU, comme spécifié.
Exécuter en Kotlin
Pour obtenir un exemple d'implémentation, consultez les applications de démonstration suivantes :
- Application Kotlin de compilation sur l'appareil de segmentation
- Application Kotlin de compilation AOT de segmentation
Ajouter des dépendances Android
Vous pouvez ajouter le dernier package LiteRT Maven à vos dépendances build.gradle :
dependencies {
...
implementation("com.google.ai.edge.litert:litert:+")
}
Intégration de l'environnement d'exécution
// 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()
Exécuter en C++ multiplate-forme
Pour obtenir un exemple d'implémentation, consultez l' application C++ de segmentation asynchrone.
Dépendances de compilation Bazel
Les utilisateurs de C++ doivent créer les dépendances de l'application avec l'accélération LiteRT NPU. La règle cc_binary qui regroupe la logique de l'application principale (par exemple, main.cc) nécessite les composants d'exécution suivants :
- Bibliothèque partagée de l'API LiteRT C : l'
dataattribut doit inclure la bibliothèque partagée de l'API LiteRT C (//litert/c:litert_runtime_c_api_shared_lib) et l'objet partagé de répartition spécifique au fournisseur pour le NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so). - Bibliothèques de backend spécifiques au NPU : par exemple, les bibliothèques Qualcomm AI RT (QAIRT)
pour l'hôte Android (comme
libQnnHtp.so,libQnnHtpPrepare.so) et la bibliothèque Hexagon DSP correspondante (libQnnHtpV79Skel.so). Cela garantit que l'environnement d'exécution LiteRT peut décharger les calculs sur le NPU. - Dépendances d'attribut : l'attribut
depsest lié aux dépendances essentielles au moment de la compilation, telles que le tampon de tenseur de LiteRT (//litert/cc:litert_tensor_buffer) et l'API pour la couche de répartition NPU (//litert/vendors/qualcomm/dispatch:dispatch_api). Cela permet au code de votre application d'interagir avec le NPU via LiteRT. - Fichiers de modèle et autres éléments : inclus via l'attribut
data.
Cette configuration permet à votre binaire compilé de charger et d'utiliser dynamiquement le NPU pour l'inférence accélérée du machine learning.
Configurer un environnement NPU
Certains backends NPU nécessitent des dépendances ou des bibliothèques d'exécution. Lorsque vous utilisez l'API de modèle compilé, LiteRT organise ces exigences via un objet Environment.
Utilisez le code suivant pour trouver les bibliothèques ou pilotes NPU appropriés :
// 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)));
Intégration de l'environnement d'exécution
L'extrait de code suivant présente une implémentation de base de l'ensemble du processus en 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));
Zéro copie avec accélération NPU
L'utilisation de la copie zéro permet à un NPU d'accéder directement aux données dans sa propre mémoire sans que le processeur n'ait besoin de les copier explicitement. En ne copiant pas les données vers et depuis la mémoire du processeur, la copie zéro peut réduire considérablement la latence de bout en bout.
Le code suivant est un exemple d'implémentation de NPU sans copie avec AHardwareBuffer, qui transmet les données directement au NPU. Cette implémentation évite les allers-retours coûteux vers la mémoire du processeur, ce qui réduit considérablement la surcharge d'inférence.
// 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();
Chaîner plusieurs inférences NPU
Pour les pipelines complexes, vous pouvez chaîner plusieurs inférences NPU. Étant donné que chaque étape utilise un tampon compatible avec l'accélérateur, votre pipeline reste principalement dans la mémoire gérée par le 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);
Mise en cache de la compilation sur l'appareil NPU
LiteRT est compatible avec la compilation sur l'appareil NPU (appelée JIT) des modèles .tflite.
La compilation JIT peut être particulièrement utile dans les situations où la compilation anticipée du modèle n'est pas possible.
La compilation JIT peut toutefois entraîner une certaine latence et une surcharge de mémoire pour traduire à la demande le modèle fourni par l'utilisateur en instructions de bytecode NPU. Pour minimiser l'impact sur les performances, les artefacts de compilation NPU peuvent être mis en cache.
Lorsque la mise en cache est activée, LiteRT ne déclenche la recompilation du modèle que lorsque cela est nécessaire, par exemple :
- La version du plug-in de compilateur NPU du fournisseur a changé ;
- L'empreinte de compilation Android a changé ;
- Le modèle fourni par l'utilisateur a changé ;
- Les options de compilation ont changé.
Pour activer la mise en cache de la compilation NPU, spécifiez la balise d'environnement CompilerCacheDir dans les options d'environnement. La valeur doit être définie sur un chemin d'accès existant et accessible en écriture de l'application.
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));
Exemple d'économies de latence et de mémoire :
Le temps et la mémoire requis pour la compilation NPU peuvent varier en fonction de plusieurs facteurs, tels que la puce NPU sous-jacente, la complexité du modèle d'entrée, etc.
Le tableau suivant compare le temps d'initialisation de l'environnement d'exécution et la consommation de mémoire lorsque la compilation NPU est requise par rapport au moment où la compilation peut être ignorée en raison de la mise en cache. Sur un exemple d'appareil, nous obtenons les résultats suivants :
| Modèle TFLite | Initialisation du modèle avec compilation NPU | Initialisation du modèle avec compilation mise en cache | Espace mémoire utilisé initial avec compilation NPU | Mémoire initiale avec compilation mise en cache |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7 465,22 ms | 198,34 ms | 1 525,24 Mo | 355,07 Mo |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1 592,54 ms | 166,47 ms | 254,90 Mo | 33,78 Mo |
Sur un autre appareil, nous obtenons les résultats suivants :
| Modèle TFLite | Initialisation du modèle avec compilation NPU | Initialisation du modèle avec compilation mise en cache | Espace mémoire utilisé initial avec compilation NPU | Mémoire initiale avec compilation mise en cache |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2 766,44 ms | 379,86 ms | 653,54 Mo | 501,21 Mo |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784,14 ms | 231,76 ms | 113,14 Mo | 67,49 Mo |