Примечание
Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.
Модели на основе L1 для разреженных сигналов
В этом примере сравниваются три регрессионные модели на основе l1 для синтетического сигнала, полученного из разреженных и коррелированных признаков, к которым затем добавляется гауссовский шум:
Известно, что при увеличении размерности данных оценки Lasso становятся близки к оценкам, полученным при выборе модели, если нерелевантные переменные не слишком сильно коррелируют с релевантными. При наличии коррелированных признаков Lasso не может самостоятельно выбрать правильную структуру разреженности [1].
Здесь мы сравниваем производительность трех моделей по показателю \(R^2\), времени обучения и разреженности оцененных коэффициентов относительно истинных значений.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Создание синтетического набора данных
Мы создаем набор данных, в котором число объектов меньше общего числа признаков. Это приводит к недоопределенной системе, то есть решение не единственно, поэтому нельзя использовать отдельно метод обыкновенных наименьших квадратов. Регуляризация добавляет штрафной член в целевую функцию, изменяя задачу оптимизации и помогая смягчить проблему недоопределенности системы.
Целевая переменная y представляет собой линейную комбинацию синусоидальных сигналов с чередующимися знаками. Для генерации y используются только 10 самых низких частот из 100 в X, а остальные признаки не несут информации. В результате получается высокоразмерное разреженное пространство признаков, где необходима определенная степень L1-регуляризации.
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 # sparsify coef
y = np.dot(X, true_coef)
У некоторых информативных признаков близкие частоты, что приводит к (анти-)корреляции.
freqs[:n_informative]
array([ 2.9502547 , 11.8059798 , 12.63394388, 12.70359377, 24.62241605,
37.84077985, 40.30506066, 44.63327171, 54.74495357, 59.02456369])
Случайная фаза добавляется с помощью numpy.random.random_sample, а к признакам и целевой переменной добавляется гауссовский шум (реализованный с помощью 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)
Такие разреженные, зашумленные и коррелированные признаки могут встречаться, например, у сенсорных узлов, отслеживающих некоторые параметры окружающей среды, поскольку в зависимости от расположения они обычно регистрируют схожие значения (пространственные корреляции). Визуализируем целевую переменную.
import matplotlib.pyplot as plt
plt.plot(time_step, y)
plt.ylabel("target signal")
plt.xlabel("time")
_ = plt.title("Superposition of sinusoidal signals")

Для простоты мы разделяем данные на обучающую и тестовую выборки. На практике для оценки дисперсии тестовой оценки следует использовать кросс-валидацию TimeSeriesSplit. Здесь мы задаем shuffle="False", поскольку при работе с данными, имеющими временную связь, нельзя использовать обучающие данные, следующие за тестовыми.
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)
Далее мы оцениваем производительность трех моделей на основе l1 по показателю качества аппроксимации \(R^2\) и времени обучения. Затем строим график для сравнения разреженности оцененных коэффициентов с истинными коэффициентами и, наконец, анализируем полученные результаты.
Lasso
В этом примере мы демонстрируем работу Lasso с фиксированным значением параметра регуляризации alpha. На практике оптимальное значение параметра alpha следует выбирать, передав стратегию кросс-валидации TimeSeriesSplit в LassoCV. Чтобы пример был простым и быстро выполнялся, здесь мы напрямую задаем оптимальное значение alpha.
from time import time
from sklearn.linear_model import Lasso
from sklearn.metrics import r2_score
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}")
Lasso fit done in 0.002s Lasso r^2 on test data : 0.480
Автоматическое определение релевантности (ARD)
Регрессия ARD — это байесовская версия Lasso. При необходимости она позволяет получить интервальные оценки всех параметров, включая дисперсию ошибки. Это подходящий вариант для сигналов с гауссовским шумом. См. пример Сравнение линейных байесовских регрессоров, в котором сравниваются регрессоры ARDRegression и BayesianRidge.
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}")
ARD fit done in 0.061s ARD r^2 on test data : 0.543
ElasticNet
ElasticNet занимает промежуточное положение между Lasso и Ridge, поскольку сочетает штрафы L1 и L2. Степень регуляризации задается двумя гиперпараметрами l1_ratio и alpha. При l1_ratio =
0 штраф является чисто L2, а модель эквивалентна Ridge. Аналогично, l1_ratio = 1 соответствует чистому штрафу L1, и модель эквивалентна Lasso. При 0 < l1_ratio < 1 штраф представляет собой комбинацию L1 и L2.
Как и ранее, мы обучаем модель с фиксированными значениями alpha и l1_ratio. Оптимальные значения этих параметров мы выбрали с помощью ElasticNetCV; здесь это не показано, чтобы сохранить простоту примера.
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}")
ElasticNet fit done in 0.001s ElasticNet r^2 on test data : 0.636
График и анализ результатов
В этом разделе мы используем тепловую карту для визуализации разреженности истинных и оцененных коэффициентов соответствующих линейных моделей.
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
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": "coefficients' values"},
cmap="seismic_r",
)
plt.ylabel("linear model")
plt.xlabel("coefficients")
plt.title(
f"Models' coefficients\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()

В данном примере ElasticNet показывает наилучший результат и выявляет большинство прогнозных признаков, но все же не находит все истинные компоненты. Обратите внимание, что и ElasticNet, и ARDRegression дают менее разреженную модель, чем Lasso.
Выводы
Известно, что Lasso эффективно восстанавливает разреженные данные, но плохо работает с сильно коррелированными признаками. Действительно, если целевая переменная зависит от нескольких коррелированных признаков, Lasso в итоге выберет только один из них. Для разреженных, но некоррелированных признаков модель Lasso будет более подходящей.
ElasticNet обеспечивает разреженность коэффициентов и уменьшает их значения, приближая их к нулю. Поэтому при наличии коррелированных признаков, влияющих на целевую переменную, модель по-прежнему может уменьшать их веса, не обнуляя их полностью. В результате модель получается менее разреженной, чем при чистом использовании Lasso, и может также учитывать непредсказательные признаки.
ARDRegression лучше справляется с гауссовским шумом, но по-прежнему не может работать с коррелированными признаками и требует больше времени из-за подгонки априорного распределения.
Ссылки
Общее время выполнения скрипта: (0 минут 0.544 секунды)
Связанные примеры
© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/linear_model/plot_lasso_and_elasticnet.html