Blog
Proyecto: Clasificación binaria de un Sonar
- Publicado por: Rafael Fernandez
- Categoría: Deep Learning
-En este tutorial del proyecto descubrirá cómo utilizar de forma eficaz la biblioteca Keras con Machine Learning paso a paso a través de un proyecto de clasificación binaria.
-Cómo cargar datos de entrenamiento y ponerlos a disposición de Keras.
-Cómo diseñar y entrenar una red neuronal para datos tabulares.
-Cómo evaluar el rendimiento de un modelo de red neuronal en Keras con datos no vistos.
-Cómo realizar la preparación de datos para mejorar la habilidad al usar redes neuronales.
-Cómo ajustar la topología y configurar las redes neuronales en Keras.
Conjunto de datos de clasificación del Sonar
El conjunto de datos que usaremos en este tutorial es el conjunto de datos del Sonar. Este es un set de datos que describe el chirrido del sonar que regresa rebotando sobre las diferentes superficies. Las 60 variables de entrada son la fuerza de los retornos en ángulos diferentes. Es un problema de clasificación binaria que requiere un modelo para diferenciar rocas de cilindros metálicos.
Es un conjunto de datos bien entendido. Todas las variables son continuas y generalmente en el rango de 0 a 1. La variable de salida es una cadena M para mina y R para roca, que necesitará ser convertida a números enteros 1 y 0. El conjunto de datos contiene 208 observaciones y los puedes descargar desde aquí.
Una ventaja de usar este conjunto de datos es que es un problema de referencia estándar. Esto significa que tenemos alguna idea de la habilidad que se espera de un buen modelo. Utilizando la validación cruzada, una red neuronal debería ser capaz de alcanzar un rendimiento de alrededor del 84% con un límite superior de precisión para modelos personalizados de alrededor del 88%.
Eficiencia del Modelo de Red Neural de Línea Base
Vamos a crear un modelo de línea de base, comenzaremos importando todas las clases y funciones que necesitaremos.
import numpy from pandas import read_csv from keras.models import Sequential from keras.layers import Dense from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline
A continuación, podemos inicializar el generador de números aleatorios para asegurarnos de que siempre obtenemos los mismos resultados al ejecutar este código. Esto ayudará si estamos depurando.
# semilla aleatoria seed = 7 numpy.random.seed(seed)
Ahora podemos cargar el conjunto de datos usando Pandas y dividir las columnas en 60 variables de entrada (X) y 1 variable de salida (Y). Usamos Pandas para cargar los datos porque maneja fácilmente las cadenas (la variable de salida), mientras que intentar cargar los datos directamente usando NumPy sería más tedioso.
# cargamos los datos dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values #divimos entre los inputs y los outputs X = dataset[:,0:60].astype(float) Y = dataset[:,60]
La variable de salida es un valor de cadena. Debemos convertirlos en valores enteros 0 y 1. Podemos hacerlo usando la clase LabelEncoder de scikit-learn. Esta clase modelará la codificación requerida usando todo el conjunto de datos a través de la función fit(), luego aplicará la codificación para crear una nueva variable de salida usando la función transform().
# encode class values as integers encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y)
Ahora estamos listos para crear nuestro modelo de red neuronal usando Keras. Vamos a utilizar scikit-learn para evaluar el modelo utilizando la validación cruzada de pliegues con stratied k-fold. Esta es una técnica de remuestreo que proporcionará una estimación del rendimiento del modelo. Para usar los modelos Keras con scikit-learn, debemos usar el envoltorio del KerasClassifier. Esta clase toma una función que crea y devuelve nuestro modelo de red neuronal. También toma argumentos que se transmitirán a la llamada a fit() como el número de epochs y el batch_size. Comencemos definiendo la función que crea nuestro modelo de referencia. Nuestro modelo tendrá una única conexión totalmente oculta con el mismo número de neuronas que las variables de entrada. Este es un buen punto de partida por defecto cuando se crean redes neuronales en un nuevo problema.
Los pesos se inicializan utilizando un pequeño número aleatorio gaussianos. Utilizamos la activación con el Rectier. La capa de salida contiene una sola neurona para hacer predicciones. Se utiliza la función de activación sigmoide para producir una salida de probabilidad en el rango de 0 a 1 que se puede convertir fácil y automáticamente a valores de clase nítidos. Finalmente, estamos usando la función de pérdida logarítmica (cruzamiento binario) durante el entrenamiento, la pérdida preferida para problemas de clasificación binaria. El modelo también utiliza la optimización eficiente de Adam para el descenso de gradiente y las métricas de precisión se recopilarán cuando se entrene el modelo.
# linea base del modelo def create_baseline(): # se crea el modelo model = Sequential() model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal', activation='sigmoid')) # se Compila el modelo model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) return model
Ahora es el momento de evaluar este modelo utilizando la validación cruzada estratificada en el marco de scikit-learn. Pasamos el número de epochs de entrenamiento al KerasClasificador, de nuevo usando valores por defecto razonables. También se desactiva la salida verbose dado que el modelo se creará 10 veces para las 10 validaciones cruzadas que se están realizando.
# se evalua el modelo con el conjunto de datos estimator = KerasClassifier(build_fn=create_baseline, epochs=100, batch_size=5, verbose=0) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(estimator, X, encoded_Y, cv=kfold) print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
La lista completa de códigos se proporciona a continuación para mayor información.
# clasificacion binaria import numpy from pandas import read_csv from keras.models import Sequential from keras.layers import Dense from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # semilla aleatoria seed = 7 numpy.random.seed(seed) # cargamos los datos dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values #divimos entre los inputs y los outputs X = dataset[:,0:60].astype(float) Y = dataset[:,60] # encode class valores y enteros encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) # baseline modelo def create_baseline(): # creamos el modelo model = Sequential() model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal', activation='sigmoid')) #Se compila modelo model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) return model # evaluacon del modelo con el conjunto de datos estimator = KerasClassifier(build_fn=create_baseline, epochs=100, batch_size=5, verbose=0) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(estimator, X, encoded_Y, cv=kfold) print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
Al ejecutar este código se obtiene el siguiente resultado que muestra la media y la desviación estándar de la precisión estimada del modelo en datos no vistos.
Baseline: 80.78% (6.98%)
En mi opinión este es un valor excelente sin hacer ningún trabajo duro.
Mejore el rendimiento con la preparación de datos
Es una buena práctica preparar los datos antes de modelar. Los modelos de redes neuronales son especialmente adecuados para tener valores de entrada consistentes, tanto en escala como en distribución. Una característica efectiva es la estandarización cuando se construyen modelos de redes neuronales. Aquí es donde los datos se reescalan de tal manera que el valor medio para cada atributo es 0 y el estándar es 1. Esto preserva las distribuciones de tipo gaussianas que normalizan las tendencias centrales para cada atributo.
Podemos usar scikit-learn para realizar la estandarización de nuestro conjunto de datos de Sonar usando la clase StandardScaler. En lugar de realizar la estandarización en todo el conjunto de datos, es una buena práctica formar el procedimiento de estandarización en los datos de formación en el transcurso de una ejecución de validación cruzada y utilizar la instancia de estandarización formada para preparar el pliegue de prueba invisible. Esto hace que la estandarización sea un paso en la preparación del modelo en el proceso de validación cruzada y evita que el algoritmo tenga conocimiento de datos no vistos durante la evaluación, conocimiento que podría ser transmitido desde el esquema de preparación de datos como una distribución más nítida.
Podemos lograr esto en scikit-learn usando una clase llamada Pipeline. Pipeline es una envoltura que ejecuta uno o varios modelos dentro de un paso del procedimiento de validación cruzada. Aquí, podemos definir una Pipeline con el StandardScaler seguido de nuestro modelo de red neuronal.
Al ejecutar este ejemplo se obtienen los siguientes resultados. Vemos una pequeña pero muy buena elevación en la precisión media.
# clasificacion binaria import numpy from pandas import read_csv from keras.models import Sequential from keras.layers import Dense from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # semilla aleatoria seed = 7 numpy.random.seed(seed) # se cargan los datos dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values #divimos entre los inputs y los outputs X = dataset[:,0:60].astype(float) Y = dataset[:,60] # encode clase valores y enteros encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) # baseline modelo def create_baseline(): # Se crea el modelo model = Sequential() model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal', activation='sigmoid')) # Se compile el modelo model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) return model # se evalua la baseline de modelo con standardized del conjunto de datos estimators = [] estimators.append(('standardize', StandardScaler())) estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, epochs=100, batch_size=5, verbose=0))) pipeline = Pipeline(estimators) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(pipeline, X, encoded_Y, cv=kfold) print("Standardized: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
Al ejecutar este ejemplo se obtienen el siguiente resultado:
Standardized: 84.63% (7.65%)
Afinación de Capas y Neuronas en el Modelo
Hay muchas cosas que afinar en una red neuronal, como la inicialización de los pesos, la activación de funciones, procedimiento de optimización, etc. Un aspecto que puede tener un tamaño superior al del efecto es la de la propia red llamada topología de red. En esta sección echamos un vistazo a dos experimentos sobre la estructura de la red: hacerla más pequeña y hacerla más grande. Estos son buenos experimentos para realizar cuando se quiere ajustar una red neuronal en su problema.
Evaluar una red más pequeña
Sospecho que hay mucha redundancia en las variables de entrada para este problema. Los datos describen la misma señal desde ángulos diferentes. Quizás algunos de esos ángulos son más relevantes que otros. Podemos forzar un tipo de extracción de características por la red restringiendo la opción espacio de representación en la primera capa oculta.
En este experimento tomamos nuestro modelo base con 60 neuronas en la capa oculta y los reducimos a la mitad (30). Esto ejercerá presión sobre la red durante el entrenamiento para que elija la estructura más importante en los datos de entrada a modelar. También estandarizaremos los datos como en el experimento anterior con la preparación de datos y trataremos de aprovechar la pequeña elevación en rendimiento.
# clasificacion binaria con red neuronal mas pequeña import numpy from pandas import read_csv from keras.models import Sequential from keras.layers import Dense from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # semilla aleatoria seed = 7 numpy.random.seed(seed) # cargamos los datos dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values #divimos entre los inputs y los outputs X = dataset[:,0:60].astype(float) Y = dataset[:,60] # encode la clase de valores como enteros encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) # modelo mas pequeño def create_smaller(): # se crea el modelo model = Sequential() model.add(Dense(30, input_dim=60, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal', activation='sigmoid')) # Se Compila el modelo model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) return model estimators = [] estimators.append(('standardize', StandardScaler())) estimators.append(('mlp', KerasClassifier(build_fn=create_smaller, epochs=100, batch_size=5, verbose=0))) pipeline = Pipeline(estimators) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(pipeline, X, encoded_Y, cv=kfold) print("Mas pequeña: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
Al ejecutar este ejemplo se obtiene el siguiente resultado. Podemos ver que tenemos un ligero aumento en la precisión media estimada y una reducción importante en la desviación estándar (horquilla media) de las puntuaciones de precisión para el modelo. Este es un gran resultado porque lo estamos haciendo un poco mejor con una red con la mitad del tamaño, que a su vez toma la mitad del tiempo para entrenar.
Mas pequeña: 85.04% (7.40%)
Evaluar una red más grande
Una topología de red neuronal con más capas ofrece más oportunidades para que la red extraiga mas características claves y recombinarlas de manera no lineal. Podemos evaluar y añadir más capas a la red mejora el rendimiento fácilmente haciendo otro pequeño ajuste para la función utilizada para crear nuestro modelo. Aquí, añadimos una nueva capa (una línea) a la red que introduce otra capa oculta con 30 neuronas después de la primera capa oculta. Nuestra red ahora tiene la topología:
60 entradas –> [60 –> 30] –> 1 salida
La idea aquí es que la red tiene la oportunidad de modelar todas las variables de entrada antes de ser embotellada y forzada a reducir a la mitad la capacidad de representación, como hicimos en el experimento anterior con la red más pequeña. En lugar de exprimir la representación de las entradas mismas, tenemos una capa oculta adicional para ayudar en el proceso.
# # clasificacion binaria con la red mas grande import numpy from pandas import read_csv from keras.models import Sequential from keras.layers import Dense from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # semilla aleatoria seed = 7 numpy.random.seed(seed) # cargamos los datos dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values #divimos entre los inputs y los outputs X = dataset[:,0:60].astype(float) Y = dataset[:,60] # encode la clase de valores como enteros encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) # model mas grande def create_larger(): # create model model = Sequential() model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu')) model.add(Dense(30, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal', activation='sigmoid')) # Compila el modelo model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) return model estimators = [] estimators.append(('standardize', StandardScaler())) estimators.append(('mlp', KerasClassifier(build_fn=create_larger, epochs=100, batch_size=5, verbose=0))) pipeline = Pipeline(estimators) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(pipeline, X, encoded_Y, cv=kfold) print("red neuronal mas grande: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
Al ejecutar este ejemplo se obtienen los siguientes resultados:
red neuronal mas grande: 85.09% (9.43%)
Podemos ver que no conseguimos una elevación en el funcionamiento del modelo. Esto puede ser un ruido estadístico o una señal de que se necesita más capacitación. Con una mayor afinación de aspectos como el algoritmo de optimización y el número de épocas de entrenamiento, se espera que sean posibles nuevas mejoras.
Resumen del post “Proyecto: Clasificación binaria de un Sonar”
En esta lección usted descubrió cómo puede trabajar paso a paso a través de un problema de clasificación binaria con Keras para conocer especialmente:
- Cómo cargar y preparar datos para su uso en Keras.
- Cómo crear un modelo de red neural de baseline.
- Cómo evaluar un modelo de Keras usando la validación cruzada de scikit-learn y stratied k-fold.
- Cómo los esquemas de preparación de datos pueden mejorar el rendimiento de sus modelos.
- Cómo los experimentos de ajuste de la topología de la red pueden mejorar el rendimiento del modelo.
➡ Sigue aprendiendo de Deep Learning en nuestro curso:
[…] usarán el conjunto de datos de clasificaciones binarias de Sonar (más información en este post). Evaluaremos los modelos desarrollados utilizando scikit-learn con una validación cruzada de 10 […]
[…] usarán el conjunto de datos de clasificaciones binarias de Sonar (más información en este post). Evaluaremos los modelos desarrollados utilizando scikit-learn con una validación cruzada de 10 […]