bat.attacks.simba

This module implements the black-box attack SimBA.

View Source
"""
This module implements the black-box attack `SimBA`.
"""

import numpy as np
import concurrent.futures
import bat.utils as utils
from tqdm import tqdm

class SimBA():
    """
    Implementation of the `SimBA` attack. Paper link: https://arxiv.org/abs/1905.07121
    """

    def __init__(self,  classifier):
        """
        Create a class: `SimBA` instance.
        - classifier: model to attack
        """
        self.classifier = classifier
    
    def init(self, x):
        """
        Initialize the attack.
        """
        y_pred = self.classifier.predict(np.array([x]))[0]

        x_adv = x
        y_list = []
        y_list.append(y_pred[np.argmax(y_pred)])

        perm = np.random.permutation(x.reshape(-1).shape[0])

        return x_adv, y_pred, y_list, perm

    def step(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1):
        """
        Single step for non-distributed attack.
        """
        y_adv = None
        x_adv_new = x_adv

        diff = np.zeros(x_adv.reshape(-1).shape[0])
        diff[perm[index]] = epsilon * 2
        diff = diff.reshape(x_adv.shape)

        plus = self.classifier.predict(np.array([np.clip(x_adv + diff, 0, 1)]))[0]
        if plus[np.argmax(y_pred)] < y_list[-1]:
            x_adv_new = np.clip(x_adv + diff, 0, 1)
            y_adv = plus
        else:
            minus = self.classifier.predict(np.array([np.clip(x_adv - diff, 0, 1)]))[0]
            if minus[np.argmax(y_pred)] < y_list[-1]:
                x_adv_new = np.clip(x_adv - diff, 0, 1)
                y_adv = minus
        
        if y_adv is not None:
            y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv_new, y_adv, y_list

    def batch(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1, max_workers=10, batch=50):
        """
        Single step for distributed attack.
        """
        noise = np.zeros(x_adv.shape)

        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            future_to_url = {executor.submit(self.step, x_adv, y_pred, y_list, perm, index+j, epsilon): j for j in range(0, batch)}
            for future in concurrent.futures.as_completed(future_to_url):
                j = future_to_url[future]
                try:
                    x_adv_new, _, _ = future.result()
                    noise = noise + (x_adv_new - x_adv)
                except Exception as exc:
                    print('Task %r generated an exception: %s' % (j, exc))
                else:
                    pass

        if(np.sum(noise) != 0):
            noise = utils.proj_lp(noise, xi = 1)

        x_adv = np.clip(x_adv + noise, 0, 1)

        y_adv = self.classifier.predict(np.array([x_adv]))[0] 
        y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv, y_adv, y_list

    def attack(self, x, epsilon=0.1, max_it=1000, distributed=False, batch=50, max_workers=10):
        """
        Initiate the attack.

        - x: input data
        - epsilon: perturbation on each pixel
        - max_it: number of iterations
        - distributed: if True, use distributed attack
        - batch: number of queries per worker
        - max_workers: number of workers
        """

        x_adv, y_pred, y_list, perm = self.init(x)

        if distributed:
            pbar = tqdm(range(0, max_it, batch), desc="Distributed SimBA")
        else:
            pbar = tqdm(range(0, max_it), desc="SimBA")

        for i in pbar:

            if distributed:
                x_adv, y_adv, y_list = self.batch(x_adv, y_pred, y_list, perm, i, epsilon, max_workers, batch)
            else:
                x_adv, y_adv, y_list = self.step(x_adv, y_pred, y_list, perm, i, epsilon)

            pbar.set_postfix({'origin prob': y_list[-1], 'l2 norm': np.sqrt(np.power(x_adv - x, 2).sum())})

            # Early break
            if y_adv is not None:
                if(np.argmax(y_adv) != np.argmax(y_pred)):
                    break
     
        return x_adv
#   class SimBA:
View Source
class SimBA():
    """
    Implementation of the `SimBA` attack. Paper link: https://arxiv.org/abs/1905.07121
    """

    def __init__(self,  classifier):
        """
        Create a class: `SimBA` instance.
        - classifier: model to attack
        """
        self.classifier = classifier
    
    def init(self, x):
        """
        Initialize the attack.
        """
        y_pred = self.classifier.predict(np.array([x]))[0]

        x_adv = x
        y_list = []
        y_list.append(y_pred[np.argmax(y_pred)])

        perm = np.random.permutation(x.reshape(-1).shape[0])

        return x_adv, y_pred, y_list, perm

    def step(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1):
        """
        Single step for non-distributed attack.
        """
        y_adv = None
        x_adv_new = x_adv

        diff = np.zeros(x_adv.reshape(-1).shape[0])
        diff[perm[index]] = epsilon * 2
        diff = diff.reshape(x_adv.shape)

        plus = self.classifier.predict(np.array([np.clip(x_adv + diff, 0, 1)]))[0]
        if plus[np.argmax(y_pred)] < y_list[-1]:
            x_adv_new = np.clip(x_adv + diff, 0, 1)
            y_adv = plus
        else:
            minus = self.classifier.predict(np.array([np.clip(x_adv - diff, 0, 1)]))[0]
            if minus[np.argmax(y_pred)] < y_list[-1]:
                x_adv_new = np.clip(x_adv - diff, 0, 1)
                y_adv = minus
        
        if y_adv is not None:
            y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv_new, y_adv, y_list

    def batch(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1, max_workers=10, batch=50):
        """
        Single step for distributed attack.
        """
        noise = np.zeros(x_adv.shape)

        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            future_to_url = {executor.submit(self.step, x_adv, y_pred, y_list, perm, index+j, epsilon): j for j in range(0, batch)}
            for future in concurrent.futures.as_completed(future_to_url):
                j = future_to_url[future]
                try:
                    x_adv_new, _, _ = future.result()
                    noise = noise + (x_adv_new - x_adv)
                except Exception as exc:
                    print('Task %r generated an exception: %s' % (j, exc))
                else:
                    pass

        if(np.sum(noise) != 0):
            noise = utils.proj_lp(noise, xi = 1)

        x_adv = np.clip(x_adv + noise, 0, 1)

        y_adv = self.classifier.predict(np.array([x_adv]))[0] 
        y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv, y_adv, y_list

    def attack(self, x, epsilon=0.1, max_it=1000, distributed=False, batch=50, max_workers=10):
        """
        Initiate the attack.

        - x: input data
        - epsilon: perturbation on each pixel
        - max_it: number of iterations
        - distributed: if True, use distributed attack
        - batch: number of queries per worker
        - max_workers: number of workers
        """

        x_adv, y_pred, y_list, perm = self.init(x)

        if distributed:
            pbar = tqdm(range(0, max_it, batch), desc="Distributed SimBA")
        else:
            pbar = tqdm(range(0, max_it), desc="SimBA")

        for i in pbar:

            if distributed:
                x_adv, y_adv, y_list = self.batch(x_adv, y_pred, y_list, perm, i, epsilon, max_workers, batch)
            else:
                x_adv, y_adv, y_list = self.step(x_adv, y_pred, y_list, perm, i, epsilon)

            pbar.set_postfix({'origin prob': y_list[-1], 'l2 norm': np.sqrt(np.power(x_adv - x, 2).sum())})

            # Early break
            if y_adv is not None:
                if(np.argmax(y_adv) != np.argmax(y_pred)):
                    break
     
        return x_adv

Implementation of the SimBA attack. Paper link: https://arxiv.org/abs/1905.07121

#   SimBA(classifier)
View Source
    def __init__(self,  classifier):
        """
        Create a class: `SimBA` instance.
        - classifier: model to attack
        """
        self.classifier = classifier

Create a class: SimBA instance.

  • classifier: model to attack
#   def init(self, x):
View Source
    def init(self, x):
        """
        Initialize the attack.
        """
        y_pred = self.classifier.predict(np.array([x]))[0]

        x_adv = x
        y_list = []
        y_list.append(y_pred[np.argmax(y_pred)])

        perm = np.random.permutation(x.reshape(-1).shape[0])

        return x_adv, y_pred, y_list, perm

Initialize the attack.

#   def step(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1):
View Source
    def step(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1):
        """
        Single step for non-distributed attack.
        """
        y_adv = None
        x_adv_new = x_adv

        diff = np.zeros(x_adv.reshape(-1).shape[0])
        diff[perm[index]] = epsilon * 2
        diff = diff.reshape(x_adv.shape)

        plus = self.classifier.predict(np.array([np.clip(x_adv + diff, 0, 1)]))[0]
        if plus[np.argmax(y_pred)] < y_list[-1]:
            x_adv_new = np.clip(x_adv + diff, 0, 1)
            y_adv = plus
        else:
            minus = self.classifier.predict(np.array([np.clip(x_adv - diff, 0, 1)]))[0]
            if minus[np.argmax(y_pred)] < y_list[-1]:
                x_adv_new = np.clip(x_adv - diff, 0, 1)
                y_adv = minus
        
        if y_adv is not None:
            y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv_new, y_adv, y_list

Single step for non-distributed attack.

#   def batch( self, x_adv, y_pred, y_list, perm, index, epsilon=0.1, max_workers=10, batch=50 ):
View Source
    def batch(self, x_adv, y_pred, y_list, perm, index, epsilon=0.1, max_workers=10, batch=50):
        """
        Single step for distributed attack.
        """
        noise = np.zeros(x_adv.shape)

        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            future_to_url = {executor.submit(self.step, x_adv, y_pred, y_list, perm, index+j, epsilon): j for j in range(0, batch)}
            for future in concurrent.futures.as_completed(future_to_url):
                j = future_to_url[future]
                try:
                    x_adv_new, _, _ = future.result()
                    noise = noise + (x_adv_new - x_adv)
                except Exception as exc:
                    print('Task %r generated an exception: %s' % (j, exc))
                else:
                    pass

        if(np.sum(noise) != 0):
            noise = utils.proj_lp(noise, xi = 1)

        x_adv = np.clip(x_adv + noise, 0, 1)

        y_adv = self.classifier.predict(np.array([x_adv]))[0] 
        y_list.append(y_adv[np.argmax(y_pred)])

        return x_adv, y_adv, y_list

Single step for distributed attack.

#   def attack( self, x, epsilon=0.1, max_it=1000, distributed=False, batch=50, max_workers=10 ):
View Source
    def attack(self, x, epsilon=0.1, max_it=1000, distributed=False, batch=50, max_workers=10):
        """
        Initiate the attack.

        - x: input data
        - epsilon: perturbation on each pixel
        - max_it: number of iterations
        - distributed: if True, use distributed attack
        - batch: number of queries per worker
        - max_workers: number of workers
        """

        x_adv, y_pred, y_list, perm = self.init(x)

        if distributed:
            pbar = tqdm(range(0, max_it, batch), desc="Distributed SimBA")
        else:
            pbar = tqdm(range(0, max_it), desc="SimBA")

        for i in pbar:

            if distributed:
                x_adv, y_adv, y_list = self.batch(x_adv, y_pred, y_list, perm, i, epsilon, max_workers, batch)
            else:
                x_adv, y_adv, y_list = self.step(x_adv, y_pred, y_list, perm, i, epsilon)

            pbar.set_postfix({'origin prob': y_list[-1], 'l2 norm': np.sqrt(np.power(x_adv - x, 2).sum())})

            # Early break
            if y_adv is not None:
                if(np.argmax(y_adv) != np.argmax(y_pred)):
                    break
     
        return x_adv

Initiate the attack.

  • x: input data
  • epsilon: perturbation on each pixel
  • max_it: number of iterations
  • distributed: if True, use distributed attack
  • batch: number of queries per worker
  • max_workers: number of workers