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

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

Ретроспектива

С 2019 по 2023 год мы делали модели на транзакциях: BERT4Rec, MultiVAE, DSSM. Пробовали разные подходы, и в целом все работало, но нужен был объединяющий все это вместе градиентный бустинг, чтобы повысить качество рекомендаций. В прошлом году удалось его реализовать.

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

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

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

Мы хотели заранее сформировать путь клиента и начать показывать ему рекомендации согласно этому спланированному пути. Обычная модель решит, что странно рекомендовать видеосервис, — лучше что-нибудь другое. А с RL-моделью идея с долгосрочным путем могла бы сработать. 

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

Подход «Многорукие бандиты»

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

Кроме того, нам хотелось добавить user-item score для оценки релевантности на основе транзакций. Для этого мы немного доработали инференс Mab2Reс — качество улучшилось и достигло уровня бустинга.

Примерные компоненты вектора пользователя, которые описывают его интересы
Примерные компоненты вектора пользователя, которые описывают его интересы

Проверили эти подходы на офлайн-данных — по метрикам получилось сравнимо с градиентным бустингом. Запустили А/Б-тест и увидели, что многорукий бандит (эпсилон-жадный алгоритм с вероятностью случайного сэмплирования 5%) постоянно немного проигрывает бустингу по кликам. Скорее всего, достичь прироста помешали случайные элементы.

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

График АБ-теста по степени равномерности распределения кликов между офферами
График АБ-теста по степени равномерности распределения кликов между офферами

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

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

Размышляя над решением этой задачи, я вспомнил одну статью от Netflix, в которой упоминалась очень важная проблема: как именно у нас был сделан А/Б-тест? Есть группы A и Б, есть выдача со случайным подмешиванием и без. Группа A получает меньше кликов, группа Б получает максимум. Все это сложено в один набор данных, на котором мы учим две модели.

Проблема следующая: если у нас есть exploration в группе A — условно успешные примеры, которые мы нашли благодаря случайному подмешиванию элементов, — эта часть данных попадает и в модель Б. 

Схема, как данные разделяются, а потом снова смешиваются между группами
Схема, как данные разделяются, а потом снова смешиваются между группами

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

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

Попутно я разочаровался в архитектуре А/Б-тестов по рекомендациям. И если у нас еще была надежда на exploration, теперь мы задумались над тем, как вообще можно измерить exploration бандитов на уровне системы. На уровне пользователей его точно не было.

Подход SASRec + RL

Схемы по внедрению RL-составляющих в обычную задачу next-item recommendation
Схемы по внедрению RL-составляющих в обычную задачу next-item recommendation

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

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

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

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

Так как мы получили прирост в офлайне, нужно провести А/Б-тест. Мы сталкиваемся с другой проблемой: не существует определения длительности тестирования долгосрочной оптимизации. 

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

Есть и другая проблема, когда мы внедряем RL в SASRec. Мы делаем это через один признак в бустинге. Но проблема в том, что долгосрочная оптимизация может быть просто съедена этим бустингом: SASRec + RL скажет, что надо рекомендовать видеосервис, чтобы пользователь через какое-то время совершил еще два клика, а бустинг скажет, что не стоит так делать и сейчас релевантно показывать другое. 

Лучшего решения мы не нашли и оставили как есть. Вот результат тестирования:

Нулевая отметка — это бустинг на обычном SASRec, а график — бустинг на SASRec + RL. Количество кликов с девятого дня увеличилось, а объем кэшбэка снова не вырос
Нулевая отметка — это бустинг на обычном SASRec, а график — бустинг на SASRec + RL. Количество кликов с девятого дня увеличилось, а объем кэшбэка снова не вырос

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

Круги Эйлера для гипотетических множеств интересов аудитории сервиса и контента, существующего на платформе
Круги Эйлера для гипотетических множеств интересов аудитории сервиса и контента, существующего на платформе

Еще у нас была проблема подглядывания: если SASRec + RL что-то где-то угадает, обычный SASRec тоже это увидит и потом сможет на этом обучиться. Поэтому возникает вопрос: если бы мы вставили в обычный SASRec клики и покупки, у нас был бы такой же результат? Но ответа не было, потому что мы тестировали SASRec + RL.

Помимо этого, были и другие вопросы без ответов. Например: 

  • Существуют ли в данных те сложные паттерны, о которых я фантазировал? То есть действительно ли люди так пользуются нашим приложением, так совершают покупки? 

  • Можем ли мы повлиять на поведение клиента своим ранжированием? Быть может, человек заходит в приложение потому, что уже готов что-то купить, а не потому, что мы убедили его своими рекомендациями, и тогда наша долгосрочная RL-оптимизация не имеет смысла. 

  • Как оценить в офлайне, что мы смогли долгосрочно оптимизировать? У нас есть результаты SASRec и SASRec + RL, по метриками они примерно одинаковы. В RL есть понятие политики, то есть, возможно, мы выучили какую-то политику рекомендаций, которая приведет пользователя к некой долгосрочной цепочке покупок. Но как ее оценить? Ведь политика — это просто функция от состояния в набор рекомендаций. 

  • И как оценить все то же самое в онлайне? Мы можем измерить количество кликов, но правда ли, что пользователи стали входить в долгосрочные паттерны?

Подход TD3BC

Не найдя ответов, мы решили попробовать третье решение. У нас уже был опыт создания RL-модели TD3BC, и она успешно эксплуатировалась. Для ее обучения мы взяли эмбеддинги с транзакцией, BERT4Rec с неким описанием интересов пользователя. А еще у нас были позитивные и негативные действия: куда пользователь кликал и куда не кликал. Их мы тоже поместили в состояние. 

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

Обучив модель, мы получили по результатам А/Б-теста такой результат:

Нулевая отметка — бустинг. По сравнению с предыдущими решениями получили заметное улучшение (проседание в районе 13—14-го дня связано с особенностями продукта: витрина постоянно меняется)
Нулевая отметка — бустинг. По сравнению с предыдущими решениями получили заметное улучшение (проседание в районе 13—14-го дня связано с особенностями продукта: витрина постоянно меняется)

Выросло не только количество кликов, но и объем кэшбэка (+3,6%).  К этому мы и стремились.

Выводы

  • Многорукие бандиты не выиграли, но снизили popular bias.

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

  • Несмотря на наши надежды, SASRec + RL увеличила количество кликов, но кэшбэк не вырос. Еще и модель обучалась дольше.

  • Непонятно, сколько времени нужно тестировать рекомендации с RL и сколько экспериментов для этого нужно. Хорошую теорию, видимо, только предстоит найти.

  • Модель TD3BC обогнала классический бустинг по кэшбэку. 

Но есть нюанс, из-за которого мы не стали развертывать RL: параллельно с тестированием TD3BC мы проводили еще один эксперимент — с другим бустингом. И он показал еще более высокие результаты.

График АБ-теста по количеству переходов на оффер для двух разных групп
График АБ-теста по количеству переходов на оффер для двух разных групп

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

Более правильный путь тестирования RL для ранжирования
Более правильный путь тестирования RL для ранжирования

Почему бы не переиспользовать TD3BC на новом бустинге? Потому что TD3BC — это нейросеть. И по-хорошему нам нужно сначала сделать нейросеть, в которую можно легко вставлять новые признаки: числовые значения, все признаки по предложению, кросс-признаки «пользователь — предложение», «пользователь — категории» и так далее. Эта модель должна обучаться с учителем, чтобы она копировала любое поведение в наборе данных (behavioral cloning). После этого можно сравнить ее с TD3BC.

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

Если вас интересует интеграция RL в SASRec, рекомендую статьи Supervized Advantage Actor-Critic for Recommender Systems и Self-Supervized Reinforcement Learning for Recommender Systems. А если остались какие-то вопросы или вы хотите поделиться мнением — залетайте в комментарии!

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


  1. mmvb
    17.02.2025 05:14

    Есть интересный устный доклад Олега по этой теме с митапа банка Тинькофф: https://vk.com/video-151223562_456239472