Оглавление:
- Введение
- Цели
- Исследовательский анализ данных
- Моделирование
- Заключение
Введение
Каждая организация может извлечь выгоду из прогноза продаж, чтобы помочь им принимать более обоснованные решения, а точное прогнозирование продаж является важным и недорогим способом увеличения прибыли. Без эффективного прогнозирования клиентского спроса и будущих продаж продуктов/услуг ни один бизнес не сможет улучшить свои финансовые показатели. Владельцы бизнеса традиционно прогнозировали доходы на основе своего многолетнего опыта, который оказался менее точным. В наши дни владельцы бизнеса не просто хотят полагаться на свои собственные суждения, чтобы предвидеть продажи; они хотят использовать исторические данные для точного прогнозирования продаж. Лучший способ прогнозировать продажи со значительно большей точностью — использовать глубокое и машинное обучение. В этой статье я покажу, как применять машинное обучение для прогнозирования будущих продаж.
Цели
Финансовая команда хочет прогнозировать продажи во всех своих магазинах в нескольких городах на шесть недель вперед. Были определены такие факторы, как рекламные акции, конкуренция, школьные и государственные праздники, сезонность и местоположение, необходимые для прогнозирования продаж в различных магазинах. Основная цель состоит в том, чтобы создать и обслуживать комплексный продукт, который предоставляет этот прогноз аналитикам финансовой группы за шесть недель до запланированного срока.
Описание набора данных
Данные, используемые для этого проекта, взяты с конкурса Kaggle, и их можно скачать здесь.
Поля данных:
Id — идентификатор, представляющий дубликат (Store, Date) в тестовом наборе.
Магазин — уникальный идентификатор для каждого магазина.
Продажи — оборот за любой день (это то, что вы прогнозируете).
Клиенты — количество клиентов в определенный день.
Открыто — индикатор того, был ли магазин открыт: 0 = закрыт, 1 = открыт.
StateHoliday — указывает на государственный праздник. Обычно все магазины, за редким исключением, закрыты в государственные праздники. Обратите внимание, что все школы закрыты в праздничные и выходные дни. a = государственный праздник, b = праздник Пасхи, c = Рождество, 0 = нет
SchoolHoliday – указывает, повлияло ли на (Магазин, Дата) закрытие государственных школ.
StoreType — различает 4 разные модели магазина: a, b, c, d.
Ассортимент — описывает уровень ассортимента: a = базовый, b = дополнительный, c = расширенный. Подробнее об ассортименте здесь.
CompetitionDistance — расстояние в метрах до ближайшего магазина конкурента.
CompetitionOpenSince[Месяц/Год] — указывает примерный год и месяц открытия ближайшего конкурента.
Акция – указывает, проводится ли в этот день акция в магазине.
Promo2 — Promo2 — это продолжающаяся и последовательная акция для некоторых магазинов: 0 = магазин не участвует, 1 = магазин участвует.
Promo2Since[Год/Неделя] – указывает год и календарную неделю, когда магазин начал участвовать в акции Promo2.
PromoInterval — описывает последовательные интервалы запуска Promo2, называя месяцы, в которые акция начинается заново. Например. «Февраль, май, август, ноябрь» означает, что каждый раунд начинается в феврале, мае, августе и ноябре любого данного года для этого магазина.
Есть два отдельных файла CSV, которые будут использоваться для обучения модели. Это:
- Train.csv
- store.csv
Мы соединим эти две таблицы на основе идентификатора магазина по каждому из данных для обучения.
Исследовательский анализ набора данных
train_df = pd.read.csv(“train.csv”) store_df = pd.read_csv(“store.csv”)
Обзор данных
train_df.head()
store_df.head()
Теперь мы можем объединить две таблицы на основе идентификатора магазина следующим образом:
data = pd.merge(train_df, store_df, on=’Store’)
Общий объем продаж в магазинах:
plt.figure(figsize=(12, 7)) sns.scatterplot(data=train_df, x=train_df[‘Store’], y=train_df[‘Sales’])
Большинство продаж находятся в диапазоне от 0 до 2200, и есть несколько выбросов, которые необходимо обработать.
Корреляция продаж с покупателями для каждого типа магазина:
plt.figure(figsize=(12, 7)) sns.scatterplot(data=train_df[“Sales”, “Customers”], x=train_df[‘Customers’], y=train_df[‘Sales’])
Количество продаж положительно коррелирует с количеством покупателей для каждого типа магазина.
Корреляция продаж с конкуренцией Расстояние
plt.figure(figsize=(12, 7)) sns.scatterplot(data=train_df[“Sales”, “CompetitionDistance”], x=train_df[CompetitionDistance], y=train_df[‘Sales’])
Интересно, что по мере увеличения дистанции до конкурентов продажи снижаются. Есть некоторые продажи для магазина типа А, так как дистанция конкуренции очень высока.
Распродажи в праздничные дни
plt.figure(figsize=(12, 7)) sns.barplot(data=holiday_df, x=’’StateHoliday’, y=’Sales’)
В праздничные дни распродаж меньше
Продажи за каждый месяц в магазине
sns.relplot(x=”Month”, y=”Sales”, hue=”StoreType”, data=train_df);
В июне самый высокий объем продаж, а в январе самый низкий
Продажи каждый день
sns.relplot(x=”DayOfWeek”, y=”Sales”, hue=”StoreType”, data=train_df);
Магазин B в основном открыт по субботам, и в нем больше продаж
Предварительная обработка данных
- Заполнение пропущенных значений
- Заполните числовые признаки их медианой
miss_1 = [‘Promo2SinceYear’, ‘Promo2SinceWeek’, ‘CompetitionOpenSinceMonth’, ‘CompetitionOpenSinceYear’, ‘CompetitionDistance’] for col in miss_1: df[col] = df[col].fillna(df[col].median()
- Заполнить категориальные признаки режимом
Categ_var = [‘PromoInterval’] for col in Categ_var: df[col] = df[col].fillna(df[col].mode()[0])
df[‘Открыть] = df[‘Открыть].fillna(0)
Они должны заполнить все пропущенные значения
- Обработка выбросов
Большинство выбросов находятся в столбце «Продажи и клиенты», я заменил выбросы на IQR.
Columns = [‘Sales’, ‘Customers’] for col in columns: Q1, Q3 = df[col].quantile( 0.25), df[col].quantile(0.75) IQR = Q3 — Q1 cut_off = IQR * 1.5 lower, upper = Q1 — cut_off, Q3 + cut_off df[col] = np.where( df[col] > upper, upper, df[col]) df[col] = np.where( df[col] < lower, lower, df[col])
- Кодировка этикетки
from sklearn.preprocessing import LabelEncoder categorical_columns = [‘PromoInterval’, ‘Assortment’, ‘StoreType’] # Label encoding label_encoded_columns = preprocess.label_encode(train_df, categorical_columns) label_encoded_columns = [] # For loop for each columns le = LabelEncoder() for col in categorical_columns: # We define new label encoder to each new column # Encode our data and create new Dataframe of it, column_dataframe = pd.DataFrame( le.fit_transform(df[col]), columns=[col]) # and add new DataFrame to “label_encoded_columns” list label_encoded_columns.append(column_dataframe) # Merge all data frames label_encoded_columns = pd.concat(label_encoded_columns, axis=1) train_df.drop(categorical_columns, axis=1, inplace=True) # Merge DataFrames train_df = pd.concat([train_df, label_encoded_columns], axis=1)
Моделирование
Мы можем использовать модели машинного обучения, такие как регрессор случайного леса, для прогнозирования продаж, а также глубокое обучение, чтобы повысить точность прогнозирования продаж.
Модель машинного обучения (случайный лесной регрессор)
При построении модели машинного обучения можно использовать конвейер sklearn для одновременного вычисления всех этапов очистки данных и моделирования. Что касается машинного обучения, я использовал Random Forest Regressor для прогнозирования продаж.
from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestRegressor data_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='mean')) ,('scaler', StandardScaler()) ]) pipeline = Pipeline(steps = [ ('preprocessor', data_transformer) ,('regressor',RandomForestRegressor(max_depth=57, random_state=0)) ])
Наконец, подогнать и оценить модель
model_rg = pipeline.fit(X_train, y_train) model_rg.score(X_test, y_test)
Я получил точность около 90%.
Чтобы сохранить модель:
# Save model based on time st model_rg = '../models/' + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + '.pkl' pickle.dump(rf_model, open(rf_model_path, 'wb'))
Подход к глубокому обучению
Для глубокого обучения я использовал TensorFlow для обучения своей модели. При обучении данных, представляющих собой временные ряды или меняющихся сверхурочно, разбиение на поезд-тест отличается от того, как это делается с использованием библиотеки train_test_split из sklearn. Это потому, что мы хотим научить наши данные предсказывать будущее на основе прошлых невидимых данных. Лучший способ сделать это — отсортировать данные на основе индекса даты и разделить их вручную. Вот раздел кода, который я использовал для разделения данных:
train_size = int(len(df2) * 0.70) test_size = len(df2) - train_size train, testt = df2.iloc[0:train_size], df2.iloc[train_size:len(df2)] print(train.shape, testt.shape) #(591036, 2) (253302, 2)
То же самое касается тестовых данных:
test_size = int(len(testt) * 0.50) val_size = len(testt) - test_size test, val = testt.iloc[0:test_size],testt.iloc[test_size:len(testt)] print(val.shape, test.shape)
Затем мы можем построить модель LSTM для обучения данных:
model = Sequential() model.add(LSTM(128, input_shape=(X_train.shape[1], train.shape[2]))) model.add(Dropout(rate=0.2)) model.add(RepeatVector(X_train.shape[1])) model.add(LSTM(128, return_sequences=True)) model.add(Dropout(rate=0.2)) model.add(TimeDistributed(Dense(X_train.shape[2]))) model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001, epsilon=1e-08, decay=0.01), loss='mae') model.summary()
Наконец, подгоните данные
history = model.fit(X_train, y_train, epochs=10, batch_size=40, validation_data=(X_val, y_val), callbacks=[keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, mode='min')], shuffle=False)
Заключение
Анализ временных рядов — очень полезный способ предсказать будущие тенденции на основе текущей практики. Современные подходы к глубокому обучению открыли отличный способ повысить точность и эффективность модели, и можно выполнить больше работы, используя точно настроенную реализацию глубокого обучения с учетом требуемых вычислительных ресурсов и времени.
Рекомендации
https://medium.com/vickdata/a-simple-guide-to-scikit-learn-pipelines-4ac0d974bdcf