Spec-Zone.ru › scikit-learn

Примечание

Перейти к концу для загрузки полного примера кода. Или запустить этот пример в вашем браузере с помощью JupyterLite или Binder

Сравнение регрессии с ядром Риджа и Гауссовым процессом

Этот пример демонстрирует различия между регрессией с ядром Риджа и регрессией с гауссовым процессом.

Как регрессия с ядром Риджа, так и регрессия с гауссовым процессом используют так называемый «трюк с ядром», чтобы сделать свои модели достаточно выразительными для подгонки к обучающим данным. Однако решаемые с помощью этих двух методов задачи машинного обучения значительно отличаются.

Регрессия с ядром Риджа найдет целевую функцию, минимизирующую функцию потерь (среднеквадратичную ошибку).

Вместо поиска единственной целевой функции, регрессия с гауссовым процессом использует вероятностный подход: определяется гауссово заднее распределение над целевыми функциями на основе теоремы Байеса. Таким образом, априорные вероятности целевых функций объединяются с функцией правдоподобия, определенной наблюдаемыми обучающими данными, для получения оценок задних распределений.

Мы проиллюстрируем эти различия на примере, и мы также сосредоточимся на настройке гиперпараметров ядра.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Генерация набора данных

Мы создаем синтетический набор данных. Истинный генеративный процесс примет 1-мерный вектор и вычислит его синус. Обратите внимание, что период этого синуса равен \(2 \pi\). Мы будем использовать эту информацию позже в этом примере.

import numpy as np

rng = np.random.RandomState(0)
data = np.linspace(0, 30, num=1_000).reshape(-1, 1)
target = np.sin(data).ravel()

Теперь мы можем представить себе сценарий, в котором мы получаем наблюдения из этого истинного процесса. Однако мы добавим некоторые сложности:

  • измерения будут шумными;
  • будут доступны только образцы с начала сигнала.
training_sample_indices = rng.choice(np.arange(0, 400), size=40, replace=False)
training_data = data[training_sample_indices]
training_noisy_target = target[training_sample_indices] + 0.5 * rng.randn(
    len(training_sample_indices)
)

Давайте построим истинную функцию и шумные измерения, доступные для обучения.

import matplotlib.pyplot as plt

plt.plot(data, target, label="True signal", linewidth=2)
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
plt.legend()
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title(
    "Illustration of the true generative process and \n"
    "noisy measurements available during training"
)
Illustration of the true generative process and  noisy measurements available during training

Ограничения простой линейной модели

Сначала мы хотим подчеркнуть ограничения линейной модели, учитывая наш набор данных. Мы подгоняем Ridge и проверяем прогнозы этой модели на нашем наборе данных.

from sklearn.linear_model import Ridge

ridge = Ridge().fit(training_data, training_noisy_target)

plt.plot(data, target, label="True signal", linewidth=2)
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
plt.plot(data, ridge.predict(data), label="Ridge regression")
plt.legend()
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title("Limitation of a linear model such as ridge")
Limitation of a linear model such as ridge

Такой регрессор Риджа плохо подгоняется к данным, так как он недостаточно выразителен.

Методы ядер: ядро Риджа и гауссов процесс

Регрессия с ядром Риджа

Мы можем сделать предыдущую линейную модель более выразительной, используя так называемое ядро. Ядро — это отображение из исходного пространства признаков в другое. Проще говоря, оно используется для отображения наших исходных данных в новое и более сложное пространство признаков. Это новое пространство явно определяется выбором ядра.

В нашем случае мы знаем, что истинный генеративный процесс является периодической функцией. Мы можем использовать ядро ExpSineSquared, которое позволяет восстановить периодичность. Класс KernelRidge будет принимать такое ядро.

Использование этой модели вместе с ядром эквивалентно отображению данных с помощью функции отображения ядра и последующего применения регрессии Риджа. На практике данные не отображаются явно; вместо этого вычисляется скалярное произведение между образцами в пространстве признаков более высокой размерности, используя «трюк с ядром».

Таким образом, давайте воспользуемся KernelRidge.

import time

from sklearn.gaussian_process.kernels import ExpSineSquared
from sklearn.kernel_ridge import KernelRidge

kernel_ridge = KernelRidge(kernel=ExpSineSquared())

start_time = time.time()
kernel_ridge.fit(training_data, training_noisy_target)
print(
    f"Fitting KernelRidge with default kernel: {time.time() - start_time:.3f} seconds"
)
Fitting KernelRidge with default kernel: 0.001 seconds
plt.plot(data, target, label="True signal", linewidth=2, linestyle="dashed")
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
plt.plot(
    data,
    kernel_ridge.predict(data),
    label="Kernel ridge",
    linewidth=2,
    linestyle="dashdot",
)
plt.legend(loc="lower right")
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title(
    "Kernel ridge regression with an exponential sine squared\n "
    "kernel using default hyperparameters"
)
Kernel ridge regression with an exponential sine squared  kernel using default hyperparameters

Полученная модель не точна. Действительно, мы не установили параметры ядра и вместо этого использовали значения по умолчанию. Мы можем их проверить.

kernel_ridge.kernel
ExpSineSquared(length_scale=1, periodicity=1)

У нашего ядра есть два параметра: масштаб длины и периодичность. Для нашего набора данных мы используем sin как генеративный процесс, предполагая периодичность сигнала \(2 \pi\). Значение параметра по умолчанию, равное \(1\), объясняет высокую частоту, наблюдаемую в прогнозах нашей модели. Аналогичные выводы можно сделать и с параметром масштаба длины. Таким образом, это говорит нам о необходимости настройки параметров ядра. Мы будем использовать случайный поиск для настройки различных параметров модели регрессии с ядром Риджа: параметр alpha и параметры ядра.

from scipy.stats import loguniform

from sklearn.model_selection import RandomizedSearchCV

param_distributions = {
    "alpha": loguniform(1e0, 1e3),
    "kernel__length_scale": loguniform(1e-2, 1e2),
    "kernel__periodicity": loguniform(1e0, 1e1),
}
kernel_ridge_tuned = RandomizedSearchCV(
    kernel_ridge,
    param_distributions=param_distributions,
    n_iter=500,
    random_state=0,
)
start_time = time.time()
kernel_ridge_tuned.fit(training_data, training_noisy_target)
print(f"Time for KernelRidge fitting: {time.time() - start_time:.3f} seconds")
Time for KernelRidge fitting: 4.294 seconds

Обучение модели теперь требует больше вычислительных ресурсов, так как нам необходимо перебрать несколько комбинаций гиперпараметров. Мы можем взглянуть на найденные гиперпараметры, чтобы получить некоторые интуиции.

kernel_ridge_tuned.best_params_
{'alpha': np.float64(1.991584977345022), 'kernel__length_scale': np.float64(0.7986499491396734), 'kernel__periodicity': np.float64(6.6072758064261095)}

Глядя на лучшие параметры, мы видим, что они отличаются от значений по умолчанию. Мы также видим, что периодичность ближе к ожидаемому значению: \(2 \pi\). Теперь мы можем проверить прогнозы нашей настроенной регрессии с ядром Риджа.

start_time = time.time()
predictions_kr = kernel_ridge_tuned.predict(data)
print(f"Time for KernelRidge predict: {time.time() - start_time:.3f} seconds")
Time for KernelRidge predict: 0.001 seconds
plt.plot(data, target, label="True signal", linewidth=2, linestyle="dashed")
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
plt.plot(
    data,
    predictions_kr,
    label="Kernel ridge",
    linewidth=2,
    linestyle="dashdot",
)
plt.legend(loc="lower right")
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title(
    "Kernel ridge regression with an exponential sine squared\n "
    "kernel using tuned hyperparameters"
)
Kernel ridge regression with an exponential sine squared  kernel using tuned hyperparameters

Мы получаем гораздо более точную модель. Мы по-прежнему наблюдаем некоторые ошибки, в основном из-за шума, добавленного в набор данных.

Регрессия с гауссовым процессом

Теперь мы будем использовать GaussianProcessRegressor для подгонки того же набора данных. При обучении гауссова процесса гиперпараметры ядра оптимизируются во время процесса обучения. Нет необходимости в дополнительном поиске гиперпараметров. Здесь мы создаем несколько более сложное ядро, чем для регрессора Риджа с ядром: мы добавляем WhiteKernel, используемый для оценки шума в наборе данных.

from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import WhiteKernel

kernel = 1.0 * ExpSineSquared(1.0, 5.0, periodicity_bounds=(1e-2, 1e1)) + WhiteKernel(
    1e-1
)
gaussian_process = GaussianProcessRegressor(kernel=kernel)
start_time = time.time()
gaussian_process.fit(training_data, training_noisy_target)
print(
    f"Time for GaussianProcessRegressor fitting: {time.time() - start_time:.3f} seconds"
)
Time for GaussianProcessRegressor fitting: 0.031 seconds

Вычислительные затраты на обучение гауссова процесса намного меньше, чем у регрессии с ядром Риджа, использующей случайный поиск. Мы можем проверить параметры вычисленных ядер.

gaussian_process.kernel_
0.675**2 * ExpSineSquared(length_scale=1.34, periodicity=6.57) + WhiteKernel(noise_level=0.182)

Действительно, мы видим, что параметры были оптимизированы. Посмотрев на параметр periodicity, мы видим, что мы нашли период, близкий к теоретическому значению \(2 \pi\). Теперь мы можем посмотреть на прогнозы нашей модели.

start_time = time.time()
mean_predictions_gpr, std_predictions_gpr = gaussian_process.predict(
    data,
    return_std=True,
)
print(
    f"Time for GaussianProcessRegressor predict: {time.time() - start_time:.3f} seconds"
)
Time for GaussianProcessRegressor predict: 0.002 seconds
plt.plot(data, target, label="True signal", linewidth=2, linestyle="dashed")
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
# Plot the predictions of the kernel ridge
plt.plot(
    data,
    predictions_kr,
    label="Kernel ridge",
    linewidth=2,
    linestyle="dashdot",
)
# Plot the predictions of the gaussian process regressor
plt.plot(
    data,
    mean_predictions_gpr,
    label="Gaussian process regressor",
    linewidth=2,
    linestyle="dotted",
)
plt.fill_between(
    data.ravel(),
    mean_predictions_gpr - std_predictions_gpr,
    mean_predictions_gpr + std_predictions_gpr,
    color="tab:green",
    alpha=0.2,
)
plt.legend(loc="lower right")
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title("Comparison between kernel ridge and gaussian process regressor")
Comparison between kernel ridge and gaussian process regressor

Мы наблюдаем, что результаты регрессора с ядром Риджа и гауссова процесса близки. Однако регрессор с гауссовым процессом также предоставляет информацию об неопределенности, которая недоступна с регрессором с ядром Риджа. Благодаря вероятностной формулировке целевых функций, гауссов процесс может выводить стандартное отклонение (или ковариацию) вместе со средними прогнозами целевых функций.

Однако это имеет свою цену: время вычисления прогнозов выше с гауссовым процессом.

Заключение

Можно подвести итог относительно возможности экстраполяции двух моделей. Действительно, мы предоставили только начало сигнала в качестве набора обучающих данных. Использование периодического ядра заставляет нашу модель повторять найденный на обучающем наборе паттерн. Используя эту информацию о ядре вместе с возможностью обеих моделей для экстраполяции, мы наблюдаем, что модели будут продолжать предсказывать синусоидальный паттерн.

Гауссовский процесс позволяет комбинировать ядра. Таким образом, мы могли бы связать ядро экспоненциального синуса в квадрате с ядром радиальной базисной функции.

from sklearn.gaussian_process.kernels import RBF

kernel = 1.0 * ExpSineSquared(1.0, 5.0, periodicity_bounds=(1e-2, 1e1)) * RBF(
    length_scale=15, length_scale_bounds="fixed"
) + WhiteKernel(1e-1)
gaussian_process = GaussianProcessRegressor(kernel=kernel)
gaussian_process.fit(training_data, training_noisy_target)
mean_predictions_gpr, std_predictions_gpr = gaussian_process.predict(
    data,
    return_std=True,
)
plt.plot(data, target, label="True signal", linewidth=2, linestyle="dashed")
plt.scatter(
    training_data,
    training_noisy_target,
    color="black",
    label="Noisy measurements",
)
# Plot the predictions of the kernel ridge
plt.plot(
    data,
    predictions_kr,
    label="Kernel ridge",
    linewidth=2,
    linestyle="dashdot",
)
# Plot the predictions of the gaussian process regressor
plt.plot(
    data,
    mean_predictions_gpr,
    label="Gaussian process regressor",
    linewidth=2,
    linestyle="dotted",
)
plt.fill_between(
    data.ravel(),
    mean_predictions_gpr - std_predictions_gpr,
    mean_predictions_gpr + std_predictions_gpr,
    color="tab:green",
    alpha=0.2,
)
plt.legend(loc="lower right")
plt.xlabel("data")
plt.ylabel("target")
_ = plt.title("Effect of using a radial basis function kernel")
Effect of using a radial basis function kernel

Влияние использования ядра радиальной базисной функции будет ослаблять эффект периодичности, как только в обучающем наборе не будет доступно ни одного образца. По мере того, как тестовые образцы удаляются от обучающих, прогнозы сходятся к своему среднему значению, а также увеличивается их стандартное отклонение.

Общее время выполнения скрипта: (0 минут 4.962 секунды)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_compare_gpr_krr.ipynb

Download Python source code: plot_compare_gpr_krr.py

Download zipped: plot_compare_gpr_krr.zip

Связанные примеры

Регрессия Гауссовских процессов: базовый вводный пример

HuberRegressor по сравнению с Ridge на наборе данных с сильными выбросами

График коэффициентов Ridge в зависимости от регуляризации

Возможность регрессии Гауссовских процессов (GPR) оценивать уровень шума в данных

© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/auto_examples/gaussian_process/plot_compare_gpr_krr.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API