Machine Learning: KNN e Agrupamento

Última atualização: 2026-08-26

Pássaros da mesma plumagem andam juntos — KNN encontra "os vizinhos mais parecidos com você" para classificação, enquanto K-Means encontra "os grupos mais coesos" para segmentação.

1. O que você vai aprender


2. A história real de um gerente de operações de usuários

(1) O problema: marketing único para 500 mil usuários é ineficiente

A plataforma do Bob tem 500 mil usuários, e cada usuário recebe os mesmos e-mails promocionais. O resultado: usuários de alto valor acham as promoções muito básicas, usuários dormentes nunca abrem os e-mails, e a taxa de conversão geral é de apenas 2%. Os usuários são diversos — uma estratégia única para todos desperdiça 80% do orçamento de marketing.

(2) A solução com agrupamento

O agrupamento pode dividir automaticamente os usuários em grupos de alto valor/ativos/dormentes/em churn, permitindo marketing de precisão.

PYTHON
from sklearn.cluster import KMeans

# Agrupamento RFM: agrupar usuários por Recência, Frequência, Valor Monetário
rfm = df[["recency", "frequency", "monetary"]]
kmeans = KMeans(n_clusters=4, random_state=42)
df["segment"] = kmeans.fit_predict(rfm)

for i in range(4):
    segment = df[df["segment"] == i]
    print(f"Segmento {i}: {len(segment)} usuários, "
          f"Valor Monetário Médio={segment['monetary'].mean():.0f} USD")

(3) O resultado: marketing de precisão aumenta a conversão 4x

Depois que o Bob usou o agrupamento para dividir os usuários em 4 segmentos, o grupo de alto valor recebeu ofertas premium, o grupo dormente recebeu pacotes de reengajamento, e a taxa de conversão geral saltou de 2% para 8% — uma melhoria de 3x no ROI de marketing.


3. Classificação KNN

(1) Métricas de distância e escolha de K

▶ Exemplo: Classificação KNN + comparando diferentes valores de K

PYTHON
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

X, y = load_iris(return_X_y=True)

# Comparar diferentes valores de K
for k in [1, 3, 5, 7, 11, 21]:
    pipe = Pipeline([
        ("scaler", StandardScaler()),
        ("knn", KNeighborsClassifier(n_neighbors=k)),
    ])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
    print(f"K={k:2d}: Acurácia={scores.mean():.3f} +/- {scores.std():.3f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Métrica de distância Características da fórmula Melhor para
Euclidiana Distância em linha reta Features contínuas, todas as dimensões igualmente importantes
Manhattan Distância em quarteirões Dados de alta dimensionalidade, muitos outliers
Cosseno Distância angular Vetores de texto, direção importa mais que magnitude
Minkowski Distância generalizada (p=2→Euclidiana, p=1→Manhattan) Ajuste flexível

(2) O efeito de K

Valor de K Fronteira de decisão Risco
K=1 Extremamente irregular Sobreajuste (sensível a ruído)
K=pequeno (3-7) Moderadamente curva Geralmente ótimo
K=grande (20+) Quase linear Subajuste

▶ Exemplo: Previsão de compra de usuário com KNN

PYTHON
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import numpy as np

rng = np.random.default_rng(42)
n = 1000
X = np.column_stack([
    rng.uniform(0, 100, n),   # tempo_navegacao
    rng.uniform(0, 50, n),    # valor_carrinho_usd
    rng.integers(1, 30, n),   # paginas_vistas
    rng.integers(0, 10, n),   # compras_anteriores
])
y = (X[:, 1] * 0.05 + X[:, 3] * 0.3 + rng.normal(0, 0.5, n) > 2).astype(int)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("knn", KNeighborsClassifier(n_neighbors=7, weights="distance")),
])
pipe.fit(X_train, y_train)
print(classification_report(y_test, pipe.predict(X_test)))

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

4. Agrupamento K-Means

(1) Fluxo do algoritmo

100%
sequenceDiagram
    participant Init as Inicializar K Centros
    participant Assign as Atribuir Pontos
    participant Update as Atualizar Centros
    participant Check as Convergiu?

    Init->>Assign: Posições aleatórias dos K centros
    loop Até convergir
        Assign->>Update: Cada ponto → centro mais próximo
        Update->>Check: Centros = média dos pontos atribuídos
        Check->>Assign: Não convergiu (centros se moveram)
    end
    Check-->>Done: Convergiu! Retornar clusters

▶ Exemplo: Agrupamento K-Means + método do cotovelo

PYTHON
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
# Gerar 4 clusters naturais
X = np.vstack([
    rng.normal([20, 500], [3, 50], (200, 2)),   # Alto valor
    rng.normal([50, 200], [5, 30], (300, 2)),   # Médio
    rng.normal([80, 50], [8, 20], (350, 2)),    # Baixo
    rng.normal([10, 800], [2, 40], (150, 2)),   # VIP
])

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Método do cotovelo
inertias = []
K_range = range(2, 10)
for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(X_scaled)
    inertias.append(km.inertia_)

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(K_range, inertias, "bo-", linewidth=2)
ax.set_xlabel("Número de Clusters (K)")
ax.set_ylabel("Inércia (Soma de Quadrados Intra-Cluster)")
ax.set_title("Método do Cotovelo para K Ótimo")
ax.axvline(x=4, color="red", linestyle="--", label="Cotovelo em K=4")
ax.legend()
plt.tight_layout()
plt.savefig("elbow_method.png", dpi=150)

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

(2) Coeficiente de Silhueta

▶ Exemplo: Escolhendo K com o Coeficiente de Silhueta

PYTHON
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import numpy as np

# Usando o mesmo X_scaled do exemplo anterior
for k in range(2, 8):
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    labels = km.fit_predict(X_scaled)
    score = silhouette_score(X_scaled, labels)
    print(f"K={k}: Coeficiente de Silhueta={score:.3f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Métrica Significado Como determinar o K ótimo
Inércia (método do cotovelo) Soma de quadrados intra-cluster No ponto de inflexão
Coeficiente de Silhueta Coesão vs. separação No valor máximo
Gap Statistic Comparação contra distribuição aleatória Primeiro K abaixo do limite superior

5. Agrupamento por densidade DBSCAN

▶ Exemplo: DBSCAN descobre clusters de formatos arbitrários

PYTHON
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
import numpy as np

rng = np.random.default_rng(42)
# Criar clusters não esféricos (dois círculos)
from sklearn.datasets import make_circles
X, _ = make_circles(n_samples=500, factor=0.5, noise=0.05, random_state=42)

X_scaled = StandardScaler().fit_transform(X)

# K-Means vs DBSCAN
km = KMeans(n_clusters=2, random_state=42, n_init=10)
km_labels = km.fit_predict(X_scaled)

db = DBSCAN(eps=0.3, min_samples=10)
db_labels = db.fit_predict(X_scaled)

n_clusters = len(set(db_labels)) - (1 if -1 in db_labels else 0)
n_noise = list(db_labels).count(-1)

print(f"Silhueta K-Means: {silhouette_score(X_scaled, km_labels):.3f}")
print(f"Clusters DBSCAN: {n_clusters}, Pontos de ruído: {n_noise}")
if n_clusters > 1:
    db_valid = db_labels != -1
    print(f"Silhueta DBSCAN: {silhouette_score(X_scaled[db_valid], db_labels[db_valid]):.3f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Dimensão K-Means DBSCAN
Formato do cluster Esférico Formatos arbitrários
Valor de K Deve ser predefinido Descoberto automaticamente
Pontos de ruído Não tratados Rotulados como -1
Densidade desigual Desempenho ruim Ainda lida bem
Velocidade de computação Rápida Moderada

6. Segmentação por perfil RFM do Bob

▶ Exemplo: Projeto completo de agrupamento RFM

PYTHON
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np

rng = np.random.default_rng(42)
n = 5000
df = pd.DataFrame({
    "user_id": range(10001, 10001 + n),
    "recency_days": rng.integers(1, 365, n),
    "frequency": rng.integers(1, 50, n),
    "monetary_usd": rng.exponential(500, n),
})

# Features RFM
rfm = df[["recency_days", "frequency", "monetary_usd"]]

# Transformação log para features enviesadas
rfm_log = rfm.copy()
rfm_log["frequency"] = np.log1p(rfm_log["frequency"])
rfm_log["monetary_usd"] = np.log1p(rfm_log["monetary_usd"])

# Padronizar
scaler = StandardScaler()
rfm_scaled = scaler.fit_transform(rfm_log)

# K-Means com K=4
km = KMeans(n_clusters=4, random_state=42, n_init=10)
df["segment"] = km.fit_predict(rfm_scaled)

# Analisar segmentos
segment_summary = df.groupby("segment").agg({
    "recency_days": "mean",
    "frequency": "mean",
    "monetary_usd": "mean",
    "user_id": "count",
}).round(1)
segment_summary.columns = ["avg_recency", "avg_frequency", "avg_monetary", "count"]
segment_summary = segment_summary.sort_values("avg_monetary", ascending=False)

# Rotular segmentos
labels = ["Campeões", "Fiéis", "Em Risco", "Perdidos"]
for idx, (_, row) in enumerate(segment_summary.iterrows()):
    print(f"{labels[idx]:10s}: {int(row['count']):5d} usuários, "
          f"Recência={row['avg_recency']:.0f}d, "
          f"Freq={row['avg_frequency']:.1f}, "
          f"Monetário={row['avg_monetary']:.0f} USD")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Segmento Recência Frequência Monetário Estratégia de marketing
Campeões Baixa (ativos recentemente) Alta Alta Atendimento VIP, recomendações premium
Fiéis Média Média-alta Média Programas de fidelidade, cross-selling
Em Risco Alta (muito tempo sem comprar) Média Média Pacotes de reengajamento, ofertas por tempo limitado
Perdidos Muito alta Baixa Baixa Outreach de baixo custo, pesquisas

❓ Perguntas Frequentes

P: Qual a diferença entre K no KNN e K no K-Means? R: K no KNN significa "quantos vizinhos votam" — é um algoritmo de classificação. K no K-Means significa "quantos grupos formar" — é um algoritmo de agrupamento. São conceitos completamente diferentes.

P: KNN exige normalização? R: Com certeza. KNN é baseado em distância, então features não normalizadas (por exemplo, renda 0–100.000 vs. idade 0–100) dominarão completamente o cálculo da distância. Sempre use com StandardScaler.

P: Como escolher K para K-Means? R: Combine o método do cotovelo e o coeficiente de silhueta. O método do cotovelo encontra o ponto de inflexão; o coeficiente de silhueta encontra o máximo. Podem discordar — ceda à razoabilidade de negócio.

P: Como escolher eps para DBSCAN? R: Plote o gráfico de K-distance (distâncias dos k vizinhos mais próximos ordenadas) e encontre o cotovelo. Valores típicos de eps vão de 0,1–1,0 (após padronização). min_samples geralmente é definido como 2 × número de dimensões.

P: Como avaliar os resultados do agrupamento? R: O agrupamento não supervisionado não tem "verdade de campo". Use o coeficiente de silhueta (avaliação matemática) + interpretabilidade de negócio (avaliação de domínio) para validação dupla. Apenas clusters que mapeiam para conceitos de negócio significativos são valiosos.

P: Por que aplicar uma transformação log antes do agrupamento RFM? R: Frequência e Monetário normalmente têm distribuições enviesadas à direita — alguns usuários de alto valor esticam as distâncias entre clusters. Uma transformação log torna a distribuição mais simétrica, melhorando o desempenho do K-Means.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Use KNN para classificar a Iris. Compare a acurácia com validação cruzada para K=1, 5, 10, 20 e encontre o K ótimo. Dica: Pipeline(StandardScaler + KNN) + cross_val_score.
  2. Intermediário (Dificuldade ⭐⭐): Gere dados de 4 clusters com make_blobs. Agrupe com K-Means e DBSCAN e compare os coeficientes de silhueta e o tratamento de pontos de ruído. Dica: DBSCAN rotula ruído como -1.
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente o agrupamento RFM do Bob — gere dados simulados de usuários, aplique transformação log + padronização, escolha K com o método do cotovelo, execute K-Means, nomeie cada segmento de forma significativa (Campeões/Fiéis/Em Risco/Perdidos) e produza os valores médios de RFM por segmento. Dica: consulte o exemplo completo na Seção 6.

← Anterior: SVM Máquinas de Vetores de Suporte | Próximo: Engenharia de Features →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%