Introdução
Neste laboratório, demonstraremos como utilizar modelos de regressão baseados em L1 para lidar com sinais de alta dimensionalidade e esparsos. Em particular, compararemos três modelos populares baseados em L1: Lasso, Determinação Automática de Relevância (ARD) e ElasticNet. Utilizaremos um conjunto de dados sintético para ilustrar o desempenho destes modelos em termos de tempo de ajuste, pontuação R2 e esparcidade dos coeficientes estimados.
Dicas da Máquina Virtual
Após o arranque da VM, clique no canto superior esquerdo para mudar para a aba Notebook para aceder ao Jupyter Notebook para a prática.
Por vezes, pode ser necessário esperar alguns segundos para o Jupyter Notebook terminar de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se tiver problemas durante o aprendizado, não hesite em contactar o Labby. Forneça feedback após a sessão e resolveremos prontamente o problema para si.
Gerar Conjunto de Dados Sintético
Primeiro, geramos um conjunto de dados onde o número de amostras é inferior ao número total de características. Isto leva a um sistema subdeterminado, ou seja, a solução não é única e não podemos aplicar os mínimos quadrados ordinários por si só. A regularização introduz um termo de penalização na função objetivo, o que modifica o problema de otimização e pode ajudar a aliviar a natureza subdeterminada do sistema. Iremos gerar um alvo y que é uma combinação linear com sinais sinusoidais alternados. Apenas as 10 frequências mais baixas das 100 frequências em X são usadas para gerar y, enquanto as restantes características não são informativas. Isto resulta num espaço de características esparso de alta dimensionalidade, onde algum grau de penalização L1 é necessário.
import numpy as np
rng = np.random.RandomState(0)
n_samples, n_features, n_informative = 50, 100, 10
time_step = np.linspace(-2, 2, n_samples)
freqs = 2 * np.pi * np.sort(rng.rand(n_features)) / 0.01
X = np.zeros((n_samples, n_features))
for i in range(n_features):
X[:, i] = np.sin(freqs[i] * time_step)
idx = np.arange(n_features)
true_coef = (-1) ** idx * np.exp(-idx / 10)
true_coef[n_informative:] = 0 ## tornar o coef esparso
y = np.dot(X, true_coef)
## introduzir fase aleatória usando numpy.random.random_sample
## adicionar algum ruído gaussiano usando numpy.random.normal
for i in range(n_features):
X[:, i] = np.sin(freqs[i] * time_step + 2 * (rng.random_sample() - 0.5))
X[:, i] += 0.2 * rng.normal(0, 1, n_samples)
y += 0.2 * rng.normal(0, 1, n_samples)
## dividir os dados em conjuntos de treino e teste usando train_test_split de sklearn
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, shuffle=False)
Lasso
Nesta etapa, demonstraremos como utilizar o modelo de regressão Lasso para estimar os coeficientes esparsos do conjunto de dados. Usaremos um valor fixo do parâmetro de regularização alpha. Na prática, o parâmetro ótimo alpha deve ser selecionado passando uma estratégia de validação cruzada TimeSeriesSplit para um LassoCV. Para manter o exemplo simples e rápido de executar, definimos diretamente o valor ótimo para alpha aqui.
from sklearn.linear_model import Lasso
from sklearn.metrics import r2_score
from time import time
t0 = time()
lasso = Lasso(alpha=0.14).fit(X_train, y_train)
print(f"Lasso fit done in {(time() - t0):.3f}s")
y_pred_lasso = lasso.predict(X_test)
r2_score_lasso = r2_score(y_test, y_pred_lasso)
print(f"Lasso r^2 on test data : {r2_score_lasso:.3f}")
Determinação Automática de Relevância (ARD)
Uma regressão ARD é a versão Bayesiana do Lasso. Pode produzir estimativas de intervalo para todos os parâmetros, incluindo a variância do erro, se necessário. É uma opção adequada quando os sinais têm ruído gaussiano.
from sklearn.linear_model import ARDRegression
t0 = time()
ard = ARDRegression().fit(X_train, y_train)
print(f"ARD fit done in {(time() - t0):.3f}s")
y_pred_ard = ard.predict(X_test)
r2_score_ard = r2_score(y_test, y_pred_ard)
print(f"ARD r^2 on test data : {r2_score_ard:.3f}")
ElasticNet
ElasticNet é um ponto intermediário entre a regressão Lasso e Ridge, pois combina uma penalidade L1 e uma penalidade L2. A quantidade de regularização é controlada pelos dois hiperparâmetros l1_ratio e alpha. Para l1_ratio = 0, a penalidade é puramente L2 e o modelo é equivalente a uma regressão Ridge. Analogamente, l1_ratio = 1 é uma penalidade puramente L1 e o modelo é equivalente a uma regressão Lasso. Para 0 < l1_ratio < 1, a penalidade é uma combinação de L1 e L2.
from sklearn.linear_model import ElasticNet
t0 = time()
enet = ElasticNet(alpha=0.08, l1_ratio=0.5).fit(X_train, y_train)
print(f"ElasticNet fit done in {(time() - t0):.3f}s")
y_pred_enet = enet.predict(X_test)
r2_score_enet = r2_score(y_test, y_pred_enet)
print(f"ElasticNet r^2 on test data : {r2_score_enet:.3f}")
Plotagem e Análise dos Resultados
Nesta etapa, utilizamos um mapa de calor para visualizar a esparcidade dos coeficientes verdadeiros e estimados dos respectivos modelos lineares.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from matplotlib.colors import SymLogNorm
df = pd.DataFrame(
{
"True coefficients": true_coef,
"Lasso": lasso.coef_,
"ARDRegression": ard.coef_,
"ElasticNet": enet.coef_,
}
)
plt.figure(figsize=(10, 6))
ax = sns.heatmap(
df.T,
norm=SymLogNorm(linthresh=10e-4, vmin=-1, vmax=1),
cbar_kws={"label": "Valores dos coeficientes"},
cmap="seismic_r",
)
plt.ylabel("Modelo linear")
plt.xlabel("Coeficientes")
plt.title(
f"Coeficientes dos Modelos\nLasso $R^2$: {r2_score_lasso:.3f}, "
f"ARD $R^2$: {r2_score_ard:.3f}, "
f"ElasticNet $R^2$: {r2_score_enet:.3f}"
)
plt.tight_layout()
Resumo
O Lasso é conhecido por recuperar dados esparsos de forma eficaz, mas não tem um bom desempenho com recursos altamente correlacionados. De fato, se vários recursos correlacionados contribuírem para o alvo, o Lasso acabaria selecionando apenas um deles. No caso de recursos esparsos, mas não correlacionados, um modelo Lasso seria mais adequado.
O ElasticNet introduz alguma esparcidade nos coeficientes e reduz seus valores a zero. Assim, na presença de recursos correlacionados que contribuem para o alvo, o modelo ainda é capaz de reduzir seus pesos sem defini-los exatamente em zero. Isso resulta em um modelo menos esparso do que um Lasso puro e pode capturar também recursos não preditivos.
O ARDRegression é melhor quando lida com ruído Gaussiano, mas ainda não consegue lidar com recursos correlacionados e requer mais tempo devido ao ajuste de uma prioridade.