Anteriormente exploramos diversos modelos lineares para regressão e classificação. Analogamente, a Regressão Polinomial estende esses modelos lineares permitindo relacionamentos não-lineares entre as features e o target, enquanto mantém a linearidade nos parâmetros.
Conceito Fundamental da Regressão Polinomial
Primordialmente, a regressão polinomial transforma o problema original criando novas features que são potências polinomiais das features originais. Decerto, isso permite que modelos lineares capturem padrões não-lineares complexos sem abandonar a estrutura linear subjacente.
Conforme a documentação do scikit-learn, essa abordagem é implementada usando a classe PolynomialFeatures em conjunto com modelos lineares como LinearRegression, Ridge, ou Lasso.
Formulação Matemática
Para uma única feature x, um polinômio de grau d é dado por:
\(y = w_0 + w_1x + w_2x^2 + w_3x^3 + \cdots + w_dx^d + \epsilon\)Para múltiplas features, inclui-se termos de interação:
\(y = w_0 + \sum_{i=1}^n w_ix_i + \sum_{i=1}^n\sum_{j=i}^n w_{ij}x_ix_j + \sum_{i=1}^n w_{ii}x_i^2 + \cdots\)Onde os coeficientes w ainda são lineares, permitindo o uso de técnicas de regressão linear padrão.
Implementação no Scikit-learn
Atualmente, o scikit-learn oferece uma abordagem modular através de pipelines:
- PolynomialFeatures: Transforma features em features polinomiais
- LinearRegression, Ridge, Lasso: Modelos lineares aplicados às novas features
- Pipeline: Combina transformação e modelo em um único estimador
Parâmetros Principais do PolynomialFeatures
- degree: Grau do polinômio (ex: 2 para quadrático, 3 para cúbico)
- interaction_only: Se incluir apenas termos de interação
- include_bias: Se incluir termo de bias (intercept)
Exemplo Prático: Regressão Polinomial em Ação
Ademais, vejamos um exemplo completo demonstrando o uso da regressão polinomial:
|
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
''' Aplicação da Regressão Polinomial para Modelagem Não-Linear CÓDIGO CORRIGIDO - Problema com número de labels no gráfico ''' import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.pipeline import Pipeline from sklearn.model_selection import train_test_split, cross_val_score from sklearn.metrics import mean_squared_error, r2_score from sklearn.datasets import make_regression # Exemplo 1: Regressão Polinomial com uma feature print("=== REGRESSÃO POLINOMIAL - UMA FEATURE ===") # Gerar dados não-lineares np.random.seed(42) n_samples = 200 X_single = np.random.uniform(-3, 3, n_samples).reshape(-1, 1) y_single = 2 * X_single.ravel() - 0.5 * X_single.ravel()**2 + 0.1 * X_single.ravel()**3 + np.random.normal(0, 0.5, n_samples) print(f"Dimensões: X {X_single.shape}, y {y_single.shape}") # Dividir em treino e teste X_train_single, X_test_single, y_train_single, y_test_single = train_test_split( X_single, y_single, test_size=0.3, random_state=42) # Criar pipelines para diferentes graus polinomiais degrees = [1, 2, 3, 4, 5, 6] pipelines = {} scores = {} for degree in degrees: # Pipeline com regressão linear pipeline = Pipeline([ ('poly', PolynomialFeatures(degree=degree, include_bias=False)), ('linear', LinearRegression()) ]) pipeline.fit(X_train_single, y_train_single) y_pred = pipeline.predict(X_test_single) mse = mean_squared_error(y_test_single, y_pred) r2 = r2_score(y_test_single, y_pred) pipelines[degree] = pipeline scores[degree] = {'mse': mse, 'r2': r2} print(f"Grau {degree}: MSE = {mse:.4f}, R² = {r2:.4f}") # Exemplo 2: Regressão Polinomial com múltiplas features print(f"\n=== REGRESSÃO POLINOMIAL - MÚLTIPLAS FEATURES ===") # Gerar dataset com relacionamentos não-lineares X_multi, y_multi = make_regression(n_samples=1000, n_features=3, n_informative=3, noise=10, random_state=42) # Adicionar não-linearidade X_multi[:, 0] = X_multi[:, 0] + 0.1 * X_multi[:, 0]**2 X_multi[:, 1] = X_multi[:, 1] * X_multi[:, 2] # Termo de interação X_train_multi, X_test_multi, y_train_multi, y_test_multi = train_test_split( X_multi, y_multi, test_size=0.3, random_state=42) # Comparar diferentes abordagens models = { 'Linear': LinearRegression(), 'Poly2 + Linear': Pipeline([ ('poly', PolynomialFeatures(degree=2)), ('linear', LinearRegression()) ]), 'Poly2 + Ridge': Pipeline([ ('poly', PolynomialFeatures(degree=2)), ('ridge', Ridge(alpha=1.0)) ]), 'Poly3 + Linear': Pipeline([ ('poly', PolynomialFeatures(degree=3)), ('linear', LinearRegression()) ]) } multi_results = {} for name, model in models.items(): model.fit(X_train_multi, y_train_multi) y_pred = model.predict(X_test_multi) mse = mean_squared_error(y_test_multi, y_pred) r2 = r2_score(y_test_multi, y_pred) multi_results[name] = {'mse': mse, 'r2': r2} print(f"{name:15}: MSE = {mse:.2f}, R² = {r2:.4f}") # Encontrar o melhor grau por validação cruzada print(f"\n=== SELEÇÃO DO MELHOR GRAU ===") degree_range = range(1, 8) cv_scores = [] best_degree = 3 # Valor padrão caso haja problemas for degree in degree_range: try: pipeline = Pipeline([ ('poly', PolynomialFeatures(degree=degree)), ('linear', LinearRegression()) ]) scores_cv = cross_val_score(pipeline, X_single, y_single, cv=5, scoring='neg_mean_squared_error') cv_scores.append(-scores_cv.mean()) print(f"Grau {degree}: MSE CV = {-scores_cv.mean():.4f}") except Exception as e: cv_scores.append(float('inf')) print(f"Grau {degree}: Erro - {e}") if cv_scores and min(cv_scores) != float('inf'): best_degree = degree_range[np.argmin(cv_scores)] else: # Usar o grau com melhor performance no teste best_degree = min(scores.items(), key=lambda x: x[1]['mse'])[0] print(f"Melhor grau selecionado: {best_degree}") # Visualização dos resultados plt.figure(figsize=(18, 12)) # Gráfico 1: Comparação de diferentes graus polinomiais (uma feature) plt.subplot(3, 4, 1) x_plot = np.linspace(-3, 3, 300).reshape(-1, 1) plt.scatter(X_test_single, y_test_single, alpha=0.6, label='Dados de teste', color='gray') for degree in [1, 2, 3, 6]: y_plot = pipelines[degree].predict(x_plot) plt.plot(x_plot, y_plot, label=f'Grau {degree}', linewidth=2) plt.xlabel('Feature X') plt.ylabel('Target y') plt.title('Regressão Polinomial - Diferentes Graus') plt.legend() plt.grid(True, alpha=0.3) # Gráfico 2: MSE vs Grau do Polinômio plt.subplot(3, 4, 2) mses = [scores[degree]['mse'] for degree in degrees] plt.plot(degrees, mses, 'o-', linewidth=2, markersize=8) plt.xlabel('Grau do Polinômio') plt.ylabel('MSE') plt.title('Erro vs Complexidade do Modelo') plt.grid(True, alpha=0.3) # Gráfico 3: R² vs Grau do Polinômio plt.subplot(3, 4, 3) r2s = [scores[degree]['r2'] for degree in degrees] plt.plot(degrees, r2s, 's-', linewidth=2, markersize=8, color='green') plt.xlabel('Grau do Polinômio') plt.ylabel('R²') plt.title('R² vs Complexidade do Modelo') plt.grid(True, alpha=0.3) # Gráfico 4: Número de features geradas plt.subplot(3, 4, 4) n_features_generated = [] for degree in degrees: poly = PolynomialFeatures(degree=degree) poly.fit(X_train_single) n_features_generated.append(poly.transform(X_train_single).shape[1]) plt.plot(degrees, n_features_generated, '^-', linewidth=2, markersize=8, color='red') plt.xlabel('Grau do Polinômio') plt.ylabel('Número de Features') plt.title('Expansão de Features vs Grau') plt.grid(True, alpha=0.3) # Gráfico 5: Comparação de modelos (múltiplas features) plt.subplot(3, 4, 5) model_names = list(multi_results.keys()) mses_multi = [multi_results[name]['mse'] for name in model_names] plt.bar(model_names, mses_multi, alpha=0.7) plt.ylabel('MSE') plt.title('Comparação de Modelos - Múltiplas Features') plt.xticks(rotation=45) for i, v in enumerate(mses_multi): plt.text(i, v + 0.5, f'{v:.1f}', ha='center', va='bottom') # Gráfico 6: R² dos modelos (múltiplas features) plt.subplot(3, 4, 6) r2s_multi = [multi_results[name]['r2'] for name in model_names] plt.bar(model_names, r2s_multi, alpha=0.7, color='green') plt.ylabel('R²') plt.title('R² dos Modelos - Múltiplas Features') plt.xticks(rotation=45) for i, v in enumerate(r2s_multi): plt.text(i, v + 0.01, f'{v:.3f}', ha='center', va='bottom') # Gráfico 7: Validação cruzada para seleção do grau ótimo plt.subplot(3, 4, 7) if cv_scores and min(cv_scores) != float('inf'): plt.plot(degree_range, cv_scores, 'o-', linewidth=2) plt.axvline(x=best_degree, color='red', linestyle='--', label=f'Melhor grau: {best_degree}') plt.xlabel('Grau do Polinômio') plt.ylabel('MSE (Validação Cruzada)') plt.title('Seleção do Grau por Validação Cruzada') plt.legend() plt.grid(True, alpha=0.3) else: plt.text(0.5, 0.5, 'Validação cruzada\nnão disponível', ha='center', va='center', transform=plt.gca().transAxes) plt.title('Validação Cruzada - Erro') # Gráfico 8: Overfitting - Treino vs Teste plt.subplot(3, 4, 8) train_scores = [] test_scores = [] for degree in degrees: pipeline = Pipeline([ ('poly', PolynomialFeatures(degree=degree)), ('linear', LinearRegression()) ]) pipeline.fit(X_train_single, y_train_single) train_pred = pipeline.predict(X_train_single) test_pred = pipeline.predict(X_test_single) train_mse = mean_squared_error(y_train_single, train_pred) test_mse = mean_squared_error(y_test_single, test_pred) train_scores.append(train_mse) test_scores.append(test_mse) plt.plot(degrees, train_scores, 'o-', label='Treino', linewidth=2) plt.plot(degrees, test_scores, 's-', label='Teste', linewidth=2) plt.xlabel('Grau do Polinômio') plt.ylabel('MSE') plt.title('Overfitting: Treino vs Teste') plt.legend() plt.grid(True, alpha=0.3) # Gráfico 9: Regularização com Ridge (controle de overfitting) plt.subplot(3, 4, 9) alphas = [0.001, 0.01, 0.1, 1, 10, 100, 1000] degree_fixed = 6 # Grau alto que provavelmente overfita ridge_scores = [] for alpha in alphas: try: pipeline = Pipeline([ ('poly', PolynomialFeatures(degree=degree_fixed)), ('ridge', Ridge(alpha=alpha)) ]) scores_cv = cross_val_score(pipeline, X_single, y_single, cv=5, scoring='neg_mean_squared_error') ridge_scores.append(-scores_cv.mean()) except: ridge_scores.append(float('inf')) if ridge_scores and min(ridge_scores) != float('inf'): best_alpha = alphas[np.argmin(ridge_scores)] plt.semilogx(alphas, ridge_scores, 'o-', linewidth=2) plt.axvline(x=best_alpha, color='red', linestyle='--', label=f'Melhor alpha: {best_alpha}') plt.xlabel('Alpha (Regularização)') plt.ylabel('MSE (Validação Cruzada)') plt.title('Regularização Ridge - Controle de Overfitting') plt.legend() plt.grid(True, alpha=0.3) else: plt.text(0.5, 0.5, 'Regularização\nnão disponível', ha='center', va='center', transform=plt.gca().transAxes) plt.title('Regularização - Erro') # Gráfico 10: Features geradas pelo PolynomialFeatures (grau 2) plt.subplot(3, 4, 10) try: poly = PolynomialFeatures(degree=2, include_bias=False) X_poly = poly.fit_transform(X_multi[:5, :]) # Apenas primeiras 5 amostras plt.imshow(X_poly, aspect='auto', cmap='viridis') plt.colorbar() plt.xlabel('Features Polinomiais') plt.ylabel('Amostras') plt.title('Matriz de Features Polinomiais (Grau 2)') except: plt.text(0.5, 0.5, 'Visualização de features\nnão disponível', ha='center', va='center', transform=plt.gca().transAxes) plt.title('Features Polinomiais - Erro') # Gráfico 11: Importância dos termos polinomiais (CORRIGIDO) plt.subplot(3, 4, 11) try: best_pipeline = Pipeline([ ('poly', PolynomialFeatures(degree=best_degree)), ('linear', LinearRegression()) ]) best_pipeline.fit(X_train_single, y_train_single) coefficients = best_pipeline.named_steps['linear'].coef_ # CORREÇÃO: Garantir que o número de labels corresponde ao número de coeficientes n_coefficients = len(coefficients) feature_names = [f'x^{i+1}' for i in range(n_coefficients)] plt.bar(range(n_coefficients), coefficients, alpha=0.7) plt.axhline(y=0, color='black', linestyle='-', alpha=0.5) plt.xlabel('Termo Polinomial') plt.ylabel('Valor do Coeficiente') plt.title(f'Coeficientes (Grau {best_degree})') # CORREÇÃO: Usar apenas se o número de coeficientes for razoável para visualização if n_coefficients <= 10: plt.xticks(range(n_coefficients), feature_names, rotation=45) else: plt.xticks(range(0, n_coefficients, max(1, n_coefficients//10)), rotation=45) except Exception as e: plt.text(0.5, 0.5, f'Erro nos coeficientes:\n{str(e)}', ha='center', va='center', transform=plt.gca().transAxes, fontsize=8) plt.title('Coeficientes - Erro') # Gráfico 12: Resíduos do modelo polinomial plt.subplot(3, 4, 12) try: best_model = pipelines.get(best_degree, pipelines[3]) # Fallback para grau 3 y_pred_best = best_model.predict(X_test_single) residuals = y_test_single - y_pred_best plt.scatter(y_pred_best, residuals, alpha=0.6) plt.axhline(y=0, color='red', linestyle='--', alpha=0.7) plt.xlabel('Valores Preditos') plt.ylabel('Resíduos') plt.title('Análise de Resíduos - Modelo Polinomial') plt.grid(True, alpha=0.3) except: plt.text(0.5, 0.5, 'Análise de resíduos\nnão disponível', ha='center', va='center', transform=plt.gca().transAxes) plt.title('Resíduos - Erro') plt.tight_layout() plt.show() # Análise detalhada das features polinomiais print(f"\n=== ANÁLISE DETALHADA DAS FEATURES POLINOMIAIS ===") # Mostrar features geradas para grau 2 com 2 features de exemplo try: X_example = np.array([[1, 2], [3, 4]]) poly = PolynomialFeatures(degree=2, include_bias=True) X_poly_example = poly.fit_transform(X_example) print(f"\nExemplo de expansão polinomial (grau 2):") print(f"Features originais:\n{X_example}") print(f"Features polinomiais (com bias):") print(f"Dimensões: {X_poly_example.shape}") print(f"Número de features geradas: {X_poly_example.shape[1]}") except Exception as e: print(f"Erro na análise de features: {e}") # Análise do melhor modelo print(f"\n=== MELHOR MODELO ENCONTRADO ===") print(f"Melhor grau polinomial: {best_degree}") print(f"MSE do melhor modelo: {scores[best_degree]['mse']:.4f}") print(f"R² do melhor modelo: {scores[best_degree]['r2']:.4f}") # Comparação com modelo linear simples try: linear_model = LinearRegression() linear_model.fit(X_train_single, y_train_single) y_pred_linear = linear_model.predict(X_test_single) mse_linear = mean_squared_error(y_test_single, y_pred_linear) improvement = (mse_linear - scores[best_degree]['mse']) / mse_linear * 100 print(f"Melhoria sobre modelo linear: {improvement:.1f}%") except: print("Não foi possível calcular a melhoria sobre modelo linear") # Resumo final print(f"\n=== RESUMO FINAL ===") print(f"A regressão polinomial mostrou melhor performance que o modelo linear") print(f"O grau {best_degree} apresentou o melhor equilíbrio entre bias e variância") print(f"Expansão polinomial é eficaz para capturar relacionamentos não-lineares") |
Vantagens da Regressão Polinomial
Embora simples, a regressão polinomial oferece benefícios significativos:
Vantagens Principais
- Flexibilidade: Captura relacionamentos não-lineares complexos
- Interpretabilidade: Mantém a estrutura linear nos parâmetros
- Compatibilidade: Funciona com todas técnicas de regressão linear
- Simplicidade: Fácil implementação e entendimento
Desafios e Considerações
Algumas considerações importantes para uso eficaz:
Desafios
- Maldição da dimensionalidade: Número de features cresce rapidamente com o grau
- Overfitting: Graus muito altos podem levar a sobreajuste
- Instabilidade numérica: Features com escalas muito diferentes
- Interpretabilidade: Coeficientes podem perder significado direto
Boas Práticas
- Use validação cruzada para selecionar o grau ótimo
- Aplique regularização (Ridge, Lasso) para controlar overfitting
- Normalize as features antes da expansão polinomial
- Considere interaction_only=True para evitar termos de alta ordem
Casos de Uso Recomendados
A regressão polinomial é particularmente eficaz em:
-
- Problemas com relacionamentos não-lineares conhecidos: Como física, engenharia
Quando interpretabilidade é importante: Mas relações lineares não são suficientes
- Baseline para modelos não-lineares: Ponto de partida antes de métodos mais complexos
- Dados com padrões curvilíneos: Como crescimento, decaimento, relações quadráticas
Enfim, a regressão polinomial representa uma ponte elegante entre modelos lineares simples e métodos não-lineares complexos, oferecendo flexibilidade adicional enquanto mantém a interpretabilidade e as ferramentas analíticas da regressão linear.