Помогают защитить Большой Барьерный Риф с TensorFlow на Kaggle Присоединяйтесь вызов

TensorFlow Lite на графическом процессоре

TensorFlow Lite поддерживает несколько аппаратных ускорителей. В этом документе описывается, как использовать серверную часть графического процессора с помощью API-интерфейсов делегата TensorFlow Lite на Android (требуется OpenCL или OpenGL ES 3.1 и выше) и iOS (требуется iOS 8 или выше).

Преимущества ускорения графического процессора

Скорость

Графические процессоры предназначены для обеспечения высокой пропускной способности для массово распараллеливаемых рабочих нагрузок. Таким образом, они хорошо подходят для глубоких нейронных сетей, которые состоят из огромного количества операторов, каждый из которых работает с некоторым входным тензором (ами), которые можно легко разделить на меньшие рабочие нагрузки и выполнять параллельно. Этот параллелизм обычно приводит к меньшей задержке. В лучшем случае логический вывод на графическом процессоре может выполняться достаточно быстро, чтобы стать подходящим для приложений реального времени, которые ранее были невозможны.

Точность

Графические процессоры выполняют свои вычисления с 16-битными или 32-битными числами с плавающей запятой и (в отличие от процессоров) не требуют квантования для оптимальной производительности. Если снижение точности сделало квантование непригодным для ваших моделей, запуск нейронной сети на графическом процессоре может устранить эту проблему.

Энергоэффективность

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

Поддерживаемые операции

TensorFlow Lite на графическом процессоре поддерживает следующие операции с 16-битной и 32-битной точностью с плавающей запятой:

  • ADD
  • AVERAGE_POOL_2D
  • CONCATENATION
  • CONV_2D
  • DEPTHWISE_CONV_2D v1-2
  • EXP
  • FULLY_CONNECTED
  • LOGISTIC
  • LSTM v2 (Basic LSTM only)
  • MAX_POOL_2D
  • MAXIMUM
  • MINIMUM
  • MUL
  • PAD
  • PRELU
  • RELU
  • RELU6
  • RESHAPE
  • RESIZE_BILINEAR v1-3
  • SOFTMAX
  • STRIDED_SLICE
  • SUB
  • TRANSPOSE_CONV

По умолчанию все операции поддерживаются только в версии 1. Включение экспериментальной поддержки квантования позволяет использовать соответствующие версии; например, ADD v2.

Основное использование

Есть два способа вызвать ускорение модели в Android в зависимости от того, используете ли вы Android Studio ML Model Binding или TensorFlow Lite Interpreter.

Android через интерпретатор TensorFlow Lite

Добавьте tensorflow-lite-gpu вместе с существующим tensorflow-lite в существующий блок dependencies .

dependencies {
    ...
    implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    implementation 'org.tensorflow:tensorflow-lite-gpu:2.3.0'
}

Затем запустите TensorFlow Lite на графическом процессоре с помощью TfLiteDelegate . В Java вы можете указать GpuDelegate через Interpreter.Options .

Котлин

    import org.tensorflow.lite.Interpreter
    import org.tensorflow.lite.gpu.CompatibilityList
    import org.tensorflow.lite.gpu.GpuDelegate

    val compatList = CompatibilityList()

    val options = Interpreter.Options().apply{
        if(compatList.isDelegateSupportedOnThisDevice){
            // if the device has a supported GPU, add the GPU delegate
            val delegateOptions = compatList.bestOptionsForThisDevice
            this.addDelegate(GpuDelegate(delegateOptions))
        } else {
            // if the GPU is not supported, run on 4 threads
            this.setNumThreads(4)
        }
    }

    val interpreter = Interpreter(model, options)

    // Run inference
    writeToInput(input)
    interpreter.run(input, output)
    readFromOutput(output)
      

Ява

    import org.tensorflow.lite.Interpreter;
    import org.tensorflow.lite.gpu.CompatibilityList;
    import org.tensorflow.lite.gpu.GpuDelegate;

    // Initialize interpreter with GPU delegate
    Interpreter.Options options = new Interpreter.Options();
    CompatibilityList compatList = CompatibilityList();

    if(compatList.isDelegateSupportedOnThisDevice()){
        // if the device has a supported GPU, add the GPU delegate
        GpuDelegate.Options delegateOptions = compatList.getBestOptionsForThisDevice();
        GpuDelegate gpuDelegate = new GpuDelegate(delegateOptions);
        options.addDelegate(gpuDelegate);
    } else {
        // if the GPU is not supported, run on 4 threads
        options.setNumThreads(4);
    }

    Interpreter interpreter = new Interpreter(model, options);

    // Run inference
    writeToInput(input);
    interpreter.run(input, output);
    readFromOutput(output);
      

Android (C / C ++)

Для использования C / C ++ GPU TensorFlow Lite на Android делегат GPU может быть создан с помощью TfLiteGpuDelegateV2Create() и уничтожен с помощью TfLiteGpuDelegateV2Delete() .

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// NEW: Prepare GPU delegate.
auto* delegate = TfLiteGpuDelegateV2Create(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// NEW: Clean up.
TfLiteGpuDelegateV2Delete(delegate);

Взгляните на TfLiteGpuDelegateOptionsV2 чтобы создать экземпляр делегата с настраиваемыми параметрами. Вы можете инициализировать параметры по умолчанию с помощью TfLiteGpuDelegateOptionsV2Default() а затем изменить их по мере необходимости.

TFLite GPU для Android C / C ++ использует Базэл систему сборки. Делегат может быть создан, например, с помощью следующей команды:

bazel build -c opt --config android_arm64 tensorflow/lite/delegates/gpu:delegate                           # for static library
bazel build -c opt --config android_arm64 tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so  # for dynamic library

iOS (C ++)

Чтобы использовать TensorFlow Lite на GPU, получите делегат GPU через TFLGpuDelegateCreate() а затем передайте его в Interpreter::ModifyGraphWithDelegate() (вместо вызова Interpreter::AllocateTensors() ).

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// NEW: Prepare GPU delegate.

auto* delegate = TFLGpuDelegateCreate(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// Clean up.
TFLGpuDelegateDelete(delegate);

Расширенное использование

Параметры делегирования для iOS

Конструктор для делегата GPU принимает struct параметров. ( Swift API , Objective-C API , C API )

Передача nullptr (C API) или ничего (Objective-C и Swift API) в инициализатор устанавливает параметры по умолчанию (которые подробно описаны в приведенном выше примере базового использования).

Быстрый

    // THIS:
    var options = MetalDelegate.Options()
    options.isPrecisionLossAllowed = false
    options.waitType = .passive
    options.isQuantizationEnabled = true
    let delegate = MetalDelegate(options: options)

    // IS THE SAME AS THIS:
    let delegate = MetalDelegate()
      

Цель-C

    // THIS:
    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.precisionLossAllowed = false;
    options.waitType = TFLMetalDelegateThreadWaitTypePassive;
    options.quantizationEnabled = true;

    TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] initWithOptions:options];

    // IS THE SAME AS THIS:
    TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] init];
      

C

    // THIS:
    const TFLGpuDelegateOptions options = {
      .allow_precision_loss = false,
      .wait_type = TFLGpuDelegateWaitType::TFLGpuDelegateWaitTypePassive,
      .enable_quantization = true,
    };

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);

    // IS THE SAME AS THIS:
    TfLiteDelegate* delegate = TFLGpuDelegateCreate(nullptr);
      

Хотя удобно использовать конструкторы nullptr или конструкторы по умолчанию, мы рекомендуем вам явно установить параметры, чтобы избежать непредвиденного поведения, если значения по умолчанию изменятся в будущем.

Запуск квантованных моделей на GPU

В этом разделе объясняется, как делегат графического процессора ускоряет 8-битные квантованные модели. Сюда входят все разновидности квантования, в том числе:

Для оптимизации производительности используйте модели с тензорами ввода и вывода с плавающей запятой.

Как это работает?

Поскольку серверная часть графического процессора поддерживает только выполнение с плавающей запятой, мы запускаем квантованные модели, давая им «представление с плавающей запятой» исходной модели. На высоком уровне это влечет за собой следующие шаги:

  • Постоянные тензоры (например, веса / смещения) деквантовываются один раз в память графического процессора. Это происходит, когда делегат применяется к интерпретатору TFLite.

  • Входные и выходные данные для программы графического процессора, если квантованы 8 бит, деквантовываются и квантуются (соответственно) для каждого логического вывода. Это делается на ЦП с использованием оптимизированных ядер TFLite.

  • Программа графического процессора модифицируется для имитации квантованного поведения путем вставки имитаторов квантования между операциями. Это необходимо для моделей, в которых операторы ожидают, что активации будут следовать границам, полученным во время квантования.

Эту функцию можно включить с помощью параметров делегата следующим образом:

Android

API Android по умолчанию поддерживают квантованные модели. Чтобы отключить, сделайте следующее:

C ++ API

TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default();
options.experimental_flags = TFLITE_GPU_EXPERIMENTAL_FLAGS_NONE;

auto* delegate = TfLiteGpuDelegateV2Create(options);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

Java API

GpuDelegate delegate = new GpuDelegate(new GpuDelegate.Options().setQuantizedModelsAllowed(false));

Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);

iOS

API-интерфейсы iOS по умолчанию поддерживают квантованные модели. Чтобы отключить, сделайте следующее:

Быстрый

    var options = MetalDelegate.Options()
    options.isQuantizationEnabled = false
    let delegate = MetalDelegate(options: options)
      

Цель-C

    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.quantizationEnabled = false;
      

C

    TFLGpuDelegateOptions options = TFLGpuDelegateOptionsDefault();
    options.enable_quantization = false;

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Буферы ввода / вывода (только для iOS, C ++ API)

Для выполнения вычислений на графическом процессоре данные должны быть доступны графическому процессору. Это часто требует выполнения копирования из памяти. Желательно по возможности не пересекать границу памяти CPU / GPU, так как это может занять значительное количество времени. Обычно такое пересечение неизбежно, но в некоторых особых случаях одно или другое можно пропустить.

Если входом сети является изображение, уже загруженное в память графического процессора (например, текстура графического процессора, содержащая канал камеры), оно может оставаться в памяти графического процессора, не попадая в память процессора. Точно так же, если выходные данные сети находятся в форме визуализируемого изображения (например, передача стиля изображения ), его можно напрямую отобразить на экране.

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

Предполагая, что входное изображение находится в памяти графического процессора, его необходимо сначала преобразовать в объект MTLBuffer для Metal. Вы можете связать TfLiteTensor с подготовленным пользователем MTLBuffer с помощью TFLGpuDelegateBindMetalBufferToTensor() . Обратите внимание, что TFLGpuDelegateBindMetalBufferToTensor() должен вызываться после Interpreter::ModifyGraphWithDelegate() . Кроме того, вывод логического вывода по умолчанию копируется из памяти графического процессора в память процессора. Это поведение можно отключить, вызвав Interpreter::SetAllowBufferHandleOutput(true) во время инициализации.

#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"

// ...

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(nullptr);

if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

interpreter->SetAllowBufferHandleOutput(true);  // disable default gpu->cpu copy
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->inputs()[0], user_provided_input_buffer)) {
  return false;
}
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->outputs()[0], user_provided_output_buffer)) {
  return false;
}

// Run inference.
if (interpreter->Invoke() != kTfLiteOk) return false;

Секреты и уловки

  • Некоторые операции, которые являются тривиальными для ЦП, могут быть дорогостоящими для ГП. Один класс таких операций включает в себя различные формы операций изменения формы (включая BATCH_TO_SPACE , SPACE_TO_BATCH , SPACE_TO_DEPTH и аналогичные операции). Если эти операции не требуются (например, они были вставлены, чтобы помочь архитектору сети разобраться в системе, но не влияют иным образом на вывод), их стоит удалить для повышения производительности.

  • На графическом процессоре тензорные данные разделяются на 4 канала. Таким образом, вычисление тензора формы [B, H, W, 5] будет выполняться примерно так же с тензором формы [B, H, W, 8] , но значительно хуже, чем [B, H, W, 4] .

    • Например, если оборудование камеры поддерживает кадры изображения в формате RGBA, подача этого 4-канального входа выполняется значительно быстрее, поскольку можно избежать копирования в память (из 3-канального RGB в 4-канальный RGBX).
  • Для достижения максимальной производительности не стесняйтесь переобучать классификатор с помощью сетевой архитектуры, оптимизированной для мобильных устройств. Это важная часть оптимизации для вывода на устройстве.