diff --git a/bob/learn/em/__init__.py b/bob/learn/em/__init__.py index 44e6b51988b903eccb68875662a682f84bb59233..879f33b9d854ad026bea3dad175dd52599a93a3e 100644 --- a/bob/learn/em/__init__.py +++ b/bob/learn/em/__init__.py @@ -1,215 +1,11 @@ -# import Libraries of other lib packages -import bob.io.base -import bob.math -import bob.learn.linear -import bob.sp - -# import our own Library import bob.extension - -bob.extension.load_bob_library("bob.learn.em", __file__) - -from ._library import * -from ._library import GMMMachine as _GMMMachine_C -from ._library import ISVBase as _ISVBase_C -from ._library import ISVMachine as _ISVMachine_C -from ._library import KMeansMachine as _KMeansMachine_C -from ._library import GMMStats as _GMMStats_C -from ._library import IVectorMachine as _IVectorMachine_C - -from . import version -from .version import module as __version__ -from .version import api as __api_version__ -from .train import * +from .mixture import linear_scoring def get_config(): - """Returns a string containing the configuration information. - """ - return bob.extension.get_config(__name__, version.externals, version.api) + """Returns a string containing the configuration information.""" + return bob.extension.get_config(__name__) # gets sphinx autodoc done right - don't remove it __all__ = [_ for _ in dir() if not _.startswith("_")] - - -class GMMMachine(_GMMMachine_C): - __doc__ = _GMMMachine_C.__doc__ - - def update_dict(self, d): - self.means = d["means"] - self.variances = d["variances"] - self.weights = d["weights"] - - @staticmethod - def gmm_shape_from_dict(d): - return d["means"].shape - - @classmethod - def create_from_dict(cls, d): - shape = GMMMachine.gmm_shape_from_dict(d) - gmm_machine = cls(shape[0], shape[1]) - gmm_machine.update_dict(d) - return gmm_machine - - @staticmethod - def to_dict(gmm_machine): - gmm_data = dict() - gmm_data["means"] = gmm_machine.means - gmm_data["variances"] = gmm_machine.variances - gmm_data["weights"] = gmm_machine.weights - return gmm_data - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - self.__dict__ = d - shape = self.gmm_shape_from_dict(d) - self.__init__(shape[0], shape[1]) - self.update_dict(d) - - -class ISVBase(_ISVBase_C): - __doc__ = _ISVBase_C.__doc__ - - @staticmethod - def to_dict(isv_base): - isv_data = dict() - isv_data["gmm"] = GMMMachine.to_dict(isv_base.ubm) - isv_data["u"] = isv_base.u - isv_data["d"] = isv_base.d - - return isv_data - - def update_dict(self, d): - ubm = GMMMachine.create_from_dict(d["gmm"]) - u = d["u"] - self.__init__(ubm, u.shape[1]) - self.u = u - self.d = d["d"] - - @classmethod - def create_from_dict(cls, d): - ubm = GMMMachine.create_from_dict(d["gmm"]) - ru = d["u"].shape[1] - isv_base = ISVBase(ubm, ru) - isv_base.u = d["u"] - isv_base.d = d["d"] - return isv_base - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - self.__dict__ = d - self.update_dict(d) - - -class ISVMachine(_ISVMachine_C): - __doc__ = _ISVMachine_C.__doc__ - - @staticmethod - def to_dict(isv_machine): - isv_data = dict() - isv_data["x"] = isv_machine.x - isv_data["z"] = isv_machine.z - isv_data["isv_base"] = ISVBase.to_dict(isv_machine.isv_base) - - return isv_data - - def update_dict(self, d): - isv_base = ISVBase.create_from_dict(d["isv_base"]) - self.__init__(isv_base) - self.x = d["x"] - self.z = d["z"] - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - self.__dict__ = d - self.update_dict(d) - - -class KMeansMachine(_KMeansMachine_C): - __doc__ = _KMeansMachine_C.__doc__ - - @staticmethod - def to_dict(kmeans_machine): - kmeans_data = dict() - kmeans_data["means"] = kmeans_machine.means - return kmeans_data - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - means = d["means"] - self.__init__(means.shape[0], means.shape[1]) - self.means = means - - -class GMMStats(_GMMStats_C): - __doc__ = _GMMStats_C.__doc__ - - @staticmethod - def to_dict(gmm_stats): - gmm_stats_data = dict() - gmm_stats_data["log_likelihood"] = gmm_stats.log_likelihood - gmm_stats_data["t"] = gmm_stats.t - gmm_stats_data["n"] = gmm_stats.n - gmm_stats_data["sum_px"] = gmm_stats.sum_px - gmm_stats_data["sum_pxx"] = gmm_stats.sum_pxx - return gmm_stats_data - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - shape = d["sum_pxx"].shape - self.__init__(shape[0], shape[1]) - self.t = d["t"] - self.n = d["n"] - self.log_likelihood = d["log_likelihood"] - self.sum_px = d["sum_px"] - self.sum_pxx = d["sum_pxx"] - - -class IVectorMachine(_IVectorMachine_C): - __doc__ = _IVectorMachine_C.__doc__ - - @staticmethod - def to_dict(ivector_machine): - ivector_data = dict() - ivector_data["gmm"] = GMMMachine.to_dict(ivector_machine.ubm) - ivector_data["sigma"] = ivector_machine.sigma - ivector_data["t"] = ivector_machine.t - - return ivector_data - - def update_dict(self, d): - ubm = GMMMachine.create_from_dict(d["gmm"]) - t = d["t"] - self.__init__(ubm, t.shape[1]) - self.sigma = d["sigma"] - self.t = t - - def __getstate__(self): - d = dict(self.__dict__) - d.update(self.__class__.to_dict(self)) - return d - - def __setstate__(self, d): - self.__dict__ = d - self.update_dict(d) diff --git a/bob/learn/em/cluster/__init__.py b/bob/learn/em/cluster/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..535fd51a3f013f15ec9a9077314e038bf3c5c92d --- /dev/null +++ b/bob/learn/em/cluster/__init__.py @@ -0,0 +1 @@ +from .k_means import KMeansMachine diff --git a/bob/learn/em/cluster/k_means.py b/bob/learn/em/cluster/k_means.py new file mode 100644 index 0000000000000000000000000000000000000000..abdf6a69e746efb17782f50a50a8c99bda2c357e --- /dev/null +++ b/bob/learn/em/cluster/k_means.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python +# @author: Yannick Dayer <yannick.dayer@idiap.ch> +# @date: Tue 27 Jul 2021 11:04:10 UTC+02 + +import logging +from typing import Tuple +from typing import Union + +import dask.array as da +import numpy as np +from dask_ml.cluster.k_means import k_init +from sklearn.base import BaseEstimator + +logger = logging.getLogger(__name__) + + +class KMeansMachine(BaseEstimator): + """Stores the k-means clusters parameters (centroid of each cluster). + + Allows the clustering of data with the ``fit`` method. + + The training works in two phases: + - An initialization (setting the initial values of the centroids) + - An e-m loop reducing the total distance between the data points and their + closest centroid. + + The initialization can use an iterative process to find the best set of + coordinates, use random starting points, or take specified coordinates. The + ``init_method`` parameter specifies which of these behavior is considered. + + Attributes + ---------- + centroids_: ndarray of shape (n_clusters, n_features) + The current clusters centroids. Available after fitting. + + Example + ------- + >>> data = dask.array.array([[0,-1,0],[-1,1,1],[3,2,1],[2,2,1],[1,0,2]]) + >>> machine = KMeansMachine(2).fit(data) + >>> machine.centroids_.compute() + ... array([[0. , 0. , 1. ], + ... [2.5, 2. , 1. ]]) + """ + + def __init__( + self, + n_clusters: int, + init_method: Union[str, np.ndarray] = "k-means||", + convergence_threshold: float = 1e-5, + max_iter: int = 20, + random_state: Union[int, np.random.RandomState] = 0, + init_max_iter: Union[int, None] = None, + ) -> None: + """ + Parameters + ---------- + n_clusters: int + The number of represented clusters. + init_method: + One of: "random", "k-means++", or "k-means||", or an array with the wanted + starting values of the centroids. + max_iter: + The maximum number of iterations for the e-m part. + random_state: + A seed or RandomState used for the initialization part. + init_max_iter: + The maximum number of iterations for the initialization part. + """ + + if n_clusters < 1: + raise ValueError("The Number of cluster should be greater thant 0.") + self.n_clusters = n_clusters + self.init_method = init_method + self.convergence_threshold = convergence_threshold + self.max_iter = max_iter + self.random_state = random_state + self.init_max_iter = init_max_iter + self.average_min_distance = np.inf + self.zeroeth_order_statistics = None + self.first_order_statistics = None + self.centroids_ = None + + def get_centroids_distance(self, x: np.ndarray) -> np.ndarray: + """Returns the distance values between x and each cluster's centroid. + + The returned values are squared Euclidean distances. + + Parameters + ---------- + x: ndarray of shape (n_features,) or (n_samples, n_features) + One data point, or a series of data points. + + Returns + ------- + distances: ndarray of shape (n_clusters,) or (n_clusters, n_samples) + For each cluster, the squared Euclidian distance (or distances) to x. + """ + return np.sum((self.centroids_[:, None] - x[None, :]) ** 2, axis=-1) + + def get_closest_centroid(self, x: np.ndarray) -> Tuple[int, float]: + """Returns the closest mean's index and squared Euclidian distance to x.""" + dists = self.get_centroids_distance(x) + min_id = np.argmin(dists, axis=0) + min_dist = dists[min_id] + return min_id, min_dist + + def get_closest_centroid_index(self, x: np.ndarray) -> np.ndarray: + """Returns the index of the closest cluster mean to x.""" + return np.argmin(self.get_centroids_distance(x), axis=0) + + def get_min_distance(self, x: np.ndarray) -> np.ndarray: + """Returns the smallest distance between that point and the clusters centroids. + + For each point in x, the minimum distance to each cluster's mean is returned. + + The returned values are squared Euclidean distances. + """ + return np.min(self.get_centroids_distance(x), axis=0) + + def __eq__(self, obj) -> bool: + return self.is_similar_to(obj, r_epsilon=0, a_epsilon=0) + + def is_similar_to(self, obj, r_epsilon=1e-05, a_epsilon=1e-08) -> bool: + if self.centroids_ is not None and obj.centroids_ is not None: + return np.allclose( + self.centroids_, obj.centroids_, rtol=r_epsilon, atol=a_epsilon + ) + else: + logger.warning( + "KMeansMachine `centroids_` was not set. You should call 'fit' first." + ) + return False + + def get_variances_and_weights_for_each_cluster(self, data: np.ndarray): + """Returns the clusters variance and weight for data clustered by the machine. + + For each cluster, finds the subset of the samples that is closest to that + centroid, and calculates: + 1) the variance of that subset (the cluster variance) + 2) the proportion of samples represented by that subset (the cluster weight) + + Parameters + ---------- + data: + The data to compute the variance of. + + Returns + ------- + Tuple of arrays: + variances: ndarray of shape (n_clusters, n_features) + For each cluster, the variance in each dimension of the data. + weights: ndarray of shape (n_clusters, ) + Weight (proportion of quantity of data point) of each cluster. + """ + n_cluster = self.n_clusters + closest_centroid_indices = self.get_closest_centroid_index(data) + weights_count = np.bincount(closest_centroid_indices, minlength=n_cluster) + weights = weights_count / weights_count.sum() + + # FIX for `too many indices for array` error if using `np.eye(n_cluster)` alone: + dask_compatible_eye = np.eye(n_cluster) * np.array(1, like=data) + + # Accumulate + means_sum = np.sum( + dask_compatible_eye[closest_centroid_indices][:, :, None] * data[:, None], + axis=0, + ) + variances_sum = np.sum( + dask_compatible_eye[closest_centroid_indices][:, :, None] + * (data[:, None] ** 2), + axis=0, + ) + + # Reduce + means = means_sum / weights_count[:, None] + variances = (variances_sum / weights_count[:, None]) - (means ** 2) + + return variances, weights + + def initialize(self, data: np.ndarray): + """Assigns the means to an initial value using a specified method or randomly.""" + logger.debug(f"Initializing k-means means with '{self.init_method}'.") + # k_init requires da.Array as input. + data = da.array(data) + self.centroids_ = k_init( + X=data, + n_clusters=self.n_clusters, + init=self.init_method, + random_state=self.random_state, + max_iter=self.init_max_iter, + ) + + def e_step(self, data: np.ndarray): + closest_k_indices = self.get_closest_centroid_index(data) + # Number of data points in each cluster + self.zeroeth_order_statistics = np.bincount( + closest_k_indices, minlength=self.n_clusters + ) + # Sum of data points coordinates in each cluster + self.first_order_statistics = np.sum( + np.eye(self.n_clusters)[closest_k_indices][:, :, None] * data[:, None], + axis=0, + ) + self.average_min_distance = self.get_min_distance(data).mean() + + def m_step(self, data: np.ndarray): + self.centroids_ = ( + self.first_order_statistics / self.zeroeth_order_statistics[:, None] + ) + + def fit(self, X, y=None): + """Fits this machine on data samples.""" + logger.debug(f"Initializing trainer.") + self.initialize(data=X) + + logger.info("Training k-means.") + distance = np.inf + step = 0 + while self.max_iter is None or step < self.max_iter: + step += 1 + logger.info( + f"Iteration {step:3d}" + (f"/{self.max_iter}" if self.max_iter else "") + ) + distance_previous = distance + self.e_step(data=X) + self.m_step(data=X) + + # If we're running in dask, persist the centroids so we don't recompute them + # from the start of the graph at every step. + for attr in ("centroids_",): + arr = getattr(self, attr) + if isinstance(arr, da.Array): + setattr(self, attr, arr.persist()) + + distance = float(self.average_min_distance) + + logger.info(f"Average minimal squared Euclidean distance = {distance}") + + if step > 1: + convergence_value = abs( + (distance_previous - distance) / distance_previous + ) + logger.info(f"Convergence value = {convergence_value}") + + # Terminates if converged (and threshold is set) + if ( + self.convergence_threshold is not None + and convergence_value <= self.convergence_threshold + ): + logger.info("Reached convergence threshold. Training stopped.") + break + else: + logger.info("Reached maximum step. Training stopped without convergence.") + self.compute() + return self + + def partial_fit(self, X, y=None): + """Applies one e-m step of k-means on the data.""" + if self.centroids_ is None: + logger.debug("First call to 'partial_fit'. Initializing...") + self.initialize(data=X) + + self.e_step(data=X) + self.m_step(data=X) + + return self + + def transform(self, X): + """Returns all the distances between the data and each cluster's mean. + + Parameters + ---------- + X: ndarray of shape (n_samples, n_features) + Series of data points. + + Returns + ------- + distances: ndarray of shape (n_clusters, n_samples) + For each mean, for each point, the squared Euclidian distance between them. + """ + return self.get_centroids_distance(X) + + def predict(self, X): + """Returns the labels of the closest cluster centroid to the data. + + Parameters + ---------- + X: ndarray of shape (n_samples, n_features) + Series of data points. + + Returns + ------- + indices: ndarray of shape (n_samples) + The indices of the closest cluster for each data point. + """ + return self.get_closest_centroid_index(X) + + def compute(self, *args, **kwargs): + """Computes delayed arrays if needed.""" + for name in ("centroids_",): + setattr(self, name, np.asarray(getattr(self, name))) diff --git a/bob/learn/em/cpp/EMPCATrainer.cpp b/bob/learn/em/cpp/EMPCATrainer.cpp deleted file mode 100644 index 1217be261cb83818373990f97312ce8670f8d47f..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/EMPCATrainer.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/** - * @date Tue Oct 11 12:18:23 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <vector> -#include <algorithm> -#include <boost/random.hpp> -#include <cmath> - -#include <bob.learn.em/EMPCATrainer.h> -#include <bob.core/array_copy.h> -#include <bob.core/check.h> -#include <bob.math/linear.h> -#include <bob.math/det.h> -#include <bob.math/inv.h> -#include <bob.math/pinv.h> -#include <bob.math/stats.h> - -bob::learn::em::EMPCATrainer::EMPCATrainer(bool compute_likelihood): - m_compute_likelihood(compute_likelihood), - m_rng(new boost::mt19937()), - m_S(0,0), - m_z_first_order(0,0), m_z_second_order(0,0,0), - m_inW(0,0), m_invM(0,0), m_sigma2(0), m_f_log2pi(0), - m_tmp_dxf(0,0), m_tmp_d(0), m_tmp_f(0), - m_tmp_dxd_1(0,0), m_tmp_dxd_2(0,0), - m_tmp_fxd_1(0,0), m_tmp_fxd_2(0,0), - m_tmp_fxf_1(0,0), m_tmp_fxf_2(0,0) -{ -} - -bob::learn::em::EMPCATrainer::EMPCATrainer(const bob::learn::em::EMPCATrainer& other): - m_compute_likelihood(other.m_compute_likelihood), - m_rng(other.m_rng), - m_S(bob::core::array::ccopy(other.m_S)), - m_z_first_order(bob::core::array::ccopy(other.m_z_first_order)), - m_z_second_order(bob::core::array::ccopy(other.m_z_second_order)), - m_inW(bob::core::array::ccopy(other.m_inW)), - m_invM(bob::core::array::ccopy(other.m_invM)), - m_sigma2(other.m_sigma2), m_f_log2pi(other.m_f_log2pi), - m_tmp_dxf(bob::core::array::ccopy(other.m_tmp_dxf)), - m_tmp_d(bob::core::array::ccopy(other.m_tmp_d)), - m_tmp_f(bob::core::array::ccopy(other.m_tmp_f)), - m_tmp_dxd_1(bob::core::array::ccopy(other.m_tmp_dxd_1)), - m_tmp_dxd_2(bob::core::array::ccopy(other.m_tmp_dxd_2)), - m_tmp_fxd_1(bob::core::array::ccopy(other.m_tmp_fxd_1)), - m_tmp_fxd_2(bob::core::array::ccopy(other.m_tmp_fxd_2)), - m_tmp_fxf_1(bob::core::array::ccopy(other.m_tmp_fxf_1)), - m_tmp_fxf_2(bob::core::array::ccopy(other.m_tmp_fxf_2)) -{ -} - -bob::learn::em::EMPCATrainer::~EMPCATrainer() -{ -} - -bob::learn::em::EMPCATrainer& bob::learn::em::EMPCATrainer::operator= - (const bob::learn::em::EMPCATrainer& other) -{ - if (this != &other) - { - m_rng = other.m_rng; - m_compute_likelihood = other.m_compute_likelihood; - m_S = bob::core::array::ccopy(other.m_S); - m_z_first_order = bob::core::array::ccopy(other.m_z_first_order); - m_z_second_order = bob::core::array::ccopy(other.m_z_second_order); - m_inW = bob::core::array::ccopy(other.m_inW); - m_invM = bob::core::array::ccopy(other.m_invM); - m_sigma2 = other.m_sigma2; - m_f_log2pi = other.m_f_log2pi; - m_tmp_dxf = bob::core::array::ccopy(other.m_tmp_dxf); - m_tmp_d = bob::core::array::ccopy(other.m_tmp_d); - m_tmp_f = bob::core::array::ccopy(other.m_tmp_f); - m_tmp_dxd_1 = bob::core::array::ccopy(other.m_tmp_dxd_1); - m_tmp_dxd_2 = bob::core::array::ccopy(other.m_tmp_dxd_2); - m_tmp_fxd_1 = bob::core::array::ccopy(other.m_tmp_fxd_1); - m_tmp_fxd_2 = bob::core::array::ccopy(other.m_tmp_fxd_2); - m_tmp_fxf_1 = bob::core::array::ccopy(other.m_tmp_fxf_1); - m_tmp_fxf_2 = bob::core::array::ccopy(other.m_tmp_fxf_2); - } - return *this; -} - -bool bob::learn::em::EMPCATrainer::operator== - (const bob::learn::em::EMPCATrainer &other) const -{ - return m_compute_likelihood == other.m_compute_likelihood && - m_rng == other.m_rng && - bob::core::array::isEqual(m_S, other.m_S) && - bob::core::array::isEqual(m_z_first_order, other.m_z_first_order) && - bob::core::array::isEqual(m_z_second_order, other.m_z_second_order) && - bob::core::array::isEqual(m_inW, other.m_inW) && - bob::core::array::isEqual(m_invM, other.m_invM) && - m_sigma2 == other.m_sigma2 && - m_f_log2pi == other.m_f_log2pi; -} - -bool bob::learn::em::EMPCATrainer::operator!= - (const bob::learn::em::EMPCATrainer &other) const -{ - return !(this->operator==(other)); -} - -bool bob::learn::em::EMPCATrainer::is_similar_to - (const bob::learn::em::EMPCATrainer &other, const double r_epsilon, - const double a_epsilon) const -{ - return m_compute_likelihood == other.m_compute_likelihood && - m_rng == other.m_rng && - bob::core::array::isClose(m_S, other.m_S, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_z_first_order, other.m_z_first_order, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_z_second_order, other.m_z_second_order, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_inW, other.m_inW, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_invM, other.m_invM, r_epsilon, a_epsilon) && - bob::core::isClose(m_sigma2, other.m_sigma2, r_epsilon, a_epsilon) && - bob::core::isClose(m_f_log2pi, other.m_f_log2pi, r_epsilon, a_epsilon); -} - -void bob::learn::em::EMPCATrainer::initialize(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar) -{ - // reinitializes array members and checks dimensionality - initMembers(machine, ar); - - // computes the mean and the covariance if required - computeMeanVariance(machine, ar); - - // Random initialization of W and sigma2 - initRandomWSigma2(machine); - - // Computes the product m_inW = W^T.W - computeWtW(machine); - // Computes inverse(M), where M = Wt * W + sigma2 * Id - computeInvM(); -} - - -void bob::learn::em::EMPCATrainer::initMembers( - const bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar) -{ - // Gets dimensions - const size_t n_samples = ar.extent(0); - const size_t n_features = ar.extent(1); - - // Checks that the dimensions are matching - const size_t n_inputs = machine.inputSize(); - const size_t n_outputs = machine.outputSize(); - - // Checks that the dimensions are matching - if (n_inputs != n_features) { - boost::format m("number of inputs (%u) does not match the number of features (%u)"); - m % n_inputs % n_features; - throw std::runtime_error(m.str()); - } - - // Covariance matrix S is only required to compute the log likelihood - if (m_compute_likelihood) - m_S.resize(n_features,n_features); - else - m_S.resize(0,0); - m_z_first_order.resize(n_samples, n_outputs); - m_z_second_order.resize(n_samples, n_outputs, n_outputs); - m_inW.resize(n_outputs, n_outputs); - m_invM.resize(n_outputs, n_outputs); - m_sigma2 = 0.; - m_f_log2pi = n_features * log(2*M_PI); - - // Cache - m_tmp_dxf.resize(n_outputs, n_features); - m_tmp_d.resize(n_outputs); - m_tmp_f.resize(n_features); - - m_tmp_dxd_1.resize(n_outputs, n_outputs); - m_tmp_dxd_2.resize(n_outputs, n_outputs); - m_tmp_fxd_1.resize(n_features, n_outputs); - m_tmp_fxd_2.resize(n_features, n_outputs); - // The following large cache matrices are only required to compute the - // log likelihood. - if (m_compute_likelihood) - { - m_tmp_fxf_1.resize(n_features, n_features); - m_tmp_fxf_2.resize(n_features, n_features); - } - else - { - m_tmp_fxf_1.resize(0,0); - m_tmp_fxf_2.resize(0,0); - } -} - -void bob::learn::em::EMPCATrainer::computeMeanVariance(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar) -{ - size_t n_samples = ar.extent(0); - blitz::Array<double,1> mu = machine.updateInputSubtraction(); - blitz::Range all = blitz::Range::all(); - if (m_compute_likelihood) - { - // Mean and scatter computation - bob::math::scatter(ar, m_S, mu); - // divides scatter by N-1 - m_S /= static_cast<double>(n_samples-1); - } - else - { - // computes the mean and updates mu - mu = 0.; - for (size_t i=0; i<n_samples; ++i) - mu += ar(i,all); - mu /= static_cast<double>(n_samples); - } -} - -void bob::learn::em::EMPCATrainer::initRandomWSigma2(bob::learn::linear::Machine& machine) -{ - // Initializes the random number generator - boost::uniform_01<> range01; - boost::variate_generator<boost::mt19937&, boost::uniform_01<> > die(*m_rng, range01); - - // W initialization (TODO: add method in core) - blitz::Array<double,2> W = machine.updateWeights(); - double ratio = 2.; /// Follows matlab implementation using a ratio of 2 - for (int i=0; i<W.extent(0); ++i) - for (int j=0; j<W.extent(1); ++j) - W(i,j) = die() * ratio; - // sigma2 initialization - m_sigma2 = die() * ratio; -} - -void bob::learn::em::EMPCATrainer::computeWtW(bob::learn::linear::Machine& machine) -{ - const blitz::Array<double,2> W = machine.getWeights(); - const blitz::Array<double,2> Wt = W.transpose(1,0); - bob::math::prod(Wt, W, m_inW); -} - -void bob::learn::em::EMPCATrainer::computeInvM() -{ - // Compute inverse(M), where M = W^T * W + sigma2 * Id - bob::math::eye(m_tmp_dxd_1); // m_tmp_dxd_1 = Id - m_tmp_dxd_1 *= m_sigma2; // m_tmp_dxd_1 = sigma2 * Id - m_tmp_dxd_1 += m_inW; // m_tmp_dxd_1 = M = W^T * W + sigma2 * Id - bob::math::inv(m_tmp_dxd_1, m_invM); // m_invM = inv(M) -} - - - -void bob::learn::em::EMPCATrainer::eStep(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) -{ - // Gets mu and W from the machine - const blitz::Array<double,1>& mu = machine.getInputSubtraction(); - const blitz::Array<double,2>& W = machine.getWeights(); - const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T - - // Computes the statistics - blitz::Range a = blitz::Range::all(); - for(int i=0; i<ar.extent(0); ++i) - { - /// 1/ First order statistics: \f$z_first_order_i = inv(M) W^T (t - \mu)\f$ - // m_tmp_f = t (sample) - mu (normalized sample) - m_tmp_f = ar(i,a) - mu; - // m_tmp_dxf = inv(M) * W^T - bob::math::prod(m_invM, Wt, m_tmp_dxf); - blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all()); - // z_first_order_i = inv(M) * W^T * (t - mu) - bob::math::prod(m_tmp_dxf, m_tmp_f, z_first_order_i); - - /// 2/ Second order statistics: - /// z_second_order_i = sigma2 * inv(M) + z_first_order_i * z_first_order_i^T - blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all()); - // m_tmp_dxd = z_first_order_i * z_first_order_i^T - bob::math::prod(z_first_order_i, z_first_order_i, m_tmp_dxd_1); // outer product - // z_second_order_i = sigma2 * inv(M) - z_second_order_i = m_invM; - z_second_order_i *= m_sigma2; - // z_second_order_i = sigma2 * inv(M) + z_first_order_i * z_first_order_i^T - z_second_order_i += m_tmp_dxd_1; - } -} - -void bob::learn::em::EMPCATrainer::mStep(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) -{ - // 1/ New estimate of W - updateW(machine, ar); - - // 2/ New estimate of sigma2 - updateSigma2(machine, ar); - - // Computes the new value of inverse(M), where M = Wt * W + sigma2 * Id - computeInvM(); -} - -void bob::learn::em::EMPCATrainer::updateW(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) { - // Get the mean mu and the projection matrix W - const blitz::Array<double,1>& mu = machine.getInputSubtraction(); - blitz::Array<double,2>& W = machine.updateWeights(); - const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T - - // Compute W = sum{ (t_{i} - mu) z_first_order_i^T} * inv( sum{z_second_order_i} ) - m_tmp_fxd_1 = 0.; - m_tmp_dxd_1 = 0.; - blitz::Range a = blitz::Range::all(); - for(int i=0; i<ar.extent(0); ++i) - { - // m_tmp_f = t (sample) - mu (normalized sample) - m_tmp_f = ar(i,a) - mu; - // first order statistics of sample i - blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all()); - // m_tmp_fxd_2 = (t - mu)*z_first_order_i - bob::math::prod(m_tmp_f, z_first_order_i, m_tmp_fxd_2); - m_tmp_fxd_1 += m_tmp_fxd_2; - - // second order statistics of sample i - blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all()); - m_tmp_dxd_1 += z_second_order_i; - } - - // m_tmp_dxd_2 = inv( sum(E(x_i.x_i^T)) ) - bob::math::inv(m_tmp_dxd_1, m_tmp_dxd_2); - // New estimates of W - bob::math::prod(m_tmp_fxd_1, m_tmp_dxd_2, W); - // Updates W'*W as well - bob::math::prod(Wt, W, m_inW); -} - -void bob::learn::em::EMPCATrainer::updateSigma2(bob::learn::linear::Machine& machine, const blitz::Array<double,2>& ar) { - // Get the mean mu and the projection matrix W - const blitz::Array<double,1>& mu = machine.getInputSubtraction(); - const blitz::Array<double,2>& W = machine.getWeights(); - const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T - - m_sigma2 = 0.; - blitz::Range a = blitz::Range::all(); - for(int i=0; i<ar.extent(0); ++i) - { - // a. sigma2 += || t - mu ||^2 - // m_tmp_f = t (sample) - mu (normalized sample) - m_tmp_f = ar(i,a) - mu; - // sigma2 += || t - mu ||^2 - m_sigma2 += blitz::sum(blitz::pow2(m_tmp_f)); - - // b. sigma2 -= 2*E(x_i)^T*W^T*(t - mu) - // m_tmp_d = W^T*(t - mu) - bob::math::prod(Wt, m_tmp_f, m_tmp_d); - // first order of i - blitz::Array<double,1> z_first_order_i = m_z_first_order(i,blitz::Range::all()); - // sigma2 -= 2*E(x_i)^T*W^T*(t - mu) - m_sigma2 -= 2*bob::math::dot(z_first_order_i, m_tmp_d); - - // c. sigma2 += trace( E(x_i.x_i^T)*W^T*W ) - // second order of i - blitz::Array<double,2> z_second_order_i = m_z_second_order(i,blitz::Range::all(),blitz::Range::all()); - // m_tmp_dxd_1 = E(x_i.x_i^T)*W^T*W - bob::math::prod(z_second_order_i, m_inW, m_tmp_dxd_1); - // sigma2 += trace( E(x_i.x_i^T)*W^T*W ) - m_sigma2 += bob::math::trace(m_tmp_dxd_1); - } - // Normalization factor - m_sigma2 /= (static_cast<double>(ar.extent(0)) * mu.extent(0)); -} - -double bob::learn::em::EMPCATrainer::computeLikelihood(bob::learn::linear::Machine& machine) -{ - // Get W projection matrix - const blitz::Array<double,2>& W = machine.getWeights(); - const blitz::Array<double,2> Wt = W.transpose(1,0); // W^T - const size_t n_features = m_S.extent(0); - - // 1/ Compute det(C), where C = sigma2.I + W.W^T - // det(C) = det(sigma2 * C / sigma2) = det(sigma2 * Id) * det(C / sigma2) - // We are using Sylvester's determinant theorem to compute a dxd - // determinant rather than a fxf one: det(I + A.B) = det(I + B.A) - // det(C) = sigma2^n_features * det(I + W.W^T/sigma2) - // = sigma2^n_features * det(I + W^T.W/sigma2) (cf. Bishop Appendix C) - // detC = det( eye(n_features) * sigma2 ) - - // detC = sigma2^n_features - double detC = pow(m_sigma2, n_features); - // m_tmp_dxd_1 = Id - bob::math::eye(m_tmp_dxd_1); - // m_tmp_dxd_2 = W^T.W - bob::math::prod(Wt, W, m_tmp_dxd_2); - // m_tmp_dxd_2 = W^T.W / sigma2 - m_tmp_dxd_2 /= m_sigma2; - // m_tmp_dxd_1 = Id + W^T.W / sigma2 - m_tmp_dxd_1 += m_tmp_dxd_2; - // detC = sigma2^n_features * det(I + W^T.W/sigma2) - detC *= bob::math::det(m_tmp_dxd_1); - - // 2/ Compute inv(C), where C = sigma2.I + W.W^T - // We are using the following identity (Property C.7 of Bishop's book) - // (A + B.D^-1.C)^-1 = A^-1 - A^-1.B(D+C.A^-1.B)^-1.C.A^-1 - // Hence, inv(C) = sigma2^-1 .(I - W.M^-1.W^T) - - // Compute inverse(M), where M = Wt * W + sigma2 * Id - computeInvM(); - // m_tmp_fxf_1 = I = eye(n_features) - bob::math::eye(m_tmp_fxf_1); - // m_tmp_fxd_1 = W * inv(M) - bob::math::prod(W, m_invM, m_tmp_fxd_1); - // m_tmp_fxf_2 = (W * inv(M) * Wt) - bob::math::prod(m_tmp_fxd_1, Wt, m_tmp_fxf_2); - // m_tmp_fxd_1 = inv(C) = (I - W.M^-1.W^T) / sigma2 - m_tmp_fxf_1 -= m_tmp_fxf_2; - m_tmp_fxf_1 /= m_sigma2; - - // 3/ Compute inv(C).S - bob::math::prod(m_tmp_fxf_1, m_S, m_tmp_fxf_2); - - // 4/ Use previous values to compute the log likelihood: - // Log likelihood = - N/2*{ d*ln(2*PI) + ln |detC| + tr(C^-1.S) } - double llh = - static_cast<double>(m_z_first_order.extent(0)) / 2. * - ( m_f_log2pi + log(fabs(detC)) + bob::math::trace(m_tmp_fxf_2) ); - - return llh; -} diff --git a/bob/learn/em/cpp/FABase.cpp b/bob/learn/em/cpp/FABase.cpp deleted file mode 100644 index 317a368d696668824f6be9a533c5ab6d4da8ff50..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/FABase.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/** - * @date Tue Jan 27 15:51:15 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/FABase.h> -#include <bob.core/array_copy.h> -#include <bob.math/linear.h> -#include <bob.math/inv.h> -#include <limits> - - -//////////////////// FABase //////////////////// -bob::learn::em::FABase::FABase(): - m_ubm(boost::shared_ptr<bob::learn::em::GMMMachine>()), m_ru(1), m_rv(1), - m_U(0,1), m_V(0,1), m_d(0) -{} - -bob::learn::em::FABase::FABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, - const size_t ru, const size_t rv): - m_ubm(ubm), m_ru(ru), m_rv(rv), - m_U(getSupervectorLength(),ru), m_V(getSupervectorLength(),rv), m_d(getSupervectorLength()) -{ - if (ru < 1) { - boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - if (rv < 1) { - boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - updateCache(); -} - -bob::learn::em::FABase::FABase(const bob::learn::em::FABase& other): - m_ubm(other.m_ubm), m_ru(other.m_ru), m_rv(other.m_rv), - m_U(bob::core::array::ccopy(other.m_U)), - m_V(bob::core::array::ccopy(other.m_V)), - m_d(bob::core::array::ccopy(other.m_d)) -{ - updateCache(); -} - -bob::learn::em::FABase::~FABase() { -} - -bob::learn::em::FABase& bob::learn::em::FABase::operator= -(const bob::learn::em::FABase& other) -{ - if (this != &other) - { - m_ubm = other.m_ubm; - m_ru = other.m_ru; - m_rv = other.m_rv; - m_U.reference(bob::core::array::ccopy(other.m_U)); - m_V.reference(bob::core::array::ccopy(other.m_V)); - m_d.reference(bob::core::array::ccopy(other.m_d)); - - updateCache(); - } - return *this; -} - -bool bob::learn::em::FABase::operator==(const bob::learn::em::FABase& b) const -{ - return ( (((m_ubm && b.m_ubm) && *m_ubm == *(b.m_ubm)) || (!m_ubm && !b.m_ubm)) && - m_ru == b.m_ru && m_rv == b.m_rv && - bob::core::array::isEqual(m_U, b.m_U) && - bob::core::array::isEqual(m_V, b.m_V) && - bob::core::array::isEqual(m_d, b.m_d)); -} - -bool bob::learn::em::FABase::operator!=(const bob::learn::em::FABase& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::FABase::is_similar_to(const bob::learn::em::FABase& b, - const double r_epsilon, const double a_epsilon) const -{ - // TODO: update is_similar_to of the GMMMachine with the 2 epsilon's - return (( ((m_ubm && b.m_ubm) && m_ubm->is_similar_to(*(b.m_ubm), a_epsilon)) || - (!m_ubm && !b.m_ubm) ) && - m_ru == b.m_ru && m_rv == b.m_rv && - bob::core::array::isClose(m_U, b.m_U, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_V, b.m_V, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_d, b.m_d, r_epsilon, a_epsilon)); -} - -void bob::learn::em::FABase::resize(const size_t ru, const size_t rv) -{ - if (ru < 1) { - boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - if (rv < 1) { - boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - - m_ru = ru; - m_rv = rv; - m_U.resizeAndPreserve(m_U.extent(0), ru); - m_V.resizeAndPreserve(m_V.extent(0), rv); - - updateCacheUbmUVD(); -} - -void bob::learn::em::FABase::resize(const size_t ru, const size_t rv, const size_t cd) -{ - if (ru < 1) { - boost::format m("value for parameter `ru' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - if (rv < 1) { - boost::format m("value for parameter `rv' (%lu) cannot be smaller than 1"); - m % ru; - throw std::runtime_error(m.str()); - } - - if (!m_ubm || (m_ubm && getSupervectorLength() == cd)) - { - m_ru = ru; - m_rv = rv; - m_U.resizeAndPreserve(cd, ru); - m_V.resizeAndPreserve(cd, rv); - m_d.resizeAndPreserve(cd); - - updateCacheUbmUVD(); - } - else { - boost::format m("value for parameter `cd' (%lu) is not set to %lu"); - m % cd % getSupervectorLength(); - throw std::runtime_error(m.str()); - } -} - -void bob::learn::em::FABase::setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm) -{ - m_ubm = ubm; - m_U.resizeAndPreserve(getSupervectorLength(), m_ru); - m_V.resizeAndPreserve(getSupervectorLength(), m_rv); - m_d.resizeAndPreserve(getSupervectorLength()); - - updateCache(); -} - -void bob::learn::em::FABase::setU(const blitz::Array<double,2>& U) -{ - if(U.extent(0) != m_U.extent(0)) { //checks dimension - boost::format m("number of rows in parameter `U' (%d) does not match the expected size (%d)"); - m % U.extent(0) % m_U.extent(0); - throw std::runtime_error(m.str()); - } - if(U.extent(1) != m_U.extent(1)) { //checks dimension - boost::format m("number of columns in parameter `U' (%d) does not match the expected size (%d)"); - m % U.extent(1) % m_U.extent(1); - throw std::runtime_error(m.str()); - } - m_U.reference(bob::core::array::ccopy(U)); - - // update cache - updateCacheUbmUVD(); -} - -void bob::learn::em::FABase::setV(const blitz::Array<double,2>& V) -{ - if(V.extent(0) != m_V.extent(0)) { //checks dimension - boost::format m("number of rows in parameter `V' (%d) does not match the expected size (%d)"); - m % V.extent(0) % m_V.extent(0); - throw std::runtime_error(m.str()); - } - if(V.extent(1) != m_V.extent(1)) { //checks dimension - boost::format m("number of columns in parameter `V' (%d) does not match the expected size (%d)"); - m % V.extent(1) % m_V.extent(1); - throw std::runtime_error(m.str()); - } - m_V.reference(bob::core::array::ccopy(V)); -} - -void bob::learn::em::FABase::setD(const blitz::Array<double,1>& d) -{ - if(d.extent(0) != m_d.extent(0)) { //checks dimension - boost::format m("size of input vector `d' (%d) does not match the expected size (%d)"); - m % d.extent(0) % m_d.extent(0); - throw std::runtime_error(m.str()); - } - m_d.reference(bob::core::array::ccopy(d)); -} - - -void bob::learn::em::FABase::updateCache() -{ - updateCacheUbm(); - updateCacheUbmUVD(); - resizeTmp(); -} - -void bob::learn::em::FABase::resizeTmp() -{ - m_tmp_IdPlusUSProdInv.resize(getDimRu(),getDimRu()); - m_tmp_Fn_x.resize(getSupervectorLength()); - m_tmp_ru.resize(getDimRu()); - m_tmp_ruD.resize(getDimRu(), getNInputs()); - m_tmp_ruru.resize(getDimRu(), getDimRu()); -} - -void bob::learn::em::FABase::updateCacheUbm() -{ - // Put supervectors in cache - if (m_ubm) - { - m_cache_mean.resize(getSupervectorLength()); - m_cache_sigma.resize(getSupervectorLength()); - m_cache_mean = m_ubm->getMeanSupervector(); - m_cache_sigma = m_ubm->getVarianceSupervector(); - } -} - -void bob::learn::em::FABase::updateCacheUbmUVD() -{ - // Compute and put U^{T}.diag(sigma)^{-1} in cache - if (m_ubm) - { - blitz::firstIndex i; - blitz::secondIndex j; - m_cache_UtSigmaInv.resize(getDimRu(), getSupervectorLength()); - m_cache_UtSigmaInv = m_U(j,i) / m_cache_sigma(j); // Ut * diag(sigma)^-1 - } -} - -void bob::learn::em::FABase::computeIdPlusUSProdInv(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,2>& output) const -{ - // Computes (Id + U^T.Sigma^-1.U.N_{i,h}.U)^-1 = - // (Id + sum_{c=1..C} N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c})^-1 - - // Blitz compatibility: ugly fix (const_cast, as old blitz version does not - // provide a non-const version of transpose()) - blitz::Array<double,2> Ut = const_cast<blitz::Array<double,2>&>(m_U).transpose(1,0); - - blitz::firstIndex i; - blitz::secondIndex j; - blitz::Range rall = blitz::Range::all(); - - bob::math::eye(m_tmp_ruru); // m_tmp_ruru = Id - // Loop and add N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c} to m_tmp_ruru at each iteration - const size_t dim_c = getNGaussians(); - const size_t dim_d = getNInputs(); - for(size_t c=0; c<dim_c; ++c) { - blitz::Range rc(c*dim_d,(c+1)*dim_d-1); - blitz::Array<double,2> Ut_c = Ut(rall,rc); - blitz::Array<double,1> sigma_c = m_cache_sigma(rc); - m_tmp_ruD = Ut_c(i,j) / sigma_c(j); // U_{c}^T.Sigma_{c}^-1 - blitz::Array<double,2> U_c = m_U(rc,rall); - // Use m_cache_IdPlusUSProdInv as an intermediate array - bob::math::prod(m_tmp_ruD, U_c, output); // U_{c}^T.Sigma_{c}^-1.U_{c} - // Finally, add N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c} to m_tmp_ruru - m_tmp_ruru += output * gmm_stats.n(c); - } - // Computes the inverse - bob::math::inv(m_tmp_ruru, output); -} - - -void bob::learn::em::FABase::computeFn_x(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,1>& output) const -{ - // Compute Fn_x = sum_{sessions h}(N*(o - m) (Normalised first order statistics) - blitz::Range rall = blitz::Range::all(); - const size_t dim_c = getNGaussians(); - const size_t dim_d = getNInputs(); - for(size_t c=0; c<dim_c; ++c) { - blitz::Range rc(c*dim_d,(c+1)*dim_d-1); - blitz::Array<double,1> Fn_x_c = output(rc); - blitz::Array<double,1> mean_c = m_cache_mean(rc); - Fn_x_c = gmm_stats.sumPx(c,rall) - mean_c*gmm_stats.n(c); - } -} - -void bob::learn::em::FABase::estimateX(const blitz::Array<double,2>& IdPlusUSProdInv, - const blitz::Array<double,1>& Fn_x, blitz::Array<double,1>& x) const -{ - // m_tmp_ru = UtSigmaInv * Fn_x = Ut*diag(sigma)^-1 * N*(o - m) - bob::math::prod(m_cache_UtSigmaInv, Fn_x, m_tmp_ru); - // x = IdPlusUSProdInv * m_cache_UtSigmaInv * Fn_x - bob::math::prod(IdPlusUSProdInv, m_tmp_ru, x); -} - - -void bob::learn::em::FABase::estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const -{ - if (!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine."); - computeIdPlusUSProdInv(gmm_stats, m_tmp_IdPlusUSProdInv); // Computes first term - computeFn_x(gmm_stats, m_tmp_Fn_x); // Computes last term - estimateX(m_tmp_IdPlusUSProdInv, m_tmp_Fn_x, x); // Estimates the value of x -} - diff --git a/bob/learn/em/cpp/FABaseTrainer.cpp b/bob/learn/em/cpp/FABaseTrainer.cpp deleted file mode 100644 index 09e4a4b69e262775b8ef529697f04ed2d2804621..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/FABaseTrainer.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/** - * @date Sat Jan 31 17:16:17 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief FABaseTrainer functions - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/FABaseTrainer.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.core/array_random.h> -#include <bob.math/inv.h> -#include <bob.math/linear.h> -#include <bob.core/check.h> -#include <bob.core/array_repmat.h> -#include <algorithm> - - -bob::learn::em::FABaseTrainer::FABaseTrainer(): - m_Nid(0), m_dim_C(0), m_dim_D(0), m_dim_ru(0), m_dim_rv(0), - m_x(0), m_y(0), m_z(0), m_Nacc(0), m_Facc(0) -{ -} - -bob::learn::em::FABaseTrainer::FABaseTrainer(const bob::learn::em::FABaseTrainer& other) -{ -} - -bob::learn::em::FABaseTrainer::~FABaseTrainer() -{ -} - -void bob::learn::em::FABaseTrainer::checkStatistics( - const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - for (size_t id=0; id<stats.size(); ++id) { - for (size_t s=0; s<stats[id].size(); ++s) { - if (stats[id][s]->sumPx.extent(0) != (int)m_dim_C) { - boost::format m("GMMStats C dimension parameter = %d is different than the expected value of %d"); - m % stats[id][s]->sumPx.extent(0) % (int)m_dim_C; - throw std::runtime_error(m.str()); - } - if (stats[id][s]->sumPx.extent(1) != (int)m_dim_D) { - boost::format m("GMMStats D dimension parameter = %d is different than the expected value of %d"); - m % stats[id][s]->sumPx.extent(1) % (int)m_dim_D; - throw std::runtime_error(m.str()); - } - } - } -} - - -void bob::learn::em::FABaseTrainer::initUbmNidSumStatistics( - const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - m_Nid = stats.size(); - boost::shared_ptr<bob::learn::em::GMMMachine> ubm = m.getUbm(); - // Put UBM in cache - m_dim_C = ubm->getNGaussians(); - m_dim_D = ubm->getNInputs(); - m_dim_ru = m.getDimRu(); - m_dim_rv = m.getDimRv(); - // Check statistics dimensionality - checkStatistics(m, stats); - // Precomputes the sum of the statistics for each client/identity - precomputeSumStatisticsN(stats); - precomputeSumStatisticsF(stats); - // Cache and working arrays - initCache(); -} - -void bob::learn::em::FABaseTrainer::precomputeSumStatisticsN( - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - m_Nacc.clear(); - blitz::Array<double,1> Nsum(m_dim_C); - for (size_t id=0; id<stats.size(); ++id) { - Nsum = 0.; - for (size_t s=0; s<stats[id].size(); ++s) { - Nsum += stats[id][s]->n; - } - m_Nacc.push_back(bob::core::array::ccopy(Nsum)); - } -} - -void bob::learn::em::FABaseTrainer::precomputeSumStatisticsF( - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - m_Facc.clear(); - blitz::Array<double,1> Fsum(m_dim_C*m_dim_D); - for (size_t id=0; id<stats.size(); ++id) { - Fsum = 0.; - for (size_t s=0; s<stats[id].size(); ++s) { - for (size_t c=0; c<m_dim_C; ++c) { - blitz::Array<double,1> Fsum_c = Fsum(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1)); - Fsum_c += stats[id][s]->sumPx(c,blitz::Range::all()); - } - } - m_Facc.push_back(bob::core::array::ccopy(Fsum)); - } -} - -void bob::learn::em::FABaseTrainer::initializeXYZ(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& vec) -{ - m_x.clear(); - m_y.clear(); - m_z.clear(); - - blitz::Array<double,1> z0(m_dim_C*m_dim_D); - z0 = 0; - blitz::Array<double,1> y0(m_dim_rv); - y0 = 0; - blitz::Array<double,2> x0(m_dim_ru,0); - x0 = 0; - for (size_t i=0; i<vec.size(); ++i) - { - m_z.push_back(bob::core::array::ccopy(z0)); - m_y.push_back(bob::core::array::ccopy(y0)); - x0.resize(m_dim_ru, vec[i].size()); - x0 = 0; - m_x.push_back(bob::core::array::ccopy(x0)); - } -} - -void bob::learn::em::FABaseTrainer::resetXYZ() -{ - for (size_t i=0; i<m_x.size(); ++i) - { - m_x[i] = 0.; - m_y[i] = 0.; - m_z[i] = 0.; - } -} - -void bob::learn::em::FABaseTrainer::initCache() -{ - const size_t dim_CD = m_dim_C*m_dim_D; - // U - m_cache_UtSigmaInv.resize(m_dim_ru, dim_CD); - m_cache_UProd.resize(m_dim_C, m_dim_ru, m_dim_ru); - m_cache_IdPlusUProd_ih.resize(m_dim_ru, m_dim_ru); - m_cache_Fn_x_ih.resize(dim_CD); - m_acc_U_A1.resize(m_dim_C, m_dim_ru, m_dim_ru); - m_acc_U_A2.resize(dim_CD, m_dim_ru); - // V - m_cache_VtSigmaInv.resize(m_dim_rv, dim_CD); - m_cache_VProd.resize(m_dim_C, m_dim_rv, m_dim_rv); - m_cache_IdPlusVProd_i.resize(m_dim_rv, m_dim_rv); - m_cache_Fn_y_i.resize(dim_CD); - m_acc_V_A1.resize(m_dim_C, m_dim_rv, m_dim_rv); - m_acc_V_A2.resize(dim_CD, m_dim_rv); - // D - m_cache_DtSigmaInv.resize(dim_CD); - m_cache_DProd.resize(dim_CD); - m_cache_IdPlusDProd_i.resize(dim_CD); - m_cache_Fn_z_i.resize(dim_CD); - m_acc_D_A1.resize(dim_CD); - m_acc_D_A2.resize(dim_CD); - - // tmp - m_tmp_CD.resize(dim_CD); - m_tmp_CD_b.resize(dim_CD); - - m_tmp_ru.resize(m_dim_ru); - m_tmp_ruD.resize(m_dim_ru, m_dim_D); - m_tmp_ruru.resize(m_dim_ru, m_dim_ru); - - m_tmp_rv.resize(m_dim_rv); - m_tmp_rvD.resize(m_dim_rv, m_dim_D); - m_tmp_rvrv.resize(m_dim_rv, m_dim_rv); -} - - - -//////////////////////////// V /////////////////////////// -void bob::learn::em::FABaseTrainer::computeVtSigmaInv(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,2>& V = m.getV(); - // Blitz compatibility: ugly fix (const_cast, as old blitz version does not - // provide a non-const version of transpose()) - const blitz::Array<double,2> Vt = const_cast<blitz::Array<double,2>&>(V).transpose(1,0); - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - blitz::firstIndex i; - blitz::secondIndex j; - m_cache_VtSigmaInv = Vt(i,j) / sigma(j); // Vt * diag(sigma)^-1 -} - -void bob::learn::em::FABaseTrainer::computeVProd(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,2>& V = m.getV(); - blitz::firstIndex i; - blitz::secondIndex j; - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - blitz::Range rall = blitz::Range::all(); - for (size_t c=0; c<m_dim_C; ++c) - { - blitz::Array<double,2> VProd_c = m_cache_VProd(c, rall, rall); - blitz::Array<double,2> Vv_c = V(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall); - blitz::Array<double,2> Vt_c = Vv_c.transpose(1,0); - blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1)); - m_tmp_rvD = Vt_c(i,j) / sigma_c(j); // Vt_c * diag(sigma)^-1 - bob::math::prod(m_tmp_rvD, Vv_c, VProd_c); - } -} - -void bob::learn::em::FABaseTrainer::computeIdPlusVProd_i(const size_t id) -{ - const blitz::Array<double,1>& Ni = m_Nacc[id]; - bob::math::eye(m_tmp_rvrv); // m_tmp_rvrv = I - blitz::Range rall = blitz::Range::all(); - for (size_t c=0; c<m_dim_C; ++c) { - blitz::Array<double,2> VProd_c = m_cache_VProd(c, rall, rall); - m_tmp_rvrv += VProd_c * Ni(c); - } - bob::math::inv(m_tmp_rvrv, m_cache_IdPlusVProd_i); // m_cache_IdPlusVProd_i = ( I+Vt*diag(sigma)^-1*Ni*V)^-1 -} - -void bob::learn::em::FABaseTrainer::computeFn_y_i(const bob::learn::em::FABase& mb, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id) -{ - const blitz::Array<double,2>& U = mb.getU(); - const blitz::Array<double,1>& d = mb.getD(); - // Compute Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) (Normalised first order statistics) - const blitz::Array<double,1>& Fi = m_Facc[id]; - const blitz::Array<double,1>& m = mb.getUbmMean(); - const blitz::Array<double,1>& z = m_z[id]; - bob::core::array::repelem(m_Nacc[id], m_tmp_CD); - m_cache_Fn_y_i = Fi - m_tmp_CD * (m + d * z); // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i}) - const blitz::Array<double,2>& X = m_x[id]; - blitz::Range rall = blitz::Range::all(); - for (int h=0; h<X.extent(1); ++h) // Loops over the sessions - { - blitz::Array<double,1> Xh = X(rall, h); // Xh = x_{i,h} (length: ru) - bob::math::prod(U, Xh, m_tmp_CD_b); // m_tmp_CD_b = U*x_{i,h} - const blitz::Array<double,1>& Nih = stats[h]->n; - bob::core::array::repelem(Nih, m_tmp_CD); - m_cache_Fn_y_i -= m_tmp_CD * m_tmp_CD_b; // N_{i,h} * U * x_{i,h} - } - // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) -} - -void bob::learn::em::FABaseTrainer::updateY_i(const size_t id) -{ - // Computes yi = Ayi * Cvs * Fn_yi - blitz::Array<double,1>& y = m_y[id]; - // m_tmp_rv = m_cache_VtSigmaInv * m_cache_Fn_y_i = Vt*diag(sigma)^-1 * sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) - bob::math::prod(m_cache_VtSigmaInv, m_cache_Fn_y_i, m_tmp_rv); - bob::math::prod(m_cache_IdPlusVProd_i, m_tmp_rv, y); -} - -void bob::learn::em::FABaseTrainer::updateY(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Precomputation - computeVtSigmaInv(m); - computeVProd(m); - // Loops over all people - for (size_t id=0; id<stats.size(); ++id) { - computeIdPlusVProd_i(id); - computeFn_y_i(m, stats[id], id); - updateY_i(id); - } -} - -void bob::learn::em::FABaseTrainer::computeAccumulatorsV( - const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Initializes the cache accumulator - m_acc_V_A1 = 0.; - m_acc_V_A2 = 0.; - // Loops over all people - blitz::firstIndex i; - blitz::secondIndex j; - blitz::Range rall = blitz::Range::all(); - for (size_t id=0; id<stats.size(); ++id) { - computeIdPlusVProd_i(id); - computeFn_y_i(m, stats[id], id); - - // Needs to return values to be accumulated for estimating V - const blitz::Array<double,1>& y = m_y[id]; - m_tmp_rvrv = m_cache_IdPlusVProd_i; - m_tmp_rvrv += y(i) * y(j); - for (size_t c=0; c<m_dim_C; ++c) - { - blitz::Array<double,2> A1_y_c = m_acc_V_A1(c, rall, rall); - A1_y_c += m_tmp_rvrv * m_Nacc[id](c); - } - m_acc_V_A2 += m_cache_Fn_y_i(i) * y(j); - } -} - -void bob::learn::em::FABaseTrainer::updateV(blitz::Array<double,2>& V) -{ - blitz::Range rall = blitz::Range::all(); - for (size_t c=0; c<m_dim_C; ++c) - { - const blitz::Array<double,2> A1 = m_acc_V_A1(c, rall, rall); - bob::math::inv(A1, m_tmp_rvrv); - const blitz::Array<double,2> A2 = m_acc_V_A2(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall); - blitz::Array<double,2> V_c = V(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), rall); - bob::math::prod(A2, m_tmp_rvrv, V_c); - } -} - - -//////////////////////////// U /////////////////////////// -void bob::learn::em::FABaseTrainer::computeUtSigmaInv(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,2>& U = m.getU(); - // Blitz compatibility: ugly fix (const_cast, as old blitz version does not - // provide a non-const version of transpose()) - const blitz::Array<double,2> Ut = const_cast<blitz::Array<double,2>&>(U).transpose(1,0); - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - blitz::firstIndex i; - blitz::secondIndex j; - m_cache_UtSigmaInv = Ut(i,j) / sigma(j); // Ut * diag(sigma)^-1 -} - -void bob::learn::em::FABaseTrainer::computeUProd(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,2>& U = m.getU(); - blitz::firstIndex i; - blitz::secondIndex j; - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - for (size_t c=0; c<m_dim_C; ++c) - { - blitz::Array<double,2> UProd_c = m_cache_UProd(c, blitz::Range::all(), blitz::Range::all()); - blitz::Array<double,2> Uu_c = U(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1), blitz::Range::all()); - blitz::Array<double,2> Ut_c = Uu_c.transpose(1,0); - blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1)); - m_tmp_ruD = Ut_c(i,j) / sigma_c(j); // Ut_c * diag(sigma)^-1 - bob::math::prod(m_tmp_ruD, Uu_c, UProd_c); - } -} - -void bob::learn::em::FABaseTrainer::computeIdPlusUProd_ih( - const boost::shared_ptr<bob::learn::em::GMMStats>& stats) -{ - const blitz::Array<double,1>& Nih = stats->n; - bob::math::eye(m_tmp_ruru); // m_tmp_ruru = I - for (size_t c=0; c<m_dim_C; ++c) { - blitz::Array<double,2> UProd_c = m_cache_UProd(c,blitz::Range::all(),blitz::Range::all()); - m_tmp_ruru += UProd_c * Nih(c); - } - bob::math::inv(m_tmp_ruru, m_cache_IdPlusUProd_ih); // m_cache_IdPlusUProd_ih = ( I+Ut*diag(sigma)^-1*Ni*U)^-1 -} - -void bob::learn::em::FABaseTrainer::computeFn_x_ih(const bob::learn::em::FABase& mb, - const boost::shared_ptr<bob::learn::em::GMMStats>& stats, const size_t id) -{ - const blitz::Array<double,2>& V = mb.getV(); - const blitz::Array<double,1>& d = mb.getD(); - // Compute Fn_x_ih = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i}) (Normalised first order statistics) - const blitz::Array<double,2>& Fih = stats->sumPx; - const blitz::Array<double,1>& m = mb.getUbmMean(); - const blitz::Array<double,1>& z = m_z[id]; - const blitz::Array<double,1>& Nih = stats->n; - bob::core::array::repelem(Nih, m_tmp_CD); - for (size_t c=0; c<m_dim_C; ++c) { - blitz::Array<double,1> Fn_x_ih_c = m_cache_Fn_x_ih(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1)); - Fn_x_ih_c = Fih(c,blitz::Range::all()); - } - m_cache_Fn_x_ih -= m_tmp_CD * (m + d * z); // Fn_x_ih = N_{i,h}*(o_{i,h} - m - D*z_{i}) - - const blitz::Array<double,1>& y = m_y[id]; - bob::math::prod(V, y, m_tmp_CD_b); - m_cache_Fn_x_ih -= m_tmp_CD * m_tmp_CD_b; - // Fn_x_ih = N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i}) -} - -void bob::learn::em::FABaseTrainer::updateX_ih(const size_t id, const size_t h) -{ - // Computes xih = Axih * Cus * Fn_x_ih - blitz::Array<double,1> x = m_x[id](blitz::Range::all(), h); - // m_tmp_ru = m_cache_UtSigmaInv * m_cache_Fn_x_ih = Ut*diag(sigma)^-1 * N_{i,h}*(o_{i,h} - m - D*z_{i} - V*y_{i}) - bob::math::prod(m_cache_UtSigmaInv, m_cache_Fn_x_ih, m_tmp_ru); - bob::math::prod(m_cache_IdPlusUProd_ih, m_tmp_ru, x); -} - -void bob::learn::em::FABaseTrainer::updateX(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Precomputation - computeUtSigmaInv(m); - computeUProd(m); - // Loops over all people - for (size_t id=0; id<stats.size(); ++id) { - int n_session_i = stats[id].size(); - for (int s=0; s<n_session_i; ++s) { - computeIdPlusUProd_ih(stats[id][s]); - computeFn_x_ih(m, stats[id][s], id); - updateX_ih(id, s); - } - } -} - -void bob::learn::em::FABaseTrainer::computeAccumulatorsU( - const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Initializes the cache accumulator - m_acc_U_A1 = 0.; - m_acc_U_A2 = 0.; - // Loops over all people - blitz::firstIndex i; - blitz::secondIndex j; - blitz::Range rall = blitz::Range::all(); - for (size_t id=0; id<stats.size(); ++id) { - int n_session_i = stats[id].size(); - for (int h=0; h<n_session_i; ++h) { - computeIdPlusUProd_ih(stats[id][h]); - computeFn_x_ih(m, stats[id][h], id); - - // Needs to return values to be accumulated for estimating U - blitz::Array<double,1> x = m_x[id](rall, h); - m_tmp_ruru = m_cache_IdPlusUProd_ih; - m_tmp_ruru += x(i) * x(j); - for (int c=0; c<(int)m_dim_C; ++c) - { - blitz::Array<double,2> A1_x_c = m_acc_U_A1(c,rall,rall); - A1_x_c += m_tmp_ruru * stats[id][h]->n(c); - } - m_acc_U_A2 += m_cache_Fn_x_ih(i) * x(j); - } - } -} - -void bob::learn::em::FABaseTrainer::updateU(blitz::Array<double,2>& U) -{ - for (size_t c=0; c<m_dim_C; ++c) - { - const blitz::Array<double,2> A1 = m_acc_U_A1(c,blitz::Range::all(),blitz::Range::all()); - bob::math::inv(A1, m_tmp_ruru); - const blitz::Array<double,2> A2 = m_acc_U_A2(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1),blitz::Range::all()); - blitz::Array<double,2> U_c = U(blitz::Range(c*m_dim_D,(c+1)*m_dim_D-1),blitz::Range::all()); - bob::math::prod(A2, m_tmp_ruru, U_c); - } -} - - -//////////////////////////// D /////////////////////////// -void bob::learn::em::FABaseTrainer::computeDtSigmaInv(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,1>& d = m.getD(); - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - m_cache_DtSigmaInv = d / sigma; // Dt * diag(sigma)^-1 -} - -void bob::learn::em::FABaseTrainer::computeDProd(const bob::learn::em::FABase& m) -{ - const blitz::Array<double,1>& d = m.getD(); - const blitz::Array<double,1>& sigma = m.getUbmVariance(); - m_cache_DProd = d / sigma * d; // Dt * diag(sigma)^-1 * D -} - -void bob::learn::em::FABaseTrainer::computeIdPlusDProd_i(const size_t id) -{ - const blitz::Array<double,1>& Ni = m_Nacc[id]; - bob::core::array::repelem(Ni, m_tmp_CD); // m_tmp_CD = Ni 'repmat' - m_cache_IdPlusDProd_i = 1.; // m_cache_IdPlusDProd_i = Id - m_cache_IdPlusDProd_i += m_cache_DProd * m_tmp_CD; // m_cache_IdPlusDProd_i = I+Dt*diag(sigma)^-1*Ni*D - m_cache_IdPlusDProd_i = 1 / m_cache_IdPlusDProd_i; // m_cache_IdPlusVProd_i = (I+Dt*diag(sigma)^-1*Ni*D)^-1 -} - -void bob::learn::em::FABaseTrainer::computeFn_z_i( - const bob::learn::em::FABase& mb, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id) -{ - const blitz::Array<double,2>& U = mb.getU(); - const blitz::Array<double,2>& V = mb.getV(); - // Compute Fn_z_i = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h}) (Normalised first order statistics) - const blitz::Array<double,1>& Fi = m_Facc[id]; - const blitz::Array<double,1>& m = mb.getUbmMean(); - const blitz::Array<double,1>& y = m_y[id]; - bob::core::array::repelem(m_Nacc[id], m_tmp_CD); - bob::math::prod(V, y, m_tmp_CD_b); // m_tmp_CD_b = V * y - m_cache_Fn_z_i = Fi - m_tmp_CD * (m + m_tmp_CD_b); // Fn_yi = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i}) - - const blitz::Array<double,2>& X = m_x[id]; - blitz::Range rall = blitz::Range::all(); - for (int h=0; h<X.extent(1); ++h) // Loops over the sessions - { - const blitz::Array<double,1>& Nh = stats[h]->n; // Nh = N_{i,h} (length: C) - bob::core::array::repelem(Nh, m_tmp_CD); - blitz::Array<double,1> Xh = X(rall, h); // Xh = x_{i,h} (length: ru) - bob::math::prod(U, Xh, m_tmp_CD_b); - m_cache_Fn_z_i -= m_tmp_CD * m_tmp_CD_b; - } - // Fn_z_i = sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h}) -} - -void bob::learn::em::FABaseTrainer::updateZ_i(const size_t id) -{ - // Computes zi = Azi * D^T.Sigma^-1 * Fn_zi - blitz::Array<double,1>& z = m_z[id]; - // m_tmp_CD = m_cache_DtSigmaInv * m_cache_Fn_z_i = Dt*diag(sigma)^-1 * sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h}) - z = m_cache_IdPlusDProd_i * m_cache_DtSigmaInv * m_cache_Fn_z_i; -} - -void bob::learn::em::FABaseTrainer::updateZ(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Precomputation - computeDtSigmaInv(m); - computeDProd(m); - // Loops over all people - for (size_t id=0; id<m_Nid; ++id) { - computeIdPlusDProd_i(id); - computeFn_z_i(m, stats[id], id); - updateZ_i(id); - } -} - -void bob::learn::em::FABaseTrainer::computeAccumulatorsD( - const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats) -{ - // Initializes the cache accumulator - m_acc_D_A1 = 0.; - m_acc_D_A2 = 0.; - // Loops over all people - blitz::firstIndex i; - blitz::secondIndex j; - for (size_t id=0; id<stats.size(); ++id) { - computeIdPlusDProd_i(id); - computeFn_z_i(m, stats[id], id); - - // Needs to return values to be accumulated for estimating D - blitz::Array<double,1> z = m_z[id]; - bob::core::array::repelem(m_Nacc[id], m_tmp_CD); - m_acc_D_A1 += (m_cache_IdPlusDProd_i + z * z) * m_tmp_CD; - m_acc_D_A2 += m_cache_Fn_z_i * z; - } -} - -void bob::learn::em::FABaseTrainer::updateD(blitz::Array<double,1>& d) -{ - d = m_acc_D_A2 / m_acc_D_A1; -} - - diff --git a/bob/learn/em/cpp/GMMBaseTrainer.cpp b/bob/learn/em/cpp/GMMBaseTrainer.cpp deleted file mode 100644 index f4697bd527e018866f6dfa10232e4b36546b5169..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/GMMBaseTrainer.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/GMMBaseTrainer.h> -#include <bob.core/assert.h> -#include <bob.core/check.h> - -bob::learn::em::GMMBaseTrainer::GMMBaseTrainer(const bool update_means, - const bool update_variances, const bool update_weights, - const double mean_var_update_responsibilities_threshold): - m_ss(new bob::learn::em::GMMStats()), - m_update_means(update_means), m_update_variances(update_variances), - m_update_weights(update_weights), - m_mean_var_update_responsibilities_threshold(mean_var_update_responsibilities_threshold) -{} - -bob::learn::em::GMMBaseTrainer::GMMBaseTrainer(const bob::learn::em::GMMBaseTrainer& b): - m_ss(new bob::learn::em::GMMStats( *b.getGMMStats() )), - m_update_means(b.m_update_means), m_update_variances(b.m_update_variances), - m_mean_var_update_responsibilities_threshold(b.m_mean_var_update_responsibilities_threshold) -{} - -bob::learn::em::GMMBaseTrainer::~GMMBaseTrainer() -{} - -void bob::learn::em::GMMBaseTrainer::initialize(bob::learn::em::GMMMachine& gmm) -{ - // Allocate memory for the sufficient statistics and initialise - m_ss->resize(gmm.getNGaussians(),gmm.getNInputs()); -} - -void bob::learn::em::GMMBaseTrainer::eStep(bob::learn::em::GMMMachine& gmm, - const blitz::Array<double,2>& data) -{ - m_ss->init(); - // Calculate the sufficient statistics and save in m_ss - gmm.accStatistics(data, *m_ss); -} - -double bob::learn::em::GMMBaseTrainer::computeLikelihood(bob::learn::em::GMMMachine& gmm) -{ - return m_ss->log_likelihood / m_ss->T; -} - - -bob::learn::em::GMMBaseTrainer& bob::learn::em::GMMBaseTrainer::operator= - (const bob::learn::em::GMMBaseTrainer &other) -{ - if (this != &other) - { - *m_ss = *other.m_ss; - m_update_means = other.m_update_means; - m_update_variances = other.m_update_variances; - m_update_weights = other.m_update_weights; - m_mean_var_update_responsibilities_threshold = other.m_mean_var_update_responsibilities_threshold; - } - return *this; -} - -bool bob::learn::em::GMMBaseTrainer::operator== - (const bob::learn::em::GMMBaseTrainer &other) const -{ - return *m_ss == *other.m_ss && - m_update_means == other.m_update_means && - m_update_variances == other.m_update_variances && - m_update_weights == other.m_update_weights && - m_mean_var_update_responsibilities_threshold == other.m_mean_var_update_responsibilities_threshold; -} - -bool bob::learn::em::GMMBaseTrainer::operator!= - (const bob::learn::em::GMMBaseTrainer &other) const -{ - return !(this->operator==(other)); -} - -bool bob::learn::em::GMMBaseTrainer::is_similar_to - (const bob::learn::em::GMMBaseTrainer &other, const double r_epsilon, - const double a_epsilon) const -{ - return *m_ss == *other.m_ss && - m_update_means == other.m_update_means && - m_update_variances == other.m_update_variances && - m_update_weights == other.m_update_weights && - bob::core::isClose(m_mean_var_update_responsibilities_threshold, - other.m_mean_var_update_responsibilities_threshold, r_epsilon, a_epsilon); -} - -void bob::learn::em::GMMBaseTrainer::setGMMStats(boost::shared_ptr<bob::learn::em::GMMStats> stats) -{ - bob::core::array::assertSameShape(m_ss->sumPx, stats->sumPx); - m_ss = stats; -} diff --git a/bob/learn/em/cpp/GMMMachine.cpp b/bob/learn/em/cpp/GMMMachine.cpp deleted file mode 100644 index d2512b7085f09d9938fbed063a092251d974b5c9..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/GMMMachine.cpp +++ /dev/null @@ -1,458 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/GMMMachine.h> -#include <bob.core/assert.h> -#include <bob.math/log.h> - -bob::learn::em::GMMMachine::GMMMachine(): m_gaussians(0) { - resize(0,0); -} - -bob::learn::em::GMMMachine::GMMMachine(const size_t n_gaussians, const size_t n_inputs): - m_gaussians(0) -{ - resize(n_gaussians,n_inputs); -} - -bob::learn::em::GMMMachine::GMMMachine(bob::io::base::HDF5File& config): - m_gaussians(0) -{ - load(config); -} - -bob::learn::em::GMMMachine::GMMMachine(const GMMMachine& other) -{ - copy(other); -} - - -bob::learn::em::GMMMachine& bob::learn::em::GMMMachine::operator=(const bob::learn::em::GMMMachine &other) { - // protect against invalid self-assignment - if (this != &other) - copy(other); - - // by convention, always return *this - return *this; -} - -bool bob::learn::em::GMMMachine::operator==(const bob::learn::em::GMMMachine& b) const -{ - if (m_n_gaussians != b.m_n_gaussians || m_n_inputs != b.m_n_inputs || - !bob::core::array::isEqual(m_weights, b.m_weights)) - return false; - - for(size_t i=0; i<m_n_gaussians; ++i) { - if(!(*(m_gaussians[i]) == *(b.m_gaussians[i]))) - return false; - } - - return true; -} - -bool bob::learn::em::GMMMachine::operator!=(const bob::learn::em::GMMMachine& b) const { - return !(this->operator==(b)); -} - -bool bob::learn::em::GMMMachine::is_similar_to(const bob::learn::em::GMMMachine& b, - const double r_epsilon, const double a_epsilon) const -{ - if (m_n_gaussians != b.m_n_gaussians || m_n_inputs != b.m_n_inputs || - !bob::core::array::isClose(m_weights, b.m_weights, r_epsilon, a_epsilon)) - return false; - - for (size_t i = 0; i < m_n_gaussians; ++i) - if (!m_gaussians[i]->is_similar_to(*b.m_gaussians[i], r_epsilon, a_epsilon)) - return false; - - return true; -} - -void bob::learn::em::GMMMachine::copy(const GMMMachine& other) { - m_n_gaussians = other.m_n_gaussians; - m_n_inputs = other.m_n_inputs; - - // Initialise weights - m_weights.resize(m_n_gaussians); - m_weights = other.m_weights; - - // Initialise Gaussians - m_gaussians.clear(); - for(size_t i=0; i<m_n_gaussians; ++i) { - boost::shared_ptr<bob::learn::em::Gaussian> g(new bob::learn::em::Gaussian(*(other.m_gaussians[i]))); - m_gaussians.push_back(g); - } - - // Initialise cache - initCache(); -} - - -bob::learn::em::GMMMachine::~GMMMachine() { -} - - -///////////////////// -// Setters -//////////////////// - -void bob::learn::em::GMMMachine::setWeights(const blitz::Array<double,1> &weights) { - bob::core::array::assertSameShape(weights, m_weights); - m_weights = weights; - recomputeLogWeights(); -} - -void bob::learn::em::GMMMachine::recomputeLogWeights() const -{ - m_cache_log_weights = blitz::log(m_weights); -} - -void bob::learn::em::GMMMachine::setMeans(const blitz::Array<double,2> &means) { - bob::core::array::assertSameDimensionLength(means.extent(0), m_n_gaussians); - bob::core::array::assertSameDimensionLength(means.extent(1), m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians[i]->updateMean() = means(i,blitz::Range::all()); - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::setMeanSupervector(const blitz::Array<double,1> &mean_supervector) { - bob::core::array::assertSameDimensionLength(mean_supervector.extent(0), m_n_gaussians*m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians[i]->updateMean() = mean_supervector(blitz::Range(i*m_n_inputs, (i+1)*m_n_inputs-1)); - m_cache_supervector = false; -} - - -void bob::learn::em::GMMMachine::setVariances(const blitz::Array<double, 2 >& variances) { - bob::core::array::assertSameDimensionLength(variances.extent(0), m_n_gaussians); - bob::core::array::assertSameDimensionLength(variances.extent(1), m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) { - m_gaussians[i]->updateVariance() = variances(i,blitz::Range::all()); - m_gaussians[i]->applyVarianceThresholds(); - } - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::setVarianceSupervector(const blitz::Array<double,1> &variance_supervector) { - bob::core::array::assertSameDimensionLength(variance_supervector.extent(0), m_n_gaussians*m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) { - m_gaussians[i]->updateVariance() = variance_supervector(blitz::Range(i*m_n_inputs, (i+1)*m_n_inputs-1)); - m_gaussians[i]->applyVarianceThresholds(); - } - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::setVarianceThresholds(const double value) { - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians[i]->setVarianceThresholds(value); - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::setVarianceThresholds(blitz::Array<double, 1> variance_thresholds) { - bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians[i]->setVarianceThresholds(variance_thresholds); - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::setVarianceThresholds(const blitz::Array<double, 2>& variance_thresholds) { - bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_gaussians); - bob::core::array::assertSameDimensionLength(variance_thresholds.extent(1), m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians[i]->setVarianceThresholds(variance_thresholds(i,blitz::Range::all())); - m_cache_supervector = false; -} - -///////////////////// -// Getters -//////////////////// - -const blitz::Array<double,2> bob::learn::em::GMMMachine::getMeans() const { - - blitz::Array<double,2> means(m_n_gaussians,m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - means(i,blitz::Range::all()) = m_gaussians[i]->getMean(); - - return means; -} - -const blitz::Array<double,2> bob::learn::em::GMMMachine::getVariances() const{ - - blitz::Array<double,2> variances(m_n_gaussians,m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - variances(i,blitz::Range::all()) = m_gaussians[i]->getVariance(); - - return variances; -} - - -const blitz::Array<double,2> bob::learn::em::GMMMachine::getVarianceThresholds() const { - //bob::core::array::assertSameDimensionLength(variance_thresholds.extent(0), m_n_gaussians); - //bob::core::array::assertSameDimensionLength(variance_thresholds.extent(1), m_n_inputs); - blitz::Array<double, 2> variance_thresholds(m_n_gaussians, m_n_inputs); - for(size_t i=0; i<m_n_gaussians; ++i) - variance_thresholds(i,blitz::Range::all()) = m_gaussians[i]->getVarianceThresholds(); - - return variance_thresholds; -} - - -///////////////////// -// Methods -//////////////////// - - -void bob::learn::em::GMMMachine::resize(const size_t n_gaussians, const size_t n_inputs) { - m_n_gaussians = n_gaussians; - m_n_inputs = n_inputs; - - // Initialise weights - m_weights.resize(m_n_gaussians); - m_weights = 1.0 / m_n_gaussians; - - // Initialise Gaussians - m_gaussians.clear(); - for(size_t i=0; i<m_n_gaussians; ++i) - m_gaussians.push_back(boost::shared_ptr<bob::learn::em::Gaussian>(new bob::learn::em::Gaussian(n_inputs))); - - // Initialise cache arrays - initCache(); -} - -double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 1> &x, - blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const -{ - // Check dimension - bob::core::array::assertSameDimensionLength(log_weighted_gaussian_likelihoods.extent(0), m_n_gaussians); - bob::core::array::assertSameDimensionLength(x.extent(0), m_n_inputs); - return logLikelihood_(x,log_weighted_gaussian_likelihoods); -} - - -double bob::learn::em::GMMMachine::logLikelihood_(const blitz::Array<double, 1> &x, - blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const -{ - // Initialise variables - double log_likelihood = bob::math::Log::LogZero; - - // Accumulate the weighted log likelihoods from each Gaussian - for(size_t i=0; i<m_n_gaussians; ++i) { - double l = m_cache_log_weights(i) + m_gaussians[i]->logLikelihood_(x); - log_weighted_gaussian_likelihoods(i) = l; - log_likelihood = bob::math::Log::logAdd(log_likelihood, l); - } - - // Return log(p(x|GMMMachine)) - return log_likelihood; -} - - -double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 2> &x) const { - // Check dimension - bob::core::array::assertSameDimensionLength(x.extent(1), m_n_inputs); - // Call the other logLikelihood_ (overloaded) function - - - double sum_ll = 0; - for (int i=0; i<x.extent(0); i++) - sum_ll+= logLikelihood_(x(i,blitz::Range::all())); - - return sum_ll/x.extent(0); -} - - - -double bob::learn::em::GMMMachine::logLikelihood(const blitz::Array<double, 1> &x) const { - // Check dimension - bob::core::array::assertSameDimensionLength(x.extent(0), m_n_inputs); - // Call the other logLikelihood_ (overloaded) function - // (log_weighted_gaussian_likelihoods will be discarded) - return logLikelihood_(x,m_cache_log_weighted_gaussian_likelihoods); -} - - - -double bob::learn::em::GMMMachine::logLikelihood_(const blitz::Array<double, 1> &x) const { - // Call the other logLikelihood (overloaded) function - // (log_weighted_gaussian_likelihoods will be discarded) - return logLikelihood_(x,m_cache_log_weighted_gaussian_likelihoods); -} - -void bob::learn::em::GMMMachine::accStatistics(const blitz::Array<double,2>& input, - bob::learn::em::GMMStats& stats) const { - // iterate over data - blitz::Range a = blitz::Range::all(); - for(int i=0; i<input.extent(0); ++i) { - // Get example - blitz::Array<double,1> x(input(i,a)); - // Accumulate statistics - accStatistics(x,stats); - } -} - -void bob::learn::em::GMMMachine::accStatistics_(const blitz::Array<double,2>& input, bob::learn::em::GMMStats& stats) const { - // iterate over data - blitz::Range a = blitz::Range::all(); - for(int i=0; i<input.extent(0); ++i) { - // Get example - blitz::Array<double,1> x(input(i, a)); - // Accumulate statistics - accStatistics_(x,stats); - } -} - -void bob::learn::em::GMMMachine::accStatistics(const blitz::Array<double, 1>& x, bob::learn::em::GMMStats& stats) const { - // check GMMStats size - bob::core::array::assertSameDimensionLength(stats.sumPx.extent(0), m_n_gaussians); - bob::core::array::assertSameDimensionLength(stats.sumPx.extent(1), m_n_inputs); - - // Calculate Gaussian and GMM likelihoods - // - m_cache_log_weighted_gaussian_likelihoods(i) = log(weight_i*p(x|gaussian_i)) - // - log_likelihood = log(sum_i(weight_i*p(x|gaussian_i))) - double log_likelihood = logLikelihood(x, m_cache_log_weighted_gaussian_likelihoods); - - accStatisticsInternal(x, stats, log_likelihood); -} - -void bob::learn::em::GMMMachine::accStatistics_(const blitz::Array<double, 1>& x, bob::learn::em::GMMStats& stats) const { - // Calculate Gaussian and GMM likelihoods - // - m_cache_log_weighted_gaussian_likelihoods(i) = log(weight_i*p(x|gaussian_i)) - // - log_likelihood = log(sum_i(weight_i*p(x|gaussian_i))) - double log_likelihood = logLikelihood_(x, m_cache_log_weighted_gaussian_likelihoods); - - accStatisticsInternal(x, stats, log_likelihood); -} - -void bob::learn::em::GMMMachine::accStatisticsInternal(const blitz::Array<double, 1>& x, - bob::learn::em::GMMStats& stats, const double log_likelihood) const -{ - // Calculate responsibilities - m_cache_P = blitz::exp(m_cache_log_weighted_gaussian_likelihoods - log_likelihood); - - // Accumulate statistics - // - total likelihood - stats.log_likelihood += log_likelihood; - - // - number of samples - stats.T++; - - // - responsibilities - stats.n += m_cache_P; - - // - first order stats - blitz::firstIndex i; - blitz::secondIndex j; - - m_cache_Px = m_cache_P(i) * x(j); - - stats.sumPx += m_cache_Px; - - // - second order stats - stats.sumPxx += (m_cache_Px(i,j) * x(j)); -} - -boost::shared_ptr<bob::learn::em::Gaussian> bob::learn::em::GMMMachine::getGaussian(const size_t i) { - if (i>=m_n_gaussians) { - throw std::runtime_error("getGaussian(): index out of bounds"); - } - return m_gaussians[i]; -} - -void bob::learn::em::GMMMachine::save(bob::io::base::HDF5File& config) const { - int64_t v = static_cast<int64_t>(m_n_gaussians); - config.set("m_n_gaussians", v); - v = static_cast<int64_t>(m_n_inputs); - config.set("m_n_inputs", v); - - for(size_t i=0; i<m_n_gaussians; ++i) { - std::ostringstream oss; - oss << "m_gaussians" << i; - - if (!config.hasGroup(oss.str())) config.createGroup(oss.str()); - config.cd(oss.str()); - m_gaussians[i]->save(config); - config.cd(".."); - } - - config.setArray("m_weights", m_weights); -} - -void bob::learn::em::GMMMachine::load(bob::io::base::HDF5File& config) { - int64_t v; - v = config.read<int64_t>("m_n_gaussians"); - m_n_gaussians = static_cast<size_t>(v); - v = config.read<int64_t>("m_n_inputs"); - m_n_inputs = static_cast<size_t>(v); - - m_gaussians.clear(); - for(size_t i=0; i<m_n_gaussians; ++i) { - m_gaussians.push_back(boost::shared_ptr<bob::learn::em::Gaussian>(new bob::learn::em::Gaussian(m_n_inputs))); - std::ostringstream oss; - oss << "m_gaussians" << i; - config.cd(oss.str()); - m_gaussians[i]->load(config); - config.cd(".."); - } - - m_weights.resize(m_n_gaussians); - config.readArray("m_weights", m_weights); - - // Initialise cache - initCache(); -} - -void bob::learn::em::GMMMachine::updateCacheSupervectors() const -{ - m_cache_mean_supervector.resize(m_n_gaussians*m_n_inputs); - m_cache_variance_supervector.resize(m_n_gaussians*m_n_inputs); - - for(size_t i=0; i<m_n_gaussians; ++i) { - blitz::Range range(i*m_n_inputs, (i+1)*m_n_inputs-1); - m_cache_mean_supervector(range) = m_gaussians[i]->getMean(); - m_cache_variance_supervector(range) = m_gaussians[i]->getVariance(); - } - m_cache_supervector = true; -} - -void bob::learn::em::GMMMachine::initCache() const { - // Initialise cache arrays - m_cache_log_weights.resize(m_n_gaussians); - recomputeLogWeights(); - m_cache_log_weighted_gaussian_likelihoods.resize(m_n_gaussians); - m_cache_P.resize(m_n_gaussians); - m_cache_Px.resize(m_n_gaussians,m_n_inputs); - m_cache_supervector = false; -} - -void bob::learn::em::GMMMachine::reloadCacheSupervectors() const { - if(!m_cache_supervector) - updateCacheSupervectors(); -} - -const blitz::Array<double,1>& bob::learn::em::GMMMachine::getMeanSupervector() const { - if(!m_cache_supervector) - updateCacheSupervectors(); - return m_cache_mean_supervector; -} - -const blitz::Array<double,1>& bob::learn::em::GMMMachine::getVarianceSupervector() const { - if(!m_cache_supervector) - updateCacheSupervectors(); - return m_cache_variance_supervector; -} - -namespace bob { namespace learn { namespace em { - std::ostream& operator<<(std::ostream& os, const GMMMachine& machine) { - os << "Weights = " << machine.m_weights << std::endl; - for(size_t i=0; i < machine.m_n_gaussians; ++i) { - os << "Gaussian " << i << ": " << std::endl << *(machine.m_gaussians[i]); - } - - return os; - } -} } } diff --git a/bob/learn/em/cpp/GMMStats.cpp b/bob/learn/em/cpp/GMMStats.cpp deleted file mode 100644 index 8a9f783c58068b2b67f9a7ac0e735447ff265143..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/GMMStats.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/GMMStats.h> -#include <bob.core/logging.h> -#include <bob.core/check.h> - -bob::learn::em::GMMStats::GMMStats() { - resize(0,0); -} - -bob::learn::em::GMMStats::GMMStats(const size_t n_gaussians, const size_t n_inputs) { - resize(n_gaussians,n_inputs); -} - -bob::learn::em::GMMStats::GMMStats(bob::io::base::HDF5File& config) { - load(config); -} - -bob::learn::em::GMMStats::GMMStats(const bob::learn::em::GMMStats& other) { - copy(other); -} - -bob::learn::em::GMMStats::~GMMStats() { -} - -bob::learn::em::GMMStats& -bob::learn::em::GMMStats::operator=(const bob::learn::em::GMMStats& other) { - // protect against invalid self-assignment - if (this != &other) - copy(other); - - // by convention, always return *this - return *this; -} - -bool bob::learn::em::GMMStats::operator==(const bob::learn::em::GMMStats& b) const -{ - return (T == b.T && log_likelihood == b.log_likelihood && - bob::core::array::isEqual(n, b.n) && - bob::core::array::isEqual(sumPx, b.sumPx) && - bob::core::array::isEqual(sumPxx, b.sumPxx)); -} - -bool -bob::learn::em::GMMStats::operator!=(const bob::learn::em::GMMStats& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::GMMStats::is_similar_to(const bob::learn::em::GMMStats& b, - const double r_epsilon, const double a_epsilon) const -{ - return (T == b.T && - bob::core::isClose(log_likelihood, b.log_likelihood, r_epsilon, a_epsilon) && - bob::core::array::isClose(n, b.n, r_epsilon, a_epsilon) && - bob::core::array::isClose(sumPx, b.sumPx, r_epsilon, a_epsilon) && - bob::core::array::isClose(sumPxx, b.sumPxx, r_epsilon, a_epsilon)); -} - - -void bob::learn::em::GMMStats::operator+=(const bob::learn::em::GMMStats& b) { - // Check dimensions - if(n.extent(0) != b.n.extent(0) || - sumPx.extent(0) != b.sumPx.extent(0) || sumPx.extent(1) != b.sumPx.extent(1) || - sumPxx.extent(0) != b.sumPxx.extent(0) || sumPxx.extent(1) != b.sumPxx.extent(1)) - // TODO: add a specialized exception - throw std::runtime_error("if you see this exception, fill a bug report"); - - // Update GMMStats object with the content of the other one - T += b.T; - log_likelihood += b.log_likelihood; - n += b.n; - sumPx += b.sumPx; - sumPxx += b.sumPxx; -} - -void bob::learn::em::GMMStats::copy(const GMMStats& other) { - // Resize arrays - resize(other.sumPx.extent(0),other.sumPx.extent(1)); - // Copy content - T = other.T; - log_likelihood = other.log_likelihood; - n = other.n; - sumPx = other.sumPx; - sumPxx = other.sumPxx; -} - -void bob::learn::em::GMMStats::resize(const size_t n_gaussians, const size_t n_inputs) { - n.resize(n_gaussians); - sumPx.resize(n_gaussians, n_inputs); - sumPxx.resize(n_gaussians, n_inputs); - init(); -} - -void bob::learn::em::GMMStats::init() { - log_likelihood = 0; - T = 0; - n = 0.0; - sumPx = 0.0; - sumPxx = 0.0; -} - -void bob::learn::em::GMMStats::save(bob::io::base::HDF5File& config) const { - //please note we fix the output values to be of a precise type so they can be - //retrieved at any platform with the exact same precision. - // TODO: add versioning, replace int64_t by uint64_t and log_liklihood by log_likelihood - int64_t sumpx_shape_0 = sumPx.shape()[0]; - int64_t sumpx_shape_1 = sumPx.shape()[1]; - config.set("n_gaussians", sumpx_shape_0); - config.set("n_inputs", sumpx_shape_1); - config.set("log_liklihood", log_likelihood); //double - config.set("T", static_cast<int64_t>(T)); - config.setArray("n", n); //Array1d - config.setArray("sumPx", sumPx); //Array2d - config.setArray("sumPxx", sumPxx); //Array2d -} - -void bob::learn::em::GMMStats::load(bob::io::base::HDF5File& config) { - log_likelihood = config.read<double>("log_liklihood"); - int64_t n_gaussians = config.read<int64_t>("n_gaussians"); - int64_t n_inputs = config.read<int64_t>("n_inputs"); - T = static_cast<size_t>(config.read<int64_t>("T")); - - //resize arrays to prepare for HDF5 readout - n.resize(n_gaussians); - sumPx.resize(n_gaussians, n_inputs); - sumPxx.resize(n_gaussians, n_inputs); - - //load data - config.readArray("n", n); - config.readArray("sumPx", sumPx); - config.readArray("sumPxx", sumPxx); -} - -namespace bob { namespace learn { namespace em { - std::ostream& operator<<(std::ostream& os, const GMMStats& g) { - os << "log_likelihood = " << g.log_likelihood << std::endl; - os << "T = " << g.T << std::endl; - os << "n = " << g.n; - os << "sumPx = " << g.sumPx; - os << "sumPxx = " << g.sumPxx; - - return os; - } -} } } diff --git a/bob/learn/em/cpp/Gaussian.cpp b/bob/learn/em/cpp/Gaussian.cpp deleted file mode 100644 index cd45f60f4248605bc48a934fbb7e7df5bf12b766..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/Gaussian.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/Gaussian.h> - -#include <bob.core/assert.h> -#include <bob.math/log.h> - -bob::learn::em::Gaussian::Gaussian() { - resize(0); -} - -bob::learn::em::Gaussian::Gaussian(const size_t n_inputs) { - resize(n_inputs); -} - -bob::learn::em::Gaussian::Gaussian(const bob::learn::em::Gaussian& other) { - copy(other); -} - -bob::learn::em::Gaussian::Gaussian(bob::io::base::HDF5File& config) { - load(config); -} - -bob::learn::em::Gaussian::~Gaussian() { -} - -bob::learn::em::Gaussian& bob::learn::em::Gaussian::operator=(const bob::learn::em::Gaussian &other) { - if(this != &other) - copy(other); - - return *this; -} - -bool bob::learn::em::Gaussian::operator==(const bob::learn::em::Gaussian& b) const -{ - return (bob::core::array::isEqual(m_mean, b.m_mean) && - bob::core::array::isEqual(m_variance, b.m_variance) && - bob::core::array::isEqual(m_variance_thresholds, b.m_variance_thresholds)); -} - -bool bob::learn::em::Gaussian::operator!=(const bob::learn::em::Gaussian& b) const { - return !(this->operator==(b)); -} - -bool bob::learn::em::Gaussian::is_similar_to(const bob::learn::em::Gaussian& b, - const double r_epsilon, const double a_epsilon) const -{ - return (bob::core::array::isClose(m_mean, b.m_mean, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_variance, b.m_variance, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_variance_thresholds, b.m_variance_thresholds, r_epsilon, a_epsilon)); -} - -void bob::learn::em::Gaussian::copy(const bob::learn::em::Gaussian& other) { - m_n_inputs = other.m_n_inputs; - - m_mean.resize(m_n_inputs); - m_mean = other.m_mean; - - m_variance.resize(m_n_inputs); - m_variance = other.m_variance; - - m_variance_thresholds.resize(m_n_inputs); - m_variance_thresholds = other.m_variance_thresholds; - - m_n_log2pi = other.m_n_log2pi; - m_g_norm = other.m_g_norm; -} - - -void bob::learn::em::Gaussian::setNInputs(const size_t n_inputs) { - resize(n_inputs); -} - -void bob::learn::em::Gaussian::resize(const size_t n_inputs) { - m_n_inputs = n_inputs; - m_mean.resize(m_n_inputs); - m_mean = 0; - m_variance.resize(m_n_inputs); - m_variance = 1; - m_variance_thresholds.resize(m_n_inputs); - m_variance_thresholds = std::numeric_limits<double>::epsilon(); - - // Re-compute g_norm, because m_n_inputs and m_variance - // have changed - preComputeNLog2Pi(); - preComputeConstants(); -} - -void bob::learn::em::Gaussian::setMean(const blitz::Array<double,1> &mean) { - // Check and set - bob::core::array::assertSameShape(m_mean, mean); - m_mean = mean; -} - -void bob::learn::em::Gaussian::setVariance(const blitz::Array<double,1> &variance) { - // Check and set - bob::core::array::assertSameShape(m_variance, variance); - m_variance = variance; - - // Variance flooring - applyVarianceThresholds(); -} - -void bob::learn::em::Gaussian::setVarianceThresholds(const blitz::Array<double,1> &variance_thresholds) { - // Check and set - bob::core::array::assertSameShape(m_variance_thresholds, variance_thresholds); - m_variance_thresholds = variance_thresholds; - - // Variance flooring - applyVarianceThresholds(); -} - -void bob::learn::em::Gaussian::setVarianceThresholds(const double value) { - blitz::Array<double,1> variance_thresholds(m_n_inputs); - variance_thresholds = value; - setVarianceThresholds(variance_thresholds); -} - -void bob::learn::em::Gaussian::applyVarianceThresholds() { - // Apply variance flooring threshold - m_variance = blitz::where( m_variance < m_variance_thresholds, m_variance_thresholds, m_variance); - - // Re-compute g_norm, because m_variance has changed - preComputeConstants(); -} - -double bob::learn::em::Gaussian::logLikelihood(const blitz::Array<double,1> &x) const { - // Check - bob::core::array::assertSameShape(x, m_mean); - return logLikelihood_(x); -} - -double bob::learn::em::Gaussian::logLikelihood_(const blitz::Array<double,1> &x) const { - double z = blitz::sum(blitz::pow2(x - m_mean) / m_variance); - // Log Likelihood - return (-0.5 * (m_g_norm + z)); -} - -void bob::learn::em::Gaussian::preComputeNLog2Pi() { - m_n_log2pi = m_n_inputs * bob::math::Log::Log2Pi; -} - -void bob::learn::em::Gaussian::preComputeConstants() { - m_g_norm = m_n_log2pi + blitz::sum(blitz::log(m_variance)); -} - -void bob::learn::em::Gaussian::save(bob::io::base::HDF5File& config) const { - config.setArray("m_mean", m_mean); - config.setArray("m_variance", m_variance); - config.setArray("m_variance_thresholds", m_variance_thresholds); - config.set("g_norm", m_g_norm); - int64_t v = static_cast<int64_t>(m_n_inputs); - config.set("m_n_inputs", v); -} - -void bob::learn::em::Gaussian::load(bob::io::base::HDF5File& config) { - int64_t v = config.read<int64_t>("m_n_inputs"); - m_n_inputs = static_cast<size_t>(v); - - m_mean.resize(m_n_inputs); - m_variance.resize(m_n_inputs); - m_variance_thresholds.resize(m_n_inputs); - - config.readArray("m_mean", m_mean); - config.readArray("m_variance", m_variance); - config.readArray("m_variance_thresholds", m_variance_thresholds); - - preComputeNLog2Pi(); - m_g_norm = config.read<double>("g_norm"); -} - -namespace bob { namespace learn { namespace em { - std::ostream& operator<<(std::ostream& os, const Gaussian& g) { - os << "Mean = " << g.m_mean << std::endl; - os << "Variance = " << g.m_variance << std::endl; - return os; - } -} } } diff --git a/bob/learn/em/cpp/ISVBase.cpp b/bob/learn/em/cpp/ISVBase.cpp deleted file mode 100644 index c299013dc143b2b34b50a7802797b6728059e076..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/ISVBase.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @date Tue Jan 27 16:02:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/ISVBase.h> -#include <bob.core/array_copy.h> -#include <bob.math/linear.h> -#include <bob.math/inv.h> -#include <bob.learn.em/LinearScoring.h> -#include <limits> - - -//////////////////// ISVBase //////////////////// -bob::learn::em::ISVBase::ISVBase() -{ -} - -bob::learn::em::ISVBase::ISVBase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, - const size_t ru): - m_base(ubm, ru, 1) -{ - blitz::Array<double,2>& V = m_base.updateV(); - V = 0; -} - -bob::learn::em::ISVBase::ISVBase(const bob::learn::em::ISVBase& other): - m_base(other.m_base) -{ -} - - -bob::learn::em::ISVBase::ISVBase(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::ISVBase::~ISVBase() { -} - -void bob::learn::em::ISVBase::save(bob::io::base::HDF5File& config) const -{ - config.setArray("U", m_base.getU()); - config.setArray("d", m_base.getD()); -} - -void bob::learn::em::ISVBase::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - blitz::Array<double,2> U = config.readArray<double,2>("U"); - blitz::Array<double,1> d = config.readArray<double,1>("d"); - const int ru = U.extent(1); - if (!m_base.getUbm()) - m_base.resize(ru, 1, U.extent(0)); - else - m_base.resize(ru, 1); - m_base.setU(U); - m_base.setD(d); - blitz::Array<double,2>& V = m_base.updateV(); - V = 0; -} - -bob::learn::em::ISVBase& -bob::learn::em::ISVBase::operator=(const bob::learn::em::ISVBase& other) -{ - if (this != &other) - { - m_base = other.m_base; - } - return *this; -} - diff --git a/bob/learn/em/cpp/ISVMachine.cpp b/bob/learn/em/cpp/ISVMachine.cpp deleted file mode 100644 index 578e5d29f61f04af197c0b27aa27f32bb70e72b7..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/ISVMachine.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/** - * @date Tue Jan 27 16:06:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/ISVMachine.h> -#include <bob.core/array_copy.h> -#include <bob.math/linear.h> -#include <bob.math/inv.h> -#include <bob.learn.em/LinearScoring.h> -#include <limits> - - -//////////////////// ISVMachine //////////////////// -bob::learn::em::ISVMachine::ISVMachine(): - m_z(1) -{ - resizeTmp(); -} - -bob::learn::em::ISVMachine::ISVMachine(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base): - m_isv_base(isv_base), - m_z(isv_base->getSupervectorLength()) -{ - if (!m_isv_base->getUbm()) - throw std::runtime_error("No UBM was set in the JFA machine."); - updateCache(); - resizeTmp(); -} - - -bob::learn::em::ISVMachine::ISVMachine(const bob::learn::em::ISVMachine& other): - m_isv_base(other.m_isv_base), - m_z(bob::core::array::ccopy(other.m_z)) -{ - updateCache(); - resizeTmp(); -} - -bob::learn::em::ISVMachine::ISVMachine(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::ISVMachine::~ISVMachine() { -} - -bob::learn::em::ISVMachine& -bob::learn::em::ISVMachine::operator=(const bob::learn::em::ISVMachine& other) -{ - if (this != &other) - { - m_isv_base = other.m_isv_base; - m_z.reference(bob::core::array::ccopy(other.m_z)); - } - return *this; -} - -bool bob::learn::em::ISVMachine::operator==(const bob::learn::em::ISVMachine& other) const -{ - return (*m_isv_base == *(other.m_isv_base) && - bob::core::array::isEqual(m_z, other.m_z)); -} - -bool bob::learn::em::ISVMachine::operator!=(const bob::learn::em::ISVMachine& b) const -{ - return !(this->operator==(b)); -} - - -bool bob::learn::em::ISVMachine::is_similar_to(const bob::learn::em::ISVMachine& b, - const double r_epsilon, const double a_epsilon) const -{ - return (m_isv_base->is_similar_to(*(b.m_isv_base), r_epsilon, a_epsilon) && - bob::core::array::isClose(m_z, b.m_z, r_epsilon, a_epsilon)); -} - -void bob::learn::em::ISVMachine::save(bob::io::base::HDF5File& config) const -{ - config.setArray("z", m_z); -} - -void bob::learn::em::ISVMachine::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - blitz::Array<double,1> z = config.readArray<double,1>("z"); - if (!m_isv_base) - m_z.resize(z.extent(0)); - setZ(z); - // update cache - updateCache(); - resizeTmp(); -} - -void bob::learn::em::ISVMachine::setZ(const blitz::Array<double,1>& z) -{ - if(z.extent(0) != m_z.extent(0)) { //checks dimension - boost::format m("size of input vector `z' (%d) does not match the expected size (%d)"); - m % z.extent(0) % m_z.extent(0); - throw std::runtime_error(m.str()); - } - m_z.reference(bob::core::array::ccopy(z)); - // update cache - updateCache(); -} - - -void bob::learn::em::ISVMachine::setX(const blitz::Array<double,1>& x) -{ - if(x.extent(0) != m_cache_x.extent(0)) { //checks dimension - boost::format m("size of input vector `x' (%d) does not match the expected size (%d)"); - m % x.extent(0) % m_cache_x.extent(0); - throw std::runtime_error(m.str()); - } - m_cache_x.reference(bob::core::array::ccopy(x)); - // update cache - updateCache(); -} - -void bob::learn::em::ISVMachine::setISVBase(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base) -{ - if (!isv_base->getUbm()) - throw std::runtime_error("No UBM was set in the JFA machine."); - m_isv_base = isv_base; - // Resize variables - resize(); -} - -void bob::learn::em::ISVMachine::resize() -{ - m_z.resizeAndPreserve(getSupervectorLength()); - updateCache(); - resizeTmp(); -} - -void bob::learn::em::ISVMachine::resizeTmp() -{ - if (m_isv_base) - { - m_tmp_Ux.resize(getSupervectorLength()); - } -} - -void bob::learn::em::ISVMachine::updateCache() -{ - if (m_isv_base) - { - // m + Dz - m_cache_mDz.resize(getSupervectorLength()); - m_cache_mDz = m_isv_base->getD()*m_z + m_isv_base->getUbm()->getMeanSupervector(); - m_cache_x.resize(getDimRu()); - } -} - -void bob::learn::em::ISVMachine::estimateUx(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,1>& Ux) -{ - estimateX(gmm_stats, m_cache_x); - bob::math::prod(m_isv_base->getU(), m_cache_x, Ux); -} - -double bob::learn::em::ISVMachine::forward(const bob::learn::em::GMMStats& input) -{ - return forward_(input); -} - -double bob::learn::em::ISVMachine::forward(const bob::learn::em::GMMStats& gmm_stats, - const blitz::Array<double,1>& Ux) -{ - // Checks that a Base machine has been set - if (!m_isv_base) throw std::runtime_error("No UBM was set in the JFA machine."); - - return bob::learn::em::linearScoring(m_cache_mDz, - m_isv_base->getUbm()->getMeanSupervector(), m_isv_base->getUbm()->getVarianceSupervector(), - gmm_stats, Ux, true); -} - -double bob::learn::em::ISVMachine::forward_(const bob::learn::em::GMMStats& input) -{ - // Checks that a Base machine has been set - if(!m_isv_base) throw std::runtime_error("No UBM was set in the JFA machine."); - - // Ux and GMMStats - estimateX(input, m_cache_x); - bob::math::prod(m_isv_base->getU(), m_cache_x, m_tmp_Ux); - - return bob::learn::em::linearScoring(m_cache_mDz, - m_isv_base->getUbm()->getMeanSupervector(), m_isv_base->getUbm()->getVarianceSupervector(), - input, m_tmp_Ux, true); -} - diff --git a/bob/learn/em/cpp/ISVTrainer.cpp b/bob/learn/em/cpp/ISVTrainer.cpp deleted file mode 100644 index cb00321a7e293a76469483b6e9e6863651b7eb66..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/ISVTrainer.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/** - * @date Tue Jul 19 12:16:17 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Joint Factor Analysis Trainer - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/ISVTrainer.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.core/array_random.h> -#include <bob.math/inv.h> -#include <bob.math/linear.h> -#include <bob.core/check.h> -#include <bob.core/array_repmat.h> -#include <algorithm> - - -//////////////////////////// ISVTrainer /////////////////////////// -bob::learn::em::ISVTrainer::ISVTrainer(const double relevance_factor): - m_relevance_factor(relevance_factor), - m_rng(new boost::mt19937()) -{} - -bob::learn::em::ISVTrainer::ISVTrainer(const bob::learn::em::ISVTrainer& other): - m_rng(other.m_rng) -{ - m_relevance_factor = other.m_relevance_factor; -} - -bob::learn::em::ISVTrainer::~ISVTrainer() -{} - -bob::learn::em::ISVTrainer& bob::learn::em::ISVTrainer::operator= -(const bob::learn::em::ISVTrainer& other) -{ - if (this != &other) - { - m_rng = other.m_rng; - m_relevance_factor = other.m_relevance_factor; - } - return *this; -} - -bool bob::learn::em::ISVTrainer::operator==(const bob::learn::em::ISVTrainer& b) const -{ - return m_rng == b.m_rng && - m_relevance_factor == b.m_relevance_factor; -} - -bool bob::learn::em::ISVTrainer::operator!=(const bob::learn::em::ISVTrainer& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::ISVTrainer::is_similar_to(const bob::learn::em::ISVTrainer& b, - const double r_epsilon, const double a_epsilon) const -{ - return m_rng == b.m_rng && - m_relevance_factor == b.m_relevance_factor; -} - -void bob::learn::em::ISVTrainer::initialize(bob::learn::em::ISVBase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - m_base_trainer.initUbmNidSumStatistics(machine.getBase(), ar); - m_base_trainer.initializeXYZ(ar); - - blitz::Array<double,2>& U = machine.updateU(); - bob::core::array::randn(*m_rng, U); - initializeD(machine); - machine.precompute(); -} - -void bob::learn::em::ISVTrainer::initializeD(bob::learn::em::ISVBase& machine) const -{ - // D = sqrt(variance(UBM) / relevance_factor) - blitz::Array<double,1> d = machine.updateD(); - d = sqrt(machine.getBase().getUbmVariance() / m_relevance_factor); -} - -void bob::learn::em::ISVTrainer::eStep(bob::learn::em::ISVBase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - m_base_trainer.resetXYZ(); - - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateX(base, ar); - m_base_trainer.updateZ(base, ar); - m_base_trainer.computeAccumulatorsU(base, ar); -} - -void bob::learn::em::ISVTrainer::mStep(bob::learn::em::ISVBase& machine) -{ - blitz::Array<double,2>& U = machine.updateU(); - m_base_trainer.updateU(U); - machine.precompute(); -} - -double bob::learn::em::ISVTrainer::computeLikelihood(bob::learn::em::ISVBase& machine) -{ - // TODO - return 0; -} - -void bob::learn::em::ISVTrainer::enroll(bob::learn::em::ISVMachine& machine, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& ar, - const size_t n_iter) -{ - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > vvec; - vvec.push_back(ar); - - const bob::learn::em::FABase& fb = machine.getISVBase()->getBase(); - - m_base_trainer.initUbmNidSumStatistics(fb, vvec); - m_base_trainer.initializeXYZ(vvec); - - for (size_t i=0; i<n_iter; ++i) { - m_base_trainer.updateX(fb, vvec); - m_base_trainer.updateZ(fb, vvec); - } - - const blitz::Array<double,1> z(m_base_trainer.getZ()[0]); - machine.setZ(z); -} diff --git a/bob/learn/em/cpp/IVectorMachine.cpp b/bob/learn/em/cpp/IVectorMachine.cpp deleted file mode 100644 index 912ba7d556f372563061b23d623670f49f1100b1..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/IVectorMachine.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/** - * @date Sat Mar 30 21:00:00 2013 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/IVectorMachine.h> -#include <bob.core/array_copy.h> -#include <bob.core/check.h> -#include <bob.math/linear.h> -#include <bob.math/linsolve.h> - -bob::learn::em::IVectorMachine::IVectorMachine() -{ -} - -bob::learn::em::IVectorMachine::IVectorMachine(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, - const size_t rt, const double variance_threshold): - m_ubm(ubm), m_rt(rt), - m_T(getSupervectorLength(),rt), m_sigma(getSupervectorLength()), - m_variance_threshold(variance_threshold) -{ - m_sigma = 0.0; - resizePrecompute(); -} - -bob::learn::em::IVectorMachine::IVectorMachine(const bob::learn::em::IVectorMachine& other): - m_ubm(other.m_ubm), m_rt(other.m_rt), - m_T(bob::core::array::ccopy(other.m_T)), - m_sigma(bob::core::array::ccopy(other.m_sigma)), - m_variance_threshold(other.m_variance_threshold) -{ - resizePrecompute(); -} - -bob::learn::em::IVectorMachine::IVectorMachine(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::IVectorMachine::~IVectorMachine() { -} - -void bob::learn::em::IVectorMachine::save(bob::io::base::HDF5File& config) const -{ - config.setArray("m_T", m_T); - config.setArray("m_sigma", m_sigma); - config.set("m_variance_threshold", m_variance_threshold); -} - -void bob::learn::em::IVectorMachine::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - m_T.reference(config.readArray<double,2>("m_T")); - m_rt = m_T.extent(1); - m_sigma.reference(config.readArray<double,1>("m_sigma")); - m_variance_threshold = config.read<double>("m_variance_threshold"); - resizePrecompute(); -} - -void bob::learn::em::IVectorMachine::resize(const size_t rt) -{ - m_rt = rt; - m_T.resizeAndPreserve(m_T.extent(0), rt); - resizePrecompute(); -} - -bob::learn::em::IVectorMachine& -bob::learn::em::IVectorMachine::operator=(const bob::learn::em::IVectorMachine& other) -{ - if (this != &other) - { - m_ubm = other.m_ubm; - m_rt = other.m_rt; - m_T.reference(bob::core::array::ccopy(other.m_T)); - m_sigma.reference(bob::core::array::ccopy(other.m_sigma)); - m_variance_threshold = other.m_variance_threshold; - resizePrecompute(); - } - return *this; -} - -bool bob::learn::em::IVectorMachine::operator==(const IVectorMachine& b) const -{ - return (((m_ubm && b.m_ubm) && *m_ubm == *(b.m_ubm)) || (!m_ubm && !b.m_ubm)) && - m_rt == b.m_rt && - bob::core::array::isEqual(m_T, b.m_T) && - bob::core::array::isEqual(m_sigma, b.m_sigma) && - m_variance_threshold == b.m_variance_threshold; -} - -bool bob::learn::em::IVectorMachine::operator!=(const bob::learn::em::IVectorMachine& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::IVectorMachine::is_similar_to(const IVectorMachine& b, - const double r_epsilon, const double a_epsilon) const -{ - // TODO: update with new is_similar_to method - return (((m_ubm && b.m_ubm) && m_ubm->is_similar_to(*(b.m_ubm), r_epsilon)) || (!m_ubm && !b.m_ubm)) && - m_rt == b.m_rt && - bob::core::array::isClose(m_T, b.m_T, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_sigma, b.m_sigma, r_epsilon, a_epsilon) && - bob::core::isClose(m_variance_threshold, b.m_variance_threshold, r_epsilon, a_epsilon); -} - -void bob::learn::em::IVectorMachine::setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm) -{ - m_ubm = ubm; - resizePrecompute(); -} - -void bob::learn::em::IVectorMachine::setT(const blitz::Array<double,2>& T) -{ - bob::core::array::assertSameShape(m_T, T); - m_T = T; - // Update cache - precompute(); -} - -void bob::learn::em::IVectorMachine::setSigma(const blitz::Array<double,1>& sigma) -{ - bob::core::array::assertSameShape(m_sigma, sigma); - m_sigma = sigma; - // Update cache - precompute(); -} - - -void bob::learn::em::IVectorMachine::setVarianceThreshold(const double thd) -{ - m_variance_threshold = thd; - // Update cache - precompute(); -} - -void bob::learn::em::IVectorMachine::applyVarianceThreshold() -{ - // Apply variance flooring threshold - m_sigma = blitz::where(m_sigma < m_variance_threshold, m_variance_threshold, m_sigma); -} - -void bob::learn::em::IVectorMachine::precompute() -{ - if (m_ubm) - { - // Apply variance threshold - applyVarianceThreshold(); - - blitz::firstIndex i; - blitz::secondIndex j; - blitz::Range rall = blitz::Range::all(); - const int C = (int)m_ubm->getNGaussians(); - const int D = (int)m_ubm->getNInputs(); - - // T_{c}^{T}.sigma_{c}^{-1} - for (int c=0; c<C; ++c) - { - blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall); - blitz::Array<double,2> Tc = m_T(blitz::Range(c*D,(c+1)*D-1), rall); - blitz::Array<double,2> Tct = Tc.transpose(1,0); - blitz::Array<double,1> sigma_c = m_sigma(blitz::Range(c*D,(c+1)*D-1)); - Tct_sigmacInv = Tct(i,j) / sigma_c(j); - } - - // T_{c}^{T}.sigma_{c}^{-1}.T_{c} - for (int c=0; c<C; ++c) - { - blitz::Array<double,2> Tc = m_T(blitz::Range(c*D,(c+1)*D-1), rall); - blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall); - blitz::Array<double,2> Tct_sigmacInv_Tc = m_cache_Tct_sigmacInv_Tc(c, rall, rall); - bob::math::prod(Tct_sigmacInv, Tc, Tct_sigmacInv_Tc); - } - } -} - -void bob::learn::em::IVectorMachine::resizePrecompute() -{ - resizeCache(); - resizeTmp(); - precompute(); -} - -void bob::learn::em::IVectorMachine::resizeCache() -{ - if (m_ubm) - { - const int C = (int)m_ubm->getNGaussians(); - const int D = (int)m_ubm->getNInputs(); - m_cache_Tct_sigmacInv.resize(C, (int)m_rt, D); - m_cache_Tct_sigmacInv_Tc.resize(C, (int)m_rt, (int)m_rt); - } -} - -void bob::learn::em::IVectorMachine::resizeTmp() -{ - if (m_ubm) - m_tmp_d.resize(m_ubm->getNInputs()); - m_tmp_t1.resize(m_rt); - m_tmp_t2.resize(m_rt); - m_tmp_tt.resize(m_rt, m_rt); -} - -void bob::learn::em::IVectorMachine::forward(const bob::learn::em::GMMStats& gs, - blitz::Array<double,1>& ivector) const -{ - bob::core::array::assertSameDimensionLength(ivector.extent(0), (int)m_rt); - forward_(gs, ivector); -} - -void bob::learn::em::IVectorMachine::computeIdTtSigmaInvT( - const bob::learn::em::GMMStats& gs, blitz::Array<double,2>& output) const -{ - // Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$ - blitz::Range rall = blitz::Range::all(); - bob::math::eye(output); - for (int c=0; c<(int)getNGaussians(); ++c) - output += gs.n(c) * m_cache_Tct_sigmacInv_Tc(c, rall, rall); -} - -void bob::learn::em::IVectorMachine::computeTtSigmaInvFnorm( - const bob::learn::em::GMMStats& gs, blitz::Array<double,1>& output) const -{ - // Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$ - blitz::Range rall = blitz::Range::all(); - output = 0; - for (int c=0; c<(int)getNGaussians(); ++c) - { - m_tmp_d = gs.sumPx(c,rall) - gs.n(c) * m_ubm->getGaussian(c)->getMean(); - blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall); - bob::math::prod(Tct_sigmacInv, m_tmp_d, m_tmp_t2); - - output += m_tmp_t2; - } -} - -void bob::learn::em::IVectorMachine::forward_(const bob::learn::em::GMMStats& gs, - blitz::Array<double,1>& ivector) const -{ - // Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$ - computeIdTtSigmaInvT(gs, m_tmp_tt); - - // Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$ - computeTtSigmaInvFnorm(gs, m_tmp_t1); - - // Solves m_tmp_tt.ivector = m_tmp_t1 - bob::math::linsolve(m_tmp_tt, m_tmp_t1, ivector); -} diff --git a/bob/learn/em/cpp/IVectorTrainer.cpp b/bob/learn/em/cpp/IVectorTrainer.cpp deleted file mode 100644 index 591de601cf195b9be555a8873dc7814512d3a64f..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/IVectorTrainer.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/** - * @date Sun Mar 31 20:15:00 2013 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/IVectorTrainer.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.core/array_random.h> -#include <bob.math/inv.h> -#include <bob.core/check.h> -#include <bob.core/array_repmat.h> -#include <algorithm> - -#include <bob.math/linear.h> -#include <bob.math/linsolve.h> - -bob::learn::em::IVectorTrainer::IVectorTrainer(const bool update_sigma): - m_update_sigma(update_sigma), - m_rng(new boost::mt19937()) -{} - -bob::learn::em::IVectorTrainer::IVectorTrainer(const bob::learn::em::IVectorTrainer& other): - m_update_sigma(other.m_update_sigma) -{ - m_rng = other.m_rng; - m_acc_Nij_wij2.reference(bob::core::array::ccopy(other.m_acc_Nij_wij2)); - m_acc_Fnormij_wij.reference(bob::core::array::ccopy(other.m_acc_Fnormij_wij)); - m_acc_Nij.reference(bob::core::array::ccopy(other.m_acc_Nij)); - m_acc_Snormij.reference(bob::core::array::ccopy(other.m_acc_Snormij)); - - m_tmp_wij.reference(bob::core::array::ccopy(other.m_tmp_wij)); - m_tmp_wij2.reference(bob::core::array::ccopy(other.m_tmp_wij2)); - m_tmp_d1.reference(bob::core::array::ccopy(other.m_tmp_d1)); - m_tmp_t1.reference(bob::core::array::ccopy(other.m_tmp_t1)); - m_tmp_dd1.reference(bob::core::array::ccopy(other.m_tmp_dd1)); - m_tmp_dt1.reference(bob::core::array::ccopy(other.m_tmp_dt1)); - m_tmp_tt1.reference(bob::core::array::ccopy(other.m_tmp_tt1)); - m_tmp_tt2.reference(bob::core::array::ccopy(other.m_tmp_tt2)); -} - -bob::learn::em::IVectorTrainer::~IVectorTrainer() -{ -} - -void bob::learn::em::IVectorTrainer::initialize( - bob::learn::em::IVectorMachine& machine) -{ - // Initializes \f$T\f$ and \f$\Sigma\f$ of the machine - blitz::Array<double,2>& T = machine.updateT(); - bob::core::array::randn(*m_rng, T); - blitz::Array<double,1>& sigma = machine.updateSigma(); - sigma = machine.getUbm()->getVarianceSupervector(); - machine.precompute(); -} - -void bob::learn::em::IVectorTrainer::resetAccumulators(const bob::learn::em::IVectorMachine& machine) -{ - // Resize the accumulator - const int C = machine.getNGaussians(); - const int D = machine.getNInputs(); - const int Rt = machine.getDimRt(); - - // Cache - m_acc_Nij_wij2.resize(C,Rt,Rt); - m_acc_Fnormij_wij.resize(C,D,Rt); - if (m_update_sigma) - { - m_acc_Nij.resize(C); - m_acc_Snormij.resize(C,D); - } - - // Tmp - m_tmp_wij.resize(Rt); - m_tmp_wij2.resize(Rt,Rt); - m_tmp_d1.resize(D); - m_tmp_t1.resize(Rt); - - m_tmp_dt1.resize(D,Rt); - m_tmp_tt1.resize(Rt,Rt); - m_tmp_tt2.resize(Rt,Rt); - if (m_update_sigma) - m_tmp_dd1.resize(D,D); - - // initialize with 0 - m_acc_Nij_wij2 = 0.; - m_acc_Fnormij_wij = 0.; - if (m_update_sigma) - { - m_acc_Nij = 0.; - m_acc_Snormij = 0.; - } -} - - -void bob::learn::em::IVectorTrainer::eStep( - bob::learn::em::IVectorMachine& machine, - const std::vector<bob::learn::em::GMMStats>& data) -{ - blitz::Range rall = blitz::Range::all(); - const int C = machine.getNGaussians(); - - // Reinitializes accumulators to 0 - resetAccumulators(machine); - - for (std::vector<bob::learn::em::GMMStats>::const_iterator it = data.begin(); - it != data.end(); ++it) - { - // Computes E{wij} and E{wij.wij^{T}} - // a. Computes \f$T^{T} \Sigma^{-1} F_{norm}\f$ - machine.computeTtSigmaInvFnorm(*it, m_tmp_t1); - // b. Computes \f$Id + T^{T} \Sigma^{-1} T\f$ - machine.computeIdTtSigmaInvT(*it, m_tmp_tt1); - // c. Computes \f$(Id + T^{T} \Sigma^{-1} T)^{-1}\f$ - - bob::math::inv(m_tmp_tt1, m_tmp_tt2); - // d. Computes \f$E{wij} = (Id + T^{T} \Sigma^{-1} T)^{-1} T^{T} \Sigma^{-1} F_{norm}\f$ - bob::math::prod(m_tmp_tt2, m_tmp_t1, m_tmp_wij); // E{wij} - // e. Computes \f$E{wij}.E{wij^{T}}\f$ - bob::math::prod(m_tmp_wij, m_tmp_wij, m_tmp_wij2); - // f. Computes \f$E{wij.wij^{T}} = (Id + T^{T} \Sigma^{-1} T)^{-1} + E{wij}.E{wij^{T}}\f$ - m_tmp_wij2 += m_tmp_tt2; // E{wij.wij^{T}} - - if (m_update_sigma) - m_acc_Nij += (*it).n; - - for (int c=0; c<C; ++c) - { - blitz::Array<double,2> acc_Nij_wij2_c = m_acc_Nij_wij2(c,rall,rall); - blitz::Array<double,2> acc_Fnormij_wij = m_acc_Fnormij_wij(c,rall,rall); - // acc_Nij_wij2_c += Nijc . E{wij.wij^{T}} - acc_Nij_wij2_c += (*it).n(c) * m_tmp_wij2; - blitz::Array<double,1> mc = machine.getUbm()->getGaussian(c)->getMean(); - // m_tmp_d1 = Fijc - Nijc * ubmmean_{c} - m_tmp_d1 = (*it).sumPx(c,rall) - (*it).n(c)*mc; // Fnorm_c - // m_tmp_dt1 = (Fijc - Nijc * ubmmean_{c}).E{wij}^{T} - bob::math::prod(m_tmp_d1, m_tmp_wij, m_tmp_dt1); - // acc_Fnormij_wij += (Fijc - Nijc * ubmmean_{c}).E{wij}^{T} - acc_Fnormij_wij += m_tmp_dt1; - if (m_update_sigma) - { - blitz::Array<double,1> acc_Snormij_c = m_acc_Snormij(c,rall); - acc_Snormij_c += (*it).sumPxx(c,rall) - mc*((*it).sumPx(c,rall) + m_tmp_d1); - } - } - } -} - -void bob::learn::em::IVectorTrainer::mStep( - bob::learn::em::IVectorMachine& machine) -{ - blitz::Range rall = blitz::Range::all(); - blitz::Array<double,2>& T = machine.updateT(); - blitz::Array<double,1>& sigma = machine.updateSigma(); - const int C = (int)machine.getNGaussians(); - const int D = (int)machine.getNInputs(); - for (int c=0; c<C; ++c) - { - // Solves linear system A.T = B to update T, based on accumulators of - // the eStep() - blitz::Array<double,2> acc_Nij_wij2_c = m_acc_Nij_wij2(c,rall,rall); - blitz::Array<double,2> tacc_Nij_wij2_c = acc_Nij_wij2_c.transpose(1,0); - blitz::Array<double,2> acc_Fnormij_wij_c = m_acc_Fnormij_wij(c,rall,rall); - blitz::Array<double,2> tacc_Fnormij_wij_c = acc_Fnormij_wij_c.transpose(1,0); - blitz::Array<double,2> T_c = T(blitz::Range(c*D,(c+1)*D-1),rall); - blitz::Array<double,2> Tt_c = T_c.transpose(1,0); - if (blitz::all(acc_Nij_wij2_c == 0)) // TODO - Tt_c = 0; - else - bob::math::linsolve(tacc_Nij_wij2_c, tacc_Fnormij_wij_c, Tt_c); - if (m_update_sigma) - { - blitz::Array<double,1> sigma_c = sigma(blitz::Range(c*D,(c+1)*D-1)); - bob::math::prod(acc_Fnormij_wij_c, Tt_c, m_tmp_dd1); - bob::math::diag(m_tmp_dd1, m_tmp_d1); - sigma_c = (m_acc_Snormij(c,rall) - m_tmp_d1) / m_acc_Nij(c); - } - } - machine.precompute(); -} - - -bob::learn::em::IVectorTrainer& bob::learn::em::IVectorTrainer::operator= - (const bob::learn::em::IVectorTrainer &other) -{ - if (this != &other) - { - m_update_sigma = other.m_update_sigma; - - m_acc_Nij_wij2.reference(bob::core::array::ccopy(other.m_acc_Nij_wij2)); - m_acc_Fnormij_wij.reference(bob::core::array::ccopy(other.m_acc_Fnormij_wij)); - m_acc_Nij.reference(bob::core::array::ccopy(other.m_acc_Nij)); - m_acc_Snormij.reference(bob::core::array::ccopy(other.m_acc_Snormij)); - - m_tmp_wij.reference(bob::core::array::ccopy(other.m_tmp_wij)); - m_tmp_wij2.reference(bob::core::array::ccopy(other.m_tmp_wij2)); - m_tmp_d1.reference(bob::core::array::ccopy(other.m_tmp_d1)); - m_tmp_t1.reference(bob::core::array::ccopy(other.m_tmp_t1)); - m_tmp_dd1.reference(bob::core::array::ccopy(other.m_tmp_dd1)); - m_tmp_dt1.reference(bob::core::array::ccopy(other.m_tmp_dt1)); - m_tmp_tt1.reference(bob::core::array::ccopy(other.m_tmp_tt1)); - m_tmp_tt2.reference(bob::core::array::ccopy(other.m_tmp_tt2)); - } - return *this; -} - -bool bob::learn::em::IVectorTrainer::operator== - (const bob::learn::em::IVectorTrainer &other) const -{ - return m_update_sigma == other.m_update_sigma && - bob::core::array::isEqual(m_acc_Nij_wij2, other.m_acc_Nij_wij2) && - bob::core::array::isEqual(m_acc_Fnormij_wij, other.m_acc_Fnormij_wij) && - bob::core::array::isEqual(m_acc_Nij, other.m_acc_Nij) && - bob::core::array::isEqual(m_acc_Snormij, other.m_acc_Snormij); -} - -bool bob::learn::em::IVectorTrainer::operator!= - (const bob::learn::em::IVectorTrainer &other) const -{ - return !(this->operator==(other)); -} - -bool bob::learn::em::IVectorTrainer::is_similar_to - (const bob::learn::em::IVectorTrainer &other, const double r_epsilon, - const double a_epsilon) const -{ - return m_update_sigma == other.m_update_sigma && - bob::core::array::isClose(m_acc_Nij_wij2, other.m_acc_Nij_wij2, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_acc_Fnormij_wij, other.m_acc_Fnormij_wij, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_acc_Nij, other.m_acc_Nij, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_acc_Snormij, other.m_acc_Snormij, r_epsilon, a_epsilon); -} diff --git a/bob/learn/em/cpp/JFABase.cpp b/bob/learn/em/cpp/JFABase.cpp deleted file mode 100644 index b80bfd31d959f8e0e7fc3004a3ee0980b49e4a50..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/JFABase.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @date Tue Jan 27 15:54:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/JFABase.h> -#include <bob.core/array_copy.h> -#include <bob.math/linear.h> -#include <bob.math/inv.h> -#include <bob.learn.em/LinearScoring.h> -#include <limits> - - -//////////////////// JFABase //////////////////// -bob::learn::em::JFABase::JFABase() -{ -} - -bob::learn::em::JFABase::JFABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, - const size_t ru, const size_t rv): - m_base(ubm, ru, rv) -{ -} - -bob::learn::em::JFABase::JFABase(const bob::learn::em::JFABase& other): - m_base(other.m_base) -{ -} - - -bob::learn::em::JFABase::JFABase(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::JFABase::~JFABase() { -} - -void bob::learn::em::JFABase::save(bob::io::base::HDF5File& config) const -{ - config.setArray("U", m_base.getU()); - config.setArray("V", m_base.getV()); - config.setArray("d", m_base.getD()); -} - -void bob::learn::em::JFABase::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - blitz::Array<double,2> U = config.readArray<double,2>("U"); - blitz::Array<double,2> V = config.readArray<double,2>("V"); - blitz::Array<double,1> d = config.readArray<double,1>("d"); - const int ru = U.extent(1); - const int rv = V.extent(1); - if (!m_base.getUbm()) - m_base.resize(ru, rv, U.extent(0)); - else - m_base.resize(ru, rv); - m_base.setU(U); - m_base.setV(V); - m_base.setD(d); -} - -bob::learn::em::JFABase& -bob::learn::em::JFABase::operator=(const bob::learn::em::JFABase& other) -{ - if (this != &other) - { - m_base = other.m_base; - } - return *this; -} diff --git a/bob/learn/em/cpp/JFAMachine.cpp b/bob/learn/em/cpp/JFAMachine.cpp deleted file mode 100644 index 508c5008bcb59479e4a966eb6de31a6f14649d99..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/JFAMachine.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/** - * @date Tue Jan 27 16:47:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/JFAMachine.h> -#include <bob.core/array_copy.h> -#include <bob.math/linear.h> -#include <bob.math/inv.h> -#include <bob.learn.em/LinearScoring.h> -#include <limits> - - -//////////////////// JFAMachine //////////////////// -bob::learn::em::JFAMachine::JFAMachine(): - m_y(1), m_z(1) -{ - resizeTmp(); -} - -bob::learn::em::JFAMachine::JFAMachine(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base): - m_jfa_base(jfa_base), - m_y(jfa_base->getDimRv()), m_z(jfa_base->getSupervectorLength()) -{ - if (!m_jfa_base->getUbm()) throw std::runtime_error("No UBM was set in the JFA machine."); - updateCache(); - resizeTmp(); -} - - -bob::learn::em::JFAMachine::JFAMachine(const bob::learn::em::JFAMachine& other): - m_jfa_base(other.m_jfa_base), - m_y(bob::core::array::ccopy(other.m_y)), - m_z(bob::core::array::ccopy(other.m_z)) -{ - updateCache(); - resizeTmp(); -} - -bob::learn::em::JFAMachine::JFAMachine(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::JFAMachine::~JFAMachine() { -} - -bob::learn::em::JFAMachine& -bob::learn::em::JFAMachine::operator=(const bob::learn::em::JFAMachine& other) -{ - if (this != &other) - { - m_jfa_base = other.m_jfa_base; - m_y.reference(bob::core::array::ccopy(other.m_y)); - m_z.reference(bob::core::array::ccopy(other.m_z)); - } - return *this; -} - -bool bob::learn::em::JFAMachine::operator==(const bob::learn::em::JFAMachine& other) const -{ - return (*m_jfa_base == *(other.m_jfa_base) && - bob::core::array::isEqual(m_y, other.m_y) && - bob::core::array::isEqual(m_z, other.m_z)); -} - -bool bob::learn::em::JFAMachine::operator!=(const bob::learn::em::JFAMachine& b) const -{ - return !(this->operator==(b)); -} - - -bool bob::learn::em::JFAMachine::is_similar_to(const bob::learn::em::JFAMachine& b, - const double r_epsilon, const double a_epsilon) const -{ - return (m_jfa_base->is_similar_to(*(b.m_jfa_base), r_epsilon, a_epsilon) && - bob::core::array::isClose(m_y, b.m_y, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_z, b.m_z, r_epsilon, a_epsilon)); -} - -void bob::learn::em::JFAMachine::save(bob::io::base::HDF5File& config) const -{ - config.setArray("y", m_y); - config.setArray("z", m_z); -} - -void bob::learn::em::JFAMachine::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - blitz::Array<double,1> y = config.readArray<double,1>("y"); - blitz::Array<double,1> z = config.readArray<double,1>("z"); - if (!m_jfa_base) - { - m_y.resize(y.extent(0)); - m_z.resize(z.extent(0)); - } - setY(y); - setZ(z); - // update cache - updateCache(); - resizeTmp(); -} - - -void bob::learn::em::JFAMachine::setY(const blitz::Array<double,1>& y) -{ - if(y.extent(0) != m_y.extent(0)) { //checks dimension - boost::format m("size of input vector `y' (%d) does not match the expected size (%d)"); - m % y.extent(0) % m_y.extent(0); - throw std::runtime_error(m.str()); - } - m_y.reference(bob::core::array::ccopy(y)); - // update cache - updateCache(); -} - -void bob::learn::em::JFAMachine::setZ(const blitz::Array<double,1>& z) -{ - if(z.extent(0) != m_z.extent(0)) { //checks dimension - boost::format m("size of input vector `z' (%d) does not match the expected size (%d)"); - m % z.extent(0) % m_z.extent(0); - throw std::runtime_error(m.str()); - } - m_z.reference(bob::core::array::ccopy(z)); - // update cache - updateCache(); -} - -void bob::learn::em::JFAMachine::setJFABase(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base) -{ - if (!jfa_base->getUbm()) - throw std::runtime_error("No UBM was set in the JFA machine."); - m_jfa_base = jfa_base; - // Resize variables - resize(); -} - -void bob::learn::em::JFAMachine::resize() -{ - m_y.resizeAndPreserve(getDimRv()); - m_z.resizeAndPreserve(getSupervectorLength()); - updateCache(); - resizeTmp(); -} - -void bob::learn::em::JFAMachine::resizeTmp() -{ - if (m_jfa_base) - { - m_tmp_Ux.resize(getSupervectorLength()); - } -} - -void bob::learn::em::JFAMachine::updateCache() -{ - if (m_jfa_base) - { - // m + Vy + Dz - m_cache_mVyDz.resize(getSupervectorLength()); - bob::math::prod(m_jfa_base->getV(), m_y, m_cache_mVyDz); - m_cache_mVyDz += m_jfa_base->getD()*m_z + m_jfa_base->getUbm()->getMeanSupervector(); - m_cache_x.resize(getDimRu()); - } -} - -void bob::learn::em::JFAMachine::estimateUx(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,1>& Ux) -{ - estimateX(gmm_stats, m_cache_x); - bob::math::prod(m_jfa_base->getU(), m_cache_x, Ux); -} - -double bob::learn::em::JFAMachine::forward(const bob::learn::em::GMMStats& input) -{ - return forward_(input); -} - -double bob::learn::em::JFAMachine::forward(const bob::learn::em::GMMStats& gmm_stats, - const blitz::Array<double,1>& Ux) -{ - // Checks that a Base machine has been set - if (!m_jfa_base) throw std::runtime_error("No UBM was set in the JFA machine."); - - return bob::learn::em::linearScoring(m_cache_mVyDz, - m_jfa_base->getUbm()->getMeanSupervector(), m_jfa_base->getUbm()->getVarianceSupervector(), - gmm_stats, Ux, true); -} - -double bob::learn::em::JFAMachine::forward_(const bob::learn::em::GMMStats& input) -{ - // Checks that a Base machine has been set - if (!m_jfa_base) throw std::runtime_error("No UBM was set in the JFA machine."); - - // Ux and GMMStats - estimateX(input, m_cache_x); - bob::math::prod(m_jfa_base->getU(), m_cache_x, m_tmp_Ux); - - return bob::learn::em::linearScoring(m_cache_mVyDz, - m_jfa_base->getUbm()->getMeanSupervector(), m_jfa_base->getUbm()->getVarianceSupervector(), - input, m_tmp_Ux, true); -} - diff --git a/bob/learn/em/cpp/JFATrainer.cpp b/bob/learn/em/cpp/JFATrainer.cpp deleted file mode 100644 index 23591c8845d3795e48e9865d58fee23cf95360b8..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/JFATrainer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @date Tue Jul 19 12:16:17 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Joint Factor Analysis Trainer - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/JFATrainer.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.core/array_random.h> -#include <bob.math/inv.h> -#include <bob.math/linear.h> -#include <bob.core/check.h> -#include <bob.core/array_repmat.h> -#include <algorithm> - - -//////////////////////////// JFATrainer /////////////////////////// -bob::learn::em::JFATrainer::JFATrainer(): - m_rng(new boost::mt19937()) -{} - -bob::learn::em::JFATrainer::JFATrainer(const bob::learn::em::JFATrainer& other): - m_rng(other.m_rng) -{} - -bob::learn::em::JFATrainer::~JFATrainer() -{} - -bob::learn::em::JFATrainer& bob::learn::em::JFATrainer::operator= -(const bob::learn::em::JFATrainer& other) -{ - if (this != &other) - { - //m_max_iterations = other.m_max_iterations; - m_rng = other.m_rng; - } - return *this; -} - -bool bob::learn::em::JFATrainer::operator==(const bob::learn::em::JFATrainer& b) const -{ - //return m_max_iterations == b.m_max_iterations && *m_rng == *(b.m_rng); - return *m_rng == *(b.m_rng); -} - -bool bob::learn::em::JFATrainer::operator!=(const bob::learn::em::JFATrainer& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::JFATrainer::is_similar_to(const bob::learn::em::JFATrainer& b, - const double r_epsilon, const double a_epsilon) const -{ - //return m_max_iterations == b.m_max_iterations && *m_rng == *(b.m_rng); - return *m_rng == *(b.m_rng); -} - -void bob::learn::em::JFATrainer::initialize(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - m_base_trainer.initUbmNidSumStatistics(machine.getBase(), ar); - m_base_trainer.initializeXYZ(ar); - - blitz::Array<double,2>& U = machine.updateU(); - bob::core::array::randn(*m_rng, U); - blitz::Array<double,2>& V = machine.updateV(); - bob::core::array::randn(*m_rng, V); - blitz::Array<double,1>& D = machine.updateD(); - bob::core::array::randn(*m_rng, D); - machine.precompute(); -} - -void bob::learn::em::JFATrainer::eStep1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateY(base, ar); - m_base_trainer.computeAccumulatorsV(base, ar); -} - -void bob::learn::em::JFATrainer::mStep1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - blitz::Array<double,2>& V = machine.updateV(); - m_base_trainer.updateV(V); -} - -void bob::learn::em::JFATrainer::finalize1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateY(base, ar); -} - - -void bob::learn::em::JFATrainer::eStep2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateX(base, ar); - m_base_trainer.computeAccumulatorsU(base, ar); -} - -void bob::learn::em::JFATrainer::mStep2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - blitz::Array<double,2>& U = machine.updateU(); - m_base_trainer.updateU(U); - machine.precompute(); -} - -void bob::learn::em::JFATrainer::finalize2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateX(base, ar); -} - - -void bob::learn::em::JFATrainer::eStep3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - const bob::learn::em::FABase& base = machine.getBase(); - m_base_trainer.updateZ(base, ar); - m_base_trainer.computeAccumulatorsD(base, ar); -} - -void bob::learn::em::JFATrainer::mStep3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - blitz::Array<double,1>& d = machine.updateD(); - m_base_trainer.updateD(d); -} - -void bob::learn::em::JFATrainer::finalize3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ -} - -/* -void bob::learn::em::JFATrainer::train_loop(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - // V subspace - for (size_t i=0; i<m_max_iterations; ++i) { - eStep1(machine, ar); - mStep1(machine, ar); - } - finalize1(machine, ar); - // U subspace - for (size_t i=0; i<m_max_iterations; ++i) { - eStep2(machine, ar); - mStep2(machine, ar); - } - finalize2(machine, ar); - // d subspace - for (size_t i=0; i<m_max_iterations; ++i) { - eStep3(machine, ar); - mStep3(machine, ar); - } - finalize3(machine, ar); -}*/ - -/* -void bob::learn::em::JFATrainer::train(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar) -{ - initialize(machine, ar); - train_loop(machine, ar); -} -*/ - -void bob::learn::em::JFATrainer::enroll(bob::learn::em::JFAMachine& machine, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& ar, - const size_t n_iter) -{ - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > vvec; - vvec.push_back(ar); - - const bob::learn::em::FABase& fb = machine.getJFABase()->getBase(); - - m_base_trainer.initUbmNidSumStatistics(fb, vvec); - m_base_trainer.initializeXYZ(vvec); - - for (size_t i=0; i<n_iter; ++i) { - m_base_trainer.updateY(fb, vvec); - m_base_trainer.updateX(fb, vvec); - m_base_trainer.updateZ(fb, vvec); - } - - const blitz::Array<double,1> y(m_base_trainer.getY()[0]); - const blitz::Array<double,1> z(m_base_trainer.getZ()[0]); - machine.setY(y); - machine.setZ(z); -} diff --git a/bob/learn/em/cpp/KMeansMachine.cpp b/bob/learn/em/cpp/KMeansMachine.cpp deleted file mode 100644 index 395cb1460996cb0c77988515a3d9a8b1e7304eca..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/KMeansMachine.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/KMeansMachine.h> - -#include <bob.core/assert.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <limits> - -bob::learn::em::KMeansMachine::KMeansMachine(): - m_n_means(0), m_n_inputs(0), m_means(0,0), - m_cache_means(0,0) -{ - m_means = 0; -} - -bob::learn::em::KMeansMachine::KMeansMachine(const size_t n_means, const size_t n_inputs): - m_n_means(n_means), m_n_inputs(n_inputs), m_means(n_means, n_inputs), - m_cache_means(n_means, n_inputs) -{ - m_means = 0; -} - -bob::learn::em::KMeansMachine::KMeansMachine(const blitz::Array<double,2>& means): - m_n_means(means.extent(0)), m_n_inputs(means.extent(1)), - m_means(bob::core::array::ccopy(means)), - m_cache_means(means.shape()) -{ -} - -bob::learn::em::KMeansMachine::KMeansMachine(const bob::learn::em::KMeansMachine& other): - m_n_means(other.m_n_means), m_n_inputs(other.m_n_inputs), - m_means(bob::core::array::ccopy(other.m_means)), - m_cache_means(other.m_cache_means.shape()) -{ -} - -bob::learn::em::KMeansMachine::KMeansMachine(bob::io::base::HDF5File& config) -{ - load(config); -} - -bob::learn::em::KMeansMachine::~KMeansMachine() { } - -bob::learn::em::KMeansMachine& bob::learn::em::KMeansMachine::operator= -(const bob::learn::em::KMeansMachine& other) -{ - if(this != &other) - { - m_n_means = other.m_n_means; - m_n_inputs = other.m_n_inputs; - m_means.reference(bob::core::array::ccopy(other.m_means)); - m_cache_means.resize(other.m_means.shape()); - } - return *this; -} - -bool bob::learn::em::KMeansMachine::operator==(const bob::learn::em::KMeansMachine& b) const -{ - return m_n_inputs == b.m_n_inputs && m_n_means == b.m_n_means && - bob::core::array::isEqual(m_means, b.m_means); -} - -bool bob::learn::em::KMeansMachine::operator!=(const bob::learn::em::KMeansMachine& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::KMeansMachine::is_similar_to(const bob::learn::em::KMeansMachine& b, - const double r_epsilon, const double a_epsilon) const -{ - return m_n_inputs == b.m_n_inputs && m_n_means == b.m_n_means && - bob::core::array::isClose(m_means, b.m_means, r_epsilon, a_epsilon); -} - -void bob::learn::em::KMeansMachine::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - m_means.reference(config.readArray<double,2>("means")); - m_n_means = m_means.extent(0); - m_n_inputs = m_means.extent(1); - m_cache_means.resize(m_n_means, m_n_inputs); -} - -void bob::learn::em::KMeansMachine::save(bob::io::base::HDF5File& config) const -{ - config.setArray("means", m_means); -} - -void bob::learn::em::KMeansMachine::setMeans(const blitz::Array<double,2> &means) -{ - bob::core::array::assertSameShape(means, m_means); - m_means = means; -} - -void bob::learn::em::KMeansMachine::setMean(const size_t i, const blitz::Array<double,1> &mean) -{ - if(i>=m_n_means) { - boost::format m("cannot set mean with index %lu: out of bounds [0,%lu["); - m % i % m_n_means; - throw std::runtime_error(m.str()); - } - bob::core::array::assertSameDimensionLength(mean.extent(0), m_means.extent(1)); - m_means(i,blitz::Range::all()) = mean; -} - -const blitz::Array<double,1> bob::learn::em::KMeansMachine::getMean(const size_t i) const -{ - if(i>=m_n_means) { - boost::format m("cannot get mean with index %lu: out of bounds [0,%lu["); - m % i % m_n_means; - throw std::runtime_error(m.str()); - } - - return m_means(i,blitz::Range::all()); - -} - -double bob::learn::em::KMeansMachine::getDistanceFromMean(const blitz::Array<double,1> &x, - const size_t i) const -{ - return blitz::sum(blitz::pow2(m_means(i,blitz::Range::all()) - x)); -} - -void bob::learn::em::KMeansMachine::getClosestMean(const blitz::Array<double,1> &x, - size_t &closest_mean, double &min_distance) const -{ - min_distance = std::numeric_limits<double>::max(); - - for(size_t i=0; i<m_n_means; ++i) { - double this_distance = getDistanceFromMean(x,i); - if(this_distance < min_distance) { - min_distance = this_distance; - closest_mean = i; - } - } -} - -double bob::learn::em::KMeansMachine::getMinDistance(const blitz::Array<double,1>& input) const -{ - size_t closest_mean = 0; - double min_distance = 0; - getClosestMean(input,closest_mean,min_distance); - return min_distance; -} - -void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterInit(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const -{ - // check arguments - bob::core::array::assertSameShape(variances, m_means); - bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means); - - // initialise output arrays - bob::core::array::assertSameShape(variances, m_means); - bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means); - variances = 0; - weights = 0; - - // initialise (temporary) mean array - m_cache_means = 0; -} - -void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterAcc(const blitz::Array<double,2>& data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const -{ - // check arguments - bob::core::array::assertSameShape(variances, m_means); - bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means); - - // iterate over data - blitz::Range a = blitz::Range::all(); - for(int i=0; i<data.extent(0); ++i) { - // - get example - blitz::Array<double,1> x(data(i,a)); - - // - find closest mean - size_t closest_mean = 0; - double min_distance = 0; - getClosestMean(x,closest_mean,min_distance); - - // - accumulate stats - m_cache_means(closest_mean, blitz::Range::all()) += x; - variances(closest_mean, blitz::Range::all()) += blitz::pow2(x); - ++weights(closest_mean); - } -} - -void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachClusterFin(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const -{ - // check arguments - bob::core::array::assertSameShape(variances, m_means); - bob::core::array::assertSameDimensionLength(weights.extent(0), m_n_means); - - // calculate final variances and weights - blitz::firstIndex idx1; - blitz::secondIndex idx2; - - // find means - m_cache_means = m_cache_means(idx1,idx2) / weights(idx1); - - // find variances - variances = variances(idx1,idx2) / weights(idx1); - variances -= blitz::pow2(m_cache_means); - - // find weights - weights = weights / blitz::sum(weights); -} - -void bob::learn::em::KMeansMachine::setCacheMeans(const blitz::Array<double,2> &cache_means) -{ - bob::core::array::assertSameShape(cache_means, m_cache_means); - m_cache_means = cache_means; -} - -void bob::learn::em::KMeansMachine::getVariancesAndWeightsForEachCluster(const blitz::Array<double,2>& data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const -{ - // initialise - getVariancesAndWeightsForEachClusterInit(variances, weights); - // accumulate - getVariancesAndWeightsForEachClusterAcc(data, variances, weights); - // merge/finalize - getVariancesAndWeightsForEachClusterFin(variances, weights); -} - -void bob::learn::em::KMeansMachine::forward(const blitz::Array<double,1>& input, double& output) const -{ - if(static_cast<size_t>(input.extent(0)) != m_n_inputs) { - boost::format m("machine input size (%u) does not match the size of input array (%d)"); - m % m_n_inputs % input.extent(0); - throw std::runtime_error(m.str()); - } - forward_(input,output); -} - -void bob::learn::em::KMeansMachine::forward_(const blitz::Array<double,1>& input, double& output) const -{ - output = getMinDistance(input); -} - -void bob::learn::em::KMeansMachine::resize(const size_t n_means, const size_t n_inputs) -{ - m_n_means = n_means; - m_n_inputs = n_inputs; - m_means.resizeAndPreserve(n_means, n_inputs); - m_cache_means.resizeAndPreserve(n_means, n_inputs); -} - -namespace bob { namespace learn { namespace em { - std::ostream& operator<<(std::ostream& os, const KMeansMachine& km) { - os << "Means = " << km.m_means << std::endl; - return os; - } -} } } diff --git a/bob/learn/em/cpp/KMeansTrainer.cpp b/bob/learn/em/cpp/KMeansTrainer.cpp deleted file mode 100644 index 0c6e3c6da2e8de4addd0e97ead72086952c23329..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/KMeansTrainer.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/KMeansTrainer.h> -#include <bob.core/array_copy.h> - -#include <boost/random.hpp> -#include <bob.core/random.h> - - -bob::learn::em::KMeansTrainer::KMeansTrainer(InitializationMethod i_m): -m_rng(new boost::mt19937()), -m_average_min_distance(0), -m_zeroethOrderStats(0), -m_firstOrderStats(0) -{ - m_initialization_method = i_m; -} - - -bob::learn::em::KMeansTrainer::KMeansTrainer(const bob::learn::em::KMeansTrainer& other){ - - m_initialization_method = other.m_initialization_method; - m_rng = other.m_rng; - m_average_min_distance = other.m_average_min_distance; - m_zeroethOrderStats = bob::core::array::ccopy(other.m_zeroethOrderStats); - m_firstOrderStats = bob::core::array::ccopy(other.m_firstOrderStats); -} - - -bob::learn::em::KMeansTrainer& bob::learn::em::KMeansTrainer::operator= -(const bob::learn::em::KMeansTrainer& other) -{ - if(this != &other) - { - m_rng = other.m_rng; - m_initialization_method = other.m_initialization_method; - m_average_min_distance = other.m_average_min_distance; - - m_zeroethOrderStats = bob::core::array::ccopy(other.m_zeroethOrderStats); - m_firstOrderStats = bob::core::array::ccopy(other.m_firstOrderStats); - } - return *this; -} - - -bool bob::learn::em::KMeansTrainer::operator==(const bob::learn::em::KMeansTrainer& b) const { - return - m_initialization_method == b.m_initialization_method && - *m_rng == *(b.m_rng) && m_average_min_distance == b.m_average_min_distance && - bob::core::array::hasSameShape(m_zeroethOrderStats, b.m_zeroethOrderStats) && - bob::core::array::hasSameShape(m_firstOrderStats, b.m_firstOrderStats) && - blitz::all(m_zeroethOrderStats == b.m_zeroethOrderStats) && - blitz::all(m_firstOrderStats == b.m_firstOrderStats); -} - -bool bob::learn::em::KMeansTrainer::operator!=(const bob::learn::em::KMeansTrainer& b) const { - return !(this->operator==(b)); -} - -void bob::learn::em::KMeansTrainer::initialize(bob::learn::em::KMeansMachine& kmeans, - const blitz::Array<double,2>& ar) -{ - // split data into as many chunks as there are means - size_t n_data = ar.extent(0); - - // assign the i'th mean to a random example within the i'th chunk - blitz::Range a = blitz::Range::all(); - if(m_initialization_method == RANDOM || m_initialization_method == RANDOM_NO_DUPLICATE) // Random initialization - { - unsigned int n_chunk = n_data / kmeans.getNMeans(); - size_t n_max_trials = (size_t)n_chunk * 5; - blitz::Array<double,1> cur_mean; - if(m_initialization_method == RANDOM_NO_DUPLICATE) - cur_mean.resize(kmeans.getNInputs()); - - for(size_t i=0; i<kmeans.getNMeans(); ++i) - { - boost::uniform_int<> die(i*n_chunk, (i+1)*n_chunk-1); - - // get random index within chunk - unsigned int index = die(*m_rng); - - // get the example at that index - blitz::Array<double, 1> mean = ar(index,a); - - if(m_initialization_method == RANDOM_NO_DUPLICATE) - { - size_t count = 0; - while(count < n_max_trials) - { - // check that the selected sampled is different than all the previously - // selected ones - bool valid = true; - for(size_t j=0; j<i && valid; ++j) - { - cur_mean = kmeans.getMean(j); - valid = blitz::any(mean != cur_mean); - } - // if different, stop otherwise, try with another one - if(valid) - break; - else - { - index = die(*m_rng); - mean = ar(index,a); - ++count; - } - } - // Initialization fails - if(count >= n_max_trials) { - boost::format m("initialization failure: surpassed the maximum number of trials (%u)"); - m % n_max_trials; - throw std::runtime_error(m.str()); - } - } - - // set the mean - kmeans.setMean(i, mean); - } - } - else // K-Means++ - { - // 1.a. Selects one sample randomly - boost::uniform_int<> die(0, n_data-1); - // Gets the example at a random index - blitz::Array<double,1> mean = ar(die(*m_rng),a); - kmeans.setMean(0, mean); - - // 1.b. Loops, computes probability distribution and select samples accordingly - blitz::Array<double,1> weights(n_data); - for(size_t m=1; m<kmeans.getNMeans(); ++m) - { - // For each sample, puts the distance to the closest mean in the weight vector - for(size_t s=0; s<n_data; ++s) - { - blitz::Array<double,1> s_cur = ar(s,a); - double& w_cur = weights(s); - // Initializes with the distance to first mean - w_cur = kmeans.getDistanceFromMean(s_cur, 0); - // Loops over the remaining mean and update the mean distance if required - for(size_t i=1; i<m; ++i) - w_cur = std::min(w_cur, kmeans.getDistanceFromMean(s_cur, i)); - } - // Square and normalize the weights vectors such that - // \f$weights[x] = D(x)^{2} \sum_{y} D(y)^{2}\f$ - weights = blitz::pow2(weights); - weights /= blitz::sum(weights); - - // Takes a sample according to the weights distribution - // Blitz iterators is fine as the weights array should be C-style contiguous - bob::core::array::assertCContiguous(weights); - bob::core::random::discrete_distribution<> die2(weights.begin(), weights.end()); - blitz::Array<double,1> new_mean = ar(die2(*m_rng),a); - kmeans.setMean(m, new_mean); - } - } -} - -void bob::learn::em::KMeansTrainer::eStep(bob::learn::em::KMeansMachine& kmeans, - const blitz::Array<double,2>& ar) -{ - // initialise the accumulators - resetAccumulators(kmeans); - - // iterate over data samples - blitz::Range a = blitz::Range::all(); - for(int i=0; i<ar.extent(0); ++i) { - // get example - blitz::Array<double, 1> x(ar(i,a)); - - // find closest mean, and distance from that mean - size_t closest_mean = 0; - double min_distance = 0; - kmeans.getClosestMean(x,closest_mean,min_distance); - - // accumulate the stats - m_average_min_distance += min_distance; - ++m_zeroethOrderStats(closest_mean); - m_firstOrderStats(closest_mean,blitz::Range::all()) += x; - } - m_average_min_distance /= static_cast<double>(ar.extent(0)); -} - -void bob::learn::em::KMeansTrainer::mStep(bob::learn::em::KMeansMachine& kmeans) -{ - blitz::Array<double,2>& means = kmeans.updateMeans(); - for(size_t i=0; i<kmeans.getNMeans(); ++i) - { - means(i,blitz::Range::all()) = - m_firstOrderStats(i,blitz::Range::all()) / m_zeroethOrderStats(i); - } -} - -double bob::learn::em::KMeansTrainer::computeLikelihood(bob::learn::em::KMeansMachine& kmeans) -{ - return m_average_min_distance; -} - - -void bob::learn::em::KMeansTrainer::resetAccumulators(bob::learn::em::KMeansMachine& kmeans) -{ - // Resize the accumulator - m_zeroethOrderStats.resize(kmeans.getNMeans()); - m_firstOrderStats.resize(kmeans.getNMeans(), kmeans.getNInputs()); - - // initialize with 0 - m_average_min_distance = 0; - m_zeroethOrderStats = 0; - m_firstOrderStats = 0; -} - -void bob::learn::em::KMeansTrainer::setZeroethOrderStats(const blitz::Array<double,1>& zeroethOrderStats) -{ - bob::core::array::assertSameShape(m_zeroethOrderStats, zeroethOrderStats); - m_zeroethOrderStats = zeroethOrderStats; -} - -void bob::learn::em::KMeansTrainer::setFirstOrderStats(const blitz::Array<double,2>& firstOrderStats) -{ - bob::core::array::assertSameShape(m_firstOrderStats, firstOrderStats); - m_firstOrderStats = firstOrderStats; -} diff --git a/bob/learn/em/cpp/LinearScoring.cpp b/bob/learn/em/cpp/LinearScoring.cpp deleted file mode 100644 index 63fdb1cce18f97686948b7ed0cfb0db3413a530b..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/LinearScoring.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @date Wed Jul 13 16:00:04 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ -#include <bob.learn.em/LinearScoring.h> -#include <bob.math/linear.h> - - -static void _linearScoring(const std::vector<blitz::Array<double,1> >& models, - const blitz::Array<double,1>& ubm_mean, - const blitz::Array<double,1>& ubm_variance, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const std::vector<blitz::Array<double,1> >* test_channelOffset, - const bool frame_length_normalisation, - blitz::Array<double,2>& scores) -{ - int C = test_stats[0]->sumPx.extent(0); - int D = test_stats[0]->sumPx.extent(1); - int CD = C*D; - int Tt = test_stats.size(); - int Tm = models.size(); - - // Check output size - bob::core::array::assertSameDimensionLength(scores.extent(0), models.size()); - bob::core::array::assertSameDimensionLength(scores.extent(1), test_stats.size()); - - blitz::Array<double,2> A(Tm, CD); - blitz::Array<double,2> B(CD, Tt); - - // 1) Compute A - for(int t=0; t<Tm; ++t) { - blitz::Array<double, 1> tmp = A(t, blitz::Range::all()); - tmp = (models[t] - ubm_mean) / ubm_variance; - } - - // 2) Compute B - if(test_channelOffset == 0) { - for(int t=0; t<Tt; ++t) - for(int s=0; s<CD; ++s) - B(s, t) = test_stats[t]->sumPx(s/D, s%D) - (ubm_mean(s) * test_stats[t]->n(s/D)); - } - else { - bob::core::array::assertSameDimensionLength((*test_channelOffset).size(), Tt); - - for(int t=0; t<Tt; ++t) { - bob::core::array::assertSameDimensionLength((*test_channelOffset)[t].extent(0), CD); - for(int s=0; s<CD; ++s) - B(s, t) = test_stats[t]->sumPx(s/D, s%D) - (test_stats[t]->n(s/D) * (ubm_mean(s) + (*test_channelOffset)[t](s))); - } - } - - // Apply the normalisation if needed - if(frame_length_normalisation) { - for(int t=0; t<Tt; ++t) { - double sum_N = test_stats[t]->T; - blitz::Array<double, 1> v_t = B(blitz::Range::all(),t); - - if (sum_N <= std::numeric_limits<double>::epsilon() && sum_N >= -std::numeric_limits<double>::epsilon()) - v_t = 0; - else - v_t /= sum_N; - } - } - - // 3) Compute LLR - bob::math::prod(A, B, scores); -} - - -void bob::learn::em::linearScoring(const std::vector<blitz::Array<double,1> >& models, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const std::vector<blitz::Array<double,1> >& test_channelOffset, - const bool frame_length_normalisation, - blitz::Array<double, 2>& scores) -{ - _linearScoring(models, ubm_mean, ubm_variance, test_stats, &test_channelOffset, frame_length_normalisation, scores); -} - -void bob::learn::em::linearScoring(const std::vector<blitz::Array<double,1> >& models, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const bool frame_length_normalisation, - blitz::Array<double, 2>& scores) -{ - _linearScoring(models, ubm_mean, ubm_variance, test_stats, 0, frame_length_normalisation, scores); -} - -void bob::learn::em::linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models, - const bob::learn::em::GMMMachine& ubm, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const bool frame_length_normalisation, - blitz::Array<double, 2>& scores) -{ - int C = test_stats[0]->sumPx.extent(0); - int D = test_stats[0]->sumPx.extent(1); - int CD = C*D; - std::vector<blitz::Array<double,1> > models_b; - // Allocate and get the mean supervector - for(size_t i=0; i<models.size(); ++i) { - blitz::Array<double,1> mod(CD); - mod = models[i]->getMeanSupervector(); - models_b.push_back(mod); - } - const blitz::Array<double,1>& ubm_mean = ubm.getMeanSupervector(); - const blitz::Array<double,1>& ubm_variance = ubm.getVarianceSupervector(); - _linearScoring(models_b, ubm_mean, ubm_variance, test_stats, 0, frame_length_normalisation, scores); -} - -void bob::learn::em::linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models, - const bob::learn::em::GMMMachine& ubm, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const std::vector<blitz::Array<double,1> >& test_channelOffset, - const bool frame_length_normalisation, - blitz::Array<double, 2>& scores) -{ - int C = test_stats[0]->sumPx.extent(0); - int D = test_stats[0]->sumPx.extent(1); - int CD = C*D; - std::vector<blitz::Array<double,1> > models_b; - // Allocate and get the mean supervector - for(size_t i=0; i<models.size(); ++i) { - blitz::Array<double,1> mod(CD); - mod = models[i]->getMeanSupervector(); - models_b.push_back(mod); - } - const blitz::Array<double,1>& ubm_mean = ubm.getMeanSupervector(); - const blitz::Array<double,1>& ubm_variance = ubm.getVarianceSupervector(); - _linearScoring(models_b, ubm_mean, ubm_variance, test_stats, &test_channelOffset, frame_length_normalisation, scores); -} - - - -double bob::learn::em::linearScoring(const blitz::Array<double,1>& models, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const bob::learn::em::GMMStats& test_stats, - const blitz::Array<double,1>& test_channelOffset, - const bool frame_length_normalisation) -{ - int C = test_stats.sumPx.extent(0); - int D = test_stats.sumPx.extent(1); - int CD = C*D; - - - blitz::Array<double,1> A(CD); - blitz::Array<double,1> B(CD); - - // 1) Compute A - A = (models - ubm_mean) / ubm_variance; - - // 2) Compute B - for (int s=0; s<CD; ++s) - B(s) = test_stats.sumPx(s/D, s%D) - (test_stats.n(s/D) * (ubm_mean(s) + test_channelOffset(s))); - - // Apply the normalisation if needed - if (frame_length_normalisation) { - double sum_N = test_stats.T; - if (sum_N == 0) - B = 0; - else - B /= sum_N; - } - - return blitz::sum(A * B); -} - diff --git a/bob/learn/em/cpp/MAP_GMMTrainer.cpp b/bob/learn/em/cpp/MAP_GMMTrainer.cpp deleted file mode 100644 index fc9a82ecfc44a3c8bc1329f6c3ae9a7bff6ab3c9..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/MAP_GMMTrainer.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/MAP_GMMTrainer.h> -#include <bob.core/check.h> - -bob::learn::em::MAP_GMMTrainer::MAP_GMMTrainer( - const bool update_means, - const bool update_variances, - const bool update_weights, - const double mean_var_update_responsibilities_threshold, - - const bool reynolds_adaptation, - const double relevance_factor, - const double alpha, - boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm): - - m_gmm_base_trainer(update_means, update_variances, update_weights, mean_var_update_responsibilities_threshold), - m_prior_gmm(prior_gmm) -{ - m_reynolds_adaptation = reynolds_adaptation; - m_relevance_factor = relevance_factor; - m_alpha = alpha; -} - - -bob::learn::em::MAP_GMMTrainer::MAP_GMMTrainer(const bob::learn::em::MAP_GMMTrainer& b): - m_gmm_base_trainer(b.m_gmm_base_trainer), - m_prior_gmm(b.m_prior_gmm) -{ - m_relevance_factor = b.m_relevance_factor; - m_alpha = b.m_alpha; - m_reynolds_adaptation = b.m_reynolds_adaptation; -} - -bob::learn::em::MAP_GMMTrainer::~MAP_GMMTrainer() -{} - -void bob::learn::em::MAP_GMMTrainer::initialize(bob::learn::em::GMMMachine& gmm) -{ - // Check that the prior GMM has been specified - if (!m_prior_gmm) - throw std::runtime_error("MAP_GMMTrainer: Prior GMM distribution has not been set"); - - // Allocate memory for the sufficient statistics and initialise - m_gmm_base_trainer.initialize(gmm); - - const size_t n_gaussians = gmm.getNGaussians(); - // TODO: check size? - gmm.setWeights(m_prior_gmm->getWeights()); - for(size_t i=0; i<n_gaussians; ++i) - { - gmm.getGaussian(i)->updateMean() = m_prior_gmm->getGaussian(i)->getMean(); - gmm.getGaussian(i)->updateVariance() = m_prior_gmm->getGaussian(i)->getVariance(); - gmm.getGaussian(i)->applyVarianceThresholds(); - } - // Initializes cache - m_cache_alpha.resize(n_gaussians); - m_cache_ml_weights.resize(n_gaussians); -} - -bool bob::learn::em::MAP_GMMTrainer::setPriorGMM(boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm) -{ - if (!prior_gmm) return false; - m_prior_gmm = prior_gmm; - return true; -} - - -void bob::learn::em::MAP_GMMTrainer::mStep(bob::learn::em::GMMMachine& gmm) -{ - // Read options and variables - double n_gaussians = gmm.getNGaussians(); - - //Checking if it is necessary to resize the cache - if((size_t)m_cache_alpha.extent(0) != n_gaussians) - initialize(gmm); //If it is different for some reason, there is no way, you have to initialize - - // Check that the prior GMM has been specified - if (!m_prior_gmm) - throw std::runtime_error("MAP_GMMTrainer: Prior GMM distribution has not been set"); - - blitz::firstIndex i; - blitz::secondIndex j; - - // Calculate the "data-dependent adaptation coefficient", alpha_i - // TODO: check if required // m_cache_alpha.resize(n_gaussians); - if (!m_reynolds_adaptation) - m_cache_alpha = m_alpha; - else - m_cache_alpha = m_gmm_base_trainer.getGMMStats()->n(i) / (m_gmm_base_trainer.getGMMStats()->n(i) + m_relevance_factor); - - // - Update weights if requested - // Equation 11 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000 - if (m_gmm_base_trainer.getUpdateWeights()) { - // Calculate the maximum likelihood weights - m_cache_ml_weights = m_gmm_base_trainer.getGMMStats()->n / static_cast<double>(m_gmm_base_trainer.getGMMStats()->T); //cast req. for linux/32-bits & osx - - // Get the prior weights - const blitz::Array<double,1>& prior_weights = m_prior_gmm->getWeights(); - blitz::Array<double,1>& new_weights = gmm.updateWeights(); - - // Calculate the new weights - new_weights = m_cache_alpha * m_cache_ml_weights + (1-m_cache_alpha) * prior_weights; - - // Apply the scale factor, gamma, to ensure the new weights sum to unity - double gamma = blitz::sum(new_weights); - new_weights /= gamma; - - // Recompute the log weights in the cache of the GMMMachine - gmm.recomputeLogWeights(); - } - - // Update GMM parameters - // - Update means if requested - // Equation 12 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000 - if (m_gmm_base_trainer.getUpdateMeans()) { - // Calculate new means - for (size_t i=0; i<n_gaussians; ++i) { - const blitz::Array<double,1>& prior_means = m_prior_gmm->getGaussian(i)->getMean(); - blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean(); - if (m_gmm_base_trainer.getGMMStats()->n(i) < m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold()) { - means = prior_means; - } - else { - // Use the maximum likelihood means - means = m_cache_alpha(i) * (m_gmm_base_trainer.getGMMStats()->sumPx(i,blitz::Range::all()) / m_gmm_base_trainer.getGMMStats()->n(i)) + (1-m_cache_alpha(i)) * prior_means; - } - } - } - - // - Update variance if requested - // Equation 13 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000 - if (m_gmm_base_trainer.getUpdateVariances()) { - // Calculate new variances (equation 13) - for (size_t i=0; i<n_gaussians; ++i) { - const blitz::Array<double,1>& prior_means = m_prior_gmm->getGaussian(i)->getMean(); - blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean(); - const blitz::Array<double,1>& prior_variances = m_prior_gmm->getGaussian(i)->getVariance(); - blitz::Array<double,1>& variances = gmm.getGaussian(i)->updateVariance(); - if (m_gmm_base_trainer.getGMMStats()->n(i) < m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold()) { - variances = (prior_variances + prior_means) - blitz::pow2(means); - } - else { - variances = m_cache_alpha(i) * m_gmm_base_trainer.getGMMStats()->sumPxx(i,blitz::Range::all()) / m_gmm_base_trainer.getGMMStats()->n(i) + (1-m_cache_alpha(i)) * (prior_variances + prior_means) - blitz::pow2(means); - } - gmm.getGaussian(i)->applyVarianceThresholds(); - } - } -} - - - -bob::learn::em::MAP_GMMTrainer& bob::learn::em::MAP_GMMTrainer::operator= - (const bob::learn::em::MAP_GMMTrainer &other) -{ - if (this != &other) - { - m_gmm_base_trainer = other.m_gmm_base_trainer; - m_relevance_factor = other.m_relevance_factor; - m_prior_gmm = other.m_prior_gmm; - m_alpha = other.m_alpha; - m_reynolds_adaptation = other.m_reynolds_adaptation; - m_cache_alpha.resize(other.m_cache_alpha.extent(0)); - m_cache_ml_weights.resize(other.m_cache_ml_weights.extent(0)); - } - return *this; -} - - -bool bob::learn::em::MAP_GMMTrainer::operator== - (const bob::learn::em::MAP_GMMTrainer &other) const -{ - return m_gmm_base_trainer == other.m_gmm_base_trainer && - m_relevance_factor == other.m_relevance_factor && - m_prior_gmm == other.m_prior_gmm && - m_alpha == other.m_alpha && - m_reynolds_adaptation == other.m_reynolds_adaptation; -} - - -bool bob::learn::em::MAP_GMMTrainer::operator!= - (const bob::learn::em::MAP_GMMTrainer &other) const -{ - return !(this->operator==(other)); -} - - -bool bob::learn::em::MAP_GMMTrainer::is_similar_to - (const bob::learn::em::MAP_GMMTrainer &other, const double r_epsilon, - const double a_epsilon) const -{ - return //m_gmm_base_trainer.is_similar_to(other.m_gmm_base_trainer, r_epsilon, a_epsilon) && - bob::core::isClose(m_relevance_factor, other.m_relevance_factor, r_epsilon, a_epsilon) && - m_prior_gmm == other.m_prior_gmm && - bob::core::isClose(m_alpha, other.m_alpha, r_epsilon, a_epsilon) && - m_reynolds_adaptation == other.m_reynolds_adaptation; -} diff --git a/bob/learn/em/cpp/ML_GMMTrainer.cpp b/bob/learn/em/cpp/ML_GMMTrainer.cpp deleted file mode 100644 index 9ac71029c3048fc36b98c85a7f675dd79feac8c8..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/ML_GMMTrainer.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.learn.em/ML_GMMTrainer.h> -#include <algorithm> - -bob::learn::em::ML_GMMTrainer::ML_GMMTrainer( - const bool update_means, - const bool update_variances, - const bool update_weights, - const double mean_var_update_responsibilities_threshold -): - m_gmm_base_trainer(update_means, update_variances, update_weights, mean_var_update_responsibilities_threshold) -{} - - - -bob::learn::em::ML_GMMTrainer::ML_GMMTrainer(const bob::learn::em::ML_GMMTrainer& b): - m_gmm_base_trainer(b.m_gmm_base_trainer) -{} - -bob::learn::em::ML_GMMTrainer::~ML_GMMTrainer() -{} - -void bob::learn::em::ML_GMMTrainer::initialize(bob::learn::em::GMMMachine& gmm) -{ - m_gmm_base_trainer.initialize(gmm); - - // Allocate cache - size_t n_gaussians = gmm.getNGaussians(); - m_cache_ss_n_thresholded.resize(n_gaussians); -} - - -void bob::learn::em::ML_GMMTrainer::mStep(bob::learn::em::GMMMachine& gmm) -{ - // Read options and variables - const size_t n_gaussians = gmm.getNGaussians(); - - //Checking if it is necessary to resize the cache - if((size_t)m_cache_ss_n_thresholded.extent(0) != n_gaussians) - initialize(gmm); //If it is different for some reason, there is no way, you have to initialize - - // - Update weights if requested - // Equation 9.26 of Bishop, "Pattern recognition and machine learning", 2006 - if (m_gmm_base_trainer.getUpdateWeights()) { - blitz::Array<double,1>& weights = gmm.updateWeights(); - weights = m_gmm_base_trainer.getGMMStats()->n / static_cast<double>(m_gmm_base_trainer.getGMMStats()->T); //cast req. for linux/32-bits & osx - // Recompute the log weights in the cache of the GMMMachine - gmm.recomputeLogWeights(); - } - - // Generate a thresholded version of m_ss.n - for(size_t i=0; i<n_gaussians; ++i) - m_cache_ss_n_thresholded(i) = std::max(m_gmm_base_trainer.getGMMStats()->n(i), m_gmm_base_trainer.getMeanVarUpdateResponsibilitiesThreshold()); - - // Update GMM parameters using the sufficient statistics (m_ss) - // - Update means if requested - // Equation 9.24 of Bishop, "Pattern recognition and machine learning", 2006 - if (m_gmm_base_trainer.getUpdateMeans()) { - for(size_t i=0; i<n_gaussians; ++i) { - blitz::Array<double,1>& means = gmm.getGaussian(i)->updateMean(); - means = m_gmm_base_trainer.getGMMStats()->sumPx(i, blitz::Range::all()) / m_cache_ss_n_thresholded(i); - } - } - - // - Update variance if requested - // See Equation 9.25 of Bishop, "Pattern recognition and machine learning", 2006 - // ...but we use the "computational formula for the variance", i.e. - // var = 1/n * sum (P(x-mean)(x-mean)) - // = 1/n * sum (Pxx) - mean^2 - if (m_gmm_base_trainer.getUpdateVariances()) { - for(size_t i=0; i<n_gaussians; ++i) { - const blitz::Array<double,1>& means = gmm.getGaussian(i)->getMean(); - blitz::Array<double,1>& variances = gmm.getGaussian(i)->updateVariance(); - variances = m_gmm_base_trainer.getGMMStats()->sumPxx(i, blitz::Range::all()) / m_cache_ss_n_thresholded(i) - blitz::pow2(means); - gmm.getGaussian(i)->applyVarianceThresholds(); - } - } -} - -bob::learn::em::ML_GMMTrainer& bob::learn::em::ML_GMMTrainer::operator= - (const bob::learn::em::ML_GMMTrainer &other) -{ - if (this != &other) - { - m_gmm_base_trainer = other.m_gmm_base_trainer; - m_cache_ss_n_thresholded.resize(other.m_cache_ss_n_thresholded.extent(0)); - } - return *this; -} - -bool bob::learn::em::ML_GMMTrainer::operator== - (const bob::learn::em::ML_GMMTrainer &other) const -{ - return m_gmm_base_trainer == other.m_gmm_base_trainer; -} - -bool bob::learn::em::ML_GMMTrainer::operator!= - (const bob::learn::em::ML_GMMTrainer &other) const -{ - return !(this->operator==(other)); -} diff --git a/bob/learn/em/cpp/PLDAMachine.cpp b/bob/learn/em/cpp/PLDAMachine.cpp deleted file mode 100644 index fe60630d42ad877943afb52946e7ae42018788f0..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/PLDAMachine.cpp +++ /dev/null @@ -1,960 +0,0 @@ -/** - * @date Fri Oct 14 18:07:56 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Machines that implements the PLDA model - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#include <bob.core/assert.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.learn.em/PLDAMachine.h> -#include <bob.math/linear.h> -#include <bob.math/det.h> -#include <bob.math/inv.h> - -#include <cmath> -#include <boost/lexical_cast.hpp> -#include <string> - -bob::learn::em::PLDABase::PLDABase(): - m_variance_threshold(0.) -{ - resizeNoInit(0, 0, 0); -} - -bob::learn::em::PLDABase::PLDABase(const size_t dim_d, const size_t dim_f, - const size_t dim_g, const double variance_threshold): - m_variance_threshold(variance_threshold) -{ - resize(dim_d, dim_f, dim_g); -} - - -bob::learn::em::PLDABase::PLDABase(const bob::learn::em::PLDABase& other): - m_dim_d(other.m_dim_d), - m_dim_f(other.m_dim_f), - m_dim_g(other.m_dim_g), - m_F(bob::core::array::ccopy(other.m_F)), - m_G(bob::core::array::ccopy(other.m_G)), - m_sigma(bob::core::array::ccopy(other.m_sigma)), - m_mu(bob::core::array::ccopy(other.m_mu)), - m_variance_threshold(other.m_variance_threshold), - m_cache_isigma(bob::core::array::ccopy(other.m_cache_isigma)), - m_cache_alpha(bob::core::array::ccopy(other.m_cache_alpha)), - m_cache_beta(bob::core::array::ccopy(other.m_cache_beta)), - m_cache_gamma(), - m_cache_Ft_beta(bob::core::array::ccopy(other.m_cache_Ft_beta)), - m_cache_Gt_isigma(bob::core::array::ccopy(other.m_cache_Gt_isigma)), - m_cache_logdet_alpha(other.m_cache_logdet_alpha), - m_cache_logdet_sigma(other.m_cache_logdet_sigma), - m_cache_loglike_constterm(other.m_cache_loglike_constterm) -{ - bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma); - resizeTmp(); -} - -bob::learn::em::PLDABase::PLDABase(bob::io::base::HDF5File& config) { - load(config); -} - -bob::learn::em::PLDABase::~PLDABase() { -} - -bob::learn::em::PLDABase& bob::learn::em::PLDABase::operator= - (const bob::learn::em::PLDABase& other) -{ - if (this != &other) - { - m_dim_d = other.m_dim_d; - m_dim_f = other.m_dim_f; - m_dim_g = other.m_dim_g; - m_F.reference(bob::core::array::ccopy(other.m_F)); - m_G.reference(bob::core::array::ccopy(other.m_G)); - m_sigma.reference(bob::core::array::ccopy(other.m_sigma)); - m_mu.reference(bob::core::array::ccopy(other.m_mu)); - m_variance_threshold = other.m_variance_threshold; - m_cache_isigma.reference(bob::core::array::ccopy(other.m_cache_isigma)); - m_cache_alpha.reference(bob::core::array::ccopy(other.m_cache_alpha)); - m_cache_beta.reference(bob::core::array::ccopy(other.m_cache_beta)); - bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma); - m_cache_Ft_beta.reference(bob::core::array::ccopy(other.m_cache_Ft_beta)); - m_cache_Gt_isigma.reference(bob::core::array::ccopy(other.m_cache_Gt_isigma)); - m_cache_logdet_alpha = other.m_cache_logdet_alpha; - m_cache_logdet_sigma = other.m_cache_logdet_sigma; - m_cache_loglike_constterm = other.m_cache_loglike_constterm; - resizeTmp(); - } - return *this; -} - -bool bob::learn::em::PLDABase::operator== - (const bob::learn::em::PLDABase& b) const -{ - if (!(m_dim_d == b.m_dim_d && m_dim_f == b.m_dim_f && - m_dim_g == b.m_dim_g && - bob::core::array::isEqual(m_F, b.m_F) && - bob::core::array::isEqual(m_G, b.m_G) && - bob::core::array::isEqual(m_sigma, b.m_sigma) && - bob::core::array::isEqual(m_mu, b.m_mu) && - m_variance_threshold == b.m_variance_threshold && - bob::core::array::isEqual(m_cache_isigma, b.m_cache_isigma) && - bob::core::array::isEqual(m_cache_alpha, b.m_cache_alpha) && - bob::core::array::isEqual(m_cache_beta, b.m_cache_beta) && - bob::core::array::isEqual(m_cache_gamma, b.m_cache_gamma) && - bob::core::array::isEqual(m_cache_Ft_beta, b.m_cache_Ft_beta) && - bob::core::array::isEqual(m_cache_Gt_isigma, b.m_cache_Gt_isigma) && - m_cache_logdet_alpha == b.m_cache_logdet_alpha && - m_cache_logdet_sigma == b.m_cache_logdet_sigma)) - return false; - - // m_cache_loglike_constterm - if (this->m_cache_loglike_constterm.size() != b.m_cache_loglike_constterm.size()) - return false; // differing sizes, they are not the same - std::map<size_t, double>::const_iterator i, j; - for (i = this->m_cache_loglike_constterm.begin(), j = b.m_cache_loglike_constterm.begin(); - i != this->m_cache_loglike_constterm.end(); ++i, ++j) - { - if (i->first != j->first || i->second != j->second) - return false; - } - - return true; -} - -bool bob::learn::em::PLDABase::operator!= - (const bob::learn::em::PLDABase& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::PLDABase::is_similar_to(const bob::learn::em::PLDABase& b, - const double r_epsilon, const double a_epsilon) const -{ - return (m_dim_d == b.m_dim_d && m_dim_f == b.m_dim_f && - m_dim_g == b.m_dim_g && - bob::core::array::isClose(m_F, b.m_F, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_G, b.m_G, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_sigma, b.m_sigma, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_mu, b.m_mu, r_epsilon, a_epsilon) && - bob::core::isClose(m_variance_threshold, b.m_variance_threshold, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_isigma, b.m_cache_isigma, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_alpha, b.m_cache_alpha, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_beta, b.m_cache_beta, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_gamma, b.m_cache_gamma, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_Ft_beta, b.m_cache_Ft_beta, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_Gt_isigma, b.m_cache_Gt_isigma, r_epsilon, a_epsilon) && - bob::core::isClose(m_cache_logdet_alpha, b.m_cache_logdet_alpha, r_epsilon, a_epsilon) && - bob::core::isClose(m_cache_logdet_sigma, b.m_cache_logdet_sigma, r_epsilon, a_epsilon) && - bob::core::isClose(m_cache_loglike_constterm, b.m_cache_loglike_constterm)); -} - -void bob::learn::em::PLDABase::load(bob::io::base::HDF5File& config) -{ - if (!config.contains("dim_d")) - { - // Then the model was saved using bob < 1.2.0 - //reads all data directly into the member variables - m_F.reference(config.readArray<double,2>("F")); - m_G.reference(config.readArray<double,2>("G")); - m_dim_d = m_F.extent(0); - m_dim_f = m_F.extent(1); - m_dim_g = m_G.extent(1); - m_sigma.reference(config.readArray<double,1>("sigma")); - m_mu.reference(config.readArray<double,1>("mu")); - m_cache_isigma.resize(m_dim_d); - precomputeISigma(); - m_variance_threshold = 0.; - m_cache_alpha.reference(config.readArray<double,2>("alpha")); - m_cache_beta.reference(config.readArray<double,2>("beta")); - // gamma and log like constant term (a-dependent terms) - if (config.contains("a_indices")) - { - blitz::Array<uint32_t, 1> a_indices; - a_indices.reference(config.readArray<uint32_t,1>("a_indices")); - for (int i=0; i<a_indices.extent(0); ++i) - { - std::string str1 = "gamma_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str1)); - std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str2); - } - } - m_cache_Ft_beta.reference(config.readArray<double,2>("Ft_beta")); - m_cache_Gt_isigma.reference(config.readArray<double,2>("Gt_isigma")); - m_cache_logdet_alpha = config.read<double>("logdet_alpha"); - m_cache_logdet_sigma = config.read<double>("logdet_sigma"); - } - else - { - // Then the model was saved using bob >= 1.2.0 - //reads all data directly into the member variables - m_F.reference(config.readArray<double,2>("F")); - m_G.reference(config.readArray<double,2>("G")); - // Conditional because previous versions had not these variables - m_dim_d = config.read<uint64_t>("dim_d"); - m_dim_f = config.read<uint64_t>("dim_f"); - m_dim_g = config.read<uint64_t>("dim_g"); - m_sigma.reference(config.readArray<double,1>("sigma")); - m_mu.reference(config.readArray<double,1>("mu")); - m_cache_isigma.resize(m_dim_d); - precomputeISigma(); - if (config.contains("variance_threshold")) - m_variance_threshold = config.read<double>("variance_threshold"); - else if (config.contains("variance_thresholds")) // In case 1.2.0 alpha/beta version has been used - { - blitz::Array<double,1> tmp; - tmp.reference(config.readArray<double,1>("variance_thresholds")); - m_variance_threshold = tmp(0); - } - m_cache_alpha.reference(config.readArray<double,2>("alpha")); - m_cache_beta.reference(config.readArray<double,2>("beta")); - // gamma's (a-dependent terms) - if(config.contains("a_indices_gamma")) - { - blitz::Array<uint32_t, 1> a_indices; - a_indices.reference(config.readArray<uint32_t,1>("a_indices_gamma")); - for(int i=0; i<a_indices.extent(0); ++i) - { - std::string str = "gamma_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str)); - } - } - // log likelihood constant term's (a-dependent terms) - if(config.contains("a_indices_loglikeconstterm")) - { - blitz::Array<uint32_t, 1> a_indices; - a_indices.reference(config.readArray<uint32_t,1>("a_indices_loglikeconstterm")); - for(int i=0; i<a_indices.extent(0); ++i) - { - std::string str = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str); - } - } - m_cache_Ft_beta.reference(config.readArray<double,2>("Ft_beta")); - m_cache_Gt_isigma.reference(config.readArray<double,2>("Gt_isigma")); - m_cache_logdet_alpha = config.read<double>("logdet_alpha"); - m_cache_logdet_sigma = config.read<double>("logdet_sigma"); - } - resizeTmp(); -} - -void bob::learn::em::PLDABase::save(bob::io::base::HDF5File& config) const -{ - config.set("dim_d", (uint64_t)m_dim_d); - config.set("dim_f", (uint64_t)m_dim_f); - config.set("dim_g", (uint64_t)m_dim_g); - config.setArray("F", m_F); - config.setArray("G", m_G); - config.setArray("sigma", m_sigma); - config.setArray("mu", m_mu); - config.set("variance_threshold", m_variance_threshold); - config.setArray("alpha", m_cache_alpha); - config.setArray("beta", m_cache_beta); - // gamma's - if(m_cache_gamma.size() > 0) - { - blitz::Array<uint32_t, 1> a_indices(m_cache_gamma.size()); - int i = 0; - for(std::map<size_t,blitz::Array<double,2> >::const_iterator - it=m_cache_gamma.begin(); it!=m_cache_gamma.end(); ++it) - { - a_indices(i) = it->first; - std::string str = "gamma_" + boost::lexical_cast<std::string>(it->first); - config.setArray(str, it->second); - ++i; - } - config.setArray("a_indices_gamma", a_indices); - } - // log likelihood constant terms - if(m_cache_loglike_constterm.size() > 0) - { - blitz::Array<uint32_t, 1> a_indices(m_cache_loglike_constterm.size()); - int i = 0; - for(std::map<size_t,double>::const_iterator - it=m_cache_loglike_constterm.begin(); it!=m_cache_loglike_constterm.end(); ++it) - { - a_indices(i) = it->first; - std::string str = "loglikeconstterm_" + boost::lexical_cast<std::string>(it->first); - config.set(str, it->second); - ++i; - } - config.setArray("a_indices_loglikeconstterm", a_indices); - } - - config.setArray("Ft_beta", m_cache_Ft_beta); - config.setArray("Gt_isigma", m_cache_Gt_isigma); - config.set("logdet_alpha", m_cache_logdet_alpha); - config.set("logdet_sigma", m_cache_logdet_sigma); -} - -void bob::learn::em::PLDABase::resizeNoInit(const size_t dim_d, const size_t dim_f, - const size_t dim_g) -{ - m_dim_d = dim_d; - m_dim_f = dim_f; - m_dim_g = dim_g; - m_F.resize(dim_d, dim_f); - m_G.resize(dim_d, dim_g); - m_sigma.resize(dim_d); - m_mu.resize(dim_d); - m_cache_alpha.resize(dim_g, dim_g); - m_cache_beta.resize(dim_d, dim_d); - m_cache_Ft_beta.resize(dim_f, dim_d); - m_cache_Gt_isigma.resize(dim_g, dim_d); - m_cache_gamma.clear(); - m_cache_isigma.resize(dim_d); - m_cache_loglike_constterm.clear(); - resizeTmp(); -} - -void bob::learn::em::PLDABase::resizeTmp() -{ - m_tmp_d_1.resize(m_dim_d); - m_tmp_d_2.resize(m_dim_d); - m_tmp_d_ng_1.resize(m_dim_d, m_dim_g); - m_tmp_nf_nf_1.resize(m_dim_f, m_dim_f); - m_tmp_ng_ng_1.resize(m_dim_g, m_dim_g); -} - -void bob::learn::em::PLDABase::resize(const size_t dim_d, const size_t dim_f, - const size_t dim_g) -{ - resizeNoInit(dim_d, dim_f, dim_g); - initMuFGSigma(); -} - -void bob::learn::em::PLDABase::setF(const blitz::Array<double,2>& F) -{ - bob::core::array::assertSameShape(F, m_F); - m_F.reference(bob::core::array::ccopy(F)); - // Precomputes useful matrices - precompute(); -} - -void bob::learn::em::PLDABase::setG(const blitz::Array<double,2>& G) -{ - bob::core::array::assertSameShape(G, m_G); - m_G.reference(bob::core::array::ccopy(G)); - // Precomputes useful matrices and values - precompute(); - precomputeLogDetAlpha(); -} - -void bob::learn::em::PLDABase::setSigma(const blitz::Array<double,1>& sigma) -{ - bob::core::array::assertSameShape(sigma, m_sigma); - m_sigma.reference(bob::core::array::ccopy(sigma)); - // Apply variance flooring threshold: This will also - // call the precompute() and precomputeLogLike() methods! - applyVarianceThreshold(); -} - -void bob::learn::em::PLDABase::setMu(const blitz::Array<double,1>& mu) -{ - bob::core::array::assertSameShape(mu, m_mu); - m_mu.reference(bob::core::array::ccopy(mu)); -} - -void bob::learn::em::PLDABase::setVarianceThreshold(const double value) -{ - // Variance flooring - m_variance_threshold = value; - // Apply variance flooring thresholds: This will also - // call the precompute() and precomputeLogLike() methods! - applyVarianceThreshold(); -} - -void bob::learn::em::PLDABase::applyVarianceThreshold() -{ - // Apply variance flooring threshold - m_sigma = blitz::where( m_sigma < m_variance_threshold, m_variance_threshold, m_sigma); - // Re-compute constants, because m_sigma has changed - precompute(); - precomputeLogLike(); -} - -const blitz::Array<double,2>& bob::learn::em::PLDABase::getGamma(const size_t a) const -{ - if(!hasGamma(a)) - throw std::runtime_error("Gamma for this number of samples is not currently in cache. You could use the getAddGamma() method instead"); - return (m_cache_gamma.find(a))->second; -} - -const blitz::Array<double,2>& bob::learn::em::PLDABase::getAddGamma(const size_t a) -{ - if(!hasGamma(a)) precomputeGamma(a); - return m_cache_gamma[a]; -} - -void bob::learn::em::PLDABase::initMuFGSigma() -{ - // To avoid problems related to precomputation - m_mu = 0.; - bob::math::eye(m_F); - bob::math::eye(m_G); - m_sigma = 1.; - // Precompute variables - precompute(); - precomputeLogLike(); -} - -void bob::learn::em::PLDABase::precompute() -{ - precomputeISigma(); - precomputeGtISigma(); - precomputeAlpha(); - precomputeBeta(); - m_cache_gamma.clear(); - precomputeFtBeta(); - m_cache_loglike_constterm.clear(); -} - -void bob::learn::em::PLDABase::precomputeLogLike() -{ - precomputeLogDetAlpha(); - precomputeLogDetSigma(); -} - -void bob::learn::em::PLDABase::precomputeISigma() -{ - // Updates inverse of sigma - m_cache_isigma = 1. / m_sigma; -} - -void bob::learn::em::PLDABase::precomputeGtISigma() -{ - // m_cache_Gt_isigma = G^T \Sigma^{-1} - blitz::firstIndex i; - blitz::secondIndex j; - blitz::Array<double,2> Gt = m_G.transpose(1,0); - m_cache_Gt_isigma = Gt(i,j) * m_cache_isigma(j); -} - -void bob::learn::em::PLDABase::precomputeAlpha() -{ - // alpha = (Id + G^T.sigma^-1.G)^-1 - - // m_tmp_ng_ng_1 = G^T.sigma^-1.G - bob::math::prod(m_cache_Gt_isigma, m_G, m_tmp_ng_ng_1); - // m_tmp_ng_ng_1 = Id + G^T.sigma^-1.G - for(int i=0; i<m_tmp_ng_ng_1.extent(0); ++i) m_tmp_ng_ng_1(i,i) += 1; - // m_cache_alpha = (Id + G^T.sigma^-1.G)^-1 - bob::math::inv(m_tmp_ng_ng_1, m_cache_alpha); -} - -void bob::learn::em::PLDABase::precomputeBeta() -{ - // beta = (sigma + G.G^T)^-1 - // BUT, there is a more efficient computation (Woodbury identity): - // beta = sigma^-1 - sigma^-1.G.(Id + G^T.sigma^-1.G)^-1.G^T.sigma^-1 - // beta = sigma^-1 - sigma^-1.G.alpha.G^T.sigma^-1 - - blitz::Array<double,2> GtISigmaT = m_cache_Gt_isigma.transpose(1,0); - // m_tmp_d_ng_1 = sigma^-1.G.alpha - bob::math::prod(GtISigmaT, m_cache_alpha, m_tmp_d_ng_1); - // m_cache_beta = -sigma^-1.G.alpha.G^T.sigma^-1 - bob::math::prod(m_tmp_d_ng_1, m_cache_Gt_isigma, m_cache_beta); - m_cache_beta = -m_cache_beta; - // m_cache_beta = sigma^-1 - sigma^-1.G.alpha.G^T.sigma^-1 - for(int i=0; i<m_cache_beta.extent(0); ++i) m_cache_beta(i,i) += m_cache_isigma(i); -} - -void bob::learn::em::PLDABase::precomputeGamma(const size_t a) -{ - - blitz::Array<double,2> gamma_a(getDimF(),getDimF()); - m_cache_gamma[a].reference(gamma_a); - computeGamma(a, gamma_a); -} - -void bob::learn::em::PLDABase::precomputeFtBeta() -{ - // m_cache_Ft_beta = F^T.beta = F^T.(sigma + G.G^T)^-1 - blitz::Array<double,2> Ft = m_F.transpose(1,0); - bob::math::prod(Ft, m_cache_beta, m_cache_Ft_beta); -} - -void bob::learn::em::PLDABase::computeGamma(const size_t a, - blitz::Array<double,2> res) const -{ - // gamma = (Id + a.F^T.beta.F)^-1 - - // Checks destination size - bob::core::array::assertSameShape(res, m_tmp_nf_nf_1); - // m_tmp_nf_nf_1 = F^T.beta.F - bob::math::prod(m_cache_Ft_beta, m_F, m_tmp_nf_nf_1); - // m_tmp_nf_nf_1 = a.F^T.beta.F - m_tmp_nf_nf_1 *= static_cast<double>(a); - // m_tmp_nf_nf_1 = Id + a.F^T.beta.F - for(int i=0; i<m_tmp_nf_nf_1.extent(0); ++i) m_tmp_nf_nf_1(i,i) += 1; - - // res = (Id + a.F^T.beta.F)^-1 - bob::math::inv(m_tmp_nf_nf_1, res); -} - -void bob::learn::em::PLDABase::precomputeLogDetAlpha() -{ - int sign; - m_cache_logdet_alpha = bob::math::slogdet(m_cache_alpha, sign); -} - -void bob::learn::em::PLDABase::precomputeLogDetSigma() -{ - m_cache_logdet_sigma = blitz::sum(blitz::log(m_sigma)); -} - -double bob::learn::em::PLDABase::computeLogLikeConstTerm(const size_t a, - const blitz::Array<double,2>& gamma_a) const -{ - // loglike_constterm[a] = a/2 * - // ( -D*log(2*pi) -log|sigma| +log|alpha| +log|gamma_a|) - int sign; - double logdet_gamma_a = bob::math::slogdet(gamma_a, sign); - double ah = static_cast<double>(a)/2.; - double res = ( -ah*((double)m_dim_d)*log(2*M_PI) - - ah*m_cache_logdet_sigma + ah*m_cache_logdet_alpha + logdet_gamma_a/2.); - return res; -} - -double bob::learn::em::PLDABase::computeLogLikeConstTerm(const size_t a) -{ - const blitz::Array<double,2>& gamma_a = getAddGamma(a); - return computeLogLikeConstTerm(a, gamma_a); -} - -void bob::learn::em::PLDABase::precomputeLogLikeConstTerm(const size_t a) -{ - double val = computeLogLikeConstTerm(a); - m_cache_loglike_constterm[a] = val; -} - -double bob::learn::em::PLDABase::getLogLikeConstTerm(const size_t a) const -{ - if(!hasLogLikeConstTerm(a)) - throw std::runtime_error("The LogLikelihood constant term for this number of samples is not currently in cache. You could use the getAddLogLikeConstTerm() method instead"); - return (m_cache_loglike_constterm.find(a))->second; -} - -double bob::learn::em::PLDABase::getAddLogLikeConstTerm(const size_t a) -{ - if(!hasLogLikeConstTerm(a)) precomputeLogLikeConstTerm(a); - return m_cache_loglike_constterm[a]; -} - -void bob::learn::em::PLDABase::clearMaps() -{ - m_cache_gamma.clear(); - m_cache_loglike_constterm.clear(); -} - -double bob::learn::em::PLDABase::computeLogLikelihoodPointEstimate( - const blitz::Array<double,1>& xij, const blitz::Array<double,1>& hi, - const blitz::Array<double,1>& wij) const -{ - // Check inputs - bob::core::array::assertSameDimensionLength(xij.extent(0), getDimD()); - bob::core::array::assertSameDimensionLength(hi.extent(0), getDimF()); - bob::core::array::assertSameDimensionLength(wij.extent(0), getDimG()); - // Computes: -D/2 log(2pi) -1/2 log(det(\Sigma)) - // -1/2 {(x_{ij}-(\mu+Fh_{i}+Gw_{ij}))^{T}\Sigma^{-1}(x_{ij}-(\mu+Fh_{i}+Gw_{ij}))} - double res = -0.5*((double)m_dim_d)*log(2*M_PI) - 0.5*m_cache_logdet_sigma; - // m_tmp_d_1 = (x_{ij} - (\mu+Fh_{i}+Gw_{ij})) - m_tmp_d_1 = xij - m_mu; - bob::math::prod(m_F, hi, m_tmp_d_2); - m_tmp_d_1 -= m_tmp_d_2; - bob::math::prod(m_G, wij, m_tmp_d_2); - m_tmp_d_1 -= m_tmp_d_2; - // add third term to res - res += -0.5*blitz::sum(blitz::pow2(m_tmp_d_1) * m_cache_isigma); - return res; -} - -namespace bob { namespace learn { namespace em { - /** - * @brief Prints a PLDABase in the output stream. This will print - * the values of the parameters \f$\mu\f$, \f$F\f$, \f$G\f$ and - * \f$\Sigma\f$ of the PLDA model. - */ - std::ostream& operator<<(std::ostream& os, const PLDABase& m) { - os << "mu = " << m.m_mu << std::endl; - os << "sigma = " << m.m_sigma << std::endl; - os << "F = " << m.m_F << std::endl; - os << "G = " << m.m_G << std::endl; - return os; - } -} } } - - -bob::learn::em::PLDAMachine::PLDAMachine(): - m_plda_base(), - m_n_samples(0), m_nh_sum_xit_beta_xi(0), m_weighted_sum(0), - m_loglikelihood(0), m_cache_gamma(), m_cache_loglike_constterm(), - m_tmp_d_1(0), m_tmp_d_2(0), m_tmp_nf_1(0), m_tmp_nf_2(0), m_tmp_nf_nf_1(0,0) -{ -} - -bob::learn::em::PLDAMachine::PLDAMachine(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base): - m_plda_base(plda_base), - m_n_samples(0), m_nh_sum_xit_beta_xi(0), m_weighted_sum(plda_base->getDimF()), - m_loglikelihood(0), m_cache_gamma(), m_cache_loglike_constterm() -{ - resizeTmp(); -} - - -bob::learn::em::PLDAMachine::PLDAMachine(const bob::learn::em::PLDAMachine& other): - m_plda_base(other.m_plda_base), - m_n_samples(other.m_n_samples), - m_nh_sum_xit_beta_xi(other.m_nh_sum_xit_beta_xi), - m_weighted_sum(bob::core::array::ccopy(other.m_weighted_sum)), - m_loglikelihood(other.m_loglikelihood), m_cache_gamma(), - m_cache_loglike_constterm(other.m_cache_loglike_constterm) -{ - bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma); - resizeTmp(); -} - -bob::learn::em::PLDAMachine::PLDAMachine(bob::io::base::HDF5File& config, - const boost::shared_ptr<bob::learn::em::PLDABase> plda_base): - m_plda_base(plda_base) -{ - load(config); -} - -bob::learn::em::PLDAMachine::~PLDAMachine() { -} - -bob::learn::em::PLDAMachine& bob::learn::em::PLDAMachine::operator= -(const bob::learn::em::PLDAMachine& other) -{ - if(this!=&other) - { - m_plda_base = other.m_plda_base; - m_n_samples = other.m_n_samples; - m_nh_sum_xit_beta_xi = other.m_nh_sum_xit_beta_xi; - m_weighted_sum.reference(bob::core::array::ccopy(other.m_weighted_sum)); - m_loglikelihood = other.m_loglikelihood; - bob::core::array::ccopy(other.m_cache_gamma, m_cache_gamma); - m_cache_loglike_constterm = other.m_cache_loglike_constterm; - resizeTmp(); - } - return *this; -} - -bool bob::learn::em::PLDAMachine::operator== - (const bob::learn::em::PLDAMachine& b) const -{ - if (!(( (!m_plda_base && !b.m_plda_base) || - ((m_plda_base && b.m_plda_base) && *(m_plda_base) == *(b.m_plda_base))) && - m_n_samples == b.m_n_samples && - m_nh_sum_xit_beta_xi ==b.m_nh_sum_xit_beta_xi && - bob::core::array::isEqual(m_weighted_sum, b.m_weighted_sum) && - m_loglikelihood == b.m_loglikelihood && - bob::core::array::isEqual(m_cache_gamma, b.m_cache_gamma))) - return false; - - // m_cache_loglike_constterm - if (this->m_cache_loglike_constterm.size() != b.m_cache_loglike_constterm.size()) - return false; // differing sizes, they are not the same - std::map<size_t, double>::const_iterator i, j; - for (i = this->m_cache_loglike_constterm.begin(), j = b.m_cache_loglike_constterm.begin(); - i != this->m_cache_loglike_constterm.end(); ++i, ++j) - { - if (i->first != j->first || i->second != j->second) - return false; - } - - return true; -} - -bool bob::learn::em::PLDAMachine::operator!= - (const bob::learn::em::PLDAMachine& b) const -{ - return !(this->operator==(b)); -} - -bool bob::learn::em::PLDAMachine::is_similar_to( - const bob::learn::em::PLDAMachine& b, const double r_epsilon, - const double a_epsilon) const -{ - return (( (!m_plda_base && !b.m_plda_base) || - ((m_plda_base && b.m_plda_base) && - m_plda_base->is_similar_to(*(b.m_plda_base), r_epsilon, a_epsilon))) && - m_n_samples == b.m_n_samples && - bob::core::isClose(m_nh_sum_xit_beta_xi, b.m_nh_sum_xit_beta_xi, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_weighted_sum, b.m_weighted_sum, r_epsilon, a_epsilon) && - bob::core::isClose(m_loglikelihood, b.m_loglikelihood, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_gamma, b.m_cache_gamma, r_epsilon, a_epsilon) && - bob::core::isClose(m_cache_loglike_constterm, b.m_cache_loglike_constterm, r_epsilon, a_epsilon)); -} - -void bob::learn::em::PLDAMachine::load(bob::io::base::HDF5File& config) -{ - //reads all data directly into the member variables - m_n_samples = config.read<uint64_t>("n_samples"); - m_nh_sum_xit_beta_xi = config.read<double>("nh_sum_xit_beta_xi"); - m_weighted_sum.reference(config.readArray<double,1>("weighted_sum")); - m_loglikelihood = config.read<double>("loglikelihood"); - // gamma and log like constant term (a-dependent terms) - clearMaps(); - if(config.contains("a_indices")) - { - blitz::Array<uint32_t, 1> a_indices; - a_indices.reference(config.readArray<uint32_t,1>("a_indices")); - for(int i=0; i<a_indices.extent(0); ++i) - { - std::string str1 = "gamma_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_gamma[a_indices(i)].reference(config.readArray<double,2>(str1)); - std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(a_indices(i)); - m_cache_loglike_constterm[a_indices(i)] = config.read<double>(str2); - } - } - resizeTmp(); -} - -void bob::learn::em::PLDAMachine::save(bob::io::base::HDF5File& config) const -{ - config.set("n_samples", m_n_samples); - config.set("nh_sum_xit_beta_xi", m_nh_sum_xit_beta_xi); - config.setArray("weighted_sum", m_weighted_sum); - config.set("loglikelihood", m_loglikelihood); - // Gamma - if(m_cache_gamma.size() > 0) - { - blitz::Array<uint32_t, 1> a_indices(m_cache_gamma.size()); - int i = 0; - for(std::map<size_t,blitz::Array<double,2> >::const_iterator - it=m_cache_gamma.begin(); it!=m_cache_gamma.end(); ++it) - { - a_indices(i) = it->first; - std::string str1 = "gamma_" + boost::lexical_cast<std::string>(it->first); - config.setArray(str1, it->second); - std::string str2 = "loglikeconstterm_" + boost::lexical_cast<std::string>(it->first); - double v = m_cache_loglike_constterm.find(it->first)->second; - config.set(str2, v); - ++i; - } - config.setArray("a_indices", a_indices); - } -} - -void bob::learn::em::PLDAMachine::setPLDABase(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base) -{ - m_plda_base = plda_base; - m_weighted_sum.resizeAndPreserve(getDimF()); - clearMaps(); - resizeTmp(); -} - - -void bob::learn::em::PLDAMachine::setWeightedSum(const blitz::Array<double,1>& ws) -{ - if(ws.extent(0) != m_weighted_sum.extent(0)) { - boost::format m("size of parameter `ws' (%d) does not match the expected size (%d)"); - m % ws.extent(0) % m_weighted_sum.extent(0); - throw std::runtime_error(m.str()); - } - m_weighted_sum.reference(bob::core::array::ccopy(ws)); -} - -const blitz::Array<double,2>& bob::learn::em::PLDAMachine::getGamma(const size_t a) const -{ - // Checks in both base machine and this machine - if (m_plda_base->hasGamma(a)) return m_plda_base->getGamma(a); - else if (!hasGamma(a)) - throw std::runtime_error("Gamma for this number of samples is not currently in cache. You could use the getAddGamma() method instead"); - return (m_cache_gamma.find(a))->second; -} - -const blitz::Array<double,2>& bob::learn::em::PLDAMachine::getAddGamma(const size_t a) -{ - if (m_plda_base->hasGamma(a)) return m_plda_base->getGamma(a); - else if (hasGamma(a)) return m_cache_gamma[a]; - // else computes it and adds it to this machine - blitz::Array<double,2> gamma_a(getDimF(),getDimF()); - m_cache_gamma[a].reference(gamma_a); - m_plda_base->computeGamma(a, gamma_a); - return m_cache_gamma[a]; -} - -double bob::learn::em::PLDAMachine::getLogLikeConstTerm(const size_t a) const -{ - // Checks in both base machine and this machine - if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - if (m_plda_base->hasLogLikeConstTerm(a)) return m_plda_base->getLogLikeConstTerm(a); - else if (!hasLogLikeConstTerm(a)) - throw std::runtime_error("The LogLikelihood constant term for this number of samples is not currently in cache. You could use the getAddLogLikeConstTerm() method instead"); - return (m_cache_loglike_constterm.find(a))->second; -} - -double bob::learn::em::PLDAMachine::getAddLogLikeConstTerm(const size_t a) -{ - if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - if (m_plda_base->hasLogLikeConstTerm(a)) return m_plda_base->getLogLikeConstTerm(a); - else if (hasLogLikeConstTerm(a)) return m_cache_loglike_constterm[a]; - // else computes it and adds it to this machine - m_cache_loglike_constterm[a] = - m_plda_base->computeLogLikeConstTerm(a, getAddGamma(a)); - return m_cache_loglike_constterm[a]; -} - -void bob::learn::em::PLDAMachine::clearMaps() -{ - m_cache_gamma.clear(); - m_cache_loglike_constterm.clear(); -} - -double bob::learn::em::PLDAMachine::forward(const blitz::Array<double,1>& sample) -{ - return forward_(sample); -} - -double bob::learn::em::PLDAMachine::forward_(const blitz::Array<double,1>& sample) -{ - // Computes the log likelihood ratio - return computeLogLikelihood(sample, true) - // match - (computeLogLikelihood(sample, false) + m_loglikelihood); // no match -} - -double bob::learn::em::PLDAMachine::forward(const blitz::Array<double,2>& samples) -{ - // Computes the log likelihood ratio - return computeLogLikelihood(samples, true) - // match - (computeLogLikelihood(samples, false) + m_loglikelihood); // no match -} - -double bob::learn::em::PLDAMachine::computeLogLikelihood(const blitz::Array<double,1>& sample, - bool enroll) const -{ - if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - // Check dimensionality - bob::core::array::assertSameDimensionLength(sample.extent(0), getDimD()); - - int n_samples = 1 + (enroll?m_n_samples:0); - - // 3/ Third term of the likelihood: -1/2*X^T*(SIGMA+A.A^T)^-1*X - // Efficient way: -1/2*sum_i(xi^T.sigma^-1.xi - xi^T.sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1.xi - // -1/2*sumWeighted^T*(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1).F)^-1*sumWeighted - // where sumWeighted = sum_i(F^T*(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1)*xi) - const blitz::Array<double,2>& beta = getPLDABase()->getBeta(); - const blitz::Array<double,2>& Ft_beta = getPLDABase()->getFtBeta(); - const blitz::Array<double,1>& mu = getPLDABase()->getMu(); - double terma = (enroll?m_nh_sum_xit_beta_xi:0.); - // sumWeighted - if (enroll && m_n_samples > 0) m_tmp_nf_1 = m_weighted_sum; - else m_tmp_nf_1 = 0; - - // terma += -1 / 2. * (xi^t*beta*xi) - m_tmp_d_1 = sample - mu; - bob::math::prod(beta, m_tmp_d_1, m_tmp_d_2); - terma += -1 / 2. * (blitz::sum(m_tmp_d_1*m_tmp_d_2)); - - // sumWeighted - bob::math::prod(Ft_beta, m_tmp_d_1, m_tmp_nf_2); - m_tmp_nf_1 += m_tmp_nf_2; - blitz::Array<double,2> gamma_a; - if (hasGamma(n_samples) || m_plda_base->hasGamma(n_samples)) - gamma_a.reference(getGamma(n_samples)); - else - { - gamma_a.reference(m_tmp_nf_nf_1); - m_plda_base->computeGamma(n_samples, gamma_a); - } - bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2); - double termb = 1 / 2. * (blitz::sum(m_tmp_nf_1*m_tmp_nf_2)); - - // 1/2/ Constant term of the log likelihood: - // 1/ First term of the likelihood: -Nsamples*D/2*log(2*PI) - // 2/ Second term of the likelihood: -1/2*log(det(SIGMA+A.A^T)) - // Efficient way: -Nsamples/2*log(det(sigma))-Nsamples/2*log(det(I+G^T.sigma^-1.G)) - // -1/2*log(det(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)*G^T*sigma^-1).F)) - double log_likelihood; // = getAddLogLikeConstTerm(static_cast<size_t>(n_samples)); - if (hasLogLikeConstTerm(n_samples) || m_plda_base->hasLogLikeConstTerm(n_samples)) - log_likelihood = getLogLikeConstTerm(n_samples); - else - log_likelihood = m_plda_base->computeLogLikeConstTerm(n_samples, gamma_a); - - log_likelihood += terma + termb; - return log_likelihood; -} - -double bob::learn::em::PLDAMachine::computeLogLikelihood(const blitz::Array<double,2>& samples, - bool enroll) const -{ - if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - // Check dimensionality - bob::core::array::assertSameDimensionLength(samples.extent(1), getDimD()); - - int n_samples = samples.extent(0) + (enroll?m_n_samples:0); - // 3/ Third term of the likelihood: -1/2*X^T*(SIGMA+A.A^T)^-1*X - // Efficient way: -1/2*sum_i(xi^T.sigma^-1.xi - xi^T.sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1.xi - // -1/2*sumWeighted^T*(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1).F)^-1*sumWeighted - // where sumWeighted = sum_i(F^T*(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)^-1*G^T*sigma^-1)*xi) - const blitz::Array<double,2>& beta = getPLDABase()->getBeta(); - const blitz::Array<double,2>& Ft_beta = getPLDABase()->getFtBeta(); - const blitz::Array<double,1>& mu = getPLDABase()->getMu(); - double terma = (enroll?m_nh_sum_xit_beta_xi:0.); - // sumWeighted - if (enroll && m_n_samples > 0) m_tmp_nf_1 = m_weighted_sum; - else m_tmp_nf_1 = 0; - for (int k=0; k<samples.extent(0); ++k) - { - blitz::Array<double,1> samp = samples(k,blitz::Range::all()); - m_tmp_d_1 = samp - mu; - // terma += -1 / 2. * (xi^t*beta*xi) - bob::math::prod(beta, m_tmp_d_1, m_tmp_d_2); - terma += -1 / 2. * (blitz::sum(m_tmp_d_1*m_tmp_d_2)); - - // sumWeighted - bob::math::prod(Ft_beta, m_tmp_d_1, m_tmp_nf_2); - m_tmp_nf_1 += m_tmp_nf_2; - } - - blitz::Array<double,2> gamma_a; - if (hasGamma(n_samples) || m_plda_base->hasGamma(n_samples)) - gamma_a.reference(getGamma(n_samples)); - else - { - gamma_a.reference(m_tmp_nf_nf_1); - m_plda_base->computeGamma(n_samples, gamma_a); - } - bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2); - double termb = 1 / 2. * (blitz::sum(m_tmp_nf_1*m_tmp_nf_2)); - - // 1/2/ Constant term of the log likelihood: - // 1/ First term of the likelihood: -Nsamples*D/2*log(2*PI) - // 2/ Second term of the likelihood: -1/2*log(det(SIGMA+A.A^T)) - // Efficient way: -Nsamples/2*log(det(sigma))-Nsamples/2*log(det(I+G^T.sigma^-1.G)) - // -1/2*log(det(I+aF^T.(sigma^-1-sigma^-1*G*(I+G^T.sigma^-1.G)*G^T*sigma^-1).F)) - double log_likelihood; // = getAddLogLikeConstTerm(static_cast<size_t>(n_samples)); - if (hasLogLikeConstTerm(n_samples) || m_plda_base->hasLogLikeConstTerm(n_samples)) - log_likelihood = getLogLikeConstTerm(n_samples); - else - log_likelihood = m_plda_base->computeLogLikeConstTerm(n_samples, gamma_a); - - log_likelihood += terma + termb; - return log_likelihood; -} - -void bob::learn::em::PLDAMachine::resize(const size_t dim_d, const size_t dim_f, - const size_t dim_g) -{ - m_weighted_sum.resizeAndPreserve(dim_f); - clearMaps(); - resizeTmp(); -} - -void bob::learn::em::PLDAMachine::resizeTmp() -{ - if (m_plda_base) - { - m_tmp_d_1.resize(getDimD()); - m_tmp_d_2.resize(getDimD()); - m_tmp_nf_1.resize(getDimF()); - m_tmp_nf_2.resize(getDimF()); - m_tmp_nf_nf_1.resize(getDimF(), getDimF()); - } -} diff --git a/bob/learn/em/cpp/PLDATrainer.cpp b/bob/learn/em/cpp/PLDATrainer.cpp deleted file mode 100644 index 323ea6c9420c808d122c734d1d8537ea9d0666fc..0000000000000000000000000000000000000000 --- a/bob/learn/em/cpp/PLDATrainer.cpp +++ /dev/null @@ -1,800 +0,0 @@ -/** - * @date Fri Oct 14 18:07:56 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Probabilistic Linear Discriminant Analysis - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - - -#include <bob.learn.em/PLDATrainer.h> -#include <bob.core/check.h> -#include <bob.core/array_copy.h> -#include <bob.core/array_random.h> -#include <bob.math/inv.h> -#include <bob.math/svd.h> -#include <bob.core/check.h> -#include <bob.core/array_repmat.h> -#include <algorithm> -#include <limits> -#include <vector> - -#include <bob.math/linear.h> -#include <bob.math/linsolve.h> - - - -bob::learn::em::PLDATrainer::PLDATrainer(const bool use_sum_second_order): - m_rng(new boost::mt19937()), - m_dim_d(0), m_dim_f(0), m_dim_g(0), - m_use_sum_second_order(use_sum_second_order), - m_initF_method(bob::learn::em::PLDATrainer::RANDOM_F), m_initF_ratio(1.), - m_initG_method(bob::learn::em::PLDATrainer::RANDOM_G), m_initG_ratio(1.), - m_initSigma_method(bob::learn::em::PLDATrainer::RANDOM_SIGMA), - m_initSigma_ratio(1.), - m_cache_S(0,0), - m_cache_z_first_order(0), m_cache_sum_z_second_order(0,0), m_cache_z_second_order(0), - m_cache_n_samples_per_id(0), m_cache_n_samples_in_training(), m_cache_B(0,0), - m_cache_Ft_isigma_G(0,0), m_cache_eta(0,0), m_cache_zeta(), m_cache_iota(), - m_tmp_nf_1(0), m_tmp_nf_2(0), m_tmp_ng_1(0), - m_tmp_D_1(0), m_tmp_D_2(0), - m_tmp_nfng_nfng(0,0), m_tmp_D_nfng_1(0,0), m_tmp_D_nfng_2(0,0) -{ -} - -bob::learn::em::PLDATrainer::PLDATrainer(const bob::learn::em::PLDATrainer& other): - m_rng(other.m_rng), - m_dim_d(other.m_dim_d), m_dim_f(other.m_dim_f), m_dim_g(other.m_dim_g), - m_use_sum_second_order(other.m_use_sum_second_order), - m_initF_method(other.m_initF_method), m_initF_ratio(other.m_initF_ratio), - m_initG_method(other.m_initG_method), m_initG_ratio(other.m_initG_ratio), - m_initSigma_method(other.m_initSigma_method), m_initSigma_ratio(other.m_initSigma_ratio), - m_cache_S(bob::core::array::ccopy(other.m_cache_S)), - m_cache_z_first_order(), - m_cache_sum_z_second_order(bob::core::array::ccopy(other.m_cache_sum_z_second_order)), - m_cache_z_second_order(), - m_cache_n_samples_per_id(other.m_cache_n_samples_per_id), - m_cache_n_samples_in_training(other.m_cache_n_samples_in_training), - m_cache_B(bob::core::array::ccopy(other.m_cache_B)), - m_cache_Ft_isigma_G(bob::core::array::ccopy(other.m_cache_Ft_isigma_G)), - m_cache_eta(bob::core::array::ccopy(other.m_cache_eta)) -{ - bob::core::array::ccopy(other.m_cache_z_first_order, m_cache_z_first_order); - bob::core::array::ccopy(other.m_cache_z_second_order, m_cache_z_second_order); - bob::core::array::ccopy(other.m_cache_zeta, m_cache_zeta); - bob::core::array::ccopy(other.m_cache_iota, m_cache_iota); - // Resize working arrays - resizeTmp(); -} - -bob::learn::em::PLDATrainer::~PLDATrainer() {} - -bob::learn::em::PLDATrainer& bob::learn::em::PLDATrainer::operator= -(const bob::learn::em::PLDATrainer& other) -{ - if(this != &other) - { - m_rng = m_rng, - m_dim_d = other.m_dim_d; - m_dim_f = other.m_dim_f; - m_dim_g = other.m_dim_g; - m_use_sum_second_order = other.m_use_sum_second_order; - m_initF_method = other.m_initF_method; - m_initF_ratio = other.m_initF_ratio; - m_initG_method = other.m_initG_method; - m_initG_ratio = other.m_initG_ratio; - m_initSigma_method = other.m_initSigma_method; - m_initSigma_ratio = other.m_initSigma_ratio; - m_cache_S = bob::core::array::ccopy(other.m_cache_S); - bob::core::array::ccopy(other.m_cache_z_first_order, m_cache_z_first_order); - m_cache_sum_z_second_order = bob::core::array::ccopy(other.m_cache_sum_z_second_order); - bob::core::array::ccopy(other.m_cache_z_second_order, m_cache_z_second_order); - m_cache_n_samples_per_id = other.m_cache_n_samples_per_id; - m_cache_n_samples_in_training = other.m_cache_n_samples_in_training; - m_cache_B = bob::core::array::ccopy(other.m_cache_B); - m_cache_Ft_isigma_G = bob::core::array::ccopy(other.m_cache_Ft_isigma_G); - m_cache_eta = bob::core::array::ccopy(other.m_cache_eta); - bob::core::array::ccopy(other.m_cache_iota, m_cache_iota); - // Resize working arrays - resizeTmp(); - } - return *this; -} - -bool bob::learn::em::PLDATrainer::operator== - (const bob::learn::em::PLDATrainer& other) const -{ - return m_rng == m_rng && - m_dim_d == other.m_dim_d && - m_dim_f == other.m_dim_f && - m_dim_g == other.m_dim_g && - m_initF_method == other.m_initF_method && - m_initF_ratio == other.m_initF_ratio && - m_initG_method == other.m_initG_method && - m_initG_ratio == other.m_initG_ratio && - m_initSigma_method == other.m_initSigma_method && - m_initSigma_ratio == other.m_initSigma_ratio && - bob::core::array::isEqual(m_cache_S, m_cache_S) && - bob::core::array::isEqual(m_cache_z_first_order, other.m_cache_z_first_order) && - bob::core::array::isEqual(m_cache_sum_z_second_order, other.m_cache_sum_z_second_order) && - bob::core::array::isEqual(m_cache_z_second_order, other.m_cache_z_second_order) && - m_cache_n_samples_per_id.size() == m_cache_n_samples_per_id.size() && - std::equal(m_cache_n_samples_per_id.begin(), m_cache_n_samples_per_id.end(), other.m_cache_n_samples_per_id.begin()) && - m_cache_n_samples_in_training.size() == m_cache_n_samples_in_training.size() && - std::equal(m_cache_n_samples_in_training.begin(), m_cache_n_samples_in_training.end(), other.m_cache_n_samples_in_training.begin()) && - bob::core::array::isEqual(m_cache_B, other.m_cache_B) && - bob::core::array::isEqual(m_cache_Ft_isigma_G, other.m_cache_Ft_isigma_G) && - bob::core::array::isEqual(m_cache_eta, other.m_cache_eta) && - bob::core::array::isEqual(m_cache_zeta, other.m_cache_zeta) && - bob::core::array::isEqual(m_cache_iota, other.m_cache_iota); -} - -bool bob::learn::em::PLDATrainer::operator!= - (const bob::learn::em::PLDATrainer &other) const -{ - return !(this->operator==(other)); -} - -bool bob::learn::em::PLDATrainer::is_similar_to - (const bob::learn::em::PLDATrainer &other, const double r_epsilon, - const double a_epsilon) const -{ - return m_rng == m_rng && - m_dim_d == other.m_dim_d && - m_dim_f == other.m_dim_f && - m_dim_g == other.m_dim_g && - m_use_sum_second_order == other.m_use_sum_second_order && - m_initF_method == other.m_initF_method && - bob::core::isClose(m_initF_ratio, other.m_initF_ratio, r_epsilon, a_epsilon) && - m_initG_method == other.m_initG_method && - bob::core::isClose(m_initG_ratio, other.m_initG_ratio, r_epsilon, a_epsilon) && - m_initSigma_method == other.m_initSigma_method && - bob::core::isClose(m_initSigma_ratio, other.m_initSigma_ratio, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_S, m_cache_S, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_z_first_order, other.m_cache_z_first_order, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_sum_z_second_order, other.m_cache_sum_z_second_order, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_z_second_order, other.m_cache_z_second_order, r_epsilon, a_epsilon) && - m_cache_n_samples_per_id.size() == m_cache_n_samples_per_id.size() && - std::equal(m_cache_n_samples_per_id.begin(), m_cache_n_samples_per_id.end(), other.m_cache_n_samples_per_id.begin()) && - m_cache_n_samples_in_training.size() == m_cache_n_samples_in_training.size() && - std::equal(m_cache_n_samples_in_training.begin(), m_cache_n_samples_in_training.end(), other.m_cache_n_samples_in_training.begin()) && - bob::core::array::isClose(m_cache_B, other.m_cache_B, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_Ft_isigma_G, other.m_cache_Ft_isigma_G, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_eta, other.m_cache_eta, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_zeta, other.m_cache_zeta, r_epsilon, a_epsilon) && - bob::core::array::isClose(m_cache_iota, other.m_cache_iota, r_epsilon, a_epsilon); -} - -void bob::learn::em::PLDATrainer::initialize(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Checks training data - checkTrainingData(v_ar); - - // Gets dimension (first Arrayset) - size_t n_features = v_ar[0].extent(1); - m_dim_d = machine.getDimD(); - // Get dimensionalities from the PLDABase - bob::core::array::assertSameDimensionLength(n_features, m_dim_d); - m_dim_f = machine.getDimF(); - m_dim_g = machine.getDimG(); - - // Reinitializes array members - initMembers(v_ar); - - // Computes the mean and the covariance if required - computeMeanVariance(machine, v_ar); - - // Initialization (e.g. using scatter) - initFGSigma(machine, v_ar); -} - -void bob::learn::em::PLDATrainer::finalize(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Precomputes constant parts of the log likelihood and (gamma_a) - precomputeLogLike(machine, v_ar); - // Adds the case 1 sample if not already done (always used for scoring) - machine.getAddGamma(1); - machine.getAddLogLikeConstTerm(1); -} - -void bob::learn::em::PLDATrainer::checkTrainingData(const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Checks that the vector of Arraysets is not empty - if (v_ar.size() == 0) { - throw std::runtime_error("input training set is empty"); - } - - // Gets dimension (first Arrayset) - int n_features = v_ar[0].extent(1); - // Checks dimension consistency - for (size_t i=0; i<v_ar.size(); ++i) { - if (v_ar[i].extent(1) != n_features) { - boost::format m("number of features (columns) of array for class %u (%d) does not match that of array for class 0 (%d)"); - m % i % v_ar[0].extent(1) % n_features; - throw std::runtime_error(m.str()); - } - } -} - -void bob::learn::em::PLDATrainer::initMembers(const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Gets dimension (first Arrayset) - const size_t n_features = v_ar[0].extent(1); // dimensionality of the data - const size_t n_identities = v_ar.size(); - - m_cache_S.resize(n_features, n_features); - m_cache_sum_z_second_order.resize(m_dim_f+m_dim_g, m_dim_f+m_dim_g); - - // Loops over the identities - for (size_t i=0; i<n_identities; ++i) - { - // Number of training samples for this identity - const size_t n_i = v_ar[i].extent(0); - // m_cache_z_first_order - blitz::Array<double,2> z_i(n_i, m_dim_f+m_dim_g); - m_cache_z_first_order.push_back(z_i); - // m_z_second_order - if (!m_use_sum_second_order) - { - blitz::Array<double,3> z2_i(n_i, m_dim_f+m_dim_g, m_dim_f+m_dim_g); - m_cache_z_second_order.push_back(z2_i); - } - - // m_cache_n_samples_per_id - m_cache_n_samples_per_id.push_back(n_i); - - // Maps dependent on the number of samples per identity - std::map<size_t,bool>::iterator it; - it = m_cache_n_samples_in_training.find(n_i); - if (it == m_cache_n_samples_in_training.end()) - { - // Indicates if there are identities with n_i training samples and if - // corresponding matrices are up to date. - m_cache_n_samples_in_training[n_i] = false; - // Allocates arrays for identities with n_i training samples - m_cache_zeta[n_i].reference(blitz::Array<double,2>(m_dim_g, m_dim_g)); - m_cache_iota[n_i].reference(blitz::Array<double,2>(m_dim_f, m_dim_g)); - } - } - - m_cache_B.resize(n_features, m_dim_f+m_dim_g); - m_cache_Ft_isigma_G.resize(m_dim_f, m_dim_g); - m_cache_eta.resize(m_dim_f, m_dim_g); - - // Working arrays - resizeTmp(); -} - -void bob::learn::em::PLDATrainer::resizeTmp() -{ - m_tmp_nf_1.resize(m_dim_f); - m_tmp_nf_2.resize(m_dim_f); - m_tmp_ng_1.resize(m_dim_g); - m_tmp_D_1.resize(m_dim_d); - m_tmp_D_2.resize(m_dim_d); - m_tmp_nfng_nfng.resize(m_dim_f+m_dim_g, m_dim_f+m_dim_g); - m_tmp_D_nfng_1.resize(m_dim_d, m_dim_f+m_dim_g); - m_tmp_D_nfng_2.resize(m_dim_d, m_dim_f+m_dim_g); -} - -void bob::learn::em::PLDATrainer::computeMeanVariance(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - blitz::Array<double,1>& mu = machine.updateMu(); - blitz::Range all = blitz::Range::all(); - // TODO: Uncomment variance computation if required - /* if(m_compute_likelihood) - { - // loads all the data in a single shot - required for scatter - blitz::Array<double,2> data(n_features, n_samples); - for (size_t i=0; i<n_samples; ++i) - data(all,i) = ar(i,all); - // Mean and scatter computation - bob::math::scatter(data, m_cache_S, mu); - // divides scatter by N-1 - m_cache_S /= static_cast<double>(n_samples-1); - } - else */ - { - // Computes the mean and updates mu - mu = 0.; - size_t n_samples = 0; - for (size_t j=0; j<v_ar.size(); ++j) { - n_samples += v_ar[j].extent(0); - for (int i=0; i<v_ar[j].extent(0); ++i) - mu += v_ar[j](i,all); - } - mu /= static_cast<double>(n_samples); - m_cache_S = 0.; - } -} - -void bob::learn::em::PLDATrainer::initFGSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Initializes F, G and sigma - initF(machine, v_ar); - initG(machine, v_ar); - initSigma(machine, v_ar); - - // Precomputes values using new F, G and sigma - machine.precompute(); -} - -void bob::learn::em::PLDATrainer::initF(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - blitz::Array<double,2>& F = machine.updateF(); - blitz::Range a = blitz::Range::all(); - - // 1: between-class scatter - if (m_initF_method == bob::learn::em::PLDATrainer::BETWEEN_SCATTER) - { - if (machine.getDimF() > v_ar.size()) { - boost::format m("The rank of the matrix F ('%ld') can't be larger than the number of classes in the training set ('%ld')"); - m % machine.getDimF() % v_ar.size(); - throw std::runtime_error(m.str()); - } - - // a/ Computes between-class scatter matrix - blitz::firstIndex bi; - blitz::secondIndex bj; - blitz::Array<double,2> S(machine.getDimD(), v_ar.size()); - S = 0.; - m_tmp_D_1 = 0.; - for (size_t i=0; i<v_ar.size(); ++i) - { - blitz::Array<double,1> Si = S(blitz::Range::all(),i); - Si = 0.; - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // Si += x_ij - Si += v_ar[i](j,a); - } - // Si = mean of the samples class i - Si /= static_cast<double>(v_ar[i].extent(0)); - m_tmp_D_1 += Si; - } - m_tmp_D_1 /= static_cast<double>(v_ar.size()); - - // b/ Removes the mean - S = S(bi,bj) - m_tmp_D_1(bi); - - // c/ SVD of the between-class scatter matrix - const size_t n_singular = std::min(machine.getDimD(),v_ar.size()); - blitz::Array<double,2> U(machine.getDimD(), n_singular); - blitz::Array<double,1> sigma(n_singular); - bob::math::svd(S, U, sigma); - - // d/ Updates F - blitz::Array<double,2> Uslice = U(a, blitz::Range(0,m_dim_f-1)); - blitz::Array<double,1> sigma_slice = sigma(blitz::Range(0,m_dim_f-1)); - sigma_slice = blitz::sqrt(sigma_slice); - F = Uslice(bi,bj) / sigma_slice(bj); - } - // otherwise: random initialization - else { - // F initialization - bob::core::array::randn(*m_rng, F); - F *= m_initF_ratio; - } -} - -void bob::learn::em::PLDATrainer::initG(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - blitz::Array<double,2>& G = machine.updateG(); - blitz::Range a = blitz::Range::all(); - - // 1: within-class scatter - if (m_initG_method == bob::learn::em::PLDATrainer::WITHIN_SCATTER) - { - // a/ Computes within-class scatter matrix - blitz::firstIndex bi; - blitz::secondIndex bj; - size_t Nsamples=0; - for (size_t i=0; i<v_ar.size(); ++i) - Nsamples += v_ar[i].extent(0); - - blitz::Array<double,2> S(machine.getDimD(), Nsamples); - S = 0.; - m_tmp_D_1 = 0.; - int counter = 0; - for (size_t i=0; i<v_ar.size(); ++i) - { - // Computes the mean of the samples class i - m_tmp_D_2 = 0.; - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // m_tmp_D_2 += x_ij - m_tmp_D_2 += v_ar[i](j,a); - } - // m_tmp_D_2 = mean of the samples class i - m_tmp_D_2 /= static_cast<double>(v_ar[i].extent(0)); - - // Generates the scatter - for (int j=0; j<v_ar[i].extent(0); ++j) - { - blitz::Array<double,1> Si = S(a, counter); - // Si = x_ij - mean_i - Si = v_ar[i](j,a) - m_tmp_D_2; - // mean of the within class - m_tmp_D_1 += Si; - ++counter; - } - } - m_tmp_D_1 /= static_cast<double>(Nsamples); - - // b/ Removes the mean - S = S(bi,bj) - m_tmp_D_1(bi); - - // c/ SVD of the between-class scatter matrix - blitz::Array<double,2> U(m_dim_d, std::min(m_dim_d, Nsamples)); - blitz::Array<double,1> sigma(std::min(m_dim_d, Nsamples)); - bob::math::svd(S, U, sigma); - - // d/ Updates G - blitz::Array<double,2> Uslice = U(blitz::Range::all(), blitz::Range(0,m_dim_g-1)); - blitz::Array<double,1> sigma_slice = sigma(blitz::Range(0,m_dim_g-1)); - sigma_slice = blitz::sqrt(sigma_slice); - G = Uslice(bi,bj) / sigma_slice(bj); - } - // otherwise: random initialization - else { - // G initialization - bob::core::array::randn(*m_rng, G); - G *= m_initG_ratio; - } -} - -void bob::learn::em::PLDATrainer::initSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - blitz::Array<double,1>& sigma = machine.updateSigma(); - blitz::Range a = blitz::Range::all(); - - // 1: percentage of the variance of G - if (m_initSigma_method == bob::learn::em::PLDATrainer::VARIANCE_G) { - const blitz::Array<double,2>& G = machine.getG(); - blitz::secondIndex bj; - m_tmp_D_1 = blitz::mean(G, bj); - // Updates sigma - sigma = blitz::fabs(m_tmp_D_1) * m_initSigma_ratio; - } - // 2: constant value - else if (m_initSigma_method == bob::learn::em::PLDATrainer::CONSTANT) { - sigma = m_initSigma_ratio; - } - // 3: percentage of the variance of the data - else if (m_initSigma_method == bob::learn::em::PLDATrainer::VARIANCE_DATA) { - // a/ Computes the global mean - // m_tmp_D_1 = 1/N sum_i x_i - m_tmp_D_1 = 0.; - size_t Ns = 0; - for (size_t i=0; i<v_ar.size(); ++i) - { - for (int j=0; j<v_ar[i].extent(0); ++j) - m_tmp_D_1 += v_ar[i](j,a); - Ns += v_ar[i].extent(0); - } - m_tmp_D_1 /= static_cast<double>(Ns); - - // b/ Computes the variance: - m_tmp_D_2 = 0.; - for (size_t i=0; i<v_ar.size(); ++i) - for (int j=0; j<v_ar[i].extent(0); ++j) - m_tmp_D_2 += blitz::pow2(v_ar[i](j,a) - m_tmp_D_1); - sigma = m_initSigma_ratio * m_tmp_D_2 / static_cast<double>(Ns-1); - } - // otherwise: random initialization - else { - // sigma initialization - bob::core::array::randn(*m_rng, sigma); - sigma = blitz::fabs(sigma) * m_initSigma_ratio; - } - // Apply variance threshold - machine.applyVarianceThreshold(); -} - -void bob::learn::em::PLDATrainer::eStep(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Precomputes useful variables using current estimates of F,G, and sigma - precomputeFromFGSigma(machine); - // Gets the mean mu from the machine - const blitz::Array<double,1>& mu = machine.getMu(); - const blitz::Array<double,2>& alpha = machine.getAlpha(); - const blitz::Array<double,2>& F = machine.getF(); - const blitz::Array<double,2>& FtBeta = machine.getFtBeta(); - const blitz::Array<double,2>& GtISigma = machine.getGtISigma(); - blitz::Range a = blitz::Range::all(); - - // blitz indices - blitz::firstIndex bi; - blitz::secondIndex bj; - // Initializes sum of z second order statistics to 0 - m_cache_sum_z_second_order = 0.; - for (size_t i=0; i<v_ar.size(); ++i) - { - // Computes expectation of z_ij = [h_i w_ij] - // 1/a/ Computes expectation of h_i - // Loop over the samples - m_tmp_nf_1 = 0.; - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // m_tmp_D_1 = x_sj-mu - m_tmp_D_1 = v_ar[i](j,a) - mu; - - // m_tmp_nf_2 = F^T.beta.(x_sj-mu) - bob::math::prod(FtBeta, m_tmp_D_1, m_tmp_nf_2); - // m_tmp_nf_1 = sum_j F^T.beta.(x_sj-mu) - m_tmp_nf_1 += m_tmp_nf_2; - } - const blitz::Array<double,2>& gamma_a = machine.getAddGamma(v_ar[i].extent(0)); - blitz::Range r_hi(0, m_dim_f-1); - // m_tmp_nf_2 = E(h_i) = gamma_A sum_j F^T.beta.(x_sj-mu) - bob::math::prod(gamma_a, m_tmp_nf_1, m_tmp_nf_2); - - // 1/b/ Precomputes: m_tmp_D_2 = F.E{h_i} - bob::math::prod(F, m_tmp_nf_2, m_tmp_D_2); - - // 2/ First and second order statistics of z - // Precomputed values - blitz::Array<double,2>& zeta_a = m_cache_zeta[v_ar[i].extent(0)]; - blitz::Array<double,2>& iota_a = m_cache_iota[v_ar[i].extent(0)]; - blitz::Array<double,2> iotat_a = iota_a.transpose(1,0); - - // Extracts statistics of z_ij = [h_i w_ij] from y_i = [h_i w_i1 ... w_iJ] - blitz::Range r1(0, m_dim_f-1); - blitz::Range r2(m_dim_f, m_dim_f+m_dim_g-1); - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // 1/ First order statistics of z - blitz::Array<double,1> z_first_order_ij_1 = m_cache_z_first_order[i](j,r1); - z_first_order_ij_1 = m_tmp_nf_2; // E{h_i} - // m_tmp_D_1 = x_sj - mu - F.E{h_i} - m_tmp_D_1 = v_ar[i](j,a) - mu - m_tmp_D_2; - // m_tmp_ng_1 = G^T.sigma^-1.(x_sj-mu-fhi) - bob::math::prod(GtISigma, m_tmp_D_1, m_tmp_ng_1); - // z_first_order_ij_2 = (Id+G^T.sigma^-1.G)^-1.G^T.sigma^-1.(x_sj-mu) = E{w_ij} - blitz::Array<double,1> z_first_order_ij_2 = m_cache_z_first_order[i](j,r2); - bob::math::prod(alpha, m_tmp_ng_1, z_first_order_ij_2); - - // 2/ Second order statistics of z - blitz::Array<double,2> z_sum_so_11 = m_cache_sum_z_second_order(r1,r1); - blitz::Array<double,2> z_sum_so_12 = m_cache_sum_z_second_order(r1,r2); - blitz::Array<double,2> z_sum_so_21 = m_cache_sum_z_second_order(r2,r1); - blitz::Array<double,2> z_sum_so_22 = m_cache_sum_z_second_order(r2,r2); - if (m_use_sum_second_order) - { - z_sum_so_11 += gamma_a + z_first_order_ij_1(bi) * z_first_order_ij_1(bj); - z_sum_so_12 += iota_a + z_first_order_ij_1(bi) * z_first_order_ij_2(bj); - z_sum_so_21 += iotat_a + z_first_order_ij_2(bi) * z_first_order_ij_1(bj); - z_sum_so_22 += zeta_a + z_first_order_ij_2(bi) * z_first_order_ij_2(bj); - } - else - { - blitz::Array<double,2> z_so_11 = m_cache_z_second_order[i](j,r1,r1); - z_so_11 = gamma_a + z_first_order_ij_1(bi) * z_first_order_ij_1(bj); - z_sum_so_11 += z_so_11; - blitz::Array<double,2> z_so_12 = m_cache_z_second_order[i](j,r1,r2); - z_so_12 = iota_a + z_first_order_ij_1(bi) * z_first_order_ij_2(bj); - z_sum_so_12 += z_so_12; - blitz::Array<double,2> z_so_21 = m_cache_z_second_order[i](j,r2,r1); - z_so_21 = iotat_a + z_first_order_ij_2(bi) * z_first_order_ij_1(bj); - z_sum_so_21 += z_so_21; - blitz::Array<double,2> z_so_22 = m_cache_z_second_order[i](j,r2,r2); - z_so_22 = zeta_a + z_first_order_ij_2(bi) * z_first_order_ij_2(bj); - z_sum_so_22 += z_so_22; - } - } - } -} - -void bob::learn::em::PLDATrainer::precomputeFromFGSigma(bob::learn::em::PLDABase& machine) -{ - // Blitz compatibility: ugly fix (const_cast, as old blitz version does not - // provide a non-const version of transpose()) - const blitz::Array<double,2>& F = machine.getF(); - const blitz::Array<double,2> Ft = const_cast<blitz::Array<double,2>&>(F).transpose(1,0); - const blitz::Array<double,2>& Gt_isigma = machine.getGtISigma(); - const blitz::Array<double,2> Gt_isigma_t = const_cast<blitz::Array<double,2>&>(Gt_isigma).transpose(1,0); - const blitz::Array<double,2>& alpha = machine.getAlpha(); - - // Precomputes F, G and sigma-based expressions - bob::math::prod(Ft, Gt_isigma_t, m_cache_Ft_isigma_G); - bob::math::prod(m_cache_Ft_isigma_G, alpha, m_cache_eta); - blitz::Array<double,2> etat = m_cache_eta.transpose(1,0); - - // Reinitializes all the zeta_a and iota_a - std::map<size_t,bool>::iterator it; - for (it=m_cache_n_samples_in_training.begin(); it!=m_cache_n_samples_in_training.end(); - ++it) - it->second = false; - - for (it=m_cache_n_samples_in_training.begin(); it!=m_cache_n_samples_in_training.end(); - ++it) - { - size_t n_i = it->first; - // Precomputes zeta and iota for identities with q_i training samples, - // if not already done - if (!it->second) - { - const blitz::Array<double,2>& gamma_a = machine.getAddGamma(n_i); - blitz::Array<double,2>& zeta_a = m_cache_zeta[n_i]; - blitz::Array<double,2>& iota_a = m_cache_iota[n_i]; - bob::math::prod(gamma_a, m_cache_eta, iota_a); - bob::math::prod(etat, iota_a, zeta_a); - zeta_a += alpha; - iota_a = - iota_a; - // Now up to date - it->second = true; - } - } -} - -void bob::learn::em::PLDATrainer::precomputeLogLike(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // Precomputes the log determinant of alpha and sigma - machine.precomputeLogLike(); - - // Precomputes the log likelihood constant term - std::map<size_t,bool>::iterator it; - for (it=m_cache_n_samples_in_training.begin(); - it!=m_cache_n_samples_in_training.end(); ++it) - { - // Precomputes the log likelihood constant term for identities with q_i - // training samples, if not already done - machine.getAddLogLikeConstTerm(it->first); - } -} - - -void bob::learn::em::PLDATrainer::mStep(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - // 1/ New estimate of B = {F G} - updateFG(machine, v_ar); - - // 2/ New estimate of Sigma - updateSigma(machine, v_ar); - - // 3/ Precomputes new values after updating F, G and sigma - machine.precompute(); - // Precomputes useful variables using current estimates of F,G, and sigma - precomputeFromFGSigma(machine); -} - -void bob::learn::em::PLDATrainer::updateFG(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - /// Computes the B matrix (B = [F G]) - /// B = (sum_ij (x_ij-mu).E{z_i}^T).(sum_ij E{z_i.z_i^T})^-1 - - // 1/ Computes the numerator (sum_ij (x_ij-mu).E{z_i}^T) - // Gets the mean mu from the machine - const blitz::Array<double,1>& mu = machine.getMu(); - blitz::Range a = blitz::Range::all(); - m_tmp_D_nfng_2 = 0.; - for (size_t i=0; i<v_ar.size(); ++i) - { - // Loop over the samples - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // m_tmp_D_1 = x_sj-mu - m_tmp_D_1 = v_ar[i](j,a) - mu; - // z_first_order_ij = E{z_ij} - blitz::Array<double,1> z_first_order_ij = m_cache_z_first_order[i](j, a); - // m_tmp_D_nfng_1 = (x_sj-mu).E{z_ij}^T - bob::math::prod(m_tmp_D_1, z_first_order_ij, m_tmp_D_nfng_1); - m_tmp_D_nfng_2 += m_tmp_D_nfng_1; - } - } - - // 2/ Computes the denominator inv(sum_ij E{z_i.z_i^T}) - bob::math::inv(m_cache_sum_z_second_order, m_tmp_nfng_nfng); - - // 3/ Computes numerator / denominator - bob::math::prod(m_tmp_D_nfng_2, m_tmp_nfng_nfng, m_cache_B); - - // 4/ Updates the machine - blitz::Array<double, 2>& F = machine.updateF(); - blitz::Array<double, 2>& G = machine.updateG(); - F = m_cache_B(a, blitz::Range(0, m_dim_f-1)); - G = m_cache_B(a, blitz::Range(m_dim_f, m_dim_f+m_dim_g-1)); -} - -void bob::learn::em::PLDATrainer::updateSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar) -{ - /// Computes the Sigma matrix - /// Sigma = 1/IJ sum_ij Diag{(x_ij-mu).(x_ij-mu)^T - B.E{z_i}.(x_ij-mu)^T} - - // Gets the mean mu and the matrix sigma from the machine - blitz::Array<double,1>& sigma = machine.updateSigma(); - const blitz::Array<double,1>& mu = machine.getMu(); - blitz::Range a = blitz::Range::all(); - - sigma = 0.; - size_t n_IJ=0; /// counts the number of samples - for (size_t i=0; i<v_ar.size(); ++i) - { - // Loop over the samples - for (int j=0; j<v_ar[i].extent(0); ++j) - { - // m_tmp_D_1 = x_ij-mu - m_tmp_D_1 = v_ar[i](j,a) - mu; - // sigma += Diag{(x_ij-mu).(x_ij-mu)^T} - sigma += blitz::pow2(m_tmp_D_1); - - // z_first_order_ij = E{z_ij} - blitz::Array<double,1> z_first_order_ij = m_cache_z_first_order[i](j,a); - // m_tmp_D_2 = B.E{z_ij} - bob::math::prod(m_cache_B, z_first_order_ij, m_tmp_D_2); - // sigma -= Diag{B.E{z_ij}.(x_ij-mu) - sigma -= (m_tmp_D_1 * m_tmp_D_2); - ++n_IJ; - } - } - // Normalizes by the number of samples - sigma /= static_cast<double>(n_IJ); - // Apply variance threshold - machine.applyVarianceThreshold(); -} - - -void bob::learn::em::PLDATrainer::enroll(bob::learn::em::PLDAMachine& plda_machine, - const blitz::Array<double,2>& ar) const -{ - // Gets dimension - const size_t dim_d = ar.extent(1); - const int n_samples = ar.extent(0); - // Compare the dimensionality from the base trainer/machine with the one - // of the enrollment samples - if (plda_machine.getDimD() != dim_d) { - boost::format m("the extent of the D dimension of the input machine (%u) does not match the input sample (%u)"); - m % plda_machine.getDimD() % dim_d; - throw std::runtime_error(m.str()); - } - const size_t dim_f = plda_machine.getDimF(); - - // Resize working arrays - m_tmp_D_1.resize(dim_d); - m_tmp_D_2.resize(dim_d); - m_tmp_nf_1.resize(dim_f); - - // Useful values from the base machine - blitz::Array<double,1>& weighted_sum = plda_machine.updateWeightedSum(); - const blitz::Array<double,1>& mu = plda_machine.getPLDABase()->getMu(); - const blitz::Array<double,2>& beta = plda_machine.getPLDABase()->getBeta(); - const blitz::Array<double,2>& FtBeta = plda_machine.getPLDABase()->getFtBeta(); - - // Updates the PLDA machine - plda_machine.setNSamples(n_samples); - double terma = 0.; - weighted_sum = 0.; - blitz::Range a = blitz::Range::all(); - for (int i=0; i<n_samples; ++i) { - m_tmp_D_1 = ar(i,a) - mu; - // a/ weighted sum - bob::math::prod(FtBeta, m_tmp_D_1, m_tmp_nf_1); - weighted_sum += m_tmp_nf_1; - // b/ first xi dependent term of the log likelihood - bob::math::prod(beta, m_tmp_D_1, m_tmp_D_2); - terma += -1 / 2. * blitz::sum(m_tmp_D_1 * m_tmp_D_2); - } - plda_machine.setWSumXitBetaXi(terma); - - // Adds the precomputed values for the cases N and N+1 if not already - // in the base machine (used by the forward function, 1 already added) - plda_machine.getAddGamma(n_samples); - plda_machine.getAddLogLikeConstTerm(n_samples); - plda_machine.getAddGamma(n_samples+1); - plda_machine.getAddLogLikeConstTerm(n_samples+1); - plda_machine.setLogLikelihood(plda_machine.computeLogLikelihood( - blitz::Array<double,2>(0,dim_d),true)); -} diff --git a/bob/learn/em/data/gmm_MAP.hdf5 b/bob/learn/em/data/gmm_MAP.hdf5 index 91c5e69141e3042ef5d211fc4098a8d59649d62d..0f57a2bde913bba76c2d9ab5a07cf81b07346d0f 100644 Binary files a/bob/learn/em/data/gmm_MAP.hdf5 and b/bob/learn/em/data/gmm_MAP.hdf5 differ diff --git a/bob/learn/em/data/gmm_ML.hdf5 b/bob/learn/em/data/gmm_ML.hdf5 index 74269fe3d824aa877e59609097828ee922d5dbc4..20362881b1661a826a8773d1658a8df559099d46 100644 Binary files a/bob/learn/em/data/gmm_ML.hdf5 and b/bob/learn/em/data/gmm_ML.hdf5 differ diff --git a/bob/learn/em/empca_trainer.cpp b/bob/learn/em/empca_trainer.cpp deleted file mode 100644 index 70cbe2037f8adc051c8db0202dd8da6e352bbe53..0000000000000000000000000000000000000000 --- a/bob/learn/em/empca_trainer.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Tue 03 Fev 11:22:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto EMPCATrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".EMPCATrainer", - "Trains a :py:class:`bob.learn.linear.Machine` using an Expectation-Maximization algorithm on the given dataset [Bishop1999]_ [Roweis1998]_ \n\n" - "Notations used are the ones from [Bishop1999]_\n\n" - "The probabilistic model is given by: :math:`t = Wx + \\mu + \\epsilon`\n\n" - " - :math:`t` is the observed data (dimension :math:`f`)\n" - " - :math:`W` is a projection matrix (dimension :math:`f \\times d`)\n" - " - :math:`x` is the projected data (dimension :math:`d < f`)\n" - " - :math:`\\mu` is the mean of the data (dimension :math:`f`)\n" - " - :math:`\\epsilon` is the noise of the data (dimension :math:`f`) \n" - " - Gaussian with zero-mean and covariance matrix :math:`\\sigma^2 Id`" - -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - - "Creates a EMPCATrainer", - "", - true - ) - .add_prototype("other","") - .add_prototype("","") - - .add_parameter("other", ":py:class:`bob.learn.em.EMPCATrainer`", "A EMPCATrainer object to be copied.") - -); - - -static int PyBobLearnEMEMPCATrainer_init_copy(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = EMPCATrainer_doc.kwlist(1); - PyBobLearnEMEMPCATrainerObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMEMPCATrainer_Type, &tt)){ - EMPCATrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::EMPCATrainer(*tt->cxx)); - return 0; -} - - -static int PyBobLearnEMEMPCATrainer_init(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 0:{ //default initializer () - self->cxx.reset(new bob::learn::em::EMPCATrainer()); - return 0; - } - case 1:{ - return PyBobLearnEMEMPCATrainer_init_copy(self, args, kwargs); - } - default:{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - EMPCATrainer_doc.print_usage(); - return -1; - } - } - BOB_CATCH_MEMBER("cannot create EMPCATrainer", -1) - return 0; -} - - -static void PyBobLearnEMEMPCATrainer_delete(PyBobLearnEMEMPCATrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMEMPCATrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMEMPCATrainer_Type)); -} - - -static PyObject* PyBobLearnEMEMPCATrainer_RichCompare(PyBobLearnEMEMPCATrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMEMPCATrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMEMPCATrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare EMPCATrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - - -/***** sigma_2 *****/ -static auto sigma2 = bob::extension::VariableDoc( - "sigma2", - "float", - "The noise sigma2 of the probabilistic model", - "" -); -PyObject* PyBobLearnEMEMPCATrainer_getSigma2(PyBobLearnEMEMPCATrainerObject* self, void*){ - BOB_TRY - return Py_BuildValue("d",self->cxx->getSigma2()); - BOB_CATCH_MEMBER("sigma2 could not be read", 0) -} -int PyBobLearnEMEMPCATrainer_setSigma2(PyBobLearnEMEMPCATrainerObject* self, PyObject* value, void*){ - BOB_TRY - - if(!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, sigma2.name()); - return -1; - } - - self->cxx->setSigma2(PyFloat_AS_DOUBLE(value)); - return 0; - BOB_CATCH_MEMBER("sigma2 could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMEMPCATrainer_getseters[] = { - { - sigma2.name(), - (getter)PyBobLearnEMEMPCATrainer_getSigma2, - (setter)PyBobLearnEMEMPCATrainer_setSigma2, - sigma2.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "", - "", - true -) -.add_prototype("linear_machine, data, [rng]") -.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMEMPCATrainer_initialize(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnLinearMachineObject* linear_machine = 0; - PyBlitzArrayObject* data = 0; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&|O!", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine, - &PyBlitzArray_Converter, &data, - &PyBoostMt19937_Type, &rng)) return 0; - auto data_ = make_safe(data); - - if(rng){ - self->cxx->setRng(rng->rng); - } - - - self->cxx->initialize(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "", - "", - true -) -.add_prototype("linear_machine,data") -.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMEMPCATrainer_e_step(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = e_step.kwlist(0); - - PyBobLearnLinearMachineObject* linear_machine; - PyBlitzArrayObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine, - &PyBlitzArray_Converter, &data)) return 0; - auto data_ = make_safe(data); - - self->cxx->eStep(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "", - 0, - true -) -.add_prototype("linear_machine,data") -.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMEMPCATrainer_m_step(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = m_step.kwlist(0); - - PyBobLearnLinearMachineObject* linear_machine; - PyBlitzArrayObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine, - &PyBlitzArray_Converter, &data)) return 0; - auto data_ = make_safe(data); - - self->cxx->mStep(*linear_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - -/*** computeLikelihood ***/ -static auto compute_likelihood = bob::extension::FunctionDoc( - "compute_likelihood", - "", - 0, - true -) -.add_prototype("linear_machine") -.add_parameter("linear_machine", ":py:class:`bob.learn.linear.Machine`", "LinearMachine Object"); -static PyObject* PyBobLearnEMEMPCATrainer_compute_likelihood(PyBobLearnEMEMPCATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = compute_likelihood.kwlist(0); - - PyBobLearnLinearMachineObject* linear_machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnLinearMachine_Type, &linear_machine)) return 0; - - double value = self->cxx->computeLikelihood(*linear_machine->cxx); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0) -} - - - -static PyMethodDef PyBobLearnEMEMPCATrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMEMPCATrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMEMPCATrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMEMPCATrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - compute_likelihood.name(), - (PyCFunction)PyBobLearnEMEMPCATrainer_compute_likelihood, - METH_VARARGS|METH_KEYWORDS, - compute_likelihood.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMEMPCATrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMEMPCATrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMEMPCATrainer_Type.tp_name = EMPCATrainer_doc.name(); - PyBobLearnEMEMPCATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMEMPCATrainerObject); - PyBobLearnEMEMPCATrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance - PyBobLearnEMEMPCATrainer_Type.tp_doc = EMPCATrainer_doc.doc(); - - // set the functions - PyBobLearnEMEMPCATrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMEMPCATrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMEMPCATrainer_init); - PyBobLearnEMEMPCATrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMEMPCATrainer_delete); - PyBobLearnEMEMPCATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMEMPCATrainer_RichCompare); - PyBobLearnEMEMPCATrainer_Type.tp_methods = PyBobLearnEMEMPCATrainer_methods; - PyBobLearnEMEMPCATrainer_Type.tp_getset = PyBobLearnEMEMPCATrainer_getseters; - PyBobLearnEMEMPCATrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMEMPCATrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMEMPCATrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMEMPCATrainer_Type); - return PyModule_AddObject(module, "EMPCATrainer", (PyObject*)&PyBobLearnEMEMPCATrainer_Type) >= 0; -} diff --git a/bob/learn/em/gaussian.cpp b/bob/learn/em/gaussian.cpp deleted file mode 100644 index 945da0f93f76bb6546452f781c5ebedd8faddd55..0000000000000000000000000000000000000000 --- a/bob/learn/em/gaussian.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Fri 21 Nov 10:38:48 2013 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto Gaussian_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".Gaussian", - "This class implements a multivariate diagonal Gaussian distribution" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructs a new multivariate gaussian object", - "", - true - ) - .add_prototype("n_inputs","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("n_inputs", "int", "Dimension of the feature vector") - .add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") -); - - - -static int PyBobLearnEMGaussian_init_number(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = Gaussian_doc.kwlist(0); - int n_inputs=1; - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &n_inputs)) - return -1; - - if(n_inputs < 0){ - PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero"); - Gaussian_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::Gaussian(n_inputs)); - return 0; -} - -static int PyBobLearnEMGaussian_init_copy(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = Gaussian_doc.kwlist(1); - PyBobLearnEMGaussianObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGaussian_Type, &tt)){ - Gaussian_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::Gaussian(*tt->cxx)); - return 0; -} - -static int PyBobLearnEMGaussian_init_hdf5(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = Gaussian_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - Gaussian_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - - self->cxx.reset(new bob::learn::em::Gaussian(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMGaussian_init(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get the number of command line arguments - Py_ssize_t nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - if (nargs==0){ - self->cxx.reset(new bob::learn::em::Gaussian()); - return 0; - } - - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - /**If the constructor input is a number**/ - if (PyBob_NumberCheck(arg)) - return PyBobLearnEMGaussian_init_number(self, args, kwargs); - /**If the constructor input is Gaussian object**/ - else if (PyBobLearnEMGaussian_Check(arg)) - return PyBobLearnEMGaussian_init_copy(self, args, kwargs); - /**If the constructor input is a HDF5**/ - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMGaussian_init_hdf5(self, args, kwargs); - else { - PyErr_Format(PyExc_TypeError, "invalid input argument"); - Gaussian_doc.print_usage(); - return -1; - } - - BOB_CATCH_MEMBER("cannot create Gaussian", -1) - return 0; -} - - - -static void PyBobLearnEMGaussian_delete(PyBobLearnEMGaussianObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMGaussian_RichCompare(PyBobLearnEMGaussianObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMGaussian_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMGaussianObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare Gaussian objects", 0) -} - -int PyBobLearnEMGaussian_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGaussian_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** MEAN *****/ -static auto mean = bob::extension::VariableDoc( - "mean", - "array_like <float, 1D>", - "Mean of the Gaussian", - "" -); -PyObject* PyBobLearnEMGaussian_getMean(PyBobLearnEMGaussianObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMean()); - BOB_CATCH_MEMBER("mean could not be read", 0) -} -int PyBobLearnEMGaussian_setMean(PyBobLearnEMGaussianObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, mean.name()); - return -1; - } - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, mean.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, mean.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], mean.name()); - return -1; - } - - auto o_ = make_safe(input); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "mean"); - if (!b) return -1; - self->cxx->setMean(*b); - return 0; - BOB_CATCH_MEMBER("mean could not be set", -1) -} - -/***** Variance *****/ -static auto variance = bob::extension::VariableDoc( - "variance", - "array_like <float, 1D>", - "Variance of the Gaussian", - "" -); -PyObject* PyBobLearnEMGaussian_getVariance(PyBobLearnEMGaussianObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVariance()); - BOB_CATCH_MEMBER("variance could not be read", 0) -} -int PyBobLearnEMGaussian_setVariance(PyBobLearnEMGaussianObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variance.name()); - return -1; - } - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], variance.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance"); - if (!b) return -1; - self->cxx->setVariance(*b); - return 0; - BOB_CATCH_MEMBER("variance could not be set", -1) -} - - -/***** variance_thresholds *****/ -static auto variance_thresholds = bob::extension::VariableDoc( - "variance_thresholds", - "array_like <float, 1D>", - "The variance flooring thresholds, i.e. the minimum allowed value of variance in each dimension. ", - "The variance will be set to this value if an attempt is made to set it to a smaller value." -); -PyObject* PyBobLearnEMGaussian_getVarianceThresholds(PyBobLearnEMGaussianObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceThresholds()); - BOB_CATCH_MEMBER("variance_thresholds could not be read", 0) -} -int PyBobLearnEMGaussian_setVarianceThresholds(PyBobLearnEMGaussianObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], variance_thresholds.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance_thresholds"); - if (!b) return -1; - self->cxx->setVarianceThresholds(*b); - return 0; - BOB_CATCH_MEMBER("variance_thresholds could not be set", -1) -} - - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int)", - "A tuple that represents the dimensionality of the Gaussian ``(dim,)``.", - "" -); -PyObject* PyBobLearnEMGaussian_getShape(PyBobLearnEMGaussianObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i)", self->cxx->getNInputs()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -static PyGetSetDef PyBobLearnEMGaussian_getseters[] = { - { - mean.name(), - (getter)PyBobLearnEMGaussian_getMean, - (setter)PyBobLearnEMGaussian_setMean, - mean.doc(), - 0 - }, - { - variance.name(), - (getter)PyBobLearnEMGaussian_getVariance, - (setter)PyBobLearnEMGaussian_setVariance, - variance.doc(), - 0 - }, - { - variance_thresholds.name(), - (getter)PyBobLearnEMGaussian_getVarianceThresholds, - (setter)PyBobLearnEMGaussian_setVarianceThresholds, - variance_thresholds.doc(), - 0 - }, - { - shape.name(), - (getter)PyBobLearnEMGaussian_getShape, - 0, - shape.doc(), - 0 - }, - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Set the input dimensionality, reset the mean to zero and the variance to one." -) -.add_prototype("input") -.add_parameter("input", "int", "Dimensionality of the feature vector"); -static PyObject* PyBobLearnEMGaussian_resize(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &input)) return 0; - if (input <= 0){ - PyErr_Format(PyExc_TypeError, "input must be greater than zero"); - resize.print_usage(); - return 0; - } - self->cxx->setNInputs(input); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - -/*** log_likelihood ***/ -static auto log_likelihood = bob::extension::FunctionDoc( - "log_likelihood", - "Output the log likelihood of the sample, x. The input size is checked.", - ".. note:: The ``__call__`` function is an alias for this.", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "Input vector") -.add_return("output","float","The log likelihood"); -static PyObject* PyBobLearnEMGaussian_loglikelihood(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = log_likelihood.kwlist(0); - - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]); - log_likelihood.print_usage(); - return 0; - } - - double value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(input)); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - -/*** log_likelihood_ ***/ -static auto log_likelihood_ = bob::extension::FunctionDoc( - "log_likelihood_", - "Output the log likelihood given a sample. The input size is NOT checked." -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "Input vector") -.add_return("output","float","The log likelihood"); -static PyObject* PyBobLearnEMGaussian_loglikelihood_(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - char** kwlist = log_likelihood_.kwlist(0); - - PyBlitzArrayObject* input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]); - log_likelihood.print_usage(); - return 0; - } - - double value = self->cxx->logLikelihood_(*PyBlitzArrayCxx_AsBlitz<double,1>(input)); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the Gassian Machine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMGaussian_Save(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the Gassian Machine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMGaussian_Load(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this Gaussian with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` and any other values internal to this machine.", - true -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.Gaussian`", "A gaussian to be compared.") -.add_parameter("[r_epsilon]", "float", "Relative precision.") -.add_parameter("[a_epsilon]", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMGaussian_IsSimilarTo(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - PyBobLearnEMGaussianObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMGaussian_Type, &other, - &r_epsilon, &a_epsilon)) return 0; - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** set_variance_thresholds ***/ -static auto set_variance_thresholds = bob::extension::FunctionDoc( - "set_variance_thresholds", - "Set the variance flooring thresholds equal to the given threshold for all the dimensions." -) -.add_prototype("input") -.add_parameter("input","float","Threshold") -; -static PyObject* PyBobLearnEMGaussian_SetVarianceThresholds(PyBobLearnEMGaussianObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = set_variance_thresholds.kwlist(0); - - double input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &input)) return 0; - - self->cxx->setVarianceThresholds(input); - - BOB_CATCH_MEMBER("cannot perform the set_variance_Thresholds method", 0) - - Py_RETURN_NONE; -} - - -static PyMethodDef PyBobLearnEMGaussian_methods[] = { - { - resize.name(), - (PyCFunction)PyBobLearnEMGaussian_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - log_likelihood.name(), - (PyCFunction)PyBobLearnEMGaussian_loglikelihood, - METH_VARARGS|METH_KEYWORDS, - log_likelihood.doc() - }, - { - log_likelihood_.name(), - (PyCFunction)PyBobLearnEMGaussian_loglikelihood_, - METH_VARARGS|METH_KEYWORDS, - log_likelihood_.doc() - }, - { - save.name(), - (PyCFunction)PyBobLearnEMGaussian_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMGaussian_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMGaussian_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - set_variance_thresholds.name(), - (PyCFunction)PyBobLearnEMGaussian_SetVarianceThresholds, - METH_VARARGS|METH_KEYWORDS, - set_variance_thresholds.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMGaussian_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMGaussian(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMGaussian_Type.tp_name = Gaussian_doc.name(); - PyBobLearnEMGaussian_Type.tp_basicsize = sizeof(PyBobLearnEMGaussianObject); - PyBobLearnEMGaussian_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyBobLearnEMGaussian_Type.tp_doc = Gaussian_doc.doc(); - - // set the functions - PyBobLearnEMGaussian_Type.tp_new = PyType_GenericNew; - PyBobLearnEMGaussian_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGaussian_init); - PyBobLearnEMGaussian_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGaussian_delete); - PyBobLearnEMGaussian_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGaussian_RichCompare); - PyBobLearnEMGaussian_Type.tp_methods = PyBobLearnEMGaussian_methods; - PyBobLearnEMGaussian_Type.tp_getset = PyBobLearnEMGaussian_getseters; - PyBobLearnEMGaussian_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGaussian_loglikelihood); - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMGaussian_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMGaussian_Type); - return PyModule_AddObject(module, "Gaussian", (PyObject*)&PyBobLearnEMGaussian_Type) >= 0; -} - diff --git a/bob/learn/em/gmm_machine.cpp b/bob/learn/em/gmm_machine.cpp deleted file mode 100644 index 429c3097d9471ddfbf9d26b043d23e3407062aaf..0000000000000000000000000000000000000000 --- a/bob/learn/em/gmm_machine.cpp +++ /dev/null @@ -1,988 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Wed 11 Dec 18:01:00 2014 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto GMMMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".GMMMachine", - "This class implements the statistical model for multivariate diagonal mixture Gaussian distribution (GMM). " - "A GMM is defined as :math:`\\sum_{c=0}^{C} \\omega_c \\mathcal{N}(x | \\mu_c, \\sigma_c)`, where :math:`C` is the number of Gaussian components :math:`\\mu_c`, :math:`\\sigma_c` and :math:`\\omega_c` are respectively the the mean, variance and the weight of each gaussian component :math:`c`.", - "See Section 2.3.9 of Bishop, \"Pattern recognition and machine learning\", 2006" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a GMMMachine", - "", - true - ) - .add_prototype("n_gaussians,n_inputs","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("n_gaussians", "int", "Number of gaussians") - .add_parameter("n_inputs", "int", "Dimension of the feature vector") - .add_parameter("other", ":py:class:`bob.learn.em.GMMMachine`", "A GMMMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMGMMMachine_init_number(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMMachine_doc.kwlist(0); - int n_inputs = 1; - int n_gaussians = 1; - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) - return -1; - - if(n_gaussians < 0){ - PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than or equal to zero"); - return -1; - } - - if(n_inputs < 0){ - PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero"); - return -1; - } - - self->cxx.reset(new bob::learn::em::GMMMachine(n_gaussians, n_inputs)); - return 0; -} - - -static int PyBobLearnEMGMMMachine_init_copy(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMMachine_doc.kwlist(1); - PyBobLearnEMGMMMachineObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &tt)){ - GMMMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::GMMMachine(*tt->cxx)); - return 0; -} - - -static int PyBobLearnEMGMMMachine_init_hdf5(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - GMMMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - - self->cxx.reset(new bob::learn::em::GMMMachine(*(config->f))); - - return 0; -} - - - -static int PyBobLearnEMGMMMachine_init(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 0: //default initializer () - self->cxx.reset(new bob::learn::em::GMMMachine()); - return 0; - - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMGMMMachine_Check(arg)) - return PyBobLearnEMGMMMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMGMMMachine_init_hdf5(self, args, kwargs); - } - case 2: - return PyBobLearnEMGMMMachine_init_number(self, args, kwargs); - default: - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - GMMMachine_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create GMMMachine", -1) - return 0; -} - - - -static void PyBobLearnEMGMMMachine_delete(PyBobLearnEMGMMMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMGMMMachine_RichCompare(PyBobLearnEMGMMMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMGMMMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMGMMMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare GMMMachine objects", 0) -} - -int PyBobLearnEMGMMMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGMMMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int)", - "A tuple that represents the number of gaussians and dimensionality of each Gaussian ``(n_gaussians, dim)``.", - "" -); -PyObject* PyBobLearnEMGMMMachine_getShape(PyBobLearnEMGMMMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** MEAN *****/ - -static auto means = bob::extension::VariableDoc( - "means", - "array_like <float, 2D>", - "The means of the gaussians", - "" -); -PyObject* PyBobLearnEMGMMMachine_getMeans(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeans()); - BOB_CATCH_MEMBER("means could not be read", 0) -} -int PyBobLearnEMGMMMachine_setMeans(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, means.name()); - return -1; - } - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, means.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, means.name()); - return -1; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], means.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "means"); - if (!b) return -1; - self->cxx->setMeans(*b); - return 0; - BOB_CATCH_MEMBER("means could not be set", -1) -} - -/***** Variance *****/ -static auto variances = bob::extension::VariableDoc( - "variances", - "array_like <float, 2D>", - "Variances of the gaussians", - "" -); -PyObject* PyBobLearnEMGMMMachine_getVariances(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVariances()); - BOB_CATCH_MEMBER("variances could not be read", 0) -} -int PyBobLearnEMGMMMachine_setVariances(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variances.name()); - return -1; - } - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variances.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variances.name()); - return -1; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], variances.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "variances"); - if (!b) return -1; - self->cxx->setVariances(*b); - return 0; - BOB_CATCH_MEMBER("variances could not be set", -1) -} - -/***** Weights *****/ -static auto weights = bob::extension::VariableDoc( - "weights", - "array_like <float, 1D>", - "The weights (also known as \"mixing coefficients\")", - "" -); -PyObject* PyBobLearnEMGMMMachine_getWeights(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getWeights()); - BOB_CATCH_MEMBER("weights could not be read", 0) -} -int PyBobLearnEMGMMMachine_setWeights(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, weights.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, weights.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, weights.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), input->shape[0], weights.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "weights"); - if (!b) return -1; - self->cxx->setWeights(*b); - return 0; - BOB_CATCH_MEMBER("weights could not be set", -1) -} - - -/***** variance_supervector *****/ -static auto variance_supervector = bob::extension::VariableDoc( - "variance_supervector", - "array_like <float, 1D>", - "The variance supervector of the GMMMachine", - "Concatenation of the variance vectors of each Gaussian of the GMMMachine" -); -PyObject* PyBobLearnEMGMMMachine_getVarianceSupervector(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceSupervector()); - BOB_CATCH_MEMBER("variance_supervector could not be read", 0) -} -int PyBobLearnEMGMMMachine_setVarianceSupervector(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, variance_supervector.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_supervector.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_supervector.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), input->shape[0], variance_supervector.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "variance_supervector"); - if (!b) return -1; - self->cxx->setVarianceSupervector(*b); - return 0; - BOB_CATCH_MEMBER("variance_supervector could not be set", -1) -} - -/***** mean_supervector *****/ -static auto mean_supervector = bob::extension::VariableDoc( - "mean_supervector", - "array_like <float, 1D>", - "The mean supervector of the GMMMachine", - "Concatenation of the mean vectors of each Gaussian of the GMMMachine" -); -PyObject* PyBobLearnEMGMMMachine_getMeanSupervector(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeanSupervector()); - BOB_CATCH_MEMBER("mean_supervector could not be read", 0) -} -int PyBobLearnEMGMMMachine_setMeanSupervector(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, mean_supervector.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, mean_supervector.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, mean_supervector.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), input->shape[0], mean_supervector.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "mean_supervector"); - if (!b) return -1; - self->cxx->setMeanSupervector(*b); - return 0; - BOB_CATCH_MEMBER("mean_supervector could not be set", -1) -} - - - -/***** variance_thresholds *****/ -static auto variance_thresholds = bob::extension::VariableDoc( - "variance_thresholds", - "array_like <float, 2D>", - "Set the variance flooring thresholds in each dimension to the same vector for all Gaussian components if the argument is a 1D numpy arrray, and equal for all Gaussian components and dimensions if the parameter is a scalar. ", - "" -); -PyObject* PyBobLearnEMGMMMachine_getVarianceThresholds(PyBobLearnEMGMMMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getVarianceThresholds()); - BOB_CATCH_MEMBER("variance_thresholds could not be read", 0) -} -int PyBobLearnEMGMMMachine_setVarianceThresholds(PyBobLearnEMGMMMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, variance_thresholds.name()); - return -1; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() && input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNGaussians(), self->cxx->getNInputs(), input->shape[1], input->shape[0], variance_thresholds.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "variance_thresholds"); - if (!b) return -1; - self->cxx->setVarianceThresholds(*b); - return 0; - BOB_CATCH_MEMBER("variance_thresholds could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMGMMMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMGMMMachine_getShape, - 0, - shape.doc(), - 0 - }, - { - means.name(), - (getter)PyBobLearnEMGMMMachine_getMeans, - (setter)PyBobLearnEMGMMMachine_setMeans, - means.doc(), - 0 - }, - { - variances.name(), - (getter)PyBobLearnEMGMMMachine_getVariances, - (setter)PyBobLearnEMGMMMachine_setVariances, - variances.doc(), - 0 - }, - { - weights.name(), - (getter)PyBobLearnEMGMMMachine_getWeights, - (setter)PyBobLearnEMGMMMachine_setWeights, - weights.doc(), - 0 - }, - { - variance_thresholds.name(), - (getter)PyBobLearnEMGMMMachine_getVarianceThresholds, - (setter)PyBobLearnEMGMMMachine_setVarianceThresholds, - variance_thresholds.doc(), - 0 - }, - { - variance_supervector.name(), - (getter)PyBobLearnEMGMMMachine_getVarianceSupervector, - (setter)PyBobLearnEMGMMMachine_setVarianceSupervector, - variance_supervector.doc(), - 0 - }, - - { - mean_supervector.name(), - (getter)PyBobLearnEMGMMMachine_getMeanSupervector, - (setter)PyBobLearnEMGMMMachine_setMeanSupervector, - mean_supervector.doc(), - 0 - }, - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the GMMMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMGMMMachine_Save(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the GMMMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMGMMMachine_Load(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this GMMMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.GMMMachine`", "A GMMMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMGMMMachine_IsSimilarTo(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMGMMMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMGMMMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Allocates space for the statistics and resets to zero.", - 0, - true -) -.add_prototype("n_gaussians,n_inputs") -.add_parameter("n_gaussians", "int", "Number of gaussians") -.add_parameter("n_inputs", "int", "Dimensionality of the feature vector"); -static PyObject* PyBobLearnEMGMMMachine_resize(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int n_gaussians = 0; - int n_inputs = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) Py_RETURN_NONE; - - if (n_gaussians <= 0){ - PyErr_Format(PyExc_TypeError, "n_gaussians must be greater than zero"); - resize.print_usage(); - return 0; - } - if (n_inputs <= 0){ - PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero"); - resize.print_usage(); - return 0; - } - - self->cxx->resize(n_gaussians, n_inputs); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - -/*** log_likelihood ***/ -static auto log_likelihood = bob::extension::FunctionDoc( - "log_likelihood", - "Output the log likelihood of the sample, x, i.e. :math:`log(p(x|GMM))`. Inputs are checked.", - ".. note:: The ``__call__`` function is an alias for this. \n " - "If `input` is 2D the average along the samples will be computed (:math:`\\frac{log(p(x|GMM))}{N}`) ", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "Input vector") -.add_return("output","float","The log likelihood"); -static PyObject* PyBobLearnEMGMMMachine_loglikelihood(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = log_likelihood.kwlist(0); - - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->ndim > 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D or 2D arrays of float64", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - int shape_index = input->ndim - 1; //Getting the index of the dimensionality (0 for 1D arrays, 1 for 2D arrays) - - if (input->shape[shape_index] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]); - log_likelihood.print_usage(); - return 0; - } - - double value = 0; - if (input->ndim == 1) - value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(input)); - else - value = self->cxx->logLikelihood(*PyBlitzArrayCxx_AsBlitz<double,2>(input)); - - - return Py_BuildValue("d", value); - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - -/*** log_likelihood_ ***/ -static auto log_likelihood_ = bob::extension::FunctionDoc( - "log_likelihood_", - "Output the log likelihood of the sample, x, i.e. :math:`log(p(x|GMM))`. Inputs are NOT checked.", - "", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "Input vector") -.add_return("output","float","The log likelihood"); -static PyObject* PyBobLearnEMGMMMachine_loglikelihood_(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = log_likelihood_.kwlist(0); - - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `input`", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64", Py_TYPE(self)->tp_name); - log_likelihood.print_usage(); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0]); - log_likelihood.print_usage(); - return 0; - } - - double value = self->cxx->logLikelihood_(*PyBlitzArrayCxx_AsBlitz<double,1>(input)); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - -/*** acc_statistics ***/ -static auto acc_statistics = bob::extension::FunctionDoc( - "acc_statistics", - "Accumulate the GMM statistics (:py:class:`bob.learn.em.GMMStats`) for this sample(s). Inputs are checked.", - "", - true -) -.add_prototype("input,stats") -.add_parameter("input", "array_like <float, 2D>", "Input vector") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM"); -static PyObject* PyBobLearnEMGMMMachine_accStatistics(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = acc_statistics.kwlist(0); - - PyBlitzArrayObject* input = 0; - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBlitzArray_Converter,&input, - &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - if (input->ndim == 1) - self->cxx->accStatistics(*PyBlitzArrayCxx_AsBlitz<double,1>(input), *stats->cxx); - else - self->cxx->accStatistics(*PyBlitzArrayCxx_AsBlitz<double,2>(input), *stats->cxx); - - - BOB_CATCH_MEMBER("cannot accumulate the statistics", 0) - Py_RETURN_NONE; -} - - -/*** acc_statistics_ ***/ -static auto acc_statistics_ = bob::extension::FunctionDoc( - "acc_statistics_", - "Accumulate the GMM statistics (:py:class:`bob.learn.em.GMMStats`) for this sample(s). Inputs are NOT checked.", - "", - true -) -.add_prototype("input,stats") -.add_parameter("input", "array_like <float, 2D>", "Input vector") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM"); -static PyObject* PyBobLearnEMGMMMachine_accStatistics_(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = acc_statistics_.kwlist(0); - - PyBlitzArrayObject* input = 0; - PyBobLearnEMGMMStatsObject* stats = 0; - - if(!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBlitzArray_Converter,&input, - &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - if (input->ndim==1) - self->cxx->accStatistics_(*PyBlitzArrayCxx_AsBlitz<double,1>(input), *stats->cxx); - else - self->cxx->accStatistics_(*PyBlitzArrayCxx_AsBlitz<double,2>(input), *stats->cxx); - - BOB_CATCH_MEMBER("cannot accumulate the statistics", 0) - Py_RETURN_NONE; -} - - - -/*** set_variance_thresholds ***/ -static auto set_variance_thresholds = bob::extension::FunctionDoc( - "set_variance_thresholds", - "Set the variance flooring thresholds in each dimension to the same vector for all Gaussian components if the argument is a 1D numpy arrray, and equal for all Gaussian components and dimensions if the parameter is a scalar.", - "", - true -) -.add_prototype("input") -.add_parameter("input", "float or array_like <float, 1D>", "The new variance threshold, or a vector of thresholds for all Gaussian components"); -static PyObject* PyBobLearnEMGMMMachine_setVarianceThresholds_method(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = set_variance_thresholds.kwlist(0); - - PyBlitzArrayObject* input_array = 0; - double input_number = 0; - if(PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &input_number)){ - self->cxx->setVarianceThresholds(input_number); - } - else if(PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input_array)) { - //protects acquired resources through this scope - auto input_ = make_safe(input_array); - self->cxx->setVarianceThresholds(*PyBlitzArrayCxx_AsBlitz<double,1>(input_array)); - } - else - return 0; - - // clear any error that might have been set in the functions above - PyErr_Clear(); - - BOB_CATCH_MEMBER("cannot accumulate set the variance threshold", 0) - Py_RETURN_NONE; -} - - - - -/*** get_gaussian ***/ -static auto get_gaussian = bob::extension::FunctionDoc( - "get_gaussian", - "Get the specified Gaussian (:py:class:`bob.learn.em.Gaussian`) component.", - ".. note:: An exception is thrown if i is out of range.", - true -) -.add_prototype("i","gaussian") -.add_parameter("i", "int", "Index of the gaussian") -.add_return("gaussian",":py:class:`bob.learn.em.Gaussian`","Gaussian object"); -static PyObject* PyBobLearnEMGMMMachine_get_gaussian(PyBobLearnEMGMMMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_gaussian.kwlist(0); - - int i = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - //Allocating the correspondent python object - PyBobLearnEMGaussianObject* retval = - (PyBobLearnEMGaussianObject*)PyBobLearnEMGaussian_Type.tp_alloc(&PyBobLearnEMGaussian_Type, 0); - - retval->cxx = self->cxx->getGaussian(i); - - return Py_BuildValue("N",retval); - - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - - -static PyMethodDef PyBobLearnEMGMMMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMGMMMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMGMMMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMGMMMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMGMMMachine_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - log_likelihood.name(), - (PyCFunction)PyBobLearnEMGMMMachine_loglikelihood, - METH_VARARGS|METH_KEYWORDS, - log_likelihood.doc() - }, - { - log_likelihood_.name(), - (PyCFunction)PyBobLearnEMGMMMachine_loglikelihood_, - METH_VARARGS|METH_KEYWORDS, - log_likelihood_.doc() - }, - { - acc_statistics.name(), - (PyCFunction)PyBobLearnEMGMMMachine_accStatistics, - METH_VARARGS|METH_KEYWORDS, - acc_statistics.doc() - }, - { - acc_statistics_.name(), - (PyCFunction)PyBobLearnEMGMMMachine_accStatistics_, - METH_VARARGS|METH_KEYWORDS, - acc_statistics_.doc() - }, - - { - get_gaussian.name(), - (PyCFunction)PyBobLearnEMGMMMachine_get_gaussian, - METH_VARARGS|METH_KEYWORDS, - get_gaussian.doc() - }, - - { - set_variance_thresholds.name(), - (PyCFunction)PyBobLearnEMGMMMachine_setVarianceThresholds_method, - METH_VARARGS|METH_KEYWORDS, - set_variance_thresholds.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMGMMMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMGMMMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMGMMMachine_Type.tp_name = GMMMachine_doc.name(); - PyBobLearnEMGMMMachine_Type.tp_basicsize = sizeof(PyBobLearnEMGMMMachineObject); - PyBobLearnEMGMMMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMGMMMachine_Type.tp_doc = GMMMachine_doc.doc(); - - // set the functions - PyBobLearnEMGMMMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMGMMMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGMMMachine_init); - PyBobLearnEMGMMMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGMMMachine_delete); - PyBobLearnEMGMMMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGMMMachine_RichCompare); - PyBobLearnEMGMMMachine_Type.tp_methods = PyBobLearnEMGMMMachine_methods; - PyBobLearnEMGMMMachine_Type.tp_getset = PyBobLearnEMGMMMachine_getseters; - PyBobLearnEMGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGMMMachine_loglikelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMGMMMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMGMMMachine_Type); - return PyModule_AddObject(module, "GMMMachine", (PyObject*)&PyBobLearnEMGMMMachine_Type) >= 0; -} diff --git a/bob/learn/em/gmm_stats.cpp b/bob/learn/em/gmm_stats.cpp deleted file mode 100644 index 32011ca86aeccf112af86f4a9c3b37abdad5881b..0000000000000000000000000000000000000000 --- a/bob/learn/em/gmm_stats.cpp +++ /dev/null @@ -1,673 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Wed 03 Dec 14:38:48 2014 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto GMMStats_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".GMMStats", - "A container for GMM statistics", - "With respect to [Reynolds2000]_ the class computes: \n\n" - "* Eq (8) is :py:class:`bob.learn.em.GMMStats.n`: :math:`n_i=\\sum\\limits_{t=1}^T Pr(i | x_t)`\n\n" - "* Eq (9) is :py:class:`bob.learn.em.GMMStats.sum_px`: :math:`E_i(x)=\\frac{1}{n(i)}\\sum\\limits_{t=1}^T Pr(i | x_t)x_t`\n\n" - "* Eq (10) is :py:class:`bob.learn.em.GMMStats.sum_pxx`: :math:`E_i(x^2)=\\frac{1}{n(i)}\\sum\\limits_{t=1}^T Pr(i | x_t)x_t^2`\n\n" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "A container for GMM statistics.", - "", - true - ) - .add_prototype("n_gaussians,n_inputs","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("n_gaussians", "int", "Number of gaussians") - .add_parameter("n_inputs", "int", "Dimension of the feature vector") - .add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMGMMStats_init_number(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMStats_doc.kwlist(0); - int n_inputs = 1; - int n_gaussians = 1; - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) - return -1; - - if(n_gaussians < 0){ - PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than or equal to zero"); - GMMStats_doc.print_usage(); - return -1; - } - - if(n_inputs < 0){ - PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero"); - GMMStats_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::GMMStats(n_gaussians, n_inputs)); - return 0; -} - - -static int PyBobLearnEMGMMStats_init_copy(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMStats_doc.kwlist(1); - PyBobLearnEMGMMStatsObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &tt)){ - GMMStats_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::GMMStats(*tt->cxx)); - return 0; -} - - -static int PyBobLearnEMGMMStats_init_hdf5(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = GMMStats_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - GMMStats_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::GMMStats(*(config->f))); - - return 0; -} - - - -static int PyBobLearnEMGMMStats_init(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 0: //default initializer () - self->cxx.reset(new bob::learn::em::GMMStats()); - return 0; - - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - /**If the constructor input is Gaussian object**/ - if (PyBobLearnEMGMMStats_Check(arg)) - return PyBobLearnEMGMMStats_init_copy(self, args, kwargs); - /**If the constructor input is a HDF5**/ - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMGMMStats_init_hdf5(self, args, kwargs); - } - case 2: - return PyBobLearnEMGMMStats_init_number(self, args, kwargs); - default: - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - GMMStats_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create GMMStats", -1) - return 0; -} - - - -static void PyBobLearnEMGMMStats_delete(PyBobLearnEMGMMStatsObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMGMMStats_RichCompare(PyBobLearnEMGMMStatsObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMGMMStats_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMGMMStatsObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare GMMStats objects", 0) -} - -int PyBobLearnEMGMMStats_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMGMMStats_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** n *****/ -static auto n = bob::extension::VariableDoc( - "n", - "array_like <float, 1D>", - "For each Gaussian, the accumulated sum of responsibilities, i.e. the sum of :math:`P(gaussian_i|x)`" -); -PyObject* PyBobLearnEMGMMStats_getN(PyBobLearnEMGMMStatsObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->n); - BOB_CATCH_MEMBER("n could not be read", 0) -} -int PyBobLearnEMGMMStats_setN(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, n.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, n.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, n.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->n.extent(0)){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->n.extent(0), input->shape[0], n.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "n"); - if (!b) return -1; - self->cxx->n = *b; - return 0; - BOB_CATCH_MEMBER("n could not be set", -1) -} - - -/***** sum_px *****/ -static auto sum_px = bob::extension::VariableDoc( - "sum_px", - "array_like <float, 2D>", - "For each Gaussian, the accumulated sum of responsibility times the sample" -); -PyObject* PyBobLearnEMGMMStats_getSum_px(PyBobLearnEMGMMStatsObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->sumPx); - BOB_CATCH_MEMBER("sum_px could not be read", 0) -} -int PyBobLearnEMGMMStats_setSum_px(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sum_px.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, sum_px.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, sum_px.name()); - return -1; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->sumPx.extent(1) && input->shape[0] != (Py_ssize_t)self->cxx->sumPx.extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->sumPx.extent(1), (Py_ssize_t)self->cxx->sumPx.extent(0), (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[0], sum_px.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "sum_px"); - if (!b) return -1; - self->cxx->sumPx = *b; - return 0; - BOB_CATCH_MEMBER("sum_px could not be set", -1) -} - - -/***** sum_pxx *****/ -static auto sum_pxx = bob::extension::VariableDoc( - "sum_pxx", - "array_like <float, 2D>", - "For each Gaussian, the accumulated sum of responsibility times the sample squared" -); -PyObject* PyBobLearnEMGMMStats_getSum_pxx(PyBobLearnEMGMMStatsObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->sumPxx); - BOB_CATCH_MEMBER("sum_pxx could not be read", 0) -} -int PyBobLearnEMGMMStats_setSum_pxx(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sum_pxx.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, sum_pxx.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, sum_pxx.name()); - return -1; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->sumPxx.extent(1) && input->shape[0] != (Py_ssize_t)self->cxx->sumPxx.extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->sumPxx.extent(1), (Py_ssize_t)self->cxx->sumPxx.extent(0), (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[0], sum_pxx.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "sum_pxx"); - if (!b) return -1; - self->cxx->sumPxx = *b; - return 0; - BOB_CATCH_MEMBER("sum_pxx could not be set", -1) -} - - -/***** t *****/ -static auto t = bob::extension::VariableDoc( - "t", - "int", - "The number of samples" -); -PyObject* PyBobLearnEMGMMStats_getT(PyBobLearnEMGMMStatsObject* self, void*){ - BOB_TRY - return Py_BuildValue("i", self->cxx->T); - BOB_CATCH_MEMBER("t could not be read", 0) -} -int PyBobLearnEMGMMStats_setT(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyInt_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an int", Py_TYPE(self)->tp_name, t.name()); - return -1; - } - - if (PyInt_AS_LONG(value) < 0){ - PyErr_Format(PyExc_TypeError, "t must be greater than or equal to zero"); - return -1; - } - - self->cxx->T = PyInt_AS_LONG(value); - BOB_CATCH_MEMBER("t could not be set", -1) - return 0; -} - - -/***** log_likelihood *****/ -static auto log_likelihood = bob::extension::VariableDoc( - "log_likelihood", - "float", - "The accumulated log likelihood of all samples" -); -PyObject* PyBobLearnEMGMMStats_getLog_likelihood(PyBobLearnEMGMMStatsObject* self, void*){ - BOB_TRY - return Py_BuildValue("d", self->cxx->log_likelihood); - BOB_CATCH_MEMBER("log_likelihood could not be read", 0) -} -int PyBobLearnEMGMMStats_setLog_likelihood(PyBobLearnEMGMMStatsObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an double", Py_TYPE(self)->tp_name, t.name()); - return -1; - } - - self->cxx->log_likelihood = PyFloat_AsDouble(value); - return 0; - BOB_CATCH_MEMBER("log_likelihood could not be set", -1) -} - - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int)", - "A tuple that represents the number of gaussians and dimensionality of each Gaussian ``(n_gaussians, dim)``.", - "" -); -PyObject* PyBobLearnEMGMMStats_getShape(PyBobLearnEMGMMStatsObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i)", self->cxx->sumPx.shape()[0], self->cxx->sumPx.shape()[1]); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - - - -static PyGetSetDef PyBobLearnEMGMMStats_getseters[] = { - { - n.name(), - (getter)PyBobLearnEMGMMStats_getN, - (setter)PyBobLearnEMGMMStats_setN, - n.doc(), - 0 - }, - { - sum_px.name(), - (getter)PyBobLearnEMGMMStats_getSum_px, - (setter)PyBobLearnEMGMMStats_setSum_px, - sum_px.doc(), - 0 - }, - { - sum_pxx.name(), - (getter)PyBobLearnEMGMMStats_getSum_pxx, - (setter)PyBobLearnEMGMMStats_setSum_pxx, - sum_pxx.doc(), - 0 - }, - { - t.name(), - (getter)PyBobLearnEMGMMStats_getT, - (setter)PyBobLearnEMGMMStats_setT, - t.doc(), - 0 - }, - { - log_likelihood.name(), - (getter)PyBobLearnEMGMMStats_getLog_likelihood, - (setter)PyBobLearnEMGMMStats_setLog_likelihood, - log_likelihood.doc(), - 0 - }, - { - shape.name(), - (getter)PyBobLearnEMGMMStats_getShape, - 0, - shape.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the GMMStats to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMGMMStats_Save(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the GMMStats to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMGMMStats_Load(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this GMMStats with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.GMMStats`", "A GMMStats object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMGMMStats_IsSimilarTo(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMGMMStatsObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMGMMStats_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Allocates space for the statistics and resets to zero.", - 0, - true -) -.add_prototype("n_gaussians,n_inputs") -.add_parameter("n_gaussians", "int", "Number of gaussians") -.add_parameter("n_inputs", "int", "Dimensionality of the feature vector"); -static PyObject* PyBobLearnEMGMMStats_resize(PyBobLearnEMGMMStatsObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int n_gaussians = 0; - int n_inputs = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_gaussians, &n_inputs)) return 0; - - if (n_gaussians <= 0){ - PyErr_Format(PyExc_TypeError, "n_gaussians must be greater than zero"); - resize.print_usage(); - return 0; - } - if (n_inputs <= 0){ - PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero"); - resize.print_usage(); - return 0; - } - - - self->cxx->resize(n_gaussians, n_inputs); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - -/*** init ***/ -static auto init = bob::extension::FunctionDoc( - "init", - " Resets statistics to zero." -) -.add_prototype(""); -static PyObject* PyBobLearnEMGMMStats_init_method(PyBobLearnEMGMMStatsObject* self) { - BOB_TRY - - self->cxx->init(); - - BOB_CATCH_MEMBER("cannot perform the init method", 0) - - Py_RETURN_NONE; -} - - - -static PyMethodDef PyBobLearnEMGMMStats_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMGMMStats_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMGMMStats_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMGMMStats_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMGMMStats_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - init.name(), - (PyCFunction)PyBobLearnEMGMMStats_init_method, - METH_NOARGS, - init.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Operators *******************************************/ -/******************************************************************/ - -static PyBobLearnEMGMMStatsObject* PyBobLearnEMGMMStats_inplaceadd(PyBobLearnEMGMMStatsObject* self, PyObject* other) { - BOB_TRY - - if (!PyBobLearnEMGMMStats_Check(other)){ - PyErr_Format(PyExc_TypeError, "expected bob.learn.em.GMMStats object"); - return 0; - } - - auto other_ = reinterpret_cast<PyBobLearnEMGMMStatsObject*>(other); - - self->cxx->operator+=(*other_->cxx); - - BOB_CATCH_MEMBER("it was not possible to process the operator +=", 0) - - Py_INCREF(self); - return self; -} - -static PyNumberMethods PyBobLearnEMGMMStats_operators = {0}; - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMGMMStats_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMGMMStats(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMGMMStats_Type.tp_name = GMMStats_doc.name(); - PyBobLearnEMGMMStats_Type.tp_basicsize = sizeof(PyBobLearnEMGMMStatsObject); - PyBobLearnEMGMMStats_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMGMMStats_Type.tp_doc = GMMStats_doc.doc(); - - // set the functions - PyBobLearnEMGMMStats_Type.tp_new = PyType_GenericNew; - PyBobLearnEMGMMStats_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMGMMStats_init); - PyBobLearnEMGMMStats_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMGMMStats_delete); - PyBobLearnEMGMMStats_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMGMMStats_RichCompare); - PyBobLearnEMGMMStats_Type.tp_methods = PyBobLearnEMGMMStats_methods; - PyBobLearnEMGMMStats_Type.tp_getset = PyBobLearnEMGMMStats_getseters; - PyBobLearnEMGMMStats_Type.tp_call = 0; - PyBobLearnEMGMMStats_Type.tp_as_number = &PyBobLearnEMGMMStats_operators; - - //set operators - PyBobLearnEMGMMStats_operators.nb_inplace_add = reinterpret_cast<binaryfunc>(PyBobLearnEMGMMStats_inplaceadd); - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMGMMStats_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMGMMStats_Type); - return PyModule_AddObject(module, "GMMStats", (PyObject*)&PyBobLearnEMGMMStats_Type) >= 0; -} diff --git a/bob/learn/em/include/bob.learn.em/EMPCATrainer.h b/bob/learn/em/include/bob.learn.em/EMPCATrainer.h deleted file mode 100644 index 90153c2adaeece5b179598316ecb7e766bb4850b..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/EMPCATrainer.h +++ /dev/null @@ -1,200 +0,0 @@ -/** - * @date Tue Oct 11 12:18:23 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Expectation Maximization Algorithm for Principal Component - * Analysis - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_EMPCA_TRAINER_H -#define BOB_LEARN_EM_EMPCA_TRAINER_H - -#include <bob.learn.linear/machine.h> -#include <blitz/array.h> - -namespace bob { namespace learn { namespace em { - -/** - * @brief Trains a linear machine using an Expectation-Maximization algorithm - * on the given dataset.\n - * References:\n - * 1. "Probabilistic Principal Component Analysis", - * Michael Tipping and Christopher Bishop, - * Journal of the Royal Statistical Society, - * Series B, 61, Part 3, pp. 611–622\n - * 2. "EM Algorithms for PCA and SPCA", - * Sam Roweis, Neural Information Processing Systems 10 (NIPS'97), - * pp.626-632 (Sensible Principal Component Analysis part)\n - * - * Notations used are the ones from reference 1.\n - * The probabilistic model is given by: \f$t = W x + \mu + \epsilon\f$\n - * - \f$t\f$ is the observed data (dimension \f$f\f$)\n - * - \f$W\f$ is a projection matrix (dimension \f$f \times d\f$)\n - * - \f$x\f$ is the projected data (dimension \f$d < f\f$)\n - * - \f$\mu\f$ is the mean of the data (dimension \f$f\f$)\n - * - \f$\epsilon\f$ is the noise of the data (dimension \f$f\f$) - * Gaussian with zero-mean and covariance matrix \f$\sigma^2 Id\f$ - */ -class EMPCATrainer -{ - public: //api - /** - * @brief Initializes a new EM PCA trainer. The training stage will place the - * resulting components in the linear machine and set it up to - * extract the variable means automatically. - */ - EMPCATrainer(bool compute_likelihood=true); - - /** - * @brief Copy constructor - */ - EMPCATrainer(const EMPCATrainer& other); - - /** - * @brief (virtual) Destructor - */ - virtual ~EMPCATrainer(); - - /** - * @brief Assignment operator - */ - EMPCATrainer& operator=(const EMPCATrainer& other); - - /** - * @brief Equal to - */ - bool operator==(const EMPCATrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const EMPCATrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const EMPCATrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief This methods performs some initialization before the EM loop. - */ - virtual void initialize(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - - /** - * @brief Calculates and saves statistics across the dataset, and saves - * these as m_z_{first,second}_order. - * - * The statistics will be used in the mStep() that follows. - */ - virtual void eStep(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - - /** - * @brief Performs a maximization step to update the parameters of the - * factor analysis model. - */ - virtual void mStep(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - - /** - * @brief Computes the average log likelihood using the current estimates - * of the latent variables. - */ - virtual double computeLikelihood(bob::learn::linear::Machine& machine); - - /** - * @brief Sets \f$\sigma^2\f$ (Mostly for test purpose) - */ - void setSigma2(double sigma2) { m_sigma2 = sigma2; } - - /** - * @brief Gets \f$\sigma^2\f$ (Mostly for test purpose) - */ - double getSigma2() const { return m_sigma2; } - - /** - * @brief Sets the Random Number Generator - */ - void setRng(const boost::shared_ptr<boost::mt19937> rng) - { m_rng = rng; } - - /** - * @brief Gets the Random Number Generator - */ - const boost::shared_ptr<boost::mt19937> getRng() const - { return m_rng; } - - - private: //representation - - bool m_compute_likelihood; - boost::shared_ptr<boost::mt19937> m_rng; - - blitz::Array<double,2> m_S; /// Covariance of the training data (required only if we need to compute the log likelihood) - blitz::Array<double,2> m_z_first_order; /// Current mean of the \f$z_{n}\f$ latent variable - blitz::Array<double,3> m_z_second_order; /// Current covariance of the \f$z_{n}\f$ latent variable - blitz::Array<double,2> m_inW; /// The matrix product \f$W^T W\f$ - blitz::Array<double,2> m_invM; /// The matrix \f$inv(M)\f$, where \f$M = W^T W + \sigma^2 Id\f$ - double m_sigma2; /// The variance \f$sigma^2\f$ of the noise epsilon of the probabilistic model - double m_f_log2pi; /// The constant \f$n_{features} log(2*\pi)\f$ used during the likelihood computation - - // Working arrays - mutable blitz::Array<double,2> m_tmp_dxf; /// size dimensionality x n_features - mutable blitz::Array<double,1> m_tmp_d; /// size dimensionality - mutable blitz::Array<double,1> m_tmp_f; /// size n_features - mutable blitz::Array<double,2> m_tmp_dxd_1; /// size dimensionality x dimensionality - mutable blitz::Array<double,2> m_tmp_dxd_2; /// size dimensionality x dimensionality - mutable blitz::Array<double,2> m_tmp_fxd_1; /// size n_features x dimensionality - mutable blitz::Array<double,2> m_tmp_fxd_2; /// size n_features x dimensionality - mutable blitz::Array<double,2> m_tmp_fxf_1; /// size n_features x n_features - mutable blitz::Array<double,2> m_tmp_fxf_2; /// size n_features x n_features - - - /** - * @brief Initializes/resizes the (array) members - */ - void initMembers(const bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - /** - * @brief Computes the mean and the variance (if required) of the training - * data - */ - void computeMeanVariance(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - /** - * @brief Random initialization of \f$W\f$ and \f$sigma^2\f$. - * W is the projection matrix (from the LinearMachine) - */ - void initRandomWSigma2(bob::learn::linear::Machine& machine); - /** - * @brief Computes the product \f$W^T W\f$. - * \f$W\f$ is the projection matrix (from the LinearMachine) - */ - void computeWtW(bob::learn::linear::Machine& machine); - /** - * @brief Computes the inverse of \f$M\f$ matrix, where - * \f$M = W^T W + \sigma^2 Id\f$. - * \f$W\f$ is the projection matrix (from the LinearMachine) - */ - void computeInvM(); - /** - * @brief M-Step (part 1): Computes the new estimate of \f$W\f$ using the - * new estimated statistics. - */ - void updateW(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); - /** - * @brief M-Step (part 2): Computes the new estimate of \f$\sigma^2\f$ using - * the new estimated statistics. - */ - void updateSigma2(bob::learn::linear::Machine& machine, - const blitz::Array<double,2>& ar); -}; - -} } } // namespaces - -#endif /* BOB_LEARN_EM_EMPCA_TRAINER_H */ diff --git a/bob/learn/em/include/bob.learn.em/FABase.h b/bob/learn/em/include/bob.learn.em/FABase.h deleted file mode 100644 index 3b521989e96e50c27518a7929249abd6f15d9414..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/FABase.h +++ /dev/null @@ -1,293 +0,0 @@ -/** - * @date Tue Jan 27 15:51:15 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief A base class for Factor Analysis - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_FABASE_H -#define BOB_LEARN_EM_FABASE_H - -#include <stdexcept> - -#include <bob.learn.em/GMMMachine.h> -#include <boost/shared_ptr.hpp> - -namespace bob { namespace learn { namespace em { - -/** - * @brief A FA Base class which contains U, V and D matrices - * TODO: add a reference to the journal articles - */ -class FABase -{ - public: - /** - * @brief Default constructor. Builds an otherwise invalid 0 x 0 FABase - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - */ - FABase(); - - /** - * @brief Constructor. Builds a new FABase. - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - * - * @param ubm The Universal Background Model - * @param ru size of U (CD x ru) - * @param rv size of U (CD x rv) - * @warning ru and rv SHOULD BE >= 1. Just set U/V/D to zero if you want - * to ignore one subspace. This is the case for ISV. - */ - FABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1, const size_t rv=1); - - /** - * @brief Copy constructor - */ - FABase(const FABase& other); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~FABase(); - - /** - * @brief Assigns from a different JFA machine - */ - FABase& operator=(const FABase &other); - - /** - * @brief Equal to - */ - bool operator==(const FABase& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const FABase& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const FABase& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Returns the UBM - */ - const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const - { return m_ubm; } - - /** - * @brief Returns the U matrix - */ - const blitz::Array<double,2>& getU() const - { return m_U; } - - /** - * @brief Returns the V matrix - */ - const blitz::Array<double,2>& getV() const - { return m_V; } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) - */ - const blitz::Array<double,1>& getD() const - { return m_d; } - - /** - * @brief Returns the UBM mean supervector (as a 1D vector) - */ - const blitz::Array<double,1>& getUbmMean() const - { return m_cache_mean; } - - /** - * @brief Returns the UBM variance supervector (as a 1D vector) - */ - const blitz::Array<double,1>& getUbmVariance() const - { return m_cache_sigma; } - - /** - * @brief Returns the number of Gaussian components C - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine."); - return m_ubm->getNGaussians(); } - - /** - * @brief Returns the feature dimensionality D - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine."); - return m_ubm->getNInputs(); } - - /** - * @brief Returns the supervector length CD - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { if(!m_ubm) throw std::runtime_error("No UBM was set in the JFA machine."); - return m_ubm->getNInputs()*m_ubm->getNGaussians(); } - - /** - * @brief Returns the size/rank ru of the U matrix - */ - const size_t getDimRu() const - { return m_ru; } - - /** - * @brief Returns the size/rank rv of the V matrix - */ - const size_t getDimRv() const - { return m_rv; } - - /** - * @brief Resets the dimensionality of the subspace U and V - * U and V are hence uninitialized. - */ - void resize(const size_t ru, const size_t rv); - - /** - * @brief Resets the dimensionality of the subspace U and V, - * assuming that no UBM has yet been set - * U and V are hence uninitialized. - */ - void resize(const size_t ru, const size_t rv, const size_t cd); - - /** - * @brief Returns the U matrix in order to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateU() - { return m_U; } - - /** - * @brief Returns the V matrix in order to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateV() - { return m_V; } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order - * to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,1>& updateD() - { return m_d; } - - - /** - * @brief Sets (the mean supervector of) the Universal Background Model - * U, V and d are uninitialized in case of dimensions update (C or D) - */ - void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm); - - /** - * @brief Sets the U matrix - */ - void setU(const blitz::Array<double,2>& U); - - /** - * @brief Sets the V matrix - */ - void setV(const blitz::Array<double,2>& V); - - /** - * @brief Sets the diagonal matrix diag(d) - * (a 1D vector is expected as an argument) - */ - void setD(const blitz::Array<double,1>& d); - - - /** - * @brief Estimates x from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const; - - /** - * @brief Compute and put U^{T}.Sigma^{-1} matrix in cache - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - void updateCacheUbmUVD(); - - - private: - /** - * @brief Update cache arrays/variables - */ - void updateCache(); - /** - * @brief Put GMM mean/variance supervector in cache - */ - void updateCacheUbm(); - /** - * @brief Resize working arrays - */ - void resizeTmp(); - /** - * @brief Computes (Id + U^T.Sigma^-1.U.N_{i,h}.U)^-1 = - * (Id + sum_{c=1..C} N_{i,h}.U_{c}^T.Sigma_{c}^-1.U_{c})^-1 - */ - void computeIdPlusUSProdInv(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,2>& out) const; - /** - * @brief Computes Fn_x = sum_{sessions h}(N*(o - m)) - * (Normalised first order statistics) - */ - void computeFn_x(const bob::learn::em::GMMStats& gmm_stats, - blitz::Array<double,1>& out) const; - /** - * @brief Estimates the value of x from the passed arguments - * (IdPlusUSProdInv and Fn_x), considering the LPT assumption - */ - void estimateX(const blitz::Array<double,2>& IdPlusUSProdInv, - const blitz::Array<double,1>& Fn_x, blitz::Array<double,1>& x) const; - - - // UBM - boost::shared_ptr<bob::learn::em::GMMMachine> m_ubm; - - // dimensionality - size_t m_ru; // size of U (CD x ru) - size_t m_rv; // size of V (CD x rv) - - // U, V, D matrices - // D is assumed to be diagonal, and only the diagonal is stored - blitz::Array<double,2> m_U; - blitz::Array<double,2> m_V; - blitz::Array<double,1> m_d; - - // Vectors/Matrices precomputed in cache - blitz::Array<double,1> m_cache_mean; - blitz::Array<double,1> m_cache_sigma; - blitz::Array<double,2> m_cache_UtSigmaInv; - - mutable blitz::Array<double,2> m_tmp_IdPlusUSProdInv; - mutable blitz::Array<double,1> m_tmp_Fn_x; - mutable blitz::Array<double,1> m_tmp_ru; - mutable blitz::Array<double,2> m_tmp_ruD; - mutable blitz::Array<double,2> m_tmp_ruru; -}; - - -} } } // namespaces - -#endif // BOB_LEARN_EM_FABASE_H diff --git a/bob/learn/em/include/bob.learn.em/FABaseTrainer.h b/bob/learn/em/include/bob.learn.em/FABaseTrainer.h deleted file mode 100644 index 037b4f2a35ab7d2237b0291a7fa75631a5008aa5..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/FABaseTrainer.h +++ /dev/null @@ -1,350 +0,0 @@ -/** - * @date Sat Jan 31 17:16:17 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief FABaseTrainer functions - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_FABASETRAINER_H -#define BOB_LEARN_EM_FABASETRAINER_H - -#include <blitz/array.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.learn.em/JFAMachine.h> -#include <vector> - -#include <map> -#include <string> -#include <bob.core/array_copy.h> -#include <boost/shared_ptr.hpp> -#include <boost/random.hpp> -#include <bob.core/logging.h> - -namespace bob { namespace learn { namespace em { - -class FABaseTrainer -{ - public: - /** - * @brief Constructor - */ - FABaseTrainer(); - - /** - * @brief Copy constructor - */ - FABaseTrainer(const FABaseTrainer& other); - - /** - * @brief Destructor - */ - ~FABaseTrainer(); - - /** - * @brief Check that the dimensionality of the statistics match. - */ - void checkStatistics(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - - /** - * @brief Initialize the dimensionality, the UBM, the sums of the - * statistics and the number of identities. - */ - void initUbmNidSumStatistics(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - - /** - * @brief Precomputes the sums of the zeroth order statistics over the - * sessions for each client - */ - void precomputeSumStatisticsN(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Precomputes the sums of the first order statistics over the - * sessions for each client - */ - void precomputeSumStatisticsF(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - - /** - * @brief Initializes (allocates and sets to zero) the x, y, z speaker - * factors - */ - void initializeXYZ(const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - - /** - * @brief Resets the x, y, z speaker factors to zero values - */ - void resetXYZ(); - - - /**** Y and V functions ****/ - /** - * @brief Computes Vt * diag(sigma)^-1 - */ - void computeVtSigmaInv(const bob::learn::em::FABase& m); - /** - * @brief Computes Vt_{c} * diag(sigma)^-1 * V_{c} for each Gaussian c - */ - void computeVProd(const bob::learn::em::FABase& m); - /** - * @brief Computes (I+Vt*diag(sigma)^-1*Ni*V)^-1 which occurs in the y - * estimation for the given person - */ - void computeIdPlusVProd_i(const size_t id); - /** - * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) - * which occurs in the y estimation of the given person - */ - void computeFn_y_i(const bob::learn::em::FABase& m, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, - const size_t id); - /** - * @brief Updates y_i (of the current person) and the accumulators to - * compute V with the cache values m_cache_IdPlusVprod_i, m_VtSigmaInv and - * m_cache_Fn_y_i - */ - void updateY_i(const size_t id); - /** - * @brief Updates y and the accumulators to compute V - */ - void updateY(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Computes the accumulators m_acc_V_A1 and m_acc_V_A2 for V - * V = A2 * A1^-1 - */ - void computeAccumulatorsV(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Updates V from the accumulators m_acc_V_A1 and m_acc_V_A2 - */ - void updateV(blitz::Array<double,2>& V); - - - /**** X and U functions ****/ - /** - * @brief Computes Ut * diag(sigma)^-1 - */ - void computeUtSigmaInv(const bob::learn::em::FABase& m); - /** - * @brief Computes Ut_{c} * diag(sigma)^-1 * U_{c} for each Gaussian c - */ - void computeUProd(const bob::learn::em::FABase& m); - /** - * @brief Computes (I+Ut*diag(sigma)^-1*Ni*U)^-1 which occurs in the x - * estimation - */ - void computeIdPlusUProd_ih(const boost::shared_ptr<bob::learn::em::GMMStats>& stats); - /** - * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - D*z_{i} - U*x_{i,h}) - * which occurs in the y estimation of the given person - */ - void computeFn_x_ih(const bob::learn::em::FABase& m, - const boost::shared_ptr<bob::learn::em::GMMStats>& stats, const size_t id); - /** - * @brief Updates x_ih (of the current person/session) and the - * accumulators to compute U with the cache values m_cache_IdPlusVprod_i, - * m_VtSigmaInv and m_cache_Fn_y_i - */ - void updateX_ih(const size_t id, const size_t h); - /** - * @brief Updates x - */ - void updateX(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Computes the accumulators m_acc_U_A1 and m_acc_U_A2 for U - * U = A2 * A1^-1 - */ - void computeAccumulatorsU(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Updates U from the accumulators m_acc_U_A1 and m_acc_U_A2 - */ - void updateU(blitz::Array<double,2>& U); - - - /**** z and D functions ****/ - /** - * @brief Computes diag(D) * diag(sigma)^-1 - */ - void computeDtSigmaInv(const bob::learn::em::FABase& m); - /** - * @brief Computes Dt_{c} * diag(sigma)^-1 * D_{c} for each Gaussian c - */ - void computeDProd(const bob::learn::em::FABase& m); - /** - * @brief Computes (I+diag(d)t*diag(sigma)^-1*Ni*diag(d))^-1 which occurs - * in the z estimation for the given person - */ - void computeIdPlusDProd_i(const size_t id); - /** - * @brief Computes sum_{sessions h}(N_{i,h}*(o_{i,h} - m - V*y_{i} - U*x_{i,h}) - * which occurs in the y estimation of the given person - */ - void computeFn_z_i(const bob::learn::em::FABase& m, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& stats, const size_t id); - /** - * @brief Updates z_i (of the current person) and the accumulators to - * compute D with the cache values m_cache_IdPlusDProd_i, m_VtSigmaInv - * and m_cache_Fn_z_i - */ - void updateZ_i(const size_t id); - /** - * @brief Updates z and the accumulators to compute D - */ - void updateZ(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Computes the accumulators m_acc_D_A1 and m_acc_D_A2 for d - * d = A2 * A1^-1 - */ - void computeAccumulatorsD(const bob::learn::em::FABase& m, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& stats); - /** - * @brief Updates d from the accumulators m_acc_D_A1 and m_acc_D_A2 - */ - void updateD(blitz::Array<double,1>& d); - - - /** - * @brief Get the zeroth order statistics - */ - const std::vector<blitz::Array<double,1> >& getNacc() const - { return m_Nacc; } - /** - * @brief Get the first order statistics - */ - const std::vector<blitz::Array<double,1> >& getFacc() const - { return m_Facc; } - /** - * @brief Get the x speaker factors - */ - const std::vector<blitz::Array<double,2> >& getX() const - { return m_x; } - /** - * @brief Get the y speaker factors - */ - const std::vector<blitz::Array<double,1> >& getY() const - { return m_y; } - /** - * @brief Get the z speaker factors - */ - const std::vector<blitz::Array<double,1> >& getZ() const - { return m_z; } - /** - * @brief Set the x speaker factors - */ - void setX(const std::vector<blitz::Array<double,2> >& X) - { m_x = X; } - /** - * @brief Set the y speaker factors - */ - void setY(const std::vector<blitz::Array<double,1> >& y) - { m_y = y; } - /** - * @brief Set the z speaker factors - */ - void setZ(const std::vector<blitz::Array<double,1> >& z) - { m_z = z; } - - /** - * @brief Initializes the cache to process the given statistics - */ - void initCache(); - - /** - * @brief Getters for the accumulators - */ - const blitz::Array<double,3>& getAccVA1() const - { return m_acc_V_A1; } - const blitz::Array<double,2>& getAccVA2() const - { return m_acc_V_A2; } - const blitz::Array<double,3>& getAccUA1() const - { return m_acc_U_A1; } - const blitz::Array<double,2>& getAccUA2() const - { return m_acc_U_A2; } - const blitz::Array<double,1>& getAccDA1() const - { return m_acc_D_A1; } - const blitz::Array<double,1>& getAccDA2() const - { return m_acc_D_A2; } - - /** - * @brief Setters for the accumulators, Very useful if the e-Step needs - * to be parallelized. - */ - void setAccVA1(const blitz::Array<double,3>& acc) - { bob::core::array::assertSameShape(acc, m_acc_V_A1); - m_acc_V_A1 = acc; } - void setAccVA2(const blitz::Array<double,2>& acc) - { bob::core::array::assertSameShape(acc, m_acc_V_A2); - m_acc_V_A2 = acc; } - void setAccUA1(const blitz::Array<double,3>& acc) - { bob::core::array::assertSameShape(acc, m_acc_U_A1); - m_acc_U_A1 = acc; } - void setAccUA2(const blitz::Array<double,2>& acc) - { bob::core::array::assertSameShape(acc, m_acc_U_A2); - m_acc_U_A2 = acc; } - void setAccDA1(const blitz::Array<double,1>& acc) - { bob::core::array::assertSameShape(acc, m_acc_D_A1); - m_acc_D_A1 = acc; } - void setAccDA2(const blitz::Array<double,1>& acc) - { bob::core::array::assertSameShape(acc, m_acc_D_A2); - m_acc_D_A2 = acc; } - - - private: - size_t m_Nid; // Number of identities - size_t m_dim_C; // Number of Gaussian components of the UBM GMM - size_t m_dim_D; // Dimensionality of the feature space - size_t m_dim_ru; // Rank of the U subspace - size_t m_dim_rv; // Rank of the V subspace - - std::vector<blitz::Array<double,2> > m_x; // matrix x of speaker factors for eigenchannels U, for each client - std::vector<blitz::Array<double,1> > m_y; // vector y of spealer factors for eigenvoices V, for each client - std::vector<blitz::Array<double,1> > m_z; // vector z of spealer factors for eigenvoices Z, for each client - - std::vector<blitz::Array<double,1> > m_Nacc; // Sum of the zeroth order statistics over the sessions for each client, dimension C - std::vector<blitz::Array<double,1> > m_Facc; // Sum of the first order statistics over the sessions for each client, dimension CD - - // Accumulators for the M-step - blitz::Array<double,3> m_acc_V_A1; - blitz::Array<double,2> m_acc_V_A2; - blitz::Array<double,3> m_acc_U_A1; - blitz::Array<double,2> m_acc_U_A2; - blitz::Array<double,1> m_acc_D_A1; - blitz::Array<double,1> m_acc_D_A2; - - // Cache/Precomputation - blitz::Array<double,2> m_cache_VtSigmaInv; // Vt * diag(sigma)^-1 - blitz::Array<double,3> m_cache_VProd; // first dimension is the Gaussian id - blitz::Array<double,2> m_cache_IdPlusVProd_i; - blitz::Array<double,1> m_cache_Fn_y_i; - - blitz::Array<double,2> m_cache_UtSigmaInv; // Ut * diag(sigma)^-1 - blitz::Array<double,3> m_cache_UProd; // first dimension is the Gaussian id - blitz::Array<double,2> m_cache_IdPlusUProd_ih; - blitz::Array<double,1> m_cache_Fn_x_ih; - - blitz::Array<double,1> m_cache_DtSigmaInv; // Dt * diag(sigma)^-1 - blitz::Array<double,1> m_cache_DProd; // supervector length dimension - blitz::Array<double,1> m_cache_IdPlusDProd_i; - blitz::Array<double,1> m_cache_Fn_z_i; - - // Working arrays - mutable blitz::Array<double,2> m_tmp_ruru; - mutable blitz::Array<double,2> m_tmp_ruD; - mutable blitz::Array<double,2> m_tmp_rvrv; - mutable blitz::Array<double,2> m_tmp_rvD; - mutable blitz::Array<double,1> m_tmp_rv; - mutable blitz::Array<double,1> m_tmp_ru; - mutable blitz::Array<double,1> m_tmp_CD; - mutable blitz::Array<double,1> m_tmp_CD_b; -}; - - -} } } // namespaces - -#endif /* BOB_LEARN_EM_FABASETRAINER_H */ diff --git a/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h b/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h deleted file mode 100644 index 9160c6b0a9b34b6480445c5175637de3970fd5c0..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/GMMBaseTrainer.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * @brief This class implements the E-step of the expectation-maximisation algorithm for a GMM Machine. - * @details See Section 9.2.2 of Bishop, "Pattern recognition and machine learning", 2006 - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_GMMBASETRAINER_H -#define BOB_LEARN_EM_GMMBASETRAINER_H - -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/GMMStats.h> -#include <limits> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements the E-step of the expectation-maximisation - * algorithm for a GMM Machine. - * @details See Section 9.2.2 of Bishop, - * "Pattern recognition and machine learning", 2006 - */ -class GMMBaseTrainer -{ - public: - /** - * @brief Default constructor - */ - GMMBaseTrainer(const bool update_means=true, - const bool update_variances=false, - const bool update_weights=false, - const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon()); - - /** - * @brief Copy constructor - */ - GMMBaseTrainer(const GMMBaseTrainer& other); - - /** - * @brief Destructor - */ - virtual ~GMMBaseTrainer(); - - /** - * @brief Initialization before the EM steps - */ - void initialize(bob::learn::em::GMMMachine& gmm); - - /** - * @brief Calculates and saves statistics across the dataset, - * and saves these as m_ss. Calculates the average - * log likelihood of the observations given the GMM, - * and returns this in average_log_likelihood. - * - * The statistics, m_ss, will be used in the mStep() that follows. - * Implements EMTrainer::eStep(double &) - */ - void eStep(bob::learn::em::GMMMachine& gmm, - const blitz::Array<double,2>& data); - - /** - * @brief Computes the likelihood using current estimates of the latent - * variables - */ - double computeLikelihood(bob::learn::em::GMMMachine& gmm); - - - /** - * @brief Assigns from a different GMMBaseTrainer - */ - GMMBaseTrainer& operator=(const GMMBaseTrainer &other); - - /** - * @brief Equal to - */ - bool operator==(const GMMBaseTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const GMMBaseTrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const GMMBaseTrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Returns the internal GMM statistics. Useful to parallelize the - * E-step - */ - const boost::shared_ptr<bob::learn::em::GMMStats> getGMMStats() const - { return m_ss; } - - /** - * @brief Sets the internal GMM statistics. Useful to parallelize the - * E-step - */ - void setGMMStats(boost::shared_ptr<bob::learn::em::GMMStats> stats); - - /** - * update means on each iteration - */ - bool getUpdateMeans() - {return m_update_means;} - - /** - * update variances on each iteration - */ - bool getUpdateVariances() - {return m_update_variances;} - - - bool getUpdateWeights() - {return m_update_weights;} - - - double getMeanVarUpdateResponsibilitiesThreshold() - {return m_mean_var_update_responsibilities_threshold;} - - - private: - - /** - * These are the sufficient statistics, calculated during the - * E-step and used during the M-step - */ - boost::shared_ptr<bob::learn::em::GMMStats> m_ss; - - - /** - * update means on each iteration - */ - bool m_update_means; - - /** - * update variances on each iteration - */ - bool m_update_variances; - - /** - * update weights on each iteration - */ - bool m_update_weights; - - /** - * threshold over the responsibilities of the Gaussians - * Equations 9.24, 9.25 of Bishop, "Pattern recognition and machine learning", 2006 - * require a division by the responsibilities, which might be equal to zero - * because of numerical issue. This threshold is used to avoid such divisions. - */ - double m_mean_var_update_responsibilities_threshold; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_GMMBASETRAINER_H diff --git a/bob/learn/em/include/bob.learn.em/GMMMachine.h b/bob/learn/em/include/bob.learn.em/GMMMachine.h deleted file mode 100644 index 0a46f822b22acce84d9faa3914729a1e48b7d115..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/GMMMachine.h +++ /dev/null @@ -1,380 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief This class implements a multivariate diagonal Gaussian distribution. - * @details See Section 2.3.9 of Bishop, "Pattern recognition and machine learning", 2006 - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_GMMMACHINE_H -#define BOB_LEARN_EM_GMMMACHINE_H - -#include <bob.learn.em/Gaussian.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.io.base/HDF5File.h> -#include <iostream> -#include <boost/shared_ptr.hpp> -#include <vector> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements a multivariate diagonal Gaussian distribution. - * @details See Section 2.3.9 of Bishop, "Pattern recognition and machine learning", 2006 - */ -class GMMMachine -{ - public: - /** - * Default constructor - */ - GMMMachine(); - - /** - * Constructor - * @param[in] n_gaussians The number of Gaussian components - * @param[in] n_inputs The feature dimensionality - */ - GMMMachine(const size_t n_gaussians, const size_t n_inputs); - - /** - * Copy constructor - * (Needed because the GMM points to its constituent Gaussian members) - */ - GMMMachine(const GMMMachine& other); - - /** - * Constructor from a Configuration - */ - GMMMachine(bob::io::base::HDF5File& config); - - /** - * Assignment - */ - GMMMachine& operator=(const GMMMachine &other); - - /** - * Equal to - */ - bool operator==(const GMMMachine& b) const; - - /** - * Not equal to - */ - bool operator!=(const GMMMachine& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const GMMMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * Destructor - */ - virtual ~GMMMachine(); - - - /** - * Reset the input dimensionality, and the number of Gaussian components. - * Initialises the weights to uniform distribution. - * @param n_gaussians The number of Gaussian components - * @param n_inputs The feature dimensionality - */ - void resize(const size_t n_gaussians, const size_t n_inputs); - - - ///////////////////////// - // Getters - //////////////////////// - - /** - * Get number of inputs - */ - size_t getNInputs() const - { return m_n_inputs; } - - /** - * Get the weights ("mixing coefficients") of the Gaussian components - */ - const blitz::Array<double,1>& getWeights() const - { return m_weights; } - - /** - * Get the logarithm of the weights of the Gaussian components - */ - inline const blitz::Array<double,1>& getLogWeights() const - { return m_cache_log_weights; } - - - /** - * Get the means - */ - const blitz::Array<double,2> getMeans() const; - - /** - * Get the mean supervector - */ - void getMeanSupervector(blitz::Array<double,1> &mean_supervector) const; - - /** - * Returns a const reference to the supervector (Put in cache) - */ - const blitz::Array<double,1>& getMeanSupervector() const; - - /** - * Get the variances - */ - const blitz::Array<double,2> getVariances() const; - - /** - * Returns a const reference to the supervector (Put in cache) - */ - const blitz::Array<double,1>& getVarianceSupervector() const; - - - /** - * Get the variance flooring thresholds for each Gaussian in each dimension - */ - const blitz::Array<double,2> getVarianceThresholds() const; - - - - /////////////////////// - // Setters - /////////////////////// - - /** - * Set the weights - */ - void setWeights(const blitz::Array<double,1> &weights); - - /** - * Set the means - */ - void setMeans(const blitz::Array<double,2> &means); - /** - * Set the means from a supervector - */ - void setMeanSupervector(const blitz::Array<double,1> &mean_supervector); - - /** - * Set the variances - */ - void setVariances(const blitz::Array<double,2> &variances); - /** - * Set the variances from a supervector - */ - void setVarianceSupervector(const blitz::Array<double,1> &variance_supervector); - - /** - * Set the variance flooring thresholds in each dimension - */ - void setVarianceThresholds(const double value); - /** - * Set the variance flooring thresholds in each dimension - * (equal for all Gaussian components) - */ - void setVarianceThresholds(blitz::Array<double,1> variance_thresholds); - /** - * Set the variance flooring thresholds for each Gaussian in each dimension - */ - void setVarianceThresholds(const blitz::Array<double,2> &variance_thresholds); - - - //////////////// - // Methods - ///////////////// - - /** - * Get the weights in order to be updated - * ("mixing coefficients") of the Gaussian components - * @warning Only trainers should use this function for efficiency reason - */ - inline blitz::Array<double,1>& updateWeights() - { return m_weights; } - - - /** - * Update the log of the weights in cache - * @warning Should be used by trainer only when using updateWeights() - */ - void recomputeLogWeights() const; - - - - /** - * Output the log likelihood of the sample, x, i.e. log(p(x|GMMMachine)) - * @param[in] x The sample - * @param[out] log_weighted_gaussian_likelihoods For each Gaussian, i: log(weight_i*p(x|Gaussian_i)) - * @return The GMMMachine log likelihood, i.e. log(p(x|GMMMachine)) - * Dimensions of the parameters are checked - */ - double logLikelihood(const blitz::Array<double, 1> &x, blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const; - - /** - * Output the log likelihood of the sample, x, i.e. log(p(x|GMMMachine)) - * @param[in] x The sample - * @param[out] log_weighted_gaussian_likelihoods For each Gaussian, i: log(weight_i*p(x|Gaussian_i)) - * @return The GMMMachine log likelihood, i.e. log(p(x|GMMMachine)) - * @warning Dimensions of the parameters are not checked - */ - double logLikelihood_(const blitz::Array<double, 1> &x, blitz::Array<double,1> &log_weighted_gaussian_likelihoods) const; - - /** - * Output the log likelihood of the sample, x, i.e. log(p(x|GMM)) - * @param[in] x The sample - * Dimension of the input is checked - */ - double logLikelihood(const blitz::Array<double, 1> &x) const; - - - /** - * Output the averaged log likelihood of a set of samples, x, i.e. log(p(x|GMM)) - * @param[in] x The sample - * Dimension of the input is checked - */ - double logLikelihood(const blitz::Array<double, 2> &x) const; - - - /** - * Output the log likelihood of the sample, x, i.e. log(p(x|GMM)) - * @param[in] x The sample - * @warning Dimension of the input is not checked - */ - double logLikelihood_(const blitz::Array<double, 1> &x) const; - - /** - * Accumulates the GMM statistics over a set of samples. - * @see bool accStatistics(const blitz::Array<double,1> &x, GMMStats stats) - * Dimensions of the parameters are checked - */ - void accStatistics(const blitz::Array<double,2>& input, GMMStats &stats) const; - - /** - * Accumulates the GMM statistics over a set of samples. - * @see bool accStatistics(const blitz::Array<double,1> &x, GMMStats stats) - * @warning Dimensions of the parameters are not checked - */ - void accStatistics_(const blitz::Array<double,2>& input, GMMStats &stats) const; - - /** - * Accumulate the GMM statistics for this sample. - * - * @param[in] x The current sample - * @param[out] stats The accumulated statistics - * Dimensions of the parameters are checked - */ - void accStatistics(const blitz::Array<double,1> &x, GMMStats &stats) const; - - /** - * Accumulate the GMM statistics for this sample. - * - * @param[in] x The current sample - * @param[out] stats The accumulated statistics - * @warning Dimensions of the parameters are not checked - */ - void accStatistics_(const blitz::Array<double,1> &x, GMMStats &stats) const; - - - /** - * Get a pointer to a particular Gaussian component - * @param[in] i The index of the Gaussian component - * @return A smart pointer to the i'th Gaussian component - * if it exists, otherwise throws an exception - */ - boost::shared_ptr<bob::learn::em::Gaussian> getGaussian(const size_t i); - - - /** - * Return the number of Gaussian components - */ - inline size_t getNGaussians() const - { return m_n_gaussians; } - - /** - * Save to a Configuration - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * Load from a Configuration - */ - void load(bob::io::base::HDF5File& config); - - /** - * Load/Reload mean/variance supervector in cache - */ - void reloadCacheSupervectors() const; - - friend std::ostream& operator<<(std::ostream& os, const GMMMachine& machine); - - - private: - /** - * Copy another GMMMachine - */ - void copy(const GMMMachine&); - - /** - * The number of Gaussian components - */ - size_t m_n_gaussians; - - /** - * The feature dimensionality - */ - size_t m_n_inputs; - - /** - * The Gaussian components - */ - std::vector<boost::shared_ptr<Gaussian> > m_gaussians; - - /** - * The weights (also known as "mixing coefficients") - */ - blitz::Array<double,1> m_weights; - - /** - * Update the mean and variance supervectors - * in cache (into a 1D blitz array) - */ - void updateCacheSupervectors() const; - - /** - * Initialise the cache members (allocate arrays) - */ - void initCache() const; - - /** - * Accumulate the GMM statistics for this sample. - * Called by accStatistics() and accStatistics_() - * - * @param[in] x The current sample - * @param[out] stats The accumulated statistics - * @param[in] log_likelihood The current log_likelihood - * @warning Dimensions of the parameters are not checked - */ - void accStatisticsInternal(const blitz::Array<double,1> &x, - GMMStats &stats, const double log_likelihood) const; - - - /// Some cache arrays to avoid re-allocation when computing log-likelihoods - mutable blitz::Array<double,1> m_cache_log_weights; - mutable blitz::Array<double,1> m_cache_log_weighted_gaussian_likelihoods; - mutable blitz::Array<double,1> m_cache_P; - mutable blitz::Array<double,2> m_cache_Px; - - mutable blitz::Array<double,1> m_cache_mean_supervector; - mutable blitz::Array<double,1> m_cache_variance_supervector; - mutable bool m_cache_supervector; - -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_GMMMACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/GMMStats.h b/bob/learn/em/include/bob.learn.em/GMMStats.h deleted file mode 100644 index af56f05ce1d6a4285d4bd3d43f0ac7b9561214a9..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/GMMStats.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_GMMSTATS_H -#define BOB_LEARN_EM_GMMSTATS_H - -#include <blitz/array.h> -#include <bob.io.base/HDF5File.h> - -namespace bob { namespace learn { namespace em { - -/** - * @brief A container for GMM statistics. - * @see GMMMachine - * - * With respect to Reynolds, "Speaker Verification Using Adapted - * Gaussian Mixture Models", DSP, 2000: - * Eq (8) is n(i) - * Eq (9) is sumPx(i) / n(i) - * Eq (10) is sumPxx(i) / n(i) - */ -class GMMStats { - public: - - /** - * Default constructor. - */ - GMMStats(); - - /** - * Constructor. - * @param n_gaussians Number of Gaussians in the mixture model. - * @param n_inputs Feature dimensionality. - */ - GMMStats(const size_t n_gaussians, const size_t n_inputs); - - /** - * Copy constructor - */ - GMMStats(const GMMStats& other); - - /** - * Constructor (from a Configuration) - */ - GMMStats(bob::io::base::HDF5File& config); - - /** - * Assigment - */ - GMMStats& operator=(const GMMStats& other); - - /** - * Equal to - */ - bool operator==(const GMMStats& b) const; - - /** - * Not Equal to - */ - bool operator!=(const GMMStats& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const GMMStats& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * Updates a GMMStats with another GMMStats - */ - void operator+=(const GMMStats& b); - - /** - * Destructor - */ - ~GMMStats(); - - /** - * Allocates space for the statistics and resets to zero. - * @param n_gaussians Number of Gaussians in the mixture model. - * @param n_inputs Feature dimensionality. - */ - void resize(const size_t n_gaussians, const size_t n_inputs); - - /** - * Resets statistics to zero. - */ - void init(); - - /** - * The accumulated log likelihood of all samples - */ - double log_likelihood; - - /** - * The accumulated number of samples - */ - size_t T; - - /** - * For each Gaussian, the accumulated sum of responsibilities, i.e. the sum of P(gaussian_i|x) - */ - blitz::Array<double,1> n; - - /** - * For each Gaussian, the accumulated sum of responsibility times the sample - */ - blitz::Array<double,2> sumPx; - - /** - * For each Gaussian, the accumulated sum of responsibility times the sample squared - */ - blitz::Array<double,2> sumPxx; - - /** - * Save to a Configuration - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * Load from a Configuration - */ - void load(bob::io::base::HDF5File& config); - - friend std::ostream& operator<<(std::ostream& os, const GMMStats& g); - - private: - /** - * Copy another GMMStats - */ - void copy(const GMMStats&); -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_GMMSTATS_H diff --git a/bob/learn/em/include/bob.learn.em/Gaussian.h b/bob/learn/em/include/bob.learn.em/Gaussian.h deleted file mode 100644 index 023f0080b1d2fe7a56c18e114b748d0ac629fcd6..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/Gaussian.h +++ /dev/null @@ -1,247 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_GAUSSIAN_H -#define BOB_LEARN_EM_GAUSSIAN_H - -#include <bob.io.base/HDF5File.h> -#include <blitz/array.h> -#include <limits> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements a multivariate diagonal Gaussian distribution. - */ -class Gaussian -{ - public: - /** - * Default constructor - */ - Gaussian(); - - /** - * Constructor - * @param[in] n_inputs The feature dimensionality - */ - Gaussian(const size_t n_inputs); - - /** - * Destructor - */ - virtual ~Gaussian(); - - /** - * Copy constructor - */ - Gaussian(const Gaussian& other); - - /** - * Constructs from a configuration file - */ - Gaussian(bob::io::base::HDF5File& config); - - /** - * Assignment - */ - Gaussian& operator=(const Gaussian &other); - - /** - * Equal to - */ - bool operator==(const Gaussian& b) const; - /** - * Not equal to - */ - bool operator!=(const Gaussian& b) const; - /** - * @brief Similar to - */ - bool is_similar_to(const Gaussian& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * Set the input dimensionality, reset the mean to zero - * and the variance to one. - * @see resize() - * @param n_inputs The feature dimensionality - * @warning The mean and variance are not initialized - */ - void setNInputs(const size_t n_inputs); - - /** - * Get the input dimensionality - */ - size_t getNInputs() const - { return m_n_inputs; } - - /** - * Set the input dimensionality, reset the mean to zero - * and the variance to one. - * @see setNInputs() - * @param n_inputs The feature dimensionality - */ - void resize(const size_t n_inputs); - - /** - * Get the mean - */ - inline const blitz::Array<double,1>& getMean() const - { return m_mean; } - - /** - * Get the mean in order to be updated - * @warning Only trainers should use this function for efficiency reason - */ - inline blitz::Array<double,1>& updateMean() - { return m_mean; } - - /** - * Set the mean - */ - void setMean(const blitz::Array<double,1> &mean); - - /** - * Get the variance (the diagonal of the covariance matrix) - */ - inline const blitz::Array<double,1>& getVariance() const - { return m_variance; } - - /** - * Get the variance in order to be updated - * @warning Only trainers should use this function for efficiency reason - */ - inline blitz::Array<double,1>& updateVariance() - { return m_variance; } - - /** - * Set the variance - */ - void setVariance(const blitz::Array<double,1> &variance); - - /** - * Get the variance flooring thresholds - */ - const blitz::Array<double,1>& getVarianceThresholds() const - { return m_variance_thresholds; } - - /** - * Get the variance thresholds in order to be updated - * @warning Only trainers should use this function for efficiency reason - */ - inline blitz::Array<double,1>& updateVarianceThreshods() - { return m_variance_thresholds; } - - /** - * Set the variance flooring thresholds - */ - void setVarianceThresholds(const blitz::Array<double,1> &variance_thresholds); - - /** - * Set the variance flooring thresholds - */ - void setVarianceThresholds(const double value); - - /** - * Apply the variance flooring thresholds - * This method is called when using setVarianceThresholds() - * @warning It is only useful when using updateVarianceThreshods(), - * and should mostly be done by trainers - */ - void applyVarianceThresholds(); - - /** - * Output the log likelihood of the sample, x - * @param x The data sample (feature vector) - */ - double logLikelihood(const blitz::Array<double,1>& x) const; - - /** - * Output the log likelihood of the sample, x - * @param x The data sample (feature vector) - * @warning The input is NOT checked - */ - double logLikelihood_(const blitz::Array<double,1>& x) const; - - /** - * Saves to a Configuration - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * Loads from a Configuration - */ - void load(bob::io::base::HDF5File& config); - - /** - * Prints a Gaussian in the output stream - */ - friend std::ostream& operator<<(std::ostream& os, const bob::learn::em::Gaussian& g); - - - private: - /** - * Copies another Gaussian - */ - void copy(const Gaussian& other); - - /** - * Computes n_inputs * log(2*pi) - */ - void preComputeNLog2Pi(); - - /** - * Computes and stores the value of g_norm, - * to later speed up evaluation of logLikelihood() - * Note: g_norm is defined as follows: - * log(Gaussian pdf) = log(1/((2pi)^(k/2)(det)^(1/2)) * exp(...)) - * = -1/2 * g_norm * (...) - */ - void preComputeConstants(); - - /** - * The mean vector of the Gaussian - */ - blitz::Array<double,1> m_mean; - - /** - * The diagonal of the covariance matrix (assumed to be diagonal) - */ - blitz::Array<double,1> m_variance; - - /** - * The variance flooring thresholds, i.e. the minimum allowed - * value of variance in each dimension. - * The variance will be set to this value if an attempt is made - * to set it to a smaller value. - */ - blitz::Array<double,1> m_variance_thresholds; - - /** - * A constant that depends only on the feature dimensionality - * m_n_log2pi = n_inputs * log(2*pi) (used to compute m_gnorm) - */ - double m_n_log2pi; - - /** - * A constant that depends only on the feature dimensionality - * (m_n_inputs) and the variance - * @see bool preComputeConstants() - */ - double m_g_norm; - - /** - * The number of inputs (feature dimensionality) - */ - size_t m_n_inputs; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_GAUSSIAN_H diff --git a/bob/learn/em/include/bob.learn.em/ISVBase.h b/bob/learn/em/include/bob.learn.em/ISVBase.h deleted file mode 100644 index 477d1972e44a59e6f8d38705256e71162ab46353..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/ISVBase.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @date Tue Jan 27 16:02:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief A base class for Joint Factor Analysis-like machines - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_ISVBASE_H -#define BOB_LEARN_EM_ISVBASE_H - -#include <stdexcept> - -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/FABase.h> - -#include <bob.io.base/HDF5File.h> -#include <boost/shared_ptr.hpp> - -namespace bob { namespace learn { namespace em { - - -/** - * @brief An ISV Base class which contains U and D matrices - * TODO: add a reference to the journal articles - */ -class ISVBase -{ - public: - /** - * @brief Default constructor. Builds an otherwise invalid 0 x 0 ISVBase - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - */ - ISVBase(); - - /** - * @brief Constructor. Builds a new ISVBase. - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - * - * @param ubm The Universal Background Model - * @param ru size of U (CD x ru) - * @warning ru SHOULD BE >= 1. - */ - ISVBase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1); - - /** - * @brief Copy constructor - */ - ISVBase(const ISVBase& other); - - /** - * @deprecated Starts a new JFAMachine from an existing Configuration object. - */ - ISVBase(bob::io::base::HDF5File& config); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~ISVBase(); - - /** - * @brief Assigns from a different JFA machine - */ - ISVBase& operator=(const ISVBase &other); - - /** - * @brief Equal to - */ - bool operator==(const ISVBase& b) const - { return m_base.operator==(b.m_base); } - - /** - * @brief Not equal to - */ - bool operator!=(const ISVBase& b) const - { return m_base.operator!=(b.m_base); } - - /** - * @brief Similar to - */ - bool is_similar_to(const ISVBase& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const - { return m_base.is_similar_to(b.m_base, r_epsilon, a_epsilon); } - - /** - * @brief Saves machine to an HDF5 file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Loads data from an existing configuration object. Resets - * the current state. - */ - void load(bob::io::base::HDF5File& config); - - /** - * @brief Returns the UBM - */ - const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const - { return m_base.getUbm(); } - - /** - * @brief Returns the U matrix - */ - const blitz::Array<double,2>& getU() const - { return m_base.getU(); } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) - */ - const blitz::Array<double,1>& getD() const - { return m_base.getD(); } - - /** - * @brief Returns the number of Gaussian components C - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { return m_base.getNGaussians(); } - - /** - * @brief Returns the feature dimensionality D - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { return m_base.getNInputs(); } - - /** - * @brief Returns the supervector length CD - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { return m_base.getSupervectorLength(); } - - /** - * @brief Returns the size/rank ru of the U matrix - */ - const size_t getDimRu() const - { return m_base.getDimRu(); } - - /** - * @brief Resets the dimensionality of the subspace U - * U is hence uninitialized. - */ - void resize(const size_t ru) - { m_base.resize(ru, 1); - blitz::Array<double,2>& V = m_base.updateV(); - V = 0; - } - - /** - * @brief Returns the U matrix in order to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateU() - { return m_base.updateU(); } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order - * to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,1>& updateD() - { return m_base.updateD(); } - - - /** - * @brief Sets (the mean supervector of) the Universal Background Model - * U, V and d are uninitialized in case of dimensions update (C or D) - */ - void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm) - { m_base.setUbm(ubm); } - - /** - * @brief Sets the U matrix - */ - void setU(const blitz::Array<double,2>& U) - { m_base.setU(U); } - - /** - * @brief Sets the diagonal matrix diag(d) - * (a 1D vector is expected as an argument) - */ - void setD(const blitz::Array<double,1>& d) - { m_base.setD(d); } - - /** - * @brief Estimates x from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const - { m_base.estimateX(gmm_stats, x); } - - /** - * @brief Precompute (put U^{T}.Sigma^{-1} matrix in cache) - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - void precompute() - { m_base.updateCacheUbmUVD(); } - - /** - * @brief Returns the FABase member - */ - const bob::learn::em::FABase& getBase() const - { return m_base; } - - - private: - // FABase - bob::learn::em::FABase m_base; -}; - - -} } } // namespaces - -#endif // BOB_LEARN_EM_JFABASE_H diff --git a/bob/learn/em/include/bob.learn.em/ISVMachine.h b/bob/learn/em/include/bob.learn.em/ISVMachine.h deleted file mode 100644 index f51f4dbf008638e91cd3cc850ba0f9f9e6cff172..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/ISVMachine.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * @date Tue Jan 27 16:06:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief A base class for Joint Factor Analysis-like machines - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_ISVMACHINE_H -#define BOB_LEARN_EM_ISVMACHINE_H - -#include <stdexcept> - -#include <bob.learn.em/ISVBase.h> -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/LinearScoring.h> - -#include <bob.io.base/HDF5File.h> -#include <boost/shared_ptr.hpp> - -namespace bob { namespace learn { namespace em { - - -/** - * @brief A ISVMachine which is associated to a ISVBase that contains - * U D matrices. - * TODO: add a reference to the journal articles - */ -class ISVMachine -{ - public: - /** - * @brief Default constructor. Builds an otherwise invalid 0 x 0 ISVMachine - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - */ - ISVMachine(); - - /** - * @brief Constructor. Builds a new ISVMachine. - * - * @param isv_base The ISVBase associated with this machine - */ - ISVMachine(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base); - - /** - * @brief Copy constructor - */ - ISVMachine(const ISVMachine& other); - - /** - * @brief Starts a new ISVMachine from an existing Configuration object. - */ - ISVMachine(bob::io::base::HDF5File& config); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~ISVMachine(); - - /** - * @brief Assigns from a different ISV machine - */ - ISVMachine& operator=(const ISVMachine &other); - - /** - * @brief Equal to - */ - bool operator==(const ISVMachine& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const ISVMachine& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const ISVMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Saves machine to an HDF5 file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Loads data from an existing configuration object. Resets - * the current state. - */ - void load(bob::io::base::HDF5File& config); - - - /** - * @brief Returns the number of Gaussian components C - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { return m_isv_base->getNGaussians(); } - - /** - * @brief Returns the feature dimensionality D - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { return m_isv_base->getNInputs(); } - - /** - * @brief Returns the supervector length CD - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { return m_isv_base->getSupervectorLength(); } - - /** - * @brief Returns the size/rank ru of the U matrix - */ - const size_t getDimRu() const - { return m_isv_base->getDimRu(); } - - /** - * @brief Returns the x session factor - */ - const blitz::Array<double,1>& getX() const - { return m_cache_x; } - - /** - * @brief Returns the z speaker factor - */ - const blitz::Array<double,1>& getZ() const - { return m_z; } - - /** - * @brief Returns the z speaker factors in order to update it - */ - blitz::Array<double,1>& updateZ() - { return m_z; } - - /** - * @brief Returns the V matrix - */ - void setZ(const blitz::Array<double,1>& z); - - - /** - * @brief Sets the session variable - */ - void setX(const blitz::Array<double,1>& x); - - - /** - * @brief Returns the ISVBase - */ - const boost::shared_ptr<bob::learn::em::ISVBase> getISVBase() const - { return m_isv_base; } - - /** - * @brief Sets the ISVBase - */ - void setISVBase(const boost::shared_ptr<bob::learn::em::ISVBase> isv_base); - - - /** - * @brief Estimates x from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const - { m_isv_base->estimateX(gmm_stats, x); } - /** - * @brief Estimates Ux from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateUx(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& Ux); - - /** - * @brief Execute the machine - * - * @param input input data used by the machine - * @warning Inputs are checked - * @return score value computed by the machine - */ - double forward(const bob::learn::em::GMMStats& input); - /** - * @brief Computes a score for the given UBM statistics and given the - * Ux vector - */ - double forward(const bob::learn::em::GMMStats& gmm_stats, - const blitz::Array<double,1>& Ux); - - /** - * @brief Execute the machine - * - * @param input input data used by the machine - * @warning Inputs are NOT checked - * @return score value computed by the machine - */ - double forward_(const bob::learn::em::GMMStats& input); - - private: - /** - * @brief Resize latent variable according to the ISVBase - */ - void resize(); - /** - * @ Update cache - */ - void updateCache(); - /** - * @brief Resize working arrays - */ - void resizeTmp(); - - // UBM - boost::shared_ptr<bob::learn::em::ISVBase> m_isv_base; - - // y and z vectors/factors learned during the enrollment procedure - blitz::Array<double,1> m_z; - - // cache - blitz::Array<double,1> m_cache_mDz; - mutable blitz::Array<double,1> m_cache_x; - - // x vector/factor in cache when computing scores - mutable blitz::Array<double,1> m_tmp_Ux; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_ISVMACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/ISVTrainer.h b/bob/learn/em/include/bob.learn.em/ISVTrainer.h deleted file mode 100644 index ae8f0087aa8fe9526719bc8c49d750fc291e2a9b..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/ISVTrainer.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @date Tue Jul 19 12:16:17 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief JFA functions - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_ISVTRAINER_H -#define BOB_LEARN_EM_ISVTRAINER_H - -#include <blitz/array.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.learn.em/FABaseTrainer.h> -#include <bob.learn.em/ISVMachine.h> -#include <vector> - -#include <map> -#include <string> -#include <bob.core/array_copy.h> -#include <boost/shared_ptr.hpp> -#include <boost/random.hpp> -#include <bob.core/logging.h> - -namespace bob { namespace learn { namespace em { - -class ISVTrainer -{ - public: - /** - * @brief Constructor - */ - ISVTrainer(const double relevance_factor=4.); - - /** - * @brief Copy onstructor - */ - ISVTrainer(const ISVTrainer& other); - - /** - * @brief Destructor - */ - virtual ~ISVTrainer(); - - /** - * @brief Assignment operator - */ - ISVTrainer& operator=(const ISVTrainer& other); - - /** - * @brief Equal to - */ - bool operator==(const ISVTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const ISVTrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const ISVTrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief This methods performs some initialization before the EM loop. - */ - virtual void initialize(bob::learn::em::ISVBase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - - /** - * @brief Calculates and saves statistics across the dataset - * The statistics will be used in the mStep() that follows. - */ - virtual void eStep(bob::learn::em::ISVBase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - - /** - * @brief Performs a maximization step to update the parameters of the - * factor analysis model. - */ - virtual void mStep(bob::learn::em::ISVBase& machine); - - /** - * @brief Computes the average log likelihood using the current estimates - * of the latent variables. - */ - virtual double computeLikelihood(bob::learn::em::ISVBase& machine); - - /** - * @brief Enrol a client - */ - void enroll(bob::learn::em::ISVMachine& machine, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& features, - const size_t n_iter); - - /** - * @brief Get the x speaker factors - */ - const std::vector<blitz::Array<double,2> >& getX() const - { return m_base_trainer.getX(); } - /** - * @brief Get the z speaker factors - */ - const std::vector<blitz::Array<double,1> >& getZ() const - { return m_base_trainer.getZ(); } - /** - * @brief Set the x speaker factors - */ - void setX(const std::vector<blitz::Array<double,2> >& X) - { m_base_trainer.setX(X); } - /** - * @brief Set the z speaker factors - */ - void setZ(const std::vector<blitz::Array<double,1> >& z) - { m_base_trainer.setZ(z); } - - /** - * @brief Getters for the accumulators - */ - const blitz::Array<double,3>& getAccUA1() const - { return m_base_trainer.getAccUA1(); } - const blitz::Array<double,2>& getAccUA2() const - { return m_base_trainer.getAccUA2(); } - - /** - * @brief Setters for the accumulators, Very useful if the e-Step needs - * to be parallelized. - */ - void setAccUA1(const blitz::Array<double,3>& acc) - { m_base_trainer.setAccUA1(acc); } - void setAccUA2(const blitz::Array<double,2>& acc) - { m_base_trainer.setAccUA2(acc); } - - /** - * @brief Sets the Random Number Generator - */ - void setRng(const boost::shared_ptr<boost::mt19937> rng) - { m_rng = rng; } - - /** - * @brief Gets the Random Number Generator - */ - const boost::shared_ptr<boost::mt19937> getRng() const - { return m_rng; } - - - private: - /** - * @brief Initialize D to sqrt(ubm_var/relevance_factor) - */ - void initializeD(bob::learn::em::ISVBase& machine) const; - - // Attributes - bob::learn::em::FABaseTrainer m_base_trainer; - - double m_relevance_factor; - - boost::shared_ptr<boost::mt19937> m_rng; ///< The random number generator for the inialization}; -}; - -} } } // namespaces - -#endif /* BOB_LEARN_EM_ISVTRAINER_H */ diff --git a/bob/learn/em/include/bob.learn.em/IVectorMachine.h b/bob/learn/em/include/bob.learn.em/IVectorMachine.h deleted file mode 100644 index 59c67493a44bfb048feb27dba39b3d18ed0b90bc..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/IVectorMachine.h +++ /dev/null @@ -1,274 +0,0 @@ -/** - * @date Sat Mar 30 20:55:00 2013 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_IVECTOR_MACHINE_H -#define BOB_LEARN_EM_IVECTOR_MACHINE_H - -#include <blitz/array.h> -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.io.base/HDF5File.h> - -namespace bob { namespace learn { namespace em { - -/** - * @brief An IVectorMachine consists of a Total Variability subspace \f$T\f$ - * and allows the extraction of IVector\n - * Reference:\n - * "Front-End Factor Analysis For Speaker Verification", - * N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet, - * IEEE Trans. on Audio, Speech and Language Processing - */ -class IVectorMachine -{ - public: - /** - * @brief Default constructor. Builds an IVectorMachine. - * The Universal Background Model and the matrices \f$T\f$ and - * \f$diag(\Sigma)\f$ are not initialized. - */ - IVectorMachine(); - - /** - * @brief Constructor. Builds a new IVectorMachine. - * The Universal Background Model and the matrices \f$T\f$ and - * \f$diag(\Sigma)\f$ are not initialized. - * - * @param ubm The Universal Background Model - * @param rt size of \f$T\f$ (CD x rt) - * @param variance_threshold variance flooring threshold for the - * \f$\Sigma\f$ (diagonal) matrix - * @warning rt SHOULD BE >= 1. - */ - IVectorMachine(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, - const size_t rt=1, const double variance_threshold=1e-10); - - /** - * @brief Copy constructor - */ - IVectorMachine(const IVectorMachine& other); - - /** - * @brief Starts a new IVectorMachine from an existing Configuration object. - */ - IVectorMachine(bob::io::base::HDF5File& config); - - /** - * @brief Destructor - */ - virtual ~IVectorMachine(); - - /** - * @brief Assigns from a different IVectorMachine - */ - IVectorMachine& operator=(const IVectorMachine &other); - - /** - * @brief Equal to - */ - bool operator==(const IVectorMachine& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const IVectorMachine& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const IVectorMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Saves model to an HDF5 file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Loads data from an existing configuration object. Resets - * the current state. - */ - void load(bob::io::base::HDF5File& config); - - /** - * @brief Returns the UBM - */ - const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const - { return m_ubm; } - - /** - * @brief Returns the \f$T\f$ matrix - */ - const blitz::Array<double,2>& getT() const - { return m_T; } - - /** - * @brief Returns the \f$\Sigma\f$ (diagonal) matrix as a 1D array - */ - const blitz::Array<double,1>& getSigma() const - { return m_sigma; } - - /** - * @brief Gets the variance flooring threshold - */ - const double getVarianceThreshold() const - { return m_variance_threshold; } - - /** - * @brief Returns the number of Gaussian components C. - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { return m_ubm->getNGaussians(); } - - /** - * @brief Returns the feature dimensionality D. - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { return m_ubm->getNInputs(); } - - /** - * @brief Returns the supervector length CD. - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { return m_ubm->getNGaussians()*m_ubm->getNInputs(); } - - /** - * @brief Returns the size/rank rt of the \f$T\f$ matrix - */ - const size_t getDimRt() const - { return m_rt; } - - /** - * @brief Resets the dimensionality of the subspace \f$T\f$. - * \f$T\f$ is hence uninitialized. - */ - void resize(const size_t rt); - - /** - * @brief Returns the \f$T\f$ matrix in order to update it. - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateT() - { return m_T; } - - /** - * @brief Returns the \f$\Sigma\f$ (diagonal) matrix in order to update it. - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,1>& updateSigma() - { return m_sigma; } - - /** - * @brief Sets (the mean supervector of) the Universal Background Model. - * \f$T\f$ and \f$\Sigma\f$ are uninitialized in case of dimensions update (C or D) - */ - void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm); - - /** - * @brief Sets the \f$T\f$ matrix - */ - void setT(const blitz::Array<double,2>& T); - - /** - * @brief Sets the \f$\Sigma\f$ (diagonal) matrix - */ - void setSigma(const blitz::Array<double,1>& sigma); - - /** - * @brief Set the variance flooring threshold - */ - void setVarianceThreshold(const double value); - - /** - * @brief Update arrays in cache - * @warning It is only useful when using updateT() or updateSigma() - * and should mostly be done by trainers - */ - void precompute(); - - /** - * @brief Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$ - * @warning No check is perform - */ - void computeIdTtSigmaInvT(const bob::learn::em::GMMStats& input, blitz::Array<double,2>& output) const; - - /** - * @brief Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$ - * @warning No check is perform - */ - void computeTtSigmaInvFnorm(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const; - - /** - * @brief Extracts an ivector from the input GMM statistics - * - * @param input GMM statistics to be used by the machine - * @param output I-vector computed by the machine - */ - void forward(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const; - - /** - * @brief Extracts an ivector from the input GMM statistics - * - * @param input GMM statistics to be used by the machine - * @param output I-vector computed by the machine - * @warning Inputs are NOT checked - */ - void forward_(const bob::learn::em::GMMStats& input, blitz::Array<double,1>& output) const; - - private: - /** - * @brief Apply the variance flooring thresholds. - * This method is called when using setVarianceThresholds() - */ - void applyVarianceThreshold(); - - /** - * @brief Resize cache - */ - void resizeCache(); - /** - * @brief Resize working arrays - */ - void resizeTmp(); - /** - * @brief Resize cache and working arrays before updating cache - */ - void resizePrecompute(); - - // UBM - boost::shared_ptr<bob::learn::em::GMMMachine> m_ubm; - - // dimensionality - size_t m_rt; ///< size of \f$T\f$ (CD x rt) - - ///< \f$T\f$ and \f$Sigma\f$ matrices. - ///< \f$Sigma\f$ is assumed to be diagonal, and only the diagonal is stored - blitz::Array<double,2> m_T; ///< The total variability matrix \f$T\f$ - blitz::Array<double,1> m_sigma; ///< The diagonal covariance matrix \f$\Sigma\f$ - double m_variance_threshold; ///< The variance flooring threshold - - blitz::Array<double,3> m_cache_Tct_sigmacInv; - blitz::Array<double,3> m_cache_Tct_sigmacInv_Tc; - - mutable blitz::Array<double,1> m_tmp_d; - mutable blitz::Array<double,1> m_tmp_t1; - mutable blitz::Array<double,1> m_tmp_t2; - mutable blitz::Array<double,2> m_tmp_tt; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_IVECTOR_MACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/IVectorTrainer.h b/bob/learn/em/include/bob.learn.em/IVectorTrainer.h deleted file mode 100644 index 0482f1513116289ecf1242bfa20d8e4856d4c7db..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/IVectorTrainer.h +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @date Sat Mar 30 20:55:00 2013 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_IVECTOR_TRAINER_H -#define BOB_LEARN_EM_IVECTOR_TRAINER_H - -#include <blitz/array.h> -#include <bob.learn.em/IVectorMachine.h> -#include <bob.learn.em/GMMStats.h> -#include <boost/shared_ptr.hpp> -#include <vector> -#include <bob.core/array_copy.h> -#include <boost/random.hpp> - -#include <boost/random/mersenne_twister.hpp> - -namespace bob { namespace learn { namespace em { - -/** - * @brief An IVectorTrainer to learn a Total Variability subspace \f$T\f$ - * (and eventually a covariance matrix \f$\Sigma\f$).\n - * Reference:\n - * "Front-End Factor Analysis For Speaker Verification", - * N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet, - * IEEE Trans. on Audio, Speech and Language Processing - */ -class IVectorTrainer -{ - public: - /** - * @brief Default constructor. Builds an IVectorTrainer - */ - IVectorTrainer(const bool update_sigma=false); - - /** - * @brief Copy constructor - */ - IVectorTrainer(const IVectorTrainer& other); - - /** - * @brief Destructor - */ - virtual ~IVectorTrainer(); - - /** - * @brief Initialization before the EM loop - */ - virtual void initialize(bob::learn::em::IVectorMachine& ivector); - - /** - * @brief Reset the statistics accumulators - * to the correct size and a value of zero. - */ - void resetAccumulators(const bob::learn::em::IVectorMachine& ivector); - - /** - * @brief Calculates statistics across the dataset, - * and saves these as: - * - m_acc_Nij_wij2 - * - m_acc_Fnormij_wij - * - m_acc_Nij (only if update_sigma is enabled) - * - m_acc_Snormij (only if update_sigma is enabled) - * - * These statistics will be used in the mStep() that follows. - */ - virtual void eStep(bob::learn::em::IVectorMachine& ivector, - const std::vector<bob::learn::em::GMMStats>& data); - - /** - * @brief Maximisation step: Update the Total Variability matrix \f$T\f$ - * and \f$\Sigma\f$ if update_sigma is enabled. - */ - virtual void mStep(bob::learn::em::IVectorMachine& ivector); - - - /** - * @brief Assigns from a different IVectorTrainer - */ - IVectorTrainer& operator=(const IVectorTrainer &other); - - /** - * @brief Equal to - */ - bool operator==(const IVectorTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const IVectorTrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const IVectorTrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Getters for the accumulators - */ - const blitz::Array<double,3>& getAccNijWij2() const - { return m_acc_Nij_wij2; } - const blitz::Array<double,3>& getAccFnormijWij() const - { return m_acc_Fnormij_wij; } - const blitz::Array<double,1>& getAccNij() const - { return m_acc_Nij; } - const blitz::Array<double,2>& getAccSnormij() const - { return m_acc_Snormij; } - - /** - * @brief Setters for the accumulators, Very useful if the e-Step needs - * to be parallelized. - */ - void setAccNijWij2(const blitz::Array<double,3>& acc) - { bob::core::array::assertSameShape(acc, m_acc_Nij_wij2); - m_acc_Nij_wij2 = acc; } - void setAccFnormijWij(const blitz::Array<double,3>& acc) - { bob::core::array::assertSameShape(acc, m_acc_Fnormij_wij); - m_acc_Fnormij_wij = acc; } - void setAccNij(const blitz::Array<double,1>& acc) - { bob::core::array::assertSameShape(acc, m_acc_Nij); - m_acc_Nij = acc; } - void setAccSnormij(const blitz::Array<double,2>& acc) - { bob::core::array::assertSameShape(acc, m_acc_Snormij); - m_acc_Snormij = acc; } - - void setRng(boost::shared_ptr<boost::mt19937> rng){ - m_rng = rng; - }; - - protected: - // Attributes - bool m_update_sigma; - - // Acccumulators - blitz::Array<double,3> m_acc_Nij_wij2; - blitz::Array<double,3> m_acc_Fnormij_wij; - blitz::Array<double,1> m_acc_Nij; - blitz::Array<double,2> m_acc_Snormij; - - // Working arrays - mutable blitz::Array<double,1> m_tmp_wij; - mutable blitz::Array<double,2> m_tmp_wij2; - mutable blitz::Array<double,1> m_tmp_d1; - mutable blitz::Array<double,1> m_tmp_t1; - mutable blitz::Array<double,2> m_tmp_dd1; - mutable blitz::Array<double,2> m_tmp_dt1; - mutable blitz::Array<double,2> m_tmp_tt1; - mutable blitz::Array<double,2> m_tmp_tt2; - - /** - * @brief The random number generator for the inialization - */ - boost::shared_ptr<boost::mt19937> m_rng; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_IVECTOR_TRAINER_H diff --git a/bob/learn/em/include/bob.learn.em/JFABase.h b/bob/learn/em/include/bob.learn.em/JFABase.h deleted file mode 100644 index c75cec864187c46a1648e6a7288188c1a2754929..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/JFABase.h +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @date Tue Jan 27 15:54:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief A base class for Joint Factor Analysis-like machines - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_JFABASE_H -#define BOB_LEARN_EM_JFABASE_H - -#include <stdexcept> - -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/FABase.h> -//#include <bob.learn.em/LinearScoring.h> - -#include <bob.io.base/HDF5File.h> -#include <boost/shared_ptr.hpp> - -namespace bob { namespace learn { namespace em { - - -/** - * @brief A JFA Base class which contains U, V and D matrices - * TODO: add a reference to the journal articles - */ -class JFABase -{ - public: - /** - * @brief Default constructor. Builds a 1 x 1 JFABase - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - */ - JFABase(); - - /** - * @brief Constructor. Builds a new JFABase. - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - * - * @param ubm The Universal Background Model - * @param ru size of U (CD x ru) - * @param rv size of U (CD x rv) - * @warning ru and rv SHOULD BE >= 1. - */ - JFABase(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm, const size_t ru=1, const size_t rv=1); - - /** - * @brief Copy constructor - */ - JFABase(const JFABase& other); - - /** - * @deprecated Starts a new JFAMachine from an existing Configuration object. - */ - JFABase(bob::io::base::HDF5File& config); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~JFABase(); - - /** - * @brief Assigns from a different JFA machine - */ - JFABase& operator=(const JFABase &other); - - /** - * @brief Equal to - */ - bool operator==(const JFABase& b) const - { return m_base.operator==(b.m_base); } - - /** - * @brief Not equal to - */ - bool operator!=(const JFABase& b) const - { return m_base.operator!=(b.m_base); } - - /** - * @brief Similar to - */ - bool is_similar_to(const JFABase& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const - { return m_base.is_similar_to(b.m_base, r_epsilon, a_epsilon); } - - /** - * @brief Saves model to an HDF5 file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Loads data from an existing configuration object. Resets - * the current state. - */ - void load(bob::io::base::HDF5File& config); - - /** - * @brief Returns the UBM - */ - const boost::shared_ptr<bob::learn::em::GMMMachine> getUbm() const - { return m_base.getUbm(); } - - /** - * @brief Returns the U matrix - */ - const blitz::Array<double,2>& getU() const - { return m_base.getU(); } - - /** - * @brief Returns the V matrix - */ - const blitz::Array<double,2>& getV() const - { return m_base.getV(); } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) - */ - const blitz::Array<double,1>& getD() const - { return m_base.getD(); } - - /** - * @brief Returns the number of Gaussian components - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { return m_base.getNGaussians();} - - /** - * @brief Returns the feature dimensionality D - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { return m_base.getNInputs(); } - - /** - * @brief Returns the supervector length CD - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { return m_base.getSupervectorLength(); } - - /** - * @brief Returns the size/rank ru of the U matrix - */ - const size_t getDimRu() const - { return m_base.getDimRu(); } - - /** - * @brief Returns the size/rank rv of the V matrix - */ - const size_t getDimRv() const - { return m_base.getDimRv(); } - - /** - * @brief Resets the dimensionality of the subspace U and V - * U and V are hence uninitialized. - */ - void resize(const size_t ru, const size_t rv) - { m_base.resize(ru, rv); } - - /** - * @brief Returns the U matrix in order to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateU() - { return m_base.updateU(); } - - /** - * @brief Returns the V matrix in order to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,2>& updateV() - { return m_base.updateV(); } - - /** - * @brief Returns the diagonal matrix diag(d) (as a 1D vector) in order - * to update it - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - blitz::Array<double,1>& updateD() - { return m_base.updateD(); } - - - /** - * @brief Sets (the mean supervector of) the Universal Background Model - * U, V and d are uninitialized in case of dimensions update (C or D) - */ - void setUbm(const boost::shared_ptr<bob::learn::em::GMMMachine> ubm) - { m_base.setUbm(ubm); } - - /** - * @brief Sets the U matrix - */ - void setU(const blitz::Array<double,2>& U) - { m_base.setU(U); } - - /** - * @brief Sets the V matrix - */ - void setV(const blitz::Array<double,2>& V) - { m_base.setV(V); } - - /** - * @brief Sets the diagonal matrix diag(d) - * (a 1D vector is expected as an argument) - */ - void setD(const blitz::Array<double,1>& d) - { m_base.setD(d); } - - /** - * @brief Estimates x from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const - { m_base.estimateX(gmm_stats, x); } - - /** - * @brief Precompute (put U^{T}.Sigma^{-1} matrix in cache) - * @warning Should only be used by the trainer for efficiency reason, - * or for testing purpose. - */ - void precompute() - { m_base.updateCacheUbmUVD(); } - - /** - * @brief Returns the FABase member - */ - const bob::learn::em::FABase& getBase() const - { return m_base; } - - - private: - // FABase - bob::learn::em::FABase m_base; -}; - - -} } } // namespaces - -#endif // BOB_LEARN_EM_JFABASE_H diff --git a/bob/learn/em/include/bob.learn.em/JFAMachine.h b/bob/learn/em/include/bob.learn.em/JFAMachine.h deleted file mode 100644 index 51649fef10d089d4055b87f33ba3a0d53e861335..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/JFAMachine.h +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @date Tue Jan 27 16:47:00 2015 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief A base class for Joint Factor Analysis-like machines - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_JFAMACHINE_H -#define BOB_LEARN_EM_JFAMACHINE_H - -#include <stdexcept> - -#include <bob.learn.em/JFABase.h> -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/LinearScoring.h> - -#include <bob.io.base/HDF5File.h> -#include <boost/shared_ptr.hpp> - -namespace bob { namespace learn { namespace em { - - -/** - * @brief A JFAMachine which is associated to a JFABase that contains - * U, V and D matrices. The JFAMachine describes the identity part - * (latent variables y and z) - * TODO: add a reference to the journal articles - */ -class JFAMachine -{ - public: - /** - * @brief Default constructor. Builds an otherwise invalid 0 x 0 JFAMachine - * The Universal Background Model and the matrices U, V and diag(d) are - * not initialized. - */ - JFAMachine(); - - /** - * @brief Constructor. Builds a new JFAMachine. - * - * @param jfa_base The JFABase associated with this machine - */ - JFAMachine(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base); - - /** - * @brief Copy constructor - */ - JFAMachine(const JFAMachine& other); - - /** - * @deprecated Starts a new JFAMachine from an existing Configuration object. - */ - JFAMachine(bob::io::base::HDF5File& config); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~JFAMachine(); - - /** - * @brief Assigns from a different JFA machine - */ - JFAMachine& operator=(const JFAMachine &other); - - /** - * @brief Equal to - */ - bool operator==(const JFAMachine& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const JFAMachine& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const JFAMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Saves machine to an HDF5 file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Loads data from an existing configuration object. Resets - * the current state. - */ - void load(bob::io::base::HDF5File& config); - - /** - * @brief Returns the number of Gaussian components C - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNGaussians() const - { return m_jfa_base->getNGaussians(); } - - /** - * @brief Returns the feature dimensionality D - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getNInputs() const - { return m_jfa_base->getNInputs(); } - - /** - * @brief Returns the supervector length CD - * (CxD: Number of Gaussian components by the feature dimensionality) - * @warning An exception is thrown if no Universal Background Model has - * been set yet. - */ - const size_t getSupervectorLength() const - { return m_jfa_base->getSupervectorLength(); } - - /** - * @brief Returns the size/rank ru of the U matrix - */ - const size_t getDimRu() const - { return m_jfa_base->getDimRu(); } - - /** - * @brief Returns the size/rank rv of the V matrix - */ - const size_t getDimRv() const - { return m_jfa_base->getDimRv(); } - - /** - * @brief Returns the x session factor - */ - const blitz::Array<double,1>& getX() const - { return m_cache_x; } - - /** - * @brief Returns the y speaker factor - */ - const blitz::Array<double,1>& getY() const - { return m_y; } - - /** - * @brief Returns the z speaker factor - */ - const blitz::Array<double,1>& getZ() const - { return m_z; } - - /** - * @brief Returns the y speaker factors in order to update it - */ - blitz::Array<double,1>& updateY() - { return m_y; } - - /** - * @brief Returns the z speaker factors in order to update it - */ - blitz::Array<double,1>& updateZ() - { return m_z; } - - /** - * @brief Returns the y speaker factors - */ - void setY(const blitz::Array<double,1>& y); - - /** - * @brief Returns the V matrix - */ - void setZ(const blitz::Array<double,1>& z); - - /** - * @brief Returns the JFABase - */ - const boost::shared_ptr<bob::learn::em::JFABase> getJFABase() const - { return m_jfa_base; } - - /** - * @brief Sets the JFABase - */ - void setJFABase(const boost::shared_ptr<bob::learn::em::JFABase> jfa_base); - - - /** - * @brief Estimates x from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateX(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& x) const - { m_jfa_base->estimateX(gmm_stats, x); } - /** - * @brief Estimates Ux from the GMM statistics considering the LPT - * assumption, that is the latent session variable x is approximated - * using the UBM - */ - void estimateUx(const bob::learn::em::GMMStats& gmm_stats, blitz::Array<double,1>& Ux); - - /** - * @brief Execute the machine - * - * @param input input data used by the machine - * @warning Inputs are checked - * @return score value computed by the machine - */ - double forward(const bob::learn::em::GMMStats& input); - /** - * @brief Computes a score for the given UBM statistics and given the - * Ux vector - */ - double forward(const bob::learn::em::GMMStats& gmm_stats, - const blitz::Array<double,1>& Ux); - - /** - * @brief Execute the machine - * - * @param input input data used by the machine - * @param score value computed by the machine - * @warning Inputs are NOT checked - */ - double forward_(const bob::learn::em::GMMStats& input); - - private: - /** - * @brief Resize latent variable according to the JFABase - */ - void resize(); - /** - * @brief Resize working arrays - */ - void resizeTmp(); - /** - * @brief Update the cache - */ - void updateCache(); - - // UBM - boost::shared_ptr<bob::learn::em::JFABase> m_jfa_base; - - // y and z vectors/factors learned during the enrollment procedure - blitz::Array<double,1> m_y; - blitz::Array<double,1> m_z; - - // cache - blitz::Array<double,1> m_cache_mVyDz; - mutable blitz::Array<double,1> m_cache_x; - - // x vector/factor in cache when computing scores - mutable blitz::Array<double,1> m_tmp_Ux; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_JFAMACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/JFATrainer.h b/bob/learn/em/include/bob.learn.em/JFATrainer.h deleted file mode 100644 index d697442edceb4f970bb97309d36abda00ce38e23..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/JFATrainer.h +++ /dev/null @@ -1,238 +0,0 @@ -/** - * @date Tue Jul 19 12:16:17 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief JFA functions - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_JFATRAINER_H -#define BOB_LEARN_EM_JFATRAINER_H - -#include <blitz/array.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.learn.em/FABaseTrainer.h> -#include <bob.learn.em/JFAMachine.h> -#include <vector> - -#include <map> -#include <string> -#include <bob.core/array_copy.h> -#include <boost/shared_ptr.hpp> -#include <boost/random.hpp> -#include <bob.core/logging.h> - -namespace bob { namespace learn { namespace em { - -class JFATrainer -{ - public: - /** - * @brief Constructor - */ - JFATrainer(); - - /** - * @brief Copy onstructor - */ - JFATrainer(const JFATrainer& other); - - /** - * @brief Destructor - */ - virtual ~JFATrainer(); - - /** - * @brief Assignment operator - */ - JFATrainer& operator=(const JFATrainer& other); - - /** - * @brief Equal to - */ - bool operator==(const JFATrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const JFATrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const JFATrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Sets the maximum number of EM-like iterations (for each subspace) - */ - //void setMaxIterations(const size_t max_iterations) - //{ m_max_iterations = max_iterations; } - - /** - * @brief Gets the maximum number of EM-like iterations (for each subspace) - */ - //size_t getMaxIterations() const - //{ return m_max_iterations; } - - /** - * @brief This methods performs some initialization before the EM loop. - */ - virtual void initialize(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - - /** - * @brief This methods performs the e-Step to train the first subspace V - */ - virtual void eStep1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the m-Step to train the first subspace V - */ - virtual void mStep1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the finalization after training the first - * subspace V - */ - virtual void finalize1(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the e-Step to train the second subspace U - */ - virtual void eStep2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the m-Step to train the second subspace U - */ - virtual void mStep2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the finalization after training the second - * subspace U - */ - virtual void finalize2(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the e-Step to train the third subspace d - */ - virtual void eStep3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the m-Step to train the third subspace d - */ - virtual void mStep3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods performs the finalization after training the third - * subspace d - */ - virtual void finalize3(bob::learn::em::JFABase& machine, - const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - - /** - * @brief This methods performs the main loops to train the subspaces U, V and d - */ - //virtual void train_loop(bob::learn::em::JFABase& machine, - //const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - /** - * @brief This methods trains the subspaces U, V and d - */ - //virtual void train(bob::learn::em::JFABase& machine, - //const std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& ar); - - /** - * @brief Enrol a client - */ - void enroll(bob::learn::em::JFAMachine& machine, - const std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& features, - const size_t n_iter); - - /** - * @brief Sets the Random Number Generator - */ - void setRng(const boost::shared_ptr<boost::mt19937> rng) - { m_rng = rng; } - - /** - * @brief Gets the Random Number Generator - */ - const boost::shared_ptr<boost::mt19937> getRng() const - { return m_rng; } - - /** - * @brief Get the x speaker factors - */ - const std::vector<blitz::Array<double,2> >& getX() const - { return m_base_trainer.getX(); } - /** - * @brief Get the y speaker factors - */ - const std::vector<blitz::Array<double,1> >& getY() const - { return m_base_trainer.getY(); } - /** - * @brief Get the z speaker factors - */ - const std::vector<blitz::Array<double,1> >& getZ() const - { return m_base_trainer.getZ(); } - /** - * @brief Set the x speaker factors - */ - void setX(const std::vector<blitz::Array<double,2> >& X) - { m_base_trainer.setX(X); } - /** - * @brief Set the y speaker factors - */ - void setY(const std::vector<blitz::Array<double,1> >& y) - { m_base_trainer.setY(y); } - /** - * @brief Set the z speaker factors - */ - void setZ(const std::vector<blitz::Array<double,1> >& z) - { m_base_trainer.setZ(z); } - - /** - * @brief Getters for the accumulators - */ - const blitz::Array<double,3>& getAccVA1() const - { return m_base_trainer.getAccVA1(); } - const blitz::Array<double,2>& getAccVA2() const - { return m_base_trainer.getAccVA2(); } - const blitz::Array<double,3>& getAccUA1() const - { return m_base_trainer.getAccUA1(); } - const blitz::Array<double,2>& getAccUA2() const - { return m_base_trainer.getAccUA2(); } - const blitz::Array<double,1>& getAccDA1() const - { return m_base_trainer.getAccDA1(); } - const blitz::Array<double,1>& getAccDA2() const - { return m_base_trainer.getAccDA2(); } - - /** - * @brief Setters for the accumulators, Very useful if the e-Step needs - * to be parallelized. - */ - void setAccVA1(const blitz::Array<double,3>& acc) - { m_base_trainer.setAccVA1(acc); } - void setAccVA2(const blitz::Array<double,2>& acc) - { m_base_trainer.setAccVA2(acc); } - void setAccUA1(const blitz::Array<double,3>& acc) - { m_base_trainer.setAccUA1(acc); } - void setAccUA2(const blitz::Array<double,2>& acc) - { m_base_trainer.setAccUA2(acc); } - void setAccDA1(const blitz::Array<double,1>& acc) - { m_base_trainer.setAccDA1(acc); } - void setAccDA2(const blitz::Array<double,1>& acc) - { m_base_trainer.setAccDA2(acc); } - - - private: - // Attributes - //size_t m_max_iterations; - boost::shared_ptr<boost::mt19937> m_rng; ///< The random number generator for the inialization - bob::learn::em::FABaseTrainer m_base_trainer; -}; - -} } } // namespaces - -#endif /* BOB_LEARN_EM_JFATRAINER_H */ diff --git a/bob/learn/em/include/bob.learn.em/KMeansMachine.h b/bob/learn/em/include/bob.learn.em/KMeansMachine.h deleted file mode 100644 index 2c8113a6a06644b7af2f6839e76106da8de930bf..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/KMeansMachine.h +++ /dev/null @@ -1,244 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ -#ifndef BOB_LEARN_EM_KMEANSMACHINE_H -#define BOB_LEARN_EM_KMEANSMACHINE_H - -#include <blitz/array.h> -#include <cfloat> - -#include <bob.io.base/HDF5File.h> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements a k-means classifier. - * @details See Section 9.1 of Bishop, "Pattern recognition and machine learning", 2006 - */ -class KMeansMachine { - public: - /** - * Default constructor. Builds an otherwise invalid 0 x 0 k-means - * machine. This is equivalent to construct a LinearMachine with two - * size_t parameters set to 0, as in LinearMachine(0, 0). - */ - KMeansMachine(); - - /** - * Constructor - * @param[in] n_means The number of means - * @param[in] n_inputs The feature dimensionality - */ - KMeansMachine(const size_t n_means, const size_t n_inputs); - - /** - * Builds a new machine with the given means. Each row of the means - * matrix should represent a mean. - */ - KMeansMachine(const blitz::Array<double,2>& means); - - /** - * Copies another machine (copy constructor) - */ - KMeansMachine(const KMeansMachine& other); - - /** - * Starts a new KMeansMachine from an existing Configuration object. - */ - KMeansMachine(bob::io::base::HDF5File& config); - - /** - * Destructor - */ - virtual ~KMeansMachine(); - - /** - * Assigns from a different machine - */ - KMeansMachine& operator=(const KMeansMachine& other); - - /** - * Equal to - */ - bool operator==(const KMeansMachine& b) const; - - /** - * Not equal to - */ - bool operator!=(const KMeansMachine& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const KMeansMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * Loads data from an existing configuration object. Resets the current - * state. - */ - void load(bob::io::base::HDF5File& config); - - /** - * Saves an existing machine to a Configuration object. - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * Output the minimum (Square Euclidean) distance between the input and - * one of the means (overrides Machine::forward) - */ - void forward(const blitz::Array<double,1>& input, double& output) const; - - /** - * Output the minimum (Square Euclidean) distance between the input and - * one of the means (overrides Machine::forward_) - * @warning Inputs are NOT checked - */ - void forward_(const blitz::Array<double,1>& input, double& output) const; - - - /** - * Set the means - */ - void setMeans(const blitz::Array<double,2>& means); - - /** - * Set the i'th mean - */ - void setMean(const size_t i, const blitz::Array<double,1>& mean); - - /** - * Get a mean - * @param[in] i The index of the mean - * @param[out] mean The mean, a 1D array, with a length equal to the number of feature dimensions. - */ - const blitz::Array<double,1> getMean(const size_t i) const; - - /** - * Get the means (i.e. a 2D array, with as many rows as means, and as - * many columns as feature dimensions.) - */ - const blitz::Array<double,2>& getMeans() const - { return m_means; } - - /** - * Get the means in order to be updated (i.e. a 2D array, with as many - * rows as means, and as many columns as feature dimensions.) - * @warning Only trainers should use this function for efficiency reasons - */ - blitz::Array<double,2>& updateMeans() - { return m_means; } - - /** - * Return the power of two of the (Square Euclidean) distance of the - * sample, x, to the i'th mean - * @param x The data sample (feature vector) - * @param i The index of the mean - */ - double getDistanceFromMean(const blitz::Array<double,1>& x, - const size_t i) const; - - /** - * Calculate the index of the mean that is closest - * (in terms of Square Euclidean distance) to the data sample, x - * @param x The data sample (feature vector) - * @param closest_mean (output) The index of the mean closest to the sample - * @param min_distance (output) The distance of the sample from the closest mean - */ - void getClosestMean(const blitz::Array<double,1>& x, - size_t &closest_mean, double &min_distance) const; - - /** - * Output the minimum (Square Euclidean) distance between the input and - * one of the means - */ - double getMinDistance(const blitz::Array<double,1>& input) const; - - /** - * For each mean, find the subset of the samples - * that is closest to that mean, and calculate - * 1) the variance of that subset (the cluster variance) - * 2) the proportion of the samples represented by that subset (the cluster weight) - * @param[in] data The data - * @param[out] variances The cluster variances (one row per cluster), - * with as many columns as feature dimensions. - * @param[out] weights A vector of weights, one per cluster - */ - void getVariancesAndWeightsForEachCluster(const blitz::Array<double,2> &data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const; - /** - * Methods consecutively called by getVariancesAndWeightsForEachCluster() - * This should help for the parallelization on several nodes by splitting the data and calling - * getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum - * with the m_cache_means, variances, and weights variables before performing the merge on one - * node using getVariancesAndWeightsForEachClusterFin(). - */ - void getVariancesAndWeightsForEachClusterInit(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const; - void getVariancesAndWeightsForEachClusterAcc(const blitz::Array<double,2> &data, blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const; - void getVariancesAndWeightsForEachClusterFin(blitz::Array<double,2>& variances, blitz::Array<double,1>& weights) const; - - /** - * Get the m_cache_means array. - * @warning This variable should only be used in the case you want to parallelize the - * getVariancesAndWeightsForEachCluster() method! - */ - const blitz::Array<double,2>& getCacheMeans() const - { return m_cache_means; } - - /** - * Set the m_cache_means array. - * @warning This variable should only be used in the case you want to parallelize the - * getVariancesAndWeightsForEachCluster() method! - */ - void setCacheMeans(const blitz::Array<double,2>& cache_means); - - /** - * Resize the means - */ - void resize(const size_t n_means, const size_t n_inputs); - - /** - * Return the number of means - */ - size_t getNMeans() const { return m_n_means; } - - /** - * Return the number of inputs - */ - size_t getNInputs() const { return m_n_inputs; } - - /** - * Prints a KMeansMachine in the output stream - */ - friend std::ostream& operator<<(std::ostream& os, const KMeansMachine& km); - - - private: - /** - * The number of means - */ - size_t m_n_means; - - /** - * The number of inputs - */ - size_t m_n_inputs; - - /** - * The means (each row is a mean) - */ - blitz::Array<double,2> m_means; - - /** - * cache to avoid re-allocation - */ - mutable blitz::Array<double,2> m_cache_means; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_KMEANSMACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/KMeansTrainer.h b/bob/learn/em/include/bob.learn.em/KMeansTrainer.h deleted file mode 100644 index 58bf1bedaff51a3413c5b75cf52a1314e7626a52..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/KMeansTrainer.h +++ /dev/null @@ -1,181 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ -#ifndef BOB_LEARN_EM_KMEANSTRAINER_H -#define BOB_LEARN_EM_KMEANSTRAINER_H - -#include <bob.learn.em/KMeansMachine.h> -#include <boost/version.hpp> -#include <boost/random/mersenne_twister.hpp> - -namespace bob { namespace learn { namespace em { - -/** - * Trains a KMeans machine. - * @brief This class implements the expectation-maximisation algorithm for a k-means machine. - * @details See Section 9.1 of Bishop, "Pattern recognition and machine learning", 2006 - * It uses a random initialisation of the means followed by the expectation-maximization algorithm - */ -class KMeansTrainer -{ - public: - /** - * @brief This enumeration defines different initialization methods for - * K-means - */ - typedef enum { - RANDOM=0, - RANDOM_NO_DUPLICATE -#if BOOST_VERSION >= 104700 - , - KMEANS_PLUS_PLUS -#endif - } - InitializationMethod; - - /** - * @brief Constructor - */ - KMeansTrainer(InitializationMethod=RANDOM_NO_DUPLICATE); - - /** - * @brief Virtualize destructor - */ - virtual ~KMeansTrainer() {} - - /** - * @brief Copy constructor - */ - KMeansTrainer(const KMeansTrainer& other); - - /** - * @brief Assigns from a different machine - */ - KMeansTrainer& operator=(const KMeansTrainer& other); - - /** - * @brief Equal to - */ - bool operator==(const KMeansTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const KMeansTrainer& b) const; - - /** - * @brief The name for this trainer - */ - virtual std::string name() const { return "KMeansTrainer"; } - - /** - * @brief Initialise the means randomly. - * Data is split into as many chunks as there are means, - * then each mean is set to a random example within each chunk. - */ - void initialize(bob::learn::em::KMeansMachine& kMeansMachine, - const blitz::Array<double,2>& sampler); - - /** - * @brief Accumulate across the dataset: - * - zeroeth and first order statistics - * - average (Square Euclidean) distance from the closest mean - * Implements EMTrainer::eStep(double &) - */ - void eStep(bob::learn::em::KMeansMachine& kmeans, - const blitz::Array<double,2>& data); - - /** - * @brief Updates the mean based on the statistics from the E-step. - */ - void mStep(bob::learn::em::KMeansMachine& kmeans); - - /** - * @brief This functions returns the average min (Square Euclidean) - * distance (average distance to the closest mean) - */ - double computeLikelihood(bob::learn::em::KMeansMachine& kmeans); - - - /** - * @brief Reset the statistics accumulators - * to the correct size and a value of zero. - */ - void resetAccumulators(bob::learn::em::KMeansMachine& kMeansMachine); - - /** - * @brief Sets the Random Number Generator - */ - void setRng(const boost::shared_ptr<boost::mt19937> rng) - { m_rng = rng; } - - /** - * @brief Gets the Random Number Generator - */ - const boost::shared_ptr<boost::mt19937> getRng() const - { return m_rng; } - - /** - * @brief Sets the initialization method used to generate the initial means - */ - void setInitializationMethod(InitializationMethod v) { m_initialization_method = v; } - - /** - * @brief Gets the initialization method used to generate the initial means - */ - InitializationMethod getInitializationMethod() const { return m_initialization_method; } - - /** - * @brief Returns the internal statistics. Useful to parallelize the E-step - */ - const blitz::Array<double,1>& getZeroethOrderStats() const { return m_zeroethOrderStats; } - const blitz::Array<double,2>& getFirstOrderStats() const { return m_firstOrderStats; } - double getAverageMinDistance() const { return m_average_min_distance; } - /** - * @brief Sets the internal statistics. Useful to parallelize the E-step - */ - void setZeroethOrderStats(const blitz::Array<double,1>& zeroethOrderStats); - void setFirstOrderStats(const blitz::Array<double,2>& firstOrderStats); - void setAverageMinDistance(const double value) { m_average_min_distance = value; } - - - private: - - /** - * @brief The initialization method - * Check that there is no duplicated means during the random initialization - */ - InitializationMethod m_initialization_method; - - /** - * @brief The random number generator for the inialization - */ - boost::shared_ptr<boost::mt19937> m_rng; - - /** - * @brief Average min (Square Euclidean) distance - */ - double m_average_min_distance; - - /** - * @brief Zeroeth order statistics accumulator. - * The k'th value in m_zeroethOrderStats is the denominator of - * equation 9.4, Bishop, "Pattern recognition and machine learning", 2006 - */ - blitz::Array<double,1> m_zeroethOrderStats; - - /** - * @brief First order statistics accumulator. - * The k'th row of m_firstOrderStats is the numerator of - * equation 9.4, Bishop, "Pattern recognition and machine learning", 2006 - */ - blitz::Array<double,2> m_firstOrderStats; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_KMEANSTRAINER_H diff --git a/bob/learn/em/include/bob.learn.em/LinearScoring.h b/bob/learn/em/include/bob.learn.em/LinearScoring.h deleted file mode 100644 index 822922ceefbbd8a5069fb77f6a788cf63dd617e9..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/LinearScoring.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @date Wed Jul 13 16:00:04 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ -#ifndef BOB_LEARN_EM_LINEARSCORING_H -#define BOB_LEARN_EM_LINEARSCORING_H - -#include <blitz/array.h> -#include <boost/shared_ptr.hpp> -#include <vector> -#include <bob.learn.em/GMMMachine.h> - -namespace bob { namespace learn { namespace em { - -/** - * Compute a matrix of scores using linear scoring. - * - * @warning Each GMM must have the same size. - * - * @param models list of mean supervector for the client models - * @param ubm_mean mean supervector of the world model - * @param ubm_variance variance supervector of the world model - * @param test_stats list of accumulate statistics for each test trial - * @param test_channelOffset list of channel offset if any (for JFA/ISA for instance) - * @param frame_length_normalisation perform a normalisation by the number of feature vectors - * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s - * @warning the output scores matrix should have the correct size (number of models x number of test_stats) - */ -void linearScoring(const std::vector<blitz::Array<double,1> >& models, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const std::vector<blitz::Array<double, 1> >& test_channelOffset, - const bool frame_length_normalisation, - blitz::Array<double,2>& scores); -void linearScoring(const std::vector<blitz::Array<double,1> >& models, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const bool frame_length_normalisation, - blitz::Array<double,2>& scores); - -/** - * Compute a matrix of scores using linear scoring. - * - * @warning Each GMM must have the same size. - * - * @param models list of client models as GMMMachines - * @param ubm world model as a GMMMachine - * @param test_stats list of accumulate statistics for each test trial - * @param frame_length_normalisation perform a normalisation by the number of feature vectors - * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s - * @warning the output scores matrix should have the correct size (number of models x number of test_stats) - */ -void linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models, - const bob::learn::em::GMMMachine& ubm, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const bool frame_length_normalisation, - blitz::Array<double,2>& scores); -/** - * Compute a matrix of scores using linear scoring. - * - * @warning Each GMM must have the same size. - * - * @param models list of client models as GMMMachines - * @param ubm world model as a GMMMachine - * @param test_stats list of accumulate statistics for each test trial - * @param test_channelOffset list of channel offset if any (for JFA/ISA for instance) - * @param frame_length_normalisation perform a normalisation by the number of feature vectors - * @param[out] scores 2D matrix of scores, <tt>scores[m, s]</tt> is the score for model @c m against statistics @c s - * @warning the output scores matrix should have the correct size (number of models x number of test_stats) - */ -void linearScoring(const std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& models, - const bob::learn::em::GMMMachine& ubm, - const std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& test_stats, - const std::vector<blitz::Array<double, 1> >& test_channelOffset, - const bool frame_length_normalisation, - blitz::Array<double,2>& scores); - -/** - * Compute a score using linear scoring. - * - * @param model mean supervector for the client model - * @param ubm_mean mean supervector of the world model - * @param ubm_variance variance supervector of the world model - * @param test_stats accumulate statistics of the test trial - * @param test_channelOffset channel offset - * @param frame_length_normalisation perform a normalisation by the number of feature vectors - */ -double linearScoring(const blitz::Array<double,1>& model, - const blitz::Array<double,1>& ubm_mean, const blitz::Array<double,1>& ubm_variance, - const bob::learn::em::GMMStats& test_stats, - const blitz::Array<double,1>& test_channelOffset, - const bool frame_length_normalisation); - -} } } // namespaces - -#endif // BOB_LEARN_EM_LINEARSCORING_H diff --git a/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h b/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h deleted file mode 100644 index aac7087fcd84c69334f2fe0cbfb14cfeff5a5369..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/MAP_GMMTrainer.h +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * @brief This class implements the maximum a posteriori M-step of the expectation-maximisation algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation. - * @details See Section 3.4 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000. We use a "single adaptation coefficient", alpha_i, and thus a single relevance factor, r. - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_MAP_GMMTRAINER_H -#define BOB_LEARN_EM_MAP_GMMTRAINER_H - -#include <bob.learn.em/GMMBaseTrainer.h> -#include <limits> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements the maximum a posteriori M-step of the expectation-maximisation algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation. - * @details See Section 3.4 of Reynolds et al., "Speaker Verification Using Adapted Gaussian Mixture Models", Digital Signal Processing, 2000. We use a "single adaptation coefficient", alpha_i, and thus a single relevance factor, r. - */ -class MAP_GMMTrainer -{ - public: - /** - * @brief Default constructor - */ - MAP_GMMTrainer( - const bool update_means=true, - const bool update_variances=false, - const bool update_weights=false, - const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon(), - const bool reynolds_adaptation=false, - const double relevance_factor=4, - const double alpha=0.5, - boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm = boost::shared_ptr<bob::learn::em::GMMMachine>()); - - /** - * @brief Copy constructor - */ - MAP_GMMTrainer(const MAP_GMMTrainer& other); - - /** - * @brief Destructor - */ - virtual ~MAP_GMMTrainer(); - - /** - * @brief Initialization - */ - void initialize(bob::learn::em::GMMMachine& gmm); - - /** - * @brief Assigns from a different MAP_GMMTrainer - */ - MAP_GMMTrainer& operator=(const MAP_GMMTrainer &other); - - /** - * @brief Equal to - */ - bool operator==(const MAP_GMMTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const MAP_GMMTrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const MAP_GMMTrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Set the GMM to use as a prior for MAP adaptation. - * Generally, this is a "universal background model" (UBM), - * also referred to as a "world model". - */ - bool setPriorGMM(boost::shared_ptr<bob::learn::em::GMMMachine> prior_gmm); - - /** - * @brief Calculates and saves statistics across the dataset, - * and saves these as m_ss. Calculates the average - * log likelihood of the observations given the GMM, - * and returns this in average_log_likelihood. - * - * The statistics, m_ss, will be used in the mStep() that follows. - * Implements EMTrainer::eStep(double &) - */ - void eStep(bob::learn::em::GMMMachine& gmm, - const blitz::Array<double,2>& data){ - m_gmm_base_trainer.eStep(gmm,data); - } - - - /** - * @brief Performs a maximum a posteriori (MAP) update of the GMM - * parameters using the accumulated statistics in m_ss and the - * parameters of the prior model - * Implements EMTrainer::mStep() - */ - void mStep(bob::learn::em::GMMMachine& gmm); - - /** - * @brief Computes the likelihood using current estimates of the latent - * variables - */ - double computeLikelihood(bob::learn::em::GMMMachine& gmm){ - return m_gmm_base_trainer.computeLikelihood(gmm); - } - - bool getReynoldsAdaptation() - {return m_reynolds_adaptation;} - - void setReynoldsAdaptation(const bool reynolds_adaptation) - {m_reynolds_adaptation = reynolds_adaptation;} - - - double getRelevanceFactor() - {return m_relevance_factor;} - - void setRelevanceFactor(const double relevance_factor) - {m_relevance_factor = relevance_factor;} - - - double getAlpha() - {return m_alpha;} - - void setAlpha(const double alpha) - {m_alpha = alpha;} - - bob::learn::em::GMMBaseTrainer& base_trainer(){return m_gmm_base_trainer;} - - - protected: - - /** - * The relevance factor for MAP adaptation, r (see Reynolds et al., \"Speaker Verification Using Adapted Gaussian Mixture Models\", Digital Signal Processing, 2000). - */ - double m_relevance_factor; - - /** - Base Trainer for the MAP algorithm. Basically implements the e-step - */ - bob::learn::em::GMMBaseTrainer m_gmm_base_trainer; - - /** - * The GMM to use as a prior for MAP adaptation. - * Generally, this is a "universal background model" (UBM), - * also referred to as a "world model" - */ - boost::shared_ptr<bob::learn::em::GMMMachine> m_prior_gmm; - - /** - * The alpha for the Torch3-like adaptation - */ - double m_alpha; - /** - * Whether Torch3-like adaptation should be used or not - */ - bool m_reynolds_adaptation; - - private: - /// cache to avoid re-allocation - mutable blitz::Array<double,1> m_cache_alpha; - mutable blitz::Array<double,1> m_cache_ml_weights; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_MAP_GMMTRAINER_H diff --git a/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h b/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h deleted file mode 100644 index 40f1e70cb1c3955284b628d91714a9e3901d6986..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/ML_GMMTrainer.h +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @date Tue May 10 11:35:58 2011 +0200 - * @author Francois Moulin <Francois.Moulin@idiap.ch> - * - * @brief This class implements the maximum likelihood M-step of the expectation-maximisation algorithm for a GMM Machine. - * @details See Section 9.2.2 of Bishop, "Pattern recognition and machine learning", 2006 - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_ML_GMMTRAINER_H -#define BOB_LEARN_EM_ML_GMMTRAINER_H - -#include <bob.learn.em/GMMBaseTrainer.h> -#include <limits> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class implements the maximum likelihood M-step of the - * expectation-maximisation algorithm for a GMM Machine. - * @details See Section 9.2.2 of Bishop, - * "Pattern recognition and machine learning", 2006 - */ -class ML_GMMTrainer{ - public: - /** - * @brief Default constructor - */ - ML_GMMTrainer(const bool update_means=true, - const bool update_variances=false, - const bool update_weights=false, - const double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon()); - - /** - * @brief Copy constructor - */ - ML_GMMTrainer(const ML_GMMTrainer& other); - - /** - * @brief Destructor - */ - virtual ~ML_GMMTrainer(); - - /** - * @brief Initialisation before the EM steps - */ - void initialize(bob::learn::em::GMMMachine& gmm); - - /** - * @brief Calculates and saves statistics across the dataset, - * and saves these as m_ss. Calculates the average - * log likelihood of the observations given the GMM, - * and returns this in average_log_likelihood. - * - * The statistics, m_ss, will be used in the mStep() that follows. - * Implements EMTrainer::eStep(double &) - */ - void eStep(bob::learn::em::GMMMachine& gmm, - const blitz::Array<double,2>& data){ - m_gmm_base_trainer.eStep(gmm,data); - } - - /** - * @brief Performs a maximum likelihood (ML) update of the GMM parameters - * using the accumulated statistics in m_ss - * Implements EMTrainer::mStep() - */ - void mStep(bob::learn::em::GMMMachine& gmm); - - /** - * @brief Computes the likelihood using current estimates of the latent - * variables - */ - double computeLikelihood(bob::learn::em::GMMMachine& gmm){ - return m_gmm_base_trainer.computeLikelihood(gmm); - } - - - /** - * @brief Assigns from a different ML_GMMTrainer - */ - ML_GMMTrainer& operator=(const ML_GMMTrainer &other); - - /** - * @brief Equal to - */ - bool operator==(const ML_GMMTrainer& b) const; - - /** - * @brief Not equal to - */ - bool operator!=(const ML_GMMTrainer& b) const; - - /** - * @brief Similar to - */ - bool is_similar_to(const ML_GMMTrainer& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - - bob::learn::em::GMMBaseTrainer& base_trainer(){return m_gmm_base_trainer;} - - protected: - - /** - Base Trainer for the MAP algorithm. Basically implements the e-step - */ - bob::learn::em::GMMBaseTrainer m_gmm_base_trainer; - - - private: - /** - * @brief Add cache to avoid re-allocation at each iteration - */ - mutable blitz::Array<double,1> m_cache_ss_n_thresholded; -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_ML_GMMTRAINER_H diff --git a/bob/learn/em/include/bob.learn.em/PLDAMachine.h b/bob/learn/em/include/bob.learn.em/PLDAMachine.h deleted file mode 100644 index 4f6362ddc322f9ef1b601c9cbee8d6239f8f6200..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/PLDAMachine.h +++ /dev/null @@ -1,702 +0,0 @@ -/** - * @date Fri Oct 14 18:07:56 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Machines that implements the Probabilistic Linear Discriminant - * Analysis Model of Prince and Helder, - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_PLDAMACHINE_H -#define BOB_LEARN_EM_PLDAMACHINE_H - -#include <blitz/array.h> -#include <bob.io.base/HDF5File.h> -#include <map> -#include <iostream> -#include <stdexcept> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class is a container for the \f$F\f$, \f$G\f$ and \f$\Sigma\f$ - * matrices and the mean vector \f$\mu\f$ of a PLDA model. This also - * precomputes useful matrices to make the model scalable.\n - * References:\n - * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: - * Applied to Face Recognition', Laurent El Shafey, Chris McCool, - * Roy Wallace, Sebastien Marcel, TPAMI'2013 - * 2. 'Probabilistic Linear Discriminant Analysis for Inference About - * Identity', Prince and Elder, ICCV'2007\n - * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed, - * Elder and Prince, TPAMI'2012 - */ -class PLDABase -{ - public: - /** - * @brief Default constructor.\n Builds an otherwise invalid 0x0x0 - * PLDABase. - */ - PLDABase(); - /** - * @brief Constructor, builds a new PLDABase.\n \f$F\f$, \f$G\f$ - * and \f$\Sigma\f$ are initialized to the 'eye' matrix (matrix with 1's - * on the diagonal and 0 outside), and \f$\mu\f$ is initialized to 0. - * - * @param dim_d Dimensionality of the feature vector - * @param dim_f size of \f$F\f$ (dim_d x dim_f) - * @param dim_g size of \f$G\f$ (dim_d x dim_g) - * @param variance_threshold The smallest possible value of the variance - * (Ignored if set to 0.) - */ - PLDABase(const size_t dim_d, const size_t dim_f, - const size_t dim_g, const double variance_threshold=0.); - /** - * @brief Copies another PLDABase - */ - PLDABase(const PLDABase& other); - /** - * @brief Starts a new PLDABase from an existing configuration - * object. - * @param config HDF5 configuration file - */ - PLDABase(bob::io::base::HDF5File& config); - - /** - * @brief Just to virtualize the destructor - */ - virtual ~PLDABase(); - - /** - * @brief Assigns from a different PLDABase - */ - PLDABase& operator=(const PLDABase &other); - - /** - * @brief Equal to.\n Even precomputed members such as \f$\alpha\f$, - * \f$\beta\f$ and \f$\gamma_a\f$'s are compared! - */ - bool operator==(const PLDABase& b) const; - /** - * @brief Not equal to.\n Defined as the negation of operator== - */ - bool operator!=(const PLDABase& b) const; - /** - * @brief Similar to.\n Even precomputed members such as \f$\alpha\f$, - * \f$\beta\f$ and \f$\gamma_a\f$'s are compared! - */ - bool is_similar_to(const PLDABase& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Loads data from an existing configuration object. Resets the - * current state. - * @param config HDF5 configuration file - */ - void load(bob::io::base::HDF5File& config); - /** - * @brief Saves an existing machine to a configuration object. - * @param config HDF5 configuration file - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Resizes the PLDABase. - * @warning \f$F\f$, \f$G\f$, \f$\Sigma\f$, \f$\mu\f$ and the variance - * flooring thresholds will be reinitialized! - * @param dim_d Dimensionality of the feature vector - * @param dim_f Rank of \f$F\f$ (dim_d x dim_f) - * @param dim_g Rank of \f$G\f$ (dim_d x dim_g) - */ - void resize(const size_t dim_d, const size_t dim_f, const size_t dim_g); - - /** - * @brief Gets the \f$F\f$ subspace/matrix of the PLDA model - */ - const blitz::Array<double,2>& getF() const - { return m_F; } - /** - * @brief Sets the \f$F\f$ subspace/matrix of the PLDA model - */ - void setF(const blitz::Array<double,2>& F); - /** - * @brief Returns the current \f$F\f$ matrix/subspace of the PLDA model - * in order to be updated. - * @warning Use with care. Only trainers should use this function for - * efficiency reasons. - */ - blitz::Array<double,2>& updateF() - { return m_F; } - - /** - * @brief Gets the \f$G\f$ subspace/matrix of the PLDA model - */ - const blitz::Array<double,2>& getG() const - { return m_G; } - /** - * @brief Sets the \f$G\f$ subspace/matrix of the PLDA model - */ - void setG(const blitz::Array<double,2>& G); - /** - * @brief Returns the current \f$G\f$ subspace/matrix of the PLDA model - * in order to be updated. - * @warning Use with care. Only trainers should use this function for - * efficiency reasons. - */ - blitz::Array<double,2>& updateG() - { return m_G; } - - /** - * @brief Gets the \f$\Sigma\f$ (diagonal) covariance matrix of the PLDA - * model - */ - const blitz::Array<double,1>& getSigma() const - { return m_sigma; } - /** - * @brief Sets the \f$\Sigma\f$ (diagonal) covariance matrix of the PLDA - * model - */ - void setSigma(const blitz::Array<double,1>& s); - /** - * @brief Returns the current \f$\Sigma\f$ (diagonal) covariance matrix of - * the PLDA model in order to be updated. - * @warning Use with care. Only trainers should use this function for - * efficiency reasons. Variance threshold should be applied after - * updating \f$\Sigma\f$! - */ - blitz::Array<double,1>& updateSigma() - { return m_sigma; } - - /** - * @brief Gets the \f$\mu\f$ mean vector of the PLDA model - */ - const blitz::Array<double,1>& getMu() const - { return m_mu; } - /** - * @brief Sets the \f$\mu\f$ mean vector of the PLDA model - */ - void setMu(const blitz::Array<double,1>& mu); - /** - * @brief Returns the current \f$\mu\f$ mean vector of the PLDA model - * in order to be updated. - * @warning Use with care. Only trainers should use this function for - * efficiency reasons. - */ - blitz::Array<double,1>& updateMu() - { return m_mu; } - - /** - * @brief Gets the variance flooring threshold - */ - double getVarianceThreshold() const - { return m_variance_threshold; } - /** - * @brief Sets the variance flooring threshold - */ - void setVarianceThreshold(const double value); - /** - * @brief Apply the variance flooring thresholds. - * This method is automatically called when using setVarianceThresholds(). - * @warning It is only useful when using updateVarianceThreshods(), - * and should mostly be done by trainers - */ - void applyVarianceThreshold(); - - /** - * @brief Gets the feature dimensionality - */ - size_t getDimD() const - { return m_dim_d; } - /** - * @brief Gets the size/rank the \f$F\f$ subspace/matrix of the PLDA model - */ - size_t getDimF() const - { return m_dim_f; } - /** - * @brief Gets the size/rank the \f$G\f$ subspace/matrix of the PLDA model - */ - size_t getDimG() const - { return m_dim_g; } - - /** - * @brief Precomputes useful values such as \f$\Sigma^{-1}\f$, - * \f$G^{T}\Sigma^{-1}\f$, \f$\alpha\f$, \f$\beta\f$, and - * \f$F^{T}\beta\f$. - * @warning Previous \f$\gamma_a\f$ values and log likelihood constant - * terms are cleared. - */ - void precompute(); - /** - * @brief Precomputes useful values for the log likelihood - * \f$\log(\det(\alpha))\f$ and \f$\log(\det(\Sigma))\f$. - */ - void precomputeLogLike(); - /** - * @brief Gets the inverse vector/diagonal matrix of \f$\Sigma^{-1}\f$ - */ - const blitz::Array<double,1>& getISigma() const - { return m_cache_isigma; } - /** - * @brief Gets the \f$\alpha\f$ matrix. - * \f$\alpha = (Id + G^T \Sigma^{-1} G)^{-1} = \mathcal{G}\f$ - */ - const blitz::Array<double,2>& getAlpha() const - { return m_cache_alpha; } - /** - * @brief Gets the \f$\beta\f$ matrix - * \f$\beta = (\Sigma + G G^T)^{-1} = \mathcal{S} = - * \Sigma^{-1} - \Sigma^{-1} G \mathcal{G} G^{T} \Sigma^{-1}\f$ - */ - const blitz::Array<double,2>& getBeta() const - { return m_cache_beta; } - /** - * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of - * samples). - * \f$\gamma_{a} = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$ - * @warning an exception is thrown if \f$\gamma_a\f$ does not exists - */ - const blitz::Array<double,2>& getGamma(const size_t a) const; - /** - * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of - * samples). - * \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$ - * @warning The matrix is computed if it does not already exists - */ - const blitz::Array<double,2>& getAddGamma(const size_t a); - /** - * @brief Gets the \f$F^T \beta\f$ matrix - */ - const blitz::Array<double,2>& getFtBeta() const - { return m_cache_Ft_beta; } - /** - * @brief Gets the \f$G^T \Sigma^{-1}\f$ matrix - */ - const blitz::Array<double,2>& getGtISigma() const - { return m_cache_Gt_isigma; } - /** - * @brief Gets \f$\log(\det(\alpha))\f$ - */ - double getLogDetAlpha() const - { return m_cache_logdet_alpha; } - /** - * @brief Gets \f$\log(\det(\Sigma))\f$ - */ - double getLogDetSigma() const - { return m_cache_logdet_sigma; } - /** - * @brief Computes the log likelihood constant term for a given \f$a\f$ - * (number of samples), given the provided \f$\gamma_a\f$ matrix - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - */ - double computeLogLikeConstTerm(const size_t a, - const blitz::Array<double,2>& gamma_a) const; - /** - * @brief Computes the log likelihood constant term for a given \f$a\f$ - * (number of samples) - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - * @warning: gamma_a will be computed and added if it does - * not already exists - */ - double computeLogLikeConstTerm(const size_t a); - /** - * @brief Tells if the log likelihood constant term for a given \f$a\f$ - * (number of samples) exists - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - */ - bool hasLogLikeConstTerm(const size_t a) const - { return (m_cache_loglike_constterm.find(a) != m_cache_loglike_constterm.end()); } - /** - * @brief Gets the log likelihood constant term for a given \f$a\f$ - * (number of samples) - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - * @warning an exception is thrown if the value does not exists - */ - double getLogLikeConstTerm(const size_t a) const; - /** - * @brief Gets the log likelihood constant term for a given \f$a\f$ - * (number of samples) - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - * @warning The value is computed if it does not already exists - */ - double getAddLogLikeConstTerm(const size_t a); - - /** - * @brief Computes the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number - * of samples) and put the result in the provided array. - * \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$ - */ - void computeGamma(const size_t a, blitz::Array<double,2> res) const; - /** - * @brief Tells if the \f$\gamma_a\f$ matrix for a given a (number of - * samples) exists. - * \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$ - */ - bool hasGamma(const size_t a) const - { return (m_cache_gamma.find(a) != m_cache_gamma.end()); } - - /** - * @brief Clears the maps (\f$\gamma_a\f$ and loglike_constterm_a). - */ - void clearMaps(); - - /** - * @brief Gets the log-likelihood of an observation, given the current model - * and the latent variables (point estimate).\n - * This will basically compute \f$p(x_{ij} | h_{i}, w_{ij}, \Theta)\f$\n - * , given by \n - * \f$\mathcal{N}(x_{ij}|[\mu + F h_{i} + G w_{ij} + \epsilon_{ij}, \Sigma])\f$\n - * , which is in logarithm, \n - * \f$-\frac{D}{2} log(2\pi) -\frac{1}{2} log(det(\Sigma)) -\frac{1}{2} {(x_{ij}-(\mu+F h_{i}+G w_{ij}))^{T}\Sigma^{-1}(x_{ij}-(\mu+F h_{i}+G w_{ij}))}\f$. - */ - double computeLogLikelihoodPointEstimate(const blitz::Array<double,1>& xij, - const blitz::Array<double,1>& hi, const blitz::Array<double,1>& wij) const; - - // Friend method declaration - friend std::ostream& operator<<(std::ostream& os, const PLDABase& m); - - - private: - // Attributes - size_t m_dim_d; ///< Dimensionality of the input feature vector - size_t m_dim_f; ///< Size/rank of the \f$F\f$ subspace - size_t m_dim_g; ///< Size/rank of the \f$G\f$ subspace - blitz::Array<double,2> m_F; ///< \f$F\f$ subspace of the PLDA model - blitz::Array<double,2> m_G; ///< \f$G\f$ subspace of the PLDA model - /** - * @brief \f$\Sigma\f$ diagonal (by assumption) covariance matrix of the - * PLDA model - */ - blitz::Array<double,1> m_sigma; - blitz::Array<double,1> m_mu; ///< \f$\mu\f$ mean vector of the PLDA model - /** - * @brief The variance flooring thresholds, i.e. the minimum allowed - * value of variance m_sigma in each dimension. - * The variance will be set to this value if an attempt is made - * to set it to a smaller value. - */ - double m_variance_threshold; - - // Internal values very useful used to optimize the code - blitz::Array<double,1> m_cache_isigma; ///< \f$\Sigma^{-1}\f$ - blitz::Array<double,2> m_cache_alpha; ///< \f$\alpha = (Id + G^T \Sigma^{-1} G)^{-1}\f$ - /** - * @brief \f$\beta = (\Sigma+G G^T)^{-1} = (\Sigma^{-1} - \Sigma^{-1} G \alpha G^T \Sigma^{-1})^{-1}\f$ - */ - blitz::Array<double,2> m_cache_beta; - std::map<size_t, blitz::Array<double,2> > m_cache_gamma; ///< \f$\gamma_{a} = (Id + a F^T \beta F)^{-1}\f$ - blitz::Array<double,2> m_cache_Ft_beta; ///< \f$F^{T} \beta \f$ - blitz::Array<double,2> m_cache_Gt_isigma; ///< \f$G^{T} \Sigma^{-1} \f$ - double m_cache_logdet_alpha; ///< \f$\log(\det(\alpha))\f$ - double m_cache_logdet_sigma; ///< \f$\log(\det(\Sigma))\f$ - /** - * @brief \f$l_{a} = \frac{a}{2} ( -D log(2*\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - */ - std::map<size_t, double> m_cache_loglike_constterm; - - // working arrays - mutable blitz::Array<double,1> m_tmp_d_1; ///< Cache vector of size dim_d - mutable blitz::Array<double,1> m_tmp_d_2; ///< Cache vector of size dim_d - mutable blitz::Array<double,2> m_tmp_d_ng_1; ///< Cache matrix of size dim_d x dim_g - mutable blitz::Array<double,2> m_tmp_nf_nf_1; ///< Cache matrix of size dim_f x dim_f - mutable blitz::Array<double,2> m_tmp_ng_ng_1; ///< Cache matrix of size dim_g x dim_g - - // private methods - void resizeNoInit(const size_t dim_d, const size_t dim_f, const size_t dim_g); - void resizeTmp(); - void initMuFGSigma(); - void precomputeISigma(); - void precomputeAlpha(); - void precomputeBeta(); - void precomputeGamma(const size_t a); - void precomputeFtBeta(); - void precomputeGtISigma(); - void precomputeLogDetAlpha(); - void precomputeLogDetSigma(); - void precomputeLogLikeConstTerm(const size_t a); -}; - - -/** - * @brief This class is a container for an enrolled identity/class. It - * contains information extracted from the enrollment samples. It should - * be used in combination with a PLDABase instance.\n - * References:\n - * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: - * Applied to Face Recognition', Laurent El Shafey, Chris McCool, - * Roy Wallace, Sebastien Marcel, TPAMI'2013 - * 2. 'Probabilistic Linear Discriminant Analysis for Inference About - * Identity', Prince and Elder, ICCV'2007\n - * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed, - * Elder and Prince, TPAMI'2012 - */ -class PLDAMachine -{ - public: - /** - * @brief Default constructor.\n - * Builds an otherwise invalid (No attached PLDABase) PLDAMachine. - */ - PLDAMachine(); - /** - * @brief Constructor, builds a new PLDAMachine, setting a - * PLDABase. - */ - PLDAMachine(const boost::shared_ptr<bob::learn::em::PLDABase> pldabase); - /** - * @brief Copies another PLDAMachine.\n Both PLDAMachine's will point - * to the same PLDABase. - */ - PLDAMachine(const PLDAMachine& other); - /** - * @brief Starts a new PLDAMachine from an existing configuration object, - * and a PLDABase. - */ - PLDAMachine(bob::io::base::HDF5File& config, - const boost::shared_ptr<bob::learn::em::PLDABase> pldabase); - - /** - * @brief Just to virtualise the destructor - */ - virtual ~PLDAMachine(); - - /** - * @brief Assigns from a different machine - */ - PLDAMachine& operator=(const PLDAMachine &other); - - /** - * @brief Equal to.\n The two PLDAMachine's should have the same - * PLDABase. Precomputed members such as \f$\gamma_a\f$'s - * are compared! - */ - bool operator==(const PLDAMachine& b) const; - /** - * @brief Not equal to.\n Defined as the negation of operator== - */ - bool operator!=(const PLDAMachine& b) const; - /** - * @brief Equal to.\n The two PLDAMachine's should have the same - * PLDABase. Precomputed members such as \f$\gamma_a\f$'s - * are compared! - */ - bool is_similar_to(const PLDAMachine& b, const double r_epsilon=1e-5, - const double a_epsilon=1e-8) const; - - /** - * @brief Loads data from an existing configuration object. Resets the - * current state. - */ - void load(bob::io::base::HDF5File& config); - /** - * @brief Saves an existing machine to a configuration object. - */ - void save(bob::io::base::HDF5File& config) const; - - /** - * @brief Gets the attached PLDABase - */ - const boost::shared_ptr<PLDABase> getPLDABase() const - { return m_plda_base; } - /** - * @brief Sets the attached PLDABase - */ - void setPLDABase(const boost::shared_ptr<bob::learn::em::PLDABase> plda_base); - - /** - * @brief Gets the feature dimensionality - */ - size_t getDimD() const - { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - return m_plda_base->getDimD(); } - /** - * @brief Gets the size/rank the \f$F\f$ subspace/matrix of the PLDA model - */ - size_t getDimF() const - { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - return m_plda_base->getDimF(); } - /** - * @brief Gets the size/rank the \f$G\f$ subspace/matrix of the PLDA model - */ - size_t getDimG() const - { if (!m_plda_base) throw std::runtime_error("No PLDABase set to this machine"); - return m_plda_base->getDimG(); } - - /** - * @brief Gets the number of enrolled samples - */ - uint64_t getNSamples() const - { return m_n_samples; } - /** - * @brief Sets the number of enrolled samples - */ - void setNSamples(const uint64_t n_samples) - { m_n_samples = n_samples; } - /** - * @brief Gets the \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$ value - */ - double getWSumXitBetaXi() const - { return m_nh_sum_xit_beta_xi; } - /** - * @brief Sets the \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$ value - */ - void setWSumXitBetaXi(const double val) - { m_nh_sum_xit_beta_xi = val; } - /** - * @brief Gets the current \f$\sum_{i} F^T \beta x_{i}\f$ value - */ - const blitz::Array<double,1>& getWeightedSum() const - { return m_weighted_sum; } - /** - * @brief Sets the \f$\sum_{i} F^T \beta x_{i}\f$ value - */ - void setWeightedSum(const blitz::Array<double,1>& weighted_sum); - /** - * @brief Returns the current \f$\sum_{i} F^T \beta x_{i}\f$ value - * in order to be updated. - * @warning Use with care. Only trainers should use this function for - * efficiency reasons. - */ - blitz::Array<double,1>& updateWeightedSum() - { return m_weighted_sum; } - /** - * @brief Gets the log likelihood of the enrollment samples - */ - double getLogLikelihood() const - { return m_loglikelihood; } - /** - * @brief Sets the log likelihood of the enrollment samples - */ - void setLogLikelihood(const double val) - { m_loglikelihood = val; } - - /** - * @brief Tells if the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number - * of samples) exists in this machine (does not check the base machine) - * \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$ - */ - bool hasGamma(const size_t a) const - { return (m_cache_gamma.find(a) != m_cache_gamma.end()); } - /** - * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of - * samples) \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$ - * Tries to find it from the base machine and then from this machine - * @warning an exception is thrown if gamma does not exists - */ - const blitz::Array<double,2>& getGamma(const size_t a) const; - /** - * @brief Gets the \f$\gamma_a\f$ matrix for a given \f$a\f$ (number of - * samples) \f$\gamma_a = (Id + a F^T \beta F)^{-1} = \mathcal{F}_{a}\f$ - * Tries to find it from the base machine and then from this machine - * @warning The matrix is computed if it does not already exists, - * and stored in this machine - */ - const blitz::Array<double,2>& getAddGamma(const size_t a); - - /** - * @brief Tells if the log likelihood constant term for a given \f$a\f$ - * (number of samples) exists in this machine - * (does not check the base machine) - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - */ - bool hasLogLikeConstTerm(const size_t a) const - { return (m_cache_loglike_constterm.find(a) != m_cache_loglike_constterm.end()); } - /** - * @brief Gets the log likelihood constant term for a given \f$a\f$ - * (number of samples) - * Tries to find it from the base machine and then from this machine - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - * @warning an exception is thrown if the value does not exists - */ - double getLogLikeConstTerm(const size_t a) const; - /** - * @brief Gets the log likelihood constant term for a given \f$a\f$ - * (number of samples) - * Tries to find it from the base machine and then from this machine - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - * @warning The value is computed if it does not already exists - */ - double getAddLogLikeConstTerm(const size_t a); - - /** - * @brief Clears the maps (\f$\gamma_a\f$ and loglike_constterm[a]). - */ - void clearMaps(); - - - /** - * @brief Compute the log-likelihood of the given sample and (optionally) - * the enrolled samples - */ - double computeLogLikelihood(const blitz::Array<double,1>& sample, - bool with_enrolled_samples=true) const; - /** - * @brief Compute the log-likelihood of the given samples and (optionally) - * the enrolled samples - */ - double computeLogLikelihood(const blitz::Array<double,2>& samples, - bool with_enrolled_samples=true) const; - - /** - * @brief Computes a log likelihood ratio from a 1D or 2D blitz::Array - */ - double forward(const blitz::Array<double,1>& sample); - double forward_(const blitz::Array<double,1>& sample); - double forward(const blitz::Array<double,2>& samples); - - - private: - /** - * @brief Associated PLDABase containing the model (\f$\mu\f$, - * \f$F\f$, \f$G\f$ and \f$\Sigma\f$) - */ - boost::shared_ptr<PLDABase> m_plda_base; - uint64_t m_n_samples; ///< Number of enrollment samples - /** - * @brief Contains the value:\n - * \f$A = -0.5 (\sum_{i} x_{i}^{T} \Sigma^{-1} x_{i} - x_{i}^T \Sigma^{-1} G \alpha G^{T} \Sigma^{-1} x_{i})\f$\n - * \f$A = -0.5 \sum_{i} x_{i}^T \beta x_{i}\f$\n - * used in the likelihood computation (first \f$x_{i}\f$ dependent term) - */ - double m_nh_sum_xit_beta_xi; - /** - * @brief Contains the value \f$\sum_{i} F^T \beta x_{i}\f$ used in the - * likelihood computation (for the second \f$x_{i}\f$ dependent term) - */ - blitz::Array<double,1> m_weighted_sum; - double m_loglikelihood; ///< Log likelihood of the enrollment samples - /** - * @brief \f$\gamma_a\f$ balues which are not already in the - * PLDABase \f$\gamma_a = (Id + a F^T \beta F)^{-1}\f$ - * (depend on the number of samples \f$a\f$) - */ - std::map<size_t, blitz::Array<double,2> > m_cache_gamma; - /** - * @brief Log likelihood constant terms which depend on the number of - * samples \f$a\f$ - * \f$l_{a} = \frac{a}{2} ( -D log(2\pi) -log|\Sigma| +log|\alpha| +log|\gamma_a|)\f$ - */ - std::map<size_t, double> m_cache_loglike_constterm; - - - // working arrays - mutable blitz::Array<double,1> m_tmp_d_1; ///< Cache vector of size dim_d - mutable blitz::Array<double,1> m_tmp_d_2; ///< Cache vector of size dim_d - mutable blitz::Array<double,1> m_tmp_nf_1; ///< Cache vector of size dim_f - mutable blitz::Array<double,1> m_tmp_nf_2; ///< Cache vector of size dim_f - mutable blitz::Array<double,2> m_tmp_nf_nf_1; ///< Cache vector of size dim_f dim_f - - /** - * @brief Resizes the PLDAMachine - */ - void resize(const size_t dim_d, const size_t dim_f, const size_t dim_g); - /** - * @brief Resize working arrays - */ - void resizeTmp(); -}; - -} } } // namespaces - -#endif // BOB_LEARN_EM_PLDAMACHINE_H diff --git a/bob/learn/em/include/bob.learn.em/PLDATrainer.h b/bob/learn/em/include/bob.learn.em/PLDATrainer.h deleted file mode 100644 index f5bc4024c14b3e013e52795201fe9ba72f9ba813..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/PLDATrainer.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @date Fri Oct 14 18:07:56 2011 +0200 - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * - * @brief Probabilistic PLDA Discriminant Analysis implemented using - * Expectation Maximization. - * - * Copyright (C) Idiap Research Institute, Martigny, Switzerland - */ - -#ifndef BOB_LEARN_EM_PLDA_TRAINER_H -#define BOB_LEARN_EM_PLDA_TRAINER_H - -#include <bob.learn.em/PLDAMachine.h> -#include <boost/shared_ptr.hpp> -#include <vector> -#include <map> -#include <bob.core/array_copy.h> -#include <boost/random.hpp> -#include <boost/random/mersenne_twister.hpp> - -namespace bob { namespace learn { namespace em { - -/** - * @brief This class can be used to train the \f$F\f$, \f$G\f$ and - * \f$\Sigma\f$ matrices and the mean vector \f$\mu\f$ of a PLDA model.\n - * References:\n - * 1. 'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: - * Applied to Face Recognition', Laurent El Shafey, Chris McCool, - * Roy Wallace, Sebastien Marcel, TPAMI'2013 - * 2. 'Probabilistic Linear Discriminant Analysis for Inference About - * Identity', Prince and Elder, ICCV'2007\n - * 3. 'Probabilistic Models for Inference about Identity', Li, Fu, Mohammed, - * Elder and Prince, TPAMI'2012 - */ -class PLDATrainer -{ - public: //api - /** - * @brief Default constructor.\n Initializes a new PLDA trainer. The - * training stage will place the resulting components in the - * PLDABase. - */ - PLDATrainer(const bool use_sum_second_order); - - /** - * @brief Copy constructor - */ - PLDATrainer(const PLDATrainer& other); - - /** - * @brief (virtual) Destructor - */ - virtual ~PLDATrainer(); - - /** - * @brief Assignment operator - */ - PLDATrainer& operator=(const PLDATrainer& other); - - /** - * @brief Equal to - */ - bool operator==(const PLDATrainer& other) const; - - /** - * @brief Not equal to - */ - bool operator!=(const PLDATrainer& other) const; - - /** - * @brief Similarity operator - */ - bool is_similar_to(const PLDATrainer& b, - const double r_epsilon=1e-5, const double a_epsilon=1e-8) const; - - /** - * @brief Performs some initialization before the E- and M-steps. - */ - void initialize(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - /** - * @brief Performs some actions after the end of the E- and M-steps. - */ - void finalize(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - /** - * @brief Calculates and saves statistics across the dataset, and saves - * these as m_z_{first,second}_order. - * The statistics will be used in the mStep() that follows. - */ - void eStep(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - /** - * @brief Performs a maximization step to update the parameters of the - * PLDABase - */ - void mStep(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - - /** - * @brief Sets whether the second order statistics are stored during the - * training procedure, or only their sum. - */ - void setUseSumSecondOrder(bool v) { m_use_sum_second_order = v; } - /** - * @brief Tells whether the second order statistics are stored during the - * training procedure, or only their sum. - */ - bool getUseSumSecondOrder() const - { return m_use_sum_second_order; } - - /** - * @brief This enum defines different methods for initializing the \f$F\f$ - * subspace - */ - typedef enum { - RANDOM_F=0, - BETWEEN_SCATTER=1 - } - InitFMethod; - /** - * @brief This enum defines different methods for initializing the \f$G\f$ - * subspace - */ - typedef enum { - RANDOM_G=0, - WITHIN_SCATTER=1 - } - InitGMethod; - /** - * @brief This enum defines different methods for initializing the - * \f$\Sigma\f$ covariance matrix - */ - typedef enum { - RANDOM_SIGMA=0, - VARIANCE_G=1, - CONSTANT=2, - VARIANCE_DATA=3 - } - InitSigmaMethod; - /** - * @brief Sets the method used to initialize \f$F\f$ - */ - void setInitFMethod(const InitFMethod m) { m_initF_method = m; } - /** - * @brief Gets the method used to initialize \f$F\f$ - */ - InitFMethod getInitFMethod() const { return m_initF_method; } - /** - * @brief Sets the ratio value used to initialize \f$F\f$ - */ - void setInitFRatio(double d) { m_initF_ratio = d; } - /** - * @brief Gets the ratio value used to initialize \f$F\f$ - */ - double getInitFRatio() const { return m_initF_ratio; } - /** - * @brief Sets the method used to initialize \f$G\f$ - */ - void setInitGMethod(const InitGMethod m) { m_initG_method = m; } - /** - * @brief Gets the method used to initialize \f$G\f$ - */ - InitGMethod getInitGMethod() const { return m_initG_method; } - /** - * @brief Sets the ratio value used to initialize \f$G\f$ - */ - void setInitGRatio(double d) { m_initG_ratio = d; } - /** - * @brief Gets the ratio value used to initialize \f$G\f$ - */ - double getInitGRatio() const { return m_initG_ratio; } - /** - * @brief Sets the method used to initialize \f$\Sigma\f$ - */ - void setInitSigmaMethod(const InitSigmaMethod m) - { m_initSigma_method = m; } - /** - * @brief Gets the method used to initialize \f$\Sigma\f$ - */ - InitSigmaMethod getInitSigmaMethod() const - { return m_initSigma_method; } - /** - * @brief Sets the ratio value used to initialize \f$\Sigma\f$ - */ - void setInitSigmaRatio(double d) { m_initSigma_ratio = d; } - /** - * @brief Gets the ratio value used to initialize \f$\Sigma\f$ - */ - double getInitSigmaRatio() const { return m_initSigma_ratio; } - - /** - * @brief Gets the z first order statistics (mostly for test purposes) - */ - const std::vector<blitz::Array<double,2> >& getZFirstOrder() const - { return m_cache_z_first_order;} - /** - * @brief Gets the z second order statistics (mostly for test purposes) - */ - const blitz::Array<double,2>& getZSecondOrderSum() const - { return m_cache_sum_z_second_order;} - /** - * @brief Gets the z second order statistics (mostly for test purposes) - */ - const std::vector<blitz::Array<double,3> >& getZSecondOrder() const - { if(m_use_sum_second_order) - throw std::runtime_error("You should disable the use_sum_second_order flag to use this feature"); - return m_cache_z_second_order; - } - - /** - * @brief Main procedure for enrolling a PLDAMachine - */ - void enroll(bob::learn::em::PLDAMachine& plda_machine, - const blitz::Array<double,2>& ar) const; - - - /** - * @brief Sets the Random Number Generator - */ - void setRng(boost::shared_ptr<boost::mt19937> rng) - { m_rng = rng; } - - /** - * @brief Gets the Random Number Generator - */ - boost::shared_ptr<boost::mt19937> getRng() const - { return m_rng; } - - private: - - boost::shared_ptr<boost::mt19937> m_rng; - - //representation - size_t m_dim_d; ///< Dimensionality of the input features - size_t m_dim_f; ///< Size/rank of the \f$F\f$ subspace - size_t m_dim_g; ///< Size/rank of the \f$G\f$ subspace - bool m_use_sum_second_order; ///< If set, only the sum of the second order statistics is stored/allocated - InitFMethod m_initF_method; ///< Initialization method for \f$F\f$ - double m_initF_ratio; ///< Ratio/factor used for the initialization of \f$F\f$ - InitGMethod m_initG_method; ///< Initialization method for \f$G\f$ - double m_initG_ratio; ///< Ratio/factor used for the initialization of \f$G\f$ - InitSigmaMethod m_initSigma_method; ///< Initialization method for \f$\Sigma\f$ - double m_initSigma_ratio; ///< Ratio/factor used for the initialization of \f$\Sigma\f$ - - // Statistics and covariance computed during the training process - blitz::Array<double,2> m_cache_S; ///< Covariance of the training data - std::vector<blitz::Array<double,2> > m_cache_z_first_order; ///< Current mean of the z_{n} latent variable (1 for each sample) - blitz::Array<double,2> m_cache_sum_z_second_order; ///< Current sum of the covariance of the z_{n} latent variable - std::vector<blitz::Array<double,3> > m_cache_z_second_order; ///< Current covariance of the z_{n} latent variable - // Precomputed - /** - * @brief Number of training samples for each individual in the training set - */ - std::vector<size_t> m_cache_n_samples_per_id; - /** - * @brief Tells if there is an identity with a 'key'/particular number of - * training samples, and if corresponding matrices are up to date. - */ - std::map<size_t,bool> m_cache_n_samples_in_training; - blitz::Array<double,2> m_cache_B; ///< \f$B = [F, G]\f$ (size nfeatures x (m_dim_f+m_dim_g) ) - blitz::Array<double,2> m_cache_Ft_isigma_G; ///< \f$F^T \Sigma^-1 G\f$ - blitz::Array<double,2> m_cache_eta; ///< \f$F^T \Sigma^-1 G \alpha\f$ - // Blocks (with \f$\gamma_{a}\f$) of \f$(Id + A^T \Sigma'^-1 A)^-1\f$ (efficient inversion) - std::map<size_t,blitz::Array<double,2> > m_cache_zeta; ///< \f$\zeta_{a} = \alpha + \eta^T \gamma_{a} \eta\f$ - std::map<size_t,blitz::Array<double,2> > m_cache_iota; ///< \f$\iota_{a} = -\gamma_{a} \eta\f$ - - // Working arrays - mutable blitz::Array<double,1> m_tmp_nf_1; ///< vector of dimension dim_f - mutable blitz::Array<double,1> m_tmp_nf_2; ///< vector of dimension dim_f - mutable blitz::Array<double,1> m_tmp_ng_1; ///< vector of dimension dim_f - mutable blitz::Array<double,1> m_tmp_D_1; ///< vector of dimension dim_d - mutable blitz::Array<double,1> m_tmp_D_2; ///< vector of dimension dim_d - mutable blitz::Array<double,2> m_tmp_nfng_nfng; ///< matrix of dimension (dim_f+dim_g)x(dim_f+dim_g) - mutable blitz::Array<double,2> m_tmp_D_nfng_1; ///< matrix of dimension (dim_d)x(dim_f+dim_g) - mutable blitz::Array<double,2> m_tmp_D_nfng_2; ///< matrix of dimension (dim_d)x(dim_f+dim_g) - - // internal methods - void computeMeanVariance(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - void initMembers(const std::vector<blitz::Array<double,2> >& v_ar); - void initFGSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - void initF(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - void initG(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - void initSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - void checkTrainingData(const std::vector<blitz::Array<double,2> >& v_ar); - void precomputeFromFGSigma(bob::learn::em::PLDABase& machine); - void precomputeLogLike(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - void updateFG(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - void updateSigma(bob::learn::em::PLDABase& machine, - const std::vector<blitz::Array<double,2> >& v_ar); - - void resizeTmp(); -}; - -} } } // namespaces - -#endif /* BOB_LEARN_EM_PLDA_TRAINER_H */ diff --git a/bob/learn/em/include/bob.learn.em/api.h b/bob/learn/em/include/bob.learn.em/api.h deleted file mode 100644 index 7548a302f751d0a20b13d5966affffed670b8bf6..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/api.h +++ /dev/null @@ -1,127 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Fri 21 Nov 10:38:48 2013 - * - * @brief Python API for bob::learn::em - */ - -#ifndef BOB_LEARN_EM_API_H -#define BOB_LEARN_EM_API_H - -/* Define Module Name and Prefix for other Modules - Note: We cannot use BOB_EXT_* macros here, unfortunately */ -#define BOB_LEARN_EM_PREFIX "bob.learn.em" -#define BOB_LEARN_EM_FULL_NAME "bob.learn.em._library" - -#include <Python.h> - -#include <bob.learn.em/config.h> -#include <boost/shared_ptr.hpp> - -/******************* - * C API functions * - *******************/ - -/* Enum defining entries in the function table */ -enum _PyBobLearnEM_ENUM{ - PyBobLearnEM_APIVersion_NUM = 0, - // bindings - ////PyBobIpBaseLBP_Type_NUM, - ////PyBobIpBaseLBP_Check_NUM, - ////PyBobIpBaseLBP_Converter_NUM, - // Total number of C API pointers - PyBobLearnEM_API_pointers -}; - - -#ifdef BOB_LEARN_EM_MODULE - - /* This section is used when compiling `bob.io.base' itself */ - - /************** - * Versioning * - **************/ - - extern int PyBobLearnEM_APIVersion; - -#else // BOB_LEARN_EM_MODULE - - /* This section is used in modules that use `bob.io.base's' C-API */ - -#if defined(NO_IMPORT_ARRAY) - extern void **PyBobLearnEM_API; -#elif defined(PY_ARRAY_UNIQUE_SYMBOL) - void **PyBobLearnEM_API; -#else - static void **PyBobLearnEM_API=NULL; -#endif - - /************** - * Versioning * - **************/ - -#define PyBobLearnEM_APIVersion (*(int *)PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM]) - -#if !defined(NO_IMPORT_ARRAY) - - /** - * Returns -1 on error, 0 on success. - */ - static int import_bob_learn_em(void) { - - PyObject *c_api_object; - PyObject *module; - - module = PyImport_ImportModule(BOB_LEARN_EM_FULL_NAME); - - if (module == NULL) return -1; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - - if (c_api_object == NULL) { - Py_DECREF(module); - return -1; - } - -#if PY_VERSION_HEX >= 0x02070000 - if (PyCapsule_CheckExact(c_api_object)) { - PyBobLearnEM_API = (void **)PyCapsule_GetPointer(c_api_object, PyCapsule_GetName(c_api_object)); - } -#else - if (PyCObject_Check(c_api_object)) { - PyBobLearnEM_API = (void **)PyCObject_AsVoidPtr(c_api_object); - } -#endif - - Py_DECREF(c_api_object); - Py_DECREF(module); - - if (!PyBobLearnEM_API) { - PyErr_SetString(PyExc_ImportError, "cannot find C/C++ API " -#if PY_VERSION_HEX >= 0x02070000 - "capsule" -#else - "cobject" -#endif - " at `" BOB_LEARN_EM_FULL_NAME "._C_API'"); - return -1; - } - - /* Checks that the imported version matches the compiled version */ - int imported_version = *(int*)PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM]; - - if (BOB_LEARN_EM_API_VERSION != imported_version) { - PyErr_Format(PyExc_ImportError, BOB_LEARN_EM_FULL_NAME " import error: you compiled against API version 0x%04x, but are now importing an API with version 0x%04x which is not compatible - check your Python runtime environment for errors", BOB_LEARN_EM_API_VERSION, imported_version); - return -1; - } - - /* If you get to this point, all is good */ - return 0; - - } - -#endif //!defined(NO_IMPORT_ARRAY) - -#endif /* BOB_LEARN_EM_MODULE */ - -#endif /* BOB_LEARN_EM_API_H */ diff --git a/bob/learn/em/include/bob.learn.em/config.h b/bob/learn/em/include/bob.learn.em/config.h deleted file mode 100644 index 71676a62966c87370c320b1c692ed5fa78f531e6..0000000000000000000000000000000000000000 --- a/bob/learn/em/include/bob.learn.em/config.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @author Manuel Guenther <manuel.guenther@idiap.ch> - * @date Thu Aug 21 20:49:42 CEST 2014 - * - * @brief General directives for all modules in bob.learn.em - */ - -#ifndef BOB_LEARN_EM_CONFIG_H -#define BOB_LEARN_EM_CONFIG_H - -/* Macros that define versions and important names */ -#define BOB_LEARN_EM_API_VERSION 0x0200 - -#ifdef BOB_IMPORT_VERSION - - /*************************************** - * Here we define some functions that should be used to build version dictionaries in the version.cpp file - * There will be a compiler warning, when these functions are not used, so use them! - ***************************************/ - - #include <Python.h> - #include <boost/preprocessor/stringize.hpp> - - /** - * bob.learn.em c/c++ api version - */ - static PyObject* bob_learn_em_version() { - return Py_BuildValue("{ss}", "api", BOOST_PP_STRINGIZE(BOB_LEARN_EM_API_VERSION)); - } - -#endif // BOB_IMPORT_VERSION - -#endif /* BOB_LEARN_EM_CONFIG_H */ diff --git a/bob/learn/em/isv_base.cpp b/bob/learn/em/isv_base.cpp deleted file mode 100644 index 352e8ef66ed09519cdcbd96804fbbc97dee6e545..0000000000000000000000000000000000000000 --- a/bob/learn/em/isv_base.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/** - * @date Wed Jan 28 11:13:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto ISVBase_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".ISVBase", - - "A ISVBase instance can be seen as a container for U and D when performing Joint Factor Analysis (JFA).\n\n" - "References: [Vogt2008]_ [McCool2013]_", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a ISVBase", - "", - true - ) - .add_prototype("ubm,ru","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.") - .add_parameter("ru", "int", "Size of U (Within client variation matrix). In the end the U matrix will have (number_of_gaussians * feature_dimension x ru)") - .add_parameter("other", ":py:class:`bob.learn.em.ISVBase`", "A ISVBase object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMISVBase_init_copy(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVBase_doc.kwlist(1); - PyBobLearnEMISVBaseObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVBase_Type, &o)){ - ISVBase_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVBase(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMISVBase_init_hdf5(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVBase_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - ISVBase_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - - self->cxx.reset(new bob::learn::em::ISVBase(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMISVBase_init_ubm(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVBase_doc.kwlist(0); - - PyBobLearnEMGMMMachineObject* ubm; - int ru = 1; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i", kwlist, &PyBobLearnEMGMMMachine_Type, &ubm, &ru)){ - ISVBase_doc.print_usage(); - return -1; - } - - if(ru < 0){ - PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one"); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVBase(ubm->cxx, ru)); - return 0; -} - - -static int PyBobLearnEMISVBase_init(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMISVBase_Check(arg)) - return PyBobLearnEMISVBase_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMISVBase_init_hdf5(self, args, kwargs); - } - case 2: - return PyBobLearnEMISVBase_init_ubm(self, args, kwargs); - default: - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - ISVBase_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create ISVBase", -1) - return 0; -} - - -static void PyBobLearnEMISVBase_delete(PyBobLearnEMISVBaseObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMISVBase_RichCompare(PyBobLearnEMISVBaseObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMISVBase_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMISVBaseObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare ISVBase objects", 0) -} - -int PyBobLearnEMISVBase_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVBase_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int)", - "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rU (within client variability matrix) `(#Gaussians, #Inputs, #rU)`.", - "" -); -PyObject* PyBobLearnEMISVBase_getShape(PyBobLearnEMISVBaseObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** supervector_length *****/ -static auto supervector_length = bob::extension::VariableDoc( - "supervector_length", - "int", - "Returns the supervector length.", - "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality" - "An exception is thrown if no Universal Background Model has been set yet." - "" -); -PyObject* PyBobLearnEMISVBase_getSupervectorLength(PyBobLearnEMISVBaseObject* self, void*) { - BOB_TRY - return Py_BuildValue("i", self->cxx->getSupervectorLength()); - BOB_CATCH_MEMBER("supervector_length could not be read", 0) -} - - -/***** u *****/ -static auto U = bob::extension::VariableDoc( - "u", - "array_like <float, 2D>", - "Returns the U matrix (within client variability matrix)", - "" -); -PyObject* PyBobLearnEMISVBase_getU(PyBobLearnEMISVBaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getU()); - BOB_CATCH_MEMBER("``u`` could not be read", 0) -} -int PyBobLearnEMISVBase_setU(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getU().extent(0) && input->shape[1] != self->cxx->getU().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)self->cxx->getU().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], U.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "u"); - if (!b) return -1; - self->cxx->setU(*b); - return 0; - BOB_CATCH_MEMBER("``u`` matrix could not be set", -1) -} - - -/***** d *****/ -static auto D = bob::extension::VariableDoc( - "d", - "array_like <float, 1D>", - "Returns the diagonal matrix diag(d) (as a 1D vector)", - "" -); -PyObject* PyBobLearnEMISVBase_getD(PyBobLearnEMISVBaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getD()); - BOB_CATCH_MEMBER("``d`` could not be read", 0) -} -int PyBobLearnEMISVBase_setD(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getD().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)input->shape[0], D.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "d"); - if (!b) return -1; - self->cxx->setD(*b); - return 0; - BOB_CATCH_MEMBER("``d`` matrix could not be set", -1) -} - - -/***** ubm *****/ -static auto ubm = bob::extension::VariableDoc( - "ubm", - ":py:class:`bob.learn.em.GMMMachine`", - "Returns the UBM (Universal Background Model", - "" -); -PyObject* PyBobLearnEMISVBase_getUBM(PyBobLearnEMISVBaseObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm(); - - //Allocating the correspondent python object - PyBobLearnEMGMMMachineObject* retval = - (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0); - retval->cxx = ubm_gmmMachine; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("ubm could not be read", 0) -} -int PyBobLearnEMISVBase_setUBM(PyBobLearnEMISVBaseObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMGMMMachine_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name()); - return -1; - } - - PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine); - - self->cxx->setUbm(ubm_gmmMachine->cxx); - - return 0; - BOB_CATCH_MEMBER("ubm could not be set", -1) -} - - - -static PyGetSetDef PyBobLearnEMISVBase_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMISVBase_getShape, - 0, - shape.doc(), - 0 - }, - - { - supervector_length.name(), - (getter)PyBobLearnEMISVBase_getSupervectorLength, - 0, - supervector_length.doc(), - 0 - }, - - { - U.name(), - (getter)PyBobLearnEMISVBase_getU, - (setter)PyBobLearnEMISVBase_setU, - U.doc(), - 0 - }, - - { - D.name(), - (getter)PyBobLearnEMISVBase_getD, - (setter)PyBobLearnEMISVBase_setD, - D.doc(), - 0 - }, - - { - ubm.name(), - (getter)PyBobLearnEMISVBase_getUBM, - (setter)PyBobLearnEMISVBase_setUBM, - ubm.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the ISVBase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMISVBase_Save(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the ISVBase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMISVBase_Load(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this ISVBase with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.ISVBase`", "A ISVBase object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMISVBase_IsSimilarTo(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMISVBaseObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMISVBase_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Resets the dimensionality of the subspace U. " - "U is hence uninitialized.", - 0, - true -) -.add_prototype("rU") -.add_parameter("rU", "int", "Size of U (Within client variation matrix)"); -static PyObject* PyBobLearnEMISVBase_resize(PyBobLearnEMISVBaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int rU = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &rU)) return 0; - - if (rU <= 0){ - PyErr_Format(PyExc_TypeError, "rU must be greater than zero"); - resize.print_usage(); - return 0; - } - - self->cxx->resize(rU); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - - - -static PyMethodDef PyBobLearnEMISVBase_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMISVBase_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMISVBase_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMISVBase_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMISVBase_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the ISV type struct; will be initialized later -PyTypeObject PyBobLearnEMISVBase_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMISVBase(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMISVBase_Type.tp_name = ISVBase_doc.name(); - PyBobLearnEMISVBase_Type.tp_basicsize = sizeof(PyBobLearnEMISVBaseObject); - PyBobLearnEMISVBase_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMISVBase_Type.tp_doc = ISVBase_doc.doc(); - - // set the functions - PyBobLearnEMISVBase_Type.tp_new = PyType_GenericNew; - PyBobLearnEMISVBase_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMISVBase_init); - PyBobLearnEMISVBase_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMISVBase_delete); - PyBobLearnEMISVBase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVBase_RichCompare); - PyBobLearnEMISVBase_Type.tp_methods = PyBobLearnEMISVBase_methods; - PyBobLearnEMISVBase_Type.tp_getset = PyBobLearnEMISVBase_getseters; - //PyBobLearnEMISVBase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVBase_forward); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMISVBase_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMISVBase_Type); - return PyModule_AddObject(module, "ISVBase", (PyObject*)&PyBobLearnEMISVBase_Type) >= 0; -} - diff --git a/bob/learn/em/isv_machine.cpp b/bob/learn/em/isv_machine.cpp deleted file mode 100644 index 097467a553d36b617fe1be644e3fddc04ba52bbf..0000000000000000000000000000000000000000 --- a/bob/learn/em/isv_machine.cpp +++ /dev/null @@ -1,702 +0,0 @@ -/** - * @date Wed Jan 28 13:03:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto ISVMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".ISVMachine", - "A ISVMachine. An attached :py:class:`bob.learn.em.ISVBase` should be provided for Joint Factor Analysis. The :py:class:`bob.learn.em.ISVMachine` carries information about the speaker factors :math:`y` and :math:`z`, whereas a :py:class:`bob.learn.em.JFABase` carries information about the matrices :math:`U` and :math:`D`.\n\n" - "References: [Vogt2008]_ [McCool2013]_", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new ISVMachine", - "", - true - ) - .add_prototype("isv_base","") - .add_prototype("other","") - .add_prototype("hdf5","") - - .add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "The ISVBase associated with this machine") - .add_parameter("other", ":py:class:`bob.learn.em.ISVMachine`", "A ISVMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMISVMachine_init_copy(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVMachine_doc.kwlist(1); - PyBobLearnEMISVMachineObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVMachine_Type, &o)){ - ISVMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVMachine(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMISVMachine_init_hdf5(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - ISVMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::ISVMachine(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMISVMachine_init_isvbase(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVMachine_doc.kwlist(0); - - PyBobLearnEMISVBaseObject* isv_base; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base)){ - ISVMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVMachine(isv_base->cxx)); - return 0; -} - - -static int PyBobLearnEMISVMachine_init(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs == 1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMISVMachine_Check(arg)) - return PyBobLearnEMISVMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMISVMachine_init_hdf5(self, args, kwargs); - // If the constructor input is a JFABase Object - else - return PyBobLearnEMISVMachine_init_isvbase(self, args, kwargs); - } - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - ISVMachine_doc.print_usage(); - return -1; - } - - BOB_CATCH_MEMBER("cannot create ISVMachine", -1) - return 0; -} - -static void PyBobLearnEMISVMachine_delete(PyBobLearnEMISVMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMISVMachine_RichCompare(PyBobLearnEMISVMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMISVMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMISVMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare ISVMachine objects", 0) -} - -int PyBobLearnEMISVMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int, int)", - "A tuple that represents the number of gaussians, dimensionality of each Gaussian and dimensionality of the rU (within client variability matrix)) ``(#Gaussians, #Inputs, #rU)``.", - "" -); -PyObject* PyBobLearnEMISVMachine_getShape(PyBobLearnEMISVMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** supervector_length *****/ -static auto supervector_length = bob::extension::VariableDoc( - "supervector_length", - "int", - - "Returns the supervector length.", - "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality. " - "An exception is thrown if no Universal Background Model has been set yet." - "" -); -PyObject* PyBobLearnEMISVMachine_getSupervectorLength(PyBobLearnEMISVMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("i", self->cxx->getSupervectorLength()); - BOB_CATCH_MEMBER("supervector_length could not be read", 0) -} - -/***** z *****/ -static auto Z = bob::extension::VariableDoc( - "z", - "array_like <float, 1D>", - "Returns the :math:`z` speaker factor. Eq (31) from [McCool2013]_", - "" -); -PyObject* PyBobLearnEMISVMachine_getZ(PyBobLearnEMISVMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZ()); - BOB_CATCH_MEMBER("`z` could not be read", 0) -} -int PyBobLearnEMISVMachine_setZ(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getZ().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getZ().extent(0), (Py_ssize_t)input->shape[0], Z.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "z"); - if (!b) return -1; - self->cxx->setZ(*b); - return 0; - BOB_CATCH_MEMBER("`z` vector could not be set", -1) -} - - -/***** x *****/ -static auto X = bob::extension::VariableDoc( - "x", - "array_like <float, 1D>", - "Returns the :math:`X` session factor. Eq (29) from [McCool2013]_", - "The latent variable x (last one computed). This is a feature provided for convenience, but this attribute is not 'part' of the machine. The session latent variable :math:`x` is indeed not class-specific, but depends on the sample considered. Furthermore, it is not saved into the machine or used when comparing machines." -); -PyObject* PyBobLearnEMISVMachine_getX(PyBobLearnEMISVMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getX()); - BOB_CATCH_MEMBER("`x` could not be read", 0) -} -int PyBobLearnEMISVMachine_setX(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, X.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, X.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, X.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getX().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getX().extent(0), (Py_ssize_t)input->shape[0], X.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "x"); - if (!b) return -1; - self->cxx->setX(*b); - return 0; - BOB_CATCH_MEMBER("`x` vector could not be set", -1) -} - - -/***** isv_base *****/ -static auto isv_base = bob::extension::VariableDoc( - "isv_base", - ":py:class:`bob.learn.em.ISVBase`", - "The ISVBase attached to this machine", - "" -); -PyObject* PyBobLearnEMISVMachine_getISVBase(PyBobLearnEMISVMachineObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::ISVBase> isv_base_o = self->cxx->getISVBase(); - - //Allocating the correspondent python object - PyBobLearnEMISVBaseObject* retval = - (PyBobLearnEMISVBaseObject*)PyBobLearnEMISVBase_Type.tp_alloc(&PyBobLearnEMISVBase_Type, 0); - retval->cxx = isv_base_o; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("isv_base could not be read", 0) -} -int PyBobLearnEMISVMachine_setISVBase(PyBobLearnEMISVMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMISVBase_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.ISVBase`", Py_TYPE(self)->tp_name, isv_base.name()); - return -1; - } - - PyBobLearnEMISVBaseObject* isv_base_o = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMISVBase_Type,&isv_base_o); - - self->cxx->setISVBase(isv_base_o->cxx); - - return 0; - BOB_CATCH_MEMBER("isv_base could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMISVMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMISVMachine_getShape, - 0, - shape.doc(), - 0 - }, - - { - supervector_length.name(), - (getter)PyBobLearnEMISVMachine_getSupervectorLength, - 0, - supervector_length.doc(), - 0 - }, - - { - isv_base.name(), - (getter)PyBobLearnEMISVMachine_getISVBase, - (setter)PyBobLearnEMISVMachine_setISVBase, - isv_base.doc(), - 0 - }, - - { - Z.name(), - (getter)PyBobLearnEMISVMachine_getZ, - (setter)PyBobLearnEMISVMachine_setZ, - Z.doc(), - 0 - }, - - { - X.name(), - (getter)PyBobLearnEMISVMachine_getX, - (setter)PyBobLearnEMISVMachine_setX, - X.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the ISVMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMISVMachine_Save(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the ISVMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMISVMachine_Load(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this ISVMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.ISVMachine`", "A ISVMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMISVMachine_IsSimilarTo(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMISVMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMISVMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** estimate_x ***/ -static auto estimate_x = bob::extension::FunctionDoc( - "estimate_x", - "Estimates the session offset x (LPT assumption) given GMM statistics.", - "Estimates :math:`x` from the GMM statistics considering the LPT assumption, that is the latent session variable :math:`x` is approximated using the UBM", - true -) -.add_prototype("stats,input") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM") -.add_parameter("input", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMISVMachine_estimateX(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = estimate_x.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&input)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_x.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_x.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), (Py_ssize_t)input->shape[0], estimate_x.name()); - return 0; - } - - self->cxx->estimateX(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input)); - - BOB_CATCH_MEMBER("cannot estimate X", 0) - Py_RETURN_NONE; -} - - -/*** estimate_ux ***/ -static auto estimate_ux = bob::extension::FunctionDoc( - "estimate_ux", - "Estimates Ux (LPT assumption) given GMM statistics.", - "Estimates Ux from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM.", - true -) -.add_prototype("stats,input") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM") -.add_parameter("input", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMISVMachine_estimateUx(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = estimate_ux.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&input)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_ux.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_ux.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs()*(Py_ssize_t)self->cxx->getNGaussians(), (Py_ssize_t)input->shape[0], estimate_ux.name()); - return 0; - } - - self->cxx->estimateUx(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input)); - - BOB_CATCH_MEMBER("cannot estimate Ux", 0) - Py_RETURN_NONE; -} - - -/*** forward_ux ***/ -static auto forward_ux = bob::extension::FunctionDoc( - "forward_ux", - "Computes a score for the given UBM statistics and given the Ux vector", - "", - true -) -.add_prototype("stats,ux") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input") -.add_parameter("ux", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMISVMachine_ForwardUx(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = forward_ux.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* ux_input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&ux_input)) - return 0; - - //protects acquired resources through this scope - auto ux_input_ = make_safe(ux_input); - - // perform check on the input - if (ux_input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, forward_ux.name()); - return 0; - } - - if (ux_input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, forward_ux.name()); - return 0; - } - - if (ux_input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), (Py_ssize_t)ux_input->shape[0], forward_ux.name()); - return 0; - } - double score = self->cxx->forward(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(ux_input)); - - return Py_BuildValue("d", score); - BOB_CATCH_MEMBER("cannot forward_ux", 0) -} - - -/*** forward ***/ -static auto forward = bob::extension::FunctionDoc( - "forward", - "Execute the machine", - "", - true -) -.add_prototype("stats") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input"); -static PyObject* PyBobLearnEMISVMachine_Forward(PyBobLearnEMISVMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = forward.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - //protects acquired resources through this scope - double score = self->cxx->forward(*stats->cxx); - - return Py_BuildValue("d", score); - BOB_CATCH_MEMBER("cannot forward", 0) - -} - - -static PyMethodDef PyBobLearnEMISVMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMISVMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMISVMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMISVMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - - { - estimate_x.name(), - (PyCFunction)PyBobLearnEMISVMachine_estimateX, - METH_VARARGS|METH_KEYWORDS, - estimate_x.doc() - }, - - { - estimate_ux.name(), - (PyCFunction)PyBobLearnEMISVMachine_estimateUx, - METH_VARARGS|METH_KEYWORDS, - estimate_ux.doc() - }, - - { - forward_ux.name(), - (PyCFunction)PyBobLearnEMISVMachine_ForwardUx, - METH_VARARGS|METH_KEYWORDS, - forward_ux.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMISVMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMISVMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMISVMachine_Type.tp_name = ISVMachine_doc.name(); - PyBobLearnEMISVMachine_Type.tp_basicsize = sizeof(PyBobLearnEMISVMachineObject); - PyBobLearnEMISVMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMISVMachine_Type.tp_doc = ISVMachine_doc.doc(); - - // set the functions - PyBobLearnEMISVMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMISVMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMISVMachine_init); - PyBobLearnEMISVMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMISVMachine_delete); - PyBobLearnEMISVMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVMachine_RichCompare); - PyBobLearnEMISVMachine_Type.tp_methods = PyBobLearnEMISVMachine_methods; - PyBobLearnEMISVMachine_Type.tp_getset = PyBobLearnEMISVMachine_getseters; - PyBobLearnEMISVMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVMachine_Forward); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMISVMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMISVMachine_Type); - return PyModule_AddObject(module, "ISVMachine", (PyObject*)&PyBobLearnEMISVMachine_Type) >= 0; -} - diff --git a/bob/learn/em/isv_trainer.cpp b/bob/learn/em/isv_trainer.cpp deleted file mode 100644 index dbf310d1ee27aa999ad019aa27fef29d720b4036..0000000000000000000000000000000000000000 --- a/bob/learn/em/isv_trainer.cpp +++ /dev/null @@ -1,612 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Mon 02 Fev 20:20:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" -#include <boost/make_shared.hpp> - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static int extract_GMMStats_1d(PyObject *list, - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - training_data.push_back(stats->cxx); - } - return 0; -} - -static int extract_GMMStats_2d(PyObject *list, - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyObject* another_list; - if(!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyList_Type, &another_list)){ - PyErr_Format(PyExc_RuntimeError, "Expected a list of lists of GMMStats objects [[GMMStats,GMMStats],[GMMStats,GMMStats].....[GMMStats,GMMStats]]"); - return -1; - } - - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > another_training_data; - for (int j=0; j<PyList_GET_SIZE(another_list); j++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(another_list, j), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - another_training_data.push_back(stats->cxx); - } - training_data.push_back(another_training_data); - } - return 0; -} - -template <int N> -static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec) -{ - PyObject* list = PyList_New(vec.size()); - for(size_t i=0; i<vec.size(); i++){ - blitz::Array<double,N> numpy_array = vec[i]; - PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array); - PyList_SET_ITEM(list, i, numpy_py_object); - } - return list; -} - -template <int N> -int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyBlitzArrayObject* blitz_object; - if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){ - PyErr_Format(PyExc_RuntimeError, "Expected numpy array object"); - return -1; - } - auto blitz_object_ = make_safe(blitz_object); - vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object)); - } - return 0; -} - - - -static auto ISVTrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".ISVTrainer", - "ISVTrainer" - "Train Intersession varibility modeling :ref:`ISV <isv>`.", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new ISVTrainer", - "", - true - ) - .add_prototype("relevance_factor","") - .add_prototype("other","") - .add_prototype("","") - .add_parameter("other", ":py:class:`bob.learn.em.ISVTrainer`", "A ISVTrainer object to be copied.") - .add_parameter("relevance_factor", "float", "") -); - - -static int PyBobLearnEMISVTrainer_init_copy(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVTrainer_doc.kwlist(1); - PyBobLearnEMISVTrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMISVTrainer_Type, &o)){ - ISVTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVTrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMISVTrainer_init_number(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ISVTrainer_doc.kwlist(0); - double relevance_factor = 4.; - - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d", kwlist, &relevance_factor)) - return -1; - - if(relevance_factor < 0){ - PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than zero"); - return -1; - } - - self->cxx.reset(new bob::learn::em::ISVTrainer(relevance_factor)); - return 0; -} - - -static int PyBobLearnEMISVTrainer_init(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch(nargs){ - case 0:{ - self->cxx.reset(new bob::learn::em::ISVTrainer()); - return 0; - } - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - if(PyBobLearnEMISVTrainer_Check(arg)) - // If the constructor input is ISVTrainer object - return PyBobLearnEMISVTrainer_init_copy(self, args, kwargs); - else - return PyBobLearnEMISVTrainer_init_number(self, args, kwargs); - - } - default:{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - ISVTrainer_doc.print_usage(); - return -1; - } - } - BOB_CATCH_MEMBER("cannot create ISVTrainer", -1) - return 0; -} - - -static void PyBobLearnEMISVTrainer_delete(PyBobLearnEMISVTrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMISVTrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMISVTrainer_Type)); -} - - -static PyObject* PyBobLearnEMISVTrainer_RichCompare(PyBobLearnEMISVTrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMISVTrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMISVTrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare ISVTrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -static auto acc_u_a1 = bob::extension::VariableDoc( - "acc_u_a1", - "array_like <float, 3D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMISVTrainer_get_acc_u_a1(PyBobLearnEMISVTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA1()); - BOB_CATCH_MEMBER("acc_u_a1 could not be read", 0) -} -int PyBobLearnEMISVTrainer_set_acc_u_a1(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - - if (input->ndim != 3){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccUA1().extent(2)) { - PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA1().extent(0), (Py_ssize_t)self->cxx->getAccUA1().extent(1), (Py_ssize_t)self->cxx->getAccUA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_u_a1.name()); - return -1; - } - - - auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_u_a1"); - if (!b) return -1; - self->cxx->setAccUA1(*b); - return 0; - BOB_CATCH_MEMBER("acc_u_a1 could not be set", -1) -} - - -static auto acc_u_a2 = bob::extension::VariableDoc( - "acc_u_a2", - "array_like <float, 2D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMISVTrainer_get_acc_u_a2(PyBobLearnEMISVTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA2()); - BOB_CATCH_MEMBER("acc_u_a2 could not be read", 0) -} -int PyBobLearnEMISVTrainer_set_acc_u_a2(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA2().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA2().extent(0), (Py_ssize_t)self->cxx->getAccUA2().extent(1), input->shape[0], input->shape[1], acc_u_a2.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_u_a2"); - if (!b) return -1; - self->cxx->setAccUA2(*b); - return 0; - BOB_CATCH_MEMBER("acc_u_a2 could not be set", -1) -} - - -static auto __X__ = bob::extension::VariableDoc( - "__X__", - "list", - "", - "" -); -PyObject* PyBobLearnEMISVTrainer_get_X(PyBobLearnEMISVTrainerObject* self, void*){ - BOB_TRY - return vector_as_list(self->cxx->getX()); - BOB_CATCH_MEMBER("__X__ could not be read", 0) -} -int PyBobLearnEMISVTrainer_set_X(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){ - BOB_TRY - - // Parses input arguments in a single shot - if (!PyList_Check(value)){ - PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __X__.name()); - return -1; - } - - std::vector<blitz::Array<double,2> > data; - if(list_as_vector(value ,data)==0){ - self->cxx->setX(data); - } - - return 0; - BOB_CATCH_MEMBER("__X__ could not be written", -1) -} - - -static auto __Z__ = bob::extension::VariableDoc( - "__Z__", - "list", - "", - "" -); -PyObject* PyBobLearnEMISVTrainer_get_Z(PyBobLearnEMISVTrainerObject* self, void*){ - BOB_TRY - return vector_as_list(self->cxx->getZ()); - BOB_CATCH_MEMBER("__Z__ could not be read", 0) -} -int PyBobLearnEMISVTrainer_set_Z(PyBobLearnEMISVTrainerObject* self, PyObject* value, void*){ - BOB_TRY - - // Parses input arguments in a single shot - if (!PyList_Check(value)){ - PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Z__.name()); - return -1; - } - - std::vector<blitz::Array<double,1> > data; - if(list_as_vector(value ,data)==0){ - self->cxx->setZ(data); - } - - return 0; - BOB_CATCH_MEMBER("__Z__ could not be written", -1) -} - - -static PyGetSetDef PyBobLearnEMISVTrainer_getseters[] = { - { - acc_u_a1.name(), - (getter)PyBobLearnEMISVTrainer_get_acc_u_a1, - (setter)PyBobLearnEMISVTrainer_set_acc_u_a1, - acc_u_a1.doc(), - 0 - }, - { - acc_u_a2.name(), - (getter)PyBobLearnEMISVTrainer_get_acc_u_a2, - (setter)PyBobLearnEMISVTrainer_set_acc_u_a2, - acc_u_a2.doc(), - 0 - }, - { - __X__.name(), - (getter)PyBobLearnEMISVTrainer_get_X, - (setter)PyBobLearnEMISVTrainer_set_X, - __X__.doc(), - 0 - }, - { - __Z__.name(), - (getter)PyBobLearnEMISVTrainer_get_Z, - (setter)PyBobLearnEMISVTrainer_set_Z, - __Z__.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("isv_base, stats, [rng]") -.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMISVTrainer_initialize(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMISVBaseObject* isv_base = 0; - PyObject* stats = 0; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base, - &PyList_Type, &stats, - &PyBoostMt19937_Type, &rng)) return 0; - - if(rng){ - self->cxx->setRng(rng->rng); - } - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->initialize(*isv_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Call the e-step procedure (for the U subspace).", - "", - true -) -.add_prototype("isv_base, stats") -.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMISVTrainer_e_step(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMISVBaseObject* isv_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMISVBase_Type, &isv_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->eStep(*isv_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "Call the m-step procedure (for the U subspace).", - "", - true -) -.add_prototype("isv_base, [stats]") -.add_parameter("isv_base", ":py:class:`bob.learn.em.ISVBase`", "ISVBase Object") -.add_parameter("stats", "object", "Ignored."); -static PyObject* PyBobLearnEMISVTrainer_m_step(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMISVBaseObject* isv_base = 0; - PyObject* stats; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMISVBase_Type, &isv_base, - &stats)) return 0; - - self->cxx->mStep(*isv_base->cxx); - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - - -/*** enroll ***/ -static auto enroll = bob::extension::FunctionDoc( - "enroll", - "", - "", - true -) -.add_prototype("isv_machine, features, n_iter") -.add_parameter("isv_machine", ":py:class:`bob.learn.em.ISVMachine`", "ISVMachine Object") -.add_parameter("features", "list(:py:class:`bob.learn.em.GMMStats`)`", "") -.add_parameter("n_iter", "int", "Number of iterations"); -static PyObject* PyBobLearnEMISVTrainer_enroll(PyBobLearnEMISVTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = enroll.kwlist(0); - - PyBobLearnEMISVMachineObject* isv_machine = 0; - PyObject* stats = 0; - int n_iter = 1; - - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!i", kwlist, &PyBobLearnEMISVMachine_Type, &isv_machine, - &PyList_Type, &stats, &n_iter)) return 0; - - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > training_data; - if(extract_GMMStats_1d(stats ,training_data)==0) - self->cxx->enroll(*isv_machine->cxx, training_data, n_iter); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the enroll method", 0) - - Py_RETURN_NONE; -} - - - -static PyMethodDef PyBobLearnEMISVTrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMISVTrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMISVTrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMISVTrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - enroll.name(), - (PyCFunction)PyBobLearnEMISVTrainer_enroll, - METH_VARARGS|METH_KEYWORDS, - enroll.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMISVTrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMISVTrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMISVTrainer_Type.tp_name = ISVTrainer_doc.name(); - PyBobLearnEMISVTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMISVTrainerObject); - PyBobLearnEMISVTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance; - PyBobLearnEMISVTrainer_Type.tp_doc = ISVTrainer_doc.doc(); - - // set the functions - PyBobLearnEMISVTrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMISVTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMISVTrainer_init); - PyBobLearnEMISVTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMISVTrainer_delete); - PyBobLearnEMISVTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMISVTrainer_RichCompare); - PyBobLearnEMISVTrainer_Type.tp_methods = PyBobLearnEMISVTrainer_methods; - PyBobLearnEMISVTrainer_Type.tp_getset = PyBobLearnEMISVTrainer_getseters; - //PyBobLearnEMISVTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMISVTrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMISVTrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMISVTrainer_Type); - return PyModule_AddObject(module, "ISVTrainer", (PyObject*)&PyBobLearnEMISVTrainer_Type) >= 0; -} diff --git a/bob/learn/em/ivector_machine.cpp b/bob/learn/em/ivector_machine.cpp deleted file mode 100644 index 159bec7415c9e44abe24675a75c8dc7bf28ed192..0000000000000000000000000000000000000000 --- a/bob/learn/em/ivector_machine.cpp +++ /dev/null @@ -1,673 +0,0 @@ -/** - * @date Wed Jan 28 17:46:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto IVectorMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".IVectorMachine", - "Statistical model for the Total Variability training for more information and explanation see the user guide in documentation (:ref:`iVectors <ivector>`)" // this documentation text is intentionally written to be long! - "", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new IVectorMachine", - "", - true - ) - .add_prototype("ubm,rt,variance_threshold","") - .add_prototype("other","") - .add_prototype("hdf5","") - - .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.") - .add_parameter("rt", "int", "Size of the Total Variability matrix (CD x rt).") - .add_parameter("variance_threshold", "float", "Variance flooring threshold for the :math:`\\Sigma` (diagonal) matrix") - - .add_parameter("other", ":py:class:`bob.learn.em.IVectorMachine`", "A IVectorMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMIVectorMachine_init_copy(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = IVectorMachine_doc.kwlist(1); - PyBobLearnEMIVectorMachineObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &o)){ - IVectorMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::IVectorMachine(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMIVectorMachine_init_hdf5(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = IVectorMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - IVectorMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::IVectorMachine(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMIVectorMachine_init_ubm(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = IVectorMachine_doc.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - int rt = 1; - double variance_threshold = 1e-10; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|d", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &rt, &variance_threshold)){ - IVectorMachine_doc.print_usage(); - return -1; - } - - if(rt < 1){ - PyErr_Format(PyExc_TypeError, "rt argument must be greater than or equal to one"); - return -1; - } - - if(variance_threshold <= 0){ - PyErr_Format(PyExc_TypeError, "variance_threshold argument must be greater than zero"); - return -1; - } - - self->cxx.reset(new bob::learn::em::IVectorMachine(gmm_machine->cxx, rt, variance_threshold)); - return 0; -} - - -static int PyBobLearnEMIVectorMachine_init(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs == 1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMIVectorMachine_Check(arg)) - return PyBobLearnEMIVectorMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else - return PyBobLearnEMIVectorMachine_init_hdf5(self, args, kwargs); - } - else if ((nargs == 2) || (nargs == 3)) - PyBobLearnEMIVectorMachine_init_ubm(self, args, kwargs); - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1,2 or 3 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - IVectorMachine_doc.print_usage(); - return -1; - } - - BOB_CATCH_MEMBER("cannot create IVectorMachine", -1) - return 0; -} - -static void PyBobLearnEMIVectorMachine_delete(PyBobLearnEMIVectorMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMIVectorMachine_RichCompare(PyBobLearnEMIVectorMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMIVectorMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMIVectorMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare IVectorMachine objects", 0) -} - -int PyBobLearnEMIVectorMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMIVectorMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int)", - "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rT (total variability matrix) ``(#Gaussians, #Inputs, #rT)``.", - "" -); -PyObject* PyBobLearnEMIVectorMachine_getShape(PyBobLearnEMIVectorMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRt()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** supervector_length *****/ -static auto supervector_length = bob::extension::VariableDoc( - "supervector_length", - "int", - - "Returns the supervector length.", - "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality" - "An exception is thrown if no Universal Background Model has been set yet." - "" -); -PyObject* PyBobLearnEMIVectorMachine_getSupervectorLength(PyBobLearnEMIVectorMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("i", self->cxx->getSupervectorLength()); - BOB_CATCH_MEMBER("supervector_length could not be read", 0) -} - - -/***** T *****/ -static auto T = bob::extension::VariableDoc( - "t", - "array_like <float, 2D>", - "Returns the Total Variability matrix, :math:`T`", - "" -); -PyObject* PyBobLearnEMIVectorMachine_getT(PyBobLearnEMIVectorMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getT()); - BOB_CATCH_MEMBER("`t` could not be read", 0) -} -int PyBobLearnEMIVectorMachine_setT(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, T.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "t"); - if (!b) return -1; - self->cxx->setT(*b); - return 0; - BOB_CATCH_MEMBER("`t` vector could not be set", -1) -} - - -/***** sigma *****/ -static auto sigma = bob::extension::VariableDoc( - "sigma", - "array_like <float, 1D>", - "The residual matrix of the model sigma", - "" -); -PyObject* PyBobLearnEMIVectorMachine_getSigma(PyBobLearnEMIVectorMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getSigma()); - BOB_CATCH_MEMBER("`sigma` could not be read", 0) -} -int PyBobLearnEMIVectorMachine_setSigma(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, sigma.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "sigma"); - if (!b) return -1; - self->cxx->setSigma(*b); - return 0; - BOB_CATCH_MEMBER("`sigma` vector could not be set", -1) -} - - -/***** variance_threshold *****/ -static auto variance_threshold = bob::extension::VariableDoc( - "variance_threshold", - "float", - "Threshold for the variance contained in sigma", - "" -); -PyObject* PyBobLearnEMIVectorMachine_getVarianceThreshold(PyBobLearnEMIVectorMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("d", self->cxx->getVarianceThreshold()); - BOB_CATCH_MEMBER("variance_threshold could not be read", 0) -} -int PyBobLearnEMIVectorMachine_setVarianceThreshold(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, variance_threshold.name()); - return -1; - } - - if (PyFloat_AS_DOUBLE(value) < 0){ - PyErr_Format(PyExc_TypeError, "variance_threshold must be greater than or equal to zero"); - return -1; - } - - self->cxx->setVarianceThreshold(PyFloat_AS_DOUBLE(value)); - BOB_CATCH_MEMBER("variance_threshold could not be set", -1) - return 0; -} - - -/***** ubm *****/ -static auto ubm = bob::extension::VariableDoc( - "ubm", - ":py:class:`bob.learn.em.GMMMachine`", - "Returns the UBM (Universal Background Model)", - "" -); -PyObject* PyBobLearnEMIVectorMachine_getUBM(PyBobLearnEMIVectorMachineObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm(); - - //Allocating the correspondent python object - PyBobLearnEMGMMMachineObject* retval = - (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0); - retval->cxx = ubm_gmmMachine; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("ubm could not be read", 0) -} -int PyBobLearnEMIVectorMachine_setUBM(PyBobLearnEMIVectorMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMGMMMachine_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name()); - return -1; - } - - PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine); - - self->cxx->setUbm(ubm_gmmMachine->cxx); - - return 0; - BOB_CATCH_MEMBER("ubm could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMIVectorMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMIVectorMachine_getShape, - 0, - shape.doc(), - 0 - }, - - { - supervector_length.name(), - (getter)PyBobLearnEMIVectorMachine_getSupervectorLength, - 0, - supervector_length.doc(), - 0 - }, - - { - T.name(), - (getter)PyBobLearnEMIVectorMachine_getT, - (setter)PyBobLearnEMIVectorMachine_setT, - T.doc(), - 0 - }, - - { - variance_threshold.name(), - (getter)PyBobLearnEMIVectorMachine_getVarianceThreshold, - (setter)PyBobLearnEMIVectorMachine_setVarianceThreshold, - variance_threshold.doc(), - 0 - }, - - { - sigma.name(), - (getter)PyBobLearnEMIVectorMachine_getSigma, - (setter)PyBobLearnEMIVectorMachine_setSigma, - sigma.doc(), - 0 - }, - - { - ubm.name(), - (getter)PyBobLearnEMIVectorMachine_getUBM, - (setter)PyBobLearnEMIVectorMachine_setUBM, - ubm.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the IVectorMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMIVectorMachine_Save(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the IVectorMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMIVectorMachine_Load(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this IVectorMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.IVectorMachine`", "A IVectorMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMIVectorMachine_IsSimilarTo(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMIVectorMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMIVectorMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - - -/*** project ***/ -static auto project = bob::extension::FunctionDoc( - "project", - "Projects the given GMM statistics into the i-vector subspace", - ".. note:: The ``__call__`` function is an alias for this function", - true -) -.add_prototype("stats") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input"); -static PyObject* PyBobLearnEMIVectorMachine_project(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = project.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - blitz::Array<double,1> ivector(self->cxx->getDimRt()); - self->cxx->forward(*stats->cxx, ivector); - - return PyBlitzArrayCxx_AsConstNumpy(ivector); - - BOB_CATCH_MEMBER("cannot project", 0) - -} - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Resets the dimensionality of the subspace T. ", - 0, - true -) -.add_prototype("rT") -.add_parameter("rT", "int", "Size of T (Total variability matrix)"); -static PyObject* PyBobLearnEMIVectorMachine_resize(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int rT = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &rT)) return 0; - - if (rT < 1){ - PyErr_Format(PyExc_TypeError, "rU must be greater than one"); - resize.print_usage(); - return 0; - } - - self->cxx->resize(rT); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - -/*** __compute_Id_TtSigmaInvT__ ***/ -static auto __compute_Id_TtSigmaInvT__ = bob::extension::FunctionDoc( - "__compute_Id_TtSigmaInvT__", - "", - "", - true -) -.add_prototype("stats") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input"); -static PyObject* PyBobLearnEMIVectorMachine_compute_Id_TtSigmaInvT__(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = __compute_Id_TtSigmaInvT__.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - - blitz::Array<double,2> output(self->cxx->getDimRt(), self->cxx->getDimRt()); - self->cxx->computeIdTtSigmaInvT(*stats->cxx, output); - return PyBlitzArrayCxx_AsConstNumpy(output); - - BOB_CATCH_MEMBER("cannot __compute_Id_TtSigmaInvT__", 0) -} - - - -/*** __compute_TtSigmaInvFnorm__ ***/ -static auto __compute_TtSigmaInvFnorm__ = bob::extension::FunctionDoc( - "__compute_TtSigmaInvFnorm__", - "", - "", - true -) -.add_prototype("stats") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input"); -static PyObject* PyBobLearnEMIVectorMachine_compute_TtSigmaInvFnorm__(PyBobLearnEMIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = __compute_TtSigmaInvFnorm__.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - - blitz::Array<double,1> output(self->cxx->getDimRt()); - self->cxx->computeTtSigmaInvFnorm(*stats->cxx, output); - return PyBlitzArrayCxx_AsConstNumpy(output); - - BOB_CATCH_MEMBER("cannot __compute_TtSigmaInvFnorm__", 0) -} - - - - -static PyMethodDef PyBobLearnEMIVectorMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - project.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_project, - METH_VARARGS|METH_KEYWORDS, - project.doc() - }, - { - __compute_Id_TtSigmaInvT__.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_compute_Id_TtSigmaInvT__, - METH_VARARGS|METH_KEYWORDS, - __compute_Id_TtSigmaInvT__.doc() - }, - { - __compute_TtSigmaInvFnorm__.name(), - (PyCFunction)PyBobLearnEMIVectorMachine_compute_TtSigmaInvFnorm__, - METH_VARARGS|METH_KEYWORDS, - __compute_TtSigmaInvFnorm__.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMIVectorMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMIVectorMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMIVectorMachine_Type.tp_name = IVectorMachine_doc.name(); - PyBobLearnEMIVectorMachine_Type.tp_basicsize = sizeof(PyBobLearnEMIVectorMachineObject); - PyBobLearnEMIVectorMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMIVectorMachine_Type.tp_doc = IVectorMachine_doc.doc(); - - // set the functions - PyBobLearnEMIVectorMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMIVectorMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMIVectorMachine_init); - PyBobLearnEMIVectorMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMIVectorMachine_delete); - PyBobLearnEMIVectorMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMIVectorMachine_RichCompare); - PyBobLearnEMIVectorMachine_Type.tp_methods = PyBobLearnEMIVectorMachine_methods; - PyBobLearnEMIVectorMachine_Type.tp_getset = PyBobLearnEMIVectorMachine_getseters; - PyBobLearnEMIVectorMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMIVectorMachine_project); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMIVectorMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMIVectorMachine_Type); - return PyModule_AddObject(module, "IVectorMachine", (PyObject*)&PyBobLearnEMIVectorMachine_Type) >= 0; -} diff --git a/bob/learn/em/ivector_trainer.cpp b/bob/learn/em/ivector_trainer.cpp deleted file mode 100644 index 96f2c3af971959710c26fea302f9556321433e14..0000000000000000000000000000000000000000 --- a/bob/learn/em/ivector_trainer.cpp +++ /dev/null @@ -1,496 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Tue 03 Fev 10:29:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" -#include <boost/make_shared.hpp> - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} /* converts PyObject to bool and returns false if object is NULL */ - -static int extract_GMMStats_1d(PyObject *list, - std::vector<bob::learn::em::GMMStats>& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - training_data.push_back(*stats->cxx); - - } - return 0; -} - - -static auto IVectorTrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".IVectorTrainer", - "IVectorTrainer" - "Trains the Total Variability subspace :math:`$T$` to generate :ref:`iVectors <ivector>`." - "", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new IVectorTrainer", - "", - true - ) - .add_prototype("update_sigma","") - .add_prototype("other","") - .add_prototype("","") - .add_parameter("other", ":py:class:`bob.learn.em.IVectorTrainer`", "A IVectorTrainer object to be copied.") - .add_parameter("update_sigma", "bool", "") -); - - -static int PyBobLearnEMIVectorTrainer_init_copy(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = IVectorTrainer_doc.kwlist(1); - PyBobLearnEMIVectorTrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorTrainer_Type, &o)){ - IVectorTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::IVectorTrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMIVectorTrainer_init_bool(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = IVectorTrainer_doc.kwlist(0); - PyObject* update_sigma = 0; - - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBool_Type, &update_sigma)) - return -1; - - self->cxx.reset(new bob::learn::em::IVectorTrainer(f(update_sigma))); - return 0; -} - - -static int PyBobLearnEMIVectorTrainer_init(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch(nargs){ - case 0:{ - self->cxx.reset(new bob::learn::em::IVectorTrainer()); - return 0; - } - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is IVectorTrainer object - if(PyBobLearnEMIVectorTrainer_Check(arg)) - return PyBobLearnEMIVectorTrainer_init_copy(self, args, kwargs); - else - return PyBobLearnEMIVectorTrainer_init_bool(self, args, kwargs); - - } - default:{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - IVectorTrainer_doc.print_usage(); - return -1; - } - } - BOB_CATCH_MEMBER("cannot create IVectorTrainer", -1) - return 0; -} - - -static void PyBobLearnEMIVectorTrainer_delete(PyBobLearnEMIVectorTrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMIVectorTrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMIVectorTrainer_Type)); -} - - -static PyObject* PyBobLearnEMIVectorTrainer_RichCompare(PyBobLearnEMIVectorTrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMIVectorTrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMIVectorTrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare IVectorTrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -static auto acc_nij_wij2 = bob::extension::VariableDoc( - "acc_nij_wij2", - "array_like <float, 3D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMIVectorTrainer_get_acc_nij_wij2(PyBobLearnEMIVectorTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccNijWij2()); - BOB_CATCH_MEMBER("acc_nij_wij2 could not be read", 0) -} -int PyBobLearnEMIVectorTrainer_set_acc_nij_wij2(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_nij_wij2.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,3>(o, "acc_nij_wij2"); - if (!b) return -1; - self->cxx->setAccNijWij2(*b); - return 0; - BOB_CATCH_MEMBER("acc_nij_wij2 could not be set", -1) -} - - -static auto acc_fnormij_wij = bob::extension::VariableDoc( - "acc_fnormij_wij", - "array_like <float, 3D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMIVectorTrainer_get_acc_fnormij_wij(PyBobLearnEMIVectorTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccFnormijWij()); - BOB_CATCH_MEMBER("acc_fnormij_wij could not be read", 0) -} -int PyBobLearnEMIVectorTrainer_set_acc_fnormij_wij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_fnormij_wij.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,3>(o, "acc_fnormij_wij"); - if (!b) return -1; - self->cxx->setAccFnormijWij(*b); - return 0; - BOB_CATCH_MEMBER("acc_fnormij_wij could not be set", -1) -} - - -static auto acc_nij = bob::extension::VariableDoc( - "acc_nij", - "array_like <float, 1D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMIVectorTrainer_get_acc_nij(PyBobLearnEMIVectorTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccNij()); - BOB_CATCH_MEMBER("acc_nij could not be read", 0) -} -int PyBobLearnEMIVectorTrainer_set_acc_nij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_nij.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "acc_nij"); - if (!b) return -1; - self->cxx->setAccNij(*b); - return 0; - BOB_CATCH_MEMBER("acc_nij could not be set", -1) -} - - -static auto acc_snormij = bob::extension::VariableDoc( - "acc_snormij", - "array_like <float, 2D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMIVectorTrainer_get_acc_snormij(PyBobLearnEMIVectorTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccSnormij()); - BOB_CATCH_MEMBER("acc_snormij could not be read", 0) -} -int PyBobLearnEMIVectorTrainer_set_acc_snormij(PyBobLearnEMIVectorTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_snormij.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "acc_snormij"); - if (!b) return -1; - self->cxx->setAccSnormij(*b); - return 0; - BOB_CATCH_MEMBER("acc_snormij could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMIVectorTrainer_getseters[] = { - { - acc_nij_wij2.name(), - (getter)PyBobLearnEMIVectorTrainer_get_acc_nij_wij2, - (setter)PyBobLearnEMIVectorTrainer_set_acc_nij_wij2, - acc_nij_wij2.doc(), - 0 - }, - { - acc_fnormij_wij.name(), - (getter)PyBobLearnEMIVectorTrainer_get_acc_fnormij_wij, - (setter)PyBobLearnEMIVectorTrainer_set_acc_fnormij_wij, - acc_fnormij_wij.doc(), - 0 - }, - { - acc_nij.name(), - (getter)PyBobLearnEMIVectorTrainer_get_acc_nij, - (setter)PyBobLearnEMIVectorTrainer_set_acc_nij, - acc_nij.doc(), - 0 - }, - { - acc_snormij.name(), - (getter)PyBobLearnEMIVectorTrainer_get_acc_snormij, - (setter)PyBobLearnEMIVectorTrainer_set_acc_snormij, - acc_snormij.doc(), - 0 - }, - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("ivector_machine, [stats], [rng]") -.add_parameter("ivector_machine", ":py:class:`bob.learn.em.IVectorMachine`", "IVectorMachine Object") -.add_parameter("stats", "object", "Ignored") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMIVectorTrainer_initialize(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMIVectorMachineObject* ivector_machine = 0; - PyObject* stats; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO!", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine, - &stats, - &PyBoostMt19937_Type, &rng)) return 0; - - if(rng){ - self->cxx->setRng(rng->rng); - } - - self->cxx->initialize(*ivector_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Call the e-step procedure (for the U subspace).", - "", - true -) -.add_prototype("ivector_machine,stats") -.add_parameter("ivector_machine", ":py:class:`bob.learn.em.ISVBase`", "IVectorMachine Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMIVectorTrainer_e_step(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMIVectorMachineObject* ivector_machine = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine, - &PyList_Type, &stats)) return 0; - - std::vector<bob::learn::em::GMMStats> training_data; - if(extract_GMMStats_1d(stats ,training_data)==0) - self->cxx->eStep(*ivector_machine->cxx, training_data); - else - return 0; - - Py_RETURN_NONE; - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "Call the m-step procedure (for the U subspace).", - "", - true -) -.add_prototype("ivector_machine, [stats]") -.add_parameter("ivector_machine", ":py:class:`bob.learn.em.ISVBase`", "IVectorMachine Object") -.add_parameter("stats", "object", "Ignored"); -static PyObject* PyBobLearnEMIVectorTrainer_m_step(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMIVectorMachineObject* ivector_machine = 0; - PyObject* stats; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMIVectorMachine_Type, &ivector_machine, - &stats)) return 0; - - self->cxx->mStep(*ivector_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - -/*** reset_accumulators ***/ -static auto reset_accumulators = bob::extension::FunctionDoc( - "reset_accumulators", - "Reset the statistics accumulators to the correct size and a value of zero.", - 0, - true -) -.add_prototype("ivector_machine") -.add_parameter("ivector_machine", ":py:class:`bob.learn.em.IVectorMachine`", "The IVector machine containing the right dimensions"); -static PyObject* PyBobLearnEMIVectorTrainer_reset_accumulators(PyBobLearnEMIVectorTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = reset_accumulators.kwlist(0); - - PyBobLearnEMIVectorMachineObject* machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMIVectorMachine_Type, &machine)) return 0; - - self->cxx->resetAccumulators(*machine->cxx); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("cannot perform the reset_accumulators method", 0) -} - - -static PyMethodDef PyBobLearnEMIVectorTrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMIVectorTrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMIVectorTrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMIVectorTrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - reset_accumulators.name(), - (PyCFunction)PyBobLearnEMIVectorTrainer_reset_accumulators, - METH_VARARGS|METH_KEYWORDS, - reset_accumulators.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMIVectorTrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMIVectorTrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMIVectorTrainer_Type.tp_name = IVectorTrainer_doc.name(); - PyBobLearnEMIVectorTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMIVectorTrainerObject); - PyBobLearnEMIVectorTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance; - PyBobLearnEMIVectorTrainer_Type.tp_doc = IVectorTrainer_doc.doc(); - - // set the functions - PyBobLearnEMIVectorTrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMIVectorTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMIVectorTrainer_init); - PyBobLearnEMIVectorTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMIVectorTrainer_delete); - PyBobLearnEMIVectorTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMIVectorTrainer_RichCompare); - PyBobLearnEMIVectorTrainer_Type.tp_methods = PyBobLearnEMIVectorTrainer_methods; - PyBobLearnEMIVectorTrainer_Type.tp_getset = PyBobLearnEMIVectorTrainer_getseters; - //PyBobLearnEMIVectorTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMIVectorTrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMIVectorTrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMIVectorTrainer_Type); - return PyModule_AddObject(module, "IVectorTrainer", (PyObject*)&PyBobLearnEMIVectorTrainer_Type) >= 0; -} diff --git a/bob/learn/em/jfa_base.cpp b/bob/learn/em/jfa_base.cpp deleted file mode 100644 index aeec4a0367f758e18ba109bd13473afc25cea9cf..0000000000000000000000000000000000000000 --- a/bob/learn/em/jfa_base.cpp +++ /dev/null @@ -1,630 +0,0 @@ -/** - * @date Wed Jan 27 17:03:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto JFABase_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".JFABase", - "Container for :math:`U`, :math:`V` and :math:`D` when performing Joint Factor Analysis (:ref:`JFA <jfa>`).\n\n" - "", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new JFABase", - "", - true - ) - .add_prototype("ubm,ru,rv","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "The Universal Background Model.") - .add_parameter("ru", "int", "Size of :math:`U` (Within client variation matrix). In the end the U matrix will have (#gaussians * #feature_dimension x ru)") - .add_parameter("rv", "int", "Size of :math:`V` (Between client variation matrix). In the end the U matrix will have (#gaussians * #feature_dimension x rv)") - .add_parameter("other", ":py:class:`bob.learn.em.JFABase`", "A JFABase object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMJFABase_init_copy(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFABase_doc.kwlist(1); - PyBobLearnEMJFABaseObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFABase_Type, &o)){ - JFABase_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::JFABase(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMJFABase_init_hdf5(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFABase_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - JFABase_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::JFABase(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMJFABase_init_ubm(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFABase_doc.kwlist(0); - - PyBobLearnEMGMMMachineObject* ubm; - int ru = 1; - int rv = 1; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!ii", kwlist, &PyBobLearnEMGMMMachine_Type, &ubm, - &ru, &rv)){ - JFABase_doc.print_usage(); - return -1; - } - - if(ru < 0){ - PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one"); - return -1; - } - - if(rv < 0){ - PyErr_Format(PyExc_TypeError, "rv argument must be greater than or equal to one"); - return -1; - } - - self->cxx.reset(new bob::learn::em::JFABase(ubm->cxx, ru, rv)); - return 0; -} - - -static int PyBobLearnEMJFABase_init(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMJFABase_Check(arg)) - return PyBobLearnEMJFABase_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMJFABase_init_hdf5(self, args, kwargs); - } - case 3: - return PyBobLearnEMJFABase_init_ubm(self, args, kwargs); - default: - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 3 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - JFABase_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create JFABase", -1) - return 0; -} - - - -static void PyBobLearnEMJFABase_delete(PyBobLearnEMJFABaseObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMJFABase_RichCompare(PyBobLearnEMJFABaseObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMJFABase_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMJFABaseObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare JFABase objects", 0) -} - -int PyBobLearnEMJFABase_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFABase_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int, int)", - "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the :math:`rU` (within client variability matrix) and dimensionality of the :math:`rV` (between client variability matrix) ``(#Gaussians, #Inputs, #rU, #rV)``.", - "" -); -PyObject* PyBobLearnEMJFABase_getShape(PyBobLearnEMJFABaseObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu(), self->cxx->getDimRv()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** supervector_length *****/ -static auto supervector_length = bob::extension::VariableDoc( - "supervector_length", - "int", - - "Returns the supervector length.", - "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality" - "An exception is thrown if no Universal Background Model has been set yet." - "" -); -PyObject* PyBobLearnEMJFABase_getSupervectorLength(PyBobLearnEMJFABaseObject* self, void*) { - BOB_TRY - return Py_BuildValue("i", self->cxx->getSupervectorLength()); - BOB_CATCH_MEMBER("supervector_length could not be read", 0) -} - - -/***** u *****/ -static auto U = bob::extension::VariableDoc( - "u", - "array_like <float, 2D>", - "Returns the :math:`U` matrix (within client variability matrix)", - "" -); -PyObject* PyBobLearnEMJFABase_getU(PyBobLearnEMJFABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getU()); - BOB_CATCH_MEMBER("``u`` could not be read", 0) -} -int PyBobLearnEMJFABase_setU(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, U.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getU().extent(0) && input->shape[1] != self->cxx->getU().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getU().extent(0), (Py_ssize_t)self->cxx->getU().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], U.name()); - return -1; - } - - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "u"); - if (!b) return -1; - self->cxx->setU(*b); - return 0; - BOB_CATCH_MEMBER("``u`` matrix could not be set", -1) -} - -/***** v *****/ -static auto V = bob::extension::VariableDoc( - "v", - "array_like <float, 2D>", - "Returns the :math:`V` matrix (between client variability matrix)", - "" -); -PyObject* PyBobLearnEMJFABase_getV(PyBobLearnEMJFABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getV()); - BOB_CATCH_MEMBER("``v`` could not be read", 0) -} -int PyBobLearnEMJFABase_setV(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, V.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, V.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, V.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getV().extent(0) && input->shape[1] != self->cxx->getV().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getV().extent(0), (Py_ssize_t)self->cxx->getV().extent(1), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], V.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "v"); - if (!b) return -1; - self->cxx->setV(*b); - return 0; - BOB_CATCH_MEMBER("``v`` matrix could not be set", -1) -} - - -/***** d *****/ -static auto D = bob::extension::VariableDoc( - "d", - "array_like <float, 1D>", - "Returns the diagonal matrix :math:`diag(d)` (as a 1D vector)", - "" -); -PyObject* PyBobLearnEMJFABase_getD(PyBobLearnEMJFABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getD()); - BOB_CATCH_MEMBER("``d`` could not be read", 0) -} -int PyBobLearnEMJFABase_setD(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, D.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getD().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not [%" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getD().extent(0), (Py_ssize_t)input->shape[0], D.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "d"); - if (!b) return -1; - self->cxx->setD(*b); - return 0; - BOB_CATCH_MEMBER("``d`` matrix could not be set", -1) -} - - -/***** ubm *****/ -static auto ubm = bob::extension::VariableDoc( - "ubm", - ":py:class:`bob.learn.em.GMMMachine`", - "Returns the UBM (Universal Background Model", - "" -); -PyObject* PyBobLearnEMJFABase_getUBM(PyBobLearnEMJFABaseObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::GMMMachine> ubm_gmmMachine = self->cxx->getUbm(); - - //Allocating the correspondent python object - PyBobLearnEMGMMMachineObject* retval = - (PyBobLearnEMGMMMachineObject*)PyBobLearnEMGMMMachine_Type.tp_alloc(&PyBobLearnEMGMMMachine_Type, 0); - retval->cxx = ubm_gmmMachine; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("ubm could not be read", 0) -} -int PyBobLearnEMJFABase_setUBM(PyBobLearnEMJFABaseObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMGMMMachine_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.GMMMachine`", Py_TYPE(self)->tp_name, ubm.name()); - return -1; - } - - PyBobLearnEMGMMMachineObject* ubm_gmmMachine = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMGMMMachine_Type,&ubm_gmmMachine); - - self->cxx->setUbm(ubm_gmmMachine->cxx); - - return 0; - BOB_CATCH_MEMBER("ubm could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMJFABase_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMJFABase_getShape, - 0, - shape.doc(), - 0 - }, - - { - supervector_length.name(), - (getter)PyBobLearnEMJFABase_getSupervectorLength, - 0, - supervector_length.doc(), - 0 - }, - - { - U.name(), - (getter)PyBobLearnEMJFABase_getU, - (setter)PyBobLearnEMJFABase_setU, - U.doc(), - 0 - }, - - { - V.name(), - (getter)PyBobLearnEMJFABase_getV, - (setter)PyBobLearnEMJFABase_setV, - V.doc(), - 0 - }, - - { - D.name(), - (getter)PyBobLearnEMJFABase_getD, - (setter)PyBobLearnEMJFABase_setD, - D.doc(), - 0 - }, - - { - ubm.name(), - (getter)PyBobLearnEMJFABase_getUBM, - (setter)PyBobLearnEMJFABase_setUBM, - ubm.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the JFABase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMJFABase_Save(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the JFABase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMJFABase_Load(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this JFABase with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.JFABase`", "A JFABase object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMJFABase_IsSimilarTo(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMJFABaseObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMJFABase_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Resets the dimensionality of the subspace U and V. " - "U and V are hence uninitialized", - 0, - true -) -.add_prototype("rU,rV") -.add_parameter("rU", "int", "Size of :math:`U` (Within client variation matrix)") -.add_parameter("rV", "int", "Size of :math:`V` (Between client variation matrix)"); -static PyObject* PyBobLearnEMJFABase_resize(PyBobLearnEMJFABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int rU = 0; - int rV = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &rU, &rV)) return 0; - - if (rU <= 0){ - PyErr_Format(PyExc_TypeError, "rU must be greater than zero"); - resize.print_usage(); - return 0; - } - if (rV <= 0){ - PyErr_Format(PyExc_TypeError, "rV must be greater than zero"); - resize.print_usage(); - return 0; - } - self->cxx->resize(rU, rV); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - - - -static PyMethodDef PyBobLearnEMJFABase_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMJFABase_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMJFABase_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMJFABase_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMJFABase_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMJFABase_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMJFABase(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMJFABase_Type.tp_name = JFABase_doc.name(); - PyBobLearnEMJFABase_Type.tp_basicsize = sizeof(PyBobLearnEMJFABaseObject); - PyBobLearnEMJFABase_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyBobLearnEMJFABase_Type.tp_doc = JFABase_doc.doc(); - - // set the functions - PyBobLearnEMJFABase_Type.tp_new = PyType_GenericNew; - PyBobLearnEMJFABase_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMJFABase_init); - PyBobLearnEMJFABase_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMJFABase_delete); - PyBobLearnEMJFABase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFABase_RichCompare); - PyBobLearnEMJFABase_Type.tp_methods = PyBobLearnEMJFABase_methods; - PyBobLearnEMJFABase_Type.tp_getset = PyBobLearnEMJFABase_getseters; - //PyBobLearnEMJFABase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFABase_forward); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMJFABase_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMJFABase_Type); - return PyModule_AddObject(module, "JFABase", (PyObject*)&PyBobLearnEMJFABase_Type) >= 0; -} - diff --git a/bob/learn/em/jfa_machine.cpp b/bob/learn/em/jfa_machine.cpp deleted file mode 100644 index 95ceb9766959bbd31d55e269f31bc9c2c4b1dd93..0000000000000000000000000000000000000000 --- a/bob/learn/em/jfa_machine.cpp +++ /dev/null @@ -1,734 +0,0 @@ -/** - * @date Wed Jan 28 17:03:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto JFAMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".JFAMachine", - "A JFAMachine. An attached :py:class:`bob.learn.em.JFABase` should be provided for Joint Factor Analysis. The :py:class:`bob.learn.em.JFAMachine` carries information about the speaker factors :math:`y` and :math:`z`, whereas a :py:class:`bob.learn.em.JFABase` carries information about the matrices :math:`U`, :math:`V` and :math:`D`.\n\n" - "References: [Vogt2008]_ [McCool2013]_", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new JFAMachine", - "", - true - ) - .add_prototype("jfa_base","") - .add_prototype("other","") - .add_prototype("hdf5","") - - .add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "The JFABase associated with this machine") - .add_parameter("other", ":py:class:`bob.learn.em.JFAMachine`", "A JFAMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMJFAMachine_init_copy(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFAMachine_doc.kwlist(1); - PyBobLearnEMJFAMachineObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFAMachine_Type, &o)){ - JFAMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::JFAMachine(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMJFAMachine_init_hdf5(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFAMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - JFAMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::JFAMachine(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMJFAMachine_init_jfabase(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFAMachine_doc.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base)){ - JFAMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::JFAMachine(jfa_base->cxx)); - return 0; -} - - -static int PyBobLearnEMJFAMachine_init(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs == 1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMJFAMachine_Check(arg)) - return PyBobLearnEMJFAMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMJFAMachine_init_hdf5(self, args, kwargs); - // If the constructor input is a JFABase Object - else - return PyBobLearnEMJFAMachine_init_jfabase(self, args, kwargs); - } - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - JFAMachine_doc.print_usage(); - return -1; - } - - BOB_CATCH_MEMBER("cannot create JFAMachine", -1) - return 0; -} - -static void PyBobLearnEMJFAMachine_delete(PyBobLearnEMJFAMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMJFAMachine_RichCompare(PyBobLearnEMJFAMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMJFAMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMJFAMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare JFAMachine objects", 0) -} - -int PyBobLearnEMJFAMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFAMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int, int)", - "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rU (within client variability matrix) and dimensionality of the rV (between client variability matrix) ``(#Gaussians, #Inputs, #rU, #rV)``.", - "" -); -PyObject* PyBobLearnEMJFAMachine_getShape(PyBobLearnEMJFAMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRu(), self->cxx->getDimRv()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** supervector_length *****/ -static auto supervector_length = bob::extension::VariableDoc( - "supervector_length", - "int", - - "Returns the supervector length.", - "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality. " - "An exception is thrown if no Universal Background Model has been set yet." - "" -); -PyObject* PyBobLearnEMJFAMachine_getSupervectorLength(PyBobLearnEMJFAMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("i", self->cxx->getSupervectorLength()); - BOB_CATCH_MEMBER("supervector_length could not be read", 0) -} - - -/***** y *****/ -static auto Y = bob::extension::VariableDoc( - "y", - "array_like <float, 1D>", - "Returns the :math:`y` speaker factor. Eq (30) from [McCool2013]_", - "" -); -PyObject* PyBobLearnEMJFAMachine_getY(PyBobLearnEMJFAMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getY()); - BOB_CATCH_MEMBER("`y` could not be read", 0) -} -int PyBobLearnEMJFAMachine_setY(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Y.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Y.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Y.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getY().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getY().extent(0), (Py_ssize_t)input->shape[0], Y.name()); - return -1; - } - - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "y"); - if (!b) return -1; - self->cxx->setY(*b); - return 0; - BOB_CATCH_MEMBER("`y` vector could not be set", -1) -} - - -/***** z *****/ -static auto Z = bob::extension::VariableDoc( - "z", - "array_like <float, 1D>", - "Returns the :math:`z` speaker factor. Eq (31) from [McCool2013]_", - "" -); -PyObject* PyBobLearnEMJFAMachine_getZ(PyBobLearnEMJFAMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZ()); - BOB_CATCH_MEMBER("`z` could not be read", 0) -} -int PyBobLearnEMJFAMachine_setZ(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, Z.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getZ().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getZ().extent(0), (Py_ssize_t)input->shape[0], Z.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "z"); - if (!b) return -1; - self->cxx->setZ(*b); - return 0; - BOB_CATCH_MEMBER("`z` vector could not be set", -1) -} - - -/***** x *****/ -static auto X = bob::extension::VariableDoc( - "x", - "array_like <float, 1D>", - "Returns the :math:`X` session factor. Eq (29) from [McCool2013]_", - "The latent variable :math:`x` (last one computed). This is a feature provided for convenience, but this attribute is not 'part' of the machine. The session latent variable :math:`x` is indeed not class-specific, but depends on the sample considered. Furthermore, it is not saved into the machine or used when comparing machines." -); -PyObject* PyBobLearnEMJFAMachine_getX(PyBobLearnEMJFAMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getX()); - BOB_CATCH_MEMBER("`x` could not be read", 0) -} - - -/***** jfa_base *****/ -static auto jfa_base = bob::extension::VariableDoc( - "jfa_base", - ":py:class:`bob.learn.em.JFABase`", - "The JFABase attached to this machine", - "" -); -PyObject* PyBobLearnEMJFAMachine_getJFABase(PyBobLearnEMJFAMachineObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::JFABase> jfa_base_o = self->cxx->getJFABase(); - - //Allocating the correspondent python object - PyBobLearnEMJFABaseObject* retval = - (PyBobLearnEMJFABaseObject*)PyBobLearnEMJFABase_Type.tp_alloc(&PyBobLearnEMJFABase_Type, 0); - retval->cxx = jfa_base_o; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("jfa_base could not be read", 0) -} -int PyBobLearnEMJFAMachine_setJFABase(PyBobLearnEMJFAMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMJFABase_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.JFABase`", Py_TYPE(self)->tp_name, jfa_base.name()); - return -1; - } - - PyBobLearnEMJFABaseObject* jfa_base_o = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMJFABase_Type,&jfa_base_o); - - self->cxx->setJFABase(jfa_base_o->cxx); - - return 0; - BOB_CATCH_MEMBER("jfa_base could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMJFAMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMJFAMachine_getShape, - 0, - shape.doc(), - 0 - }, - - { - supervector_length.name(), - (getter)PyBobLearnEMJFAMachine_getSupervectorLength, - 0, - supervector_length.doc(), - 0 - }, - - { - jfa_base.name(), - (getter)PyBobLearnEMJFAMachine_getJFABase, - (setter)PyBobLearnEMJFAMachine_setJFABase, - jfa_base.doc(), - 0 - }, - - { - Y.name(), - (getter)PyBobLearnEMJFAMachine_getY, - (setter)PyBobLearnEMJFAMachine_setY, - Y.doc(), - 0 - }, - - { - Z.name(), - (getter)PyBobLearnEMJFAMachine_getZ, - (setter)PyBobLearnEMJFAMachine_setZ, - Z.doc(), - 0 - }, - - { - X.name(), - (getter)PyBobLearnEMJFAMachine_getX, - 0, - X.doc(), - 0 - }, - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the JFAMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMJFAMachine_Save(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the JFAMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMJFAMachine_Load(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this JFAMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.JFAMachine`", "A JFAMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMJFAMachine_IsSimilarTo(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMJFAMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMJFAMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** estimate_x ***/ -static auto estimate_x = bob::extension::FunctionDoc( - "estimate_x", - "Estimates the session offset x (LPT assumption) given GMM statistics.", - "Estimates x from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM", - true -) -.add_prototype("stats,input") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM") -.add_parameter("input", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMJFAMachine_estimateX(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = estimate_x.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&input)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_x.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_x.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), (Py_ssize_t)input->shape[0], estimate_x.name()); - return 0; - } - - self->cxx->estimateX(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input)); - - BOB_CATCH_MEMBER("cannot estimate X", 0) - Py_RETURN_NONE; -} - - -/*** estimate_ux ***/ -static auto estimate_ux = bob::extension::FunctionDoc( - "estimate_ux", - "Estimates Ux (LPT assumption) given GMM statistics.", - "Estimates Ux from the GMM statistics considering the LPT assumption, that is the latent session variable x is approximated using the UBM.", - true -) -.add_prototype("stats,input") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics of the GMM") -.add_parameter("input", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMJFAMachine_estimateUx(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = estimate_ux.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&input)) - return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, estimate_ux.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, estimate_ux.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs()*(Py_ssize_t)self->cxx->getNGaussians(), (Py_ssize_t)input->shape[0], estimate_ux.name()); - return 0; - } - - self->cxx->estimateUx(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(input)); - - BOB_CATCH_MEMBER("cannot estimate Ux", 0) - Py_RETURN_NONE; -} - - -/*** forward_ux ***/ -static auto forward_ux = bob::extension::FunctionDoc( - "forward_ux", - "Computes a score for the given UBM statistics and given the Ux vector", - "", - true -) -.add_prototype("stats,ux") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input") -.add_parameter("ux", "array_like <float, 1D>", "Input vector"); -static PyObject* PyBobLearnEMJFAMachine_ForwardUx(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = forward_ux.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* ux_input = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter,&ux_input)) - return 0; - - //protects acquired resources through this scope - auto ux_input_ = make_safe(ux_input); - - // perform check on the input - if (ux_input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, forward_ux.name()); - return 0; - } - - if (ux_input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, forward_ux.name()); - return 0; - } - - if (ux_input->shape[0] != (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs()) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d, elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getNGaussians()*(Py_ssize_t)self->cxx->getNInputs(), (Py_ssize_t)ux_input->shape[0], forward_ux.name()); - return 0; - } - - double score = self->cxx->forward(*stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(ux_input)); - - return Py_BuildValue("d", score); - BOB_CATCH_MEMBER("cannot forward_ux", 0) - -} - - -/*** log_likelihood ***/ -static auto log_likelihood = bob::extension::FunctionDoc( - "log_likelihood", - "Computes the log-likelihood of the given samples", - ".. note:: the ``__call__`` function is an alias for this function.", - true -) -.add_prototype("stats") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "Statistics as input"); -static PyObject* PyBobLearnEMJFAMachine_log_likelihood(PyBobLearnEMJFAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = log_likelihood.kwlist(0); - - PyBobLearnEMGMMStatsObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMStats_Type, &stats)) - return 0; - - //protects acquired resources through this scope - double score = self->cxx->forward(*stats->cxx); - - return Py_BuildValue("d", score); - BOB_CATCH_MEMBER("cannot log_likelihood", 0) - -} - - -static PyMethodDef PyBobLearnEMJFAMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMJFAMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMJFAMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMJFAMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - - { - estimate_x.name(), - (PyCFunction)PyBobLearnEMJFAMachine_estimateX, - METH_VARARGS|METH_KEYWORDS, - estimate_x.doc() - }, - - { - estimate_ux.name(), - (PyCFunction)PyBobLearnEMJFAMachine_estimateUx, - METH_VARARGS|METH_KEYWORDS, - estimate_ux.doc() - }, - - { - forward_ux.name(), - (PyCFunction)PyBobLearnEMJFAMachine_ForwardUx, - METH_VARARGS|METH_KEYWORDS, - forward_ux.doc() - }, - { - log_likelihood.name(), - (PyCFunction)PyBobLearnEMJFAMachine_log_likelihood, - METH_VARARGS|METH_KEYWORDS, - log_likelihood.doc() - }, - - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMJFAMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMJFAMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMJFAMachine_Type.tp_name = JFAMachine_doc.name(); - PyBobLearnEMJFAMachine_Type.tp_basicsize = sizeof(PyBobLearnEMJFAMachineObject); - PyBobLearnEMJFAMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyBobLearnEMJFAMachine_Type.tp_doc = JFAMachine_doc.doc(); - - // set the functions - PyBobLearnEMJFAMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMJFAMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMJFAMachine_init); - PyBobLearnEMJFAMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMJFAMachine_delete); - PyBobLearnEMJFAMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFAMachine_RichCompare); - PyBobLearnEMJFAMachine_Type.tp_methods = PyBobLearnEMJFAMachine_methods; - PyBobLearnEMJFAMachine_Type.tp_getset = PyBobLearnEMJFAMachine_getseters; - PyBobLearnEMJFAMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFAMachine_log_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMJFAMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMJFAMachine_Type); - return PyModule_AddObject(module, "JFAMachine", (PyObject*)&PyBobLearnEMJFAMachine_Type) >= 0; -} diff --git a/bob/learn/em/jfa_trainer.cpp b/bob/learn/em/jfa_trainer.cpp deleted file mode 100644 index 5d9abadc96821d28cec456dc20b1ef49c5364376..0000000000000000000000000000000000000000 --- a/bob/learn/em/jfa_trainer.cpp +++ /dev/null @@ -1,1107 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Sun 01 Fev 09:40:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" -#include <boost/make_shared.hpp> - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static int extract_GMMStats_1d(PyObject *list, - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - training_data.push_back(stats->cxx); - } - return 0; -} - -static int extract_GMMStats_2d(PyObject *list, - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyObject* another_list; - if(!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyList_Type, &another_list)){ - PyErr_Format(PyExc_RuntimeError, "Expected a list of lists of GMMStats objects [[GMMStats,GMMStats],[GMMStats,GMMStats].....[GMMStats,GMMStats]]"); - return -1; - } - - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > another_training_data; - for (int j=0; j<PyList_GET_SIZE(another_list); j++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(another_list, j), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - another_training_data.push_back(stats->cxx); - } - training_data.push_back(another_training_data); - } - return 0; -} - -template <int N> -static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec) -{ - PyObject* list = PyList_New(vec.size()); - for(size_t i=0; i<vec.size(); i++){ - blitz::Array<double,N> numpy_array = vec[i]; - PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array); - PyList_SET_ITEM(list, i, numpy_py_object); - } - return list; -} - -template <int N> -int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyBlitzArrayObject* blitz_object; - if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){ - PyErr_Format(PyExc_RuntimeError, "Expected numpy array object"); - return -1; - } - auto blitz_object_ = make_safe(blitz_object); - vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object)); - } - return 0; -} - - - -static auto JFATrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".JFATrainer", - "Trains a Joint Factor Analysis (:ref:`JFA <jfa>`) on top of GMMs" - "", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Constructor. Builds a new JFATrainer", - "", - true - ) - .add_prototype("other","") - .add_prototype("","") - .add_parameter("other", ":py:class:`bob.learn.em.JFATrainer`", "A JFATrainer object to be copied.") -); - - -static int PyBobLearnEMJFATrainer_init_copy(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = JFATrainer_doc.kwlist(0); - PyBobLearnEMJFATrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMJFATrainer_Type, &o)){ - JFATrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::JFATrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMJFATrainer_init(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch(nargs){ - case 0:{ - self->cxx.reset(new bob::learn::em::JFATrainer()); - return 0; - } - case 1:{ - // If the constructor input is JFATrainer object - return PyBobLearnEMJFATrainer_init_copy(self, args, kwargs); - } - default:{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 and 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - JFATrainer_doc.print_usage(); - return -1; - } - } - BOB_CATCH_MEMBER("cannot create JFATrainer", -1) - return 0; -} - - -static void PyBobLearnEMJFATrainer_delete(PyBobLearnEMJFATrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMJFATrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMJFATrainer_Type)); -} - - -static PyObject* PyBobLearnEMJFATrainer_RichCompare(PyBobLearnEMJFATrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMJFATrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMJFATrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare JFATrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -static auto acc_v_a1 = bob::extension::VariableDoc( - "acc_v_a1", - "array_like <float, 3D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_v_a1(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccVA1()); - BOB_CATCH_MEMBER("acc_v_a1 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_v_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_v_a1.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_v_a1.name()); - return -1; - } - - if (input->ndim != 3){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_v_a1.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccVA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccVA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccVA1().extent(2)) { - PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccVA1().extent(0), (Py_ssize_t)self->cxx->getAccVA1().extent(1), (Py_ssize_t)self->cxx->getAccVA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_v_a1.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_v_a1"); - if (!b) return -1; - self->cxx->setAccVA1(*b); - return 0; - BOB_CATCH_MEMBER("acc_v_a1 could not be set", -1) -} - - -static auto acc_v_a2 = bob::extension::VariableDoc( - "acc_v_a2", - "array_like <float, 2D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_v_a2(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccVA2()); - BOB_CATCH_MEMBER("acc_v_a2 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_v_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_v_a2.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_v_a2.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_v_a2.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccVA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccVA2().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccVA2().extent(0), (Py_ssize_t)self->cxx->getAccVA2().extent(1), input->shape[0], input->shape[1], acc_v_a2.name()); - return -1; - } - - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_v_a2"); - if (!b) return -1; - self->cxx->setAccVA2(*b); - return 0; - BOB_CATCH_MEMBER("acc_v_a2 could not be set", -1) -} - - -static auto acc_u_a1 = bob::extension::VariableDoc( - "acc_u_a1", - "array_like <float, 3D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_u_a1(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA1()); - BOB_CATCH_MEMBER("acc_u_a1 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_u_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 3D array of floats", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - - if (input->ndim != 3){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 3D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a1.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA1().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA1().extent(1) && input->shape[2] != (Py_ssize_t)self->cxx->getAccUA1().extent(2)) { - PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA1().extent(0), (Py_ssize_t)self->cxx->getAccUA1().extent(1), (Py_ssize_t)self->cxx->getAccUA1().extent(2), (Py_ssize_t)input->shape[0], (Py_ssize_t)input->shape[1], (Py_ssize_t)input->shape[2], acc_u_a1.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,3>(input, "acc_u_a1"); - if (!b) return -1; - self->cxx->setAccUA1(*b); - return 0; - BOB_CATCH_MEMBER("acc_u_a1 could not be set", -1) -} - - -static auto acc_u_a2 = bob::extension::VariableDoc( - "acc_u_a2", - "array_like <float, 2D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_u_a2(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccUA2()); - BOB_CATCH_MEMBER("acc_u_a2 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_u_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_u_a2.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccUA2().extent(0) && input->shape[1] != (Py_ssize_t)self->cxx->getAccUA2().extent(1)) { - PyErr_Format(PyExc_TypeError, "`%s' 3D `input` array should have the shape [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] not [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccUA2().extent(0), (Py_ssize_t)self->cxx->getAccUA2().extent(1), input->shape[0], input->shape[1], acc_u_a2.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "acc_u_a2"); - if (!b) return -1; - self->cxx->setAccUA2(*b); - return 0; - BOB_CATCH_MEMBER("acc_u_a2 could not be set", -1) -} - - -static auto acc_d_a1 = bob::extension::VariableDoc( - "acc_d_a1", - "array_like <float, 1D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_d_a1(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccDA1()); - BOB_CATCH_MEMBER("acc_d_a1 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_d_a1(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_d_a1.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_d_a1.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_d_a1.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccDA1().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccDA1().extent(0), input->shape[0], acc_d_a1.name()); - return -1; - } - - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "acc_d_a1"); - if (!b) return -1; - self->cxx->setAccDA1(*b); - return 0; - BOB_CATCH_MEMBER("acc_d_a1 could not be set", -1) -} - - -static auto acc_d_a2 = bob::extension::VariableDoc( - "acc_d_a2", - "array_like <float, 1D>", - "Accumulator updated during the E-step", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_acc_d_a2(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAccDA2()); - BOB_CATCH_MEMBER("acc_d_a2 could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_acc_d_a2(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, acc_d_a1.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, acc_d_a2.name()); - return -1; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, acc_d_a2.name()); - return -1; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getAccDA2().extent(0)) { - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, (Py_ssize_t)self->cxx->getAccDA2().extent(0), input->shape[0], acc_d_a2.name()); - return -1; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(input, "acc_d_a2"); - if (!b) return -1; - self->cxx->setAccDA2(*b); - return 0; - BOB_CATCH_MEMBER("acc_d_a2 could not be set", -1) -} - - -static auto __X__ = bob::extension::VariableDoc( - "__X__", - "list", - "", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_X(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return vector_as_list(self->cxx->getX()); - BOB_CATCH_MEMBER("__X__ could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_X(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - - // Parses input arguments in a single shot - if (!PyList_Check(value)){ - PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __X__.name()); - return -1; - } - - std::vector<blitz::Array<double,2> > data; - if(list_as_vector(value ,data)==0){ - self->cxx->setX(data); - } - - return 0; - BOB_CATCH_MEMBER("__X__ could not be written", -1) -} - - - -static auto __Y__ = bob::extension::VariableDoc( - "__Y__", - "list", - "", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_Y(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return vector_as_list(self->cxx->getY()); - BOB_CATCH_MEMBER("__Y__ could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_Y(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - - // Parses input arguments in a single shot - if (!PyList_Check(value)){ - PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Y__.name()); - return -1; - } - - std::vector<blitz::Array<double,1> > data; - if(list_as_vector(value ,data)==0){ - self->cxx->setY(data); - } - - return 0; - BOB_CATCH_MEMBER("__Y__ could not be written", -1) -} - - - -static auto __Z__ = bob::extension::VariableDoc( - "__Z__", - "list", - "", - "" -); -PyObject* PyBobLearnEMJFATrainer_get_Z(PyBobLearnEMJFATrainerObject* self, void*){ - BOB_TRY - return vector_as_list(self->cxx->getZ()); - BOB_CATCH_MEMBER("__Z__ could not be read", 0) -} -int PyBobLearnEMJFATrainer_set_Z(PyBobLearnEMJFATrainerObject* self, PyObject* value, void*){ - BOB_TRY - - // Parses input arguments in a single shot - if (!PyList_Check(value)){ - PyErr_Format(PyExc_TypeError, "Expected a list in `%s'", __Z__.name()); - return -1; - } - - std::vector<blitz::Array<double,1> > data; - if(list_as_vector(value ,data)==0){ - self->cxx->setZ(data); - } - - return 0; - BOB_CATCH_MEMBER("__Z__ could not be written", -1) -} - - - -static PyGetSetDef PyBobLearnEMJFATrainer_getseters[] = { - { - acc_v_a1.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_v_a1, - (setter)PyBobLearnEMJFATrainer_set_acc_v_a1, - acc_v_a1.doc(), - 0 - }, - { - acc_v_a2.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_v_a2, - (setter)PyBobLearnEMJFATrainer_set_acc_v_a2, - acc_v_a2.doc(), - 0 - }, - { - acc_u_a1.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_u_a1, - (setter)PyBobLearnEMJFATrainer_set_acc_u_a1, - acc_u_a1.doc(), - 0 - }, - { - acc_u_a2.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_u_a2, - (setter)PyBobLearnEMJFATrainer_set_acc_u_a2, - acc_u_a2.doc(), - 0 - }, - { - acc_d_a1.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_d_a1, - (setter)PyBobLearnEMJFATrainer_set_acc_d_a1, - acc_d_a1.doc(), - 0 - }, - { - acc_d_a2.name(), - (getter)PyBobLearnEMJFATrainer_get_acc_d_a2, - (setter)PyBobLearnEMJFATrainer_set_acc_d_a2, - acc_d_a2.doc(), - 0 - }, - { - __X__.name(), - (getter)PyBobLearnEMJFATrainer_get_X, - (setter)PyBobLearnEMJFATrainer_set_X, - __X__.doc(), - 0 - }, - { - __Y__.name(), - (getter)PyBobLearnEMJFATrainer_get_Y, - (setter)PyBobLearnEMJFATrainer_set_Y, - __Y__.doc(), - 0 - }, - { - __Z__.name(), - (getter)PyBobLearnEMJFATrainer_get_Z, - (setter)PyBobLearnEMJFATrainer_set_Z, - __Z__.doc(), - 0 - }, - - - - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("jfa_base, stats, [rng]") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMJFATrainer_initialize(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats, - &PyBoostMt19937_Type, &rng)) return 0; - - if(rng){ - self->cxx->setRng(rng->rng); - } - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->initialize(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_stepv ***/ -static auto e_step_v = bob::extension::FunctionDoc( - "e_step_v", - "Call the 1st e-step procedure (for the V subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_e_step_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - //Parses input arguments in a single shot - char** kwlist = e_step_v.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->eStep1(*jfa_base->cxx, training_data); - else - return 0; - - - BOB_CATCH_MEMBER("cannot perform the e_step_v method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step_v ***/ -static auto m_step_v = bob::extension::FunctionDoc( - "m_step_v", - "Call the 1st m-step procedure (for the V subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_m_step_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = m_step_v.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->mStep1(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the m_step_v method", 0) - - Py_RETURN_NONE; -} - - -/*** finalize_v ***/ -static auto finalize_v = bob::extension::FunctionDoc( - "finalize_v", - "Call the 1st finalize procedure (for the V subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_finalize_v(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - //Parses input arguments in a single shot - char** kwlist = finalize_v.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->finalize1(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the finalize_v method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step_u ***/ -static auto e_step_u = bob::extension::FunctionDoc( - "e_step_u", - "Call the 2nd e-step procedure (for the U subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_e_step_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = e_step_u.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->eStep2(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the e_step_u method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step_u ***/ -static auto m_step_u = bob::extension::FunctionDoc( - "m_step_u", - "Call the 2nd m-step procedure (for the U subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_m_step_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = m_step_u.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->mStep2(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the m_step_u method", 0) - - Py_RETURN_NONE; -} - - -/*** finalize_u ***/ -static auto finalize_u = bob::extension::FunctionDoc( - "finalize_u", - "Call the 2nd finalize procedure (for the U subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_finalize_u(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = finalize_u.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->finalize2(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the finalize_u method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step_d ***/ -static auto e_step_d = bob::extension::FunctionDoc( - "e_step_d", - "Call the 3rd e-step procedure (for the d subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_e_step_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = e_step_d.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->eStep3(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the e_step_d method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step_d ***/ -static auto m_step_d = bob::extension::FunctionDoc( - "m_step_d", - "Call the 3rd m-step procedure (for the d subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_m_step_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = m_step_d.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->mStep3(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the m_step_d method", 0) - - Py_RETURN_NONE; -} - - -/*** finalize_d ***/ -static auto finalize_d = bob::extension::FunctionDoc( - "finalize_d", - "Call the 3rd finalize procedure (for the d subspace).", - "", - true -) -.add_prototype("jfa_base,stats") -.add_parameter("jfa_base", ":py:class:`bob.learn.em.JFABase`", "JFABase Object") -.add_parameter("stats", ":py:class:`bob.learn.em.GMMStats`", "GMMStats Object"); -static PyObject* PyBobLearnEMJFATrainer_finalize_d(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = finalize_d.kwlist(0); - - PyBobLearnEMJFABaseObject* jfa_base = 0; - PyObject* stats = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMJFABase_Type, &jfa_base, - &PyList_Type, &stats)) return 0; - - std::vector<std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > > training_data; - if(extract_GMMStats_2d(stats ,training_data)==0) - self->cxx->finalize3(*jfa_base->cxx, training_data); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the finalize_d method", 0) - - Py_RETURN_NONE; -} - - -/*** enroll ***/ -static auto enroll = bob::extension::FunctionDoc( - "enroll", - "", - "", - true -) -.add_prototype("jfa_machine,features,n_iter") -.add_parameter("jfa_machine", ":py:class:`bob.learn.em.JFAMachine`", "JFAMachine Object") -.add_parameter("features", "[:py:class:`bob.learn.em.GMMStats`]", "") -.add_parameter("n_iter", "int", "Number of iterations"); -static PyObject* PyBobLearnEMJFATrainer_enroll(PyBobLearnEMJFATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // Parses input arguments in a single shot - char** kwlist = enroll.kwlist(0); - - PyBobLearnEMJFAMachineObject* jfa_machine = 0; - PyObject* stats = 0; - int n_iter = 1; - - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!i", kwlist, &PyBobLearnEMJFAMachine_Type, &jfa_machine, - &PyList_Type, &stats, &n_iter)) return 0; - - std::vector<boost::shared_ptr<bob::learn::em::GMMStats> > training_data; - if(extract_GMMStats_1d(stats ,training_data)==0) - self->cxx->enroll(*jfa_machine->cxx, training_data, n_iter); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the enroll method", 0) - - Py_RETURN_NONE; -} - - - -static PyMethodDef PyBobLearnEMJFATrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMJFATrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step_v.name(), - (PyCFunction)PyBobLearnEMJFATrainer_e_step_v, - METH_VARARGS|METH_KEYWORDS, - e_step_v.doc() - }, - { - e_step_u.name(), - (PyCFunction)PyBobLearnEMJFATrainer_e_step_u, - METH_VARARGS|METH_KEYWORDS, - e_step_u.doc() - }, - { - e_step_d.name(), - (PyCFunction)PyBobLearnEMJFATrainer_e_step_d, - METH_VARARGS|METH_KEYWORDS, - e_step_d.doc() - }, - { - m_step_v.name(), - (PyCFunction)PyBobLearnEMJFATrainer_m_step_v, - METH_VARARGS|METH_KEYWORDS, - m_step_v.doc() - }, - { - m_step_u.name(), - (PyCFunction)PyBobLearnEMJFATrainer_m_step_u, - METH_VARARGS|METH_KEYWORDS, - m_step_u.doc() - }, - { - m_step_d.name(), - (PyCFunction)PyBobLearnEMJFATrainer_m_step_d, - METH_VARARGS|METH_KEYWORDS, - m_step_d.doc() - }, - { - finalize_v.name(), - (PyCFunction)PyBobLearnEMJFATrainer_finalize_v, - METH_VARARGS|METH_KEYWORDS, - finalize_v.doc() - }, - { - finalize_u.name(), - (PyCFunction)PyBobLearnEMJFATrainer_finalize_u, - METH_VARARGS|METH_KEYWORDS, - finalize_u.doc() - }, - { - finalize_d.name(), - (PyCFunction)PyBobLearnEMJFATrainer_finalize_d, - METH_VARARGS|METH_KEYWORDS, - finalize_d.doc() - }, - { - enroll.name(), - (PyCFunction)PyBobLearnEMJFATrainer_enroll, - METH_VARARGS|METH_KEYWORDS, - enroll.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMJFATrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMJFATrainer(PyObject* module) -{ - // initialize the type JFATrainer - PyBobLearnEMJFATrainer_Type.tp_name = JFATrainer_doc.name(); - PyBobLearnEMJFATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMJFATrainerObject); - PyBobLearnEMJFATrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance; - PyBobLearnEMJFATrainer_Type.tp_doc = JFATrainer_doc.doc(); - - // set the functions - PyBobLearnEMJFATrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMJFATrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMJFATrainer_init); - PyBobLearnEMJFATrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMJFATrainer_delete); - PyBobLearnEMJFATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMJFATrainer_RichCompare); - PyBobLearnEMJFATrainer_Type.tp_methods = PyBobLearnEMJFATrainer_methods; - PyBobLearnEMJFATrainer_Type.tp_getset = PyBobLearnEMJFATrainer_getseters; - //PyBobLearnEMJFATrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMJFATrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMJFATrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMJFATrainer_Type); - return PyModule_AddObject(module, "JFATrainer", (PyObject*)&PyBobLearnEMJFATrainer_Type) >= 0; -} diff --git a/bob/learn/em/kmeans_machine.cpp b/bob/learn/em/kmeans_machine.cpp deleted file mode 100644 index 9fc87269c50f4c89aa9289c66988d7404568431a..0000000000000000000000000000000000000000 --- a/bob/learn/em/kmeans_machine.cpp +++ /dev/null @@ -1,865 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Fri 26 Dec 16:18:00 2014 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto KMeansMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".KMeansMachine", - "Statistical model for the :ref:`k-means <kmeans>` .\n" - "See Section 9.1 of Bishop, \"Pattern recognition and machine learning\", 2006" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a KMeansMachine", - "", - true - ) - .add_prototype("n_means,n_inputs","") - .add_prototype("other","") - .add_prototype("hdf5","") - .add_prototype("","") - - .add_parameter("n_means", "int", "Number of means") - .add_parameter("n_inputs", "int", "Dimension of the feature vector") - .add_parameter("other", ":py:class:`bob.learn.em.KMeansMachine`", "A KMeansMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMKMeansMachine_init_number(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = KMeansMachine_doc.kwlist(0); - int n_inputs = 1; - int n_means = 1; - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_means, &n_inputs)) - return -1; - - if(n_means < 0){ - PyErr_Format(PyExc_TypeError, "means argument must be greater than or equal to zero"); - KMeansMachine_doc.print_usage(); - return -1; - } - - if(n_inputs < 0){ - PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero"); - KMeansMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::KMeansMachine(n_means, n_inputs)); - return 0; -} - - -static int PyBobLearnEMKMeansMachine_init_copy(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = KMeansMachine_doc.kwlist(1); - PyBobLearnEMKMeansMachineObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &tt)){ - KMeansMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::KMeansMachine(*tt->cxx)); - return 0; -} - - -static int PyBobLearnEMKMeansMachine_init_hdf5(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = KMeansMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - KMeansMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::KMeansMachine(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMKMeansMachine_init(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 0: //default initializer () - self->cxx.reset(new bob::learn::em::KMeansMachine()); - return 0; - - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMKMeansMachine_Check(arg)) - return PyBobLearnEMKMeansMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMKMeansMachine_init_hdf5(self, args, kwargs); - } - case 2: - return PyBobLearnEMKMeansMachine_init_number(self, args, kwargs); - default: - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0, 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - KMeansMachine_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create KMeansMachine", -1) - return 0; -} - - - -static void PyBobLearnEMKMeansMachine_delete(PyBobLearnEMKMeansMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMKMeansMachine_RichCompare(PyBobLearnEMKMeansMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMKMeansMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMKMeansMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare KMeansMachine objects", 0) -} - -int PyBobLearnEMKMeansMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMKMeansMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int)", - "A tuple that represents the number of means and dimensionality of the feature vector``(n_means, dim)``.", - "" -); -PyObject* PyBobLearnEMKMeansMachine_getShape(PyBobLearnEMKMeansMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i)", self->cxx->getNMeans(), self->cxx->getNInputs()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - -/***** MEAN *****/ - -static auto means = bob::extension::VariableDoc( - "means", - "array_like <float, 2D>", - "The means", - "" -); -PyObject* PyBobLearnEMKMeansMachine_getMeans(PyBobLearnEMKMeansMachineObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMeans()); - BOB_CATCH_MEMBER("means could not be read", 0) -} -int PyBobLearnEMKMeansMachine_setMeans(PyBobLearnEMKMeansMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* input; - if (!PyBlitzArray_Converter(value, &input)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, means.name()); - return -1; - } - auto o_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, means.name()); - return 0; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, means.name()); - return 0; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs()) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], means.name()); - return 0; - } - - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(input, "means"); - if (!b) return -1; - self->cxx->setMeans(*b); - return 0; - BOB_CATCH_MEMBER("means could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMKMeansMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMKMeansMachine_getShape, - 0, - shape.doc(), - 0 - }, - { - means.name(), - (getter)PyBobLearnEMKMeansMachine_getMeans, - (setter)PyBobLearnEMKMeansMachine_setMeans, - means.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the KMeansMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMKMeansMachine_Save(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the KMeansMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMKMeansMachine_Load(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this KMeansMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.KMeansMachine`", "A KMeansMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMKMeansMachine_IsSimilarTo(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMKMeansMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMKMeansMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Allocates space for the statistics and resets to zero.", - 0, - true -) -.add_prototype("n_means,n_inputs") -.add_parameter("n_means", "int", "Number of means") -.add_parameter("n_inputs", "int", "Dimensionality of the feature vector"); -static PyObject* PyBobLearnEMKMeansMachine_resize(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int n_means = 0; - int n_inputs = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &n_means, &n_inputs)) return 0; - - if (n_means <= 0){ - PyErr_Format(PyExc_TypeError, "n_means must be greater than zero"); - resize.print_usage(); - return 0; - } - if (n_inputs <= 0){ - PyErr_Format(PyExc_TypeError, "n_inputs must be greater than zero"); - resize.print_usage(); - return 0; - } - - self->cxx->resize(n_means, n_inputs); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - -/*** get_mean ***/ -static auto get_mean = bob::extension::FunctionDoc( - "get_mean", - "Get the i'th mean.", - ".. note:: An exception is thrown if i is out of range.", - true -) -.add_prototype("i","mean") -.add_parameter("i", "int", "Index of the mean") -.add_return("mean","array_like <float, 1D>","Mean array"); -static PyObject* PyBobLearnEMKMeansMachine_get_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_mean.kwlist(0); - - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMean(i)); - - BOB_CATCH_MEMBER("cannot get the mean", 0) -} - - -/*** set_mean ***/ -static auto set_mean = bob::extension::FunctionDoc( - "set_mean", - "Set the i'th mean.", - ".. note:: An exception is thrown if i is out of range.", - true -) -.add_prototype("i,mean") -.add_parameter("i", "int", "Index of the mean") -.add_parameter("mean", "array_like <float, 1D>", "Mean array"); -static PyObject* PyBobLearnEMKMeansMachine_set_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = set_mean.kwlist(0); - - int i = 0; - PyBlitzArrayObject* mean = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &mean)) return 0; - - //protects acquired resources through this scope - auto mean_ = make_safe(mean); - - // perform check on the input - if (mean->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, set_mean.name()); - return 0; - } - - if (mean->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, set_mean.name()); - return 0; - } - - if (mean->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), mean->shape[0], set_mean.name()); - return 0; - } - - //setting the mean - self->cxx->setMean(i, *PyBlitzArrayCxx_AsBlitz<double,1>(mean)); - - BOB_CATCH_MEMBER("cannot set the mean", 0) - - Py_RETURN_NONE; -} - - - -/*** get_distance_from_mean ***/ -static auto get_distance_from_mean = bob::extension::FunctionDoc( - "get_distance_from_mean", - "Return the power of two of the square Euclidean distance of the sample, x, to the i'th mean.", - ".. note:: An exception is thrown if i is out of range.", - true -) -.add_prototype("input,i","output") -.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)") -.add_parameter("i", "int", "The index of the mean") -.add_return("output","float","Square Euclidean distance of the sample, x, to the i'th mean"); -static PyObject* PyBobLearnEMKMeansMachine_get_distance_from_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_distance_from_mean.kwlist(0); - - PyBlitzArrayObject* input = 0; - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i", kwlist, &PyBlitzArray_Converter, &input, &i)){ - return 0; - } - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_distance_from_mean.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_distance_from_mean.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_distance_from_mean.name()); - return 0; - } - - double output = self->cxx->getDistanceFromMean(*PyBlitzArrayCxx_AsBlitz<double,1>(input),i); - return Py_BuildValue("d", output); - - BOB_CATCH_MEMBER("cannot compute the likelihood", 0) -} - - -/*** get_closest_mean ***/ -static auto get_closest_mean = bob::extension::FunctionDoc( - "get_closest_mean", - "Calculate the index of the mean that is closest (in terms of square Euclidean distance) to the data sample, x.", - "", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)") -.add_return("output", "(int, int)", "Tuple containing the closest mean and the minimum distance from the input"); -static PyObject* PyBobLearnEMKMeansMachine_get_closest_mean(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_closest_mean.kwlist(0); - - PyBlitzArrayObject* input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - size_t closest_mean = 0; - double min_distance = -1; - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_closest_mean.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_closest_mean.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_closest_mean.name()); - return 0; - } - - self->cxx->getClosestMean(*PyBlitzArrayCxx_AsBlitz<double,1>(input), closest_mean, min_distance); - - return Py_BuildValue("(i,d)", closest_mean, min_distance); - - BOB_CATCH_MEMBER("cannot compute the closest mean", 0) -} - - -/*** get_min_distance ***/ -static auto get_min_distance = bob::extension::FunctionDoc( - "get_min_distance", - "Output the minimum (Square Euclidean) distance between the input and the closest mean ", - "", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 1D>", "The data sample (feature vector)") -.add_return("output", "float", "The minimum distance"); -static PyObject* PyBobLearnEMKMeansMachine_get_min_distance(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_min_distance.kwlist(0); - - PyBlitzArrayObject* input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - double min_distance = 0; - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_min_distance.name()); - return 0; - } - - if (input->ndim != 1){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 1D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_min_distance.name()); - return 0; - } - - if (input->shape[0] != (Py_ssize_t)self->cxx->getNInputs()){ - PyErr_Format(PyExc_TypeError, "`%s' 1D `input` array should have %" PY_FORMAT_SIZE_T "d elements, not %" PY_FORMAT_SIZE_T "d for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[0], get_min_distance.name()); - return 0; - } - - min_distance = self->cxx->getMinDistance(*PyBlitzArrayCxx_AsBlitz<double,1>(input)); - - return Py_BuildValue("d", min_distance); - - BOB_CATCH_MEMBER("cannot compute the min distance", 0) -} - -/**** get_variances_and_weights_for_each_cluster ***/ -static auto get_variances_and_weights_for_each_cluster = bob::extension::FunctionDoc( - "get_variances_and_weights_for_each_cluster", - "For each mean, find the subset of the samples that is closest to that mean, and calculate" - " 1) the variance of that subset (the cluster variance)" - " 2) the proportion of the samples represented by that subset (the cluster weight)", - "", - true -) -.add_prototype("input","output") -.add_parameter("input", "array_like <float, 2D>", "The data sample (feature vector)") -.add_return("output", "(array_like <float, 2D>, array_like <float, 1D>)", "A tuple with the variances and the weights respectively"); -static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_variances_and_weights_for_each_cluster.kwlist(0); - - PyBlitzArrayObject* input = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &input)) return 0; - - //protects acquired resources through this scope - auto input_ = make_safe(input); - - // perform check on the input - if (input->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, get_variances_and_weights_for_each_cluster.name()); - return 0; - } - - if (input->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, get_variances_and_weights_for_each_cluster.name()); - return 0; - } - - if (input->shape[1] != (Py_ssize_t)self->cxx->getNInputs() ) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, self->cxx->getNInputs(), input->shape[1], get_variances_and_weights_for_each_cluster.name()); - return 0; - } - - blitz::Array<double,2> variances(self->cxx->getNMeans(),self->cxx->getNInputs()); - blitz::Array<double,1> weights(self->cxx->getNMeans()); - - self->cxx->getVariancesAndWeightsForEachCluster(*PyBlitzArrayCxx_AsBlitz<double,2>(input),variances,weights); - - return Py_BuildValue("(N,N)",PyBlitzArrayCxx_AsConstNumpy(variances), PyBlitzArrayCxx_AsConstNumpy(weights)); - - BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0) -} - - -/**** __get_variances_and_weights_for_each_cluster_init__ ***/ -static auto __get_variances_and_weights_for_each_cluster_init__ = bob::extension::FunctionDoc( - "__get_variances_and_weights_for_each_cluster_init__", - "Methods consecutively called by getVariancesAndWeightsForEachCluster()" - "This should help for the parallelization on several nodes by splitting the data and calling" - "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum" - "with the m_cache_means, variances, and weights variables before performing the merge on one" - "node using getVariancesAndWeightsForEachClusterFin().", - "", - true -) -.add_prototype("variances,weights","") -.add_parameter("variances", "array_like <float, 2D>", "Variance array") -.add_parameter("weights", "array_like <float, 1D>", "Weight array"); -static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_init(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = __get_variances_and_weights_for_each_cluster_init__.kwlist(0); - - PyBlitzArrayObject* variances = 0; - PyBlitzArrayObject* weights = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&", kwlist, &PyBlitzArray_Converter, &variances, &PyBlitzArray_Converter, &weights)) return 0; - - //protects acquired resources through this scope - auto weights_ = make_safe(weights); - auto variances_ = make_safe(variances); - - self->cxx->getVariancesAndWeightsForEachClusterInit(*PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights)); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0) -} - - -/**** __get_variances_and_weights_for_each_cluster_acc__ ***/ -static auto __get_variances_and_weights_for_each_cluster_acc__ = bob::extension::FunctionDoc( - "__get_variances_and_weights_for_each_cluster_acc__", - "Methods consecutively called by getVariancesAndWeightsForEachCluster()" - "This should help for the parallelization on several nodes by splitting the data and calling" - "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum" - "with the m_cache_means, variances, and weights variables before performing the merge on one" - "node using getVariancesAndWeightsForEachClusterFin().", - "", - true -) -.add_prototype("data,variances,weights","") -.add_parameter("data", "array_like <float, 2D>", "data array") -.add_parameter("variances", "array_like <float, 2D>", "Variance array") -.add_parameter("weights", "array_like <float, 1D>", "Weight array"); -static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_acc(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = __get_variances_and_weights_for_each_cluster_acc__.kwlist(0); - - PyBlitzArrayObject* data = 0; - PyBlitzArrayObject* variances = 0; - PyBlitzArrayObject* weights = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&", kwlist, &PyBlitzArray_Converter, &data, &PyBlitzArray_Converter, &variances, &PyBlitzArray_Converter, &weights)) return 0; - - //protects acquired resources through this scope - auto data_ = make_safe(data); - auto weights_ = make_safe(weights); - auto variances_ = make_safe(variances); - - self->cxx->getVariancesAndWeightsForEachClusterAcc(*PyBlitzArrayCxx_AsBlitz<double,2>(data), *PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights)); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0) -} - - -/**** __get_variances_and_weights_for_each_cluster_fin__ ***/ -static auto __get_variances_and_weights_for_each_cluster_fin__ = bob::extension::FunctionDoc( - "__get_variances_and_weights_for_each_cluster_fin__", - "Methods consecutively called by getVariancesAndWeightsForEachCluster()" - "This should help for the parallelization on several nodes by splitting the data and calling" - "getVariancesAndWeightsForEachClusterAcc() for each split. In this case, there is a need to sum" - "with the m_cache_means, variances, and weights variables before performing the merge on one" - "node using getVariancesAndWeightsForEachClusterFin().", - "", - true -) -.add_prototype("variances,weights","") -.add_parameter("variances", "array_like <float, 2D>", "Variance array") -.add_parameter("weights", "array_like <float, 1D>", "Weight array"); -static PyObject* PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_fin(PyBobLearnEMKMeansMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = __get_variances_and_weights_for_each_cluster_fin__.kwlist(0); - - PyBlitzArrayObject* variances = 0; - PyBlitzArrayObject* weights = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&", kwlist, &PyBlitzArray_Converter, &variances, &PyBlitzArray_Converter, &weights)) return 0; - - //protects acquired resources through this scope - auto weights_ = make_safe(weights); - auto variances_ = make_safe(variances); - - self->cxx->getVariancesAndWeightsForEachClusterFin(*PyBlitzArrayCxx_AsBlitz<double,2>(variances), *PyBlitzArrayCxx_AsBlitz<double,1>(weights)); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("cannot compute the variances and weights for each cluster", 0) -} - - -static PyMethodDef PyBobLearnEMKMeansMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - get_mean.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_mean, - METH_VARARGS|METH_KEYWORDS, - get_mean.doc() - }, - { - set_mean.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_set_mean, - METH_VARARGS|METH_KEYWORDS, - set_mean.doc() - }, - { - get_distance_from_mean.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_distance_from_mean, - METH_VARARGS|METH_KEYWORDS, - get_distance_from_mean.doc() - }, - { - get_closest_mean.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_closest_mean, - METH_VARARGS|METH_KEYWORDS, - get_closest_mean.doc() - }, - { - get_min_distance.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_min_distance, - METH_VARARGS|METH_KEYWORDS, - get_min_distance.doc() - }, - { - get_variances_and_weights_for_each_cluster.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster, - METH_VARARGS|METH_KEYWORDS, - get_variances_and_weights_for_each_cluster.doc() - }, - { - __get_variances_and_weights_for_each_cluster_init__.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_init, - METH_VARARGS|METH_KEYWORDS, - __get_variances_and_weights_for_each_cluster_init__.doc() - }, - { - __get_variances_and_weights_for_each_cluster_acc__.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_acc, - METH_VARARGS|METH_KEYWORDS, - __get_variances_and_weights_for_each_cluster_acc__.doc() - }, - { - __get_variances_and_weights_for_each_cluster_fin__.name(), - (PyCFunction)PyBobLearnEMKMeansMachine_get_variances_and_weights_for_each_cluster_fin, - METH_VARARGS|METH_KEYWORDS, - __get_variances_and_weights_for_each_cluster_fin__.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMKMeansMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMKMeansMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMKMeansMachine_Type.tp_name = KMeansMachine_doc.name(); - PyBobLearnEMKMeansMachine_Type.tp_basicsize = sizeof(PyBobLearnEMKMeansMachineObject); - PyBobLearnEMKMeansMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBobLearnEMKMeansMachine_Type.tp_doc = KMeansMachine_doc.doc(); - - // set the functions - PyBobLearnEMKMeansMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMKMeansMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMKMeansMachine_init); - PyBobLearnEMKMeansMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMKMeansMachine_delete); - PyBobLearnEMKMeansMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMKMeansMachine_RichCompare); - PyBobLearnEMKMeansMachine_Type.tp_methods = PyBobLearnEMKMeansMachine_methods; - PyBobLearnEMKMeansMachine_Type.tp_getset = PyBobLearnEMKMeansMachine_getseters; - //PyBobLearnEMGMMMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMGMMMachine_loglikelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMKMeansMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMKMeansMachine_Type); - return PyModule_AddObject(module, "KMeansMachine", (PyObject*)&PyBobLearnEMKMeansMachine_Type) >= 0; -} diff --git a/bob/learn/em/kmeans_trainer.cpp b/bob/learn/em/kmeans_trainer.cpp deleted file mode 100644 index d03148cdb70482dfce55ddc807f9a7fb0ede96ff..0000000000000000000000000000000000000000 --- a/bob/learn/em/kmeans_trainer.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Tue 13 Jan 16:50:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" -#include <boost/assign.hpp> - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -// InitializationMethod type conversion - -#if BOOST_VERSION >= 104700 - static const std::map<std::string, bob::learn::em::KMeansTrainer::InitializationMethod> IM = boost::assign::map_list_of - ("RANDOM", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM) - ("RANDOM_NO_DUPLICATE", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM_NO_DUPLICATE) - ("KMEANS_PLUS_PLUS", bob::learn::em::KMeansTrainer::InitializationMethod::KMEANS_PLUS_PLUS) - ; -#else - static const std::map<std::string, bob::learn::em::KMeansTrainer::InitializationMethod> IM = boost::assign::map_list_of - ("RANDOM", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM), - ("RANDOM_NO_DUPLICATE", bob::learn::em::KMeansTrainer::InitializationMethod::RANDOM_NO_DUPLICATE) - ; -#endif - -static inline bob::learn::em::KMeansTrainer::InitializationMethod string2IM(const std::string& o){ /* converts string to InitializationMethod type */ - auto it = IM.find(o); - if (it == IM.end()) throw std::runtime_error("The given InitializationMethod '" + o + "' is not known; choose one of ('RANDOM', 'RANDOM_NO_DUPLICATE', 'KMEANS_PLUS_PLUS')"); - else return it->second; -} -static inline const std::string& IM2string(bob::learn::em::KMeansTrainer::InitializationMethod o){ /* converts InitializationMethod type to string */ - for (auto it = IM.begin(); it != IM.end(); ++it) if (it->second == o) return it->first; - throw std::runtime_error("The given InitializationMethod type is not known"); -} - - -static auto KMeansTrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".KMeansTrainer", - "Trains a KMeans clustering :ref:`k-means <kmeans>`." - "This class implements the expectation-maximization algorithm for a k-means." - "See Section 9.1 of Bishop, \"Pattern recognition and machine learning\", 2006" - "It uses a random initialization of the means followed by the expectation-maximization algorithm" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a KMeansTrainer", - "", - true - ) - .add_prototype("initialization_method","") - .add_prototype("other","") - .add_prototype("","") - - .add_parameter("initialization_method", "str", "The initialization method of the means.\nPossible values are: 'RANDOM', 'RANDOM_NO_DUPLICATE', 'KMEANS_PLUS_PLUS' ") - .add_parameter("other", ":py:class:`bob.learn.em.KMeansTrainer`", "A KMeansTrainer object to be copied.") - -); - - -static int PyBobLearnEMKMeansTrainer_init_copy(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = KMeansTrainer_doc.kwlist(1); - PyBobLearnEMKMeansTrainerObject* tt; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansTrainer_Type, &tt)){ - KMeansTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::KMeansTrainer(*tt->cxx)); - return 0; -} - -static int PyBobLearnEMKMeansTrainer_init_str(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = KMeansTrainer_doc.kwlist(0); - char* value; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &value)){ - KMeansTrainer_doc.print_usage(); - return -1; - } - self->cxx.reset(new bob::learn::em::KMeansTrainer(string2IM(std::string(value)))); - return 0; -} - - -static int PyBobLearnEMKMeansTrainer_init(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - switch (nargs) { - - case 0:{ //default initializer () - self->cxx.reset(new bob::learn::em::KMeansTrainer()); - return 0; - } - case 1:{ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is KMeansTrainer object - if (PyBobLearnEMKMeansTrainer_Check(arg)) - return PyBobLearnEMKMeansTrainer_init_copy(self, args, kwargs); - else if(PyString_Check(arg)) - return PyBobLearnEMKMeansTrainer_init_str(self, args, kwargs); - } - default:{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 0 or 1 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - KMeansTrainer_doc.print_usage(); - return -1; - } - } - BOB_CATCH_MEMBER("cannot create KMeansTrainer", -1) - return 0; -} - - -static void PyBobLearnEMKMeansTrainer_delete(PyBobLearnEMKMeansTrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMKMeansTrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMKMeansTrainer_Type)); -} - - -static PyObject* PyBobLearnEMKMeansTrainer_RichCompare(PyBobLearnEMKMeansTrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMKMeansTrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMKMeansTrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare KMeansTrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** initialization_method *****/ -static auto initialization_method = bob::extension::VariableDoc( - "initialization_method", - "str", - "Initialization method.", - "Possible values:\n" - " `RANDOM`: Random initialization \n\n" - " `RANDOM_NO_DUPLICATE`: Random initialization without repetition \n\n" - " `KMEANS_PLUS_PLUS`: Apply the kmeans++ initialization http://en.wikipedia.org/wiki/K-means%2B%2B \n\n" -); -PyObject* PyBobLearnEMKMeansTrainer_getInitializationMethod(PyBobLearnEMKMeansTrainerObject* self, void*) { - BOB_TRY - return Py_BuildValue("s", IM2string(self->cxx->getInitializationMethod()).c_str()); - BOB_CATCH_MEMBER("initialization method could not be read", 0) -} -int PyBobLearnEMKMeansTrainer_setInitializationMethod(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyString_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, initialization_method.name()); - return -1; - } - self->cxx->setInitializationMethod(string2IM(PyString_AS_STRING(value))); - - return 0; - BOB_CATCH_MEMBER("initialization method could not be set", -1) -} - - -/***** zeroeth_order_statistics *****/ -static auto zeroeth_order_statistics = bob::extension::VariableDoc( - "zeroeth_order_statistics", - "array_like <float, 1D>", - "Returns the internal statistics. Useful to parallelize the E-step", - "" -); -PyObject* PyBobLearnEMKMeansTrainer_getZeroethOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZeroethOrderStats()); - BOB_CATCH_MEMBER("zeroeth_order_statistics could not be read", 0) -} -int PyBobLearnEMKMeansTrainer_setZeroethOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, zeroeth_order_statistics.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "zeroeth_order_statistics"); - if (!b) return -1; - self->cxx->setZeroethOrderStats(*b); - return 0; - BOB_CATCH_MEMBER("zeroeth_order_statistics could not be set", -1) -} - - -/***** first_order_statistics *****/ -static auto first_order_statistics = bob::extension::VariableDoc( - "first_order_statistics", - "array_like <float, 2D>", - "Returns the internal statistics. Useful to parallelize the E-step", - "" -); -PyObject* PyBobLearnEMKMeansTrainer_getFirstOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getFirstOrderStats()); - BOB_CATCH_MEMBER("first_order_statistics could not be read", 0) -} -int PyBobLearnEMKMeansTrainer_setFirstOrderStatistics(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, first_order_statistics.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "first_order_statistics"); - if (!b) return -1; - self->cxx->setFirstOrderStats(*b); - return 0; - BOB_CATCH_MEMBER("first_order_statistics could not be set", -1) -} - - -/***** average_min_distance *****/ -static auto average_min_distance = bob::extension::VariableDoc( - "average_min_distance", - "str", - "Average min (square Euclidean) distance. Useful to parallelize the E-step.", - "" -); -PyObject* PyBobLearnEMKMeansTrainer_getAverageMinDistance(PyBobLearnEMKMeansTrainerObject* self, void*) { - BOB_TRY - return Py_BuildValue("d", self->cxx->getAverageMinDistance()); - BOB_CATCH_MEMBER("Average Min Distance method could not be read", 0) -} -int PyBobLearnEMKMeansTrainer_setAverageMinDistance(PyBobLearnEMKMeansTrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an double", Py_TYPE(self)->tp_name, average_min_distance.name()); - return -1; - } - self->cxx->setAverageMinDistance(PyFloat_AS_DOUBLE(value)); - - return 0; - BOB_CATCH_MEMBER("Average Min Distance could not be set", -1) -} - - - - -static PyGetSetDef PyBobLearnEMKMeansTrainer_getseters[] = { - { - initialization_method.name(), - (getter)PyBobLearnEMKMeansTrainer_getInitializationMethod, - (setter)PyBobLearnEMKMeansTrainer_setInitializationMethod, - initialization_method.doc(), - 0 - }, - { - zeroeth_order_statistics.name(), - (getter)PyBobLearnEMKMeansTrainer_getZeroethOrderStatistics, - (setter)PyBobLearnEMKMeansTrainer_setZeroethOrderStatistics, - zeroeth_order_statistics.doc(), - 0 - }, - { - first_order_statistics.name(), - (getter)PyBobLearnEMKMeansTrainer_getFirstOrderStatistics, - (setter)PyBobLearnEMKMeansTrainer_setFirstOrderStatistics, - first_order_statistics.doc(), - 0 - }, - { - average_min_distance.name(), - (getter)PyBobLearnEMKMeansTrainer_getAverageMinDistance, - (setter)PyBobLearnEMKMeansTrainer_setAverageMinDistance, - average_min_distance.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialise the means randomly", - "Data is split into as many chunks as there are means, then each mean is set to a random example within each chunk.", - true -) -.add_prototype("kmeans_machine, data, [rng]") -.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMKMeansTrainer_initialize(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMKMeansMachineObject* kmeans_machine = 0; - PyBlitzArrayObject* data = 0; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&|O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine, - &PyBlitzArray_Converter, &data, - &PyBoostMt19937_Type, &rng)) return 0; - auto data_ = make_safe(data); - - // perform check on the input - if (data->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, initialize.name()); - return 0; - } - - if (data->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, initialize.name()); - return 0; - } - - if (data->shape[1] != (Py_ssize_t)kmeans_machine->cxx->getNInputs() ) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, kmeans_machine->cxx->getNInputs(), data->shape[1], initialize.name()); - return 0; - } - - if(rng){ - self->cxx->setRng(rng->rng); - } - - self->cxx->initialize(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Compute the E-step, which is basically the distances ", - "Accumulate across the dataset:" - " -zeroeth and first order statistics" - " -average (Square Euclidean) distance from the closest mean", - true -) -.add_prototype("kmeans_machine,data") -.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMKMeansTrainer_e_step(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMKMeansMachineObject* kmeans_machine; - PyBlitzArrayObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine, - &PyBlitzArray_Converter, &data)) return 0; - auto data_ = make_safe(data); - - if (data->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->shape[1] != (Py_ssize_t)kmeans_machine->cxx->getNInputs() ) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, kmeans_machine->cxx->getNInputs(), data->shape[1], e_step.name()); - return 0; - } - - auto state = PyEval_SaveThread(); - self->cxx->eStep(*kmeans_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - PyEval_RestoreThread(state); - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "Updates the mean based on the statistics from the E-step", - 0, - true -) -.add_prototype("kmeans_machine, [data]") -.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object") -.add_parameter("data", "object", "Ignored."); -static PyObject* PyBobLearnEMKMeansTrainer_m_step(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMKMeansMachineObject* kmeans_machine; - PyObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine, - &data)) return 0; - self->cxx->mStep(*kmeans_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - -/*** computeLikelihood ***/ -static auto compute_likelihood = bob::extension::FunctionDoc( - "compute_likelihood", - "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)", - 0, - true -) -.add_prototype("kmeans_machine") -.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object"); -static PyObject* PyBobLearnEMKMeansTrainer_compute_likelihood(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = compute_likelihood.kwlist(0); - - PyBobLearnEMKMeansMachineObject* kmeans_machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine)) return 0; - - double value = self->cxx->computeLikelihood(*kmeans_machine->cxx); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0) -} - - -/*** reset_accumulators ***/ -static auto reset_accumulators = bob::extension::FunctionDoc( - "reset_accumulators", - "Reset the statistics accumulators to the correct size and a value of zero.", - 0, - true -) -.add_prototype("kmeans_machine") -.add_parameter("kmeans_machine", ":py:class:`bob.learn.em.KMeansMachine`", "KMeansMachine Object"); -static PyObject* PyBobLearnEMKMeansTrainer_reset_accumulators(PyBobLearnEMKMeansTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = reset_accumulators.kwlist(0); - - PyBobLearnEMKMeansMachineObject* kmeans_machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMKMeansMachine_Type, &kmeans_machine)) return 0; - - self->cxx->resetAccumulators(*kmeans_machine->cxx); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("cannot perform the reset_accumulators method", 0) -} - - -static PyMethodDef PyBobLearnEMKMeansTrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMKMeansTrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMKMeansTrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMKMeansTrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - compute_likelihood.name(), - (PyCFunction)PyBobLearnEMKMeansTrainer_compute_likelihood, - METH_VARARGS|METH_KEYWORDS, - compute_likelihood.doc() - }, - { - reset_accumulators.name(), - (PyCFunction)PyBobLearnEMKMeansTrainer_reset_accumulators, - METH_VARARGS|METH_KEYWORDS, - reset_accumulators.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMKMeansTrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMKMeansTrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMKMeansTrainer_Type.tp_name = KMeansTrainer_doc.name(); - PyBobLearnEMKMeansTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMKMeansTrainerObject); - PyBobLearnEMKMeansTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance - PyBobLearnEMKMeansTrainer_Type.tp_doc = KMeansTrainer_doc.doc(); - - // set the functions - PyBobLearnEMKMeansTrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMKMeansTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMKMeansTrainer_init); - PyBobLearnEMKMeansTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMKMeansTrainer_delete); - PyBobLearnEMKMeansTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMKMeansTrainer_RichCompare); - PyBobLearnEMKMeansTrainer_Type.tp_methods = PyBobLearnEMKMeansTrainer_methods; - PyBobLearnEMKMeansTrainer_Type.tp_getset = PyBobLearnEMKMeansTrainer_getseters; - PyBobLearnEMKMeansTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMKMeansTrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMKMeansTrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMKMeansTrainer_Type); - return PyModule_AddObject(module, "KMeansTrainer", (PyObject*)&PyBobLearnEMKMeansTrainer_Type) >= 0; -} diff --git a/bob/learn/em/linear_scoring.cpp b/bob/learn/em/linear_scoring.cpp deleted file mode 100644 index f29b59370b12ef35635c78ec6a267dec756f8ac9..0000000000000000000000000000000000000000 --- a/bob/learn/em/linear_scoring.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Wed 05 Feb 16:10:48 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/*Convert a PyObject to a a list of GMMStats*/ -//template<class R, class P1, class P2> -static int extract_gmmstats_list(PyObject *list, - std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++){ - - PyBobLearnEMGMMStatsObject* stats; - if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMStats_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMStats objects"); - return -1; - } - training_data.push_back(stats->cxx); - } - return 0; -} - -static int extract_gmmmachine_list(PyObject *list, - std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> >& training_data) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++){ - - PyBobLearnEMGMMMachineObject* stats; - if (!PyArg_Parse(PyList_GetItem(list, i), "O!", &PyBobLearnEMGMMMachine_Type, &stats)){ - PyErr_Format(PyExc_RuntimeError, "Expected GMMMachine objects"); - return -1; - } - training_data.push_back(stats->cxx); - } - return 0; -} - - - -/*Convert a PyObject to a list of blitz Array*/ -template <int N> -int extract_array_list(PyObject* list, std::vector<blitz::Array<double,N> >& vec) -{ - - if(list==0) - return 0; - - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyBlitzArrayObject* blitz_object; - if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){ - PyErr_Format(PyExc_RuntimeError, "Expected numpy array object"); - return -1; - } - auto blitz_object_ = make_safe(blitz_object); - vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object)); - } - return 0; -} - -/* converts PyObject to bool and returns false if object is NULL */ -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} - - -/*** linear_scoring ***/ -bob::extension::FunctionDoc linear_scoring1 = bob::extension::FunctionDoc( - "linear_scoring", - "The :ref:`Linear scoring <linearscoring>` is an approximation to the log-likelihood ratio that was shown to be as accurate and up to two orders of magnitude more efficient to compute [Glembek2009]_." - "", - 0, - true -) -.add_prototype("models, ubm, test_stats, test_channelOffset, frame_length_normalisation", "output") -.add_parameter("models", "[:py:class:`bob.learn.em.GMMMachine`]", "") -.add_parameter("ubm", ":py:class:`bob.learn.em.GMMMachine`", "") -.add_parameter("test_stats", "[:py:class:`bob.learn.em.GMMStats`]", "") -.add_parameter("test_channelOffset", "[array_like<float,1>]", "") -.add_parameter("frame_length_normalisation", "bool", "") -.add_return("output","array_like<float,1>","Score"); - - -bob::extension::FunctionDoc linear_scoring2 = bob::extension::FunctionDoc( - "linear_scoring", - "", - 0, - true -) -.add_prototype("models, ubm_mean, ubm_variance, test_stats, test_channelOffset, frame_length_normalisation", "output") -.add_parameter("models", "list(array_like<float,1>)", "") -.add_parameter("ubm_mean", "list(array_like<float,1>)", "") -.add_parameter("ubm_variance", "list(array_like<float,1>)", "") -.add_parameter("test_stats", "list(:py:class:`bob.learn.em.GMMStats`)", "") -.add_parameter("test_channelOffset", "list(array_like<float,1>)", "") -.add_parameter("frame_length_normalisation", "bool", "") -.add_return("output","array_like<float,1>","Score"); - - - -bob::extension::FunctionDoc linear_scoring3 = bob::extension::FunctionDoc( - "linear_scoring", - "", - 0, - true -) -.add_prototype("model, ubm_mean, ubm_variance, test_stats, test_channelOffset, frame_length_normalisation", "output") -.add_parameter("model", "array_like<float,1>", "") -.add_parameter("ubm_mean", "array_like<float,1>", "") -.add_parameter("ubm_variance", "array_like<float,1>", "") -.add_parameter("test_stats", ":py:class:`bob.learn.em.GMMStats`", "") -.add_parameter("test_channelOffset", "array_like<float,1>", "") -.add_parameter("frame_length_normalisation", "bool", "") -.add_return("output","array_like<float,1>","Score"); - -PyObject* PyBobLearnEM_linear_scoring(PyObject*, PyObject* args, PyObject* kwargs) { - - //Cheking the number of arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - //Reading the first input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - //Checking the signature of the method (list of GMMMachine as input) - if ((PyList_Check(arg)) && PyBobLearnEMGMMMachine_Check(PyList_GetItem(arg, 0)) && (nargs >= 3) && (nargs<=5) ){ - - char** kwlist = linear_scoring1.kwlist(0); - - PyObject* gmm_list_o = 0; - PyBobLearnEMGMMMachineObject* ubm = 0; - PyObject* stats_list_o = 0; - PyObject* channel_offset_list_o = 0; - PyObject* frame_length_normalisation = Py_False; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!O!|O!O!", kwlist, &PyList_Type, &gmm_list_o, - &PyBobLearnEMGMMMachine_Type, &ubm, - &PyList_Type, &stats_list_o, - &PyList_Type, &channel_offset_list_o, - &PyBool_Type, &frame_length_normalisation)){ - linear_scoring1.print_usage(); - return 0; - } - - std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> > stats_list; - if(extract_gmmstats_list(stats_list_o ,stats_list)!=0) - Py_RETURN_NONE; - - std::vector<boost::shared_ptr<const bob::learn::em::GMMMachine> > gmm_list; - if(extract_gmmmachine_list(gmm_list_o ,gmm_list)!=0) - Py_RETURN_NONE; - - std::vector<blitz::Array<double,1> > channel_offset_list; - if(extract_array_list(channel_offset_list_o ,channel_offset_list)!=0) - Py_RETURN_NONE; - - blitz::Array<double, 2> scores = blitz::Array<double, 2>(gmm_list.size(), stats_list.size()); - if(channel_offset_list.size()==0) - bob::learn::em::linearScoring(gmm_list, *ubm->cxx, stats_list, f(frame_length_normalisation),scores); - else - bob::learn::em::linearScoring(gmm_list, *ubm->cxx, stats_list, channel_offset_list, f(frame_length_normalisation),scores); - - return PyBlitzArrayCxx_AsConstNumpy(scores); - } - - //Checking the signature of the method (list of arrays as input - else if ((PyList_Check(arg)) && PyArray_Check(PyList_GetItem(arg, 0)) && (nargs >= 4) && (nargs<=6) ){ - - char** kwlist = linear_scoring2.kwlist(0); - - PyObject* model_supervector_list_o = 0; - PyBlitzArrayObject* ubm_means = 0; - PyBlitzArrayObject* ubm_variances = 0; - PyObject* stats_list_o = 0; - PyObject* channel_offset_list_o = 0; - PyObject* frame_length_normalisation = Py_False; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&O&O!|O!O!", kwlist, &PyList_Type, &model_supervector_list_o, - &PyBlitzArray_Converter, &ubm_means, - &PyBlitzArray_Converter, &ubm_variances, - &PyList_Type, &stats_list_o, - &PyList_Type, &channel_offset_list_o, - &PyBool_Type, &frame_length_normalisation)){ - linear_scoring2.print_usage(); - return 0; - } - - //protects acquired resources through this scope - auto ubm_means_ = make_safe(ubm_means); - auto ubm_variances_ = make_safe(ubm_variances); - - std::vector<blitz::Array<double,1> > model_supervector_list; - if(extract_array_list(model_supervector_list_o ,model_supervector_list)!=0) - Py_RETURN_NONE; - - std::vector<boost::shared_ptr<const bob::learn::em::GMMStats> > stats_list; - if(extract_gmmstats_list(stats_list_o ,stats_list)!=0) - Py_RETURN_NONE; - - std::vector<blitz::Array<double,1> > channel_offset_list; - if(extract_array_list(channel_offset_list_o ,channel_offset_list)!=0) - Py_RETURN_NONE; - - blitz::Array<double, 2> scores = blitz::Array<double, 2>(model_supervector_list.size(), stats_list.size()); - if(channel_offset_list.size()==0) - bob::learn::em::linearScoring(model_supervector_list, *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), stats_list, f(frame_length_normalisation),scores); - else - bob::learn::em::linearScoring(model_supervector_list, *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), stats_list, channel_offset_list, f(frame_length_normalisation),scores); - - return PyBlitzArrayCxx_AsConstNumpy(scores); - - } - - //Checking the signature of the method (list of arrays as input - else if (PyArray_Check(arg) && (nargs >= 5) && (nargs<=6) ){ - - char** kwlist = linear_scoring3.kwlist(0); - - PyBlitzArrayObject* model = 0; - PyBlitzArrayObject* ubm_means = 0; - PyBlitzArrayObject* ubm_variances = 0; - PyBobLearnEMGMMStatsObject* stats = 0; - PyBlitzArrayObject* channel_offset = 0; - PyObject* frame_length_normalisation = Py_False; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&O!O&|O!", kwlist, &PyBlitzArray_Converter, &model, - &PyBlitzArray_Converter, &ubm_means, - &PyBlitzArray_Converter, &ubm_variances, - &PyBobLearnEMGMMStats_Type, &stats, - &PyBlitzArray_Converter, &channel_offset, - &PyBool_Type, &frame_length_normalisation)){ - linear_scoring3.print_usage(); - return 0; - } - - //protects acquired resources through this scope - auto model_ = make_safe(model); - auto ubm_means_ = make_safe(ubm_means); - auto ubm_variances_ = make_safe(ubm_variances); - auto channel_offset_ = make_safe(channel_offset); - - double score = bob::learn::em::linearScoring(*PyBlitzArrayCxx_AsBlitz<double,1>(model), *PyBlitzArrayCxx_AsBlitz<double,1>(ubm_means),*PyBlitzArrayCxx_AsBlitz<double,1>(ubm_variances), *stats->cxx, *PyBlitzArrayCxx_AsBlitz<double,1>(channel_offset), f(frame_length_normalisation)); - - return Py_BuildValue("d",score); - } - - - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - linear_scoring requires 5 or 6 arguments, but you provided %d (see help)", nargs); - linear_scoring1.print_usage(); - linear_scoring2.print_usage(); - linear_scoring3.print_usage(); - return 0; - } - -} diff --git a/bob/learn/em/main.cpp b/bob/learn/em/main.cpp deleted file mode 100644 index c2e7211f47fb44321375a2ffd56640c29c31f97c..0000000000000000000000000000000000000000 --- a/bob/learn/em/main.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Fri Nov 21 12:39:21 CET 2014 - * - * @brief Bindings to bob::learn::em routines - */ - -#ifdef NO_IMPORT_ARRAY -#undef NO_IMPORT_ARRAY -#endif -#include "main.h" - -static PyMethodDef module_methods[] = { - { - linear_scoring1.name(), - (PyCFunction)PyBobLearnEM_linear_scoring, - METH_VARARGS|METH_KEYWORDS, - linear_scoring1.doc() - }, - - {0}//Sentinel -}; - - -PyDoc_STRVAR(module_docstr, "Bob EM based Machine Learning Routines"); - -int PyBobLearnEM_APIVersion = BOB_LEARN_EM_API_VERSION; - - -#if PY_VERSION_HEX >= 0x03000000 -static PyModuleDef module_definition = { - PyModuleDef_HEAD_INIT, - BOB_EXT_MODULE_NAME, - module_docstr, - -1, - module_methods, - 0, 0, 0, 0 -}; -#endif - -static PyObject* create_module (void) { - -# if PY_VERSION_HEX >= 0x03000000 - PyObject* module = PyModule_Create(&module_definition); - auto module_ = make_xsafe(module); - const char* ret = "O"; -# else - PyObject* module = Py_InitModule3(BOB_EXT_MODULE_NAME, module_methods, module_docstr); - const char* ret = "N"; -# endif - if (!module) return 0; - - if (!init_BobLearnEMGaussian(module)) return 0; - if (!init_BobLearnEMGMMStats(module)) return 0; - if (!init_BobLearnEMGMMMachine(module)) return 0; - if (!init_BobLearnEMKMeansMachine(module)) return 0; - if (!init_BobLearnEMKMeansTrainer(module)) return 0; - if (!init_BobLearnEMMLGMMTrainer(module)) return 0; - if (!init_BobLearnEMMAPGMMTrainer(module)) return 0; - - if (!init_BobLearnEMJFABase(module)) return 0; - if (!init_BobLearnEMJFAMachine(module)) return 0; - if (!init_BobLearnEMJFATrainer(module)) return 0; - - if (!init_BobLearnEMISVBase(module)) return 0; - if (!init_BobLearnEMISVMachine(module)) return 0; - if (!init_BobLearnEMISVTrainer(module)) return 0; - - if (!init_BobLearnEMIVectorMachine(module)) return 0; - if (!init_BobLearnEMIVectorTrainer(module)) return 0; - - if (!init_BobLearnEMPLDABase(module)) return 0; - if (!init_BobLearnEMPLDAMachine(module)) return 0; - if (!init_BobLearnEMPLDATrainer(module)) return 0; - - if (!init_BobLearnEMEMPCATrainer(module)) return 0; - - - static void* PyBobLearnEM_API[PyBobLearnEM_API_pointers]; - - /* exhaustive list of C APIs */ - - /************** - * Versioning * - **************/ - - PyBobLearnEM_API[PyBobLearnEM_APIVersion_NUM] = (void *)&PyBobLearnEM_APIVersion; - - -#if PY_VERSION_HEX >= 0x02070000 - - /* defines the PyCapsule */ - - PyObject* c_api_object = PyCapsule_New((void *)PyBobLearnEM_API, - BOB_EXT_MODULE_PREFIX "." BOB_EXT_MODULE_NAME "._C_API", 0); - -#else - - PyObject* c_api_object = PyCObject_FromVoidPtr((void *)PyBobLearnEM_API, 0); - -#endif - - if (!c_api_object) return 0; - - if (PyModule_AddObject(module, "_C_API", c_api_object) < 0) return 0; - - /* imports bob.learn.em's C-API dependencies */ - if (import_bob_blitz() < 0) return 0; - if (import_bob_core_logging() < 0) return 0; - if (import_bob_core_random() < 0) return 0; - if (import_bob_io_base() < 0) return 0; - if (import_bob_sp() < 0) return 0; - if (import_bob_learn_activation() < 0) return 0; - if (import_bob_learn_linear() < 0) return 0; - - return Py_BuildValue(ret, module); -} - -PyMODINIT_FUNC BOB_EXT_ENTRY_NAME (void) { -# if PY_VERSION_HEX >= 0x03000000 - return -# endif - create_module(); -} diff --git a/bob/learn/em/main.h b/bob/learn/em/main.h deleted file mode 100644 index 7d2cb43eca159beb3b16bc03430a5c8dc9039584..0000000000000000000000000000000000000000 --- a/bob/learn/em/main.h +++ /dev/null @@ -1,274 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Fri Nov 21 10:31:25 CET 2014 - * - * @brief Header file for bindings to bob::learn::em - */ - -#ifndef BOB_LEARN_EM_MAIN_H -#define BOB_LEARN_EM_MAIN_H - -#include <bob.blitz/cppapi.h> -#include <bob.blitz/cleanup.h> -#include <bob.core/api.h> -#include <bob.core/random_api.h> -#include <bob.io.base/api.h> -#include <bob.sp/api.h> -#include <bob.learn.activation/api.h> -#include <bob.learn.linear/api.h> - -#include <bob.extension/documentation.h> - -#define BOB_LEARN_EM_MODULE -#include <bob.learn.em/api.h> - -#include <bob.learn.em/Gaussian.h> -#include <bob.learn.em/GMMStats.h> -#include <bob.learn.em/GMMMachine.h> -#include <bob.learn.em/KMeansMachine.h> - -#include <bob.learn.em/KMeansTrainer.h> -//#include <bob.learn.em/GMMBaseTrainer.h> -#include <bob.learn.em/ML_GMMTrainer.h> -#include <bob.learn.em/MAP_GMMTrainer.h> - -#include <bob.learn.em/JFABase.h> -#include <bob.learn.em/JFAMachine.h> -#include <bob.learn.em/JFATrainer.h> - -#include <bob.learn.em/ISVBase.h> -#include <bob.learn.em/ISVMachine.h> -#include <bob.learn.em/ISVTrainer.h> - - -#include <bob.learn.em/IVectorMachine.h> -#include <bob.learn.em/IVectorTrainer.h> - -#include <bob.learn.em/EMPCATrainer.h> - -#include <bob.learn.em/PLDAMachine.h> -#include <bob.learn.em/PLDATrainer.h> - - -/// inserts the given key, value pair into the given dictionaries -static inline int insert_item_string(PyObject* dict, PyObject* entries, const char* key, Py_ssize_t value){ - auto v = make_safe(Py_BuildValue("n", value)); - if (PyDict_SetItemString(dict, key, v.get()) < 0) return -1; - return PyDict_SetItemString(entries, key, v.get()); -} - -// Gaussian -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::Gaussian> cxx; -} PyBobLearnEMGaussianObject; - -extern PyTypeObject PyBobLearnEMGaussian_Type; -bool init_BobLearnEMGaussian(PyObject* module); -int PyBobLearnEMGaussian_Check(PyObject* o); - - -// GMMStats -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::GMMStats> cxx; -} PyBobLearnEMGMMStatsObject; - -extern PyTypeObject PyBobLearnEMGMMStats_Type; -bool init_BobLearnEMGMMStats(PyObject* module); -int PyBobLearnEMGMMStats_Check(PyObject* o); - - -// GMMMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::GMMMachine> cxx; -} PyBobLearnEMGMMMachineObject; - -extern PyTypeObject PyBobLearnEMGMMMachine_Type; -bool init_BobLearnEMGMMMachine(PyObject* module); -int PyBobLearnEMGMMMachine_Check(PyObject* o); - - -// KMeansMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::KMeansMachine> cxx; -} PyBobLearnEMKMeansMachineObject; - -extern PyTypeObject PyBobLearnEMKMeansMachine_Type; -bool init_BobLearnEMKMeansMachine(PyObject* module); -int PyBobLearnEMKMeansMachine_Check(PyObject* o); - - -// KMeansTrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::KMeansTrainer> cxx; -} PyBobLearnEMKMeansTrainerObject; - -extern PyTypeObject PyBobLearnEMKMeansTrainer_Type; -bool init_BobLearnEMKMeansTrainer(PyObject* module); -int PyBobLearnEMKMeansTrainer_Check(PyObject* o); - - -// ML_GMMTrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::ML_GMMTrainer> cxx; -} PyBobLearnEMMLGMMTrainerObject; - -extern PyTypeObject PyBobLearnEMMLGMMTrainer_Type; -bool init_BobLearnEMMLGMMTrainer(PyObject* module); -int PyBobLearnEMMLGMMTrainer_Check(PyObject* o); - - -// MAP_GMMTrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::MAP_GMMTrainer> cxx; -} PyBobLearnEMMAPGMMTrainerObject; - -extern PyTypeObject PyBobLearnEMMAPGMMTrainer_Type; -bool init_BobLearnEMMAPGMMTrainer(PyObject* module); -int PyBobLearnEMMAPGMMTrainer_Check(PyObject* o); - - -// JFABase -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::JFABase> cxx; -} PyBobLearnEMJFABaseObject; - -extern PyTypeObject PyBobLearnEMJFABase_Type; -bool init_BobLearnEMJFABase(PyObject* module); -int PyBobLearnEMJFABase_Check(PyObject* o); - - -// ISVBase -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::ISVBase> cxx; -} PyBobLearnEMISVBaseObject; - -extern PyTypeObject PyBobLearnEMISVBase_Type; -bool init_BobLearnEMISVBase(PyObject* module); -int PyBobLearnEMISVBase_Check(PyObject* o); - - -// JFAMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::JFAMachine> cxx; -} PyBobLearnEMJFAMachineObject; - -extern PyTypeObject PyBobLearnEMJFAMachine_Type; -bool init_BobLearnEMJFAMachine(PyObject* module); -int PyBobLearnEMJFAMachine_Check(PyObject* o); - -// JFATrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::JFATrainer> cxx; -} PyBobLearnEMJFATrainerObject; - - -extern PyTypeObject PyBobLearnEMJFATrainer_Type; -bool init_BobLearnEMJFATrainer(PyObject* module); -int PyBobLearnEMJFATrainer_Check(PyObject* o); - -// ISVMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::ISVMachine> cxx; -} PyBobLearnEMISVMachineObject; - -extern PyTypeObject PyBobLearnEMISVMachine_Type; -bool init_BobLearnEMISVMachine(PyObject* module); -int PyBobLearnEMISVMachine_Check(PyObject* o); - -// ISVTrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::ISVTrainer> cxx; -} PyBobLearnEMISVTrainerObject; - -extern PyTypeObject PyBobLearnEMISVTrainer_Type; -bool init_BobLearnEMISVTrainer(PyObject* module); -int PyBobLearnEMISVTrainer_Check(PyObject* o); - -// IVectorMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::IVectorMachine> cxx; -} PyBobLearnEMIVectorMachineObject; - -extern PyTypeObject PyBobLearnEMIVectorMachine_Type; -bool init_BobLearnEMIVectorMachine(PyObject* module); -int PyBobLearnEMIVectorMachine_Check(PyObject* o); - - -// IVectorTrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::IVectorTrainer> cxx; -} PyBobLearnEMIVectorTrainerObject; - -extern PyTypeObject PyBobLearnEMIVectorTrainer_Type; -bool init_BobLearnEMIVectorTrainer(PyObject* module); -int PyBobLearnEMIVectorTrainer_Check(PyObject* o); - - -// PLDABase -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::PLDABase> cxx; -} PyBobLearnEMPLDABaseObject; - -extern PyTypeObject PyBobLearnEMPLDABase_Type; -bool init_BobLearnEMPLDABase(PyObject* module); -int PyBobLearnEMPLDABase_Check(PyObject* o); - - -// PLDAMachine -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::PLDAMachine> cxx; -} PyBobLearnEMPLDAMachineObject; - -extern PyTypeObject PyBobLearnEMPLDAMachine_Type; -bool init_BobLearnEMPLDAMachine(PyObject* module); -int PyBobLearnEMPLDAMachine_Check(PyObject* o); - - -// PLDATrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::PLDATrainer> cxx; -} PyBobLearnEMPLDATrainerObject; - -extern PyTypeObject PyBobLearnEMPLDATrainer_Type; -bool init_BobLearnEMPLDATrainer(PyObject* module); -int PyBobLearnEMPLDATrainer_Check(PyObject* o); - - - -// EMPCATrainer -typedef struct { - PyObject_HEAD - boost::shared_ptr<bob::learn::em::EMPCATrainer> cxx; -} PyBobLearnEMEMPCATrainerObject; - -extern PyTypeObject PyBobLearnEMEMPCATrainer_Type; -bool init_BobLearnEMEMPCATrainer(PyObject* module); -int PyBobLearnEMEMPCATrainer_Check(PyObject* o); - - - -//Linear scoring -PyObject* PyBobLearnEM_linear_scoring(PyObject*, PyObject* args, PyObject* kwargs); -extern bob::extension::FunctionDoc linear_scoring1; -extern bob::extension::FunctionDoc linear_scoring2; -extern bob::extension::FunctionDoc linear_scoring3; - -#endif // BOB_LEARN_EM_MAIN_H diff --git a/bob/learn/em/map_gmm_trainer.cpp b/bob/learn/em/map_gmm_trainer.cpp deleted file mode 100644 index 5616892a52878ff8077b458bb80ee46d820e84b9..0000000000000000000000000000000000000000 --- a/bob/learn/em/map_gmm_trainer.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Web 23 Jan 16:42:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} /* converts PyObject to bool and returns false if object is NULL */ - -static auto MAP_GMMTrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".MAP_GMMTrainer", - "This class implements the maximum a posteriori (:ref:`MAP <map>`) M-step of the expectation-maximization algorithm for a GMM Machine. The prior parameters are encoded in the form of a GMM (e.g. a universal background model). The EM algorithm thus performs GMM adaptation." -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a MAP_GMMTrainer", - "Additionally to the copy constructor, there are two different ways to call this constructor, one using the ``relevance_factor`` and one using the ``alpha``, both which have the same signature. " - "Hence, the only way to differentiate the two functions is by using keyword arguments.", - true - ) - - .add_prototype("prior_gmm, relevance_factor, [update_means], [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","") - .add_prototype("prior_gmm, alpha, [update_means], [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","") - .add_prototype("other","") - - .add_parameter("prior_gmm", ":py:class:`bob.learn.em.GMMMachine`", "The prior GMM to be adapted (Universal Background Model UBM).") - .add_parameter("relevance_factor", "float", "If set the Reynolds Adaptation procedure will be applied. See Eq (14) from [Reynolds2000]_") - .add_parameter("alpha", "float", "Set directly the alpha parameter (Eq (14) from [Reynolds2000]_), ignoring zeroth order statistics as a weighting factor.") - - .add_parameter("update_means", "bool", "[Default: ``True``] Update means on each iteration") - .add_parameter("update_variances", "bool", "[Default: ``True``] Update variances on each iteration") - .add_parameter("update_weights", "bool", "[Default: ``True``] Update weights on each iteration") - .add_parameter("mean_var_update_responsibilities_threshold", "float", "[Default: min_float] Threshold over the responsibilities of the Gaussians Equations 9.24, 9.25 of Bishop, `Pattern recognition and machine learning`, 2006 require a division by the responsibilities, which might be equal to zero because of numerical issue. This threshold is used to avoid such divisions.") - - .add_parameter("other", ":py:class:`bob.learn.em.MAP_GMMTrainer`", "A MAP_GMMTrainer object to be copied.") -); - - -static int PyBobLearnEMMAPGMMTrainer_init_copy(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = MAP_GMMTrainer_doc.kwlist(2); - PyBobLearnEMMAPGMMTrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMMAPGMMTrainer_Type, &o)){ - MAP_GMMTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::MAP_GMMTrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMMAPGMMTrainer_init_base_trainer(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist1 = MAP_GMMTrainer_doc.kwlist(0); - char** kwlist2 = MAP_GMMTrainer_doc.kwlist(1); - - PyBobLearnEMGMMMachineObject* gmm_machine; - bool reynolds_adaptation = false; - double alpha = 0.5; - double relevance_factor = 4.0; - double aux = 0; - - PyObject* update_means = Py_True; - PyObject* update_variances = Py_False; - PyObject* update_weights = Py_False; - double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon(); - - PyObject* keyword_relevance_factor = Py_BuildValue("s", kwlist1[1]); - PyObject* keyword_alpha = Py_BuildValue("s", kwlist2[1]); - - auto keyword_relevance_factor_ = make_safe(keyword_relevance_factor); - auto keyword_alpha_ = make_safe(keyword_alpha); - - //Here we have to select which keyword argument to read - if (kwargs && PyDict_Contains(kwargs, keyword_relevance_factor)){ - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!d|O!O!O!d", kwlist1, - &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &aux, - &PyBool_Type, &update_means, - &PyBool_Type, &update_variances, - &PyBool_Type, &update_weights, - &mean_var_update_responsibilities_threshold)) - return -1; - reynolds_adaptation = true; - } else if (kwargs && PyDict_Contains(kwargs, keyword_alpha)){ - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!d|O!O!O!d", kwlist2, - &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &aux, - &PyBool_Type, &update_means, - &PyBool_Type, &update_variances, - &PyBool_Type, &update_weights, - &mean_var_update_responsibilities_threshold)) - return -1; - reynolds_adaptation = false; - } else { - PyErr_Format(PyExc_RuntimeError, "%s. One of the two keyword arguments '%s' or '%s' must be present.", Py_TYPE(self)->tp_name, kwlist1[1], kwlist2[1]); - MAP_GMMTrainer_doc.print_usage(); - return -1; - } - - if (reynolds_adaptation) - relevance_factor = aux; - else - alpha = aux; - - - self->cxx.reset(new bob::learn::em::MAP_GMMTrainer(f(update_means), f(update_variances), f(update_weights), - mean_var_update_responsibilities_threshold, - reynolds_adaptation, relevance_factor, alpha, gmm_machine->cxx)); - return 0; - -} - - - -static int PyBobLearnEMMAPGMMTrainer_init(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if (nargs==0){ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s (see help)", Py_TYPE(self)->tp_name); - MAP_GMMTrainer_doc.print_usage(); - return -1; - } - - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is GMMBaseTrainer object - if(PyBobLearnEMMAPGMMTrainer_Check(arg)) - return PyBobLearnEMMAPGMMTrainer_init_copy(self, args, kwargs); - else{ - return PyBobLearnEMMAPGMMTrainer_init_base_trainer(self, args, kwargs); - } - - BOB_CATCH_MEMBER("cannot create MAP_GMMTrainer", -1) - return 0; -} - - -static void PyBobLearnEMMAPGMMTrainer_delete(PyBobLearnEMMAPGMMTrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMMAPGMMTrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMMAPGMMTrainer_Type)); -} - - -static PyObject* PyBobLearnEMMAPGMMTrainer_RichCompare(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMMAPGMMTrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMMAPGMMTrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare MAP_GMMTrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** relevance_factor *****/ -static auto relevance_factor = bob::extension::VariableDoc( - "relevance_factor", - "float", - "If set the reynolds_adaptation parameters, will apply the Reynolds Adaptation Factor. See Eq (14) from [Reynolds2000]_", - "" -); -PyObject* PyBobLearnEMMAPGMMTrainer_getRelevanceFactor(PyBobLearnEMMAPGMMTrainerObject* self, void*){ - BOB_TRY - return Py_BuildValue("d",self->cxx->getRelevanceFactor()); - BOB_CATCH_MEMBER("relevance_factor could not be read", 0) -} -int PyBobLearnEMMAPGMMTrainer_setRelevanceFactor(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){ - BOB_TRY - - if(!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, relevance_factor.name()); - return -1; - } - - self->cxx->setRelevanceFactor(PyFloat_AS_DOUBLE(value)); - return 0; - BOB_CATCH_MEMBER("relevance_factor could not be set", -1) -} - - -/***** alpha *****/ -static auto alpha = bob::extension::VariableDoc( - "alpha", - "float", - "Set directly the alpha parameter (Eq (14) from [Reynolds2000]_), ignoring zeroth order statistics as a weighting factor.", - "" -); -PyObject* PyBobLearnEMMAPGMMTrainer_getAlpha(PyBobLearnEMMAPGMMTrainerObject* self, void*){ - BOB_TRY - return Py_BuildValue("d",self->cxx->getAlpha()); - BOB_CATCH_MEMBER("alpha could not be read", 0) -} -int PyBobLearnEMMAPGMMTrainer_setAlpha(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){ - BOB_TRY - - if(!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, alpha.name()); - return -1; - } - - self->cxx->setAlpha(PyFloat_AS_DOUBLE(value)); - return 0; - BOB_CATCH_MEMBER("alpha could not be set", -1) -} - - -static auto gmm_statistics = bob::extension::VariableDoc( - "gmm_statistics", - ":py:class:`GMMStats`", - "The GMM statistics that were used internally in the E- and M-steps", - "Setting and getting the internal GMM statistics might be useful to parallelize the GMM training." -); -PyObject* PyBobLearnEMMAPGMMTrainer_get_gmm_statistics(PyBobLearnEMMAPGMMTrainerObject* self, void*){ - BOB_TRY - PyBobLearnEMGMMStatsObject* stats = (PyBobLearnEMGMMStatsObject*)PyBobLearnEMGMMStats_Type.tp_alloc(&PyBobLearnEMGMMStats_Type, 0); - stats->cxx = self->cxx->base_trainer().getGMMStats(); - return Py_BuildValue("N", stats); - BOB_CATCH_MEMBER("gmm_statistics could not be read", 0) -} -int PyBobLearnEMMAPGMMTrainer_set_gmm_statistics(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* value, void*){ - BOB_TRY - if (!PyBobLearnEMGMMStats_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a GMMStats object", Py_TYPE(self)->tp_name, gmm_statistics.name()); - return -1; - } - self->cxx->base_trainer().setGMMStats(reinterpret_cast<PyBobLearnEMGMMStatsObject*>(value)->cxx); - return 0; - BOB_CATCH_MEMBER("gmm_statistics could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMMAPGMMTrainer_getseters[] = { - { - alpha.name(), - (getter)PyBobLearnEMMAPGMMTrainer_getAlpha, - (setter)PyBobLearnEMMAPGMMTrainer_setAlpha, - alpha.doc(), - 0 - }, - { - relevance_factor.name(), - (getter)PyBobLearnEMMAPGMMTrainer_getRelevanceFactor, - (setter)PyBobLearnEMMAPGMMTrainer_setRelevanceFactor, - relevance_factor.doc(), - 0 - }, - { - gmm_statistics.name(), - (getter)PyBobLearnEMMAPGMMTrainer_get_gmm_statistics, - (setter)PyBobLearnEMMAPGMMTrainer_set_gmm_statistics, - gmm_statistics.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("gmm_machine, [data], [rng]") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "object", "Ignored.") -.add_parameter("rng", "object", "Ignored."); -static PyObject* PyBobLearnEMMAPGMMTrainer_initialize(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine = 0; - PyObject* data; - PyObject* rng; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &data, &rng)) return 0; - - self->cxx->initialize(*gmm_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Calculates and saves statistics across the dataset and saves these as :py:attr:`gmm_statistics`. ", - - "Calculates the average log likelihood of the observations given the GMM," - "and returns this in average_log_likelihood." - "The statistics, :py:attr:`gmm_statistics`, will be used in the :py:meth:`m_step` that follows.", - - true -) -.add_prototype("gmm_machine,data") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMMAPGMMTrainer_e_step(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - PyBlitzArrayObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &PyBlitzArray_Converter, &data)) return 0; - auto data_ = make_safe(data); - - - // perform check on the input - if (data->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->shape[1] != (Py_ssize_t)gmm_machine->cxx->getNInputs() ) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, gmm_machine->cxx->getNInputs(), data->shape[1], e_step.name()); - return 0; - } - - auto state = PyEval_SaveThread(); - self->cxx->eStep(*gmm_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - PyEval_RestoreThread(state); - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - - "Performs a maximum a posteriori (MAP) update of the GMM parameters using the accumulated statistics in :py:attr:`gmm_statistics` and the parameters of the prior model", - "", - true -) -.add_prototype("gmm_machine, [data]") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "object", "Ignored."); -static PyObject* PyBobLearnEMMAPGMMTrainer_m_step(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - PyObject* data; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &data)) return 0; - - self->cxx->mStep(*gmm_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - -/*** computeLikelihood ***/ -static auto compute_likelihood = bob::extension::FunctionDoc( - "compute_likelihood", - "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)", - 0, - true -) -.add_prototype("gmm_machine") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object"); -static PyObject* PyBobLearnEMMAPGMMTrainer_compute_likelihood(PyBobLearnEMMAPGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = compute_likelihood.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine)) return 0; - - double value = self->cxx->computeLikelihood(*gmm_machine->cxx); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0) -} - - - -static PyMethodDef PyBobLearnEMMAPGMMTrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMMAPGMMTrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMMAPGMMTrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMMAPGMMTrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - compute_likelihood.name(), - (PyCFunction)PyBobLearnEMMAPGMMTrainer_compute_likelihood, - METH_VARARGS|METH_KEYWORDS, - compute_likelihood.doc() - }, - - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMMAPGMMTrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMMAPGMMTrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMMAPGMMTrainer_Type.tp_name = MAP_GMMTrainer_doc.name(); - PyBobLearnEMMAPGMMTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMMAPGMMTrainerObject); - PyBobLearnEMMAPGMMTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance - PyBobLearnEMMAPGMMTrainer_Type.tp_doc = MAP_GMMTrainer_doc.doc(); - - // set the functions - PyBobLearnEMMAPGMMTrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMMAPGMMTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMMAPGMMTrainer_init); - PyBobLearnEMMAPGMMTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMMAPGMMTrainer_delete); - PyBobLearnEMMAPGMMTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMMAPGMMTrainer_RichCompare); - PyBobLearnEMMAPGMMTrainer_Type.tp_methods = PyBobLearnEMMAPGMMTrainer_methods; - PyBobLearnEMMAPGMMTrainer_Type.tp_getset = PyBobLearnEMMAPGMMTrainer_getseters; - PyBobLearnEMMAPGMMTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMMAPGMMTrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMMAPGMMTrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMMAPGMMTrainer_Type); - return PyModule_AddObject(module, "MAP_GMMTrainer", (PyObject*)&PyBobLearnEMMAPGMMTrainer_Type) >= 0; -} diff --git a/bob/learn/em/mixture/__init__.py b/bob/learn/em/mixture/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..229f13e224bae240869b1965f580bccf591fc073 --- /dev/null +++ b/bob/learn/em/mixture/__init__.py @@ -0,0 +1,3 @@ +from .gmm import GMMMachine +from .gmm import GMMStats +from .linear_scoring import linear_scoring diff --git a/bob/learn/em/mixture/gmm.py b/bob/learn/em/mixture/gmm.py new file mode 100644 index 0000000000000000000000000000000000000000..0be5a1772bc19184cd8534312f8901192f96c575 --- /dev/null +++ b/bob/learn/em/mixture/gmm.py @@ -0,0 +1,905 @@ +#!/usr/bin/env python +# @author: Yannick Dayer <yannick.dayer@idiap.ch> +# @date: Fri 30 Jul 2021 10:06:47 UTC+02 + +"""This module provides classes and functions for the training and usage of GMM.""" + +import logging +import copy +from typing import Union + +import dask.array as da +import numpy as np +from sklearn.base import BaseEstimator + +from bob.learn.em.cluster import KMeansMachine + +from h5py import File as HDF5File + +logger = logging.getLogger(__name__) + +EPSILON = np.finfo(float).eps + + +class GMMStats: + """Stores accumulated statistics of a GMM. + + Attributes + ---------- + log_likelihood: float + The sum of log_likelihood of each sample on a GMM. + t: int + The number of considered samples. + n: array of shape (n_gaussians,) + Sum of responsibility. + sum_px: array of shape (n_gaussians, n_features) + First order statistic + sum_pxx: array of shape (n_gaussians, n_features) + Second order statistic + """ + + def __init__(self, n_gaussians: int, n_features: int) -> None: + self.n_gaussians = n_gaussians + self.n_features = n_features + self.log_likelihood = 0 + self.t = 0 + self.n = np.zeros(shape=(self.n_gaussians,), dtype=float) + self.sum_px = np.zeros(shape=(self.n_gaussians, self.n_features), dtype=float) + self.sum_pxx = np.zeros(shape=(self.n_gaussians, self.n_features), dtype=float) + + def init_fields(self, log_likelihood=0.0, t=0, n=None, sum_px=None, sum_pxx=None): + """Initializes the statistics values to a defined value, or zero by default.""" + # The accumulated log likelihood of all samples + self.log_likelihood = log_likelihood + # The accumulated number of samples + self.t = t + # For each Gaussian, the accumulated sum of responsibilities, i.e. the sum of + # P(gaussian_i|x) + self.n = np.zeros(shape=(self.n_gaussians,), dtype=float) if n is None else n + # For each Gaussian, the accumulated sum of responsibility times the sample + self.sum_px = ( + np.zeros(shape=(self.n_gaussians, self.n_features), dtype=float) + if sum_px is None + else sum_px + ) + # For each Gaussian, the accumulated sum of responsibility times the sample + # squared + self.sum_pxx = ( + np.zeros(shape=(self.n_gaussians, self.n_features), dtype=float) + if sum_pxx is None + else sum_pxx + ) + + def reset(self): + """Sets all statistics to zero.""" + self.init_fields() + + @classmethod + def from_hdf5(cls, hdf5): + """Creates a new GMMStats object from an `HDF5File` object.""" + if isinstance(hdf5, str): + hdf5 = HDF5File(hdf5, "r") + try: + version_major, version_minor = hdf5.get("meta_file_version")[()].split(".") + logger.debug( + f"Reading a GMMStats HDF5 file of version {version_major}.{version_minor}" + ) + except TypeError: + version_major, version_minor = 0, 0 + if int(version_major) >= 1: + if hdf5["meta_writer_class"][()] != str(cls): + logger.warning(f"{hdf5['meta_writer_class'][()]} is not {cls}.") + self = cls( + n_gaussians=hdf5["n_gaussians"][()], n_features=hdf5["n_features"][()] + ) + self.log_likelihood = hdf5["log_likelihood"][()] + self.t = hdf5["T"][()] + self.n = hdf5["n"][()] + self.sum_px = hdf5["sumPx"][()] + self.sum_pxx = hdf5["sumPxx"][()] + else: # Legacy file version + logger.info("Loading a legacy HDF5 stats file.") + self = cls( + n_gaussians=int(hdf5["n_gaussians"][()]), + n_features=int(hdf5["n_inputs"][()]), + ) + self.log_likelihood = float(hdf5["log_liklihood"][()]) + self.t = int(hdf5["T"][()]) + self.n = hdf5["n"][()].reshape((self.n_gaussians,)) + self.sum_px = hdf5["sumPx"][()].reshape(self.shape) + self.sum_pxx = hdf5["sumPxx"][()].reshape(self.shape) + return self + + def save(self, hdf5): + """Saves the current statistsics in an `HDF5File` object.""" + if isinstance(hdf5, str): + hdf5 = HDF5File(hdf5, "w") + hdf5["meta_file_version"] = "1.0" + hdf5["meta_writer_class"] = str(self.__class__) + hdf5["n_gaussians"] = self.n_gaussians + hdf5["n_features"] = self.n_features + hdf5["log_likelihood"] = float(self.log_likelihood) + hdf5["T"] = int(self.t) + hdf5["n"] = np.array(self.n) + hdf5["sumPx"] = np.array(self.sum_px) + hdf5["sumPxx"] = np.array(self.sum_pxx) + + def load(self, hdf5): + """Overwrites the current statistics with those in an `HDF5File` object.""" + new_self = self.from_hdf5(hdf5) + if new_self.shape != self.shape: + logger.warning("Loaded GMMStats from hdf5 with a different shape.") + self.resize(*new_self.shape) + self.init_fields( + new_self.log_likelihood, + new_self.t, + new_self.n, + new_self.sum_px, + new_self.sum_pxx, + ) + + def __add__(self, other): + if self.n_gaussians != other.n_gaussians or self.n_features != other.n_features: + raise ValueError("Statistics could not be added together (shape mismatch)") + new_stats = GMMStats(self.n_gaussians, self.n_features) + new_stats.log_likelihood = self.log_likelihood + other.log_likelihood + new_stats.t = self.t + other.t + new_stats.n = self.n + other.n + new_stats.sum_px = self.sum_px + other.sum_px + new_stats.sum_pxx = self.sum_pxx + other.sum_pxx + return new_stats + + def __iadd__(self, other): + if self.n_gaussians != other.n_gaussians or self.n_features != other.n_features: + raise ValueError("Statistics could not be added together (shape mismatch)") + self.log_likelihood += other.log_likelihood + self.t += other.t + self.n += other.n + self.sum_px += other.sum_px + self.sum_pxx += other.sum_pxx + return self + + def __eq__(self, other): + return ( + self.log_likelihood == other.log_likelihood + and self.t == other.t + and np.array_equal(self.n, other.n) + and np.array_equal(self.sum_px, other.sum_px) + and np.array_equal(self.sum_pxx, other.sum_pxx) + ) + + def is_similar_to(self, other, rtol=1e-5, atol=1e-8): + """Returns True if `other` has the same values (within a tolerance).""" + return ( + np.isclose(self.log_likelihood, other.log_likelihood, rtol=rtol, atol=atol) + and np.isclose(self.t, other.t, rtol=rtol, atol=atol) + and np.allclose(self.n, other.n, rtol=rtol, atol=atol) + and np.allclose(self.sum_px, other.sum_px, rtol=rtol, atol=atol) + and np.allclose(self.sum_pxx, other.sum_pxx, rtol=rtol, atol=atol) + ) + + def resize(self, n_gaussians, n_features): + """Reinitializes the machine with new dimensions.""" + self.n_gaussians = n_gaussians + self.n_features = n_features + self.init_fields() + + @property + def shape(self): + """The number of gaussians and their dimensionality.""" + return (self.n_gaussians, self.n_features) + + def compute(self): + for name in ("log_likelihood", "n", "sum_px", "sum_pxx"): + setattr(self, name, np.asarray(getattr(self, name))) + + +class GMMMachine(BaseEstimator): + """Transformer that stores a Gaussian Mixture Model (GMM) parameters. + + This class implements the statistical model for multivariate diagonal mixture + Gaussian distribution (GMM), as well as ways to train a model on data. + + A GMM is defined as + :math:`\\sum_{c=0}^{C} \\omega_c \\mathcal{N}(x | \\mu_c, \\sigma_c)`, where + :math:`C` is the number of Gaussian components :math:`\\mu_c`, :math:`\\sigma_c` + and :math:`\\omega_c` are respectively the the mean, variance and the weight of + each gaussian component :math:`c`. + See Section 2.3.9 of Bishop, \"Pattern recognition and machine learning\", 2006 + + Two types of training are available MLE and MAP, chosen with `trainer`. + + Maximum Likelihood Estimation (:ref:`MLE <mle>`, ML) + --------------------------------------- + The mixtures are initialized (with k-means by default). + The means, variances, and weights of the mixtures are then trained on the data to + increase the likelihood value. (:ref:`MLE <mle>`) + + Maximum a Posteriori (:ref:`MAP <map>`) + -------------------------- + The MAP machine takes another GMM machine as prior, called Universal Background + Model (UBM). + The means, variances, and weights of the MAP mixtures are then trained on the data + as adaptation of the UBM. + + Both training method use a Expectation-Maximization (e-m) algorithm to iteratively + train the GMM. + + Note + ---- + When setting manually any of the means, variances or variance thresholds, the + k-means initialization will be skipped in `fit`. + + Usage + ----- + Maximum likelihood: + >>> data = np.array([[0,0,0],[1,1,1]]) + >>> ml_machine = GMMMachine(n_gaussians=2, trainer="ml") + >>> ml_machine = ml_machine.fit(data) + >>> print(ml_machine.means) + [[1. 1. 1.] + [0. 0. 0.]] + + Maximum a Posteriori: + >>> post_data = np.array([[0.5, 0.5, 0],[1.5, 1, 1.5]]) + >>> map_machine = GMMMachine(n_gaussians=2, trainer="map", ubm=ml_machine) + >>> map_machine = map_machine.fit(post_data) + >>> print(map_machine.means) + [[1.1 1. 1.1] + [0.1 0.1 0. ]] + + Partial fitting: + >>> machine = GMMMachine(n_gaussians=2, trainer="ml") + >>> for step in range(5): + ... machine = machine.fit_partial(data) + >>> print(machine.means) + [[1. 1. 1.] + [0. 0. 0.]] + + Attributes + ---------- + means, variances, variance_thresholds + Gaussians parameters. + weights + Gaussians weights. + """ + + def __init__( + self, + n_gaussians: int, + trainer: str = "ml", + ubm: "Union[GMMMachine, None]" = None, + convergence_threshold: float = 1e-5, + max_fitting_steps: Union[int, None] = 200, + random_state: Union[int, np.random.RandomState] = 0, + weights: "Union[np.ndarray[('n_gaussians',), float], None]" = None, + k_means_trainer: Union[KMeansMachine, None] = None, + update_means: bool = True, + update_variances: bool = False, + update_weights: bool = False, + mean_var_update_threshold: float = EPSILON, + alpha: float = 0.5, + relevance_factor: Union[None, float] = 4, + variance_thresholds: float = EPSILON, + ): + """ + Parameters + ---------- + n_gaussians + The number of gaussians to be represented by the machine. + trainer + `"ml"` for the maximum likelihood estimator method; + `"map"` for the maximum a posteriori method. (MAP Requires `ubm`) + ubm: GMMMachine + Universal Background Model. GMMMachine Required for the MAP method. + convergence_threshold + The threshold value of likelihood difference between e-m steps used for + stopping the training iterations. + max_fitting_steps + The number of e-m iterations to fit the GMM. Stop the training even when + the convergence threshold isn't met. + random_state + Specifies a RandomState or a seed for reproducibility. + weights + The weight of each Gaussian. (defaults to `1/n_gaussians`) + k_means_trainer + Optional trainer for the k-means method, replacing the default one. + update_means + Update the Gaussians means at every m step. + update_variances + Update the Gaussians variances at every m step. + update_weights + Update the GMM weights at every m step. + mean_var_update_threshold: + Threshold value used when updating the means and variances. + alpha: + Ratio for MAP adaptation. Used when `trainer == "map"` and + `relevance_factor is None`) + relevance_factor: + Factor for the computation of alpha with Reynolds adaptation. (Used when + `trainer == "map"`) + variance_thresholds: + The variance flooring thresholds, i.e. the minimum allowed value of variance in each dimension. + The variance will be set to this value if an attempt is made to set it to a smaller value. + """ + + self.n_gaussians = n_gaussians + self.trainer = trainer if trainer in ["ml", "map"] else "ml" + self.m_step_func = map_gmm_m_step if self.trainer == "map" else ml_gmm_m_step + if self.trainer == "map" and ubm is None: + raise ValueError("A UBM is required for MAP GMM.") + if ubm is not None and ubm.n_gaussians != self.n_gaussians: + raise ValueError("The UBM machine is not compatible with this machine.") + self.ubm = ubm + if max_fitting_steps is None and convergence_threshold is None: + raise ValueError( + "Either or both convergence_threshold and max_fitting_steps must be set" + ) + self.convergence_threshold = convergence_threshold + self.max_fitting_steps = max_fitting_steps + self.random_state = random_state + self.k_means_trainer = k_means_trainer + self.update_means = update_means + self.update_variances = update_variances + self.update_weights = update_weights + self.mean_var_update_threshold = mean_var_update_threshold + self._means = None + self._variances = None + self._variance_thresholds = mean_var_update_threshold + self._g_norms = None + + if self.ubm is not None: + self.means = copy.deepcopy(self.ubm.means) + self.variances = copy.deepcopy(self.ubm.variances) + self.variance_thresholds = copy.deepcopy(self.ubm.variance_thresholds) + self.weights = copy.deepcopy(self.ubm.weights) + else: + self.weights = np.full( + (self.n_gaussians,), fill_value=(1 / self.n_gaussians), dtype=float + ) + if weights is not None: + self.weights = weights + self.alpha = alpha + self.relevance_factor = relevance_factor + + @property + def weights(self): + """The weights of each Gaussian mixture.""" + return self._weights + + @weights.setter + def weights(self, weights: "np.ndarray[('n_gaussians',), float]"): + self._weights = weights + self._log_weights = np.log(self._weights) + + @property + def means(self): + """The means of each Gaussian.""" + if self._means is None: + raise ValueError("GMMMachine means were never set.") + return self._means + + @means.setter + def means(self, means: "np.ndarray[('n_gaussians', 'n_features'), float]"): + self._means = means + + @property + def variances(self): + """The (diagonal) variances of the gaussians.""" + if self._variances is None: + raise ValueError("GMMMachine variances were never set.") + return self._variances + + @variances.setter + def variances(self, variances: "np.ndarray[('n_gaussians', 'n_features'), float]"): + self._variances = np.maximum(self.variance_thresholds, variances) + # Recompute g_norm for each gaussian [array of shape (n_gaussians,)] + n_log_2pi = self._variances.shape[-1] * np.log(2 * np.pi) + self._g_norms = np.array(n_log_2pi + np.log(self._variances).sum(axis=-1)) + + @property + def variance_thresholds(self): + """Threshold below which variances are clamped to prevent precision losses.""" + if self._variance_thresholds is None: + return EPSILON + return self._variance_thresholds + + @variance_thresholds.setter + def variance_thresholds( + self, + threshold: "Union[float, np.ndarray[('n_gaussians', 'n_features'), float]]", + ): + self._variance_thresholds = threshold + if self._variances is not None: + self.variances = np.maximum(threshold, self._variances) + + @property + def g_norms(self): + """Precomputed g_norms (depends on variances and feature shape).""" + if self._g_norms is None: + # Recompute g_norm for each gaussian [array of shape (n_gaussians,)] + n_log_2pi = self.variances.shape[-1] * np.log(2 * np.pi) + self._g_norms = n_log_2pi + np.log(self._variances).sum(axis=-1) + return self._g_norms + + @property + def log_weights(self): + """Retrieve the logarithm of the weights.""" + return self._log_weights + + @property + def shape(self): + """Shape of the gaussians in the GMM machine.""" + return self.means.shape + + @classmethod + def from_hdf5(cls, hdf5, ubm=None): + """Creates a new GMMMachine object from an `HDF5File` object.""" + if isinstance(hdf5, str): + hdf5 = HDF5File(hdf5, "r") + try: + version_major, version_minor = hdf5.get("meta_file_version")[()].split(".") + logger.debug( + f"Reading a GMMMachine HDF5 file of version {version_major}.{version_minor}" + ) + except (TypeError, RuntimeError): + version_major, version_minor = 0, 0 + if int(version_major) >= 1: + if hdf5["meta_writer_class"][()] != str(cls): + logger.warning(f"{hdf5['meta_writer_class'][()]} is not {cls}.") + if hdf5["trainer"][()] == "map" and ubm is None: + raise ValueError("The UBM is needed when loading a MAP machine.") + self = cls( + n_gaussians=hdf5["n_gaussians"][()], + trainer=hdf5["trainer"][()], + ubm=ubm, + convergence_threshold=1e-5, + max_fitting_steps=hdf5["max_fitting_steps"][()], + weights=hdf5["weights"][()], + k_means_trainer=None, + update_means=hdf5["update_means"][()], + update_variances=hdf5["update_variances"][()], + update_weights=hdf5["update_weights"][()], + ) + gaussians_group = hdf5["gaussians"] + self.means = gaussians_group["means"][()] + self.variances = gaussians_group["variances"][()] + self.variance_thresholds = gaussians_group["variance_thresholds"][()] + else: # Legacy file version + logger.info("Loading a legacy HDF5 machine file.") + n_gaussians = int(hdf5["m_n_gaussians"][()]) + g_means = [] + g_variances = [] + g_variance_thresholds = [] + for i in range(n_gaussians): + gaussian_group = hdf5[f"m_gaussians{i}"] + g_means.append(gaussian_group["m_mean"][()]) + g_variances.append(gaussian_group["m_variance"][()]) + g_variance_thresholds.append( + gaussian_group["m_variance_thresholds"][()] + ) + weights = hdf5["m_weights"][()].reshape(n_gaussians) + self = cls(n_gaussians=n_gaussians, ubm=ubm, weights=weights) + self.means = np.array(g_means).reshape(n_gaussians, -1) + self.variances = np.array(g_variances).reshape(n_gaussians, -1) + self.variance_thresholds = np.array(g_variance_thresholds).reshape( + n_gaussians, -1 + ) + return self + + def save(self, hdf5): + """Saves the current statistics in an `HDF5File` object.""" + if isinstance(hdf5, str): + hdf5 = HDF5File(hdf5, "w") + hdf5["meta_file_version"] = "1.0" + hdf5["meta_writer_class"] = str(self.__class__) + hdf5["n_gaussians"] = self.n_gaussians + hdf5["trainer"] = self.trainer + hdf5["convergence_threshold"] = self.convergence_threshold + hdf5["max_fitting_steps"] = self.max_fitting_steps + hdf5["weights"] = self.weights + hdf5["update_means"] = self.update_means + hdf5["update_variances"] = self.update_variances + hdf5["update_weights"] = self.update_weights + gaussians_group = hdf5.create_group("gaussians") + gaussians_group["means"] = self.means + gaussians_group["variances"] = self.variances + gaussians_group["variance_thresholds"] = self.variance_thresholds + + def __eq__(self, other): + return ( + np.array_equal(self.means, other.means) + and np.array_equal(self.variances, other.variances) + and np.array_equal(self.variance_thresholds, other.variance_thresholds) + and np.array_equal(self.weights, other.weights) + ) + + def is_similar_to(self, other, rtol=1e-5, atol=1e-8): + """Returns True if `other` has the same gaussians (within a tolerance).""" + return ( + np.allclose(self.means, other.means, rtol=rtol, atol=atol) + and np.allclose(self.variances, other.variances, rtol=rtol, atol=atol) + and np.allclose( + self.variance_thresholds, + other.variance_thresholds, + rtol=rtol, + atol=atol, + ) + and np.allclose(self.weights, other.weights, rtol=rtol, atol=atol) + ) + + def initialize_gaussians( + self, data: "Union[np.ndarray[('n_samples', 'n_features'), float], None]" = None + ): + """Populates gaussians parameters with either k-means or the UBM values.""" + if self.trainer == "map": + self.means = copy.deepcopy(self.ubm.means) + self.variances = copy.deepcopy(self.ubm.variances) + self.variance_thresholds = copy.deepcopy(self.ubm.variance_thresholds) + self.weights = copy.deepcopy(self.ubm.weights) + else: + logger.debug("GMM means was never set. Initializing with k-means.") + if data is None: + raise ValueError("Data is required when training with k-means.") + logger.info("Initializing GMM with k-means.") + kmeans_machine = self.k_means_trainer or KMeansMachine( + self.n_gaussians, + random_state=self.random_state, + ) + kmeans_machine = kmeans_machine.fit(data) + + ( + variances, + weights, + ) = kmeans_machine.get_variances_and_weights_for_each_cluster(data) + + # Set the GMM machine's gaussians with the results of k-means + self.means = np.array(copy.deepcopy(kmeans_machine.centroids_)) + self.variances = np.array(copy.deepcopy(variances)) + self.weights = np.array(copy.deepcopy(weights)) + + def log_weighted_likelihood( + self, data: "np.ndarray[('n_samples', 'n_features'), float]" + ): + """Returns the weighted log likelihood for each Gaussian for a set of data. + + Parameters + ---------- + data + Data to compute the log likelihood on. + + Returns + ------- + array of shape (n_gaussians, n_samples) + The weighted log likelihood of each sample of each Gaussian. + """ + # Compute the likelihood for each data point on each Gaussian + z = ( + (data[None, ..., :] - self.means[..., None, :]) ** 2 + / self.variances[..., None, :] + ).sum(axis=-1) + l = -0.5 * (self.g_norms[:, None] + z) + log_weighted_likelihood = self.log_weights[:, None] + l + return log_weighted_likelihood + + def log_likelihood(self, data: "np.ndarray[('n_samples', 'n_features'), float]"): + """Returns the current log likelihood for a set of data in this Machine. + + Parameters + ---------- + data + Data to compute the log likelihood on. + + Returns + ------- + array of shape (n_samples) + The log likelihood of each sample. + """ + if data.ndim == 1: + data = data.reshape((1, -1)) + + # All likelihoods [array of shape (n_gaussians, n_samples)] + log_weighted_likelihood = self.log_weighted_likelihood(data) + + def logaddexp_reduce(array, axis=0, keepdims=False): + return np.logaddexp.reduce( + array, axis=axis, keepdims=keepdims, initial=-np.inf + ) + + if isinstance(log_weighted_likelihood, np.ndarray): + ll_reduced = logaddexp_reduce(log_weighted_likelihood) + else: + # Sum along gaussians axis (using logAddExp to prevent underflow) + ll_reduced = da.reduction( + x=log_weighted_likelihood, + chunk=logaddexp_reduce, + aggregate=logaddexp_reduce, + axis=0, + dtype=float, + keepdims=False, + ) + return ll_reduced + + # Likelihoods of each sample on this machine. [array of shape (n_samples,)] + + def acc_statistics( + self, + data: "np.ndarray[('n_samples', 'n_features'), float]", + statistics: Union[GMMStats, None] = None, + ): + """Accumulates the statistics of GMMStats for a set of data. + + This can be used to compute a GMM step in parallel: each worker/thread applies + the e-step of a copy of the same GMM on part of the training data, and the + resulting `GMMStats` object of each worker is summed before applying the m-step. + + Parameters + ---------- + data: + The data to extract the statistics on the GMM. + statistics: + A GMMStats object that will accumulate the previous and current stats. + Values are modified in-place AND returned. (or only returned if + `statistics` is None) + """ + # Ensure data is a series of samples (2D array) + if data.ndim == 1: + data = data.reshape(shape=(1, -1)) + + # Allow the absence of previous statistics + if statistics is None: + statistics = GMMStats(self.n_gaussians, data.shape[-1]) + + # Log weighted Gaussian likelihoods [array of shape (n_gaussians,n_samples)] + log_weighted_likelihoods = self.log_weighted_likelihood(data) + # Log likelihood [array of shape (n_samples,)] + log_likelihood = self.log_likelihood(data) + # Responsibility P [array of shape (n_gaussians, n_samples)] + responsibility = np.exp(log_weighted_likelihoods - log_likelihood[None, :]) + + # Accumulate + + # Total likelihood [float] + statistics.log_likelihood += log_likelihood.sum() + # Count of samples [int] + statistics.t += data.shape[0] + # Responsibilities [array of shape (n_gaussians,)] + statistics.n = statistics.n + responsibility.sum(axis=-1) + # p * x [array of shape (n_gaussians, n_samples, n_features)] + px = np.multiply(responsibility[:, :, None], data[None, :, :]) + # First order stats [array of shape (n_gaussians, n_features)] + statistics.sum_px = statistics.sum_px + px.sum(axis=1) + # Second order stats [array of shape (n_gaussians, n_features)] + pxx = np.multiply(px[:, :, :], data[None, :, :]) + statistics.sum_pxx = statistics.sum_pxx + pxx.sum(axis=1) + + return statistics + + def e_step(self, data: "np.ndarray[('n_samples', 'n_features'), float]"): + """Expectation step of the e-m algorithm.""" + return self.acc_statistics(data) + + def m_step( + self, + stats: GMMStats, + **kwargs, + ): + """Maximization step of the e-m algorithm.""" + self.m_step_func( + self, + statistics=stats, + update_means=self.update_means, + update_variances=self.update_variances, + update_weights=self.update_weights, + mean_var_update_threshold=self.mean_var_update_threshold, + reynolds_adaptation=self.relevance_factor is not None, + alpha=self.alpha, + relevance_factor=self.relevance_factor, + **kwargs, + ) + + def fit(self, X, y=None, **kwargs): + """Trains the GMM on data until convergence or maximum step is reached.""" + if self._means is None: + self.initialize_gaussians(X) + else: + logger.debug("GMM means already set. Initialization was not run!") + + if self._variances is None: + logger.warning("Variances were not defined before fit. Using variance=1") + self.variances = np.ones_like(self.means) + + average_output = 0 + logger.info("Training GMM...") + step = 0 + while self.max_fitting_steps is None or step < self.max_fitting_steps: + step += 1 + logger.info( + f"Iteration {step:3d}" + + (f"/{self.max_fitting_steps:3d}" if self.max_fitting_steps else "") + ) + + average_output_previous = average_output + stats = self.e_step(X) + self.m_step( + stats=stats, + ) + + # if we're running in dask, persist weights, means, and variances so + # we don't recompute each step. + for attr in ["weights", "means", "variances"]: + arr = getattr(self, attr) + if isinstance(arr, da.Array): + setattr(self, attr, arr.persist()) + + # Note: Uses the stats from before m_step, leading to an additional m_step + # (which is not bad because it will always converge) + average_output = float(stats.log_likelihood / stats.t) + logger.debug(f"log likelihood = {average_output}") + + if step > 1: + convergence_value = abs( + (average_output_previous - average_output) / average_output_previous + ) + logger.debug(f"convergence val = {convergence_value}") + + # Terminates if converged (and likelihood computation is set) + if ( + self.convergence_threshold is not None + and convergence_value <= self.convergence_threshold + ): + logger.info("Reached convergence threshold. Training stopped.") + break + else: + logger.info("Reached maximum step. Training stopped without convergence.") + self.compute() + return self + + def fit_partial(self, X, y=None, **kwargs): + """Applies one iteration of GMM training.""" + if self._means is None: + self.initialize_gaussians(X) + + stats = self.e_step(X) + self.m_step(stats=stats) + return self + + def transform(self, X, **kwargs): + """Returns the statistics for `X`.""" + return self.e_step(X) + + def _more_tags(self): + return { + "stateless": False, + "requires_fit": True, + } + + def compute(self, *args, **kwargs): + for name in ("weights", "means", "variances"): + setattr(self, name, np.asarray(getattr(self, name))) + + +def ml_gmm_m_step( + machine: GMMMachine, + statistics: GMMStats, + update_means=True, + update_variances=False, + update_weights=False, + mean_var_update_threshold=EPSILON, + **kwargs, +): + """Updates a gmm machine parameter according to the e-step statistics.""" + logger.debug("ML GMM Trainer m-step") + + # Update weights if requested + # (Equation 9.26 of Bishop, "Pattern recognition and machine learning", 2006) + if update_weights: + logger.debug("Update weights.") + machine.weights = statistics.n / statistics.t + + # Threshold the low n to prevent divide by zero + thresholded_n = np.clip(statistics.n, mean_var_update_threshold, None) + + # Update GMM parameters using the sufficient statistics (m_ss): + + # Update means if requested + # (Equation 9.24 of Bishop, "Pattern recognition and machine learning", 2006) + if update_means: + logger.debug("Update means.") + # Using n with the applied threshold + machine.means = statistics.sum_px / thresholded_n[:, None] + + # Update variances if requested + # (Equation 9.25 of Bishop, "Pattern recognition and machine learning", 2006) + # ...but we use the "computational formula for the variance", i.e. + # var = 1/n * sum (P(x-mean)(x-mean)) + # = 1/n * sum (Pxx) - mean^2 + if update_variances: + logger.debug("Update variances.") + machine.variances = statistics.sum_pxx / thresholded_n[:, None] - np.power( + machine.means, 2 + ) + + +def map_gmm_m_step( + machine: GMMMachine, + statistics: GMMStats, + update_means=True, + update_variances=False, + update_weights=False, + reynolds_adaptation=True, + relevance_factor=4, + alpha=0.5, + mean_var_update_threshold=EPSILON, +): + """Updates a GMMMachine parameters using statistics adapted from a UBM.""" + if machine.ubm is None: + raise ValueError("A machine used for MAP must have a UBM.") + # Calculate the "data-dependent adaptation coefficient", alpha_i + # [array of shape (n_gaussians, )] + if reynolds_adaptation: + alpha = statistics.n / (statistics.n + relevance_factor) + else: + if not hasattr(alpha, "ndim"): + alpha = np.full((machine.n_gaussians,), alpha) + + # - Update weights if requested + # Equation 11 of Reynolds et al., "Speaker Verification Using Adapted + # Gaussian Mixture Models", Digital Signal Processing, 2000 + if update_weights: + # Calculate the maximum likelihood weights [array of shape (n_gaussians,)] + ml_weights = statistics.n / statistics.t + + # Calculate the new weights + machine.weights = alpha * ml_weights + (1 - alpha) * machine.ubm.weights + + # Apply the scale factor, gamma, to ensure the new weights sum to unity + gamma = machine.weights.sum() + machine.weights /= gamma + + # Update GMM parameters + # - Update means if requested + # Equation 12 of Reynolds et al., "Speaker Verification Using Adapted + # Gaussian Mixture Models", Digital Signal Processing, 2000 + if update_means: + # Apply threshold to prevent divide by zero below + n_threshold = np.where( + statistics.n < mean_var_update_threshold, + mean_var_update_threshold, + statistics.n, + ) + # n_threshold = np.full(statistics.n.shape, fill_value=mean_var_update_threshold) + # n_threshold[statistics.n > mean_var_update_threshold] = statistics.n[ + # statistics.n > mean_var_update_threshold + # ] + new_means = ( + np.multiply( + alpha[:, None], + (statistics.sum_px / n_threshold[:, None]), + ) + + np.multiply((1 - alpha[:, None]), machine.ubm.means) + ) + machine.means = np.where( + statistics.n[:, None] < mean_var_update_threshold, + machine.ubm.means, + new_means, + ) + + # - Update variance if requested + # Equation 13 of Reynolds et al., "Speaker Verification Using Adapted + # Gaussian Mixture Models", Digital Signal Processing, 2000 + if update_variances: + # Calculate new variances (equation 13) + prior_norm_variances = (machine.ubm.variances + machine.ubm.means) - np.power( + machine.means, 2 + ) + new_variances = ( + alpha[:, None] * statistics.sum_pxx / statistics.n[:, None] + + (1 - alpha[:, None]) * (machine.ubm.variances + machine.ubm.means) + - np.power(machine.means, 2) + ) + machine.variances = np.where( + statistics.n[:, None] < mean_var_update_threshold, + prior_norm_variances, + new_variances, + ) diff --git a/bob/learn/em/mixture/linear_scoring.py b/bob/learn/em/mixture/linear_scoring.py new file mode 100644 index 0000000000000000000000000000000000000000..d7aac713791d95e07d4dbf2ae29a6f3afa56eb3a --- /dev/null +++ b/bob/learn/em/mixture/linear_scoring.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# @author: Yannick Dayer <yannick.dayer@idiap.ch> +# @date: Thu 21 Oct 2021 14:14:07 UTC+02 + +"""Implements the linear scoring function.""" + +import logging +from typing import Union + +import numpy as np + +from bob.learn.em.mixture import GMMMachine +from bob.learn.em.mixture import GMMStats + +logger = logging.getLogger(__name__) + +EPSILON = np.finfo(float).eps + + +def linear_scoring( + models_means: "Union[list[GMMMachine], np.ndarray[('n_models', 'n_gaussians', 'n_features'), float]]", + ubm: GMMMachine, + test_stats: Union["list[GMMStats]", GMMStats], + test_channel_offsets: "np.ndarray[('n_test_stats', 'n_gaussians'), float]" = 0, + frame_length_normalization: bool = False, +) -> "np.ndarray[('n_models', 'n_test_stats'), float]": + """Estimation of the LLR between a target model and the UBM for a test instance. + + The Linear scoring is an approximation to the log-likelihood ratio (LLR) that was + shown to be as accurate and up to two orders of magnitude more efficient to + compute. [Glembek2009] + + Parameters + ---------- + models_means + The model(s) to score against. If a list of `GMMMachine` is given, the means + of each model are considered. + ubm: + The Universal Background Model. Accepts a `GMMMachine` object. If the + `GMMMachine` uses MAP, it's `ubm` attribute is used. + test_stats: + The instances to score. + test_channel_offsets + Offset values added to the test instances. + + Returns + ------- + Array of shape (n_models, n_probes) + The scores of each probe against each model. + """ + if isinstance(models_means[0], GMMMachine): + models_means = np.array([model.means for model in models_means]) + if not hasattr(models_means, "ndim"): + models_means = np.array(models_means) + if models_means.ndim < 2: + raise ValueError( + "models_means must be of shape `(n_models, n_gaussians, n_features)`." + ) + if models_means.ndim == 2: + models_means = models_means[None, :, :] + + if ubm.trainer == "map": + ubm = ubm.ubm + + if isinstance(test_stats, GMMStats): + test_stats = [test_stats] + + # All stats.sum_px [array of shape (n_test_stats, n_gaussians, n_features)] + sum_px = np.array([stat.sum_px for stat in test_stats]) + # All stats.n [array of shape (n_test_stats, n_gaussians)] + n = np.array([stat.n for stat in test_stats]) + # All stats.t [array of shape (n_test_stats,)] + t = np.array([stat.t for stat in test_stats]) + # Offsets [array of shape (n_test_stats, `n_gaussians * n_features`)] + test_channel_offsets = np.array(test_channel_offsets) + + # Compute A [array of shape (n_models, n_gaussians * n_features)] + a = (models_means - ubm.means) / ubm.variances + # Compute B [array of shape (n_gaussians * n_features, n_test_stats)] + b = sum_px[:, :, :] - ( + n[:, :, None] * (ubm.means[None, :, :] + test_channel_offsets) + ) + b = np.transpose(b, axes=(1, 2, 0)) + # Apply normalization if needed. + if frame_length_normalization: + b = np.where(abs(t) <= EPSILON, 0, b[:, :] / t[None, :]) + return np.tensordot(a, b, 2) diff --git a/bob/learn/em/ml_gmm_trainer.cpp b/bob/learn/em/ml_gmm_trainer.cpp deleted file mode 100644 index 7e7dc488fd7f11903dc32fea1d04030bd5d7c6b7..0000000000000000000000000000000000000000 --- a/bob/learn/em/ml_gmm_trainer.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Web 22 Jan 16:45:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} /* converts PyObject to bool and returns false if object is NULL */ - -static auto ML_GMMTrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".ML_GMMTrainer", - "This class implements the maximum likelihood M-step (:ref:`MLE <mle>`) of the expectation-maximisation algorithm for a GMM Machine." -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Creates a ML_GMMTrainer", - "", - true - ) - .add_prototype("update_means, [update_variances], [update_weights], [mean_var_update_responsibilities_threshold]","") - .add_prototype("other","") - - .add_parameter("update_means", "bool", "Update means on each iteration") - .add_parameter("update_variances", "bool", "Update variances on each iteration") - .add_parameter("update_weights", "bool", "Update weights on each iteration") - .add_parameter("mean_var_update_responsibilities_threshold", "float", "Threshold over the responsibilities of the Gaussians Equations 9.24, 9.25 of Bishop, `Pattern recognition and machine learning`, 2006 require a division by the responsibilities, which might be equal to zero because of numerical issue. This threshold is used to avoid such divisions.") - - - .add_parameter("other", ":py:class:`bob.learn.em.ML_GMMTrainer`", "A ML_GMMTrainer object to be copied.") -); - - -static int PyBobLearnEMMLGMMTrainer_init_copy(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ML_GMMTrainer_doc.kwlist(1); - PyBobLearnEMMLGMMTrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMMLGMMTrainer_Type, &o)){ - ML_GMMTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ML_GMMTrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMMLGMMTrainer_init_base_trainer(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = ML_GMMTrainer_doc.kwlist(0); - - PyObject* update_means = Py_True; - PyObject* update_variances = Py_False; - PyObject* update_weights = Py_False; - double mean_var_update_responsibilities_threshold = std::numeric_limits<double>::epsilon(); - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!O!O!d", kwlist, - &PyBool_Type, &update_means, - &PyBool_Type, &update_variances, - &PyBool_Type, &update_weights, - &mean_var_update_responsibilities_threshold)){ - ML_GMMTrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::ML_GMMTrainer(f(update_means), f(update_variances), f(update_weights), - mean_var_update_responsibilities_threshold)); - return 0; -} - - - -static int PyBobLearnEMMLGMMTrainer_init(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if (nargs==0) - return PyBobLearnEMMLGMMTrainer_init_base_trainer(self, args, kwargs); - else{ - - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is GMMBaseTrainer object - if (PyBobLearnEMMLGMMTrainer_Check(arg)) - return PyBobLearnEMMLGMMTrainer_init_copy(self, args, kwargs); - else - return PyBobLearnEMMLGMMTrainer_init_base_trainer(self, args, kwargs); - } - BOB_CATCH_MEMBER("cannot create GMMBaseTrainer_init_bool", -1) - return 0; -} - - -static void PyBobLearnEMMLGMMTrainer_delete(PyBobLearnEMMLGMMTrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMMLGMMTrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMMLGMMTrainer_Type)); -} - - -static PyObject* PyBobLearnEMMLGMMTrainer_RichCompare(PyBobLearnEMMLGMMTrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMMLGMMTrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMMLGMMTrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare ML_GMMTrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -static auto gmm_statistics = bob::extension::VariableDoc( - "gmm_statistics", - ":py:class:`GMMStats`", - "The GMM statistics that were used internally in the E- and M-steps", - "Setting and getting the internal GMM statistics might be useful to parallelize the GMM training." -); -PyObject* PyBobLearnEMMLGMMTrainer_get_gmm_statistics(PyBobLearnEMMLGMMTrainerObject* self, void*){ - BOB_TRY - PyBobLearnEMGMMStatsObject* stats = (PyBobLearnEMGMMStatsObject*)PyBobLearnEMGMMStats_Type.tp_alloc(&PyBobLearnEMGMMStats_Type, 0); - stats->cxx = self->cxx->base_trainer().getGMMStats(); - return Py_BuildValue("N", stats); - BOB_CATCH_MEMBER("gmm_statistics could not be read", 0) -} -int PyBobLearnEMMLGMMTrainer_set_gmm_statistics(PyBobLearnEMMLGMMTrainerObject* self, PyObject* value, void*){ - BOB_TRY - if (!PyBobLearnEMGMMStats_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a GMMStats object", Py_TYPE(self)->tp_name, gmm_statistics.name()); - return -1; - } - self->cxx->base_trainer().setGMMStats(reinterpret_cast<PyBobLearnEMGMMStatsObject*>(value)->cxx); - return 0; - BOB_CATCH_MEMBER("gmm_statistics could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMMLGMMTrainer_getseters[] = { - { - gmm_statistics.name(), - (getter)PyBobLearnEMMLGMMTrainer_get_gmm_statistics, - (setter)PyBobLearnEMMLGMMTrainer_set_gmm_statistics, - gmm_statistics.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("gmm_machine, [data], [rng]") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "object", "Ignored.") -.add_parameter("rng", "object", "Ignored."); -static PyObject* PyBobLearnEMMLGMMTrainer_initialize(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - PyBobLearnEMGMMMachineObject* gmm_machine = 0; - PyObject* data; - PyObject* rng; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &data, &rng)) return 0; - - self->cxx->initialize(*gmm_machine->cxx); - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Calculates and saves statistics across the dataset," - "and saves these as m_ss. ", - - "Calculates the average log likelihood of the observations given the GMM," - "and returns this in average_log_likelihood." - "The statistics, :py:attr:`gmm_statistics`, will be used in the :py:func:`m_step` that follows.", - - true -) -.add_prototype("gmm_machine,data") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMMLGMMTrainer_e_step(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - PyBlitzArrayObject* data = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &PyBlitzArray_Converter, &data)) return 0; - auto data_ = make_safe(data); - - // perform check on the input - if (data->type_num != NPY_FLOAT64){ - PyErr_Format(PyExc_TypeError, "`%s' only supports 64-bit float arrays for input array `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->ndim != 2){ - PyErr_Format(PyExc_TypeError, "`%s' only processes 2D arrays of float64 for `%s`", Py_TYPE(self)->tp_name, e_step.name()); - return 0; - } - - if (data->shape[1] != (Py_ssize_t)gmm_machine->cxx->getNInputs() ) { - PyErr_Format(PyExc_TypeError, "`%s' 2D `input` array should have the shape [N, %" PY_FORMAT_SIZE_T "d] not [N, %" PY_FORMAT_SIZE_T "d] for `%s`", Py_TYPE(self)->tp_name, gmm_machine->cxx->getNInputs(), data->shape[1], e_step.name()); - return 0; - } - - auto state = PyEval_SaveThread(); - self->cxx->eStep(*gmm_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - PyEval_RestoreThread(state); - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "Performs a maximum likelihood (ML) update of the GMM parameters " - "using the accumulated statistics in :py:attr:`gmm_statistics`", - - "See Section 9.2.2 of Bishop, \"Pattern recognition and machine learning\", 2006", - - true -) -.add_prototype("gmm_machine, [data]") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object") -.add_parameter("data", "object", "Ignored."); -static PyObject* PyBobLearnEMMLGMMTrainer_m_step(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine = 0; - PyObject* data; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine, - &data)) return 0; - - self->cxx->mStep(*gmm_machine->cxx); - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - -/*** computeLikelihood ***/ -static auto compute_likelihood = bob::extension::FunctionDoc( - "compute_likelihood", - "This functions returns the average min (Square Euclidean) distance (average distance to the closest mean)", - 0, - true -) -.add_prototype("gmm_machine") -.add_parameter("gmm_machine", ":py:class:`bob.learn.em.GMMMachine`", "GMMMachine Object"); -static PyObject* PyBobLearnEMMLGMMTrainer_compute_likelihood(PyBobLearnEMMLGMMTrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = compute_likelihood.kwlist(0); - - PyBobLearnEMGMMMachineObject* gmm_machine; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMGMMMachine_Type, &gmm_machine)) return 0; - - double value = self->cxx->computeLikelihood(*gmm_machine->cxx); - return Py_BuildValue("d", value); - - BOB_CATCH_MEMBER("cannot perform the computeLikelihood method", 0) -} - - - -static PyMethodDef PyBobLearnEMMLGMMTrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMMLGMMTrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMMLGMMTrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMMLGMMTrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - compute_likelihood.name(), - (PyCFunction)PyBobLearnEMMLGMMTrainer_compute_likelihood, - METH_VARARGS|METH_KEYWORDS, - compute_likelihood.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMMLGMMTrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMMLGMMTrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMMLGMMTrainer_Type.tp_name = ML_GMMTrainer_doc.name(); - PyBobLearnEMMLGMMTrainer_Type.tp_basicsize = sizeof(PyBobLearnEMMLGMMTrainerObject); - PyBobLearnEMMLGMMTrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance - PyBobLearnEMMLGMMTrainer_Type.tp_doc = ML_GMMTrainer_doc.doc(); - - // set the functions - PyBobLearnEMMLGMMTrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMMLGMMTrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMMLGMMTrainer_init); - PyBobLearnEMMLGMMTrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMMLGMMTrainer_delete); - PyBobLearnEMMLGMMTrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMMLGMMTrainer_RichCompare); - PyBobLearnEMMLGMMTrainer_Type.tp_methods = PyBobLearnEMMLGMMTrainer_methods; - PyBobLearnEMMLGMMTrainer_Type.tp_getset = PyBobLearnEMMLGMMTrainer_getseters; - PyBobLearnEMMLGMMTrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMMLGMMTrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMMLGMMTrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMMLGMMTrainer_Type); - return PyModule_AddObject(module, "ML_GMMTrainer", (PyObject*)&PyBobLearnEMMLGMMTrainer_Type) >= 0; -} diff --git a/bob/learn/em/plda_base.cpp b/bob/learn/em/plda_base.cpp deleted file mode 100644 index 839dc9c233ab87d0e187c2493ad8d89f8d490474..0000000000000000000000000000000000000000 --- a/bob/learn/em/plda_base.cpp +++ /dev/null @@ -1,1097 +0,0 @@ -/** - * @date Thu Jan 29 15:44:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static auto PLDABase_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".PLDABase", - - "This class is a container for the :math:`F` (between class variantion matrix), :math:`G` (within class variantion matrix) and :math:`\\Sigma` " - "matrices and the mean vector :math:`\\mu` of a PLDA model. This also" - "precomputes useful matrices to make the model scalable." - "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - - "Constructor, builds a new PLDABase. :math:`F`, :math:`G` " - "and :math:`\\Sigma` are initialized to the 'eye' matrix (matrix with 1's " - "on the diagonal and 0 outside), and :math:`\\mu` is initialized to 0.", - - "", - true - ) - .add_prototype("dim_d,dim_f,dim_g,variance_threshold","") - .add_prototype("other","") - .add_prototype("hdf5","") - - .add_parameter("dim_d", "int", "Dimensionality of the feature vector.") - .add_parameter("dim_f", "int", "Size of :math:`F` (between class variantion matrix).") - .add_parameter("dim_g", "int", "Size of :math:`G` (within class variantion matrix).") - .add_parameter("variance_threshold", "float", "The smallest possible value of the variance (Ignored if set to 0.)") - - .add_parameter("other", ":py:class:`bob.learn.em.PLDABase`", "A PLDABase object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMPLDABase_init_copy(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDABase_doc.kwlist(1); - PyBobLearnEMPLDABaseObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDABase_Type, &o)){ - PLDABase_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::PLDABase(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMPLDABase_init_hdf5(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDABase_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ - PLDABase_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::PLDABase(*(config->f))); - - return 0; -} - - -static int PyBobLearnEMPLDABase_init_dim(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDABase_doc.kwlist(0); - - int dim_D, dim_F, dim_G = 1; - double variance_threshold = 0.0; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii|d", kwlist, &dim_D, &dim_F, &dim_G, &variance_threshold)){ - PLDABase_doc.print_usage(); - return -1; - } - - if(dim_D <= 0){ - PyErr_Format(PyExc_TypeError, "dim_D argument must be greater than or equal to one"); - return -1; - } - - if(dim_F <= 0){ - PyErr_Format(PyExc_TypeError, "dim_F argument must be greater than or equal to one"); - return -1; - } - - if(dim_G <= 0){ - PyErr_Format(PyExc_TypeError, "dim_G argument must be greater than or equal to one"); - return -1; - } - - if(variance_threshold < 0){ - PyErr_Format(PyExc_TypeError, "variance_threshold argument must be greater than or equal to zero"); - return -1; - } - - - self->cxx.reset(new bob::learn::em::PLDABase(dim_D, dim_F, dim_G, variance_threshold)); - return 0; -} - -static int PyBobLearnEMPLDABase_init(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs==1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMPLDABase_Check(arg)) - return PyBobLearnEMPLDABase_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobIoHDF5File_Check(arg)) - return PyBobLearnEMPLDABase_init_hdf5(self, args, kwargs); - } - else if((nargs==3)||(nargs==4)) - return PyBobLearnEMPLDABase_init_dim(self, args, kwargs); - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1, 3 or 4 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - PLDABase_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create PLDABase", -1) - return 0; -} - - - -static void PyBobLearnEMPLDABase_delete(PyBobLearnEMPLDABaseObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMPLDABase_RichCompare(PyBobLearnEMPLDABaseObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMPLDABase_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMPLDABaseObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare PLDABase objects", 0) -} - -int PyBobLearnEMPLDABase_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDABase_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int)", - "A tuple that represents the dimensionality of the feature vector dim_d, the :math:`F` matrix and the :math:`G` matrix.", - "" -); -PyObject* PyBobLearnEMPLDABase_getShape(PyBobLearnEMPLDABaseObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i)", self->cxx->getDimD(), self->cxx->getDimF(), self->cxx->getDimG()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - - -/***** F *****/ -static auto F = bob::extension::VariableDoc( - "f", - "array_like <float, 2D>", - "Returns the :math:`F` matrix (between class variantion matrix)", - "" -); -PyObject* PyBobLearnEMPLDABase_getF(PyBobLearnEMPLDABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getF()); - BOB_CATCH_MEMBER("`f` could not be read", 0) -} -int PyBobLearnEMPLDABase_setF(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, F.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "f"); - if (!b) return -1; - self->cxx->setF(*b); - return 0; - BOB_CATCH_MEMBER("`f` vector could not be set", -1) -} - -/***** G *****/ -static auto G = bob::extension::VariableDoc( - "g", - "array_like <float, 2D>", - "Returns the :math:`G` matrix (between class variantion matrix)", - "" -); -PyObject* PyBobLearnEMPLDABase_getG(PyBobLearnEMPLDABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getG()); - BOB_CATCH_MEMBER("`g` could not be read", 0) -} -int PyBobLearnEMPLDABase_setG(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, G.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "g"); - if (!b) return -1; - self->cxx->setG(*b); - return 0; - BOB_CATCH_MEMBER("`g` vector could not be set", -1) -} - - -/***** mu *****/ -static auto mu = bob::extension::VariableDoc( - "mu", - "array_like <float, 1D>", - "Gets the :math:`\\mu` mean vector of the PLDA model", - "" -); -PyObject* PyBobLearnEMPLDABase_getMu(PyBobLearnEMPLDABaseObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getMu()); - BOB_CATCH_MEMBER("`mu` could not be read", 0) -} -int PyBobLearnEMPLDABase_setMu(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, mu.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "mu"); - if (!b) return -1; - self->cxx->setMu(*b); - return 0; - BOB_CATCH_MEMBER("`mu` vector could not be set", -1) -} - - -/***** __isigma__ *****/ -static auto __isigma__ = bob::extension::VariableDoc( - "__isigma__", - "array_like <float, 1D>", - "Gets the inverse vector/diagonal matrix of :math:`\\Sigma^{-1}`", - "" -); -static PyObject* PyBobLearnEMPLDABase_getISigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getISigma()); - BOB_CATCH_MEMBER("__isigma__ could not be read", 0) -} - - -/***** __alpha__ *****/ -static auto __alpha__ = bob::extension::VariableDoc( - "__alpha__", - "array_like <float, 2D>", - "Gets the \f$\alpha\f$ matrix." - ":math:`\\alpha = (Id + G^T \\Sigma^{-1} G)^{-1} = \\mathcal{G}`", - "" -); -static PyObject* PyBobLearnEMPLDABase_getAlpha(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAlpha()); - BOB_CATCH_MEMBER("__alpha__ could not be read", 0) -} - - -/***** __beta__ *****/ -static auto __beta__ = bob::extension::VariableDoc( - "__beta__", - "array_like <float, 2D>", - "Gets the :math:`\\beta` matrix " - ":math:`\\beta = (\\Sigma + G G^T)^{-1} = \\mathcal{S} = \\Sigma^{-1} - \\Sigma^{-1} G \\mathcal{G} G^{T} \\Sigma^{-1}`", - "" -); -static PyObject* PyBobLearnEMPLDABase_getBeta(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getBeta()); - BOB_CATCH_MEMBER("__beta__ could not be read", 0) -} - - -/***** __ft_beta__ *****/ -static auto __ft_beta__ = bob::extension::VariableDoc( - "__ft_beta__", - "array_like <float, 2D>", - "Gets the :math:`F^T \\beta' matrix", - "" -); -static PyObject* PyBobLearnEMPLDABase_getFtBeta(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getFtBeta()); - BOB_CATCH_MEMBER("__ft_beta__ could not be read", 0) -} - - -/***** __gt_i_sigma__ *****/ -static auto __gt_i_sigma__ = bob::extension::VariableDoc( - "__gt_i_sigma__", - "array_like <float, 2D>", - "Gets the :math:`G^T \\Sigma^{-1}` matrix", - "" -); -static PyObject* PyBobLearnEMPLDABase_getGtISigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGtISigma()); - BOB_CATCH_MEMBER("__gt_i_sigma__ could not be read", 0) -} - - -/***** __logdet_alpha__ *****/ -static auto __logdet_alpha__ = bob::extension::VariableDoc( - "__logdet_alpha__", - "float", - "Gets :math:`\\log(\\det(\\alpha))`", - "" -); -static PyObject* PyBobLearnEMPLDABase_getLogDetAlpha(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("d",self->cxx->getLogDetAlpha()); - BOB_CATCH_MEMBER("__logdet_alpha__ could not be read", 0) -} - -/***** __logdet_sigma__ *****/ -static auto __logdet_sigma__ = bob::extension::VariableDoc( - "__logdet_sigma__", - "float", - "Gets :math:`\\log(\\det(\\Sigma))`", - "" -); -static PyObject* PyBobLearnEMPLDABase_getLogDetSigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("d",self->cxx->getLogDetSigma()); - BOB_CATCH_MEMBER("__logdet_sigma__ could not be read", 0) -} - - -/***** variance_threshold *****/ -static auto variance_threshold = bob::extension::VariableDoc( - "variance_threshold", - "float", - "", - "" -); -static PyObject* PyBobLearnEMPLDABase_getVarianceThreshold(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("d",self->cxx->getVarianceThreshold()); - BOB_CATCH_MEMBER("variance_threshold could not be read", 0) -} -int PyBobLearnEMPLDABase_setVarianceThreshold(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an float", Py_TYPE(self)->tp_name, variance_threshold.name()); - return -1; - } - - self->cxx->setVarianceThreshold(PyFloat_AS_DOUBLE(value)); - BOB_CATCH_MEMBER("variance_threshold could not be set", -1) - return 0; -} - - - - -/***** sigma *****/ -static auto sigma = bob::extension::VariableDoc( - "sigma", - "array_like <float, 1D>", - "Gets the :math:`\\sigma` (diagonal) covariance matrix of the PLDA model", - "" -); -static PyObject* PyBobLearnEMPLDABase_getSigma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getSigma()); - BOB_CATCH_MEMBER("sigma could not be read", 0) -} -int PyBobLearnEMPLDABase_setSigma(PyBobLearnEMPLDABaseObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, sigma.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "sigma"); - if (!b) return -1; - self->cxx->setSigma(*b); - return 0; - BOB_CATCH_MEMBER("`sigma` vector could not be set", -1) -} - - -static PyGetSetDef PyBobLearnEMPLDABase_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMPLDABase_getShape, - 0, - shape.doc(), - 0 - }, - { - F.name(), - (getter)PyBobLearnEMPLDABase_getF, - (setter)PyBobLearnEMPLDABase_setF, - F.doc(), - 0 - }, - { - G.name(), - (getter)PyBobLearnEMPLDABase_getG, - (setter)PyBobLearnEMPLDABase_setG, - G.doc(), - 0 - }, - { - mu.name(), - (getter)PyBobLearnEMPLDABase_getMu, - (setter)PyBobLearnEMPLDABase_setMu, - mu.doc(), - 0 - }, - { - __isigma__.name(), - (getter)PyBobLearnEMPLDABase_getISigma, - 0, - __isigma__.doc(), - 0 - }, - { - __alpha__.name(), - (getter)PyBobLearnEMPLDABase_getAlpha, - 0, - __alpha__.doc(), - 0 - }, - { - __beta__.name(), - (getter)PyBobLearnEMPLDABase_getBeta, - 0, - __beta__.doc(), - 0 - }, - { - __ft_beta__.name(), - (getter)PyBobLearnEMPLDABase_getFtBeta, - 0, - __ft_beta__.doc(), - 0 - }, - { - __gt_i_sigma__.name(), - (getter)PyBobLearnEMPLDABase_getGtISigma, - 0, - __gt_i_sigma__.doc(), - 0 - }, - { - __logdet_alpha__.name(), - (getter)PyBobLearnEMPLDABase_getLogDetAlpha, - 0, - __logdet_alpha__.doc(), - 0 - }, - { - __logdet_sigma__.name(), - (getter)PyBobLearnEMPLDABase_getLogDetSigma, - 0, - __logdet_sigma__.doc(), - 0 - }, - { - sigma.name(), - (getter)PyBobLearnEMPLDABase_getSigma, - (setter)PyBobLearnEMPLDABase_setSigma, - sigma.doc(), - 0 - }, - { - variance_threshold.name(), - (getter)PyBobLearnEMPLDABase_getVarianceThreshold, - (setter)PyBobLearnEMPLDABase_setVarianceThreshold, - variance_threshold.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the PLDABase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMPLDABase_Save(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the PLDABase to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMPLDABase_Load(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this PLDABase with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.PLDABase`", "A PLDABase object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMPLDABase_IsSimilarTo(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMPLDABaseObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMPLDABase_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/*** resize ***/ -static auto resize = bob::extension::FunctionDoc( - "resize", - "Resizes the dimensionality of the PLDA model. Paramaters :math:`\\mu`, :math:`F`, :math:`G` and :math:`\\Sigma` are reinitialized.", - 0, - true -) -.add_prototype("dim_d,dim_f,dim_g") -.add_parameter("dim_d", "int", "Dimensionality of the feature vector.") -.add_parameter("dim_f", "int", "Size of :math:`F` (between class variantion matrix).") -.add_parameter("dim_g", "int", "Size of :math:`G` (within class variantion matrix)."); -static PyObject* PyBobLearnEMPLDABase_resize(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = resize.kwlist(0); - - int dim_D, dim_F, dim_G = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii", kwlist, &dim_D, &dim_F, &dim_G)) return 0; - - if(dim_D <= 0){ - PyErr_Format(PyExc_TypeError, "dim_d argument must be greater than or equal to one"); - Py_RETURN_NONE; - } - - if(dim_F <= 0){ - PyErr_Format(PyExc_TypeError, "dim_f argument must be greater than or equal to one"); - Py_RETURN_NONE; - } - - if(dim_G <= 0){ - PyErr_Format(PyExc_TypeError, "dim_g argument must be greater than or equal to one"); - Py_RETURN_NONE; - } - - self->cxx->resize(dim_D, dim_F, dim_G); - - BOB_CATCH_MEMBER("cannot perform the resize method", 0) - - Py_RETURN_NONE; -} - - -/***** get_gamma *****/ -static auto get_gamma = bob::extension::FunctionDoc( - "get_gamma", - "Gets the :math:`\\gamma_a` matrix for a given :math:`a` (number of samples). " - ":math:`\\gamma_{a}=(Id + a F^T \\beta F)^{-1} = \\mathcal{F}_{a}`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","array_like <float, 2D>","Get the :math:`\\gamma` matrix"); -static PyObject* PyBobLearnEMPLDABase_getGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_gamma.kwlist(0); - - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i)); - BOB_CATCH_MEMBER("`get_gamma` could not be read", 0) -} - - -/***** has_gamma *****/ -static auto has_gamma = bob::extension::FunctionDoc( - "has_gamma", - "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists. " - ":math:`\\gamma_a=(Id + aF^T \\beta F)^{-1}`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","bool",""); -static PyObject* PyBobLearnEMPLDABase_hasGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = has_gamma.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - if(self->cxx->hasGamma(i)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; - BOB_CATCH_MEMBER("`has_gamma` could not be read", 0) -} - - -/***** compute_gamma *****/ -static auto compute_gamma = bob::extension::FunctionDoc( - "compute_gamma", - "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists." - " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1}`", - 0, - true -) -.add_prototype("a,res") -.add_parameter("a", "int", "Index") -.add_parameter("res", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMPLDABase_computeGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = compute_gamma.kwlist(0); - int i = 0; - PyBlitzArrayObject* res = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &res)) return 0; - - auto res_ = make_safe(res); - - self->cxx->computeGamma(i,*PyBlitzArrayCxx_AsBlitz<double,2>(res)); - Py_RETURN_NONE; - BOB_CATCH_MEMBER("`compute_gamma` could not be read", 0) -} - -/***** get_add_gamma *****/ -static auto get_add_gamma = bob::extension::FunctionDoc( - "get_add_gamma", - "Gets the :math:`gamma_a` matrix for a given :math:`f_a` (number of samples)." - " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1} =\\mathcal{F}_{a}`." - "Tries to find it from the base machine and then from this machine.", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","array_like <float, 2D>",""); -static PyObject* PyBobLearnEMPLDABase_getAddGamma(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_add_gamma.kwlist(0); - - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAddGamma(i)); - BOB_CATCH_MEMBER("`get_add_gamma` could not be read", 0) -} - - -/***** has_log_like_const_term *****/ -static auto has_log_like_const_term = bob::extension::FunctionDoc( - "has_log_like_const_term", - "Tells if the log likelihood constant term for a given :math:`a` (number of samples) exists in this machine (does not check the base machine). " - ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","bool",""); -static PyObject* PyBobLearnEMPLDABase_hasLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = has_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - if(self->cxx->hasLogLikeConstTerm(i)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; - BOB_CATCH_MEMBER("`has_log_like_const_term` could not be read", 0) -} - - -/***** compute_log_like_const_term" *****/ -static auto compute_log_like_const_term = bob::extension::FunctionDoc( - "compute_log_like_const_term", - "Computes the log likelihood constant term for a given :math:`a` (number of samples), given the provided :math:`\\gamma_a` matrix. " - ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`", - - 0, - true -) -.add_prototype("a,res") -.add_parameter("a", "int", "Index") -.add_parameter("res", "array_like <float, 2D>", "Input data"); -static PyObject* PyBobLearnEMPLDABase_computeLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = compute_log_like_const_term.kwlist(0); - int i = 0; - PyBlitzArrayObject* res = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&", kwlist, &i, &PyBlitzArray_Converter, &res)) return 0; - - auto res_ = make_safe(res); - - self->cxx->computeLogLikeConstTerm(i,*PyBlitzArrayCxx_AsBlitz<double,2>(res)); - Py_RETURN_NONE; - BOB_CATCH_MEMBER("`compute_gamma` could not be read", 0) -} - - -/***** get_add_log_like_const_term *****/ -static auto get_add_log_like_const_term = bob::extension::FunctionDoc( - "get_add_log_like_const_term", - - "Gets the log likelihood constant term for a given :math:`a` (number of samples). " - ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","float",""); -static PyObject* PyBobLearnEMPLDABase_getAddLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_add_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return Py_BuildValue("d",self->cxx->getAddLogLikeConstTerm(i)); - - BOB_CATCH_MEMBER("`get_add_log_like_const_term` could not be read", 0) -} - - -/***** get_log_like_const_term *****/ -static auto get_log_like_const_term = bob::extension::FunctionDoc( - "get_log_like_const_term", - "Gets the log likelihood constant term for a given :math:`a` (number of samples). " - ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","float",""); -static PyObject* PyBobLearnEMPLDABase_getLogLikeConstTerm(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return Py_BuildValue("d",self->cxx->getLogLikeConstTerm(i)); - - BOB_CATCH_MEMBER("`get_log_like_const_term` could not be read", 0) -} - -/***** clear_maps *****/ -static auto clear_maps = bob::extension::FunctionDoc( - "clear_maps", - "Clears the maps (:math:`\\gamma_a` and loglike_constterm_a).", - 0, - true -) -.add_prototype(""); -static PyObject* PyBobLearnEMPLDABase_clearMaps(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - self->cxx->clearMaps(); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("`clear_maps` could not be read", 0) -} - - -/***** compute_log_likelihood_point_estimate *****/ -static auto compute_log_likelihood_point_estimate = bob::extension::FunctionDoc( - "compute_log_likelihood_point_estimate", - "Gets the log-likelihood of an observation, given the current model and the latent variables (point estimate)." - "This will basically compute :math:`p(x_{ij} | h_{i}, w_{ij}, \\Theta)`, given by " - ":math:`\\mathcal{N}(x_{ij}|[\\mu + F h_{i} + G w_{ij} + \\epsilon_{ij}, \\Sigma])`, which is in logarithm, " - ":math:`\\frac{D}{2} log(2\\pi) -\\frac{1}{2} log(det(\\Sigma)) -\\frac{1}{2} {(x_{ij}-(\\mu+F h_{i}+G w_{ij}))^{T}\\Sigma^{-1}(x_{ij}-(\\mu+F h_{i}+G w_{ij}))}`", - 0, - true -) -.add_prototype("xij,hi,wij","output") -.add_parameter("xij", "array_like <float, 1D>", "") -.add_parameter("hi", "array_like <float, 1D>", "") -.add_parameter("wij", "array_like <float, 1D>", "") -.add_return("output", "float", ""); -static PyObject* PyBobLearnEMPLDABase_computeLogLikelihoodPointEstimate(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = compute_log_likelihood_point_estimate.kwlist(0); - PyBlitzArrayObject* xij, *hi, *wij; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&", kwlist, &PyBlitzArray_Converter, &xij, - &PyBlitzArray_Converter, &hi, - &PyBlitzArray_Converter, &wij)) return 0; - - auto xij_ = make_safe(xij); - auto hi_ = make_safe(hi); - auto wij_ = make_safe(wij); - - return Py_BuildValue("d", self->cxx->computeLogLikelihoodPointEstimate(*PyBlitzArrayCxx_AsBlitz<double,1>(xij), *PyBlitzArrayCxx_AsBlitz<double,1>(hi), *PyBlitzArrayCxx_AsBlitz<double,1>(wij))); - - BOB_CATCH_MEMBER("`compute_log_likelihood_point_estimate` could not be read", 0) -} - -/***** __precompute__ *****/ -static auto __precompute__ = bob::extension::FunctionDoc( - "__precompute__", - "Precomputes useful values for the log likelihood " - ":math:`\\log(\\det(\\alpha))` and :math:`\\log(\\det(\\Sigma))`.", - 0, - true -); -static PyObject* PyBobLearnEMPLDABase_precompute(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - self->cxx->precompute(); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("`precompute` could not be read", 0) -} - - -/***** __precompute_log_like__ *****/ -static auto __precompute_log_like__ = bob::extension::FunctionDoc( - "__precompute_log_like__", - - "Precomputes useful values for the log likelihood " - ":math:`\\log(\\det(\\alpha))` and :math:`\\log(\\det(\\Sigma))`.", - - 0, - true -); -static PyObject* PyBobLearnEMPLDABase_precomputeLogLike(PyBobLearnEMPLDABaseObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - self->cxx->precomputeLogLike(); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("`__precompute_log_like__` could not be read", 0) -} - - -static PyMethodDef PyBobLearnEMPLDABase_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMPLDABase_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMPLDABase_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMPLDABase_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - resize.name(), - (PyCFunction)PyBobLearnEMPLDABase_resize, - METH_VARARGS|METH_KEYWORDS, - resize.doc() - }, - { - get_gamma.name(), - (PyCFunction)PyBobLearnEMPLDABase_getGamma, - METH_VARARGS|METH_KEYWORDS, - get_gamma.doc() - }, - { - has_gamma.name(), - (PyCFunction)PyBobLearnEMPLDABase_hasGamma, - METH_VARARGS|METH_KEYWORDS, - has_gamma.doc() - }, - { - compute_gamma.name(), - (PyCFunction)PyBobLearnEMPLDABase_computeGamma, - METH_VARARGS|METH_KEYWORDS, - compute_gamma.doc() - }, - { - get_add_gamma.name(), - (PyCFunction)PyBobLearnEMPLDABase_getAddGamma, - METH_VARARGS|METH_KEYWORDS, - get_add_gamma.doc() - }, - { - has_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDABase_hasLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - has_log_like_const_term.doc() - }, - { - compute_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDABase_computeLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - compute_log_like_const_term.doc() - }, - { - get_add_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDABase_getAddLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - get_add_log_like_const_term.doc() - }, - { - get_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDABase_getLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - get_log_like_const_term.doc() - }, - { - clear_maps.name(), - (PyCFunction)PyBobLearnEMPLDABase_clearMaps, - METH_NOARGS, - clear_maps.doc() - }, - { - compute_log_likelihood_point_estimate.name(), - (PyCFunction)PyBobLearnEMPLDABase_computeLogLikelihoodPointEstimate, - METH_VARARGS|METH_KEYWORDS, - compute_log_likelihood_point_estimate.doc() - }, - { - __precompute__.name(), - (PyCFunction)PyBobLearnEMPLDABase_precompute, - METH_NOARGS, - __precompute__.doc() - }, - { - __precompute_log_like__.name(), - (PyCFunction)PyBobLearnEMPLDABase_precomputeLogLike, - METH_NOARGS, - __precompute_log_like__.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMPLDABase_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMPLDABase(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMPLDABase_Type.tp_name = PLDABase_doc.name(); - PyBobLearnEMPLDABase_Type.tp_basicsize = sizeof(PyBobLearnEMPLDABaseObject); - PyBobLearnEMPLDABase_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyBobLearnEMPLDABase_Type.tp_doc = PLDABase_doc.doc(); - - // set the functions - PyBobLearnEMPLDABase_Type.tp_new = PyType_GenericNew; - PyBobLearnEMPLDABase_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMPLDABase_init); - PyBobLearnEMPLDABase_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMPLDABase_delete); - PyBobLearnEMPLDABase_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDABase_RichCompare); - PyBobLearnEMPLDABase_Type.tp_methods = PyBobLearnEMPLDABase_methods; - PyBobLearnEMPLDABase_Type.tp_getset = PyBobLearnEMPLDABase_getseters; - //PyBobLearnEMPLDABase_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDABase_forward); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMPLDABase_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMPLDABase_Type); - return PyModule_AddObject(module, "PLDABase", (PyObject*)&PyBobLearnEMPLDABase_Type) >= 0; -} diff --git a/bob/learn/em/plda_machine.cpp b/bob/learn/em/plda_machine.cpp deleted file mode 100644 index c85e9d542c3536343385f8c87e01d54098e20001..0000000000000000000000000000000000000000 --- a/bob/learn/em/plda_machine.cpp +++ /dev/null @@ -1,806 +0,0 @@ -/** - * @date Thu Jan 30 11:10:15 2015 +0200 - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} /* converts PyObject to bool and returns false if object is NULL */ - -static auto PLDAMachine_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".PLDAMachine", - - "This class is a container for an enrolled identity/class. It contains information extracted from the enrollment samples. " - "It should be used in combination with a PLDABase instance.\n\n" - "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - - "Constructor, builds a new PLDAMachine.", - - "", - true - ) - .add_prototype("plda_base","") - .add_prototype("other","") - .add_prototype("hdf5,plda_base","") - - .add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "") - .add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be copied.") - .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") - -); - - -static int PyBobLearnEMPLDAMachine_init_copy(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDAMachine_doc.kwlist(1); - PyBobLearnEMPLDAMachineObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDAMachine_Type, &o)){ - PLDAMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::PLDAMachine(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMPLDAMachine_init_hdf5(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDAMachine_doc.kwlist(2); - - PyBobIoHDF5FileObject* config = 0; - PyBobLearnEMPLDABaseObject* plda_base; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O!", kwlist, &PyBobIoHDF5File_Converter, &config, - &PyBobLearnEMPLDABase_Type, &plda_base)){ - PLDAMachine_doc.print_usage(); - return -1; - } - auto config_ = make_safe(config); - self->cxx.reset(new bob::learn::em::PLDAMachine(*(config->f),plda_base->cxx)); - - return 0; -} - - -static int PyBobLearnEMPLDAMachine_init_pldabase(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDAMachine_doc.kwlist(0); - PyBobLearnEMPLDABaseObject* plda_base; - - //Here we have to select which keyword argument to read - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base)){ - PLDAMachine_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::PLDAMachine(plda_base->cxx)); - return 0; -} - -static int PyBobLearnEMPLDAMachine_init(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs==1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - // If the constructor input is Gaussian object - if (PyBobLearnEMPLDAMachine_Check(arg)) - return PyBobLearnEMPLDAMachine_init_copy(self, args, kwargs); - // If the constructor input is a HDF5 - else if (PyBobLearnEMPLDABase_Check(arg)) - return PyBobLearnEMPLDAMachine_init_pldabase(self, args, kwargs); - } - else if(nargs==2) - return PyBobLearnEMPLDAMachine_init_hdf5(self, args, kwargs); - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1 or 2 arguments, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - PLDAMachine_doc.print_usage(); - return -1; - } - BOB_CATCH_MEMBER("cannot create PLDAMachine", -1) - return 0; -} - - - -static void PyBobLearnEMPLDAMachine_delete(PyBobLearnEMPLDAMachineObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject* PyBobLearnEMPLDAMachine_RichCompare(PyBobLearnEMPLDAMachineObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMPLDAMachine_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMPLDAMachineObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare PLDAMachine objects", 0) -} - -int PyBobLearnEMPLDAMachine_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDAMachine_Type)); -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -/***** shape *****/ -static auto shape = bob::extension::VariableDoc( - "shape", - "(int,int, int)", - "A tuple that represents the dimensionality of the feature vector dim_d, the :math:`F` matrix and the :math:`G` matrix.", - "" -); -PyObject* PyBobLearnEMPLDAMachine_getShape(PyBobLearnEMPLDAMachineObject* self, void*) { - BOB_TRY - return Py_BuildValue("(i,i,i)", self->cxx->getDimD(), self->cxx->getDimF(), self->cxx->getDimG()); - BOB_CATCH_MEMBER("shape could not be read", 0) -} - - -/***** n_samples *****/ -static auto n_samples = bob::extension::VariableDoc( - "n_samples", - "int", - "Number of enrolled samples", - "" -); -static PyObject* PyBobLearnEMPLDAMachine_getNSamples(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("i",self->cxx->getNSamples()); - BOB_CATCH_MEMBER("n_samples could not be read", 0) -} -int PyBobLearnEMPLDAMachine_setNSamples(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyInt_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an int", Py_TYPE(self)->tp_name, n_samples.name()); - return -1; - } - - if (PyInt_AS_LONG(value) < 0){ - PyErr_Format(PyExc_TypeError, "n_samples must be greater than or equal to zero"); - return -1; - } - - self->cxx->setNSamples(PyInt_AS_LONG(value)); - BOB_CATCH_MEMBER("n_samples could not be set", -1) - return 0; -} - - -/***** w_sum_xit_beta_xi *****/ -static auto w_sum_xit_beta_xi = bob::extension::VariableDoc( - "w_sum_xit_beta_xi", - "float", - "Gets the :math:`A = -0.5 \\sum_{i} x_{i}^T \\beta x_{i}` value", - "" -); -static PyObject* PyBobLearnEMPLDAMachine_getWSumXitBetaXi(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("d",self->cxx->getWSumXitBetaXi()); - BOB_CATCH_MEMBER("w_sum_xit_beta_xi could not be read", 0) -} -int PyBobLearnEMPLDAMachine_setWSumXitBetaXi(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an float", Py_TYPE(self)->tp_name, w_sum_xit_beta_xi.name()); - return -1; - } - - self->cxx->setWSumXitBetaXi(PyFloat_AS_DOUBLE(value)); - BOB_CATCH_MEMBER("w_sum_xit_beta_xi could not be set", -1) - return 0; -} - - -/***** plda_base *****/ -static auto plda_base = bob::extension::VariableDoc( - "plda_base", - ":py:class:`bob.learn.em.PLDABase`", - "The PLDABase attached to this machine", - "" -); -PyObject* PyBobLearnEMPLDAMachine_getPLDABase(PyBobLearnEMPLDAMachineObject* self, void*){ - BOB_TRY - - boost::shared_ptr<bob::learn::em::PLDABase> plda_base_o = self->cxx->getPLDABase(); - - //Allocating the correspondent python object - PyBobLearnEMPLDABaseObject* retval = - (PyBobLearnEMPLDABaseObject*)PyBobLearnEMPLDABase_Type.tp_alloc(&PyBobLearnEMPLDABase_Type, 0); - retval->cxx = plda_base_o; - - return Py_BuildValue("N",retval); - BOB_CATCH_MEMBER("plda_base could not be read", 0) -} -int PyBobLearnEMPLDAMachine_setPLDABase(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBobLearnEMPLDABase_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a :py:class:`bob.learn.em.PLDABase`", Py_TYPE(self)->tp_name, plda_base.name()); - return -1; - } - - PyBobLearnEMPLDABaseObject* plda_base_o = 0; - PyArg_Parse(value, "O!", &PyBobLearnEMPLDABase_Type,&plda_base_o); - - self->cxx->setPLDABase(plda_base_o->cxx); - - return 0; - BOB_CATCH_MEMBER("plda_base could not be set", -1) -} - - -/***** weighted_sum *****/ -static auto weighted_sum = bob::extension::VariableDoc( - "weighted_sum", - "array_like <float, 1D>", - "Get/Set :math:`\\sum_{i} F^T \\beta x_{i}` value", - "" -); -static PyObject* PyBobLearnEMPLDAMachine_getWeightedSum(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getWeightedSum()); - BOB_CATCH_MEMBER("weighted_sum could not be read", 0) -} -int PyBobLearnEMPLDAMachine_setWeightedSum(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){ - BOB_TRY - PyBlitzArrayObject* o; - if (!PyBlitzArray_Converter(value, &o)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, weighted_sum.name()); - return -1; - } - auto o_ = make_safe(o); - auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "weighted_sum"); - if (!b) return -1; - self->cxx->setWeightedSum(*b); - return 0; - BOB_CATCH_MEMBER("`weighted_sum` vector could not be set", -1) -} - - -/***** log_likelihood *****/ -static auto log_likelihood = bob::extension::VariableDoc( - "log_likelihood", - "float", - "Get the current log likelihood", - "" -); -static PyObject* PyBobLearnEMPLDAMachine_getLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - return Py_BuildValue("d",self->cxx->getLogLikelihood()); - BOB_CATCH_MEMBER("log_likelihood could not be read", 0) -} -int PyBobLearnEMPLDAMachine_setLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* value, void*){ - BOB_TRY - - if (!PyBob_NumberCheck(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects a float", Py_TYPE(self)->tp_name, log_likelihood.name()); - return -1; - } - - self->cxx->setLogLikelihood(PyFloat_AS_DOUBLE(value)); - BOB_CATCH_MEMBER("log_likelihood could not be set", -1) - return 0; -} - - -static PyGetSetDef PyBobLearnEMPLDAMachine_getseters[] = { - { - shape.name(), - (getter)PyBobLearnEMPLDAMachine_getShape, - 0, - shape.doc(), - 0 - }, - { - n_samples.name(), - (getter)PyBobLearnEMPLDAMachine_getNSamples, - (setter)PyBobLearnEMPLDAMachine_setNSamples, - n_samples.doc(), - 0 - }, - { - w_sum_xit_beta_xi.name(), - (getter)PyBobLearnEMPLDAMachine_getWSumXitBetaXi, - (setter)PyBobLearnEMPLDAMachine_setWSumXitBetaXi, - w_sum_xit_beta_xi.doc(), - 0 - }, - { - plda_base.name(), - (getter)PyBobLearnEMPLDAMachine_getPLDABase, - (setter)PyBobLearnEMPLDAMachine_setPLDABase, - plda_base.doc(), - 0 - }, - { - weighted_sum.name(), - (getter)PyBobLearnEMPLDAMachine_getWeightedSum, - (setter)PyBobLearnEMPLDAMachine_setWeightedSum, - weighted_sum.doc(), - 0 - }, - { - log_likelihood.name(), - (getter)PyBobLearnEMPLDAMachine_getLogLikelihood, - (setter)PyBobLearnEMPLDAMachine_setLogLikelihood, - log_likelihood.doc(), - 0 - }, - {0} // Sentinel -}; - - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - - -/*** save ***/ -static auto save = bob::extension::FunctionDoc( - "save", - "Save the configuration of the PLDAMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); -static PyObject* PyBobLearnEMPLDAMachine_Save(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - - BOB_TRY - - // get list of arguments - char** kwlist = save.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->save(*hdf5->f); - - BOB_CATCH_MEMBER("cannot save the data", 0) - Py_RETURN_NONE; -} - -/*** load ***/ -static auto load = bob::extension::FunctionDoc( - "load", - "Load the configuration of the PLDAMachine to a given HDF5 file" -) -.add_prototype("hdf5") -.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); -static PyObject* PyBobLearnEMPLDAMachine_Load(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = load.kwlist(0); - PyBobIoHDF5FileObject* hdf5; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; - - auto hdf5_ = make_safe(hdf5); - self->cxx->load(*hdf5->f); - - BOB_CATCH_MEMBER("cannot load the data", 0) - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this PLDAMachine with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMPLDAMachine_IsSimilarTo(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMPLDAMachineObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMPLDAMachine_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - -/***** get_gamma *****/ -static auto get_gamma = bob::extension::FunctionDoc( - "get_gamma", - "Gets the :math:`\\gamma_a` matrix for a given :math:`a` (number of samples). " - ":math:`\\gamma_{a}=(Id + a F^T \\beta F)^{-1}= \\mathcal{F}_{a}`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","array_like <float, 2D>","Get the :math:`\\gamma` matrix"); -static PyObject* PyBobLearnEMPLDAMachine_getGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_gamma.kwlist(0); - - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getGamma(i)); - BOB_CATCH_MEMBER("`get_gamma` could not be read", 0) -} - - -/***** has_gamma *****/ -static auto has_gamma = bob::extension::FunctionDoc( - "has_gamma", - "Tells if the :math:`\\gamma_a` matrix for a given a (number of samples) exists. " - ":math:`\\gamma_a=(Id + a F^T \\beta F)^{-1}`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","bool",""); -static PyObject* PyBobLearnEMPLDAMachine_hasGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = has_gamma.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - if(self->cxx->hasGamma(i)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; - BOB_CATCH_MEMBER("`has_gamma` could not be read", 0) -} - - -/***** get_add_gamma *****/ -static auto get_add_gamma = bob::extension::FunctionDoc( - "get_add_gamma", - "Gets the :math:`gamma_a` matrix for a given :math:`f_a` (number of samples)." - " :math:`\\gamma_a=(Id + a F^T \\beta F)^{-1} =\\mathcal{F}_{a}`." - "Tries to find it from the base machine and then from this machine.", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","array_like <float, 2D>",""); -static PyObject* PyBobLearnEMPLDAMachine_getAddGamma(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_add_gamma.kwlist(0); - - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getAddGamma(i)); - BOB_CATCH_MEMBER("`get_add_gamma` could not be read", 0) -} - - -/***** has_log_like_const_term *****/ -static auto has_log_like_const_term = bob::extension::FunctionDoc( - "has_log_like_const_term", - "Tells if the log likelihood constant term for a given :math:`a` (number of samples) exists in this machine (does not check the base machine). " - ":math:`l_{a}=\\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|\\gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","bool",""); -static PyObject* PyBobLearnEMPLDAMachine_hasLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = has_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - if(self->cxx->hasLogLikeConstTerm(i)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; - BOB_CATCH_MEMBER("`has_log_like_const_term` could not be read", 0) -} - - -/***** get_add_log_like_const_term *****/ -static auto get_add_log_like_const_term = bob::extension::FunctionDoc( - "get_add_log_like_const_term", - - "Gets the log likelihood constant term for a given :math:`a` (number of samples). " - ":math:`l_{a} = \\frac{a}{2} ( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| +log|gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","float",""); -static PyObject* PyBobLearnEMPLDAMachine_getAddLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_add_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return Py_BuildValue("d",self->cxx->getAddLogLikeConstTerm(i)); - - BOB_CATCH_MEMBER("`get_add_log_like_const_term` could not be read", 0) -} - - -/***** get_log_like_const_term *****/ -static auto get_log_like_const_term = bob::extension::FunctionDoc( - "get_log_like_const_term", - "Gets the log likelihood constant term for a given :math:`a` (number of samples). " - ":math:`l_{a}=\\frac{a}{2}( -D log(2\\pi) -log|\\Sigma| +log|\\alpha| + log|\\gamma_a|)`", - 0, - true -) -.add_prototype("a","output") -.add_parameter("a", "int", "Index") -.add_return("output","float",""); -static PyObject* PyBobLearnEMPLDAMachine_getLogLikeConstTerm(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = get_log_like_const_term.kwlist(0); - int i = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &i)) return 0; - - return Py_BuildValue("d",self->cxx->getLogLikeConstTerm(i)); - - BOB_CATCH_MEMBER("`get_log_like_const_term` could not be read", 0) -} - -/***** clear_maps *****/ -static auto clear_maps = bob::extension::FunctionDoc( - "clear_maps", - "Clears the maps (:math:`\\gamma_a` and loglike_constterm_a).", - 0, - true -) -.add_prototype(""); -static PyObject* PyBobLearnEMPLDAMachine_clearMaps(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - self->cxx->clearMaps(); - Py_RETURN_NONE; - - BOB_CATCH_MEMBER("`clear_maps` could not be read", 0) -} - - -/***** compute_log_likelihood *****/ -static auto compute_log_likelihood = bob::extension::FunctionDoc( - "compute_log_likelihood", - "Compute the log-likelihood of the given sample and (optionally) the enrolled samples", - 0, - true -) -.add_prototype("sample,with_enrolled_samples","output") -.add_parameter("sample", "array_like <float, 1D>,array_like <float, 2D>", "Sample") -.add_parameter("with_enrolled_samples", "bool", "") -.add_return("output","float","The log-likelihood"); -static PyObject* PyBobLearnEMPLDAMachine_computeLogLikelihood(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = compute_log_likelihood.kwlist(0); - - PyBlitzArrayObject* samples; - PyObject* with_enrolled_samples = Py_True; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|O!", kwlist, &PyBlitzArray_Converter, &samples, - &PyBool_Type, &with_enrolled_samples)) return 0; - auto samples_ = make_safe(samples); - - /*Using the proper method according to the dimension*/ - if (samples->ndim==1) - return Py_BuildValue("d",self->cxx->computeLogLikelihood(*PyBlitzArrayCxx_AsBlitz<double,1>(samples), f(with_enrolled_samples))); - else - return Py_BuildValue("d",self->cxx->computeLogLikelihood(*PyBlitzArrayCxx_AsBlitz<double,2>(samples), f(with_enrolled_samples))); - - - BOB_CATCH_MEMBER("`compute_log_likelihood` could not be read", 0) -} - - -/***** log_likelihood_ratio *****/ -static auto log_likelihood_ratio = bob::extension::FunctionDoc( - "log_likelihood_ratio", - "Computes a log likelihood ratio from a 1D or 2D blitz::Array", - 0, - true -) -.add_prototype("samples","output") -.add_parameter("samples", "array_like <float, 1D>,array_like <float, 2D>", "Sample") -.add_return("output","float","The log-likelihood ratio"); -static PyObject* PyBobLearnEMPLDAMachine_log_likelihood_ratio(PyBobLearnEMPLDAMachineObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - char** kwlist = log_likelihood_ratio.kwlist(0); - - PyBlitzArrayObject* samples; - - /*Convert to PyObject first to access the number of dimensions*/ - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBlitzArray_Converter, &samples)) return 0; - auto samples_ = make_safe(samples); - - //There are 2 methods in C++, one <double,1> and the another <double,2> - if(samples->ndim==1) - return Py_BuildValue("d",self->cxx->forward(*PyBlitzArrayCxx_AsBlitz<double,1>(samples))); - else - return Py_BuildValue("d",self->cxx->forward(*PyBlitzArrayCxx_AsBlitz<double,2>(samples))); - - BOB_CATCH_MEMBER("log_likelihood_ratio could not be executed", 0) -} - - -static PyMethodDef PyBobLearnEMPLDAMachine_methods[] = { - { - save.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_Save, - METH_VARARGS|METH_KEYWORDS, - save.doc() - }, - { - load.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_Load, - METH_VARARGS|METH_KEYWORDS, - load.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - { - get_gamma.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_getGamma, - METH_VARARGS|METH_KEYWORDS, - get_gamma.doc() - }, - { - has_gamma.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_hasGamma, - METH_VARARGS|METH_KEYWORDS, - has_gamma.doc() - }, - { - get_add_gamma.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_getAddGamma, - METH_VARARGS|METH_KEYWORDS, - get_add_gamma.doc() - }, - { - has_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_hasLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - has_log_like_const_term.doc() - }, - { - get_add_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_getAddLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - get_add_log_like_const_term.doc() - }, - { - get_log_like_const_term.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_getLogLikeConstTerm, - METH_VARARGS|METH_KEYWORDS, - get_log_like_const_term.doc() - }, - { - clear_maps.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_clearMaps, - METH_NOARGS, - clear_maps.doc() - }, - { - compute_log_likelihood.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_computeLogLikelihood, - METH_VARARGS|METH_KEYWORDS, - compute_log_likelihood.doc() - }, - { - log_likelihood_ratio.name(), - (PyCFunction)PyBobLearnEMPLDAMachine_log_likelihood_ratio, - METH_VARARGS|METH_KEYWORDS, - log_likelihood_ratio.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the JFA type struct; will be initialized later -PyTypeObject PyBobLearnEMPLDAMachine_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMPLDAMachine(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMPLDAMachine_Type.tp_name = PLDAMachine_doc.name(); - PyBobLearnEMPLDAMachine_Type.tp_basicsize = sizeof(PyBobLearnEMPLDAMachineObject); - PyBobLearnEMPLDAMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT; - PyBobLearnEMPLDAMachine_Type.tp_doc = PLDAMachine_doc.doc(); - - // set the functions - PyBobLearnEMPLDAMachine_Type.tp_new = PyType_GenericNew; - PyBobLearnEMPLDAMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMPLDAMachine_init); - PyBobLearnEMPLDAMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMPLDAMachine_delete); - PyBobLearnEMPLDAMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDAMachine_RichCompare); - PyBobLearnEMPLDAMachine_Type.tp_methods = PyBobLearnEMPLDAMachine_methods; - PyBobLearnEMPLDAMachine_Type.tp_getset = PyBobLearnEMPLDAMachine_getseters; - PyBobLearnEMPLDAMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDAMachine_log_likelihood_ratio); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMPLDAMachine_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMPLDAMachine_Type); - return PyModule_AddObject(module, "PLDAMachine", (PyObject*)&PyBobLearnEMPLDAMachine_Type) >= 0; -} diff --git a/bob/learn/em/plda_trainer.cpp b/bob/learn/em/plda_trainer.cpp deleted file mode 100644 index 49842a3b086f4c3e865812a69a71eaedf553924d..0000000000000000000000000000000000000000 --- a/bob/learn/em/plda_trainer.cpp +++ /dev/null @@ -1,721 +0,0 @@ -/** - * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> - * @date Wed 04 Feb 14:15:00 2015 - * - * @brief Python API for bob::learn::em - * - * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - */ - -#include "main.h" -#include <boost/make_shared.hpp> -#include <boost/assign.hpp> - -//Defining maps for each initializatio method -static const std::map<std::string, bob::learn::em::PLDATrainer::InitFMethod> FMethod = boost::assign::map_list_of - ("RANDOM_F", bob::learn::em::PLDATrainer::RANDOM_F) - ("BETWEEN_SCATTER", bob::learn::em::PLDATrainer::BETWEEN_SCATTER) - ; - -static const std::map<std::string, bob::learn::em::PLDATrainer::InitGMethod> GMethod = boost::assign::map_list_of - ("RANDOM_G", bob::learn::em::PLDATrainer::RANDOM_G) - ("WITHIN_SCATTER", bob::learn::em::PLDATrainer::WITHIN_SCATTER) - ; - -static const std::map<std::string, bob::learn::em::PLDATrainer::InitSigmaMethod> SigmaMethod = boost::assign::map_list_of - ("RANDOM_SIGMA", bob::learn::em::PLDATrainer::RANDOM_SIGMA) - ("VARIANCE_G", bob::learn::em::PLDATrainer::VARIANCE_G) - ("CONSTANT", bob::learn::em::PLDATrainer::CONSTANT) - ("VARIANCE_DATA", bob::learn::em::PLDATrainer::VARIANCE_DATA) - ; - -//String to type -static inline bob::learn::em::PLDATrainer::InitFMethod string2FMethod(const std::string& o){ - auto it = FMethod.find(o); - if (it == FMethod.end()) throw std::runtime_error("The given FMethod '" + o + "' is not known; choose one of ('RANDOM_F','BETWEEN_SCATTER')"); - else return it->second; -} - -static inline bob::learn::em::PLDATrainer::InitGMethod string2GMethod(const std::string& o){ - auto it = GMethod.find(o); - if (it == GMethod.end()) throw std::runtime_error("The given GMethod '" + o + "' is not known; choose one of ('RANDOM_G','WITHIN_SCATTER')"); - else return it->second; -} - -static inline bob::learn::em::PLDATrainer::InitSigmaMethod string2SigmaMethod(const std::string& o){ - auto it = SigmaMethod.find(o); - if (it == SigmaMethod.end()) throw std::runtime_error("The given SigmaMethod '" + o + "' is not known; choose one of ('RANDOM_SIGMA','VARIANCE_G', 'CONSTANT', 'VARIANCE_DATA')"); - else return it->second; -} - -//Type to string -static inline const std::string& FMethod2string(bob::learn::em::PLDATrainer::InitFMethod o){ - for (auto it = FMethod.begin(); it != FMethod.end(); ++it) if (it->second == o) return it->first; - throw std::runtime_error("The given FMethod type is not known"); -} - -static inline const std::string& GMethod2string(bob::learn::em::PLDATrainer::InitGMethod o){ - for (auto it = GMethod.begin(); it != GMethod.end(); ++it) if (it->second == o) return it->first; - throw std::runtime_error("The given GMethod type is not known"); -} - -static inline const std::string& SigmaMethod2string(bob::learn::em::PLDATrainer::InitSigmaMethod o){ - for (auto it = SigmaMethod.begin(); it != SigmaMethod.end(); ++it) if (it->second == o) return it->first; - throw std::runtime_error("The given SigmaMethod type is not known"); -} - - -static inline bool f(PyObject* o){return o != 0 && PyObject_IsTrue(o) > 0;} /* converts PyObject to bool and returns false if object is NULL */ - -template <int N> -int list_as_vector(PyObject* list, std::vector<blitz::Array<double,N> >& vec) -{ - for (int i=0; i<PyList_GET_SIZE(list); i++) - { - PyBlitzArrayObject* blitz_object; - if (!PyArg_Parse(PyList_GetItem(list, i), "O&", &PyBlitzArray_Converter, &blitz_object)){ - PyErr_Format(PyExc_RuntimeError, "Expected numpy array object"); - return -1; - } - auto blitz_object_ = make_safe(blitz_object); - vec.push_back(*PyBlitzArrayCxx_AsBlitz<double,N>(blitz_object)); - } - return 0; -} - - -template <int N> -static PyObject* vector_as_list(const std::vector<blitz::Array<double,N> >& vec) -{ - PyObject* list = PyList_New(vec.size()); - for(size_t i=0; i<vec.size(); i++){ - blitz::Array<double,N> numpy_array = vec[i]; - PyObject* numpy_py_object = PyBlitzArrayCxx_AsNumpy(numpy_array); - PyList_SET_ITEM(list, i, numpy_py_object); - } - return list; -} - - -/******************************************************************/ -/************ Constructor Section *********************************/ -/******************************************************************/ - - -static auto PLDATrainer_doc = bob::extension::ClassDoc( - BOB_EXT_MODULE_PREFIX ".PLDATrainer", - "This class can be used to train the :math:`F`, :math:`G` and " - " :math:`\\Sigma` matrices and the mean vector :math:`\\mu` of a PLDA model." - "References: [ElShafey2014]_ [PrinceElder2007]_ [LiFu2012]_ ", - "" -).add_constructor( - bob::extension::FunctionDoc( - "__init__", - "Default constructor.\n Initializes a new PLDA trainer. The " - "training stage will place the resulting components in the " - "PLDABase.", - "", - true - ) - .add_prototype("use_sum_second_order","") - .add_prototype("other","") - .add_prototype("","") - - .add_parameter("other", ":py:class:`bob.learn.em.PLDATrainer`", "A PLDATrainer object to be copied.") - .add_parameter("use_sum_second_order", "bool", "") -); - -static int PyBobLearnEMPLDATrainer_init_copy(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDATrainer_doc.kwlist(1); - PyBobLearnEMPLDATrainerObject* o; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnEMPLDATrainer_Type, &o)){ - PLDATrainer_doc.print_usage(); - return -1; - } - - self->cxx.reset(new bob::learn::em::PLDATrainer(*o->cxx)); - return 0; -} - - -static int PyBobLearnEMPLDATrainer_init_bool(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - - char** kwlist = PLDATrainer_doc.kwlist(0); - PyObject* use_sum_second_order = Py_False; - - //Parsing the input argments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!", kwlist, &PyBool_Type, &use_sum_second_order)) - return -1; - - self->cxx.reset(new bob::learn::em::PLDATrainer(f(use_sum_second_order))); - return 0; -} - - -static int PyBobLearnEMPLDATrainer_init(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - // get the number of command line arguments - int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); - - if(nargs==0) - return PyBobLearnEMPLDATrainer_init_bool(self, args, kwargs); - else if(nargs==1){ - //Reading the input argument - PyObject* arg = 0; - if (PyTuple_Size(args)) - arg = PyTuple_GET_ITEM(args, 0); - else { - PyObject* tmp = PyDict_Values(kwargs); - auto tmp_ = make_safe(tmp); - arg = PyList_GET_ITEM(tmp, 0); - } - - if(PyBobLearnEMPLDATrainer_Check(arg)) - // If the constructor input is PLDATrainer object - return PyBobLearnEMPLDATrainer_init_copy(self, args, kwargs); - else - return PyBobLearnEMPLDATrainer_init_bool(self, args, kwargs); - } - else{ - PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires only 0 or 1 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); - PLDATrainer_doc.print_usage(); - return -1; - } - - BOB_CATCH_MEMBER("cannot create PLDATrainer", -1) - return 0; -} - - -static void PyBobLearnEMPLDATrainer_delete(PyBobLearnEMPLDATrainerObject* self) { - self->cxx.reset(); - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -int PyBobLearnEMPLDATrainer_Check(PyObject* o) { - return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnEMPLDATrainer_Type)); -} - - -static PyObject* PyBobLearnEMPLDATrainer_RichCompare(PyBobLearnEMPLDATrainerObject* self, PyObject* other, int op) { - BOB_TRY - - if (!PyBobLearnEMPLDATrainer_Check(other)) { - PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); - return 0; - } - auto other_ = reinterpret_cast<PyBobLearnEMPLDATrainerObject*>(other); - switch (op) { - case Py_EQ: - if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; - case Py_NE: - if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; - default: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - BOB_CATCH_MEMBER("cannot compare PLDATrainer objects", 0) -} - - -/******************************************************************/ -/************ Variables Section ***********************************/ -/******************************************************************/ - -static auto z_second_order = bob::extension::VariableDoc( - "z_second_order", - "array_like <float, 3D>", - "", - "" -); -PyObject* PyBobLearnEMPLDATrainer_get_z_second_order(PyBobLearnEMPLDATrainerObject* self, void*){ - BOB_TRY - //return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZSecondOrder()); - return vector_as_list(self->cxx->getZSecondOrder()); - BOB_CATCH_MEMBER("z_second_order could not be read", 0) -} - - -static auto z_second_order_sum = bob::extension::VariableDoc( - "z_second_order_sum", - "array_like <float, 2D>", - "", - "" -); -PyObject* PyBobLearnEMPLDATrainer_get_z_second_order_sum(PyBobLearnEMPLDATrainerObject* self, void*){ - BOB_TRY - return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZSecondOrderSum()); - BOB_CATCH_MEMBER("z_second_order_sum could not be read", 0) -} - - -static auto z_first_order = bob::extension::VariableDoc( - "z_first_order", - "array_like <float, 2D>", - "", - "" -); -PyObject* PyBobLearnEMPLDATrainer_get_z_first_order(PyBobLearnEMPLDATrainerObject* self, void*){ - BOB_TRY - //return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getZFirstOrder()); - return vector_as_list(self->cxx->getZFirstOrder()); - BOB_CATCH_MEMBER("z_first_order could not be read", 0) -} - - -/***** init_f_method *****/ -static auto init_f_method = bob::extension::VariableDoc( - "init_f_method", - "str", - "The method used for the initialization of :math:`$F$`.", - "Possible values are: ('RANDOM_F', 'BETWEEN_SCATTER')" -); -PyObject* PyBobLearnEMPLDATrainer_getFMethod(PyBobLearnEMPLDATrainerObject* self, void*) { - BOB_TRY - return Py_BuildValue("s", FMethod2string(self->cxx->getInitFMethod()).c_str()); - BOB_CATCH_MEMBER("init_f_method method could not be read", 0) -} -int PyBobLearnEMPLDATrainer_setFMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyString_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_f_method.name()); - return -1; - } - self->cxx->setInitFMethod(string2FMethod(PyString_AS_STRING(value))); - - return 0; - BOB_CATCH_MEMBER("init_f_method method could not be set", -1) -} - - -/***** init_g_method *****/ -static auto init_g_method = bob::extension::VariableDoc( - "init_g_method", - "str", - "The method used for the initialization of :math:`$G$`.", - "Possible values are: ('RANDOM_G', 'WITHIN_SCATTER')" -); -PyObject* PyBobLearnEMPLDATrainer_getGMethod(PyBobLearnEMPLDATrainerObject* self, void*) { - BOB_TRY - return Py_BuildValue("s", GMethod2string(self->cxx->getInitGMethod()).c_str()); - BOB_CATCH_MEMBER("init_g_method method could not be read", 0) -} -int PyBobLearnEMPLDATrainer_setGMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyString_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_g_method.name()); - return -1; - } - self->cxx->setInitGMethod(string2GMethod(PyString_AS_STRING(value))); - - return 0; - BOB_CATCH_MEMBER("init_g_method method could not be set", -1) -} - -/***** init_sigma_method *****/ -static auto init_sigma_method = bob::extension::VariableDoc( - "init_sigma_method", - "str", - "The method used for the initialization of :math:`$\\Sigma$`.", - "Possible values are: ('RANDOM_SIGMA', 'VARIANCE_G', 'CONSTANT', 'VARIANCE_DATA')" -); -PyObject* PyBobLearnEMPLDATrainer_getSigmaMethod(PyBobLearnEMPLDATrainerObject* self, void*) { - BOB_TRY - return Py_BuildValue("s", SigmaMethod2string(self->cxx->getInitSigmaMethod()).c_str()); - BOB_CATCH_MEMBER("init_sigma_method method could not be read", 0) -} -int PyBobLearnEMPLDATrainer_setSigmaMethod(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyString_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, init_sigma_method.name()); - return -1; - } - self->cxx->setInitSigmaMethod(string2SigmaMethod(PyString_AS_STRING(value))); - - return 0; - BOB_CATCH_MEMBER("init_sigma_method method could not be set", -1) -} - - -static auto use_sum_second_order = bob::extension::VariableDoc( - "use_sum_second_order", - "bool", - "Tells whether the second order statistics are stored during the training procedure, or only their sum.", - "" -); -PyObject* PyBobLearnEMPLDATrainer_getUseSumSecondOrder(PyBobLearnEMPLDATrainerObject* self, void*){ - BOB_TRY - return Py_BuildValue("O",self->cxx->getUseSumSecondOrder()?Py_True:Py_False); - BOB_CATCH_MEMBER("use_sum_second_order could not be read", 0) -} -int PyBobLearnEMPLDATrainer_setUseSumSecondOrder(PyBobLearnEMPLDATrainerObject* self, PyObject* value, void*) { - BOB_TRY - - if (!PyBool_Check(value)){ - PyErr_Format(PyExc_RuntimeError, "%s %s expects an str", Py_TYPE(self)->tp_name, use_sum_second_order.name()); - return -1; - } - self->cxx->setUseSumSecondOrder(f(value)); - - return 0; - BOB_CATCH_MEMBER("use_sum_second_order method could not be set", -1) -} - - - -static PyGetSetDef PyBobLearnEMPLDATrainer_getseters[] = { - { - z_first_order.name(), - (getter)PyBobLearnEMPLDATrainer_get_z_first_order, - 0, - z_first_order.doc(), - 0 - }, - { - z_second_order_sum.name(), - (getter)PyBobLearnEMPLDATrainer_get_z_second_order_sum, - 0, - z_second_order_sum.doc(), - 0 - }, - { - z_second_order.name(), - (getter)PyBobLearnEMPLDATrainer_get_z_second_order, - 0, - z_second_order.doc(), - 0 - }, - { - init_f_method.name(), - (getter)PyBobLearnEMPLDATrainer_getFMethod, - (setter)PyBobLearnEMPLDATrainer_setFMethod, - init_f_method.doc(), - 0 - }, - { - init_g_method.name(), - (getter)PyBobLearnEMPLDATrainer_getGMethod, - (setter)PyBobLearnEMPLDATrainer_setGMethod, - init_g_method.doc(), - 0 - }, - { - init_sigma_method.name(), - (getter)PyBobLearnEMPLDATrainer_getSigmaMethod, - (setter)PyBobLearnEMPLDATrainer_setSigmaMethod, - init_sigma_method.doc(), - 0 - }, - { - use_sum_second_order.name(), - (getter)PyBobLearnEMPLDATrainer_getUseSumSecondOrder, - (setter)PyBobLearnEMPLDATrainer_setUseSumSecondOrder, - use_sum_second_order.doc(), - 0 - }, - {0} // Sentinel -}; - - -/******************************************************************/ -/************ Functions Section ***********************************/ -/******************************************************************/ - -/*** initialize ***/ -static auto initialize = bob::extension::FunctionDoc( - "initialize", - "Initialization before the EM steps", - "", - true -) -.add_prototype("plda_base, data, [rng]") -.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object") -.add_parameter("data", "list", "") -.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop."); -static PyObject* PyBobLearnEMPLDATrainer_initialize(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = initialize.kwlist(0); - - PyBobLearnEMPLDABaseObject* plda_base = 0; - PyObject* data = 0; - PyBoostMt19937Object* rng = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base, - &PyList_Type, &data, - &PyBoostMt19937_Type, &rng)) return 0; - - std::vector<blitz::Array<double,2> > data_vector; - if(list_as_vector(data ,data_vector)==0){ - if(rng){ - self->cxx->setRng(rng->rng); - } - - self->cxx->initialize(*plda_base->cxx, data_vector); - } - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the initialize method", 0) - - Py_RETURN_NONE; -} - - -/*** e_step ***/ -static auto e_step = bob::extension::FunctionDoc( - "e_step", - "Expectation step before the EM steps", - "", - true -) -.add_prototype("plda_base,data") -.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object") -.add_parameter("data", "list", ""); -static PyObject* PyBobLearnEMPLDATrainer_e_step(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = e_step.kwlist(0); - - PyBobLearnEMPLDABaseObject* plda_base = 0; - PyObject* data = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base, - &PyList_Type, &data)) return 0; - - std::vector<blitz::Array<double,2> > data_vector; - if(list_as_vector(data ,data_vector)==0) - self->cxx->eStep(*plda_base->cxx, data_vector); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the e_step method", 0) - - Py_RETURN_NONE; -} - - -/*** m_step ***/ -static auto m_step = bob::extension::FunctionDoc( - "m_step", - "Maximization step ", - "", - true -) -.add_prototype("plda_base,data") -.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object") -.add_parameter("data", "list", ""); -static PyObject* PyBobLearnEMPLDATrainer_m_step(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = m_step.kwlist(0); - - PyBobLearnEMPLDABaseObject* plda_base = 0; - PyObject* data = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base, - &PyList_Type, &data)) return 0; - - std::vector<blitz::Array<double,2> > data_vector; - if(list_as_vector(data ,data_vector)==0) - self->cxx->mStep(*plda_base->cxx, data_vector); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the m_step method", 0) - - Py_RETURN_NONE; -} - - -/*** finalize ***/ -static auto finalize = bob::extension::FunctionDoc( - "finalize", - "finalize before the EM steps", - "", - true -) -.add_prototype("plda_base,data") -.add_parameter("plda_base", ":py:class:`bob.learn.em.PLDABase`", "PLDAMachine Object") -.add_parameter("data", "list", ""); -static PyObject* PyBobLearnEMPLDATrainer_finalize(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = finalize.kwlist(0); - - PyBobLearnEMPLDABaseObject* plda_base = 0; - PyObject* data = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!", kwlist, &PyBobLearnEMPLDABase_Type, &plda_base, - &PyList_Type, &data)) return 0; - - std::vector<blitz::Array<double,2> > data_vector; - if(list_as_vector(data ,data_vector)==0) - self->cxx->finalize(*plda_base->cxx, data_vector); - else - return 0; - - BOB_CATCH_MEMBER("cannot perform the finalize method", 0) - - Py_RETURN_NONE; -} - - - -/*** enroll ***/ -static auto enroll = bob::extension::FunctionDoc( - "enroll", - "Main procedure for enrolling a PLDAMachine", - "", - true -) -.add_prototype("plda_machine,data") -.add_parameter("plda_machine", ":py:class:`bob.learn.em.PLDAMachine`", "PLDAMachine Object") -.add_parameter("data", "list", ""); -static PyObject* PyBobLearnEMPLDATrainer_enroll(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwargs) { - BOB_TRY - - /* Parses input arguments in a single shot */ - char** kwlist = enroll.kwlist(0); - - PyBobLearnEMPLDAMachineObject* plda_machine = 0; - PyBlitzArrayObject* data = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O&", kwlist, &PyBobLearnEMPLDAMachine_Type, &plda_machine, - &PyBlitzArray_Converter, &data)) return 0; - - auto data_ = make_safe(data); - self->cxx->enroll(*plda_machine->cxx, *PyBlitzArrayCxx_AsBlitz<double,2>(data)); - - BOB_CATCH_MEMBER("cannot perform the enroll method", 0) - - Py_RETURN_NONE; -} - - -/*** is_similar_to ***/ -static auto is_similar_to = bob::extension::FunctionDoc( - "is_similar_to", - - "Compares this PLDATrainer with the ``other`` one to be approximately the same.", - "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " - "relative and absolute precision for the ``weights``, ``biases`` " - "and any other values internal to this machine." -) -.add_prototype("other, [r_epsilon], [a_epsilon]","output") -.add_parameter("other", ":py:class:`bob.learn.em.PLDAMachine`", "A PLDAMachine object to be compared.") -.add_parameter("r_epsilon", "float", "Relative precision.") -.add_parameter("a_epsilon", "float", "Absolute precision.") -.add_return("output","bool","True if it is similar, otherwise false."); -static PyObject* PyBobLearnEMPLDATrainer_IsSimilarTo(PyBobLearnEMPLDATrainerObject* self, PyObject* args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - char** kwlist = is_similar_to.kwlist(0); - - //PyObject* other = 0; - PyBobLearnEMPLDATrainerObject* other = 0; - double r_epsilon = 1.e-5; - double a_epsilon = 1.e-8; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, - &PyBobLearnEMPLDATrainer_Type, &other, - &r_epsilon, &a_epsilon)){ - - is_similar_to.print_usage(); - return 0; - } - - if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - - - -static PyMethodDef PyBobLearnEMPLDATrainer_methods[] = { - { - initialize.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_initialize, - METH_VARARGS|METH_KEYWORDS, - initialize.doc() - }, - { - e_step.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_e_step, - METH_VARARGS|METH_KEYWORDS, - e_step.doc() - }, - { - m_step.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_m_step, - METH_VARARGS|METH_KEYWORDS, - m_step.doc() - }, - { - finalize.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_finalize, - METH_VARARGS|METH_KEYWORDS, - finalize.doc() - }, - { - enroll.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_enroll, - METH_VARARGS|METH_KEYWORDS, - enroll.doc() - }, - { - is_similar_to.name(), - (PyCFunction)PyBobLearnEMPLDATrainer_IsSimilarTo, - METH_VARARGS|METH_KEYWORDS, - is_similar_to.doc() - }, - {0} /* Sentinel */ -}; - - -/******************************************************************/ -/************ Module Section **************************************/ -/******************************************************************/ - -// Define the Gaussian type struct; will be initialized later -PyTypeObject PyBobLearnEMPLDATrainer_Type = { - PyVarObject_HEAD_INIT(0,0) - 0 -}; - -bool init_BobLearnEMPLDATrainer(PyObject* module) -{ - // initialize the type struct - PyBobLearnEMPLDATrainer_Type.tp_name = PLDATrainer_doc.name(); - PyBobLearnEMPLDATrainer_Type.tp_basicsize = sizeof(PyBobLearnEMPLDATrainerObject); - PyBobLearnEMPLDATrainer_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;//Enable the class inheritance; - PyBobLearnEMPLDATrainer_Type.tp_doc = PLDATrainer_doc.doc(); - - // set the functions - PyBobLearnEMPLDATrainer_Type.tp_new = PyType_GenericNew; - PyBobLearnEMPLDATrainer_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnEMPLDATrainer_init); - PyBobLearnEMPLDATrainer_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnEMPLDATrainer_delete); - PyBobLearnEMPLDATrainer_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnEMPLDATrainer_RichCompare); - PyBobLearnEMPLDATrainer_Type.tp_methods = PyBobLearnEMPLDATrainer_methods; - PyBobLearnEMPLDATrainer_Type.tp_getset = PyBobLearnEMPLDATrainer_getseters; - //PyBobLearnEMPLDATrainer_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnEMPLDATrainer_compute_likelihood); - - - // check that everything is fine - if (PyType_Ready(&PyBobLearnEMPLDATrainer_Type) < 0) return false; - - // add the type to the module - Py_INCREF(&PyBobLearnEMPLDATrainer_Type); - return PyModule_AddObject(module, "PLDATrainer", (PyObject*)&PyBobLearnEMPLDATrainer_Type) >= 0; -} diff --git a/bob/learn/em/test/test_em.py b/bob/learn/em/test/test_em.py deleted file mode 100644 index 4c27813719eef7831d2ef3828c9fe3818b9b2018..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_em.py +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Francois Moulin <Francois.Moulin@idiap.ch> -# Tue May 10 11:35:58 2011 +0200 -# -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland - -"""Test trainer package -""" -import unittest -import numpy - -import bob.io.base -from bob.io.base.test_utils import datafile - -from bob.learn.em import KMeansMachine, GMMMachine, KMeansTrainer, \ - ML_GMMTrainer, MAP_GMMTrainer - -import bob.learn.em - -import bob.core -logger = bob.core.log.setup("bob.learn.em") - -#, MAP_GMMTrainer - -def loadGMM(): - gmm = GMMMachine(2, 2) - - gmm.weights = bob.io.base.load(datafile('gmm.init_weights.hdf5', __name__, path="../data/")) - gmm.means = bob.io.base.load(datafile('gmm.init_means.hdf5', __name__, path="../data/")) - gmm.variances = bob.io.base.load(datafile('gmm.init_variances.hdf5', __name__, path="../data/")) - #gmm.variance_thresholds = numpy.array([[0.001, 0.001],[0.001, 0.001]], 'float64') - - return gmm - -def equals(x, y, epsilon): - return (abs(x - y) < epsilon).all() - -class MyTrainer1(KMeansTrainer): - """Simple example of python trainer: """ - - def __init__(self): - KMeansTrainer.__init__(self) - - def train(self, machine, data): - a = numpy.ndarray((2, 2), 'float64') - a[0, :] = data[1] - a[1, :] = data[2] - machine.means = a - -def test_gmm_ML_1(): - - # Trains a GMMMachine with ML_GMMTrainer - - ar = bob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__, path="../data/")) - gmm = loadGMM() - - # test rng handling - ml_gmmtrainer = ML_GMMTrainer(True, True, True) - rng = bob.core.random.mt19937(12345) - bob.learn.em.train(ml_gmmtrainer, gmm, ar, convergence_threshold=0.001, rng=rng) - - gmm = loadGMM() - ml_gmmtrainer = ML_GMMTrainer(True, True, True) - #ml_gmmtrainer.train(gmm, ar) - bob.learn.em.train(ml_gmmtrainer, gmm, ar, convergence_threshold=0.001) - - #config = bob.io.base.HDF5File(datafile('gmm_ML.hdf5", __name__), 'w') - #gmm.save(config) - - gmm_ref = GMMMachine(bob.io.base.HDF5File(datafile('gmm_ML.hdf5', __name__, path="../data/"))) - gmm_ref_32bit_debug = GMMMachine(bob.io.base.HDF5File(datafile('gmm_ML_32bit_debug.hdf5', __name__, path="../data/"))) - gmm_ref_32bit_release = GMMMachine(bob.io.base.HDF5File(datafile('gmm_ML_32bit_release.hdf5', __name__, path="../data/"))) - - assert (gmm == gmm_ref) or (gmm == gmm_ref_32bit_release) or (gmm == gmm_ref_32bit_release) - - -def test_gmm_ML_2(): - - # Trains a GMMMachine with ML_GMMTrainer; compares to an old reference - - ar = bob.io.base.load(datafile('dataNormalized.hdf5', __name__, path="../data/")) - - # Initialize GMMMachine - gmm = GMMMachine(5, 45) - gmm.means = bob.io.base.load(datafile('meansAfterKMeans.hdf5', __name__, path="../data/")).astype('float64') - gmm.variances = bob.io.base.load(datafile('variancesAfterKMeans.hdf5', __name__, path="../data/")).astype('float64') - gmm.weights = numpy.exp(bob.io.base.load(datafile('weightsAfterKMeans.hdf5', __name__, path="../data/")).astype('float64')) - - threshold = 0.001 - gmm.set_variance_thresholds(threshold) - - # Initialize ML Trainer - prior = 0.001 - max_iter_gmm = 25 - accuracy = 0.00001 - ml_gmmtrainer = ML_GMMTrainer(True, True, True, prior) - - # Run ML - #ml_gmmtrainer.train(gmm, ar) - bob.learn.em.train(ml_gmmtrainer, gmm, ar, max_iterations = max_iter_gmm, convergence_threshold=accuracy) - - # Test results - # Load torch3vision reference - meansML_ref = bob.io.base.load(datafile('meansAfterML.hdf5', __name__, path="../data/")) - variancesML_ref = bob.io.base.load(datafile('variancesAfterML.hdf5', __name__, path="../data/")) - weightsML_ref = bob.io.base.load(datafile('weightsAfterML.hdf5', __name__, path="../data/")) - - - # Compare to current results - assert equals(gmm.means, meansML_ref, 3e-3) - assert equals(gmm.variances, variancesML_ref, 3e-3) - assert equals(gmm.weights, weightsML_ref, 1e-4) - - -def test_gmm_ML_parallel(): - - # Trains a GMMMachine with ML_GMMTrainer; compares to an old reference - - ar = bob.io.base.load(datafile('dataNormalized.hdf5', __name__, path="../data/")) - - # Initialize GMMMachine - gmm = GMMMachine(5, 45) - gmm.means = bob.io.base.load(datafile('meansAfterKMeans.hdf5', __name__, path="../data/")).astype('float64') - gmm.variances = bob.io.base.load(datafile('variancesAfterKMeans.hdf5', __name__, path="../data/")).astype('float64') - gmm.weights = numpy.exp(bob.io.base.load(datafile('weightsAfterKMeans.hdf5', __name__, path="../data/")).astype('float64')) - - threshold = 0.001 - gmm.set_variance_thresholds(threshold) - - # Initialize ML Trainer - prior = 0.001 - max_iter_gmm = 25 - accuracy = 0.00001 - ml_gmmtrainer = ML_GMMTrainer(True, True, True, prior) - - # Run ML - import multiprocessing.pool - pool = multiprocessing.pool.ThreadPool(3) -# pool = multiprocessing.Pool(1) - bob.learn.em.train(ml_gmmtrainer, gmm, ar, max_iterations = max_iter_gmm, convergence_threshold=accuracy, pool=pool) - - # Test results - # Load torch3vision reference - meansML_ref = bob.io.base.load(datafile('meansAfterML.hdf5', __name__, path="../data/")) - variancesML_ref = bob.io.base.load(datafile('variancesAfterML.hdf5', __name__, path="../data/")) - weightsML_ref = bob.io.base.load(datafile('weightsAfterML.hdf5', __name__, path="../data/")) - - # Compare to current results - assert equals(gmm.means, meansML_ref, 3e-3) - assert equals(gmm.variances, variancesML_ref, 3e-3) - assert equals(gmm.weights, weightsML_ref, 1e-4) - - - -def test_gmm_MAP_1(): - - # Train a GMMMachine with MAP_GMMTrainer - - ar = bob.io.base.load(datafile('faithful.torch3_f64.hdf5', __name__, path="../data/")) - - # test with rng - rng = bob.core.random.mt19937(12345) - gmm = GMMMachine(bob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__, path="../data/"))) - gmmprior = GMMMachine(bob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__, path="../data/"))) - map_gmmtrainer = MAP_GMMTrainer(update_means=True, update_variances=False, update_weights=False, prior_gmm=gmmprior, relevance_factor=4.) - bob.learn.em.train(map_gmmtrainer, gmm, ar, rng=rng) - - gmm = GMMMachine(bob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__, path="../data/"))) - gmmprior = GMMMachine(bob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__, path="../data/"))) - - map_gmmtrainer = MAP_GMMTrainer(update_means=True, update_variances=False, update_weights=False, prior_gmm=gmmprior, relevance_factor=4.) - - #map_gmmtrainer.train(gmm, ar) - bob.learn.em.train(map_gmmtrainer, gmm, ar) - - gmm_ref = GMMMachine(bob.io.base.HDF5File(datafile('gmm_MAP.hdf5', __name__, path="../data/"))) - - assert (equals(gmm.means,gmm_ref.means,1e-3) and equals(gmm.variances,gmm_ref.variances,1e-3) and equals(gmm.weights,gmm_ref.weights,1e-3)) - - -def test_gmm_MAP_2(): - - # Train a GMMMachine with MAP_GMMTrainer and compare with matlab reference - - data = bob.io.base.load(datafile('data.hdf5', __name__, path="../data/")) - data = data.reshape((1, data.shape[0])) # make a 2D array out of it - means = bob.io.base.load(datafile('means.hdf5', __name__, path="../data/")) - variances = bob.io.base.load(datafile('variances.hdf5', __name__, path="../data/")) - weights = bob.io.base.load(datafile('weights.hdf5', __name__, path="../data/")) - - gmm = GMMMachine(2,50) - gmm.means = means - gmm.variances = variances - gmm.weights = weights - - map_adapt = MAP_GMMTrainer(update_means=True, update_variances=False, update_weights=False, mean_var_update_responsibilities_threshold=0.,prior_gmm=gmm, relevance_factor=4.) - - gmm_adapted = GMMMachine(2,50) - gmm_adapted.means = means - gmm_adapted.variances = variances - gmm_adapted.weights = weights - - #map_adapt.max_iterations = 1 - #map_adapt.train(gmm_adapted, data) - bob.learn.em.train(map_adapt, gmm_adapted, data, max_iterations = 1) - - new_means = bob.io.base.load(datafile('new_adapted_mean.hdf5', __name__, path="../data/")) - - # print new_means[0,:] - # print gmm_adapted.means[:,0] - - # Compare to matlab reference - assert equals(new_means[0,:], gmm_adapted.means[:,0], 1e-4) - assert equals(new_means[1,:], gmm_adapted.means[:,1], 1e-4) - - -def test_gmm_MAP_3(): - - # Train a GMMMachine with MAP_GMMTrainer; compares to old reference - - ar = bob.io.base.load(datafile('dataforMAP.hdf5', __name__, path="../data/")) - - # Initialize GMMMachine - n_gaussians = 5 - n_inputs = 45 - prior_gmm = GMMMachine(n_gaussians, n_inputs) - prior_gmm.means = bob.io.base.load(datafile('meansAfterML.hdf5', __name__, path="../data/")) - prior_gmm.variances = bob.io.base.load(datafile('variancesAfterML.hdf5', __name__, path="../data/")) - prior_gmm.weights = bob.io.base.load(datafile('weightsAfterML.hdf5', __name__, path="../data/")) - - threshold = 0.001 - prior_gmm.set_variance_thresholds(threshold) - - # Initialize MAP Trainer - relevance_factor = 0.1 - prior = 0.001 - max_iter_gmm = 1 - accuracy = 0.00001 - map_factor = 0.5 - map_gmmtrainer = MAP_GMMTrainer(prior_gmm, alpha=map_factor, update_means=True, update_variances=False, update_weights=False, mean_var_update_responsibilities_threshold=accuracy) - #map_gmmtrainer.max_iterations = max_iter_gmm - #map_gmmtrainer.convergence_threshold = accuracy - - gmm = GMMMachine(n_gaussians, n_inputs) - gmm.set_variance_thresholds(threshold) - - # Train - #map_gmmtrainer.train(gmm, ar) - bob.learn.em.train(map_gmmtrainer, gmm, ar, max_iterations = max_iter_gmm, convergence_threshold=prior) - - # Test results - # Load torch3vision reference - meansMAP_ref = bob.io.base.load(datafile('meansAfterMAP.hdf5', __name__, path="../data/")) - variancesMAP_ref = bob.io.base.load(datafile('variancesAfterMAP.hdf5', __name__, path="../data/")) - weightsMAP_ref = bob.io.base.load(datafile('weightsAfterMAP.hdf5', __name__, path="../data/")) - - # Compare to current results - # Gaps are quite large. This might be explained by the fact that there is no - # adaptation of a given Gaussian in torch3 when the corresponding responsibilities - # are below the responsibilities threshold - assert equals(gmm.means, meansMAP_ref, 2e-1) - assert equals(gmm.variances, variancesMAP_ref, 1e-4) - assert equals(gmm.weights, weightsMAP_ref, 1e-4) - - -def test_gmm_test(): - - # Tests a GMMMachine by computing scores against a model and compare to - # an old reference - - ar = bob.io.base.load(datafile('dataforMAP.hdf5', __name__, path="../data/")) - - # Initialize GMMMachine - n_gaussians = 5 - n_inputs = 45 - gmm = GMMMachine(n_gaussians, n_inputs) - gmm.means = bob.io.base.load(datafile('meansAfterML.hdf5', __name__, path="../data/")) - gmm.variances = bob.io.base.load(datafile('variancesAfterML.hdf5', __name__, path="../data/")) - gmm.weights = bob.io.base.load(datafile('weightsAfterML.hdf5', __name__, path="../data/")) - - threshold = 0.001 - gmm.set_variance_thresholds(threshold) - - # Test against the model - score_mean_ref = -1.50379e+06 - score = 0. - for v in ar: score += gmm(v) - score /= len(ar) - - # Compare current results to torch3vision - assert abs(score-score_mean_ref)/score_mean_ref<1e-4 - - -def test_custom_trainer(): - - # Custom python trainer - - ar = bob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__, path="../data/")) - - mytrainer = MyTrainer1() - - machine = KMeansMachine(2, 2) - mytrainer.train(machine, ar) - - for i in range(0, 2): - assert (ar[i+1] == machine.means[i, :]).all() - - - -def test_EMPCA(): - - # Tests our Probabilistic PCA trainer for linear machines for a simple - # problem: - ar=numpy.array([ - [1, 2, 3], - [2, 4, 19], - [3, 6, 5], - [4, 8, 13], - ], dtype='float64') - - # Expected llh 1 and 2 (Reference values) - exp_llh1 = -32.8443 - exp_llh2 = -30.8559 - - # Do two iterations of EM to check the training procedure - T = bob.learn.em.EMPCATrainer() - m = bob.learn.linear.Machine(3,2) - # Initialization of the trainer - T.initialize(m, ar) - # Sets ('random') initialization values for test purposes - w_init = numpy.array([1.62945, 0.270954, 1.81158, 1.67002, 0.253974, - 1.93774], 'float64').reshape(3,2) - sigma2_init = 1.82675 - m.weights = w_init - T.sigma2 = sigma2_init - # Checks that the log likehood matches the reference one - # This should be sufficient to check everything as it requires to use - # the new value of W and sigma2 - # This does an E-Step, M-Step, computes the likelihood, and compares it to - # the reference value obtained using matlab - T.e_step(m, ar) - T.m_step(m, ar) - llh1 = T.compute_likelihood(m) - assert abs(exp_llh1 - llh1) < 2e-4 - T.e_step(m, ar) - T.m_step(m, ar) - llh2 = T.compute_likelihood(m) - assert abs(exp_llh2 - llh2) < 2e-4 - diff --git a/bob/learn/em/test/test_gaussian.py b/bob/learn/em/test/test_gaussian.py deleted file mode 100644 index 0e64705d0c74d2445d7887e8b10461edaee1d14a..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_gaussian.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Thu Feb 16 16:54:45 2012 +0200 -# -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland - -"""Tests the Gaussian machine -""" - -import os -import numpy -import tempfile - -import bob.io.base - -from bob.learn.em import Gaussian - -def equals(x, y, epsilon): - return (abs(x - y) < epsilon) - -def test_GaussianNormal(): - # Test the likelihood computation of a simple normal Gaussian - gaussian = Gaussian(2) - # By default, initialized with zero mean and unit variance - logLH = gaussian.log_likelihood(numpy.array([0.4, 0.2], 'float64')) - assert equals(logLH, -1.93787706641, 1e-10) - -def test_GaussianMachine(): - # Test a GaussianMachine more thoroughly - - # Initializes a Gaussian with zero mean and unit variance - g = Gaussian(3) - assert (g.mean == 0.0).all() - assert (g.variance == 1.0).all() - assert g.shape == (3,) - - # Set and check mean, variance, variance thresholds - mean = numpy.array([0, 1, 2], 'float64') - variance = numpy.array([3, 2, 1], 'float64') - g.mean = mean - g.variance = variance - g.set_variance_thresholds(0.0005) - assert (g.mean == mean).all() - assert (g.variance == variance).all() - assert (g.variance_thresholds == 0.0005).all() - - # Save and read from file - filename = str(tempfile.mkstemp(".hdf5")[1]) - g.save(bob.io.base.HDF5File(filename, 'w')) - g_loaded = Gaussian(bob.io.base.HDF5File(filename)) - assert g == g_loaded - assert (g != g_loaded ) is False - assert g.is_similar_to(g_loaded) - - # Save and read from file using the keyword argument - filename = str(tempfile.mkstemp(".hdf5")[1]) - g.save(hdf5=bob.io.base.HDF5File(filename, 'w')) - g_loaded = Gaussian(hdf5=bob.io.base.HDF5File(filename)) - assert g == g_loaded - assert (g != g_loaded ) is False - assert g.is_similar_to(g_loaded) - - # Save and loading from file using the keyword argument - filename = str(tempfile.mkstemp(".hdf5")[1]) - g.save(bob.io.base.HDF5File(filename, 'w')) - g_loaded = bob.learn.em.Gaussian() - g_loaded.load(bob.io.base.HDF5File(filename)) - assert g == g_loaded - assert (g != g_loaded ) is False - assert g.is_similar_to(g_loaded) - - # Save and loading from file using the keyword argument - filename = str(tempfile.mkstemp(".hdf5")[1]) - g.save(bob.io.base.HDF5File(filename, 'w')) - g_loaded = bob.learn.em.Gaussian() - g_loaded.load(hdf5=bob.io.base.HDF5File(filename)) - assert g == g_loaded - assert (g != g_loaded ) is False - assert g.is_similar_to(g_loaded) - - - # Make them different - g_loaded.set_variance_thresholds(0.001) - assert (g == g_loaded ) is False - assert g != g_loaded - - # Check likelihood computation - sample1 = numpy.array([0, 1, 2], 'float64') - sample2 = numpy.array([1, 2, 3], 'float64') - sample3 = numpy.array([2, 3, 4], 'float64') - ref1 = -3.652695334228046 - ref2 = -4.569362000894712 - ref3 = -7.319362000894712 - eps = 1e-10 - assert equals(g.log_likelihood(sample1), ref1, eps) - assert equals(g.log_likelihood(sample2), ref2, eps) - assert equals(g.log_likelihood(sample3), ref3, eps) - - # Check resize and assignment - g.resize(5) - assert g.shape == (5,) - g2 = Gaussian() - g2 = g - assert g == g2 - assert (g != g2 ) is False - g3 = Gaussian(g) - assert g == g3 - assert (g != g3 ) is False - - # Clean-up - os.unlink(filename) diff --git a/bob/learn/em/test/test_gmm.py b/bob/learn/em/test/test_gmm.py index 1733c6692104c6ab40b6e4df7f4ee8367b9fa80d..785b0ae760049013d889a3e8723ba099b73e35d4 100644 --- a/bob/learn/em/test/test_gmm.py +++ b/bob/learn/em/test/test_gmm.py @@ -8,122 +8,113 @@ """Tests the GMM machine and the GMMStats container """ +import numpy as np +import dask.array as da + import os -import numpy import tempfile +from copy import deepcopy +from h5py import File as HDF5File +from pkg_resources import resource_filename + +from bob.io.base import load as load_array -import bob.io.base -from bob.io.base.test_utils import datafile +from bob.learn.em.mixture import GMMMachine +from bob.learn.em.mixture import GMMStats -from bob.learn.em import GMMStats, GMMMachine +from bob.learn.em.cluster import KMeansMachine def test_GMMStats(): # Test a GMMStats # Initializes a GMMStats - gs = GMMStats(2,3) + n_gaussians = 2 + n_features = 3 + gs = GMMStats(n_gaussians,n_features) log_likelihood = -3. T = 57 - n = numpy.array([4.37, 5.31], 'float64') - sumpx = numpy.array([[1., 2., 3.], [4., 5., 6.]], 'float64') - sumpxx = numpy.array([[10., 20., 30.], [40., 50., 60.]], 'float64') + n = np.array([4.37, 5.31], "float64") + sumpx = np.array([[1., 2., 3.], [4., 5., 6.]], "float64") + sumpxx = np.array([[10., 20., 30.], [40., 50., 60.]], "float64") gs.log_likelihood = log_likelihood gs.t = T gs.n = n gs.sum_px = sumpx gs.sum_pxx = sumpxx - assert gs.log_likelihood == log_likelihood - assert gs.t == T - assert (gs.n == n).all() - assert (gs.sum_px == sumpx).all() - assert (gs.sum_pxx == sumpxx).all() - assert gs.shape==(2,3) - - # Saves and reads from file + np.testing.assert_equal(gs.log_likelihood, log_likelihood) + np.testing.assert_equal(gs.t, T) + np.testing.assert_equal(gs.n, n) + np.testing.assert_equal(gs.sum_px, sumpx) + np.testing.assert_equal(gs.sum_pxx, sumpxx) + np.testing.assert_equal(gs.shape, (n_gaussians, n_features)) + + # Saves and reads from file using `from_hdf5` filename = str(tempfile.mkstemp(".hdf5")[1]) - gs.save(bob.io.base.HDF5File(filename, 'w')) - gs_loaded = GMMStats(bob.io.base.HDF5File(filename)) - assert gs == gs_loaded - assert (gs != gs_loaded ) is False - assert gs.is_similar_to(gs_loaded) - - # Saves and reads from file using the keyword argument - filename = str(tempfile.mkstemp(".hdf5")[1]) - gs.save(hdf5=bob.io.base.HDF5File(filename, 'w')) - gs_loaded = GMMStats(bob.io.base.HDF5File(filename)) + gs.save(HDF5File(filename, "w")) + gs_loaded = GMMStats.from_hdf5(HDF5File(filename, "r")) assert gs == gs_loaded assert (gs != gs_loaded ) is False assert gs.is_similar_to(gs_loaded) - # Saves and load from file using the keyword argument + # Saves and load from file using `load` filename = str(tempfile.mkstemp(".hdf5")[1]) - gs.save(hdf5=bob.io.base.HDF5File(filename, 'w')) - gs_loaded = GMMStats() - gs_loaded.load(bob.io.base.HDF5File(filename)) + gs.save(hdf5=HDF5File(filename, "w")) + gs_loaded = GMMStats(n_gaussians, n_features) + gs_loaded.load(HDF5File(filename, "r")) assert gs == gs_loaded assert (gs != gs_loaded ) is False assert gs.is_similar_to(gs_loaded) - # Saves and load from file using the keyword argument - filename = str(tempfile.mkstemp(".hdf5")[1]) - gs.save(hdf5=bob.io.base.HDF5File(filename, 'w')) - gs_loaded = GMMStats() - gs_loaded.load(hdf5=bob.io.base.HDF5File(filename)) - assert gs == gs_loaded - assert (gs != gs_loaded ) is False - assert gs.is_similar_to(gs_loaded) - - # Makes them different gs_loaded.t = 58 assert (gs == gs_loaded ) is False assert gs != gs_loaded - assert (gs.is_similar_to(gs_loaded)) is False + assert not (gs.is_similar_to(gs_loaded)) + # Accumulates from another GMMStats - gs2 = GMMStats(2,3) + gs2 = GMMStats(n_gaussians, n_features) gs2.log_likelihood = log_likelihood gs2.t = T - gs2.n = n - gs2.sum_px = sumpx - gs2.sum_pxx = sumpxx + gs2.n = n.copy() + gs2.sum_px = sumpx.copy() + gs2.sum_pxx = sumpxx.copy() gs2 += gs - eps = 1e-8 - assert gs2.log_likelihood == 2*log_likelihood - assert gs2.t == 2*T - assert numpy.allclose(gs2.n, 2*n, eps) - assert numpy.allclose(gs2.sum_px, 2*sumpx, eps) - assert numpy.allclose(gs2.sum_pxx, 2*sumpxx, eps) - - # Reinit and checks for zeros - gs_loaded.init() - assert gs_loaded.log_likelihood == 0 - assert gs_loaded.t == 0 - assert (gs_loaded.n == 0).all() - assert (gs_loaded.sum_px == 0).all() - assert (gs_loaded.sum_pxx == 0).all() + np.testing.assert_equal(gs2.log_likelihood, 2*log_likelihood) + np.testing.assert_equal(gs2.t, 2*T) + np.testing.assert_almost_equal(gs2.n, 2*n, decimal=8) + np.testing.assert_almost_equal(gs2.sum_px, 2*sumpx, decimal=8) + np.testing.assert_almost_equal(gs2.sum_pxx, 2*sumpxx, decimal=8) + + # Re-init and checks for zeros + gs_loaded.init_fields() + np.testing.assert_equal(gs_loaded.log_likelihood, 0) + np.testing.assert_equal(gs_loaded.t, 0) + np.testing.assert_equal(gs_loaded.n, np.zeros((n_gaussians,))) + np.testing.assert_equal(gs_loaded.sum_px, np.zeros((n_gaussians, n_features))) + np.testing.assert_equal(gs_loaded.sum_pxx, np.zeros((n_gaussians, n_features))) # Resize and checks size - assert gs_loaded.shape==(2,3) - gs_loaded.resize(4,5) - assert gs_loaded.shape==(4,5) + assert gs_loaded.shape==(n_gaussians, n_features) + gs_loaded.resize(4,5) + assert gs_loaded.shape == (4,5) assert gs_loaded.sum_px.shape[0] == 4 assert gs_loaded.sum_px.shape[1] == 5 # Clean-up os.unlink(filename) -def test_GMMMachine_1(): +def test_GMMMachine(): # Test a GMMMachine basic features - weights = numpy.array([0.5, 0.5], 'float64') - weights2 = numpy.array([0.6, 0.4], 'float64') - means = numpy.array([[3, 70, 0], [4, 72, 0]], 'float64') - means2 = numpy.array([[3, 7, 0], [4, 72, 0]], 'float64') - variances = numpy.array([[1, 10, 1], [2, 5, 2]], 'float64') - variances2 = numpy.array([[10, 10, 1], [2, 5, 2]], 'float64') - varianceThresholds = numpy.array([[0, 0, 0], [0, 0, 0]], 'float64') - varianceThresholds2 = numpy.array([[0.0005, 0.0005, 0.0005], [0, 0, 0]], 'float64') + weights = np.array([0.5, 0.5], "float64") + weights2 = np.array([0.6, 0.4], "float64") + means = np.array([[3, 70, 0], [4, 72, 0]], "float64") + means2 = np.array([[3, 7, 0], [4, 72, 0]], "float64") + variances = np.array([[1, 10, 1], [2, 5, 2]], "float64") + variances2 = np.array([[10, 10, 1], [2, 5, 2]], "float64") + varianceThresholds = np.array([[0, 0, 0], [0, 0, 0]], "float64") + varianceThresholds2 = np.array([[0.0005, 0.0005, 0.0005], [0, 0, 0]], "float64") # Initializes a GMMMachine - gmm = GMMMachine(2,3) + gmm = GMMMachine(n_gaussians=2) # Sets the weights, means, variances and varianceThresholds and # Checks correctness gmm.weights = weights @@ -131,61 +122,49 @@ def test_GMMMachine_1(): gmm.variances = variances gmm.variance_thresholds = varianceThresholds assert gmm.shape == (2,3) - assert (gmm.weights == weights).all() - assert (gmm.means == means).all() - assert (gmm.variances == variances).all() - assert (gmm.variance_thresholds == varianceThresholds).all() - - # Checks supervector-like accesses - assert (gmm.mean_supervector == means.reshape(means.size)).all() - assert (gmm.variance_supervector == variances.reshape(variances.size)).all() - newMeans = numpy.array([[3, 70, 2], [4, 72, 2]], 'float64') - newVariances = numpy.array([[1, 1, 1], [2, 2, 2]], 'float64') + np.testing.assert_equal(gmm.weights, weights) + np.testing.assert_equal(gmm.means, means) + np.testing.assert_equal(gmm.variances, variances) + np.testing.assert_equal(gmm.variance_thresholds, varianceThresholds) + newMeans = np.array([[3, 70, 2], [4, 72, 2]], "float64") + newVariances = np.array([[1, 1, 1], [2, 2, 2]], "float64") # Checks particular varianceThresholds-related methods - varianceThresholds1D = numpy.array([0.3, 1, 0.5], 'float64') - gmm.set_variance_thresholds(varianceThresholds1D) - assert (gmm.variance_thresholds[0,:] == varianceThresholds1D).all() - assert (gmm.variance_thresholds[1,:] == varianceThresholds1D).all() + varianceThresholds1D = np.array([0.3, 1, 0.5], "float64") + gmm.variance_thresholds = varianceThresholds1D + np.testing.assert_equal(gmm.variance_thresholds, varianceThresholds1D) - gmm.set_variance_thresholds(0.005) - assert (gmm.variance_thresholds == 0.005).all() + gmm.variance_thresholds = 0.005 + np.testing.assert_equal(gmm.variance_thresholds, 0.005) - # Checks Gaussians access gmm.means = newMeans gmm.variances = newVariances - assert (gmm.get_gaussian(0).mean == newMeans[0,:]).all() - assert (gmm.get_gaussian(1).mean == newMeans[1,:]).all() - assert (gmm.get_gaussian(0).variance == newVariances[0,:]).all() - assert (gmm.get_gaussian(1).variance == newVariances[1,:]).all() - - # Checks resize - gmm.resize(4,5) - assert gmm.shape == (4,5) + np.testing.assert_equal(gmm.means, newMeans) + np.testing.assert_equal(gmm.variances, newVariances) # Checks comparison - gmm2 = GMMMachine(gmm) - gmm3 = GMMMachine(2,3) + gmm2 = deepcopy(gmm) + gmm3 = GMMMachine(n_gaussians=2) gmm3.weights = weights2 gmm3.means = means gmm3.variances = variances - #gmm3.varianceThresholds = varianceThresholds - gmm4 = GMMMachine(2,3) + gmm3.variance_thresholds = varianceThresholds + gmm4 = GMMMachine(n_gaussians=2) gmm4.weights = weights gmm4.means = means2 gmm4.variances = variances - #gmm4.varianceThresholds = varianceThresholds - gmm5 = GMMMachine(2,3) + gmm4.variance_thresholds = varianceThresholds + gmm5 = GMMMachine(n_gaussians=2) gmm5.weights = weights gmm5.means = means gmm5.variances = variances2 - #gmm5.varianceThresholds = varianceThresholds - gmm6 = GMMMachine(2,3) + gmm5.variance_thresholds = varianceThresholds + gmm6 = GMMMachine(n_gaussians=2) gmm6.weights = weights gmm6.means = means gmm6.variances = variances - #gmm6.varianceThresholds = varianceThresholds2 + gmm6.variance_thresholds = varianceThresholds2 assert gmm == gmm2 assert (gmm != gmm2) is False @@ -203,61 +182,705 @@ def test_GMMMachine_1(): assert (gmm == gmm6) is False assert gmm.is_similar_to(gmm6) is False -def test_GMMMachine_2(): - # Test a GMMMachine (statistics) + # Saving and loading + filename = tempfile.mkstemp(suffix=".hdf5")[1] + gmm.save(HDF5File(filename, "w")) + gmm1 = GMMMachine.from_hdf5(HDF5File(filename, "r")) + assert gmm == gmm1 + gmm.save(filename) + gmm1 = GMMMachine.from_hdf5(filename) + assert gmm == gmm1 + os.unlink(filename) + + # Weights + n_gaussians = 5 + machine = GMMMachine(n_gaussians) + + default_weights = np.full(shape=(n_gaussians,), fill_value=1.0 / n_gaussians) + default_log_weights = np.full( + shape=(n_gaussians,), fill_value=np.log(1.0 / n_gaussians) + ) + + # Test weights getting and setting + np.testing.assert_almost_equal(machine.weights, default_weights) + np.testing.assert_almost_equal(machine.log_weights, default_log_weights) + + modified_weights = default_weights + modified_weights[: n_gaussians // 2] = (1 / n_gaussians) / 2 + modified_weights[n_gaussians // 2 + n_gaussians % 2 :] = (1 / n_gaussians) * 1.5 + + # Ensure setter works (log_weights is updated correctly) + machine.weights = modified_weights + np.testing.assert_almost_equal(machine.weights, modified_weights) + np.testing.assert_almost_equal(machine.log_weights, np.log(modified_weights)) + - arrayset = bob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__, path="../data/")) - gmm = GMMMachine(2, 2) - gmm.weights = numpy.array([0.5, 0.5], 'float64') - gmm.means = numpy.array([[3, 70], [4, 72]], 'float64') - gmm.variances = numpy.array([[1, 10], [2, 5]], 'float64') - gmm.variance_thresholds = numpy.array([[0, 0], [0, 0]], 'float64') +def test_GMMMachine_stats(): + """Tests a GMMMachine (statistics)""" - stats = GMMStats(2, 2) - gmm.acc_statistics(arrayset, stats) + arrayset = load_array(resource_filename("bob.learn.em", "data/faithful.torch3_f64.hdf5")) + gmm = GMMMachine(n_gaussians=2) + gmm.weights = np.array([0.5, 0.5], "float64") + gmm.means = np.array([[3, 70], [4, 72]], "float64") + gmm.variances = np.array([[1, 10], [2, 5]], "float64") + gmm.variance_thresholds = np.array([[0, 0], [0, 0]], "float64") - stats_ref = GMMStats(bob.io.base.HDF5File(datafile("stats.hdf5",__name__, path="../data/"))) + stats = gmm.acc_statistics(arrayset) - assert stats.t == stats_ref.t - assert numpy.allclose(stats.n, stats_ref.n, atol=1e-10) - #assert numpy.array_equal(stats.sumPx, stats_ref.sumPx) - #Note AA: precision error above - assert numpy.allclose(stats.sum_px, stats_ref.sum_px, atol=1e-10) - assert numpy.allclose(stats.sum_pxx, stats_ref.sum_pxx, atol=1e-10) + stats_ref = GMMStats(n_gaussians=2, n_features=2) + stats_ref.load(HDF5File(resource_filename("bob.learn.em", "data/stats.hdf5"), "r")) -def test_GMMMachine_3(): - # Test a GMMMachine (log-likelihood computation) + np.testing.assert_equal(stats.t, stats_ref.t) + np.testing.assert_almost_equal(stats.n, stats_ref.n, decimal=10) + # np.testing.assert_equal(stats.sum_px, stats_ref.sum_px) + # Note AA: precision error above + np.testing.assert_almost_equal(stats.sum_px, stats_ref.sum_px, decimal=10) + np.testing.assert_almost_equal(stats.sum_pxx, stats_ref.sum_pxx, decimal=10) - data = bob.io.base.load(datafile('data.hdf5', __name__, path="../data/")) - gmm = GMMMachine(2, 50) - gmm.weights = bob.io.base.load(datafile('weights.hdf5', __name__, path="../data/")) - gmm.means = bob.io.base.load(datafile('means.hdf5', __name__, path="../data/")) - gmm.variances = bob.io.base.load(datafile('variances.hdf5', __name__, path="../data/")) - # Compare the log-likelihood with the one obtained using Chris Matlab - # implementation +def test_GMMMachine_ll_computation(): + """Test a GMMMachine (log-likelihood computation)""" + + data = load_array(resource_filename("bob.learn.em", "data/data.hdf5")) + gmm = GMMMachine(n_gaussians=2) + gmm.weights = load_array(resource_filename("bob.learn.em", "data/weights.hdf5")) + gmm.means = load_array(resource_filename("bob.learn.em", "data/means.hdf5")) + gmm.variances = load_array(resource_filename("bob.learn.em", "data/variances.hdf5")) + + # Compare the log-likelihood with the one obtained using Chris Matlab implementation matlab_ll_ref = -2.361583051672024e+02 - assert abs(gmm(data) - matlab_ll_ref) < 1e-10 - - -def test_GMMMachine_4(): + np.testing.assert_almost_equal(gmm.log_likelihood(data), matlab_ll_ref, decimal=10) + - import numpy - numpy.random.seed(3) # FIXING A SEED +def test_GMMMachine_single_ll_vs_multiple(): - data = numpy.random.rand(100,50) #Doesn't matter if it is ramdom. The average of 1D array (in python) MUST output the same result for the 2D array (in C++) - - gmm = GMMMachine(2, 50) - gmm.weights = bob.io.base.load(datafile('weights.hdf5', __name__, path="../data/")) - gmm.means = bob.io.base.load(datafile('means.hdf5', __name__, path="../data/")) - gmm.variances = bob.io.base.load(datafile('variances.hdf5', __name__, path="../data/")) + np.random.seed(3) # FIXING A SEED + + data = np.random.rand(100,50) # Doesn't matter if it is random. The average of 1D array (in python) MUST output the same result for the 2D array (in C++) + + gmm = GMMMachine(n_gaussians=2) + gmm.weights = load_array(resource_filename("bob.learn.em", "data/weights.hdf5")) + gmm.means = load_array(resource_filename("bob.learn.em", "data/means.hdf5")) + gmm.variances = load_array(resource_filename("bob.learn.em", "data/variances.hdf5")) ll = 0 for i in range(data.shape[0]): - ll += gmm(data[i,:]) + ll += gmm.log_likelihood(data[i,:]) ll /= data.shape[0] - - assert ll==gmm(data) - - + + assert np.isclose(ll, gmm.log_likelihood(data).mean()) + + +def test_GMMStats_operations(): + """Test a GMMStats.""" + # Initializing a GMMStats + data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [7, 8, 9]]) + n_gaussians = 2 + n_features = data.shape[-1] + machine = GMMMachine(n_gaussians) + + machine.means = np.array([[0, 0, 0], [8, 8, 8]]) + machine.variances = np.ones_like(machine.means) + + # Populate the GMMStats + stats = machine.acc_statistics(data) + + # Check shapes + assert stats.n.shape == (n_gaussians,), stats.n.shape + assert stats.sum_px.shape == (n_gaussians, n_features), stats.sum_px.shape + assert stats.sum_pxx.shape == (n_gaussians, n_features), stats.sum_pxx.shape + + # Check values + expected_ll = -37.2998511206581 + expected_n = np.array([1, 3]) + expected_sumPx = np.array([[1, 2, 3], [18, 21, 24]]) + expected_sumPxx = np.array([[1, 4, 9], [114, 153, 198]]) + + np.testing.assert_almost_equal(stats.log_likelihood, expected_ll) + assert stats.t == data.shape[0] + np.testing.assert_almost_equal(stats.n, expected_n) + np.testing.assert_almost_equal(stats.sum_px, expected_sumPx) + np.testing.assert_almost_equal(stats.sum_pxx, expected_sumPxx) + + # Adding Statistics + new_stats = stats + stats + + new_expected_ll = expected_ll * 2 + new_expected_n = expected_n * 2 + new_expected_sumPx = expected_sumPx * 2 + new_expected_sumPxx = expected_sumPxx * 2 + + assert new_stats.n.shape == (n_gaussians,), new_stats.n.shape + assert new_stats.sum_px.shape == (n_gaussians, n_features), new_stats.sum_px.shape + assert new_stats.sum_pxx.shape == (n_gaussians, n_features), new_stats.sum_pxx.shape + + np.testing.assert_almost_equal(new_stats.log_likelihood, new_expected_ll) + assert new_stats.t == data.shape[0] * 2 + np.testing.assert_almost_equal(new_stats.n, new_expected_n) + np.testing.assert_almost_equal(new_stats.sum_px, new_expected_sumPx) + np.testing.assert_almost_equal(new_stats.sum_pxx, new_expected_sumPxx) + + # In-place adding of Statistics + new_stats += stats + + new_expected_ll += expected_ll + new_expected_n += expected_n + new_expected_sumPx += expected_sumPx + new_expected_sumPxx += expected_sumPxx + + assert new_stats.n.shape == (n_gaussians,), new_stats.n.shape + assert new_stats.sum_px.shape == (n_gaussians, n_features), new_stats.sum_px.shape + assert new_stats.sum_pxx.shape == (n_gaussians, n_features), new_stats.sum_pxx.shape + + np.testing.assert_almost_equal(new_stats.log_likelihood, new_expected_ll) + assert new_stats.t == data.shape[0] * 3 + np.testing.assert_almost_equal(new_stats.n, new_expected_n) + np.testing.assert_almost_equal(new_stats.sum_px, new_expected_sumPx) + np.testing.assert_almost_equal(new_stats.sum_pxx, new_expected_sumPxx) + + +def test_machine_parameters(): + n_gaussians = 3 + n_features = 2 + machine = GMMMachine(n_gaussians) + machine.means = np.repeat([[0], [1], [-1]], n_features, 1) + machine.variances = np.ones_like(machine.means) + np.testing.assert_equal(machine.means, np.repeat([[0], [1], [-1]], n_features, 1)) + np.testing.assert_equal(machine.variances, np.ones((n_gaussians, n_features))) + + # Setters + new_means = np.repeat([[1], [2], [3]], n_features, axis=1) + machine.means = new_means + assert machine.means.shape == (n_gaussians, n_features) + np.testing.assert_equal(machine.means, new_means) + new_variances = np.repeat([[0.2], [1.1], [1]], n_features, axis=1) + machine.variances = new_variances + assert machine.variances.shape == (n_gaussians, n_features) + np.testing.assert_equal(machine.variances, new_variances) + + +def test_gmm_kmeans_plusplus_init(): + n_gaussians = 3 + machine = GMMMachine( + n_gaussians, + k_means_trainer=KMeansMachine(n_clusters=n_gaussians, init_method="k-means++"), + ) + data = np.array([[1.5, 1], [1, 1.5], [-1, 0.5], [-1.5, 0], [2, 2], [2.5, 2.5]]) + machine = machine.fit(data) + expected_means = np.array([[2.25, 2.25], [-1.25, 0.25], [1.25, 1.25]]) + expected_variances = np.array([[1/16, 1/16], [1/16, 1/16], [1/16, 1/16]]) + np.testing.assert_almost_equal(machine.means, expected_means, decimal=3) + np.testing.assert_almost_equal(machine.variances, expected_variances) + + +def test_gmm_kmeans_parallel_init(): + n_gaussians = 3 + machine = GMMMachine( + n_gaussians, + k_means_trainer=KMeansMachine(n_clusters=n_gaussians, init_method="k-means||"), + ) + data = np.array([[1.5, 1], [1, 1.5], [-1, 0.5], [-1.5, 0], [2, 2], [2.5, 2.5]]) + machine = machine.fit(data) + expected_means = np.array([[1.25, 1.25], [-1.25, 0.25], [2.25, 2.25]]) + expected_variances = np.array([[1/16, 1/16], [1/16, 1/16], [1/16, 1/16]]) + np.testing.assert_almost_equal(machine.means, expected_means, decimal=3) + np.testing.assert_almost_equal(machine.variances, expected_variances) + + +def test_likelihood(): + data = np.array([[1, 1, 1], [-1, 0, 0], [0, 0, 1], [2, 2, 2]]) + n_gaussians = 3 + machine = GMMMachine(n_gaussians) + machine.means = np.repeat([[0], [1], [-1]], 3, 1) + machine.variances = np.ones_like(machine.means) + log_likelihood = machine.log_likelihood(data) + expected_ll = np.array( + [-3.6519900964986527, -3.83151883210222, -3.83151883210222, -5.344374066745753] + ) + np.testing.assert_almost_equal(log_likelihood, expected_ll) + + +def test_likelihood_variance(): + data = np.array([[1, 1, 1], [-1, 0, 0], [0, 0, 1], [2, 2, 2]]) + n_gaussians = 3 + machine = GMMMachine(n_gaussians) + machine.means = np.repeat([[0], [1], [-1]], 3, 1) + machine.variances = np.array([ + [1.1, 1.2, 0.8], + [0.2, 0.4, 0.5], + [1, 1, 1], + ]) + log_likelihood = machine.log_likelihood(data) + expected_ll = np.array( + [ + -2.202846959440514, + -3.8699524542323793, + -4.229029034375473, + -6.940892214952679, + ] + ) + np.testing.assert_almost_equal(log_likelihood, expected_ll) + + +def test_likelihood_weight(): + data = np.array([[1, 1, 1], [-1, 0, 0], [0, 0, 1], [2, 2, 2]]) + n_gaussians = 3 + machine = GMMMachine(n_gaussians) + machine.means = np.repeat([[0], [1], [-1]], 3, 1) + machine.variances = np.ones_like(machine.means) + machine.weights = [0.6, 0.1, 0.3] + log_likelihood = machine.log_likelihood(data) + expected_ll = np.array( + [-4.206596356117164, -3.492325679996329, -3.634745457950943, -6.49485678536014] + ) + np.testing.assert_almost_equal(log_likelihood, expected_ll) + + +def test_GMMMachine_object(): + n_gaussians = 5 + machine = GMMMachine(n_gaussians) + + default_weights = np.full(shape=(n_gaussians,), fill_value=1.0 / n_gaussians) + default_log_weights = np.full( + shape=(n_gaussians,), fill_value=np.log(1.0 / n_gaussians) + ) + + # Test weights getting and setting + np.testing.assert_almost_equal(machine.weights, default_weights) + np.testing.assert_almost_equal(machine.log_weights, default_log_weights) + + modified_weights = default_weights + modified_weights[: n_gaussians // 2] = (1 / n_gaussians) / 2 + modified_weights[n_gaussians // 2 + n_gaussians % 2 :] = (1 / n_gaussians) * 1.5 + + # Ensure setter works (log_weights is updated correctly) + machine.weights = modified_weights + np.testing.assert_almost_equal(machine.weights, modified_weights) + np.testing.assert_almost_equal(machine.log_weights, np.log(modified_weights)) + + +def test_ml_em(): + # Simple GMM test + data = np.array([[1, 2, 2], [2, 1, 2], [7, 8, 9], [7, 7, 8], [7, 9, 7]]) + n_gaussians = 2 + n_features = data.shape[-1] + + machine = GMMMachine(n_gaussians, update_means=True, update_variances=True, update_weights=True) + machine.means = np.repeat([[2], [8]], n_features, 1) + machine.variances = np.ones_like(machine.means) + + stats = machine.e_step( data) + machine.m_step(stats) + + expected_means = np.array([[1.5, 1.5, 2.0], [7.0, 8.0, 8.0]]) + np.testing.assert_almost_equal(machine.means, expected_means) + expected_weights = np.array([2/5, 3/5]) + np.testing.assert_almost_equal(machine.weights, expected_weights) + eps = np.finfo(float).eps + expected_variances = np.array([[1/4, 1/4, eps], [eps, 2/3, 2/3]]) + np.testing.assert_almost_equal(machine.variances, expected_variances) + + +def test_map_em(): + n_gaussians = 2 + prior_machine = GMMMachine(n_gaussians) + prior_machine.means = np.array([[2, 2, 2], [8, 8, 8]]) + prior_machine.variances = np.ones_like(prior_machine.means) + prior_machine.weights = np.array([0.5, 0.5]) + + machine = GMMMachine(n_gaussians, trainer="map", ubm=prior_machine, update_means=True, update_variances=True, update_weights=True) + + post_data = np.array([[1, 2, 2], [2, 1, 2], [7, 8, 9], [7, 7, 8], [7, 9, 7]]) + + machine.initialize_gaussians(None) + + # Machine equals to priors before fitting + np.testing.assert_equal(machine.means, prior_machine.means) + np.testing.assert_equal(machine.variances, prior_machine.variances) + np.testing.assert_equal(machine.weights, prior_machine.weights) + + stats = machine.e_step(post_data) + machine.m_step(stats) + + expected_means = np.array([ + [1.83333333, 1.83333333, 2.], + [7.57142857, 8, 8] + ]) + np.testing.assert_almost_equal(machine.means, expected_means) + eps = np.finfo(float).eps + expected_vars = np.array([[eps, eps, eps], [eps, eps, eps]]) + np.testing.assert_almost_equal(machine.variances, expected_vars) + expected_weights = np.array([0.46226415, 0.53773585]) + np.testing.assert_almost_equal(machine.weights, expected_weights) + + +def test_ml_transformer(): + data = np.array([[1, 2, 2], [2, 1, 2], [7, 8, 9], [7, 7, 8], [7, 9, 7]]) + test_data = np.array([[1, 1, 1], [1, 1, 2], [8, 9, 9], [8, 8, 8]]) + n_gaussians = 2 + n_features = 3 + + machine = GMMMachine(n_gaussians, update_means=True, update_variances=True, update_weights=True) + machine.means = np.array([[2, 2, 2], [8, 8, 8]]) + machine.variances = np.ones_like(machine.means) + + machine = machine.fit(data) + + expected_means = np.array([[1.5, 1.5, 2.0], [7.0, 8.0, 8.0]]) + np.testing.assert_almost_equal(machine.means, expected_means) + expected_weights = np.array([2/5, 3/5]) + np.testing.assert_almost_equal(machine.weights, expected_weights) + eps = np.finfo(float).eps + expected_variances = np.array([[1/4, 1/4, eps], [eps, 2/3, 2/3]]) + np.testing.assert_almost_equal(machine.variances, expected_variances) + + stats = machine.transform(test_data) + + expected_stats = GMMStats(n_gaussians, n_features) + expected_stats.init_fields( + log_likelihood=-6755399441055685.0, + t=test_data.shape[0], + n=np.array([2, 2], dtype=float), + sum_px=np.array([[2, 2, 3], [16, 17, 17]], dtype=float), + sum_pxx=np.array([[2, 2, 5], [128, 145, 145]], dtype=float), + ) + assert stats.is_similar_to(expected_stats) + + +def test_map_transformer(): + post_data = np.array([[1, 2, 2], [2, 1, 2], [7, 8, 9], [7, 7, 8], [7, 9, 7]]) + test_data = np.array([[1, 1, 1], [1, 1, 2], [8, 9, 9], [8, 8, 8]]) + n_gaussians = 2 + n_features = 3 + prior_machine = GMMMachine(n_gaussians) + prior_machine.means = np.array([[2, 2, 2], [8, 8, 8]]) + prior_machine.variances = np.ones_like(prior_machine.means) + prior_machine.weights = np.array([0.5, 0.5]) + + machine = GMMMachine(n_gaussians, trainer="map", ubm=prior_machine, update_means=True, update_variances=True, update_weights=True) + + machine = machine.fit(post_data) + + expected_means = np.array([ + [1.83333333, 1.83333333, 2.], + [7.57142857, 8, 8] + ]) + np.testing.assert_almost_equal(machine.means, expected_means) + eps = np.finfo(float).eps + expected_vars = np.array([[eps, eps, eps], [eps, eps, eps]]) + np.testing.assert_almost_equal(machine.variances, expected_vars) + expected_weights = np.array([0.46226415, 0.53773585]) + np.testing.assert_almost_equal(machine.weights, expected_weights) + + stats = machine.transform(test_data) + + expected_stats = GMMStats(n_gaussians, n_features) + expected_stats.init_fields( + log_likelihood=-1.3837590691807108e+16, + t=test_data.shape[0], + n=np.array([2, 2], dtype=float), + sum_px=np.array([[2, 2, 3], [16, 17, 17]], dtype=float), + sum_pxx=np.array([[2, 2, 5], [128, 145, 145]], dtype=float), + ) + assert stats.is_similar_to(expected_stats) + + +## Tests from `test_em.py` + +def loadGMM(): + gmm = GMMMachine(n_gaussians=2) + + gmm.weights = load_array(resource_filename("bob.learn.em", "data/gmm.init_weights.hdf5")) + gmm.means = load_array(resource_filename("bob.learn.em", "data/gmm.init_means.hdf5")) + gmm.variances = load_array(resource_filename("bob.learn.em", "data/gmm.init_variances.hdf5")) + + return gmm + +def test_gmm_ML_1(): + """Trains a GMMMachine with ML_GMMTrainer""" + ar = load_array(resource_filename("bob.learn.em", "data/faithful.torch3_f64.hdf5")) + gmm = loadGMM() + + # test rng handling + gmm.convergence_threshold = 0.001 + gmm.update_means = True + gmm.update_variances = True + gmm.update_weights = True + gmm.random_state = np.random.RandomState(seed=12345) + gmm = gmm.fit(ar) + + gmm = loadGMM() + gmm.convergence_threshold = 0.001 + gmm.update_means = True + gmm.update_variances = True + gmm.update_weights = True + gmm = gmm.fit(ar) + + # Generate reference + # gmm.save(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "w")) + + gmm_ref = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "r")) + assert gmm == gmm_ref + + +def test_gmm_ML_2(): + """Trains a GMMMachine with ML_GMMTrainer; compares to a reference""" + ar = load_array(resource_filename("bob.learn.em", "data/dataNormalized.hdf5")) + + # Initialize GMMMachine + gmm = GMMMachine(n_gaussians=5) + gmm.means = load_array(resource_filename("bob.learn.em", "data/meansAfterKMeans.hdf5")).astype("float64") + gmm.variances = load_array(resource_filename("bob.learn.em", "data/variancesAfterKMeans.hdf5")).astype("float64") + gmm.weights = np.exp(load_array(resource_filename("bob.learn.em", "data/weightsAfterKMeans.hdf5")).astype("float64")) + + threshold = 0.001 + gmm.variance_thresholds = threshold + + # Initialize ML Trainer + gmm.mean_var_update_threshold = 0.001 + gmm.max_fitting_steps = 25 + gmm.convergence_threshold = 0.000001 + gmm.update_means = True + gmm.update_variances = True + gmm.update_weights = True + + # Run ML + gmm = gmm.fit(ar) + # Test results + # Load torch3vision reference + meansML_ref = load_array(resource_filename("bob.learn.em", "data/meansAfterML.hdf5")) + variancesML_ref = load_array(resource_filename("bob.learn.em", "data/variancesAfterML.hdf5")) + weightsML_ref = load_array(resource_filename("bob.learn.em", "data/weightsAfterML.hdf5")) + + # Compare to current results + np.testing.assert_allclose(gmm.means, meansML_ref, atol=3e-3) + np.testing.assert_allclose(gmm.variances, variancesML_ref, atol=3e-3) + np.testing.assert_allclose(gmm.weights, weightsML_ref, atol=1e-4) + + +def test_gmm_MAP_1(): + """Train a GMMMachine with MAP_GMMTrainer""" + ar = load_array(resource_filename("bob.learn.em", "data/faithful.torch3_f64.hdf5")) + + # test with rng + gmmprior = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "r")) + gmm = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "r"), ubm = gmmprior) + gmm.update_means = True + gmm.update_variances = False + gmm.update_weights = False + rng = np.random.RandomState(seed=12345) + gmm.random_state = rng + gmm = gmm.fit(ar) + + gmmprior = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "r")) + gmm = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_ML.hdf5"), "r"), ubm = gmmprior) + gmm.update_means = True + gmm.update_variances = False + gmm.update_weights = False + + gmm = gmm.fit(ar) + + # Generate reference + # gmm.save(HDF5File(resource_filename("bob.learn.em", "data/gmm_MAP.hdf5"), "w")) + + gmm_ref = GMMMachine.from_hdf5(HDF5File(resource_filename("bob.learn.em", "data/gmm_MAP.hdf5"), "r")) + + np.testing.assert_almost_equal(gmm.means, gmm_ref.means, decimal=3) + np.testing.assert_almost_equal(gmm.variances, gmm_ref.variances, decimal=3) + np.testing.assert_almost_equal(gmm.weights, gmm_ref.weights, decimal=3) + + +def test_gmm_MAP_2(): + """Train a GMMMachine with MAP_GMMTrainer and compare with matlab reference""" + + data = load_array(resource_filename("bob.learn.em", "data/data.hdf5")) + data = data.reshape((1, -1)) # make a 2D array out of it + means = load_array(resource_filename("bob.learn.em", "data/means.hdf5")) + variances = load_array(resource_filename("bob.learn.em", "data/variances.hdf5")) + weights = load_array(resource_filename("bob.learn.em", "data/weights.hdf5")) + + gmm = GMMMachine(n_gaussians=2) + gmm.means = means + gmm.variances = variances + gmm.weights = weights + + gmm_adapted = GMMMachine( + n_gaussians=2, + trainer="map", + ubm=gmm, + max_fitting_steps=1, + update_means=True, + update_variances=False, + update_weights=False, + mean_var_update_threshold=0., + ) + gmm_adapted.means = means + gmm_adapted.variances = variances + gmm_adapted.weights = weights + + gmm_adapted = gmm_adapted.fit(data) + + new_means = load_array(resource_filename("bob.learn.em", "data/new_adapted_mean.hdf5")) + + # Compare to matlab reference + np.testing.assert_allclose(new_means.T, gmm_adapted.means, rtol=1e-4) + + +def test_gmm_MAP_3(): + """Train a GMMMachine with MAP_GMMTrainer; compares to old reference""" + ar = load_array(resource_filename("bob.learn.em", "data/dataforMAP.hdf5")) + + # Initialize GMMMachine + n_gaussians = 5 + prior_gmm = GMMMachine(n_gaussians) + prior_gmm.means = load_array(resource_filename("bob.learn.em", "data/meansAfterML.hdf5")) + prior_gmm.variances = load_array(resource_filename("bob.learn.em", "data/variancesAfterML.hdf5")) + prior_gmm.weights = load_array(resource_filename("bob.learn.em", "data/weightsAfterML.hdf5")) + + threshold = 0.001 + prior_gmm.variance_thresholds = threshold + + # Initialize MAP Trainer + prior = 0.001 + accuracy = 0.00001 + gmm = GMMMachine( + n_gaussians, + trainer="map", + ubm=prior_gmm, + convergence_threshold=prior, + max_fitting_steps=1, + update_means=True, + update_variances=False, + update_weights=False, + mean_var_update_threshold=accuracy, + relevance_factor=None, + ) + gmm.variance_thresholds = threshold + + # Train + gmm = gmm.fit(ar) + + # Test results + # Load torch3vision reference + meansMAP_ref = load_array(resource_filename("bob.learn.em", "data/meansAfterMAP.hdf5")) + variancesMAP_ref = load_array(resource_filename("bob.learn.em", "data/variancesAfterMAP.hdf5")) + weightsMAP_ref = load_array(resource_filename("bob.learn.em", "data/weightsAfterMAP.hdf5")) + + # Compare to current results + # Gaps are quite large. This might be explained by the fact that there is no + # adaptation of a given Gaussian in torch3 when the corresponding responsibilities + # are below the responsibilities threshold + np.testing.assert_allclose(gmm.means, meansMAP_ref, atol=2e-1) + np.testing.assert_allclose(gmm.variances, variancesMAP_ref, atol=1e-4) + np.testing.assert_allclose(gmm.weights, weightsMAP_ref, atol=1e-4) + + +def test_gmm_test(): + """Tests a GMMMachine by computing scores against a model and comparing to a reference + """ + + ar = load_array(resource_filename("bob.learn.em", "data/dataforMAP.hdf5")) + + # Initialize GMMMachine + n_gaussians = 5 + gmm = GMMMachine(n_gaussians) + gmm.means = load_array(resource_filename("bob.learn.em", "data/meansAfterML.hdf5")) + gmm.variances = load_array(resource_filename("bob.learn.em", "data/variancesAfterML.hdf5")) + gmm.weights = load_array(resource_filename("bob.learn.em", "data/weightsAfterML.hdf5")) + + threshold = 0.001 + gmm.variance_thresholds = threshold + + # Test against the model + score_mean_ref = -1.50379e+06 + score = gmm.log_likelihood(ar).sum() + score /= len(ar) + + # Compare current results to torch3vision + assert abs(score-score_mean_ref)/score_mean_ref<1e-4 + + +def test_gmm_ML_dask(): + """Trains a GMMMachine with dask array data; compares to a reference""" + + ar = da.array(load_array(resource_filename("bob.learn.em", "data/dataNormalized.hdf5"))) + + # Initialize GMMMachine + gmm = GMMMachine(n_gaussians=5) + gmm.means = load_array(resource_filename("bob.learn.em", "data/meansAfterKMeans.hdf5")).astype("float64") + gmm.variances = load_array(resource_filename("bob.learn.em", "data/variancesAfterKMeans.hdf5")).astype("float64") + gmm.weights = np.exp(load_array(resource_filename("bob.learn.em", "data/weightsAfterKMeans.hdf5")).astype("float64")) + + threshold = 0.001 + gmm.variance_thresholds = threshold + + # Initialize ML Trainer + gmm.mean_var_update_threshold = 0.001 + gmm.max_fitting_steps = 25 + gmm.convergence_threshold = 0.00001 + gmm.update_means = True + gmm.update_variances = True + gmm.update_weights = True + + # Run ML + gmm.fit(ar) + + # Test results + # Load torch3vision reference + meansML_ref = load_array(resource_filename("bob.learn.em", "data/meansAfterML.hdf5")) + variancesML_ref = load_array(resource_filename("bob.learn.em", "data/variancesAfterML.hdf5")) + weightsML_ref = load_array(resource_filename("bob.learn.em", "data/weightsAfterML.hdf5")) + + # Compare to current results + np.testing.assert_allclose(gmm.means, meansML_ref, atol=3e-3) + np.testing.assert_allclose(gmm.variances, variancesML_ref, atol=3e-3) + np.testing.assert_allclose(gmm.weights, weightsML_ref, atol=1e-4) + +def test_gmm_MAP_dask(): + """Test a GMMMachine for MAP with a dask array as data.""" + ar = da.array(load_array(resource_filename("bob.learn.em", "data/dataforMAP.hdf5"))) + + # Initialize GMMMachine + n_gaussians = 5 + prior_gmm = GMMMachine(n_gaussians) + prior_gmm.means = load_array(resource_filename("bob.learn.em", "data/meansAfterML.hdf5")) + prior_gmm.variances = load_array(resource_filename("bob.learn.em", "data/variancesAfterML.hdf5")) + prior_gmm.weights = load_array(resource_filename("bob.learn.em", "data/weightsAfterML.hdf5")) + + threshold = 0.001 + prior_gmm.variance_thresholds = threshold + + # Initialize MAP Trainer + prior = 0.001 + accuracy = 0.00001 + gmm = GMMMachine( + n_gaussians, + trainer="map", + ubm=prior_gmm, + convergence_threshold=prior, + max_fitting_steps=1, + update_means=True, + update_variances=False, + update_weights=False, + mean_var_update_threshold=accuracy, + relevance_factor=None, + ) + gmm.variance_thresholds = threshold + + # Train + gmm = gmm.fit(ar) + + # Test results + # Load torch3vision reference + meansMAP_ref = load_array(resource_filename("bob.learn.em", "data/meansAfterMAP.hdf5")) + variancesMAP_ref = load_array(resource_filename("bob.learn.em", "data/variancesAfterMAP.hdf5")) + weightsMAP_ref = load_array(resource_filename("bob.learn.em", "data/weightsAfterMAP.hdf5")) + + # Compare to current results + # Gaps are quite large. This might be explained by the fact that there is no + # adaptation of a given Gaussian in torch3 when the corresponding responsibilities + # are below the responsibilities threshold + np.testing.assert_allclose(gmm.means, meansMAP_ref, atol=2e-1) + np.testing.assert_allclose(gmm.variances, variancesMAP_ref, atol=1e-4) + np.testing.assert_allclose(gmm.weights, weightsMAP_ref, atol=1e-4) diff --git a/bob/learn/em/test/test_ivector.py b/bob/learn/em/test/test_ivector.py deleted file mode 100644 index 6c7e63c98191c8da6fceee6a3626fcafb11d42ab..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_ivector.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Mon Apr 2 11:19:00 2013 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - - -"""Tests the I-Vector machine -""" - -import numpy -import numpy.linalg -import numpy.random - -from bob.learn.em import GMMMachine, GMMStats, IVectorMachine - - -### Test class inspired by an implementation of Chris McCool -### Chris McCool (chris.mccool@nicta.com.au) -class IVectorMachinePy(): - """An IVector extractor""" - - def __init__(self, ubm=None, dim_t=1): - # Our state - self.m_ubm = ubm - self.m_dim_t = dim_t - # Resize the matrices T and sigma - self.resize() - # Precompute - self.precompute() - - def resize(self): - if self.m_ubm: - dim_cd = self.m_ubm.shape[0] * self.m_ubm.shape[1] - self.m_t = numpy.random.randn(dim_cd, self.m_dim_t) - self.m_sigma = numpy.random.randn(dim_cd) - - def precompute(self): - if self.m_ubm and self.m_t is not None and self.m_sigma is not None: - #dim_c = self.m_ubm.dim_c - #dim_d = self.m_ubm.dim_d - dim_c,dim_d = self.m_ubm.shape - self.m_cache_TtSigmaInv = {} - self.m_cache_TtSigmaInvT = {} - for c in range(dim_c): - start = c*dim_d - end = (c+1)*dim_d - Tc = self.m_t[start:end,:] - self.m_cache_TtSigmaInv[c] = Tc.transpose() / self.m_sigma[start:end] - self.m_cache_TtSigmaInvT[c] = numpy.dot(self.m_cache_TtSigmaInv[c], Tc); - - def set_ubm(self, ubm): - self.m_ubm = ubm - # Precompute - self.precompute() - - def get_ubm(self): - return self.m_ubm - - def set_t(self, t): - # @warning: no dimensions check - self.m_t = t - # Precompute - self.precompute() - - def get_t(self): - return self.m_t - - def set_sigma(self, sigma): - # @warning: no dimensions check - self.m_sigma = sigma - # Precompute - self.precompute() - - def get_sigma(self): - return self.m_sigma - - - def _get_TtSigmaInv_Fnorm(self, N, F): - # Initialization - #dim_c = self.m_ubm.dim_c - #dim_d = self.m_ubm.dim_d - dim_c,dim_d = self.m_ubm.shape - mean_supervector = self.m_ubm.mean_supervector - TtSigmaInv_Fnorm = numpy.zeros(shape=(self.m_dim_t,), dtype=numpy.float64) - - # Loop over each Gaussian component - dim_c = self.m_ubm.shape[0] - for c in range(dim_c): - start = c*dim_d - end = (c+1)*dim_d - Fnorm = F[c,:] - N[c] * mean_supervector[start:end] - TtSigmaInv_Fnorm = TtSigmaInv_Fnorm + numpy.dot(self.m_cache_TtSigmaInv[c], Fnorm) - return TtSigmaInv_Fnorm - - def _get_I_TtSigmaInvNT(self, N): - # Initialization - #dim_c = self.m_ubm.dim_c - #dim_d = self.m_ubm.dim_d - dim_c, dim_d = self.m_ubm.shape - - TtSigmaInvNT = numpy.eye(self.m_dim_t, dtype=numpy.float64) - for c in range(dim_c): - TtSigmaInvNT = TtSigmaInvNT + self.m_cache_TtSigmaInvT[c] * N[c] - - return TtSigmaInvNT - - def project(self, gmmstats): - if self.m_ubm and self.m_t is not None and self.m_sigma is not None: - N = gmmstats.n - F = gmmstats.sum_px - - TtSigmaInv_Fnorm = self._get_TtSigmaInv_Fnorm(N, F) - TtSigmaInvNT = self._get_I_TtSigmaInvNT(N) - - return numpy.linalg.solve(TtSigmaInvNT, TtSigmaInv_Fnorm) - - -def test_machine(): - - # Ubm - ubm = GMMMachine(2,3) - ubm.weights = numpy.array([0.4,0.6]) - ubm.means = numpy.array([[1.,7,4],[4,5,3]]) - ubm.variances = numpy.array([[0.5,1.,1.5],[1.,1.5,2.]]) - - # Defines GMMStats - gs = GMMStats(2,3) - log_likelihood = -3. - T = 1 - n = numpy.array([0.4, 0.6], numpy.float64) - sumpx = numpy.array([[1., 2., 3.], [2., 4., 3.]], numpy.float64) - sumpxx = numpy.array([[10., 20., 30.], [40., 50., 60.]], numpy.float64) - gs.log_likelihood = log_likelihood - gs.t = T - gs.n = n - gs.sum_px = sumpx - gs.sum_pxx = sumpxx - - # IVector (Python) - m = IVectorMachinePy(ubm, 2) - t = numpy.array([[1.,2],[4,1],[0,3],[5,8],[7,10],[11,1]]) - m.set_t(t) - sigma = numpy.array([1.,2.,1.,3.,2.,4.]) - m.set_sigma(sigma) - - wij_ref = numpy.array([-0.04213415, 0.21463343]) # Reference from original Chris implementation - wij = m.project(gs) - assert numpy.allclose(wij_ref, wij, 1e-5) - - # IVector (C++) - mc = IVectorMachine(ubm, 2) - mc.t = t - mc.sigma = sigma - - wij_ref = numpy.array([-0.04213415, 0.21463343]) # Reference from original Chris implementation - wij = mc.project(gs) - assert numpy.allclose(wij_ref, wij, 1e-5) diff --git a/bob/learn/em/test/test_ivector_trainer.py b/bob/learn/em/test/test_ivector_trainer.py deleted file mode 100644 index 195988291d63b6c4d4c5c7b183a3df84880b88d8..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_ivector_trainer.py +++ /dev/null @@ -1,468 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Tests the I-Vector trainer -""" - -import numpy -import numpy.linalg -import numpy.random -import nose.tools - -from bob.learn.em import GMMMachine, GMMStats, IVectorMachine, IVectorTrainer -import bob.learn.em - -### Test class inspired by an implementation of Chris McCool -### Chris McCool (chris.mccool@nicta.com.au) -class IVectorTrainerPy(): - """An IVector extractor""" - - def __init__(self, convergence_threshold=0.001, max_iterations=10, - compute_likelihood=False, sigma_update=False, variance_floor=1e-5): - self.m_convergence_threshold = convergence_threshold - self.m_max_iterations = max_iterations - self.m_compute_likelihood = compute_likelihood - self.m_sigma_update = sigma_update - self.m_variance_floor = variance_floor - - def initialize(self, machine, data): - ubm = machine.ubm - self.m_dim_c = ubm.shape[0] - self.m_dim_d = ubm.shape[1] - self.m_dim_t = machine.t.shape[1] - self.m_meansupervector = ubm.mean_supervector - t = numpy.random.randn(self.m_dim_c*self.m_dim_d, self.m_dim_t) - machine.t = t - machine.sigma = machine.ubm.variance_supervector - - def e_step(self, machine, data): - n_samples = len(data) - self.m_acc_Nij_Sigma_wij2 = {} - self.m_acc_Fnorm_Sigma_wij = {} - self.m_acc_Snorm = numpy.zeros(shape=(self.m_dim_c*self.m_dim_d,), dtype=numpy.float64) - self.m_N = numpy.zeros(shape=(self.m_dim_c,), dtype=numpy.float64) - - for c in range(self.m_dim_c): - self.m_acc_Nij_Sigma_wij2[c] = numpy.zeros(shape=(self.m_dim_t,self.m_dim_t), dtype=numpy.float64) - self.m_acc_Fnorm_Sigma_wij[c] = numpy.zeros(shape=(self.m_dim_d,self.m_dim_t), dtype=numpy.float64) - - for n in range(n_samples): - Nij = data[n].n - Fij = data[n].sum_px - Sij = data[n].sum_pxx - - # Estimate latent variables - TtSigmaInv_Fnorm = machine.__compute_TtSigmaInvFnorm__(data[n]) - I_TtSigmaInvNT = machine.__compute_Id_TtSigmaInvT__(data[n]) - - Fnorm = numpy.zeros(shape=(self.m_dim_c*self.m_dim_d,), dtype=numpy.float64) - Snorm = numpy.zeros(shape=(self.m_dim_c*self.m_dim_d,), dtype=numpy.float64) - - # Compute normalized statistics - for c in range(self.m_dim_c): - start = c*self.m_dim_d - end = (c+1)*self.m_dim_d - - Fc = Fij[c,:] - Sc = Sij[c,:] - mc = self.m_meansupervector[start:end] - - Fc_mc = Fc * mc - Nc_mc_mcT = Nij[c] * mc * mc - - Fnorm[start:end] = Fc - Nij[c] * mc - Snorm[start:end] = Sc - (2 * Fc_mc) + Nc_mc_mcT - - # Latent variables - I_TtSigmaInvNT_inv = numpy.linalg.inv(I_TtSigmaInvNT) - E_w_ij = numpy.dot(I_TtSigmaInvNT_inv, TtSigmaInv_Fnorm) - E_w_ij2 = I_TtSigmaInvNT_inv + numpy.outer(E_w_ij, E_w_ij) - - # Do the accumulation for each component - self.m_acc_Snorm = self.m_acc_Snorm + Snorm # (dim_c*dim_d) - for c in range(self.m_dim_c): - start = c*self.m_dim_d - end = (c+1)*self.m_dim_d - current_Fnorm = Fnorm[start:end] # (dim_d) - self.m_acc_Nij_Sigma_wij2[c] = self.m_acc_Nij_Sigma_wij2[c] + Nij[c] * E_w_ij2 # (dim_t, dim_t) - self.m_acc_Fnorm_Sigma_wij[c] = self.m_acc_Fnorm_Sigma_wij[c] + numpy.outer(current_Fnorm, E_w_ij) # (dim_d, dim_t) - self.m_N[c] = self.m_N[c] + Nij[c] - - - def m_step(self, machine, data): - A = self.m_acc_Nij_Sigma_wij2 - - T = numpy.zeros(shape=(self.m_dim_c*self.m_dim_d,self.m_dim_t), dtype=numpy.float64) - Told = machine.t - if self.m_sigma_update: - sigma = numpy.zeros(shape=self.m_acc_Snorm.shape, dtype=numpy.float64) - for c in range(self.m_dim_c): - start = c*self.m_dim_d; - end = (c+1)*self.m_dim_d; - # T update - A = self.m_acc_Nij_Sigma_wij2[c].transpose() - B = self.m_acc_Fnorm_Sigma_wij[c].transpose() - if numpy.array_equal(A, numpy.zeros(A.shape)): - X = numpy.zeros(shape=(self.m_dim_t,self.m_dim_d), dtype=numpy.float64) - else: - X = numpy.linalg.solve(A, B) - T[start:end,:] = X.transpose() - # Sigma update - if self.m_sigma_update: - Told_c = Told[start:end,:].transpose() - # warning: Use of the new T estimate! (revert second next line if you don't want that) - Fnorm_Ewij_Tt = numpy.diag(numpy.dot(self.m_acc_Fnorm_Sigma_wij[c], X)) - #Fnorm_Ewij_Tt = numpy.diag(numpy.dot(self.m_acc_Fnorm_Sigma_wij[c], Told_c)) - sigma[start:end] = (self.m_acc_Snorm[start:end] - Fnorm_Ewij_Tt) / self.m_N[c] - - machine.t = T - if self.m_sigma_update: - sigma[sigma < self.m_variance_floor] = self.m_variance_floor - machine.sigma = sigma - - def finalize(self, machine, data): - pass - - def train(self, machine, data): - self.initialize(machine, data) - average_output_previous = -sys.maxsize - average_output = -sys.maxsize - self.e_step(machine, data) - - i = 0 - while True: - average_output_previous = average_output - self.m_step(machine, data) - self.e_step(machine, data) - if(self.m_max_iterations > 0 and i+1 >= self.m_max_iterations): - break - i += 1 - - -def test_trainer_nosigma(): - # Ubm - ubm = GMMMachine(2,3) - ubm.weights = numpy.array([0.4,0.6]) - ubm.means = numpy.array([[1.,7,4],[4,5,3]]) - ubm.variances = numpy.array([[0.5,1.,1.5],[1.,1.5,2.]]) - - # Defines GMMStats - gs1 = GMMStats(2,3) - log_likelihood1 = -3. - T1 = 1 - n1 = numpy.array([0.4, 0.6], numpy.float64) - sumpx1 = numpy.array([[1., 2., 3.], [2., 4., 3.]], numpy.float64) - sumpxx1 = numpy.array([[10., 20., 30.], [40., 50., 60.]], numpy.float64) - gs1.log_likelihood = log_likelihood1 - gs1.t = T1 - gs1.n = n1 - gs1.sum_px = sumpx1 - gs1.sum_pxx = sumpxx1 - - gs2 = GMMStats(2,3) - log_likelihood2 = -4. - T2 = 1 - n2 = numpy.array([0.2, 0.8], numpy.float64) - sumpx2 = numpy.array([[2., 1., 3.], [3., 4.1, 3.2]], numpy.float64) - sumpxx2 = numpy.array([[12., 15., 25.], [39., 51., 62.]], numpy.float64) - gs2.log_likelihood = log_likelihood2 - gs2.t = T2 - gs2.n = n2 - gs2.sum_px = sumpx2 - gs2.sum_pxx = sumpxx2 - - data = [gs1, gs2] - - - acc_Nij_Sigma_wij2_ref1 = {0: numpy.array([[ 0.03202305, -0.02947769], [-0.02947769, 0.0561132 ]]), - 1: numpy.array([[ 0.07953279, -0.07829414], [-0.07829414, 0.13814242]])} - acc_Fnorm_Sigma_wij_ref1 = {0: numpy.array([[-0.29622691, 0.61411796], [ 0.09391764, -0.27955961], [-0.39014455, 0.89367757]]), - 1: numpy.array([[ 0.04695882, -0.13977981], [-0.05718673, 0.24159665], [-0.17098161, 0.47326585]])} - acc_Snorm_ref1 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref1 = numpy.array([0.6, 1.4]) - t_ref1 = numpy.array([[ 1.59543739, 11.78239235], [ -3.20130371, -6.66379081], [ 4.79674111, 18.44618316], - [ -0.91765407, -1.5319461 ], [ 2.26805901, 3.03434944], [ 2.76600031, 4.9935962 ]]) - - acc_Nij_Sigma_wij2_ref2 = {0: numpy.array([[ 0.37558389, -0.15405228], [-0.15405228, 0.1421269 ]]), - 1: numpy.array([[ 1.02076081, -0.57683953], [-0.57683953, 0.53912239]])} - acc_Fnorm_Sigma_wij_ref2 = {0: numpy.array([[-1.1261668 , 1.46496753], [-0.03579289, -0.37875811], [-1.09037391, 1.84372565]]), - 1: numpy.array([[-0.01789645, -0.18937906], [ 0.35221084, 0.15854126], [-0.10004552, 0.72559036]])} - acc_Snorm_ref2 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref2 = numpy.array([0.6, 1.4]) - t_ref2 = numpy.array([[ 2.2133685, 12.70654597], [ -2.13959381, -4.98404887], [ 4.35296231, 17.69059484], - [ -0.54644055, -0.93594252], [ 1.29308324, 1.67762053], [ 1.67583072, 3.13894546]]) - acc_Nij_Sigma_wij2_ref = [acc_Nij_Sigma_wij2_ref1, acc_Nij_Sigma_wij2_ref2] - acc_Fnorm_Sigma_wij_ref = [acc_Fnorm_Sigma_wij_ref1, acc_Fnorm_Sigma_wij_ref2] - acc_Snorm_ref = [acc_Snorm_ref1, acc_Snorm_ref2] - N_ref = [N_ref1, N_ref2] - t_ref = [t_ref1, t_ref2] - - # Python implementation - # Machine - m = IVectorMachine(ubm, 2) - t = numpy.array([[1.,2],[4,1],[0,3],[5,8],[7,10],[11,1]]) - sigma = numpy.array([1.,2.,1.,3.,2.,4.]) - - # Initialization - trainer = IVectorTrainerPy() - trainer.initialize(m, data) - m.t = t - m.sigma = sigma - for it in range(2): - # E-Step - trainer.e_step(m, data) - for k in acc_Nij_Sigma_wij2_ref[it]: - assert numpy.allclose(acc_Nij_Sigma_wij2_ref[it][k], trainer.m_acc_Nij_Sigma_wij2[k], 1e-5) - for k in acc_Fnorm_Sigma_wij_ref[it]: - assert numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.m_acc_Fnorm_Sigma_wij[k], 1e-5) - assert numpy.allclose(acc_Snorm_ref[it], trainer.m_acc_Snorm, 1e-5) - assert numpy.allclose(N_ref[it], trainer.m_N, 1e-5) - - # M-Step - trainer.m_step(m, data) - assert numpy.allclose(t_ref[it], m.t, 1e-5) - - # C++ implementation - # Machine - m = IVectorMachine(ubm, 2) - - # Initialization - trainer = IVectorTrainer() - trainer.initialize(m) - m.t = t - m.sigma = sigma - for it in range(2): - # E-Step - trainer.e_step(m, data) - for k in acc_Nij_Sigma_wij2_ref[it]: - assert numpy.allclose(acc_Nij_Sigma_wij2_ref[it][k], trainer.acc_nij_wij2[k], 1e-5) - for k in acc_Fnorm_Sigma_wij_ref[it]: - assert numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.acc_fnormij_wij[k], 1e-5) - - # M-Step - trainer.m_step(m) - assert numpy.allclose(t_ref[it], m.t, 1e-5) - - - #testing exceptions - nose.tools.assert_raises(RuntimeError, trainer.e_step, m, [1,2,2]) - - -def test_trainer_update_sigma(): - # Ubm - dim_c = 2 - dim_d = 3 - ubm = GMMMachine(dim_c,dim_d) - ubm.weights = numpy.array([0.4,0.6]) - ubm.means = numpy.array([[1.,7,4],[4,5,3]]) - ubm.variances = numpy.array([[0.5,1.,1.5],[1.,1.5,2.]]) - - # Defines GMMStats - gs1 = GMMStats(dim_c,dim_d) - log_likelihood1 = -3. - T1 = 1 - n1 = numpy.array([0.4, 0.6], numpy.float64) - sumpx1 = numpy.array([[1., 2., 3.], [2., 4., 3.]], numpy.float64) - sumpxx1 = numpy.array([[10., 20., 30.], [40., 50., 60.]], numpy.float64) - gs1.log_likelihood = log_likelihood1 - gs1.t = T1 - gs1.n = n1 - gs1.sum_px = sumpx1 - gs1.sum_pxx = sumpxx1 - - gs2 = GMMStats(dim_c,dim_d) - log_likelihood2 = -4. - T2 = 1 - n2 = numpy.array([0.2, 0.8], numpy.float64) - sumpx2 = numpy.array([[2., 1., 3.], [3., 4.1, 3.2]], numpy.float64) - sumpxx2 = numpy.array([[12., 15., 25.], [39., 51., 62.]], numpy.float64) - gs2.log_likelihood = log_likelihood2 - gs2.t = T2 - gs2.n = n2 - gs2.sum_px = sumpx2 - gs2.sum_pxx = sumpxx2 - - data = [gs1, gs2] - - # Reference values - acc_Nij_Sigma_wij2_ref1 = {0: numpy.array([[ 0.03202305, -0.02947769], [-0.02947769, 0.0561132 ]]), - 1: numpy.array([[ 0.07953279, -0.07829414], [-0.07829414, 0.13814242]])} - acc_Fnorm_Sigma_wij_ref1 = {0: numpy.array([[-0.29622691, 0.61411796], [ 0.09391764, -0.27955961], [-0.39014455, 0.89367757]]), - 1: numpy.array([[ 0.04695882, -0.13977981], [-0.05718673, 0.24159665], [-0.17098161, 0.47326585]])} - acc_Snorm_ref1 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref1 = numpy.array([0.6, 1.4]) - t_ref1 = numpy.array([[ 1.59543739, 11.78239235], [ -3.20130371, -6.66379081], [ 4.79674111, 18.44618316], - [ -0.91765407, -1.5319461 ], [ 2.26805901, 3.03434944], [ 2.76600031, 4.9935962 ]]) - sigma_ref1 = numpy.array([ 16.39472121, 34.72955353, 3.3108037, 43.73496916, 38.85472445, 68.22116903]) - - acc_Nij_Sigma_wij2_ref2 = {0: numpy.array([[ 0.50807426, -0.11907756], [-0.11907756, 0.12336544]]), - 1: numpy.array([[ 1.18602399, -0.2835859 ], [-0.2835859 , 0.39440498]])} - acc_Fnorm_Sigma_wij_ref2 = {0: numpy.array([[ 0.07221453, 1.1189786 ], [-0.08681275, -0.35396112], [ 0.15902728, 1.47293972]]), - 1: numpy.array([[-0.04340637, -0.17698056], [ 0.10662127, 0.21484933],[ 0.13116645, 0.64474271]])} - acc_Snorm_ref2 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref2 = numpy.array([0.6, 1.4]) - t_ref2 = numpy.array([[ 2.93105054, 11.89961223], [ -1.08988119, -3.92120757], [ 4.02093173, 15.82081981], - [ -0.17376634, -0.57366984], [ 0.26585634, 0.73589952], [ 0.60557877, 2.07014704]]) - sigma_ref2 = numpy.array([5.12154025e+00, 3.48623823e+01, 1.00000000e-05, 4.37792350e+01, 3.91525332e+01, 6.85613258e+01]) - - acc_Nij_Sigma_wij2_ref = [acc_Nij_Sigma_wij2_ref1, acc_Nij_Sigma_wij2_ref2] - acc_Fnorm_Sigma_wij_ref = [acc_Fnorm_Sigma_wij_ref1, acc_Fnorm_Sigma_wij_ref2] - acc_Snorm_ref = [acc_Snorm_ref1, acc_Snorm_ref2] - N_ref = [N_ref1, N_ref2] - t_ref = [t_ref1, t_ref2] - sigma_ref = [sigma_ref1, sigma_ref2] - - - # Python implementation - # Machine - m = IVectorMachine(ubm, 2) - t = numpy.array([[1.,2],[4,1],[0,3],[5,8],[7,10],[11,1]]) - sigma = numpy.array([1.,2.,1.,3.,2.,4.]) - - # Initialization - trainer = IVectorTrainerPy(sigma_update=True) - trainer.initialize(m, data) - m.t = t - m.sigma = sigma - for it in range(2): - # E-Step - trainer.e_step(m, data) - for k in acc_Nij_Sigma_wij2_ref[it]: - assert numpy.allclose(acc_Nij_Sigma_wij2_ref[it][k], trainer.m_acc_Nij_Sigma_wij2[k], 1e-5) - for k in acc_Fnorm_Sigma_wij_ref[it]: - assert numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.m_acc_Fnorm_Sigma_wij[k], 1e-5) - assert numpy.allclose(acc_Snorm_ref[it], trainer.m_acc_Snorm, 1e-5) - assert numpy.allclose(N_ref[it], trainer.m_N, 1e-5) - - # M-Step - trainer.m_step(m, data) - assert numpy.allclose(t_ref[it], m.t, 1e-5) - assert numpy.allclose(sigma_ref[it], m.sigma, 1e-5) - - - # C++ implementation - # Machine - m = IVectorMachine(ubm, 2) - m.variance_threshold = 1e-5 - - # Initialization - trainer = IVectorTrainer(update_sigma=True) - trainer.initialize(m) - m.t = t - m.sigma = sigma - for it in range(2): - # E-Step - trainer.e_step(m, data) - for k in acc_Nij_Sigma_wij2_ref[it]: - assert numpy.allclose(acc_Nij_Sigma_wij2_ref[it][k], trainer.acc_nij_wij2[k], 1e-5) - for k in acc_Fnorm_Sigma_wij_ref[it]: - assert numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.acc_fnormij_wij[k], 1e-5) - assert numpy.allclose(acc_Snorm_ref[it].reshape(dim_c,dim_d), trainer.acc_snormij, 1e-5) - assert numpy.allclose(N_ref[it], trainer.acc_nij, 1e-5) - - # M-Step - trainer.m_step(m) - assert numpy.allclose(t_ref[it], m.t, 1e-5) - assert numpy.allclose(sigma_ref[it], m.sigma, 1e-5) - - -def test_trainer_update_sigma_parallel(): - # Ubm - dim_c = 2 - dim_d = 3 - ubm = GMMMachine(dim_c,dim_d) - ubm.weights = numpy.array([0.4,0.6]) - ubm.means = numpy.array([[1.,7,4],[4,5,3]]) - ubm.variances = numpy.array([[0.5,1.,1.5],[1.,1.5,2.]]) - - # Defines GMMStats - gs1 = GMMStats(dim_c,dim_d) - log_likelihood1 = -3. - T1 = 1 - n1 = numpy.array([0.4, 0.6], numpy.float64) - sumpx1 = numpy.array([[1., 2., 3.], [2., 4., 3.]], numpy.float64) - sumpxx1 = numpy.array([[10., 20., 30.], [40., 50., 60.]], numpy.float64) - gs1.log_likelihood = log_likelihood1 - gs1.t = T1 - gs1.n = n1 - gs1.sum_px = sumpx1 - gs1.sum_pxx = sumpxx1 - - gs2 = GMMStats(dim_c,dim_d) - log_likelihood2 = -4. - T2 = 1 - n2 = numpy.array([0.2, 0.8], numpy.float64) - sumpx2 = numpy.array([[2., 1., 3.], [3., 4.1, 3.2]], numpy.float64) - sumpxx2 = numpy.array([[12., 15., 25.], [39., 51., 62.]], numpy.float64) - gs2.log_likelihood = log_likelihood2 - gs2.t = T2 - gs2.n = n2 - gs2.sum_px = sumpx2 - gs2.sum_pxx = sumpxx2 - - data = [gs1, gs2] - - # Reference values - acc_Nij_Sigma_wij2_ref1 = {0: numpy.array([[ 0.03202305, -0.02947769], [-0.02947769, 0.0561132 ]]), - 1: numpy.array([[ 0.07953279, -0.07829414], [-0.07829414, 0.13814242]])} - acc_Fnorm_Sigma_wij_ref1 = {0: numpy.array([[-0.29622691, 0.61411796], [ 0.09391764, -0.27955961], [-0.39014455, 0.89367757]]), - 1: numpy.array([[ 0.04695882, -0.13977981], [-0.05718673, 0.24159665], [-0.17098161, 0.47326585]])} - acc_Snorm_ref1 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref1 = numpy.array([0.6, 1.4]) - t_ref1 = numpy.array([[ 1.59543739, 11.78239235], [ -3.20130371, -6.66379081], [ 4.79674111, 18.44618316], - [ -0.91765407, -1.5319461 ], [ 2.26805901, 3.03434944], [ 2.76600031, 4.9935962 ]]) - sigma_ref1 = numpy.array([ 16.39472121, 34.72955353, 3.3108037, 43.73496916, 38.85472445, 68.22116903]) - - acc_Nij_Sigma_wij2_ref2 = {0: numpy.array([[ 0.50807426, -0.11907756], [-0.11907756, 0.12336544]]), - 1: numpy.array([[ 1.18602399, -0.2835859 ], [-0.2835859 , 0.39440498]])} - acc_Fnorm_Sigma_wij_ref2 = {0: numpy.array([[ 0.07221453, 1.1189786 ], [-0.08681275, -0.35396112], [ 0.15902728, 1.47293972]]), - 1: numpy.array([[-0.04340637, -0.17698056], [ 0.10662127, 0.21484933],[ 0.13116645, 0.64474271]])} - acc_Snorm_ref2 = numpy.array([16.6, 22.4, 16.6, 61.4, 55., 97.4]) - N_ref2 = numpy.array([0.6, 1.4]) - t_ref2 = numpy.array([[ 2.93105054, 11.89961223], [ -1.08988119, -3.92120757], [ 4.02093173, 15.82081981], - [ -0.17376634, -0.57366984], [ 0.26585634, 0.73589952], [ 0.60557877, 2.07014704]]) - sigma_ref2 = numpy.array([5.12154025e+00, 3.48623823e+01, 1.00000000e-05, 4.37792350e+01, 3.91525332e+01, 6.85613258e+01]) - - acc_Nij_Sigma_wij2_ref = [acc_Nij_Sigma_wij2_ref1, acc_Nij_Sigma_wij2_ref2] - acc_Fnorm_Sigma_wij_ref = [acc_Fnorm_Sigma_wij_ref1, acc_Fnorm_Sigma_wij_ref2] - acc_Snorm_ref = [acc_Snorm_ref1, acc_Snorm_ref2] - N_ref = [N_ref1, N_ref2] - t_ref = [t_ref1, t_ref2] - sigma_ref = [sigma_ref1, sigma_ref2] - - - # Machine - t = numpy.array([[1.,2],[4,1],[0,3],[5,8],[7,10],[11,1]]) - sigma = numpy.array([1.,2.,1.,3.,2.,4.]) - - - # C++ implementation - # Machine - serial_m = IVectorMachine(ubm, 2) - serial_m.variance_threshold = 1e-5 - - # SERIAL TRAINER - serial_trainer = IVectorTrainer(update_sigma=True) - serial_m.t = t - serial_m.sigma = sigma - - bob.learn.em.train(serial_trainer, serial_m, data, max_iterations=5, initialize=True) - - # PARALLEL TRAINER - parallel_m = IVectorMachine(ubm, 2) - parallel_m.variance_threshold = 1e-5 - - parallel_trainer = IVectorTrainer(update_sigma=True) - parallel_m.t = t - parallel_m.sigma = sigma - - bob.learn.em.train(parallel_trainer, parallel_m, data, max_iterations=5, initialize=True, pool=2) - - assert numpy.allclose(serial_trainer.acc_nij_wij2, parallel_trainer.acc_nij_wij2, 1e-5) - assert numpy.allclose(serial_trainer.acc_fnormij_wij, parallel_trainer.acc_fnormij_wij, 1e-5) - assert numpy.allclose(serial_trainer.acc_snormij, parallel_trainer.acc_snormij, 1e-5) - assert numpy.allclose(serial_trainer.acc_nij, parallel_trainer.acc_nij, 1e-5) - diff --git a/bob/learn/em/test/test_jfa.py b/bob/learn/em/test/test_jfa.py deleted file mode 100644 index 6d38736350fc5b36b8147926efb8a2d29a890e9c..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_jfa.py +++ /dev/null @@ -1,396 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Wed Feb 15 23:24:35 2012 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Tests on the JFA-based machines -""" - -import os -import numpy -import numpy.linalg -import tempfile - -import bob.io.base - -from bob.learn.em import GMMMachine, GMMStats, JFABase, ISVBase, ISVMachine, JFAMachine - -def estimate_x(dim_c, dim_d, mean, sigma, U, N, F): - # Compute helper values - UtSigmaInv = {} - UtSigmaInvU = {} - dim_ru = U.shape[1] - for c in range(dim_c): - start = c*dim_d - end = (c+1)*dim_d - Uc = U[start:end,:] - UtSigmaInv[c] = Uc.transpose() / sigma[start:end] - UtSigmaInvU[c] = numpy.dot(UtSigmaInv[c], Uc); - - # I + (U^{T} \Sigma^-1 N U) - I_UtSigmaInvNU = numpy.eye(dim_ru, dtype=numpy.float64) - for c in range(dim_c): - I_UtSigmaInvNU = I_UtSigmaInvNU + UtSigmaInvU[c] * N[c] - - # U^{T} \Sigma^-1 F - UtSigmaInv_Fnorm = numpy.zeros((dim_ru,), numpy.float64) - for c in range(dim_c): - start = c*dim_d - end = (c+1)*dim_d - Fnorm = F[c,:] - N[c] * mean[start:end] - UtSigmaInv_Fnorm = UtSigmaInv_Fnorm + numpy.dot(UtSigmaInv[c], Fnorm) - - return numpy.linalg.solve(I_UtSigmaInvNU, UtSigmaInv_Fnorm) - -def estimate_ux(dim_c, dim_d, mean, sigma, U, N, F): - return numpy.dot(U, estimate_x(dim_c, dim_d, mean, sigma, U, N, F)) - - -def test_JFABase(): - - # Creates a UBM - weights = numpy.array([0.4, 0.6], 'float64') - means = numpy.array([[1, 6, 2], [4, 3, 2]], 'float64') - variances = numpy.array([[1, 2, 1], [2, 1, 2]], 'float64') - ubm = GMMMachine(2,3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a JFABase - U = numpy.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], 'float64') - V = numpy.array([[6, 5], [4, 3], [2, 1], [1, 2], [3, 4], [5, 6]], 'float64') - d = numpy.array([0, 1, 0, 1, 0, 1], 'float64') - m = JFABase(ubm, ru=1, rv=1) - - _,_,ru,rv = m.shape - assert ru == 1 - assert rv == 1 - - # Checks for correctness - m.resize(2,2) - m.u = U - m.v = V - m.d = d - n_gaussians,dim,ru,rv = m.shape - supervector_length = m.supervector_length - - assert (m.u == U).all() - assert (m.v == V).all() - assert (m.d == d).all() - assert n_gaussians == 2 - assert dim == 3 - assert supervector_length == 6 - assert ru == 2 - assert rv == 2 - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = JFABase(bob.io.base.HDF5File(filename)) - m_loaded.ubm = ubm - assert m == m_loaded - assert (m != m_loaded) is False - assert m.is_similar_to(m_loaded) - - # Copy constructor - mc = JFABase(m) - assert m == mc - - # Variant - #mv = JFABase() - # Checks for correctness - #mv.ubm = ubm - #mv.resize(2,2) - #mv.u = U - #mv.v = V - #mv.d = d - #assert (m.u == U).all() - #assert (m.v == V).all() - #assert (m.d == d).all() - #assert m.dim_c == 2 - #assert m.dim_d == 3 - #assert m.dim_cd == 6 - #assert m.dim_ru == 2 - #assert m.dim_rv == 2 - - # Clean-up - os.unlink(filename) - -def test_ISVBase(): - - # Creates a UBM - weights = numpy.array([0.4, 0.6], 'float64') - means = numpy.array([[1, 6, 2], [4, 3, 2]], 'float64') - variances = numpy.array([[1, 2, 1], [2, 1, 2]], 'float64') - ubm = GMMMachine(2,3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a ISVBase - U = numpy.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], 'float64') - d = numpy.array([0, 1, 0, 1, 0, 1], 'float64') - m = ISVBase(ubm, ru=1) - _,_,ru = m.shape - assert ru == 1 - - # Checks for correctness - m.resize(2) - m.u = U - m.d = d - n_gaussians,dim,ru = m.shape - supervector_length = m.supervector_length - assert (m.u == U).all() - assert (m.d == d).all() - assert n_gaussians == 2 - assert dim == 3 - assert supervector_length == 6 - assert ru == 2 - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = ISVBase(bob.io.base.HDF5File(filename)) - m_loaded.ubm = ubm - assert m == m_loaded - assert (m != m_loaded) is False - assert m.is_similar_to(m_loaded) - - # Copy constructor - mc = ISVBase(m) - assert m == mc - - # Variant - #mv = ISVBase() - # Checks for correctness - #mv.ubm = ubm - #mv.resize(2) - #mv.u = U - #mv.d = d - #assert (m.u == U).all() - #assert (m.d == d).all() - #ssert m.dim_c == 2 - #assert m.dim_d == 3 - #assert m.dim_cd == 6 - #assert m.dim_ru == 2 - - # Clean-up - os.unlink(filename) - -def test_JFAMachine(): - - # Creates a UBM - weights = numpy.array([0.4, 0.6], 'float64') - means = numpy.array([[1, 6, 2], [4, 3, 2]], 'float64') - variances = numpy.array([[1, 2, 1], [2, 1, 2]], 'float64') - ubm = GMMMachine(2,3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a JFABase - U = numpy.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], 'float64') - V = numpy.array([[6, 5], [4, 3], [2, 1], [1, 2], [3, 4], [5, 6]], 'float64') - d = numpy.array([0, 1, 0, 1, 0, 1], 'float64') - base = JFABase(ubm,2,2) - base.u = U - base.v = V - base.d = d - - # Creates a JFAMachine - y = numpy.array([1,2], 'float64') - z = numpy.array([3,4,1,2,0,1], 'float64') - m = JFAMachine(base) - m.y = y - m.z = z - n_gaussians,dim,ru,rv = m.shape - supervector_length = m.supervector_length - - assert n_gaussians == 2 - assert dim == 3 - assert supervector_length == 6 - assert ru == 2 - assert rv == 2 - assert (m.y == y).all() - assert (m.z == z).all() - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = JFAMachine(bob.io.base.HDF5File(filename)) - m_loaded.jfa_base = base - assert m == m_loaded - assert (m != m_loaded) is False - assert m.is_similar_to(m_loaded) - - # Copy constructor - mc = JFAMachine(m) - assert m == mc - - # Variant - #mv = JFAMachine() - # Checks for correctness - #mv.jfa_base = base - #m.y = y - #m.z = z - #assert m.dim_c == 2 - #assert m.dim_d == 3 - #assert m.dim_cd == 6 - #assert m.dim_ru == 2 - #assert m.dim_rv == 2 - #assert (m.y == y).all() - #assert (m.z == z).all() - - # Defines GMMStats - gs = GMMStats(2,3) - log_likelihood = -3. - T = 1 - n = numpy.array([0.4, 0.6], 'float64') - sumpx = numpy.array([[1., 2., 3.], [4., 5., 6.]], 'float64') - sumpxx = numpy.array([[10., 20., 30.], [40., 50., 60.]], 'float64') - gs.log_likelihood = log_likelihood - gs.t = T - gs.n = n - gs.sum_px = sumpx - gs.sum_pxx = sumpxx - - # Forward GMMStats and check estimated value of the x speaker factor - eps = 1e-10 - x_ref = numpy.array([0.291042849767692, 0.310273618998444], 'float64') - score_ref = -2.111577181208289 - score = m.log_likelihood(gs) - assert numpy.allclose(m.x, x_ref, eps) - assert abs(score_ref-score) < eps - - # x and Ux - x = numpy.ndarray((2,), numpy.float64) - m.estimate_x(gs, x) - n_gaussians, dim,_,_ = m.shape - x_py = estimate_x(n_gaussians, dim, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - assert numpy.allclose(x, x_py, eps) - - ux = numpy.ndarray((6,), numpy.float64) - m.estimate_ux(gs, ux) - n_gaussians, dim,_,_ = m.shape - ux_py = estimate_ux(n_gaussians, dim, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - assert numpy.allclose(ux, ux_py, eps) - assert numpy.allclose(m.x, x, eps) - - score = m.forward_ux(gs, ux) - - assert abs(score_ref-score) < eps - - # Clean-up - os.unlink(filename) - -def test_ISVMachine(): - - # Creates a UBM - weights = numpy.array([0.4, 0.6], 'float64') - means = numpy.array([[1, 6, 2], [4, 3, 2]], 'float64') - variances = numpy.array([[1, 2, 1], [2, 1, 2]], 'float64') - ubm = GMMMachine(2,3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a ISVBaseMachine - U = numpy.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], 'float64') - #V = numpy.array([[0], [0], [0], [0], [0], [0]], 'float64') - d = numpy.array([0, 1, 0, 1, 0, 1], 'float64') - base = ISVBase(ubm,2) - base.u = U - #base.v = V - base.d = d - - # Creates a JFAMachine - z = numpy.array([3,4,1,2,0,1], 'float64') - m = ISVMachine(base) - m.z = z - - n_gaussians,dim,ru = m.shape - supervector_length = m.supervector_length - assert n_gaussians == 2 - assert dim == 3 - assert supervector_length == 6 - assert ru == 2 - assert (m.z == z).all() - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = ISVMachine(bob.io.base.HDF5File(filename)) - m_loaded.isv_base = base - assert m == m_loaded - assert (m != m_loaded) is False - assert m.is_similar_to(m_loaded) - - # Copy constructor - mc = ISVMachine(m) - assert m == mc - - # Variant - mv = ISVMachine(base) - # Checks for correctness - #mv.isv_base = base - m.z = z - - n_gaussians,dim,ru = m.shape - supervector_length = m.supervector_length - assert n_gaussians == 2 - assert dim == 3 - assert supervector_length == 6 - assert ru == 2 - assert (m.z == z).all() - - # Defines GMMStats - gs = GMMStats(2,3) - log_likelihood = -3. - T = 1 - n = numpy.array([0.4, 0.6], 'float64') - sumpx = numpy.array([[1., 2., 3.], [4., 5., 6.]], 'float64') - sumpxx = numpy.array([[10., 20., 30.], [40., 50., 60.]], 'float64') - gs.log_likelihood = log_likelihood - gs.t = T - gs.n = n - gs.sum_px = sumpx - gs.sum_pxx = sumpxx - - # Forward GMMStats and check estimated value of the x speaker factor - eps = 1e-10 - x_ref = numpy.array([0.291042849767692, 0.310273618998444], 'float64') - score_ref = -3.280498193082100 - - score = m(gs) - assert numpy.allclose(m.x, x_ref, eps) - assert abs(score_ref-score) < eps - - # Check using alternate forward() method - supervector_length = m.supervector_length - Ux = numpy.ndarray(shape=(supervector_length,), dtype=numpy.float64) - m.estimate_ux(gs, Ux) - score = m.forward_ux(gs, Ux) - assert abs(score_ref-score) < eps - - # x and Ux - x = numpy.ndarray((2,), numpy.float64) - m.estimate_x(gs, x) - n_gaussians,dim,_ = m.shape - x_py = estimate_x(n_gaussians, dim, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - assert numpy.allclose(x, x_py, eps) - - ux = numpy.ndarray((6,), numpy.float64) - m.estimate_ux(gs, ux) - n_gaussians,dim,_ = m.shape - ux_py = estimate_ux(n_gaussians, dim, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - assert numpy.allclose(ux, ux_py, eps) - assert numpy.allclose(m.x, x, eps) - - score = m.forward_ux(gs, ux) - assert abs(score_ref-score) < eps - - # Clean-up - os.unlink(filename) diff --git a/bob/learn/em/test/test_jfa_trainer.py b/bob/learn/em/test/test_jfa_trainer.py deleted file mode 100644 index 457fcfbf6a82e7c8435dc28e1355b67011a4bdfe..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_jfa_trainer.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Tue Jul 19 12:16:17 2011 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Test JFA trainer package -""" - -import numpy -import numpy.linalg - -import bob.core.random -import nose.tools - -from bob.learn.em import GMMStats, GMMMachine, JFABase, JFAMachine, ISVBase, ISVMachine, JFATrainer, ISVTrainer - - -def equals(x, y, epsilon): - return (abs(x - y) < epsilon).all() - -# Define Training set and initial values for tests -F1 = numpy.array( [0.3833, 0.4516, 0.6173, 0.2277, 0.5755, 0.8044, 0.5301, - 0.9861, 0.2751, 0.0300, 0.2486, 0.5357]).reshape((6,2)) -F2 = numpy.array( [0.0871, 0.6838, 0.8021, 0.7837, 0.9891, 0.5341, 0.0669, - 0.8854, 0.9394, 0.8990, 0.0182, 0.6259]).reshape((6,2)) -F=[F1, F2] - -N1 = numpy.array([0.1379, 0.1821, 0.2178, 0.0418]).reshape((2,2)) -N2 = numpy.array([0.1069, 0.9397, 0.6164, 0.3545]).reshape((2,2)) -N=[N1, N2] - -gs11 = GMMStats(2,3) -gs11.n = N1[:,0] -gs11.sum_px = F1[:,0].reshape(2,3) -gs12 = GMMStats(2,3) -gs12.n = N1[:,1] -gs12.sum_px = F1[:,1].reshape(2,3) - -gs21 = GMMStats(2,3) -gs21.n = N2[:,0] -gs21.sum_px = F2[:,0].reshape(2,3) -gs22 = GMMStats(2,3) -gs22.n = N2[:,1] -gs22.sum_px = F2[:,1].reshape(2,3) - -TRAINING_STATS = [[gs11, gs12], [gs21, gs22]] -UBM_MEAN = numpy.array([0.1806, 0.0451, 0.7232, 0.3474, 0.6606, 0.3839]) -UBM_VAR = numpy.array([0.6273, 0.0216, 0.9106, 0.8006, 0.7458, 0.8131]) -M_d = numpy.array([0.4106, 0.9843, 0.9456, 0.6766, 0.9883, 0.7668]) -M_v = numpy.array( [0.3367, 0.4116, 0.6624, 0.6026, 0.2442, 0.7505, 0.2955, - 0.5835, 0.6802, 0.5518, 0.5278,0.5836]).reshape((6,2)) -M_u = numpy.array( [0.5118, 0.3464, 0.0826, 0.8865, 0.7196, 0.4547, 0.9962, - 0.4134, 0.3545, 0.2177, 0.9713, 0.1257]).reshape((6,2)) - -z1 = numpy.array([0.3089, 0.7261, 0.7829, 0.6938, 0.0098, 0.8432]) -z2 = numpy.array([0.9223, 0.7710, 0.0427, 0.3782, 0.7043, 0.7295]) -y1 = numpy.array([0.2243, 0.2691]) -y2 = numpy.array([0.6730, 0.4775]) -x1 = numpy.array([0.9976, 0.8116, 0.1375, 0.3900]).reshape((2,2)) -x2 = numpy.array([0.4857, 0.8944, 0.9274, 0.9175]).reshape((2,2)) -M_z=[z1, z2] -M_y=[y1, y2] -M_x=[x1, x2] - - -def test_JFATrainer_updateYandV(): - # test the JFATrainer for updating Y and V - - v_ref = numpy.array( [0.7228, 0.7892, 0.6475, 0.6080, 0.8631, 0.8416, - 1.6512, 1.6068, 0.0500, 0.0101, 0.4325, 0.6719]).reshape((6,2)) - - y1 = numpy.array([0., 0.]) - y2 = numpy.array([0., 0.]) - y3 = numpy.array([0.9630, 1.3868]) - y4 = numpy.array([0.0426, -0.3721]) - y=[y1, y2] - - # call the updateY function - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = JFABase(ubm,2,2) - t = JFATrainer() - t.initialize(m, TRAINING_STATS) - m.u = M_u - m.v = M_v - m.d = M_d - t.__X__ = M_x - t.__Y__ = y - t.__Z__ = M_z - t.e_step_v(m, TRAINING_STATS) - t.m_step_v(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - assert equals(t.__Y__[0], y3, 2e-4) - assert equals(t.__Y__[1], y4, 2e-4) - assert equals(m.v, v_ref, 2e-4) - - -def test_JFATrainer_updateXandU(): - # test the JFATrainer for updating X and U - - u_ref = numpy.array( [0.6729, 0.3408, 0.0544, 1.0653, 0.5399, 1.3035, - 2.4995, 0.4385, 0.1292, -0.0576, 1.1962, 0.0117]).reshape((6,2)) - - x1 = numpy.array([0., 0., 0., 0.]).reshape((2,2)) - x2 = numpy.array([0., 0., 0., 0.]).reshape((2,2)) - x3 = numpy.array([0.2143, 1.8275, 3.1979, 0.1227]).reshape((2,2)) - x4 = numpy.array([-1.3861, 0.2359, 5.3326, -0.7914]).reshape((2,2)) - x = [x1, x2] - - # call the updateX function - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = JFABase(ubm,2,2) - t = JFATrainer() - t.initialize(m, TRAINING_STATS) - m.u = M_u - m.v = M_v - m.d = M_d - t.__X__ = x - t.__Y__ = M_y - t.__Z__ = M_z - t.e_step_u(m, TRAINING_STATS) - t.m_step_u(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - assert equals(t.__X__[0], x3, 2e-4) - assert equals(t.__X__[1], x4, 2e-4) - assert equals(m.u, u_ref, 2e-4) - - -def test_JFATrainer_updateZandD(): - # test the JFATrainer for updating Z and D - - d_ref = numpy.array([0.3110, 1.0138, 0.8297, 1.0382, 0.0095, 0.6320]) - - z1 = numpy.array([0., 0., 0., 0., 0., 0.]) - z2 = numpy.array([0., 0., 0., 0., 0., 0.]) - z3_ref = numpy.array([0.3256, 1.8633, 0.6480, 0.8085, -0.0432, 0.2885]) - z4_ref = numpy.array([-0.3324, -0.1474, -0.4404, -0.4529, 0.0484, -0.5848]) - z=[z1, z2] - - # call the updateZ function - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = JFABase(ubm,2,2) - t = JFATrainer() - t.initialize(m, TRAINING_STATS) - m.u = M_u - m.v = M_v - m.d = M_d - t.__X__ = M_x - t.__Y__ = M_y - t.__Z__ = z - t.e_step_d(m, TRAINING_STATS) - t.m_step_d(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - assert equals(t.__Z__[0], z3_ref, 2e-4) - assert equals(t.__Z__[1], z4_ref, 2e-4) - assert equals(m.d, d_ref, 2e-4) - - -def test_JFATrainAndEnrol(): - # Train and enroll a JFAMachine - - # Calls the train function - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - mb = JFABase(ubm, 2, 2) - t = JFATrainer() - t.initialize(mb, TRAINING_STATS) - mb.u = M_u - mb.v = M_v - mb.d = M_d - bob.learn.em.train_jfa(t,mb, TRAINING_STATS, initialize=False) - - v_ref = numpy.array([[0.245364911936476, 0.978133261775424], [0.769646805052223, 0.940070736856596], [0.310779202800089, 1.456332053893072], - [0.184760934399551, 2.265139705602147], [0.701987784039800, 0.081632150899400], [0.074344030229297, 1.090248340917255]], 'float64') - u_ref = numpy.array([[0.049424652628448, 0.060480486336896], [0.178104127464007, 1.884873813495153], [1.204011484266777, 2.281351307871720], - [7.278512126426286, -0.390966087173334], [-0.084424326581145, -0.081725474934414], [4.042143689831097, -0.262576386580701]], 'float64') - d_ref = numpy.array([9.648467e-18, 2.63720683155e-12, 2.11822157653706e-10, 9.1047243e-17, 1.41163442535567e-10, 3.30581e-19], 'float64') - - eps = 1e-10 - assert numpy.allclose(mb.v, v_ref, eps) - assert numpy.allclose(mb.u, u_ref, eps) - assert numpy.allclose(mb.d, d_ref, eps) - - # Calls the enroll function - m = JFAMachine(mb) - - Ne = numpy.array([0.1579, 0.9245, 0.1323, 0.2458]).reshape((2,2)) - Fe = numpy.array([0.1579, 0.1925, 0.3242, 0.1234, 0.2354, 0.2734, 0.2514, 0.5874, 0.3345, 0.2463, 0.4789, 0.5236]).reshape((6,2)) - gse1 = GMMStats(2,3) - gse1.n = Ne[:,0] - gse1.sum_px = Fe[:,0].reshape(2,3) - gse2 = GMMStats(2,3) - gse2.n = Ne[:,1] - gse2.sum_px = Fe[:,1].reshape(2,3) - - gse = [gse1, gse2] - t.enroll(m, gse, 5) - - y_ref = numpy.array([0.555991469319657, 0.002773650670010], 'float64') - z_ref = numpy.array([8.2228e-20, 3.15216909492e-13, -1.48616735364395e-10, 1.0625905e-17, 3.7150503117895e-11, 1.71104e-19], 'float64') - assert numpy.allclose(m.y, y_ref, eps) - assert numpy.allclose(m.z, z_ref, eps) - - #Testing exceptions - nose.tools.assert_raises(RuntimeError, t.initialize, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.initialize, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.e_step_u, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.e_step_u, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.m_step_u, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.m_step_u, mb, [[1,2,2]]) - - nose.tools.assert_raises(RuntimeError, t.e_step_v, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.e_step_v, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.m_step_v, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.m_step_v, mb, [[1,2,2]]) - - nose.tools.assert_raises(RuntimeError, t.e_step_d, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.e_step_d, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.m_step_d, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.m_step_d, mb, [[1,2,2]]) - - nose.tools.assert_raises(RuntimeError, t.enroll, m, [[1,2,2]],5) - - - -def test_ISVTrainAndEnrol(): - # Train and enroll an 'ISVMachine' - - eps = 1e-10 - d_ref = numpy.array([0.39601136, 0.07348469, 0.47712682, 0.44738127, 0.43179856, 0.45086029], 'float64') - u_ref = numpy.array([[0.855125642430777, 0.563104284748032], [-0.325497865404680, 1.923598985291687], [0.511575659503837, 1.964288663083095], [9.330165761678115, 1.073623827995043], [0.511099245664012, 0.278551249248978], [5.065578541930268, 0.509565618051587]], 'float64') - z_ref = numpy.array([-0.079315777443826, 0.092702428248543, -0.342488761656616, -0.059922635809136 , 0.133539981073604, 0.213118695516570], 'float64') - - # Calls the train function - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - mb = ISVBase(ubm,2) - t = ISVTrainer(4.) - t.initialize(mb, TRAINING_STATS) - mb.u = M_u - for i in range(10): - t.e_step(mb, TRAINING_STATS) - t.m_step(mb) - - assert numpy.allclose(mb.d, d_ref, eps) - assert numpy.allclose(mb.u, u_ref, eps) - - # Calls the enroll function - m = ISVMachine(mb) - - Ne = numpy.array([0.1579, 0.9245, 0.1323, 0.2458]).reshape((2,2)) - Fe = numpy.array([0.1579, 0.1925, 0.3242, 0.1234, 0.2354, 0.2734, 0.2514, 0.5874, 0.3345, 0.2463, 0.4789, 0.5236]).reshape((6,2)) - gse1 = GMMStats(2,3) - gse1.n = Ne[:,0] - gse1.sum_px = Fe[:,0].reshape(2,3) - gse2 = GMMStats(2,3) - gse2.n = Ne[:,1] - gse2.sum_px = Fe[:,1].reshape(2,3) - - gse = [gse1, gse2] - t.enroll(m, gse, 5) - assert numpy.allclose(m.z, z_ref, eps) - - #Testing exceptions - nose.tools.assert_raises(RuntimeError, t.initialize, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.initialize, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.e_step, mb, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.e_step, mb, [[1,2,2]]) - nose.tools.assert_raises(RuntimeError, t.enroll, m, [[1,2,2]],5) - - - -def test_JFATrainInitialize(): - # Check that the initialization is consistent and using the rng (cf. issue #118) - - eps = 1e-10 - - # UBM GMM - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - - ## JFA - jb = JFABase(ubm, 2, 2) - # first round - rng = bob.core.random.mt19937(0) - jt = JFATrainer() - #jt.rng = rng - jt.initialize(jb, TRAINING_STATS, rng) - u1 = jb.u - v1 = jb.v - d1 = jb.d - - # second round - rng = bob.core.random.mt19937(0) - jt.initialize(jb, TRAINING_STATS, rng) - u2 = jb.u - v2 = jb.v - d2 = jb.d - - assert numpy.allclose(u1, u2, eps) - assert numpy.allclose(v1, v2, eps) - assert numpy.allclose(d1, d2, eps) - - -def test_ISVTrainInitialize(): - - # Check that the initialization is consistent and using the rng (cf. issue #118) - eps = 1e-10 - - # UBM GMM - ubm = GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - - ## ISV - ib = ISVBase(ubm, 2) - # first round - rng = bob.core.random.mt19937(0) - it = ISVTrainer(10) - #it.rng = rng - it.initialize(ib, TRAINING_STATS, rng) - u1 = ib.u - d1 = ib.d - - # second round - rng = bob.core.random.mt19937(0) - #it.rng = rng - it.initialize(ib, TRAINING_STATS, rng) - u2 = ib.u - d2 = ib.d - - assert numpy.allclose(u1, u2, eps) - assert numpy.allclose(d1, d2, eps) diff --git a/bob/learn/em/test/test_kmeans.py b/bob/learn/em/test/test_kmeans.py index be80ba27a154cd549e048b0504b4c84c06df6a38..cbf5f0954e67d5f59b8387c51da66231e65a4a20 100644 --- a/bob/learn/em/test/test_kmeans.py +++ b/bob/learn/em/test/test_kmeans.py @@ -6,91 +6,191 @@ # Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the KMeans machine + +Tries each test with a numpy array and the equivalent dask array. """ -import os -import numpy -import tempfile +import copy + +import dask.array as da +import numpy as np + +from bob.learn.em.cluster import KMeansMachine + + +def to_numpy(*args): + result = [] + for x in args: + result.append(np.array(x)) + if len(result) == 1: + return result[0] + return result + -import bob.io.base -from bob.learn.em import KMeansMachine +def to_dask_array(*args): + result = [] + for x in args: + result.append(da.from_array(np.array(x))) + if len(result) == 1: + return result[0] + return result -def equals(x, y, epsilon): - return (abs(x - y) < epsilon) def test_KMeansMachine(): - # Test a KMeansMachine - - means = numpy.array([[3, 70, 0], [4, 72, 0]], 'float64') - mean = numpy.array([3,70,1], 'float64') - - # Initializes a KMeansMachine - km = KMeansMachine(2,3) - km.means = means - assert km.shape == (2,3) - - # Sets and gets - assert (km.means == means).all() - assert (km.get_mean(0) == means[0,:]).all() - assert (km.get_mean(1) == means[1,:]).all() - km.set_mean(0, mean) - assert (km.get_mean(0) == mean).all() - - # Distance and closest mean - eps = 1e-10 - - assert equals( km.get_distance_from_mean(mean, 0), 0, eps) - assert equals( km.get_distance_from_mean(mean, 1), 6, eps) - - (index, dist) = km.get_closest_mean(mean) - - assert index == 0 - assert equals( dist, 0, eps) - assert equals( km.get_min_distance(mean), 0, eps) - - # Loads and saves - filename = str(tempfile.mkstemp(".hdf5")[1]) - km.save(bob.io.base.HDF5File(filename, 'w')) - km_loaded = KMeansMachine(bob.io.base.HDF5File(filename)) - assert km == km_loaded - - # Resize - km.resize(4,5) - assert km.shape == (4,5) - - # Copy constructor and comparison operators - km.resize(2,3) - km2 = KMeansMachine(km) - assert km2 == km - assert (km2 != km) is False - assert km2.is_similar_to(km) - means2 = numpy.array([[3, 70, 0], [4, 72, 2]], 'float64') - km2.means = means2 - assert (km2 == km) is False - assert km2 != km - assert (km2.is_similar_to(km)) is False - - # Clean-up - os.unlink(filename) - - -def test_KMeansMachine2(): - kmeans = bob.learn.em.KMeansMachine(2,2) - kmeans.means = numpy.array([[1.2,1.3],[0.2,-0.3]]) - - data = numpy.array([ - [1.,1], - [1.2, 3], - [0,0], - [0.3,0.2], - [0.2,0] - ]) - variances, weights = kmeans.get_variances_and_weights_for_each_cluster(data) - - variances_result = numpy.array([[ 0.01,1.], - [ 0.01555556, 0.00888889]]) - weights_result = numpy.array([ 0.4, 0.6]) - - assert equals(weights_result,weights, 1e-3).all() - assert equals(variances_result,variances,1e-3).all() - + # Test a KMeansMachine + + means = np.array([[3, 70, 0], [4, 72, 0]], "float64") + test_val = np.array([3, 70, 1], "float64") + test_arr = np.array([[3, 70, 1],[5, 72, 0]], "float64") + + for transform in (to_numpy, to_dask_array): + means, test_val, test_arr = transform(means, test_val, test_arr) + + # Initializes a KMeansMachine + km = KMeansMachine(2) + km.centroids_ = means + + # Distance and closest mean + np.testing.assert_equal(km.transform(test_val)[0], np.array([1])) + np.testing.assert_equal(km.transform(test_val)[1], np.array([6])) + + (index, dist) = km.get_closest_centroid(test_val) + assert index == 0 + np.testing.assert_equal(dist, np.array([[1.0]])) + + (indices, dists) = km.get_closest_centroid(test_arr) + np.testing.assert_equal(indices, np.array([0,1])) + np.testing.assert_equal(dists, np.array([[1,8],[6,1]])) + + index = km.predict(test_val) + assert index == 0 + + indices = km.predict(test_arr) + np.testing.assert_equal(indices, np.array([0,1])) + + np.testing.assert_equal(km.get_min_distance(test_val), np.array([1])) + np.testing.assert_equal(km.get_min_distance(test_arr), np.array([1,1])) + + # Check __eq__ and is_similar_to + km2 = KMeansMachine(2) + assert km != km2 + assert not km.is_similar_to(km2) + km2 = copy.deepcopy(km) + assert km == km2 + assert km.is_similar_to(km2) + km2.centroids_[0,0] += 1 + assert km != km2 + assert not km.is_similar_to(km2) + + +def test_KMeansMachine_var_and_weight(): + for transform in (to_numpy, to_dask_array): + kmeans = KMeansMachine(2) + kmeans.centroids_ = transform(np.array([[1.2, 1.3], [0.2, -0.3]])) + + data = np.array([[1.0, 1], [1.2, 3], [0, 0], [0.3, 0.2], [0.2, 0]]) + data = transform(data) + variances, weights = kmeans.get_variances_and_weights_for_each_cluster(data) + + variances_result = np.array([[0.01, 1.0], [0.01555556, 0.00888889]]) + weights_result = np.array([0.4, 0.6]) + + np.testing.assert_almost_equal(variances, variances_result, decimal=7) + np.testing.assert_equal(weights, weights_result) + + +def test_kmeans_fit(): + np.random.seed(0) + data1 = np.random.normal(loc=1, size=(2000, 3)) + data2 = np.random.normal(loc=-1, size=(2000, 3)) + print(data1.min(), data1.max()) + print(data2.min(), data2.max()) + data = np.concatenate([data1, data2], axis=0) + + for transform in (to_numpy, to_dask_array): + data = transform(data) + machine = KMeansMachine(2, random_state=0).fit(data) + centroids = machine.centroids_[np.argsort(machine.centroids_[:, 0])] + expected = [ + [-1.07173464, -1.06200356, -1.00724920], + [0.99479125, 0.99665564, 0.97689017], + ] + np.testing.assert_almost_equal(centroids, expected, decimal=7) + + # Early stop + machine = KMeansMachine(2, max_iter=2) + machine.fit(data) + + +def test_kmeans_fit_partial(): + np.random.seed(0) + data1 = np.random.normal(loc=1, size=(2000, 3)) + data2 = np.random.normal(loc=-1, size=(2000, 3)) + data = np.concatenate([data1, data2], axis=0) + + for transform in (to_numpy, to_dask_array): + data = transform(data) + machine = KMeansMachine(2, random_state=0) + for _ in range(20): + machine.partial_fit(data) + centroids = machine.centroids_[np.argsort(machine.centroids_[:, 0])] + expected = [ + [-1.07173464, -1.06200356, -1.00724920], + [0.99479125, 0.99665564, 0.97689017], + ] + np.testing.assert_almost_equal(centroids, expected, decimal=7) + + +def test_kmeans_fit_init_pp(): + np.random.seed(0) + data1 = np.random.normal(loc=1, size=(2000, 3)) + data2 = np.random.normal(loc=-1, size=(2000, 3)) + data = np.concatenate([data1, data2], axis=0) + + for transform in (to_numpy, to_dask_array): + data = transform(data) + machine = KMeansMachine(2, init_method="k-means++", random_state=0).fit(data) + centroids = machine.centroids_[np.argsort(machine.centroids_[:, 0])] + expected = [ + [-1.07173464, -1.06200356, -1.00724920], + [0.99479125, 0.99665564, 0.97689017], + ] + np.testing.assert_almost_equal(centroids, expected, decimal=7) + + +def test_kmeans_fit_init_random(): + np.random.seed(0) + data1 = np.random.normal(loc=1, size=(2000, 3)) + data2 = np.random.normal(loc=-1, size=(2000, 3)) + data = np.concatenate([data1, data2], axis=0) + for transform in (to_numpy, to_dask_array): + data = transform(data) + machine = KMeansMachine(2, init_method="random", random_state=0).fit(data) + centroids = machine.centroids_[np.argsort(machine.centroids_[:, 0])] + expected = [ + [-1.07329460, -1.06207104, -1.00714365], + [0.99529015, 0.99570570, 0.97580858], + ] + np.testing.assert_almost_equal(centroids, expected, decimal=7) + +def test_kmeans_parameters(): + np.random.seed(0) + data1 = np.random.normal(loc=1, size=(2000, 3)) + data2 = np.random.normal(loc=-1, size=(2000, 3)) + data = np.concatenate([data1, data2], axis=0) + for transform in (to_numpy, to_dask_array): + data = transform(data) + machine = KMeansMachine( + n_clusters=2, + init_method="k-means||", + convergence_threshold=1e-5, + max_iter=5, + random_state=0, + init_max_iter=5, + ).fit(data) + centroids = machine.centroids_[np.argsort(machine.centroids_[:, 0])] + expected = [ + [-1.07173464, -1.06200356, -1.00724920], + [0.99479125, 0.99665564, 0.97689017], + ] + np.testing.assert_almost_equal(centroids, expected, decimal=7) diff --git a/bob/learn/em/test/test_kmeans_trainer.py b/bob/learn/em/test/test_kmeans_trainer.py deleted file mode 100644 index 537df0e9abc7b52bbec5fc19311de7bf24c1c446..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_kmeans_trainer.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Fri Jan 18 12:46:00 2013 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Test K-Means algorithm -""" -import numpy - -import bob.core -import bob.io -from bob.io.base.test_utils import datafile - -from bob.learn.em import KMeansMachine, KMeansTrainer - - -def equals(x, y, epsilon): - return (abs(x - y) < epsilon).all() - - -def kmeans_plus_plus(machine, data, seed): - """Python implementation of K-Means++ (initialization)""" - n_data = data.shape[0] - rng = bob.core.random.mt19937(seed) - u = bob.core.random.uniform('int32', 0, n_data - 1) - index = u(rng) - machine.set_mean(0, data[index, :]) - weights = numpy.zeros(shape=(n_data,), dtype=numpy.float64) - - for m in range(1, machine.dim_c): - for s in range(n_data): - s_cur = data[s, :] - w_cur = machine.get_distance_from_mean(s_cur, 0) - for i in range(m): - w_cur = min(machine.get_distance_from_mean(s_cur, i), w_cur) - weights[s] = w_cur - weights *= weights - weights /= numpy.sum(weights) - d = bob.core.random.discrete('int32', weights) - index = d(rng) - machine.set_mean(m, data[index, :]) - - -def NormalizeStdArray(path): - array = bob.io.base.load(path).astype('float64') - std = array.std(axis=0) - return (array / std, std) - - -def multiplyVectorsByFactors(matrix, vector): - for i in range(0, matrix.shape[0]): - for j in range(0, matrix.shape[1]): - matrix[i, j] *= vector[j] - - -def flipRows(array): - if len(array.shape) == 2: - return numpy.array([numpy.array(array[1, :]), numpy.array(array[0, :])], 'float64') - elif len(array.shape) == 1: - return numpy.array([array[1], array[0]], 'float64') - else: - raise Exception('Input type not supportd by flipRows') - - -if hasattr(KMeansTrainer, 'KMEANS_PLUS_PLUS'): - def test_kmeans_plus_plus(): - # Tests the K-Means++ initialization - dim_c = 5 - dim_d = 7 - n_samples = 150 - data = numpy.random.randn(n_samples, dim_d) - seed = 0 - - # C++ implementation - machine = KMeansMachine(dim_c, dim_d) - trainer = KMeansTrainer() - trainer.rng = bob.core.random.mt19937(seed) - trainer.initialization_method = 'KMEANS_PLUS_PLUS' - trainer.initialize(machine, data) - - # Python implementation - py_machine = KMeansMachine(dim_c, dim_d) - kmeans_plus_plus(py_machine, data, seed) - assert equals(machine.means, py_machine.means, 1e-8) - - -def test_kmeans_noduplicate(): - # Data/dimensions - dim_c = 2 - dim_d = 3 - seed = 0 - data = numpy.array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [4, 5, 6.]]) - # Defines machine and trainer - machine = KMeansMachine(dim_c, dim_d) - trainer = KMeansTrainer() - rng = bob.core.random.mt19937(seed) - trainer.initialization_method = 'RANDOM_NO_DUPLICATE' - trainer.initialize(machine, data, rng) - # Makes sure that the two initial mean vectors selected are different - assert equals(machine.get_mean(0), machine.get_mean(1), 1e-8) == False - - -def test_kmeans_a(): - # Trains a KMeansMachine - # This files contains draws from two 1D Gaussian distributions: - # * 100 samples from N(-10,1) - # * 100 samples from N(10,1) - data = bob.io.base.load(datafile("samplesFrom2G_f64.hdf5", __name__, path="../data/")) - - machine = KMeansMachine(2, 1) - - trainer = KMeansTrainer() - # trainer.train(machine, data) - bob.learn.em.train(trainer, machine, data) - - [variances, weights] = machine.get_variances_and_weights_for_each_cluster(data) - variances_b = numpy.ndarray(shape=(2, 1), dtype=numpy.float64) - weights_b = numpy.ndarray(shape=(2,), dtype=numpy.float64) - machine.__get_variances_and_weights_for_each_cluster_init__(variances_b, weights_b) - machine.__get_variances_and_weights_for_each_cluster_acc__(data, variances_b, weights_b) - machine.__get_variances_and_weights_for_each_cluster_fin__(variances_b, weights_b) - m1 = machine.get_mean(0) - m2 = machine.get_mean(1) - - ## Check means [-10,10] / variances [1,1] / weights [0.5,0.5] - if (m1 < m2): - means = numpy.array(([m1[0], m2[0]]), 'float64') - else: - means = numpy.array(([m2[0], m1[0]]), 'float64') - assert equals(means, numpy.array([-10., 10.]), 2e-1) - assert equals(variances, numpy.array([1., 1.]), 2e-1) - assert equals(weights, numpy.array([0.5, 0.5]), 1e-3) - - assert equals(variances, variances_b, 1e-8) - assert equals(weights, weights_b, 1e-8) - - -def test_kmeans_b(): - # Trains a KMeansMachine - (arStd, std) = NormalizeStdArray(datafile("faithful.torch3.hdf5", __name__, path="../data/")) - - machine = KMeansMachine(2, 2) - - trainer = KMeansTrainer() - # trainer.seed = 1337 - bob.learn.em.train(trainer, machine, arStd, convergence_threshold=0.001) - - [variances, weights] = machine.get_variances_and_weights_for_each_cluster(arStd) - - means = numpy.array(machine.means) - variances = numpy.array(variances) - - multiplyVectorsByFactors(means, std) - multiplyVectorsByFactors(variances, std ** 2) - - gmmWeights = bob.io.base.load(datafile('gmm.init_weights.hdf5', __name__, path="../data/")) - gmmMeans = bob.io.base.load(datafile('gmm.init_means.hdf5', __name__, path="../data/")) - gmmVariances = bob.io.base.load(datafile('gmm.init_variances.hdf5', __name__, path="../data/")) - - if (means[0, 0] < means[1, 0]): - means = flipRows(means) - variances = flipRows(variances) - weights = flipRows(weights) - - assert equals(means, gmmMeans, 1e-3) - assert equals(weights, gmmWeights, 1e-3) - assert equals(variances, gmmVariances, 1e-3) - - # Check that there is no duplicate means during initialization - machine = KMeansMachine(2, 1) - trainer = KMeansTrainer() - trainer.initialization_method = 'RANDOM_NO_DUPLICATE' - data = numpy.array([[1.], [1.], [1.], [1.], [1.], [1.], [2.], [3.]]) - bob.learn.em.train(trainer, machine, data) - assert (numpy.isnan(machine.means).any()) == False - - -def test_kmeans_parallel(): - # Trains a KMeansMachine - (arStd, std) = NormalizeStdArray(datafile("faithful.torch3.hdf5", __name__, path="../data/")) - - machine = KMeansMachine(2, 2) - - trainer = KMeansTrainer() - # trainer.seed = 1337 - - import multiprocessing.pool - pool = multiprocessing.pool.ThreadPool(3) - bob.learn.em.train(trainer, machine, arStd, convergence_threshold=0.001, pool = pool) - - [variances, weights] = machine.get_variances_and_weights_for_each_cluster(arStd) - - means = numpy.array(machine.means) - variances = numpy.array(variances) - - multiplyVectorsByFactors(means, std) - multiplyVectorsByFactors(variances, std ** 2) - - gmmWeights = bob.io.base.load(datafile('gmm.init_weights.hdf5', __name__, path="../data/")) - gmmMeans = bob.io.base.load(datafile('gmm.init_means.hdf5', __name__, path="../data/")) - gmmVariances = bob.io.base.load(datafile('gmm.init_variances.hdf5', __name__, path="../data/")) - - if (means[0, 0] < means[1, 0]): - means = flipRows(means) - variances = flipRows(variances) - weights = flipRows(weights) - - assert equals(means, gmmMeans, 1e-3) - assert equals(weights, gmmWeights, 1e-3) - assert equals(variances, gmmVariances, 1e-3) - - -def test_trainer_execption(): - from nose.tools import assert_raises - - # Testing Inf - machine = KMeansMachine(2, 2) - data = numpy.array([[1.0, 2.0], [2, 3.], [1, 1.], [2, 5.], [numpy.inf, 1.0]]) - trainer = KMeansTrainer() - assert_raises(ValueError, bob.learn.em.train, trainer, machine, data, 10) - - # Testing Nan - machine = KMeansMachine(2, 2) - data = numpy.array([[1.0, 2.0], [2, 3.], [1, numpy.nan], [2, 5.], [2.0, 1.0]]) - trainer = KMeansTrainer() - assert_raises(ValueError, bob.learn.em.train, trainer, machine, data, 10) diff --git a/bob/learn/em/test/test_linearscoring.py b/bob/learn/em/test/test_linearscoring.py index 9958e20bd2e91c09d7adc9fb655897ba9e749ff6..55aa3f4d759f4050c8a73e4d57a5eacafcd5241d 100644 --- a/bob/learn/em/test/test_linearscoring.py +++ b/bob/learn/em/test/test_linearscoring.py @@ -8,121 +8,121 @@ """Tests on the LinearScoring function """ -import numpy +import numpy as np -from bob.learn.em import GMMMachine, GMMStats, linear_scoring +from bob.learn.em.mixture import GMMMachine +from bob.learn.em.mixture import GMMStats +from bob.learn.em.mixture import linear_scoring def test_LinearScoring(): - ubm = GMMMachine(2, 2) - ubm.weights = numpy.array([0.5, 0.5], 'float64') - ubm.means = numpy.array([[3, 70], [4, 72]], 'float64') - ubm.variances = numpy.array([[1, 10], [2, 5]], 'float64') - ubm.variance_thresholds = numpy.array([[0, 0], [0, 0]], 'float64') + ubm = GMMMachine(n_gaussians=2) + ubm.weights = np.array([0.5, 0.5], 'float64') + ubm.means = np.array([[3, 70], [4, 72]], 'float64') + ubm.variances = np.array([[1, 10], [2, 5]], 'float64') + ubm.variance_thresholds = np.array([[0, 0], [0, 0]], 'float64') - model1 = GMMMachine(2, 2) - model1.weights = numpy.array([0.5, 0.5], 'float64') - model1.means = numpy.array([[1, 2], [3, 4]], 'float64') - model1.variances = numpy.array([[9, 10], [11, 12]], 'float64') - model1.variance_thresholds = numpy.array([[0, 0], [0, 0]], 'float64') + model1 = GMMMachine(n_gaussians=2) + model1.weights = np.array([0.5, 0.5], 'float64') + model1.means = np.array([[1, 2], [3, 4]], 'float64') + model1.variances = np.array([[9, 10], [11, 12]], 'float64') + model1.variance_thresholds = np.array([[0, 0], [0, 0]], 'float64') - model2 = GMMMachine(2, 2) - model2.weights = numpy.array([0.5, 0.5], 'float64') - model2.means = numpy.array([[5, 6], [7, 8]], 'float64') - model2.variances = numpy.array([[13, 14], [15, 16]], 'float64') - model2.variance_thresholds = numpy.array([[0, 0], [0, 0]], 'float64') + model2 = GMMMachine(n_gaussians=2) + model2.weights = np.array([0.5, 0.5], 'float64') + model2.means = np.array([[5, 6], [7, 8]], 'float64') + model2.variances = np.array([[13, 14], [15, 16]], 'float64') + model2.variance_thresholds = np.array([[0, 0], [0, 0]], 'float64') stats1 = GMMStats(2, 2) - stats1.sum_px = numpy.array([[1, 2], [3, 4]], 'float64') - stats1.n = numpy.array([1, 2], 'float64') + stats1.sum_px = np.array([[1, 2], [3, 4]], 'float64') + stats1.n = np.array([1, 2], 'float64') stats1.t = 1+2 stats2 = GMMStats(2, 2) - stats2.sum_px = numpy.array([[5, 6], [7, 8]], 'float64') - stats2.n = numpy.array([3, 4], 'float64') + stats2.sum_px = np.array([[5, 6], [7, 8]], 'float64') + stats2.n = np.array([3, 4], 'float64') stats2.t = 3+4 stats3 = GMMStats(2, 2) - stats3.sum_px = numpy.array([[5, 6], [7, 3]], 'float64') - stats3.n = numpy.array([3, 4], 'float64') + stats3.sum_px = np.array([[5, 6], [7, 3]], 'float64') + stats3.n = np.array([3, 4], 'float64') stats3.t = 3+4 - test_channeloffset = [numpy.array([9, 8, 7, 6], 'float64'), numpy.array([5, 4, 3, 2], 'float64'), numpy.array([1, 0, 1, 2], 'float64')] + test_channeloffset = [np.array([[9, 8], [7, 6]], 'float64'), np.array([[5, 4], [3, 2]], 'float64'), np.array([[1, 0], [1, 2]], 'float64')] # Reference scores (from Idiap internal matlab implementation) - ref_scores_00 = numpy.array([[2372.9, 5207.7, 5275.7], [2215.7, 4868.1, 4932.1]], 'float64') - ref_scores_01 = numpy.array( [[790.9666666666667, 743.9571428571428, 753.6714285714285], [738.5666666666667, 695.4428571428572, 704.5857142857144]], 'float64') - ref_scores_10 = numpy.array([[2615.5, 5434.1, 5392.5], [2381.5, 4999.3, 5022.5]], 'float64') - ref_scores_11 = numpy.array([[871.8333333333332, 776.3000000000001, 770.3571428571427], [793.8333333333333, 714.1857142857143, 717.5000000000000]], 'float64') + ref_scores_00 = np.array([[2372.9, 5207.7, 5275.7], [2215.7, 4868.1, 4932.1]], 'float64') + ref_scores_01 = np.array( [[790.9666666666667, 743.9571428571428, 753.6714285714285], [738.5666666666667, 695.4428571428572, 704.5857142857144]], 'float64') + ref_scores_10 = np.array([[2615.5, 5434.1, 5392.5], [2381.5, 4999.3, 5022.5]], 'float64') + ref_scores_11 = np.array([[871.8333333333332, 776.3000000000001, 770.3571428571427], [793.8333333333333, 714.1857142857143, 717.5000000000000]], 'float64') # 1/ Use GMMMachines # 1/a/ Without test_channelOffset, without frame-length normalisation scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3]) - assert (abs(scores - ref_scores_00) < 1e-7).all() + np.testing.assert_almost_equal(scores, ref_scores_00, decimal=7) # 1/b/ Without test_channelOffset, with frame-length normalisation - scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], [], True) - assert (abs(scores - ref_scores_01) < 1e-7).all() - #scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], (), True) - #assert (abs(scores - ref_scores_01) < 1e-7).all() - #scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], None, True) - #assert (abs(scores - ref_scores_01) < 1e-7).all() + scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], frame_length_normalization=True) + np.testing.assert_almost_equal(scores, ref_scores_01, decimal=7) + scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], 0, True) + np.testing.assert_almost_equal(scores, ref_scores_01, decimal=7) # 1/c/ With test_channelOffset, without frame-length normalisation scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], test_channeloffset) - assert (abs(scores - ref_scores_10) < 1e-7).all() + np.testing.assert_almost_equal(scores, ref_scores_10, decimal=7) # 1/d/ With test_channelOffset, with frame-length normalisation - scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], test_channeloffset, True) - assert (abs(scores - ref_scores_11) < 1e-7).all() + scores = linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], test_channeloffset, frame_length_normalization=True) + np.testing.assert_almost_equal(scores, ref_scores_11, decimal=7) - # 2/ Use mean/variance supervectors + # 2/ Use means instead of models # 2/a/ Without test_channelOffset, without frame-length normalisation - scores = linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3]) + scores = linear_scoring([model1.means, model2.means], ubm, [stats1, stats2, stats3]) assert (abs(scores - ref_scores_00) < 1e-7).all() # 2/b/ Without test_channelOffset, with frame-length normalisation - scores = linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], [], True) + scores = linear_scoring([model1.means, model2.means], ubm, [stats1, stats2, stats3], frame_length_normalization=True) assert (abs(scores - ref_scores_01) < 1e-7).all() # 2/c/ With test_channelOffset, without frame-length normalisation - scores = linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], test_channeloffset) + scores = linear_scoring([model1.means, model2.means], ubm, [stats1, stats2, stats3], test_channeloffset) assert (abs(scores - ref_scores_10) < 1e-7).all() # 2/d/ With test_channelOffset, with frame-length normalisation - scores = linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], test_channeloffset, True) + scores = linear_scoring([model1.means, model2.means], ubm, [stats1, stats2, stats3], test_channeloffset, frame_length_normalization=True) assert (abs(scores - ref_scores_11) < 1e-7).all() # 3/ Using single model/sample # 3/a/ without frame-length normalisation - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0]) - assert abs(score - ref_scores_10[0,0]) < 1e-7 - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1]) - assert abs(score - ref_scores_10[0,1]) < 1e-7 - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2]) - assert abs(score - ref_scores_10[0,2]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0]) - assert abs(score - ref_scores_10[1,0]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1]) - assert abs(score - ref_scores_10[1,1]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2]) - assert abs(score - ref_scores_10[1,2]) < 1e-7 - - - # 3/b/ without frame-length normalisation - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0], True) - assert abs(score - ref_scores_11[0,0]) < 1e-7 - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1], True) - assert abs(score - ref_scores_11[0,1]) < 1e-7 - score = linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2], True) - assert abs(score - ref_scores_11[0,2]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0], True) - assert abs(score - ref_scores_11[1,0]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1], True) - assert abs(score - ref_scores_11[1,1]) < 1e-7 - score = linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2], True) - assert abs(score - ref_scores_11[1,2]) < 1e-7 + score = linear_scoring(model1.means, ubm, stats1, test_channeloffset[0]) + np.testing.assert_almost_equal(score, ref_scores_10[0,0], decimal=7) + score = linear_scoring(model1.means, ubm, stats2, test_channeloffset[1]) + np.testing.assert_almost_equal(score, ref_scores_10[0,1], decimal=7) + score = linear_scoring(model1.means, ubm, stats3, test_channeloffset[2]) + np.testing.assert_almost_equal(score, ref_scores_10[0,2], decimal=7) + score = linear_scoring(model2.means, ubm, stats1, test_channeloffset[0]) + np.testing.assert_almost_equal(score, ref_scores_10[1,0], decimal=7) + score = linear_scoring(model2.means, ubm, stats2, test_channeloffset[1]) + np.testing.assert_almost_equal(score, ref_scores_10[1,1], decimal=7) + score = linear_scoring(model2.means, ubm, stats3, test_channeloffset[2]) + np.testing.assert_almost_equal(score, ref_scores_10[1,2], decimal=7) + + + # 3/b/ with frame-length normalisation + score = linear_scoring(model1.means, ubm, stats1, test_channeloffset[0], True) + np.testing.assert_almost_equal(score, ref_scores_11[0,0], decimal=7) + score = linear_scoring(model1.means, ubm, stats2, test_channeloffset[1], True) + np.testing.assert_almost_equal(score, ref_scores_11[0,1], decimal=7) + score = linear_scoring(model1.means, ubm, stats3, test_channeloffset[2], True) + np.testing.assert_almost_equal(score, ref_scores_11[0,2], decimal=7) + score = linear_scoring(model2.means, ubm, stats1, test_channeloffset[0], True) + np.testing.assert_almost_equal(score, ref_scores_11[1,0], decimal=7) + score = linear_scoring(model2.means, ubm, stats2, test_channeloffset[1], True) + np.testing.assert_almost_equal(score, ref_scores_11[1,1], decimal=7) + score = linear_scoring(model2.means, ubm, stats3, test_channeloffset[2], True) + np.testing.assert_almost_equal(score, ref_scores_11[1,2], decimal=7) diff --git a/bob/learn/em/test/test_picklability.py b/bob/learn/em/test/test_picklability.py index b3c6948ea3aee570f5cce3d7ad07259523b94583..f83c8c9541dce817b84a9300496301141dc7e1be 100644 --- a/bob/learn/em/test/test_picklability.py +++ b/bob/learn/em/test/test_picklability.py @@ -2,83 +2,11 @@ # vim: set fileencoding=utf-8 : # Tiago de Freitas Pereira <tiago.pereira@idiap.ch> -from bob.learn.em import ( - GMMMachine, - ISVBase, - ISVMachine, - KMeansMachine, - GMMStats, - IVectorMachine, -) -import numpy import pickle +import numpy -def test_gmm_machine(): - gmm_machine = GMMMachine(3, 3) - gmm_machine.means = numpy.arange(9).reshape(3, 3).astype("float") - gmm_machine_after_pickle = pickle.loads(pickle.dumps(gmm_machine)) - - assert numpy.allclose( - gmm_machine_after_pickle.means, gmm_machine_after_pickle.means, 10e-3 - ) - assert numpy.allclose( - gmm_machine_after_pickle.variances, gmm_machine_after_pickle.variances, 10e-3 - ) - assert numpy.allclose( - gmm_machine_after_pickle.weights, gmm_machine_after_pickle.weights, 10e-3 - ) - - -def test_isv_base(): - ubm = GMMMachine(3, 3) - ubm.means = numpy.arange(9).reshape(3, 3).astype("float") - isv_base = ISVBase(ubm, 2) - isv_base.u = numpy.arange(18).reshape(9, 2).astype("float") - isv_base.d = numpy.arange(9).astype("float") - - isv_base_after_pickle = pickle.loads(pickle.dumps(isv_base)) - - assert numpy.allclose(isv_base.u, isv_base_after_pickle.u, 10e-3) - assert numpy.allclose(isv_base.d, isv_base_after_pickle.d, 10e-3) - - -def test_isv_machine(): - - # Creates a UBM - weights = numpy.array([0.4, 0.6], "float64") - means = numpy.array([[1, 6, 2], [4, 3, 2]], "float64") - variances = numpy.array([[1, 2, 1], [2, 1, 2]], "float64") - ubm = GMMMachine(2, 3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a ISVBaseMachine - U = numpy.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], "float64") - # V = numpy.array([[0], [0], [0], [0], [0], [0]], 'float64') - d = numpy.array([0, 1, 0, 1, 0, 1], "float64") - base = ISVBase(ubm, 2) - base.u = U - base.d = d - - # Creates a ISVMachine - z = numpy.array([3, 4, 1, 2, 0, 1], "float64") - x = numpy.array([1, 2], "float64") - isv_machine = ISVMachine(base) - isv_machine.z = z - isv_machine.x = x - - isv_machine_after_pickle = pickle.loads(pickle.dumps(isv_machine)) - assert numpy.allclose( - isv_machine_after_pickle.isv_base.u, isv_machine.isv_base.u, 10e-3 - ) - assert numpy.allclose( - isv_machine_after_pickle.isv_base.d, isv_machine.isv_base.d, 10e-3 - ) - assert numpy.allclose(isv_machine_after_pickle.x, isv_machine.x, 10e-3) - assert numpy.allclose(isv_machine_after_pickle.z, isv_machine.z, 10e-3) - +from bob.learn.em.cluster import KMeansMachine def test_kmeans_machine(): # Test a KMeansMachine @@ -94,49 +22,3 @@ def test_kmeans_machine(): assert numpy.allclose( kmeans_machine_after_pickle.means, kmeans_machine.means, 10e-3 ) - - -def test_gmmstats(): - - gs = GMMStats(2, 3) - log_likelihood = -3.0 - T = 1 - n = numpy.array([0.4, 0.6], numpy.float64) - sumpx = numpy.array([[1.0, 2.0, 3.0], [2.0, 4.0, 3.0]], numpy.float64) - sumpxx = numpy.array([[10.0, 20.0, 30.0], [40.0, 50.0, 60.0]], numpy.float64) - gs.log_likelihood = log_likelihood - gs.t = T - gs.n = n - gs.sum_px = sumpx - gs.sum_pxx = sumpxx - - gs_after_pickle = pickle.loads(pickle.dumps(gs)) - assert gs == gs_after_pickle - - -def test_ivector_machine(): - - # Ubm - ubm = GMMMachine(2, 3) - ubm.weights = numpy.array([0.4, 0.6]) - ubm.means = numpy.array([[1.0, 7, 4], [4, 5, 3]]) - ubm.variances = numpy.array([[0.5, 1.0, 1.5], [1.0, 1.5, 2.0]]) - - ivector_machine = IVectorMachine(ubm, 2) - t = numpy.array([[1.0, 2], [4, 1], [0, 3], [5, 8], [7, 10], [11, 1]]) - sigma = numpy.array([1.0, 2.0, 1.0, 3.0, 2.0, 4.0]) - ivector_machine.t = t - ivector_machine.sigma = sigma - - ivector_after_pickle = pickle.loads(pickle.dumps(ivector_machine)) - assert numpy.allclose(ivector_after_pickle.sigma, ivector_machine.sigma, 10e-3) - assert numpy.allclose(ivector_after_pickle.t, ivector_machine.t, 10e-3) - assert numpy.allclose( - ivector_after_pickle.ubm.means, ivector_machine.ubm.means, 10e-3 - ) - assert numpy.allclose( - ivector_after_pickle.ubm.variances, ivector_machine.ubm.variances, 10e-3 - ) - assert numpy.allclose( - ivector_after_pickle.ubm.weights, ivector_machine.ubm.weights, 10e-3 - ) diff --git a/bob/learn/em/test/test_plda.py b/bob/learn/em/test/test_plda.py deleted file mode 100644 index b51a5989e44eee349b084ee8fecf7a87f0a19b4e..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_plda.py +++ /dev/null @@ -1,568 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Sat Oct 22 23:01:09 2011 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Tests PLDA machine -""" - -import numpy -import os -import tempfile -import nose.tools -import math - -import bob.io.base - -from bob.learn.em import PLDABase, PLDAMachine - -# Defines common variables globally -# Dimensionalities -C_dim_d = 7 -C_dim_f = 2 -C_dim_g = 3 -# Values for F and G -C_G=numpy.array([-1.1424, -0.5044, -0.1917, - -0.6249, 0.1021, -0.8658, - -1.1687, 1.1963, 0.1807, - 0.3926, 0.1203, 1.2665, - 1.3018, -1.0368, -0.2512, - -0.5936, -0.8571, -0.2046, - 0.4364, -0.1699, -2.2015], 'float64').reshape(C_dim_d, C_dim_g) -# F <-> PCA on G -C_F=numpy.array([-0.054222647972093, -0.000000000783146, - 0.596449127693018, 0.000000006265167, - 0.298224563846509, 0.000000003132583, - 0.447336845769764, 0.000000009397750, - -0.108445295944185, -0.000000001566292, - -0.501559493741856, -0.000000006265167, - -0.298224563846509, -0.000000003132583], 'float64').reshape(C_dim_d, C_dim_f) - -def equals(x, y, epsilon): - return (abs(x - y) < epsilon).all() - -def compute_i_sigma(sigma): - # Inverse of a diagonal matrix (represented by a 1D numpy array) - return (1. / sigma) - -def compute_alpha(G, sigma): - # alpha = (Id + G^T.sigma^-1.G)^-1 = \mathcal{G} - dim_g = G.shape[1] - isigma = numpy.diag(compute_i_sigma(sigma)) - return numpy.linalg.inv(numpy.eye(dim_g) + numpy.dot(numpy.dot(G.transpose(), isigma), G)) - -def compute_beta(G, sigma): - # beta = (sigma + G.G^T)^-1 = sigma^-1 - sigma^-1.G.alpha.G^T.sigma^-1 = \mathcal{S} - isigma = numpy.diag(compute_i_sigma(sigma)) - gt_isigma = numpy.dot(G.transpose(), isigma) - alpha = compute_alpha(G, sigma) - return (isigma - numpy.dot(numpy.dot(gt_isigma.transpose(), alpha), gt_isigma)) - -def compute_gamma(F, G, sigma, a): - # gamma_a = (Id + a.F^T.beta.F)^-1 = \mathcal{F}_{a} - dim_f = F.shape[1] - beta = compute_beta(G, sigma) - return numpy.linalg.inv(numpy.eye(dim_f) + a * numpy.dot(numpy.dot(F.transpose(), beta), F)) - -def compute_ft_beta(F, G, sigma): - # F^T.beta = F^T.\mathcal{S} - beta = compute_beta(G, sigma) - return numpy.dot(numpy.transpose(F), beta) - -def compute_gt_i_sigma(G, sigma): - # G^T.sigma^-1 - isigma = compute_i_sigma(sigma) - return numpy.transpose(G) * isigma - -def compute_logdet_alpha(G, sigma): - # \log(\det(\alpha)) = \log(\det(\mathcal{G})) - alpha = compute_alpha(G, sigma) - return math.log(numpy.linalg.det(alpha)) - -def compute_logdet_sigma(sigma): - # \log(\det(\sigma)) = \log(\det(\sigma)) = \log(\prod(\sigma_{i})) - return math.log(numpy.prod(sigma)) - -def compute_loglike_constterm(F, G, sigma, a): - # loglike_constterm[a] = a/2 * ( -D*\log(2*pi) -\log|\sigma| +\log|\alpha| +\log|\gamma_a|) - gamma_a = compute_gamma(F, G, sigma, a) - logdet_gamma_a = math.log(abs(numpy.linalg.det(gamma_a))) - ah = a/2. - dim_d = F.shape[0] - logdet_sigma = compute_logdet_sigma(sigma) - logdet_alpha = compute_logdet_alpha(G, sigma) - res = -ah*dim_d*math.log(2*math.pi) - ah*logdet_sigma + ah*logdet_alpha + logdet_gamma_a/2. - return res; - -def compute_log_likelihood_point_estimate(observation, mu, F, G, sigma, hi, wij): - """ - This function computes p(x_{ij} | h_{i}, w_{ij}, \Theta), which is given by - N_{x}[\mu + Fh_{i} + Gw_{ij} + epsilon_{ij}, \Sigma], N_{x} being a - Gaussian distribution. As it returns the corresponding log likelihood, - this is given by the sum of the following three terms: - C1 = -dim_d/2 log(2pi) - C2 = -1/2 log(det(\Sigma)) - C3 = -1/2 (x_{ij}-\mu-Fh_{i}-Gw_{ij})^{T}\Sigma^{-1}(x_{ij}-\mu-Fh_{i}-Gw_{ij}) - """ - - ### Pre-computes some of the constants - dim_d = observation.shape[0] # A scalar - log_2pi = numpy.log(2. * numpy.pi) # A scalar - C1 = -(dim_d / 2.) * log_2pi # A scalar - C2 = -(1. / 2.) * numpy.sum( numpy.log(sigma) ) # (dim_d, 1) - - ### Subtract the identity and session components from the observed vector. - session_plus_identity = numpy.dot(F, hi) + numpy.dot(G, wij) - normalised_observation = numpy.reshape(observation - mu - session_plus_identity, (dim_d,1)) - ### Now calculate C3 - sigma_inverse = numpy.reshape(1. / sigma, (dim_d,1)) # (dim_d, 1) - C3 = -(1. / 2.) * numpy.sum(normalised_observation * sigma_inverse * normalised_observation) - - ### Returns the log likelihood - log_likelihood = C1 + C2 + C3 - return (log_likelihood) - - -def compute_log_likelihood(observations, mu, F, G, sigma): - """ - This function computes the log-likelihood of the observations given the parameters - of the PLDA model. This is done by fulling integrating out the latent variables. - """ - # Work out the number of samples that we have and normalise the data. - J_i = observations.shape[0]; # An integer > 0 - norm_observations = observations - numpy.tile(mu, [J_i,1]); # (J_i, D_x) - - # There are three terms that need to be computed: C1, C2 and C3 - - # 1. Computes C1 - # C1 = - J_{i} * dim_d/2 log(2*pi) - dim_d = observations.shape[1] # A scalar - dim_f = F.shape[1] - log_2pi = numpy.log(2. * numpy.pi); # A scalar - C1 = - J_i * (dim_d / 2.) * log_2pi; # A scalar - - # 2. Computes C2 - # C2 = - J_i/2 * [log(det(sigma)) - log(det(alpha^-1))] + log(det(gamma_{J_i}))/2 - ld_sigma = compute_logdet_sigma(sigma) - ld_alpha = compute_logdet_alpha(G, sigma) - gamma = compute_gamma(F, G, sigma, J_i) - ld_gamma = math.log(numpy.linalg.det(gamma)) - C2 = - J_i/2.*(ld_sigma - ld_alpha) + ld_gamma/2. - - # 3. Computes C3 - # This is a quadratic part and consists of - # C3 = -0.5 * sum x^T beta x + 0.5 * Quadratic term in x - # C3 = -0.5 * (C3a - C3b) - C3a = 0.0; - C3b_sum_part = numpy.zeros((dim_f,1)); - isigma = numpy.diag(compute_i_sigma(sigma)) - beta = compute_beta(G, sigma) - ft_beta = numpy.dot(numpy.transpose(F), beta) - for j in range(0, J_i): - ### Calculations for C3a - current_vector = numpy.reshape(norm_observations[j,:], (dim_d,1)); # (D_x, 1) - vector_E = numpy.dot(beta, current_vector); # (D_x, 1) - current_result = numpy.dot(current_vector.transpose(), vector_E); # A floating point value - C3a = C3a + current_result[0][0]; # A floating point value - ### Calculations for C3b - C3b_sum_part = C3b_sum_part + numpy.dot(ft_beta, current_vector); # (nf, 1) - - ### Final calculations for C3b, using the matrix gamma_{J_i} - C3b = numpy.dot(numpy.dot(C3b_sum_part.transpose(), gamma), C3b_sum_part); - C3 = -0.5 * (C3a - C3b[0][0]); - - return C1 + C2 + C3 - - -def test_plda_basemachine(): - # Data used for performing the tests - sigma = numpy.ndarray(C_dim_d, 'float64') - sigma.fill(0.01) - mu = numpy.ndarray(C_dim_d, 'float64') - mu.fill(0) - - # Defines reference results based on matlab - alpha_ref = numpy.array([ 0.002189051545735, 0.001127099941432, - -0.000145483208153, 0.001127099941432, 0.003549267943741, - -0.000552001405453, -0.000145483208153, -0.000552001405453, - 0.001440505362615], 'float64').reshape(C_dim_g, C_dim_g) - beta_ref = numpy.array([ 50.587191765140361, -14.512478352504877, - -0.294799164567830, 13.382002504394316, 9.202063877660278, - -43.182264846086497, 11.932345916716455, -14.512478352504878, - 82.320149045633045, -12.605578822979698, 19.618675892079366, - 13.033691341150439, -8.004874490989799, -21.547363307109187, - -0.294799164567832, -12.605578822979696, 52.123885798398241, - 4.363739008635009, 44.847177605628545, 16.438137537463710, - 5.137421840557050, 13.382002504394316, 19.618675892079366, - 4.363739008635011, 75.070401560513488, -4.515472972526140, - 9.752862741017488, 34.196127678931106, 9.202063877660285, - 13.033691341150439, 44.847177605628552, -4.515472972526142, - 56.189416227691098, -7.536676357632515, -10.555735414707383, - -43.182264846086497, -8.004874490989799, 16.438137537463703, - 9.752862741017490, -7.536676357632518, 56.430571485722126, - 9.471758169835317, 11.932345916716461, -21.547363307109187, - 5.137421840557051, 34.196127678931099, -10.555735414707385, - 9.471758169835320, 27.996266602110637], 'float64').reshape(C_dim_d, C_dim_d) - gamma3_ref = numpy.array([ 0.005318799462241, -0.000000012993151, - -0.000000012993151, 0.999999999999996], 'float64').reshape(C_dim_f, C_dim_f) - - # Constructor tests - #m = PLDABase() - #assert m.dim_d == 0 - #assert m.dim_f == 0 - #assert m.dim_g == 0 - #del m - m = PLDABase(C_dim_d, C_dim_f, C_dim_g) - assert m.shape[0] == C_dim_d - assert m.shape[1] == C_dim_f - assert m.shape[2] == C_dim_g - assert abs(m.variance_threshold - 0.) < 1e-10 - del m - m = PLDABase(C_dim_d, C_dim_f, C_dim_g, 1e-2) - assert m.shape[0] == C_dim_d - assert m.shape[1] == C_dim_f - assert m.shape[2] == C_dim_g - assert abs(m.variance_threshold - 1e-2) < 1e-10 - del m - - # Defines base machine - m = PLDABase(C_dim_d, C_dim_f, C_dim_g) - #m.resize(C_dim_d, C_dim_f, C_dim_g) - # Sets the current mu, F, G and sigma - m.mu = mu - m.f = C_F - m.g = C_G - m.sigma = sigma - gamma3 = m.get_add_gamma(3).copy() - constTerm3 = m.get_add_log_like_const_term(3) - - # Compares precomputed values to matlab reference - for ii in range(m.__alpha__.shape[0]): - for jj in range(m.__alpha__.shape[1]): - absdiff = abs(m.__alpha__[ii,jj]- alpha_ref[ii,jj]) - assert absdiff < 1e-10, 'PLDABase alpha matrix does not match reference at (%d,%d) to 10^-10: |%g-%g| = %g' % (ii, jj, m.__alpha__[ii,jj], alpha_ref[ii,jj], absdiff) - assert equals(m.__alpha__, alpha_ref, 1e-10) - assert equals(m.__beta__, beta_ref, 1e-10) - assert equals(gamma3, gamma3_ref, 1e-10) - - # Compares precomputed values to the ones returned by python implementation - assert equals(m.__isigma__, compute_i_sigma(sigma), 1e-10) - assert equals(m.__alpha__, compute_alpha(C_G,sigma), 1e-10) - assert equals(m.__beta__, compute_beta(C_G,sigma), 1e-10) - assert equals(m.get_add_gamma(3), compute_gamma(C_F,C_G,sigma,3), 1e-10) - assert m.has_gamma(3) - assert equals(m.get_gamma(3), compute_gamma(C_F,C_G,sigma,3), 1e-10) - assert equals(m.__ft_beta__, compute_ft_beta(C_F,C_G,sigma), 1e-10) - assert equals(m.__gt_i_sigma__, compute_gt_i_sigma(C_G,sigma), 1e-10) - assert math.fabs(m.__logdet_alpha__ - compute_logdet_alpha(C_G,sigma)) < 1e-10 - assert math.fabs(m.__logdet_sigma__ - compute_logdet_sigma(sigma)) < 1e-10 - assert abs(m.get_add_log_like_const_term(3) - compute_loglike_constterm(C_F,C_G,sigma,3)) < 1e-10 - assert m.has_log_like_const_term(3) - assert abs(m.get_log_like_const_term(3) - compute_loglike_constterm(C_F,C_G,sigma,3)) < 1e-10 - - # Defines base machine - del m - m = PLDABase(C_dim_d, C_dim_f, C_dim_g) - # Sets the current mu, F, G and sigma - m.mu = mu - m.f = C_F - m.g = C_G - m.sigma = sigma - gamma3 = m.get_add_gamma(3).copy() - constTerm3 = m.get_add_log_like_const_term(3) - - # Compares precomputed values to matlab reference - assert equals(m.__alpha__, alpha_ref, 1e-10) - assert equals(m.__beta__, beta_ref, 1e-10) - assert equals(gamma3, gamma3_ref, 1e-10) - - # values before being saved - isigma = m.__isigma__.copy() - alpha = m.__alpha__.copy() - beta = m.__beta__.copy() - FtBeta = m.__ft_beta__.copy() - GtISigma = m.__gt_i_sigma__.copy() - logdetAlpha = m.__logdet_alpha__ - logdetSigma = m.__logdet_sigma__ - - # Saves to file, loads and compares to original - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = PLDABase(bob.io.base.HDF5File(filename)) - - # Compares the values loaded with the former ones - assert m_loaded == m - assert (m_loaded != m) is False - assert equals(m_loaded.mu, mu, 1e-10) - assert equals(m_loaded.f, C_F, 1e-10) - assert equals(m_loaded.g, C_G, 1e-10) - assert equals(m_loaded.sigma, sigma, 1e-10) - assert equals(m_loaded.__isigma__, isigma, 1e-10) - assert equals(m_loaded.__alpha__, alpha, 1e-10) - assert equals(m_loaded.__beta__, beta, 1e-10) - assert equals(m_loaded.__ft_beta__, FtBeta, 1e-10) - assert equals(m_loaded.__gt_i_sigma__, GtISigma, 1e-10) - assert abs(m_loaded.__logdet_alpha__ - logdetAlpha) < 1e-10 - assert abs(m_loaded.__logdet_sigma__ - logdetSigma) < 1e-10 - assert m_loaded.has_gamma(3) - assert equals(m_loaded.get_gamma(3), gamma3_ref, 1e-10) - assert equals(m_loaded.get_add_gamma(3), gamma3_ref, 1e-10) - assert m_loaded.has_log_like_const_term(3) - assert abs(m_loaded.get_add_log_like_const_term(3) - constTerm3) < 1e-10 - - # Compares the values loaded with the former ones when copying - m_copy = PLDABase(m_loaded) - assert m_loaded == m_copy - assert (m_loaded != m_copy) is False - # Test clear_maps method - assert m_copy.has_gamma(3) - assert m_copy.has_log_like_const_term(3) - m_copy.clear_maps() - assert (m_copy.has_gamma(3)) is False - assert (m_copy.has_log_like_const_term(3)) is False - - # Check variance flooring thresholds-related methods - v_zo = numpy.array([0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]) - v_zo_ = 0.01 - v_zzo = numpy.array([0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001]) - v_zzo_ = 0.001 - m_copy.variance_threshold = v_zo_ - assert (m_loaded == m_copy) is False - assert m_loaded != m_copy - m_copy.variance_threshold = v_zzo_ - m_copy.sigma = v_zo - assert equals(m_copy.sigma, v_zo, 1e-10) - m_copy.variance_threshold = v_zo_ - m_copy.sigma = v_zzo - assert equals(m_copy.sigma, v_zo, 1e-10) - m_copy.variance_threshold = v_zzo_ - m_copy.sigma = v_zzo - assert equals(m_copy.sigma, v_zzo, 1e-10) - m_copy.variance_threshold = v_zo_ - assert equals(m_copy.sigma, v_zo, 1e-10) - - # Clean-up - os.unlink(filename) - - -def test_plda_basemachine_loglikelihood_pointestimate(): - - # Data used for performing the tests - # Features and subspaces dimensionality - sigma = numpy.ndarray(C_dim_d, 'float64') - sigma.fill(0.01) - mu = numpy.ndarray(C_dim_d, 'float64') - mu.fill(0) - xij = numpy.array([0.7, 1.3, 2.5, 0.3, 1.3, 2.7, 0.9]) - hi = numpy.array([-0.5, 0.5]) - wij = numpy.array([-0.1, 0.2, 0.3]) - - m = PLDABase(C_dim_d, C_dim_f, C_dim_g) - # Sets the current mu, F, G and sigma - m.mu = mu - m.f = C_F - m.g = C_G - m.sigma = sigma - - #assert equals(m.compute_log_likelihood_point_estimate(xij, hi, wij), compute_log_likelihood_point_estimate(xij, mu, C_F, C_G, sigma, hi, wij), 1e-6) - log_likelihood_point_estimate = m.compute_log_likelihood_point_estimate(xij, hi, wij) - log_likelihood_point_estimate_python = compute_log_likelihood_point_estimate(xij, mu, C_F, C_G, sigma, hi, wij) - assert equals(log_likelihood_point_estimate, log_likelihood_point_estimate_python, 1e-6) - - -def test_plda_machine(): - - # Data used for performing the tests - # Features and subspaces dimensionality - sigma = numpy.ndarray(C_dim_d, 'float64') - sigma.fill(0.01) - mu = numpy.ndarray(C_dim_d, 'float64') - mu.fill(0) - - # Defines base machine - mb = PLDABase(C_dim_d, C_dim_f, C_dim_g) - # Sets the current mu, F, G and sigma - mb.mu = mu - mb.f = C_F - mb.g = C_G - mb.sigma = sigma - - # Test constructors and dim getters - m = PLDAMachine(mb) - assert m.shape[0] == C_dim_d - assert m.shape[1]== C_dim_f - assert m.shape[2] == C_dim_g - - m0 = PLDAMachine(mb) - #m0.plda_base = mb - assert m0.shape[0] == C_dim_d - assert m0.shape[1] == C_dim_f - assert m0.shape[2] == C_dim_g - - # Defines machine - n_samples = 2 - WSumXitBetaXi = 0.37 - weightedSum = numpy.array([1.39,0.54], 'float64') - log_likelihood = -0.22 - - m.n_samples = n_samples - m.w_sum_xit_beta_xi = WSumXitBetaXi - m.weighted_sum = weightedSum - m.log_likelihood = log_likelihood - - gamma3 = m.get_add_gamma(3).copy() - constTerm3 = m.get_add_log_like_const_term(3) - - # Saves to file, loads and compares to original - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.base.HDF5File(filename, 'w')) - m_loaded = PLDAMachine(bob.io.base.HDF5File(filename), mb) - - # Compares the values loaded with the former ones - assert m_loaded == m - assert (m_loaded != m) is False - assert abs(m_loaded.n_samples - n_samples) < 1e-10 - assert abs(m_loaded.w_sum_xit_beta_xi - WSumXitBetaXi) < 1e-10 - assert equals(m_loaded.weighted_sum, weightedSum, 1e-10) - assert abs(m_loaded.log_likelihood - log_likelihood) < 1e-10 - assert m_loaded.has_gamma(3) - assert equals(m_loaded.get_add_gamma(3), gamma3, 1e-10) - assert equals(m_loaded.get_gamma(3), gamma3, 1e-10) - assert m_loaded.has_log_like_const_term(3) - assert abs(m_loaded.get_add_log_like_const_term(3) - constTerm3) < 1e-10 - assert abs(m_loaded.get_log_like_const_term(3) - constTerm3) < 1e-10 - - # Test clear_maps method - assert m_loaded.has_gamma(3) - assert m_loaded.has_log_like_const_term(3) - m_loaded.clear_maps() - assert (m_loaded.has_gamma(3)) is False - assert (m_loaded.has_log_like_const_term(3)) is False - - # Check exceptions - #m_loaded2 = PLDAMachine(bob.io.base.HDF5File(filename)) - #m_loaded2.load(bob.io.base.HDF5File(filename)) - #nose.tools.assert_raises(RuntimeError, getattr, m_loaded2, 'shape') - #nose.tools.assert_raises(RuntimeError, getattr, m_loaded2, 'dim_f') - #nose.tools.assert_raises(RuntimeError, getattr, m_loaded2, 'dim_g') - #nose.tools.assert_raises(RuntimeError, m_loaded2.forward, [1.]) - #nose.tools.assert_raises(RuntimeError, m_loaded2.compute_log_likelihood, [1.]) - - # Clean-up - os.unlink(filename) - - -def test_plda_machine_log_likelihood_Python(): - - # Data used for performing the tests - # Features and subspaces dimensionality - sigma = numpy.ndarray(C_dim_d, 'float64') - sigma.fill(0.01) - mu = numpy.ndarray(C_dim_d, 'float64') - mu.fill(0) - - # Defines base machine - mb = PLDABase(C_dim_d, C_dim_f, C_dim_g) - # Sets the current mu, F, G and sigma - mb.mu = mu - mb.f = C_F - mb.g = C_G - mb.sigma = sigma - - # Defines machine - m = PLDAMachine(mb) - - # Defines (random) samples and check compute_log_likelihood method - ar_e = numpy.random.randn(2,C_dim_d) - ar_p = numpy.random.randn(C_dim_d) - ar_s = numpy.vstack([ar_e, ar_p]) - assert abs(m.compute_log_likelihood(ar_s, False) - compute_log_likelihood(ar_s, mu, C_F, C_G, sigma)) < 1e-10 - ar_p2d = numpy.reshape(ar_p, (1,C_dim_d)) - - a = m.compute_log_likelihood(ar_p, False) - - assert abs(m.compute_log_likelihood(ar_p, False) - compute_log_likelihood(ar_p2d, mu, C_F, C_G, sigma)) < 1e-10 - - # Defines (random) samples and check forward method - ar2_e = numpy.random.randn(4,C_dim_d) - ar2_p = numpy.random.randn(C_dim_d) - ar2_s = numpy.vstack([ar2_e, ar2_p]) - m.log_likelihood = m.compute_log_likelihood(ar2_e, False) - llr = m.compute_log_likelihood(ar2_s, True) - (m.compute_log_likelihood(ar2_s, False) + m.log_likelihood) - assert abs(m(ar2_s) - llr) < 1e-10 - ar2_p2d = numpy.random.randn(3,C_dim_d) - ar2_s2d = numpy.vstack([ar2_e, ar2_p2d]) - llr2d = m.compute_log_likelihood(ar2_s2d, True) - (m.compute_log_likelihood(ar2_s2d, False) + m.log_likelihood) - assert abs(m(ar2_s2d) - llr2d) < 1e-10 - -def test_plda_machine_log_likelihood_Prince(): - - # Data used for performing the tests - # Features and subspaces dimensionality - D = 7 - nf = 2 - ng = 3 - - # initial values for F, G and sigma - G_init=numpy.array([-1.1424, -0.5044, -0.1917, - -0.6249, 0.1021, -0.8658, - -1.1687, 1.1963, 0.1807, - 0.3926, 0.1203, 1.2665, - 1.3018, -1.0368, -0.2512, - -0.5936, -0.8571, -0.2046, - 0.4364, -0.1699, -2.2015]).reshape(D,ng) - # F <-> PCA on G - F_init=numpy.array([-0.054222647972093, -0.000000000783146, - 0.596449127693018, 0.000000006265167, - 0.298224563846509, 0.000000003132583, - 0.447336845769764, 0.000000009397750, - -0.108445295944185, -0.000000001566292, - -0.501559493741856, -0.000000006265167, - -0.298224563846509, -0.000000003132583]).reshape(D,nf) - sigma_init = 0.01 * numpy.ones((D,), 'float64') - mean_zero = numpy.zeros((D,), 'float64') - - # base machine - mb = PLDABase(D,nf,ng) - mb.sigma = sigma_init - mb.g = G_init - mb.f = F_init - mb.mu = mean_zero - - # Data for likelihood computation - x1 = numpy.array([0.8032, 0.3503, 0.4587, 0.9511, 0.1330, 0.0703, 0.7061]) - x2 = numpy.array([0.9317, 0.1089, 0.6517, 0.1461, 0.6940, 0.6256, 0.0437]) - x3 = numpy.array([0.7979, 0.9862, 0.4367, 0.3447, 0.0488, 0.2252, 0.5810]) - X = numpy.ndarray((3,D), 'float64') - X[0,:] = x1 - X[1,:] = x2 - X[2,:] = x3 - a = [] - a.append(x1) - a.append(x2) - a.append(x3) - a = numpy.array(a) - - # reference likelihood from Prince implementation - ll_ref = -182.8880743535197 - - # machine - m = PLDAMachine(mb) - ll = m.compute_log_likelihood(X) - assert abs(ll - ll_ref) < 1e-10 - - # log likelihood ratio - Y = numpy.ndarray((2,D), 'float64') - Y[0,:] = x1 - Y[1,:] = x2 - Z = numpy.ndarray((1,D), 'float64') - Z[0,:] = x3 - llX = m.compute_log_likelihood(X) - llY = m.compute_log_likelihood(Y) - llZ = m.compute_log_likelihood(Z) - # reference obtained by computing the likelihood of [x1,x2,x3], [x1,x2] - # and [x3] separately - llr_ref = -4.43695386675 - assert abs((llX - (llY + llZ)) - llr_ref) < 1e-10 diff --git a/bob/learn/em/test/test_plda_trainer.py b/bob/learn/em/test/test_plda_trainer.py deleted file mode 100644 index 9c55bd12d4733970e402515bee974c504ea7cc2f..0000000000000000000000000000000000000000 --- a/bob/learn/em/test/test_plda_trainer.py +++ /dev/null @@ -1,750 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <Laurent.El-Shafey@idiap.ch> -# Fri Oct 14 18:07:56 2011 +0200 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Tests PLDA trainer -""" - -import sys -import numpy -import numpy.linalg - -from bob.learn.em import PLDATrainer, PLDABase, PLDAMachine -import bob.learn.em -import nose.tools - -class PythonPLDATrainer(): - """A simplified (and slower) version of the PLDATrainer""" - - def __init__(self, convergence_threshold=0.001, max_iterations=10, - compute_likelihood=False, use_sum_second_order=True): - # Our state - self.m_convergence_threshold = convergence_threshold - self.m_max_iterations = max_iterations - self.m_compute_likelihood = compute_likelihood - self.m_dim_f = 0 - self.m_dim_g = 0 - self.m_B = numpy.ndarray(shape=(0,0), dtype=numpy.float64) - self.m_n_samples_per_id = numpy.ndarray(shape=(0,), dtype=numpy.float64) - self.m_z_first_order = [] - self.m_z_second_order = [] - self.m_sum_z_second_order = numpy.ndarray(shape=(0,0), dtype=numpy.float64) - - def reset(): - """Resets our internal state""" - self.m_convergence_threshold = 0.001 - self.m_max_iterations = 10 - self.m_compute_likelihood = False - self.m_dim_f = 0 - self.m_dim_g = 0 - self.m_n_samples_per_id = numpy.ndarray(shape=(0,), dtype=numpy.float64) - self.m_z_first_order = [] - self.m_z_second_order = [] - self.m_sum_z_second_order = numpy.ndarray(shape=(0,0), dtype=numpy.float64) - - def __check_training_data__(self, data): - if len(data) == 0: - raise RuntimeError("Training data set is empty") - n_features = data[0].shape[1] - for v in data: - if(v.shape[1] != n_features): - raise RuntimeError("Inconsistent feature dimensionality in training data set") - - def __init_members__(self, data): - n_features = data[0].shape[1] - self.m_z_first_order = [] - df_dg = self.m_dim_f+self.m_dim_g - self.m_sum_z_second_order = numpy.resize(self.m_sum_z_second_order, (df_dg, df_dg)) - self.m_n_samples_per_id = numpy.resize(self.m_n_samples_per_id, (len(data))) - self.m_B = numpy.resize(self.m_B, (n_features, df_dg)) - for i in range(len(data)): - ns_i = data[i].shape[0] - self.m_n_samples_per_id[i] = ns_i - self.m_z_first_order.append(numpy.ndarray(shape=(ns_i, df_dg), dtype=numpy.float64)) - self.m_z_second_order.append(numpy.ndarray(shape=(ns_i, df_dg, df_dg), dtype=numpy.float64)) - - def __init_mu__(self, machine, data): - mu = numpy.zeros(shape=machine.mu.shape[0], dtype=numpy.float64) - c = 0 - # Computes the mean of the data - for v in data: - for i in range(v.shape[0]): - mu += v[i,:] - c +=1 - mu /= c - machine.mu = mu - - def __init_f__(self, machine, data): - n_ids = len(data) - S = numpy.zeros(shape=(machine.shape[0], n_ids), dtype=numpy.float64) - Si_sum = numpy.zeros(shape=(machine.shape[0],), dtype=numpy.float64) - for i in range(n_ids): - Si = S[:,i] - data_i = data[i] - for j in range(data_i.shape[0]): - Si += data_i[j,:] - Si /= data_i.shape[0] - Si_sum += Si - Si_sum /= n_ids - - S = S - numpy.tile(Si_sum.reshape([machine.shape[0],1]), [1,n_ids]) - U, sigma, S_ = numpy.linalg.svd(S, full_matrices=False) - U_slice = U[:,0:self.m_dim_f] - sigma_slice = sigma[0:self.m_dim_f] - sigma_slice_sqrt = numpy.sqrt(sigma_slice) - machine.f = U_slice / sigma_slice_sqrt - - def __init_g__(self, machine, data): - n_samples = 0 - for v in data: - n_samples += v.shape[0] - S = numpy.zeros(shape=(machine.shape[0], n_samples), dtype=numpy.float64) - Si_sum = numpy.zeros(shape=(machine.shape[0],), dtype=numpy.float64) - cache = numpy.zeros(shape=(machine.shape[0],), dtype=numpy.float64) - c = 0 - for i in range(len(data)): - cache = 0 - data_i = data[i] - for j in range(data_i.shape[0]): - cache += data_i[j,:] - cache /= data_i.shape[0] - for j in range(data_i.shape[0]): - S[:,c] = data_i[j,:] - cache - Si_sum += S[:,c] - c += 1 - Si_sum /= n_samples - - S = S - numpy.tile(Si_sum.reshape([machine.shape[0],1]), [1,n_samples]) - U, sigma, S_ = numpy.linalg.svd(S, full_matrices=False) - U_slice = U[:,0:self.m_dim_g] - sigma_slice_sqrt = numpy.sqrt(sigma[0:self.m_dim_g]) - machine.g = U_slice / sigma_slice_sqrt - - def __init_sigma__(self, machine, data, factor = 1.): - """As a variance of the data""" - cache1 = numpy.zeros(shape=(machine.shape[0],), dtype=numpy.float64) - cache2 = numpy.zeros(shape=(machine.shape[0],), dtype=numpy.float64) - n_samples = 0 - for v in data: - for j in range(v.shape[0]): - cache1 += v[j,:] - n_samples += v.shape[0] - cache1 /= n_samples - for v in data: - for j in range(v.shape[0]): - cache2 += numpy.square(v[j,:] - cache1) - machine.sigma = factor * cache2 / (n_samples - 1) - - def __init_mu_f_g_sigma__(self, machine, data): - self.__init_mu__(machine, data) - self.__init_f__(machine, data) - self.__init_g__(machine, data) - self.__init_sigma__(machine, data) - - def initialize(self, machine, data): - self.__check_training_data__(data) - n_features = data[0].shape[1] - if(machine.shape[0] != n_features): - raise RuntimeError("Inconsistent feature dimensionality between the machine and the training data set") - self.m_dim_f = machine.shape[1] - self.m_dim_g = machine.shape[2] - self.__init_members__(data) - # Warning: Default initialization of mu, F, G, sigma using scatters - self.__init_mu_f_g_sigma__(machine, data) - # Make sure that the precomputation has been performed - machine.__precompute__() - - def __compute_sufficient_statistics_given_observations__(self, machine, observations): - """ - We compute the expected values of the latent variables given the observations - and parameters of the model. - - First order or the expected value of the latent variables.: - F = (I+A^{T}\Sigma'^{-1}A)^{-1} * A^{T}\Sigma^{-1} (\tilde{x}_{s}-\mu'). - Second order stats: - S = (I+A^{T}\Sigma'^{-1}A)^{-1} + (F*F^{T}). - """ - - # Get the number of observations - J_i = observations.shape[0] # An integer > 0 - dim_d = observations.shape[1] # A scalar - # Useful values - mu = machine.mu - F = machine.f - G = machine.g - sigma = machine.sigma - isigma = machine.__isigma__ - alpha = machine.__alpha__ - ft_beta = machine.__ft_beta__ - gamma = machine.get_add_gamma(J_i) - # Normalise the observations - normalised_observations = observations - numpy.tile(mu, [J_i,1]) # (dim_d, J_i) - - ### Expected value of the latent variables using the scalable solution - # Identity part first - sum_ft_beta_part = numpy.zeros(self.m_dim_f) # (dim_f) - for j in range(0, J_i): - current_observation = normalised_observations[j,:] # (dim_d) - sum_ft_beta_part = sum_ft_beta_part + numpy.dot(ft_beta, current_observation) # (dim_f) - h_i = numpy.dot(gamma, sum_ft_beta_part) # (dim_f) - # Reproject the identity part to work out the session parts - Fh_i = numpy.dot(F, h_i) # (dim_d) - z_first_order = numpy.zeros((J_i, self.m_dim_f+self.m_dim_g)) - for j in range(0, J_i): - current_observation = normalised_observations[j,:] # (dim_d) - w_ij = numpy.dot(alpha, G.transpose()) # (dim_g, dim_d) - w_ij = numpy.multiply(w_ij, isigma) # (dim_g, dim_d) - w_ij = numpy.dot(w_ij, (current_observation - Fh_i)) # (dim_g) - z_first_order[j,:] = numpy.hstack([h_i,w_ij]) # (dim_f+dim_g) - - ### Calculate the expected value of the squared of the latent variables - # The constant matrix we use has the following parts: [top_left, top_right; bottom_left, bottom_right] - # P = Inverse_I_plus_GTEG * G^T * Sigma^{-1} * F (dim_g, dim_f) - # top_left = gamma (dim_f, dim_f) - # bottom_left = top_right^T = P * gamma (dim_g, dim_f) - # bottom_right = Inverse_I_plus_GTEG - bottom_left * P^T (dim_g, dim_g) - top_left = gamma - P = numpy.dot(alpha, G.transpose()) - P = numpy.dot(numpy.dot(P,numpy.diag(isigma)), F) - bottom_left = -1 * numpy.dot(P, top_left) - top_right = bottom_left.transpose() - bottom_right = alpha -1 * numpy.dot(bottom_left, P.transpose()) - constant_matrix = numpy.bmat([[top_left,top_right],[bottom_left, bottom_right]]) - - # Now get the actual expected value - z_second_order = numpy.zeros((J_i, self.m_dim_f+self.m_dim_g, self.m_dim_f+self.m_dim_g)) - for j in range(0, J_i): - z_second_order[j,:,:] = constant_matrix + numpy.outer(z_first_order[j,:],z_first_order[j,:]) # (dim_f+dim_g,dim_f+dim_g) - - ### Return the first and second order statistics - return(z_first_order, z_second_order) - - def e_step(self, machine, data): - self.m_sum_z_second_order.fill(0.) - for i in range(len(data)): - ### Get the observations for this label and the number of observations for this label. - observations_for_h_i = data[i] - J_i = observations_for_h_i.shape[0] # An integer > 0 - - ### Gather the statistics for this identity and then separate them for each observation. - [z_first_order, z_second_order] = self.__compute_sufficient_statistics_given_observations__(machine, observations_for_h_i) - self.m_z_first_order[i] = z_first_order - self.m_z_second_order[i] = z_second_order - J_i = len(z_second_order) - for j in range(0, J_i): - self.m_sum_z_second_order += z_second_order[j] - - def __update_f_and_g__(self, machine, data): - ### Initialise the numerator and the denominator. - dim_d = machine.shape[0] - accumulated_B_numerator = numpy.zeros((dim_d,self.m_dim_f+self.m_dim_g)) - accumulated_B_denominator = numpy.linalg.inv(self.m_sum_z_second_order) - mu = machine.mu - - ### Go through and process on a per subjectid basis - for i in range(len(data)): - # Normalise the observations - J_i = data[i].shape[0] - normalised_observations = data[i] - numpy.tile(mu, [J_i,1]) # (J_i, dim_d) - - ### Gather the statistics for this label - z_first_order_i = self.m_z_first_order[i] # List of (dim_f+dim_g) vectors - - ### Accumulate for the B matrix for this identity (current_label). - for j in range(0, J_i): - current_observation_for_h_i = normalised_observations[j,:] # (dim_d) - accumulated_B_numerator = accumulated_B_numerator + numpy.outer(current_observation_for_h_i, z_first_order_i[j,:]) # (dim_d, dim_f+dim_g); - - ### Update the B matrix which we can then use this to update the F and G matrices. - B = numpy.dot(accumulated_B_numerator,accumulated_B_denominator) - machine.f = B[:,0:self.m_dim_f].copy() - machine.g = B[:,self.m_dim_f:self.m_dim_f+self.m_dim_g].copy() - - def __update_sigma__(self, machine, data): - ### Initialise the accumulated Sigma - dim_d = machine.shape[0] - mu = machine.mu - accumulated_sigma = numpy.zeros(dim_d) # An array (dim_d) - number_of_observations = 0 - B = numpy.hstack([machine.f, machine.g]) - - ### Go through and process on a per subjectid basis (based on the labels we were given. - for i in range(len(data)): - # Normalise the observations - J_i = data[i].shape[0] - normalised_observations = data[i] - numpy.tile(mu, [J_i,1]) # (J_i, dim_d) - - ### Gather the statistics for this identity and then separate them for each - ### observation. - z_first_order_i = self.m_z_first_order[i] # List of (dim_f+dim_g) vectors - - ### Accumulate for the sigma matrix, which will be diagonalised - for j in range(0, J_i): - current_observation_for_h_i = normalised_observations[j,:] # (dim_d) - left = current_observation_for_h_i * current_observation_for_h_i # (dim_d) - projected_direction = numpy.dot(B, z_first_order_i[j,:]) # (dim_d) - right = projected_direction * current_observation_for_h_i # (dim_d) - accumulated_sigma = accumulated_sigma + (left - right) # (dim_d) - number_of_observations = number_of_observations + 1 - - ### Normalise by the number of observations (1/IJ) - machine.sigma = accumulated_sigma / number_of_observations; - - def m_step(self, machine, data): - self.__update_f_and_g__(machine, data) - self.__update_sigma__(machine, data) - machine.__precompute__() - - def finalize(self, machine, data): - machine.__precompute_log_like__() - - def train(self, machine, data): - self.initialize(machine, data) - average_output_previous = -sys.maxsize - average_output = -sys.maxsize - self.e_step(machine, data) - - i = 0 - while True: - average_output_previous = average_output - self.m_step(machine, data) - self.e_step(machine, data) - if(self.m_max_iterations > 0 and i+1 >= self.m_max_iterations): - break - i += 1 - - -def notest_plda_EM_vs_Python(): - - # Data used for performing the tests - # Features and subspaces dimensionality - D = 7 - nf = 2 - ng = 3 - - # first identity (4 samples) - a = numpy.array([ - [1,2,3,4,5,6,7], - [7,8,3,3,1,8,2], - [3,2,1,4,5,1,7], - [9,0,3,2,1,4,6], - ], dtype='float64') - - # second identity (3 samples) - b = numpy.array([ - [5,6,3,4,2,0,2], - [1,7,8,9,4,4,8], - [8,7,2,5,1,1,1], - ], dtype='float64') - - # list of arrays (training data) - l = [a,b] - - # initial values for F, G and sigma - G_init=numpy.array([-1.1424, -0.5044, -0.1917, - -0.6249, 0.1021, -0.8658, - -1.1687, 1.1963, 0.1807, - 0.3926, 0.1203, 1.2665, - 1.3018, -1.0368, -0.2512, - -0.5936, -0.8571, -0.2046, - 0.4364, -0.1699, -2.2015]).reshape(D,ng) - - # F <-> PCA on G - F_init=numpy.array([-0.054222647972093, -0.000000000783146, - 0.596449127693018, 0.000000006265167, - 0.298224563846509, 0.000000003132583, - 0.447336845769764, 0.000000009397750, - -0.108445295944185, -0.000000001566292, - -0.501559493741856, -0.000000006265167, - -0.298224563846509, -0.000000003132583]).reshape(D,nf) - sigma_init = 0.01 * numpy.ones(D, 'float64') - - # Runs the PLDA trainer EM-steps (2 steps) - # Defines base trainer and machine - t = PLDATrainer() - t_py = PythonPLDATrainer(max_iterations=10) - m = PLDABase(D,nf,ng) - m_py = PLDABase(D,nf,ng) - - # Sets the same initialization methods - t.init_f_method = 'BETWEEN_SCATTER' - t.init_g_method = 'WITHIN_SCATTER' - t.init_sigma_method = 'VARIANCE_DATA' - - #t.train(m, l) - bob.learn.em.train(t, m, l, max_iterations=10) - t_py.train(m_py, l) - - assert numpy.allclose(m.mu, m_py.mu) - assert numpy.allclose(m.f, m_py.f) - assert numpy.allclose(m.g, m_py.g) - assert numpy.allclose(m.sigma, m_py.sigma) - - -def test_plda_EM_vs_Prince(): - # Data used for performing the tests - # Features and subspaces dimensionality - dim_d = 7 - dim_f = 2 - dim_g = 3 - - # first identity (4 samples) - a = numpy.array([ - [1,2,3,4,5,6,7], - [7,8,3,3,1,8,2], - [3,2,1,4,5,1,7], - [9,0,3,2,1,4,6], - ], dtype='float64') - - # second identity (3 samples) - b = numpy.array([ - [5,6,3,4,2,0,2], - [1,7,8,9,4,4,8], - [8,7,2,5,1,1,1], - ], dtype='float64') - - # list of arrays (training data) - l = [a,b] - - # initial values for F, G and sigma - G_init=numpy.array([-1.1424, -0.5044, -0.1917, - -0.6249, 0.1021, -0.8658, - -1.1687, 1.1963, 0.1807, - 0.3926, 0.1203, 1.2665, - 1.3018, -1.0368, -0.2512, - -0.5936, -0.8571, -0.2046, - 0.4364, -0.1699, -2.2015]).reshape(dim_d,dim_g) - - # F <-> PCA on G - F_init=numpy.array([-0.054222647972093, -0.000000000783146, - 0.596449127693018, 0.000000006265167, - 0.298224563846509, 0.000000003132583, - 0.447336845769764, 0.000000009397750, - -0.108445295944185, -0.000000001566292, - -0.501559493741856, -0.000000006265167, - -0.298224563846509, -0.000000003132583]).reshape(dim_d,dim_f) - sigma_init = 0.01 * numpy.ones(dim_d, 'float64') - - # Defines reference results based on Princes'matlab implementation - # After 1 iteration - z_first_order_a_1 = numpy.array( - [-2.624115900658397, -0.000000034277848, 1.554823055585319, 0.627476234024656, -0.264705934182394, - -2.624115900658397, -0.000000034277848, -2.703482671599357, -1.533283607433197, 0.553725774828231, - -2.624115900658397, -0.000000034277848, 2.311647528461115, 1.266362142140170, -0.317378177105131, - -2.624115900658397, -0.000000034277848, -1.163402640008200, -0.372604542926019, 0.025152800097991 - ]).reshape(4, dim_f+dim_g) - z_first_order_b_1 = numpy.array( - [ 3.494168818797438, 0.000000045643026, 0.111295550530958, -0.029241422535725, 0.257045446451067, - 3.494168818797438, 0.000000045643026, 1.102110715965762, 1.481232954001794, -0.970661225144399, - 3.494168818797438, 0.000000045643026, -1.212854031699468, -1.435946529317718, 0.717884143973377 - ]).reshape(3, dim_f+dim_g) - - z_second_order_sum_1 = numpy.array( - [64.203518285366087, 0.000000747228248, 0.002703277337642, 0.078542842475345, 0.020894328259862, - 0.000000747228248, 6.999999999999980, -0.000000003955962, 0.000000002017232, -0.000000003741593, - 0.002703277337642, -0.000000003955962, 19.136889380923918, 11.860493771107487, -4.584339465366988, - 0.078542842475345, 0.000000002017232, 11.860493771107487, 8.771502339750128, -3.905706024997424, - 0.020894328259862, -0.000000003741593, -4.584339465366988, -3.905706024997424, 2.011924970338584 - ]).reshape(dim_f+dim_g, dim_f+dim_g) - - sigma_1 = numpy.array( - [2.193659969999207, 3.748361365521041, 0.237835235737085, - 0.558546035892629, 0.209272700958400, 1.717782807724451, - 0.248414618308223]) - - F_1 = numpy.array( - [-0.059083416465692, 0.000000000751007, - 0.600133217253169, 0.000000006957266, - 0.302789123922871, 0.000000000218947, - 0.454540641429714, 0.000000003342540, - -0.106608957780613, -0.000000001641389, - -0.494267694269430, -0.000000011059552, - -0.295956102084270, -0.000000006718366]).reshape(dim_d,dim_f) - - G_1 = numpy.array( - [-1.836166150865047, 2.491475145758734, 5.095958946372235, - -0.608732205531767, -0.618128420353493, -1.085423135463635, - -0.697390472635929, -1.047900122276840, -6.080211153116984, - 0.769509301515319, -2.763610156675313, -5.972172587527176, - 1.332474692714491, -1.368103875407414, -2.096382536513033, - 0.304135903830416, -5.168096082564016, -9.604769461465978, - 0.597445549865284, -1.347101803379971, -5.900246013340080]).reshape(dim_d,dim_g) - - # After 2 iterations - z_first_order_a_2 = numpy.array( - [-2.144344161196005, -0.000000027851878, 1.217776189037369, 0.232492571855061, -0.212892893868819, - -2.144344161196005, -0.000000027851878, -2.382647766948079, -1.759951013670071, 0.587213207926731, - -2.144344161196005, -0.000000027851878, 2.143294830538722, 0.909307594408923, -0.183752098508072, - -2.144344161196005, -0.000000027851878, -0.662558006326892, 0.717992497547010, -0.202897892977004 - ]).reshape(4, dim_f+dim_g) - z_first_order_b_2 = numpy.array( - [ 2.695117129662246, 0.000000035005543, -0.156173294945791, -0.123083763746364, 0.271123341933619, - 2.695117129662246, 0.000000035005543, 0.690321563509753, 0.944473716646212, -0.850835940962492, - 2.695117129662246, 0.000000035005543, -0.930970138998433, -0.949736472690315, 0.594216348861889 - ]).reshape(3, dim_f+dim_g) - - z_second_order_sum_2 = numpy.array( - [41.602421167226410, 0.000000449434708, -1.513391506933811, -0.477818674270533, 0.059260102368316, - 0.000000449434708, 7.000000000000005, -0.000000023255959, -0.000000005157439, -0.000000003230262, - -1.513391506933810, -0.000000023255959, 14.399631061987494, 8.068678077509025, -3.227586434905497, - -0.477818674270533, -0.000000005157439, 8.068678077509025, 7.263248678863863, -3.060665688064639, - 0.059260102368316, -0.000000003230262, -3.227586434905497, -3.060665688064639, 1.705174220723198 - ]).reshape(dim_f+dim_g, dim_f+dim_g) - - sigma_2 = numpy.array( - [1.120493935052524, 1.777598857891599, 0.197579528599150, - 0.407657093211478, 0.166216300651473, 1.044336960403809, - 0.287856936559308]) - - F_2 = numpy.array( - [-0.111956311978966, 0.000000000781025, - 0.702502767389263, 0.000000007683917, - 0.337823622542517, 0.000000000637302, - 0.551363737526339, 0.000000004854293, - -0.096561040511417, -0.000000001716011, - -0.661587484803602, -0.000000012394362, - -0.346593051621620, -0.000000007134046]).reshape(dim_d,dim_f) - - G_2 = numpy.array( - [-2.266404374274820, 4.089199685832099, 7.023039382876370, - 0.094887459097613, -3.226829318470136, -3.452279917194724, - -0.498398131733141, -1.651712333649899, -6.548008210704172, - 0.574932298590327, -2.198978667003715, -5.131253543126156, - 1.415857426810629, -1.627795701160212, -2.509013676007012, - -0.543552834305580, -3.215063993186718, -7.006305082499653, - 0.562108137758111, -0.785296641855087, -5.318335345720314]).reshape(dim_d,dim_g) - - # Runs the PLDA trainer EM-steps (2 steps) - - # Defines base trainer and machine - t = PLDATrainer() - t0 = PLDATrainer(t) - m = PLDABase(dim_d,dim_f,dim_g) - t.initialize(m,l) - m.sigma = sigma_init - m.g = G_init - m.f = F_init - - # Defines base trainer and machine (for Python implementation - t_py = PythonPLDATrainer() - m_py = PLDABase(dim_d,dim_f,dim_g) - t_py.initialize(m_py,l) - m_py.sigma = sigma_init - m_py.g = G_init - m_py.f = F_init - - # E-step 1 - t.e_step(m,l) - t_py.e_step(m_py,l) - # Compares statistics to Prince matlab reference - assert numpy.allclose(t.z_first_order[0], z_first_order_a_1, 1e-10) - assert numpy.allclose(t.z_first_order[1], z_first_order_b_1, 1e-10) - assert numpy.allclose(t.z_second_order_sum, z_second_order_sum_1, 1e-10) - # Compares statistics against the ones of the python implementation - assert numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10) - assert numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10) - assert numpy.allclose(t.z_second_order_sum, t_py.m_sum_z_second_order, 1e-10) - - # M-step 1 - t.m_step(m,l) - t_py.m_step(m_py,l) - # Compares F, G and sigma to Prince matlab reference - assert numpy.allclose(m.f, F_1, 1e-10) - assert numpy.allclose(m.g, G_1, 1e-10) - assert numpy.allclose(m.sigma, sigma_1, 1e-10) - # Compares F, G and sigma to the ones of the python implementation - assert numpy.allclose(m.f, m_py.f, 1e-10) - assert numpy.allclose(m.g, m_py.g, 1e-10) - assert numpy.allclose(m.sigma, m_py.sigma, 1e-10) - - # E-step 2 - t.e_step(m,l) - t_py.e_step(m_py,l) - # Compares statistics to Prince matlab reference - assert numpy.allclose(t.z_first_order[0], z_first_order_a_2, 1e-10) - assert numpy.allclose(t.z_first_order[1], z_first_order_b_2, 1e-10) - assert numpy.allclose(t.z_second_order_sum, z_second_order_sum_2, 1e-10) - # Compares statistics against the ones of the python implementation - assert numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10) - assert numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10) - assert numpy.allclose(t.z_second_order_sum, t_py.m_sum_z_second_order, 1e-10) - - # M-step 2 - t.m_step(m,l) - t_py.m_step(m_py,l) - # Compares F, G and sigma to Prince matlab reference - assert numpy.allclose(m.f, F_2, 1e-10) - assert numpy.allclose(m.g, G_2, 1e-10) - assert numpy.allclose(m.sigma, sigma_2, 1e-10) - # Compares F, G and sigma to the ones of the python implementation - assert numpy.allclose(m.f, m_py.f, 1e-10) - assert numpy.allclose(m.g, m_py.g, 1e-10) - assert numpy.allclose(m.sigma, m_py.sigma, 1e-10) - - - # Test the second order statistics computation - # Calls the initialization methods and resets randomly initialized values - # to new reference ones (to make the tests deterministic) - t.use_sum_second_order = False - t.initialize(m,l) - m.sigma = sigma_init - m.g = G_init - m.f = F_init - t_py.initialize(m_py,l) - m_py.sigma = sigma_init - m_py.g = G_init - m_py.f = F_init - - # E-step 1 - t.e_step(m,l) - t_py.e_step(m_py,l) - # Compares statistics to Prince matlab reference - assert numpy.allclose(t.z_first_order[0], z_first_order_a_1, 1e-10) - assert numpy.allclose(t.z_first_order[1], z_first_order_b_1, 1e-10) - # Compares statistics against the ones of the python implementation - assert numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10) - assert numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10) - assert numpy.allclose(t.z_second_order[0], t_py.m_z_second_order[0], 1e-10) - assert numpy.allclose(t.z_second_order[1], t_py.m_z_second_order[1], 1e-10) - assert numpy.allclose(t.z_second_order_sum, t_py.m_sum_z_second_order, 1e-10) - - # M-step 1 - t.m_step(m,l) - t_py.m_step(m_py,l) - # Compares F, G and sigma to the ones of the python implementation - assert numpy.allclose(m.f, m_py.f, 1e-10) - assert numpy.allclose(m.g, m_py.g, 1e-10) - assert numpy.allclose(m.sigma, m_py.sigma, 1e-10) - - # E-step 2 - t.e_step(m,l) - t_py.e_step(m_py,l) - # Compares statistics to Prince matlab reference - assert numpy.allclose(t.z_first_order[0], z_first_order_a_2, 1e-10) - assert numpy.allclose(t.z_first_order[1], z_first_order_b_2, 1e-10) - # Compares statistics against the ones of the python implementation - assert numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10) - assert numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10) - assert numpy.allclose(t.z_second_order[0], t_py.m_z_second_order[0], 1e-10) - assert numpy.allclose(t.z_second_order[1], t_py.m_z_second_order[1], 1e-10) - assert numpy.allclose(t.z_second_order_sum, t_py.m_sum_z_second_order, 1e-10) - - # M-step 2 - t.m_step(m,l) - t_py.m_step(m_py,l) - # Compares F, G and sigma to the ones of the python implementation - assert numpy.allclose(m.f, m_py.f, 1e-10) - assert numpy.allclose(m.g, m_py.g, 1e-10) - assert numpy.allclose(m.sigma, m_py.sigma, 1e-10) - - #testing exceptions - nose.tools.assert_raises(RuntimeError, t.initialize, m, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.e_step, m, [1,2,2]) - nose.tools.assert_raises(RuntimeError, t.m_step, m, [1,2,2]) - - - -def test_plda_enrollment(): - # Data used for performing the tests - # Features and subspaces dimensionality - dim_d = 7 - dim_f = 2 - dim_g = 3 - - # initial values for F, G and sigma - G_init=numpy.array([-1.1424, -0.5044, -0.1917, - -0.6249, 0.1021, -0.8658, - -1.1687, 1.1963, 0.1807, - 0.3926, 0.1203, 1.2665, - 1.3018, -1.0368, -0.2512, - -0.5936, -0.8571, -0.2046, - 0.4364, -0.1699, -2.2015]).reshape(dim_d,dim_g) - # F <-> PCA on G - F_init=numpy.array([-0.054222647972093, -0.000000000783146, - 0.596449127693018, 0.000000006265167, - 0.298224563846509, 0.000000003132583, - 0.447336845769764, 0.000000009397750, - -0.108445295944185, -0.000000001566292, - -0.501559493741856, -0.000000006265167, - -0.298224563846509, -0.000000003132583]).reshape(dim_d,dim_f) - sigma_init = 0.01 * numpy.ones((dim_d,), 'float64') - mean_zero = numpy.zeros((dim_d,), 'float64') - - # base machine - mb = PLDABase(dim_d,dim_f,dim_g) - mb.sigma = sigma_init - mb.g = G_init - mb.f = F_init - mb.mu = mean_zero - - # Data for likelihood computation - x1 = numpy.array([0.8032, 0.3503, 0.4587, 0.9511, 0.1330, 0.0703, 0.7061]) - x2 = numpy.array([0.9317, 0.1089, 0.6517, 0.1461, 0.6940, 0.6256, 0.0437]) - x3 = numpy.array([0.7979, 0.9862, 0.4367, 0.3447, 0.0488, 0.2252, 0.5810]) - a_enroll = [] - a_enroll.append(x1) - a_enroll.append(x2) - a_enroll = numpy.array(a_enroll) - - # reference likelihood from Prince implementation - ll_ref = -182.8880743535197 - - # Computes the likelihood using x1 and x2 as enrollment samples - # and x3 as a probe sample - m = PLDAMachine(mb) - t = PLDATrainer() - t.enroll(m, a_enroll) - ll = m.compute_log_likelihood(x3) - - assert abs(ll - ll_ref) < 1e-10 - - # reference obtained by computing the likelihood of [x1,x2,x3], [x1,x2] - # and [x3] separately - llr_ref = -4.43695386675 - llr = m(x3) - assert abs(llr - llr_ref) < 1e-10 - # - llr_separate = m.compute_log_likelihood(numpy.array([x1,x2,x3]), False) - \ - (m.compute_log_likelihood(numpy.array([x1,x2]), False) + m.compute_log_likelihood(numpy.array([x3]), False)) - assert abs(llr - llr_separate) < 1e-10 - - - -def test_plda_comparisons(): - - t1 = PLDATrainer() - t2 = PLDATrainer() - - #t2.rng = t1.rng - - assert t1 == t2 - assert (t1 != t2 ) is False - assert t1.is_similar_to(t2) - - training_set = [numpy.array([[1,2,3,4]], numpy.float64), numpy.array([[3,4,3,4]], numpy.float64)] - m = PLDABase(4,1,1,1e-8) - rng = bob.core.random.mt19937(37) - t1.initialize(m, training_set,rng) - t1.e_step(m, training_set) - t1.m_step(m, training_set) - assert (t1 == t2 ) is False - assert t1 != t2 - assert (t1.is_similar_to(t2) ) is False - rng = bob.core.random.mt19937(37) - t2.initialize(m, training_set, rng) - t2.e_step(m, training_set) - t2.m_step(m, training_set) - assert t1 == t2 - assert (t1 != t2 ) is False - assert t1.is_similar_to(t2) - - rng = bob.core.random.mt19937(77) - t2.initialize(m, training_set, rng) - t2.e_step(m, training_set) - t2.m_step(m, training_set) - assert (t1 == t2 ) is False - assert t1 != t2 - assert (t1.is_similar_to(t2) ) is False diff --git a/bob/learn/em/train.py b/bob/learn/em/train.py deleted file mode 100644 index e0e500ba8a2dacdc4f76ee0f5bcacbf9848cb9cd..0000000000000000000000000000000000000000 --- a/bob/learn/em/train.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> -# Fri Feb 13 13:18:10 2015 +0200 -# -# Copyright (C) 2011-2015 Idiap Research Institute, Martigny, Switzerland -import numpy -from ._library import * -import logging -from multiprocessing.pool import ThreadPool - -logger = logging.getLogger(__name__) - - -def _set_average(trainer, trainers, machine, data, trainer_type): - """_set_average(trainer, data) -> None - - This function computes the average of the given data and sets it to the given machine. - - This function works for different types of trainers, and can be used to parallelize the training. - For some trainers, the data is returned instead of set in the trainer. - - **Parameters:** - - trainer : one of :py:class:`KMeansTrainer`, :py:class:`MAP_GMMTrainer`, :py:class:`ML_GMMTrainer`, :py:class:`ISVTrainer`, :py:class:`IVectorTrainer`, :py:class:`PLDATrainer`, :py:class:`EMPCATrainer` - The trainer to set the data to. - - trainers : [ trainer ] - The list of trainer objects that were used in the parallel training process. - All trainers must be of the same class as the ``trainer``. - - data : [ object ] - The list of data objects that should be set to the trainer. - Usually this list is generated by parallelizing the e-step of the ``trainer``. - """ - - if trainer_type == "KMeansTrainer": - # K-Means statistics - trainer.reset_accumulators(machine) - for t in trainers: - trainer.zeroeth_order_statistics = trainer.zeroeth_order_statistics + t.zeroeth_order_statistics - trainer.first_order_statistics = trainer.first_order_statistics + t.first_order_statistics - trainer.average_min_distance = trainer.average_min_distance + t.average_min_distance - - trainer.average_min_distance /= data.shape[0] - - elif trainer_type in ("ML_GMMTrainer", "MAP_GMMTrainer"): - # GMM statistics - trainer.gmm_statistics = trainers[0].gmm_statistics - for t in trainers[1:]: - trainer.gmm_statistics += t.gmm_statistics - - elif trainer_type == "IVectorTrainer": - # GMM statistics - trainer.reset_accumulators(machine) - trainer.acc_fnormij_wij = trainers[0].acc_fnormij_wij - trainer.acc_nij_wij2 = trainers[0].acc_nij_wij2 - trainer.acc_nij = trainers[0].acc_nij - trainer.acc_snormij = trainers[0].acc_snormij - - for t in trainers[1:]: - trainer.acc_fnormij_wij = trainer.acc_fnormij_wij + t.acc_fnormij_wij - trainer.acc_nij_wij2 = trainer.acc_nij_wij2 + t.acc_nij_wij2 - trainer.acc_nij = trainer.acc_nij + t.acc_nij - trainer.acc_snormij = trainer.acc_snormij + t.acc_snormij - - - else: - raise NotImplementedError("Implement Me!") - - -def _parallel_e_step(args): - """This function applies the e_step of the given trainer (first argument) on the given data (second argument). - It is called by each parallel process. - """ - trainer, machine, data = args - trainer.e_step(machine, data) - - -def train(trainer, machine, data, max_iterations=50, convergence_threshold=None, initialize=True, rng=None, check_inputs=True, pool=None, trainer_type=None): - - """ - Trains a machine given a trainer and the proper data - - **Parameters**: - trainer : one of :py:class:`KMeansTrainer`, :py:class:`MAP_GMMTrainer`, :py:class:`ML_GMMTrainer`, :py:class:`ISVTrainer`, :py:class:`IVectorTrainer`, :py:class:`PLDATrainer`, :py:class:`EMPCATrainer` - A trainer mechanism - machine : one of :py:class:`KMeansMachine`, :py:class:`GMMMachine`, :py:class:`ISVBase`, :py:class:`IVectorMachine`, :py:class:`PLDAMachine`, :py:class:`bob.learn.linear.Machine` - A container machine - data : array_like <float, 2D> - The data to be trained - max_iterations : int - The maximum number of iterations to train a machine - convergence_threshold : float - The convergence threshold to train a machine. If None, the training procedure will stop with the iterations criteria - initialize : bool - If True, runs the initialization procedure - rng : :py:class:`bob.core.random.mt19937` - The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loop - check_inputs: - Shallow checks in the inputs. Check for inf and NaN - pool : ``int`` or ``multiprocessing.ThreadPool`` or ``None`` - If given, the provided process pool will be used to parallelize the M-step of the - EM algorithm. You should provide a ThreadPool not a multi process Pool. If pool is - an integer, it will be used to create a ThreadPool with that many processes. - trainer_type : ``str`` or ``None`` - This is used for the parallel e_step method to see how several processes' data can - be merged into one trainer before the m_step. By default - ``trainer.__class__.__name__`` is used. This is useful if you have custom trainers - and want to use this function. - """ - if check_inputs and isinstance(data, numpy.ndarray): - sum_data = numpy.sum(data) - - if numpy.isinf(sum_data): - raise ValueError("Please, check your inputs; numpy.inf detected in `data` ") - - if numpy.isnan(sum_data): - raise ValueError("Please, check your inputs; numpy.nan detected in `data` ") - - if isinstance(pool, int): - pool = ThreadPool(pool) - - if trainer_type is None: - trainer_type = trainer.__class__.__name__ - - def _e_step(trainer, machine, data): - - # performs the e-step, possibly in parallel - if pool is None: - # use only one core - trainer.e_step(machine, data) - else: - - # use the given process pool - n_processes = pool._processes - - # Mapping references of the data - split_data = [] - offset = 0 - - step = int(len(data) // n_processes) - - for p in range(n_processes): - if p == n_processes - 1: - # take all the data in the last chunk - split_data.append(data[offset:]) - else: - split_data.append(data[offset: offset + step]) - - offset += step - - # create trainers for each process - trainers = [trainer.__class__(trainer) for p in range(n_processes)] - # no need to copy the machines - machines = [machine.__class__(machine) for p in range(n_processes)] - # call the parallel processes - pool.map(_parallel_e_step, zip(trainers, machines, split_data)) - # update the trainer with the data of the other trainers - _set_average(trainer, trainers, machine, data, trainer_type) - - # Initialization - if initialize: - if rng is not None: - trainer.initialize(machine, data, rng) - else: - trainer.initialize(machine, data) - - _e_step(trainer, machine, data) - average_output = 0 - average_output_previous = 0 - - if hasattr(trainer,"compute_likelihood"): - average_output = trainer.compute_likelihood(machine) - - for i in range(max_iterations): - logger.info("Iteration = %d/%d", i, max_iterations) - average_output_previous = average_output - trainer.m_step(machine, data) - _e_step(trainer, machine,data) - - if hasattr(trainer,"compute_likelihood"): - average_output = trainer.compute_likelihood(machine) - - if isinstance(machine, KMeansMachine): - logger.info("average euclidean distance = %f", average_output) - else: - logger.info("log likelihood = %f", average_output) - - convergence_value = abs((average_output_previous - average_output)/average_output_previous) - logger.info("convergence value = %f",convergence_value) - - #Terminates if converged (and likelihood computation is set) - if convergence_threshold is not None and convergence_value <= convergence_threshold: - break - if hasattr(trainer,"finalize"): - trainer.finalize(machine, data) - - -def train_jfa(trainer, jfa_base, data, max_iterations=10, initialize=True, rng=None): - """ - Trains a :py:class:`bob.learn.em.JFABase` given a :py:class:`bob.learn.em.JFATrainer` and the proper data - - **Parameters**: - trainer : :py:class:`bob.learn.em.JFATrainer` - A JFA trainer mechanism - jfa_base : :py:class:`bob.learn.em.JFABase` - A container machine - data : [[:py:class:`bob.learn.em.GMMStats`]] - The data to be trained - max_iterations : int - The maximum number of iterations to train a machine - initialize : bool - If True, runs the initialization procedure - rng : :py:class:`bob.core.random.mt19937` - The Mersenne Twister mt19937 random generator used for the initialization of subspaces/arrays before the EM loops - """ - - if initialize: - if rng is not None: - trainer.initialize(jfa_base, data, rng) - else: - trainer.initialize(jfa_base, data) - - # V Subspace - logger.info("V subspace estimation...") - for i in range(max_iterations): - logger.info("Iteration = %d/%d", i, max_iterations) - trainer.e_step_v(jfa_base, data) - trainer.m_step_v(jfa_base, data) - trainer.finalize_v(jfa_base, data) - - # U subspace - logger.info("U subspace estimation...") - for i in range(max_iterations): - logger.info("Iteration = %d/%d", i, max_iterations) - trainer.e_step_u(jfa_base, data) - trainer.m_step_u(jfa_base, data) - trainer.finalize_u(jfa_base, data) - - # D subspace - logger.info("D subspace estimation...") - for i in range(max_iterations): - logger.info("Iteration = %d/%d", i, max_iterations) - trainer.e_step_d(jfa_base, data) - trainer.m_step_d(jfa_base, data) - trainer.finalize_d(jfa_base, data) diff --git a/bob/learn/em/version.cpp b/bob/learn/em/version.cpp deleted file mode 100644 index 866894fb97b991c1124d6f0766684056d719a1ba..0000000000000000000000000000000000000000 --- a/bob/learn/em/version.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> - * @date Mon Apr 14 20:43:48 CEST 2014 - * - * @brief Binds configuration information available from bob - */ - -#include <bob.learn.em/config.h> - -#define BOB_IMPORT_VERSION -#include <bob.blitz/config.h> -#include <bob.blitz/cleanup.h> -#include <bob.core/config.h> -#include <bob.io.base/config.h> -#include <bob.sp/config.h> -#include <bob.math/config.h> -#include <bob.learn.activation/config.h> -#include <bob.learn.linear/config.h> - - -static PyObject* build_version_dictionary() { - - PyObject* retval = PyDict_New(); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - if (!dict_steal(retval, "Blitz++", blitz_version())) return 0; - if (!dict_steal(retval, "Boost", boost_version())) return 0; - if (!dict_steal(retval, "Compiler", compiler_version())) return 0; - if (!dict_steal(retval, "Python", python_version())) return 0; - if (!dict_steal(retval, "NumPy", numpy_version())) return 0; - if (!dict_steal(retval, "HDF5", hdf5_version())) return 0; - if (!dict_steal(retval, "bob.blitz", bob_blitz_version())) return 0; - if (!dict_steal(retval, "bob.core", bob_core_version())) return 0; - if (!dict_steal(retval, "bob.io.base", bob_io_base_version())) return 0; - if (!dict_steal(retval, "bob.sp", bob_sp_version())) return 0; - if (!dict_steal(retval, "bob.math", bob_math_version())) return 0; - if (!dict_steal(retval, "bob.learn.activation", bob_learn_activation_version())) return 0; - if (!dict_steal(retval, "bob.learn.linear", bob_learn_linear_version())) return 0; - - return Py_BuildValue("O", retval); -} - -static PyMethodDef module_methods[] = { - {0} /* Sentinel */ -}; - -PyDoc_STRVAR(module_docstr, -"Information about software used to compile the C++ Bob API" -); - -#if PY_VERSION_HEX >= 0x03000000 -static PyModuleDef module_definition = { - PyModuleDef_HEAD_INIT, - BOB_EXT_MODULE_NAME, - module_docstr, - -1, - module_methods, - 0, 0, 0, 0 -}; -#endif - -static PyObject* create_module (void) { - -# if PY_VERSION_HEX >= 0x03000000 - PyObject* module = PyModule_Create(&module_definition); - auto module_ = make_xsafe(module); - const char* ret = "O"; -# else - PyObject* module = Py_InitModule3(BOB_EXT_MODULE_NAME, module_methods, module_docstr); - const char* ret = "N"; -# endif - if (!module) return 0; - - /* register version numbers and constants */ - if (PyModule_AddIntConstant(module, "api", BOB_LEARN_EM_API_VERSION) < 0) return 0; - if (PyModule_AddStringConstant(module, "module", BOB_EXT_MODULE_VERSION) < 0) return 0; - - PyObject* externals = build_version_dictionary(); - if (!externals) return 0; - if (PyModule_AddObject(module, "externals", externals) < 0) return 0; - - return Py_BuildValue(ret, module); -} - -PyMODINIT_FUNC BOB_EXT_ENTRY_NAME (void) { -# if PY_VERSION_HEX >= 0x03000000 - return -# endif - create_module(); -} diff --git a/conda/meta.yaml b/conda/meta.yaml index 4e333eadf389a171c3b8add91f20937ce73e73d6..37bcc615cf5ef6b85465592622c7152a62b01dbc 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -15,39 +15,40 @@ build: - "{{ PYTHON }} setup.py sdist --formats=zip" {% endif %} - "{{ PYTHON }} -m pip install . -vv" + # installs the documentation source, readme to share/doc so it is available + # during test time + - install -d "${PREFIX}/share/doc/{{ name }}" + - cp -R README.rst requirements.txt doc "${PREFIX}/share/doc/{{ name }}/" requirements: - build: - - {{ compiler('c') }} - - {{ compiler('cxx') }} - - pkg-config {{ pkg_config }} - - cmake {{ cmake }} - - make {{ make }} host: - python {{ python }} - setuptools {{ setuptools }} - pip {{ pip }} - bob.extension - bob.blitz - - bob.core >2.0.5 - bob.io.base - bob.sp - - bob.math - bob.learn.activation - bob.learn.linear - - libblitz {{ libblitz }} - - boost {{ boost }} - numpy {{ numpy }} + - dask {{ dask }} + - dask-ml {{ dask_ml }} + - h5py {{ h5py }} run: - python - setuptools - - boost - {{ pin_compatible('numpy') }} + - {{ pin_compatible('dask') }} + - {{ pin_compatible('dask-ml') }} + - {{ pin_compatible('h5py') }} test: imports: - {{ name }} commands: + # runs tests for package only, report only what is in the package + # creates html and xml reports and place them in specific directories - nosetests --with-coverage --cover-package={{ name }} -sv {{ name }} - sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx - sphinx-build -aEb doctest {{ project_dir }}/doc sphinx diff --git a/develop.cfg b/develop.cfg index 40bd10aa0e2846152a12010f8c5ee123d60b95f9..8058224ef600e2139e69947bd2f99842e5fb1e75 100644 --- a/develop.cfg +++ b/develop.cfg @@ -10,11 +10,8 @@ extensions = bob.buildout auto-checkout = * develop = src/bob.extension - src/bob.blitz - src/bob.core src/bob.io.base src/bob.sp - src/bob.math src/bob.learn.activation src/bob.learn.linear . @@ -26,11 +23,8 @@ newest = false [sources] bob.extension = git https://gitlab.idiap.ch/bob/bob.extension -bob.blitz = git https://gitlab.idiap.ch/bob/bob.blitz -bob.core = git https://gitlab.idiap.ch/bob/bob.core bob.io.base = git https://gitlab.idiap.ch/bob/bob.io.base bob.sp = git https://gitlab.idiap.ch/bob/bob.sp -bob.math = git https://gitlab.idiap.ch/bob/bob.math bob.learn.activation = git https://gitlab.idiap.ch/bob/bob.learn.activation bob.learn.linear = git https://gitlab.idiap.ch/bob/bob.learn.linear diff --git a/doc/guide.rst b/doc/guide.rst index 1ce2e6d148319641d6c1922537fb16ac1ebc05c7..b91540e614bcc586b529f0a0abfc5a6472b4e05b 100644 --- a/doc/guide.rst +++ b/doc/guide.rst @@ -49,16 +49,16 @@ between :math:`Js` of successive iterations are lower than a convergence threshold. In this implementation, the training consists in the definition of the -statistical model, called machine, (:py:class:`bob.learn.em.KMeansMachine`) and -this statistical model is learned via a trainer -(:py:class:`bob.learn.em.KMeansTrainer`). +statistical model, called machine, (:py:class:`bob.learn.em.cluster.KMeansMachine`) and +this statistical model is learned by executing the +:py:meth:`~bob.learn.em.cluster.KMeansMachine.fit` method. Follow bellow an snippet on how to train a KMeans using Bob_. .. doctest:: :options: +NORMALIZE_WHITESPACE - >>> import bob.learn.em + >>> from bob.learn.em.cluster import KMeansMachine >>> import numpy >>> data = numpy.array( ... [[3,-3,100], @@ -66,18 +66,13 @@ Follow bellow an snippet on how to train a KMeans using Bob_. ... [3.5,-3.5,99], ... [-7,7,-100], ... [-5,5,-101]], dtype='float64') - >>> # Create a kmeans m with k=2 clusters with a dimensionality equal to 3 - >>> kmeans_machine = bob.learn.em.KMeansMachine(2, 3) - >>> kmeans_trainer = bob.learn.em.KMeansTrainer() - >>> max_iterations = 200 - >>> convergence_threshold = 1e-5 + >>> # Create a k-means machine with k=2 clusters + >>> kmeans_machine = KMeansMachine(2, convergence_threshold=1e-5, max_iter=200) >>> # Train the KMeansMachine - >>> bob.learn.em.train(kmeans_trainer, kmeans_machine, data, - ... max_iterations=max_iterations, - ... convergence_threshold=convergence_threshold) - >>> print(kmeans_machine.means) - [[ -6. 6. -100.5] - [ 3.5 -3.5 99. ]] + >>> kmeans_machine = kmeans_machine.fit(data) + >>> print(numpy.array(kmeans_machine.centroids_)) + [[ 3.5 -3.5 99. ] + [ -6. 6. -100.5]] Bellow follow an intuition (source code + plot) of a kmeans training using the @@ -103,7 +98,7 @@ This statistical model is defined in the class :py:class:`bob.learn.em.GMMMachine` as bellow. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> # Create a GMM with k=2 Gaussians with the dimensionality of 3 @@ -135,7 +130,7 @@ estimator. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -171,8 +166,10 @@ Bellow follow an intuition of the GMM trained the maximum likelihood estimator using the Iris flower `dataset <https://en.wikipedia.org/wiki/Iris_flower_data_set>`_. -.. plot:: plot/plot_ML.py - :include-source: False +.. + TODO uncomment when implemented + .. plot:: plot/plot_ML.py + :include-source: False Maximum a posteriori Estimator (MAP) @@ -198,7 +195,7 @@ Follow bellow an snippet on how to train a GMM using the MAP estimator. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -232,8 +229,10 @@ Follow bellow an snippet on how to train a GMM using the MAP estimator. Bellow follow an intuition of the GMM trained with the MAP estimator using the Iris flower `dataset <https://en.wikipedia.org/wiki/Iris_flower_data_set>`_. -.. plot:: plot/plot_MAP.py - :include-source: False +.. + TODO uncomment when implemented + .. plot:: plot/plot_MAP.py + :include-source: False Session Variability Modeling with Gaussian Mixture Models @@ -274,7 +273,7 @@ prior GMM. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -326,8 +325,10 @@ The arrows :math:`U_{1}`, :math:`U_{2}` and :math:`U_{3}` are the directions of the within class variations, with respect to each Gaussian component, that will be suppressed a posteriori. -.. plot:: plot/plot_ISV.py - :include-source: False +.. + TODO uncomment when implemented + .. plot:: plot/plot_ISV.py + :include-source: False The ISV statistical model is stored in this container @@ -337,7 +338,7 @@ Intersession variability modeling. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -400,8 +401,10 @@ between class variations with respect to each Gaussian component that will be added a posteriori. -.. plot:: plot/plot_JFA.py - :include-source: False +.. + TODO uncomment when implemented + .. plot:: plot/plot_JFA.py + :include-source: False The JFA statistical model is stored in this container :py:class:`bob.learn.em.JFABase` and the training is performed by @@ -409,7 +412,7 @@ The JFA statistical model is stored in this container Intersession variability modeling. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -472,8 +475,10 @@ Follow bellow an intuition of the data from the Iris flower `dataset <https://en.wikipedia.org/wiki/Iris_flower_data_set>`_, embedded in the iVector space. -.. plot:: plot/plot_iVector.py - :include-source: False +.. + TODO uncomment when implemented + .. plot:: plot/plot_iVector.py + :include-source: False The iVector statistical model is stored in this container @@ -482,7 +487,7 @@ The iVector statistical model is stored in this container a Total variability modeling. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -557,7 +562,7 @@ This scoring technique is implemented in :py:func:`bob.learn.em.linear_scoring`. The snippet bellow shows how to compute scores using this approximation. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> import bob.learn.em >>> import numpy @@ -604,7 +609,7 @@ Let us consider a training set of two classes, each with 3 samples of dimensionality 3. .. doctest:: - :options: +NORMALIZE_WHITESPACE + :options: +NORMALIZE_WHITESPACE +SKIP >>> data1 = numpy.array( ... [[3,-3,100], @@ -621,6 +626,7 @@ Learning a PLDA model can be performed by instantiating the class :py:meth:`bob.learn.em.train` method. .. doctest:: + :options: +SKIP >>> # This creates a PLDABase container for input feature of dimensionality >>> # 3 and with subspaces F and G of rank 1 and 2, respectively. @@ -638,6 +644,7 @@ obtained by calling the :py:meth:`bob.learn.em.PLDAMachine.compute_log_likelihood()` method. .. doctest:: + :options: +SKIP >>> plda = bob.learn.em.PLDAMachine(pldabase) >>> samples = numpy.array( @@ -651,6 +658,7 @@ a set of enrollment samples, then, several instances of the :py:meth:`bob.learn.em.PLDATrainer.enroll()` method as follows. .. doctest:: + :options: +SKIP >>> plda1 = bob.learn.em.PLDAMachine(pldabase) >>> samples1 = numpy.array( @@ -668,6 +676,7 @@ several test samples can be computed as previously described, and this separately for each model. .. doctest:: + :options: +SKIP >>> sample = numpy.array([3.2,-3.3,58], dtype=numpy.float64) >>> l1 = plda1.compute_log_likelihood(sample) @@ -684,6 +693,7 @@ computed, which is defined in more formal way by: :math:`s = \ln(P(x_{test},x_{enroll})) - \ln(P(x_{test})P(x_{enroll}))` .. doctest:: + :options: +SKIP >>> s1 = plda1(sample) >>> s2 = plda2(sample) diff --git a/doc/index.rst b/doc/index.rst index ac15c60c31fb2854aeb92873333d4e88d1483fbc..c32431d6ed2646a1f30bfe0225e3e14b2718630d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -32,20 +32,20 @@ References ----------- .. [Reynolds2000] *Reynolds, Douglas A., Thomas F. Quatieri, and Robert B. Dunn*. **Speaker Verification Using Adapted Gaussian Mixture Models**, Digital signal processing 10.1 (2000): 19-41. - -.. [Vogt2008] *R. Vogt, S. Sridharan*. **'Explicit Modelling of Session Variability for Speaker Verification'**, Computer Speech & Language, 2008, vol. 22, no. 1, pp. 17-38 - -.. [McCool2013] *C. McCool, R. Wallace, M. McLaren, L. El Shafey, S. Marcel*. **'Session Variability Modelling for Face Authentication'**, IET Biometrics, 2013 - -.. [ElShafey2014] *Laurent El Shafey, Chris McCool, Roy Wallace, Sebastien Marcel*. **'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: Applied to Face Recognition'**, TPAMI'2014 - -.. [PrinceElder2007] *Prince and Elder*. **'Probabilistic Linear Discriminant Analysis for Inference About Identity'**, ICCV'2007 - -.. [LiFu2012] *Li, Fu, Mohammed, Elder and Prince*. **'Probabilistic Models for Inference about Identity'**, TPAMI'2012 - -.. [Bishop1999] Tipping, Michael E., and Christopher M. Bishop. "Probabilistic principal component analysis." Journal of the Royal Statistical Society: Series B (Statistical Methodology) 61.3 (1999): 611-622. - -.. [Roweis1998] Roweis, Sam. "EM algorithms for PCA and SPCA." Advances in neural information processing systems (1998): 626-632. +.. + .. [Vogt2008] *R. Vogt, S. Sridharan*. **'Explicit Modelling of Session Variability for Speaker Verification'**, Computer Speech & Language, 2008, vol. 22, no. 1, pp. 17-38 +.. + .. [McCool2013] *C. McCool, R. Wallace, M. McLaren, L. El Shafey, S. Marcel*. **'Session Variability Modelling for Face Authentication'**, IET Biometrics, 2013 +.. + .. [ElShafey2014] *Laurent El Shafey, Chris McCool, Roy Wallace, Sebastien Marcel*. **'A Scalable Formulation of Probabilistic Linear Discriminant Analysis: Applied to Face Recognition'**, TPAMI'2014 +.. + .. [PrinceElder2007] *Prince and Elder*. **'Probabilistic Linear Discriminant Analysis for Inference About Identity'**, ICCV'2007 +.. + .. [LiFu2012] *Li, Fu, Mohammed, Elder and Prince*. **'Probabilistic Models for Inference about Identity'**, TPAMI'2012 +.. + .. [Bishop1999] Tipping, Michael E., and Christopher M. Bishop. "Probabilistic principal component analysis." Journal of the Royal Statistical Society: Series B (Statistical Methodology) 61.3 (1999): 611-622. +.. + .. [Roweis1998] Roweis, Sam. "EM algorithms for PCA and SPCA." Advances in neural information processing systems (1998): 626-632. .. [Glembek2009] Glembek, Ondrej, et al. "Comparison of scoring methods used in speaker recognition with joint factor analysis." Acoustics, Speech and Signal Processing, 2009. ICASSP 2009. IEEE International Conference on. IEEE, 2009. diff --git a/doc/plot/plot_ISV.py b/doc/plot/plot_ISV.py deleted file mode 100755 index 876d71b8da5b61a53b944ecfcd21cbaa1e17155f..0000000000000000000000000000000000000000 --- a/doc/plot/plot_ISV.py +++ /dev/null @@ -1,130 +0,0 @@ -import bob.db.iris -import bob.learn.em -import bob.learn.linear -import matplotlib.pyplot as plt -import numpy -numpy.random.seed(2) # FIXING A SEED - - -def train_ubm(features, n_gaussians): - """ - Train UBM - - **Parameters** - features: 2D numpy array with the features - - n_gaussians: Number of Gaussians - - """ - input_size = features.shape[1] - - kmeans_machine = bob.learn.em.KMeansMachine(int(n_gaussians), input_size) - ubm = bob.learn.em.GMMMachine(int(n_gaussians), input_size) - - # The K-means clustering is firstly used to used to estimate the initial - # means, the final variances and the final weights for each gaussian - # component - kmeans_trainer = bob.learn.em.KMeansTrainer('RANDOM_NO_DUPLICATE') - bob.learn.em.train(kmeans_trainer, kmeans_machine, features) - - # Getting the means, weights and the variances for each cluster. This is a - # very good estimator for the ML - (variances, weights) = kmeans_machine.get_variances_and_weights_for_each_cluster(features) - means = kmeans_machine.means - - # initialize the UBM with the output of kmeans - ubm.means = means - ubm.variances = variances - ubm.weights = weights - - # Creating the ML Trainer. We will adapt only the means - trainer = bob.learn.em.ML_GMMTrainer( - update_means=True, update_variances=False, update_weights=False) - bob.learn.em.train(trainer, ubm, features) - - return ubm - - -def isv_train(features, ubm): - """ - Train U matrix - - **Parameters** - features: List of :py:class:`bob.learn.em.GMMStats` organized by class - - n_gaussians: UBM (:py:class:`bob.learn.em.GMMMachine`) - - """ - - stats = [] - for user in features: - user_stats = [] - for f in user: - s = bob.learn.em.GMMStats(ubm.shape[0], ubm.shape[1]) - ubm.acc_statistics(f, s) - user_stats.append(s) - stats.append(user_stats) - - relevance_factor = 4 - subspace_dimension_of_u = 1 - - isvbase = bob.learn.em.ISVBase(ubm, subspace_dimension_of_u) - trainer = bob.learn.em.ISVTrainer(relevance_factor) - # trainer.rng = bob.core.random.mt19937(int(self.init_seed)) - bob.learn.em.train(trainer, isvbase, stats, max_iterations=50) - - return isvbase - - -# GENERATING DATA -data_per_class = bob.db.iris.data() -setosa = numpy.column_stack( - (data_per_class['setosa'][:, 0], data_per_class['setosa'][:, 3])) -versicolor = numpy.column_stack( - (data_per_class['versicolor'][:, 0], data_per_class['versicolor'][:, 3])) -virginica = numpy.column_stack( - (data_per_class['virginica'][:, 0], data_per_class['virginica'][:, 3])) -data = numpy.vstack((setosa, versicolor, virginica)) - -# TRAINING THE PRIOR -ubm = train_ubm(data, 3) -isvbase = isv_train([setosa, versicolor, virginica], ubm) - -# Variability direction -u0 = isvbase.u[0:2, 0] / numpy.linalg.norm(isvbase.u[0:2, 0]) -u1 = isvbase.u[2:4, 0] / numpy.linalg.norm(isvbase.u[2:4, 0]) -u2 = isvbase.u[4:6, 0] / numpy.linalg.norm(isvbase.u[4:6, 0]) - -figure, ax = plt.subplots() -plt.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") -plt.scatter(versicolor[:, 0], versicolor[:, 1], - c="goldenrod", label="versicolor") -plt.scatter(virginica[:, 0], virginica[:, 1], c="dimgrey", label="virginica") - -plt.scatter(ubm.means[:, 0], ubm.means[:, 1], c="blue", - marker="x", label="centroids - mle") -# plt.scatter(ubm.means[:, 0], ubm.means[:, 1], c="blue", -# marker=".", label="within class varibility", s=0.01) - -ax.arrow(ubm.means[0, 0], ubm.means[0, 1], u0[0], u0[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[1, 0], ubm.means[1, 1], u1[0], u1[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[2, 0], ubm.means[2, 1], u2[0], u2[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -plt.text(ubm.means[0, 0] + u0[0], ubm.means[0, 1] + - u0[1] - 0.1, r'$\mathbf{U}_1$', fontsize=15) -plt.text(ubm.means[1, 0] + u1[0], ubm.means[1, 1] + - u1[1] - 0.1, r'$\mathbf{U}_2$', fontsize=15) -plt.text(ubm.means[2, 0] + u2[0], ubm.means[2, 1] + - u2[1] - 0.1, r'$\mathbf{U}_3$', fontsize=15) - -plt.xticks([], []) -plt.yticks([], []) - -# plt.grid(True) -plt.xlabel('Sepal length') -plt.ylabel('Petal width') -plt.legend() -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_JFA.py b/doc/plot/plot_JFA.py deleted file mode 100755 index a2b05d5731b3d8aaefd419df0184d0b9daaacf2a..0000000000000000000000000000000000000000 --- a/doc/plot/plot_JFA.py +++ /dev/null @@ -1,156 +0,0 @@ -import bob.db.iris -import bob.learn.em -import bob.learn.linear -import matplotlib.pyplot as plt -import numpy -numpy.random.seed(2) # FIXING A SEED - - -def train_ubm(features, n_gaussians): - """ - Train UBM - - **Parameters** - features: 2D numpy array with the features - - n_gaussians: Number of Gaussians - - """ - - input_size = features.shape[1] - - kmeans_machine = bob.learn.em.KMeansMachine(int(n_gaussians), input_size) - ubm = bob.learn.em.GMMMachine(int(n_gaussians), input_size) - - # The K-means clustering is firstly used to used to estimate the initial - # means, the final variances and the final weights for each gaussian - # component - kmeans_trainer = bob.learn.em.KMeansTrainer('RANDOM_NO_DUPLICATE') - bob.learn.em.train(kmeans_trainer, kmeans_machine, features) - - # Getting the means, weights and the variances for each cluster. This is a - # very good estimator for the ML - (variances, weights) = kmeans_machine.get_variances_and_weights_for_each_cluster(features) - means = kmeans_machine.means - - # initialize the UBM with the output of kmeans - ubm.means = means - ubm.variances = variances - ubm.weights = weights - - # Creating the ML Trainer. We will adapt only the means - trainer = bob.learn.em.ML_GMMTrainer( - update_means=True, update_variances=False, update_weights=False) - bob.learn.em.train(trainer, ubm, features) - - return ubm - - -def jfa_train(features, ubm): - """ - Trains U and V matrix - - **Parameters** - features: List of :py:class:`bob.learn.em.GMMStats` organized by class - - n_gaussians: UBM (:py:class:`bob.learn.em.GMMMachine`) - - """ - - stats = [] - for user in features: - user_stats = [] - for f in user: - s = bob.learn.em.GMMStats(ubm.shape[0], ubm.shape[1]) - ubm.acc_statistics(f, s) - user_stats.append(s) - stats.append(user_stats) - - subspace_dimension_of_u = 1 - subspace_dimension_of_v = 1 - - jfa_base = bob.learn.em.JFABase( - ubm, subspace_dimension_of_u, subspace_dimension_of_v) - trainer = bob.learn.em.JFATrainer() - # trainer.rng = bob.core.random.mt19937(int(self.init_seed)) - bob.learn.em.train_jfa(trainer, jfa_base, stats, max_iterations=50) - - return jfa_base - - -# GENERATING DATA -data_per_class = bob.db.iris.data() -setosa = numpy.column_stack( - (data_per_class['setosa'][:, 0], data_per_class['setosa'][:, 3])) -versicolor = numpy.column_stack( - (data_per_class['versicolor'][:, 0], data_per_class['versicolor'][:, 3])) -virginica = numpy.column_stack( - (data_per_class['virginica'][:, 0], data_per_class['virginica'][:, 3])) -data = numpy.vstack((setosa, versicolor, virginica)) - -# TRAINING THE PRIOR -ubm = train_ubm(data, 3) -jfa_base = jfa_train([setosa, versicolor, virginica], ubm) - -# Variability direction U -u0 = jfa_base.u[0:2, 0] / numpy.linalg.norm(jfa_base.u[0:2, 0]) -u1 = jfa_base.u[2:4, 0] / numpy.linalg.norm(jfa_base.u[2:4, 0]) -u2 = jfa_base.u[4:6, 0] / numpy.linalg.norm(jfa_base.u[4:6, 0]) - - -# Variability direction V -v0 = jfa_base.v[0:2, 0] / numpy.linalg.norm(jfa_base.v[0:2, 0]) -v1 = jfa_base.v[2:4, 0] / numpy.linalg.norm(jfa_base.v[2:4, 0]) -v2 = jfa_base.v[4:6, 0] / numpy.linalg.norm(jfa_base.v[4:6, 0]) - - -figure, ax = plt.subplots() -plt.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") -plt.scatter(versicolor[:, 0], versicolor[:, 1], - c="goldenrod", label="versicolor") -plt.scatter(virginica[:, 0], virginica[:, 1], c="dimgrey", label="virginica") - -plt.scatter(ubm.means[:, 0], ubm.means[:, 1], c="blue", - marker="x", label="centroids - mle") -# plt.scatter(ubm.means[:, 0], ubm.means[:, 1], c="blue", -# marker=".", label="within class varibility", s=0.01) - -# U -ax.arrow(ubm.means[0, 0], ubm.means[0, 1], u0[0], u0[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[1, 0], ubm.means[1, 1], u1[0], u1[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[2, 0], ubm.means[2, 1], u2[0], u2[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -plt.text(ubm.means[0, 0] + u0[0], ubm.means[0, 1] + - u0[1] - 0.1, r'$\mathbf{U}_1$', fontsize=15) -plt.text(ubm.means[1, 0] + u1[0], ubm.means[1, 1] + - u1[1] - 0.1, r'$\mathbf{U}_2$', fontsize=15) -plt.text(ubm.means[2, 0] + u2[0], ubm.means[2, 1] + - u2[1] - 0.1, r'$\mathbf{U}_3$', fontsize=15) - -# V -ax.arrow(ubm.means[0, 0], ubm.means[0, 1], v0[0], v0[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[1, 0], ubm.means[1, 1], v1[0], v1[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -ax.arrow(ubm.means[2, 0], ubm.means[2, 1], v2[0], v2[1], - fc="k", ec="k", head_width=0.05, head_length=0.1) -plt.text(ubm.means[0, 0] + v0[0], ubm.means[0, 1] + - v0[1] - 0.1, r'$\mathbf{V}_1$', fontsize=15) -plt.text(ubm.means[1, 0] + v1[0], ubm.means[1, 1] + - v1[1] - 0.1, r'$\mathbf{V}_2$', fontsize=15) -plt.text(ubm.means[2, 0] + v2[0], ubm.means[2, 1] + - v2[1] - 0.1, r'$\mathbf{V}_3$', fontsize=15) - -plt.xticks([], []) -plt.yticks([], []) - -# plt.grid(True) -plt.xlabel('Sepal length') -plt.ylabel('Petal width') -plt.legend(loc=2) -plt.ylim([-1, 3.5]) - -plt.tight_layout() -# plt.show() diff --git a/doc/plot/plot_MAP.py b/doc/plot/plot_MAP.py deleted file mode 100644 index d873b2ca69f32363e76271070e76a21b81be7b6c..0000000000000000000000000000000000000000 --- a/doc/plot/plot_MAP.py +++ /dev/null @@ -1,46 +0,0 @@ -import matplotlib.pyplot as plt -import bob.db.iris -import bob.learn.em -import numpy -numpy.random.seed(10) - -data_per_class = bob.db.iris.data() -setosa = numpy.column_stack( - (data_per_class['setosa'][:, 0], data_per_class['setosa'][:, 3])) -versicolor = numpy.column_stack( - (data_per_class['versicolor'][:, 0], data_per_class['versicolor'][:, 3])) -virginica = numpy.column_stack( - (data_per_class['virginica'][:, 0], data_per_class['virginica'][:, 3])) - -data = numpy.vstack((setosa, versicolor, virginica)) - -# Two clusters with a feature dimensionality of 3 -mle_machine = bob.learn.em.GMMMachine(3, 2) -mle_machine.means = numpy.array([[5, 3], [4, 2], [7, 3.]]) - -# Creating some random data centered in -map_machine = bob.learn.em.GMMMachine(3, 2) -map_trainer = bob.learn.em.MAP_GMMTrainer(mle_machine, relevance_factor=4) -bob.learn.em.train(map_trainer, map_machine, data, max_iterations=200, - convergence_threshold=1e-5) # Train the KMeansMachine - - -figure, ax = plt.subplots() -# plt.scatter(data[:, 0], data[:, 1], c="olivedrab", label="new data") -plt.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") -plt.scatter(versicolor[:, 0], versicolor[:, 1], - c="goldenrod", label="versicolor") -plt.scatter(virginica[:, 0], virginica[:, 1], - c="dimgrey", label="virginica") -plt.scatter(mle_machine.means[:, 0], - mle_machine.means[:, 1], c="blue", marker="x", - label="prior centroids - mle", s=60) -plt.scatter(map_machine.means[:, 0], map_machine.means[:, 1], c="red", - marker="^", label="adapted centroids - map", s=60) -plt.legend() -plt.xticks([], []) -plt.yticks([], []) -ax.set_xlabel("Sepal length") -ax.set_ylabel("Petal width") -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_ML.py b/doc/plot/plot_ML.py index 18902c4dd8939e33e537b274f9d3b31deaa3b75b..dd6857ca93927beef4b198e53ab2d81eb666bbb9 100644 --- a/doc/plot/plot_ML.py +++ b/doc/plot/plot_ML.py @@ -1,34 +1,69 @@ -import bob.learn.em +from bob.learn.em.mixture import GMMMachine import bob.db.iris import numpy import matplotlib.pyplot as plt +from matplotlib.patches import Ellipse +from matplotlib.lines import Line2D + +import logging + +logger = logging.getLogger("bob.learn.em") +logger.setLevel("DEBUG") + data_per_class = bob.db.iris.data() setosa = numpy.column_stack( - (data_per_class['setosa'][:, 0], data_per_class['setosa'][:, 3])) + (data_per_class["setosa"][:, 0], data_per_class["setosa"][:, 3]) +) versicolor = numpy.column_stack( - (data_per_class['versicolor'][:, 0], data_per_class['versicolor'][:, 3])) + (data_per_class["versicolor"][:, 0], data_per_class["versicolor"][:, 3]) +) virginica = numpy.column_stack( - (data_per_class['virginica'][:, 0], data_per_class['virginica'][:, 3])) + (data_per_class["virginica"][:, 0], data_per_class["virginica"][:, 3]) +) data = numpy.vstack((setosa, versicolor, virginica)) # Two clusters with a feature dimensionality of 3 -machine = bob.learn.em.GMMMachine(3, 2) -trainer = bob.learn.em.ML_GMMTrainer(True, True, True) -machine.means = numpy.array([[5, 3], [4, 2], [7, 3.]]) -bob.learn.em.train(trainer, machine, data, max_iterations=200, - convergence_threshold=1e-5) # Train the KMeansMachine +machine = GMMMachine( + 3, + convergence_threshold=1e-5, + update_means=True, + update_variances=True, + update_weights=True, +) + +# Initialize the means with known values (optional, skips kmeans) +machine.means = numpy.array([[5, 3], [4, 2], [7, 3]], dtype=float) +machine = machine.fit(data) + +# Plotting figure, ax = plt.subplots() -plt.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") -plt.scatter(versicolor[:, 0], versicolor[:, 1], - c="goldenrod", label="versicolor") -plt.scatter(virginica[:, 0], virginica[:, 1], - c="dimgrey", label="virginica") -plt.scatter(machine.means[:, 0], - machine.means[:, 1], c="blue", marker="x", label="centroids", s=60) -plt.legend() +ax.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") +ax.scatter(versicolor[:, 0], versicolor[:, 1], c="goldenrod", label="versicolor") +ax.scatter(virginica[:, 0], virginica[:, 1], c="dimgrey", label="virginica") +ax.scatter( + machine.means[:, 0], + machine.means[:, 1], + c="blue", + marker="x", + label="centroids", + s=60, +) + +# Draw ellipses for covariance +for mean, variance in zip(machine.means, machine.variances): + eigvals, eigvecs = numpy.linalg.eig(numpy.diag(variance)) + axis = numpy.sqrt(eigvals) * numpy.sqrt(5.991) + angle = 180.0 * numpy.arctan(eigvecs[1][0] / eigvecs[1][1]) / numpy.pi + ax.add_patch(Ellipse(mean, *axis, angle=angle, linewidth=1, fill=False, zorder=2)) + +# Plot details (legend, axis labels) +plt.legend( + handles=ax.get_legend_handles_labels()[0] + + [Line2D([0], [0], color="black", label="covariances")] +) plt.xticks([], []) plt.yticks([], []) ax.set_xlabel("Sepal length") diff --git a/doc/plot/plot_Tnorm.py b/doc/plot/plot_Tnorm.py deleted file mode 100644 index 8772a95d885ca0c93be117675a86ae04bf614522..0000000000000000000000000000000000000000 --- a/doc/plot/plot_Tnorm.py +++ /dev/null @@ -1,48 +0,0 @@ -import matplotlib.pyplot as plt -import bob.learn.em -import numpy -numpy.random.seed(10) - -n_clients = 10 -n_scores_per_client = 200 - -# Defining some fake scores for genuines and impostors -impostor_scores = numpy.random.normal(-15.5, - 5, (n_scores_per_client, n_clients)) -genuine_scores = numpy.random.normal(0.5, 5, (n_scores_per_client, n_clients)) - -# Defining the scores for the statistics computation -t_scores = numpy.random.normal(-5., 5, (n_scores_per_client, n_clients)) - -# T - Normalizing -t_norm_impostors = bob.learn.em.tnorm(impostor_scores, t_scores) -t_norm_genuine = bob.learn.em.tnorm(genuine_scores, t_scores) - -# PLOTTING -figure = plt.subplot(2, 1, 1) -ax = figure.axes -plt.title("Raw scores", fontsize=8) -plt.hist(impostor_scores.reshape(n_scores_per_client * n_clients), - label='Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(genuine_scores.reshape(n_scores_per_client * n_clients), - label='Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.legend(fontsize=8) -plt.yticks([], []) - - -figure = plt.subplot(2, 1, 2) -ax = figure.axes -plt.title("T-norm scores", fontsize=8) -plt.hist(t_norm_impostors.reshape(n_scores_per_client * n_clients), - label='T-Norm Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(t_norm_genuine.reshape(n_scores_per_client * n_clients), - label='T-Norm Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.legend(fontsize=8) -plt.yticks([], []) - -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_ZTnorm.py b/doc/plot/plot_ZTnorm.py deleted file mode 100644 index 8300fdccb419bdc8843c3396f3e78940b56684d9..0000000000000000000000000000000000000000 --- a/doc/plot/plot_ZTnorm.py +++ /dev/null @@ -1,54 +0,0 @@ -import matplotlib.pyplot as plt -import bob.learn.em -import numpy -numpy.random.seed(10) - -n_clients = 10 -n_scores_per_client = 200 - -# Defining some fake scores for genuines and impostors -impostor_scores = numpy.random.normal(-15.5, - 5, (n_scores_per_client, n_clients)) -genuine_scores = numpy.random.normal(0.5, 5, (n_scores_per_client, n_clients)) - -# Defining the scores for the statistics computation -z_scores = numpy.random.normal(-5., 5, (n_scores_per_client, n_clients)) -t_scores = numpy.random.normal(-6., 5, (n_scores_per_client, n_clients)) - -# T-normalizing the Z-scores -zt_scores = bob.learn.em.tnorm(z_scores, t_scores) - -# ZT - Normalizing -zt_norm_impostors = bob.learn.em.ztnorm( - impostor_scores, z_scores, t_scores, zt_scores) -zt_norm_genuine = bob.learn.em.ztnorm( - genuine_scores, z_scores, t_scores, zt_scores) - -# PLOTTING -figure = plt.subplot(2, 1, 1) -ax = figure.axes -plt.title("Raw scores", fontsize=8) -plt.hist(impostor_scores.reshape(n_scores_per_client * n_clients), - label='Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(genuine_scores.reshape(n_scores_per_client * n_clients), - label='Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.legend(fontsize=8) -plt.yticks([], []) - - -figure = plt.subplot(2, 1, 2) -ax = figure.axes -plt.title("T-norm scores", fontsize=8) -plt.hist(zt_norm_impostors.reshape(n_scores_per_client * n_clients), - label='T-Norm Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(zt_norm_genuine.reshape(n_scores_per_client * n_clients), - label='T-Norm Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.legend(fontsize=8) -plt.yticks([], []) - -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_Znorm.py b/doc/plot/plot_Znorm.py deleted file mode 100644 index 8c16ebbd08dc62864040205012b4682b8de96562..0000000000000000000000000000000000000000 --- a/doc/plot/plot_Znorm.py +++ /dev/null @@ -1,49 +0,0 @@ -import matplotlib.pyplot as plt -import bob.learn.em -import numpy -numpy.random.seed(10) - - -n_clients = 10 -n_scores_per_client = 200 - -# Defining some fake scores for genuines and impostors -impostor_scores = numpy.random.normal(-15.5, - 5, (n_scores_per_client, n_clients)) -genuine_scores = numpy.random.normal(0.5, 5, (n_scores_per_client, n_clients)) - -# Defining the scores for the statistics computation -z_scores = numpy.random.normal(-5., 5, (n_scores_per_client, n_clients)) - -# Z - Normalizing -z_norm_impostors = bob.learn.em.znorm(impostor_scores, z_scores) -z_norm_genuine = bob.learn.em.znorm(genuine_scores, z_scores) - -# PLOTTING -figure = plt.subplot(2, 1, 1) -ax = figure.axes -plt.title("Raw scores", fontsize=8) -plt.hist(impostor_scores.reshape(n_scores_per_client * n_clients), - label='Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(genuine_scores.reshape(n_scores_per_client * n_clients), - label='Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.legend(fontsize=8) -plt.yticks([], []) - - -figure = plt.subplot(2, 1, 2) -ax = figure.axes -plt.title("Z-norm scores", fontsize=8) -plt.hist(z_norm_impostors.reshape(n_scores_per_client * n_clients), - label='Z-Norm Impostors', normed=True, - color='C1', alpha=0.5, bins=50) -plt.hist(z_norm_genuine.reshape(n_scores_per_client * n_clients), - label='Z-Norm Genuine', normed=True, - color='C0', alpha=0.5, bins=50) -plt.yticks([], []) -plt.legend(fontsize=8) - -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_iVector.py b/doc/plot/plot_iVector.py deleted file mode 100755 index 95953d3a2d40c64ca7a08d0dd6eff0d7346524fb..0000000000000000000000000000000000000000 --- a/doc/plot/plot_iVector.py +++ /dev/null @@ -1,201 +0,0 @@ -import bob.db.iris -import bob.learn.em -import bob.learn.linear -import matplotlib.pyplot as plt -import numpy -numpy.random.seed(2) # FIXING A SEED - - -def train_ubm(features, n_gaussians): - """ - Train UBM - - **Parameters** - features: 2D numpy array with the features - - n_gaussians: Number of Gaussians - - """ - - input_size = features.shape[1] - - kmeans_machine = bob.learn.em.KMeansMachine(int(n_gaussians), input_size) - ubm = bob.learn.em.GMMMachine(int(n_gaussians), input_size) - - # The K-means clustering is firstly used to used to estimate the initial - # means, the final variances and the final weights for each gaussian - # component - kmeans_trainer = bob.learn.em.KMeansTrainer('RANDOM_NO_DUPLICATE') - bob.learn.em.train(kmeans_trainer, kmeans_machine, features) - - # Getting the means, weights and the variances for each cluster. This is a - # very good estimator for the ML - (variances, weights) = kmeans_machine.get_variances_and_weights_for_each_cluster(features) - means = kmeans_machine.means - - # initialize the UBM with the output of kmeans - ubm.means = means - ubm.variances = variances - ubm.weights = weights - - # Creating the ML Trainer. We will adapt only the means - trainer = bob.learn.em.ML_GMMTrainer( - update_means=True, update_variances=False, update_weights=False) - bob.learn.em.train(trainer, ubm, features) - - return ubm - - -def ivector_train(features, ubm): - """ - Trains T matrix - - **Parameters** - features: List of :py:class:`bob.learn.em.GMMStats` - - n_gaussians: UBM (:py:class:`bob.learn.em.GMMMachine`) - - """ - - stats = [] - for user in features: - s = bob.learn.em.GMMStats(ubm.shape[0], ubm.shape[1]) - for f in user: - ubm.acc_statistics(f, s) - stats.append(s) - - subspace_dimension_of_t = 2 - - ivector_trainer = bob.learn.em.IVectorTrainer(update_sigma=True) - ivector_machine = bob.learn.em.IVectorMachine( - ubm, subspace_dimension_of_t, 10e-5) - - # train IVector model - bob.learn.em.train(ivector_trainer, ivector_machine, stats, 500) - - return ivector_machine - - -def acc_stats(data, gmm): - gmm_stats = [] - for d in data: - s = bob.learn.em.GMMStats(gmm.shape[0], gmm.shape[1]) - gmm.acc_statistics(d, s) - gmm_stats.append(s) - - return gmm_stats - - -def compute_ivectors(gmm_stats, ivector_machine): - """ - Given :py:class:`bob.learn.em.GMMStats` and an T matrix, get the iVectors. - """ - - ivectors = [] - for g in gmm_stats: - ivectors.append(ivector_machine(g)) - - return numpy.array(ivectors) - - -# GENERATING DATA -data_per_class = bob.db.iris.data() -setosa = numpy.column_stack( - (data_per_class['setosa'][:, 0], data_per_class['setosa'][:, 3])) -versicolor = numpy.column_stack( - (data_per_class['versicolor'][:, 0], data_per_class['versicolor'][:, 3])) -virginica = numpy.column_stack( - (data_per_class['virginica'][:, 0], data_per_class['virginica'][:, 3])) -data = numpy.vstack((setosa, versicolor, virginica)) - -# TRAINING THE PRIOR -ubm = train_ubm(data, 3) -ivector_machine = ivector_train([setosa, versicolor, virginica], ubm) - -# Variability direction U -# t0 = T[0:2, 0] / numpy.linalg.norm(T[0:2, 0]) -# t1 = T[2:4, 0] / numpy.linalg.norm(T[2:4, 0]) -# t2 = T[4:6, 0] / numpy.linalg.norm(T[4:6, 0]) - - -# figure, ax = plt.subplots() -figure = plt.subplot(2, 1, 1) -ax = figure.axes -plt.title("Raw fetures") -plt.scatter(setosa[:, 0], setosa[:, 1], c="darkcyan", label="setosa") -plt.scatter(versicolor[:, 0], versicolor[:, 1], - c="goldenrod", label="versicolor") -plt.scatter(virginica[:, 0], virginica[:, 1], c="dimgrey", label="virginica") -# plt.grid(True) -# plt.xlabel('Sepal length') -plt.ylabel('Petal width') -plt.legend(loc=2) -plt.ylim([-1, 3.5]) -plt.xticks([], []) -plt.yticks([], []) - - -figure = plt.subplot(2, 1, 2) -ax = figure.axes -ivector_setosa = compute_ivectors(acc_stats(setosa, ubm), ivector_machine) -ivector_versicolor = compute_ivectors( - acc_stats(versicolor, ubm), ivector_machine) -ivector_virginica = compute_ivectors( - acc_stats(virginica, ubm), ivector_machine) - - -# Whitening iVectors -whitening_trainer = bob.learn.linear.WhiteningTrainer() -whitener_machine = bob.learn.linear.Machine( - ivector_setosa.shape[1], ivector_setosa.shape[1]) -whitening_trainer.train(numpy.vstack( - (ivector_setosa, ivector_versicolor, ivector_virginica)), whitener_machine) -ivector_setosa = whitener_machine(ivector_setosa) -ivector_versicolor = whitener_machine(ivector_versicolor) -ivector_virginica = whitener_machine(ivector_virginica) - - -# LDA ivectors -lda_trainer = bob.learn.linear.FisherLDATrainer() -lda_machine = bob.learn.linear.Machine( - ivector_setosa.shape[1], ivector_setosa.shape[1]) -lda_trainer.train([ivector_setosa, ivector_versicolor, - ivector_virginica], lda_machine) -ivector_setosa = lda_machine(ivector_setosa) -ivector_versicolor = lda_machine(ivector_versicolor) -ivector_virginica = lda_machine(ivector_virginica) - - -# WCCN ivectors -# wccn_trainer = bob.learn.linear.WCCNTrainer() -# wccn_machine = bob.learn.linear.Machine( -# ivector_setosa.shape[1], ivector_setosa.shape[1]) -# wccn_trainer.train([ivector_setosa, ivector_versicolor, -# ivector_virginica], wccn_machine) -# ivector_setosa = wccn_machine(ivector_setosa) -# ivector_versicolor = wccn_machine(ivector_versicolor) -# ivector_virginica = wccn_machine(ivector_virginica) - - -plt.title("First two ivectors") -plt.scatter(ivector_setosa[:, 0], - ivector_setosa[:, 1], c="darkcyan", label="setosa", - marker="x") -plt.scatter(ivector_versicolor[:, 0], - ivector_versicolor[:, 1], c="goldenrod", label="versicolor", - marker="x") -plt.scatter(ivector_virginica[:, 0], - ivector_virginica[:, 1], c="dimgrey", label="virginica", - marker="x") - -plt.xticks([], []) -plt.yticks([], []) - -# plt.grid(True) -# plt.xlabel('Sepal length') -# plt.ylabel('Petal width') -plt.legend(loc=2) -plt.ylim([-1, 3.5]) - -plt.tight_layout() -plt.show() diff --git a/doc/plot/plot_kmeans.py b/doc/plot/plot_kmeans.py index b56f42999e7b2b9c2515b747905b709a77258fde..ccafc146e6cda674c2dcad3249eb0770318f5976 100644 --- a/doc/plot/plot_kmeans.py +++ b/doc/plot/plot_kmeans.py @@ -1,4 +1,4 @@ -import bob.learn.em +from bob.learn.em.cluster import KMeansMachine import bob.db.iris import numpy import matplotlib.pyplot as plt @@ -14,11 +14,10 @@ virginica = numpy.column_stack( data = numpy.vstack((setosa, versicolor, virginica)) # Training KMeans -# Two clusters with a feature dimensionality of 3 -machine = bob.learn.em.KMeansMachine(3, 2) -trainer = bob.learn.em.KMeansTrainer() -bob.learn.em.train(trainer, machine, data, max_iterations=200, - convergence_threshold=1e-5) # Train the KMeansMachine +# 3 clusters with a feature dimensionality of 2 +machine = KMeansMachine(n_clusters=3, init_method="k-means++").fit(data) + +predictions = machine.predict(data) # Plotting figure, ax = plt.subplots() @@ -28,8 +27,8 @@ plt.scatter(versicolor[:, 0], versicolor[:, 1], c="goldenrod", label="versicolor") plt.scatter(virginica[:, 0], virginica[:, 1], c="dimgrey", label="virginica") -plt.scatter(machine.means[:, 0], - machine.means[:, 1], c="blue", marker="x", label="centroids", +plt.scatter(machine.centroids_[:, 0], + machine.centroids_[:, 1], c="blue", marker="x", label="centroids", s=60) plt.legend() plt.xticks([], []) @@ -37,3 +36,4 @@ plt.yticks([], []) ax.set_xlabel("Sepal length") ax.set_ylabel("Petal width") plt.tight_layout() +plt.show() diff --git a/doc/py_api.rst b/doc/py_api.rst index ba31cb5074149fc23d7696d0fd44e4d1caebac58..64695e03be4fbb3c60340ccbd01eb6693cd6a7c2 100644 --- a/doc/py_api.rst +++ b/doc/py_api.rst @@ -18,7 +18,8 @@ Trainers .. autosummary:: - bob.learn.em.KMeansTrainer +.. + TODO uncomment when implemented bob.learn.em.ML_GMMTrainer bob.learn.em.MAP_GMMTrainer bob.learn.em.ISVTrainer @@ -32,10 +33,12 @@ Machines .. autosummary:: - bob.learn.em.KMeansMachine - bob.learn.em.Gaussian - bob.learn.em.GMMStats - bob.learn.em.GMMMachine + bob.learn.em.cluster.KMeansMachine + bob.learn.em.mixture.GMMStats + bob.learn.em.mixture.GMMMachine + +.. + TODO uncomment when implemented bob.learn.em.ISVBase bob.learn.em.ISVMachine bob.learn.em.JFABase @@ -46,11 +49,11 @@ Machines Functions --------- -.. autosummary:: +.. + TODO uncomment when implemented + .. autosummary:: - bob.learn.em.linear_scoring - bob.learn.em.train - bob.learn.em.train_jfa + bob.learn.em.linear_scoring Detailed Information -------------------- diff --git a/requirements.txt b/requirements.txt index 77918d31c7c8099fd269eec2e61a9268d0e40391..8a86a69f50e30281292a5f1c6a71ecd22fb71f01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ setuptools bob.extension -bob.blitz -bob.core > 2.0.5 bob.io.base bob.sp -bob.math > 2 bob.learn.activation -bob.learn.linear \ No newline at end of file +bob.learn.linear +dask +dask-ml +h5py diff --git a/setup.py b/setup.py index 3528a1dcc0624d904f58acbb179f938642fa315f..732b0d949db969ed12a8ff6676e3d5115385ff5f 100644 --- a/setup.py +++ b/setup.py @@ -3,14 +3,17 @@ # Andre Anjos <andre.anjos@idiap.ch> # Mon 16 Apr 08:18:08 2012 CEST +from setuptools import dist +from setuptools import setup + +from bob.extension.utils import find_packages +from bob.extension.utils import load_requirements + bob_packages = ['bob.core', 'bob.io.base', 'bob.sp', 'bob.math', 'bob.learn.activation', 'bob.learn.linear'] +dist.Distribution(dict(setup_requires=['bob.extension>=2.0.7'] + bob_packages)) -from setuptools import setup, find_packages, dist -dist.Distribution(dict(setup_requires=['bob.extension>=2.0.7', 'bob.blitz'] + bob_packages)) -from bob.blitz.extension import Extension, Library, build_ext -from bob.extension.utils import load_requirements -build_requires = load_requirements() +install_requires = load_requirements() # Define package version version = open("version.txt").read().rstrip() @@ -27,6 +30,7 @@ setup( license='BSD', author='Andre Anjos', author_email='andre.anjos@idiap.ch', + keywords="bob, em, expectation-maximization", long_description=open('README.rst').read(), @@ -34,109 +38,17 @@ setup( include_package_data=True, zip_safe=False, - setup_requires = build_requires, - install_requires = build_requires, - - - - ext_modules = [ - Extension("bob.learn.em.version", - [ - "bob/learn/em/version.cpp", - ], - bob_packages = bob_packages, - packages = packages, - boost_modules = boost_modules, - version = version, - ), - - Library("bob.learn.em.bob_learn_em", - [ - "bob/learn/em/cpp/Gaussian.cpp", - "bob/learn/em/cpp/GMMMachine.cpp", - "bob/learn/em/cpp/GMMStats.cpp", - "bob/learn/em/cpp/IVectorMachine.cpp", - "bob/learn/em/cpp/KMeansMachine.cpp", - "bob/learn/em/cpp/LinearScoring.cpp", - "bob/learn/em/cpp/PLDAMachine.cpp", - - "bob/learn/em/cpp/FABase.cpp", - "bob/learn/em/cpp/JFABase.cpp", - "bob/learn/em/cpp/ISVBase.cpp", - "bob/learn/em/cpp/JFAMachine.cpp", - "bob/learn/em/cpp/ISVMachine.cpp", - - "bob/learn/em/cpp/FABaseTrainer.cpp", - "bob/learn/em/cpp/JFATrainer.cpp", - "bob/learn/em/cpp/ISVTrainer.cpp", - - "bob/learn/em/cpp/EMPCATrainer.cpp", - "bob/learn/em/cpp/GMMBaseTrainer.cpp", - "bob/learn/em/cpp/IVectorTrainer.cpp", - "bob/learn/em/cpp/KMeansTrainer.cpp", - "bob/learn/em/cpp/MAP_GMMTrainer.cpp", - "bob/learn/em/cpp/ML_GMMTrainer.cpp", - "bob/learn/em/cpp/PLDATrainer.cpp", - ], - bob_packages = bob_packages, - packages = packages, - boost_modules = boost_modules, - version = version, - ), - - Extension("bob.learn.em._library", - [ - "bob/learn/em/gaussian.cpp", - "bob/learn/em/gmm_stats.cpp", - "bob/learn/em/gmm_machine.cpp", - "bob/learn/em/kmeans_machine.cpp", - "bob/learn/em/kmeans_trainer.cpp", - - "bob/learn/em/ml_gmm_trainer.cpp", - "bob/learn/em/map_gmm_trainer.cpp", - - "bob/learn/em/jfa_base.cpp", - "bob/learn/em/jfa_machine.cpp", - "bob/learn/em/jfa_trainer.cpp", - - "bob/learn/em/isv_base.cpp", - "bob/learn/em/isv_machine.cpp", - "bob/learn/em/isv_trainer.cpp", - - "bob/learn/em/ivector_machine.cpp", - "bob/learn/em/ivector_trainer.cpp", - - "bob/learn/em/plda_base.cpp", - "bob/learn/em/plda_machine.cpp", - - "bob/learn/em/empca_trainer.cpp", - - "bob/learn/em/plda_trainer.cpp", - - "bob/learn/em/linear_scoring.cpp", - - "bob/learn/em/main.cpp", - ], - bob_packages = bob_packages, - packages = packages, - boost_modules = boost_modules, - version = version, - ), - ], - - cmdclass = { - 'build_ext': build_ext - }, - - classifiers = [ - 'Framework :: Bob', - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: Software Development :: Libraries :: Python Modules', + install_requires = install_requires, + + classifiers=[ + 'Framework :: Bob', + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Natural Language :: English', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Software Development :: Libraries :: Python Modules', ], - ) +) diff --git a/version.txt b/version.txt index 9d782a130a3faa004414edb64de54c879d7696e3..2f99c1e8f69abf11fb3a4c953004c69bc26207c0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.1.9b0 +3.0.0b0