Redes neuronales
Universidad Tecnológica de Bolivar
2023-07-24
El ámbito de las redes neuronales y su hermano mayor, el deep learning, es complejo y amplio. Durante los últimos años, el interés y la aplicación de este tipo de modelos han experimentado tal expansión que se ha convertido en una disciplina por sí misma.
Si bien entender bien sus fundamentos requiere de una cantidad notable de tiempo y práctica, esto no significa que se necesiten adquirir todos ellos para empezar a sacarles partido.
Deep learning: son modelos más complejos (perceptrón multicapa, redes convolucionales, redes recurrentes, LSTM…) cuyos requerimientos computacionales hacen necesario el uso de muy optimizado de CPUs o GPUs. Para este tipo de modelos se tiene que recurrir a frameworks especializados como H2O, Tensorflow-Keras o Torch.
En este documento se muestran ejemplos de modelos relativamente sencillos (perceptrón simple y perceptrón multicapa) haciendo uso del paquete H2O.
Representación de una red neuronal
Para facilitar la comprensión de la estructura de las redes, es útil representar una red equivalente a un modelo de regresión lineal.
\[y = w_1 x_1 + ... + w_d x_d + b\]
Cada neurona de la capa de entrada representa el valor de uno de los predictores. Las flechas representan los coeficientes de regresión, que en términos de redes se llaman pesos, y la neurona de salida representa el valor predicho. Para que esta representación equivalga a la ecuación de un modelo lineal, faltan dos cosas:
El bias(sesgo) del modelo.
Las operaciones de multiplicación y suma que combinan el valor de los predictores con los pesos del modelo.
Cada neurona de la capa intermedia tiene un valor de bias(sesgo), pero suele omitirse en las representaciones gráficas. En cuanto a las operaciones matemáticas, es el elemento clave que ocurre dentro de las neuronas y conviene verlo con detalle.
La neurona es la unidad funcional de los modelos de redes. Dentro de cada neurona ocurren simplemente dos operaciones: la suma ponderada de sus entradas y la aplicación de una función de activación.
Si bien el valor que llega a la neurona, siempre es una combinación lineal, gracias a la función de activación, se pueden generar salidas muy diversas. Es en la función de activación donde reside el potencial de los modelos de redes para aprender relaciones no lineales.
El valor neto de entrada a una neurona es la suma de los valores que le llegan, ponderados por el peso de las conexiones, más el bias.
\[entrada=\sum_{i=1}^{n}x_iw_i+b =\textbf{XW}+b\]
A la entrada se le aplica la función de activación (\(g\)) que lo transforma en en el valor de activación (\(a\)), (\(a\) es el valor final de la neurona).
\[a=g(entrada)=g(\textbf{XW}+b)\]
Las funciones de activación controlan en gran medida qué información se propaga desde una capa a la siguiente (forward propagation). Estas funciones convierten el valor neto de entrada a la neuronal (combinación de los input, pesos y bias) en un nuevo valor.
Gracias a combinar funciones de activación no lineales con múltiples capas (ver más adelante), los modelos de redes son capaces de aprender relaciones no lineales.
La gran mayoría de funciones de activación convierten el valor de entrada neto de la neurona en un valor dentro del rango (0, 1) o (-1, 1).
Cuando el valor de activación de una neurona (salida de su función de activación) es cero, se dice que la neurona está inactiva, ya que no pasa ningún tipo de información a las siguientes neuronas. A continuación, se describen las funciones de activación más empleadas.
La función de activación ReLu aplica una transformación no lineal muy simple, activa la neurona solo si el input está por encima de cero.
\[ReLU(x)=max(x,0)\]
De esta forma, la función de activación retiene únicamente los valores positivos y descarta los negativos dándoles una activación de cero.
ReLU es con diferencia la función de activación más empleada por sus buenos resultados en aplicaciones diversas. La razón de esto reside en el comportamiento de su derivada (gradiente), que es cero o constante.
La función sigmoide transforma valores en el rango de \((-\infty, +\infty)\) a valores en el rango (0, 1).
\[\operatorname{sigmoid}(x) = \frac{1}{1 + e^{-x}}\]
Aunque la función de activación sigmoide se utilizó mucho en los inicios de los modelos de redes, en la actualidad, suele preferirse la función ReLU.
Un caso en el que la función de activación sigmoide sigue siendo la función utilizada por defecto es en las neuronas de la capa de salida de los modelos de clasificación binaria (usada también en regresión logistica), ya que su salida puede interpretarse como probabilidad.
La función de activación Tanh, se comporta de forma similar a la función sigmoide, pero su salida está acotada en el rango (-1, 1).
\[\operatorname{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}\]
Sin las funciones de activación, las redes neuronales solo pueden aprender relaciones lineales.
A continuación, na tabla que contiene otras funciones de activación.
La función de coste (l), también llamada función de pérdida, loss function o cost function, es la encargada de cuantificar la distancia entre el valor real y el valor predicho por la red, en otras palabras, mide cuánto se equivoca la red al realizar predicciones.
En la mayoría de casos, la función de coste devuelve valores positivos.
La función de coste puede calcularse para:
Es el segundo caso el que se utiliza para dirigir el entrenamiento de los modelos.
Dependiendo del tipo de problema, regresión o clasificación, tenemos las funciones de coste:
El error cuadrático medio (mean squared error, MSE) es con diferencia la función de coste más utilizada en problemas de regresión. El error cuadrático se calcula como la diferencia al cuadrado entre el valor predicho \(\hat{y}\) y el valor real \(y\).
\[L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l_i(\mathbf{w}, b) = \frac{1}{n}\sum_{i=1}^n \left(\hat{y}_i - y_i\right)^2\]
Las funciones de coste suelen escribirse con la notación \(l(\textbf{w},b)\) para hacer referencia a que su valor depende de los pesos y bias del modelo, ya que son estos los que determinan el valor de las predicciones \(y_i\).
Cuando un modelo se entrena utilizando el error cuadrático medio como función de coste, está aprendiendo a predecir la media de la variable respuesta.
El error medio absoluto (mean absolute error, MAE) consiste en promediar el error absoluto de las predicciones.
\[L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n |\hat{y}_i - y_i|\]
En problemas de clasificación, la capa de salida utiliza como función de activación la función softmax.
Cuando la clasificación es de tipo binaria, donde la variable respuesta es 1 o 0, y \(p=Pr(y=1)\), la función de coste log-loss se define como:
\[L_{\log}(y, p) = -\log \operatorname{Pr}(y|p) = -(y \log (p) + (1 - y) \log (1 - p))\] Para problemas de clasificación con más de dos clases, esta fórmula se generaliza a:
\[L_{\log}(Y, P) = -\log \operatorname{Pr}(Y|P) = - \frac{1}{N} \sum_{i=0}^{N-1} \sum_{k=0}^{K-1} y_{i,k} \log p_{i,k}\]
En ambos casos, minimizar esta la función equivale a que la probabilidad predicha para la clase correcta tienda a 1 y a 0 en las demás clases.
El modelo de red neuronal con una única capa (single-layer perceptron), aunque supuso un gran avance en el campo del machine learning, solo es capaz de aprender patrones sencillos. Para superar esta limitación, los investigadores descubrieron que, combinando múltiples “capas ocultas”, la red puede aprender relaciones mucho más complejas entre los predictores y la variable respuesta. A esta estructura se le conoce como perceptrón multicapa o multilayer perceptron (MLP), y puede considerarse como el primer modelo de deep learning.
El proceso de entrenamiento de una red neuronal consiste en ajustar el valor de los pesos y bias de tal forma que, las predicciones que se generen, tengan el menor error posible. Gracias a esto, el modelo es capaz de identificar qué predictores tienen mayor influencia y de qué forma están relacionados entre ellos y con la variable respuesta.
La idea intuitiva de cómo entrenar una red neuronal es la siguiente:
Iniciar la red con valores aleatorios de los pesos y bias.
Para cada observación de entrenamiento, calcular el error que comete la red al hacer su predicción. Promediar los errores de todas las observaciones.
Identificar la responsabilidad que ha tenido cada peso y bias en el error de las predicciones.
Modificar ligeramente los pesos y bias de la red (de forma proporcional a su responsabilidad en el error) en la dirección correcta para que se reduzca el error.
Repetir los pasos 2, 3, 4 y 5 hasta que la red sea suficientemente buena.
Si bien la idea parece sencilla, alcanzar una forma de implementarla ha requerido la combinación de múltiples métodos matemáticos, en concreto, el algoritmo de retropropagación (backpropagation) y la optimización por descenso de gradiente (gradient descent).
Si bien la idea del entrenamiento parece sencilla, alcanzar una forma de implementarla ha requerido la combinación de múltiples métodos matemáticos, en concreto, el algoritmo de retropropagación (backpropagation) y la optimización por descenso de gradiente (gradient descent).
Backpropagation es el algoritmo que permite cuantificar la influencia que tiene cada peso y bias en las predicciones de la red. Para conseguirlo, hace uso de la regla de la cadena (chain rule) para calcular el gradiente, que no es más que el vector formado por las derivadas parciales de una función.
En el caso de las redes, la derivada parcial del error respecto a un parámetro (peso o bias) mide cuánta “responsabilidad” ha tenido ese parámetro en el error cometido. Gracias a esto, se puede identificar qué pesos de la red hay que modificar para mejorarla. El siguiente paso necesario, es determinar cuánto y cómo modificarlos (optimización).
Descenso de gradiente (gradient descent) es un algoritmo de optimización que permite minimizar una función haciendo actualizaciones de sus parámetros en la dirección del valor negativo de su gradiente. Aplicado a las redes neuronales, el descenso de gradiente permite ir actualizando los pesos y bias del modelo para reducir su error.
A la hora de entrenar modelos basados en redes neuronales, es necesario aplicar a los datos, al menos, dos tipos de transformaciones.
Binarización (one hot encoding) de las variables categóricas
La binarización (one-hot-encoding) consiste en crear nuevas variables dummy con cada uno de los niveles de las variables cualitativas. Por ejemplo, una variable llamada color que contenga los niveles rojo, verde y azul, se convertirá en tres nuevas variables (color_rojo, color_verde, color_azul), todas con el valor 0 excepto la que coincide con la observación, que toma el valor 1.
Cuando los predictores son numéricos, la escala en la que se miden, así como la magnitud de su varianza pueden influir en gran medida en el modelo. Existen principalmente 2 estrategias para escalar:
Centrado: consiste en restarle a cada valor la media del predictor al que pertenece. Si los datos están almacenados en un dataframe, el centrado se consigue restándole a cada valor la media de la columna en la que se encuentra. Como resultado de esta transformación, todos los predictores pasan a tener una media de cero, es decir, los valores se centran en torno al origen.
Normalización (estandarización): consiste en transformar los datos de forma que todos los predictores estén aproximadamente en la misma escala.
Normalización Z-score: dividir cada predictor entre su desviación típica después de haber sido centrado, de esta forma, los datos pasan a tener un rango de \((-\infty, +\infty)\)
Estandarización max-min: transformar los datos de forma que estén dentro del rango [0, 1].
El sobreajuste es un comportamiento de aprendizaje automático no deseado que se produce cuando el modelo de aprendizaje automático ofrece predicciones precisas para los datos de entrenamiento, pero no para los datos nuevos.
Solo obtiene predicciones precisas si el modelo de aprendizaje automático se generaliza a todos los tipos de datos dentro de su dominio. El sobreajuste ocurre cuando el modelo no puede generalizar y se ajusta demasiado al conjunto de datos de entrenamiento. El sobreajuste ocurre por varios motivos, como:
Considere un caso de uso en el que un modelo de aprendizaje automático tiene que analizar fotos e identificar las que contienen perros. Si el modelo de aprendizaje automático se entrenó en un conjunto de datos que contenía la mayoría de las fotos que mostraban perros afuera en los parques, es posible que aprenda a usar el césped como una característica para la clasificación y es posible que no reconozca a un perro dentro de una habitación.
Otro ejemplo de sobreajuste es un algoritmo de aprendizaje automático que predice el rendimiento académico y el resultado de la graduación de un estudiante universitario mediante el análisis de varios factores, como los ingresos familiares, el rendimiento académico anterior y las calificaciones académicas de los padres. Sin embargo, los datos de la prueba solo incluyen candidatos de un género o grupo étnico específico. En este caso, el sobreajuste hace que la precisión de la predicción del algoritmo disminuya para los candidatos con género o etnia fuera del conjunto de datos de prueba.
La gran “flexibilidad” que tienen las redes neuronales es un arma de doble filo. Por un lado, son capaces de generar modelos que aprenden relaciones muy complejas, sin embargo, tenemos los siguiente problema que se presentas a veces:
Lo que los incapacita al tratar de predecir nuevas observaciones. La forma de minimizar este problema y conseguir modelos útiles pasa por configurar de forma adecuada sus hiperparámetros. Son muchos los hiperparámetros de un modelo basado en redes y su nomenclatura varía de unas implementaciones a otras, sin embargo, los de mayor impacto siempre están presentes:
La arquitectura de una red, el número de capas y el número de neuronas que forman parte de cada capa, determinan en gran medida la complejidad del modelo y con ello su potencial capacidad de aprendizaje.
Cuantas más neuronas y capas, mayor la complejidad de las relaciones que puede aprender el modelo. Sin embargo, dado que cada neurona está conectada por pesos al resto de neuronas de las capas adyacentes, el número de parámetros a aprender aumenta y con ello el tiempo de entrenamiento.
Los métodos de regularización tienen el objetivo de reducir el sobreajuste (overfitting) de los modelos. Un modelo con sobreajuste memoriza los datos de entrenamiento pero es incapaz de predecir correctamente nuevas observaciones. Entre las muchas que existen, se destacan la regularización L1 y L2 (weight decay) y el dropout.
El objetivo de la regularización L1 y L2, esta última también conocida como weight decay, es evitar que los pesos tomen valores excesivamente elevados. De esta forma, se evita que unas pocas neuronas dominen el comportamiento de la red y se fuerza a que las características poco informativas (ruido) tengan pesos próximos o iguales a cero.
Este proceso consiste en desactivar aleatoriamente una serie de neuronas durante el proceso de entrenamiento. En concreto, durante cada iteración del entrenamiento, se ponen a cero los pesos de una fracción aleatoria de neuronas por capa. El método de dropout, descrito por Srivastava et al. en 2014, se ha convertido en un estándar para entrenar redes neuronales. El porcentaje de neuronas que suele desactivarse por capa (dropout rate) suele ser un valor entre 0.2 y 0.5.
H2O es un conjunto de herramientas y librerias que tienen el objetivo de combinar los principales algoritmos de machine learning y aprendizaje estadístico con el Big Data.
Comprimir y almacenar los datos: H2O es capaz de trabajar con millones de registros en un único ordenador (emplea todos sus cores) o en un cluster de muchos ordenadores.
Escalabilidad: sus algoritmos son igualmente útiles cuando se trabaja con un volumen de datos reducido.
standardize: por defecto, se estandarizan los valores para que tengan media cero y varianza uno. Los modelos basados en redes no son independientes de la escala de los predictores, por lo que es fundamental estandarizarlos.
missing_values_handling: las redes neuronales no aceptan observaciones con valores ausentes. H2O ofrece la opción de excluirlos Skip o imputarlos con la media MeanImputation.
shuffle_training_data: mezclar aleatoriamente las observaciones antes de entrenar el modelo.
categorical_encoding: codificado de las variables categóricas.
activation: función de activación de las neuronas de las capas intermedias (Tahn, Tahn with dropout, Rectifier, Rectifier with dropout, Maxout, Maxout with dropout). La función de activación de la capa de salida se selecciona automáticamente dependiendo de si es un problema de regresión o clasificación.
loss: función de coste que se intenta minimizar durante el entrenamiento de la red. Esta es la forma en que se cuantifica el error que comete la red y en función de ello aprende, por lo tanto, es importante escoger la función de coste más adecuada para el problema en cuestión (esto es así para todos los algoritmos, no solo para redes).
epochs: número de iteraciones de aprendizaje durante el entrenamiento de la red. Encontrar el valor óptimo de épocas es muy importante ya que, si son muy pocas, la red puede no aprender lo suficiente, pero si son demasiadas, se produce overfitting.
adaptive_rate: si se indica esta opción (activada por defecto), se emplea el algoritmo (ADADELTA) como algoritmo de aprendizaje. Con él, se consigue que el learning rate se adapte automáticamente (reduciéndose) a medida que el entrenamiento avanza. Es recomendable probar en primer lugar esta opción, ya que consigue un balance entre velocidad de aprendizaje y resultados bastante bueno. Este algoritmo está controlado por dos parámetros:
En este primer ejemplo se muestra cómo, dependiendo de la arquitectura (capas ocultas y tamaño de las mismas), un modelo basado en redes neuronales puede aprender funciones no lineales con gran facilidad. Para evitar problemas de overfitting, es importante identificar la combinación de hiperparámetros que consigue un equilibrio adecuado en el aprendizaje. Se trata de un ejemplo muy sencillo, cuyo objetivo es que el lector se familiarice con la flexibilidad que tienen este tipo de modelos y en cómo realizar una búsqueda de hiperparámetros mediante grid search y validación cruzada.
Los paquetes utilizadas en este ejemplo son:
Una vez que la librería H2O ha sido cargada, hay que iniciar el cluster. Para este ejemplo, se emplea un único ordenador del que se utilizan todos sus cores en paralelo.
Se simulan observaciones en dos dimensiones, pertenecientes a tres grupos, cuya separación no es lineal.
Grafiquemos los datos
Creación de los datos y la partición (train y test) de estos en H2O
datos_h2o <- as.h2o(datos)
particiones <- h2o.splitFrame(data = datos_h2o, ratios = c(0.6, 0.2), seed = 123)
datos_train <- h2o.assign(data = particiones[[1]], key = "datos_train")
datos_validation <- h2o.assign(data = particiones[[2]], key = "datos_validacion")
datos_test <- h2o.assign(data = particiones[[3]], key = "datos_test")Se procede a crear 4 modelos en orden creciente de complejidad (número de neuronas y capas), para comprobar cómo la arquitectura de la red afecta a su capacidad de aprendizaje.
Modelo 1
Modelo 2
Modelo 3
Modelo 4
# predicciones de cada modelo agregadas a los datos
grid_predicciones$modelo_1 <- as.vector(predicciones_1$predict)
grid_predicciones$modelo_2 <- as.vector(predicciones_2$predict)
grid_predicciones$modelo_3 <- as.vector(predicciones_3$predict)
grid_predicciones$modelo_4 <- as.vector(predicciones_4$predict)# Gráfico de predicciones
# ==============================================================================
p1 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_1)) +
geom_point(size = 0.5) +
theme_fivethirtyeight() +
labs(title = "Arquitectura: (5)") +
theme_bw()
p2 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_2)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (10)") +
theme_bw()
p3 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_3)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (20, 20)") +
theme_bw()
p4 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_4)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (50, 50, 50)") +
theme_bw()
ggarrange(p1, p2, p3, p4, nrow = 2, ncol = 2)Puede observarse cómo, a medida que aumenta la complejidad de la red (más neuronas y más capas), las fronteras de decisión se adaptan más y más a los datos de entrenamiento.
En este apartado, se muestra cómo afectan al aprendizaje algunos de los hiperparámetros más influyentes. Para ello, se combinan los métodos de grid search, random search y early stopping.
Búsqueda cartesiana probando todos los valores definidos por el usuario.
# Número de neuronas
hiperparametros <- list(hidden = c(1, 5, 10, 15, 25, 50, 100, 300, 500))
grid_dl <- h2o.grid(
algorithm = "deeplearning",
activation = "Rectifier",
adaptive_rate = FALSE,
epochs = 100,
# Variable respuesta y predictores
x = c("x_1", "x_2"),
y = "y",
training_frame = datos_train,
# validation_frame = datos_validation, # Para validación simple
nfolds = 3, # validación cruzada
standardize = TRUE,
hyper_params = hiperparametros,
search_criteria = list(strategy = "Cartesian"),
seed = 123,
grid_id = "grid_dl"
)# Se muestran los modelos ordenados de mayor a menor accuracy
resultados_grid <- h2o.getGrid(
sort_by = 'accuracy',
grid_id = "grid_dl",
decreasing = TRUE
)
data.frame(resultados_grid@summary_table) %>%
mutate(
accuracy = as.numeric(accuracy),
hidden = str_remove_all(hidden, pattern = "\\[|\\]"),
hidden = as.numeric(hidden),
) %>%
ggplot(aes(x=hidden, y=accuracy, group=1)) +
geom_line() +
geom_point() +
labs(title="Accuracy del modelo vs número de neuronas") +
theme_bw()# Learning rate (Tasa de aprendisaje)
hiperparametros <- list(rate = c(0.00001, 0.0001, 0.0001, 0.001, 0.01, 0.1, 1, 10))
grid_dl_2 <- h2o.grid(
algorithm = "deeplearning",
activation = "Rectifier",
adaptive_rate = FALSE,
rate_annealing = 0,
rate_decay = 0,
nesterov_accelerated_gradient = FALSE,
hidden = 10,
epochs = 50,
x = c("x_1", "x_2"),
y = "y",
training_frame = datos_train,
# validation_frame = datos_validation, # Para validación simple
nfolds = 3, # validación cruzada
standardize = TRUE,
hyper_params = hiperparametros,
search_criteria = list(strategy = "Cartesian"),
seed = 123,
grid_id = "grid_dl_2"
)# Se muestran los modelos ordenados de mayor a menor accuracy
resultados_grid <- h2o.getGrid(
sort_by = 'accuracy',
grid_id = "grid_dl_2",
decreasing = TRUE
)
data.frame(resultados_grid@summary_table) %>% select(-model_ids) %>%
mutate(accuracy = as.numeric(accuracy),
rate = as.numeric(rate)) %>%
ggplot(aes(x=rate, y=accuracy, group=1)) +
scale_x_continuous(trans='log10') +
geom_line() +
geom_point() +
labs(title="Accuracy del modelo vs learning rate") +
theme_bw()Si bien los dos ejemplos anteriores sirven para tener una idea intuitiva de cómo afecta cada hiperparámetro, no es posible optimizarlos de forma individual, ya que el impacto final que tiene cada uno depende de qué valor tomen los demás. La búsqueda de hiperparámetros debe hacerse en conjunto.
# Learning rate + número de neuronas
hiperparametros <- list(
rate = c(0.00001, 0.0001, 0.0001, 0.001, 0.01, 0.1, 1),
hidden = c(1, 5, 10, 15, 25, 50, 100, 300, 500)
)
grid_dl_3 <- h2o.grid(
algorithm = "deeplearning",
activation = "Rectifier",
adaptive_rate = FALSE,
rate_annealing = 0,
rate_decay = 0,
nesterov_accelerated_gradient = FALSE,
epochs = 50,
x = c("x_1", "x_2"),
y = "y",
training_frame = datos_train,
# validation_frame = datos_validation, # Para validación simple
nfolds = 3, # validación cruzada
standardize = TRUE,
hyper_params = hiperparametros,
search_criteria = list(strategy = "Cartesian"),
seed = 123,
grid_id = "grid_dl_3"
)
resultados_grid <- h2o.getGrid(
sort_by = 'accuracy',
grid_id = "grid_dl_3",
decreasing = TRUE
)
data.frame(resultados_grid@summary_table)Una de las características de los modelos de deep learning es que, con el número suficiente de épocas, el modelo final tiende a ajustarse perfectamente a los datos de entrenamiento causando overfitting. Este comportamiento implica que el analista tiene que encontrar el número adecuado de épocas y, para ello, suele tener que entrenar el modelo con cientos o miles de épocas hasta identificar el momento en el que empieza el overfitting. Esto suele ser poco eficiente en términos de tiempo, ya que, posiblemente, se estén realizando iteraciones innecesarias.
De nuevo, la experiencia de los creadores de H2O queda reflejada en su herramienta, esta vez incluyendo toda una serie de estrategias para detener el proceso de ajuste de un modelo Deeplearning a partir del momento en el que este deja de mejorar. Por defecto, la métrica empleada se calcula con los datos de validación. Tres argumentos controlan la estrategia de parada:
stopping_metric: métrica empleada para cuantificar cuánto mejora el modelo.
stopping_tolerance: porcentaje mínimo de mejora entre dos mediciones consecutivas por debajo del cual se considera que el modelo no ha mejorado.
stopping_rounds: número de mediciones consecutivas en las que no se debe superar el stopping_tolerance para que el algoritmo se detenga.
Detener el entrenamiento de un modelo cuando el error de clasificación no se reduce más de un 1% durante dos mediciones consecutivas: stopping_rounds=2, stopping_tolerance=0.01 y stopping_metric=“misclassification”.
Detener el entrenamiento de un modelo cuando el logloss no se reduce nada durante 3 mediciones consecutivas: stopping_rounds=3, stopping_tolerance=0 y stopping_metric=“logloss”.
Detener el entrenamiento de un modelo cuando la media de AUC no aumenta más de un 0.1% durante cinco mediciones consecutivas: stopping_rounds=5, stopping_tolerance=0.001 y stopping_metric=“AUC”.
También es posible detener el entrenamiento del modelo cuando se supera un tiempo máximo especificado con el argumento max_runtimesecs > 0.
modelo <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = datos_train,
validation_frame = datos_validation,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
rate = 0.1,
hidden = 10,
stopping_rounds = 5,
stopping_metric = "misclassification",
stopping_tolerance = 0.001,
score_validation_samples = 1000,
epochs = 100000000,#Múchas epocas
seed = 123)
modelo@model$scoring_history
plot(modelo)Dado el elevado número de hiperparámetros que tienen los modelos de redes neuronales, la combinación de posibles configuraciones es muy elevada. Esto hace que la búsqueda de hiperparámetros por grid search cartesiano (todas las combinaciones) sea poco práctica. En su lugar, suele emplearse random grid search, que hace una búsqueda de combinaciones aleatorias.
Algunas combinaciones aleatorias pueden ser muy poco favorables, por esta razón es conveniente activar la parada temprana.
# Hiperparámetros que se quieren optimizar mediante búsqueda aleatoria.
# Se definen los posibles valores de cada hiperparámetro, entre los que se
# escoge aleatoriamente.
hiperparametros <- list(
activation = c("Rectifier", "Maxout", "Tanh", "RectifierWithDropout"),
hidden = list(c(5), c(10), c(50), c(10, 10)),
l1 = c(0, 0.00001, 0.0001),
l2 = c(0, 0.00001, 0.0001),
rate = c(0, 01, 0.005, 0.001),
rate_annealing = c(1e-8, 1e-7, 1e-6),
rho = c(0.9, 0.95, 0.99, 0.999),
epsilon = c(1e-10, 1e-8, 1e-6, 1e-4),
momentum_start = c(0, 0.5),
momentum_stable = c(0.99, 0.5, 0),
input_dropout_ratio = c(0, 0.1, 0.2),
max_w2 = c(10, 100, 1000, 3.4028235e+38)
)
# Al ser una búsqueda aleatoria, hay que indicar criterios de parada.
search_criteria <- list(
strategy = "RandomDiscrete",
max_runtime_secs = 5*60, # Tiempo máximo de búsqueda (5 minutos)
max_models = 100, # Número máximo de modelos
stopping_tolerance = 0.01,
stopping_rounds = 5,
seed = 1234
)
grid_dl_4 <- h2o.grid(
algorithm = "deeplearning",
epochs = 5000,
x = c("x_1", "x_2"),
y = "y",
training_frame = datos_train,
# validation_frame = datos_validation, # Validación simple
nfolds = 3, # validación cruzada
standardize = TRUE,
hyper_params = hiperparametros,
search_criteria = search_criteria,
seed = 123,
grid_id = "grid_dl_4"
)
resultados_grid <- h2o.getGrid(
sort_by = 'accuracy',
grid_id = "grid_dl_4",
decreasing = TRUE
)
data.frame(resultados_grid@summary_table)El set de datos SaratogaHouses del paquete mosaicData contiene información sobre la precio de 1728 viviendas situadas en Saratoga County, New York, USA en el año 2006. Además del precio, incluye 15 variables adicionales:
El objetivo es obtener un modelo de red neuronal capaz de predecir el precio de la vivienda.
Renombramos las columnas para que sean más descriptivas
# Se renombran las columnas para que sean más descriptivas
colnames(datos) <- c("precio", "metros_totales", "antiguedad", "precio_terreno",
"metros_habitables", "universitarios",
"dormitorios", "chimenea", "banyos", "habitaciones",
"calefaccion", "consumo_calefacion", "desague",
"vistas_lago","nueva_construccion", "aire_acondicionado")Antes de entrenar un modelo predictivo, o incluso antes de realizar cualquier cálculo con un nuevo conjunto de datos, es importante realizar una exploración descriptiva de los mismos. Este proceso permite entender mejor qué información contiene cada variable, así como detectar posibles errores.
Tabla resumen
Todas las columnas tienen el tipo adecuado.
Gráfico de los missing values
Las columnas están completas, no hay valores ausentes.
Distribuciónd de la variable respuesta
Summary de los datos
Gráfico de distribución para cada variable numérica
La variable chimenea, aunque es de tipo numérico, apenas toma unos pocos valores y la gran mayoría de observaciones pertenecen a solo dos de ellos. En casos como este, suele ser conveniente tratar la variable como cualitativa.
Valores observados de chimenea
Se convierte la variable chimenea a factor
# Grafico para cada variable cualitativa
plot_bar(
datos,
ncol = 3,
title = "Número de observaciones por grupo",
ggtheme = theme_bw(),
theme_config = list(
plot.title = element_text(size = 14, face = "bold"),
strip.text = element_text(colour = "black", size = 8, face = 2),
legend.position = "none"
)
)Si alguno de los niveles de una variable cualitativa tiene muy pocas observaciones en comparación a los otros niveles, puede ocurrir que, durante la validación cruzada o bootstrapping, algunas particiones no contengan ninguna observación de dicha clase (varianza cero), lo que puede dar lugar a errores. Para este caso, hay que tener precaución con la variable chimenea. Se unifican los niveles de 2, 3 y 4 en un nuevo nivel llamado “2_mas”.
Recodificamos la variable chimenea
Con el objetivo de poder estimar el error que comete el modelo al predecir nuevas observaciones, se dividen los datos en dos grupos, uno de entrenamiento y otro de test (80%, 20%).
Los modelos de redes neuronales requieren como mínimo de dos tipos de preprocesado: binarización (one hot encoding) de las variables categóricas y estandarización de las variables continuas.
# Se almacenan en un objeto `recipe` todos los pasos de preprocesado y, finalmente,
# se aplican a los datos.
transformer <- recipe(
formula = precio ~ .,
data = datos_train
) %>%
step_naomit(all_predictors()) %>%
step_nzv(all_predictors()) %>%
step_center(all_numeric(), -all_outcomes()) %>%
step_scale(all_numeric(), -all_outcomes()) %>%
step_dummy(all_nominal(), -all_outcomes())
transformerUna vez que se ha definido el objeto recipe, con la función prep() se aprenden las transformaciones con los datos de entrenamiento y se aplican a los dos conjuntos con bake().
Tras el preprocesado de los datos, se han generado un total de 19 variables (18 predictores y la variable respuesta).
Inicialización del cluster
Se eliminan los datos del cluster por si ya había sido iniciado
Se transfieren los datos al cluster de H2O.
Búsqueda de los hiperparámetros
Búsqueda por validación cruzada
# Búsqueda por validación cruzada
variable_respuesta <- 'precio'
predictores <- setdiff(colnames(datos_train),
variable_respuesta)
grid <- h2o.grid(
algorithm = "deeplearning",
activation = "Rectifier",
x = predictores,
y = variable_respuesta,
training_frame = datos_train,
nfolds = 3, #validacion cruzada
standardize = FALSE,
hyper_params = hiperparametros,
search_criteria = list(strategy = "Cartesian"),
seed = 123,
grid_id = "grid"
)Resultados del grid
Mejor modelo encontrado
Aunque mediante los métodos de validación se consiguen buenas estimaciones del error que tiene un modelo al predecir nuevas observaciones, la mejor forma de evaluar un modelo final es prediciendo un conjunto test, es decir, un conjunto de observaciones que se ha mantenido al margen del proceso de entrenamiento y optimización.
La combinación de hiperparámetros con la que se obtienen mejores resultados acorde a las métricas de validación cruzada es:
https://raw.githubusercontent.com/deepanshu88/Datasets/master/UploadedFiles/german_credit.csv