Так уж исторически сложилось, что самым часто употребляемым инструментом для подготовки моделей машинного обучения, или, как их еще называют, предиктивных моделей, является Python, а точнее, ряд прикладных библиотек для него, которые берут на себя как задачи подготовки данных, так и непосредственное обучение моделей и их применение. И с точки зрения аналитиков, которые эти модели строят, это действительно хороший и удобный инструмент, обладающий достаточной гибкостью и невысоким порогом вхождения, ведь в работе Data Scientist’ов и без того немало что нужно знать и уметь. Если бы при этом еще требовалось держать в голове работу с памятью, многопоточность и прочие прелести «взрослых» языков, эти и без того редкие специалисты стали бы «вымирающим видом».
Все это приводит к тому, что существуют два не очень связанных мира. Один — это творческий подход и анархия при анализе, построении и проверке гипотез, обучении модели. и второй — строгий и упорядоченный мир продуктива, где важны скорость, потребляемые ресурсы и возможность масштабирования. И пропасть между этими мирами достаточно широка не только потому, что Python никогда не был языком для highLoad и параллельных вычислений, и даже не потому, что DataScientist’ы не могут подготовить быстро работающие модели, а потому, что пока нет устоявшихся и общепризнанных стандартов переноса такой сущности, как обученная модель, из среды разработки/анализа в продуктивную среду.
Для того чтобы было более понятно, о какой проблеме я веду речь, давайте подробнее остановимся на том, что же такое обученная модель. С точки зрения математики это всего лишь набор коэффициентов, описывающий некоторую функцию F(x), при подстановке в которую этих самых x мы получаем ответ в виде y. И тут вроде бы не может быть никаких проблем: уж что-что, а перенести простой массив чисел из одной среды в другую — дело несложное.
Все это приводит к тому, что существуют два не очень связанных мира. Один — это творческий подход и анархия при анализе, построении и проверке гипотез, обучении модели. и второй — строгий и упорядоченный мир продуктива, где важны скорость, потребляемые ресурсы и возможность масштабирования.
Но загвоздка заключается в том, что в большинстве моделей те x, которые мы должны отдать в функцию для вычисления, — это совсем не те данные, которые мы получаем на выходе из бизнес-систем. Перед вычислением мы должны обработать пропущенные данные, разобрать даты на составные части, привести числовые данные к единой шкале, провести нормализацию, убрать непечатаемые символы из текстовых данных и привести их в нормальную форму, превратить все это в вектор и провести еще много других преобразований. Все это усугубляется тем, что для различных моделей набор этих преобразований будет разным, поэтому мы не можем предусмотреть какой-то общий этап подготовки. Алгоритм таких преобразований также является неотъемлемой частью обученной модели, тут-то и возникает сложность с тем, каким образом эта совокупность может переехать из одной среды в другую.
Одно из очевидных решений — использовать одинаковые среды при анализе и на продуктиве.
Действительно, мы можем взять тот же самый Python, с тем же набором библиотек, описать с помощью него же преобразования данных, подсунуть массив коэффициентов, обернуть все это в REST-интерфейс с помощью того же Flask и, в принципе, достигнуть результата. Бурное развитие легковесных контейнеров виртуализации типа docker и инфраструктуры к ним еще больше упрощает эту работу. Фактически обученной моделью при таком подходе является весь контейнер, который содержит нужные версии библиотек, а это крайне важно, потому что отсутствие совместимости или разнящиеся результаты работы даже на уровне минорных версий библиотек в этой области далеко не редкость.
Несмотря на то что такую модель легковесной никак не назовешь, это вполне работающий вариант. Одним из его неоспоримых плюсов является чрезвычайная гибкость модели — по факту мы вольны написать какой угодно код на Python и использовать его в своих преобразованиях. На другой чаше весов, очевидно, необходимость этот самый код писать. И каждая такая модель будет вашим новым «велосипедом». Хотите масштабируемость и параллельные вычисления? Извольте написать соответствующий код… Нужна оптимизация по памяти и производительности? Добро пожаловать в мир Python-ниндзя! Самое главное, что у этого подхода есть фундаментальное ограничение — первоначально не самая быстрая среда исполнения. Если задержка предсказания в секунду или десять для вас не проблема, беспокоиться не о чем, но если у вас запасе меньше секунды, а обрабатывать надо тысячи операций в секунду?
Сама собой просится идея перенести модель из Python в другую среду, более подготовленную к суровым будням продуктива. И тут хотелось бы описать, какие собственно варианты у нас есть на текущий момент. Мы не зря упомянули текущий момент, потому что в данной еще довольно молодой области достаточно активно идут изменения и постоянно появляются новые инструменты, а старые уходят в небытие, рождаются новые идеи и подходы.
PMML
Пожалуй, одной из первых попыток создать универсальный формат переноса предиктивных моделей было появление стандарта PMML (Predictive Model Markup Language) в далеком 1997 г. Причем на ранних этапах речь шла даже не о каком-то реальном воплощении его в коде, а скорее о появлении спецификации как таковой. Впрочем, несмотря на глубоко проработанную консорциумом DMG (Data Mining Group) спецификацию и заявленный широкий круг компаний и продуктов, которые этот формат поддерживают, сейчас, на мой взгляд, наблюдается явная нехватка реализаций этой самой спецификации. По факту есть лишь одна библиотека JPMML, реализованная на Java, которая поддерживает основные модели и преобразования, заявленные в спецификации версии 4.3. Большинство других продуктов, как правило, имеют ограничения как в экспорте своих моделей в данный формат, так и в применении моделей PMML. И больше всего настораживает то, что практически все наиболее развитые инструменты PMML по факту поддерживает один разработчик из Эстонии — Villu Ruusmann. И хотя он продолжает активно развивать JPPML и сопутствующие конвертеры, только его усилий явно недостаточно.
Впрочем, за счет того, что спецификация составлена с учетом расширяемости с помощью определяемых пользователем функций, а также за счет того, что коды JPMML открыты, не составляет большого труда собрать свою доработанную версию библиотеки. Например, в своем продукте мы добавляли в модели возможность использования дополнительных моделей при предсказании пропущенных значений и расширяли список доступных трансформаций для различных типов данных.
Как же выглядит процесс переноса данной модели на примере использования JPMML? Для начала вы должны подготовить обученную модель в одном из инструментов, для которого существуют конвертеры. Сейчас это LightGBM, R, SkLearn, SparkML, TensorFlow, Xgboost — для них конвертеры написаны автором JPPML, а также большой ряд продуктов типа Weka, RapidMiner, IBM, SPSS и др., в которых есть встроенные возможности экспорта в формат PMML. Конечно, если мы говорим о стандарте, при подготовке модели должны учитывать возможности, которые он предоставляет. Как правило, это означает, что мы не можем использовать самые свежие методы и какие-то элегантные решения. Но практика использования машинного обучения показывает, что в реальном мире, в отличие от соревнований DataScientist’ов, разница в результате на несколько десятых и даже на 2–3%, которая может быть обеспечена новейшими методами и моделями, не влияет на ценность результатов работы модели. После подготовки модели с помощью экспорта или конвертера вы сохраняете обученную модель в pmml-файл в формате xml. Теперь все, что нужно сделать, — загрузить эту модель на сервер и выполнить ее с помощью Java-библиотеки JPMML. Причем для упрощения данного процесса есть замечательный инструмент Openscoring, который предоставляет REST-интерфейс над методами JPPML. По заявлениям компании AirBnB, после небольших доработок данный подход позволял им получать предсказания при анализе возможного мошенничества в течение 10 мс. наши измерения также подтверждают высокую производительность использования JPPML в связке с Openscoring, что дает возможность использовать это решение в тех случаях, когда время ответа критично — например, при выявлении мошенничества в операциях с картами.
PFA
Естественным дальнейшим развитием стандарта PMML стал формат PFA (Portable Format for Analytics). Собственно, это попытка преодолеть минусы PMML, к которым относится список методов преобразований данных и моделей, ограниченный стандартом. Для этого в спецификации, кроме стандартных методов, появились элементарные операции типа арифметических и алгебраических выражений или операций над строками, а также элементы управления типа условий и циклов: комбинируя их, можно описывать алгоритмы напрямую. Кроме того, переход с XML на JSON также положительно сказался на объеме конечного файла модели. При этом, к сожалению, главный недостаток PMML пока так и не преодолен: реализаций спецификации совсем немного, к ним можно отнести Hadrian (Java), Titus (Python), Aurelius (R). И все эти реализации поддерживаются OpenDataGroup, которая предоставляет продукты с их использованием на коммерческой основе, что в целом неплохо, так как позволяет им развивать и поддерживать функционал. С точки зрения применения модели, пожалуй, стоит отметить тот факт, что генерация моделей возможна только из Python и R, также существует конвертер из pmml, в остальных случаях вы должны будете подготовить модель в pfa-формате в ручном режиме.
Практика использования машинного обучения показывает, что в реальном мире, в отличие от соревнований DataScientist’ов, разница в результате на несколько десятых и даже на 2–3%, которая может быть обеспечена новейшими методами и моделями, не влияет на ценность результатов работы модели.
Mleap
Еще одна заметная попытка написать общий формат сериализации и выполнения моделей — это проект Mleap. Несмотря на то что проект достаточно молодой: первые коммиты на GitHub от 2016 г., — он уже обладает функционалом, необходимым для переноса моделей между средами. В отличие от pmml и pfa, создатели mleap не пытались описать универсальную спецификацию, а сконцентрировались на возможности переноса моделей из основных инструментов, которые сейчас используют аналитики: PySpark, Scikit-learn, Spark, Tensorflow. Это позволило гораздо более полно поддерживать те возможности, которые предоставляют данные инструменты. Кроме того, к плюсам можно отнести единый движок выполнения. Это означает, что проблем совместимости с моделями, экспортированными из разных инструментов, не возникнет. Также хочется отметить, что при написании движка авторы сделали большой упор на производительность, многопоточность и параллельное выполнение из коробки — по их заявлениям, было достигнуто время отклика менее 1 мс. При этом выполнение моделей можно запускать в таких средах, как Spark. Данный проект выглядит достаточно перспективно, особенно если в аналитике вы используете один из поддерживаемых инструментов и вам требуется экстраординарная скорость выполнения готовых моделей.
В заключение хотелось бы еще раз отметить, что данный обзор возможностей по переносу моделей из среды разработки в продуктив нельзя считать полным. Во-первых, потому что я сознательно не уделял внимания коммерческим продуктам, а во-вторых, как уже было упомянуто, потому что в этой сфере постоянно появляются и исчезают инструменты и решения, и, естественно, о каких-то новых возможностях можно просто не знать. Однако если обзор натолкнул вас на какие-то мысли и дал зацепки для ваших идей, то я с полной уверенностью могу сказать, что писал его не зря.