Создание многоликого, многозадачного LSTM с Keras
Преамбула
В настоящее время я работаю над проблемой машинного обучения, когда нам поручено использовать прошлые данные о продажах продуктов, чтобы прогнозировать объемы продаж в будущем (чтобы магазины могли лучше планировать свои запасы). Мы имеем по существу данные временных рядов, где для каждого продукта мы знаем, сколько единиц было продано в какие дни. У нас также есть информация о том, какая погода была, был ли государственный праздник, если какой-либо из продуктов был продан и т.д.
Мы с успехом смоделировали это с использованием MLP с плотными слоями и просто использовали подход с раздвижным окном, чтобы включить объемы продаж из окружающих дней. Однако мы полагаем, что мы сможем получить гораздо лучшие результаты с помощью подхода временного ряда, такого как LSTM.
Данные
По существу, мы имеем следующие данные:
![введите описание изображения здесь]()
( EDIT: для ясности столбца "Время" на приведенном выше рисунке неверно. У нас есть входы один раз в день, а не один раз в месяц, но в остальном структура одна и та же!)
Итак, данные X имеют форму:
(numProducts, numTimesteps, numFeatures) = (50 products, 1096 days, 90 features)
И данные Y имеют форму:
(numProducts, numTimesteps, numTargets) = (50 products, 1096 days, 3 binary targets)
![введите описание изображения здесь]()
Итак, у нас есть данные в течение трех лет (2014, 2015, 2016) и мы хотим тренироваться на этом, чтобы сделать прогнозы на 2017 год. (Это, конечно, не на 100% верно, поскольку у нас есть данные до октября 2017 года, но пусть просто игнорировать это сейчас)
Проблема
Я хотел бы создать LSTM в Keras, который позволяет мне делать эти прогнозы. Есть несколько мест, где я застрял. Итак, у меня есть шесть конкретных вопросов (я знаю, что один должен попытаться ограничить пост Stackoverflow одним вопросом, но все они переплетаются).
Во-первых, , как бы я нарезал свои данные для партий? Поскольку у меня есть три полных года, имеет смысл просто пропустить три партии, каждый раз размером один год? Или имеет смысл делать меньшие партии (скажем, 30 дней), а также использовать раздвижные окна? То есть вместо 36 партий по 30 дней каждый, я использую 36 * 6 партий по 30 дней каждый, каждый раз скользящий с 5 днями? Или это не так, как следует использовать LSTM? (Обратите внимание, что в данных имеется довольно немного сезонности, мне тоже нужно поймать этот долгосрочный тренд).
Во-вторых, имеет смысл использовать return_sequences=True
здесь? Другими словами, я сохраняю свои данные Y как (50, 1096, 3)
, так что (насколько я понял) на каждом временном шаге есть прогноз, по которому может быть рассчитана потеря по целевым данным? Или мне было бы лучше с return_sequences=False
, так что для оценки потери использовалось только окончательное значение каждой партии (т.е. При использовании годовых партий, то в 2016 году для продукта 1 мы оцениваем значение ).
В-третьих, как я должен работать с 50 различными продуктами?. Они разные, но все еще сильно коррелированы, и мы видели с другими подходами (например, MLP с простыми временными окнами), что результаты лучше, когда все продукты рассматриваются в одной и той же модели. Некоторые идеи, которые в настоящее время находятся на столе:
- изменить целевую переменную не только на 3 переменные, но 3 * 50 = 150; т.е. для каждого продукта есть три цели, каждая из которых обучается одновременно.
- разделить результаты после слоя LSTM на 50 плотных сетей, которые вводят в качестве входных данных из LSTM, а также некоторые функции, характерные для каждого продукта, то есть мы получаем многозадачную сеть с 50 функциями потерь, которые мы затем оптимизируем вместе. Это будет безумие?
- рассматривают продукт как одно наблюдение и включают специфические для продукта функции уже на уровне LSTM. Используйте только этот слой, за которым следует выпадающий слой размером 3 (для трех целей). Пропустите каждый продукт в отдельной партии.
В-четвертых, как я могу работать с данными проверки? Обычно я просто оставил случайно выбранный образец для проверки, но здесь нам нужно сохранить порядок времени на месте. Поэтому я думаю, что лучше всего просто оставить несколько месяцев в стороне?
В-пятых, и это часть, которая, вероятно, самая неясная для меня - , как я могу использовать фактические результаты для выполнения прогнозов? Скажем, я использовал return_sequences=False
, и я тренировался все три года в три партии (каждый раз до ноября) с целью обучения модели предсказать следующее значение (декабрь 2014, декабрь 2015, декабрь 2016 года). Если я хочу использовать эти результаты в 2017 году, как это работает? Если бы я понял это правильно, единственное, что я могу сделать в этом случае, - это то, что я буду кормить модель всеми точками данных за период с января по ноябрь 2017 года, и это вернет мне прогноз на декабрь 2017 года. Это правильно? Однако, если бы я использовал return_sequences=True
, а затем тренировался по всем данным до декабря 2016 года, смог бы я получить прогноз на январь 2017 года, просто предоставив модели функции, наблюдаемые в январе 2017 года? Или мне нужно также предоставить его за 12 месяцев до января 2017 года? Что же касается февраля 2017 года, я должен дополнительно указать значение для 2017 года, а еще 11 месяцев до этого? (Если это звучит, как будто я смущен, это потому, что я!)
Наконец, в зависимости от структуры, которую я должен использовать, как это сделать в Keras? То, что я имею в виду в данный момент, это что-то в следующих строках: (хотя это будет только для одного продукта, поэтому не решает, что все продукты в одной модели):
Код Keras
trainX = trainingDataReshaped #Data for Product 1, Jan 2014 to Dec 2016
trainY = trainingTargetReshaped
validX = validDataReshaped #Data for Product 1, for ??? Maybe for a few months?
validY = validTargetReshaped
numSequences = trainX.shape[0]
numTimeSteps = trainX.shape[1]
numFeatures = trainX.shape[2]
numTargets = trainY.shape[2]
model = Sequential()
model.add(LSTM(100, input_shape=(None, numFeatures), return_sequences=True))
model.add(Dense(numTargets, activation="softmax"))
model.compile(loss=stackEntry.params["loss"],
optimizer="adam",
metrics=['accuracy'])
history = model.fit(trainX, trainY,
batch_size=30,
epochs=20,
verbose=1,
validation_data=(validX, validY))
predictX = predictionDataReshaped #Data for Product 1, Jan 2017 to Dec 2017
prediction=model.predict(predictX)
Ответы
Ответ 1
Итак:
Во-первых, как бы я нарезал свои данные для партий? Поскольку у меня есть три полных года, имеет смысл просто проталкивать три партии, каждый раз размером один год? Или имеет смысл делать небольшие партии (скажем, 30 дней), а также использовать раздвижные окна? То есть вместо 36 партий по 30 дней каждый, я использую 36 * 6 партий 30 дней каждый, каждый раз скользящий с 5 днями? Или это не следует использовать LSTM? (Заметим, что есть довольно много сезонности в данных, мне нужно поймать такой долгосрочный тренда).
Честно говоря - моделирование таких данных - это что-то очень тяжелое. Прежде всего - я бы не советовал вам использовать LSTM
, поскольку они скорее предназначены для захвата немного разных типов данных (например, НЛП или речи, где действительно важно моделировать долгосрочные зависимости, а не сезонность), и они нужно много данных, чтобы их можно было изучить. Я бы скорее посоветовал вам использовать GRU
или SimpleRNN
, которые легче освоить и должны быть лучше для вашей задачи.
Когда дело доходит до пакетной обработки - я бы определенно посоветовал вам использовать технику с фиксированным окном, так как в конечном итоге он создаст больше точек данных, чем кормит целый год или целый месяц. Постарайтесь установить несколько дней в качестве метапараметра, который также будет оптимизирован с использованием разных значений при обучении и выборе наиболее подходящего.
Когда дело доходит до сезонности - конечно, это случай, но:
- У вас может быть слишком мало данных и собранных лет, чтобы обеспечить хорошую оценку сезонных тенденций,
- Использование любой рекуррентной нейронной сети для захвата таких сезонов - идея действительно плохая.
Вместо этого я советую вам:
- попробуйте добавить сезонные функции (например, переменная месяца, дневная переменная, переменная, которая установлена как истина, если в этот день есть определенный праздник или сколько дней на следующий важный праздник - это комната, где вы может быть действительно творческим)
- Использовать агрегированные данные за прошлый год как функцию - вы могли бы, например, подать результаты прошлого года или их скопления, например, среднее число результатов за последний год, максимум, минимум - и т.д.
Во-вторых, имеет смысл использовать return_sequences = True здесь? В другими словами, я сохраняю свои данные Y как есть (50, 1096, 3), так что (насколько это возможно Я это понял) на каждом временном шаге есть прогноз, для которого потеря может быть рассчитана против целевых данных? Или я буду лучше off с return_sequences = False, так что только конечное значение каждого пакет используется для оценки потерь (т.е. при использовании годовых партий, тогда в 2016 году для продукта 1, мы оцениваем значение декабря 2016 года (1,1,1)).
Использование return_sequences=True
может быть полезно, но только в следующих случаях:
- Если заданный
LSTM
(или другой повторяющийся слой) будет сопровождаться еще одним повторяющимся слоем.
- В сценарии - когда вы подаете смещенную оригинальную серию как результат того, что вы одновременно изучаете модель в разных временных окнах и т.д.
Способ, описанный во второй точке, может быть интересным подходом, но помните о том, что его может быть немного сложно реализовать, поскольку вам нужно будет переписать свою модель, чтобы получить производственный результат. Что может быть сложнее, так это то, что вам нужно будет протестировать свою модель против многих типов нестабильности во времени - и такой подход может сделать это совершенно неосуществимым.
В-третьих, как я должен заниматься 50 различными продуктами? Они есть разные, но все же сильно коррелированные, и мы видели с другими (например, MLP с простыми временными окнами), что результаты лучше, когда все продукты рассматриваются в одной и той же модели. Некоторые идеи, которые в настоящее время находятся на столе:
- изменить целевую переменную не только на 3 переменные, но 3 * 50 = 150; т.е. для каждого продукта есть три цели, каждая из которых обучается одновременно.
- разделить результаты после слоя LSTM на 50 плотных сетей, которые принимают в качестве входных данных из LSTM, а также некоторые функции, которые специфичны для каждого продукта - т.е. мы получаем многозадачную сеть с 50 функций потерь, которые мы затем оптимизируем вместе. Будет ли это сошел с ума?
- рассматривает продукт как одно наблюдение и включает в себя специфические для продукта функции уже на уровне LSTM. Используйте только этот слой за которым следует выровненный слой размером 3 (для трех целей). От себя через каждый продукт в отдельной партии.
Я бы выбрал первый выбор, но перед тем, как дать подробное объяснение, я обсужу недостатки 2-го и 3-го:
- Во втором подходе: это не было бы безумием, но вы потеряете много корреляций между целями продуктов,
- В третьем подходе вы потеряете много интересных шаблонов, возникающих в зависимости от разных временных рядов.
Прежде чем перейти к моему выбору, обсудим еще одну проблему - избыточность в вашем наборе данных. Я предполагаю, что у вас есть 3 вида функций:
- специфические продукты (скажем, что их "m" )
- общие функции - скажем, что есть "n" из них.
Теперь у вас есть таблица размера (timesteps, m * n, products)
. Я бы превратил его в таблицу формы (timesteps, products * m + n)
, поскольку общие функции одинаковы для всех продуктов. Это позволит вам сэкономить массу памяти, а также обеспечить возможность подачи в повторяющуюся сеть (помните, что повторяющиеся слои в keras
имеют только одно измерение функции, тогда как у вас было два - product
и feature
).
Итак, почему первый подход является лучшим на мой взгляд? Becasue использует многие интересные зависимости от данных. Конечно, это может нанести вред процессу обучения, но есть простой способ преодолеть это: уменьшение размерности. Вы можете, например, train PCA
на вашем 150-мерном векторе и уменьшите его размер до гораздо меньшего - благодаря тому, что у вас есть ваши зависимости, моделированные с помощью PCA
, и ваш результат имеет гораздо более допустимый размер.
В-четвертых, как мне работать с данными валидации? Обычно я бы просто не проверяйте случайно выбранный образец для проверки, но здесь мы необходимо сохранить порядок времени. Поэтому я думаю, что лучше всего просто отложите несколько месяцев?
Это действительно важный вопрос. По моему опыту - вам нужно протестировать свое решение против многих типов нестабильности, чтобы быть уверенным, что он работает нормально. Итак, несколько правил, которые вы должны иметь в виду:
- Между вашими последовательностями обучения и тестовыми последовательностями должно быть без перекрытия. Если бы это было так - у вас будут действительные значения из набора тестов, поданного на модель во время обучения,
- Вам нужно проверить стабильность времени модели на многие виды зависимостей времени.
Последняя точка может быть немного расплывчатой - так, чтобы предоставить вам несколько примеров:
- стабильность года. Проверяйте свою модель, обучая ее, используя каждую возможную комбинацию из двух лет и проверяйте ее на одном уровне (например, 2015, 2016 против 2017, 2015, 2017 против 2016 года и т.д. ) - это покажет вам, как изменения года влияют на вашу модель,
- будущая стабильность прогноза - подготовьте свою модель по подгруппе недель/месяцев/лет и протестируйте ее с помощью следующего результата за неделю/месяц/год (например, подготовьте его в январе 2015 года, январе 2016 года и январе 2017 и протестировать его, используя февраль 2015 года, февраль 2016 года, данные за февраль 2017 года и т.д.).
- стабильность месяца - модель поезда при сохранении определенного месяца в тестовом наборе.
Конечно, вы могли бы попробовать еще один аут-аут.
В-пятых, и это часть, которая, вероятно, самая непонятная для меня - как я могу использовать фактические результаты для выполнения прогнозов? Скажем, я использовал return_sequences = False, и я тренировался все три года в трех (каждый раз до ноября) с целью обучения модели прогнозировать следующее значение (декабрь 2014 года, декабрь 2015 года, декабрь 2016 года). Если я захочу использовать эти результаты в 2017 году, как это работает? Если я правильно понял, единственное, что я могу сделать в этом случае, - это чтобы затем загрузить модель всех точек данных за январь-ноябрь 2017 года, и это вернет мне прогноз на декабрь 2017 года. Это правильно? Однако, если бы я использовал return_sequences = True, то тренировался по всем данным до Декабрь 2016 года, смогу ли я получить прогноз на январь 2017 года давая модели функции, наблюдаемые в январе 2017 года? Или мне нужно также дать ему за 12 месяцев до января 2017 года? Как насчет февраля 2017 года, я кроме того, необходимо дать значение для 2017 года, плюс еще 11 месяцев до этого? (Если это звучит, как будто я смущен, это потому, что я!)
Это зависит от того, как вы построили свою модель:
- Если вы использовали
return_sequences=True
, вам нужно переписать его, чтобы иметь return_sequence=False
или просто взять вывод и рассмотреть только последний шаг от результата,
- Если вы использовали фиксированное окно - тогда вам нужно просто подать окно перед предсказанием модели,
-
если вы использовали различную длину - вы могли бы кормить любые временные интервалы, которые обрабатывают ваш период прогноза, который вы хотите (но я советую вам прокормить не менее 7 дней обработки).
Наконец, в зависимости от того, какую структуру я должен использовать, как это сделать в Keras? То, что я имею в виду в данный момент, - это что-то в следующих строках: (хотя это будет только для одного продукта, поэтому не решает иметь все продукты в одной модели)
Здесь - больше информации о том, какую модель вы выбрали.
Ответ 2
Вопрос 1
Существует несколько подходов к этой проблеме. То, что вы предлагаете, похоже, является скользящим окном.
Но на самом деле вам не нужно нарезать измерение времени, вы можете вводить все 3 года одновременно. Вы можете нарезать размер продуктов, если ваша партия становится слишком большой для памяти и скорости.
Вы можете работать с одним массивом с формой (products, time, features)
Вопрос 2
Да, имеет смысл использовать return_sequences=True
.
Если я правильно понял ваш вопрос, у вас есть y
прогнозы на каждый день, правильно?
Вопрос 3
Это действительно открытый вопрос. Все подходы имеют свои преимущества.
Но если вы планируете объединить все функции продукта, являясь этими функциями различной природы, вы должны, вероятно, расширить все возможные возможности, как если бы был большой горячий вектор, учитывающий все функции всех продуктов.
Если у каждого продукта есть независимые функции, которые применяются только к самому себе, идея создания отдельных моделей для каждого продукта не кажется мне безумной.
Вы также можете сделать идентификатор продукта одним горячим вводом вектора и использовать одну модель.
Вопрос 4
В зависимости от выбранного вами подхода вы можете:
- Разделите некоторые продукты как данные проверки
- Оставьте окончательную часть шагов времени как данные проверки
- Попробуйте метод перекрестной проверки, оставляя разные длины для обучения и тестирования (чем дольше тестовые данные, тем больше ошибка, тем не менее, вы можете обрезать эти тестовые данные с фиксированной длиной).
Вопрос 5
Могут быть также много подходов.
Существуют подходы, когда вы используете раздвижные окна. Вы тренируете свою модель для фиксированных длительностей.
И есть подходы, где вы тренируете слои LSTM со всей длиной. В этом случае вы должны сначала предсказать всю известную часть, а затем начать прогнозирование неизвестной части.
Мой вопрос: есть ли данные X
, известные за период, когда вы должны предсказать y
? Из X
также неизвестно в этот период, поэтому вы также должны предсказать X
?
Вопрос 6
Я рекомендую вам взглянуть на этот вопрос и его ответ: Как бороться с многоступенчатым прогнозированием временных рядов в многомерном LSTM в keras
См. также этот ноутбук, которому удается продемонстрировать идею: https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb
В этом ноутбуке, однако, я использовал подход, который помещает X и Y в качестве входов. И мы прогнозируем будущие X и Y.
Вы можете попытаться создать модель (если это случай) только для предсказания X. Затем вторая модель для прогнозирования Y из X.
В другом случае (если у вас уже есть все данные X, нет необходимости предсказать X), вы можете создать модель, которая только прогнозирует Y из X. (Вы все равно будете следовать за частью метода в записной книжке, где вы сначала предскажите уже известный Y только для того, чтобы ваша модель была скорректирована до того места, где она находится в последовательности, тогда вы прогнозируете неизвестный Y). Это можно сделать одним единственным полноразмерным входом X (который содержит обучение X на начало и тест X в конце).
Бонусный ответ
Зная, какой подход и какой тип модели выбрать, вероятно, является точным ответом на победу в конкурсе... так что на этот вопрос нет лучшего ответа, каждый участник пытается выяснить этот ответ.
Ответ 3
Следуя двум уже предоставленным ответам, я думаю, вам стоит взглянуть на эту статью Amazon Research по прогнозированию продаж с использованием LSTM, чтобы увидеть, как они справляются с перечисленными вами проблемами:
https://arxiv.org/abs/1704.04110
Кроме того, я должен также указать, что правильная регуляризация чрезвычайно важна при использовании повторяющихся сетей, поскольку их способность к переобучению может быть действительно впечатляющей. Возможно, вам стоит взглянуть на "вариационный повторный выпад", как описано в этой статье.
https://arxiv.org/abs/1512.05287
Примечание: это уже реализовано в Tensorflow!