Python实现基于高斯过程(Gaussian Process)的贝叶斯优化与不确定性估计

Python实现基于高斯过程(Gaussian Process)的贝叶斯优化与不确定性估计

大家好,今天我们来深入探讨一个在优化问题中非常强大的工具:基于高斯过程的贝叶斯优化,以及如何利用高斯过程进行不确定性估计。我们将重点关注使用Python实现这些概念。

1. 引言:优化的挑战与贝叶斯优化的优势

在机器学习、工程设计等领域,我们经常需要优化一个目标函数,找到使其达到最大值或最小值的参数组合。然而,很多实际问题中的目标函数往往具有以下挑战:

  • 非凸性: 存在多个局部最优解,传统的梯度下降方法容易陷入局部最优。
  • 黑盒性: 我们无法获得目标函数的梯度信息,只能通过评估不同参数组合的结果来了解其性质。
  • 评估代价高昂: 每次评估目标函数都需要耗费大量的计算资源或时间,例如运行一次复杂的模拟。

贝叶斯优化(Bayesian Optimization)是一种解决这些挑战的有效方法。它通过建立目标函数的概率模型(通常是高斯过程),并利用采集函数(Acquisition Function)来指导搜索过程,从而在尽可能少的评估次数下找到全局最优解。

2. 高斯过程 (Gaussian Process) 回顾

高斯过程是一种强大的非参数概率模型,它定义了一个函数上的概率分布。简单来说,高斯过程认为函数值服从多元高斯分布。给定一些观测数据,我们可以使用高斯过程来预测未知点的函数值,并估计预测结果的不确定性。

一个高斯过程由它的均值函数 m(x) 和协方差函数 k(x, x’) 完全定义,其中 xx’ 是输入空间中的两个点。

  • 均值函数 m(x): 表示函数在点 x 的期望值,通常设置为0。
  • 协方差函数 k(x, x’): 描述了函数在点 xx’ 之间的相关性。常用的协方差函数包括:

    • 径向基函数 (RBF) / 平方指数核 (Squared Exponential Kernel):

      k(x, x') = σ^2 * exp(-||x - x'||^2 / (2 * l^2))

      其中,σ^2 是信号方差,l 是长度尺度。RBF核假设附近的点具有更高的相关性,并且可以捕捉平滑的函数。

    • Matérn核:

      Matérn核是RBF核的推广,它引入了一个参数 ν 来控制函数的平滑度。Matérn 5/2核是一个常用的选择:

      k(x, x') = σ^2 * (1 + sqrt(5) * ||x - x'|| / l + 5 * ||x - x'||^2 / (3 * l^2)) * exp(-sqrt(5) * ||x - x'|| / l)

      Matérn核比RBF核更灵活,可以适应不同平滑度的函数。
      3. 高斯过程的Python实现

我们可以使用 scikit-learn 库来实现高斯过程。

import numpy as np
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C

# 定义核函数
kernel = C(1.0, (1e-3, 1e3)) * RBF(1.0, (1e-2, 1e2))

# 创建高斯过程回归模型
gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=9)

# 训练数据
X = np.atleast_2d([1., 3., 5., 6., 7., 8.]).T
y = np.array([0.5, 1.2, 3.1, 4.2, 5.1, 5.4])

# 训练模型
gp.fit(X, y)

# 预测新的数据点
x_pred = np.atleast_2d(np.linspace(0, 10, 100)).T
y_pred, sigma = gp.predict(x_pred, return_std=True) # sigma 是标准差,用于衡量不确定性

# 可视化结果
import matplotlib.pyplot as plt

plt.figure()
plt.plot(X, y, 'r.', markersize=10, label='Observations')
plt.plot(x_pred, y_pred, 'b-', label='Prediction')
plt.fill(np.concatenate([x_pred, x_pred[::-1]]),
         np.concatenate([y_pred - 1.9600 * sigma,
                        (y_pred + 1.9600 * sigma)[::-1]]),
         alpha=.5, fc='b', ec='None', label='95% confidence interval')
plt.xlabel('$x$')
plt.ylabel('$f(x)$')
plt.legend(loc='upper left')
plt.title('Gaussian Process Regression')
plt.show()

这段代码首先定义了一个RBF核函数,并创建了一个高斯过程回归模型。然后,我们使用一些训练数据来训练模型,并预测新的数据点。gp.predict(x_pred, return_std=True) 函数返回预测值和标准差,标准差用于衡量预测的不确定性。最后,我们使用matplotlib库将结果可视化,包括观测数据、预测曲线和95%置信区间。

4. 贝叶斯优化的工作原理

贝叶斯优化通过迭代的方式,不断地更新目标函数的概率模型,并利用采集函数来选择下一个需要评估的参数组合。其基本步骤如下:

  1. 初始化: 选择一些初始的参数组合,并评估目标函数。
  2. 建立概率模型: 使用观测数据(参数组合及其对应的目标函数值)来建立目标函数的概率模型。通常使用高斯过程作为概率模型。
  3. 定义采集函数: 采集函数用于衡量不同参数组合的潜在价值。它平衡了探索(探索未知的区域)和利用(利用已知的最优区域)。
  4. 选择下一个评估点: 找到使采集函数最大化的参数组合,作为下一个需要评估的点。
  5. 评估目标函数: 评估目标函数在选定的参数组合上的值。
  6. 更新概率模型: 将新的观测数据添加到已有的数据集中,并更新概率模型。
  7. 重复步骤3-6,直到达到停止条件(例如,评估次数达到上限)。

5. 采集函数 (Acquisition Function)

采集函数是贝叶斯优化的核心,它决定了下一个需要评估的参数组合。常用的采集函数包括:

  • 期望改善 (Expected Improvement, EI): EI计算了超越当前最优值的期望。

    EI(x) = E[max(0, f(x) - f(x^+))]

    其中,f(x) 是目标函数在高斯过程下的预测值,f(x^+) 是当前最优值。EI鼓励选择那些既有可能超越当前最优值,又具有较高不确定性的区域。

  • 概率改善 (Probability of Improvement, PI): PI计算了超越当前最优值的概率。

    PI(x) = P(f(x) > f(x^+))

    PI只关注超越当前最优值的概率,忽略了改善的幅度。

  • 置信上限 (Upper Confidence Bound, UCB): UCB将预测均值和预测标准差结合起来,鼓励探索未知的区域。

    UCB(x) = μ(x) + κ * σ(x)

    其中,μ(x) 是预测均值,σ(x) 是预测标准差,κ 是一个控制探索程度的参数。

6. 贝叶斯优化的Python实现

这里我们使用scipy.optimize库的minimize函数来优化采集函数,并用scikit-learn的高斯过程回归模型。

import numpy as np
from scipy.stats import norm
from scipy.optimize import minimize
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C

# 定义目标函数 (这里使用一个简单的示例函数)
def objective_function(x):
    return (x-2)**2 + np.sin(x)

# 定义期望改善 (Expected Improvement) 采集函数
def expected_improvement(x, gp, y_max, xi=0.01):
    mu, sigma = gp.predict(x.reshape(-1, 1), return_std=True)
    sigma = sigma.reshape(-1, 1)
    a = (mu - y_max - xi)
    Z = a / sigma
    EI = a * norm.cdf(Z) + sigma * norm.pdf(Z)
    return EI

# 定义采集函数的优化器
def acquisition_optimizer(gp, y_max, bounds, n_restarts=5):
    best_x = None
    best_ei = -np.inf
    for i in range(n_restarts):
        x0 = np.random.uniform(bounds[0][0], bounds[0][1], size=1)  # 随机初始化
        res = minimize(lambda x: -expected_improvement(x, gp, y_max),  # 最小化负的 EI
                       x0,
                       bounds=bounds,
                       method='L-BFGS-B')
        if -res.fun > best_ei:
            best_ei = -res.fun
            best_x = res.x
    return best_x

# 贝叶斯优化主循环
def bayesian_optimization(objective_function, bounds, n_iterations=10, n_initial_points=5):
    # 初始化数据
    X_sample = np.random.uniform(bounds[0][0], bounds[0][1], size=(n_initial_points, 1))
    y_sample = np.array([objective_function(x) for x in X_sample]).reshape(-1, 1)

    # 定义高斯过程模型
    kernel = C(1.0, (1e-3, 1e3)) * RBF(1.0, (1e-2, 1e2))
    gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=9)

    # 优化循环
    for i in range(n_iterations):
        # 拟合高斯过程
        gp.fit(X_sample, y_sample)

        # 找到当前最优值
        y_max = y_sample.max()

        # 选择下一个评估点
        next_x = acquisition_optimizer(gp, y_max, bounds)

        # 评估目标函数
        next_y = objective_function(next_x)

        # 更新数据
        X_sample = np.vstack((X_sample, next_x))
        y_sample = np.vstack((y_sample, next_y))

        print(f"Iteration {i+1}: x = {next_x[0]:.4f}, y = {next_y:.4f}")

    # 返回最优解
    best_index = np.argmin(y_sample)
    return X_sample[best_index], y_sample[best_index]

# 定义搜索空间
bounds = [(-5, 5)]  # x 的范围是 -5 到 5

# 运行贝叶斯优化
best_x, best_y = bayesian_optimization(objective_function, bounds)

print(f"Best x: {best_x[0]:.4f}, Best y: {best_y:.4f}")

# 可视化贝叶斯优化的过程
import matplotlib.pyplot as plt

# 重新运行贝叶斯优化,并保存每次迭代的结果
def bayesian_optimization_with_history(objective_function, bounds, n_iterations=10, n_initial_points=5):
    X_sample = np.random.uniform(bounds[0][0], bounds[0][1], size=(n_initial_points, 1))
    y_sample = np.array([objective_function(x) for x in X_sample]).reshape(-1, 1)

    kernel = C(1.0, (1e-3, 1e3)) * RBF(1.0, (1e-2, 1e2))
    gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=9)

    X_history = [X_sample.copy()]
    y_history = [y_sample.copy()]

    for i in range(n_iterations):
        gp.fit(X_sample, y_sample)
        y_max = y_sample.max()
        next_x = acquisition_optimizer(gp, y_max, bounds)
        next_y = objective_function(next_x)
        X_sample = np.vstack((X_sample, next_x))
        y_sample = np.vstack((y_sample, next_y))

        X_history.append(X_sample.copy())
        y_history.append(y_sample.copy())

    best_index = np.argmin(y_sample)
    return X_sample[best_index], y_sample[best_index], X_history, y_history

best_x, best_y, X_history, y_history = bayesian_optimization_with_history(objective_function, bounds)

# 创建一个网格用于绘制目标函数和高斯过程
x_grid = np.linspace(bounds[0][0], bounds[0][1], 100).reshape(-1, 1)
y_true = np.array([objective_function(x) for x in x_grid]).reshape(-1, 1)

# 绘制每次迭代的结果
n_iterations = len(X_history) - 1
fig, axes = plt.subplots(n_iterations, 1, figsize=(8, 2 * n_iterations))

for i in range(n_iterations):
    # 绘制目标函数
    axes[i].plot(x_grid, y_true, label='Objective Function', color='black')

    # 绘制观测数据
    axes[i].plot(X_history[i], y_history[i], 'r.', markersize=10, label='Observations')

    # 绘制高斯过程的预测
    gp.fit(X_history[i], y_history[i])
    y_pred, sigma = gp.predict(x_grid, return_std=True)
    axes[i].plot(x_grid, y_pred, 'b-', label='GP Mean')
    axes[i].fill_between(x_grid.flatten(),
                         y_pred - 1.96 * sigma,
                         y_pred + 1.96 * sigma,
                         alpha=0.2, color='blue', label='GP Uncertainty (95% CI)')

    # 绘制下一个评估点
    next_x = acquisition_optimizer(gp, y_history[i].max(), bounds)
    axes[i].axvline(x=next_x[0], color='green', linestyle='--', label='Next Evaluation Point')

    axes[i].set_title(f'Iteration {i+1}')
    axes[i].legend(loc='upper left')

plt.tight_layout()
plt.show()

这段代码实现了一个简单的贝叶斯优化算法。它首先定义了目标函数和期望改善采集函数,然后使用scipy.optimize.minimize函数来优化采集函数。在主循环中,它不断地更新高斯过程模型,选择下一个评估点,并评估目标函数。最后,它返回找到的最优解。

7. 不确定性估计的应用

高斯过程不仅可以用于预测函数值,还可以估计预测结果的不确定性。不确定性估计在很多应用中都非常重要,例如:

  • 主动学习 (Active Learning): 选择那些具有较高不确定性的样本进行标注,可以提高模型的学习效率。
  • 风险管理: 在决策过程中,考虑不确定性可以帮助我们更好地评估风险。
  • 探索-利用平衡: 在贝叶斯优化中,利用不确定性信息来平衡探索和利用,可以更快地找到全局最优解。

8. 总结:贝叶斯优化与高斯过程,解决复杂优化问题的利器

我们详细介绍了基于高斯过程的贝叶斯优化,以及如何使用Python实现它。 贝叶斯优化在高代价黑盒优化问题中表现出色,并通过高斯过程进行不确定性估计。 理解并掌握这些技术,能有效解决实际应用中的复杂优化问题。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注