class IterativeGradientMethod(Attack):
    """
    Abstract base class for all iterative gradient based attacks.

    Args:
        network (Cell): Target model.
        eps (float): Proportion of adversarial perturbation generated by the
            attack to data range. Default: 0.3.
        eps_iter (float): Proportion of single-step adversarial perturbation
            generated by the attack to data range. Default: 0.1.
        bounds (tuple): Upper and lower bounds of data, indicating the data range.
            In form of (clip_min, clip_max). Default: (0.0, 1.0).
        nb_iter (int): Number of iteration. Default: 5.
        loss_fn (Loss): Loss function for optimization. If None, the input network \
            is already equipped with loss function. Default: None.
    """
    def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), nb_iter=5,
                 loss_fn=None):
        super(IterativeGradientMethod, self).__init__()
        self._network = check_model('network', network, Cell)
        self._eps = check_value_positive('eps', eps)
        self._eps_iter = check_value_positive('eps_iter', eps_iter)
        self._nb_iter = check_int_positive('nb_iter', nb_iter)
        self._bounds = None
        if bounds is not None:
            self._bounds = check_param_multi_types('bounds', bounds, [list, tuple])
            for b in self._bounds:
                _ = check_param_multi_types('bound', b, [int, float])
        if loss_fn is None:
            self._loss_grad = network
        else:
            self._loss_grad = GradWrapWithLoss(WithLossCell(self._network, loss_fn))
        self._loss_grad.set_train()

    @abstractmethod
    def generate(self, inputs, labels):
        """
        Generate adversarial examples based on input samples and original/target labels.

        Args:
            inputs (Union[numpy.ndarray, tuple]): Benign input samples used as references to create
                adversarial examples.
            labels (Union[numpy.ndarray, tuple]): Original/target labels. \
                For each input if it has more than one label, it is wrapped in a tuple.
        Raises:
            NotImplementedError: This function is not available in
                IterativeGradientMethod.

        Examples:
            >>> adv_x = attack.generate([[0.1, 0.9, 0.6],
            >>>                          [0.3, 0, 0.3]],
            >>>                         [[0, , 1, 0, 0, 0, 0, 0, 0, 0],
            >>>                          [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]])
        """
        msg = 'The function generate() is an abstract method in class ' \
              '`IterativeGradientMethod`, and should be implemented ' \
              'in child class.'
        LOGGER.error(TAG, msg)
        raise NotImplementedError(msg)
Example #2
0
class GradientMethod(Attack):
    """
    Abstract base class for all single-step gradient-based attacks.

    Args:
        network (Cell): Target model.
        eps (float): Proportion of single-step adversarial perturbation generated
            by the attack to data range. Default: 0.07.
        alpha (float): Proportion of single-step random perturbation to data range.
            Default: None.
        bounds (tuple): Upper and lower bounds of data, indicating the data range.
            In form of (clip_min, clip_max). Default: None.
        loss_fn (Loss): Loss function for optimization. Default: None.

    Examples:
        >>> inputs = np.array([[0.1, 0.2, 0.6], [0.3, 0, 0.4]])
        >>> labels = np.array([[0, 1, 0, 0, 0], [0, 0, 1, 0, 0]])
        >>> attack = FastGradientMethod(network)
        >>> adv_x = attack.generate(inputs, labels)
    """
    def __init__(self,
                 network,
                 eps=0.07,
                 alpha=None,
                 bounds=None,
                 loss_fn=None):
        super(GradientMethod, self).__init__()
        self._network = check_model('network', network, Cell)
        self._eps = check_value_positive('eps', eps)
        self._dtype = None
        if bounds is not None:
            self._bounds = check_param_multi_types('bounds', bounds,
                                                   [list, tuple])
            for b in self._bounds:
                _ = check_param_multi_types('bound', b, [int, float])
        else:
            self._bounds = bounds
        if alpha is not None:
            self._alpha = check_value_positive('alpha', alpha)
        else:
            self._alpha = alpha
        if loss_fn is None:
            loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False,
                                                    sparse=False)
        with_loss_cell = WithLossCell(self._network, loss_fn)
        self._grad_all = GradWrapWithLoss(with_loss_cell)
        self._grad_all.set_train()

    def generate(self, inputs, labels):
        """
        Generate adversarial examples based on input samples and original/target labels.

        Args:
            inputs (numpy.ndarray): Benign input samples used as references to create
                    adversarial examples.
            labels (numpy.ndarray): Original/target labels.

        Returns:
            numpy.ndarray, generated adversarial examples.
        """
        inputs, labels = check_pair_numpy_param('inputs', inputs, 'labels',
                                                labels)
        self._dtype = inputs.dtype
        gradient = self._gradient(inputs, labels)
        # use random method or not
        if self._alpha is not None:
            random_part = self._alpha * np.sign(
                np.random.normal(size=inputs.shape)).astype(self._dtype)
            perturbation = (self._eps - self._alpha) * gradient + random_part
        else:
            perturbation = self._eps * gradient

        if self._bounds is not None:
            clip_min, clip_max = self._bounds
            perturbation = perturbation * (clip_max - clip_min)
            adv_x = inputs + perturbation
            adv_x = np.clip(adv_x, clip_min, clip_max)
        else:
            adv_x = inputs + perturbation
        return adv_x

    @abstractmethod
    def _gradient(self, inputs, labels):
        """
        Calculate gradients based on input samples and original/target labels.

        Args:
            inputs (numpy.ndarray): Benign input samples used as references to
                create adversarial examples.
            labels (numpy.ndarray): Original/target labels.

        Raises:
            NotImplementedError: It is an abstract method.
        """
        msg = 'The function _gradient() is an abstract method in class ' \
              '`GradientMethod`, and should be implemented in child class.'
        LOGGER.error(TAG, msg)
        raise NotImplementedError(msg)