Tengo un modelo XGBoost que intenta predecir si una moneda subirá o bajará el próximo período (5 min). Tengo un conjunto de datos de 2004 a 2018. Divido los datos aleatorizados en 95% de validación de tren y 5% y la precisión en el conjunto de Validación es de hasta 55%. Cuando uso el modelo en un nuevo conjunto de prueba (datos de 2019), la precisión baja a menos del 51%.
¿Alguien puede explicar por qué podría ser eso?
Quiero decir, supongo que el modelo no ha "visto" (entrenado) los datos de validación más de lo que tiene los datos de la prueba, ¿puede realmente ser demasiado adecuado?
He adjuntado un modelo simple a continuación para ilustrar. Ese da el 54% en el conjunto de validación pero solo el 50.9% en el conjunto de prueba .
Agradecido por cualquier ayuda!
Nota: una teoría que tuve fue que, como algunas de las características se basan en datos históricos (p. Ej., Promedio móvil), podría tratarse de algún tipo de fuga de datos. Luego intenté corregir eso solo con datos de muestra que no formaban parte de la creación de la media móvil. Por ejemplo, si hay un promedio móvil de 3 períodos, entonces no muestreo / uso las filas de datos de 2 períodos anteriores. Eso no cambió nada, por lo que no está en el modelo a continuación.
NB2 El siguiente modelo es una versión simple de lo que uso. La razón de un conjunto de validación para mí es que uso un algoritmo genético para el ajuste de hiperparámetros, pero todo eso se elimina aquí para mayor claridad.
import pandas as pd
import talib as ta
from sklearn.utils import shuffle
pd.options.mode.chained_assignment = None
from sklearn.metrics import accuracy_score
# ## TRAINING AND VALIDATING
# ### Read in data
input_data_file = 'EURUSDM5_2004-2018_cleaned.csv' # For train and validation
df = pd.read_csv(input_data_file)
# ### Generate features
#######################
# SET TARGET
#######################
df['target'] = df['Close'].shift(-1)>df['Close'] # target is binary, i.e. either up or down next period
#######################
# DEFINE FEATURES
#######################
df['rsi'] = ta.RSI(df['Close'], 14)
# ### Treat the data
#######################
# FIND AND MAKE CATEGORICAL VARAIBLES AND DO ONE-HOT ENCODING
#######################
for col in df.drop('target',axis=1).columns: # Crude way of defining variables with few unique variants as categorical
if df[col].nunique() < 25:
df[col] = pd.Categorical(df[col])
cats = df.select_dtypes(include='category') # Do one-hot encoding for the categorical variables
for cat_col in cats:
df = pd.concat([df,pd.get_dummies(df[cat_col], prefix=cat_col,dummy_na=False)],axis=1).drop([cat_col],axis=1)
uints = df.select_dtypes(include='uint8')
for col in uints.columns: # Variables from the one-hot encoding is not created as categoricals so do it here
df[col] = df[col].astype('category')
#######################
# REMOVE ROWS WITH NO TRADES
#######################
df = df[df['Volume']>0]
#######################
# BALANCE NUMBER OF UP/DOWN IN TARGET SO THE MODEL CANNOT SIMPLY CHOOSE ONE AND BE SUCCESSFUL THAT WAY
#######################
df_true = df[df['target']==True]
df_false = df[df['target']==False]
len_true = len(df_true)
len_false = len(df_false)
rows = min(len_true,len_false)
df_true = df_true.head(rows)
df_false = df_false.head(rows)
df = pd.concat([df_true,df_false],ignore_index=True)
df = shuffle(df)
df.dropna(axis=0, how='any', inplace=True)
# ### Split data
df = shuffle(df)
split = int(0.95*len(df))
train_set = df.iloc[0:split]
val_set = df.iloc[split:-1]
# ### Generate X,y
X_train = train_set[train_set.columns.difference(['target', 'Datetime'])]
y_train = train_set['target']
X_val = val_set[val_set.columns.difference(['target', 'Datetime'])]
y_val = val_set['target']
# ### Scale
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
cont = X_train.select_dtypes(exclude='category') # Find columns with continous (not categorical) variables
X_train[cont.columns] = sc.fit_transform(X_train[cont.columns]) # Fit and transform
cont = X_val.select_dtypes(exclude='category') # Find columns with continous (not categorical) variables
X_val[cont.columns] = sc.transform(X_val[cont.columns]) # Transform
cats = X_train.select_dtypes(include='category')
for col in cats.columns:
X_train[col] = X_train[col].astype('uint8')
cats = X_val.select_dtypes(include='category')
for col in cats.columns:
X_val[col] = X_val[col].astype('uint8')
# ## MODEL
from xgboost import XGBClassifier
model = XGBClassifier()
model.fit(X_train, y_train)
predictions = model.predict(X_val)
acc = 100*accuracy_score(y_val, predictions)
print('{0:0.1f}%'.format(acc))
# # TESTING
input_data_file = 'EURUSDM5_2019_cleaned.csv' # For testing
df = pd.read_csv(input_data_file)
#######################
# SET TARGET
#######################
df['target'] = df['Close'].shift(-1)>df['Close'] # target is binary, i.e. either up or down next period
#######################
# DEFINE FEATURES
#######################
df['rsi'] = ta.RSI(df['Close'], 14)
#######################
# FIND AND MAKE CATEGORICAL VARAIBLES AND DO ONE-HOT ENCODING
#######################
for col in df.drop('target',axis=1).columns: # Crude way of defining variables with few unique variants as categorical
if df[col].nunique() < 25:
df[col] = pd.Categorical(df[col])
cats = df.select_dtypes(include='category') # Do one-hot encoding for the categorical variables
for cat_col in cats:
df = pd.concat([df,pd.get_dummies(df[cat_col], prefix=cat_col,dummy_na=False)],axis=1).drop([cat_col],axis=1)
uints = df.select_dtypes(include='uint8')
for col in uints.columns: # Variables from the one-hot encoding is not created as categoricals so do it here
df[col] = df[col].astype('category')
#######################
# REMOVE ROWS WITH NO TRADES
#######################
df = df[df['Volume']>0]
df.dropna(axis=0, how='any', inplace=True)
X_test = df[df.columns.difference(['target', 'Datetime'])]
y_test = df['target']
cont = X_test.select_dtypes(exclude='category') # Find columns with continous (not categorical) variables
X_test[cont.columns] = sc.transform(X_test[cont.columns]) # Transform
cats = X_test.select_dtypes(include='category')
for col in cats.columns:
X_test[col] = X_test[col].astype('uint8')
predictions = model.predict(X_test)
acc = 100*accuracy_score(y_test, predictions)
print('{0:0.1f}%'.format(acc))