quacknet.convulationalManager
1from quacknet.convulationalFeutures import ConvulationalNetwork 2from quacknet.convulationalBackpropagation import CNNbackpropagation 3from quacknet.activationDerivativeFunctions import ReLUDerivative 4from quacknet.convulationalOptimiser import CNNoptimiser 5import numpy as np 6 7class CNNModel(CNNoptimiser): 8 def __init__(self, NeuralNetworkClass): 9 self.layers = [] 10 self.weights = [] 11 self.biases = [] 12 self.NeuralNetworkClass = NeuralNetworkClass 13 14 def addLayer(self, layer): 15 """ 16 Adds a layer to the CNN model. 17 18 Args: 19 layer (class): ConvLayer, PoolingLayer, ActivationLayer, and DenseLayer 20 """ 21 self.layers.append(layer) 22 23 def forward(self, inputTensor): 24 """ 25 Performs a forward pass through all layers. 26 27 Args: 28 inputTensor (ndarray): Input data tensor to the CNN. 29 30 Returns: 31 list: List of tensors output by each layer including the input. 32 """ 33 allTensors = [inputTensor] 34 for layer in self.layers: 35 inputTensor = layer.forward(inputTensor) 36 allTensors.append(inputTensor) 37 return allTensors 38 39 def _backpropagation(self, allTensors, trueValues): 40 """ 41 Performs backpropagation through all layers to compute gradients. 42 43 Args: 44 allTensors (list): List of all layer outputs from forward propagation. 45 46 Returns: 47 allWeightGradients (list): List of all the weight gradients calculated during backpropgation. 48 allBiasGradients (list): List of all the bias gradients calculated during backpropgation. 49 """ 50 weightGradients, biasGradients, errorTerms = self.layers[-1]._backpropagation(trueValues) # <-- this is a neural network 51 allWeightGradients = [weightGradients] 52 allBiasGradients = [biasGradients] 53 for i in range(len(self.layers) - 2, -1, -1): 54 if(type(self.layers[i]) == PoolingLayer or type(self.layers[i]) == ActivationLayer): 55 errorTerms = self.layers[i]._backpropagation(errorTerms, allTensors[i]) 56 elif(type(self.layers[i]) == ConvLayer): 57 weightGradients, biasGradients, errorTerms = self.layers[i]._backpropagation(errorTerms, allTensors[i]) 58 allWeightGradients.insert(0, weightGradients) 59 allBiasGradients.insert(0, biasGradients) 60 61 return allWeightGradients, allBiasGradients 62 63 def _optimser(self, inputData, labels, useBatches, weights, biases, batchSize, alpha, beta1, beta2, epsilon): 64 """ 65 Runs the Adam optimiser either with or without batches. 66 67 Args: 68 inputData (ndarray): All the training data. 69 labels (ndarray): All the true labels for the training data. 70 useBatches (bool): Whether to use batching. 71 weights (list): Current weights. 72 biases (list): Current biases. 73 batchSize (int): Size of batches. 74 alpha (float): Learning rate. 75 beta1 (float): Adam's beta1 parameter. 76 beta2 (float): Adam's beta2 parameter. 77 epsilon (float): Adam's epsilon parameter. 78 79 Returns: 80 list: The nodes (returned to calculate accuracy and loss). 81 list: Updated weights after optimisation 82 list: Updated biases after optimisation 83 """ 84 if(useBatches == True): 85 return CNNoptimiser._AdamsOptimiserWithBatches(self, inputData, labels, weights, biases, batchSize, alpha, beta1, beta2, epsilon) 86 else: 87 return CNNoptimiser._AdamsOptimiserWithoutBatches(self, inputData, labels, weights, biases, alpha, beta1, beta2, epsilon) 88 89 def train(self, inputData, labels, useBatches, batchSize, alpha = 0.001, beta1 = 0.9, beta2 = 0.999, epsilon = 1e-8): 90 """ 91 Trains the CNN for one epoch and calculates accuracy and average loss. 92 93 Args: 94 inputData (ndarray): All the training data. 95 labels (ndarray): All the true labels for the training data. 96 useBatches (bool): Whether to use batching. 97 batchSize (int): Size of batches. 98 alpha (float, optional): Learning rate. Default is 0.001. 99 beta1 (float, optional): Adam's beta1 parameter. Default is 0.9. 100 beta2 (float, optional): Adam's beta2 parameter. Default is 0.999. 101 epsilon (float, optional): Adam's epsilon parameter. Default is 1e-8. 102 103 Returns: 104 float: accuracy percentage. 105 float: average loss. 106 """ 107 correct, totalLoss = 0, 0 108 109 nodes, self.weights, self.biases = self._optimser(inputData, labels, useBatches, self.weights, self.biases, batchSize, alpha, beta1, beta2, epsilon) 110 111 lastLayer = len(nodes[0]) - 1 112 for i in range(len(nodes)): 113 totalLoss += self.NeuralNetworkClass.lossFunction(nodes[i][lastLayer], labels[i]) 114 nodeIndex = np.argmax(nodes[i][lastLayer]) 115 labelIndex = np.argmax(labels[i]) 116 if(nodeIndex == labelIndex): 117 correct += 1 118 return 100 * (correct / len(labels)), totalLoss / len(labels) 119 120 def createWeightsBiases(self): 121 """ 122 Initialises weights and biases for convolutional and dense layers. 123 """ 124 for i in range(len(self.layers)): 125 if(type(self.layers[i]) == ConvLayer): 126 kernalSize = self.layers[i].kernalSize 127 numKernals = self.layers[i].numKernals 128 depth = self.layers[i].depth 129 130 bounds = np.sqrt(2 / kernalSize) # He initialisation 131 132 self.weights.append(np.random.normal(0, bounds, size=(numKernals, depth, kernalSize, kernalSize))) 133 self.biases.append(np.zeros((numKernals))) 134 135 self.layers[i].kernalWeights = self.weights[-1] 136 self.layers[i].kernalBiases = self.biases[-1] 137 elif(type(self.layers[i]) == DenseLayer): 138 self.weights.append(self.layers[i].NeuralNetworkClass.weights) 139 self.biases.append(self.layers[i].NeuralNetworkClass.biases) 140 141 def saveModel(self, NNweights, NNbiases, CNNweights, CNNbiases, filename = "modelWeights.npz"): 142 """ 143 Saves model weights and biases to a compressed npz file. 144 145 Args: 146 NNweights (list): Weights of the dense neural network layers. 147 NNbiases (list): Biases of the dense neural network layers. 148 CNNweights (list): Weights of the convolutional layers. 149 CNNbiases (list): Biases of the convolutional layers. 150 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 151 """ 152 CNNweights = np.array(CNNweights, dtype=object) 153 CNNbiases = np.array(CNNbiases, dtype=object) 154 NNweights = np.array(NNweights, dtype=object) 155 NNbiases = np.array(NNbiases, dtype=object) 156 np.savez_compressed(filename, CNNweights = CNNweights, CNNbiases = CNNbiases, NNweights = NNweights, NNbiases = NNbiases, allow_pickle = True) 157 158 def loadModel(self, neuralNetwork, filename = "modelWeights.npz"): 159 """ 160 Loads model weights and biases from a compressed npz file and assigns them to layers. 161 162 Args: 163 neuralNetwork (class): The dense neural network to load weights into. 164 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 165 """ 166 data = np.load(filename, allow_pickle = True) 167 CNNweights = data["CNNweights"] 168 CNNbiases = data["CNNbiases"] 169 NNweights = data["NNweights"] 170 NNbiases = data["NNbiases"] 171 172 self.layers[-1].NeuralNetworkClass.weights = NNweights 173 self.layers[-1].NeuralNetworkClass.biases = NNbiases 174 neuralNetwork.weights = NNweights 175 neuralNetwork.biases = NNbiases 176 self.weights = CNNweights 177 self.biases = CNNbiases 178 179 currWeightIndex = 0 180 for i in range(len(self.layers)): 181 if(type(self.layers[i]) == ConvLayer): 182 self.layers[i].kernalWeights = CNNweights[currWeightIndex] 183 self.layers[i].kernalBiases = CNNbiases[currWeightIndex] 184 currWeightIndex += 1 185 186class ConvLayer(ConvulationalNetwork, CNNbackpropagation): 187 def __init__(self, kernalSize, depth, numKernals, stride, padding = "no"): 188 """ 189 Initialises a convolutional layer. 190 191 Args: 192 kernalSize (int): The size of the covolution kernel (assumed it is a square). 193 depth (int): Depth of the input tensor. 194 numKernals (int): Number of kernels in this layer. 195 stride (int): The stride length for convolution. 196 padding (str or int, optional): Padding size or "no" for no padding. Default is "no". 197 """ 198 self.kernalSize = kernalSize 199 self.numKernals = numKernals 200 self.kernalWeights = [] 201 self.kernalBiases = [] 202 self.depth = depth 203 self.stride = stride 204 self.padding = padding 205 if(padding.lower() == "no" or padding.lower() == "n"): 206 self.usePadding = False 207 else: 208 self.padding = int(self.padding) 209 self.usePadding = True 210 211 def forward(self, inputTensor): 212 """ 213 Performs a forward convolution pass. 214 215 Args: 216 inputTensor (ndarray): Input tensor to convolve. 217 218 Returns: 219 ndarray: Output tensor after convolution. 220 """ 221 return ConvulationalNetwork._kernalisation(self, inputTensor, self.kernalWeights, self.kernalBiases, self.kernalSize, self.usePadding, self.padding, self.stride) 222 223 def _backpropagation(self, errorPatch, inputTensor): 224 """ 225 Performs backpropagation to compute gradients for convolutional layer. 226 227 Args: 228 errorPatch (ndarray): Error gradient from the next layer. 229 inputTensor (ndarray): Input tensor to convolve. 230 231 Returns: 232 ndarray: Gradients of the loss with respect to kernels. 233 ndarray: Gradients of the loss with respect to biases for each kernel. 234 ndarray: Error terms propagated to the previous layer. 235 """ 236 return CNNbackpropagation._ConvolutionDerivative(self, errorPatch, self.kernalWeights, inputTensor, self.stride) 237 238class PoolingLayer(CNNbackpropagation): 239 def __init__(self, gridSize, stride, mode = "max"): 240 """ 241 Initialises a pooling layer. 242 243 Args: 244 gridSize (int): The size of the pooling window. 245 stride (int): The stride length for pooling. 246 mode (str, optional): Pooling mode of "max", "ave" (average), or "gap" (global average pooling). Default is "max". 247 """ 248 self.gridSize = gridSize 249 self.stride = stride 250 self.mode = mode.lower() 251 252 def forward(self, inputTensor): 253 """ 254 Performs forward pooling operation. 255 256 Args: 257 inputTensor (ndarray): Input tensor to pool. 258 259 Returns: 260 ndarray: Output tensor after pooling. 261 """ 262 if(self.mode == "gap" or self.mode == "global"): 263 return ConvulationalNetwork._poolingGlobalAverage(self, inputTensor) 264 return ConvulationalNetwork._pooling(self, inputTensor, self.gridSize, self.stride, self.mode) 265 266 def _backpropagation(self, errorPatch, inputTensor): 267 """ 268 Performs backpropagation through the pooling layer. 269 270 Args: 271 errorPatch (ndarray): Error gradient from the next layer. 272 inputTensor (ndarray): Input tensor during forward propagation. 273 274 Returns: 275 inputGradient (ndarray): Gradient of the loss. 276 """ 277 if(self.mode == "max"): 278 return CNNbackpropagation._MaxPoolingDerivative(self, errorPatch, inputTensor, self.gridSize, self.stride) 279 elif(self.mode == "ave"): 280 return CNNbackpropagation._AveragePoolingDerivative(self, errorPatch, inputTensor, self.gridSize, self.stride) 281 else: 282 return CNNbackpropagation._GlobalAveragePoolingDerivative(self, inputTensor) 283 284class DenseLayer: # basically a fancy neural network 285 def __init__(self, NeuralNetworkClass): 286 """ 287 Initialises a dense layer using a NeuralNetworkClass. 288 289 Args: 290 NeuralNetworkClass (class): the fully connected neural network class. 291 """ 292 self.NeuralNetworkClass = NeuralNetworkClass 293 self.orignalShape = 0 # orignalShape is the original shape of the input tensor 294 295 def forward(self, inputTensor): 296 """ 297 Flattens the input tensor and performs a forward pass. 298 299 Args: 300 inputTensor (ndarray): Input tensor to flatten and process. 301 302 Returns: 303 ndarray: Output of the dense layer. 304 """ 305 self.orignalShape = np.array(inputTensor).shape 306 inputArray = ConvulationalNetwork._flatternTensor(self, inputTensor) 307 self.layerNodes = self.NeuralNetworkClass.forwardPropagation(inputArray) 308 return self.layerNodes[-1] 309 310 def _backpropagation(self, trueValues): #return weigtGradients, biasGradients, errorTerms 311 """ 312 Performs backpropagation through the dense layer. 313 314 Args: 315 trueValues (ndarray): True labels for the input data. 316 317 Returns: 318 weightGradients (list of ndarray): Gradients of weights for each layer. 319 biasGradients (list of ndarray): Gradients of biases for each layer. 320 errorTerms (ndarray): Error terms from the output layer weights, reshaped to the input tensor. 321 """ 322 weightGradients, biasGradients, errorTerms = self.NeuralNetworkClass._backPropgation( 323 self.layerNodes, 324 self.NeuralNetworkClass.weights, 325 self.NeuralNetworkClass.biases, 326 trueValues, 327 True 328 ) 329 #errorTerms = np.array(self.NeuralNetworkClass.weights).T @ errorTerms 330 #errorTerms = errorTerms.reshape(self.orignalShape) 331 332 for i in reversed(range(len(self.NeuralNetworkClass.weights))): 333 errorTerms = self.NeuralNetworkClass.weights[i] @ errorTerms 334 errorTerms = errorTerms.reshape(self.orignalShape) 335 336 return weightGradients, biasGradients, errorTerms 337 338class ActivationLayer: # basically aplies an activation function over the whole Tensor (eg. leaky relu) 339 def forward(self, inputTensor): 340 """ 341 Applies the Leaky ReLU activation function to the input tensor. 342 343 Args: 344 inputTensor (ndarray): A 3D array representing the input. 345 346 Returns: 347 ndarray: A tensor with the same shape as the input with Leaky ReLU applied to it. 348 """ 349 return ConvulationalNetwork._activation(self, inputTensor) 350 351 def _backpropagation(self, errorPatch, inputTensor): 352 """ 353 Compute the gradient of the loss with respect to the input of the activation layer during backpropagation. 354 355 Args: 356 errorPatch (ndarray): Error gradient from the next layer. 357 inputTensor (ndarray): Input to the activation layer during forward propagation. 358 359 Returns: 360 inputGradient (ndarray): Gradient of the loss with respect to the inputTensor 361 """ 362 return CNNbackpropagation._ActivationLayerDerivative(self, errorPatch, ReLUDerivative, inputTensor) 363
8class CNNModel(CNNoptimiser): 9 def __init__(self, NeuralNetworkClass): 10 self.layers = [] 11 self.weights = [] 12 self.biases = [] 13 self.NeuralNetworkClass = NeuralNetworkClass 14 15 def addLayer(self, layer): 16 """ 17 Adds a layer to the CNN model. 18 19 Args: 20 layer (class): ConvLayer, PoolingLayer, ActivationLayer, and DenseLayer 21 """ 22 self.layers.append(layer) 23 24 def forward(self, inputTensor): 25 """ 26 Performs a forward pass through all layers. 27 28 Args: 29 inputTensor (ndarray): Input data tensor to the CNN. 30 31 Returns: 32 list: List of tensors output by each layer including the input. 33 """ 34 allTensors = [inputTensor] 35 for layer in self.layers: 36 inputTensor = layer.forward(inputTensor) 37 allTensors.append(inputTensor) 38 return allTensors 39 40 def _backpropagation(self, allTensors, trueValues): 41 """ 42 Performs backpropagation through all layers to compute gradients. 43 44 Args: 45 allTensors (list): List of all layer outputs from forward propagation. 46 47 Returns: 48 allWeightGradients (list): List of all the weight gradients calculated during backpropgation. 49 allBiasGradients (list): List of all the bias gradients calculated during backpropgation. 50 """ 51 weightGradients, biasGradients, errorTerms = self.layers[-1]._backpropagation(trueValues) # <-- this is a neural network 52 allWeightGradients = [weightGradients] 53 allBiasGradients = [biasGradients] 54 for i in range(len(self.layers) - 2, -1, -1): 55 if(type(self.layers[i]) == PoolingLayer or type(self.layers[i]) == ActivationLayer): 56 errorTerms = self.layers[i]._backpropagation(errorTerms, allTensors[i]) 57 elif(type(self.layers[i]) == ConvLayer): 58 weightGradients, biasGradients, errorTerms = self.layers[i]._backpropagation(errorTerms, allTensors[i]) 59 allWeightGradients.insert(0, weightGradients) 60 allBiasGradients.insert(0, biasGradients) 61 62 return allWeightGradients, allBiasGradients 63 64 def _optimser(self, inputData, labels, useBatches, weights, biases, batchSize, alpha, beta1, beta2, epsilon): 65 """ 66 Runs the Adam optimiser either with or without batches. 67 68 Args: 69 inputData (ndarray): All the training data. 70 labels (ndarray): All the true labels for the training data. 71 useBatches (bool): Whether to use batching. 72 weights (list): Current weights. 73 biases (list): Current biases. 74 batchSize (int): Size of batches. 75 alpha (float): Learning rate. 76 beta1 (float): Adam's beta1 parameter. 77 beta2 (float): Adam's beta2 parameter. 78 epsilon (float): Adam's epsilon parameter. 79 80 Returns: 81 list: The nodes (returned to calculate accuracy and loss). 82 list: Updated weights after optimisation 83 list: Updated biases after optimisation 84 """ 85 if(useBatches == True): 86 return CNNoptimiser._AdamsOptimiserWithBatches(self, inputData, labels, weights, biases, batchSize, alpha, beta1, beta2, epsilon) 87 else: 88 return CNNoptimiser._AdamsOptimiserWithoutBatches(self, inputData, labels, weights, biases, alpha, beta1, beta2, epsilon) 89 90 def train(self, inputData, labels, useBatches, batchSize, alpha = 0.001, beta1 = 0.9, beta2 = 0.999, epsilon = 1e-8): 91 """ 92 Trains the CNN for one epoch and calculates accuracy and average loss. 93 94 Args: 95 inputData (ndarray): All the training data. 96 labels (ndarray): All the true labels for the training data. 97 useBatches (bool): Whether to use batching. 98 batchSize (int): Size of batches. 99 alpha (float, optional): Learning rate. Default is 0.001. 100 beta1 (float, optional): Adam's beta1 parameter. Default is 0.9. 101 beta2 (float, optional): Adam's beta2 parameter. Default is 0.999. 102 epsilon (float, optional): Adam's epsilon parameter. Default is 1e-8. 103 104 Returns: 105 float: accuracy percentage. 106 float: average loss. 107 """ 108 correct, totalLoss = 0, 0 109 110 nodes, self.weights, self.biases = self._optimser(inputData, labels, useBatches, self.weights, self.biases, batchSize, alpha, beta1, beta2, epsilon) 111 112 lastLayer = len(nodes[0]) - 1 113 for i in range(len(nodes)): 114 totalLoss += self.NeuralNetworkClass.lossFunction(nodes[i][lastLayer], labels[i]) 115 nodeIndex = np.argmax(nodes[i][lastLayer]) 116 labelIndex = np.argmax(labels[i]) 117 if(nodeIndex == labelIndex): 118 correct += 1 119 return 100 * (correct / len(labels)), totalLoss / len(labels) 120 121 def createWeightsBiases(self): 122 """ 123 Initialises weights and biases for convolutional and dense layers. 124 """ 125 for i in range(len(self.layers)): 126 if(type(self.layers[i]) == ConvLayer): 127 kernalSize = self.layers[i].kernalSize 128 numKernals = self.layers[i].numKernals 129 depth = self.layers[i].depth 130 131 bounds = np.sqrt(2 / kernalSize) # He initialisation 132 133 self.weights.append(np.random.normal(0, bounds, size=(numKernals, depth, kernalSize, kernalSize))) 134 self.biases.append(np.zeros((numKernals))) 135 136 self.layers[i].kernalWeights = self.weights[-1] 137 self.layers[i].kernalBiases = self.biases[-1] 138 elif(type(self.layers[i]) == DenseLayer): 139 self.weights.append(self.layers[i].NeuralNetworkClass.weights) 140 self.biases.append(self.layers[i].NeuralNetworkClass.biases) 141 142 def saveModel(self, NNweights, NNbiases, CNNweights, CNNbiases, filename = "modelWeights.npz"): 143 """ 144 Saves model weights and biases to a compressed npz file. 145 146 Args: 147 NNweights (list): Weights of the dense neural network layers. 148 NNbiases (list): Biases of the dense neural network layers. 149 CNNweights (list): Weights of the convolutional layers. 150 CNNbiases (list): Biases of the convolutional layers. 151 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 152 """ 153 CNNweights = np.array(CNNweights, dtype=object) 154 CNNbiases = np.array(CNNbiases, dtype=object) 155 NNweights = np.array(NNweights, dtype=object) 156 NNbiases = np.array(NNbiases, dtype=object) 157 np.savez_compressed(filename, CNNweights = CNNweights, CNNbiases = CNNbiases, NNweights = NNweights, NNbiases = NNbiases, allow_pickle = True) 158 159 def loadModel(self, neuralNetwork, filename = "modelWeights.npz"): 160 """ 161 Loads model weights and biases from a compressed npz file and assigns them to layers. 162 163 Args: 164 neuralNetwork (class): The dense neural network to load weights into. 165 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 166 """ 167 data = np.load(filename, allow_pickle = True) 168 CNNweights = data["CNNweights"] 169 CNNbiases = data["CNNbiases"] 170 NNweights = data["NNweights"] 171 NNbiases = data["NNbiases"] 172 173 self.layers[-1].NeuralNetworkClass.weights = NNweights 174 self.layers[-1].NeuralNetworkClass.biases = NNbiases 175 neuralNetwork.weights = NNweights 176 neuralNetwork.biases = NNbiases 177 self.weights = CNNweights 178 self.biases = CNNbiases 179 180 currWeightIndex = 0 181 for i in range(len(self.layers)): 182 if(type(self.layers[i]) == ConvLayer): 183 self.layers[i].kernalWeights = CNNweights[currWeightIndex] 184 self.layers[i].kernalBiases = CNNbiases[currWeightIndex] 185 currWeightIndex += 1
15 def addLayer(self, layer): 16 """ 17 Adds a layer to the CNN model. 18 19 Args: 20 layer (class): ConvLayer, PoolingLayer, ActivationLayer, and DenseLayer 21 """ 22 self.layers.append(layer)
Adds a layer to the CNN model.
Args: layer (class): ConvLayer, PoolingLayer, ActivationLayer, and DenseLayer
24 def forward(self, inputTensor): 25 """ 26 Performs a forward pass through all layers. 27 28 Args: 29 inputTensor (ndarray): Input data tensor to the CNN. 30 31 Returns: 32 list: List of tensors output by each layer including the input. 33 """ 34 allTensors = [inputTensor] 35 for layer in self.layers: 36 inputTensor = layer.forward(inputTensor) 37 allTensors.append(inputTensor) 38 return allTensors
Performs a forward pass through all layers.
Args: inputTensor (ndarray): Input data tensor to the CNN.
Returns: list: List of tensors output by each layer including the input.
90 def train(self, inputData, labels, useBatches, batchSize, alpha = 0.001, beta1 = 0.9, beta2 = 0.999, epsilon = 1e-8): 91 """ 92 Trains the CNN for one epoch and calculates accuracy and average loss. 93 94 Args: 95 inputData (ndarray): All the training data. 96 labels (ndarray): All the true labels for the training data. 97 useBatches (bool): Whether to use batching. 98 batchSize (int): Size of batches. 99 alpha (float, optional): Learning rate. Default is 0.001. 100 beta1 (float, optional): Adam's beta1 parameter. Default is 0.9. 101 beta2 (float, optional): Adam's beta2 parameter. Default is 0.999. 102 epsilon (float, optional): Adam's epsilon parameter. Default is 1e-8. 103 104 Returns: 105 float: accuracy percentage. 106 float: average loss. 107 """ 108 correct, totalLoss = 0, 0 109 110 nodes, self.weights, self.biases = self._optimser(inputData, labels, useBatches, self.weights, self.biases, batchSize, alpha, beta1, beta2, epsilon) 111 112 lastLayer = len(nodes[0]) - 1 113 for i in range(len(nodes)): 114 totalLoss += self.NeuralNetworkClass.lossFunction(nodes[i][lastLayer], labels[i]) 115 nodeIndex = np.argmax(nodes[i][lastLayer]) 116 labelIndex = np.argmax(labels[i]) 117 if(nodeIndex == labelIndex): 118 correct += 1 119 return 100 * (correct / len(labels)), totalLoss / len(labels)
Trains the CNN for one epoch and calculates accuracy and average loss.
Args: inputData (ndarray): All the training data. labels (ndarray): All the true labels for the training data. useBatches (bool): Whether to use batching. batchSize (int): Size of batches. alpha (float, optional): Learning rate. Default is 0.001. beta1 (float, optional): Adam's beta1 parameter. Default is 0.9. beta2 (float, optional): Adam's beta2 parameter. Default is 0.999. epsilon (float, optional): Adam's epsilon parameter. Default is 1e-8.
Returns: float: accuracy percentage. float: average loss.
121 def createWeightsBiases(self): 122 """ 123 Initialises weights and biases for convolutional and dense layers. 124 """ 125 for i in range(len(self.layers)): 126 if(type(self.layers[i]) == ConvLayer): 127 kernalSize = self.layers[i].kernalSize 128 numKernals = self.layers[i].numKernals 129 depth = self.layers[i].depth 130 131 bounds = np.sqrt(2 / kernalSize) # He initialisation 132 133 self.weights.append(np.random.normal(0, bounds, size=(numKernals, depth, kernalSize, kernalSize))) 134 self.biases.append(np.zeros((numKernals))) 135 136 self.layers[i].kernalWeights = self.weights[-1] 137 self.layers[i].kernalBiases = self.biases[-1] 138 elif(type(self.layers[i]) == DenseLayer): 139 self.weights.append(self.layers[i].NeuralNetworkClass.weights) 140 self.biases.append(self.layers[i].NeuralNetworkClass.biases)
Initialises weights and biases for convolutional and dense layers.
142 def saveModel(self, NNweights, NNbiases, CNNweights, CNNbiases, filename = "modelWeights.npz"): 143 """ 144 Saves model weights and biases to a compressed npz file. 145 146 Args: 147 NNweights (list): Weights of the dense neural network layers. 148 NNbiases (list): Biases of the dense neural network layers. 149 CNNweights (list): Weights of the convolutional layers. 150 CNNbiases (list): Biases of the convolutional layers. 151 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 152 """ 153 CNNweights = np.array(CNNweights, dtype=object) 154 CNNbiases = np.array(CNNbiases, dtype=object) 155 NNweights = np.array(NNweights, dtype=object) 156 NNbiases = np.array(NNbiases, dtype=object) 157 np.savez_compressed(filename, CNNweights = CNNweights, CNNbiases = CNNbiases, NNweights = NNweights, NNbiases = NNbiases, allow_pickle = True)
Saves model weights and biases to a compressed npz file.
Args: NNweights (list): Weights of the dense neural network layers. NNbiases (list): Biases of the dense neural network layers. CNNweights (list): Weights of the convolutional layers. CNNbiases (list): Biases of the convolutional layers. filename (str, optional): Filename to save the weights. Default is "modelWeights.npz".
159 def loadModel(self, neuralNetwork, filename = "modelWeights.npz"): 160 """ 161 Loads model weights and biases from a compressed npz file and assigns them to layers. 162 163 Args: 164 neuralNetwork (class): The dense neural network to load weights into. 165 filename (str, optional): Filename to save the weights. Default is "modelWeights.npz". 166 """ 167 data = np.load(filename, allow_pickle = True) 168 CNNweights = data["CNNweights"] 169 CNNbiases = data["CNNbiases"] 170 NNweights = data["NNweights"] 171 NNbiases = data["NNbiases"] 172 173 self.layers[-1].NeuralNetworkClass.weights = NNweights 174 self.layers[-1].NeuralNetworkClass.biases = NNbiases 175 neuralNetwork.weights = NNweights 176 neuralNetwork.biases = NNbiases 177 self.weights = CNNweights 178 self.biases = CNNbiases 179 180 currWeightIndex = 0 181 for i in range(len(self.layers)): 182 if(type(self.layers[i]) == ConvLayer): 183 self.layers[i].kernalWeights = CNNweights[currWeightIndex] 184 self.layers[i].kernalBiases = CNNbiases[currWeightIndex] 185 currWeightIndex += 1
Loads model weights and biases from a compressed npz file and assigns them to layers.
Args: neuralNetwork (class): The dense neural network to load weights into. filename (str, optional): Filename to save the weights. Default is "modelWeights.npz".
187class ConvLayer(ConvulationalNetwork, CNNbackpropagation): 188 def __init__(self, kernalSize, depth, numKernals, stride, padding = "no"): 189 """ 190 Initialises a convolutional layer. 191 192 Args: 193 kernalSize (int): The size of the covolution kernel (assumed it is a square). 194 depth (int): Depth of the input tensor. 195 numKernals (int): Number of kernels in this layer. 196 stride (int): The stride length for convolution. 197 padding (str or int, optional): Padding size or "no" for no padding. Default is "no". 198 """ 199 self.kernalSize = kernalSize 200 self.numKernals = numKernals 201 self.kernalWeights = [] 202 self.kernalBiases = [] 203 self.depth = depth 204 self.stride = stride 205 self.padding = padding 206 if(padding.lower() == "no" or padding.lower() == "n"): 207 self.usePadding = False 208 else: 209 self.padding = int(self.padding) 210 self.usePadding = True 211 212 def forward(self, inputTensor): 213 """ 214 Performs a forward convolution pass. 215 216 Args: 217 inputTensor (ndarray): Input tensor to convolve. 218 219 Returns: 220 ndarray: Output tensor after convolution. 221 """ 222 return ConvulationalNetwork._kernalisation(self, inputTensor, self.kernalWeights, self.kernalBiases, self.kernalSize, self.usePadding, self.padding, self.stride) 223 224 def _backpropagation(self, errorPatch, inputTensor): 225 """ 226 Performs backpropagation to compute gradients for convolutional layer. 227 228 Args: 229 errorPatch (ndarray): Error gradient from the next layer. 230 inputTensor (ndarray): Input tensor to convolve. 231 232 Returns: 233 ndarray: Gradients of the loss with respect to kernels. 234 ndarray: Gradients of the loss with respect to biases for each kernel. 235 ndarray: Error terms propagated to the previous layer. 236 """ 237 return CNNbackpropagation._ConvolutionDerivative(self, errorPatch, self.kernalWeights, inputTensor, self.stride)
188 def __init__(self, kernalSize, depth, numKernals, stride, padding = "no"): 189 """ 190 Initialises a convolutional layer. 191 192 Args: 193 kernalSize (int): The size of the covolution kernel (assumed it is a square). 194 depth (int): Depth of the input tensor. 195 numKernals (int): Number of kernels in this layer. 196 stride (int): The stride length for convolution. 197 padding (str or int, optional): Padding size or "no" for no padding. Default is "no". 198 """ 199 self.kernalSize = kernalSize 200 self.numKernals = numKernals 201 self.kernalWeights = [] 202 self.kernalBiases = [] 203 self.depth = depth 204 self.stride = stride 205 self.padding = padding 206 if(padding.lower() == "no" or padding.lower() == "n"): 207 self.usePadding = False 208 else: 209 self.padding = int(self.padding) 210 self.usePadding = True
Initialises a convolutional layer.
Args: kernalSize (int): The size of the covolution kernel (assumed it is a square). depth (int): Depth of the input tensor. numKernals (int): Number of kernels in this layer. stride (int): The stride length for convolution. padding (str or int, optional): Padding size or "no" for no padding. Default is "no".
212 def forward(self, inputTensor): 213 """ 214 Performs a forward convolution pass. 215 216 Args: 217 inputTensor (ndarray): Input tensor to convolve. 218 219 Returns: 220 ndarray: Output tensor after convolution. 221 """ 222 return ConvulationalNetwork._kernalisation(self, inputTensor, self.kernalWeights, self.kernalBiases, self.kernalSize, self.usePadding, self.padding, self.stride)
Performs a forward convolution pass.
Args: inputTensor (ndarray): Input tensor to convolve.
Returns: ndarray: Output tensor after convolution.
239class PoolingLayer(CNNbackpropagation): 240 def __init__(self, gridSize, stride, mode = "max"): 241 """ 242 Initialises a pooling layer. 243 244 Args: 245 gridSize (int): The size of the pooling window. 246 stride (int): The stride length for pooling. 247 mode (str, optional): Pooling mode of "max", "ave" (average), or "gap" (global average pooling). Default is "max". 248 """ 249 self.gridSize = gridSize 250 self.stride = stride 251 self.mode = mode.lower() 252 253 def forward(self, inputTensor): 254 """ 255 Performs forward pooling operation. 256 257 Args: 258 inputTensor (ndarray): Input tensor to pool. 259 260 Returns: 261 ndarray: Output tensor after pooling. 262 """ 263 if(self.mode == "gap" or self.mode == "global"): 264 return ConvulationalNetwork._poolingGlobalAverage(self, inputTensor) 265 return ConvulationalNetwork._pooling(self, inputTensor, self.gridSize, self.stride, self.mode) 266 267 def _backpropagation(self, errorPatch, inputTensor): 268 """ 269 Performs backpropagation through the pooling layer. 270 271 Args: 272 errorPatch (ndarray): Error gradient from the next layer. 273 inputTensor (ndarray): Input tensor during forward propagation. 274 275 Returns: 276 inputGradient (ndarray): Gradient of the loss. 277 """ 278 if(self.mode == "max"): 279 return CNNbackpropagation._MaxPoolingDerivative(self, errorPatch, inputTensor, self.gridSize, self.stride) 280 elif(self.mode == "ave"): 281 return CNNbackpropagation._AveragePoolingDerivative(self, errorPatch, inputTensor, self.gridSize, self.stride) 282 else: 283 return CNNbackpropagation._GlobalAveragePoolingDerivative(self, inputTensor)
240 def __init__(self, gridSize, stride, mode = "max"): 241 """ 242 Initialises a pooling layer. 243 244 Args: 245 gridSize (int): The size of the pooling window. 246 stride (int): The stride length for pooling. 247 mode (str, optional): Pooling mode of "max", "ave" (average), or "gap" (global average pooling). Default is "max". 248 """ 249 self.gridSize = gridSize 250 self.stride = stride 251 self.mode = mode.lower()
Initialises a pooling layer.
Args: gridSize (int): The size of the pooling window. stride (int): The stride length for pooling. mode (str, optional): Pooling mode of "max", "ave" (average), or "gap" (global average pooling). Default is "max".
253 def forward(self, inputTensor): 254 """ 255 Performs forward pooling operation. 256 257 Args: 258 inputTensor (ndarray): Input tensor to pool. 259 260 Returns: 261 ndarray: Output tensor after pooling. 262 """ 263 if(self.mode == "gap" or self.mode == "global"): 264 return ConvulationalNetwork._poolingGlobalAverage(self, inputTensor) 265 return ConvulationalNetwork._pooling(self, inputTensor, self.gridSize, self.stride, self.mode)
Performs forward pooling operation.
Args: inputTensor (ndarray): Input tensor to pool.
Returns: ndarray: Output tensor after pooling.
285class DenseLayer: # basically a fancy neural network 286 def __init__(self, NeuralNetworkClass): 287 """ 288 Initialises a dense layer using a NeuralNetworkClass. 289 290 Args: 291 NeuralNetworkClass (class): the fully connected neural network class. 292 """ 293 self.NeuralNetworkClass = NeuralNetworkClass 294 self.orignalShape = 0 # orignalShape is the original shape of the input tensor 295 296 def forward(self, inputTensor): 297 """ 298 Flattens the input tensor and performs a forward pass. 299 300 Args: 301 inputTensor (ndarray): Input tensor to flatten and process. 302 303 Returns: 304 ndarray: Output of the dense layer. 305 """ 306 self.orignalShape = np.array(inputTensor).shape 307 inputArray = ConvulationalNetwork._flatternTensor(self, inputTensor) 308 self.layerNodes = self.NeuralNetworkClass.forwardPropagation(inputArray) 309 return self.layerNodes[-1] 310 311 def _backpropagation(self, trueValues): #return weigtGradients, biasGradients, errorTerms 312 """ 313 Performs backpropagation through the dense layer. 314 315 Args: 316 trueValues (ndarray): True labels for the input data. 317 318 Returns: 319 weightGradients (list of ndarray): Gradients of weights for each layer. 320 biasGradients (list of ndarray): Gradients of biases for each layer. 321 errorTerms (ndarray): Error terms from the output layer weights, reshaped to the input tensor. 322 """ 323 weightGradients, biasGradients, errorTerms = self.NeuralNetworkClass._backPropgation( 324 self.layerNodes, 325 self.NeuralNetworkClass.weights, 326 self.NeuralNetworkClass.biases, 327 trueValues, 328 True 329 ) 330 #errorTerms = np.array(self.NeuralNetworkClass.weights).T @ errorTerms 331 #errorTerms = errorTerms.reshape(self.orignalShape) 332 333 for i in reversed(range(len(self.NeuralNetworkClass.weights))): 334 errorTerms = self.NeuralNetworkClass.weights[i] @ errorTerms 335 errorTerms = errorTerms.reshape(self.orignalShape) 336 337 return weightGradients, biasGradients, errorTerms
286 def __init__(self, NeuralNetworkClass): 287 """ 288 Initialises a dense layer using a NeuralNetworkClass. 289 290 Args: 291 NeuralNetworkClass (class): the fully connected neural network class. 292 """ 293 self.NeuralNetworkClass = NeuralNetworkClass 294 self.orignalShape = 0 # orignalShape is the original shape of the input tensor
Initialises a dense layer using a NeuralNetworkClass.
Args: NeuralNetworkClass (class): the fully connected neural network class.
296 def forward(self, inputTensor): 297 """ 298 Flattens the input tensor and performs a forward pass. 299 300 Args: 301 inputTensor (ndarray): Input tensor to flatten and process. 302 303 Returns: 304 ndarray: Output of the dense layer. 305 """ 306 self.orignalShape = np.array(inputTensor).shape 307 inputArray = ConvulationalNetwork._flatternTensor(self, inputTensor) 308 self.layerNodes = self.NeuralNetworkClass.forwardPropagation(inputArray) 309 return self.layerNodes[-1]
Flattens the input tensor and performs a forward pass.
Args: inputTensor (ndarray): Input tensor to flatten and process.
Returns: ndarray: Output of the dense layer.
339class ActivationLayer: # basically aplies an activation function over the whole Tensor (eg. leaky relu) 340 def forward(self, inputTensor): 341 """ 342 Applies the Leaky ReLU activation function to the input tensor. 343 344 Args: 345 inputTensor (ndarray): A 3D array representing the input. 346 347 Returns: 348 ndarray: A tensor with the same shape as the input with Leaky ReLU applied to it. 349 """ 350 return ConvulationalNetwork._activation(self, inputTensor) 351 352 def _backpropagation(self, errorPatch, inputTensor): 353 """ 354 Compute the gradient of the loss with respect to the input of the activation layer during backpropagation. 355 356 Args: 357 errorPatch (ndarray): Error gradient from the next layer. 358 inputTensor (ndarray): Input to the activation layer during forward propagation. 359 360 Returns: 361 inputGradient (ndarray): Gradient of the loss with respect to the inputTensor 362 """ 363 return CNNbackpropagation._ActivationLayerDerivative(self, errorPatch, ReLUDerivative, inputTensor)
340 def forward(self, inputTensor): 341 """ 342 Applies the Leaky ReLU activation function to the input tensor. 343 344 Args: 345 inputTensor (ndarray): A 3D array representing the input. 346 347 Returns: 348 ndarray: A tensor with the same shape as the input with Leaky ReLU applied to it. 349 """ 350 return ConvulationalNetwork._activation(self, inputTensor)
Applies the Leaky ReLU activation function to the input tensor.
Args: inputTensor (ndarray): A 3D array representing the input.
Returns: ndarray: A tensor with the same shape as the input with Leaky ReLU applied to it.