Documenting

parent f9965fd8
...@@ -51,19 +51,21 @@ class TextDataShuffler(BaseDataShuffler): ...@@ -51,19 +51,21 @@ class TextDataShuffler(BaseDataShuffler):
batch_size=batch_size batch_size=batch_size
) )
# TODO: very bad solution to deal with bob.shape images an tf shape images
self.bob_shape = tuple([input_shape[2]] + list(input_shape[0:2]))
def load_from_file(self, file_name, shape): def load_from_file(self, file_name, shape):
d = bob.io.base.load(file_name) d = bob.io.base.load(file_name)
#import ipdb; ipdb.set_trace(); if d.shape[0] != 3 and self.input_shape[2] != 3: # GRAY SCALE IMAGE
if len(d.shape) == 2:
data = numpy.zeros(shape=(d.shape[0], d.shape[1], 1)) data = numpy.zeros(shape=(d.shape[0], d.shape[1], 1))
data[:, :, 0] = d data[:, :, 0] = d
data = self.rescale(data) data = self.rescale(data)
else: else:
d = self.rescale(d)
data = self.bob2skimage(d) data = self.bob2skimage(d)
return data return data
def bob2skimage(self, bob_image): def bob2skimage(self, bob_image):
""" """
Convert bob color image to the skcit image Convert bob color image to the skcit image
...@@ -71,9 +73,9 @@ class TextDataShuffler(BaseDataShuffler): ...@@ -71,9 +73,9 @@ class TextDataShuffler(BaseDataShuffler):
skimage = numpy.zeros(shape=(bob_image.shape[1], bob_image.shape[2], 3)) skimage = numpy.zeros(shape=(bob_image.shape[1], bob_image.shape[2], 3))
skimage[:,:,0] = bob_image[0,:,:] #Copying red skimage[:, :, 0] = bob_image[0, :, :] #Copying red
skimage[:,:,1] = bob_image[1,:,:] #Copying green skimage[:, :, 1] = bob_image[1, :, :] #Copying green
skimage[:,:,2] = bob_image[2,:,:] #Copying blue skimage[:, :, 2] = bob_image[2, :, :] #Copying blue
return skimage return skimage
...@@ -102,7 +104,9 @@ class TextDataShuffler(BaseDataShuffler): ...@@ -102,7 +104,9 @@ class TextDataShuffler(BaseDataShuffler):
Reescale a single sample with input_shape Reescale a single sample with input_shape
""" """
if self.input_shape != data.shape: #if self.input_shape != data.shape:
if self.bob_shape != data.shape:
# TODO: Implement a better way to do this reescaling # TODO: Implement a better way to do this reescaling
# If it is gray scale # If it is gray scale
if self.input_shape[2] == 1: if self.input_shape[2] == 1:
...@@ -111,8 +115,18 @@ class TextDataShuffler(BaseDataShuffler): ...@@ -111,8 +115,18 @@ class TextDataShuffler(BaseDataShuffler):
bob.ip.base.scale(copy, dst) bob.ip.base.scale(copy, dst)
dst = numpy.reshape(dst, self.input_shape) dst = numpy.reshape(dst, self.input_shape)
else: else:
dst = numpy.resize(data, self.input_shape) # Scaling with numpy, because bob is c,w,d instead of w,h,c #dst = numpy.resize(data, self.bob_shape) # Scaling with numpy, because bob is c,w,d instead of w,h,c
#bob.ip.base.scale(data, dst) dst = numpy.zeros(shape=self.bob_shape)
# TODO: LAME SOLUTION
if data.shape[0] != 3: # GRAY SCALE IMAGES IN A RGB DATABASE
step_data = numpy.zeros(shape=(3, data.shape[0], data.shape[1]))
step_data[0, ...] = data[:, :]
step_data[1, ...] = data[:, :]
step_data[2, ...] = data[:, :]
data = step_data
bob.ip.base.scale(data, dst)
return dst return dst
else: else:
...@@ -176,4 +190,3 @@ class TextDataShuffler(BaseDataShuffler): ...@@ -176,4 +190,3 @@ class TextDataShuffler(BaseDataShuffler):
data_n *= self.scale_value data_n *= self.scale_value
return data_a, data_p, data_n return data_a, data_p, data_n
...@@ -59,9 +59,8 @@ class Conv2D(Layer): ...@@ -59,9 +59,8 @@ class Conv2D(Layer):
name="w_" + str(self.name) name="w_" + str(self.name)
) )
if self.activation is not None: self.b = self.bias_initialization(shape=[self.filters],
self.b = self.bias_initialization(shape=[self.filters], name="b_" + str(self.name) + "bias")
name="b_" + str(self.name) + "bias")
def get_graph(self): def get_graph(self):
...@@ -69,9 +68,8 @@ class Conv2D(Layer): ...@@ -69,9 +68,8 @@ class Conv2D(Layer):
conv2d = tf.nn.conv2d(self.input_layer, self.W, strides=[1, 1, 1, 1], padding='SAME') conv2d = tf.nn.conv2d(self.input_layer, self.W, strides=[1, 1, 1, 1], padding='SAME')
if self.activation is not None: if self.activation is not None:
non_linear_conv2d = self.activation(tf.nn.bias_add(conv2d, self.b)) output = self.activation(tf.nn.bias_add(conv2d, self.b))
output = non_linear_conv2d
else: else:
output = conv2d output = tf.nn.bias_add(conv2d, self.b)
return output return output
...@@ -10,11 +10,11 @@ from .Layer import Layer ...@@ -10,11 +10,11 @@ from .Layer import Layer
class MaxPooling(Layer): class MaxPooling(Layer):
def __init__(self, name, shape=[1, 2, 2, 1]): def __init__(self, name, shape=[1, 2, 2, 1], activation=None):
""" """
Constructor Constructor
""" """
super(MaxPooling, self).__init__(name, use_gpu=False) super(MaxPooling, self).__init__(name, use_gpu=False, activation=activation)
self.shape = shape self.shape = shape
def create_variables(self, input_layer): def create_variables(self, input_layer):
...@@ -25,4 +25,7 @@ class MaxPooling(Layer): ...@@ -25,4 +25,7 @@ class MaxPooling(Layer):
with tf.name_scope(str(self.name)): with tf.name_scope(str(self.name)):
output = tf.nn.max_pool(self.input_layer, ksize=self.shape, strides=[1, 1, 1, 1], padding='SAME') output = tf.nn.max_pool(self.input_layer, ksize=self.shape, strides=[1, 1, 1, 1], padding='SAME')
if self.activation is not None:
output = self.activation(output)
return output return output
...@@ -4,23 +4,6 @@ ...@@ -4,23 +4,6 @@
# @date: Wed 11 May 2016 09:39:36 CEST # @date: Wed 11 May 2016 09:39:36 CEST
""" """
Class that creates the architecture presented in the paper:
Chopra, Sumit, Raia Hadsell, and Yann LeCun. "Learning a similarity metric discriminatively, with application to
face verification." 2005 IEEE Computer Society Conference on Computer Vision and Pattern Recognition (CVPR'05). Vol. 1. IEEE, 2005.
This is modifield version of the original architecture.
It is inspired on https://gitlab.idiap.ch/bob/xfacereclib.cnn/blob/master/lua/network.lua
-- C1 : Convolutional, kernel = 7x7 pixels, 15 feature maps
-- M2 : MaxPooling, 2x2
-- HT : Hard Hyperbolic Tangent
-- C3 : Convolutional, kernel = 6x6 pixels, 45 feature maps
-- M4 : MaxPooling, 4x3
-- HT : Hard Hyperbolic Tangent
-- R : Reshaping layer HT 5x5 => 25 (45 times; once for each feature map)
-- L5 : Linear 25 => 250
""" """
...@@ -33,7 +16,49 @@ from bob.learn.tensorflow.initialization import Constant ...@@ -33,7 +16,49 @@ from bob.learn.tensorflow.initialization import Constant
class Chopra(SequenceNetwork): class Chopra(SequenceNetwork):
"""Class that creates the architecture presented in the paper:
Chopra, Sumit, Raia Hadsell, and Yann LeCun. "Learning a similarity metric discriminatively, with application to
face verification." 2005 IEEE Computer Society Conference on Computer Vision and Pattern Recognition (CVPR'05). Vol. 1. IEEE, 2005.
This is modifield version of the original architecture.
It is inspired on https://gitlab.idiap.ch/bob/xfacereclib.cnn/blob/master/lua/network.lua
-- C1 : Convolutional, kernel = 7x7 pixels, 15 feature maps
-- M2 : MaxPooling, 2x2
-- HT : Hard Hyperbolic Tangent
-- C3 : Convolutional, kernel = 6x6 pixels, 45 feature maps
-- M4 : MaxPooling, 4x3
-- HT : Hard Hyperbolic Tangent
-- R : Reshaping layer HT 5x5 => 25 (45 times; once for each feature map)
-- L5 : Linear 25 => 250
**Parameters**
conv1_kernel_size:
conv1_output:
pooling1_size:
conv2_kernel_size:
conv2_output:
pooling2_size
fc1_output:
seed:
"""
def __init__(self, def __init__(self,
conv1_kernel_size=7, conv1_kernel_size=7,
conv1_output=15, conv1_output=15,
...@@ -51,42 +76,25 @@ class Chopra(SequenceNetwork): ...@@ -51,42 +76,25 @@ class Chopra(SequenceNetwork):
seed=10, seed=10,
use_gpu=False): use_gpu=False):
"""
Create all the necessary variables for this CNN
**Parameters**
conv1_kernel_size=5,
conv1_output=32,
pooling1_size=[1, 2, 2, 1],
conv2_kernel_size=5,
conv2_output=64,
pooling2_size=[1, 4, 3, 1],
fc1_output=50,
seed = 10
"""
super(Chopra, self).__init__(default_feature_layer=default_feature_layer, super(Chopra, self).__init__(default_feature_layer=default_feature_layer,
use_gpu=use_gpu) use_gpu=use_gpu)
self.add(Conv2D(name="conv1", kernel_size=conv1_kernel_size, self.add(Conv2D(name="conv1", kernel_size=conv1_kernel_size,
filters=conv1_output, filters=conv1_output,
activation=tf.nn.tanh, activation=None,
weights_initialization=Xavier(seed=seed, use_gpu=self.use_gpu), weights_initialization=Xavier(seed=seed, use_gpu=self.use_gpu),
bias_initialization=Constant(use_gpu=self.use_gpu) bias_initialization=Constant(use_gpu=self.use_gpu)
)) ))
self.add(MaxPooling(name="pooling1", shape=pooling1_size)) self.add(MaxPooling(name="pooling1", shape=pooling1_size, activation=tf.nn.tanh))
self.add(Conv2D(name="conv2", kernel_size=conv2_kernel_size, self.add(Conv2D(name="conv2", kernel_size=conv2_kernel_size,
filters=conv2_output, filters=conv2_output,
activation=tf.nn.tanh, activation=None,
weights_initialization=Xavier(seed=seed, use_gpu=self.use_gpu), weights_initialization=Xavier(seed=seed, use_gpu=self.use_gpu),
bias_initialization=Constant(use_gpu=self.use_gpu) bias_initialization=Constant(use_gpu=self.use_gpu)
)) ))
self.add(MaxPooling(name="pooling2", shape=pooling2_size)) self.add(MaxPooling(name="pooling2", shape=pooling2_size, activation=tf.nn.tanh))
self.add(FullyConnected(name="fc1", output_dim=fc1_output, self.add(FullyConnected(name="fc1", output_dim=fc1_output,
activation=None, activation=None,
......
...@@ -15,7 +15,36 @@ from bob.learn.tensorflow.initialization import Constant ...@@ -15,7 +15,36 @@ from bob.learn.tensorflow.initialization import Constant
class MLP(SequenceNetwork): class MLP(SequenceNetwork):
"""An MLP is a representation of a Multi-Layer Perceptron.
This implementation is feed-forward and fully-connected.
The implementation allows setting a global and the output activation functions.
References to fully-connected feed-forward networks: Bishop's Pattern Recognition and Machine Learning, Chapter 5. Figure 5.1 shows what is programmed.
MLPs normally are multi-layered systems, with 1 or more hidden layers.
**Parameters**
output_shape: number of neurons in the output.
hidden_layers: :py:class:`list` that contains the amount of hidden layers, where each element is the number of neurons
hidden_activation: Activation function of the hidden layers. Possible values can be seen
`here <https://www.tensorflow.org/versions/r0.11/api_docs/python/nn.html#activation-functions>`_.
If you set to ``None``, the activation will be linear.
output_activation: Activation of the output layer. If you set to `None`, the activation will be linear
weights_initialization: How you will initialize the neurons.
See :py:mod:`bob.learn.tensorflow.initialization`.
bias_initialization: How you will initialize the biases.
See :py:mod:`bob.learn.tensorflow.initialization`.
use_gpu: If ``True`` uses the GPU in the computation
seed = 10
"""
def __init__(self, def __init__(self,
output_shape, output_shape,
hidden_layers=[10], hidden_layers=[10],
...@@ -24,16 +53,6 @@ class MLP(SequenceNetwork): ...@@ -24,16 +53,6 @@ class MLP(SequenceNetwork):
weights_initialization=Xavier(), weights_initialization=Xavier(),
bias_initialization=Constant(), bias_initialization=Constant(),
use_gpu=False): use_gpu=False):
"""
Create all the necessary variables for this CNN
**Parameters**
output_shape: Shape of the output
hidden_layers: List that contains the amount of hidden layers, where each element is the number of neurons
hidden_activation: Activation function of the hidden layer. If you set to `None`, the activation will be linear
output_activation: Activation of the output layer. If you set to `None`, the activation will be linear
seed = 10
"""
super(MLP, self).__init__(use_gpu=use_gpu) super(MLP, self).__init__(use_gpu=use_gpu)
if (not (isinstance(hidden_layers, list) or isinstance(hidden_layers, tuple))) or len(hidden_layers) == 0: if (not (isinstance(hidden_layers, list) or isinstance(hidden_layers, tuple))) or len(hidden_layers) == 0:
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch> # @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Thu 11 Aug 2016 09:39:36 CEST # @date: Thu 11 Aug 2016 09:39:36 CEST
"""
Class that creates the lenet architecture
"""
import tensorflow as tf import tensorflow as tf
import abc import abc
...@@ -19,29 +16,28 @@ from bob.learn.tensorflow.layers import Layer, MaxPooling, Dropout, Conv2D, Full ...@@ -19,29 +16,28 @@ from bob.learn.tensorflow.layers import Layer, MaxPooling, Dropout, Conv2D, Full
class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
""" """
Base class to create architectures using TensorFlow Sequential model is a linear stack of :py:mod:`bob.learn.tensorflow.layers`.
**Parameters**
default_feature_layer: Default layer name (:py:obj:`str`) used as a feature layer.
use_gpu: If ``True`` uses the GPU in the computation.
""" """
def __init__(self, def __init__(self,
default_feature_layer=None, default_feature_layer=None,
use_gpu=False): use_gpu=False):
"""
Base constructor
**Parameters**
feature_layer:
"""
self.sequence_net = OrderedDict() self.sequence_net = OrderedDict()
self.default_feature_layer = default_feature_layer self.default_feature_layer = default_feature_layer
self.input_divide = 1. self.input_divide = 1.
self.input_subtract = 0. self.input_subtract = 0.
self.use_gpu=use_gpu self.use_gpu = use_gpu
#self.saver = None
def add(self, layer): def add(self, layer):
""" """
Add a layer in the sequence network Add a :py:class:`bob.learn.tensorflow.layers.Layer` in the sequence network
""" """
if not isinstance(layer, Layer): if not isinstance(layer, Layer):
...@@ -49,12 +45,16 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -49,12 +45,16 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
self.sequence_net[layer.name] = layer self.sequence_net[layer.name] = layer
def compute_graph(self, input_data, feature_layer=None, training=True): def compute_graph(self, input_data, feature_layer=None, training=True):
""" """Given the current network, return the Tensorflow graph
Given the current network, return the Tensorflow graph
**Parameter** **Parameter**
input_data:
cut: Name of the layer that you want to cut. input_data: tensorflow placeholder as input data
feature_layer: Name of the :py:class:`bob.learn.tensorflow.layer.Layer` that you want to "cut".
If `None` will run the graph until the end.
training: If `True` will generating the graph for training
""" """
input_offset = input_data input_offset = input_data
...@@ -71,9 +71,26 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -71,9 +71,26 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
return input_offset return input_offset
def compute_projection_graph(self, placeholder): def compute_projection_graph(self, placeholder):
"""Generate a graph for feature extraction
**Parameters**
placeholder: tensorflow placeholder as input data
"""
return self.compute_graph(placeholder) return self.compute_graph(placeholder)
def __call__(self, data, session=None, feature_layer=None): def __call__(self, data, session=None, feature_layer=None):
"""Run a graph
**Parameters**
data: tensorflow placeholder as input data
session: tensorflow `session <https://www.tensorflow.org/versions/r0.11/api_docs/python/client.html#Session>`_
feature_layer: Name of the :py:class:`bob.learn.tensorflow.layer.Layer` that you want to "cut".
If `None` will run the graph until the end.
"""
if session is None: if session is None:
session = tf.Session() session = tf.Session()
...@@ -88,6 +105,9 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -88,6 +105,9 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
return session.run([self.compute_graph(feature_placeholder, feature_layer, training=False)], feed_dict=feed_dict)[0] return session.run([self.compute_graph(feature_placeholder, feature_layer, training=False)], feed_dict=feed_dict)[0]
def dump_variables(self): def dump_variables(self):
"""Return all the tensorflow `variables <https://www.tensorflow.org/versions/r0.11/api_docs/python/state_ops.html#Variable>`_ used in the graph
"""
variables = {} variables = {}
for k in self.sequence_net: for k in self.sequence_net:
...@@ -99,8 +119,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -99,8 +119,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
return variables return variables
def save(self, hdf5, step=None): def save(self, hdf5, step=None):
""" """Save the state of the network in HDF5 format
Save the state of the network in HDF5 format
**Parameters**
hdf5: An opened :py:class:`bob.io.base.HDF5File`
step: The current training step. If not `None`, will create a HDF5 group with the current step.
""" """
# Directory that stores the tensorflow variables # Directory that stores the tensorflow variables
...@@ -124,6 +150,16 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -124,6 +150,16 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
hdf5.set('input_subtract', self.input_subtract) hdf5.set('input_subtract', self.input_subtract)
def load(self, hdf5, shape, session=None): def load(self, hdf5, shape, session=None):
"""Load the network
**Parameters**
hdf5: The saved network in the :py:class:`bob.io.base.HDF5File` format
shape: Input shape of the network
session: tensorflow `session <https://www.tensorflow.org/versions/r0.11/api_docs/python/client.html#Session>`_
"""
if session is None: if session is None:
session = tf.Session() session = tf.Session()
...@@ -160,7 +196,6 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -160,7 +196,6 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
tf.histogram_summary(name, var) tf.histogram_summary(name, var)
def generate_summaries(self): def generate_summaries(self):
for k in self.sequence_net.keys(): for k in self.sequence_net.keys():
current_layer = self.sequence_net[k] current_layer = self.sequence_net[k]
...@@ -171,15 +206,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -171,15 +206,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
def compute_magic_number(self, hypothetic_image_dimensions=(28, 28, 1)): def compute_magic_number(self, hypothetic_image_dimensions=(28, 28, 1)):
""" """
Here it is done an estimative of the capacity of CNN. Here it is done an estimative of the capacity of DNN.
**Parameters**
:param hypothetic_sample_number: an ar hypothetic_image_dimensions: Possible image dimentions `w, h, c` (width x height x channels)
:param hypothetic_image_dimensions:
:return:
""" """
stride = 1# ALWAYS EQUALS TO ONE stride = 1# ALWAYS EQUALS TO ONE
current_image_dimensions = list(hypothetic_image_dimensions) current_image_dimensions = list(hypothetic_image_dimensions)
samples_per_sample = 0 samples_per_sample = 0
......
...@@ -145,4 +145,3 @@ def main(): ...@@ -145,4 +145,3 @@ def main():
iterations=ITERATIONS, iterations=ITERATIONS,
snapshot=VALIDATION_TEST) snapshot=VALIDATION_TEST)
trainer.train(train_data_shuffler, validation_data_shuffler) trainer.train(train_data_shuffler, validation_data_shuffler)
...@@ -25,14 +25,35 @@ extensions = [ ...@@ -25,14 +25,35 @@ extensions = [
'sphinx.ext.intersphinx', 'sphinx.ext.intersphinx',
'sphinx.ext.napoleon', 'sphinx.ext.napoleon',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'matplotlib.sphinxext.plot_directive'
] ]
import sphinx import sphinx
if sphinx.__version__ >= "1.4.1": if sphinx.__version__ >= "1.4.1":
extensions.append('sphinx.ext.imgmath') extensions.append('sphinx.ext.imgmath')
imgmath_image_format = 'svg'
else: else:
extensions.append('sphinx.ext.pngmath') extensions.append('sphinx.ext.pngmath')
# Be picky about warnings
nitpicky = True
# Ignores stuff we can't easily resolve on other project's sphinx manuals
nitpick_ignore = []
# Allows the user to override warnings from a separate file
if os.path.exists('nitpick-exceptions.txt'):
for line in open('nitpick-exceptions.txt'):
if line.strip() == "" or line.startswith("#"):
continue
dtype, target = line.split(None, 1)
target = target.strip()
try: # python 2.x
target = unicode(target)
except NameError:
pass
nitpick_ignore.append((dtype, target))
# Always includes todos # Always includes todos
todo_include_todos = True todo_include_todos = True
...@@ -111,7 +132,7 @@ pygments_style = 'sphinx' ...@@ -111,7 +132,7 @@ pygments_style = 'sphinx'
# Some variables which are useful for generated material # Some variables which are useful for generated material
project_variable = project.replace('.', '_') project_variable = project.replace('.', '_')
short_description = u'Tensorflow bob bindings' short_description = u'bob.learn.tensorflow API'
owner = [u'Idiap Research Institute'] owner = [u'Idiap Research Institute']
...@@ -216,8 +237,13 @@ autodoc_default_flags = [ ...@@ -216,8 +237,13 @@ autodoc_default_flags = [
] ]
# For inter-documentation mapping: # For inter-documentation mapping:
from bob.extension.utils import link_documentation from bob.extension.utils import link_documentation, load_requirements
intersphinx_mapping = link_documentation() sphinx_requirements = "extra-intersphinx.txt"
if os.path.exists(sphinx_requirements):
intersphinx_mapping = link_documentation(additional_packages=['python', 'numpy']+load_requirements(sphinx_requirements))
else:
intersphinx_mapping = link_documentation()
# We want to remove all private (i.e. _. or __.__) members # We want to remove all private (i.e. _. or __.__) members
# that are not in the list of accepted functions # that are not in the list of accepted functions
......
...@@ -36,4 +36,3 @@ Indices and tables ...@@ -36,4 +36,3 @@ Indices and tables
* :ref:`modindex` * :ref:`modindex`
* :ref:`search` * :ref:`search`
.. include:: links.rst
...@@ -6,8 +6,89 @@ ...@@ -6,8 +6,89 @@
Python API Python API
============ ============
Architectures
-------------
.. autosummary::
bob.learn.tensorflow.network.SequenceNetwork
bob.learn.tensorflow.network.Chopra
bob.learn.tensorflow.network.Dummy
bob.learn.tensorflow.network.Lenet