Commit 2272885e authored by peastman's avatar peastman
Browse files

Merged changes

parents 8375adcc 01430dd7
Loading
Loading
Loading
Loading
+165 −0
Original line number Diff line number Diff line
import collections

import numpy as np
import six
import tensorflow as tf

from deepchem.data import NumpyDataset
from deepchem.feat.graph_features import ConvMolFeaturizer
from deepchem.feat.mol_graphs import ConvMol
from deepchem.metrics import to_one_hot
from deepchem.models.tensorgraph.graph_layers import WeaveGather, \
  DTNNEmbedding, DTNNStep, DTNNGather, DAGLayer, \
  DAGGather, DTNNExtract, MessagePassing, SetGather
from deepchem.models.tensorgraph.graph_layers import WeaveLayerFactory
from deepchem.models.tensorgraph.layers import Dense, SoftMax, \
  SoftMaxCrossEntropy, GraphConv, BatchNorm, \
  GraphPool, GraphGather, WeightedError, Dropout, BatchNorm, Stack, Flatten, GraphCNN, GraphCNNPool
from deepchem.models.tensorgraph.layers import L2Loss, Label, Weights, Feature
from deepchem.models.tensorgraph.tensor_graph import TensorGraph
from deepchem.trans import undo_transforms


class PetroskiSuchModel(TensorGraph):
  """
      Model from Robust Spatial Filtering with Graph Convolutional Neural Networks
      https://arxiv.org/abs/1703.00792
      """

  def __init__(self,
               n_tasks,
               max_atoms=200,
               dropout=0.0,
               mode="classification",
               **kwargs):
    """
            Parameters
            ----------
            n_tasks: int
              Number of tasks
            mode: str
              Either "classification" or "regression"
            """
    self.n_tasks = n_tasks
    self.mode = mode
    self.max_atoms = max_atoms
    self.error_bars = True if 'error_bars' in kwargs and kwargs['error_bars'] else False
    self.dropout = dropout
    kwargs['use_queue'] = False
    super(PetroskiSuchModel, self).__init__(**kwargs)
    self.build_graph()

  def build_graph(self):
    self.vertex_features = Feature(shape=(None, self.max_atoms, 75))
    self.adj_matrix = Feature(shape=(None, self.max_atoms, 1, self.max_atoms))
    self.mask = Feature(shape=(None, self.max_atoms, 1))

    gcnn1 = BatchNorm(
        GraphCNN(
            num_filters=64,
            in_layers=[self.vertex_features, self.adj_matrix, self.mask]))
    gcnn1 = Dropout(self.dropout, in_layers=gcnn1)
    gcnn2 = BatchNorm(
        GraphCNN(num_filters=64, in_layers=[gcnn1, self.adj_matrix, self.mask]))
    gcnn2 = Dropout(self.dropout, in_layers=gcnn2)
    gc_pool, adj_matrix = GraphCNNPool(
        num_vertices=32, in_layers=[gcnn2, self.adj_matrix, self.mask])
    gc_pool = BatchNorm(gc_pool)
    gc_pool = Dropout(self.dropout, in_layers=gc_pool)
    gcnn3 = BatchNorm(GraphCNN(num_filters=32, in_layers=[gc_pool, adj_matrix]))
    gcnn3 = Dropout(self.dropout, in_layers=gcnn3)
    gc_pool2, adj_matrix2 = GraphCNNPool(
        num_vertices=8, in_layers=[gcnn3, adj_matrix])
    gc_pool2 = BatchNorm(gc_pool2)
    gc_pool2 = Dropout(self.dropout, in_layers=gc_pool2)
    flattened = Flatten(in_layers=gc_pool2)
    readout = Dense(
        out_channels=256, activation_fn=tf.nn.relu, in_layers=flattened)
    costs = []
    self.my_labels = []
    for task in range(self.n_tasks):
      if self.mode == 'classification':
        classification = Dense(
            out_channels=2, activation_fn=None, in_layers=[readout])

        softmax = SoftMax(in_layers=[classification])
        self.add_output(softmax)

        label = Label(shape=(None, 2))
        self.my_labels.append(label)
        cost = SoftMaxCrossEntropy(in_layers=[label, classification])
        costs.append(cost)
      if self.mode == 'regression':
        regression = Dense(
            out_channels=1, activation_fn=None, in_layers=[readout])
        self.add_output(regression)

        label = Label(shape=(None, 1))
        self.my_labels.append(label)
        cost = L2Loss(in_layers=[label, regression])
        costs.append(cost)
    if self.mode == "classification":
      entropy = Stack(in_layers=costs, axis=-1)
    elif self.mode == "regression":
      entropy = Stack(in_layers=costs, axis=1)
    self.my_task_weights = Weights(shape=(None, self.n_tasks))
    loss = WeightedError(in_layers=[entropy, self.my_task_weights])
    self.set_loss(loss)

  def default_generator(self,
                        dataset,
                        epochs=1,
                        predict=False,
                        deterministic=True,
                        pad_batches=True):
    for epoch in range(epochs):
      if not predict:
        print('Starting epoch %i' % epoch)
      for ind, (X_b, y_b, w_b, ids_b) in enumerate(
          dataset.iterbatches(
              self.batch_size, pad_batches=True, deterministic=deterministic)):
        d = {}
        for index, label in enumerate(self.my_labels):
          if self.mode == 'classification':
            d[label] = to_one_hot(y_b[:, index])
          if self.mode == 'regression':
            d[label] = np.expand_dims(y_b[:, index], -1)
        d[self.my_task_weights] = w_b
        d[self.adj_matrix] = np.expand_dims(np.array([x[0] for x in X_b]), -2)
        d[self.vertex_features] = np.array([x[1] for x in X_b])
        mask = np.zeros(shape=(self.batch_size, self.max_atoms, 1))
        for i in range(self.batch_size):
          mask_size = X_b[i][2]
          mask[i][:mask_size][0] = 1
        d[self.mask] = mask
        yield d

  def predict_proba_on_generator(self, generator, transformers=[]):
    if not self.built:
      self.build()
    with self._get_tf("Graph").as_default():
      out_tensors = [x.out_tensor for x in self.outputs]
      results = []
      for feed_dict in generator:
        feed_dict = {
            self.layers[k.name].out_tensor: v
            for k, v in six.iteritems(feed_dict)
        }
        feed_dict[self._training_placeholder] = 1.0  ##
        result = np.array(self.session.run(out_tensors, feed_dict=feed_dict))
        if len(result.shape) == 3:
          result = np.transpose(result, axes=[1, 0, 2])
        if len(transformers) > 0:
          result = undo_transforms(result, transformers)
        results.append(result)
      return np.concatenate(results, axis=0)

  def evaluate(self, dataset, metrics, transformers=[], per_task_metrics=False):
    if not self.built:
      self.build()
    return self.evaluate_generator(
        self.default_generator(dataset, predict=True),
        metrics,
        labels=self.my_labels,
        weights=[self.my_task_weights],
        per_task_metrics=per_task_metrics)
+25 −13
Original line number Diff line number Diff line
@@ -581,16 +581,18 @@ class DAGLayer(Layer):
          Number of features listed per atom.
        max_atoms: int, optional
          Maximum number of atoms in molecules.
        layer_sizes: list of int, optional(default=[1000])
          Structure of hidden layer(s)
        layer_sizes: list of int, optional(default=[100])
          List of hidden layer size(s): 
          length of this list represents the number of hidden layers, 
          and each element is the width of corresponding hidden layer.
        init: str, optional
          Weight initialization for filters.
        activation: str, optional
          Activation function applied
          Activation function applied.
        dropout: float, optional
          Dropout probability, not supported here
          Dropout probability in hidden layer(s).
        batch_size: int, optional
          number of molecules in a batch
          number of molecules in a batch.
        """
    super(DAGLayer, self).__init__(**kwargs)

@@ -683,7 +685,8 @@ class DAGLayer(Layer):
      # DAGgraph_step maps from batch_inputs to a batch of graph_features
      # of shape: (batch_size*max_atoms) * n_graph_features
      # representing the graph features of target atoms in each graph
      batch_outputs = self.DAGgraph_step(batch_inputs, self.W_list, self.b_list)
      batch_outputs = self.DAGgraph_step(batch_inputs, self.W_list, self.b_list,
                                         **kwargs)

      # index for targe atoms
      target_index = tf.stack([tf.range(n_atoms), parents[:, count, 0]], axis=1)
@@ -698,11 +701,14 @@ class DAGLayer(Layer):
      self.out_tensor = out_tensor
    return out_tensor

  def DAGgraph_step(self, batch_inputs, W_list, b_list):
  def DAGgraph_step(self, batch_inputs, W_list, b_list, **kwargs):
    outputs = batch_inputs
    for idw, W in enumerate(W_list):
      outputs = tf.nn.xw_plus_b(outputs, W, b_list[idw])
      outputs = self.activation(outputs)
      training = kwargs['training'] if 'training' in kwargs else 1.0
      if not self.dropout is None:
        outputs = tf.nn.dropout(outputs, 1.0 - self.dropout * training)
    return outputs

  def none_tensors(self):
@@ -733,19 +739,21 @@ class DAGGather(Layer):
        Parameters
        ----------
        n_graph_feat: int, optional
          Number of features for each atom
          Number of features for each atom.
        n_outputs: int, optional
          Number of features for each molecule.
        max_atoms: int, optional
          Maximum number of atoms in molecules.
        layer_sizes: list of int, optional
          Structure of hidden layer(s)
          List of hidden layer size(s): 
          length of this list represents the number of hidden layers, 
          and each element is the width of corresponding hidden layer.
        init: str, optional
          Weight initialization for filters.
        activation: str, optional
          Activation function applied
          Activation function applied.
        dropout: float, optional
          Dropout probability, not supported
          Dropout probability in the hidden layer(s).
        """
    super(DAGGather, self).__init__(**kwargs)

@@ -794,18 +802,22 @@ class DAGGather(Layer):
    # Extract atom_features
    graph_features = tf.segment_sum(atom_features, membership)
    # sum all graph outputs
    outputs = self.DAGgraph_step(graph_features, self.W_list, self.b_list)
    outputs = self.DAGgraph_step(graph_features, self.W_list, self.b_list,
                                 **kwargs)
    out_tensor = outputs
    if set_tensors:
      self.variables = self.trainable_weights
      self.out_tensor = out_tensor
    return out_tensor

  def DAGgraph_step(self, batch_inputs, W_list, b_list):
  def DAGgraph_step(self, batch_inputs, W_list, b_list, **kwargs):
    outputs = batch_inputs
    for idw, W in enumerate(W_list):
      outputs = tf.nn.xw_plus_b(outputs, W, b_list[idw])
      outputs = self.activation(outputs)
      training = kwargs['training'] if 'training' in kwargs else 1.0
      if not self.dropout is None:
        outputs = tf.nn.dropout(outputs, 1.0 - self.dropout * training)
    return outputs

  def none_tensors(self):
+23 −160
Original line number Diff line number Diff line
@@ -391,22 +391,33 @@ class DAGModel(TensorGraph):
               n_atom_feat=75,
               n_graph_feat=30,
               n_outputs=30,
               layer_sizes=[100],
               layer_sizes_gather=[100],
               dropout=None,
               mode="classification",
               **kwargs):
    """
            Parameters
            ----------
            n_tasks: int
              Number of tasks
              Number of tasks.
            max_atoms: int, optional
              Maximum number of atoms in a molecule, should be defined based on dataset
              Maximum number of atoms in a molecule, should be defined based on dataset.
            n_atom_feat: int, optional
              Number of features per atom.
            n_graph_feat: int, optional
              Number of features for atom in the graph
              Number of features for atom in the graph.
            n_outputs: int, optional
              Number of features for each molecule
            mode: str
              Number of features for each molecule.
            layer_sizes: list of int, optional
              List of hidden layer size(s) in the propagation step:
              length of this list represents the number of hidden layers,
              and each element is the width of corresponding hidden layer.
            layer_sizes_gather: list of int, optional
              List of hidden layer size(s) in the gather step.
            dropout: None or float, optional
              Dropout probability, applied after each propagation step and gather step.
            mode: str, optional
              Either "classification" or "regression" for type of model.
            """
    self.n_tasks = n_tasks
@@ -414,6 +425,9 @@ class DAGModel(TensorGraph):
    self.n_atom_feat = n_atom_feat
    self.n_graph_feat = n_graph_feat
    self.n_outputs = n_outputs
    self.layer_sizes = layer_sizes
    self.layer_sizes_gather = layer_sizes_gather
    self.dropout = dropout
    self.mode = mode
    super(DAGModel, self).__init__(**kwargs)
    self.build_graph()
@@ -435,6 +449,8 @@ class DAGModel(TensorGraph):
        n_graph_feat=self.n_graph_feat,
        n_atom_feat=self.n_atom_feat,
        max_atoms=self.max_atoms,
        layer_sizes=self.layer_sizes,
        dropout=self.dropout,
        batch_size=self.batch_size,
        in_layers=[
            self.atom_features, self.parents, self.calculation_orders,
@@ -444,6 +460,8 @@ class DAGModel(TensorGraph):
        n_graph_feat=self.n_graph_feat,
        n_outputs=self.n_outputs,
        max_atoms=self.max_atoms,
        layer_sizes=self.layer_sizes_gather,
        dropout=self.dropout,
        in_layers=[dag_layer1, self.membership])

    costs = []
@@ -544,150 +562,6 @@ class DAGModel(TensorGraph):
    return out


class PetroskiSuchModel(TensorGraph):
  """
      Model from Robust Spatial Filtering with Graph Convolutional Neural Networks
      https://arxiv.org/abs/1703.00792
      """

  def __init__(self,
               n_tasks,
               max_atoms=200,
               dropout=0.0,
               mode="classification",
               **kwargs):
    """
            Parameters
            ----------
            n_tasks: int
              Number of tasks
            mode: str
              Either "classification" or "regression"
            """
    self.n_tasks = n_tasks
    self.mode = mode
    self.max_atoms = max_atoms
    self.dropout = dropout
    kwargs['use_queue'] = False
    super(PetroskiSuchModel, self).__init__(**kwargs)
    self.build_graph()

  def build_graph(self):
    self.vertex_features = Feature(shape=(None, self.max_atoms, 75))
    self.adj_matrix = Feature(shape=(None, self.max_atoms, 1, self.max_atoms))
    self.mask = Feature(shape=(None, self.max_atoms, 1))

    gcnn1 = BatchNorm(
        GraphCNN(
            num_filters=64,
            in_layers=[self.vertex_features, self.adj_matrix, self.mask]))
    gcnn1 = Dropout(self.dropout, in_layers=gcnn1)
    gcnn2 = BatchNorm(
        GraphCNN(num_filters=64, in_layers=[gcnn1, self.adj_matrix, self.mask]))
    gcnn2 = Dropout(self.dropout, in_layers=gcnn2)
    gc_pool, adj_matrix = GraphCNNPool(
        num_vertices=32, in_layers=[gcnn2, self.adj_matrix, self.mask])
    gc_pool = BatchNorm(gc_pool)
    gc_pool = Dropout(self.dropout, in_layers=gc_pool)
    gcnn3 = BatchNorm(GraphCNN(num_filters=32, in_layers=[gc_pool, adj_matrix]))
    gcnn3 = Dropout(self.dropout, in_layers=gcnn3)
    gc_pool2, adj_matrix2 = GraphCNNPool(
        num_vertices=8, in_layers=[gcnn3, adj_matrix])
    gc_pool2 = BatchNorm(gc_pool2)
    gc_pool2 = Dropout(self.dropout, in_layers=gc_pool2)
    flattened = Flatten(in_layers=gc_pool2)
    readout = Dense(
        out_channels=256, activation_fn=tf.nn.relu, in_layers=flattened)
    costs = []
    self.my_labels = []
    for task in range(self.n_tasks):
      if self.mode == 'classification':
        classification = Dense(
            out_channels=2, activation_fn=None, in_layers=[readout])

        softmax = SoftMax(in_layers=[classification])
        self.add_output(softmax)

        label = Label(shape=(None, 2))
        self.my_labels.append(label)
        cost = SoftMaxCrossEntropy(in_layers=[label, classification])
        costs.append(cost)
      if self.mode == 'regression':
        regression = Dense(
            out_channels=1, activation_fn=None, in_layers=[readout])
        self.add_output(regression)

        label = Label(shape=(None, 1))
        self.my_labels.append(label)
        cost = L2Loss(in_layers=[label, regression])
        costs.append(cost)
    if self.mode == "classification":
      entropy = Stack(in_layers=costs, axis=-1)
    elif self.mode == "regression":
      entropy = Stack(in_layers=costs, axis=1)
    self.my_task_weights = Weights(shape=(None, self.n_tasks))
    loss = WeightedError(in_layers=[entropy, self.my_task_weights])
    self.set_loss(loss)

  def default_generator(self,
                        dataset,
                        epochs=1,
                        predict=False,
                        deterministic=True,
                        pad_batches=True):
    for epoch in range(epochs):
      if not predict:
        print('Starting epoch %i' % epoch)
      for ind, (X_b, y_b, w_b, ids_b) in enumerate(
          dataset.iterbatches(
              self.batch_size, pad_batches=True, deterministic=deterministic)):
        d = {}
        for index, label in enumerate(self.my_labels):
          if self.mode == 'classification':
            d[label] = to_one_hot(y_b[:, index])
          if self.mode == 'regression':
            d[label] = np.expand_dims(y_b[:, index], -1)
        d[self.my_task_weights] = w_b
        d[self.adj_matrix] = np.expand_dims(np.array([x[0] for x in X_b]), -2)
        d[self.vertex_features] = np.array([x[1] for x in X_b])
        mask = np.zeros(shape=(self.batch_size, self.max_atoms, 1))
        for i in range(self.batch_size):
          mask_size = X_b[i][2]
          mask[i][:mask_size][0] = 1
        d[self.mask] = mask
        yield d

  def predict_proba_on_generator(self, generator, transformers=[]):
    if not self.built:
      self.build()
    with self._get_tf("Graph").as_default():
      out_tensors = [x.out_tensor for x in self.outputs]
      results = []
      for feed_dict in generator:
        feed_dict = {
            self.layers[k.name].out_tensor: v
            for k, v in six.iteritems(feed_dict)
        }
        feed_dict[self._training_placeholder] = 1.0  ##
        result = np.array(self.session.run(out_tensors, feed_dict=feed_dict))
        if len(result.shape) == 3:
          result = np.transpose(result, axes=[1, 0, 2])
        if len(transformers) > 0:
          result = undo_transforms(result, transformers)
        results.append(result)
      return np.concatenate(results, axis=0)

  def evaluate(self, dataset, metrics, transformers=[], per_task_metrics=False):
    if not self.built:
      self.build()
    return self.evaluate_generator(
        self.default_generator(dataset, predict=True),
        metrics,
        labels=self.my_labels,
        weights=[self.my_task_weights],
        per_task_metrics=per_task_metrics)


class GraphConvModel(TensorGraph):

  def __init__(self,
@@ -1112,17 +986,6 @@ class DAGTensorGraph(DAGModel):
    super(DAGModel, self).__init__(*args, **kwargs)


class PetroskiSuchTensorGraph(PetroskiSuchModel):

  def __init__(self, *args, **kwargs):

    warnings.warn(
        TENSORGRAPH_DEPRECATION.format("PetroskiSuchTensorGraph",
                                       "PetroskiSuchModel"), FutureWarning)

    super(PetroskiSuchModel, self).__init__(*args, **kwargs)


class MPNNTensorGraph(MPNNModel):

  def __init__(self, *args, **kwargs):
+5 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ import deepchem
from deepchem.data import NumpyDataset
from deepchem.models import GraphConvModel
from deepchem.models import TensorGraph
from deepchem.molnet.load_function.delaney_datasets import load_delaney
from deepchem.molnet import load_bace_classification, load_delaney
from deepchem.models.tensorgraph.layers import ReduceSum, L2Loss
from deepchem.models import WeaveModel
from deepchem.feat import ConvMolFeaturizer
@@ -19,7 +19,11 @@ class TestGraphModels(unittest.TestCase):
                  featurizer='GraphConv',
                  num_tasks=2):
    data_points = 10
    if mode == 'classification':
      tasks, all_dataset, transformers = load_bace_classification(featurizer)
    else:
      tasks, all_dataset, transformers = load_delaney(featurizer)

    train, valid, test = all_dataset
    for i in range(1, num_tasks):
      tasks.append("random_task")
+1 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ from __future__ import unicode_literals

import numpy as np

from models import GraphConvModel
from deepchem.models import GraphConvModel

np.random.seed(123)
import tensorflow as tf