Full text
Equation Chapter 1 Section 1 Trabajo Fin de Máster Máster en Ingeniería Electrónica, Robótica y Automática Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Autor: Jesús Tamarit Domínguez Tutor: Rubén Martín Clemente Dpto. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2021
Dp to . Ingeniería de Sistemas y Automática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2019
Trabajo Fin de Máster Máster en Ingeniería Electrónica, Robótica y Automática Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Autor: Jesús Tamarit Domínguez Tutor: Rubén Martín Clemente Profesor titular Dpto. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2021
Trabajo Fin de Máster: Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Autor: Jesús Tamarit Domínguez Tutor: Rubén Martín Clemente El tribunal nombrado para juzgar el Trabajo arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2021 El Secretario del Tribunal
A mi familia A mis maestros
Resumen En este proyecto se ha abordado el problema de la detección de fraude en transacciones bancarias utilizando un modelo de inteligencia artificial ajustado y entrenado con un conjunto de datos relativos a dichas transacciones que se ha procesado anteriormente. Estos datos contienen información sobre cada transacción y su emisor y se han cargado usando librerías de manejo de datos y reduciendo tanto su uso de memoria como su dimensionalidad para mejorar el entrenamiento del modelo. Por otro lado, se ha realizado un análisis gráfico de las distintas características a fin de denotar las más importantes. Posteriormente, se ha realizado el entrenamiento de un modelo de Inteligencia Artificial con estos datos. Además, se ha realizado un ajuste de los parámetros de dicho modelo para conseguir los mejores resultados, los cuales se han analizado. Finalmente, se han propuesto diferentes formas de mejorar y extender el proyecto utilizando distintas técnicas y procedimientos. Abstract In this project, the problem of detecting fraud in banking transactions has been addressed using an artificial intelligence model adjusted and trained with a set of data related to said transactions that has been previously processed. This data contains information about each transaction and its issuer and has been loaded using data management libraries and reduced its memory usage and dimensionality to improve model training. On the other hand, a graphic analysis of the different characteristics has been carried out in order to denote the most important ones. Subsequently, the training of an Artificial Intelligence model with these data has been carried out. In addition, an adjustment of the parameters of said model has been carried out to achieve the best results, which have been analyzed. Finally, different ways to improve and extend the project have been proposed using different techniques and procedures.
3 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito relacionadas con los datos de las tarjetas (Número, fabricante, etc.) y los datos relativos al dispositivo emisor de la transacción (Sistema operativo, tipo de dispositivo, etc.). Además, cada transacción tendrá relacionado un número de identificación (TransactionID) y una variable de tiempo (TransactionDT). Estas características se verán con más detalle en el Apartado 3.1. Los nodos ocultos se organizan en capas de tamaño variable y se encargarán de realizar los distintos cálculos y relaciones entre las diferentes entradas, asignando a cada nodo un peso distinto durante el entrenamiento. Finalmente, los nodos de salida agrupan todas las relaciones anteriores y, en el caso de un sistema clasificador como el que se tiene, asignará a cada clase de salida una probabilidad de predicción. Como en este caso las únicas salidas posibles a predecir van a ser “fraude” y “no fraude” se dice que estamos ante un sistema de clasificación binario. Utilizando un modelo con un esquema similar al de la Figura 1-3, entrenándolo usando los datos de entrada procesados y ajustando los parámetros del entrenamiento del modelo se conseguirá obtener un sistema que detecte el fraude con buena precisión. Por último, se utilizarán los datos del dataset de prueba para realizar la predicción del fraude con sus datos y exportar esas predicciones a la web Kaggle. En esta web se evaluará de forma online el rendimiento de este modelo de predicción respecto al de otras personas. 1.2 Objetivos y Estructura de la memoria Entre los objetivos del proyecto se destacan: Carga y procesamiento correcto de los datos disponibles. Realización de un análisis de los datos disponibles para una mejor comprensión de ellos. Implementación del modelo de Inteligencia Artificial para su entrenamiento con los datos. Búsqueda de los parámetros del modelo más adecuados para el entrenamiento. Entrenamiento del modelo y análisis de los resultados obtenidos. Subida de las predicciones con el dataset de prueba a Kaggle y obtención de un buen resultado. La memoria se compone de 7 capítulos mediante los cuáles se irán ampliando y comentando más detalladamente estos objetivos y cómo se han llevado a cabo para llegar finalmente a las conclusiones de estos.
Introducción 4
5 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 2 PREPARACIÓN Y CARGA DE DATOS l primer paso para el desarrollo del proyecto va a consistir en la preparación del entorno Python que se va a utilizar importando todos los módulos necesarios así como los datos disponibles sobre las transacciones monetarias. Posteriormente se hará una observación de los datos disponibles con el fin de observar qué características van a ser las mejores para entrenar a la inteligencia artificial para que ofrezca los mejores resultados posibles. 2.1 Preparación del entorno Como ya se ha comentado, el proyecto va a estar escrito en el lenguaje de programación Python. Python es un lenguaje de programación interpretado, multiparadigma, dinámico y multiplataforma. Posee una licencia de código abierto y desde 1991 se ha ido desarrollando en múltiples versiones. En este proyecto concreto se va a utilizar la versión 3.9.4 del lenguaje. Figura 2-1. Logo de Python Con la instalación base de Python se puede programar utilizando la línea de comandos, aunque es bastante poco práctico. Para ello se utilizará el software Visual Studio Code como IDE o Entorno de desarrollo integrado que actuará tanto como editor de código como intérprete de este y terminal de salida de la ejecución del código. Este software pertenece a Microsoft y cuenta con gran cantidad de extensiones y diferentes opciones de configuración que permiten utilizarlo en la mayoría de los lenguajes de programación existentes con gran comodidad. Una vez se tengan instalados los programas anteriores se procederá a instalar los módulos o librerías de Python que se van a utilizar para el desarrollo del proyecto. Se van a comentar las características y funciones de las librerías más importantes: Pandas: Se trata de una librería para el manejo de datos. Con ella se puede realizar E
Preparación y Carga de Datos 6 desde la importación de los datos desde una gran cantidad de formatos hasta la transformación de los datos en sí. Para ello almacena los datos en “dataframes” en los que guarda la información de las diferentes columnas y filas. Con ellos se pueden realizar funciones como ordenar, transformar, cambiar tamaños, cambiar tipos, pintar gráficas, etc. Estos “dataframes” se pueden utilizar directamente en el resto de las funciones de otras librerías ya que Pandas se encuentra bastante extendido y es de uso común. Figura 2-2. Logo de Visual Studio Code Numpy: Esta librería es una de las más importantes de Python y de ella dependen la mayoría de las librerías que se usan en el proyecto. Numpy proporciona a Python numerosas funciones matemáticas de alto nivel, permitiendo además la creación de matrices y vectores de grandes dimensiones. Esta librería se creó debido a que Python no fue diseñado inicialmente para la computación numérica y con la librería Numpy se consigue un rendimiento mucho mayor que con Python base tanto en rapidez de escritura del código como en la ejecución. Figura 2-3. Comparación entre Matplotlib y Seaborn Matplotlib: Se trata de otra de las principales librerías de Python que se usa para la creación de visualizaciones de datos tanto estáticas como animadas o interactivas. Una de sus mayores ventajas es que permite un control total de la personalización de los
7 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito gráficos, pudiendo elegir entre diferentes colores, tipos de líneas, personalización de leyendas y ejes, etc. Los gráficos se realizan de forma rápida y sencilla y pueden ser exportados como imágenes o guardados en un archivo local. Seaborn: Es una librería de visualización de datos en Python. Está basada en Matplotlib y sirve como su extensión. Sirve como una integración entre Matplotlib y Pandas ya que ayuda a explorar y entender los datos utilizando gráficos más avanzados e informativos. Por ejemplo, en Seaborn se pueden crear “pairplots” que relacionen características de un “dataframe” entre sí. Figura 2-4. Ejemplo de un Pairplot Os: Este módulo de Python viene incluido en la instalación base del lenguaje y se usa para utilizar las funcionalidades del sistema operativo desde el propio código. En este
Preparación y Carga de Datos 8 proyecto se va a utilizar para modificar el “path”. Sklearn: Esta librería de código abierto contiene funciones relacionadas con la inteligencia artificial. Está diseñada para operar con Pandas y Numpy y añade algoritmos de clasificación, regresión y “clustering” además de otros algoritmos como k-medias, máquinas de vectores de soporte (SVM), bosques aleatorios de predictores o técnicas de potenciación del gradiente. Contiene varios paquetes de funciones, como ejemplo se muestran los siguientes: o Metrics: Contiene distintas funciones relacionadas con mediciones y cálculos de distintos errores, medias y varianzas como por ejemplo el MAE o la curva ROC. o Preprocessing: Las funciones que contiene este paquete están relacionadas con el preprocesamiento de los datos antes de usarlos para el entrenamiento del modelo. Por ejemplo, contiene los LabelEncoder y OneHotEncoder para codificar variables categóricas. o Model_Selection: En este paquete se encuentran funciones relacionadas con los modelos de sklearn . Algunas de ellas se utilizan para realizar la división entre el set de entrenamiento y el de prueba, para realizar optimización de los parámetros o para realizar divisiones de los datos de entrenamiento para mejorar la generalización. Figura 2-5. Paquetes de SKLearn
9 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Lightgbm: Se trata de uno de los muchos modelos entrenables creados a partir del algoritmo de potenciación del gradiente en árbol de decisión (GBDT). En esta implementación del algoritmo se incluyen mejoras respecto al resto de modelos que le permiten mantener la misma precisión que el resto mientras disminuye el tiempo de entrenamiento del modelo hasta 20 veces. En el Apartado 5 se tratará con mayor profundidad este modelo. Time/Datetime: Estos dos módulos de Python se incluyen con su instalación base y se pueden utilizar para acceder a la hora y fecha del sistema. En este proyecto se utilizarán para calcular el tiempo que tardán los algoritmos en ejecutarse. GC: Se trata del módulo Garbage Collector incluido con la instalación de Python y que permitirá reunir variables u otro tipo de información que se ha quedado guardada en memoria y eliminarla para reducir el consumo de la memoria RAM del ordenador. Warnings/Logging: Los módulos warnings y logging está incluidos en Python base y se utilizan para ajustar el nivel de información que queremos que aparezca a través de la consola. En este caso Warnings se usará para filtrar los mensajes menos importantes y Logging se utilizará para eliminar los mensajes menos importantes de un paquete en concreto. Graphviz: Este módulo consiste en un generador de diagramas de grafos de código abierto. Con él se pueden generar diagramas de todo tipo, modificarlos de forma personalizada e incluso guardarlos en un archivo local o exportarlos a un archivo de imagen. En este caso se va a utilizar para generar diagramas de grafos del modelo ya entrenado, en el cuál se van a poder observar todos los nodos intermedios del modelo, así como sus entradas y salidas. Figura 2-6. Ejemplo de un diagrama de grafos
Preparación y Carga de Datos 10 El código relacionado con la preparación del entorno es el siguiente: Código 2.1 – Preparación del entorno import numpy as np import pandas as pd import seaborn as sns import os import matplotlib.pyplot as plt from sklearn.metrics import mean_absolute_error, roc_curve import lightgbm as lgb import time import datetime from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold, KFold, RepeatedKFold, GroupKFold, GridSearchCV, train_test_split, TimeSeriesSplit from sklearn import metrics import gc import warnings warnings.filterwarnings("ignore") import graphviz import logging logging.getLogger('googleapicliet.discovery_cache').setLevel(logging.ERROR) 2.2 Importación de los datos En siguiente paso en el proceso es la carga de los datos. Estos datos están almacenados en varios archivos CSV o de variables separadas por comas. Los archivos que se van a utilizar en este proyecto se encuentran alojados en la web en la página “Kaggle” y son en total cuatro archivos, dos archivos relacionados con los datos de entrenamiento y otros dos archivos relacionados con los datos de prueba. Los nombres completos de los archivos son los siguientes: train_transaction.csv train_identity.csv test_transaction.csv test_identity.csv La estructura de estos archivos se analizará posteriormente en el Apartado 3.1. Existen dos opciones a la hora de importar estos archivos, las cuales se van a analizar a continuación.
11 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 2.3.1 Usando Google Drive La primera de las opciones disponibles para cargar los datos consiste en la utilización de varios módulos de Python para cargar los archivos alojados en Google Drive. Para ello será necesario una cuenta de Google con la cual se realizará la autentificación y realizar la subida de los archivos previamente. Una vez subidos los archivos se obtendrán sus números de identificación públicos que se utilizarán junto a los módulos “PyDrive”, “Google Colab” y “OAuth2Client” para descargar los archivos de la web y guardarlos en una variable de tipo dataframe de la librería Pandas. La principal ventaja de este método consiste en la gran portabilidad del código, ya que no va a depender de la existencia de los archivos en la máquina local. En cambio, el proceso es mucho más lento al depender de la conexión a la red. El código relacionado con la carga de datos desde Google Drive es el siguiente: Código 2.2 – Carga mediante Google Drive !pip install -U -q PyDrive2 from pydrive2.auth import GoogleAuth from pydrive2.drive import GoogleDrive from google.colab import auth from oauth2client.client import GoogleCredentials # Autenticacion y creacion de variables auth.authenticate_user() gauth = GoogleAuth() gauth.credentials = GoogleCredentials.get_application_default() drive = GoogleDrive(gauth) # Links de los archivos compartidos en Drive ids = [["1aHWZA-O3r3JRV6N95ix_5r_GRh- BpTwr","train_identity"],["1ZBg64Zrh_X4tLPdL51wfVeza1jdqNgLs","train_transact ion"],["1_qW7biLaKpZppjq4czcHLZILvERV8OyX","test_identity"],["1OHHiJ9HH5kyw76 A5-46tcXXhdIBdbhvc","test_transaction"]] for id, name in ids: print ("Cargando archivo con id: " + str(id) + " y nombre: " + str(name)) downloaded = drive.CreateFile({'id':id}) downloaded.GetContentFile('Filename.csv') globals()[name] = pd.read_csv('Filename.csv') # Limpieza de variables del gauth, drive, ids, downloaded 2.3.2 Usando archivos locales La segunda opción disponible para realizar la carga de los datos es la lectura de los archivos con extensión CSV que se encuentren alojados directamente de forma local en el ordenador. Este proceso es mucho más sencillo de realizar que la carga mediante Google Drive ya que la propia librería Pandas contiene funciones dedicadas a la carga de archivos CSV como por ejemplo la función read_csv() . Además, estas funciones permiten la detección automática de
Preparación y Carga de Datos 12 los nombres de las columnas y ordenan los datos introduciéndolos en una variable de tipo dataframe . Como este método utiliza archivos locales es necesario previamente obtener esos archivos, aunque, una vez obtenidos, se realiza la lectura de estos de forma bastante rápida. El código relacionado con la carga de datos desde archivos locales es el siguiente: Código 2.3 - Carga mediante archivos locales print("Cargando datos...") train_transaction = pd.read_csv("train_transaction.csv") train_identity = pd.read_csv("train_identity.csv") test_transaction = pd.read_csv("test_transaction.csv") test_identity = pd.read_csv("test_identity.csv") print("Datos cargados correctamente") Una vez importados los datos que se van a utilizar en sus respectivos dataframes se pueden utilizar los métodos asociados a este objeto. Uno de ellos es el método head() que permite mostrar por pantalla las primeras filas de la variable asociada. Por tanto, podemos observar la forma que van a tener las distintas primeras filas de los datos de entrenamiento: Figura 2-7. Primeras filas del dataset train_transaction Figura 2-8. Primeras filas del dataset train_identity
19 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 3.2 Análisis gráfico de variables Para realizar un mejor análisis de las variables anteriores se va a proceder a la creación de distintos histogramas y otro tipo de gráficos usando las funciones de las librerías Matplotlib , Seaborn y Pandas . Por ejemplo, se puede observar la relación entre la cantidad monetaria usada en la transacción y el fraude utilizando el siguiente código: Código 3.2 – Relación entre “TransactionAmt” y “isFraud” plt.style.use('seaborn') plt.figure(figsize=(20,5)) plt.subplot(121) plt.hist(train['TransactionAmt'].where(train['isFraud'] == 0), 500, rwidth=0.85) plt.axis([min(train['TransactionAmt']),3000,0,50000]) plt.title('Histograma de TransactionAmt | NO FRAUDE') plt.xlabel("Cantidad") plt.ylabel("Nº Veces") plt.subplot(122) plt.hist(train['TransactionAmt'].where(train['isFraud'] == 1), 100, rwidth=0.85) plt.axis([min(train['TransactionAmt']),3000,0,7000]) plt.title('Histograma de TransactionAmt | FRAUDE') plt.xlabel("Cantidad") plt.ylabel("Nº Veces") plt.show() Figura 3-2. Relación entre cantidad monetaria y fraude
Análisis de los Datos 20 Como se puede observar en el gráfico anterior, la mayoría de las transacciones fraudulentas se van a producir en aquellas que conllevan una cantidad monetaria inferior a 400 dólares, al igual que la mayoría de las transacciones no fraudulentas son por debajo de esa cantidad. Sin embargo, parece que existe una distribución ligeramente más concentrada en las cantidades por debajo de 200 dólares en las transacciones no fraudulentas (83.17%) que en las fraudulentas (77.90%). Para acelerar el proceso de creación de los gráficos se va a crear una función llamada plot_prop() que se va a encargar de pintar las variables que se le indiquen a través de los argumentos utilizando para ello funciones contenidas en Matplotlib y Pandas. Además, esta función también se encargará de calcular el porcentaje de fraude cometido para cada valor posible que tomen las variables indicadas. El código relacionado con la creación de esta función es el siguiente: Código 3.3 – Función para representar gráficamente las variables def plot_prop (variable, head_value = None, title = None, xlabel = 'Cuenta'): if title is None: title = variable plt.title(title) plt.xlabel(xlabel) if head_value is None: train[variable].where(train['isFraud'] == 0)\ .value_counts().plot.barh() train[variable].where(train['isFraud'] == 1)\ .value_counts().plot.barh(color = 'red') p1 = train[variable].where(train['isFraud'] == 0).value_counts() p2 = train[variable].where(train['isFraud'] == 1).value_counts() else: train[variable].where(train['isFraud'] == 0)| .value_counts().head(head_value).plot.barh() train[variable].where(train['isFraud'] == 1)\ .value_counts().head(head_value).plot\ .barh(color = 'red') p1 = train[variable].where(train['isFraud'] == 0)\ .value_counts().head(head_value) p2 = train[variable].where(train['isFraud'] == 1)\ .value_counts().head(head_value) plt.legend(['NO FRAUDE','FRAUDE']) prop = p2*100/(p1+p2) print("---Porcentaje de fraude---") print(prop) print("--------------------------") Una vez escrita, la función se puede utilizar para el análisis de las siguientes variables de una forma más rápida.
21 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 3.2.1 Proveedor, Tipo de tarjeta y Tipo de dispositivo (isFraud, card4, card6 y DeviceType) El gráfico se crea de la siguiente forma. “Cuenta” hace referencia al número de transacciones. Código 3.4 – Representación variables “isFraud”, “card4”, “card6” y “DeviceType” plt.figure(figsize=(20,10)) plt.subplot(221) plt.title('isFraud') plt.xlabel("Cuenta") train['isFraud'].value_counts().plot.barh() plt.subplot(222) plot_prop('card4') plt.subplot(223) plot_prop('card6') plt.subplot(224) plot_prop('DeviceType') plt.show() ---Porcentaje de fraude--- american express 2.869836695485111 discover 7.728161178770110 mastercard 3.433095334985757 visa 3.475609914571676 Name: card4, dtype: float64 ---Porcentaje de fraude--- charge card NaN credit 6.678479857167788 debit 2.426250971727834 debit or credit NaN Name: card6, dtype: float64 ---Porcentaje de fraude--- desktop 6.521458345564493 mobile 10.166232365890915 Name: DeviceType, dtype: float64 Figura 3-3. Distribución Fraude/No Fraude en card4 (Proveedor), card6 (Tipo de tarjeta) y DeviceType
Análisis de los Datos 22 Como se puede observar en el gráfico anterior en las variables relativas al proveedor de las tarjetas se puede apreciar que la mayoría de ellas pertenecen a visa o mastercard y que las transacciones fraudulentas y no fraudulentas se reparten aproximadamente de la misma forma entre los dos proveedores (3.4%). Sin embargo, las tarjetas del proveedor discover tienen un porcentaje mayor de fraude (7.7 %). Además, observando el tipo de tarjeta, las tarjetas de crédito tienen un porcentaje de fraude (6.68 %) mucho mayor que el de las tarjetas de débito (2.43 %). De la misma forma, observando el tipo de dispositivo se puede concluir que la mayoría de los dispositivos son de tipo mobile y que tienen un porcentaje de fraude (10.2%) mayor que el de los dispositivos de tipo desktop (6.5%). 3.2.2 Proveedor de correo electrónico del emisor (P_emaildomain) El gráfico se ha generado de la siguiente forma: Código 3.5 – Representación variable “P_emaildomain” plt.figure(figsize=(10,7)) plot_prop('P_emaildomain', 5 ,'Servidor de correo del emisor(P_emaildomain)') plt.show() ---Porcentaje de fraude--- anonymous.com 2.321747121466025 aol.com 2.181059775884620 gmail.com 4.354185369271529 hotmail.com 5.295027624309392 yahoo.com 2.275744545940912 Name: P_emaildomain, dtype: float64 -------------------------- Figura 3-4. Distribución Fraude/No Fraude en P_emaildomain
23 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Observando el gráfico anterior se puede concluir que la gran mayoría de los proveedores de correo electrónico usados en las transacciones son Hotmail.com o Gmail.com . Debido a esto, también se encuentran más extendidos entre los que generan las transacciones fraudulentas y estos dos proveedores son los que tienen un mayor porcentaje de transacciones fraudulentas (5.29% y 4.35%, respectivamente). Otra de las posibilidades por las que los que generan transacciones fraudulentas utilizan los proveedores Hotmail.com y Gmail.com puede ser debido a que están más extendidos entre la población general. 3.2.3 Sistema Operativo (Id_30) El gráfico se genera utilizando las siguientes funciones: Código 3.6 - Representación variable “id_30” plt.figure(figsize=(10,7)) plot_prop('id_30', 5 ,'Sistema Operativo usado (id_30)') plt.show() ---Porcentaje de fraude--- Android 7.0 5.781957506095437 Windows 10 3.105648782793666 Windows 7 3.295194508009153 iOS 11.1.2 NaN iOS 11.2.1 4.916711445459431 iOS 11.2.5 NaN Name: id_30, dtype: float64 -------------------------- Figura 3-5. Distribución Fraude/No Fraude en id_30
Análisis de los Datos 24 Si se observa el gráfico anterior se puede concluir que la mayoría de los sistemas operativos usados para las transacciones bancarias son Windows . El resto de los sistemas operativos que se utilizan en estas transacciones están diseñados para teléfonos móviles, como por ejemplo iOS 11 o Android . Estos sistemas operativos relacionados con dispositivos móviles van a tener un porcentaje de fraude mayor que el SO Windows (5% frente a 3.2%). 3.2.4 Navegador Web (Id_31) Este gráfico se ha generado utilizando el siguiente código: Código 3.7 - Representación variable “id_31” plt.figure(figsize=(10,7)) plot_prop('id_31', 5 ,'Navegador Web usado (id_31)') plt.show() ---Porcentaje de fraude--- chrome 63.0 6.831818181818182 chrome 65.0 NaN chrome generic NaN ie 11.0 for desktop NaN mobile safari 11.0 6.272815316993221 mobile safari generic 9.987798500958689 safari generic NaN Name: id_31, dtype: float64 -------------------------- Figura 3-6. Distribución Fraude/No Fraude en id_31
25 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Esta variable se encuentra relacionada con el navegador web usado en las transacciones. Observando el gráfico anterior se puede deducir que la mayoría de los navegadores web que han sido utilizados en las transacciones pertenecen a las familias de Chrome o Safari , teniendo esta última un porcentaje de fraude ligeramente mayor que la anterior (6.83% frente a un 10%). Todos los datos anteriores van a ser interpretados por el modelo de clasificación en su etapa de entrenamiento. En este entrenamiento el modelo asignará mayor o menor peso en la predicción a una determinada variable dependiendo del porcentaje de fraude, de su concentración, de su correlación con otras variables, etc. 3.3 Análisis en el tiempo Por último, se va a proceder al análisis de la variable TransactionDT. La variable TransactionDT va a indicar el momento de tiempo en el que se realiza cada transacción. Para realizar un análisis correcto de esta variable se va a mostrar en un histograma los momentos de tiempos que se encuentran en los dataset "train" y "test" utilizando el siguiente fragmento de código: Código 3.8 - Representación variable temporal “TransactionDT” plt.figure(figsize=(10,5)) plt.hist(train['TransactionDT'], label='train') plt.hist(test['TransactionDT'], label='test') plt.legend() plt.title('Histograma de la variable TransactionDT') plt.show() Figura 3-7. Histograma de la variable TransactionDT
Análisis de los Datos 26 En el eje de la abcisas se ha representado el tiempo a partir de un instante de tiempo inicial t=0 que hace referencia a una marca de tiempo sin definir en los datos que se proporcionan. Este tiempo se encuentra representado en segundos y, por tanto, en total se tienen datos de aproximadamente un año repartidos entre los datos “train” y “test”. Si se observa la imagen anterior se puede apreciar que los momentos de tiempo de los dos dataset no se solapan entre sí en el tiempo. Esto significa que se podrá usar la función TimeSeriesSplit() de la librería sklearn para realizar la validación cruzada del modelo. Esta función dividirá los datos en grupos de intervalos fijos de tiempo en vez de hacerlo de forma aleatoria e irá realizando la validación cruzada avanzando en esos grupos, usando los grupos anteriores como "train" y el siguiente como "test" en cada iteración del algoritmo. El progreso del algoritmo en cada iteración se muestra en la siguiente imagen: Figura 3-8. División de datos usando TimeSeriesSplit() En la imagen anterior se puede observar que el algoritmo coge en la primera iteración el primer set como entrenamiento y el segundo como prueba y realiza la validación cruzada. En la siguiente iteración se escoge como prueba el tercer set y los anteriores como entrenamiento. De esta forma se avanza hasta realizar la validación cruzada con todos los sets de datos.
Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 27 4 TÉCNICAS DE REDUCCIÓN DE LA DIMENSIONALIDAD n los datos existe gran cantidad de información que es irrelevante o redundante. Hay muchas columnas en las que la mayoría de sus valores son nulos. Otras, en cambio, tienen un solo valor con mucho mayor porcentaje de aparición que el resto de los valores posibles o incluso un único valor para todas las filas. Por último, existirán columnas que estén altamente correladas entre sí y que no aportan información nueva al modelo más allá de la información que ha aportado la columna correlada correspondiente. Esto ocurrirá tanto en el dataset "train" como en el dataset "test". Por ello se procederá a buscar las columnas que cumplan las condiciones anteriormente mencionadas en los dos datasets y a eliminarlas de ellos para disminuir la cantidad de información irrelevante, lo que ayudará a mejorar la precisión del modelo y a reducir la carga computacional necesaria al entrenarlo. Con ello se conseguirá además una mejora en el tiempo que necesita el entrenamiento del modelo. En primer lugar, se va a realizar un análisis y presentación de las técnicas de reducción de la dimensionalidad más importantes. Figura 4-1. Técnicas de reducción de la dimensionalidad E
Técnicas de Reducción de la Dimensionalidad 28 4.1 Missing Value Ratio Se trata de la eliminación de las columnas cuyo porcentaje de filas con valores nulos o “NaN” sea mayor de un determinado porcentaje objetivo, ya que estas columnas no aportan ningún tipo de información nueva en el conjunto general de los datos, sino que solo aportan información de las pocas filas que tienen esos valores no nulos. El resto de valores NaN los tratará directamente el modelo. En este caso se ha optado por eliminar las columnas con un porcentaje de valores nulos mayor o igual al 60%. Código 4.1 – Ejemplo de columna con muchos NaN train['D14'].isnull().value_counts(normalize = True) True 0.894695 False 0.105305 Name: D14, dtype: float64 Por ejemplo, si se observa la columna “D14” (Relacionada con el tiempo de la transacción) de los datos de entrenamiento se puede observar que el 89.46% de sus valores son nulos. 4.2 Low Variance Filter Esta técnica consiste en la eliminación de las columnas que tengan una varianza muy baja en los valores de sus filas. Es decir, se van a eliminar aquellas columnas en las que un gran porcentaje de los valores de sus filas sean de un valor único y, por tanto, no aportan información útil al entrenamiento del modelo. Se ha optado por eliminar las columnas que tengan un porcentaje de valores iguales mayor o igual al 75%. Código 4.2 – Ejemplo de columna con muchos valores iguales train['card3'].value_counts(normalize = True) 150.0 0.885075 185.0 0.095668 106.0 0.002667 146.0 0.002126 144.0 0.002126 ... 228.0 0.000002 139.0 0.000002 114.0 0.000002 164.0 0.000002 221.0 0.000002 Name: card3, Length: 114, dtype: float64 En el código anterior se puede observar cómo el 88.50% de los valores de la columna “card3” (Relacionada con el banco emisor de la tarjeta) tienen un valor de 150.0.
35 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 5 PREPARACIÓN DE LOS DATOS PARA EL MODELO n este apartado se terminará de procesar los datos utilizando un “LabelEncoder” para codificar de forma numérica las columnas categóricas. Además, se guardarán los datos ya procesados en una variable de extensión “pickle” (.pkl) para no tener que repetir el procesamiento de los datos cada vez que se quiera arrancar el entrenamiento del modelo. 5.1 Codificación de variables categóricas Como ya se comentó anteriormente, las columnas con variables categóricas (variables que sólo pueden tomar un valor concreto de un grupo limitado y fijo de valores) se tratarán usando un “LabelEncoder”. En otro caso en el que los datos contuvieran un menor número de características se podría utilizar el método “OneHotEncoder”. Este último método crea una nueva columna por cada valor único en la columna original y rellena estas columnas con ceros y unos según el valor determinado que tome cada fila. Así se consigue aumentar la precisión del modelo final, pero se añade una columna por cada valor que pueda tomar cada variable categórica, lo que en este caso aumentaría bastante el tamaño de los datos. Código 5.1 – Definición de variables categóricas cat_cols = ['ProductCD', 'card4', 'card6','P_emaildomain', 'R_emaildomain', 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9','id_12','id_15','id_16','id_23','id_27', 'id_28', 'id_29', 'id_30', 'id_31', 'id_32', 'id_33', 'id_34', 'id_35', 'id_36', 'id_37', 'id_38','DeviceType', 'DeviceInfo'] LabelEncoder() consiste en una herramienta contenida en la librería SKLearn que permite codificar variables categóricas transformando el valor categórico contenido en una palabra en números de forma que a cada posible valor le asigna un número entero entre 0 y n_valores-1. Esta codificación añade el problema de la secuenciación numérica. El codificador va a asignar a cada posible valor categórico un número según el orden de aparición de cada valor. Es decir, primero va a asignar como valor el cero, después el uno, etc. A la hora de entrenar el modelo, éste puede encontrar un patrón entre el valor numérico asignado y el resto de los datos. De esta forma, el modelo puede asignar diferentes pesos según este número. Pero realmente no existe ningún tipo de relación entre el número y el valor categórico. E
Preparación de los Datos para el Modelo 36 El método OneHotEncoder() eliminaría este problema de secuenciación numérica pero al añadir tantas columnas es preferible usar LabelEncoder(). Figura 5-1. Ejemplo del uso de un LabelEncoder() En el siguiente fragmento de código se puede observar la implementación y utilización del LabelEncoder(): Código 5.2 – Utilización del LabelEncoder labelencoder = LabelEncoder() count = 0 for col in cat_cols: if col in train.columns: count += 1 # Nos aseguramos de que utiliza todas las clases existentes en ambos datasets en el entrenamiento # Para ello se entrena el labelencoder con los dos datasets labelencoder.fit(list(train[col].astype(str).values) +\ list(test[col].astype(str).values)) # Convertimos las columnas train[col] = labelencoder\ .transform(list(train[col].astype(str).values)) test[col] = labelencoder.transform(list(test[col].astype(str).values)) # Limpieza de variables del labelencoder print("Se han codificado " + str(count) + " columnas.") Se han codificado 26 columnas. 5.2 División de los datos Finalmente, se deben reconfigurar y dividir los datos que se van a usar en el entrenamiento del modelo. Para ello se crearán los siguientes nuevos datasets:
37 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito El dataset X_train se compondrá de los datos de entrada del modelo a utilizar en el entrenamiento y se conseguirá ordenando en el tiempo (según la variable TransactionDT) las filas del dataset “train” que sirven como datos de entrada. Además, es necesario eliminar del dataset las columnas que no se van a usar como datos de entrenamiento, las cuales son las columnas “isFraud”, “TransactionDT” y “TransactionID”. El dataset X_train_ID será la salida objetivo del modelo para cada fila X correspondiente. En este caso se compondrá con la columna binaria “isFraud”. El dataset X_test se va a formar con los datos de entrada del modelo a utilizar en la etapa de predicción final y se conseguirá ordenando en el tiempo (según la variable TransactionDT) las columnas del dataset “test”. Al igual que con el dataset X, es necesario eliminar las columnas “TransactionDT” y “TransactionID”. El dataset X_test_ID se va a usar simplemente a efectos prácticos para su uso en representaciones gráficas y en el archivo con extensión .csv en el que se guardarán las predicciones una vez realizadas. Figura 5-2. División de datasets Por tanto, los datasets “train” y “test” se dividirán usando los criterios anteriores: Código 5.3 – División de los datos X_train = train.sort_values('TransactionDT')\ .drop(['isFraud','TransactionDT','TransactionID'], axis=1) X_train_ID = train.sort_values('TransactionDT')['isFraud'] X_test = test.sort_values('TransactionDT')\ .drop(['TransactionDT','TransactionID'], axis=1) X_test_ID = test.sort_values('TransactionDT')['TransactionID'] # Limpieza de variables del train, test # Limpieza de variables utilizando el limpiador de Python (Modulo Garbage Collector) obj = gc.collect()
Preparación de los Datos para el Modelo 38 print("Se han limpiado " + str(obj) + " objetos.") Se han limpiado 396 objetos. 5.3 Almacenamiento de los datos Con el procesamiento que se ha realizado desde la carga de los datos hasta este punto ya se tienen listos los datos para su utilización en el entrenamiento del modelo. Sin embargo, puede ser bastante útil la exportación de estos datos a un archivo local. De esta forma, los datasets procesados van a quedar guardados externamente y podrán ser importados siempre que se necesiten. Esto simplificará y aumentará la rapidez de ejecuciones posteriores del proceso de entrenamiento o uso del modelo. El manejo de estos datos se realizará mediante un tipo de archivo contenido en la librería Pandas. Estos archivos tienen un formato denominado “pickle” y una extensión “.pkl”. Este formato implementa protocolos binarios para serializar o deserializar estructuras de objetos de Python, de manera que los objetos son convertidos en un stream de bytes o viceversa. La exportación de los datos se realizará mediante un método que contienen los objetos de tipo dataset denominado .to_pickle() con el siguiente código: Código 5.4 – Guardado de datos en archivo “Pickle” X_train.to_pickle("X_train.pkl") X_train_ID.to_pickle("X_train_ID.pkl") X_test.to_pickle("X_test.pkl") X_test_ID.to_pickle("X_test_ID.pkl") Cada vez que se quiera realizar el proceso contrario, es necesario utilizar la función read_pickle() incluida en la librería Pandas junto con la dirección del archivo que se va a importar. En este caso, se tendría que utilizar de la siguiente forma: Código 5.5 – Carga de datos desde un archivo “Pickle” X_train = pd.read_pickle("X_train.pkl") X_train_ID = pd.read_pickle("X_train_ID.pkl") X_test = pd.read_pickle("X_test.pkl") X_test_ID = pd.read_pickle("X_test_ID.pkl") Otra opción sería la exportación a un archivo CSV que, aunque ocupa una mayor cantidad de memoria, tienen una compatibilidad más extendida.
39 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 6 CREACIÓN Y ENTRENAMIENTO DEL MODELO n este apartado se procederá a la creación del modelo y a su entrenamiento para, más tarde, utilizarlo para predecir los resultados del dataset “test”. Para ello, se van a utilizar diversas técnicas y algoritmos para el entrenamiento del modelo, así como para la medición de distintos parámetros del modelo. Primeramente, se va a crear una función que monitorice la efectividad del modelo durante su entrenamiento. Posteriormente, se realizará una comparación entre los distintos tipos de modelos de clasificación que existen en varias librerías diferentes. Además, una vez elegido el modelo que se va a utilizar es necesario ajustar sus parámetros. Finalmente, se realizará el entrenamiento del modelo y se observarán sus resultados. Figura 6-1. Entrenamiento y uso del modelo 6.1 Función de evaluación del modelo Para la evaluación del modelo se va a definir la función que se utilizará para realizar el cálculo del “Área Bajo la Curva Característica Operativa del Receptor” (AUC-ROC). Este factor será utilizado por el modelo durante el entrenamiento para la evaluación de su efectividad (en lugar de su precisión) ya que se tiene un modelo de tipo binario en el que se calculan probabilidades de que la salida sea 0 o 1 (Fraude o No Fraude). La curva ROC es una representación de la sensibilidad frente a la especificidad en un sistema E
Creación y Entrenamiento del Modelo 40 clasificador binario según varía el umbral de discriminación. Dicho de otro modo, es la relación entre el TPR (Ratio de positivos verdaderos) y el FPR (Ratio de falsos positivos) según varía el umbral de discriminación. El umbral se utiliza en este caso ya que el modelo va a predecir las probabilidades de que una cierta entrada produzca cada salida (Fraude o No Fraude) y se debe fijar un umbral de probabilidad a partir del cual se considera que el modelo ha asignado esa salida a una de las dos. Según se va variando ese umbral se van tomando los puntos que se consiguen y de esa forma se crea una curva, la curva ROC. Figura 6-2. Curva ROC Para obtener los puntos que forman la curva ROC se deben tener en cuenta las dos variables que la forman, el TPR y el FPR: El ratio de verdaderos positivos (TPR) o sensibilidad se conseguirá con la siguiente fórmula: TPR = TP / (TP + FN) El ratio de falsos positivos (FPR) o 1 - especificidad se conseguirá con la siguiente fórmula: FPR = FP / (FP + TN) Con ello se conseguirán diversos puntos. Por ejemplo, algunos de ellos pueden ser los siguientes: Figura 6-3. Diversos puntos en la ROC
41 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Como se puede observar en la representación anterior, la zona superior izquierda representa un TPR cercano a 1, mientras que el FPR sería cercano a 0, por lo que la clasificación será casi perfecta. La zona central corresponde a una equivalencia entre TPR y FPR, lo que indicaría que el modelo clasificaría los puntos de forma aleatoria. En el diagrama se puede observar que el punto A obtiene aparentemente mejores resultados que el punto B que se encuentra en la zona por debajo de la línea de clasificación aleatoria. En esta zona hay una mayor cantidad de clasificaciones incorrectas que correctas. Pero ésto no tiene por qué ser malo, ya que al estar ante un clasificador binario bastaría con invertir los resultados. De esta forma, la mayoría de las veces que el clasificador asigna una clase el resultado real sería el contrario como se puede apreciar en el siguiente diagrama: Figura 6-4. Diversos puntos en la ROC invertidos Al invertir los resultados del punto B se obtiene el punto B’, el cual se puede observar que mantiene una simetría inversa respecto a la línea central y, finalmente, estaría más cerca de la clasificación perfecta ideal que el punto A. Por tanto, el área bajo la curva ROC (AUC) se creará con los puntos anteriores y tomará un valor entre 0 y 1. Mientras más cercano sea el valor a 1, mejor va a predecir el modelo: Figura 6-5. Valor AUC según la curva ROC
Creación y Entrenamiento del Modelo 42 Esta nueva función para el cálculo de la AUC-ROC se ha creado debido a que la función que la librería SKLearn incluye por defecto realiza los cálculos de forma mucho más lenta y, para un dataset del tamaño que se está manejando en este caso, el entrenamiento tomaría una gran cantidad de tiempo. Con la nueva función se consigue reducir este tiempo significativamente. La función que calcula la AUC-ROC es la siguiente: Código 6.1 – Funciones para el cálculo de la AUC-ROC def fast_auc(y_true, y_prob): # Basado en: https://www.kaggle.com/c/microsoft-malware- prediction/discussion/76013 y_true = np.asarray(y_true) y_true = y_true[np.argsort(y_prob)] nfalse = 0 auc = 0 n = len(y_true) for i in range(n): y_i = y_true[i] nfalse += (1 - y_i) auc += y_i * nfalse auc /= (nfalse * (n - nfalse)) return auc def eval_auc(y_true, y_pred): return 'auc', fast_auc(y_true, y_pred), True Finalmente, se crea otra función que se encargue de representar gráficamente la curva AUCROC utilizando para ello la función roc_curve() de SKLearn para obtener el ratio de verdaderos y falsos positivos: Código 6.2 – Función para la representación gráfica de la curva ROC def plot_roc_curve(scores, y, oof): # Plot curva ROC plt.figure(figsize=(6,6)) fpr, tpr, thres = roc_curve(y, oof) plt.plot(fpr, tpr, color='b') plt.xlim([-0.05, 1.05]) plt.ylim([-0.05, 1.05]) plt.xlabel('FPR') plt.ylabel('TPR') plt.title('Curva ROC') Además de la ROC, el modelo usará internamente otras medidas como la precisión y la “F1- Score” (Que se calcula usando la precisión y la sensibilidad), que se encuentran en sklearn.metrics .
43 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 6.2 Modelos de clasificación En el contexto del machine learning existen una gran cantidad de técnicas y algoritmos de clasificación que se han ido perfeccionando durante años. Por ello, hay una gran diversidad y disponibilidad a la hora de escoger un modelo para utilizar para la clasificación del fraude en transacciones monetarias. Los métodos que se pueden usar para el proyecto utilizan todos ellos un algoritmo de "Gradient Boosting". El Gradiente representa la pendiente de la tangente de la función de pérdidas (lo que quiere decir que a menor gradiente mejores resultados va a tener el modelo). Además, durante cada etapa del entrenamiento, se pasará a la siguiente etapa del algoritmo de entrenamiento la información de los datos con mayor gradiente y con ello se conseguirá que el algoritmo invierta más tiempo en entrenar las zonas con mayor gradiente para ajustarlas mejor. A este proceso se le denomina Boosting. En este caso se tienen una gran cantidad de datos que procesar y el tiempo de procesamiento se puede alargar en gran medida. Debido a ello se ha optado por utilizar el algoritmo de LightGBM ya que dispone de un equilibrio entre rapidez de entrenamiento y de ajuste de hiperparámetros y una precisión de predicción buena. El modelo LightGBM (Que utiliza la función LGBMClassifier() ) utiliza una técnica de gradiente de muestreo unilateral (GOSS) en la que se toman instancias de los datos con mayores gradientes en cada iteración y un porcentaje de muestreo aleatorio del resto de datos. Con esto se consigue que el entrenamiento y el ajuste de hiperparámetros sea rápido. Otra ventaja de este método es que acepta variables categóricas con un algoritmo rápido y tiene una precisión buena. 6.3 Creación del modelo Teniendo las funciones anteriores creadas se puede proceder a crear otra función que lo agrupará todo y se encargará del entrenamiento del modelo según los parametros que se le proporcionen. El objetivo de esta función será el entrenamiento del modelo utilizando X_train para luego realizar la predicción de la salida en el dataset de prueba X_test. Este resultado se exportará a un archivo CSV y se subirá a Kaggle. La función va a tener varios argumentos que se definen de la siguiente forma: X_train: Dataset que va a ser usado en el entrenamiento. X_test: Dataset que va a ser usado en la etapa de prueba. X_train_ID: Columna de datos que va a ser utilizada como salida del modelo en la etapa de entrenamiento. Es decir, mientras el modelo se esté entrenando éste será el objetivo que se debe conseguir a la salida. Params: Estructura de parámetros que se van a pasar al modelo para ajustarlo
Creación y Entrenamiento del Modelo 44 correctamente y mejorar sus resultados. Estos parámetros se explicarán con más detalle y se realizará una función para su elección en el Apartado 6.4. Folds: Modelo de división de datos que se va a utilizar en el algoritmo. Como se comentó en el Apartado 3.3, se va a utilizar la función TimeSeriesSplit() para realizar una división de datos en el tiempo con 5 divisiones (folds). Este modelo se crea de la siguiente forma: Código 6.3 – División de datos en el tiempo folds = TimeSeriesSplit(n_splits=5) Plot_feature_importance: Flag que le indica al modelo si se quiere representar la importancia que el modelo le da a cada característica en un gráfico al utilizar la función. Model: Argumento por el que se puede utilizar otra función de modelo para el entrenamiento. Si no se indica nada, se utiliza el modelo LGBMClassifier() . Verbose: Este argumento le indica al modelo cada cuántas iteraciones debe mostrar las puntuaciones medias que se han calculado en ese intervalo. Early_stopping_rounds: Este argumento hace referencia al número de iteraciones que deben ocurrir para que se active la técnica de parada anticipada del entrenamiento. Esta técnica va a detener el entrenamiento del modelo cuando no se mejoren los resultados obtenidos durante el número de rondas indicado, lo que ayuda a prevenir el sobreentrenamiento. N_estimators: Número de estimadores que se van a utilizar en el modelo, es decir, número de árboles que se van a entrenar. N_folds: Número de divisiones que se han utilizado en el modelo de división de datos. Debe ser igual que el argumento de la función del parámetro Folds. N_jobs: Número de núcleos del procesador que se van a utilizar en el entrenamiento del modelo. Si es igual a -1 , utiliza todos los núcleos posibles. El código relacionado con la creación de esta función es el siguiente: Código 6.4 – Función para el entrenamiento del modelo def train_model_classification(X_train, X_test, X_train_ID, params, folds, plot_feature_importance=False, model=None, verbose=500, early_stopping_rounds=200, n_estimators=5000, n_folds=5, n_jobs=-1): # Inicializacion de variables: columns = X_train.columns n_splits = n_folds # Número de divisiones X_test = X_test[columns]
51 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito (...) [CV 6/10; 27/27] END num_leaves=128, reg_alpha=0.4, reg_lambda=0.4; total time= 1.8min [CV 7/10; 27/27] END num_leaves=128, reg_alpha=0.4, reg_lambda=0.4; total time= 1.9min [CV 8/10; 27/27] END num_leaves=128, reg_alpha=0.4, reg_lambda=0.4; total time= 2.0min [CV 10/10; 27/27] END num_leaves=128, reg_alpha=0.4, reg_lambda=0.4; total time= 1.8min [CV 9/10; 27/27] END num_leaves=128, reg_alpha=0.4, reg_lambda=0.4; total time= 1.8min Mejores parametros encontrados: {'num_leaves': 128, 'reg_alpha': 0.4, 'reg_lambda': 0.2} Puntuaciones: 0.927 (+/-0.002) for {'num_leaves': 32, 'reg_alpha': 0.2, 'reg_lambda': 0.2} 0.926 (+/-0.003) for {'num_leaves': 32, 'reg_alpha': 0.2, 'reg_lambda': 0.3} 0.926 (+/-0.002) for {'num_leaves': 32, 'reg_alpha': 0.2, 'reg_lambda': 0.4} 0.927 (+/-0.002) for {'num_leaves': 32, 'reg_alpha': 0.3, 'reg_lambda': 0.2} 0.927 (+/-0.003) for {'num_leaves': 32, 'reg_alpha': 0.3, 'reg_lambda': 0.3} 0.926 (+/-0.003) for {'num_leaves': 32, 'reg_alpha': 0.3, 'reg_lambda': 0.4} 0.927 (+/-0.002) for {'num_leaves': 32, 'reg_alpha': 0.4, 'reg_lambda': 0.2} 0.927 (+/-0.002) for {'num_leaves': 32, 'reg_alpha': 0.4, 'reg_lambda': 0.3} 0.927 (+/-0.003) for {'num_leaves': 32, 'reg_alpha': 0.4, 'reg_lambda': 0.4} 0.941 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.2, 'reg_lambda': 0.2} 0.940 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.2, 'reg_lambda': 0.3} 0.940 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.2, 'reg_lambda': 0.4} 0.941 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.3, 'reg_lambda': 0.2} 0.940 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.3, 'reg_lambda': 0.3} 0.940 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.3, 'reg_lambda': 0.4} 0.941 (+/-0.004) for {'num_leaves': 64, 'reg_alpha': 0.4, 'reg_lambda': 0.2} 0.941 (+/-0.003) for {'num_leaves': 64, 'reg_alpha': 0.4, 'reg_lambda': 0.3} 0.940 (+/-0.004) for {'num_leaves': 64, 'reg_alpha': 0.4, 'reg_lambda': 0.4} 0.951 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.2, 'reg_lambda': 0.2} 0.950 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.2, 'reg_lambda': 0.3} 0.950 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.2, 'reg_lambda': 0.4} 0.951 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.3, 'reg_lambda': 0.2} 0.951 (+/-0.004) for {'num_leaves': 128, 'reg_alpha': 0.3, 'reg_lambda': 0.3} 0.950 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.3, 'reg_lambda': 0.4} 0.951 (+/-0.004) for {'num_leaves': 128, 'reg_alpha': 0.4, 'reg_lambda': 0.2} 0.951 (+/-0.004) for {'num_leaves': 128, 'reg_alpha': 0.4, 'reg_lambda': 0.3} 0.950 (+/-0.003) for {'num_leaves': 128, 'reg_alpha': 0.4, 'reg_lambda': 0.4}
Creación y Entrenamiento del Modelo 52 precision recall f1-score support 0 0.98 1.00 0.99 114012 1 0.92 0.51 0.66 4096 accuracy 0.98 118108 macro avg 0.95 0.75 0.82 118108 weighted avg 0.98 0.98 0.98 118108 Como se puede observar en la salida por terminal del código anterior el valor óptimo de los parámetros es: num_leaves = 128, reg_alpha = 0.4 y reg_lambda = 0.2. Una vez determinados los valores óptimos de los parámetros anteriores se agrupan todos en el diccionario de parámetros: Código 6.6 – Conjunto de parámetros finales params = {'min_child_samples': 79, 'objective': 'binary', 'max_depth': 13, 'learning_rate': 0.03, "boosting_type": "gbdt", "subsample": 0.9, "subsample_freq": 3, "bagging_seed": 11, 'colsample_bytree': 0.9, "metric": 'auc', "verbosity": -1, 'num_leaves': 128, 'reg_alpha': 0.4, 'reg_lambda': 0.2, } 6.5 Entrenamiento del modelo y resultados Una vez que se ha creado la función encargada del entrenamiento del modelo, se tienen los datos correctamente procesados y se han determinado los hiperparámetros óptimos se puede usar la función de entrenamiento del modelo proporcionándole todos los datos necesarios como argumentos y se observan los resultados: Código 6.7 – Entrenamiento del modelo result_lgb = train_model_classification(X_train=X_train, X_test=X_test,\ X_train_ID=X_train_ID, params=params, folds=folds,\ plot_feature_importance=True, verbose=500,\ early_stopping_rounds=200, n_estimators=5000,\ n_folds=5, n_jobs=-1) Fold 1 comenzada en Thu Jun 17 17:59:53 2021 Training until validation scores don't improve for 200 rounds Early stopping, best iteration is: [195] training's auc: 0.994318 valid_1's auc: 0.886762
53 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito --------------------------------------------- Fold 2 comenzada en Thu Jun 17 18:06:17 2021 Training until validation scores don't improve for 200 rounds [500] training's auc: 0.996968 valid_1's auc: 0.903799 Early stopping, best iteration is: [437] training's auc: 0.995574 valid_1's auc: 0.904611 --------------------------------------------- Fold 3 comenzada en Thu Jun 17 18:22:58 2021 Training until validation scores don't improve for 200 rounds [500] training's auc: 0.991698 valid_1's auc: 0.8945 Early stopping, best iteration is: [363] training's auc: 0.98602 valid_1's auc: 0.895842 --------------------------------------------- Fold 4 comenzada en Thu Jun 17 18:41:06 2021 Training until validation scores don't improve for 200 rounds [500] training's auc: 0.986012 valid_1's auc: 0.91735 [1000] training's auc: 0.99612 valid_1's auc: 0.918434 Early stopping, best iteration is: [904] training's auc: 0.995131 valid_1's auc: 0.918952 --------------------------------------------- Fold 5 comenzada en Thu Jun 17 19:26:33 2021 Training until validation scores don't improve for 200 rounds [500] training's auc: 0.98192 valid_1's auc: 0.917863 Early stopping, best iteration is: [684] training's auc: 0.988344 valid_1's auc: 0.918342 --------------------------------------------- Puntuacion Cross-Validation | mean: 0.9049, std: 0.0126. Los resultados del algoritmo anterior se han guardado en la variable de tipo diccionario result_lgb. Este diccionario cuenta con las siguientes claves en su interior: Oof: En esta clave del diccionario se han ido almacenando las distintas predicciones que el modelo ha ido realizando en la etapa de validación en cada fold de la división. Prediction: Esta clave del diccionario almacena las predicciones que se han realizado utilizando como entrada el dataset de prueba (X_test). Es decir, estas predicciones son las que luego se utilizarán para extraerlas a un archivo CSV y subirlo a la página web Kaggle. Scores: Su función es el almacenamiento de las puntuaciones que se han ido consiguiendo en cada fold del entrenamiento del modelo. En este caso, almacenará el resultado de la puntuación de la ROC_AUC. Feature_importance: En esta clave se guarda el valor de la importancia que el modelo ha asignado a cada una de las características durante el entrenamiento. El cálculo de este valor se va a tratar en el siguiente apartado. Top_columns: Esta clave almacena los índices de las columnas de las 25 características con mayor valor de importancia del modelo. De esta forma, puede usarse en una etapa posterior para disponer directamente de estas características o para generar gráficas. Es necesario recordar que, al estar los datos divididos en 5 folds distintos, los valores de las claves del diccionario “Oof”, “Prediction”, “Scores” y “Feature_importance” deben dividirse por el número de folds para obtener su valor real.
Creación y Entrenamiento del Modelo 54 El algoritmo ha ido entrenando los distintos folds y aprendiendo de forma que se ha conseguido ir mejorando la puntuación conseguida en validación en cada uno de ellos. Esto también es debido a que en cada fold sucesivo se tienen en cuenta una mayor cantidad de datos para realizar el entrenamiento, lo que ha supuesto también un mayor tiempo de procesamiento como se puede observar en las marcas de tiempo de la salida por terminal anterior. Se ha representado en un gráfico la puntuación conseguida en validación en cada fold y, para el cálculo de la puntuación final, se ha utilizado la media de estas puntuaciones: Figura 6-8. Puntuaciones obtenidas en cada Fold Como se puede observar en la gráfica anterior, la puntuación de precisión en validación media entre todos los Folds que se ha conseguido es de un 90.49%, el cual es un valor bastante bueno en este caso que se tiene gran cantidad de características. También resulta interesante la visualización de las puntuaciones conseguidas en la evaluación del dataset de entrenamiento para ver si el modelo ha terminado con un ajuste deficiente (“underfitting”) o sobreajustado (“overfitting”): Figura 6-9. Puntuaciones obtenidas en cada Fold en Entrenamiento
55 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito Observando el gráfico se observa que el modelo en general está altamente ajustado a los datos de entrenamiento, con lo que se ha conseguido una puntuación media de los Folds en entrenamiento de un 99.19%. Por tanto, se tiene finalmente un modelo entrenado que está ligeramente sobreajustado ya que, aunque esté bastante ajustado a los datos de entrenamiento, generaliza de forma correcta y consigue una buena puntuación en la etapa de validación. 6.5.1 Importancia de las características Resulta interesante analizar cuáles son las características a las que el modelo ha dado más importancia en el entrenamiento. Esta importancia se ha calculado utilizando los flujos de los árboles de decisión (Se verán posteriormente en el Apartado 6.5.3). Cada hoja del árbol va a tener un valor de predicción asociado y unos nodos padres anteriores en el flujo que también tienen un valor de predicción esperado. Por tanto, según las características que influyen en ese flujo y el cambio entre el valor de predicción esperado y el real al pasar de un nodo padre a hijo, se puede calcular la contribución de cada característica al entrenamiento del modelo. La función utilizada en el cálculo es 𝑰𝒎𝒑𝒐𝒓𝒕𝒂𝒏𝒄𝒊𝒂 𝒇(𝒙)= 𝒑𝒊𝒏𝒊 +∑𝒄𝒐𝒏𝒕𝒓𝒊𝒃(𝒙, 𝒌) 𝑲 𝒌𝟏 donde “ p_ini ” es la predicción inicial y “ contrib(x,k)” es la diferencia que genera la característica k en el vector de características x. Figura 6-10. Importancia de las características en el modelo entrenado
Creación y Entrenamiento del Modelo 56 Como se puede observar en la gráfica anterior, algunas de las características a las que el clasificador les asigna más peso en la predicción del fraude son aquellas que ya se denotaron como importantes en el Apartado 2, ya que son de las características principales de las transacciones analizadas. Por ejemplo, las cuatro características más importantes para la detección del fraude han sido “ card1 ” y “ card2 ” (Valores numéricos relacionados con las tarjetas), “ TransactionAmt ” (Cantidad monetaria transferida) y “ addr1 ” (Dirección de correo electrónico codificada en un número por privacidad). Sin embargo, algunas de ellas que parecían ser importantes a simple vista (como “ card4 ” - Proveedor y “ card6 ” – Tipo de tarjeta) no han resultado tan importantes en el entrenamiento final del modelo. 6.5.2 Curva ROC Figura 6-11. Curva ROC Esta representación y su curva asociada se han generado como ya se comentó en el Apartado 6.1. En el esquema anterior se observa que la curva ROC tiene un valor de AUC cercano a 1, por lo que el clasificador va a tener una precisión buena.
57 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 6.5.3 Diagrama de árbol Como se comentó anteriormente, se puede utilizar el módulo Graphviz para observar el diagrama de grafos del modelo. Debido a la gran cantidad de características y nodos que se tienen en este modelo se genera un diagrama de gran tamaño. Este diagrama se ha generado utilizando la función Create_tree_digraph() que se encuentra dentro de la función de entrenamiento train_model_clasification(). Solo se muestra a continuación una pequeña parte del diagrama total: [9] Figura 6-12. Porción del diagrama de grafos del modelo Observando el diagrama anterior, se puede ver que el modelo cuenta con dos tipos de nodos: Los nodos intermedios son aquellos que dividen el flujo de la predicción según una condición. En este caso, las condiciones para elegir un camino u otro es el valor de una cierta característica determinada. Estos valores o pesos son determinados por el modelo en la etapa de entrenamiento. El segundo tipo de nodo es el nodo hoja (“leaf”). Este tipo de nodo es un nodo final o sumidero que define el valor de la predicción que van a tener los datos que sigan la ruta hasta él. Como se denotó en el Código 6.4, es necesario dividir este valor por el número de folds utilizados (5 en este caso) para obtener el valor real de la predicción. Debido a la gran cantidad de datos y características que se tienen, se va a generar un diagrama con una gran cantidad de nodos de ambos tipos. 6.5.4 Cálculo de fraudes totales detectados Para contar el número de fraudes que se han detectado en total en los datos de prueba se pueden analizar las probabilidades encontradas, que se encuentran almacenadas en la variable result_lgb[‘prediction’] . Por ejemplo, se pueden tomar como fraudes las predicciones con una probabilidad mayor de 0.5, con lo que se tendría el siguiente código:
Creación y Entrenamiento del Modelo 58 Código 6.8 – Cálculo del número de fraudes totales res = pd.DataFrame(result_lgb['prediction'], columns=['isFraud']) frauds = len(res.where(res >= 0.5).dropna()\ .sort_values(by=['isFraud'], ascending = False)) print("Se han predecido en total " + str(frauds) +\ " fraudes en el dataset \"test\"") Se han predecido en total 6361 fraudes en el dataset "test" Figura 6-13. Valores de las predicciones en X_test (Recortada en el eje Y) Al observar el histograma anterior se puede ver que la mayoría de las predicciones se sitúan en las zonas de predicción menor a 0.3 (No Fraude) y predicción mayor a 0.8 (Fraude). Existe también una zona intermedia en la cuál el valor de la predicción oscila entre 0.3 y 0.8 y que tiene alrededor de 500 predicciones por cada franja de valores. Por defecto se toma el umbral en un valor de 0.5 para determinar el valor que tomará una predicción y en este caso parece que es un valor correcto ya que cae en la zona intermedia y la mayoría de las predicciones quedan agrupadas a su izquierda o derecha. Con ello se han detectado finalmente 6361 fraudes en el dataset de prueba (X_test).
59 Inteligencia artificial para la detección de fraude en transacciones realizadas con tarjetas de crédito 6.6 Almacenamiento de resultados en un archivo externo Los resultados de las predicciones se pueden guardar en un archivo de tipo “CSV”. De esta forma, pueden consultarse en cualquier momento, utilizarse más tarde con otro tipo de código y subirse a la web de Kaggle. El guardado de los resultados se ha realizado utilizando el siguiente código: Código 6.9 – Guardado de las predicciones en un archivo “CSV” res = pd.DataFrame(result_lgb['prediction'], columns=['isFraud']) # X_test_ID son los identificadores TransactionID del dataset X_test sub = pd.concat([X_test_ID, res], axis=1) sub.to_csv('submission.csv', index=False) El resultado es el archivo “submission.csv” y se muestran sus primeras filas a continuación: Figura 6-14. Primeras filas del archivo CSV 6.6.1 Subida a Kaggle Por último, se ha procedido a la subida del archivo CSV a la página web de Kaggle, que evaluará el rendimiento de la predicción en el dataset de prueba. La puntuación conseguida es la siguiente: Figura 6-15. Puntuación final conseguida en Kaggle
Creación y Entrenamiento del Modelo 60 Finalmente, se ha obtenido una puntuación de 90.95% en la predicción con el dataset X_test , un valor bueno y similar al obtenido en validación.