Example #1
0
    def _minimize(self):
        if self.return_errors:
            raise NotImplementedError(
                "Tensorflow minimizer does not yet support return errors")

        if self.use_hessian:
            # This optimizer can use the hessian information
            # Compute the inverse hessian at the guess
            inv_hess = self.lf.inverse_hessian(self.guess,
                                               omit_grads=tuple(
                                                   self.fix.keys()))
            # Explicitly symmetrize the matrix
            inv_hess = fd.symmetrize_matrix(inv_hess)
            inv_hess = fd.np_to_tf(inv_hess)

            # Hessian cannot be used later in the inference
            self.use_hessian = False
        else:
            inv_hess = None

        kwargs = self.optimizer_kwargs

        x_guess = fd.np_to_tf(self._dict_to_array(self.guess))

        return tfp.optimizer.bfgs_minimize(
            self.fun_and_grad,
            initial_position=x_guess,
            initial_inverse_hessian_estimate=inv_hess,
            **kwargs)
Example #2
0
    def __init__(self,
                 data=None,
                 batch_size=10,
                 max_sigma=3,
                 data_is_annotated=False,
                 _skip_tf_init=False,
                 _skip_bounds_computation=False,
                 fit_params=None,
                 **params):
        """

        :param data:
        :param batch_size: used
        :param max_sigma:
        :param data_is_annotated:
        :param _skip_tf_init:
        :param _skip_bounds_computation:
        :param fit_params: List of parameters to fit
        :param params: New defaults
        """
        self.defaults = dict()
        self.data = data
        if data is None:
            # We're calling the source without data. Set the batch_size here
            # since we can't pass it to set_data later
            self.batch_size = batch_size
        else:
            self._init_padding(batch_size, _skip_tf_init)
            self.data_tensor = fd.np_to_tf(self.data[self.column])
            self.data_tensor = tf.reshape(self.data_tensor,
                                          (-1, self.batch_size, 1))
Example #3
0
    def gimme(self,
              fname,
              data_tensor=None,
              ptensor=None,
              bonus_arg=None,
              numpy_out=False):
        """Evaluate the model function fname with all required arguments

        :param fname: Name of the model function to compute
        :param bonus_arg: If fname takes a bonus argument, the data for it
        :param numpy_out: If True, return (tuple of) numpy arrays,
        otherwise (tuple of) tensors.
        :param data_tensor: Data tensor, columns as self.name_id
        If not given, use self.data (used in annotate)
        :param ptensor: Parameter tensor, columns as self.param_id
        If not give, use defaults dictionary (used in annotate)
        Before using gimme, you must use set_data to
        populate the internal caches.
        """
        # TODO: make a clean way to keep track of i_batch or have it as input
        assert (bonus_arg is not None) == (fname in self.special_data_methods)

        if data_tensor is None:
            # We're in an annotate
            assert hasattr(self, 'data'), "You must set data first"
        else:
            # We're computing
            if not hasattr(self, 'name_id'):
                raise ValueError(
                    "You must set_data first (and populate the tensor cache)")

        f = getattr(self, fname)

        if callable(f):
            args = [self._fetch(x, data_tensor) for x in self.f_dims[fname]]
            if bonus_arg is not None:
                args = [bonus_arg] + args
            kwargs = {
                pname: self._fetch_param(pname, ptensor)
                for pname in self.f_params[fname]
            }
            res = f(*args, **kwargs)

        else:
            if bonus_arg is None:
                n = len(
                    self.data) if data_tensor is None else data_tensor.shape[0]
                x = tf.ones(n, dtype=fd.float_type())
            else:
                x = tf.ones_like(bonus_arg, dtype=fd.float_type())
            res = f * x

        if numpy_out:
            return fd.tf_to_np(res)
        return fd.np_to_tf(res)
Example #4
0
    def _fetch(self, x, data_tensor=None):
        """Return a tensor column from the original dataframe (self.data)
        :param x: column name
        :param data_tensor: Data tensor, columns as in self.name_id
        """
        if x in self.ignore_columns():
            raise RuntimeError(
                "Attempt to fetch %s, which is in ignore_columns" % x)
        if data_tensor is None:
            # We're inside annotate, just return the column
            return fd.np_to_tf(self.data[x].values)

        return data_tensor[:, self.name_id[x]]
Example #5
0
    def _fetch(self, x, data_tensor=None):
        """Return a tensor column from the original dataframe (self.data)
        :param x: column name
        :param data_tensor: Data tensor, columns as in self.name_id
        """
        if data_tensor is None:
            # We're inside annotate, just return the column
            return fd.np_to_tf(self.data[x].values)

        col_id = tf.dtypes.cast(self.name_id.lookup(tf.constant(x)),
                                fd.int_type())
        # if i_batch is None:
        #     return tf.reshape(self.data_tensor[:,:,col_id], [-1])
        # else:
        return data_tensor[:, col_id]
Example #6
0
 def fun_and_grad(self, x):
     return fd.np_to_tf(super().fun_and_grad(x))
Example #7
0
    def bestfit(self,
                guess=None,
                fix=None,
                optimizer=tfp.optimizer.bfgs_minimize,
                llr_tolerance=0.1,
                get_lowlevel_result=False,
                use_hessian=True,
                autograph=True,
                **kwargs):
        """Return best-fit parameter tensor

        :param guess: Guess parameters: dict {param: guess} of guesses to use.
        :param fix: dict {param: value} of parameters to keep fixed
        during the minimzation.
        :param llr_tolerance: stop minimizer if change in -2 log likelihood
        becomes less than this (roughly: using guess to convert to
        relative tolerance threshold)
        :param get_lowlevel_result: Returns the full optimizer result instead
        of only the best fit parameters. Bool.
        :param use_hessian: Passes the hessian estimated at the guess to the
        optimizer. Bool.
        """
        if fix is None:
            fix = dict()
        if guess is None:
            guess = dict()

        if not isinstance(guess, dict):
            raise ValueError("Guess must be a dictionary")
        guess = {**self.guess(), **guess, **fix}

        # Unfortunately we can only set the relative tolerance for the
        # objective; we'd like to set the absolute one.
        # Use the guess log likelihood to normalize;
        if llr_tolerance is not None:
            kwargs.setdefault('f_relative_tolerance',
                              llr_tolerance/self.minus_ll(**guess)[0])

        # Create a vector of guesses for the optimizer
        # Store as temp attributes so we can access them also in objective
        self._varnames = [k for k in self.param_names if k not in fix]
        self._guess_vect = fd.np_to_tf(np.array([
            guess[k] for k in self._varnames]))
        self._fix = fix

        # Minimize multipliers to the guess, rather than the guess itself
        # This is a basic kind of standardization that helps make the gradient
        # vector reasonable.
        x_norm = tf.ones(len(self._varnames), dtype=fd.float_type())

        if optimizer == tfp.optimizer.bfgs_minimize and use_hessian:
            # This optimizer can use the hessian information
            # Compute the inverse hessian at the guess
            inv_hess = self.inverse_hessian(guess, omit_grads=tuple(fix.keys()))
            # We use scaled values in the optimizer so also scale the
            # hessian. We need to multiply the hessian with the parameter
            # values. This is the inverse hessian so we divide.
            inv_hess /= tf.linalg.tensordot(
                self._guess_vect, self._guess_vect, axes=0)
            # Explicitly symmetrize the matrix
            inv_hess = fd.symmetrize_matrix(inv_hess)
        else:
            inv_hess = None

        self._autograph_objective = autograph
        res = optimizer(self.objective,
                        x_norm,
                        initial_inverse_hessian_estimate=inv_hess,
                        **kwargs)
        if get_lowlevel_result:
            return res
        if res.failed:
            raise ValueError(f"Optimizer failure! Result: {res}")
        res = res.position * self._guess_vect
        res = {k: res[i].numpy() for i, k in enumerate(self._varnames)}
        return {**res, **fix}