Expandindo os SVMs para Tarefas de Regressão
O SVR (Support Vector Regression) estende o conceito dos Support Vector Machines para problemas de regressão, mantendo a mesma filosofia de maximizar margens enquanto tolera pequenos erros através do parâmetro epsilon. Esta abordagem é particularmente eficaz para dados não lineares e com presença de outliers.
Fundamentos Matemáticos do SVR
Primeiramente, o SVR difere fundamentalmente da regressão tradicional por não buscar minimizar o erro quadrático, mas sim encontrar uma função que tenha no máximo epsilon desvio dos valores reais. A formulação matemática é expressa como:
\(\min_{w, b} \frac{1}{2} \|w\|^2 + C \sum_{i=1}^n (\xi_i + \xi_i^*)\)
sujeito a:
\(\begin{cases} y_i – (w \cdot \phi(x_i) + b) \leq \varepsilon + \xi_i \\ (w \cdot \phi(x_i) + b) – y_i \leq \varepsilon + \xi_i^* \\ \xi_i, \xi_i^* \geq 0 \end{cases}\)
onde ξ_i e ξ_i^* são variáveis de folga que permitem violações do tubo epsilon.
Parâmetros Principais do SVR
Certamente, entender os parâmetros específicos do SVR é essencial para seu uso eficaz:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
from sklearn.svm import SVR from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score from sklearn.preprocessing import StandardScaler # Criando dados de regressão X, y = make_regression(n_samples=1000, n_features=4, noise=10.0, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # Normalizando os dados (importante para SVR) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Inicializando SVR com parâmetros principais svr_model = SVR( kernel='rbf', # Tipo de kernel C=1.0, # Parâmetro de regularização epsilon=0.1, # Largura do tubo epsilon gamma='scale', # Parâmetro do kernel degree=3, # Grau para kernel polinomial coef0=0.0, # Termo independente shrinking=True, # Usar heurística shrinking tol=1e-3, # Tolerância cache_size=200, # Tamanho do cache verbose=False, max_iter=-1 # Iterações ilimitadas ) # Treinando o modelo svr_model.fit(X_train_scaled, y_train) # Fazendo predições e avaliando y_pred = svr_model.predict(X_test_scaled) mse = mean_squared_error(y_test, y_pred) r2 = r2_score(y_test, y_pred) print(f"MSE: {mse:.4f}") print(f"R²: {r2:.4f}") print(f"Número de vetores suporte: {len(svr_model.support_vectors_)}") print(f"Fração de vetores suporte: {len(svr_model.support_vectors_) / len(X_train):.2%}") |
O Papel do Parâmetro Epsilon
Conquanto o parâmetro C seja familiar dos SVMs de classificação, o epsilon é exclusivo do SVR e controla a largura do tubo dentro do qual nenhuma penalidade é aplicada. Esta característica é fundamental para o comportamento do modelo:
- Epsilon pequeno: Tubo estreito, modelo mais preciso mas potencialmente overfit
- Epsilon grande: Tubo largo, modelo mais robusto a ruídos
- Valor zero: Equivalente à regressão tradicional com função de perdo epsilon-insensitive
Efeito do Epsilon na Performance
Embora a teoria seja importante, decerto ver o efeito prático do parâmetro epsilon é crucial. Portanto, analisemos sistematicamente:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
import numpy as np import matplotlib.pyplot as plt from sklearn.svm import SVR from sklearn.datasets import make_regression from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler # Criando dados não lineares para demonstração np.random.seed(42) X_nonlinear = np.sort(5 * np.random.rand(200, 1), axis=0) y_nonlinear = np.sin(X_nonlinear).ravel() + np.random.normal(0, 0.1, X_nonlinear.shape[0]) # Testando diferentes valores de epsilon epsilon_values = [0.01, 0.05, 0.1, 0.2, 0.5, 1.0] results = [] scaler = StandardScaler() X_scaled = scaler.fit_transform(X_nonlinear) for epsilon in epsilon_values: svr = SVR(kernel='rbf', C=1.0, epsilon=epsilon, gamma='scale') # Validação cruzada scores = cross_val_score(svr, X_scaled, y_nonlinear, cv=5, scoring='neg_mean_squared_error') mean_mse = -scores.mean() # Treinando para análise detalhada svr.fit(X_scaled, y_nonlinear) n_sv = len(svr.support_vectors_) results.append({ 'epsilon': epsilon, 'mse': mean_mse, 'n_support_vectors': n_sv, 'fraction_support_vectors': n_sv / len(X_nonlinear) }) print(f"epsilon={epsilon}: MSE={mean_mse:.4f}, " f"Vetores Suporte={n_sv} ({n_sv/len(X_nonlinear):.1%})") # Visualizando os resultados plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot([r['epsilon'] for r in results], [r['mse'] for r in results], 'bo-') plt.xlabel('Epsilon') plt.ylabel('MSE') plt.title('Efeito do Epsilon no Erro') plt.subplot(1, 2, 2) plt.plot([r['epsilon'] for r in results], [r['n_support_vectors'] for r in results], 'ro-') plt.xlabel('Epsilon') plt.ylabel('Número de Vetores Suporte') plt.title('Efeito do Epsilon nos Vetores Suporte') plt.tight_layout() plt.show() |
Comparação com Outros Algoritmos de Regressão
Atualmente, muitos praticantes questionam quando o SVR é preferível sobre outros métodos de regressão. Aliás, cada abordagem tem suas vantagens específicas:
Análise Comparativa Prática
Enquanto algoritmos como Random Forest e Gradient Boosting são populares, igualmente importante é entender quando o SVR se destaca:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score import pandas as pd import numpy as np # Dataset para comparação X_comp, y_comp = make_regression(n_samples=500, n_features=5, noise=5.0, random_state=42) # Normalizando para SVR scaler_comp = StandardScaler() X_comp_scaled = scaler_comp.fit_transform(X_comp) # Modelos para comparação models = { 'SVR (RBF)': SVR(kernel='rbf', C=1.0, epsilon=0.1, gamma='scale'), 'SVR (Linear)': SVR(kernel='linear', C=1.0, epsilon=0.1), 'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42), 'Ridge Regression': Ridge(alpha=1.0) } comparison_results = [] for name, model in models.items(): if 'SVR' in name: X_used = X_comp_scaled else: X_used = X_comp # Validação cruzada mse_scores = -cross_val_score(model, X_used, y_comp, cv=5, scoring='neg_mean_squared_error') r2_scores = cross_val_score(model, X_used, y_comp, cv=5, scoring='r2') comparison_results.append({ 'Model': name, 'MSE Mean': mse_scores.mean(), 'MSE Std': mse_scores.std(), 'R2 Mean': r2_scores.mean(), 'R2 Std': r2_scores.std() }) # Criando DataFrame para análise df_comparison = pd.DataFrame(comparison_results) print("\nComparação de Algoritmos de Regressão:") print(df_comparison.round(4).to_string(index=False)) |
Casos de Uso Específicos do SVR
Surpreendentemente, o SVR brilha em situações específicas onde métodos tradicionais podem falhar:
- Dados com relações não lineares complexas
- Presença de outliers que não devem influenciar excessivamente o modelo
- Problemas com dimensionalidade moderada a alta
- Quando a interpretabilidade através de vetores suporte é desejável
Aplicação em Séries Temporais
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import numpy as np import matplotlib.pyplot as plt from sklearn.svm import SVR from sklearn.preprocessing import StandardScaler # Criando dados de série temporal def create_time_series_data(n_points=200): t = np.linspace(0, 4*np.pi, n_points) y = np.sin(t) + 0.3*np.sin(3*t) + 0.1*np.random.normal(size=n_points) return t.reshape(-1, 1), y # Preparando dados para previsão de série temporal def create_lagged_features(data, n_lags=5): X, y = [], [] for i in range(n_lags, len(data)): X.append(data[i-n_lags:i]) y.append(data[i]) return np.array(X), np.array(y) # Gerando e preparando dados t, y_ts = create_time_series_data(300) y_lagged, X_lagged = create_lagged_features(y_ts, n_lags=10) # Treinando SVR para previsão de série temporal scaler_ts = StandardScaler() X_lagged_scaled = scaler_ts.fit_transform(X_lagged.reshape(X_lagged.shape[0], -1)) svr_ts = SVR(kernel='rbf', C=1.0, epsilon=0.05, gamma='scale') svr_ts.fit(X_lagged_scaled[:-50], y_lagged[:-50]) # Treino com todas exceto últimas 50 # Previsões y_pred_ts = svr_ts.predict(X_lagged_scaled[-50:]) # Visualização plt.figure(figsize=(12, 6)) plt.plot(t[-50:], y_lagged[-50:], 'b-', label='Valores Reais', linewidth=2) plt.plot(t[-50:], y_pred_ts, 'r--', label='Previsões SVR', linewidth=2) plt.fill_between(t[-50:], y_pred_ts - 0.1, y_pred_ts + 0.1, alpha=0.2, color='red', label='Tubo Epsilon') plt.xlabel('Tempo') plt.ylabel('Valor') plt.title('Previsão de Série Temporal com SVR') plt.legend() plt.grid(True, alpha=0.3) plt.show() print(f"Número de vetores suporte: {len(svr_ts.support_vectors_)}") print(f"R² no conjunto de teste: {r2_score(y_lagged[-50:], y_pred_ts):.4f}") |
Otimização de Hiperparâmetros para SVR
Contudo, o desempenho do SVR depende criticamente da escolha adequada de hiperparâmetros. Assim, técnicas sistemáticas de otimização são essenciais:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV from sklearn.svm import SVR from sklearn.datasets import make_regression import numpy as np # Dataset para tuning X_tune, y_tune = make_regression(n_samples=800, n_features=4, noise=8.0, random_state=42) # Normalizando scaler_tune = StandardScaler() X_tune_scaled = scaler_tune.fit_transform(X_tune) # Busca em Grade para SVR param_grid_svr = { 'C': [0.1, 1, 10, 100], 'epsilon': [0.01, 0.1, 0.2, 0.5], 'kernel': ['linear', 'rbf', 'poly'], 'gamma': ['scale', 'auto', 0.1, 1], 'degree': [2, 3] # Para kernel poly } svr_tune = SVR() grid_search_svr = GridSearchCV(svr_tune, param_grid_svr, cv=5, scoring='neg_mean_squared_error', n_jobs=-1, verbose=1) grid_search_svr.fit(X_tune_scaled, y_tune) print("Melhores parâmetros SVR:", grid_search_svr.best_params_) print("Melhor MSE:", -grid_search_svr.best_score_) # Busca aleatória para espaços maiores param_dist_svr = { 'C': np.logspace(-2, 3, 20), 'epsilon': np.logspace(-3, 0, 20), 'gamma': np.logspace(-3, 2, 20) } random_search_svr = RandomizedSearchCV(SVR(kernel='rbf'), param_dist_svr, n_iter=50, cv=5, scoring='neg_mean_squared_error', random_state=42, n_jobs=-1) random_search_svr.fit(X_tune_scaled, y_tune) print("\nMelhores parâmetros (Randomized):", random_search_svr.best_params_) print("Melhor MSE (Randomized):", -random_search_svr.best_score_) |
Limitações e Considerações Práticas
Inegavelmente, o SVR tem suas limitações. Então, é importante considerar:
- Requer normalização/scale dos dados para melhor performance
- Computacionalmente intensivo para grandes conjuntos de dados
- Sensível à escolha de kernel e parâmetros
- Menos interpretável que modelos lineares simples
- O parâmetro epsilon pode ser contra-intuitivo para iniciantes
Tratamento de Dados em Grande Escala
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
from sklearn.svm import LinearSVR import time # Para dados muito grandes, considerar LinearSVR X_large, y_large = make_regression(n_samples=10000, n_features=20, noise=5.0, random_state=42) # Comparando SVR linear vs LinearSVR scaler_large = StandardScaler() X_large_scaled = scaler_large.fit_transform(X_large) # SVR com kernel linear start_time = time.time() svr_linear = SVR(kernel='linear', C=1.0, epsilon=0.1) svr_linear.fit(X_large_scaled, y_large) svr_time = time.time() - start_time # LinearSVR (otimizado para problemas lineares) start_time = time.time() linear_svr = LinearSVR(C=1.0, epsilon=0.1, random_state=42, max_iter=1000) linear_svr.fit(X_large_scaled, y_large) linear_svr_time = time.time() - start_time print(f"SVR (linear kernel): {svr_time:.2f} segundos") print(f"LinearSVR: {linear_svr_time:.2f} segundos") print(f"LinearSVR é {svr_time/linear_svr_time:.1f}x mais rápido") # Comparando performance svr_score = svr_linear.score(X_large_scaled, y_large) linear_svr_score = linear_svr.score(X_large_scaled, y_large) print(f"\nSVR R²: {svr_score:.4f}") print(f"LinearSVR R²: {linear_svr_score:.4f}") |
Conclusão e Recomendações Práticas
Enfim, o SVR oferece uma abordagem robusta e flexível para problemas de regressão, especialmente quando relações não lineares estão presentes. Inegavelmente, seu maior valor está na capacidade de modelar padrões complexos enquanto mantém resistência a outliers através do parâmetro epsilon.
Afinal, a escolha do SVR sobre outros métodos frequentemente se justifica quando a natureza dos dados exige modelagem não linear e quando a presença de ruídos não deve dominar o modelo. Eventualmente, o esforço adicional no tuning de parâmetros é recompensado por performance superior em cenários específicos.
Portanto, considere o SVR para problemas de regressão complexos, especialmente quando métodos lineares falham em capturar os padrões subjacentes. Inclusive para aplicações onde o conceito de vetores suporte oferece insights valiosos sobre a estrutura dos dados.