GPflow 2.0中的自定义Haversine Matern52内核

时间:2019-12-28 15:51:58

标签: python tensorflow tensorflow2.0 tensorflow-probability gpflow

使用GPflow 2.0,我想用Haversine而不是Euclidean距离实现自定义的Matern 5/2内核。我在gpflow.kernels.Matern52类之上创建了一个自定义类,该类包含一个scaled_squared_dist函数以覆盖从 Stationary类继承的scaled_squared_euclid_dist

当前编写的类不会更改Matern52类;使用HaversineKernel_Matern52内核的GP回归的行为与使用Matern52内核的GP回归完全一样。

import gpflow
from gpflow.utilities.ops import square_distance

class HaversineKernel_Matern52(gpflow.kernels.Matern52):
    """
    Isotropic Matern52 Kernel with Haversine distance instead of euclidean distance.
    Assumes 2-dimensional inputs, with columns [latitude, longitude] in degrees.
    """

    def __init__(self, lengthscale=1.0, variance=1.0, active_dims=None, ard=None):
        super().__init__(active_dims=active_dims, variance=variance, 
                         lengthscale=lengthscale, ard=ard)

    def haversine_dist(self, X, X2):
        pi = np.pi / 180
        f = tf.expand_dims(X * pi, -2)  # ... x N x 1 x D
        f2 = tf.expand_dims(X2 * pi, -3)  # ... x 1 x M x D
        d = tf.sin((f - f2) / 2) ** 2
        lat1, lat2 = tf.expand_dims(X[:, 0] * pi, -1), \
                    tf.expand_dims(X2[:, 0] * pi, -2)
        cos_prod = tf.cos(lat2) * tf.cos(lat1)
        a = d[:,:,0] + cos_prod * d[:,:,1]
        c = tf.asin(tf.sqrt(a)) * 6371 * 2
        return c

    def scaled_squared_dist(self, X, X2=None):
        """
        Returns ||(X - X2ᵀ) / ℓ||² i.e. squared L2-norm.
        """

        X_scaled = X / self.lengthscale
        X2_scaled = X2 / self.lengthscale if X2 is not None else X2
        return square_distance(X_scaled, X2_scaled)

我需要更改什么才能使该内核正确地重新计算Haversine距离?

此问题基于GPflow issue #969

谢谢!

1 个答案:

答案 0 :(得分:2)

GP代码利用内核的customAdapter = new CustomAdapter(); gridView.setAdapter(customAdapter); (和import Foundation class MyItem: Codable { let id: Int let lists: Lists let time: Time } class Lists: Codable { let pause: Pause } class Pause: Codable { let attached: [Attached] } class Attached: Codable { let from, to, length: Int } class Time: Codable { let start_time, end_time: Int } // MARK: - Helper functions for creating encoders and decoders fileprivate func newJSONDecoder() -> JSONDecoder { let decoder = JSONDecoder() if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { decoder.dateDecodingStrategy = .iso8601 } return decoder } fileprivate func newJSONEncoder() -> JSONEncoder { let encoder = JSONEncoder() if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { encoder.dateEncodingStrategy = .iso8601 } return encoder } )方法。在GPflow 2.0.0rc1和develop分支中,对于let myItem = try? newJSONDecoder().decode(MyItem.self, from: jsonData) // Read from (for example) let from = myItem.lists.pause.attached[0].from 的子类,K调用K_diag,但是您在Haversine版本中定义的方法称为Stationary,因此这是一个 new 方法,实际上您不会从K内核类中覆盖其基类方法! (也许最好将gpflow.kernels.stationaries.Stationary中的方法称为self.scaled_squared_euclid_dist。)

此外,您的scaled_squared_dist仅呼叫Matern52而不是scaled_squared_dist;假设后者返回一个距离,而不是其平方,则还需要将其包装在scaled_squared_dist中。 square_distance方法似乎也没有考虑到self.haversine_dist参数。

如果您想尝试几个具有Haversine距离的不同内核,一种更健壮/可重用的编码方式可能是编写一个包装类,该包装类将任何固定内核作为 argument ,并重新定义内核矩阵方法:

tf.square()

其中haversine_dist假定已在其他位置定义。未经测试的代码,但它适用于任何固定内核,无论是lengthscale(定义{{​​1}})还是class HaversineDistance(gpflow.kernels.Stationary): def __init__(self, base: gpflow.kernels.Stationary): self.base = base @property def variance(self): return self.base.variance # for K_diag to work def scaled_haversine_dist(self, X, X2=None): """ Returns the Haversine distance between X and X2ᵀ. """ X_scaled = X / self.lengthscale X2_scaled = X2 / self.lengthscale if X2 is not None else X2 return haversine_dist(X_scaled, X2_scaled) def K(self, X, X2=None, presliced=False): if not presliced: X, X2 = self.slice(X, X2) r = self.scaled_haversine_dist(X, X2) if hasattr(self.base, "K_r"): return self.base.K_r(r) else: return self.base.K_r2(tf.square(r)) (定义{{​​1}})。