Как мы разрабатываем персональные товарные рекомендации

    image
    Наши клиенты-магазины хотят делать крутой маркетинг. Чтобы люди больше покупали, они регулярно шлют им email-рассылки. И каждый раз думают: “Что же написать в письме?”.




    Можно писать просто: “Покупайте у нас почаще!”, но это не очень-то работает. Идея получше — вставлять в письмо рекламу товаров. Желательно, рекламу товаров, которые заинтересуют покупателей.


    Дальше расскажу о том, как мы с нуля делали настоящие персональные рекомендации.


    Выбираем, что рекомендовать


    Рекомендовать можно что угодно.


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


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


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


    Делаем базовую модель


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


    Для начала скачиваем таблицу покупок и разворачиваем ее в разреженную матрицу клиент-продукт.


    image

    Преобразование списка покупок в матрицу клиент-продукт


    Дальше используем коллаборативную фильтрацию. Интуиция здесь проста: клиент №1 купил продукт №1 и два продукта №3; клиент №2 также купил продукт №1 — вероятно, он захочет купить продукт №3.


    Коллаборативная фильтрация предполагает разложение одной большой разреженной матрицы на две плотные вытянутые матрицы латентных признаков существенно меньшего размера. Для разложения мы использовали готовую реализацию AlternatingLeastSquares из библиотеки implicit.


    После обучения модели получаем латентные признаки для всех клиентов и всех продуктов. Они представлены в виде матриц (количество клиентов х количество признаков) и (количество продуктов х количество признаков) соответственно.


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


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


    image

    Одинаковым цветом выделены покупки, которые сильно влияют друг на друга. Рамкой выделены некоторые будущие рекомендации


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


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


    Как ни странно, но такая простая схема оказалась рабочей и клиенты с удовольствием тестировали наши рекомендации. Мы делали AB тесты, где сравнивали персональные рекомендации с хитами продаж. В некоторых случаях разницы не было никакой, но в некоторых видели статистически значимый прирост конверсий в заказы до 10%. В основном это были магазины с высокой частотой покупок, и, соответственно, большим количеством данных.


    Используем больше данных


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


    Добавляем информацию о всех действиях


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


    Для каждого типа действия мы выбирали вес и добавляли его в нашу разреженную матрицу с этим весом.


    Учитываем время действия


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


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


    Учитываем явные признаки


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


    Для продуктов мы стали использовать цену, производителя, категорию продукта и всевозможные дополнительные поля, которые указывает магазин (размер одежды, цвет мебели, наличие ГМО в собачьем корме и т.п.).


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


    1. Делаем матрицы признаков (отдельно для клиентов и продуктов), предварительно их обработав стандартными методами: scaling, one-hot encoding.
    2. На основе признаков продуктов и имеющейся матрицы клиент-продукт восстанавливаем парные дополнительные признаки для клиентов. Восстановление делается таким образом, чтобы произведение признаков продуктов на парные к ним признаки клиентов как можно лучше приближало исходную матрицу. Эти дополнительные признаки приклеиваем к уже имеющимся латентным.
    3. Аналогично делаем с признаками клиентов.

    image

    Дополнение латентных признаков явными


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


    Настраиваем гиперпараметры


    Сначала о метриках


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


    Безусловно, онлайн AB-тесты — это более надежный способ проверить, насколько хороши рекомендации. И тесты эти показывали неплохие результаты.


    Однако, онлайн тесты это жутко неудобно:


    • Нужно найти магазин, который захочет рисковать своими клиентами и тестировать новую версию алгоритма.


    • Потом нужно подождать день/неделю/две, чтобы получить какие-то статистически значимые результаты.


    • Нужно повторить это 100 раз, чтобы протестировать 100 различных вариантов.



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


    В качестве офлайн метрики выбрали популярную и простую — nDCG@K (Normalized Discounted Cumulative Gain at K), точнее ее среднее для нескольких тысяч клиентов.


    Эта метрика качества ранжирования. Она принимает значения от 0 до 1 и показывает, насколько хороши выдаваемые рекомендации.


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


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


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


    А вот теперь о гиперпараметрах


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


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


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


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


    После настройки оказалось, что в среднем метрика по всем проектам выросла в 6 раз, а самый сильный прирост — в 35 раз!


    Улучшаем функционал


    Мы умеем делать неплохие персональные рекомендации. Круто.


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


    Зачем возьмут? Например, чтобы отправить в письме. Звучит неплохо.


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


    Но вам-то нужен мобильник! Как же быть?


    Пришел черед делать рекомендации, которые работают в режиме реального времени.


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


    Обучаем модель мы по-прежнему раз в сутки.


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


    Чтобы это стало возможным, нам потребовалось:


    1. Сделать хранилище, куда после обучения складываются модели.
    2. Организовать key-value хранилище для хранения ранее совершенных клиентом действий, потому что загружать их каждый раз из базы очень долго.
    3. Существенно ускорить наши рекомендации. Основной точкой ускорения стало использование библиотеки приближенного поиска ближайших соседей nmslib для выбора наиболее релевантных человеку продуктов. Хитрые ребята из Microsoft придумали, как свести задачу перемножения матриц и выбора топа элементов с наибольшим значением к задаче поиска ближайших соседей.
    4. Сделать микросервис, который отслеживает действия человека на сайте, присылает запрос на рекомендации, забирает эти рекомендации и показывает человеку.

    И эта адская машина заработала. И заработала быстро. Как только вы посмотрели мобильник, за доли секунды рекомендации пересчитываются, и вот вам уже рекомендуются мобильники, чехлы, стекла и тому подобное.
    Чтобы было понятнее, что есть быстро: мы выдерживаем нагрузку 7000 rpm (запросов в минуту), используя лишь 3 ядра CPU Intel Xeon 2.6 GHz. Такую нагрузку дают в пики активности 2 наиболее крупных наших клиента.


    Что дальше


    Кажется, что из текущей модели мы выжали все, что можно.


    Дальнейшее улучшение потребует кардинальных изменений.


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


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


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


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


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


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


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


    Так что мы продолжаем улучшаться, и будем стараться писать о том, что делаем. Не прощаемся :)

    Mindbox
    Автоматизация маркетига

    Комментарии 13

      +2
      Жаль ebay никак не догонит, что если я искал товар, а потом его купил, то НЕ НАДО мне больше совать его рекламу на каждом шагу!
        0
        Мы поняли это опытным путем еще до начала работы над этими рекомендациями
        0
        А какие сроки у вас занимает разработка?
          0
          Мы работаем итерациями по 1-3 недели, постепенно улучшая алгоритм. Разрабатывать начали немногим больше года назад, в общей сложности потратили около 3-4 месяцев
          0
          Спасибо за интересную статью. К сожалению, конкретных цифр по метрикам вы не привели (в отличие, скажем, от западных статей, где в обязательном порядке приводят величины полноты, точности и F1-меры). Потому что, если сама метрика маленькая, то ее рост в 6 раз это результат на уровне статистического шума. Но так не продать клиентам рекомендации, поэтому вы грамотно скрыли нюансы ;) Еще более интересно сравнивать прирост рекомендаций разработанного алгоритма по сравнению с тривиальными стратегиями, а там картинка будет еще более плачевная, особенно если под это дело подвести экономику (ведь за рекомендации ваш клиент будет платить, как за сервис).

          Учет сезонности — это хорошо. Но есть масса отраслей, где ваш подход не будет работать. Например, цикличность покупок: продукты питания, товары для дома, стройматериалы… А вы предлагаете из рекомендаций исключать то, что человек уже покупал. Еще есть отдельная тема рекомендовать то, что нужно побыстрее продать (в том числе со скидкой). Здесь алгоритмы коллобративной фильтрации работают плохо, они вообще изначально использовались в системах, где есть обратная связь (например, оценка покупки, заказа).
          Это ниша интернет-магазинов, но в оффлайн торговле это работать не будет.


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

            Масса отраслей, про которые вы говорите, относятся к FMCG. В силу ряда причин среди наших клиентов нет никого из этих отраслей, поэтому подход везде работает.
            По поводу того, что нужно побыстрее продать — Вы правы, это отдельная тема. Поэтому у нас есть не один алгоритм рекомендаций, а около 20 — на все случаи жизни :)

            А вот про офлайн Вы не правы — у нас многие клиенты работают в офлайне, и они тоже успешно используют наши рекомендации.
              0
              Благодарю за ответ. Не убедили, но мой опыт в основном с данными в FMCG.
              Выбрать метрику, которую сложно интерпретировать, по величине которой сложно сказать, хороши ли рекомендации — а для чего тогда выбирать такую метрику? Цифры ради цифр?

              Приводить цифры — хороший тон (хотя бы диапазон). И обязательно точность и полноту. Тогда многое лично для меня прояснится. И пусть на одной-двух компаниях, не разглашая. Понимаете, если у вас тривиальная стратегия рекомендаций дает точность 10%, а ваш алгоритм — 15%, то это не окупится у заказчика никогда. Но заказчик это купит и будет считать, что рекомендации работают, не зная, что они чуть-чуть лучше системы, построенной на простых правилах. А когда товарных позиций тысячи, точность будет всегда низкая, либо все рекомендации будут крутиться около хитов продаж, тогда здесь снова тривиальная стратегия даст точность, близкую к коллобративной фильтрации. А если еще и ассортимент часто обновляется… Предсказывать конкретные товарные позиции в таких условиях нереально. Нужно подниматься на уровни выше, групп, подгрупп.

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

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


                Как Вы предлагаете считать точность и полноту рекомендаций? Это задача ранжирования, а не классификации.


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


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


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

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

              Что значит, как я предлагаю считать точность и полноту рекомендаций? Это стандартные метрики оценки качества рекомендаций: Prec@k, Rec@k и F1@k где k — число рекомендаций.
              Если prec@5 = 20%, то я понимаю, что в среднем из 5 порекомендованных товаров 1 купили. Кстати, оптимальный размер k — то же вопрос, так же как и подбор длины окна прошлой корзины.

              То, что человеку нужно предложить конкретный товар, это тоже заблуждение (так не везде). Если у меня высокая вероятность купить кроссовки, то как вы мне предложите конкретную модель? Есть такая поговорка: сколько людей — столько мнений. И никакой алгоритм не подберет мне нужную. Потому что я и сам не знаю и буду ориентироваться вначале на те модели, где есть скидка. Так может, мне в рекомендациях прислать купон на скидку на кроссовки?..

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

              Проблема исключения купленных товаров лежит в том, что рекомендуются сами товары. Если рекомендовать на более высоком уровне, там такой проблемы нет.
                0
                Метрики, про которые Вы говорите слишком упрощены — они не учитывают порядок рекомендаций. Кроме того, если я скажу, что prec@5 = 3%, сможете ли Вы понять, хорошо это или плохо? Вроде мало, но в денежном эквиваленте может быть совсем неплохо.
                В любом случае, все эти метрики искусственные, поэтому мы не ориентировались на них как на абсолютную оценку качества. Для абсолютной оценки мы проводим AB-тесты.

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

                Пока алгоритмы выбирают наши клиенты при помощи наших же менеджеров. Они прорабатывают множество кейсов, настраивают триггеры. Например, когда человек только зашел на сайт, ему рекомендуются популярные товары, когда посмотрел несколько товаров, рекомендуются похожие, когда добавил в корзину — сопутствующие. У каждого своя специфика и крайне сложно сделать хорошую систему, которая сама бы все учитывала и подбирала оптимальный набор алгоритмов. Но Вы правы, такая система нужна и когда-нибудь мы ее обязательно сделаем.
                  0
                  О какой важности порядка рекомендаций идет речь, если мы предлагаем человеку, скажем, 2 товара (prec@2 и recall@2)? Он их либо купит, либо не купит.

                  Если Вы мне скажете, что prec@5 = 3%, а тривиальная стратегия дает 2,5%, я скажу вам, что это плохо. Но ваш клиент будет думать, что это хорошо, потому что про 3% ему сообщат, а про 2,5% — нет :). Это рынок, нужно продавать услуги, я все понимаю. Но все же, когда Вы пишете статьи, нужно понимать, что их могут читать люди, которые хорошо погружены в тему. Так же как и не нужно писать, что среди ваших клиентов нет FMCG. Потому что статью может читать человек, который работал с компанией сегмента FMCG, у которой один из подрядчиков Mindbox.
                0

                А как вы интегрируетесь с магазином, чтобы забирать статистику, товары, а также как матчите разные данные аналитики (заказы, какие-то действия на сайте и тд)?

                  0
                  Для интеграции с внутренней системой магазина у нас есть API. Мы предлагаем CDP (Customer Data Platform) — платформу для хранения данных клиентов, магазин присылает нам информацию обо всех действиях, совершенных пользователем: вход в лк, просмотр товара, оформление заказа и т.д. Даже если у магазина стоит 10 разных систем и каждая хранит какие-то свои данные, у нас все собирается в одном месте. Благодаря этому мы можем собирать статистику и делать продвинутую аналитику

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

                Самое читаемое