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.
- DLL/Data/Metrics.py +626 -0
- DLL/Data/Preprocessing.py +384 -0
- DLL/Data/_DataReader.py +55 -0
- DLL/Data/__init__.py +1 -0
- DLL/DeepLearning/Callbacks/_BackUp.py +71 -0
- DLL/DeepLearning/Callbacks/_BaseCallback.py +43 -0
- DLL/DeepLearning/Callbacks/_EarlyStopping.py +102 -0
- DLL/DeepLearning/Callbacks/_ReduceLROnPlateau.py +90 -0
- DLL/DeepLearning/Callbacks/__init__.py +4 -0
- DLL/DeepLearning/Initialisers/_Initialiser.py +24 -0
- DLL/DeepLearning/Initialisers/_Kaiming_He.py +55 -0
- DLL/DeepLearning/Initialisers/_Xavier_Glorot.py +39 -0
- DLL/DeepLearning/Initialisers/__init__.py +2 -0
- DLL/DeepLearning/Layers/Activations/_Activation.py +23 -0
- DLL/DeepLearning/Layers/Activations/_ReLU.py +51 -0
- DLL/DeepLearning/Layers/Activations/_Sigmoid.py +51 -0
- DLL/DeepLearning/Layers/Activations/_SoftMax.py +83 -0
- DLL/DeepLearning/Layers/Activations/_Tanh.py +47 -0
- DLL/DeepLearning/Layers/Activations/__init__.py +4 -0
- DLL/DeepLearning/Layers/Regularisation/_BaseRegularisation.py +12 -0
- DLL/DeepLearning/Layers/Regularisation/_BatchNormalisation.py +106 -0
- DLL/DeepLearning/Layers/Regularisation/_Dropout.py +79 -0
- DLL/DeepLearning/Layers/Regularisation/_GroupNormalisation.py +112 -0
- DLL/DeepLearning/Layers/Regularisation/_InstanceNormalisation.py +50 -0
- DLL/DeepLearning/Layers/Regularisation/_LayerNormalisation.py +37 -0
- DLL/DeepLearning/Layers/Regularisation/__init__.py +5 -0
- DLL/DeepLearning/Layers/_Add.py +109 -0
- DLL/DeepLearning/Layers/_Attention.py +161 -0
- DLL/DeepLearning/Layers/_BaseLayer.py +87 -0
- DLL/DeepLearning/Layers/_Bidirectional.py +113 -0
- DLL/DeepLearning/Layers/_Conv2D.py +133 -0
- DLL/DeepLearning/Layers/_Dense.py +109 -0
- DLL/DeepLearning/Layers/_DenseKAN.py +184 -0
- DLL/DeepLearning/Layers/_Flatten.py +55 -0
- DLL/DeepLearning/Layers/_Identity.py +50 -0
- DLL/DeepLearning/Layers/_Input.py +11 -0
- DLL/DeepLearning/Layers/_LSTM.py +261 -0
- DLL/DeepLearning/Layers/_LayerList.py +98 -0
- DLL/DeepLearning/Layers/_MaxPooling2D.py +101 -0
- DLL/DeepLearning/Layers/_RNN.py +185 -0
- DLL/DeepLearning/Layers/_Reshape.py +52 -0
- DLL/DeepLearning/Layers/__init__.py +14 -0
- DLL/DeepLearning/Losses/_BCE.py +96 -0
- DLL/DeepLearning/Losses/_BaseLoss.py +18 -0
- DLL/DeepLearning/Losses/_CCE.py +87 -0
- DLL/DeepLearning/Losses/_Exponential.py +101 -0
- DLL/DeepLearning/Losses/_Huber.py +104 -0
- DLL/DeepLearning/Losses/_MAE.py +85 -0
- DLL/DeepLearning/Losses/_MSE.py +86 -0
- DLL/DeepLearning/Losses/__init__.py +6 -0
- DLL/DeepLearning/Model.py +265 -0
- DLL/DeepLearning/Optimisers/_ADADELTA.py +53 -0
- DLL/DeepLearning/Optimisers/_ADAGRAD.py +51 -0
- DLL/DeepLearning/Optimisers/_ADAM.py +65 -0
- DLL/DeepLearning/Optimisers/_BaseOptimiser.py +20 -0
- DLL/DeepLearning/Optimisers/_LBFGS.py +120 -0
- DLL/DeepLearning/Optimisers/_RMSPROP.py +62 -0
- DLL/DeepLearning/Optimisers/_SGD.py +44 -0
- DLL/DeepLearning/Optimisers/__init__.py +6 -0
- DLL/DeepLearning/__init__.py +0 -0
- DLL/Exceptions/_Exceptions.py +19 -0
- DLL/Exceptions/__init__.py +2 -0
- DLL/MachineLearning/SupervisedLearning/Calibration/_CalibratedClassifier.py +88 -0
- DLL/MachineLearning/SupervisedLearning/Calibration/_IsotonicRegression.py +114 -0
- DLL/MachineLearning/SupervisedLearning/Calibration/__init__.py +2 -0
- DLL/MachineLearning/SupervisedLearning/GaussianProcesses/_GaussianProcessRegressor.py +190 -0
- DLL/MachineLearning/SupervisedLearning/GaussianProcesses/__init__.py +1 -0
- DLL/MachineLearning/SupervisedLearning/Kernels.py +851 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/TimeSeries.py +147 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_Elasticnet.py +161 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_LassoRegression.py +157 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_LinearRegression.py +81 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_LogisticRegression.py +182 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_RANSAC.py +97 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/_RidgeRegression.py +78 -0
- DLL/MachineLearning/SupervisedLearning/LinearModels/__init__.py +6 -0
- DLL/MachineLearning/SupervisedLearning/NaiveBayes/_BernoulliNaiveBayes.py +115 -0
- DLL/MachineLearning/SupervisedLearning/NaiveBayes/_GaussianNaiveBayes.py +111 -0
- DLL/MachineLearning/SupervisedLearning/NaiveBayes/_MultinomialNaiveBayes.py +115 -0
- DLL/MachineLearning/SupervisedLearning/NaiveBayes/__init__.py +3 -0
- DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsClassifier.py +115 -0
- DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsRegressor.py +90 -0
- DLL/MachineLearning/SupervisedLearning/Neighbors/__init__.py +2 -0
- DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVC.py +293 -0
- DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVR.py +100 -0
- DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/__init__.py +2 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostClassifier.py +179 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostRegressor.py +141 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_DecisionTree.py +219 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingClassifier.py +207 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingRegressor.py +122 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_LGBMClassifier.py +288 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_LGBMRegressor.py +168 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_LGBMTree.py +199 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_RandomForest.py +96 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_RegressionForest.py +75 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_RegressionTree.py +148 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostClassifier.py +239 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostRegressor.py +131 -0
- DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostTree.py +105 -0
- DLL/MachineLearning/SupervisedLearning/Trees/__init__.py +12 -0
- DLL/MachineLearning/SupervisedLearning/__init__.py +0 -0
- DLL/MachineLearning/UnsupervisedLearning/Clustering/_DBScan.py +87 -0
- DLL/MachineLearning/UnsupervisedLearning/Clustering/_GaussianMixtureModels.py +121 -0
- DLL/MachineLearning/UnsupervisedLearning/Clustering/_KMeans.py +120 -0
- DLL/MachineLearning/UnsupervisedLearning/Clustering/_SpectralClustering.py +105 -0
- DLL/MachineLearning/UnsupervisedLearning/Clustering/__init__.py +4 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_LDA.py +118 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_PCA.py +101 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_QDA.py +69 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_RobustPCA.py +143 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_TSNE.py +151 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_UMAP.py +176 -0
- DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/__init__.py +6 -0
- DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/_IsolationForest.py +175 -0
- DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/__init__.py +1 -0
- DLL/MachineLearning/UnsupervisedLearning/__init__.py +0 -0
- DLL/MachineLearning/__init__.py +0 -0
- DLL/__init__.py +0 -0
- pydllib-1.0.dist-info/METADATA +104 -0
- pydllib-1.0.dist-info/RECORD +124 -0
- pydllib-1.0.dist-info/WHEEL +5 -0
- pydllib-1.0.dist-info/licenses/LICENSE +21 -0
- pydllib-1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from warnings import warn
|
|
2
|
+
|
|
3
|
+
from . import Callback
|
|
4
|
+
from ...Exceptions import NotCompiledError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ReduceLROnPlateau(Callback):
|
|
8
|
+
"""
|
|
9
|
+
The reduce learning rate on plateau callback. Reduces the learning rate of the model optimiser if no improvement is made.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
monitor (str, optional): The quantity, which is monitored. Must be in the metrics of the model. Defaults to "val_loss".
|
|
13
|
+
patience (int, optional): The number of epochs the model waits without improvement. Must be a positive integer. Defaults to 0.
|
|
14
|
+
mode (str, optional): Determines if the monitored metric better if it is maximized or minimized. Must be in ["min", "max"]. Defaults to "min".
|
|
15
|
+
warmup_length (int, optional): Determines how many epochs are trained on the original learning rate. Must be non-negative. Defaults to 0.
|
|
16
|
+
factor (float, optional): The learning rate is multiplied by this factor when training. Must be in range (0, 1). Defaults to 0.5.
|
|
17
|
+
verbose (bool, optional): Determines if information about the callbacks is printed. Must be a boolean. Defaults to False.
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, monitor="val_loss", patience=0, mode="min", warmup_length=0, factor=0.5, 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(warmup_length, int) or warmup_length < 0:
|
|
25
|
+
raise ValueError("warmup_length must be non-negative.")
|
|
26
|
+
if not isinstance(factor, float) or factor <= 0 or factor >= 1:
|
|
27
|
+
raise ValueError("factor must be in range (0, 1).")
|
|
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.warmup_length = warmup_length
|
|
35
|
+
self.factor = factor
|
|
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
|
+
self.model = model
|
|
48
|
+
|
|
49
|
+
def on_train_start(self):
|
|
50
|
+
"""
|
|
51
|
+
Initializes the needed attributes.
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
NotCompiledError: callback.set_model must be called before the training starts
|
|
55
|
+
"""
|
|
56
|
+
if not hasattr(self, "model"):
|
|
57
|
+
raise NotCompiledError("callback.set_model must be called before the training starts.")
|
|
58
|
+
if not hasattr(self.model.optimiser, "learning_rate"):
|
|
59
|
+
warn("The chosen optimizer has no learning_rate attribute. The ReduceLROnPlateau callback has no effect.")
|
|
60
|
+
|
|
61
|
+
self.wait = 0
|
|
62
|
+
self.best_value = float("inf")
|
|
63
|
+
|
|
64
|
+
def on_epoch_end(self, epoch, metrics):
|
|
65
|
+
"""
|
|
66
|
+
Calculates if the learning rate should be removed.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
epoch (int): The current epoch.
|
|
70
|
+
metrics (dict[str, float]): The values of the chosen metrics of the last epoch.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
NotCompiledError: callback.set_model must be called before the training starts
|
|
74
|
+
"""
|
|
75
|
+
if not hasattr(self, "model"):
|
|
76
|
+
raise NotCompiledError("callback.set_model must be called before the training starts.")
|
|
77
|
+
|
|
78
|
+
value = metrics[self.monitor]
|
|
79
|
+
if self.mode * value < self.best_value:
|
|
80
|
+
self.wait = 0
|
|
81
|
+
self.best_value = self.mode * value
|
|
82
|
+
elif epoch > self.warmup_length:
|
|
83
|
+
self.wait += 1
|
|
84
|
+
if self.wait >= self.patience:
|
|
85
|
+
new_lr_message = None
|
|
86
|
+
if hasattr(self.model.optimiser, "learning_rate"):
|
|
87
|
+
self.wait = 0
|
|
88
|
+
self.model.optimiser.learning_rate *= self.factor
|
|
89
|
+
new_lr_message = self.model.optimiser.learning_rate
|
|
90
|
+
if self.verbose: print(f"Learning rate reduced 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}. The current learning rate is {new_lr_message}.")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Initialiser(ABC):
|
|
5
|
+
@abstractmethod
|
|
6
|
+
def initialise(self, shape):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
def _get_dims(self, shape):
|
|
10
|
+
shape = tuple(shape)
|
|
11
|
+
if len(shape) < 1:
|
|
12
|
+
input_dim = output_dim = 1
|
|
13
|
+
elif len(shape) == 1:
|
|
14
|
+
input_dim = output_dim = shape[0]
|
|
15
|
+
elif len(shape) == 2:
|
|
16
|
+
input_dim = shape[0]
|
|
17
|
+
output_dim = shape[0]
|
|
18
|
+
else: # convolutional layers (output_depth, input_depth, ...)
|
|
19
|
+
product = 1
|
|
20
|
+
for dim in shape[2:]:
|
|
21
|
+
product *= dim
|
|
22
|
+
input_dim = product * shape[1]
|
|
23
|
+
output_dim = product * shape[0]
|
|
24
|
+
return int(input_dim), int(output_dim)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from math import sqrt
|
|
3
|
+
|
|
4
|
+
from ._Initialiser import Initialiser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Kaiming_Uniform(Initialiser):
|
|
8
|
+
"""
|
|
9
|
+
The Kaiming He uniform initialiser. Kaiming He initialiser should be used for the ReLU or any other activation, which is nonlinear close to origin.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
mode (str, optional): Determines if the variance is constant in the forward or backward propagation. If "input_dim", variance is constant in forward propagation, while if "output_dim", the variance is constant in back propagation. Defaults to "input_dim".
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, mode="input_dim"):
|
|
15
|
+
if mode not in ["input_dim", "output_dim"]:
|
|
16
|
+
raise ValueError('mode must be one of "input_dim" or "output_dim".')
|
|
17
|
+
self.mode = mode
|
|
18
|
+
|
|
19
|
+
def initialise(self, shape, data_type=torch.float32, device=torch.device("cpu")):
|
|
20
|
+
"""
|
|
21
|
+
Initialises a tensor of the wanted shape with values in :math:`U(-a, a)`, where :math:`a = \\sqrt{\\frac{6}{d}}`.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
shape (torch.Size): The shape of the wanted tensor.
|
|
25
|
+
data_type (torch.dtype, optional): The data type used in the returned tensor. Defaults to torch.float32.
|
|
26
|
+
device (torch.device, optional): The device of the tensor. Determines if the computation is made using the gpu or the cpu. Defaults to torch.device("cpu").
|
|
27
|
+
"""
|
|
28
|
+
input_dim, output_dim = self._get_dims(shape)
|
|
29
|
+
a = sqrt(6/(input_dim if self.mode == "input_dim" else output_dim))
|
|
30
|
+
return 2 * a * torch.rand(size=shape, dtype=data_type, device=device) - a
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Kaiming_Normal(Initialiser):
|
|
34
|
+
"""
|
|
35
|
+
The Kaiming He normal initialiser. Kaiming He initialiser should be used for the ReLU or any other activation, which is nonlinear close to origin.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
mode (str, optional): Determines if the variance is constant in the forward or backward propagation. If "input_dim", variance is constant in forward propagation, while if "output_dim", the variance is constant in back propagation. Defaults to "input_dim".
|
|
39
|
+
"""
|
|
40
|
+
def __init__(self, mode="input_dim"):
|
|
41
|
+
if mode not in ["input_dim", "output_dim"]:
|
|
42
|
+
raise ValueError('mode must be one of "input_dim" or "output_dim".')
|
|
43
|
+
self.mode = mode
|
|
44
|
+
|
|
45
|
+
def initialise(self, shape, data_type=torch.float32, device=torch.device("cpu")):
|
|
46
|
+
"""
|
|
47
|
+
Initialises a tensor of the wanted shape with values in :math:`N(0, \\sigma^2)`, where :math:`\\sigma = \\sqrt{\\frac{2}{d_{\\text{input}} + d_{\\text{output}}}}`.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
shape (torch.Size): The shape of the wanted tensor.
|
|
51
|
+
data_type (torch.dtype, optional): The data type used in the returned tensor. Defaults to torch.float32.
|
|
52
|
+
device (torch.device, optional): The device of the tensor. Determines if the computation is made using the gpu or the cpu. Defaults to torch.device("cpu").
|
|
53
|
+
"""
|
|
54
|
+
input_dim, output_dim = self._get_dims(shape)
|
|
55
|
+
return torch.normal(mean=0, std=sqrt(2/(input_dim if self.mode == "input_dim" else output_dim)), size=shape, dtype=data_type, device=device)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from math import sqrt
|
|
3
|
+
|
|
4
|
+
from ._Initialiser import Initialiser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Xavier_Uniform(Initialiser):
|
|
8
|
+
"""
|
|
9
|
+
The Xavier Glorot uniform initialiser. Xavier Glorot initialiser should be used for tanh, sigmoid, softmax or other activations, which are approximately linear close to origin.
|
|
10
|
+
"""
|
|
11
|
+
def initialise(self, shape, data_type=torch.float32, device=torch.device("cpu")):
|
|
12
|
+
"""
|
|
13
|
+
Initialises a tensor of the wanted shape with values in :math:`U(-a, a)`, where :math:`a = \\sqrt{\\frac{6}{d_{\\text{input}} + d_{\\text{output}}}}`.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
shape (torch.Size): The shape of the wanted tensor.
|
|
17
|
+
data_type (torch.dtype, optional): The data type used in the returned tensor. Defaults to torch.float32.
|
|
18
|
+
device (torch.device, optional): The device of the tensor. Determines if the computation is made using the gpu or the cpu. Defaults to torch.device("cpu").
|
|
19
|
+
"""
|
|
20
|
+
input_dim, output_dim = self._get_dims(shape)
|
|
21
|
+
a = sqrt(6/(input_dim + output_dim))
|
|
22
|
+
return 2 * a * torch.rand(size=shape, dtype=data_type, device=device) - a
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Xavier_Normal(Initialiser):
|
|
26
|
+
"""
|
|
27
|
+
The Xavier Glorot normal initialiser. Xavier Glorot initialiser should be used for tanh, sigmoid, softmax or other activations, which are approximately linear close to origin.
|
|
28
|
+
"""
|
|
29
|
+
def initialise(self, shape, data_type=torch.float32, device=torch.device("cpu")):
|
|
30
|
+
"""
|
|
31
|
+
Initialises a tensor of the wanted shape with values in :math:`N(0, \\sigma^2)`, where :math:`\\sigma = \\sqrt{\\frac{2}{d_{\\text{input}} + d_{\\text{output}}}}`.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
shape (torch.Size): The shape of the wanted tensor.
|
|
35
|
+
data_type (torch.dtype, optional): The data type used in the returned tensor. Defaults to torch.float32.
|
|
36
|
+
device (torch.device, optional): The device of the tensor. Determines if the computation is made using the gpu or the cpu. Defaults to torch.device("cpu").
|
|
37
|
+
"""
|
|
38
|
+
input_dim, output_dim = self._get_dims(shape)
|
|
39
|
+
return torch.normal(mean=0, std=sqrt(2/(input_dim + output_dim)), size=shape, dtype=data_type, device=device)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .._BaseLayer import BaseLayer
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Activation(BaseLayer):
|
|
5
|
+
def __init__(self, **kwargs):
|
|
6
|
+
super().__init__(None, None, **kwargs)
|
|
7
|
+
|
|
8
|
+
if self.output_shape is not None:
|
|
9
|
+
raise ValueError("The output_shape should be None for activation layers.")
|
|
10
|
+
if self.activation is not None:
|
|
11
|
+
raise ValueError("Activation layer must not have an activation function.")
|
|
12
|
+
if self.normalisation is not None:
|
|
13
|
+
raise ValueError("Activation layer must not have a normalisation layer.")
|
|
14
|
+
|
|
15
|
+
self.name = "Activation"
|
|
16
|
+
|
|
17
|
+
def initialise_layer(self, input_shape, data_type, device):
|
|
18
|
+
self.output_shape = input_shape
|
|
19
|
+
super().initialise_layer(input_shape, data_type, device)
|
|
20
|
+
|
|
21
|
+
def summary(self, offset=""):
|
|
22
|
+
output_shape = self.output_shape[0] if len(self.output_shape) == 1 else self.output_shape
|
|
23
|
+
return offset + f"{self.name} - Output: ({output_shape})"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from ._Activation import Activation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ReLU(Activation):
|
|
7
|
+
"""
|
|
8
|
+
The basic rectified linear unit activation function.
|
|
9
|
+
"""
|
|
10
|
+
def __init__(self, **kwargs):
|
|
11
|
+
super().__init__(**kwargs)
|
|
12
|
+
self.name = "ReLU"
|
|
13
|
+
|
|
14
|
+
def forward(self, input, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
Calculates the following function for every element of the input matrix:
|
|
17
|
+
|
|
18
|
+
.. math::
|
|
19
|
+
|
|
20
|
+
\\text{ReLU}(x) = \\text{max}(0, x).
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
input (torch.Tensor of shape (batch_size, ...)): The input to the layer. Must be a torch.Tensor of any shape.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
torch.Tensor: The output tensor after applying the activation function of the same shape as the input.
|
|
27
|
+
"""
|
|
28
|
+
if not isinstance(input, torch.Tensor):
|
|
29
|
+
raise TypeError("input must be a torch.Tensor.")
|
|
30
|
+
|
|
31
|
+
self.input = input
|
|
32
|
+
output = torch.maximum(self.input, torch.zeros_like(self.input, device=self.input.device, dtype=self.input.dtype))
|
|
33
|
+
return output
|
|
34
|
+
|
|
35
|
+
def backward(self, dCdy, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
Calculates the gradient of the loss function with respect to the input of the layer.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
dCdy (torch.Tensor of the same shape as returned from the forward method): The gradient given by the next layer.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
torch.Tensor of shape (batch_size, ...): The new gradient after backpropagation through the layer.
|
|
44
|
+
"""
|
|
45
|
+
if not isinstance(dCdy, torch.Tensor):
|
|
46
|
+
raise TypeError("dCdy must be a torch.Tensor.")
|
|
47
|
+
if dCdy.shape != self.input.shape:
|
|
48
|
+
raise ValueError(f"dCdy is not the same shape as the spesified output_shape ({dCdy.shape[1:], self.output_shape}).")
|
|
49
|
+
|
|
50
|
+
dCdx = dCdy * (self.input > 0)
|
|
51
|
+
return dCdx
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from ._Activation import Activation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Sigmoid(Activation):
|
|
7
|
+
"""
|
|
8
|
+
The sigmoid activation function.
|
|
9
|
+
"""
|
|
10
|
+
def __init__(self, **kwargs):
|
|
11
|
+
super().__init__(**kwargs)
|
|
12
|
+
self.name = "Sigmoid"
|
|
13
|
+
|
|
14
|
+
def forward(self, input, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
Calculates the following function for every element of the input matrix:
|
|
17
|
+
|
|
18
|
+
.. math::
|
|
19
|
+
|
|
20
|
+
\\sigma(x) = \\frac{1}{1 + e^{-x}}.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
input (torch.Tensor of shape (batch_size, ...)): The input to the layer. Must be a torch.Tensor of any shape.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
torch.Tensor: The output tensor after applying the activation function of the same shape as the input.
|
|
27
|
+
"""
|
|
28
|
+
if not isinstance(input, torch.Tensor):
|
|
29
|
+
raise TypeError("input must be a torch.Tensor.")
|
|
30
|
+
|
|
31
|
+
self.input = input
|
|
32
|
+
self.output = 1 / (1 + torch.exp(-self.input))
|
|
33
|
+
return self.output
|
|
34
|
+
|
|
35
|
+
def backward(self, dCdy, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
Calculates the gradient of the loss function with respect to the input of the layer.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
dCdy (torch.Tensor of the same shape as returned from the forward method): The gradient given by the next layer.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
torch.Tensor of shape (batch_size, ...): The new gradient after backpropagation through the layer.
|
|
44
|
+
"""
|
|
45
|
+
if not isinstance(dCdy, torch.Tensor):
|
|
46
|
+
raise TypeError("dCdy must be a torch.Tensor.")
|
|
47
|
+
if dCdy.shape != self.output.shape:
|
|
48
|
+
raise ValueError(f"dCdy is not the same shape as the spesified output_shape ({dCdy.shape[1:], self.output_shape}).")
|
|
49
|
+
|
|
50
|
+
dCdx = (self.output * (1 - self.output)) * dCdy
|
|
51
|
+
return dCdx
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from ._Activation import Activation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SoftMax(Activation):
|
|
7
|
+
"""
|
|
8
|
+
The softmax activation function.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
dim (int): The dimension on which the softmax is calculated. If the data is (n_samples, n_channels, n_features) and one wants to calculate the softmax on the channels, one should select dim=1 or dim=-2.
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self, dim=-1, **kwargs):
|
|
14
|
+
super().__init__(**kwargs)
|
|
15
|
+
self.dim = dim
|
|
16
|
+
self.name = "Softmax"
|
|
17
|
+
|
|
18
|
+
def forward(self, input, **kwargs):
|
|
19
|
+
"""
|
|
20
|
+
Calculates the following function for every element of the input matrix:
|
|
21
|
+
|
|
22
|
+
.. math::
|
|
23
|
+
|
|
24
|
+
\\text{Softmax}(x)_i = \\frac{e^{x_i}}{\\sum_{j=1}^{K} e^{x_j}},
|
|
25
|
+
|
|
26
|
+
where :math:`K` is the number of features of the input.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
input (torch.Tensor of shape (n_samples, n_features)): The input to the layer. Must be a torch.Tensor of the spesified shape.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
torch.Tensor: The output tensor after applying the activation function of the same shape as the input.
|
|
33
|
+
"""
|
|
34
|
+
if not isinstance(input, torch.Tensor):
|
|
35
|
+
raise TypeError("input must be a torch.Tensor.")
|
|
36
|
+
if input.ndim <= self.dim or -input.ndim > self.dim:
|
|
37
|
+
raise ValueError(f"dim must be in range [{-input.ndim}, {input.ndim}) for input of this shape, but is currently {self.dim}.")
|
|
38
|
+
|
|
39
|
+
self.input = input
|
|
40
|
+
exponential_input = torch.exp(self.input - torch.max(self.input, dim=self.dim, keepdim=True).values)
|
|
41
|
+
self.output = exponential_input / torch.sum(exponential_input, dim=self.dim, keepdim=True)
|
|
42
|
+
return self.output
|
|
43
|
+
|
|
44
|
+
def backward(self, dCdy, **kwargs):
|
|
45
|
+
"""
|
|
46
|
+
Calculates the gradient of the loss function with respect to the input of the layer.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
dCdy (torch.Tensor of the same shape as returned from the forward method): The gradient given by the next layer.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
torch.Tensor of shape (n_samples, n_features): The new gradient after backpropagation through the layer.
|
|
53
|
+
"""
|
|
54
|
+
if not isinstance(dCdy, torch.Tensor):
|
|
55
|
+
raise TypeError("dCdy must be a torch.Tensor.")
|
|
56
|
+
if dCdy.shape != self.output.shape:
|
|
57
|
+
raise ValueError(f"dCdy is not the same shape as the spesified output_shape ({dCdy.shape[1:], self.output_shape}).")
|
|
58
|
+
|
|
59
|
+
# v1 - 2 dimensional
|
|
60
|
+
# n = dCdy.shape[1]
|
|
61
|
+
# # dCdx = torch.stack([(dCdy[i] @ (torch.tile(datapoint, (n, 1)).T * (torch.eye(n, device=self.device, dtype=self.data_type) - torch.tile(datapoint, (n, 1))))) for i, datapoint in enumerate(self.output)])
|
|
62
|
+
|
|
63
|
+
# v2 - 2 dimensional, but faster
|
|
64
|
+
# datapoints_expanded = self.output.unsqueeze(1).repeat(1, n, 1)
|
|
65
|
+
# identity_matrix = torch.eye(n, device=dCdy.device, dtype=dCdy.dtype)
|
|
66
|
+
# matrix_diff = identity_matrix - datapoints_expanded
|
|
67
|
+
# dCdx = dCdy.unsqueeze(1) @ (datapoints_expanded.transpose(1, 2) * matrix_diff)
|
|
68
|
+
# return dCdx.squeeze(1)
|
|
69
|
+
|
|
70
|
+
# v3 - n dimensional
|
|
71
|
+
# dim = self.dim if self.dim >= 0 else dCdy.ndim + self.dim
|
|
72
|
+
# n = self.output.shape[dim]
|
|
73
|
+
# output_expanded = self.output.unsqueeze(dim)
|
|
74
|
+
# identity_matrix = torch.eye(n, device=dCdy.device, dtype=dCdy.dtype).reshape((1,) * dim + (n, n) + (1,) * (dCdy.ndim - dim - 1))
|
|
75
|
+
# matrix_diff = identity_matrix - output_expanded
|
|
76
|
+
# # probably not optimal permuting the two wanted dimensions to the end for the matrix multiplication
|
|
77
|
+
# dCdx = dCdy.unsqueeze(dim).permute(*range(dim), *range(dim + 2, dCdy.ndim + 1), dim, dim + 1) @ (output_expanded.transpose(dim, dim + 1) * matrix_diff).permute(*range(dim), *range(dim + 2, dCdy.ndim + 1), dim, dim + 1)
|
|
78
|
+
# return dCdx.permute(*range(dim), dCdy.ndim - 1, dCdy.ndim, *range(dim, dCdy.ndim - 1)).squeeze(dim)
|
|
79
|
+
|
|
80
|
+
# v4 - n dimensional, but faster
|
|
81
|
+
output = self.output
|
|
82
|
+
dCdx = output * (dCdy - torch.sum(dCdy * output, dim=self.dim, keepdim=True))
|
|
83
|
+
return dCdx
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from ._Activation import Activation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Tanh(Activation):
|
|
7
|
+
"""
|
|
8
|
+
The hyperbolic tangent activation function.
|
|
9
|
+
"""
|
|
10
|
+
def __init__(self, **kwargs):
|
|
11
|
+
super().__init__(**kwargs)
|
|
12
|
+
self.name = "Tanh"
|
|
13
|
+
|
|
14
|
+
def forward(self, input, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
Calculates the hyperbolic tangent function for every element of the input matrix.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
input (torch.Tensor of shape (batch_size, ...)): The input to the layer. Must be a torch.Tensor of any shape.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
torch.Tensor: The output tensor after applying the activation function of the same shape as the input.
|
|
23
|
+
"""
|
|
24
|
+
if not isinstance(input, torch.Tensor):
|
|
25
|
+
raise TypeError("input must be a torch.Tensor.")
|
|
26
|
+
|
|
27
|
+
self.input = input
|
|
28
|
+
output = torch.tanh(input)
|
|
29
|
+
return output
|
|
30
|
+
|
|
31
|
+
def backward(self, dCdy, **kwargs):
|
|
32
|
+
"""
|
|
33
|
+
Calculates the gradient of the loss function with respect to the input of the layer.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
dCdy (torch.Tensor of the same shape as returned from the forward method): The gradient given by the next layer.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
torch.Tensor of shape (batch_size, ...): The new gradient after backpropagation through the layer.
|
|
40
|
+
"""
|
|
41
|
+
if not isinstance(dCdy, torch.Tensor):
|
|
42
|
+
raise TypeError("dCdy must be a torch.Tensor.")
|
|
43
|
+
if dCdy.shape != self.input.shape:
|
|
44
|
+
raise ValueError(f"dCdy is not the same shape as the spesified output_shape ({dCdy.shape[1:], self.output_shape}).")
|
|
45
|
+
|
|
46
|
+
dCdx = dCdy * (1 - torch.tanh(self.input) ** 2)
|
|
47
|
+
return dCdx
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ..Activations._Activation import Activation
|
|
2
|
+
from ....Exceptions import NotCompiledError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseRegularisation(Activation):
|
|
6
|
+
def summary(self, offset=""):
|
|
7
|
+
if not hasattr(self, "input_shape"):
|
|
8
|
+
raise NotCompiledError("layer must be initialized correctly before calling layer.summary().")
|
|
9
|
+
|
|
10
|
+
output_shape = self.output_shape[0] if len(self.output_shape) == 1 else self.output_shape
|
|
11
|
+
params_summary = " - Parameters: " + str(self.nparams) if self.nparams > 0 else ""
|
|
12
|
+
return offset + f"{self.name} - Output: ({output_shape})" + params_summary
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from ._BaseRegularisation import BaseRegularisation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BatchNorm(BaseRegularisation):
|
|
8
|
+
"""
|
|
9
|
+
The batch normalisation layer for neural networks.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
patience (float, optional): The number deciding how fast the mean and variance in training. Must be strictly between 0 and 1. Defaults to 0.9.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, patience=0.9, **kwargs):
|
|
15
|
+
if patience <= 0 or 1 <= patience:
|
|
16
|
+
raise ValueError("patience must be strictly between 0 and 1.")
|
|
17
|
+
|
|
18
|
+
super().__init__(**kwargs)
|
|
19
|
+
self.patience = patience
|
|
20
|
+
self.epsilon = 1e-6
|
|
21
|
+
self.name = "Batch normalisation"
|
|
22
|
+
|
|
23
|
+
def initialise_layer(self, **kwargs):
|
|
24
|
+
"""
|
|
25
|
+
:meta private:
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
super().initialise_layer(**kwargs)
|
|
29
|
+
|
|
30
|
+
self.gamma = torch.ones(self.output_shape, dtype=self.data_type, device=self.device)
|
|
31
|
+
self.beta = torch.zeros(self.output_shape, dtype=self.data_type, device=self.device)
|
|
32
|
+
self.running_var = torch.ones(self.output_shape, dtype=self.data_type, device=self.device)
|
|
33
|
+
self.running_mean = torch.zeros(self.output_shape, dtype=self.data_type, device=self.device)
|
|
34
|
+
self.nparams = 2 * np.prod(self.output_shape)
|
|
35
|
+
|
|
36
|
+
def forward(self, input, training=False, **kwargs):
|
|
37
|
+
"""
|
|
38
|
+
Normalises the input to have zero mean and one variance with the following equation:
|
|
39
|
+
|
|
40
|
+
.. math::
|
|
41
|
+
y = \\gamma\\frac{x - \\mathbb{E}[x]}{\\sqrt{\\text{var}(x) + \\epsilon}} + \\beta,
|
|
42
|
+
|
|
43
|
+
where :math:`x` is the input, :math:`\\mathbb{E}[x]` is the expected value or the mean accross the batch dimension, :math:`\\text{var}(x)` is the variance accross the variance accross the batch dimension, :math:`\\epsilon` is a small constant and :math:`\\gamma` and :math:`\\beta` are trainable parameters.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
input (torch.Tensor of shape (batch_size, channels, ...)): The input to the layer. Must be a torch.Tensor of the spesified shape given by layer.input_shape.
|
|
47
|
+
training (bool, optional): The boolean flag deciding if the model is in training mode. Defaults to False.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
torch.Tensor: The output tensor after the normalisation with the same shape as the input.
|
|
51
|
+
"""
|
|
52
|
+
if not isinstance(input, torch.Tensor):
|
|
53
|
+
raise TypeError("input must be a torch.Tensor.")
|
|
54
|
+
if input.shape[1:] != self.input_shape:
|
|
55
|
+
raise ValueError(f"input is not the same shape as the spesified input_shape ({input.shape[1:], self.input_shape}).")
|
|
56
|
+
if not isinstance(training, bool):
|
|
57
|
+
raise TypeError("training must be a boolean.")
|
|
58
|
+
if input.shape[0] <= 1:
|
|
59
|
+
raise ValueError("The batch size must be atleast 2.")
|
|
60
|
+
|
|
61
|
+
self.input = input
|
|
62
|
+
if training:
|
|
63
|
+
mean = torch.mean(input, axis=0)
|
|
64
|
+
variance = torch.var(input, axis=0, unbiased=True)
|
|
65
|
+
self.std = torch.sqrt(variance + self.epsilon)
|
|
66
|
+
self.running_mean = self.patience * self.running_mean + (1 - self.patience) * mean
|
|
67
|
+
self.running_var = self.patience * self.running_var + (1 - self.patience) * variance
|
|
68
|
+
self.x_centered = (self.input - mean)
|
|
69
|
+
self.x_norm = self.x_centered / self.std
|
|
70
|
+
self.output = self.gamma * self.x_norm + self.beta
|
|
71
|
+
else:
|
|
72
|
+
self.output = self.gamma * ((self.input - self.running_mean) / torch.sqrt(self.running_var + self.epsilon)) + self.beta
|
|
73
|
+
return self.output
|
|
74
|
+
|
|
75
|
+
def backward(self, dCdy, **kwargs):
|
|
76
|
+
"""
|
|
77
|
+
Calculates the gradient of the loss function with respect to the input of the layer. Also calculates the gradients of the loss function with respect to the model parameters.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
dCdy (torch.Tensor of the same shape as returned from the forward method): The gradient given by the next layer.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
torch.Tensor of shape (batch_size, channels, ...): The new gradient after backpropagation through the layer.
|
|
84
|
+
"""
|
|
85
|
+
if not isinstance(dCdy, torch.Tensor):
|
|
86
|
+
raise TypeError("dCdy must be a torch.Tensor.")
|
|
87
|
+
if dCdy.shape[1:] != self.output.shape[1:]:
|
|
88
|
+
raise ValueError(f"dCdy is not the same shape as the spesified output_shape ({dCdy.shape[1:], self.output.shape[1:]}).")
|
|
89
|
+
|
|
90
|
+
batch_size = self.output.shape[0]
|
|
91
|
+
dCdx_norm = dCdy * self.gamma
|
|
92
|
+
dCdgamma = (dCdy * self.x_norm).mean(axis=0)
|
|
93
|
+
dCdbeta = dCdy.mean(axis=0)
|
|
94
|
+
dCdvar = (dCdx_norm * self.x_centered * -self.std**(-3) / 2).sum(axis=0)
|
|
95
|
+
dCdmean = -((dCdx_norm / self.std).sum(axis=0) + dCdvar * (2 / batch_size) * self.x_centered.sum(axis=0))
|
|
96
|
+
dCdx = dCdx_norm / self.std + dCdvar * 2 * self.x_centered / (batch_size - 1) + dCdmean / batch_size
|
|
97
|
+
|
|
98
|
+
self.gamma.grad += dCdgamma
|
|
99
|
+
self.beta.grad += dCdbeta
|
|
100
|
+
return dCdx
|
|
101
|
+
|
|
102
|
+
def get_parameters(self):
|
|
103
|
+
"""
|
|
104
|
+
:meta private:
|
|
105
|
+
"""
|
|
106
|
+
return (self.gamma, self.beta)
|