![Так могла бы выглядеть наша команда, но мы на удаленке Так могла бы выглядеть наша команда, но мы на удаленке](https://habrastorage.org/getpro/habr/upload_files/ee4/abd/e94/ee4abde949e0e170796574d8ca1bc5de.jpg)
В офлайне покупатель видит полки магазина и сам может понять, чего нет в наличии, а что можно положить в корзину и купить прямо сейчас. В онлайне«глазами» пользователя становится каталог: он всегда должен быть актуальным.
Предположим, вы хотите заказать в СберМаркете доставку любимых конфет. Сайт и приложение выводят их в поиск, но на момент сборки заказа товар может закончиться или оказаться просроченным.
Из-за отмены всем станет грустно. Есть шанс, что в такой ситуации покупатель и вовсе откажется от сервиса, а компания потеряет потенциальную выручку. Чтобы пользователи не расстраивались и оставались с нами дольше, мы ввели метрику под названием «доля ненайденных товаров». Она показывает процент таких продуктов от общего числа заказов.
Рассказываем, как мы внедрили алгоритм автоматического отключения таких «призраков» и уменьшили долю ненайденных товаров на 25%.
Как работает СберМаркет
Клиент заказывает товары с полок конкретного магазина. Наши сборщики собирают корзину, а курьеры — доставляют заказ домой в течение двух часов. Если сборщику не удалось найти товар (или продукт оказался плохого качества), он сигнализирует об этом в специальном приложении, затем сообщает клиенту и предлагает заменить товар на другой.
К СберМаркету подключены сотни магазинов по всей России. В каждом — уникальный ассортимент из десятков тысяч товаров. В общем каталоге сервиса больше 16 млн позиций. На таких объемах контролировать актуальность и наличие тяжело.
Чтобы улучшить сервис, нам предстояло не только сделать эффективный алгоритм, но и внедрить его в кратчайшие сроки. За время пандемии количество заказов выросло в 15 раз — нужно соответствовать.
Идея алгоритма
В каждом из магазинов, подключенных к СберМаркету, есть постоянная команда сборщиков. Лидеры команды регулярно присылают в отдел контента список артикулов тех товаров, которые нужно отключить в каталоге: например, когда случилась задержка поставки или истек срок годности.
Исторически 70% таких артикулов имеют очень высокий показатель ненайденных товаров за последние дни продаж — более 50%. Значит, нужно срочно заблокировать позицию в каталоге, чтобы клиенты не смогли купить то, что мы не можем привезти.
Выше представлена динамика неуспешных сборок по товару из категории «фрукты» в одном магазине. С 21 июня по 1 июля показатель ненайденных товаров был 100%. Хотя ретейлер передавал нам другие данные.
Клиенты могли спокойно покупать эту позицию, а потом получали звонки от сборщиков: «такого нет, можем заменить или отменить». Возможно, товар имелся в наличии, но был незрелыми или испорченными.
Было бы логичным отключить позицию еще в самом начале пика. Так мы спасли бы сборщиков от лишней работы, а клиентов — от грусти и звонков. Как мы сформулировали задачу по итогам кейса: нужно построить такой алгоритм, который будет каждый час мониторить ненайденные сборщиками товары, отбирать из них самые «проблемные» и моментально отключать их в каталоге магазина.
И начали писать его на Python.
Как работает алгоритм
Для каждого магазина мы каждый час берем список ненайденных товаров за текущий день.
Из базы данных наших заказов вытаскиваем историю последних сборок. Для каждого ненайденного товара берём историю THRESHOLD_ORDERS.
Если в последних THRESHOLD_ORDERS заказах процент ненайденных товаров более THRESHOLD_CANCELLATION процентов, то эту позицию нужно заблокировать.
Если товар ещё ни разу не блокировался или с момента последней блокировки уже прошло DAYS_NO_CANC дней, то товар блокируется на HIDE_1 дней.
Если с момента последней блокировки товара прошло менее DAYSNOCANC дней, то:
~ товар блокируется на HIDE_2 дней, если текущая блокировка ставится второй раз подряд;
~ товар блокируется на HIDE_3 дней, если уже блокировался более двух раз подряд.
Пример параметров алгоритма для магазина:
'params': {
'DAYS_NO_CANC': 4,
'HIDE_1': 2,
'HIDE_2': 11,
'HIDE_3': 9,
'THRESHOLD_CANCELLATION': 0.4,
'THRESHOLD_ORDERS': 3
}
Параметры алгоритма подбирались отдельно для каждого магазина на исторических данных с использованием библиотеки hyperopt на Python. В процессе оптимизации максимизировалась метрика F-мера с beta 0.7.
Зачем так усложнять
Почему нельзя просто всегда блокировать товары на N дней? Зачем нам параметры DAYS_NO_CANC, HIDE_1, HIDE_2 и HIDE_3?
Теоретически можно блокировать сразу все ненайденные позиции на несколько недель вперед. Мы точно поймаем всех «призраков», но в таком случае выключим практически весь ассортимент магазина в каталоге. В этой задаче нам важно сохранить высокую точность.
Кейс №1: товары нужно блокировать на маленький срок
У ретейлера A в магазине закончились бананы. Новую поставку смогут выложить в торговый зал через 1-2 дня. Бананы необходимо заблокировать на максимально маленький срок — после этого они точно будут доступны для клиентов и сборщиков.
Кейс №2: товары нужно блокировать на большой срок
У ретейлера B случился сбой в поставке яблочного сока. Возможно, новая партия доедет до магазина через 2-3 недели. Нет смысла блокировать товар на маленький срок, так как после такой блокировки клиенты все равно смогут заказать товар, но сборщик не сможет его собрать.
Со всеми эвристическими параметрами мы даем возможность алгоритму идентифицировать сезонные и аномальные ненайденные товары.
В первом кейсе с бананами блокировка произойдет только на HIDE_1 дней, после чего товар больше блокироваться не будет. Сок из второго кейса сперва заблокируется на HIDE_1 дней, а из-за повторных случаев ненайденных товаров заблокируется еще раз, но теперь уже на HIDE_2 дней.
Параметр HIDE_3 нужен для того, чтобы сохранять длительные блокировки. Возможно, сок из второго кейса появится не через 2-3 недели, а через месяц. В этом случае после блокировки на HIDE_2 дней сок заблокируется на HIDE_3 дней.
Параметр DAYS_NO_CANC нужен для того, чтобы повторно блокировать товары. Мы как бы ждем DAYS_NO_CANC дней, чтобы удостовериться, что с товаром действительно все хорошо и его не нужно блокировать повторно.
Сейчас мы уже понимаем факторы, которые влияют на доступность товара, и часто знаем о позициях в магазине больше, чем сам ретейлер.
СберМаркет генерирует огромные массивы данных и этим открывает возможности для экспериментов с подходами машинного обучения. Мы уже разрабатываем модель, которая будет предсказывать доступность товаров в магазине в нужный момент времени.
Чего вам не хватает в сервисах доставки продуктов или у нас? Делитесь в комментариях — подумаем над решением.