การเร่งความเร็ว NPU ด้วย LiteRT

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