feat(Models): added MAE & RMSE metrics, fixed NaN & Inf values in data,

This commit is contained in:
Mark Aron Szulyovszky
2021-11-14 23:50:25 +01:00
parent 3581c8db83
commit 07302a7541
6 changed files with 51 additions and 225 deletions
+2
View File
@@ -47,6 +47,7 @@ def __load_df(path: str, prefix: str, add_features: bool, log_returns: bool) ->
df['mom_60'] = df['close'].pct_change(60) df['mom_60'] = df['close'].pct_change(60)
df['mom_90'] = df['close'].pct_change(90) df['mom_90'] = df['close'].pct_change(90)
df = df.replace([np.inf, -np.inf], 0.)
df = df.drop(columns=['open', 'high', 'low', 'close']) df = df.drop(columns=['open', 'high', 'low', 'close'])
df.columns = [prefix + "_" + c for c in df.columns] df.columns = [prefix + "_" + c for c in df.columns]
return df return df
@@ -54,4 +55,5 @@ def __load_df(path: str, prefix: str, add_features: bool, log_returns: bool) ->
# %% # %%
def create_target_cum_forward_returns(df: pd.DataFrame, source_column: str, period: int) -> pd.DataFrame: def create_target_cum_forward_returns(df: pd.DataFrame, source_column: str, period: int) -> pd.DataFrame:
df['target'] = df[source_column].diff(period).shift(-period) df['target'] = df[source_column].diff(period).shift(-period)
df = df.iloc[:-period]
return df return df
-97
View File
@@ -1,97 +0,0 @@
#%% Import all the stuff, load data, define constants
from load_data import load_files, create_target_cum_forward_returns
import pandas as pd
from tensorflow import keras
from utils.normalize import normalize
import tensorflow as tf
from utils.visualize import visualize_loss
data = load_files('data/', add_features=True, log_returns=False)
data.reset_index(drop=True, inplace=True)
data = data[[column for column in data.columns if not column.endswith('volume')]]
data = data[["BTC_returns", "BTC_mom_10", "BTC_mom_20", "BTC_mom_30", "BTC_mom_60", "BTC_vol_10", "BTC_vol_20", "BTC_vol_60", "day_month", "day_week", "month"]]
ticker_to_predict = 'BTC_returns'
data = create_target_cum_forward_returns(data, ticker_to_predict, 10)
learning_rate = 0.002
batch_size = 64
epochs = 500
split_fraction = 0.715
train_split = int(split_fraction * int(data.shape[0]))
past = 10
future = 1
start = past + future
end = start + train_split
#%% split data into training - validation sets
train_data = data.loc[0 : train_split - 1]
val_data = data.loc[train_split:]
#%% create features and target for training set & keras dataset
x_train = normalize(train_data).values
# x_train = normalize(train_data).drop(ticker_to_predict, axis=1).values
y_train = normalize(data).iloc[start:end][ticker_to_predict].values
dataset_train = keras.preprocessing.timeseries_dataset_from_array(
x_train,
y_train,
sequence_length=past,
batch_size=batch_size,
)
#%% create features and target for validation set & keras dataset
x_end = len(val_data) - past - future
label_start = train_split + past + future
x_val = normalize(val_data).iloc[:x_end].values
# x_val = normalize(val_data).iloc[:x_end].drop(ticker_to_predict, axis=1).values
y_val = normalize(data).iloc[label_start:][ticker_to_predict].values
dataset_val = keras.utils.timeseries_dataset_from_array(
x_val,
y_val,
sequence_length=past,
batch_size=batch_size,
)
#%%
for batch in dataset_train.take(10):
batch_inputs, batch_targets = batch
print("Input shape:", batch_inputs.shape)
print("Target shape:", batch_targets.shape)
print(batch_inputs)
print(batch_targets)
# %%
model = keras.Sequential()
model.add(keras.layers.Dense(units = 10, activation = 'sigmoid', input_shape=(batch_inputs.shape[1], batch_inputs.shape[2])))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(units = 4, activation = 'sigmoid'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(units = 1))
optimizer = keras.optimizers.Adam(learning_rate=learning_rate, clipnorm=1.0)
model.compile(optimizer=optimizer, loss="mean_squared_error")
model.summary()
# %%
path_checkpoint = "model_checkpoint.h5"
history = model.fit(
dataset_train,
epochs=epochs,
validation_data=dataset_val,
)
# %%
visualize_loss(history, "Training and Validation Loss")
+34 -18
View File
@@ -1,23 +1,28 @@
#%% Import all the stuff, load data, define constants #%% Import all the stuff, load data, define constants
from sklearn.utils import shuffle
from load_data import load_files, create_target_cum_forward_returns from load_data import load_files, create_target_cum_forward_returns
import pandas as pd import pandas as pd
from tensorflow import keras from tensorflow import keras
from utils.normalize import normalize from utils.normalize import normalize
import tensorflow as tf import tensorflow as tf
from utils.visualize import visualize_loss from utils.visualize import visualize_loss
from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import MinMaxScaler
from utils.evaluate import print_metrics
import numpy as np
from utils.rolling import rolling_window
data = load_files('data/', add_features=True, log_returns=False) data = load_files('data/', add_features=True, log_returns=False)
data.reset_index(drop=True, inplace=True) data.reset_index(drop=True, inplace=True)
data = data[[column for column in data.columns if not column.endswith('volume')]] data = data[[column for column in data.columns if not column.endswith('volume')]]
data = data[["BTC_returns", "BTC_mom_10", "BTC_mom_20", "BTC_mom_30", "BTC_mom_60", "BTC_vol_10", "BTC_vol_20", "BTC_vol_60", "day_month", "day_week", "month"]] data = data[["BTC_returns", "BTC_mom_10", "BTC_mom_20", "BTC_mom_30", "BTC_mom_60", "BTC_vol_10", "BTC_vol_20", "BTC_vol_60", "day_month", "day_week", "month"]]
ticker_to_predict = 'BTC_returns' target_col = 'target'
data = create_target_cum_forward_returns(data, ticker_to_predict, 10) data = create_target_cum_forward_returns(data, 'BTC_returns', 10)
learning_rate = 0.002 learning_rate = 0.002
batch_size = 64 batch_size = 64
epochs = 500 epochs = 100
split_fraction = 0.715 split_fraction = 0.715
train_split = int(split_fraction * int(data.shape[0])) train_split = int(split_fraction * int(data.shape[0]))
@@ -33,12 +38,11 @@ train_data = data.loc[0 : train_split - 1]
val_data = data.loc[train_split:] val_data = data.loc[train_split:]
#%% create features and target for training set & keras dataset #%% create features and target for training set & keras dataset
feature_scaler = StandardScaler() feature_scaler = MinMaxScaler(feature_range= (-1, 1))
target_scaler = StandardScaler() target_scaler = MinMaxScaler(feature_range= (-1, 1))
x_train = feature_scaler.fit_transform(train_data.values) # you get the mean and std x_train = feature_scaler.fit_transform(train_data.drop(target_col, axis=1).values) # you get the mean and std
# x_train = normalize(train_data).drop(ticker_to_predict, axis=1).values y_train = target_scaler.fit_transform(data.iloc[start:end][target_col].values.reshape(-1, 1))
y_train = target_scaler.fit_transform(data.iloc[start:end][ticker_to_predict].values.reshape(-1, 1))
dataset_train = keras.preprocessing.timeseries_dataset_from_array( dataset_train = keras.preprocessing.timeseries_dataset_from_array(
x_train, x_train,
@@ -52,15 +56,15 @@ dataset_train = keras.preprocessing.timeseries_dataset_from_array(
x_end = len(val_data) - past - future x_end = len(val_data) - past - future
label_start = train_split + past + future label_start = train_split + past + future
x_val = feature_scaler.transform(val_data.iloc[:x_end].values) # you use the training data's mean and std x_val = feature_scaler.transform(val_data.drop(target_col, axis=1).iloc[:x_end].values) # you use the training data's mean and std
# x_val = normalize(val_data).iloc[:x_end].drop(ticker_to_predict, axis=1).values y_val = target_scaler.transform(data.iloc[label_start:][target_col].values.reshape(-1, 1))
y_val = target_scaler.transform(data.iloc[label_start:][ticker_to_predict].values.reshape(-1, 1))
dataset_val = keras.utils.timeseries_dataset_from_array( dataset_val = keras.utils.timeseries_dataset_from_array(
x_val, x_val,
y_val, y_val,
sequence_length=past, sequence_length=past,
batch_size=batch_size, batch_size=batch_size,
shuffle=False,
) )
@@ -72,22 +76,25 @@ for batch in dataset_train.take(10):
print("Input shape:", batch_inputs.shape) print("Input shape:", batch_inputs.shape)
print("Target shape:", batch_targets.shape) print("Target shape:", batch_targets.shape)
print(batch_inputs) # print(batch_inputs)
print(batch_targets) # print(batch_targets)
# %% # %%
model = keras.Sequential() model = keras.Sequential()
model.add(keras.layers.LSTM(units = 32, return_sequences = True, activation = 'sigmoid', input_shape=(batch_inputs.shape[1], batch_inputs.shape[2]))) model.add(keras.layers.LSTM(units = 10, return_sequences = True, activation = 'sigmoid', input_shape=(batch_inputs.shape[1], batch_inputs.shape[2])))
model.add(keras.layers.Dropout(0.4)) model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(units = 10, activation = 'sigmoid')) model.add(keras.layers.Dense(units = 64, activation = 'sigmoid'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(units = 32, activation = 'sigmoid'))
model.add(keras.layers.Dropout(0.4)) model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(units = 1)) model.add(keras.layers.Dense(units = 1))
optimizer = keras.optimizers.Adam(learning_rate=learning_rate, clipnorm=1.0) optimizer = keras.optimizers.Adam(learning_rate=learning_rate)
model.compile(optimizer=optimizer, loss="mean_squared_error") model.compile(optimizer=optimizer, loss="mean_squared_error")
model.summary() model.summary()
# %% # %%
path_checkpoint = "model_checkpoint.h5" path_checkpoint = "model_checkpoint.h5"
history = model.fit( history = model.fit(
@@ -95,6 +102,15 @@ history = model.fit(
epochs=epochs, epochs=epochs,
validation_data=dataset_val, validation_data=dataset_val,
) )
# %%
#%%
pred = model.predict(rolling_window(x_val, 11))
pred = pred.reshape(pred.shape[0], 1)
pred = target_scaler.inverse_transform(pred)
print_metrics(y_val, pred)
#%%
visualize_loss(history, "Training and Validation Loss") visualize_loss(history, "Training and Validation Loss")
-110
View File
@@ -1,110 +0,0 @@
#%%
from math import sqrt
from numpy import concatenate
import numpy as np
from matplotlib import pyplot
from pandas import read_csv
from pandas import DataFrame
from pandas import concat
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
from keras.layers import LSTM
#%%
# convert series to supervised learning
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
if i == 0:
names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
else:
names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
#%% load dataset
from load_data import load_files
data = load_files('data/', True)
data.reset_index(drop=True, inplace=True)
data = data[[column for column in data.columns if not column.endswith('volume')]]
data = data[["BTC_returns", "BTC_vol_10"]]
values = data.values
# ensure all data is float
# values = values.astype('float32')
#%%
np.isposinf(values).sum()
#%% normalize features
scaler = MinMaxScaler(feature_range=(-1, 1))
scaled = scaler.fit_transform(values)
# specify the number of lag hours
past = 10
n_features = 8
#%% frame as supervised learning
reframed = series_to_supervised(scaled, past, 1)
print(reframed.shape)
#%% split into train and test sets
values = reframed.values
n_train_hours = 365 * 24
train = values[:n_train_hours, :]
test = values[n_train_hours:, :]
# split into input and outputs
n_obs = past * n_features
train_X, train_y = train[:, :n_obs], train[:, -n_features]
test_X, test_y = test[:, :n_obs], test[:, -n_features]
print(train_X.shape, len(train_X), train_y.shape)
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], past, n_features))
test_X = test_X.reshape((test_X.shape[0], past, n_features))
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)
#%% design network
model = Sequential()
model.add(LSTM(50, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(1))
model.compile(loss='mae', optimizer='adam')
#%% fit network
history = model.fit(train_X, train_y, epochs=50, batch_size=72, validation_data=(test_X, test_y), verbose=2, shuffle=False)
# plot history
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
# make a prediction
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], n_hours*n_features))
# invert scaling for forecast
inv_yhat = concatenate((yhat, test_X[:, -7:]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
# invert scaling for actual
test_y = test_y.reshape((len(test_y), 1))
inv_y = concatenate((test_y, test_X[:, -7:]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
# calculate RMSE
rmse = sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)
+9
View File
@@ -0,0 +1,9 @@
from math import sqrt
from sklearn.metrics import mean_squared_error, mean_absolute_error
def print_metrics(y_true, y_pred):
rmse = sqrt(mean_squared_error(y_true, y_pred))
print("RMSE: %.2f" % rmse)
mae = mean_absolute_error(y_true, y_pred)
print("MAE: %.2f" % mae)
+6
View File
@@ -0,0 +1,6 @@
import numpy as np
def rolling_window(a, window):
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)