def explain(self, validation_data, model, class_index, num_samples=5, noise=1.0): """ Compute SmoothGrad for a specific class index Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect class_index (int): Index of targeted class num_samples (int): Number of noisy samples to generate for each input image noise (float): Standard deviation for noise normal distribution Returns: np.ndarray: Grid of all the smoothed gradients """ images, _ = validation_data noisy_images = SmoothGrad.generate_noisy_images( images, num_samples, noise) smoothed_gradients = SmoothGrad.get_averaged_gradients( noisy_images, model, class_index, num_samples) grayscale_gradients = transform_to_normalized_grayscale( tf.abs(smoothed_gradients)).numpy() grid = grid_display(grayscale_gradients) return grid
def explain(self, validation_data, model, class_index, patch_size): """ Compute Occlusion Sensitivity maps for a specific class index. Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect class_index (int): Index of targeted class patch_size (int): Size of patch to apply on the image Returns: np.ndarray: Grid of all the sensitivity maps with shape (batch_size, H, W, 3) """ images, _ = validation_data sensitivity_maps = np.array([ self.get_sensitivity_map(model, image, class_index, patch_size) for image in images ]) heatmaps = np.array([ heatmap_display(heatmap, image) for heatmap, image in zip(sensitivity_maps, images) ]) grid = grid_display(heatmaps) return grid
def explain(self, validation_data, model, class_index, n_steps=10): """ Compute Integrated Gradients for a specific class index Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect class_index (int): Index of targeted class n_steps (int): Number of steps in the path Returns: np.ndarray: Grid of all the integrated gradients """ images, _ = validation_data interpolated_images = IntegratedGradients.generate_interpolations( np.array(images), n_steps) integrated_gradients = IntegratedGradients.get_integrated_gradients( interpolated_images, model, class_index, n_steps) grayscale_integrated_gradients = transform_to_normalized_grayscale( tf.abs(integrated_gradients)).numpy() grid = grid_display(grayscale_integrated_gradients) return grid
def explain(self, validation_data, model, layer_name, class_index): """ Compute GradCAM for a specific class index. Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect layer_name (str): Targeted layer for GradCAM class_index (int): Index of targeted class Returns: numpy.ndarray: Grid of all the GradCAM """ images, _ = validation_data outputs, guided_grads = GradCAM.get_gradients_and_filters( model, images, layer_name, class_index ) cams = GradCAM.generate_ponderated_output(outputs, guided_grads) heatmaps = np.array( [heatmap_display(cam.numpy(), image) for cam, image in zip(cams, images)] ) grid = grid_display(heatmaps) return grid
def explain( self, validation_data, model, class_index, layer_name=None, use_guided_grads=True, colormap=cv2.COLORMAP_VIRIDIS, image_weight=0.7, mean_transform=None, std_dev_transform=None, ): """ Compute GradCAM for a specific class index. Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect class_index (int): Index of targeted class layer_name (str): Targeted layer for GradCAM. If no layer is provided, it is automatically infered from the model architecture. colormap (int): OpenCV Colormap to use for heatmap visualization image_weight (float): An optional `float` value in range [0,1] indicating the weight of the input image to be overlaying the calculated attribution maps. Defaults to `0.7`. use_guided_grads (boolean): Whether to use guided grads or raw gradients Returns: numpy.ndarray: Grid of all the GradCAM """ images, _ = validation_data if layer_name is None: layer_name = self.infer_grad_cam_target_layer(model) outputs, grads = GradCAM.get_gradients_and_filters( model, images, layer_name, class_index, use_guided_grads ) cams = GradCAM.generate_ponderated_output(outputs, grads) heatmaps = np.array( [ # not showing the actual image if image_weight=0 heatmap_display( cam.numpy(), image, colormap, image_weight, mean_transform, std_dev_transform, ) for cam, image in zip(cams, images) ] ) grid = grid_display(heatmaps) return grid
def test_should_raise_warning_if_grid_size_is_too_small(array_to_display): with pytest.warns(Warning) as w: grid = grid_display(array_to_display, num_rows=1, num_columns=1) assert ( w[0].message.args[0] == "Given values for num_rows and num_columns doesn't allow to display all images. Values have been overrided to respect at least num_columns" ) assert grid.shape == (28 * 7, 28, 3)
def explain( self, validation_data, model, class_index, layer_name=None, colormap=cv2.COLORMAP_VIRIDIS, ): """ Compute GradCAM for a specific class index. Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). model (tf.keras.Model): tf.keras model to inspect class_index (int): Index of targeted class layer_name (str): Targeted layer for GradCAM. If no layer is provided, it is automatically infered from the model architecture. colormap (int): OpenCV Colormap to use for heatmap visualization Returns: numpy.ndarray: Grid of all the GradCAM """ images, _ = validation_data if layer_name is None: layer_name = self.infer_grad_cam_target_layer(model) outputs, guided_grads = GradCAM.get_gradients_and_filters( model, images, layer_name, class_index) cams = GradCAM.generate_ponderated_output(outputs, guided_grads) heatmaps = np.array([ heatmap_display(cam.numpy(), image, colormap) for cam, image in zip(cams, images) ]) grid = grid_display(heatmaps) return grid, outputs, guided_grads
def explain_score_model(self, validation_data, score_model, class_index): """ Perform gradients backpropagation for a given input Args: validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data to perform the method on. Tuple containing (x, y). score_model (tf.keras.Model): tf.keras model to inspect. The last layer should not have any activation function. class_index (int): Index of targeted class Returns: numpy.ndarray: Grid of all the gradients """ images, _ = validation_data gradients = self.compute_gradients(images, score_model, class_index) grayscale_gradients = transform_to_normalized_grayscale( tf.abs(gradients)).numpy() grid = grid_display(grayscale_gradients) return grid
def plot_well_gradCAM(X, days, segment_labels, classify_labels, model, save_root='.'): explainer = GradCAM() _model = Model( model.input, model.classify_out) # model instance for explain classify output X = X[:9] days = days[:9] n_r = 3 if len(X) > 4 else 2 input_grid = grid_display(X[..., 0], num_rows=n_r, num_columns=n_r) pos_cam_grid = explainer.explain((list(X), None), _model, class_index=3, image_weight=0.) neg_cam_grid = explainer.explain((list(X), None), _model, class_index=0, image_weight=0.) preds = model.predict(X) classify_preds = preds[1][..., 1] + preds[1][..., 2] * 2 + preds[1][..., 3] * 3 segment_preds = preds[0][..., 1] + preds[0][..., 2] * 2 + preds[0][..., 3] * 3 segment_pred_grid = grid_display(segment_preds, num_rows=n_r, num_columns=n_r) classify_pred_grid = grid_display(classify_preds.reshape((-1, 1, 1)), num_rows=n_r, num_columns=n_r) plt.clf() plt.figure(figsize=(15, 10)) plt.subplot(2, 3, 1) plt.imshow(input_grid) plt.title("Input Phase Contrast\ndays: %s\nsegment_label: %s" % (str(days), str(classify_labels))) plt.axis('off') plt.subplot(2, 3, 2) plt.imshow(pos_cam_grid) plt.title("Grad CAM on class 3") plt.axis('off') plt.subplot(2, 3, 3) plt.imshow(neg_cam_grid) plt.title("Grad CAM on class 0") plt.axis('off') plt.subplot(2, 3, 4) plt.imshow(classify_pred_grid, vmin=0., vmax=3., cmap='viridis') plt.title("Classification prediction") plt.axis('off') plt.subplot(2, 3, 5) plt.imshow(segment_pred_grid, vmin=0., vmax=3., cmap='viridis') plt.title("Segmentation prediction") plt.axis('off') if not segment_labels[0] is None: plt.subplot(2, 3, 6) y = segment_labels[0] segment_mat = y[..., 1] + y[..., 2] * 2 + y[..., 3] * 3 plt.imshow(segment_mat, vmin=0., vmax=3., cmap='viridis') plt.title("Final fluorescence, class %d" % class_index) plt.axis('off') plt.savefig(os.path.join(save_root, '%s-explain.png' % ('-'.join(w))), dpi=300) return pos_cam_grid, neg_cam_grid
def test_should_fill_with_zeros_if_missing_elements(array_to_display): grid = grid_display(array_to_display) assert grid.shape == (28 * 3, 28 * 3, 3) np.testing.assert_equal(grid[56:, 28:, :], np.zeros((28, 56, 3)))
def test_should_return_a_grid_square_by_default(grid_display_kwargs, expected_shape, array_to_display): grid = grid_display(array_to_display, **grid_display_kwargs) assert grid.shape == expected_shape