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 :)

viernes, 19 de julio de 2024

Corrección del Algoritmo Perceptrón de la web Machine Learning from Scratch

 Revisaba el algoritmo percentrón, como se explica en los tutoriales que hay por internet, es un algoritmo que recibe una entrada binaria (1 o 0), que suele ser un vector, donde cada elemento del vector está asociado a un peso. Además cada elemento del vector tiene asociado una etiqueta binaria. Lo que se desea con el algoritmo perceptrón es hallar parámetros (también llamados "pesos") que predigan correctamente la etiqueta si se ingresa un nuevo vector.

El perceptrón sólo actualiza sus pesos si predice mal una etiqueta. No siempre es necesario un condicional (un if... else...), si una etiqueta ha sido predicha correctamente, el error será cero y los pesos mantendrán su valor. Esta idea se usa en How To Implement The Perceptron Algorithm From Scratch In Python - MachineLearningMastery.com

El algoritmo implementado en esta web https://dafriedman97.github.io/mlbook/content/c3/s2/perceptron.html es uno de los más simples que he encontrado, pero hay un error. Teóricamente el algoritmo Perceptrón debería aproximarse bastante a una clasificación correcta, pero casi nunca del 100%. En esta web siempre resulta que el algoritmo no converge.

Hallé dos errores. una es en la función sign. No es necesario que devuelva {-1, +1}. Las etiquetas ya están clasificadas como {0, 1} así que las predicciones del perceptrón pueden dejarse con esos valores. Al cambiar la función sign también tuve que cambiar estas líneas:

 En mi caso basta evaluar cada elemento del vector de etiquetas:

 

El otro error es al evaluar si el algoritmo converge. Pide una exactitud del 100% para la convergencia:

Esto casi nunca se consigue. No eliminé estas líneas porque en casos muy raros sí puede cumplirse que las etiquetas predichas y las reales sean todas iguales.  

El código corregido es:

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 main():
    # import data
    cancer = datasets.load_breast_cancer()
    X = cancer['data']
    y = cancer['target']
    perceptron = Perceptron()
    perceptron.fit(X, y, n_iter = 1e2, lr = 0.01)
      
if __name__ == "__main__":
    main() 


El resultado es: