LiteRT предоставляет унифицированный интерфейс для использования нейронных процессоров (NPU) без необходимости разбираться в компиляторах, средах выполнения или зависимостях библиотек, специфичных для конкретного производителя. Использование LiteRT для ускорения работы NPU повышает производительность при выполнении вычислений в реальном времени и с большими моделями, а также минимизирует копирование памяти за счет использования аппаратных буферов без копирования.
Начать
- Для ознакомления с классическими моделями машинного обучения см. следующие демонстрационные приложения.
- Приложение Kotlin для сегментации изображений : предоставляет примеры компиляции AOT и компиляции на устройстве (JIT) соответственно.
- Приложение на C++ с асинхронной сегментацией : демонстрирует как компиляцию AOT, так и компиляцию на устройстве (JIT) в одном и том же приложении.
- Для работы с большими языковыми моделями (LLM) см. руководство по выполнению LLM на NPU с использованием LiteRT-LM.
Поставщики НПУ
LiteRT поддерживает ускорение NPU от следующих производителей:
Qualcomm AI Engine Direct
- Поддержка компиляции AOT и выполнения компиляции на устройстве через API
CompiledModel. - Подробную информацию о настройке см. в документации Quantum AI Engine Direct .
- Актуальную информацию можно найти в разделе «Раскрытие максимальной производительности на NPU Qualcomm с помощью LiteRT» .
MediaTek NeuroPilot
- Поддержка компиляции AOT и выполнения компиляции на устройстве через API
CompiledModel. - Подробную информацию о настройке см. в описании MediaTek NeuroPilot .
- Актуальную информацию можно найти в статье MediaTek NPU и LiteRT: Powering the next generation of on-device AI .
Google Tensor
Google Tensor SDK находится в экспериментальном режиме. Зарегистрироваться можно здесь .
Компиляция AOT и компиляция на устройстве
LiteRT NPU поддерживает как компиляцию AOT, так и компиляцию на устройстве, чтобы соответствовать вашим конкретным требованиям к развертыванию:
- Автономная (AOT) компиляция : Этот метод лучше всего подходит для больших и сложных моделей, где целевая система на кристалле (SoC) известна. Предварительная компиляция значительно снижает затраты на инициализацию и уменьшает использование памяти при запуске приложения пользователем.
- Онлайн-компиляция (на устройстве) : также известная как JIT-компиляция. Идеально подходит для платформенно-независимого распространения небольших моделей. Модель компилируется на устройстве пользователя во время инициализации, не требуя дополнительных подготовительных шагов, но увеличивая затраты на первый запуск.
В этом руководстве показано, как выполнить развертывание как для компиляции AOT, так и для компиляции на устройстве в три этапа.
Шаг 1: AOT-компиляция для целевых NPU SoC.
Для компиляции вашей модели .tflite под поддерживаемые SoC можно использовать компилятор LiteRT AOT (ahead of time). Также можно одновременно использовать несколько производителей и версий SoC в рамках одного процесса компиляции. Подробнее см. в этом блокноте по компиляции LiteRT AOT . Хотя компиляция AOT необязательна, она настоятельно рекомендуется для больших моделей, чтобы сократить время инициализации на устройстве. Этот шаг не требуется для компиляции на устройстве.
Шаг 2: Разверните приложение в Google Play, если используете Android.
На Android используйте Google Play for On-device AI (PODAI), чтобы развернуть библиотеки модели и среды выполнения NPU вместе с вашим приложением.
- Для моделей, компилируемых непосредственно на устройстве : добавьте исходный файл модели .tflite непосредственно в каталог assets/ вашего приложения.
- Пример реализации см. в приложении Kotlin для сегментации, компилируемом на устройстве .
- Для моделей, созданных с помощью AOT-компиляции : используйте LiteRT для экспорта скомпилированных моделей в единый пакет AI Pack для Google Play . Затем загрузите этот пакет AI Pack в Google Play, чтобы автоматически доставлять корректные скомпилированные модели на устройства пользователей.
- Инструкции по экспорту скомпилированных моделей в Play AI Pack см. в блокноте LiteRT AOT Compilation .
- Пример реализации см. в приложении Kotlin с AOT-компиляцией сегментации .
- Для библиотек среды выполнения NPU используйте Play Feature Delivery , чтобы распространить необходимые библиотеки среды выполнения на устройства пользователей.
См. следующие разделы о том, как развертывать приложения с помощью Play AI Pack и Play Feature Delivery .
Развертывайте AOT-модели с помощью Play AI Pack.
Следующие шаги помогут вам развернуть модели, скомпилированные с помощью AOT, используя Play AI Packs.
Добавить пакет AI в проект
Для импорта пакетов AI Pack в проект 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
Добавьте пакеты AI Packs в конфигурацию Gradle.
Скопируйте файл device_targeting_configuration.xml из сгенерированных пакетов AI Packs в каталог основного модуля приложения. Затем обновите 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 для доставки по запросу.
Доставка по запросу позволяет запрашивать модель во время выполнения, что полезно, если модель требуется только для определенных пользовательских сценариев. Ваша модель будет загружена во внутреннюю память вашего приложения. При настройке функции Android AI Pack в файле build.gradle.kts проверьте возможности устройства. См. также инструкции по доставке во время установки и быстрой доставке от 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,
)
Развертывайте библиотеки среды выполнения NPU с помощью Play Feature Delivery.
Play Feature Delivery поддерживает несколько вариантов доставки для оптимизации первоначального размера загрузки, включая доставку во время установки, доставку по запросу, условную доставку и мгновенную доставку. Здесь мы приводим базовое руководство по доставке во время установки.
Добавьте в проект библиотеки среды выполнения NPU.
Загрузите файл litert_npu_runtime_libraries.zip для AOT-компиляции или 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. Например, для NPU Qualcomm выполните следующую команду:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
Добавьте библиотеки среды выполнения NPU в конфигурацию Gradle.
Скопируйте файл device_targeting_configuration.xml из сгенерированных пакетов AI Packs в каталог основного модуля приложения. Затем обновите 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: Выполнение вывода на NPU с использованием среды выполнения LiteRT.
LiteRT абстрагирует сложность разработки для конкретных версий SoC, позволяя запускать вашу модель на NPU всего несколькими строками кода. Он также предоставляет надежный встроенный механизм резервного копирования: вы можете указать CPU, GPU или оба варианта в качестве параметров, и LiteRT автоматически будет использовать их, если NPU недоступен. Удобно, что компиляция AOT также поддерживает резервное копирование. Она обеспечивает частичное делегирование на NPU, при котором неподдерживаемые подграфы беспрепятственно запускаются на CPU или GPU в соответствии с указанными параметрами.
Запуск на Kotlin
Примеры реализации можно посмотреть в следующих демонстрационных приложениях:
- Сегментация на устройстве. Компиляция приложения Kotlin.
- Сегментация AOT-компиляция 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 библиотеки бэкэнда : например, библиотеки Qualcomm AI RT (QAIRT) для хоста Android (такие как
libQnnHtp.so,libQnnHtpPrepare.so) и соответствующая библиотека Hexagon DSP (libQnnHtpV79Skel.so). Это гарантирует, что среда выполнения LiteRT сможет перенести вычисления на NPU. - Атрибут зависимостей : атрибут
depsсвязывает необходимые зависимости времени компиляции, такие как буфер тензоров LiteRT (//litert/cc:litert_tensor_buffer) и API для уровня диспетчеризации NPU (//litert/vendors/qualcomm/dispatch:dispatch_api). Это позволяет коду вашего приложения взаимодействовать с NPU через LiteRT. - Файлы моделей и другие ресурсы : включаются посредством атрибута
data.
Такая настройка позволяет скомпилированному исполняемому файлу динамически загружать и использовать нейропроцессор для ускорения выполнения вычислений в машинном обучении.
Настройте среду 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 с нулевым копированием данных и использованием AHardwareBuffer , передающего данные непосредственно в NPU. Эта реализация позволяет избежать дорогостоящих обращений к памяти ЦП, что значительно снижает накладные расходы на вывод данных.
// 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 мс | 198,34 мс | 1525,24 МБ | 355,07 МБ |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592,54 мс | 166,47 мс | 254,90 МБ | 33,78 МБ |
На другом устройстве мы получаем следующее:
| модель TFLite | инициализация модели с компиляцией NPU | инициализация модели с кэшированной компиляцией | инициализация объема используемой памяти при компиляции NPU | инициализация памяти с использованием кэшированной компиляции |
|---|---|---|---|---|
| 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 МБ |