Использование Darts для оптимизации разработки анализа временных рядов Python
Введение
Прогнозирование временных рядов — уникальная область машинного обучения. При работе с временными рядами на самом деле существует неотъемлемая временная зависимость между различными точками ряда, и поэтому различные наблюдения сильно зависят друг от друга. Если вам интересно узнать больше об основах анализа временных рядов, дополнительные подробности можно найти в этой моей предыдущей статье.
В случае классических задач классификации и регрессии scikit-learn может предоставить большинство утилит, которые могут нам понадобиться для начала работы с хорошим базовым уровнем (например, предварительная обработка данных). -обработка, модели с низким кодом, метрики оценки и т. д.), хотя с временными рядами дело обстоит совсем по-другому. За прошедшие годы стало доступно множество специализированных библиотек, охватывающих некоторые ключевые этапы рабочего процесса анализа временных рядов (например, статистические модели, Prophet , индивидуальное тестирование на истории и т. д.), но до Дартс было невозможно охватить все в одном решении.
Демонстрация
В рамках этой статьи мы рассмотрим практическую демонстрацию того, как использовать Darts для анализа набора временных рядов Delhi Daily Climate от Kaggle [1]. Весь код, использованный в этой статье (и не только!) доступен на моем GitHub и учетных записях Kaggle.
Прежде всего, нам нужно убедиться, что Darts установлен в нашей среде.
pip install darts
Предварительная обработка данных
На данный момент мы готовы импортировать необходимые библиотеки и наборы данных (рис. 1). Чтобы облегчить наш анализ, столбец даты сначала преобразуется из строки в дату и время, а затем устанавливается в качестве индекса фрейма данных.
import numpy as np import pandas as pd from matplotlib import pyplot as plt import darts from darts.ad import QuantileDetector df = pd.read_csv('DailyDelhiClimateTrain.csv') df["date"] = pd.to_datetime(df["date"]) df = df.set_index('date') df.head(5)
После очистки набора данных мы теперь можем разделить его на обучающие и тестовые подмножества и визуализировать временные ряды (рис. 2). Для нашего анализа мы просто сосредоточимся на средней температуре на данный момент.
ts = darts.TimeSeries.from_series(df.meantemp) train, val = ts.split_before(0.75) train.plot(label="Training Data") val.plot(label="Validation Data")
Обнаружение аномалий
Из-за своей природы временные ряды обычно обрабатываются как часть сервисов реального времени или потоковых сервисов, хотя это может сделать их более восприимчивыми к неправильным измерениям и генерации выбросов. Чтобы отслеживать наши временные ряды на предмет возможных аномальных значений, можно использовать различные методы обнаружения аномалий. Два возможных подхода используют квантили или пороги. С помощью квантилей мы решили пометить верхний и нижний процент наших значений в ряду как выбросы, а с помощью порогов мы указываем фиксированные эталонные уровни, выше или ниже которых каждое значение помечается как аномалия.
В приведенном ниже примере рассмотрение всего, что ниже 3 % и выше 97 %, в качестве выбросов приводит к общему проценту значений вне квантилей, равному 5,8 % (рис. 3).
anomaly_detector = QuantileDetector(low_quantile=0.03, high_quantile=0.97) anomalies = anomaly_detector.fit_detect(ts) l = anomalies.pd_series().values print("Percentage of values outside quantiles:", round(sum(l)/len(l)*100, 3), "%") idx = pd.date_range(min(ts.pd_series().index), max(ts.pd_series().index)) anomalies = ts.pd_series()[np.array(l,dtype=bool)].reindex(idx, fill_value=np.nan) normal = ts.pd_series()[~np.array(l,dtype=bool)].reindex(idx, fill_value=np.nan) normal.plot(color="black", label="Normal") anomalies.plot(color="red", label="Anomalies")
Базовая модель
На данный момент мы готовы углубиться в наш временной ряд и проверить, присутствует ли в нем какая-либо форма сезонности. Как и ожидалось и показано в приведенном ниже фрагменте кода, похоже, что ряд статистически соответствует аналогичному сезонному шаблону примерно каждый год.
for m in range(2, 370): seasonal, period = darts.utils.statistics.check_seasonality(train, m=m, max_lag=400, alpha=0.05) if seasonal: print("Seasonality of order:", str(period)) Seasonality of order: 354 Seasonality of order: 356 Seasonality of order: 361
Имея эту информацию, мы можем обучить первую наивную базовую модель, которая просто учитывает сезонный характер в ряду и никакой другой информации (рис. 4). При таком подходе MAPE (средняя абсолютная ошибка в процентах) составляет 11,35%. Два основных преимущества использования MAPE в качестве нашей оценочной метрики:
- При использовании абсолютного значения положительные и отрицательные ошибки не компенсируются.
- Ошибки не зависят от масштабирования зависимой переменной.
k = 361 naive_model = darts.models.NaiveSeasonal(K=k) naive_model.fit(train) naive_forecast = naive_model.predict(len(val)) print("MAPE: ", darts.metrics.mape(ts, naive_forecast)) ts.plot(label="Actual") naive_forecast.plot(label="Naive Forecast (K=" + str(k) + ")")
Выбор статистических моделей
Имея хорошую базовую модель, мы готовы экспериментировать с некоторыми более продвинутыми методами (например, экспоненциальное сглаживание, ARIMA, AutoARIMA, Prophet). При необходимости многие дополнительные модели, такие как: CatBoost, Фильтры Калмана, случайные леса, рекуррентные нейронные сети и временные сверточные сети доступны как часть Darts.
def model_check(model): model.fit(train) forecast = model.predict(len(val)) print(str(model) + ", MAPE: ", darts.metrics.mape(ts, forecast)) return model exp_smoothing = model_check(darts.models.ExponentialSmoothing()) arima = model_check(darts.models.ARIMA()) auto_arima = model_check(darts.models.AutoARIMA()) prophet = model_check(darts.models.Prophet()) ExponentialSmoothing(), MAPE: 37.758 ARIMA(12, 1, 0), MAPE: 41.819 Auto-ARIMA, MAPE: 32.594 Prophet, MAPE: 9.794
Учитывая приведенные выше результаты, Prophet кажется наиболее многообещающей из моделей, рассмотренных до сих пор в рамках анализа. В любом случае, при некоторой дополнительной работе результаты можно даже улучшить с помощью оптимизации гиперпараметров, особенно за счет использования преимуществ знаний в области бизнеса с традиционными статистическими моделями, такими как ARIMA и экспоненциальное сглаживание. Дополнительные сведения о том, как работает ARIMA, и его различных гиперпараметрах можно найти здесь.
Тестирование на истории
Чтобы еще больше подтвердить качество нашей модели, теперь мы можем протестировать ее, воспроизведя ее с использованием доступных исторических данных (рис. 5). В этом случае регистрируется MAPE 7,8%.
historical_fcast = prophet.historical_forecasts(ts, start=0.6, forecast_horizon=30, verbose=True) print("MAPE: ", darts.metrics.mape(ts, historical_fcast)) ts.plot(label="Actual") historical_fcast.plot(label="Backtest 30 days ahead forecast")
Ковариационный анализ
Чтобы завершить наш анализ, теперь мы можем проверить, может ли использование информации, хранящейся в других столбцах набора данных, таких как влажность и скорость ветра, помочь нам создать более эффективную модель. Есть 2 основных типа ковариат: прошлое и будущее. С прошлыми ковариатами во время прогнозирования доступны только прошлые значения, вместо этого с будущими ковариатами также доступны будущие значения во время прогнозирования.
В этом примере используется модель N-BEATS (временной ряд нейронного анализа расширения) со столбцами влажности и скорости ветра, используемыми в качестве прошлых ковариат (рис. 6).
humidity = darts.TimeSeries.from_series(df.humidity) wind_speed = darts.TimeSeries.from_series(df.wind_speed) cov_model = darts.models.NBEATSModel(input_chunk_length=361, output_chunk_length=len(val)) cov_model.fit(train, past_covariates=humidity.stack(wind_speed)) cov_forecast = cov_model.predict(len(val), past_covariates=humidity.stack(wind_speed)) print("MAPE: ", darts.metrics.mape(ts, cov_forecast)) ts.plot(label="Actual") cov_forecast.plot(label="Covariate Forecast")
В результате тренировочного процесса зарегистрирован показатель MAPE, равный 10,9%, что в данном случае уступает нашей исходной модели Prophet.
Контакты
Если вы хотите быть в курсе моих последних статей и проектов, подпишитесь на меня на Medium и подпишитесь на мой список рассылки. Вот некоторые из моих контактных данных:
- Линкедин
- "Персональный сайт"
- Средний профиль
- Гитхаб
- Каггл
Библиография
[1] Временные ряды ежедневных климатических данных (SUMANTHVRAO, Лицензия CC0: Public Domain). Доступ по адресу: https://www.kaggle.com/datasets/sumanthvrao/daily-climate-time-series-data?select=DailyDelhiClimateTrain.csv