dragon-ml-toolbox 13.3.0__py3-none-any.whl → 16.2.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.
Files changed (48) hide show
  1. {dragon_ml_toolbox-13.3.0.dist-info → dragon_ml_toolbox-16.2.0.dist-info}/METADATA +20 -6
  2. dragon_ml_toolbox-16.2.0.dist-info/RECORD +51 -0
  3. {dragon_ml_toolbox-13.3.0.dist-info → dragon_ml_toolbox-16.2.0.dist-info}/licenses/LICENSE-THIRD-PARTY.md +10 -0
  4. ml_tools/ETL_cleaning.py +20 -20
  5. ml_tools/ETL_engineering.py +23 -25
  6. ml_tools/GUI_tools.py +20 -20
  7. ml_tools/MICE_imputation.py +207 -5
  8. ml_tools/ML_callbacks.py +43 -26
  9. ml_tools/ML_configuration.py +788 -0
  10. ml_tools/ML_datasetmaster.py +303 -448
  11. ml_tools/ML_evaluation.py +351 -93
  12. ml_tools/ML_evaluation_multi.py +139 -42
  13. ml_tools/ML_inference.py +290 -209
  14. ml_tools/ML_models.py +33 -106
  15. ml_tools/ML_models_advanced.py +323 -0
  16. ml_tools/ML_optimization.py +12 -12
  17. ml_tools/ML_scaler.py +11 -11
  18. ml_tools/ML_sequence_datasetmaster.py +341 -0
  19. ml_tools/ML_sequence_evaluation.py +219 -0
  20. ml_tools/ML_sequence_inference.py +391 -0
  21. ml_tools/ML_sequence_models.py +139 -0
  22. ml_tools/ML_trainer.py +1604 -179
  23. ml_tools/ML_utilities.py +351 -4
  24. ml_tools/ML_vision_datasetmaster.py +1540 -0
  25. ml_tools/ML_vision_evaluation.py +284 -0
  26. ml_tools/ML_vision_inference.py +405 -0
  27. ml_tools/ML_vision_models.py +641 -0
  28. ml_tools/ML_vision_transformers.py +284 -0
  29. ml_tools/PSO_optimization.py +6 -6
  30. ml_tools/SQL.py +4 -4
  31. ml_tools/_keys.py +171 -0
  32. ml_tools/_schema.py +1 -1
  33. ml_tools/custom_logger.py +37 -14
  34. ml_tools/data_exploration.py +502 -93
  35. ml_tools/ensemble_evaluation.py +54 -11
  36. ml_tools/ensemble_inference.py +7 -33
  37. ml_tools/ensemble_learning.py +1 -1
  38. ml_tools/math_utilities.py +1 -1
  39. ml_tools/optimization_tools.py +2 -2
  40. ml_tools/path_manager.py +5 -5
  41. ml_tools/serde.py +2 -2
  42. ml_tools/utilities.py +192 -4
  43. dragon_ml_toolbox-13.3.0.dist-info/RECORD +0 -41
  44. ml_tools/RNN_forecast.py +0 -56
  45. ml_tools/keys.py +0 -87
  46. {dragon_ml_toolbox-13.3.0.dist-info → dragon_ml_toolbox-16.2.0.dist-info}/WHEEL +0 -0
  47. {dragon_ml_toolbox-13.3.0.dist-info → dragon_ml_toolbox-16.2.0.dist-info}/licenses/LICENSE +0 -0
  48. {dragon_ml_toolbox-13.3.0.dist-info → dragon_ml_toolbox-16.2.0.dist-info}/top_level.txt +0 -0
ml_tools/ML_models.py CHANGED
@@ -7,16 +7,15 @@ import json
7
7
  from ._logger import _LOGGER
8
8
  from .path_manager import make_fullpath
9
9
  from ._script_info import _script_info
10
- from .keys import PytorchModelArchitectureKeys
10
+ from ._keys import PytorchModelArchitectureKeys
11
11
  from ._schema import FeatureSchema
12
12
 
13
13
 
14
14
  __all__ = [
15
- "MultilayerPerceptron",
16
- "AttentionMLP",
17
- "MultiHeadAttentionMLP",
18
- "TabularTransformer",
19
- "SequencePredictorLSTM",
15
+ "DragonMLP",
16
+ "DragonAttentionMLP",
17
+ "DragonMultiHeadAttentionNet",
18
+ "DragonTabularTransformer"
20
19
  ]
21
20
 
22
21
 
@@ -174,7 +173,7 @@ class _BaseAttention(_BaseMLP):
174
173
  return logits, attention_weights
175
174
 
176
175
 
177
- class MultilayerPerceptron(_BaseMLP):
176
+ class DragonMLP(_BaseMLP):
178
177
  """
179
178
  Creates a versatile Multilayer Perceptron (MLP) for regression or classification tasks.
180
179
  """
@@ -208,10 +207,10 @@ class MultilayerPerceptron(_BaseMLP):
208
207
  # Extracts the number of neurons from each nn.Linear layer
209
208
  layer_sizes = [str(layer.in_features) for layer in self.mlp if isinstance(layer, nn.Linear)]
210
209
 
211
- return self._repr_helper(name="MultilayerPerceptron", mlp_layers=layer_sizes)
210
+ return self._repr_helper(name="DragonMLP", mlp_layers=layer_sizes)
212
211
 
213
212
 
214
- class AttentionMLP(_BaseAttention):
213
+ class DragonAttentionMLP(_BaseAttention):
215
214
  """
216
215
  A Multilayer Perceptron (MLP) that incorporates an Attention layer to dynamically weigh input features.
217
216
 
@@ -244,10 +243,10 @@ class AttentionMLP(_BaseAttention):
244
243
  if isinstance(layer, nn.Linear):
245
244
  arch.append(str(layer.in_features))
246
245
 
247
- return self._repr_helper(name="AttentionMLP", mlp_layers=arch)
246
+ return self._repr_helper(name="DragonAttentionMLP", mlp_layers=arch)
248
247
 
249
248
 
250
- class MultiHeadAttentionMLP(_BaseAttention):
249
+ class DragonMultiHeadAttentionNet(_BaseAttention):
251
250
  """
252
251
  An MLP that incorporates a standard `nn.MultiheadAttention` layer to process
253
252
  the input features.
@@ -292,10 +291,10 @@ class MultiHeadAttentionMLP(_BaseAttention):
292
291
  )
293
292
  arch_str = f"{self.in_features} -> [MultiHead(h={self.num_heads})] -> {mlp_part}"
294
293
 
295
- return f"MultiHeadAttentionMLP(arch: {arch_str})"
294
+ return f"DragonMultiHeadAttentionNet(arch: {arch_str})"
296
295
 
297
296
 
298
- class TabularTransformer(nn.Module, _ArchitectureHandlerMixin):
297
+ class DragonTabularTransformer(nn.Module, _ArchitectureHandlerMixin):
299
298
  """
300
299
  A Transformer-based model for tabular data tasks.
301
300
 
@@ -306,10 +305,10 @@ class TabularTransformer(nn.Module, _ArchitectureHandlerMixin):
306
305
  def __init__(self, *,
307
306
  schema: FeatureSchema,
308
307
  out_targets: int,
309
- embedding_dim: int = 32,
308
+ embedding_dim: int = 256,
310
309
  num_heads: int = 8,
311
310
  num_layers: int = 6,
312
- dropout: float = 0.1):
311
+ dropout: float = 0.2):
313
312
  """
314
313
  Args:
315
314
  schema (FeatureSchema):
@@ -317,14 +316,28 @@ class TabularTransformer(nn.Module, _ArchitectureHandlerMixin):
317
316
  out_targets (int):
318
317
  Number of output targets (1 for regression).
319
318
  embedding_dim (int):
320
- The dimension for all feature embeddings. Must be divisible
321
- by num_heads.
319
+ The dimension for all feature embeddings. Must be divisible by num_heads. Common values: (64, 128, 192, 256, etc.)
322
320
  num_heads (int):
323
- The number of heads in the multi-head attention mechanism.
321
+ The number of heads in the multi-head attention mechanism. Common values: (4, 8, 16)
324
322
  num_layers (int):
325
- The number of sub-encoder-layers in the transformer encoder.
323
+ The number of sub-encoder-layers in the transformer encoder. Common values: (4, 8, 12)
326
324
  dropout (float):
327
325
  The dropout value.
326
+
327
+ ## Note:
328
+
329
+ **Embedding Dimension:** "Width" of the model. It's the N-dimension vector that will be used to represent each one of the features.
330
+ - Each continuous feature gets its own learnable N-dimension vector.
331
+ - Each categorical feature gets an embedding table that maps every category (e.g., "color=red", "color=blue") to a unique N-dimension vector.
332
+
333
+ **Attention Heads:** Controls the "Multi-Head Attention" mechanism. Instead of looking at all the feature interactions at once, the model splits its attention into N parallel heads.
334
+ - Embedding Dimensions get divided by the number of Attention Heads, resulting in the dimensions assigned per head.
335
+
336
+ **Number of Layers:** "Depth" of the model. Number of identical `TransformerEncoderLayer` blocks that are stacked on top of each other.
337
+ - Layer 1: The attention heads find simple, direct interactions between the features.
338
+ - Layer 2: Takes the output of Layer 1 and finds interactions between those interactions and so on.
339
+ - Trade-off: More layers are more powerful but are slower to train and more prone to overfitting. If the training loss goes down but the validation loss goes up, you might have too many layers (or need more dropout).
340
+
328
341
  """
329
342
  super().__init__()
330
343
 
@@ -488,7 +501,7 @@ class TabularTransformer(nn.Module, _ArchitectureHandlerMixin):
488
501
 
489
502
  arch_str = " -> ".join(parts)
490
503
 
491
- return f"TabularTransformer(arch: {arch_str})"
504
+ return f"DragonTabularTransformer(arch: {arch_str})"
492
505
 
493
506
 
494
507
  class _FeatureTokenizer(nn.Module):
@@ -648,91 +661,5 @@ class _MultiHeadAttentionLayer(nn.Module):
648
661
  return out, attn_weights.squeeze()
649
662
 
650
663
 
651
- class SequencePredictorLSTM(nn.Module, _ArchitectureHandlerMixin):
652
- """
653
- A simple LSTM-based network for sequence-to-sequence prediction tasks.
654
-
655
- This model is designed for datasets where each input sequence maps to an
656
- output sequence of the same length. It's suitable for forecasting problems
657
- prepared by the `SequenceMaker` class.
658
-
659
- The expected input shape is `(batch_size, sequence_length, features)`.
660
-
661
- Args:
662
- features (int): The number of features in the input sequence. Defaults to 1.
663
- hidden_size (int): The number of features in the LSTM's hidden state.
664
- Defaults to 100.
665
- recurrent_layers (int): The number of recurrent LSTM layers. Defaults to 1.
666
- dropout (float): The dropout probability for all but the last LSTM layer.
667
- Defaults to 0.
668
- """
669
- def __init__(self, features: int = 1, hidden_size: int = 100,
670
- recurrent_layers: int = 1, dropout: float = 0):
671
- super().__init__()
672
-
673
- # --- Validation ---
674
- if not isinstance(features, int) or features < 1:
675
- raise ValueError("features must be a positive integer.")
676
- if not isinstance(hidden_size, int) or hidden_size < 1:
677
- raise ValueError("hidden_size must be a positive integer.")
678
- if not isinstance(recurrent_layers, int) or recurrent_layers < 1:
679
- raise ValueError("recurrent_layers must be a positive integer.")
680
- if not (0.0 <= dropout < 1.0):
681
- raise ValueError("dropout must be a float between 0.0 and 1.0.")
682
-
683
- # --- Save configuration ---
684
- self.features = features
685
- self.hidden_size = hidden_size
686
- self.recurrent_layers = recurrent_layers
687
- self.dropout = dropout
688
-
689
- # Build model
690
- self.lstm = nn.LSTM(
691
- input_size=features,
692
- hidden_size=hidden_size,
693
- num_layers=recurrent_layers,
694
- dropout=dropout,
695
- batch_first=True # This is crucial for (batch, seq, feature) input
696
- )
697
- self.linear = nn.Linear(in_features=hidden_size, out_features=features)
698
-
699
- def forward(self, x: torch.Tensor) -> torch.Tensor:
700
- """
701
- Defines the forward pass.
702
-
703
- Args:
704
- x (torch.Tensor): The input tensor with shape
705
- (batch_size, sequence_length, features).
706
-
707
- Returns:
708
- torch.Tensor: The output tensor with shape
709
- (batch_size, sequence_length, features).
710
- """
711
- # The LSTM returns the full output sequence and the final hidden/cell states
712
- lstm_out, _ = self.lstm(x)
713
-
714
- # Pass the LSTM's output sequence to the linear layer
715
- predictions = self.linear(lstm_out)
716
-
717
- return predictions
718
-
719
- def get_architecture_config(self) -> dict:
720
- """Returns the configuration of the model."""
721
- return {
722
- 'features': self.features,
723
- 'hidden_size': self.hidden_size,
724
- 'recurrent_layers': self.recurrent_layers,
725
- 'dropout': self.dropout
726
- }
727
-
728
- def __repr__(self) -> str:
729
- """Returns the developer-friendly string representation of the model."""
730
- return (
731
- f"SequencePredictorLSTM(features={self.lstm.input_size}, "
732
- f"hidden_size={self.lstm.hidden_size}, "
733
- f"recurrent_layers={self.lstm.num_layers})"
734
- )
735
-
736
-
737
664
  def info():
738
665
  _script_info(__all__)
@@ -0,0 +1,323 @@
1
+ import torch
2
+ from torch import nn
3
+ from typing import Union, Dict, Any
4
+ from pathlib import Path
5
+ import json
6
+
7
+ from ._logger import _LOGGER
8
+ from .path_manager import make_fullpath
9
+ from ._keys import PytorchModelArchitectureKeys
10
+ from ._schema import FeatureSchema
11
+ from ._script_info import _script_info
12
+ from .ML_models import _ArchitectureHandlerMixin
13
+
14
+ # Imports from pytorch_tabular
15
+ try:
16
+ from omegaconf import DictConfig
17
+ from pytorch_tabular.models import GatedAdditiveTreeEnsembleModel, NODEModel
18
+ except ImportError:
19
+ _LOGGER.error(f"GATE and NODE require 'pip install pytorch_tabular omegaconf' dependencies.")
20
+ raise ImportError()
21
+
22
+
23
+ __all__ = [
24
+ "DragonGateModel",
25
+ "DragonNodeModel",
26
+ ]
27
+
28
+
29
+ class _BasePytabWrapper(nn.Module, _ArchitectureHandlerMixin):
30
+ """
31
+ Internal Base Class: Do not use directly.
32
+
33
+ This is an adapter to make pytorch_tabular models compatible with the
34
+ dragon-ml-toolbox pipeline.
35
+
36
+ It handles:
37
+ 1. Schema-based initialization.
38
+ 2. Single-tensor forward pass, which is then split into the
39
+ dict {'continuous': ..., 'categorical': ...} that pytorch_tabular expects.
40
+ 3. Saving/Loading architecture using the pipeline's _ArchitectureHandlerMixin.
41
+ """
42
+ def __init__(self, schema: FeatureSchema):
43
+ super().__init__()
44
+
45
+ self.schema = schema
46
+ self.model_name = "Base" # To be overridden by child
47
+ self.internal_model: nn.Module = None # type: ignore # To be set by child
48
+ self.model_hparams: Dict = dict() # To be set by child
49
+
50
+ # --- Derive indices from schema ---
51
+ categorical_map = schema.categorical_index_map
52
+
53
+ if categorical_map:
54
+ # The order of keys/values is implicitly linked and must be preserved
55
+ self.categorical_indices = list(categorical_map.keys())
56
+ self.cardinalities = list(categorical_map.values())
57
+ else:
58
+ self.categorical_indices = []
59
+ self.cardinalities = []
60
+
61
+ # Derive numerical indices by finding what's not categorical
62
+ all_indices = set(range(len(schema.feature_names)))
63
+ categorical_indices_set = set(self.categorical_indices)
64
+ self.numerical_indices = sorted(list(all_indices - categorical_indices_set))
65
+
66
+ def _build_pt_config(self, out_targets: int, **kwargs) -> DictConfig:
67
+ """Helper to create the minimal config dict for a pytorch_tabular model."""
68
+ # 'regression' is the most neutral for model architecture. The final output_dim is what truly matters.
69
+ task = "regression"
70
+
71
+ config_dict = {
72
+ # --- Data / Schema Params ---
73
+ 'task': task,
74
+ 'continuous_cols': list(self.schema.continuous_feature_names),
75
+ 'categorical_cols': list(self.schema.categorical_feature_names),
76
+ 'continuous_dim': len(self.numerical_indices),
77
+ 'categorical_dim': len(self.categorical_indices),
78
+ 'categorical_cardinality': self.cardinalities,
79
+ 'target': ['dummy_target'], # Required, but not used
80
+
81
+ # --- Model Params ---
82
+ 'output_dim': out_targets,
83
+ **kwargs
84
+ }
85
+
86
+ # Add common params that most models need
87
+ if 'loss' not in config_dict:
88
+ config_dict['loss'] = 'NotUsed'
89
+ if 'metrics' not in config_dict:
90
+ config_dict['metrics'] = []
91
+
92
+ return DictConfig(config_dict)
93
+
94
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
95
+ """
96
+ Accepts a single tensor and converts it to the dict
97
+ that pytorch_tabular models expect.
98
+ """
99
+ # 1. Split the single tensor input
100
+ x_cont = x[:, self.numerical_indices].float()
101
+ x_cat = x[:, self.categorical_indices].long()
102
+
103
+ # 2. Create the input dict
104
+ input_dict = {
105
+ 'continuous': x_cont,
106
+ 'categorical': x_cat
107
+ }
108
+
109
+ # 3. Pass to the internal pytorch_tabular model
110
+ # The model returns a dict, we extract the logits
111
+ model_output_dict = self.internal_model(input_dict)
112
+
113
+ # 4. Return the logits tensor
114
+ return model_output_dict['logits']
115
+
116
+ def get_architecture_config(self) -> Dict[str, Any]:
117
+ """Returns the full configuration of the model."""
118
+ # Deconstruct schema into a JSON-friendly dict
119
+ schema_dict = {
120
+ 'feature_names': self.schema.feature_names,
121
+ 'continuous_feature_names': self.schema.continuous_feature_names,
122
+ 'categorical_feature_names': self.schema.categorical_feature_names,
123
+ 'categorical_index_map': self.schema.categorical_index_map,
124
+ 'categorical_mappings': self.schema.categorical_mappings
125
+ }
126
+
127
+ config = {
128
+ 'schema_dict': schema_dict,
129
+ 'out_targets': self.out_targets,
130
+ **self.model_hparams
131
+ }
132
+ return config
133
+
134
+ @classmethod
135
+ def load(cls: type, file_or_dir: Union[str, Path], verbose: bool = True) -> nn.Module:
136
+ """Loads a model architecture from a JSON file."""
137
+ user_path = make_fullpath(file_or_dir)
138
+
139
+ if user_path.is_dir():
140
+ json_filename = PytorchModelArchitectureKeys.SAVENAME + ".json"
141
+ target_path = make_fullpath(user_path / json_filename, enforce="file")
142
+ elif user_path.is_file():
143
+ target_path = user_path
144
+ else:
145
+ _LOGGER.error(f"Invalid path: '{file_or_dir}'")
146
+ raise IOError()
147
+
148
+ with open(target_path, 'r') as f:
149
+ saved_data = json.load(f)
150
+
151
+ saved_class_name = saved_data[PytorchModelArchitectureKeys.MODEL]
152
+ config = saved_data[PytorchModelArchitectureKeys.CONFIG]
153
+
154
+ if saved_class_name != cls.__name__:
155
+ _LOGGER.error(f"Model class mismatch. File specifies '{saved_class_name}', but '{cls.__name__}' was expected.")
156
+ raise ValueError()
157
+
158
+ # --- RECONSTRUCTION LOGIC ---
159
+ if 'schema_dict' not in config:
160
+ _LOGGER.error("Invalid architecture file: missing 'schema_dict'. This file may be from an older version.")
161
+ raise ValueError("Missing 'schema_dict' in config.")
162
+
163
+ schema_data = config.pop('schema_dict')
164
+
165
+ # JSON saves all dict keys as strings, convert them back to int.
166
+ raw_index_map = schema_data['categorical_index_map']
167
+ if raw_index_map is not None:
168
+ rehydrated_index_map = {int(k): v for k, v in raw_index_map.items()}
169
+ else:
170
+ rehydrated_index_map = None
171
+
172
+ # JSON deserializes tuples as lists, convert them back.
173
+ schema = FeatureSchema(
174
+ feature_names=tuple(schema_data['feature_names']),
175
+ continuous_feature_names=tuple(schema_data['continuous_feature_names']),
176
+ categorical_feature_names=tuple(schema_data['categorical_feature_names']),
177
+ categorical_index_map=rehydrated_index_map,
178
+ categorical_mappings=schema_data['categorical_mappings']
179
+ )
180
+
181
+ config['schema'] = schema
182
+ # --- End Reconstruction ---
183
+
184
+ model = cls(**config)
185
+ if verbose:
186
+ _LOGGER.info(f"Successfully loaded architecture for '{saved_class_name}'")
187
+ return model
188
+
189
+ def __repr__(self) -> str:
190
+ internal_model_str = str(self.internal_model)
191
+ # Grab the first line of the internal model's repr
192
+ internal_repr = internal_model_str.split('\n')[0]
193
+ return f"{self.model_name}(internal_model={internal_repr})"
194
+
195
+
196
+ class DragonGateModel(_BasePytabWrapper):
197
+ """
198
+ Adapter for the Gated Additive Tree Ensemble (GATE) model from the 'pytorch_tabular' library.
199
+
200
+ GATE is a hybrid model that uses Gated Feature Learning Units (GFLUs) to
201
+ learn powerful feature representations. These learned features are then
202
+ fed into an additive ensemble of differentiable decision trees, combining
203
+ the representation learning of deep networks with the structured
204
+ decision-making of tree ensembles.
205
+ """
206
+ def __init__(self, *,
207
+ schema: FeatureSchema,
208
+ out_targets: int,
209
+ embedding_dim: int = 32,
210
+ gflu_stages: int = 6,
211
+ num_trees: int = 20,
212
+ tree_depth: int = 5,
213
+ dropout: float = 0.1):
214
+ """
215
+ Args:
216
+ schema (FeatureSchema):
217
+ The definitive schema object from data_exploration.
218
+ out_targets (int):
219
+ Number of output targets.
220
+ embedding_dim (int):
221
+ Dimension of the categorical embeddings. (Recommended: 16 to 64)
222
+ gflu_stages (int):
223
+ Number of Gated Feature Learning Units (GFLU) stages. (Recommended: 2 to 6)
224
+ num_trees (int):
225
+ Number of trees in the ensemble. (Recommended: 10 to 50)
226
+ tree_depth (int):
227
+ Depth of each tree. (Recommended: 4 to 8)
228
+ dropout (float):
229
+ Dropout rate for the GFLU.
230
+ """
231
+ super().__init__(schema)
232
+ self.model_name = "DragonGateModel"
233
+ self.out_targets = out_targets
234
+
235
+ # Store hparams for saving/loading
236
+ self.model_hparams = {
237
+ 'embedding_dim': embedding_dim,
238
+ 'gflu_stages': gflu_stages,
239
+ 'num_trees': num_trees,
240
+ 'tree_depth': tree_depth,
241
+ 'dropout': dropout
242
+ }
243
+
244
+ # Build the minimal config for the GateModel
245
+ pt_config = self._build_pt_config(
246
+ out_targets=out_targets,
247
+ embedding_dim=embedding_dim,
248
+ gflu_stages=gflu_stages,
249
+ num_trees=num_trees,
250
+ tree_depth=tree_depth,
251
+ dropout=dropout,
252
+ # GATE-specific params
253
+ gflu_dropout=dropout,
254
+ chain_trees=False,
255
+ )
256
+
257
+ # Instantiate the internal pytorch_tabular model
258
+ self.internal_model = GatedAdditiveTreeEnsembleModel(config=pt_config)
259
+
260
+
261
+ class DragonNodeModel(_BasePytabWrapper):
262
+ """
263
+ Adapter for the Neural Oblivious Decision Ensembles (NODE) model from the 'pytorch_tabular' library.
264
+
265
+ NODE is a model based on an ensemble of differentiable 'oblivious'
266
+ decision trees. An oblivious tree uses the same splitting feature and
267
+ threshold across all nodes at the same depth. This structure, combined
268
+ with a differentiable formulation, allows the model to be trained
269
+ end-to-end with gradient descent, learning feature interactions and
270
+ splitting thresholds simultaneously.
271
+ """
272
+ def __init__(self, *,
273
+ schema: FeatureSchema,
274
+ out_targets: int,
275
+ embedding_dim: int = 32,
276
+ num_trees: int = 1024,
277
+ tree_depth: int = 6,
278
+ dropout: float = 0.1):
279
+ """
280
+ Args:
281
+ schema (FeatureSchema):
282
+ The definitive schema object from data_exploration.
283
+ out_targets (int):
284
+ Number of output targets.
285
+ embedding_dim (int):
286
+ Dimension of the categorical embeddings. (Recommended: 16 to 64)
287
+ num_trees (int):
288
+ Total number of trees in the ensemble. (Recommended: 256 to 2048)
289
+ tree_depth (int):
290
+ Depth of each tree. (Recommended: 4 to 8)
291
+ dropout (float):
292
+ Dropout rate.
293
+ """
294
+ super().__init__(schema)
295
+ self.model_name = "DragonNodeModel"
296
+ self.out_targets = out_targets
297
+
298
+ # Store hparams for saving/loading
299
+ self.model_hparams = {
300
+ 'embedding_dim': embedding_dim,
301
+ 'num_trees': num_trees,
302
+ 'tree_depth': tree_depth,
303
+ 'dropout': dropout
304
+ }
305
+
306
+ # Build the minimal config for the NodeModel
307
+ pt_config = self._build_pt_config(
308
+ out_targets=out_targets,
309
+ embedding_dim=embedding_dim,
310
+ num_trees=num_trees,
311
+ tree_depth=tree_depth,
312
+ # NODE-specific params
313
+ num_layers=1, # NODE uses num_layers=1 for a single ensemble
314
+ total_trees=num_trees,
315
+ dropout_rate=dropout,
316
+ )
317
+
318
+ # Instantiate the internal pytorch_tabular model
319
+ self.internal_model = NODEModel(config=pt_config)
320
+
321
+
322
+ def info():
323
+ _script_info(__all__)
@@ -14,9 +14,9 @@ from functools import partial
14
14
  from .path_manager import make_fullpath, sanitize_filename
15
15
  from ._logger import _LOGGER
16
16
  from ._script_info import _script_info
17
- from .ML_inference import PyTorchInferenceHandler
18
- from .keys import PyTorchInferenceKeys
19
- from .SQL import DatabaseManager
17
+ from .ML_inference import DragonInferenceHandler
18
+ from ._keys import PyTorchInferenceKeys
19
+ from .SQL import DragonSQL
20
20
  from .optimization_tools import _save_result, create_optimization_bounds
21
21
  from .utilities import save_dataframe_filename
22
22
  from .math_utilities import discretize_categorical_values
@@ -24,14 +24,14 @@ from ._schema import FeatureSchema
24
24
 
25
25
 
26
26
  __all__ = [
27
- "MLOptimizer",
27
+ "DragonOptimizer",
28
28
  "FitnessEvaluator",
29
29
  "create_pytorch_problem",
30
30
  "run_optimization"
31
31
  ]
32
32
 
33
33
 
34
- class MLOptimizer:
34
+ class DragonOptimizer:
35
35
  """
36
36
  A wrapper class for setting up and running EvoTorch optimization tasks.
37
37
 
@@ -47,7 +47,7 @@ class MLOptimizer:
47
47
  >>> cont_bounds = {'feature_A': (0, 100), 'feature_B': (-10, 10)}
48
48
  >>>
49
49
  >>> # 3. Initialize the optimizer
50
- >>> optimizer = MLOptimizer(
50
+ >>> optimizer = DragonOptimizer(
51
51
  ... inference_handler=my_handler,
52
52
  ... schema=schema,
53
53
  ... continuous_bounds_map=cont_bounds,
@@ -63,7 +63,7 @@ class MLOptimizer:
63
63
  ... )
64
64
  """
65
65
  def __init__(self,
66
- inference_handler: PyTorchInferenceHandler,
66
+ inference_handler: DragonInferenceHandler,
67
67
  schema: FeatureSchema,
68
68
  continuous_bounds_map: Dict[str, Tuple[float, float]],
69
69
  task: Literal["min", "max"],
@@ -75,7 +75,7 @@ class MLOptimizer:
75
75
  Initializes the optimizer by creating the EvoTorch problem and searcher.
76
76
 
77
77
  Args:
78
- inference_handler (PyTorchInferenceHandler):
78
+ inference_handler (DragonInferenceHandler):
79
79
  An initialized inference handler containing the model.
80
80
  schema (FeatureSchema):
81
81
  The definitive schema object from data_exploration.
@@ -172,18 +172,18 @@ class FitnessEvaluator:
172
172
  A callable class that wraps the PyTorch model inference handler and performs
173
173
  on-the-fly discretization for the EvoTorch fitness function.
174
174
 
175
- This class is automatically instantiated by MLOptimizer and passed to
175
+ This class is automatically instantiated by DragonOptimizer and passed to
176
176
  create_pytorch_problem, encapsulating the evaluation logic.
177
177
  """
178
178
  def __init__(self,
179
- inference_handler: PyTorchInferenceHandler,
179
+ inference_handler: DragonInferenceHandler,
180
180
  categorical_index_map: Optional[Dict[int, int]] = None,
181
181
  discretize_start_at_zero: bool = True):
182
182
  """
183
183
  Initializes the fitness evaluator.
184
184
 
185
185
  Args:
186
- inference_handler (PyTorchInferenceHandler):
186
+ inference_handler (DragonInferenceHandler):
187
187
  An initialized inference handler containing the model.
188
188
  categorical_index_map (Dict[int, int] | None):
189
189
  Maps {column_index: cardinality} for discretization.
@@ -426,7 +426,7 @@ def run_optimization(
426
426
  _LOGGER.info(f"🏁 Starting optimal solution space analysis with {repetitions} repetitions...")
427
427
 
428
428
  first_run_logger = None # To store the logger from the first rep
429
- db_context = DatabaseManager(db_path) if save_format in ['sqlite', 'both'] else nullcontext()
429
+ db_context = DragonSQL(db_path) if save_format in ['sqlite', 'both'] else nullcontext()
430
430
 
431
431
  with db_context as db_manager:
432
432
  # --- Setup Database Schema (if applicable) ---