Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Extract normalized grayscale from SmoothGrad * Add Integrated Gradients method * Add Integrated Gradients example * Add Integrated Gradients callback * Add Integrated Gradients to README * Add Integrated Gradients in docs
- Loading branch information
Raphael Meudec
committed
Aug 23, 2019
1 parent
0ee8db4
commit 58554e2
Showing
15 changed files
with
391 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import tensorflow as tf | ||
|
||
from tf_explain.core.integrated_gradients import IntegratedGradients | ||
|
||
IMAGE_PATH = './cat.jpg' | ||
|
||
if __name__ == '__main__': | ||
model = tf.keras.applications.vgg16.VGG16(weights='imagenet', include_top=True) | ||
|
||
img = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(224, 224)) | ||
img = tf.keras.preprocessing.image.img_to_array(img) | ||
|
||
model.summary() | ||
data = ([img], None) | ||
|
||
tabby_cat_class_index = 281 | ||
explainer = IntegratedGradients() | ||
# Compute SmoothGrad on VGG16 | ||
grid = explainer.explain(data, model, tabby_cat_class_index, n_steps=15) | ||
explainer.save(grid, '.', 'integrated_gradients.png') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import numpy as np | ||
from tf_explain.callbacks.integrated_gradients import IntegratedGradientsCallback | ||
|
||
|
||
def test_should_call_integrated_gradients_callback( | ||
random_data, convolutional_model, output_dir, mocker | ||
): | ||
mock_explainer = mocker.MagicMock(explain=mocker.MagicMock(return_value=0)) | ||
mocker.patch( | ||
"tf_explain.callbacks.integrated_gradients.IntegratedGradients", | ||
return_value=mock_explainer, | ||
) | ||
mock_image_summary = mocker.patch( | ||
"tf_explain.callbacks.integrated_gradients.tf.summary.image" | ||
) | ||
|
||
images, labels = random_data | ||
|
||
callbacks = [ | ||
IntegratedGradientsCallback( | ||
validation_data=random_data, class_index=0, output_dir=output_dir, n_steps=3 | ||
) | ||
] | ||
|
||
convolutional_model.fit(images, labels, batch_size=2, epochs=1, callbacks=callbacks) | ||
|
||
mock_explainer.explain.assert_called_once_with( | ||
random_data, convolutional_model, 0, 3 | ||
) | ||
mock_image_summary.assert_called_once_with( | ||
"IntegratedGradients", np.array([0]), step=0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import numpy as np | ||
|
||
from tf_explain.core.integrated_gradients import IntegratedGradients | ||
|
||
|
||
def test_should_explain_output(convolutional_model, random_data, mocker): | ||
mocker.patch( | ||
"tf_explain.core.integrated_gradients.grid_display", side_effect=lambda x: x | ||
) | ||
images, labels = random_data | ||
explainer = IntegratedGradients() | ||
grid = explainer.explain((images, labels), convolutional_model, 0) | ||
|
||
# Outputs is in grayscale format | ||
assert grid.shape == images.shape[:-1] | ||
|
||
|
||
def test_generate_linear_path(): | ||
input_shape = (28, 28, 1) | ||
target = np.ones(input_shape) | ||
baseline = np.zeros(input_shape) | ||
n_steps = 3 | ||
|
||
expected_output = [baseline, 1 / 2 * (target - baseline), target] | ||
|
||
output = IntegratedGradients.generate_linear_path(baseline, target, n_steps) | ||
|
||
np.testing.assert_almost_equal(output, expected_output) | ||
|
||
|
||
def test_get_integrated_gradients(random_data, convolutional_model): | ||
images, _ = random_data | ||
n_steps = 4 | ||
gradients = IntegratedGradients.get_integrated_gradients( | ||
images, convolutional_model, 0, n_steps=n_steps | ||
) | ||
|
||
expected_output_shape = (images.shape[0] / n_steps, *images.shape[1:]) | ||
|
||
assert gradients.shape == expected_output_shape |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
from .activations_visualization import ActivationsVisualizationCallback | ||
from .grad_cam import GradCAMCallback | ||
from .integrated_gradients import IntegratedGradientsCallback | ||
from .occlusion_sensitivity import OcclusionSensitivityCallback | ||
from .smoothgrad import SmoothGradCallback | ||
|
||
|
||
__all__ = [ | ||
"ActivationsVisualizationCallback", | ||
"GradCAMCallback", | ||
"IntegratedGradientsCallback", | ||
"OcclusionSensitivityCallback", | ||
"SmoothGradCallback", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
""" | ||
Callback Module for Integrated Gradients | ||
""" | ||
from datetime import datetime | ||
from pathlib import Path | ||
|
||
import numpy as np | ||
import tensorflow as tf | ||
from tensorflow.keras.callbacks import Callback | ||
|
||
from tf_explain.core.integrated_gradients import IntegratedGradients | ||
|
||
|
||
class IntegratedGradientsCallback(Callback): | ||
|
||
""" | ||
Perform Integrated Gradients algorithm for a given input | ||
Paper: [Axiomatic Attribution for Deep Networks](https://arxiv.org/pdf/1703.01365.pdf) | ||
""" | ||
|
||
def __init__( | ||
self, | ||
validation_data, | ||
class_index, | ||
n_steps=5, | ||
output_dir=Path("./logs/integrated_gradients"), | ||
): | ||
""" | ||
Constructor. | ||
Args: | ||
validation_data (Tuple[np.ndarray, Optional[np.ndarray]]): Validation data | ||
to perform the method on. Tuple containing (x, y). | ||
class_index (int): Index of targeted class | ||
n_steps (int): Number of steps in the path | ||
output_dir (str): Output directory path | ||
""" | ||
super(IntegratedGradientsCallback, self).__init__() | ||
self.validation_data = validation_data | ||
self.class_index = class_index | ||
self.n_steps = n_steps | ||
self.output_dir = Path(output_dir) / datetime.now().strftime("%Y%m%d-%H%M%S.%f") | ||
Path.mkdir(Path(self.output_dir), parents=True, exist_ok=True) | ||
|
||
self.file_writer = tf.summary.create_file_writer(str(self.output_dir)) | ||
|
||
def on_epoch_end(self, epoch, logs=None): | ||
""" | ||
Draw Integrated Gradients outputs at each epoch end to Tensorboard. | ||
Args: | ||
epoch (int): Epoch index | ||
logs (dict): Additional information on epoch | ||
""" | ||
explainer = IntegratedGradients() | ||
grid = explainer.explain( | ||
self.validation_data, self.model, self.class_index, self.n_steps | ||
) | ||
|
||
# Using the file writer, log the reshaped image. | ||
with self.file_writer.as_default(): | ||
tf.summary.image( | ||
"IntegratedGradients", np.expand_dims([grid], axis=-1), step=epoch | ||
) |
Oops, something went wrong.