Ten dokument wyjaśnia, jak rozszerzyć TensorFlow Serving o nowy rodzaj obsługi. Najwybitniejszym servable typ jest SavedModelBundle
, ale może to być przydatne do określenia innych rodzajów servables, aby służyć dane, które idzie w parze z modelu. Przykłady obejmują: tabelę przeglądową słownika, logikę transformacji funkcji. Wszelkie C ++ klasa może być servable np int
, std::map<string, int>
lub dowolny klasa zdefiniowana w pliku binarnym - nazwijmy go YourServable
.
Definiowanie Loader
i SourceAdapter
dla YourServable
Aby włączyć TensorFlow służącym do zarządzania i służyć YourServable
, trzeba zdefiniować dwie rzeczy:
Loader
klasa obciążeń, zapewnia dostęp do, i rozładowuje instancjąYourServable
.SourceAdapter
że wystąpienie ładowarki z ścieżek plików systemowych jakiegoś bazowego format danych np. Jako alternatywę doSourceAdapter
, można napisać kompletneSource
. Jednakże, ponieważSourceAdapter
podejście jest bardziej powszechne i bardziej modularny, skupiamy się na tym tutaj.
Loader
abstrakcji jest określona w core/loader.h
. Wymaga zdefiniowania metod ładowania, uzyskiwania dostępu i rozładowywania typu serwowalnego. Dane, z których ładowany jest serwer, mogą pochodzić z dowolnego miejsca, ale często pochodzą one ze ścieżki systemu pamięci masowej. Załóżmy, że w przypadku YourServable
. Załóżmy dalej, masz już Source<StoragePath>
że jesteś zadowolony z (jeśli nie, zobacz Niestandardowe Źródło dokumentu).
Oprócz swojej Loader
, trzeba będzie zdefiniować SourceAdapter
że instancję Loader
z danej ścieżki pamięci. Większość prostych przypadków użycia można określić dwa obiekty zwięźle z SimpleLoaderSourceAdapter
klasy (w core/simple_loader.h
). Zaawansowane użycie gabloty mogą zdecydować się podać Loader
i SourceAdapter
zajęcia oddzielnie przy użyciu API niższego poziomu, np jeśli SourceAdapter
musi zachować pewien stan, i / lub w przypadku potrzeby państwowe mają być dzielone między Loader
przypadkach.
Jest to implementacja prostego HashMap servable który używa SimpleLoaderSourceAdapter
w servables/hashmap/hashmap_source_adapter.cc
. Może się okazać, że wygodnie zrobić kopię HashmapSourceAdapter
a następnie zmodyfikować go do własnych potrzeb.
Realizacja HashmapSourceAdapter
ma dwie części:
Logika załadować HashMap z pliku, w
LoadHashmapFromFile()
.Zastosowanie
SimpleLoaderSourceAdapter
zdefiniowaćSourceAdapter
emitujący hashmap ładowarki podstawieLoadHashmapFromFile()
. NowySourceAdapter
może być instancja z wiadomości Configuration Protocol typuHashmapSourceAdapterConfig
. Obecnie komunikat konfiguracyjny zawiera tylko format pliku, a na potrzeby implementacji referencyjnej obsługiwany jest tylko jeden prosty format.Uwaga wywołanie
Detach()
w destructor. To wezwanie jest wymagane, aby uniknąć wyścigu między stanem burzenia a trwającymi wywołaniami lambdy Twórcy w innych wątkach. (Mimo że ten prosty adapter źródłowy nie ma żadnego stanu, klasa bazowa wymusza jednak wywołanie Detach()).
Organizowanie YourServable
obiekty mają być załadowane w menedżera
Oto, jak podłączyć swój nowy SourceAdapter
dla YourServable
ładowarki do podstawowego źródła ścieżek pamięci masowej, a kierownik (ze złej obsługi błędów; prawdziwy kod powinien być bardziej ostrożny):
Najpierw utwórz menedżera:
std::unique_ptr<AspiredVersionsManager> manager = ...;
Następnie utwórz YourServable
adapter źródłowego i podłączyć go do kierownika:
auto your_adapter = new YourServableSourceAdapter(...);
ConnectSourceToTarget(your_adapter, manager.get());
Na koniec utwórz proste źródło ścieżki i podłącz je do adaptera:
std::unique_ptr<FileSystemStoragePathSource> path_source;
// Here are some FileSystemStoragePathSource config settings that ought to get
// it working, but for details please see its documentation.
FileSystemStoragePathSourceConfig config;
// We just have a single servable stream. Call it "default".
config.set_servable_name("default");
config.set_base_path(FLAGS::base_path /* base path for our servable files */);
config.set_file_system_poll_wait_seconds(1);
TF_CHECK_OK(FileSystemStoragePathSource::Create(config, &path_source));
ConnectSourceToTarget(path_source.get(), your_adapter.get());
Dostęp do załadowanych YourServable
przedmiotów
Oto jak uzyskać uchwyt do załadowanego YourServable
i używać go:
auto handle_request = serving::ServableRequest::Latest("default");
ServableHandle<YourServable*> servable;
Status status = manager->GetServableHandle(handle_request, &servable);
if (!status.ok()) {
LOG(INFO) << "Zero versions of 'default' servable have been loaded so far";
return;
}
// Use the servable.
(*servable)->SomeYourServableMethod();
Zaawansowane: organizowanie wielu udostępnianych instancji do udostępniania stanu
Adaptery SourceAdapters mogą przechowywać stan, który jest współdzielony przez wiele emitowanych serwerów. Na przykład:
Współdzielona pula wątków lub inny zasób, z którego korzysta wiele serwerów.
Udostępniona struktura danych tylko do odczytu, z której korzysta wiele obiektów udostępnianych, aby uniknąć narzutu czasowego i przestrzennego związanego z replikacją struktury danych w każdej możliwej do udostępnienia instancji.
Stan współdzielony, którego czas i rozmiar inicjalizacji jest znikomy (np. pule wątków) może być chętnie tworzony przez SourceAdapter, który następnie osadza wskaźnik do niego w każdym emitowanym ładującym serwable. Tworzenie drogiego lub dużego stanu współdzielenia powinno być odroczone do pierwszego odpowiedniego wywołania Loader::Load(), tj. zarządzanego przez menedżera. Symetrycznie, wywołanie Loader::Unload() do końcowego serwowalnego przy użyciu współużytkowanego stanu drogi/duży powinno go zniszczyć.