W tym dokumencie wyjaśniono, jak rozszerzyć obsługę TensorFlow w celu monitorowania różnych systemów pamięci masowej w celu odkrywania nowych (wersji) modeli lub danych do obsługi. W szczególności omawia sposób tworzenia i używania modułu monitorującego ścieżkę systemu pamięci masowej pod kątem pojawiania się nowych podścieżek, przy czym każda podścieżka reprezentuje nową wersję do załadowania. Ten rodzaj modułu nazywa się Source<StoragePath>
, ponieważ emituje obiekty typu StoragePath
(wpisane jako string
). Można go złożyć z SourceAdapter
, który tworzy obsługiwany Loader
z danej ścieżki wykrytej przez źródło.
Na początek uwaga o ogólności
Używanie ścieżek jako uchwytów do danych, które można udostępnić, nie jest wymagane; ilustruje jedynie jeden ze sposobów przyjmowania elementów serwowalnych do systemu. Nawet jeśli Twoje środowisko nie hermetyzuje udostępnianych danych w ścieżkach, ten dokument zapozna Cię z kluczowymi abstrakcjami. Masz możliwość utworzenia modułów Source<T>
i SourceAdapter<T1, T2>
dla typów pasujących do Twojego środowiska (np. komunikaty RPC lub pub/sub, rekordy bazy danych) lub po prostu utworzenia monolitycznego Source<std::unique_ptr<Loader>>
, który bezpośrednio emituje obsługiwane programy ładujące.
Oczywiście niezależnie od rodzaju danych emitowanych przez Twoje źródło (czy są to ścieżki POSIX, ścieżki Google Cloud Storage czy uchwyty RPC), muszą istnieć towarzyszące moduły, które będą w stanie załadować na tej podstawie serwable. Takie moduły nazywane są SourceAdapters
. Tworzenie niestandardowego jest opisane w dokumencie Custom Servable . TensorFlow Serving jest dostarczany z jednym do tworzenia instancji sesji TensorFlow w oparciu o ścieżki w systemach plików obsługiwanych przez TensorFlow. Można dodać obsługę dodatkowych systemów plików do TensorFlow, rozszerzając abstrakcję RandomAccessFile
( tensorflow/core/public/env.h
).
Ten dokument skupia się na tworzeniu źródła, które emituje ścieżki w systemie plików obsługiwanym przez TensorFlow. Kończy się omówieniem, jak używać źródła w połączeniu z wcześniej istniejącymi modułami do obsługi modeli TensorFlow.
Tworzenie swojego źródła
Mamy referencyjną implementację Source<StoragePath>
o nazwie FileSystemStoragePathSource
(w sources/storage_path/file_system_storage_path_source*
). FileSystemStoragePathSource
monitoruje określoną ścieżkę systemu plików, szuka numerycznych podkatalogów i raportuje najnowszy z nich jako wersję, którą zamierza załadować. W tym dokumencie omówiono najważniejsze aspekty FileSystemStoragePathSource
. Może się okazać, że wygodnie będzie utworzyć kopię pliku FileSystemStoragePathSource
, a następnie zmodyfikować ją zgodnie ze swoimi potrzebami.
Po pierwsze, FileSystemStoragePathSource
implementuje interfejs API Source<StoragePath>
, który jest specjalizacją interfejsu API Source<T>
z T
powiązanym z StoragePath
. Interfejs API składa się z pojedynczej metody SetAspiredVersionsCallback()
, która zapewnia zamknięcie, które źródło może wywołać, aby zakomunikować, że chce załadować określony zestaw obsługiwanych wersji.
FileSystemStoragePathSource
używa wywołania zwrotnego aspired-versions w bardzo prosty sposób: okresowo sprawdza system plików (w zasadzie wykonując ls
) i jeśli znajdzie jedną lub więcej ścieżek wyglądających na wersje możliwe do obsłużenia, określa, która z nich jest najnowszą wersją i wywołuje wywołanie zwrotne z listą rozmiaru pierwszego zawierającą właśnie tę wersję (w konfiguracji domyślnej). Zatem w dowolnym momencie FileSystemStoragePathSource
żąda załadowania co najwyżej jednego obiektu obsługowego, a jego implementacja wykorzystuje idempotencję wywołania zwrotnego, aby zachować bezstanowość (nie ma nic złego w wielokrotnym wywoływaniu wywołania zwrotnego z tymi samymi argumentami).
FileSystemStoragePathSource
ma statyczną fabrykę inicjalizacji (metoda Create()
), która pobiera komunikat protokołu konfiguracyjnego. Komunikat konfiguracyjny zawiera szczegółowe informacje, takie jak ścieżka podstawowa do monitorowania i interwał monitorowania. Zawiera także nazwę strumienia, który ma zostać wyemitowany. (Alternatywne podejścia mogą wyodrębnić nazwę strumienia, który można obsłużyć, ze ścieżki podstawowej, aby wyemitować wiele strumieni, które można obsłużyć w oparciu o obserwację głębszej hierarchii katalogów; te warianty wykraczają poza zakres implementacji referencyjnej).
Większość implementacji składa się z wątku, który okresowo sprawdza system plików, wraz z pewną logiką identyfikowania i sortowania wszelkich wykrytych podścieżek numerycznych. Wątek jest uruchamiany wewnątrz SetAspiredVersionsCallback()
(nie w Create()
), ponieważ jest to punkt, w którym źródło powinno „zacząć się” i wie, gdzie wysłać żądania wersji aspirowanej.
Używanie źródła do ładowania sesji TensorFlow
Prawdopodobnie będziesz chciał użyć nowego modułu źródłowego w połączeniu z SavedModelBundleSourceAdapter
( servables/tensorflow/saved_model_bundle_source_adapter*
), który zinterpretuje każdą ścieżkę emitowaną przez twoje źródło jako eksport TensorFlow i przekonwertuje każdą ścieżkę na moduł ładujący dla servable TensorFlow SavedModelBundle
. Prawdopodobnie podłączysz adapter SavedModelBundle
do AspiredVersionsManager
, który zajmie się faktycznym ładowaniem i udostępnianiem serwable. Dobrą ilustrację łączenia tych trzech rodzajów modułów w celu uzyskania działającej biblioteki serwera można znaleźć w servables/tensorflow/simple_servers.cc
. Oto przegląd głównego przepływu kodu (ze złą obsługą błędów; prawdziwy kod powinien być bardziej ostrożny):
Najpierw utwórz menedżera:
std::unique_ptr<AspiredVersionsManager> manager = ...;
Następnie utwórz adapter źródłowy SavedModelBundle
i podłącz go do menedżera:
std::unique_ptr<SavedModelBundleSourceAdapter> bundle_adapter;
SavedModelBundleSourceAdapterConfig config;
// ... populate 'config' with TensorFlow options.
TF_CHECK_OK(SavedModelBundleSourceAdapter::Create(config, &bundle_adapter));
ConnectSourceToTarget(bundle_adapter.get(), manager.get());
Na koniec utwórz źródło ścieżki i podłącz je do adaptera SavedModelBundle
:
auto your_source = new YourPathSource(...);
ConnectSourceToTarget(your_source, bundle_adapter.get());
Funkcja ConnectSourceToTarget()
(zdefiniowana w core/target.h
) jedynie wywołuje SetAspiredVersionsCallback()
w celu połączenia Source<T>
z Target<T>
( Target
to moduł przechwytujący żądania wersji aspirowanej, tj. adapter lub menedżer ).