Делегат графического процессора TensorFlow Lite

TensorFlow Lite поддерживает несколько аппаратных ускорителей. В этом документе описывается, как использовать серверную часть графического процессора с помощью API делегатов TensorFlow Lite на Android и iOS.

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

В отличие от центральных процессоров, графические процессоры вычисляют с 16-битными или 32-битными числами с плавающей запятой и не требуют квантования для оптимальной производительности. Делегат принимает 8-битные квантованные модели, но вычисления будут выполняться в числах с плавающей запятой. Подробности см. в расширенной документации .

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

Учебники по демонстрационным приложениям

Самый простой способ опробовать делегат графического процессора — это следовать приведенным ниже руководствам, в которых рассказывается о создании наших демонстрационных приложений классификации с поддержкой графического процессора. Код GPU пока только двоичный; это будет с открытым исходным кодом в ближайшее время. Как только вы поймете, как заставить работать наши демонстрации, вы сможете попробовать это на своих собственных моделях.

Android (с Android Studio)

Пошаговое руководство смотрите в видеоролике GPU Delegate для Android .

Шаг 1. Клонируйте исходный код TensorFlow и откройте его в Android Studio.

git clone https://github.com/tensorflow/tensorflow

Шаг 2. Отредактируйте app/build.gradle , чтобы использовать ночной AAR GPU.

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

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

Шаг 3. Соберите и запустите

Выполнить → Запустить «приложение». Когда вы запустите приложение, вы увидите кнопку для включения графического процессора. Измените квантованную модель на модель с плавающей запятой, а затем щелкните GPU, чтобы запустить на GPU.

запустить демоверсию android gpu и переключиться на gpu

iOS (с XCode)

Пошаговое руководство смотрите в видеоролике GPU Delegate для iOS .

Шаг 1. Получите исходный код демо и убедитесь, что он компилируется.

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

Шаг 2. Измените подфайл, чтобы использовать графический процессор TensorFlow Lite CocoaPod.

Начиная с версии 2.3.0 делегат графического процессора по умолчанию исключается из модуля, чтобы уменьшить размер двоичного файла. Вы можете включить их, указав subspec. Для модуля TensorFlowLiteSwift :

pod 'TensorFlowLiteSwift/Metal', '~> 0.0.1-nightly',

ИЛИ ЖЕ

pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']

Вы можете сделать то же самое для TensorFlowLiteObjC или TensorFlowLitC , если хотите использовать Objective-C (начиная с версии 2.4.0) или C API.

До выпуска 2.3.0

До TensorFlow Lite 2.0.0

Мы создали бинарный CocoaPod, который включает делегат GPU. Чтобы переключить проект на его использование, измените файл `tensorflow/tensorflow/lite/examples/ios/camera/Podfile`, чтобы использовать модуль `TensorFlowLiteGpuExperimental` вместо `TensorFlowLite`.


    target 'YourProjectName'
      # pod 'TensorFlowLite', '1.12.0'
      pod 'TensorFlowLiteGpuExperimental'
    

До TensorFlow Lite 2.2.0

Начиная с TensorFlow Lite 2.1.0 и заканчивая 2.2.0, делегат графического процессора включен в модуль TensorFlowLiteC. Вы можете выбрать между «TensorFlowLiteC» и «TensorFlowLiteSwift» в зависимости от языка.

Шаг 3. Включите делегат GPU

Чтобы включить код, который будет использовать делегат GPU, вам нужно будет изменить TFLITE_USE_GPU_DELEGATE с 0 на 1 в CameraExampleViewController.h .

#define TFLITE_USE_GPU_DELEGATE 1

Шаг 4. Соберите и запустите демонстрационное приложение

После выполнения предыдущего шага вы сможете запустить приложение.

Шаг 5. Режим выпуска

Хотя на шаге 4 вы работали в режиме отладки, для повышения производительности вам следует перейти на выпускную сборку с соответствующими оптимальными настройками Metal. В частности, чтобы изменить эти настройки, перейдите в Product > Scheme > Edit Scheme... . Выберите Run . На вкладке Info измените Build Configuration с Debug на Release , снимите флажок Debug executable .

настройка выпуска

Затем перейдите на вкладку « Options » и измените « GPU Frame Capture » на « Disabled » и « Metal API Validation » на « Disabled ».

настройка металлических опций

Наконец, убедитесь, что выбраны сборки только для релизов на 64-битной архитектуре. В разделе Project navigator -> tflite_camera_example -> PROJECT -> tflite_camera_example -> Build Settings установите для параметра « Build Active Architecture Only > Release значение «Да».

настройка параметров выпуска

Пробуем делегат GPU на своей модели

Андроид

Существует два способа вызвать ускорение модели в зависимости от того, используете ли вы привязку модели Android Studio ML или интерпретатор TensorFlow Lite.

Интерпретатор TensorFlow Lite

Посмотрите демонстрацию, чтобы узнать, как добавить делегата. В своем приложении добавьте AAR, как указано выше, импортируйте модуль org.tensorflow.lite.gpu.GpuDelegate и используйте функцию addDelegate для регистрации делегата графического процессора в интерпретаторе:

Котлин

    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);
      

iOS

Быстрый

    import TensorFlowLite

    // Load model ...

    // Initialize TensorFlow Lite interpreter with the GPU delegate.
    let delegate = MetalDelegate()
    if let interpreter = try Interpreter(modelPath: modelPath,
                                         delegates: [delegate]) {
      // Run inference ...
    }
      

Цель-C

    // Import module when using CocoaPods with module support
    @import TFLTensorFlowLite;

    // Or import following headers manually
    #import "tensorflow/lite/objc/apis/TFLMetalDelegate.h"
    #import "tensorflow/lite/objc/apis/TFLTensorFlowLite.h"

    // Initialize GPU delegate
    TFLMetalDelegate* metalDelegate = [[TFLMetalDelegate alloc] init];

    // Initialize interpreter with model path and GPU delegate
    TFLInterpreterOptions* options = [[TFLInterpreterOptions alloc] init];
    NSError* error = nil;
    TFLInterpreter* interpreter = [[TFLInterpreter alloc]
                                    initWithModelPath:modelPath
                                              options:options
                                            delegates:@[ metalDelegate ]
                                                error:&error];
    if (error != nil) { /* Error handling... */ }

    if (![interpreter allocateTensorsWithError:&error]) { /* Error handling... */ }
    if (error != nil) { /* Error handling... */ }

    // Run inference ...

        ```
          

С (до 2.3.0)

        #include "tensorflow/lite/c/c_api.h"
        #include "tensorflow/lite/delegates/gpu/metal_delegate.h"

        // Initialize model
        TfLiteModel* model = TfLiteModelCreateFromFile(model_path);

        // Initialize interpreter with GPU delegate
        TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
        TfLiteDelegate* delegate = TFLGPUDelegateCreate(nil);  // default config
        TfLiteInterpreterOptionsAddDelegate(options, metal_delegate);
        TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
        TfLiteInterpreterOptionsDelete(options);

        TfLiteInterpreterAllocateTensors(interpreter);

        NSMutableData *input_data = [NSMutableData dataWithLength:input_size * sizeof(float)];
        NSMutableData *output_data = [NSMutableData dataWithLength:output_size * sizeof(float)];
        TfLiteTensor* input = TfLiteInterpreterGetInputTensor(interpreter, 0);
        const TfLiteTensor* output = TfLiteInterpreterGetOutputTensor(interpreter, 0);

        // Run inference
        TfLiteTensorCopyFromBuffer(input, inputData.bytes, inputData.length);
        TfLiteInterpreterInvoke(interpreter);
        TfLiteTensorCopyToBuffer(output, outputData.mutableBytes, outputData.length);

        // Clean up
        TfLiteInterpreterDelete(interpreter);
        TFLGpuDelegateDelete(metal_delegate);
        TfLiteModelDelete(model);
          

## Supported Models and Ops

With the release of the GPU delegate, we included a handful of models that can
be run on the backend:

*   [MobileNet v1 (224x224) image classification](https://ai.googleblog.com/2017/06/mobilenets-open-source-models-for.html) [[download]](https://storage.googleapis.com/download.tensorflow.org/models/tflite/gpu/mobilenet_v1_1.0_224.tflite)
    <br /><i>(image classification model designed for mobile and embedded based vision applications)</i>
*   [DeepLab segmentation (257x257)](https://ai.googleblog.com/2018/03/semantic-image-segmentation-with.html) [[download]](https://storage.googleapis.com/download.tensorflow.org/models/tflite/gpu/deeplabv3_257_mv_gpu.tflite)
    <br /><i>(image segmentation model that assigns semantic labels (e.g., dog, cat, car) to every pixel in the input image)</i>
*   [MobileNet SSD object detection](https://ai.googleblog.com/2018/07/accelerated-training-and-inference-with.html) [[download]](https://storage.googleapis.com/download.tensorflow.org/models/tflite/gpu/mobile_ssd_v2_float_coco.tflite)
    <br /><i>(image classification model that detects multiple objects with bounding boxes)</i>
*   [PoseNet for pose estimation](https://github.com/tensorflow/tfjs-models/tree/master/posenet) [[download]](https://storage.googleapis.com/download.tensorflow.org/models/tflite/gpu/multi_person_mobilenet_v1_075_float.tflite)
    <br /><i>(vision model that estimates the poses of a person(s) in image or video)</i>

To see a full list of supported ops, please see the
[advanced documentation](gpu_advanced).

## Non-supported models and ops

If some of the ops are not supported by the GPU delegate, the framework will
only run a part of the graph on the GPU and the remaining part on the CPU. Due
to the high cost of CPU/GPU synchronization, a split execution mode like this
will often result in slower performance than when the whole network is run on
the CPU alone. In this case, the user will get a warning like:

```none
WARNING: op code #42 cannot be handled by this delegate.
```

Мы не предоставили обратный вызов для этой ошибки, так как это не настоящий сбой во время выполнения, а то, что разработчик может наблюдать, пытаясь заставить сеть работать на делегате.

Советы по оптимизации

Оптимизация для мобильных устройств

Некоторые операции, которые тривиальны для ЦП, могут иметь высокую стоимость для графического процессора на мобильных устройствах. Операции изменения формы особенно затратны для выполнения, включая 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).

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

Сокращение времени инициализации с сериализацией

Функция делегата графического процессора позволяет загружать предварительно скомпилированный код ядра и данные модели, сериализованные и сохраненные на диске из предыдущих запусков. Такой подход позволяет избежать повторной компиляции и сокращает время запуска до 90%. Инструкции о том, как применить сериализацию к вашему проекту, см. в разделе Сериализация делегата графического процессора .