lecrapaud 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lecrapaud might be problematic. Click here for more details.

Files changed (63) hide show
  1. lecrapaud/__init__.py +1 -0
  2. lecrapaud/api.py +271 -0
  3. lecrapaud/config.py +25 -0
  4. lecrapaud/db/__init__.py +1 -0
  5. lecrapaud/db/alembic/README +1 -0
  6. lecrapaud/db/alembic/env.py +78 -0
  7. lecrapaud/db/alembic/script.py.mako +26 -0
  8. lecrapaud/db/alembic/versions/2025_04_06_1738-7390745388e4_initial_setup.py +295 -0
  9. lecrapaud/db/alembic/versions/2025_04_06_1755-40cd8d3e798e_unique_constraint_for_data.py +30 -0
  10. lecrapaud/db/alembic/versions/2025_05_23_1724-2360941fa0bd_longer_string.py +52 -0
  11. lecrapaud/db/alembic/versions/2025_05_27_1159-b96396dcfaff_add_env_to_trading_tables.py +34 -0
  12. lecrapaud/db/alembic/versions/2025_05_27_1337-40cbfc215f7c_fix_nb_character_on_portfolio.py +39 -0
  13. lecrapaud/db/alembic/versions/2025_05_27_1526-3de994115317_to_datetime.py +36 -0
  14. lecrapaud/db/alembic/versions/2025_05_27_2003-25c227c684f8_add_fees_to_transactions.py +30 -0
  15. lecrapaud/db/alembic/versions/2025_05_27_2047-6b6f2d38e9bc_double_instead_of_float.py +132 -0
  16. lecrapaud/db/alembic/versions/2025_05_31_1111-c175e4a36d68_generalise_stock_to_group.py +36 -0
  17. lecrapaud/db/alembic/versions/2025_05_31_1256-5681095bfc27_create_investment_run_and_portfolio_.py +62 -0
  18. lecrapaud/db/alembic/versions/2025_05_31_1806-339927587383_add_investment_run_id.py +107 -0
  19. lecrapaud/db/alembic/versions/2025_05_31_1834-52b809a34371_make_nullablee.py +38 -0
  20. lecrapaud/db/alembic/versions/2025_05_31_1849-3b8550297e8e_change_date_to_datetime.py +44 -0
  21. lecrapaud/db/alembic/versions/2025_05_31_1852-e6b8c95d8243_add_date_to_portfolio_history.py +30 -0
  22. lecrapaud/db/alembic/versions/2025_06_10_1136-db8cdd83563a_addnewsandoptiontodata.py +32 -0
  23. lecrapaud/db/alembic/versions/2025_06_17_1652-c45f5e49fa2c_make_fields_nullable.py +89 -0
  24. lecrapaud/db/models/__init__.py +11 -0
  25. lecrapaud/db/models/base.py +181 -0
  26. lecrapaud/db/models/dataset.py +129 -0
  27. lecrapaud/db/models/feature.py +45 -0
  28. lecrapaud/db/models/feature_selection.py +125 -0
  29. lecrapaud/db/models/feature_selection_rank.py +79 -0
  30. lecrapaud/db/models/model.py +40 -0
  31. lecrapaud/db/models/model_selection.py +63 -0
  32. lecrapaud/db/models/model_training.py +62 -0
  33. lecrapaud/db/models/score.py +65 -0
  34. lecrapaud/db/models/target.py +67 -0
  35. lecrapaud/db/session.py +45 -0
  36. lecrapaud/directory_management.py +28 -0
  37. lecrapaud/experiment.py +64 -0
  38. lecrapaud/feature_engineering.py +846 -0
  39. lecrapaud/feature_selection.py +1167 -0
  40. lecrapaud/integrations/openai_integration.py +225 -0
  41. lecrapaud/jobs/__init__.py +13 -0
  42. lecrapaud/jobs/config.py +17 -0
  43. lecrapaud/jobs/scheduler.py +36 -0
  44. lecrapaud/jobs/tasks.py +57 -0
  45. lecrapaud/model_selection.py +1671 -0
  46. lecrapaud/predictions.py +292 -0
  47. lecrapaud/preprocessing.py +984 -0
  48. lecrapaud/search_space.py +848 -0
  49. lecrapaud/services/__init__.py +0 -0
  50. lecrapaud/services/embedding_categorical.py +71 -0
  51. lecrapaud/services/indicators.py +309 -0
  52. lecrapaud/speed_tests/experiments.py +139 -0
  53. lecrapaud/speed_tests/test-gpu-bilstm.ipynb +261 -0
  54. lecrapaud/speed_tests/test-gpu-resnet.ipynb +166 -0
  55. lecrapaud/speed_tests/test-gpu-transformers.ipynb +254 -0
  56. lecrapaud/speed_tests/tests.ipynb +145 -0
  57. lecrapaud/speed_tests/trash.py +37 -0
  58. lecrapaud/training.py +239 -0
  59. lecrapaud/utils.py +246 -0
  60. lecrapaud-0.1.0.dist-info/LICENSE +201 -0
  61. lecrapaud-0.1.0.dist-info/METADATA +105 -0
  62. lecrapaud-0.1.0.dist-info/RECORD +63 -0
  63. lecrapaud-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,848 @@
1
+ from typing import Optional
2
+
3
+ # ML models
4
+ from sklearn.linear_model import (
5
+ SGDRegressor,
6
+ LinearRegression,
7
+ SGDClassifier,
8
+ LogisticRegression,
9
+ )
10
+ from sklearn.tree import DecisionTreeRegressor, DecisionTreeClassifier
11
+ from sklearn.neural_network import MLPRegressor, MLPClassifier
12
+ from sklearn.svm import LinearSVR, LinearSVC
13
+ from sklearn.naive_bayes import GaussianNB
14
+
15
+ # Ensemble models
16
+ from lightgbm import LGBMRegressor, LGBMClassifier
17
+ from xgboost import XGBRegressor, XGBClassifier
18
+ from sklearn.ensemble import (
19
+ RandomForestRegressor,
20
+ AdaBoostRegressor,
21
+ RandomForestClassifier,
22
+ AdaBoostClassifier,
23
+ BaggingClassifier,
24
+ )
25
+ import lightgbm as lgb
26
+ import xgboost as xgb
27
+
28
+ # DL models
29
+ from keras import Model, Input
30
+ from keras.layers import (
31
+ Dense,
32
+ LSTM,
33
+ Bidirectional,
34
+ GRU,
35
+ LayerNormalization,
36
+ RepeatVector,
37
+ MultiHeadAttention,
38
+ Add,
39
+ GlobalAveragePooling1D,
40
+ Dropout,
41
+ Activation,
42
+ TimeDistributed,
43
+ )
44
+ from tcn import TCN
45
+ from keras.initializers import Identity
46
+ from keras.regularizers import L2
47
+ from keras.activations import sigmoid
48
+
49
+ # Search spaces
50
+ from ray import tune
51
+ import pandas as pd
52
+
53
+ # we cannot use tune.sample_from function to make conditionnal search space, because hyperopt and bayesian opt need a fixed search space
54
+
55
+ ml_models = [
56
+ {
57
+ "model_name": "linear",
58
+ "recurrent": False,
59
+ "need_scaling": True,
60
+ "classification": {
61
+ "create_model": LogisticRegression,
62
+ "search_params": {
63
+ "penalty": tune.choice(
64
+ ["l2"]
65
+ ), # None is not compatible with liblinear, and l1/elasticnet not compatible with most solvers
66
+ "C": tune.loguniform(1e-4, 1e2),
67
+ "l1_ratio": tune.quniform(0.2, 0.8, 0.1),
68
+ "solver": tune.choice(
69
+ [
70
+ "sag",
71
+ "saga",
72
+ "liblinear",
73
+ "lbfgs",
74
+ "newton-cg",
75
+ "newton-cholesky",
76
+ ]
77
+ ),
78
+ "max_iter": tune.randint(100, 1000),
79
+ "n_jobs": -1,
80
+ "random_state": 42,
81
+ },
82
+ },
83
+ "regression": {
84
+ "create_model": LinearRegression,
85
+ "search_params": {
86
+ "n_jobs": -1,
87
+ "fit_intercept": tune.choice([True, False]),
88
+ },
89
+ },
90
+ },
91
+ {
92
+ "model_name": "sgd",
93
+ "recurrent": False,
94
+ "need_scaling": True,
95
+ "classification": {
96
+ "create_model": SGDClassifier,
97
+ "search_params": {
98
+ "loss": tune.choice(
99
+ [
100
+ "hinge",
101
+ "log_loss",
102
+ "modified_huber",
103
+ "squared_hinge",
104
+ ]
105
+ ),
106
+ "penalty": tune.choice(["l1", "l2", "elasticnet"]),
107
+ "alpha": tune.loguniform(1e-6, 1e-2),
108
+ "l1_ratio": tune.quniform(0.2, 0.8, 0.1),
109
+ "max_iter": tune.randint(1000, 5000),
110
+ "shuffle": tune.choice([True, False]),
111
+ "random_state": 42,
112
+ },
113
+ },
114
+ "regression": {
115
+ "create_model": SGDRegressor,
116
+ "search_params": {
117
+ "penalty": tune.choice(["l1", "l2", "elasticnet"]),
118
+ "alpha": tune.loguniform(1e-6, 1e-2),
119
+ "l1_ratio": tune.quniform(0.2, 0.8, 0.1),
120
+ "max_iter": tune.randint(1000, 5000),
121
+ "random_state": 42,
122
+ },
123
+ },
124
+ },
125
+ {
126
+ "model_name": "naive_bayes",
127
+ "recurrent": False,
128
+ "need_scaling": False,
129
+ "classification": {
130
+ "create_model": GaussianNB, # Naive Bayes classifier for classification
131
+ "search_params": {
132
+ "var_smoothing": tune.loguniform(
133
+ 1e-9, 1e-6
134
+ ) # Smoothing parameter to deal with zero probabilities
135
+ },
136
+ },
137
+ "regression": None,
138
+ },
139
+ {
140
+ "model_name": "bagging_naive_bayes",
141
+ "recurrent": False,
142
+ "need_scaling": False,
143
+ "classification": {
144
+ "create_model": BaggingClassifier,
145
+ "search_params": {
146
+ "estimator": GaussianNB(), # Base model for bagging
147
+ "n_estimators": tune.randint(10, 100), # Number of base estimators
148
+ "max_samples": tune.uniform(
149
+ 0.5, 1.0
150
+ ), # Proportion of samples to draw for each base estimator
151
+ "max_features": tune.uniform(
152
+ 0.5, 1.0
153
+ ), # Proportion of features to draw for each base estimator
154
+ "bootstrap": tune.choice(
155
+ [True, False]
156
+ ), # Whether samples are drawn with replacement
157
+ "bootstrap_features": tune.choice(
158
+ [True, False]
159
+ ), # Whether features are drawn with replacement
160
+ "random_state": 42, # Fixed random state for reproducibility
161
+ },
162
+ },
163
+ "regression": None,
164
+ },
165
+ {
166
+ "model_name": "svm",
167
+ "recurrent": False,
168
+ "need_scaling": True,
169
+ "classification": {
170
+ "create_model": LinearSVC,
171
+ "search_params": {
172
+ # "penalty": tune.choice(["l1", "l2"]), # issue with l1 + hinge
173
+ "C": tune.loguniform(1e-4, 1e2), # Regularization strength
174
+ "max_iter": tune.randint(100, 2000), # Maximum number of iterations
175
+ "tol": tune.loguniform(1e-5, 1e-2), # Tolerance for stopping criteria
176
+ "fit_intercept": tune.choice(
177
+ [True, False]
178
+ ), # Whether to calculate intercept
179
+ "loss": tune.choice(["hinge", "squared_hinge"]), # Loss function
180
+ "dual": "auto", # Dual only when hinge loss is not used and samples < features
181
+ "random_state": 42, # Fixed random state for reproducibility
182
+ },
183
+ },
184
+ "regression": {
185
+ "create_model": LinearSVR,
186
+ "search_params": {
187
+ "C": tune.loguniform(1e-4, 1e2), # Regularization strength
188
+ "max_iter": tune.randint(100, 2000), # Maximum number of iterations
189
+ "tol": tune.loguniform(1e-5, 1e-2), # Tolerance for stopping criteria
190
+ "epsilon": tune.loguniform(
191
+ 1e-4, 1e-1
192
+ ), # Epsilon in the epsilon-insensitive loss function
193
+ "fit_intercept": tune.choice(
194
+ [True, False]
195
+ ), # Whether to calculate intercept
196
+ "loss": tune.choice(
197
+ ["epsilon_insensitive", "squared_epsilon_insensitive"]
198
+ ), # Loss function
199
+ "dual": "auto", # Dual is not applicable for certain configurations in SVR
200
+ "random_state": 42, # Fixed random state for reproducibility
201
+ },
202
+ },
203
+ },
204
+ {
205
+ "model_name": "tree",
206
+ "recurrent": False,
207
+ "need_scaling": False,
208
+ "classification": {
209
+ "create_model": DecisionTreeClassifier,
210
+ "search_params": {
211
+ "criterion": tune.choice(["gini", "entropy", "log_loss"]),
212
+ "max_depth": tune.randint(8, 64),
213
+ "min_samples_split": tune.randint(2, 10),
214
+ "min_samples_leaf": tune.randint(1, 4),
215
+ "max_features": tune.uniform(
216
+ 0.5, 1.0
217
+ ), # Proportion of features to draw for each base estimator
218
+ "random_state": 42,
219
+ },
220
+ },
221
+ "regression": {
222
+ "create_model": DecisionTreeRegressor,
223
+ "search_params": {
224
+ "max_depth": tune.randint(8, 64),
225
+ "min_samples_split": tune.randint(2, 10),
226
+ "min_samples_leaf": tune.randint(1, 4),
227
+ "max_features": tune.uniform(
228
+ 0.5, 1.0
229
+ ), # Proportion of features to draw for each base estimator
230
+ "random_state": 42,
231
+ },
232
+ },
233
+ },
234
+ {
235
+ "model_name": "forest",
236
+ "recurrent": False,
237
+ "need_scaling": False,
238
+ "classification": {
239
+ "create_model": RandomForestClassifier,
240
+ "search_params": {
241
+ "n_estimators": tune.randint(50, 1000), # Number of trees in the forest
242
+ "max_depth": tune.randint(8, 64), # Maximum depth of the trees
243
+ "min_samples_split": tune.randint(
244
+ 2, 20
245
+ ), # Minimum samples required to split a node
246
+ "min_samples_leaf": tune.randint(
247
+ 1, 10
248
+ ), # Minimum samples required at a leaf node
249
+ "max_features": tune.choice(
250
+ ["sqrt", "log2", None]
251
+ ), # Number of features to consider at each split
252
+ "bootstrap": tune.choice(
253
+ [True, False]
254
+ ), # Whether to use bootstrap sampling
255
+ "criterion": tune.choice(
256
+ ["gini", "entropy", "log_loss"]
257
+ ), # The function to measure the quality of a split
258
+ # "oob_score": tune.choice(
259
+ # [True, False]
260
+ # ), # Whether to use out-of-bag samples to estimate generalization accuracy: not working if bootstrap = False
261
+ "n_jobs": -1, # Use all processors
262
+ "random_state": 42, # Fixed random state for reproducibility
263
+ },
264
+ },
265
+ "regression": {
266
+ "create_model": RandomForestRegressor,
267
+ "search_params": {
268
+ "n_estimators": tune.randint(50, 1000), # Number of trees in the forest
269
+ "max_depth": tune.randint(5, 30), # Maximum depth of the trees
270
+ "min_samples_split": tune.randint(
271
+ 2, 20
272
+ ), # Minimum samples required to split a node
273
+ "min_samples_leaf": tune.randint(
274
+ 1, 10
275
+ ), # Minimum samples required at a leaf node
276
+ "max_features": tune.choice(
277
+ ["sqrt", "log2", None]
278
+ ), # Number of features to consider at each split
279
+ "bootstrap": tune.choice(
280
+ [True, False]
281
+ ), # Whether to use bootstrap sampling
282
+ "criterion": tune.choice(
283
+ ["squared_error", "absolute_error", "friedman_mse"]
284
+ ), # Loss function to use
285
+ # "oob_score": tune.choice(
286
+ # [True, False]
287
+ # ), # Whether to use out-of-bag samples to estimate generalization accuracy: not working if bootstrap = False
288
+ "n_jobs": -1, # Use all processors
289
+ "random_state": 42, # Fixed random state for reproducibility
290
+ },
291
+ },
292
+ },
293
+ {
294
+ "model_name": "adaboost",
295
+ "recurrent": False,
296
+ "need_scaling": False,
297
+ "classification": {
298
+ "create_model": AdaBoostClassifier,
299
+ "search_params": {
300
+ "n_estimators": tune.randint(50, 1000), # Number of boosting stages
301
+ "learning_rate": tune.loguniform(
302
+ 1e-4, 1
303
+ ), # Learning rate shrinks the contribution of each classifier
304
+ "random_state": 42, # Fixed random state for reproducibility
305
+ "estimator": tune.choice(
306
+ [
307
+ DecisionTreeClassifier(max_depth=2**i, random_state=42)
308
+ for i in range(1, 6)
309
+ ]
310
+ ), # Base estimators are decision trees with varying depths
311
+ },
312
+ },
313
+ "regression": {
314
+ "create_model": AdaBoostRegressor,
315
+ "search_params": {
316
+ "n_estimators": tune.randint(50, 1000), # Number of boosting stages
317
+ "learning_rate": tune.loguniform(1e-4, 1), # Learning rate
318
+ "loss": tune.choice(
319
+ ["linear", "square", "exponential"]
320
+ ), # Loss function to use when updating weights
321
+ "random_state": 42, # Fixed random state for reproducibility
322
+ "estimator": tune.choice(
323
+ [
324
+ DecisionTreeRegressor(max_depth=2**i, random_state=42)
325
+ for i in range(1, 6)
326
+ ]
327
+ ), # Base estimators are decision trees with varying depths
328
+ },
329
+ },
330
+ },
331
+ {
332
+ "model_name": "xgb",
333
+ "recurrent": False,
334
+ "need_scaling": False,
335
+ "classification": {
336
+ "create_model": "xgb",
337
+ "search_params": {
338
+ "num_boost_round": tune.randint(
339
+ 50, 1000
340
+ ), # Number of boosting rounds (trees)
341
+ "early_stopping_rounds": tune.randint(5, 50),
342
+ "model_params": {
343
+ "max_depth": tune.randint(3, 10), # Maximum depth of trees
344
+ "eta": tune.loguniform(
345
+ 1e-4, 0.5
346
+ ), # Learning rate, note 'eta' is used instead of 'learning_rate'
347
+ "subsample": tune.quniform(
348
+ 0.6, 1, 0.05
349
+ ), # Subsample ratio of training instances
350
+ "colsample_bytree": tune.quniform(
351
+ 0.6, 1, 0.05
352
+ ), # Subsample ratio of columns for each tree
353
+ "gamma": tune.uniform(
354
+ 0, 10
355
+ ), # Minimum loss reduction for further partitioning
356
+ "min_child_weight": tune.loguniform(
357
+ 1, 10
358
+ ), # Minimum sum of instance weights in a child
359
+ "alpha": tune.loguniform(
360
+ 1e-5, 1
361
+ ), # L1 regularization term on weights
362
+ "lambda": tune.loguniform(
363
+ 1e-5, 1
364
+ ), # L2 regularization term on weights
365
+ "random_state": 42, # Fixed random state
366
+ "n_jobs": -1, # Number of parallel threads for computation
367
+ },
368
+ },
369
+ },
370
+ "regression": {
371
+ "create_model": "xgb",
372
+ "search_params": {
373
+ "num_boost_round": tune.randint(
374
+ 50, 1000
375
+ ), # Number of boosting rounds (trees)
376
+ "early_stopping_rounds": tune.randint(5, 50),
377
+ "model_params": {
378
+ "max_depth": tune.randint(3, 10), # Maximum depth of trees
379
+ "eta": tune.loguniform(1e-4, 0.5), # Learning rate (eta)
380
+ "subsample": tune.quniform(
381
+ 0.6, 1, 0.05
382
+ ), # Subsample ratio of training instances
383
+ "colsample_bytree": tune.quniform(
384
+ 0.6, 1, 0.05
385
+ ), # Subsample ratio of columns for each tree
386
+ "gamma": tune.uniform(
387
+ 0, 10
388
+ ), # Minimum loss reduction for further partitioning
389
+ "min_child_weight": tune.loguniform(
390
+ 1, 10
391
+ ), # Minimum sum of instance weights in a child
392
+ "alpha": tune.loguniform(
393
+ 1e-5, 1
394
+ ), # L1 regularization term on weights
395
+ "lambda": tune.loguniform(
396
+ 1e-5, 1
397
+ ), # L2 regularization term on weights
398
+ "random_state": 42, # Fixed random state
399
+ "n_jobs": -1, # Number of parallel threads for computation
400
+ },
401
+ },
402
+ },
403
+ },
404
+ {
405
+ "model_name": "lgb",
406
+ "recurrent": False,
407
+ "need_scaling": False,
408
+ "classification": {
409
+ "create_model": "lgb",
410
+ "search_params": {
411
+ "num_boost_round": tune.randint(
412
+ 50, 1000
413
+ ), # Number of boosting rounds (trees)
414
+ "early_stopping_rounds": tune.randint(5, 50),
415
+ "model_params": {
416
+ "max_depth": tune.randint(3, 10), # Maximum depth of trees
417
+ "learning_rate": tune.loguniform(1e-4, 0.5), # Learning rate
418
+ "num_leaves": tune.randint(
419
+ 20, 150
420
+ ), # Maximum number of leaves in one tree
421
+ "subsample": tune.quniform(
422
+ 0.6, 1, 0.05
423
+ ), # Fraction of training data for each boosting round
424
+ "colsample_bytree": tune.quniform(
425
+ 0.6, 1, 0.05
426
+ ), # Fraction of features to use per tree
427
+ "min_data_in_leaf": tune.randint(
428
+ 20, 100
429
+ ), # Minimum number of data points in a leaf
430
+ "lambda_l1": tune.loguniform(1e-5, 1), # L1 regularization
431
+ "lambda_l2": tune.loguniform(1e-5, 1), # L2 regularization
432
+ "random_state": 42, # Fixed random state for reproducibility
433
+ "n_jobs": -1, # Use all cores for parallel computation
434
+ },
435
+ },
436
+ },
437
+ "regression": {
438
+ "create_model": "lgb",
439
+ "search_params": {
440
+ "num_boost_round": tune.randint(
441
+ 50, 1000
442
+ ), # Number of boosting rounds (trees)
443
+ "early_stopping_rounds": tune.randint(5, 50),
444
+ "model_params": {
445
+ "max_depth": tune.randint(3, 10), # Maximum depth of trees
446
+ "learning_rate": tune.loguniform(1e-4, 0.5), # Learning rate
447
+ "num_leaves": tune.randint(
448
+ 20, 150
449
+ ), # Maximum number of leaves in one tree
450
+ "subsample": tune.quniform(
451
+ 0.6, 1, 0.05
452
+ ), # Fraction of training data for each boosting round
453
+ "colsample_bytree": tune.quniform(
454
+ 0.6, 1, 0.05
455
+ ), # Fraction of features to use per tree
456
+ "min_data_in_leaf": tune.randint(
457
+ 20, 100
458
+ ), # Minimum number of data points in a leaf
459
+ "lambda_l1": tune.loguniform(1e-5, 1), # L1 regularization
460
+ "lambda_l2": tune.loguniform(1e-5, 1), # L2 regularization
461
+ "random_state": 42, # Fixed random state for reproducibility
462
+ "n_jobs": -1, # Use all cores for parallel computation
463
+ },
464
+ },
465
+ },
466
+ },
467
+ ]
468
+
469
+
470
+ def get_model_constructor(model_name: str):
471
+
472
+ def constructor(
473
+ params: dict,
474
+ input_shape: tuple[int, int],
475
+ target_type: str,
476
+ num_class: Optional[int] = None,
477
+ ):
478
+ """
479
+ Builds the recurrent model based on the initialized parameters and model name.
480
+ :return: A Keras Model object.
481
+ """
482
+ inputs = Input(shape=input_shape)
483
+
484
+ # Model selection logic
485
+ if model_name == "LSTM-1":
486
+ x = LSTM(**params["model_params"])(inputs)
487
+
488
+ elif model_name == "LSTM-2":
489
+ x = LSTM(**params["model_params"], return_sequences=True)(inputs)
490
+ x = LSTM(**params["model_params"])(x)
491
+
492
+ elif model_name == "LSTM-2-Deep":
493
+ x = LSTM(**params["model_params"], return_sequences=True)(inputs)
494
+ x = LSTM(**params["model_params"])(x)
495
+ x = Dense(50)(x)
496
+
497
+ elif model_name == "BiLSTM-1":
498
+ x = Bidirectional(LSTM(**params["model_params"]))(inputs)
499
+
500
+ elif model_name == "BiLSTM-2": # TODO: create search params ?
501
+ x = Bidirectional(LSTM(**params["model_params"], return_sequences=True))(
502
+ inputs
503
+ )
504
+ x = Bidirectional(LSTM(**params["model_params"]))(x)
505
+
506
+ elif model_name == "GRU-1":
507
+ x = GRU(**params["model_params"])(inputs)
508
+
509
+ elif model_name == "GRU-2": # TODO: create search params ?
510
+ x = GRU(**params["model_params"], return_sequences=True)(inputs)
511
+ x = GRU(**params["model_params"])(x)
512
+
513
+ elif model_name == "GRU-2-Deep": # TODO: create search params ?
514
+ x = GRU(**params["model_params"], return_sequences=True)(inputs)
515
+ x = GRU(**params["model_params"])(x)
516
+ x = Dense(50)(x)
517
+
518
+ elif model_name == "BiGRU-1":
519
+ x = Bidirectional(GRU(**params["model_params"]))(inputs)
520
+
521
+ elif model_name == "BiGRU-2": # TODO: create search params ?
522
+ x = Bidirectional(GRU(**params["model_params"], return_sequences=True))(
523
+ inputs
524
+ )
525
+ x = Bidirectional(GRU(**params["model_params"]))(x)
526
+
527
+ elif model_name == "TCN-1":
528
+ x = TCN(**params["model_params"])(inputs)
529
+
530
+ elif model_name == "TCN-2": # TODO: create search params ?
531
+ x = TCN(**params["model_params"], return_sequences=True)(inputs)
532
+ x = TCN(**params["model_params"])(x)
533
+
534
+ elif model_name == "TCN-2-Deep": # TODO: create search params ?
535
+ x = TCN(**params["model_params"], return_sequences=True)(inputs)
536
+ x = TCN(**params["model_params"])(x)
537
+ x = Dense(50)(x)
538
+
539
+ elif model_name == "BiTCN-1": # TODO: create search params ?
540
+ x = Bidirectional(TCN(**params["model_params"], return_sequences=False))(
541
+ inputs
542
+ )
543
+
544
+ elif model_name == "BiTCN-2": # TODO: create search params ?
545
+ x = Bidirectional(TCN(**params["model_params"], return_sequences=True))(
546
+ inputs
547
+ )
548
+ x = Bidirectional(TCN(**params["model_params"], return_sequences=False))(x)
549
+
550
+ elif model_name == "Seq2Seq":
551
+ # encoder
552
+ encoder_last_h1, encoder_last_h2, encoder_last_c = LSTM(
553
+ **params["model_params"], return_state=True
554
+ )(inputs)
555
+ encoder_last_h1 = LayerNormalization(epsilon=1e-6)(encoder_last_h1)
556
+ encoder_last_c = LayerNormalization(epsilon=1e-6)(encoder_last_c)
557
+
558
+ # decoder
559
+ decoder_timesteps = max(int(input_shape[0] / 5), 2)
560
+ decoder = RepeatVector(decoder_timesteps)(encoder_last_h1)
561
+ x = LSTM(**params["model_params"], return_state=False)(
562
+ decoder, initial_state=[encoder_last_h1, encoder_last_c]
563
+ )
564
+
565
+ elif model_name == "Transformer":
566
+
567
+ def transformer_encoder(
568
+ inputs, num_layers, head_size, num_heads, ff_dim, dropout=0
569
+ ):
570
+ for _ in range(num_layers):
571
+ # Attention and Normalization
572
+ x = LayerNormalization(epsilon=1e-6)(inputs)
573
+ x = MultiHeadAttention(
574
+ key_dim=head_size, num_heads=num_heads, dropout=dropout
575
+ )(x, x)
576
+ x = Add()([x, inputs])
577
+
578
+ # Feed Forward Part
579
+ y = LayerNormalization(epsilon=1e-6)(x)
580
+ y = Dense(ff_dim, activation="relu")(y)
581
+ y = Dropout(dropout)(y)
582
+ y = Dense(inputs.shape[-1])(y)
583
+ inputs = Add()([y, x])
584
+
585
+ return inputs
586
+
587
+ x = transformer_encoder(inputs, **params["model_params"])
588
+ x = GlobalAveragePooling1D()(x)
589
+ x = LayerNormalization(epsilon=1e-6)(x)
590
+
591
+ else:
592
+ raise ValueError(f"Invalid model name: {model_name}")
593
+
594
+ # Define output layer based on target type
595
+ if num_class is not None and num_class > 2:
596
+ outputs = Dense(
597
+ num_class,
598
+ kernel_initializer=Identity(),
599
+ kernel_regularizer=L2(l2=params["l2"]),
600
+ activation="softmax",
601
+ )(x)
602
+ else:
603
+ outputs = Dense(
604
+ 1,
605
+ kernel_initializer=Identity(),
606
+ kernel_regularizer=L2(l2=params["l2"]),
607
+ activation=(sigmoid if target_type == "classification" else "linear"),
608
+ )(x)
609
+
610
+ # Build the model
611
+ model = Model(inputs=inputs, outputs=outputs, name=model_name)
612
+
613
+ # Set the name of the model based on its parameters
614
+ units = (
615
+ params["model_params"].get("nb_filters")
616
+ or params["model_params"].get("units")
617
+ or params["model_params"].get("head_size")
618
+ )
619
+ nb_params = model.count_params()
620
+ timesteps = input_shape[0]
621
+ model.model_name = model.name
622
+
623
+ return model
624
+
625
+ return constructor
626
+
627
+
628
+ dl_recurrent_models = [
629
+ {
630
+ "recurrent": True,
631
+ "need_scaling": True,
632
+ "model_name": "LSTM-1",
633
+ "create_model": get_model_constructor("LSTM-1"),
634
+ "search_params": {
635
+ "model_params": {
636
+ "units": tune.choice([32, 64, 128]),
637
+ "activation": tune.choice(["tanh", "relu"]),
638
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
639
+ "kernel_initializer": Identity(),
640
+ "recurrent_initializer": Identity(),
641
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
642
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
643
+ },
644
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
645
+ "batch_size": tune.choice([32, 64, 128]),
646
+ "epochs": tune.choice([50, 100, 200]),
647
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
648
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
649
+ "l2": tune.loguniform(1e-6, 1e-1),
650
+ },
651
+ },
652
+ {
653
+ "recurrent": True,
654
+ "need_scaling": True,
655
+ "model_name": "LSTM-2",
656
+ "create_model": get_model_constructor("LSTM-2"),
657
+ "search_params": {
658
+ "model_params": {
659
+ "units": tune.choice([32, 64, 128]),
660
+ "activation": tune.choice(["tanh", "relu"]),
661
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
662
+ "kernel_initializer": Identity(),
663
+ "recurrent_initializer": Identity(),
664
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
665
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
666
+ },
667
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
668
+ "batch_size": tune.choice([32, 64, 128]),
669
+ "epochs": tune.choice([50, 100, 200]),
670
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
671
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
672
+ "l2": tune.loguniform(1e-6, 1e-1),
673
+ },
674
+ },
675
+ {
676
+ "recurrent": True,
677
+ "need_scaling": True,
678
+ "model_name": "LSTM-2-Deep",
679
+ "create_model": get_model_constructor("LSTM-2-Deep"),
680
+ "search_params": {
681
+ "model_params": {
682
+ "units": tune.choice([32, 64, 128]),
683
+ "activation": tune.choice(["tanh", "relu"]),
684
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
685
+ "kernel_initializer": Identity(),
686
+ "recurrent_initializer": Identity(),
687
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
688
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
689
+ },
690
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
691
+ "batch_size": tune.choice([32, 64, 128]),
692
+ "epochs": tune.choice([50, 100, 200]),
693
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
694
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
695
+ "l2": tune.loguniform(1e-6, 1e-1),
696
+ },
697
+ },
698
+ {
699
+ "recurrent": True,
700
+ "need_scaling": True,
701
+ "model_name": "BiLSTM-1",
702
+ "create_model": get_model_constructor("BiLSTM-1"),
703
+ "search_params": {
704
+ "model_params": {
705
+ "units": tune.choice([32, 64, 128]),
706
+ "activation": tune.choice(["tanh", "relu"]),
707
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
708
+ "kernel_initializer": Identity(),
709
+ "recurrent_initializer": Identity(),
710
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
711
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
712
+ },
713
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
714
+ "batch_size": tune.choice([32, 64, 128]),
715
+ "epochs": tune.choice([50, 100, 200]),
716
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
717
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
718
+ "l2": tune.loguniform(1e-6, 1e-1),
719
+ },
720
+ },
721
+ {
722
+ "recurrent": True,
723
+ "need_scaling": True,
724
+ "model_name": "GRU-1",
725
+ "create_model": get_model_constructor("GRU-1"),
726
+ "search_params": {
727
+ "model_params": {
728
+ "units": tune.choice([32, 64, 128]),
729
+ "activation": tune.choice(["tanh", "relu"]),
730
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
731
+ "kernel_initializer": Identity(),
732
+ "recurrent_initializer": Identity(),
733
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
734
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
735
+ },
736
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
737
+ "batch_size": tune.choice([32, 64, 128]),
738
+ "epochs": tune.choice([50, 100, 200]),
739
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
740
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
741
+ "l2": tune.loguniform(1e-6, 1e-1),
742
+ },
743
+ },
744
+ {
745
+ "recurrent": True,
746
+ "need_scaling": True,
747
+ "model_name": "BiGRU-1",
748
+ "create_model": get_model_constructor("GRU-1"),
749
+ "search_params": {
750
+ "model_params": {
751
+ "units": tune.choice([32, 64, 128]),
752
+ "activation": tune.choice(["tanh", "relu"]),
753
+ "recurrent_activation": tune.choice(["sigmoid", "relu"]),
754
+ "kernel_initializer": Identity(),
755
+ "recurrent_initializer": Identity(),
756
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
757
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
758
+ },
759
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
760
+ "batch_size": tune.choice([32, 64, 128]),
761
+ "epochs": tune.choice([50, 100, 200]),
762
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
763
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
764
+ "l2": tune.loguniform(1e-6, 1e-1),
765
+ },
766
+ },
767
+ {
768
+ "recurrent": True,
769
+ "need_scaling": True,
770
+ "model_name": "TCN-1",
771
+ "create_model": get_model_constructor("TCN-1"),
772
+ "search_params": {
773
+ "model_params": {
774
+ "nb_filters": tune.choice([32, 64, 128]),
775
+ "kernel_size": tune.choice([2, 3, 5]),
776
+ "dropout_rate": tune.quniform(0.0, 0.5, 0.1),
777
+ },
778
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
779
+ "batch_size": tune.choice([32, 64, 128]),
780
+ "epochs": tune.choice([50, 100, 200]),
781
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
782
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
783
+ "l2": tune.loguniform(1e-6, 1e-1),
784
+ },
785
+ },
786
+ {
787
+ "recurrent": True,
788
+ "need_scaling": True,
789
+ "model_name": "Seq2Seq",
790
+ "create_model": get_model_constructor("Seq2Seq"),
791
+ "search_params": {
792
+ "model_params": {
793
+ "units": tune.choice([32, 64, 128]),
794
+ "kernel_initializer": Identity(),
795
+ "recurrent_initializer": Identity(),
796
+ "dropout": tune.quniform(0.0, 0.5, 0.1),
797
+ "recurrent_dropout": tune.quniform(0.0, 0.5, 0.1),
798
+ },
799
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
800
+ "batch_size": tune.choice([32, 64, 128]),
801
+ "epochs": tune.choice([50, 100, 200]),
802
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
803
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
804
+ "l2": tune.loguniform(1e-6, 1e-1),
805
+ },
806
+ },
807
+ {
808
+ "recurrent": True,
809
+ "need_scaling": True,
810
+ "model_name": "Transformer",
811
+ "create_model": get_model_constructor("Transformer"),
812
+ "search_params": {
813
+ "model_params": {
814
+ "head_size": tune.choice(
815
+ [32, 64, 128, 256, 512]
816
+ ), # Example of different head sizes to explore
817
+ "num_heads": tune.choice(
818
+ [8, 16, 32]
819
+ ), # Exploring different number of heads
820
+ "ff_dim": tune.choice(
821
+ [128, 256, 512, 1024, 2048]
822
+ ), # Feed-forward dimension options
823
+ "num_layers": tune.choice([6, 12, 24]), # Number of transformer layers
824
+ "dropout": tune.quniform(
825
+ 0.1, 0.5, 0.1
826
+ ), # Dropout rate between 0.1 and 0.5
827
+ },
828
+ "learning_rate": tune.loguniform(1e-4, 1e-2),
829
+ "batch_size": tune.choice([32, 64, 128]),
830
+ "epochs": tune.choice([50, 100, 200]),
831
+ "timesteps": tune.choice([5, 10, 20, 50, 120]),
832
+ "clipnorm": tune.quniform(0.5, 2.0, 0.5),
833
+ "l2": tune.loguniform(1e-6, 1e-1),
834
+ },
835
+ },
836
+ ]
837
+
838
+
839
+ def get_models_idx(*model_names):
840
+ models = ml_models + dl_recurrent_models
841
+
842
+ matching_idx = [
843
+ i for i, model in enumerate(models) if model["model_name"] in model_names
844
+ ]
845
+ return matching_idx
846
+
847
+
848
+ all_models = ml_models + dl_recurrent_models