Создание модуля, который обнаруживает новые обслуживаемые пути

В этом документе объясняется, как расширить TensorFlow Serving для мониторинга различных систем хранения с целью обнаружения новых (версий) моделей или данных для обслуживания. В частности, здесь описано, как создать и использовать модуль, который отслеживает путь системы хранения на предмет появления новых подпутей, где каждый подпуть представляет собой новую обслуживаемую версию для загрузки. Такой модуль называется Source<StoragePath> , поскольку он генерирует объекты типа StoragePath (тип которого определен как string ). Его можно составить с помощью SourceAdapter , который создает обслуживаемый Loader по заданному пути, который обнаруживает источник.

Сначала замечание об общности

Использование путей в качестве дескрипторов обслуживаемых данных не требуется; он просто иллюстрирует один из способов добавления обслуживаемых объектов в систему. Даже если ваша среда не инкапсулирует обслуживаемые данные в пути, этот документ познакомит вас с ключевыми абстракциями. У вас есть возможность создать модули Source<T> и SourceAdapter<T1, T2> для типов, которые подходят вашей среде (например, сообщения RPC или публикации/подписки, записи базы данных) или просто создать монолитный Source<std::unique_ptr<Loader>> , который напрямую генерирует обслуживаемые загрузчики.

Конечно, какой бы тип данных ни отправлял ваш источник (будь то пути POSIX, пути Google Cloud Storage или дескрипторы RPC), должны быть сопутствующие модули, которые могут загружать обслуживаемые объекты на основе этого. Такие модули называются SourceAdapters . Создание пользовательского файла описано в документе Custom Servable . В состав TensorFlow Serving входит один для создания экземпляров сеансов TensorFlow на основе путей в файловых системах, которые поддерживает TensorFlow. В TensorFlow можно добавить поддержку дополнительных файловых систем, расширив абстракцию RandomAccessFile ( tensorflow/core/public/env.h ).

В этом документе основное внимание уделяется созданию источника, который генерирует пути в файловой системе, поддерживаемой TensorFlow. Он заканчивается описанием того, как использовать ваш исходный код в сочетании с уже существующими модулями для обслуживания моделей TensorFlow.

Создание вашего источника

У нас есть эталонная реализация Source<StoragePath> , называемая FileSystemStoragePathSourcesources/storage_path/file_system_storage_path_source* ). FileSystemStoragePathSource отслеживает определенный путь к файловой системе, отслеживает числовые подкаталоги и сообщает о последней из них как о версии, которую он стремится загрузить. В этом документе рассматриваются основные аспекты FileSystemStoragePathSource . Возможно, вам будет удобно сделать копию FileSystemStoragePathSource , а затем изменить ее в соответствии со своими потребностями.

Во-первых, FileSystemStoragePathSource реализует API Source<StoragePath> , который является специализацией API Source<T> с T , привязанным к StoragePath . API состоит из одного метода SetAspiredVersionsCallback() , который предоставляет замыкание, которое источник может вызвать, чтобы сообщить, что он хочет загрузить определенный набор обслуживаемых версий.

FileSystemStoragePathSource использует обратный вызов aspired-versions очень простым способом: он периодически проверяет файловую систему (по сути, выполняя ls ), и если он находит один или несколько путей, которые выглядят как обслуживаемые версии, он определяет, какой из них является последней версией, и вызывает обратный вызов со списком размером один, содержащим только эту версию (в конфигурации по умолчанию). Таким образом, в любой момент времени FileSystemStoragePathSource запрашивает для загрузки не более одного обслуживаемого объекта, и его реализация использует преимущество идемпотентности обратного вызова, чтобы оставаться без состояния (нет никакого вреда в многократном вызове обратного вызова с теми же аргументами).

FileSystemStoragePathSource имеет статическую фабрику инициализации (метод Create() ), которая принимает сообщение протокола конфигурации. Сообщение конфигурации включает такие сведения, как базовый путь для мониторинга и интервал мониторинга. Он также включает имя обслуживаемого потока, который будет излучаться. (Альтернативные подходы могут извлекать имя обслуживаемого потока из базового пути для создания нескольких обслуживаемых потоков на основе наблюдения за более глубокой иерархией каталогов; эти варианты выходят за рамки эталонной реализации.)

Основная часть реализации состоит из потока, который периодически проверяет файловую систему, а также некоторой логики для идентификации и сортировки любых числовых подпутей, которые он обнаруживает. Поток запускается внутри SetAspiredVersionsCallback() (а не в Create() ), поскольку это точка, с которой источник должен «запуститься» и знает, куда отправлять запросы желаемой версии.

Использование вашего источника для загрузки сеансов TensorFlow

Вероятно, вы захотите использовать новый исходный модуль вместе с SavedModelBundleSourceAdapter ( servables/tensorflow/saved_model_bundle_source_adapter* ), который будет интерпретировать каждый путь, который выдает ваш источник, как экспорт TensorFlow, и преобразовывать каждый путь в загрузчик для обслуживаемого файла TensorFlow SavedModelBundle . Скорее всего, вы подключите адаптер SavedModelBundle к AspiredVersionsManager , который позаботится о фактической загрузке и обслуживании обслуживаемых объектов. Хорошая иллюстрация объединения этих трех типов модулей для получения работающей серверной библиотеки находится в servables/tensorflow/simple_servers.cc . Вот пошаговое описание основного потока кода (с плохой обработкой ошибок; реальный код должен быть более осторожным):

Сначала создайте менеджера:

std::unique_ptr<AspiredVersionsManager> manager = ...;

Затем создайте исходный адаптер SavedModelBundle и подключите его к менеджеру:

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

Наконец, создайте источник пути и подключите его к адаптеру SavedModelBundle :

auto your_source = new YourPathSource(...);
ConnectSourceToTarget(your_source, bundle_adapter.get());

Функция ConnectSourceToTarget() (определенная в core/target.h ) просто вызывает SetAspiredVersionsCallback() для подключения Source<T> к Target<T> ( Target — это модуль, который перехватывает запросы требуемой версии, т. е. адаптер или менеджер ).