From 477dd39e32697901efd8eead6094635b63be0872 Mon Sep 17 00:00:00 2001 From: razon1494 Date: Fri, 15 Aug 2025 12:14:07 -0500 Subject: [PATCH 1/5] attack deffense test --- examples/defense_cora.py | 14 +++ examples/mea_cora.py | 21 +++++ pygip.egg-info/PKG-INFO | 131 ++++++++++++++++++++++++++++ pygip.egg-info/SOURCES.txt | 61 +++++++++++++ pygip.egg-info/dependency_links.txt | 1 + pygip.egg-info/requires.txt | 15 ++++ pygip.egg-info/top_level.txt | 3 + reqs.txt | 2 +- 8 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 examples/defense_cora.py create mode 100644 examples/mea_cora.py create mode 100644 pygip.egg-info/PKG-INFO create mode 100644 pygip.egg-info/SOURCES.txt create mode 100644 pygip.egg-info/dependency_links.txt create mode 100644 pygip.egg-info/requires.txt create mode 100644 pygip.egg-info/top_level.txt diff --git a/examples/defense_cora.py b/examples/defense_cora.py new file mode 100644 index 0000000..af5a118 --- /dev/null +++ b/examples/defense_cora.py @@ -0,0 +1,14 @@ +# examples/defense_cora.py +from datasets import Cora +# Your probe showed the module name is capitalized: +from models.defense.RandomWM import RandomWM # module: RandomWM, class: RandomWM + +def main(): + dataset = Cora() + mead = RandomWM(dataset, 0.25) # sampling ratio per README + print("Initialized defense; starting defend()...") + mead.defend() + print("Defense finished.") + +if __name__ == "__main__": + main() diff --git a/examples/mea_cora.py b/examples/mea_cora.py new file mode 100644 index 0000000..5997930 --- /dev/null +++ b/examples/mea_cora.py @@ -0,0 +1,21 @@ +from datasets import Cora + +MEA = None +try: + from models.attack import ModelExtractionAttack0 as MEA +except Exception: + try: + from models.attack import ModelExtractionAttack as MEA + except Exception: + # Direct import from the submodule if not re-exported + from models.attack.mea import ModelExtractionAttack0 as MEA + +def main(): + dataset = Cora() + mea = MEA(dataset, 0.25) # sampling ratio + print("Initialized MEA; starting attack...") + mea.attack() + print("Attack finished.") + +if __name__ == "__main__": + main() diff --git a/pygip.egg-info/PKG-INFO b/pygip.egg-info/PKG-INFO new file mode 100644 index 0000000..af47581 --- /dev/null +++ b/pygip.egg-info/PKG-INFO @@ -0,0 +1,131 @@ +Metadata-Version: 2.4 +Name: pygip +Version: 0.1.0 +Summary: A Python package for Graph Intellectual Property Protection +Home-page: https://github.com/pygip/pygip +Author: Bolin Shen +Author-email: blshen@fsu.edu +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Science/Research +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: torch>=1.7.0 +Requires-Dist: dgl>=0.6.0 +Requires-Dist: torch-geometric>=2.0.0 +Requires-Dist: numpy>=1.19.0 +Requires-Dist: scipy>=1.6.0 +Requires-Dist: networkx>=2.5 +Requires-Dist: scikit-learn>=0.24.0 +Requires-Dist: tqdm>=4.50.0 +Provides-Extra: dev +Requires-Dist: pytest>=6.0; extra == "dev" +Requires-Dist: pytest-cov>=2.0; extra == "dev" +Requires-Dist: flake8>=3.9.0; extra == "dev" +Requires-Dist: black>=21.5b2; extra == "dev" +Requires-Dist: isort>=5.8.0; extra == "dev" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license-file +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +# PyGIP + +PyGIP is a Python library designed for experimenting with graph-based model extraction attacks and defenses. It provides +a modular framework to implement and test attack and defense strategies on graph datasets. + +## Installation + +To get started with PyGIP, set up your environment by installing the required dependencies: + +```bash +pip install -r reqs.txt +``` + +Ensure you have Python installed (version 3.8 or higher recommended) along with the necessary libraries listed +in `reqs.txt`. + +Specifically, using following command to install `dgl 2.2.1` and ensure your `pytorch==2.3.0`. + +```shell +pip install dgl==2.2.1 -f https://data.dgl.ai/wheels/torch-2.3/repo.html +``` + +cuda + +```shell +pip install dgl==2.2.1 -f https://data.dgl.ai/wheels/torch-2.3/cu118/repo.html +``` + +## Quick Start + +Here’s a simple example to launch a Model Extraction Attack using PyGIP: + +```python +from datasets import Cora +from models.attack import ModelExtractionAttack0 + +# Load the Cora dataset +dataset = Cora() + +# Initialize the attack with a sampling ratio of 0.25 +mea = ModelExtractionAttack0(dataset, 0.25) + +# Execute the attack +mea.attack() +``` + +This code loads the Cora dataset, initializes a basic model extraction attack (`ModelExtractionAttack0`), and runs the +attack with a specified sampling ratio. + +And a simple example to run a Defense method against Model Extraction Attack: + +```python +from datasets import Cora +from models.defense import RandomWM + +# Load the Cora dataset +dataset = Cora() + +# Initialize the attack with a sampling ratio of 0.25 +mead = RandomWM(dataset, 0.25) + +# Execute the attack +mead.defend() +``` + +which runs the Random Watermarking Graph to defend against MEA. + +If you want to use cuda, please set environment variable: + +```shell +export PYGIP_DEVICE=cuda:0 +``` + +## Implementation & Contributors Guideline + +Refer to [Implementation Guideline](.github/IMPLEMENTATION.md) + +Refer to [Contributors Guideline](.github/CONTRIBUTING.md) + +## License + +MIT License + +## Contact + +For questions or contributions, please contact blshen@fsu.edu. diff --git a/pygip.egg-info/SOURCES.txt b/pygip.egg-info/SOURCES.txt new file mode 100644 index 0000000..663e6e4 --- /dev/null +++ b/pygip.egg-info/SOURCES.txt @@ -0,0 +1,61 @@ +LICENSE +MANIFEST.in +README.md +reqs.txt +setup.py +datasets/__init__.py +datasets/datasets.py +models/__init__.py +models/attack/AdvMEA.py +models/attack/__init__.py +models/attack/base.py +models/attack/mea/MEA.py +models/attack/mea/__init__.py +models/attack/mea/data/attack2_generated_graph/citeseer/graph_label.txt +models/attack/mea/data/attack2_generated_graph/citeseer/query_labels.txt +models/attack/mea/data/attack2_generated_graph/citeseer/selected_index.txt +models/attack/mea/data/attack2_generated_graph/cora/graph_label.txt +models/attack/mea/data/attack2_generated_graph/cora/query_labels.txt +models/attack/mea/data/attack2_generated_graph/cora/selected_index.txt +models/attack/mea/data/attack2_generated_graph/pubmed/graph_label.txt +models/attack/mea/data/attack2_generated_graph/pubmed/query_labels.txt +models/attack/mea/data/attack2_generated_graph/pubmed/selected_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/attack_6_sub_shadow_graph_index_attack_2.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/attack_6_sub_shadow_graph_index_attack_3.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/protential_1200_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/protential_1300_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/protential_500_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/protential_700_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/protential_900_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/citeseer/target_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/attack_6_sub_shadow_graph_index_attack_2.txt +models/attack/mea/data/attack3_shadow_graph/cora/attack_6_sub_shadow_graph_index_attack_3.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_1200_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_1300_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_300_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_500_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_700_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_900_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/protential_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/cora/target_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/attack_6_sub_shadow_graph_index_attack_2.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/attack_6_sub_shadow_graph_index_attack_3.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/protential_1200_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/protential_1300_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/protential_500_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/protential_700_shadow_graph_index.txt +models/attack/mea/data/attack3_shadow_graph/pubmed/protential_900_shadow_graph_index.txt +models/defense/RandomWM.py +models/defense/__init__.py +models/defense/base.py +models/nn/__init__.py +models/nn/backbones.py +pygip.egg-info/PKG-INFO +pygip.egg-info/SOURCES.txt +pygip.egg-info/dependency_links.txt +pygip.egg-info/requires.txt +pygip.egg-info/top_level.txt +utils/__init__.py +utils/dglTopyg.py +utils/hardware.py +utils/metrics.py \ No newline at end of file diff --git a/pygip.egg-info/dependency_links.txt b/pygip.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pygip.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pygip.egg-info/requires.txt b/pygip.egg-info/requires.txt new file mode 100644 index 0000000..07c2946 --- /dev/null +++ b/pygip.egg-info/requires.txt @@ -0,0 +1,15 @@ +torch>=1.7.0 +dgl>=0.6.0 +torch-geometric>=2.0.0 +numpy>=1.19.0 +scipy>=1.6.0 +networkx>=2.5 +scikit-learn>=0.24.0 +tqdm>=4.50.0 + +[dev] +pytest>=6.0 +pytest-cov>=2.0 +flake8>=3.9.0 +black>=21.5b2 +isort>=5.8.0 diff --git a/pygip.egg-info/top_level.txt b/pygip.egg-info/top_level.txt new file mode 100644 index 0000000..b36a5f6 --- /dev/null +++ b/pygip.egg-info/top_level.txt @@ -0,0 +1,3 @@ +datasets +models +utils diff --git a/reqs.txt b/reqs.txt index fd6522d..7a2ee54 100644 --- a/reqs.txt +++ b/reqs.txt @@ -6,7 +6,7 @@ async-timeout==5.0.1 attrs==25.3.0 certifi==2024.7.4 charset-normalizer==3.3.2 -dgl==2.2.0 +dgl==2.2.1 filelock==3.15.4 frozenlist==1.5.0 fsspec==2024.6.1 From bbbd23c085645db453f5ad18d059cbc666dbb643 Mon Sep 17 00:00:00 2001 From: razon1494 Date: Sun, 17 Aug 2025 06:53:46 -0500 Subject: [PATCH 2/5] Dummy Attack Tested --- examples/mea_cora_attack6.py | 12 +++ models/attack/__init__.py | 4 +- models/attack/mea/MEA.py | 182 +++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 examples/mea_cora_attack6.py diff --git a/examples/mea_cora_attack6.py b/examples/mea_cora_attack6.py new file mode 100644 index 0000000..b655203 --- /dev/null +++ b/examples/mea_cora_attack6.py @@ -0,0 +1,12 @@ +from datasets import Cora +from models.attack import ModelExtractionAttack6 as MEA + +def main(): + dataset = Cora() + mea = MEA(dataset, 0.25) + print("Initialized MEA-6; starting attack...") + res = mea.attack() + print("Attack-6 finished. Results:", res) + +if __name__ == "__main__": + main() diff --git a/models/attack/__init__.py b/models/attack/__init__.py index ec6064d..9f08dd8 100644 --- a/models/attack/__init__.py +++ b/models/attack/__init__.py @@ -5,7 +5,8 @@ ModelExtractionAttack2, ModelExtractionAttack3, ModelExtractionAttack4, - ModelExtractionAttack5 + ModelExtractionAttack5, + ModelExtractionAttack6, ) __all__ = [ @@ -16,4 +17,5 @@ 'ModelExtractionAttack3', 'ModelExtractionAttack4', 'ModelExtractionAttack5', + 'ModelExtractionAttack6', ] diff --git a/models/attack/mea/MEA.py b/models/attack/mea/MEA.py index 61ebf81..12a95ef 100644 --- a/models/attack/mea/MEA.py +++ b/models/attack/mea/MEA.py @@ -1111,3 +1111,185 @@ def attack(self): print(f"Error type: {type(e)}") torch.cuda.empty_cache() raise + + + +class ModelExtractionAttack6(ModelExtractionAttack): + def __init__(self, dataset, attack_node_fraction, model_path=None, alpha=0.8): + super().__init__(dataset, attack_node_fraction, model_path) + self.alpha = alpha + + def get_nonzero_indices(self, matrix_row): + return np.where(matrix_row != 0)[0] + + def attack(self): + """ + Main attack procedure. + + 1. Samples a subset of nodes (`sub_graph_node_index`) for querying. + 2. Synthesizes features for neighboring nodes and their neighbors. + 3. Builds a sub-graph, trains a new GCN on it, and evaluates + fidelity & accuracy w.r.t. the target model. + """ + try: + torch.cuda.empty_cache() + g = self.graph.clone().to(self.device) + g_matrix = g.adjacency_matrix().to_dense().cpu().numpy() + del g + + sub_graph_node_index = np.random.choice( + self.num_nodes, self.attack_node_num, replace=False).tolist() + + batch_size = 32 + features_query = self.features.clone() + + syn_nodes = [] + for node_index in sub_graph_node_index: + one_step_node_index = self.get_nonzero_indices(g_matrix[node_index]).tolist() + syn_nodes.extend(one_step_node_index) + + for first_order_node_index in one_step_node_index: + two_step_node_index = self.get_nonzero_indices(g_matrix[first_order_node_index]).tolist() + syn_nodes.extend(two_step_node_index) + + sub_graph_syn_node_index = list(set(syn_nodes) - set(sub_graph_node_index)) + total_sub_nodes = list(set(sub_graph_syn_node_index + sub_graph_node_index)) + + # Process synthetic nodes in batches + for i in range(0, len(sub_graph_syn_node_index), batch_size): + batch_indices = sub_graph_syn_node_index[i:i + batch_size] + + for node_index in batch_indices: + features_query[node_index] = 0 + one_step_node_index = self.get_nonzero_indices(g_matrix[node_index]).tolist() + one_step_node_index = list(set(one_step_node_index).intersection(set(sub_graph_node_index))) + + num_one_step = len(one_step_node_index) + if num_one_step > 0: + for first_order_node_index in one_step_node_index: + this_node_degree = len(self.get_nonzero_indices(g_matrix[first_order_node_index])) + features_query[node_index] += ( + self.features[first_order_node_index] * self.alpha / + torch.sqrt(torch.tensor(num_one_step * this_node_degree, device=self.device)) + ) + + two_step_nodes = [] + for first_order_node_index in one_step_node_index: + two_step_nodes.extend(self.get_nonzero_indices(g_matrix[first_order_node_index]).tolist()) + + total_two_step_node_index = list(set(two_step_nodes) - set(one_step_node_index)) + total_two_step_node_index = list( + set(total_two_step_node_index).intersection(set(sub_graph_node_index))) + + num_two_step = len(total_two_step_node_index) + if num_two_step > 0: + for second_order_node_index in total_two_step_node_index: + this_node_first_step_nodes = self.get_nonzero_indices( + g_matrix[second_order_node_index]).tolist() + this_node_second_step_nodes = set() + + for nodes_in_this_node in this_node_first_step_nodes: + this_node_second_step_nodes.update( + self.get_nonzero_indices(g_matrix[nodes_in_this_node]).tolist()) + + this_node_second_step_nodes = this_node_second_step_nodes - set(this_node_first_step_nodes) + this_node_second_degree = len(this_node_second_step_nodes) + + if this_node_second_degree > 0: + features_query[node_index] += ( + self.features[second_order_node_index] * (1 - self.alpha) / + torch.sqrt( + torch.tensor(num_two_step * this_node_second_degree, device=self.device)) + ) + + torch.cuda.empty_cache() + + # Update masks + for i in range(self.num_nodes): + if i in sub_graph_node_index: + self.test_mask[i] = 0 + self.train_mask[i] = 1 + elif i in sub_graph_syn_node_index: + self.test_mask[i] = 1 + self.train_mask[i] = 0 + else: + self.test_mask[i] = 1 + self.train_mask[i] = 0 + + # Create subgraph adjacency matrix + sub_g = np.zeros((len(total_sub_nodes), len(total_sub_nodes))) + for sub_index in range(len(total_sub_nodes)): + sub_g[sub_index] = g_matrix[total_sub_nodes[sub_index], total_sub_nodes] + + del g_matrix + + sub_train_mask = self.train_mask[total_sub_nodes] + sub_features = features_query[total_sub_nodes] + sub_labels = self.labels[total_sub_nodes] + + # Get query labels + self.net1.eval() + with torch.no_grad(): + g = self.graph.to(self.device) + logits_query = self.net1(g, features_query) + _, labels_query = torch.max(logits_query, dim=1) + sub_labels_query = labels_query[total_sub_nodes] + del logits_query + + # Create DGL graph + sub_g = nx.from_numpy_array(sub_g) + sub_g.remove_edges_from(nx.selfloop_edges(sub_g)) + sub_g.add_edges_from(zip(sub_g.nodes(), sub_g.nodes())) + sub_g = DGLGraph(sub_g) + sub_g = sub_g.to(self.device) + + degs = sub_g.in_degrees().float() + norm = torch.pow(degs, -0.5) + norm[torch.isinf(norm)] = 0 + norm = norm.to(self.device) + sub_g.ndata['norm'] = norm.unsqueeze(1) + + # Train extraction model + net = GCN(self.num_features, self.num_classes).to(self.device) + optimizer = torch.optim.Adam(net.parameters(), lr=1e-2, weight_decay=5e-4) + best_performance_metrics = GraphNeuralNetworkMetric() + + print("=========Model Extracting(Attack)==========================") + for epoch in tqdm(range(200)): + net.train() + logits = net(sub_g, sub_features) + logp = F.log_softmax(logits, dim=1) + loss = F.nll_loss(logp[sub_train_mask], sub_labels_query[sub_train_mask]) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + with torch.no_grad(): + focus_gnn_metrics = GraphNeuralNetworkMetric( + 0, 0, net, g, self.features, self.test_mask, self.labels, labels_query + ) + focus_gnn_metrics.evaluate() + + best_performance_metrics.fidelity = max( + best_performance_metrics.fidelity, focus_gnn_metrics.fidelity) + best_performance_metrics.accuracy = max( + best_performance_metrics.accuracy, focus_gnn_metrics.accuracy) + + if epoch % 10 == 0: + torch.cuda.empty_cache() + + print("========================Final results (Attack 6):=========================================") + print(best_performance_metrics) + results = { + "fidelity": float(best_performance_metrics.fidelity), + "accuracy": float(best_performance_metrics.accuracy), + } + self.net2 = net + return results + + except RuntimeError as e: + print(f"Attack 6 Runtime error: {e}") + torch.cuda.empty_cache() + raise + From ae9b933a53a587456a33d96b57d09168ea43bca9 Mon Sep 17 00:00:00 2001 From: razon1494 Date: Sun, 17 Aug 2025 07:05:55 -0500 Subject: [PATCH 3/5] WIP: save before branch cleanup --- examples/example.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/example.py diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..f93a417 --- /dev/null +++ b/examples/example.py @@ -0,0 +1,13 @@ +# examples/example.py +from datasets import Cora +from models.attack import ModelExtractionAttack6 as MEA + +def main(): + dataset = Cora() + mea = MEA(dataset, 0.25) + print("Running Attack-6 on Cora...") + res = mea.attack() + print("Results:", res) + +if __name__ == "__main__": + main() From 9d72f2bdcc9a7f60f2508e72c9f56f70b7b9e902 Mon Sep 17 00:00:00 2001 From: razon1494 Date: Sun, 17 Aug 2025 07:23:47 -0500 Subject: [PATCH 4/5] chore: stop tracking egg-info; ignore build artifacts --- .gitignore | 8 ++ pygip.egg-info/PKG-INFO | 131 ---------------------------- pygip.egg-info/SOURCES.txt | 61 ------------- pygip.egg-info/dependency_links.txt | 1 - pygip.egg-info/requires.txt | 15 ---- pygip.egg-info/top_level.txt | 3 - 6 files changed, 8 insertions(+), 211 deletions(-) delete mode 100644 pygip.egg-info/PKG-INFO delete mode 100644 pygip.egg-info/SOURCES.txt delete mode 100644 pygip.egg-info/dependency_links.txt delete mode 100644 pygip.egg-info/requires.txt delete mode 100644 pygip.egg-info/top_level.txt diff --git a/.gitignore b/.gitignore index 1a9048e..d10bb44 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,11 @@ Icon #virtual environments folder .venv + +/pygip.egg-info/ +/*.egg-info +/__pycache__/ +/venv/ +/.venv/ +*.pyc +.DS_Store diff --git a/pygip.egg-info/PKG-INFO b/pygip.egg-info/PKG-INFO deleted file mode 100644 index af47581..0000000 --- a/pygip.egg-info/PKG-INFO +++ /dev/null @@ -1,131 +0,0 @@ -Metadata-Version: 2.4 -Name: pygip -Version: 0.1.0 -Summary: A Python package for Graph Intellectual Property Protection -Home-page: https://github.com/pygip/pygip -Author: Bolin Shen -Author-email: blshen@fsu.edu -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Science/Research -Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Operating System :: OS Independent -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: torch>=1.7.0 -Requires-Dist: dgl>=0.6.0 -Requires-Dist: torch-geometric>=2.0.0 -Requires-Dist: numpy>=1.19.0 -Requires-Dist: scipy>=1.6.0 -Requires-Dist: networkx>=2.5 -Requires-Dist: scikit-learn>=0.24.0 -Requires-Dist: tqdm>=4.50.0 -Provides-Extra: dev -Requires-Dist: pytest>=6.0; extra == "dev" -Requires-Dist: pytest-cov>=2.0; extra == "dev" -Requires-Dist: flake8>=3.9.0; extra == "dev" -Requires-Dist: black>=21.5b2; extra == "dev" -Requires-Dist: isort>=5.8.0; extra == "dev" -Dynamic: author -Dynamic: author-email -Dynamic: classifier -Dynamic: description -Dynamic: description-content-type -Dynamic: home-page -Dynamic: license-file -Dynamic: provides-extra -Dynamic: requires-dist -Dynamic: requires-python -Dynamic: summary - -# PyGIP - -PyGIP is a Python library designed for experimenting with graph-based model extraction attacks and defenses. It provides -a modular framework to implement and test attack and defense strategies on graph datasets. - -## Installation - -To get started with PyGIP, set up your environment by installing the required dependencies: - -```bash -pip install -r reqs.txt -``` - -Ensure you have Python installed (version 3.8 or higher recommended) along with the necessary libraries listed -in `reqs.txt`. - -Specifically, using following command to install `dgl 2.2.1` and ensure your `pytorch==2.3.0`. - -```shell -pip install dgl==2.2.1 -f https://data.dgl.ai/wheels/torch-2.3/repo.html -``` - -cuda - -```shell -pip install dgl==2.2.1 -f https://data.dgl.ai/wheels/torch-2.3/cu118/repo.html -``` - -## Quick Start - -Here’s a simple example to launch a Model Extraction Attack using PyGIP: - -```python -from datasets import Cora -from models.attack import ModelExtractionAttack0 - -# Load the Cora dataset -dataset = Cora() - -# Initialize the attack with a sampling ratio of 0.25 -mea = ModelExtractionAttack0(dataset, 0.25) - -# Execute the attack -mea.attack() -``` - -This code loads the Cora dataset, initializes a basic model extraction attack (`ModelExtractionAttack0`), and runs the -attack with a specified sampling ratio. - -And a simple example to run a Defense method against Model Extraction Attack: - -```python -from datasets import Cora -from models.defense import RandomWM - -# Load the Cora dataset -dataset = Cora() - -# Initialize the attack with a sampling ratio of 0.25 -mead = RandomWM(dataset, 0.25) - -# Execute the attack -mead.defend() -``` - -which runs the Random Watermarking Graph to defend against MEA. - -If you want to use cuda, please set environment variable: - -```shell -export PYGIP_DEVICE=cuda:0 -``` - -## Implementation & Contributors Guideline - -Refer to [Implementation Guideline](.github/IMPLEMENTATION.md) - -Refer to [Contributors Guideline](.github/CONTRIBUTING.md) - -## License - -MIT License - -## Contact - -For questions or contributions, please contact blshen@fsu.edu. diff --git a/pygip.egg-info/SOURCES.txt b/pygip.egg-info/SOURCES.txt deleted file mode 100644 index 663e6e4..0000000 --- a/pygip.egg-info/SOURCES.txt +++ /dev/null @@ -1,61 +0,0 @@ -LICENSE -MANIFEST.in -README.md -reqs.txt -setup.py -datasets/__init__.py -datasets/datasets.py -models/__init__.py -models/attack/AdvMEA.py -models/attack/__init__.py -models/attack/base.py -models/attack/mea/MEA.py -models/attack/mea/__init__.py -models/attack/mea/data/attack2_generated_graph/citeseer/graph_label.txt -models/attack/mea/data/attack2_generated_graph/citeseer/query_labels.txt -models/attack/mea/data/attack2_generated_graph/citeseer/selected_index.txt -models/attack/mea/data/attack2_generated_graph/cora/graph_label.txt -models/attack/mea/data/attack2_generated_graph/cora/query_labels.txt -models/attack/mea/data/attack2_generated_graph/cora/selected_index.txt -models/attack/mea/data/attack2_generated_graph/pubmed/graph_label.txt -models/attack/mea/data/attack2_generated_graph/pubmed/query_labels.txt -models/attack/mea/data/attack2_generated_graph/pubmed/selected_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/attack_6_sub_shadow_graph_index_attack_2.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/attack_6_sub_shadow_graph_index_attack_3.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/protential_1200_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/protential_1300_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/protential_500_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/protential_700_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/protential_900_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/citeseer/target_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/attack_6_sub_shadow_graph_index_attack_2.txt -models/attack/mea/data/attack3_shadow_graph/cora/attack_6_sub_shadow_graph_index_attack_3.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_1200_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_1300_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_300_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_500_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_700_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_900_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/protential_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/cora/target_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/attack_6_sub_shadow_graph_index_attack_2.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/attack_6_sub_shadow_graph_index_attack_3.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/protential_1200_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/protential_1300_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/protential_500_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/protential_700_shadow_graph_index.txt -models/attack/mea/data/attack3_shadow_graph/pubmed/protential_900_shadow_graph_index.txt -models/defense/RandomWM.py -models/defense/__init__.py -models/defense/base.py -models/nn/__init__.py -models/nn/backbones.py -pygip.egg-info/PKG-INFO -pygip.egg-info/SOURCES.txt -pygip.egg-info/dependency_links.txt -pygip.egg-info/requires.txt -pygip.egg-info/top_level.txt -utils/__init__.py -utils/dglTopyg.py -utils/hardware.py -utils/metrics.py \ No newline at end of file diff --git a/pygip.egg-info/dependency_links.txt b/pygip.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/pygip.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pygip.egg-info/requires.txt b/pygip.egg-info/requires.txt deleted file mode 100644 index 07c2946..0000000 --- a/pygip.egg-info/requires.txt +++ /dev/null @@ -1,15 +0,0 @@ -torch>=1.7.0 -dgl>=0.6.0 -torch-geometric>=2.0.0 -numpy>=1.19.0 -scipy>=1.6.0 -networkx>=2.5 -scikit-learn>=0.24.0 -tqdm>=4.50.0 - -[dev] -pytest>=6.0 -pytest-cov>=2.0 -flake8>=3.9.0 -black>=21.5b2 -isort>=5.8.0 diff --git a/pygip.egg-info/top_level.txt b/pygip.egg-info/top_level.txt deleted file mode 100644 index b36a5f6..0000000 --- a/pygip.egg-info/top_level.txt +++ /dev/null @@ -1,3 +0,0 @@ -datasets -models -utils From 0965da022acb359d4d54a84a898ad71f2804c641 Mon Sep 17 00:00:00 2001 From: razon1494 Date: Sun, 17 Aug 2025 21:42:42 -0500 Subject: [PATCH 5/5] chore(example): updated comments --- examples/defense_cora.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/defense_cora.py b/examples/defense_cora.py index af5a118..0e72bfa 100644 --- a/examples/defense_cora.py +++ b/examples/defense_cora.py @@ -1,11 +1,9 @@ -# examples/defense_cora.py from datasets import Cora -# Your probe showed the module name is capitalized: -from models.defense.RandomWM import RandomWM # module: RandomWM, class: RandomWM +from models.defense.RandomWM import RandomWM def main(): dataset = Cora() - mead = RandomWM(dataset, 0.25) # sampling ratio per README + mead = RandomWM(dataset, 0.25) # sampling ratio print("Initialized defense; starting defend()...") mead.defend() print("Defense finished.")