Методология LSTM, представленная в конце 90-х годов, лишь недавно стала жизнеспособной и мощной техникой прогнозирования. В этой статье мы собираемся использовать LSTM RNN для набора данных временного ряда Rossman Pharmaceutical, чтобы прогнозировать продажи для реальной бизнес-задачи, взятой из Kaggle.

Этот блог охватывает следующие моменты:

1. Постановка проблемы

2. Обзор данных и исследовательский анализ данных

3. Прогнозирование с помощью алгоритма машинного обучения

4. Использование модели LSTM For Forecasting and Building с глубоким обучением

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

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

Данные

Файлы
train.csv — исторические данные, включая данные о продажах
test.csv — исторические данные, исключая данные о продажах
sample_submission.csv — пример файла отправки в правильном формат
store.csv — дополнительная информация о магазинах

Описания полей подробнее объясняются здесь.

Теперь давайте выполним EDA, чтобы получить представление о данных.

Информация о train.csv следующая:

Информация о store.csv следующая:

Здесь мы заметили, что на train.csv у нас около 1 миллиона точек данных. Здесь наша целевая переменная — «Продажи и клиенты». На store.csv всего 1115 уникальных магазинов. И многие столбцы здесь имеют нулевые значения. Мы позаботимся о них через некоторое время.

Изучим данные с помощью графиков

Во-первых, давайте проверим и сравним поведение продаж до, во время и после праздников.

давайте начнем с рождественских каникул

def Christmas_Season_Sales(year:str,nextyear:str):
 before_dates = (df["Date"] >= pd.to_datetime(f"{year}-12-10")) (df["Date"] < pd.to_datetime(f"{year}-12-25"))
 before_xmas =df[before_dates].groupby("Date").agg({"Sales":"mean"})
 during_dates = (df["Date"] >= pd.to_datetime(f"{year}-12-25")) & (df["Date"] < pd.to_datetime(f"{year}-12-31"))
 during_xmas = df[during_dates].groupby("Date").agg({"Sales": "mean"})
 after_dates = (df["Date"] >= pd.to_datetime(f"{nextyear}-01-01")) & (df["Date"] < pd.to_datetime(f"{nextyear}-01-16"))
 after_xmas = df[after_dates].groupby("Date").agg({"Sales": "mean"})
 return before_xmas, during_xmas, after_xmas

Далее мы построим разницу

sns.lineplot(
x=before_holiday.index, y=before_holiday["Sales"], label="Before", color="blue")
sns.lineplot(
x=during_holiday.index, y=during_holiday["Sales"], label="During", color="red")
sns.lineplot(
x=after_holiday.index, y=after_holiday["Sales"], label="After", color="green")
plt.title(f"{title}", size=20)
plt.xticks(rotation=90, fontsize=14)
plt.yticks(fontsize=14)
plt.xlabel(xlabel="Date", fontsize=16)
plt.ylabel(ylabel="Avg Sales", fontsize=16)
plt.legend(loc="upper left", fontsize=16)
plt.show()

Мы применили ту же реализацию для праздника Пасхи, и результат был следующим:

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

Далее мы увидим влияние промо на продажи.

Здесь мы заметили, что предел промо-эффекта в магазине типа B намного меньше, чем в других типах магазинов. Таким образом, применение промокода для типа b может не дать нужного результата.

Следующее наблюдение касается корреляции между продажами и клиентами.

Мы можем заметить, что эти два показателя сильно коррелированы.

Расстояние от конкурентов — еще один фактор, который может повлиять на продажи. Давайте посмотрим сюжет, чтобы описать это.

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

Последнее сравнение, которое у нас есть, это продажи до и после конкуренции.

Мы можем наблюдать здесь, что продажи снизились на 3,6% после почти конкуренции.

Сводка наблюдений

  • Продажи перед рождественским сезоном превосходны, а во время Рождества падают.
  • Продажи в течение пасхального сезона, по наблюдениям, в равной степени выше, чем в предпасхальный сезон.
  • Промокод может напрямую повлиять на продажи.
  • Мы заметили, что предел промо-эффекта в магазине типа B намного меньше, чем в других типах магазинов. Таким образом, применение промокода для типа b может не дать необходимого результата.
  • Дистанция конкуренции не влияет на продажи.
  • Продажи снизились на 3,6% после добавления близкой конкуренции.

Предварительная обработка и обучение машинному обучению

Чтобы получить готовые к модели чистые данные, первое, что мы делаем, — это объединяем train.csv и store.csv в столбце Store.

merged_df=pd.merge(train_df, store_df, on='Store')

После слияния нам нужно обработать нулевые значения в некоторых столбцах. Поэтому мы заполнили нулевые значения dtypes объекта с помощью mode(). Те, что содержат dtypes чисел, заполняются mean() и median().

Следующее, что мы должны сделать, это создать конвейер для обучения.

pipeline = Pipeline(steps=[
("preprocessor", preprocessor),
("regressor", RandomForestRegressor(n_jobs=-1, n_estimators=15)),
])

Ниже приведено сравнение фактического и прогнозного результата RandomForestRegressor.

Модель построения с глубоким обучением

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

Долговременная кратковременная память — это искусственная нейронная сеть, используемая в областях искусственного интеллекта и глубокого обучения. В отличие от стандартных нейронных сетей с прямой связью, LSTM имеет связи с обратной связью. Такая рекуррентная нейронная сеть может обрабатывать не только отдельные точки данных, но и целые последовательности данных.

Чтобы построить модель с глубоким обучением, мы выполним ряд следующих шагов.

1. Нам нужно изолировать набор данных Rossmann Store Sales в данные временных рядов.

sales_series = df[["Date", "Sales"]].sort_values(by="Date")
sales_series.groupby("Date").agg({"Sales": "mean"})

2. Проверьте, являются ли ваши данные временных рядов стационарными.

Чтобы проверить это, нам сначала нужно построить ряд.

Поскольку мы можем наблюдать сезонность на графике, данные не являются стационарными.

3. Разность данных временных рядов

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

def difference(dataset, interval=1):
  diff = list()
  for i in range(interval, len(dataset)):
    value = dataset[i] - dataset[i - interval]
    diff.append(value)
  return pd.Series(diff)
#call defined function
X = sales_series.values
diff = difference(X)
plt.plot(diff)
plt.show()

Мы можем заметить, что разность устранила зависимость ряда от времени.

4. Проверка автокорреляции и частичной автокорреляции

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

PACF – это функция частичной автокорреляции, которая объясняет частичную корреляцию между сериями и запаздываниями самих по себе.

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
plot_acf(data_agg)
plot_pacf(data_agg, lags=50)

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

Задача контролируемого обучения состоит из входных шаблонов (X) и выходных шаблонов (y), так что алгоритм может научиться предсказывать выходные шаблоны на основе входных шаблонов.

Мы можем использовать функцию shift() в Pandas, чтобы автоматически создавать новую структуру задач временных рядов с заданной длиной входных и выходных последовательностей.

def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
"""
Frame a time series as a supervised learning dataset.
Arguments:
data: Sequence of observations as a list or NumPy array.
n_in: Number of lag observations as input (X).
n_out: Number of observations as output (y).
dropnan: Boolean whether or not to drop rows with NaN values.
Returns:
Pandas DataFrame of series framed for supervised learning.
"""
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg

6. Масштабируйте данные в диапазоне (-1, 1)

nd = supervised_data.to_numpy()
# define min max scaler
scaler = MinMaxScaler()
# transform data
scaled_data = scaler.fit_transform(nd)
scaled_df =pd.DataFrame(scaled_data,columns=supervised_data.columns)
scaled_df

7. Создайтерегрессионную модель LSTM для прогнозирования следующей продажи

Сначала нам нужно выбрать размер, размер партии, размер окна и эпохи.

SIZE = len(scaled_df["Sales"])
WINDOW_SIZE = 48
BATCH_SIZE= SIZE-WINDOW_SIZE*2
EPOCHS = 100
model = Sequential()
model.add(LSTM(20, input_shape=[None, 1], return_sequences=True))
model.add(LSTM(10, input_shape=[None, 1]))
model.add(Dense(1))
model.compile(loss="huber_loss", optimizer="adam")
model.summary()
history = model.fit(
self.TrainDataset,
epochs=EPOCHS,
validation_data=self.ValidDataset,
verbose=verbose,
)

Заключение

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

Ссылки и другие полезные ресурсы:

  1. понимание-lstm-и-его-быстрая-реализация
  2. Понимание LSTM
  3. Как удалить тренды и сезонность с помощью разностного преобразования

Вы можете связаться со мной здесь LinkedIn