PyDLLib 1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. DLL/Data/Metrics.py +626 -0
  2. DLL/Data/Preprocessing.py +384 -0
  3. DLL/Data/_DataReader.py +55 -0
  4. DLL/Data/__init__.py +1 -0
  5. DLL/DeepLearning/Callbacks/_BackUp.py +71 -0
  6. DLL/DeepLearning/Callbacks/_BaseCallback.py +43 -0
  7. DLL/DeepLearning/Callbacks/_EarlyStopping.py +102 -0
  8. DLL/DeepLearning/Callbacks/_ReduceLROnPlateau.py +90 -0
  9. DLL/DeepLearning/Callbacks/__init__.py +4 -0
  10. DLL/DeepLearning/Initialisers/_Initialiser.py +24 -0
  11. DLL/DeepLearning/Initialisers/_Kaiming_He.py +55 -0
  12. DLL/DeepLearning/Initialisers/_Xavier_Glorot.py +39 -0
  13. DLL/DeepLearning/Initialisers/__init__.py +2 -0
  14. DLL/DeepLearning/Layers/Activations/_Activation.py +23 -0
  15. DLL/DeepLearning/Layers/Activations/_ReLU.py +51 -0
  16. DLL/DeepLearning/Layers/Activations/_Sigmoid.py +51 -0
  17. DLL/DeepLearning/Layers/Activations/_SoftMax.py +83 -0
  18. DLL/DeepLearning/Layers/Activations/_Tanh.py +47 -0
  19. DLL/DeepLearning/Layers/Activations/__init__.py +4 -0
  20. DLL/DeepLearning/Layers/Regularisation/_BaseRegularisation.py +12 -0
  21. DLL/DeepLearning/Layers/Regularisation/_BatchNormalisation.py +106 -0
  22. DLL/DeepLearning/Layers/Regularisation/_Dropout.py +79 -0
  23. DLL/DeepLearning/Layers/Regularisation/_GroupNormalisation.py +112 -0
  24. DLL/DeepLearning/Layers/Regularisation/_InstanceNormalisation.py +50 -0
  25. DLL/DeepLearning/Layers/Regularisation/_LayerNormalisation.py +37 -0
  26. DLL/DeepLearning/Layers/Regularisation/__init__.py +5 -0
  27. DLL/DeepLearning/Layers/_Add.py +109 -0
  28. DLL/DeepLearning/Layers/_Attention.py +161 -0
  29. DLL/DeepLearning/Layers/_BaseLayer.py +87 -0
  30. DLL/DeepLearning/Layers/_Bidirectional.py +113 -0
  31. DLL/DeepLearning/Layers/_Conv2D.py +133 -0
  32. DLL/DeepLearning/Layers/_Dense.py +109 -0
  33. DLL/DeepLearning/Layers/_DenseKAN.py +184 -0
  34. DLL/DeepLearning/Layers/_Flatten.py +55 -0
  35. DLL/DeepLearning/Layers/_Identity.py +50 -0
  36. DLL/DeepLearning/Layers/_Input.py +11 -0
  37. DLL/DeepLearning/Layers/_LSTM.py +261 -0
  38. DLL/DeepLearning/Layers/_LayerList.py +98 -0
  39. DLL/DeepLearning/Layers/_MaxPooling2D.py +101 -0
  40. DLL/DeepLearning/Layers/_RNN.py +185 -0
  41. DLL/DeepLearning/Layers/_Reshape.py +52 -0
  42. DLL/DeepLearning/Layers/__init__.py +14 -0
  43. DLL/DeepLearning/Losses/_BCE.py +96 -0
  44. DLL/DeepLearning/Losses/_BaseLoss.py +18 -0
  45. DLL/DeepLearning/Losses/_CCE.py +87 -0
  46. DLL/DeepLearning/Losses/_Exponential.py +101 -0
  47. DLL/DeepLearning/Losses/_Huber.py +104 -0
  48. DLL/DeepLearning/Losses/_MAE.py +85 -0
  49. DLL/DeepLearning/Losses/_MSE.py +86 -0
  50. DLL/DeepLearning/Losses/__init__.py +6 -0
  51. DLL/DeepLearning/Model.py +265 -0
  52. DLL/DeepLearning/Optimisers/_ADADELTA.py +53 -0
  53. DLL/DeepLearning/Optimisers/_ADAGRAD.py +51 -0
  54. DLL/DeepLearning/Optimisers/_ADAM.py +65 -0
  55. DLL/DeepLearning/Optimisers/_BaseOptimiser.py +20 -0
  56. DLL/DeepLearning/Optimisers/_LBFGS.py +120 -0
  57. DLL/DeepLearning/Optimisers/_RMSPROP.py +62 -0
  58. DLL/DeepLearning/Optimisers/_SGD.py +44 -0
  59. DLL/DeepLearning/Optimisers/__init__.py +6 -0
  60. DLL/DeepLearning/__init__.py +0 -0
  61. DLL/Exceptions/_Exceptions.py +19 -0
  62. DLL/Exceptions/__init__.py +2 -0
  63. DLL/MachineLearning/SupervisedLearning/Calibration/_CalibratedClassifier.py +88 -0
  64. DLL/MachineLearning/SupervisedLearning/Calibration/_IsotonicRegression.py +114 -0
  65. DLL/MachineLearning/SupervisedLearning/Calibration/__init__.py +2 -0
  66. DLL/MachineLearning/SupervisedLearning/GaussianProcesses/_GaussianProcessRegressor.py +190 -0
  67. DLL/MachineLearning/SupervisedLearning/GaussianProcesses/__init__.py +1 -0
  68. DLL/MachineLearning/SupervisedLearning/Kernels.py +851 -0
  69. DLL/MachineLearning/SupervisedLearning/LinearModels/TimeSeries.py +147 -0
  70. DLL/MachineLearning/SupervisedLearning/LinearModels/_Elasticnet.py +161 -0
  71. DLL/MachineLearning/SupervisedLearning/LinearModels/_LassoRegression.py +157 -0
  72. DLL/MachineLearning/SupervisedLearning/LinearModels/_LinearRegression.py +81 -0
  73. DLL/MachineLearning/SupervisedLearning/LinearModels/_LogisticRegression.py +182 -0
  74. DLL/MachineLearning/SupervisedLearning/LinearModels/_RANSAC.py +97 -0
  75. DLL/MachineLearning/SupervisedLearning/LinearModels/_RidgeRegression.py +78 -0
  76. DLL/MachineLearning/SupervisedLearning/LinearModels/__init__.py +6 -0
  77. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_BernoulliNaiveBayes.py +115 -0
  78. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_GaussianNaiveBayes.py +111 -0
  79. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_MultinomialNaiveBayes.py +115 -0
  80. DLL/MachineLearning/SupervisedLearning/NaiveBayes/__init__.py +3 -0
  81. DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsClassifier.py +115 -0
  82. DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsRegressor.py +90 -0
  83. DLL/MachineLearning/SupervisedLearning/Neighbors/__init__.py +2 -0
  84. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVC.py +293 -0
  85. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVR.py +100 -0
  86. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/__init__.py +2 -0
  87. DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostClassifier.py +179 -0
  88. DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostRegressor.py +141 -0
  89. DLL/MachineLearning/SupervisedLearning/Trees/_DecisionTree.py +219 -0
  90. DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingClassifier.py +207 -0
  91. DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingRegressor.py +122 -0
  92. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMClassifier.py +288 -0
  93. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMRegressor.py +168 -0
  94. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMTree.py +199 -0
  95. DLL/MachineLearning/SupervisedLearning/Trees/_RandomForest.py +96 -0
  96. DLL/MachineLearning/SupervisedLearning/Trees/_RegressionForest.py +75 -0
  97. DLL/MachineLearning/SupervisedLearning/Trees/_RegressionTree.py +148 -0
  98. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostClassifier.py +239 -0
  99. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostRegressor.py +131 -0
  100. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostTree.py +105 -0
  101. DLL/MachineLearning/SupervisedLearning/Trees/__init__.py +12 -0
  102. DLL/MachineLearning/SupervisedLearning/__init__.py +0 -0
  103. DLL/MachineLearning/UnsupervisedLearning/Clustering/_DBScan.py +87 -0
  104. DLL/MachineLearning/UnsupervisedLearning/Clustering/_GaussianMixtureModels.py +121 -0
  105. DLL/MachineLearning/UnsupervisedLearning/Clustering/_KMeans.py +120 -0
  106. DLL/MachineLearning/UnsupervisedLearning/Clustering/_SpectralClustering.py +105 -0
  107. DLL/MachineLearning/UnsupervisedLearning/Clustering/__init__.py +4 -0
  108. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_LDA.py +118 -0
  109. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_PCA.py +101 -0
  110. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_QDA.py +69 -0
  111. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_RobustPCA.py +143 -0
  112. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_TSNE.py +151 -0
  113. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_UMAP.py +176 -0
  114. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/__init__.py +6 -0
  115. DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/_IsolationForest.py +175 -0
  116. DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/__init__.py +1 -0
  117. DLL/MachineLearning/UnsupervisedLearning/__init__.py +0 -0
  118. DLL/MachineLearning/__init__.py +0 -0
  119. DLL/__init__.py +0 -0
  120. pydllib-1.0.dist-info/METADATA +104 -0
  121. pydllib-1.0.dist-info/RECORD +124 -0
  122. pydllib-1.0.dist-info/WHEEL +5 -0
  123. pydllib-1.0.dist-info/licenses/LICENSE +21 -0
  124. pydllib-1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,384 @@
1
+ import torch
2
+ from math import floor
3
+ from itertools import combinations_with_replacement
4
+
5
+ from ..Exceptions import NotCompiledError
6
+
7
+
8
+ def data_split(X, Y, train_split=0.8, validation_split=0.2):
9
+ """
10
+ Splits the data into train, validation and test sets.
11
+
12
+ Args:
13
+ X (torch.Tensor of shape (n_samples, ...)): The input values.
14
+ Y (torch.Tensor of shape (n_samples, ...)): The target values.
15
+ train_split (float, optional): The precentage of train data of the whole data. Must be a real number in range (0, 1]. Defaults to 0.8.
16
+ validation_split (float, optional): The precentage of validation data of the whole data. Must be a real number in range [0, 1). Defaults to 0.2.
17
+
18
+ Returns:
19
+ x_train, y_train, x_val, y_val, x_test, y_test (tuple[torch.Tensor]): The original data shuffled and split according to train and validation splits.
20
+
21
+ Note:
22
+ The sum of train_split and validation_split must be less than or equal to 1. The remaining samples are returned as the test data.
23
+ """
24
+ if not isinstance(train_split, float | int) or train_split <= 0 or train_split > 1:
25
+ raise ValueError("train_split must be a real number in range (0, 1].")
26
+ if not isinstance(validation_split, float | int) or validation_split < 0 or validation_split >= 1:
27
+ raise ValueError("validation_split must be a real number in range [0, 1).")
28
+ if train_split + validation_split > 1:
29
+ raise ValueError("The sum of train_split and validation_split must be less than or equal to 1.")
30
+
31
+ data_length = X.size(0)
32
+ perm = torch.randperm(data_length, requires_grad=False, device=X.device)
33
+ x_data = X.index_select(0, perm)
34
+ y_data = Y.index_select(0, perm)
35
+ split_index1 = floor(data_length * train_split)
36
+ split_index2 = floor(data_length * (train_split + validation_split))
37
+ x_train, y_train = x_data[:split_index1], y_data[:split_index1]
38
+ x_val, y_val = x_data[split_index1:split_index2], y_data[split_index1:split_index2]
39
+ x_test, y_test = x_data[split_index2:], y_data[split_index2:]
40
+ return x_train, y_train, x_val, y_val, x_test, y_test
41
+
42
+
43
+ class OneHotEncoder:
44
+ """
45
+ The one-hot encoder.
46
+ """
47
+ def fit(self, data):
48
+ """
49
+ Finds the classes in the data.
50
+
51
+ Args:
52
+ data (torch.Tensor of shape (n_samples,) or (n_samples, n_features)): the true labels of samples.
53
+ """
54
+ if not isinstance(data, torch.Tensor) or (data.ndim != 1 and data.ndim != 2):
55
+ raise ValueError("data must be a 1 or 2 dimensional torch tensor.")
56
+
57
+ if data.ndim == 1: data = data.unsqueeze(1)
58
+
59
+ # unique_elements = [torch.unique(feature) for feature in data.T]
60
+ # self.element_to_index = [{element.item(): i for i, element in enumerate(uniques)} for uniques in unique_elements]
61
+ # self.index_to_element = [{i: element for element, i in table.items()} for table in self.element_to_index]
62
+ # self.one_hot_lengths = [len(uniques) for uniques in unique_elements]
63
+ range_elements = [range(torch.min(feature).int(), torch.max(feature).int() + 1) for feature in data.T]
64
+ self.element_to_index = [{element: i for i, element in enumerate(uniques)} for uniques in range_elements]
65
+ self.index_to_element = [{i: element for element, i in table.items()} for table in self.element_to_index]
66
+ self.one_hot_lengths = [len(uniques) for uniques in range_elements]
67
+
68
+ def encode(self, data):
69
+ """
70
+ One-hot encodes the data. OneHotEncoder.fit() must be called before encoding.
71
+
72
+ Args:
73
+ data (torch.Tensor of shape (n_samples,) or (n_samples, n_features)): the true labels of samples.
74
+
75
+ Returns:
76
+ torch.Tensor of shape (n_samples, n_classes_1 + ... + n_classes_n_features): A one-hot encoded tensor.
77
+ """
78
+ if not isinstance(data, torch.Tensor) or (data.ndim != 1 and data.ndim != 2):
79
+ raise ValueError("data must be a 1 or 2 dimensional torch tensor.")
80
+ if not hasattr(self, "one_hot_lengths"):
81
+ raise NotCompiledError("OneHotEncoder.fit() must be called before encoding.")
82
+
83
+ if data.ndim == 1: data = data.unsqueeze(1)
84
+ encoded_features = []
85
+ for i in range(data.shape[1]):
86
+ label_to_distribution = torch.tensor([self._get_distribution(self.element_to_index[i][y.item()], self.one_hot_lengths[i]) for y in data[:, i]], device=data.device)
87
+ encoded_features.append(label_to_distribution)
88
+ return torch.cat(encoded_features, dim=1)
89
+
90
+ def fit_encode(self, data):
91
+ """
92
+ First fits the encoder and then one-hot encodes the data.
93
+
94
+ Args:
95
+ data (torch.Tensor of shape (n_samples,) or (n_samples, n_features)): the true labels of samples.
96
+
97
+ Returns:
98
+ torch.Tensor of shape (n_samples, n_classes_1 + ... + n_classes_n_features): A one-hot encoded tensor.
99
+ """
100
+ if not isinstance(data, torch.Tensor) or (data.ndim != 1 and data.ndim != 2):
101
+ raise ValueError("data must be a 1 or 2 dimensional torch tensor.")
102
+
103
+ self.fit(data)
104
+ return self.encode(data)
105
+
106
+ def decode(self, data):
107
+ """
108
+ One-hot encodes the data. OneHotEncoder.fit() must be called before decoding.
109
+
110
+ Args:
111
+ data (torch.Tensor of shape (n_samples, n_classes_1 + ... + n_classes_n_features)): the predictions of samples.
112
+
113
+ Returns:
114
+ torch.Tensor of shape (n_samples,) or (n_samples, n_features): A decoded predictions transformed to the original classes.
115
+ """
116
+ if not isinstance(data, torch.Tensor) or data.ndim != 2:
117
+ raise ValueError("data must be a 2 dimensional torch tensor.")
118
+ if not hasattr(self, "one_hot_lengths"):
119
+ raise NotCompiledError("OneHotEncoder.fit() must be called before decoding.")
120
+
121
+ decoded = []
122
+ i = 0
123
+ j = 0
124
+ while i < sum(self.one_hot_lengths):
125
+ feature = data[:, i:(i + self.one_hot_lengths[j])]
126
+ features_decoded = [self.index_to_element[j][torch.argmax(tensor, dim=0).item()] for tensor in feature]
127
+ decoded.append(features_decoded)
128
+ i += self.one_hot_lengths[j]
129
+ j += 1
130
+ decoded = torch.tensor(decoded, device=data.device).T
131
+ if len(self.one_hot_lengths) == 1: decoded = decoded.squeeze(1)
132
+ return decoded
133
+
134
+ def _get_distribution(self, index, size):
135
+ distribution = [0 if i != index else 1 for i in range(size)]
136
+ return distribution
137
+
138
+
139
+ class CategoricalEncoder:
140
+ """
141
+ The categorical encoder.
142
+ """
143
+ def fit(self, data):
144
+ """
145
+ Finds the classes in the data.
146
+
147
+ Args:
148
+ data (torch.Tensor of shape (n_samples,)): the true labels of samples.
149
+ """
150
+ if not isinstance(data, torch.Tensor) or data.ndim != 1:
151
+ raise ValueError("data must be a 1 dimensional torch tensor.")
152
+
153
+ self.unique_elements = torch.unique(data)
154
+ self.element_to_key = {element.item(): i for i, element in enumerate(self.unique_elements)}
155
+
156
+ def encode(self, data):
157
+ """
158
+ Encodes the data to values [0, ..., n_classes - 1]. CategoricalEncoder.fit() must be called before encoding.
159
+
160
+ Args:
161
+ data (torch.Tensor of shape (n_samples,)): the true labels of samples.
162
+
163
+ Returns:
164
+ torch.Tensor of shape (n_samples,): An encoded tensor.
165
+ """
166
+ if not isinstance(data, torch.Tensor) or data.ndim != 1:
167
+ raise ValueError("data must be a 1 dimensional torch tensor.")
168
+ if not hasattr(self, "element_to_key"):
169
+ raise NotCompiledError("CategoricalEncoder.fit() must be called before encoding.")
170
+
171
+ label_to_distribution = torch.tensor([self.element_to_key[y.item()] for y in data], device=data.device)
172
+ return label_to_distribution
173
+
174
+ def fit_encode(self, data):
175
+ """
176
+ First fits the encoder and then encodes the data.
177
+
178
+ Args:
179
+ data (torch.Tensor of shape (n_samples,)): the true labels of samples.
180
+
181
+ Returns:
182
+ torch.Tensor of shape (n_samples,): An encoded tensor.
183
+ """
184
+ if not isinstance(data, torch.Tensor) or data.ndim != 1:
185
+ raise ValueError("data must be a 1 dimensional torch tensor.")
186
+
187
+ self.fit(data)
188
+ return self.encode(data)
189
+
190
+ def decode(self, data):
191
+ """
192
+ Decodes the data to the original classes. CategoricalEncoder.fit() must be called before decoding.
193
+
194
+ Args:
195
+ data (torch.Tensor of shape (n_samples,)): the predicted labels of samples.
196
+
197
+ Returns:
198
+ torch.Tensor of shape (n_samples,): A decoded predictions transformed to the original classes.
199
+ """
200
+ if not isinstance(data, torch.Tensor) or data.ndim != 1:
201
+ raise ValueError("data must be a 1 dimensional torch tensor.")
202
+ if not hasattr(self, "element_to_key"):
203
+ raise NotCompiledError("CategoricalEncoder.fit() must be called before decoding.")
204
+
205
+ return torch.tensor([self.unique_elements[label] for label in data], device=data.device)
206
+
207
+
208
+ class MinMaxScaler:
209
+ """
210
+ The min-max scaler.
211
+ """
212
+ def fit(self, data):
213
+ """
214
+ Finds the minimum and the maximum of the data.
215
+
216
+ Args:
217
+ data (torch.Tensor): the input samples.
218
+ """
219
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
220
+ raise ValueError("data must be a torch.Tensor.")
221
+
222
+ self.min = torch.min(data, dim=0).values
223
+ self.max = torch.max(data, dim=0).values
224
+
225
+ if torch.any(self.max - self.min == torch.tensor(0)):
226
+ raise ZeroDivisionError("Some features do not change and result in division by zero.")
227
+
228
+ def transform(self, data):
229
+ """
230
+ Normalises the data between 0 and 1.
231
+
232
+ Args:
233
+ data (torch.Tensor): the input samples.
234
+
235
+ Returns:
236
+ torch.Tensor: the transformed data.
237
+ """
238
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
239
+ raise ValueError("data must be a torch.Tensor.")
240
+ if not hasattr(self, "min"):
241
+ raise NotCompiledError("MinMaxScaler.fit() must be fitted before transforming.")
242
+
243
+ return (data - self.min) / (self.max - self.min)
244
+
245
+ def fit_transform(self, data):
246
+ """
247
+ First fits the scaler and then transforms the data.
248
+
249
+ Args:
250
+ data (torch.Tensor): the input samples.
251
+
252
+ Returns:
253
+ torch.Tensor: the transformed data.
254
+ """
255
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
256
+ raise ValueError("data must be a torch.Tensor.")
257
+
258
+ self.fit(data)
259
+ return self.transform(data)
260
+
261
+ def inverse_transform(self, data):
262
+ """
263
+ Scales the data back to it's original space.
264
+
265
+ Args:
266
+ data (torch.Tensor): the input samples.
267
+
268
+ Returns:
269
+ torch.Tensor: the transformed data.
270
+ """
271
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
272
+ raise ValueError("data must be a torch.Tensor.")
273
+ if not hasattr(self, "min"):
274
+ raise NotCompiledError("MinMaxScaler.fit() must be fitted before inverse transforming.")
275
+
276
+ return data * (self.max - self.min) + self.min
277
+
278
+ class StandardScaler:
279
+ """
280
+ The standard scaler.
281
+ """
282
+ def fit(self, data):
283
+ """
284
+ Finds the mean and the variance of the data.
285
+
286
+ Args:
287
+ data (torch.Tensor): the input samples.
288
+ """
289
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
290
+ raise ValueError("data must be torch.Tensor.")
291
+
292
+ self.mean = torch.mean(data, dim=0)
293
+ self.var = torch.var(data, dim=0)
294
+
295
+ def transform(self, data):
296
+ """
297
+ Transforms the data to zero mean and one variance.
298
+
299
+ Args:
300
+ data (torch.Tensor): the input samples.
301
+
302
+ Returns:
303
+ torch.Tensor: the transformed data.
304
+ """
305
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
306
+ raise ValueError("data must be a torch.Tensor.")
307
+ if not hasattr(self, "mean"):
308
+ raise NotCompiledError("StandardScaler.fit() must be fitted before transforming.")
309
+
310
+ return (data - self.mean) / torch.sqrt(self.var)
311
+
312
+ def fit_transform(self, data):
313
+ """
314
+ First fits the scaler and then encodes the data.
315
+
316
+ Args:
317
+ data (torch.Tensor): the input samples.
318
+
319
+ Returns:
320
+ torch.Tensor: the transformed data.
321
+ """
322
+ if not isinstance(data, torch.Tensor):
323
+ raise ValueError("data must be a torch.Tensor.")
324
+
325
+ self.fit(data)
326
+ return self.transform(data)
327
+
328
+ def inverse_transform(self, data):
329
+ """
330
+ Scales the data back to it's original space.
331
+
332
+ Args:
333
+ data (torch.Tensor): the input samples.
334
+
335
+ Returns:
336
+ torch.Tensor: the transformed data.
337
+ """
338
+ if not isinstance(data, torch.Tensor) or data.ndim == 0:
339
+ raise ValueError("data must be a torch.Tensor.")
340
+ if not hasattr(self, "mean"):
341
+ raise NotCompiledError("StandardScaler.fit() must be fitted before inverse transforming.")
342
+
343
+ return data * torch.sqrt(self.var) + self.mean
344
+
345
+
346
+ class PolynomialFeatures:
347
+ """
348
+ Polynomial features.
349
+
350
+ Args:
351
+ degree (int, optional): The degree of the polynomial. Must be a positive integer. Defaults to 2.
352
+ include_bias (bool): If true, a column of ones is included. Must be a boolean. Defaults to True.
353
+ """
354
+ def __init__(self, degree=2, include_bias=True):
355
+ if not isinstance(degree, int) or degree <= 0:
356
+ raise ValueError("degree must be a positive integer.")
357
+ if not isinstance(include_bias, bool):
358
+ raise TypeError("include_bias must be a boolean.")
359
+
360
+ self.degree = degree
361
+ self.include_bias = include_bias
362
+
363
+ def transform(self, data):
364
+ """
365
+ Creates a matrix of data containing every possible combination of the given set of features.
366
+
367
+ Args:
368
+ data (torch.Tensor of shape (n_samples, n_features)): the input samples.
369
+
370
+ Returns:
371
+ torch.Tensor of shape (n_samples, sum([nCr(n_features + deg - 1, deg) for deg in range(1, degree + 1)]) + 1): A tensor of the new features.
372
+ """
373
+ if not isinstance(data, torch.Tensor) or data.ndim != 2:
374
+ raise ValueError("data must be a 2 dimensional torch tensor.")
375
+
376
+ n_samples, n_features = data.shape
377
+ features = [torch.ones(n_samples, device=data.device, dtype=data.dtype)] if self.include_bias else []
378
+
379
+ for deg in range(1, self.degree + 1):
380
+ for items in combinations_with_replacement(range(n_features), deg):
381
+ new_feature = torch.prod(torch.stack([data[:, i] for i in items]), axis=0)
382
+ features.append(new_feature)
383
+
384
+ return torch.vstack(features).T
@@ -0,0 +1,55 @@
1
+ import torch
2
+
3
+
4
+ class DataReader:
5
+ """
6
+ The data reader.
7
+
8
+ Args:
9
+ X (torch.Tensor of shape (n_samples, ...)): The input data.
10
+ Y (torch.Tensor of shape (n_samples, ...)): The target labels or values.
11
+ batch_size (int, optional): The batch size. If the batch size is larger than the number of samples, the maximum number of samples is used as the batch_size. Must be a positive integer. Defaults to 64.
12
+ shuffle (bool, optional): Determines if the input data is shuffled in the beginning. Defaults to True.
13
+ shuffle_every_epoch (bool, optional): Determines if the input data is shuffled every time all data points are used. Defaults to False.
14
+ """
15
+ def __init__(self, X, Y, batch_size=64, shuffle=True, shuffle_every_epoch=False):
16
+ if not isinstance(X, torch.Tensor):
17
+ raise TypeError("X must be a torch.Tensor.")
18
+ if not isinstance(Y, torch.Tensor):
19
+ raise TypeError("Y must be a torch.Tensor.")
20
+ if len(X) != len(Y):
21
+ raise ValueError("X and Y must have the same number of samples.")
22
+ if not isinstance(batch_size, int) or batch_size <= 0:
23
+ raise ValueError("batch_size must be positive integer.")
24
+ if not isinstance(shuffle, bool):
25
+ raise TypeError("shuffle must be a boolean.")
26
+ if not isinstance(shuffle_every_epoch, bool):
27
+ raise TypeError("shuffle_every_epoch must be a boolean.")
28
+
29
+ self.data_length = Y.size(0)
30
+ self.shuffle = shuffle
31
+ self.shuffle_every_epoch = shuffle_every_epoch
32
+ if self.shuffle:
33
+ self.perm = torch.randperm(self.data_length, device=X.device)
34
+ self.X = X.index_select(0, self.perm)
35
+ self.Y = Y.index_select(0, self.perm)
36
+ else:
37
+ self.X = X
38
+ self.Y = Y
39
+ self.batch_size = batch_size if len(Y) >= batch_size else len(Y)
40
+
41
+ def get_data(self):
42
+ """
43
+ A generator giving going through the entire dataset.
44
+
45
+ Yields:
46
+ tuple[torch.Tensor, torch.Tensor]: X_batch, y_batch
47
+ """
48
+ iteration = 0
49
+ while iteration * self.batch_size < self.data_length:
50
+ yield self.X[iteration * self.batch_size:(iteration + 1) * self.batch_size], self.Y[iteration * self.batch_size:(iteration + 1) * self.batch_size]
51
+ iteration += 1
52
+ if self.shuffle_every_epoch and self.shuffle:
53
+ self.perm = torch.randperm(self.data_length, device=self.X.device)
54
+ self.X = self.X.index_select(0, self.perm)
55
+ self.Y = self.Y.index_select(0, self.perm)
DLL/Data/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from ._DataReader import DataReader
@@ -0,0 +1,71 @@
1
+ from . import Callback
2
+ from ...Exceptions import NotCompiledError
3
+ from ..Model import save_model
4
+
5
+
6
+ class BackUp(Callback):
7
+ """
8
+ Saves the fitted model to the spesified filepath.
9
+
10
+ Args:
11
+ filepath (str | , optional): The filepath where the model is saved. Defaults to "./model.pkl".
12
+ frequency (int, optional): The frequency of saving the model. Must be a positive integer. Defaults to 1.
13
+ on_batch (bool, optional): Determines if a model is saved every frequency batch or epoch. Defaults to False.
14
+ verbose (bool, optional): Determines if information about the callbacks is printed. Must be a boolean. Defaults to False.
15
+ """
16
+ def __init__(self, filepath="./model.pkl", frequency=1, on_batch=False, verbose=False):
17
+ if not isinstance(filepath, str):
18
+ raise TypeError("filepath must be a string.")
19
+ if not isinstance(frequency, int) or frequency <= 0:
20
+ raise ValueError("frequency must be a positive integer.")
21
+ if not isinstance(on_batch, bool):
22
+ raise TypeError("on_batch must be boolean.")
23
+ if not isinstance(verbose, bool):
24
+ raise TypeError("verbose must be a boolean.")
25
+
26
+ self.filepath = filepath
27
+ self.frequency = frequency
28
+ self.on_batch = on_batch
29
+ self.counter = 0
30
+ self.verbose = verbose
31
+
32
+ def on_epoch_end(self, epoch, metrics):
33
+ """
34
+ Calculates if the model should be backed up on this epoch.
35
+
36
+ Args:
37
+ epoch (int): The current epoch.
38
+ metrics (dict[str, float]): The values of the chosen metrics of the last epoch.
39
+
40
+ Raises:
41
+ NotCompiledError: callback.set_model must be called before the training starts
42
+ """
43
+ if not hasattr(self, "model"):
44
+ raise NotCompiledError("callback.set_model must be called before the training starts.")
45
+
46
+ if not self.on_batch:
47
+ self.counter += 1
48
+ if self.counter % self.frequency == 0:
49
+ save_model(self.model, filepath=self.filepath)
50
+ if self.verbose: print(f"Model backed up at epoch {epoch + 1} at {self.filepath}")
51
+ else:
52
+ self.counter = 0
53
+
54
+ def on_batch_end(self, epoch):
55
+ """
56
+ Calculates if the model should be backed up on this batch.
57
+
58
+ Args:
59
+ epoch (int): The current epoch.
60
+
61
+ Raises:
62
+ NotCompiledError: callback.set_model must be called before the training starts
63
+ """
64
+ if not hasattr(self, "model"):
65
+ raise NotCompiledError("callback.set_model must be called before the training starts.")
66
+
67
+ if self.on_batch:
68
+ self.counter += 1
69
+ if self.counter % self.frequency == 0:
70
+ save_model(self.model, filepath=self.filepath)
71
+ if self.verbose: print(f"Model backed up at epoch {epoch + 1} at {self.filepath}")
@@ -0,0 +1,43 @@
1
+ from abc import ABC
2
+
3
+
4
+ class Callback(ABC):
5
+ def set_model(self, model):
6
+ """
7
+ Lets the callback know about the chosen model. Is automatically called when using Model.fit()
8
+
9
+ Args:
10
+ Model (:ref:`models_section_label`): The chosen model.
11
+ """
12
+ self.model = model
13
+
14
+ def on_train_start(self):
15
+ """
16
+ A method, which is automatically called before the training starts.
17
+ """
18
+ pass
19
+
20
+ def on_train_end(self):
21
+ """
22
+ A method, which is automatically called after training.
23
+ """
24
+ pass
25
+
26
+ def on_epoch_end(self, epoch, metrics):
27
+ """
28
+ A method, which is automatically called after every epoch.
29
+
30
+ Args:
31
+ epoch (int): The current epoch.
32
+ metrics (dict[str, float]): The values of the chosen metrics of the last epoch.
33
+ """
34
+ pass
35
+
36
+ def on_batch_end(self, epoch):
37
+ """
38
+ A method, which is automatically called after every batch.
39
+
40
+ Args:
41
+ epoch (int): The current epoch.
42
+ """
43
+ pass
@@ -0,0 +1,102 @@
1
+ from . import Callback
2
+ from ...Exceptions import NotCompiledError
3
+
4
+
5
+ class EarlyStopping(Callback):
6
+ """
7
+ The early stopping callback. Stops the training of the model early if no improvement is made.
8
+
9
+ Args:
10
+ monitor (str, optional): The quantity, which is monitored for the early stop. Must be in the metrics of the model. Defaults to "val_loss".
11
+ patience (int, optional): The number of epochs the model waits without improvement. Must be a positive integer. Defaults to 1.
12
+ mode (str, optional): Determines if the monitored metric better if it is maximized or minimized. Must be in ["min", "max"]. Defaults to "min".
13
+ restore_best_model (bool, optional): Determines if the best model is restored after the training is finished. Defaults to False.
14
+ warmup_length (int, optional): Determines how many epochs are trained no matter what. Must be non-negative. Defaults to 0.
15
+ verbose (bool, optional): Determines if information about the callbacks is printed. Must be a boolean. Defaults to False.
16
+ Note:
17
+ Using restore_best_model = True considerably slows down training as the best model must be saved every epoch the model improves.
18
+ """
19
+ def __init__(self, monitor="val_loss", patience=1, mode="min", restore_best_model=False, warmup_length=0, verbose=False):
20
+ if not isinstance(patience, int) or patience <= 0:
21
+ raise ValueError("patience must be a positive integer.")
22
+ if mode not in ["min", "max"]:
23
+ raise ValueError('mode must be in ["min", "max"].')
24
+ if not isinstance(restore_best_model, bool):
25
+ raise TypeError("restore_best_model must be a boolean.")
26
+ if not isinstance(warmup_length, int) or warmup_length < 0:
27
+ raise ValueError("warmup_length must be non-negative.")
28
+ if not isinstance(verbose, bool):
29
+ raise TypeError("verbose must be a boolean.")
30
+
31
+ self.monitor = monitor
32
+ self.patience = patience
33
+ self.mode = 1 if mode == "min" else -1
34
+ self.restore_best_model = restore_best_model
35
+ self.warmup_length = warmup_length
36
+ self.verbose = verbose
37
+
38
+ def set_model(self, model):
39
+ """
40
+ Lets the callback know about the chosen model. Is automatically called when using Model.fit()
41
+
42
+ Args:
43
+ Model (:ref:`models_section_label`): The chosen model.
44
+ """
45
+ if not hasattr(model, "metrics") or self.monitor not in model.metrics:
46
+ raise ValueError("monitor must be in the metrics of the model.")
47
+
48
+ self.model = model
49
+
50
+ def on_train_start(self):
51
+ """
52
+ Sets the needed attributes.
53
+
54
+ Raises:
55
+ NotCompiledError: callback.set_model must be called before the training starts
56
+ """
57
+ if not hasattr(self, "model"):
58
+ raise NotCompiledError("callback.set_model must be called before the training starts.")
59
+
60
+ if self.restore_best_model:
61
+ self.stored_model = self.model.clone()
62
+ self.wait = 0
63
+ self.best_value = float("inf")
64
+
65
+ def on_train_end(self):
66
+ """
67
+ Resets the model to the stored model if restore_best_model was chosen.
68
+
69
+ Raises:
70
+ NotCompiledError: callback.set_model must be called before the training starts
71
+ """
72
+ if not hasattr(self, "model"):
73
+ raise NotCompiledError("callback.set_model must be called before the training starts.")
74
+
75
+ if self.restore_best_model:
76
+ self.model.layers = self.stored_model.layers
77
+
78
+ def on_epoch_end(self, epoch, metrics):
79
+ """
80
+ Calculates if the model has improved on the last epoch.
81
+
82
+ Args:
83
+ epoch (int): The current epoch.
84
+ metrics (dict[str, float]): The values of the chosen metrics of the last epoch.
85
+
86
+ Raises:
87
+ NotCompiledError: callback.set_model must be called before the training starts
88
+ """
89
+ if not hasattr(self, "model"):
90
+ raise NotCompiledError("callback.set_model must be called before the training starts.")
91
+
92
+ value = metrics[self.monitor]
93
+ if self.mode * value < self.best_value:
94
+ self.wait = 0
95
+ self.best_value = self.mode * value
96
+ if self.restore_best_model:
97
+ self.stored_model = self.model.clone()
98
+ elif epoch > self.warmup_length:
99
+ self.wait += 1
100
+ if self.wait >= self.patience:
101
+ if self.verbose: print(f"Stopped training early at epoch {epoch + 1} after {self.patience} epochs of no improvement. The monitored metric is currently {value:.3f} and the best model has the value of {self.mode * self.best_value:.3f}.")
102
+ self.model.stop_training = True