def _make_digraph(self): """ Constructs the DiGraph """ graph = DiGraph(nodesep='1', ranksep='1') for l in range(len(self.model.layers)): layer = self.model.layers[l] if type(layer) != Dense: raise ValueError( "Unsupported Layer Type: %s Only Dense Layers are Supported", type(layer)) for n in range(0, layer.input_shape[1]): if l == 0: graph.add_node(unique_index(l, n), shape="circle", color="#3498db", label='') else: graph.add_node(unique_index(l, n), shape="circle", color="#2ecc71", label='') for h in range(0, layer.output_shape[1]): if l == len(self.model.layers) - 1: graph.add_node(unique_index(l + 1, h), shape="circle", color="#3498db", label='') graph.add_edge(unique_index(l, n), unique_index(l + 1, h), color="#B20000") return graph
def test_dense_input_xor_customized(): ACTIVATION = "sigmoid" model = keras.models.Sequential() model.add(layers.Dense(2, input_dim=2, activation=ACTIVATION)) model.add(layers.Dense(1, activation=ACTIVATION)) model.compile(loss="binary_crossentropy") X = np.array([[0, 0], [0, 1], [1, 0], [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_customized.png") dg = DenseGraph(model) the_graph = dg.get_graph() for l in range(len(model.layers)): layer = model.layers[l] for n in range(0, layer.input_shape[1]): set_node_attributes( the_graph, { unique_index(l, n): { 'shape': "diamond", 'color': "#00ff00", 'label': "" } }) for h in range(0, layer.output_shape[1]): if l == len(model.layers) - 1: set_node_attributes( the_graph, { unique_index(l + 1, h): { 'shape': "square", 'color': "#ff0000", 'label': "" } }) set_edge_attributes( the_graph, { (unique_index(l, n), unique_index(l + 1, h)): { 'color': "#0000ff" } }) dg.set_graph(the_graph) dg.animate_activations(X, filename='test_input_xor_customized', x_color="#FF0000", x_marker="^")
def test_dense_input_line_customized(): 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_customized.png") dg = DenseGraph(model) # test: each node is different the_graph = dg.get_graph() for l in range(len(model.layers)): layer = model.layers[l] for n in range(0, layer.input_shape[1]): set_node_attributes( the_graph, { unique_index(l, n): { 'shape': get_random_shape(), 'color': get_random_color(), 'label': "" } }) for h in range(0, layer.output_shape[1]): if l == len(model.layers) - 1: set_node_attributes( the_graph, { unique_index(l + 1, h): { 'shape': get_random_shape(), 'color': get_random_color(), 'label': "" } }) set_edge_attributes( the_graph, { (unique_index(l, n), unique_index(l + 1, h)): { 'color': get_random_color() } }) dg.set_graph(the_graph) dg.animate_activations(X, filename='test_input_line_customized', duration=300)
def test_dense_input_xor_customized_alternative(): """ Different from test_dense_input_xor_customized(), this function does not loop through the model. """ ACTIVATION = "sigmoid" model = keras.models.Sequential() model.add(layers.Dense(2, input_dim=2, activation=ACTIVATION)) model.add(layers.Dense(1, activation=ACTIVATION)) model.compile(loss="binary_crossentropy") X = np.array([[0, 0], [0, 1], [1, 0], [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_customized.png") dg = DenseGraph(model) the_graph = dg.get_graph() # the input has a shape of 2, same as the inner dense layer # set the input nodes first l = 0 for n in range(2): # a shape of 2 set_node_attributes( the_graph, { unique_index(l, n): { 'shape': "diamond", 'color': "#00ff00", 'label': "" } }) # set the inner dense layer then. In this case, there is only 1 inner dense layer. l = 1 for n in range(2): # a shape of 2 set_node_attributes( the_graph, { unique_index(l, n): { 'shape': "diamond", 'color': "#00ff00", 'label': "" } }) # set the output dense layer, which has a shape of 1 l = 2 set_node_attributes( the_graph, { unique_index(l, 0): { # the index 'shape': "square", 'color': "#ff0000", 'label': "" } }) dg.set_graph(the_graph) dg.animate_activations(X, filename='test_input_xor_customized_alternative', x_color="#FF0000", x_marker="^")
def animate_regression(self, X, filename='activations', duration=1000, x_color="#3498db", x_marker="o", rounded=True, roundedN=2): """ Creates an animation of the graph activated by each data point, used for regression Parameters: X : ndarray input to a Keras model filename : str name of file to which visualization will be saved duration : int duration in ms between images in GIF x_color: str. the color (in hex form) of the points in the pyplot graph x_marker: str. the shape of the points in the pyplot graph rounded: bool. whether to round the values roundedN: int the number of decimal places for the rounded values; will be ignored if rounded is false Returns: None """ network_images = [] input_images = [] color_maps = {} predictions = [X] for i in range(len(self._int_models)): predictions.append(self._int_models[i].predict(X)) predictions.append(self.model.predict(X)) for i in range(len(X)): for l in range(len(self.model.layers)): layer = self.model.layers[l] layerVals = predictions[l][i] vmax = max(layerVals) # multiple here to make the difference of color more obvious norm = Normalize(vmin=min(layerVals), vmax=vmax) for n in range(0, layer.input_shape[1]): act = predictions[l][i][n] index = unique_index(l, n) the_color_map = get_or_create_colormap_with_dict( self._graph.nodes[index]["color"], color_maps) if rounded: label = str(round(act, roundedN)) else: label = str(act) if l == 0: # since this is regression, the first layer is always the input data and they do not have colors set_node_attributes( self._graph, { index: { 'label': label, 'fixedsize': True # important for graphviz } }) else: if act == 0: # no color for 0 color = "#ffffff" else: # minus here, so the higher value has a darker color color = str( rgb2hex(the_color_map(norm(vmax - act)))) set_node_attributes( self._graph, { index: { 'label': label, 'style': 'filled', 'color': color, 'fixedsize': True } }) if l == len(self.model.layers ) - 1: # need to additionally show the output network_images.append(self._snap(filename)) self._update_input_images_for_regression( input_images, [i], X, predictions[-1], x_color, x_marker) for h in range(0, layer.output_shape[1]): act = predictions[l + 1][i][h] if rounded: label = str(round(act, roundedN)) else: label = str(act) index = unique_index(l + 1, h) set_node_attributes( self._graph, {index: { 'label': label, 'fixedsize': True }}) network_images.append(self._snap(filename)) self._update_input_images_for_regression( input_images, [i], X, predictions[-1], x_color, x_marker) self._reset() if len(X[0]) in [1, 2]: self._stack_gifs(network_images, input_images, filename, duration=duration) else: self._convert_gif(network_images, filename, duration) return
def animate_activations(self, X, filename='activations', duration=1000, x_color="#3498db", x_marker="o"): """ Creates an animation of the graph activated by each data point Parameters: X : ndarray input to a Keras model filename : str name of file to which visualization will be saved duration : int duration in ms between images in GIF x_color: str. the color (in hex form) of the points in the pyplot graph x_marker: str. the shape of the points in the pyplot graph Returns: None """ network_images = [] input_images = [] predictions = [X] for i in range(len(self._int_models)): predictions.append(self._int_models[i].predict(X)) predictions.append(self.model.predict(X)) for i in range(len(X)): for l in range(len(self.model.layers)): layer = self.model.layers[l] layerVals = predictions[l][i] norm = Normalize(vmin=min(layerVals), vmax=max(layerVals)) color_maps = {} for n in range(0, layer.input_shape[1]): act = predictions[l][i][n] index = unique_index(l, n) the_color_map = get_or_create_colormap_with_dict( self._graph.nodes[index]["color"], color_maps) if l == 0: set_node_attributes( self._graph, { index: { 'style': 'filled', 'color': str( rgb2hex(the_color_map(norm(act)))) } }) if int(act) == act: set_node_attributes(self._graph, {index: { 'label': str(act) }}) else: set_node_attributes( self._graph, { index: { 'style': 'filled', 'color': str( rgb2hex(the_color_map(norm(act)))) } }) for h in range(0, layer.output_shape[1]): if l == len(self.model.layers) - 1: act = predictions[l + 1][i][h] index = unique_index(l + 1, h) the_color_map = get_or_create_colormap_with_dict( self._graph.nodes[index]["color"], color_maps) set_node_attributes( self._graph, { index: { 'label': str(int(round(act))), 'style': 'filled', 'color': str( rgb2hex(the_color_map(norm(act)))) } }) network_images.append(self._snap(filename)) input_images.append( self._snap_X([i], X, filename, x_color=x_color, x_marker=x_marker)) self._reset() self._stack_gifs(network_images, input_images, filename, duration=duration) return