O que é regressão linear e por que regularizar?
Primeiramente, imagine desenhar uma linha reta entre vários pontos. Por conseguinte, essa linha tenta prever um valor (como o preço de uma casa). Esse método chama-se regressão linear simples. Contudo, dados reais costumam ser bagunçados e cheios de variáveis. Assim, o modelo pode ficar complexo demais para os dados. Esse problema é conhecido como overfitting (sobreajuste). Para evitar isso, usamos técnicas de regularização. Dessa forma, elas aplicam “punições” aos coeficientes do modelo. Além disso, o modelo fica mais simples e generalizável. Três métodos populares são Ridge, Lasso e Elastic Net. Cada um age de maneira diferente nos coeficientes. Especialistas usam esses métodos em aprendizado de máquina. Seu principal benefício é melhorar previsões em novos dados. Portanto, a regularização é essencial para modelos confiáveis.
Ridge: encolhendo coeficientes sem zerar
Ridge é a primeira técnica que vamos explorar. Ela adiciona uma penalidade ao quadrado dos coeficientes. Os profissionais chamam essa penalidade de norma L2. O objetivo é encolher todos os coeficientes, mas sem zerá-los. Assim, nenhuma variável sai completamente do modelo. Isso é útil quando todas as variáveis importam um pouco. Por exemplo, dados genéticos ou de sensores se beneficiam disso. A força da penalidade depende de um parâmetro (alpha). Consequentemente, alpha maior produz coeficientes menores. Isso resulta em modelo mais simples e robusto. Uma desvantagem é que Ridge mantém variáveis irrelevantes. Elas ficam perto de zero, mas não exatamente zero. Portanto, isso pode dificultar a interpretação do modelo final. Ainda assim, Ridge evita overfitting severo com eficiência. Muitas situações práticas se beneficiam dessa abordagem.
Lasso: escolhendo variáveis importantes
Lasso usa uma penalidade diferente, chamada norma L1. Ela soma os valores absolutos dos coeficientes. O resultado é que alguns coeficientes viram exatamente zero. Desse modo, Lasso faz seleção automática de variáveis. Variáveis zeradas saem do modelo final completamente. Isso torna o modelo mais simples e fácil de explicar. Por exemplo, marketing com 100 possíveis canais de venda. Lasso descobre quais canais realmente influenciam as vendas. Contudo, Lasso tem uma limitação importante. Se duas variáveis forem muito correlacionadas, ele escolhe uma. A outra variável fica zerada, mesmo que ambas sejam úteis. Além disso, poucos dados podem deixar Lasso instável. A escolha do alpha também exige cuidado e testes. Apesar disso, Lasso é muito usado em dados de alta dimensão.
Elastic Net: o melhor dos dois mundos
Elastic Net combina Ridge e Lasso em um só método. Sua penalidade mistura norma L1 e norma L2. Dois parâmetros controlam essa mistura: alpha e l1ratio. O alpha controla a força total da regularização. Já o l1ratio decide quanto de L1 versus L2 usar. Quando l1ratio é 1, Elastic Net vira Lasso puro. Quando l1ratio é 0, Elastic Net vira Ridge puro. Valores intermediários trazem benefícios de ambos. Por exemplo, ele encolhe coeficientes como Ridge. Mas também zera variáveis menos importantes como Lasso. Além disso, Elastic Net funciona bem com variáveis correlacionadas. Ele mantém grupos de variáveis relevantes juntos normalmente. Por essa razão, os especialistas preferem esse método. Em muitos projetos, Elastic Net supera Ridge e Lasso. Portanto, sua flexibilidade representa uma grande vantagem prática.
Como aplicar esses métodos na prática
Primeiramente, padronize os dados antes de tudo. Isso significa colocar todas as variáveis na mesma escala. Caso contrário, as penalidades seriam aplicadas de forma injusta. Use StandardScaler da biblioteca scikit-learn para isso. Depois, divida os dados em treino e teste. A regularização depende apenas dos dados de treino. Escolha o parâmetro alpha (e l1_ratio) com cuidado. Uma técnica comum é a validação cruzada (cross-validation). Ela testa vários valores e seleciona o melhor. No scikit-learn, existem RidgeCV, LassoCV e ElasticNetCV. Essas classes já implementam a busca automática. Avalie modelos regularizados pelo R² ou MSE. Sempre compare com uma regressão linear sem regularização. Isso mostra se a regularização realmente ajudou. Por fim, interprete os coeficientes com atenção. Eles indicam o impacto de cada variável no resultado. Lembre-se: regularização não resolve todos os problemas. Dados mal coletados ou com poucos exemplos continuam sendo um desafio. Portanto, combine sempre boas práticas de coleta e limpeza. Com esses passos, você estará pronto para usar Ridge, Lasso e Elastic Net.
enunciado: previsão de eficiência energética
Você recebeu um conjunto de dados sobre edifícios. O objetivo é prever a carga de aquecimento (variável alvo). Existem oito características disponíveis:
- Área relativa do edifício
- Área do telhado
- Altura do edifício
- Tamanho do vidro
- Distribuição do vidro
- Orientação (codificada)
- Área da superfície do telhado
- Área da superfície da parede
Seu trabalho é construir um modelo de regressão linear regularizado. O modelo deve aprender a relação entre as características e a carga de aquecimento. A arquitetura do modelo é simples: uma combinação linear das entradas. A fórmula da regressão linear é: \(\hat{y} = w_1 x_1 + w_2 x_2 + … + w_n x_n + b\) Onde \(w_i\) são os pesos e \(b\) é o intercepto. A regularização Ridge adiciona uma penalidade à soma dos quadrados dos pesos. Para Ridge, a função de custo é: \(J(w) = \text{MSE}(w) + \alpha \sum_{i=1}^{n} w_i^2\) Para Lasso, a penalidade usa o valor absoluto dos pesos: \(J(w) = \text{MSE}(w) + \alpha \sum_{i=1}^{n} |w_i|\) Elastic Net combina ambas as penalidades: \(J(w) = \text{MSE}(w) + \alpha \cdot \text{l1\_ratio} \sum |w_i| + \alpha \cdot (1 – \text{l1\_ratio}) \sum w_i^2\) Hiperparâmetros importantes:
alpha– controla a força da regularização. Quanto maior, mais simples o modelo.l1_ratio(Elastic Net) – mistura entre Lasso (1) e Ridge (0).max_iter– número máximo de iterações para convergência.tol– tolerância para parar o treinamento.
Use validação cruzada para escolher o melhor alpha. Avalie o modelo com o erro quadrático médio (MSE) e o R². Compare os três métodos (Ridge, Lasso, Elastic Net).
Exemplo com Ridge
|
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 |
# Ridge Regression com visualizações import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.linear_model import RidgeCV, LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, r2_score from sklearn.datasets import fetch_openml # Configurar estilo dos gráficos sns.set_style("whitegrid") plt.rcParams['figure.figsize'] = (12, 5) # 1. Carregar dados print("Carregando dados...") data = fetch_openml(data_id=487, as_frame=True) X = data.data y_temp = data.target.to_numpy() if y_temp.ndim == 2: y = y_temp[:, 0] else: y = y_temp # 2. Dividir dados X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 3. Padronizar scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 4. Treinar Ridge com diferentes alphas para análise alphas = np.logspace(-3, 3, 50) ridge_model = RidgeCV(alphas=alphas, store_cv_values=True, scoring='neg_mean_squared_error') ridge_model.fit(X_train_scaled, y_train) # 5. Resultados print(f"Melhor alpha: {ridge_model.alpha_:.4f}") # 6. Previsões y_pred_ridge = ridge_model.predict(X_test_scaled) mse_ridge = mean_squared_error(y_test, y_pred_ridge) r2_ridge = r2_score(y_test, y_pred_ridge) print(f"MSE no teste: {mse_ridge:.4f}") print(f"R² no teste: {r2_ridge:.4f}") # 7. Comparação com Linear Regression lr = LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr = lr.predict(X_test_scaled) mse_lr = mean_squared_error(y_test, y_pred_lr) print(f"\nLinear Regression - MSE: {mse_lr:.4f}") print(f"Ridge reduziu MSE em {(mse_lr - mse_ridge)/mse_lr*100:.2f}%") # ========== GRÁFICOS ========== # Gráfico 1: Previsões vs Valores Reais plt.subplot(1, 2, 1) plt.scatter(y_test, y_pred_ridge, alpha=0.6, edgecolors='k', linewidth=0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2) plt.xlabel('Valores Reais') plt.ylabel('Previsões') plt.title(f'Ridge: Previsões vs Real\nR² = {r2_ridge:.3f}') # Gráfico 2: Distribuição dos Erros plt.subplot(1, 2, 2) erros = y_test - y_pred_ridge sns.histplot(erros, bins=30, kde=True) plt.xlabel('Erro (Real - Previsto)') plt.ylabel('Frequência') plt.title(f'Distribuição dos Erros\nMédia = {erros.mean():.3f}, Std = {erros.std():.3f}') plt.tight_layout() plt.show() # Gráfico 3: Comparação de Coeficientes plt.figure(figsize=(10, 5)) coef_ridge = ridge_model.coef_ coef_lr = lr.coef_ x_pos = np.arange(len(X.columns)) width = 0.35 plt.bar(x_pos - width/2, coef_lr, width, label='Linear Regression', alpha=0.7) plt.bar(x_pos + width/2, coef_ridge, width, label=f'Ridge (alpha={ridge_model.alpha_:.3f})', alpha=0.7) plt.xlabel('Características') plt.ylabel('Coeficiente') plt.title('Comparação dos Coeficientes') plt.xticks(x_pos, X.columns, rotation=45, ha='right') plt.legend() plt.tight_layout() plt.show() # Gráfico 4: Efeito do alpha na validação cruzada cv_mse = -ridge_model.cv_values_.mean(axis=0) # Média do MSE por alpha plt.figure(figsize=(8, 5)) plt.semilogx(alphas, cv_mse, 'b-o', linewidth=2, markersize=6) plt.axvline(ridge_model.alpha_, color='r', linestyle='--', label=f'Melhor alpha = {ridge_model.alpha_:.4f}') plt.xlabel('Alpha (escala logarítmica)') plt.ylabel('MSE médio (validação cruzada)') plt.title('Escolha do Alpha por Validação Cruzada') plt.legend() plt.grid(True, alpha=0.3) plt.show() |
Exemplo com Lasso
|
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 |
# Lasso Regression com visualizações import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.linear_model import LassoCV, LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, r2_score from sklearn.datasets import fetch_openml sns.set_style("whitegrid") plt.rcParams['figure.figsize'] = (12, 5) # 1. Carregar dados print("Carregando dados...") data = fetch_openml(data_id=487, as_frame=True) X = data.data y_temp = data.target.to_numpy() if y_temp.ndim == 2: y = y_temp[:, 0] else: y = y_temp # 2. Dividir dados X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 3. Padronizar scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 4. Treinar Lasso alphas = np.logspace(-4, 2, 50) lasso_model = LassoCV(alphas=alphas, cv=5, random_state=42, max_iter=10000) lasso_model.fit(X_train_scaled, y_train) # 5. Resultados print(f"Melhor alpha: {lasso_model.alpha_:.6f}") print(f"Coeficientes zerados: {np.sum(lasso_model.coef_ == 0)} de {len(lasso_model.coef_)}") # 6. Previsões y_pred_lasso = lasso_model.predict(X_test_scaled) mse_lasso = mean_squared_error(y_test, y_pred_lasso) r2_lasso = r2_score(y_test, y_pred_lasso) print(f"MSE no teste: {mse_lasso:.4f}") print(f"R² no teste: {r2_lasso:.4f}") # 7. Comparação lr = LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr = lr.predict(X_test_scaled) mse_lr = mean_squared_error(y_test, y_pred_lr) print(f"\nLinear Regression - MSE: {mse_lr:.4f}") print(f"Lasso reduziu MSE em {(mse_lr - mse_lasso)/mse_lr*100:.2f}%") # ========== GRÁFICOS ========== # Gráfico 1: Previsões vs Real plt.subplot(1, 2, 1) plt.scatter(y_test, y_pred_lasso, alpha=0.6, edgecolors='k', linewidth=0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2) plt.xlabel('Valores Reais') plt.ylabel('Previsões Lasso') plt.title(f'Lasso: Previsões vs Real\nR² = {r2_lasso:.3f}') # Gráfico 2: Distribuição dos Erros plt.subplot(1, 2, 2) erros = y_test - y_pred_lasso sns.histplot(erros, bins=30, kde=True) plt.xlabel('Erro (Real - Previsto)') plt.ylabel('Frequência') plt.title(f'Distribuição dos Erros\nMédia = {erros.mean():.3f}') plt.tight_layout() plt.show() # Gráfico 3: Coeficientes do Lasso (destacando zerados) plt.figure(figsize=(10, 5)) cores = ['red' if c == 0 else 'steelblue' for c in lasso_model.coef_] plt.bar(range(len(lasso_model.coef_)), lasso_model.coef_, color=cores, alpha=0.7) plt.axhline(y=0, color='black', linestyle='-', linewidth=0.5) plt.xlabel('Características') plt.ylabel('Coeficiente') plt.title(f'Coeficientes do Lasso (alpha={lasso_model.alpha_:.5f})\nVermelho = zerados') plt.xticks(range(len(X.columns)), X.columns, rotation=45, ha='right') plt.tight_layout() plt.show() # Gráfico 4: Caminho de regularização print("\nCalculando caminho de regularização...") alphas_path = np.logspace(-4, 2, 100) coefs = [] for a in alphas_path: from sklearn.linear_model import Lasso lasso_temp = Lasso(alpha=a, max_iter=10000) lasso_temp.fit(X_train_scaled, y_train) coefs.append(lasso_temp.coef_) coefs = np.array(coefs) plt.figure(figsize=(10, 5)) for i in range(coefs.shape[1]): plt.semilogx(alphas_path, coefs[:, i], label=X.columns[i], linewidth=2) plt.axvline(lasso_model.alpha_, color='black', linestyle='--', label=f'Alpha escolhido = {lasso_model.alpha_:.5f}') plt.xlabel('Alpha (log scale)') plt.ylabel('Coeficientes') plt.title('Caminho de Regularização do Lasso') plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() |
Exemplo com Elastic Net
|
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 |
# Elastic Net Regression com visualizações import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.linear_model import ElasticNetCV, LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, r2_score from sklearn.datasets import fetch_openml sns.set_style("whitegrid") plt.rcParams['figure.figsize'] = (14, 6) # 1. Carregar dados print("Carregando dados...") data = fetch_openml(data_id=487, as_frame=True) X = data.data y_temp = data.target.to_numpy() if y_temp.ndim == 2: y = y_temp[:, 0] else: y = y_temp # 2. Dividir dados X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 3. Padronizar scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 4. Treinar Elastic Net alphas = np.logspace(-4, 2, 30) l1_ratios = np.linspace(0.1, 0.9, 9) elastic_model = ElasticNetCV( l1_ratio=l1_ratios, alphas=alphas, cv=5, random_state=42, max_iter=10000 ) elastic_model.fit(X_train_scaled, y_train) # 5. Resultados print(f"Melhor alpha: {elastic_model.alpha_:.6f}") print(f"Melhor l1_ratio: {elastic_model.l1_ratio_:.3f}") print(f"Coeficientes zerados: {np.sum(elastic_model.coef_ == 0)} de {len(elastic_model.coef_)}") # 6. Previsões y_pred_elastic = elastic_model.predict(X_test_scaled) mse_elastic = mean_squared_error(y_test, y_pred_elastic) r2_elastic = r2_score(y_test, y_pred_elastic) print(f"MSE no teste: {mse_elastic:.4f}") print(f"R² no teste: {r2_elastic:.4f}") # 7. Comparação lr = LinearRegression() lr.fit(X_train_scaled, y_train) y_pred_lr = lr.predict(X_test_scaled) mse_lr = mean_squared_error(y_test, y_pred_lr) print(f"\nLinear Regression - MSE: {mse_lr:.4f}") print(f"Elastic Net reduziu MSE em {(mse_lr - mse_elastic)/mse_lr*100:.2f}%") # ========== GRÁFICOS ========== # Gráfico 1: Previsões vs Real plt.subplot(1, 2, 1) plt.scatter(y_test, y_pred_elastic, alpha=0.6, edgecolors='k', linewidth=0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2) plt.xlabel('Valores Reais') plt.ylabel('Previsões Elastic Net') plt.title(f'Elastic Net: Previsões vs Real\nR² = {r2_elastic:.3f}') # Gráfico 2: Distribuição dos Erros plt.subplot(1, 2, 2) erros = y_test - y_pred_elastic sns.histplot(erros, bins=30, kde=True) plt.xlabel('Erro (Real - Previsto)') plt.ylabel('Frequência') plt.title(f'Distribuição dos Erros\nStd = {erros.std():.3f}') plt.tight_layout() plt.show() # Gráfico 3: Coeficientes dos três métodos plt.figure(figsize=(12, 5)) x_pos = np.arange(len(X.columns)) width = 0.25 # Re-treinar Ridge e Lasso para comparação justa from sklearn.linear_model import Ridge, Lasso ridge_comp = Ridge(alpha=elastic_model.alpha_) lasso_comp = Lasso(alpha=elastic_model.alpha_, max_iter=10000) ridge_comp.fit(X_train_scaled, y_train) lasso_comp.fit(X_train_scaled, y_train) plt.bar(x_pos - width, ridge_comp.coef_, width, label='Ridge', alpha=0.7) plt.bar(x_pos, lasso_comp.coef_, width, label='Lasso', alpha=0.7) plt.bar(x_pos + width, elastic_model.coef_, width, label=f'Elastic Net (l1_ratio={elastic_model.l1_ratio_:.2f})', alpha=0.7) plt.xlabel('Características') plt.ylabel('Coeficiente') plt.title('Comparação de Coeficientes entre Métodos') plt.xticks(x_pos, X.columns, rotation=45, ha='right') plt.axhline(y=0, color='black', linestyle='-', linewidth=0.5) plt.legend() plt.tight_layout() plt.show() # Gráfico 4: Heatmap do desempenho por alpha e l1_ratio print("\nGerando heatmap da validação cruzada...") mse_matrix = elastic_model.mse_path_.mean(axis=2) # média dos folds plt.figure(figsize=(10, 6)) im = plt.imshow(mse_matrix, aspect='auto', cmap='viridis_r', origin='lower') plt.colorbar(im, label='MSE médio') plt.xlabel('Índice de alpha') plt.ylabel('Índice de l1_ratio') plt.title('Desempenho da Validação Cruzada - Elastic Net') yticks = range(len(l1_ratios)) yticklabels = [f'{r:.2f}' for r in l1_ratios] plt.yticks(yticks, yticklabels) plt.tight_layout() plt.show() |