Ejemplo n.º 1
0
    def loss_gradient(self, x: np.ndarray, y: np.ndarray,
                      **kwargs) -> np.ndarray:
        """
        Compute the gradient of the loss function w.r.t. `x`.

        :param x: Sample input with shape as expected by the model.
        :param y: Target values (class labels) one-hot-encoded of shape (nb_samples, nb_classes) or indices of shape
                  (nb_samples,).
        :param sampling: True if loss gradients should be determined with Monte Carlo sampling.
        :type sampling: `bool`
        :return: Array of gradients of the same shape as `x`.
        """
        import torch  # lgtm [py/repeated-import]

        sampling = kwargs.get("sampling")

        if sampling:
            # Apply preprocessing
            x_preprocessed, y_preprocessed = self._apply_preprocessing(
                x, y, fit=False)

            # Check label shape
            if self._reduce_labels:
                y_preprocessed = np.argmax(y_preprocessed, axis=1)

            # Convert the inputs to Tensors
            inputs_t = torch.from_numpy(x_preprocessed).to(self._device)
            inputs_t.requires_grad = True

            # Convert the labels to Tensors
            labels_t = torch.from_numpy(y_preprocessed).to(self._device)
            inputs_repeat_t = inputs_t.repeat_interleave(self.sample_size, 0)

            noise = torch.randn_like(inputs_repeat_t,
                                     device=self._device) * self.scale
            inputs_noise_t = inputs_repeat_t + noise
            if self.clip_values is not None:
                inputs_noise_t.clamp(self.clip_values[0], self.clip_values[1])

            model_outputs = self._model(inputs_noise_t)[-1]
            softmax = torch.nn.functional.softmax(model_outputs, dim=1)
            average_softmax = (softmax.reshape(-1, self.sample_size,
                                               model_outputs.shape[-1]).mean(
                                                   1, keepdim=True).squeeze(1))
            log_softmax = torch.log(average_softmax.clamp(min=1e-20))
            loss = torch.nn.functional.nll_loss(log_softmax, labels_t)

            # Clean gradients
            self._model.zero_grad()

            # Compute gradients
            loss.backward()
            gradients = inputs_t.grad.cpu().numpy().copy()  # type: ignore
            gradients = self._apply_preprocessing_gradient(x, gradients)
            assert gradients.shape == x.shape

        else:
            gradients = PyTorchClassifier.loss_gradient(self, x, y, **kwargs)

        return gradients
def test_loss_gradient_amp(
    art_warning,
    get_default_mnist_subset,
    image_dl_estimator,
    expected_values,
    mnist_shape,
    device_type,
):
    import torch
    import torch.nn as nn

    from art.estimators.classification.pytorch import PyTorchClassifier

    try:
        (expected_gradients_1, expected_gradients_2) = expected_values()

        (_, _), (x_test_mnist, y_test_mnist) = get_default_mnist_subset

        classifier, _ = image_dl_estimator(from_logits=True)
        optimizer = torch.optim.Adam(classifier.model.parameters(), lr=0.01)

        # Redefine the classifier with amp
        clip_values = (0, 1)
        criterion = nn.CrossEntropyLoss()
        classifier = PyTorchClassifier(
            clip_values=clip_values,
            model=classifier.model,
            preprocessing_defences=[],
            loss=criterion,
            input_shape=(1, 28, 28),
            nb_classes=10,
            device_type=device_type,
            optimizer=optimizer,
            use_amp=True,
            loss_scale=1.0,
        )

        # Compute loss gradients
        gradients = classifier.loss_gradient(x_test_mnist, y_test_mnist)

        # Test shape
        assert gradients.shape == (x_test_mnist.shape[0], ) + mnist_shape

        # First test of gradients
        sub_gradients = gradients[0, 0, :, 14]

        np.testing.assert_array_almost_equal(
            sub_gradients,
            expected_gradients_1,
            decimal=4,
        )

        # Second test of gradients
        sub_gradients = gradients[0, 0, 14, :]

        np.testing.assert_array_almost_equal(
            sub_gradients,
            expected_gradients_2,
            decimal=4,
        )

        # Compute loss gradients with framework
        gradients = classifier.loss_gradient_framework(
            torch.tensor(x_test_mnist).to(classifier.device),
            torch.tensor(y_test_mnist).to(classifier.device))
        gradients = gradients.cpu().numpy()

        # Test shape
        assert gradients.shape == (x_test_mnist.shape[0], ) + mnist_shape

        # First test of gradients
        sub_gradients = gradients[0, 0, :, 14]

        np.testing.assert_array_almost_equal(
            sub_gradients,
            expected_gradients_1,
            decimal=4,
        )

        # Second test of gradients
        sub_gradients = gradients[0, 0, 14, :]

        np.testing.assert_array_almost_equal(
            sub_gradients,
            expected_gradients_2,
            decimal=4,
        )

    except ARTTestException as e:
        art_warning(e)