Увеличиваем выручку с помощью математики: как учитывать бизнес-контекст в оптимизационных задачах

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

В статье будет математика, псевдокод, но главное — постараюсь рассказать, как вся эта техническая часть опирается на бизнес-контекст. Поехали!

704112fab609b37909c41f444fad541a.jpg

Описание бизнес-процесса: карточка товара

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

9c79106c9a6a46cc2a527aca8e4cf7d7.png

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

Товарные предложения

Товарные предложения

Напротив каждого товарного предложения есть своя кнопка «Купить». Видно, что товарные предложения отсортированы определенным образом и на их позицию влияет ряд факторов. Назовем их факторами «привлекательности». Чем выше по нашим расчётам «привлекательность» предложения для клиента, тем выше оно в списке.

А ещё обратите внимание, что верхнее предложение идентично тому, что отображается у фиолетовой кнопки «Купить». Это не совпадение. Товарное предложение на верхнем месте становится выбором по умолчанию при нажатии вышеупомянутой кнопки. Эти товарные предложения мы называем «избранными».

Постановка задачи: увеличить выручку 

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

Управляя этими факторами, можно влиять на сортировку товарных предложений.

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

Выручка формируется из двух ключевых составляющих:

С одной стороны, если предложение с самой высокой ценой попадает в «избранные», выручка от каждой продажи будет велика. Однако, вероятно, такой товар будет покупаться реже из-за своей высокой стоимости. 

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

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

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

Поскольку важна интерпретируемость, то модели типа Black-Box отпадают автоматически. Так получаем оптимизационную задачу, где целью является поиск наилучших коэффициентов для увеличения выручки.

Давайте расскажу, как мы исследовали эту задачу. 

Формализация задачи

Требуется найти вектор параметров x, x∊ R^{k=4}каждый из которых лежит в интервале[0, 1]и используется в функции

ϕ(x)= -с_{ij}+\sum_{k=1}^{4}B_{ij}^{k}  x_{k}

Эта функция определяет продавца товара.

Все продавцы маркетплейса индексированы i, все товары индексированы j; c_{ij}- стоимость товараj у продавца i. Параметр для цены не подбирается, а назначается равным единице (максимальное значение интервала для остальных параметров); B_{ij}^{(k)} четыре численных характеристики продавцаiтовара j.

Если продавецi не продает товар j, то полагаемc_{ij}=0, а B_{ij}^{(k)}= -∞

Положимϕ_{ij}(x)— значение функции для пары(i, j).

Пусть параметры вектораxзаданы, а товарjзафиксирован (то есть определено значение его индекса j^{*}). Тогда для товараj^{*} основным является тот продавец, для которого значение функции достигает максимума. Если максимум достигается в нескольких точках, то основного продавца выбираем случайным образом.

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

Матрица разметки

Рассмотрим матрицуL, в которой по осям продавец-товар однозначно показано, какой продавец для какого товара является оптимальным.

Пусть параметрыx=(x_1,...,x_4)известны, тогда определена\{0, 1\}матрицаL(x)=(l_{ij}(x)), гдеl_{ij}(x)=1, если для товара j при заданном xосновной продавец — i, в противном случае l_{ij}(x)=0.

В таком случае для произвольной пары продавец-товар (i^{*}, j^{*})

l_{i^*j^*}(x):= \begin{cases} 1, &\text{если}~ ϕ_{i^*j^*}(x)= max_i\{ϕ_{{ij^*}}(x)\}, \\ 0, ~&\text{в противном случае.} \end{cases}

Отметим, что должно выполняться равенство\sum_{i} l_{ij}=1для любого товараj. Поэтому если для некоторогоj^*существуют несколько значенийi_{1}, i_{2}, ... таких, что l_{i_1j^*}=1, ~l_{i_2j^*}=1, ..., то случайным образом отбираем только один из таких кандидатов, а для остальных индексов значение функцииl для этого j^*зануляем.

Матрица объемов продаж R

Для каждого товара jпо историческим данным определяем объем продаж (в количественном эквиваленте) этого товара за единицу времени (за предыдущий день) каждым продавцом i. Если продавец i не реализует товар j, то полагаем, что r_{ij}=0.

Матрица цен C

Для каждого товара j определяем цену у продавцаi.Берется последняя известная цена (за предыдущий день).

Функция ожидаемой выручки

По определению эта функция равна

F(x)=\sum_{i,j} l_{ij}(x)⋅r_{ij}⋅c_{ij}→max,

0≤x_k≤1,для всех k=1,2,3,4.

Функция \sum_{i,j} l_{ij}(x)⋅r_{ij}⋅c_{ij} не является непрерывной в рассматриваемом гиперкубе. Поэтому оптимизационные методы, основанные на дифференциальном исчислении, могут давать неудовлетворительный результат. Вероятно, для решения предпочтительней будет использовать какой-либо derivative-free optimization solver.

Псевдокод описанной модели:

import numpy as np
import pandas as pd




def construct_model(x: list[float], features: list[str], df: pd.DataFrame) -> float:
   """Модель оптимизации выручки


   :param x: список оптимизируемых параметров (инициализация)
   :param features: список с названиями факторов привлекательности (price,
   cashback, delivery_time, delivery_price)
   :param df: датафрейм с данными о факторах привлекательности и объемах продаж
   (quantity с индексом (продавец, товар)


   :return: ожидаемая выручка при заданных значениях x"""
   dims = размер тензора (i, j, k)
   missed_vals = вектор длиной k заполненной - Inf и 0 (для цены)
   coords = сохраняем индексы ненулевых элементов из датафрейм df 
   B = создаем тензор размерностью dims и заполняем missed_vals
   B[coords] = заменяем missed_vals на значения (где они имеются)
   R = создаем матрицу из нулей размером (i, j)
   R[coords] = по аналогии, заменяем нулевые значения на фактические
   C = матрица цен размером (i, j)
   phi = считаем привлекательность как скалярное произведение B и x
   max_phi = выбираем индекс самого привлекательного продавца
   L = матрицу max_phi пребразумем {0, 1} матрицу
   LRC = L * R * C поэлементно перемножаем матрицы 
   f = - sum(LRC) суммируем по i, j


   return f

Ограничения и допущения

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

Избранное товарное предложение будет иметь больший объем продаж по сравнению с остальными из-за его заметного местоположения — прямо под главной фиолетовой кнопкой «Купить». Этот высокий объем продаж может зависеть от UX-дизайна карточки товара.

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

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

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

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

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

Перед корректировкой отметим два момента:

  • объемы продаж избранных товарных предложений не зависят от сортировки, поэтому их значения не требуют корректировки;

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

Поэтому, для каждого товара (не товарного предложения, а товара), по историческим данным о продажах за последний месяц, строится линейная модель зависимости объема продаж от цены (точнее от Δ_{цены}), которая имеет вид:

Quantity=Level+Trend⋅(Price - Price_{favorite})

  • Price-цена, по которой был куплен товар;

  • Price_{favorite}-цена избранного товарного предложения в момент покупки товара;

  • Quantity-объем продаж при разнице цены покупки товара и цены избранного предложения.

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

В результате построения были получены следующие взаимосвязи (пример для нескольких товаров).

31574fcf07aa09577b04b367e20d46ba.png

В качестве моделей использовалась робастная линейная модель с Huber Loss функцией.

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

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

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

\hat{r}_{ij}=r_{i_{max}j}+Trend_j⋅(c_{ij}-c_{i_{max}j})

Данная формула является хорошо интерпретируемой и имеет концептуальный смысл. Если

r_{ij}:= \begin{cases} r_{i_{max}j}, &c_{ij}=c_{i_{max}j}, \\ >r_{i_{max}j}, ~&c_{ij}<c_{i_{max}j}\\<r_{i_{max}j}, ~&c_{ij}>c_{i_{max}j} \end{cases}» src=«https://habrastorage.org/getpro/habr/upload_files/2e1/0d3/4a3/2e10d34a32415357a5f0efb22340bec1.svg» /></p>

<p>В итоге модель имеет следующий вид</p>

<p><img alt=

Оптимизация: имитация отжига 

Из-за негладкости функции применение градиентных оптимизационных методов ограничено (хоть и не полностью). Учитывая это, в качестве оптимизационного алгоритма предлагается использовать simulated annealing (имитация отжига). Используем его реализацию Dual Annealing из пакета scipy.optimize.

Первым аргументом передадим приведенную выше функцию construct\_model. В аргументе bounds зададим границы [0, 1] для каждого коэффициента.

Анализ результатов

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

  • ожидаемая выручка увеличилась на 2.7%;

  • средняя цена уменьшилась на 1.3%;

  • средний кэшбэк увеличился на 1.7%;

  • средние сроки доставки уменьшились на 4.4%;

  • средняя стоимость доставки увеличилась на 2.7%;

Следующим шагом мы хотели убедиться, что ситуация будет аналогичной не только на исторических данных, но и в реальности. Для этого был проведен А/B-тест.

A/B-тест

В эксперименте участвовали товары из 1 категории и 3 городов.

В остальном все по классике:

  • выбираем правило разделения на пилотную и контрольную группы;

  • выбираем ключевую и контрольные метрики;

  • проводим А/А-тесты для проверки ключевой метрики;

  • выбираем величину минимального детектируемого эффекта;

  • считаем необходимый размер выборки;

  • проводим A/B-тест.

Сплитование — по клиентам. Одной группе будет показан новый функционал, другой — старый. В качестве ключевой метрики берем среднюю выручку. В качестве контрольных метрик используем: количество клиентов в группах, среднее количество купленных товаров в корзине, средний размер примененного кешбэка. После этого проведем А/А-тест для ключевой метрики с целью проверки равномерности p-value.

Следующим шагом является выбор минимального детектируемого эффекта. В качестве отправной точки будем использовать значение ожидаемой выручки в 2.7%. Исходя из этого, рассчитаем необходимый размер выборки, который даст статистически значимый результат, принимая уровни ошибок 1 и 2 рода равными 0.05 и 0.2 соответственно.

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

Как найти оптимальное решение

Впереди — много всего интересного.

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

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

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

Это исследование дало нам не только математические и экспериментальные результаты. Помимо потенциальных точек роста для бизнеса, подтвержденных статистически, ещё мы получили опыт. Лишний раз увидели на практике, в какой области могут лежать ответы на вопрос «Как найти оптимальное решение прикладной оптимизационной задачи?». 


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

Нужно помнить, что задача в первую очередь прикладная и только потом оптимизационная. Остальное — дело техники. 

© Habrahabr.ru