Seguramente en algún momento de sus vidas escucharon hablar de Redes Neuronales Artificiales. Éstas son básicamente una serie de algoritmos que tienen el objetivo de imitar la forma en que trabaja el cerebro humano. Nuestro cerebro está compuesto por millones de neuronas que por medio de estímulos eléctricos hacen que las diferentes neuronas se conecten mediante un proceso denominado Sinapsis.
Tanto una Neurona artificial como una biológica tendrán entradas y salidas que se conectan hacia otras neuronas, que lo denominamos Nodos. Los nodos se clasifican en Nodos de entrada, nodos ocultos y nodos de salida.

Mas adelante, en futuros Posts nos centraremos más en detalle en el funcionamiento de una Red Neuronal. Hoy, vamos a concentrarnos en la unidad básica, denominado Perceptrón.
Perceptrón
El concepto de Perceptrón ya existe desde 1943, fue creado por Frank Rosenblatt a partir del modelo neuronal de Warren McCulloch y Walter Pitts.
Es una red neuronal de una sola capa, también conocido como Single layer neural network. Básicamente es un clasificador lineal binario, esto quiere decir que nos permite clasificar al conjunto de entradas únicamente dentro de 2 posibilidades.

La neurona recibe un conjunto de entradas (x=x_1...x_m), a cada una se les asocia un peso (w=w_1...w_m) que les dará un nivel de importancia a cada entrada. Luego podemos definir z que se igualará a la sumatoria ponderada de cada entrada por su correspondiente peso:
z = w_{1}x_{1} + \ldots + w_{m}x_{m}
w = \begin{bmatrix} w_{1} \\ \vdots \\ w_{m} \end{bmatrix} x = \begin{bmatrix} x_{1} \\ \vdots \\ x_{m} \end{bmatrix}
Para determinar una salida, se deberá someter a la suma ponderada de las entradas*pesos, es decir, al valor de z a una función de activación. Dicha función pueden ser: Sigmoide, Tangente Hiperbólica, ReLU, entre otras. Para este ejemplo tomaremos la función step o también llamado escalón, al que denotaremos con ϕ(z).
La función step nos dice que el Perceptrón se encuentra activo si su valor es mayor o igual al valor de un umbral predefinido (ϑ), o inactivo para cualquier otro caso. Entonces:
ϕ(z) = \left \{ \begin{matrix} 1 & Si \ z ≥ ϑ \\ -1 & Cualquier \ otro \ caso \end{matrix}\right.
¿Cómo aprende un Perceptrón?
El algoritmo que utiliza un Perceptrón para aprender es bastante fácil, ya que únicamente deberá ir actualizando el valor de los pesos.
Los pesos iniciales generalmente se generan por medio de funciones matemáticas o librerías (por ejemplo en Python) que nos brindan valores aleatorios, o simplemente se pueden inicializar todos los pesos en 0(cero).
Para realizar la actualización de los pesos, se deberá aplicar la siguiente ecuación:
w_n = w_i + α * (y_d - y)*x_i
Donde:
w_n: Es el valor del nuevo Peso. (Valor actualizado)
w_i: El valor del peso anterior.
α: Valor de la taza de aprendizaje.
y_d: Valor de la salida deseada.
y: Salida del Perceptrón.
x_i: Valor de la entrada correspondiente.
Un Perceptrón en Python
Para el ejemplo vamos a implementar un algoritmo basado en el libro Python Machine Learning de Sebastian Raschka.
Antes de empezar refresquemos un concepto clave en lo que respecta a un perceptrón, y vamos a referirnos a que solamente es capaz de trabajar para aquellos casos donde las muestras son linealmente separables, lo que significa que si trazamos una línea, nos queda de un lado un único tipo de muestras y del otro uno diferente. Para aquellos casos que no son linealmente separables, se utilizar redes neuronales mas complejas.

Para éste caso utilizaremos uno de los dataset mas comúnmente utilizados en Machine Learning que es IRIS. En donde intentaremos separar mediante un Perceptrón las flores Iris-Setosa de las Iris-Versicolor.
El dataset está compuesto por 5 columnas, en donde, las primeras 4 corresponden a las características, es decir, a los datos de entrada, y la ultima a la salida deseada (Clase de Iris).
Datos de entrada:
Largo del sépalo
Ancho del sépalo
Largo del pétalo
Ancho del pétalo

Si graficamos la distribución de los datos de Iris-Setosa e Iris-Versicolor, podemos observar que son Linealmente separables:

Código en Python
Esta parte es la que comienza a ponerse buena 🙂
Para comenzar a desarrollar nuestro Perceptron, vamos a utilizar las librerias de Python numpy: para las operaciones matemáticas, pandas: para leer el dataset desde el disco duro, y matplotlib: para graficar la salida.
Implementaremos una clase Perceptron que contendrá los siguientes métodos:
__init__: Es donde se va a definir cuál será la tasa de aprendizaje y el número de iteraciones que se realizará al dataset (También conocido como épocas).
entrenar: Recibirá las entradas y la salida deseada. Será el encargado de entrenar a nuestro dataset ¿Recuerdan cómo se entrenaba un Perceptrón? Simplemente ir ajustando los pesos.
predecir: Es la función de activación step. Retornará 1 si el valor de z es mayor o igual a 0, y retornará -1 en caso de que no lo sea.
net_entradas: Corresponde al valor de z. Es decir, el producto de las entradas (x) por los pesos (w).
import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap class Perceptron: def __init__(self, tasa, num_iter): self.tasa = tasa self.num_iter = num_iter def entrenar(self, entradas, salida_deseada): self.pesos = np.zeros(1 + entradas.shape[1]) self.errores_vec = [] for i in range(self.num_iter): errores = 0 for xi, target in zip(entradas, salida_deseada): nuevo_peso = self.tasa * (target - self.predecir(xi)) self.pesos[1:] += nuevo_peso * xi self.pesos[0] += nuevo_peso errores += int(nuevo_peso != 0.0) self. errores_vec.append(errores) return self def predecir(self, entradas): func = np.where(self.net_entradas(entradas) >= 0.0, 1, -1) return func def net_entradas(self, entradas): func_activ = np.dot(entradas, self.pesos[1:]) + self.pesos[0] return func_activ
Ahora que ya se encuentra codificado la clase con los métodos nos queda crear el objeto, cargarlo con el dataset y luego utilizar sus métodos para lograr a un Perceptrón inteligente 🙂
#Cargamos los datos df = pd.read_csv("./data/iris.data", header = None) #Extraemos entradas = df.iloc[0:100, [0, 2]].values y = df.iloc[0:100, 4].values y = np.where( y == 'Iris-setosa', -1, 1 ) ##Iniciamos la clase Perceptron ptn = Perceptron(tasa = 0.1, num_iter = 10) ##Entrenamos al Perceptron ptn.entrenar(entradas, y)
Genial 🙂 Ya tenemos a nuestro Perceptrón creado y entrenado para cumplir su propósito de separar los Iris-Setosas de los Iris-Versicolor.
Ahora vamos a graficar para ver si el Perceptrón fue capaz de separar linealmente. Para ello vamos a graficar el modelo.
Vamos a definir un nuevo método de plot, con el siguiente código:
def plot_decision_regions(self, X, y, classifier, resolution=0.02): # setup marker generator and color map markers = ('s', 'x', 'o', '^', 'v') colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan') cmap = ListedColormap(colors[:len(np.unique(y))]) # plot the decision surface x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1 x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution)) Z = classifier.predecir(np.array([xx1.ravel(), xx2.ravel()]).T) Z = Z.reshape(xx1.shape) plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap) plt.xlim(xx1.min(), xx1.max()) plt.ylim(xx2.min(), xx2.max()) # plot class samples for idx, cl in enumerate(np.unique(y)): plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=cmap(idx), edgecolor='black', marker=markers[idx], label=cl)
ptn.plot_decision_regions(entradas, y, classifier=ptn) plt.xlabel('Longitud del Sépalo [cm]') plt.ylabel('Longitud del Pétalo [cm]') plt.legend(loc='upper left') plt.tight_layout() plt.show()
Ahora llegó el momento de ver si realmente el Perceptrón fue capaz de ser entrenado exitosamente. Y taráaaaaaaaaaaaaaaaaaaaaaaaaaaaaan:

Efectivamente, hemos logrado separar a Iris-setosa de Iris-versicolor 🙂 🙂
Espero haya sido claro en la explicación. Cualquier cosa, me dejan los comentarios y trataré de responderlos. Recuerden que tanto yo como ustedes, estoy comenzando a aprender. Sigamos por ese camino de crecimiento personal 🙂