LiteRT は、ベンダー固有のコンパイラ、ランタイム、ライブラリの依存関係を操作することなく、ニューラル プロセッサ ユニット(NPU)を使用するための統合インターフェースを提供します。NPU アクセラレーションに LiteRT を使用すると、リアルタイム推論と大規模モデル推論のパフォーマンスが向上し、ゼロコピー ハードウェア バッファの使用によりメモリコピーが最小限に抑えられます。
使ってみる
従来の ML モデルの場合は、次のデモアプリをご覧ください。
- 画像セグメンテーション Kotlin アプリ: AOT コンパイルと オンデバイス(JIT) コンパイル。
- 画像セグメンテーション C++ アプリ: 同じアプリでの AOT コンパイルとオンデバイス(JIT) コンパイル。
生成 AI モデルの場合は、次のデモとガイドをご覧ください。
- EmbeddingGemma セマンティック類似性 C++ アプリ: CPU/GPU/NPU 推論。
- LiteRT-LM を使用して LLM を実行するガイド。
NPU ベンダー
LiteRT は、次のベンダーの NPU アクセラレーションをサポートしています。
Qualcomm AI Engine Direct
CompiledModelAPI を使用して、AOT コンパイルとオンデバイス コンパイルの実行をサポートします。- 設定の詳細については、Qualcomm AI Engine Direct をご覧ください。
- 最新情報については、LiteRT を使用して Qualcomm NPU のピーク パフォーマンスを引き出すをご覧ください。
MediaTek NeuroPilot
CompiledModelAPI を使用して、AOT コンパイルとオンデバイス コンパイルの実行をサポートします。- 設定の詳細については、MediaTek NeuroPilot をご覧ください。
- 最新情報については、MediaTek NPU と LiteRT: 次世代のオンデバイス AI を実現する をご覧ください。
Google Tensor
Google Tensor SDK は試験運用版です。こちらでお申込み願います。
Intel OpenVino
CompiledModelAPI を使用して、AOT コンパイルとオンデバイス コンパイルの実行をサポートします。- 設定の詳細については、Intel OpenVino をご覧ください。
AOT コンパイルとオンデバイス コンパイル
LiteRT NPU は、特定のデプロイ要件を満たすために、AOT コンパイルとオンデバイス コンパイルの両方をサポートしています。
- オフライン(AOT)コンパイル: ターゲット SoC がわかっている大規模で複雑なモデルに最適です。 事前コンパイルを行うと、初期化コストが大幅に削減され、ユーザーがアプリを起動したときのメモリ使用量が削減されます。
- オンライン(オンデバイス)コンパイル: JIT コンパイルとも呼ばれます。これは、小規模モデルのプラットフォームに依存しないモデル配信に最適です。モデルは初期化時にユーザーのデバイスでコンパイルされるため、追加の準備手順は必要ありませんが、初回実行時のコストが高くなります。
次のガイドでは、AOT コンパイルとオンデバイス コンパイルの両方でデプロイする方法を 3 つのステップで説明します。
ステップ 1: ターゲット NPU SoC の AOT コンパイル
LiteRT AOT(事前)コンパイラを使用して、.tflite モデルをサポートされている SoC にコンパイルできます。また、1 回のコンパイル プロセスで複数の SoC ベンダーとバージョンを同時にターゲットにすることもできます。詳細については、 この LiteRT AOT コンパイル ノートブックをご覧ください。 省略可能ですが、大規模なモデルでは、オンデバイスの初期化時間を短縮するために AOT コンパイルを強くおすすめします。この手順は、オンデバイス コンパイルでは必要ありません。
ステップ 2: Android の場合は Google Play でデプロイする
Android では、Google Play for On-device AI (PODAI)を使用して、モデルと NPU ランタイム ライブラリをアプリとともにデプロイします。
- オンデバイス コンパイルのモデルの場合: 元の .tflite モデルファイルをアプリの assets/ ディレクトリに直接追加します。
- 実装例については、 Segmentation オンデバイス コンパイル Kotlin アプリをご覧ください。
- AOT コンパイルのモデルの場合: LiteRT を使用して、コンパイル済みモデル
を 1 つの Google Play AI Pack にエクスポートします。
次に、AI パックを Google Play にアップロードして、正しいコンパイル済みモデルをユーザーのデバイスに自動的に配信します。
- コンパイル済みモデルを Play AI Pack にエクスポートする手順については、 LiteRT AOT コンパイル ノートブック をご覧ください。
- 実装例については、 Segmentation AOT コンパイル Kotlin アプリをご覧ください。
- NPU ランタイム ライブラリの場合は、Play Feature Delivery を使用して、 正しいランタイム ライブラリをユーザーのデバイスに配信します。
Play AI Pack と Play Feature Delivery を使用してデプロイする方法については、次のセクションをご覧ください。
Play AI Pack を使用して AOT モデルをデプロイする
次の手順では、Play AI Pack を使用して AOT コンパイル済みモデルをデプロイする方法について説明します。
プロジェクトに AI パックを追加する
AI パックを Gradle プロジェクトのルート ディレクトリにコピーして、AI パックを Gradle プロジェクトにインポートします。次に例を示します。
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
各 AI パックを 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
Gradle 構成に AI パックを追加する
生成された AI パックから device_targeting_configuration.xml をメインアプリ モジュールのディレクトリにコピーします。次に、settings.gradle.kts を更新します。
// my_app/setting.gradle.kts
...
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
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 パックを構成する
オンデマンド配信 を使用すると、実行時にモデルをリクエストできます。これは、特定のユーザーフローでのみモデルが必要な場合に便利です。モデルはアプリの内部ストレージ スペースにダウンロードされます。build.gradle.kts ファイルで Android AI Pack 機能を構成したら、デバイスの機能を確認します。PODAI からの
手順
とインストール時の配信とfast-follow 配信の手順もご覧ください。
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,
)
Play Feature Delivery を使用して NPU ランタイム ライブラリをデプロイする
Play Feature Delivery は、初回ダウンロード サイズを最適化するための複数の配信オプションをサポートしています。 これには、インストール時の配信、オンデマンド配信、条件付き配信、 即時配信が含まれます。ここでは、基本的なインストール時の配信ガイドを示します。
プロジェクトに NPU ランタイム ライブラリを追加する
AOT コンパイル用の litert_npu_runtime_libraries.zip またはオンデバイス コンパイル用の litert_npu_runtime_libraries_jit.zip をダウンロードし、プロジェクトのルート ディレクトリに展開します。
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
スクリプトを実行して、NPU サポート ライブラリをダウンロードします。たとえば、Qualcomm NPU の場合は次のコマンドを実行します。
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Gradle 構成に NPU ランタイム ライブラリを追加する
生成された AI パックから device_targeting_configuration.xml をメインアプリ モジュールのディレクトリにコピーします。次に、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")
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"))
...
}
ステップ 3: LiteRT ランタイムを使用して NPU で推論する
LiteRT は、特定の SoC バージョンに対する開発の複雑さを抽象化し、数行のコードで NPU 上でモデルを実行できるようにします。また、堅牢な組み込みフォールバック メカニズムも提供します。CPU、GPU、またはその両方をオプションとして指定できます。NPU が使用できない場合、LiteRT は自動的にそれらを使用します。AOT コンパイルもフォールバックをサポートしています。サポートされていないサブグラフが指定どおりに CPU または GPU でシームレスに実行される NPU での部分的な委任を提供します。
Kotlin で実行する
実装例については、次のデモアプリをご覧ください。
Android の依存関係を追加する
最新の LiteRT Maven パッケージを build.gradle の依存関係に追加できます。
dependencies {
...
implementation("com.google.ai.edge.litert:litert:+")
}
ランタイム統合
// 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()
C++ クロスプラットフォームで実行する
実装例については、 非同期セグメンテーション C++ アプリをご覧ください。
Bazel ビルドの依存関係
C++ ユーザーは、LiteRT NPU アクセラレーションを使用してアプリケーションの依存関係をビルドする必要があります。コア アプリケーション ロジック(main.cc など)をパッケージ化する cc_binary ルールには、次のランタイム コンポーネントが必要です。
- LiteRT C API 共有ライブラリ:
data属性には、 LiteRT C API 共有ライブラリ(//litert/c:litert_runtime_c_api_shared_lib) と、NPU のベンダー固有のディスパッチ共有オブジェクト (//litert/vendors/qualcomm/dispatch:dispatch_api_so)を含める必要があります。 - NPU 固有のバックエンド ライブラリ: たとえば、Android ホスト用の Qualcomm AI RT(QAIRT)
ライブラリ(
libQnnHtp.so、libQnnHtpPrepare.soなど)と、対応する Hexagon DSP ライブラリ(libQnnHtpV79Skel.so)。これにより、 LiteRT ランタイムは NPU に計算をオフロードできます。 - 属性の依存関係:
deps属性は、LiteRT のテンソル バッファ (//litert/cc:litert_tensor_buffer) や NPU ディスパッチ レイヤの API (//litert/vendors/qualcomm/dispatch:dispatch_api) など、コンパイル時の重要な依存関係にリンクします。これにより、アプリケーション コードは LiteRT を介して NPU とやり取りできます。 - モデルファイルとその他のアセット:
data属性を使用して含めます。
この設定により、コンパイルされたバイナリは、高速な ML 推論のために NPU を動的に読み込んで使用できます。
NPU 環境を設定する
一部の NPU バックエンドでは、ランタイムの依存関係またはライブラリが必要です。コンパイル済みモデル API を使用する場合、LiteRT は Environment オブジェクトを使用してこれらの要件を整理します。
適切な NPU ライブラリまたはドライバを見つけるには、次のコードを使用します。
// 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)));
ランタイム統合
次のコード スニペットは、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));
NPU アクセラレーションによるゼロコピー
ゼロコピーを使用すると、NPU は CPU が明示的にデータをコピーしなくても、独自のメモリ内のデータに直接アクセスできます。CPU メモリとの間でデータをコピーしないことで、ゼロコピーはエンドツーエンドのレイテンシを大幅に短縮できます。
次のコードは、AHardwareBuffer を使用してゼロコピー NPU を実装し、データを NPU に直接渡す例です。この実装では、CPU メモリへの高コストなラウンドトリップを回避し、推論のオーバーヘッドを大幅に削減します。
// 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();
複数の NPU 推論を連結する
複雑なパイプラインの場合は、複数の NPU 推論を連結できます。各ステップではアクセラレータに適したバッファが使用されるため、パイプラインは 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);
NPU オンデバイス コンパイルのキャッシュ
LiteRT は、.tflite モデルの NPU オンデバイス(JIT)コンパイルをサポートしています。JIT コンパイルは、モデルを事前にコンパイルすることができない場合に特に便利です。
ただし、JIT コンパイルでは、ユーザーが提供したモデルを NPU バイトコード命令にオンデマンドで変換するために、レイテンシとメモリのオーバーヘッドが発生する可能性があります。パフォーマンスへの影響を最小限に抑えるため、NPU コンパイル アーティファクトをキャッシュに保存できます。
キャッシュが有効になっている場合、LiteRT は必要な場合にのみモデルの再コンパイルをトリガーします。次に例を示します。
- ベンダーの NPU コンパイラ プラグインのバージョンが変更された。
- Android ビルドのフィンガープリントが変更された。
- ユーザーが提供したモデルが変更された。
- コンパイル オプションが変更された。
NPU コンパイルのキャッシュを有効にするには、環境オプションで CompilerCacheDir 環境タグを指定します。値は、アプリケーションの既存の書き込み可能なパスに設定する必要があります。
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));
レイテンシとメモリの削減例:
NPU コンパイルに必要な時間とメモリは、基盤となる NPU チップ、入力モデルの複雑さなど、いくつかの要因によって異なります。
次の表は、NPU コンパイルが必要な場合と、キャッシュによりコンパイルをスキップできる場合の、ランタイムの初期化時間とメモリ使用量を比較したものです。あるサンプル デバイスでは、次の結果が得られました。
| TFLite モデル | NPU コンパイルによるモデルの初期化 | キャッシュされたコンパイルによるモデルの初期化 | NPU コンパイルによる初期メモリ使用量 | キャッシュされたコンパイルによる初期メモリ |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7465.22 ミリ秒 | 198.34 ミリ秒 | 1525.24 MB | 355.07 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592.54 ミリ秒 | 166.47 ミリ秒 | 254.90 MB | 33.78 MB |
別のデバイスでは、次の結果が得られました。
| TFLite モデル | NPU コンパイルによるモデルの初期化 | キャッシュされたコンパイルによるモデルの初期化 | NPU コンパイルによる初期メモリ使用量 | キャッシュされたコンパイルによる初期メモリ |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2766.44 ミリ秒 | 379.86 ミリ秒 | 653.54 MB | 501.21 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784.14 ミリ秒 | 231.76 ミリ秒 | 113.14 MB | 67.49 MB |