In [1]:
%tensorflow_version 1.x
#The default TensorFlow version in Colab switched from 1.x to 2.x on the 27th of March, 2020.
In [2]:
# Clone the entire repo.
!git clone https://github.com/tensorflow/tcav.git tcav
%cd tcav
!ls
In [3]:
%cd /content/tcav/tcav/tcav_examples/image_models/imagenet
%run download_and_make_datasets.py --source_dir=YOUR_FOLDER --number_of_images_per_folder=10 --number_of_random_folders=10
In [4]:
%cd /content/tcav
This notebook walks you through things you need to run TCAV.
Before running this notebook, run the following to download all the data.
cd tcav/tcav_examples/image_models/imagenet
python download_and_make_datasets.py --source_dir=YOUR_PATH --number_of_images_per_folder=50 --number_of_random_folders=3
In high level, you need:
pip install the tcav and tensorflow packages (or tensorflow-gpu if using GPU)
In [0]:
%load_ext autoreload
%autoreload 2
In [0]:
import tcav.activation_generator as act_gen
import tcav.cav as cav
import tcav.model as model
import tcav.tcav as tcav
import tcav.utils as utils
import tcav.utils_plot as utils_plot # utils_plot requires matplotlib
import os
import tensorflow as tf
and tell TCAV where they are.
source_dir: where images of concepts, target class and random images (negative samples when learning CAVs) live. Each should be a sub-folder within this directory.
Note that random image directories can be in any name. In this example, we are using random500_0, random500_1,.. for an arbitrary reason.
You need roughly 50-200 images per concept and target class (10-20 pictures also tend to work, but 200 is pretty safe).
cav_dir: directory to store CAVs (None if you don't want to store)
target, concept: names of the target class (that you want to investigate) and concepts (strings) - these are folder names in source_dir
bottlenecks: list of bottleneck names (intermediate layers in your model) that you want to use for TCAV. These names are defined in the model wrapper below.
In [8]:
# This is the name of your model wrapper (InceptionV3 and GoogleNet are provided in model.py)
model_to_run = 'GoogleNet'
# the name of the parent directory that results are stored (only if you want to cache)
project_name = 'tcav_class_test'
working_dir = '/content/tcav/tcav'
# where activations are stored (only if your act_gen_wrapper does so)
activation_dir = working_dir+ '/activations/'
# where CAVs are stored.
# You can say None if you don't wish to store any.
cav_dir = working_dir + '/cavs/'
# where the images live.
source_dir = '/content/tcav/tcav/tcav_examples/image_models/imagenet/YOUR_FOLDER'
bottlenecks = [ 'mixed4c'] # @param
utils.make_dir_if_not_exists(activation_dir)
utils.make_dir_if_not_exists(working_dir)
utils.make_dir_if_not_exists(cav_dir)
# this is a regularizer penalty parameter for linear classifier to get CAVs.
alphas = [0.1]
target = 'zebra'
concepts = ["dotted","striped","zigzagged"]
Next step is to tell TCAV how to communicate with your model. See model.GoogleNetWrapper_public for details.
You can define a subclass of ModelWrapper abstract class to do this. Let me walk you thru what each function does (tho they are pretty self-explanatory). This wrapper includes a lot of the functions that you already have, for example, get_prediction.
First, store your bottleneck tensors in self.bottlenecks_tensors as a dictionary. You only need bottlenecks that you are interested in running TCAV with. Similarly, fill in self.ends dictionary with input, logit and prediction tensors.
Get your loss tensor, and assigned it to self.loss. This is what TCAV uses to take directional derivatives.
While doing so, you would also want to set
self.y_input
this simply is a tensorflow place holder for the target index in the logit layer (e.g., 0 index for a dog, 1 for a cat). For multi-class classification, typically something like this works:
self.y_input = tf.placeholder(tf.int64, shape=[None])
For example, for a multiclass classifier, something like below would work.
# Construct gradient ops.
with g.as_default():
self.y_input = tf.placeholder(tf.int64, shape=[None])
self.pred = tf.expand_dims(self.ends['prediction'][0], 0)
self.loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
labels=tf.one_hot(self.y_input, len(self.labels)),
logits=self.pred))
self._make_gradient_tensors()
_make_gradient_tensors()
does what you expect - given the loss and bottleneck tensors defined above, it adds gradient tensors.
Get the mapping from labels (strings) to indice in the logit layer (int) in a dictionary format.
def id_to_label(self, idx)
def label_to_id(self, label)
Set your input image shape at self.image_shape
Set your model name to self.model_name
You are done with writing the model wrapper! I wrote two model wrapers, InceptionV3 and Googlenet.
sess: a tensorflow session.
In [9]:
%cp -av '/content/tcav/tcav/tcav_examples/image_models/imagenet/YOUR_FOLDER/mobilenet_v2_1.0_224' '/content/tcav/tcav/mobilenet_v2_1.0_224'
%rm '/content/tcav/tcav/tcav_examples/image_models/imagenet/YOUR_FOLDER/mobilenet_v2_1.0_224'
In [10]:
%cp -av '/content/tcav/tcav/tcav_examples/image_models/imagenet/YOUR_FOLDER/inception5h' '/content/tcav/tcav/inception5h'
%rm '/content/tcav/tcav/tcav_examples/image_models/imagenet/YOUR_FOLDER/inception5h'
In [11]:
sess = utils.create_session()
# GRAPH_PATH is where the trained model is stored.
GRAPH_PATH = "/content/tcav/tcav/inception5h/tensorflow_inception_graph.pb"
# LABEL_PATH is where the labels are stored. Each line contains one class, and they are ordered with respect to their index in
# the logit layer. (yes, id_to_label function in the model wrapper reads from this file.)
# For example, imagenet_comp_graph_label_strings.txt looks like:
# dummy
# kit fox
# English setter
# Siberian husky ...
LABEL_PATH = "/content/tcav/tcav/inception5h/imagenet_comp_graph_label_strings.txt"
mymodel = model.GoogleNetWrapper_public(sess,
GRAPH_PATH,
LABEL_PATH)
Lastly, you will implement a class of the ActivationGenerationInterface which TCAV uses to load example data for a given concept or target, call into your model wrapper and return activations. I pulled out this logic outside of mymodel because this step often takes the longest. By making it modular, you can cache your activations and/or parallelize your computations, as I have done in ActivationGeneratorBase.process_and_load_activations in activation_generator.py.
The process_and_load_activations method of the activation generator must return a dictionary of activations that has concept or target name as a first key, and the bottleneck name as a second key. So something like:
{concept1: {bottleneck1: [[0.2, 0.1, ....]]},
concept2: {bottleneck1: [[0.1, 0.02, ....]]},
target1: {bottleneck1: [[0.02, 0.99, ....]]}
In [0]:
act_generator = act_gen.ImageActivationGenerator(mymodel, source_dir, activation_dir, max_examples=100)
Let's do it.
num_random_exp: number of experiments to confirm meaningful concept direction. TCAV will search for this many folders named random500_0, random500_1, etc. You can alternatively set the random_concepts keyword to be a list of folders of random concepts. Run at least 10-20 for meaningful tests.
random_counterpart: as well as the above, you can optionally supply a single folder with random images as the "positive set" for statistical testing. Reduces computation time at the cost of less reliable random TCAV scores.
In [13]:
tf.logging.set_verbosity(0)
num_random_exp=10
## only running num_random_exp = 10 to save some time. The paper number are reported for 500 random runs.
mytcav = tcav.TCAV(sess,
target,
concepts,
bottlenecks,
act_generator,
alphas,
cav_dir=cav_dir,
num_random_exp=num_random_exp)#10)
print ('This may take a while... Go get coffee!')
results = mytcav.run(run_parallel=False)
print ('done!')
In [14]:
utils_plot.plot_results(results, num_random_exp=num_random_exp)