sábado, 11 de septiembre de 2021

Regresión logística y clasificación para Machine Learning V. Red Neuronal keras-tensorflow

Preparación de datos para alimentar una  red neuronal de clasificación

Anteriormente construimos un modelo de regresión logística usando scikit-learn. Y lo hicimos utilizando un conjunto de datos RL1RL2RL3RL4

Estos post están pensados para que el lector realice las pruebas con sus propios Datasets, pues ya estoy harto de seguir ejemplos siguiendo las instrucciones para tratar un dataset previamente descargado de internet con datos que ni entendemos ni nos sirven de mucho. Creo que se aprende mucho más creando nuestro propio dataset y adaptando las instrucciones a nuestras necesidades en vez de ejecutar como un robot las instrucciones del script. 

En este caso he utilizado un archivo .csv llamado Completo_Etiquetado_Puntuado.csv con datos de resultados de partidos de fútbol con el objetivo de jugar a la quiniela y al quinigol. Pero el lector deberá utilizar el suyo propio,( con datos del tipo que quiera) en este blog hay un cursillo de cómo crear nuestro propio dataset

Ahora construiremos un modelo de clasificación similar. Pero esta vez, será una red neuronal, que construiremos usando Keras. Primero importamos todas las bibliotecas que necesitaremos.

Regresión logística y clasificación para Machine Learning


import matplotlib.pyplot as plt

import numpy as np

import pandas as pd

import sklearn

from sklearn.metrics import r2_score

from sklearn import preprocessing

Importamos Keras, en este caso aprovechando que tenemos instalado en nuestro entorno virtual TensorFlow, utilizaremos keras de TensorFlow.

from tensorflow.keras import layers

from tensorflow import keras

import tensorflow as tf

from tensorflow.keras.models import Sequential

from tensorflow.keras.layers import Dense

Comenzamos con matplotlib.pyplot, así como con NumPy, pandas, sklearn y también las bibliotecas r2_score y de preprocesamiento de sklearn. Luego deshabilitamos las advertencias  e importamos las partes de la biblioteca de Keras que necesitamos.

import warnings

warnings.filterwarnings('ignore')

El tipo de modelo que construiremos será un modelo secuencial, lo que significa que la salida de una capa de la red neuronal alimentará como entrada a la siguiente capa. Y cada una de las capas de nuestra red neuronal serán capas densas. Una vez que ejecutemos estas dos declaraciones de entrada, recibiremos una notificación de que actualmente estamos usando el backend de TensorFlow.

Este es el predeterminado para Keras. A continuación, cargamos nuestro conjunto de datos en el DataFrame Pandas, y luego mezclamos este DataFrame, haciendo uso de la función de muestra.

quinielas = pd.read_csv('Completo_Etiquetado_Puntuado.csv' )

quinielas = quinielas.sample(frac = 1).reset_index(drop = True)

Es una buena práctica mezclar cualquier conjunto de datos, porque muchos de ellos vienen preordenados en algún orden. Y es importante para dividir el conjunto de datos en conjuntos de prueba y de entrenamiento.

Existe el riesgo de que se introduzcan sesgos en el entrenamiento y en los conjuntos de prueba si los datos no se barajan. Después de barajar, etiquetamos y codificamos la columna de etiquetas en nuestro Dataframe, tal como lo hicimos cuando usamos la biblioteca scikit-learn para crear un modelo de regresión logística.

from sklearn.preprocessing import LabelEncoder

lb_make = LabelEncoder()

quinielas['Q1'] = lb_make.fit_transform(quinielas['Q1'])

Una vez más, usamos la función fit_transform de LabelEncoder para hacer esto.

A continuación definimos nuestros datos x, y lo hacemos eliminando todas las columnas en nuestro DataFrame, que no deseamos que se incluyan como características de nuestro modelo.

x_data = quinielas.drop(columns=['temporada','Q1','QX','Q2','QGC0','QGC1','QGC2','QGCM','QGF0','QGF1','QGF2','QGFM','golesLocal','golesVisitante','fecha'])

Red Neuronal keras-tensorflow

Aquí estamos eliminando las columnas de etiquetas. Cuando incorporamos características continuas a nuestro modelo de Keras, debemos asegurarnos de que todas se hayan escalado. Para normalizar las características utilizamos StandardScaler de scikit-learn, que se puede encontrar en preprocessing. Y luego usamos su función fit_transform para escalar nuestro x_data y devolver un conjunto de características normalizadas. 

ss = preprocessing.StandardScaler()

x_data = ss.fit_transform(x_data)

x_data[:10]

Obtenemos una vista previa de las primeras diez filas de nuestras características  normalizadas, que devuelven una gran variedad de números. Lo que para cada columna debe tener una media de aproximadamente cero

Regresión logística y clasificación para Machine Learning
Se muestra una salida para el bloque. Contiene una serie de valores escalados, todos dentro de una matriz numpy. 

Convertimos estas características normalizadas en un Pandas DataFrame,  lo que formará nuestro x_data Y nuestro y_data será la columna de etiqueta del DataFrame.

x_data = pd.DataFrame(x_data)

y_data = quinielas ['Q1']

A continuación, convertimos ambos en matrices NumPy. 

x_data = np.array(x_data)

y_data = np.array(y_data)

Y luego, dividimos nuestros datos en conjuntos de prueba y entrenamiento

train_data = x_data[:29718]

test_data = x_data[29718:]

train_labels = y_data [:29718]

test_labels = y_data [29718:]

Así que aquí reservamos el 80% de nuestros x_data para entrenamiento y el 20% restante para pruebas. Y también hacemos exactamente lo mismo con y_data. Y con esto, nuestros datos ahora están preparados. Y solo nos queda construir y  evaluar nuestra red neuronal. 

Construyendo y evaluando el clasificador de Keras 

El primer paso en la construcción de una red neuronal, con el fin de realizar una regresión logística en nuestros datos de quinielas, es inicializar el modelo para que sea un modelo secuencial. 

model = Sequential()

Para eso, simplemente llamamos al constructor secuencial. Y ahora agregaremos capas a esta red neuronal. 

model.add(Dense(64,

    name = 'Input_Layer',

    input_shape = (10,),

    activation = 'relu'

    )

)

Utilizamos la función add del modelo. Y nuestra primera capa será una capa densa, que contendrá un total de 64 neuronas. 

Le asignamos un nombre, que será Input_Layer, y luego establecemos un cantidad de columnas de entrada , como tenemos un total de 10 características, y también definimos la función de activación para todas las neuronas de esta capa en particular. Y usaremos esa función de activación de unidad relu o rectilínea.

Esta función asegurará que la neurona generará cero para cualquier entrada negativa y pasará la entrada como está si es un valor positivo. Eso constituye la primera capa o Input_Layer de nuestra red neuronal. 

Ahora agregaremos la capa de salida u Output_Layer de esta red neuronal.

model.add(Dense(2,

    name = 'Output_Layer',

    activation = 'softmax'

    )

)

También será una capa densa que contiene con total de tres neuronas. El número de neuronas en esta Output_Layer corresponderá al número de clases de salida. En nuestro ejemplo, esto podría ser 1 o no 1 (X o 2), razón por la cual tenemos dos neuronas aquí. 

(para la X y el 2 utilizaremos la misma red pero con la salida adaptada sólo a la X o sólo al 2 pues se trata de una salida binaria)

A esto lo llamaremos Output_Layer, y la función de activación que se usa aquí será softmax. La activación softmax generará los valores de probabilidad para cada posible etiqueta de salida. En nuestro ejemplo, la salida incluirá la probabilidad de que el resultado sea 1 o no 1 ( X o 2) y la probabilidad de que pertenezca a uno u otro. El que tenga el valor de probabilidad más alto será la predicción de nuestro modelo.

La activación softmax se usa típicamente en la capa final u Output_Layer de una red neuronal, ya que aquí es donde ocurre la clasificación final. Ahora que hemos agregado estas dos capas a nuestro modelo secuencial, podemos hacer uso de su función de resumen para echar un vistazo rápido a lo que contiene este modelo.

model.summary ()

Muestra una salida que contiene una descripci´0n de la red neuronal que hemos creado. La columna Param # muestra el número de parámetros entrenables 704 para la entrada y 130 para la salida. Debajo de la tabla, se muestra un resumen en tres líneas con los  parámetros totales: 834

Construyendo y evaluando el clasificador de Keras

Nuestro modelo tiene dos capas densas llamadas Input_Layer y Output_Layer. Hay 64 salidas de este Input_Layer que corresponden a las 64 neuronas que contiene. Los 704 parámetros para Input_Layer incluyen todos los pesos y sesgos de sus neuronas. Hay 10 características de entrada, que multiplicadas por las 64 neuronas nos dan 640 parámetros de peso. Agregamos a eso los 64 parámetros de sesgo (bias), uno para cada neurona, y terminamos con 704. Y de forma similar, hay 130 parámetros para la capa de salida, lo que da un total de 834 parámetros para esta red neuronal. A continuación  compilamos el modelo, y aquí es donde vinculamos nuestro modelo al back-end específico, que en nuestro caso es TensorFlow.

model.compile(optimizer='adam',

    loss='sparse_categorical_crossentropy',

    metrics=['accuracy'])

Es durante la etapa de compilación donde necesitamos especificar el optimizador que se utilizará. Así que emplearemos el optimizador de uso común llamado adam, y el valor que  optimizará esto será sparse_categorical_crossentropy.

Esta es una de las funciones de pérdida estándar que se utilizan para construir un modelo de clasificación. Un valor bajo para la entropía cruzada implica que los valores predichos están sincronizados con los valores reales, que el modelo está tratando de predecir. El trabajo del optimizador adam será minimizar este valor de sparse_categorical_crossentropy. Al compilar un modelo, también podemos establecer las métricas, que necesitaremos para evaluar el modelo.

Y una de las métricas integradas que podemos utilizar es la precisión. Una vez que establezcamos esto, comenzamos el entrenamiento de nuestro modelo de clasificación llamando a la función de ajuste del modelo.

model.fit(train_data,

    train_labels,

    epochs = 30,

    batch_size = 5,

    verbose = 0)

Pasamos nuestros datos de entrenamiento, así como las etiquetas de entrenamiento. Y al hacer uso de los argumentos epochs y batch_size, especificamos que nuestro modelo debe repasar todos los datos de entrenamiento un total de 30 veces en lotes de 5. Y al establecer el argumento verbose en 0, nos aseguramos de que durante el entrenamiento no se generará salida. El entrenamiento puede llevar un tiempo.

Para hacer eso, simplemente llamamos a la función de evaluación del modelo, a la que pasamos nuestros datos de prueba junto con las etiquetas de prueba. Y esto nos devolverá la pérdida, lo que hará por defecto. Y luego, debido a que especificamos la métrica de precisión cuando compilamos el modelo, también se devolverá la puntuación de precisión. Luego echamos un vistazo a la pérdida, y este número en sí mismo no es tan significativo para nosotros.

loss, accuracy = model.evaluate(test_data,

    test_labels,

    verbose = 0)

print('loss = {:2f}'.format(loss))

Una vez entrenado nuestro modelo echamos un vistazo a la precisión 

print('Accuracy = {:2f}'.format(accuracy))

y obtenemos una precisión bastante alta del 66%. Si comparamos esto con nuestro modelo de regresión logística scikit-learn, que dio una precisión del 50,0 %, queda claro que para este conjunto de datos en particular, el modelo de red neuronal funciona significativamente mejor.

Pero ojo. no se dejen engañar, calculen la probabilidad de acierto de la X y verán que es del orden del 72% ¿cómo es esto posible? en realidad lo que está prediciendo es la probabilidad de acertar una X o una no X, lo mismo para el 1 y el 2. De ahí ese resultado. Ojo con esto es bastante engañoso.


 


No hay comentarios:

Publicar un comentario