Predicción de default con datos de Lending Club


Si buscás “Lending Club default prediction” vas a encontrar notebooks que reportan AUC de 0,90 para arriba, mientras que el rango publicado para trabajos sin fuga de información va de 0,69 a 0,73. Este proyecto llega a 0,72, y entender esa brecha terminó siendo la lección más valiosa de todo el ejercicio: la diferencia está en qué información se le permite ver al modelo. Estas son las decisiones detrás del trabajo, callejones sin salida incluidos.

La trampa escondida en 151 columnas

El dataset de Lending Club tiene 2,26 millones de préstamos y 151 columnas. El problema es que la mayoría de esas columnas describen lo que pasó después de otorgar el préstamo. Pagos acumulados. Montos recuperados por cobranzas. El puntaje FICO actualizado, meses o años después. Programas de alivio y de negociación de deuda.

Si entrenás un modelo con esas columnas, estás entrenando con la respuesta. Un préstamo con pocos pagos acumulados obviamente cayó en default, pero el día que el prestamista tiene que decidir, ese número todavía no existe. Esto se llama fuga de información (data leakage), y en este dataset está por todos lados. Los modelos con 0,90+ casi siempre están leyendo el futuro.

Así que la primera decisión de modelado, y la más importante, no tuvo nada que ver con algoritmos. Fue una sola pregunta, aplicada a 151 columnas: ¿el prestamista conocía este dato el día de la aprobación? Sobrevivieron veintiocho columnas.

Que los datos respondan las decisiones de diseño

Tres momentos donde una medición rápida reemplazó una suposición:

El bloque del buró de crédito. Unas 60 columnas de historial crediticio detallado parecían prometedoras, pero venían llenas de faltantes. Agrupar los vacíos por año de originación mostró el patrón: 100% vacío hasta 2011, mitad vacío en 2012, completo desde 2013. Las columnas se incorporaron al esquema de la plataforma en 2013; el faltante era estructural, no aleatorio. Dos columnas distintas compartían exactamente la misma tasa de vacíos (0,519816), que es la huella digital de un bloque que aparece junto. Descarté el bloque y conservé el puntaje FICO, que existe en todo el período y lo resume.

El problema de la censura. Un préstamo a 36 meses otorgado en 2017 no puede estar “terminado” a fines de 2018, salvo que el cliente haya pagado todo anticipado o dejado de pagar casi de inmediato. Los cohortes recientes solo aportan casos extremos. Medir la proporción de préstamos terminados por año lo hizo visible: 100% en 2011-2013, 38% en 2017, 11% en 2018. Ese sesgo no se puede arreglar, pero se puede medir y declarar como limitación.

El estado de residencia. Las tasas de default van de aproximadamente 14% (Oregon) a 24% (Arkansas) entre estados grandes, así que la geografía trae señal real y yo la quería adentro. Incluirla bien no es trivial: para evitar fuga, cualquier agrupamiento de estados por riesgo tiene que aprenderse solo del conjunto de entrenamiento. Eso pedía más tiempo del que este proyecto tenía, así que la variable quedó afuera y la geografía pasó a la lista de mejoras.

Faltantes que traen información

Mi hallazgo favorito: el 5,8% de los solicitantes no declaró su antigüedad laboral. Su tasa de default es del 27%, siete puntos arriba del promedio. El vacío en sí mismo es una señal (no declarar dice algo del solicitante), así que en vez de imputarlo, se convirtió en su propia categoría. Mientras tanto, los valores declarados casi no mueven la aguja: de “menos de 1 año” a “10+ años”, la tasa apenas va del 21% al 19%.

La lección: antes de rellenar faltantes, siempre revisar qué predice el faltante en sí.

El accuracy engaña con clases desbalanceadas

Solo el 20% de los préstamos terminados cayó en default. Un modelo que predice “todos pagan” logra 0,80 de accuracy detectando cero defaults. Mi primera regresión logística hizo casi exactamente eso: 0,802 de accuracy, 8,5% de los defaults detectados.

El arreglo empieza por la métrica, no por el modelo. Un default no detectado le cuesta al prestamista el capital prestado (unos $15.000). Rechazar por error a un buen pagador solo le cuesta el interés (unos $2.500). Con esa relación de costos de 7 a 1, la métrica que importa es el recall de la clase default, cuántos defaults reales atrapás, controlada por la precision para que el modelo no rechace a todo el mundo. Con el balanceo de clases, la misma regresión logística pasó de atrapar el 8,5% de los defaults al 64%, cambiando accuracy (de 0,80 a 0,66) por un modelo que de verdad sirve. Pérdida esperada sobre el conjunto de prueba: de $804M sin modelo a $453M con el ganador.

Comparación de modelos

Curvas ROC de los tres mejores modelos

Comparé regresión logística, un árbol de decisión, un SVM lineal, un random forest y gradient boosting, todos con exploración de hiperparámetros y la misma métrica. El resultado me sorprendió: terminaron todos en una banda muy angosta (recall 0,63-0,68, F1 0,42-0,43).

El gradient boosting ganó en todas las métricas (recall 0,678, AUC 0,720, entrenado en 24 segundos). Pero la lectura honesta de la tabla es que el techo lo pone la información disponible al originar, no el algoritmo. Cuando cinco modelos muy distintos empatan, dejá de tunear y mirá tus datos.

Dos notas al margen de la comparación:

  • El random forest necesitó 4 veces el tiempo de entrenamiento del boosting para quedar atrás en todas las métricas.
  • La regresión logística pelada entregó casi la misma performance en 5 segundos, con coeficientes que se pueden explicar. En un entorno regulado como un banco, ese es un argumento serio.

Oportunidades de mejora

Cosas que este proyecto dejó sobre la mesa: recalcular el ratio deuda/ingreso incluyendo la cuota del préstamo nuevo (una variable sugerida en Namvar et al. 2018, el paper de referencia para trabajos sin fuga sobre este dataset); incorporar la geografía con agrupamientos de estados aprendidos sobre el conjunto de entrenamiento; y ajustar el umbral de decisión con una función de ganancia en dólares en vez de un corte fijo.

El análisis completo, con todos los números y las figuras, está en el repositorio.