Hydrosphere — управляем ML как сервисом

c9533b654f8eba279e2f2bb72ca5b4f7.png

Подобно тому, как в мире разработки многокомпонентных систем применяются подходы для управления и мониторинга микросервисами на основе инструментов DevOps (для запуска и восстановления сервисов, передачи данных, наблюдения за распределенными операциями, выполнение замеров и получение операционных метрик), так же и для моделей машинного обучения становится важным обеспечить возможность их развертывания, обновления и наблюдения за метриками (точность модели, время выполнения прямого прохождения нейронной сети и другими). В обобщенном виде такие решения получили названия MLOps и в этой статье мы рассмотрим возможности платформы Hydrosphere 3.0.

Hydrosphere поддерживает разные фреймворки для описания нейронных сетей (в том числе, Tensorflow/Keras, PyTorch, fastai и другие) и обеспечивает полный цикл управления моделью, включая контроль обучения, версионирование, развертывание и наблюдение за операционными метриками, а также позволяет обнаруживать деградацию сети и потерю производительности и точности (из-за переобучения или некачественных данных, выбранных для обучения) на ранних этапах. Hydrosphere также предоставляет возможность интерпретации процесса вывода результата и визуализации работы модели машинного обучения для определения причины неправильного вывода. 

Hydrosphere использует для развертывания моделей Kubeflow (исполнение моделей машинного обучения в подах Kubernetes) или Apache AirFlow (инструмент для управления любыми конвейнерными операциями). Для управления Hydrosphere используется веб-интерфейс, а также API (REST или gRPC) для внешних приложений. Для развертывания моделей используются OCI-контейнеры с gRPC-интерфейсом, который оборачивает реальный код исполнения модели (на любом языке программирования) и позволяет обобщить способ передачи входного вектора в модель и получения результата исполнения модели. Модели могут версионироваться (каждая версия — отдельный образ контейнера) и объединяться в конвейеры обработки в концепции приложения (Application), при этом на каждой стадии конвейера может использоваться одна или несколько версий модели, выводы которых объединяются с учетом весов. Модель и конвейер описываются в виде yaml-файлов, которые описывают входы и выходы модели (тип и размерность массива или категоризированные значения, также поддерживаются типы для текстов, изображений, звука и видео), при развертывании модели сохраняются во внутреннем реестре. 

Hydrosphere выполняет функции маршрутизации внешних запросов (Gateway), управления запущенными моделями (Manager), получения операционных метрик (Sonar, также может отправлять метрики в Prometheus) и автоматического обнаружения выбросов (Auto OD) и создания отчетов о статистическом распределении в обучающем наборе и реальных данных (Drift Report). Также Hydrosphere содержит два компонента для объяснения выводов модели (Explanation) и визуализации выводов на многомерных массивах (Data Projection). 

Для установки Hydrosphere можно использовать готовый стек на Docker Compose или Kubernetes (через Helm) и утилиту командной строки (pip install hs), которая позволяет управлять приложениями и моделями, а также конфигурировать кластер Hydrosphere. В нашем примере будем использовать Docker Compose (про установку в Kubernetes можно почитать на этой странице):

export HYDROSPHERE_RELEASE=3.0.0
wget -O hydro-serving-${HYDROSPHERE_RELEASE}.tar.gz https://github.com/Hydrospheredata/hydro-serving/archive/${HYDROSPHERE_RELEASE}.tar.gz
tar -xvf hydro-serving-${HYDROSPHERE_RELEASE}.tar.gz
cd hydro-serving-${HYDROSPHERE_RELEASE}
docker-compose up -d

После запуска мы можем увидеть контейнеры для всех подсистем Hydrosphere (ui, visualization, gateway, auto-od, sonar, serving-manager), дополнительные контейнера для хранения данных (mongodb, postgres, minio), alertmanager для оповещения о деградации моделей. Веб-интерфейс публикуется на порт 80. Для внешнего доступа (через Python SDK) будет использовать gRPC-интерфейс на порте 9090. 

Для развертывания модели прежде всего необходимо сконфигурировать новый кластер:

pip install hs
hs cluster add --name=demo --server=http://localhost/
hs cluster use demo
git clone https://github.com/Hydrospheredata/hydro-serving-example.git
cd hydro-serving-example/examples/custom_metrics/census/models/model
hs apply -f serving.yaml

При выполнении apply будет выполнена сборка контейнера для поддержки модели и его развертывание в кластер. Рассмотрим более подробно структуру проекта с моделью ML и yaml-файл для описания развертывания:

kind: Model
name: "census"
payload:
  - "src/"
  - "requirements.txt"
  - "model.joblib"
  - "encoders.joblib"
runtime: "hydrosphere/serving-runtime-python-3.7:3.0.0-alpha.1"
install-command: "pip install -r requirements.txt"
training-data: "../../data/train.csv"
metadata:
  documentation: "https://docs.hydrosphere.io/quickstart/tutorials/train-and-deploy-census-income-classification-model"
contract:
  name: "predict"
  inputs:
    age:
      shape: scalar
      type: int64
      profile: numerical
    hours-per-week:
      shape: scalar
      type: int64
      profile: categorical
//...другие поля...
    native-country:
      shape: scalar
      type: string
      profile: categorical
  outputs:
    income:
      shape: scalar
      type: string
      profile: categorical

Модель определяет правила сборки контейнера — базовый образ (runtime), расположение тестовых данных в проекте, список дополнительных файлов для добавления в контейнер — payload, название модели, команда для конфигурирования собираемого контейнера (здесь используется установка зависимостей из requirements.txt, но может быть выполнен запуск сценария, но нужно при этом не забыть добавить его через payload). Взаимодействие с моделью описывается через contract, который содержит набор входов (inputs) и выходов (модели), каждый из которых определяется формой матрицы (может быть в формате массива размерностей или scalar для скалярных величин), типом значения (int64, float, string и другие, подробно список типов можно посмотреть здесь), профилем значения (numerical, categorical, image, text). 

На текущий момент доступны две среды исполнения — hydrosphere/serving-runtime-python-3.7 и hydrosphere/serving-runtime-python-3.8, но может использовать любой образ, который создает совместимый grpc-сервис.

Сам проект должен включать в себя необходимые для запуска модели зависимости (устанавливаются через install-command), описание конвейеров или обученные веса для нейронных сетей (также должны добавлять в payload), а также код с экспортируемой функцией predict или любую другую, название указывается в описании модели в contract.name (принимает входные значения как **kwargs) и возвращает dict с выходными значениями. При запуске код должен создать необходимые объекты для представления ML-модели, загрузить веса или описания конвейеров, выполнить все необходимые операции для дальнейшего использования модели. 

Для развертывания приложения используются другой тип yaml-файлов (kind: Application), в которых перечисляются стадии исполнения в pipeline, при этом внутри стадии может использоваться одна или несколько моделей (при указании модели дополнительно можно уточнять номер версии):

kind: Application
name: demo
pipeline:
- - model: "census:1"
    weight: 100

Для установки приложения также используется hs apply application.yaml. Также приложение может быть создано через веб-интерфейс. Перейдем на страницу http://localhost/applications/demo и попробуем проверить исполнение приложения (кнопка Test):

1faacf018b1532819771b8d5b093939d.png

Здесь необходимо заполнить категориальные данные корректными значениями, либо обратиться к кластеру программно и передать данные, загруженные из тестового набора:

from hydrosdk import Cluster, Application

cluster = Cluster("http://localhost", grpc_address="localhost:9090")

app = Application.find(cluster, "demo")
predictor = app.predictor()
# load data and call predictor.predict(data), where data is a dictionary

Попробуем создать простую модель на основе keras для детектирования цифр (обучена на наборе данных MNIST. Создадим новый пустой каталог и внутри него, в src/train.py сохраним сценарий для обучения нейронной сети на наборе данных MNIST. Результат обучения сохраним в файл .h5 (HDF5):

import numpy as np

from keras.datasets import mnist
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.utils import np_utils

nb_classes = 10

(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)

model = Sequential()
model.add(Dense(512, input_shape=(784,)))
model.add(Activation('relu')) 
                           
model.add(Dropout(0.2))  
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Dense(10))
model.add(Activation('softmax')) 
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.fit(X_train, Y_train,
          batch_size=128, epochs=4,
          validation_data=(X_test, Y_test))
model.save('trained.h5')

Запустим обучение:  

python3 src/train.py

Теперь создадим реализацию модели для использования в hydrosphere:

import tensorflow as tf
import numpy as np

model = tf.keras.models.load_model('trained.h5')

def predict(data):
  predictions = model.predict(data['image'])
  return {'result':np.argmax(predictions)}

# simulate digit "1"
digit_one = [1.0 if d%28==14 else 0.0 for d in range(0,28*28)]
data = {'image': np.array(digit_one, dtype=np.float32).reshape([1,-1])}
assert predict(data)['result']==1

Здесь в модели добавлен тест, который симулирует изображение цифры »1» (вертикальная черта) и проверяет вывод сети на корректность.

Для публикации модели необходимо создать файл с определением контракта и метаданных:

kind: Model
name: "digit"
payload:
  - "src/model.py"
  - "trained.h5"
runtime: "hydrosphere/serving-runtime-python-3.7:3.0.0-alpha.1"
install-command: "pip install tensorflow keras numpy"
contract:
  name: "predict"
  inputs:
    image:
      shape: [1,784]
      type: float32
      profile: numerical
  outputs:
    result:
      shape: scalar
      type: int32
      profile: categorical

И также подготовим развертывание приложения:

kind: Application
name: digits
pipeline:
- - model: digit:1

Установим модель и приложение в Hydrosphere:

hs apply -f model.yaml
hs apply -f application.yaml

И подключимся к нему из внешнего приложения:

from hydrosdk import Cluster, Application

cluster = Cluster("http://localhost", grpc_address="localhost:9090")

app = Application.find(cluster, "digits")
predictor = app.predictor()
digit_one = [1.0 if d%28==14 else 0.0 for d in range(0,28*28)]
print(predictor.predict(digit_one))

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

Таким образом, использование Hydrosphere позволяет интегрировать управление моделями и ML-приложениями в CI/CD-конвейер доставки других компонентов системы, а также обеспечивает механизм обнаружения и исполнения ML-приложений и мониторинга их функционирования.

В заключение приглашаю всех на бесплатный урок по теме: «MLFlow и переобучение ML-моделей». Зарегистрироваться на урок можно по ссылке ниже.

© Habrahabr.ru