-
Notifications
You must be signed in to change notification settings - Fork 1
Experiments: Final Attack Success #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… uses target synthetic data.
…rent experimental setups
* Added testing several targets on multiple gpus * Added a comment
emersodb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this looks good. A few small comments.
| targets = [] | ||
| for target_id in target_ids: | ||
| # Override target model id in config | ||
| config.target_model.target_model_id = target_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overwriting the config object is a bit odd. If we go with my comments about sending specific pieces to the function instead of the config itself, I think that will also help you side-step this guy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am now passing the specific pieces that are actually needed in this function. However, I still need to send target_model_config: DictConfig, and update target_model_config.target_model_id = target_id in the for loop to change the target_id at runtime. This is necessary since attack_results_path and challenge_label_path are defined based on target_model_config.target_model_id which lets us avoid manually setting paths for each target model.
In order to avoid overriding this config value, I would need to manually set the paths in the code based on target_id or ask the user to define a path for every target model in config. This overriding is not ideal, but seems faster and easier for running experiments (also a big advantage of hydro I guess). I might still be missing something, and happy to discuss if you have ideas about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I see what you mean. Basically changing the target id has side-effects in the config for the other paths. That's good with me. It's just a bit more indirection than I'm used to 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right! Definitely, it is not ideal! We should think about improving it for sure.
📝 WalkthroughWalkthroughThis pull request adds a new script for computing ensemble attack success metrics. The script loads challenge labels and prediction probabilities from saved model outputs, computes attack success across multiple target models by calculating TPR at FPR=0.1, and saves results to a text file. Configuration is updated with a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
examples/ensemble_attack/compute_attack_success.py(1 hunks)examples/ensemble_attack/configs/experiment_config.yaml(1 hunks)examples/ensemble_attack/test_attack_model.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/ensemble_attack/compute_attack_success.py (1)
src/midst_toolkit/attacks/ensemble/metric_utils.py (1)
get_tpr_at_fpr(7-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: unit-tests
- GitHub Check: integration-tests
- GitHub Check: run-code-check
🔇 Additional comments (4)
examples/ensemble_attack/test_attack_model.py (1)
113-115: Only whitespace / formatting changeThis hunk just adds surrounding blank lines; the assertion and overall control flow are unchanged, so there’s no behavioral impact.
examples/ensemble_attack/compute_attack_success.py (2)
17-50: Loader helper is consistent and defensively implementedThe loader correctly validates the presence of the
.npyfile, loads probabilities and labels, and enforces a length match before returning, which aligns with how the predictions are produced elsewhere in the pipeline.
104-126: Main entrypoint wiring looks goodAsserting the presence of
attack_success_computation.target_ids_to_test, logging the IDs, and then delegating tocompute_attack_success_for_given_targetswith the specific config pieces keeps the entrypoint simple and readable.examples/ensemble_attack/configs/experiment_config.yaml (1)
106-107: New config block matches the attack-success script’s expectations
attack_success_computation.target_ids_to_testis named and structured exactly as used incompute_attack_success.main, so the wiring between config and script is coherent.
| predictions = [] | ||
| targets = [] | ||
| for target_id in target_ids: | ||
| # Override target model id in config as ``attack_probabilities_result_path`` and | ||
| # ``challenge_label_path`` are dependent on it and change in runtime. | ||
| target_model_config.target_model_id = target_id | ||
| # Load challenge labels and prediction probabilities | ||
| log(INFO, f"Loading challenge labels and prediction probabilities for target model ID {target_id}...") | ||
| test_target, test_prediction_probabilities = load_target_challenge_labels_and_probabilities( | ||
| metaclassifier_model_name=metaclassifier_model_name, | ||
| attack_results_path=Path(target_model_config.attack_probabilities_result_path), | ||
| challenge_label_path=Path(target_model_config.challenge_label_path), | ||
| ) | ||
| predictions.append(test_prediction_probabilities) | ||
| targets.append(test_target) | ||
|
|
||
| # Flatten arrays | ||
| predictions = np.concatenate(predictions) | ||
| targets = np.concatenate(targets) | ||
|
|
||
| assert len(predictions) == len(targets), "Number of predictions must match number of targets." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Script executed:
#!/bin/bash
# First, locate and examine the file
find . -type f -name "compute_attack_success.py" -path "*/ensemble_attack/*" | head -5Repository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
./examples/ensemble_attack/compute_attack_success.py
</shell_result>
Script executed:
#!/bin/bash
# Check file size first
wc -l ./examples/ensemble_attack/compute_attack_success.pyRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
130 ./examples/ensemble_attack/compute_attack_success.py
</shell_result>
Script executed:
#!/bin/bash
# Read the entire file
cat -n ./examples/ensemble_attack/compute_attack_success.pyRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
1 """Provided test prediction probabilities of several attacked target models,
2 this script computes and saves the attack success metric.
3 """
4
5 from logging import INFO
6 from pathlib import Path
7
8 import hydra
9 import numpy as np
10 import pandas as pd
11 from omegaconf import DictConfig
12
13 from midst_toolkit.attacks.ensemble.metric_utils import get_tpr_at_fpr
14 from midst_toolkit.common.logger import log
15
16
17 def load_target_challenge_labels_and_probabilities(
18 metaclassifier_model_name: str, attack_results_path: Path, challenge_label_path: Path
19 ) -> tuple[np.ndarray, np.ndarray]:
20 """
21 Loads and returns the challenge labels and test prediction probabilities for
22 a given target model.
23
24 Args:
25 metaclassifier_model_name: Name of the metaclassifier model used in the attack.
26 attack_results_path: Path to the directory where attack results are saved.
27 challenge_label_path: Path to the CSV file containing challenge labels.
28
29 Return:
30 A tuple containing:
31 - test_target: Numpy array of true membership labels for the challenge points.
32 - test_prediction_probabilities: Numpy array of prediction probabilities
33 outputted by the metaclassifier for the challenge points.
34 """
35 attack_result_file_path = attack_results_path / f"{metaclassifier_model_name}_test_pred_proba.npy"
36 assert attack_result_file_path.exists(), (
37 f"No file found at {attack_result_file_path}. Make sure the path is correct, or run the attack on the target model first."
38 )
39
40 # Load the attack results containing test prediction probabilities.
41 test_prediction_probabilities = np.load(attack_result_file_path)
42
43 # Challenge labels are the true membership labels for the challenge points.
44 test_target = pd.read_csv(challenge_label_path).to_numpy().squeeze()
45
46 assert len(test_prediction_probabilities) == len(test_target), (
47 "Number of challenge labels must match number of prediction probabilities."
48 )
49
50 return test_target, test_prediction_probabilities
51
52
53 def compute_attack_success_for_given_targets(
54 target_model_config: DictConfig,
55 target_ids: list[int],
56 experiment_directory: Path,
57 metaclassifier_model_name: str,
58 ) -> None:
59 """
60 Computes and saves the attack success metric given the test prediction probabilities
61 of several attacked target models by concatenating the target models' targets and predictions.
62 NOTE: This function does not compute the average success across all models but rather
63 treats all predictions and labels together for metric computation.
64
65 Args:
66 target_model_config: Configuration object for target models set in ``experiments_config.yaml``.
67 target_ids: List of target model IDs to compute the attack success for.
68 experiment_directory: Path to the base experiment directory where results are saved.
69 metaclassifier_model_name: Name of the metaclassifier model used in the attack.
70 """
71 predictions = []
72 targets = []
73 for target_id in target_ids:
74 # Override target model id in config as ``attack_probabilities_result_path`` and
75 # ``challenge_label_path`` are dependent on it and change in runtime.
76 target_model_config.target_model_id = target_id
77 # Load challenge labels and prediction probabilities
78 log(INFO, f"Loading challenge labels and prediction probabilities for target model ID {target_id}...")
79 test_target, test_prediction_probabilities = load_target_challenge_labels_and_probabilities(
80 metaclassifier_model_name=metaclassifier_model_name,
81 attack_results_path=Path(target_model_config.attack_probabilities_result_path),
82 challenge_label_path=Path(target_model_config.challenge_label_path),
83 )
84 predictions.append(test_prediction_probabilities)
85 targets.append(test_target)
86
87 # Flatten arrays
88 predictions = np.concatenate(predictions)
89 targets = np.concatenate(targets)
90
91 assert len(predictions) == len(targets), "Number of predictions must match number of targets."
92
93 # Compute TPR@FPR for all the target models
94 tpr_at_fpr = get_tpr_at_fpr(targets, predictions, max_fpr=0.1)
95
96 # Save the final attack success rate into a text file.
97 metric_save_path = experiment_directory / f"attack_success_for_{metaclassifier_model_name}.txt"
98
99 log(INFO, f"Saving attack success value of {tpr_at_fpr} TPR at FPR=0.1 to {metric_save_path}")
100 with open(metric_save_path, "w") as f:
101 f.write(f"Final TPR at FPR=0.1: {tpr_at_fpr:.4f}\n")
102
103
104 @hydra.main(config_path="configs", config_name="experiment_config", version_base=None)
105 def main(
106 config: DictConfig,
107 ) -> None:
108 """
109 Main function to compute and save the attack success metric given the test prediction probabilities
110 of several attacked target models.
111
112 Args:
113 config: Configuration object set in ``experiments_config.yaml``.
114 """
115 assert config.attack_success_computation.target_ids_to_test is not None, (
116 "Please specify target model IDs to compute attack success for in the config "
117 "by specifying `attack_success_computation.target_ids_to_test`."
118 )
119 target_ids = list(config.attack_success_computation.target_ids_to_test)
120 log(INFO, f"Computing attack success for target model IDs: {target_ids}...")
121 compute_attack_success_for_given_targets(
122 target_model_config=config.target_model,
123 target_ids=target_ids,
124 experiment_directory=Path(config.base_experiment_dir),
125 metaclassifier_model_name=config.metaclassifier.meta_classifier_model_name,
126 )
127
128
129 if __name__ == "__main__":
130 main()
</shell_result>
Script executed:
#!/bin/bash
# Search for other callers of the function
rg -n "compute_attack_success_for_given_targets" ./examples/ensemble_attack/Repository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
./examples/ensemble_attack/compute_attack_success.py:53:def compute_attack_success_for_given_targets(
./examples/ensemble_attack/compute_attack_success.py:121: compute_attack_success_for_given_targets(
</shell_result>
Script executed:
#!/bin/bash
# Search entire codebase for other callers
rg -n "compute_attack_success_for_given_targets" --type pyRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
examples/ensemble_attack/compute_attack_success.py:53:def compute_attack_success_for_given_targets(
examples/ensemble_attack/compute_attack_success.py:121: compute_attack_success_for_given_targets(
</shell_result>
Script executed:
#!/bin/bash
python3 << 'EOF'
import numpy as np
# Test 1: np.concatenate with empty list
print("Test 1: np.concatenate([])")
try:
result = np.concatenate([])
print(f" Result: {result}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
# Test 2: np.concatenate with single empty array
print("\nTest 2: np.concatenate([np.array([])])")
try:
result = np.concatenate([np.array([])])
print(f" Result: {result}")
print(f" Result shape: {result.shape}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
# Test 3: Check behavior with multiple empty arrays
print("\nTest 3: np.concatenate([np.array([]), np.array([])])")
try:
result = np.concatenate([np.array([]), np.array([])])
print(f" Result: {result}")
print(f" Result shape: {result.shape}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
EOFRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 1
stderr:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'numpy'
</shell_result>
Script executed:
#!/bin/bash
pip install numpy -q && python3 << 'EOF'
import numpy as np
# Test 1: np.concatenate with empty list
print("Test 1: np.concatenate([])")
try:
result = np.concatenate([])
print(f" Result: {result}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
# Test 2: np.concatenate with single empty array
print("\nTest 2: np.concatenate([np.array([])])")
try:
result = np.concatenate([np.array([])])
print(f" Result: {result}")
print(f" Result shape: {result.shape}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
# Test 3: Check behavior with multiple empty arrays
print("\nTest 3: np.concatenate([np.array([]), np.array([])])")
try:
result = np.concatenate([np.array([]), np.array([])])
print(f" Result: {result}")
print(f" Result shape: {result.shape}")
except Exception as e:
print(f" Exception: {type(e).__name__}: {e}")
EOFRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
Test 1: np.concatenate([])
Exception: ValueError: need at least one array to concatenate
Test 2: np.concatenate([np.array([])])
Result: []
Result shape: (0,)
Test 3: np.concatenate([np.array([]), np.array([])])
Result: []
Result shape: (0,)
stderr:
WARNING: The scripts f2py and numpy-config are installed in '/home/jailuser/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
</shell_result>
Script executed:
#!/bin/bash
# Search for config files and documentation about target_ids_to_test
fd -e yaml -e yml | xargs grep -l "target_ids_to_test" 2>/dev/null | head -10Repository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
examples/ensemble_attack/configs/experiment_config.yaml
</shell_result>
Script executed:
#!/bin/bash
cat -n examples/ensemble_attack/configs/experiment_config.yamlRepository: VectorInstitute/midst-toolkit
<shell_result>
Repository: VectorInstitute/midst-toolkit
Exit code: 0
stdout:
1 # Ensemble experiment configuration
2 # This config can be used to run both the Ensemble attack training (``run_attack.py``) and testing phases (``tets_attack_model.py``).
3 base_experiment_dir: examples/ensemble_attack/tabddpm_20k_experiment_data # Processed data, and experiment artifacts will be stored here
4 base_data_config_dir: examples/ensemble_attack/data_configs # Training and data type configs are saved under this directory
5
6 # Pipeline control
7 pipeline:
8 run_data_processing: true # Set this to false if you have already saved the processed data
9 run_shadow_model_training: true # Set this to false if shadow models are already trained and saved
10 run_metaclassifier_training: true
11
12 target_model: # This is only used for testing the attack on a real target model.
13 # This is for models trained on 20k data and generating 20k synthetic data
14 target_model_directory: /projects/midst-experiments/all_tabddpms/tabddpm_trained_with_20k/train/
15 target_model_id: 21 # Will be overridden per SLURM array task
16 target_model_name: tabddpm_${target_model.target_model_id}
17 target_synthetic_data_path: ${target_model.target_model_directory}/${target_model.target_model_name}/synthetic_data/20k/20k.csv
18 challenge_data_path: ${target_model.target_model_directory}/${target_model.target_model_name}/challenge_with_id.csv
19 challenge_label_path: ${target_model.target_model_directory}/${target_model.target_model_name}/challenge_label.csv
20
21 target_attack_artifact_dir: ${base_experiment_dir}/target_${target_model.target_model_id}_attack_artifacts/
22 attack_probabilities_result_path: ${target_model.target_attack_artifact_dir}/attack_model_${target_model.target_model_id}_proba
23 target_shadow_models_output_path: ${target_model.target_attack_artifact_dir}/tabddpm_${target_model.target_model_id}_shadows_dir
24
25
26 # Data paths
27 data_paths:
28 midst_data_path: /projects/midst-experiments/all_tabddpms # Used to collect the data
29 population_path: ${base_experiment_dir}/population_data # Path where the collected population data will be stored
30 processed_attack_data_path: ${base_experiment_dir}/attack_data # Path where the processed attack real train and evaluation data is stored
31 attack_evaluation_result_path: ${base_experiment_dir}/evaluation_results # Path where the attack evaluation results will be stored
32
33 model_paths:
34 metaclassifier_model_path: ${base_experiment_dir}/trained_models # Path where the trained metaclassifier model will be saved
35
36
37 # Dataset specific information used for processing in this example
38 data_processing_config:
39 population_attack_data_types_to_collect:
40 [
41 "tabddpm_trained_with_20k",
42 ]
43 challenge_attack_data_types_to_collect:
44 [
45 "tabddpm_trained_with_20k",
46 ]
47 population_splits: ["train"] # Data splits to be collected for population data
48 challenge_splits: ["train"] # Data splits to be collected for challenge points
49 # The column name in the data to be used for stratified splitting.
50 column_to_stratify: "trans_type" # Attention: This value is not documented in the original codebase.
51 folder_ranges: #Specify folder ranges for any of the mentioned splits.
52 train: [[1, 20]] # Folders to be used for train data collection in the experiments
53 # File names in MIDST data directories.
54 single_table_train_data_file_name: "train_with_id.csv"
55 multi_table_train_data_file_name: "trans.csv"
56 challenge_data_file_name: "challenge_with_id.csv"
57 population_sample_size: 40000 # Population size is the total data that your attack has access to.
58 # In experiments, this is sampled out of all the collected training data in case the available data
59 # is more than this number. Note that, half of this data is actually used for training, the other half
60 # is used for evaluation. For example, with 40k population size, only 20k is used for training the attack model.
61 # TODO: make sure to consider this in experiments.
62
63 # Training and data settings for shadow models (temporary, numbers subject to change)
64 shadow_training:
65 # Data Config files path used for training a TabDDPM model
66 training_json_config_paths: # Config json files used for tabddpm training on the trans table
67 table_domain_file_path: ${base_data_config_dir}/trans_domain.json
68 dataset_meta_file_path: ${base_data_config_dir}/dataset_meta.json
69 tabddpm_training_config_path: ${base_data_config_dir}/trans.json
70 # Model training artifacts are saved under shadow_models_data_path/workspace_name/exp_name
71 # Also, training configs for each shadow model are created under shadow_models_data_path.
72 shadow_models_output_path: ${base_experiment_dir}/shadow_models_and_data
73 target_model_output_path: ${base_experiment_dir}/shadow_target_model_and_data
74 # Paths to final shadow models used for metaclassifier training (relative to shadow_models_output_path)
75 # These paths are a result of running the shadow model training pipeline, specifically the
76 # train_three_sets_of_shadow_models in shadow_model_training.py
77 # Each .pkl file contains the training data, trained model and training results for all shadow models in a list.
78 final_shadow_models_path: [
79 "${shadow_training.shadow_models_output_path}/initial_model_rmia_1/shadow_workspace/pre_trained_model/rmia_shadows.pkl",
80 "${shadow_training.shadow_models_output_path}/initial_model_rmia_2/shadow_workspace/pre_trained_model/rmia_shadows.pkl",
81 "${shadow_training.shadow_models_output_path}/shadow_model_rmia_third_set/shadow_workspace/trained_model/rmia_shadows_third_set.pkl",
82 ]
83 target_synthetic_data_path: ${shadow_training.target_model_output_path}/target_synthetic_data.csv
84 # Path to final shadow target's synthetic data (relative to target_model_output_path)
85 fine_tuning_config:
86 fine_tune_diffusion_iterations: 200000 # Original code: 200000
87 fine_tune_classifier_iterations: 20000 # Original code: 20000
88 pre_train_data_size: 60000 # Original code: 60000
89 number_of_points_to_synthesize: 20000 # Number of synthetic data samples to be generated by shadow models.
90 # Original code: 20000
91
92
93 # Metaclassifier settings
94 metaclassifier:
95 # Data types json file is used for xgboost model training.
96 data_types_file_path: ${base_data_config_dir}/data_types.json
97 model_type: "xgb"
98 # Model training parameters
99 num_optuna_trials: 100 # Original code: 100
100 num_kfolds: 5
101 use_gpu: false
102 # Temporary. Might remove having an epoch parameter.
103 epochs: 1
104 meta_classifier_model_name: ${metaclassifier.model_type}_metaclassifier_model
105
106 attack_success_computation:
107 target_ids_to_test: [21,22,23] # List of target model IDs to compute the attack success for.
108
109 # General settings
110 random_seed: 42 # Set to null for no seed, or an integer for a fixed seed
</shell_result>
Guard against empty target_ids to avoid concatenation failure
The review comment is correct. Testing confirms that np.concatenate([]) raises ValueError: need at least one array to concatenate. If target_ids is empty, the for loop (line 73) doesn't execute, leaving predictions and targets as empty lists. When np.concatenate() is called on line 88-89, it will fail with a cryptic error. The entry validation in main() (lines 115-118) only checks if target_ids_to_test is not None, but doesn't validate that it's non-empty. Early validation with an explicit error message is clearer than relying on numpy's exception.
Suggested change:
def compute_attack_success_for_given_targets(
target_model_config: DictConfig,
target_ids: list[int],
experiment_directory: Path,
metaclassifier_model_name: str,
) -> None:
"""
Computes and saves the attack success metric given the test prediction probabilities
of several attacked target models by concatenating the target models' targets and predictions.
NOTE: This function does not compute the average success across all models but rather
treats all predictions and labels together for metric computation.
Args:
target_model_config: Configuration object for target models set in ``experiments_config.yaml``.
target_ids: List of target model IDs to compute the attack success for.
experiment_directory: Path to the base experiment directory where results are saved.
metaclassifier_model_name: Name of the metaclassifier model used in the attack.
"""
+ if not target_ids:
+ raise ValueError("target_ids must be a non-empty list of target model IDs.")
+
predictions = []
targets = []📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| predictions = [] | |
| targets = [] | |
| for target_id in target_ids: | |
| # Override target model id in config as ``attack_probabilities_result_path`` and | |
| # ``challenge_label_path`` are dependent on it and change in runtime. | |
| target_model_config.target_model_id = target_id | |
| # Load challenge labels and prediction probabilities | |
| log(INFO, f"Loading challenge labels and prediction probabilities for target model ID {target_id}...") | |
| test_target, test_prediction_probabilities = load_target_challenge_labels_and_probabilities( | |
| metaclassifier_model_name=metaclassifier_model_name, | |
| attack_results_path=Path(target_model_config.attack_probabilities_result_path), | |
| challenge_label_path=Path(target_model_config.challenge_label_path), | |
| ) | |
| predictions.append(test_prediction_probabilities) | |
| targets.append(test_target) | |
| # Flatten arrays | |
| predictions = np.concatenate(predictions) | |
| targets = np.concatenate(targets) | |
| assert len(predictions) == len(targets), "Number of predictions must match number of targets." | |
| if not target_ids: | |
| raise ValueError("target_ids must be a non-empty list of target model IDs.") | |
| predictions = [] | |
| targets = [] | |
| for target_id in target_ids: | |
| # Override target model id in config as ``attack_probabilities_result_path`` and | |
| # ``challenge_label_path`` are dependent on it and change in runtime. | |
| target_model_config.target_model_id = target_id | |
| # Load challenge labels and prediction probabilities | |
| log(INFO, f"Loading challenge labels and prediction probabilities for target model ID {target_id}...") | |
| test_target, test_prediction_probabilities = load_target_challenge_labels_and_probabilities( | |
| metaclassifier_model_name=metaclassifier_model_name, | |
| attack_results_path=Path(target_model_config.attack_probabilities_result_path), | |
| challenge_label_path=Path(target_model_config.challenge_label_path), | |
| ) | |
| predictions.append(test_prediction_probabilities) | |
| targets.append(test_target) | |
| # Flatten arrays | |
| predictions = np.concatenate(predictions) | |
| targets = np.concatenate(targets) | |
| assert len(predictions) == len(targets), "Number of predictions must match number of targets." |
🤖 Prompt for AI Agents
In examples/ensemble_attack/compute_attack_success.py around lines 71-91 (and
adjust main validation near lines 115-118), guard against an empty target_ids
list: before the loop, check if target_ids is empty and raise a clear ValueError
or log an error and exit (e.g., "target_ids must be a non-empty list") so
np.concatenate is never called on empty lists; additionally, strengthen the
validation in main() to ensure target_ids_to_test is not None and has at least
one element, returning or exiting with the same clear message if empty.
PR Type
[ Feature ]
Short Description
Clickup Ticket(s): Link
Added a script for attack success rate computation in the setting of experiments where we are attacking several target models, and have previously saved attack probabilities for each target model.
Tests Added
NA