- Caso de uso de data science em bancos: detecção de fraude
- Preparando o dataset
- Calculando a taxa de fraude no dataset
- Usando SMOTE para reequilibrar os dados
- Aplicando regressão logística
- Conclusão
O setor bancário é um dos poucos que historicamente coletam grandes volumes de dados estruturados e também um dos primeiros a adotar tecnologias de data science.
Como a data science é usada em bancos? Hoje, os dados se tornaram o ativo mais valioso nessa área. A data science é essencial para que os bancos acompanhem os concorrentes, atraiam mais clientes, aumentem a fidelidade dos atuais, tomem decisões mais eficientes baseadas em dados, impulsionem o negócio, ganhem eficiência operacional, aprimorem serviços e produtos existentes e lancem novos, reforcem a segurança e, como resultado de tudo isso, aumentem a receita. Não é surpresa que a maior parte da demanda por profissionais de data science venha do setor bancário.
A data science permite que a indústria bancária execute com sucesso diversas tarefas, incluindo:
- análise de risco de investimento
- previsão de valor do ciclo de vida do cliente (CLV)
- segmentação de clientes
- previsão de churn de clientes
- marketing personalizado
- análise de sentimento do cliente
- assistentes virtuais e chatbots
A seguir, vamos detalhar um dos casos de uso mais comuns de data science em bancos.
Caso de uso de data science em bancos: detecção de fraude
Atividades fraudulentas são um desafio não só no setor bancário, mas também em áreas como governo, seguros, setor público, vendas e saúde. Qualquer negócio que lide com um grande volume de transações online corre um risco significativo de fraude. Crimes financeiros podem assumir várias formas, como transações fraudulentas com cartão de crédito, cheques bancários falsificados, sonegação fiscal, lavagem de dinheiro, ataques cibernéticos, roubo de contas de clientes, identidades sintéticas, aplicações falsas e golpes.
A detecção de fraude é um conjunto de medidas proativas para identificar e prevenir atividades fraudulentas e perdas financeiras. Suas principais técnicas analíticas podem ser divididas em dois grupos:
- Estatísticas: cálculo de parâmetros estatísticos, regressão, distribuições de probabilidade, correspondência de dados
- Inteligência artificial (IA): mineração de dados, machine learning, deep learning
O machine learning é um pilar essencial na detecção de fraudes. Seu conjunto de ferramentas oferece duas abordagens:
- Métodos supervisionados: k-vizinhos mais próximos, regressão logística, máquinas de vetor de suporte, árvore de decisão, random forest, análise de séries temporais, redes neurais, etc.
- Métodos não supervisionados: análise de clusters, análise de vínculos, mapas auto-organizáveis, análise de componentes principais, reconhecimento de anomalias, etc.
Não existe um algoritmo universal e infalível de machine learning para detecção de fraude. Em casos reais de data science, normalmente testamos várias técnicas ou combinações delas, calculamos a acurácia preditiva do modelo e escolhemos a melhor abordagem.
O grande desafio dos sistemas de detecção de fraude é se adaptar rapidamente a padrões e táticas que mudam o tempo todo e identificar com agilidade esquemas novos e cada vez mais elaborados. Casos de fraude são sempre minoria e ficam bem ocultos entre as transações legítimas.
Preparando o dataset
Vamos explorar uma implementação de detecção de fraude em cartão de crédito usando a linguagem Python. Vamos trabalhar com o dataset creditcard_data, que é uma amostra modificada de um conjunto do Kaggle sobre Credit Card Fraud Detection. Os dados originais representam transações feitas em cartões de crédito de clientes europeus durante dois dias de setembro de 2013.
Vamos importar os dados e dar uma olhada rápida:
import pandas as pd
creditcard_data = pd.read_csv('creditcard_data.csv', index_col=0)
print(creditcard_data.info())
print('\n')
pd.options.display.max_columns = len(creditcard_data)
print(creditcard_data.head(3))
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5050 entries, 0 to 5049
Data columns (total 30 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 V1 5050 non-null float64
1 V2 5050 non-null float64
2 V3 5050 non-null float64
3 V4 5050 non-null float64
4 V5 5050 non-null float64
5 V6 5050 non-null float64
6 V7 5050 non-null float64
7 V8 5050 non-null float64
8 V9 5050 non-null float64
9 V10 5050 non-null float64
10 V11 5050 non-null float64
11 V12 5050 non-null float64
12 V13 5050 non-null float64
13 V14 5050 non-null float64
14 V15 5050 non-null float64
15 V16 5050 non-null float64
16 V17 5050 non-null float64
17 V18 5050 non-null float64
18 V19 5050 non-null float64
19 V20 5050 non-null float64
20 V21 5050 non-null float64
21 V22 5050 non-null float64
22 V23 5050 non-null float64
23 V24 5050 non-null float64
24 V25 5050 non-null float64
25 V26 5050 non-null float64
26 V27 5050 non-null float64
27 V28 5050 non-null float64
28 Amount 5050 non-null float64
29 Class 5050 non-null int64
dtypes: float64(29), int64(1)
memory usage: 1.2 MB
V1 V2 V3 V4 V5 V6 V7 \
0 1.725265 -1.337256 -1.012687 -0.361656 -1.431611 -1.098681 -0.842274
1 0.683254 -1.681875 0.533349 -0.326064 -1.455603 0.101832 -0.520590
2 1.067973 -0.656667 1.029738 0.253899 -1.172715 0.073232 -0.745771
V8 V9 V10 V11 V12 V13 V14 \
0 -0.026594 -0.032409 0.215113 1.618952 -0.654046 -1.442665 -1.546538
1 0.114036 -0.601760 0.444011 1.521570 0.499202 -0.127849 -0.237253
2 0.249803 1.383057 -0.483771 -0.782780 0.005242 -1.273288 -0.269260
V15 V16 V17 V18 V19 V20 V21 \
0 -0.230008 1.785539 1.419793 0.071666 0.233031 0.275911 0.414524
1 -0.752351 0.667190 0.724785 -1.736615 0.702088 0.638186 0.116898
2 0.091287 -0.347973 0.495328 -0.925949 0.099138 -0.083859 -0.189315
V22 V23 V24 V25 V26 V27 V28 \
0 0.793434 0.028887 0.419421 -0.367529 -0.155634 -0.015768 0.010790
1 -0.304605 -0.125547 0.244848 0.069163 -0.460712 -0.017068 0.063542
2 -0.426743 0.079539 0.129692 0.002778 0.970498 -0.035056 0.017313
Amount Class
0 189.00 0
1 315.17 0
2 59.98 0
O dataset contém as seguintes variáveis:
- Variáveis numericamente codificadas de V1 a V28, que são componentes principais obtidos por uma transformação PCA. Por questões de confidencialidade, não há informações de contexto sobre as variáveis originais.
- A variável Amount representa o valor da transação.
- A variável Class indica se a transação foi fraudulenta (1) ou não (0).
Por natureza, ocorrências de fraude felizmente são uma minoria extrema em qualquer lista de transações. Porém, algoritmos de machine learning tendem a funcionar melhor quando as classes no dataset estão mais ou menos igualmente representadas. Caso contrário, há poucos dados para aprender. Esse problema é conhecido como desbalanceamento de classes.
Calculando a taxa de fraude no dataset
Vamos calcular a porcentagem de transações fraudulentas em relação ao total de transações no nosso dataset:
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
e criar um gráfico para visualizar os pontos de dados de fraude versus não fraude:
import matplotlib.pyplot as plt
import numpy as np
def prep_data(df):
X = df.iloc[:, 1:28]
X = np.array(X).astype(float)
y = df.iloc[:, 29]
y = np.array(y).astype(float)
return X, y
def plot_data(X, y):
plt.scatter(X[y==0, 0], X[y==0, 1], label='Class #0', alpha=0.5, linewidth=0.15)
plt.scatter(X[y==1, 0], X[y==1, 1], label='Class #1', alpha=0.5, linewidth=0.15, c='r')
plt.legend()
return plt.show()
X, y = prep_data(creditcard_data)
plot_data(X, y)

Usando SMOTE para reequilibrar os dados
Agora conseguimos confirmar que a proporção de transações fraudulentas é muito baixa e que temos um caso de desbalanceamento de classes. Para corrigir, podemos reequilibrar os dados usando a técnica de oversampling da minoria sintética (SMOTE). Diferente do oversampling aleatório, o SMOTE é um pouco mais sofisticado, pois não cria apenas cópias exatas das observações. Em vez disso, usa características dos vizinhos mais próximos dos casos de fraude para gerar novas amostras sintéticas, bastante semelhantes às observações existentes na classe minoritária. Vamos aplicar SMOTE aos nossos dados de cartão de crédito:
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

Como podemos ver, o uso do SMOTE passa a gerar mais observações da classe minoritária. Para enxergar ainda melhor os resultados, vamos compará-los com os dados originais:
def compare_plot(X, y, X_resampled, y_resampled, method):
f, (ax1, ax2) = plt.subplots(1, 2)
c0 = ax1.scatter(X[y==0, 0], X[y==0, 1], label='Class #0',alpha=0.5)
c1 = ax1.scatter(X[y==1, 0], X[y==1, 1], label='Class #1',alpha=0.5, c='r')
ax1.set_title('Original set')
ax2.scatter(X_resampled[y_resampled==0, 0], X_resampled[y_resampled==0, 1], label='Class #0', alpha=.5)
ax2.scatter(X_resampled[y_resampled==1, 0], X_resampled[y_resampled==1, 1], label='Class #1', alpha=.5,c='r')
ax2.set_title(method)
plt.figlegend((c0, c1), ('Class #0', 'Class #1'), loc='lower center', ncol=2, labelspacing=0.)
plt.tight_layout(pad=3)
return plt.show()
print(f'Original set:\n'
f'{pd.value_counts(pd.Series(y))}\n\n'
f'SMOTE:\n'
f'{pd.value_counts(pd.Series(y_resampled))}\n')
compare_plot(X, y, X_resampled, y_resampled, method='SMOTE')
Original set:
0.0 5000
1.0 50
dtype: int64
SMOTE:
0.0 5000
1.0 5000
dtype: int64

Ou seja, o método SMOTE balanceou completamente nossos dados, e a classe minoritária agora tem o mesmo tamanho da classe majoritária.
Voltaremos em breve à aplicação prática do SMOTE, mas por enquanto vamos retomar os dados originais e tentar detectar casos de fraude. Seguindo a abordagem "old school", precisamos criar algumas regras para capturar fraudes. Essas regras podem considerar, por exemplo, locais incomuns de transações ou frequência suspeita de compras. A ideia é definir valores de corte com base em estatísticas comuns, muitas vezes nas médias das observações, e usar esses limites nas features para detectar fraude.
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
No nosso caso específico, vamos aplicar as seguintes condições: V1 < -3 e V3 < -5. Em seguida, para estimar o desempenho dessa abordagem, vamos comparar os casos de fraude sinalizados com os reais:
creditcard_data['flag_as_fraud'] = np.where(np.logical_and(creditcard_data['V1']<-3, creditcard_data['V3']<-5), 1, 0)
print(pd.crosstab(creditcard_data['Class'], creditcard_data['flag_as_fraud'], rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0 1
Actual Fraud
0 4984 16
1 28 22
Aplicando regressão logística
Detectamos 22 de 50 casos de fraude, mas deixamos 28 passarem e ainda tivemos 16 falsos positivos. Vamos ver se técnicas de machine learning conseguem superar esses resultados.
Agora vamos implementar um algoritmo simples de classificação por regressão logística nos nossos dados de cartão de crédito para identificar ocorrências fraudulentas e, em seguida, visualizar os resultados em uma matriz de confusão:
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
lr = LogisticRegression()
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1504 1
1.0 1 9
É importante notar que temos menos observações na matriz de confusão porque estamos usando apenas o conjunto de teste para calcular os resultados do modelo, isto é, somente 30% do dataset.
Capturamos uma porcentagem maior de casos de fraude: 90% (9 de 10), comparado ao resultado anterior de 44% (22 de 50). Também tivemos bem menos falsos positivos do que antes, o que já é uma melhoria.
Agora, vamos voltar ao problema de desbalanceamento de classes discutido anteriormente e verificar se conseguimos aumentar ainda mais a qualidade das previsões combinando o modelo de regressão logística com o método de reamostragem SMOTE. Para fazer isso de forma eficiente e de uma vez só, precisamos definir um pipeline e executá-lo nos dados:
from imblearn.pipeline import Pipeline
# Definindo qual método de reamostragem e qual modelo de ML usar no pipeline
resampling = SMOTE()
lr = LogisticRegression()
pipeline = Pipeline([('SMOTE', resampling), ('Logistic Regression', lr)])
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1496 9
1.0 1 9
Como podemos ver, neste caso o SMOTE não trouxe melhorias: ainda capturamos 90% das ocorrências de fraude e, além disso, tivemos um número um pouco maior de falsos positivos. A explicação é que a reamostragem nem sempre leva a melhores resultados. Quando os casos de fraude estão muito dispersos nos dados, seus vizinhos mais próximos nem sempre são fraudes; assim, o SMOTE pode introduzir viés.
Conclusão
Como próximos passos para aumentar a acurácia da regressão logística, podemos ajustar alguns parâmetros do algoritmo. Também vale considerar K-fold cross-validation em vez de simplesmente dividir o dataset em duas partes. Por fim, podemos experimentar outros algoritmos de machine learning (por exemplo, árvore de decisão ou random forest) e avaliar se trazem resultados melhores.
Se você quiser se aprofundar nos aspectos teóricos e técnicos da implementação de modelos de detecção de fraude, explore o curso Fraud Detection in Python.
