Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.

Прогнозирование уровня CO2 на наборе данных Мауна-Лоа с помощью регрессии гауссовского процесса (GPR)

Этот пример основан на разделе 5.4.3 книги «Gaussian Processes for Machine Learning» [1]. В нём показан пример разработки сложного ядра и оптимизации гиперпараметров с помощью градиентного подъёма по логарифму маргинального правдоподобия. Данные представляют собой среднемесячные концентрации CO2 в атмосфере (в частях на миллион по объёму (ppm)), измеренные в обсерватории Мауна-Лоа на Гавайях в период с 1958 по 2001 год. Цель — смоделировать концентрацию CO2 как функцию времени \(t\) и выполнить экстраполяцию на годы после 2001-го.

Литература

[1]

Rasmussen, Carl Edward. «Gaussian processes in machine learning». Летняя школа по машинному обучению. Springer, Berlin, Heidelberg, 2003.

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

Создание набора данных

Мы получим набор данных из измерений проб воздуха, собранных в обсерватории Мауна-Лоа. Нас интересует оценка концентрации CO2 и её экстраполяция на последующие годы. Сначала загрузим исходный набор данных из OpenML в виде фрейма данных pandas. Позже мы заменим его на Polars, когда fetch_openml добавит встроенную поддержку этого формата.

from sklearn.datasets import fetch_openml

co2 = fetch_openml(data_id=41187, as_frame=True)
co2.frame.head()
year month day weight flag station co2
0 1958 3 29 4 0 MLO 316.1
1 1958 4 5 6 0 MLO 317.3
2 1958 4 12 4 0 MLO 317.6
3 1958 4 19 6 0 MLO 317.5
4 1958 4 26 2 0 MLO 316.4


Сначала обработаем исходный фрейм данных: создадим столбец с датой и выберем его вместе со столбцом CO2.

import polars as pl

co2_data = pl.DataFrame(co2.frame[["year", "month", "day", "co2"]]).select(
    pl.date("year", "month", "day"), "co2"
)
co2_data.head()
shape: (5, 2)
date co2
date f64
1958-03-29 316.1
1958-04-05 317.3
1958-04-12 317.6
1958-04-19 317.5
1958-04-26 316.4


co2_data["date"].min(), co2_data["date"].max()
(datetime.date(1958, 3, 29), datetime.date(2001, 12, 29))

Как видно, данные о концентрации CO2 собраны в некоторые дни с марта 1958 года по декабрь 2001 года. Построим график исходных данных, чтобы лучше понять их.

import matplotlib.pyplot as plt

plt.plot(co2_data["date"], co2_data["co2"])
plt.xlabel("date")
plt.ylabel("CO$_2$ concentration (ppm)")
_ = plt.title("Raw air samples measurements from the Mauna Loa Observatory")
Raw air samples measurements from the Mauna Loa Observatory

Предварительно обработаем набор данных, рассчитав среднемесячное значение и исключив месяцы, для которых не проводились измерения. Такая обработка сгладит данные.

co2_data = (
    co2_data.sort(by="date")
    .group_by_dynamic("date", every="1mo")
    .agg(pl.col("co2").mean())
    .drop_nulls()
)
plt.plot(co2_data["date"], co2_data["co2"])
plt.xlabel("date")
plt.ylabel("Monthly average of CO$_2$ concentration (ppm)")
_ = plt.title(
    "Monthly average of air samples measurements\nfrom the Mauna Loa Observatory"
)
Monthly average of air samples measurements from the Mauna Loa Observatory

В этом примере мы будем предсказывать концентрацию CO2 как функцию даты. Также нас интересует экстраполяция на последующие годы после 2001-го.

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

X = co2_data.select(
    pl.col("date").dt.year() + pl.col("date").dt.month() / 12
).to_numpy()
y = co2_data["co2"].to_numpy()

Выбор подходящего ядра

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

Долгосрочный восходящий тренд можно аппроксимировать с помощью ядра радиальных базисных функций (RBF) с большим параметром масштаба длины. Ядро RBF с большим масштабом длины обеспечивает гладкость этого компонента. При этом возрастающий тренд не задаётся жёстко, чтобы оставить нашей модели некоторую свободу. Конкретные значения масштаба длины и амплитуды являются свободными гиперпараметрами.

from sklearn.gaussian_process.kernels import RBF

long_term_trend_kernel = 50.0**2 * RBF(length_scale=50.0)

Сезонная изменчивость описывается периодическим ядром на основе экспоненты квадрата синуса с фиксированным периодом в 1 год. Масштаб длины этого периодического компонента, определяющий его гладкость, является свободным параметром. Чтобы допустить затухание при отклонении от точной периодичности, используется произведение с ядром RBF. Масштаб длины этого компонента RBF определяет время затухания и является ещё одним свободным параметром. Этот тип ядра также называют локально-периодическим.

from sklearn.gaussian_process.kernels import ExpSineSquared

seasonal_kernel = (
    2.0**2
    * RBF(length_scale=100.0)
    * ExpSineSquared(length_scale=1.0, periodicity=1.0, periodicity_bounds="fixed")
)

Небольшие нерегулярные колебания описываются компонентом рационально-квадратичного ядра; необходимо определить его масштаб длины и параметр alpha, который характеризует разброс масштабов длины. Рационально-квадратичное ядро эквивалентно ядру RBF с несколькими масштабами длины и лучше учитывает разнообразные нерегулярные колебания.

from sklearn.gaussian_process.kernels import RationalQuadratic

irregularities_kernel = 0.5**2 * RationalQuadratic(length_scale=1.0, alpha=1.0)

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

from sklearn.gaussian_process.kernels import WhiteKernel

noise_kernel = 0.1**2 * RBF(length_scale=0.1) + WhiteKernel(
    noise_level=0.1**2, noise_level_bounds=(1e-5, 1e5)
)

Таким образом, итоговое ядро представляет собой сумму всех предыдущих ядер.

co2_kernel = (
    long_term_trend_kernel + seasonal_kernel + irregularities_kernel + noise_kernel
)
co2_kernel
50**2 * RBF(length_scale=50) + 2**2 * RBF(length_scale=100) * ExpSineSquared(length_scale=1, periodicity=1) + 0.5**2 * RationalQuadratic(alpha=1, length_scale=1) + 0.1**2 * RBF(length_scale=0.1) + WhiteKernel(noise_level=0.01)

Обучение модели и экстраполяция

Теперь можно обучить регрессор на основе гауссовского процесса на имеющихся данных. Чтобы следовать примеру из литературы, вычтем среднее значение из целевой переменной. Можно было бы использовать normalize_y=True. Однако в этом случае целевая переменная также масштабировалась бы (значения y делились бы на стандартное отклонение). Поэтому гиперпараметры разных ядер имели бы иной смысл, поскольку выражались бы не в ppm.

from sklearn.gaussian_process import GaussianProcessRegressor

y_mean = y.mean()
gaussian_process = GaussianProcessRegressor(kernel=co2_kernel, normalize_y=False)
gaussian_process.fit(X, y - y_mean)
GaussianProcessRegressor(kernel=50**2 * RBF(length_scale=50) + 2**2 * RBF(length_scale=100) * ExpSineSquared(length_scale=1, periodicity=1) + 0.5**2 * RationalQuadratic(alpha=1, length_scale=1) + 0.1**2 * RBF(length_scale=0.1) + WhiteKernel(noise_level=0.01))
В среде Jupyter повторно выполните эту ячейку, чтобы показать представление HTML, или доверьте работу блокноту.
На GitHub невозможно отобразить представление HTML; попробуйте открыть эту страницу на nbviewer.org.
Параметры
kernel kernel: экземпляр ядра, по умолчанию=None

Ядро, задающее ковариационную функцию ГП.
Если передано значение `None`,
по умолчанию используется ядро `ConstantKernel() * RBF()`.
Обратите внимание, что
гиперпараметры ядра оптимизируются во время обучения,
если только границы не помечены как `"fixed"`
или аргумент `optimizer` не установлен в `None`.
50**2 * RBF(l...se_level=0.01)
kernel__k1 50**2 * RBF(l...ength_scale=1)
kernel__k2 0.1**2 * RBF(...se_level=0.01)
kernel__k1__k1 50**2 * RBF(l...periodicity=1)
kernel__k1__k2 0.5**2 * Rati...ength_scale=1)
kernel__k1__k1__k1 50**2 * RBF(length_scale=50)
kernel__k1__k1__k2 2**2 * RBF(le...periodicity=1)
kernel__k1__k1__k1__k1 50**2
kernel__k1__k1__k1__k2 RBF(length_scale=50)
kernel__k1__k1__k1__k1__constant_value 2500.0
kernel__k1__k1__k1__k1__constant_value_bounds (1e-05, ...)
kernel__k1__k1__k1__k2__length_scale 50.0
kernel__k1__k1__k1__k2__length_scale_bounds (1e-05, ...)
kernel__k1__k1__k2__k1 2**2 * RBF(length_scale=100)
kernel__k1__k1__k2__k2 ExpSineSquare...periodicity=1)
kernel__k1__k1__k2__k1__k1 2**2
kernel__k1__k1__k2__k1__k2 RBF(length_scale=100)
kernel__k1__k1__k2__k1__k1__constant_value 4.0
kernel__k1__k1__k2__k1__k1__constant_value_bounds (1e-05, ...)
kernel__k1__k1__k2__k1__k2__length_scale 100.0
kernel__k1__k1__k2__k1__k2__length_scale_bounds (1e-05, ...)
kernel__k1__k1__k2__k2__length_scale 1.0
kernel__k1__k1__k2__k2__periodicity 1.0
kernel__k1__k1__k2__k2__length_scale_bounds (1e-05, ...)
kernel__k1__k1__k2__k2__periodicity_bounds 'fixed'
kernel__k1__k2__k1 0.5**2
kernel__k1__k2__k2 RationalQuadr...ength_scale=1)
kernel__k1__k2__k1__constant_value 0.25
kernel__k1__k2__k1__constant_value_bounds (1e-05, ...)
kernel__k1__k2__k2__length_scale 1.0
kernel__k1__k2__k2__alpha 1.0
kernel__k1__k2__k2__length_scale_bounds (1e-05, ...)
kernel__k1__k2__k2__alpha_bounds (1e-05, ...)
kernel__k2__k1 0.1**2 * RBF(length_scale=0.1)
kernel__k2__k2 WhiteKernel(noise_level=0.01)
kernel__k2__k1__k1 0.1**2
kernel__k2__k1__k2 RBF(length_scale=0.1)
kernel__k2__k1__k1__constant_value 0.010000000000000002
kernel__k2__k1__k1__constant_value_bounds (1e-05, ...)
kernel__k2__k1__k2__length_scale 0.1
kernel__k2__k1__k2__length_scale_bounds (1e-05, ...)
kernel__k2__k2__noise_level 0.010000000000000002
kernel__k2__k2__noise_level_bounds (1e-05, ...)
alpha alpha: число с плавающей точкой или ndarray формы (n_samples,), по умолчанию=1e-10

Значение, добавляемое к диагонали матрицы ядра во время обучения.
Это может предотвратить потенциальную численную проблему во время обучения,
гарантируя, что вычисленные значения образуют положительно определённую матрицу.
Также это можно интерпретировать как дисперсию дополнительного гауссовского
шума измерений в обучающих наблюдениях. Обратите внимание, что это
не то же самое, что использование `WhiteKernel`. Если передан массив, он должен
содержать столько же элементов, сколько и данные, используемые для обучения,
и будет применяться как зависящий от точки уровень шума. Возможность задать
уровень шума напрямую в виде параметра предусмотрена главным образом для
удобства и согласованности с :class:`~sklearn.linear_model.Ridge`.
Пример того, как параметр alpha управляет дисперсией шума в регрессии
гауссовского процесса, см. в разделе
:ref:`sphx_glr_auto_examples_gaussian_process_plot_gpr_noisy_targets.py`.
1e-10
optimizer optimizer: "fmin_l_bfgs_b", вызываемый объект или None, по умолчанию="fmin_l_bfgs_b"

Это может быть один из встроенных оптимизаторов для оптимизации
параметров ядра, указанный строкой, или внешний
оптимизатор, переданный в виде вызываемого объекта. Если передан вызываемый объект, он
должен иметь следующую сигнатуру::

def optimizer(obj_func, initial_theta, bounds):
# * 'obj_func': целевая функция, которую требуется минимизировать; она
# принимает гиперпараметры theta и необязательный флаг
# eval_gradient, определяющий, нужно ли возвращать
# градиент вместе со значением функции
# * 'initial_theta': начальное значение theta, которое могут
# использовать локальные оптимизаторы
# * 'bounds': границы значений theta
....
# Возвращаются найденные оптимальные гиперпараметры theta и
# соответствующее значение целевой функции.
return theta_opt, func_min

По умолчанию используется алгоритм L-BFGS-B из `scipy.optimize.minimize`.
Если передано значение None, параметры ядра остаются фиксированными.
Доступные встроенные оптимизаторы: `{'fmin_l_bfgs_b'}`.
'fmin_l_bfgs_b'
n_restarts_optimizer n_restarts_optimizer: целое число, по умолчанию=0

Количество перезапусков оптимизатора для поиска параметров ядра,
максимизирующих логарифм маргинального правдоподобия. Первый запуск
оптимизатора выполняется с начальными параметрами ядра,
а остальные (если есть) — со значениями theta, случайно выбранными
из пространства допустимых значений theta согласно логарифмически равномерному распределению. Если значение больше 0, все границы
должны быть конечными. Обратите внимание, что `n_restarts_optimizer == 0` означает,
что выполняется один запуск.
0
normalize_y normalize_y: логическое значение, по умолчанию=False

Нужно ли нормализовать целевые значения `y`, вычитая среднее
и масштабируя их до единичной дисперсии. Это рекомендуется в случаях,
когда используются априорные распределения с нулевым средним и единичной дисперсией. Обратите внимание: в этой
реализации нормализация отменяется перед выводом прогнозов ГП.

.. versionchanged:: 0.23
False
copy_X_train copy_X_train: логическое значение, по умолчанию=True

Если True, в объекте сохраняется постоянная копия обучающих данных.
В противном случае сохраняется только ссылка на обучающие данные,
из-за чего прогнозы могут измениться, если данные будут изменены
внешним образом.
True
n_targets n_targets: целое число, по умолчанию=None

Количество измерений целевых значений. Используется для определения числа
выходов при выборке из априорных распределений (например, при вызове
:meth:`sample_y` до :meth:`fit`). После вызова
:meth:`fit` этот параметр игнорируется.

.. versionadded:: 1.3
None
random_state random_state: целое число, экземпляр RandomState или None, по умолчанию=None

Определяет генерацию случайных чисел, используемых для инициализации центров.
Передайте целое число, чтобы получать воспроизводимые результаты при нескольких вызовах функции.
См. :term:`глоссарий <random_state>`.
None
Обученные атрибуты
Имя Тип Значение
L_ L_: объект, подобный массиву, формы (n_samples, n_samples)

Нижнетреугольное разложение Холецкого ядра в ``X_train_``.
ndarray[float64](521, 521) [[44.86, 0. , 0. ,..., 0. , 0. , 0. ], [44.85, 0.96, 0. ,..., 0. , 0. , 0. ], [44.83, 1.55, 0.75,..., 0. , 0. , 0. ], ..., [31.35, 2.17, 2. ,..., 0.29, 0. , 0. ], [31.31, 1.83, 1.85,..., 0.19, 0.29, 0. ], [31.29, 1.5 , 1.68,..., 0.16, 0.19, 0.29]]
X_train_ X_train_: объект, подобный массиву, формы (n_samples, n_features), или список объектов

Векторы признаков или другие представления обучающих данных (также
необходимые для предсказания).
ndarray[float64](521, 1) [[1958.25], [1958.33], [1958.42], ..., [2001.83], [2001.92], [2002. ]]
alpha_ alpha_: объект, подобный массиву, формы (n_samples,)

Двойственные коэффициенты обучающих точек в пространстве ядра.
ndarray[float64](521,) [-0.86, 2.37,-3.4 ,..., 1.17,-2.45, 2.96]
kernel_ kernel_: экземпляр ядра

Ядро, используемое для предсказаний. Структура ядра совпадает
со структурой переданного параметра, но содержит оптимизированные гиперпараметры.
Sum 44.8**2 * RBF..._level=0.0367)
log_marginal_likelihood_value_ log_marginal_likelihood_value_: число с плавающей точкой

Логарифм маргинального правдоподобия ``self.kernel_.theta``.
float64 -115.1
n_features_in_ n_features_in_: целое число

Количество признаков, обнаруженных во время :term:`fit`.

.. versionadded:: 0.24
int 1
y_train_ y_train_: объект, подобный массиву, формы (n_samples,) или (n_samples, n_targets)

Целевые значения в обучающих данных (также необходимые для предсказания).
ndarray[float64](521,) [-23.72,-22.62,-22.39,..., 28.23, 29.55, 31.2 ]


Теперь с помощью гауссовского процесса выполним предсказания для:

  • обучающих данных, чтобы оценить качество подгонки;
  • будущих данных, чтобы посмотреть, какую экстраполяцию выполняет модель.

Для этого создадим синтетические данные от 1958 года до текущего месяца. Кроме того, необходимо добавить среднее значение, вычтенное во время обучения.

import datetime

import numpy as np

today = datetime.datetime.now()
current_month = today.year + today.month / 12
X_test = np.linspace(start=1958, stop=current_month, num=1_000).reshape(-1, 1)
mean_y_pred, std_y_pred = gaussian_process.predict(X_test, return_std=True)
mean_y_pred += y_mean
plt.plot(X, y, color="black", linestyle="dashed", label="Measurements")
plt.plot(X_test, mean_y_pred, color="tab:blue", alpha=0.4, label="Gaussian process")
plt.fill_between(
    X_test.ravel(),
    mean_y_pred - std_y_pred,
    mean_y_pred + std_y_pred,
    color="tab:blue",
    alpha=0.2,
)
plt.legend()
plt.xlabel("Year")
plt.ylabel("Monthly average of CO$_2$ concentration (ppm)")
_ = plt.title(
    "Monthly average of air samples measurements\nfrom the Mauna Loa Observatory"
)
Monthly average of air samples measurements from the Mauna Loa Observatory

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

Интерпретация гиперпараметров ядра

Теперь рассмотрим гиперпараметры ядра.

gaussian_process.kernel_
44.8**2 * RBF(length_scale=51.6) + 2.64**2 * RBF(length_scale=91.5) * ExpSineSquared(length_scale=1.48, periodicity=1) + 0.536**2 * RationalQuadratic(alpha=2.89, length_scale=0.968) + 0.188**2 * RBF(length_scale=0.122) + WhiteKernel(noise_level=0.0367)

Итак, большая часть сигнала целевой переменной после вычитания среднего описывается долгосрочным восходящим трендом с амплитудой ~45 ppm и масштабом длины ~52 года. Амплитуда периодического компонента составляет ~2,6 ppm, время затухания — ~90 лет, а масштаб длины — ~1,5. Большое время затухания указывает на то, что этот компонент близок к сезонной периодичности. Амплитуда коррелированного шума составляет ~0,2 ppm при масштабе длины ~0,12 года; вклад белого шума — ~0,04 ppm. Таким образом, общий уровень шума очень низок, что указывает на то, что модель очень хорошо описывает данные.

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

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_gpr_co2.ipynb

Download Python source code: plot_gpr_co2.py

Download zipped: plot_gpr_co2.zip

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

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

Способность регрессии гауссовского процесса (GPR) оценивать уровень шума в данных

Сравнение регрессии с гребневой регуляризацией на основе ядра и регрессии гауссовского процесса

Классификация гауссовским процессом (GPC) на наборе данных iris

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/gaussian_process/plot_gpr_co2.html

Spec-Zone.ru

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