Прогнозирование уровня CO2 на наборе данных Mona Loa с использованием гауссовской регрессии (GPR)#

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

Ссылки

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

Построить набор данных#

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

from sklearn.datasets import fetch_openml

co2 = fetch_openml(data_id=41187, as_frame=True)
co2.frame.head()
год month day вес флаг станция 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()
форма: (5, 2)
датаco2
датаf64
1958-03-29316.1
1958-04-05317.3
1958-04-12317.6
1958-04-19317.5
1958-04-26316.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")
)

Небольшие нерегулярности объясняются компонентом ядра рационального квадрата, чьи параметры длины и альфа, которые количественно определяют диффузность длин, должны быть определены. Ядро рационального квадрата эквивалентно ядру 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.


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

  • обучающие данные для проверки качества соответствия;

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

Таким образом, мы создаем синтетические данные с 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.736 секунд)

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

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

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

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

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

Сравнение ядерной гребневой регрессии и регрессии по методу Гауссовских процессов

Сравнение ядерной гребневой регрессии и регрессии по методу Гауссовских процессов

Гауссовский процесс классификации (GPC) на наборе данных iris

Гауссовский процесс классификации (GPC) на наборе данных iris

Галерея, созданная Sphinx-Gallery