sdg-core-lib 0.1.0__tar.gz
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 sdg-core-lib might be problematic. Click here for more details.
- sdg_core_lib-0.1.0/PKG-INFO +9 -0
- sdg_core_lib-0.1.0/README.md +0 -0
- sdg_core_lib-0.1.0/pyproject.toml +35 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/NumericDataset.py +150 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/browser.py +73 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/model_factory.py +72 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/ModelInfo.py +42 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/TrainingInfo.py +40 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/UnspecializedModel.py +106 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/KerasBaseVAE.py +172 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/VAE.py +61 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/implementation/TabularVAE.py +96 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/implementation/TimeSeriesVAE.py +156 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/data_generator/models/keras/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/evaluate/Metrics.py +48 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/evaluate/TabularComparison.py +276 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/evaluate/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/job.py +56 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/FunctionApplier.py +14 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/function_factory.py +41 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/FunctionInfo.py +25 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/FunctionResult.py +15 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/Parameter.py +33 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/UnspecializedFunction.py +42 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/distribution_evaluator/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/distribution_evaluator/implementation/NormalTester.py +65 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/distribution_evaluator/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/IntervalThreshold.py +32 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/MonoThreshold.py +28 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/implementation/InnerThreshold.py +43 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/implementation/LowerThreshold.py +32 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/implementation/OuterThreshold.py +42 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/implementation/UpperThreshold.py +32 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/post_process/functions/filter/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/preprocess/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/preprocess/scale.py +51 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/keras/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/keras/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/keras/implementation/test_TabularVAE.py +120 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/keras/implementation/test_TimeSeriesVAE.py +110 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/keras/test_KerasBaseVAE.py +74 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/test_ModelInfo.py +27 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/test_TrainingInfo.py +30 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/models/test_UnspecializedModel.py +32 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/data_generator/test_model_factory.py +52 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/evaluate/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/evaluate/test_Metrics.py +62 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/evaluate/test_TabularComparisonEvaluator.py +75 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/infer_test.json +168 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/infer_test_nodata.json +77 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/infer_test_nodata_wrong.json +11 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/distribution_evaluator/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/distribution_evaluator/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/distribution_evaluator/implementation/test_NormalTester.py +55 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/filters/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/filters/implementation/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/post_process/functions/filters/implementation/test_InnerThreshold.py +30 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/pre_process/__init__.py +0 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/pre_process/test_scaling.py +55 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/test_browser.py +11 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/test_dataset.py +149 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/test_job.py +128 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/train_test.json +166 -0
- sdg_core_lib-0.1.0/src/sdg_core_lib/test/train_test_2.json +9 -0
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sdg-core-lib"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "emiliocimino", email = "emilio.cimino@outlook.it" }
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
|
|
12
|
+
[dependency-groups]
|
|
13
|
+
dev = [
|
|
14
|
+
"numpy==2.0.2",
|
|
15
|
+
"pandas==2.2.3",
|
|
16
|
+
"seaborn==0.13.2",
|
|
17
|
+
"scikit-learn==1.5.2",
|
|
18
|
+
"keras==3.6.0",
|
|
19
|
+
"tensorflow==2.18.0",
|
|
20
|
+
"loguru",
|
|
21
|
+
"skops",
|
|
22
|
+
"statsmodels"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
test = [
|
|
26
|
+
{include-group = "dev"},
|
|
27
|
+
"pytest"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
sdg-core-lib = "sdg_core_lib:main"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["uv_build>=0.8.22,<0.9.0"]
|
|
35
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
NUMERICAL = "continuous"
|
|
5
|
+
CATEGORICAL = "categorical"
|
|
6
|
+
OTHER = "none"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NumericDataset:
|
|
10
|
+
"""
|
|
11
|
+
Class to handle numeric datasets.
|
|
12
|
+
The class loads a dataset from a list of dictionaries into a pandas DataFrame.
|
|
13
|
+
It also identifies which columns are numerical and which are categorical.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, dataset: list[dict]):
|
|
17
|
+
self.dataset: list[dict] = dataset
|
|
18
|
+
self.dataframe: pd.DataFrame = pd.DataFrame()
|
|
19
|
+
self.columns: list[str] = []
|
|
20
|
+
self.continuous_columns = []
|
|
21
|
+
self.categorical_columns = []
|
|
22
|
+
self.unrecognized_columns = []
|
|
23
|
+
self.continuous_data: pd.DataFrame = pd.DataFrame()
|
|
24
|
+
self.categorical_data: pd.DataFrame = pd.DataFrame()
|
|
25
|
+
self.input_shape: str = ""
|
|
26
|
+
self._configure()
|
|
27
|
+
|
|
28
|
+
def _configure(self):
|
|
29
|
+
"""
|
|
30
|
+
Convert data from requests into an easy-to-process dataframe
|
|
31
|
+
dataset: [{
|
|
32
|
+
column_data: [ ... ],
|
|
33
|
+
column_name: str,
|
|
34
|
+
column_type: str [continuous/categorical],
|
|
35
|
+
column_datatype: str
|
|
36
|
+
}]
|
|
37
|
+
:return: a pandas Dataframe where each column is structured as expected
|
|
38
|
+
:raises: ValueError if dataset is empty
|
|
39
|
+
"""
|
|
40
|
+
data = self.dataset
|
|
41
|
+
if len(self.dataset) == 0:
|
|
42
|
+
raise ValueError("Dataset is empty")
|
|
43
|
+
column_names = []
|
|
44
|
+
categorical_columns = []
|
|
45
|
+
numerical_columns = []
|
|
46
|
+
unrecognized_columns = []
|
|
47
|
+
data_structure = []
|
|
48
|
+
for col in data:
|
|
49
|
+
content = col.get("column_data", [])
|
|
50
|
+
content_type = col.get("column_datatype", "object")
|
|
51
|
+
column_name = col.get("column_name", "")
|
|
52
|
+
column_type = col.get("column_type", "")
|
|
53
|
+
data_structure.append(np.array(content, dtype=content_type))
|
|
54
|
+
column_names.append(column_name)
|
|
55
|
+
if column_type == NUMERICAL:
|
|
56
|
+
numerical_columns.append(column_name)
|
|
57
|
+
elif column_type == CATEGORICAL:
|
|
58
|
+
categorical_columns.append(column_name)
|
|
59
|
+
else:
|
|
60
|
+
unrecognized_columns.append(column_name)
|
|
61
|
+
|
|
62
|
+
input_data = {
|
|
63
|
+
col["column_name"]: np.array(col.get("column_data", [])).tolist()
|
|
64
|
+
for col in data
|
|
65
|
+
}
|
|
66
|
+
data_frame = pd.DataFrame(input_data)
|
|
67
|
+
data_structure = np.array(data_frame.to_numpy().tolist())
|
|
68
|
+
|
|
69
|
+
self.dataframe = data_frame
|
|
70
|
+
self.columns = column_names
|
|
71
|
+
self.categorical_columns = categorical_columns
|
|
72
|
+
self.continuous_columns = numerical_columns
|
|
73
|
+
self.unrecognized_columns = unrecognized_columns
|
|
74
|
+
self.continuous_data = data_frame[numerical_columns]
|
|
75
|
+
self.categorical_data = data_frame[categorical_columns]
|
|
76
|
+
self.input_shape = str(data_structure.shape[1:])
|
|
77
|
+
|
|
78
|
+
def _categorize_column(self, col):
|
|
79
|
+
if col in self.continuous_columns:
|
|
80
|
+
return NUMERICAL
|
|
81
|
+
if col in self.categorical_columns:
|
|
82
|
+
return CATEGORICAL
|
|
83
|
+
return OTHER
|
|
84
|
+
|
|
85
|
+
def parse_tabular_data_json(self) -> list[dict]:
|
|
86
|
+
"""
|
|
87
|
+
Converts data from a dataframe into a list of dictionaries
|
|
88
|
+
:return: a dictionary in form of:
|
|
89
|
+
dataset: [{
|
|
90
|
+
column_data: [ ... ],
|
|
91
|
+
column_name: str,
|
|
92
|
+
column_type: str [numerical/categorical],
|
|
93
|
+
column_datatype: str
|
|
94
|
+
}]
|
|
95
|
+
"""
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
"column_data": self.dataframe[col].to_numpy().tolist(),
|
|
99
|
+
"column_name": col,
|
|
100
|
+
"column_type": self._categorize_column(col),
|
|
101
|
+
"column_datatype": str(self.dataframe[col].to_numpy().dtype),
|
|
102
|
+
}
|
|
103
|
+
for col in self.dataframe.columns
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
def parse_data_to_registry(self) -> list[dict]:
|
|
107
|
+
"""
|
|
108
|
+
Translates data structure from input coherence to a structured feature list
|
|
109
|
+
:return:
|
|
110
|
+
"""
|
|
111
|
+
feature_list = []
|
|
112
|
+
for idx, col in enumerate(self.dataset):
|
|
113
|
+
feat = {
|
|
114
|
+
"feature_name": col.get("column_name", ""),
|
|
115
|
+
"feature_position": idx,
|
|
116
|
+
"is_categorical": (
|
|
117
|
+
True if col.get("column_type", "") == CATEGORICAL else False
|
|
118
|
+
),
|
|
119
|
+
"type": col.get("column_datatype", ""),
|
|
120
|
+
}
|
|
121
|
+
feature_list.append(feat)
|
|
122
|
+
return feature_list
|
|
123
|
+
|
|
124
|
+
def get_data(self) -> tuple[pd.DataFrame, list[str], list[str], list[str]]:
|
|
125
|
+
"""
|
|
126
|
+
Returns the data in the dataset as a tuple of 4 elements:
|
|
127
|
+
|
|
128
|
+
1. The pandas DataFrame containing the data
|
|
129
|
+
2. A list of column names
|
|
130
|
+
3. A list of continuous column names
|
|
131
|
+
4. A list of categorical column names
|
|
132
|
+
|
|
133
|
+
:return: (dataframe, columns, continuous_columns, categorical_columns)
|
|
134
|
+
:rtype: tuple[pandas.DataFrame, list[str], list[str], list[str]]
|
|
135
|
+
"""
|
|
136
|
+
return (
|
|
137
|
+
self.dataframe,
|
|
138
|
+
self.columns,
|
|
139
|
+
self.continuous_columns,
|
|
140
|
+
self.categorical_columns,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def get_numpy_data(dataframe: pd.DataFrame) -> np.ndarray:
|
|
145
|
+
"""
|
|
146
|
+
Correctly Returns numpy array with complex structures, like columns with type list
|
|
147
|
+
:param dataframe: numpy dataframe
|
|
148
|
+
:return: correctly structured numpy array
|
|
149
|
+
"""
|
|
150
|
+
return np.array(dataframe.to_numpy().tolist())
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import importlib
|
|
4
|
+
from typing import Generator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def find_implementations(
|
|
8
|
+
root_path: str, implementation_folder: str = "implementation"
|
|
9
|
+
) -> list[str]:
|
|
10
|
+
"""
|
|
11
|
+
Takes a root path and a name of a folder. Returns all modules existing in each of the so-named folders
|
|
12
|
+
:param implementation_folder: folder name where implemented modules exist
|
|
13
|
+
:param root_path: root path in which to explore
|
|
14
|
+
:return: list of stringed modules represented in py-like dot-notation
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
root_dir = Path(root_path).resolve() # Ensure absolute path
|
|
18
|
+
implementation_dirs = root_dir.rglob(
|
|
19
|
+
implementation_folder
|
|
20
|
+
) # Find all 'implementation' folders
|
|
21
|
+
module_paths = []
|
|
22
|
+
|
|
23
|
+
for impl_dir in implementation_dirs:
|
|
24
|
+
py_files = [
|
|
25
|
+
file for file in impl_dir.glob("*.py") if file.name != "__init__.py"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
for file in py_files:
|
|
29
|
+
rel_path = file.relative_to(root_dir).with_suffix("") # Remove extension
|
|
30
|
+
module_path = ".".join(rel_path.parts) # Convert to module notation
|
|
31
|
+
module_paths.append(module_path)
|
|
32
|
+
|
|
33
|
+
return module_paths
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def browse(path: str, package: str) -> Generator[dict | None, None, None]:
|
|
37
|
+
"""
|
|
38
|
+
Generator function to iterate.
|
|
39
|
+
It exploits the find_implementations function to gather all module names, then extract from each module
|
|
40
|
+
the main class. Each main class so extracted provides a dictionary description.
|
|
41
|
+
|
|
42
|
+
:return: dictionary description of each implementation existing in sdg_core_lib
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
modules = find_implementations(path)
|
|
46
|
+
list_module_names = [f"{package}{module}" for module in modules]
|
|
47
|
+
|
|
48
|
+
for module_name in list_module_names:
|
|
49
|
+
class_name = module_name.split(".")[-1]
|
|
50
|
+
try:
|
|
51
|
+
module = importlib.import_module(module_name)
|
|
52
|
+
except ImportError:
|
|
53
|
+
yield None
|
|
54
|
+
continue
|
|
55
|
+
Class = getattr(module, class_name)
|
|
56
|
+
|
|
57
|
+
yield Class.self_describe()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def browse_functions():
|
|
61
|
+
base_function_package = "sdg_core_lib.post_process.functions."
|
|
62
|
+
base_function_path = os.path.join(
|
|
63
|
+
os.path.dirname(os.path.abspath(__file__)), "post_process/functions/"
|
|
64
|
+
)
|
|
65
|
+
return browse(base_function_path, base_function_package)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def browse_algorithms():
|
|
69
|
+
base_model_package = "sdg_core_lib.data_generator.models."
|
|
70
|
+
base_model_path = os.path.join(
|
|
71
|
+
os.path.dirname(os.path.abspath(__file__)), "data_generator/models/"
|
|
72
|
+
)
|
|
73
|
+
return browse(base_model_path, base_model_package)
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
|
|
3
|
+
from sdg_core_lib.data_generator.models.UnspecializedModel import UnspecializedModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def dynamic_import(class_name: str):
|
|
7
|
+
"""
|
|
8
|
+
Dynamically imports a class given its name.
|
|
9
|
+
|
|
10
|
+
:param class_name: a string with the full name of the class to import
|
|
11
|
+
:return: the class itself
|
|
12
|
+
"""
|
|
13
|
+
module_name, class_name = class_name.rsplit(".", 1)
|
|
14
|
+
module = importlib.import_module(module_name)
|
|
15
|
+
return getattr(module, class_name)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def model_factory(model_dict: dict, input_shape: str = None) -> UnspecializedModel:
|
|
19
|
+
"""
|
|
20
|
+
This function is a generic model factory. Takes a dictionary containing useful model information and plugs
|
|
21
|
+
them in the model itself.
|
|
22
|
+
Input shape may be passed as an argument (i.e) from the request data itself, or [alternatively] may be present in
|
|
23
|
+
model dictionary. If not explicitly passed, it will use the model dictionary
|
|
24
|
+
|
|
25
|
+
:param model_dict: A dictionary containing model information, structured as follows:
|
|
26
|
+
{
|
|
27
|
+
"image" -> contains the possible path where to find the model image. If not none, model will be loaded from there
|
|
28
|
+
"metadata" -> a dictionary itself, containing miscellaneous information
|
|
29
|
+
"algorithm_name" -> includes the model class module to _load
|
|
30
|
+
"model_name" -> the model name, used to identify the model itself
|
|
31
|
+
"input_shape" [optional] -> contains a stringed tuple that identifies the input layer shape
|
|
32
|
+
}
|
|
33
|
+
:param input_shape:
|
|
34
|
+
:return: An instance of a BaseModel class or any subclass
|
|
35
|
+
"""
|
|
36
|
+
model_file, metadata, model_type, model_name, input_shape_model = parse_model_info(
|
|
37
|
+
model_dict
|
|
38
|
+
)
|
|
39
|
+
if input_shape is None:
|
|
40
|
+
input_shape = input_shape_model
|
|
41
|
+
|
|
42
|
+
ModelClass = dynamic_import(model_type)
|
|
43
|
+
model = ModelClass(
|
|
44
|
+
metadata=metadata,
|
|
45
|
+
model_name=model_name,
|
|
46
|
+
input_shape=input_shape,
|
|
47
|
+
load_path=model_file,
|
|
48
|
+
)
|
|
49
|
+
return model
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_model_info(model_dict: dict):
|
|
53
|
+
"""
|
|
54
|
+
Extracts the necessary information from the model dictionary and returns them as separate arguments.
|
|
55
|
+
|
|
56
|
+
:param model_dict: A dictionary containing model information, structured as follows:
|
|
57
|
+
{
|
|
58
|
+
"image" -> contains the possible path where to find the model image. If not none, model will be loaded from there
|
|
59
|
+
"metadata" -> a dictionary itself, containing miscellaneous information
|
|
60
|
+
"algorithm_name" -> includes the model class module to _load
|
|
61
|
+
"model_name" -> the model name, used to identify the model itself
|
|
62
|
+
"input_shape" [optional] -> contains a stringed tuple that identifies the input layer shape
|
|
63
|
+
}
|
|
64
|
+
:return: model_file, metadata, model_type, model_name, input_shape
|
|
65
|
+
"""
|
|
66
|
+
model_file = model_dict.get("image", None)
|
|
67
|
+
metadata = model_dict.get("metadata", {})
|
|
68
|
+
model_type = model_dict.get("algorithm_name")
|
|
69
|
+
model_name = model_dict.get("model_name")
|
|
70
|
+
input_shape = model_dict.get("input_shape", "")
|
|
71
|
+
|
|
72
|
+
return model_file, metadata, model_type, model_name, input_shape
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class AllowedData:
|
|
2
|
+
def __init__(self, dtype: str, is_categorical: bool):
|
|
3
|
+
self.dtype = dtype
|
|
4
|
+
self.is_categorical = is_categorical
|
|
5
|
+
|
|
6
|
+
def to_json(self):
|
|
7
|
+
return {"type": self.dtype, "is_categorical": self.is_categorical}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ModelInfo:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
name: str,
|
|
14
|
+
default_loss_function: str,
|
|
15
|
+
description: str,
|
|
16
|
+
allowed_data: list[AllowedData],
|
|
17
|
+
):
|
|
18
|
+
self.name = name
|
|
19
|
+
self.default_loss_function = default_loss_function
|
|
20
|
+
self.description = description
|
|
21
|
+
self.allowed_data = allowed_data
|
|
22
|
+
|
|
23
|
+
def get_model_info(self):
|
|
24
|
+
"""
|
|
25
|
+
Returns a dictionary containing the model information.
|
|
26
|
+
|
|
27
|
+
The dictionary includes the model's name, default loss function, description,
|
|
28
|
+
and a list of allowed data types with their categorical status.
|
|
29
|
+
|
|
30
|
+
:return: dict containing the model's information
|
|
31
|
+
"""
|
|
32
|
+
allowed_data = [ad.to_json() for ad in self.allowed_data]
|
|
33
|
+
system_model_info = {
|
|
34
|
+
"algorithm": {
|
|
35
|
+
"name": self.name,
|
|
36
|
+
"default_loss_function": self.default_loss_function,
|
|
37
|
+
"description": self.description,
|
|
38
|
+
},
|
|
39
|
+
"datatypes": allowed_data,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return system_model_info
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TrainingInfo:
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
loss_fn: str,
|
|
8
|
+
train_samples: int,
|
|
9
|
+
train_loss: float,
|
|
10
|
+
validation_samples: int = None,
|
|
11
|
+
validation_loss: float = None,
|
|
12
|
+
):
|
|
13
|
+
self._loss_fn = loss_fn
|
|
14
|
+
self._train_samples = train_samples
|
|
15
|
+
self._train_loss = train_loss
|
|
16
|
+
self._validation_samples = validation_samples
|
|
17
|
+
self._validation_loss = validation_loss
|
|
18
|
+
|
|
19
|
+
def to_dict(self) -> dict:
|
|
20
|
+
"""
|
|
21
|
+
Convert the TrainingInfo to a dictionary
|
|
22
|
+
|
|
23
|
+
:return: dict: A dictionary with the training info
|
|
24
|
+
"""
|
|
25
|
+
return {
|
|
26
|
+
"loss_function": self._loss_fn,
|
|
27
|
+
"train_samples": self._train_samples,
|
|
28
|
+
"train_loss": self._train_loss,
|
|
29
|
+
"val_samples": self._validation_samples,
|
|
30
|
+
"val_loss": self._validation_loss,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def to_json(self) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Convert the TrainingInfo to a JSON string
|
|
36
|
+
|
|
37
|
+
:return: str: A JSON string with the training info
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
return json.dumps(self.to_dict())
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
from sdg_core_lib import NumericDataset
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class UnspecializedModel(ABC):
|
|
8
|
+
"""
|
|
9
|
+
Abstract class for all models. Implements common functionalities and defines abstract methods that must be implemented
|
|
10
|
+
by all subclasses.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
_metadata (dict): A dictionary containing miscellaneous information about the model.
|
|
14
|
+
model_name (str): The model name, used to identify the model itself.
|
|
15
|
+
input_shape (tuple): A tuple containing the input shape of the model.
|
|
16
|
+
_load_path (str): A string containing the path where to load the model from.
|
|
17
|
+
_model (keras.Model): The model instance.
|
|
18
|
+
_scaler (Scaler): The scaler instance.
|
|
19
|
+
training_info (TrainingInfo): The training info instance.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
metadata: dict,
|
|
25
|
+
model_name: str,
|
|
26
|
+
input_shape: str = None,
|
|
27
|
+
load_path: str = None,
|
|
28
|
+
):
|
|
29
|
+
self._metadata = metadata
|
|
30
|
+
self.model_name = model_name
|
|
31
|
+
self.input_shape = self._parse_stringed_input_shape(input_shape)
|
|
32
|
+
self._load_path = load_path
|
|
33
|
+
self._model = None # Placeholder for the model instance
|
|
34
|
+
self._scaler = None # Placeholder for model scaler
|
|
35
|
+
self.training_info = None # Placeholder for training info
|
|
36
|
+
self._model_misc = None # Placeholder for model miscellaneous info
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def _build(self, input_shape: str):
|
|
40
|
+
raise NotImplementedError
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def _load(self, model_filepath: str):
|
|
44
|
+
"""Load trained_models weights."""
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def _instantiate(self):
|
|
49
|
+
raise NotImplementedError
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def _scale(self, data: np.array):
|
|
53
|
+
"""Scale inputs with its logic"""
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def _inverse_scale(self, data: np.array):
|
|
58
|
+
"""Inverse scale inputs with its logic"""
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def _pre_process(self, data: NumericDataset, **kwargs):
|
|
63
|
+
"""Pre-process data"""
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def train(self, data):
|
|
68
|
+
"""Train the model."""
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def fine_tune(self, data: np.array, **kwargs):
|
|
73
|
+
"""Fine-tune the model."""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def infer(self, n_rows: int, **kwargs):
|
|
78
|
+
"""Run inference."""
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def save(self, folder_path):
|
|
83
|
+
"""Save Model."""
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def set_hyperparameters(self, **kwargs):
|
|
88
|
+
"""Set Hyperparameters"""
|
|
89
|
+
raise NotImplementedError
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def self_describe(cls):
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _parse_stringed_input_shape(stringed_shape: str) -> tuple[int, ...]:
|
|
97
|
+
"""
|
|
98
|
+
Parses a stringed list of numbers into a tuple
|
|
99
|
+
|
|
100
|
+
:param stringed_shape: a stringed list of number in format "[x,y,z]"
|
|
101
|
+
:return: a tuple of numbers, in format (x, y, z)
|
|
102
|
+
"""
|
|
103
|
+
brackets = ["(", ")", "[", "]", "{", "}"]
|
|
104
|
+
for b in brackets:
|
|
105
|
+
stringed_shape = stringed_shape.replace(b, "")
|
|
106
|
+
return tuple([int(n) for n in stringed_shape.split(",") if n != ""])
|
|
File without changes
|