Last active
October 13, 2021 02:19
-
-
Save mpds/1abec0ba2debfd224b2b225c06701f70 to your computer and use it in GitHub Desktop.
Item 1 - Perceptron
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
def generate_dataset(N: int, p1: list, p2: list, seed: int = 42): | |
"""Generate a random dataset with size N from p1 and p2.""" | |
p1_ = np.tile(p1, reps=int(N // 2)).reshape(N // 2, 2) | |
p2_ = np.tile(p2, reps=int(N // 2)).reshape(N // 2, 2) | |
rng = np.random.default_rng(seed) # random number generator | |
X = np.concatenate((p1_, p2_)) + rng.standard_normal((N, 2)) # examples | |
Y = np.concatenate((np.tile(+1, p1_.shape[0]), np.tile(-1, p2_.shape[0]))) # labels | |
return (X, Y) | |
def split_dataset(dataset, train_split_percentage: float, seed: int = 42): | |
"""Shuffle and split dataset.""" | |
X, Y = dataset | |
rng = np.random.default_rng(seed) # random number generator | |
idx = rng.permutation(X.shape[0]) | |
x, y = X[idx], Y[idx] | |
n_train = int(X.shape[0] * train_split_percentage) | |
x_train, y_train = x[:n_train], y[:n_train] | |
x_test, y_test = x[n_train:], y[n_train:] | |
return (x_train, y_train), (x_test, y_test) | |
class Perceptron: | |
def __init__(self, lr: float = 1): | |
self.weights = np.array([0.0, 0.0]) | |
self.bias = 0.0 | |
self.lr = lr # learning rate | |
def activation(self, z: np.ndarray): | |
"""Implement the signal function. | |
Returns 1 if x > 0, -1 otherwise. | |
""" | |
return np.where(z > 0, 1, -1) | |
def forward(self, x: np.ndarray): | |
"""Perform a forward pass.""" | |
z = np.dot(x, self.weights) + self.bias # induced field | |
ypred = self.activation(z) # activation function | |
return ypred | |
def loss(self, y: float, ypred: float): | |
"""Calculate the loss.""" | |
return y - ypred | |
def step(self, x: np.ndarray, y: float): | |
"""Perform a forward pass and update weights and biases. | |
x : np.ndarray | |
An example of the dataset. | |
y : float | |
Ground truth label. | |
""" | |
ypred = self.forward(x) # forward step calculation | |
# update weights and bias | |
delta = self.lr * self.loss(y, ypred) | |
self.weights = self.weights + (delta * x) | |
self.bias = self.bias + delta | |
if __name__ == "__main__": | |
dataset = generate_dataset(100, p1=[-1, 1], p2=[1, -3]) | |
train, test = split_dataset(dataset, train_split_percentage=0.7) | |
X, Y = 0, 1 # indices that correspond to examples/labels respectively | |
# hyperparams | |
epoch, max_epochs = 1, 1000 | |
loss = 1 | |
tol = 1e-8 | |
model = Perceptron() | |
# training loop | |
while loss > tol and epoch < max_epochs: | |
for x, y in zip( | |
train[X], train[Y] | |
): # loop through dataset, one ex. per iteration | |
model.step(x, y) | |
preds = model.forward(train[X]) # predictions over training set | |
loss = np.mean(np.abs(model.loss(train[Y], preds))) # average abs loss | |
epoch += 1 | |
print("Épocas:", epoch) | |
print("Valor dos pesos:", model.weights) | |
print("Valor do bias:", model.bias) | |
acc = (preds == train[Y]).sum() / train[Y].shape[0] * 100 | |
print(f"Acurácia no treinamento: {acc:.2f}%") | |
yhat = model.forward(test[X]) | |
print(f"Acurácia no teste: {(yhat == test[Y]).sum() / yhat.shape[0] * 100:.2f}%") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment