Skip to content

Commit

Permalink
Merge pull request #1 from raghakot/master
Browse files Browse the repository at this point in the history
Update
  • Loading branch information
sebastianwindeck authored Aug 20, 2019
2 parents 668b0e1 + 1eec6f1 commit 97b2102
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 85 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ data format.

## Quick links
* Read the documentation at [https://raghakot.github.io/keras-vis](https://raghakot.github.io/keras-vis).
* The Japanese edition is [https://keisen.github.io/keras-vis-docs-ja](https://keisen.github.io/keras-vis-docs-ja).
* The Japanese edition is [https://keisen.github.io/keras-vis-docs-ja](https://keisen.github.io/keras-vis-docs-ja).
* Join the slack [channel](https://keras-vis.herokuapp.com/) for questions/discussions.
* We are tracking new features/tasks in [waffle.io](https://waffle.io/raghakot/keras-vis). Would love it if you lend us
a hand and submit PRs.
Expand Down Expand Up @@ -127,13 +127,13 @@ It is possible to generate an animated gif of optimization progress by leveragin
activation maximization for 'ouzel' class (output_index: 20).

```python
from keras.applications import VGG16

from vis.losses import ActivationMaximization
from vis.regularizers import TotalVariation, LPNorm
from vis.modifiers import Jitter
from vis.input_modifiers import Jitter
from vis.optimizer import Optimizer

from vis.callbacks import GifGenerator
from vis.utils.vggnet import VGG16

# Build the VGG16 network with ImageNet weights
model = VGG16(weights='imagenet', include_top=True)
Expand All @@ -151,7 +151,7 @@ losses = [
(TotalVariation(model.input), 10)
]
opt = Optimizer(model.input, losses)
opt.minimize(max_iter=500, verbose=True, image_modifiers=[Jitter()], callbacks=[GifGenerator('opt_progress')])
opt.minimize(max_iter=500, verbose=True, input_modifiers=[Jitter()], callbacks=[GifGenerator('opt_progress')])

```

Expand Down
123 changes: 75 additions & 48 deletions applications/self_driving/visualize_attention.ipynb

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions docs/md_autogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ def doc2md(self, func):
freeform text
"""
doc = getdoc(func) or ""
# The specfication of Inspect#getdoc() was changed since version 3.5,
# the documentation strings are now inherited if not overridden.
# For details see: https://docs.python.org/3.6/library/inspect.html#inspect.getdoc
doc = "" if func.__doc__ is None else getdoc(func) or ""
blockindent = 0
argindent = 1
out = []
Expand Down Expand Up @@ -309,7 +312,7 @@ def class2md(self, cls, depth=2):
handlers.append("\n%s %s.%s\n *Handler*" % (subsection, clsname, name))

methods = []
for name, obj in getmembers(cls, inspect.ismethod):
for name, obj in getmembers(cls, lambda a: inspect.ismethod(a) or inspect.isfunction(a)):
if not name.startswith("_") and hasattr(obj,
"__module__") and obj.__module__ == modname and name not in handlers:
methods.append(self.func2md(obj, clsname=clsname, depth=depth + 1))
Expand Down
2 changes: 1 addition & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
site_name: Keras-vis Documentation
theme: readthedocs
docs_dir: sources
repo_url: http://github.com/raghakot/keras-vis
repo_url: https://github.com/raghakot/keras-vis
edit_uri: blob/master/docs/templates
site_description: 'Documentation for keras-vis, Neural Network Visualization Toolkit.'

Expand Down
12 changes: 6 additions & 6 deletions docs/templates/visualizations/activation_maximization.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ output activations. i.e., we compute

$$\frac{\partial ActivationMaximizationLoss}{\partial input}$$

and use that estimate to update the input. [ActivationMaximization](../vis.losses#activationmaximization) loss simply
and use that estimate to update the input. [ActivationMaximization](../vis.losses.md#activationmaximization) loss simply
outputs small values for large filter activations (we are minimizing losses during gradient descent iterations).
This allows us to understand what sort of input patterns activate a particular filter. For example, there could be
an eye filter that activates for the presence of eye within the input image.
Expand All @@ -20,9 +20,9 @@ an eye filter that activates for the presence of eye within the input image.

There are two APIs exposed to perform activation maximization.

1. [visualize_activation](../vis.visualization#visualize_activation): This is the general purpose API for visualizing
1. [visualize_activation](../vis.visualization.md#visualize_activation): This is the general purpose API for visualizing
activations.
2. [visualize_activation_with_losses](../vis.visualization#visualize_activation_with_losses): This is intended for
2. [visualize_activation_with_losses](../vis.visualization.md#visualize_activation_with_losses): This is intended for
research use-cases where some custom weighted losses can be minimized.

See [examples/](https://github.com/raghakot/keras-vis/tree/master/examples) for code examples.
Expand Down Expand Up @@ -60,7 +60,7 @@ output should correspond to more apples showing up in the input image. Similarly
This can be achieved by using `grad_modifier` option. As the name suggests, it is used to modify the gradient of losses
with respect to inputs. By default, `ActivationMaximization` loss is used to increase the output. By setting
`grad_modifier='negate'` you can negate the gradients, thus causing output values to decrease.
[gradient_modifiers](../vis.grad_modifiers) are very powerful and show up in other visualization APIs as well.
[gradient_modifiers](../vis.grad_modifiers.md) are very powerful and show up in other visualization APIs as well.


#### Conv filter visualization
Expand All @@ -70,7 +70,7 @@ what a filter might be computing. Here, `filter_indices` refers to the index of

### Advanced usage

[backprop_modifiers](../vis.backprop_modifiers) allow you to modify the backpropagation behavior. For examples,
[backprop_modifiers](../vis.backprop_modifiers.md) allow you to modify the backpropagation behavior. For examples,
you could tweak backprop to only propagate positive gradients by using `backprop_modifier='relu'`. This parameter also
accepts a function and can be used to implement your crazy research idea :)

Expand All @@ -81,7 +81,7 @@ By default, `visualize_activation` uses `TotalVariation` and `LpNorm` regulariza
is very likely that you would see `ActivationMaximization Loss` bounce back and forth as they are dominated by regularization
loss weights. Try setting all weights to zero and gradually try increasing values of total variation weight.

- To get sharper looking images, use [Jitter](../vis.input_modifiers#jitter) input modifier.
- To get sharper looking images, use [Jitter](../vis.input_modifiers.md#jitter) input modifier.

- Regression models usually do not provide enough gradient information to generate meaningful input images. Try seeding
the input using `seed_input` and see if the modifications to the input make sense.
Expand Down
4 changes: 2 additions & 2 deletions docs/templates/visualizations/class_activation_maps.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ In keras-vis, we use [grad-CAM](https://arxiv.org/pdf/1610.02391.pdf) as its con

There are two APIs exposed to visualize grad-CAM and are almost identical to [saliency usage](saliency#Usage).

1. [visualize_cam](../vis.visualization#visualize_cam): This is the general purpose API for visualizing
1. [visualize_cam](../vis.visualization.md#visualize_cam): This is the general purpose API for visualizing
grad-CAM.
2. [visualize_cam_with_losses](../vis.visualization#visualize_cam_with_losses): This is intended for
2. [visualize_cam_with_losses](../vis.visualization.md#visualize_cam_with_losses): This is intended for
research use-cases where some custom weighted loss can be used.

The only notable addition is the `penultimate_layer_idx` parameter. This can be used to specify the pre-layer
Expand Down
8 changes: 4 additions & 4 deletions docs/templates/visualizations/saliency.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ regions that most contribute towards the output.

There are two APIs exposed to visualize saliency.

1. [visualize_saliency](../vis.visualization#visualize_saliency): This is the general purpose API for visualizing
1. [visualize_saliency](../vis.visualization.md#visualize_saliency): This is the general purpose API for visualizing
saliency.
2. [visualize_saliency_with_losses](../vis.visualization#visualize_saliency_with_losses): This is intended for
2. [visualize_saliency_with_losses](../vis.visualization.md#visualize_saliency_with_losses): This is intended for
research use-cases where some custom weighted loss can be used.

See [examples/](https://github.com/raghakot/keras-vis/tree/master/examples) for code examples.
Expand Down Expand Up @@ -68,7 +68,7 @@ gradients (which indicate the decrease) as positive and therefore visualize decr

- To visualize what contributed to the predicted output, we want to consider gradients that have very low positive
or negative values. This can be achieved by performing `grads = abs(1 / grads)` to magnifies small gradients. Equivalently,
you can use `grad_modifier='small_values'`, which does the same thing. [gradient_modifiers](../vis.grad_modifiers)
you can use `grad_modifier='small_values'`, which does the same thing. [gradient_modifiers](../vis.grad_modifiers.md)
are very powerful and show up in other visualization APIs as well.

You can see a practical application for this in the
Expand All @@ -84,7 +84,7 @@ In guided saliency, the backprop step is modified to only propagate positive gra
For details see the paper: [String For Simplicity: The All Convolutional Net](https://arxiv.org/pdf/1412.6806.pdf).

For both these cases, we can use `backprop_modifier='relu'` and `backprop_modifier='guided'` respectively. You
can also implement your own [backprop_modifier](../vis.backprop_modifiers) to try your crazy research idea :)
can also implement your own [backprop_modifier](../vis.backprop_modifiers.md) to try your crazy research idea :)

#### Conv filter saliency

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import find_packages


version = '0.4.1'
version = '0.5.0'

setup(name='keras-vis',
version=version,
Expand All @@ -12,7 +12,7 @@
url='https://github.com/raghakot/keras-vis',
download_url='https://github.com/raghakot/keras-vis/tarball/{}'.format(version),
license='MIT',
install_requires=['keras', 'six', 'scikit-image', 'matplotlib', 'h5py'],
install_requires=['keras>=2.0', 'six', 'scikit-image', 'matplotlib', 'h5py'],
extras_require={
'vis_utils': ['Pillow', 'imageio'],
'tests': ['pytest',
Expand Down
14 changes: 12 additions & 2 deletions vis/callbacks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import
import pprint
import numpy as np
from .utils import utils

try:
Expand All @@ -14,7 +15,7 @@ def _check_imageio():


class OptimizerCallback(object):
"""Abstract class for defining callbacks for use with [Optimizer.minimize](vis.optimizer#optimizerminimize).
"""Abstract class for defining callbacks for use with [Optimizer.minimize](vis.optimizer.md#optimizerminimize).
"""

def callback(self, i, named_losses, overall_loss, grads, wrt_value):
Expand Down Expand Up @@ -47,18 +48,27 @@ def callback(self, i, named_losses, overall_loss, grads, wrt_value):
class GifGenerator(OptimizerCallback):
"""Callback to construct gif of optimized image.
"""
def __init__(self, path):
def __init__(self, path, input_range=(0, 255)):
"""
Args:
path: The file path to save gif.
input_range: Specifies the input range as a `(min, max)` tuple.
This is used to rescale the `wrt_value` passed to `callback` method
to the given range. (Default value=(0, 255))
"""
self.input_range = input_range
_check_imageio()
if not path.endswith('.gif'):
path += '.gif'
self.writer = imageio.get_writer(path, mode='I', loop=1)

def callback(self, i, named_losses, overall_loss, grads, wrt_value):
img = utils.deprocess_input(wrt_value[0])
# If range has integer numbers, cast to 'uint8'
if self.input_range is not None and \
isinstance(self.input_range[0], int) and \
isinstance(self.input_range[1], int):
img = np.clip(img, self.input_range[0], self.input_range[1]).astype('uint8')
img = utils.draw_text(img, "Step {}".format(i + 1))
self.writer.append_data(img)

Expand Down
2 changes: 1 addition & 1 deletion vis/input_modifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class InputModifier(object):
"""Abstract class for defining an input modifier. An input modifier can be used with the
[Optimizer.minimize](vis.optimizer#optimizerminimize) to make `pre` and `post` changes to the optimized input
[Optimizer.minimize](vis.optimizer.md#optimizerminimize) to make `pre` and `post` changes to the optimized input
during the optimization process.
```python
Expand Down
6 changes: 3 additions & 3 deletions vis/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, input_tensor, losses, input_range=(0, 255), wrt_tensor=None,
Args:
input_tensor: An input tensor of shape: `(samples, channels, image_dims...)` if `image_data_format=
channels_first` or `(samples, image_dims..., channels)` if `image_data_format=channels_last`.
losses: List of ([Loss](vis.losses#Loss), weight) tuples.
losses: List of ([Loss](vis.losses.md#Loss), weight) tuples.
input_range: Specifies the input range as a `(min, max)` tuple. This is used to rescale the
final optimized input to the given range. (Default value=(0, 255))
wrt_tensor: Short for, with respect to. This instructs the optimizer that the aggregate loss from `losses`
Expand Down Expand Up @@ -116,13 +116,13 @@ def minimize(self, seed_input=None, max_iter=200,
channels_first` or `(samples, image_dims..., channels)` if `image_data_format=channels_last`.
Seeded with random noise if set to None. (Default value = None)
max_iter: The maximum number of gradient descent iterations. (Default value = 200)
input_modifiers: A list of [InputModifier](vis.input_modifiers#inputmodifier) instances specifying
input_modifiers: A list of [InputModifier](vis.input_modifiers.md#inputmodifier) instances specifying
how to make `pre` and `post` changes to the optimized input during the optimization process.
`pre` is applied in list order while `post` is applied in reverse order. For example,
`input_modifiers = [f, g]` means that `pre_input = g(f(inp))` and `post_input = f(g(inp))`
grad_modifier: gradient modifier to use. See [grad_modifiers](vis.grad_modifiers.md). If you don't
specify anything, gradients are unchanged. (Default value = None)
callbacks: A list of [OptimizerCallback](vis.callbacks#optimizercallback) instances to trigger.
callbacks: A list of [OptimizerCallback](vis.callbacks.md#optimizercallback) instances to trigger.
verbose: Logs individual losses at the end of every gradient descent iteration.
Very useful to estimate loss weight factor(s). (Default value = True)
Expand Down
8 changes: 4 additions & 4 deletions vis/visualization/activation_maximization.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ def visualize_activation_with_losses(input_tensor, losses, wrt_tensor=None,
channels_first` or `(samples, image_dims..., channels)` if `image_data_format=channels_last`.
wrt_tensor: Short for, with respect to. The gradients of losses are computed with respect to this tensor.
When None, this is assumed to be the same as `input_tensor` (Default value: None)
losses: List of ([Loss](vis.losses#Loss), weight) tuples.
losses: List of ([Loss](vis.losses.md#Loss), weight) tuples.
seed_input: Seeds the optimization with a starting image. Initialized with a random value when set to None.
(Default value = None)
input_range: Specifies the input range as a `(min, max)` tuple. This is used to rescale the
final optimized input to the given range. (Default value=(0, 255))
optimizer_params: The **kwargs for optimizer [params](vis.optimizer#optimizerminimize). Will default to
optimizer_params: The **kwargs for optimizer [params](vis.optimizer.md#optimizerminimize). Will default to
reasonable values when required keys are not found.
Returns:
Expand Down Expand Up @@ -67,7 +67,7 @@ def visualize_activation(model, layer_idx, filter_indices=None, wrt_tensor=None,
If None, all filters are visualized. (Default value = None)
For `keras.layers.Dense` layer, `filter_idx` is interpreted as the output index.
If you are visualizing final `keras.layers.Dense` layer, consider switching 'softmax' activation for
'linear' using [utils.apply_modifications](vis.utils.utils#apply_modifications) for better results.
'linear' using [utils.apply_modifications](vis.utils.utils.md#apply_modifications) for better results.
wrt_tensor: Short for, with respect to. The gradients of losses are computed with respect to this tensor.
When None, this is assumed to be the same as `input_tensor` (Default value: None)
seed_input: Seeds the optimization with a starting input. Initialized with a random value when set to None.
Expand All @@ -81,7 +81,7 @@ def visualize_activation(model, layer_idx, filter_indices=None, wrt_tensor=None,
act_max_weight: The weight param for `ActivationMaximization` loss. Not used if 0 or None. (Default value = 1)
lp_norm_weight: The weight param for `LPNorm` regularization loss. Not used if 0 or None. (Default value = 10)
tv_weight: The weight param for `TotalVariation` regularization loss. Not used if 0 or None. (Default value = 10)
optimizer_params: The **kwargs for optimizer [params](vis.optimizer#optimizerminimize). Will default to
optimizer_params: The **kwargs for optimizer [params](vis.optimizer.md#optimizerminimize). Will default to
reasonable values when required keys are not found.
Example:
Expand Down
Loading

0 comments on commit 97b2102

Please sign in to comment.