LSTM имеет несколько функций: что я сделал не так? - PullRequest
1 голос
/ 07 апреля 2020

Код ниже прогнозирует значение Close (цены на акции) с 3 входами: Close, Open и Volume. Набор данных:

             Close    Open   Volume
Date                               
2019-09-20  5489.0  5389.0  1578781
2019-09-23  5420.0  5460.0   622325
2019-09-24  5337.5  5424.0   688395
2019-09-25  5343.5  5326.5   628849
2019-09-26  5387.5  5345.0   619344
...            ...     ...      ...
2020-03-30  4459.0  4355.0  1725236
2020-03-31  4715.0  4550.0  2433310
2020-04-01  4674.5  4596.0  1919728
2020-04-02  5050.0  4865.0  3860103
2020-04-03  5204.5  5050.0  3133078

[134 rows x 3 columns]

Информация:

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 134 entries, 2019-09-20 to 2020-04-03
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   Close   134 non-null    float64
 1   Open    134 non-null    float64
 2   Volume  134 non-null    int64  
dtypes: float64(2), int64(1)

Вопрос в том, как исправить скрипт, чтобы получить правильный прогноз с 3 функциями за последние 10 дней, потому что я получаю это:

Epoch 1/1
64/64 [==============================] - 6s 88ms/step - loss: 37135470.9219
[[32.588608]
 [32.587284]
 [32.586754]
 [32.587196]
 [32.58649 ]
 [32.58663 ]
 [32.586098]
 [32.58682 ]
 [32.586452]
 [32.588108]]
rmse: 4625.457010985681

Проблема остается, даже если я вообще уберу масштабирование (fit_transform). В других темах мне сказали, что нет необходимости масштабировать y_train. Полный код скрипта:

from math import sqrt
from numpy import concatenate
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding
from keras.layers import LSTM
import numpy as np
from datetime import datetime, timedelta
import yfinance as yf

start = (datetime.now() - timedelta(days=200)).strftime("%Y-%m-%d")
end = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
df = yf.download(tickers="LKOH.ME", start=start, end=end, interval="1d")
dataset = df.loc[start:end].filter(['Close', 'Open', 'Volume']).values
scaler = MinMaxScaler(feature_range=(0,1))

training_data_len = len(dataset) - 10 # last 10 days to test
train_data = dataset[0:int(training_data_len), :]
x_train = []
y_train = []

for i in range(60, len(train_data)):
    x_train.append(train_data[i-60:i, :]) # get all 3 features
    y_train.append(train_data[i, 0]) # 0 means we predict Close

x_train, y_train = np.array(x_train), np.array(y_train)
x_train = x_train.reshape((x_train.shape[0], x_train.shape[1]*x_train.shape[2])) # convert to 2d for fit_transform()
x_train = scaler.fit_transform(x_train)
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))

model = Sequential()
# Do I need to change it to input_shape=(x_train.shape[1], 3), because of 3 features?
model.add(LSTM(50, return_sequences=True, input_shape=(x_train.shape[1], 1)))
model.add(LSTM(50))
model.add(Dense(25))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x_train, y_train, batch_size=1, epochs=1)

test_data = dataset[training_data_len - 60:, :]
x_test = []
y_test = dataset[training_data_len:, 0]
for i in range(60, len(test_data)):
    x_test.append(test_data[i-60:i, :])

x_test = np.array(x_test)
x_test = x_test.reshape((x_test.shape[0], x_test.shape[1]*x_test.shape[2]))
x_test = scaler.fit_transform(x_test)
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

predictions = model.predict(x_test)
print(predictions)
print('rmse:', np.sqrt(np.mean(((predictions - y_test) ** 2))))

Ответы [ 2 ]

1 голос
/ 12 апреля 2020

Несмотря на то, что опубликованный ответ является технически правильным и содержит полезные ссылки, я нахожу немного раздражающим, что результаты подбора не имеют особого смысла (вы можете заметить, что предсказания постоянны, даже если y_test нет). Да, масштабирование исправляет потерю - при значениях порядка 1000 мера L2 делает любой алгоритм на основе градиента очень нестабильным, и ответ Ришаба обращается к этому. Вот мой фрагмент кода. Со следующими изменениями в дополнение к масштабированию:

  • Используйте больше данных. Я случайно выбрал 10000 дней, но если их будет больше, вы, вероятно, получите лучшие результаты. 200 баллов недостаточно, чтобы получить какую-либо конвергенцию лучше, чем прямая.
  • При большем количестве баллов используйте более крупную партию, так как в противном случае потребуется некоторое время, чтобы соответствовать
  • С более крупной партией, используйте больше эпох (хотя в этом случае более 3 не дают лучшей конвергенции)

Наконец, не просто смотрите на среднеквадратичное отклонение, наносите на график ваши данные. Малое среднеквадратичное среднеквадратичное отклонение не обязательно означает, что есть какое-либо значимое совпадение.

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

from math import sqrt
from numpy import concatenate
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding
from keras.layers import LSTM
import numpy as np
from datetime import datetime, timedelta
import yfinance as yf
from matplotlib import pyplot as plt

start = (datetime.now() - timedelta(days=10000)).strftime("%Y-%m-%d")
end = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
df = yf.download(tickers="LKOH.ME", start=start, end=end, interval="1d")
scaler = MinMaxScaler(feature_range=(0,1))
dataset = scaler.fit_transform(df.loc[start:end].filter(['Close', 'Open', 'Volume']).values)

training_data_len = len(dataset) - 10 # last 10 days to test
train_data = dataset[0:int(training_data_len), :]
x_train = []
y_train = []

for i in range(60, len(train_data)):
    x_train.append(train_data[i-60:i, :]) # get all 3 features
    y_train.append(train_data[i, 0]) # 0 means we predict Close

x_train, y_train = np.array(x_train), np.array(y_train)
x_train = x_train.reshape((x_train.shape[0], x_train.shape[1]*x_train.shape[2])) # convert to 2d for fit_transform()
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))

model = Sequential()
# Do I need to change it to input_shape=(x_train.shape[1], 3), because of 3 features?
model.add(LSTM(50, return_sequences=True, input_shape=(x_train.shape[1], 1)))
model.add(LSTM(50))
model.add(Dense(25))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x_train, y_train, batch_size=100, epochs=3)

test_data = dataset[training_data_len - 60:, :]
x_test = []
y_test = dataset[training_data_len:, 0]
for i in range(60, len(test_data)):
    x_test.append(test_data[i-60:i, :])

x_test = np.array(x_test)
x_test = x_test.reshape((x_test.shape[0], x_test.shape[1]*x_test.shape[2]))
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

predictions = model.predict(x_test)
print(predictions)
print('rmse:', np.sqrt(np.mean(((predictions - y_test) ** 2))))

Вот вывод:

>>> print(predictions)
[[0.64643383]
 [0.63276255]
 [0.6288108 ]
 [0.6320714 ]
 [0.6572328 ]
 [0.6998471 ]
 [0.7333    ]
 [0.7492812 ]
 [0.7503019 ]
 [0.75124526]]
>>> print('rmse:', np.sqrt(np.mean(((predictions - y_test) ** 2))))
rmse: 0.0712241892828221

Для построения графика, для данных обучения подходят

plt.plot(model.predict(x_train))
plt.plot(y_train)
plt.show()    

и для тестовых прогнозов

plt.plot(model.predict(x_test))
plt.plot(y_test)
plt.show()    
1 голос
/ 11 апреля 2020

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

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

После некоторых улучшений

from math import sqrt
from numpy import concatenate
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_squared_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Embedding
from tensorflow.keras.layers import LSTM
from tensorflow.keras.optimizers import SGD
import numpy as np
from datetime import datetime, timedelta
import yfinance as yf

start = (datetime.now() - timedelta(days=200)).strftime("%Y-%m-%d")
end = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
df = yf.download(tickers="LKOH.ME", start=start, end=end, interval="1d")
dataset = df.loc[start:end].filter(['Close', 'Open', 'Volume']).values
scaler = MinMaxScaler(feature_range=(0,1))


dataset = scaler.fit_transform(dataset)
training_data_len = len(dataset) - 10 # last 10 days to test
train_data = dataset[0:int(training_data_len), :]
x_train = []
y_train = []

for i in range(60, len(train_data)):
    x_train.append(train_data[i-60:i, :]) # get all 3 features
    y_train.append(train_data[i, 0]) # 0 means we predict Close


x_train, y_train = np.array(x_train), np.array(y_train)
x_train = x_train.reshape((x_train.shape[0], x_train.shape[1]*x_train.shape[2])) # convert to 2d for fit_transform()
x_train_scale = scaler.fit(x_train)
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))

model = Sequential()
# Do I need to change it to input_shape=(x_train.shape[1], 3), because of 3 features?
# yes, i did that.

model.add(LSTM(units=50,return_sequences=True, kernel_initializer='random_uniform', input_shape=(x_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(LSTM(units=50,return_sequences=True, kernel_initializer='random_uniform'))
model.add(Dropout(0.2))
model.add(LSTM(units=50,return_sequences=True, kernel_initializer='random_uniform'))
model.add(Dropout(0.2))
model.add(LSTM(units=50, kernel_initializer='random_uniform'))
model.add(Dropout(0.2))
model.add(Dense(units=25, activation='relu'))
model.add(Dense(units=1))

# compile model
model.compile(optimizer='adam', loss='mean_squared_error')
model.summary()
model.fit(x_train, y_train, batch_size=5, epochs=2)

test_data = dataset[training_data_len - 60:, :]
x_test = []
y_test = dataset[training_data_len:, 0]
for i in range(60, len(test_data)):
    x_test.append(test_data[i-60:i, :])

x_test = np.array(x_test)
x_test = x_test.reshape((x_test.shape[0], x_test.shape[1]*x_test.shape[2]))
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

predictions = model.predict(x_test)
# predictions = y_train_scale.inverse_transform(predictions)
print(predictions)
print('rmse:', np.sqrt(np.mean(((predictions - y_test) ** 2))))


Прогнозы 1:

start = (datetime.now() - timedelta(days=200)).strftime("%Y-%m-%d")

opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0, clipvalue=0.5)
model.compile(loss='mean_squared_error', optimizer=opt)
[[0.6151125 ]
 [0.6151124 ]
 [0.6151121 ]
 [0.6151119 ]
 [0.61511195]
 [0.61511236]
 [0.61511326]
 [0.615114  ]
 [0.61511385]
 [0.6151132 ]]
rmse: 0.24450220836260966

Прогноз 2:

start = (datetime.now() - timedelta(days=1000)).strftime("%Y-%m-%d")

model.compile(optimizer='adam', loss='mean_squared_error')
[[0.647125  ]
 [0.6458076 ]
 [0.6405072 ]
 [0.63450944]
 [0.6315386 ]
 [0.6384401 ]
 [0.65666   ]
 [0.68073314]
 [0.703547  ]
 [0.72095114]]
rmse: 0.1236932687978488

Цены на фондовом рынке крайне непредсказуемы и волатильны. Это означает, что в данных отсутствуют согласованные закономерности, позволяющие моделировать цены акций с течением времени практически идеально. Так что для разработки хорошей стратегии требуется много НИОКР.

Вещи, которые вы можете сделать:

  1. Добавить больше тренировочных данных в вашу модель, чтобы можно было лучше обобщить.
  2. Сделайте модель глубже. Поиграйте с моделью Hyperparameters, чтобы выжать производительность модели. Хорошую справку о настройке гиперпараметра можно найти здесь .

Более подробную информацию о различных других методах предварительной обработки данных и архитектуре модели можно найти по ссылкам ниже:

Прогнозы фондового рынка с помощью LSTM в Python

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

...