Разбираемся в задаче
ML-компоненты в системе помогают решать задачи, которые сложно формализовать. Дальше в докладе мы будем считать, что они выступают в роли автоматизированного способа строить эвристики, не усложняя продуктового кода.
Проблема в том, что с появлением в проекте ML-инженеров процессы в команде усложняются. Их майндсет отличается от майндсета владельца продукта и разработчиков.
В обычном сценарии разработчики сконцентрированы на какой-то задаче или проблеме. Чтобы её решить, они формируют гипотезу или план, пишут код, анализируют логи. Потом отдают код в релиз — и проблема решена.
Для ML-инженеров всё строится вокруг замеров. На основе замеров они формулируют гипотезы, проводят эксперименты и делают новый замер. Если метрики улучшились достаточно, чтобы внедрение ML-компонентов имело смысл, — случается релиз. Но дальше весь цикл замеров и экспериментов начинается заново. И этот цикл продолжается до тех пор, пока следующий этап внедрения ML-системы не окажется слишком дорогим.
ML-проекты очень близки по своей философии к исследовательским проектам. Они помогают тестировать разные гипотезы, чтобы найти перспективные подходы и быстрее решить проблему. Об этом важно помнить при поддержке рантайма.
Удобство инфраструктуры для ML-разработчика → время от гипотезы до посчитанных метрик.
Выгружаем данные
Внедрение ML-компонентов начинается с выгрузки данных, цель которой:
- Оценить профит от внедрения системы
- Получить первые артефакты для внедрения
На этой стадии требуется замороженный образ системы — датасет, который ML-разработчики экспортируют в Jupyter Notebook или другие системы, чтобы учить на нём модель и рассчитывать метрики. В общем, делать итерации: гипотеза → эксперимент → замер.
Для выгрузки данных понадобятся тестовые сессии или примеры, собранные по логам. Нужно выгрузить максимум данных и понимать, что они означают, чтобы ML-разработчики могли формулировать содержательные гипотезы. Желательно использовать какие-то понятные общепринятые форматы, например JSON.
Важно помнить, что даже при разовых задачах стадия выгрузки данных станет регулярной. Если в команде проекта есть ресурсы — есть смысл заняться автоматизацией этого процесса.
Внедряем ML-компоненты
Когда ML-разработчики провели эксперименты на датасетах и увидели улучшения метрик, можно переходить к стадии внедрения.
Оцениваем возможности для внедрения
В первую очередь нужно понять, какие данные подаются на вход модели:
- Реально ли рассчитать в рантайме
- Придётся ли существенно менять и утяжелять пайплайн данных
- Сколько capacity и latency это будет требовать
Затем разобраться, сколько это стоит:
- Насколько сложно реализовать предполагаемое внедрение ML-компонентов
- Есть ли пригодная библиотека применения
Стоит различать две ситуации: когда сложно внедрить и когда это совсем нереально. Если что-то можно внедрить в тесте, на маленький поток, сделать MVP — стоит всё-таки постараться это сделать. Важно найти возможность смотреть на текущий прогресс, потому что непонятно, как эксперименты на датасетах отразятся на настоящем продукте.
Определяемся, что внедряем
Чтобы собрать стек, который реализует эксперимент на датасетах в настоящей системе, понадобятся модели — большие файлы с весами. Важно решить, чем эти модели вычислять, какие данные подавать и как использовать результаты.
Когда всё готово, можно переходить к написанию кода.
Сводим диффы
Важная стадия, которую иногда пропускают, — проверить, что получилось. Спойлер: замеры 100% будут давать результаты хуже, чем при экспериментах на датасетах. Возможные причины:
- В модель подаются не те данные, например библиотеки разных версий
- Модель считается неправильно на одинаковых данных, результаты не совпадают с результатами ML-разработчиков
Свести диффы за конечное время может помочь правильная архитектура, когда рантайм разделён на стадии:
- Сбор данных
- Применение модели
- Применение результатов модели в продуктовой логике
Тогда можно будет дебажить по частям: сравнить рантайм с датасетами и найти расхождения. Например, если проблема во входных данных, нужно каждое поле отдельно раздебажить и пытаться свести. Если удалось разобраться, что в модель подали одинаковые данные, можно сравнить применение моделей.
Когда есть две детерминированные компоненты, стоит постараться сделать так, чтобы любая детерминированность была залогирована. Чтобы можно было проделать их одинаково в контуре датасетов и в контуре рантайма и найти, где расхождение.
Нужно позаботиться о системе логирования, потому что, чем лучше логи, тем проще дебажить.
Готовимся к изменениям в ТЗ
В ML-проектах можно заранее предсказать, какие изменения в ТЗ случатся наверняка. Нужно подготовиться к тому, чтобы рантайм было легко сконфигурировать под эти изменения. Желательно облегчить процесс выкатки новых весов:
- Решить проблему совместимости входов моделей
- Уметь переключаться между версиями в рантайме
- Автоматизировать доставку весов
Улучшаем ML-компоненты
Изменить схему сведения диффов
После запуска системы с ML-компонентами стоит подумать, как сделать, чтобы сведение диффов не отнимало слишком много сил. Можно перейти к схеме, когда сначала меняем рантайм, а потом уже обучаем ML-компоненты на актуальное состояние тех данных, которые доступны в рантайме.
В такой схеме есть небольшой минус: поля, которые не нужны в новой версии моделей, теперь не получится просто переименовывать по месту. Нужно добавлять новые с похожим именем, но немного другим алгоритмом. Это может давать существенные накладные расходы на обслуживание всего рантайма, но в среднем это выгоднее.
Решить проблему тестирования и приёмки
Когда в рантайме есть ML, отсутствие ошибок в мониторинге не означает, что система работает правильно. Иногда даже фикс очевидного бага может ухудшить продукт, просадить какие-то пользовательские метрики.
Важно понять причины, почему так происходит:
- Если подавать в модель «мусор», то есть не то, к чему она привыкла, — на выходе будут не те данные
- Если в модель подавали «мусор» на стадии обучения — теперь это правильный «мусор»
То, что логически написано в коде, может оказаться неправильным. Теперь правильно то, что модель видела на обучении.
Никакая система тестирования не позволит успеть за темпом, с которым система меняется. Поэтому перед релизом нужно знать актуальные запросы, которые приходят в модель.
Важно уметь определять, что релиз zerodiff, то есть не даёт изменений системы. Если хочется поменять поведение системы — это должен быть отдельный тип релиза, который нужно принять продуктовыми метриками.
Мониторим качество
Защититься от неконтролируемых изменений в системе помогает мониторинг качества. Вот как он реализован в Яндекс Поиске:
Мы исходили из гипотезы, что только сильные изменения текущего состояния системы сильно меняют метрики. Делаем релизы поочерёдно в разные зоны доступности, стеки которых независимы друг от друга. Если видим, что просели метрики в одном из дата-центров, можем отследить неожиданные изменения, откатить их и получить примеры для дебага.
Такая система позволяет убедиться, что даже если какие-то вещи случились без корректной приёмки или была какая-то ошибка — пользователи пострадают минимально.