LiteRT cung cấp một giao diện hợp nhất để sử dụng Đơn vị xử lý thần kinh (NPU) mà không yêu cầu bạn điều hướng các trình biên dịch, thời gian chạy hoặc phần phụ thuộc thư viện dành riêng cho nhà cung cấp. Việc sử dụng LiteRT để tăng tốc NPU giúp tăng hiệu suất cho việc suy luận theo thời gian thực và của mô hình lớn, đồng thời giảm thiểu việc sao chép bộ nhớ thông qua việc sử dụng bộ đệm phần cứng không sao chép.
Bắt đầu
- Đối với các mô hình học máy cũ, hãy xem các ứng dụng minh hoạ sau.
- Ứng dụng phân đoạn hình ảnh bằng Kotlin: cung cấp các ví dụ tương ứng cho biên dịch AOT và biên dịch trên thiết bị (JIT).
- Ứng dụng phân đoạn không đồng bộ C++: minh hoạ cả tính năng biên dịch AOT và biên dịch trên thiết bị (JIT) trong cùng một ứng dụng.
- Đối với Mô hình ngôn ngữ lớn (LLM), hãy xem hướng dẫn về cách thực thi LLM trên NPU bằng LiteRT-LM.
Nhà cung cấp NPU
LiteRT hỗ trợ tính năng tăng tốc NPU với các nhà cung cấp sau:
Qualcomm AI Engine Direct
- Hỗ trợ AOT và thực thi biên dịch trên thiết bị thông qua API
CompiledModel. - Hãy xem phần Qualcomm AI Engine Direct để biết thông tin chi tiết về cách thiết lập.
- Hãy xem bài viết Khai thác hiệu suất tối đa trên NPU Qualcomm bằng LiteRT để nắm được thông tin mới nhất.
MediaTek NeuroPilot
- Hỗ trợ AOT và thực thi biên dịch trên thiết bị thông qua API
CompiledModel. - Hãy xem phần MediaTek NeuroPilot để biết thông tin chi tiết về cách thiết lập.
- Hãy xem bài viết NPU và LiteRT của MediaTek: Hỗ trợ AI thế hệ tiếp theo trên thiết bị để nắm được thông tin mới nhất.
Google Tensor
Google Tensor SDK đang ở giai đoạn truy cập thử nghiệm. Hãy đăng ký ở đây.
Biên dịch AOT và biên dịch trên thiết bị
NPU LiteRT hỗ trợ cả AOT và quá trình biên dịch trên thiết bị để đáp ứng các yêu cầu triển khai cụ thể của bạn:
- Biên dịch ngoại tuyến (AOT): Phương pháp này phù hợp nhất với các mô hình lớn, phức tạp mà SoC mục tiêu đã biết. Việc biên dịch trước thời gian sẽ giảm đáng kể chi phí khởi tạo và giảm mức sử dụng bộ nhớ khi người dùng chạy ứng dụng của bạn.
- Biên dịch trực tuyến (trên thiết bị): Còn được gọi là biên dịch JIT. Đây là lựa chọn lý tưởng để phân phối mô hình nhỏ không phụ thuộc vào nền tảng. Mô hình này được biên dịch trên thiết bị của người dùng trong quá trình khởi tạo, không yêu cầu bước chuẩn bị bổ sung nhưng phải chịu chi phí chạy lần đầu cao hơn.
Hướng dẫn sau đây cho biết cách triển khai cho cả AOT và biên dịch trên thiết bị trong 3 bước.
Bước 1: Biên dịch AOT cho SoC NPU mục tiêu
Bạn có thể sử dụng Trình biên dịch AOT (trước thời gian chạy) LiteRT để biên dịch mô hình .tflite sang các SoC được hỗ trợ. Bạn cũng có thể nhắm đến nhiều nhà cung cấp và phiên bản SoC cùng lúc trong một quy trình biên dịch duy nhất. Xem thêm thông tin chi tiết trong sổ tay Biên dịch AOT LiteRT này. Mặc dù không bắt buộc, nhưng bạn nên sử dụng tính năng biên dịch AOT cho các mô hình lớn hơn để giảm thời gian khởi động trên thiết bị. Bạn không cần thực hiện bước này để biên dịch trên thiết bị.
Bước 2: Triển khai bằng Google Play nếu bạn dùng Android
Trên Android, hãy sử dụng Google Play cho AI trên thiết bị (PODAI) để triển khai mô hình và thư viện thời gian chạy NPU bằng ứng dụng của bạn.
- Đối với các mô hình biên dịch trên thiết bị: thêm trực tiếp tệp mô hình .tflite gốc vào thư mục assets/ của ứng dụng.
- Hãy xem ví dụ về cách triển khai trong Ứng dụng Kotlin biên dịch phân đoạn trên thiết bị.
- Đối với các mô hình biên dịch AOT: hãy dùng LiteRT để xuất các mô hình đã biên dịch vào một Gói AI Play duy nhất của Google.
Sau đó, bạn tải gói AI lên Google Play để tự động phân phối các mô hình đã biên dịch chính xác đến thiết bị của người dùng.
- Hãy xem Sổ tay biên dịch AOT LiteRT để biết hướng dẫn về cách xuất các mô hình đã biên dịch vào một Play AI Pack.
- Xem ví dụ về cách triển khai trong Ứng dụng Kotlin biên dịch AOT phân đoạn.
- Đối với các thư viện thời gian chạy NPU, hãy sử dụng Play Feature Delivery để phân phối các thư viện thời gian chạy phù hợp cho thiết bị của người dùng.
Hãy xem các phần sau đây về cách triển khai bằng Play AI Pack và Play Feature Delivery.
Triển khai các mô hình AOT bằng Play AI Pack
Các bước sau đây sẽ hướng dẫn bạn triển khai các mô hình được biên dịch AOT bằng Gói AI của Play.
Thêm gói AI vào dự án
Nhập Gói AI vào dự án Gradle bằng cách sao chép(các) Gói AI vào thư mục gốc của dự án Gradle. Ví dụ:
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Thêm từng Gói AI vào cấu hình bản dựng 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
Thêm Gói AI vào cấu hình Gradle
Sao chép device_targeting_configuration.xml từ Gói AI đã tạo vào thư mục của mô-đun ứng dụng chính. Sau đó, hãy cập nhật settings.gradle.kts:
// my_app/setting.gradle.kts
...
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
Cập nhật 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")
}
Định cấu hình AI Pack để phân phối theo yêu cầu
Tính năng phân phối theo yêu cầu cho phép bạn yêu cầu mô hình tại thời gian chạy. Tính năng này hữu ích nếu mô hình chỉ cần thiết cho một số quy trình của người dùng. Mô hình của bạn sẽ được tải xuống bộ nhớ trong của ứng dụng. Khi tính năng Gói AI Android được định cấu hình trong tệp build.gradle.kts, hãy kiểm tra các chức năng của thiết bị.
Xem thêm hướng dẫn về phân phối tại thời gian cài đặt và phân phối tiếp nối nhanh từ 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,
)
Triển khai các thư viện thời gian chạy NPU bằng Play Feature Delivery
Play Feature Delivery hỗ trợ nhiều lựa chọn phân phối để tối ưu hoá kích thước tải xuống ban đầu, bao gồm cả phân phối tại thời điểm cài đặt, phân phối theo yêu cầu, phân phối có điều kiện và phân phối tức thì. Ở đây, chúng tôi trình bày hướng dẫn cơ bản về tính năng phân phối khi cài đặt.
Thêm thư viện thời gian chạy NPU vào dự án
Tải litert_npu_runtime_libraries.zip xuống để biên dịch AOT hoặc litert_npu_runtime_libraries_jit.zip để biên dịch trên thiết bị, rồi giải nén tệp này trong thư mục gốc của dự án:
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
Chạy tập lệnh để tải các thư viện hỗ trợ NPU xuống. Ví dụ: hãy chạy lệnh sau cho NPU Qualcomm:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Thêm các thư viện thời gian chạy NPU vào cấu hình Gradle
Sao chép device_targeting_configuration.xml từ Gói AI đã tạo vào thư mục của mô-đun ứng dụng chính. Sau đó, hãy cập nhật 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")
Cập nhật 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"))
...
}
Bước 3: Suy luận trên NPU bằng cách sử dụng Môi trường thời gian chạy LiteRT
LiteRT loại bỏ sự phức tạp của việc phát triển dựa trên các phiên bản SoC cụ thể, cho phép bạn chạy mô hình trên NPU chỉ bằng một vài dòng mã. LiteRT cũng cung cấp một cơ chế dự phòng mạnh mẽ, tích hợp sẵn: bạn có thể chỉ định CPU, GPU hoặc cả hai làm lựa chọn và LiteRT sẽ tự động sử dụng các lựa chọn này nếu NPU không có sẵn. Rất may là quá trình biên dịch AOT cũng hỗ trợ dự phòng. Thư viện này cung cấp tính năng uỷ quyền một phần trên NPU, trong đó các đồ thị con không được hỗ trợ sẽ chạy liền mạch trên CPU hoặc GPU theo chỉ định.
Chạy trong Kotlin
Hãy xem ví dụ về cách triển khai trong các ứng dụng minh hoạ sau:
Thêm các phần phụ thuộc Android
Bạn có thể thêm gói LiteRT Maven mới nhất vào các phần phụ thuộc build.gradle:
dependencies {
...
implementation("com.google.ai.edge.litert:litert:+")
}
Tích hợp trong thời gian chạy
// 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()
Chạy trên nhiều nền tảng C++
Hãy xem ví dụ về cách triển khai trong Ứng dụng phân đoạn không đồng bộ bằng C++.
Phần phụ thuộc của bản dựng Bazel
Người dùng C++ phải tạo các phần phụ thuộc của ứng dụng bằng tính năng tăng tốc NPU LiteRT. Quy tắc cc_binary đóng gói logic ứng dụng cốt lõi (ví dụ: main.cc) yêu cầu các thành phần thời gian chạy sau:
- Thư viện dùng chung LiteRT C API: thuộc tính
dataphải bao gồm thư viện dùng chung LiteRT C API (//litert/c:litert_runtime_c_api_shared_lib) và đối tượng dùng chung điều phối dành riêng cho nhà cung cấp cho NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so). - Thư viện phụ trợ dành riêng cho NPU: Ví dụ: thư viện Qualcomm AI RT (QAIRT) cho máy chủ lưu trữ Android (chẳng hạn như
libQnnHtp.so,libQnnHtpPrepare.so) và thư viện Hexagon DSP tương ứng (libQnnHtpV79Skel.so). Điều này đảm bảo rằng thời gian chạy LiteRT có thể giảm tải các phép tính cho NPU. - Phụ thuộc vào thuộc tính: thuộc tính
depsliên kết với các phần phụ thuộc thiết yếu trong thời gian biên dịch, chẳng hạn như bộ đệm tensor của LiteRT (//litert/cc:litert_tensor_buffer) và API cho lớp điều phối NPU (//litert/vendors/qualcomm/dispatch:dispatch_api). Điều này cho phép mã ứng dụng của bạn tương tác với NPU thông qua LiteRT. - Tệp mô hình và các thành phần khác: Được thêm thông qua thuộc tính
data.
Chế độ thiết lập này cho phép tệp nhị phân đã biên dịch của bạn tải và sử dụng NPU một cách linh hoạt để suy luận học máy có tăng tốc.
Thiết lập môi trường NPU
Một số phần phụ trợ NPU yêu cầu các thư viện hoặc phần phụ thuộc thời gian chạy. Khi sử dụng API mô hình được biên dịch, LiteRT sẽ sắp xếp các yêu cầu này thông qua một đối tượng Environment.
Hãy dùng mã sau để tìm các thư viện hoặc trình điều khiển NPU phù hợp:
// 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)));
Tích hợp trong thời gian chạy
Đoạn mã sau đây cho thấy cách triển khai cơ bản của toàn bộ quy trình bằng 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));
Sao chép mà không cần tăng tốc NPU
Việc sử dụng tính năng sao chép bằng 0 cho phép NPU truy cập trực tiếp vào dữ liệu trong bộ nhớ riêng mà không cần CPU sao chép dữ liệu đó một cách rõ ràng. Bằng cách không sao chép dữ liệu đến và đi từ bộ nhớ CPU, tính năng sao chép bằng 0 có thể giảm đáng kể độ trễ từ đầu đến cuối.
Đoạn mã sau đây là một ví dụ về việc triển khai NPU Zero-Copy bằng AHardwareBuffer, truyền dữ liệu trực tiếp đến NPU. Việc triển khai này tránh các chuyến khứ hồi tốn kém đến bộ nhớ CPU, giúp giảm đáng kể chi phí suy luận.
// 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();
Chuỗi nhiều suy luận NPU
Đối với các quy trình phức tạp, bạn có thể liên kết nhiều suy luận NPU. Vì mỗi bước sử dụng một vùng đệm thân thiện với bộ tăng tốc, nên quy trình của bạn chủ yếu nằm trong bộ nhớ do NPU quản lý:
// 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);
Lưu vào bộ nhớ đệm quá trình biên dịch trên thiết bị NPU
LiteRT hỗ trợ quá trình biên dịch NPU trên thiết bị (còn gọi là JIT) của các mô hình .tflite. Tính năng biên dịch JIT có thể đặc biệt hữu ích trong những trường hợp không thể biên dịch mô hình trước.
Tuy nhiên, quá trình biên dịch JIT có thể đi kèm với một số độ trễ và mức sử dụng bộ nhớ để dịch mô hình do người dùng cung cấp thành các chỉ dẫn mã byte NPU theo yêu cầu. Để giảm thiểu tác động đến hiệu suất, bạn có thể lưu vào bộ nhớ đệm các cấu phần phần mềm biên dịch NPU.
Khi bộ nhớ đệm được bật, LiteRT sẽ chỉ kích hoạt quá trình biên dịch lại mô hình khi cần, ví dụ:
- Phiên bản trình bổ trợ trình biên dịch NPU của nhà cung cấp đã thay đổi;
- Vân tay số của bản dựng Android đã thay đổi;
- Mô hình do người dùng cung cấp đã thay đổi;
- Các lựa chọn biên dịch đã thay đổi.
Để bật tính năng lưu vào bộ nhớ đệm quá trình biên dịch NPU, hãy chỉ định thẻ môi trường CompilerCacheDir trong các lựa chọn về môi trường. Giá trị phải được đặt thành một đường dẫn có thể ghi hiện có của ứng dụng.
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));
Ví dụ về độ trễ và mức tiết kiệm bộ nhớ:
Thời gian và bộ nhớ cần thiết để biên dịch NPU có thể khác nhau tuỳ thuộc vào một số yếu tố, chẳng hạn như chip NPU cơ bản, độ phức tạp của mô hình đầu vào, v.v.
Bảng sau đây so sánh thời gian khởi tạo thời gian chạy và mức tiêu thụ bộ nhớ khi cần biên dịch NPU so với khi có thể bỏ qua quá trình biên dịch do lưu vào bộ nhớ đệm. Trên một thiết bị mẫu, chúng ta sẽ có được những thông tin sau:
| Mô hình TFLite | khởi tạo mô hình bằng quá trình biên dịch NPU | khởi tạo mô hình bằng quá trình biên dịch được lưu vào bộ nhớ đệm | mức sử dụng bộ nhớ khởi động với quá trình biên dịch NPU | khởi động bộ nhớ bằng quá trình biên dịch được lưu vào bộ nhớ đệm |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7465,22 mili giây | 198,34 mili giây | 1525,24 MB | 355,07 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592,54 mili giây | 166,47 mili giây | 254,90 MB | 33,78 MB |
Trên một thiết bị khác, chúng tôi thu thập những thông tin sau:
| Mô hình TFLite | khởi tạo mô hình bằng quá trình biên dịch NPU | khởi tạo mô hình bằng quá trình biên dịch được lưu vào bộ nhớ đệm | mức sử dụng bộ nhớ khởi động với quá trình biên dịch NPU | khởi động bộ nhớ bằng quá trình biên dịch được lưu vào bộ nhớ đệm |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2766,44 mili giây | 379,86 mili giây | 653,54 MB | 501,21 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784,14 mili giây | 231,76 mili giây | 113,14 MB | 67,49 MB |