Skip to content

Commit

Permalink
0.0.10 release
Browse files Browse the repository at this point in the history
  • Loading branch information
gallettilance committed Nov 15, 2024
1 parent 69ff2ba commit 70d1da2
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 96 deletions.
37 changes: 13 additions & 24 deletions kviz/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
import tensorflow.keras as keras


COLORS = np.array(['purple', 'blue'])
tuples = list(zip(
map(plt.Normalize(0, 1), [0, .5, 1]),
[COLORS[0], 'white', COLORS[1]])
)
CMAP = LinearSegmentedColormap.from_list("", tuples, 100)


class Visualizer():
"""
Class for creating and rendering visualization of Keras
Expand Down Expand Up @@ -156,17 +164,8 @@ def _get_int_models(self):

for l in range(len(self.model.layers)):
int_model = keras.Sequential()
inp = self.model.layers[0]._input_shape_arg[0]

for prev_layer in range(l + 1):
int_layer = self.model.layers[prev_layer]
int_model.add(keras.layers.Dense(
int_layer.units,
input_dim=inp,
activation=int_layer.activation)
)
int_model.layers[prev_layer].set_weights(int_layer.get_weights())
inp = int_layer.units
int_model.add(self.model.layers[prev_layer])
int_model.compile(loss=self.model.loss)
intermediate_models.append(int_model)

Expand Down Expand Up @@ -228,14 +227,10 @@ def _snap_decision_boundary(self, X, Y, filename):
np.arange(y_min, y_max, h))
meshData = np.c_[xx.ravel(), yy.ravel()]

# TODO catch max number of classes
colors = np.array([x for x in 'bgrcmyk'])

fig, ax = plt.subplots(frameon=False)
ax.scatter(X[:, 0], X[:, 1], color=colors[Y].tolist(), s=100, alpha=.9)
Z = self.model.predict(meshData)
Z = np.array([int(round(z[0])) for z in Z]).reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=.5, cmap=plt.cm.Paired)
ax.scatter(X[:, 0], X[:, 1], color=COLORS[Y].tolist(), s=100, alpha=.9)
Z = self.model.predict(meshData).reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=.4, cmap=CMAP)
fig.savefig(filename + '.png')
plt.close()

Expand Down Expand Up @@ -342,7 +337,7 @@ def fit(self, X, Y, snap_freq=10, filename='decision_boundary', duration=1000, *

for _ in range(int(epochs / snap_freq)):
self.model.fit(X, Y, epochs=snap_freq, **kwargs)
# self._int_models = self._get_int_models() TODO: make this function more efficient
self._int_models = self._get_int_models() # TODO: make this function more efficient
images.append(im.fromarray(self._snap_decision_boundary(X, Y, filename)))

self._convert_gif(images, filename, duration)
Expand All @@ -369,12 +364,6 @@ def view_activations_for(self, X, filename='activations', duration=1000, x_color
None
"""

cvals = [0, .5, 1]
colors = ['purple', 'white', 'blue']
norm = plt.Normalize(min(cvals), max(cvals))
tuples = list(zip(map(norm, cvals), colors))
CMAP = LinearSegmentedColormap.from_list("", tuples, 100)

cvals = [0, 1]
colors = ['white', 'green']
norm = plt.Normalize(min(cvals), max(cvals))
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
setup(
name='kviz',
packages=['kviz'],
version='0.0.9',
version='0.0.10',
description='A Library for visualizing keras neural networks',
install_requires=[
'tensorflow',
Expand Down
79 changes: 8 additions & 71 deletions tests/test_visualizer.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import sklearn.datasets as datasets

from kviz.visualizer import Visualizer


def test_dense_no_input():
def test_render():
ACTIVATION = "sigmoid"
model = keras.models.Sequential()
model.add(layers.Dense(2, input_dim=2))
model.add(layers.Dense(1, activation=ACTIVATION))
model.compile(loss="binary_crossentropy")

dg = Visualizer(model)
dg.render(filename='test_no_input')
dg.render(filename='test_render')
g1 = dg.get_graph()
dg.set_graph(g1)
dg.get_graph()


def test_dense_input_xor():
def test_view_activations_for():
ACTIVATION = "sigmoid"
model = keras.models.Sequential()
model.add(layers.Dense(3, input_dim=2, activation=ACTIVATION))
Expand All @@ -35,74 +33,12 @@ def test_dense_input_xor():
[1, 1]])
Y = np.array([x[0] ^ x[1] for x in X])

model.fit(X, Y, batch_size=4, epochs=1000)

colors = np.array(['b', 'g'])
fig, ax = plt.subplots()
ax.scatter(X[:, 0], X[:, 1], color=colors[Y].tolist(), s=50, alpha=0.8)

h = .02
x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
meshData = np.c_[xx.ravel(), yy.ravel()]

Z = model.predict(meshData)
Z = np.array([0 if x < .5 else 1 for x in Z])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=.3, cmap=plt.cm.Paired)
ax.axis('off')

fig.savefig("test_model_xor.png")

dg = Visualizer(model)
dg.view_activations_for(X, filename='test_input_xor')


def test_dense_input_line():
ACTIVATION = "sigmoid"
model = keras.models.Sequential()
model.add(layers.Dense(3, input_dim=2, activation=ACTIVATION))
model.add(layers.Dense(1, activation=ACTIVATION))
model.compile(loss="binary_crossentropy")

t, _ = datasets.make_blobs(n_samples=50, centers=[[.5, .5]], cluster_std=.1, random_state=1)
X = np.array(t)
Y = np.array([1 if x[0] - x[1] >= 0 else 0 for x in X])

model.fit(X, Y, batch_size=50, epochs=100)

# see which nodes activate for a given class
X0 = X[X[:, 0] - X[:, 1] <= 0]
X1 = X[X[:, 0] - X[:, 1] >= 0]

X = np.concatenate((X0, X1), axis=0)

fig, ax = plt.subplots()
ax.scatter(X0[:, 0], X0[:, 1], color='b', s=10, alpha=0.8)
ax.scatter(X1[:, 0], X1[:, 1], facecolors='none', edgecolors='black')

h = .01
x_min, x_max = 0, 1
y_min, y_max = 0, 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
meshData = np.c_[xx.ravel(), yy.ravel()]

Z = model.predict(meshData)
Z = np.array([0 if x < .5 else 1 for x in Z])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=.3, cmap=plt.cm.Paired)
ax.axis('off')

fig.savefig("test_model_line.png")

dg = Visualizer(model)
dg.view_activations_for(X, filename='test_input_line', duration=300)
dg.fit(X, Y, 10, 'test_view_activations_for_decision_boundary', 100, epochs=2000)
dg.view_activations_for(X, filename='test_view_activations_for')


def test_animate_learning():
def test_fit():
ACTIVATION = "sigmoid"

def custom_activation(x):
Expand All @@ -118,4 +54,5 @@ def custom_activation(x):
Y = np.array([1 if x[0]**2 + x[1]**2 >= 1 else 0 for x in X])

dg = Visualizer(model)
dg.fit(X, Y, filename='test_animate', verbose=0, batch_size=50)
dg.fit(X, Y, 10, 'test_fit', 100, epochs=200, verbose=0, batch_size=50)
dg.view_activations_for(X, 'test_fit_activations', 100)

0 comments on commit 70d1da2

Please sign in to comment.