Как можно использовать машинное обучение для прогнозирования спроса в цепочках поставок?

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

Что такое машинное обучение?

Специалисты по цепочке поставок обычно используют статистику старой школы для прогнозирования спроса. Но с появлением в последнее время алгоритмов машинного обучения в нашем распоряжении появились новые инструменты, с помощью которых можно легко добиться отличной производительности с точки зрения точности прогнозов для типичного набора данных промышленного спроса. Эти модели смогут изучить многие взаимосвязи, которые выходят за рамки возможностей традиционных статистических моделей. Например, как добавить внешнюю информацию (например, погоду) в модель прогноза.

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

Машинное обучение отличается: здесь алгоритм (то есть машина) будет изучать отношения из набора обучающих данных (то есть нашего исторического спроса), а затем сможет применить эти отношения к новым данным. В то время как традиционная статистическая модель будет использовать предопределенную взаимосвязь (модель) для прогнозирования спроса, алгоритм машинного обучения не будет предполагать a priori конкретную взаимосвязь (например, сезонность или линейный тренд); он будет изучать эти закономерности непосредственно на основе исторического спроса.

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

Еще одно важное различие между использованием машинного обучения и моделей экспоненциального сглаживания для прогнозирования нашего спроса заключается в том, что алгоритм машинного обучения изучает закономерности из всего нашего набора данных. Модели экспоненциального сглаживания будут обрабатывать каждый элемент индивидуально, независимо от других. Алгоритм машинного обучения будет изучать закономерности из всего набора данных и применять то, что лучше всего работает для каждого продукта. Можно повысить точность модели экспоненциального сглаживания, увеличив длину каждого временного ряда (т. Е. Предоставив больше исторических периодов для каждого продукта). С помощью машинного обучения мы сможем повысить точность нашей модели, предоставив больше продуктов.

Добро пожаловать в мир машинного обучения.

Машинное обучение для прогноза спроса

Чтобы сделать прогноз, мы зададим алгоритм машинного обучения следующий вопрос:
На основе последних n периодов спроса, какой будет спрос в течение следующий период (ы)?
Мы обучим модель, предоставив ей данные с определенным макетом:
- n последовательных периодов спроса в качестве входных данных. < br /> - спрос на ближайший период (ы) в качестве выпуска.
Давайте посмотрим на пример (с квартальным прогнозом для упрощения таблицы):

Для нашей задачи прогноза мы в основном будем показывать нашему алгоритму машинного обучения различные выдержки из нашего набора исторических данных о спросе в качестве входных данных и каждый раз показывать, каким было следующее наблюдение за спросом. В нашем примере выше алгоритм изучит взаимосвязь между спросом за последние четыре квартала и спросом на следующий квартал. Алгоритм узнает, что если у нас есть 5, 15, 10 и 7 в качестве последних четырех наблюдений спроса, следующим наблюдением спроса будет 6, так что его прогноз должен быть 6.

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

Как мы увидим позже, машинное обучение может давать очень точные прогнозы. И как человек, управляющий машиной, мы все еще должны задать себе много вопросов:
- Какие данные использовать в алгоритме, чтобы он понимал правильные отношения.
- Какой алгоритм машинного обучения использовать (есть много разных!).
- Какие параметры использовать в нашей модели. Как вы увидите, каждый алгоритм машинного обучения имеет некоторые настройки, которые мы можем настроить, чтобы повысить его точность.

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

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

Соглашение об именах В процессе очистки данных мы будем использовать стандартную нотацию науки о данных и называть входы X и выходы Y. В частности, наборы данных X_train и Y_train будут содержать весь исторический спрос, который мы будем использовать для обучения нашего алгоритма (X_train является входными данными, а Y_train выходы). И наборы данных X_test и Y_test будут использоваться для тестирования нашей модели.

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

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

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

Это означает, что наш обучающий набор не пойдет до 3 квартала 4, поскольку он сохраняется для набора тестов: последний цикл будет использоваться в качестве заключительного теста.

Наши наборы данных X_train и Y_train будут выглядеть, как в таблице ниже:

Помните, что наш алгоритм будет изучать отношения в X_train, чтобы предсказать Y_train. Мы могли бы написать это как X_train - ›Y_train.

Последний тест будет проведен для нашего инструмента с помощью этих наборов данных X_test и Y_test:

Это каждый раз, когда мы знаем четыре последних квартала спроса на каждую позицию непосредственно перед третьим кварталом (т. Е. С второго квартала до третьего третьего квартала). Это означает, что наш алгоритм не увидит эти взаимосвязи на этапе обучения, так как он будет проверяться на точность, достигнутую в этих конкретных упражнениях по прогнозированию. Мы измерим его точность на этом тестовом наборе и предположим, что его точность будет аналогичной при прогнозировании будущего спроса.

Длина набора данных
В любом упражнении по машинному обучению важно обращать внимание на то, сколько данных передается в алгоритм. Чем больше, тем лучше. С другой стороны, чем больше периодов мы используем для прогнозирования (мы назовем это x_len), тем меньше мы сможем перебирать набор данных в цикле. Кроме того, если мы хотим спрогнозировать сразу несколько периодов (y_len), это будет стоить нам части набора данных, поскольку нам нужно больше данных (Y_train длиннее) для выполнения одного цикла в нашем наборе данных.

Обычно, если у нас есть набор данных с n периодами, мы сможем заставить его проходить 1 + n-x_len-y_len.

петли = 1 + n- x_len- y_len

Лучше всего сохранять по крайней мере количество прогонов, достаточное для прохождения двух полных лет, чтобы 23 + x_len + y_len ≤ n. Это означает, что алгоритм будет иметь два полных сезонных цикла для изучения возможных взаимосвязей. Если бы у него был только один, вы бы столкнулись с высоким риском переобучения.

Сделай сам
Сбор данных
Создание и очистка набора данных - важная часть любого проекта в области науки о данных. Чтобы проиллюстрировать все модели, которые мы создадим в следующих главах, мы будем использовать исторические продажи автомобилей в Норвегии с января 2007 года по январь 2017 года в качестве примера набора данных. Вы можете загрузить этот набор данных здесь: www.supchains.com/download
Вы получите файл csv с именем norway_new_car_sales_by_make.csv. Этот набор данных содержит продажи 65 автопроизводителей за 121 месяц. В среднем в Норвегии продается чуть более 140000 новых автомобилей в год, так что можно приблизительно оценить рынок в 4 миллиарда долларов, если предположить, что цена нового автомобиля в Норвегии в среднем составляет около 30000 долларов. . Этот набор данных невелик по размеру, но достаточно велик, чтобы его можно было экспериментировать с новыми моделями и идеями. Тем не менее, модели машинного обучения могут показывать лучшие результаты на других более крупных наборах данных.

Принесите свой собственный набор данных Во второй части статьи мы обсудим, как применить модель машинного обучения к этому примеру набора данных. Но на самом деле нас интересует ваш собственный набор данных. Не тратьте время зря и уже начните собирать некоторые исторические данные о спросе, чтобы вы могли протестировать следующие модели на своих собственных исторических данных о спросе по мере того, как мы продвигаемся по различным темам. Рекомендуется начать с набора данных, содержащего данные не менее чем за три года (лучше 5 лет) и более сотни различных продуктов. Чем больше, тем лучше.
Создание обучающих и тестовых наборов
Мы создадим первый код для извлечения данных из этого csv и отформатируем его с помощью даты в виде столбцов и продукты в виде строк.

# Load the CSV file (should be in the same directory) 
data = pd.read_csv(“norway_new_car_sales_by_make.csv”) 
 
# Create a column “Period” with both the Year and the Month 
data[“Period”] = data[“Year”].astype(str) + “-” + data[“Month”].astype(str) 
# We use the datetime formatting to make sure format is consistent 
data[“Period”] = pd.to_datetime(data[“Period”]).dt.strftime(“%Y-%m”) 
 
# Create a pivot of the data to show the periods on columns and the car makers on rows 
df = pd.pivot_table(data=data, values=”Quantity”, index=”Make”, columns=”Period”, aggfunc=’sum’, fill_value=0) 
 
# Print data to Excel for reference 
df.to_excel(“Clean Demand.xlsx”)

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

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

def import_data():
 data = pd.read_csv(“norway_new_car_sales_by_make.csv”)
 data[“Period”] = data[“Year”].astype(str) + “-” + data[“Month”].astype(str)
 data[“Period”] = pd.to_datetime(data[“Period”]).dt.strftime(“%Y-%m”)
 df = pd.pivot_table(data=data,values=”Quantity”,index=”Make”,columns=”Period”,aggfunc=’sum’,fill_value=0)
 return df

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

df наш первоначальный исторический спрос;
x_len количество месяцев, которое мы будем использовать для прогноза;
y_len количество месяцев, которые мы хотим спрогнозировать;
y_test_len количество месяцев, которые мы оставим в качестве финального теста;

и возвращает X_train, Y_train, X_test & Y_test.

def datasets(df, x_len=12, y_len=1, y_test_len=12):
D = df.values
 periods = D.shape[1]
 
 # Training set creation: run through all the possible time windows
 loops = periods + 1 — x_len — y_len — y_test_len 
 train = []
 for col in range(loops):
 train.append(D[:,col:col+x_len+y_len])
 train = np.vstack(train)
 X_train, Y_train = np.split(train,[x_len],axis=1)
 
 # Test set creation: unseen “future” data with the demand just before
 max_col_test = periods — x_len — y_len + 1
 test = []
 for col in range(loops,max_col_test):
 test.append(D[:,col:col+x_len+y_len])
 test = np.vstack(test)
 X_test, Y_test = np.split(test,[x_len],axis=1)
 
 # this data formatting is needed if we only predict a single period
 if y_len == 1:
 Y_train = Y_train.ravel()
 Y_test = Y_test.ravel()
 
 return X_train, Y_train, X_test, Y_test

В нашей функции мы должны использовать .ravel () как для Y_train, так и для Y_test, если мы хотим прогнозировать только один период за раз.
array.ravel () уменьшает размер массива NumPy до 1D.
Наша функция всегда создает Y_train и Y_test как 2D-массивы (т.е. массивы со строками и столбцами). Если мы хотим прогнозировать только один период за раз, эти массивы будут иметь только один столбец (и несколько строк). К сожалению, для функций, которые мы будем использовать позже, потребуются одномерные массивы, если мы хотим прогнозировать только один период.

Теперь мы можем легко вызвать нашу новую функцию datasets (df), а также import_data ().

import numpy as np
import pandas as pd
df = import_data()
X_train, Y_train, X_test, Y_test = datasets(df)

Теперь мы получаем наборы данных, которые нам нужны для питания нашего алгоритма машинного обучения (X_train и Y_train), и наборы данных, необходимые для его тестирования (X_test и Y_test).

Обратите внимание, что мы взяли y_test_len как 12 периодов. Это означает, что мы протестируем наш алгоритм на 12 различных прогнозах (поскольку мы прогнозируем только один период за раз).
Прогнозирование нескольких периодов одновременно Вы можете изменить y_len, если хотите прогнозировать несколько периодов однажды. Обратите внимание, чтобы y_test_len ≥ y_len; в противном случае вы не сможете проверить все прогнозы своего алгоритма.

Дерево регрессии

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

Чтобы сделать прогноз, дерево начнется с самого основания с первого вопроса «да / нет»; и на основе ответа он будет продолжать задавать новые вопросы «да / нет», пока не дойдет до окончательного прогноза. Каким-то образом вы могли бы увидеть эти деревья как большую игру в Угадай, кто? (знаменитая игра 80-х): модель будет задавать несколько последовательных вопросов, пока не найдет правильный ответ.

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

Различные фрагменты информации, которые дерево имеет в своем распоряжении для разделения узла, называются функциями. Например, дерево на рисунке выше может разделить узел на три компонента: Рот, Нос и Очки.

Как это работает?
Чтобы проиллюстрировать, как будет расти наше дерево, давайте вернемся к нашему ежеквартальному фиктивному набору данных.

Основываясь на этом наборе обучающих данных, умный вопрос, который нужно задать себе, чтобы сделать прогноз, звучит так: является ли первое наблюдение спроса ›7?

Это умный вопрос, поскольку вы знаете, что ответ (Да / Нет) предоставит интересное представление о поведении спроса в следующем квартале. Если ответ - да, спрос, который мы пытаемся спрогнозировать, скорее всего, будет довольно высоким (›8), а если ответ - нет, то спрос, который мы пытаемся прогноз, вероятно, будет низким (≤7).

Вот пример плохого вопроса.

Это не помогает, поскольку этот вопрос не разделяет наш набор данных на два разных подмножества (то есть внутри каждого подмножества все еще есть много вариаций). Если ответ на вопрос Является ли третье наблюдение спроса ‹6? - да, у нас все еще есть диапазон спроса от 1 до 11, и если ответ будет нет, диапазон от 4 до 13. Этот вопрос просто бесполезен для прогнозирования будущего спроса.

Не вдаваясь в подробности внутренней математической работы дерева, алгоритм роста нашего дерева будет на каждом узле выбирать вопрос (т. Е. разделение) об одной из доступных функций. (т. е. предыдущие кварталы), что минимизирует ошибку прогнозирования для двух новых подмножеств данных.
Первый алгоритм, предложенный для создания дерева решений, был опубликован в 1963 году Морганом и Сонквистом в их статье «Проблемы анализа данных опроса и предложения». как вырастить дерево решений (многие из них были разработаны с 60-х годов). Все они преследуют цель задать наиболее значимые вопросы о различных функциях набора данных, чтобы разбить его на разные подмножества, пока не будет достигнут какой-либо критерий.

Параметры
Важно понимать, что без критерия остановки роста нашего дерева оно будет расти до тех пор, пока каждое наблюдение данных (иначе называемое выборкой) не будет иметь своего собственного лист. Это ужасная идея, поскольку даже если у вас будет идеальная точность на вашем обучающем наборе, вы не сможете воспроизвести эти результаты на новых данных. Мы ограничим рост нашего дерева по некоторому критерию. Давайте посмотрим на самые важные из них (мы уже используем соглашение об именах scikit-learn).

Максимальная глубина Максимальное количество последовательных вопросов (узлов), которое может задать дерево.
Минимальное количество разделенных выборок Минимальное количество выборок, которое требуется в узле для запуска нового расколоть. Если вы установите это значение 6, узел, в котором осталось всего 5 наблюдений, не будет разделен дальше.
Минимум выборки листа Минимальное количество наблюдений, которое должно быть в листе. Это очень важный параметр. Чем ближе это значение к 0, тем выше риск переобучения, поскольку ваше дерево будет расти до тех пор, пока не будет задано достаточно вопросов, чтобы рассматривать каждое наблюдение отдельно.
Критерий. Это ключевой показатель эффективности, который алгоритм будет минимизировать (MSE или MAE).

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

Сделай сам
Мы будем использовать библиотеку Python scikit-learn (www.scikit-learn.org) для выращивания нашего первого дерева. Это хорошо известная библиотека с открытым исходным кодом, которая используется специалистами по обработке данных во всем мире. Он построен поверх NumPy, поэтому легко взаимодействует с остальной частью нашего кода.

Первый шаг - вызвать scikit-learn и создать экземпляр дерева регрессии. Как только это будет сделано, мы должны обучить его на основе наших массивов X_train и Y_train.

from sklearn.tree import DecisionTreeRegressor 
 
# — Instantiate a Decision Tree Regressor 
tree = DecisionTreeRegressor(max_depth=5,min_samples_leaf=5) 
 
# — Fit the tree to the training data 
tree.fit(X_train,Y_train)

Обратите внимание, что мы создали дерево с максимальной глубиной 5 (т. Е. Для классификации одной точки задается не более пяти последовательных вопросов «да / нет»), где каждый лист дерева имеет как минимум 5 образцов.

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

# Create a prediction based on our model 
Y_train_pred = tree.predict(X_train) 
 
# Compute the Mean Absolute Error of the model 
import numpy as np
MAE_tree = np.mean(abs(Y_train — Y_train_pred))/np.mean(Y_train) 
 
# Print the results 
print(“Tree on train set MAE%:”,round(MAE_tree*100,1))

Вы должны получить MAE 15,1%. Теперь давайте сравним точность с тестовым набором:

Y_test_pred = tree.predict(X_test) 
MAE_test = np.mean(abs(Y_test — Y_test_pred))/np.mean(Y_test) 
print(“Tree on test set MAE%:”,round(MAE_test*100,1))

Теперь мы получаем около 21,1%. Это означает, что наше дерево регрессии чрезмерно приспособлено к историческим требованиям: мы потеряли 6 баллов MAE в тестовом наборе по сравнению с историческим набором данных.

Идти дальше

Есть много способов улучшить этот результат:
- Оптимизировать параметры дерева.
- Использовать более сложные модели (например, Forest, ETR, Extreme Gradient Boosting).
- Оптимизировать входные данные .
- Использовать внешние данные.

Все это описано в книге Наука о данных для прогноза цепочки поставок (доступна на Amazon).

Об авторе



Николас Вандепут - специалист по данным о цепочке поставок, специализирующийся на прогнозировании спроса и оптимизации запасов. Он основал свою консалтинговую компанию SupChains в 2016 году и стал соучредителем SKU Science - быстрой, простой и доступной платформы для прогнозирования спроса - в 2018 году. Увлеченный образованием, Николас одновременно заядлый ученик и любит преподавать в университетах: он с 2014 года преподает прогнозирование и оптимизацию запасов для магистрантов в Брюсселе, Бельгия. С 2020 года он также преподает оба предмета в CentraleSupelec, Париж, Франция. Он опубликовал Data Science for Supply Chain Forecasting в 2018 г. (2-е издание в 2021 г.) и Оптимизация инвентаризации: модели и симуляции в 2020 г.