-
Notifications
You must be signed in to change notification settings - Fork 11
/
utils.py
87 lines (74 loc) · 2.55 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import torch
def distance(X, Y, square=True):
"""
Compute Euclidean distances between two sets of samples
Basic framework: pytorch
:param X: d * n, where d is dimensions and n is number of data points in X
:param Y: d * m, where m is number of data points in Y
:param square: whether distances are squared, default value is True
:return: n * m, distance matrix
"""
n = X.shape[1]
m = Y.shape[1]
x = torch.norm(X, dim=0)
x = x * x # n * 1
x = torch.t(x.repeat(m, 1))
y = torch.norm(Y, dim=0)
y = y * y # m * 1
y = y.repeat(n, 1)
crossing_term = torch.t(X).matmul(Y)
result = x + y - 2 * crossing_term
result = result.relu()
if not square:
result = torch.sqrt(result)
return result
def cal_weights_via_CAN(X, num_neighbors, links=0):
"""
Solve Problem: Clustering-with-Adaptive-Neighbors(CAN)
:param X: d * n
:param num_neighbors:
:return:
"""
size = X.shape[1]
distances = distance(X, X)
distances = torch.max(distances, torch.t(distances))
sorted_distances, _ = distances.sort(dim=1)
top_k = sorted_distances[:, num_neighbors]
top_k = torch.t(top_k.repeat(size, 1)) + 10**-10
sum_top_k = torch.sum(sorted_distances[:, 0:num_neighbors], dim=1)
sum_top_k = torch.t(sum_top_k.repeat(size, 1))
sorted_distances = None
torch.cuda.empty_cache()
T = top_k - distances
distances = None
torch.cuda.empty_cache()
weights = torch.div(T, num_neighbors * top_k - sum_top_k)
T = None
top_k = None
sum_top_k = None
torch.cuda.empty_cache()
weights = weights.relu().cpu()
if links is not 0:
links = torch.Tensor(links).cuda()
weights += torch.eye(size).cuda()
weights += links
weights /= weights.sum(dim=1).reshape([size, 1])
torch.cuda.empty_cache()
raw_weights = weights
weights = (weights + weights.t()) / 2
raw_weights = raw_weights.cuda()
weights = weights.cuda()
return weights, raw_weights
def get_Laplacian_from_weights(weights):
# W = torch.eye(weights.shape[0]).cuda() + weights
# degree = torch.sum(W, dim=1).pow(-0.5)
# return (W * degree).t()*degree
degree = torch.sum(weights, dim=1).pow(-0.5)
return (weights * degree).t()*degree
def noise(weights, ratio=0.1):
sampling = torch.rand(weights.shape).cuda() + torch.eye(weights.shape[0]).cuda()
sampling = (sampling > ratio).type(torch.IntTensor).cuda()
return weights * sampling
if __name__ == '__main__':
tX = torch.rand(3, 8)
print(cal_weights_via_CAN(tX, 3))