Tworzenie nowego rodzaju serwowalnego

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

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:

  1. Loader klasa obciążeń, zapewnia dostęp do, i rozładowuje instancją YourServable .

  2. SourceAdapter że wystąpienie ładowarki z ścieżek plików systemowych jakiegoś bazowego format danych np. Jako alternatywę do SourceAdapter , można napisać kompletne Source . 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:

  1. Logika załadować HashMap z pliku, w LoadHashmapFromFile() .

  2. Zastosowanie SimpleLoaderSourceAdapter zdefiniować SourceAdapter emitujący hashmap ładowarki podstawie LoadHashmapFromFile() . Nowy SourceAdapter może być instancja z wiadomości Configuration Protocol typu HashmapSourceAdapterConfig . 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ć.