nystrom-ncut 0.1.2__tar.gz → 0.1.3__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.
Files changed (23) hide show
  1. {nystrom_ncut-0.1.2/src/nystrom_ncut.egg-info → nystrom_ncut-0.1.3}/PKG-INFO +1 -1
  2. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/pyproject.toml +1 -1
  3. nystrom_ncut-0.1.3/requirements.txt +9 -0
  4. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/__init__.py +1 -0
  5. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/distance_utils.py +3 -1
  6. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/nystrom/nystrom_utils.py +2 -2
  7. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/sampling_utils.py +4 -15
  8. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/visualize_utils.py +4 -4
  9. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3/src/nystrom_ncut.egg-info}/PKG-INFO +1 -1
  10. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/tests/test.py +66 -12
  11. nystrom_ncut-0.1.2/requirements.txt +0 -7
  12. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/LICENSE +0 -0
  13. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/MANIFEST.in +0 -0
  14. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/README.md +0 -0
  15. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/setup.cfg +0 -0
  16. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/__init__.py +0 -0
  17. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/common.py +0 -0
  18. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/nystrom/__init__.py +0 -0
  19. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/nystrom/distance_realization.py +0 -0
  20. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut/nystrom/normalized_cut.py +0 -0
  21. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut.egg-info/SOURCES.txt +0 -0
  22. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut.egg-info/dependency_links.txt +0 -0
  23. {nystrom_ncut-0.1.2 → nystrom_ncut-0.1.3}/src/nystrom_ncut.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nystrom_ncut
3
- Version: 0.1.2
3
+ Version: 0.1.3
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/
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nystrom_ncut"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  authors = [
9
9
  { name = "Huzheng Yang", email = "huze.yann@gmail.com" },
10
10
  { name = "Wentinn Liao", email = "wentinn.liao@gmail.com" },
@@ -0,0 +1,9 @@
1
+ einops
2
+ scikit-learn==1.6.1
3
+ umap-learn
4
+ pycolormap-2d
5
+ tqdm
6
+ torch==2.5.1
7
+ pytorch3d==0.7.8
8
+ torchdata==0.10.1
9
+ torchvision==0.20.1
@@ -8,6 +8,7 @@ from .distance_utils import (
8
8
  )
9
9
  from .sampling_utils import (
10
10
  SampleConfig,
11
+ subsample_features,
11
12
  )
12
13
  from .visualize_utils import (
13
14
  extrapolate_knn,
@@ -41,7 +41,9 @@ def distance_from_features(
41
41
  D = torch.cdist(features, features_B, p=2) ** 2
42
42
 
43
43
  # Outlier-robust scale invariance using quantiles to estimate standard deviation
44
- stds = torch.quantile(features, q=torch.tensor((0.158655, 0.841345), device=features.device), dim=0)
44
+ c = 2.0
45
+ p = torch.erf(torch.tensor((-c, c), device=features.device) * (2 ** -0.5))
46
+ stds = torch.quantile(features, q=(p + 1) / 2, dim=0)
45
47
  stds = (stds[1] - stds[0]) / 2
46
48
  D = D / (2 * torch.linalg.norm(stds) ** 2)
47
49
  else:
@@ -12,7 +12,7 @@ from ..distance_utils import (
12
12
  )
13
13
  from ..sampling_utils import (
14
14
  SampleConfig,
15
- run_subgraph_sampling,
15
+ subsample_features,
16
16
  )
17
17
 
18
18
 
@@ -180,7 +180,7 @@ class OnlineNystromSubsampleFit(OnlineNystrom):
180
180
  if precomputed_sampled_indices is not None:
181
181
  self.anchor_indices = precomputed_sampled_indices
182
182
  else:
183
- self.anchor_indices = run_subgraph_sampling(
183
+ self.anchor_indices = subsample_features(
184
184
  features=features,
185
185
  disttype=self.distance,
186
186
  config=self.sample_config,
@@ -3,11 +3,10 @@ from dataclasses import dataclass
3
3
  from typing import Literal
4
4
 
5
5
  import torch
6
- from dgl.geometry import farthest_point_sampler
6
+ from pytorch3d.ops import sample_farthest_points
7
7
 
8
8
  from .distance_utils import (
9
9
  DistanceOptions,
10
- affinity_from_features,
11
10
  to_euclidean,
12
11
  )
13
12
 
@@ -25,7 +24,7 @@ class SampleConfig:
25
24
 
26
25
 
27
26
  @torch.no_grad()
28
- def run_subgraph_sampling(
27
+ def subsample_features(
29
28
  features: torch.Tensor,
30
29
  disttype: DistanceOptions,
31
30
  config: SampleConfig,
@@ -57,25 +56,15 @@ def run_subgraph_sampling(
57
56
 
58
57
  elif config.method == "fps_recursive":
59
58
  features = to_euclidean(features, disttype)
60
- sampled_indices = run_subgraph_sampling(
59
+ sampled_indices = subsample_features(
61
60
  features=features,
62
61
  disttype=disttype,
63
62
  config=SampleConfig(method="fps", num_sample=config.num_sample, fps_dim=config.fps_dim)
64
63
  )
65
-
66
64
  nc = config._ncut_obj
67
-
68
- A = affinity_from_features(features, affinity_focal_gamma=nc.kernel.affinity_focal_gamma, distance=nc.kernel.distance)
69
- R = torch.diag(torch.sum(A, dim=-1) ** -0.5)
70
- L = R @ A @ R
71
-
72
65
  for _ in range(config.n_iter):
73
66
  fps_features, eigenvalues = nc.fit_transform(features, precomputed_sampled_indices=sampled_indices)
74
67
 
75
- _L = fps_features @ torch.diag(eigenvalues) @ fps_features.mT
76
- RE = torch.abs(_L / L - 1)
77
-
78
- print(f"Iteration {_} --- max: {RE.max().item()}, mean: {RE.mean().item()}, min: {RE.min().item()}")
79
68
  fps_features = to_euclidean(fps_features[:, :config.fps_dim], "cosine")
80
69
  sampled_indices = torch.sort(fpsample(fps_features, config)).values
81
70
  else:
@@ -93,4 +82,4 @@ def fpsample(
93
82
  U, S, V = torch.pca_lowrank(features, q=config.fps_dim)
94
83
  features = U * S
95
84
 
96
- return farthest_point_sampler(features[None], config.num_sample)[0]
85
+ return sample_farthest_points(features[None], K=config.num_sample)[1][0]
@@ -19,7 +19,7 @@ from .distance_utils import (
19
19
  )
20
20
  from .sampling_utils import (
21
21
  SampleConfig,
22
- run_subgraph_sampling,
22
+ subsample_features,
23
23
  )
24
24
 
25
25
 
@@ -120,7 +120,7 @@ def extrapolate_knn_with_subsampling(
120
120
  device = full_output.device if device is None else device
121
121
 
122
122
  # sample subgraph
123
- anchor_indices = run_subgraph_sampling(
123
+ anchor_indices = subsample_features(
124
124
  features=full_features,
125
125
  disttype=distance,
126
126
  config=sample_config,
@@ -160,7 +160,7 @@ def _rgb_with_dimensionality_reduction(
160
160
  ) -> torch.Tensor:
161
161
 
162
162
  if True:
163
- _subgraph_indices = run_subgraph_sampling(
163
+ _subgraph_indices = subsample_features(
164
164
  features=features,
165
165
  disttype=disttype,
166
166
  config=SampleConfig(method="fps"),
@@ -172,7 +172,7 @@ def _rgb_with_dimensionality_reduction(
172
172
  distance=disttype,
173
173
  )
174
174
 
175
- subgraph_indices = run_subgraph_sampling(
175
+ subgraph_indices = subsample_features(
176
176
  features=features,
177
177
  disttype=disttype,
178
178
  config=SampleConfig(method="fps", num_sample=num_sample),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: nystrom_ncut
3
- Version: 0.1.2
3
+ Version: 0.1.3
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/
@@ -1,8 +1,9 @@
1
1
  import numpy as np
2
2
  import torch
3
+ import torch.nn as nn
3
4
  from matplotlib import pyplot as plt
4
5
 
5
- from src.nystrom_ncut import NCut, affinity_from_features, SampleConfig
6
+ from src.nystrom_ncut import NCut, affinity_from_features, SampleConfig, subsample_features
6
7
 
7
8
  # from ncut_pytorch.src import rgb_from_umap_sphere
8
9
  # from ncut_pytorch.src.new_ncut_pytorch import NewNCUT
@@ -48,12 +49,13 @@ if __name__ == "__main__":
48
49
  torch.manual_seed(1212)
49
50
  np.random.seed(1212)
50
51
 
51
- n, d = 1000, 2
52
- num_sample = 900
52
+
53
+ n, d = 10000, 2
54
+ num_sample = 30
53
55
 
54
56
  M = torch.randn((n, d))
55
- M[:int(0.9 * n)] += 2
56
- M[int(0.9 * n):] -= 2
57
+ M[:int(0.9 * n)] += 3
58
+ M[int(0.9 * n):] -= 3
57
59
 
58
60
  distance = "rbf"
59
61
 
@@ -61,6 +63,58 @@ if __name__ == "__main__":
61
63
  R = torch.diag(torch.sum(A, dim=-1) ** -0.5)
62
64
  L = R @ A @ R
63
65
 
66
+ n_components = 30 # num_sample
67
+ eig_solver = "svd_lowrank"
68
+
69
+ nc = NCut(
70
+ n_components=n_components,
71
+ sample_config=SampleConfig(method="random", num_sample=num_sample),
72
+ distance=distance,
73
+ eig_solver=eig_solver,
74
+ )
75
+
76
+ torch.seed()
77
+ indices = subsample_features(M, disttype=distance, config=SampleConfig(method="fps", num_sample=num_sample))
78
+ samples = nn.Parameter(M[indices])
79
+
80
+ optimizer = torch.optim.AdamW((samples,), lr=1e-1)
81
+
82
+ output_dir = "../output/anchor_features_descent"
83
+ relative_losses, absolute_losses = [], []
84
+ for it in range(1000):
85
+ if it % 10 == 0:
86
+ plt.scatter(*M.mT, color="red")
87
+ plt.scatter(*samples.mT.detach(), color="black")
88
+ plt.title(f"Iteration {it}")
89
+
90
+ plt.savefig(f"{output_dir}/iteration{it}.png")
91
+ plt.show()
92
+
93
+ all_points = torch.cat((samples, M), dim=0)
94
+
95
+ X, eigs = nc.fit_transform(all_points, precomputed_sampled_indices=torch.arange(num_sample))
96
+ X = X[num_sample:]
97
+
98
+ _L = X @ torch.diag(eigs) @ X.mT
99
+
100
+ relative_loss = torch.linalg.norm(_L / L - 1) ** 2
101
+ with torch.no_grad():
102
+ absolute_loss = torch.linalg.norm(_L - L) ** 2
103
+
104
+ optimizer.zero_grad()
105
+ relative_loss.backward()
106
+ optimizer.step()
107
+
108
+ print(f"Relative: {relative_loss.item()}, Absolute: {absolute_loss.item()}")
109
+ relative_losses.append(relative_loss.item())
110
+ absolute_losses.append(absolute_loss.item())
111
+
112
+ torch.save(torch.tensor(relative_losses), f"{output_dir}/relative_losses.pt")
113
+ torch.save(torch.tensor(absolute_losses), f"{output_dir}/absolute_losses.pt")
114
+
115
+ raise Exception()
116
+
117
+
64
118
  # C = L[num_sample:, num_sample:]
65
119
  #
66
120
  # _A = L[:num_sample, :num_sample]
@@ -82,20 +136,20 @@ if __name__ == "__main__":
82
136
 
83
137
  max_rel = []
84
138
  for _ in range(1):
85
- nc0 = NCut(
139
+ nc = NCut(
86
140
  n_components=n_components,
87
- # sample_config=SampleConfig(method="random", n_iter=10),
88
- sample_config=SampleConfig(method="fps_recursive", num_sample=num_sample, n_iter=101),
141
+ # sample_config=SampleConfig(method="random", num_sample=num_sample),
142
+ sample_config=SampleConfig(method="fps_recursive", num_sample=num_sample, n_iter=10),
89
143
  distance=distance,
90
144
  eig_solver=eig_solver,
91
145
  )
92
- X0, eigs0 = nc0.fit_transform(M)
146
+ X, eigs = nc.fit_transform(M)
93
147
 
94
- re0 = rel_error(X0, eigs0)
95
- max_rel.append(re0.max().item())
148
+ re = rel_error(X, eigs)
149
+ max_rel.append(re.max().item())
96
150
 
97
151
  if _ % 100 == 0:
98
- print_re(re0)
152
+ print_re(re)
99
153
  # print_re(re0)
100
154
 
101
155
  # plt.hist(max_rel, bins=30)
@@ -1,7 +0,0 @@
1
- einops
2
- scikit-learn
3
- umap-learn
4
- pycolormap-2d
5
- tqdm
6
- torch==2.1.0
7
- dgl==2.2.0
File without changes
File without changes
File without changes
File without changes