Vahid Mirjalili

Python Machine Learning


Скачать книгу

forma gratuita los contenidos adicionales del libro.

      Ahora, antes de saltar a la implementación en la sección siguiente, vamos a resumir cuanto acabamos de aprender en un simple diagrama que ilustra el concepto general del perceptrón:

      Este diagrama muestra cómo el perceptrón recibe las entradas de una muestra x y las combina con los pesos w para calcular la entrada de red. A continuación, la entrada de red pasa por la función de umbral, que genera una salida binaria -1 o +1; la etiqueta de clase predicha de la muestra. Durante la fase de aprendizaje, esta salida se utiliza para calcular el error de la predicción y actualizar los pesos.

      En la sección anterior, hemos aprendido cómo funciona la regla del perceptrón de Rosenblatt. Sigamos adelante. Vamos a implementarla en Python y a aplicarla al conjunto de datos Iris que presentamos en el Capítulo 1, Dar a los ordenadores el poder de aprender de los datos.

      Vamos a tomar un enfoque orientado a objetos para definir la interfaz del perceptrón como una clase de Python, lo cual nos permite iniciar nuevos objetos Perceptron que pueden aprender de los datos mediante un método fit y hacer predicciones mediante un método predict separado. Como norma, agregamos un guion bajo (_) a aquellos atributos que no han sido creados durante la inicialización del objeto sino mediante la llamada de otros métodos del objeto, por ejemplo, self.w_.

Si aún no estás familiarizado con las librerías científicas de Python o necesitas un repaso, puedes consultar estos recursos (en inglés):•NumPy: https://sebastianraschka.com/pdf/books/dlb/appendix_f_numpy-intro.pdf•pandas: https://pandas.pydata.org/pandas-docs/stable/10min.html•Matplotlib: http://matplotlib.org/users/beginner.html

      Esta es la implementación de un perceptrón:

      import numpy as np

      class Perceptron(object):

       """Perceptron classifier.

       Parameters

       ------------

       eta : float

       Learning rate (between 0.0 and 1.0)

       n_iter : int

       Passes over the training dataset.

       random_state : int

       Random number generator seed for random weight

       initialization.

       Attributes

       -----------

       w_ : 1d-array

       Weights after fitting.

       errors_ : list

       Number of misclassifications (updates) in each epoch.

       """

       def __init__(self, eta=0.01, n_iter=50, random_state=1):

       self.eta = eta

       self.n_iter = n_iter

       self.random_state = random_state

       def fit(self, X, y):

       """Fit training data.

       Parameters

       ----------

       X : {array-like}, shape = [n_samples, n_features]

       Training vectors, where n_samples is the number of

       samples and

       n_features is the number of features.

       y : array-like, shape = [n_samples]

       Target values.

       Returns

       -------

       self : object

       """

       rgen = np.random.RandomState(self.random_state)

       self.w_ = rgen.normal(loc=0.0, scale=0.01,

       size=1 + X.shape[1])

       self.errors_ = []

       for _ in range(self.n_iter):

       errors = 0

       for xi, target in zip(X, y):

       update = self.eta * (target - self.predict(xi))

       self.w_[1:] += update * xi

       self.w_[0] += update

       errors += int(update != 0.0)

       self.errors_.append(errors)

       return self

       def net_input(self, X):

       """Calculate net input"""

       return np.dot(X, self.w_[1:]) + self.w_[0]

       def predict(self, X):

       """Return class label after unit step"""

       return np.where(self.net_input(X) >= 0.0, 1, -1)

      Con esta implementación del perceptrón, ya podemos inicializar nuevos objetos Perceptron con un rango de aprendizaje proporcionado eta y n_iter, que es el número de épocas (pasos por el conjunto de entrenamiento). Mediante el método fit, inicializamos los pesos en self.w_ para un vector , donde m significa el número de dimensiones (características) en el conjunto de datos, al cual añadimos 1 para el primer elemento en este vector que representa el parámetro del sesgo. Recuerda que el primer elemento en este vector, self.w_[0], representa el denominado parámetro del sesgo del cual hemos hablado anteriormente.

      Observa también que este vector contiene pequeños números aleatorios extraídos de una distribución normal con desviación estándar 0.01 con rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]), donde rgen es un generador de números aleatorios NumPy que hemos sembrado con una semilla aleatoria especificada por el usuario, por lo que podemos reproducir, si lo deseamos, resultados previos.

      La razón por la cual no ponemos los pesos a cero es que el rango de aprendizaje (eta) solo tiene efecto sobre el resultado de la clasificación si los pesos empiezan por valores distintos a cero. Si todos los pesos empiezan en cero, el parámetro eta del rango de aprendizaje afecta solo a la escala del vector peso, no a la dirección. Si estás familiarizado con la trigonometría, considera un vector , donde el ángulo entre y un vector sería exactamente cero, como queda demostrado en el siguiente fragmento de código:

      >>> v1 = np.array([1, 2, 3])

      >>> v2 = 0.5 * v1

      >>> np.arccos(v1.dot(v2) / (np.linalg.norm(v1) *

      ... np.linalg.norm(v2)))

      0.0