Создание стандартного сервера модели 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%

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