mirror of
https://github.com/webclinic017/drift.git
synced 2026-08-21 14:58:11 +00:00
feat(Transformations): replaced feature selection pre-processing step with online version (with cache) (#170)
* feat(Transformations): removed feature-selection pre-processing step completely * fix(Core): removed unnecessary `original_X` * fix(Transformations): use the X_expanding_window to transform subsequent data * fix(RFE): should check for model correctly * fix(Config): only re-train the model every 40 timestamp * fix(MetaLabeling): pass in the correct X to meta-labeling step * fix(Transformation): PCA should at least keep as many features as sliding_window_size * feat(Transformations): cache transformations across the same asset * fix(Tests): missing preloaded_transformations arg * chore(Config): got rid of unnecessary 'classification_models' and 'regression_models' dictionary keys
This commit is contained in:
+7
-7
@@ -30,7 +30,7 @@ def get_dev_config() -> tuple[dict, dict, dict]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
regression_models = ["Lasso"]
|
regression_models = ["Lasso"]
|
||||||
classification_models = ["LR_two_class"]
|
classification_models = ["LogisticRegression_two_class"]
|
||||||
|
|
||||||
model_config = dict(
|
model_config = dict(
|
||||||
primary_models = regression_models if data_config['method'] == 'regression' else classification_models,
|
primary_models = regression_models if data_config['method'] == 'regression' else classification_models,
|
||||||
@@ -52,7 +52,7 @@ def get_default_ensemble_config() -> tuple[dict, dict, dict]:
|
|||||||
expanding_window_meta_labeling = True,
|
expanding_window_meta_labeling = True,
|
||||||
sliding_window_size_primary = 380,
|
sliding_window_size_primary = 380,
|
||||||
sliding_window_size_meta_labeling = 240,
|
sliding_window_size_meta_labeling = 240,
|
||||||
retrain_every = 20,
|
retrain_every = 40,
|
||||||
scaler = 'minmax', # 'normalize' 'minmax' 'standardize'
|
scaler = 'minmax', # 'normalize' 'minmax' 'standardize'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ def get_default_ensemble_config() -> tuple[dict, dict, dict]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
regression_models = ["Lasso", "KNN", "RFR"]
|
regression_models = ["Lasso", "KNN", "RFR"]
|
||||||
classification_models = ["LR_two_class", "LDA", "NB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
classification_models = ["LogisticRegression_two_class", "LDA", "NB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
||||||
meta_labeling_models = ['LR_two_class', 'LGBM']
|
meta_labeling_models = ['LogisticRegression_two_class', 'LGBM']
|
||||||
ensemble_model = 'Average'
|
ensemble_model = 'Average'
|
||||||
|
|
||||||
model_config = dict(
|
model_config = dict(
|
||||||
@@ -97,7 +97,7 @@ def get_lightweight_ensemble_config() -> tuple[dict, dict, dict]:
|
|||||||
expanding_window_meta_labeling = True,
|
expanding_window_meta_labeling = True,
|
||||||
sliding_window_size_primary = 380,
|
sliding_window_size_primary = 380,
|
||||||
sliding_window_size_meta_labeling = 240,
|
sliding_window_size_meta_labeling = 240,
|
||||||
retrain_every = 20,
|
retrain_every = 40,
|
||||||
scaler = 'minmax', # 'normalize' 'minmax' 'standardize'
|
scaler = 'minmax', # 'normalize' 'minmax' 'standardize'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ def get_lightweight_ensemble_config() -> tuple[dict, dict, dict]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
regression_models = ["Lasso", "KNN"]
|
regression_models = ["Lasso", "KNN"]
|
||||||
classification_models = ['LR_two_class', 'SVC']
|
classification_models = ['LogisticRegression_two_class', 'SVC']
|
||||||
meta_labeling_models = ['LR_two_class', 'LGBM']
|
meta_labeling_models = ['LogisticRegression_two_class', 'LGBM']
|
||||||
ensemble_model = 'Average'
|
ensemble_model = 'Average'
|
||||||
|
|
||||||
model_config = dict(
|
model_config = dict(
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ def __preprocess_feature_extractors_config(data_dict: dict) -> dict:
|
|||||||
return data_dict
|
return data_dict
|
||||||
|
|
||||||
def __preprocess_model_config(model_config:dict, method:str) -> dict:
|
def __preprocess_model_config(model_config:dict, method:str) -> dict:
|
||||||
model_map, _, _, _, _ = get_model_map(model_config)
|
model_map = get_model_map(model_config)
|
||||||
model_config['primary_models'] = [(model_name, model_map[method + '_models'][model_name]) for model_name in model_config['primary_models']]
|
model_config['primary_models'] = [(model_name, model_map['primary_models'][model_name]) for model_name in model_config['primary_models']]
|
||||||
if len(model_config['meta_labeling_models']) > 0:
|
if len(model_config['meta_labeling_models']) > 0:
|
||||||
model_config['meta_labeling_models'] = [(model_name, model_map[method + '_models'][model_name]) for model_name in model_config['meta_labeling_models']]
|
model_config['meta_labeling_models'] = [(model_name, model_map['primary_models'][model_name]) for model_name in model_config['meta_labeling_models']]
|
||||||
if model_config['ensemble_model'] is not None:
|
if model_config['ensemble_model'] is not None:
|
||||||
model_config['ensemble_model'] = (model_config['ensemble_model'], model_map['ensemble_models'][model_config['ensemble_model']])
|
model_config['ensemble_model'] = (model_config['ensemble_model'], model_map['ensemble_models'][model_config['ensemble_model']])
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,8 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from utils.helpers import has_enough_samples_to_train
|
from utils.helpers import has_enough_samples_to_train
|
||||||
from feature_selection.dim_reduction import reduce_dimensionality
|
|
||||||
from models.model_map import get_model_map
|
|
||||||
from feature_selection.feature_selection import select_features
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_data(X:pd.DataFrame, y:pd.Series, configs: dict) -> tuple[pd.DataFrame,pd.DataFrame]:
|
|
||||||
|
|
||||||
model_config, training_config, data_config = itemgetter('model_config', 'training_config', 'data_config')(configs)
|
|
||||||
_, _, _, default_feature_selector_regression, default_feature_selector_classification = get_model_map(model_config)
|
|
||||||
|
|
||||||
original_X = X.copy()
|
|
||||||
|
|
||||||
# 2b. Feature Selection
|
|
||||||
print("Feature Selection started")
|
|
||||||
# TODO: this needs to be done per model!
|
|
||||||
backup_model = default_feature_selector_regression if data_config['method'] == 'regression' else default_feature_selector_classification
|
|
||||||
X = select_features(X = X, y = y, model = model_config['primary_models'][0][1], n_features_to_select = training_config['n_features_to_select'], backup_model = backup_model, scaling = training_config['scaler'])
|
|
||||||
|
|
||||||
return X, original_X
|
|
||||||
|
|
||||||
def check_data(X:pd.DataFrame, y:pd.Series, training_config:dict):
|
def check_data(X:pd.DataFrame, y:pd.Series, training_config:dict):
|
||||||
""" Returns True if data is valid, else returns False."""
|
""" Returns True if data is valid, else returns False."""
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import pandas as pd
|
|
||||||
from sklearn.decomposition import PCA
|
|
||||||
|
|
||||||
def reduce_dimensionality(X: pd.DataFrame, no_of_compoments: int) -> pd.DataFrame:
|
|
||||||
pca = PCA(n_components= no_of_compoments)
|
|
||||||
result = pd.DataFrame(pca.fit_transform(X), index= X.index)
|
|
||||||
result.columns = ['PCA_' + str(i) for i in range(1, no_of_compoments+1)]
|
|
||||||
return result
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
from sklearn.feature_selection import RFE
|
|
||||||
from sklearn.model_selection import TimeSeriesSplit
|
|
||||||
import pandas as pd
|
|
||||||
from models.base import Model
|
|
||||||
from models.sklearn import SKLearnModel
|
|
||||||
from utils.scaler import get_scaler
|
|
||||||
from utils.types import ScalerTypes
|
|
||||||
# from utils.hashing import hash_df, hash_series
|
|
||||||
# from diskcache import Cache
|
|
||||||
# cache = Cache(".cachedir/feature_selection")
|
|
||||||
|
|
||||||
# def select_features(**kwargs) -> pd.DataFrame:
|
|
||||||
# hashed = kwargs['data_config_hash'] + kwargs['model'].get_name() + str(kwargs['n_features_to_select']) + kwargs['backup_model'].get_name() + kwargs['scaling']
|
|
||||||
# if hashed in cache:
|
|
||||||
# return cache.get(hashed)
|
|
||||||
# else:
|
|
||||||
# return_value = __select_features(**kwargs)
|
|
||||||
# cache[hashed] = return_value
|
|
||||||
# return return_value
|
|
||||||
|
|
||||||
def select_features(X: pd.DataFrame, y: pd.Series, model: Model, n_features_to_select: int, backup_model: SKLearnModel, scaling: ScalerTypes) -> pd.DataFrame:
|
|
||||||
''' Select features using RFECV, returns a pd.DataFrame (X) with only the selected features.'''
|
|
||||||
if model.model_type != 'ml': return X
|
|
||||||
|
|
||||||
# 2. Recursive feature selection
|
|
||||||
cv = TimeSeriesSplit(n_splits=5)
|
|
||||||
scaler = get_scaler(scaling)
|
|
||||||
X_scaled = scaler.fit_transform(X, y)
|
|
||||||
|
|
||||||
feat_selector_model = model.model
|
|
||||||
if hasattr(feat_selector_model, 'feature_importances_') == False and hasattr(feat_selector_model, 'coef_') == False:
|
|
||||||
feat_selector_model = backup_model.model
|
|
||||||
|
|
||||||
# selector = RFECV(feat_selector_model, cv = cv, step=5, min_features_to_select=min_features_to_select)
|
|
||||||
step = 0.05
|
|
||||||
selector = RFE(feat_selector_model, n_features_to_select= n_features_to_select, step=step)
|
|
||||||
selector = selector.fit(X_scaled, y)
|
|
||||||
print("Kept %d features out of %d" % (selector.n_features_, X_scaled.shape[1]))
|
|
||||||
|
|
||||||
return pd.DataFrame(X[X.columns[selector.support_]], index= X.index)
|
|
||||||
@@ -9,7 +9,6 @@ class StaticAverageModel(Model):
|
|||||||
|
|
||||||
data_transformation = 'original'
|
data_transformation = 'original'
|
||||||
only_column = 'model_'
|
only_column = 'model_'
|
||||||
feature_selection = 'off'
|
|
||||||
model_type = 'static'
|
model_type = 'static'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -7,9 +7,8 @@ import numpy as np
|
|||||||
|
|
||||||
class Model(ABC):
|
class Model(ABC):
|
||||||
|
|
||||||
|
method: Literal["regression", "classification"]
|
||||||
data_transformation: Literal["transformed", "original"]
|
data_transformation: Literal["transformed", "original"]
|
||||||
feature_selection: Literal["on", "off"]
|
|
||||||
# data_format: Literal["wide", "narrow"]
|
|
||||||
only_column: Optional[str]
|
only_column: Optional[str]
|
||||||
model_type: Literal['ml', 'static']
|
model_type: Literal['ml', 'static']
|
||||||
predict_window_size: Literal['single_timestamp', 'window_size']
|
predict_window_size: Literal['single_timestamp', 'window_size']
|
||||||
|
|||||||
+91
-102
@@ -1,111 +1,100 @@
|
|||||||
from models.sklearn import SKLearnModel
|
from models.sklearn import SKLearnModel
|
||||||
from sklearn.ensemble import RandomForestClassifier
|
from sklearnex.ensemble import RandomForestClassifier
|
||||||
from sklearn.ensemble import RandomForestRegressor
|
from sklearnex.ensemble import RandomForestRegressor
|
||||||
|
|
||||||
model_map = {
|
default_feature_selector_classification = SKLearnModel(RandomForestClassifier(n_jobs=-1, max_depth=20, random_state=1), 'classification')
|
||||||
"regression_models": dict(),
|
default_feature_selector_regression = SKLearnModel(RandomForestRegressor(n_jobs=-1, max_depth=20, random_state=1), 'regression')
|
||||||
"classification_models": dict(),
|
|
||||||
"ensemble_models": dict()
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_model_map(config:dict):
|
def get_model_map(config:dict):
|
||||||
|
|
||||||
if len(config['primary_models']) > 0 and isinstance(config['primary_models'][0], str):
|
model_map = {
|
||||||
print("Going to Load models")
|
"primary_models": dict(),
|
||||||
combined_list = config['primary_models'] + config['meta_labeling_models'] + [config['ensemble_model']]
|
"ensemble_models": dict(),
|
||||||
for model_name in combined_list:
|
}
|
||||||
if model_name == 'LR':
|
|
||||||
from sklearn.linear_model import LinearRegression
|
|
||||||
model_map['regression_models']['LR'] = SKLearnModel(LinearRegression(n_jobs=-1))
|
|
||||||
elif model_name == 'Lasso':
|
|
||||||
from sklearn.linear_model import Lasso
|
|
||||||
model_map['regression_models']['Lasso'] = SKLearnModel(Lasso(alpha=100, random_state=1))
|
|
||||||
elif model_name == 'Ridge':
|
|
||||||
from sklearn.linear_model import Ridge
|
|
||||||
model_map['regression_models']['Ridge'] = SKLearnModel(Ridge(alpha=0.1))
|
|
||||||
elif model_name == 'BayesianRidge':
|
|
||||||
from sklearn.linear_model import BayesianRidge
|
|
||||||
model_map['regression_models']['BayesianRidge'] = SKLearnModel(BayesianRidge())
|
|
||||||
elif model_name == 'KNN':
|
|
||||||
from sklearnex.neighbors import KNeighborsRegressor
|
|
||||||
model_map['regression_models']['KNN'] = SKLearnModel(KNeighborsRegressor(n_neighbors=25))
|
|
||||||
elif model_name == 'AB':
|
|
||||||
from sklearn.ensemble import AdaBoostRegressor
|
|
||||||
model_map['regression_models']['AB'] = SKLearnModel(AdaBoostRegressor(random_state=1))
|
|
||||||
elif model_name == 'MLP':
|
|
||||||
from sklearn.neural_network import MLPRegressor
|
|
||||||
model_map['regression_models']['MLP'] = SKLearnModel(MLPRegressor(hidden_layer_sizes=(100,20), max_iter=1000))
|
|
||||||
elif model_name == 'RFR':
|
|
||||||
# from sklearn.ensemble import RandomForestRegressor
|
|
||||||
model_map['regression_models']['RFR'] = SKLearnModel(RandomForestRegressor(n_jobs=-1, max_depth=20, random_state=1))
|
|
||||||
elif model_name == 'SVR':
|
|
||||||
from sklearnex.svm import SVR
|
|
||||||
model_map['regression_models']['SVR'] = SKLearnModel(SVR(kernel='rbf', C=1e3, gamma=0.1))
|
|
||||||
elif model_name == 'StaticNaive':
|
|
||||||
from models.naive import StaticNaiveModel
|
|
||||||
model_map['regression_models']['StaticNaive'] = StaticNaiveModel()
|
|
||||||
elif model_name == 'DNN':
|
|
||||||
from models.neural import LightningNeuralNetModel
|
|
||||||
from models.pytorch.neural_nets import MultiLayerPerceptron
|
|
||||||
import torch.nn.functional as F
|
|
||||||
model_map['regression_models']['DNN'] = LightningNeuralNetModel(
|
|
||||||
MultiLayerPerceptron(
|
|
||||||
hidden_layers_ratio = [1.0],
|
|
||||||
probabilities = False,
|
|
||||||
loss_function = F.mse_loss),
|
|
||||||
max_epochs=15
|
|
||||||
)
|
|
||||||
|
|
||||||
|
combined_list = config['primary_models'] + config['meta_labeling_models'] + [config['ensemble_model']]
|
||||||
|
for model_name in combined_list:
|
||||||
|
if model_name == 'LinearRegression':
|
||||||
|
from sklearn.linear_model import LinearRegression
|
||||||
|
model_map['primary_models']['LR'] = SKLearnModel(LinearRegression(n_jobs=-1), 'regression')
|
||||||
|
elif model_name == 'Lasso':
|
||||||
|
from sklearn.linear_model import Lasso
|
||||||
|
model_map['primary_models']['Lasso'] = SKLearnModel(Lasso(alpha=100, random_state=1), 'regression')
|
||||||
|
elif model_name == 'Ridge':
|
||||||
|
from sklearn.linear_model import Ridge
|
||||||
|
model_map['primary_models']['Ridge'] = SKLearnModel(Ridge(alpha=0.1), 'regression')
|
||||||
|
elif model_name == 'BayesianRidge':
|
||||||
|
from sklearn.linear_model import BayesianRidge
|
||||||
|
model_map['primary_models']['BayesianRidge'] = SKLearnModel(BayesianRidge(), 'regression')
|
||||||
|
elif model_name == 'KNN':
|
||||||
|
from sklearnex.neighbors import KNeighborsRegressor
|
||||||
|
model_map['primary_models']['KNN'] = SKLearnModel(KNeighborsRegressor(n_neighbors=25), 'regression')
|
||||||
|
elif model_name == 'AB':
|
||||||
|
from sklearn.ensemble import AdaBoostRegressor
|
||||||
|
model_map['primary_models']['AB'] = SKLearnModel(AdaBoostRegressor(random_state=1), 'regression')
|
||||||
|
elif model_name == 'MLP':
|
||||||
|
from sklearn.neural_network import MLPRegressor
|
||||||
|
model_map['primary_models']['MLP'] = SKLearnModel(MLPRegressor(hidden_layer_sizes=(100,20), max_iter=1000), 'regression')
|
||||||
|
elif model_name == 'RFR':
|
||||||
|
# from sklearn.ensemble import RandomForestRegressor
|
||||||
|
model_map['primary_models']['RFR'] = SKLearnModel(RandomForestRegressor(n_jobs=-1, max_depth=20, random_state=1), 'regression')
|
||||||
|
elif model_name == 'SVR':
|
||||||
|
from sklearnex.svm import SVR
|
||||||
|
model_map['primary_models']['SVR'] = SKLearnModel(SVR(kernel='rbf', C=1e3, gamma=0.1), 'regression')
|
||||||
|
elif model_name == 'StaticNaive':
|
||||||
|
from models.naive import StaticNaiveModel
|
||||||
|
model_map['primary_models']['StaticNaive'] = StaticNaiveModel()
|
||||||
|
elif model_name == 'DNN':
|
||||||
|
from models.neural import LightningNeuralNetModel
|
||||||
|
from models.pytorch.neural_nets import MultiLayerPerceptron
|
||||||
|
import torch.nn.functional as F
|
||||||
|
model_map['primary_models']['DNN'] = LightningNeuralNetModel(
|
||||||
|
MultiLayerPerceptron(
|
||||||
|
hidden_layers_ratio = [1.0],
|
||||||
|
probabilities = False,
|
||||||
|
loss_function = F.mse_loss),
|
||||||
|
max_epochs=15
|
||||||
|
)
|
||||||
|
|
||||||
elif model_name == 'LR_two_class':
|
elif model_name == 'LogisticRegression_two_class':
|
||||||
from sklearn.linear_model import LogisticRegression
|
from sklearn.linear_model import LogisticRegression
|
||||||
model_map['classification_models']['LR_two_class'] = SKLearnModel(LogisticRegression(C=10, random_state=1, solver='liblinear', max_iter=1000))
|
model_map['primary_models']['LogisticRegression_two_class'] = SKLearnModel(LogisticRegression(C=10, random_state=1, solver='liblinear', max_iter=1000), 'classification')
|
||||||
elif model_name == 'LR_three_class':
|
elif model_name == 'LogisticRegression_three_class':
|
||||||
from sklearnex.linear_model import LogisticRegression as LogisticRegression_EX
|
from sklearnex.linear_model import LogisticRegression as LogisticRegression_EX
|
||||||
model_map['classification_models']['LR_three_class'] = SKLearnModel(LogisticRegression_EX(C=10, random_state=1, max_iter=1000, n_jobs=-1))
|
model_map['primary_models']['LogisticRegression_three_class'] = SKLearnModel(LogisticRegression_EX(C=10, random_state=1, max_iter=1000, n_jobs=-1), 'classification')
|
||||||
elif model_name == 'LDA':
|
elif model_name == 'LDA':
|
||||||
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
|
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
|
||||||
model_map['classification_models']['LDA'] = SKLearnModel(LinearDiscriminantAnalysis())
|
model_map['primary_models']['LDA'] = SKLearnModel(LinearDiscriminantAnalysis(), 'classification')
|
||||||
elif model_name == 'KNN':
|
elif model_name == 'KNN':
|
||||||
from sklearn.neighbors import KNeighborsClassifier
|
from sklearn.neighbors import KNeighborsClassifier
|
||||||
model_map['classification_models']['KNN'] = SKLearnModel(KNeighborsClassifier())
|
model_map['primary_models']['KNN'] = SKLearnModel(KNeighborsClassifier(), 'classification')
|
||||||
elif model_name == 'CART':
|
elif model_name == 'CART':
|
||||||
from sklearn.tree import DecisionTreeClassifier
|
from sklearn.tree import DecisionTreeClassifier
|
||||||
model_map['classification_models']['CART'] = SKLearnModel(DecisionTreeClassifier(max_depth=15, random_state=1))
|
model_map['primary_models']['CART'] = SKLearnModel(DecisionTreeClassifier(max_depth=15, random_state=1), 'classification')
|
||||||
elif model_name == 'NB':
|
elif model_name == 'NB':
|
||||||
from sklearn.naive_bayes import GaussianNB
|
from sklearn.naive_bayes import GaussianNB
|
||||||
model_map['classification_models']['NB'] = SKLearnModel(GaussianNB())
|
model_map['primary_models']['NB'] = SKLearnModel(GaussianNB(), 'classification')
|
||||||
elif model_name == 'AB':
|
elif model_name == 'AB':
|
||||||
from sklearn.ensemble import AdaBoostClassifier
|
from sklearn.ensemble import AdaBoostClassifier
|
||||||
model_map['classification_models']['AB'] = SKLearnModel(AdaBoostClassifier(n_estimators=15))
|
model_map['primary_models']['AB'] = SKLearnModel(AdaBoostClassifier(n_estimators=15), 'classification')
|
||||||
elif model_name == 'RFC':
|
elif model_name == 'RFC':
|
||||||
# from sklearn.ensemble import RandomForestClassifier
|
model_map['primary_models']['RFC'] = SKLearnModel(RandomForestClassifier(n_jobs=-1, max_depth=20, random_state=1), 'classification')
|
||||||
model_map['classification_models']['RFC'] = SKLearnModel(RandomForestClassifier(n_jobs=-1, max_depth=20, random_state=1))
|
elif model_name == 'SVC':
|
||||||
elif model_name == 'SVC':
|
from sklearn.svm import SVC
|
||||||
from sklearn.svm import SVC
|
model_map['primary_models']['SVC'] = SKLearnModel(SVC(kernel='rbf', C=1e3, probability=True, random_state=1), 'classification')
|
||||||
model_map['classification_models']['SVC'] = SKLearnModel(SVC(kernel='rbf', C=1e3, probability=True, random_state=1))
|
elif model_name == 'XGB_two_class':
|
||||||
elif model_name == 'XGB_two_class':
|
from xgboost import XGBClassifier
|
||||||
from xgboost import XGBClassifier
|
from models.xgboost import XGBoostModel
|
||||||
from models.xgboost import XGBoostModel
|
model_map['primary_models']['XGB_two_class'] = XGBoostModel(XGBClassifier(n_jobs=-1, max_depth = 20, random_state=1, objective='binary:logistic', use_label_encoder= False, eval_metric='mlogloss'))
|
||||||
model_map['classification_models']['XGB_two_class'] = XGBoostModel(XGBClassifier(n_jobs=-1, max_depth = 20, random_state=1, objective='binary:logistic', use_label_encoder= False, eval_metric='mlogloss'))
|
elif model_name == 'LGBM':
|
||||||
elif model_name == 'LGBM':
|
from lightgbm import LGBMClassifier
|
||||||
from lightgbm import LGBMClassifier
|
model_map['primary_models']['LGBM'] = SKLearnModel(LGBMClassifier(n_jobs=-1, max_depth=20, random_state=1), 'classification')
|
||||||
model_map['classification_models']['LGBM'] = SKLearnModel(LGBMClassifier(n_jobs=-1, max_depth=20, random_state=1))
|
elif model_name == 'StaticMom':
|
||||||
elif model_name == 'StaticMom':
|
from models.momentum import StaticMomentumModel
|
||||||
from models.momentum import StaticMomentumModel
|
model_map['primary_models']['StaticMom'] = StaticMomentumModel(allow_short=True)
|
||||||
model_map['classification_models']['StaticMom'] = StaticMomentumModel(allow_short=True)
|
elif model_name == 'Average':
|
||||||
elif model_name == 'Average':
|
from models.average import StaticAverageModel
|
||||||
from models.average import StaticAverageModel
|
model_map['ensemble_models']['Average'] = StaticAverageModel()
|
||||||
model_map['ensemble_models']['Average'] = StaticAverageModel()
|
|
||||||
|
|
||||||
|
return model_map
|
||||||
model_names_classification = list(model_map["classification_models"].keys())
|
|
||||||
model_names_regression = list(model_map["regression_models"].keys())
|
|
||||||
|
|
||||||
|
|
||||||
default_feature_selector_classification = SKLearnModel(RandomForestClassifier(n_jobs=-1, max_depth=20, random_state=1))
|
|
||||||
default_feature_selector_regression = SKLearnModel(RandomForestRegressor(n_jobs=-1, max_depth=20, random_state=1))
|
|
||||||
|
|
||||||
|
|
||||||
return model_map, model_names_classification, model_names_regression, default_feature_selector_regression, default_feature_selector_classification
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,9 +7,9 @@ class StaticMomentumModel(Model):
|
|||||||
Model that uses only one feature: momentum. It's positive if momentum is greater than 0, otherwise it's negative.
|
Model that uses only one feature: momentum. It's positive if momentum is greater than 0, otherwise it's negative.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
method = 'classification'
|
||||||
data_transformation = 'original'
|
data_transformation = 'original'
|
||||||
only_column = 'mom'
|
only_column = 'mom'
|
||||||
feature_selection = 'off'
|
|
||||||
model_type = 'static'
|
model_type = 'static'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,9 +7,9 @@ class StaticNaiveModel(Model):
|
|||||||
Model that carries the last observation (from returns) to the next one, naively.
|
Model that carries the last observation (from returns) to the next one, naively.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
method = 'regression'
|
||||||
data_transformation = 'original'
|
data_transformation = 'original'
|
||||||
only_column = None
|
only_column = None
|
||||||
feature_selection = 'off'
|
|
||||||
model_type = 'static'
|
model_type = 'static'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,9 +7,9 @@ import pytorch_lightning as pl
|
|||||||
|
|
||||||
class LightningNeuralNetModel(Model):
|
class LightningNeuralNetModel(Model):
|
||||||
|
|
||||||
|
method = 'regression'
|
||||||
data_transformation = 'transformed'
|
data_transformation = 'transformed'
|
||||||
only_column = None
|
only_column = None
|
||||||
feature_selection = 'off'
|
|
||||||
model_type = 'ml'
|
model_type = 'ml'
|
||||||
|
|
||||||
''' Standard lightning methods '''
|
''' Standard lightning methods '''
|
||||||
|
|||||||
+5
-3
@@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from typing import Literal
|
||||||
from models.base import Model
|
from models.base import Model
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from sklearn.base import clone
|
from sklearn.base import clone
|
||||||
@@ -6,14 +7,15 @@ from sklearn.base import clone
|
|||||||
|
|
||||||
class SKLearnModel(Model):
|
class SKLearnModel(Model):
|
||||||
|
|
||||||
|
method: Literal["regression", "classification"]
|
||||||
data_transformation = 'transformed'
|
data_transformation = 'transformed'
|
||||||
only_column = None
|
only_column = None
|
||||||
feature_selection = 'on'
|
|
||||||
model_type = 'ml'
|
model_type = 'ml'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
def __init__(self, model):
|
def __init__(self, model, method: Literal['regression', 'classification']):
|
||||||
self.model = model
|
self.model = model
|
||||||
|
self.method = method
|
||||||
|
|
||||||
def fit(self, X: np.ndarray, y: np.ndarray) -> None:
|
def fit(self, X: np.ndarray, y: np.ndarray) -> None:
|
||||||
self.model.fit(X, y)
|
self.model.fit(X, y)
|
||||||
@@ -24,7 +26,7 @@ class SKLearnModel(Model):
|
|||||||
return (pred, probability)
|
return (pred, probability)
|
||||||
|
|
||||||
def clone(self) -> SKLearnModel:
|
def clone(self) -> SKLearnModel:
|
||||||
return SKLearnModel(clone(self.model))
|
return SKLearnModel(clone(self.model), self.method)
|
||||||
|
|
||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
return self.model.__class__.__name__
|
return self.model.__class__.__name__
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ class StatsModel(Model):
|
|||||||
# This is work in progress
|
# This is work in progress
|
||||||
data_transformation = 'transformed'
|
data_transformation = 'transformed'
|
||||||
only_column = None
|
only_column = None
|
||||||
feature_selection = 'on'
|
|
||||||
model_type = 'ml'
|
model_type = 'ml'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,9 +6,9 @@ from sklearn.base import clone
|
|||||||
|
|
||||||
class XGBoostModel(Model):
|
class XGBoostModel(Model):
|
||||||
|
|
||||||
|
method = 'classification'
|
||||||
data_transformation = 'transformed'
|
data_transformation = 'transformed'
|
||||||
only_column = None
|
only_column = None
|
||||||
feature_selection = 'on'
|
|
||||||
model_type = 'ml'
|
model_type = 'ml'
|
||||||
predict_window_size = 'single_timestamp'
|
predict_window_size = 'single_timestamp'
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# #%%
|
|
||||||
# import pandas as pd
|
|
||||||
# import pandas_ta as ta
|
|
||||||
# from config.config import get_default_ensemble_config
|
|
||||||
# from config.preprocess import preprocess_config
|
|
||||||
# from data_loader.load_data import load_data
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# model_config, training_config, data_config = get_default_ensemble_config()
|
|
||||||
# model_config, training_config, data_config = preprocess_config(model_config, training_config, data_config)
|
|
||||||
|
|
||||||
# data_config['target_asset'] = data_config['assets'][0]
|
|
||||||
# X, y, target_returns = load_data(**data_config)
|
|
||||||
# # %%
|
|
||||||
# X.ta.donchian()
|
|
||||||
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# X.ta.ema()
|
|
||||||
# # %%
|
|
||||||
# X.ta.adjusted = "ADA_USD_returns"
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# X.ta.sma(length=10)
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# X
|
|
||||||
# # %%
|
|
||||||
# X.ta.categories
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# ind_list = X.ta.indicators(as_list=True)
|
|
||||||
|
|
||||||
# # %%
|
|
||||||
# ind_list
|
|
||||||
# # %%
|
|
||||||
# X.ta.ao('ADA_USD_returns', length=10)
|
|
||||||
# # %%
|
|
||||||
# ta.ao()
|
|
||||||
+3
-4
@@ -2,7 +2,7 @@ import pandas as pd
|
|||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|
||||||
from data_loader.load_data import load_data
|
from data_loader.load_data import load_data
|
||||||
from data_loader.process_data import process_data, check_data
|
from data_loader.process_data import check_data
|
||||||
|
|
||||||
from reporting.wandb import launch_wandb, register_config_with_wandb
|
from reporting.wandb import launch_wandb, register_config_with_wandb
|
||||||
from reporting.reporting import report_results
|
from reporting.reporting import report_results
|
||||||
@@ -56,13 +56,12 @@ def __run_training(model_config:dict, training_config:dict, data_config:dict):
|
|||||||
# 1. Load data, check for validity and process data (feature selection, dimensionality reduction, etc.)
|
# 1. Load data, check for validity and process data (feature selection, dimensionality reduction, etc.)
|
||||||
X, y, target_returns = load_data(**configs['data_config'])
|
X, y, target_returns = load_data(**configs['data_config'])
|
||||||
if check_data(X, y, configs['training_config']) is False: continue
|
if check_data(X, y, configs['training_config']) is False: continue
|
||||||
X, original_X = process_data(X, y, configs)
|
|
||||||
|
|
||||||
# 2. Train a Primary model with optional metalabeling for each asset
|
# 2. Train a Primary model with optional metalabeling for each asset
|
||||||
training_step_primary, current_predictions = primary_step(X, y, original_X, asset, target_returns, configs, reporting)
|
training_step_primary, current_predictions = primary_step(X, y, asset, target_returns, configs, reporting)
|
||||||
|
|
||||||
# 3. Train an Ensemble model with optional metalabeling for each asset
|
# 3. Train an Ensemble model with optional metalabeling for each asset
|
||||||
training_step_secondary = secondary_step(X, y, original_X, current_predictions, asset, target_returns, configs, reporting)
|
training_step_secondary = secondary_step(X, y, current_predictions, asset, target_returns, configs, reporting)
|
||||||
|
|
||||||
# 4. Save the models
|
# 4. Save the models
|
||||||
reporting.all_assets.append(Reporting.Asset(ticker=asset[1], primary=training_step_primary, secondary=training_step_secondary))
|
reporting.all_assets.append(Reporting.Asset(ticker=asset[1], primary=training_step_primary, secondary=training_step_secondary))
|
||||||
|
|||||||
+4
-4
@@ -47,11 +47,11 @@ parameters:
|
|||||||
primary_models:
|
primary_models:
|
||||||
distribution: categorical
|
distribution: categorical
|
||||||
values:
|
values:
|
||||||
- ["LDA", "LR_two_class", "KNN", "SVC", "CART", "NB", "AB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
- ["LDA", "LogisticRegression_two_class", "KNN", "SVC", "CART", "NB", "AB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
||||||
- ["LR_two_class", "LDA", "NB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
- ["LogisticRegression_two_class", "LDA", "NB", "RFC", "XGB_two_class", "LGBM", "StaticMom"]
|
||||||
- ["LR_two_class", "LDA", "LGBM", "RFC", "XGB_two_class"]
|
- ["LogisticRegression_two_class", "LDA", "LGBM", "RFC", "XGB_two_class"]
|
||||||
meta_labeling_models:
|
meta_labeling_models:
|
||||||
value: ["LGBM", "LR_two_class"]
|
value: ["LGBM", "LogisticRegression_two_class"]
|
||||||
own_features:
|
own_features:
|
||||||
value: ['date_days', 'level_2', 'lags_up_to_5']
|
value: ['date_days', 'level_2', 'lags_up_to_5']
|
||||||
other_features:
|
other_features:
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ parameters:
|
|||||||
index_column:
|
index_column:
|
||||||
value: 'int'
|
value: 'int'
|
||||||
primary_models:
|
primary_models:
|
||||||
value: ["LR_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC", "StaticMom"]
|
value: ["LogisticRegression_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC", "StaticMom"]
|
||||||
meta_labeling_models:
|
meta_labeling_models:
|
||||||
values: ["LR_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC"]
|
values: ["LogisticRegression_two_class", "LDA", "KNN", "CART", "NB", "AB", "RFC"]
|
||||||
distribution: categorical
|
distribution: categorical
|
||||||
own_features:
|
own_features:
|
||||||
values: [['single_mom', 'date_days'], [], ['level_1', 'date_days'], ['date_days', 'level_2']]
|
values: [['single_mom', 'date_days'], [], ['level_1', 'date_days'], ['date_days', 'level_2']]
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ parameters:
|
|||||||
index_column:
|
index_column:
|
||||||
value: 'int'
|
value: 'int'
|
||||||
primary_models:
|
primary_models:
|
||||||
values: [['LR_two_class'], ['SVC'], ['LDA'], ['KNN'], ['CART'], ['MNB'], ['NB'], ['AB'], ['RFC'], ['XGB_two_class'], ['LGBM']]
|
values: [['LogisticRegression_two_class'], ['SVC'], ['LDA'], ['KNN'], ['CART'], ['MNB'], ['NB'], ['AB'], ['RFC'], ['XGB_two_class'], ['LGBM']]
|
||||||
distribution: categorical
|
distribution: categorical
|
||||||
meta_labeling_models:
|
meta_labeling_models:
|
||||||
value: ["LGBM", "LR_two_class"]
|
value: ["LGBM", "LogisticRegression_two_class"]
|
||||||
own_features:
|
own_features:
|
||||||
value: ['date_days', 'level_2', 'lags_up_to_5']
|
value: ['date_days', 'level_2', 'lags_up_to_5']
|
||||||
other_features:
|
other_features:
|
||||||
|
|||||||
@@ -77,14 +77,15 @@ def test_evaluation():
|
|||||||
expanding_window=False,
|
expanding_window=False,
|
||||||
window_size=window_length,
|
window_size=window_length,
|
||||||
retrain_every=10,
|
retrain_every=10,
|
||||||
transformations=[])
|
transformations=[],
|
||||||
|
preloaded_transformations=None)
|
||||||
predictions, _ = walk_forward_inference(
|
predictions, _ = walk_forward_inference(
|
||||||
model_name='test',
|
model_name='test',
|
||||||
model_over_time=model_over_time,
|
model_over_time=model_over_time,
|
||||||
transformations_over_time=transformations_over_time,
|
transformations_over_time=transformations_over_time,
|
||||||
X=X,
|
X=X,
|
||||||
expanding_window=False,
|
expanding_window=False,
|
||||||
window_size=window_length
|
window_size=window_length,
|
||||||
)
|
)
|
||||||
|
|
||||||
# verify if predictions are the same as y
|
# verify if predictions are the same as y
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ def test_walk_forward_train_test():
|
|||||||
expanding_window=False,
|
expanding_window=False,
|
||||||
window_size=window_length,
|
window_size=window_length,
|
||||||
retrain_every=10,
|
retrain_every=10,
|
||||||
transformations=[])
|
transformations=[],
|
||||||
|
preloaded_transformations=None
|
||||||
|
)
|
||||||
predictions, _ = walk_forward_inference(
|
predictions, _ = walk_forward_inference(
|
||||||
model_name='test',
|
model_name='test',
|
||||||
model_over_time=model_over_time,
|
model_over_time=model_over_time,
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from utils.evaluate import discretize_threeway_threshold, evaluate_predictions
|
from utils.evaluate import discretize_threeway_threshold, evaluate_predictions
|
||||||
from utils.helpers import random_string, equal_except_nan, drop_until_first_valid_index
|
from utils.helpers import equal_except_nan
|
||||||
from training.primary_model import train_primary_model
|
from training.primary_model import train_primary_model
|
||||||
from feature_selection.feature_selection import select_features
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from models.model_map import get_model_map
|
from models.model_map import default_feature_selector_classification, default_feature_selector_regression
|
||||||
from models.base import Model
|
from models.base import Model
|
||||||
from reporting.types import Reporting
|
from reporting.types import Reporting
|
||||||
from typing import Union
|
from typing import Union
|
||||||
@@ -23,22 +22,14 @@ def train_meta_labeling_model(
|
|||||||
preloaded_models: Union[list[Reporting.Single_Model], None] = None
|
preloaded_models: Union[list[Reporting.Single_Model], None] = None
|
||||||
) -> tuple[pd.Series, pd.Series, pd.DataFrame, list[Reporting.Single_Model]]:
|
) -> tuple[pd.Series, pd.Series, pd.DataFrame, list[Reporting.Single_Model]]:
|
||||||
|
|
||||||
_, _, _, default_feature_selector_regression, default_feature_selector_classification = get_model_map(model_config)
|
|
||||||
discretize = discretize_threeway_threshold(0.33)
|
discretize = discretize_threeway_threshold(0.33)
|
||||||
discretized_predictions = input_predictions.apply(discretize)
|
discretized_predictions = input_predictions.apply(discretize)
|
||||||
meta_y: pd.Series = pd.concat([discretized_predictions, y], axis=1).apply(equal_except_nan, axis = 1)
|
meta_y: pd.Series = pd.concat([discretized_predictions, y], axis=1).apply(equal_except_nan, axis = 1)
|
||||||
|
|
||||||
print("Feature Selection started")
|
meta_X = pd.concat([X, input_predictions, discretized_predictions], axis = 1)
|
||||||
backup_model = default_feature_selector_regression if data_config['method'] == 'regression' else default_feature_selector_classification
|
|
||||||
meta_feature_selection_input_X, meta_feature_selection_input_y = drop_until_first_valid_index(X, meta_y)
|
|
||||||
feature_selection_output = select_features(X = meta_feature_selection_input_X, y = meta_feature_selection_input_y, model = models[0][1], n_features_to_select = training_config['n_features_to_select'], backup_model = backup_model, scaling = training_config['scaler'])
|
|
||||||
meta_selected_features_X = X[feature_selection_output.columns]
|
|
||||||
|
|
||||||
meta_X = pd.concat([meta_selected_features_X, input_predictions, discretized_predictions], axis = 1)
|
|
||||||
|
|
||||||
_, meta_preds, meta_probabilities, all_models_single_asset = train_primary_model(
|
_, meta_preds, meta_probabilities, all_models_single_asset = train_primary_model(
|
||||||
ticker_to_predict = "prediction_correct",
|
ticker_to_predict = "prediction_correct",
|
||||||
original_X = meta_X,
|
|
||||||
X = meta_X,
|
X = meta_X,
|
||||||
y = meta_y,
|
y = meta_y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ from models.base import Model
|
|||||||
from utils.scaler import get_scaler
|
from utils.scaler import get_scaler
|
||||||
from utils.types import ScalerTypes
|
from utils.types import ScalerTypes
|
||||||
from reporting.types import Reporting
|
from reporting.types import Reporting
|
||||||
|
from transformations.rfe import RFETransformation
|
||||||
|
from transformations.pca import PCATransformation
|
||||||
|
|
||||||
def train_primary_model(
|
def train_primary_model(
|
||||||
ticker_to_predict: str,
|
ticker_to_predict: str,
|
||||||
original_X: pd.DataFrame,
|
|
||||||
X: pd.DataFrame,
|
X: pd.DataFrame,
|
||||||
y: pd.Series,
|
y: pd.Series,
|
||||||
target_returns: pd.Series,
|
target_returns: pd.Series,
|
||||||
@@ -33,24 +34,31 @@ def train_primary_model(
|
|||||||
if preloaded_models is not None:
|
if preloaded_models is not None:
|
||||||
models = preloaded_models
|
models = preloaded_models
|
||||||
|
|
||||||
|
transformations_over_time = None
|
||||||
|
|
||||||
for model_name, model in models:
|
for model_name, model in models:
|
||||||
if preloaded_models is None:
|
if preloaded_models is None:
|
||||||
model_over_time, transformations_over_time = walk_forward_train(
|
model_over_time, transformations_over_time = walk_forward_train(
|
||||||
model_name=model_name,
|
model_name=model_name,
|
||||||
model = model,
|
model = model,
|
||||||
X = X if model.feature_selection == 'on' else original_X,
|
X = X,
|
||||||
y = y,
|
y = y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
expanding_window = expanding_window,
|
expanding_window = expanding_window,
|
||||||
window_size = sliding_window_size,
|
window_size = sliding_window_size,
|
||||||
retrain_every = retrain_every,
|
retrain_every = retrain_every,
|
||||||
transformations= [get_scaler(scaler)],
|
transformations= [
|
||||||
|
get_scaler(scaler),
|
||||||
|
PCATransformation(ratio_components_to_keep=0.5, sliding_window_size=sliding_window_size),
|
||||||
|
RFETransformation(n_feature_to_select=40, model=model)
|
||||||
|
],
|
||||||
|
preloaded_transformations=transformations_over_time,
|
||||||
)
|
)
|
||||||
preds, probs = walk_forward_inference(
|
preds, probs = walk_forward_inference(
|
||||||
model_name = model_name,
|
model_name = model_name,
|
||||||
model_over_time= model_over_time if preloaded_models is None else pd.Series(model),
|
model_over_time= model_over_time if preloaded_models is None else pd.Series(model),
|
||||||
transformations_over_time = transformations_over_time,
|
transformations_over_time = transformations_over_time,
|
||||||
X = X if model.feature_selection == 'on' else original_X,
|
X = X,
|
||||||
expanding_window = expanding_window,
|
expanding_window = expanding_window,
|
||||||
window_size = sliding_window_size
|
window_size = sliding_window_size
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from typing import Union
|
|||||||
def primary_step(
|
def primary_step(
|
||||||
X: pd.DataFrame,
|
X: pd.DataFrame,
|
||||||
y:pd.Series,
|
y:pd.Series,
|
||||||
original_X:pd.DataFrame,
|
|
||||||
asset:list,
|
asset:list,
|
||||||
target_returns:pd.Series,
|
target_returns:pd.Series,
|
||||||
configs: dict,
|
configs: dict,
|
||||||
@@ -24,7 +23,6 @@ def primary_step(
|
|||||||
# 3. Train Primary models
|
# 3. Train Primary models
|
||||||
current_result, current_predictions, current_probabilities, all_models_for_single_asset = train_primary_model(
|
current_result, current_predictions, current_probabilities, all_models_for_single_asset = train_primary_model(
|
||||||
ticker_to_predict = asset[1],
|
ticker_to_predict = asset[1],
|
||||||
original_X = original_X,
|
|
||||||
X = X,
|
X = X,
|
||||||
y = y,
|
y = y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
@@ -48,7 +46,7 @@ def primary_step(
|
|||||||
primary_model_predictions = current_predictions[model_name]
|
primary_model_predictions = current_predictions[model_name]
|
||||||
primary_meta_result, primary_meta_preds, primary_meta_probabilities, meta_labeling_models = train_meta_labeling_model(
|
primary_meta_result, primary_meta_preds, primary_meta_probabilities, meta_labeling_models = train_meta_labeling_model(
|
||||||
target_asset=asset[1],
|
target_asset=asset[1],
|
||||||
X = original_X,
|
X = X,
|
||||||
input_predictions= primary_model_predictions,
|
input_predictions= primary_model_predictions,
|
||||||
y = y,
|
y = y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
@@ -75,7 +73,6 @@ def primary_step(
|
|||||||
def secondary_step(
|
def secondary_step(
|
||||||
X:pd.DataFrame,
|
X:pd.DataFrame,
|
||||||
y:pd.Series,
|
y:pd.Series,
|
||||||
original_X:pd.DataFrame,
|
|
||||||
current_predictions:pd.DataFrame,
|
current_predictions:pd.DataFrame,
|
||||||
asset:list,
|
asset:list,
|
||||||
target_returns:pd.Series,
|
target_returns:pd.Series,
|
||||||
@@ -89,7 +86,6 @@ def secondary_step(
|
|||||||
if model_config['ensemble_model'] is not None:
|
if model_config['ensemble_model'] is not None:
|
||||||
ensemble_result, ensemble_predictions, _, ensemble_models_one_asset = train_primary_model(
|
ensemble_result, ensemble_predictions, _, ensemble_models_one_asset = train_primary_model(
|
||||||
ticker_to_predict = asset[1],
|
ticker_to_predict = asset[1],
|
||||||
original_X = current_predictions,
|
|
||||||
X = current_predictions,
|
X = current_predictions,
|
||||||
y = y,
|
y = y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
@@ -116,7 +112,7 @@ def secondary_step(
|
|||||||
# 3. Train a Meta-labeling model on the averaged level-1 model predictions
|
# 3. Train a Meta-labeling model on the averaged level-1 model predictions
|
||||||
ensemble_meta_result, ensemble_meta_predictions, ensemble_meta_probabilities, ensemble_meta_labeling_models = train_meta_labeling_model(
|
ensemble_meta_result, ensemble_meta_predictions, ensemble_meta_probabilities, ensemble_meta_labeling_models = train_meta_labeling_model(
|
||||||
target_asset=asset[1],
|
target_asset=asset[1],
|
||||||
X = original_X,
|
X = X,
|
||||||
input_predictions= ensemble_predictions,
|
input_predictions= ensemble_predictions,
|
||||||
y = y,
|
y = y,
|
||||||
target_returns = target_returns,
|
target_returns = target_returns,
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ from models.base import Model
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from utils.helpers import get_first_valid_return_index
|
from utils.helpers import get_first_valid_return_index
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from sklearn.preprocessing import MinMaxScaler, Normalizer, StandardScaler
|
|
||||||
from typing import Union
|
|
||||||
from sklearn.base import clone
|
|
||||||
from transformations.base import Transformation
|
from transformations.base import Transformation
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
def walk_forward_train(
|
def walk_forward_train(
|
||||||
model_name: str,
|
model_name: str,
|
||||||
@@ -18,6 +16,7 @@ def walk_forward_train(
|
|||||||
window_size: int,
|
window_size: int,
|
||||||
retrain_every: int,
|
retrain_every: int,
|
||||||
transformations: list[Transformation],
|
transformations: list[Transformation],
|
||||||
|
preloaded_transformations: Optional[list[pd.Series]],
|
||||||
) -> tuple[pd.Series, list[pd.Series]]:
|
) -> tuple[pd.Series, list[pd.Series]]:
|
||||||
assert len(X) == len(y)
|
assert len(X) == len(y)
|
||||||
models_over_time = pd.Series(index=y.index).rename(model_name)
|
models_over_time = pd.Series(index=y.index).rename(model_name)
|
||||||
@@ -44,9 +43,12 @@ def walk_forward_train(
|
|||||||
X_expanding_window = X[first_nonzero_return:train_window_end]
|
X_expanding_window = X[first_nonzero_return:train_window_end]
|
||||||
y_expanding_window = y[first_nonzero_return:train_window_end]
|
y_expanding_window = y[first_nonzero_return:train_window_end]
|
||||||
|
|
||||||
current_transformations = [t.clone() for t in transformations]
|
if preloaded_transformations is not None and len(transformations) > 0:
|
||||||
for transformation_index, transformation in enumerate(current_transformations):
|
current_transformations = [transformation_over_time[index] for transformation_over_time in preloaded_transformations]
|
||||||
transformation.fit_transform(X_expanding_window, y_expanding_window)
|
else:
|
||||||
|
current_transformations = [t.clone() for t in transformations]
|
||||||
|
for transformation_index, transformation in enumerate(current_transformations):
|
||||||
|
X_expanding_window = transformation.fit_transform(X_expanding_window, y_expanding_window)
|
||||||
|
|
||||||
X_slice = X[train_window_start:train_window_end]
|
X_slice = X[train_window_start:train_window_end]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
from transformations.base import Transformation
|
||||||
|
from typing import Optional
|
||||||
|
from copy import deepcopy
|
||||||
|
from sklearn.decomposition import PCA
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
class PCATransformation(Transformation):
|
||||||
|
|
||||||
|
pca: PCA
|
||||||
|
|
||||||
|
def __init__(self, ratio_components_to_keep: float, sliding_window_size: int):
|
||||||
|
self.ratio_components_to_keep = ratio_components_to_keep
|
||||||
|
self.sliding_window_size = sliding_window_size
|
||||||
|
|
||||||
|
def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None) -> None:
|
||||||
|
self.pca = PCA(n_components = min(int(len(X.columns) * self.ratio_components_to_keep), self.sliding_window_size))
|
||||||
|
self.pca.fit(X, y)
|
||||||
|
|
||||||
|
def fit_transform(self, X: pd.DataFrame, y: Optional[pd.Series] = None) -> pd.DataFrame:
|
||||||
|
self.fit(X, y)
|
||||||
|
return self.transform(X)
|
||||||
|
|
||||||
|
def transform(self, X: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
X = pd.DataFrame(self.pca.transform(X), index = X.index)
|
||||||
|
X.columns = ['PCA_' + str(i) for i in range(1, len(X.columns)+1)]
|
||||||
|
return X
|
||||||
|
|
||||||
|
def clone(self) -> PCATransformation:
|
||||||
|
return deepcopy(self)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return "PCA"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
from transformations.base import Transformation
|
||||||
|
from typing import Optional
|
||||||
|
from copy import deepcopy
|
||||||
|
from sklearn.feature_selection import RFE
|
||||||
|
import pandas as pd
|
||||||
|
from models.base import Model
|
||||||
|
from models.model_map import default_feature_selector_classification
|
||||||
|
|
||||||
|
class RFETransformation(Transformation):
|
||||||
|
|
||||||
|
rfe: RFE
|
||||||
|
n_feature_to_select: int
|
||||||
|
|
||||||
|
def __init__(self, n_feature_to_select: int, model: Model, step = 0.1):
|
||||||
|
self.n_feature_to_keep = n_feature_to_select
|
||||||
|
self.model = model
|
||||||
|
if hasattr(self.model, 'model') == False: return
|
||||||
|
if hasattr(self.model.model, 'feature_importances_') == False and hasattr(self.model.model, 'coef_') == False:
|
||||||
|
model = default_feature_selector_classification
|
||||||
|
self.rfe = RFE(model.model, n_features_to_select= n_feature_to_select, step=step)
|
||||||
|
|
||||||
|
def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None) -> None:
|
||||||
|
if self.rfe is None: return
|
||||||
|
self.rfe.fit(X, y)
|
||||||
|
|
||||||
|
def fit_transform(self, X: pd.DataFrame, y: Optional[pd.Series] = None) -> pd.DataFrame:
|
||||||
|
if self.rfe is None: return X
|
||||||
|
self.fit(X, y)
|
||||||
|
return self.transform(X)
|
||||||
|
|
||||||
|
def transform(self, X: pd.DataFrame) -> pd.DataFrame:
|
||||||
|
if self.rfe is None: return X
|
||||||
|
return pd.DataFrame(X[X.columns[self.rfe.support_]], index= X.index)
|
||||||
|
|
||||||
|
def clone(self) -> RFETransformation:
|
||||||
|
return deepcopy(self)
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return "RFE"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user