Привет! Я Артем Караблинов, data scientist направления ранжирования и навигации в Lamoda Tech. В середине 2021 года мы запустили новый алгоритм ранжирования каталога, основанный на машинном обучении. С его помощью мы добились существенного улучшения продуктовых метрик за счет внедрения learning-to-rank подхода машинного обучения.  Это стало прочной основой перед этапом полностью персонализированного каталога, который мы выпустили в начале 2023 года.  В этой статье я расскажу, почему задача ранжирования каталога важна для бизнеса, как мы построили систему ранжирования каталога на основе ML и перешли на нее с эвристик.  Здесь не будет хардкор-подробностей про начинку алгоритмов, но для понимания статьи пригодятся базовые знания ML. Ранжирование уже пережило три этапа персонализаций: Эвристический этап Внедрение ML  ML-персонализация (персональное двухэтапное ранжирование) В статье я рассказываю про первые два этапа. Взаимодействие пользователя с товаром чаще всего начинается с каталога. Здесь можно выделить два сценария: пользователь может пойти через дерево категорий и выбрать то, что его интересует («Обувь» → «Кроссовки»), а может задать поисковый запрос «мужские кроссовки», и опять попадет на выдачу. Так или иначе, пользователь попадает в каталог, где у нас много товаров, которые нужно как-то отсортировать.  В каталог Lamoda заходит более 17 миллионов пользователей в месяц. При этом мы знаем, что 95% смотрят только первые две страницы каталога — это чуть больше ста товаров. А так как в популярных категориях, таких как платья или кроссовки, находятся тысячи товаров, важно показывать хорошие результаты наверху: если пользователь не увидит классную рубашку, то вряд ли он ее купит.  Но как понять, какие товары стоит показать в верхней части выдачи? Например, мы можем посмотреть, хорошо ли товар продавался в последнее время. Или можем посмотреть на цену товара, на размер его скидки, доступность размеров. Возникает вопрос: как совместить эти параметры в единую логику? Изначально наш подход обходился без машинного обучения. У нас было много данных о товарах, исходя из которых мы совмещали ключевые факторы «привлекательности» в единую формулу, чтобы в итоге рассчитать скоринг каждого из них. Первый фактор — конверсия. Это понимание того, насколько хорошо товар продавался в последнее время. Мы знаем, как часто пользователи кликали на тот или иной товар, добавляли его в корзину или в избранное, а также выкупали. Для расчета конверсии мы выбрали следующую формулу: Оценка конверсии по данным за последний месяц: CR = (cart_add + 10*purchases ) / impressions, cart_add — количество добавлений товара в корзину. purchases — количество покупок товара. impressions — количество показов товара в каталоге. Следующий фактор — доступность размеров. Товар мог очень хорошо продаваться последние две недели или месяц, но у него могли закончиться все популярные размеры. Исходя из этого, товар в любом случае потеряет популярность, пока не будет новой поставки на склад. Поэтому добавим в формулу учет доступности размеров.  И еще один фактор — цена. Если представить, что у товаров одинаковая конверсия и полная доступность размеров, то нам выгоднее всего ставить более дорогие товары наверх из-за бо́льшей выручки. Тем самым мы повышаем вероятность, что именно их купит пользователь.  В итоге все факторы объединяются в такую формулу скоринга:  Расчет скоринга с учетом доступности размеров и цены: score(sku) = CR * f2(availability) * f3(price) f2 и f3 — это некоторые монотонные функции, которые приводят доступность и цену к такому масштабу, при котором их можно осмысленно перемножать с конверсией. А CR — конверсия из формулы выше.  Теперь посмотрим, что будет, если отсортировать товары по этой формуле. Если мы посмотрим на все женские или на все мужские товары, то в топе будет много нижнего белья и носочков из-за высокой конверсионности этих товаров: их проще выбрать и легче решиться на покупку, а также эти группы товаров не подлежат возврату. Согласитесь, это не совсем то, что мы хотим видеть, когда заходим в категорию «Одежда». Но при этом, когда мы посмотрим на топ женских кроссовок, то увидим, что там формула работает нормально.  Созданный нами скоринг хорошо работает только в рамках отдельных категорий. При этом он не учитывает конкретный каталог, который просматривает пользователь. К тому же мы никак не можем повлиять на то, чтобы в каталоге похожие товары не отображались рядом: если у нас показаны носки, то не стоит ставить рядом еще одни. Также на этом уровне нам тяжело контролировать долю скидок и новинок в топе выдачи.  Поэтому дальше мы переходим к этапу разнообразия, где мы замешиваем товары из разных категорий и учитываем, является ли товар новинкой и есть ли на него скидки.  Начнем движение снизу вверх. Среди платьев у нас есть три группы: платья-новинки, платья со скидками и остальные платья. Внутри каждой группы товары отсортированы по убыванию скоринга. Мы поднимаемся наверх и вероятностно замешиваем товары из групп в соответствии с долей выручки этих групп за последний период времени.  Аналогично происходит для категорий одежды. В итоге получается, если платья по доле выручки за последние две недели собирают 20%, а брюки собирают 10%, значит, в верхней части выдачи одежды у нас будет 20% места под платья и 10% под брюки.  Таким образом у нас решаются проблемы сортировки по скорингу в общих категориях. Теперь мужские и женские товары выглядят адекватно как в каталогах верхнего, так и нижнего уровней.  Вроде бы все классно, но может ли одна сортировка хорошо работать для всех пользователей? Конечно нет. Мы хотим учитывать разные предпочтения пользователей из разных стран или с разных платформ.  Поэтому мы добавили деление пользователей по сегментам на основании данных о просматриваемом каталоге, геопозиции и платформе, внутри которых производим скоринг товаров. Благодаря такому сегментированию каталог становится более гибким и улучшается качество ранжирования. Например, в Питере уже холоднее, чем в Москве, и там чаще стали покупать верхнюю одежду. Таким образом, в Питере в топе выдачи будет больше верхней одежды по сравнению с платьями в Москве.  Мы знаем, как сортировать товары по формуле, как делать каталоги верхнего уровня более разнообразными и как делать это в разрезе разных сегментов. Осталось понять, как мы используем в онлайне отсортированные списки товаров.  После расчета скоринга и этапа разнообразия мы получаем отсортированные списки товаров в разрезе разных сегментов. Они загружаются в ElasticSearch и используются в онлайне для сортировки. Когда пользователь заходит в какой-либо каталог Lamoda, мы определяем его сегмент: мы уже знаем, какой это каталог, с какой платформы зашел пользователь и из какой страны. Получив значение сегмента, мы делаем запрос в поисковый движок ElasticSearch, из которого получаем отсортированный список товаров и возвращаем его пользователю.  Данная часть базового ранжирования в онлайне остается неизменной и по сей день, а вот какие изменения мы сделали для внедрения машинного обучения, расскажу далее. Итак, первый алгоритм был основан на эвристической формуле, составленной из трех частей: конверсионности товара, доступности размеров и цены. Этот способ рабочий, но он себя исчерпал. Оказалось, что нам очень сложно его улучшать. Помимо этого у этого эвристического подхода есть еще ряд недостатков: он не использует сложные свойства данных; не оптимизирует понятным образом бизнес-метрику; непредсказуемо себя ведет при любом небольшом изменении любой из компонент или при добавлении дополнительных факторов. Например, на картинке представлены две выдачи в одном из самых популярных каталогов. Они отличаются на миллионы NMV в месяц. Понять на глаз, какая из них лучше, невозможно.  То есть при эвристическом подходе мы пришли к тому состоянию ранжирования, когда осознанная работа в ручном, интуитивном режиме становится невозможна. Именно поэтому мы внедрили машинное обучение, которое лишено недостатков, описанных выше, и позволяет эффективно работать над улучшением качества ранжирования. Мы построили модель, который использует максимально полное описание свойств товара и предсказывает для него некоторое число, показывающее, насколько высоко товар должен находиться в выдаче для конкретного сегмента пользователей. Для внедрения ML нам нужны исторические данные, на основании которых будет рассчитываться признаковое описание товаров, а также алгоритмы машинного обучения. Все действия, совершенные на Lamoda, логируются со всех платформ и попадают в наш кликстрим. Среди этих логов есть данные о взаимодействии пользователя с каталожной выдачей. Там мы можем найти информацию о показах товаров в разных разделах каталога (impressions), о кликах и добавлениях товаров в избранное и корзину, а также о заказах. Все эти данные необходимы для обучения и применения модели.  Мы построили большой пайплайн с послойной обработкой этих данных, в процессе которого мы их очищаем, агрегируем и рассчитываем признаки товаров, а также делаем атрибуцию целевых событий к показам. На выходе получаем датасет, который идет на вход алгоритму машинного обучения. Как я уже сказал, обработка данных происходит послойно и состоит из нескольких этапов. Сами признаки мы рассчитываем для пар «товар — сегмент», где каждая строчка в датасете связана с показом товара в каталоге:  Изначально мы использовали простые счетчики событий в качестве признаков — то есть количество тех или иных действий, совершенных пользователями при взаимодействии с товаром. Но постепенно подход к построению признакового описания товаров эволюционировал и стал более комплексным. После ряда исследований мы решили изменить простые счетчики. Например, считаем средний размер скидки товара за последние 60 дней внутри определенной категории. Другой пример: CTR (отношение кликов к показам) за 60 дней внутри определенного сегмента, например, платформы или страны. При этом мы пришли к использованию CTR с экспоненциальным затуханием за некоторый период: кликам, которые были сделаны 60 дней назад, даем меньший вес, чем более свежим. Конечно, есть и более простые признаки: средний рейтинг товара, количество доступных единиц товара на стоке, количество дней, которое товар присутствует на Lamoda, и многое другое. Важная часть процесса построения модели — целевая переменная или таргет. Им может быть клик, добавление в избранное, оформление заказа или другой более комплексный вариант. Важно, чтобы целевая метрика отражала действие, которое мы хотим предсказать, и была связана с бизнес-метриками. В нашем случае в качестве целевой переменной мы выбрали добавление товара в корзину. Добавлений в корзину много и при этом они хорошо коррелируют с ключевыми бизнес-метриками, в рост которых мы целимся в A/B-тестах.  Итак, у нас уже есть данные и желание обучить модель для ранжирования с целевой переменной в виде добавления в корзину. Перед нами типичная learning-to-rank задача: нам нужно обучить модель правильно ранжировать товары друг относительно друга в рамках групп (user_id, dt, catalog). В качестве алгоритма мы используем градиентный бустинг для learning-to-rank задачи. Изначально мы использовали библиотеку XGBoost, но впоследствие перешли на использование CatBoost. Благодаря этому мы улучшили утилизацию ресурсов, снизили время обучения и смогли обучаться на большем количестве данных, а также повысили качество модели на валидации. Сэмплированные данные подаются в модель стратифицировано по 10 млн строк на каждую платформу — суммарно 40 млн строк. Обучаем модель на факт добавления товара в корзину и при каждом переобучении проверяем качество модели по офлайн-метрике NDCG@60. При подготовке данных, которые подаем в модель, мы сэмплируем изначальный train-датасет, оставляя сессии с количеством просмотренных товаров не менее 10 и не более 200.  Первый вариант формулы состоял из трех частей: конверсионности товара, доступности размеров и цены. При переходе на ML мы изменили первую компоненту этой формулы, заменив ее на предсказание модели.  В перспективе мы хотим заложить в модель возможность самостоятельно выделять из данных закономерности, которые отражены в формуле в оставшихся двух компонентах, и обучаться на денежный таргет. Так мы полностью уйдем от эвристики к оптимизации. Так выглядит итоговая схема всего пайплайна обучения. У нас есть отдельный этап обработки данных, на выходе из которого мы получаем датасеты для обучения и применения модели. Обучаем модель, применяем ее, используем предсказания в формуле и заливаем все в онлайновую часть Elasticsearch.  В рамках такого перехода была разработана большая система, состоящая из многих частей: от подготовки данных до обучения модели. Поэтому хочу напомнить, что иногда что-то идет не так даже на самом финальном этапе, и важно не забывать про тесты и мониторинг.  Например, мы обучили модель, сложили итоговую сортировку в Elasticsearch, но при ручном тестировании видим, что в топе преобладают дорогие товары из премиум-сегмента или снова просочилось много невозвратных товаров, таких как носки. Проблем может быть масса, поэтому важно настроить мониторинг на всех этапах пайплайна как для данных, так и для обучения модели, но эта тема тянет на еще одну статью. Скажу лишь, что лучше упреждать любые ошибки. Идеально, если вы можете понять, что в онлайне идет что-то не так, не только по обращениям в службу поддержки, но и по вашим мониторингам. Новый подход успешно показал себя в A/B-эксперименте в плане влияния на показатели бизнеса: мы получили значимый рост основных метрик, например, +1,1% к конверсии в покупку.  Кроме верхнеуровневых показателей и мы увидели консистентное улучшение и других поведенческих метрик: пользователи, которым показывалось новое ранжирование, чаще добавляли товары в корзину и избранное, а также чаще оформляли заказы. Переход на ML-подход в ранжировании каталога открывает большие возможности для дальнейшей работы над улучшением качества решения. Он позволяет эффективнее проверять гипотезы и выявлять сложные закономерности из данных, которые не способен выявить человек. Итак, чем хорош ML-подход в e-commerce: работает и приносит пользу для бизнеса; подходит для следующего шага в развитии после эвристик; открывает возможности для эффективной работы над качеством ранжирования, а также для будущего внедрения персонализации. В следующих статьях мы расскажем о том, как повысили уровень персонализации, сделав каталог, который подстраивается под персональные предпочтения — по брендам, цветам, размерам. Этот проект уже прошел тесты и доступен для всех пользователей.  Data Scientist