
Нельзя просто так взять и расставить электросамокаты в городе. Надо, чтобы они находились в нужное время, в нужном месте и в нужном количестве, чтобы выполнять свою транспортную задачу. Спрос на поездки в разных локациях неодинаковый, поэтому если поставить самокаты ровным слоем на улицах города — будет неэффективно. Нельзя также расставить их только в местах сильного пользовательского притяжения, забыв при этом про периферию.
Нужен хоббит алгоритм, который бы рассчитал, какое количество поездок можно ожидать на определенной парковке в определенный временной промежуток.
Меня зовут Никита Зеленский, я руковожу отделом по работе с данными в Whoosh, разработчике технологических решений и операторе микромобильности. Эту статью мы написали вместе с Иваном Маричевым, дата‑сайнтистом Whoosh. Он же и автор алгоритма, о котором пойдет речь.
Здесь мы расскажем, как мы реализовывали модель прогнозирования спроса на самокаты, с чем сталкивались при прототипировании, какие модели были протестированы, чем наш случай отличается от прогнозирования спроса в каршеринге, спроса для пополнения запасов в дарксторе и т. п. (Самокат, самокаты Whoosh передают привет!)
История получилась про наши подходы и грабли, которые мы в итоге собрали. Чуть‑чуть про технику, чуть‑чуть про бизнес — нескучно и с ветерком (как на самокате).
Whoosh!
Проблематика
Зачем менять устоявшуюся схему, разрабатывать ML‑модель, если можно все сделать в Экселе?
Основная причина — текущая ребалансировка (расстановка на парковках и перемещение по локациям) самокатов покрывала не весь пользовательский спрос. Иногда пользователям, чтобы взять в аренду самокат, приходилось идти на другую парковку, что порой неудобно и отнимает время. В долгосрочной перспективе это способствует оттоку клиентов, а наша задача — сделать микромобильный транспорт доступным и регулярным.
Вопрос — почему бы просто не выставлять сильно завышенное количество техники? Представим следующую картину: в среднестатистическом городе Х имеется 350–400 парковок (если не брать в расчет города‑миллионники). По каждой парковке мы собираем статистику: какое количество поездок пользователи на ней начали и закончили. Зная эти цифры, можно было бы оставлять на них количество самокатов, равное скользящему среднему от количества поездок за 7 дней + 20%? Но не все так просто:
Флот << прогноз. Маловероятно, что прогнозируемое таким образом количество техники будет возможно предоставить в рамках флота, доступного в городе. Даже если вы лидер отрасли и у вас большое количество самокатов в локации — это сильно неразумный способ, который замедлит интеграцию в новые регионы и районы.
Логистика. После появления такого количества флота на парковке, неизбежно вырастут расходы на ребалансировку, которые напрямую зависят от количества регулярно перевозимых самокатов.
Актуальность. Метод скользящего средн��го не сможет удовлетворять пиковый спрос внутри дня. Спрос на поездки зависит от времени суток, поэтому нам важно, чтобы наши самокаты были там, где они нужны в конкретный час: когда вы утром идете на работу, спрос выше у метро и остановок общественного транспорта, а вечером — наоборот, самокаты требуются преимущественно от работы до других локаций города.
Урбанистика. Отдельное «спасибо» можно ожидать от горожан и властей, которые явно не одобрят загромождение городских пешеходных зон большим количеством неиспользуемой техники.
Неэкономично. Выставление техники на парковки с запасом неизбежно приведет к простою самокатов, когда в теории они могут быть востребованы для поездок по другим маршрутам.
И это только самые очевидные причины, почему такой подход в долгосрочной перспективе не годится. Мы не отменяем важность решений на основании дедукции и опыта людей, которые работают в операционных центрах в городах нашего присутствия — у них есть понимание специфики конкретных локаций. Но с таким потоком данных и постоянно изменяющихся бизнес‑условий лучше справится машинный «мозг».
EDA
Ключевой этап при создание любой модели — сбор данных, а именно их чистота и репрезентативность, т. е. насколько наши данные корректны и содержат ли всю необходимую информацию (это тот случай, когда принцип GIGO актуален как никогда). Также необходимо быть готовым, что создание ML‑моделей — процесс крайне итеративный, и часто при оценке метрик моделей может потребоваться произвести новую выборку признаков или обогатить исходный датасет новыми данными.
Что мы имеем на входе?
Нерегулярность спроса: для кикшеринга характерен нерегулярный спрос. Влияет множество факторов: погода, праздники, пробки и т. п. Наши исторические данные по поездкам агрегированы по 30 минут — и при такой форме агрегации в данных не будет ни тенденции, ни дневной сезонности.


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


Пропуски в данных. Наш бизнес имеет определенную сезонную динамику. В большинстве городов мы работаем 9 месяцев: с марта по ноябрь. А анализируя временные ряды, такие пробелы между датами (с декабря по февраль) просто так не выкинешь — их необходимо правильно обрабатывать: подсказывать модели периоды завершения сезона и его начала, что было между этими датами, а также периоды, когда локация встает на кратковременную паузу. В противном случае, мы будем иметь «кривую» линию тренда с завышенным или заниженным показателями.
Также в ходе EDA было определено, что прогнозирование на все 100% парковок для города не имеет бизнес‑смысла, так как 40% «топовых» парковок генерировали 80% всех поездок города.
В качестве тестового города выступает город Сочи в связи с тем, что сезон аренды самокатов в городах южного округа продолжается круглогодично. Тем самым мы, конечно, немного проигнорируем п.3 из списка нюансов выше, но у нас другие цели для MVP:
Проверить работоспособность алгоритма (а это проще сделать без разрыва во временных рядах)
Протестировать его в реальных условиях в период межсезонья, чтобы принять решение о возможном масштабировании на все города.
Выбор модели
С вводными разобрались, теперь приступим к выбору обучающей модели. Как упоминалось ранее, парковок, и, соответственно, рядов для прогнозирования, много — десятки тысяч. Для нас скорость обучения важна не менее (если не более), чем точность самой модели, поэтому при принятии решения какую модель (или модели) выбирать, мы основывались на показателях RMSE и среднему времени, которое мы получаем при прогнозировании данных для n‑парковок. Мы протестировали модели SARIMAX, LightGBM, Prophet и NeuralProphet.
Как мы обучали модели, проводили валидацию, проверяли стационарность и сам код мы решили не писать — есть очень много подробных инструкций и примеров (например, тут); в этой части статьи расскажем про наши результаты и выводы.
SARIMAX — стандартная ARIMA (авторегрессионное интегрированное скользящее среднее) — наиболее популярный метод для однофакторного прогнозирования данных временных рядов. Недостатки ARIMA в том, что при построении прогноза учитываются только данные самого ряда, без внешних факторов‑регрессоров. Так как наши показатели спроса напрямую зависят от множества факторов (погода, праздники, пробки и т. д.), в нашей ситуации алгоритм не будет работать.
SARIMAX же является расширением ARIMA с добавленными алгоритмами обработки сезонности и экзогенных переменных — регрессоров (используемые гиперпараметры — SARIMAX(3, 0, 2)x(2, 1, 1)).

Плюсы:
Высокая точность уже «из коробки», учитывая доверительный интервал прогноза;
Развёрнутая документация
Из всех моделей прогнозирования временных рядов она является наиболее интерпретируемой, т.к. имеет четкое математическое обоснование.
Минусы:
долгий подбор гиперпараметров (около 10 минут на парковку).
LightGBM — реализация градиентного бустинга за авторством Microsoft. По опыту примения бустинговых моделей (чаще всего в регрессионных задачах, хотя и в двоичной\множественной классификации алгоритм также хорош), LightGBM — явный фаворит в связи с более высокой (x1.5 — x2) скоростью обучения при практически идентичной точности (разница на уровне погрешности ± 1%) в сравнении с XGBoost, CatBoost, etc.
Плюсы:
Высокая скорость обучения.
Большой набор гиперпараметров.
Менее ресурсоемко по сравнению с аналогами.
Возможность параллельного и GPU-обучения.
Минусы:
К сожалению, мы получили переобучение. Даже при автоматизированном подборе гиперпараметров, на тесте так же отсутствует предсказательная способность. Решили не закапываться и поискать возможные альтернативы.


Prophet — относительно молодая библиотека прогнозирования от “ЛицоКниги”. Prophet является расширением авторегрессионных моделей, которое позволяет использовать не только лаговые значения целевой переменной, но и дополнительные признаки (реализовано через применение ряда Фурье к таргету), т.е. позволяет использовать дополнительные переменные — регрессоры. Для нас это было особенно актуально, так как сами лаговые значения таргета не обеспечивают точного прогноза, и требуются дополнительные независимые переменные.
Prophet имеет ряд других полезных функций, например:
Высокая точность и скорость обучения “из коробки”.
Удобно реализованы базовые функции (в частности, добавления регрессоров).
Отслеживание изменений тенденции.
Функционал создания пула дат со значениями-аутлаерами (праздниками, спортивными событиями), которые исключались из построения общего тренда + сезонности.
Поддерживается и регулярно обновляется разработчиками.
И пара минусов:
По умолчанию нет настроек нормализации временного ряда и сопутствующих регрессоров.
Пропуски\аутлаеры в данных лучше обработать вручную.

NeuralProphet ‑ модификация базовой библиотеки Prophet с добавлением нейронного слоя. По сравнению с Prophet бэкэнд переехал со Stan на PyTorch. Идея использовать Prophet, обладающий высокими результатами (особенно после тюнинга) по функциям потери\времени, и при этом добавить однослойную нейронную сеть для теоретического улучшения точности показалась нам крайне интересной, после чего мы построили модель уже на NeuralProphet. API у библиотек практически идентичны, как и почти все ключевые функции, поэтому технически реализовать было несложно.

Результаты экспериментов с моделью NeuralProphet
Из плюсов можно выделить:
Хороший уровень точности «из коробки»
В библиотеку интегрирован слой pyTorch, благодаря которому реализован подбор параметров через градиентный спуск
Автоматическая предобработка данных (заполнение пропусков, нормализация и т. д.)
Минусы:
Низкая скорость обучения, связанная с профилированием DL‑настроек (количество эпох, learning rate, etc) для каждой парковки в индивидуальности
Что получили на выходе?
Prophet | NeuralProphet | LightGBM | SARIMAX | |
RMSE | 1.3 | 2.19 | 13.23 | 1.43 |
total_time (min) | 5.59 | 32.20 | 2.33 | 58.20 |
Табл.1 сравнения метрики RMSE и времени выполнения алгоритмов на n‑парковок.
Не трудно догадаться, что из всего зоопарка моделей нами в качестве рабочей был выбран Prophet. Связано это в первую очередь с простотой настройки модели и ее гипер‑параметров, удобным добавлением регрессоров и достаточной гибкостью при работе с различными структурами временных рядов.
Разумеется, как бы хороша библиотека не была, чуда «по умолчанию» не произошло, и прежде чем Prophet начал демонстрировать приемлемые результаты предстояло решить ряд проблем. Например, данные по температуре меньше -1°C модель принимала как NaN и пришлось видоизменять датасет с погодой под этот баг. На старте прототипирования у нас были сложности с прогнозированием пиков, так как к дневной сезонности прибавлялись естественные шумы. Решить эту проблему получилось путем добавлением регрессоров с высокой корреляцией к таргету, в первую очередь, связанные с погодными метриками и пользовательской активностью.
Большие надежды также возлагались на NeuralProphet, так как библиотека позиционируется как «быстрее‑выше‑сильнее Prophet во всем», однако в нашем случае это оказалось не совсем верно. Небольшую прибавку в точности (+3–5%) получалась только путем значительного увеличения количества эпох, как следствие — всего времени обучения в 2 — 2,5 раза.
В случае с топовыми и дефицитными парковками Prophet позволил умеренно‑эффективно прогнозировать данные не только там, где присутствует явная структура (тренд + сезонность), но и там, где спрос не обладает устойчивой структурой.


Общая модель ребаланса
Необходимо учитывать, что парковки обладают различной структурой не только в плане спроса («топовые» и «слабо востребованные»), но и в плане ориентированности.
Часть парковок являются кольцевыми, то есть начало поездки и ее окончание — это одна и та же парковка. Как правило, это актуально для парковок возле парков или других крупных точек притяжения. Другая часть парковок являются однонаправленными — с парковки часто стартуют, и редко финишируют (или наоборот). Это обычная картина для парковок с большим количеством близлежащих жилых домов. Что это нам дает? Это значит, что старты на отдельной взятой парковке могут быть распределены нормально, а вот финиши — нет.
Искушенный аналитик спросит — для чего вообще рассчитывать финиши, если они, в отличие от стартов, не аккумулируют прибыль и их нет необходимости как‑либо обеспечивать со стороны ребаланса?
Мы научились с минимальной погрешностью прогнозировать количество стартов на несколько тысяч парковок. Но в нашем случае это лишь подзадача в более масштабном проекте — спроектировать алгоритм ребаланса, который бы выполнял роль планировщика для сотрудников операционных центров, прямо указывая, к какому промежутку времени какое количество самокатов необходимо будет подвести, чтобы покрыть потребительский спрос. Одного верного предсказанного количества стартов в данном случае будет недостаточно, так как в конечном виде потребность в ребалансе парковки — функция из несколький переменных.
Берем последнее историческое наблюдение количества самокатов (scooters_last).
Прогнозируем количество стартов на ближайшие X‑часов (starts_forecast).
Прогнозируем количество финишей на тоже количество X‑часов (finishes_forecast).
Необходимый флот на парковку = scooters_last - starts_forecast + finishes_forecast

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

где:
ds — время, к которому подвезти самокаты,
parking_id — номер парковки,
rebalance — фактическое значение ребаланса (положительное — профицит, отрицательное — дефицит),
class_ — тип парковки, требуется для приоритизации парковок для сервисных центров.
Вывод
В статье кратко рассказали, как мы реализовывали алгоритм расчета ребаланса, основная цель которого — оптимизировать человеко‑часы и облегчить жизнь нашим сервисным командам, которые обеспечивают пользователей самокатами.
Разумеется, это далеко не весь список задач, в который у нас внедрен ML: здесь и классификации (в том числе и гео‑классификации по индексу Морана), и регрессионные модели расчета целевых бизнес метрик, и многое другое.
Если статья показалась вам интересной, и вы хотите продолжения — пишите в комментариях, мы с удовольствием ответим на вопросы по теме.
Уважайте пешеходов, водите осторожно и до встречи на улицах.
Whoosh!
