diff --git a/buildout.cfg b/buildout.cfg index 69e404425b1f9b9c63dae635d85174970a6b30f7..0579e0525ff52c0b2385a09e00417c2e399c6baf 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -10,7 +10,9 @@ extensions = xbob.buildout auto-checkout = * develop = src/xbob.extension src/xbob.blitz + src/xbob.core src/xbob.io.base + src/xbob.sp . ; options for xbob.buildout extension @@ -22,7 +24,9 @@ prefixes = /idiap/group/torch5spro/releases/preview/install/linux-x86_64-release [sources] xbob.extension = git https://github.com/bioidiap/xbob.extension branch=prototype xbob.blitz = git https://github.com/bioidiap/xbob.blitz +xbob.core = git https://github.com/bioidiap/xbob.core xbob.io.base = git https://github.com/bioidiap/xbob.io.base +xbob.sp = git https://github.com/bioidiap/xbob.sp [scripts] recipe = xbob.buildout:scripts diff --git a/setup.py b/setup.py index 4dcce76617d043d601d6dbbc4f6d6c5c9794b872..69d1d14566b3b5039bc5ba95a3344aa3edf41e8c 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,9 @@ setup( install_requires=[ 'setuptools', 'xbob.blitz', + 'xbob.core', 'xbob.io.base', + 'xbob.sp', ], namespace_packages=[ @@ -53,7 +55,6 @@ setup( [ "xbob/learn/misc/bic.cpp", "xbob/learn/misc/bic_trainer.cpp", - "xbob/learn/misc/blitz_numpy.cpp", "xbob/learn/misc/empca_trainer.cpp", "xbob/learn/misc/gabor.cpp", "xbob/learn/misc/gaussian.cpp", @@ -65,16 +66,21 @@ setup( "xbob/learn/misc/jfa_trainer.cpp", "xbob/learn/misc/kmeans.cpp", "xbob/learn/misc/kmeans_trainer.cpp", + "xbob/learn/misc/machine.cpp", "xbob/learn/misc/linearscoring.cpp", - "xbob/learn/misc/main.cpp", - "xbob/learn/misc/ndarray.cpp", - "xbob/learn/misc/ndarray_numpy.cpp", "xbob/learn/misc/plda.cpp", "xbob/learn/misc/plda_trainer.cpp", - "xbob/learn/misc/tinyvector.cpp", "xbob/learn/misc/wiener.cpp", "xbob/learn/misc/wiener_trainer.cpp", "xbob/learn/misc/ztnorm.cpp", + + # external requirements as boost::python bindings + "xbob/learn/misc/GaborWaveletTransform.cpp", + "xbob/learn/misc/blitz_numpy.cpp", + "xbob/learn/misc/ndarray.cpp", + "xbob/learn/misc/ndarray_numpy.cpp", + "xbob/learn/misc/tinyvector.cpp", + "xbob/learn/misc/main.cpp", ], packages = packages, diff --git a/xbob/learn/misc/GaborWaveletTransform.cpp b/xbob/learn/misc/GaborWaveletTransform.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1afb09ccd0cf238edf6fb06113c1b03540a522b --- /dev/null +++ b/xbob/learn/misc/GaborWaveletTransform.cpp @@ -0,0 +1,314 @@ +/** + * @author Manuel Guenther <Manuel.Guenther@idiap.ch> + * @date 2012-02-27 + * + * @brief Binds the Gabor wavelet transform + * + * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + */ + +#include "ndarray.h" + +#include <bob/core/array_type.h> + +#include <bob/ip/GaborWaveletTransform.h> +#include <bob/sp/FFT2D.h> +#include <blitz/array.h> + +#include "bob/core/cast.h" +#include "bob/ip/color.h" + +template <class T> +static inline const blitz::Array<std::complex<double>,2> complex_cast (bob::python::const_ndarray input){ + blitz::Array<T,2> gray(input.type().shape[1],input.type().shape[2]); + bob::ip::rgb_to_gray(input.bz<T,3>(), gray); + return bob::core::array::cast<std::complex<double> >(gray); +} + +static inline const blitz::Array<std::complex<double>, 2> convert_image(bob::python::const_ndarray input){ + if (input.type().nd == 3){ + // perform color type conversion + switch (input.type().dtype){ + case bob::core::array::t_uint8: return complex_cast<uint8_t>(input); + case bob::core::array::t_uint16: return complex_cast<uint16_t>(input); + case bob::core::array::t_float64: return complex_cast<double>(input); + default: throw std::runtime_error("unsupported input data type"); + } + } else { + switch (input.type().dtype){ + case bob::core::array::t_uint8: return bob::core::array::cast<std::complex<double> >(input.bz<uint8_t,2>()); + case bob::core::array::t_uint16: return bob::core::array::cast<std::complex<double> >(input.bz<uint16_t,2>()); + case bob::core::array::t_float64: return bob::core::array::cast<std::complex<double> >(input.bz<double,2>()); + case bob::core::array::t_complex128: return input.bz<std::complex<double>,2>(); + default: throw std::runtime_error("unsupported input data type"); + } + } +} + +static inline void transform (bob::ip::GaborKernel& kernel, blitz::Array<std::complex<double>,2>& input, blitz::Array<std::complex<double>,2>& output){ + // perform fft on input image + bob::sp::FFT2D fft(input.extent(0), input.extent(1)); + blitz::Array<std::complex<double>,2> tmp(input.shape()); + fft(input, output); + + // apply the kernel in frequency domain + kernel.transform(output, tmp); + + // perform ifft on the result + bob::sp::IFFT2D ifft(output.extent(0), output.extent(1)); + ifft(tmp, output); +} + +static void gabor_wavelet_transform_1 (bob::ip::GaborKernel& kernel, bob::python::const_ndarray input_image, bob::python::ndarray output_image){ + // convert input image into complex type + blitz::Array<std::complex<double>,2> input = convert_image(input_image); + // cast output image to complex type + blitz::Array<std::complex<double>,2> output = output_image.bz<std::complex<double>,2>(); + // transform input to output + transform(kernel, input, output); +} + +static blitz::Array<std::complex<double>,2> gabor_wavelet_transform_2 (bob::ip::GaborKernel& kernel, bob::python::const_ndarray input_image){ + // convert input ndarray to complex blitz array + blitz::Array<std::complex<double>,2> input = convert_image(input_image); + // allocate output array + blitz::Array<std::complex<double>,2> output(input.extent(0), input.extent(1)); + + // transform input to output + transform(kernel, input, output); + + // return the nd array + return output; +} + + +static blitz::Array<std::complex<double>,3> empty_trafo_image (bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image){ + int index = input_image.type().nd-2; + assert(index >= 0); + return blitz::Array<std::complex<double>,3>(gwt.numberOfKernels(), input_image.type().shape[index], input_image.type().shape[index+1]); +} + +static void perform_gwt_1 (bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image, bob::python::ndarray output_trafo_image){ + const blitz::Array<std::complex<double>,2>& image = convert_image(input_image); + blitz::Array<std::complex<double>,3> trafo_image = output_trafo_image.bz<std::complex<double>,3>(); + gwt.performGWT(image, trafo_image); +} + +static blitz::Array<std::complex<double>,3> perform_gwt_2 (bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image){ + const blitz::Array<std::complex<double>,2>& image = convert_image(input_image); + blitz::Array<std::complex<double>,3> trafo_image(gwt.numberOfKernels(), image.shape()[0], image.shape()[1]); + gwt.performGWT(image, trafo_image); + return trafo_image; +} + +static bob::python::ndarray empty_jet_image(bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image, bool include_phases){ + const blitz::Array<std::complex<double>,2>& image = convert_image(input_image); + if (include_phases) + return bob::python::ndarray (bob::core::array::t_float64, image.extent(0), image.extent(1), 2, (int)gwt.numberOfKernels()); + else + return bob::python::ndarray (bob::core::array::t_float64, image.extent(0), image.extent(1), (int)gwt.numberOfKernels()); +} + +static void compute_jets_1(bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image, bob::python::ndarray output_jet_image, bool normalized){ + const blitz::Array<std::complex<double>,2>& image = convert_image(input_image); + + if (output_jet_image.type().nd == 3){ + // compute jet image with absolute values only + blitz::Array<double,3> jet_image = output_jet_image.bz<double,3>(); + gwt.computeJetImage(image, jet_image, normalized); + } else if (output_jet_image.type().nd == 4){ + blitz::Array<double,4> jet_image = output_jet_image.bz<double,4>(); + gwt.computeJetImage(image, jet_image, normalized); + } else { + boost::format m("parameter `output_jet_image' has an unexpected shape: %s"); + m % output_jet_image.type().str(); + throw std::runtime_error(m.str()); + } +} + +static bob::python::ndarray compute_jets_2(bob::ip::GaborWaveletTransform& gwt, bob::python::const_ndarray input_image, bool include_phases, bool normalized){ + bob::python::ndarray output_jet_image = empty_jet_image(gwt, input_image, include_phases); + compute_jets_1(gwt, input_image, output_jet_image, normalized); + return output_jet_image; +} + + +static void normalize_gabor_jet(bob::python::ndarray gabor_jet){ + if (gabor_jet.type().nd == 1){ + blitz::Array<double,1> jet(gabor_jet.bz<double,1>()); + bob::ip::normalizeGaborJet(jet); + } else if (gabor_jet.type().nd == 2){ + blitz::Array<double,2> jet(gabor_jet.bz<double,2>()); + bob::ip::normalizeGaborJet(jet); + } else { + boost::format m("parameter `gabor_jet' has an unexpected shape: %s"); + m % gabor_jet.type().str(); + throw std::runtime_error(m.str()); + } +} + +void bind_ip_gabor_wavelet_transform() { + // bind Gabor Kernel class + boost::python::class_<bob::ip::GaborKernel, boost::shared_ptr<bob::ip::GaborKernel> >( + "GaborKernel", + "This class can be used to filter an image with a single Gabor wavelet.", + boost::python::no_init + ) + + .def( + boost::python::init< const blitz::TinyVector<int,2>&, const blitz::TinyVector<double,2>&, boost::python::optional <const double, const double, const bool, const double> >( + ( + boost::python::arg("self"), + boost::python::arg("resolution"), + boost::python::arg("wavelet_frequency"), + boost::python::arg("sigma") = 2. * M_PI, + boost::python::arg("pow_of_k") = 0., + boost::python::arg("dc_free") = true, + boost::python::arg("epsilon") = 1e-10 + ), + "Initializes the Gabor wavelet of the given wavelet frequency to be used as a filter for the given image resolution. The optional parameters can be changed, but have useful default values." + ) + ) + + .def(boost::python::init<bob::ip::GaborKernel&>((boost::python::arg("self"), boost::python::arg("other")))) + .def(boost::python::self == boost::python::self) + .def(boost::python::self != boost::python::self) + + .def( + "__call__", + &gabor_wavelet_transform_1, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("output_image")), + "This function Gabor-filters the given input_image, which can be of any type, to the output image. The output image needs to have the same resolution as the input image and must be of complex type." + ) + + .def( + "__call__", + &gabor_wavelet_transform_2, + (boost::python::arg("self"), boost::python::arg("input_image")), + "This function Gabor-filters the given input_image, which can be of any type. The output image is of complex type. It will be automatically generated and returned." + ); + + + // declare GWT class + boost::python::class_<bob::ip::GaborWaveletTransform, boost::shared_ptr<bob::ip::GaborWaveletTransform> >( + "GaborWaveletTransform", + "This class can be used to perform a Gabor wavelet transform from one image to an image of (normalized) Gabor jets or to a complex-valued multi-layer trafo image.", + boost::python::no_init + ) + + .def( + boost::python::init<boost::python::optional<int,int,double,double,double,double,bool> >( + ( + boost::python::arg("self"), + boost::python::arg("number_of_scales") = 5, + boost::python::arg("number_of_angles") = 8, + boost::python::arg("sigma") = 2. * M_PI, + boost::python::arg("k_max") = M_PI / 2., + boost::python::arg("k_fac") = 1./sqrt(2.), + boost::python::arg("pow_of_k") = 0., + boost::python::arg("dc_free") = true + ), + "Initializes the Gabor wavelet transform by generating Gabor wavelets in number_of_scales different frequencies and number_of_angles different directions. The remaining parameters are parameters of the Gabor wavelets to be generated. " + ) + ) + + .def(boost::python::init<bob::ip::GaborWaveletTransform&>((boost::python::arg("self"), boost::python::arg("other")))) + .def(boost::python::init<bob::io::HDF5File&>((boost::python::arg("self"), boost::python::arg("config")))) + .def(boost::python::self == boost::python::self) + .def(boost::python::self != boost::python::self) + + .def( + "save", + &bob::ip::GaborWaveletTransform::save, + (boost::python::arg("self"), boost::python::arg("config")), + "Saves the parameterization of this Gabor wavelet transform to HDF5 file." + ) + + .def( + "load", + &bob::ip::GaborWaveletTransform::load, + (boost::python::arg("self"), boost::python::arg("config")), + "Loads the parameterization of this Gabor wavelet transform from HDF5 file." + ) + + .add_property( + "number_of_kernels", + &bob::ip::GaborWaveletTransform::numberOfKernels, + "The number of Gabor wavelets (i.e. number of directions times number of scales, i.e. the length of a Gabor jet, i.e. the number of layers of the trafo image) used in this Gabor wavelet transform." + ) + + .add_property( + "number_of_scales", + &bob::ip::GaborWaveletTransform::numberOfScales, + "The number of scales that this Gabor wavelet family holds." + ) + + .add_property( + "number_of_directions", + &bob::ip::GaborWaveletTransform::numberOfDirections, + "The number of directions that this Gabor wavelet family holds." + ) + + .def( + "empty_trafo_image", + &empty_trafo_image, + (boost::python::arg("self"), boost::python::arg("input_image")), + "This function creates an empty trafo image for the given input image. Use this function to generate the trafo image in the correct size and with the correct data type. In case you have to transform multiple images of the same size, this trafo image can be reused." + ) + + .def( + "perform_gwt", + &perform_gwt_1, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("output_trafo_image")), + "Performs a Gabor wavelet transform and fills the given Gabor wavelet transformed image (output_trafo_image)" + ) + + .def( + "perform_gwt", + &perform_gwt_2, + (boost::python::arg("self"), boost::python::arg("input_image")), + "Performs a Gabor wavelet transform and returns a Gabor wavelet transformed image" + ) + + .def( + "__call__", + &perform_gwt_1, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("output_trafo_image")), + "Performs a Gabor wavelet transform and fills the given Gabor wavelet transformed image (output_trafo_image)" + ) + + .def( + "__call__", + &perform_gwt_2, + (boost::python::arg("self"), boost::python::arg("input_image")), + "Performs a Gabor wavelet transform and returns a Gabor wavelet transformed image" + ) + + .def( + "empty_jet_image", + &empty_jet_image, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("include_phases")=true), + "This function creates an empty jet image (with or without Gabor phases) for the given input image. Use this function to generate the jet image in the correct size and with the correct data type. In case you have to transform multiple images of the same size, this jet image can be reused." + ) + + .def( + "compute_jets", + &compute_jets_1, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("output_jet_image"), boost::python::arg("normalized")=true), + "Performs a Gabor wavelet transform and fills given image of Gabor jets. If the normalized parameter is set to True (the default), the absolute parts of the Gabor jets are normalized to unit Euclidean length." + ) + + .def( + "compute_jets", + &compute_jets_2, + (boost::python::arg("self"), boost::python::arg("input_image"), boost::python::arg("include_phases")=true, boost::python::arg("normalized")=true), + "Performs a Gabor wavelet transform and returns the image of Gabor jets, with or without Gabor phases. If the normalized parameter is set to True (the default), the absolute parts of the Gabor jets are normalized to unit Euclidean length." + ); + + boost::python::def( + "normalize_gabor_jet", + &normalize_gabor_jet, + (boost::python::arg("gabor_jet")), + "Normalizes the Gabor jet (with or without phase) to unit Euclidean length." + ); +} diff --git a/xbob/learn/misc/kmeans.cpp b/xbob/learn/misc/kmeans.cpp index beaacb3fc5f384db22f659c69cd8a6ed180a7da9..6ba49f79103f9e5ed85ca18c6bb46fd8fb5fc480 100644 --- a/xbob/learn/misc/kmeans.cpp +++ b/xbob/learn/misc/kmeans.cpp @@ -5,7 +5,7 @@ * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland */ -#include "ndarray" +#include "ndarray.h" #include <bob/machine/KMeansMachine.h> diff --git a/xbob/learn/misc/machine.cpp b/xbob/learn/misc/machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f6f212abdc2f17a2a843f00418eeda086198810 --- /dev/null +++ b/xbob/learn/misc/machine.cpp @@ -0,0 +1,35 @@ +/** + * @author Laurent El Shafey <Laurent.El-Shafey@idiap.ch> + * @date Tue Jul 26 15:11:33 2011 +0200 + * + * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + */ + +#include "ndarray.h" +#include <bob/machine/Machine.h> + +using namespace boost::python; + +static double forward(const bob::machine::Machine<blitz::Array<double,1>, double>& m, + bob::python::const_ndarray input) { + double output; + m.forward(input.bz<double,1>(), output); + return output; +} + +static double forward_(const bob::machine::Machine<blitz::Array<double,1>, double>& m, + bob::python::const_ndarray input) { + double output; + m.forward_(input.bz<double,1>(), output); + return output; +} + +void bind_machine_base() +{ + class_<bob::machine::Machine<blitz::Array<double,1>, double>, boost::noncopyable>("MachineDoubleBase", + "Root class for all Machine<blitz::Array<double,1>, double>", no_init) + .def("__call__", &forward_, (arg("self"), arg("input")), "Executes the machine on the given 1D numpy array of float64, and returns the output. NO CHECK is performed.") + .def("forward", &forward, (arg("self"), arg("input")), "Executes the machine on the given 1D numpy array of float64, and returns the output.") + .def("forward_", &forward_, (arg("self"), arg("input")), "Executes the machine on the given 1D numpy array of float64, and returns the output. NO CHECK is performed.") + ; +} diff --git a/xbob/learn/misc/main.cpp b/xbob/learn/misc/main.cpp index 192b9cf32113c4ff0a27cb9d0611c45c9546f17f..6058c6581190892c04532aac6b41abbbd17a8116 100644 --- a/xbob/learn/misc/main.cpp +++ b/xbob/learn/misc/main.cpp @@ -13,6 +13,7 @@ void bind_core_tinyvector(); void bind_core_ndarray_numpy(); void bind_core_bz_numpy(); +void bind_ip_gabor_wavelet_transform(); /** machine bindings **/ void bind_machine_base(); @@ -48,6 +49,7 @@ BOOST_PYTHON_MODULE(_library) { bind_core_tinyvector(); bind_core_ndarray_numpy(); bind_core_bz_numpy(); + bind_ip_gabor_wavelet_transform(); /** machine bindings **/ bind_machine_base(); @@ -64,7 +66,6 @@ BOOST_PYTHON_MODULE(_library) { bind_machine_wiener(); /** trainer bindings **/ - bind_trainer_gmm(); bind_trainer_kmeans(); bind_trainer_jfa(); diff --git a/xbob/learn/misc/test_bic.py b/xbob/learn/misc/test_bic.py index beb924fa8e20a557bbd93eb8ef6a36b628d72f46..cad8fb742c82a04ae365130b0799e089dd361c7a 100644 --- a/xbob/learn/misc/test_bic.py +++ b/xbob/learn/misc/test_bic.py @@ -3,15 +3,13 @@ # Manuel Guenther <Manuel.Guenther@idiap.ch> # Thu Jun 14 14:45:06 CEST 2012 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Test BIC trainer and machine """ -import os, sys -import unittest -import bob import numpy +from . import BICMachine, BICTrainer def equals(x, y, epsilon): return (abs(x - y) < epsilon).all() @@ -43,8 +41,8 @@ class BICTrainerAndMachineTest(unittest.TestCase): intra_data, extra_data = self.training_data() # train BIC machine - machine = bob.machine.BICMachine() - trainer = bob.trainer.BICTrainer() + machine = BICMachine() + trainer = BICTrainer() # train machine with intrapersonal data only trainer.train(machine, intra_data, intra_data) @@ -64,17 +62,17 @@ class BICTrainerAndMachineTest(unittest.TestCase): intra_data, extra_data = self.training_data() # train BIC machine - trainer = bob.trainer.BICTrainer(2,2) + trainer = BICTrainer(2,2) # The data are chosen such that the third eigenvalue is zero. # Hence, calculating rho (i.e., using the Distance From Feature Space) is impossible - machine = bob.machine.BICMachine(True) + machine = BICMachine(True) def should_raise(): trainer.train(machine, intra_data, intra_data) self.assertRaises(RuntimeError, should_raise) # So, now without rho... - machine = bob.machine.BICMachine(False) + machine = BICMachine(False) # First, train the machine with intrapersonal data only trainer.train(machine, intra_data, intra_data) diff --git a/xbob/learn/misc/test_em.py b/xbob/learn/misc/test_em.py index 50bc8e0d56e51f95c8f3b1091941bcebf2e0dd76..eca6be6b416906474aad993611f35e8938a12861 100644 --- a/xbob/learn/misc/test_em.py +++ b/xbob/learn/misc/test_em.py @@ -7,26 +7,20 @@ """Test trainer package """ -import os, sys import unittest -import bob -import random import numpy -import pkg_resources -def F(f, module=None): - """Returns the test file on the "data" subdirectory""" - if module is None: - return pkg_resources.resource_filename(__name__, os.path.join('data', f)) - return pkg_resources.resource_filename('bob.%s.test' % module, - os.path.join('data', f)) +import xbob.io.base +from xbob.io.base.test_utils import datafile + +from . import KMeansMachine, GMMMachine def loadGMM(): gmm = bob.machine.GMMMachine(2, 2) - gmm.weights = bob.io.load(F('gmm.init_weights.hdf5')) - gmm.means = bob.io.load(F('gmm.init_means.hdf5')) - gmm.variances = bob.io.load(F('gmm.init_variances.hdf5')) + gmm.weights = xbob.io.base.load(datafile('gmm.init_weights.hdf5', __name__)) + gmm.means = xbob.io.base.load(datafile('gmm.init_means.hdf5', __name__)) + gmm.variances = xbob.io.base.load(datafile('gmm.init_variances.hdf5', __name__)) gmm.variance_threshold = numpy.array([0.001, 0.001], 'float64') return gmm @@ -36,215 +30,212 @@ def equals(x, y, epsilon): class MyTrainer1(bob.trainer.KMeansTrainer): """Simple example of python trainer: """ - def __init__(self): - bob.trainer.KMeansTrainer.__init__(self) - + def __init__(): + bob.trainer.KMeansTrainer.__init__() + def train(self, machine, data): a = numpy.ndarray((2, 2), 'float64') a[0, :] = data[1] a[1, :] = data[2] machine.means = a -class GMMTest(unittest.TestCase): - """Performs various trainer tests.""" - - def test01_gmm_ML(self): - - # Trains a GMMMachine with ML_GMMTrainer - - ar = bob.io.load(F("faithful.torch3_f64.hdf5")) - - gmm = loadGMM() - - ml_gmmtrainer = bob.trainer.ML_GMMTrainer(True, True, True) - ml_gmmtrainer.train(gmm, ar) - - #config = bob.io.HDF5File(F('gmm_ML.hdf5", 'w')) - #gmm.save(config) - - gmm_ref = bob.machine.GMMMachine(bob.io.HDF5File(F('gmm_ML.hdf5'))) - gmm_ref_32bit_debug = bob.machine.GMMMachine(bob.io.HDF5File(F('gmm_ML_32bit_debug.hdf5'))) - gmm_ref_32bit_release = bob.machine.GMMMachine(bob.io.HDF5File(F('gmm_ML_32bit_release.hdf5'))) - - self.assertTrue((gmm == gmm_ref) or (gmm == gmm_ref_32bit_release) or (gmm == gmm_ref_32bit_debug)) - - def test02_gmm_ML(self): - - # Trains a GMMMachine with ML_GMMTrainer; compares to an old reference - - ar = bob.io.load(F('dataNormalized.hdf5')) - - # Initialize GMMMachine - gmm = bob.machine.GMMMachine(5, 45) - gmm.means = bob.io.load(F('meansAfterKMeans.hdf5')).astype('float64') - gmm.variances = bob.io.load(F('variancesAfterKMeans.hdf5')).astype('float64') - gmm.weights = numpy.exp(bob.io.load(F('weightsAfterKMeans.hdf5')).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 = bob.trainer.ML_GMMTrainer(True, True, True, prior) - ml_gmmtrainer.max_iterations = max_iter_gmm - ml_gmmtrainer.convergence_threshold = accuracy - - # Run ML - ml_gmmtrainer.train(gmm, ar) - - # Test results - # Load torch3vision reference - meansML_ref = bob.io.load(F('meansAfterML.hdf5')) - variancesML_ref = bob.io.load(F('variancesAfterML.hdf5')) - weightsML_ref = bob.io.load(F('weightsAfterML.hdf5')) - - # Compare to current results - self.assertTrue(equals(gmm.means, meansML_ref, 3e-3)) - self.assertTrue(equals(gmm.variances, variancesML_ref, 3e-3)) - self.assertTrue(equals(gmm.weights, weightsML_ref, 1e-4)) - - def test03_gmm_MAP(self): - - # Train a GMMMachine with MAP_GMMTrainer - - ar = bob.io.load(F('faithful.torch3_f64.hdf5')) - - gmm = bob.machine.GMMMachine(bob.io.HDF5File(F("gmm_ML.hdf5"))) - gmmprior = bob.machine.GMMMachine(bob.io.HDF5File(F("gmm_ML.hdf5"))) - - map_gmmtrainer = bob.trainer.MAP_GMMTrainer(16) - map_gmmtrainer.set_prior_gmm(gmmprior) - map_gmmtrainer.train(gmm, ar) - - #config = bob.io.HDF5File(F('gmm_MAP.hdf5", 'w')) - #gmm.save(config) - - gmm_ref = bob.machine.GMMMachine(bob.io.HDF5File(F('gmm_MAP.hdf5'))) - #gmm_ref_32bit_release = bob.machine.GMMMachine(bob.io.HDF5File(F('gmm_MAP_32bit_release.hdf5'))) - - self.assertTrue((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 test04_gmm_MAP(self): - - # Train a GMMMachine with MAP_GMMTrainer and compare with matlab reference - - map_adapt = bob.trainer.MAP_GMMTrainer(4., True, False, False, 0.) - data = bob.io.load(F('data.hdf5', 'machine')) - data = data.reshape((1, data.shape[0])) # make a 2D array out of it - means = bob.io.load(F('means.hdf5', 'machine')) - variances = bob.io.load(F('variances.hdf5', 'machine')) - weights = bob.io.load(F('weights.hdf5', 'machine')) - - gmm = bob.machine.GMMMachine(2,50) - gmm.means = means - gmm.variances = variances - gmm.weights = weights - - map_adapt.set_prior_gmm(gmm) - - gmm_adapted = bob.machine.GMMMachine(2,50) - gmm_adapted.means = means - gmm_adapted.variances = variances - gmm_adapted.weights = weights - - map_adapt.max_iterations = 1 - print(data.shape) - map_adapt.train(gmm_adapted, data) - - new_means = bob.io.load(F('new_adapted_mean.hdf5')) - - # Compare to matlab reference - self.assertTrue(equals(new_means[0,:], gmm_adapted.means[:,0], 1e-4)) - self.assertTrue(equals(new_means[1,:], gmm_adapted.means[:,1], 1e-4)) - - def test05_gmm_MAP(self): - - # Train a GMMMachine with MAP_GMMTrainer; compares to old reference - - ar = bob.io.load(F('dataforMAP.hdf5')) - - # Initialize GMMMachine - n_gaussians = 5 - n_inputs = 45 - prior_gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) - prior_gmm.means = bob.io.load(F('meansAfterML.hdf5')) - prior_gmm.variances = bob.io.load(F('variancesAfterML.hdf5')) - prior_gmm.weights = bob.io.load(F('weightsAfterML.hdf5')) - - 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 = bob.trainer.MAP_GMMTrainer(relevance_factor, True, False, False, prior) - map_gmmtrainer.max_iterations = max_iter_gmm - map_gmmtrainer.convergence_threshold = accuracy - map_gmmtrainer.set_prior_gmm(prior_gmm) - map_gmmtrainer.set_t3_map(map_factor); - - gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) - gmm.set_variance_thresholds(threshold) - - # Train - map_gmmtrainer.train(gmm, ar) - - # Test results - # Load torch3vision reference - meansMAP_ref = bob.io.load(F('meansAfterMAP.hdf5')) - variancesMAP_ref = bob.io.load(F('variancesAfterMAP.hdf5')) - weightsMAP_ref = bob.io.load(F('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 - self.assertTrue(equals(gmm.means, meansMAP_ref, 2e-1)) - self.assertTrue(equals(gmm.variances, variancesMAP_ref, 1e-4)) - self.assertTrue(equals(gmm.weights, weightsMAP_ref, 1e-4)) - - def test06_gmm_test(self): - - # Tests a GMMMachine by computing scores against a model and compare to - # an old reference - - ar = bob.io.load(F('dataforMAP.hdf5')) - - # Initialize GMMMachine - n_gaussians = 5 - n_inputs = 45 - gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) - gmm.means = bob.io.load(F('meansAfterML.hdf5')) - gmm.variances = bob.io.load(F('variancesAfterML.hdf5')) - gmm.weights = bob.io.load(F('weightsAfterML.hdf5')) - - 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.forward(v) - score /= len(ar) - - # Compare current results to torch3vision - self.assertTrue(abs(score-score_mean_ref)/score_mean_ref<1e-4) - - def test07_custom_trainer(self): - - # Custom python trainer - - ar = bob.io.load(F("faithful.torch3_f64.hdf5")) - - mytrainer = MyTrainer1() - - machine = bob.machine.KMeansMachine(2, 2) - mytrainer.train(machine, ar) - - for i in range(0, 2): - self.assertTrue((ar[i+1] == machine.means[i, :]).all()) +def test_gmm_ML_1(): + + # Trains a GMMMachine with ML_GMMTrainer + + ar = xbob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__)) + + gmm = loadGMM() + + ml_gmmtrainer = bob.trainer.ML_GMMTrainer(True, True, True) + ml_gmmtrainer.train(gmm, ar) + + #config = xbob.io.base.HDF5File(datafile('gmm_ML.hdf5", __name__), 'w') + #gmm.save(config) + + gmm_ref = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile('gmm_ML.hdf5', __name__))) + gmm_ref_32bit_debug = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile('gmm_ML_32bit_debug.hdf5', __name__))) + gmm_ref_32bit_release = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile('gmm_ML_32bit_release.hdf5', __name__))) + + assert (gmm == gmm_ref) or (gmm == gmm_ref_32bit_release) or (gmm == gmm_ref_32bit_debug) + +def test_gmm_ML_2(): + + # Trains a GMMMachine with ML_GMMTrainer; compares to an old reference + + ar = xbob.io.base.load(datafile('dataNormalized.hdf5', __name__)) + + # Initialize GMMMachine + gmm = bob.machine.GMMMachine(5, 45) + gmm.means = xbob.io.base.load(datafile('meansAfterKMeans.hdf5', __name__)).astype('float64') + gmm.variances = xbob.io.base.load(datafile('variancesAfterKMeans.hdf5', __name__)).astype('float64') + gmm.weights = numpy.exp(xbob.io.base.load(datafile('weightsAfterKMeans.hdf5', __name__)).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 = bob.trainer.ML_GMMTrainer(True, True, True, prior) + ml_gmmtrainer.max_iterations = max_iter_gmm + ml_gmmtrainer.convergence_threshold = accuracy + + # Run ML + ml_gmmtrainer.train(gmm, ar) + + # Test results + # Load torch3vision reference + meansML_ref = xbob.io.base.load(datafile('meansAfterML.hdf5', __name__)) + variancesML_ref = xbob.io.base.load(datafile('variancesAfterML.hdf5', __name__)) + weightsML_ref = xbob.io.base.load(datafile('weightsAfterML.hdf5', __name__)) + + # 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 = xbob.io.base.load(datafile('faithful.torch3_f64.hdf5', __name__)) + + gmm = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__))) + gmmprior = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile("gmm_ML.hdf5", __name__))) + + map_gmmtrainer = bob.trainer.MAP_GMMTrainer(16) + map_gmmtrainer.set_prior_gmm(gmmprior) + map_gmmtrainer.train(gmm, ar) + + #config = xbob.io.base.HDF5File(datafile('gmm_MAP.hdf5", 'w', __name__)) + #gmm.save(config) + + gmm_ref = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile('gmm_MAP.hdf5', __name__))) + #gmm_ref_32bit_release = bob.machine.GMMMachine(xbob.io.base.HDF5File(datafile('gmm_MAP_32bit_release.hdf5', __name__))) + + 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 + + map_adapt = bob.trainer.MAP_GMMTrainer(4., True, False, False, 0.) + data = xbob.io.base.load(datafile('data.hdf5', 'machine', __name__)) + data = data.reshape((1, data.shape[0])) # make a 2D array out of it + means = xbob.io.base.load(datafile('means.hdf5', 'machine', __name__)) + variances = xbob.io.base.load(datafile('variances.hdf5', 'machine', __name__)) + weights = xbob.io.base.load(datafile('weights.hdf5', 'machine', __name__)) + + gmm = bob.machine.GMMMachine(2,50) + gmm.means = means + gmm.variances = variances + gmm.weights = weights + + map_adapt.set_prior_gmm(gmm) + + gmm_adapted = bob.machine.GMMMachine(2,50) + gmm_adapted.means = means + gmm_adapted.variances = variances + gmm_adapted.weights = weights + + map_adapt.max_iterations = 1 + print(data.shape) + map_adapt.train(gmm_adapted, data) + + new_means = xbob.io.base.load(datafile('new_adapted_mean.hdf5', __name__)) + + # 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 = xbob.io.base.load(datafile('dataforMAP.hdf5', __name__)) + + # Initialize GMMMachine + n_gaussians = 5 + n_inputs = 45 + prior_gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) + prior_gmm.means = xbob.io.base.load(datafile('meansAfterML.hdf5', __name__)) + prior_gmm.variances = xbob.io.base.load(datafile('variancesAfterML.hdf5', __name__)) + prior_gmm.weights = xbob.io.base.load(datafile('weightsAfterML.hdf5', __name__)) + + 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 = bob.trainer.MAP_GMMTrainer(relevance_factor, True, False, False, prior) + map_gmmtrainer.max_iterations = max_iter_gmm + map_gmmtrainer.convergence_threshold = accuracy + map_gmmtrainer.set_prior_gmm(prior_gmm) + map_gmmtrainer.set_t3_map(map_factor); + + gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) + gmm.set_variance_thresholds(threshold) + + # Train + map_gmmtrainer.train(gmm, ar) + + # Test results + # Load torch3vision reference + meansMAP_ref = xbob.io.base.load(datafile('meansAfterMAP.hdf5', __name__)) + variancesMAP_ref = xbob.io.base.load(datafile('variancesAfterMAP.hdf5', __name__)) + weightsMAP_ref = xbob.io.base.load(datafile('weightsAfterMAP.hdf5', __name__)) + + # 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 = xbob.io.base.load(datafile('dataforMAP.hdf5', __name__)) + + # Initialize GMMMachine + n_gaussians = 5 + n_inputs = 45 + gmm = bob.machine.GMMMachine(n_gaussians, n_inputs) + gmm.means = xbob.io.base.load(datafile('meansAfterML.hdf5', __name__)) + gmm.variances = xbob.io.base.load(datafile('variancesAfterML.hdf5', __name__)) + gmm.weights = xbob.io.base.load(datafile('weightsAfterML.hdf5', __name__)) + + 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.forward(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 = xbob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__)) + + mytrainer = MyTrainer1() + + machine = bob.machine.KMeansMachine(2, 2) + mytrainer.train(machine, ar) + + for i in range(0, 2): + assert (ar[i+1] == machine.means[i, :]).all() diff --git a/xbob/learn/misc/test_gaussian.py b/xbob/learn/misc/test_gaussian.py index c4b8cc7b46cabe9b692eeecfff8e2b6dc755c7fd..95e18b0e0722cbd00280d050e4466999955d61d1 100644 --- a/xbob/learn/misc/test_gaussian.py +++ b/xbob/learn/misc/test_gaussian.py @@ -8,83 +8,82 @@ """Tests the Gaussian machine """ -import os, sys -import unittest -import bob +import os import numpy import tempfile +import xbob.io.base + +from . import Gaussian + def equals(x, y, epsilon): return (abs(x - y) < epsilon) -class GaussianMachineTest(unittest.TestCase): - """Performs various Gaussian machine tests.""" - - def test01_GaussianNormal(self): - # Test the likelihood computation of a simple normal Gaussian - gaussian = bob.machine.Gaussian(2) - # By default, initialized with zero mean and unit variance - logLH = gaussian.log_likelihood(numpy.array([0.4, 0.2], 'float64')) - self.assertTrue( equals(logLH, -1.93787706641, 1e-10)) +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 test02_GaussianMachine(self): - # Test a GaussianMachine more thoroughly +def test_GaussianMachine(): + # Test a GaussianMachine more thoroughly - # Initializes a Gaussian with zero mean and unit variance - g = bob.machine.Gaussian(3) - self.assertTrue( (g.mean == 0.0).all() ) - self.assertTrue( (g.variance == 1.0).all() ) - self.assertTrue( g.dim_d == 3 ) + # 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.dim_d == 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) - self.assertTrue( (g.mean == mean).all() ) - self.assertTrue( (g.variance == variance).all() ) - self.assertTrue( (g.variance_thresholds == 0.0005).all() ) + # 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.HDF5File(filename, 'w')) - g_loaded = bob.machine.Gaussian(bob.io.HDF5File(filename)) - self.assertTrue( g == g_loaded ) - self.assertFalse( g != g_loaded ) - self.assertTrue(g.is_similar_to(g_loaded)) - # Make them different - g_loaded.set_variance_thresholds(0.001) - self.assertFalse( g == g_loaded ) - self.assertTrue( g != g_loaded ) + # Save and read from file + filename = str(tempfile.mkstemp(".hdf5")[1]) + g.save(xbob.io.base.HDF5File(filename, 'w')) + g_loaded = Gaussian(xbob.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 - self.assertTrue( equals(g.log_likelihood(sample1), ref1, eps) ) - self.assertTrue( equals(g.forward(sample1), ref1, eps) ) - self.assertTrue( equals(g.log_likelihood(sample2), ref2, eps) ) - self.assertTrue( equals(g.forward(sample2), ref2, eps) ) - self.assertTrue( equals(g.log_likelihood(sample3), ref3, eps) ) - self.assertTrue( equals(g.forward(sample3), ref3, eps) ) + # 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.forward(sample1), ref1, eps) + assert equals(g.log_likelihood(sample2), ref2, eps) + assert equals(g.forward(sample2), ref2, eps) + assert equals(g.log_likelihood(sample3), ref3, eps) + assert equals(g.forward(sample3), ref3, eps) - # Check resize and assignment - g.shape = (6,) - self.assertTrue( g.shape == (6,) ) - g.resize(5) - self.assertTrue( g.dim_d == 5 ) - g2 = bob.machine.Gaussian() - g2 = g - self.assertTrue( g == g2 ) - self.assertFalse( g != g2 ) - g3 = bob.machine.Gaussian(g) - self.assertTrue( g == g3 ) - self.assertFalse( g != g3 ) + # Check resize and assignment + g.shape = (6,) + assert g.shape == (6,) + g.resize(5) + assert g.dim_d == 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) + # Clean-up + os.unlink(filename) diff --git a/xbob/learn/misc/test_gmm.py b/xbob/learn/misc/test_gmm.py index a9517236710703edd26ad14ebf0b935da9aac013..2c95483379e7bacd541e3f7e29d4b462b03e0330 100644 --- a/xbob/learn/misc/test_gmm.py +++ b/xbob/learn/misc/test_gmm.py @@ -3,217 +3,212 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Thu Feb 16 17:57:10 2012 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the GMM machine and the GMMStats container """ -import os, sys -import unittest -import bob +import os import numpy import tempfile -import pkg_resources - -def F(f): - """Returns the test file on the "data" subdirectory""" - return pkg_resources.resource_filename(__name__, os.path.join('data', f)) - -class GMMMachineTest(unittest.TestCase): - """Performs various GMM machine-related tests.""" - - def test01_GMMStats(self): - # Test a GMMStats - - # Initializes a GMMStats - gs = bob.machine.GMMStats(2,3) - 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') - gs.log_likelihood = log_likelihood - gs.t = T - gs.n = n - gs.sum_px = sumpx - gs.sum_pxx = sumpxx - self.assertTrue( gs.log_likelihood == log_likelihood ) - self.assertTrue( gs.t == T ) - self.assertTrue( (gs.n == n).all() ) - self.assertTrue( (gs.sum_px == sumpx).all() ) - self.assertTrue( (gs.sum_pxx == sumpxx).all() ) - - # Saves and reads from file - filename = str(tempfile.mkstemp(".hdf5")[1]) - gs.save(bob.io.HDF5File(filename, 'w')) - gs_loaded = bob.machine.GMMStats(bob.io.HDF5File(filename)) - self.assertTrue( gs == gs_loaded ) - self.assertFalse( gs != gs_loaded ) - self.assertTrue( gs.is_similar_to(gs_loaded) ) - # Makes them different - gs_loaded.t = 58 - self.assertFalse( gs == gs_loaded ) - self.assertTrue( gs != gs_loaded ) - self.assertFalse( gs.is_similar_to(gs_loaded) ) - # Accumulates from another GMMStats - gs2 = bob.machine.GMMStats(2,3) - gs2.log_likelihood = log_likelihood - gs2.t = T - gs2.n = n - gs2.sum_px = sumpx - gs2.sum_pxx = sumpxx - gs2 += gs - eps = 1e-8 - self.assertTrue( gs2.log_likelihood == 2*log_likelihood ) - self.assertTrue( gs2.t == 2*T ) - self.assertTrue( numpy.allclose(gs2.n, 2*n, eps) ) - self.assertTrue( numpy.allclose(gs2.sum_px, 2*sumpx, eps) ) - self.assertTrue( numpy.allclose(gs2.sum_pxx, 2*sumpxx, eps) ) - - # Reinit and checks for zeros - gs_loaded.init() - self.assertTrue( gs_loaded.log_likelihood == 0 ) - self.assertTrue( gs_loaded.t == 0 ) - self.assertTrue( (gs_loaded.n == 0).all() ) - self.assertTrue( (gs_loaded.sum_px == 0).all() ) - self.assertTrue( (gs_loaded.sum_pxx == 0).all() ) - # Resize and checks size - gs_loaded.resize(4,5) - self.assertTrue( gs_loaded.sum_px.shape[0] == 4) - self.assertTrue( gs_loaded.sum_px.shape[1] == 5) - - # Clean-up - os.unlink(filename) - - def test02_GMMMachine(self): - # 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') - - # Initializes a GMMMachine - gmm = bob.machine.GMMMachine(2,3) - # Sets the weights, means, variances and varianceThresholds and - # Checks correctness - gmm.weights = weights - gmm.means = means - gmm.variances = variances - gmm.varianceThresholds = varianceThresholds - self.assertTrue( gmm.dim_c == 2 ) - self.assertTrue( gmm.dim_d == 3 ) - self.assertTrue( (gmm.weights == weights).all() ) - self.assertTrue( (gmm.means == means).all() ) - self.assertTrue( (gmm.variances == variances).all() ) - self.assertTrue( (gmm.variance_thresholds == varianceThresholds).all() ) - - # Checks supervector-like accesses - self.assertTrue( (gmm.mean_supervector == means.reshape(means.size)).all() ) - self.assertTrue( (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') - gmm.mean_supervector = newMeans.reshape(newMeans.size) - gmm.variance_supervector = newVariances.reshape(newVariances.size) - self.assertTrue( (gmm.mean_supervector == newMeans.reshape(newMeans.size)).all() ) - self.assertTrue( (gmm.variance_supervector == newVariances.reshape(newVariances.size)).all() ) - - # Checks particular varianceThresholds-related methods - varianceThresholds1D = numpy.array([0.3, 1, 0.5], 'float64') - gmm.set_variance_thresholds(varianceThresholds1D) - self.assertTrue( (gmm.variance_thresholds[0,:] == varianceThresholds1D).all() ) - self.assertTrue( (gmm.variance_thresholds[1,:] == varianceThresholds1D).all() ) - gmm.set_variance_thresholds(0.005) - self.assertTrue( (gmm.variance_thresholds == 0.005).all() ) - - # Checks Gaussians access - self.assertTrue( (gmm.update_gaussian(0).mean == newMeans[0,:]).all() ) - self.assertTrue( (gmm.update_gaussian(1).mean == newMeans[1,:]).all() ) - self.assertTrue( (gmm.update_gaussian(0).variance == newVariances[0,:]).all() ) - self.assertTrue( (gmm.update_gaussian(1).variance == newVariances[1,:]).all() ) - - # Checks resize - gmm.shape = (5,6) - self.assertTrue( gmm.shape == (5,6) ) - gmm.resize(4,5) - self.assertTrue( gmm.dim_c == 4 ) - self.assertTrue( gmm.dim_d == 5 ) - - # Checks comparison - gmm2 = bob.machine.GMMMachine(gmm) - gmm3 = bob.machine.GMMMachine(2,3) - gmm3.weights = weights2 - gmm3.means = means - gmm3.variances = variances - gmm3.varianceThresholds = varianceThresholds - gmm4 = bob.machine.GMMMachine(2,3) - gmm4.weights = weights - gmm4.means = means2 - gmm4.variances = variances - gmm4.varianceThresholds = varianceThresholds - gmm5 = bob.machine.GMMMachine(2,3) - gmm5.weights = weights - gmm5.means = means - gmm5.variances = variances2 - gmm5.varianceThresholds = varianceThresholds - gmm6 = bob.machine.GMMMachine(2,3) - gmm6.weights = weights - gmm6.means = means - gmm6.variances = variances - gmm6.varianceThresholds = varianceThresholds2 - - self.assertTrue( gmm == gmm2) - self.assertFalse( gmm != gmm2) - self.assertTrue( gmm.is_similar_to(gmm2) ) - self.assertTrue( gmm != gmm3) - self.assertFalse( gmm == gmm3) - self.assertFalse( gmm.is_similar_to(gmm3) ) - self.assertTrue( gmm != gmm4) - self.assertFalse( gmm == gmm4) - self.assertFalse( gmm.is_similar_to(gmm4) ) - self.assertTrue( gmm != gmm5) - self.assertFalse( gmm == gmm5) - self.assertFalse( gmm.is_similar_to(gmm5) ) - self.assertTrue( gmm != gmm6) - self.assertFalse( gmm == gmm6) - self.assertFalse( gmm.is_similar_to(gmm6) ) - - def test03_GMMMachine(self): - # Test a GMMMachine (statistics) - - arrayset = bob.io.load(F("faithful.torch3_f64.hdf5")) - gmm = bob.machine.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') - - stats = bob.machine.GMMStats(2, 2) - gmm.acc_statistics(arrayset, stats) - - stats_ref = bob.machine.GMMStats(bob.io.HDF5File(F("stats.hdf5"))) - - self.assertTrue(stats.t == stats_ref.t) - self.assertTrue( numpy.allclose(stats.n, stats_ref.n, atol=1e-10) ) - #self.assertTrue( numpy.array_equal(stats.sumPx, stats_ref.sumPx) ) - #Note AA: precision error above - self.assertTrue ( numpy.allclose(stats.sum_px, stats_ref.sum_px, atol=1e-10) ) - self.assertTrue( numpy.allclose(stats.sum_pxx, stats_ref.sum_pxx, atol=1e-10) ) - - def test04_GMMMachine(self): - # Test a GMMMachine (log-likelihood computation) - - data = bob.io.load(F('data.hdf5')) - gmm = bob.machine.GMMMachine(2, 50) - gmm.weights = bob.io.load(F('weights.hdf5')) - gmm.means = bob.io.load(F('means.hdf5')) - gmm.variances = bob.io.load(F('variances.hdf5')) - - # Compare the log-likelihood with the one obtained using Chris Matlab - # implementation - matlab_ll_ref = -2.361583051672024e+02 - self.assertTrue( abs(gmm(data) - matlab_ll_ref) < 1e-10) + +import xbob.io.base +from xbob.io.base.test_utils import datafile + +from . import GMMStats, GMMMachine + +def test_GMMStats(): + # Test a GMMStats + + # Initializes a GMMStats + gs = GMMStats(2,3) + 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') + 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() + + # Saves and reads from file + filename = str(tempfile.mkstemp(".hdf5")[1]) + gs.save(xbob.io.base.HDF5File(filename, 'w')) + gs_loaded = GMMStats(xbob.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 + # Accumulates from another GMMStats + gs2 = GMMStats(2,3) + gs2.log_likelihood = log_likelihood + gs2.t = T + gs2.n = n + gs2.sum_px = sumpx + gs2.sum_pxx = sumpxx + 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() + # Resize and checks size + gs_loaded.resize(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(): + # 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') + + # Initializes a GMMMachine + gmm = GMMMachine(2,3) + # Sets the weights, means, variances and varianceThresholds and + # Checks correctness + gmm.weights = weights + gmm.means = means + gmm.variances = variances + gmm.varianceThresholds = varianceThresholds + assert gmm.dim_c == 2 + assert gmm.dim_d == 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') + gmm.mean_supervector = newMeans.reshape(newMeans.size) + gmm.variance_supervector = newVariances.reshape(newVariances.size) + assert (gmm.mean_supervector == newMeans.reshape(newMeans.size)).all() + assert (gmm.variance_supervector == newVariances.reshape(newVariances.size)).all() + + # 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() + gmm.set_variance_thresholds(0.005) + assert (gmm.variance_thresholds == 0.005).all() + + # Checks Gaussians access + assert (gmm.update_gaussian(0).mean == newMeans[0,:]).all() + assert (gmm.update_gaussian(1).mean == newMeans[1,:]).all() + assert (gmm.update_gaussian(0).variance == newVariances[0,:]).all() + assert (gmm.update_gaussian(1).variance == newVariances[1,:]).all() + + # Checks resize + gmm.shape = (5,6) + assert gmm.shape == (5,6) + gmm.resize(4,5) + assert gmm.dim_c == 4 + assert gmm.dim_d == 5 + + # Checks comparison + gmm2 = GMMMachine(gmm) + gmm3 = GMMMachine(2,3) + gmm3.weights = weights2 + gmm3.means = means + gmm3.variances = variances + gmm3.varianceThresholds = varianceThresholds + gmm4 = GMMMachine(2,3) + gmm4.weights = weights + gmm4.means = means2 + gmm4.variances = variances + gmm4.varianceThresholds = varianceThresholds + gmm5 = GMMMachine(2,3) + gmm5.weights = weights + gmm5.means = means + gmm5.variances = variances2 + gmm5.varianceThresholds = varianceThresholds + gmm6 = GMMMachine(2,3) + gmm6.weights = weights + gmm6.means = means + gmm6.variances = variances + gmm6.varianceThresholds = varianceThresholds2 + + assert gmm == gmm2 + assert (gmm != gmm2) is False + assert gmm.is_similar_to(gmm2) + assert gmm != gmm3 + assert (gmm == gmm3) is False + assert gmm.is_similar_to(gmm3) is False + assert gmm != gmm4 + assert (gmm == gmm4) is False + assert gmm.is_similar_to(gmm4) is False + assert gmm != gmm5 + assert (gmm == gmm5) is False + assert gmm.is_similar_to(gmm5) is False + assert gmm != gmm6 + assert (gmm == gmm6) is False + assert gmm.is_similar_to(gmm6) is False + +def test_GMMMachine_2(): + # Test a GMMMachine (statistics) + + arrayset = xbob.io.base.load(datafile("faithful.torch3_f64.hdf5", __name__)) + 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') + + stats = GMMStats(2, 2) + gmm.acc_statistics(arrayset, stats) + + stats_ref = GMMStats(xbob.io.base.HDF5File(datafile("stats.hdf5", __name__))) + + 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) + +def test_GMMMachine_3(): + # Test a GMMMachine (log-likelihood computation) + + data = xbob.io.base.load(datafile('data.hdf5', __name__)) + gmm = GMMMachine(2, 50) + gmm.weights = xbob.io.base.load(datafile('weights.hdf5', __name__)) + gmm.means = xbob.io.base.load(datafile('means.hdf5', __name__)) + gmm.variances = xbob.io.base.load(datafile('variances.hdf5', __name__)) + + # 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 diff --git a/xbob/learn/misc/test_ivector.py b/xbob/learn/misc/test_ivector.py index eaa650684ae59c88733ffe1aab61ce0b6bed26b9..f111042df5e5216aa224dabf5a6e8d17f168cb85 100644 --- a/xbob/learn/misc/test_ivector.py +++ b/xbob/learn/misc/test_ivector.py @@ -3,14 +3,17 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Mon Apr 2 11:19:00 2013 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the I-Vector machine """ -import unittest -import bob, numpy, numpy.linalg, numpy.random +import numpy +import numpy.linalg +import numpy.random + +from . import GMMMachine, GMMStats, IVectorMachine ### Test class inspired by an implementation of Chris McCool @@ -53,7 +56,7 @@ class IVectorMachinePy(): def get_ubm(self): return self.m_ubm - + def set_t(self, t): # @warning: no dimensions check self.m_t = t @@ -62,7 +65,7 @@ class IVectorMachinePy(): def get_t(self): return self.m_t - + def set_sigma(self, sigma): # @warning: no dimensions check self.m_sigma = sigma @@ -71,7 +74,7 @@ class IVectorMachinePy(): def get_sigma(self): return self.m_sigma - + def _get_TtSigmaInv_Fnorm(self, N, F): # Initialization @@ -94,12 +97,12 @@ class IVectorMachinePy(): dim_c = self.m_ubm.dim_c dim_d = self.m_ubm.dim_d - TtSigmaInvNT = numpy.eye(self.m_dim_t, dtype=numpy.float64) + 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 forward(self, gmmstats): if self.m_ubm and not (self.m_t == None) and not (self.m_sigma == None): N = gmmstats.n @@ -111,46 +114,43 @@ class IVectorMachinePy(): return numpy.linalg.solve(TtSigmaInvNT, TtSigmaInv_Fnorm) - -class IVectorTests(unittest.TestCase): - - def test01_machine(self): - # Ubm - ubm = bob.machine.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 = bob.machine.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.forward(gs) - self.assertTrue(numpy.allclose(wij_ref, wij, 1e-5)) - - # IVector (C++) - mc = bob.machine.IVectorMachine(ubm, 2) - mc.t = t - mc.sigma = sigma - - wij_ref = numpy.array([-0.04213415, 0.21463343]) # Reference from original Chris implementation - wij = mc.forward(gs) - self.assertTrue(numpy.allclose(wij_ref, wij, 1e-5)) - +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.forward(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.forward(gs) + assert numpy.allclose(wij_ref, wij, 1e-5) diff --git a/xbob/learn/misc/test_ivector_trainer.py b/xbob/learn/misc/test_ivector_trainer.py index 60d72aa7d57651bcc5bb5d14f9cfd3d0f61ea73a..2e047e320a76e81cc31b7ae09c0ef2aa06a5721f 100644 --- a/xbob/learn/misc/test_ivector_trainer.py +++ b/xbob/learn/misc/test_ivector_trainer.py @@ -2,13 +2,16 @@ # vim: set fileencoding=utf-8 : # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the I-Vector trainer """ -import unittest -import bob, numpy, numpy.linalg, numpy.random +import numpy +import numpy.linalg +import numpy.random + +from . import GMMMachine, GMMStats, IVectorMachine, IVectorTrainer ### Test class inspired by an implementation of Chris McCool ### Chris McCool (chris.mccool@nicta.com.au) @@ -137,227 +140,224 @@ class IVectorTrainerPy(): i += 1 -class IVectorTests(unittest.TestCase): - - def test01_trainer_nosigma(self): - # Ubm - ubm = bob.machine.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 = bob.machine.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 = bob.machine.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 = bob.machine.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]: - self.assertTrue(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]: - self.assertTrue(numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.m_acc_Fnorm_Sigma_wij[k], 1e-5)) - self.assertTrue(numpy.allclose(acc_Snorm_ref[it], trainer.m_acc_Snorm, 1e-5)) - self.assertTrue(numpy.allclose(N_ref[it], trainer.m_N, 1e-5)) - - # M-Step - trainer.m_step(m, data) - self.assertTrue(numpy.allclose(t_ref[it], m.t, 1e-5)) - - # C++ implementation - # Machine - m = bob.machine.IVectorMachine(ubm, 2) - - # Initialization - trainer = bob.trainer.IVectorTrainer() - 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]: - self.assertTrue(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]: - self.assertTrue(numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.acc_fnormij_wij[k], 1e-5)) - - # M-Step - trainer.m_step(m, data) - self.assertTrue(numpy.allclose(t_ref[it], m.t, 1e-5)) - - - def test02_trainer_update_sigma(self): - # Ubm - dim_c = 2 - dim_d = 3 - ubm = bob.machine.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 = bob.machine.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 = bob.machine.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 = bob.machine.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]: - self.assertTrue(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]: - self.assertTrue(numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.m_acc_Fnorm_Sigma_wij[k], 1e-5)) - self.assertTrue(numpy.allclose(acc_Snorm_ref[it], trainer.m_acc_Snorm, 1e-5)) - self.assertTrue(numpy.allclose(N_ref[it], trainer.m_N, 1e-5)) - - # M-Step - trainer.m_step(m, data) - self.assertTrue(numpy.allclose(t_ref[it], m.t, 1e-5)) - self.assertTrue(numpy.allclose(sigma_ref[it], m.sigma, 1e-5)) - - - # C++ implementation - # Machine - m = bob.machine.IVectorMachine(ubm, 2) - m.variance_threshold = 1e-5 - - # Initialization - trainer = bob.trainer.IVectorTrainer(update_sigma=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]: - self.assertTrue(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]: - self.assertTrue(numpy.allclose(acc_Fnorm_Sigma_wij_ref[it][k], trainer.acc_fnormij_wij[k], 1e-5)) - self.assertTrue(numpy.allclose(acc_Snorm_ref[it].reshape(dim_c,dim_d), trainer.acc_snormij, 1e-5)) - self.assertTrue(numpy.allclose(N_ref[it], trainer.acc_nij, 1e-5)) - - # M-Step - trainer.m_step(m, data) - self.assertTrue(numpy.allclose(t_ref[it], m.t, 1e-5)) - self.assertTrue(numpy.allclose(sigma_ref[it], m.sigma, 1e-5)) +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, 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.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, data) + assert numpy.allclose(t_ref[it], m.t, 1e-5) + +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, 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.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, data) + assert numpy.allclose(t_ref[it], m.t, 1e-5) + assert numpy.allclose(sigma_ref[it], m.sigma, 1e-5) diff --git a/xbob/learn/misc/test_jfa.py b/xbob/learn/misc/test_jfa.py index a71579c0c6f5f76d7049b334301ddd61adbd5d10..06f219e8d9e06caf870eb791e19621b794f59827 100644 --- a/xbob/learn/misc/test_jfa.py +++ b/xbob/learn/misc/test_jfa.py @@ -3,18 +3,20 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Wed Feb 15 23:24:35 2012 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests on the JFA-based machines """ -import os, sys -import unittest -import math -import bob -import numpy, numpy.linalg +import os +import numpy +import numpy.linalg import tempfile +import xbob.io.base + +from . import GMMMachine, GMMStats, JFABase, ISVBase + def estimate_x(dim_c, dim_d, mean, sigma, U, N, F): # Compute helper values UtSigmaInv = {} @@ -28,10 +30,10 @@ def estimate_x(dim_c, dim_d, mean, sigma, U, N, F): UtSigmaInvU[c] = numpy.dot(UtSigmaInv[c], Uc); # I + (U^{T} \Sigma^-1 N U) - I_UtSigmaInvNU = numpy.eye(dim_ru, dtype=numpy.float64) + 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): @@ -39,336 +41,333 @@ def estimate_x(dim_c, dim_d, mean, sigma, U, N, F): 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)) -class FATest(unittest.TestCase): - """Performs various FA tests.""" - - def test01_JFABase(self): - - # 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 = bob.machine.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 = bob.machine.JFABase(ubm) - self.assertTrue( m.dim_ru == 1) - self.assertTrue( m.dim_rv == 1) - - # Checks for correctness - m.resize(2,2) - m.u = U - m.v = V - m.d = d - self.assertTrue( (m.u == U).all() ) - self.assertTrue( (m.v == V).all() ) - self.assertTrue( (m.d == d).all() ) - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( m.dim_rv == 2) - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.HDF5File(filename, 'w')) - m_loaded = bob.machine.JFABase(bob.io.HDF5File(filename)) - m_loaded.ubm = ubm - self.assertTrue( m == m_loaded ) - self.assertFalse( m != m_loaded ) - self.assertTrue( m.is_similar_to(m_loaded) ) - - # Copy constructor - mc = bob.machine.JFABase(m) - self.assertTrue( m == mc ) - - # Variant - mv = bob.machine.JFABase() - # Checks for correctness - mv.ubm = ubm - mv.resize(2,2) - mv.u = U - mv.v = V - mv.d = d - self.assertTrue( (m.u == U).all() ) - self.assertTrue( (m.v == V).all() ) - self.assertTrue( (m.d == d).all() ) - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( m.dim_rv == 2) - - # Clean-up - os.unlink(filename) - - def test02_ISVBase(self): - - # 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 = bob.machine.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 = bob.machine.ISVBase(ubm) - self.assertTrue( m.dim_ru == 1) - - # Checks for correctness - m.resize(2) - m.u = U - m.d = d - self.assertTrue( (m.u == U).all() ) - self.assertTrue( (m.d == d).all() ) - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.HDF5File(filename, 'w')) - m_loaded = bob.machine.ISVBase(bob.io.HDF5File(filename)) - m_loaded.ubm = ubm - self.assertTrue( m == m_loaded ) - self.assertFalse( m != m_loaded ) - self.assertTrue( m.is_similar_to(m_loaded) ) - - # Copy constructor - mc = bob.machine.ISVBase(m) - self.assertTrue( m == mc ) - - # Variant - mv = bob.machine.ISVBase() - # Checks for correctness - mv.ubm = ubm - mv.resize(2) - mv.u = U - mv.d = d - self.assertTrue( (m.u == U).all() ) - self.assertTrue( (m.d == d).all() ) - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - - # Clean-up - os.unlink(filename) - - def test03_JFAMachine(self): - - # 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 = bob.machine.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 = bob.machine.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 = bob.machine.JFAMachine(base) - m.y = y - m.z = z - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( m.dim_rv == 2) - self.assertTrue( (m.y == y).all() ) - self.assertTrue( (m.z == z).all() ) - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.HDF5File(filename, 'w')) - m_loaded = bob.machine.JFAMachine(bob.io.HDF5File(filename)) - m_loaded.jfa_base = base - self.assertTrue( m == m_loaded ) - self.assertFalse( m != m_loaded ) - self.assertTrue(m.is_similar_to(m_loaded)) - - # Copy constructor - mc = bob.machine.JFAMachine(m) - self.assertTrue( m == mc ) - - # Variant - mv = bob.machine.JFAMachine() - # Checks for correctness - mv.jfa_base = base - m.y = y - m.z = z - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( m.dim_rv == 2) - self.assertTrue( (m.y == y).all() ) - self.assertTrue( (m.z == z).all() ) - - # Defines GMMStats - gs = bob.machine.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.forward(gs) - self.assertTrue( numpy.allclose(m.__x__, x_ref, eps) ) - self.assertTrue( abs(score_ref-score) < eps ) - - # x and Ux - x = numpy.ndarray((2,), numpy.float64) - m.estimate_x(gs, x) - x_py = estimate_x(m.dim_c, m.dim_d, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - self.assertTrue( numpy.allclose(x, x_py, eps)) - - ux = numpy.ndarray((6,), numpy.float64) - m.estimate_ux(gs, ux) - ux_py = estimate_ux(m.dim_c, m.dim_d, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - self.assertTrue( numpy.allclose(ux, ux_py, eps)) - self.assertTrue( numpy.allclose(m.__x__, x, eps) ) - - score = m.forward_ux(gs, ux) - self.assertTrue( abs(score_ref-score) < eps ) - - # Clean-up - os.unlink(filename) - - def test04_ISVMachine(self): - - # 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 = bob.machine.GMMMachine(2,3) - ubm.weights = weights - ubm.means = means - ubm.variances = variances - - # Creates a JFABaseMachine - 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 = bob.machine.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 = bob.machine.ISVMachine(base) - m.z = z - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( (m.z == z).all() ) - - # Saves and loads - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.HDF5File(filename, 'w')) - m_loaded = bob.machine.ISVMachine(bob.io.HDF5File(filename)) - m_loaded.isv_base = base - self.assertTrue( m == m_loaded ) - self.assertFalse( m != m_loaded ) - self.assertTrue(m.is_similar_to(m_loaded)) - - # Copy constructor - mc = bob.machine.ISVMachine(m) - self.assertTrue( m == mc ) - - # Variant - mv = bob.machine.ISVMachine() - # Checks for correctness - mv.isv_base = base - m.z = z - self.assertTrue( m.dim_c == 2) - self.assertTrue( m.dim_d == 3) - self.assertTrue( m.dim_cd == 6) - self.assertTrue( m.dim_ru == 2) - self.assertTrue( (m.z == z).all() ) - - # Defines GMMStats - gs = bob.machine.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.forward(gs) - self.assertTrue( numpy.allclose(m.__x__, x_ref, eps) ) - self.assertTrue( abs(score_ref-score) < eps ) - - # Check using alternate forward() method - Ux = numpy.ndarray(shape=(m.dim_cd,), dtype=numpy.float64) - m.estimate_ux(gs, Ux) - score = m.forward_ux(gs, Ux) - self.assertTrue( abs(score_ref-score) < eps ) - - # x and Ux - x = numpy.ndarray((2,), numpy.float64) - m.estimate_x(gs, x) - x_py = estimate_x(m.dim_c, m.dim_d, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - self.assertTrue( numpy.allclose(x, x_py, eps)) - - ux = numpy.ndarray((6,), numpy.float64) - m.estimate_ux(gs, ux) - ux_py = estimate_ux(m.dim_c, m.dim_d, ubm.mean_supervector, ubm.variance_supervector, U, n, sumpx) - self.assertTrue( numpy.allclose(ux, ux_py, eps)) - self.assertTrue( numpy.allclose(m.__x__, x, eps) ) - - score = m.forward_ux(gs, ux) - self.assertTrue( abs(score_ref-score) < eps ) - - # Clean-up - os.unlink(filename) +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) + assert m.dim_ru == 1 + assert m.dim_rv == 1 + + # Checks for correctness + m.resize(2,2) + m.u = U + m.v = V + m.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 + + # Saves and loads + filename = str(tempfile.mkstemp(".hdf5")[1]) + m.save(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = JFABase(xbob.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) + assert m.dim_ru == 1 + + # Checks for correctness + m.resize(2) + m.u = U + m.d = d + assert (m.u == U).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 + + # Saves and loads + filename = str(tempfile.mkstemp(".hdf5")[1]) + m.save(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = ISVBase(xbob.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() + assert 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 + 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() + + # Saves and loads + filename = str(tempfile.mkstemp(".hdf5")[1]) + m.save(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = JFAMachine(xbob.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.forward(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) + x_py = estimate_x(m.dim_c, m.dim_d, 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) + ux_py = estimate_ux(m.dim_c, m.dim_d, 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 JFABaseMachine + 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 + assert m.dim_c == 2 + assert m.dim_d == 3 + assert m.dim_cd == 6 + assert m.dim_ru == 2 + assert (m.z == z).all() + + # Saves and loads + filename = str(tempfile.mkstemp(".hdf5")[1]) + m.save(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = ISVMachine(xbob.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() + # Checks for correctness + mv.isv_base = base + 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.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.forward(gs) + assert numpy.allclose(m.__x__, x_ref, eps) + assert abs(score_ref-score) < eps + + # Check using alternate forward() method + Ux = numpy.ndarray(shape=(m.dim_cd,), 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) + x_py = estimate_x(m.dim_c, m.dim_d, 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) + ux_py = estimate_ux(m.dim_c, m.dim_d, 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/xbob/learn/misc/test_jfa_trainer.py b/xbob/learn/misc/test_jfa_trainer.py index e8bb2cc6cc062f4df73433766aabb75ac8d3f9c4..8754de8419472ab450a133d6327d1ee7029ab75f 100644 --- a/xbob/learn/misc/test_jfa_trainer.py +++ b/xbob/learn/misc/test_jfa_trainer.py @@ -3,15 +3,15 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Tue Jul 19 12:16:17 2011 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Test JFA trainer package """ -import unittest -import bob -import numpy, numpy.linalg +import numpy +import numpy.linalg +from . import GMMStats, GMMMachine, JFABase, JFAMachine, ISVBase, ISVMachine def equals(x, y, epsilon): return (abs(x - y) < epsilon).all() @@ -27,17 +27,17 @@ 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 = bob.machine.GMMStats(2,3) +gs11 = GMMStats(2,3) gs11.n = N1[:,0] gs11.sum_px = F1[:,0].reshape(2,3) -gs12 = bob.machine.GMMStats(2,3) +gs12 = GMMStats(2,3) gs12.n = N1[:,1] gs12.sum_px = F1[:,1].reshape(2,3) -gs21 = bob.machine.GMMStats(2,3) +gs21 = GMMStats(2,3) gs21.n = N2[:,0] gs21.sum_px = F2[:,0].reshape(2,3) -gs22 = bob.machine.GMMStats(2,3) +gs22 = GMMStats(2,3) gs22.n = N2[:,1] gs22.sum_px = F2[:,1].reshape(2,3) @@ -61,260 +61,254 @@ M_y=[y1, y2] M_x=[x1, x2] - - -class FATrainerTest(unittest.TestCase): - """Performs various FA trainer tests.""" - - - def test01_JFATrainer_updateYandV(self): - # 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 = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = bob.machine.JFABase(ubm,2,2) - t = bob.trainer.JFATrainer(10) - 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_step1(m, TRAINING_STATS) - t.m_step1(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - self.assertTrue(equals(t.__Y__[0], y3, 2e-4)) - self.assertTrue(equals(t.__Y__[1], y4, 2e-4)) - self.assertTrue(equals(m.v, v_ref, 2e-4)) - - - def test02_JFATrainer_updateXandU(self): - # 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 = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = bob.machine.JFABase(ubm,2,2) - t = bob.trainer.JFATrainer(10) - 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_step2(m, TRAINING_STATS) - t.m_step2(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - self.assertTrue(equals(t.__X__[0], x3, 2e-4)) - self.assertTrue(equals(t.__X__[1], x4, 2e-4)) - self.assertTrue(equals(m.u, u_ref, 2e-4)) - - - def test03_JFATrainer_updateZandD(self): - # 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 = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - m = bob.machine.JFABase(ubm,2,2) - t = bob.trainer.JFATrainer(10) - 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_step3(m, TRAINING_STATS) - t.m_step3(m, TRAINING_STATS) - - # Expected results(JFA cookbook, matlab) - self.assertTrue(equals(t.__Z__[0], z3_ref, 2e-4)) - self.assertTrue(equals(t.__Z__[1], z4_ref, 2e-4)) - self.assertTrue(equals(m.d, d_ref, 2e-4)) - - - def test04_JFATrainAndEnrol(self): - # Train and enrol a JFAMachine - - # Calls the train function - ubm = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - mb = bob.machine.JFABase(ubm, 2, 2) - t = bob.trainer.JFATrainer(10) - t.initialize(mb, TRAINING_STATS) - mb.u = M_u - mb.v = M_v - mb.d = M_d - t.train_loop(mb, TRAINING_STATS) - - 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 - self.assertTrue( numpy.allclose(mb.v, v_ref, eps) ) - self.assertTrue( numpy.allclose(mb.u, u_ref, eps) ) - self.assertTrue( numpy.allclose(mb.d, d_ref, eps) ) - - # Calls the enrol function - m = bob.machine.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 = bob.machine.GMMStats(2,3) - gse1.n = Ne[:,0] - gse1.sum_px = Fe[:,0].reshape(2,3) - gse2 = bob.machine.GMMStats(2,3) - gse2.n = Ne[:,1] - gse2.sum_px = Fe[:,1].reshape(2,3) - - gse = [gse1, gse2] - t.enrol(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') - self.assertTrue( numpy.allclose(m.y, y_ref, eps) ) - self.assertTrue( numpy.allclose(m.z, z_ref, eps) ) - - - def test05_ISVTrainAndEnrol(self): - # Train and enrol 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 = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - mb = bob.machine.ISVBase(ubm,2) - t = bob.trainer.ISVTrainer(10, 4.) - #t.train(mb, TRAINING_STATS) - t.initialize(mb, TRAINING_STATS) - mb.u = M_u - for i in range(10): - t.e_step(mb, TRAINING_STATS) - t.m_step(mb, TRAINING_STATS) - t.finalize(mb, TRAINING_STATS) - - self.assertTrue( numpy.allclose(mb.d, d_ref, eps) ) - self.assertTrue( numpy.allclose(mb.u, u_ref, eps) ) - - # Calls the enrol function - m = bob.machine.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 = bob.machine.GMMStats(2,3) - gse1.n = Ne[:,0] - gse1.sum_px = Fe[:,0].reshape(2,3) - gse2 = bob.machine.GMMStats(2,3) - gse2.n = Ne[:,1] - gse2.sum_px = Fe[:,1].reshape(2,3) - - gse = [gse1, gse2] - t.enrol(m, gse, 5) - self.assertTrue( numpy.allclose(m.z, z_ref, eps) ) - - def test06_JFATrainInitialize(self): - # Check that the initialization is consistent and using the rng (cf. issue #118) - - eps = 1e-10 - - # UBM GMM - ubm = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - - ## JFA - jb = bob.machine.JFABase(ubm, 2, 2) - # first round - rng = bob.core.random.mt19937(0) - jt = bob.trainer.JFATrainer(10) - jt.rng = rng - jt.initialize(jb, TRAINING_STATS) - u1 = jb.u - v1 = jb.v - d1 = jb.d - - # second round - rng = bob.core.random.mt19937(0) - jt.rng = rng - jt.initialize(jb, TRAINING_STATS) - u2 = jb.u - v2 = jb.v - d2 = jb.d - - self.assertTrue( numpy.allclose(u1, u2, eps) ) - self.assertTrue( numpy.allclose(v1, v2, eps) ) - self.assertTrue( numpy.allclose(d1, d2, eps) ) - - def test07_ISVTrainInitialize(self): - # Check that the initialization is consistent and using the rng (cf. issue #118) - - eps = 1e-10 - - # UBM GMM - ubm = bob.machine.GMMMachine(2,3) - ubm.mean_supervector = UBM_MEAN - ubm.variance_supervector = UBM_VAR - - ## ISV - ib = bob.machine.ISVBase(ubm, 2) - # first round - rng = bob.core.random.mt19937(0) - it = bob.trainer.ISVTrainer(10) - it.rng = rng - it.initialize(ib, TRAINING_STATS) - u1 = ib.u - d1 = ib.d - - # second round - rng = bob.core.random.mt19937(0) - it.rng = rng - it.initialize(ib, TRAINING_STATS) - u2 = ib.u - d2 = ib.d - - self.assertTrue( numpy.allclose(u1, u2, eps) ) - self.assertTrue( numpy.allclose(d1, d2, eps) ) +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 = bob.trainer.JFATrainer(10) + 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_step1(m, TRAINING_STATS) + t.m_step1(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 = bob.trainer.JFATrainer(10) + 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_step2(m, TRAINING_STATS) + t.m_step2(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 = bob.trainer.JFATrainer(10) + 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_step3(m, TRAINING_STATS) + t.m_step3(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 enrol 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 = bob.trainer.JFATrainer(10) + t.initialize(mb, TRAINING_STATS) + mb.u = M_u + mb.v = M_v + mb.d = M_d + t.train_loop(mb, TRAINING_STATS) + + 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 enrol 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.enrol(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) + + +def test_ISVTrainAndEnrol(): + # Train and enrol 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 = bob.trainer.ISVTrainer(10, 4.) + #t.train(mb, TRAINING_STATS) + t.initialize(mb, TRAINING_STATS) + mb.u = M_u + for i in range(10): + t.e_step(mb, TRAINING_STATS) + t.m_step(mb, TRAINING_STATS) + t.finalize(mb, TRAINING_STATS) + + assert numpy.allclose(mb.d, d_ref, eps) + assert numpy.allclose(mb.u, u_ref, eps) + + # Calls the enrol 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.enrol(m, gse, 5) + assert numpy.allclose(m.z, z_ref, eps) + +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 = bob.trainer.JFATrainer(10) + jt.rng = rng + jt.initialize(jb, TRAINING_STATS) + u1 = jb.u + v1 = jb.v + d1 = jb.d + + # second round + rng = bob.core.random.mt19937(0) + jt.rng = rng + jt.initialize(jb, TRAINING_STATS) + 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 = bob.trainer.ISVTrainer(10) + it.rng = rng + it.initialize(ib, TRAINING_STATS) + u1 = ib.u + d1 = ib.d + + # second round + rng = bob.core.random.mt19937(0) + it.rng = rng + it.initialize(ib, TRAINING_STATS) + u2 = ib.u + d2 = ib.d + + assert numpy.allclose(u1, u2, eps) + assert numpy.allclose(d1, d2, eps) diff --git a/xbob/learn/misc/test_kmeans.py b/xbob/learn/misc/test_kmeans.py index f65f2082b6ec2a6bf727d01e4bffd08d5f5e1f55..16f01a3c4a5b9c27d9ae4ec3e7ccc369628faac0 100644 --- a/xbob/learn/misc/test_kmeans.py +++ b/xbob/learn/misc/test_kmeans.py @@ -3,73 +3,71 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Thu Feb 16 17:57:10 2012 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the KMeans machine """ -import os, sys -import unittest -import bob -import numpy, math +import os +import numpy import tempfile +import xbob.io.base +from . import KMeansMachine + def equals(x, y, epsilon): return (abs(x - y) < epsilon) -class KMeansMachineTest(unittest.TestCase): - """Performs various KMeans machine-related tests.""" - - def test01_KMeansMachine(self): - # Test a KMeansMachine +def test_KMeansMachine(): + # Test a KMeansMachine - means = numpy.array([[3, 70, 0], [4, 72, 0]], 'float64') - mean = numpy.array([3,70,1], 'float64') + means = numpy.array([[3, 70, 0], [4, 72, 0]], 'float64') + mean = numpy.array([3,70,1], 'float64') - # Initializes a KMeansMachine - km = bob.machine.KMeansMachine(2,3) - km.means = means - self.assertTrue( km.dim_c == 2 ) - self.assertTrue( km.dim_d == 3 ) + # Initializes a KMeansMachine + km = KMeansMachine(2,3) + km.means = means + assert km.dim_c == 2 + assert km.dim_d == 3 - # Sets and gets - self.assertTrue( (km.means == means).all() ) - self.assertTrue( (km.get_mean(0) == means[0,:]).all() ) - self.assertTrue( (km.get_mean(1) == means[1,:]).all() ) - km.set_mean(0, mean) - self.assertTrue( (km.get_mean(0) == mean).all() ) + # 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 - self.assertTrue( equals( km.get_distance_from_mean(mean, 0), 0, eps) ) - self.assertTrue( equals( km.get_distance_from_mean(mean, 1), 6, eps) ) - (index, dist) = km.get_closest_mean(mean) - self.assertTrue( index == 0) - self.assertTrue( equals( dist, 0, eps) ) - self.assertTrue( equals( km.get_min_distance(mean), 0, eps) ) + # 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.HDF5File(filename, 'w')) - km_loaded = bob.machine.KMeansMachine(bob.io.HDF5File(filename)) - self.assertTrue( km == km_loaded ) + # Loads and saves + filename = str(tempfile.mkstemp(".hdf5")[1]) + km.save(xbob.io.base.HDF5File(filename, 'w')) + km_loaded = KMeansMachine(xbob.io.base.HDF5File(filename)) + assert km == km_loaded - # Resize - km.resize(4,5) - self.assertTrue( km.dim_c == 4 ) - self.assertTrue( km.dim_d == 5 ) + # Resize + km.resize(4,5) + assert km.dim_c == 4 + assert km.dim_d == 5 - # Copy constructor and comparison operators - km.resize(2,3) - km2 = bob.machine.KMeansMachine(km) - self.assertTrue( km2 == km) - self.assertFalse( km2 != km) - self.assertTrue( km2.is_similar_to(km) ) - means2 = numpy.array([[3, 70, 0], [4, 72, 2]], 'float64') - km2.means = means2 - self.assertFalse( km2 == km) - self.assertTrue( km2 != km) - self.assertFalse( km2.is_similar_to(km) ) + # 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) + # Clean-up + os.unlink(filename) diff --git a/xbob/learn/misc/test_kmeans_trainer.py b/xbob/learn/misc/test_kmeans_trainer.py index e7a5aa9e967a8eba600314f15ba5f5b35d344b43..2964ce205d3bc0e87c1a32115310f56a1e4440f1 100644 --- a/xbob/learn/misc/test_kmeans_trainer.py +++ b/xbob/learn/misc/test_kmeans_trainer.py @@ -3,23 +3,15 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Fri Jan 18 12:46:00 2013 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Test K-Means algorithm """ -import os, sys -import unittest -import bob -import random import numpy -import pkg_resources -def F(f, module=None): - """Returns the test file on the "data" subdirectory""" - if module is None: - return pkg_resources.resource_filename(__name__, os.path.join('data', f)) - return pkg_resources.resource_filename('bob.%s.test' % module, - os.path.join('data', f)) +import xbob.core +import xbob.io +from xbob.io.base.test_utils import datafile def equals(x, y, epsilon): return (abs(x - y) < epsilon).all() @@ -27,8 +19,8 @@ def equals(x, y, epsilon): def kmeans_plus_plus(machine, data, seed): """Python implementation of K-Means++ (initialization)""" n_data = data.shape[0] - mt = bob.core.random.mt19937(seed) - rng = bob.core.random.uniform_int32(0, n_data-1) + mt = xbob.core.random.mt19937(seed) + rng = xbob.core.random.uniform_int32(0, n_data-1) index = rng(mt) machine.set_mean(0, data[index,:]) weights = numpy.zeros(shape=(n_data,), dtype=numpy.float64) @@ -42,13 +34,13 @@ def kmeans_plus_plus(machine, data, seed): weights[s] = w_cur weights *= weights weights /= numpy.sum(weights) - rng_d = bob.core.random.discrete_int32(weights) + rng_d = xbob.core.random.discrete_int32(weights) index = rng_d(mt) machine.set_mean(m, data[index,:]) def NormalizeStdArray(path): - array = bob.io.load(path).astype('float64') + array = xbob.io.base.load(path).astype('float64') std = array.std(axis=0) return (array/std, std) @@ -65,123 +57,119 @@ def flipRows(array): else: raise Exception('Input type not supportd by flipRows') -class KMeansTest(unittest.TestCase): - """Performs various trainer tests.""" - - if hasattr(bob.trainer.KMeansTrainer, 'KMEANS_PLUS_PLUS'): - def test00_kmeans_plus_plus(self): - - # 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 = bob.machine.KMeansMachine(dim_c, dim_d) - trainer = bob.trainer.KMeansTrainer() - trainer.rng = bob.core.random.mt19937(seed) - trainer.initialization_method = bob.trainer.KMeansTrainer.KMEANS_PLUS_PLUS - trainer.initialize(machine, data) - - # Python implementation - py_machine = bob.machine.KMeansMachine(dim_c, dim_d) - kmeans_plus_plus(py_machine, data, seed) - self.assertTrue(equals(machine.means, py_machine.means, 1e-8)) - - def test01_kmeans_noduplicate(self): - # Data/dimensions - dim_c = 2 - dim_d = 3 +if hasattr(bob.trainer.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 - data = numpy.array([[1,2,3],[1,2,3],[1,2,3],[4,5,6.]]) - # Defines machine and trainer + + # C++ implementation machine = bob.machine.KMeansMachine(dim_c, dim_d) trainer = bob.trainer.KMeansTrainer() - trainer.rng = bob.core.random.mt19937(seed) - trainer.initialization_method = bob.trainer.KMeansTrainer.RANDOM_NO_DUPLICATE + trainer.rng = xbob.core.random.mt19937(seed) + trainer.initialization_method = bob.trainer.KMeansTrainer.KMEANS_PLUS_PLUS trainer.initialize(machine, data) - # Makes sure that the two initial mean vectors selected are different - self.assertFalse(equals(machine.get_mean(0), machine.get_mean(1), 1e-8)) - - def test02_kmeans_a(self): - - # 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.load(F("samplesFrom2G_f64.hdf5")) - - machine = bob.machine.KMeansMachine(2, 1) - - trainer = bob.trainer.KMeansTrainer() - trainer.train(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') - self.assertTrue(equals(means, numpy.array([-10.,10.]), 2e-1)) - self.assertTrue(equals(variances, numpy.array([1.,1.]), 2e-1)) - self.assertTrue(equals(weights, numpy.array([0.5,0.5]), 1e-3)) - - self.assertTrue(equals(variances, variances_b, 1e-8)) - self.assertTrue(equals(weights, weights_b, 1e-8)) - - def test03_kmeans_b(self): - - # Trains a KMeansMachine - (arStd,std) = NormalizeStdArray(F("faithful.torch3.hdf5")) - - machine = bob.machine.KMeansMachine(2, 2) - - trainer = bob.trainer.KMeansTrainer() - #trainer.seed = 1337 - trainer.train(machine, arStd) - - [variances, weights] = machine.get_variances_and_weights_for_each_cluster(arStd) - means = machine.means - - multiplyVectorsByFactors(means, std) - multiplyVectorsByFactors(variances, std ** 2) - - gmmWeights = bob.io.load(F('gmm.init_weights.hdf5')) - gmmMeans = bob.io.load(F('gmm.init_means.hdf5')) - gmmVariances = bob.io.load(F('gmm.init_variances.hdf5')) - - if (means[0, 0] < means[1, 0]): - means = flipRows(means) - variances = flipRows(variances) - weights = flipRows(weights) - - self.assertTrue(equals(means, gmmMeans, 1e-3)) - self.assertTrue(equals(weights, gmmWeights, 1e-3)) - self.assertTrue(equals(variances, gmmVariances, 1e-3)) - - # Check comparison operators - trainer1 = bob.trainer.KMeansTrainer() - trainer2 = bob.trainer.KMeansTrainer() - trainer1.rng = trainer2.rng - self.assertTrue( trainer1 == trainer2) - self.assertFalse( trainer1 != trainer2) - trainer1.max_iterations = 1337 - self.assertFalse( trainer1 == trainer2) - self.assertTrue( trainer1 != trainer2) - - # Check that there is no duplicate means during initialization - machine = bob.machine.KMeansMachine(2, 1) - trainer = bob.trainer.KMeansTrainer() - trainer.initialization_method = bob.trainer.KMeansTrainer.RANDOM_NO_DUPLICATE - data = numpy.array([[1.], [1.], [1.], [1.], [1.], [1.], [2.], [3.]]) - trainer.train(machine, data) - self.assertFalse( numpy.isnan(machine.means).any()) + # Python implementation + py_machine = bob.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 = bob.machine.KMeansMachine(dim_c, dim_d) + trainer = bob.trainer.KMeansTrainer() + trainer.rng = xbob.core.random.mt19937(seed) + trainer.initialization_method = bob.trainer.KMeansTrainer.RANDOM_NO_DUPLICATE + trainer.initialize(machine, data) + # Makes sure that the two initial mean vectors selected are different + assert (equals(machine.get_mean(0), machine.get_mean(1), 1e-8)) is 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 = xbob.io.base.load(datafile("samplesFrom2G_f64.hdf5", __name__)) + + machine = bob.machine.KMeansMachine(2, 1) + + trainer = bob.trainer.KMeansTrainer() + trainer.train(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__)) + + machine = bob.machine.KMeansMachine(2, 2) + + trainer = bob.trainer.KMeansTrainer() + #trainer.seed = 1337 + trainer.train(machine, arStd) + + [variances, weights] = machine.get_variances_and_weights_for_each_cluster(arStd) + means = machine.means + + multiplyVectorsByFactors(means, std) + multiplyVectorsByFactors(variances, std ** 2) + + gmmWeights = xbob.io.base.load(datafile('gmm.init_weights.hdf5', __name__)) + gmmMeans = xbob.io.base.load(datafile('gmm.init_means.hdf5', __name__)) + gmmVariances = xbob.io.base.load(datafile('gmm.init_variances.hdf5', __name__)) + + 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 comparison operators + trainer1 = bob.trainer.KMeansTrainer() + trainer2 = bob.trainer.KMeansTrainer() + trainer1.rng = trainer2.rng + assert trainer1 == trainer2 + assert (trainer1 != trainer2) is False + trainer1.max_iterations = 1337 + assert (trainer1 == trainer2) is False + assert trainer1 != trainer2 + + # Check that there is no duplicate means during initialization + machine = bob.machine.KMeansMachine(2, 1) + trainer = bob.trainer.KMeansTrainer() + trainer.initialization_method = bob.trainer.KMeansTrainer.RANDOM_NO_DUPLICATE + data = numpy.array([[1.], [1.], [1.], [1.], [1.], [1.], [2.], [3.]]) + trainer.train(machine, data) + assert (numpy.isnan(machine.means).any()) is False diff --git a/xbob/learn/misc/test_linearscoring.py b/xbob/learn/misc/test_linearscoring.py index e72a5ef268d4c5a5dc623bf3d7748363939618e3..a86f66e1f50f2a65e280986515a99e22a5737871 100644 --- a/xbob/learn/misc/test_linearscoring.py +++ b/xbob/learn/misc/test_linearscoring.py @@ -3,126 +3,123 @@ # Francois Moulin <Francois.Moulin@idiap.ch> # Wed Jul 13 16:00:04 2011 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests on the LinearScoring function """ -import os, sys -import unittest -import bob import numpy -class LinearScoringTest(unittest.TestCase): - """Performs various LinearScoring tests.""" - - def test01_LinearScoring(self): - ubm = bob.machine.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') - - model1 = bob.machine.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') - - model2 = bob.machine.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') - - stats1 = bob.machine.GMMStats(2, 2) - stats1.sum_px = numpy.array([[1, 2], [3, 4]], 'float64') - stats1.n = numpy.array([1, 2], 'float64') - stats1.t = 1+2 - - stats2 = bob.machine.GMMStats(2, 2) - stats2.sum_px = numpy.array([[5, 6], [7, 8]], 'float64') - stats2.n = numpy.array([3, 4], 'float64') - stats2.t = 3+4 - - stats3 = bob.machine.GMMStats(2, 2) - stats3.sum_px = numpy.array([[5, 6], [7, 3]], 'float64') - stats3.n = numpy.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')] - - # 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') - - - # 1/ Use GMMMachines - # 1/a/ Without test_channelOffset, without frame-length normalisation - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3]) - self.assertTrue((abs(scores - ref_scores_00) < 1e-7).all()) - - # 1/b/ Without test_channelOffset, with frame-length normalisation - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], [], True) - self.assertTrue((abs(scores - ref_scores_01) < 1e-7).all()) - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], (), True) - self.assertTrue((abs(scores - ref_scores_01) < 1e-7).all()) - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], None, True) - self.assertTrue((abs(scores - ref_scores_01) < 1e-7).all()) - - # 1/c/ With test_channelOffset, without frame-length normalisation - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], test_channeloffset) - self.assertTrue((abs(scores - ref_scores_10) < 1e-7).all()) - - # 1/d/ With test_channelOffset, with frame-length normalisation - scores = bob.machine.linear_scoring([model1, model2], ubm, [stats1, stats2, stats3], test_channeloffset, True) - self.assertTrue((abs(scores - ref_scores_11) < 1e-7).all()) - - - # 2/ Use mean/variance supervectors - # 2/a/ Without test_channelOffset, without frame-length normalisation - scores = bob.machine.linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3]) - self.assertTrue((abs(scores - ref_scores_00) < 1e-7).all()) - - # 2/b/ Without test_channelOffset, with frame-length normalisation - scores = bob.machine.linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], [], True) - self.assertTrue((abs(scores - ref_scores_01) < 1e-7).all()) - - # 2/c/ With test_channelOffset, without frame-length normalisation - scores = bob.machine.linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], test_channeloffset) - self.assertTrue((abs(scores - ref_scores_10) < 1e-7).all()) - - # 2/d/ With test_channelOffset, with frame-length normalisation - scores = bob.machine.linear_scoring([model1.mean_supervector, model2.mean_supervector], ubm.mean_supervector, ubm.variance_supervector, [stats1, stats2, stats3], test_channeloffset, True) - self.assertTrue((abs(scores - ref_scores_11) < 1e-7).all()) - - # 3/ Using single model/sample - # 3/a/ without frame-length normalisation - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0]) - self.assertTrue(abs(score - ref_scores_10[0,0]) < 1e-7) - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1]) - self.assertTrue(abs(score - ref_scores_10[0,1]) < 1e-7) - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2]) - self.assertTrue(abs(score - ref_scores_10[0,2]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0]) - self.assertTrue(abs(score - ref_scores_10[1,0]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1]) - self.assertTrue(abs(score - ref_scores_10[1,1]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2]) - self.assertTrue(abs(score - ref_scores_10[1,2]) < 1e-7) - - # 3/b/ without frame-length normalisation - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0], True) - self.assertTrue(abs(score - ref_scores_11[0,0]) < 1e-7) - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1], True) - self.assertTrue(abs(score - ref_scores_11[0,1]) < 1e-7) - score = bob.machine.linear_scoring(model1.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2], True) - self.assertTrue(abs(score - ref_scores_11[0,2]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats1, test_channeloffset[0], True) - self.assertTrue(abs(score - ref_scores_11[1,0]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats2, test_channeloffset[1], True) - self.assertTrue(abs(score - ref_scores_11[1,1]) < 1e-7) - score = bob.machine.linear_scoring(model2.mean_supervector, ubm.mean_supervector, ubm.variance_supervector, stats3, test_channeloffset[2], True) - self.assertTrue(abs(score - ref_scores_11[1,2]) < 1e-7) +from . import GMMMachine, GMMStats, 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') + + 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') + + 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') + + stats1 = GMMStats(2, 2) + stats1.sum_px = numpy.array([[1, 2], [3, 4]], 'float64') + stats1.n = numpy.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.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.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')] + + # 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') + + + # 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() + + # 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() + + # 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() + + # 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() + + + # 2/ Use mean/variance supervectors + # 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]) + 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) + 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) + 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) + 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 diff --git a/xbob/learn/misc/test_plda.py b/xbob/learn/misc/test_plda.py index c8953ecc1fdf301253d2277fe786bedb4c842a1b..07aec4fa494d78c430991e57184d10ccaaa73b53 100644 --- a/xbob/learn/misc/test_plda.py +++ b/xbob/learn/misc/test_plda.py @@ -3,18 +3,21 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Sat Oct 22 23:01:09 2011 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests PLDA machine """ -import os, sys -import unittest -import bob -import random, tempfile, math +import os +import tempfile +import math import numpy import numpy.linalg +import xbob.io.base + +from . import PLDABase, PLDAMachine + # Defines common variables globally # Dimensionalities C_dim_d = 7 @@ -29,12 +32,12 @@ C_G=numpy.array([-1.1424, -0.5044, -0.1917, -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, +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): @@ -62,7 +65,7 @@ def compute_gamma(F, G, sigma, 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) @@ -77,7 +80,7 @@ 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)) @@ -94,37 +97,37 @@ def compute_loglike_constterm(F, G, sigma, a): 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, + 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) + 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. + + ### 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)); + 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; + 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 + 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. @@ -149,7 +152,7 @@ def compute_log_likelihood(observations, mu, F, G, sigma): C2 = - J_i/2.*(ld_sigma - ld_alpha) + ld_gamma/2. # 3. Computes C3 - # This is a quadratic part and consists of + # 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; @@ -173,389 +176,390 @@ def compute_log_likelihood(observations, mu, F, G, sigma): return C1 + C2 + C3 -class PLDAMachineTest(unittest.TestCase): - """Performs various PLDA machine tests.""" - - def test01_plda_basemachine(self): - # 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 = bob.machine.PLDABase() - self.assertTrue(m.dim_d == 0) - self.assertTrue(m.dim_f == 0) - self.assertTrue(m.dim_g == 0) - del m - m = bob.machine.PLDABase(C_dim_d, C_dim_f, C_dim_g) - self.assertTrue(m.dim_d == C_dim_d) - self.assertTrue(m.dim_f == C_dim_f) - self.assertTrue(m.dim_g == C_dim_g) - self.assertTrue( abs(m.variance_threshold - 0.) < 1e-10 ) - del m - m = bob.machine.PLDABase(C_dim_d, C_dim_f, C_dim_g, 1e-2) - self.assertTrue(m.dim_d == C_dim_d) - self.assertTrue(m.dim_f == C_dim_f) - self.assertTrue(m.dim_g == C_dim_g) - self.assertTrue( abs(m.variance_threshold - 1e-2) < 1e-10 ) - del m - - # Defines base machine - m = bob.machine.PLDABase() - 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) - print(m.__alpha__ - alpha_ref) - self.assertTrue(equals(m.__alpha__, alpha_ref, 1e-10)) - self.assertTrue(equals(m.__beta__, beta_ref, 1e-10)) - self.assertTrue(equals(gamma3, gamma3_ref, 1e-10)) - - # Compares precomputed values to the ones returned by python implementation - self.assertTrue(equals(m.__isigma__, compute_i_sigma(sigma), 1e-10)) - self.assertTrue(equals(m.__alpha__, compute_alpha(C_G,sigma), 1e-10)) - self.assertTrue(equals(m.__beta__, compute_beta(C_G,sigma), 1e-10)) - self.assertTrue(equals(m.get_add_gamma(3), compute_gamma(C_F,C_G,sigma,3), 1e-10)) - self.assertTrue(m.has_gamma(3)) - self.assertTrue(equals(m.get_gamma(3), compute_gamma(C_F,C_G,sigma,3), 1e-10)) - self.assertTrue(equals(m.__ft_beta__, compute_ft_beta(C_F,C_G,sigma), 1e-10)) - self.assertTrue(equals(m.__gt_i_sigma__, compute_gt_i_sigma(C_G,sigma), 1e-10)) - self.assertTrue(math.fabs(m.__logdet_alpha__ - compute_logdet_alpha(C_G,sigma)) < 1e-10) - self.assertTrue(math.fabs(m.__logdet_sigma__ - compute_logdet_sigma(sigma)) < 1e-10) - self.assertTrue(abs(m.get_add_log_like_const_term(3) - compute_loglike_constterm(C_F,C_G,sigma,3)) < 1e-10) - self.assertTrue(m.has_log_like_const_term(3)) - self.assertTrue(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 = bob.machine.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 - self.assertTrue(equals(m.__alpha__, alpha_ref, 1e-10)) - self.assertTrue(equals(m.__beta__, beta_ref, 1e-10)) - self.assertTrue(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.HDF5File(filename, 'w')) - m_loaded = bob.machine.PLDABase(bob.io.HDF5File(filename)) - - # Compares the values loaded with the former ones - self.assertTrue(m_loaded == m) - self.assertFalse(m_loaded != m) - self.assertTrue(equals(m_loaded.mu, mu, 1e-10)) - self.assertTrue(equals(m_loaded.f, C_F, 1e-10)) - self.assertTrue(equals(m_loaded.g, C_G, 1e-10)) - self.assertTrue(equals(m_loaded.sigma, sigma, 1e-10)) - self.assertTrue(equals(m_loaded.__isigma__, isigma, 1e-10)) - self.assertTrue(equals(m_loaded.__alpha__, alpha, 1e-10)) - self.assertTrue(equals(m_loaded.__beta__, beta, 1e-10)) - self.assertTrue(equals(m_loaded.__ft_beta__, FtBeta, 1e-10)) - self.assertTrue(equals(m_loaded.__gt_i_sigma__, GtISigma, 1e-10)) - self.assertTrue(abs(m_loaded.__logdet_alpha__ - logdetAlpha) < 1e-10) - self.assertTrue(abs(m_loaded.__logdet_sigma__ - logdetSigma) < 1e-10) - self.assertTrue(m_loaded.has_gamma(3)) - self.assertTrue(equals(m_loaded.get_gamma(3), gamma3_ref, 1e-10)) - self.assertTrue(equals(m_loaded.get_add_gamma(3), gamma3_ref, 1e-10)) - self.assertTrue(m_loaded.has_log_like_const_term(3)) - self.assertTrue(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 = bob.machine.PLDABase(m_loaded) - self.assertTrue(m_loaded == m_copy) - self.assertFalse(m_loaded != m_copy) - # Test clear_maps method - self.assertTrue(m_copy.has_gamma(3)) - self.assertTrue(m_copy.has_log_like_const_term(3)) - m_copy.clear_maps() - self.assertFalse(m_copy.has_gamma(3)) - self.assertFalse(m_copy.has_log_like_const_term(3)) - - # 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_ - self.assertFalse(m_loaded == m_copy) - self.assertTrue(m_loaded != m_copy) - m_copy.variance_threshold = v_zzo_ - m_copy.sigma = v_zo - self.assertTrue(equals(m_copy.sigma, v_zo, 1e-10)) - m_copy.variance_threshold = v_zo_ - m_copy.sigma = v_zzo - self.assertTrue(equals(m_copy.sigma, v_zo, 1e-10)) - m_copy.variance_threshold = v_zzo_ - m_copy.sigma = v_zzo - self.assertTrue(equals(m_copy.sigma, v_zzo, 1e-10)) - m_copy.variance_threshold = v_zo_ - self.assertTrue(equals(m_copy.sigma, v_zo, 1e-10)) - - # Clean-up - os.unlink(filename) - - - def test02_plda_basemachine_loglikelihood_pointestimate(self): - # 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 = bob.machine.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 - - self.assertTrue(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)) - - - def test03_plda_machine(self): - # 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 = bob.machine.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 = bob.machine.PLDAMachine(mb) - self.assertTrue(m.dim_d == C_dim_d) - self.assertTrue(m.dim_f == C_dim_f) - self.assertTrue(m.dim_g == C_dim_g) - - m0 = bob.machine.PLDAMachine() - m0.plda_base = mb - self.assertTrue(m0.dim_d == C_dim_d) - self.assertTrue(m0.dim_f == C_dim_f) - self.assertTrue(m0.dim_g == 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.HDF5File(filename, 'w')) - m_loaded = bob.machine.PLDAMachine(bob.io.HDF5File(filename), mb) - - # Compares the values loaded with the former ones - self.assertTrue(m_loaded == m) - self.assertFalse(m_loaded != m) - self.assertTrue(abs(m_loaded.n_samples - n_samples) < 1e-10) - self.assertTrue(abs(m_loaded.w_sum_xit_beta_xi - WSumXitBetaXi) < 1e-10) - self.assertTrue(equals(m_loaded.weighted_sum, weightedSum, 1e-10)) - self.assertTrue(abs(m_loaded.log_likelihood - log_likelihood) < 1e-10) - self.assertTrue(m_loaded.has_gamma(3)) - self.assertTrue(equals(m_loaded.get_add_gamma(3), gamma3, 1e-10)) - self.assertTrue(equals(m_loaded.get_gamma(3), gamma3, 1e-10)) - self.assertTrue(m_loaded.has_log_like_const_term(3)) - self.assertTrue(abs(m_loaded.get_add_log_like_const_term(3) - constTerm3) < 1e-10) - self.assertTrue(abs(m_loaded.get_log_like_const_term(3) - constTerm3) < 1e-10) - - # Test clear_maps method - self.assertTrue(m_loaded.has_gamma(3)) - self.assertTrue(m_loaded.has_log_like_const_term(3)) - m_loaded.clear_maps() - self.assertFalse(m_loaded.has_gamma(3)) - self.assertFalse(m_loaded.has_log_like_const_term(3)) - - # Check exceptions - m_loaded2 = bob.machine.PLDAMachine() - m_loaded2.load(bob.io.HDF5File(filename)) - self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_d') - self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_f') - self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_g') - self.assertRaises(RuntimeError, m_loaded2.forward, [1.]) - self.assertRaises(RuntimeError, m_loaded2.compute_log_likelihood, [1.]) - - # Clean-up - os.unlink(filename) - - - def test04_plda_machine_log_likelihood_Python(self): - # 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 = bob.machine.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 = bob.machine.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]) - self.assertTrue(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)) - self.assertTrue(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) - self.assertTrue(abs(m.forward(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) - self.assertTrue(abs(m.forward(ar2_s2d) - llr2d) < 1e-10) - - - def test05_plda_machine_log_likelihood_Prince(self): - # 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 = bob.machine.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 = bob.machine.PLDAMachine(mb) - ll = m.compute_log_likelihood(X) - self.assertTrue(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 - self.assertTrue(abs((llX - (llY + llZ)) - llr_ref) < 1e-10) +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.dim_d == C_dim_d + assert m.dim_f == C_dim_f + assert m.dim_g == 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.dim_d == C_dim_d + assert m.dim_f == C_dim_f + assert m.dim_g == C_dim_g + assert abs(m.variance_threshold - 1e-2) < 1e-10 + del m + + # Defines base machine + m = PLDABase() + 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) + print(m.__alpha__ - alpha_ref) + 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(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = PLDABase(xbob.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 + + self.assertTrue(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)) + + +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.dim_d == C_dim_d + assert m.dim_f == C_dim_f + assert m.dim_g == C_dim_g + + m0 = PLDAMachine() + m0.plda_base = mb + assert m0.dim_d == C_dim_d + assert m0.dim_f == C_dim_f + assert m0.dim_g == 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(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = PLDAMachine(xbob.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() + m_loaded2.load(xbob.io.base.HDF5File(filename)) + self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_d') + self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_f') + self.assertRaises(RuntimeError, getattr, m_loaded2, 'dim_g') + self.assertRaises(RuntimeError, m_loaded2.forward, [1.]) + self.assertRaises(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)) + 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.forward(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.forward(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/xbob/learn/misc/test_plda_trainer.py b/xbob/learn/misc/test_plda_trainer.py index d1d403c2e6d73dc006efcf2e353e3f2f0252e2b1..7ac737e4c4981d31df17374527bc259831f7d602 100644 --- a/xbob/learn/misc/test_plda_trainer.py +++ b/xbob/learn/misc/test_plda_trainer.py @@ -3,14 +3,16 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Fri Oct 14 18:07:56 2011 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests PLDA trainer """ -import sys, unittest -import bob -import numpy, numpy.linalg +import sys +import numpy +import numpy.linalg + +from . import PLDATrainer, PLDABase, PLDAMachine class PythonPLDATrainer(): """A simplified (and slower) version of the PLDATrainer""" @@ -29,7 +31,7 @@ class PythonPLDATrainer(): self.m_z_second_order = [] self.m_sum_z_second_order = numpy.ndarray(shape=(0,0), dtype=numpy.float64) - def reset(self): + def reset(): """Resets our internal state""" self.m_convergence_threshold = 0.001 self.m_max_iterations = 10 @@ -313,422 +315,419 @@ class PythonPLDATrainer(): i += 1 -class PLDATrainerTest(unittest.TestCase): - """Performs various PLDA trainer tests.""" - - def test01_plda_EM_vs_Python(self): - - # 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 = bob.trainer.PLDATrainer(10) - t_py = PythonPLDATrainer() - m = bob.machine.PLDABase(D,nf,ng) - m_py = bob.machine.PLDABase(D,nf,ng) - - # Sets the same initialization methods - t.init_f_method = bob.trainer.PLDATrainer.BETWEEN_SCATTER - t.init_g_method = bob.trainer.PLDATrainer.WITHIN_SCATTER - t.init_sigma_method = bob.trainer.PLDATrainer.VARIANCE_DATA - - t.train(m, l) - t_py.train(m_py, l) - self.assertTrue(numpy.allclose(m.mu, m_py.mu)) - self.assertTrue(numpy.allclose(m.f, m_py.f)) - self.assertTrue(numpy.allclose(m.g, m_py.g)) - self.assertTrue(numpy.allclose(m.sigma, m_py.sigma)) - - def test02_plda_EM_vs_Prince(self): - # 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 = bob.trainer.PLDATrainer() - t0 = bob.trainer.PLDATrainer(t) - m = bob.machine.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 = bob.machine.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 - self.assertTrue(numpy.allclose(t.z_first_order[0], z_first_order_a_1, 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], z_first_order_b_1, 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order_sum, z_second_order_sum_1, 1e-10)) - # Compares statistics against the ones of the python implementation - self.assertTrue(numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(m.f, F_1, 1e-10)) - self.assertTrue(numpy.allclose(m.g, G_1, 1e-10)) - self.assertTrue(numpy.allclose(m.sigma, sigma_1, 1e-10)) - # Compares F, G and sigma to the ones of the python implementation - self.assertTrue(numpy.allclose(m.f, m_py.f, 1e-10)) - self.assertTrue(numpy.allclose(m.g, m_py.g, 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(t.z_first_order[0], z_first_order_a_2, 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], z_first_order_b_2, 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order_sum, z_second_order_sum_2, 1e-10)) - # Compares statistics against the ones of the python implementation - self.assertTrue(numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(m.f, F_2, 1e-10)) - self.assertTrue(numpy.allclose(m.g, G_2, 1e-10)) - self.assertTrue(numpy.allclose(m.sigma, sigma_2, 1e-10)) - # Compares F, G and sigma to the ones of the python implementation - self.assertTrue(numpy.allclose(m.f, m_py.f, 1e-10)) - self.assertTrue(numpy.allclose(m.g, m_py.g, 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(t.z_first_order[0], z_first_order_a_1, 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], z_first_order_b_1, 1e-10)) - # Compares statistics against the ones of the python implementation - self.assertTrue(numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order[0], t_py.m_z_second_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order[1], t_py.m_z_second_order[1], 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(m.f, m_py.f, 1e-10)) - self.assertTrue(numpy.allclose(m.g, m_py.g, 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(t.z_first_order[0], z_first_order_a_2, 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], z_first_order_b_2, 1e-10)) - # Compares statistics against the ones of the python implementation - self.assertTrue(numpy.allclose(t.z_first_order[0], t_py.m_z_first_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_first_order[1], t_py.m_z_first_order[1], 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order[0], t_py.m_z_second_order[0], 1e-10)) - self.assertTrue(numpy.allclose(t.z_second_order[1], t_py.m_z_second_order[1], 1e-10)) - self.assertTrue(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 - self.assertTrue(numpy.allclose(m.f, m_py.f, 1e-10)) - self.assertTrue(numpy.allclose(m.g, m_py.g, 1e-10)) - self.assertTrue(numpy.allclose(m.sigma, m_py.sigma, 1e-10)) - - - def test03_plda_enrollment(self): - # 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 = bob.machine.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_enrol = [] - a_enrol.append(x1) - a_enrol.append(x2) - a_enrol = numpy.array(a_enrol) - - # 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 = bob.machine.PLDAMachine(mb) - t = bob.trainer.PLDATrainer() - t.enrol(m, a_enrol) - ll = m.compute_log_likelihood(x3) - self.assertTrue(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.forward(x3) - self.assertTrue(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)) - self.assertTrue(abs(llr - llr_separate) < 1e-10) - - def test04_plda_comparisons(self): - - t1 = bob.trainer.PLDATrainer() - t2 = bob.trainer.PLDATrainer() - t2.rng = t1.rng - self.assertTrue( t1 == t2 ) - self.assertFalse( t1 != t2 ) - self.assertTrue( t1.is_similar_to(t2) ) - - training_set = [numpy.array([[1,2,3,4]], numpy.float64), numpy.array([[3,4,3,4]], numpy.float64)] - m = bob.machine.PLDABase(4,1,1,1e-8) - t1.rng.seed(37) - t1.initialize(m, training_set) - t1.e_step(m, training_set) - t1.m_step(m, training_set) - self.assertFalse( t1 == t2 ) - self.assertTrue( t1 != t2 ) - self.assertFalse( t1.is_similar_to(t2) ) - t2.rng.seed(37) - t2.initialize(m, training_set) - t2.e_step(m, training_set) - t2.m_step(m, training_set) - self.assertTrue( t1 == t2 ) - self.assertFalse( t1 != t2 ) - self.assertTrue( t1.is_similar_to(t2) ) - t2.rng.seed(77) - t2.initialize(m, training_set) - t2.e_step(m, training_set) - t2.m_step(m, training_set) - self.assertFalse( t1 == t2 ) - self.assertTrue( t1 != t2 ) - self.assertFalse( t1.is_similar_to(t2) ) +def test_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(10) + t_py = PythonPLDATrainer() + m = PLDABase(D,nf,ng) + m_py = PLDABase(D,nf,ng) + + # Sets the same initialization methods + t.init_f_method = PLDATrainer.BETWEEN_SCATTER + t.init_g_method = PLDATrainer.WITHIN_SCATTER + t.init_sigma_method = PLDATrainer.VARIANCE_DATA + + t.train(m, l) + 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) + + +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_enrol = [] + a_enrol.append(x1) + a_enrol.append(x2) + a_enrol = numpy.array(a_enrol) + + # 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.enrol(m, a_enrol) + 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.forward(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) + t1.rng.seed(37) + t1.initialize(m, training_set) + 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 + t2.rng.seed(37) + t2.initialize(m, training_set) + 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) + t2.rng.seed(77) + t2.initialize(m, training_set) + 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/xbob/learn/misc/test_wiener.py b/xbob/learn/misc/test_wiener.py index d1de97d467e5b6ec302f2c613451512dad6e8254..15e3028c4720cec6ab9c2977ab9304b949a2ca0d 100644 --- a/xbob/learn/misc/test_wiener.py +++ b/xbob/learn/misc/test_wiener.py @@ -2,108 +2,106 @@ # vim: set fileencoding=utf-8 : # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the WienerMachine """ -import os, sys -import unittest -import math -import bob -import numpy, numpy.random +import os +import numpy import tempfile +import nose.tools -class WienerMachineTest(unittest.TestCase): - """Performs various WienerMachine tests.""" - - def test01_initialization(self): - - # Getters/Setters - m = bob.machine.WienerMachine(5,4,0.5) - self.assertEqual(m.height, 5) - self.assertEqual(m.width, 4) - self.assertEqual(m.shape, (5,4)) - m.height = 8 - m.width = 7 - self.assertEqual(m.height, 8) - self.assertEqual(m.width, 7) - self.assertEqual(m.shape, (8,7)) - m.shape = (5,6) - self.assertEqual(m.height, 5) - self.assertEqual(m.width, 6) - self.assertEqual(m.shape, (5,6)) - ps1 = 0.2 + numpy.fabs(numpy.random.randn(5,6)) - ps2 = 0.2 + numpy.fabs(numpy.random.randn(5,6)) - m.ps = ps1 - self.assertTrue( numpy.allclose(m.ps, ps1) ) - m.ps = ps2 - self.assertTrue( numpy.allclose(m.ps, ps2) ) - pn1 = 0.5 - m.pn = pn1 - self.assertTrue( abs(m.pn - pn1) < 1e-5 ) - var_thd = 1e-5 - m.variance_threshold = var_thd - self.assertTrue( abs(m.variance_threshold - var_thd) < 1e-5 ) - - # Comparison operators - m2 = bob.machine.WienerMachine(m) - self.assertTrue( m == m2 ) - self.assertFalse( m != m2 ) - m3 = bob.machine.WienerMachine(ps2, pn1) - m3.variance_threshold = var_thd - self.assertTrue( m == m3 ) - self.assertFalse( m != m3 ) - - # Computation of the Wiener filter W - w_py = 1 / (1. + m.pn / m.ps) - self.assertTrue( numpy.allclose(m.w, w_py) ) - - - def test02_load_save(self): - - m = bob.machine.WienerMachine(5,4,0.5) - - # Save and read from file - filename = str(tempfile.mkstemp(".hdf5")[1]) - m.save(bob.io.HDF5File(filename, 'w')) - m_loaded = bob.machine.WienerMachine(bob.io.HDF5File(filename)) - self.assertTrue( m == m_loaded ) - self.assertFalse( m != m_loaded ) - self.assertTrue(m.is_similar_to(m_loaded)) - # Make them different - m_loaded.variance_threshold = 0.001 - self.assertFalse( m == m_loaded ) - self.assertTrue( m != m_loaded ) - - # Clean-up - os.unlink(filename) - - - def test03_forward(self): - - ps = 0.2 + numpy.fabs(numpy.random.randn(5,6)) - pn = 0.5 - m = bob.machine.WienerMachine(ps,pn) - - # Python way - sample = numpy.random.randn(5,6) - sample_fft = bob.sp.fft(sample.astype(numpy.complex128)) - w = m.w - sample_fft_filtered = sample_fft * m.w - sample_filtered_py = numpy.absolute(bob.sp.ifft(sample_fft_filtered)) - - # Bob c++ way - sample_filtered0 = m.forward(sample) - sample_filtered1 = m(sample) - sample_filtered2 = numpy.zeros((5,6),numpy.float64) - m.forward_(sample, sample_filtered2) - sample_filtered3 = numpy.zeros((5,6),numpy.float64) - m.forward(sample, sample_filtered3) - sample_filtered4 = numpy.zeros((5,6),numpy.float64) - m(sample, sample_filtered4) - self.assertTrue( numpy.allclose(sample_filtered0, sample_filtered_py) ) - self.assertTrue( numpy.allclose(sample_filtered1, sample_filtered_py) ) - self.assertTrue( numpy.allclose(sample_filtered2, sample_filtered_py) ) - self.assertTrue( numpy.allclose(sample_filtered3, sample_filtered_py) ) - self.assertTrue( numpy.allclose(sample_filtered4, sample_filtered_py) ) +import xbob.sp +import xbob.io.base + +from . import WienerMachine + +def test_initialization(): + + # Getters/Setters + m = WienerMachine(5,4,0.5) + nose.tools.eq_(m.height, 5) + nose.tools.eq_(m.width, 4) + nose.tools.eq_(m.shape, (5,4)) + m.height = 8 + m.width = 7 + nose.tools.eq_(m.height, 8) + nose.tools.eq_(m.width, 7) + nose.tools.eq_(m.shape, (8,7)) + m.shape = (5,6) + nose.tools.eq_(m.height, 5) + nose.tools.eq_(m.width, 6) + nose.tools.eq_(m.shape, (5,6)) + ps1 = 0.2 + numpy.fabs(numpy.random.randn(5,6)) + ps2 = 0.2 + numpy.fabs(numpy.random.randn(5,6)) + m.ps = ps1 + assert numpy.allclose(m.ps, ps1) + m.ps = ps2 + assert numpy.allclose(m.ps, ps2) + pn1 = 0.5 + m.pn = pn1 + assert abs(m.pn - pn1) < 1e-5 + var_thd = 1e-5 + m.variance_threshold = var_thd + assert abs(m.variance_threshold - var_thd) < 1e-5 + + # Comparison operators + m2 = WienerMachine(m) + assert m == m2 + assert (m != m2 ) is False + m3 = WienerMachine(ps2, pn1) + m3.variance_threshold = var_thd + assert m == m3 + assert (m != m3 ) is False + + # Computation of the Wiener filter W + w_py = 1 / (1. + m.pn / m.ps) + assert numpy.allclose(m.w, w_py) + +def test_load_save(): + + m = WienerMachine(5,4,0.5) + + # Save and read from file + filename = str(tempfile.mkstemp(".hdf5")[1]) + m.save(xbob.io.base.HDF5File(filename, 'w')) + m_loaded = WienerMachine(xbob.io.base.HDF5File(filename)) + assert m == m_loaded + assert (m != m_loaded ) is False + assert m.is_similar_to(m_loaded) + # Make them different + m_loaded.variance_threshold = 0.001 + assert (m == m_loaded ) is False + assert m != m_loaded + + # Clean-up + os.unlink(filename) + +def test_forward(): + + ps = 0.2 + numpy.fabs(numpy.random.randn(5,6)) + pn = 0.5 + m = WienerMachine(ps,pn) + + # Python way + sample = numpy.random.randn(5,6) + sample_fft = xbob.sp.fft(sample.astype(numpy.complex128)) + w = m.w + sample_fft_filtered = sample_fft * m.w + sample_filtered_py = numpy.absolute(xbob.sp.ifft(sample_fft_filtered)) + + # Bob c++ way + sample_filtered0 = m.forward(sample) + sample_filtered1 = m(sample) + sample_filtered2 = numpy.zeros((5,6),numpy.float64) + m.forward_(sample, sample_filtered2) + sample_filtered3 = numpy.zeros((5,6),numpy.float64) + m.forward(sample, sample_filtered3) + sample_filtered4 = numpy.zeros((5,6),numpy.float64) + m(sample, sample_filtered4) + assert numpy.allclose(sample_filtered0, sample_filtered_py) + assert numpy.allclose(sample_filtered1, sample_filtered_py) + assert numpy.allclose(sample_filtered2, sample_filtered_py) + assert numpy.allclose(sample_filtered3, sample_filtered_py) + assert numpy.allclose(sample_filtered4, sample_filtered_py) diff --git a/xbob/learn/misc/test_wiener_trainer.py b/xbob/learn/misc/test_wiener_trainer.py index 70a4ba804b04373a581935e119a336414379f657..676429ffb4b92e7f008490ca3fb3ffd8f4c1932b 100644 --- a/xbob/learn/misc/test_wiener_trainer.py +++ b/xbob/learn/misc/test_wiener_trainer.py @@ -2,17 +2,15 @@ # vim: set fileencoding=utf-8 : # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests the WienerTrainer """ -import os, sys -import unittest -import math -import bob -import numpy, numpy.random -import tempfile +import numpy +import xbob.sp + +from . import WienerMachine, WienerTrainer def train_wiener_ps(training_set): @@ -24,7 +22,7 @@ def train_wiener_ps(training_set): for n in range(n_samples): sample = (training_set[n,:,:]).astype(numpy.complex128) - training_fftabs[n,:,:] = numpy.absolute(bob.sp.fft(sample)) + training_fftabs[n,:,:] = numpy.absolute(xbob.sp.fft(sample)) mean = numpy.mean(training_fftabs, axis=0) @@ -37,42 +35,39 @@ def train_wiener_ps(training_set): return var_ps -class WienerTrainerTest(unittest.TestCase): - """Performs various WienerTrainer tests.""" - - def test01_initialization(self): - - # Constructors and comparison operators - t1 = bob.trainer.WienerTrainer() - t2 = bob.trainer.WienerTrainer() - t3 = bob.trainer.WienerTrainer(t2) - t4 = t3 - self.assertTrue( t1 == t2) - self.assertFalse( t1 != t2) - self.assertTrue( t1.is_similar_to(t2) ) - self.assertTrue( t1 == t3) - self.assertFalse( t1 != t3) - self.assertTrue( t1.is_similar_to(t3) ) - self.assertTrue( t1 == t4) - self.assertFalse( t1 != t4) - self.assertTrue( t1.is_similar_to(t4) ) - - - def test02_train(self): - - n_samples = 20 - height = 5 - width = 6 - training_set = 0.2 + numpy.fabs(numpy.random.randn(n_samples, height, width)) - - # Python implementation - var_ps = train_wiener_ps(training_set) - # Bob C++ implementation (variant 1) + comparison against python one - t = bob.trainer.WienerTrainer() - m1 = t.train(training_set) - self.assertTrue( numpy.allclose(var_ps, m1.ps) ) - # Bob C++ implementation (variant 2) + comparison against python one - m2 = bob.machine.WienerMachine(height, width, 0.) - t.train(m2, training_set) - self.assertTrue( numpy.allclose(var_ps, m2.ps) ) +def test_initialization(): + + # Constructors and comparison operators + t1 = WienerTrainer() + t2 = WienerTrainer() + t3 = WienerTrainer(t2) + t4 = t3 + assert t1 == t2 + assert (t1 != t2) is False + assert t1.is_similar_to(t2) + assert t1 == t3 + assert (t1 != t3) is False + assert t1.is_similar_to(t3) + assert t1 == t4 + assert (t1 != t4) is False + assert t1.is_similar_to(t4) + + +def test_train(): + + n_samples = 20 + height = 5 + width = 6 + training_set = 0.2 + numpy.fabs(numpy.random.randn(n_samples, height, width)) + + # Python implementation + var_ps = train_wiener_ps(training_set) + # Bob C++ implementation (variant 1) + comparison against python one + t = WienerTrainer() + m1 = t.train(training_set) + assert numpy.allclose(var_ps, m1.ps) + # Bob C++ implementation (variant 2) + comparison against python one + m2 = WienerMachine(height, width, 0.) + t.train(m2, training_set) + assert numpy.allclose(var_ps, m2.ps) diff --git a/xbob/learn/misc/test_ztnorm.py b/xbob/learn/misc/test_ztnorm.py index 9de2c37ba9fb90f303a86e68b9d134df9d475bd1..0af05f2d6bc392255756aa959e72cffeb677bbf1 100644 --- a/xbob/learn/misc/test_ztnorm.py +++ b/xbob/learn/misc/test_ztnorm.py @@ -4,20 +4,17 @@ # Laurent El Shafey <Laurent.El-Shafey@idiap.ch> # Tue Jul 19 15:33:20 2011 +0200 # -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland """Tests on the ZTNorm function """ -import os, sys -import unittest import numpy -import bob -import pkg_resources -def F(f): - """Returns the test file on the "data" subdirectory""" - return pkg_resources.resource_filename(__name__, os.path.join('data', f)) +from xbob.io.base.test_utils import datafile +import xbob.io.base + +from . import znorm, tnorm, ztnorm def sameValue(vect_A, vect_B): sameMatrix = numpy.zeros((vect_A.shape[0], vect_B.shape[0]), 'bool') @@ -27,7 +24,7 @@ def sameValue(vect_A, vect_B): sameMatrix[j, i] = (vect_A[j] == vect_B[i]) return sameMatrix - + def tnorm(A, C): Cmean = numpy.mean(C, axis=0) if C.shape[1] > 1: @@ -35,92 +32,90 @@ def tnorm(A, C): else: Cstd = numpy.ones(shape=(C.shape[1],), dtype=numpy.float64) return (A - numpy.tile(Cmean.reshape(1,C.shape[1]), (A.shape[0],1))) / numpy.tile(Cstd.reshape(1,C.shape[1]), (A.shape[0],1)) - + def znorm(A, B): Bmean = numpy.mean(B, axis=1) if B.shape[1] > 1: Bstd = numpy.sqrt(numpy.sum((B - numpy.tile(Bmean.reshape(B.shape[0],1), (1,B.shape[1]))) ** 2, axis=1) / (B.shape[1]-1)) else: Bstd = numpy.ones(shape=(B.shape[0],), dtype=numpy.float64) - + return (A - numpy.tile(Bmean.reshape(B.shape[0],1), (1,A.shape[1]))) / numpy.tile(Bstd.reshape(B.shape[0],1), (1,A.shape[1])) - -class ZTNormTest(unittest.TestCase): - """Performs various ZTNorm tests.""" - - def test01_ztnorm_simple(self): - # 3x5 - my_A = numpy.array([[1, 2, 3, 4, 5], - [6, 7, 8, 9, 8], - [7, 6, 5, 4, 3]],'float64') - # 3x4 - my_B = numpy.array([[5, 4, 7, 8],[9, 8, 7, 4],[5, 6, 3, 2]],'float64') - # 2x5 - my_C = numpy.array([[5, 4, 3, 2, 1],[2, 1, 2, 3, 4]],'float64') - # 2x4 - my_D = numpy.array([[8, 6, 4, 2],[0, 2, 4, 6]],'float64') - - # 4x1 - znorm_id = numpy.array([1, 2, 3, 4],'uint32') - # 2x1 - tnorm_id = numpy.array([1, 5],'uint32') - - scores = bob.machine.ztnorm(my_A, my_B, my_C, my_D, - sameValue(tnorm_id, znorm_id)) - - ref_scores = numpy.array([[-4.45473107e+00, -3.29289322e+00, -1.50519101e+01, -8.42086557e-01, 6.46544511e-03], [-8.27619927e-01, 7.07106781e-01, 1.13757710e+01, 2.01641412e+00, 7.63765080e-01], [ 2.52913570e+00, 2.70710678e+00, 1.24400233e+01, 7.07106781e-01, 6.46544511e-03]], 'float64') - - self.assertTrue((abs(scores - ref_scores) < 1e-7).all()) - - def test02_ztnorm_big(self): - my_A = bob.io.load(F("ztnorm_eval_eval.mat")) - my_B = bob.io.load(F("ztnorm_znorm_eval.mat")) - my_C = bob.io.load(F("ztnorm_eval_tnorm.mat")) - my_D = bob.io.load(F("ztnorm_znorm_tnorm.mat")) - - # ZT-Norm - ref_scores = bob.io.load(F("ztnorm_result.mat")) - scores = bob.machine.ztnorm(my_A, my_B, my_C, my_D) - self.assertTrue((abs(scores - ref_scores) < 1e-7).all()) - - # T-Norm - scores = bob.machine.tnorm(my_A, my_C) - scores_py = tnorm(my_A, my_C) - self.assertTrue((abs(scores - scores_py) < 1e-7).all()) - - # Z-Norm - scores = bob.machine.znorm(my_A, my_B) - scores_py = znorm(my_A, my_B) - self.assertTrue((abs(scores - scores_py) < 1e-7).all()) - - def test03_tnorm_simple(self): - # 3x5 - my_A = numpy.array([[1, 2, 3, 4, 5], - [6, 7, 8, 9, 8], - [7, 6, 5, 4, 3]],'float64') - # 2x5 - my_C = numpy.array([[5, 4, 3, 2, 1],[2, 1, 2, 3, 4]],'float64') - - zC = bob.machine.tnorm(my_A, my_C) - zC_py = tnorm(my_A, my_C) - self.assertTrue((abs(zC - zC_py) < 1e-7).all()) - - empty = numpy.zeros(shape=(0,0), dtype=numpy.float64) - zC = bob.machine.ztnorm(my_A, empty, my_C, empty) - self.assertTrue((abs(zC - zC_py) < 1e-7).all()) - - def test04_znorm_simple(self): - # 3x5 - my_A = numpy.array([[1, 2, 3, 4, 5], - [6, 7, 8, 9, 8], - [7, 6, 5, 4, 3]], numpy.float64) - # 3x4 - my_B = numpy.array([[5, 4, 7, 8],[9, 8, 7, 4],[5, 6, 3, 2]], numpy.float64) - - zA = bob.machine.znorm(my_A, my_B) - zA_py = znorm(my_A, my_B) - self.assertTrue((abs(zA - zA_py) < 1e-7).all()) - - empty = numpy.zeros(shape=(0,0), dtype=numpy.float64) - zA = bob.machine.ztnorm(my_A, my_B, empty, empty) - self.assertTrue((abs(zA - zA_py) < 1e-7).all()) + + +def test_ztnorm_simple(): + # 3x5 + my_A = numpy.array([[1, 2, 3, 4, 5], + [6, 7, 8, 9, 8], + [7, 6, 5, 4, 3]],'float64') + # 3x4 + my_B = numpy.array([[5, 4, 7, 8],[9, 8, 7, 4],[5, 6, 3, 2]],'float64') + # 2x5 + my_C = numpy.array([[5, 4, 3, 2, 1],[2, 1, 2, 3, 4]],'float64') + # 2x4 + my_D = numpy.array([[8, 6, 4, 2],[0, 2, 4, 6]],'float64') + + # 4x1 + znorm_id = numpy.array([1, 2, 3, 4],'uint32') + # 2x1 + tnorm_id = numpy.array([1, 5],'uint32') + + scores = ztnorm(my_A, my_B, my_C, my_D, + sameValue(tnorm_id, znorm_id)) + + ref_scores = numpy.array([[-4.45473107e+00, -3.29289322e+00, -1.50519101e+01, -8.42086557e-01, 6.46544511e-03], [-8.27619927e-01, 7.07106781e-01, 1.13757710e+01, 2.01641412e+00, 7.63765080e-01], [ 2.52913570e+00, 2.70710678e+00, 1.24400233e+01, 7.07106781e-01, 6.46544511e-03]], 'float64') + + assert (abs(scores - ref_scores) < 1e-7).all() + +def test_ztnorm_big(): + my_A = xbob.io.base.load(datafile("ztnorm_eval_eval.mat", __name__)) + my_B = xbob.io.base.load(datafile("ztnorm_znorm_eval.mat", __name__)) + my_C = xbob.io.base.load(datafile("ztnorm_eval_tnorm.mat", __name__)) + my_D = xbob.io.base.load(datafile("ztnorm_znorm_tnorm.mat", __name__)) + + # ZT-Norm + ref_scores = xbob.io.base.load(datafile("ztnorm_result.mat", __name__)) + scores = ztnorm(my_A, my_B, my_C, my_D) + assert (abs(scores - ref_scores) < 1e-7).all() + + # T-Norm + scores = tnorm(my_A, my_C) + scores_py = tnorm(my_A, my_C) + assert (abs(scores - scores_py) < 1e-7).all() + + # Z-Norm + scores = znorm(my_A, my_B) + scores_py = znorm(my_A, my_B) + assert (abs(scores - scores_py) < 1e-7).all() + +def test_tnorm_simple(): + # 3x5 + my_A = numpy.array([[1, 2, 3, 4, 5], + [6, 7, 8, 9, 8], + [7, 6, 5, 4, 3]],'float64') + # 2x5 + my_C = numpy.array([[5, 4, 3, 2, 1],[2, 1, 2, 3, 4]],'float64') + + zC = tnorm(my_A, my_C) + zC_py = tnorm(my_A, my_C) + assert (abs(zC - zC_py) < 1e-7).all() + + empty = numpy.zeros(shape=(0,0), dtype=numpy.float64) + zC = ztnorm(my_A, empty, my_C, empty) + assert (abs(zC - zC_py) < 1e-7).all() + +def test_znorm_simple(): + # 3x5 + my_A = numpy.array([[1, 2, 3, 4, 5], + [6, 7, 8, 9, 8], + [7, 6, 5, 4, 3]], numpy.float64) + # 3x4 + my_B = numpy.array([[5, 4, 7, 8],[9, 8, 7, 4],[5, 6, 3, 2]], numpy.float64) + + zA = znorm(my_A, my_B) + zA_py = znorm(my_A, my_B) + assert (abs(zA - zA_py) < 1e-7).all() + + empty = numpy.zeros(shape=(0,0), dtype=numpy.float64) + zA = ztnorm(my_A, my_B, empty, empty) + assert (abs(zA - zA_py) < 1e-7).all()