Commit 7ed4af0b authored by Bharath Ramsundar's avatar Bharath Ramsundar Committed by GitHub
Browse files

Merge pull request #705 from peastman/optimizers

Created API for TensorGraph optimizers
parents 5172a182 998c5322
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import deepchem as dc
import deepchem.rl.envs.tictactoe
from deepchem.models.tensorgraph.layers import Flatten, Dense, SoftMax, \
    BatchNorm, Squeeze
from deepchem.models.tensorgraph.optimizers import Adam


class TicTacToePolicy(dc.rl.Policy):
@@ -66,8 +67,7 @@ def eval_tic_tac_toe(value_weight,
        entropy_weight=0.01,
        value_weight=value_weight,
        model_dir=model_dir,
        optimizer=dc.models.tensorgraph.TFWrapper(
            tf.train.AdamOptimizer, learning_rate=0.001))
        optimizer=Adam(learning_rate=0.001))
    try:
      a3c.restore()
    except:
+170 −0
Original line number Diff line number Diff line
"""Optimizers and related classes for use with TensorGraph."""

import tensorflow as tf


class Optimizer(object):
  """An algorithm for optimizing a TensorGraph based model.

  This is an abstract class.  Subclasses represent specific optimization algorithms.
  """

  def _create_optimizer(self, global_step):
    """Construct the TensorFlow optimizer.

    Parameters
    ----------
    global_step: tensor
      a tensor containing the global step index during optimization, used for learning rate decay

    Returns
    -------
    a new TensorFlow optimizer implementing the algorithm
    """
    raise NotImplemented("Subclasses must implement this")


class LearningRateSchedule(object):
  """A schedule for changing the learning rate over the course of optimization.

  This is an abstract class.  Subclasses represent specific schedules.
  """

  def _create_tensor(self, global_step):
    """Construct a tensor that equals the learning rate.

    Parameters
    ----------
    global_step: tensor
      a tensor containing the global step index during optimization

    Returns
    -------
    a tensor that equals the learning rate
    """
    raise NotImplemented("Subclasses must implement this")


class Adam(Optimizer):
  """The Adam optimization algorithm."""

  def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999,
               epsilon=1e-08):
    """Construct an Adam optimizer.

    Parameters
    ----------
    learning_rate: float or LearningRateSchedule
      the learning rate to use for optimization
    beta1: float
      a parameter of the Adam algorithm
    beta2: float
      a parameter of the Adam algorithm
    epsilon: float
      a parameter of the Adam algorithm
    """
    self.learning_rate = learning_rate
    self.beta1 = beta1
    self.beta2 = beta2
    self.epsilon = epsilon

  def _create_optimizer(self, global_step):
    if isinstance(self.learning_rate, LearningRateSchedule):
      learning_rate = self.learning_rate._create_tensor(global_step)
    else:
      learning_rate = self.learning_rate
    return tf.train.AdamOptimizer(
        learning_rate=learning_rate,
        beta1=self.beta1,
        beta2=self.beta2,
        epsilon=self.epsilon)


class GradientDescent(Optimizer):
  """The gradient descent optimization algorithm."""

  def __init__(self, learning_rate=0.001):
    """Construct a gradient descent optimizer.

    Parameters
    ----------
    learning_rate: float or LearningRateSchedule
      the learning rate to use for optimization
    """
    self.learning_rate = learning_rate

  def _create_optimizer(self, global_step):
    if isinstance(self.learning_rate, LearningRateSchedule):
      learning_rate = self.learning_rate._create_tensor(global_step)
    else:
      learning_rate = self.learning_rate
    return tf.train.GradientDescentOptimizer(learning_rate=learning_rate)


class ExponentialDecay(LearningRateSchedule):
  """A learning rate that decreases exponentially with the number of training steps."""

  def __init__(self, initial_rate, decay_rate, decay_steps, staircase=True):
    """Create an exponentially decaying learning rate.

    The learning rate starts as initial_rate.  Every decay_steps training steps, it is multiplied by decay_rate.

    Parameters
    ----------
    initial_rate: float
      the initial learning rate
    decay_rate: float
      the base of the exponential
    decay_steps: int
      the number of training steps over which the rate decreases by decay_rate
    staircase: bool
      if True, the learning rate decreases by discrete jumps every decay_steps.
      if False, the learning rate decreases smoothly every step
    """
    self.initial_rate = initial_rate
    self.decay_rate = decay_rate
    self.decay_steps = decay_steps
    self.staircase = staircase

  def _create_tensor(self, global_step):
    return tf.train.exponential_decay(
        learning_rate=self.initial_rate,
        global_step=global_step,
        decay_rate=self.decay_rate,
        decay_steps=self.decay_steps,
        staircase=self.staircase)


class PolynomialDecay(LearningRateSchedule):
  """A learning rate that decreases from an initial value to a final value over a fixed number of training steps."""

  def __init__(self, initial_rate, final_rate, decay_steps, power=1.0):
    """Create a smoothly decaying learning rate.

    The learning rate starts as initial_rate.  It smoothly decreases to final_rate over decay_steps training steps.
    It decays as a function of (1-step/decay_steps)**power.  Once the final rate is reached, it remains there for
    the rest of optimization.

    Parameters
    ----------
    initial_rate: float
      the initial learning rate
    final_rate: float
      the final learning rate
    decay_steps: int
      the number of training steps over which the rate decreases from initial_rate to final_rate
    power: float
      the exponent controlling the shape of the decay
    """
    self.initial_rate = initial_rate
    self.final_rate = final_rate
    self.decay_steps = decay_steps
    self.power = power

  def _create_tensor(self, global_step):
    return tf.train.polynomial_decay(
        learning_rate=self.initial_rate,
        end_learning_rate=self.final_rate,
        global_step=global_step,
        decay_steps=self.decay_steps,
        power=self.power)
+8 −12
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from deepchem.data import NumpyDataset
from deepchem.metrics import to_one_hot, from_one_hot
from deepchem.models.models import Model
from deepchem.models.tensorgraph.layers import InputFifoQueue, Label, Feature, Weights
from deepchem.models.tensorgraph.optimizers import Adam
from deepchem.trans import undo_transforms
from deepchem.utils.evaluate import GeneratorEvaluator
from deepchem.feat.graph_features import ConvMolFeaturizer
@@ -54,6 +55,8 @@ class TensorGraph(Model):
    graph: tensorflow.Graph
      the Graph in which to create Tensorflow objects.  If None, a new Graph
      is created.
    learning_rate: float or LearningRateSchedule
      the learning rate to use for optimization
    kwargs
    """

@@ -67,12 +70,8 @@ class TensorGraph(Model):
    self.loss = None
    self.built = False
    self.queue_installed = False
    self.optimizer = TFWrapper(
        tf.train.AdamOptimizer,
        learning_rate=learning_rate,
        beta1=0.9,
        beta2=0.999,
        epsilon=1e-7)
    self.optimizer = Adam(
        learning_rate=learning_rate, beta1=0.9, beta2=0.999, epsilon=1e-7)

    # Singular place to hold Tensor objects which don't serialize
    # These have to be reconstructed on restoring from pickle
@@ -425,11 +424,7 @@ class TensorGraph(Model):
    self.outputs.append(layer)

  def set_optimizer(self, optimizer):
    """Set the optimizer to use for fitting.

    The argument should be a callable object (most often a TFWrapper) that constructs
    a Tensorflow optimizer when called.
    """
    """Set the optimizer to use for fitting."""
    self.optimizer = optimizer

  def get_pickling_errors(self, obj, seen=None):
@@ -562,7 +557,8 @@ class TensorGraph(Model):
    elif obj == "FileWriter":
      self.tensor_objects['FileWriter'] = tf.summary.FileWriter(self.model_dir)
    elif obj == 'Optimizer':
      self.tensor_objects['Optimizer'] = self.optimizer()
      self.tensor_objects['Optimizer'] = self.optimizer._create_optimizer(
          self._get_tf('GlobalStep'))
    elif obj == 'train_op':
      self.tensor_objects['train_op'] = self._get_tf('Optimizer').minimize(
          self.loss.out_tensor, global_step=self._get_tf('GlobalStep'))
+41 −0
Original line number Diff line number Diff line
import deepchem.models.tensorgraph.optimizers as optimizers
import tensorflow as tf
from tensorflow.python.framework import test_util


class TestLayers(test_util.TensorFlowTestCase):
  """Test optimizers and related classes."""

  def test_adam(self):
    """Test creating an Adam optimizer."""
    opt = optimizers.Adam(learning_rate=0.01)
    with self.test_session() as sess:
      global_step = tf.Variable(0)
      tfopt = opt._create_optimizer(global_step)
      assert isinstance(tfopt, tf.train.AdamOptimizer)

  def test_gradient_descent(self):
    """Test creating a Gradient Descent optimizer."""
    opt = optimizers.GradientDescent(learning_rate=0.01)
    with self.test_session() as sess:
      global_step = tf.Variable(0)
      tfopt = opt._create_optimizer(global_step)
      assert isinstance(tfopt, tf.train.GradientDescentOptimizer)

  def test_exponential_decay(self):
    """Test creating an optimizer with an exponentially decaying learning rate."""
    rate = optimizers.ExponentialDecay(
        initial_rate=0.001, decay_rate=0.99, decay_steps=10000)
    opt = optimizers.Adam(learning_rate=rate)
    with self.test_session() as sess:
      global_step = tf.Variable(0)
      tfopt = opt._create_optimizer(global_step)

  def test_polynomial_decay(self):
    """Test creating an optimizer with a polynomially decaying learning rate."""
    rate = optimizers.PolynomialDecay(
        initial_rate=0.001, final_rate=0.0001, decay_steps=10000)
    opt = optimizers.Adam(learning_rate=rate)
    with self.test_session() as sess:
      global_step = tf.Variable(0)
      tfopt = opt._create_optimizer(global_step)
+5 −9
Original line number Diff line number Diff line
@@ -12,7 +12,8 @@ from deepchem.data.datasets import Databag
from deepchem.models.tensorgraph.layers import Dense, SoftMaxCrossEntropy, ReduceMean, SoftMax
from deepchem.models.tensorgraph.layers import Feature, Label
from deepchem.models.tensorgraph.layers import ReduceSquareDifference
from deepchem.models.tensorgraph.tensor_graph import TensorGraph, TFWrapper
from deepchem.models.tensorgraph.tensor_graph import TensorGraph
from deepchem.models.tensorgraph.optimizers import GradientDescent, ExponentialDecay


class TestTensorGraph(unittest.TestCase):
@@ -179,14 +180,9 @@ class TestTensorGraph(unittest.TestCase):
    tg.add_output(output)
    tg.set_loss(loss)
    global_step = tg.get_global_step()

    def optimizer_function():
      starter_learning_rate = 0.1
      learning_rate = tf.train.exponential_decay(
          starter_learning_rate, global_step, 100000, 0.96, staircase=True)
      return tf.train.GradientDescentOptimizer(learning_rate)

    tg.set_optimizer(TFWrapper(optimizer_function))
    learning_rate = ExponentialDecay(
        initial_rate=0.1, decay_rate=0.96, decay_steps=100000)
    tg.set_optimizer(GradientDescent(learning_rate=learning_rate))
    tg.fit(dataset, nb_epoch=1000)
    prediction = np.squeeze(tg.predict_proba_on_batch(X))
    tg.save()
Loading