Skip to content

Commit

Permalink
Merge pull request #59 from keisen/hotfixes/#58
Browse files Browse the repository at this point in the history
Hotfixes/#58
  • Loading branch information
keisen authored Jun 3, 2021
2 parents 6e01066 + a7416d5 commit a96f632
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 27 deletions.
24 changes: 19 additions & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,42 @@ jobs:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]
tensorflow: [2.0.4, 2.1.3, 2.2.2, 2.3.2, 2.4.1, 2.5.0]
include:
- python-version: 3.9
tensorflow: 2.5.0
extra-require: ["develop,examples", "develop,examples,tfa"]
exclude:
- python-version: 3.8
tensorflow: 2.0.4
- python-version: 3.8
tensorflow: 2.1.3
- tensorflow: 2.0.4
extra-require: "develop,examples,tfa"
- tensorflow: 2.1.3
extra-require: "develop,examples,tfa"
- tensorflow: 2.2.2
extra-require: "develop,examples,tfa"
include:
- python-version: 3.9
tensorflow: 2.5.0
extra-require: "develop,examples"
- python-version: 3.9
tensorflow: 2.5.0
extra-require: "develop,examples,tfa"
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
- name: Update packaging tools
run: |
python -m pip install --no-cache-dir --upgrade pip
python -m pip install --no-cache-dir --upgrade setuptools
python -m pip install --no-cache-dir -e .[develop,examples] tensorflow==${{ matrix.tensorflow }}
- name: Install dependencies
run: |
python -m pip install --no-cache-dir -e .[${{ matrix.extra-require }}] tensorflow==${{ matrix.tensorflow }}
- name: Test with pytest
run: |
PYTHONPATH=$PWD:$PYTHONPATH py.test
Expand Down
21 changes: 19 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="tf-keras-vis",
version="0.6.1",
version="0.6.2",
author="keisen",
author_email="k.keisen@gmail.com",
description="Neural network visualization toolkit for tf.keras",
Expand All @@ -14,9 +14,26 @@
url="https://github.com/keisen/tf-keras-vis",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"Environment :: GPU :: NVIDIA CUDA",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Education",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Scientific/Engineering :: Image Recognition",
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
],
python_requires='>=3.6, <3.10',
install_requires=['scipy', 'pillow', 'deprecated', 'imageio', 'packaging'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
from tensorflow.keras.models import load_model

from tf_keras_vis.activation_maximization import ActivationMaximization
from tf_keras_vis.utils.input_modifiers import Jitter, Rotate
from tf_keras_vis.utils.test import (MockCallback, MockListOfScore, MockScore,
MockTupleOfScore, does_not_raise,
dummy_sample, mock_conv_model,
mock_conv_model_with_flot32_output,
mock_multiple_io_model)
from tf_keras_vis.utils.input_modifiers import Jitter, Rotate2D
from tf_keras_vis.utils.regularizers import Norm, TotalVariation2D
from tf_keras_vis.utils.test import (MockCallback, MockListOfScore, MockScore, MockTupleOfScore,
does_not_raise, dummy_sample, mock_conv_model,
mock_conv_model_with_flot32_output, mock_multiple_io_model)

if version(tf.version.VERSION) >= version("2.4.0"):
from tensorflow.keras.mixed_precision import set_global_policy
Expand Down Expand Up @@ -166,8 +165,9 @@ def test__call__if_seed_input_is_(self, seed_inputs, expectation, multiple_io_mo
def test__call__with_inputs_modifiers(self, multiple_io_model):
activation_maximization = ActivationMaximization(multiple_io_model)
result = activation_maximization(
MockScore(), steps=3, input_modifiers={'input-1': [Jitter(jitter=8),
Rotate(degree=3)]})
MockScore(),
steps=3,
input_modifiers={'input-1': [Jitter(jitter=8), Rotate2D(degree=3)]})
assert result[0].shape == (1, 8, 8, 3)
assert result[1].shape == (1, 10, 10, 3)

Expand Down Expand Up @@ -227,3 +227,57 @@ def test__call__when_reuse_optimizer(self):
with pytest.raises(ValueError):
result = activation_maximization(MockScore(), steps=3, optimizer=optimizer)
assert result.shape == (1, 8, 8, 3)


class TestActivationMaximizationWithDenseModel():
@pytest.mark.parametrize("scores,expectation", [
(None, pytest.raises(ValueError)),
(MockScore(), does_not_raise()),
(MockTupleOfScore(), does_not_raise()),
(MockListOfScore(), does_not_raise()),
([MockScore()], does_not_raise()),
])
def test__call__if_score_is_(self, scores, expectation, dense_model):
activation_maximization = ActivationMaximization(dense_model)
with expectation:
result = activation_maximization(scores,
input_modifiers=[],
regularizers=[Norm(10.)],
steps=3)
assert result.shape == (1, 8)

@pytest.mark.parametrize("seed_input,expected", [
([dummy_sample((8, ))], [(1, 8)]),
(dummy_sample((1, 8)), (1, 8)),
([dummy_sample((1, 8))], [(1, 8)]),
])
def test__call__if_seed_input_is_(self, seed_input, expected, dense_model):
activation_maximization = ActivationMaximization(dense_model)
result = activation_maximization(MockScore(),
seed_input=seed_input,
input_modifiers=[],
regularizers=[Norm(10.)],
steps=3)
if type(expected) is list:
assert type(result) == list
result = result[0]
expected = expected[0]
assert result.shape == expected

@pytest.mark.parametrize("input_modifiers,regularizers,expectation", [
([Jitter(), Rotate2D()], [TotalVariation2D(), Norm()], pytest.raises(ValueError)),
([Jitter()], [], pytest.raises(ValueError)),
([Rotate2D()], [], pytest.raises(ValueError)),
([], [TotalVariation2D()], pytest.raises(ValueError)),
([], [Norm()], does_not_raise()),
([], [], does_not_raise()),
])
def test__call__if_input_modifiers_or_regurarizers_are(self, input_modifiers, regularizers,
expectation, dense_model):
activation_maximization = ActivationMaximization(dense_model)
with expectation:
result = activation_maximization(MockScore(),
input_modifiers=input_modifiers,
regularizers=regularizers,
steps=3)
assert result.shape == (1, 8)
4 changes: 2 additions & 2 deletions tests/tf-keras-vis/saliency_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ def test__call__if_smoothing_is_active(self, smooth_samples, conv_model):

def test__call__if_model_has_only_dense_layers(self, dense_model):
saliency = Saliency(dense_model)
result = saliency(MockScore(), dummy_sample((3, )), keepdims=True)
assert result.shape == (1, 3)
result = saliency(MockScore(), dummy_sample((8, )), keepdims=True)
assert result.shape == (1, 8)

@pytest.mark.parametrize("score_class,modefier_enabled,clone_enabled,"
"batch_size,expectation", [
Expand Down
7 changes: 4 additions & 3 deletions tf_keras_vis/activation_maximization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from packaging.version import parse as version

from tf_keras_vis import ModelVisualization
from tf_keras_vis.utils import (check_steps, is_mixed_precision, listify, lower_precision_dtype)
from tf_keras_vis.utils.input_modifiers import Jitter, Rotate
from tf_keras_vis.utils import (check_steps, is_mixed_precision, listify,
lower_precision_dtype)
from tf_keras_vis.utils.input_modifiers import Jitter, Rotate2D
from tf_keras_vis.utils.regularizers import Norm, TotalVariation2D

if version(tf.version.VERSION) >= version("2.4.0"):
Expand All @@ -21,7 +22,7 @@ def __call__(
score,
seed_input=None,
input_range=(0, 255),
input_modifiers=[Jitter(jitter=8), Rotate(degree=3)],
input_modifiers=[Jitter(jitter=8), Rotate2D(degree=3)],
regularizers=[TotalVariation2D(weight=1.),
Norm(weight=1., p=2)],
steps=200,
Expand Down
25 changes: 20 additions & 5 deletions tf_keras_vis/utils/input_modifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import numpy as np
import tensorflow as tf
from deprecated import deprecated
from scipy.ndimage import rotate


Expand Down Expand Up @@ -32,32 +33,46 @@ def __init__(self, jitter=8):

def __call__(self, seed_input):
ndim = len(seed_input.shape)
if ndim < 3:
raise ValueError("The dimensions of seed_input must be 3 or more "
f"(batch_size, ..., channels), but was {ndim}")
seed_input = tf.roll(seed_input,
shift=tuple(np.random.randint(-self.jitter, self.jitter, ndim - 2)),
axis=tuple(range(ndim)[1:-1]))
return seed_input


class Rotate(InputModifier):
def __init__(self, degree=3.):
class Rotate2D(InputModifier):
def __init__(self, degree=3.0):
"""Implements an input modifier that introduces random rotation.
Rotate has been shown to produce crisper activation maximization images.
# Arguments:
degree: Integer or float. The amount of rotation to apply.
"""
self.rg = float(degree)
self.degree = float(degree)

def __call__(self, seed_input):
ndim = len(seed_input.shape)
if ndim != 4:
raise ValueError("seed_input shape must be (batch_size, height, width, channels),"
f" but was {seed_input.shape}")
if tf.is_tensor(seed_input):
seed_input = seed_input.numpy()
if seed_input.dtype == np.float16:
seed_input = seed_input.astype(np.float32)
seed_input = rotate(seed_input,
np.random.uniform(-self.rg, self.rg),
np.random.uniform(-self.degree, self.degree),
axes=tuple(range(len(seed_input.shape))[1:-1]),
reshape=False,
mode='nearest',
order=1,
prefilter=True)
return tf.constant(seed_input)
seed_input = tf.constant(seed_input)
return seed_input


@deprecated(version='0.6.2', reason="Please use Rotate2D class instead of Rotate class.")
class Rotate(Rotate2D):
def __init__(self, degree=3.0):
super().__init__(degree=3.0) # pragma: no cover
4 changes: 2 additions & 2 deletions tf_keras_vis/utils/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@


def mock_dense_model():
inputs = Input((3, ), name='input-1')
x = Dense(5, activation='relu', name='dense-1')(inputs)
inputs = Input((8, ), name='input-1')
x = Dense(6, activation='relu', name='dense-1')(inputs)
x = Dense(2, activation='softmax', name='dense-2')(x)
return Model(inputs=inputs, outputs=x)

Expand Down

0 comments on commit a96f632

Please sign in to comment.