import numpy as np import math from scipy.special import expit from secrets import token_hex from alphabet import CYRILLIC_ALPHABET """The neural network class.""" class NeuralNetwork: def __init__(self, learning_rate: float, input_resolution: int) -> None: self.learning_rate = learning_rate self.input_layer_size = input_resolution self.hidden_layer_size = len(CYRILLIC_ALPHABET) self._hidden_weights = self._random_array(self.hidden_layer_size, input_resolution) self._output_weights = self._random_array(input_resolution, 1) """ Train the neural network. It loads the dataset contained in ./data, converts each image into a numpy array and uses that data for training. Algorithm: 1-. Feedforward the input matrix. 2-. Calculate the output layer error. 3-. Adjust the weights of the output layer via gradient descent. 3-. Calculate the hidden layer error by backpropagating the output layer error. 4-. Adjust the weights of the hidden layer via gradient descent. """ def train(self): pass """ Guess the letter contained in the image file pointed by input_image (a path). """ def guess(self, input_image: np.array) -> str: output_layer = self.feedforward(input_image) return self._guessed_char(output_layer) """ Feedforwarding. """ def feedforward(self, input_layer: np.array): hidden_layer_inputs = np.dot(self._hidden_weights, input_layer) hidden_layer_outputs = self._get_layer_output(hidden_layer_inputs) output_layer_inputs = np.dot(hidden_layer_outputs, self._output_weights) # The output layer outputs. (Final output of the neural network). return self._get_layer_output(output_layer_inputs) """ Save the weights to a csv file. """ def save(self): np.savetxt(f"./hidden_weights_{token_hex(8)}.csv", self._hidden_weights, delimiter=',') np.savetxt(f"./output_weights_{token_hex(8)}.csv", self._output_weights, delimiter=',') """ Load the weights from a csv file. """ def load(self, hidden_weights_file: str, output_weights_file: str): with open(hidden_weights_file) as hidden_weights: self._hidden_weights = np.loadtxt(hidden_weights, delimiter=',') with open(output_weights_file) as output_weights: self._output_weights = np.loadtxt(output_weights, delimiter=',') """ Get the result from a sigmoid matrix (the index with the highest chance of being the correct answer). """ def _guessed_char(self, output_layer: np.array) -> str: return CYRILLIC_ALPHABET[np.argmax(np.transpose(output_layer))] """ Apply the sigmoid function to a given layer """ def _get_layer_output(self, layer: np.array) -> np.array: return expit(layer) """ Get the hidden layer and output layer error matrices. """ def _get_errors(self, target: str) -> tuple: output_layer_errors = np.substract(self._get_expected_outputs(target), self._output_layer) # Backpropagate the errors. hidden_layer_errors = np.dot(np.transpose(self._hidden_weights), output_layer_errors) return (hidden_layer_errors, output_layer_errors) """ Given a cyrillic letter, get the target outputs. """ def _get_expected_outputs(self, target: str) -> np.array: index = CYRILLIC_ALPHABET.index(target) expected_outputs = np.zeros(len(CYRILLIC_ALPHABET), dtype=np.int8) expected_outputs[index] = 1 return expected_outputs """ Generate a random array via an uniform distribution. """ def _random_array(self, rows: int, columns: int) -> np.array: low = -1 / math.sqrt(rows) high = 1 / math.sqrt(columns) return np.random.uniform(low, high, (rows, columns))