Delegat do akceleracji GPU w przypadku iOS

Zastosowanie procesorów graficznych (GPU) do uruchamiania modeli systemów uczących się może znacznie zwiększyć wydajność modelu i wygodę użytkowników aplikacji obsługujących systemy uczące się. Na urządzeniach z iOS możesz włączyć akcelerację renderowania modeli za pomocą przekazywania. Przedstawiciele działają jako sterowniki sprzętowe TensorFlow Lite, co pozwala Ci uruchamiać kod modelu na procesorach GPU.

Na tej stronie opisujemy, jak włączyć akcelerację GPU w modelach TensorFlow Lite w aplikacjach na iOS. Więcej informacji o używaniu delegata GPU w przypadku TensorFlow Lite, w tym o sprawdzonych metodach i zaawansowanych technikach, znajdziesz na stronie delegatów GPU.

Używanie GPU z interfejsem Interpreter API

Interfejs API interpretera TensorFlow Lite udostępnia zestaw interfejsów API ogólnych przeznaczenia do tworzenia aplikacji systemów uczących się. Z instrukcji poniżej dowiesz się, jak dodać obsługę GPU do aplikacji na iOS. W tym przewodniku zakładamy, że masz już aplikację na iOS, która może wykonywać model ML za pomocą TensorFlow Lite.

Zmodyfikuj plik Podfile, aby uwzględnić w nim obsługę GPU

Od wersji TensorFlow Lite 2.3.0 delegat GPU jest wykluczany z poda, aby zmniejszyć rozmiar pliku binarnego. Możesz je uwzględnić, określając podspecyfikację poda TensorFlowLiteSwift:

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

LUB

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

Możesz też użyć polecenia TensorFlowLiteObjC lub TensorFlowLiteC, jeśli chcesz użyć interfejsu Objective-C, który jest dostępny w wersji 2.4.0 lub nowszej, albo interfejsu C API.

Inicjowanie i używanie przedstawicieli GPU

Delegata GPU możesz używać z interfejsem Interpreter API TensorFlow Lite w wielu językach programowania. Zalecamy używanie kodu Swift i Objective-C, ale możesz też używać języków C++ i C. Używanie C jest wymagane, jeśli używasz TensorFlow Lite w wersji starszej niż 2.4. W podanych niżej przykładach kodu dowiesz się, jak używać przedstawicieli w przypadku każdego z tych języków.

Swift

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

Objective-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++

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

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

C (przed 2.4.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);
      

Uwagi dotyczące użycia języka interfejsu GPU API

  • Wersje TensorFlow Lite starsze niż 2.4.0 mogą używać tylko interfejsu API C w przypadku Objective-C.
  • Interfejs C++ API jest dostępny tylko wtedy, gdy korzystasz z bazela lub samodzielnie tworzysz TensorFlow Lite. Interfejsu C++ API nie można używać z CocoaPods.
  • Jeśli używasz TensorFlow Lite z przekazywaniem GPU w C++, pobierz przekazanie GPU za pomocą funkcji TFLGpuDelegateCreate(), a następnie przekaż je do Interpreter::ModifyGraphWithDelegate(), zamiast wywoływać Interpreter::AllocateTensors().

Kompilowanie i testowanie w trybie wydania

Przejdź na kompilację wersji z odpowiednimi ustawieniami akceleratora Metal API, aby uzyskać lepszą wydajność i przeprowadzić ostateczne testy. W tej sekcji dowiesz się, jak włączyć kompilację wersji i skonfigurować ustawienie akceleracji Metal.

Aby przejść na kompilację wersji:

  1. Zmień ustawienia kompilacji: wybierz Produkt > Schemat > Edytuj schemat..., a następnie kliknij Uruchom.
  2. Na karcie Informacje zmień Konfiguracja kompilacji na Wersja i odznacz Debuguj plik wykonywalny. skonfigurowanie wersji
  3. Kliknij kartę Options (Opcje) i zmień ustawienie GPU Frame Capture na Disabled (Wyłączone), a Metal API Sprawdzanie poprawności na wartość Disabled (Wyłączone).
    konfigurowanie opcji metalu
  4. Wybierz kompilacje tylko do publikacji i architekturę 64-bitową. W sekcji Nawigator projektu > tflite_camera_example > PROJECT > your_project_name > Build Settings (Ustawienia kompilacji) ustaw Build tylko aktywna architektura > Wersja na Tak. ustawienia wersji

Zaawansowana obsługa GPU

W tej sekcji omawiamy zaawansowane sposoby korzystania z delegata GPU w przypadku iOS, w tym przekazywanie opcji, buforowanie danych wejściowych i wyjściowych oraz korzystanie z modeli skwantyzowanych.

Przekazywanie dostępu do opcji w systemie iOS

Konstruktor delegata GPU akceptuje wartość struct opcji interfejsów Swift API, Objective-C API i C API. Przekazywanie do inicjatora funkcji nullptr (C API) lub niczego (Objective-C i Swift API) powoduje ustawienie opcji domyślnych (które opisujemy w powyższym przykładzie użycia podstawowego).

Swift

// 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()
      

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

Bufory danych wejściowych/wyjściowych korzystające z interfejsu C++ API

Przetwarzanie danych na GPU wymaga, aby dane były dla niego dostępne. Często z tego powodu musisz wykonać kopię pamięci. W miarę możliwości unikaj przekraczania granic pamięci procesora i GPU, ponieważ może to zająć dużo czasu. Zwykle takie przejście jest nieuniknione, ale w niektórych przypadkach jeden lub drugi element można pominąć.

Jeśli danymi wejściowego sieci jest obraz załadowany już w pamięci GPU (np. tekstura GPU zawierająca obraz z kamery), może on pozostać w pamięci GPU bez konieczności wprowadzania jej do pamięci procesora. I podobnie, jeśli dane wyjściowe sieci mają postać renderowanego obrazu (np. operacji transferu stylu obrazu), możesz bezpośrednio wyświetlić wynik na ekranie.

Aby uzyskać najlepszą wydajność, TensorFlow Lite pozwala użytkownikom na bezpośrednie odczytywanie i zapisywanie w buforze sprzętowym TensorFlow oraz pomijanie kopii pamięci, których można uniknąć.

Zakładając, że dane wejściowe obrazu są w pamięci GPU, musisz najpierw przekonwertować go na obiekt MTLBuffer dla Metal. Możesz powiązać element TfLiteTensor z przygotowanym przez użytkownika elementem MTLBuffer za pomocą funkcji TFLGpuDelegateBindMetalBufferToTensor(). Pamiętaj, że ta funkcja musi zostać wywołana po Interpreter::ModifyGraphWithDelegate(). Dodatkowo dane wyjściowe wnioskowania są domyślnie kopiowane z pamięci GPU do pamięci procesora. Możesz wyłączyć to zachowanie, wywołując Interpreter::SetAllowBufferHandleOutput(true) podczas inicjowania.

C++

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

Po wyłączeniu działania domyślnego kopiowanie danych wyjściowych wnioskowania z pamięci GPU do pamięci procesora wymaga jawnego wywołania Interpreter::EnsureTensorDataIsReadable() dla każdego tensora wyjściowego. Ta metoda działa również w przypadku modeli kwantyzowanych, ale nadal musisz użyć bufora o rozmiarze float32 z danymi float32, ponieważ jest on powiązany z wewnętrznym, zdeskwantyzowanym buforem.

Modele kwantyzowane

Biblioteki delegowane przez GPU na iOS domyślnie obsługują modele poddane kwantyzacji. Aby używać skwantyzowanych modeli z delegatem GPU, nie musisz wprowadzać żadnych zmian w kodzie. W sekcji poniżej wyjaśniono, jak wyłączyć obsługę ilościową na potrzeby testowania lub eksperymentu.

Wyłącz obsługę modelu skwantyzowanego

Poniższy kod pokazuje, jak wyłączyć obsługę modeli skwantyzowanych.

Swift

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

Objective-C

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

C

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

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Więcej informacji o uruchamianiu modeli skwantyzowanych z akceleracją GPU znajdziesz w artykule o przekazywaniu delegacji GPU.