Objetivo
Entender o que é Machine Learning, seus tipos, terminologia fundamental e o fluxo completo de um projeto de ML. Introduzir o Scikit-learn como framework padrão e implementar os primeiros modelos simples para solidificar os conceitos.
1. O que é Machine Learning?
Machine Learning é um subcampo da Inteligência Artificial onde sistemas aprendem padrões a partir de dados, sem serem explicitamente programados para cada situação.
A diferença fundamental em relação à programação tradicional:
Programação tradicional:
Regras + Dados → Respostas
Machine Learning:
Dados + Respostas → Regras (o modelo aprende as regras)
Exemplo concreto: detectar spam no email.
Programação tradicional: você escreve regras manualmente como "se contém 'ganhe dinheiro' e 'clique aqui' então é spam". Frágil, porque spammers mudam as palavras.
Machine Learning: você fornece milhares de emails marcados como spam ou não spam. O algoritmo aprende sozinho quais padrões distinguem os dois, incluindo padrões que você nunca pensaria em escrever manualmente.
2. Tipos de Machine Learning
2.1 Aprendizado Supervisionado
O algoritmo aprende a partir de exemplos rotulados — cada exemplo de treino tem uma resposta correta associada.
Entrada: (features, label)
Objetivo: aprender a mapear features → label para novos exemplos
Exemplos:
- Prever preço de imóveis (features: tamanho, localização → label: preço)
- Classificar email como spam ou não (features: palavras → label: spam/não spam)
- Diagnóstico médico (features: exames → label: doença/saudável)
- Reconhecimento de imagem (features: pixels → label: gato/cachorro/etc)
Divide-se em: - Regressão: label é um valor contínuo (preço, temperatura, probabilidade) - Classificação: label é uma categoria (spam/não spam, gato/cachorro, 0/1/2)
2.2 Aprendizado Não Supervisionado
O algoritmo aprende padrões em dados sem rótulos. Não há resposta correta.
Entrada: apenas features (sem labels)
Objetivo: descobrir estrutura, padrões ou representações nos dados
Exemplos:
- Clustering: agrupar clientes por comportamento de compra
- Redução de dimensionalidade: comprimir features mantendo informação
- Detecção de anomalias: encontrar transações suspeitas
- Geração de dados: criar imagens realistas (GANs)
2.3 Aprendizado por Reforço
Um agente aprende a tomar decisões interagindo com um ambiente, recebendo recompensas ou penalidades pelas suas ações.
Agente → Ação → Ambiente → Recompensa/Penalidade → Agente (aprende)
Exemplos:
- Jogar videogames (AlphaGo, OpenAI Five)
- Robótica (aprender a caminhar)
- Trading algorítmico
- Sistemas de recomendação
3. Terminologia Fundamental
import numpy as np
import pandas as pd
# Dataset de exemplo: prever se um cliente vai cancelar o serviço (churn)
dados = {
"idade": [25, 45, 35, 52, 28],
"renda": [3000, 8000, 5000, 12000, 2500],
"tempo_cliente": [12, 60, 36, 84, 6],
"satisfacao": [4, 2, 3, 5, 1],
"churn": [0, 1, 0, 0, 1] # 0=ficou, 1=cancelou
}
df = pd.DataFrame(dados)
print(df)
Terminologia:
- Dataset (conjunto de dados): a tabela inteira com todos os exemplos
- Exemplo / Instância / Amostra: uma linha da tabela (um cliente)
- Feature / Atributo / Variável independente: uma coluna de entrada (idade, renda...)
- Label / Target / Variável dependente: o que queremos prever (churn)
- Matriz X: todas as features, shape (n_exemplos, n_features)
- Vetor y: todos os labels, shape (n_exemplos,)
# Separando features e target
X = df[["idade", "renda", "tempo_cliente", "satisfacao"]].values
y = df["churn"].values
print(f"X shape: {X.shape}") # (5, 4) — 5 exemplos, 4 features
print(f"y shape: {y.shape}") # (5,)
print(f"\nX:\n{X}")
print(f"\ny: {y}")
4. O Fluxo Completo de um Projeto de ML
1. Definição do problema
└── O que queremos prever? Que tipo de problema é?
2. Coleta de dados
└── Fontes, volume, qualidade
3. Exploração e análise (EDA)
└── Distribuições, correlações, outliers, ausentes
4. Pré-processamento
└── Limpeza, encoding, normalização
5. Divisão treino/teste
└── Garantir que avaliamos em dados nunca vistos
6. Escolha e treinamento do modelo
└── Selecionar algoritmo, ajustar hiperparâmetros
7. Avaliação
└── Métricas no conjunto de teste
8. Deploy
└── Colocar em produção para uso real
5. Divisão Treino, Validação e Teste
Este é um dos conceitos mais importantes em ML. O objetivo é avaliar o modelo em dados que ele nunca viu durante o treinamento.
from sklearn.model_selection import train_test_split
import numpy as np
np.random.seed(42)
# Dataset simulado
n = 1000
X = np.random.randn(n, 5)
y = (X[:, 0] + X[:, 1] > 0).astype(int)
# Divisão simples: 80% treino, 20% teste
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # garante proporção igual de classes em treino e teste
)
print(f"Treino: {X_train.shape[0]} exemplos ({len(X_train)/n:.0%})")
print(f"Teste: {X_test.shape[0]} exemplos ({len(X_test)/n:.0%})")
print(f"\nProporção da classe 1 no treino: {y_train.mean():.3f}")
print(f"Proporção da classe 1 no teste: {y_test.mean():.3f}")
Saída:
Treino: 800 exemplos (80%)
Teste: 200 exemplos (20%)
Proporção da classe 1 no treino: 0.499
Proporção da classe 1 no teste: 0.500
# Divisão com validação: 70% treino, 15% validação, 15% teste
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.15, random_state=42, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.15/0.85, random_state=42, stratify=y_temp
)
print(f"Treino: {len(X_train)} exemplos")
print(f"Validação: {len(X_val)} exemplos")
print(f"Teste: {len(X_test)} exemplos")
Por que três conjuntos: - Treino: o modelo aprende com esses dados - Validação: usado para ajustar hiperparâmetros e escolher o melhor modelo - Teste: usado UMA ÚNICA VEZ no final para estimar a performance real
Se você usar o conjunto de teste para escolher hiperparâmetros, estará fazendo data leakage — o modelo indiretamente "vê" o teste durante o desenvolvimento.
6. Overfitting e Underfitting
Um dos conceitos mais importantes de ML.
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
np.random.seed(42)
# Dados reais: função quadrática com ruído
x_real = np.linspace(0, 10, 100)
y_real = 2 * x_real - 0.5 * x_real**2 + 8 + np.random.normal(0, 3, 100)
x_treino = x_real[:70]
y_treino = y_real[:70]
x_teste = x_real[70:]
y_teste = y_real[70:]
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
titulos = ["Underfitting\n(grau 1 — muito simples)",
"Bom ajuste\n(grau 2 — correto)",
"Overfitting\n(grau 15 — muito complexo)"]
graus = [1, 2, 15]
for ax, grau, titulo in zip(axes, graus, titulos):
modelo = make_pipeline(PolynomialFeatures(grau), LinearRegression())
modelo.fit(x_treino.reshape(-1, 1), y_treino)
x_plot = np.linspace(0, 10, 300).reshape(-1, 1)
y_plot = modelo.predict(x_plot)
ax.scatter(x_treino, y_treino, s=20, alpha=0.6,
color="steelblue", label="Treino")
ax.scatter(x_teste, y_teste, s=20, alpha=0.6,
color="coral", label="Teste")
ax.plot(x_plot, y_plot, color="black", linewidth=2, label="Modelo")
ax.set_title(titulo)
ax.set_ylim(-20, 40)
ax.legend(fontsize=8)
plt.suptitle("Underfitting vs Bom Ajuste vs Overfitting", y=1.02)
plt.tight_layout()
plt.show()
Definições:
Underfitting: modelo muito simples, não captura os padrões nos dados. Alto erro no treino E no teste. Solução: usar modelo mais complexo, adicionar features.
Overfitting: modelo muito complexo, memoriza o ruído dos dados de treino. Baixo erro no treino, alto erro no teste. Solução: regularização, mais dados, simplificar o modelo.
Bom ajuste: modelo com complexidade certa, generaliza bem para novos dados. Erro razoável em treino e teste.
7. O Bias-Variance Tradeoff
Erro total = Bias² + Variance + Ruído irredutível
Bias (viés): erro por simplificação excessiva do modelo
→ Alto bias = underfitting
Variance (variância): sensibilidade a flutuações nos dados de treino
→ Alta variance = overfitting
O desafio: reduzir um tende a aumentar o outro
# Visualização conceitual do tradeoff
complexidades = np.linspace(1, 10, 100)
bias_squared = 1.5 / complexidades
variance = 0.05 * complexidades**1.8
erro_total = bias_squared + variance + 0.1 # 0.1 = ruído irredutível
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(complexidades, bias_squared, label="Bias²",
color="steelblue", linewidth=2)
ax.plot(complexidades, variance, label="Variance",
color="coral", linewidth=2)
ax.plot(complexidades, erro_total, label="Erro Total",
color="purple", linewidth=2.5, linestyle="--")
ax.axhline(y=0.1, color="gray", linestyle=":", label="Ruído irredutível")
otimo = np.argmin(erro_total)
ax.axvline(x=complexidades[otimo], color="green",
linestyle="--", alpha=0.7, label="Complexidade ótima")
ax.set_xlabel("Complexidade do Modelo")
ax.set_ylabel("Erro")
ax.set_title("Bias-Variance Tradeoff")
ax.legend()
ax.set_ylim(0, 3)
plt.tight_layout()
plt.show()
8. Introdução ao Scikit-learn
Scikit-learn é o framework padrão de ML em Python. Sua filosofia é uma API consistente para todos os algoritmos:
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
# A API do Scikit-learn segue sempre o mesmo padrão:
# 1. Instanciar o modelo com hiperparâmetros
modelo = LogisticRegression(C=1.0, max_iter=1000)
# 2. Treinar com .fit(X_train, y_train)
modelo.fit(X_train, y_train)
# 3. Predizer com .predict(X_test)
y_pred = modelo.predict(X_test)
# 4. Avaliar
acuracia = accuracy_score(y_test, y_pred)
print(f"Acurácia: {acuracia:.4f}")
Todos os modelos do Scikit-learn seguem exatamente esse padrão. Isso é deliberado — uma vez que você aprende a interface, sabe usar qualquer algoritmo.
8.1 Pré-processadores também seguem a mesma API
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Instanciar
scaler = StandardScaler()
# fit: calcula média e desvio padrão do treino
scaler.fit(X_train)
# transform: aplica a normalização
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test) # usa parâmetros do treino!
# fit_transform: fit + transform em um passo (só no treino)
X_train_scaled = scaler.fit_transform(X_train)
print(f"Antes: média={X_train[:, 0].mean():.2f}, desvio={X_train[:, 0].std():.2f}")
print(f"Depois: média={X_train_scaled[:, 0].mean():.2f}, desvio={X_train_scaled[:, 0].std():.2f}")
Saída:
Antes: média=0.03, desvio=1.01
Depois: média=0.00, desvio=1.00
Por que aplicar o scaler do treino ao teste: se você recalculasse média e desvio usando o conjunto de teste, estaria "vazando" informação do teste para o modelo. O correto é aprender a normalização no treino e aplicar os mesmos parâmetros ao teste.
9. Primeiro Modelo Completo: K-Nearest Neighbors
KNN é um dos algoritmos mais intuitivos: para classificar um novo ponto, olhe para os K vizinhos mais próximos e vote na classe majoritária.
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
import numpy as np
np.random.seed(42)
# Dataset: classificar se cliente vai comprar produto premium
n = 500
X = np.column_stack([
np.random.normal(40, 12, n), # idade
np.random.normal(6000, 2000, n), # renda
np.random.randint(1, 6, n), # satisfacao
np.random.randint(1, 120, n) # tempo_cliente
]).astype(np.float32)
# Label: compra premium se renda alta E satisfacao alta
y = ((X[:, 1] > 6000) & (X[:, 2] >= 3)).astype(int)
# Adicionar algum ruído
ruido_idx = np.random.choice(n, 50, replace=False)
y[ruido_idx] = 1 - y[ruido_idx]
# Divisão treino/teste
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Normalização — MUITO importante para KNN (usa distância euclidiana)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
# Treinamento
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train_s, y_train)
# Predição e avaliação
y_pred = knn.predict(X_test_s)
acuracia = accuracy_score(y_test, y_pred)
print(f"Acurácia no teste: {acuracia:.4f}")
print(f"\nRelatório de classificação:")
print(classification_report(y_test, y_pred,
target_names=["Não Premium", "Premium"]))
Saída:
Acurácia no teste: 0.8400
Relatório de classificação:
precision recall f1-score support
Não Premium 0.87 0.88 0.87 60
Premium 0.80 0.78 0.79 40
accuracy 0.84 100
macro avg 0.83 0.83 0.83 100
weighted avg 0.84 0.84 0.84 100
10. Efeito do Hiperparâmetro K no KNN
# Testando diferentes valores de K
k_valores = range(1, 31)
acuracias_treino = []
acuracias_teste = []
for k in k_valores:
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train_s, y_train)
acc_treino = accuracy_score(y_train, knn.predict(X_train_s))
acc_teste = accuracy_score(y_test, knn.predict(X_test_s))
acuracias_treino.append(acc_treino)
acuracias_teste.append(acc_teste)
# Visualizar
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(k_valores, acuracias_treino, label="Treino",
color="steelblue", linewidth=2, marker="o", markersize=4)
ax.plot(k_valores, acuracias_teste, label="Teste",
color="coral", linewidth=2, marker="o", markersize=4)
melhor_k = k_valores[np.argmax(acuracias_teste)]
ax.axvline(x=melhor_k, color="green", linestyle="--",
label=f"Melhor K={melhor_k}")
ax.set_xlabel("Valor de K")
ax.set_ylabel("Acurácia")
ax.set_title("KNN: Efeito de K no Treino e Teste")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"Melhor K: {melhor_k}")
print(f"Melhor acurácia no teste: {max(acuracias_teste):.4f}")
Observe: - K=1: overfitting (100% no treino, pior no teste — memoriza cada ponto) - K grande: underfitting (muito suave, perde padrões locais) - K ótimo: em algum ponto intermediário
11. Métricas de Avaliação Introdutórias
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score,
confusion_matrix)
# Simulando predições
y_real = np.array([1, 0, 1, 1, 0, 1, 0, 0, 1, 0])
y_pred = np.array([1, 0, 1, 0, 0, 1, 1, 0, 1, 0])
# Acurácia: de tudo que foi classificado, quanto acertou?
print(f"Acurácia: {accuracy_score(y_real, y_pred):.4f}")
# Precisão: dos que o modelo disse ser positivo, quantos eram?
print(f"Precisão: {precision_score(y_real, y_pred):.4f}")
# Recall: dos positivos reais, quantos o modelo encontrou?
print(f"Recall: {recall_score(y_real, y_pred):.4f}")
# F1: média harmônica entre precisão e recall
print(f"F1-Score: {f1_score(y_real, y_pred):.4f}")
# Matriz de confusão
cm = confusion_matrix(y_real, y_pred)
print(f"\nMatriz de Confusão:")
print(cm)
print(f"\nVerdadeiros Positivos (VP): {cm[1,1]}")
print(f"Verdadeiros Negativos (VN): {cm[0,0]}")
print(f"Falsos Positivos (FP): {cm[0,1]}")
print(f"Falsos Negativos (FN): {cm[1,0]}")
Saída:
Acurácia: 0.8000
Precisão: 0.8000
Recall: 0.8000
F1-Score: 0.8000
Matriz de Confusão:
[[4 1]
[1 4]]
Verdadeiros Positivos (VP): 4
Verdadeiros Negativos (VN): 4
Falsos Positivos (FP): 1
Falsos Negativos (FN): 1
Quando usar cada métrica:
Acurácia: boa quando as classes são balanceadas. Enganosa com classes desbalanceadas.
Precisão: quando o custo de falsos positivos é alto. Ex: filtro de spam (não quero perder emails importantes).
Recall: quando o custo de falsos negativos é alto. Ex: diagnóstico de câncer (não quero perder casos reais).
F1: quando você quer um equilíbrio entre precisão e recall, especialmente com classes desbalanceadas.
12. Exemplo Completo: Pipeline de ML do Zero ao Resultado
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, f1_score, classification_report
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(42)
print("=" * 55)
print("PIPELINE COMPLETO DE ML — DETECÇÃO DE CHURN")
print("=" * 55)
# ── PASSO 1: Gerar dataset ──────────────────────────────
n = 800
df = pd.DataFrame({
"idade": np.random.randint(18, 70, n),
"renda": np.random.uniform(1500, 15000, n).round(2),
"satisfacao": np.random.randint(1, 6, n),
"tempo_cliente": np.random.randint(1, 120, n),
"num_produtos": np.random.randint(1, 6, n),
})
df["churn"] = (
(df["satisfacao"] <= 2) |
((df["renda"] < 3000) & (df["tempo_cliente"] < 12))
).astype(int)
ruido = np.random.choice(n, 80, replace=False)
df.loc[ruido, "churn"] = 1 - df.loc[ruido, "churn"]
print(f"\n[1] Dataset: {df.shape}")
print(f" Taxa de churn: {df['churn'].mean():.1%}")
# ── PASSO 2: Separar X e y ──────────────────────────────
features = ["idade", "renda", "satisfacao", "tempo_cliente", "num_produtos"]
X = df[features].values.astype(np.float32)
y = df["churn"].values
# ── PASSO 3: Dividir treino/teste ───────────────────────
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print(f"\n[2] Divisão treino/teste:")
print(f" Treino: {len(X_train)}, Teste: {len(X_test)}")
# ── PASSO 4: Normalizar ─────────────────────────────────
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
print(f"\n[3] Normalização aplicada (StandardScaler)")
# ── PASSO 5: Treinar múltiplos modelos ──────────────────
modelos = {
"KNN (K=5)": KNeighborsClassifier(n_neighbors=5),
"KNN (K=15)": KNeighborsClassifier(n_neighbors=15),
"Regressão Logística": LogisticRegression(max_iter=1000, random_state=42),
"Árvore de Decisão": DecisionTreeClassifier(max_depth=5, random_state=42),
}
print(f"\n[4] Resultados dos modelos:")
print(f" {'Modelo':<25} {'Acurácia':>10} {'F1-Score':>10}")
print(f" {'-'*47}")
resultados = {}
for nome, modelo in modelos.items():
modelo.fit(X_train_s, y_train)
y_pred = modelo.predict(X_test_s)
acc = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
resultados[nome] = {"acuracia": acc, "f1": f1, "modelo": modelo}
print(f" {nome:<25} {acc:>10.4f} {f1:>10.4f}")
# ── PASSO 6: Melhor modelo ──────────────────────────────
melhor_nome = max(resultados, key=lambda k: resultados[k]["f1"])
melhor = resultados[melhor_nome]
print(f"\n[5] Melhor modelo: {melhor_nome}")
print(f" Acurácia: {melhor['acuracia']:.4f}")
print(f" F1-Score: {melhor['f1']:.4f}")
y_pred_melhor = melhor["modelo"].predict(X_test_s)
print(f"\n[6] Relatório completo do melhor modelo:")
print(classification_report(y_test, y_pred_melhor,
target_names=["Ficou", "Cancelou"]))
# ── PASSO 7: Comparação visual ──────────────────────────
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
nomes = list(resultados.keys())
accs = [resultados[n]["acuracia"] for n in nomes]
f1s = [resultados[n]["f1"] for n in nomes]
cores = ["steelblue" if n != melhor_nome else "coral" for n in nomes]
axes[0].barh(nomes, accs, color=cores, alpha=0.85)
axes[0].set_xlim(0.5, 1.0)
axes[0].set_title("Acurácia por Modelo")
axes[0].set_xlabel("Acurácia")
for i, v in enumerate(accs):
axes[0].text(v + 0.002, i, f"{v:.4f}", va="center", fontsize=10)
axes[1].barh(nomes, f1s, color=cores, alpha=0.85)
axes[1].set_xlim(0.3, 1.0)
axes[1].set_title("F1-Score por Modelo")
axes[1].set_xlabel("F1-Score")
for i, v in enumerate(f1s):
axes[1].text(v + 0.002, i, f"{v:.4f}", va="center", fontsize=10)
plt.suptitle("Comparação de Modelos", fontsize=14)
plt.tight_layout()
plt.show()
Saída resumida:
===================================================
PIPELINE COMPLETO DE ML — DETECÇÃO DE CHURN
===================================================
[1] Dataset: (800, 6)
Taxa de churn: 32.4%
[2] Divisão treino/teste:
Treino: 640, Teste: 160
[3] Normalização aplicada (StandardScaler)
[4] Resultados dos modelos:
Modelo Acurácia F1-Score
───────────────────────────────────────────────
KNN (K=5) 0.8063 0.6809
KNN (K=15) 0.8125 0.6897
Regressão Logística 0.8313 0.7143
Árvore de Decisão 0.8438 0.7407
[5] Melhor modelo: Árvore de Decisão
Acurácia: 0.8438
F1-Score: 0.7407
Resumo da Aula
- ML é sobre aprender padrões a partir de dados em vez de programar regras manualmente
- Três tipos principais: supervisionado (com labels), não supervisionado (sem labels), por reforço (com recompensas)
- Terminologia: dataset, exemplo, feature, label, X (matriz de features), y (vetor de labels)
- Sempre divida os dados em treino e teste antes de qualquer processamento
- O scaler deve ser ajustado (fit) apenas no treino e aplicado (transform) no teste
- Overfitting: modelo memoriza o treino, generaliza mal. Underfitting: modelo muito simples
- Bias-variance tradeoff: reduzir bias tende a aumentar variance e vice-versa
- Scikit-learn tem API consistente: instanciar → fit → predict → avaliar
- KNN: classifica por vizinhança. K pequeno = overfitting, K grande = underfitting
- Acurácia é enganosa com classes desbalanceadas — prefira F1 nesses casos
- O fluxo completo é: dados → EDA → pré-processamento → divisão → treino → avaliação
Exercícios
-
Explique a diferença entre aprendizado supervisionado e não supervisionado. Classifique cada problema abaixo em qual tipo pertence e explique o motivo: a) Agrupar notícias por tema sem categorias predefinidas b) Prever o preço de ações com base em histórico c) Detectar transações fraudulentas com base em exemplos de fraudes passadas d) Reduzir um dataset de 100 features para 10 mantendo máxima variância
✓ Resposta:Aprendizado supervisionado usa dados rotulados — cada exemplo de treino tem uma resposta correta associada. O objetivo é aprender a mapear inputs para outputs específicos. Aprendizado não supervisionado não tem rótulos — o algoritmo descobre estruturas e padrões por conta própria.
a) Agrupar notícias por tema sem categorias predefinidas — não supervisionado (clustering). Não há labels de categorias, o algoritmo descobre os agrupamentos naturalmente.
b) Prever o preço de ações com base em histórico — supervisionado (regressão). O histórico fornece os valores passados como labels para aprender a relação temporal.
c) Detectar transações fraudulentas com exemplos de fraudes passadas — supervisionado (classificação binária). Cada transação histórica tem um label: fraude ou não fraude.
d) Reduzir um dataset de 100 features para 10 mantendo máxima variância — não supervisionado (redução de dimensionalidade, PCA). Não há labels envolvidos, apenas a estrutura dos dados.
-
Por que é errado aplicar
fit_transformdo StandardScaler no conjunto de teste? O que aconteceria se você fizesse isso? Dê um exemplo concreto onde esse erro causaria problema.✓ Resposta:Se você aplicar
fit_transformno conjunto de teste, estará calculando a média e o desvio padrão dos dados de teste e usando esses parâmetros para normalizar. Isso é errado porque você está usando informação do teste durante o processamento — o modelo indiretamente "viu" características estatísticas do teste.O correto é usar apenas os parâmetros calculados no treino (via
fit) para transformar tanto o treino quanto o teste (viatransform).Exemplo concreto onde o erro causaria problema: imagine um dataset de treino com rendas entre R$1.000 e R$10.000 (média R$5.500) e um conjunto de teste com rendas entre R$20.000 e R$50.000 (média R$35.000). Se você normalizar o teste com sua própria média, os valores normalizados parecerão similares aos do treino. Mas na realidade o perfil de renda é completamente diferente — e o modelo nunca aprendeu a lidar com rendas tão altas. Ao usar os parâmetros do treino para normalizar o teste, esses valores ficariam com z-scores muito altos, revelando corretamente que são outliers fora da distribuição do treino.
-
Explique overfitting e underfitting com uma analogia do cotidiano (não use exemplos de programação). Como você detectaria cada um ao analisar as métricas de treino e teste de um modelo?
✓ Resposta:Analogia com estudo para uma prova:
Underfitting é como um aluno que estudou muito superficialmente. Ele erra tanto as questões do simulado (treino) quanto as da prova real (teste). O problema não é memorização excessiva — é falta de aprendizado. Na prática, você detecta underfitting quando tanto a métrica de treino quanto a de teste são baixas e similares entre si.
Overfitting é como um aluno que decorou as respostas específicas do simulado em vez de entender os conceitos. Ele vai bem no simulado (treino) mas mal na prova real (teste), porque as questões são diferentes. Na prática, você detecta overfitting quando a métrica de treino é muito alta (próxima de 100%) e a métrica de teste é significativamente menor. A diferença grande entre treino e teste é o sinal clássico.
-
Dado o seguinte cenário: você treinou um modelo de diagnóstico de câncer. O modelo tem 98% de acurácia, mas o dataset tem 98% de exemplos negativos (sem câncer). O que isso significa? Qual métrica seria mais adequada nesse caso e por quê?
✓ Resposta:Isso significa que o modelo pode ter aprendido a simplesmente prever "sem câncer" para todos os pacientes e ainda assim atingir 98% de acurácia, pois 98% dos exemplos são negativos. Um modelo assim seria completamente inútil — ele nunca detectaria um caso real de câncer.
A métrica adequada seria Recall (sensibilidade), que mede: dos pacientes que realmente têm câncer, quantos o modelo identificou corretamente?
Um modelo que sempre diz "sem câncer" teria recall de 0% para a classe positiva, revelando imediatamente que é inútil para o objetivo.
Também seria importante analisar o F1-Score da classe positiva, que equilibra precisão e recall, e a matriz de confusão completa para ver exatamente quantos casos reais de câncer o modelo está perdendo (falsos negativos). Nesse contexto médico, falsos negativos têm custo muito maior que falsos positivos — perder um diagnóstico de câncer é muito mais grave do que gerar um alarme falso que será confirmado por exames posteriores.
-
No KNN, por que é obrigatório normalizar as features antes de treinar? O que aconteceria com um dataset onde uma feature é "renda" (valores entre 1000 e 50000) e outra é "satisfacao" (valores entre 1 e 5) se você não normalizasse?
✓ Resposta:O KNN calcula a distância euclidiana entre pontos para encontrar os vizinhos mais próximos. Se as features têm escalas muito diferentes, a feature com maior escala domina completamente o cálculo de distância, tornando as outras irrelevantes.
Com renda (1.000 a 50.000) e satisfação (1 a 5) sem normalização:
A diferença de renda entre dois clientes pode ser 30.000. A diferença máxima de satisfação é 4. Na distância euclidiana, √(30000² + 4²) ≈ 30000 — a satisfação contribui com menos de 0.0001% da distância. O modelo ignora completamente a satisfação e classifica os vizinhos apenas pela renda, mesmo que a satisfação seja o fator mais importante para prever churn.
Após normalização Z-score, ambas as features têm média 0 e desvio 1. Uma diferença de 1 unidade normalizada tem o mesmo peso para renda e para satisfação. O modelo passa a considerar ambas proporcionalmente.
-
Explique o conceito de data leakage (vazamento de dados). Dê dois exemplos concretos de como ele pode acontecer em um pipeline de ML e como cada um pode ser evitado.
✓ Resposta:Data leakage ocorre quando informação que não estaria disponível no momento da predição real "vaza" para o processo de treinamento, fazendo o modelo parecer melhor do que é na prática.
Exemplo 1 — Normalização com dados do teste: você calcula a média e desvio padrão de todo o dataset (treino + teste) antes de dividir, e usa esses parâmetros para normalizar. Isso faz com que os parâmetros de normalização sejam influenciados pelo teste. Evita-se dividindo o dataset primeiro e aplicando
fitapenas no treino.# ERRADO — leakage scaler.fit(X_total) # vê treino E teste X_train_s = scaler.transform(X_train) X_test_s = scaler.transform(X_test) # CORRETO — sem leakage scaler.fit(X_train) # vê apenas o treino X_train_s = scaler.transform(X_train) X_test_s = scaler.transform(X_test)Exemplo 2 — Feature que contém informação futura: em um modelo de previsão de churn mensal, você inclui como feature o "número de ligações para o suporte no mês do churn". Essa informação só existe depois que o churn aconteceu — você não a teria disponível no momento de fazer a predição. O modelo aprende a depender de um dado que não existirá na produção. Evita-se sendo cuidadoso com a ordem temporal dos dados e garantindo que todas as features usadas no treino sejam informações disponíveis antes do evento que você quer prever.
Referências
- Documentação oficial Scikit-learn
- Scikit-learn — guia do usuário
- Scikit-learn — KNeighborsClassifier
- Real Python — Supervised Learning with Scikit-learn
- Real Python — K-Nearest Neighbors
- Google ML Crash Course (gratuito)
- Hands-On Machine Learning with Scikit-learn, Keras and TensorFlow (Aurélien Géron)
- StatQuest — Bias and Variance