В этой статье я объясню, почему архитектуры, ориентированные на микросервисы, не годятся для машинного обучения. Затем я расскажу, как такие компании, как AirBnB и Uber, использовали для управления хранилище функций, такое как StreamSQL.

Хранилища функций позволяют декларативно определять определения функций машинного обучения и использовать их при обучении и обслуживании. Это позволяет командам совместно использовать, повторно использовать и обнаруживать эти функции в разных группах и моделях. Он также управляет версиями функций и мониторингом. Их конечная цель — позволить командам машинного обучения сосредоточиться на построении моделей, а не конвейеров данных.

Микросервисы стали де-факто архитектурным выбором для технологических компаний. Микросервисы позволяют небольшим командам в крупных компаниях создавать небольшие независимые компоненты. Команды могут решать проблемы и выполнять требования, не превращаясь в гигантский монолит. Это позволяет командам двигаться быстрее. Однако при чрезмерном использовании превращение маркера сеанса пользователя в профиль пользователя может вызвать двадцать сетевых вызовов.

Я недавно видел это видео в Твиттере, и оно вдохновило меня написать этот пост. Видео веселое и прекрасно отражает эту концепцию (соответствующий текст из видео ниже)

Менеджер по продукту: почему так сложно отобразить дату рождения на странице профиля?

Инженер: сначала мы должны вызвать службу Bingo, чтобы получить идентификатор пользователя, а затем вызвать Papaya, чтобы превратить его в маркер сеанса пользователя. Мы проверяем их с помощью LMNOP, затем мы можем получить информацию о пользователе из Racoon. Но у Racoon нет гарантии, что [дата рождения], поэтому далее мы звоним…

Пример: система рекомендаций Reddit

Микросервисы исключительно болезненны при построении моделей машинного обучения на сложных источниках данных, таких как поведение пользователей. В этих случаях, чтобы сделать прогноз, вам нужно получить данные из множества микрослужб, которые, в свою очередь, извлекают данные из множества других служб, чтобы получить всю необходимую им контекстную информацию. Например, если вы хотите создать персонализированную ленту Reddit, вы можете захотеть узнать обо всех сообществах, частью которых является пользователь, о самых популярных сообщениях в этих сообществах, о любых сообщениях, на которые они нажимали и которые им понравились, а также о других подробностях. Добавление дополнительных входных данных может предоставить модели больше сигнала. Например, некоторые пользователи могут вести себя в выходные совершенно иначе, чем в будние дни. Предоставление модели дополнительных входных данных — дня недели — позволяет ей улавливать эти тенденции.

Каждый логический вход в модель машинного обучения называется функцией. Процесс выдвижения гипотез, построения и тестирования входных данных для модели называется проектированием признаков. Как правило, разработка функций — одна из самых трудоемких задач для команд машинного обучения. Творческое придумывание новых функций — это только часть процесса; большая часть времени тратится на поиск нужных данных, изучение их особенностей и пограничных случаев, а также создание конвейеров данных для очистки и преобразования их в пригодную для использования форму.

В архитектуре на основе микросервисов единственный способ собрать определенные данные — через вызов API. Одна модель может иметь множество функций, для которых требуются вызовы API. Например, модели может потребоваться запросить пользовательский сервис для сообществ, частью которых является пользователь, затем ей нужно запросить самые популярные сообщения в каждом из этих сообществ, а затем запросить лайки, полученные каждым из них. Поскольку системы почти никогда не создаются для такого рода использования, это приводит к эффекту домино для сетевых вызовов в нисходящем направлении. В отличие от веб-интерфейса, модели машинного обучения не гибки в отношении отсутствующих функций. Ему придется дождаться завершения всех запросов, иначе он рискует получить бесполезные результаты. Сами модели машинного обучения довольно трудоемки в вычислительном отношении и очень медленны; смешайте это с микросервисами, и станет невозможно обслуживать рекомендации в реальном времени.

Получение тренировочных данных — отстой

Модели машинного обучения работают, имитируя то, что они наблюдают. Для этого модели требуется набор данных наблюдаемых результатов и входных данных в момент наблюдения для обучения. Создание обучающего набора в архитектуре на основе микросервисов — отстой. В нашем примере с Reddit нам нужен набор голосов пользователей вместе с набором функций в тот момент, когда они выполнили голосование. Хотя это далеко от идеала, технически мы можем получить отзывы пользователей и все данные сообщений путем очистки микросервисов пользователей и сообщений. Однако точность на определенный момент времени делает эту проблему невозможной во многих случаях. Для обучения нам нужно было бы знать, какой рейтинг у поста в сабреддите; однако микросервис subreddit вряд ли будет поддерживать ретроактивные запросы.

Один из вариантов — полностью отказаться от микросервисов, нарушив инкапсуляцию и считывая данные из дампов базы данных. Теперь мы могли обойти API-интерфейсы и подключить наборы данных непосредственно к Apache Spark или какой-либо другой системе пакетной обработки. Мы можем объединять таблицы и работать с данными гораздо удобнее. Это также позволяет генерировать обучающие наборы данных так быстро, как Spark может обработать данные. Хотя использование озера данных поначалу кажется разумным, оно приводит к тому, что службы машинного обучения зависят от схемы необработанных данных. Схема обязательно изменится со временем. Микросервисы удаляются и заменяются. Несоответствия данных, ошибки и проблемы накапливаются. В конце концов, каждая команда машинного обучения должна поддерживать конвейеры данных, которые превратились в архаичный беспорядок спагетти-кода, чтобы исправить ситуацию. Конвейеры уязвимы для изменений и требуют огромного количества времени и ресурсов.

Другой вариант — использовать платформу обработки событий, такую ​​как Apache Kafka, Apache Pulsar или Segment, чтобы позволить командам машинного обучения подписываться на нужные им потоки событий. Многие подводные камни озер данных применимы и к потоковой передаче событий. Однако, в отличие от дампов озера данных, потоки событий, как правило, содержат данные более высокого качества. Поскольку потоки событий часто обеспечивают работу критически важных сервисов, команды должны соблюдать более высокие стандарты в отношении качества данных и документации. И наоборот, озера данных предназначены исключительно для команд машинного обучения и аналитики и не соответствуют высоким стандартам.

Обработка потока событий страдает от проблемы холодного запуска. Платформы потоковой передачи событий редко настроены на длительное хранение событий, часто это всего лишь одна неделя. Если вы хотите сгенерировать новую функцию, вы можете застрять только с данными за последнюю неделю для создания обучающего набора. Проблема холодного запуска становится еще более очевидной при создании новой функции с отслеживанием состояния. Функция с отслеживанием состояния требует, чтобы вы объединяли события в течение некоторого окна времени. Например, количество сообщений, сделанных пользователем за последнюю неделю. В таких ситуациях могут потребоваться недели, чтобы даже начать генерировать набор обучающих данных.

Разработка признаков — это итеративный процесс. Вы выдвигаете гипотезу, строите эксперимент и проводите тест. Вы либо объединяете его с основной моделью, либо выбрасываете. Чем быстрее происходят итерации, тем быстрее модель становится лучше. Если на выполнение одного теста уходят недели, команды машинного обучения теряют способность выполнять свою работу. Команды в конечном итоге тратят время на прокладывание труб данных и играют в политику, чтобы получить доступ к данным, вместо того, чтобы строить лучшие модели.

Как команды решают проблему с микросервисами

Проблема микросервисов не нова и не уникальна. Интересно, что многие компании независимо друг от друга нашли одно и то же решение проблемы. AirBnB построил Zipline, Uber — Michelangelo, а Lyft — Dryft. Эти системы вместе называются Feature Stores.

Что такое магазин функций?

Хранилище функций предоставляет специалистам по данным стандартизированный способ определения функций. Хранилище функций обрабатывает генерацию обучающих данных и предоставляет онлайн-функции для обслуживания. Он абстрагирует разработку данных от рабочего процесса машинного обучения. Под капотом он координирует несколько систем больших данных для беспрепятственной обработки как входящих, так и прошлых событий. Если вас интересуют конкретные технологии, вот макет нашей инфраструктуры хранилища функций.

В исходной микросервисной архитектуре Reddit каждый сервис владел своими данными. Микросервис сообщений является источником достоверных данных о сообщениях, пользовательский микросервис — источником достоверных данных о пользователях и так далее. Хранилище функций пытается создать свои собственные представления этих данных в своих собственных внутренних структурах данных. Это достигается путем обработки потока событий предметной области в материализованное состояние. События домена — это логические события, например, когда пользователь голосует за статью или когда создается новый пост. Материализованное представление — это набор результатов выполнения запроса в потоке событий. Итак, если бы мы хотели, чтобы модель знала количество сообщений, за которые проголосовал пользователь, мы могли бы создать материализованное представление с помощью этой логики:

SELECT user, COUNT(DISTINCT item) FROM upvote_stream GROUP BY user;

Все материализованные представления существуют в одном хранилище функций и предварительно обработаны для использования в машинном обучении. Мы объединили все данные микросервисов, которые нам нужны, в монолитное хранилище данных. Это устраняет проблемы, связанные с получением функций в реальном времени из микросервисов. Теперь функции можно получить за один цикл. Дополнительным преимуществом хранилища функций является отказоустойчивость, поскольку материализованные представления хранятся в высокодоступном и, в конечном счете, согласованном хранилище данных. У нас есть собственная бизнес-логика при создании функций, поскольку мы сами обрабатываем необработанные события, поэтому мы слабо связаны с бизнес-логикой в ​​каждом микросервисе.

Event-Sourcing для генерации обучающих данных

Наши функции поддерживаются в соответствии с поступающими событиями. По сути, это зеркало таблиц микросервисов, но уже предварительно обработанное для использования в машинном обучении и все в одном месте. В отличие от микросервисов, хранилище функций сохраняет каждое входящее событие в журнале на неопределенный срок. Если бы мы воспроизвели каждое отдельное событие в журнале в пустое хранилище функций, мы бы получили точно такое же материализованное состояние. Журнал событий становится источником правды для системы. Этот шаблон проектирования называется Event Sourcing.

Event Sourcing позволяет нам создавать обучающий набор данных для наших моделей. Чтобы проиллюстрировать, как это делается, давайте возьмем пример Reddit, где мы хотим предсказать следующую публикацию, которую пользователь проголосует. Соответствующие события предметной области передаются в хранилище функций, которое затем обновляет входные функции модели. Наблюдаемые результаты также должны передаваться в хранилище функций, в этом случае каждый пользователь голосует.

feature_store.append_observation(userId, postId, now())

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

def generate_training_set():
    for observed in observations:
        feature_store.process_events_until(observed.time)
        features = feature_store.get_features(observed.userId)
        yield (features, observed.postId)

Рекомендации по дизайну

Магазин функций позволяет нам отделиться от микросервисной архитектуры и владеть нашими собственными функциями. Однако создание и обслуживание хранилища функций не является бесплатным. Команды должны учитывать следующие моменты перед развертыванием хранилища функций.

Общесистемная потоковая передача событий

Для хранилища функций требуется, чтобы события предметной области передавались через платформу потоковой передачи событий, такую ​​как Kafka или Pulsar. Это позволяет хранилищу функций материализовать свое состояние независимо от микросервисов. Сохранение журнала событий позволяет материализовать функции в любой момент времени.

Перевод крупной системы на основе микросервисов на использование потоков событий — это грандиозный сдвиг. Процедуры должны быть введены для захвата важных событий. Для этого может потребоваться обновление старых, критически важных микросервисов с новой зависимостью и новыми условиями ошибок для обнаружения. Другой вариант — использовать семантику Change-Data-Capture из каждой базы данных, чтобы преобразовать обновления в поток. Однако в этом случае хранилище функций становится уязвимым для изменений схемы внутри базы данных микросервиса.

Обработка изменений схемы событий

Хранилище функций по-прежнему зависит от схемы потоков событий. Если поток меняет свою схему или микросервис ведет себя неправильно и загружает ненужные данные, он может вывести из строя хранилище функций ниже по течению. К схемам потоков событий следует относиться с той же осторожностью, что и к схеме базы данных. Процедуры миграции должны быть четкими и проверенными. События должны быть записаны в расширяемом формате, таком как Protobuf или JSON.

Хранилище и вычислительная мощность

Обработка и хранение огромных объемов данных не бесплатны. Во многих случаях хранилище функций будет повторять вычисления, выполняемые отдельными микросервисами. В магазине функций стоимость и сложность инфраструктуры меняются на скорость разработки и простоту использования. Создание и обслуживание хранилища функций требует как денег, так и специализированных инженеров.

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

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

Проверьте StreamSQL.io

Магазин функций StreamSQL позволяет командам машинного обучения сосредоточиться на построении моделей, а не конвейеров данных. Команды машинного обучения могут создать одно определение функции для использования в обучении и обслуживании. StreamSQL — это основа для разработки, совместного использования и обнаружения функций между командами и моделями. Загляните здесь!

Первоначально опубликовано на https://streamsql.io.