Regressão de Sinal Esparso com Modelos Baseados em L1

Beginner

This tutorial is from open-source community. Access the source code

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.