Хабрахабр

Machine learning @ booking.com

Машинное обучение позволяет сделать сервис гораздо удобнее для пользователей. Начать внедрять рекомендации не так сложно, первые результаты можно получить, даже не имея налаженной инфраструктуры, главное начать. А уже потом строить масштабную систему. Именно так все начиналось в Booking.com. А во что это вылилось, какие сейчас используются подходы, как модели внедряются в продакшен, каких их мониторить, рассказал Виктор Билык на HighLoad++ Siberia. Возможные ошибки и проблемы не остались за бортом доклада, кому-то это поможет обойти мели, а кого-то натолкнет на новые идеи.

О спикере: Виктор Билык внедряет продукты машинного обучения в промышленную эксплуатацию в Booking.com.

Сначала давайте посмотрим, где Booking.com применяет машинное обучение, в  каких продуктах.

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

Это скриншот в моем аккаунте, и в двух из этих направлений в этом году я обязательно побываю.

Кроме того, есть модели, которые не так заметны, например, Fraud Detection. У нас обрабатываются моделями практически любые текстовые сообщения от клиентов, начиная с банальных спам-фильтров, заканчивая такими сложными продуктами, как Assistant и ChatToBook, где используются модели для определения намерений и распознавания сущностей.

Модели нам говорят, зачем люди едут, скажем, в Берлин.
Мы анализируем отзывы.

С помощью моделей машинного обучения анализируется, за что хвалят отель, чтобы вам не приходилось читать тысячи отзывов самим.

Например, здесь мы пытаемся предсказать, когда отель будет распродан.
В некоторых местах нашего интерфейса практически каждый кусочек завязан на предсказаниях каких-то моделей.

Часто оказываемся правы — через 19 часов последняя комната уже забронирована.

Здесь мы пытаемся формализовать субъективное: что вообще такое выгодное предложение. Или, например, — бейдж «Выгодное предложение». Ведь это, кроме цены, зависит от многих факторов, таких как дополнительные услуги, а зачастую вообще от внешних причин, если, например, в этом городе сейчас проходит чемпионат мира по футболу или большая техническая конференция. Как понять, что цены, выставленные отелем на эти даты, хороши?

Начало внедрения

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

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

С Perl есть вполне очевидная проблема — он не создан для интенсивных вычислений, и наш бэкенд уже нагружен другими вещами. Чаще всего эти кусочки продукта были построены на Perl-стеке. Поэтому Product Owner (PO) был бы весьма против этого. При этом разработку серьезных систем, которые решали бы эту проблему, приоритезировать внутри команды бы не удалось, потому что фокус команды на решении пользовательской проблемы, а не на решении пользовательской проблемы с помощью машинного обучения.

Давайте разберемся, как это тогда происходило.

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

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

  1. страна, где находится посетитель;
  2. город, в котором он ищет себе отель.

Нам нужно предсказать вероятность какого-то события. Мы просто взрываем все входные векторы: скажем, 100 000 городов, 200 стран — итого 20 миллионов строчек в MySQL. Звучит, как вполне работоспособный вариант для вывода в продакшен каких-то небольших систем ранжирования или других простеньких моделей.

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

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

Сервис предсказаний

Первое, что мы сделали — сервис предсказаний. Вероятно, эниже самая простая архитектура, когда-либо показанная на Хабре и HighLoad++.

На самом деле, я немножко лукавлю — система была чуть-чуть сложнее, потому что нам нужно было как-то это мониторить и выкатывать. Мы написали небольшое приложение на Scala+Akka+Spray, которое просто принимало входящие векторы и отдавало предсказание обратно. В реальности это все выглядело так:

Туда очень просто писать, и этот поток очень просто перенаправлять. В Booking.com есть система событий — что-то вроде журнала для всех систем. На первых порах нам нужно было мы отправляли в Graphite и Grafana клиентскую телеметрию с perceived latencies и подробную информацию с серверной стороны.

Продать такой продукт было достаточно просто, потому что мы получили возможность внедрять более сложные модели и тратить при этом гораздо меньше времени.
Мы сделали простые клиентские библиотеки для Perl — спрятали весь RPC в локальный вызов, поместили туда несколько моделей и сервис начал взлетать.

Дейтасайентисты начали работать с гораздо меньшими ограничениями, а работа бэкендеров в некоторых случаях сводилась к однострочнику.

Предсказания в продукте

Но давайте ненадолго вернемся к тому, как мы пользовались этими предсказаниями в продукте.

Базируясь на этом предсказании, мы как-то меняем пользовательский интерфейс. Есть модель, которая на основе известных фактов делает предсказание. Это, конечно, не единственный сценарий использования машинного обучения у нас в компании, но достаточно распространенный.

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

Если он не взлетает — нет никакого статистически значимого изменения целевых метрик — неизвестно, в чем проблема: непонятный, маленький, незаметный бейдж или плохая модель. Представьте, запускаем бейдж «Выгодное предложение» в рамках AB-эксперимента.

То, что работало вчера, необязательно работает сегодня. К тому же модели могут деградировать, и причин для этого может быть очень много. Нам нужно как-то понимать, что модель все еще хорошо обобщает и в этих кусочках входящего пространства.
К тому же мы постоянно находимся в режиме cold-старта, постоянно подключаем новые города и отели, люди из новых городов приходят к нам.

Скорее всего, в результате переобучения она начала понимать случайные шумы, как просьбу посмеяться, и начинала заходиться хохотом по ночам, пугая владельцев. Самым, наверное, известным в последнее время случаем деградации модели была история с Алексой.

Мониторинг предсказаний

Для того чтобы мониторить предсказания, мы немного доработали нашу систему (схема ниже). Точно также из event-системы перенаправили поток в Hadoop и начали сохранять, помимо всего, что мы сохраняли раньше, еще и все входные векторы, и все предсказания, которые сделала наша система. Потом с помощью Oozie мы их агрегировали в MySQL и оттуда показывали небольшим веб-приложением тем, кто заинтересован в каких-то качественных характеристиках моделей.

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

Мы хотим предсказать, едет ли пользователь в отпуск один или с семьей. Рассмотрим это на примере. Только уже съездив в отпуск, пользователь получит приглашение оставить отзыв, где среди прочего будет вопрос, был ли он там один или с семьей. Это предсказание нам нужно, когда человек выбирает отель, но правду мы сможем узнать только через год.

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

Этим «попроще» оказалась просто гистограмма предсказаний, сделанных моделью.

Видно, что она неплохо разделяет пользователей на два класса: слева холм — это те, кто не сделают этого; справа холм — те, кто это сделает.
Выше на графике логистическая регрессия, которая предсказывает, поменяет пользователь дату своего путешествия или нет.

Хорошо видно, что на этой неделе (это недельный график) модель предсказывает смену дат немножко чаще. На самом деле мы показываем даже два графика: один за текущий период, а другой за предыдущий. Трудно точно сказать, сезонность это, или та самая деградация со временем.

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

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

Интересно посмотреть, как графики меняются в различных разрезах.

Хорошо видно, что на планшетах модель предсказывает более вероятную смену дат. Слева — вероятность смены дат на десктопе, справа — на планшетах. Это, скорее всего, связано с тем, что планшет часто используют для планирования путешествия и реже для бронирования.

Еще интересно смотреть, как эти графики меняются по мере движения пользователей по воронке продаж.

Видно, что до странички бронирования добирается гораздо большее количество людей, которые уже определились со своими датами. Слева вероятность смены дат на страничке поиска, справа — на первой страничке бронирования.

Как выглядят плохие? Но это были хорошие графики. Иногда это просто шум, иногда огромный холм, что означает, что модель не может эффективно разделить какие-то два класса предсказаний.
Очень по-разному.

Иногда это огромные пики.

Это тоже логистическая регрессия, и до какого-то момента она показывала красивую картинку с двумя холмами, но одним утром она стала такой.

Для того, чтобы понять, что произошло внутри, нужно понимать, как вычисляется логистическая регрессия.

Краткая справка

Одной из этих фич была цена ночи в отеле (в евро).
Это логистическая функция от скалярного произведения, где xn — это какие-то фичи.

Вызывать эту модель стоило бы как-нибудь так:

Нужно было сконвертировать цену в евро, но разработчик забыл это сделать.
Обратите внимание на выделение.

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

Пороговые значения

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

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

Предположим, мы хотим запустить эксперимент, который в случае, если модель говорит «да», как-то меняет пользовательский интерфейс. Однако если начать двигать эту точку, можно добиться весьма интересных эффектов. Ведь количество людей, которые получили это предсказание, — это площадь под кривой. Если пододвинуть эту точку вправо, сокращается аудитория нашего эксперимента. Точно также если не хватает статмощности, можно увеличить аудиторию своего эксперимента, но понизив точность предсказаний. Однако на практике точность предсказаний (precision) гораздо выше.

Кроме самих предсказаний мы начали мониторить входящие значения в векторах.

One Hot Encoding

Большинство фич в наших самых простых моделях категориальные. Это значит, что это не числа, а некие категории: город, из которого пользователь, или город, в котором он ищет отель. Мы пользуемся One Hot Encoding и превращаем каждое из возможных значений в единицу в бинарном векторе. Поскольку вначале мы пользовались только нашим собственным вычислительным ядром, было легко определить ситуации, когда для входящей категории нет места во входящем векторе, то есть модель не видела этих данных во время обучения.

Так это обычно выглядит.

Вполне естественно, что модель не видела примерно 5% значений, так как мы постоянно подключаем новые города. destination_id — город, в котором пользователь ищет отель. visitor_cty_id =23,32%, потому что дейтасайентисты иногда сознательно опускают малораспространенные города.

В плохом случае это может выглядеть так:

Чаще всего это возникает из-за использования форматов, отличных от тех, что использовались при обучении, или просто банальных опечаток. Сразу 3 свойства, 100% значений которых модель никогда не видела.

Сейчас с помощью дэшбордов мы обнаруживаем и исправляем такие ситуации очень быстро.

Витрина машинного обучения

Давайте поговорим о других проблемах, которые мы решили. После того, как мы сделали клиентские библиотеки и мониторинг, сервис начал очень быстро набирать обороты. Нас буквально завалило заявками из разных частей компании: «Давайте подключим еще эту модель! Давайте обновим старую!» Мы просто зашивались, фактически любая новая разработка остановилась.

Теперь можно просто зайти на наш портал, тот самый, который мы использовали сначала только для мониторинга, и буквально нажав кнопку загрузить модель в продакшен. Мы вышли из ситуации, сделав киоск самообслуживания для дейтасайентистов. Через несколько минут она будет работать и давать предсказания.

Оставалась еще одна проблема.

Как дать знать команде в какой-то совершенно другой части компании, что есть модель, которая могла бы им помочь? Booking.com — это примерно 200 IT-команд. Как узнать, какие вообще есть модели и как ими пользоваться? Вы можете просто не знать, что такая команда даже существует. Это не значит, что у нас нет никаких других горизонтальных связей, просто PO занимается этим больше других. Традиционно внешними коммуникациями у нас в командах занимаются PO (Product Owner). Нужно что-то с этим делать. Но очевидно, что в таких масштабах коммуникация «один на один» не масштабируется.

Как можно облегчить коммуникацию?

Мы вдруг поняли, что тот портал, который мы делали исключительно для мониторинга, постепенно начинает превращаться в витрину машинного обучения у нас внутри компании.

Когда моделей стало много, мы добавили метки тем и областей применимости для удобной группировки.
Мы дали возможность дейтасайентистам подробно описывать свои модели.

Это продукт внутри нашей компании, который обеспечивает проведение A/B-экспериментов и хранит в себе всю историю экспериментирования.
Мы связали наш инструмент с ExperimentTool.

Это изменило всё. Теперь вместе с описанием модели можно еще и посмотреть, что делали другие команды с этой моделью раньше и насколько успешно.

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

Когда они придумывают какие-то новые продуктовые идеи, они просто подбирают подходящие им модели и пользуются ими. Например, многие команды используют это во время мозговых штурмов. Для этого не нужно ничего сложного.

Во что это вылилось для нас? Прямо сейчас в пике мы доставляем около 200 тысяч предсказаний в секунду, при этом с latency меньше 20-30 мс, причем включая HTTP round trip, и размещение больше 200 моделей.

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

Были и ошибки. Такого, конечно, не бывает. Мы почему-то предполагали, что большинство наших моделей будут рекомендательные системы с тяжелыми входными векторами, и стек Scala+Akka был выбран именно потому, что с его помощью очень легко организовать параллельные вычисления. В самом начале, например, мы заложили небольшую бомбу замедленного действия. В какой-то момент мы наши 100 машин обрабатывали всего 100 000 RPS, и отказы случались с вполне характерными симптомами: утилизация CPU низкая, но получаются таймауты.
Но в реальности накладные расходы на всю эту параллелизацию, на сбор вместе оказался выше, чем возможный выигрыш.

Конечно, мы так не делаем, потому что у нас несколько дата-центров, нам нужна избыточность вычислений и всё остальное, но, тем не менее, теоретически мы можем обслуживать больше 100 000 RPS всего 4 машинами. Тогда мы вернулись к нашему вычислительному ядру, пересмотрели, сделали бейнчмарки и в результате capacity-тестирования узнали, что для того же самого трафика нам нужно всего 4 машины.

В какой-то момент у нас собралось небольшое количество моделей, которые применялись буквально по всей воронке, начиная со странички индекса, заканчивая подтверждением о бронировании. Мы постоянно ищем какие-то новые мониторы, которые могут нам помочь найти и исправить ошибки, но не всегда делаем шаги в правильном направлении.

Посчитали дисперсию, сгруппировав все по ID пользователя, никаких серьезных проблем не обнаружилось. Мы решили — давайте посмотрим на то, как модели меняют свои предсказания для одного и того же пользователя. Предсказания моделей были стабильны, дисперсия в районе 0.

Другая ошибка — опять же и техническая, и организационная — мы начали упираться в память.

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

Нагрузка и объемы модели распределялись примерно равномерно — 49-51%. Когда мы начинали делать шардинг, мы посмотрели на живые данные и шардить собирались очень просто — по ID модели. У нас были горячие модели, которые использовались гораздо больше других, и дисбаланс был большой. Но когда мы закончили заниматься шардингом, батч уже использовался в продакшене. Окончательно мы решим эту проблему, когда наконец уедем в контейнеры.

Планы на будущее

  • Label based metrics

В первую очередь мы все-таки хотим дать дейтасайентистам возможность наблюдать в динамике те же самые метрики, которые они используют при обучении. Мы хотим Label based metrics, и наблюдать precision и recall в реальном времени.

  • More tools & integrations

В компании еще остались внутренние инструменты и продукты, с которыми мы плохо интегрированы. В основном это высоконагруженные проекты, потому что для всего остального мы сделали пару клиентских библиотек для Perl и Java, и все, кому нужно, могут этим пользоваться. У аналитиков есть удобная интеграция со Spark, они могут для своих целей использовать наши модели.

  • Reusable training pipelines

Мы хотим иметь возможность вместе с моделями деплоить кастомный код.

Все процедуры, которые происходят до получения входящего вектора весов, например, разбиение текста на предложения, на слова, steaming — вы должны в продакшен-окружении повторить еще раз, желательно тем же самым образом для того, чтобы избежать ошибок. Например, представьте себе спам-классификатор.

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

  • Async models

Мы хотим сделать асинхронное предсказание. Сложность наших моделей растет, а все, что медленнее 50 мс, мы считаем очень медленным. Представьте себе модель, которая делает предсказания исключительно на основании истории посещённых страниц на нашем сайте. Тогда мы можем запускать эти предсказания в момент рендера странички, а забирать и использовать их тогда, когда нам надо.

Start small

Самое важное, что я узнал, пока работал над внедрением моделей в продакшен в Booking.com, и что я хочу, чтобы вы запомнили, унесли домой и пользовались — начинайте с малого!

Мы своих первых успехов с машинным обучением достигли, смешно сказать, взрывая предсказания в MySQL. Возможно, у вас тоже есть первые шаги, которые вы можете сделать уже сейчас. Вам не нужны какие-то сложные инструменты для этого. B у меня тот же совет дейтасайентистам. Если вы не работаете с видео, с голосом и с изображением, если ваша задача как-то связана с транзакционными данными — не берите слишком сложные модели сразу.

Зачем вам нейронная сеть, пока вы не попробовали логистическую регрессию?

Monitor

Мониторьте все на свете — вы же мониторите свое программное обеспечение, веб-серверы, железо. Модель — это такое же программное обеспечение. Единственная разница — это программное обеспечение, которое написали не вы. Его написала другая программа, которую, в свою очередь, написал дейтасайентист. В остальном всё то же самое: входные аргументы, возвращаемые значения. Знайте, что у вас происходит в реальности, насколько модель хорошо справляется со своей работой, все ли идет штатным путем — мониторьте!

Organization footprint

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

(Don’t) Follow our steps

Я поделился какими-то успехами, неудачами, проблемами, с которыми мы столкнулись. Надеюсь, кому-то это поможет обойти те мели, на которые мы сели. Делайте как мы, но не повторяйте наших ошибок. Или повторяйте — кто в конце концов сказал, что ваша ситуация точно такая же, как наша? Кто сказал, что то, что не заработало у нас, не заработает у вас?

Пробуйте, ошибайтесь, делитесь своими ошибками!

Дополнительно в расписании 9 треков мастер-классов и митапов. На HighLoad++ 2018, который состоится 8 и 9 ноября в СКОЛКОВО, будет 135 спикеров, уже готовых поделиться результатами своих экспериментов. Темы найдутся для каждого, и билеты еще можно успеть забронировать.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть