radnn 0.0.3__py3-none-any.whl → 0.0.5__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.
- radnn/__init__.py +2 -1
- radnn/core.py +43 -14
- radnn/evaluation/__init__.py +1 -0
- radnn/evaluation/evaluate_classification.py +113 -0
- radnn/experiment/__init__.py +3 -2
- radnn/experiment/ml_experiment_config.py +85 -22
- radnn/experiment/ml_experiment_env.py +223 -0
- radnn/plots/__init__.py +4 -0
- radnn/plots/plot_confusion_matrix.py +54 -0
- radnn/plots/plot_learning_curve.py +73 -0
- radnn/plots/plot_roc.py +75 -0
- radnn/plots/plot_voronoi_2d.py +95 -0
- radnn/system/__init__.py +1 -0
- radnn/system/filesystem.py +44 -22
- radnn/system/hosts/__init__.py +10 -0
- radnn/system/hosts/colab_host.py +52 -0
- radnn/system/hosts/linux_host.py +3 -0
- radnn/system/hosts/windows_host.py +41 -0
- radnn/system/tee_logger.py +60 -0
- {radnn-0.0.3.dist-info → radnn-0.0.5.dist-info}/METADATA +1 -1
- radnn-0.0.5.dist-info/RECORD +32 -0
- radnn-0.0.3.dist-info/RECORD +0 -19
- {radnn-0.0.3.dist-info → radnn-0.0.5.dist-info}/LICENSE.txt +0 -0
- {radnn-0.0.3.dist-info → radnn-0.0.5.dist-info}/WHEEL +0 -0
- {radnn-0.0.3.dist-info → radnn-0.0.5.dist-info}/top_level.txt +0 -0
radnn/__init__.py
CHANGED
radnn/core.py
CHANGED
|
@@ -16,19 +16,7 @@ def is_opencv_installed():
|
|
|
16
16
|
|
|
17
17
|
# ----------------------------------------------------------------------------------------------------------------------
|
|
18
18
|
def system_name() -> str:
|
|
19
|
-
|
|
20
|
-
sHostName = socket.gethostname()
|
|
21
|
-
#sIPAddress = socket.gethostbyname(sHostName)
|
|
22
|
-
|
|
23
|
-
bIsColab = "google.colab" in sys.modules
|
|
24
|
-
if bIsColab:
|
|
25
|
-
sResult = "(colab)" #+ sIPAddress
|
|
26
|
-
else:
|
|
27
|
-
if sPlatform == "Windows":
|
|
28
|
-
sResult = "(windows)-" + sHostName
|
|
29
|
-
else:
|
|
30
|
-
sResult = "(linux)-" + sHostName
|
|
31
|
-
return sResult
|
|
19
|
+
return MLInfrastructure.host_name(False)
|
|
32
20
|
# ----------------------------------------------------------------------------------------------------------------------
|
|
33
21
|
def now_iso():
|
|
34
22
|
return datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
|
|
@@ -42,4 +30,45 @@ def shell_command_output(command_string):
|
|
|
42
30
|
oResult.append(sLine)
|
|
43
31
|
|
|
44
32
|
return oResult
|
|
45
|
-
# ----------------------------------------------------------------------------------------------------------------------
|
|
33
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#TODO: macOS support
|
|
41
|
+
|
|
42
|
+
class MLInfrastructure(object):
|
|
43
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
44
|
+
@classmethod
|
|
45
|
+
def is_linux(cls):
|
|
46
|
+
return not (cls.is_windows or cls.is_colab)
|
|
47
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
48
|
+
@classmethod
|
|
49
|
+
def is_windows(cls):
|
|
50
|
+
sPlatform = platform.system()
|
|
51
|
+
return (sPlatform == "Windows")
|
|
52
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
53
|
+
@classmethod
|
|
54
|
+
def is_colab(cls):
|
|
55
|
+
return "google.colab" in sys.modules
|
|
56
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
57
|
+
@classmethod
|
|
58
|
+
def host_name(cls, is_using_ip_address=True) -> str:
|
|
59
|
+
sPlatform = platform.system()
|
|
60
|
+
sHostName = socket.gethostname()
|
|
61
|
+
sIPAddress = socket.gethostbyname(sHostName)
|
|
62
|
+
|
|
63
|
+
bIsColab = "google.colab" in sys.modules
|
|
64
|
+
if bIsColab:
|
|
65
|
+
sResult = "(colab)"
|
|
66
|
+
if is_using_ip_address:
|
|
67
|
+
sResult += "-" + sIPAddress
|
|
68
|
+
else:
|
|
69
|
+
if sPlatform == "Windows":
|
|
70
|
+
sResult = "(windows)-" + sHostName
|
|
71
|
+
else:
|
|
72
|
+
sResult = "(linux)-" + sHostName
|
|
73
|
+
return sResult
|
|
74
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .evaluate_classification import EvaluateClassification
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2020-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
import numpy as np
|
|
27
|
+
from sklearn import metrics
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ==============================================================================================================================
|
|
31
|
+
class EvaluateClassification(object):
|
|
32
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
33
|
+
def __init__(self, actual_classes, predicted_classes, probabilities=None):
|
|
34
|
+
self.actual_classes = actual_classes
|
|
35
|
+
self.predicted_classes = predicted_classes
|
|
36
|
+
self.confusion_matrix = np.asarray(metrics.confusion_matrix(self.actual_classes, self.predicted_classes))
|
|
37
|
+
|
|
38
|
+
self.accuracy = metrics.accuracy_score(self.actual_classes, self.predicted_classes)
|
|
39
|
+
self.precision, self.recall, self.f1score, self.support = metrics.precision_recall_fscore_support(
|
|
40
|
+
self.actual_classes, self.predicted_classes, average=None)
|
|
41
|
+
self.average_precision, self.average_recall, self.average_f1score, self.average_support = metrics.precision_recall_fscore_support(
|
|
42
|
+
self.actual_classes, self.predicted_classes, average='weighted')
|
|
43
|
+
|
|
44
|
+
if probabilities is not None:
|
|
45
|
+
self.auc = metrics.roc_auc_score(actual_classes, probabilities)
|
|
46
|
+
else:
|
|
47
|
+
self.auc = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
self.true_neg = self.confusion_matrix[0][0]
|
|
51
|
+
self.false_pos = self.confusion_matrix[0][1]
|
|
52
|
+
self.false_neg = self.confusion_matrix[1][0]
|
|
53
|
+
self.true_pos = self.confusion_matrix[1][1]
|
|
54
|
+
|
|
55
|
+
self.class_count = None
|
|
56
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
57
|
+
def print_confusion_matrix(self):
|
|
58
|
+
nSize = len(self.confusion_matrix[0])
|
|
59
|
+
print(" Predicted ")
|
|
60
|
+
print(" --" + "-" * 5 * nSize)
|
|
61
|
+
sLabel = "Actual"
|
|
62
|
+
for nIndex, nRow in enumerate(self.confusion_matrix):
|
|
63
|
+
print(" %s | %s |" % (sLabel, " ".join(["%4d" % x for x in nRow])))
|
|
64
|
+
if nIndex == 0:
|
|
65
|
+
sLabel = " " * len(sLabel)
|
|
66
|
+
print(" --" + "-" * 5 * nSize)
|
|
67
|
+
print()
|
|
68
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
69
|
+
def format_series_as_pc(self, metric_series):
|
|
70
|
+
oValStr = []
|
|
71
|
+
for x in metric_series:
|
|
72
|
+
sX = f"{x*100.0:.2f}"
|
|
73
|
+
oValStr.append(sX)
|
|
74
|
+
|
|
75
|
+
oValues = [f"{x:^7}" for x in oValStr]
|
|
76
|
+
return " |".join(oValues)
|
|
77
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
78
|
+
def print_per_class(self, class_names=None):
|
|
79
|
+
if class_names is not None:
|
|
80
|
+
nClassCount = len(class_names.keys())
|
|
81
|
+
oClasses = [f"{class_names[x]:7}" for x in list(range(nClassCount))]
|
|
82
|
+
else:
|
|
83
|
+
oClasses = sorted(np.unique(self.actual_classes))
|
|
84
|
+
nClassCount = len(oClasses)
|
|
85
|
+
oClasses = [f"{x:^7}" for x in oClasses]
|
|
86
|
+
self.class_count = nClassCount
|
|
87
|
+
|
|
88
|
+
sClasses = " |".join(oClasses)
|
|
89
|
+
nRepeat = 28 + (7+2)*self.class_count
|
|
90
|
+
print(f" |{sClasses}|")
|
|
91
|
+
print("-"*nRepeat)
|
|
92
|
+
print(f"Per Class Recall % |{self.format_series_as_pc(self.recall[:])}|")
|
|
93
|
+
print(f"Per Class Precision % |{self.format_series_as_pc(self.precision[:])}|")
|
|
94
|
+
print("-" * nRepeat)
|
|
95
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
96
|
+
def print_overall(self):
|
|
97
|
+
print(f"Weighted Average Recall % :{self.average_recall*100.0:.3f}")
|
|
98
|
+
print(f"Weighted Average Precision %:{self.average_precision*100.0:.3f}")
|
|
99
|
+
print(f"Accuracy % :{self.accuracy*100.0 :.3f}")
|
|
100
|
+
print(f"Average F1 Score % :{self.average_f1score*100.0:.3f}")
|
|
101
|
+
if (self.class_count == 2) and (self.auc is not None):
|
|
102
|
+
print(f"Area Under the Curve (AUC):{self.auc:.4f}")
|
|
103
|
+
print()
|
|
104
|
+
# --------------------------------------------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ==============================================================================================================================
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# alias for compatibility
|
|
111
|
+
class CEvaluator(EvaluateClassification):
|
|
112
|
+
pass
|
|
113
|
+
|
radnn/experiment/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
from .ml_experiment_config import
|
|
2
|
-
|
|
1
|
+
from .ml_experiment_config import get_experiment_code, get_experiment_code_ex, experiment_number_and_variation, experiment_code_and_timestamp
|
|
2
|
+
from .ml_experiment_config import MLExperimentConfig
|
|
3
|
+
from .ml_experiment_env import MLExperimentEnv
|
|
3
4
|
|
|
@@ -25,31 +25,88 @@
|
|
|
25
25
|
|
|
26
26
|
import os
|
|
27
27
|
import json
|
|
28
|
+
import re
|
|
29
|
+
from datetime import datetime
|
|
28
30
|
|
|
31
|
+
from radnn.system import FileSystem
|
|
29
32
|
|
|
30
33
|
# --------------------------------------------------------------------------------------
|
|
31
|
-
def
|
|
34
|
+
def legacy_model_code(config_dict):
|
|
35
|
+
if "ModelName" in config_dict:
|
|
36
|
+
sCode = config_dict["ModelName"]
|
|
37
|
+
if "ModelVariation" in config_dict:
|
|
38
|
+
sCode += "_" + config_dict["ModelVariation"]
|
|
39
|
+
if "ExperimentNumber" in config_dict:
|
|
40
|
+
sCode = sCode + "_%02d" % config_dict["ExperimentNumber"]
|
|
41
|
+
else:
|
|
42
|
+
raise Exception("Invalid experiment configuration. Needs at least the key 'ModelName'.")
|
|
43
|
+
return sCode
|
|
44
|
+
# --------------------------------------------------------------------------------------
|
|
45
|
+
def get_experiment_code(config_dict):
|
|
32
46
|
if ("Experiment.BaseName" in config_dict) and ("Experiment.Number" in config_dict):
|
|
33
47
|
sBaseName = config_dict["Experiment.BaseName"]
|
|
34
48
|
nNumber = int(config_dict["Experiment.Number"])
|
|
35
49
|
sVariation = None
|
|
36
50
|
if "Experiment.Variation" in config_dict:
|
|
37
|
-
sVariation = config_dict["Experiment.Variation"]
|
|
51
|
+
sVariation = str(config_dict["Experiment.Variation"])
|
|
38
52
|
nFoldNumber = None
|
|
39
53
|
if "Experiment.FoldNumber" in config_dict:
|
|
40
|
-
nFoldNumber = config_dict["Experiment.FoldNumber"]
|
|
54
|
+
nFoldNumber = int(config_dict["Experiment.FoldNumber"])
|
|
41
55
|
|
|
42
|
-
sCode = "
|
|
56
|
+
sCode = f"{sBaseName}_{nNumber:02d}"
|
|
43
57
|
if sVariation is not None:
|
|
44
|
-
sCode += "
|
|
58
|
+
sCode += "." + sVariation
|
|
45
59
|
if nFoldNumber is not None:
|
|
46
|
-
sCode += "
|
|
60
|
+
sCode += f"-{nFoldNumber:02d}"
|
|
47
61
|
else:
|
|
48
62
|
raise Exception("Invalid experiment configuration. Needs at least two keys 'Experiment.BaseName'\n"
|
|
49
63
|
+ "and `Experiment.Number`.")
|
|
50
64
|
|
|
51
65
|
return sCode
|
|
52
66
|
# --------------------------------------------------------------------------------------
|
|
67
|
+
def get_experiment_code_ex(base_name, number, variation=None, fold_number=None):
|
|
68
|
+
if (base_name is not None) and (number is not None):
|
|
69
|
+
nNumber = int(number)
|
|
70
|
+
sVariation = None
|
|
71
|
+
if variation is not None:
|
|
72
|
+
sVariation = str(variation)
|
|
73
|
+
nFoldNumber = None
|
|
74
|
+
if fold_number is not None:
|
|
75
|
+
nFoldNumber = int(fold_number)
|
|
76
|
+
|
|
77
|
+
sCode = f"{base_name}_{nNumber:02d}"
|
|
78
|
+
if variation is not None:
|
|
79
|
+
sCode += "." + sVariation
|
|
80
|
+
if nFoldNumber is not None:
|
|
81
|
+
sCode += f"-{nFoldNumber:02d}"
|
|
82
|
+
else:
|
|
83
|
+
raise Exception("Invalid experiment code parts. Needs a base name and a number.")
|
|
84
|
+
|
|
85
|
+
return sCode
|
|
86
|
+
# --------------------------------------------------------------------------------------
|
|
87
|
+
def experiment_number_and_variation(experiment_code):
|
|
88
|
+
if type(experiment_code) == int:
|
|
89
|
+
nNumber = int(experiment_code)
|
|
90
|
+
sVariation = None
|
|
91
|
+
else:
|
|
92
|
+
sParts = experiment_code.split(".")
|
|
93
|
+
nNumber = int(sParts[0])
|
|
94
|
+
if len(sParts) > 1:
|
|
95
|
+
sVariation = sParts[1]
|
|
96
|
+
else:
|
|
97
|
+
sVariation = None
|
|
98
|
+
|
|
99
|
+
return nNumber, sVariation
|
|
100
|
+
# --------------------------------------------------------------------------------------
|
|
101
|
+
def experiment_code_and_timestamp(filename):
|
|
102
|
+
sName, _ = os.path.splitext(os.path.split(filename)[1])
|
|
103
|
+
sParts = re.split(r"_", sName, 2)
|
|
104
|
+
sISODate = f"{sParts[0]}T{sParts[1][0:2]}:{sParts[1][2:4]}:{sParts[1][4:6]}"
|
|
105
|
+
sExperimentCode = sParts[2]
|
|
106
|
+
dRunTimestamp = datetime.fromisoformat(sISODate)
|
|
107
|
+
return sExperimentCode, dRunTimestamp
|
|
108
|
+
# --------------------------------------------------------------------------------------
|
|
109
|
+
|
|
53
110
|
|
|
54
111
|
|
|
55
112
|
|
|
@@ -58,27 +115,27 @@ def experiment_code(config_dict):
|
|
|
58
115
|
# =========================================================================================================================
|
|
59
116
|
class MLExperimentConfig(dict):
|
|
60
117
|
# --------------------------------------------------------------------------------------
|
|
61
|
-
def __init__(self, filename=None,
|
|
62
|
-
self["Experiment.BaseName"] =
|
|
118
|
+
def __init__(self, filename=None, base_name=None, number=None, variation=None, fold_number=None, hyperparams=None):
|
|
119
|
+
self["Experiment.BaseName"] = base_name
|
|
63
120
|
self.filename = filename
|
|
64
|
-
if filename is not None:
|
|
121
|
+
if self.filename is not None:
|
|
65
122
|
self.load()
|
|
66
123
|
|
|
67
|
-
if
|
|
68
|
-
self["Experiment.Number"] =
|
|
69
|
-
if
|
|
70
|
-
self["Experiment.Variation"] =
|
|
71
|
-
if
|
|
72
|
-
self["Experiment.FoldNumber"] =
|
|
124
|
+
if number is not None:
|
|
125
|
+
self["Experiment.Number"] = number
|
|
126
|
+
if variation is not None:
|
|
127
|
+
self["Experiment.Variation"] = variation
|
|
128
|
+
if fold_number is not None:
|
|
129
|
+
self["Experiment.FoldNumber"] = fold_number
|
|
73
130
|
|
|
74
131
|
if hyperparams is not None:
|
|
75
132
|
self.assign(hyperparams)
|
|
76
133
|
# --------------------------------------------------------------------------------------
|
|
77
134
|
@property
|
|
78
135
|
def experiment_code(self):
|
|
79
|
-
return
|
|
136
|
+
return get_experiment_code(self)
|
|
80
137
|
# --------------------------------------------------------------------------------------
|
|
81
|
-
def load(self, filename, must_exist=False):
|
|
138
|
+
def load(self, filename=None, must_exist=False):
|
|
82
139
|
if filename is None:
|
|
83
140
|
filename = self.filename
|
|
84
141
|
|
|
@@ -118,16 +175,22 @@ class MLExperimentConfig(dict):
|
|
|
118
175
|
return self
|
|
119
176
|
|
|
120
177
|
# --------------------------------------------------------------------------------------
|
|
121
|
-
def save_config(self,
|
|
178
|
+
def save_config(self, fs, filename_only=None):
|
|
179
|
+
if isinstance(fs, FileSystem):
|
|
180
|
+
fs = fs.configs
|
|
181
|
+
|
|
122
182
|
if filename_only is None:
|
|
123
|
-
filename_only =
|
|
183
|
+
filename_only = get_experiment_code(self)
|
|
124
184
|
|
|
125
|
-
sFileName =
|
|
185
|
+
sFileName = fs.file(filename_only + ".json")
|
|
126
186
|
return self.save(sFileName)
|
|
127
187
|
|
|
128
188
|
# --------------------------------------------------------------------------------------
|
|
129
|
-
def load_config(self,
|
|
130
|
-
|
|
189
|
+
def load_config(self, fs, filename_only):
|
|
190
|
+
if isinstance(fs, FileSystem):
|
|
191
|
+
fs = fs.configs
|
|
192
|
+
|
|
193
|
+
sFileName = fs.file(filename_only + ".json")
|
|
131
194
|
return self.load(sFileName)
|
|
132
195
|
# --------------------------------------------------------------------------------------
|
|
133
196
|
def setDefaults(self):
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2023-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import shutil
|
|
28
|
+
import sys
|
|
29
|
+
import re
|
|
30
|
+
|
|
31
|
+
from radnn.experiment import MLExperimentConfig
|
|
32
|
+
from radnn.experiment import get_experiment_code_ex, experiment_number_and_variation, experiment_code_and_timestamp
|
|
33
|
+
from radnn.core import now_iso
|
|
34
|
+
from radnn.system import FileSystem, FileStore
|
|
35
|
+
from radnn.system.tee_logger import TeeLogger
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MLExperimentEnv(dict):
|
|
40
|
+
|
|
41
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
42
|
+
@classmethod
|
|
43
|
+
def experiment_filename_split(cls, filename):
|
|
44
|
+
sTryFileName, sTryExt = os.path.splitext(filename)
|
|
45
|
+
bIsVariationAndFold = "-" in sTryExt
|
|
46
|
+
if bIsVariationAndFold:
|
|
47
|
+
#LREXPLAINET22_MNIST_64.1-01
|
|
48
|
+
sMainParts = filename.split("-")
|
|
49
|
+
assert len(sMainParts) == 2, "Wrong experiment filename"
|
|
50
|
+
sFoldNumber, _ = os.path.splitext(sMainParts[1])
|
|
51
|
+
sParts = sMainParts[0].split("_")
|
|
52
|
+
sModelName = f"{sParts[0]}_{sParts[1]}"
|
|
53
|
+
sModelVar = sParts[2]
|
|
54
|
+
else:
|
|
55
|
+
sFileNameOnly, _ = os.path.splitext(filename)
|
|
56
|
+
sMainParts = sFileNameOnly.split("-")
|
|
57
|
+
if len(sMainParts) > 1:
|
|
58
|
+
sFoldNumber = sMainParts[1]
|
|
59
|
+
else:
|
|
60
|
+
sFoldNumber = None
|
|
61
|
+
sParts = sMainParts[0].split("_")
|
|
62
|
+
sModelName = f"{sParts[0]}_{sParts[1]}"
|
|
63
|
+
sModelVar = sParts[2]
|
|
64
|
+
|
|
65
|
+
return sModelName, sModelVar, sFoldNumber
|
|
66
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
67
|
+
@classmethod
|
|
68
|
+
def preload_config(cls, config_folder, experiment_group=None, experiment_base_name=None, experiment_variation=None, experiment_fold_number=None, experiment_filename=None):
|
|
69
|
+
oPrintOutput = []
|
|
70
|
+
oPrintOutput.append(f"[?] Experiment started at {now_iso()}")
|
|
71
|
+
oPrintOutput.append(f" |__ {'model':<24}: {experiment_base_name}")
|
|
72
|
+
|
|
73
|
+
dExperimentSpec = {"base_name": experiment_base_name, "variation": experiment_variation, "fold_number": experiment_fold_number}
|
|
74
|
+
if experiment_filename is not None:
|
|
75
|
+
# LREXPLAINET18_MNIST_08.1-01.json
|
|
76
|
+
_, sFileNameFull = os.path.split(experiment_filename)
|
|
77
|
+
experiment_base_name, experiment_variation, experiment_fold_number = cls.experiment_filename_split(sFileNameFull)
|
|
78
|
+
experiment_fold_number = int(experiment_fold_number)
|
|
79
|
+
dExperimentSpec = {"base_name": experiment_base_name, "variation": experiment_variation, "fold_number": experiment_fold_number}
|
|
80
|
+
|
|
81
|
+
if experiment_group is not None:
|
|
82
|
+
config_folder = os.path.join(config_folder, experiment_group)
|
|
83
|
+
oConfigFS = FileStore(config_folder, must_exist=True)
|
|
84
|
+
|
|
85
|
+
if "." in experiment_variation:
|
|
86
|
+
sParts = experiment_variation.split(".")
|
|
87
|
+
experiment_variation = f"{int(sParts[0]):02d}.{sParts[1]}"
|
|
88
|
+
else:
|
|
89
|
+
experiment_variation = f"{int(experiment_variation):02d}"
|
|
90
|
+
sMessage = f" |__ {'variation':<24}: {experiment_variation}"
|
|
91
|
+
|
|
92
|
+
if experiment_fold_number is not None:
|
|
93
|
+
experiment_variation = f"{experiment_variation}-{experiment_fold_number:02d}"
|
|
94
|
+
sMessage += f" fold: {experiment_fold_number}"
|
|
95
|
+
oPrintOutput.append(sMessage)
|
|
96
|
+
|
|
97
|
+
if experiment_filename is not None:
|
|
98
|
+
sExperimentFileName = experiment_filename
|
|
99
|
+
else:
|
|
100
|
+
sExperimentFileName = oConfigFS.file(f"{experiment_base_name}_{experiment_variation}.json")
|
|
101
|
+
oConfig = MLExperimentConfig(sExperimentFileName,p_nExperimentNumber=experiment_variation)
|
|
102
|
+
oPrintOutput.append(f" |__ {'configuration file':<24}: {sExperimentFileName}")
|
|
103
|
+
|
|
104
|
+
return oConfig, oPrintOutput, dExperimentSpec
|
|
105
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
106
|
+
|
|
107
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
108
|
+
def __init__(self, config_fs, model_fs=None, base_name=None, number=None, variation=None, fold_number=None,
|
|
109
|
+
experiment_filename=None, experiment_code=None, experiment_config=None, model_filestore=None):
|
|
110
|
+
|
|
111
|
+
if isinstance(config_fs, FileSystem):
|
|
112
|
+
oConfigFS = config_fs.configs
|
|
113
|
+
oModelFS = config_fs.models
|
|
114
|
+
else:
|
|
115
|
+
oConfigFS = config_fs
|
|
116
|
+
oModelFS = model_fs
|
|
117
|
+
if oModelFS is None:
|
|
118
|
+
oModelFS = config_fs
|
|
119
|
+
|
|
120
|
+
# ...................... | Fields | ......................
|
|
121
|
+
self.config_fs = oConfigFS
|
|
122
|
+
self.model_fs = oModelFS
|
|
123
|
+
self.base_name = base_name
|
|
124
|
+
self.number = number
|
|
125
|
+
self.variation = variation
|
|
126
|
+
self.fold_number = fold_number
|
|
127
|
+
|
|
128
|
+
if (experiment_filename is not None) or (experiment_code is not None):
|
|
129
|
+
self.base_name, self.number, self.variation, self.fold_number, _ = self.determine_code_parts(experiment_filename,
|
|
130
|
+
experiment_code, number, variation, fold_number)
|
|
131
|
+
|
|
132
|
+
self.experiment_filename = experiment_filename
|
|
133
|
+
if self.experiment_filename is None:
|
|
134
|
+
sExperimentCode = get_experiment_code_ex(self.base_name, self.number, self.variation, self.fold_number)
|
|
135
|
+
if experiment_code is not None:
|
|
136
|
+
assert sExperimentCode == experiment_code, "Re-created experiment code mismatch."
|
|
137
|
+
self.experiment_filename = self.config_fs.file(f"{sExperimentCode}.json")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
self.is_debugable = False
|
|
141
|
+
self.is_retraining = False
|
|
142
|
+
|
|
143
|
+
self._config = experiment_config
|
|
144
|
+
if self._config is None:
|
|
145
|
+
self._config = MLExperimentConfig(self.experiment_filename, number=self.number)
|
|
146
|
+
self.experiment_fs = self.model_fs.subfs(self.experiment_code)
|
|
147
|
+
# ........................................................
|
|
148
|
+
|
|
149
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
150
|
+
def save_config(self):
|
|
151
|
+
self._config.save(self.experiment_filename)
|
|
152
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
153
|
+
def determine_code_parts(self, experiment_filename, experiment_code, number, variation, fold_number):
|
|
154
|
+
sExperimentCode = None
|
|
155
|
+
if experiment_code is not None:
|
|
156
|
+
sExperimentCode = experiment_code
|
|
157
|
+
elif experiment_filename is not None:
|
|
158
|
+
_, sFileNameFull = os.path.split(experiment_filename)
|
|
159
|
+
sExperimentCode, _ = os.path.splitext(sFileNameFull)
|
|
160
|
+
|
|
161
|
+
sISODate = None
|
|
162
|
+
if sExperimentCode is not None:
|
|
163
|
+
oMatch = re.match(r"^\d{4}-\d{2}-\d{2}_\d{6}_", sExperimentCode)
|
|
164
|
+
if oMatch is not None:
|
|
165
|
+
sISODate = oMatch.group()[:-1]
|
|
166
|
+
sExperimentCode = sExperimentCode[18:]
|
|
167
|
+
|
|
168
|
+
base_name, variation, sFoldNumber = MLExperimentEnv.experiment_filename_split(sExperimentCode)
|
|
169
|
+
if sFoldNumber is not None:
|
|
170
|
+
fold_number = int(sFoldNumber)
|
|
171
|
+
|
|
172
|
+
if number is None:
|
|
173
|
+
number, variation = experiment_number_and_variation(variation)
|
|
174
|
+
|
|
175
|
+
return base_name, number, variation, fold_number, sISODate
|
|
176
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
177
|
+
@property
|
|
178
|
+
def config(self):
|
|
179
|
+
return self._config
|
|
180
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
181
|
+
@property
|
|
182
|
+
def experiment_code(self):
|
|
183
|
+
return get_experiment_code_ex(self.base_name, self.number, self.variation, self.fold_number)
|
|
184
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
185
|
+
def copy_config(self, start_timestamp):
|
|
186
|
+
sOriginalFileName = self.experiment_filename
|
|
187
|
+
sNewFileName = self.experiment_fs.file(f"{start_timestamp}_{self.experiment_code}.json")
|
|
188
|
+
shutil.copy(sOriginalFileName, sNewFileName)
|
|
189
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
190
|
+
def move_log(self, start_timestamp, original_filename):
|
|
191
|
+
self.copy_config(start_timestamp)
|
|
192
|
+
_, original_filename_only = os.path.split(original_filename)
|
|
193
|
+
sNewFileName = f"{start_timestamp}_{self.experiment_code}.{original_filename_only}"
|
|
194
|
+
shutil.move(original_filename, self.experiment_fs.file(sNewFileName))
|
|
195
|
+
sys.stdout = TeeLogger(self.experiment_fs.file(sNewFileName))
|
|
196
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
197
|
+
'''
|
|
198
|
+
def AssignSystemParams(self, p_oParamsDict):
|
|
199
|
+
self.number = p_oParamsDict["ModelNumber"]
|
|
200
|
+
self.is_debugable = p_oParamsDict["IsDebuggable"]
|
|
201
|
+
self.is_retraining = p_oParamsDict["IsRetraining"]
|
|
202
|
+
|
|
203
|
+
self._config = MLExperimentConfig(self.model_fs.file(self.experiment_code + ".json"), number=self.number)
|
|
204
|
+
'''
|
|
205
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
206
|
+
def __str__(self)->str:
|
|
207
|
+
sResult = f"Experiment Code: {self.experiment_code} | Debugable: {self.is_debugable} | Retraining: {self.is_retraining}\n"
|
|
208
|
+
sResult += f" |___ base name: {self.base_name} , number:{self.number} , variation:{self.variation}, fold_number: {self.fold_number}\n"
|
|
209
|
+
sResult += f" |___ file name: {self.experiment_filename}\n"
|
|
210
|
+
sResult += f"Models FileStore : {self.model_fs}\n"
|
|
211
|
+
sResult += f"Configs FileStore : {self.config_fs}\n"
|
|
212
|
+
sResult += f"Experiment FileStore: {self.experiment_fs}\n"
|
|
213
|
+
sResult += f"Experiment Filename : {self.experiment_filename}\n"
|
|
214
|
+
sResult += f"Configuration\n"
|
|
215
|
+
sResult += "-"*40 + "\n"
|
|
216
|
+
sResult += str(self._config) + "\n"
|
|
217
|
+
return sResult
|
|
218
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
219
|
+
def __repr__(self)->str:
|
|
220
|
+
return self.__str__()
|
|
221
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
|
radnn/plots/__init__.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2020-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
import matplotlib.pyplot as plt
|
|
28
|
+
|
|
29
|
+
class PlotConfusionMatrix(object):
|
|
30
|
+
# --------------------------------------------------------------------------------------
|
|
31
|
+
def __init__(self, confusion_matrix, title="Confusion Matrix"):
|
|
32
|
+
self.confusion_matrix = confusion_matrix
|
|
33
|
+
self.title = title
|
|
34
|
+
# --------------------------------------------------------------------------------------
|
|
35
|
+
def prepare(self):
|
|
36
|
+
fig, ax = plt.subplots(figsize=(7.5, 7.5))
|
|
37
|
+
ax.matshow(self.confusion_matrix, cmap=plt.cm.Blues, alpha=0.3)
|
|
38
|
+
for i in range(self.confusion_matrix.shape[0]):
|
|
39
|
+
for j in range(self.confusion_matrix.shape[1]):
|
|
40
|
+
ax.text(x=j, y=i, s=self.confusion_matrix[i, j], va='center', ha='center', size='xx-large')
|
|
41
|
+
|
|
42
|
+
plt.xlabel('Predicted Label', fontsize=18)
|
|
43
|
+
plt.ylabel('Actual Label', fontsize=18)
|
|
44
|
+
plt.title(self.title, fontsize=18)
|
|
45
|
+
return self
|
|
46
|
+
# --------------------------------------------------------------------------------------
|
|
47
|
+
def save(self, filename):
|
|
48
|
+
plt.savefig(filename, bbox_inches='tight')
|
|
49
|
+
return self
|
|
50
|
+
# --------------------------------------------------------------------------------------
|
|
51
|
+
def show(self):
|
|
52
|
+
plt.show()
|
|
53
|
+
# --------------------------------------------------------------------------------------
|
|
54
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2020-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
import matplotlib.pyplot as plt
|
|
27
|
+
|
|
28
|
+
class PlotLearningCurve(object):
|
|
29
|
+
# --------------------------------------------------------------------------------------
|
|
30
|
+
def __init__(self, metrics_dict, model_name):
|
|
31
|
+
self.metrics_dict = metrics_dict
|
|
32
|
+
self.model_name = model_name
|
|
33
|
+
print("Keys in training process log:", self.metrics_dict.keys())
|
|
34
|
+
# --------------------------------------------------------------------------------------
|
|
35
|
+
def prepare(self, metric_key="accuracy", custom_title=None, is_legend_right=False):
|
|
36
|
+
plt.clf()
|
|
37
|
+
plt.plot(self.metrics_dict[metric_key])
|
|
38
|
+
sValidationMetricName = "val_" + metric_key
|
|
39
|
+
if sValidationMetricName in self.metrics_dict:
|
|
40
|
+
plt.plot(self.metrics_dict[sValidationMetricName])
|
|
41
|
+
if custom_title is None:
|
|
42
|
+
plt.title(self.model_name + ' ' + metric_key)
|
|
43
|
+
else:
|
|
44
|
+
plt.title(self.model_name + ' ' + custom_title)
|
|
45
|
+
plt.ylabel(metric_key)
|
|
46
|
+
plt.xlabel("Epoch")
|
|
47
|
+
if is_legend_right:
|
|
48
|
+
plt.legend(["train", 'validation'], loc="upper right")
|
|
49
|
+
else:
|
|
50
|
+
plt.legend(["train", "validation"], loc="upper left")
|
|
51
|
+
return self
|
|
52
|
+
# --------------------------------------------------------------------------------------
|
|
53
|
+
def prepare_cost(self, cost_function=None):
|
|
54
|
+
if isinstance(cost_function, str):
|
|
55
|
+
sCostFunctionName = cost_function
|
|
56
|
+
else:
|
|
57
|
+
sClassName = str(cost_function.__class__)
|
|
58
|
+
if ("keras" in sClassName) and ("losses" in sClassName):
|
|
59
|
+
sCostFunctionNameParts = cost_function.name.split("_")
|
|
60
|
+
sCostFunctionNameParts = [x.capitalize() + " " for x in sCostFunctionNameParts]
|
|
61
|
+
sCostFunctionName = " ".join(sCostFunctionNameParts)
|
|
62
|
+
|
|
63
|
+
return self.prepare("loss", sCostFunctionName, True)
|
|
64
|
+
# --------------------------------------------------------------------------------------
|
|
65
|
+
def save(self, filename):
|
|
66
|
+
plt.savefig(filename, bbox_inches='tight')
|
|
67
|
+
return self
|
|
68
|
+
# --------------------------------------------------------------------------------------
|
|
69
|
+
def show(self):
|
|
70
|
+
plt.show()
|
|
71
|
+
# --------------------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
|
radnn/plots/plot_roc.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2024-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
from sklearn import metrics
|
|
27
|
+
import matplotlib.pyplot as plt
|
|
28
|
+
|
|
29
|
+
'''
|
|
30
|
+
Receiver Operator Characteristics (ROC) Plot
|
|
31
|
+
'''
|
|
32
|
+
class PlotROC(object):
|
|
33
|
+
# --------------------------------------------------------------------------------------
|
|
34
|
+
def __init__(self, actual_labels, predicted_probs, title="Receiving Operator Characteristics Curve"):
|
|
35
|
+
self.actual_labels = actual_labels
|
|
36
|
+
self.predicted_probs = predicted_probs
|
|
37
|
+
self.title = title
|
|
38
|
+
# --------------------------------------------------------------------------------------
|
|
39
|
+
def prepare(self, true_threshold=0.5, figure_size=[6.00, 6.00], is_showing_grid=True):
|
|
40
|
+
plt.rcParams["figure.figsize"] = figure_size
|
|
41
|
+
plt.rcParams["figure.autolayout"] = True
|
|
42
|
+
|
|
43
|
+
nFPR, nTPR, nThresholds = metrics.roc_curve(self.actual_labels, self.predicted_probs)
|
|
44
|
+
nAUC = metrics.roc_auc_score(self.actual_labels, self.predicted_probs)
|
|
45
|
+
plt.xlim(0, 1.02)
|
|
46
|
+
plt.ylim(0, 1.02)
|
|
47
|
+
if is_showing_grid:
|
|
48
|
+
plt.grid()
|
|
49
|
+
plt.plot(nFPR, nTPR, label="ROC curve (AUC=%.2f)" % nAUC, linewidth=2)
|
|
50
|
+
plt.plot([0.0, 1.0], [0.0, 1.0], 'r--', label="Random prediction") # , color="yellow", linewidth=1)
|
|
51
|
+
|
|
52
|
+
plt.ylabel("TPR (True Positive Rate)")
|
|
53
|
+
plt.xlabel("FPR (False Positive Rate)")
|
|
54
|
+
|
|
55
|
+
nPointIndex = None
|
|
56
|
+
for nThresholdIndex, nThreshold in enumerate(nThresholds):
|
|
57
|
+
if nThreshold < true_threshold:
|
|
58
|
+
nPointIndex = nThresholdIndex
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
if nPointIndex is not None:
|
|
62
|
+
plt.plot(nFPR[nPointIndex], nTPR[nPointIndex]
|
|
63
|
+
, label="Threshold %.2f" % nThreshold
|
|
64
|
+
, marker="o", markersize=10, markeredgecolor="blue", markerfacecolor="yellow")
|
|
65
|
+
plt.title(self.title, fontsize=18)
|
|
66
|
+
plt.legend(loc=4)
|
|
67
|
+
return self
|
|
68
|
+
# --------------------------------------------------------------------------------------
|
|
69
|
+
def save(self, filename):
|
|
70
|
+
plt.savefig(filename, bbox_inches='tight')
|
|
71
|
+
return self
|
|
72
|
+
# --------------------------------------------------------------------------------------
|
|
73
|
+
def show(self):
|
|
74
|
+
plt.show()
|
|
75
|
+
# --------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2024-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
import matplotlib.pyplot as plt
|
|
28
|
+
import numpy as np
|
|
29
|
+
from matplotlib import cm
|
|
30
|
+
|
|
31
|
+
class PlotVoronoi2D(object):
|
|
32
|
+
# --------------------------------------------------------------------------------------
|
|
33
|
+
# Constructor
|
|
34
|
+
def __init__(self, samples_2d, labels, ground_truth_cluster_count=10, title="2D Voronoi Plot"):
|
|
35
|
+
# ................................................................
|
|
36
|
+
# // Fields \\
|
|
37
|
+
self.title = title
|
|
38
|
+
self.samples_2d = samples_2d
|
|
39
|
+
self.labels = labels
|
|
40
|
+
self.ground_truth_cluster_count = ground_truth_cluster_count
|
|
41
|
+
|
|
42
|
+
if (self.ground_truth_cluster_count <= 10):
|
|
43
|
+
self.color_map = cm.get_cmap("tab10")
|
|
44
|
+
elif (self.ground_truth_cluster_count <= 20):
|
|
45
|
+
self.color_map = cm.get_cmap("tab20")
|
|
46
|
+
else:
|
|
47
|
+
self.color_map = cm.get_cmap("prism")
|
|
48
|
+
|
|
49
|
+
self.point_size = 8
|
|
50
|
+
self.plot_dimensions = [14, 8]
|
|
51
|
+
# ................................................................
|
|
52
|
+
# --------------------------------------------------------------------------------------
|
|
53
|
+
def prepare_for_kmeans(self, kmeans_model):
|
|
54
|
+
reduced_data = self.samples_2d
|
|
55
|
+
|
|
56
|
+
# Step size of the mesh. Decrease to increase the quality of the VQ.
|
|
57
|
+
#h = .02 # point in the mesh [x_min, x_max]x[y_min, y_max].
|
|
58
|
+
h = .4 # point in the mesh [x_min, x_max]x[y_min, y_max].
|
|
59
|
+
|
|
60
|
+
# Plot the decision boundary. For that, we will assign a color to each
|
|
61
|
+
x_min, x_max = reduced_data[:, 0].min() - 1, reduced_data[:, 0].max() + 1
|
|
62
|
+
y_min, y_max = reduced_data[:, 1].min() - 1, reduced_data[:, 1].max() + 1
|
|
63
|
+
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
|
|
64
|
+
|
|
65
|
+
# Obtain labels for each point in mesh. Use last trained model.
|
|
66
|
+
Z = kmeans_model.predict(np.c_[xx.ravel(), yy.ravel()])
|
|
67
|
+
|
|
68
|
+
# Put the result into a color plot
|
|
69
|
+
Z = Z.reshape(xx.shape)
|
|
70
|
+
plt.figure(1, figsize=self.plot_dimensions)
|
|
71
|
+
plt.clf()
|
|
72
|
+
plt.imshow(Z, interpolation="nearest",
|
|
73
|
+
extent=(xx.min(), xx.max(), yy.min(), yy.max()),
|
|
74
|
+
cmap=cm.get_cmap("tab20"), aspect="auto", origin="lower")
|
|
75
|
+
|
|
76
|
+
#plt.plot(reduced_data[:, 0], reduced_data[:, 1], 'k.', markersize=2)
|
|
77
|
+
plt.scatter(reduced_data[:, 0], reduced_data[:, 1], c=self.labels, s=self.point_size, cmap=self.color_map)
|
|
78
|
+
|
|
79
|
+
# Plot the centroids as a white X
|
|
80
|
+
centroids = kmeans_model.cluster_centers_
|
|
81
|
+
plt.scatter(centroids[:, 0], centroids[:, 1], marker="x", s=169, linewidths=3
|
|
82
|
+
,color="w", zorder=10 )
|
|
83
|
+
plt.title(self.title)
|
|
84
|
+
plt.xlim(x_min, x_max)
|
|
85
|
+
plt.ylim(y_min, y_max)
|
|
86
|
+
plt.xticks(())
|
|
87
|
+
plt.yticks(())
|
|
88
|
+
# --------------------------------------------------------------------------------------
|
|
89
|
+
def save(self, filename):
|
|
90
|
+
plt.savefig(filename, bbox_inches='tight')
|
|
91
|
+
return self
|
|
92
|
+
# --------------------------------------------------------------------------------------
|
|
93
|
+
def show(self):
|
|
94
|
+
plt.show()
|
|
95
|
+
# --------------------------------------------------------------------------------------
|
radnn/system/__init__.py
CHANGED
radnn/system/filesystem.py
CHANGED
|
@@ -33,55 +33,77 @@ from radnn.system.files import JSONFile
|
|
|
33
33
|
# =======================================================================================================================
|
|
34
34
|
class FileSystem(object):
|
|
35
35
|
# --------------------------------------------------------------------------------------------------------
|
|
36
|
-
def __init__(self, config_folder=
|
|
36
|
+
def __init__(self, config_folder="MLConfig", model_folder="MLModels", dataset_folder="MLData", must_exist=False, setup_filename="*auto*"):
|
|
37
37
|
'''
|
|
38
38
|
Initializes the file system settings for an experiment
|
|
39
39
|
:param config_folder: The folder that contains the experiment hyperparameter files.
|
|
40
40
|
:param model_folder: The main folder where the models are stored.
|
|
41
41
|
:param dataset_folder: The main folder under which dataset folder are stores.
|
|
42
|
-
:param model_group: A group of models under the main folder will be used instead.
|
|
43
42
|
:param setup_filename: The filename of the setup file.
|
|
44
43
|
:param must_exist: False: Auto-creates the directories on the file system | True: Raises an error if it does not exist.
|
|
45
44
|
'''
|
|
46
45
|
|
|
47
46
|
self.setup_filename = setup_filename
|
|
48
|
-
if self.setup_filename
|
|
47
|
+
if self.setup_filename == "*auto*":
|
|
49
48
|
self.setup_filename = system_name() + ".fsys"
|
|
49
|
+
self._must_exist = must_exist
|
|
50
|
+
self._has_loaded_groups = False
|
|
51
|
+
self.model_groups = []
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
dSetup = None
|
|
54
|
+
if self.setup_filename is not None:
|
|
55
|
+
if os.path.exists(self.setup_filename):
|
|
56
|
+
oFile = JSONFile(self.setup_filename)
|
|
57
|
+
dSetup = oFile.load()
|
|
58
|
+
config_folder = dSetup["Configs"]
|
|
59
|
+
model_folder = dSetup["Models"]
|
|
60
|
+
dataset_folder = dSetup["Datasets"]
|
|
61
|
+
if "ModelGroups" in dSetup:
|
|
62
|
+
self.model_groups = dSetup["ModelGroups"]
|
|
63
|
+
self._has_loaded_groups = True
|
|
64
|
+
|
|
65
|
+
if dSetup is None:
|
|
61
66
|
dSetup = dict()
|
|
62
67
|
dSetup["Configs"] = config_folder
|
|
63
68
|
dSetup["Models"] = model_folder
|
|
64
69
|
dSetup["Datasets"] = dataset_folder
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
|
|
72
|
+
if len(self.model_groups) > 0:
|
|
73
|
+
for model_group in self.model_groups:
|
|
74
|
+
config_folder = os.path.join(config_folder, model_group)
|
|
75
|
+
model_folder = os.path.join(model_folder, model_group)
|
|
69
76
|
|
|
70
77
|
# ...................... | Fields | ......................
|
|
71
78
|
self.setup = dSetup
|
|
72
|
-
self.configs = FileStore(config_folder, must_exist=
|
|
73
|
-
self.models = FileStore(model_folder, must_exist=
|
|
74
|
-
self.datasets = FileStore(dataset_folder, must_exist=
|
|
79
|
+
self.configs = FileStore(config_folder, must_exist=self._must_exist)
|
|
80
|
+
self.models = FileStore(model_folder, must_exist=self._must_exist)
|
|
81
|
+
self.datasets = FileStore(dataset_folder, must_exist=self._must_exist)
|
|
75
82
|
# ........................................................
|
|
76
83
|
# --------------------------------------------------------------------------------------------------------
|
|
84
|
+
def group(self, group_name):
|
|
85
|
+
if not self._has_loaded_groups:
|
|
86
|
+
if group_name is not None:
|
|
87
|
+
self.model_groups.append(group_name)
|
|
88
|
+
self.setup["ModelGroups"] = self.model_groups
|
|
89
|
+
|
|
90
|
+
self.configs = self.configs.subfs(group_name, must_exist=self._must_exist)
|
|
91
|
+
self.models = self.models.subfs(group_name, must_exist=self._must_exist)
|
|
92
|
+
|
|
93
|
+
return self
|
|
94
|
+
# --------------------------------------------------------------------------------------------------------
|
|
77
95
|
def save_setup(self):
|
|
78
96
|
oFile = JSONFile(self.setup_filename)
|
|
79
97
|
oFile.save(self.setup)
|
|
80
98
|
# --------------------------------------------------------------------------------------------------------
|
|
81
99
|
def __str__(self)->str:
|
|
82
|
-
sResult = f"
|
|
83
|
-
sResult += f"
|
|
84
|
-
sResult += f"
|
|
100
|
+
sResult = f" Configs: \"{self.configs.base_folder}\",\n"
|
|
101
|
+
sResult += f" Models: \"{self.models.base_folder}\",\n"
|
|
102
|
+
sResult += f" Datasets: \"{self.datasets.base_folder}\"\n"
|
|
103
|
+
if len(self.model_groups) > 0:
|
|
104
|
+
sModelGroups = str(self.model_groups)
|
|
105
|
+
sModelGroups = sModelGroups.replace("'", "\"")
|
|
106
|
+
sResult += f" ModelGroups: {sModelGroups}\n"
|
|
85
107
|
sResult = "{\n" + sResult + "}"
|
|
86
108
|
return sResult
|
|
87
109
|
# --------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from radnn.core import MLInfrastructure
|
|
2
|
+
|
|
3
|
+
if MLInfrastructure.is_windows():
|
|
4
|
+
from .windows_host import WindowsHost
|
|
5
|
+
|
|
6
|
+
if MLInfrastructure.is_colab():
|
|
7
|
+
from .colab_host import ColabHost
|
|
8
|
+
|
|
9
|
+
if MLInfrastructure.is_linux():
|
|
10
|
+
from .linux_host import LinuxHost
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from google.colab import drive
|
|
3
|
+
from google.colab import files
|
|
4
|
+
from radnn.core import MLInfrastructure
|
|
5
|
+
|
|
6
|
+
COLAB_ROOT_FOLDER = "/content/gdrive/My Drive/Colab Notebooks"
|
|
7
|
+
|
|
8
|
+
class ColabHost(object):
|
|
9
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
10
|
+
def __init__(self, colab_root=COLAB_ROOT_FOLDER, workspace_folder=None):
|
|
11
|
+
self.colab_root = colab_root
|
|
12
|
+
self.workspace_prefixes = None
|
|
13
|
+
self.workspace_folder = workspace_folder
|
|
14
|
+
self.current_folder = None
|
|
15
|
+
|
|
16
|
+
drive.mount("/content/gdrive")
|
|
17
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
18
|
+
def detect_workspace(self, prefixes=None):
|
|
19
|
+
self.workspace_prefixes = prefixes
|
|
20
|
+
|
|
21
|
+
if self.workspace_prefixes is None:
|
|
22
|
+
self.workspace_folder = self.colab_root
|
|
23
|
+
else:
|
|
24
|
+
self.workspace_folder = None
|
|
25
|
+
for sProjectsFolder in os.listdir(self.colab_root):
|
|
26
|
+
for sPrefix in self.workspace_prefixes:
|
|
27
|
+
if sProjectsFolder.startswith(sPrefix):
|
|
28
|
+
self.workspace_folder = os.path.join(self.colab_root, sProjectsFolder)
|
|
29
|
+
break
|
|
30
|
+
return self
|
|
31
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
32
|
+
def change_dir(self, path):
|
|
33
|
+
self.current_folder = path
|
|
34
|
+
os.chdir(self.current_folder)
|
|
35
|
+
print("Current directory is: ", os.getcwd())
|
|
36
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
37
|
+
def change_to_project_dir(self, project_name):
|
|
38
|
+
if self.workspace_folder is not None:
|
|
39
|
+
self.current_folder = os.path.join(self.workspace_folder, project_name)
|
|
40
|
+
else:
|
|
41
|
+
self.current_folder = os.path.join(self.colab_root, project_name)
|
|
42
|
+
os.chdir(self.current_folder)
|
|
43
|
+
print("Current directory is: ", os.getcwd())
|
|
44
|
+
|
|
45
|
+
return self
|
|
46
|
+
# --------------------------------------------------------------------------------------------------------
|
|
47
|
+
def __str__(self)->str:
|
|
48
|
+
return f"Host: {MLInfrastructure.host_name()}"
|
|
49
|
+
# --------------------------------------------------------------------------------------------------------
|
|
50
|
+
def __repr__(self)->str:
|
|
51
|
+
return self.__str__()
|
|
52
|
+
# --------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import win32api
|
|
2
|
+
|
|
3
|
+
class WindowsHost(object):
|
|
4
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
5
|
+
@classmethod
|
|
6
|
+
def dll_info_root(self, dll_filename):
|
|
7
|
+
dInfo = dict()
|
|
8
|
+
ver_strings = ('Comments', 'InternalName', 'ProductName',
|
|
9
|
+
'CompanyName', 'LegalCopyright', 'ProductVersion',
|
|
10
|
+
'FileDescription', 'LegalTrademarks', 'PrivateBuild',
|
|
11
|
+
'FileVersion', 'OriginalFilename', 'SpecialBuild')
|
|
12
|
+
# fname = os.environ["comspec"]
|
|
13
|
+
dFileVersionInfo = win32api.GetFileVersionInfo(dll_filename, '\\')
|
|
14
|
+
## backslash as parm returns dictionary of numeric info corresponding to VS_FIXEDFILEINFO struc
|
|
15
|
+
|
|
16
|
+
for sKey, oValue in dFileVersionInfo.items():
|
|
17
|
+
dInfo[sKey] = oValue
|
|
18
|
+
return dInfo
|
|
19
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
20
|
+
@classmethod
|
|
21
|
+
def dll_info(cls, dll_filename):
|
|
22
|
+
dInfo = dict()
|
|
23
|
+
|
|
24
|
+
dFileVersionsInfo = win32api.GetFileVersionInfo(dll_filename, '\\VarFileInfo\\Translation')
|
|
25
|
+
## \VarFileInfo\Translation returns list of available (language, codepage) pairs that can be used to retreive string info
|
|
26
|
+
## any other must be of the form \StringfileInfo\%04X%04X\parm_name, middle two are language/codepage pair returned from above
|
|
27
|
+
dInfo = dict()
|
|
28
|
+
for sLanguageCode, sCodePage in dFileVersionsInfo:
|
|
29
|
+
dInfo["lang"] = sLanguageCode
|
|
30
|
+
dInfo["codepage"] = sCodePage
|
|
31
|
+
|
|
32
|
+
#print('lang: ', lang, 'codepage:', codepage)
|
|
33
|
+
oVersionStrings = ('Comments', 'InternalName', 'ProductName',
|
|
34
|
+
'CompanyName', 'LegalCopyright', 'ProductVersion',
|
|
35
|
+
'FileDescription', 'LegalTrademarks', 'PrivateBuild',
|
|
36
|
+
'FileVersion', 'OriginalFilename', 'SpecialBuild')
|
|
37
|
+
for sVersionString in oVersionStrings:
|
|
38
|
+
str_info = u'\\StringFileInfo\\%04X%04X\\%s' % (sLanguageCode, sCodePage, sVersionString)
|
|
39
|
+
dInfo[sVersionString] = repr(win32api.GetFileVersionInfo(dll_filename, str_info))
|
|
40
|
+
return dInfo
|
|
41
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ......................................................................................
|
|
2
|
+
# MIT License
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2024-2025 Pantelis I. Kaplanoglou
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# ......................................................................................
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
from datetime import datetime
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TeeLogger(object):
|
|
32
|
+
# --------------------------------------------------------------------------------------------------------
|
|
33
|
+
def __init__(self, file_name):
|
|
34
|
+
self.last_flush = datetime.now()
|
|
35
|
+
sMode = "w"
|
|
36
|
+
if os.path.exists(file_name):
|
|
37
|
+
sMode = "a"
|
|
38
|
+
self.file = open(file_name, sMode)
|
|
39
|
+
self.stdout = sys.stdout
|
|
40
|
+
self.isclosed = False
|
|
41
|
+
# --------------------------------------------------------------------------------------------------------
|
|
42
|
+
def write(self, message):
|
|
43
|
+
self.stdout.write(message)
|
|
44
|
+
self.file.write(message)
|
|
45
|
+
dNow = datetime.now()
|
|
46
|
+
oDelta = dNow - self.last_flush
|
|
47
|
+
if oDelta.total_seconds() >= 5:
|
|
48
|
+
self.flush()
|
|
49
|
+
self.last_flush = dNow
|
|
50
|
+
# --------------------------------------------------------------------------------------------------------
|
|
51
|
+
def flush(self):
|
|
52
|
+
if not self.isclosed:
|
|
53
|
+
self.stdout.flush()
|
|
54
|
+
self.file.flush()
|
|
55
|
+
# --------------------------------------------------------------------------------------------------------
|
|
56
|
+
def close(self):
|
|
57
|
+
self.isclosed = True
|
|
58
|
+
self.stdout.close()
|
|
59
|
+
self.file.close()
|
|
60
|
+
# --------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
radnn/__init__.py,sha256=FqalNi2naG2HW4WceCD7_Tnp8cHHUXCCY9HYsg9Buxk,140
|
|
2
|
+
radnn/core.py,sha256=cWGl_B3bHASEDdZHgDKN9CwGo7-TFjj3y2YD-Ei4bD4,2828
|
|
3
|
+
radnn/evaluation/__init__.py,sha256=7dXDyJfOpSAr7G8jfDofsW4YEHNElCTTyMXuLCtpoOI,59
|
|
4
|
+
radnn/evaluation/evaluate_classification.py,sha256=wacQ_02loI2ApOKrUIDTKqqrwi0_TpUZJQfTpJ6rtag,5497
|
|
5
|
+
radnn/experiment/__init__.py,sha256=8gxrFS4bG7rg2kgrDEhemJgDbO-5KhBYc4owJZ-S--k,247
|
|
6
|
+
radnn/experiment/ml_experiment_config.py,sha256=MuFb4sY4kgCwxtJGmpOace4eTopy7mVLg3IDCpy6evI,8547
|
|
7
|
+
radnn/experiment/ml_experiment_env.py,sha256=I54WILYjCJf9P133dFgOMsXGxkftlU8DcmwXDnBW930,11151
|
|
8
|
+
radnn/plots/__init__.py,sha256=sfhHqTDmYce5tg8jLJCN1IkELwbW3TCibm0PEYoomKY,181
|
|
9
|
+
radnn/plots/plot_confusion_matrix.py,sha256=KWW5k7-gaLPXO4oQBnzwAwgI-rvPOmqw8uiZbFlmU5o,2625
|
|
10
|
+
radnn/plots/plot_learning_curve.py,sha256=tgx3vAm4rg5qScqxLQ2T82BqrRWjiYaIKgLQp5Y19cQ,3480
|
|
11
|
+
radnn/plots/plot_roc.py,sha256=iyOnhux2qFEs7W-szEOCg2VCkcnBEaOJxZkWWYuSnCQ,3493
|
|
12
|
+
radnn/plots/plot_voronoi_2d.py,sha256=Kq3ZaLhLaFHQAurhFKNy7NNuIm3RArCk_WxPT8Ftxek,4409
|
|
13
|
+
radnn/system/__init__.py,sha256=uJLg56njcLtaRO0Kyudat055DGxqWsLMvrn9P8_Rt6A,119
|
|
14
|
+
radnn/system/filestore.py,sha256=gNESaiJNwdrFbkzcbkSv-O4V8vrBagOSR90WcFueNsA,8977
|
|
15
|
+
radnn/system/filesystem.py,sha256=jt37OP9UgdJihjKLRWpMmERt1L0EdS-uPRrdIduH_BQ,5523
|
|
16
|
+
radnn/system/tee_logger.py,sha256=WcyIJiW851PP6Jk7SyCHj63BEt7HnWa5-GJtw1CjGHE,2629
|
|
17
|
+
radnn/system/files/__init__.py,sha256=u2oGHNKM362JwqrDgwmWTAiisL067h6H9uu-d0iTBks,169
|
|
18
|
+
radnn/system/files/csvfile.py,sha256=9uKqmCdqLAKomANejcSruHle1r3gVdByQb1I2otLcMs,2246
|
|
19
|
+
radnn/system/files/fileobject.py,sha256=qT9qzpH6YFSIzaKfILxhpaQ1P2o2tqRh9J5J2L_Kp8Y,3851
|
|
20
|
+
radnn/system/files/imgfile.py,sha256=HxvvittOUG1mmIp-Bg8vHnLPtRYD-N4nE86G0Rcys6M,2242
|
|
21
|
+
radnn/system/files/jsonfile.py,sha256=CX0Y6kPEaF_eEUb8ah-5Wvp35JRVYcpvOXg-DCxRnf0,3172
|
|
22
|
+
radnn/system/files/picklefile.py,sha256=0BFiYzr6BbGWMXyQCMjYHa_GlW8nEZ95CQtn7lpH254,5526
|
|
23
|
+
radnn/system/files/textfile.py,sha256=r7FZ1pdFGYxKrQ_kgkEK35GIqRU8k_MjdB0OrDd5m0o,2891
|
|
24
|
+
radnn/system/hosts/__init__.py,sha256=k2gkMJhe96Nf-V2ex6jZqmCRX9vA_K6gFB8J8Ii9ahc,261
|
|
25
|
+
radnn/system/hosts/colab_host.py,sha256=tAuLn9gTkX9cCSEmm7sG1JmCtNIgFlASwBt2olHUSe4,2434
|
|
26
|
+
radnn/system/hosts/linux_host.py,sha256=TZ-5JEZV0WNEB5trTbRWM0REhpuK5oYxoLodKKqhm9s,57
|
|
27
|
+
radnn/system/hosts/windows_host.py,sha256=u4IvgfFoKWdrFy0hYG__AoxfEJYHF4-ISNXTCJ5uN8I,2257
|
|
28
|
+
radnn-0.0.5.dist-info/LICENSE.txt,sha256=vYtt_GDvm_yW65X9YMBOOu8Vqc9SAvqH94TbfBc2ckU,1106
|
|
29
|
+
radnn-0.0.5.dist-info/METADATA,sha256=_9T1n2CkCwcPhzFOtQcP4E8s6GkzQI4Q2TLEU2aeNF4,2861
|
|
30
|
+
radnn-0.0.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
31
|
+
radnn-0.0.5.dist-info/top_level.txt,sha256=FKlLIm6gRAeZlRzs-HCBJAB1q9ELJ7MgaL-qqFuPo6M,6
|
|
32
|
+
radnn-0.0.5.dist-info/RECORD,,
|
radnn-0.0.3.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
radnn/__init__.py,sha256=arFgYRlsbwGiNdsYNwxWkafpqvG4x9-7Xw4gwehj6Ds,109
|
|
2
|
-
radnn/core.py,sha256=1CNl5Qejop4pCUIDfHEx97PO3bQVq1WrQ-a5IIRGaCE,1665
|
|
3
|
-
radnn/experiment/__init__.py,sha256=GqESBzSW5fyoFENFOWLC4FB0xqqn0KleEiWRPZG6_Dc,75
|
|
4
|
-
radnn/experiment/ml_experiment_config.py,sha256=6REP6lT4Rj6_WXu3kuPsOSX4DkCAC8ZpR-2Ua2fQffU,6280
|
|
5
|
-
radnn/system/__init__.py,sha256=kFFqJ3SPucLmSoL4QP2TBO7IdFfjmgn3eGh6HGMFOM0,84
|
|
6
|
-
radnn/system/filestore.py,sha256=gNESaiJNwdrFbkzcbkSv-O4V8vrBagOSR90WcFueNsA,8977
|
|
7
|
-
radnn/system/filesystem.py,sha256=16m3-WDXGs5HoasbV7s7--bFSdfs9zYTg6nwTvqy22s,4642
|
|
8
|
-
radnn/system/files/__init__.py,sha256=u2oGHNKM362JwqrDgwmWTAiisL067h6H9uu-d0iTBks,169
|
|
9
|
-
radnn/system/files/csvfile.py,sha256=9uKqmCdqLAKomANejcSruHle1r3gVdByQb1I2otLcMs,2246
|
|
10
|
-
radnn/system/files/fileobject.py,sha256=qT9qzpH6YFSIzaKfILxhpaQ1P2o2tqRh9J5J2L_Kp8Y,3851
|
|
11
|
-
radnn/system/files/imgfile.py,sha256=HxvvittOUG1mmIp-Bg8vHnLPtRYD-N4nE86G0Rcys6M,2242
|
|
12
|
-
radnn/system/files/jsonfile.py,sha256=CX0Y6kPEaF_eEUb8ah-5Wvp35JRVYcpvOXg-DCxRnf0,3172
|
|
13
|
-
radnn/system/files/picklefile.py,sha256=0BFiYzr6BbGWMXyQCMjYHa_GlW8nEZ95CQtn7lpH254,5526
|
|
14
|
-
radnn/system/files/textfile.py,sha256=r7FZ1pdFGYxKrQ_kgkEK35GIqRU8k_MjdB0OrDd5m0o,2891
|
|
15
|
-
radnn-0.0.3.dist-info/LICENSE.txt,sha256=vYtt_GDvm_yW65X9YMBOOu8Vqc9SAvqH94TbfBc2ckU,1106
|
|
16
|
-
radnn-0.0.3.dist-info/METADATA,sha256=fYW8JdhjMVYxbnseMrLVO_TEP7rqc50zrXLhd--jM1M,2861
|
|
17
|
-
radnn-0.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
18
|
-
radnn-0.0.3.dist-info/top_level.txt,sha256=FKlLIm6gRAeZlRzs-HCBJAB1q9ELJ7MgaL-qqFuPo6M,6
|
|
19
|
-
radnn-0.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|