|
| 1 | +import torch |
| 2 | +from torch import autograd |
| 3 | +import torch.nn as nn |
| 4 | +import torch.optim as optim |
| 5 | +import torchvision.transforms as transforms |
| 6 | +import torchvision.datasets as datasets |
| 7 | +import torch.nn.functional as F |
| 8 | +import numpy as np |
| 9 | +TARGET_MULT = 10000.0 |
| 10 | + |
| 11 | +USE_CUDA = torch.cuda.is_available() |
| 12 | +Variable = lambda *args, **kwargs: autograd.Variable(*args, **kwargs).cuda() if USE_CUDA else autograd.Variable(*args, **kwargs) |
| 13 | + |
| 14 | +CARTPOLE_STD=[0.7322321, 1.0629482, 0.12236707, 0.43851405] |
| 15 | +ACROBOT_STD=[0.36641926, 0.65119815, 0.6835106, 0.67652863, 2.0165246, 3.0202584] |
| 16 | + |
| 17 | + |
| 18 | +def pgd(model, X, y, verbose=False, params={}, env_id="", norm_type='l_2'): |
| 19 | + X /= 255 |
| 20 | + epsilon = params.get('epsilon', 0.00392) |
| 21 | + niters = params.get('niters', 10) |
| 22 | + img_min = params.get('img_min', 0.0) |
| 23 | + img_max = params.get('img_max', 1.0) |
| 24 | + network_type = params.get('network_type', 'nature') |
| 25 | + loss_func = params.get('loss_func', nn.CrossEntropyLoss()) |
| 26 | + step_size = epsilon * 1.0 / niters |
| 27 | + y = Variable(torch.tensor(y)) |
| 28 | + if verbose: |
| 29 | + print('epislon: {}, step size: {}, target label: {}'.format(epsilon, step_size, y)) |
| 30 | + |
| 31 | + X_adv = Variable(X.data, requires_grad=True) |
| 32 | + |
| 33 | + for i in range(niters): |
| 34 | + |
| 35 | + if network_type == 'noisynet': |
| 36 | + model.model.sample() |
| 37 | + |
| 38 | + _, logits = model.forward_requires_grad(X_adv, return_q=True) |
| 39 | + |
| 40 | + loss = loss_func(logits, y) |
| 41 | + if verbose: |
| 42 | + print('current loss: ', loss.data.cpu().numpy()) |
| 43 | + model.zero_grad() |
| 44 | + loss.backward() |
| 45 | + |
| 46 | + if norm_type == 'l_inf': |
| 47 | + eta = step_size * X_adv.grad.data.sign() |
| 48 | + elif norm_type == 'l_2': |
| 49 | + if not torch.norm(X_adv.grad).item(): |
| 50 | + eta = step_size * X_adv.grad.data |
| 51 | + else: |
| 52 | + eta = step_size * X_adv.grad.data / torch.norm(X_adv.grad).data |
| 53 | + |
| 54 | + X_adv = Variable(X_adv.data + eta, requires_grad=True) |
| 55 | + # adjust to be within [-epsilon, epsilon] |
| 56 | + |
| 57 | + if norm_type == 'l_inf': |
| 58 | + eta = torch.clamp(X_adv.data - X.data, -epsilon, epsilon) |
| 59 | + |
| 60 | + elif norm_type == 'l_2': |
| 61 | + eta = X_adv.data - X.data |
| 62 | + # print('iter', i, 'second eta', torch.norm(eta)) |
| 63 | + if torch.norm(eta) > epsilon: |
| 64 | + eta *= epsilon / torch.norm(eta) |
| 65 | + |
| 66 | + X_adv.data = X.data + eta |
| 67 | + if verbose: |
| 68 | + print('max eta: ', np.max(abs(eta.data.cpu().numpy()))) |
| 69 | + print('linf diff before clamp: ', np.max(abs(X_adv.data.cpu().numpy()-X.data.cpu().numpy()))) |
| 70 | + |
| 71 | + X_adv.data = torch.clamp(X_adv.data, img_min, img_max) |
| 72 | + if verbose: |
| 73 | + print('linf diff after clamp: ',np.max(abs(X_adv.data.cpu().numpy()-X.data.cpu().numpy()))) |
| 74 | + |
| 75 | + if verbose: |
| 76 | + print('{} iterations'.format(i+1)) |
| 77 | + |
| 78 | + return torch.clamp((X_adv.data * 255).long(), 0, 255) |
| 79 | + |
| 80 | + |
| 81 | +def fgsm(model, X, y, verbose=False, params={}): |
| 82 | + epsilon=params.get('epsilon', 1) |
| 83 | + img_min=params.get('img_min', 0.0) |
| 84 | + img_max=params.get('img_max', 1.0) |
| 85 | + X_adv = Variable(X.data, requires_grad=True) |
| 86 | + logits = model.forward(X_adv) |
| 87 | + loss = F.nll_loss(logits, y) |
| 88 | + model.features.zero_grad() |
| 89 | + loss.backward() |
| 90 | + eta = epsilon*X_adv.grad.data.sign() |
| 91 | + X_adv = Variable(X_adv.data + eta, requires_grad=True) |
| 92 | + X_adv.data = torch.clamp(X_adv.data, img_min, img_max) |
| 93 | + return X_adv.data |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +def rand_attack(model, X, y, verbose=False, params={}, env_id=""): |
| 98 | + epsilon = params.get('epsilon', 0.00392) |
| 99 | + if env_id == "CartPole-v0": |
| 100 | + epsilon = torch.from_numpy(CARTPOLE_STD) * epsilon |
| 101 | + if env_id == "Acrobot-v1": |
| 102 | + epsilon = torch.from_numpy(ACROBOT_STD) * epsilon |
| 103 | + img_min = params.get('img_min', 0.0) |
| 104 | + img_max = params.get('img_max', 1.0) |
| 105 | + noise = 2 * epsilon * torch.rand(X.data.size()) - epsilon |
| 106 | + if USE_CUDA: |
| 107 | + noise = noise.cuda() |
| 108 | + X_adv = torch.clamp(X.data + noise, img_min, img_max) |
| 109 | + X_adv = Variable(X_adv.data, requires_grad=True) |
| 110 | + return X_adv.data |
| 111 | + |
| 112 | + |
| 113 | +def attack(model, X, attack_config, loss_func=nn.CrossEntropyLoss(), epsilon=0.00392, smooth_type='', network_type='nature'): |
| 114 | + # method = attack_config.get('method', 'pgd') |
| 115 | + # verbose = attack_config.get('verbose', False) |
| 116 | + # params = attack_config.get('params', {}) |
| 117 | + method = 'pgd' |
| 118 | + verbose = False |
| 119 | + params = { |
| 120 | + 'epsilon': epsilon, |
| 121 | + 'network_type': network_type, |
| 122 | + } |
| 123 | + params['loss_func'] = loss_func |
| 124 | + |
| 125 | + if network_type == 'noisynet': |
| 126 | + model.model.sample() |
| 127 | + |
| 128 | + if smooth_type == 'local': |
| 129 | + _, output = model.forward(X, cert=False, return_q=True) |
| 130 | + elif smooth_type == 'global': |
| 131 | + _, output = model.forward(X, return_q=True) |
| 132 | + else: |
| 133 | + raise NotImplementedError(f'smooth_type = {smooth_type} not implemented!') |
| 134 | + |
| 135 | + y = torch.argmax(output, dim=1) |
| 136 | + # y = model.act(X, cert=False) |
| 137 | + if method == 'cw': |
| 138 | + atk = cw |
| 139 | + elif method == 'rand': |
| 140 | + atk = rand_attack |
| 141 | + elif method == 'fgsm': |
| 142 | + atk = fgsm |
| 143 | + else: |
| 144 | + atk = pgd |
| 145 | + adv_X = atk(model, X, y, verbose=verbose, params=params) |
| 146 | + abs_diff = abs(adv_X.cpu().numpy()-X.cpu().numpy()) |
| 147 | + if verbose: |
| 148 | + print('adv image range: {}-{}, ori action: {}, adv action: {}, l1 norm: {}, l2 norm: {}, linf norm: {}'.format(torch.min(adv_X).cpu().numpy(), torch.max(adv_X).cpu().numpy(), model.act(X)[0], model.act(adv_X)[0], np.sum(abs_diff), np.linalg.norm(abs_diff), np.max(abs_diff))) |
| 149 | + return adv_X |
| 150 | + |
0 commit comments