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)