11import json
2+ from copy import deepcopy
23
4+ from attacks .mi_attacks import MIAttacker
5+ from aux .data_info import DataInfo
36from data_structures .configs import ConfigPattern , PoisonAttackConfig , PoisonDefenseConfig , EvasionAttackConfig , \
47 EvasionDefenseConfig , MIAttackConfig , MIDefenseConfig
58from aux .utils import POISON_ATTACK_PARAMETERS_PATH , POISON_DEFENSE_PARAMETERS_PATH , \
69 EVASION_ATTACK_PARAMETERS_PATH , EVASION_DEFENSE_PARAMETERS_PATH , MI_ATTACK_PARAMETERS_PATH , \
710 MI_DEFENSE_PARAMETERS_PATH
811from datasets .gen_dataset import GeneralDataset
912from models_builder .attack_defense_manager import FrameworkAttackDefenseManager
10- from models_builder .gnn_models import GNNModelManager
13+ from models_builder .gnn_models import GNNModelManager , Metric
1114from web_interface .back_front .block import Block
1215from web_interface .back_front .utils import WebInterfaceError
1316
@@ -130,7 +133,7 @@ def __init__(
130133 ):
131134 super ().__init__ (* args , ** kwargs )
132135
133- self .gen_dataset = None
136+ self .gen_dataset : GeneralDataset = None
134137 self .model_manager : GNNModelManager = None
135138 self .metrics : list = None
136139
@@ -139,6 +142,9 @@ def __init__(
139142 "AD-ma" : None ,
140143 }
141144
145+ # Copy of the dataset before attacks applied.
146+ self ._gen_dataset_backup : GeneralDataset = None
147+
142148 def _init (
143149 self ,
144150 gen_dataset : GeneralDataset ,
@@ -150,27 +156,75 @@ def _init(
150156 return FrameworkAttackDefenseManager .available_ad_methods (
151157 self .gen_dataset , self .model_manager )
152158
159+ def _finalize (
160+ self
161+ ) -> bool :
162+ # Make a dataset backup
163+ if self ._gen_dataset_backup is None :
164+ # Make a dataset backup
165+ # FIXME This is a bad way - for large datasets very bad. It is a temporary solution
166+ self ._gen_dataset_backup = deepcopy (self .gen_dataset )
167+ else :
168+ # Restore dataset
169+ self ._restore_dataset ()
170+ return True
171+
172+ def _clear_configs (
173+ self
174+ ) -> None :
175+ self .ad_configs = {
176+ "AD-ea" : None ,
177+ "AD-ma" : None ,
178+ }
179+
153180 def do (
154181 self ,
155182 do ,
156183 params
157184 ) -> str :
158185 if do == "run with attacks" :
186+ # Effect of pressing 'accept'
187+ self ._finalize ()
188+ self ._is_set = True # to make diagram call unlock() when we break this block
189+
190+ self ._clear_configs ()
159191 for name , config in json .loads (params .get ('configs' )).items ():
160192 # FIXME check config
161193 self .ad_configs [name ] = ConfigPattern (
162194 ** config ,
163195 _import_path = NAME_TO_PATH [name ],
164196 _config_class = NAME_TO_CLASS [name ])
165197
166- if self .ad_configs ["AD-ea" ]:
167- self .model_manager .set_evasion_attacker (self .ad_configs ["AD-ea" ])
168- if self .ad_configs ["AD-ma" ]:
169- self .model_manager .set_mi_attacker (self .ad_configs ["AD-ma" ])
198+ self .model_manager .set_evasion_attacker (self .ad_configs ["AD-ea" ])
199+ self .model_manager .set_mi_attacker (self .ad_configs ["AD-ma" ])
200+
201+ # Apply attacks
202+ metrics_values = {}
203+ metrics_values ['After attacks' ] = self .model_manager .evaluate_model (
204+ gen_dataset = self .gen_dataset , metrics = self .metrics )
170205
171- metrics_values = self .model_manager .evaluate_model (
172- self .gen_dataset , metrics = self .metrics )
173- self .model_manager .compute_stats_data (self .gen_dataset , predictions = True , logits = True )
206+ # Get MI metrics
207+ import numpy as np
208+ assert not self .gen_dataset .is_multi ()
209+ target_list = np .random .choice (
210+ self .gen_dataset .info .nodes [0 ], size = 100 , replace = False )
211+ mask_loc = Metric .create_mask_by_target_list (
212+ y_true = self .gen_dataset .labels , target_list = target_list )
213+ # self.model_manager.evaluate_model(
214+ # gen_dataset=self.gen_dataset,
215+ # metrics=[Metric("F1", mask=mask_loc, average='macro')])
216+ # Apply MI attack on a special mask
217+ self .model_manager .mi_attacker .attack (
218+ gen_dataset = self .gen_dataset , model = self .model_manager .gnn ,
219+ mask_tensor = mask_loc )
220+ res = self .model_manager .mi_attacker .results .get (mask_loc )
221+ if res is not None :
222+ metrics_values ['MI attack results' ] = MIAttacker .compute_single_attack_accuracy (
223+ mask_loc , res , self .gen_dataset .train_mask )
224+
225+ # Update model logits and predictions
226+ self .model_manager .compute_stats_data (
227+ gen_dataset = self .gen_dataset , predictions = True , logits = True )
174228
175229 # print("metrics_values after attacks", metrics_values)
176230 stats_data = {k : self .gen_dataset .visible_part .filter (v )
@@ -179,6 +233,41 @@ def do(
179233 metrics_values = metrics_values , stats_data = stats_data , socket = self .socket )
180234 return ''
181235
236+ elif do == "save attack configs" :
237+ # We want to save the given config
238+ self ._clear_configs ()
239+ for name , config in json .loads (params .get ('configs' )).items ():
240+ # FIXME check config
241+ self .ad_configs [name ] = ConfigPattern (
242+ ** config ,
243+ _import_path = NAME_TO_PATH [name ],
244+ _config_class = NAME_TO_CLASS [name ])
245+ return self ._save_attack_confgis ()
246+
182247 else :
183248 raise WebInterfaceError (f"Unknown 'do' command '{ do } ' for model" )
184249
250+ def _save_attack_confgis (
251+ self
252+ ) -> str :
253+ # FIXME discuss scenario with Kirill
254+ # no sense to save model, only configs
255+ path = self .model_manager .save_model_executor ()
256+ self .gen_dataset .save_train_test_mask (path )
257+ DataInfo .refresh_models_dir_structure ()
258+ return str (path )
259+
260+ def _unlock (
261+ self
262+ ) -> None :
263+ # Retract changes - reset dataset as before evasion attacks
264+ # and remove attacks from model manager
265+ self ._restore_dataset ()
266+ self .model_manager .set_evasion_attacker (None )
267+ self .model_manager .set_mi_attacker (None )
268+
269+ def _restore_dataset (
270+ self
271+ ) -> None :
272+ # FIXME This is a bad way - for large datasets very bad. It is a temporary solution
273+ self .gen_dataset = deepcopy (self ._gen_dataset_backup )
0 commit comments