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 doInterpreter::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:
- Edytuj ustawienia kompilacji, wybierając Produkt > Schemat > Edytuj schemat... , a następnie wybierając Uruchom .
- Na karcie informacje zmień konfigurację kompilacji na wydanie i odznacz Debug executable .
- Kliknij kartę Opcje i zmień Przechwytywanie klatek GPU na Wyłączone , a Walidacja interfejsu API metalu na Wyłączone .
- 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 .
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 .