Выкупят/не выкупят: наш ML-пилот в «Утконосе»

    В этом посте речь пойдет про пилотное ML-исследование для онлайн-гипермаркета «Утконос», где мы прогнозировали выкуп скоропортящихся товаров. При этом мы учли данные не только по остаткам на складе, но и производственный календарь с выходными и праздниками и даже погоду (жара, снег, дождь и град нипочем только «Taft’у Три погоды», но не покупателям). Теперь мы знаем, например, что «загадочная русская душа» особенно жаждет мяса по субботам, а белые яйца ценит выше коричневых. Но обо всем по порядку.



    Пилот в ритейле больше чем пилот


    В ритейле машинное обучение находится в двойственном положении. С одной стороны, ритейлеры накопили внушительные объемы данных за весьма впечатляющие промежутки времени: отдельные чеки покупок, данные с карт лояльности… С другой — ритейл существует настолько давно, что задачу прогнозирования спроса начал решать задолго до появления моды на data science и сегодня имеет в распоряжении необходимые BI-инструменты.
    Получается, что ритейл — одно из наиболее перспективных полей для экспериментов датасайнтистов и внедрения машинного обучения, но бизнес смотрит на всё это со скепсисом: точно ли это хорошо для меня? Ведь уже есть работающие, проверенные многолетним опытом решения.
    И вот тут самое время договориться о пилотном исследовании!
    Сами по себе пилоты по сравнению с полноценным ML-проектом имеют понятные ограничения и специфику.


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

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


    Немного о задаче


    Задолго до знакомства с машинным обучением «Утконос» уже использовал собственную аналитическую систему, которая с весьма высокой точностью прогнозировала выкуп товаров на неделю. Тем не менее, ритейлера интересует возможность повышения эффективности планирования. В первую очередь это касалось скоропортящихся продуктов, многие из которых еще и весьма недешевы. Традиционная вилка: купишь много — будут убытки, купишь недостаточно — покупатель уйдет за своей любимой телячьей вырезкой, заготовленной при полной луне и слегка моросящем дожде, к конкуренту. Для достаточно точного прогноза на послезавтра лучше подходят решения на базе машинного обучения, позволяющие учитывать большее число факторов, чем классические BI-инструменты. «Утконос» согласился выступить нашим партнером для эксперимента, направленного на проверку гипотез применимости Machine Learning для e-commerce.


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


    • два товара из категории «Охлажденное мясо» — как скоропортящиеся продукты, данные по которым наиболее важно обновлять оперативно;
    • и два товара из категории «Яйцо куриное» — как товары со специфическим сезонным спросом, которые нельзя спрогнозировать просто как «в четверг все покупают X, а в пятницу — X умножить на коэффициент». Хотя куриные яйца не являются сложными для прогнозирования выкупа и как раз для них вполне приемлем недельный горизонт планирования, именно на этих товарах предстояло показать, что машинное обучение действительно видит сложные взаимосвязи и строит нетривиальный прогноз.

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


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


    Нас ожидало короткое, но увлекательное приключение.


    Данные со складов: загадочные и необходимые


    При работе с историческими данными первый же вопрос, который перед нами встал, — как отделить реальные продажи от «максимально доступных продаж» (т.е. случаев, когда заканчивающийся на складе товар был выкуплен на 100%, однако при его наличии объем продаж мог быть выше)? Такие неосуществленные желания покупателей ведь никак не отображаются в данных.
    Доступность товаров на складе. К слову, по опыту предыдущих проектов в ритейле, мы ожидали, что это будут остатки, выраженные в единицах измерения. Однако в данном случае мы имели дело с относительным показателем «доступность», которая измерялась в процентах в течение дня. Что же касается наличия товара на складе, то этот показатель весьма относителен: то, что в какой-то промежуток товара не было, вовсе не означало, что его хотели купить.
    Поэкспериментировав с разными вариантами (реконструкцией «действительного спроса» на основании по-разному рассчитанных коэффициентов и фильтрацией датасета продаж с разными порогами доступности), мы в итоге подобрали оптимальный порог, который не делал набор данных слишком узким. Идеал — наличие товара в течение всего дня — заметно сокращал данные даже для самых продаваемых товаров.


    Товар 1: охлажденное мясо (необычной птицы)


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


    В целях экономии времени «из коробки» достали готовую библиотеку, хорошо работающую с временными рядами, — Prophet от Facebook.



    Результаты работы модели на обучающих данных сразу показывают и достоинства, и недостатки. Модель хорошо улавливает «сезонность» спроса, но плохо — пики покупок. Также праздники, зашитые в Prophet по умолчанию. Относительное отклонение составляет 31.36%, будем его дальше использовать как базовый результат.


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



    У нашего охлажденного мяса четкий возрастающий тренд на общее число покупок, число покупок растет от понедельника к субботе и падает в воскресенье, летом покупки заметно «проседают». Плохо, что лето не попадает в наш тестовый период; с другой стороны — запомним, что период каникул и отпусков важен для уровня продаж, ведь летние каникулы — в России далеко не единственные.
    Логичный вопрос: можно ли использовать эту модель сразу для прогноза на следующие полгода?


    Интуитивно кажется, что нет. Эксперимент показал, что так и есть. Общий рисунок сезонности в течение недели верный. Но сразу стало очевидно, что от общего рисунка сезонности есть миллион отклонений как вверх, так и вниз, а среднее отклонение 45.71 % сильно превышает результаты на обучающих данных. Понятно, что это никуда не годится.


    Для начала попробуем обучать модель ежедневно, представив, что каждый день после завершения работы магазина датасет дополняется продажами за «сегодня». Мы уже знаем, что в продажах есть восходящий тренд в целом — возможно, что оборот на наших тестовых данных растет с большей интенсивностью за счет более активной маркетинговой активности, чем было на обучающем наборе.
    Относительный успех: при ежедневном дообучении модели относительное отклонение составляет 33.79%. Мы дополнили параметры модели информацией о перенесенных выходных, религиозных постах и праздниках, традиционных для России (таких как Новый год, Пасха и ряд других). Также были добавлены резкие изменения погоды: дни, когда температура скакала вверх или вниз на 10+ градусов или просто была заметно выше или ниже других дней этого месяца. Теперь в среднем за полгода наш прогноз отклонялся от реальных продаж на 28.48%, а в целом модель начала лучше учитывать всплески покупательской активности. Мы улучшили среднее отклонение на пять процентов! При том, что с выбросами Prophet в принципе работает плохо и данные рекомендуется от них очищать, — это было заметное движение вперед.




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



    Коэффициент корреляции между средней ценой за единицу в день и число проданного объема этого вида охлажденного мяса составил ¬–0.61 при p < 0.01. Понятно, что «средняя цена за единицу» — не идеальный показатель: в том случае, если в течение дня было много покупок от, скажем, партнеров с постоянной большой скидкой, в данные закрадутся опасные шумы. Мы же хотели выделить дни, когда были маркетинговые воздействия: общие скидки на группу товаров, скидки всем, кто введет свободно распространявшийся промокод и т.п.


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


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



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


    Товар 2: яйцо куриное


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



    В целом наша модель ожидает, что некоторое повышение спроса в Пасху будет, но ее прогноз меньше реального показателя почти в 2 раза (и это отклонение в ~100% во время пасхальной недели делает среднее отклонение за полгода невероятно большим). Почему? Ведь пасхальная неделя случается ежегодно — в данных предыдущих 2 лет должен быть паттерн!


    Исследовательский анализ показал, что паттерна нет. В 2018 году (это наши тестовые данные) пик покупок приходится на всю неделю перед Пасхой вплоть до 7 апреля. В саму Пасху (8 апреля в 2018) покупки яиц всегда падают, что совершенно корректно видит модель. А вот в 2017 году Пасха приходится на 16 апреля, а пик покупок в исторических данных — 8 апреля, и в этом году пик — один день. В 2016 году Пасха приходится на 1 мая. Пик покупок — 29 апреля, с подъемами на день до и день после. В 2015 году Пасха приходится на 12 апреля, пик покупок снова один день, 9 апреля.



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


    Сможем лучше!


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


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


    В набор данных о «внешнем мире» была собрана следующая информация:


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

    Отдельно были добавлены признаки проведения отдельных маркетинговых акций и цена за единицу товара.


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



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



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


    Александра Царева, специалист отдела интеллектуального анализа «Инфосистемы Джет»

    Инфосистемы Джет
    739,00
    Системный интегратор
    Поделиться публикацией

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

      0
      Прошу прощения, а не из-за этого нововведения, примерно начиная с Нового Года, из уже сформированной и заказанной (а, иногда, и предоплаченной) корзины «пропадают» товары с формулировкой: «К сожалению, не будет доставлен следующий товар: xxx»?
        0
        Система, для которой мы сделали пилотное исследование, как раз должна так прогнозировать спрос, чтобы товары были на складе всегда в оптимальном количестве.
        Как и что устроено в системе заказа у магазина сейчас, к сожалению, никак не могу прокомментировать.
          0
          В случае «пропажи» важна синхронизация сайта с бд склада, а не столько наличие товара на складе.
        +1
        Странновато выглядит график с продажами в день прогноза, как топовой фичей. Вы уверены, что у вас модель не «протекала»? А то выглядит, будто вы частично на ответе обучались.
        И еще вопрос — а почему не исключить сезонность и тренд из данных простой моделью, а уже по остаткам от нее пройтись RandomForest'ом? Он же тренд не сможет поймать.
          0
          Я не думаю, что там есть утечка. В рамках имитации обучения «на послезавтра» условное «будущее» модели не показывалось. Плюс исключение фичи «продажи в день прогноза» делает прогноз более точным на некоторых временных промежутках (но на некоторых нет). В основном она помогает в периоды, когда активно применяются маркетинговые акции: заметное увеличение проданного сегодня может указывать на сместившийся спрос с послезавтра.

          Тренд на общее возрастание числа продаж мы постарались учесть при генерации фич для RandomForest'а, добавив туда в том числе общие продажи по товару за предыдущие периоды от дня прогноза.
            0
            почему лес не сможет отследить тренд?
          +2
          При всех шероховатостях, вы все равно молодцы, что пытаетесь идти по трудному пути!

          1. Пилотом обычно называют все же тестовое внедрение, а у Вас я из текста так и не понял, пошел ли Ваш прогноз в заказ. Это очень важно, тк моментально дает Вам кучу фидбека, гордости за себя и тикетов для работы ;)
          2. Не указан горизонт и уровень прогнозирования, а это очень важно, для понимания цифр. Одно дело, прогнозировать ближайшую неделю и суммарно товарную группу, другое — один товар на пару месяцев вперед.
          3. Касаемо фичи «продажи в день прогноза», если прогноз на будущее. Обычно делают каскад фич: продажи предыдущего дня, продажи предыдущего того же дня недели (пн к пн и т.д.), месяца, года. Не уверен, что здесь это сработает, но все же. И тогда прогноз на много дней надо запускать итеративно, на дальнейших днях либо подменяя факт прогнозом, либо используя модель без фичи.
          4. Не очень понятно, подавали ли флаги дней недели в RF. Банально, логарифмировали ли целевую переменную. Дни недели должны быть в топе!
          5. Такое чувство, что фичу «дней до Курбан-байрам» RF просто использовал как некий идентификатор дня или восходящий тренд.

          С тз индикативности, хотелось бы увидеть прогноз по каким-нибудь шоколадкам, кофе или бытовой химии. Они куда лучше должны показать качество модели, чем спокойное 45 недель в году куриное яйцо.
            0
            Спасибо за комментарии!
            1. У нас пилотным исследованием (пилотом) обычно называется именно исследование, до внедрения, о нем эта история. А дальнейшие действия заказчика комментировать не можем.
            2. Мы моделировали ежедневный прогноз (на один конкретный день — послезавтра) для одного товара (конкретный SKU, не группа).
            3. Да, у нас были некоторые из перечисленных вами фичей для RandomForest плюс некоторые другие (вроде изменения цены товара относительно этих периодов). Думаю, что можно наинженерить еще и подобрать оптимум, но в данном случае для нас было важно показать принципиальную возможность улучшения первых результатов.
            4. Флаг дня недели — это «день недели для продаж» (в смысле, что день, когда будут происходить продажи, а не когда мы строим прогноз), он пятая по важности фича для RandomForest.
            5. У нас было в числе фичей некоторое число других праздников как флагов и как дней, оставшихся до них (например, Пасха и Новый год), они тоже оказывали влияние для отдельных товаров, хотя и не вошли в топ-5. Скорее всего, это все же шум какой-то природы, но для того, чтобы установить это точно, — стоит еще поисследовать возможные фичи. Например, я бы проверила, нет ли такого, что от мая к концу лета мясо просто покупают больше/меньше благодаря выездам на шашлыки, а Курбан-байрам к концу лета близок.

            К сожалению, по разнообразию прогнозируемых SKU мы были ограничены как по постановке задачи и времени, так набором данных.
              0
              Спасибо за ответ по содержанию.
              По-моему, совсем немного недоработали, чтобы добить задачу и сделать более универсальное решение.
              Ваш вариант заточен на прогноз одного дня, в случае, если захотите прогнозировать даже завтра+послезавтра модель придется перестраивать основательно.
              В таком случае, топ критериев вполне интерпретируем.
              Во-первых, модель вполне разумно говорит, что продажи завтра очень похожи на вчера. Это логично, так она избавляется от влияния годовой сезонности и изменения ассортимента, в некотором смысле восстанавливает тренд.
              Во-вторых, модель строит коэффициент продажи в (день_недели_завтра / день_недели_вчера), вернее 7 таких коэффициентов. Во сколько раз продажи в среду выше таковых в понедельник, во сколько четверг выше вторника и т.д. Так она восстанавливает недельную сезонность.
              На самом деле, здесь уже сделано 95% работы, остается обработать исключительные ситуации.
              В третьих, учесть цену. В идеале, было бы подавать на вход скидку а не цену, результаты бы чуть улучшились.
              Ну и в-четвертых — праздники как еще неописанные всплески спроса. Их Ваша модель скорее всего видит через те самые дни до праздника.

              В принципе, можно было бы построить регрессию по данным с аккуратно заведенными фичами и получить аналитическую функцию прогноза, но Ваш вариант, особенно для пилота, вполне жизнеспособен. Было бы еще интересно проверить, нет ли подозрительных выбросов переобучения, приводящих к тому, что например при повышении цены в каком-то диапазоне растет спрос.
            0
            Даты пиков покупки яиц в 2015-2016гг. обусловлены тем, что яйца красят в «чистый четверг».

            Но результат в другие годы это не объясняет.
              +1
              Замечательная и образцовая статья по ML! Авторы большие молодцы. Удалось уйти от шаблонов и трескотни. Для того, чтобы статью можно было использовать как пример всем (сверх искусственным интеллектам и ML), хотелось бы видеть в конце оценку результатов в виде таблички. То есть сравнение ожидаемых результатов и полученных результатов. И снова — МОЛОДЦЫ! И Джет и Утконос реально (и скажу честно неожиданно) порадовали прежде всего пониманием и подбором правильных людей и идей.
                +1

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

                  0
                  С большим удовольствием почитаю про ограничения регрессора случайного леса в таких случаях, если вас не затруднит сослаться на материалы по теме! Пока известные мне материалы в основном касались ошибок при генерации фичей и предупреждали про опасность попадания части целевой переменной, которая находится выше по восходящему тренду и не имеет аналогов в тренировочном наборе, в тестовый сет.
                  По нашему опыту, для некоторых проектов после преобразования данных из time like в feature like случайный лес дает такие же или лучше результаты (вот просто для примера работа, где Random Forest сравнивается с ARIMA в прогнозировании вспышек гриппа). Про то, как лучше учитывать специфику временного ряда при генерации фич, написано тоже немало. Например, для модели из статьи учитывать возрастающий тренд оборота магазина помогают общие фичи продаж.
                0
                Некоторое время работал с системой прогноза спроса одного российского ритейлера, так что тема статьи мне во многом близка. Правда, там было совсем мало машинного обучения, в основном старая добрая эконометрика.
                Мы сталкивались с теми же самыми проблемами, что и вы. Спрос чрезвычайно чувствителен к внешним событиям, которые невозможно предсказать из данных по продажам. То есть с сезонностью ещё можно работать, а вот взмах руки коллеги из ценообразования, приводящий к кардинальному изменению спроса, предсказать просто напросто невозможно. И ладно бы, если этот взмах влиял только на один товар — но зачастую такие резкие перемены приводили к полному «переделу рынка» между разными производителями категории. На мой взгляд, невозможно добиться высокой точности даже по ходовым товарам, если отсутствует координация действий между ценообразованиями и прогнозистами. И нужно строить модели, учитывающие перекрестные эластичности спроса, иначе необъяснимые взлеты и падения будут возникать всегда.
                Касательно FB Prophe — мне кажется, или он у вас цепляет сезонность там где не нужно? Например, на последнем графике с яйцами, он похоже зацепил новогодний взлет и падение, и пытался их спрогнозировать по сезонности, хотя ясно, что период там совсем не подходящий. В этом, на мой взгляд, кроется опасность «черных ящиков» — никогда не знаешь, чего от них ждать.
                  0
                  Спасибо, что поделились своим опытом! Цена очень важна, не знаю, как бы мы работали без нее. Думаю, что учет витринных цен тоже добавил бы нам точности. Про перекрестный спрос подумали, но исключили из пилота ради того самого оптимального соотношения результата к затраченным усилиям, а так тема очень перспективная.

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

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

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