Всем привет! На связи Ваня Ващенко, и я лид по развитию нейросетевых моделей в команде персональных рекомендаций Wildberries. Раньше я развивал B2C-рекомендации и нейросети кредитного скоринга в крупнейшем банке, а теперь вы видите результаты работы нашей команды каждый раз, когда заходите на главную страницу любимого маркетплейса. Сегодняшний рассказ — о том, как мы развиваем WildBERT.

WildBERT основан на классической архитектуре Bidirectional Encoder Representations from Transformers (BERT), улучшенной под задачи и проблемы, с которыми сталкивается маркетплейс. Скорее, это не одна конкретная модель, а концепция, которую мы применяем в разных процессах:

  • подборки Item-to-Item,

  • кандидатная модель в офлайн-выдаче — обучение на заказах за последний год,

  • доранжирование,

  • векторы User-to-Item для генерации признаков внутри кластеров пользователей,

  • реактивный источник в онлайн-выдаче — обучение на кликах за последнюю неделю.

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

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

Предыдущие оптимизации офлайн-модели

Прежде чем перейти к последним новостям, расскажу о том, что команда сделала до 2025 года.

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

5 млн — всё ещё много для хранения полного набора негативных примеров, поэтому при обучении модели используется случайное сэмплирование отрицательных примеров внутри батча, In-Batch Random Negative Sampling. Это стандартный подход для эффективной обработки таких объёмов.

Чтобы в рекомендациях не доминировали товары одного типа, мы ввели постоянную квоту на категорию при инференсе модели.

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

A/B-тестирование Head-to-Head

Чтобы быстрее проверять новые идеи, мы перешли на подход Head-to-Head (H2H). Его отличие от обычного A/B-теста в том, что у нас нет классической контрольной группы. Вместо этого мы сравниваем между собой две модели: группа A — базовый офлайн-BERT, который уже работает в продакшне, группа B — его измененная версия. Обе модели обучаются на одинаковых репрезентативных данных. Далее мы напрямую смотрим, какой BERT даёт лучшие результаты, без какого-либо «чистого» контроля.

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

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

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

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

Свежайшие эксперименты

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

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

Улучшение функции потерь (Loss Function). Вместо случайного негативного сэмплирования (Negative Sampling) теперь используется подход тяжёлых негативов (Hard Negative). Также мы добавили компоненту в лосс-функцию, отвечающую за разнообразие рекомендаций.

Дообучение на отложенных датах. Мы внедрили регулярное дообучение (Fine-Tuning) — тонкую настройку офлайн-модели на свежих данных. Этот подход позволил сократить лаг между тем, что модель видела на обучении, и фактической выдачей рекомендаций. Прежде лаг между событием на обучении и его учётом в рекомендациях составлял два дня, теперь сократился до не более чем одних суток.

Повышение разнообразия. Ранее у нас действовали простые квоты на категории (константа — не больше N товаров одной категории в рекомендациях). Если раньше на категорию выделялась фиксированная квота позиций, то сейчас внутри категорий мы применяем экспоненциальное затухание скоров товаров. Это приводит к уменьшению встречаемости преобладающей категории, тогда как для других групп ограничения могут быть мягче. Общее разнообразие подборки растёт, а мы действительно видим рост конверсии.

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

H2H-тесты показали значимый прирост метрик на этих улучшениях. Например, тест по hard-негативам показал около +2% к GMV (Gross Merchandise Value) на главной странице. Фичи пользователя — одно из лучших внедрений, они дали почти +3% роста GMV.

В конце я подробно расскажу о наших планах, но здесь не могу не упомянуть, что мы заложили на будущее переход от Item ID к Semantic ID. Причём на семантики хочется перевести не только идентификаторы, но и офлайн-метрики. Такой подход кажется более корректным и репрезентативным для оценки.

Неудачные гипотезы

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

Добавление «эталонных» кликов. Мы попробовали добавлять в последовательность не все клики по товару, а только качественные — когда пользователь не просто посмотрел карточку, но и кликнул, например, на отзыв или фотографию, то есть проявил больший интерес к товару. Такой сигнал реже и надёжнее, чем обычный клик. Казалось, что это улучшит модель, но на практике офлайн-модель стала более онлайновой — начала сильнее оптимизироваться на клики, которые частично каннибализировали метрики онлайн-источников. Проще говоря, наша Long-State модель стала вести себя как краткосрочная. А хотелось обратного: чтобы офлайн-модель улучшала метрики без просадки онлайновых алгоритмов. В итоге от идеи пока что отказались.

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

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

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

Онлайн-инференс (nearline)

Год назад мы только размышляли о возможности онлайн-инференса (он же — near real-time). На сегодняшний день мы наконец запустили нашу BERT-модель в боевом онлайн-режиме.

Как работает nearline?

Kafka стримит события пользовательских действий в реальном времени.

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

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

Выполняется приближённый поиск ближайших соседей (ANN) по этому эмбеддингу с помощью HNSW, чтобы найти кандидатов товаров.

Сформированный список кандидатов сохраняется в key-value хранилище (User Vector Storage).

Вся конструкция работает непрерывно: по триггеру от Kafka цикл запускается заново, обновляя nearline-рекомендации для пользователя каждый раз, когда тот совершает новое действие (или накапливается серия действий).

Офлайн vs nearline: основные различия

Теперь у нас фактически одновременно работают две концепции выдачи рекомендаций: офлайн и nearline. Они решают разные задачи и участвуют в разных процессах.

Вспомните, как я описывал офлайн-модель выше:

  • обучается на данных о заказах,

  • временной горизонт ≈1 год,

  • расширенный эмбеддинг,

  • лаг выдачи — до 1 дня.

Nearline:

  • обучается на всех типах взаимодействий (с предварительной фильтрацией, чтобы последовательность не разрасталась слишком сильно),

  • размер эмбеддинга уменьшён,

  • временной горизонт составляет несколько недель,

  • лаг выдачи — считанные секунды.

Настроив nearline, мы запустили A/B-тест, где на части трафика заменили офлайн-выдачу на обновку. Уже в первой итерации получились довольно оптимистичные результаты. Сначала мы заметили небольшую просадку по разнообразию рекомендаций — но быстро это исправили, расширив пул категорий, доступных пользователю. Когда мы перестали показывать слишком много однотипных товаров и увеличили разнообразие, конверсия выросла.

Что будет дальше

У нас уже есть амбициозные планы на ближайшие пару-тройку кварталов.

  1. Мы хотим финально настроить инфраструктуру для A/B-тестов онлайн. По сути, она уже готова, нужно лишь перенести наш офлайновый H2H-подход на онлайн-выдачу — с учётом различий в архитектуре.

  2. Неплохо было бы все наработки, которые мы внедрили в офлайне, проверить и на онлайн-инференсе. Понятно, что не всё применимо один в один, но если фича реализована и показала успех в офлайне, почему бы не попробовать её в онлайне?

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

  4. Мы хотим экспериментально подобрать оптимальный способ комбинирования офлайн- и nearline-подходов, чтобы добиться максимальной конверсии и качества рекомендаций. Наша онлайн-модель BERT сейчас получает на вход не только краткосрочный стейт пользователя, но и некоторую информацию от офлайн-модели (по сути, Long-State пользователя). Эта комбинация довольно сложная внутри, но факт есть факт: офлайн участвует в формировании онлайновых рекомендаций.

  5. В планах изменить характер выдачи nearline-модели. Сейчас рекомендации выглядят немного кликбейтово, это скорее «клики после кликов»: модель оптимизируется на последующие клики пользователя. А нам важно, чтобы эти клики конвертировались в покупки. Идея — перейти к концепции «покупки после кликов».

  6. И наконец: мы не отстаём от трендов HSTU-подобных моделей и активно экспериментируем в этом направлении.

Спасибо, что дочитали! Если у вас есть вопросы, буду рад ответить в комментариях.

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


  1. ChePeter
    04.12.2025 10:16

    Как сказал повар Лазерсон - разница между Россией и Западом глубока и не преодолима. Мы пьем и закусываем, а они едят и запивают.

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