我们可以使用广播规则和简洁的 NumPy 函数einsum
来向量化数组操作。简而言之,广播允许我们通过向结果数组添加新维度来单行操作数组,同时einsum
允许我们通过显式使用索引表示法(而不是矩阵)对多个数组执行操作。
幸运的是,计算内核不需要循环。请参阅下面的矢量化解决方案ARD_kernel
function,它在我的机器中比原始的 loopy 版本快 30 倍。现在,einsum
通常是尽可能快,但可能有更快的方法,但我没有检查其他任何东西(例如,通常@
的运算符而不是einsum
)。
此外,代码中缺少一个术语(Kronecker delta),我不知道它是否故意省略(如果您在实现它时遇到问题,请告诉我,我会编辑答案)。
import numpy as np
N = 300 # Number of data points in X1
M = 400 # Number of data points in X2
D = 6 # Number of features (ARD dimensions)
np.random.seed(1) # Fix random seed for reproducibility
X1 = np.random.rand(N, D)
X2 = np.random.rand(M, D)
Lambda = np.random.rand(D, 1)
L_inv = np.diag(np.random.rand(D))
sigma_f = np.random.rand()
# Loopy function
def ARD_kernel_loops(X1, X2, Lambda, L_inv, sigma_f):
K = np.empty((N, M))
M3 = Lambda@Lambda.T + L_inv**2
for n in range(N):
for m in range(M):
d = (X1[n,:] - X2[m,:]).reshape(-1,1)
K[n, m] = np.exp(-0.5 * d.T@M3@d)
return K * sigma_f**2
# Vectorized function
def ARD_kernel(X1, X2, Lambda, L_inv, sigma_f):
M3 = Lambda.squeeze()*Lambda + L_inv**2 # Use broadcasting to avoid transpose
d = X1[:,None] - X2[None,...] # Use broadcasting to avoid loops
# order=F for memory layout (as your arrays are (N,M,D) instead of (D,N,M))
return sigma_f**2 * np.exp(-0.5 * np.einsum("ijk,kl,ijl->ij", d, M3, d, order = 'F'))