MLOps и переносимость ML-моделей с помощью ONNX и Apache Spark

MLOPS Spark примеры курсы обучение, Spark MLLib, курсы Spark для дата-инженеров, обучение Apache Spark, Spark ML MLOps, обучение инженеров Machine Learning, Школа Больших Данных Учебный Центр Коммерсант

Обучая специалистов по Data Science, аналитиков и инженеров данных лучшим практикам MLOps, сегодня поговорим про переносимость моделей машинного обучения между разными этапами жизненного цикла ML-систем, от разработки до развертывания в production. А в качестве примера разберем, как использовать обученную ML-модель из Apache Spark за пределами кластера, упаковав ее в ONNX или контейнер с контекстом Spark.

Сложности MLOps: почему нельзя просто так взять и развернуть ML-систему

Развертывание ML-систем отличается от другого ПО и обученной модели как услуги. Для систем машинного обучения требуется многоступенчатый автоматизированный конвейер развертывания для переобучения, проверки и развертывания модели, что усложняет процесс. Дополнительно к типовым этапам тестирования ПО (модульное, интеграционное и пр.), тестирование системы Machine Learning еще включает валидацию и обучение модели. Наконец, производительность систем машинного обучения сильно зависит от качества данных, ML-модель необходимо часто переобучать и обновлять, что увеличивает количества итераций в конвейере.

Таким образом, главные трудности ML-проекта связаны не разработкой высокоточных алгоритмов машинного обучения, обобщением или интерпретацией результатов прогнозирования, а с запуском созданной системы в производство. Это связано с тем, что лишь малая часть реальной ML-системы состоит из кода моделей Machine Learning, а гораздо больше времени и усилий требуется на развертывание, переобучение, обслуживание, обновления и улучшения, эксперименты, аудит, управление версиями и мониторинг. Все эти шаги существуют на уровне всей ML-системы/платформы, а не на уровне кодирования алгоритмов. Поэтому очень важна стратегия развертывания модели. При определении этой стратегии необходимо ответить на следующие вопросы:

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

Ответить на эти и другие аналогичные вопросы помогает концепция MLOps для автоматизации и мониторинга масштабируемых ML-систем. Применяя принципы DevOps к разработке и развертыванию решений машинного обучения, MLOps включает оркестровку экспериментов, отслеживание показателей, реестр моделей, инструменты автоматизации рутинных операций и мониторинг производительности моделей, а также их переносимость между разными этапами жизненного цикла, от разработки до развертывания в production. Как MLOps решает это на практике, рассмотрим далее.

Переносимость моделей Machine Learning: популярные форматы

Например, Data Scientist пишет код в блокнотах типа Jupyter Notebook или Google Colab. При переносе этого кода в производственную среду его следует преобразовать в легковесный формат обмена, сжатый и сериализованный, который не зависит от языка разработки. Такими форматами являются следующие:

  • Pickle – бинарный вариант Python-объекта для сериализации и десериализации его структуры, т.е. преобразования иерархии объектов Python в поток байтов и наоборот. О достоинствах и недостатках формата Pickle читайте в нашей новой статье.
  • ONNX (Open Neural Network Exchange) — формат с открытым исходным кодом для ML-моделей, обеспечивающий общий набор операторов и универсальный формат файла для различных платформ и инструментов. ONNX-формат описывает граф вычислений (ввод, вывод и операции) и является автономным. Он ориентирован на глубокое обучение, поддерживается Microsoft и Facebook, отлично работает с TensorFlow и PyTorch.
  • PMML (Predictive Model Markup Language) — формат обмена предиктивными моделями на основе XML, позволяющий разработать модель в одной системе для одного приложения и развернуть ее в другой с помощью другого приложения, передав конфигурационный XML-файл.
  • PFA (Portable Format for Analytics) – стандарт для статистических моделей и механизмов преобразования данных, который отличается легкостью переносимости между различными системами и моделями. Функции предварительной и последующей обработки могут быть объединены в цепочку и встроены в сложные рабочие процессы. PFA может быть простым преобразованием необработанных данных или сложным набором параллельных моделей интеллектуального анализа данных с файлом конфигурации JSON или YAML.
  • NNEF (Neural Network Exchange Format) – формат, который облегчает процесс развертывания машинного обучения, позволяя использовать набор инструментов обучения нейросетей для приложений на различных устройствах и платформах.

Также есть форматы, специфичные для отдельных фреймворков, например, POJO/MOJO для AutoML-платформы H2O и Spark MLWritable для Apache Spark. Как именно работать с этими форматами, реализуя идеи MLOps, поговорим далее. А в нашей новой статье вы узнаете, как можно вместо этого использовать открытую ML-библиотеку сериализации MLeap. 

Упаковка моделей Machine Learning в ONNX и контейнер с контекстом Spark

Предположим, необходимо использовать обученную ML-модель из Apache Spark за пределами кластера. При этом есть несколько вариантов:

  • если модель обучена с помощью TensorFlow, PyTorch, Scikit-Learn и пр., можно упаковать ее модель в различные переносимые форматы, которые не зависят напрямую от Spark;
  • если же модель обучалась с помощью MLLib, например, в пространстве имен PySpark.ml.*, можно экспортировать ее в переносимый формат типа ONNX, а затем использовать среду выполнения ONNX для запуска модели. Однако, не все модели Spark MLLib поддерживают ONNX. Также можно сохранить модель и загрузить ее из контейнера, создав объект контекста Spark без кластера.
  • применение MLFlow для сохранения модели и ее упаковки с использованием спецификации MLModel. MLFlow поддерживает Apache Spark и может упаковывать модель с использованием спецификации MLModel, чтобы развернуть ее в любом месте. О том, что представляет собой спецификация ML-модели в MLFlow, читайте в нашей новой статье.

В Spark ML-модель машинного обучения представлена преобразователем (​​трансформером), который преобразует входной датафрейм с фичами в другой датафрейм с прогнозами. Трансформер также может выводить другой набор фичей. Поскольку в машинном обучении обычно нужно выполнить несколько шагов для получения желаемого прогноза, в Spark есть объект PipelineMode, который представляет собой последовательность шагов или трансформеров, объединяя их в конвейер преобразователей. Он объединяет несколько трансформеров вместе, чтобы задать рабочий процесс машинного обучения.

Рассмотрим пример с PipelineModel из 3 этапов:

  • токенизатор принимает текст и возвращает слова;
  • функция хеширования, которая принимает слова и возвращает векторы;
  • модель логистической регрессии, которая принимает фичи и возвращает результаты прогнозирования.

Получим PipelineModel, обучая Pipeline с помощью метода fit():

tokenizer = Tokenizer(inputCol="text", outputCol="words")
hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol="features")
lr = LogisticRegression(maxIter=10, regParam=0.01)
pipeline = Pipeline(stages=[tokenizer, hashingTF, lr])model = pipeline.fit(training)

Чтобы запустить этот объект PipelineModel вне Spark, можно экспортировать его в ONNX и запустить модель с использованием среды выполнения ONNX. Хотя ONNX изначально был разработан для моделей глубокого обучения, он поддерживает и обычный ML, как и Spark. Сюда входят алгоритмы индексирования строк, OneHotEncoding, Word2Vec, Scalers, Inputer, Binarizer, Bucketizer, линейные модели, случайный лес, градиентное усиление, наивный байесовский анализ, SVM, PCA.

NLP с Python

Код курса
PNLP
Ближайшая дата курса
29 июля, 2024
Продолжительность
40 ак.часов
Стоимость обучения
90 000 руб.

Чтобы экспортировать модель в формат ONNX, нужно сначала установить набор инструментов onnxmltools, доступный только для PySpark. Затем можно экспортировать модель конвейера в ONNX следующим образом:

from onnxmltools
import convert_sparkml
from onnxmltools.convert.sparkml.utils 
import buildInitialTypesSimpleinitial_types = buildInitialTypesSimple(test_df.drop("label"))
onnx_model = convert_sparkml(model, 'Pyspark model', initial_types, spark_session = spark)

Прокомментируем вышеприведенный участок кода:

  • Функция buildInitialTypesSimple создает список всех ожидаемых входных данных от модели (фичей). Она принимает образец DataFrame в качестве параметра. В примере это датафрейм test_df.
  • Model – имя установленного конвейера (PipelineModel), Pyspark model – описание модели.
  • initial_types — ожидаемые входные имена функций и типы, которые могут быть представлены с помощью функции buildInitialTypesSimple или созданы вручную, например [(‘education’, StringTensorType([1, 1]))]
  • spark_session=spark передает методу контекст SparkSession.

Сохраним эту модель конвейера в файл:

with open(os.path.join("/tmp/", "model.onnx"), "wb") as f:
f.write(onnx_model.SerializeToString())

Далее можно загрузить эту модель, используя среду выполнения ONNX в Python, например:

import onnxruntime
session = onnxruntime.InferenceSession(model_file_path, None)
output = session.get_outputs()[0]
inputs = session.get_inputs()input_data= {i.name: v for i, v in zip(inputs, input_sample.values.reshape(len(inputs),1,1).astype(np.float32))}}results = session.run([output.name], input_data)

input_data — это словарь с ключами в качестве имен функций и тензорами (1,1) в качестве значений. Функция reshape преобразует входные данные в массив Tensor с формой (feature_count,1,1), что и ожидается. Также важно приводить значения как float32. Наконец, можно отправить эту модель в Docker-контейнер с установленным Python и нужными библиотеками, например, из набора ONNXMLTools. Файл загрузит модель с помощью метода onnxruntime.InferenceSession(), а процедура скоринга вызовет метод session.run().

При этом можно столкнуться с некоторыми ограничениями конвертера Spark для ONNX, такими как отсутствие экспорта для Feature Hashing, TFIDF, RFormula, NGram, SQLTransformer и некоторых других моделей (кластеризация, FP, ALS и пр.). А также проблемы с поддержкой других языков программирования, помимо PySpark и отсутствием поддержки Tokenizer в спецификации ONNX.

Машинное обучение на Python

Код курса
PYML
Ближайшая дата курса
15 июля, 2024
Продолжительность
24 ак.часов
Стоимость обучения
54 000 руб.

Другой способ запустить PipelineModel внутри контейнера, самостоятельно реализовав идеи MLOps, — экспортировать модель и создать контекст Spark внутри контейнера, даже если кластер недоступен. В этом случае при сохранении PipelineModel, объект реализует интерфейс MLWritable, который предоставляет методы save(path) и write().overwrite().save(path). Этот метод сохраняет модель в иерархической структуре папок, где подробно описаны все шаги в конвейере. Вся структура необходима для загрузки и восстановления обученной модели. Для использования с MLFlow или Azure Machine Learning Services можно заархивировать этот каталог:

import shutilmodel.write().overwrite().save(model_path)
path_drv = shutil.make_archive(model_name, format='zip', base_dir=model_path)

Метод Shutil.make_archive создаст файл в узле драйвера в своей локальной файловой системе. Чтобы использовать его внутри контейнера, нужно установить Python-библиотеку PySpark в образ контейнера, т.к. модель Spark будет загружаться напрямую. Далее в скрипте ML-прогнозирования следует создать Spark-сессию, распаковать архив в папку и загрузить объект PipelineModel:

import pyspark
from pyspark.ml 
import PipelineModelspark = pyspark.sql.SparkSession
.builder.appName("pyspark_runtime").getOrCreate()model_unpacked = "./" + model_name
shutil.unpack_archive(model_path, model_unpacked)trainedModel = PipelineModel.load(model_unpacked)

Переменные spark и trainingModel должны быть доступны во всех подпрограммах, т.е. это глобальные переменные. Наконец, можно запустить ML-модель следующим образом:

input_df = spark.createDataFrame(pandas_df)
predictions = trainedModel.transform(input_df).collect()
preds = [x['prediction'] 
for x in predictioprint('[INFO] Results was ' + json.dumps(preds))

Читайте в нашей новой статье, как самостоятельно внедрить MLOps-подход без сложных сторонних решений, автоматизировав задачу пакетного прогнозирования и планирование ее запуска по расписанию с помощью Apache Spark. А практически освоить лучшие практики MLOps с Apache Spark и других инструментов аналитики больших данных, вам помогут специализированные курсы в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Источники

  1. https://medium.com/swlh/productionizing-machine-learning-models-bb7f018f8122
  2. https://towardsdatascience.com/how-to-containerize-models-trained-in-spark-f7ed9265f5c9
  3. https://pypi.org/project/onnxmltools/
  4. https://github.com/santiagxf/portable-sparkml/blob/master/score_pyspark.py
Поиск по сайту