Detectando Fraudes em Cartões de Crédito com Machine Learning

Enfrentando as fraudes em cartões de crédito, uma das principais preocupações dos bancos e fintechs.

João Gustavo
Data Hackers

--

Photo by blocks on Unsplash

To read in English, click here!

No Brasil, cerca de 12,1 milhões de pessoas já foram vítimas de alguma fraude financeira no último ano (inclusive parentes meus). Traduzindo em valores, os golpes financeiros ultrapassaram de R$ 1,8 Bilhão de prejuízo por ano. Portanto, a não detecção dessas fraudes provocará prejuízos consideráveis, tanto para as instituições financeiras quanto para o consumidor.

Outro fator a ser considerado é a quantidade de falsos positivos, ou seja, é aquele incômodo quando você tenta realizar uma compra e o tem seu cartão bloqueado preventivamente. Geralmente, isso ocorre quando são transações fora do padrão do cliente.

Esses são os motivos, que fazem o investimento na área de detecção de fraudes por meio da Inteligência Artificial continuar crescendo a cada ano, representando uma grande oportunidade em Data Science.

Dispondo de grandes volumes de dados como base histórica, um algoritmo de machine learning apenas um pouco melhor que os anteriores já representa uma economia de milhões de Reais. Esse é o desafio, aprimorar cada vez mais o uso de algoritmos visando inibir ou evitar transações fraudulentas.

Obtenção dos Dados

Photo by Isaac Smith on Unsplash

Os dados usados foram disponibilizados por algumas empresas europeias de cartão de crédito. No dataset utilizado, temos apenas 492 fraudes em meio a quase 290 mil transações, as transações apresentadas ocorreram apenas em dois dias.

É possível observar o quanto este dataset é extremamente desbalanceado, onde as fraudes representam apenas 0,17% do total de transações. Portanto, teremos um trabalho considerável na hora de balancear os dados.

Alguns outros detalhes interessantes são o fato das variáveis serem todas numéricas e estarem descaracterizadas (para preservar a privacidade dos clientes e por segurança) e é por isso que as colunas nos são apresentas como [V1, V2, V3… V28].

Na página do kaggle, onde foram alocados os dados, é informado que as variáveis foram transformadas com o método análise de componentes principais (PCA).

Este método permite a redução da dimensionalidade enquanto mantém o maior número possível de informações. Para isso, o algoritmo encontra um novo conjunto de recursos — os chamados componentes.

Esses componentes são em número menor ou igual às variáveis originais. No caso deste projeto, os componentes achados pela transformação da PCA são as próprias colunas [V1,V2,V3…V28].

Uma vez com os dados importados para dentro de um DataFrame, podemos iniciar nossa análise exploratória e por fim preparar um modelo de Machine Learning ou até mesmo alguns.

Vamos começar!

Análise Exploratória dos Dados

Aqui é onde vou mostrar algumas informações relevantes, de forma que possam entender melhor a análise e se sintam à vontade durante a leitura.

Podemos começar checando as 5 primeiras entradas do DataFrame e ver o que elas nos dizem.

Portanto, podemos notar que as colunas Time e Amount foram mantidas em seus valores originais, de modo a não prejudicar a análise. Podemos também, esclarecer que a nossa variável alvo está localizada na coluna Class, onde 0 é uma transação comum e 1 uma transação fraudulenta.

5 Primeiras Entradas

Usando o método describe(), é possível confirmar que as variáveis que sofreram a Transformação PCA, não possuem discrepância aparente, assim como a coluna Time.

No entanto, a variável Amount, possui um valor médio (para ambas as classes) de 88.34, mediana igual a 22 e um impressionante desvio padrão de 250,12. Seu valor máximo é 25691.16, isso explica o desvio padrão, visto que a maior parte das transações são realizadas em quantias menores.

Describe do DataFrame

Podem comemorar, porque ao procurar por valores ausentes, não encontramos NENHUM! É isso mesmo, não será necessária todo aquele trabalho de limpeza nos dados.

Já sabemos que as transações classificadas como fraudulentas representam apenas 0,17% do nosso dataset. Para podermos ter uma visualização melhor, vou plotar um gráfico de barras e assim podemos confirmar esse desbalanceamento.

Distribuição das Classes

No gráfico acima, fica nítida a discrepância entre as classes. Portanto, será necessário fazer o balanceamento dos dados, de forma que nossos modelos não sejam prejudicados ao treinar com dados desbalanceados. Parece que mesmo poupando trabalho com a falta de valores ausentes, ainda será necessário muito trabalho para balancear esses dados.

De modo a compararmos o comportamento e a distribuição das classes foram plotados 4 gráficos.

  • São 2 gráficos (1 e 3) — Que usam como a referência a dimensão tempo (Time), no entanto, não foi identificada nenhuma informação a partir desses gráficos.
  • E mais 2 gráficos (2 e 4) — Que usam como referência o valor das transações (Amount), nesses dois foi possível notar que o valor das transações comuns ficam entre 0 e 5000, diferentemente das transações fraudulentas que pouquíssimas tem seu valor acima de 500.

Observe os gráficos abaixo:

Gráficos das Transações

Foram plotados também, boxplots, uma ótima ferramenta para checar se há diferença no padrão das transações em relação ao seu valor (Amount).

Abaixo é possível notar que às duas classes possuem uma distribuição diferente, provavelmente nossos modelos serão beneficiados com esse comportamento.

Boxplot para checar a distribuição em relação ao valor das transações

Apresentação dos Modelos

Para mostrar a diferença de desempenho entre modelos treinados com dados balanceados e desbalanceados, foram construídos 8 modelos, sendo:

4 Modelos de Regressão Logística, onde um deles será treinado com dados desbalanceados e os outros três serão treinados com dados balanceados por diferentes métodos.

4 Modelos de Árvores de Decisão, que serão distribuídos da mesma forma que os Modelos de Regressão Logística.

Preparação dos Dados

Antes de começar a construção dos modelos, é necessário preparar os dados. Primeiro, as colunas Time e Amount foram padronizadas, antes possuíam seus valores originais. Para realizar o processo, foi usada a classe StandardScaler.

# Importar pacotes nescessários do Scikit-Learn para preparar os dados
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Criar cópia do DataFrame
df_clean = df.copy()
# Padronizar as colunas Time e Amount
scaler = StandardScaler()
# Padronizando a coluna Amount
df_clean['Scaler_amount'] = scaler.fit_transform(df_clean['Amount'].values.reshape(-1, 1))
# Padronizando a coluna Time
df_clean['Scaler_time'] = scaler.fit_transform(df_clean['Time'].values.reshape(-1, 1))
# Excluindo as colunas com valores originais
df_clean.drop(['Time', 'Amount'], axis=1, inplace=True)
# Ver as primeiras entradas
df_clean.head()
Primeiras entradas com Dados Padronizados

Separar entre Treino e Teste

Para que os modelos possam ter um bom desempenho, é necessário separar os dados entre treino e teste, dessa forma ele poderá fazer previsões com uma maior precisão quando estiver lidando com dados que não teve contato.

# Separar os dados entre feature matrix e target vector
X = df_clean.drop('Class', axis=1)
y = df_clean['Class']
# Dividir o dataset entre treino e teste
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, shuffle=True)

Balanceamento dos Dados

Por último, foi feito o balanceamento dos dados, para que os modelos apresentem um melhor desempenho na identificação de transações fraudulentas. Com o balanceamento feito, é possível evitar o overfitting (quando um modelo se torna ótimo em fazer previsões com dados conhecidos, mas tem um desempenho inferior ao lidar com dados novos e que possuem uma menor aparição.).

Para o balanceamento, foram usados 3 métodos, sendo eles:

  • RandomUnderSampling (RUS) — Este método descarta um subconjunto aleatório da classe majoritária, preservando as características da classe minoritária, ideal para quando se há um grande volume de dados. Este método pode acarretar em um desempenho inferior na hora de fazer predições da classe majoritária.
  • ADASYN — Gera novas amostras, próximas às originais que são classificadas de forma errada usando um classificador K-Nearest Neighbors. Para entender melhor este método, clique aqui.
  • SMOTE — Enquanto isso a implementação básica do SMOTE, não fará distinção entre amostras fáceis e difíceis de serem classificadas, como é feito no ADASYN.

Balanceamento dos Dados com Under-Sampling(RUS)

# Balanceamento dos dados
from imblearn.under_sampling import RandomUnderSampler
# Definindo parâmetros
rus = RandomUnderSampler()
X_rus, y_rus = rus.fit_sample(X_train, y_train)
# Checar o balanceamento das classes
print(pd.Series(y_rus).value_counts())
# Plotar a nova distribuição de Classes
sns.countplot(y_rus);
Distribuição das Classes com RUS

Balanceamento dos Dados com Over-Sampling (SMOTE)

# Balanceamento dos dados
from imblearn.over_sampling import SMOTE
# Definindo parâmetros
smo = SMOTE()
X_smo, y_smo = smo.fit_resample(X_train, y_train)
# Checar o balanceamento das classes
print(pd.Series(y_smo).value_counts())
# Plotar a nova distribuição de Classes
sns.countplot(y_smo);
Distribuição das Classes com SMOTE

Balanceamento dos Dados com Over-Sampling (ADASYN)

# Balanceamento dos dados
from imblearn.over_sampling import ADASYN
# Definindo Parâmetros
ada = ADASYN()
X_ada, y_ada = ada.fit_resample(X_train, y_train)
# Checar o balanceamento das classes
print(pd.Series(y_ada).value_counts())
# Plotar a nova distribuição de Classes
sns.countplot(y_ada);
Distribuição das Classes com (ADASYN)

Machine Learning

Primeiro, é necessário entender o que é Machine Learning.

O algoritmo de aprendizagem de máquina (Machine Learning), também chamado de modelo, é uma expressão matemática que representa informação ou dados no contexto de qualquer problema em particular, sendo muitas vezes um problema comercial. O principal objetivo é ir dos dados para percepção, por meio do algoritmo. Existem duas categorias de aprendizagem de máquina: supervisionada e não supervisionada.

Para este projeto, foram construídos apenas modelos supervisionados.

Modelos supervisionados, são usados para explicar ou prever dados, isso é feito com a ajuda de dados antigos que serão destinados ao treino do modelo e assim ele será capaz de prever dados de saída para novas entradas.

Regressão Logística

A Regressão Logística é um algoritmo de aprendizado de máquina usado para problemas de classificação, realiza análise preditiva e se baseia no conceito de probabilidade.

Caso queira um entendimento aprofundado sobre regressão logística, clique aqui!

Construção do Modelo sem Balanceamento dos Dados

O processo de treino desse modelo foi feito apenas com dados dividos entre treino e teste, mas não com os dados balanceados. Para construção do modelo, foi usado o Scikit-learn .

# Escolher e importar um modelo
from sklearn.linear_model import LogisticRegression
# Escolher e instanciar os Hyperparameters
model_log = LogisticRegression()
# Fit do Modelo (Treinar)
model_log.fit(X_train, y_train)
# Fazer previsões em cima de novos dados
y_pred_log = model_log.predict(X_test)

Avaliando o Desempenho do Modelo

Aqui vamos imprimir um relatório de classificação, a área sob a curva (AUC) e uma matriz de confusão. Isso nos ajudará a avaliar o desempenho do modelo, de forma que poderemos comparar com os modelos seguintes e dizer qual possui um melhor desempenho na detecção de fraudes.

Uma ótima métrica que é possível utilizar para ver a taxa de acertos de fraude é a coluna recall do relatório de classificação.

O AUC é uma das melhores métricas para avaliação do modelo, uma vez que quando as previsões estão 100% erradas seu valor é zero e quando estão 100% certas seu valor é 1. Cada modelo vai apresentar um valor de AUC, isso nos auxilia a determinar o melhor modelo. Para entender melhor o AUC, clique aqui!

Abaixo podemos ver que:

  • Nosso modelo possui uma grande quantidade de falsos negativos (46 transações fraudulentas classificadas como comuns), o que não é nada bom, pois o cliente ou o banco terá de arcar com os custos.
  • Houve um ótimo desempenho para prever as transações comuns, mas um desempenho abaixo do esperado na hora de prever fraudes (apenas 77).
  • Em casos como este, a precisão e a acurácia não são nossa prioridade, uma vez que se o modelo errar mais para falsos positivos e menos para falsos negativos, já é um grande avanço.
  • O AUC deste modelo não foi dos piores, mas é possível nós melhorarmos esse 0.8130

Construção do Modelo (RUS)

O processo de treino desse modelo é extremamente parecido com o anterior, a diferença é que ele foi treinado com dados balanceados pelo método (RUS) e as previsões foram feitas em cima dos dados de teste.

# Escolher e instanciar os Hyperparameters
model_log_rus = LogisticRegression()
# Fit do modelo com dados RUS (Treinar)
model_log_rus.fit(X_rus, y_rus)
# Fazer previsões em cima de novos dados
y_pred_rus = model_log_rus.predict(X_test)

Avaliando o Desempenho do Modelo

Dessa vez, as métricas nos dizem que:

  • A quantidade de falsos negativos diminuiu consideravelmente (de 46 para 10), o que é um ponto positivo. Dessa forma, o prejuízo do banco será menor.
  • Com a melhora na previsão de fraudes (de 77 para 113) quase o dobro, houve também um grande aumento nos falsos positivos (de 8 para 2710), mas isso já era esperado e não é o pior cenário possível.
  • Esse modelo teve um excelente AUC0.9403

Construção do Modelo (SMOTE)

Neste caso, o modelo foi treinado com os dados balanceado pelo método (SMOTE) e depois efetuou suas previsões em cima dos dados de teste.

# Escolher e importar um modelo
from sklearn.linear_model import LogisticRegression
# Escolher e instanciar os Hyperparameters
model_log_smo = LogisticRegression()
# Fit do modelo com dados SMOTE (Treinar)
model_log_smo.fit(X_smo, y_smo)
# Fazer previsões em cima de novos dados
y_pred_smo = model_log_smo.predict(X_test)

Avaliando o Desempenho do Modelo

Com este modelo foi possível notar, que:

  • A quantidade de falsos negativos teve um aumento, quando comparado ao modelo (RUS), foram 12 falsos negativos contra 10.
  • A quantidade de fraudes previstas também sofreu alterações, foram 111 contra 113 do modelo (RUS).
  • Porém, o diferencial deste modelo é que a quantidade de falsos positivos diminuiu bastante (de 2710 para 1702) uma diminuição de mais de 1000 casos, fazendo com que os clientes do banco que utilizasse esse modelo tivessem seus cartões bloqueados menos vezes.
  • Esse modelo teve um excelente AUC mas não o melhor : 0.9392

Construção do Modelo (ADASYN)

Este modelo foi treinado com dados balanceados pelo método (ADASYN) e efetuous suas previsões em cima dos dados de teste.

# Escolher e plotar um modelo
model_log_ada = LogisticRegression()
# Fit do modelo com dados ADASYN (Treinar)
model_log_ada.fit(X_ada, y_ada)
# Fazer previsões em cima de novos dados
y_pred_ada = model_log_ada.predict(X_test)

Avaliando o Desempenho do Modelo

Este é o último modelo de Regressão Logística e apresentou resultados bem interessantes, vamos dar uma olhada:

  • A quantidade de falsos negativos foi a menor dos nossos modelos de regressão logística, sendo apenas 6 falsos negativos, contra as 10 do modelo (RUS).
  • A quantidade de fraudes previstas foi a maior entre todos os modelos, foram 117 fraudes previstas, enquanto nos outros modelos não passava de 113 previsões.
  • Porém, um ponto negativo deste modelo é que a quantidade de falsos positivos aumentou bastante, tendo a maior quantidade quando comparado aos outros modelos (sendo 6533 contra os 1702 do modelo SMOTE), assim os clientes do banco que escolhesse esse modelo, teriam seus cartões bloqueados mais vezes, mas em compensação o prejuízo seria menor caso seu cartão fosse fraudado.
  • Esse modelo teve o menor AUC dos modelos que treinaram com dados balanceados : 0.9297

Porém, vale lembrar que o AUC sozinho não é suficiente para dizer que esse é o melhor ou o pior modelo, pois depende de qual a solução mais eficiente para a instituição. Às vezes, a melhor solução para o banco é aquela que vai ter uma maior quantidade de falsos positivos e ter menos falsos negativos, do que o inverso.

Árvores de Decisão (Decision Trees)

Os processos a seguir são extremamente parecidos com os anteriores, porém agora o modelo será uma árvore de decisão, sendo assim o método usado é diferente para classificar as transações.

Dessa forma, seremos capazes de definir qual modelo seria melhor para Detecção de Fraude, uma árvore de decisão ou uma regressão logística.

Para melhor compreensão, irei dar uma breve explicação de como um modelo de Árvore de Decisão funciona.

Basicamente uma árvore de decisão é composta por um nó inicial (conhecido também como raiz), nós internos, ramos e folhas. De forma que os dados possam ser divididos em subconjuntos cada vez mais puros, assim é encontrada a maior pureza em sua classificação.

Para uma visualização gráfica de como é construída uma árvore de decisão, acesse o link do R2D3.

Construção do Modelo de Árvore sem Balanceamento de Dados

Para construir o modelo, mais uma vez foi usado o Scikit-Learn. Assim como foi feito nos modelos de Regressão Logística, esse primeiro modelo foi treinado com dados desbalanceados.

# Escolher e importar o modelo
from sklearn.tree import DecisionTreeClassifier
# Escolher os Hyperparameters
model_tree = DecisionTreeClassifier(max_depth=4,criterion='entropy')
# Fit do Modelo(Treinar)
model_tree.fit(X_train, y_train)
# Fazer previsões em cima dos dados de teste
y_pred_tree = model_tree.predict(X_test)

Avaliando o Desempenho do Modelo

Aqui vamos usar as métricas que você já está familiarizado, o relatório de classificação, a área sob a curva (AUC) e uma matriz de confusão.

Lembre-se sempre que podemos ver a taxa de acertos de fraude na coluna recall do relatório de classificção.

Vamos dar uma olhada em como nosso primeiro modelo de árvore de decisão se comportou:

  • Nosso modelo possui uma quantidade menor de falsos negativos que o primeiro modelo de RL (Regressão Logística) (34 contra as 46 transações fraudulentas classificadas como comuns), aqui podemos verificar que uma árvore de decisão teve um desempenho melhor com dados desbalanceados.
  • Houve um ótimo desempenho para prever as transações comuns, até mesmo melhor que o modelo de RL, mas um desempenho abaixo do que seria satisfatório (89 contra 77).
  • Lembrando mais uma vez, em casos como este que a precisão e a acurácia não são nossa prioridade, uma vez que se o modelo errar mais para falsos positivos e menos para falsos negativos, já é um grande avanço.
  • O AUC deste modelo foi melhor que o nosso modelo de RL : 0.8617.

Até o momento, tudo indica que modelos de Árvore de Decisão são melhores nessa situação, mas será que apenas com dados desbalanceados ou com balanceados também?

Isso você poderá descobrir nos próximos modelos.

Construção do Modelo de Árvore(RUS)

O mesmo de antes, modelo treinado com dados balanceados pelo método (RUS), mas dessa vez em uma árvore de decisão. Aqui é possível checar se as Árvores de Decisão vão continuar tendo um melhor desempenho que os Modelos de Regressão Logística.

# Escolher os hyperparameters
model_tree_rus = DecisionTreeClassifier(max_depth=8,
criterion='entropy')
# Fit do Modelo com Dados RUS(Treinar)
model_tree_rus.fit(X_rus, y_rus)
# Fazer previsões em cima dos dados de teste
y_pred_tree_rus = model_tree_rus.predict(X_test)

Avaliando o Desempenho do Modelo

Vamos verificar como nosso primeiro modelo de árvore de decisão, com dados balanceados, se comportou:

  • A quantidade de falsos negativos diminuiu consideravelmente (de 34 para 13), o que é um ponto positivo, mas infelizmente o modelo de RL obteve um desempenho superior ao treinar com esses dados.
  • Houve uma melhora na previsão de fraudes (de 89 para 110) e isso gerou um grande aumento nos falsos positivos (de 6 para 6863), isso foi um tanto quanto inesperado, esse aumento se torna desastroso quando comparado ao modelo de RL.
  • Podemos afirmar que o AUC de 0.8989, foi o menor quando comparado aos modelos treinados com dados balanceados.

Construção do Modelo de Árvore (SMOTE)

Este modelo foi treinado com dados balanceados pelo método (SMOTE) e depois realizou suas previsões em cima dos dados de teste.

# Escolher os Hyperparameters
model_tree_smo = DecisionTreeClassifier(max_depth=6,
criterion='entropy')
# Fit do Modelo com dados SMOTE (Treinar)
model_tree_smo.fit(X_smo, y_smo)
# Fazer previsões em cima dos dados de teste
y_pred_tree_smo = model_tree_smo.predict(X_test)

Avaliando o Desempenho do Modelo

Com este modelo, podemos ver que:

  • A quantidade de falsos negativos teve um aumento, quando comparado ao modelo de árvore (RUS), foram 13 falsos negativos para 18.
  • A quantidade de fraudes previstas caiu , foram 111 contra 113 do modelo de árvore (RUS).
  • Porém, esse modelo de árvore foi o que menos apresentou quantidades de falsos positivos (são 3588 contra os 6863 do modelo anterior), uma diminuição de mais de 3200 casos, fazendo com que os clientes do banco que escolhesse utilizar esse modelo, tivessem seus cartões bloqueados menos vezes.
  • Esse modelo teve um excelente AUC mas não o melhor — 0.9016

Os modelos de RL ainda aparentam ter um desempenho melhor na hora de detectar fraudes.

Construção do Modelo de Árvore (ADASYN)

Este modelo foi treinado com dados balanceados pelo método (ADASYN) e realizou suas previsões em cima dos dados de teste.

# Escolher os Hyperparameters
model_tree_ada = DecisionTreeClassifier(max_depth=8, criterion='entropy')
# Fit do Modelo com dados ADASYN (Treinar)
model_tree_ada.fit(X_ada, y_ada)
# Fazer previsões em cima dos dados de teste
y_pred_tree_ada = model_tree_ada.predict(X_test)

Avaliando o Desempenho do Modelo

No último modelo de Árvore de Decisão, tivemos os resultados mais equilibrados, mas não os melhores, dê uma olhada:

  • A quantidade de falsos negativos não foi a menor e nem a maior entre os modelos de árvore, estando no meio 15 falsos negativos, contra as 13 do modelo de árvore (RUS).
  • A quantidade de fraudes se manteve equilibrada mais uma vez, foram 108 fraudes previstas, enquanto outro modelo foi capaz de realizar 110 previsões.
  • Assim como o modelo (ADASYN) de RL, o modelo de árvore também tem um ponto negativo quanto a quantidade de falsos positivos (sendo 4032 contra os 3588 do SMOTE), mas vale lembrar que esse não é o modelo com a maior quantidade de falsos positivos.
  • Esse modelo teve o maior AUC dos modelos de árvore — 0.9107

Conclusão

Como podemos ver, esse é um projeto diferente dos outros, uma vez que não possui valores ausentes e não foi nescessário fazer um limpeza dos dados. Apesar de termos trabalhado com dados de qualidade e bem limpos, foi necessário lidar com o desbalanceamento e a Transformação PCA, assim demandando um trabalho considerável.

É possível concluir que:

  • Algoritmos de Regressão Logística possuem um desempenho melhor ao lidar com dados balanceados.
  • Algoritmos de Árvore de Decisão foram superiores ao lidar com dados desbalanceados.
  • A solução ideal é aquela que melhor atende a instituição, podendo ser a com o maior AUC ou a com o maior número de detecção de fraudes.
  • O algoritmo que melhor conseguiu prever fraudes foi o Modelo de Regressão Logística que treinou com dados balanceados pelo método ADASYN.

Para ter acesso ao projeto completo, clique aqui! Aproveita para me seguir no LinkedIn e fica de olho no meu GitHub, lá você poderá encontrar mais projetos futuramente.

--

--