first commit

This commit is contained in:
Justine
2026-03-01 20:07:02 +01:00
commit c9351ca3c6
5 changed files with 122 additions and 0 deletions

108
main.py Executable file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env python
# coding: utf-8
import numpy as np
import pandas as pd
class Perceptron():
def __init__(self, learning_rate = .01, n_iterations = 1000):
# Basic elements of a perceptron. See https://sharpsight.ai/blog/python-perceptron-from-scratch
self.learning_rate = learning_rate
self.n_iterations = n_iterations
self.bias = None
self.weights = None
def activation_function(self, net_input):
# Activation function, that determines if the perceptron fires a 1 or a 0, if the sum of the weighted inputs + the bias (net_input) reaches a threshold.
# This is a step function, using np.where which is basically an inline if then else.
# if net_input > 0, return 1, else return 0.
# A step function is basically a function that returns something underneath a threshold, and something else above the threshold.
# The step function is classical and simple enough for a perceptron activation function, but there are many others.
return np.where(net_input > 0, 1, 0)
# The learning part of the perceptron (the "fit" function).
# Learning means the system improves performance on some task as it is exposed to more data.
# So, we are going to improve our perceptron's performance as a classifier by exposing it to training examples.
# The basic gist of it is :
# Take a list of inputs (called features) and a truth goal. Multiply the inputs with their respective weights.
# Take all of the weights and do a *summation* of them, which in this case is a simple addition.
# Take this result and give it to the activation function
# It has completed a run, with a truth goal y and a result ŷ. We do y - ŷ.
# Finally, we adjust the weights such as, for each weight :
# new weight = old weight + learning rate * (truth goal - result) * feature
# With this done, do it again with the next list of inputs.
def fit(self, features, targets):
# features : 2-dimensional numpy array (a matrix in math terms) containing the "features" of the training set, ie. the list of inputs in the above explanation. It contains the WHOLE training set, ie. a list of lists, ie. a matrix.
# targets : the truth goals
#the "shape" is the shape of the matrix, ie. the number of rows and columns.
# n_examples == the number of rows == the number of examples
# n_features == the number of columns == the number of "features" each example has.
n_examples, n_features = features.shape
# ORIGINAL COMMENT change these to use different initialization scheme
# We initialize the weights, as a list the size as long as n_features.
# We also initialize the bias function.
# Every value being set here is simply a random float between -0.5 and +0.5.
# This is the *initialization scheme*. This is a basic one but there are many others depending on what we are trying to achieve.
self.weights = np.random.uniform(size = n_features, low = -0.5, high = 0.5)
self.bias = np.random.uniform(low = -0.5, high = 0.5)
# This is the part where we iterate through the training data and update our weights.
# The number of iterations is generally way higher that the number of examples in our data.
# In practice we shuffle the examples, loop through them once. This is called an epoch.
# We can do a lot of epochs (n_iterations here is the number of epochs).
# 1000 or 2000 is reasonable here.
for _ in range(self.n_iterations):
# Start of an epoch.
# enumerate takes our list of sublists and gives a result such as (index of the sublist in the root list, sublist)
for example_index, example_features in enumerate(features):
# np.dot is used to perform 1-dimension matrix multiplication here (called "dot product" but fuck that) : take each value in example_features, and multiply them by each weight in self.weights. Then add the bias term to each resulting value.
net_input = np.dot(example_features, self.weights) + self.bias
# Here, we pass our list of results (net_input) and give it to the activation function, and record that in y_predicted, which is either 0 or 1.
y_predicted = self.activation_function(net_input)
# Then, we update the weights.
self._update_weights(example_features, targets[example_index], y_predicted)
# This is where we apply the function I talked about above :
# new weight = old weight + learning rate * (truth goal - result) * feature.
# This is simply done on multiple lines.
def _update_weights(self, example_features, y_actual, y_predicted):
# truth goal - result...
error = y_actual - y_predicted
# learning rate * (truth goal - result) gives us the weight correction to apply
weight_correction = self.learning_rate * error
# Apply the weight correction by adding it to the weights (remember, the value of the correction can be negative, decreasing the value=
# However, it seems we lack some parentheses ? Since apparently python applies PEMDAS, but maybe not in this case
self.weights = self.weights + weight_correction * example_features
# The bias gets corrected as well.
self.bias = self.bias + weight_correction
# Finally, we have a predict function, to use our perceptron once it is trained.
# It outputs either 0 or 1. It is the same principle as used in training, except we simply return y_predicted
def predict(self, features):
net_input = np.dot(features, self.weights) + self.bias
y_predicted = self.activation_function(net_input)
return y_predicted
# Now can we use our perceptron ? This is not covered in the tutorial.
# Instantiate the model
model = Perceptron(learning_rate=0.1, n_iterations=1000)
# Load the data. We try to detect if x1 + x2 > 2
df = pd.read_csv('data.csv')
x = df[['x1', 'x2']].values
y = df['label'].values
# Fit the model, ie. train it
print(f"Model initialized, starting to train with {model.n_iterations} iterations on {len(y)} data samples")
model.fit(x, y)
# predict
res = model.predict(x)
print(f"Features (x) : {x}")
print(f"Truths (y): {y}")
print(f"Predictions : {res}")