使用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。
谢谢!
答案 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}})。