Новая инфраструктура ранжирования на рынке Building Faire

Faire — оптовая торговая площадка, объединяющая более 250 000 ритейлеров с лучшими мировыми независимыми брендами. Владельцы малого бизнеса в Северной Америке и Европе используют Faire, чтобы найти лучшие продукты для продажи своим клиентам.

Традиционно эти ритейлеры пополняли свои магазины, посещая личные торговые выставки. Один или два раза в год они посещали торговую выставку и размещали заказы на 6–12 месяцев, чтобы пополнить свой магазин. Решения о покупке принимались на основе опыта и устных отзывов с целью удовлетворения потребительского спроса. Если товары плохо продавались, розничные продавцы должны были снижать цены на товары или оставлять их на своих полках.

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

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

До времен

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

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

Ранжирование действует во время или после поиска, чтобы оптимизировать порядок продуктов, которые мы возвращаем. Обычно это делается с помощью числовой оценки, которая представляет рынок или бизнес-цель. Когда мы запустили Faire, у нас не было ни времени, ни необходимых ресурсов для создания сложных систем ранжирования, и вместо этого мы использовали очень простые системы поиска и повторного ранжирования, которые помогли нам сдвинуться с мертвой точки. Наша самая первая поисковая система, созданная в 2017 году, была основана на простых запросах MySQL, выполняемых к нашей действующей базе данных, которая явно была ограниченной по возможностям и медленной. Когда эта система начала ломаться, мы обновили каждую из наших основных поверхностей по частям. В поиске мы перешли на простой обратный индекс, управляемый сторонней компанией. Позже мы перешли на ElasticSearch (подробнее об этом ниже). Тем временем у нас все еще был поиск на основе MySQL для страницы бренда и навигации по категориям. Это создало множество несоответствий как в нашей серверной архитектуре, так и в пользовательском опыте на разных платформах: выбираемые пользователем фильтры были разными для поиска и навигации по категориям, правила сопоставления текста были совершенно разными для поиска продукта и поиска по страницам бренда, и наши журналы показов следовали другим правилам. схемы для каждой поверхности. Эти головные боли научили нас тому, что поиск должен быть единым для всех поверхностей. Со временем мы работали над устранением этих различий, и по мере того, как мы лучше понимали наши цели и требования, мы создали единый механизм поиска на основе Elasticsearch.

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

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

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

Требования

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

Задержка. Система ранжирования в реальном времени имеет множество жестких ограничений, наиболее важным из которых является задержка. Из предыдущих исследований мы знали, что при возврате результатов важна каждая миллисекунда. Задержки поиска до 500 мс, как правило, незаметны для пользователей [1], поэтому мы выбрали это значение в качестве нашего бюджета задержки запроса/ответа на стороне сервера.

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

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

  1. Легко вставляется в существующую поверхность. Мы хотели свести к минимуму первоначальные затраты, необходимые для переноса устаревшей поверхности на новую систему.
  2. Простота расширения за счет новых функций и моделей машинного обучения. Мы хотели, чтобы система убрала как можно больше барьеров для добавления новых функций ML, позволяя специалистам по данным и инженерам ML быстро выполнять итерации.

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

Мониторинг и оповещения. Любая сложная система подвержена большому количеству отказов. Ранжирование в реальном времени не является исключением из этого правила. Это означало, что нам нужно было иметь возможность отслеживать работоспособность системы и создавать системы мониторинга и оповещения с самого начала.

Системные компоненты

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

Это общий обзор системы. Ниже мы опишем каждый компонент, а также особенности проектирования и реализации каждого из них.

Retrieval

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

Для каждого запроса мы получаем ~1000 наиболее релевантных кандидатов из ElasticSearch для повторного ранжирования. Для поиска ElasticSearch определяет релевантность на основе функции взвешенной оценки, используя в своей основе BM25 (причудливую версию TF-IDF). В навигации по категориям мы переформулируем наш запрос, чтобы получить лучшие продукты по показателю качества в указанной категории. Наконец, для страницы бренда мы просто запрашиваем все продукты в каталоге указанного бренда.

Мы также работаем над добавлением поиска на основе внедрения в наш кластер ElasticSearch, чтобы поддерживать поверхности рекомендаций, которые не следуют эвристическому правилу запроса. В настоящее время мы обеспечиваем схожесть брендов и продуктов с помощью встраивания на основе машинного обучения, которые либо используют генерируемые в автономном режиме наборы данных k ближайших соседей (kNN), либо иным образом индексируются для приблизительного извлечения kNN с использованием параллельной системы в памяти. Обслуживание этих внедрений с помощью ElasticSearch позволит фильтровать поиск рекомендуемых продуктов (например, «Похожие продукты от брендов в вашем регионе»), семантический поиск и сочетания релевантности и персонализации.

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

Магазин функций

Для каждого из продуктов в наборе кандидатов нам нужно оценить модель на лету. Это означает, что когда поступает запрос, нам нужно получить и/или вычислить функции машинного обучения на основе розничного продавца, делающего запрос, контекста запроса (будь то поиск, навигация по категориям и т. д.) и продуктов-кандидатов. Чтобы максимизировать гибкость и производительность нашего набора функций, мы внедрили систему, которая использует как автономные пакетные вычисления, так и онлайн-вычисления в реальном времени.

Автономное вычисление признаков

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

Одним из мощных мотивов для вычисления вложений является решение проблемы пространственной сложности вычисления функций парного взаимодействия. Например, при ранжировании в поиске мы можем захотеть измерить сходство текста между поисковым запросом и названием продукта. Мы могли бы наивно вычислить признак сходства в автономном режиме, но это нужно было бы сделать для всех возможных комбинаций из 100 000+ запросов и 1 миллиона продуктов. В качестве альтернативы мы могли бы просто вычислить встраивания для каждого запроса и продукта по отдельности — в режиме реального времени мы можем затем получить каждый запрос и встраивание продукта и вычислить их сходство.

И функции, и встраивания вычисляются в автономном режиме с использованием комбинации запросов SQL и моделей машинного обучения с использованием нашего хранилища данных и управляются через Airflow. На момент написания у нас было более 2000 функций и несколько десятков вложений, вычисляемых с регулярной частотой.

Онлайн-вычисление признаков

Автономные функции и встраивания ежедневно вычисляются в нашем хранилище данных. Чтобы предоставлять эти функции в Интернете, мы используем схему «ключ: значение», которая упрощает поиск всех функций и вложений для одного объекта (продавца, бренда, продукта и т. д.). Эта карта хранится в выделенном кластере Redis, чтобы обеспечить низкую задержку при ранжировании.

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

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

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

Оффлайн обучение

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

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

Магазин моделей

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

Оценка модели при повторном ранжировании

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

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

Заключение

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

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

Команда данных Faire набирает сотрудников — присоединяйтесь к нам!

[1] https://iarapakis.github.io/papers/TOIS17.pdf