Понимаем с полуслова: как работает поиск товаров в СберМаркете

16ab18fa340712fbd0510b7f8ba279f4.jpg

Всем привет! Меня зовут Аня Власова. Я работаю ML-инженером в команде Поиска СберМаркета. В этой статье я расскажу, как устроены наши процессы: с момента, когда пользователь вводит запрос, до получения поисковой выдачи. Если вы разрабатываете поиск или просто интересуетесь темой, то наверняка сможете найти интересные инсайты для своей работы.

Коротко о том, что вас ждет:

  • Зоны ответственности команды Поиска;

  • Как мы отбираем кандидатов для отображения их в поисковой выдаче;

  • Финальное ранжирование товаров ml моделью.

7 из 10 товаров в СберМаркете добавляются в корзину именно из Поиска, так что даже маленькие изменения в наших продуктах оказывают большой и видимый эффект на бизнес. Именно поэтому мы уделяем много внимания постоянному улучшению наших решений и уже добились хороших результатов. Надеюсь, что вы почерпнете что-то новое из нашего кейса и сможете применить это в своей работе. Поехали!

Перед отправкой запроса

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

После того, как пользователь начнет вводить запрос, перед ним появятся подсказки с релевантными категориями и предложениями — ими тоже занимается наша команда.

Итак, запрос введен. Например, покупатель ищет «перец» и получает такую выдачу.

83cf3bccffabd11fcfda47366abfda85.png

Что произошло между этими двумя событиями? Скоро узнаем, но обо всем по порядку.

Отбор кандидатов

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

Индексация

Вся информация о товарах находятся в индексе —  хранилище документов (товаров) в поисковом движке. Индекс состоит из нескольких шардов — маленьких подиндексов, которые структурируют хранилище и помогают быстрее находить нужные документы. 

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

Пример применения синонимов

Пример применения синонимов

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

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

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

Поступает запрос

Начало пайпайна — предварительное ранжирование

Начало пайпайна — предварительное ранжирование

Здесь начинается процесс выбора релевантных товаров и предварительного ранжирования, который состоит из нескольких частей:

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

  2. Затем мы осуществляем процесс текстовой обработки, который я описывала выше, но уже не для товара, а для запроса пользователя — удаляем стоп-слова, производим морфологическую нормализацию, расширяем синонимами. Причем синонимы для поисковых запросов могут отличаться от тех, которые мы применяем для названия товаров. В Elasticsearch есть два параметра: Index time — для названий на этапе индексации,  и query time —  для запросов.

  3. Далее мы смотрим, совпадает ли поисковый запрос или его модификации (нормализованный запрос или синонимы) с названием товара или его категорией, брендом и остальными полями, которые участвуют в поиске. При наличии совпадений товар считается релевантным запросу.

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

  5. Если и это не помогло, ослабляем запрос и снова проверяем на совпадения поисковый запрос и название товара. 

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

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

Текстовая близость и кликбейт

Для оценки релевантности и предварительного ранжирования используем два параметра:

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

  • кликбейт — считаем, как часто товар по данному поисковому запросу покупали. 

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

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

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

Ранжирование ML-моделью

Полный пайплайн Поиска в СберМаркете

Полный пайплайн Поиска в СберМаркете

Мы используем модель градиентного бустинга XGBRanker. В него мы передаем товары с их ключевыми признаками:  

  1. Взаимодействие пользователя с товаром.

  2. Наличие и размер скидки. 

  3. Популярность (покупаемость) товара. 

Как известно, в модель ранжирования, помимо признаков и целевых меток (то есть таргета) нужно также передавать дополнительный параметр — qid. Именно по этому параметру все входные данные разбиваются на группы, в рамках которых и происходит ранжирование. Мы для обучения модели разбиваем данные по поисковым сессиям. Одна сессия — это один запрос в выбранном пользователем магазине в определенный момент времени. 

Подготовка таргета

Начнем с таргета или, другими словами, целевой метки. Мы присваиваем ему значения от 0 до 4 по следующему принципу:

  • 4 — если товар был добавлен в корзину пользователем и выкуплен. 

  • 3 — если товар был добавлен в корзину, но покупка по каким-то причинам не завершилась.

  • 2 — если товар был добавлен в корзину, но затем удален.

  • 1 — если пользователь открыл карточку товара, но не добавил его в корзину.

  • 0 —  если товар был в поисковой выдаче, а пользователь его проигнорировал, но здесь все немного сложнее.

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

Сбор признаков

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

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

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

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

Обучение модели

После того, как таргет и признаки подготовлены, все данные отправляются в модель. Для оффлайн-тестирования используем метрики качества ранжирования NDCG@k и MAP@k.

Рейтинг важных признаков

Теперь перейдем к самым значимым признакам и обсудим первую десятку.

Целых три позиции среди первых топ-10 занимает признак — отношение добавлений товара к показам в поисковой выдаче в рамках ритейлера/региона/всего СберМаркета. Допустим, пользователь искал перцы в Metro. Смотрим, как часто определенный товар, например, перец красный добавлялся в корзину по запросу «перец» и делим его на число показов показов этого товара в других выдачах. 

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

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

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

Выводы

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

И пусть всегда находится именно то, что вы ищите!

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

© Habrahabr.ru