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, 10 de enero de 2025

Implementando el algoritmo ADALINE

La idea fue convertir el algoritmo perceptron de esta estrada en el algoritmo ADALINE. La diferencia entre ambos algoritmos está explicada en https://pabloinsente.github.io/the-adaline y es casi mínima. En esta web fue donde conocí inicialmente el algoritmo ADALINE y donde el algoritmo perceptron está mejor explicado. La conversión de uno a otro parecía simple, y en realidad lo fue luego de superar las siguientes dificultades:

Primera dificultad: esta imagen es errónea:

La correcta es:
La forma correcta se deduce del código fuente en la web.

Segunda dificultad:
No especifica cómo debe actualizarse el parámetro bias b de la ecuación del error


e(w,x)=(y(b+wx))2

Mirando los códigos fuente en https://pabloinsente.github.io/the-adaline y en https://medium.com/@mr.sk12112002/understanding-adaline-da79ab8bbc5a  se ve que el bias es la suma de las derivadas de la función del error multiplicada por el ratio de aprendizaje, es decir:

bias +=sum(y(b+wx)) * lr

Tercera dificultad: los códigos de las webs que revisé son para un input X unidimensional o bidimensional con sólo dos valores por muestra, el mió tiene 30 valores por cada muestra.. Revisando la teoría y el código que modifiqué para el perceptron noté dónde debía hacer el cambio. 

El código resultante 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 = 0
        w = np.random.randn(self.D)/5
        yhat = []
        
        for i in range(int(self.n_iter)):
            yhat = np.dot(self.X, w) + 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(w, self.X[n]) + beta)
                delta = 2 * self.lr * (self.y[n] - yhat_n)
                w += delta * self.X[n]
                beta += np.cumsum(delta)

        # Return Values #
        self.beta = beta
        self.w = w
        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.w, self.X[n]) + self.beta)
        
            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[:-5], y[:-5], n_iter = 1.5e2, lr = 0.01)
    yy= np.atleast_1d(y[-5:])
    perceptron.predict(X[-5:], yy,  yy.size)
    
      
if __name__ == "__main__":
    main()


Al ejecutarlo resulta:


La exactitud es mejor que con el algoritmo perceptron tal y como indica la teoría, pues el algoritmo ADALINE ajusta sus parámetros en cada iteración, mientras que el perceptron sólo los ajusta cuando obtiene un resultado erróneo.