توفّر LiteRT واجهة موحّدة لاستخدام وحدات المعالجة العصبية (NPU) بدون الحاجة إلى استخدام برامج التجميع أو أوقات التشغيل أو تبعيات المكتبة الخاصة بمورّد معيّن. يؤدي استخدام LiteRT لتسريع NPU إلى تحسين الأداء في الاستدلال في الوقت الفعلي والنماذج الكبيرة، كما يقلّل من عمليات نسخ الذاكرة من خلال استخدام مخزن مؤقت للأجهزة بدون نسخ.
البدء
للبدء، اطّلِع على دليل النظرة العامة حول NPU:
- بالنسبة إلى نماذج تعلُّم الآلة التقليدية، راجِع الأقسام التالية للاطّلاع على خطوات التحويل والتجميع والنشر.
- بالنسبة إلى النماذج اللغوية الكبيرة (LLMs)، استخدِم إطار عمل LiteRT-LM:
للاطّلاع على أمثلة على عمليات تنفيذ LiteRT مع إمكانية استخدام وحدة المعالجة العصبية، يُرجى الرجوع إلى التطبيقات التجريبية التالية:
مورّدو وحدات المعالجة العصبية
تتوافق LiteRT مع تسريع وحدة المعالجة العصبية (NPU) من خلال المورّدين التاليين:
Qualcomm AI Engine Direct
- تتوفّر مسارات تنفيذ الترجمة الفورية والترجمة على الجهاز من خلال Compiled Model API.
- اطّلِع على Qualcomm AI Engine Direct لمعرفة تفاصيل الإعداد.
MediaTek NeuroPilot
- تتوفّر مسارات تنفيذ AOT وJIT من خلال Compiled Model API.
- راجِع MediaTek NeuroPilot لمعرفة تفاصيل الإعداد.
تحويل النماذج وتجميعها لوحدة المعالجة العصبية
لاستخدام تسريع وحدة المعالجة العصبية (NPU) مع LiteRT، يجب تحويل النماذج إلى تنسيق ملف LiteRT وتجميعها لاستخدامها على الجهاز. يمكنك استخدام برنامج LiteRT AOT (الترجمة المسبقة) لتجميع النماذج في حزمة الذكاء الاصطناعي، والتي تجمع النماذج المجمّعة مع إعدادات استهداف الأجهزة. يتحقّق ذلك من عرض النماذج بشكل صحيح على الأجهزة استنادًا إلى ما إذا كانت مجهّزة أو محسّنة لأنظمة SoC معيّنة.
بعد تحويل النماذج وتجميعها، يمكنك استخدام Play للذكاء الاصطناعي على الجهاز فقط (PODAI) لتحميل النماذج إلى Google Play وتوفيرها للأجهزة من خلال إطار عمل On-Demand AI.
استخدِم دفتر ملاحظات تجميع AOT في LiteRT للحصول على دليل شامل حول تحويل النماذج وتجميعها لوحدة المعالجة العصبية.
[نظام التشغيل Android فقط] النشر باستخدام "حزمة الذكاء الاصطناعي في Play"
بعد تحويل النموذج وتجميع حزمة الذكاء الاصطناعي، اتّبِع الخطوات التالية لنشر حزمة الذكاء الاصطناعي على Google Play.
استيراد حِزم الذكاء الاصطناعي إلى مشروع 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
إضافة مكتبات وقت تشغيل وحدة المعالجة العصبية إلى المشروع
نزِّل 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) من Qualcomm:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
إضافة حِزم الذكاء الاصطناعي ومكتبات وقت تشغيل وحدة المعالجة العصبية إلى إعدادات Gradle
انسخ device_targeting_configuration.xml من حِزم الذكاء الاصطناعي التي تم إنشاؤها إلى دليل وحدة تطبيقك الرئيسية. بعد ذلك، عدِّل 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" في ملف 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 لوضع "التجميع أثناء التنفيذ"
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val compiledModel = CompiledModel.create(
"model/my_model.tflite",
CompiledModel.Options(Accelerator.NPU),
env,
)
الاستدلال على وحدة المعالجة العصبية باستخدام 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()
الاستدلال على وحدة معالجة عصبية باستخدام LiteRT في C++
إنشاء العناصر التابعة
يجب أن ينشئ مستخدمو C++ تبعيات التطبيق باستخدام تسريع LiteRT NPU
قاعدة cc_binary التي تحزّم منطق التطبيق الأساسي
(على سبيل المثال، يتطلّب main.cc) مكوّنات وقت التشغيل التالية:
- مكتبة LiteRT C API المشترَكة: يجب أن تتضمّن السمة
dataمكتبة LiteRT C API المشترَكة (//litert/c:litert_runtime_c_api_shared_lib) وكائن الإرسال المشترَك الخاص بالمورّد لوحدة المعالجة العصبية (//litert/vendors/qualcomm/dispatch:dispatch_api_so). - مكتبات الخلفية الخاصة بوحدة المعالجة العصبية: على سبيل المثال، مكتبات Qualcomm AI RT (QAIRT) الخاصة بمضيف Android (مثل
libQnnHtp.soوlibQnnHtpPrepare.so) ومكتبة Hexagon DSP المقابلة (libQnnHtpV79Skel.so). يضمن ذلك إمكانية إحالة عمليات الحساب إلى وحدة المعالجة العصبية في وقت التشغيل LiteRT. - عمليات الربط بين السمات: تربط السمة
depsعمليات الربط ببعض العناصر الأساسية التي تعتمد عليها عملية الترجمة، مثل مخزن موتر LiteRT (//litert/cc:litert_tensor_buffer) وواجهة برمجة التطبيقات الخاصة بطبقة الإرسال في وحدة المعالجة العصبية (//litert/vendors/qualcomm/dispatch:dispatch_api)، ما يتيح لرمز تطبيقك التفاعل مع وحدة المعالجة العصبية من خلال LiteRT. - ملفات التصاميم ومواد العرض الأخرى: يتم تضمينها من خلال السمة
data.
يتيح هذا الإعداد للرمز الثنائي المجمَّع تحميل وحدة المعالجة العصبية واستخدامها بشكل ديناميكي لإجراء استنتاج سريع في ما يخص تعلُّم الآلة.
إعداد بيئة وحدة معالجة عصبية
تتطلّب بعض الخلفيات البرمجية لوحدة المعالجة العصبية (NPU) مكتبات أو تبعيات وقت التشغيل. عند استخدام واجهة برمجة التطبيقات الخاصة بالنماذج المجمّعة، تنظّم LiteRT هذه المتطلبات من خلال عنصر Environment.
استخدِم الرمز التالي للعثور على مكتبات أو برامج تشغيل وحدة المعالجة العصبية المناسبة:
// 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) إلى نسخ هذه البيانات بشكل صريح. من خلال عدم نسخ البيانات إلى ذاكرة وحدة المعالجة المركزية ومنها، يمكن أن تقلّل عملية النسخ بدون وسيط بشكل كبير من وقت الاستجابة من البداية إلى النهاية.
يوضّح الرمز التالي مثالاً على تنفيذ Zero-Copy NPU باستخدام
AHardwareBuffer، مع تمرير البيانات مباشرةً إلى وحدة المعالجة العصبية. يتجنّب هذا التنفيذ
عمليات نقل البيانات المكلفة إلى ذاكرة وحدة المعالجة المركزية، ما يقلّل بشكل كبير من تكلفة الاستدلال.
// 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();
تسلسل استنتاجات متعددة من وحدة المعالجة العصبية
بالنسبة إلى خطوط الإنتاج المعقّدة، يمكنك ربط عدة استنتاجات من وحدة المعالجة العصبية. بما أنّ كل خطوة تستخدم مخزنًا مؤقتًا متوافقًا مع أداة التسريع، يظل خط أنابيبك في الغالب في الذاكرة التي تديرها وحدة المعالجة العصبية:
// 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);
التخزين المؤقت للتجميع في الوقت الفعلي في وحدة المعالجة العصبية
تتيح LiteRT تجميع نماذج .tflite أثناء التشغيل (JIT) على وحدة المعالجة العصبية (NPU). يمكن أن تكون عملية الترجمة الفورية مفيدة بشكل خاص في الحالات التي لا يمكن فيها ترجمة النموذج مسبقًا.
ومع ذلك، قد تتسبّب عملية الترجمة الفورية في بعض التأخيرات واستهلاك الذاكرة، وذلك لترجمة النموذج الذي يقدّمه المستخدم إلى تعليمات برمجية ثنائية لوحدة المعالجة العصبية عند الطلب. للحدّ من التأثير في الأداء، يمكن تخزين عناصر تجميع وحدة المعالجة العصبية مؤقتًا.
عند تفعيل التخزين المؤقت، لن يؤدي LiteRT إلى إعادة تجميع النموذج إلا عند الحاجة، مثلاً:
- تغيَّر إصدار إضافة برنامج التجميع NPU الخاص بالمورّد
- تغيّر الملف المرجعي لإصدار Android.
- تغيّر النموذج الذي يقدّمه المستخدم؛
- تم تغيير خيارات التجميع.
لتفعيل التخزين المؤقت لتجميع وحدة المعالجة العصبية، حدِّد علامة 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) وعند إمكانية تخطّي التجميع بسبب التخزين المؤقت. على أحد الأجهزة النموذجية، نحصل على ما يلي:
| نموذج TFLite | model init with NPU compilation | model init with cached compilation | init memory footprint with NPU compilation | init memory with cached compilation |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7465.22 ملي ثانية | 198.34 ملي ثانية | 1525.24 ميغابايت | 355.07 ميغابايت |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592.54 ملي ثانية | 166.47 ملي ثانية | 254.90 ميغابايت | 33.78 ميغابايت |
على جهاز آخر، نحصل على ما يلي:
| نموذج TFLite | model init with NPU compilation | model init with cached compilation | init memory footprint with NPU compilation | init memory with cached compilation |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2766.44 ملي ثانية | 379.86 ملي ثانية | 653.54 ميغابايت | 501.21 ميغابايت |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784.14 ملي ثانية | 231.76 ملي ثانية | 113.14 ميغابايت | 67.49 ميغابايت |