Commit dbd33526 authored by evanfeinberg's avatar evanfeinberg
Browse files

created tutorial

parent fd57a816
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -44,10 +44,20 @@ class SklearnModel(Model):

  def predict_on_batch(self, X):
    """
    Makes predictions on given batch of new data.
    Makes predictions on batch of data.
    """
    return self.raw_model.predict(X)

  def predict(self, X):
    """
    Makes predictions on dataset.
    """
    # Sets batch_size which the default impl in Model expects
    #TODO(enf/rbharath): This is kludgy. Fix later.
    if "batch_size" not in self.model_params.keys():
      self.model_params["batch_size"] = 32
    return super(SklearnModel, self).predict(X)

  def save(self, out_dir):
    """Saves sklearn model to disk using joblib."""
    super(SklearnModel, self).save(out_dir)
+31 −6
Original line number Diff line number Diff line
@@ -73,9 +73,11 @@ class TestAPI(unittest.TestCase):
        _, _ = evaluator.compute_model_performance(
            test_csv_out, test_stats_out)

  def _featurize_train_test_split(self, splittype, compound_featurizers, complex_featurizers, input_transforms,
                        output_transforms, input_file, tasks, protein_pdb_field=None, ligand_pdb_field=None):

  def _featurize_train_test_split(self, splittype, compound_featurizers, 
                                  complex_featurizers, input_transforms,
                                  output_transforms, input_file, tasks, 
                                  protein_pdb_field=None, ligand_pdb_field=None,
                                  shard_size=100):
    # Featurize input
    featurizers = compound_featurizers + complex_featurizers

@@ -91,7 +93,8 @@ class TestAPI(unittest.TestCase):
    #Featurizes samples and transforms them into NumPy arrays suitable for ML.
    #returns an instance of class FeaturizedSamples()

    samples = featurizer.featurize(input_file, self.feature_dir, self.samples_dir)
    samples = featurizer.featurize(input_file, self.feature_dir, self.samples_dir,
                                   shard_size=shard_size)

    # Splits featurized samples into train/test
    train_samples, test_samples = samples.train_test_split(
@@ -115,7 +118,7 @@ class TestAPI(unittest.TestCase):
    complex_featurizers = []
    input_transforms = []
    output_transforms = ["normalize"]
    model_params = {"batch_size": 5}
    model_params = {}
    task_types = {"log-solubility": "regression"}
    input_file = "example.csv"
    train_dataset, test_dataset = self._featurize_train_test_split(splittype, compound_featurizers, 
@@ -127,6 +130,28 @@ class TestAPI(unittest.TestCase):
    model = SklearnModel(task_types, model_params, model_instance=RandomForestRegressor())
    self._create_model(train_dataset, test_dataset, model)

  def test_singletask_rf_ECFP_regression_sharded_API(self):
    """Test of singletask RF ECFP regression API: sharded edition."""
    splittype = "scaffold"
    compound_featurizers = [CircularFingerprint(size=1024)]
    complex_featurizers = []
    input_transforms = []
    output_transforms = ["normalize"]
    model_params = {}
    task_types = {"label": "regression"}
    input_file = "../../../datasets/pdbbind_core_df.pkl.gz"
    train_dataset, test_dataset = self._featurize_train_test_split(splittype, compound_featurizers, 
                                                    complex_featurizers, input_transforms,
                                                    output_transforms, input_file, task_types.keys(),
                                                    shard_size=50)
    # We set shard size above to force the creation of multiple shards of the data.
    # pdbbind_core has ~200 examples.

    model_params["data_shape"] = train_dataset.get_data_shape()

    from sklearn.ensemble import RandomForestRegressor
    model = SklearnModel(task_types, model_params, model_instance=RandomForestRegressor())
    self._create_model(train_dataset, test_dataset, model)

  def test_singletask_rf_RDKIT_descriptor_regression_API(self):
    """Test of singletask RF RDKIT-descriptor regression API."""
@@ -136,7 +161,7 @@ class TestAPI(unittest.TestCase):
    input_transforms = ["normalize", "truncate"]
    output_transforms = ["normalize"]
    task_types = {"log-solubility": "regression"}
    model_params = {"batch_size": 5}
    model_params = {}
    input_file = "example.csv"
    train_dataset, test_dataset = self._featurize_train_test_split(splittype, compound_featurizers, 
                                                    complex_featurizers, input_transforms,
+4 KiB

File added.

No diff preview for this file type.

+300 −0
Original line number Diff line number Diff line
%% Cell type:markdown id: tags:

Written by Evan Feinberg and Bharath Ramsundar

Copyright 2016, Stanford University

#Welcome to the deepchem tutorial. In this iPython Notebook, one can follow along with the code below to learn how to fit machine learning models with rich predictive power on chemical datasets.

%% Cell type:markdown id: tags:

Overview:

In this tutorial, you will trace an arc from loading a raw dataset to fitting a cutting edge ML technique for predicting binding affinities. This will be accomplished by writing simple commands to access the deepchem Python API, encompassing the following broad steps:

1. Loading a chemical dataset, consisting of a series of protein-ligand complexes.
2. Featurizing each protein-ligand complexes with various featurization schemes.
3. Fitting a series of models with these featurized protein-ligand complexes.
4. Visualizing the results.

%% Cell type:markdown id: tags:

First, let's point to a "dataset" file. This can come in the format of a CSV file or Pandas DataFrame. Regardless
of file format, it must be columnar data, where each row is a molecular system, and each column represents
a different piece of information about that system. For instance, in this example, every row reflects a
protein-ligand complex, and the following columns are present: a unique complex identifier; the SMILES string
of the ligand; the binding affinity (Ki) of the ligand to the protein in the complex; a Python `list` of all lines
in a PDB file for the protein alone; and a Python `list` of all lines in a ligand file for the ligand alone.

This should become clearer with the example.

%% Cell type:code id: tags:

``` python
dataset_file= "../datasets/pdbbind_core_df.pkl.gz"
from deepchem.utils.save import load_from_disk
dataset = load_from_disk(dataset_file)
```

%% Cell type:markdown id: tags:

Let's see what `dataset` looks like:

%% Cell type:code id: tags:

``` python
print("Type of dataset is: %s" % str(type(dataset)))
print("Columns of dataset are: %s" % str(dataset.columns.values))
print("Shape of dataset is: %s" % str(dataset.shape))
```

%% Output

    Type of dataset is: <class 'pandas.core.frame.DataFrame'>
    Columns of dataset are: ['pdb_id' 'smiles' 'complex_id' 'protein_pdb' 'ligand_pdb' 'ligand_mol2'
     'label']
    Shape of dataset is: (193, 7)

%% Cell type:markdown id: tags:

So, let's take a quick look at the first molecule. The first complex contains a protein with a PDB ID of:

%% Cell type:code id: tags:

``` python
complex_1 = dataset.iterrows().next()[1]
complex_1["pdb_id"]
```

%% Output

    '2d3u'

%% Cell type:markdown id: tags:

, and contains a ligand with a SMILES string of:

%% Cell type:code id: tags:

``` python
complex_1["smiles"]
```

%% Output

    'CC1CCCCC1S(O)(O)NC1CC(C2CCC(CN)CC2)SC1C(O)O'

%% Cell type:markdown id: tags:

This complex has a `Ki` of:

%% Cell type:code id: tags:

``` python
complex_1['label']
```

%% Output

    '6.92'

%% Cell type:markdown id: tags:

Now that we're oriented, let's use ML to do some chemistry.

So, step (2) will entail featurizing the dataset.

The available featurizations that come standard with deepchem are ECFP4 fingerprints, RDKit descriptors, and NNScore binding pocket descriptors.

%% Cell type:code id: tags:

``` python
from deepchem.featurizers.fingerprints import CircularFingerprint
from deepchem.featurizers.basic import RDKitDescriptors
from deepchem.featurizers.nnscore import NNScoreComplexFeaturizer

compound_featurizers = [CircularFingerprint(size=1024)]
complex_featurizers = []
```

%% Cell type:markdown id: tags:

Note how we separate our featurizers into those that featurize individual chemical compounds, compound_featurizers, and those that featurize molecular complexes, complex_featurizers.

Now, let's perform the actual featurization. Calling ```featurizer.featurize()``` will return an instance of class ```FeaturizedSamples```. Internally, ```featurizer.featurize()``` (a) computes the user-specified features on the data, (b) transforms the inputs into X and y NumPy arrays suitable for ML algorithms, and (c) constructs a ```FeaturizedSamples()``` instance that has useful methods, such as an iterator, over the featurized data.

%% Cell type:code id: tags:

``` python
#Make a directory in which to store the featurized complexes.
import tempfile, shutil
feature_dir = tempfile.mkdtemp()
samples_dir = tempfile.mkdtemp()
```

%% Cell type:code id: tags:

``` python
from deepchem.featurizers.featurize import DataFeaturizer
featurizers = compound_featurizers + complex_featurizers
featurizer = DataFeaturizer(tasks=["label"],
                            smiles_field="smiles",
                            protein_pdb_field="protein_pdb",
                            ligand_pdb_field="ligand_pdb",
                            compound_featurizers=compound_featurizers,
                            complex_featurizers=complex_featurizers,
                            id_field="complex_id",
                            verbose=False)
featurized_samples = featurizer.featurize(dataset_file, feature_dir, samples_dir,
                                          shard_size=100)
```

%% Cell type:markdown id: tags:

Now, we conduct a train-test split. If you'd like, you can choose `splittype="scaffold"` instead to perform a train-test split based on Bemis-Murcko scaffolds.

%% Cell type:code id: tags:

``` python
splittype = "random"
train_dir, test_dir = tempfile.mkdtemp(), tempfile.mkdtemp()


train_samples, test_samples = featurized_samples.train_test_split(
    splittype, train_dir, test_dir)
```

%% Cell type:markdown id: tags:

We generate separate instances of the Dataset() object to hermetically seal the train dataset from the test dataset. This style lends itself easily to validation-set type hyperparameter searches, which we will illustate in a separate tutorial.

%% Cell type:code id: tags:

``` python
from deepchem.utils.dataset import Dataset
train_dataset = Dataset(data_dir=train_dir, samples=train_samples,
                        featurizers=featurizers, tasks=["label"])
test_dataset = Dataset(data_dir=test_dir, samples=test_samples,
                       featurizers=featurizers, tasks=["label"])
```

%% Cell type:markdown id: tags:

The performance of many ML algorithms hinges greatly on careful data preprocessing. Deepchem comes standard with a few options for such preprocessing.

%% Cell type:code id: tags:

``` python
input_transforms = ["normalize", "truncate"]
output_transforms = ["normalize"]
train_dataset.transform(input_transforms, output_transforms)
test_dataset.transform(input_transforms, output_transforms)
```

%% Cell type:markdown id: tags:

Now, we're ready to do some learning! To set up a model, we will need: (a) a dictionary ```task_types``` that maps a task, in this case ```label```, i.e. the Ki, to the type of the task, in this case ```regression```. For the multitask use case, one will have a series of keys, each of which is a different task (Ki, solubility, renal half-life, etc.) that maps to a different task type (regression or classification).

To fit a deepchem model, first we instantiate one of the provided (or user-written) model classes. In this case, we have a created a convenience class to wrap around any ML model available in Sci-Kit Learn that can in turn be used to interoperate with deepchem. To instantiate an ```SklearnModel```, you will need (a) task_types, (b) model_params, another ```dict``` as illustrated below, and (c) a ```model_instance``` defining the type of model you would like to fit, in this case a ```RandomForestRegressor```.

%% Cell type:code id: tags:

``` python
task_types = {"label": "regression"}
model_params = {"data_shape": train_dataset.get_data_shape()}

from sklearn.ensemble import RandomForestRegressor
from deepchem.models.standard import SklearnModel
model = SklearnModel(task_types, model_params, model_instance=RandomForestRegressor())
model.fit(train_dataset)
model_dir = tempfile.mkdtemp()
model.save(model_dir)
```

%% Cell type:code id: tags:

``` python
from deepchem.utils.evaluate import Evaluator
evaluator = Evaluator(model, train_dataset, verbose=True)
with tempfile.NamedTemporaryFile() as train_csv_out:
  with tempfile.NamedTemporaryFile() as train_stats_out:
    df, r2score = evaluator.compute_model_performance(
        train_csv_out, train_stats_out)
```

%% Output

    Saving predictions to <open file '<fdopen>', mode 'w+b' at 0x7f9aeae9ac00>
    Saving model performance scores to <open file '<fdopen>', mode 'w+b' at 0x7f9aeae9ab70>

%% Cell type:code id: tags:

``` python
r2score
```

%% Output

      task_name  r2_score  rms_error
    0     label  0.826262   0.939944

%% Cell type:markdown id: tags:

While we have computed three separate featurizations -- ECFP4, RDKitDescriptors, and NNScore -- in this example we choose to use the RDKitDescriptors featurization. This will serve as a baseline as you apply more advanced featurization schemes.

%% Cell type:markdown id: tags:

It is essential for many ML methods, depending on the data type, to perform preprocessing in order to attain optimal performance. Here, we choose to normalize and truncate the inputs (X, the featurized complexes) while normalizing the output (y, the binding affinities):

%% Cell type:markdown id: tags:

To recap, the initial dataset has now been featurized, split into train and test sets, and transformed. We are now ready to learn some chemistry! To warm up, let's apply a more traditional but quite robust statistical learning technique: random forests.

%% Cell type:markdown id: tags:

Now that we've fit an Random Forest Regressor on the data, let's evaluate its performance:

%% Cell type:markdown id: tags:

Let's compare this to performance with a deep neural network.

%% Cell type:code id: tags:

``` python
from deepchem.models import Model

task_type = "regression"
model_params = {"activation": "relu",
              "dropout": 0.,
              "momentum": .9, "nesterov": False,
              "decay": 1e-4, "batch_size": 5,
              "nb_epoch": 10}
model_name = "singletask_deep_regressor"

nb_hidden_vals = [10, 100]
learning_rate_vals = [.01, .001]
init_vals = ["glorot_uniform"]
hyperparameters = [nb_hidden_vals, learning_rate_vals, init_vals]
hyperparameter_rows = []
for hyperparameter_tuple in itertools.product(*hyperparameters):
    nb_hidden, learning_rate, init = hyperparameter_tuple
    model_params["nb_hidden"] = nb_hidden
    model_params["learning_rate"] = learning_rate
    model_params["init"] = init

    model_dir = tempfile.mkdtemp()

    r2_score = create_and_eval_model(train_dataset, test_dataset, task_type,
                                     model_params, model_name, model_dir, tasks)

    print("%s: %s" % (hyperparameter_tuple, r2_score))
    hyperparameter_rows.append(list(hyperparameter_tuple) + [r2_score])

    shutil.rmtree(model_dir)

hyperparameter_df = pd.DataFrame(hyperparameter_rows,
                               columns=('nb_hidden', 'learning_rate',
                                        'init', 'r2_score'))

hyperparameter_df
```
+5 −0
Original line number Diff line number Diff line
,nb_hidden,learning_rate,init,r2_score
0,10,0.01,glorot_uniform,
1,10,0.001,glorot_uniform,0.0545074482377
2,100,0.01,glorot_uniform,
3,100,0.001,glorot_uniform,0.261622878258
Loading