Más sobre ciencia de datos en cienciadedatos.net
Versión PDF
Attribute wise learning for scoring outliers (ALSO) es un algoritmo no supervisado de detección de anomalías (outliers) cuando los datos no están etiquetados. Para cada variable disponible en el set de datos, se entrena un modelo de regresión que predice dicho atributo en función del resto de variables. El grado de anomalía de cada observación se calcula como el agregado del error cuadrático de los modelos al tratar de predecirla.
El término anomalía o outlier hace referencia a un observación que se desvía notablemente del resto de la muestra, es decir, es inconsistente con el patrón que siguen la mayoría de los datos. El objetivo de los métodos de detección de anomalías es encontrar estas desviaciones y así poder identificar los comportamientos atípicos. Estos métodos pueden clasificarse en tres grupos dependiendo del tipo de datos disponible para entrenarlos:
Supervisados: modelos de clasificación entrenados con datos para los que se conoce el tipo (normal o anomalía) de cada observación.
Semi-supervisados: modelos entrenados únicamente con observaciones pertenecientes a uno de los grupos, por lo general, el normal.
No supervisados: modelos entrenados con datos de los que se desconoce por completo si se trata de observaciones anómalas o normales.
Attribute wise learning for scoring outliers (ALSO) es un algoritmo no supervisado para la detección de anomalías. Para cada variable disponible en el set de datos, se entrena un modelo de regresión que predice dicho atributo en función del resto de variables. El grado de anomalía de cada observación se obtiene a partir el agregado del error cuadrático de los modelos al tratar de predecirla. De esta forma, ALSO reformula el problema, inicialmente no supervisado, en una combinación de problemas supervisados, permitiendo así hacer uso de cualquiera de los algoritmos supervisados disponibles (regresión lineal, random forest, boosting…).
La idea en la que está inspirado ALSO es la siguiente: las anomalías se caracterizan por alejarse del patrón general que siguen la mayoría de los datos. Un modelo capaz aprender este patrón, tendrá problemas al tratar de predecir las anomalías ya que no se comportan acorde a lo esperado por el modelo. Esto significa que las anomalias tendrán un error cuadrático más elevado que el resto de predicciones. Véase la siguiente imagen en la que se muestra la relación entre dos variables a través de un modelo lineal lm()
. Cada punto representa una observación y, el segmento que los une a la recta del modelo, es el error de predicción. Puede observarse como, la anomalía marcada en rojo, tiene un error muy superior.
Este ejemplo es una simplificación para ilustrar la idea. En la práctica, los datos suelen tener múltiples variables (dimensiones) y solo en algunas de ellas las anomalías presentan comportamientos atípicos. Por esta razón, el algoritmo ALSO repite este proceso con cada una de las variables disponibles y, finalmente, agrega el error de todos los modelos para crear una puntuación de anomalía.
Consideraciones prácticas
Algunas consideraciones adicionales se deben de tener en cuenta a la hora de implementar este algoritmo.
Para evitar problemas de overfitting, el proceso de entrenamiento-predicción de cada modelo debe hacerse empleando una estrategia de valición cruzada. Con esto se asegure que, cuando se predice una observación, esta no ha participado en el entrenamiento.
Dado que la magnitud del error depende de las unidades en las que se mide cada variable, los datos tienen que ser estandarizados (media cero y desviación típica 1).
Acorde a la idea de que las anomalías se detectan por desviaciones del patrón general, estas solo pueden encontrarse si dicho patrón existe. Esto significa que, las variables no informativas, no deben de contribuir a la métrica final ya que carecen de patrón. Para evitar esto ocurra, el algoritmo ALSO pondera el error de cada modelo en función su capacidad para capturar un patrón en los datos. La forma de calcular el peso de cada modelo es la siguiente: como los datos han sido estandarizados, el error cuadrático medio (RMSE) de un modelo que únicamente predice la media es 1. Este es el error esperado de un modelo que no ha identificado ningún patrón en los datos y por lo tanto puede emplearse como referencia. Si se calcula el peso de un modelo k (\(w_k\)) como \[w_k = 1 - \text{min{1, RMSE(modelo k)}}\] todo modelo que prediga igual o peor que empleando simplemente la media tendrá un peso \(w_k = 0\). Teniendo en cuenta todo esto, la puntuación de anomalía \(s\) de una observación \(X_i\) perteneciente a un dataset con \(p\) variables es:
\[s(X_i) = \sum^p_{k=1} w_k \ error^2_k(X_i)\]
Al ser un método no supervisado, no hay forma de conocer el valor óptimo a partir del cual se debe de considerar que se trata de una anomalía. La puntuación asignada a cada observación es una medida relativa respecto al resto de observaciones. En la práctica, suelen considerarse como potenciales outliers aquellas observaciones cuya puntuación predicha está por encima de un determinado cuantil.
Dado que cada modelo es independiente del resto, el algoritmo es fácilmente paralelizable.
ALSO es robusto en escenarios con muchas variables (alta dimensionalidad) en los que un porcentaje elevado de variables no son informativas. Esta ventaja es consecuencia de el algoritmo no emplea el concepto de densidad ni distancia para cuantificar el grado de anomalía y de que es capaz de dar peso 0 a las variables que no presentan ningún patrón.
Si se emplean modelos interpretables (modelos lineales, árboles…), ALSO presenta la ventaja de que permite explicar el porqué de las puntuaciones generadas.
Otro aspecto a tener en cuenta es la noción de anomalía que tiene este algoritmo. Para ALSO, la cuantificación de anomalía depende del error de predicción, no de lo aislada que se encuentre la observación respecto al resto. Este problema se hace evidente en modelos que permitan la extrapolación, como por ejemplo un modelo lineal. Si una observación cae muy alejada del resto pero es próxima a la predicción del modelo, su puntuación será muy baja y no se considerará anomalía. Que esto suponga o no un problema depende del caso de uso que se le de al detector, aun así, siempre conviene conocer las limitaciones de los modelos que se emplean.
Este documento pertenece a una serie en la que se muestran diferentes métodos no supervisados para la detección de anomalías: Detección de anomalías: Autoencoders y PCA, Detección de anomalías: trimmed k-means y Detección de anomalías: Isolation Forest.
Los paquetes empleados en este documento son:
library(R.matlab) # Lectura de archivos .mat
library(ranger) # Modelo randomforest
library(tidyverse) # Preparación de datos y gráficos
Los datos empleados en este documento se han obtenido de Outlier Detection DataSets (ODDS), un repositorio con sets de datos comúnmente empleados para comparar la capacidad que tienen diferentes algoritmos a la hora de identificar outliers. Shebuti Rayana (2016). ODDS Library [http://odds.cs.stonybrook.edu]. Stony Brook, NY: Stony Brook University, Department of Computer Science.
Todos los data sets del repositorio están etiquetados, se conoce si las observaciones son o no anomalías (variable y). Aunque los métodos que se describen en el documento son no supervisados, es decir, no hacen uso de la variable respuesta, conocer la verdadera clasificación permite evaluar su capacidad para identificar correctamente las anomalías.
Los datos están disponibles en formato matlab (.mat). Para leer su contenido se emplea la función readMat()
del paquete R.matlab v3.6.2
.
cardio_mat <- readMat("./datos/cardio.mat")
df_cardio <- as.data.frame(cardio_mat$X)
df_cardio$y <- as.character(cardio_mat$y)
En las siguientes sección se implementa y aplica el algoritmo ALSO empleando Random Forest como modelo predictivo. Utilizar este modelo dentro de ALSO tiene tres ventajas:
La mayoría de implementaciones, entre ellas la de ranger
, almacena las predicciones out of bag, lo que evita tener que recurrir a validación cruzada.
Permite emplear predictores numéricos y cualitativos.
El modelo random forest divide el espacio inicial en varios subespacios, lo que permite identificar anomalías locales.
detectar_anomalias_also <- function(datos, standarize=TRUE, verbose=TRUE) {
# Escalado de las variables
# ----------------------------------------------------------------------------
if (standarize) {
datos <- purrr::map_if(.x = datos, .f = scale, .p = is.numeric) %>%
as.data.frame()
}
# Identificación de variables numéricas
# ----------------------------------------------------------------------------
numeric_columns <- which(purrr::map_lgl(.x = datos, .f = is.numeric))
# Iteración para predecir cada variable numérica
# ----------------------------------------------------------------------------
mat_errores <- matrix(
data = NA,
ncol = length(numeric_columns),
nrow = nrow(datos)
)
for (i in seq_along(numeric_columns)) {
columna <- numeric_columns[i]
# Modelo random forest
# --------------------------------------------------------------------------
modelo_k <- ranger::ranger(
x = datos[-columna],
y = datos[[columna]],
num.trees = 1000,
max.depth = 4,
oob.error = TRUE
)
# Predicciones out of bag
# --------------------------------------------------------------------------
predicciones <- modelo_k$predictions
# Peso del modelo
# --------------------------------------------------------------------------
# El modelo ranger almacena el out of bag mean squared error
rmse <- sqrt(modelo_k$prediction.error)
peso_modelo <- 1 - min(1, rmse)
# Error
# --------------------------------------------------------------------------
error <- (datos[[columna]] - predicciones)^2
# Se pondera por el peso del modelo
error <- peso_modelo * error
mat_errores[, i] <- error
if (verbose) {
cat(
paste(
"Modelo", colnames(datos)[columna], "-->",
"Peso:", round(peso_modelo, 4), "\n"
)
)
}
}
# Agregado del error de todos los modelos
# --------------------------------------------------------------------------
score_anomalia <- apply(mat_errores, 1, sum)
return(score_anomalia)
}
## Modelo V1 --> Peso: 0.5252
## Modelo V2 --> Peso: 0.351
## Modelo V3 --> Peso: 0.1
## Modelo V4 --> Peso: 0.1531
## Modelo V5 --> Peso: 0.4248
## Modelo V6 --> Peso: 0.3871
## Modelo V7 --> Peso: 0.4212
## Modelo V8 --> Peso: 0.332
## Modelo V9 --> Peso: 0.4185
## Modelo V10 --> Peso: 0.4891
## Modelo V11 --> Peso: 0.1838
## Modelo V12 --> Peso: 0.7048
## Modelo V13 --> Peso: 0.6377
## Modelo V14 --> Peso: 0.5164
## Modelo V15 --> Peso: 0.3339
## Modelo V16 --> Peso: 0.0902
## Modelo V17 --> Peso: 0.6033
## Modelo V18 --> Peso: 0.7032
## Modelo V19 --> Peso: 0.723
## Modelo V20 --> Peso: 0.4478
## Modelo V21 --> Peso: 0.2652
Una vez que la puntuación (score) ha sido calculada, se puede emplear como criterio para identificar anomalías. En la práctica, si se está empleando esta estrategia de detección es porque no se dispone de datos etiquetados, es decir, no se conoce qué observaciones son realmente anomalías. Sin embargo, como en este ejemplo se dispone de la clasificación real, se puede verificar si realmente los datos anómalos tienen un score superior.
ggplot(data = datos, aes(x = score_anomalia)) +
geom_histogram() +
labs(title = "Distribución de la puntuación de anomalía ALSO",
x = "clasificación (0 = normal, 1 = anomalía)") +
theme_bw()
ggplot(data = datos,
aes(x = y, y = log(score_anomalia))) +
geom_jitter(aes(color = y), width = 0.03, alpha = 0.3) +
geom_violin(alpha = 0) +
geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0) +
stat_summary(fun = "mean", colour = "orangered2", size = 3, geom = "point") +
labs(title = "Puntuación anomalías ALSO",
x = "clasificación (0 = normal, 1 = anomalía)",
y = "Score anomalía") +
theme_bw() +
theme(legend.position = "none")
La el valor promedio en el grupo de las anomalías (1) es superior. Sin embargo, al existir solapamiento, si se clasifican las n observaciones con mayor score como anomalías, se incurriría en errores de falsos positivos.
Acorde a la documentación, el set de datos Cardiotocogrpahy contiene 176 anomalías. Véase la matriz de confusión resultante si se clasifican como anomalías las 176 observaciones con mayor score predicho.
resultados <- datos %>%
select(y, score_anomalia) %>%
arrange(desc(score_anomalia)) %>%
mutate(clasificacion = if_else(row_number() <= 176, "1", "0"))
mat_confusion <- MLmetrics::ConfusionMatrix(
y_pred = resultados$clasificacion,
y_true = resultados$y
)
mat_confusion
## y_pred
## y_true 0 1
## 0 1539 116
## 1 116 60
De las 176 observaciones identificadas como anomalías, solo el 34% (60/176) lo son.
El porcentaje de falsos positivos (66%) es muy elevado. El método ALSO con la configuración de random forest empleada no consigue buenos resultados en este set de datos. Es importante recalcar que esto no significa que ALSO no sea un buen algoritmo de detección, es posible que se mejoren los resultados si se utilizan otros hiperparámetros u otro tipo de modelo (lineal, svm …). Este ejemplo pone también de manifiesto lo variables que son los resultados de los métodos de detección de anomalías en función del set de datos al que se aplican.
sesion_info <- devtools::session_info()
dplyr::select(
tibble::as_tibble(sesion_info$packages),
c(package, loadedversion, source)
)
Outlier Analysis Aggarwal, Charu C.
Paulheim, H., Meusel, R. A decomposition of the outlier detection problem into a set of supervised learning problems. Mach Learn 100, 509–531 (2015). https://doi.org/10.1007/s10994-015-5507-y
¿Cómo citar este documento?
Detección de anomalías: Attribute wise learning for scoring outliers (ALSO) por Joaquín Amat Rodrigo, disponible con licencia CC BY-NC-SA 4.0 en https://www.cienciadedatos.net/documentos/67_deteccion_anomalias_ALSO.html
Este material, creado por Joaquín Amat Rodrigo, tiene licencia Attribution-NonCommercial-ShareAlike 4.0 International.
Se permite:
Compartir: copiar y redistribuir el material en cualquier medio o formato.
Adaptar: remezclar, transformar y crear a partir del material.
Bajo los siguientes términos:
Atribución: Debes otorgar el crédito adecuado, proporcionar un enlace a la licencia e indicar si se realizaron cambios. Puedes hacerlo de cualquier manera razonable, pero no de una forma que sugiera que el licenciante te respalda o respalda tu uso.
NoComercial: No puedes utilizar el material para fines comerciales.
CompartirIgual: Si remezclas, transformas o creas a partir del material, debes distribuir tus contribuciones bajo la misma licencia que el original.