Introdução ao Lasso LARS
O Lasso LARS constitui uma implementação eficiente que combina as vantagens da regressão Lasso com o algoritmo Least Angle Regression. Primordialmente, esta abordagem permite computar a solução completa do caminho Lasso de forma computacionalmente eficiente, sem a necessidade de otimização convexa iterativa.
Princípio de Funcionamento
O Lasso LARS opera através de um processo sequencial similar ao LARS tradicional, mas com uma modificação crucial: quando um coeficiente atinge zero, a feature correspondente é removida do conjunto ativo. Esta simples alteração garante que as soluções produzidas sejam idênticas às do Lasso convencional.
Diferenciação do LARS Clássico
Enquanto o LARS tradicional sempre mantém as features no modelo uma vez selecionadas, o Lasso LARS permite a remoção de features quando seus coeficientes se tornam zero. Isto produz modelos mais esparsos e alinhados com a filosofia de seleção de features do Lasso.
Vantagens Computacionais
- Computação eficiente de todo o caminho de regularização
- Menor custo computacional para problemas com muitas features
- Estabilidade numérica superior em comparação com métodos baseados em descida de gradiente
- Capacidade de lidar com situações onde número de features excede número de amostras
Formulação Matemática
O Lasso LARS resolve o mesmo problema de otimização do Lasso tradicional:
\(\min_{w} \frac{1}{2n_{\text{samples}}} ||X w – y||_2^2 + \alpha ||w||_1\)Contudo, a estratégia de solução é fundamentalmente diferente. Ao invés de otimização convexa, utiliza-se uma abordagem geométrica baseada em direções equiangulares.
Algoritmo Lasso LARS
- Iniciar com coeficientes zero e resíduo igual ao target
- Encontrar a feature mais correlacionada com o resíduo
- Mover na direção desta feature até que outra feature tenha correlação igual
- Continuar na direção equiangular entre features ativas
- Se algum coeficiente atingir zero, remover a feature do conjunto ativo
- Repetir até inclusão de todas as features relevantes
Cenários de Aplicação Ideal
O Lasso LARS é particularmente adequado para:
- Problemas com número de features maior que número de amostras (p > n)
- Análise exploratória onde se deseja examinar todo o caminho de regularização
- Situações que requerem seleção eficiente de features
- Aplicações onde a estabilidade numérica é crítica
Implementação no scikit-learn
No scikit-learn, a classe LassoLars implementa esta técnica. Ademais, LassoLarsCV fornece seleção automática do parâmetro alpha através de validação cruzada.
Parâmetros Principais
alpha: Parâmetro de regularização (constante ou array)fit_intercept: Cálculo do termo interceptoverbose: Nível de detalhe durante execuçãonormalize: Normalização prévia dos dadosmax_iter: Número máximo de iterações
Exemplo Prático: Comparação com Outras Abordagens
O exemplo a seguir demonstra o uso do Lasso LARS e sua comparação com implementações alternativas do 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 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 |
import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LassoLars, Lasso, LassoLarsIC 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 import time print("=" * 60) print("COMPARAÇÃO: LASSO LARS VS LASSO TRADICIONAL") print("=" * 60) # Gerar dados com alta dimensionalidade (p > n scenario) X, y = make_regression(n_samples=80, n_features=50, n_informative=10, noise=0.3, random_state=42) # Normalizar os dados scaler = StandardScaler() X_scaled = scaler.fit_transform(X) y_centered = y - np.mean(y) # Dividir em treino e teste X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_centered, test_size=0.3, random_state=42) print(f"Cenário p > n: {X.shape[1]} features, {X.shape[0]} amostras") print(f"Features informativas verdadeiras: 10") # 1. Lasso LARS com alpha fixo print("\n1. LASSO LARS (ALPHA=0.1)") start_time = time.time() lasso_lars = LassoLars(alpha=0.1) lasso_lars.fit(X_train, y_train) lars_time = time.time() - start_time y_pred_lars = lasso_lars.predict(X_test) coef_nao_nulos_lars = np.sum(lasso_lars.coef_ != 0) print(f"Tempo de execução: {lars_time:.4f} segundos") print(f"Coeficientes não nulos: {coef_nao_nulos_lars}/50") print(f"MSE: {mean_squared_error(y_test, y_pred_lars):.4f}") print(f"R²: {r2_score(y_test, y_pred_lars):.4f}") # 2. Lasso tradicional print("\n2. LASSO TRADICIONAL (ALPHA=0.1)") start_time = time.time() lasso_trad = Lasso(alpha=0.1, max_iter=10000, random_state=42) lasso_trad.fit(X_train, y_train) lasso_time = time.time() - start_time y_pred_trad = lasso_trad.predict(X_test) coef_nao_nulos_trad = np.sum(lasso_trad.coef_ != 0) print(f"Tempo de execução: {lasso_time:.4f} segundos") print(f"Coeficientes não nulos: {coef_nao_nulos_trad}/50") print(f"MSE: {mean_squared_error(y_test, y_pred_trad):.4f}") print(f"R²: {r2_score(y_test, y_pred_trad):.4f}") # 3. Lasso LARS com critério de informação print("\n3. LASSO LARS COM CRITÉRIO AIC") lasso_lars_aic = LassoLarsIC(criterion='aic') lasso_lars_aic.fit(X_train, y_train) y_pred_aic = lasso_lars_aic.predict(X_test) print(f"Alpha selecionado (AIC): {lasso_lars_aic.alpha_:.6f}") print(f"Coeficientes não nulos: {np.sum(lasso_lars_aic.coef_ != 0)}/50") print(f"MSE: {mean_squared_error(y_test, y_pred_aic):.4f}") # Visualização comparativa plt.figure(figsize=(15, 10)) # Gráfico 1: Comparação de coeficientes plt.subplot(2, 2, 1) coeficientes = [lasso_lars.coef_, lasso_trad.coef_, lasso_lars_aic.coef_] nomes = ['LassoLars', 'Lasso Trad', 'LassoLars AIC'] cores = ['blue', 'red', 'green'] for i, (coef, nome) in enumerate(zip(coeficientes, nomes)): plt.bar(np.arange(50) + i*0.25, coef, width=0.23, label=nome, alpha=0.7, color=cores[i]) plt.axhline(y=0, color='black', linestyle='-', alpha=0.3) plt.xlabel('Features') plt.ylabel('Valor do Coeficiente') plt.title('Comparação dos Coeficientes - Cenário p > n') plt.legend() plt.grid(True, alpha=0.3) # Gráfico 2: Caminho de regularização LassoLars plt.subplot(2, 2, 2) # Calcular caminho completo com alpha zero lars_path = LassoLars(alpha=0) lars_path.fit(X_train, y_train) # Extrair alphas ao longo do caminho alphas = lars_path.alphas_ coef_path = lars_path.coef_path_ # Plotar caminho para primeiras 15 features for i in range(min(15, coef_path.shape[0])): plt.plot(alphas, coef_path[i], label=f'F{i+1}', linewidth=1.5) plt.xlabel('Alpha') plt.ylabel('Valor do Coeficiente') plt.title('Caminho LassoLars - Evolução com Alpha') plt.xscale('log') plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') plt.grid(True, alpha=0.3) # Gráfico 3: Comparação de esparsidade plt.subplot(2, 2, 3) features_nao_nulas = [np.sum(coef != 0) for coef in coeficientes] plt.bar(nomes, features_nao_nulas, color=cores, alpha=0.7) plt.ylabel('Número de Features Selecionadas') plt.title('Comparação de Esparsidade') for i, v in enumerate(features_nao_nulas): plt.text(i, v + 0.5, str(v), ha='center', va='bottom') plt.grid(True, alpha=0.3) # Gráfico 4: Comparação de performance plt.subplot(2, 2, 4) mse_valores = [ mean_squared_error(y_test, y_pred_lars), mean_squared_error(y_test, y_pred_trad), mean_squared_error(y_test, y_pred_aic) ] tempos = [lars_time, lasso_time, 0] # AIC não tem tempo separado x_pos = np.arange(len(nomes)) width = 0.35 plt.bar(x_pos - width/2, mse_valores, width, label='MSE', alpha=0.7, color='blue') plt.bar(x_pos + width/2, tempos, width, label='Tempo (s)', alpha=0.7, color='orange') plt.xlabel('Método') plt.ylabel('MSE / Tempo (s)') plt.title('Performance e Eficiência Computacional') plt.xticks(x_pos, nomes) plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() # Análise de concordância na seleção de features print("\n4. ANÁLISE DE CONCORDÂNCIA NA SELEÇÃO") print("Feature | LassoLars | LassoTrad | Concordância") print("-" * 50) concordancia_total = 0 for i in range(50): selecionada_lars = lasso_lars.coef_[i] != 0 selecionada_trad = lasso_trad.coef_[i] != 0 concordancia = "SIM" if selecionada_lars == selecionada_trad else "NÃO" if selecionada_lars == selecionada_trad: concordancia_total += 1 status_lars = "SEL" if selecionada_lars else "---" status_trad = "SEL" if selecionada_trad else "---" print(f"F{i+1:2d} | {status_lars} | {status_trad} | {concordancia}") print(f"\nTaxa de concordância: {concordancia_total/50*100:.1f}%") # Análise adicional do caminho LassoLars print("\n5. INFORMAÇÕES DO CAMINHO LASSO LARS") print(f"Número de iterações: {len(lars_path.alphas_)}") print(f"Alpha máximo: {lars_path.alphas_[0]:.6f}") print(f"Alpha mínimo: {lars_path.alphas_[-1]:.6f}") |
Considerações sobre Estabilidade Numérica
O Lasso LARS demonstra particular robustez em situações numericamente desafiadoras. Por utilizar decomposições matriciais estáveis e evitar problemas de convergência associados a métodos iterativos, frequentemente produz resultados mais confiáveis em problemas mal condicionados.
Limitações e Considerações
Embora poderoso, o Lasso LARS apresenta algumas limitações:
- Pode ser menos eficiente quando número de amostras é muito grande
- Implementação atual não suporta sparse matrices de forma nativa
- Menos flexível para personalização em comparação com abordagens baseadas em otimização
Considerações Finais
Inegavelmente, o Lasso LARS representa uma fusão elegante entre a intuitividade geométrica do LARS e o poder de seleção de features do Lasso. Sua capacidade de computar eficientemente todo o caminho de regularização o torna invaluable para análise exploratória e seleção de modelos.
Decerto, em cenários de alta dimensionalidade (p > n), o Lasso LARS frequentemente supera implementações tradicionais do Lasso tanto em termos de eficiência computacional quanto de estabilidade numérica. Ademais, fornece insights valiosos sobre a ordem de importância das features através do exame do caminho de regularização.
Analogamente a outras técnicas de regularização, a escolha do parâmetro alpha permanece crucial. Portanto, a combinação do Lasso LARS com métodos de validação cruzada ou critérios de informação constitui uma abordagem robusta para problemas práticos de machine learning.