Почему нейронная сеть дает такой хороший результат?

Использую lstm, для предсказания временных рядов. И, к примеру, использую только 15% процентов всей выборки для обучения и её предсказания практически безупречны.

    scaler = MinMaxScaler(feature_range=(0, 1))
    dataset = scaler.fit_transform(yest_nadarayG.reshape(-1, 1))

    train_size = int(len(dataset) * 0.15)
    test_size = len(dataset) - train_size
    train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]



    #Создание матриц для внедрения их в модель МЛ
    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]
            dataX.append(a)
            dataY.append(dataset[i + look_back, 0])
        return np.array(dataX), np.array(dataY)



    look_back = 1
    trainX, trainY = create_dataset(train, look_back)
    testX, testY = create_dataset(test, look_back)

    trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))

    # LSTM
    model = Sequential()
    model.add(LSTM(4, input_shape=(1, look_back)))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)

    #trainPredict = model.predict(trainX)
    testPredict = model.predict(testX)

    #trainPredict = scaler.inverse_transform(trainPredict)
    trainY = scaler.inverse_transform([trainY])
    testPredict = scaler.inverse_transform(testPredict)
    testY = scaler.inverse_transform([testY])
    
   
    testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:, 0]))
    print('Test Score: %.2f RMSE' % (testScore))

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

    testPredictPlot = np.empty_like(dataset)
    testPredictPlot[:, :] = np.nan
    testPredictPlot[len(trainPredict) + (look_back * 2) + 1:len(dataset) - 1, :] = testPredict

    plt.plot(scaler.inverse_transform(dataset))
    plt.plot(testPredictPlot)
    plt.show()

введите сюда описание изображения

Что я делаю не так? Ведь нейросеть не может по такому маленькому кол-ву данных выдывать практически 100% результат?


Ответы (1 шт):

Автор решения: CrazyElf

На самом деле, судя по графику, не так уж и хорошо ваше предсказание. Сколько у вас получилось RMSE? Попробуйте сравнить хотя бы с таким бейзлайном (следующее значение = предыдущему), как правильно предложил Stanislav Volodarskiy:

testScore = math.sqrt(mean_squared_error(testY[0][1:], testY[0][:-1]))
print('Baseline Score: %.2f RMSE' % (testScore))

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

И практика показывает, что в случае графиков биржевых цен и подобных им данных, лучшее предсказание - это действительно цена в предыдущий момент времени. Вот если вы хотите предсказать на некоторый промежуток времени вперёд - там уже хитрые модели вроде ARIMA могут получить некоторое преимущество. А предсказание на шаг вперёд всегда будет лучшее то, которое равно предыдущему шагу.

Да, и ещё на задачах на временные ряды очень важна кросс-валидация, если хотите добиться реально хороших результатов. Обучение на едином куске тренировочных данных без использования кросс-валидации (причём, специального варианта кросс-валидации, заточенного под временные ряды) у вас всегда будет потом плохо работать на тестовых данных на самом деле.

Пример полученного мной результата:

Test Score: 5181.37 RMSE
Baseline Score: 2935.01 RMSE

введите сюда описание изображения

Код на котором я экспериментировал (lookback я разный пробовал, это не сильно влияет на самом деле):

import numpy as np
from sklearn.preprocessing import MinMaxScaler
from keras import Sequential
from keras.layers import LSTM,Dense
import math
from sklearn.metrics import mean_squared_error
import matplotlib.pylab as plt

dataset = ((np.random.rand(1000, 1) - 0.5)*10000).cumsum().reshape(-1, 1)

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

train_size = int(len(dataset) * 0.15)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]

#Создание матриц для внедрения их в модель МЛ
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]
        dataX.append(a)
        dataY.append(dataset[i + look_back, 0])
    return np.array(dataX), np.array(dataY)

look_back = 5
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)

trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))

# LSTM
model = Sequential()
model.add(LSTM(4, input_shape=(1, look_back)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=20, batch_size=1, verbose=0)

trainPredict = model.predict(trainX)
testPredict = model.predict(testX)

#trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])

testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:, 0]))
print('Test Score: %.2f RMSE' % (testScore))

testScore = math.sqrt(mean_squared_error(testY[0][1:], testY[0][:-1]))
print('Baseline Score: %.2f RMSE' % (testScore))

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

testPredictPlot = np.empty_like(dataset)
testPredictPlot[:, :] = np.nan
testPredictPlot[len(trainPredict) + (look_back * 2) + 1:len(dataset) - 1, :] = testPredict

plt.plot(scaler.inverse_transform(dataset))
plt.plot(testPredictPlot)
plt.show()
→ Ссылка