Unverified Commit 66628f7b authored by Bharath Ramsundar's avatar Bharath Ramsundar Committed by GitHub
Browse files

Merge pull request #1153 from peastman/robust

make_estimator() works for RobustMultitaskClassifier, RobustMultitaskRegressor, and Sequential
parents ff662561 af19a75d
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -112,7 +112,8 @@ class Layer(object):
    tensors = []
    for input in in_layers:
      tensors.append(tf.convert_to_tensor(input))
    if reshape and len(tensors) > 1:
    if reshape and len(tensors) > 1 and all(
        '_shape' in dir(l) for l in in_layers):
      shapes = [t.get_shape() for t in tensors]
      if any(s != shapes[0] for s in shapes[1:]):
        # Reshape everything to match the input with the most dimensions.
@@ -1070,6 +1071,7 @@ class TimeSeriesDense(Layer):
class Input(Layer):

  def __init__(self, shape, dtype=tf.float32, **kwargs):
    if shape is not None:
      self._shape = tuple(shape)
    self.dtype = dtype
    super(Input, self).__init__(**kwargs)
@@ -1079,18 +1081,25 @@ class Input(Layer):
    if in_layers is None:
      in_layers = self.in_layers
    in_layers = convert_to_layers(in_layers)
    try:
      shape = self.shape
    except NotImplementedError:
      shape = None
    if len(in_layers) > 0:
      queue = in_layers[0]
      placeholder = queue.out_tensors[self.get_pre_q_name()]
      self.out_tensor = tf.placeholder_with_default(placeholder, self._shape)
      self.out_tensor = tf.placeholder_with_default(placeholder, shape)
      return self.out_tensor
    out_tensor = tf.placeholder(dtype=self.dtype, shape=self._shape)
    out_tensor = tf.placeholder(dtype=self.dtype, shape=shape)
    if set_tensors:
      self.out_tensor = out_tensor
    return out_tensor

  def create_pre_q(self):
    q_shape = (None,) + self._shape[1:]
    try:
      q_shape = (None,) + self.shape[1:]
    except NotImplementedError:
      q_shape = None
    return Input(shape=q_shape, name="%s_pre_q" % self.name, dtype=self.dtype)

  def get_pre_q_name(self):
+7 −4
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ class SequenceDNN(Sequential):
  ----------
  seq_length : int
      length of input sequence.
  loss: string
    the loss function to use.  Supported values are 'binary_crossentropy' and 'mse'.
  num_tasks : int, optional
      number of tasks. Default: 1.
  num_filters : list[int] | tuple[int]
@@ -39,6 +41,7 @@ class SequenceDNN(Sequential):

  def __init__(self,
               seq_length,
               loss,
               use_RNN=False,
               num_tasks=1,
               num_filters=15,
@@ -48,7 +51,7 @@ class SequenceDNN(Sequential):
               dropout=0.0,
               verbose=True,
               **kwargs):
    super(SequenceDNN, self).__init__(**kwargs)
    super(SequenceDNN, self).__init__(loss, **kwargs)
    self.num_tasks = num_tasks
    self.verbose = verbose
    self.add(layers.Conv2D(num_filters, kernel_size=kernel_size))
+22 −28
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ from deepchem.metrics import to_one_hot

from deepchem.models.tensorgraph.tensor_graph import TensorGraph, TFWrapper
from deepchem.models.tensorgraph.layers import Feature, Label, Weights, \
    WeightedError, Dense, Dropout, WeightDecay, Reshape, SoftMaxCrossEntropy, \
    WeightedError, Dense, Dropout, WeightDecay, Reshape, SoftMax, SoftMaxCrossEntropy, \
    L2Loss, ReduceSum, Concat, Stack


@@ -153,12 +153,13 @@ class RobustMultitaskClassifier(TensorGraph):
      task_out = Dense(in_layers=[task_layer], out_channels=n_classes)
      task_outputs.append(task_out)

    output = Stack(axis=1, in_layers=task_outputs)
    logits = Stack(axis=1, in_layers=task_outputs)

    output = SoftMax(logits)
    self.add_output(output)
    labels = Label(shape=(None, n_tasks, n_classes))
    weights = Weights(shape=(None, n_tasks))
    loss = SoftMaxCrossEntropy(in_layers=[labels, output])
    loss = SoftMaxCrossEntropy(in_layers=[labels, logits])
    weighted_loss = WeightedError(in_layers=[loss, weights])
    if weight_decay_penalty != 0.0:
      weighted_loss = WeightDecay(
@@ -190,6 +191,19 @@ class RobustMultitaskClassifier(TensorGraph):
          feed_dict[self.task_weights[0]] = w_b
        yield feed_dict

  def create_estimator_inputs(self, feature_columns, weight_column, features,
                              labels, mode):
    tensors = {}
    for layer, column in zip(self.features, feature_columns):
      tensors[layer] = tf.feature_column.input_layer(features, [column])
    if weight_column is not None:
      tensors[self.task_weights[0]] = tf.feature_column.input_layer(
          features, [weight_column])
    if labels is not None:
      tensors[self.labels[0]] = tf.one_hot(
          tf.cast(labels, tf.int32), self.n_classes)
    return tensors

  def predict_proba(self, dataset, transformers=[], outputs=None):
    # Results is of shape (n_samples, n_tasks, n_classes)
    return super(RobustMultitaskClassifier, self).predict(
@@ -350,23 +364,3 @@ class RobustMultitaskRegressor(TensorGraph):
          weight_decay_penalty_type,
          in_layers=[weighted_loss])
    self.set_loss(weighted_loss)

  def default_generator(self,
                        dataset,
                        epochs=1,
                        predict=False,
                        deterministic=True,
                        pad_batches=True):
    for epoch in range(epochs):
      for (X_b, y_b, w_b, ids_b) in dataset.iterbatches(
          batch_size=self.batch_size,
          deterministic=deterministic,
          pad_batches=pad_batches):
        feed_dict = dict()
        if y_b is not None and not predict:
          feed_dict[self.labels[0]] = y_b
        if X_b is not None:
          feed_dict[self.features[0]] = X_b
        if w_b is not None and not predict:
          feed_dict[self.task_weights[0]] = w_b
        yield feed_dict
+62 −41
Original line number Diff line number Diff line
@@ -30,14 +30,20 @@ class Sequential(TensorGraph):
  >>> X = np.random.rand(20, 2)
  >>> y = [[0, 1] for x in range(20)]
  >>> dataset = dc.data.NumpyDataset(X, y)
  >>> model = dc.models.Sequential(learning_rate=0.01)                  
  >>> model = dc.models.Sequential(loss='binary_crossentropy', learning_rate=0.01)
  >>> model.add(layers.Dense(out_channels=2))
  >>> model.add(layers.SoftMax())

  Parameters
  ----------
  loss: string
    the loss function to use.  Supported values are 'binary_crossentropy' and 'mse'.
  """

  def __init__(self, **kwargs):
  def __init__(self, loss, **kwargs):
    """Initializes a sequential model
    """
    self._loss_function = loss
    self.num_layers = 0
    self._prev_layer = None
    if "use_queue" in kwargs:
@@ -58,7 +64,7 @@ class Sequential(TensorGraph):
    """
    self._layer_list.append(layer)

  def fit(self, dataset, loss, **kwargs):
  def fit(self, dataset, **kwargs):
    """Fits on the specified dataset.

    If called for the first time, constructs the TensorFlow graph for this
@@ -69,18 +75,19 @@ class Sequential(TensorGraph):
    ----------
    dataset: dc.data.Dataset
      Dataset with data
    loss: string
      Only "binary_crossentropy" or "mse" for now.
    """
    X_shape, y_shape, _, _ = dataset.get_shape()
    # Calling fit() for first time
    if not self.built:
      feature_shape = X_shape[1:]
      label_shape = y_shape[1:]
    self._create_graph((None,) + X_shape[1:], (None,) + y_shape[1:])
    super(Sequential, self).fit(dataset, **kwargs)

  def _create_graph(self, feature_shape, label_shape):
    """This is called to create the full TensorGraph from the added layers."""
    if self.built:
      return  # The graph has already been created.
    # Add in features
      features = Feature(shape=(None,) + feature_shape)
    features = Feature(shape=feature_shape)
    # Add in labels
      labels = Label(shape=(None,) + label_shape)
    labels = Label(shape=label_shape)

    # Add in all layers
    prev_layer = features
@@ -95,10 +102,10 @@ class Sequential(TensorGraph):
    # The last layer is the output of the model
    self.outputs.append(prev_layer)

      if loss == "binary_crossentropy":
    if self._loss_function == "binary_crossentropy":
      smce = SoftMaxCrossEntropy(in_layers=[labels, prev_layer])
      self.set_loss(ReduceMean(in_layers=[smce]))
      elif loss == "mse":
    elif self._loss_function == "mse":
      mse = ReduceSquareDifference(in_layers=[prev_layer, labels])
      self.set_loss(mse)
    else:
@@ -106,7 +113,21 @@ class Sequential(TensorGraph):
      # losses.
      raise ValueError("Unsupported loss.")

    super(Sequential, self).fit(dataset, **kwargs)
    self.build()

  def make_estimator(self,
                     feature_columns,
                     weight_column=None,
                     metrics={},
                     model_dir=None,
                     config=None):
    self._create_graph((None,) + feature_columns[0].shape, None)
    return super(Sequential, self).make_estimator(
        feature_columns,
        weight_column=weight_column,
        metrics=metrics,
        model_dir=model_dir,
        config=config)

  def restore(self, checkpoint=None):
    """Not currently supported.
+6 −2
Original line number Diff line number Diff line
@@ -845,8 +845,9 @@ class TensorGraph(Model):
  def make_estimator(self,
                     feature_columns,
                     weight_column=None,
                     metrics={},
                     model_dir=None,
                     metrics={}):
                     config=None):
    """Construct a Tensorflow Estimator from this model.

    tf.estimator.Estimator is the standard Tensorflow API for representing models.
@@ -875,6 +876,8 @@ class TensorGraph(Model):
    model_dir: str
      the directory in which the Estimator should save files.  If None, this
      defaults to the model's model_dir.
    config: RunConfig
      configuration options for the Estimator
    """
    # Check the inputs.

@@ -953,7 +956,8 @@ class TensorGraph(Model):

    # Create the Estimator.

    return tf.estimator.Estimator(model_fn=model_fn, model_dir=model_dir)
    return tf.estimator.Estimator(
        model_fn=model_fn, model_dir=model_dir, config=config)

  def create_estimator_inputs(self, feature_columns, weight_column, features,
                              labels, mode):
Loading