为什么gpytorch似乎不如scikit-learn准确?

我最近发现了gpytorch (https://github.com/cornellius-gp/gpytorch)。它似乎是一个将GPR集成到pytorch中的优秀工具包。初步测试也非常积极。使用gpytorch,可以利用GPU的强大性能以及智能算法,相较于其他工具包如scikit-learn,性能得到了提升。

然而,我发现估计所需的超参数要困难得多。在scikit-learn中,这部分工作在后台进行,并且非常稳健。我希望从社区获得一些关于原因的反馈,并讨论是否有比gpytorch文档中示例提供的更好的方法来估计这些参数。

为了比较,我使用了gpytorch官方页面上提供的一个示例代码(https://github.com/cornellius-gp/gpytorch/blob/master/examples/03_Multitask_GP_Regression/Multitask_GP_Regression.ipynb),并在两个部分进行了修改:

  1. 我使用了不同的核函数(gpytorch.kernels.MaternKernel(nu=2.5) 代替 gpytorch.kernels.RBFKernel())
  2. 我使用了不同的输出函数

下面,我首先提供使用gpytorch的代码。随后,我提供scikit-learn的代码。最后,我比较结果

导入(用于gpytorch和scikit-learn):

import mathimport torchimport numpy as npimport gpytorch

生成数据(用于gpytorch和scikit-learn):

n = 20train_x = torch.zeros(pow(n, 2), 2)for i in range(n):    for j in range(n):        # 每个坐标从0到1变化,n=100步        train_x[i * n + j][0] = float(i) / (n-1)        train_x[i * n + j][1] = float(j) / (n-1)train_y_1 = (torch.sin(train_x[:, 0]) + torch.cos(train_x[:, 1]) * (2 * math.pi) + torch.randn_like(train_x[:, 0]).mul(0.01))/4train_y_2 = torch.sin(train_x[:, 0]) + torch.cos(train_x[:, 1]) * (2 * math.pi) + torch.randn_like(train_x[:, 0]).mul(0.01)train_y = torch.stack([train_y_1, train_y_2], -1)test_x = torch.rand((n, len(train_x.shape)))test_y_1 = (torch.sin(test_x[:, 0]) + torch.cos(test_x[:, 1]) * (2 * math.pi) + torch.randn_like(test_x[:, 0]).mul(0.01))/4test_y_2 = torch.sin(test_x[:, 0]) + torch.cos(test_x[:, 1]) * (2 * math.pi) + torch.randn_like(test_x[:, 0]).mul(0.01)test_y = torch.stack([test_y_1, test_y_2], -1)

现在来进行如引用的文档中所述的示例中的估计:

torch.manual_seed(2) # 为了更稳健的比较class MultitaskGPModel(gpytorch.models.ExactGP):    def __init__(self, train_x, train_y, likelihood):        super(MultitaskGPModel, self).__init__(train_x, train_y, likelihood)        self.mean_module = gpytorch.means.MultitaskMean(            gpytorch.means.ConstantMean(), num_tasks=2        )        self.covar_module = gpytorch.kernels.MultitaskKernel(            gpytorch.kernels.MaternKernel(nu=2.5), num_tasks=2, rank=1        )    def forward(self, x):        mean_x = self.mean_module(x)        covar_x = self.covar_module(x)        return gpytorch.distributions.MultitaskMultivariateNormal(mean_x, covar_x)likelihood = gpytorch.likelihoods.MultitaskGaussianLikelihood(num_tasks=2)model = MultitaskGPModel(train_x, train_y, likelihood)# 寻找最优模型超参数model.train()likelihood.train()# 使用adam优化器optimizer = torch.optim.Adam([    {'params': model.parameters()},  # 包括GaussianLikelihood参数], lr=0.1)# GPs的"损失" - 边缘对数似然mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model)n_iter = 50for i in range(n_iter):    optimizer.zero_grad()    output = model(train_x)    loss = -mll(output, train_y)    loss.backward()    # print('Iter %d/%d - Loss: %.3f' % (i + 1, n_iter, loss.item()))    optimizer.step()# 设置为评估模式model.eval()likelihood.eval()# 进行预测with torch.no_grad(), gpytorch.settings.fast_pred_var():    predictions = likelihood(model(test_x))    mean = predictions.mean    lower, upper = predictions.confidence_region()test_results_gpytorch = np.median((test_y - mean) / test_y, axis=0)

下面,我提供scikit-learn的代码。这稍微方便一些^^:

from sklearn.gaussian_process import GaussianProcessRegressorfrom sklearn.gaussian_process.kernels import WhiteKernel, Maternkernel = 1.0 * Matern(length_scale=0.1, length_scale_bounds=(1e-5, 1e5), nu=2.5) \         + WhiteKernel()gp = GaussianProcessRegressor(kernel=kernel, alpha=0.0).fit(train_x.numpy(),                                                            train_y.numpy())# x_interpolation = test_x.detach().numpy()[np.newaxis, :].transpose()y_mean_interpol, y_std_norm = gp.predict(test_x.numpy(), return_std=True)test_results_scitlearn = np.median((test_y.numpy() - y_mean_interpol) / test_y.numpy(), axis=0)

最后我比较结果:

comparisson = (test_results_scitlearn - test_results_gpytorch)/test_results_scitlearnprint('变量1: scikit-learn更准确的倍数: ' + str(abs(comparisson[0])))print('变量2: scikit-learn更准确的倍数: ' + str(comparisson[1]))

遗憾的是,我没有找到一种简单的方法来为scikit-learn固定种子。最后一次运行代码时,它返回:

变量1: scikit-learn更准确的倍数: 11.362540360431087

变量2: scikit-learn更准确的倍数: 29.64760087022618

对于gpytorch,我认为优化器可能运行在某些局部最优点。但我无法想到任何更稳健的优化算法,同时仍然使用pytorch。

期待大家的建议!

Lazloo


回答:

(我也在你为此创建的GitHub问题中回答了你的问题 这里)

主要原因是你使用了scikit-learn和gpytorch中的不同模型。特别是,scikit-learn在多输出设置中默认学习独立的GP(例如,讨论见这里)。在GPyTorch中,你使用了Bonilla等人2008年引入的多任务GP方法(见Bonilla et al, 2008)。纠正这一差异后得到:

test_results_gpytorch = [5.207913e-04 -8.469360e-05]

test_results_scitlearn = [3.65288816e-04 4.79017145e-05]

Related Posts

如何使用Google Protobuf解析、编辑和生成object_detection/pipeline.config文件

我在一个常见的集成学习范式中训练多个模型,目前我在处理…

我的GridSearchCV不起作用,我不知道为什么

大家好,我在使用GridSearchCV时遇到了问题,…

Keras: 两个同时进行的层,其中一个对前一层的输出进行卷积

我想实现这样的模型连接: 输入图像1 -> 卷积层1 …

如何将行数据转换为列数据而不使用独热编码

我有一个如下所示的数据集。 MonthDate Day…

使用 ML Kit 与 NNAPI

我正在尝试在运行 Android 9 的设备上使用新的…

Vowpal Wabbit 可能的哈希冲突

我在VW中生成了一个模型,并且在相同的数据上生成了两个…

发表回复

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