R-Detect / MMD.py
songyiliao's picture
feat: initial cmommit (#1)
1244519 verified
import torch
def flexible_kernel(X, Y, X_org, Y_org, sigma, sigma0=0.1, epsilon=1e-08):
"""Flexible kernel calculation as in MMDu."""
Dxy = Pdist2(X, Y)
Dxy_org = Pdist2(X_org, Y_org)
L = 1
Kxy = (1 - epsilon) * torch.exp(
-((Dxy / sigma0) ** L) - Dxy_org / sigma
) + epsilon * torch.exp(-Dxy_org / sigma)
return Kxy
def MMD_Diff_Var(Kyy, Kzz, Kxy, Kxz, epsilon=1e-08):
"""Compute the variance of the difference statistic MMDXY - MMDXZ."""
"""Referenced from: https://github.com/eugenium/MMD/blob/master/mmd.py"""
m = Kxy.shape[0]
n = Kyy.shape[0]
r = Kzz.shape[0]
# Remove diagonal elements
Kyynd = Kyy - torch.diag(torch.diag(Kyy))
Kzznd = Kzz - torch.diag(torch.diag(Kzz))
u_yy = torch.sum(Kyynd) * (1.0 / (n * (n - 1)))
u_zz = torch.sum(Kzznd) * (1.0 / (r * (r - 1)))
u_xy = torch.sum(Kxy) / (m * n)
u_xz = torch.sum(Kxz) / (m * r)
t1 = (1.0 / n**3) * torch.sum(Kyynd.T @ Kyynd) - u_yy**2
t2 = (1.0 / (n**2 * m)) * torch.sum(Kxy.T @ Kxy) - u_xy**2
t3 = (1.0 / (n * m**2)) * torch.sum(Kxy @ Kxy.T) - u_xy**2
t4 = (1.0 / r**3) * torch.sum(Kzznd.T @ Kzznd) - u_zz**2
t5 = (1.0 / (r * m**2)) * torch.sum(Kxz @ Kxz.T) - u_xz**2
t6 = (1.0 / (r**2 * m)) * torch.sum(Kxz.T @ Kxz) - u_xz**2
t7 = (1.0 / (n**2 * m)) * torch.sum(Kyynd @ Kxy.T) - u_yy * u_xy
t8 = (1.0 / (n * m * r)) * torch.sum(Kxy.T @ Kxz) - u_xz * u_xy
t9 = (1.0 / (r**2 * m)) * torch.sum(Kzznd @ Kxz.T) - u_zz * u_xz
if type(epsilon) == torch.Tensor:
epsilon_tensor = epsilon.clone().detach()
else:
epsilon_tensor = torch.tensor(epsilon, device=Kyy.device)
zeta1 = torch.max(t1 + t2 + t3 + t4 + t5 + t6 - 2 * (t7 + t8 + t9), epsilon_tensor)
zeta2 = torch.max(
(1 / m / (m - 1)) * torch.sum((Kyynd - Kzznd - Kxy.T - Kxy + Kxz + Kxz.T) ** 2)
- (u_yy - 2 * u_xy - (u_zz - 2 * u_xz)) ** 2,
epsilon_tensor,
)
data = {
"t1": t1.item(),
"t2": t2.item(),
"t3": t3.item(),
"t4": t4.item(),
"t5": t5.item(),
"t6": t6.item(),
"t7": t7.item(),
"t8": t8.item(),
"t9": t9.item(),
"zeta1": zeta1.item(),
"zeta2": zeta2.item(),
}
Var = (4 * (m - 2) / (m * (m - 1))) * zeta1
Var_z2 = Var + (2.0 / (m * (m - 1))) * zeta2
return Var, Var_z2, data
def Pdist2(x, y):
"""compute the paired distance between x and y."""
x_norm = (x**2).sum(1).view(-1, 1)
if y is not None:
y_norm = (y**2).sum(1).view(1, -1)
else:
y = x
y_norm = x_norm.view(1, -1)
Pdist = x_norm + y_norm - 2.0 * torch.mm(x, torch.transpose(y, 0, 1))
Pdist[Pdist < 0] = 0
return Pdist
def MMD_batch2(
Fea,
len_s,
Fea_org,
sigma,
sigma0=0.1,
epsilon=10 ** (-10),
is_var_computed=True,
use_1sample_U=True,
coeff_xy=2,
):
X = Fea[0:len_s, :]
Y = Fea[len_s:, :]
L = 1 # generalized Gaussian (if L>1)
nx = X.shape[0]
ny = Y.shape[0]
Dxx = Pdist2(X, X)
Dyy = torch.zeros(Fea.shape[0] - len_s, 1).to(Dxx.device)
# Dyy = Pdist2(Y, Y)
Dxy = Pdist2(X, Y).transpose(0, 1)
Kx = torch.exp(-Dxx / sigma0)
Ky = torch.exp(-Dyy / sigma0)
Kxy = torch.exp(-Dxy / sigma0)
nx = Kx.shape[0]
is_unbiased = False
xx = torch.div((torch.sum(Kx)), (nx * nx))
yy = Ky.reshape(-1)
xy = torch.div(torch.sum(Kxy, dim=1), (nx))
mmd2 = xx - 2 * xy + yy
return mmd2
# MMD for three samples
def MMD_3_Sample_Test(
ref_fea,
fea_y,
fea_z,
ref_fea_org,
fea_y_org,
fea_z_org,
sigma,
sigma0,
epsilon,
alpha,
):
"""Run three-sample test (TST) using deep kernel kernel."""
X = ref_fea.clone().detach()
Y = fea_y.clone().detach()
Z = fea_z.clone().detach()
X_org = ref_fea_org.clone().detach()
Y_org = fea_y_org.clone().detach()
Z_org = fea_z_org.clone().detach()
Kyy = flexible_kernel(Y, Y, Y_org, Y_org, sigma, sigma0, epsilon)
Kzz = flexible_kernel(Z, Z, Z_org, Z_org, sigma, sigma0, epsilon)
Kxy = flexible_kernel(X, Y, X_org, Y_org, sigma, sigma0, epsilon)
Kxz = flexible_kernel(X, Z, X_org, Z_org, sigma, sigma0, epsilon)
Kyynd = Kyy - torch.diag(torch.diag(Kyy))
Kzznd = Kzz - torch.diag(torch.diag(Kzz))
Diff_Var, _, _ = MMD_Diff_Var(Kyy, Kzz, Kxy, Kxz, epsilon)
u_yy = torch.sum(Kyynd) / (Y.shape[0] * (Y.shape[0] - 1))
u_zz = torch.sum(Kzznd) / (Z.shape[0] * (Z.shape[0] - 1))
u_xy = torch.sum(Kxy) / (X.shape[0] * Y.shape[0])
u_xz = torch.sum(Kxz) / (X.shape[0] * Z.shape[0])
t = u_yy - 2 * u_xy - (u_zz - 2 * u_xz)
if Diff_Var.item() <= 0:
Diff_Var = torch.max(epsilon, torch.tensor(1e-08))
p_value = torch.distributions.Normal(0, 1).cdf(-t / torch.sqrt((Diff_Var)))
t = t / torch.sqrt(Diff_Var)
if p_value > alpha:
h = 0
else:
h = 1
return h, p_value.item(), t.item()