【问题标题】:GPflow2: Multi output kernels (MOK) with partially shared kernelsGPflow2:具有部分共享内核的多输出内核 (MOK)
【发布时间】:2021-08-04 00:15:14
【问题描述】:

我有一个关于 gpflow 2 中的多输出内核的问题。对于我正在处理的应用程序,我想创建一个独立的多输出内核,它在 一些 输出维度上共享内核,但不是全部。 GPflow 中的两个相关类是SharedIndependentSeparateIndependent 多输出内核类。然而,它们要么对所有 P 个输出维度使用一个共享内核,要么对 P 个输出维度使用 P 个单独的内核。

下面附加的代码是对多输出内核笔记本 (https://gpflow.readthedocs.io/en/master/notebooks/advanced/multioutput.html) 的小改编。 4 个输出维度可分为 2 组(每组 2 个输出维度):一组灵活性高,另一组灵活性低。我想为这个任务使用 2 个内核,以便为 Squared Exponential 内核检测 2 个不同的长度尺度参数。为了完整起见,我从示例笔记本中添加了 SharedIndependent 类和 SeparateIndependent 类的实现。

我目前尝试将这些组合如下:SeparateIndependent([SharedIndependent(SquaredExponential()+Linear(), output_dim=2) for _ in range(2)])。这会导致以下错误:(15是诱导点数)

ValueError: 维度必须相等,但对于 '{{node 来说是 2 和 15 add_8}} = AddV2[T=DT_DOUBLE](stack, mul_13)' 输入形状: [2,15,2,15,2],[1,15,15]。

为此,我还尝试创建一个新的多输出内核类,它在很大程度上模仿了SeparateIndependent 类,并对K(X, X2)K_diag(X) 函数中的循环列表理解进行了一些更改。这并没有成功,因为不知道如何为多输出内核类注册正确的Kuu()Kuf()conditional() 函数。尝试创建与 SeparateIndependent 类相同但名称不同的类也失败了。

我很高兴知道是否可以组合 SharedIndependentSeparateIndependent 类,或者构造一个新的 MOK 类是否是解决问题的更好方法。如果是这样,解决注册条件表达式(Kuu()Kuf()conditional())问题的最佳方法是什么?

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
import gpflow
from gpflow.kernels import SquaredExponential, Linear, SharedIndependent, SeparateIndependent
from gpflow.inducing_variables import SharedIndependentInducingVariables, InducingPoints
from gpflow.utilities import print_summary
from gpflow.ci_utils import ci_niter
gpflow.config.set_default_float(np.float64)
np.random.seed(0)
MAXITER = ci_niter(2000)

N = 100  # number of points
D = 1  # number of input dimensions
M = 15  # number of inducing points
L = P = 4  # number of latent GPs, number of observations = output dimension

def generate_data(N=100):
    X = np.random.rand(N)[:, None] * 10 - 5  # Inputs = N x D
    G = np.hstack((0.5 * np.sin(X/2) + X, 3.0 * np.cos(X/2) - X,0.5 * np.sin(4 * X) + X, 3.0 * np.cos(4*X) - X))  # G = N x L
    W = np.array([[0.5, -0.3, 0, 0], [0.5, -0.3, 0, 0], [0, 0, -0.4, 0.6],[0.0, 0.0, 0.6, -0.4]])  # L x P
    F = np.matmul(G, W)  # N x P
    Y = F + np.random.randn(*F.shape) * [0.2, 0.2, 0.2, 0.2]

    return X, Y
X, Y = data = generate_data(N)
print(X.shape, Y.shape)
Zinit = np.linspace(-5, 5, M)[:, None]

def plot_model(m, name, lower=-7.0, upper=7.0):
    pX = np.linspace(lower, upper, 100)[:, None]
    pY, pYv = m.predict_y(pX)
    if pY.ndim == 3:
        pY = pY[:, 0, :]
    plt.plot(X, Y, "x")
    plt.gca().set_prop_cycle(None)
    plt.plot(pX, pY)
    for i in range(pY.shape[1]):
        top = pY[:, i] + 2.0 * pYv[:, i] ** 0.5
        bot = pY[:, i] - 2.0 * pYv[:, i] ** 0.5
        plt.fill_between(pX[:, 0], top, bot, alpha=0.3)
    plt.xlabel("X")
    plt.ylabel("f")
    plt.title(f"{name} kernel.")
    plt.show()

# initialization of inducing input locations (M random points from the training inputs)
Z = Zinit.copy()
# create multi-output inducing variables from Z
iv = SharedIndependentInducingVariables(InducingPoints(Z))

def optimize_model_with_scipy(model):
    optimizer = gpflow.optimizers.Scipy()
    optimizer.minimize(
        model.training_loss_closure(data),
        variables=model.trainable_variables,
        method="l-bfgs-b",
        options={"disp": True, "maxiter": MAXITER},
    )


# create multi-output kernel
kernels = [
        (SeparateIndependent([SquaredExponential() + Linear() for _ in range(P)]),'Seperate Independent'),
        (SharedIndependent(SquaredExponential()+Linear(), output_dim=P), 'Shared Independent'),
        (SeparateIndependent([SharedIndependent(SquaredExponential()+Linear(), output_dim=2) for _ in range(2)]), 'Partially shared independent')
       ]
for (kernel, name) in kernels:
    m = gpflow.models.SVGP(kernel, gpflow.likelihoods.Gaussian(), inducing_variable=iv, num_latent_gps=P)
    print_summary(m)
    optimize_model_with_scipy(m)
    print_summary(m)
    plot_model(m, name)

【问题讨论】:

    标签: python gpflow


    【解决方案1】:

    您应该能够通过重新使用内核对象(没有任何自定义类)来绑定一些,但不是所有内核参数:

    k_group1 = SquaredExponential() + Linear()
    k_group2 = SquaredExponential() + Linear()
    kernel_list = [k_group1, k_group1, k_group2, k_group2]
    # or if you have many more classes: [k_group1 for _ in range(num_group1)] + [k_group2 for _ in range(num_group2)]
    mo_kernel = SeparateIndependent(kernel_list)
    

    【讨论】:

      【解决方案2】:

      更新:我通过创建一个包含 D 共享独立多输出内核的单独多输出内核类解决了这个问题。条件表达式遍历这些以将它们应用于适当的输出维度。请注意,这仅适用于共享独立诱导变量。

      class CustomMultiOutput(MultioutputKernel, Combination):    
          def __init__(self, kernels, name=None):
              kernels = [SharedIndependent(k, output_dim=nu) for k in kernels]
              super().__init__(kernels=kernels, name=name)
      
          @property
          def num_latent_gps(self):
              return len(self.kernels)
      
          @property
          def latent_kernels(self):
              """The underlying kernels in the multioutput kernel"""
              return tuple(self.kernels)
      
      @conditional.register(object, SharedIndependentInducingVariables, CustomMultiOutput, object)
      def custom_shared_independent_conditional(
          Xnew,inducing_variable,
          kernel, f,*,
          full_cov=False, full_output_cov=False, q_sqrt=None, white=False, ):
          N, P = Xnew.shape[0], q_sqrt.shape[0]
          nu = kernel.nu
          fmeans, fvars = [], []
      
          for i, k in enumerate(kernel.kernels):
              nu_idx_start, nu_idx_end = int(i * kernel.nu), int((i + 1) * kernel.nu)
              f_i = f[:, nu_idx_start:nu_idx_end]
              q_sqrt_i = q_sqrt[nu_idx_start:nu_idx_end, :]
              Kmm = covariances.Kuu(inducing_variable, k, jitter=default_jitter())  # [M, M]
              Kmn = covariances.Kuf(inducing_variable, k, Xnew)  # [M, N]
              Knn = k.kernel(Xnew, full_cov=full_cov)
      
              fmean, fvar = base_conditional(
                  Kmn, Kmm, Knn, f_i, full_cov=full_cov, q_sqrt=q_sqrt_i, white=white
              )  # [N, P],  [P, N, N] or [N, P]
              fmeans.append(tf.transpose(fmean,perm=[1,0]))
              if full_cov:
                  fvars.append(tf.transpose(fvar,perm=[0,2,1]))
              else:
                  fvars.append(tf.transpose(fvar,perm=[1,0]))
      
          fmeans = tf.transpose(tf.reshape(fmeans,shape=(-1,N)))
          if full_cov:
              fvars = tf.reshape(fvars,shape=(-1,N,N))
          else:
              fvars = tf.transpose(tf.reshape(fvars, shape=(-1,N)))
      
          return fmeans, expand_independent_outputs(fvars, full_cov, full_output_cov)
      

      【讨论】:

        猜你喜欢
        • 2020-11-27
        • 2015-02-18
        • 1970-01-01
        • 2021-07-26
        • 1970-01-01
        • 1970-01-01
        • 2015-08-22
        • 1970-01-01
        • 2012-12-14
        相关资源
        最近更新 更多