<jupyter_start><jupyter_text>**Algoritmo de Clasificación: Perceptrón Paralelo**<jupyter_code>import pandas as pd
import numpy as np
import math
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler<jupyter_output><empty_output><jupyter_text>Extracción de datos<jupyter_code>url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"
X2 = pd.read_csv(url, header = None)
y2 = X2.iloc[:,0]
X2.drop(X2.columns[0], axis='columns', inplace=True)
X2.columns = range(X2.shape[1])
print(X2)
print(y2)<jupyter_output>0     1     2     3    4     5   ...    7     8      9     10    11    12
0    14.23  1.71  2.43  15.6  127  2.80  ...  0.28  2.29   5.64  1.04  3.92  1065
1    13.20  1.78  2.14  11.2  100  2.65  ...  0.26  1.28   4.38  1.05  3.40  1050
2    13.16  2.36  2.67  18.6  101  2.80  ...  0.30  2.81   5.68  1.03  3.17  1185
3    14.37  1.95  2.50  16.8  113  3.85  ...  0.24  2.18   7.80  0.86  3.45  1480
4    13.24  2.59  2.87  21.0  118  2.80  ...  0.39  1.82   4.32  1.04  2.93   735
..     ...   ...   ...   ...  ...   ...  ...   ...   ...    ...   ...   ...   ...
173  13.71  5.65  2.45  20.5   95  1.68  ...  0.52  1.06   7.70  0.64  1.74   740
174  13.40  3.91  2.48  23.0  102  1.80  ...  0.43  1.41   7.30  0.70  1.56   750
175  13.27  4.28  2.26  20.0  120  1.59  ...  0.43  1.35  10.20  0.59  1.56   835
176  13.17  2.59  2.37  20.0  120  1.65  ...  0.53  1.46   9.30  0.60  1.62   840
177  14.13  4.10  2.74  24.5   96  2.05  ...  0.56  1.35   9.20  0.61  1.60   560

[178 rows x 13 [...]<jupyter_text>Debemos quitar los vectores de características que pertenecen a la clase 3. También considerar las clases 1 y 2 como las clases -1 y 1, respectivamente.<jupyter_code>X = pd.DataFrame()
y = []

i=0

while y2[i] != 3:
  X = X.append(X2.iloc[i])
  if y2[i] == 1:
    y.append(-1) # a la clase 1 la consideramos como la clase -1
  else:
    y.append(1) # a la clase 2 la consideramos como la clase 1
  i += 1
  
y = pd.Series(y)

print(X)
print(y)<jupyter_output>0     1     2     3      4     5   ...    7     8     9     10    11      12
0    14.23  1.71  2.43  15.6  127.0  2.80  ...  0.28  2.29  5.64  1.04  3.92  1065.0
1    13.20  1.78  2.14  11.2  100.0  2.65  ...  0.26  1.28  4.38  1.05  3.40  1050.0
2    13.16  2.36  2.67  18.6  101.0  2.80  ...  0.30  2.81  5.68  1.03  3.17  1185.0
3    14.37  1.95  2.50  16.8  113.0  3.85  ...  0.24  2.18  7.80  0.86  3.45  1480.0
4    13.24  2.59  2.87  21.0  118.0  2.80  ...  0.39  1.82  4.32  1.04  2.93   735.0
..     ...   ...   ...   ...    ...   ...  ...   ...   ...   ...   ...   ...     ...
125  12.07  2.16  2.17  21.0   85.0  2.60  ...  0.37  1.35  2.76  0.86  3.28   378.0
126  12.43  1.53  2.29  21.5   86.0  2.74  ...  0.39  1.77  3.94  0.69  2.84   352.0
127  11.79  2.13  2.78  28.5   92.0  2.13  ...  0.58  1.76  3.00  0.97  2.44   466.0
128  12.37  1.63  2.30  24.5   88.0  2.22  ...  0.40  1.90  2.12  0.89  2.78   342.0
129  12.04  4.30  2.38  22.0   80.0  2.10  ...  0.42  1.35  2.60 [...]<jupyter_text>Defina la proporción de datos para el conjunto de entrenamiento<jupyter_code>prop = float(input("Proporcion de datos para el conjunto de entrenamiento: p = ")) # proporcion del conjunto de entrenamiento
X_train_o, X_test_o, y_train, y_test = train_test_split(X, y, train_size=prop) #, random_state=0)<jupyter_output>Proporcion de datos para el conjunto de entrenamiento: p = 0.6<jupyter_text>Debemos escalar los vectores de características y extender los vectores (añadir el componente para el bias).<jupyter_code>scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train_o)
X_test = scaler.transform(X_test_o)

X_train = pd.DataFrame(X_train)
X_test = pd.DataFrame(X_test)

d = X_train.shape[1] # dimensión del vector de características

bias = [1 for i in range(X_train.shape[0])] 
X_train[d] = pd.Series(bias) # añadir componente de bias al conjunto de entrenamiento

bias = [1 for i in range(X_test.shape[0])]
X_test[d] = pd.Series(bias) # añadir componente de bias al conjunto de prueba

d = d+1 # dimensión del vector de características extendido
print(X_train)
print(y_train)
print(X_test)
print(y_test)<jupyter_output>0         1         2         3   ...        10        11        12  13
0   0.349462  0.020367  0.317204  0.264151  ...  0.356436  0.274678  0.336662   1
1   0.000000  0.126273  0.451613  0.685535  ...  1.000000  0.515021  0.092011   1
2   0.604839  0.152749  0.795699  0.308176  ...  0.386139  0.476395  0.429387   1
3   0.543011  0.236253  1.000000  0.905660  ...  0.425743  0.656652  0.393723   1
4   0.360215  0.150713  0.505376  0.874214  ...  0.188119  0.476395  0.045649   1
..       ...       ...       ...       ...  ...       ...       ...       ...  ..
73  0.360215  0.057026  0.301075  0.566038  ...  0.415842  0.776824  0.165478   1
74  0.857527  0.164969  0.505376  0.358491  ...  0.237624  0.703863  0.493581   1
75  0.680108  0.171079  0.591398  0.622642  ...  0.277228  0.583691  0.600571   1
76  0.338710  0.105906  0.333333  0.339623  ...  0.524752  0.459227  0.106990   1
77  0.712366  0.191446  0.537634  0.415094  ...  0.524752  0.515021  0.507846   1

[78 rows x 14 c[...]<jupyter_text>Distancia Minkowski de $x,y$ vectores en $\mathbb{R}^n$<jupyter_code>def MinkowskiDistance(x,y, p=2): 
  dist = 0
  n = len(x)

  for i in range(0,n):
    dist += abs(x[i]-y[i])**p

  dist = dist**(1/p)

  return dist<jupyter_output><empty_output><jupyter_text>Clase del perceptrón<jupyter_code>class perceptron:
  def __init__(self, d):
    self.d = d # d es la dimension de los vectores de caracteristicas extendidos
    self.alpha = np.random.uniform(-0.5, 0.5, d) # El vector de pesos se inicializa con valores aleatorios en [-0.5, 0.5]

  def decision_function(self, z):
    # z es el vector de caracteristicas extendido
    # funcion de decision f
    # f(z) = 1, si (alpha)(z) >= 0 (producto punto de alpha y z), y f(z) = -1 en otro caso 

    if np.dot(self.alpha, z)  >= 0:
      return 1
    else:
      return -1

  def normalize_alpha(self):
    # función para normalizar el vector de pesos alpha

    norm = MinkowskiDistance(self.alpha, np.zeros(self.d)) # la norma de alpha es la distancia euclidiana de alpha al vector 0

    self.alpha = (1/norm)*self.alpha<jupyter_output><empty_output><jupyter_text>Clase del perceptrón paralelo<jupyter_code>class parallel_perceptron:
  def __init__(self, d, n, margin, epsilon, mu):
    self.d = d # d es la dimension de los vectores de caracteristicas extendidos
    self.n = n # numero de perceptrones en la capa
    self.margin = margin # margen
    self.epsilon = epsilon # exactitud del algoritmo
    self.mu = mu # importancia de un margen claro

    self.capa = [perceptron(d) for i in range(n)]

  def squash_function(self, z):
    # z es el vector de caracteristicas extendido
    # funcion s(p) = +- 1 si signo(p) = +-1, donde p = f_1(z)+...+f_n(z) y f_i es la funcion de decision del perceptron i

    p = 0

    for i in range(self.n):
      p+= self.capa[i].decision_function(z)

    if p >= 0:
      return 1
    else:
      return -1

  def p_deltaRule(self, z, o, t):
    # z es el vector de caracteristicas extendido
    # o es la clase del vector z
    # Regla de actualización de los perceptrones en la época t

    eta = 1/(2*(t**(0.5)))

    o_h = self.squash_function(z) # prediccion de o

    for i in range(n):
    
      dot_prod = np.dot(self.capa[i].alpha, z)

      if o_h > o + self.epsilon and dot_prod >= 0:
        result = -z
      elif o_h < o-self.epsilon and dot_prod < 0:
        result = z
      elif o_h <= o+self.epsilon and 0 <= dot_prod < self.margin:
        result = self.mu * z
      elif o_h >= o-self.epsilon and -self.margin < dot_prod < 0: 
        result = -self.mu * z
      else:
        result = 0

      self.capa[i].alpha = self.capa[i].alpha + eta*result

      self.capa[i].normalize_alpha()

  def fit(self, X_train, y_train, num_epochs):
    # Función de entrenamiento del perceptrón paralelo usando la regla p-delta
    X_train = X_train.to_numpy()
    y_train = y_train.to_numpy()

    N = X_train.shape[0]

    # Por cada época t, entrenamos el perceptrón paralelo con una permutación aleatoria de los datos

    for t in range(1,num_epochs+1):
      ind = np.random.choice(N, N, replace=False) # Permutación aleatoria de los números 0, 1, ..., N-1

      # Regla p-delta para cada elemento del conjunto de entrenamiento

      for i in ind:
        self.p_deltaRule(X_train[i], y_train[i], t)

  def predict(self, X_test):
    X_test = X_test.to_numpy()
    N = X_test.shape[0]

    labels = []

    for i in range(N):
      labels.append(self.squash_function(X_test[i]))

    return pd.Series(labels)<jupyter_output><empty_output><jupyter_text>Función que reporta la matriz de confusión $M$, tal que $M[i][j]$ es la cantidad de patrones de la clase $i$ clasificados como clase $j$. También reporta el *accuracy*.<jupyter_code>def performance(labels_pred, labels_real, Labels):
  # Reporta la matriz de confusion y la exactitud
  # labels_pred es el etiquetado hecho por el clasificador, labels_real son las etiquetas reales, y Labels es el conjunto de etiquetas existentes

  labels_pred = labels_pred.to_numpy()
  labels_real = labels_real.to_numpy()

  Labels_dict = {} # diccionario auxiliar para asociar Label[i] con el índice i
  totalLabels = len(Labels)

  for i in range(totalLabels):
    Labels_dict[Labels[i]]=i

  M = np.zeros((totalLabels, totalLabels))
  n = len(labels_pred)

  for i in range(n):
    M[Labels_dict[int(labels_real[i])]][Labels_dict[int(labels_pred[i])]] += 1

  acc = 0

  for i in range(totalLabels):
    acc += M[i][i]

  acc = acc/n
  M = pd.DataFrame(M, columns= Labels, index = Labels)

  return M, acc<jupyter_output><empty_output><jupyter_text>Parámetros del perceptrón paralelo<jupyter_code>n = int(input("Numero de perceptrones: n = "))
margin = float(input("Margen (margin) = "))
epsilon = float(input("Exactitud (epsilon) = "))
mu = float(input("importancia del margen claro (mu) = "))
num_epochs = int(input("Numero de epocas = "))<jupyter_output>Numero de perceptrones: n = 7
Margen (margin) = 0.05
Exactitud (epsilon) = 0.05
importancia del margen claro (mu) = 1
Numero de epocas = 1000<jupyter_text>Clasificación del conjunto de entrenamiento. Impresión de las etiquetas predichas, matriz de confunsión y exactitud.<jupyter_code>classifier = parallel_perceptron(d, n, margin, epsilon, mu)
classifier.fit(X_train, y_train, num_epochs)
labels_train = classifier.predict(X_train)
M_train, acc_train = performance(labels_train, y_train, [-1, 1])
print(labels_train)
print(M_train)
print(acc_train)<jupyter_output>0     1
1     1
2    -1
3    -1
4     1
     ..
73    1
74   -1
75   -1
76    1
77   -1
Length: 78, dtype: int64
      -1     1
-1  36.0   0.0
 1   0.0  42.0
1.0<jupyter_text>Clasificación del conjunto de prueba. Impresión de las etiquetas predichas, matriz de confunsión y exactitud.<jupyter_code>labels_test = classifier.predict(X_test)
M_test, acc_test = performance(labels_test, y_test, [-1,1])
print(labels_test)
print(M_test)
print(acc_test)<jupyter_output>0    -1
1     1
2    -1
3    -1
4    -1
5     1
6     1
7     1
8    -1
9     1
10   -1
11   -1
12    1
13    1
14    1
15   -1
16   -1
17    1
18    1
19   -1
20   -1
21   -1
22    1
23   -1
24    1
25    1
26    1
27    1
28   -1
29   -1
30   -1
31   -1
32    1
33    1
34   -1
35    1
36    1
37   -1
38    1
39   -1
40    1
41    1
42    1
43   -1
44    1
45   -1
46   -1
47    1
48    1
49   -1
50    1
51    1
dtype: int64
      -1     1
-1  23.0   0.0
 1   1.0  28.0
0.9807692307692307
{'fraction_non_alphanumeric': 0.12454842594185446, 'fraction_numerical': 0.14467572681919835, 'mean_word_length': 2.245046050795423, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 48, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '617350', 'license': 'MIT', 'n_tokens_mistral': 5704, 'n_tokens_neox': 4948, 'n_words': 1352, 'path': 'Parallel_Perceptron_MiguelRuiz.ipynb', 'repo_name': 'miguelruor/IA_code', 'size': '11626'}