Luiz Felipe P. Figueiredo
  • Home
  • Tech Skills
  • Skill Sets
  • Work Experience
  • Projects
  • Dashboards
  • Blog
  • About me

Detecção de transações fraudulentas com Cartões de Crédito

  • Mostrar o código
  • Esconder o código

  • Ver o código fonte
Python
Machine Learning
Desenvolvimento de modelo de Machine Learning para detecção de fraudes em cartões.
Autor

Luiz Felipe P. Figueiredo

Data de Publicação

03/12/2023

Objetivo

Este projeto tem como objetivo, desenvolver um modelo de aprendizado de maquina a fim de detectar transações fraudulentas envolvendo cartões de crédito. Isso será alcançado por meio da exploração e aplicação de vários algoritmos de aprendizado de máquina, seguidos pela avaliação e comparação de métricas de desempenho com o objetivo de selecionar o modelo mais eficaz.

Descrição dos Dados

Os dados que serão utilizado neste projeto, foram retirados do kaggle. Trata-se de uma base de dados que contém transações feitas por cartões de créditos europeus em setembro de 2013. Além disso a base de dados contém 28 variáveis ou colunas que foram anonimizadas a fim de proteger a sigilosidade dos dados em questão.

Descrição das variáveis

  • Time - Segundos que se passaram entre cada transação e a primeira transação no dataset;
  • V1 a V28 - Variáveis anonimizadas devido a confidencialidade dos dados, essa variáveis foram transformadas utilizando Análise de Componentes Principais;
  • Amount - Valor de cada transação;
  • Class - Variável resposta, onde 1 equivale a uma compra fraudulenta e 0 com não fraudulenta.
Código
# bibliotecas utilizadas
import numpy as np
import pandas as pd
import plotly.io as pio
pio.renderers.default = "notebook_connected"
import plotly.colors as colors
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score, average_precision_score, roc_curve, precision_recall_curve
from sklearn.linear_model import LogisticRegression

try:
    from imblearn.over_sampling import SMOTE
except Exception:
    SMOTE = None

Verificação dos Dados

Código
# preview do dataset
df = pd.read_csv("C:/Users/luisf/Desktop/DATACAMP/Portfolio/datasets/credit_card_fraud/creditcard.csv")
df.head()
Time V1 V2 V3 V4 V5 V6 V7 V8 V9 ... V21 V22 V23 V24 V25 V26 V27 V28 Amount Class
0 0.0 -1.359807 -0.072781 2.536347 1.378155 -0.338321 0.462388 0.239599 0.098698 0.363787 ... -0.018307 0.277838 -0.110474 0.066928 0.128539 -0.189115 0.133558 -0.021053 149.62 0
1 0.0 1.191857 0.266151 0.166480 0.448154 0.060018 -0.082361 -0.078803 0.085102 -0.255425 ... -0.225775 -0.638672 0.101288 -0.339846 0.167170 0.125895 -0.008983 0.014724 2.69 0
2 1.0 -1.358354 -1.340163 1.773209 0.379780 -0.503198 1.800499 0.791461 0.247676 -1.514654 ... 0.247998 0.771679 0.909412 -0.689281 -0.327642 -0.139097 -0.055353 -0.059752 378.66 0
3 1.0 -0.966272 -0.185226 1.792993 -0.863291 -0.010309 1.247203 0.237609 0.377436 -1.387024 ... -0.108300 0.005274 -0.190321 -1.175575 0.647376 -0.221929 0.062723 0.061458 123.50 0
4 2.0 -1.158233 0.877737 1.548718 0.403034 -0.407193 0.095921 0.592941 -0.270533 0.817739 ... -0.009431 0.798278 -0.137458 0.141267 -0.206010 0.502292 0.219422 0.215153 69.99 0

5 rows × 31 columns

Verificação de Valores Faltantes e dados desbalanceados.

Código
# Verificação de valores faltantes
n_valores_faltantes = int(df[df.columns[df.isnull().any()]].isnull().sum().sum())
print(f'Existe um total de {n_valores_faltantes} valores faltantes nesta base de dados')
Existe um total de 0 valores faltantes nesta base de dados
Código
# Verificando Dados Desbalanceados
rotulos = {0: 'Não Fraude', 1: 'Fraude'}
contagens = df['Class'].map(rotulos).value_counts()
dist_class = pd.DataFrame({'Class': contagens.index, 'Contagem': contagens.values})

fig = px.bar(dist_class, x='Class', y='Contagem', log_y=True, title='Distribuição da Variável Class', labels=dict(x='Class', y='Frequência'))
fig.show()

Acima, pode-se se verificar que não existem valores faltantes neste dataset, além disso, estes dados são extremamente desbalanceados, a variável resposta Class contem 284.315 compras legitimas e somente 492 compras fraudulentas, deste modo, será necessário implementar técnicas para tratar este desbalanceamento, pois, implementar um modelo de aprendizado de maquina seria um problema e com certeza geraria um modelo com desempenho ruim.

Tratamento De Dados

Como as variáveis Time e Amount não foram dimensionadas, é preciso realizar a transformação dessas variáveis, utilizando o sklearn.preprocessing.RobustScaler já que esse “scaler” é robusto para outliers [1].

Código
# Dimensionamento das variáveis Time e Amount
robust_scaler = RobustScaler()
features_df = df.copy()

features_df[['Scaled_Amount', 'Scaled_Time']] = robust_scaler.fit_transform(df[['Amount', 'Time']])
features_df = features_df.drop(['Amount', 'Time'], axis=1)
Código
# Definição de variáveis preditoras (X) e alvo (y)
X = features_df.drop('Class', axis=1)
y = features_df['Class']
Código
# Divisão treino/teste estratificada
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, random_state=42, stratify=y
)

# Balanceamento com SMOTE (apenas nos dados de treino)
if SMOTE is None:
    raise ImportError("imblearn não está instalado. Instale com: pip install imbalanced-learn")
smote = SMOTE(random_state=42)
X_train_res, y_train_res = smote.fit_resample(X_train, y_train)
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
Cell In[7], line 8
      6 # Balanceamento com SMOTE (apenas nos dados de treino)
      7 if SMOTE is None:
----> 8     raise ImportError("imblearn não está instalado. Instale com: pip install imbalanced-learn")
      9 smote = SMOTE(random_state=42)
     10 X_train_res, y_train_res = smote.fit_resample(X_train, y_train)

ImportError: imblearn não está instalado. Instale com: pip install imbalanced-learn

Tratando dados desbalanceados

Utilizamos SMOTE (Synthetic Minority Over-sampling Technique) para gerar amostras sintéticas da classe minoritária (Class = 1, Fraude) apenas no conjunto de treino, evitando vazamento de informação. Isso ajuda os modelos a aprender melhor os padrões raros de fraude [2,3].

Código
0    45
Name: Class, dtype: int64
Código
df['Class'].value_counts()
0    284315
1       492
Name: Class, dtype: int64
Código
cluster = 0
center = round(len(df[df['Cluster']==cluster])/2)
novo_df = pd.DataFrame()
pd.concat([novo_df, df[df['Cluster'] == cluster].iloc[center:center+1]])
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 ... V23 V24 V25 V26 V27 V28 Amount Time Class Cluster
141127 -0.748337 1.352324 1.21948 0.001557 -0.321504 -0.940654 0.385059 0.323884 -0.663196 -0.553007 ... 0.021669 0.383776 -0.148222 0.071436 0.144934 0.059351 1.79 84137.0 0 0

1 rows × 32 columns

Código
df[df['Cluster'] == cluster].iloc[center]
V1            -0.748337
V2             1.352324
V3             1.219480
V4             0.001557
V5            -0.321504
V6            -0.940654
V7             0.385059
V8             0.323884
V9            -0.663196
V10           -0.553007
V11           -0.432604
V12            0.465073
V13            0.756511
V14            0.325729
V15            0.735773
V16            0.288397
V17           -0.262055
V18           -0.396918
V19            0.004576
V20            0.026471
V21           -0.178891
V22           -0.550972
V23            0.021669
V24            0.383776
V25           -0.148222
V26            0.071436
V27            0.144934
V28            0.059351
Amount         1.790000
Time       84137.000000
Class          0.000000
Cluster        0.000000
Name: 141127, dtype: float64

Referências

  • 1 - Scikit-learn developers. RobustScaler. Scikit-learn: machine learning in Python. Disponível em: link. Acesso em: 05 dez. 2023.
  • 2 - BROWNLEE, Jason. SMOTE for Imbalanced Classification with Python. Machine Learning Mastery, 2020. Disponível em: <1>. Acesso em: 05 dez. 2023.
  • 3 - GENESIS. SMOTE: Synthetic Minority Oversampling Technique. From The GENESIS, 2018. Disponível em: <1>. Acesso em: 05 dez. 2023.
Código
teste = pd.read_excel('/Users/luisf/Desktop/Planilha sem título.xlsx')
teste.columns
Index(['ANO ', 'LOJA', 'JANEIRO', 'FEVEREIRO', 'MARÇO', 'ABRIL', 'MAIO ',
       'JUNHO ', 'JULHO ', 'AGOSTO', 'SETEMBRO', 'OUTUBRO', 'NOVEMBRO ',
       'DEZEMBRO'],
      dtype='object')
Código
# Reestruturando os dados para o formato "long" para usar com Plotly Express
teste_melted = teste.melt(id_vars=["ANO ", "LOJA"], var_name="Mês", value_name="Vendas")

# Criando o gráfico de linhas com Plotly Express
fig = px.line(teste_melted[teste_melted['ANO ']==2018], x="Mês", y="Vendas", color="LOJA",
              title='Vendas Mensais por Loja', markers=True)

# Mostrando o gráfico
fig.show()
Código
help(pd.melt)

ADASYN TOMEK Links SMOTE Under e Oversampling

Hibridos

Código
240 - 200
40
Código
40/20
2.0

Modelagem

Treinaremos uma Regressão Logística com regularização padrão como baseline, avaliando em dados de teste originais (não balanceados) e reportando métricas apropriadas para classes desbalanceadas (ROC-AUC e PR-AUC).

Código
# Treino do modelo baseline
log_reg = LogisticRegression(max_iter=200, class_weight=None, solver='lbfgs')
log_reg.fit(X_train_res, y_train_res)

# Predições
y_proba = log_reg.predict_proba(X_test)[:, 1]
y_pred = (y_proba >= 0.5).astype(int)

# Métricas
roc_auc = roc_auc_score(y_test, y_proba)
ap = average_precision_score(y_test, y_proba)
print(f"ROC-AUC: {roc_auc:.4f}")
print(f"Average Precision (PR-AUC): {ap:.4f}")
print("\nClassification Report (limiar=0.5):")
print(classification_report(y_test, y_pred, digits=4))
Código
# Matriz de confusão
cm = confusion_matrix(y_test, y_pred)
cm_fig = px.imshow(cm, text_auto=True, color_continuous_scale='Blues',
                   labels=dict(x='Predito', y='Verdadeiro', color='Contagem'),
                   x=['Não Fraude', 'Fraude'], y=['Não Fraude', 'Fraude'],
                   title='Matriz de Confusão (limiar=0.5)')
cm_fig.update_xaxes(side='bottom')
cm_fig.show()
Código
# Curvas ROC e Precisão-Revocação
fpr, tpr, _ = roc_curve(y_test, y_proba)
prec, rec, _ = precision_recall_curve(y_test, y_proba)

fig = make_subplots(rows=1, cols=2, subplot_titles=("ROC Curve", "Precision-Recall Curve"))

fig.add_trace(go.Scatter(x=fpr, y=tpr, mode='lines', name=f'ROC (AUC={roc_auc:.3f})'), row=1, col=1)
fig.add_trace(go.Scatter(x=[0,1], y=[0,1], mode='lines', name='No Skill', line=dict(dash='dash')), row=1, col=1)

fig.add_trace(go.Scatter(x=rec, y=prec, mode='lines', name=f'PR (AP={ap:.3f})'), row=1, col=2)
no_skill = y_test.mean()
fig.add_trace(go.Scatter(x=[0,1], y=[no_skill, no_skill], mode='lines', name='No Skill', line=dict(dash='dash')), row=1, col=2)

fig.update_layout(title_text='Curvas ROC e Precisão-Revocação', showlegend=True)
fig.update_xaxes(title_text='FPR', row=1, col=1)
fig.update_yaxes(title_text='TPR', row=1, col=1)
fig.update_xaxes(title_text='Recall', row=1, col=2)
fig.update_yaxes(title_text='Precision', row=1, col=2)
fig.show()

Observações finais

  • O dataset é altamente desbalanceado; PR-AUC é uma métrica mais informativa que acurácia.
  • SMOTE foi aplicado somente em treino para evitar vazamento.
  • Próximos passos: testar modelos como RandomForest, XGBoost/LightGBM, calibrar limiar por curva PR, e validação temporal se for um cenário real.

This work is licensed under CC BY-SA 4.0

Designed and Developed collaboratively by
Jose C. S. Junior & Luiz F. P. Figueiredo

Built with Quarto