Projetos
Passo a passo · Deep Learning · Tarefa 01

Predição de Churn com Redes Neurais (MLP)

Vamos construir do zero uma rede neural que aprende a identificar clientes com risco de cancelamento. Se você nunca viu Deep Learning, não se preocupe — vou explicar tudo com exemplos do dia a dia.

Visão geral

O que vamos fazer?

Uma empresa de telefonia quer saber quais clientes têm mais chance de cancelar o serviço antes que isso aconteça. Com esse aviso antecipado, o time comercial pode agir — ligar, oferecer um desconto — e salvar o contrato.

Imagina que você tem uma lista de 7.000 clientes. Cada um tem 20 informações: quanto tempo está na empresa, qual plano contratou, quanto paga por mês… A rede neural vai aprender com exemplos históricos para responder: esse cliente vai cancelar ou não?

Isso é um problema de classificação binária (sim ou não). Vamos resolvê-lo com uma rede MLP (Multilayer Perceptron) — a arquitetura mais clássica de Deep Learning.

Ferramentas

O que você vai precisar

  • Python 3.10+ — a linguagem que vamos usar.
  • Pandas — para ler e organizar os dados em tabelas.
  • Scikit-learn — tem o MLPClassifier que vamos usar.
  • Matplotlib / Seaborn — para os gráficos.
  • Jupyter Notebook — ambiente interativo onde rodamos o código.

Dataset: IBM Telco Customer Churn — arquivo Telco-Customer-Churn.csv com 7.043 registros de clientes.

Passo 1

Carregar e explorar os dados

Antes de construir qualquer modelo, precisamos entender o que temos. É como conhecer os ingredientes antes de cozinhar.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# pd.read_csv() lê o arquivo e transforma em uma tabela (DataFrame)
df = pd.read_csv('Telco-Customer-Churn.csv')

# .shape retorna (linhas, colunas)
print(f'Shape: {df.shape}')   # Shape: (7043, 21)

pd.read_csv() é como "abrir uma planilha do Excel" dentro do Python. O resultado (df) é uma tabela com 7.043 linhas e 21 colunas.

# .info() mostra o tipo de cada coluna
# ATENÇÃO: TotalCharges aparece como "object" (texto) — isso é um erro que vamos corrigir!
df.info()

# Quantos clientes cancelaram?
churn_pct = df['Churn'].value_counts(normalize=True) * 100
print(churn_pct)
# No  → 73.5%
# Yes → 26.5%

Só 26,5% dos clientes cancelaram. Isso significa que o dataset é desbalanceado — e precisamos cuidar disso na hora de dividir o treino e o teste.

O que descobrimos? 7.043 clientes, 26,5% com churn, e TotalCharges está como texto quando devia ser número.

Passo 2

Preparar os dados

A rede neural só entende números. Precisamos converter textos em números e colocar tudo na mesma escala — como se fossem notas de 0 a 10 em vez de valores brutos.

# Corrige TotalCharges: converte texto para número
# errors='coerce' → se encontrar espaço em branco, vira NaN (nulo)
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')

# Separa o target (y) das features (X)
y = (df['Churn'] == 'Yes').astype(int).values
# True/False vira 1/0:  cancelou=1, ficou=0

X = df.drop(columns=['Churn']).copy()
# Removemos a coluna Churn de X — ela é a resposta, não uma pista
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# Divide: 80% treino, 20% teste
# stratify=y → mantém a proporção de 26,5% de churn nos dois grupos
X_train, X_test, y_train, y_test = train_test_split(
    X.values, y, test_size=0.20, random_state=42, stratify=y
)

# Normalização: coloca tudo na mesma escala (média 0, desvio padrão 1)
# Sem isso, "tenure" (0-72) domina sobre "SeniorCitizen" (0 ou 1)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)   # só aplica a escala do treino, não recalcula

O que descobrimos? Treino com 5.634 clientes, teste com 1.409. A normalização é essencial para a rede neural convergir rápido e de forma estável.

Passo 3

Treinar o modelo base (MLP)

Uma rede MLP é como uma série de filtros. Cada camada processa a informação e passa para a próxima — até chegar na resposta. Pensa num funil de decisão com várias etapas.

from sklearn.neural_network import MLPClassifier

mlp = MLPClassifier(
    # ARQUITETURA: 2 camadas ocultas
    # Primeira com 64 neurônios, segunda com 32
    hidden_layer_sizes=(64, 32),

    # FUNÇÃO DE ATIVAÇÃO: ReLU
    # Se o valor for negativo → vira 0; se for positivo → fica igual
    # Isso permite que a rede aprenda padrões não-lineares
    activation='relu',

    # OTIMIZADOR: adam → ajusta os pesos automaticamente
    solver='adam',

    max_iter=300,

    # EARLY STOPPING: para o treino quando a validação não melhora mais
    early_stopping=True,
    validation_fraction=0.15,

    random_state=42
)

# .fit() = momento em que a rede APRENDE com os 5.634 exemplos
mlp.fit(X_train, y_train)
print('Treinamento concluído!')

O que descobrimos? O Early Stopping interrompeu o treino automaticamente antes de memorizar demais. Isso é fundamental para um modelo que generaliza bem.

Passo 4

Analisar curvas e comparar com modelo regularizado

O gráfico de loss mostra se a rede aprendeu bem. Se a linha de treino cai mas a de validação sobe, a rede está "decorando" — isso é overfitting.

# .loss_curve_ → loss a cada época (queremos que caia)
train_loss = mlp.loss_curve_
val_scores = mlp.validation_scores_
epochs = range(1, len(train_loss) + 1)

fig, axes = plt.subplots(1, 2, figsize=(13, 4))
axes[0].plot(epochs, train_loss)
axes[0].set_title('Curva de Loss (Treino)')
axes[1].plot(epochs, val_scores, 'g')
axes[1].set_title('Acurácia de Validação')
plt.tight_layout()
plt.show()
# Modelo regularizado: adiciona penalização L2 nos pesos
# alpha=0.001 = freio leve — a rede aprende padrões mais gerais
mlp_reg = MLPClassifier(
    hidden_layer_sizes=(64, 32),
    activation='relu',
    solver='adam',
    max_iter=300,
    alpha=0.001,          # Regularização L2
    early_stopping=True,
    validation_fraction=0.15,
    n_iter_no_change=15,
    random_state=42
)
mlp_reg.fit(X_train, y_train)
from sklearn.metrics import classification_report, ConfusionMatrixDisplay

y_pred = mlp_reg.predict(X_test)
print(classification_report(y_test, y_pred, target_names=['Não Churn', 'Churn']))

O que descobrimos? O modelo regularizado com L2 + Early Stopping apresentou curvas mais estáveis e F1-score mais equilibrado — especialmente na classe Churn (a que mais importa para o negócio).

Recursos

Código completo e referências

Notebook completo com todas as células, gráficos e outputs:

github.com/Jair-pc/puc-minas-arquiteturas_de_deep_learning

Dataset: IBM Telco Customer Churn — disponível publicamente no Kaggle e IBM Developer.

Estudo de caso Próximo: CNN