LiteRT มีอินเทอร์เฟซแบบรวมเพื่อใช้หน่วยประมวลผลนิวรอน (NPU) โดยไม่ต้องบังคับให้คุณไปยังคอมไพเลอร์ รันไทม์ หรือการอ้างอิงไลบรารี เฉพาะของผู้ให้บริการ การใช้ LiteRT เพื่อการเร่งความเร็ว NPU จะช่วยเพิ่มประสิทธิภาพสำหรับการอนุมานแบบเรียลไทม์และแบบโมเดลขนาดใหญ่ รวมถึงลดการคัดลอกหน่วยความจำผ่านการใช้บัฟเฟอร์ฮาร์ดแวร์แบบไม่คัดลอก
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน โปรดดูคู่มือภาพรวมของ NPU
- สําหรับโมเดล ML แบบคลาสสิก โปรดดูขั้นตอนการแปลง การคอมไพล์ และการติดตั้งใช้งานในส่วนต่อไปนี้
- สำหรับโมเดลภาษาขนาดใหญ่ (LLM) ให้ใช้เฟรมเวิร์ก LiteRT-LM ดังนี้
ดูตัวอย่างการใช้งาน LiteRT ที่รองรับ NPU ได้ในแอปพลิเคชันเดโมต่อไปนี้
ผู้ให้บริการ NPU
LiteRT รองรับการเร่งความเร็ว NPU กับผู้ให้บริการต่อไปนี้
Qualcomm AI Engine Direct
- ระบบรองรับเส้นทางการดำเนินการ AOT และการคอมไพล์ในอุปกรณ์ผ่าน Compiled Model API
- ดูรายละเอียดการตั้งค่าได้ที่ Qualcomm AI Engine Direct
MediaTek NeuroPilot
- เส้นทางการดำเนินการ AOT และ JIT ได้รับการรองรับผ่าน Compiled Model API
- ดูรายละเอียดการตั้งค่าได้ที่ MediaTek NeuroPilot
แปลงและคอมไพล์โมเดลสำหรับ NPU
หากต้องการใช้การเร่งความเร็ว NPU กับ LiteRT คุณต้องแปลงโมเดลเป็น รูปแบบไฟล์ LiteRT และคอมไพล์เพื่อการใช้งาน NPU ในอุปกรณ์ คุณสามารถใช้คอมไพเลอร์ LiteRT AOT (Ahead-Of-Time) เพื่อคอมไพล์โมเดลเป็น AI Pack ซึ่งจะรวม โมเดลที่คอมไพล์แล้วเข้ากับการกำหนดค่าการกำหนดเป้าหมายอุปกรณ์ ซึ่งจะยืนยันว่า โมเดลได้รับการแสดงอย่างถูกต้องในอุปกรณ์โดยขึ้นอยู่กับว่าอุปกรณ์นั้นติดตั้งหรือ ได้รับการเพิ่มประสิทธิภาพสำหรับ SoC ใดโดยเฉพาะ
หลังจากแปลงและคอมไพล์โมเดลแล้ว คุณจะใช้ Play สำหรับ AI ในอุปกรณ์ (PODAI) เพื่ออัปโหลด โมเดลไปยัง Google Play และส่งโมเดลไปยังอุปกรณ์ผ่านเฟรมเวิร์ก AI แบบออนดีมานด์ ได้
ใช้สมุดบันทึกการคอมไพล์ AOT ของ LiteRT เพื่อดูคำแนะนำแบบครบวงจรในการแปลงและคอมไพล์โมเดลสำหรับ NPU
[AOT เท่านั้น] ติดตั้งใช้งานด้วยแพ็ก AI ของ Play
หลังจากแปลงโมเดลและคอมไพล์แพ็ก AI แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อ ติดตั้งใช้งานแพ็ก AI กับ Google Play
นำเข้าแพ็ก 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
เพิ่มไลบรารีรันไทม์ของ NPU ลงในโปรเจ็กต์
ดาวน์โหลด litert_npu_runtime_libraries.zip สำหรับ AOT หรือ litert_npu_runtime_libraries_jit.zip สำหรับ JIT แล้วแตกไฟล์ในไดเรกทอรีรากของโปรเจ็กต์
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 เช่น เรียกใช้คำสั่งต่อไปนี้สำหรับ NPU ของ Qualcomm
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
เพิ่มแพ็ก AI และไลบรารีรันไทม์ของ NPU ลงในการกำหนดค่า Gradle
คัดลอก device_targeting_configuration.xml จากแพ็ก AI ที่สร้างขึ้นไปยัง
ไดเรกทอรีของโมดูลแอปหลัก จากนั้นอัปเดต settings.gradle.kts โดยทำดังนี้
// my_app/setting.gradle.kts
...
// [AOT only]
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
// 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
}
}
// [AOT Only]
// AI Packs
assetPacks.add(":ai_packs:my_model")
assetPacks.add(":ai_packs:my_model_mtk")
// 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"))
...
}
[AOT เท่านั้น] ใช้การติดตั้งใช้งานตามต้องการ
เมื่อกำหนดค่าฟีเจอร์ Android AI Pack ในไฟล์ build.gradle.kts แล้ว
ให้ตรวจสอบความสามารถของอุปกรณ์และใช้ NPU ในอุปกรณ์ที่รองรับ โดยใช้ GPU และ CPU
เป็นตัวเลือกสำรอง
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,
)
สร้าง CompiledModel สำหรับโหมด JIT
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val compiledModel = CompiledModel.create(
"model/my_model.tflite",
CompiledModel.Options(Accelerator.NPU),
env,
)
การอนุมานใน NPU โดยใช้ LiteRT ใน Kotlin
หากต้องการเริ่มใช้ตัวเร่ง NPU ให้ส่งพารามิเตอร์ NPU เมื่อสร้าง
โมเดลที่คอมไพล์แล้ว (CompiledModel)
ข้อมูลโค้ดต่อไปนี้แสดงการติดตั้งใช้งานพื้นฐานของกระบวนการทั้งหมดใน Kotlin
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()
inputBuffers[0].writeFloat(FloatArray(data_size) { data_value })
model.run(inputBuffers, outputBuffers)
val outputFloatArray = outputBuffers[0].readFloat()
inputBuffers.forEach { it.close() }
outputBuffers.forEach { it.close() }
model.close()
การอนุมานใน NPU โดยใช้ LiteRT ใน C++
สร้างทรัพยากร Dependency
ผู้ใช้ 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: เช่น ไลบรารี Qualcomm AI RT (QAIRT)
สำหรับโฮสต์ Android (เช่น
libQnnHtp.so,libQnnHtpPrepare.so) และไลบรารี Hexagon DSP ที่เกี่ยวข้อง (libQnnHtpV79Skel.so) ซึ่งจะช่วยให้รันไทม์ LiteRT สามารถส่งต่อการคำนวณไปยัง NPU ได้ - การขึ้นต่อกันของแอตทริบิวต์: แอตทริบิวต์
depsจะลิงก์กับทรัพยากร Dependency ที่จำเป็นในเวลาคอมไพล์ เช่น บัฟเฟอร์เทนเซอร์ของ LiteRT (//litert/cc:litert_tensor_buffer) และ API สำหรับเลเยอร์การจัดส่ง NPU (//litert/vendors/qualcomm/dispatch:dispatch_api) ซึ่งจะช่วยให้โค้ดแอปพลิเคชัน โต้ตอบกับ NPU ผ่าน LiteRT ได้ - ไฟล์โมเดลและชิ้นงานอื่นๆ: รวมไว้ผ่านแอตทริบิวต์
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
การใช้การคัดลอกเป็นศูนย์ช่วยให้ NPU เข้าถึงข้อมูลในหน่วยความจำของตัวเองได้โดยตรงโดยไม่ต้องให้ CPU คัดลอกข้อมูลนั้นอย่างชัดเจน การไม่คัดลอกข้อมูลไปยังและจากหน่วยความจำ CPU จะช่วยลดเวลาในการตอบสนองแบบต้นทางถึงปลายทางได้อย่างมาก
โค้ดต่อไปนี้เป็นตัวอย่างการติดตั้งใช้งาน NPU แบบไม่คัดลอกด้วย
AHardwareBuffer ซึ่งส่งข้อมูลไปยัง 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 รองรับการคอมไพล์แบบทันที (JIT) ของโมเดล .tflite ใน NPU การคอมไพล์ 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 | model init with NPU compilation | model init with cached compilation | หน่วยความจำที่ใช้เริ่มต้นเมื่อคอมไพล์ 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 | model init with NPU compilation | model init with cached compilation | หน่วยความจำที่ใช้เริ่มต้นเมื่อคอมไพล์ 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 |