Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfixes/#58 #59

Merged
merged 3 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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="[email protected]",
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