Delegat akceleracji GPU dla iOS

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Używanie jednostek przetwarzania grafiki (GPU) do uruchamiania modeli uczenia maszynowego (ML) może znacznie poprawić wydajność modelu i wrażenia użytkownika aplikacji obsługujących ML. Na urządzeniach z systemem iOS można włączyć akcelerowane przez GPU wykonywanie modeli za pomocą delegata . Delegaci działają jako sterowniki sprzętowe dla TensorFlow Lite, umożliwiając uruchamianie kodu modelu na procesorach GPU.

Na tej stronie opisano, jak włączyć akcelerację GPU dla modeli TensorFlow Lite w aplikacjach na iOS. Aby uzyskać więcej informacji na temat używania delegata GPU dla TensorFlow Lite, w tym najlepszych praktyk i zaawansowanych technik, zobacz stronę delegatów GPU .

Użyj procesora graficznego z interfejsem API interpretera

TensorFlow Lite Interpreter API zapewnia zestaw interfejsów API ogólnego przeznaczenia do tworzenia aplikacji uczenia maszynowego. Poniższe instrukcje poprowadzą Cię przez proces dodawania obsługi GPU do aplikacji na iOS. W tym przewodniku założono, że masz już aplikację na iOS, która może pomyślnie wykonać model ML za pomocą TensorFlow Lite.

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

Począwszy od wersji TensorFlow Lite 2.3.0, delegat GPU jest wykluczony z pod, aby zmniejszyć rozmiar binarny. Możesz je uwzględnić, określając podspecyfikację dla pod TensorFlowLiteSwift :

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

LUB

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

Możesz również użyć TensorFlowLiteObjC lub TensorFlowLiteC , jeśli chcesz użyć Objective-C, który jest dostępny dla wersji 2.4.0 i wyższych, lub C API.

Zainicjuj i użyj delegata GPU

Możesz użyć delegata GPU z interfejsem API TensorFlow Lite Interpreter z wieloma językami programowania. Zalecane są Swift i Objective-C, ale można również używać C++ i C. Korzystanie z C jest wymagane, jeśli używasz wersji TensorFlow Lite wcześniejszej niż 2.4. W poniższych przykładach kodu opisano, jak używać delegata w każdym z tych języków.

Szybki

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

Cel 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żywania języka GPU API

  • Wersje TensorFlow Lite wcześniejsze niż 2.4.0 mogą używać tylko C API dla Objective-C.
  • API C++ jest dostępne tylko wtedy, gdy korzystasz z bazela lub samodzielnie budujesz TensorFlow Lite. C++ API nie może być używane z CocoaPods.
  • Używając TensorFlow Lite z delegatem GPU z C++, pobierz delegata GPU za pomocą funkcji TFLGpuDelegateCreate() , a następnie przekaż go do Interpreter::ModifyGraphWithDelegate() , zamiast wywoływać Interpreter::AllocateTensors() .

Twórz i testuj w trybie wydania

Zmień na kompilację wydania z odpowiednimi ustawieniami akceleratora interfejsu Metal API, aby uzyskać lepszą wydajność i do testów końcowych. W tej sekcji wyjaśniono, jak włączyć kompilację wydania i skonfigurować ustawienie przyspieszenia Metal.

Aby zmienić na kompilację wydania:

  1. Edytuj ustawienia kompilacji, wybierając Produkt > Schemat > Edytuj schemat... , a następnie wybierając Uruchom .
  2. Na karcie informacje zmień konfigurację kompilacji na wydanie i odznacz Debug executable .konfigurowanie wydania
  3. Kliknij kartę Opcje i zmień Przechwytywanie klatek GPU na Wyłączone , a Walidacja interfejsu API metalu na Wyłączone .
    konfigurowanie opcji metalowych
  4. Pamiętaj, aby wybrać kompilacje tylko do wydania w architekturze 64-bitowej. W obszarze Nawigator projektu > przykład_kamery tflite > PROJEKT > nazwa_projektu > Ustawienia kompilacji ustaw opcję Zbuduj tylko aktywną architekturę > Zwolnij na Tak . konfigurowanie opcji wydania

Zaawansowana obsługa GPU

W tej sekcji omówiono zaawansowane zastosowania delegata GPU dla systemu iOS, w tym opcje delegata, bufory wejściowe i wyjściowe oraz użycie modeli skwantowanych.

Opcje delegatów dla iOS

Konstruktor delegata GPU akceptuje struct opcji w interfejsach API Swift , Objective-C API i C API . Przekazanie nullptr (C API) lub nic (Objective-C i Swift API) do inicjatora ustawia opcje domyślne (które są wyjaśnione w powyższym przykładzie Podstawowe użycie).

Szybki

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

Cel 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 wejścia/wyjścia przy użyciu C++ API

Obliczenia na GPU wymagają, aby dane były dostępne dla GPU. To wymaganie często oznacza, że ​​musisz wykonać kopię pamięci. Jeśli to możliwe, należy unikać przekraczania granicy pamięci CPU/GPU, ponieważ może to zająć dużo czasu. Zazwyczaj takie przejście jest nieuniknione, ale w niektórych szczególnych przypadkach jedno lub drugie można pominąć.

Jeśli sygnałem wejściowym sieci jest obraz już załadowany do pamięci GPU (na przykład tekstura GPU zawierająca obraz z kamery), może on pozostać w pamięci GPU bez wchodzenia do pamięci procesora. Podobnie, jeśli dane wyjściowe sieci mają postać renderowalnego obrazu, takiego jak operacja przesyłania stylu obrazu , wynik można wyświetlić bezpośrednio na ekranie.

Aby osiągnąć najlepszą wydajność, TensorFlow Lite umożliwia użytkownikom bezpośredni odczyt i zapis do bufora sprzętowego TensorFlow oraz ominięcie kopii pamięci, których można uniknąć.

Zakładając, że obraz wejściowy znajduje się w pamięci GPU, musisz najpierw przekonwertować go na obiekt MTLBuffer dla Metal. TfLiteTensor można skojarzyć z TfLiteTensor przygotowanym przez użytkownika MTLBuffer pomocą funkcji TFLGpuDelegateBindMetalBufferToTensor() . Zauważ, że ta funkcja musi być wywołana po Interpreter::ModifyGraphWithDelegate() . Ponadto 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 inicjalizacji.

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 zachowania domyślnego kopiowanie danych wyjściowych wnioskowania z pamięci GPU do pamięci procesora wymaga jawnego wywołania funkcji Interpreter::EnsureTensorDataIsReadable() dla każdego tensora wyjściowego. To podejście działa również w przypadku modeli skwantyzowanych, ale nadal musisz używać bufora o rozmiarze float32 z danymi float32 , ponieważ bufor jest powiązany z wewnętrznym buforem zdekwantyzowanym.

Modele skwantowane

Biblioteki delegatów GPU systemu iOS domyślnie obsługują modele kwantowane . Nie trzeba wprowadzać żadnych zmian w kodzie, aby używać modeli skwantyzowanych z delegatem GPU. Poniższa sekcja wyjaśnia, jak wyłączyć obsługę kwantową do celów testowych lub eksperymentalnych.

Wyłącz obsługę modeli skwantowanych

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

Szybki

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

Cel C

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

C

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

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Aby uzyskać więcej informacji na temat uruchamiania modeli skwantyzowanych z akceleracją GPU, zobacz Omówienie delegata GPU .