Source code for garage.tf.optimizers.lbfgs_optimizer

"""Limited-memory BFGS (L-BFGS) optimizer."""
import time

import scipy.optimize
import tensorflow as tf

from garage.tf.misc import tensor_utils
from garage.tf.optimizers.utils import LazyDict


[docs]class LbfgsOptimizer: """Limited-memory BFGS (L-BFGS) optimizer. Performs unconstrained optimization via L-BFGS. Args: max_opt_itr (int): Maximum iteration for update. callback (callable): Function to call during optimization. """ def __init__(self, max_opt_itr=20, callback=None): self._max_opt_itr = max_opt_itr self._opt_fun = None self._target = None self._callback = callback
[docs] def update_opt(self, loss, target, inputs, extra_inputs=None, name='LbfgsOptimizer', **kwargs): """Construct operation graph for the optimizer. Args: loss (tf.Tensor): Loss objective to minimize. target (object): Target object to optimize. The object should implemenet `get_params()` and `get_param_values`. inputs (list[tf.Tensor]): List of input placeholders. extra_inputs (list[tf.Tensor]): List of extra input placeholders. name (str): Name scope. kwargs (dict): Extra unused keyword arguments. Some optimizers have extra input, e.g. KL constraint. """ del kwargs self._target = target params = target.get_params() with tf.name_scope(name): def get_opt_output(): """Helper function to construct graph. Returns: list[tf.Tensor]: Loss and gradient tensor. """ with tf.name_scope('get_opt_output'): flat_grad = tensor_utils.flatten_tensor_variables( tf.gradients(loss, params)) return [ tf.cast(loss, tf.float64), tf.cast(flat_grad, tf.float64) ] if extra_inputs is None: extra_inputs = list() self._opt_fun = LazyDict( f_loss=lambda: tensor_utils.compile_function( inputs + extra_inputs, loss), f_opt=lambda: tensor_utils.compile_function( inputs=inputs + extra_inputs, outputs=get_opt_output(), ))
[docs] def loss(self, inputs, extra_inputs=None): """The loss. Args: inputs (list[numpy.ndarray]): List of input values. extra_inputs (list[numpy.ndarray]): List of extra input values. Returns: float: Loss. Raises: Exception: If loss function is None, i.e. not defined. """ if self._opt_fun is None: raise Exception( 'Use update_opt() to setup the loss function first.') if extra_inputs is None: extra_inputs = list() return self._opt_fun['f_loss'](*(list(inputs) + list(extra_inputs)))
[docs] def optimize(self, inputs, extra_inputs=None, name='optimize'): """Perform optimization. Args: inputs (list[numpy.ndarray]): List of input values. extra_inputs (list[numpy.ndarray]): List of extra input values. name (str): Name scope. Raises: Exception: If loss function is None, i.e. not defined. """ if self._opt_fun is None: raise Exception( 'Use update_opt() to setup the loss function first.') with tf.name_scope(name): f_opt = self._opt_fun['f_opt'] if extra_inputs is None: extra_inputs = list() def f_opt_wrapper(flat_params): """Helper function to set parameters values. Args: flat_params (numpy.ndarray): Flattened parameter values. Returns: list[tf.Tensor]: Loss and gradient tensor. """ self._target.set_param_values(flat_params) ret = f_opt(*inputs) return ret itr = [0] start_time = time.time() if self._callback: def opt_callback(params): """Callback function wrapper. Args: params (numpy.ndarray): Parameters. """ loss = self._opt_fun['f_loss'](*(inputs + extra_inputs)) elapsed = time.time() - start_time self._callback( dict( loss=loss, params=params, itr=itr[0], elapsed=elapsed, )) itr[0] += 1 else: opt_callback = None scipy.optimize.fmin_l_bfgs_b( func=f_opt_wrapper, x0=self._target.get_param_values(), maxiter=self._max_opt_itr, callback=opt_callback, )
def __getstate__(self): """Object.__getstate__. Returns: dict: The state to be pickled for the instance. """ new_dict = self.__dict__.copy() del new_dict['_opt_fun'] return new_dict