commit 4b12aa44f2c532041ee6a37a4289156bc432e71d Author: Nicola Zangrandi Date: Thu Feb 8 09:55:43 2024 +0100 Split from monorepo diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8cd494 --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.idea/inspectionProfiles/Project_Default.xml +.idea/misc.xml +.idea/modules.xml +.idea/workspace.xml +.idea/NeuralNetwork.iml diff --git a/Matrix.py b/Matrix.py new file mode 100644 index 0000000..eaacf66 --- /dev/null +++ b/Matrix.py @@ -0,0 +1,84 @@ +import random + + +class Matrix: + def __init__(self, rows, cols): + self.rows = rows + self.cols = cols + self.data = [] + + for i in range(0, rows): + self.data.append([0 for _ in range(0, cols)]) + + @staticmethod + def from_list(lst): + m = Matrix(len(lst), 1) + m.map(lambda x, i, j: lst[i]) + return m + + @staticmethod + def product(a, b): + """Matrix product""" + if a.cols != b.rows: + return None + + result = Matrix(a.rows, b.cols) + result.map(lambda x, i, j: sum([a.data[i][k] * b.data[k][j] for k in range(0, a.cols)])) + + return result + + @staticmethod + def transpose(m): + """Transpose a matrix""" + result = Matrix(m.cols, m.rows) + result.map(lambda x, i, j: m.data[j][i]) + return result + + @staticmethod + def subtract(a, b): + result = Matrix(a.rows, a.cols) + result.map(lambda x, i, j: a.data[i][j] - b.data[i][j]) + return result + + @staticmethod + def clone(m): + result = Matrix(m.rows, m.cols) + result.map(lambda x, i, j: m.data[i][j]) + return result + + def map(self, fun): + """Execute a function on every element of the list, use the return value as the new value of the element""" + for i in range(0, self.rows): + for j in range(0, self.cols): + self.data[i][j] = fun(self.data[i][j], i, j) + + def randomize(self): + """Fill the matrix with random integers""" + self.map(lambda x, i, j: random.randint(-1, 1)) + + def multiply(self, n): + """Hadamard or scalar product""" + if type(n) is Matrix: + self.map(lambda x, i, j: x * n.data[i][j]) + else: + self.map(lambda x, i, j: x * n) + + def add(self, n): + """Entrywise or scalar addition""" + if type(n) is Matrix: + self.map(lambda x, i, j: x + n.data[i][j]) + else: + self.map(lambda x, i, j: x + n) + + def print(self): + """Pretty print the matrix""" + for row in self.data: + for elem in row: + print(elem, end='\t') + print() + + def to_list(self): + """Returns the internal matrix as a list""" + lst = [] + self.map(lambda x, i, j: lst.append(x)) + return lst diff --git a/NeuralNetwork.py b/NeuralNetwork.py new file mode 100644 index 0000000..0a534b0 --- /dev/null +++ b/NeuralNetwork.py @@ -0,0 +1,83 @@ +import math +from Matrix import Matrix + + +def sigmoid(x): + """The Sigmoid function""" + return 1 / (1 + math.exp(-x)) + + +def dsigmoid(x): + """The derivative of the Sigmoid function""" + return sigmoid(x) * (1 - sigmoid(x)) + + +class NeuralNetwork: + def __init__(self, num_i, num_h, num_o): + self.learning_rate = 0.1 + self.input_nodes = num_i + self.hidden_nodes = num_h + self.output_nodes = num_o + + self.weights_ih = Matrix(self.hidden_nodes, self.input_nodes) + self.weights_ho = Matrix(self.output_nodes, self.hidden_nodes) + self.bias_h = Matrix(self.hidden_nodes, 1) + self.bias_o = Matrix(self.output_nodes, 1) + + self.weights_ih.randomize() + self.weights_ho.randomize() + self.bias_h.randomize() + self.bias_o.randomize() + + def feedforward(self, input_list): + """Feedforward algorithm, basically the hidden layer""" + inputs = Matrix.from_list(input_list) + hidden = Matrix.product(self.weights_ih, inputs) + hidden.add(self.bias_h) + hidden.map(lambda x, i, j: sigmoid(x)) + + output = Matrix.product(self.weights_ho, hidden) + output.add(self.bias_o) + output.map(lambda x, i, j: sigmoid(x)) + + return output.to_list() + + def train(self, input_list, answer): + """Train the NN on a known input/answer combination""" + inputs = Matrix.from_list(input_list) + hidden = Matrix.product(self.weights_ih, inputs) + hidden.add(self.bias_h) + hidden.map(lambda x, i, j: sigmoid(x)) + + outputs = Matrix.product(self.weights_ho, hidden) + outputs.add(self.bias_o) + outputs.map(lambda x, i, j: sigmoid(x)) + + targets = Matrix.from_list(answer) + + output_errors = Matrix.subtract(targets, outputs) + + gradients = Matrix.clone(outputs) + gradients.map(lambda x, i, j: x * (1 - x)) + gradients.multiply(output_errors) + gradients.multiply(self.learning_rate) + + hidden_t = Matrix.transpose(hidden) + weight_ho_deltas = Matrix.product(gradients, hidden_t) + + self.weights_ho.add(weight_ho_deltas) + self.bias_o.add(gradients) + + who_t = Matrix.transpose(self.weights_ho) + hidden_errors = Matrix.product(who_t, output_errors) + + hidden_gradient = Matrix.clone(hidden) + hidden_gradient.map(lambda x, i, j: x * (1 - x)) + hidden_gradient.multiply(hidden_errors) + hidden_gradient.multiply(self.learning_rate) + + inputs_t = Matrix.transpose(inputs) + weight_ih_deltas = Matrix.product(hidden_gradient, inputs_t) + + self.weights_ih.add(weight_ih_deltas) + self.bias_h.add(hidden_gradient) diff --git a/Perceptron.py b/Perceptron.py new file mode 100644 index 0000000..96100ca --- /dev/null +++ b/Perceptron.py @@ -0,0 +1,32 @@ +import random + +class Perceptron: + def __init__(self, n): + self.weights = [random.randint(-1, 1) for _ in range(0, n)] + self.lr = 0.1 + + @staticmethod + def sign(output): + if output >= 0: + return 1 + else: + return -1 + + def guess(self, inputs): + s = 0 + for i, inp in enumerate(inputs): + s += inp * self.weights[i] + + return Perceptron.sign(s) + + def guessY(self, x): + w0 = self.weights[0] + w1 = self.weights[1] + w2 = self.weights[2] + + return -(w2 / w1) - (w0 / w1) * x + + def train(self, inputs, target): + error = target - self.guess(inputs) + for i, w in enumerate(self.weights): + self.weights[i] += error * inputs[i] * self.lr