LiteRT는 공급업체별 컴파일러, 런타임 또는 라이브러리 종속 항목을 탐색하라는 요청 없이 신경망 처리 장치 (NPU)를 사용할 수 있는 통합 인터페이스를 제공합니다. NPU 가속에 LiteRT를 사용하면 실시간 및 대형 모델 추론의 성능이 향상되고 제로 카피 하드웨어 버퍼 사용을 통해 메모리 복사가 최소화됩니다.
시작하기
- 클래식 ML 모델의 경우 다음 데모 애플리케이션을 참고하세요.
- 이미지 세분화 Kotlin 앱: 각각 AOT 컴파일 및 온디바이스(JIT) 컴파일의 예를 제공합니다.
- 비동기 세분화 C++ 앱: 동일한 앱에서 AOT 컴파일과 온디바이스 (JIT) 컴파일을 모두 보여줍니다.
- 대규모 언어 모델 (LLM)의 경우 LiteRT-LM을 사용하여 NPU에서 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는 실험용 액세스 단계에 있습니다. 여기에서 등록하세요.
AOT 및 온디바이스 컴파일
LiteRT NPU는 특정 배포 요구사항을 충족하기 위해 AOT와 기기 내 컴파일을 모두 지원합니다.
- 오프라인 (AOT) 컴파일: 타겟 SoC를 알고 있는 크고 복잡한 모델에 가장 적합합니다. 사전 컴파일은 사용자가 앱을 실행할 때 초기화 비용을 크게 줄이고 메모리 사용량을 낮춥니다.
- 온라인 (기기 내) 컴파일: JIT 컴파일이라고도 합니다. 이는 소규모 모델의 플랫폼에 구애받지 않는 모델 배포에 적합합니다. 이 모델은 초기화 중에 사용자의 기기에서 컴파일되므로 추가 준비 단계가 필요하지 않지만 첫 실행 비용이 더 높습니다.
다음 가이드에서는 3단계로 AOT 및 온디바이스 컴파일 모두에 배포하는 방법을 보여줍니다.
1단계: 타겟 NPU SoC의 AOT 컴파일
LiteRT AOT (ahead of time) 컴파일러를 사용하여 지원되는 SoC에 .tflite 모델을 컴파일할 수 있습니다. 단일 컴파일 프로세스 내에서 여러 SoC 공급업체와 버전을 동시에 타겟팅할 수도 있습니다. 자세한 내용은 이 LiteRT AOT 컴파일 노트북을 참고하세요. 선택사항이지만 대규모 모델의 경우 기기 내 초기화 시간을 줄이기 위해 AOT 컴파일을 사용하는 것이 좋습니다. 이 단계는 온디바이스 컴파일에는 필요하지 않습니다.
2단계: Android를 사용하는 경우 Google Play를 통해 배포
Android에서는 Google Play for On-device AI(PODAI)를 사용하여 앱과 함께 모델 및 NPU 런타임 라이브러리를 배포합니다.
- 온디바이스 컴파일 모델의 경우: 원본 .tflite 모델 파일을 앱의 assets/ 디렉터리에 직접 추가합니다.
- 세분화 온디바이스 컴파일 Kotlin 앱에서 구현 예를 참고하세요.
- AOT 컴파일 모델의 경우: LiteRT를 사용하여 컴파일된 모델을 단일 Google Play AI Pack으로 내보냅니다.
그런 다음 AI 팩을 Google Play에 업로드하여 컴파일된 올바른 모델을 사용자 기기에 자동으로 제공합니다.
- 컴파일된 모델을 Play AI Pack으로 내보내는 방법에 관한 안내는 LiteRT AOT 컴파일 노트북을 참고하세요.
- Segmentation AOT compilation Kotlin App에서 구현 예를 참고하세요.
- NPU 런타임 라이브러리의 경우 Play Feature Delivery를 사용하여 올바른 런타임 라이브러리를 사용자 기기에 배포합니다.
Play AI Pack 및 Play Feature Delivery를 사용하여 배포하는 방법은 다음 섹션을 참고하세요.
Play AI Pack으로 AOT 모델 배포
다음 단계에서는 Play AI Pack을 사용하여 AOT 컴파일된 모델을 배포하는 방법을 안내합니다.
프로젝트에 AI Pack 추가
AI 팩을 Gradle 프로젝트의 루트 디렉터리에 복사하여 Gradle 프로젝트로 가져옵니다. 예를 들면 다음과 같습니다.
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
각 AI Pack을 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 Pack 구성
주문형 전송을 사용하면 런타임에 모델을 요청할 수 있습니다. 이는 특정 사용자 흐름에만 모델이 필요한 경우에 유용합니다. 모델이 앱의 내부 저장소 공간에 다운로드됩니다. build.gradle.kts 파일에 구성된 Android AI Pack 기능으로 기기 기능을 확인합니다.
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,
)
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 가속으로 애플리케이션의 종속 항목을 빌드해야 합니다. 핵심 애플리케이션 로직(예:cc_binary main.cc)에는 다음 런타임 구성요소가 필요합니다.
- 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 호스트 (예:
libQnnHtp.so,libQnnHtpPrepare.so)용 Qualcomm AI RT (QAIRT) 라이브러리 및 해당 Hexagon DSP 라이브러리 (libQnnHtpV79Skel.so)입니다. 이를 통해 LiteRT 런타임이 NPU에 계산을 오프로드할 수 있습니다. - 속성 종속 항목:
deps속성은 LiteRT의 텐서 버퍼(//litert/cc:litert_tensor_buffer) 및 NPU 디스패치 레이어용 API(//litert/vendors/qualcomm/dispatch:dispatch_api)와 같은 필수 컴파일 시간 종속 항목에 연결됩니다. 이를 통해 애플리케이션 코드가 LiteRT를 통해 NPU와 상호작용할 수 있습니다. - 모델 파일 및 기타 애셋:
data속성을 통해 포함됩니다.
이 설정을 사용하면 컴파일된 바이너리가 가속화된 머신러닝 추론을 위해 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 가속을 통한 제로 카피
무사본을 사용하면 CPU가 해당 데이터를 명시적으로 복사할 필요 없이 NPU가 자체 메모리에서 데이터에 직접 액세스할 수 있습니다. 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 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 |
Google은 다른 기기에서 다음 정보를 획득합니다.
| TFLite 모델 | NPU 컴파일을 사용한 모델 초기화 | 캐시된 컴파일로 모델 초기화 | NPU 컴파일을 사용한 메모리 사용량 초기화 | 캐시된 컴파일로 메모리 초기화 |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2,766.44ms | 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 |