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 forma correcta se deduce del código fuente en la web.
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.