Делегат NNAPI TensorFlow Lite

API нейронных сетей Android (NNAPI) доступен на всех устройствах Android под управлением Android 8.1 (уровень API 27) или выше. Он обеспечивает ускорение моделей TensorFlow Lite на устройствах Android с поддерживаемыми аппаратными ускорителями, включая:

  • Графический процессор (GPU)
  • Цифровой сигнальный процессор (DSP)
  • Блок нейронной обработки (NPU)

Производительность будет зависеть от конкретного оборудования, доступного на устройстве.

На этой странице описано, как использовать делегат NNAPI с интерпретатором TensorFlow Lite в Java и Kotlin. Информацию об API Android C см. в документации Android Native Developer Kit .

Испытание делегата NNAPI на вашей собственной модели

Импорт Gradle

Делегат NNAPI является частью интерпретатора Android TensorFlow Lite версии 1.14.0 или выше. Вы можете импортировать его в свой проект, добавив следующее в файл градиента модуля:

dependencies {
   implementation 'org.tensorflow:tensorflow-lite:+'
}

Инициализация делегата NNAPI

Добавьте код для инициализации делегата NNAPI перед инициализацией интерпретатора TensorFlow Lite.

Котлин

import android.content.res.AssetManager
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.nnapi.NnApiDelegate
import java.io.FileInputStream
import java.io.IOException
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
...

val options = Interpreter.Options()
var nnApiDelegate: NnApiDelegate? = null
// Initialize interpreter with NNAPI delegate for Android Pie or above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = NnApiDelegate()
    options.addDelegate(nnApiDelegate)
}
val assetManager = assets

// Initialize TFLite interpreter
val tfLite: Interpreter
try {
    tfLite = Interpreter(loadModelFile(assetManager, "model.tflite"), options)
} catch (e: Exception) {
    throw RuntimeException(e)
}

// Run inference
// ...

// Unload delegate
tfLite.close()
nnApiDelegate?.close()

...

@Throws(IOException::class)
private fun loadModelFile(assetManager: AssetManager, modelFilename: String): MappedByteBuffer {
    val fileDescriptor = assetManager.openFd(modelFilename)
    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
    val fileChannel = inputStream.channel
    val startOffset = fileDescriptor.startOffset
    val declaredLength = fileDescriptor.declaredLength
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}

...

Джава

import android.content.res.AssetManager;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.nnapi.NnApiDelegate;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
...

Interpreter.Options options = (new Interpreter.Options());
NnApiDelegate nnApiDelegate = null;
// Initialize interpreter with NNAPI delegate for Android Pie or above
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = new NnApiDelegate();
    options.addDelegate(nnApiDelegate);
}

AssetManager assetManager = getAssets();
// Initialize TFLite interpreter
try {
    tfLite = new Interpreter(loadModelFile(assetManager, "model.tflite"), options);
} catch (Exception e) {
    throw new RuntimeException(e);
}

// Run inference
// ...

// Unload delegate
tfLite.close();
if(null != nnApiDelegate) {
    nnApiDelegate.close();
}

...

private MappedByteBuffer loadModelFile(AssetManager assetManager, String modelFilename) throws IOException {
    AssetFileDescriptor fileDescriptor = assetManager.openFd(modelFilename);
    FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
    FileChannel fileChannel = inputStream.getChannel();
    long startOffset = fileDescriptor.getStartOffset();
    long declaredLength = fileDescriptor.getDeclaredLength();
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}

...

Лучшие практики

Проверьте производительность перед развертыванием

Производительность во время выполнения может существенно различаться в зависимости от архитектуры модели, размера, операций, доступности оборудования и использования оборудования во время выполнения. Например, если приложение интенсивно использует графический процессор для рендеринга, ускорение NNAPI может не улучшить производительность из-за конкуренции за ресурсы. Мы рекомендуем запустить простой тест производительности с помощью журнала отладки, чтобы измерить время вывода. Прежде чем включать NNAPI в производство, запустите тест на нескольких телефонах с разными наборами микросхем (производителями или моделями одного и того же производителя), которые являются репрезентативными для вашей пользовательской базы.

Для продвинутых разработчиков TensorFlow Lite также предлагает инструмент для тестирования моделей для Android .

Создайте список исключенных устройств

В производственной среде могут возникнуть случаи, когда NNAPI не работает должным образом. Мы рекомендуем разработчикам вести список устройств, которым не следует использовать ускорение NNAPI в сочетании с определенными моделями. Вы можете создать этот список на основе значения "ro.board.platform" , которое можно получить, используя следующий фрагмент кода:

String boardPlatform = "";

try {
    Process sysProcess =
        new ProcessBuilder("/system/bin/getprop", "ro.board.platform").
        redirectErrorStream(true).start();

    BufferedReader reader = new BufferedReader
        (new InputStreamReader(sysProcess.getInputStream()));
    String currentLine = null;

    while ((currentLine=reader.readLine()) != null){
        boardPlatform = line;
    }
    sysProcess.destroy();
} catch (IOException e) {}

Log.d("Board Platform", boardPlatform);

Опытным разработчикам рекомендуется поддерживать этот список через систему удаленной настройки. Команда TensorFlow активно работает над способами упрощения и автоматизации обнаружения и применения оптимальной конфигурации NNAPI.

Квантование

Квантование уменьшает размер модели за счет использования для вычислений 8-битных целых чисел или 16-битных чисел с плавающей запятой вместо 32-битных чисел с плавающей запятой. Размеры 8-битных целочисленных моделей составляют четверть от 32-битных версий с плавающей запятой; 16-битные числа с плавающей запятой составляют половину размера. Квантование может значительно повысить производительность, хотя этот процесс может привести к снижению некоторой точности модели.

Существует несколько типов методов квантования после обучения, но для максимальной поддержки и ускорения на современном оборудовании мы рекомендуем полное целочисленное квантование . Этот подход преобразует и вес, и операции в целые числа. Для работы этого процесса квантования требуется репрезентативный набор данных.

Используйте поддерживаемые модели и операции

Если делегат NNAPI не поддерживает некоторые комбинации операций или параметров в модели, платформа запускает в ускорителе только поддерживаемые части графа. Оставшаяся часть выполняется на ЦП, что приводит к раздельному выполнению. Из-за высокой стоимости синхронизации ЦП и ускорителя это может привести к снижению производительности по сравнению с выполнением всей сети на одном ЦП.

NNAPI работает лучше всего, когда модели используют только поддерживаемые операции . Известно, что следующие модели совместимы с NNAPI:

Ускорение NNAPI также не поддерживается, если модель содержит выходные данные динамического размера. В этом случае вы получите предупреждение типа:

ERROR: Attempting to use a delegate that only supports static-sized tensors \
with a graph that has dynamic-sized tensors.

Включить реализацию процессора NNAPI

Граф, который не может быть полностью обработан ускорителем, может вернуться к реализации ЦП NNAPI. Однако, поскольку это обычно менее производительно, чем интерпретатор TensorFlow, этот параметр по умолчанию отключен в делегате NNAPI для Android 10 (уровень API 29) или выше. Чтобы переопределить это поведение, установите для setUseNnapiCpu значение true в объекте NnApiDelegate.Options .