Использование Facebook Prophet и различных алгоритмов машинного обучения (регрессии) для прогнозирования продаж…

Walmart - американская многонациональная розничная корпорация, которая управляет сетью гипермаркетов (также называемых суперцентрами), дисконтных универмагов и бакалейных товаров категории g из Соединенных Штатов со штаб-квартирой в Бентонвилле, Арканзас. Согласно списку Fortune Global 500 в 2020 году Walmart - крупнейшая компания в мире по размеру выручки с 548,743 миллиарда долларов США.

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

Оглавление

  1. Бизнес-проблема
  2. Сопоставление бизнес-проблемы с проблемой машинного обучения
  3. Используемая метрика производительности
  4. Почему только этот показатель производительности?
  5. Исследовательский анализ данных (EDA)
  6. Предварительная обработка данных
  7. Базовая модель без каких-либо функций
  8. Функциональная инженерия
  9. Важность функции при использовании случайного леса
  10. Базовая модель после разработки функций
  11. Модель временных рядов для прогнозирования, например, Facebook Prophet
  12. Различные модели машинного обучения
  13. Окончательная модель
  14. Развертывание
  15. использованная литература
  16. Будущая работа
  17. EndNote

1. бизнес-проблема

1.1 Описание:

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

Кроме того, в течение года Walmart проводит несколько рекламных мероприятий по уценке. Эти скидки предшествуют знаменательным праздникам, четырьмя крупнейшими из которых являются Суперкубок, День труда, День Благодарения и Рождество. Недели, включающие эти праздничные дни, имеют в оценке в пять раз больший вес, чем недели, не являющиеся выходными. Частично проблема, связанная с этим соревнованием, заключается в моделировании эффектов уценки в эти праздничные недели при отсутствии полных / идеальных исторических данных.

Кредит: Kaggle

Заявление о проблеме

У Walmart 45 магазинов, в каждом из которых 99 отделов. У нас есть данные о еженедельных продажах каждой пары «магазин-отдел» с 2010–02–05 по 2012–11–01, и мы должны спрогнозировать еженедельные продажи каждой пары магазин – отдел с 2012–11–02 по 2013–07–26.

Подробнее о проблеме

2. Сопоставление бизнес-проблемы с проблемой машинного обучения

2.1 Обзор набора данных:

Он имеет 4 набора данных.

а) train.csv - в нем есть store, dept, date, weekly_sales, isHoliday, охватывающий период с 2010–02–05 по 2012–11–01.

Магазин - номер магазина

Dept - номер отдела

Дата - неделя

Еженедельные продажи - продажи для данного отдела в данном магазине.

IsHoliday - является ли неделя особой праздничной.

б) features.csv - этот файл содержит дополнительные данные, относящиеся к магазину, отделу и региональной активности за указанные даты. Он содержит следующие поля:

Магазин - номер магазина

Дата - неделя

Температура - средняя температура в регионе.

Fuel Price - стоимость топлива в регионе

MarkDown1–5 - анонимные данные, связанные с рекламными скидками, которые проводит Walmart. Данные уценки доступны только после ноября 2011 г. и доступны не для всех магазинов постоянно. Любое отсутствующее значение отмечается NA.

CPI - индекс потребительских цен.

Безработица - уровень безработицы.

IsHoliday - является ли неделя особой праздничной.

c) store.csv - этот файл содержит анонимную информацию о 45 магазинах с указанием типа и размера магазина.

г) test.csv - этот файл идентичен train.csv, за исключением того, что мы удерживаем еженедельные продажи. Вы должны спрогнозировать продажи для каждой тройки магазина, отдела и даты в этом файле.

Для удобства четыре праздника попадают в следующие недели в наборе данных (не все праздники включены в данные):

Суперкубок: 12-фев-10, 11-фев-11, 10-фев-12, 8-фев-13

День труда: 10 сентября 10, 9 сентября 11, 7 сентября 12, 6 сентября 13.

День Благодарения: 26-ноя-10, 25-ноя-11, 23-ноя-12, 29-ноя-13

Рождество: 31 декабря 10, 30 декабря 11, 28 декабря 12, 27 декабря 13

2.2 Тип задачи машинного обучения

А. Это проблема временных рядов, так как в нем еженедельные продажи по датам составляют около 2 лет, и мы должны прогнозировать на следующий год.

б) Это также проблема регрессии, так как для данной тройки даты филиала мы должны прогнозировать еженедельные продажи, которые являются реальной величиной.

3.Используемая метрика производительности

Walmart предоставил метрику взвешенной средней абсолютной ошибки (WMAE), математическая функция которой показана ниже.

4.Почему только этот показатель производительности?

Прежде всего, мы видим, почему мы не использовали здесь RMSE в качестве метрики производительности, поскольку она наиболее известна, а также мы предпочли MAE по следующим причинам:
• Данный набор данных не является большим набором данных, поэтому влияние выбросов повлияет больше, чем обычно. Таким образом, RMSE может почти вдвое увеличить влияние выбросов.
• Давайте рассмотрим пример двух моделей:

M1 с точками: 11,11,11,110

M2 с точками: 14,14`` 14,106

Фактические баллы: 10,10,10,100

RMSE M1 = 1/4 * (sqrt (1 + 1 + 1 + 100)) = 2,54

RMSE M2 = 1/4 * (sqrt (16 + 16 + 16 + 36)) = 2,29

MAE M1 = 1/4 * (1 + 1 + 1 + 10) = 3,25

MAE M2 = 1/4 * (4 + 4 + 4 + 6) = 4,5

Итак, мы можем ясно видеть, что RMSE M1 ›RMSE M2 и
MAE M1‹ MAE M2, и на самом деле моя модель 1 лучше, поскольку все выходы близки к фактическим, кроме одного, но RMSE дает здесь неправильные результаты

Теперь мы используем WMAE вместо обычного MAE, так как нам нужно увеличить влияние продаж на праздничной неделе, поскольку Walmart планирует мероприятия в соответствии с приближающимися праздниками. И WMAE явно делает это, поскольку дает в 5 раз больший вес тем неделям, где есть праздничные дни.

Короче говоря, в RMSE ошибки возведены в квадрат, что означает, что большим ошибкам присваивается гораздо больший вес. Таким образом, ошибка 10 в 100 раз хуже, чем ошибка 1. При использовании MAE ошибка масштабируется линейно. Следовательно, ошибка 10 в 10 раз хуже, чем ошибка 1. Таким образом, только из-за единственной точки выброса я могу получить огромный член ошибки, который в конечном итоге может увеличить общую ошибку и ухудшить модель.

5.Анализ исследовательских данных (EDA)

После объединения набора данных доступны три типа функций:

а). Одномерный анализ в столбце Store: - - ›поиск уникальных значений столбца Store

б). Одномерный анализ столбца "Отдел": - → поиск уникальных значений столбца "Отдел"

в). Одномерный анализ в столбце "Еженедельные продажи": - → график изменения даты

Вывод
1. Мы видим, что в ноябре и декабре недельные продажи становятся очень высокими, а это означает, что, возможно, праздники, приходящиеся на ноябрь и декабрь, оказывают огромное влияние на недельные продажи.

2. Небольшой спад продаж наблюдается в декабре и начале января.

3. На графике еженедельных продаж за определенный период времени присутствует сезонность, которую легко увидеть на графике.

4. Средние еженедельные продажи колеблются в районе 4,5 * 10⁷.

г) Одномерный анализ столбцов markdown1, markdown2, markdown3, markdown4, markdown5

Вывод

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

2. Все значения уценки дают всплеск в начале, как в январе 2012 года, и в начале уценка 3 самая высокая, но после начала уценка 1 высокая во всем наборе данных.

3. Markdown 3 имеет больше значений по сравнению с другими уценками, когда мы видим в 200000 точек данных. Таким образом, мы можем сделать вывод, что он оказал наибольшее влияние по сравнению с другими уценками на еженедельные продажи.

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

д.) Одномерный анализ по столбцу типа

Вывод

Мы узнали, что у магазинов типа A их медианы выше, чем у любых других медиан в магазинах других типов, поэтому еженедельные продажи в магазинах типа A больше, чем в магазинах других типов.

Всего существует 3 типа магазинов: Type A, Type Band Type.

Всего 45 магазинов. С помощью круговой диаграммы и гистограммы мы можем сказать, что магазин типа A является самым большим, а магазин C - самым маленьким.

е). Анализ столбца «Тип» с еженедельными продажами

raw=final_data.groupby([‘Type’,’Date’,’IsHoliday’])[‘Weekly_Sales’].sum().reset_index()
type_A=raw[raw[‘Type’]==’A’]
type_B=raw[raw[‘Type’]==’B’]
type_C=raw[raw[‘Type’]==’C’]
sns.distplot(type_A[‘Weekly_Sales’],label=’type_A’)
sns.distplot(type_B[‘Weekly_Sales’],label=’type_B’)
sns.distplot(type_C[‘Weekly_Sales’],label=’type_C’)
sns.set(rc={‘figure.figsize’:(12.7,6.27)})
plt.legend()
plt.title(‘Distribution of weekly sales of type of store’)
plt.show()

Вывод

1.Очень легко разделить значения еженедельных продаж для всех типов магазинов, так как значения полностью находятся в разных зонах.

2. среднее значение типа A составляет около 3 * 10⁷ и распределено по Гауссу.

3. среднее значение типа B составляет около 1,5 * 10⁷ и распределено по Гауссу.

4. Среднее значение типа C составляет около 0,2 * 10⁷ и распределено по Гауссу. Его дисперсия также очень меньше.

г) Анализ еженедельных продаж по сравнению с магазином с праздничным оттенком

data4 = pd.concat([final_data['Store'], final_data['Weekly_Sales'], final_data['IsHoliday']], axis=1)
plt.figure(figsize=(20,6))
plt.title('Box Plot of Weekly Sales by Store Number and Holiday')
fig = sns.boxplot(x='Store', y='Weekly_Sales', data=data4, showfliers=False, hue='IsHoliday')

1. Из графика видно, что недельные продажи всех магазинов в праздничные дни выше по сравнению с неделей без выходных.

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

6.Предварительная обработка данных

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

Вывод:

Мы видим, что все нулевые значения в столбцах ИПЦ и безработицы относятся к периоду 2013–05–03, а в нашем наборе данных о поездах или объединенном наборе данных содержатся даты до 2012–10–26.

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

Расчет столбца уценки в окончательных данных поезда

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

final_data.loc[final_data.MarkDown1.isnull() ,'MarkDown1']= 0
final_data.loc[final_data.MarkDown2.isnull() ,'Markdown2']= 0
final_data.loc[final_data.MarkDown3.isnull() ,'Markdown3']= 0
final_data.loc[final_data.MarkDown4.isnull() ,'Markdown4']= 0
final_data.loc[final_data.MarkDown5.isnull() ,'Markdown5']= 0

2.Имполяция ИПЦ и безработицы в столбце данных о характеристиках

Проблема: у нас есть значения NaN для столбцов CPI и Unemployment после 2012–11–01, что в основном содержится в тестовых данных, у нас нет значений CPI.

Решение: мы будем использовать модель временных рядов, например, Facebook Prophet, для прогнозирования CPI и безработицы для тестовых данных на основе данных поездов, которые доступны нам.

4. проверка отрицательных значений неотрицательного признака и удаление этих строк

final_data[final_data.Weekly_Sales<0]

Вывод

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

2.Оставьте все неотрицательные характеристики, такие как ИПЦ, Безработица, Цена на топливо, не имеет значения ‹0, поэтому нет необходимости удалять там какие-либо строки.

7. Базовая модель без каких-либо функций.

Сначала я разделю данные на обучение и тестирую на 70–30%.

final_data['Date']=pd.to_numeric(pd.to_datetime(final_data['Date']))
y = final_data['Weekly_Sales']
X = final_data.drop(['Weekly_Sales'], axis=1) 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) # Train:Test = 70:30 splitting.
# Final shapes.
print('Train shape:', X_train.shape, y_train.shape)
print('Test shape: ', X_test.shape, y_test.shape)

Определение функции WMAE для расчета оценки WMAE.

Теперь примените к нему простую регрессионную модель.

Вывод:

Мы сохраним эту оценку WMAE в качестве основы для наших дальнейших расчетов. Теперь мне нужно заняться разработкой функций, а затем применить некоторые более совершенные модели, чтобы уменьшить, таким образом, оценку wmae.

8. Разработка функций

Я построю несколько новых функций из существующих и буду кодировать категориальные функции…

1. Сначала будут температурные интервалы, которые будут категориальным признаком числового непрерывного признака, то есть температуры.

temp_90_100_f=final_data[((final_data.Temperature>90) & (final_data.Temperature< 100))].Weekly_Sales.sum()
temp_80_90_f=final_data[((final_data.Temperature>80) & (final_data.Temperature< 90))].Weekly_Sales.sum()
temp_70_80_f=final_data[((final_data.Temperature>70) & (final_data.Temperature< 80))].Weekly_Sales.sum()
temp_60_70_f=final_data[((final_data.Temperature>60) & (final_data.Temperature< 70))].Weekly_Sales.sum()
temp_50_60_f=final_data[((final_data.Temperature>50) & (final_data.Temperature< 60))].Weekly_Sales.sum()
temp_40_50_f=final_data[((final_data.Temperature>40) & (final_data.Temperature< 50))].Weekly_Sales.sum()
temp_30_40_f=final_data[((final_data.Temperature>30) & (final_data.Temperature< 40))].Weekly_Sales.sum()
temp=[temp_90_100_f,temp_80_90_f,temp_70_80_f,temp_60_70_f,temp_50_60_f,temp_40_50_f,temp_30_40_f]
temp_series=pd.Series(temp,index=['90-100','80-90','70-80','60-70','50-60','40-50','30-40'])

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

Вывод

1. В основном я буду кодировать этикетку, но на основе еженедельных продаж, как здесь, мы видим, что у слота 60_70 самые высокие еженедельные продажи, поэтому я даю ему метку 1

Слот 2.90_100 имеет самые низкие еженедельные продажи, поэтому я даю ему последний ярлык, то есть 7. Итак, я отсортировал все слоты и присвоил им соответствующие ярлыки. Итак, мы создадим эту новую функцию.

def temp(data):
    data["Temp_bins"] = np.nan
    data.loc[((final_data.Temperature>30) & (data.Temperature< 40)) ,'Temp_bins']= 649176675.9499999
    data.loc[((final_data.Temperature>40) & (data.Temperature< 50)) ,'Temp_bins']= 964084701.44
    data.loc[((final_data.Temperature>50) & (data.Temperature< 60)) ,'Temp_bins']= 1122389636.8200002
    data.loc[((final_data.Temperature>60) & (data.Temperature< 70)) ,'Temp_bins']= 1292714835.6399999
    data.loc[((final_data.Temperature>70) & (data.Temperature< 80)) ,'Temp_bins']= 1289690535.0500002
    data.loc[((final_data.Temperature>80) & (data.Temperature< 90)) ,'Temp_bins']= 851502899.1800001
    data.loc[((final_data.Temperature>90) & (data.Temperature< 100)),'Temp_bins']= 143672161.36
    data.loc[data.Temp_bins.isnull() ,'Temp_bins']= 0
    return data
final_data=temp(final_data)

2. Разделение даты на месяц, год, день, неделю

# Form Date, Year, Month, Week, Day
def split(data):
    data['Date'] = pd.to_datetime(data['Date'])
    data['Year'] = data['Date'].dt.year
    data['Month']= data['Date'].dt.month
    data['Week'] = data['Date'].dt.week
    data['Day']  = data['Date'].dt.day
    return data
final_data=split(final_data)

3 .. Количество дней до Рождества

import datetime
def days_from_Christmas(x):
    if x['Year']== 2010 :
        diff=datetime.datetime(2010, 12, 31)-x['Date']
        return diff.days
    if ((x['Year']== 2011) and (x['Date']< datetime.datetime(2011, 12, 30))):
        diff=datetime.datetime(2011, 12, 30)-x['Date']
        return diff.days
    else:
        return 0
final_data['diff_from_Christmas'] = final_data.apply(days_from_Christmas_for_train, axis=1)

4. Количество дней до Дня благодарения

def days_from_thanksgiving(x):
    if ((x['Year']== 2010) and (x['Date']< datetime.datetime(2010, 11, 26))):
        diff=datetime.datetime(2010, 11, 26)-x['Date']
        return diff.days
    if ((x['Year']== 2011) and (x['Date']< datetime.datetime(2011, 11, 25))):
        diff=datetime.datetime(2011, 11, 25)-x['Date']
        return diff.days
    else:
        return 0
final_data['days_from_thanksgiving'] = final_data.apply(days_from_thanksgiving_for_train, axis=1)

5. Кодирование ярлыков на основе четырех праздничных недель

Мы видим, что есть только 4 недели, для которых предусмотрены праздничные дни для каждого года, эта неделя - единственная неделя, которая содержит особые выходные, поэтому мы создадим новую функцию, в которой мы сохраним эту неделю, и если на ней будут выходные.

def holiday_type(x):
    if   (x['IsHoliday']== 1) & (x['Week']==6):
        return 1 #SuperBowl
    elif (x['IsHoliday']== 1) & (x['Week']==36):
        return 2 #LaborDay
    elif (x['IsHoliday']== 1) & (x['Week']==47):
        return 3 #Thanksgiving
    elif (x['IsHoliday']== 1) & (x['Week']==52):
        return 4 #Christmas
    else:
        return 0
#converting IsHoliday into 0 and 1
final_data['IsHoliday_bins'] = final_data.apply(holiday_type, axis=1)

6. Подсчет количества выходных в неделю для США.

dates =[]
for ptr in holidays.US(years = 2010).items():
    dates.append(ptr[0])
for ptr in holidays.US(years = 2011).items():
    dates.append(ptr[0])
for ptr in holidays.US(years = 2012).items():
    dates.append(ptr[0])
holiday_count=[] 
for index, row in final_data.iterrows():
    dat = final_data['Date'][index]
    dt=[]
    for i in range(0,5):
        dt.append(dat - datetime.timedelta(days = i))
    for i in range(1,3):
        dt.append(dat + datetime.timedelta(days = i))
    count = 0
    for date in dates:
        if date in dt:
            count +=1
 holiday_count.append(count)
final_data['IsHoliday_bins'] = final_data.apply(holiday_type, axis=1)

7. Кодирование метки для функции типа

final_data.loc[(final_data.Type=='A') ,'Type']= 1
final_data.loc[(final_data.Type=='B') ,'Type']= 2
final_data.loc[(final_data.Type=='C') ,'Type']= 3

8. Выполнение кодирования метки функции типа

final_data.loc[(final_data.IsHoliday==True) ,'IsHoliday']= 1
final_data.loc[(final_data.IsHoliday==False) ,'IsHoliday']= 0

9. Важность функции при использовании случайного леса

Топ-10 функций согласно модели RandomForestRegressor в порядке убывания:

Департамент ›Размер› Магазин ›Неделя› CPI ›Тип› Безработица ›Температура› День ›IsHoliday_bins

10. Базовая модель после Feature Engineering

11. модель временных рядов для прогнозирования, например, Facebook Prophet

Что такое Пророк?

«Prophet» - это библиотека с открытым исходным кодом, доступная на R или Python, которая помогает пользователям анализировать и прогнозировать значения временных рядов, выпущенные в 2017 году. Благодаря огромным усилиям разработчиков сделать анализ данных временных рядов доступным без экспертных работ, это очень важно. удобный, но при этом легко настраиваемый даже для неопытных пользователей. Как мило!!

Пророк может справиться;

  • тренд с его точками изменения,
  • сезонность (годовая, недельная, ежедневная и другая, определяемая пользователем),
  • праздничный эффект, и
  • входные регрессоры

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

Исходный код:

От Facebook Prophet я получил 3731 балл в тесте WMAE.

12.Различные модели машинного обучения

Мы использовали эти алгоритмы машинного обучения после настройки гиперпараметров для расчета оценки WMAE.

  1. Линейная регрессия
  2. KNeighborsRegressor
  3. RandomForestRegressor
  4. LGBMRegressor
  5. CatBoostRegressor
  6. XGBRegressor
  7. StackingRegressor

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

10 основных характеристик, выбранных на основе важности характеристик на основе модели:

['Store','Dept','Size','Week','Type','Day''IsHoliday_bins','Temperature','CPI','Unemployment']

Основные характеристики, выбранные на основе знаний в предметной области:

['Store','Dept','IsHoliday','Size','Week','Type','Day','Year','Holidays']

13.Финальная модель

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

from sklearn.ensemble import RandomForestRegressor
final_data_train=final_data_train[['Store','Dept','IsHoliday','Size','Week','Type','Year','Weekly_Sales','Holidays','Day']]
final_data_test=final_data_test[['Store','Dept','IsHoliday','Size','Week','Type','Year','Holidays','Day']]
final_data_train['IsHoliday']=final_data_train['IsHoliday'].astype('bool')
final_data_test['IsHoliday']=final_data_test['IsHoliday'].astype('bool')
final_data_train['Type']=final_data_train['Type'].astype('int')
final_data_test['Type']=final_data_test['Type'].astype('int')
y = final_data_train['Weekly_Sales']
X = final_data_train.drop(['Weekly_Sales'], axis=1)
rf_Model = RandomForestRegressor(n_estimators= 140,max_depth=27,n_jobs = -1)
rf_Model.fit(X, y)
y_hat= rf_Model.predict(final_data_test)

Отправка Kaggle

Я набрал 2638,69 баллов WMAE на Kaggle и попал в топ 5% в таблице лидеров.

14.Развертывание

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

15. ссылки









16.Будущая работа

  1. Мы можем попробовать различные методы глубокого обучения, такие как RNN, LSTM, GRU.
  2. В facebook prophet есть что исследовать, я только что использовал стандартный по умолчанию вместе с одним дополнительным аддоном, то есть добавлением Regressor, но есть еще что добавить, например, эффект праздника, сезонность (годовая, еженедельная, ежедневная и другая определяемая пользователем сезонность) .

17.EndNote

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

Вы можете найти полный код на моем Github здесь

Рад связаться с вами в » «LinkedIn