Machine Learning: Máquinas de Vetores de Suporte

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

O SVM encontra a "rua mais larga" — empurrando a fronteira de decisão o mais longe possível de ambas as classes, tornando-a naturalmente mais robusta.

1. O que você vai aprender


2. A história real de um analista de mercado

(1) O problema: fronteiras borradas nos níveis de valor dos clientes

Charlie é responsável pela segmentação de clientes no mercado europeu, dividindo os clientes em níveis de valor Alto/Médio/Baixo para alocar recursos de atendimento. Mas a fronteira entre Alto e Médio é difícil de traçar — usar uma única métrica (por exemplo, valor gasto) para classificar causa 20% dos clientes próximos à fronteira a serem frequentemente mal classificados. Uma fronteira linear não consegue capturar as relações complexas entre múltiplas dimensões do cliente.

(2) A solução com truque do kernel do SVM

O SVM usa o truque do kernel para mapear os dados em um espaço de dimensão superior, encontrando uma fronteira clara onde os dados antes eram inseparáveis.

PYTHON
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# O kernel RBF mapeia para um espaço de alta dimensão
pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("svm", SVC(kernel="rbf", C=1.0, gamma="scale")),
])
pipe.fit(X_train, y_train)
print(f"Acurácia: {pipe.score(X_test, y_test):.4f}")

(3) O resultado: a precisão em clientes de fronteira melhorou de 65% para 89%

Depois de substituir a classificação linear por SVM-RBF, Charlie viu a acurácia de classificação para clientes de fronteira saltar de 65% para 89%, reduzindo a alocação incorreta de recursos de atendimento em 40%.


3. Classificador de margem máxima

(1) Ideia central do SVM

O SVM encontra o hiperplano de separação que maximiza a margem. Apenas as amostras que estão nas fronteiras da margem (vetores de suporte) determinam o resultado da classificação.

100%
graph TB
    INPUT[Dados de Entrada] --> MAP[Mapeamento por Kernel<br/>Baixa-D → Alta-D]
    MAP --> HYPER[Encontrar Hiperplano<br/>de Margem Máxima]
    HYPER --> SV[Vetores de Suporte<br/>Definem a fronteira]
    SV --> MARGIN[Largura da Margem<br/>Maior = Mais Robusto]
    MARGIN --> CLASSIFY[Classificação<br/>Função de Decisão]

▶ Exemplo: Visualização do SVM linear

PYTHON
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

X, y = make_blobs(n_samples=100, centers=2, random_state=42, cluster_std=1.5)

# Treinar SVM com kernel linear
svm = SVC(kernel="linear", C=1.0)
svm.fit(X, y)

fig, ax = plt.subplots(figsize=(8, 6))

# Plotar pontos de dados
ax.scatter(X[:, 0], X[:, 1], c=y, cmap="bwr", s=30, edgecolors="black")

# Plotar fronteira de decisão e margens
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = svm.decision_function(xy).reshape(XX.shape)

ax.contour(XX, YY, Z, colors="k", levels=[-1, 0, 1], alpha=0.5, linestyles=["--", "-", "--"])

# Destacar vetores de suporte
ax.scatter(svm.support_vectors_[:, 0], svm.support_vectors_[:, 1],
           s=100, facecolors="none", edgecolors="k", linewidths=2)
ax.set_title(f"SVM Linear (C={svm.C})\nVetores de suporte: {len(svm.support_vectors_)}")
plt.tight_layout()
plt.savefig("svm_linear.png", dpi=150)

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

(2) Margem rígida vs. margem flexível (parâmetro C)

Valor de C Margem Tolerância a erros Risco de sobreajuste Caso de uso
Grande (100+) Estreita Quase nenhuma Alto Dados quase linearmente separáveis
Médio (1) Moderada Alguma tolerância Baixo Casos gerais
Pequeno (0,01) Larga Alta tolerância Baixo Dados ruidosos/com sobreposição

▶ Exemplo: Comparando o efeito do parâmetro C

PYTHON
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
from sklearn.model_selection import cross_val_score
import numpy as np

X, y = make_blobs(n_samples=200, centers=2, random_state=42, cluster_std=2.5)

for C in [0.01, 0.1, 1.0, 10.0, 100.0]:
    svm = SVC(kernel="rbf", C=C, gamma="scale")
    scores = cross_val_score(svm, X, y, cv=5, scoring="accuracy")
    svm.fit(X, y)
    print(f"C={C:6.2f}: Acurácia={scores.mean():.3f} +/- {scores.std():.3f}, "
          f"Vetores de suporte={len(svm.support_vectors_)}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

4. O truque do kernel

(1) Escolhendo uma função de kernel

▶ Exemplo: Comparando diferentes funções de kernel

PYTHON
from sklearn.svm import SVC
from sklearn.datasets import make_moons, make_circles
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

# Dados não lineares (luas)
X_moons, y_moons = make_moons(n_samples=500, noise=0.2, random_state=42)

kernels = {
    "Linear": SVC(kernel="linear", C=1),
    "Polinomial (grau=3)": SVC(kernel="poly", degree=3, C=1),
    "RBF": SVC(kernel="rbf", C=1, gamma="scale"),
}

for name, svm in kernels.items():
    pipe = Pipeline([("scaler", StandardScaler()), ("svm", svm)])
    scores = cross_val_score(pipe, X_moons, y_moons, cv=5, scoring="accuracy")
    print(f"{name:20s}: Acurácia={scores.mean():.3f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Kernel Fórmula Caso de uso Parâmetros-chave
Linear $\langle x, x' \rangle$ Alta dimensionalidade, separável linearmente C
RBF $e^{-\gamma|x-x'|^2}$ Uso geral, não linear C, gamma
Polinomial $(\langle x, x' \rangle + c)^d$ Interações entre features C, degree, coef0
Sigmoid $\tanh(\gamma\langle x, x' \rangle + c)$ Semelhante a rede neural C, gamma, coef0

(2) O parâmetro Gamma

▶ Exemplo: Efeito do Gamma no kernel RBF

PYTHON
from sklearn.svm import SVC
from sklearn.datasets import make_moons
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

X, y = make_moons(n_samples=500, noise=0.15, random_state=42)

for gamma in [0.01, 0.1, 1.0, 10.0, "scale", "auto"]:
    pipe = Pipeline([
        ("scaler", StandardScaler()),
        ("svm", SVC(kernel="rbf", C=1, gamma=gamma)),
    ])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
    print(f"gamma={str(gamma):6s}: Acurácia={scores.mean():.3f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Gamma Fronteira de decisão Efeito
Muito pequeno (0,01) Suave Subajuste
Moderado (1) Moderadamente curva Geralmente ótimo
Muito grande (10+) Altamente irregular Sobreajuste
"scale" 1/(n_features * X.var()) Padrão do sklearn
"auto" 1/n_features Padrão legado

5. Por que a normalização importa e SVR

(1) Sensibilidade do SVM às escalas das features

▶ Exemplo: Impacto da normalização no SVM

PYTHON
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

# Sem normalização
svm_raw = SVC(kernel="rbf", C=1, gamma="scale")
scores_raw = cross_val_score(svm_raw, X, y, cv=5, scoring="accuracy")

# Com normalização
pipe = Pipeline([("scaler", StandardScaler()), ("svm", SVC(kernel="rbf", C=1, gamma="scale"))])
scores_scaled = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")

print(f"Sem normalização: {scores_raw.mean():.4f}")
print(f"Com normalização:    {scores_scaled.mean():.4f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
⚠️ Nota: O SVM usa métricas de distância para calcular a margem. Se as escalas das features forem muito diferentes (por exemplo, idade 0–100 vs. renda 0–1.000.000), a feature de escala maior dominará o cálculo da margem. Sempre use SVM com StandardScaler.

(2) Regressão SVR

▶ Exemplo: Previsão de vendas com SVR

PYTHON
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score
import numpy as np

rng = np.random.default_rng(42)
X = rng.uniform(0, 100, (200, 3))
y = 50 + 0.8 * X[:, 0] + rng.normal(0, 5, 200)

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

# SVR com kernel RBF
pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("svr", SVR(kernel="rbf", C=100, epsilon=5)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)

print(f"SVR R²: {r2_score(y_test, y_pred):.4f}")
print(f"SVR MAE: {mean_absolute_error(y_test, y_pred):.2f}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso
Parâmetro Significado Efeito
C Força da regularização C grande → menor tolerância a erros → possível sobreajuste
epsilon (ε) Largura da banda insensível ε grande → mais pontos ignorados → ajuste mais suave
kernel Tipo de função de kernel Mesmo que o SVM de classificação

6. Segmentação de valor de clientes europeus do Charlie

▶ Exemplo: Projeto completo de classificação com SVM

PYTHON
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import classification_report
import pandas as pd
import numpy as np

rng = np.random.default_rng(42)
n = 2000
df = pd.DataFrame({
    "annual_spending_eur": rng.exponential(5000, n),
    "purchase_frequency": rng.poisson(8, n),
    "avg_order_value_eur": rng.exponential(150, n),
    "tenure_months": rng.integers(1, 60, n),
    "support_tickets": rng.poisson(3, n),
})

# Lógica de nível de valor (baseado em EUR)
score = (df["annual_spending_eur"] / 5000 * 0.3
         + df["purchase_frequency"] / 20 * 0.25
         + df["avg_order_value_eur"] / 200 * 0.2
         + df["tenure_months"] / 60 * 0.15
         + rng.normal(0, 0.1, n))

df["tier"] = pd.cut(score, bins=[0, 0.3, 0.6, 1.5], labels=["Baixo", "Médio", "Alto"])

X = df.drop(columns=["tier"])
y = df["tier"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# Grid search para os melhores parâmetros do SVM
pipe = Pipeline([("scaler", StandardScaler()), ("svm", SVC())])
param_grid = {
    "svm__kernel": ["linear", "rbf"],
    "svm__C": [0.1, 1, 10],
    "svm__gamma": ["scale", 0.1, 1],
}

grid = GridSearchCV(pipe, param_grid, cv=3, scoring="accuracy", n_jobs=-1)
grid.fit(X_train, y_train)

print(f"Melhores parâmetros: {grid.best_params_}")
print(f"Melhor acurácia na CV: {grid.best_score_:.4f}")
print(f"\nRelatório no teste:\n{classification_report(y_test, grid.predict(X_test))}")

Saída:

TEXT 📖 Somente leitura
# Executado com sucesso

❓ Perguntas Frequentes

P: Por que os dados do SVM precisam ser normalizados? R: O SVM calcula margens com base em métricas de distância. Se a feature A variar de 0 a 1 e a feature B variar de 0 a 1.000.000, B dominará completamente o cálculo da margem. O StandardScaler garante que todas as features contribuam igualmente.

P: Para o kernel RBF, o que importa mais — gamma ou C? R: Ambos são importantes e interagem entre si. Use GridSearchCV para pesquisá-los em conjunto. Intervalos de busca típicos: C=[0,1, 1, 10, 100], gamma=[0,001, 0,01, 0,1, 1, "scale"].

P: SVM é adequado para grandes conjuntos de dados? R: Não muito. A complexidade de treino do SVM é O(n²) a O(n³), tornando-o muito lento além de 100 mil amostras. Para grandes conjuntos de dados, use LinearSVC (kernel linear) ou Random Forest/XGBoost.

P: SVM consegue gerar probabilidades? R: Não por padrão. Definir probability=True treina um modelo adicional de Platt Scaling para gerar probabilidades, mas o treino fica mais lento e as probabilidades não são tão bem calibradas quanto as da regressão logística.

P: Qual a diferença entre um kernel linear e a regressão logística? R: Ambos são classificadores lineares. A diferença é que o SVM maximiza a margem (focando apenas nas amostras da fronteira), enquanto a regressão logística maximiza a verossimilhança (considerando todas as amostras). SVM é mais robusto perto da fronteira; a regressão logística gera probabilidades melhor calibradas.

P: Como escolher o parâmetro epsilon para SVR? R: Epsilon é a largura da banda insensível — erros menores que ε não são contabilizados na perda. Um valor típico é uma estimativa do nível de ruído da variável alvo. Por exemplo, se os erros de previsão de vendas estão dentro de ±5k USD, defina ε=5.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Use SVC(kernel="rbf") para classificar o conjunto de dados make_moons. Compare a acurácia com e sem StandardScaler. Dica: Pipeline vs. fit direto.
  2. Intermediário (Dificuldade ⭐⭐): Use GridSearchCV para procurar os parâmetros ótimos do SVM (kernel, C, gamma) no conjunto de dados Iris e encontre a melhor combinação. Dica: SVC dentro de um Pipeline + param_grid.
  3. Desafio (Dificuldade ⭐⭐⭐): Reproduza a segmentação de clientes do Charlie — gere dados de clientes com 3 classes, compare os relatórios de classificação de SVM/LogisticRegression/RandomForest, e analise qual modelo funciona melhor para cenários multiclasse com fronteiras borradas. Dica: use classification_report para comparar precisão/revocação/f1.

← Anterior: Árvores de Decisão e Florestas Aleatórias | Próximo: KNN e Agrupamento →

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%