nystrom-ncut 0.0.5__py3-none-any.whl → 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nystrom_ncut/__init__.py +2 -3
- nystrom_ncut/common.py +5 -1
- nystrom_ncut/ncut_pytorch.py +18 -28
- nystrom_ncut/nystrom.py +2 -2
- nystrom_ncut/propagation_utils.py +82 -98
- nystrom_ncut/visualize_utils.py +26 -50
- {nystrom_ncut-0.0.5.dist-info → nystrom_ncut-0.0.7.dist-info}/METADATA +2 -2
- nystrom_ncut-0.0.7.dist-info/RECORD +11 -0
- {nystrom_ncut-0.0.5.dist-info → nystrom_ncut-0.0.7.dist-info}/WHEEL +1 -1
- nystrom_ncut-0.0.5.dist-info/RECORD +0 -11
- {nystrom_ncut-0.0.5.dist-info → nystrom_ncut-0.0.7.dist-info}/LICENSE +0 -0
- {nystrom_ncut-0.0.5.dist-info → nystrom_ncut-0.0.7.dist-info}/top_level.txt +0 -0
nystrom_ncut/__init__.py
CHANGED
@@ -4,8 +4,8 @@ from .ncut_pytorch import (
|
|
4
4
|
)
|
5
5
|
from .propagation_utils import (
|
6
6
|
affinity_from_features,
|
7
|
-
|
8
|
-
|
7
|
+
extrapolate_knn_with_subsampling,
|
8
|
+
extrapolate_knn,
|
9
9
|
quantile_normalize,
|
10
10
|
)
|
11
11
|
from .visualize_utils import (
|
@@ -17,6 +17,5 @@ from .visualize_utils import (
|
|
17
17
|
rgb_from_cosine_tsne_3d,
|
18
18
|
rotate_rgb_cube,
|
19
19
|
convert_to_lab_color,
|
20
|
-
propagate_rgb_color,
|
21
20
|
get_mask,
|
22
21
|
)
|
nystrom_ncut/common.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
from typing import Any
|
1
|
+
from typing import Any, Literal
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import torch
|
5
5
|
import torch.nn.functional as Fn
|
6
6
|
|
7
7
|
|
8
|
+
DistanceOptions = Literal["cosine", "euclidean", "rbf"]
|
9
|
+
SampleOptions = Literal["farthest", "random"]
|
10
|
+
|
11
|
+
|
8
12
|
def ceildiv(a: int, b: int) -> int:
|
9
13
|
return -(-a // b)
|
10
14
|
|
nystrom_ncut/ncut_pytorch.py
CHANGED
@@ -4,6 +4,10 @@ from typing import Literal, Tuple
|
|
4
4
|
import torch
|
5
5
|
import torch.nn.functional as Fn
|
6
6
|
|
7
|
+
from .common import (
|
8
|
+
DistanceOptions,
|
9
|
+
SampleOptions,
|
10
|
+
)
|
7
11
|
from .nystrom import (
|
8
12
|
EigSolverOptions,
|
9
13
|
OnlineKernel,
|
@@ -16,9 +20,6 @@ from .propagation_utils import (
|
|
16
20
|
)
|
17
21
|
|
18
22
|
|
19
|
-
DistanceOptions = Literal["cosine", "euclidean", "rbf"]
|
20
|
-
|
21
|
-
|
22
23
|
class LaplacianKernel(OnlineKernel):
|
23
24
|
def __init__(
|
24
25
|
self,
|
@@ -46,9 +47,10 @@ class LaplacianKernel(OnlineKernel):
|
|
46
47
|
affinity_focal_gamma=self.affinity_focal_gamma,
|
47
48
|
distance=self.distance,
|
48
49
|
) # [n x n]
|
50
|
+
d = features.shape[-1]
|
49
51
|
U, L = solve_eig(
|
50
52
|
self.A,
|
51
|
-
num_eig=
|
53
|
+
num_eig=d + 1, # d * (d + 3) // 2 + 1,
|
52
54
|
eig_solver=self.eig_solver,
|
53
55
|
) # [n x (d + 1)], [d + 1]
|
54
56
|
self.Ainv = U @ torch.diag(1 / L) @ U.mT # [n x n]
|
@@ -97,11 +99,10 @@ class NCUT(OnlineNystrom):
|
|
97
99
|
n_components: int = 100,
|
98
100
|
affinity_focal_gamma: float = 1.0,
|
99
101
|
num_sample: int = 10000,
|
100
|
-
sample_method:
|
102
|
+
sample_method: SampleOptions = "farthest",
|
101
103
|
distance: DistanceOptions = "cosine",
|
102
104
|
eig_solver: EigSolverOptions = "svd_lowrank",
|
103
105
|
normalize_features: bool = None,
|
104
|
-
move_output_to_cpu: bool = False,
|
105
106
|
chunk_size: int = 8192,
|
106
107
|
):
|
107
108
|
"""
|
@@ -117,7 +118,6 @@ class NCUT(OnlineNystrom):
|
|
117
118
|
eig_solver (str): eigen decompose solver, ['svd_lowrank', 'lobpcg', 'svd', 'eigh'].
|
118
119
|
normalize_features (bool): normalize input features before computing affinity matrix,
|
119
120
|
default 'None' is True for cosine distance, False for euclidean distance and rbf
|
120
|
-
move_output_to_cpu (bool): move output to CPU, set to True if you have memory issue
|
121
121
|
chunk_size (int): chunk size for large-scale matrix multiplication
|
122
122
|
"""
|
123
123
|
OnlineNystrom.__init__(
|
@@ -127,18 +127,18 @@ class NCUT(OnlineNystrom):
|
|
127
127
|
eig_solver=eig_solver,
|
128
128
|
chunk_size=chunk_size,
|
129
129
|
)
|
130
|
-
self.num_sample = num_sample
|
131
|
-
self.sample_method = sample_method
|
132
|
-
self.
|
133
|
-
self.
|
130
|
+
self.num_sample: int = num_sample
|
131
|
+
self.sample_method: SampleOptions = sample_method
|
132
|
+
self.anchor_indices: torch.Tensor = None
|
133
|
+
self.distance: DistanceOptions = distance
|
134
|
+
self.normalize_features: bool = normalize_features
|
134
135
|
if self.normalize_features is None:
|
135
136
|
if distance in ["cosine"]:
|
136
137
|
self.normalize_features = True
|
137
138
|
if distance in ["euclidean", "rbf"]:
|
138
139
|
self.normalize_features = False
|
139
140
|
|
140
|
-
self.
|
141
|
-
self.chunk_size = chunk_size
|
141
|
+
self.chunk_size: int = chunk_size
|
142
142
|
|
143
143
|
def _fit_helper(
|
144
144
|
self,
|
@@ -152,16 +152,6 @@ class NCUT(OnlineNystrom):
|
|
152
152
|
)
|
153
153
|
self.num_sample = _n
|
154
154
|
|
155
|
-
# check if features dimension greater than num_eig
|
156
|
-
if self.eig_solver in ["svd_lowrank", "lobpcg"]:
|
157
|
-
assert (
|
158
|
-
_n >= self.n_components * 2
|
159
|
-
), "number of nodes should be greater than 2*num_eig"
|
160
|
-
elif self.eig_solver in ["svd", "eigh"]:
|
161
|
-
assert (
|
162
|
-
_n >= self.n_components
|
163
|
-
), "number of nodes should be greater than num_eig"
|
164
|
-
|
165
155
|
assert self.distance in ["cosine", "euclidean", "rbf"], "distance should be 'cosine', 'euclidean', 'rbf'"
|
166
156
|
|
167
157
|
if self.normalize_features:
|
@@ -169,20 +159,20 @@ class NCUT(OnlineNystrom):
|
|
169
159
|
features = torch.nn.functional.normalize(features, dim=-1)
|
170
160
|
|
171
161
|
if precomputed_sampled_indices is not None:
|
172
|
-
|
162
|
+
_sampled_indices = precomputed_sampled_indices
|
173
163
|
else:
|
174
|
-
|
164
|
+
_sampled_indices = run_subgraph_sampling(
|
175
165
|
features,
|
176
166
|
self.num_sample,
|
177
167
|
sample_method=self.sample_method,
|
178
168
|
)
|
179
|
-
|
180
|
-
sampled_features = features[
|
169
|
+
self.anchor_indices = torch.sort(_sampled_indices).values
|
170
|
+
sampled_features = features[self.anchor_indices]
|
181
171
|
OnlineNystrom.fit(self, sampled_features)
|
182
172
|
|
183
173
|
_n_not_sampled = _n - len(sampled_features)
|
184
174
|
if _n_not_sampled > 0:
|
185
|
-
unsampled_indices = torch.full((_n,), True, device=features.device).scatter_(0,
|
175
|
+
unsampled_indices = torch.full((_n,), True, device=features.device).scatter_(0, self.anchor_indices, False)
|
186
176
|
unsampled_features = features[unsampled_indices]
|
187
177
|
V_unsampled, _ = OnlineNystrom.update(self, unsampled_features)
|
188
178
|
else:
|
nystrom_ncut/nystrom.py
CHANGED
@@ -72,7 +72,7 @@ class OnlineNystrom:
|
|
72
72
|
self.anchor_features = features
|
73
73
|
|
74
74
|
self.kernel.fit(self.anchor_features)
|
75
|
-
self.inverse_approximation_dim = max(self.n_components, features.shape[-1]
|
75
|
+
self.inverse_approximation_dim = max(self.n_components, features.shape[-1] + 1)
|
76
76
|
U, L = self._update_to_kernel() # [n x (? + 1)], [? + 1]
|
77
77
|
|
78
78
|
self.transform_matrix = (U / L)[:, :self.n_components] # [n x n_components]
|
@@ -135,7 +135,7 @@ class OnlineNystrom:
|
|
135
135
|
def solve_eig(
|
136
136
|
A: torch.Tensor,
|
137
137
|
num_eig: int,
|
138
|
-
eig_solver:
|
138
|
+
eig_solver: EigSolverOptions,
|
139
139
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
140
140
|
"""PyTorch implementation of Eigensolver cut without Nystrom-like approximation.
|
141
141
|
|
@@ -3,9 +3,14 @@ from typing import Literal
|
|
3
3
|
|
4
4
|
import numpy as np
|
5
5
|
import torch
|
6
|
-
import torch.nn.functional as
|
6
|
+
import torch.nn.functional as Fn
|
7
7
|
|
8
|
-
from .common import
|
8
|
+
from .common import (
|
9
|
+
DistanceOptions,
|
10
|
+
SampleOptions,
|
11
|
+
ceildiv,
|
12
|
+
lazy_normalize,
|
13
|
+
)
|
9
14
|
|
10
15
|
|
11
16
|
@torch.no_grad()
|
@@ -13,7 +18,7 @@ def run_subgraph_sampling(
|
|
13
18
|
features: torch.Tensor,
|
14
19
|
num_sample: int,
|
15
20
|
max_draw: int = 1000000,
|
16
|
-
sample_method:
|
21
|
+
sample_method: SampleOptions = "farthest",
|
17
22
|
):
|
18
23
|
if num_sample >= features.shape[0]:
|
19
24
|
# if too many samples, use all samples and bypass Nystrom-like approximation
|
@@ -74,7 +79,7 @@ def farthest_point_sampling(
|
|
74
79
|
def distance_from_features(
|
75
80
|
features: torch.Tensor,
|
76
81
|
features_B: torch.Tensor,
|
77
|
-
distance:
|
82
|
+
distance: DistanceOptions,
|
78
83
|
):
|
79
84
|
"""Compute affinity matrix from input features.
|
80
85
|
Args:
|
@@ -96,7 +101,6 @@ def distance_from_features(
|
|
96
101
|
D = D / (2 * features.var(dim=0).sum())
|
97
102
|
else:
|
98
103
|
raise ValueError("distance should be 'cosine' or 'euclidean', 'rbf'")
|
99
|
-
|
100
104
|
return D
|
101
105
|
|
102
106
|
|
@@ -104,7 +108,7 @@ def affinity_from_features(
|
|
104
108
|
features: torch.Tensor,
|
105
109
|
features_B: torch.Tensor = None,
|
106
110
|
affinity_focal_gamma: float = 1.0,
|
107
|
-
distance:
|
111
|
+
distance: DistanceOptions = "cosine",
|
108
112
|
):
|
109
113
|
"""Compute affinity matrix from input features.
|
110
114
|
|
@@ -132,23 +136,23 @@ def affinity_from_features(
|
|
132
136
|
return A
|
133
137
|
|
134
138
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
knn: int = 10,
|
140
|
-
distance:
|
139
|
+
def extrapolate_knn(
|
140
|
+
anchor_features: torch.Tensor, # [n x d]
|
141
|
+
anchor_output: torch.Tensor, # [n x d']
|
142
|
+
extrapolation_features: torch.Tensor, # [m x d]
|
143
|
+
knn: int = 10, # k
|
144
|
+
distance: DistanceOptions = "cosine",
|
141
145
|
affinity_focal_gamma: float = 1.0,
|
142
146
|
chunk_size: int = 8192,
|
143
147
|
device: str = None,
|
144
|
-
move_output_to_cpu: bool = False
|
145
|
-
):
|
148
|
+
move_output_to_cpu: bool = False
|
149
|
+
) -> torch.Tensor: # [m x d']
|
146
150
|
"""A generic function to propagate new nodes using KNN.
|
147
151
|
|
148
152
|
Args:
|
149
|
-
|
150
|
-
|
151
|
-
|
153
|
+
anchor_features (torch.Tensor): features from subgraph, shape (num_sample, n_features)
|
154
|
+
anchor_output (torch.Tensor): output from subgraph, shape (num_sample, D)
|
155
|
+
extrapolation_features (torch.Tensor): features from existing nodes, shape (new_num_samples, n_features)
|
152
156
|
knn (int): number of KNN to propagate eige nvectors
|
153
157
|
distance (str): distance metric, 'cosine' (default) or 'euclidean', 'rbf'
|
154
158
|
chunk_size (int): chunk size for matrix multiplication
|
@@ -160,97 +164,77 @@ def propagate_knn(
|
|
160
164
|
>>> old_eigenvectors = torch.randn(3000, 20)
|
161
165
|
>>> old_features = torch.randn(3000, 100)
|
162
166
|
>>> new_features = torch.randn(200, 100)
|
163
|
-
>>> new_eigenvectors =
|
167
|
+
>>> new_eigenvectors = extrapolate_knn(old_features,old_eigenvectors,new_features,knn=3)
|
164
168
|
>>> # new_eigenvectors.shape = (200, 20)
|
165
169
|
|
166
170
|
"""
|
167
|
-
device =
|
168
|
-
|
169
|
-
if knn == 1:
|
170
|
-
return propagate_nearest(
|
171
|
-
subgraph_output,
|
172
|
-
inp_features,
|
173
|
-
subgraph_features,
|
174
|
-
chunk_size=chunk_size,
|
175
|
-
device=device,
|
176
|
-
move_output_to_cpu=move_output_to_cpu,
|
177
|
-
)
|
171
|
+
device = anchor_output.device if device is None else device
|
178
172
|
|
179
173
|
# used in nystrom_ncut
|
180
174
|
# propagate eigen_vector from subgraph to full graph
|
181
|
-
|
175
|
+
anchor_output = anchor_output.to(device)
|
182
176
|
|
183
|
-
n_chunks = ceildiv(
|
177
|
+
n_chunks = ceildiv(extrapolation_features.shape[0], chunk_size)
|
184
178
|
V_list = []
|
185
|
-
for _v in torch.chunk(
|
186
|
-
_v = _v.to(device)
|
187
|
-
_A = affinity_from_features(
|
188
|
-
|
179
|
+
for _v in torch.chunk(extrapolation_features, n_chunks, dim=0):
|
180
|
+
_v = _v.to(device) # [_m x d]
|
181
|
+
_A = affinity_from_features(anchor_features, _v, affinity_focal_gamma, distance).mT # [_m x n]
|
189
182
|
if knn is not None:
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
if
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
)
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
subgraph_features = subgraph_features.to(device)
|
222
|
-
for i in range(0, inp_features.shape[0], chunk_size):
|
223
|
-
end = min(i + chunk_size, inp_features.shape[0])
|
224
|
-
_v = inp_features[i:end].to(device)
|
225
|
-
_A = -distance_from_features(subgraph_features, _v, distance).mT
|
226
|
-
|
227
|
-
# keep top1 for each row
|
228
|
-
top_idx = _A.argmax(dim=-1).cpu()
|
229
|
-
_V = subgraph_output[top_idx]
|
183
|
+
_A, indices = _A.topk(k=knn, dim=-1, largest=True) # [_m x k], [_m x k]
|
184
|
+
_anchor_output = anchor_output[indices] # [_m x k x d]
|
185
|
+
else:
|
186
|
+
_anchor_output = anchor_output[None] # [1 x n x d]
|
187
|
+
_A = Fn.normalize(_A, p=1, dim=-1)
|
188
|
+
|
189
|
+
# if distance == 'cosine':
|
190
|
+
# _A = _v @ subgraph_features.T
|
191
|
+
# elif distance == 'euclidean':
|
192
|
+
# _A = - torch.cdist(_v, subgraph_features, p=2)
|
193
|
+
# elif distance == 'rbf':
|
194
|
+
# _A = - torch.cdist(_v, subgraph_features, p=2) ** 2
|
195
|
+
# else:
|
196
|
+
# raise ValueError("distance should be 'cosine' or 'euclidean', 'rbf'")
|
197
|
+
#
|
198
|
+
# # keep topk KNN for each row
|
199
|
+
# topk_sim, topk_idx = _A.topk(knn, dim=-1, largest=True)
|
200
|
+
# row_id = torch.arange(topk_idx.shape[0], device=_A.device)[:, None].expand(
|
201
|
+
# -1, topk_idx.shape[1]
|
202
|
+
# )
|
203
|
+
# _A = torch.sparse_coo_tensor(
|
204
|
+
# torch.stack([row_id, topk_idx], dim=-1).reshape(-1, 2).T,
|
205
|
+
# topk_sim.reshape(-1),
|
206
|
+
# size=(_A.shape[0], _A.shape[1]),
|
207
|
+
# device=_A.device,
|
208
|
+
# )
|
209
|
+
# _A = _A.to_dense().to(dtype=subgraph_output.dtype)
|
210
|
+
# _D = _A.sum(-1)
|
211
|
+
# _A /= _D[:, None]
|
212
|
+
|
213
|
+
_V = (_A[:, None, :] @ _anchor_output).squeeze(1)
|
230
214
|
if move_output_to_cpu:
|
231
215
|
_V = _V.cpu()
|
232
216
|
V_list.append(_V)
|
233
217
|
|
234
|
-
|
235
|
-
return
|
218
|
+
anchor_output = torch.cat(V_list, dim=0)
|
219
|
+
return anchor_output
|
236
220
|
|
237
221
|
|
238
222
|
# wrapper functions for adding new nodes to existing graph
|
239
|
-
def
|
240
|
-
|
241
|
-
|
242
|
-
|
223
|
+
def extrapolate_knn_with_subsampling(
|
224
|
+
full_features: torch.Tensor,
|
225
|
+
full_output: torch.Tensor,
|
226
|
+
extrapolation_features: torch.Tensor,
|
243
227
|
knn: int,
|
244
228
|
num_sample: int,
|
245
|
-
sample_method:
|
229
|
+
sample_method: SampleOptions,
|
246
230
|
chunk_size: int,
|
247
|
-
device: str
|
231
|
+
device: str
|
248
232
|
):
|
249
233
|
"""Propagate eigenvectors to new nodes using KNN. Note: this is equivalent to the class API `NCUT.tranform(new_features)`, expect for the sampling is re-done in this function.
|
250
234
|
Args:
|
251
|
-
|
252
|
-
|
253
|
-
|
235
|
+
full_output (torch.Tensor): eigenvectors from existing nodes, shape (num_sample, num_eig)
|
236
|
+
full_features (torch.Tensor): features from existing nodes, shape (n_samples, n_features)
|
237
|
+
extrapolation_features (torch.Tensor): features from new nodes, shape (n_new_samples, n_features)
|
254
238
|
knn (int): number of KNN to propagate eigenvectors, default 3
|
255
239
|
num_sample (int): number of samples for subgraph sampling, default 50000
|
256
240
|
sample_method (str): sample method, 'farthest' (default) or 'random'
|
@@ -263,31 +247,31 @@ def propagate_eigenvectors(
|
|
263
247
|
>>> old_eigenvectors = torch.randn(3000, 20)
|
264
248
|
>>> old_features = torch.randn(3000, 100)
|
265
249
|
>>> new_features = torch.randn(200, 100)
|
266
|
-
>>> new_eigenvectors =
|
250
|
+
>>> new_eigenvectors = extrapolate_knn_with_subsampling(extrapolation_features,old_eigenvectors,old_features,knn=3,num_sample=,sample_method=,chunk_size=,device=)
|
267
251
|
>>> # new_eigenvectors.shape = (200, 20)
|
268
252
|
"""
|
269
253
|
|
270
|
-
device =
|
254
|
+
device = full_output.device if device is None else device
|
271
255
|
|
272
256
|
# sample subgraph
|
273
|
-
|
274
|
-
|
257
|
+
anchor_indices = run_subgraph_sampling(
|
258
|
+
full_features,
|
275
259
|
num_sample,
|
276
260
|
sample_method=sample_method,
|
277
261
|
)
|
278
262
|
|
279
|
-
|
280
|
-
|
281
|
-
|
263
|
+
anchor_output = full_output[anchor_indices].to(device)
|
264
|
+
anchor_features = full_features[anchor_indices].to(device)
|
265
|
+
extrapolation_features = extrapolation_features.to(device)
|
282
266
|
|
283
267
|
# propagate eigenvectors from subgraph to new nodes
|
284
|
-
new_eigenvectors =
|
285
|
-
|
286
|
-
|
287
|
-
|
268
|
+
new_eigenvectors = extrapolate_knn(
|
269
|
+
anchor_features,
|
270
|
+
anchor_output,
|
271
|
+
extrapolation_features,
|
288
272
|
knn=knn,
|
289
273
|
chunk_size=chunk_size,
|
290
|
-
device=device
|
274
|
+
device=device
|
291
275
|
)
|
292
276
|
return new_eigenvectors
|
293
277
|
|
nystrom_ncut/visualize_utils.py
CHANGED
@@ -6,11 +6,14 @@ import torch
|
|
6
6
|
import torch.nn.functional as F
|
7
7
|
from sklearn.base import BaseEstimator
|
8
8
|
|
9
|
-
from .common import
|
9
|
+
from .common import (
|
10
|
+
DistanceOptions,
|
11
|
+
lazy_normalize,
|
12
|
+
)
|
10
13
|
from .propagation_utils import (
|
11
14
|
run_subgraph_sampling,
|
12
|
-
|
13
|
-
|
15
|
+
extrapolate_knn,
|
16
|
+
extrapolate_knn_with_subsampling,
|
14
17
|
quantile_min_max,
|
15
18
|
quantile_normalize
|
16
19
|
)
|
@@ -31,14 +34,29 @@ def _rgb_with_dimensionality_reduction(
|
|
31
34
|
reduction_dim: int,
|
32
35
|
reduction_kwargs: Dict[str, Any],
|
33
36
|
transform_func: Callable[[torch.Tensor], torch.Tensor] = _identity,
|
37
|
+
pre_smooth: bool = True,
|
34
38
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
39
|
+
|
40
|
+
if pre_smooth:
|
41
|
+
_subgraph_indices = run_subgraph_sampling(
|
42
|
+
features,
|
43
|
+
num_sample,
|
44
|
+
sample_method="farthest",
|
45
|
+
)
|
46
|
+
features = extrapolate_knn(
|
47
|
+
features[_subgraph_indices],
|
48
|
+
features[_subgraph_indices],
|
49
|
+
features,
|
50
|
+
distance="cosine",
|
51
|
+
)
|
52
|
+
|
35
53
|
subgraph_indices = run_subgraph_sampling(
|
36
54
|
features,
|
37
55
|
num_sample,
|
38
56
|
sample_method="farthest",
|
39
57
|
)
|
40
58
|
|
41
|
-
_inp = features[subgraph_indices].
|
59
|
+
_inp = features[subgraph_indices].numpy(force=True)
|
42
60
|
_subgraph_embed = reduction(
|
43
61
|
n_components=reduction_dim,
|
44
62
|
metric=metric,
|
@@ -47,14 +65,14 @@ def _rgb_with_dimensionality_reduction(
|
|
47
65
|
).fit_transform(_inp)
|
48
66
|
|
49
67
|
_subgraph_embed = torch.tensor(_subgraph_embed, dtype=torch.float32)
|
50
|
-
X_nd = transform_func(
|
68
|
+
X_nd = transform_func(extrapolate_knn(
|
69
|
+
features[subgraph_indices],
|
51
70
|
_subgraph_embed,
|
52
71
|
features,
|
53
|
-
features[subgraph_indices],
|
54
|
-
distance=metric,
|
55
72
|
knn=knn,
|
73
|
+
distance=metric,
|
56
74
|
device=device,
|
57
|
-
move_output_to_cpu=True
|
75
|
+
move_output_to_cpu=True
|
58
76
|
))
|
59
77
|
rgb = rgb_func(X_nd, q)
|
60
78
|
return X_nd, rgb
|
@@ -413,48 +431,6 @@ def rgb_from_2d_colormap(X_2d, q=0.95):
|
|
413
431
|
return rgb
|
414
432
|
|
415
433
|
|
416
|
-
def propagate_rgb_color(
|
417
|
-
rgb: torch.Tensor,
|
418
|
-
eigenvectors: torch.Tensor,
|
419
|
-
new_eigenvectors: torch.Tensor,
|
420
|
-
knn: int = 10,
|
421
|
-
num_sample: int = 1000,
|
422
|
-
sample_method: Literal["farthest", "random"] = "farthest",
|
423
|
-
chunk_size: int = 8192,
|
424
|
-
device: str = None,
|
425
|
-
):
|
426
|
-
"""Propagate RGB color to new nodes using KNN.
|
427
|
-
Args:
|
428
|
-
rgb (torch.Tensor): RGB color for each data sample, shape (n_samples, 3)
|
429
|
-
features (torch.Tensor): features from existing nodes, shape (n_samples, n_features)
|
430
|
-
new_features (torch.Tensor): features from new nodes, shape (n_new_samples, n_features)
|
431
|
-
knn (int): number of KNN to propagate RGB color, default 1
|
432
|
-
num_sample (int): number of samples for subgraph sampling, default 50000
|
433
|
-
sample_method (str): sample method, 'farthest' (default) or 'random'
|
434
|
-
chunk_size (int): chunk size for matrix multiplication, default 8192
|
435
|
-
device (str): device to use for computation, if None, will not change device
|
436
|
-
Returns:
|
437
|
-
torch.Tensor: propagated RGB color for each data sample, shape (n_new_samples, 3)
|
438
|
-
|
439
|
-
Examples:
|
440
|
-
>>> old_rgb = torch.randn(3000, 3)
|
441
|
-
>>> old_eigenvectors = torch.randn(3000, 20)
|
442
|
-
>>> new_eigenvectors = torch.randn(200, 20)
|
443
|
-
>>> new_rgb = propagate_rgb_color(old_rgb, new_eigenvectors, old_eigenvectors)
|
444
|
-
>>> # new_eigenvectors.shape = (200, 3)
|
445
|
-
"""
|
446
|
-
return propagate_eigenvectors(
|
447
|
-
eigenvectors=rgb,
|
448
|
-
features=eigenvectors,
|
449
|
-
new_features=new_eigenvectors,
|
450
|
-
knn=knn,
|
451
|
-
num_sample=num_sample,
|
452
|
-
sample_method=sample_method,
|
453
|
-
chunk_size=chunk_size,
|
454
|
-
device=device,
|
455
|
-
)
|
456
|
-
|
457
|
-
|
458
434
|
# application: get segmentation mask fron a reference eigenvector (point prompt)
|
459
435
|
def _transform_heatmap(heatmap, gamma=1.0):
|
460
436
|
"""Transform the heatmap using gamma, normalize and min-max normalization.
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: nystrom_ncut
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.7
|
4
4
|
Summary: Normalized Cut and Nyström Approximation
|
5
5
|
Author-email: Huzheng Yang <huze.yann@gmail.com>, Wentinn Liao <wentinn.liao@gmail.com>
|
6
6
|
Project-URL: Documentation, https://github.com/JophiArcana/Nystrom-NCUT/
|
@@ -0,0 +1,11 @@
|
|
1
|
+
nystrom_ncut/__init__.py,sha256=Vlc_iAlfvTNUiJXpZLWUOaL2Q-YqZqgr7WoG6cVnD0g,439
|
2
|
+
nystrom_ncut/common.py,sha256=G6w_8_BfBUMc6r8WFgA0NH4K6am7AzZCSdrQEVjra7U,671
|
3
|
+
nystrom_ncut/ncut_pytorch.py,sha256=S_nrD3ecK9PX91ZRWu3AVHsXN3V5lse7oRs4P0rDT0Y,11229
|
4
|
+
nystrom_ncut/nystrom.py,sha256=-l26oiJ0oPReSGlMlYV3gftszgFdAAHAi7OFtGPZ4Ic,8802
|
5
|
+
nystrom_ncut/propagation_utils.py,sha256=t8pHp4VjqEeCNWl5jIu79WhQzINj982n-z2UAcxnEUY,12181
|
6
|
+
nystrom_ncut/visualize_utils.py,sha256=17z3kTUHWb9ZKF_UfNIMGESI7YrjxaHYIq-0xJ02bng,17295
|
7
|
+
nystrom_ncut-0.0.7.dist-info/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
8
|
+
nystrom_ncut-0.0.7.dist-info/METADATA,sha256=q7lIa-_S1iEbNoV2ml_0TbOuCGOSAuq-0ryJc1HAf0k,6058
|
9
|
+
nystrom_ncut-0.0.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
10
|
+
nystrom_ncut-0.0.7.dist-info/top_level.txt,sha256=j7g_j0S048EvguFFnGgD5Ewd3r2H6klsxd5A4dd-wHw,13
|
11
|
+
nystrom_ncut-0.0.7.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
nystrom_ncut/__init__.py,sha256=Cww-_OsyQHLKpgw_Wh28_tUOvIMMr7Ey8w-tH7v99xQ,452
|
2
|
-
nystrom_ncut/common.py,sha256=qdR_JwknT9H1Cv5LopwdwZfORFx-O8MLiRI6ZF1Qohc,558
|
3
|
-
nystrom_ncut/ncut_pytorch.py,sha256=wRQXUPBOW2_vutocKf0J19HrFVkBYQePAYUEfotLfx4,11701
|
4
|
-
nystrom_ncut/nystrom.py,sha256=HbwON9pLW3gEZvOmbDJwkQNHolOo1EBvwBPeh2p2uJE,8833
|
5
|
-
nystrom_ncut/propagation_utils.py,sha256=mD6rZ_mwYjYXs1cp5ZaTK0FrJ4YhyCdoIUrdGRP9k-M,12119
|
6
|
-
nystrom_ncut/visualize_utils.py,sha256=QmBatlX7Q-ZWF_iJ1zFDnPHFuofz3tCmtoNeeoMPw3U,18558
|
7
|
-
nystrom_ncut-0.0.5.dist-info/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
8
|
-
nystrom_ncut-0.0.5.dist-info/METADATA,sha256=n9zlRYBD02k478INScrj9V9rZ1mhXTylcMjkmQDgl1A,6058
|
9
|
-
nystrom_ncut-0.0.5.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
10
|
-
nystrom_ncut-0.0.5.dist-info/top_level.txt,sha256=j7g_j0S048EvguFFnGgD5Ewd3r2H6klsxd5A4dd-wHw,13
|
11
|
-
nystrom_ncut-0.0.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|