访问Pyro Paramstore的不同方法会产生不同的结果

我在学习Pyro的预测入门教程,并试图在模型训练后访问学习到的参数时,发现使用不同的访问方法会得到一些参数的不同结果(而其他参数的结果相同)。

以下是教程中简化的可重现代码:

import torchimport pyroimport pyro.distributions as distfrom pyro.contrib.examples.bart import load_bart_odfrom pyro.contrib.forecast import ForecastingModel, Forecasterpyro.enable_validation(True)pyro.clear_param_store()pyro.__version__# '1.3.1'torch.__version__# '1.5.0+cu101'# 导入并准备数据dataset = load_bart_od()T, O, D = dataset["counts"].shapedata = dataset["counts"][:T // (24 * 7) * 24 * 7].reshape(T // (24 * 7), -1).sum(-1).log()data = data.unsqueeze(-1)T0 = 0              # 开始T2 = data.size(-2)  # 结束T1 = T2 - 52        # 训练/测试分割# 定义模型类class Model1(ForecastingModel):    def model(self, zero_data, covariates):        data_dim = zero_data.size(-1)          feature_dim = covariates.size(-1)        bias = pyro.sample("bias", dist.Normal(0, 10).expand([data_dim]).to_event(1))        weight = pyro.sample("weight", dist.Normal(0, 0.1).expand([feature_dim]).to_event(1))        prediction = bias + (weight * covariates).sum(-1, keepdim=True)        assert prediction.shape[-2:] == zero_data.shape        noise_scale = pyro.sample("noise_scale", dist.LogNormal(-5, 5).expand([1]).to_event(1))        noise_dist = dist.Normal(0, noise_scale)        self.predict(noise_dist, prediction)# 拟合模型pyro.set_rng_seed(1)pyro.clear_param_store()time = torch.arange(float(T2)) / 365covariates = torch.stack([time], dim=-1)forecaster = Forecaster(Model1(), data[:T1], covariates[:T1], learning_rate=0.1)

到目前为止,一切顺利;现在,我想要检查存储在Paramstore中的学习到的潜在参数。似乎有不止一种方法可以做到这一点;使用get_all_param_names()方法:

for name in pyro.get_param_store().get_all_param_names():    print(name, pyro.param(name).data.numpy())

我得到

AutoNormal.locs.bias [14.585433]AutoNormal.scales.bias [0.00631594]AutoNormal.locs.weight [0.11947815]AutoNormal.scales.weight [0.00922901]AutoNormal.locs.noise_scale [-2.0719821]AutoNormal.scales.noise_scale [0.03469057]

但是使用named_parameters()方法:

pyro.get_param_store().named_parameters()

对于位置(locs)参数给出相同的值,但所有scales参数给出不同的值

dict_items([('AutoNormal.locs.bias', Parameter containing: tensor([14.5854], requires_grad=True)), ('AutoNormal.scales.bias', Parameter containing: tensor([-5.0647], requires_grad=True)), ('AutoNormal.locs.weight', Parameter containing: tensor([0.1195], requires_grad=True)), ('AutoNormal.scales.weight', Parameter containing: tensor([-4.6854], requires_grad=True)),('AutoNormal.locs.noise_scale', Parameter containing: tensor([-2.0720], requires_grad=True)), ('AutoNormal.scales.noise_scale', Parameter containing: tensor([-3.3613], requires_grad=True))])

这是怎么可能的?根据文档Paramstore是一个简单的键值存储;其中只有这六个键:

pyro.get_param_store().get_all_param_names() # .keys()方法给出相同的结果# 结果dict_keys(['AutoNormal.locs.bias','AutoNormal.scales.bias', 'AutoNormal.locs.weight', 'AutoNormal.scales.weight', 'AutoNormal.locs.noise_scale', 'AutoNormal.scales.noise_scale'])

因此,不可能一种方法访问一组项目而另一种方法访问另一组项目。

我在这里错过了什么吗?


回答:

情况如下,正如我在与这个问题并行打开的Github线程中所揭示的…

Paramstore不再仅仅是一个简单的键值存储 – 它还执行约束转换;引用上述链接中的一位Pyro开发者的说法:

这里有一些历史背景。ParamStore最初只是一个键值存储。然后我们添加了对受约束参数的支持;这引入了用户面对的受约束值和内部不受约束值之间的新层次分离。我们创建了一个新的类似字典的用户面对接口,只暴露受约束的值,但为了保持与旧代码的向后兼容性,我们保留了旧的接口。两个接口在源文件中是区分的[…]但正如你所观察到的,看起来我们忘记了将旧接口标记为已弃用。

我猜在澄清文档时,我们应该:

  1. 澄清ParamStore不再是一个简单的键值存储,而是也执行约束转换;

  2. 将所有“旧”风格的接口方法标记为已弃用;

  3. 从示例和教程中删除“旧”风格的接口使用。

因此,事实证明,虽然pyro.param()在受约束(用户面对)的空间中返回结果,但较旧的方法named_parameters()返回不受约束的(即仅供内部使用)的值,因此出现了明显的差异。

的确不难验证两个方法返回的scales值确实通过对数变换相关:

import numpy as npitems = list(pyro.get_param_store().named_parameters())  # 不受约束的空间i = 0for name in pyro.get_param_store().keys():    if 'scales' in name:    temp = np.log(                  pyro.param(name).item()  # 受约束的空间                 )    print(temp, items[i][1][0].item() , np.allclose(temp, items[i][1][0].item()))  i+=1# 结果:-5.027793402915326 -5.0277934074401855 True-4.600319371162187 -4.6003193855285645 True-3.3920585732532835 -3.3920586109161377 True

为什么这种差异只影响scales参数?那是因为scales(即本质上是方差)根据定义被约束为正值;这对locs(即均值)不成立,它们不受约束,因此两种表示对它们来说是相同的。

作为上述问题的结果,现在在Paramstore文档中添加了一个新的项目,提供了一个相关的提示:

一般来说,参数与受约束和不受约束的值相关。例如,在幕后,一个被约束为正值的参数在对数空间中表示为不受约束的张量。

以及在旧接口的named_parameters()方法的文档中:

请注意,如果参数受到约束,unconstrained_value处于约束隐式使用的未受约束空间中。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

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