[Перевод] Развёртывание XGBoost-моделей с помощью Ray Serve

XGBoost — это оптимизированная библиотека, реализующая алгоритм градиентного бустинга. Эта библиотека спроектирована с прицелом на высокую продуктивность и гибкость, в ней используется параллельная работа с древовидными структурами, что позволяет быстро и эффективно решать различные задачи из сфер Data Science и Machine Learning. В предыдущем материале мы исследовали три подхода к ускорению обучения XGBoost-моделей.

7950726512ff5b301833adcdeb4b302a.png

Библиотека XGBoost, после появления, быстро стала эталонным инструментом, который направлен на решение задач, предусматривающих обработку структурированных данных. Причина этого, в основном, кроется в высокой скорости её работы и в её исключительной производительности. Она быстрее, чем другие реализации ансамблей классификаторов, её базовый алгоритм поддаётся распараллеливанию, то есть — её можно использовать в средах со множеством CPU и GPU.

Публиковать XGBoost-модели можно в облачных средах. Среди них — Amazon SageMaker,  KubeFlow, Google Cloud AI Platform, Microsoft Azure ML SDK. Это — мощные платформы, поддерживаемые крупнейшими игроками рынка облачных услуг. Но плата за их использование может быть очень высока. Кроме того, они работают только в рамках собственных экосистем.

Самостоятельно довести модель машинного обучения от уровня концептуальной идеи до уровня продакшна — это, обычно, сложно и долго. Для облегчения решения этой задачи созданы несколько фреймворков. Они нацелены на развёртывание XGBoost-моделей в продакшне.

В этом материале мы расскажем о том, как развёртывать XGBoost-модели с помощью двух фреймворков — Flask и Ray Serve. Мы, кроме того, сравнивая характеристики работы моделей в продакшне, рассмотрим преимущества Ray Serve перед другими подобными решениями.

Развёртывание XGBoost-моделей с помощью Flask

Flask — это самый распространённый Python-микрофреймворк, используемый для развёртывания XGBoost-моделей. Дело в том, что он не имеет зависимостей от внешних библиотек. Flask считается исключительно удачным фреймворком для развёртывания XGBoost-моделей по нескольким причинам: его очень легко настраивать, он эффективен и поддерживает конечные точки REST. Кроме того, Flask, в отличие от XGBoost Server, не привязан ни к какому определённому фреймворку. Он поддерживает механизмы обработки HTTP-запросов. Flask, к тому же, ещё и бесплатен, что выгодно отличает его от SageMaker и других облачных решений. Выше перечислены лишь немногие возможности Flask, делающие этот фреймворк оптимальным решением для развёртывания XGBoost-моделей в продакшне.

В этом разделе мы обучим и протестируем XGBoost-модель, а потом развернём её с помощью Flask. Эта модель будет прогнозировать возникновение диабета с использованием набора данных pima-indians-diabetes с сайта UCI Machine Learning Repository. Этот небольшой набор данных содержит числовые медицинские показатели, связанные с диабетом, представленные восемью признаками. Тут имеется и одна целевая переменная, определяющая результат — Outcome. В результате мы воспользуемся XGBoost для моделирования и решения простой задачи прогнозирования.

Создание XGBoost-модели

Для начала, помимо данных, загрузим кое-какие зависимости. Потом запустим обучение:

from numpy import loadtxt
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# загрузка данных
dataset = loadtxt('pima-indians-diabetes.csv', delimiter=",")
# разбиение набора данных на X и Y
X = dataset[:,0:8]
Y = dataset[:,8]
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.33, random_state=7)

Теперь создадим XGBoost-модель и обучим её на имеющихся числовых данных:

model = XGBClassifier()
model.fit(X_train, y_train)

После того, как модель обучится, протестируем её с использованием тестового набора данных. Далее — вычислим показатели, используемые для оценки качества модели:

y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))

Развёртывание XGBoost-модели с помощью Flask

Этот этап нашей работы состоит из нескольких шагов, представленных в следующих подразделах.

Сериализация модели с помощью модуля pickle

После того, как модель обучена и протестирована, её можно сохранить с помощью модуля pickle, применяемого для сериализации моделей. Это позволит загрузить её тогда, когда она понадобится, и, передав ей входные данные, получить прогноз.

import pickle

# сохранение модели
with open('model.pkl','wb') as f:
    pickle.dump(model, f)

# загрузка модели
with open("model.pkl", "rb") as f:
    model = pickle.load(f)

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

Для развёртывания XGBoost-модели мы воспользуемся фреймворком Flask. Чтобы создать Flask-приложение, которое может прогнозировать возникновение диабета, нам нужен будет соответствующий маршрут, обращение к которому позволит получить прогноз модели.

import pickle
import numpy as np
from flask import Flask, request, jsonify, render_template

app = Flask(__name__)
with open("model.pk", "rb") as f:
    model = pickle.load(f)

@app.route("/predict", methods=["POST"])
def predict():
    data = request.get_json(force=True)
    prediction = model.predict([np.array(list(data.values()))])
    output = prediction[0]
    return jsonify(output)

if __name__ == "__main__":
    app.run(debug=True)

Обращение к API predict с помощью запроса

На этом шаге мы создаём файл request.py. Код этого файла выводит спрогнозированное значение, обращаясь к API, определённым в файле app.py.

import requests

url = "http://localhost:5000/predict"
r = requests.post(
    url,
    json={
        "Pregnancies": 6,
        "Glucose": 148,
        "BloodPressure": 72,
        "SkinThickness": 35,
        "Insulin": 0,
        "BMI": 33.6,
        "DiabetesPedigree": 0.625,
        "Age": 50,
        "Outcome": 1,
    },
)
print(r.json())

Развёртывание XGBoost-модели с помощью Ray Serve

Несмотря на то, что Flask — это отличный, полезный инструмент для развёртывания ML-моделей, у него есть и минусы. Например, он не подходит для больших моделей, ему не хватает возможностей, позволяющих организовать вход в систему и аутентификацию пользователей.

Но, помимо этих минусов, главным недостатком Flask в деле обслуживания ML-моделей являются те сложности, которые встают перед тем, кому нужно масштабировать Flask-приложение. При использовании Flask масштабирование каждого из компонентов означает необходимость запуска множества параллельных экземпляров приложения. При этом разработчик сам должен принять решение о том, как именно это сделать. Может — эта задача будет решена с помощью виртуальных машин, а может — с помощью обычных серверов или посредством кластера Kubernetes. В любом случае разработчик будет заниматься запуском экземпляров приложения и балансировкой нагрузки. А вот развёртывание XGBoost-моделей с помощью Ray Serve позволяет сразу решить все эти проблемы. Дело в том, что эта система даёт разработчику простой веб-сервер, который берёт на себя решение сложных задач маршрутизации, масштабирования, тестирования моделей. То есть — всего того, что нужно для развёртывания моделей в продакшне.

С помощью Ray Serve можно без труда масштабировать модель в кластере Ray, состоящем из нескольких узлов. Это так благодаря возможности динамического обновления работающих экземпляров приложения. Ray Server, кроме того, это система, не привязанная к какому-то конкретному фреймворку. Поэтому с её помощью можно обеспечивать работу моделей, подготовленных с помощью различных фреймворков. Например — таких, как TensorFlow,  PyTorch и Scikit-learn. В целом можно сказать, что Ray Serve — это система, которая позволяет организовывать высокоэффективную и высокопроизводительную работу моделей в продакшне.

Возьмём XGBoost-модель, которую мы создали выше, и развернём её с помощью Ray Serve.

Сначала установим Ray Serve:

pip install "ray[serve]"

Теперь запустим экземпляр Ray Serve, работающий поверх нескольких кластеров Ray.

ray start --head

Далее — запустим следующий Python-скрипт, в котором Ray Serve импортируется, инициализируется и запускается, а так же — подключается к локальному кластеру Ray:

import ray
from ray import serve
ray.init(address='auto', namespace="serve") # Подключение к локальному кластеру Ray.
serve.start(detached=True) # Запуск процессов Ray Serve в кластере Ray.

Обратите внимание на то, что тут метод serve.start применяется для запуска нескольких агентов Ray, которые используются Ray Serve для маршрутизации HTTP-запросов к подходящим моделям.

Кроме того, анализируя этот пример, учитывайте то, что мы запускаем Ray всего лишь в локальном окружении, делая это для тестирования кода. Но и это уже даёт нам преимущество перед Flask. Дело в том, что Ray задействует все доступные CPU-ядра компьютера, в то время как Flask-приложение, созданное ранее, использует только одно ядро. И это — лишь малая часть полезных возможностей Flask. Так, не сложнее, чем в локальном окружении, модель можно развернуть на Ray-кластере, содержащем десятки или даже сотни узлов. Это позволяет масштабировать вычислительные ресурсы, выделяемые модели, не внося совершенно никаких изменений в код приложения.

Теперь, когда система Ray Serve готова к работе, пришло время создать модель и развернуть её. Так как наша XGBoost-модель уже создана и обучена, нам нужно лишь загрузить её и представить в виде класса. После этого можно начинать процесс её развёртывания с помощью Ray Serve. Этим и займёмся:

import pickle
import json
import ray
from ray import serve

@serve.deployment(num_replicas=2, route_prefix="/regressor")
class XGB:
    def __init__(self):
        with open("model.pkl", "rb") as f:
            self.model = pickle.load(f)

    async def __call__(self, starlette_request):
        payload = await starlette_request.json()
        print("Worker: received starlette request with data", payload)

        input_vector = [
            payload["Pregnancies"],
            payload["Glucose"],
            payload["Blood Pressure"],
            payload["Skin Thickness"],
            payload["Insulin"],
            payload["BMI"],
            payload["DiabetesPedigree"],
            payload["Age"],
        ]
        prediction = self.model.predict([input_vector])[0]
        return {"result": prediction}

А теперь начинается настоящее волшебство. Следующие несколько строк кода развернут XGBoost-модель на работающем инстансе Ray Serve. Делается это посредством обращений к API Ray Serve в Python-фреймворке.

# Теперь инициализируем экземпляр Ray Serve или подключаемся к существующему экземпляру.
serve.start(detached=True)
# Развёртываем модель.
XGB.deploy()

Готово! XGBoost-модель успешно развёрнута в Ray Serve. Сделано это очень просто — путём вызова метода deploy ранее объявленного класса. На самом деле, тут имеются две копии модели, работающие в одно и то же время и обрабатывающие ответы. Нашу систему легко масштабировать. Для этого достаточно настроить параметр num_replicas.

Теперь мы можем обращаться к конечной точке развёрнутой модели, отправляя ей запросы. Обратите внимание на то, что HTTP-сервер, по умолчанию, работает на localhost:8000.

import requests

sample_request_input = {
    "Pregnancies": 6,
    "Glucose": 148,
    "BloodPressure": 72,
    "SkinThickness": 35,
    "Insulin": 0,
    "BMI": 33.6,
    "DiabetesPedigree": 0.625,
    "Age": 50,
}
response = requests.get("http://localhost:8000/regressor", json=sample_request_input)
print(response.text)
# Ответ:
#  "result": "1"

Как видно, если модель выдаёт результат 1 — это означает, что возникновение диабета «возможно» или «ожидается в скором времени». Если модель выдаёт 0 — это означает, что возникновение диабета не ожидается.

Итоги

Мы рассказали вам всё, что собирались! Теперь вы знаете о том, как развёртывать XGBoost-модели с использованием Flask, и о том, как без труда их масштабировать с помощью Ray Serve.

Для того чтобы больше узнать о Ray Serve — лучше всего начать с официальной документации. А именно — сначала можно освоить основы, а потом углубиться в детали обслуживания ML-моделей.

О, а приходите к нам работать?

© Habrahabr.ru