Commit 6ea00875 authored by Bharath Ramsundar's avatar Bharath Ramsundar
Browse files

Merge pull request #46 from rbharath/predict-new

Prediction on external data + simple-test-suite
parents 3d6d26d0 578bea4c
Loading
Loading
Loading
Loading
+8 −15
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ class Model(object):
    self.model_type = model_type
    self.task_types = task_types
    self.model_params = model_params
    self.raw_model = None

  def fit_on_batch(self, X, y, w):
    """
@@ -49,7 +50,7 @@ class Model(object):
    """
    Return raw model.
    """
    return(self.raw_model)
    return self.raw_model

  @staticmethod
  def get_model_filename(out_dir):
@@ -117,7 +118,6 @@ class Model(object):
              "model_type": self.model_type}
    save_to_disk(params, Model.get_params_filename(out_dir))

  # TODO(rbharath): This training is currently broken w.r.t minibatches! Fix.
  def fit(self, dataset):
    """
    Fits a model on data in a Dataset object.
@@ -130,7 +130,8 @@ class Model(object):
      for i, (X, y, w, _) in enumerate(dataset.itershards()):
        print("Training on batch-%s/epoch-%s" % (str(i+1), str(epoch+1)))
        nb_sample = np.shape(X)[0]
        interval_points = np.linspace(0,nb_sample, np.ceil(float(nb_sample)/batch_size)+1).astype(int)
        interval_points = np.linspace(
            0, nb_sample, np.ceil(float(nb_sample)/batch_size)+1, dtype=int)
        for j in range(len(interval_points)-1):
          indices = range(interval_points[j], interval_points[j+1])
          X_batch = X[indices, :]
@@ -138,9 +139,6 @@ class Model(object):
          w_batch = w[indices]
          self.fit_on_batch(X_batch, y_batch, w_batch)

  # TODO(rbharath): What does this function do when y is not provided. Suspect
  # it breaks. Need to fix.

  # TODO(rbharath): The structure of the produced df might be
  # complicated. Better way to model?
  def predict(self, dataset):
@@ -154,21 +152,16 @@ class Model(object):
                    + ["y_means", "y_stds"])
    pred_y_df = pd.DataFrame(columns=column_names)

    # TODO(rbharath/enf): This is only for GPU models, and is currently depends
    # on magic numbers.
    MAX_GPU_RAM = float(691007488/50)
    batch_size = self.model_params["batch_size"]
    for (X, y, w, ids) in dataset.itershards():
      if sys.getsizeof(X) > MAX_GPU_RAM:
        nb_block = float(sys.getsizeof(X))/MAX_GPU_RAM
      nb_sample = np.shape(X)[0]
        interval_points = np.linspace(0,nb_sample,nb_block+1).astype(int)
      interval_points = np.linspace(
          0, nb_sample, np.ceil(float(nb_sample)/batch_size)+1, dtype=int)
      y_preds = []
        for j in range(0,len(interval_points)-1):
      for j in range(len(interval_points)-1):
        indices = range(interval_points[j], interval_points[j+1])
        y_preds.append(self.predict_on_batch(X[indices, :]))
      y_pred = np.concatenate(y_preds)
      else:
        y_pred = self.predict_on_batch(X)
      y_pred = np.reshape(y_pred, np.shape(y))

      shard_df = pd.DataFrame(columns=column_names)
+13 −3
Original line number Diff line number Diff line
@@ -66,7 +66,8 @@ class MultiTaskDNN(KerasModel):
          Dense(model_params["nb_hidden"], init='uniform',
                activation=model_params["activation"]),
          name="dense", input="input")
      model.add_node(Dropout(model_params["dropout"]), name="dropout",
      model.add_node(Dropout(model_params["dropout"]),
                     name="dropout",
                     input="dense")
      top_layer = "dropout"
      for ind, task in enumerate(sorted_tasks):
@@ -96,6 +97,7 @@ class MultiTaskDNN(KerasModel):
      self.raw_model = model

  def get_data_dict(self, X, y=None):
    """Wrap data X in dict for graph computations (Keras graph only for now)."""
    data = {}
    data["input"] = X
    for ind, task in enumerate(sorted(self.task_types.keys())):
@@ -110,7 +112,7 @@ class MultiTaskDNN(KerasModel):
  def get_sample_weight(self, w):
    """Get dictionaries needed to fit models"""
    sample_weight = {}
    for ind, task in enumerate(sorted(self.task_types.keys())):
    for ind in range(len(sorted(self.task_types.keys()))):
      sample_weight["task%d" % ind] = w[:, ind]
    return sample_weight

@@ -124,6 +126,7 @@ class MultiTaskDNN(KerasModel):
    data = self.get_data_dict(X, y)
    sample_weight = self.get_sample_weight(w)
    loss = self.raw_model.train_on_batch(data, sample_weight=sample_weight)
    return loss

  def predict_on_batch(self, X):
    """
@@ -136,8 +139,15 @@ class MultiTaskDNN(KerasModel):
    nb_tasks = len(sorted_tasks)
    y_pred = np.zeros((nb_samples, nb_tasks))
    for ind, task in enumerate(sorted_tasks):
      task_type = self.task_types[task]
      taskname = "task%d" % ind
      y_pred[:,ind] = np.squeeze(y_pred_dict[taskname])
      if task_type == "classification":
        # Class probabilities are predicted for classification outputs. Instead,
        # output the most likely class.
        y_pred_task = np.squeeze(np.argmax(y_pred_dict[taskname], axis=1))
      else:
        y_pred_task = np.squeeze(y_pred_dict[taskname])
      y_pred[:, ind] = y_pred_task
    y_pred = np.squeeze(y_pred)
    return y_pred

+8 −1
Original line number Diff line number Diff line
@@ -15,16 +15,23 @@ from deepchem.models import Model
from deepchem.models.deep import KerasModel

def shuffle_shape(shape):
  """
  Shuffle shape of form (N, N, N, C) into (C, N, N, N).
  """
  (axis_length, _, _, n_channels) = shape
  shuffled_shape = (n_channels, axis_length, axis_length, axis_length)
  return shuffled_shape

def shuffle_data(X):
  """
  Make data of shape (C, N, N, N) from (N, N, N, C)

  C is n_channels, N is axis_length.
  """
  (n_samples, axis_length, _, _, n_channels) = np.shape(X)
  X = np.reshape(X, (n_samples, n_channels, axis_length, axis_length, axis_length))
  return X


class DockingDNN(KerasModel):
  """
  Wrapper class for fitting 3D convolutional networks for deep docking.
+1 −1
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ class SklearnModel(Model):
      Xs.append(X)
      ys.append(y)
    X = np.concatenate(Xs)
    y = np.concatenate(ys)
    y = np.concatenate(ys).ravel()
    self.raw_model.fit(X, y)

  def predict_on_batch(self, X):
+95 −47
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from __future__ import unicode_literals
import argparse
import glob
import os
import multiprocessing as mp
from functools import partial
from deepchem.utils.featurize import DataFeaturizer
from deepchem.utils.featurize import FeaturizedSamples
@@ -43,10 +44,6 @@ def add_featurize_group(featurize_cmd):
  featurize_group.add_argument(
      "--threshold", type=float, default=None,
      help="If specified, will be used to binarize real-valued target-fields.")
  featurize_group.add_argument(
      "--feature-dir", type=str, required=0,
      help="Directory where featurized dataset will be stored.\n"
           "Will be created if does not exist")
  featurize_group.add_argument(
      "--parallel", type=float, default=None,
      help="Use multiprocessing will be used to parallelize featurization.")
@@ -63,10 +60,6 @@ def add_transforms_group(cmd):
      choices=["normalize", "log"],
      help="Supported transforms are 'log' and 'normalize'. 'None' will be taken\n"
           "to mean no transforms are required.")
  transform_group.add_argument(
      "--mode", default="singletask",
      choices=["singletask", "multitask"],
      help="Type of model being built.")
  transform_group.add_argument(
      "--feature-types", nargs="+", required=1,
      choices=["user-specified-features", "ECFP", "RDKIT-descriptors"],
@@ -128,6 +121,7 @@ def add_model_group(fit_cmd):
      "--nesterov", action="store_true",
      help="If set, use Nesterov acceleration.")


def add_model_command(subparsers):
  """Adds flags for model subcommand."""
  model_cmd = subparsers.add_parser(
@@ -136,6 +130,9 @@ def add_model_command(subparsers):
  model_cmd.add_argument(
      "--featurize", action="store_true",
      help="Perform the featurization step.")
  model_cmd.add_argument(
      "--generate-dataset", action="store_true",
      help="Generate dataset from featurized data.")
  model_cmd.add_argument(
      "--train-test-split", action="store_true",
      help="Perform the train-test-split step.")
@@ -146,8 +143,20 @@ def add_model_command(subparsers):
      "--eval", action="store_true",
      help="Perform model eval step.")
  model_cmd.add_argument(
      "--base-dir", type=str, required=1,
      "--eval-full", action="store_true",
      help="Evaluate model on full dataset.")
  model_cmd.add_argument(
      "--base-dir", type=str, default=None,
      help="The base directory for the model.")
  model_cmd.add_argument(
      "--feature-dir", type=str, default=None,
      help="The feature storage directory for the model.")
  model_cmd.add_argument(
      "--data-dir", type=str, default=None,
      help="The data storage directory for the model.")
  model_cmd.add_argument(
      "--model-dir", type=str, default=None,
      help="The model storage directory for the model.")
  add_featurize_group(model_cmd)

  add_transforms_group(model_cmd)
@@ -163,66 +172,99 @@ def extract_model_params(args):
            "activation", "momentum", "nesterov"]

  model_params = {param : getattr(args, param) for param in params}
  return(model_params)
  return model_params

def ensure_exists(dirs):
  """Creates dirs if they don't exist."""
  for directory in dirs:
    if not os.path.exists(directory):
      os.makedirs(directory)

def create_model(args):
  """Creates a model"""
  base_dir = args.base_dir
  feature_dir = os.path.join(base_dir, "features")
  data_dir = os.path.join(base_dir, "data")
  model_dir = os.path.join(base_dir, "model")
  ensure_exists([base_dir, feature_dir, data_dir, model_dir])

  model_name = args.model
  if args.base_dir is not None:
    feature_dir = os.path.join(args.base_dir, "features")
    data_dir = os.path.join(args.base_dir, "data")
    model_dir = os.path.join(args.base_dir, "model")
    ensure_exists([args.base_dir, feature_dir, data_dir, model_dir])
  else:
    if (args.model_dir is None or
        args.data_dir is None or
        args.feature_dir is None):
      raise ValueError("If base-dir not specified, must specify "
                       "feature-dir, data-dir, model-dir.")

    feature_dir, model_dir, data_dir = (args.feature_dir, args.model_dir,
                                        args.data_dir)
    ensure_exists([feature_dir, data_dir, model_dir])

  if args.featurize:
    print("+++++++++++++++++++++++++++++++++")
    print("Perform featurization")
  if args.featurize:
    featurize_inputs(
        feature_dir, args.input_files,
        args.user_specified_features, args.tasks,
        args.smiles_field, args.split_field, args.id_field, args.threshold,
        args.parallel)
        feature_dir, data_dir, args.input_files, args.user_specified_features,
        args.tasks, args.smiles_field, args.split_field, args.id_field,
        args.threshold, args.parallel)

  if args.generate_dataset:
    print("+++++++++++++++++++++++++++++++++")
  print("Perform train-test split")
  paths = [feature_dir]
    print("Generate dataset for featurized samples")
    samples_dir = os.path.join(data_dir, "samples")
    samples = FeaturizedSamples(samples_dir, reload_data=True)

    print("Generating dataset.")
    full_data_dir = os.path.join(data_dir, "full-data")
    full_dataset = Dataset(full_data_dir, samples, args.feature_types)

    print("Transform data.")
    full_dataset.transform(args.input_transforms, args.output_transforms)


  if args.train_test_split:
    print("+++++++++++++++++++++++++++++++++")
    print("Perform train-test split")
    train_test_split(
        paths, args.input_transforms, args.output_transforms, args.feature_types,
        args.splittype, args.mode, data_dir)
        args.input_transforms, args.output_transforms, args.feature_types,
        args.splittype, data_dir)

  if args.fit:
    print("+++++++++++++++++++++++++++++++++")
    print("Fit model")
  if args.fit:
    model_params = extract_model_params(args)
    fit_model(
        model_name, model_params, model_dir, data_dir)

  if args.eval:
    print("+++++++++++++++++++++++++++++++++")
    print("Eval Model on Train")
    print("-------------------")
  if args.eval:
    train_dir = os.path.join(data_dir, "train-data")
    csv_out_train = os.path.join(data_dir, "train.csv")
    stats_out_train = os.path.join(data_dir, "train-stats.txt")
    csv_out_test = os.path.join(data_dir, "test.csv")
    stats_out_test = os.path.join(data_dir, "test-stats.txt")
    train_dir = os.path.join(data_dir, "train-data")
    eval_trained_model(
        model_name, model_dir, train_dir, csv_out_train,
        stats_out_train, split="train")
        stats_out_train)

    print("Eval Model on Test")
    print("------------------")
  if args.eval:
    test_dir = os.path.join(data_dir, "test-data")
    csv_out_test = os.path.join(data_dir, "test.csv")
    stats_out_test = os.path.join(data_dir, "test-stats.txt")
    eval_trained_model(
        model_name, model_dir, test_dir, csv_out_test,
        stats_out_test, split="test")
        stats_out_test)

  if args.eval_full:
    print("+++++++++++++++++++++++++++++++++")
    print("Eval Model on Full Dataset")
    print("--------------------------")
    full_data_dir = os.path.join(data_dir, "full-data")
    csv_out_full = os.path.join(data_dir, "full.csv")
    stats_out_full = os.path.join(data_dir, "full-stats.txt")
    eval_trained_model(
        model_name, model_dir, full_data_dir, csv_out_full,
        stats_out_full)

def parse_args(input_args=None):
  """Parse command-line arguments."""
@@ -231,10 +273,11 @@ def parse_args(input_args=None):
  add_model_command(subparsers)
  return parser.parse_args(input_args)

def featurize_inputs(feature_dir, input_files,
def featurize_inputs(feature_dir, data_dir, input_files,
                     user_specified_features, tasks, smiles_field,
                     split_field, id_field, threshold, parallel):

  """Allows for parallel data featurization."""
  featurize_input_partial = partial(featurize_input,
                                    feature_dir=feature_dir,
                                    user_specified_features=user_specified_features,
@@ -252,6 +295,12 @@ def featurize_inputs(feature_dir, input_files,
    for input_file in input_files:
      featurize_input_partial(input_file)

  dataset_files = glob.glob(os.path.join(feature_dir, "*.joblib"))

  print("Writing samples to disk.")
  samples_dir = os.path.join(data_dir, "samples")
  FeaturizedSamples(samples_dir, dataset_files)

def featurize_input(input_file, feature_dir, user_specified_features, tasks,
                    smiles_field, split_field, id_field, threshold):
  """Featurizes raw input data."""
@@ -266,29 +315,26 @@ def featurize_input(input_file, feature_dir, user_specified_features, tasks,
      feature_dir, "%s.joblib" %(os.path.splitext(os.path.basename(input_file))[0]))
  featurizer.featurize(input_file, FeaturizedSamples.feature_types, out)

def train_test_split(paths, input_transforms, output_transforms,
                     feature_types, splittype, mode, data_dir):
def train_test_split(input_transforms, output_transforms,
                     feature_types, splittype, data_dir):
  """Saves transformed model."""

  dataset_files = []
  for path in paths:
    dataset_files += glob.glob(os.path.join(path, "*.joblib"))
  print("Loading featurized data.")
  samples_dir = os.path.join(data_dir, "samples")
  samples = FeaturizedSamples(samples_dir, dataset_files, reload=False)
  samples = FeaturizedSamples(samples_dir, reload_data=True)

  print("Split data into train/test")
  train_samples_dir = os.path.join(data_dir, "train-samples")
  test_samples_dir = os.path.join(data_dir, "test-samples")
  train_samples, test_samples = samples.train_test_split(splittype,
    train_samples_dir, test_samples_dir)
  train_samples, test_samples = samples.train_test_split(
      splittype, train_samples_dir, test_samples_dir)

  train_data_dir = os.path.join(data_dir, "train-data")
  test_data_dir = os.path.join(data_dir, "test-data")

  print("Generating train data.")
  print("Generating train dataset.")
  train_dataset = Dataset(train_data_dir, train_samples, feature_types)
  print("Generating test data.")

  print("Generating test dataset.")
  test_dataset = Dataset(test_data_dir, test_samples, feature_types)

  print("Transforming train data.")
@@ -311,13 +357,15 @@ def fit_model(model_name, model_params, model_dir, data_dir):
  model.save(model_dir)

def eval_trained_model(model_type, model_dir, data_dir,
                       csv_out, stats_out, split="test"):
                       csv_out, stats_out):
  """Evaluates a trained model on specified data."""
  model = Model.load(model_type, model_dir)
  data = Dataset(data_dir)

  evaluator = Evaluator(model, data, verbose=True)
  evaluator.compute_model_performance(csv_out, stats_out)
  _, perf_df = evaluator.compute_model_performance(csv_out, stats_out)
  print("Model Performance.")
  print(perf_df)

def main():
  """Invokes argument parser."""
Loading