Разбиение на обучающую и тестовую выборку без учета времени — распространенная ошибка, про которую все уже знают. Правильное разбиение может привести к неочевидным проблемам, которые мы обнаружим и решим.
Рассмотрим небольшой пример. Нам нужно спрогнозировать выручку по магазинам на следующий месяц, признаки — агрегаты выручки в прошлом, доступны данные за год. Модель специально выберем стандартную, градиентный бустинг над решающими деревьями. Для наглядности оценивать качество будем по средней относительной ошибке MAPE.
Разобьем данные на обучающую(train) и тестовую(test) выборки случайно, обучим модель и оценим результат. Кросс-валидацию и валидационную выборку упускаем для простоты.
Подход со случайным разбиением дает ошибку на test в 12%. Возможно этого хватит, но в продакшене модель обученная на прошлом будет прогнозировать будущую выручку. В случайном разбиении это не учтено.
Сделаем разбиение по времени, теперь в test последние 3 месяца. Всегда стоит разобраться, как будет использоваться модель и выбрать стратегию валидации, повторяющую продакшн.
Обучаем новую модель, MAPE выросла с 12% до 24%! Более того, ошибка растет с каждым месяцем.
В отчетах или на демо 12% выглядят лучше, чем 24%, но в проде модель будет работать с ошибкой в 24%. Оценивая модель некорректно, вы можете неприятно удивиться результатам АБ-теста или приемки.
Что произошло?
Мы столкнулись со смещением данных или dataset shift. Мир со временем меняется, и эти изменения отражаются в данных. Обученная на прошлом модель ничего о них не знает. Пытаясь обучиться на train, модель начинает все сильнее ошибаться test.
Скрытый текст
Смещение можно принять за переобучение. В нашем примере ошибка на test 24%, а на train 6%. Предотвращение переобучения повысит метрики, но не решит проблему.
Что это значит для нас:
Большая ошибка прогноза. 24% Может быть достаточно, но можно меньше.
Быстрая деградация модели. Без регулярного переобучения 19% в первый месяц быстро превратятся в 30%.
Сложно обучить модель. Изменение гиперпараметров и работа с данными слабо влияют на метрики качества.
Обнаружим смещение
Есть 3 причины смещения и все они могут возникнуть одновременно:
Covariate shift: Разное распределение признаков в train и test.
Prior probability shift или label shift: Разное распределение целевой переменной.
Concept drift: Изменилась зависимость между признаками и целевой переменной.
Несколько методов обнаружения смещения, пойдем от простого к сложному:
Проверить изменение целевой переменной и признаков во времени глазами. Иногда можно заметить очевидные аномалии и тренды.
Сравнить распределения признаков и целевой переменной в train и test, лучше с помощью стат. тестов. Этот метод обнаруживает смещение в отдельных признаках, но не учитывает их взаимодействие.
Построить классификатор отличающий train и test. Объединяем train и test выборки, признаки из исходного датасета, а целевая переменная - принадлежность к train или test. Чем выше качество классификатора, тем сильнее смещение. Взаимодействие признаков уже учитывается, по feature importance можно определить смещенные признаки.
Теперь разберемся что происходит у нас. Не обязательно сразу бросаться в подготовленные датасеты, общую картину можно быстро понять по уже готовым отчетам и дэшбордам, особенно это выручает когда ваши данные еще не готовы. Откроем отчет с показателями по нашей компании и посмотрим на выручку.
Благодаря общим усилиям, выручка в магазинах растет и продолжает расти, есть тренд. Посмотрим на целевую переменную в наших данных.
То же самое. Логично, это тот же показатель, что и на предыдущем графике. Более того, наши признаки подсчитаны из выручки за прошлые месяцы, тренд есть и в них. Причина смещения — тренд, который модель не может учесть. Разберемся, как он влияет на модель:
Меняется распределение признаков, посчитанных из истории выручки. Выученные сплиты решающих деревьев начинают работать хуже.
Меняется распределение целевой переменной. Решающее дерево не умеет экстраполировать, модель никогда не видела таких больших значений выручки и не может их спрогнозировать.
В прошлом тренда не было или он был другим. У нас мало данных с нужными зависимостями.
В реальных задачах все будет сложнее: появляются новые категории объектов, различные акции и промо или меняется методология сбора данных и расчета метрик.
Скрытый текст
Несколько примеров из жизни.
Работая над проектом из нефтяной промышленности, заметили, что с определенного момента данных становится значительно больше и падает качество. Выяснилось, что у месторождения сменился владелец. Бурить начали активнее и по-другому собирать показатели.
Другой случай возник при работе над системой динамического ценообразования. Прямо перед запуском в метриках продукта появился тренд, как в нашем примере. Построенные модели с ним не справлялись, помогло удаление тренда и другие методы.
Устраним смещение
Если коротко, то нам нужно сделать train и test похожими. Несколько простых методов:
Удаление наиболее различающихся признаков. Может помочь, но как правило смещение не содержится только в каких-то конкретных признаках, к тому же теряем ценную информацию.
Oversampling или undersampling. Насэмплим данные в train, чтобы распределения в train и test были похожими. Помогает в случае label shift.
Преобразование признаков и целевой переменной: переход к относительными значениям, масштабирование данных, объединение или разделение категорий, удаление тренда и т.д.
Удаление данных. Например, если в test или train есть аномалии и вы уверены, что модель не должна их обрабатывать, можно их удалить.
Также можно воспользоваться техниками из transfer learning или собрать больше данных.
А теперь удалим тренд. Из каждого значения выручки вычтем предыдущее значение, пересчитаем признаки. Модель будет прогнозировать не выручку, а изменение выручки относительно предыдущего месяца.
Для оценки качества обязательно выполним обратное преобразование: прибавим к прогнозу модели выручку за предыдущий месяц. Теперь ошибка 7% и не растет во времени! Мы не только уменьшили MAPE c 24% до 7%, но и сделали нашу модель надежнее. Если тренд продолжится, модель будет работать корректно.
На практике полностью удалять смещение не нужно. Как только качество устраивает, заканчиваем. Если есть сомнения, проверьте, что разница в ошибках стат. значимая.
Итог
Разбивая датасет по времени, можно столкнуться со смещением. Обнаружить и устранить смещение непросто, но в результате вы получите надежную и точную модель. Вложенные усилия обязательно окупятся в продакшене.
Что мы узнали:
Важно правильно выбрать стратегию валидации. Качество может отличаться в разы, а узнаете вы об этом только в проде.
Всегда стоит проверить, как данные изменяются во времени. Хотя бы по основным показателям и целевой переменной.
Удаление смещения повышает качество модели и делает ее надежнее.
Расскажите в комментариях о своих случаях смещения, некорректных или необычных способах валидации. Буду рад любым дополнениям и уточнениям.
Полезные ссылки:
Подробнее о смещении можно прочесть в книге Shimodaira, H. Dataset Shift in Machine Learning. Neural Information Processing Series, Yale University Press, CAMBRIDGE 2009
Комментарии (7)
Ivstrek
27.10.2024 14:09Временной ряд так не прогнозируется потому что такие прогнозы не учитывают причин по которым формируются данные
imageman
27.10.2024 14:09Прочитал у вас "не умеет экстраполировать", так это [практически] любой метод так. Методы хорошо умеют интерполировать, а экстраполяцию как повезёт. А узнать про изменение данных нам помогут методы поиска аномалий (тут на хабре статей много, сходу самую нужную не нашел). На обучающих данных учим любимый метод поиска аномалий, потом проверяем этим методом все данные (в проде). Как только частота срабатываний ощутимо поменялась, так заново весь цикл обучения модели.
Ev_V Автор
27.10.2024 14:09Привет! В приведенном примере рассчитывать на экстраполяцию точно не стоит.
Оценивать изменения через поиск аномалий хорошая идея и в проде применять удобно, спасибо. На этапе обучения, когда все данные доступны, может быть проще воспользоваться описанными методами, также они могут дать больше информации о смещении.
lazy_val
Краткое содержание:
Если мы хотим экстраполировать временной ряд, нельзя для расчета коэффициентов модели (обучения) брать отдельные отсчеты (точки) наугад, надо взять все отсчеты внутри непрерывного отрезка
Спасибо, очень познавательно.
P.S. Для обучения можно также взять 80% отсчетов в конце общей выборки, и проверить их на 20% отсчетов, находящихся в начале.
P.P.S. Зачем вам вообще для решения этой задачи градиентный бустинг? Авторегрессионной модели скользящего среднего (АР-СС, она же ARIMA) выше крыши хватило бы
imageman
Для обучения можно также взять 80% отсчетов в конце общей выборки
Можно пойти дальше. Подобрать оптимальные гиперпараметры обучения на выборке 80+20 (или даже 70+30), а потом с этими гиперпараметрами обучить на всех 100 (надеясь что точность повысится и не будет переобучения). Хотя, думаю, вы и сами это знаете.
lazy_val
Чтобы оценить "глубину" рядов AR и MA обычно применяют частичную и полную АКФ соответственно, и да, никто не мешает нам взять для этого всю выборку целиком
И применение частичной АКФ позволяет устранить влияние смещения данных
Без всяких бустингов
Ev_V Автор
Привет! Спасибо за дополнение. Действительно, можно взять данные из непрерывного отрезка, например из начала. На практике, без дополнительных вводных, я бы так делать не стал. Лучше использовать самые поздние данные из конца выборки, так как они больше похожи на текущие данные в проде(это стоит проверить) и это соответствует сценарию применения модели.
В приведенном простом примере действительно хватит авторегрессионной модели, но чаще используют бустинг. Показать на его примере, я считаю, полезнее. Все-таки смещение возникает в самых разных задачах, а данная задача выбрана из-за наглядности.