def __call__(self, inputs, labels, *, binary_search_steps=15): x = ep.astensor(inputs) y = ep.astensor(labels) assert x.shape[0] == y.shape[0] assert y.ndim == 1 min_, max_ = self.model.bounds() target = (max_ + min_) / 2 v = target - x N = len(x) x0 = x npdtype = x.numpy().dtype lower_bound = np.zeros((N,), dtype=npdtype) upper_bound = np.ones((N,), dtype=npdtype) epsilons = lower_bound for _ in range(binary_search_steps): x = x0 + atleast_kd(ep.from_numpy(x0, epsilons), x0.ndim) * v logits = ep.astensor(self.model.forward(x.tensor)) classes = logits.argmax(axis=-1) is_adv = (classes != labels).numpy() lower_bound = np.where(is_adv, lower_bound, epsilons) upper_bound = np.where(is_adv, epsilons, upper_bound) epsilons = (lower_bound + upper_bound) / 2 epsilons = upper_bound epsilons = ep.from_numpy(x0, epsilons) x = x0 + atleast_kd(epsilons, x0.ndim) * v return x.tensor
def main() -> None: model = models.resnet101(pretrained=True).eval() preprocessing = dict(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], axis=-3) fmodel = PyTorchModel(model, bounds=(0, 1), preprocessing=preprocessing) images, labels = fb.utils.samples(fmodel, dataset='imagenet', batchsize=16) clean_acc = fb.utils.accuracy(fmodel, images, labels) print(f"clean accuracy: {clean_acc * 100:.1f} %") attack = fb.attacks.LinfDeepFoolAttack() epsilons = np.linspace(0.0, 0.005, num=20) images = ep.astensor(images) labels = ep.astensor(labels) criterion = fb.criteria.Misclassification(labels) raw, clipped, is_adv = attack(fmodel, images, labels, epsilons=epsilons) robust_accuracy = 1 - is_adv.float32().mean(axis=-1) print("robust accuracy for perturbations with") for eps, acc in zip(epsilons, robust_accuracy): print(f" Linf norm ≤ {eps:<6}: {acc.item() * 100:4.1f} %") plt.plot(epsilons, robust_accuracy.numpy())
def pytorch_mnist(request: Any) -> ModelAndData: fmodel = fbn.zoo.ModelLoader.get().load("examples/zoo/mnist/", module_name="foolbox_model") x, y = fbn.samples(fmodel, dataset="mnist", batchsize=16) x = ep.astensor(x) y = ep.astensor(y) return fmodel, x, y
def __call__( self, inputs, labels, *, rescale=False, epsilon=2.0, step_size=0.4, num_steps=10, ): def loss_fn(inputs: ep.Tensor, labels: ep.Tensor) -> ep.Tensor: logits = ep.astensor(self.model.forward(inputs.tensor)) return ep.crossentropy(logits, labels).sum() if rescale: min_, max_ = self.model.bounds() scale = (max_ - min_) * np.sqrt(np.prod(inputs.shape[1:])) epsilon = epsilon * scale step_size = step_size * scale x = ep.astensor(inputs) y = ep.astensor(labels) assert x.shape[0] == y.shape[0] assert y.ndim == 1 x0 = x for _ in range(num_steps): _, gradients = ep.value_and_grad(loss_fn, x, y) gradients = normalize_l2_norms(gradients) x = x + step_size * gradients x = x0 + clip_l2_norms(x - x0, epsilon) x = ep.clip(x, *self.model.bounds()) return x.tensor
def __call__(self, inputs, labels, *, steps=1000): x = ep.astensor(inputs) y = ep.astensor(labels) assert x.shape[0] == y.shape[0] assert y.ndim == 1 assert x.ndim == 4 if self.channel_axis == 1: h, w = x.shape[2:4] elif self.channel_axis == 3: h, w = x.shape[1:3] else: raise ValueError( "expected 'channel_axis' to be 1 or 3, got {channel_axis}") size = max(h, w) min_, max_ = self.model.bounds() x0 = x x0np = x0.numpy() epsilons = np.linspace(0, 1, num=steps + 1)[1:] logits = ep.astensor(self.model.forward(x0.tensor)) classes = logits.argmax(axis=-1) is_adv = classes != labels found = is_adv result = x0 for epsilon in epsilons: # TODO: reduce the batch size to the ones that haven't been sucessful sigmas = [epsilon * size] * 4 sigmas[0] = 0 sigmas[self.channel_axis] = 0 # TODO: once we can implement gaussian_filter in eagerpy, avoid converting from numpy x = gaussian_filter(x0np, sigmas) x = np.clip(x, min_, max_) x = ep.from_numpy(x0, x) logits = ep.astensor(self.model.forward(x.tensor)) classes = logits.argmax(axis=-1) is_adv = classes != labels new_adv = ep.logical_and(is_adv, found.logical_not()) result = ep.where(atleast_kd(new_adv, x.ndim), x, result) found = ep.logical_or(new_adv, found) if found.all(): break return result.tensor
def test_tensorflow_l2_carlini_wagner_attack(fmodel_and_data): fmodel, x, y, batch_size, num_classes = fmodel_and_data y = ep.astensor(fmodel.forward(x)).argmax(axis=-1) attack = L2CarliniWagnerAttack(fmodel) advs = attack(x, y, max_iterations=100) y_advs = ep.astensor(fmodel.forward(advs)).argmax(axis=-1) assert x.shape == advs.shape assert (y_advs == y).float32().mean() < 1
def test_tensorflow_linf_basic_iterative_attack(fmodel_and_data): fmodel, x, y, batch_size, num_classes = fmodel_and_data y = ep.astensor(fmodel.forward(x)).argmax(axis=-1) attack = LinfinityBasicIterativeAttack(fmodel) advs = attack(x, y, rescale=False, epsilon=0.3) perturbation = ep.astensor(advs - x) y_advs = ep.astensor(fmodel.forward(advs)).argmax(axis=-1) assert x.shape == advs.shape assert perturbation.abs().max() <= 0.3 + 1e-7 assert (y_advs == y).float32().mean() < 1
def download_dataset(base_path, examples, data_format, bounds, dimension): images_path = base_path + os.sep + "subset" labels_path = base_path + os.sep + "labels.txt" images, labels = [], [] files = os.listdir(images_path) files.sort() labels_numbers = [] for idx in range(0, examples): # okresl nazwe pliku file = files[idx] labels_numbers.append(int(re.match(r".+val_0+(.+)\.", file).group(1))) # otworz plik path = os.path.join(images_path, file) image = Image.open(path) image = image.resize(dimension) image = np.asarray(image, dtype=np.float32) # jesli obraz jest zapisany w skali szarosci to dodawana jest trzecia os if image.ndim == 2: image = np.repeat(image[:, :, np.newaxis], 3, axis=2) # ewentualne przestawienie kanalow if data_format == "channels_first": image = np.transpose(image, (2, 0, 1)) # dodanie obrazu do listy obrazow images.append(image) # pobranie etykiet with open(labels_path, "r") as csv_file: reader = csv.reader(csv_file) for i, line in enumerate(reader, 1): if len(labels) == len(files): break if i in labels_numbers: labels.append(int(line[0])) # konwersja list do tablic numpy images = np.stack(images) labels = np.array(labels) # wartosci pikseli zdjec domyslnie zawieraja sie w przedziale od 0 do 255 # jesli ograniczenie wartosci pikseli dla modelu jest inny to nalezy skonwertowac zdjecia if bounds != (0, 255): images = images / 255 * (bounds[1] - bounds[0]) + bounds[0] images = ep.from_numpy(ep.torch.zeros(0, device="cpu"), images).raw labels = ep.from_numpy(ep.torch.zeros(0, device="cpu"), labels).raw return ep.astensor(images), ep.astensor(labels).astype(torch.long)
def pytorch_resnet18(request: Any) -> ModelAndData: if request.config.option.skipslow: pytest.skip() import torchvision.models as models model = models.resnet18(pretrained=True).eval() preprocessing = dict(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], axis=-3) fmodel = fbn.PyTorchModel(model, bounds=(0, 1), preprocessing=preprocessing) x, y = fbn.samples(fmodel, dataset="imagenet", batchsize=16) x = ep.astensor(x) y = ep.astensor(y) return fmodel, x, y
def tensorflow_mobilenetv2(request: Any) -> ModelAndData: if request.config.option.skipslow: pytest.skip() import tensorflow as tf model = tf.keras.applications.MobileNetV2(weights="imagenet") fmodel = fbn.TensorFlowModel( model, bounds=(0, 255), preprocessing=dict(mean=127.5, std=127.5) ) x, y = fbn.samples(fmodel, dataset="imagenet", batchsize=16) x = ep.astensor(x) y = ep.astensor(y) return fmodel, x, y
def test_gaussian_blur_attack(): channels = 3 batch_size = 8 h = w = 32 bounds = (0, 1) class Model(nn.Module): def forward(self, x): # instead of our usual model that's robust to the BlurAttack, # we use a slighlty different model that can be attacked x = x[:, :, 1:, :] - x[:, :, :-1, :] x = x[:, :, :, 1:] - x[:, :, :, :-1] x = torch.mean(x, 3) x = torch.mean(x, 2) return x model = Model().eval() fmodel = PyTorchModel(model, bounds=bounds) np.random.seed(0) x = np.random.uniform(*bounds, size=(batch_size, channels, h, w)).astype(np.float32) x = torch.from_numpy(x).to(fmodel.device) y = fmodel.forward(x).argmax(axis=-1) attack = GaussianBlurAttack(fmodel, channel_axis=1) advs = attack(x, y) perturbations = ep.astensor(advs - x) norms = flatten(perturbations).square().sum(axis=-1).sqrt() y_advs = fmodel.forward(advs).argmax(axis=-1) assert x.shape == advs.shape assert norms.max().item() <= 20.0 + 1e-7 assert (y_advs == y).float().mean() < 1
def loss_fun(x: ep.Tensor, k: int) -> ep.Tensor: logits = ep.astensor(self.model.forward(x.tensor)) ik = classes[:, k] l0 = -ep.crossentropy(logits, i0) lk = -ep.crossentropy(logits, ik) loss = lk - l0 return loss.sum(), (loss, logits)
def loss_fun(x: ep.Tensor, k: int) -> ep.Tensor: logits = ep.astensor(self.model.forward(x.tensor)) ik = classes[:, k] l0 = logits[rows, i0] lk = logits[rows, ik] loss = lk - l0 return loss.sum(), (loss, logits)
def test_rescale_axis(request: Any, dummy: ep.Tensor) -> None: backend = request.config.option.backend if backend == "numpy": pytest.skip() x_np = np.random.uniform(0.0, 1.0, size=(16, 3, 64, 64)) x_np_ep = ep.astensor(x_np) x_up_np_ep = rescale_images(x_np_ep, (16, 3, 128, 128), 1) x_up_np = x_up_np_ep.numpy() x = ep.from_numpy(dummy, x_np) x_ep = ep.astensor(x) x_up_ep = rescale_images(x_ep, (16, 3, 128, 128), 1) x_up = x_up_ep.numpy() assert np.allclose(x_up_np, x_up, atol=1e-5)
def __call__(self, inputs, labels): inputs = ep.astensor(inputs) labels = ep.astensor(labels) x = ep.zeros_like(inputs) adv_samples = [] for k in range(len(labels)): while True: idx = np.random.randint(len(self.labels)) if int(labels[k].numpy()) != self.labels[idx]: adv_samples.append(ep.astensor(self.samples[idx]).numpy()) break x = ep.from_numpy(x, np.stack(adv_samples)) return x.tensor
def draw_line(x1, y1, x2, y2, radius, color, t, blend=0.75): ''' Draws a line onto an image tensor. All units are in pixels Parameters: x1: x position for endpoint 1 y1: y position for endpoint 1 x2: x position for endpoint 2 y2: y position for endpoint 2 radius: line width (radius) color: rgb color tensor with shape (3,) and values in the range 0.0-1.0 blend (optional): blending distance Returns: tensor with circle drawn onto it ''' if type(t) == torch.Tensor: t = t.permute(1, 2, 0) t = ep.astensor(t) uvx, uvy = make_uv(t) pax, pay, bax, bay = uvx - x1, uvy - y1, x2 - x1, y2 - y1 h = ep.clip((pax * bax + pay * bay) / (bax * bax + bay * bay), 0.0, 1.0) dlx, dly = pax - bax * h, pay - bay * h dist = (dlx * dlx + dly * dly).sqrt() - radius t = dist_to_col(dist, color, blend, t) t = t.raw if type(t) == torch.Tensor: t = t.permute(2, 0, 1) return t
def test_deepfool(Attack, loss): channels = 3 batch_size = 8 h = w = 32 bounds = (0, 1) class Model(nn.Module): def forward(self, x): x = torch.mean(x, 3) x = torch.mean(x, 2) return x model = Model().eval() fmodel = PyTorchModel(model, bounds=bounds) np.random.seed(0) x = np.random.uniform(*bounds, size=(batch_size, channels, h, w)).astype(np.float32) x = torch.from_numpy(x).to(fmodel.device) y = fmodel.forward(x).argmax(axis=-1) attack = Attack(fmodel) advs = attack(x, y, loss=loss) perturbations = ep.astensor(advs - x) norms = flatten(perturbations).square().sum(axis=-1).sqrt() y_advs = fmodel.forward(advs).argmax(axis=-1) assert x.shape == advs.shape assert norms.max().item() <= 40.0 + 1e-7 assert (y_advs == y).float().mean() < 1
def run( self, model: Model, inputs: T, criterion: Union[Criterion, T], *, early_stop: Optional[float] = None, starting_points: Optional[ep.Tensor] = None, **kwargs: Any, ) -> T: originals, restore_type = ep.astensor_(inputs) self._nqueries = {i: 0 for i in range(len(originals))} self._set_cos_sin_function(originals) self.theta_max = ep.ones(originals, len(originals)) * self._theta_max criterion = get_criterion(criterion) self._criterion_is_adversarial = get_is_adversarial(criterion, model) # Get Starting Point if starting_points is not None: best_advs = starting_points elif starting_points is None: init_attack: MinimizationAttack = LinearSearchBlendedUniformNoiseAttack(steps=50) best_advs = init_attack.run(model, originals, criterion, early_stop=early_stop) else: raise ValueError("starting_points {} doesn't exist.".format(starting_points)) assert self._is_adversarial(best_advs).all() # Initialize the direction orthogonalized with the first direction fd = best_advs - originals norm = ep.norms.l2(fd.flatten(1), axis=1) fd = fd / atleast_kd(norm, fd.ndim) self._directions_ortho = {i: v.expand_dims(0) for i, v in enumerate(fd)} # Load Basis if "basis_params" in kwargs: self._basis = Basis(originals, **kwargs["basis_params"]) else: self._basis = Basis(originals) for _ in range(self._steps): # Get candidates. Shape: (n_candidates, batch_size, image_size) candidates = self._get_candidates(originals, best_advs) candidates = candidates.transpose((1, 0, 2, 3, 4)) best_candidates = ep.zeros_like(best_advs).raw for i, o in enumerate(originals): o_repeated = ep.concatenate([o.expand_dims(0)] * len(candidates[i]), axis=0) index = ep.argmax(self.distance(o_repeated, candidates[i])).raw best_candidates[i] = candidates[i][index].raw is_success = self.distance(best_candidates, originals) < self.distance(best_advs, originals) best_advs = ep.where(atleast_kd(is_success, best_candidates.ndim), ep.astensor(best_candidates), best_advs) if all(v > self._max_queries for v in self._nqueries.values()): print("Max queries attained for all the images.") break return restore_type(best_advs)
def dist_to_col(dist, color, blend, t): msk = ep.clip((dist + blend) / (2.0 * blend), 0.0, 1.0) msk = msk * msk * (3.0 - 2.0 * msk) msk = msk.expand_dims(axis=2).tile([1, 1, 3]) col_t = ep.astensor(color).expand_dims(axis=0).expand_dims(axis=0).tile( [t.shape[0], t.shape[1], 1]) return msk * t + (1.0 - msk) * col_t
def test_linf_fast_gradient_attack(): channels = 3 batch_size = 8 h = w = 32 bounds = (0, 1) class Model(nn.Module): def forward(self, x): x = torch.mean(x, 3) x = torch.mean(x, 2) return x model = Model().eval() fmodel = PyTorchModel(model, bounds=bounds) np.random.seed(0) x = np.random.uniform(*bounds, size=(batch_size, channels, h, w)).astype(np.float32) x = torch.from_numpy(x).to(fmodel.device) y = fmodel.forward(x).argmax(axis=-1) attack = LinfinityFastGradientAttack(fmodel) advs = attack(x, y, rescale=False, epsilon=0.3) perturbations = ep.astensor(advs - x) y_advs = fmodel.forward(advs).argmax(axis=-1) assert x.shape == advs.shape assert perturbations.abs().max() <= 0.3 + 1e-7 assert (y_advs == y).float().mean() < 1
def get_zig_zag_mask(self, frequence_range: Tuple[float, float], mask_shape: Tuple[int, int] = (8, 8)) -> Any: total_component = mask_shape[0] * mask_shape[1] n_coeff_kept = int(total_component * min(1, frequence_range[1])) n_coeff_to_start = int(total_component * max(0, frequence_range[0])) imsize = self._originals.shape mask_shape = (imsize[0], imsize[1], mask_shape[0], mask_shape[1]) mask = ep.zeros(self._originals, mask_shape).raw s = 0 while n_coeff_kept > 0: for i in range(min(s + 1, mask_shape[2])): for j in range(min(s + 1, mask_shape[3])): if i + j == s: if n_coeff_to_start > 0: n_coeff_to_start -= 1 continue if s % 2: mask[:, :, i, j] = 1 else: mask[:, :, j, i] = 1 n_coeff_kept -= 1 if n_coeff_kept == 0: return mask s += 1 return ep.astensor(mask)
def idct2_8_8(self, dct: ep.astensor) -> ep.astensor: im_dct = ep.zeros_like(dct).raw dct = dct.raw for i in np.r_[:dct.shape[2]:8]: for j in np.r_[:dct.shape[3]:8]: im_dct[:, :, i:(i+8),j:(j+8)] = self._f_idct2(dct[:, :, i:(i+8),j:(j+8)]) return ep.astensor(im_dct)
def _add_step_in_circular_direction(degree: ep.Tensor) -> ep.Tensor: degree = atleast_kd(degree, direction1.ndim).raw * np.pi / 180 results = self._cos(degree) * direction1 + self._sin(degree) * direction2 results = (originals + ep.astensor(results * distances * self._cos(degree))).clip(0, 1) if self.with_quantification: results = self._quantify(results) return results
def test_l1_brendel_bethge_attack(): channels = 3 batch_size = 8 h = w = 32 bounds = (0, 1) class Model(nn.Module): def forward(self, x): x = torch.mean(x, 3) x = torch.mean(x, 2) return x model = Model().eval() fmodel = PyTorchModel(model, bounds=bounds) np.random.seed(0) x = np.random.uniform(*bounds, size=(batch_size, channels, h, w)).astype(np.float32) x = torch.from_numpy(x).to(fmodel.device) y = fmodel.forward(x).argmax(axis=-1) attack = L1BrendelBethgeAttack(fmodel) advs = attack(x, y, steps=100, lr_num_decay=10) perturbations = ep.astensor(advs - x) norms = flatten(perturbations).abs().sum(axis=-1) y_advs = fmodel.forward(advs).argmax(axis=-1) assert x.shape == advs.shape assert norms.max().item() <= 32 * 32 * 3 / 2 assert (y_advs == y).float().mean() < 1e-5
def __call__(self, inputs, labels, *, directions=1000, steps=1000): x = ep.astensor(inputs) min_, max_ = self.model.bounds() N = len(x) assert directions >= 1 for j in range(directions): # random noise inputs tend to be classified into the same class, # so we might need to make very many draws if the original class # is that one random_ = ep.uniform(x, x.shape, min_, max_) logits_ = self.model.forward(random_) classes_ = logits_.argmax(axis=-1) is_adv_ = atleast_kd(classes_ != labels, x.ndim) if j == 0: random = random_ is_adv = is_adv_ else: cond1 = is_adv.astype(x.dtype) cond2 = is_adv_.astype(x.dtype) random = cond1 * random + (1 - cond1) * cond2 * random_ is_adv = is_adv.logical_or(is_adv_) if is_adv.all(): break if not is_adv.all(): warnings.warn( f"{self.__class__.__name__} failed to draw sufficent random" " inputs that are adversarial ({is_adv.sum()} / {N}).") x0 = x npdtype = x.numpy().dtype epsilons = np.linspace(0, 1, num=steps + 1, dtype=npdtype) best = np.ones((N, ), dtype=npdtype) for epsilon in epsilons: x = (1 - epsilon) * x0 + epsilon * random # TODO: due to limited floating point precision, clipping can be required logits = self.model.forward(x) classes = logits.argmax(axis=-1) is_adv = (classes != labels).numpy() best = np.minimum( np.logical_not(is_adv).astype(npdtype) + is_adv.astype(npdtype) * epsilon, best, ) if (best < 1).all(): break best = ep.from_numpy(x0, best) best = atleast_kd(best, x0.ndim) x = (1 - best) * x0 + best * random return x.tensor
def tensorflow_resnet50(request: Any) -> ModelAndData: if request.config.option.skipslow: pytest.skip() import tensorflow as tf if not tf.test.is_gpu_available(): pytest.skip("ResNet50 test too slow without GPU") model = tf.keras.applications.ResNet50(weights="imagenet") preprocessing = dict(flip_axis=-1, mean=[104.0, 116.0, 123.0]) # RGB to BGR fmodel = fbn.TensorFlowModel(model, bounds=(0, 255), preprocessing=preprocessing) x, y = fbn.samples(fmodel, dataset="imagenet", batchsize=16) x = ep.astensor(x) y = ep.astensor(y) return fmodel, x, y
def test_pytorch_numpy_compatibility() -> None: import numpy as np import torch x_np = np.random.uniform(0.0, 1.0, size=(16, 3, 64, 64)) x_torch = torch.from_numpy(x_np) x_np_ep = ep.astensor(x_np) x_torch_ep = ep.astensor(x_torch) x_up_np_ep = rescale_images(x_np_ep, (16, 3, 128, 128), 1) x_up_torch_ep = rescale_images(x_torch_ep, (16, 3, 128, 128), 1) x_up_np = x_up_np_ep.numpy() x_up_torch = x_up_torch_ep.numpy() assert np.allclose(x_up_np, x_up_torch)
def test_jax_numpy_compatibility() -> None: import numpy as np import jax.numpy as jnp x_np = np.random.uniform(0.0, 1.0, size=(16, 3, 64, 64)) x_jax = jnp.array(x_np) x_np_ep = ep.astensor(x_np) x_jax_ep = ep.astensor(x_jax) x_up_np_ep = rescale_images(x_np_ep, (16, 3, 128, 128), 1) x_up_jax_ep = rescale_images(x_jax_ep, (16, 3, 128, 128), 1) x_up_np = x_up_np_ep.numpy() x_up_jax = x_up_jax_ep.numpy() assert np.allclose(x_up_np, x_up_jax)
def get_match_target(targets, labels): import torch result = [] if isinstance(labels, ep.Tensor): labels = labels.raw for label in labels: result.append(targets[label, :]) return ep.astensor(torch.tensor(result).to("cuda"))
def images( images, *, n=None, data_format=None, bounds=(0, 1), ncols=None, nrows=None, figsize=None, scale=1, **kwargs, ): x = ep.astensor(images) assert x.ndim == 4 if n is not None: x = x[:n] if data_format is None: channels_first = x.shape[1] == 1 or x.shape[1] == 3 channels_last = x.shape[-1] == 1 or x.shape[-1] == 3 if channels_first == channels_last: raise ValueError("data_format ambigous, please specify explicitly") else: channels_first = data_format == "channels_first" channels_last = data_format == "channels_last" assert channels_first != channels_last x = x.numpy() if channels_first: x = np.transpose(x, axes=(0, 2, 3, 1)) min_, max_ = bounds x = (x - min_) / (max_ - min_) if nrows is None and ncols is None: nrows = 1 if ncols is None: ncols = (len(x) + nrows - 1) // nrows elif nrows is None: nrows = (len(x) + ncols - 1) // ncols if figsize is None: figsize = (ncols * scale, nrows * scale) fig, axes = plt.subplots( ncols=ncols, nrows=nrows, figsize=figsize, squeeze=False, constrained_layout=True, **kwargs, ) for row in range(nrows): for col in range(ncols): ax = axes[row][col] ax.set_xticks([]) ax.set_yticks([]) ax.axis("off") i = row * ncols + col if i < len(x): ax.imshow(x[i])