Además, ¡no te pierdas nuestra hoja de trucos de Keras , que te muestra los seis pasos que debes seguir para construir redes neuronales en Python con ejemplos de código!
Introducción a las redes neuronales artificiales
Antes de profundizar en Keras y en cómo puedes utilizarlo para iniciarte en el aprendizaje profundo en Python, probablemente deberías saber un par de cosas sobre redes neuronales. Como has leído brevemente en la sección anterior, las redes neuronales encontraron su inspiración en la biología, donde el término "red neuronal" también puede utilizarse para las neuronas. El cerebro humano es, pues, un ejemplo de este tipo de red neuronal, que se compone de varias neuronas.
Y, como todos sabéis, el cerebro es capaz de realizar cálculos bastante complejos, y de ahí viene la inspiración para las Redes Neuronales Artificiales. La red en su conjunto es una potente herramienta de modelización.
Perceptrones
La red neuronal más sencilla es el "perceptrón", que, en su forma más simple, consta de una sola neurona. De forma muy parecida a las neuronas biológicas, que tienen dendritas y axones, la neurona artificial única es una estructura arbórea simple que tiene nodos de entrada y un único nodo de salida, que está conectado a cada nodo de entrada. Aquí tienes una comparación visual de ambos:
Como puedes ver en la imagen, las neuronas artificiales tienen seis componentes. De izquierda a derecha, estos son
Nodos de entrada . Resulta que cada nodo de entrada está asociado a un valor numérico, que puede ser cualquier número real. Recuerda que los números reales constituyen todo el espectro de los números: pueden ser positivos o negativos, enteros o decimales.
Conexiones . Del mismo modo, cada conexión que parte del nodo de entrada tiene asociado un peso, que también puede ser cualquier número real.
A continuación, se reúnen todos los valores de los nodos de entrada y los pesos de las conexiones: se utilizan como entradas para una suma ponderada : \ (y = f (\suma_{i=1}^{D} w_i*x_i)\ ) , o, dicho de otro modo, \ (y = f(w_1*x_1 + w_2*x_2 + ... w_D*x_D)\) .
Este resultado será la entrada para una función de transferencia o activación . En el caso más sencillo pero trivial, esta función de transferencia sería una función de identidad, \ (f(x)=x\) o \ (y=x\) . En este caso, \ (x\) es la suma ponderada de los nodos de entrada y las conexiones. Sin embargo, al igual que una neurona biológica sólo se dispara cuando se supera un determinado umbral, la neurona artificial también sólo se disparará cuando la suma de las entradas supere un umbral, pongamos por ejemplo 0. ¡Esto es algo que no puedes volver a encontrar en una función de identidad! La forma más intuitiva que se nos ocurre es idear un sistema como el siguiente:
\(f(x) = 0\ ) si \ (x<0\) \(f(x) = 0,5\) si \ (x=0\) \(f(x) = 1\) si \ (x>0\)
Por supuesto, ya te puedes imaginar que la salida no va a ser una línea suave: será una función discontinua. Como esto puede causar problemas en el tratamiento matemático, a menudo se utiliza una variante continua, la función sigmoidea. Un ejemplo de función sigmoidea que quizá ya conozcas es la función logística. Si utilizas esta función, ¡el resultado será mucho más suave!
Como resultado, tienes el nodo de salida , que se asocia a la función (como la función sigmoidea) de la suma ponderada de los nodos de entrada. Ten en cuenta que la función sigmoidea es una función matemática que da lugar a una curva en forma de "S"; leerás más sobre esto más adelante.
Por último, el perceptrón puede tener un parámetro adicional, llamado sesgo , que puedes considerar como el peso asociado a un nodo de entrada adicional que se fija permanentemente en 1. El valor del sesgo es fundamental porque te permite desplazar la función de activación hacia la izquierda o hacia la derecha, lo que puede determinar el éxito de tu aprendizaje.
Observa que la consecuencia lógica de este modelo es que los perceptrones sólo funcionan con datos numéricos. Esto implica que debes convertir cualquier dato nominal en un formato numérico.
Ahora que sabes que los perceptrones funcionan con umbrales, el paso a utilizarlos con fines de clasificación no está tan lejos: el perceptrón puede acordar que cualquier salida por encima de un determinado umbral indica que una instancia pertenece a una clase, mientras que una salida por debajo del umbral podría dar lugar a que la entrada sea miembro de la otra clase. La línea recta en la que la salida es igual al umbral es entonces el límite entre las dos clases.
Perceptrones multicapa
Las redes de perceptrones son perceptrones multicapa, ¡y esto es lo que este tutorial implementará en Python con la ayuda de Keras! Los perceptrones multicapa también se conocen como "redes neuronales feed-forward". Como ya habrás adivinado, se trata de redes más complejas que el perceptrón, ya que están formadas por múltiples neuronas que se organizan en capas. El número de capas suele limitarse a dos o tres, pero teóricamente, ¡no hay límite!
Las capas actúan de forma muy parecida a las neuronas biológicas sobre las que has leído más arriba: las salidas de una capa sirven como entradas para la capa siguiente.
Entre las capas, puedes distinguir una capa de entrada, capas ocultas y una capa de salida. Los perceptrones multicapa suelen estar totalmente conectados. Esto significa que hay una conexión entre cada perceptrón de una capa concreta y cada perceptrón de la capa siguiente. Aunque la conectividad no es un requisito, éste suele ser el caso.
Observa que, mientras que el perceptrón sólo podía representar separaciones lineales entre clases, el perceptrón multicapa supera esa limitación y también puede representar límites de decisión más complejos.
Predicción de tipos de vino en Python con Keras
Para este tutorial, utilizarás el conjunto de datos de calidad del vino que puedes encontrar en el conjunto de datos de calidad del vino del Repositorio de Aprendizaje Automático de la UCI. Lo ideal es que realices el aprendizaje profundo en conjuntos de datos más grandes, pero a efectos de este tutorial, utilizarás uno más pequeño. Esto se debe principalmente a que el objetivo es que empieces a utilizar la biblioteca y te familiarices con el funcionamiento de las redes neuronales.
Puede que ya conozcas este conjunto de datos, ya que es uno de los más populares para iniciarse en el aprendizaje de la resolución de problemas de aprendizaje automático. En este caso, te servirá para iniciarte en el aprendizaje profundo en Python con Keras.
¡Empecemos ya!
Comprender los datos
Sin embargo, antes de empezar a cargar los datos, puede ser una buena idea comprobar cuánto sabes realmente sobre el vino (en relación con el conjunto de datos, por supuesto). La mayoría de vosotros sabréis que, en general, hay dos tipos de vino muy populares: el tinto y el blanco.
(Estoy seguro de que hay muchos más, pero para simplificar y debido a mi limitado conocimiento de los vinos, me quedaré con esto. Siento si decepciono a los verdaderos entendidos que hay entre vosotros :)).
Saber esto ya es una cosa, pero si quieres analizar estos datos, necesitarás saber un poco más.
En primer lugar, consulta la carpeta de descripción de datos para ver qué variables se han incluido. Éste suele ser el primer paso para comprender tus datos. Ve a esta página para consultar la descripción o sigue leyendo para conocer un poco mejor tus datos.
Los datos consisten en dos conjuntos de datos relacionados con las variantes roja y blanca del vino portugués "Vinho Verde". Como se indica en la descripción, sólo encontrarás variables fisicoquímicas y sensoriales incluidas en este conjunto de datos. El archivo de descripción de datos enumera las 12 variables que se incluyen en los datos, pero para los que, como yo, tampoco son expertos en química, he aquí una breve descripción de cada variable:
Acidez fija : los ácidos son propiedades importantes del vino y contribuyen en gran medida a su sabor. Normalmente, la acidez total se divide en dos grupos: los ácidos volátiles y los ácidos no volátiles o fijos. Entre los ácidos fijos que puedes encontrar en los vinos están los siguientes: tartárico, málico, cítrico y succínico. Esta variable se expresa en g(\(ácido tartárico\) )/\(dm^3\) en los conjuntos de datos.
Acidez volátil: la acidez volátil es básicamente el proceso por el que el vino se convierte en vinagre. En EE.UU., los límites legales de Acidez Volátil son 1,2 g/L para el vino tinto de mesa y 1,1 g/L para el vino blanco de mesa. En estos conjuntos de datos, la acidez volátil se expresa en g(\(ácido acético) )/\(dm^3\) .
El ácido cítrico es uno de los ácidos fijos que encontrarás en los vinos. Se expresa en g/\(dm^3\) en los dos conjuntos de datos.
El azúcar residual suele referirse al azúcar que queda después de que la fermentación se detenga, o se pare. Se expresa en g/\(dm^3\) en los datos de red y white.
Los cloruros pueden contribuir significativamente a la salinidad del vino. Aquí verás que se expresa en g(\(cloruro sódico) )/\(dm^3\) .
Dióxido de azufre libre : la parte del dióxido de azufre que se añade a un vino y que se pierde en él se dice que está ligada, mientras que la parte activa se dice que está libre. El enólogo siempre intentará conseguir la mayor proporción de azufre libre para ligar. Esta variable se expresa en mg/(dm^3) en los datos.
El dióxido de azufre total es la suma del dióxido de azufre ligado y del dióxido de azufre libre (SO2). Aquí se expresa en mg/(dm^3\) . Hay límites legales para los niveles de azufre en los vinos: en la UE, los vinos tintos sólo pueden tener 160mg/L, mientras que los blancos y rosados pueden tener unos 210mg/L. Los vinos dulces pueden tener 400 mg/L. En EE.UU., los límites legales están fijados en 350 mg/L, y en Australia, en 250 mg/L.
La densidad se utiliza generalmente como medida de la conversión del azúcar en alcohol. Aquí se expresa en g/\(cm^3\) .
El pH o potencial de hidrógeno es una escala numérica para especificar la acidez o basicidad del vino. Como ya sabrás, las soluciones con un pH inferior a 7 son ácidas, mientras que las soluciones con un pH superior a 7 son básicas. Con un pH de 7, el agua pura es neutra. La mayoría de los vinos tienen un pH entre 2,9 y 3,9 y, por tanto, son ácidos.
Los sulfatos son al vino lo que el gluten a los alimentos. Puede que ya conozcas los sulfitos por los dolores de cabeza que pueden provocar. Forman parte habitual de la elaboración del vino en todo el mundo y se consideran necesarias. En este caso, se expresan en g(\(sulfato potásico) )/(dm^3\) .
Alcohol : el vino es una bebida alcohólica, y como sabes, el porcentaje de alcohol puede variar de un vino a otro. No debería sorprender que esta variable se incluya en los conjuntos de datos, donde se expresa en % vol.
Calidad : los expertos en vino calificaron la calidad del vino entre 0 (muy mala) y 10 (muy excelente). La cifra final es la mediana de al menos tres evaluaciones realizadas por esos mismos expertos en vino.
Todo esto, por supuesto, es información muy básica que quizá necesites saber para empezar. Si eres un verdadero conocedor de vinos, ¡probablemente sepas todo esto y más!
Ahora, ¡es el momento de obtener tus datos!
Cargar los datos
Esto puede hacerse fácilmente con la biblioteca de manipulación de datos Pandas de Python. Sigue la convención de importación e importa el paquete con su alias, pd.
A continuación, utiliza la función read_csv() para leer los archivos CSV en los que están almacenados los datos. Además, utiliza el argumento sep para especificar que el separador, en este caso, es un punto y coma y no una coma normal.
Pruébalo en el fragmento Light de DataCamp que aparece a continuación:
¡Impresionante! Ha sido pan comido, ¿verdad?
Probablemente ya hayas hecho esto un millón de veces, pero siempre es un paso esencial para empezar. ¡Ahora estás completamente preparado para empezar a explorar, manipular y modelar tus datos!
Exploración de datos
Con los datos a mano, ¡te resultará fácil saber más sobre estos vinos! Una de las primeras cosas que probablemente querrás hacer es empezar por obtener una vista rápida de tus dos DataFrames:
# Print info on white wine
_____(white.____())
# Print info on red wine
_____(red.____())
Ahora es el momento de comprobar si tu importación ha tenido éxito: comprueba dos veces si los datos contienen todas las variables que te prometió el archivo de descripción de datos del Repositorio de Aprendizaje Automático de la UCI. Además del número de variables, comprueba también la calidad de la importación: ¿son correctos los tipos de datos? ¿Pasaron todas las filas? ¿Hay algún valor nulo que debas tener en cuenta al limpiar los datos?
También podrías comprobar tus datos con algo más que info():
Una breve recapitulación de todas estas funciones pandas: verás que head(), tail() y sample() son fantásticas porque te proporcionan una forma rápida de inspeccionar tus datos sin complicaciones.
A continuación, describe() ofrece algunas estadísticas resumidas sobre tus datos que pueden ayudarte a evaluar la calidad de los mismos. Ves que algunas de las variables tienen mucha diferencia en sus valores min y max. Esto es algo de lo que te ocuparás más adelante, pero en este momento, es imprescindible que seas consciente de ello.
Por último, has comprobado la presencia de valores nulos en red con ayuda de isnull(). Se trata de una función que siempre puede resultar útil cuando aún tienes dudas después de haber leído los resultados de info().
Consejo : comprueba también si los datos del vino contienen valores nulos. Puedes hacerlo utilizando el shell IPython del fragmento DataCamp Light que ves justo arriba.
Ahora que ya has inspeccionado tus datos para ver si la importación se ha realizado correctamente y con éxito, es hora de profundizar un poco más.
Visualizar los datos
Una forma de hacerlo es observar la distribución de algunas de las variables del conjunto de datos y hacer gráficos de dispersión para ver posibles correlaciones. Por supuesto, puedes llevar todo esto a un nivel mucho más alto si quieres utilizar estos datos para tu propio proyecto.
Alcohol
Una variable que podría resultarte interesante a primera vista es alcohol. Probablemente sea una de las primeras cosas que llaman tu atención cuando inspeccionas un conjunto de datos sobre el vino. Puedes visualizar las distribuciones con cualquier biblioteca de visualización de datos, pero en este caso, el tutorial hace uso de matplotlib para trazar las distribuciones rápidamente:
Como puedes ver en la imagen de abajo, observas que los niveles de alcohol entre el vino tinto y el blanco son prácticamente los mismos: tienen alrededor de un 9% de alcohol. Por supuesto, también hay una cantidad considerable de observaciones que tienen un 10% u 11% de porcentaje de alcohol.
Ten en cuenta que puedes volver a comprobarlo si utilizas la función histogram() del paquete numpy para calcular el histograma de los datos de white y red, tal como se muestra a continuación:
import numpy as np
print(np.histogram(red.alcohol, bins=[7,8,9,10,11,12,13,14,15]))
print(np.histogram(white.alcohol, bins=[7,8,9,10,11,12,13,14,15]))
Si te interesan los tutoriales de matplotlib, no dejes de consultar el tutorial Matplotlib para principiantes de DataCamp y el tutorial Visualización de datos volumétricos en 3D , que muestra cómo utilizar la API de controladores de eventos de Matplotlib.
Sulfatos
A continuación, una cosa que me interesa es la relación entre los sulfatos y la calidad del vino. Como has leído más arriba, los sulfatos pueden provocar dolores de cabeza a la gente, y me pregunto si esto influye en la calidad del vino. Es más, a menudo oigo que sobre todo las mujeres no quieren beber vino precisamente porque provoca dolores de cabeza. ¿Quizás esto afecte a las puntuaciones del vino tinto?
Echemos un vistazo.
Como puedes ver en la imagen inferior, el vino tinto parece contener más sulfatos que el vino blanco, que tiene menos sulfatos por encima de 1 g/\(dm^3\) . En el caso del vino blanco, sólo parece haber un par de excepciones que se sitúan justo por encima de 1 g/\(dm^3\) , mientras que en el caso de los vinos tintos es definitivamente mayor. Esto podría explicar el dicho general de que el vino tinto provoca dolores de cabeza, pero ¿qué hay de la calidad?
Puedes ver claramente que hay un vino blanco con una cantidad relativamente baja de sulfatos que obtiene una puntuación de 9, pero para el resto, es difícil interpretar correctamente los datos en este momento.
Por supuesto, debes tener en cuenta que la diferencia en las observaciones también podría afectar a los gráficos y a cómo podrías interpretarlos.
Acidez
Aparte de los sulfatos, la acidez es una de las características principales y vitales del vino, necesaria para conseguir vinos de calidad. Los grandes vinos suelen equilibrar la acidez, el tanino, el alcohol y el dulzor. Un poco más de investigación me enseñó que en cantidades de 0,2 a 0,4 g/L, la acidez volátil no afecta a la calidad de un vino. Sin embargo, a niveles más altos, la acidez volátil puede dar al vino una sensación táctil aguda y avinagrada. Una acidez volátil extrema significa un vino gravemente defectuoso.
Pongamos los datos a prueba y hagamos un diagrama de dispersión que represente el alcohol frente a la acidez volátil. Los puntos de datos deben colorearse según su clasificación o etiqueta quality:
Observa que los colores de esta imagen se eligen aleatoriamente con ayuda del módulo NumPy random. Siempre puedes cambiar esto pasando una lista a las variables redcolors o whitecolors. Pero asegúrate de que son iguales (excepto 1, porque los datos del vino blanco tienen un valor quality único más que los datos del vino tinto), ¡de lo contrario tus leyendas no van a coincidir!
Consulta el gráfico completo aquí:
En la imagen anterior, ves que los niveles que has leído más arriba se mantienen especialmente para el vino blanco: la mayoría de los vinos con etiqueta 8 tienen niveles de acidez volátil de 0,5 o inferiores, pero es muy difícil decir si tiene o no un efecto sobre la calidad, ya que todos los puntos de datos están muy densamente agrupados hacia un lado del gráfico.
Esto es sólo una exploración rápida de datos. Si te interesa elaborar este paso en tus propios proyectos, ten en cuenta los posts sobre exploración de datos de DataCamp, como los tutoriales Python Exploratory Data Analysis y Python Data Profiling , que te guiarán por los fundamentos del AED.
Conclusión del Análisis Exploratorio de Datos
Tal vez esto haya sido mucho que digerir, así que nunca es tarde para hacer un pequeño resumen de lo que has visto durante tu EDA y que podría ser importante para el desarrollo posterior de este tutorial:
Algunas de las variables de tus conjuntos de datos tienen valores considerablemente alejados. Puedes ocuparte de esto, y lo harás, en la siguiente sección del tutorial.
Tienes un escenario ideal: no hay valores nulos en los conjuntos de datos.
La mayoría de los vinos incluidos en el conjunto de datos tienen alrededor de un 9% de alcohol.
El vino tinto parece contener más sulfatos que el blanco, que tiene menos sulfatos por encima de 1 g/(dm^3\) .
Viste que la mayoría de los vinos tenían una acidez volátil de 0,5 o inferior. De momento, no hay relación directa con la calidad del vino.
Hasta ahora, has examinado los datos del vino blanco y del vino tinto por separado. Las dos parecen diferir algo cuando observas de cerca algunas de las variables, y en otros casos, las dos parecen muy similares. ¿Crees que podría haber una forma de clasificar las entradas en función de sus variables en vino blanco o tinto?
Sólo hay una forma de averiguarlo: ¡preprocesa los datos y modélalos de forma que puedas ver lo que ocurre!
Preprocesar datos
Ahora que has explorado tus datos, ¡es el momento de actuar sobre la base de los conocimientos que has obtenido! ¡Vamos a preprocesar los datos para que puedas empezar a construir tu propia red neuronal!
En este caso estableces ignore_index en True porque no quieres conservar las etiquetas de índice de white cuando añadas los datos a red: quieres que las etiquetas continúen desde donde se quedaron en red, no duplicar las etiquetas de índice de la unión de ambos conjuntos de datos.
Intermezzo: Matriz de correlaciones
Ahora que tienes el conjunto de datos completo, es una buena idea hacer también una exploración rápida de los datos; ya sabes algunas cosas al mirar los dos conjuntos de datos por separado, y ahora es el momento de reunir algunas ideas más sólidas, tal vez.
Como puede resultar algo difícil interpretar los gráficos, también es buena idea trazar una matriz de correlaciones. De este modo, podrás saber más rápidamente qué variables están correlacionadas:
import seaborn as sns
corr = wines.corr()
sns.heatmap(corr,
xticklabels=corr.columns.values,
yticklabels=corr.columns.values)
sns.plt.show()
Como era de esperar, hay algunas variables que se correlacionan, como density y residual sugar. Además, volatile acidity y type están más estrechamente relacionados de lo que cabría suponer originalmente al observar los dos conjuntos de datos por separado, y era de esperar que free sulfur dioxide y total sulfur dioxide estuvieran correlacionados.
¡Muy interesante!
Conjuntos de entrenamiento y prueba
Los datos desequilibrados suelen referirse a un problema de clasificación en el que las clases no están representadas por igual.La mayoría de los conjuntos de datos de clasificación no tienen exactamente el mismo número de instancias en cada clase, pero una pequeña diferencia a menudo no importa. Por tanto, debes asegurarte de que las dos clases de vino están presentes en el modelo de entrenamiento. Es más, la cantidad de instancias de los dos tipos de vino tiene que ser más o menos igual para que no favorezcas a una u otra clase en tus predicciones.
En este caso, parece que hay un desequilibrio, pero de momento te conformarás con esto. Después, puedes evaluar el modelo y, si su rendimiento es inferior, puedes recurrir al submuestreo o al sobremuestreo para cubrir la diferencia de observaciones.
De momento, importa el train_test_split de sklearn.model_selection y asigna los datos y las etiquetas de destino a las variables X y y. Verás que tienes que aplanar la matriz de etiquetas objetivo para estar totalmente preparado para utilizar las variables X y y como entrada para la función train_test_split(). Manos a la obra, ¡ponte a trabajar en el trozo Light de DataCamp que aparece a continuación!
Ya estás en camino de construir tu primera red neuronal, ¡pero aún hay una cosa de la que tienes que ocuparte! ¿Sigues sabiendo lo que descubriste cuando mirabas los resúmenes de los conjuntos de datos de white y red?
De hecho, algunos de los valores estaban algo alejados. Podría tener sentido hacer alguna normalización aquí.
Normalizar los datos
La normalización es una forma de tratar estos valores tan distantes. El paquete scikit-learn te ofrece una forma estupenda y rápida de normalizar tus datos: importa el módulo StandardScaler de sklearn.preprocessing ¡y ya estás listo para escalar tus datos de entrenamiento y prueba!
from sklearn. preprocessing import StandardScaler
scaler = StandardScaler( ) . fit( X_train)
X_train = scaler. transform( X_train)
X_test = scaler. transform( X_test) Con la tecnología de
Ahora que tienes los datos preprocesados, puedes pasar al verdadero trabajo: construir tu propia red neuronal para clasificar los vinos.
Datos del modelo
Antes de empezar a modelar, vuelve a tu pregunta original: ¿puedes predecir si un vino es tinto o blanco fijándote en sus propiedades químicas, como la acidez volátil o los sulfatos?
Como sólo tienes dos clases, a saber, blanco y rojo, vas a hacer una clasificación binaria. Como puedes imaginar, "binario" significa 0 ó 1, sí o no. Como las redes neuronales sólo pueden trabajar con datos numéricos, ya has codificado el rojo como 1 y el blanco como 0.
Un tipo de red que funciona bien en un problema de este tipo es el perceptrón multicapa. Como has leído al principio de este tutorial, este tipo de red neuronal suele estar totalmente conectada. Eso significa que, para resolver este problema, debes construir una pila bastante sencilla de capas totalmente conectadas. En cuanto a la función de activación que utilizarás, lo mejor es que utilices una de las más comunes para familiarizarte con Keras y las redes neuronales, que es la función de activación relu.
Ahora, ¿cómo empiezas a construir tu perceptrón multicapa? Una forma rápida de empezar es utilizar el modelo secuencial de Keras: es una pila lineal de capas. Puedes crear fácilmente el modelo pasando una lista de instancias de capa al constructor, que configuras ejecutando model = Sequential().
A continuación, lo mejor es que recuerdes la estructura del perceptrón multicapa tal y como la habrás leído al principio de este tutorial: tienes una capa de entrada, unas capas ocultas y una capa de salida. Por eso, cuando hagas tu modelo, es importante que tengas en cuenta que tu primera capa tiene que dejar clara la forma de entrada. El modelo necesita saber qué forma de entrada debe esperar y por eso siempre encontrarás los argumentos input_shape, input_dim, input_length, o batch_size en la documentación de las capas y en los ejemplos prácticos de esas capas.
En este caso, tendrás que utilizar una capa Dense, que es una capa totalmente conectada. Las capas densas realizan la siguiente operación: output = activation(dot(input, kernel) + bias). Observa que sin la función de activación, tu capa Densa constaría sólo de dos operaciones lineales: un producto punto y una suma.
En la primera capa, el argumento activation toma el valor relu. A continuación, también verás que se ha definido la dirección input_shape. Este es el input de la operación que acabas de ver: el modelo toma como entrada matrices de forma (12,), o (*, 12). Por último, ves que la primera capa tiene 12 como primer valor del argumento units de Dense(), que es la dimensionalidad del espacio de salida y que en realidad son 12 unidades ocultas. Esto significa que el modelo dará como salida matrices de forma (*, 12): ésta es la dimensionalidad del espacio de salida. No te preocupes si no lo entiendes del todo ahora, ¡ya leerás más sobre ello más adelante!
La units representa en realidad la kernel de la fórmula anterior o la matriz de pesos, compuesta por todos los pesos dados a todos los nodos de entrada, creada por la capa. Observa que en el ejemplo no incluyes ningún sesgo, ya que no has incluido el argumento use_bias y lo has fijado en TRUE, que también es una posibilidad.
La capa intermedia también utiliza la función de activación relu. La salida de esta capa serán matrices de forma (*,8).
Estás terminando la red con una capa Dense de tamaño 1. La capa final también utilizará una función de activación sigmoidea para que su salida sea en realidad una probabilidad; esto significa que dará como resultado una puntuación entre 0 y 1, que indicará la probabilidad de que la muestra tenga el objetivo "1", o la probabilidad de que el vino sea tinto.
# Import `Sequential` from `keras.models`
from keras.models import Sequential
# Import `Dense` from `keras.layers`
from keras.layers import Dense
# Initialize the constructor
model = Sequential()
# Add an input layer
model.add(Dense(12, activation='relu', input_shape=(11,)))
# Add one hidden layer
model.add(Dense(8, activation='relu'))
# Add an output layer
model.add(Dense(1, activation='sigmoid'))
En definitiva, ves que hay dos decisiones clave de arquitectura que tienes que tomar para hacer tu modelo: cuántas capas vas a utilizar y cuántas "unidades ocultas" elegirás para cada capa.
En este caso, elegiste 12 unidades ocultas para la primera capa de tu modelo: como has leído más arriba, ésta es la dimensionalidad del espacio de salida. En otras palabras, estás fijando la cantidad de libertad que permites que tenga la red cuando está aprendiendo representaciones. Si permitieras más unidades ocultas, tu red podrá aprender representaciones más complejas, pero también será una operación más costosa y propensa al sobreajuste.
Recuerda que el sobreajuste se produce cuando el modelo es demasiado complejo: describirá el error aleatorio o el ruido y no la relación subyacente que debe describir. En otras palabras, ¡los datos de entrenamiento se modelan demasiado bien!
Ten en cuenta que cuando no dispongas de tantos datos de entrenamiento, es preferible que utilices una red pequeña con muy pocas capas ocultas (normalmente sólo una, como en el ejemplo anterior).
Si quieres obtener información sobre el modelo que acabas de crear, puedes utilizar la función atribuida output_shape o la función summary(), entre otras. A continuación se enumeran algunas de las más básicas.
Prueba a ejecutarlos para ver qué resultados obtienes exactamente y qué te dicen sobre el modelo que acabas de crear:
Compilar y ajustar
A continuación, es hora de compilar tu modelo y ajustarlo a los datos: una vez más, utiliza compile() y fit() para hacerlo.
model. compile ( loss= 'binary_crossentropy' ,
optimizer= 'adam' ,
metrics= [ 'accuracy' ] )
model. fit( X_train, y_train, epochs= 20 , batch_size= 1 , verbose= 1 ) Con la tecnología de
Al compilar, configuras el modelo con el optimizador adam y la función de pérdida binary_crossentropy. Además, también puedes controlar la precisión durante el entrenamiento pasando ['accuracy'] al argumento metrics.
optimizer y loss son dos argumentos necesarios si quieres compilar el modelo. Algunos de los algoritmos de optimización más utilizados son el Descenso Gradiente Estocástico (SGD), el ADAM y el RMSprop. Dependiendo del algoritmo que elijas, tendrás que ajustar ciertos parámetros, como la velocidad de aprendizaje o el impulso. La elección de una función de pérdida depende de la tarea que tengas entre manos: por ejemplo, para un problema de regresión, normalmente utilizarás el Error Cuadrático Medio (ECM). Como ves en este ejemplo, has utilizado binary_crossentropy para el problema de clasificación binaria de determinar si un vino es tinto o blanco. Por último, con la clasificación multiclase, utilizarás categorical_crossentropy.
Después, puedes entrenar el modelo durante 20 épocas o iteraciones sobre todas las muestras de X_train y y_train, en lotes de 1 muestra. También puedes especificar el argumento verbose. Poniéndolo en 1, indicas que quieres ver el registro de la barra de progreso.
En otras palabras, tienes que entrenar el modelo para un número determinado de épocas o exposiciones al conjunto de datos de entrenamiento. Una época es una sola pasada por todo el conjunto de entrenamiento, seguida de una prueba del conjunto de verificación. El tamaño de lote que especifiques en el código anterior define el número de muestras que se van a propagar por la red. Además, al hacer esto, optimizas la eficacia porque te aseguras de no cargar demasiados patrones de entrada en la memoria al mismo tiempo.
Predecir valores
¡Pongamos en práctica tu modelo! Con él puedes hacer predicciones para las etiquetas del conjunto de pruebas. Sólo tienes que utilizar predict() y pasarle el conjunto de prueba para que prediga las etiquetas de los datos. En este caso, el resultado se almacena en y_pred:
y_pred = model. predict( X_test) Con la tecnología de
Antes de ir a evaluar tu modelo, ya puedes hacerte una idea rápida de la precisión comprobando cómo se comparan y_pred y y_test:
y_pred[ : 5 ] Con la tecnología de
array( [ [ 0 ] ,
[ 1 ] ,
[ 0 ] ,
[ 0 ] ,
[ 0 ] ] , dtype= int32) Con la tecnología de
y_test[ : 5 ] Con la tecnología de
array( [ 0 , 1 , 0 , 0 , 0 ] ) Con la tecnología de
Ves que estos valores parecen cuadrar, pero ¿qué es todo esto sin algunas cifras concretas?
Evaluar el modelo
Ahora que has construido tu modelo y lo has utilizado para hacer predicciones sobre datos que tu modelo aún no había visto, es hora de evaluar su rendimiento. Puedes comparar visualmente las predicciones con las etiquetas de prueba reales (y_test), o puedes utilizar todo tipo de métricas para determinar el rendimiento real. En este caso, utilizarás evaluate() para hacerlo. Introduce los datos de prueba y las etiquetas de prueba y, si quieres, pon el argumento verbose a 1. Verás que aparecen más registros cuando hagas esto.
score = model. evaluate( X_test, y_test, verbose= 1 )
print ( score) Con la tecnología de
[ 0.025217213829228164 , 0.99487179487179489 ] Con la tecnología de
La puntuación es una lista que contiene la combinación de la pérdida y la precisión. En este caso, ves que ambas parecen muy buenas, pero en este caso conviene recordar que tus datos estaban algo desequilibrados: tenías más observaciones de vino blanco que de vino tinto. Puede que la precisión sólo refleje la distribución de clases de tus datos, ya que sólo predecirá white ¡porque esas observaciones están presentes en abundancia!
Antes de empezar a reordenar los datos y reunirlos de otra forma, siempre es buena idea probar diferentes métricas de evaluación. Para ello, puedes recurrir a scikit-learn (que importas como sklearn, igual que antes cuando hacías los conjuntos de entrenamiento y prueba).
En este caso, probarás algunas técnicas básicas de evaluación de la clasificación, como:
La matriz de confusión, que es un desglose de las predicciones en una tabla que muestra las predicciones correctas y los tipos de predicciones incorrectas realizadas. En el mejor de los casos, sólo verás números en la diagonal, ¡lo que significa que todas tus predicciones eran correctas!
La precisión es una medida de la exactitud de un clasificador. Cuanto mayor sea la precisión, más exacto será el clasificador.
La recuperación es una medida de la exhaustividad de un clasificador. Cuanto mayor sea la recuperación, más casos cubrirá el clasificador.
La puntuación F1 o puntuación F es una media ponderada de la precisión y la recuperación.
El Kappa o kappa de Cohen es la precisión de la clasificación normalizada por el desequilibrio de las clases en los datos.
from sklearn. metrics import confusion_matrix, precision_score, recall_score, f1_score, cohen_kappa_score
confusion_matrix( y_test, y_pred) Con la tecnología de
array( [ [ 1585 , 3 ] ,
[ 8 , 549 ] ] ) Con la tecnología de
precision_score( y_test, y_pred) Con la tecnología de
0.994565217391 Con la tecnología de
recall_score( y_test, y_pred) Con la tecnología de
0.98563734290843807 Con la tecnología de
f1_score( y_test, y_pred) Con la tecnología de
0.99008115419296661 Con la tecnología de
cohen_kappa_score( y_test, y_pred) Con la tecnología de
0.98662321692498967 Con la tecnología de
¡Todas estas puntuaciones son muy buenas! Has hecho un modelo bastante exacto a pesar de que tienes muchas más filas que son del tipo de vino blanco.
¡Buen trabajo!
Algunos experimentos más
Has construido con éxito tu primer modelo, pero puedes ir aún más lejos con éste. ¿Por qué no pruebas las siguientes cosas y ves cuál es su efecto? Como has leído más arriba, las dos decisiones arquitectónicas clave que debes tomar tienen que ver con las capas y los nodos ocultos. Son puntos de partida estupendos:
Has utilizado 1 capa oculta. Intenta utilizar 2 ó 3 capas ocultas;
Utiliza capas con más unidades ocultas o con menos unidades ocultas;
Toma la columna quality como etiquetas de destino y el resto de los datos (¡incluida la columna type codificada!) como tus datos. ¡Ahora tienes un problema de clasificación multiclase!
Pero, ¿por qué no probar también a cambiar la función de activación? En lugar de relu, prueba a utilizar la función de activación tanh ¡y comprueba cuál es el resultado!
Predecir la calidad del vino
¡Tu modelo de clasificación funcionó perfectamente para una primera ejecución!
Pero hay mucho más que puedes hacer además de subir un nivel y probar estructuras más complejas que el perceptrón multicapa. ¿Por qué no intentar hacer una red neuronal para predecir la calidad del vino?
En este caso, el tutorial supone que quality es una variable continua: la tarea no es entonces una tarea de clasificación binaria, sino una tarea de regresión ordinal. Es un tipo de regresión que se utiliza para predecir una variable ordinal: el valor quality existe en una escala arbitraria en la que la ordenación relativa entre los distintos valores quality es significativa. En esta escala, la escala de calidad 0-10 de "muy malo" a "muy bueno" es un ejemplo de ello.
Ten en cuenta que también podrías ver este tipo de problema como un problema de clasificación y considerar las etiquetas de calidad como etiquetas de clase fijas.
En cualquier caso, esta configuración de la situación significaría que tus etiquetas de destino van a ser la columna quality de tus DataFrames red y white para la segunda parte de este tutorial. Esto requerirá un preprocesamiento adicional.
Preprocesar datos
Como la variable quality se convierte en tu clase objetivo, ahora tendrás que aislar las etiquetas de calidad del resto del conjunto de datos. Pondrás wines.quality en una variable diferente y y pondrás los datos de los vinos, a excepción de la columna quality en una variable x.
A continuación, estarás preparado para dividir los datos en conjuntos de entrenamiento y de prueba, pero no seguirás este enfoque en este caso (¡aunque podrías!). En esta segunda parte del tutorial, utilizarás la validación k-fold, que requiere que dividas los datos en K particiones. Normalmente, K se fija en 4 ó 5. A continuación, instanciarás modelos idénticos y entrenarás cada uno en una partición, evaluando también en las particiones restantes. La puntuación de validación del modelo es entonces una media de las K puntuaciones de validación obtenidas.
Verás cómo hacerlo más adelante. Por ahora, utiliza StandardScaler para asegurarte de que tus datos están en un buen lugar antes de ajustarlos al modelo, igual que antes.
# Isolate target labels
y = wines.________
# Isolate data
X = wines.drop('quality', axis=_)
Recuerda que también tienes que volver a realizar el escalado porque tenías muchas diferencias en algunos de los valores de tus datos de red, white (y, en consecuencia, también wines).
Pruébalo en el fragmento Light de DataCamp que aparece a continuación. ¡Se han cargado todas las bibliotecas necesarias para ti!
# Scale the data with `StandardScaler`
X = ________________.fit_transform(X)
Ahora estás de nuevo en el punto en el que estabas hace un rato. ¡Puedes empezar de nuevo a modelar la red neuronal!
Modelo de arquitectura de red neuronal
Ahora que has vuelto a preprocesar los datos, ha llegado de nuevo el momento de construir un modelo de red neuronal, un perceptrón multicapa. Aunque lo utilices para una tarea de regresión, la arquitectura podría ser muy parecida, con dos capas Dense.
No olvides que la primera capa es tu capa de entrada. Tendrás que pasarle la forma de tus datos de entrada. En este caso, ves que vas a utilizar input_dim para pasar las dimensiones de los datos de entrada a la capa Dense.
Observa de nuevo que la primera capa que defines es la capa de entrada. Esta capa necesita conocer las dimensiones de entrada de tus datos. Pasas las dimensiones de entrada, que en este caso son 12 (¡no olvides que también cuentas la columna Type que has generado en la primera parte del tutorial!) De nuevo utilizas la función de activación relu, pero una vez más no hay sesgo. El número de unidades ocultas es 64.
Tu red termina con una sola unidad Dense(1), y no incluye una activación. Esta es una configuración típica de la regresión escalar, en la que intentas predecir un único valor continuo).
Compilar el modelo, ajustar los datos
Con tu modelo a mano, puedes volver a compilarlo y ajustar los datos a él. Pero espera. ¿No necesitas las particiones de validación del pliegue K sobre las que has leído antes? Así es.
import numpy as np
from sklearn. model_selection import StratifiedKFold
seed = 7
np. random. seed( seed)
kfold = StratifiedKFold( n_splits= 5 , shuffle= True , random_state= seed)
for train, test in kfold. split( X, Y) :
model = Sequential( )
model. add( Dense( 64 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 1 ) )
model. compile ( optimizer= 'rmsprop' , loss= 'mse' , metrics= [ 'mae' ] )
model. fit( X[ train] , Y[ train] , epochs= 10 , verbose= 1 ) Con la tecnología de
Utiliza la función compile() para compilar el modelo y luego utiliza fit() para ajustar el modelo a los datos. Para compilar el modelo, asegúrate de nuevo de que defines al menos los argumentos optimizer y loss. En este caso, puedes utilizar rsmprop, uno de los algoritmos de optimización más populares, y mse como función de pérdida, que es muy típica para problemas de regresión como el tuyo.
El argumento adicional metrics que definas es en realidad una función que se utiliza para juzgar el rendimiento de tu modelo. Para los problemas de regresión, es frecuente tomar como métrica el Error Medio Absoluto (MAE). Leerás más sobre esto en la siguiente sección.
Pasa los datos de entrenamiento y las etiquetas a fit(), determina cuántas épocas quieres ejecutar el ajuste, el tamaño del lote y, si quieres, puedes poner el argumento verbose a 1 para obtener más registros, porque esto puede llevar algo de tiempo.
Evaluar el modelo
Al igual que antes, también debes evaluar tu modelo. Además de añadir y_pred = model.predict(X[test]) al resto del código anterior, también sería una buena idea utilizar algunas de las métricas de evaluación de sklearn, como también has hecho en la primera parte del tutorial.
Para ello, puedes utilizar el Error Cuadrático Medio (ECM) y el Error Absoluto Medio (EAM). La primera, también llamada "desviación media al cuadrado" (DMS), mide la media de los cuadrados de los errores o desviaciones. En otras palabras, cuantifica la diferencia entre el estimador y lo que se estima. De este modo, puedes saber algo más sobre la calidad de tu estimador: siempre es no negativo, y los valores más cercanos a cero son mejores.
La última medida de evaluación, MAE, significa Error Absoluto Medio: cuantifica lo cerca que están las predicciones de los resultados finales.
Añade estas líneas al trozo de código anterior, y ten cuidado con las sangrías:
mse_value, mae_value = model. evaluate( X[ test] , Y[ test] , verbose= 0 )
print ( mse_value)
0.522478731072
print ( mae_value)
0.561965950103 Con la tecnología de
Ten en cuenta que, además de las puntuaciones MSE y MAE, también puedes utilizar la puntuación R2 o la función de puntuación de regresión. Aquí, debes buscar una puntuación de 1,0, que es la mejor. Sin embargo, ¡la puntuación también puede ser negativa!
from sklearn. metrics import r2_score
r2_score( Y[ test] , y_pred)
0.3125092543 Con la tecnología de
A primera vista, son cifras bastante horribles, ¿verdad? Lo bueno de esto, sin embargo, es que ahora puedes experimentar optimizando el código para que los resultados sean un poco mejores.
¡De eso trata la siguiente y última sección!
Ajuste del modelo
Afinar tu modelo es algo que probablemente harás a menudo, porque no todos los problemas son tan sencillos como el que has visto en la primera parte de este tutorial. Como has leído más arriba, ya hay dos decisiones críticas que probablemente querrás ajustar: cuántas capas vas a utilizar y cuántas "unidades ocultas" elegirás para cada capa.
Al principio, esto será, en efecto, todo un viaje.
Añadir capas
¿Qué pasaría si añades otra capa a tu modelo? ¿Y si tuviera este aspecto?
model = Sequential( )
model. add( Dense( 64 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 64 , activation= 'relu' ) )
model. add( Dense( 1 ) ) Con la tecnología de
Unidades ocultas
Prueba también el efecto de añadir más unidades ocultas a la arquitectura de tu modelo y estudia el efecto en la evaluación, así:
model = Sequential( )
model. add( Dense( 128 , input_dim= 12 , activation= 'relu' ) )
model. add( Dense( 1 ) ) Con la tecnología de
Ten en cuenta de nuevo que, en general, como no tienes una tonelada de datos, peor puede ser y será el sobreajuste. Por eso debes utilizar una red pequeña.
Algunos experimentos más: Parámetros de optimización
Además de añadir capas y jugar con las unidades ocultas, también puedes intentar ajustar (algunos de) los parámetros del algoritmo de optimización que das a la función compile(). Hasta ahora, siempre has pasado una cadena, como rmsprop, al argumento optimizer.
¡Pero no siempre tiene por qué ser así!
Prueba, por ejemplo, a importar RMSprop desde keras.models y ajusta la tasa de aprendizaje lr. También puedes cambiar los valores por defecto que se han establecido para los demás parámetros de RMSprop(), pero no es recomendable. Puedes obtener más información aquí .
from keras. optimizers import RMSprop
rmsprop = RMSprop( lr= 0.0001 )
model. compile ( optimizer= rmsprop, loss= 'mse' , metrics= [ 'mae' ] ) Con la tecnología de
Prueba también a experimentar con otros algoritmos de optimización, como el Descenso Gradiente Estocástico (SGD). ¿Notas algún efecto?
from keras. optimizers import SGD, RMSprop
sgd= SGD( lr= 0.1 )
model. compile ( optimizer= sgd, loss= 'mse' , metrics= [ 'mae' ] ) Con la tecnología de
¡Ve más lejos!
Este tutorial es sólo un comienzo en tu viaje por el aprendizaje profundo con Python y Keras. Todavía queda mucho por cubrir, así que ¿por qué no haces el curso de Aprendizaje Profundo en Python de DataCamp? Mientras tanto, asegúrate también de consultar la documentación de Keras , si aún no lo has hecho. Encontrarás más ejemplos e información sobre todas las funciones, argumentos, más capas, etc. Sin duda, ¡será un recurso indispensable cuando estés aprendiendo a trabajar con redes neuronales en Python! Si en cambio te apetece leer un libro que explique los fundamentos del aprendizaje profundo (con Keras) junto con cómo se utiliza en la práctica, deberías leer sin duda el libro de François Chollet Deep Learning in Python .
A estas alturas, puede que ya conozcas el aprendizaje automático, una rama de la informática que estudia el diseño de algoritmos capaces de aprender. Hoy te vas a centrar en el aprendizaje profundo, un subcampo del aprendizaje automático que es un conjunto de algoritmos que se inspira en la estructura y función del cerebro. Estos algoritmos suelen denominarse Redes Neuronales Artificiales (RNA). El aprendizaje profundo es uno de los campos más candentes de la ciencia de datos, con muchos casos prácticos que han dado resultados asombrosos en robótica, reconocimiento de imágenes e Inteligencia Artificial (IA).
Una de las bibliotecas de Python más potentes y fáciles de usar para desarrollar y evaluar modelos de aprendizaje profundo es Keras; envuelve las eficientes bibliotecas de cálculo numérico Theano y TensorFlow. La ventaja de esto es principalmente que puedes iniciarte en las redes neuronales de forma fácil y divertida.
El tutorial de hoy sobre Keras para principiantes te introducirá en los fundamentos del aprendizaje profundo en Python: