Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

7. Comparación de resultados y reflexión crítica

Este capítulo corresponde a la sección 9.10.4.8 y al entregable 9.10.5. Reúne en tablas y gráficos comparativos:

  • las métricas de los seis modelos en ambos entornos;

  • los tiempos de cómputo;

  • los resultados de las tres pruebas estadísticas.

Cierra con la reflexión crítica que pide el enunciado. Todo se lee de los resultados que guardaron los capítulos 2 a 6. La única excepción es el experimento de escalabilidad de la sección 7.5, que mide tiempos de ajuste con volúmenes crecientes.

7.1 Tabla comparativa de métricas

Las métricas que dependen de un umbral (accuracy, precision, recall y F1) usan el umbral de cada modelo fijado en entrenamiento. El AUC de la tabla es el exacto, calculado con las puntuaciones del conjunto de prueba común.

Loading...
<Figure size 1800x500 with 3 Axes>

7.2 Curvas ROC

<Figure size 1600x700 with 2 Axes>

7.3 Tiempo de cómputo

Los tiempos corresponden al hardware declarado en el capítulo 2. En cada entorno se reportan tres componentes:

  • preprocesamiento: desde el CSV hasta las matrices o DataFrames listos, incluida la lectura;

  • entrenamiento: búsqueda de hiperparámetros con validación cruzada de 3 pliegues más el reajuste final;

  • predicción sobre el conjunto de prueba.

En PySpark se reporta aparte la transferencia al driver de las puntuaciones de prueba.

Loading...
Loading...
<Figure size 1700x500 with 2 Axes>

7.4 Resultados de las pruebas estadísticas

DeLong entre entornos (familia de 6 comparaciones, Holm):
Loading...
<Figure size 1700x650 with 4 Axes>
Loading...

7.5 Experimento de escalabilidad: ¿a partir de qué volumen gana PySpark?

La reflexión pregunta a partir de qué volumen de datos PySpark comienza a superar a scikit-learn. Para responder con mediciones y no con suposiciones, se mide un único ajuste (sin validación cruzada) de dos modelos representativos:

  • la regresión logística, que hace muchas pasadas baratas sobre los datos;

  • un árbol de decisión de profundidad 10, que hace pocas pasadas caras.

Cada modelo se ajusta sobre el conjunto de entrenamiento completo replicado 1, 2 y 4 veces (1,08 M, 2,15 M y 4,30 M de filas), con los mejores hiperparámetros de cada entorno. La réplica solo sirve para emular volúmenes mayores y medir tiempos: no aporta información nueva y no se usa para evaluar desempeño. En ningún caso se reduce el dataset.

Ambos entornos corren en el mismo equipo:

  • scikit-learn usa un proceso, con L-BFGS en la regresión logística y un solo hilo en el árbol;

  • Spark usa local[*], con 12 particiones y caché.

Loading...
<Figure size 1500x500 with 2 Axes>
Loading...

El experimento muestra que no existe un único volumen a partir del cual PySpark supere a scikit-learn: la respuesta depende del algoritmo. En el árbol de decisión, un ajuste único con Spark ya es más rápido con el conjunto de entrenamiento original (13,4 frente a 18,7 segundos). El cruce estimado se sitúa en torno a 0,75 millones de filas, y con 4,3 millones la ventaja de Spark es de tres veces (28 frente a 82 segundos). DecisionTreeClassifier de scikit-learn construye el árbol en un solo hilo y su costo crece linealmente con el volumen (19,8 s por millón de filas), mientras que Spark reparte el cálculo de histogramas entre los doce núcleos (4,6 s por millón de filas más un costo fijo de unos 8 segundos). En la regresión logística ocurre lo contrario: scikit-learn es más rápido en los tres tamaños y además escala mejor (1,8 frente a 3,8 s por millón de filas), porque L-BFGS opera sobre una matriz densa en memoria con operaciones vectorizadas y multihilo. Spark, en cambio, paga en cada iteración la agregación distribuida del gradiente. En este caso las rectas no se cruzan, de modo que en un único equipo Spark no alcanzaría a scikit-learn a ningún volumen.

Estas mediciones corresponden a un solo ajuste. En la búsqueda completa de hiperparámetros, GridSearchCV ajusta en paralelo hasta doce combinaciones y pliegues, mientras que CrossValidator las ajusta secuencialmente (con parallelism=1 por defecto) y materializa en caché cada pliegue. Por eso el árbol, más rápido en Spark en un ajuste aislado, resultó más lento en la validación cruzada (219 frente a 136 segundos). El límite práctico de scikit-learn en este equipo no es el tiempo sino la memoria: la matriz de entrenamiento ocupa unos 364 bytes por fila en float32, y con n_jobs=-1 cada proceso copia su pliegue, de modo que la validación cruzada paralela agotaría los 16 GB de RAM hacia los tres o cuatro millones de filas. Por encima de ese volumen, o en un clúster con varios nodos, Spark pasa a ser la opción viable y no solo la más rápida.

7.6 Modelo ganador

El modelo ganador es gradient boosting, que ocupa el primer lugar en ambos entornos con los mismos hiperparámetros: 100 iteraciones o árboles, profundidad 5 y tasa de aprendizaje 0,1. En el conjunto de prueba alcanza un AUC de 0,7198 en scikit-learn y de 0,7195 en PySpark, una diferencia no significativa (pHolm=0,33p_{Holm} = 0{,}33), con un AUC-PR de 0,389 y un F1 de 0,40. Su ventaja sobre la regresión logística (0,006 de AUC) es significativa y supera el margen de relevancia en ambos entornos. Frente al bosque aleatorio, es relevante en PySpark (0,0057) y se queda ligeramente por debajo del margen en scikit-learn (0,0047). Además, gradient boosting presenta la menor brecha entre entrenamiento y prueba de los modelos no lineales (0,004), lo que indica una buena capacidad de generalización.

Conviene dimensionar el resultado. Un AUC de 0,72, equivalente a un coeficiente de Gini de 0,44, es un desempeño moderado, típico de los modelos de originación que solo disponen de la información de la solicitud. El análisis LIME del capítulo 6 muestra que parte del error es irreducible con esas variables, y que los modelos se apoyan en buena medida en la propia tasa de interés asignada por la plataforma. Con el umbral fijado en entrenamiento, el modelo identifica el 40 % de los defaults con una precisión del 40 %, el doble de la tasa base. Desde una perspectiva práctica, la regresión logística ofrece una alternativa atractiva: pierde solo 0,006 de AUC, se entrena en segundos, sus coeficientes son directamente interpretables y es la solución más estable entre entornos. En cuanto a la implementación del ganador, los dos entornos son estadísticamente equivalentes en capacidad de ordenamiento, y la elección debe basarse en el costo. En este equipo, el GBT de Spark se entrenó en 81 minutos frente a 125 de GradientBoostingClassifier, aunque HistGradientBoostingClassifier de scikit-learn alcanzó un desempeño similar en una fracción de ese tiempo (sección 3.7).

7.7 Reflexión crítica

¿Qué entorno fue más rápido y por qué? scikit-learn completó el flujo en 178 minutos y PySpark en 229, es decir, 1,29 veces más. scikit-learn fue más rápido en el preprocesamiento (40 frente a 158 segundos), en la predicción y en cuatro de los seis modelos. La regresión logística fue 26 veces más rápida y Naive Bayes 14 veces. PySpark solo aventajó a scikit-learn en gradient boosting (81 frente a 125 minutos) y, marginalmente, en LinearSVC. La expectativa del enunciado de que PySpark superara en velocidad a scikit-learn no se cumplió en estas condiciones, y la razón es estructural. Con 1,35 millones de filas, la matriz de entrenamiento ocupa 392 MB y cabe holgadamente en la memoria de un único equipo. En ese régimen, Spark no aporta capacidad adicional y sí añade costos fijos en cada pasada sobre los datos: planificación de tareas, serialización entre Python y la JVM, agregaciones en árbol y materialización de pliegues. Estos costos dominan en los algoritmos de muchas iteraciones baratas y se amortizan en los de pocas pasadas costosas, como la construcción de árboles profundos. El caché resultó indispensable (sin él, un ajuste fue catorce veces más lento). El CrossValidator secuencial multiplicó por diez o más el costo de un ajuste aislado. Y la configuración obligatoria, con 400 particiones pensadas para un clúster, ralentizaba entre seis y diez veces los ajustes en un equipo de doce núcleos, lo que obligó a reducir las particiones de los DataFrames de modelado a doce.

¿Cuál fue más preciso? En términos prácticos, ninguno. En el modelo ganador y en la regresión logística, los dos entornos son estadísticamente indistinguibles en AUC. scikit-learn obtuvo pequeñas ventajas significativas en el árbol (+0,0027) y en el bosque (+0,0014), y en el AUC-PR del boosting (+0,0013), todas muy por debajo del margen de relevancia. PySpark superó ampliamente a scikit-learn en LinearSVC (+0,169), pero por diferencias del optimizador y no por una mayor capacidad del entorno.

¿Qué diferencias en el AUC fueron significativas tras la corrección por comparaciones múltiples, y cuáles son relevantes en la práctica? Entre entornos, cuatro de las seis comparaciones son significativas con Holm (árbol, bosque, Naive Bayes y LinearSVC), pero solo la de LinearSVC es relevante. Dentro de cada entorno, 15 de 15 pares en scikit-learn y 14 de 15 en PySpark son significativos, y 13 y 14, respectivamente, superan además el margen de 0,005. Las diferencias relevantes separan tres niveles de desempeño: gradient boosting en la cima; el bosque y la regresión logística en un nivel intermedio, prácticamente empatados; y el árbol, Naive Bayes y LinearSVC por debajo. Con 269 mil observaciones de prueba y modelos muy correlacionados, la significancia estadística es casi automática, y es el margen de relevancia, fijado antes de ver los resultados, el que permite una lectura sustantiva.

¿Qué diferencias de implementación explican las discrepancias? La más importante es la formulación de LinearSVC. scikit-learn solo admite la pérdida hinge mediante el dual de liblinear, que además regulariza el intercepto y no converge con valores grandes de CC. Spark optimiza el primal con OWL-QN sin penalizar el intercepto. Con la misma pérdida, esto produce un AUC de 0,46 frente a 0,63. En los árboles, Spark discretiza cada variable continua en 32 intervalos por cuantiles aproximados, mientras que scikit-learn evalúa todos los cortes posibles. Además, el bootstrap y el submuestreo de variables del bosque siguen generadores aleatorios distintos que dependen del particionado. En la regresión logística, la relación C=1/(λn)C = 1/(\lambda n) es solo aproximada porque CC pondera la suma de las pérdidas y regParam su promedio, y porque en validación cruzada cada pliegue tiene 2n/32n/3 observaciones; aun así, la superficie de validación es tan plana que ambos entornos llegan al mismo AUC con parámetros distintos. Otros detalles menores también influyen: Spark estima la desviación estándar muestral y scikit-learn la poblacional, scikit-learn añade un término de estabilidad a las varianzas de Naive Bayes, y los pliegues de validación cruzada difieren entre entornos. Ninguna de estas diferencias altera las conclusiones sobre los modelos.

¿Qué limitaciones tiene la prueba de DeLong en este diseño experimental y cómo la complementan McNemar y el bootstrap pareado? ¿Coinciden las conclusiones? DeLong solo cuantifica la variabilidad debida al muestreo del conjunto de prueba, no la del entrenamiento (semillas, pliegues o bootstrap). Además, supone observaciones independientes y resume el desempeño en todos los umbrales, sin informar sobre el punto de operación. McNemar evalúa las decisiones en el umbral elegido, aunque pondera por igual los falsos positivos y los falsos negativos, cuyo costo en crédito es muy distinto. El bootstrap pareado prescinde de aproximaciones asintóticas y extiende la comparación al AUC-PR y al F1, más informativos para la clase minoritaria. Las conclusiones coinciden plenamente en lo esencial: DeLong, el bootstrap del AUC y McNemar concuerdan en las ocho comparaciones complementarias. Las únicas discrepancias aparecen en el AUC-PR y el F1, que capturan aspectos del desempeño que el AUC no refleja. Ninguna de las tres pruebas resuelve la limitación de no considerar la variabilidad del entrenamiento; para ello se requerirían esquemas de remuestreo del entrenamiento, como 5×2cv, cuyo costo computacional era prohibitivo aquí.

¿A partir de qué volumen de datos PySpark comienza a superar a scikit-learn? Según el experimento de la sección 7.5, depende del algoritmo y del esquema de entrenamiento. En un ajuste único de árbol, Spark ya es más rápido a partir de unos 0,75 millones de filas y su ventaja crece con el volumen. En la regresión logística no se observa cruce en un único equipo. Con validación cruzada, el paralelismo entre ajustes de GridSearchCV desplaza el cruce hacia volúmenes mayores. En consecuencia, la ventaja decisiva de Spark en un único equipo aparece cuando los datos dejan de caber en la memoria de un proceso de scikit-learn, lo que en este equipo ocurriría hacia los tres o cuatro millones de filas con validación cruzada paralela. En un clúster, Spark escala horizontalmente y su ventaja se manifiesta a volúmenes muy superiores a los de este problema.

¿Qué aporta LIME y cuáles son sus limitaciones en entornos distribuidos? LIME permitió verificar que los dos modelos ganadores se equivocan por las mismas razones y que se apoyan en variables con sentido económico (tasa de interés, plazo, puntaje FICO, endeudamiento), lo que aporta validez sustantiva a los modelos y ayuda a distinguir el error irreducible del error del modelo. Su limitación principal en un entorno distribuido es el costo: cada explicación exige un trabajo de Spark completo, y explicar una predicción tomó unas 500 veces más que en scikit-learn (380 frente a 0,75 segundos). A ello se suman la necesidad de disponer en memoria local de las estadísticas del entrenamiento y la dificultad para trasladar el modelo al lugar donde se ejecuta el explicador. A estas se añaden las limitaciones generales del método: perturbaciones que ignoran la correlación entre variables, dependencia de la discretización y del kernel, y carácter estrictamente local de las explicaciones.

¿Qué efecto tuvo cada una de las condiciones obligatorias en el rendimiento? El uso del dataset completo (1,35 millones de préstamos) hizo que el entrenamiento de gradient boosting en scikit-learn superara las dos horas. A cambio, produjo intervalos de confianza muy estrechos (±0,0024 de AUC), que hacen significativas diferencias ínfimas y obligan a interpretar con el margen de relevancia. El caché obligatorio fue la condición de mayor impacto positivo: sin él, cada iteración releería el CSV de 1,7 GB, con una penalización de catorce veces. El uso exclusivo de CrossValidator con ParamGridBuilder, sin bucles manuales, garantizó un procedimiento de selección equivalente al de GridSearchCV, pero con ajustes secuenciales y pliegues materializados en caché, lo que multiplicó el costo y la presión sobre la memoria. La configuración de la sesión tuvo efectos mixtos. Las 400 particiones de spark.sql.shuffle.partitions y spark.default.parallelism resultaron contraproducentes en un único equipo (entre seis y diez veces más lentas) y se mitigaron con un coalesce justificado experimentalmente. spark.driver.memory=8g, en un equipo de 16 GB compartido con el sistema operativo, dejó un margen estrecho, que contribuyó a los fallos por memoria documentados en el capítulo 4. spark.executor.memory no tiene efecto en modo local, y spark.memory.fraction=0.8 favoreció el caché de los pliegues. La prohibición de .toPandas() y .collect() obligó a calcular las métricas de forma distribuida y limitó la transferencia al driver a las puntuaciones de prueba (42 segundos en total para los seis modelos), sin impacto relevante en el tiempo, aunque sí en la complejidad del código y de la interpretabilidad. Por último, la partición común guardada en disco fue la condición metodológica clave: sin ella, las pruebas pareadas entre entornos no habrían sido posibles.

En síntesis, en un problema de 1,35 millones de observaciones que cabe en la memoria de un solo equipo, scikit-learn y PySpark alcanzan desempeños estadísticamente equivalentes en los modelos bien especificados, y scikit-learn resulta en general más rápido. La elección del entorno debe guiarse por el volumen de los datos y la infraestructura disponible, más que por la precisión esperada. La elección del algoritmo, y en particular de su formulación y su optimizador, como muestra el caso de LinearSVC, pesa más en el resultado que la elección de la librería.