Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

jueves, 22 de agosto de 2024

Probando el Algoritmo Perceptron de la web Machine Learning from Scratch

Continuación de https://programacionamartillazos.blogspot.com/2024/07/correccion-del-algoritmo-perceptron-de.html

Ahora toca probar los algoritmos. Para esto hice una pequeña modificación en el código de la web Machine Learning from Scratch y al que yo modifiqué:

Código de Machine Learning from Scratch:

from tkinter import Y
import numpy as np
np.set_printoptions(suppress=True)
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import datasets

class Perceptron:
    
    def sign(self, a):
        return (-1)**(a < 0)

    def to_binary(self, y):
            return y > 0

    def standard_scaler(self, X):
        mean = X.mean(0)
        sd = X.std(0)
        return (X - mean)/sd

    def fit(self, X, y, n_iter = 10**3, lr = 0.001, add_intercept = True, standardize = True):
        
        # Add Info #
        if standardize:
            X = self.standard_scaler(X)
        if add_intercept:
            ones = np.ones(len(X)).reshape(-1, 1)
        self.X = X
        self.N, self.D = self.X.shape
        self.y = y
        self.n_iter = n_iter
        self.lr = lr
        self.converged = False
        
        # Fit #
        beta = np.random.randn(self.D)/5
        for i in range(int(self.n_iter)):
            
            # Form predictions
            yhat = self.to_binary(self.sign(np.dot(self.X, beta)))
            
            # Check for convergence
            if np.all(yhat == self.sign(self.y)):
                self.converged = True
                self.iterations_until_convergence = i
                break
                
            # Otherwise, adjust
            for n in range(self.N):
                yhat_n = self.sign(np.dot(beta, self.X[n]))
                if (self.y[n]*yhat_n == -1):
                    beta += self.lr * self.y[n]*self.X[n]

        # Return Values #
        self.beta = beta
        self.yhat = self.to_binary(self.sign(np.dot(self.X, self.beta)))
        
        print(self.y[:50])
        print(self.yhat[:50])
        
        accuracy_true = self.y == self.yhat
        self.accuracy = np.count_nonzero(accuracy_true)/self.N * 100
        
        print("accuracy: ", self.accuracy, "%")
        
    def predict(self, X, y, N):
        accuracy_true = 0
        for n in range(N):
            yhat_n = self.to_binary(self.sign(np.dot(self.beta, X[n])))
        
            if (y[n] == yhat_n):
                accuracy_true += 1
                
        accuracy_true = accuracy_true/N * 100
        print("accuracy: ", accuracy_true, "%")
        
    
def main():
    # import data
    cancer = datasets.load_breast_cancer()
    X = cancer['data']
    y = cancer['target']
    perceptron = Perceptron()
    perceptron.fit(X[:-10], y[:-10], n_iter = 2e2, lr = 0.01)
    yy= np.atleast_1d(y[-10:])
    perceptron.predict(X[-10:], yy,  yy.size)
    
      
if __name__ == "__main__":
    main()


Código que modifiqué:

from tkinter import Y
import numpy as np
np.set_printoptions(suppress=True)
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import datasets

class Perceptron:
    
    def sign(self, a):
        if a > 0:
            return 1
        else:
            return 0
    

    def standard_scaler(self, X):
        mean = X.mean(0)
        sd = X.std(0)
        return (X - mean)/sd

    def fit(self, X, y, n_iter = 10**3, lr = 0.001, add_intercept = True, standardize = True):
        
        # Add Info #
        if standardize:
            X = self.standard_scaler(X)
        if add_intercept:
            ones = np.ones(len(X)).reshape(-1, 1)
            
        self.X = X
        self.N, self.D = self.X.shape
        self.y = y
        self.n_iter = n_iter
        self.lr = lr
        self.converged = False
        self.accuracy = 0

        vsign = np.vectorize(self.sign)
        
        # Fit #
        beta = np.random.randn(self.D)/5
        
        for i in range(int(self.n_iter)):
            # Form predictions
            
            yhat = np.dot(self.X, beta)
            yhat = vsign(yhat)

            # Check for convergence
            if np.all(yhat == self.y):
                self.converged = True
                self.iterations_until_convergence = i
                break
                
            # Otherwise, adjust
            for n in range(self.N):
                yhat_n = self.sign(np.dot(beta, self.X[n]))
        
                if (self.y[n] != yhat_n):
                    beta += self.lr * self.y[n]*self.X[n]

        # Return Values #
        self.beta = beta
        self.yhat = yhat
        
        print(self.y[:50])
        print(self.yhat[:50])
        
        accuracy_true = self.y == self.yhat
        self.accuracy = np.count_nonzero(accuracy_true)/self.N * 100
        
        print("accuracy: ", self.accuracy, "%")
        
    def predict(self, X, y, N):
        accuracy_true = 0
        for n in range(N):
            yhat_n = self.sign(np.dot(self.beta, X[n]))
        
            if (y[n] == yhat_n):
                accuracy_true += 1
                
        accuracy_true = accuracy_true/N * 100
        print("accuracy: ", accuracy_true, "%")
        
    
def main():
    # import data
    cancer = datasets.load_breast_cancer()
    X = cancer['data']
    y = cancer['target']
    perceptron = Perceptron()
    perceptron.fit(X[:-10], y[:-10], n_iter = 2e2, lr = 0.01)
    yy= np.atleast_1d(y[-10:])
    perceptron.predict(X[-10:], yy,  yy.size)
    
      
if __name__ == "__main__":
    main()



El cálculo de la exactitud se hace en dos momentos, con la data de entrenamiento (dentro de la función fit), y con la data de validación (también de prueba en este caso) dentro de la función predict.

Dentro de la función main, lo que se hace es partir la data en dos pedazos: la data de entrenamiento que no incluye los 10 últimos elementos que se pasan a la función fit, y luego los diez últimos elementos que serán la data de evaluación o prueba, que se pasarán como parámetro a la función predict.

En el algoritmo perceptrón el único parámetro que se debe optimizar es beta (es un vector de valores en realidad), el cual luego se usará en la predicción. Los resultados son:

Código de Machine Learning from Scratch:


 

 

 

 

 

 

Código que modifiqué:


 

 

 

 

El primer valor "accuracy" es el calculado para la data de entrenamiento, el segundo valor es el resultado con la data de testeo.

Ahora otra prueba pero con menos data de entrenamiento, para esto se cambia en la función main donde dice 10 por cualquier otro valor (debe ser el mismo). Para este ejemplo, voy a coger 100 elementos para que sean los de testeo:


 

 

 

 

 

El resultado es:

Código de Machine Learning from Scratch:


Código Modificado:


Ambos códigos son equivalentes, la diferencia entre ambos es menor al 1%, siendo el código original el que calcula el parámetro beta un poquito mejor. 

Por otro lado, esto demuestra que el resultado depende de la cantidad de data que tengamos para entrenar el algoritmo. En este ejemplo es muy poca data. Una forma de compensarlo es aumentando el número de iteraciones; pero poner demasiadas, con apenas unos pocos cientos de elementos, se corre el riesgo de "overfitting": es decir sobreentrenar nuestro algoritmo de forma que sólo tenga la precisión necesaria con la data de entrenamiento.