Керас не делает хороших прогнозов

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

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

Плохие результаты

Где:

  • Синий: набор данных (реальные данные, которые я пытаюсь «аппроксимировать»)
  • Оранжевый: предсказание

Набор данных представляет собой временной ряд

вотфайл csv с набором данных

Вот код:

import numpy
import matplotlib.pyplot as plt
import pandas
import math

from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
from keras.regularizers import l2, activity_l2


def create_dataset(dataset, look_back=1):
    dataX, dataY = [], []
    for i in range(len(dataset) - look_back - 1):
        a = dataset[i:(i + look_back), 0:4] 
        dataX.append(a)
        dataY.append(dataset[i + look_back, 4]) 
    return numpy.array(dataX), numpy.array(dataY)



# fix random seed for reproducibility
seed=7
numpy.random.seed(seed)


# load dataset
dataframe = pandas.read_csv('datos_horarios.csv', engine='python') 
dataset = dataframe.values


# normalizar el dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)

#split data into train data and test data
train_size = int(len(dataset) * 0.67) 
test_size = len(dataset) - train_size
train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]

# reshape to X=t y Y=t+1
look_back = 1
trainX, trainY = create_dataset(train, look_back)  
testX, testY = create_dataset(test, look_back)

# reshape inputs to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], look_back, 4))
testX = numpy.reshape(testX, (testX.shape[0], look_back, 4))
# create and adjust LSTM network

model = Sequential()
model.add(Dropout(0.3, input_shape=(look_back,4))) 
model.add(LSTM(6, input_shape=(look_back,4), W_regularizer=l2(0.001))) 
model.add(Dense(10)) 
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam' ,momentum=0.99)
history= model.fit(trainX, trainY,validation_split=0.33, nb_epoch=250, batch_size=32)


# Plot
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epochs')
plt.legend(['training', 'validation'], loc='upper right')
plt.show()

# make predictions
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
print(trainPredict)

numero_inputs=4
inp=numero_inputs-1
# Get something which has as many features as dataset
trainPredict_extended = numpy.zeros((len(trainPredict),numero_inputs+1))
# Put the predictions there
trainPredict_extended[:,inp+1] = trainPredict[:,0]
# Inverse transform it and select the 3rd column.
trainPredict = scaler.inverse_transform(trainPredict_extended)[:,inp+1]

# Get something which has as many features as dataset
testPredict_extended = numpy.zeros((len(testPredict),numero_inputs+1))
# Put the predictions there
testPredict_extended[:,inp+1] = testPredict[:,0]
# Inverse transform it and select the 3rd column.
testPredict = scaler.inverse_transform(testPredict_extended)[:,inp+1]

trainY_extended = numpy.zeros((len(trainY),numero_inputs+1))
trainY_extended[:,inp+1]=trainY
trainY=scaler.inverse_transform(trainY_extended)[:,inp+1]

testY_extended = numpy.zeros((len(testY),numero_inputs+1))
testY_extended[:,inp+1]=testY
testY=scaler.inverse_transform(testY_extended)[:,inp+1]
# Calcular error medio cuadratico
trainScore = math.sqrt(mean_squared_error(trainY, trainPredict))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY, testPredict))
print('Test Score: %.2f RMSE' % (testScore))
# add train predictions to the plot

trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, 0] = trainPredict

# add test predictions to the plot
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, 0] = testPredict

# Plot real data and training and test predictions
serie,=plt.plot(scaler.inverse_transform(dataset)[:,numero_inputs])  #invierto muestras en formato (0,1) a valores reales y los ploteo
entrenamiento,=plt.plot(trainPredictPlot[:,0],linestyle='--')  #ploteo las predicciones de entrenamiento
prediccion_test,=plt.plot(testPredictPlot[:,0],linestyle='--')
plt.ylabel(' (m3)')
plt.xlabel('h')
plt.legend([serie,entrenamiento,prediccion_test],['Time series','Training','Prediction'], loc='upper right')
plt.show()

Любые идеи о том, как я могу решить эту проблему? Или хотя бы в чем проблема?

ВВОДЫ ПО КОЛОННЕ:

  1. Время суток (каждые полчаса), преобразованное в десятичное число.
  2. День недели (1-понедельник...7-воскресенье)
  3. Месяц года (1-12)
  4. День месяца (1-31)

ВЫХОД:

  1. Перекачиваемая вода (м3)

РЕДАКТИРОВАНИЕ Используя код @a_guest и изменяя некоторые параметры, такие как количество эпох или значение history, результаты действительно хороши:

Ссылка против прогноза Относительное отклонение


person Jvr    schedule 14.03.2017    source источник
comment
Несколько вопросов: пытаетесь ли вы предсказать будущие значения временных рядов на основе старых? Что именно представляют ваши данные (схема накачки)? Ожидается ли, что ваши данные будут очень шумными? Также ознакомьтесь с этой статьей. (это немного длинно, но информативно). И если у вас есть некоторое представление о том, что ожидать в качестве значений, вам может быть лучше использовать простую подгонку!   -  person a_guest    schedule 14.03.2017
comment
Цель состоит в том, чтобы получить схему водяного насоса (когда он должен включаться и отключаться в течение дня). Временной ряд представляет собой перекачиваемую воду (м3) с интервалом в полчаса. На самом деле, я видел эту статью, и мой код основан на этом веб-сайте (я прочитал все статьи). К сожалению, я делаю диссертацию, и использование ИНС обязательно. Собираюсь отредактировать пост, чтобы показать, какие есть входы.   -  person Jvr    schedule 14.03.2017
comment
Ваш вывод LSTM содержит 6 нейронов, а Dense состоит из 10. Более того - у вас всего шесть единиц для анализа истории. Я бы сначала попытался увеличить количество узлов в слое LSTM, скажем, до 40.   -  person Marcin Możejko    schedule 14.03.2017
comment
Спасибо, я не очень хорошо понимаю, что такое количество нейронов на слой. Я изменил количество узлов на 40, но результаты выглядят почти такими же (но обучение, кажется, улучшается, а также улучшается MSE!). Спасибо, это прогресс.   -  person Jvr    schedule 14.03.2017
comment
@Jvr Вы используете значение look_back для 1, что означает, что вы пытаетесь предсказать значение только на основе предыдущего значения (?). Вероятно, вам следует увеличить количество учитываемых точек данных. Также обратите внимание, что ваши данные, вероятно, демонстрируют разную сезонность с разной периодичностью (суточный цикл (больше воды в день) + годовой цикл (больше воды летом)). Чтобы узнать годовой цикл, вам нужно гораздо больше данных. IMO для прогноза вы должны охватить как минимум последние 48 часов (чтобы сеть уже видела этот шаблон хотя бы один раз).   -  person a_guest    schedule 14.03.2017
comment
Да, look_back равен 1 из-за моего последнего эксперимента по изменению параметров. Но раньше я пробовал с другими номерами. Сейчас попробую еще раз. Да, сезонность — это основная проблема, которую я обнаружил. Очевидно, что летом, например, откачиваемая вода значительно увеличивается, и на самом деле цель диссертации состоит в том, чтобы определить закономерность для выходных в каждом сезоне и закономерность для будних дней в каждом сезоне, но я не вообще уметь. Проблема в том, что я могу получить данные только за сегодня, завтра я смогу получить данные за завтра... но у меня нет исторических рядов.   -  person Jvr    schedule 14.03.2017
comment
@a_guest Я установил look_back=96 (2 дня) и получил тот же показатель MSE, но за 15 минут обучения вместо нескольких секунд. Я не знаю, как решить эту проблему, поскольку у меня больше нет данных для использования в качестве временных рядов. Я не знаю, сработает ли исправление сезонности...   -  person Jvr    schedule 14.03.2017
comment
Ну, на самом деле, глядя на ваш сюжет, предсказания не так уж плохи; сеть, кажется, может извлечь некоторые особенности из ваших данных, в частности, рост в течение дня, а затем резкое падение в конце дня. Это также примерно то, что показывает синяя кривая (оценки машин здесь, как правило, менее предвзяты). Если вы хотите, чтобы ваша сеть также изучала колебания, соответствующие более высоким частотам, это может помочь использовать больше нейронов или добавить в сеть дополнительные слои. Но остерегайтесь переоснащения, которое больше не запоминает шаблоны.   -  person a_guest    schedule 14.03.2017
comment
@a_guest Вот что я сделал; добавьте больше нейронов и дополнительных слоев, но, похоже, это не улучшится :(   -  person Jvr    schedule 14.03.2017
comment
Хотя это ограничило бы применение вашего кода, что произойдет, если вы добавите предыдущее значение y (y_{t-1}) к входным данным x (в момент времени x_{t})?   -  person Tomer Levinboim    schedule 14.03.2017
comment
@Tomer Levinboim Я думаю, что ваше предложение реализовано с помощью функции create_dataset () и установки look_back в качестве количества предыдущих шагов.   -  person Jvr    schedule 15.03.2017
comment
Спасибо... Я сделал быстрый комментарий, основанный на части INPUTS BY COLUMN.   -  person Tomer Levinboim    schedule 16.03.2017


Ответы (1)


Не ответ, но я делюсь здесь кодом, с помощью которого я получил следующие результаты:

Прогнозы

Относительное отклонение

Обратите внимание, что параметры сети выбраны произвольно, т.е. не оптимизированы. То есть вы, скорее всего, можете получить лучшие результаты, варьируя эти параметры. Также изменение значения history (или look_back в вашем случае), вероятно, оказывает значительное влияние на качество прогнозов.

from keras.models import Sequential
from keras.layers import Dense
import matplotlib.pyplot as plt
import numpy

numpy.random.seed(12)

history = 96


def generate_data():
    data = numpy.loadtxt('datos_horarios.csv', delimiter=',', dtype=float)
    # Normalize data.
    data[:, -1] /= numpy.max(data[:, -1])
    train_test_data = []
    for i in xrange(data.shape[0] - history - 1):
        # Include the reference value here, will be extracted later.
        train_test_data.append(data[i:i+history+1, -1].flatten())
    return numpy.array(train_test_data)

train_test_data = generate_data()
# Shuffle data set in order to randomly select training and test data.
numpy.random.shuffle(train_test_data)

n_samples = train_test_data.shape[0]
n_train_samples = int(0.8 * n_samples)

train_data = train_test_data[:n_train_samples, :-1]
train_data_reference = train_test_data[:n_train_samples, -1][:, None]

test_data = train_test_data[n_train_samples:, :-1]
test_data_reference = train_test_data[n_train_samples:, -1]

print 'Tranining data: ', train_data
print 'Reference values: ', train_data_reference


model = Sequential()
model.add(Dense(history, input_dim=history, activation='sigmoid'))
model.add(Dense(history/2, activation='sigmoid'))
model.add(Dense(history/4, activation='sigmoid'))
model.add(Dense(history/8, activation='sigmoid'))
model.add(Dense(history/16, activation='sigmoid'))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='mean_squared_error', metrics=['accuracy'])
model.summary()

model.fit(train_data, train_data_reference, shuffle=True, nb_epoch=200, batch_size=10)

# Use the complete data set to see the network performance.
# Regenerate data set because it was shuffled before.
train_test_data = generate_data()
test_data_predicted = model.predict(train_test_data[:, :-1]).flatten()
test_data_reference = train_test_data[:, -1]

relative_deviation = test_data_predicted/test_data_reference - 1.0
print 'Relative deviation: ', relative_deviation

plt.figure()
plt.plot(range(len(test_data_reference)), test_data_reference, 'b-', label='reference')
plt.plot(range(len(test_data_predicted)), test_data_predicted, 'r--', label='predicted')
plt.xlabel('test case #')
plt.ylabel('predictions')
plt.title('Reference values vs predicted values')
plt.legend()

plt.figure()
plt.plot(range(len(test_data_predicted)), relative_deviation, 'bx', label='relative deviation')
plt.xlabel('test case #')
plt.ylabel('relative deviation')
plt.title('Relative deviation of predicted values (predicted / reference - 1)')
plt.legend()

plt.show()
person a_guest    schedule 15.03.2017
comment
Этот ответ просто потрясающий! Вы мне очень помогли. И я очень хорошо понимаю ваш код. Как вы сказали, я изменю некоторые параметры архитектуры модели, и если я получу хорошие результаты, я обновлю сообщение. Большое спасибо! - person Jvr; 15.03.2017
comment
@Jvr Рад это слышать. Когда я смотрю на график вашего редактирования, у меня возникает ощущение, что ваша сеть могла переоснастить данные (т. е. он выучил данные наизусть). Без сомнения, он очень хорошо повторяет исходную кривую, однако это также означает, что он уже не так хорошо отражает лежащие в его основе паттерны (он содержит все микрофлуктуации, шум). Вы также должны проверить производительность, используя тестовые данные, которые отличны от обучающих данных. Также было бы разумно разделить обучающие и тестовые данные по времени. - person a_guest; 15.03.2017
comment
Да, переоснащен. Что я сделал, чтобы узнать это, так это сделать перекрестную проверку, и теперь я пытаюсь установить правильные параметры, чтобы получить правильный шаблон... - person Jvr; 15.03.2017