Создание стандартного сервера модели TensorFlow

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

В этом руководстве используется простая модель регрессии Softmax, представленная в руководстве TensorFlow для классификации рукописных изображений (данные MNIST). Если вы не знаете, что такое TensorFlow или MNIST, см. учебник MNIST для начинающих ML .

Код этого руководства состоит из двух частей:

  • Файл Python mnist_saved_model.py , который обучает и экспортирует несколько версий модели.

  • Файл C++ main.cc , который является стандартным сервером моделей TensorFlow, который обнаруживает новые экспортированные модели и запускает службу gRPC для их обслуживания.

В этом руководстве рассматриваются следующие задачи:

  1. Обучите и экспортируйте модель TensorFlow.
  2. Управляйте версиями модели с помощью TensorFlow Serving ServerCore .
  3. Настройте пакетную обработку с помощью SavedModelBundleSourceAdapterConfig .
  4. Обслуживайте запрос с помощью TensorFlow Serving ServerCore .
  5. Запустите и протестируйте службу.

Прежде чем начать, сначала установите Docker

Обучение и экспорт модели TensorFlow

Во-первых, если вы еще этого не сделали, клонируйте этот репозиторий на свой локальный компьютер:

git clone https://github.com/tensorflow/serving.git
cd serving

Очистите каталог экспорта, если он уже существует:

rm -rf /tmp/models

Обучите (со 100 итерациями) и экспортируйте первую версию модели:

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=100 --model_version=1 /tmp/mnist

Обучите (с 2000 итерациями) и экспортируйте вторую версию модели:

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=2000 --model_version=2 /tmp/mnist

Как вы можете видеть в mnist_saved_model.py , обучение и экспорт выполняются так же, как и в базовом руководстве по обслуживанию TensorFlow . В демонстрационных целях вы намеренно сокращаете количество итераций обучения для первого запуска и экспортируете его как версию v1, в то время как обычно обучаете его для второго запуска и экспортируете как v2 в тот же родительский каталог - чего мы ожидаем от последнего. более высокая точность классификации за счет более интенсивного обучения. Вы должны увидеть данные обучения для каждого запуска обучения в вашем каталоге /tmp/mnist :

$ ls /tmp/mnist
1  2

Серверное ядро

Теперь представьте, что версии v1 и v2 модели динамически генерируются во время выполнения, когда экспериментируются новые алгоритмы или когда модель обучается с использованием нового набора данных. В производственной среде вам может потребоваться создать сервер, поддерживающий постепенное развертывание, на котором версию 2 можно будет обнаруживать, загружать, экспериментировать, отслеживать или отменять при обслуживании версии 1. В качестве альтернативы вы можете захотеть отключить версию v1, прежде чем поднимать версию v2. TensorFlow Serving поддерживает оба варианта: один из них хорош для поддержания доступности во время перехода, а другой — для минимизации использования ресурсов (например, оперативной памяти).

TensorFlow Serving Manager делает именно это. Он обрабатывает полный жизненный цикл моделей TensorFlow, включая их загрузку, обслуживание и выгрузку, а также смену версий. В этом руководстве вы создадите свой сервер на основе TensorFlow Serving ServerCore , который внутри является оболочкой AspiredVersionsManager .

int main(int argc, char** argv) {
  ...

  ServerCore::Options options;
  options.model_server_config = model_server_config;
  options.servable_state_monitor_creator = &CreateServableStateMonitor;
  options.custom_model_config_loader = &LoadCustomModelConfig;

  ::google::protobuf::Any source_adapter_config;
  SavedModelBundleSourceAdapterConfig
      saved_model_bundle_source_adapter_config;
  source_adapter_config.PackFrom(saved_model_bundle_source_adapter_config);
  (*(*options.platform_config_map.mutable_platform_configs())
      [kTensorFlowModelPlatform].mutable_source_adapter_config()) =
      source_adapter_config;

  std::unique_ptr<ServerCore> core;
  TF_CHECK_OK(ServerCore::Create(options, &core));
  RunServer(port, std::move(core));

  return 0;
}

ServerCore::Create() принимает параметр ServerCore::Options. Вот несколько часто используемых вариантов:

  • ModelServerConfig , определяющий модели для загрузки. Модели объявляются либо через model_config_list , который объявляет статический список моделей, либо через custom_model_config , который определяет собственный способ объявления списка моделей, которые могут обновляться во время выполнения.
  • PlatformConfigMap , который сопоставляет имя платформы (например, tensorflow ) с PlatformConfig , который используется для создания SourceAdapter . SourceAdapter адаптирует StoragePath (путь, по которому обнаружена версия модели) для Loader модели (загружает версию модели из пути хранения и предоставляет Manager интерфейсы перехода состояний). Если PlatformConfig содержит SavedModelBundleSourceAdapterConfig , будет создан SavedModelBundleSourceAdapter , который мы объясним позже.

SavedModelBundle — ключевой компонент TensorFlow Serving. Он представляет модель TensorFlow, загруженную по заданному пути, и предоставляет тот же интерфейс Session::Run , что и TensorFlow, для выполнения вывода. SavedModelBundleSourceAdapter адаптирует путь хранения к Loader<SavedModelBundle> , чтобы временем жизни модели можно было управлять с помощью Manager . Обратите внимание, что SavedModelBundle является преемником устаревшего SessionBundle . Пользователям рекомендуется использовать SavedModelBundle , поскольку поддержка SessionBundle скоро будет удалена.

При всем этом ServerCore внутренне выполняет следующие действия:

  • Создает экземпляр FileSystemStoragePathSource , который отслеживает пути экспорта модели, объявленные в model_config_list .
  • Создает SourceAdapter с помощью PlatformConfigMap с платформой модели, объявленной в model_config_list , и подключает к нему FileSystemStoragePathSource . Таким образом, всякий раз, когда по пути экспорта обнаруживается новая версия модели, SavedModelBundleSourceAdapter адаптирует ее к Loader<SavedModelBundle> .
  • Создает конкретную реализацию Manager под названием AspiredVersionsManager , которая управляет всеми такими экземплярами Loader , созданными SavedModelBundleSourceAdapter . ServerCore экспортирует интерфейс Manager , делегируя вызовы AspiredVersionsManager .

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

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

Пакетирование

Еще одна типичная функция сервера, которую мы хотим использовать в производственной среде, — это пакетная обработка. Современные аппаратные ускорители (графические процессоры и т. д.), используемые для вывода машинного обучения, обычно достигают наилучшей эффективности вычислений, когда запросы вывода выполняются большими пакетами.

Пакетную обработку можно включить, указав правильный SessionBundleConfig при создании SavedModelBundleSourceAdapter . В этом случае мы устанавливаем для BatchingParameters практически значения по умолчанию. Пакетную обработку можно настроить, установив собственные значения тайм-аута, размера пакета и т. д. Подробную информацию см. в BatchingParameters .

SessionBundleConfig session_bundle_config;
// Batching config
if (enable_batching) {
  BatchingParameters* batching_parameters =
      session_bundle_config.mutable_batching_parameters();
  batching_parameters->mutable_thread_pool_name()->set_value(
      "model_server_batch_threads");
}
*saved_model_bundle_source_adapter_config.mutable_legacy_config() =
    session_bundle_config;

При достижении полного пакета запросы на вывод внутренне объединяются в один большой запрос (тензор) и вызывается tensorflow::Session::Run() (именно отсюда и происходит фактическое повышение эффективности графических процессоров).

Обслуживать с менеджером

Как упоминалось выше, TensorFlow Serving Manager спроектирован как универсальный компонент, который может обрабатывать загрузку, обслуживание, выгрузку и смену версий моделей, созданных произвольными системами машинного обучения. Его API построены на основе следующих ключевых концепций:

  • Servable : Servable — это любой непрозрачный объект, который можно использовать для обслуживания клиентских запросов. Размер и степень детализации обслуживаемого объекта являются гибкими: один обслуживаемый объект может включать в себя что угодно: от одного фрагмента таблицы поиска до одной модели машинного обучения и кортежа моделей. Обслуживаемый объект может быть любого типа и интерфейса.

  • Версия обслуживаемого файла : сервируемые файлы имеют версии, и TensorFlow Serving Manager может управлять одной или несколькими версиями обслуживаемых файлов. Управление версиями позволяет одновременно загружать более одной версии обслуживаемого объекта, поддерживая постепенное развертывание и экспериментирование.

  • Обслуживаемый поток : Обслуживаемый поток — это последовательность версий обслуживаемого объекта с возрастающими номерами версий.

  • Модель : модель машинного обучения представлена ​​одним или несколькими обслуживаемыми объектами. Примеры обслуживаемых объектов:

    • Сеанс TensorFlow или оболочки вокруг них, такие как SavedModelBundle .
    • Другие виды моделей машинного обучения.
    • Таблицы поиска словарного запаса.
    • Встраивание справочных таблиц.

    Составная модель может быть представлена ​​как несколько независимых обслуживаемых объектов или как один составной обслуживаемый объект. Обслуживаемый объект также может соответствовать части Модели, например, с большой справочной таблицей, сегментированной по множеству экземпляров Manager .

Чтобы поместить все это в контекст этого урока:

  • Модели TensorFlow представлены одним видом обслуживаемых объектов — SavedModelBundle . SavedModelBundle внутренне состоит из tensorflow:Session в сочетании с некоторыми метаданными о том, какой граф загружается в сеанс и как его запустить для вывода.

  • Существует каталог файловой системы, содержащий поток экспорта TensorFlow, каждый в своем собственном подкаталоге, имя которого является номером версии. Внешний каталог можно рассматривать как сериализованное представление обслуживаемого потока для обслуживаемой модели TensorFlow. Каждый экспорт соответствует обслуживаемому объекту, который можно загрузить.

  • AspiredVersionsManager отслеживает поток экспорта и динамически управляет жизненным циклом всех обслуживаемых объектов SavedModelBundle .

TensorflowPredictImpl::Predict , тогда просто:

  • Запрашивает SavedModelBundle у менеджера (через ServerCore).
  • Использует generic signatures для сопоставления имен логических тензоров в PredictRequest с реальными именами тензоров и привязки значений к тензорам.
  • Выполняет вывод.

Протестируйте и запустите сервер

Скопируйте первую версию экспорта в отслеживаемую папку:

mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored

Затем запустите сервер:

docker run -p 8500:8500 \
  --mount type=bind,source=/tmp/monitored,target=/models/mnist \
  -t --entrypoint=tensorflow_model_server tensorflow/serving --enable_batching \
  --port=8500 --model_name=mnist --model_base_path=/models/mnist &

Сервер будет каждую секунду выдавать сообщения журнала с надписью «Стремящаяся версия для обслуживаемого...», что означает, что он нашел экспорт и отслеживает его дальнейшее существование.

Давайте запустим клиент с --concurrency=10 . Это отправит параллельные запросы на сервер и, таким образом, активирует вашу логику пакетной обработки.

tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

В результате получается результат, который выглядит так:

...
Inference error rate: 13.1%

Затем копируем вторую версию экспорта в отслеживаемую папку и повторно запускаем тест:

cp -r /tmp/mnist/2 /tmp/monitored
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

В результате получается результат, который выглядит так:

...
Inference error rate: 9.5%

Это подтверждает, что ваш сервер автоматически обнаруживает новую версию и использует ее для обслуживания!