Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
keisen committed Jan 7, 2020
1 parent 2d8ff72 commit 647494f
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 32 deletions.
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
dist: trusty
dist: xenial
language: python
python:
- "3.5"
- "3.6"
- "3.7"
env:
- TF_VERSION=2.0.0
install:
- pip install -U pip
- pip install -U -e .[cpu,develop,examples]
- pip install -U --force-reinstall -e .[development,examples] tensorflow==$TF_VERSION
script:
- PYTHONPATH=$PWD:$PYTHONPATH py.test
- jupyter-nbconvert --ExecutePreprocessor.timeout=86400 --to notebook --execute --stdout examples/attention.ipynb > /dev/null
- jupyter-nbconvert --ExecutePreprocessor.timeout=86400 --to notebook --execute --stdout examples/activation_maximization.ipynb > /dev/null
- jupyter-nbconvert --ExecutePreprocessor.timeout=86400 --to notebook --execute examples/attention.ipynb
32 changes: 11 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# tf-keras-vis
[![Build Status](https://travis-ci.org/keisen/tf-keras-vis.svg?branch=master)](https://travis-ci.org/keisen/tf-keras-vis)
[![Maintainability](https://api.codeclimate.com/v1/badges/4c9aa58cc5571aedac69/maintainability)](https://codeclimate.com/github/keisen/tf-keras-vis/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/4c9aa58cc5571aedac69/test_coverage)](https://codeclimate.com/github/keisen/tf-keras-vis/test_coverage)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

tf-keras-vis is a visualization toolkit for debugging Keras models with Tensorflow 2.0, but not original Keras.

The features of tf-keras-vis are based on [keras-vis](https://github.com/raghakot/keras-vis), but tf-keras-vis's APIs doesn't have compatibility with keras-vis's because, instead of getting it, we prioritized to get following features.
Expand All @@ -16,44 +21,29 @@ And then we will add some algorisms such as below.

## Requirements

* Python 3.6+
* (tensorflow or tensorflow-gpu) >= 2.0
* Python 3.5, 3.6 or 3.7
* tensorflow(or tensorflow-gpu)==2.0.0


## Installation

* PyPI

```bash
$ pip install tf-keras-vis
$ pip install tf-keras-vis tensorflow
```

* Sources
* Docker

```bash
$ cd tf-keras-vis
$ pip install -e .
```

Or

```bash
$ cd tf-keras-vis
$ python setup.py install
$ docker pull keisen/tf-keras-vis
```


## Usage

T.B.D.

For now, Please see [examples/activation_maximization.ipynb](https://github.com/keisen/tf-keras-vis/blob/master/examples/activation_maximization.ipynb) and [examples/attention.ipynb](https://github.com/keisen/tf-keras-vis/blob/master/examples/attention.ipynb).
When you want to run jupyter notebook, we recommend that install tf-keras-vis such as follow:

```bash
$ cd tf-keras-vis
$ pip install -e .[examples]
```
T.B.D.


## API Documentation
Expand Down
11 changes: 5 additions & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ description-file = README.md
[flake8]
exclude =
.git
.venv
__pycache__
dist/*
docker/*
examples/*
max-line-length = 100
max-complexity = 10
Expand All @@ -18,10 +18,10 @@ spaces_before_comment = 2
[isort]
skip =
.git
.venv
__pycache__
build
deprecated
dist
docker
examples

[pyls]
configurationSources = ['flake8']
Expand All @@ -31,13 +31,12 @@ addopts =
-v
--durations=10
--cache-clear
--cov=ml/
--cov=tf_keras_vis/
--cov-report=term-missing

[coverage:run]
omit =
tests/*
.venv/*
__pycache__/*

[coverage:report]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.5',
python_requires='>=3.5, <3.8',
install_requires=['numpy', 'scipy', 'imageio', 'pillow'],
extras_require={
'cpu': ['tensorflow>=2.0'],
Expand Down
37 changes: 37 additions & 0 deletions tests/tf-keras-vis/test__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import numpy as np
import pytest
import tensorflow as tf
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential

from tf_keras_vis import ModelVisualization


@pytest.fixture(scope="function", autouse=True)
def model():
return Sequential([Dense(5, input_shape=(3, )), Dense(2, activation='softmax')])


class MockVisualizer(ModelVisualization):
def __call__(self):
pass


def change_activation(model):
model.layers[-1].activation = tf.keras.activations.linear


def test__init__(model):
mock = MockVisualizer(model)
assert mock.model != model
assert np.array_equal(mock.model.get_weights()[0], model.get_weights()[0])

mock = MockVisualizer(model, change_activation)
assert mock.model != model
assert mock.model.layers[-1].activation == tf.keras.activations.linear
assert model.layers[-1].activation == tf.keras.activations.softmax

another_model = Sequential([Dense(5, input_shape=(3, ))])
mock = MockVisualizer(model, lambda m: another_model)
assert mock.model != model
assert mock.model == another_model
98 changes: 98 additions & 0 deletions tests/tf-keras-vis/test_activation_maximization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import numpy as np
import pytest
from tensorflow.keras.layers import Conv2D, Dense, Flatten, Concatenate, Input
from tensorflow.keras.models import Sequential, Model

from tf_keras_vis.activation_maximization import ActivationMaximization
from tf_keras_vis.utils.losses import SmoothedLoss
from tf_keras_vis.utils.callbacks import OptimizerCallback


class MockCallback(OptimizerCallback):
def on_begin(self):
self.on_begin_was_called = True

def __call__(self, i, values, grads, losses, model_outpus, **kwargs):
self.on_call_was_called = True

def on_end(self):
self.on_end_was_called = True


@pytest.fixture(scope="function", autouse=True)
def multiple_inputs_model():
a = Input(shape=(8, 8, 3))
b = Input(shape=(8, 8, 3))
c = Input(shape=(8, 8, 3))
x1 = Conv2D(5, 3, activation='relu')(a)
x2 = Conv2D(5, 3, activation='relu')(b)
x3 = Conv2D(5, 3, activation='relu')(c)
x = Concatenate()([x1, x2, x3])
x = Dense(3)(x)
return Model([a, b, c], [x])


@pytest.fixture(scope="function", autouse=True)
def cnn_model():
return _cnn_model()


def _cnn_model():
return Sequential([
Input(shape=(8, 8, 3)),
Conv2D(5, 3, activation='relu'),
Flatten(),
Dense(2, activation='softmax')
])


def test__call__if_loss_is_None(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
try:
activation_maximization(None, steps=1)
assert False
except ValueError:
assert True


def test__call__(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
result = activation_maximization(SmoothedLoss(1), steps=1)
assert result.shape == (1, 8, 8, 3)


def test__call__if_loss_is_list(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
result = activation_maximization([SmoothedLoss(1)], steps=1)
assert result.shape == (1, 8, 8, 3)


def test__call__with_seed_input(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
result = activation_maximization(SmoothedLoss(1),
seed_input=np.random.sample((8, 8, 3)),
steps=1)
assert result.shape == (1, 8, 8, 3)


def test__call__with_callback(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
mock = MockCallback()
result = activation_maximization(SmoothedLoss(1), steps=1, callbacks=mock)
assert result.shape == (1, 8, 8, 3)
assert mock.on_begin_was_called
assert mock.on_call_was_called
assert mock.on_end_was_called


def test__call__with_gradient_modifier(cnn_model):
activation_maximization = ActivationMaximization(cnn_model)
result = activation_maximization(SmoothedLoss(1), steps=1, gradient_modifier=lambda x: x)
assert result.shape == (1, 8, 8, 3)


def test__call__with_mutiple_inputs_model(multiple_inputs_model):
activation_maximization = ActivationMaximization(multiple_inputs_model)
result = activation_maximization(SmoothedLoss(1), steps=1, input_modifiers=None)
assert result[0].shape == (1, 8, 8, 3)
assert result[1].shape == (1, 8, 8, 3)
77 changes: 77 additions & 0 deletions tests/tf-keras-vis/test_gradcam.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import numpy as np
import pytest
from tensorflow.keras.layers import Conv2D, Dense, Flatten
from tensorflow.keras.models import Sequential

from tf_keras_vis.gradcam import Gradcam
from tf_keras_vis.utils.losses import SmoothedLoss


@pytest.fixture(scope="function", autouse=True)
def dense_model():
return Sequential(
[Dense(5, input_shape=(3, ), activation='relu'),
Dense(2, activation='softmax')])


@pytest.fixture(scope="function", autouse=True)
def cnn_model():
return Sequential([
Conv2D(5, 3, input_shape=(8, 8, 3), activation='relu'),
Flatten(),
Dense(2, activation='softmax')
])


def test__call__if_loss_is_None(cnn_model):
gradcam = Gradcam(cnn_model)
try:
gradcam(None, None)
assert False
except ValueError:
assert True


def test__call__if_seed_input_is_None(cnn_model):
gradcam = Gradcam(cnn_model)
try:
gradcam(SmoothedLoss(1), None)
assert False
except ValueError:
assert True


def test__call__if_seed_input_has_not_batch_dim(cnn_model):
gradcam = Gradcam(cnn_model)
result = gradcam(SmoothedLoss(1), np.random.sample((8, 8, 3)))
assert result.shape == (1, 8, 8)


def test__call__(cnn_model):
gradcam = Gradcam(cnn_model)
result = gradcam(SmoothedLoss(1), np.random.sample((1, 8, 8, 3)))
assert result.shape == (1, 8, 8)


def test__call__if_penultimate_layer_is_None(cnn_model):
gradcam = Gradcam(cnn_model)
result = gradcam(SmoothedLoss(1), np.random.sample((1, 8, 8, 3)), penultimate_layer=None)
assert result.shape == (1, 8, 8)


def test__call__if_penultimate_layer_is_noexist_index(cnn_model):
gradcam = Gradcam(cnn_model)
try:
gradcam(SmoothedLoss(1), np.random.sample((1, 8, 8, 3)), penultimate_layer=100000)
assert False
except ValueError:
assert True


def test__call__if_penultimate_layer_is_noexist_name(cnn_model):
gradcam = Gradcam(cnn_model)
try:
gradcam(SmoothedLoss(1), np.random.sample((1, 8, 8, 3)), penultimate_layer='hoge')
assert False
except ValueError:
assert True
Loading

0 comments on commit 647494f

Please sign in to comment.