Ok, creemos un modelo de Machine Learning para detectar fraudes financieros.

Carlos Ortiz Urshela
10 min readJan 15, 2021

--

Para mi una de las aplicaciones más interesantes y útiles en la práctica del ML es la detección de fraudes en transacciones financieras, un campo de acción que puede ser manejado desde el enfoque de modelos de clasificación o también desde el análisis de anomalías.

En general explorar y experimentar con modelos para detección de fraudes se hace un poco más complejo por la falta de set de datos públicos, lo cual es entendible ya que pocas entidades financieras están dispuestos a poner información financiera y operativa potencialmente sensible en el dominio público.

El objetivo de este articulo es exponer una aproximación sencilla al problema de la la detección de fraudes, que espero le pueda servir a alguien ya sea como una referencia inicial, o quizá como punto de arranque para la creación de modelos más complejos.

Este proyecto esta basado en un dataset sintético creado por PaySim quien ha generado a partir de transacciones reales privadas de un servicio de dinero móvil implementado en un país africano, un set de datos sintético que simula una operación transaccional normal a la cual se ha inyectado comportamiento malicioso para luego evaluar el desempeño de modelos de detección de fraudes. El set de datos original se encuentra disponible en la plataforma Kaggle (https://www.kaggle.com/ntnu-testimon/paysim1).

El código de este proyecto se encuentra en

https://github.com/ortizcarlos/fraud_detection/blob/master/Financial%20fraud%20detection.ipynb

Iniciemos con algo de data exploration

Iniciemos explorando los datos para tener un entendimiento de la información con datos clave como el tamaño del dataset, columnas, datos faltantes, outliers, etc.

El set de datos es moderadamente grande con:6362620 Filas y
11 Columnas

Definición de las columnas del dataset (copiado desde Kaggle)

step — maps a unit of time in the real world. In this case 1 step is 1 hour of time. Total steps 744 (30 days simulation).

type — CASH-IN, CASH-OUT, DEBIT, PAYMENT and TRANSFER.

amount — amount of the transaction in local currency.

nameOrig — customer who started the transaction

oldbalanceOrg — initial balance before the transaction

newbalanceOrig — new balance after the transaction

nameDest — customer who is the recipient of the transaction

oldbalanceDest — initial balance recipient before the transaction. Note that there is not information for customers that start with M (Merchants).

newbalanceDest — new balance recipient after the transaction. Note that there is not information for customers that start with M (Merchants).

isFraud — This is the transactions made by the fraudulent agents inside the simulation. In this specific dataset the fraudulent behavior of the agents aims to profit by taking control or customers accounts and try to empty the funds by transferring to another account and then cashing out of the system.

isFlaggedFraud — The business model aims to control massive transfers from one account to another and flags illegal attempts. An illegal attempt in this dataset is an attempt to transfer more than 200.000 in a single transaction.

Tipos de transacciones y distribución

'PAYMENT', 'TRANSFER', 'CASH_OUT', 'DEBIT', 'CASH_IN'

Distribución por tipo de transacción (type)

Miremos cuántos fraudes tenemos.

Solo 8213 filas de un total de algo mas de 6 Millones representan fraudes ( solo un 0.13% ! ). Esto es casi de esperar, por lo general en un set de datos financiero el número de muestras con fraudes es casi siempre muy pequeño comparado con el número total de transacciones.

Este desbalance de clases (Los fraudes representan solo el 0.13% del total) es un elemento muy importante a ser considerado al momento de crear nuestro modelo.

Veamos ahora cuantos fraudes hay por tipo de transacción:

De acuerdo a los datos, los fraudes se han presentado solamente en las transacciones tipo CASH_OUT y TRANSFER

Distribución del monto de las transacciones ( Columna amount)

Visualicemos histogramas del monto de la transacciones por tipo de transacción (type), intentando identificar algún patrón en los datos.

Los histogramas anteriores muestran que para todos los casos los datos se encuentran concentrados a la izquierda con outliers muy marcados en los tipos de transacción CASH_OUT y TRANSFER, precisamente los tipos de transacciones donde tenemos fraudes. Veamos si al visualizar el rango intercuartil de los datos podemos ver algo más interesante.

Visualicemos ahora la distribución del monto en las transacciones fraudulentas.

El histograma de los fraudes muestra igualmente que los datos se encuentran concentrados a la izquierda (asimetría positiva) y outliers en 10M.

Feature Engineering

Iniciemos con la conversión de la columna type, la cual es categórica, con los valores (‘PAYMENT’, ‘TRANSFER’, ‘CASH_OUT’, ‘DEBIT’, ‘CASH_IN’)

Para la columna type se podría considerar en una representación ordinal como 1 = PAYMENT, 2 = TRANSFER, 3 = CASH_OUT y así en adelante. Sin embargo esto podría hacer que el modelo aprendiera que PAYMENT y TRANSFER son mas similares que DEBIT y CASH_IN, siendo que en realidad no hay una relación ordinal entre ellos. Por lo cual es mejor hacer una codificación tipo One Hot Encoding.

Correlación de las features con el indicador de fraude

Veamos ahora la correlación de todas las columnas del dataset con respecto al label (isFraud), que es valor que nuestro modelo debe ser capaz de predecir.

Podemos observar que las columnas que más se correlacionan al label isFraud son:

De forma positiva: amount, TRANSFER, isFlaggedFraud, CASH_OUT, oldbalanceOrg

De forma negativa: PAYMENT y CASH_IN (precisamente no hay fraudes para este tipo de transacciones)

Selección de datos para entrenamiento y pruebas

Antes de dividir el conjunto de datos en entrenamiento y pruebas, es importante buscar un método de división que sea mas eficaz para el modelo que uno puramente aleatorio. En este caso siendo que tenemos tan poco registros que indican fraudes, sería bueno que el set de datos de entrenamiento y pruebas tenga una igual proporción de filas con fraudes. Este tipo de división se llama división estratificada y para ello vamos a utilizar StratifiedShuffleSplit de scikit learn.

Fase de selección y entrenamiento del modelo

Iniciemos creando un modelo basado en el algoritmo Random Forest, el cual es un algoritmo flexible y fácil de usar, que produce buenos resultados incluso sin ajustar los hiper-parámetros, además de eso es útil para manejar set de datos grandes y con una muchas features, aunque este set de datos en particular no es tan grande.

Luego de entrenar el modelo encontramos Wow!, un accuracy del 99.99%! pero… no tan rápido! no nos dejemos deslumbrar por ese valor. Recordemos que el número de filas con fraudes representa SOLO el 0.13% del total, por lo cual un Accuracy del 99.99% solo nos puede estar diciendo que nuestro modelo es excelente para predecir transacciones NO fraudulentas :)

Para los modelos de clasificación existen métricas más adecuadas para evaluar el desempeño, entre ellas la matriz de confusión y la precision y recall. Cuando nuestro modelo predice un fraude, precision nos dice con que porcentaje de exactitud lo ha hecho. Mientras que recall ( también conocida como sentitividad o exhaustividad) nos dice que porcentaje del total de fraudes, es nuestro modelo capaz de identificar.

Primero calculamos la matrix de confusión, donde obtenemos: TP: true positives, FP: False positives, TN: True negatives y FN: False negatives. Con estos calculamos precision y recall.

Los resultados siguen siendo muy buenos! tenemos una precisión del 100% y un recall de 91.64%!

Es decir nuestro modelo es capaz de reconocer el 91.64% de los fraudes y cuando predice un fraude lo hace con un 100% de exactitud!

Sin embargo, debemos ser cautos y asegurar que los buenos valores de estas métricas no tengan su origen simplemente en que el modelo esta haciendo overfit de los datos de entrenamiento…

Cross validation

Para asegurar que el desempeño del modelo es tan bueno como lo dicen las métricas anteriores, implementemos un mecanismo llamado validación cruzada (Cross Validation), que básicamente consiste en dividir el set de datos de entrenamiento en k bloques (folds) de datos más pequeños, y con cada uno de estos:

El modelo se entrena utilizando k-1 bloques del set de datos de entrenamiento.

El modelo resultante se valida con el bloque o fold restante que no ha sido visto por el modelo, usando una métrica predefinida (en este caso F1).

F1 que es una métrica calculada a partir de la precisión y la exhaustividad (recall), básicamente un F1 alto indica que tanto precision como recall son altos. La validación cruzada es una operación computacional costosa, por lo tanto hay que estar preparados para esperar algunos minutos…

Con los resultados de la validación cruzada podemos notar que el desempeño del modelo ya no es tan fantástico como inicialmente pensamos. Anteriormente la precision era del 100% y el recall del 99%, ahora podemos notar que el F1 es en promedio del 85%, un valor que sin embargo no es tan malo, al contrario muy bueno para tener un modelo con el que iniciar.

Evaluando el modelo con lo datos de test

Luego de evaluar el modelo con el set de datos de pruebas, el cual no ha sido visto por el modelo, encontramos que precision sigue siendo alta 98% mientras que el recall es del 78.5%.

Esto nos indica que cuando el modelo predice un fraude lo hace correctamente el 98% de las veces, y es capaz de identificar el 78.5% del total los fraudes. en otras palabras el modelo puede dejar de identificar 20 de cada 100 fraudes :s lo cual podría ser un factor todavía muy riesgoso dependiendo de las expectativas de la organización financiera que lo va a usar.

Refinemos el modelo

Intentemos mejorar el rendimiento del modelo, ahora siguiendo una estrategia para lidiar con el desbalance de muestras con fraudes, que como sabemos es solo del 0.13%. En este caso en particular vamos a aumentar aleatoriamente el número de muestras con fraudes usando el módulo resample de scikit learn, aumentando 50 veces el número de muestras.

Adicionalmente, cuando visualizamos la distribución del monto de la transacción (columna Amount) notamos outliers en los tipos de transacción (columna type) CASH_OUT y TRANSFER, también podríamos si es necesario, eliminar estos outliers que afectan fuertemente el escalamiento de la columna amount.

Luego del proceso, queda ahora que las muestras con fraudes representan el 6% del total. Como estamos aumentando el numero de muestras del set de datos original, debemos nuevamente ejecutar los pasos de feature engineering.

Tenemos un nuevo dataset, así que entrenemos nuevamente el modelo y miremos las métricas

En este nuevo modelo el recall es 96.24%! mucho mejor que el anterior 91.64%.

Hagamos validación cruzada para asegurarnos si el modelo sigue haciendo overfit de los datos. Continuamos haciendo una validación con 4 folds y F1 como scoring.

Sorprendente! F1 ahora es 0.99. Nuestro modelo parece lucir después que hemos lidiado con el desbalance de muestras con fraudes!

Evaluando el nuevo modelo con lo datos de test

Luego de evaluar el modelo con el dataset de test, el cual no ha sido visto por el modelo, encontramos que precision sigue siendo alta 99.8%, mientras que recall ahora es del 96.3%. Un modelo bastante mejor que el anterior.

Esto nos indica que cuando el modelo predice un fraude lo hace correctamente un 99.8% de las veces, y es capaz de identificar el 96.3% del total los fraudes!

Consideraciones

Es este modelo perfecto? realmente no lo creo, podemos decir que es bueno para el set de datos actual y que es un buen comienzo para pensar en un set de datos más complejo y rico que pueda representar con mas detalle la realidad de las transacciones financieras.

Si me encargaran la tarea de crear un modelo nivel productivo de predicción de fraudes financieros, comenzaría por tener set de datos mucho mas grande, y que incluyera más features, las cuales podrían ser:

dayOfWeekTx

hourOfDayTx

monthTx

isBlackListedIp

isFrequentDestAccount

isFrequentMerchant

isFrequentLocation

invalidLocationInTimeFrame

channelWeb

channelATM

channelMobile

isAnAverageTxAmount

isLookABurstOfTxs

Todas las features anteriores pueden ser calculadas sin mayor problema justo al momento de evaluar la posibilidad de fraude de una transacción en curso. Solo es cuestión de crear los pipelines de datos necesarios para capturar las estadísticas de cada cliente.

Si te gustó este post agradecería que lo compartieras en tus redes sociales :) gracias!!

El código de este proyecto se encuentra en

https://github.com/ortizcarlos/fraud_detection/blob/master/Financial%20fraud%20detection.ipynb

--

--

Carlos Ortiz Urshela
Carlos Ortiz Urshela

Written by Carlos Ortiz Urshela

Machine Learning Engineer | Enterprise Solutions Architect — Interested in AI-based solutions to problems in healthcare, logistics, and HR. CTO of Klever.