44import os
55import json
66import argparse
7+ import shutil
8+ from pathlib import Path
79
810from torch_geometric .utils import dense_to_sparse
911import 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 кластеров,
0 commit comments