Example #1
0
File: learn.py Project: dirko/pyugm
    def __init__(self, model, prior=1.0, initial_noise=10.0**-12, update_order=None):
        """
        The learner object.

        :param prior: Float representing the prior sigma squared of all parameters.
        """
        self._model = model

        # Get number of parameters and map parameters to position in parameter vector
        parameter_set = set()
        for factor in self._model.factors:
            if factor.parameters is not None:
                for parameter in factor.parameters.reshape(-1, ):
                    if isinstance(parameter, str):
                        parameter_set.add(parameter)
        self._index_to_parameters = {}
        self._parameters_to_index = {}
        for index, key in enumerate(sorted(list(parameter_set))):
            self._index_to_parameters[index] = key
            self._parameters_to_index[key] = index

        self._dimension = len(self._index_to_parameters)
        self._parameters = numpy.random.randn(self._dimension) * initial_noise
        if self._dimension > 0:
            self._prior_location = numpy.zeros(self._dimension)
            self._prior_precision = numpy.eye(self._dimension) * prior
            self._prior_normaliser = (-0.5 * self._dimension * numpy.log(2.0 * numpy.pi)
                                      + 0.5 * numpy.log(numpy.linalg.det((self._prior_precision))))
        self._update_order = update_order
        if not self._update_order:
            self._update_order = FloodingProtocol(self._model)

        # Results
        self._iterations = []
        self._optimizer_result = None
Example #2
0
File: learn.py Project: dirko/pyugm
class LearnMrfParameters(object):
    """
    Find a Gaussian approximation to the posterior given a model and prior.
    """
    def __init__(self, model, prior=1.0, initial_noise=10.0**-12, update_order=None):
        """
        The learner object.

        :param prior: Float representing the prior sigma squared of all parameters.
        """
        self._model = model

        # Get number of parameters and map parameters to position in parameter vector
        parameter_set = set()
        for factor in self._model.factors:
            if factor.parameters is not None:
                for parameter in factor.parameters.reshape(-1, ):
                    if isinstance(parameter, str):
                        parameter_set.add(parameter)
        self._index_to_parameters = {}
        self._parameters_to_index = {}
        for index, key in enumerate(sorted(list(parameter_set))):
            self._index_to_parameters[index] = key
            self._parameters_to_index[key] = index

        self._dimension = len(self._index_to_parameters)
        self._parameters = numpy.random.randn(self._dimension) * initial_noise
        if self._dimension > 0:
            self._prior_location = numpy.zeros(self._dimension)
            self._prior_precision = numpy.eye(self._dimension) * prior
            self._prior_normaliser = (-0.5 * self._dimension * numpy.log(2.0 * numpy.pi)
                                      + 0.5 * numpy.log(numpy.linalg.det((self._prior_precision))))
        self._update_order = update_order
        if not self._update_order:
            self._update_order = FloodingProtocol(self._model)

        # Results
        self._iterations = []
        self._optimizer_result = None

    @property
    def parameters(self):
        """
        The current learned parameter values.
        :returns: Dictionary where the key is a parameter name and the value its value.
        """
        parameter_dictionary = {}
        for i, value in enumerate(self._parameters):
            parameter_dictionary[self._index_to_parameters[i]] = value
        return parameter_dictionary

    def log_likelihood_and_gradient(self, evidence):
        """
        Run inference on the model to find the log-likelihood of the model given evidence and its gradient with respect
            to the model parameters.
        :param evidence: A dictionary where the key is a variable name and the value its observed value.
        :returns: The log-likelihood and a vector of derivatives.
        """
        self._update_order.reset()
        inference = LoopyBeliefUpdateInference(self._model, update_order=self._update_order)
        inference.calibrate(parameters=self.parameters)
        log_z_total = inference.partition_approximation()
        model_expected_counts = self._accumulate_expected_counts(inference)

        self._update_order.reset()
        inference = LoopyBeliefUpdateInference(self._model, update_order=self._update_order)
        inference.calibrate(evidence, parameters=self.parameters)
        log_z_observed = inference.partition_approximation()
        empirical_expected_counts = self._accumulate_expected_counts(inference)

        log_likelihood = log_z_observed - log_z_total
        derivative = empirical_expected_counts - model_expected_counts

        if self._dimension > 0:
            derivative += -numpy.dot(self._prior_precision, (self._parameters - self._prior_location))
            log_likelihood += -0.5 * numpy.dot(numpy.dot((self._parameters - self._prior_location).T,
                                                         self._prior_precision),
                                               (self._parameters - self._prior_location))
            log_likelihood += self._prior_normaliser
        return log_likelihood, derivative

    def _accumulate_expected_counts(self, inference):
        """
        Iterate through beliefs and add parameter values.
        :returns: Vector of expected counts for each parameter.
        """
        expected_counts = numpy.zeros(self._parameters.shape)
        for belief in inference.beliefs.values():
            factor_sum = numpy.sum(belief.data)
            for parameter, value in zip(belief.parameters.flatten(), belief.data.flatten()):
                if isinstance(parameter, str):
                    expected_counts[self._parameters_to_index[parameter]] += (value / factor_sum)  # * norm / normalizer
        return expected_counts

    def _parameter_dictionary_to_vector(self, dictionary):
        """
        Helper to switch between a dictionary representation of parameter values to vector representation.
        :param dictionary: A dictionary where the key is a parameter name and the value its value.
        :returns: A numpy array.
        """
        return_vector = numpy.array(len(self._parameters_to_index))
        for i in xrange(len(return_vector)):
            return_vector[i] = dictionary[self._index_to_parameters[i]]
        return return_vector

    def fit(self,
            evidence,
            initial_parameters=None,
            optimizer=scipy.optimize.fmin_l_bfgs_b,
            optimizer_kwargs=None):
        """
        Fit the model to the data.
        :param evidence: Dictionary where the key is a variable name and the value the observed value of that variable.
        :param initial_parameters: numpy.array of initial parameter values. If None then random values around 0 is used.
        :param optimizer: The optimization function to use.
        :param optimizer_kwargs: Keyword arguments that are passed to the optimizer.
        :returns: The learner object.
        """
        initial_parameter_vector = self._parameters
        if initial_parameters is not None:
            initial_parameter_vector = self._parameter_dictionary_to_vector(initial_parameters)
        if not optimizer_kwargs:
            optimizer_kwargs = {'pgtol': 10.0**-10}

        def objective_function(parameter_vector):
            """
            Function that is passed to the optimizer.
            :param parameter_vector: Parameter vector.
            :returns: Negative log-likelihood. gradient.
            """
            self._parameters = parameter_vector
            log_likelihood, grad = self.log_likelihood_and_gradient(evidence)
            self._iterations.append([log_likelihood, parameter_vector])
            return -log_likelihood, -grad

        self._optimizer_result = optimizer(objective_function, initial_parameter_vector, **optimizer_kwargs)
        return self

    def result(self):
        """
        Get the learning results.
        :returns: Log-likelihood, parameters that maximises the log-likelihood.
        """
        if self._optimizer_result:
            return self._optimizer_result[1], self._optimizer_result[0]
        else:
            raise Exception('No result yet - run fit.')
def lbp_combine_best_patches(patch_image_directory,
                             context_image,
                             prediction_fn,
                             min_stride=1,
                             max_iters=1,
                             max_stride=3):
    # open the context image
    context = Image.open(context_image)

    # get smallest pw and images as np arrays
    smallest_pw, patch_images = get_stylized_images(patch_image_directory)

    # get context image scaled down so that it's the same size as the stylized imgs
    patchR, patchC, _ = patch_images[0].shape
    context = context.resize((patchC, patchR), Image.ANTIALIAS)
    context = np.array(context)

    lbf_folder = 'lbf_output/'
    if not os.path.exists(lbf_folder):
        os.makedirs(lbf_folder)

    inner_folder = lbf_folder + prediction_fn + '_folder/'
    if not os.path.exists(inner_folder):
        os.makedirs(inner_folder)

    stride = min_stride
    while stride <= max_stride:
        evidence, factors = construct_graph(patch_images, context, smallest_pw,
                                            stride)
        model = Model(factors)

        # Get some feedback on how inference is converging by listening in on some of the label beliefs.
        def reporter(infe, orde):
            print('{:3}'.format(orde.total_iterations))

        order = FloodingProtocol(model, max_iterations=max_iters)
        inference = LoopyBeliefUpdateInference(model, order, callback=reporter)
        inference.calibrate(evidence)

        K = len(patch_images)
        rrange = range(0, patchR - smallest_pw + 1, stride)
        crange = range(0, patchC - smallest_pw + 1, stride)
        num_r = len(rrange)  # number of patches vertically
        num_c = len(crange)  # number of patches horizontally

        ff_labels = [[None for i in range(num_c)] for j in range(num_r)]
        ff_r = 0
        for r in rrange:
            ff_c = 0
            for c in crange:
                variable_name = 'label_{}_{}'.format(r, c)

                # first factor is the context-style factor tha we want
                label_factor = inference.get_marginals(variable_name)[0]

                # save the actual patch location to make it easier to remap them later on
                ff_labels[ff_r][ff_c] = [[r, c], label_factor.normalized_data]
                ff_c += 1
            ff_r += 1

        # save the labels so they can be easily reused
        ff_labels = np.array(ff_labels)
        np.save(
            "%s%s_stride_%d_first_factor_label_data" %
            (inner_folder, prediction_fn, stride), ff_labels)
        stride += 1