在 GPflow 2.0 中使用 Tensorflow 双射链进行边界超参数优化

Bounding hyperparameter optimization with Tensorflow bijector chain in GPflow 2.0

在 GPflow 2.0 中做 GP 回归时,我想在 lengthscale 上设置硬边界(即限制 lengthscale 优化范围)。在这个线程 () 之后,我构建了一个 TensorFlow Bijector 链(参见下面的 bounded_lengthscale 函数)。但是,下面的双射链并不会阻止模型在假定范围之外进行优化。我需要更改什么才能使 bounded_lengthscale 函数对优化设置严格限制?

下面是MRE:

import gpflow 
import numpy as np
from gpflow.utilities import print_summary
import tensorflow as tf
from tensorflow_probability import bijectors as tfb

# Noisy training data
noise = 0.3
X = np.arange(-3, 4, 1).reshape(-1, 1).astype('float64')
Y = (np.sin(X) + noise * np.random.randn(*X.shape)).reshape(-1,1)

def bounded_lengthscale(low, high, lengthscale):
    """Returns lengthscale Parameter with optimization bounds."""
    affine = tfb.AffineScalar(shift=low, scale=high-low)
    sigmoid = tfb.Sigmoid()
    logistic = tfb.Chain([affine, sigmoid])
    parameter = gpflow.Parameter(lengthscale, transform=logistic, dtype=tf.float32)
    parameter = tf.cast(parameter, dtype=tf.float64)
    return parameter

# build GPR model
k = gpflow.kernels.Matern52()
m = gpflow.models.GPR(data=(X, Y), kernel=k)

m.kernel.lengthscale.assign(bounded_lengthscale(0, 1, 0.5))

print_summary(m)

# train model
@tf.function(autograph=False)
def objective_closure():
    return - m.log_marginal_likelihood()

opt = gpflow.optimizers.Scipy()
opt_logs = opt.minimize(objective_closure,
                        m.trainable_variables)
print_summary(m)

谢谢!

在 MWE 中,您 assign 一个新的 到一个 Parameter 已经存在(并且没有逻辑转换)。该值是使用逻辑变换构造的参数具有的约束-space 值,但不会保留该变换。相反,您需要 将不带逻辑变换的参数替换为 具有所需变换的参数:m.kernel.lengthscale = bounded_lengthscale(0,1,0.5).

请注意,您分配给 kernel.lengthscale 属性的对象 必须 是一个 Parameter 实例;如果你像在 MWE 中一样分配 tf.cast(parameter) 的 return 值,这相当于一个常量,它实际上不会被优化!

由于 float32/float64 不匹配,简单地移动此问题中 MWE 中的 tf.cast 不会立即起作用。要修复它,AffineScalar 双射器需要在 float64 中;它没有 dtype 参数,而是将 shift=scale= 的参数转换为所需的类型:

def bounded_lengthscale(low, high, lengthscale):
    """Make lengthscale tfp Parameter with optimization bounds."""
    affine = tfb.AffineScalar(shift=tf.cast(low, tf.float64),
                              scale=tf.cast(high-low, tf.float64))
    sigmoid = tfb.Sigmoid()
    logistic = tfb.Chain([affine, sigmoid])
    parameter = gpflow.Parameter(lengthscale, transform=logistic, dtype=tf.float64)
    return parameter

m.kernel.lengthscale = bounded_lengthscale(0, 1, 0.5)

(GPflow 可能应该包含这样的辅助函数,以使有界参数转换更易于使用 - GPflow 始终感谢人们提供帮助,因此如果您想将其转换为拉取请求,请这样做!)

tfb.Sigmoid 现在接受 lowhigh 参数,正如@Brian Patton 在评论中预测的那样。

因此,代码可以简化为:

from tensorflow_probability import bijectors as tfb

def bounded_lengthscale(low, high, lengthscale):
    """Make lengthscale tfp Parameter with optimization bounds."""
    sigmoid = tfb.Sigmoid(low, high)
    parameter = gpflow.Parameter(lengthscale, transform=sigmoid, dtype='float32')
    return parameter

m.kernel.lengthscale = bounded_lengthscale(0, 1, 0.5)