📱 Подписаться
IT и цифровая трансформация

Эволюция similar-рекомендаций: как мы переосмыслили ленту похожих товаров

📰 Habr 👁️ 0 просмотров

dsborisov5 часов назад

Эволюция similar-рекомендаций: как мы переосмыслили ленту похожих товаров

Уровень сложностиСреднийВремя на прочтение14 минОхват и читатели4.2KБлог компании Lamoda TechМашинное обучение*КейсВ онлайн-шопинге важно, чтобы поиск нужной вещи не занимал много времени, а результат был персональным для каждого пользователя. Одна из фичей, которая позволяет это делать — рекомендации похожих товаров. Если бы их создавали люди, результат подборок сильно бы зависел от их вкуса: ведь каждый может ориентироваться на разные признаки вроде цвета, бренда, фасона или цены. В fashion похожесть вообще редко сводится к одному признаку. 

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

Меня зовут Дима Борисов, я дата-сайентист в команде рекомендательных систем Lamoda Tech. Расскажу о том, как мы развивали общую систему similar-рекомендаций и переосмыслили ленту похожих товаров в приложении. Коротко пробежимся по основным принципам работы рекомендаций и старой модели, подробнее остановимся на новом подходе. Расскажу, как сейчас работает улучшенный алгоритм на DS и бэкенд-стороне: с современными энкодерами, обученным реранкером и онлайн-ранжированием.

Базовая архитектура и старый алгоритм

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

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

Задачу похожих товаров можно решать несколькими способами.

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

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

Графовые подходы представляют пользователей и товары в виде связей: user-item или item-item графов. Они помогают находить более сложные зависимости, но требуют отдельной инфраструктуры и аккуратной работы с обновлением данных.

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

Наш старый алгоритм поиска похожих товаров был основан на двух подходах:

• Визуальный поиск по фотографиям. В интерфейсе он соответствовал баблу «Визуально».
• Контентный поиск по атрибутам. Он лежал за баблами «В стиле этого бренда» и «Этого цвета».Визуальный подход в целом сохранился и будет упомянут в статье далее как отдельный кандидатогенератор. Поэтому сначала разберем контентную часть.

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

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

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

АтрибутВесsimАтрибутБренд: Mademan Wbrand0Бренд: UsetWbrand_sm0.76Цвет: синийWcolor1Цвет: синийWcolor_sm1Цена: 2 730Wprice0.89Цена: 4 409Для оценки похожести категориальных признаков мы использовали Word2Vec. Идея была в том, чтобы представить атрибут не сам по себе, а вместе с контекстом товара. Например, не просто цвет = розовый, а женские юбки + цвет = розовый.

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

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

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

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

Таргет строился так: 

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

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

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

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

Что хорошо работало:Чего не хватало:Интерпретируемость. Модель опиралась на понятные атрибуты товара: бренд, цвет, цену, категорию и иные признаки. Поэтому было проще объяснить, почему один товар считается похожим на другой.Разных модальностей. Алгоритм работал в основном с атрибутами и не использовал в полной мере изображения и текстовые описания товаров.Работа с item cold start. Для новых товаров не нужно было ждать накопления поведенческой статистики. Если у товара уже заполнены атрибуты, его можно включать в расчет похожести.Масштабируемости. Для разных категорий и контекстов приходилось поддерживать много отдельных моделей, что усложняло развитие системы и нагружало инфраструктуру.Гибкость по категориям. Для разных товарных контекстов можно обучать отдельные веса атрибутов. Например, для спортивной обуви и платьев важность признаков отличалась.Персонализации. Похожесть товаров считалась в основном общей для всех пользователей, без явного учета индивидуальных предпочтений.Проверенная production-логика. Алгоритм давно работал в проде и решал свою задачу.Единого эмбеддинга товара. Хотелось получить универсальное векторное представление, которое можно использовать не только в similar-рекомендациях, но и в других задачах.На основе таких вводных данных мы начали работу над новым алгоритмом. 

Обновляем алгоритм рекомендаций

В основу нового поиска similar-рекомендаций мы заложили два подхода: 

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

Новый алгоритм мы обучали в парадигме metric learning. Основная идея такого подхода в том, чтобы расположить товары в латентном пространстве эмбеддингов: похожие должны находиться ближе друг к другу, а непохожие дальше.

В терминах обучения у нас есть товар-якорь, positive-примери negative-пример. Positive — это товар, который мы считаем похожим на якорь. Negative — товар, который должен быть от него дальше в embedding-пространстве. Поэтому качество модели во многом зависит от того, насколько правильно мы определим positive-примеры.

Для сбора positive-таргета мы отталкивались от логики старого алгоритма, но расширили ее поведенческими сигналами из двух источников:

• Заказы. Если товары одной категории попадали в один заказ пользователя, мы считали, что между ними есть связь. Такие товары могли быть заменителями или близкими альтернативами, поэтому мы присваивали им один класс похожести.
• Пользовательские сессии. Если в рамках одной сессии пользователь просматривал товары одной категории с близкими ключевыми атрибутами, мы тоже считали их positive-примерами. Для сессий условие было строже, чем для заказов, потому что просмотров намного больше и в них больше шума.Дальше negative-примеры подбирались уже в процессе обучения. Для этого мы использовали hard negative mining: модель искала товары, которые визуально или семантически похожи на якорь, но не относятся к его positive-классу. Такие примеры помогают модели лучше разделять действительно похожие и просто случайно близкие товары.

Как настраивали датасет 

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

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

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

Как обучали модель

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

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

Во время обучения батчи балансировали по категориям, чтобы модель видела разнородные товары и не смещалась в сторону самых частотных групп. Для подбора сложных негативов использовали Multi Similarity Miner, а в качестве функции потерь выбрали Circle Loss. Такая связка помогала сближать товары внутри одного positive-класса и одновременно отдалять сложные negative-примеры.

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

В качестве картиночных и текстовых энкодеров пробовали несколько моделей, среди них dino-v2 и e5-large. По текстовой части лучшее качество в наших экспериментах показал rubert-tiny2, а по картиночной — fashion-clip.

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

Другой большой блок экспериментов был связан с функцией потерь и метрикой расстояния. Мы тестировали triplet loss, contrastive loss и Circle Loss с разными гиперпараметрами. В качестве расстояния сравнивали cosine и euclidean distance. В финальном сетапе остановились на Circle Loss, который лучше всего показал себя на нашей задаче.

Основными метриками для принятия решения были CMC@8 и Precision@8. CMC@8 показывает, оказался ли хотя бы один релевантный товар среди первых 8 кандидатов. Precision@8 показывает долю релевантных товаров в топ-8 выдачи.

Этап экспериментовЧто фиксировалиЧто сравнивалиВыбранный вариантCMC@8Precision@8Baseline без дообученияГотовые эмбеддинги, без обучения на наших данныхТолько изображение, только текст, изображение + текстText-only baseline оказался самым сильным среди baseline-подходов0.68620.6518Выбор энкодеровDataset_1, concat, Circle Loss, cosine distance, 30 эпохfashion-clip, dino-v2, clip для изображений; rubert-tiny2, ruroberta-large, e5-large для текстаfashion-clip + rubert-tiny2 выбрали как лучший баланс качества, скорости и веса модели0.82950.7966Объединение модальностейfashion-clip + rubert-tiny2, Dataset_1, Circle LossПростая конкатенация, обучаемые projection-head слои, усреднение эмбеддинговПростая конкатенация image и text embedding0.82950.7966Выбор датасетаfashion-clip + rubert-tiny2, concat, Circle LossDataset_1 против Dataset_2 с годовым окномDataset_2, потому что он лучше покрывает сезонность0.84010.7999Loss, mining и distancefashion-clip + rubert-tiny2, concat, Dataset_1, pilot-обучение на 5 эпохахTriplet Loss, Contrastive Loss, Circle Loss; cosine и euclidean distance; разные miner-ыCircle Loss + cosine distance + Multi Similarity Miner0.80730.7738Финальное обучениеВсе выбранные компонентыФинальная проверка выбранного сетапаfashion-clip + rubert-tiny2, concat, Dataset_2, Circle Loss, cosine distance, Multi Similarity Miner0.84230.8006Важно, что не везде мы выбирали вариант с максимальной метрикой в третьем знаке после запятой. Например, среди текстовых энкодеров более тяжелые модели давали очень близкое качество. Но финальный выбор делали не только по offline-метрикам, еще учитывали сложности дальнейшего внедрения.

Как улучшить качество с помощью детектора одежды

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

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

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

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

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

Архитектура инференса новых алгоритмов

Посмотрим, как на основе этих изменений обновилась архитектура.

• Инференс основного алгоритмаОсновной алгоритм использует две модальности: изображение и текст товара.

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

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

Дальше по этим эмбеддингам ищем ближайших соседей для каждого якорного товара. После candidate generation применяем бизнес-слой: фильтры по доступности, категории, полу, ассортиментным ограничениям и другим правилам. Финальный список кандидатов для каждого якоря сохраняем в key-value-хранилище Aerospike.

Качество новой модели сначала оценивали в офлайне по recall@k. Эта метрика хорошо подходит для этапа candidate generation, потому что здесь нам важна полнота: нужно набрать как можно больше потенциально релевантных товаров, которые дальше сможет отсортировать реранкер.

По итогам офлайн-оценки получили значимый прирост recall@k. Также на исторических сессиях увидели рост продуктовых прокси-метрик: просмотры страниц выросли на 13%, а добавления в корзину в рамках одной сессии — на 30%.2. Инференс визуального алгоритма

Второй алгоритм мы называем визуальным. В отличие от основного, он не дообучается на нашей задаче, а использует готовый FashionCLIP as is.

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

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

Здесь мы также получили значимый прирост по целевой метрике: +2% к добавлению в корзину.

Контроль пересечения кандидатов

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

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

При генерации 200 кандидатов на каждый якорный товар пересечение между движками составило около 20%. Это хороший сигнал: алгоритмы находят разные типы похожести и вместе дают более разнообразный пул кандидатов.

Финальное ранжирование: оптимизируем precision

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

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

Для реранкера мы используем CatBoost. Его задача взять готовый набор кандидатов и отсортировать его в порядке ожидаемой релевантности. В качестве таргета используем добавление в корзину, а обучаемся на реальных показах товаров. Это сильный поведенческий сигнал: пользователь увидел товар в выдаче и явно проявил к нему интерес.

В модели используются три основные группы признаков: 

• Товарные (sku)
• Пользовательские (user)
• Парные (user-sku)Парные признаки особенно важны для персонализации. Они помогают понять, насколько товар соответствует привычным предпочтениям пользователя: по брендам, стилям, цветовым группам и другим фасетам.

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

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

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

Как все работает вместе

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

Сервис реранкера ходит в key-value-хранилище Aerospike и забирает оттуда кандидатов, фичи и эмбеддинги, которые нужны для онлайн-ранжирования. Дальше внутри сервиса происходит CatBoost inference, расчет embedding-фичей через dot product и жадный merge кандидатов из двух источников.

После этого мы обрезаем результат до top-N товаров и передаем финальную выдачу в сервис рекомендаций. А уже оттуда пользователь получает ленту похожих товаров в приложении.

Отдельно работает офлайн-пайплайн. На Hadoop-кластере каждый день считаются фичи и кандидаты для рекомендательной полки. По расписанию в Airflow обучается реранкер, после чего фичи отправляются в Aerospike, а модель CatBoost-реранкера сохраняется в S3.

В итоге мы объединили два новых кандидатогенератора и персонализированный реранкер, и после запуска A/B-теста получили статистически значимый прирост по покупкам на 1,5%.

Итоги и планы

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

Мы продолжим развивать рекомендации, вот что в ближайших планах:

• Улучшение детектора одежды и расширение словаря с маппингом.
• Эксперименты с трейн-датасетом, пересмотр варианта сбора групп для ранжирования. 
• Ресерч новых моделей в основной кандидатный движок и замена энкодеров на более новые. 
• Добавление новых фич в реранкер — это постоянный процесс.Сталкивались ли вы с подобными задачами и что удалось реализовать? Теги:• рекомендательные системы
• ecommerce
• ai
• ml
• машинное обучение
• искусственный интеллектХабы:• Блог компании Lamoda Tech
• Машинное обучение

Получайте больше инсайтов о систематизации бизнеса

Подписывайтесь на Telegram-канал Business Operations — ежедневные материалы о бизнес-процессах, операционном управлении и повышении эффективности

💬 Подписаться на канал