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 :)
Mostrando entradas con la etiqueta AI. Mostrar todas las entradas
Mostrando entradas con la etiqueta AI. Mostrar todas las entradas

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.

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.

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:



lunes, 26 de febrero de 2024

La diferencia entre un ejercicio de regresión y uno de clasificación con Tensorflow

 Estaba revisando estos dos códigos:
The Hello World of Deep Learning
Beyond Hello World: A computer Vision Example


El tutorial no lo dice, al menos no con la claridad que yo desearía, pero el primero es un problema de regresión y el otro es un problema de clasificación.

En el caso de la regresión, Tensorflow predice un valor en función a una entrada. En el caso de un problema de clasificación, Tensorflow devuelve el índice con mayor probabilidad de corresponder a la etiqueta (o clasificación) de una entrada.

Ambos códigos son casi idénticos, voy a copiar debajo los que tengo en mi máquina y con los que he estado jugando:

Éste es el de regresión:

import tensorflow as tf
import numpy as np
from tensorflow import keras

# Build a simple Sequential model
model = tf.keras.Sequential([keras.layers.Dense(units=1, input_shape=[1])])
# Compile the model
model.compile(optimizer='sgd', loss='mean_squared_error')
 
# Declare model inputs and outputs for training
xs = np.array([-1.0,  0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)
 
# Train the model
model.fit(xs, ys, epochs=100)

# Make a prediction
print(model.predict([10.0]))
 

Éste es el código del problema de clasificación:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
 
 
# Load the training and test split of the Fashion MNIST dataset
(training_images, training_labels), (test_images, test_labels) = fmnist.load_data()

print(len(training_images))
print(len(test_images))
print(training_labels[0:10])
 

# You can put between 0 to 59999 here
index = 562

# Set number of characters per row when printing
np.set_printoptions(linewidth=128)

# Print the label and image
print(f'LABEL: {training_labels[index]}')
print(f'\nIMAGE PIXEL ARRAY:\n {training_images[index]}')

# Visualize the image
plt.imshow(training_images[index])

# Normalize the pixel values of the train and test images
training_images  = training_images / 255.0
test_images = test_images / 255.0
 
# Build the classification model
model = tf.keras.models.Sequential([tf.keras.layers.Flatten(),
                                    tf.keras.layers.Dense(256, activation=tf.nn.relu),
                                    tf.keras.layers.Dense(10, activation=tf.nn.softmax)])
 

model.compile(optimizer = tf.optimizers.Adam(),
              loss = 'sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(training_images, training_labels, epochs=15)
 
# Evaluate the model on unseen data
model.evaluate(test_images, test_labels)


Aparte de las diferencias al construir la red neuronal o al predecir o evaluar un valor, o la data que se está usando, la diferencia principal es la función de pérdida o Loss Function, ésta es la que define si la red neuronal que estamos construyendo se usará para clasificar o predecir. Durante el entrenamiento de la red, esta función mide cuánto se están acercando los valores calculador por la red a los valores verdaderos (usualmente usando el cuadrado de la diferencia entre ambos valores, pero hay funciones más complejas), esta información se usa en el siguiente bucle para ajustar los parámetros de la red y disminuir el valor de la función de pérdida. La red neuronal hace estos ajustes usando otra función, la de optimización, usualmente la del gradiente descendente. En el caso de las funciones cóncavas, cuando su gradiente se hace cero, se ha alcanzado el mínimo. Lo que hace la función de optimización es minimizar a la de pérdida usando este principio.

Una lista de distintas funciones de pérdida está en este enlace.