Full Blog TOC

Full Blog Table Of Content with Keywords Available HERE

Sunday, August 4, 2024

Implement Perception Using NumPy and Using PyTorch

Image from: https://datascientest.com/en/perceptron-definition-and-use-cases

 


In this post we include a perceptron implementation using NumPy and using PyTorch.


The Main Code

We use input data from this location. The input structure is as follows:


The main code does the following:
  • Reads the data
  • Split to training data and testing data
  • Creates and trains a single perceptron
  • Validates the training results vs. the testing data


def prepare_input(x, y):
samples_number = y.shape[0]
indices = np.arange(samples_number)
random_state = np.random.RandomState(RANDOM_SEED)
random_state.shuffle(indices)
x, y = x[indices], y[indices]
train_factor = 0.7
train_size = int(train_factor * samples_number)
x_train, x_test = x[:train_size], x[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
print('train shape', x_train.shape, y_train.shape)
print('test shape', x_test.shape, y_test.shape)
return x_train, y_train, x_test, y_test


def normalize_input(mean, std, data):
return (data - mean) / std


def plot_xy_by_classes(samples, labels, file_name, added_line=None):
class1_indices = labels == 0
class2_indices = labels == 1
class1_x = samples[class1_indices, 0]
class1_y = samples[class1_indices, 1]
class2_x = samples[class2_indices, 0]
class2_y = samples[class2_indices, 1]
plt.clf()

if added_line:
line_x, line_y = added_line
plt.plot(line_x, line_y)
plt.scatter(class1_x, class1_y, label='class1', marker='o')
plt.scatter(class2_x, class2_y, label='class2', marker='s')
plt.legend()
plt.savefig(f'output/{file_name}.pdf')


def main():
x, y = read_input()
plot_xy_by_classes(x, y, 'original')
x_train, y_train, x_test, y_test = prepare_input(x, y)
mean = x_train.mean(axis=0)
std = x_train.std(axis=0)
x_train = normalize_input(mean, std, x_train)
x_test = normalize_input(mean, std, x_test)
plot_xy_by_classes(x_train, y_train, 'train')
plot_xy_by_classes(x_test, y_test, 'test')

perceptron = Perceptron(2)
perceptron.train(x_train, y_train, 10)
perceptron.describe()
accuracy = perceptron.evaluate(x_test, y_test)
print('accuracy', accuracy)
plot_xy_by_classes(x_train, y_train, 'train_with_model', added_line=perceptron.model_line())
plot_xy_by_classes(x_test, y_test, 'test_with_model', added_line=perceptron.model_line())


main()

Perceptron Using NumPy


NumPy requires the following dependecies:

pip3 install numpy 
pip3 install mathplotlib


And the Perceptron code is:



class Perceptron:
def __init__(self, features_number):
self.features_number = features_number
self.weights_vector = np.zeros((features_number, 1), dtype=np.float32)
self.bias = np.zeros(1, dtype=np.float32)

def forward(self, x):
z = x @ self.weights_vector + self.bias
predictions = np.where(z > 0., 1, 0)
return predictions

def backward(self, x, y):
predictions = self.forward(x)
errors = y - predictions
return errors

def train(self, x, y, epochs_number):
for epoch_index in range(epochs_number):
for sample_index in range(y.shape[0]):
sample_x = x[sample_index]
sample_label = y[sample_index]
errors = self.backward(sample_x, sample_label)
correction = errors * sample_x
correction = correction.reshape(self.features_number, 1)
self.weights_vector += correction
self.bias += errors

def evaluate(self, x, y):
predictions = self.forward(x).flatten()
correct = predictions == y
accuracy = np.sum(correct) / y.shape[0]
return accuracy

def describe(self):
print('weights', self.weights_vector.flatten())
print('bias', self.bias)

def model_line(self):
x1 = -2
x2 = 2
y1 = -(self.bias + x1 * self.weights_vector[0]) / self.weights_vector[1]
y2 = -(self.bias + x2 * self.weights_vector[0]) / self.weights_vector[1]
line_x = [x1, x2]
line_y = [y1, y2]
return line_x, line_y

Perceptron Using PyTorch


PyTorch requires the following dependencies:

pip3 install numpy
pip3 install mathplotlib
pip3 install torch

And the Perceptron code is:

class Perceptron:
def __init__(self, features_number):
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
self.features_number = features_number
self.weights_vector = torch.zeros(features_number, 1, dtype=torch.float32, device=self.device)
self.bias = torch.zeros(1, dtype=torch.float32, device=self.device)
self.ones = torch.ones(1)
self.zeros = torch.zeros(1)

def forward(self, x):
z = x @ self.weights_vector + self.bias
predictions = torch.where(z > 0., self.ones, self.zeros)
return predictions

def backward(self, x, y):
predictions = self.forward(x)
errors = y - predictions
return errors

def train(self, x, y, epochs_number):
x = torch.tensor(x, dtype=torch.float32, device=self.device)
y = torch.tensor(y, dtype=torch.float32, device=self.device)
for epoch_index in range(epochs_number):
for sample_index in range(y.shape[0]):
sample_x = x[sample_index]
sample_label = y[sample_index]
errors = self.backward(sample_x, sample_label)
correction = errors * sample_x
correction = correction.reshape(self.features_number, 1)
self.weights_vector += correction
self.bias += errors

def evaluate(self, x, y):
x = torch.tensor(x, dtype=torch.float32, device=self.device)
y = torch.tensor(y, dtype=torch.float32, device=self.device)
predictions = self.forward(x).flatten()
correct = predictions == y
accuracy = torch.sum(correct).float() / y.shape[0]
return accuracy

def describe(self):
print('weights', self.weights_vector.flatten())
print('bias', self.bias)

def model_line(self):
x1 = -2
x2 = 2
y1 = -(self.bias + x1 * self.weights_vector[0]) / self.weights_vector[1]
y2 = -(self.bias + x2 * self.weights_vector[0]) / self.weights_vector[1]
line_x = [x1, x2]
line_y = [y1, y2]
return line_x, line_y


Run Results

Plotting the reuslts, we can see the model in the training data:




And the model over the test data:






No comments:

Post a Comment