TensorFlow Lite GPU-Delegierter

TensorFlow Lite unterstützt mehrere Hardwarebeschleuniger. In diesem Dokument wird beschrieben, wie Sie das GPU-Back-End mit den TensorFlow Lite-Delegierten-APIs unter Android und iOS verwenden.

GPUs sind auf einen hohen Durchsatz für massiv parallelisierbare Workloads ausgelegt. Daher eignen sie sich gut für tiefe neuronale Netze, die aus einer großen Anzahl von Operatoren bestehen, von denen jeder an einigen Eingabetensoren arbeitet, die leicht in kleinere Workloads unterteilt und parallel ausgeführt werden können, was typischerweise zu einer geringeren Latenz führt. Im besten Fall läuft die Inferenz auf der GPU nun schnell genug für bisher nicht verfügbare Echtzeitanwendungen.

Im Gegensatz zu CPUs rechnen GPUs mit 16-Bit- oder 32-Bit-Gleitkommazahlen und benötigen keine Quantisierung für eine optimale Leistung. Der Delegat akzeptiert quantisierte 8-Bit-Modelle, aber die Berechnung wird in Gleitkommazahlen durchgeführt. Weitere Informationen finden Sie in der erweiterten Dokumentation .

Ein weiterer Vorteil der GPU-Inferenz ist die Energieeffizienz. GPUs führen die Berechnungen sehr effizient und optimiert durch, sodass sie weniger Strom verbrauchen und weniger Wärme erzeugen, als wenn dieselbe Aufgabe auf CPUs ausgeführt wird.

Demo-App-Tutorials

Der einfachste Weg, den GPU-Delegaten auszuprobieren, besteht darin, die folgenden Tutorials zu befolgen, in denen unsere Klassifizierungs-Demoanwendungen mit GPU-Unterstützung erstellt werden. Der GPU-Code ist vorerst nur binär; es wird bald Open-Source sein. Sobald Sie verstanden haben, wie unsere Demos funktionieren, können Sie dies an Ihren eigenen benutzerdefinierten Modellen ausprobieren.

Android (mit Android Studio)

Eine Schritt-für-Schritt-Anleitung finden Sie im Video zu GPU Delegate für Android .

Schritt 1. Klonen Sie den TensorFlow-Quellcode und öffnen Sie ihn in Android Studio

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

Schritt 2. Bearbeiten Sie app/build.gradle , um den nächtlichen GPU-AAR zu verwenden

Fügen Sie das Paket tensorflow-lite-gpu neben dem vorhandenen Paket tensorflow-lite im vorhandenen dependencies .

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

Schritt 3. Erstellen und ausführen

Ausführen → 'App' ausführen. Wenn Sie die Anwendung ausführen, sehen Sie eine Schaltfläche zum Aktivieren der GPU. Wechseln Sie von quantisiert zu einem Float-Modell und klicken Sie dann auf GPU, um auf der GPU ausgeführt zu werden.

Android-GPU-Demo ausführen und auf GPU wechseln

iOS (mit XCode)

Eine Schritt-für-Schritt-Anleitung finden Sie im Video zu GPU Delegate für iOS .

Schritt 1. Holen Sie sich den Demo-Quellcode und stellen Sie sicher, dass er kompiliert wird.

Folgen Sie unserem Tutorial zur iOS-Demo-App. Dies bringt Sie an einen Punkt, an dem die unveränderte iOS-Kamera-Demo auf Ihrem Telefon funktioniert.

Schritt 2. Ändern Sie die Poddatei, um den TensorFlow Lite GPU CocoaPod zu verwenden

Ab Version 2.3.0 wird der GPU-Delegat standardmäßig aus dem Pod ausgeschlossen, um die Binärgröße zu reduzieren. Sie können sie einschließen, indem Sie Unterspezifikationen angeben. Für TensorFlowLiteSwift Pod:

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

ODER

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

Sie können für TensorFlowLiteObjC oder TensorFlowLitC ähnlich TensorFlowLitC wenn Sie Objective-C (ab Version 2.4.0) oder C-API verwenden möchten.

Vor Version 2.3.0

Bis TensorFlow Lite 2.0.0

Wir haben einen binären CocoaPod erstellt, der den GPU-Delegaten enthält. Um das Projekt zu ändern, um es zu verwenden, ändern Sie die Datei `tensorflow/tensorflow/lite/examples/ios/camera/Podfile`, um den Pod `TensorFlowLiteGpuExperimental` anstelle von `TensorFlowLite` zu ​​verwenden.


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

Bis TensorFlow Lite 2.2.0

Von TensorFlow Lite 2.1.0 bis 2.2.0 ist der GPU-Delegat im `TensorFlowLiteC`-Pod enthalten. Sie können je nach Sprache zwischen `TensorFlowLiteC` und `TensorFlowLiteSwift` wählen.

Schritt 3. Aktivieren Sie den GPU-Delegaten

Um den Code zu aktivieren, der den GPU-Delegaten verwendet, müssen Sie TFLITE_USE_GPU_DELEGATE in TFLITE_USE_GPU_DELEGATE von 0 auf 1 CameraExampleViewController.h .

#define TFLITE_USE_GPU_DELEGATE 1

Schritt 4. Erstellen und starten Sie die Demo-App

Nachdem Sie den vorherigen Schritt ausgeführt haben, sollten Sie die App ausführen können.

Schritt 5. Freigabemodus

Während Sie in Schritt 4 im Debug-Modus ausgeführt wurden, sollten Sie für eine bessere Leistung zu einem Release-Build mit den entsprechenden optimalen Metal-Einstellungen wechseln. Um diese Einstellungen zu bearbeiten, gehen Sie insbesondere zu Product > Scheme > Edit Scheme... . Wählen Sie Run . Ändern Sie auf der Registerkarte Info die Build Configuration von Debug auf Release , deaktivieren Sie Debug executable .

Freigabe einrichten

Klicken Sie dann auf die Registerkarte Options und ändern Sie GPU Frame Capture in Disabled und Metal API Validation in Disabled .

Einrichten von Metalloptionen

Stellen Sie abschließend sicher, dass Sie Nur-Release-Builds auf 64-Bit-Architektur auswählen. Project navigator -> tflite_camera_example -> PROJECT -> tflite_camera_example -> Build Settings unter Project navigator -> tflite_camera_example -> PROJECT -> tflite_camera_example -> Build Settings Build Active Architecture Only > Release auf Yes.

Freigabeoptionen einrichten

Testen Sie den GPU-Delegaten an Ihrem eigenen Modell

Android

Es gibt zwei Möglichkeiten, die Modellbeschleunigung aufzurufen, je nachdem, ob Sie Android Studio ML Model Binding oder TensorFlow Lite Interpreter verwenden.

TensorFlow Lite-Interpreter

Sehen Sie sich die Demo an, um zu sehen, wie Sie den Delegaten hinzufügen. org.tensorflow.lite.gpu.GpuDelegate in Ihrer Anwendung den AAR wie oben beschrieben hinzu, importieren org.tensorflow.lite.gpu.GpuDelegate Modul org.tensorflow.lite.gpu.GpuDelegate und verwenden Sie die Funktion addDelegate , um den GPU-Delegaten beim Interpreter zu registrieren:

Kotlin

    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)
      

Java

    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

Schnell

    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 ...
    }
      

Ziel 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 ...

    ```
      

C (bis 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);
      

Unterstützte Modelle und Ops

Mit der Veröffentlichung des GPU-Delegaten haben wir eine Handvoll Modelle hinzugefügt, die im Backend ausgeführt werden können:

Eine vollständige Liste der unterstützten Operationen finden Sie in der erweiterten Dokumentation .

Nicht unterstützte Modelle und Operationen

Wenn einige der Operationen vom GPU-Delegaten nicht unterstützt werden, führt das Framework nur einen Teil des Diagramms auf der GPU und den verbleibenden Teil auf der CPU aus. Aufgrund der hohen Kosten für die CPU/GPU-Synchronisierung führt ein solcher geteilter Ausführungsmodus oft zu einer geringeren Leistung, als wenn das gesamte Netzwerk nur auf der CPU ausgeführt wird. In diesem Fall erhält der Benutzer eine Warnung wie:

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

Wir haben keinen Rückruf für diesen Fehler bereitgestellt, da dies kein echter Laufzeitfehler ist, sondern etwas, das der Entwickler beobachten kann, während er versucht, das Netzwerk auf dem Delegaten zum Laufen zu bringen.

Tipps zur Optimierung

Einige Vorgänge, die auf der CPU trivial sind, können hohe Kosten für die GPU verursachen. Eine Klasse einer solchen Operation sind verschiedene Formen von BATCH_TO_SPACE , einschließlich BATCH_TO_SPACE , SPACE_TO_BATCH , SPACE_TO_DEPTH und so weiter. Wenn diese Ops nur für das logische Denken des Netzwerkarchitekten in das Netzwerk eingefügt werden, lohnt es sich, sie aus Gründen der Leistung zu entfernen.

Auf der GPU werden die Tensordaten in 4 Kanäle aufgeteilt. Eine Berechnung mit einem Tensor der Form [B,H,W,5] wird also mit einem Tensor der Form [B,H,W,8] ungefähr gleich [B,H,W,8] aber deutlich schlechter als [B,H,W,4] .

In diesem Sinne, wenn die Kamerahardware Bildframes in RGBA unterstützt, ist das Einspeisen dieses 4-Kanal-Eingangs wesentlich schneller, da eine Speicherkopie (von 3-Kanal-RGB zu 4-Kanal-RGBX) vermieden werden kann.

Zögern Sie nicht, Ihren Klassifikator mit einer für Mobilgeräte optimierten Netzwerkarchitektur neu zu trainieren, um die beste Leistung zu erzielen. Dies ist ein wesentlicher Teil der Optimierung für die Inferenz auf dem Gerät.