ncut-pytorch 3.0.0.dev0__tar.gz → 3.0.0.dev2__tar.gz
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.
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/PKG-INFO +1 -1
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/color/coloring.py +0 -1
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/color/mspace.py +1 -1
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/ncut.py +19 -26
- ncut_pytorch-3.0.0.dev2/ncut_pytorch/ncuts/ncut_click.py +102 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/ncuts/ncut_nystrom.py +117 -106
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/grad.py +1 -40
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/math.py +18 -1
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/sigma.py +11 -17
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/PKG-INFO +1 -1
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/pyproject.toml +1 -1
- ncut_pytorch-3.0.0.dev0/ncut_pytorch/ncuts/ncut_click.py +0 -106
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/LICENSE +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/README.md +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/color/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/color/mspace_nopl.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/ncuts/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/ncuts/ncut_kway.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/api.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/dinov3.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/hires_dino.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/lowres_dino.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/patch.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/transform.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino_predictor.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/jafar_predictor.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/predictor.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/vision_predictor.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/__init__.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/device.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/sample.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/utils/torch_mod.py +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/SOURCES.txt +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/dependency_links.txt +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/requires.txt +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/top_level.txt +0 -0
- {ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/setup.cfg +0 -0
|
@@ -56,7 +56,7 @@ def ncut_wrapper(features, n_eig, sigma=None):
|
|
|
56
56
|
|
|
57
57
|
# features.requires_grad_(True)
|
|
58
58
|
sigma = sigma or features.std(0).sum().item()
|
|
59
|
-
|
|
59
|
+
eigvec, eigval = ncut_fn(features, n_eig, sigma=sigma)
|
|
60
60
|
W = rbf_affinity(features, sigma=sigma)
|
|
61
61
|
# W = cosine_affinity(features, sigma=1.0)
|
|
62
62
|
A = normalize_affinity(W)
|
|
@@ -14,28 +14,30 @@ class Ncut:
|
|
|
14
14
|
def __init__(
|
|
15
15
|
self,
|
|
16
16
|
n_eig: int = 100,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
sigma: float = None,
|
|
20
|
-
repulsion_sigma: float = None,
|
|
21
|
-
repulsion_weight: float =
|
|
22
|
-
extrapolation_factor: float = 1.0,
|
|
23
|
-
device: str = None,
|
|
17
|
+
quantile_sigma: float = 0.25,
|
|
18
|
+
quantile_sigma_repulsion: float = 0.20,
|
|
19
|
+
sigma: float | None = None,
|
|
20
|
+
repulsion_sigma: float | None = None,
|
|
21
|
+
repulsion_weight: float | None = None,
|
|
24
22
|
affinity_fn: Union["rbf_affinity", "cosine_affinity"] = rbf_affinity,
|
|
23
|
+
extrapolation_factor: float = 1.0,
|
|
24
|
+
exact_gradient: bool = False,
|
|
25
|
+
device: str | None = None,
|
|
25
26
|
**kwargs,
|
|
26
27
|
):
|
|
27
28
|
"""
|
|
28
29
|
|
|
29
30
|
Args:
|
|
30
31
|
n_eig (int): number of eigenvectors
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
n_eig (int): number of eigenvectors
|
|
33
|
+
quantile_sigma (float): quantile of affinity sigma parameter, lower quantile_sigma results in sharper eigenvectors
|
|
34
|
+
quantile_sigma_repulsion (float): quantile of repulsion sigma parameter, lower quantile_sigma_repulsion results in sharper eigenvectors
|
|
33
35
|
sigma (float): affinity parameter, override d_sigma if provided
|
|
34
36
|
repulsion_sigma (float): (if use repulsion) repulsion sigma parameter, default None (no repulsion)
|
|
35
37
|
repulsion_weight (float): (if use repulsion) repulsion weight, default 0.2
|
|
36
|
-
extrapolation_factor (float): control how far can we extrapolate, larger extrapolation_factor means we can extrapolate further, default 1.0
|
|
37
|
-
device (str): device, default 'auto' (auto detect GPU)
|
|
38
38
|
affinity_fn (callable): affinity function, default rbf_affinity. Should accept (X1, X2=None, sigma=float) and return affinity matrix
|
|
39
|
+
extrapolation_factor (float): control how far can we extrapolate, larger extrapolation_factor means we can extrapolate further, default 1.0
|
|
40
|
+
exact_gradient (bool): use full spectrum and exact gradient, can be slower and unstable, default False device (str): device, default 'auto' (auto detect GPU)
|
|
39
41
|
|
|
40
42
|
Examples:
|
|
41
43
|
>>> from ncut_pytorch import Ncut
|
|
@@ -52,13 +54,14 @@ class Ncut:
|
|
|
52
54
|
>>> print(new_eigvec.shape) # (500, 20)
|
|
53
55
|
"""
|
|
54
56
|
self.n_eig = n_eig
|
|
55
|
-
self.
|
|
57
|
+
self.quantile_sigma = quantile_sigma
|
|
58
|
+
self.quantile_sigma_repulsion = quantile_sigma_repulsion
|
|
56
59
|
self.sigma = sigma
|
|
57
60
|
self.repulsion_sigma = repulsion_sigma
|
|
58
61
|
self.repulsion_weight = repulsion_weight
|
|
59
62
|
self.extrapolation_factor = extrapolation_factor
|
|
63
|
+
self.exact_gradient = exact_gradient
|
|
60
64
|
self.device = device
|
|
61
|
-
self.track_grad = track_grad
|
|
62
65
|
self.affinity_fn = affinity_fn
|
|
63
66
|
self.kwargs = kwargs
|
|
64
67
|
|
|
@@ -83,12 +86,13 @@ class Ncut:
|
|
|
83
86
|
ncut_fn(
|
|
84
87
|
X,
|
|
85
88
|
n_eig=self.n_eig,
|
|
86
|
-
|
|
89
|
+
quantile_sigma=self.quantile_sigma,
|
|
90
|
+
quantile_sigma_repulsion=self.quantile_sigma_repulsion,
|
|
87
91
|
sigma=self.sigma,
|
|
88
92
|
repulsion_sigma=self.repulsion_sigma,
|
|
89
93
|
repulsion_weight=self.repulsion_weight,
|
|
90
94
|
device=self.device,
|
|
91
|
-
|
|
95
|
+
exact_gradient=self.exact_gradient,
|
|
92
96
|
no_propagation=True,
|
|
93
97
|
affinity_fn=self.affinity_fn,
|
|
94
98
|
**self.kwargs
|
|
@@ -121,7 +125,6 @@ class Ncut:
|
|
|
121
125
|
self._nystrom_x,
|
|
122
126
|
extrapolation_factor=self.extrapolation_factor,
|
|
123
127
|
device=self.device,
|
|
124
|
-
track_grad=self.track_grad,
|
|
125
128
|
**self.kwargs
|
|
126
129
|
)
|
|
127
130
|
return eigvec
|
|
@@ -137,15 +140,5 @@ class Ncut:
|
|
|
137
140
|
"""
|
|
138
141
|
return self.fit(X).transform(X)
|
|
139
142
|
|
|
140
|
-
def __new__(cls, X: torch.Tensor = None, n_eig: int = 100, track_grad: bool = False, d_sigma: float = None,
|
|
141
|
-
device: str = None, affinity_fn: Callable[[torch.Tensor, torch.Tensor, float], torch.Tensor] = rbf_affinity,
|
|
142
|
-
**kwargs) -> Union["Ncut", torch.Tensor]:
|
|
143
|
-
if X is not None:
|
|
144
|
-
# function-like behavior
|
|
145
|
-
eigvec, eigval = ncut_fn(X, n_eig=n_eig, track_grad=track_grad, d_sigma=d_sigma, device=device, affinity_fn=affinity_fn, **kwargs)
|
|
146
|
-
return eigvec
|
|
147
|
-
# normal class instantiation
|
|
148
|
-
return super().__new__(cls)
|
|
149
|
-
|
|
150
143
|
def __call__(self, X: torch.Tensor) -> torch.Tensor:
|
|
151
144
|
return self.fit_transform(X)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
__all__ = ['ncut_click_prompt']
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Union
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
from ncut_pytorch.utils.sigma import find_sigma_by_degree
|
|
9
|
+
from ncut_pytorch.utils.math import rbf_affinity, cosine_affinity, normalize_affinity
|
|
10
|
+
from ncut_pytorch.utils.sample import farthest_point_sampling
|
|
11
|
+
from ncut_pytorch.utils.device import auto_device
|
|
12
|
+
from .ncut_nystrom import NystromConfig
|
|
13
|
+
from .ncut_nystrom import nystrom_propagate
|
|
14
|
+
from .ncut_nystrom import _plain_ncut
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
#TODO: automatically optimize click_weight based on the iou of fg and bg
|
|
18
|
+
def ncut_click_prompt(
|
|
19
|
+
X: torch.Tensor,
|
|
20
|
+
fg_indices: np.ndarray,
|
|
21
|
+
bg_indices: np.ndarray = None,
|
|
22
|
+
click_weight: float = 0.5,
|
|
23
|
+
bg_weight: float = 0.1,
|
|
24
|
+
n_eig: int = 2,
|
|
25
|
+
quantile_sigma: float = 0.25,
|
|
26
|
+
device: str = None,
|
|
27
|
+
sigma: float = None,
|
|
28
|
+
affinity_fn: Callable[[torch.Tensor, torch.Tensor, float], torch.Tensor] = rbf_affinity,
|
|
29
|
+
exact_gradient: bool = False,
|
|
30
|
+
no_propagation: bool = False,
|
|
31
|
+
return_indices_and_sigma: bool = False,
|
|
32
|
+
**kwargs,
|
|
33
|
+
) -> Union[tuple[torch.Tensor, torch.Tensor], tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]]:
|
|
34
|
+
|
|
35
|
+
config = NystromConfig()
|
|
36
|
+
config.update(kwargs)
|
|
37
|
+
|
|
38
|
+
# use GPU if available
|
|
39
|
+
device = auto_device(X.device, device)
|
|
40
|
+
|
|
41
|
+
if bg_indices is None:
|
|
42
|
+
bg_indices = np.array([], dtype=np.int64)
|
|
43
|
+
|
|
44
|
+
# subsample for nystrom approximation
|
|
45
|
+
nystrom_indices = farthest_point_sampling(X, n_sample=config.n_sample, device=device)
|
|
46
|
+
nystrom_indices = torch.tensor(nystrom_indices, dtype=torch.long)
|
|
47
|
+
# remove fg and bg from fps_idx
|
|
48
|
+
nystrom_indices = nystrom_indices[~np.isin(nystrom_indices, np.concatenate([fg_indices, bg_indices]))]
|
|
49
|
+
# add fg and bg to fps_idx
|
|
50
|
+
nystrom_indices = np.concatenate([fg_indices, bg_indices, nystrom_indices])
|
|
51
|
+
fg_indices = np.arange(len(fg_indices))
|
|
52
|
+
bg_indices = np.arange(len(bg_indices)) + len(fg_indices)
|
|
53
|
+
n_fgbg = len(fg_indices) + len(bg_indices)
|
|
54
|
+
|
|
55
|
+
nystrom_X = X[nystrom_indices].to(device)
|
|
56
|
+
|
|
57
|
+
# find optimal sigma for affinity matrix
|
|
58
|
+
if sigma is None and affinity_fn == rbf_affinity:
|
|
59
|
+
sigma = find_sigma_by_degree(nystrom_X, quantile_sigma, affinity_fn)
|
|
60
|
+
# TODO: change to std()
|
|
61
|
+
elif sigma is None and affinity_fn == cosine_affinity:
|
|
62
|
+
sigma = 0.5
|
|
63
|
+
|
|
64
|
+
# compute Ncut on the nystrom sampled subgraph
|
|
65
|
+
A = affinity_fn(nystrom_X, sigma=sigma)
|
|
66
|
+
A = normalize_affinity(A)
|
|
67
|
+
|
|
68
|
+
# modify the affinity from the clicks
|
|
69
|
+
X_click = 1 * A[fg_indices].mean(0)
|
|
70
|
+
if len(bg_indices) > 0:
|
|
71
|
+
X_click = X_click - bg_weight * A[bg_indices].mean(0)
|
|
72
|
+
|
|
73
|
+
X_click = X_click * A.shape[0]
|
|
74
|
+
|
|
75
|
+
A_click = affinity_fn(X_click.unsqueeze(1), sigma=0.5)
|
|
76
|
+
A_click = normalize_affinity(A_click)
|
|
77
|
+
|
|
78
|
+
_A = click_weight * A_click + (1 - click_weight) * A
|
|
79
|
+
|
|
80
|
+
nystrom_eigvec, eigval = _plain_ncut(_A, n_eig, exact_gradient=exact_gradient)
|
|
81
|
+
|
|
82
|
+
if no_propagation:
|
|
83
|
+
return nystrom_eigvec, eigval, nystrom_indices, sigma
|
|
84
|
+
|
|
85
|
+
# propagate eigenvectors from subgraph to full graph
|
|
86
|
+
eigvec, nystrom_indices2 = nystrom_propagate(
|
|
87
|
+
nystrom_eigvec,
|
|
88
|
+
X,
|
|
89
|
+
nystrom_X,
|
|
90
|
+
n_neighbors=config.n_neighbors,
|
|
91
|
+
n_sample=config.n_sample2,
|
|
92
|
+
matmul_chunk_size=config.matmul_chunk_size,
|
|
93
|
+
device=device,
|
|
94
|
+
return_indices=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if return_indices_and_sigma:
|
|
99
|
+
indices = nystrom_indices[nystrom_indices2]
|
|
100
|
+
return eigvec, eigval, indices, sigma
|
|
101
|
+
|
|
102
|
+
return eigvec, eigval
|
|
@@ -6,10 +6,9 @@ import torch
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from ncut_pytorch.utils.sigma import find_sigma_by_degree
|
|
8
8
|
from ncut_pytorch.utils.math import rbf_affinity, cosine_affinity
|
|
9
|
-
from ncut_pytorch.utils.math import gram_schmidt, normalize_affinity, grad_safe_eig_solve, correct_rotation, keep_topk_per_row
|
|
9
|
+
from ncut_pytorch.utils.math import gram_schmidt, normalize_affinity, grad_safe_eig_solve, correct_rotation, keep_topk_per_row, svd_lowrank
|
|
10
10
|
from ncut_pytorch.utils.sample import farthest_point_sampling
|
|
11
11
|
from ncut_pytorch.utils.device import auto_device
|
|
12
|
-
from ncut_pytorch.utils.grad import grad_manager
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class NystromConfig:
|
|
@@ -23,7 +22,6 @@ class NystromConfig:
|
|
|
23
22
|
n_neighbors = 32 # number of neighbors for eigenvector propagation, 10 is large enough for most cases
|
|
24
23
|
n_neighbors_max_ratio = 1/32 # max ratio of n_neighbors to n_sample2, to avoid over smoothing
|
|
25
24
|
matmul_chunk_size = 65536 # chunk size for matrix multiplication, larger chunk size is faster but requires more memory
|
|
26
|
-
move_output_to_cpu = True # if True, will move output to cpu, saves VRAM
|
|
27
25
|
|
|
28
26
|
def update(self, kwargs: dict):
|
|
29
27
|
for key, value in kwargs.items():
|
|
@@ -36,15 +34,16 @@ class NystromConfig:
|
|
|
36
34
|
def ncut_fn(
|
|
37
35
|
X: torch.Tensor,
|
|
38
36
|
n_eig: int = 100,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
quantile_sigma: float = 0.25,
|
|
38
|
+
quantile_sigma_repulsion: float = 0.20,
|
|
39
|
+
sigma: float | None = None,
|
|
40
|
+
repulsion_sigma: float | None = None,
|
|
41
|
+
repulsion_weight: float | None = None,
|
|
42
|
+
affinity_fn: Union["rbf_affinity", "cosine_affinity"] = rbf_affinity,
|
|
45
43
|
extrapolation_factor: float = 1.0,
|
|
44
|
+
exact_gradient: bool = False,
|
|
45
|
+
device: str | None = None,
|
|
46
46
|
make_orthogonal: bool = False,
|
|
47
|
-
affinity_fn: Union["rbf_affinity", "cosine_affinity"] = rbf_affinity,
|
|
48
47
|
no_propagation: bool = False,
|
|
49
48
|
**kwargs,
|
|
50
49
|
) -> Union[tuple[torch.Tensor, torch.Tensor], tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]]:
|
|
@@ -53,15 +52,15 @@ def ncut_fn(
|
|
|
53
52
|
Args:
|
|
54
53
|
X (torch.Tensor): input features, shape (N, D)
|
|
55
54
|
n_eig (int): number of eigenvectors
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
device (str): device, default 'auto' (auto detect GPU)
|
|
55
|
+
quantile_sigma (float): quantile of affinity sigma parameter, lower quantile_sigma results in sharper eigenvectors
|
|
56
|
+
quantile_sigma_repulsion (float): quantile of repulsion sigma parameter, lower quantile_sigma_repulsion results in sharper eigenvectors
|
|
59
57
|
sigma (float): affinity parameter, override d_sigma if provided
|
|
60
58
|
repulsion_sigma (float): (if use repulsion) repulsion sigma parameter, default None (no repulsion)
|
|
61
59
|
repulsion_weight (float): (if use repulsion) repulsion weight, default 0.2
|
|
60
|
+
affinity_fn (callable): affinity function, default rbf_affinity. Should accept (X1, X2=None, sigma=float) and return affinity matrix
|
|
62
61
|
extrapolation_factor (float): control how far can we extrapolate, larger extrapolation_factor means we can extrapolate further, default 1.0
|
|
62
|
+
exact_gradient (bool): use full spectrum and exact gradient, can be slower and unstable, default False
|
|
63
63
|
make_orthogonal (bool): make eigenvectors orthogonal
|
|
64
|
-
affinity_fn (callable): affinity function, default rbf_affinity. Should accept (X1, X2=None, sigma=float) and return affinity matrix
|
|
65
64
|
|
|
66
65
|
Returns:
|
|
67
66
|
eigenvectors (torch.Tensor): shape (N, n_eig)
|
|
@@ -76,60 +75,67 @@ def ncut_fn(
|
|
|
76
75
|
"""
|
|
77
76
|
config = NystromConfig()
|
|
78
77
|
config.update(kwargs)
|
|
79
|
-
|
|
80
|
-
# use GPU if available
|
|
81
78
|
device = auto_device(X.device, device)
|
|
82
79
|
|
|
83
|
-
#
|
|
80
|
+
# subsample for nystrom approximation
|
|
84
81
|
is_enough_data = X.shape[0] > config.n_sample
|
|
82
|
+
n_sample = min(config.n_sample, int(X.shape[0]*config.n_sample_max_ratio))
|
|
83
|
+
nystrom_indices = farthest_point_sampling(X, n_sample=n_sample, device=device) if is_enough_data else np.arange(X.shape[0])
|
|
84
|
+
nystrom_X = X[nystrom_indices].to(device)
|
|
85
|
+
|
|
86
|
+
sigma, repulsion_sigma = find_optimal_sigma(nystrom_X, quantile_sigma, quantile_sigma_repulsion, sigma, repulsion_sigma, affinity_fn)
|
|
87
|
+
|
|
88
|
+
if repulsion_sigma and repulsion_weight:
|
|
89
|
+
nystrom_eigvec, eigval = ncut_with_repulsion(nystrom_X, n_eig, sigma,
|
|
90
|
+
repulsion_sigma, repulsion_weight, affinity_fn, exact_gradient)
|
|
91
|
+
else:
|
|
92
|
+
A = affinity_fn(nystrom_X, sigma=sigma)
|
|
93
|
+
nystrom_eigvec, eigval = _plain_ncut(A, n_eig, exact_gradient)
|
|
94
|
+
|
|
95
|
+
if no_propagation:
|
|
96
|
+
return nystrom_eigvec, eigval, nystrom_indices, sigma
|
|
97
|
+
|
|
98
|
+
if not is_enough_data:
|
|
99
|
+
# skip nystrom approximation if not enough data, use exact ncut
|
|
100
|
+
return nystrom_eigvec, eigval
|
|
101
|
+
|
|
102
|
+
# propagate eigenvectors from subgraph to full graph
|
|
103
|
+
eigvec = nystrom_propagate(
|
|
104
|
+
nystrom_eigvec,
|
|
105
|
+
X,
|
|
106
|
+
nystrom_X,
|
|
107
|
+
extrapolation_factor=extrapolation_factor,
|
|
108
|
+
n_neighbors=config.n_neighbors,
|
|
109
|
+
n_sample=config.n_sample2,
|
|
110
|
+
matmul_chunk_size=config.matmul_chunk_size,
|
|
111
|
+
device=device,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# post-hoc orthogonalization
|
|
115
|
+
if make_orthogonal:
|
|
116
|
+
eigvec = gram_schmidt(eigvec)
|
|
85
117
|
|
|
86
|
-
|
|
87
|
-
# subsample for nystrom approximation
|
|
88
|
-
n_sample = min(config.n_sample, int(X.shape[0]*config.n_sample_max_ratio))
|
|
89
|
-
nystrom_indices = farthest_point_sampling(X, n_sample=n_sample, device=device) if is_enough_data else np.arange(X.shape[0])
|
|
90
|
-
nystrom_X = X[nystrom_indices].to(device)
|
|
91
|
-
|
|
92
|
-
# find optimal sigma for affinity matrix
|
|
93
|
-
if sigma is None:
|
|
94
|
-
if affinity_fn == rbf_affinity:
|
|
95
|
-
sigma = find_sigma_by_degree(nystrom_X, d_sigma, affinity_fn)
|
|
96
|
-
elif affinity_fn == cosine_affinity:
|
|
97
|
-
sigma = 0.5
|
|
98
|
-
else:
|
|
99
|
-
raise ValueError(f"`sigma` needs to be provided for affinity function {affinity_fn}, (sigma=0.5)")
|
|
100
|
-
|
|
101
|
-
if repulsion_sigma is not None:
|
|
102
|
-
nystrom_eigvec, eigval = ncut_with_repulsion(nystrom_X, n_eig, sigma_attraction=sigma, sigma_repulsion=repulsion_sigma, repulsion_weight=repulsion_weight, affinity_fn=affinity_fn)
|
|
103
|
-
else:
|
|
104
|
-
A = affinity_fn(nystrom_X, sigma=sigma)
|
|
105
|
-
nystrom_eigvec, eigval = _plain_ncut(A, n_eig)
|
|
106
|
-
|
|
107
|
-
if no_propagation:
|
|
108
|
-
return nystrom_eigvec, eigval, nystrom_indices, sigma
|
|
109
|
-
|
|
110
|
-
if not is_enough_data:
|
|
111
|
-
return nystrom_eigvec, eigval
|
|
112
|
-
|
|
113
|
-
# propagate eigenvectors from subgraph to full graph
|
|
114
|
-
eigvec = nystrom_propagate(
|
|
115
|
-
nystrom_eigvec,
|
|
116
|
-
X,
|
|
117
|
-
nystrom_X,
|
|
118
|
-
extrapolation_factor=extrapolation_factor,
|
|
119
|
-
n_neighbors=config.n_neighbors,
|
|
120
|
-
n_sample=config.n_sample2,
|
|
121
|
-
matmul_chunk_size=config.matmul_chunk_size,
|
|
122
|
-
device=device,
|
|
123
|
-
move_output_to_cpu=config.move_output_to_cpu,
|
|
124
|
-
track_grad=track_grad,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
# post-hoc orthogonalization
|
|
128
|
-
if make_orthogonal:
|
|
129
|
-
eigvec = gram_schmidt(eigvec)
|
|
130
|
-
|
|
131
|
-
return eigvec, eigval
|
|
118
|
+
return eigvec, eigval
|
|
132
119
|
|
|
120
|
+
def find_optimal_sigma(
|
|
121
|
+
X: torch.Tensor,
|
|
122
|
+
quantile_sigma: float = 0.25,
|
|
123
|
+
quantile_sigma_repulsion: float = 0.20,
|
|
124
|
+
sigma: float | None = None,
|
|
125
|
+
repulsion_sigma: float | None = None,
|
|
126
|
+
affinity_fn: Union["rbf_affinity", "cosine_affinity"] = rbf_affinity,
|
|
127
|
+
):
|
|
128
|
+
"""Find optimal sigma for affinity matrix and repulsion matrix."""
|
|
129
|
+
if affinity_fn == rbf_affinity:
|
|
130
|
+
sigma = sigma or find_sigma_by_degree(X, quantile_sigma, affinity_fn)
|
|
131
|
+
repulsion_sigma = repulsion_sigma or find_sigma_by_degree(X, quantile_sigma_repulsion, affinity_fn, init_sigma=sigma)
|
|
132
|
+
elif affinity_fn == cosine_affinity:
|
|
133
|
+
sigma = sigma or 0.5
|
|
134
|
+
repulsion_sigma = repulsion_sigma or 0.3
|
|
135
|
+
else:
|
|
136
|
+
if sigma is None:
|
|
137
|
+
raise ValueError(f"`sigma` need to be provided for affinity function {affinity_fn}, (sigma=0.5, repulsion_sigma=0.3)")
|
|
138
|
+
return sigma, repulsion_sigma
|
|
133
139
|
|
|
134
140
|
def ncut_with_repulsion(
|
|
135
141
|
X: torch.Tensor,
|
|
@@ -138,6 +144,7 @@ def ncut_with_repulsion(
|
|
|
138
144
|
sigma_repulsion: float = None,
|
|
139
145
|
repulsion_weight: float = 0.2,
|
|
140
146
|
affinity_fn: Union["rbf_affinity", "cosine_affinity"] = cosine_affinity,
|
|
147
|
+
exact_gradient: bool = False,
|
|
141
148
|
eps: float = 1e-8,
|
|
142
149
|
):
|
|
143
150
|
A = affinity_fn(X, sigma=sigma_attraction)
|
|
@@ -148,7 +155,10 @@ def ncut_with_repulsion(
|
|
|
148
155
|
D = D_A + D_R
|
|
149
156
|
W = A - R + torch.diag(D_R)
|
|
150
157
|
W = W / D[:, None]
|
|
151
|
-
|
|
158
|
+
if exact_gradient:
|
|
159
|
+
eigvec, eigval, _ = grad_safe_eig_solve(W, n_eig)
|
|
160
|
+
else:
|
|
161
|
+
eigvec, eigval, _ = svd_lowrank(W, n_eig)
|
|
152
162
|
eigvec = correct_rotation(eigvec)
|
|
153
163
|
return eigvec, eigval
|
|
154
164
|
|
|
@@ -156,9 +166,13 @@ def ncut_with_repulsion(
|
|
|
156
166
|
def _plain_ncut(
|
|
157
167
|
A: torch.Tensor,
|
|
158
168
|
n_eig: int = 100,
|
|
169
|
+
exact_gradient: bool = False,
|
|
159
170
|
):
|
|
160
171
|
A = normalize_affinity(A)
|
|
161
|
-
|
|
172
|
+
if exact_gradient:
|
|
173
|
+
eigvec, eigval, _ = grad_safe_eig_solve(A, n_eig)
|
|
174
|
+
else:
|
|
175
|
+
eigvec, eigval, _ = svd_lowrank(A, n_eig)
|
|
162
176
|
eigvec = eigvec[:, :n_eig]
|
|
163
177
|
eigval = eigval[:n_eig]
|
|
164
178
|
eigvec = correct_rotation(eigvec)
|
|
@@ -170,7 +184,6 @@ def nystrom_propagate(
|
|
|
170
184
|
X: torch.Tensor,
|
|
171
185
|
nystrom_X: torch.Tensor,
|
|
172
186
|
extrapolation_factor: float = 1.0,
|
|
173
|
-
track_grad: bool = False,
|
|
174
187
|
device: str = None,
|
|
175
188
|
return_indices: bool = False,
|
|
176
189
|
**kwargs,
|
|
@@ -183,7 +196,6 @@ def nystrom_propagate(
|
|
|
183
196
|
X (torch.Tensor): input features for all nodes, shape (N, D)
|
|
184
197
|
nystrom_X (torch.Tensor): input features from nystrom sampled nodes, shape (m, D)
|
|
185
198
|
extrapolation_factor (float): control how far can we extrapolate, larger extrapolation_factor means we can extrapolate further, default 1.0
|
|
186
|
-
track_grad (bool): keep track of pytorch gradients, default False
|
|
187
199
|
device (str): device to use for computation, if 'auto', will detect GPU automatically
|
|
188
200
|
affinity_fn (callable): affinity function, default rbf_affinity. Should accept (X1, X2=None, sigma=float) and return affinity matrix
|
|
189
201
|
|
|
@@ -194,45 +206,44 @@ def nystrom_propagate(
|
|
|
194
206
|
config = NystromConfig()
|
|
195
207
|
config.update(kwargs)
|
|
196
208
|
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return all_outs
|
|
209
|
+
device = auto_device(nystrom_out.device, device)
|
|
210
|
+
output_device = X.device
|
|
211
|
+
indices = farthest_point_sampling(nystrom_out, config.n_sample2, device=device)
|
|
212
|
+
nystrom_out = nystrom_out[indices].to(device)
|
|
213
|
+
nystrom_X = nystrom_X[indices].to(device)
|
|
214
|
+
|
|
215
|
+
sigma = find_sigma_by_degree(nystrom_X, affinity_fn=rbf_affinity, quantile_sigma=0.25)
|
|
216
|
+
sigma = sigma * extrapolation_factor
|
|
217
|
+
|
|
218
|
+
D = rbf_affinity(nystrom_X, sigma=sigma).mean(1)
|
|
219
|
+
|
|
220
|
+
all_outs = []
|
|
221
|
+
n_chunk = config.matmul_chunk_size
|
|
222
|
+
n_neighbors = int(min(config.n_neighbors, len(indices)*config.n_neighbors_max_ratio))
|
|
223
|
+
n_neighbors = max(n_neighbors, 4)
|
|
224
|
+
for i in range(0, X.shape[0], n_chunk):
|
|
225
|
+
end = min(i + n_chunk, X.shape[0])
|
|
226
|
+
|
|
227
|
+
_Ai = rbf_affinity(X[i:end].to(device), nystrom_X, sigma=sigma)
|
|
228
|
+
_Ai, _indices = keep_topk_per_row(_Ai, n_neighbors) # (n, n_neighbors)
|
|
229
|
+
_Di = D[_indices].sum(1)
|
|
230
|
+
_Ai = _Ai / _Di[:, None]
|
|
231
|
+
|
|
232
|
+
weights = _Ai[..., None] # (n, n_neighbors, 1)
|
|
233
|
+
neighbors = nystrom_out[_indices.flatten()]
|
|
234
|
+
neighbors = neighbors.reshape(-1, n_neighbors, nystrom_out.shape[-1]) # (n, n_neighbors, d)
|
|
235
|
+
out = weights * neighbors # (n, n_neighbors, d)
|
|
236
|
+
out = out.sum(dim=1) # (n, d)
|
|
237
|
+
|
|
238
|
+
out = out.to(output_device)
|
|
239
|
+
all_outs.append(out)
|
|
240
|
+
|
|
241
|
+
all_outs = torch.cat(all_outs, dim=0)
|
|
242
|
+
|
|
243
|
+
if return_indices:
|
|
244
|
+
return all_outs, indices
|
|
245
|
+
|
|
246
|
+
return all_outs
|
|
236
247
|
|
|
237
248
|
|
|
238
249
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
__all__ = ["rbf_eigvec_manual_grad"
|
|
1
|
+
__all__ = ["rbf_eigvec_manual_grad"]
|
|
2
2
|
|
|
3
3
|
import torch
|
|
4
|
-
from contextlib import contextmanager
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
@torch.no_grad()
|
|
8
6
|
def rbf_eigvec_manual_grad(
|
|
@@ -115,40 +113,3 @@ def rbf_eigvec_manual_grad(
|
|
|
115
113
|
|
|
116
114
|
return grad_u
|
|
117
115
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@contextmanager
|
|
121
|
-
def grad_manager(enabled: bool):
|
|
122
|
-
"""Context manager to temporarily set gradient computation mode.
|
|
123
|
-
|
|
124
|
-
This context manager allows you to control gradient computation for a block
|
|
125
|
-
of code, and automatically restores the previous gradient state when exiting
|
|
126
|
-
the context.
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
enabled (bool): If True, enables gradient tracking within the context.
|
|
130
|
-
If False, disables gradient tracking within the context.
|
|
131
|
-
|
|
132
|
-
Yields:
|
|
133
|
-
None
|
|
134
|
-
|
|
135
|
-
Examples:
|
|
136
|
-
>>> import torch
|
|
137
|
-
>>> from ncut_pytorch.utils.grad import set_grad_enabled
|
|
138
|
-
>>>
|
|
139
|
-
>>> # Disable gradients for inference
|
|
140
|
-
>>> with set_grad_enabled(False):
|
|
141
|
-
... result = model(input_tensor)
|
|
142
|
-
>>>
|
|
143
|
-
>>> # Enable gradients for training
|
|
144
|
-
>>> with set_grad_enabled(True):
|
|
145
|
-
... loss = criterion(model(input_tensor), target)
|
|
146
|
-
... loss.backward()
|
|
147
|
-
"""
|
|
148
|
-
prev_grad_state = torch.is_grad_enabled()
|
|
149
|
-
torch.set_grad_enabled(enabled)
|
|
150
|
-
try:
|
|
151
|
-
yield
|
|
152
|
-
finally:
|
|
153
|
-
torch.set_grad_enabled(prev_grad_state)
|
|
154
|
-
|
|
@@ -18,7 +18,7 @@ import logging
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
import torch
|
|
20
20
|
|
|
21
|
-
from .torch_mod import svd_lowrank
|
|
21
|
+
from .torch_mod import svd_lowrank as my_svd_lowrank
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def check_gamma_deprecated(gamma: float | None) -> float:
|
|
@@ -122,6 +122,23 @@ def pca_lowrank(
|
|
|
122
122
|
return u @ torch.diag(s)
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
def svd_lowrank(mat: torch.Tensor, q: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
126
|
+
"""SVD lowrank implementation for float16 and bfloat16."""
|
|
127
|
+
dtype = mat.dtype
|
|
128
|
+
try:
|
|
129
|
+
with torch.autocast(device_type=mat.device.type, enabled=False):
|
|
130
|
+
if dtype == torch.float16 or dtype == torch.bfloat16:
|
|
131
|
+
mat = mat.float() # svd_lowrank does not support float16
|
|
132
|
+
u, s, v = my_svd_lowrank(mat, q=q + 10)
|
|
133
|
+
except RuntimeError:
|
|
134
|
+
if dtype == torch.float16 or dtype == torch.bfloat16:
|
|
135
|
+
mat = mat.float()
|
|
136
|
+
u, s, v = my_svd_lowrank(mat, q=q + 10)
|
|
137
|
+
|
|
138
|
+
u, s, v = u[:, :q], s[:q], v[:, :q]
|
|
139
|
+
return u.to(dtype), s.to(dtype), v.to(dtype)
|
|
140
|
+
|
|
141
|
+
|
|
125
142
|
def quantile_min_max(
|
|
126
143
|
x: torch.Tensor,
|
|
127
144
|
q1: float = 0.01,
|
|
@@ -9,7 +9,7 @@ from .sample import farthest_point_sampling
|
|
|
9
9
|
@torch.no_grad()
|
|
10
10
|
def _find_sigma_by_degree(
|
|
11
11
|
X: torch.Tensor, # [n_samples, n_features]
|
|
12
|
-
|
|
12
|
+
quantile_sigma: float = 0.25,
|
|
13
13
|
affinity_fn: callable = rbf_affinity,
|
|
14
14
|
X2: torch.Tensor | None = None,
|
|
15
15
|
init_sigma: float = 0.5,
|
|
@@ -17,27 +17,21 @@ def _find_sigma_by_degree(
|
|
|
17
17
|
max_iter: int = 100,
|
|
18
18
|
) -> float:
|
|
19
19
|
"""Binary search for optimal sigma to achieve target mean edge weight."""
|
|
20
|
-
if
|
|
21
|
-
|
|
20
|
+
if quantile_sigma <= 0 or quantile_sigma >= 1:
|
|
21
|
+
raise ValueError(f"quantile_sigma must be between 0 and 1, got {quantile_sigma}")
|
|
22
22
|
sigma = init_sigma
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
current_degrees = affinity_fn(X, X2=X2, sigma=scale_inv_sigma).mean(1)
|
|
28
|
-
for _ in range(2):
|
|
29
|
-
current_degree = current_degrees.mean().item()
|
|
30
|
-
mask = current_degrees < current_degree
|
|
31
|
-
current_degrees = current_degrees[mask]
|
|
32
|
-
d_sigma = current_degrees.mean().item()
|
|
24
|
+
scale_inv_sigma = X.std(0).sum()
|
|
25
|
+
current_degrees = affinity_fn(X, X2=X2, sigma=scale_inv_sigma).mean(1)
|
|
26
|
+
target_degree = current_degrees.float().quantile(quantile_sigma).item()
|
|
33
27
|
|
|
34
28
|
# Binary search for sigma
|
|
35
29
|
current_degree = affinity_fn(X, X2=X2, sigma=sigma).mean().item()
|
|
36
30
|
low, high = 0, float('inf')
|
|
37
|
-
tol = r_tol *
|
|
31
|
+
tol = r_tol * target_degree
|
|
38
32
|
i_iter = 0
|
|
39
|
-
while abs(current_degree -
|
|
40
|
-
if current_degree >
|
|
33
|
+
while abs(current_degree - target_degree) > tol and i_iter < max_iter:
|
|
34
|
+
if current_degree > target_degree:
|
|
41
35
|
high = sigma
|
|
42
36
|
sigma = (low + sigma) / 2
|
|
43
37
|
else:
|
|
@@ -52,7 +46,7 @@ def _find_sigma_by_degree(
|
|
|
52
46
|
@torch.no_grad()
|
|
53
47
|
def find_sigma_by_degree(
|
|
54
48
|
X: torch.Tensor, # [n_samples, n_features]
|
|
55
|
-
|
|
49
|
+
quantile_sigma: float = 0.25,
|
|
56
50
|
affinity_fn: callable = rbf_affinity,
|
|
57
51
|
X2: torch.Tensor | None = None,
|
|
58
52
|
init_sigma: float = 0.5,
|
|
@@ -62,4 +56,4 @@ def find_sigma_by_degree(
|
|
|
62
56
|
) -> float:
|
|
63
57
|
"""Find sigma after FPS-based downsampling for efficiency."""
|
|
64
58
|
indices = farthest_point_sampling(X, n_sample)
|
|
65
|
-
return _find_sigma_by_degree(X[indices],
|
|
59
|
+
return _find_sigma_by_degree(X[indices], quantile_sigma, affinity_fn, X2=X2, init_sigma=init_sigma, r_tol=r_tol, max_iter=max_iter)
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
__all__ = ['ncut_click_prompt']
|
|
2
|
-
|
|
3
|
-
from typing import Callable, Union
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import torch
|
|
7
|
-
|
|
8
|
-
from ncut_pytorch.utils.sigma import find_sigma_by_degree
|
|
9
|
-
from ncut_pytorch.utils.math import rbf_affinity, cosine_affinity, normalize_affinity
|
|
10
|
-
from ncut_pytorch.utils.sample import farthest_point_sampling
|
|
11
|
-
from ncut_pytorch.utils.device import auto_device
|
|
12
|
-
from ncut_pytorch.utils.grad import grad_manager
|
|
13
|
-
from .ncut_nystrom import NystromConfig
|
|
14
|
-
from .ncut_nystrom import nystrom_propagate
|
|
15
|
-
from .ncut_nystrom import _plain_ncut
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#TODO: automatically optimize click_weight based on the iou of fg and bg
|
|
19
|
-
def ncut_click_prompt(
|
|
20
|
-
X: torch.Tensor,
|
|
21
|
-
fg_indices: np.ndarray,
|
|
22
|
-
bg_indices: np.ndarray = None,
|
|
23
|
-
click_weight: float = 0.5,
|
|
24
|
-
bg_weight: float = 0.1,
|
|
25
|
-
n_eig: int = 2,
|
|
26
|
-
track_grad: bool = False,
|
|
27
|
-
d_sigma: float = None,
|
|
28
|
-
device: str = None,
|
|
29
|
-
sigma: float = None,
|
|
30
|
-
affinity_fn: Callable[[torch.Tensor, torch.Tensor, float], torch.Tensor] = rbf_affinity,
|
|
31
|
-
no_propagation: bool = False,
|
|
32
|
-
return_indices_and_sigma: bool = False,
|
|
33
|
-
**kwargs,
|
|
34
|
-
) -> Union[tuple[torch.Tensor, torch.Tensor], tuple[torch.Tensor, torch.Tensor, torch.Tensor, float]]:
|
|
35
|
-
|
|
36
|
-
config = NystromConfig()
|
|
37
|
-
config.update(kwargs)
|
|
38
|
-
|
|
39
|
-
# use GPU if available
|
|
40
|
-
device = auto_device(X.device, device)
|
|
41
|
-
|
|
42
|
-
with grad_manager(track_grad):
|
|
43
|
-
if bg_indices is None:
|
|
44
|
-
bg_indices = np.array([], dtype=np.int64)
|
|
45
|
-
|
|
46
|
-
# subsample for nystrom approximation
|
|
47
|
-
nystrom_indices = farthest_point_sampling(X, n_sample=config.n_sample, device=device)
|
|
48
|
-
nystrom_indices = torch.tensor(nystrom_indices, dtype=torch.long)
|
|
49
|
-
# remove fg and bg from fps_idx
|
|
50
|
-
nystrom_indices = nystrom_indices[~np.isin(nystrom_indices, np.concatenate([fg_indices, bg_indices]))]
|
|
51
|
-
# add fg and bg to fps_idx
|
|
52
|
-
nystrom_indices = np.concatenate([fg_indices, bg_indices, nystrom_indices])
|
|
53
|
-
fg_indices = np.arange(len(fg_indices))
|
|
54
|
-
bg_indices = np.arange(len(bg_indices)) + len(fg_indices)
|
|
55
|
-
n_fgbg = len(fg_indices) + len(bg_indices)
|
|
56
|
-
|
|
57
|
-
nystrom_X = X[nystrom_indices].to(device)
|
|
58
|
-
|
|
59
|
-
# find optimal sigma for affinity matrix
|
|
60
|
-
if sigma is None and affinity_fn == rbf_affinity:
|
|
61
|
-
sigma = find_sigma_by_degree(nystrom_X, d_sigma, affinity_fn)
|
|
62
|
-
# TODO: change to std()
|
|
63
|
-
elif sigma is None and affinity_fn == cosine_affinity:
|
|
64
|
-
sigma = 0.5
|
|
65
|
-
|
|
66
|
-
# compute Ncut on the nystrom sampled subgraph
|
|
67
|
-
A = affinity_fn(nystrom_X, sigma=sigma)
|
|
68
|
-
A = normalize_affinity(A)
|
|
69
|
-
|
|
70
|
-
# modify the affinity from the clicks
|
|
71
|
-
X_click = 1 * A[fg_indices].mean(0)
|
|
72
|
-
if len(bg_indices) > 0:
|
|
73
|
-
X_click = X_click - bg_weight * A[bg_indices].mean(0)
|
|
74
|
-
|
|
75
|
-
X_click = X_click * A.shape[0]
|
|
76
|
-
|
|
77
|
-
A_click = affinity_fn(X_click.unsqueeze(1), sigma=0.5)
|
|
78
|
-
A_click = normalize_affinity(A_click)
|
|
79
|
-
|
|
80
|
-
_A = click_weight * A_click + (1 - click_weight) * A
|
|
81
|
-
|
|
82
|
-
nystrom_eigvec, eigval = _plain_ncut(_A, n_eig)
|
|
83
|
-
|
|
84
|
-
if no_propagation:
|
|
85
|
-
return nystrom_eigvec, eigval, nystrom_indices, sigma
|
|
86
|
-
|
|
87
|
-
# propagate eigenvectors from subgraph to full graph
|
|
88
|
-
eigvec, nystrom_indices2 = nystrom_propagate(
|
|
89
|
-
nystrom_eigvec,
|
|
90
|
-
X,
|
|
91
|
-
nystrom_X,
|
|
92
|
-
n_neighbors=config.n_neighbors,
|
|
93
|
-
n_sample=config.n_sample2,
|
|
94
|
-
matmul_chunk_size=config.matmul_chunk_size,
|
|
95
|
-
device=device,
|
|
96
|
-
move_output_to_cpu=config.move_output_to_cpu,
|
|
97
|
-
track_grad=track_grad,
|
|
98
|
-
return_indices=True,
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if return_indices_and_sigma:
|
|
103
|
-
indices = nystrom_indices[nystrom_indices2]
|
|
104
|
-
return eigvec, eigval, indices, sigma
|
|
105
|
-
|
|
106
|
-
return eigvec, eigval
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/hires_dino.py
RENAMED
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/lowres_dino.py
RENAMED
|
File without changes
|
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino/transform.py
RENAMED
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/dino_predictor.py
RENAMED
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/jafar_predictor.py
RENAMED
|
File without changes
|
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch/predictor/vision_predictor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ncut_pytorch-3.0.0.dev0 → ncut_pytorch-3.0.0.dev2}/ncut_pytorch.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|