Skip to content

Commit 1954cd4

Browse files
committed
merged
2 parents af7e0c1 + 693ccaf commit 1954cd4

7 files changed

Lines changed: 90 additions & 66 deletions

File tree

code/dependencies/GCN.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import sys
2+
import os
3+
import json
4+
15
import torch
26
import torch.nn as nn
37
import torch.nn.functional as F
@@ -21,6 +25,8 @@
2125
from torch_geometric.loader import DataLoader
2226
from torch_geometric.utils import dropout_edge
2327

28+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
29+
from Graph import Graph
2430

2531
class SimpleGCN(nn.Module):
2632
def __init__(
@@ -232,39 +238,31 @@ def process_graph(graph):
232238
adj, features = CustomDataset.preprocess(adj, features)
233239
return graph.index, adj, features
234240

235-
def __init__(self, graphs, accuracies=None, use_tqdm=False):
236-
self.indexes = []
237-
self.adjs, self.features = [], []
241+
def __init__(self, models_dict_path, accuracies=None, use_tqdm=False):
242+
self.models_dict_path = models_dict_path
243+
238244
self.accuracies = (
239245
torch.tensor(accuracies, dtype=torch.float)
240246
if accuracies is not None
241247
else None
242248
)
243249

244-
iterator = tqdm(graphs) if use_tqdm else graphs
245-
246-
results = Parallel(n_jobs=-1)(
247-
delayed(CustomDataset.process_graph)(graph) for graph in iterator
248-
)
249-
250-
for index, adj, features in results:
251-
self.indexes.append(index)
252-
self.adjs.append(adj)
253-
self.features.append(features)
254-
255250
def __getitem__(self, index):
256-
adj = self.adjs[index]
257-
features = self.features[index]
251+
path = self.models_dict_path[index]
252+
with path.open("r", encoding="utf-8") as f:
253+
data = json.load(f)
254+
graph = Graph(data, index=index)
255+
_, adj, features = self.process_graph(graph)
258256
edge_index, _ = dense_to_sparse(adj)
259257

260258
data = Data(x=features, edge_index=edge_index)
261-
data.index = self.indexes[index]
259+
data.index = index
262260
if self.accuracies is not None:
263261
data.y = self.accuracies[index]
264262
return data
265263

266264
def __len__(self):
267-
return len(self.indexes)
265+
return len(self.models_dict_path)
268266

269267

270268
class TripletGraphDataset(Dataset):

code/dependencies/train_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class TrainConfig:
3939
batch_size_inference: int
4040
min_accuracy_for_pool: float
4141
plot_tsne: bool
42-
42+
43+
tmp_archs_path: str
4344
best_models_save_path: str
4445

4546
prepared_dataset_path: str
@@ -62,8 +63,7 @@ class TrainConfig:
6263
# Internal fields
6364
model_accuracy: Optional[Any] = field(default=None, init=False)
6465
model_diversity: Optional[Any] = field(default=None, init=False)
65-
models_dict: list = field(default_factory=list, init=False)
66-
graphs: Optional[Any] = field(default=None, init=False)
66+
models_dict_path: list = field(default_factory=list, init=False)
6767
diversity_matrix: Optional[np.ndarray] = field(default=None, init=False)
6868
discrete_diversity_matrix: Optional[np.ndarray] = field(default=None, init=False)
6969
base_train_dataset: Optional[Any] = field(default=None, init=False)

code/inference_surrogate.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import os
55
import json
66
import argparse
7+
import shutil
8+
from pathlib import Path
79

810
from torch_geometric.utils import dense_to_sparse
911
import matplotlib.pyplot as plt
@@ -81,6 +83,9 @@ def _find_dist_to_best(self, best_embs, emb):
8183
return dists.min().item()
8284

8385
def architecture_search(self):
86+
tmp_dir = Path(self.config.tmp_archs_path)
87+
shutil.rmtree(tmp_dir, ignore_errors=True)
88+
tmp_dir.mkdir(parents=True, exist_ok=True)
8489

8590
while len(self.config.best_models) < self.config.n_ensemble_models:
8691
print(
@@ -90,11 +95,18 @@ def architecture_search(self):
9095
# 1) Сгенерировать архитектуры
9196
arch_dicts = generate_arch_dicts(
9297
self.config.n_models_to_generate, use_tqdm=True
93-
) # list of dicts
98+
)
99+
100+
# 2) Сохраняем как JSON-файлы
101+
arch_paths = []
102+
for i, arch in enumerate(arch_dicts):
103+
path = tmp_dir / f"arch_{len(os.listdir(tmp_dir)) + i}.json"
104+
with path.open("w", encoding="utf-8") as f:
105+
json.dump(arch, f)
106+
arch_paths.append(path)
94107

95-
# 2) Построить графы и датасет
96-
graphs = [Graph(arch, index=i) for i, arch in enumerate(arch_dicts)]
97-
dataset = CustomDataset(graphs, use_tqdm=True)
108+
# 3) Датасет и DataLoader
109+
dataset = CustomDataset(arch_paths, use_tqdm=True)
98110
loader = DataLoader(
99111
dataset,
100112
batch_size=self.config.batch_size_inference,
@@ -103,7 +115,7 @@ def architecture_search(self):
103115
collate_fn=collate_graphs,
104116
)
105117

106-
# 3) Извлечь эмбеддинги (numpy)
118+
# 4) Извлечь эмбеддинги
107119
with torch.no_grad():
108120
emb_acc_np, _ = extract_embeddings(
109121
self.config.model_accuracy, loader, device, use_tqdm=True
@@ -112,47 +124,47 @@ def architecture_search(self):
112124
self.config.model_diversity, loader, device, use_tqdm=True
113125
)
114126

115-
# 4) Фильтрация по точности
116-
mask = (
117-
emb_acc_np >= self.config.min_accuracy_for_pool
118-
) # numpy boolean array, shape (N,)
127+
# 5) Фильтрация по точности
128+
mask = emb_acc_np >= self.config.min_accuracy_for_pool
129+
valid_paths = [p for p, ok in zip(arch_paths, mask) if ok]
130+
valid_div_embs = emb_div_np[mask].astype(np.float16)
131+
valid_accs = emb_acc_np[mask]
119132

120-
valid_archs = [arch for arch, ok in zip(arch_dicts, mask) if ok]
121-
valid_div_embs = emb_div_np[mask].astype(np.float16) # shape (n_valid, d)
122-
valid_accs = emb_acc_np[mask] # shape (n_valid,)
123-
124-
for arch, emb, acc in zip(valid_archs, valid_div_embs, valid_accs):
133+
# 6) Добавляем в пул
134+
for path, emb, acc in zip(valid_paths, valid_div_embs, valid_accs):
135+
arch = json.loads(path.read_text(encoding="utf-8"))
125136
self.config.potential_archs.append(arch)
126137
self.config.potential_embeddings.append(emb)
127138
self.config.potential_accuracies.append(acc)
128139

129-
# 6) Очистка временных переменных и сборка мусора
130-
del arch_dicts, graphs, dataset, loader
140+
# 7) Удаляем все файлы, которые не попали в пул
141+
kept = set(valid_paths)
142+
for path in arch_paths:
143+
if path not in kept and path.exists():
144+
path.unlink()
145+
146+
# 8) Очистка мусора
147+
del arch_dicts, arch_paths, dataset, loader
131148
del emb_acc_np, emb_div_np
132149
torch.cuda.empty_cache()
133150
gc.collect()
134151

135-
# 7) Если пул заполнен — выбираем наиболее разнообразные модели
152+
# 9) Если пул заполнен — выбираем наиболее разнообразные модели
136153
while (
137154
len(self.config.potential_archs) >= self.config.n_models_in_pool
138155
and len(self.config.best_models) < self.config.n_ensemble_models
139156
):
140157
if self.config.best_embeddings:
141-
best_arr = np.stack(
142-
self.config.best_embeddings
143-
) # shape (len(best), d)
144-
# Для каждого emb в пуле — минимальное расстояние до best_arr
158+
best_arr = np.stack(self.config.best_embeddings)
145159
distances = [
146160
np.min(np.linalg.norm(emb - best_arr, axis=1))
147161
for emb in self.config.potential_embeddings
148162
]
149163
else:
150-
# Для первой модели ничем не ограничены
151164
distances = [np.inf] * len(self.config.potential_embeddings)
152165

153166
farthest = int(np.argmax(distances))
154167

155-
# Добавляем в лучшие
156168
self.config.best_models.append(
157169
self.config.potential_archs.pop(farthest)
158170
)
@@ -164,6 +176,9 @@ def architecture_search(self):
164176
f"Selected #{len(self.config.best_models)}/{self.config.n_ensemble_models}: acc={acc:.4f}, dist={distances[farthest]:.4f}"
165177
)
166178

179+
# В конце удалим временные файлы полностью
180+
shutil.rmtree(tmp_dir, ignore_errors=True)
181+
167182
def select_central_models_by_clusters(self):
168183
"""
169184
Кластеризует potential_embeddings на K кластеров,

code/surrogate_hp.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"developer_mode": false,
77

88
"n_models": 300,
9+
910
"upper_margin": 0.75,
1011
"lower_margin": 0.25,
1112
"diversity_matrix_metric": "overlap",
@@ -16,7 +17,7 @@
1617

1718
"acc_num_epochs": 10,
1819
"acc_lr": 1e-2,
19-
"acc_final_lr": 1e-5,
20+
"acc_final_lr": 5e-4,
2021
"acc_dropout": 0.2,
2122
"acc_n_heads": 16,
2223
"draw_fig_acc": false,
@@ -36,9 +37,10 @@
3637
"n_models_in_pool": 128,
3738
"n_models_to_generate": 4096,
3839
"batch_size_inference": 4096,
39-
"min_accuracy_for_pool": 0.01,
40+
"min_accuracy_for_pool": 0.5,
4041
"plot_tsne": false,
4142
"best_models_save_path": "best_models/",
43+
"tmp_archs_path": "datasets/tmp_archs/",
4244

4345
"n_epochs_final": 1,
4446
"lr_final": 0.025,
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

code/train_surrogate.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,27 @@ def __init__(self, config: TrainConfig):
4343
self.dataset_path = Path(config.dataset_path)
4444

4545
def load_dataset(self) -> None:
46+
self.config.models_dict_path = []
4647
for i, file_path in enumerate(tqdm(
4748
self.dataset_path.rglob("*.json"), desc="Loading dataset"
4849
)):
49-
if i >= self.config.n_models:
50-
break
51-
try:
52-
data = json.loads(file_path.read_text(encoding="utf-8"))
53-
self.config.models_dict.append(data)
54-
except json.JSONDecodeError as e:
55-
logger.error("Error decoding JSON from %s: %s", file_path, e)
56-
if len(self.config.models_dict) < self.config.n_models:
50+
self.config.models_dict_path.append(file_path)
51+
52+
if len(self.config.models_dict_path) < self.config.n_models:
5753
raise ValueError(
58-
f"Only {len(self.config.models_dict)} models found, but n_models={self.config.n_models}"
54+
f"Only {len(self.config.models_dict_path)} model paths found, but n_models={self.config.n_models}"
5955
)
6056

61-
self.config.models_dict = random.sample(
62-
self.config.models_dict, self.config.n_models
57+
self.config.models_dict_path = random.sample(
58+
self.config.models_dict_path, self.config.n_models
6359
)
6460

6561
def _prepare_predictions(self, num_samples: Optional[int] = None):
6662
preds = []
67-
for data in self.config.models_dict:
68-
arr = np.array(data["valid_predictions"])
63+
for path in self.config.models_dict_path:
64+
with path.open("r", encoding="utf-8") as f:
65+
data = json.load(f)
66+
arr = np.array(data["test_predictions"])
6967
preds.append(arr[:num_samples] if num_samples else arr)
7068
return preds
7169

@@ -92,14 +90,18 @@ def create_discrete_diversity_matrix(self) -> None:
9290
D[M < lower[:, None]] = -1
9391
self.config.discrete_diversity_matrix = D
9492

95-
def convert_into_graphs(self) -> None:
96-
self.config.graphs = [
97-
Graph(d, index=i) for i, d in enumerate(self.config.models_dict)
98-
]
93+
def get_accuracies(self):
94+
accs = []
95+
for path in self.config.models_dict_path:
96+
with path.open("r", encoding="utf-8") as f:
97+
data = json.load(f)
98+
accs.append(data["test_accuracy"])
99+
return accs
100+
99101

100102
def create_datasets(self) -> None:
101-
accs = [d["valid_accuracy"] for d in self.config.models_dict]
102-
ds = CustomDataset(self.config.graphs, accs)
103+
accs = self.get_accuracies()
104+
ds = CustomDataset(self.config.models_dict_path, accs)
103105
train_n = int(self.config.train_size * self.config.n_models)
104106
self.config.base_train_dataset, self.config.base_valid_dataset = random_split(
105107
ds, [train_n, self.config.n_models - train_n]
@@ -222,12 +224,19 @@ def save_models(self) -> None:
222224
config = TrainConfig(**params)
223225

224226
trainer = SurrogateTrainer(config)
227+
print("Loading models")
225228
trainer.load_dataset()
229+
print("Getting diversuty matrix")
226230
trainer.get_diversity_matrix()
231+
print("Creating discrete diversity matrix")
227232
trainer.create_discrete_diversity_matrix()
228-
trainer.convert_into_graphs()
233+
print("Creating datasets")
229234
trainer.create_datasets()
235+
print("Creating dataloaders")
230236
trainer.create_dataloaders()
237+
print("Training accuracy surrogate")
231238
trainer.train_accuracy_model()
239+
print("Training diversity surrogate")
232240
trainer.train_diversity_model()
241+
print("Saving models")
233242
trainer.save_models()

0 commit comments

Comments
 (0)