Привет, Хабр! Меня зовут Аня Южанина, я работаю ML-инженером в Купере (ex СберМаркет). Сегодня я расскажу о межретейлерном поиске. Это когда вы ищете какой-то товар и Купер показывает его в ассортименте разных ретейлеров. Зачем вообще нужен такой поиск и как внедрить умное ранжирование магазинов?

Виды поиска в Купере

У нас есть два вида поиска.

  • Первый — внутри магазина. Пользователь выбирает одного ретейлера (например, METRO) и собирает корзину там. Этот поиск полезен если, пользователь предпочитает заказывать из конкретного ретейлера.

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

Два вида поиска в интерфейса нашего приложения
Два вида поиска в интерфейса нашего приложения

Поиск среди всех магазинов работает так:

  1. Пользователь вводит запрос.

  2. Мы определяем все доступные магазины по геопозиции пользователя.

  3. В каждом из доступных магазинов осуществляем поиск товаров по запросу пользователя.

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

Допустим, я готовлю завтрак и вдруг понимаю, что забыла купить авокадо. Ввожу «Авокадо» в поиск Купера, и на экране появляется вот такая страница:

Сначала идут магазины, где есть авокадо (METRO, «Лента», «Глобус»), потом — выдача из выбранного магазина.

Но как приложение определяет, что именно для этого запроса сначала нужно показать именно METRO, а не «Ленту»? Каковы принципы и технология ранжирования? Все ответы даю дальше!

Давным-давно в далекой-далекой галактике…

…у Купера не было умного ранжирования.

Вне зависимости от соответствия предпочтениям пользователя на экран выводились товары ретейлера с наибольшей выручкой (при наличии совпадений). Чаще всего это был METRO.

Но в Купере представлены самые разные товары: это и продукты, и электроника, и украшения, и лекарства, и что еще только не!

В приложении больше 20 категорий
В приложении больше 20 категорий

Если пользователь хотел приобрести, скажем, новый смартфон и вводил в поиск «Apple», он мог в первую очередь наткнуться на бурбон из METRO:

Чем это было плохо?

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

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

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

Первый подход к ранжированному поиску

Когда мы впервые сели за задачу, то решили разделить процесс ранжирования на два этапа.

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

    Классификатор разработали на основе модели fastText. Она содержит обученные векторные представления слов, то есть смыслы, которые зашиты в форму, которую может понять компьютер.

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

  2. На втором этапе мы ранжировали рестораны относительно друг друга и магазины относительно друг друга на основе статических данных: сколько раз товар по данному поисковому запросу добавлялся из данного ресторана или магазина?

Когда мы провели A/B-тестирование, то поняли, что поиск с ранжированием воспринимается пользователями гораздо лучше. Это мотивировало нас продолжить работу и учесть при ранжировании дополнительные факторы.

Доработанная модель

Во втором подходе мы сохранили классификатор на основе fastText, но заменили ранжирование на основе статистических данных ранжированием на основе предсказаний модели XGBRanker.

Мы стали учитывать:

  • статистику по добавлениям в различных срезах: в целом по запросу, по запросу и региону, по запросу и конкретному пользователю;

  • стоимость и скорость доставки;

  • популярность ритейлера;

  • цены товаров в ритейлере.

Чтобы модель XGBRanker понимала, насколько релевантен для пользователя тот или иной ритейлер, ее обучали по следующим целевым значениям:

  1. Пользователь выбрал магазин, добавил из него товар и совершил покупку.

  2. Пользователь выбрал магазин, добавил товар, но потом удалил.

  3. Пользователь выбрал магазин, посмотрел выдачу или какой-то товар, но не совершил дальнейших действий;

  4. Магазин был в поисковой выдаче, но пользователь проигнорировал его.

Шансы, что пользователь быстро доберется до нужного товара, стали гораздо выше. Релевантность выдачи в межритейлерном поиске (NDCG) выросла на 5 п.п.

Улучшилась даже выдача для категорий, не связанных с продуктами питания. Например, по запросу «наушники» теперь первым делом показываются специализированные магазины, такие как «Технопарк», вместо продуктовых магазинов, где тоже могут продаваться недорогие наушники.

Влияние на бизнес

После второго подхода мы снова провели A/B-тестирование, и оно снова показало, что новое решение лучше :) Выросли все метрики конверсии и среднее количество товаров, добавленных из межритейлерного поиска. Статистически значимо увеличилась выручка — даже жаль, что я не могу поделиться конкретными цифрами! А еще снизилась доля пустых выдач и частота использования межритейлерного поиска. Последнее свидетельствует о том, что пользователи стали быстрее находить нужные товары и, следовательно, реже возвращаться к поиску.

Большой прирост по метрикам ранжирования магазинов и ресторанов дала двухэтапная модель, где на первом этапе используется классификатор, а на втором — модель XGBRanker.

Что еще мы попробовали + наши планы

Отдельно хочется упомянуть поиск по названиям магазинов и ресторанов.

Как я писала выше, в нашем приложении представлены сотни ресторанов. Раньше поиск выдавал нужный ресторан только в том случае, если запрос полностью совпадал с официальным наименованием: по запросу «Кулинарная лавка Братьев Караваевых» выдача была, а по неполному запросу «Братья Караваевы» — уже нет.

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

Однако эта функция пока остается в разработке, потому что результаты A/B-тестирования оказались неоднозначными.

Также мы собираемся расширить классификатор, добавив разделение не только на рестораны и магазины, но и на магазины разных видов (продукты, электроника, аптека и т. д.).

Буду рада, если моя статья окажется для коллег интересной и полезной. Как вы думаете, как еще можно улучшить пользовательский опыт в Купере? Поделитесь идеями в комментариях :)

Tech-команда Купера (ex СберМаркет) ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров.

Комментарии (2)


  1. dmpink
    02.08.2024 10:53
    +3

    Совершенствование технологий - это хорошо. С Купером что-то другое не так. Например, мне пришёл криво собранный заказ из ресторана. На всех других площадках я бы пошёл в чат и всё решил бы. У Купера чата нет. А так, да, ещё пара смен названий и всё будет хорошо.


    1. Hait
      02.08.2024 10:53

      Сходил я у Яндекса в чат. Отправили отписку, что рестораны не их и они передадут мою претензию в ресторан. На этом всё. Ни промокода, ни вычитание не того блюда