Capítulo 4. Automatizar el monitoreo de la calidad de los datos con aprendizaje automático

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

El aprendizaje automático es un enfoque estadístico que, comparado con las pruebas basadas en reglas y el monitoreo de métricas, tiene muchas ventajas: es escalable, puede detectar cambios desconocidos y, a riesgo de antropomorfizar, es inteligente. Puede aprender de entradas anteriores, utilizar información contextual para minimizar los falsos positivos y, de hecho, comprender tus datos cada vez mejor con el tiempo.

En los capítulos anteriores, hemos explorado cuándo y cómo la automatización con ML tiene sentido para tu estrategia de monitoreo de la calidad de los datos. Ahora es el momento de explorar el mecanismo central: cómo puedes entrenar, desarrollar y utilizar un modelo para detectar problemas decalidad de datos , eincluso explicar aspectos como su gravedad y dónde se producen en tus datos.

En este capítulo, te explicaremos qué enfoque de aprendizaje automático funciona mejor para el monitoreo de la calidad de los datos y te mostraremos el algoritmo (serie de pasos) que puedes seguir para aplicar este enfoque. Responderemos a preguntas como cuántos datos debes muestrear y cómo hacer que los resultados del modelo sean explicables. Es importante advertir que seguir estos pasos no dará como resultado un modelo listo para monitorear datos del mundo real. En el Capítulo 5, nos ocuparemos de los aspectos prácticos de ajustar y probar tu sistema para que funcione de forma fiable en un entorno empresarial.

Requisitos

Hay muchas técnicas de ML que podrías aplicar a un problema determinado. Para averiguar el enfoque adecuado para tu caso de uso, es esencial definir los requisitos por adelantado. Creemos que un modelo para el monitoreo de la calidad de los datos debe tener cuatro características: sensibilidad, especificidad, transparencia y escalabilidad.

Sensibilidad

La sensibilidad es una medida de la capacidad de un modelo ML para detectar verdaderos positivos. Para ser eficaz, un algoritmo debe ser capaz de detectar una amplia variedad de problemas de calidad de datos en datos tabulares del mundo real. Un buen punto de referencia es ser capaz de detectar cambios que afecten al 1% o más de los registros.

En la práctica, descubrimos que intentar detectar cambios que afectan a menos del 1% de los registros produce un sistema sencillamente demasiado ruidoso. Incluso si los cambios detectados son estadísticamente significativos, habrá demasiados de ellos para clasificar y comprender, especialmente cuando se escalan a un gran número de tablas complejas. Nuestra experiencia nos ha sugerido que los cambios que afectan al 1% o más de los registros son cambios estructurales significativos en los procesos de generación o transformación de datos, que podrían suponer nuevos golpes y cicatrices importantes.

Para encontrar cambios inferiores al 1%, puedes utilizar enfoques deterministas (como reglas de validación), o puedes centrar el ML en un subconjunto de datos ejecutando el modelo en una vista que consulte sólo los registros más importantes.

Por ejemplo, una plataforma de redes sociales puede hacer un seguimiento de cientos de tipos diferentes de eventos en una única tabla grande de procesamiento de eventos. Ejecutar ML en la totalidad de la tabla detectaría problemas graves con el formato y la estructura de los tipos más comunes de eventos que se recopilan. Pero en lugar de eso, podrías ejecutar el modelo en cada uno de los cientos de subconjuntos específicos de eventos si quisieras prestarles mucha atención a todos ellos.

Especificidad

Contrapartida de la sensibilidad, la especificidad te dice lo bueno que es el modelo para no activar alertas de falsos positivos. Esto es especialmente importante en el monitoreo de la calidad de los datos, donde la fatiga de las alertas puede amenazar la adopción y eficacia de todo el enfoque.

Normalmente, un sistema de monitoreo tiende a sobrealertar por varias razones. Una razón puede ser la estacionalidad: si hay patrones en los datos que se repiten diaria, semanal o anualmente, puede parecer que los datos están cambiando, pero en realidad no están cambiando de forma inusual o inesperada. El monitoreo también será ruidoso si no es capaz de agrupar columnas correlacionadas afectadas por el mismo cambio de datos. O podría enviar alertas falsas positivas si revisa una muestra de datos demasiado pequeña o evalúa los datos en una ventana temporal demasiado pequeña. Además, hay algunos conjuntos de datos que son mucho más "caóticos" que otros, por lo que el umbral de sensibilidad de una comprobación debe calibrarse para cada conjunto de datos (y puede que deba evolucionar con el tiempo).

Exploraremos cómo un modelo puede aprender y tener en cuenta la estacionalidad, las correlaciones y otros retos de los datos del mundo real en el Capítulo 5.

Transparencia

Cuando surjan problemas, el modelo debe ser transparente y ayudar a los usuarios a comprenderlos y a determinar sus causas. Puede que pienses que esto no tiene que ver con el modelo en sí, después detodo, las visualizaciones extravagantes y el análisis de la causa raíz ocurrirán después de que se detecte un problema en los datos. Pero tus opciones dependen realmente del enfoque ML que utilices. La arquitectura y la implementación de tu modelo dictarán hasta qué punto podrás explicar y atribuir sus predicciones. Por ejemplo, algunas funciones de ML pueden ayudar a mejorar la precisión, pero serán difíciles de explicar a los usuarios en el contexto de la calidad de los datos.

Escalabilidad

Para funcionar a diario con miles de millones de filas de un almacén de datos, tu sistema también debe escalar: en coste humano, de almacenamiento y computacional. No debe requerir ninguna configuración inicial ni reajuste por parte de los administradores para funcionar, ya que esto sólo crearía otra forma de reglas escritas a mano, que ya hemos demostrado en el Capítulo 2 que no es una solución escalable. Debe tener una huella de consulta mínima en el almacén de datos y ser capaz de ejecutarse rápidamente en hardware barato fuera del almacén. Estas limitaciones afectarán a muchos aspectos de nuestras decisiones de modelado, y a lo largo de este capítulo abordaremos formas de hacer que la solución sea más escalable.

Sin requisitos

Definir lo que un sistema no necesita hacer puede ser tan útil como definir lo que debe hacer. Tal vez recuerdes del Capítulo 2 que un modelo de ML no supervisado debe ser una parte de un enfoque de calidad de datos de cuatro pilares que también incluya pruebas basadas en reglas y monitoreo de métricas. Esto se debe a que es sencillamente inviable esperar que la automatización resuelva todos los problemas de calidad de los datos.

Aquí tienes una lista de no-requisitos para nuestro modelo:

  • No necesita identificar registros individuales que sean malos (para eso están las pruebas basadas en reglas: cuando necesitas que los datos sean perfectos). En su lugar, esperamos que busque cambios estructurales en porcentajes significativos de registros.

  • No es necesario procesar los datos en tiempo real. No sólo sería difícil escalar la evaluación en tiempo real de un modelo ML para la detección de la calidad de los datos, sino que también podría verse obligado a evaluar registros individuales, lo que no está dentro de nuestro alcance. En su lugar, esperamos que evalúe los datos en lotes diarios o cada hora.

  • No podemos esperar que sea capaz de decir si los datos han estado siempre corruptos -así no funciona el ML, ya que el modelo debe entrenarse con datos históricos-. Si esos datos históricos son erróneos, ¡no podemos hacer nada al respecto! Por eso sólo se debe confiar en un enfoque ML para identificar nuevos cambios en los datos.

  • No podemos esperar que analice los datos sin tener noción del tiempo. El modelo rastreará los datos a lo largo del tiempo para detectar cambios. Si no hay una marca de tiempo integrada en los propios datos, tendremos que desarrollar otras formas de identificar cuándo se generaron los datos (hablaremos de esto más adelante).

El monitoreo de la calidad de los datos no es la detección de valores atípicos

Al terminar nuestro debate sobre los requisitos de un modelo de monitoreo de la calidad de los datos, merece la pena dedicar un momento a abordar una confusión común: la diferencia entre la detección de valores atípicos y el monitoreo de la calidad de los datos.

La detección de valores atípicos puede ser una forma útil de comprender conjuntos de datos complejos. Hay muchas formas de identificar valores atípicos, pero uno de los enfoques más escalables y flexibles es utilizar una variante del bosque aleatorio llamada Bosque de Aislamiento para identificar filas de datos que están lejos del "centro" de una distribución multivariante, como se muestra en la Figura 4-1.

An example of using Isolation Forest in scikit-learn to find outlier observations (those points outside of the delineated clusters) (scikit-learn at https://scikit-learn.org/stable/auto_examples/miscellaneous/plot_anomaly_comparison.html#sphx-glr-auto-examples-miscellaneous-plot-anomaly-comparison-py)
Figura 4-1. Un ejemplo de uso del Bosque de Aislamiento en scikit-learn para encontrar observaciones atípicas (aquellos puntos fuera de los clusters delineados); de "Comparing Anomaly Detection Algorithms for Outlier Detection on Toy Datasets", Scikit-learn.

La detección de valores atípicos puede realizarse con ML, y trata de averiguar aspectos inusuales de los datos. Pero las similitudes con el monitoreo de la calidad de los datos terminan ahí. Al fin y al cabo, todo conjunto de datos tendrá observaciones inusuales, ¡incluso una distribución normal tiene valores extremos! Estos valores atípicos pueden ser interesantes (podrían ser registros fraudulentos o simplemente sucesos o combinaciones de datos muy raros), pero no van a ser necesariamente problemas de calidad de los datos, que pueden afectar a registros comunes o raros con igual probabilidad.

Para identificar los problemas de calidad de los datos, necesitamos saber cuándo se produce un cambio estructural repentino en la distribución de los datos que llegan a la tabla. Necesitamos saber si, en el pasado, los registros aparecían siempre con una determinada distribución, patrón o relación, y ahora, de repente, eso ha cambiado de forma significativa. Por otra parte, todo conjunto de datos tiene valores atípicos. La detección de valores atípicos resuelve un problema fundamentalmente distinto.

Enfoque y algoritmo ML

Ahora que hemos cubierto los requisitos, compartiremos el enfoque que recomendamos y los pasos que puedes seguir para ponerlo en práctica. Dudamos en afirmar que ésta sea la única forma de utilizar el ML para detectar problemas de calidad de los datos, pero aún no hemos encontrado un enfoque que cumpla los requisitos con mayor eficacia en la práctica. Como siempre, el diablo está en los detalles. Aspectos como la ingeniería de características y el ajuste/amortiguación de parámetros marcan la diferencia entre una aplicación eficaz y otra que alerta en exceso o en defecto sobre los datos del mundo real, como veremos más adelante en el Capítulo 5.

Recordemos que queremos desarrollar un modelo de ML para detectar cambios inesperados en nuestros datos, sin que ningún humano etiquete los datos y nos diga qué constituye un problema de calidad de los datos. Esto hace que este tipo de problema de ML sea una tarea de aprendizaje no supervisado. Sin embargo, resulta que hay una característica de los datos que podemos utilizar como si fuera una etiqueta humana, y es el momento en que los datos llegaron a la tabla.

Aquí reside la clave de este enfoque. Cada día, tomamos una instantánea de los datos. Luego, cada día, intentamos entrenar un clasificador para predecir si los datos son de hoy o no.

Image

Si no hay nada estadísticamente destacable en los datos de hoy, entonces nuestro intento de entrenar un clasificador debería fracasar: predecir si los datos son de hoy o no debería ser una tarea imposible, ¡básicamente tirar una moneda al aire!

Por otra parte, si podemos construir un clasificador que prediga con cierta exactitud si un dato procede de hoy, entonces podemos estar bastante seguros de que algo es inusual en los datos de hoy. Y es inusual de una manera significativa, porque unos pocos cambios aleatorios en un par de registros no van a ser suficientes para entrenar un modelo que haga una predicción en un sentido u otro. De hecho, incluso podremos utilizar este método para decir lo significativo que es el cambio y establecer umbrales apropiados para evitar la fatiga de alerta. Al explicar las predicciones del modelo, podemos explicar qué es lo más probable que esté pasando dentro de los datos.

Un modelo puede detectar un cambio que sea significativo aunque ese cambio no sea interesante. El ejemplo más obvio de esto es cuando hay una columna date. Esa columna va a cambiar todos los días, ¡por lo que siempre representará un cambio drástico en los datos! Veremos cómo tratar casos como éste en el Capítulo 5. El otro tipo de cambio que puede no ser significativo es aquel que al usuario final simplemente no le importa. Hablaremos de cómo tratar este tipo de alertas en el Capítulo 6.

Ahora que ya tienes la idea principal, vamos a explorar cada paso con más detalle:

Muestreo de datos

¿Cómo construyes un conjunto de datos para entrenar tu modelo y cuál es el tamaño de muestra adecuado?

Codificación de características

¿Cómo pasas de una fila de una de tus tablas a un conjunto de características que tu modelo puede utilizar para hacer predicciones?

Desarrollo de modelos

¿Cuál es la arquitectura de modelo adecuada para este algoritmo y cómo debes entrenar el modelo?

Explicabilidad del modelo

Una vez que has entrenado un modelo, ¿cómo lo utilizas para explicar un problema de calidad de datos?

Muestreo de datos

El punto de partida para construir cualquier modelo es crear un conjunto de datos de entrenamiento tomando muestras de tu conjunto general de datos. Para el algoritmo que acabamos de describir, necesitarás un conjunto sólido de datos muestreados aleatoriamente tanto de "hoy" (etiqueta = 1 para la clase que intentamos predecir) como de "no hoy" (etiqueta = 0). Los datos de "no hoy" deben ser una mezcla de periodos anteriores de comparación temporal: ayer (o la última vez que recibiste una actualización de datos) para los cambios repentinos, así como otros momentos de la semana o del año para controlar la estacionalidad (consulta la sección "Estacionalidad").

Por ejemplo, en la Figura 4-2 vemos un conjunto de datos de ejemplo con 150k a 250k filas de datos por día. Una muestra robusta podría incluir 10.000 filas de cada una de las siguientes fechas:

  • 2021-01-16: la fecha en la que quieres evaluar los problemas de calidad de los datos

  • 2021-01-15: ayer, lo que ayudará a identificar cualquier cambio repentino

  • 2021-01-09: hace una semana, para controlar la estacionalidad del día de la semana

  • 2021-01-02: hace dos semanas, por si la semana pasada hubiera anomalías

Graph showing sample size compared to the entire size of the dataset
Figura 4-2. Gráfico que muestra el tamaño de la muestra comparado con el tamaño total del conjunto de datos.

Tamaño de la muestra

En la práctica, encontramos en que este algoritmo necesita al menos 100 registros diarios para tener alguna posibilidad de encontrar cambios significativos en datos razonablemente complejos. Pero esto nos lleva a preguntarnos: ¿cuál es el límite superior del número de registros útiles para el algoritmo?

Tu frecuencia de muestreo puede elegirse para equilibrar el coste computacional frente a la precisión. Hemos ejecutado este algoritmo con conjuntos de datos que tienen hasta decenas de miles de millones de filas añadidas al día. En la práctica, y basándonos en pruebas rigurosas, hemos comprobado que 10.000 registros al día (si se muestrean aleatoriamente) proporcionan datos suficientes para captar la mayoría de los problemas de calidad de los datos, incluso los que afectan a tan sólo el 1-5% de los registros. La mejora de la calidad decae a medida que el tamaño de las muestras supera los 100.000.

Se pueden utilizar muestras de gran tamaño (digamos, 1.000.000 de registros al día), pero no se ha demostrado que el coste computacional merezca la pena. Un conjunto de datos tendría que ser muy, muy estable (poco caos de fondo), y el cambio tendría que producirse en un porcentaje muy pequeño de registros (digamos, el 0,1% de los registros), para que este aumento del tamaño de la muestra mereciera la pena.

Puede parecer un error muestrear un tamaño de muestra fijo (10.000 registros), en lugar de muestrear, digamos, el 10% de los datos. Al fin y al cabo, si tengo 1.000 millones de registros, ¿cómo pueden 10.000 seguir siendo representativos de esa enorme población?

Tal vez de forma contraintuitiva, como la muestra se elige totalmente al azar, su precisión no depende del tamaño total de los datos, sólo del tamaño absoluto de la muestra. Por ejemplo, considera la estimación de la renta media de un país. El hecho de que la población de China sea de 1.400 millones de habitantes y la de Luxemburgo de 600.000, ¿significa que tendríamos que muestrear a más personas en China para obtener una estimación de la renta media? No. En ambos casos, podríamos tomar 1.000 personas y obtener una estimación muy buena de la renta media.

Sesgo y eficacia

Es esencial que la muestra se tome al azar de la tabla. Si hay algún sesgo en el muestreo, el algoritmo podrá encontrar ese sesgo y lo representará como un cambio falso positivo en los datos que confundirá a los usuarios.

También es fundamental asegurarse de que el muestreo sea lo más eficiente posible. En la práctica, sacar registros aleatorios del almacén de datos para el modelo de aprendizaje automático suele ser la operación más cara en este tipo de sistema. Esto se debe a que la tabla puede tener miles de millones o incluso billones de registros, y cientos o miles de columnas. Si una consulta requiriera ingenuamente leer cada registro en memoria o enviarlo a través de una red para realizar el muestreo, esto sería desastroso para el rendimiento e incurriría en una gran cantidad de costes de almacén de datos.

A veces, el sesgo y la eficiencia pueden tener una relación de vaivén. Por ejemplo, una forma de escalar eficientemente el muestreo aleatorio en los almacenes de datos modernos es utilizar operadores TABLESAMPLE en lugar de llamadas a random(). El operador TABLESAMPLE está implementado de tal forma que permite al almacén muestrear de forma eficiente registros aleatorios durante la ejecución de la consulta, sin tener que leer los registros en memoria, pero en algunos casos puede tener contrapartidas negativas en cuanto al sesgo.

En BigQuery, la implementación del operador TABLESAMPLE funciona "seleccionando aleatoriamente un porcentaje de bloques de datos de la tabla y leyendo todas las filas de los bloques seleccionados". La documentación de continúa explicando que, normalmente, "BigQuery divide las tablas o particiones de tablas en bloques si tienen un tamaño superior a 1 GB aproximadamente". Esto significa que, en la práctica, los resultados devueltos por el operador TABLESAMPLE a menudo no serán aleatorios en BigQuery y, en cambio, pueden estar completamente en una sola partición. Si has dividido tus datos en función de un identificador que utilizas con frecuencia para las uniones -por ejemplo, un identificador de cliente-, entonces tendrás subconjuntos específicos de clientes que tendrán muchas más probabilidades de aparecer en tu muestra aleatoria que otros. Esto podría sesgar significativamente los resultados de tu ML, haciendo que veas continuamente un cambio en la población de usuarios a lo largo del tiempo que se debe enteramente a la implementación del muestreo y no a una deriva real en tus datos en sí.

Entonces, ¿cómo tomar muestras de forma eficaz y evitar sesgos? He aquí nuestras recomendaciones:

  • Asegúrate de que sólo utilizas un pequeño número de días de datos cada vez que ejecutes el algoritmo. Estos días pueden almacenarse como instantáneas para no tener que volver a consultarlos (aunque puede merecer la pena volver a consultarlos, ya que los datos históricos de la tabla podrían haber cambiado).

  • Asegúrate de que la tabla está particionada en la columna de fecha que estás utilizando para seleccionar los datos. Esto permite al almacén de datos navegar eficazmente sólo a los archivos del disco que representan esos días y leer y procesar sólo esas fechas de datos sin tener que acceder a otras fechas irrelevantes.

  • Utiliza el operador TABLESAMPLE para muestrear de forma eficiente una muestra aleatoria aproximada mayor que la que necesitas (por ejemplo, si necesitas un 0,3%, entonces muestrea un 1%). A menudo, el porcentaje límite inferior que se puede muestrear con este operador es el 1%, aunque la implementación varía según el almacén. Ten en cuenta que no todas las bases de datos o almacenes de datos admiten TABLESAMPLE de forma robusta-véase el párrafo anterior sobre BigQuery.

  • Cuenta el número total de registros que tendrás en las fechas que estás consultando, para saber el porcentaje exacto de muestra que tendrás que consultar.

  • Toma la muestra aleatoria final utilizando un código parecido a: random() <= X para algunos X que te proporcione aproximadamente el número correcto de filas en cada fecha después de la operación TABLESAMPLE. Esto es mucho más eficaz que algo parecido a order by random() limit 10,000, que requeriría cargar todos los datos en la memoria de un nodo maestro del almacén y ordenarlos por un número aleatorio antes de aplicar un límite. La ventaja del enfoque random() <= X es que puede aplicarse de forma distribuida en el almacén en cada uno de los nodos de cálculo de los trabajadores. Ten en cuenta que el pequeño inconveniente es que es poco probable que tu muestra aleatoria sea exactamente de 10.000 filas, sino que será un número bastante aproximado.

Otra consideración importante a la hora de consultar datos es asegurarse de que el filtro SQL WHERE se implementa de forma eficiente. Por ejemplo, para una tabla con la columna de fecha created_date, especificada como una cadena en formato AAAA-MM-DD, esto sería muy ineficiente:

WHERE cast(created_date as date) = cast('2023-06-01' as date)

Este código requeriría que la base de datos leyera cada partición y convirtiera la columna created_date en memoria para decidir si el registro debe incluirse.

En su lugar, inténtalo:

WHERE created_date = '2023-06-01'

Ahora el almacén de datos puede utilizar metadatos sobre cada partición para decidir cuáles excluir por completo de ser tenidas en cuenta por la consulta. Esto puede ser todo un reto para las tablas formateadas con particiones de fecha u hora poco habituales. En Anomalo, hemos tenido que añadir soporte para todos los tipos de la Figura 4-3.

Example of types of date/time partitions
Figura 4-3. Ejemplo de tipos de particiones fecha/hora.

Se sabe que Jeremy, a veces, bromea diciendo que esperamos apoyar pronto el tiempo en formato "número de días desde que nació Kevin Bacon".

Codificación de características

Los modelos de ML no suelen entrenarse con datos brutos, sino que aprenden utilizando características numéricas, que son transformaciones de los datos brutos en señales que el modelo puede utilizar. La forma en que se transforman los datos brutos puede tener un impacto significativo en el rendimiento del modelo y normalmente requiere tanto experiencia en ML como experiencia en la materia de los datos y el problema en cuestión. Este proceso, denominado ingeniería de características, debe estar totalmente automatizado en nuestro algoritmo de detección de anomalías.

El funcionamiento es el siguiente: cada registro de la muestra tendrá una serie de columnas, y cada columna puede ser un entero, un flotante, una cadena, un booleano, una fecha o una marca de tiempo, o un tipo complejo como JSON o una matriz. Necesitarás un proceso automatizado que recorra cada columna (expandiendo los tipos complejos como JSON en subcolumnas si es necesario; consulta "Datos semiestructurados" para saber más sobre esto), extraiga la información que pueda ser interesante para tu modelo y codifique esta información en una matriz de coma flotante de características ML.

Encoding data as features. Note that the response variable (a.k.a. label) corresponds to the date: 0 for yesterday and 1 for today.
Figura 4-4. Codificación de datos como características. Observa que la variable de respuesta (también llamada etiqueta) corresponde a la fecha: 0 para ayer y 1 para hoy. Puedes ver una versión a tamaño completo de esta imagen en https://oreil.ly/adqm_4_4.

Querrás desarrollar una biblioteca de tipos de codificadores candidatos a aplicar, basada en las características que creas que podrían indicarte si los datos han cambiado de forma significativa (ver Figura 4-4). Aquí tienes algunos codificadores que recomendamos:

Numeric

Convierte valores booleanos, enteros y flotantes en flotantes

Frequency

Con qué frecuencia aparece cada valor en la muestra de datos

IsNull

Un indicador binario para saber si la columna es NULL

TimeDelta

Segundos entre una hora y el momento en que se creó el registro

SecondOfDay

La hora del día en que se creó el registro

OneHot

Un codificador de un solo paso, que te permite asignar valores de características (como categorías o valores enteros frecuentes) a una variable indicadora binaria de sí o no para cada valor único de la columna

Los científicos de datos pueden preguntarse sobre la aplicabilidad de los codificadores habituales, como la frecuencia de términos-frecuencia inversa de documentos (TF-IDF), la codificación media o el suavizado de Laplace. Muchos codificadores estándar no son muy relevantes para los modelos basados en árboles (transformación logarítmica, codificación de la media, análisis de componentes principales [ACP]). Otros requerirían muchos conocimientos sobre los datos concretos para utilizarlos bien (suavizado de Laplace), y otros podrían ser útiles pero serían muy difíciles de interpretar (TF-IDF, incrustación de palabras/vectores).

Debes tener cuidado con la complejidad de tus codificadores, porque al final tendrás que utilizarlos para explicar al usuario el problema de la calidad de los datos. Por ejemplo, probamos un codificador de "brecha" para campos temporales, enteros y numéricos, que tomaba cada observación y calculaba la brecha entre ella y el siguiente valor más grande de esa columna. En la práctica, esto pudo detectar algunos tipos de problemas de calidad de los datos, pero también detectaría muchos otros cambios en los datos que serían difíciles de entender y/o irrelevantes para nuestros fines, como cambios en el grano de cómo se están registrando los datos o cambios no relacionados en el volumen (y por tanto en la densidad) de las observaciones.

Desarrollo de modelos

Para cumplir los requisitos de escalabilidad de y que funcione en un entorno práctico, necesitas una arquitectura de modelos que sea rápida en la inferencia y en el entrenamiento, que pueda entrenarse con muestras relativamente pequeñas y que se generalice a cualquier tipo de datos tabulares (cuando se codifiquen adecuadamente las características). Los árboles de decisión potenciados por gradiente funcionan bien para este caso de uso, y encontrarás bibliotecas como XGBoost fácilmente disponibles para el desarrollo de modelos.

Los árboles de decisión con refuerzo de gradiente funcionan de forma iterativa, construyendo una secuencia de árboles de decisión en el conjunto de datos, donde cada árbol (o "paso") está diseñado para corregir los errores de todos los árboles que le precedieron. En última instancia, la predicción del modelo tiene en cuenta los resultados de todos los árboles que se entrenaron en cada paso (esto se conoce como modelo conjunto). Véase la Figura 4-5.

A gradient-boosted decision tree (adapted from Haowen Deng et al., “Ensemble Learning for the Prediction of Neonatal Jaundice with Genetic Features,” BMC Medical Informatics and Decision Making, 2011)
Figura 4-5. Un árbol de decisión potenciado por gradiente (adaptado de Haowen Deng et al., "Ensemble Learning for the Early Prediction of Neonatal Jaundice with Genetic Features", BMC Medical Informatics and Decision Making 21, no. 338 [2021]).

Afortunadamente, los árboles de decisión potenciados por gradiente tienen un número muy reducido de parámetros que realmente importan afinar (principalmente, la tasa de aprendizaje y la complejidad de cada árbol, aunque hay otros) y pueden entrenarse en conjuntos de datos con miles o incluso millones de registros muy rápidamente.

Algunos enfoques alternativos, como los modelos lineales, son demasiado simples para aprender los complejos patrones de la mayoría de los conjuntos de datos estructurados. Otros enfoques, como las redes neuronales, suelen ser demasiado complejos para problemas como éste y requieren volúmenes extremadamente grandes de datos heterogéneos para llegar a ser muy potentes (como en los modelos de imagen y lenguaje).

El inconveniente de los árboles de decisión potenciados por gradiente, como cualquier técnica estructurada de ML, es que requieren ingeniería de características: los expertos humanos tienen que decirle al modelo qué aspectos de los datos debe tener en cuenta al hacer sus predicciones, y esto puede llevar mucho tiempo y energía.

Formación y evaluación

En teoría, los árboles de decisión potenciados por gradiente podrían seguir iterando e iterando sin fin, por lo que es esencial limitar el número de pasos a algún límite. Para ello, normalmente querrás evaluar el rendimiento del modelo después de cada paso. Selecciona una parte aleatoria de tus datos para utilizarla como conjunto de reserva para la evaluación (y no para el entrenamiento) y prueba el modelo después de cada iteración. El rendimiento de tu modelo te indicará si hay algo anómalo en los datos actuales.

En concreto, hay tres patrones de rendimiento del modelo que solemos ver en la práctica y que se muestran en la Figura 4-6. En estos gráficos, el eje x representa el número de árboles añadidos al modelo (el número de iteraciones), mientras que el eje y representa una medida de la precisión del modelo (el logaritmo de la función de pérdida). Ten en cuenta que, en este caso, el eje y representa técnicamente la cantidad de "error" que hay en las predicciones del modelo, por lo que un valor más bajo indica una mayor precisión.

El primer escenario, "Sin anomalía", es cuando se avanza poco en los datos de entrenamiento y el rendimiento en los datos de prueba empieza a empeorar muy rápidamente. Esto significa que es poco probable que haya alguna anomalía en los nuevos datos.

El segundo escenario, "Incompleto", ocurre cuando el modelo no tiene tiempo suficiente para converger. Alcanzas un número máximo de árboles (fijado para evitar que el modelo funcione indefinidamente) y aún así observas que el error de entrenamiento y el error de prueba disminuyen. Tendrás que añadir más árboles o, quizás de forma más prudente, aumentar la tasa de aprendizaje, lo que hace que el algoritmo de refuerzo de gradiente dé "pasos" más grandes en la dirección de cada árbol que evalúa.

El tercer escenario, "Óptimo", se produce cuando el modelo progresa adecuadamente en el entrenamiento y en la prueba, hasta un punto en el que la pérdida en la prueba empieza a aumentar. Esto indica que puedes detenerte en el punto en que la pérdida en la prueba estaba en su mínimo. En ese punto, el modelo habrá aprendido todo lo que puede sobre lo que diferencia a estos dos conjuntos de datos, dados los demás parámetros del algoritmo de aprendizaje.

The three most common scenarios encountered when plotting the model’s performance on training and test data as the number of trees increases. Performance is measured using a log loss error function (a lower value on the y-axis indicates better performance).
Figura 4-6. Los tres escenarios más comunes encontrados al trazar el rendimiento del modelo en los datos de entrenamiento y de prueba a medida que aumenta el número de árboles. El rendimiento se mide utilizando una función de error de pérdida logarítmica (un valor más bajo en el eje y indica un mejor rendimiento).

En la práctica, para proporcionar estadísticas del modelo y resultados de explicabilidad que se puedan interpretar de forma coherente, tendrás que encontrar un equilibrio entre la optimización de tu modelo para un único conjunto de datos y la construcción de un modelo que se generalice a muchos conjuntos de datos a lo largo de distintos periodos de tiempo.

Eficacia informática

Muchas organizaciones tienen tablas importantes que pueden incluir miles de millones de registros. Algunos ejemplos son:

  • Datos transaccionales de industrias de servicios financieros

  • Datos de eventos sin procesar de aplicaciones o sitios web con mucho tráfico

  • Datos de impresiones y eventos de publicidad digital

  • Datos de los sensores físicos

  • Información de mensajes de plataformas sociales

Con datos a esta escala, es fácil crear una estrategia de monitoreo que resulte prohibitiva en cuanto a costes, o que simplemente no funcione con éxito ni siquiera con los almacenes de datos modernos.

Como hemos puesto un límite al número de registros que muestreamos al día, la mayoría de los cálculos y el uso de memoria del modelo aumentarán linealmente con el número de columnas añadidas. Por ejemplo, la búsqueda de la mejor división mientras se expande un árbol de decisión en cada nodo aumentará linealmente con el número de columnas sobre las que haya que buscar. Aunque las tablas típicas tienen entre 10 y 50 columnas, es habitual que las tablas tengan 200 columnas, y algunas tablas tienen miles. Además, las tablas pueden tener datos JSON que tendrás que expandir automáticamente en nuevas columnas sintéticas, lo que puede llevar a tablas con 10.000 columnas en algunas situaciones.

Las siguientes optimizaciones pueden hacer que tu algoritmo sea más eficiente desde el punto de vista computacional:

  • Asegúrate de que sólo consultas los datos de un día cada vez y haz instantáneas de los resultados en la medida de lo posible para crear un historial. Ten en cuenta que esto tiene un coste, ya que los algoritmos tendrán menos historial con el que trabajar el primer día y no serán tan eficaces en un escenario de "arranque en frío".

  • Tomar muestras aleatorias de registros de la tabla mediante el almacén de datos (utilizando las técnicas eficientes indicadas anteriormente en "Sesgo y eficiencia") y calcular resultados más complejos de perfiles o ML sobre las muestras aleatorias.

  • Si utilizas árboles de decisión con refuerzo de gradiente, limita la profundidad y el número total de árboles, ya que no solemos buscar interacciones muy complejas, y detente pronto si el error de la prueba aumenta significativamente durante el proceso de entrenamiento.

  • Optimizar el propio proceso de aprendizaje, lo que, dependiendo de tu plataforma informática y del algoritmo de aprendizaje, podría incluir pasos como utilizar codificaciones dispersas, distribuir el aprendizaje mediante multiprocesamiento o utilizar GPU.

Explicabilidad del modelo

Si tienes un modelo que funciona bien en el conjunto de pruebas, esto indica que has encontrado un posible problema de calidad de los datos. El siguiente paso es explicar lo que ha descubierto el modelo.

La explicabilidad es clave por varias razones. En primer lugar, te indica lo anómalos que son los datos de hoy. Esto te permite realizar muchos tipos de ajustes para evitar la fatiga de alertas (más sobre esto en los Capítulos 5 y 6). Para aquellos problemas en los que sí dispares una alerta, conocer la gravedad ayudará a los usuarios finales a priorizar su respuesta.

En segundo lugar, la explicabilidad te indica en qué parte de los datos se encuentra esa anomalía. Esto te permite dirigir a los investigadores a los segmentos adecuados de los datos y crear todo tipo de ayudas interesantes para el análisis de la causa raíz, como muestras de datos erróneos (más detalles en el Capítulo 6).

Entonces, ¿cómo funciona la explicabilidad del modelo? La idea es obtener una puntuación que acredite cuánto ha contribuido cada celda {fila, columna} individual del conjunto de datos a la predicción del modelo. Aunque existen varios enfoques, nosotros utilizamos SHAP, que esencialmente se aproxima a una estimación lineal local de lo que hace el algoritmo para cada celda del conjunto de datos.

Para ver cómo funciona esto en la práctica, supongamos que intentamos detectar problemas de calidad de los datos en una tabla de datos de transacciones con tarjeta de crédito y que hemos muestreado 10.000 registros de ayer y hoy, codificado nuestras características y construido nuestro modelo prediciendo en qué día llegó cada registro. Entonces, sigamos los cuatro registros siguientes a través del proceso de explicabilidad SHAP:

Importe Tipo Puntuación FICO Marca Tipo Límite de crédito Fuente
$18 Deslízate por 684 Descubre Débito $12,564 Hoy
$59 Chip 578 Mastercard Crédito $7,600 Hoy
-$445 Chip 689 Visa Crédito $6,700 Ayer
$137 Chip 734 Mastercard Crédito $7,100 Ayer

En este caso, tenemos dos registros de ayer y dos registros de hoy. (Recuerda que la columna de origen no se utiliza para hacer predicciones sobre el día en que llegaron los datos, sino que es la respuesta para cuya predicción estamos entrenando el modelo).

A continuación, supongamos que tomamos nuestro modelo y hacemos predicciones para cada fila sobre el día en que creemos que es probable que haya llegado:

Importe Tipo Puntuación FICO Marca Tipo Límite de crédito Fuente Predicción Pr( Hoy )
$18 Deslízate por 684 Descubre Débito $12,564 Hoy 51%
$59 Chip 578 Mastercard Crédito $7,600 Hoy 78%
-$445 Chip 689 Visa Crédito $6,700 Ayer 45%
$137 Chip 734 Mastercard Crédito $7,100 Ayer 52%

En este caso, comprobamos que nuestro modelo cree que hay un 78% de probabilidades de que el segundo registro sea de hoy, mientras que los otros tres registros están dentro de un ±5% de la predicción media del 50%, lo que indicaría que el modelo no tiene un sesgo fuerte respecto a de qué día proceden los datos.

En lugar de trabajar directamente con la probabilidad predicha (que es difícil de expresar como una relación lineal, dado que las probabilidades están naturalmente acotadas entre el 0% y el 100%), convertimos las probabilidades en sus probabilidades logarítmicas, utilizando la fórmula log odds = ln [ probability / (1 - probability) ]:

Importe Tipo Puntuación FICO Marca Tipo Límite de crédito Fuente Predicción Pr( Hoy ) Probabilidades de registro
$18 Deslízate por 684 Descubre Débito $12,564 Hoy 51% 0.02
$59 Chip 578 Mastercard Crédito $7,600 Hoy 78% 0.55
-$445 Chip 689 Visa Crédito $6,700 Ayer 45% -0.09
$137 Chip 734 Mastercard Crédito $7,100 Ayer 52% 0.03

A continuación, podemos ejecutar el algoritmo SHAP, que descompondrá estos estadísticos de probabilidades logarítmicas en una combinación lineal de las contribuciones de cada una de las columnas, tal como se utiliza en el modelo ML (en realidad, tendríamos que obtener los valores SHAP a nivel de característica, y luego agregarlos, pero se entiende):

Importe Tipo Puntuación FICO Marca Tipo Límite de crédito Predicción Pr( Hoy ) Predicción Pr( Hoy ) Probabilidades de registro
0.01 0.02 0.02 -0.01 0.00 -0.01 Hoy 51% 0.02
-0.03 0.01 0.41 0.19 -0.01 -0.03 Hoy 78% 0.55
0.02 -0.03 -0.05 0.02 -0.03 -0.02 Ayer 45% -0.09
0.01 -0.02 -0.01 0.01 0.02 0.02 Ayer 52% 0.03

En este caso, vemos que los valores de las columnas FICO SCORE y BRAND contribuyen significativamente a la predicción del modelo de que el segundo registro es de hoy. Examinando los valores de los datos anteriores, vemos que esto corresponde a:

  • FICO SCORE = 578

  • BRAND = 'Mastercard'

Esto sugiere que puede estar ocurriendo algo anómalo con la distribución de las puntuaciones crediticias bajas de las transacciones con Mastercard (aunque aquí sólo estamos examinando unos pocos registros; en la práctica, observaríamos la distribución de los valores SHAP resumida en los 10.000 registros diarios).

Tras normalizar y ajustar adecuadamente los valores SHAP siguiendo las técnicas del Capítulo 5, el resultado final es lo que llamamos "puntuación de anomalía". Es importante destacar que esta puntuación puede agregarse y/o fragmentarse para proporcionar muchos niveles diferentes de granularidad.

En el nivel más bajo, puedes ver la puntuación de anomalía de cada celda individual {fila, columna} de los datos muestreados. A partir de aquí, puedes agregar las puntuaciones de anomalía de una fila para encontrar las entradas más anómalas, o por conjuntos de filas para encontrar segmentos anómalos. Puedes tomar la puntuación media de anomalía por columna para encontrar las columnas más anómalas. O puedes calcular la puntuación de anomalía de toda la tabla. También puedes agrupar las puntuaciones de anomalía para encontrar correlaciones entre columnas (más sobre esto en el Capítulo 5).

Conocer la puntuación de la anomalía no sólo es importante para los datos en los que se ha producido un cambio significativo. Calculando la puntuación para cada registro de la tabla, puedes crear visualizaciones que pongan la anomalía en contexto, como en la Figura 4-7.

In this sample merchants dataset, the data quality monitoring platform has detected an issue where the value fastfood has decreased significantly in the column merchantcategorycode. The anomaly score is compared to the anomaly score for other values in this table. You can see that there was simultaneously a significant increase in the value food. These two changes may be related, as suddenly fastfood records were misclassified as simply food.
Figura 4-7. En este conjunto de datos de muestra de comerciantes, la plataforma de monitoreo de la calidad de los datos ha detectado un problema en el que el valor fastfood ha disminuido significativamente en la columna merchantcategorycode. La puntuación de anomalía se compara con la puntuación de anomalía de otros valores de esta tabla. Puedes ver que simultáneamente se ha producido un aumento significativo del valor food. Estos dos cambios pueden estar relacionados, ya que de repente los registros fastfood se clasificaron erróneamente como simplemente food. Puedes ver una versión a tamaño completo de esta imagen en https://oreil.ly/adqm_4_7.

Como puedes ver en la Figura 4-7, puedes asignar categorías legibles por humanos a las puntuaciones de anomalía para ayudar a su interpretación. Basándonos en nuestra experiencia de trabajo con una amplia variedad de conjuntos de datos, agrupamos las puntuaciones de anomalía en seis cubos diferentes, de mínimo a extremo. Estas categorías se basan en el logaritmo de la puntuación global de la anomalía: cada dos cubos representa un orden de magnitud de aumento en la puntuación:

Mínimo

Los cambios en los datos son poco o nada significativos.

Débil

Un pequeño porcentaje de los datos se ve afectado por un cambio que requiere una explicación y un estudio cuidadoso para detectarlo.

Moderado

Un pequeño porcentaje de los datos se ve afectado por un cambio evidente, o un porcentaje moderado se ve afectado por un cambio que requiere una explicación sencilla.

Fuerte

Un porcentaje significativo de los datos está afectado por un cambio obvio, o la mayoría de los datos está afectada por un cambio que se explica fácilmente (aunque no sea obvio a primera vista).

Grave

La mayoría de los datos están sujetos a un cambio que es obvio.

Extremo

Hay un cambio que obviamente afecta a casi todos los datos de hoy.

Puede que notes un umbral en la Figura 4-7:es importante utilizar la puntuación de anomalía de cada tabla para aprender un umbral personalizado sobre cuándo activar una alerta, ya que los datos de algunas tablas cambian con más frecuencia que los de otras. Hablaremos de esto en el Capítulo 5.

Armarlo con pseudocódigo

El siguiente pseudocódigo de Python ofrece un ejemplo de cómo podrías aplicar el enfoque descrito en este capítulo para encontrar anomalías entre dos días de datos y resumirlas por columnas. Pero no te tomes este código demasiado al pie de la letra; sólo pretende ilustrar los conceptos y cómo encajan a alto nivel. En particular, ten en cuenta que ignora cuestiones más complejas, como la estacionalidad, las miradas retrospectivas múltiples y las características correlacionadas, y que no implementa en detalle las piezas de muestreo, ingeniería de características o cálculo de puntuación de anomalías.

# General imports
import pandas as pd
import datetime as dt
import xgboost as xgb
from sklearn.model_selection import train_test_split
from shap import TreeExplainer

# Import hypothetical sub-modules that perform more detailed tasks
from data_access import query_random_sample
from feature_engineering import determine_features, encode_feature
from explainability import compute_column_scores

def detect_anomalies(
        table: str,
        time_column: str,
        current_date: dt.date,
        prior_date: dt.date,
        sample_size: int
    ) -> dict[str, float]:

Aquí estamos definiendo un método en pseudocódigo que detectará anomalías en los datos comparando muestras de dos fechas distintas, entrenando un modelo y calculando puntuaciones de anomalía.

Acepta como parámetros los siguientes:

table

el nombre de la tabla a consultar

time_column

el nombre de la columna de tiempo utilizada para filtrar los datos

current_date

la fecha actual para la que muestrear los datos

prior_date

la fecha anterior para la que muestrear los datos

sample_size

el número de filas a muestrear aleatoriamente para cada fecha

Devuelve un diccionario donde cada clave es un nombre de columna y cada valor es la puntuación de anomalía de la columna.

El siguiente fragmento del pseudocódigo implementa el cuerpo del método y nos lleva desde el paso de muestreo de datos hasta la explicación de las predicciones del modelo:

    # Obtain random samples of data for the specified dates
    data_current = query_random_sample(
        table, time_column, current_date, sample_size)
    data_prior = query_random_sample(
        table, time_column, prior_date, sample_size)

    # Create a binary response variable indicating the date
    y = [1] * len(data_current) + [0] * len(data_prior)

    # Concatenate the data, ensuring the order of concatenation
    data_all = pd.concat([data_current, data_prior], ignore_index=True)

    # Determine the features to build based on the data columns
    feature_list = {
        column: determine_features(data_all, column) 
        for column in data_all.columns
    }

    # Encode the features, here assuming that encode_feature returns a DataFrame
    encoded_features = [
        encode_feature(data_all, column, feature) 
        for column, feature in feature_list
    ]

    # Combine the encoded features into a single DataFrame
    X = pd.concat(encoded_features, axis=1)

    # Split data into training and evaluation sets
    X_train, X_eval, y_train, y_eval = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    # Train a machine learning model using the features and response variable
    model = xgb.XGBClassifier()
    model.fit(
        X_train, 
        y_train, 
        early_stopping_rounds=10, 
        eval_set=[(X_eval, y_eval)], 
        verbose=False,
    )

    # Obtain SHAP values to explain the model's predictions
    explainer = TreeExplainer(model)
    shap_values = explainer.shap_values(X)

    # Compute anomaly scores for each column based on the SHAP values
    column_scores = compute_column_scores(shap_values, feature_list)
    return column_scores

Otras aplicaciones

Nos hemos centrado en cómo el ML no supervisado puede ayudarte a detectar cambios estructurales repentinos en tus datos de forma continua. Sin embargo, el enfoque ML descrito en este capítulo tiene dos casos de uso adicionales que merece la pena mencionar.

La primera es encontrar problemas de calidad de datos heredados, que aparecerán como golpes y cicatrices en el historial de tus datos. Esto puede hacerse ejecutando el algoritmo descrito en este capítulo en una secuencia de fechas históricas e investigando las anomalías que encuentres. De hecho, en el Capítulo 5, describiremos cómo utilizamos este proceso, que llamamos backtesting, para medir la eficacia de nuestros modelos.

Pero ten cuidado, ya que este enfoque puede conllevar algunas complicaciones. La primera es que puedes encontrar problemas muy difíciles de explicar. A menudo hay cambios que nadie recuerda en la organización, y validar si son preocupantes o no requeriría un costoso y tedioso trabajo de detective. La segunda complicación es que puedes estar esperando encontrar algunos problemas que simplemente no existen. Esto suele ocurrir cuando se solucionan problemas conocidos de calidad de los datos y un equipo los rellena para reparar la cicatriz. Una vez que esto ocurre, ya no deberías poder detectar el problema en el historial de tus datos.

El segundo caso de uso es más significativo, y sólo lo trataremos brevemente aquí. En lugar de utilizar ML no supervisado para comparar datos de la misma tabla a lo largo del tiempo, puedes comparar dos muestras de datos de la misma tabla (o de tablas diferentes con el mismo esquema de columnas) para encontrar diferencias significativas entre ellas.

En este caso, el algoritmo ML no supervisado va a detectar, y ayudar a explicar, cualquier distribución significativa o diferencias de relación entre los dos conjuntos de datos. Como utiliza el muestreo, este enfoque puede aplicarse a tablas masivas, ¡e incluso a tablas que residen en diferentes almacenes o bases de datos de origen!

Esto permite los siguientes tipos de aplicaciones:

  • Comparar los datos brutos de una base de datos de origen con los datos depurados y transformados del almacén de destino.

  • Comparar los datos de la versión actual de tu canal ETL con los datos producidos por una nueva versión propuesta

  • Comparar una muestra de datos actual con una muestra del pasado lejano

  • Comparar datos de diferentes segmentos de negocio, geografías, categorías de productos o campañas de marketing

La Figura 4-8 muestra cómo una plataforma de monitoreo puede exponer esta función como una comprobación personalizada que los usuarios pueden configurar y ejecutar bajo demanda para comparar y contrastar conjuntos de datos de interés.

Conclusión

Tanto si eres un científico de datos experimentado como si eres nuevo en el aprendizaje automático, esperamos que este capítulo haya sido un manual útil sobre cómo construir un modelo para detectar cambios repentinos en la distribución de los datos de un día para otro. Hemos cubierto el concepto general, que se basa en intentar construir un clasificador para predecir si una fila determinada de la tabla pertenece a los datos de hoy. Si puedes hacerlo, está claro que algo ha cambiado en los datos de hoy. Puedes utilizar los valores SHAP para dar a las filas individuales de la tabla una puntuación en función de cuánto ayudaron al modelo a tomar su determinación. A efectos del monitoreo de la calidad de los datos, estas puntuaciones pueden convertirse en indicadores de lo inusuales que son esos datos, y de qué manera. Este enfoque puede incluso ampliarse para explicar cambios históricos en tus datos o comparar dos distribuciones de resultados de consultas SQL.

Using unsupervised ML, a data quality monitoring platform can expose a check that allows users to compare two datasets.
Figura 4-8. Utilizando ML no supervisado, una plataforma de monitoreo de la calidad de los datos puede exponer una comprobación que permita a los usuarios comparar dos conjuntos de datos.

Los pasos que acabamos de describir pueden parecer sencillos en la práctica, pero todo cambia cuando entras en los detalles de trabajar con datos reales. Los datos reales tienen tendencias estacionales, contienen correlaciones que no quieres tratar como cuestiones separadas, y a menudo se actualizan en el lugar sin que lo indique, por nombrar sólo algunos obstáculos. Discutiremos estos retos, y cómo puedes superarlos, en el Capítulo 5.

Get Automatizar la supervisión de la calidad de los datos now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.