Commit 285c4e6a authored by Bharath Ramsundar's avatar Bharath Ramsundar
Browse files

Graph Conv Overfit and preliminary support tests

parent 83a9f0c7
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -709,7 +709,7 @@ class TestOverfitAPI(test_util.TensorFlowTestCase):
        beta2=.999, verbosity="high")

      # Fit trained model
      model.fit(dataset)
      model.fit(dataset, nb_epoch=30)
      model.save()

      # Eval model on train
@@ -717,4 +717,9 @@ class TestOverfitAPI(test_util.TensorFlowTestCase):
      evaluator = Evaluator(model, dataset, transformers, verbosity=verbosity)
      scores = evaluator.compute_model_performance([classification_metric])

    ############################################################ DEBUG
    print("scores")
    print(scores)
    ############################################################ DEBUG

    assert scores[classification_metric.name] > .9
+25 −39
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ class SequentialGraphModel(object):
  placeholders from GraphTopology to each graph layer (from keras_layers) added
  to the network. Non graph layers don't get the extra placeholders. 
  """
  def __init__(self, n_atoms, n_feat, batch_size):
  def __init__(self, n_atoms, n_feat):
    """
    Parameters
    ----------
@@ -32,21 +32,18 @@ class SequentialGraphModel(object):
      (Max?) Number of atoms in system.
    n_feat: int
      Number of features per atom.
    batch_size: int
      Batch size for training models.
    """
    
    #super(SequentialGraphModel, self).__init__()
    self.batch_size = batch_size
    # Create graph topology and x
    self.graph_topology = GraphTopology(n_atoms, n_feat, self.batch_size)
    self.graph_topology = GraphTopology(n_atoms, n_feat)
    self.output = self.graph_topology.get_atom_features_placeholder()
    # Keep track of the layers
    self.layers = []  

  def add(self, layer):
    """Adds a new layer to model."""
    # Update new value of x
    # For graphical layers, add connectivity placeholders 
    if type(layer).__name__ in ['GraphConv', 'GraphGather', 'GraphPool']:
      if (len(self.layers) > 0 and hasattr(self.layers[-1], "__name__")):
        assert (self.layers[-1].__name__ != "GraphGather",
@@ -60,26 +57,6 @@ class SequentialGraphModel(object):
    # Add layer to the layer list
    self.layers.append(layer)

  '''
  def graph_gather(self, activation='linear'):
    gather = GraphGather(self.batch_size, activation=activation)

    self.layers.append(gather)
    
    self.output = gather(
        [self.output] + self.graph_topology.get_topology_placeholders())
  '''
    
  '''
  def return_container(self, sess):
    return GraphContainer(sess, input=self.return_inputs(),
                          output=self.return_outputs(),
                          graph_topology=self.graph_topology)
  '''
  
  def get_batch_size(self):
    return self.batch_size

  def get_graph_topology(self):
    return self.graph_topology

@@ -97,18 +74,28 @@ class SequentialGraphModel(object):
    return self.layers[layer_id]

class SequentialSupportGraphModel(object):
  def __init__(self, n_atom, n_feat, test_batch_size, support_batch_size):

    self.test_batch_size = test_batch_size
    self.support_batch_size = support_batch_size
  """An analog of Keras Sequential model for test/support models."""
  def __init__(self, n_test, n_support, n_feat):
    """
    Parameters
    ----------
    n_test: int
      Number of test atoms.
    n_support: int
      Number of support atoms.
    n_feat: int
      Number of atomic features.
    """
    self.n_test = n_test
    self.n_support = n_support

    # Create graph topology and x
    self.test_graph_topology = GraphTopology(
        n_atom, n_feat, test_batch_size, name='test')
        n_test, n_feat, name='test')
    self.support_graph_topology = GraphTopology(
        n_atom, n_feat, support_batch_size, name='support')
    self.test = self.test_graph_topology.get_nodes()
    self.support = self.support_graph_topology.get_nodes()
        n_support, n_feat, name='support')
    self.test = self.test_graph_topology.get_atom_features_placeholder()
    self.support = self.support_graph_topology.get_atom_features_placeholder()

    # Keep track of the layers
    self.layers = []  
@@ -118,10 +105,6 @@ class SequentialSupportGraphModel(object):
  def add(self, layer):
    # Add layer to the layer list
    self.layers.append(layer)
    ############################################################# DEBUG
    print("SequentialSupportGraphModel.add()")
    print(layer)
    ############################################################# DEBUG

    # Update new value of x
    if type(layer).__name__ in ['GraphConv', 'GraphGather', 'GraphPool']:
@@ -157,18 +140,21 @@ class SequentialSupportGraphModel(object):
    
    self.bool_pre_gather = False

  '''
  def return_container(self, sess):
    return SupportGraphContainer(
        sess, input=self.return_inputs(),
        output=self.return_outputs(), 
        graph_topology_test=self.test_graph_topology,
        graph_topology_support=self.support_graph_topology)
  '''
  
  def return_outputs(self):
    return [self.test] + [self.support]

  def return_inputs(self):
    return self.test_graph_topology.get_inputs() + self.support_graph_topology.get_inputs()
    return (self.test_graph_topology.get_inputs()
            + self.support_graph_topology.get_inputs())

  def get_layer(self, layer_id):
    return self.layers[layer_id]
+9 −8
Original line number Diff line number Diff line
@@ -26,17 +26,19 @@ def merge_dicts(l):

class GraphTopology(object):
  """Manages placeholders associated with batch of graphs and their topology"""
  def __init__(self, n_atoms, n_feat, batch_size, name='topology', max_deg=6,
  def __init__(self, n_atoms, n_feat, name='topology', max_deg=6,
               min_deg=0):
    """
    Note that batch size is not specified in a GraphTopology object. A batch
    of molecules must be combined into a disconnected graph and fed to topology
    directly to handle batches.

    Parameters
    ----------
    n_atoms: int
      Number of atoms (max) in graphs.
    n_feat: int
      Number of features per atom.
    batch_size: int
      Number of molecules per batch.
    name: str, optional
      Name of this manager.
    max_deg: int, optional
@@ -47,15 +49,17 @@ class GraphTopology(object):
    
    self.n_atoms = n_atoms
    self.n_feat = n_feat
    self.batch_size = batch_size

    self.name = name
    self.max_deg = max_deg
    self.min_deg = min_deg

    self.atom_features_placeholder = Input(
        #tensor=K.placeholder(
        #    shape=(None, self.n_feat), dtype='float32',
        #    name=self.name+'_atom_features'))
        tensor=K.placeholder(
            shape=(None, self.n_feat), dtype='float32',
            shape=(self.n_atoms, self.n_feat), dtype='float32',
            name=self.name+'_atom_features'))
    self.deg_adj_lists_placeholders = [
        Input(tensor=K.placeholder(
@@ -92,9 +96,6 @@ class GraphTopology(object):
    """
    return self.topology

  def get_batch_size(self):
    return self.batch_size

  def get_atom_features_placeholder(self):
    return self.atom_features_placeholder

+54 −3
Original line number Diff line number Diff line
@@ -456,8 +456,29 @@ class GraphPool(Layer):
    return atom_features 

class AttnLSTMEmbedding(Layer):
  """Implements AttnLSTM as in matching networks paper.

  References:
  Matching Networks for One Shot Learning
  https://arxiv.org/pdf/1606.04080v1.pdf

  Order Matters: Sequence to sequence for sets
  https://arxiv.org/abs/1511.06391
  """
  def __init__(self, max_depth, init='glorot_uniform', activation='linear',
               dropout=None, **kwargs):
    """
    Parameters
    ----------
    max_depth: int
      Number of "processing steps" used by sequence-to-sequence for sets model.
    init: str, optional
      Type of initialization of weights
    activation: str, optional
      Activation for layers.
    dropout: float, optional
      Dropout probability
    """
    super(AttnLSTMEmbedding, self).__init__(**kwargs)

    self.init = initializations.get(init)  # Set weight initialization
@@ -466,7 +487,9 @@ class AttnLSTMEmbedding(Layer):


  def build(self, input_shape):
    print(input_shape)
    """Initializes trainable weights."""
    # x_input_shape = (N_test, N_feat)
    # xp_input_shape = (N_support, N_feat)
    x_input_shape, xp_input_shape = input_shape  #Unpack

    N_test = x_input_shape[0]
@@ -481,13 +504,41 @@ class AttnLSTMEmbedding(Layer):
    self.trainable_weights = [self.q_init, self.r_init]
      
  def get_output_shape_for(self, input_shape):
    """Returns the output shape. Same as input_shape.

    Parameters
    ----------
    input_shape: list
      Will be of form [(n_test, n_feat), (n_support, n_feat)]

    Returns
    -------
    list
      Of same shape as input [(n_test, n_feat), (n_support, n_feat)]
    """
    x_input_shape, xp_input_shape = input_shape  #Unpack

    return input_shape

  def call(self, x_xp, mask=None):
    x, xp = x_xp  #Unpack
    print('unpacked')
    """Execute this layer on input tensors.

    Parameters
    ----------
    x_xp: list
      List of two tensors (X, Xp). X should be of shape (n_test, n_feat) and
      Xp should be of shape (n_support, n_feat) where n_test is the size of
      the test set, n_support that of the support set, and n_feat is the number
      of per-atom features.

    Returns
    -------
    list
      Returns two tensors of same shape as input. Namely the output shape will
      be [(n_test, n_feat), (n_support, n_feat)]
    """
    # x is test set, xp is support set.
    x, xp = x_xp

    # Get initializations
    q = self.q_init
+4 −4
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ def get_loss_fn(final_loss):

class MultitaskGraphClassifier(Model):

  def __init__(self, sess, model, n_tasks, logdir,
  def __init__(self, sess, model, n_tasks, logdir, batch_size=50,
               final_loss='cross_entropy', learning_rate=.001,
               optimizer_type="adam", learning_rate_decay_time=1000,
               beta1=.9, beta2=.999, verbosity=None):
@@ -58,7 +58,7 @@ class MultitaskGraphClassifier(Model):
    self.logdir = logdir
           
    # Extract model info 
    self.batch_size = self.model.get_batch_size()
    self.batch_size = batch_size 
    # Get graph topology for x
    self.graph_topology = self.model.get_graph_topology()
    self.feat_dim = self.model.get_num_output_features()
@@ -180,7 +180,7 @@ class MultitaskGraphClassifier(Model):
        softmax.append(tf.nn.softmax(logits, name='softmax_%d' % i))
    return softmax

  def fit(self, dataset, nb_epoch=10, batch_size=50, pad_batches=False,
  def fit(self, dataset, nb_epoch=10, pad_batches=False,
          max_checkpoints_to_keep=5, log_every_N_batches=50, **kwargs):
    # Perform the optimization
    log("Training for %d epochs" % nb_epoch, self.verbosity)
@@ -195,7 +195,7 @@ class MultitaskGraphClassifier(Model):
      log("Starting epoch %d" % epoch, self.verbosity)
      # ToDo(hraut->rbharath) : what is the ids_b for? Is it the zero's? 
      for batch_num, (X_b, y_b, w_b, ids_b) in enumerate(dataset.iterbatches(
          batch_size, pad_batches=pad_batches)):
          self.batch_size, pad_batches=pad_batches)):
        if batch_num % log_every_N_batches == 0:
          log("On batch %d" % batch_num, self.verbosity)
        self.sess.run(
Loading