-
Notifications
You must be signed in to change notification settings - Fork 0
/
do_ipoisoning.py
296 lines (236 loc) · 11.8 KB
/
do_ipoisoning.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
"""
Do indiscriminate poisoning (w. Eager Execution of TF)
"""
import os, sys
# suppress tensorflow errors -- too many, what's the reason...?
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import json
import argparse
import numpy as np
# tensorflow modules
import tensorflow as tf
from tensorflow.compat.v1.logging import set_verbosity, ERROR
from tensorflow.compat.v1.estimator.inputs import numpy_input_fn
# custom libs
from utils import io
from utils import datasets, models, optims
# ------------------------------------------------------------
# Compute validation accuracy
# ------------------------------------------------------------
def _validate(model, validset):
corrects = []
for (_, (data, labels)) in enumerate(validset.take(-1)):
logits, penultimate = model(data, training=False)
predicts = tf.argmax(logits, axis=1)
predicts = tf.dtypes.cast(predicts, tf.int32)
corrects.append(tf.equal(predicts, labels).numpy())
cur_acc = np.mean(corrects)
return cur_acc
"""
Main: to select the target and the poisons
"""
if __name__ == '__main__':
# --------------------------------------------------------------------------
# Arguments for this script: command line compatibility
# --------------------------------------------------------------------------
parser = argparse.ArgumentParser( \
description='Conduct the indiscriminate poisoning attacks (w/w.o DP-SGD).')
# load arguments
parser.add_argument('--pin-gpu', type=str, default='0',
help='the index of a GPU to pin (default: 0)')
# load arguments (use -es to fit the # of characters)
parser.add_argument('--dataset', type=str, default='subtask',
help='the name of a dataset (default: subtask)')
parser.add_argument('--datapth', type=str, default='...',
help='the location of a dataset (default: ...)')
parser.add_argument('--poisonp', type=str, default='...',
help='the location of a poison data (default: ...)')
# model parameters
parser.add_argument('--network', type=str, default='lr',
help='the name of a network (default: lr)')
parser.add_argument('--netbase', type=str, default='',
help='the location of the model file (default: ...)')
parser.add_argument('--privacy', action='store_true',
help='set this flag when we use DP-SGD')
# privacy-parameters
parser.add_argument('--epsilon', type=float, default=0.0,
help='epsilon as a privacy budget (default: 0.0)')
parser.add_argument('--delta', type=float, default=0.0,
help='delta as a privacy guarantee (default: 0.0)')
parser.add_argument('--nclip', type=float, default=0.0,
help='l2 value for clipping the norm (default: 0.0)')
parser.add_argument('--noise', type=float, default=0.0,
help='noise-level that adds to queries - sigma (default: 0.0)')
# load the arguments
args = parser.parse_args()
print (json.dumps(vars(args), indent=2))
# ------------------------------------------------------------
# Tensorflow configurations
# ------------------------------------------------------------
# enforce tensorflow use the specified GPU
os.environ["CUDA_VISIBLE_DEVICES"] = args.pin_gpu
print (' : Pin this task to GPU [{}]'.format(args.pin_gpu))
# control tensorflow info. level
set_verbosity(tf.compat.v1.logging.ERROR)
# enable eager execution
tf.enable_eager_execution()
# ------------------------------------------------------------
# Load the baseline model
# ------------------------------------------------------------
# extract the basic information from the baseline model (always vanilla)
net_tokens = args.netbase.split('/')
if 'subtask' == args.dataset:
# : subtask case
net_tokens = net_tokens[3].split('_')
else:
# : fashion_mnist/cifar10
net_tokens = net_tokens[2].split('_')
# model parameters
batch_size = int(net_tokens[2])
epochs = int(net_tokens[3])
learn_rate = float(net_tokens[4])
# error case
if 'dp_' in args.netbase:
assert False, ('Error: Baseline accuracy cannot come from a DP-model.')
# load the model
baseline_vars = models.extract_tf_model_parameters(args.network, args.netbase)
baseline_model = models.load_model( \
args.dataset, args.datapth, args.network, vars=baseline_vars)
print (' : Load the baseline model [{}] from [{}]'.format(args.network, args.netbase))
# ------------------------------------------------------------
# Load the dataset (Data + Poisons)
# ------------------------------------------------------------
# load the dataset
if args.poisonp.endswith('.pkl'):
(x_train, y_train), (x_test, y_test), (x_poison, y_poison) = \
datasets.load_lfip_poisons(args.poisonp)
elif args.poisonp.endswith('.mat'):
(x_train, y_train), (x_test, y_test), (x_poison, y_poison) = \
datasets.load_slab_poisons(args.poisonp)
else:
assert False, ('Error: unknown format file - {}'.format(args.poisonp))
# enforce the poisons to be within [0, 1] range
x_poison = np.clip(x_poison, 0., 1.)
# [DEBUG]
print (' : Load the poison data from [{}]'.format(args.poisonp))
print (' Train : {} in [{}, {}]'.format(x_train.shape, x_train.min(), x_train.max()))
print (' Test : {} in [{}, {}]'.format(x_test.shape, x_test.min(), x_test.max()))
print (' Poison: {} in [{}, {}]'.format(x_poison.shape, x_poison.min(), x_poison.max()))
# compose into the tensorflow datasets
clean_validset = datasets.convert_to_tf_dataset(x_test, y_test)
# to examine the training time accuracy on clean and poison samples
ctrain_examine = datasets.convert_to_tf_dataset(x_train, y_train)
ptrain_examine = datasets.convert_to_tf_dataset(x_poison, y_poison)
# load the baseline acc
baseline_acc = _validate(baseline_model, clean_validset)
print (' : Baseline model\'s accuracy is [{}]'.format(baseline_acc))
# --------------------------------------------------------------------------
# Set the location to store...
# --------------------------------------------------------------------------
# extract the setup
poison_task = args.poisonp.split('/')[3]
poison_data = args.poisonp.split('/')[4].replace('.pkl', '')
# compose
store_base = os.path.join( \
'results', 'ipoisoning', poison_task, poison_data)
# fix store locations for each
if not args.privacy:
netname_pfix = 'vanilla_{}_{}_{}_{}'.format( \
args.network, batch_size, epochs, learn_rate)
else:
netname_pfix = 'dp_{}_{}_{}_{}_{}_{}_{}_{}'.format( \
args.network, batch_size, epochs, learn_rate, \
args.epsilon, args.delta, args.nclip, args.noise)
results_model = os.path.join(store_base, netname_pfix)
if not os.path.exists(results_model): os.makedirs(results_model)
results_data = os.path.join(results_model, 'attack_results.csv')
# [DEBUG]
print (' : Store locations are:')
print (' - Model folder: {}'.format(results_model))
print (' - Attack data : {}'.format(results_data))
# --------------------------------------------------------------------------
# Compose the poison dataset
# --------------------------------------------------------------------------
x_total = np.concatenate((x_train, x_poison), axis=0)
y_total = np.concatenate((y_train, y_poison), axis=0)
poison_trainset = datasets.convert_to_tf_dataset( \
x_total, y_total, batch_size, shuffle=True)
poison_trainsize= x_total.shape[0]
print (' : Convert the label-flipped dataset into tf datasets')
# --------------------------------------------------------------------------
# Load the new model
# --------------------------------------------------------------------------
del baseline_model
poison_model = models.load_model(args.dataset, args.datapth, args.network)
poison_lrate = learn_rate
if not args.privacy:
poison_optim = optims.define_optimizer(args.network, poison_lrate)
print (' : Model will be trained with a vanilla optimizer')
else:
poison_optim = optims.define_dpoptimizer( \
args.network, poison_lrate, \
batch_size, args.nclip, args.noise)
print (' : Model will be trained with a DP optimizer [{}, {}]'.format(args.nclip, args.noise))
# --------------------------------------------------------------------------
# Run in the normal mode (only for the attacks)
# --------------------------------------------------------------------------
# best accuracy holder
best_acc = 0.0
# attack result holder
attack_results = []
# do training
steps_per_epoch = poison_trainsize // batch_size
for epoch in range(1, epochs+1):
# : train the model for an epoch
for mbatch, (data, labels) in enumerate(poison_trainset.take(-1)):
# :: records the updates from the poison/clean instances
with tf.GradientTape(persistent=True) as gradient_tape:
# dummy calls
logits, _ = poison_model(data, training=True)
var_list = poison_model.trainable_variables
# ::: record the gradients
if not args.privacy:
def loss_fn():
logits, penultimate = poison_model(data, training=True)
vector_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)
scalar_loss = tf.reduce_mean(vector_loss)
return scalar_loss
grads_and_vars = poison_optim.compute_gradients(loss_fn, var_list)
else:
def loss_fn():
logits, penultimate = poison_model(data, training=True)
vector_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)
return vector_loss
grads_and_vars = poison_optim.compute_gradients( \
loss_fn, var_list, gradient_tape=gradient_tape)
# :: update the model parameters (when there are poisons)
poison_optim.apply_gradients(grads_and_vars)
# : end for mbatch ...
# : compute the validation accuracy and the accuracy over our target
current_acc = _validate(poison_model, clean_validset)
current_ctrain_acc = _validate(poison_model, ctrain_examine)
current_ptrain_acc = _validate(poison_model, ptrain_examine)
# : record the best accuracy
if best_acc < current_acc:
best_acc = current_acc
# : report the current state (cannot compute the total eps, as we split the ....)
print (' : Epoch {} - acc {:.4f} (base) / {:.4f} (curr) / {:.4f} (best)'.format( \
epoch, baseline_acc, current_acc, best_acc))
# : store the current results
attack_results.append([ \
epoch, x_poison.shape[0], \
baseline_acc, current_acc, \
current_ctrain_acc, current_ptrain_acc])
# : [Optimization] when the accuracy over the baseline, stop
if best_acc >= baseline_acc:
print (' Best >= Baseline, stop'); break
# : flush the stdouts
sys.stdout.flush()
# end for epoch...
# report the attack results...
print (' : [Result] epoch {}, poison {}, base {:.4f}, curr-best {:.4f}'.format( \
epoch, x_poison.shape[0], baseline_acc, best_acc))
# store the attack results
attack_results.append([epoch, x_poison.shape[0], baseline_acc, best_acc])
io.store_to_csv(results_data, attack_results)
# done.