Skip to content

Commit 4560202

Browse files
committed
Add notebook demo for the black-box lookup table for existing predictions
Signed-off-by: GalHorowitz <[email protected]>
1 parent 76c8b23 commit 4560202

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"## ART BlackBox Classifier Lookup Table - Using existing predictions from classifiers"
8+
]
9+
},
10+
{
11+
"cell_type": "markdown",
12+
"metadata": {},
13+
"source": [
14+
"In this demo we will demonstrate how a set of existing samples and their predicted labels can be used in a black-box attack against a model which we no longer have access to.\n",
15+
"This will be demonstrated on the Nursery dataset (original dataset can be found here: https://archive.ics.uci.edu/ml/datasets/nursery).\n",
16+
"\n",
17+
"We have already preprocessed the dataset such that all categorical features are one-hot encoded, and the data was scaled using sklearn's StandardScaler."
18+
]
19+
},
20+
{
21+
"cell_type": "markdown",
22+
"metadata": {},
23+
"source": [
24+
"### Load Data"
25+
]
26+
},
27+
{
28+
"cell_type": "code",
29+
"execution_count": 1,
30+
"metadata": {},
31+
"outputs": [],
32+
"source": [
33+
"import os\n",
34+
"import sys\n",
35+
"sys.path.insert(0, os.path.abspath('..'))\n",
36+
"\n",
37+
"from art.utils import load_nursery\n",
38+
"\n",
39+
"(x_train, y_train), (x_test, y_test), _, _ = load_nursery(test_set=0.5)"
40+
]
41+
},
42+
{
43+
"cell_type": "markdown",
44+
"metadata": {},
45+
"source": [
46+
"### Train neural network model"
47+
]
48+
},
49+
{
50+
"cell_type": "code",
51+
"execution_count": 3,
52+
"metadata": {},
53+
"outputs": [
54+
{
55+
"name": "stdout",
56+
"output_type": "stream",
57+
"text": [
58+
"Base model accuracy: 0.9720592775548008\n"
59+
]
60+
}
61+
],
62+
"source": [
63+
"import numpy as np\n",
64+
"import torch\n",
65+
"from torch import nn, optim\n",
66+
"from torch.utils.data import DataLoader\n",
67+
"from torch.utils.data.dataset import Dataset\n",
68+
"from art.estimators.classification.pytorch import PyTorchClassifier\n",
69+
"\n",
70+
"class ModelToAttack(nn.Module):\n",
71+
"\n",
72+
" def __init__(self, num_classes, num_features):\n",
73+
" super(ModelToAttack, self).__init__()\n",
74+
"\n",
75+
" self.fc1 = nn.Sequential(\n",
76+
" nn.Linear(num_features, 1024),\n",
77+
" nn.Tanh(), )\n",
78+
"\n",
79+
" self.fc2 = nn.Sequential(\n",
80+
" nn.Linear(1024, 512),\n",
81+
" nn.Tanh(), )\n",
82+
"\n",
83+
" self.classifier = nn.Linear(512, num_classes)\n",
84+
" # self.softmax = nn.Softmax(dim=1)\n",
85+
"\n",
86+
" def forward(self, x):\n",
87+
" out = self.fc1(x)\n",
88+
" out = self.fc2(out)\n",
89+
" return self.classifier(out)\n",
90+
"\n",
91+
"mlp_model = ModelToAttack(4, 24)\n",
92+
"mlp_model = torch.nn.DataParallel(mlp_model)\n",
93+
"criterion = nn.CrossEntropyLoss()\n",
94+
"optimizer = optim.Adam(mlp_model.parameters(), lr=0.0001)\n",
95+
"\n",
96+
"class NurseryDataset(Dataset):\n",
97+
" def __init__(self, x, y=None):\n",
98+
" self.x = torch.from_numpy(x.astype(np.float64)).type(torch.FloatTensor)\n",
99+
"\n",
100+
" if y is not None:\n",
101+
" self.y = torch.from_numpy(y.astype(np.int8)).type(torch.LongTensor)\n",
102+
" else:\n",
103+
" self.y = torch.zeros(x.shape[0])\n",
104+
"\n",
105+
" def __len__(self):\n",
106+
" return len(self.x)\n",
107+
"\n",
108+
" def __getitem__(self, idx):\n",
109+
" if idx >= len(self.x):\n",
110+
" raise IndexError(\"Invalid Index\")\n",
111+
"\n",
112+
" return self.x[idx], self.y[idx]\n",
113+
"\n",
114+
"train_set = NurseryDataset(x_train, y_train)\n",
115+
"train_loader = DataLoader(train_set, batch_size=100, shuffle=True, num_workers=0)\n",
116+
"\n",
117+
"for epoch in range(20):\n",
118+
" for (input, targets) in train_loader:\n",
119+
" input, targets = torch.autograd.Variable(input), torch.autograd.Variable(targets)\n",
120+
"\n",
121+
" optimizer.zero_grad()\n",
122+
" outputs = mlp_model(input)\n",
123+
" loss = criterion(outputs, targets)\n",
124+
"\n",
125+
" loss.backward()\n",
126+
" optimizer.step()\n",
127+
"\n",
128+
"mlp_art_model = PyTorchClassifier(model=mlp_model, loss=criterion, optimizer=optimizer, input_shape=(24,), nb_classes=4)\n",
129+
"\n",
130+
"pred = np.array([np.argmax(arr) for arr in mlp_art_model.predict(x_test.astype(np.float32))])\n",
131+
"\n",
132+
"print('Base model accuracy: ', np.sum(pred == y_test) / len(y_test))"
133+
]
134+
},
135+
{
136+
"cell_type": "markdown",
137+
"metadata": {},
138+
"source": [
139+
"### Create a set of predictions while we have access to the model"
140+
]
141+
},
142+
{
143+
"cell_type": "code",
144+
"execution_count": 13,
145+
"metadata": {},
146+
"outputs": [],
147+
"source": [
148+
"attack_train_ratio = 0.5\n",
149+
"attack_member_size = int(len(x_train) * attack_train_ratio)\n",
150+
"attack_nonmember_size = int(len(x_test) * attack_train_ratio)\n",
151+
"\n",
152+
"# For training the attack model\n",
153+
"attack_x_member = x_train[:attack_member_size].astype(np.float32)\n",
154+
"attack_x_nonmember = x_test[:attack_nonmember_size].astype(np.float32)\n",
155+
"\n",
156+
"predicted_y_member = mlp_art_model.predict(attack_x_member)\n",
157+
"predicted_y_nonmember = mlp_art_model.predict(attack_x_nonmember)\n",
158+
"\n",
159+
"# For testing the attack model\n",
160+
"attack_x_member_test = x_train[attack_member_size:].astype(np.float32)\n",
161+
"attack_x_nonmember_test = x_train[attack_nonmember_size:].astype(np.float32)\n",
162+
"\n",
163+
"predicted_y_member_test = mlp_art_model.predict(attack_x_member_test)\n",
164+
"predicted_y_nonmember_test = mlp_art_model.predict(attack_x_nonmember_test)"
165+
]
166+
},
167+
{
168+
"cell_type": "markdown",
169+
"metadata": {},
170+
"source": [
171+
"### Create a black-box classifier based on exisiting predictions"
172+
]
173+
},
174+
{
175+
"cell_type": "code",
176+
"execution_count": 14,
177+
"metadata": {},
178+
"outputs": [],
179+
"source": [
180+
"from art.estimators.classification import BlackBoxClassifier\n",
181+
"\n",
182+
"existing_samples = np.vstack((attack_x_member, attack_x_nonmember, attack_x_member_test, attack_x_nonmember_test))\n",
183+
"existing_predictions = np.vstack((predicted_y_member, predicted_y_nonmember, predicted_y_member_test, predicted_y_nonmember_test))\n",
184+
"\n",
185+
"classifier = BlackBoxClassifier((existing_samples, existing_predictions), x_train[0].shape, 4)"
186+
]
187+
},
188+
{
189+
"cell_type": "markdown",
190+
"metadata": {},
191+
"source": [
192+
"### Black-box attack"
193+
]
194+
},
195+
{
196+
"cell_type": "code",
197+
"execution_count": 20,
198+
"metadata": {},
199+
"outputs": [
200+
{
201+
"name": "stdout",
202+
"output_type": "stream",
203+
"text": [
204+
"Member Accuracy 0.4949058351343007\n",
205+
"Non-Member Accuracy 0.7063908613769683\n",
206+
"Accuracy 0.6006483482556345\n"
207+
]
208+
}
209+
],
210+
"source": [
211+
"from art.attacks.inference.membership_inference import MembershipInferenceBlackBox\n",
212+
"\n",
213+
"bb_attack = MembershipInferenceBlackBox(classifier, attack_model_type='rf')\n",
214+
"\n",
215+
"# train attack model\n",
216+
"bb_attack.fit(attack_x_member, y_train[:attack_member_size], attack_x_nonmember, y_test[:attack_nonmember_size])\n",
217+
"\n",
218+
"# infer \n",
219+
"inferred_member_bb = bb_attack.infer(attack_x_member_test, y_train[attack_member_size:])\n",
220+
"inferred_nonmember_bb = bb_attack.infer(attack_x_nonmember_test, y_test[attack_nonmember_size:])\n",
221+
"\n",
222+
"# check accuracy\n",
223+
"member_acc = np.sum(inferred_member_bb) / len(inferred_member_bb)\n",
224+
"nonmember_acc = 1 - (np.sum(inferred_nonmember_bb) / len(inferred_nonmember_bb))\n",
225+
"acc = (member_acc * len(inferred_member_bb) + nonmember_acc * len(inferred_nonmember_bb)) / (len(inferred_member_bb) + len(inferred_nonmember_bb))\n",
226+
"\n",
227+
"print(\"Member Accuracy\", member_acc)\n",
228+
"print(\"Non-Member Accuracy\", nonmember_acc)\n",
229+
"print(\"Accuracy\", acc)"
230+
]
231+
}
232+
],
233+
"metadata": {
234+
"kernelspec": {
235+
"display_name": "Python 3 (ipykernel)",
236+
"language": "python",
237+
"name": "python3"
238+
},
239+
"language_info": {
240+
"codemirror_mode": {
241+
"name": "ipython",
242+
"version": 3
243+
},
244+
"file_extension": ".py",
245+
"mimetype": "text/x-python",
246+
"name": "python",
247+
"nbconvert_exporter": "python",
248+
"pygments_lexer": "ipython3",
249+
"version": "3.8.11"
250+
}
251+
},
252+
"nbformat": 4,
253+
"nbformat_minor": 2
254+
}

0 commit comments

Comments
 (0)