DMP_BBO library
Public Member Functions | Protected Member Functions | Static Protected Member Functions | Friends | List of all members

GMR (Gaussian Mixture Regression) function approximator. More...

#include <FunctionApproximatorGMR.hpp>

Inheritance diagram for FunctionApproximatorGMR:
Inheritance graph
[legend]
Collaboration diagram for FunctionApproximatorGMR:
Collaboration graph
[legend]

Public Member Functions

 FunctionApproximatorGMR (const MetaParametersGMR *const meta_parameters, const ModelParametersGMR *const model_parameters=NULL)
 Initialize a function approximator with meta- and model-parameters. More...
 
 FunctionApproximatorGMR (const ModelParametersGMR *const model_parameters)
 Initialize a function approximator with model parameters. More...
 
virtual FunctionApproximatorclone (void) const
 Return a pointer to a deep copy of the FunctionApproximator object. More...
 
void train (const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets)
 Train the function approximator with corresponding input and target examples. More...
 
void predict (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &output)
 Query the function approximator to make a prediction. More...
 
void predictVariance (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &variances)
 Query the function approximator to get the variance of a prediction This function is not implemented by all function approximators. More...
 
void predict (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &outputs, Eigen::MatrixXd &variances)
 Query the function approximator to make a prediction, and also to predict its variance. More...
 
std::string getName (void) const
 Get the name of this function approximator. More...
 
void predictDot (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &outputs, Eigen::MatrixXd &outputs_dot)
 Query the function approximator to make a prediction and to compute the derivate of that prediction. More...
 
void predictDot (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &outputs, Eigen::MatrixXd &outputs_dot, Eigen::MatrixXd &variances)
 Query the function approximator to make a prediction and to compute the derivate of that prediction, and also to predict its variance. More...
 
void trainIncremental (const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets)
 Train the function approximator incrementally with corresponding input and target examples. More...
 
- Public Member Functions inherited from FunctionApproximator
 FunctionApproximator (const MetaParameters *const meta_parameters, const ModelParameters *const model_parameters=NULL)
 Initialize a function approximator with meta- and optionally model-parameters. More...
 
 FunctionApproximator (const ModelParameters *const model_parameters)
 Initialize a function approximator with model-parameters. More...
 
void train (const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets, std::string save_directory, bool overwrite=false)
 Train the function approximator with corresponding input and target examples (and write results to file). More...
 
void reTrain (const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets)
 Re-train the function approximator with corresponding input and target examples. More...
 
void reTrain (const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets, std::string save_directory, bool overwrite=false)
 Re-train the function approximator with corresponding input and target examples (and write results to file). More...
 
virtual void predict (const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &outputs, std::vector< Eigen::MatrixXd > &variances)
 Query the function approximator to make a prediction, and also to predict its variance. More...
 
bool isTrained (void) const
 Determine whether the function approximator has already been trained with data or not. More...
 
int getExpectedInputDim (void) const
 The expected dimensionality of the input data. More...
 
int getExpectedOutputDim (void) const
 The expected dimensionality of the output data. More...
 
void getSelectableParameters (std::set< std::string > &selected_values_labels) const
 Return all the names of the parameter types that can be selected. More...
 
void setSelectedParameters (const std::set< std::string > &selected_values_labels)
 Determine which subset of parameters is represented in the vector returned by Parameterizable::getParameterVectorSelected. More...
 
void getParameterVectorSelectedMinMax (Eigen::VectorXd &min, Eigen::VectorXd &max) const
 Get the minimum and maximum of the selected parameters in one vector. More...
 
int getParameterVectorSelectedSize (void) const
 Get the size of the vector of selected parameters, as returned by getParameterVectorSelected(. More...
 
void setParameterVectorSelected (const Eigen::VectorXd &values, bool normalized=false)
 Set all the values of the selected parameters with one vector. More...
 
void getParameterVectorSelected (Eigen::VectorXd &values, bool normalized=false) const
 Get the values of the selected parameters in one vector. More...
 
void getParameterVectorMask (const std::set< std::string > selected_values_labels, Eigen::VectorXi &selected_mask) const
 Get a mask for selecting parameters. More...
 
int getParameterVectorAllSize (void) const
 Get the size of the parameter values vector when it contains all available parameter values. More...
 
void getParameterVectorAll (Eigen::VectorXd &values) const
 Return a vector that returns all available parameter values. More...
 
void setParameterVectorAll (const Eigen::VectorXd &values)
 Set all available parameter values with one vector. More...
 
UnifiedModelgetUnifiedModel (void) const
 Return a representation of this function approximator's model as a unified model. More...
 
std::string toString (void) const
 Returns a string representation of the object. More...
 
const MetaParametersgetMetaParameters (void) const
 Accessor for FunctionApproximator::meta_parameters_. More...
 
const ModelParametersgetModelParameters (void) const
 Accessor for FunctionApproximator::model_parameters_. More...
 
void setParameterVectorModifierPrivate (std::string modifier, bool new_value)
 Turn certain modifiers on or off, see Parameterizable::setParameterVectorModifier(). More...
 
virtual bool saveGridData (const Eigen::VectorXd &min, const Eigen::VectorXd &max, const Eigen::VectorXi &n_samples_per_dim, std::string directory, bool overwrite=false) const
 Generate a grid of inputs, and output the response of the basis functions and line segments for these inputs. More...
 
- Public Member Functions inherited from Parameterizable
virtual ~Parameterizable (void)
 Destructor.
 
virtual void getParameterVectorSelectedNormalized (Eigen::VectorXd &values) const
 Get the normalized values of the selected parameters in one vector. More...
 
void getParameterVectorSelectedMinMax (Eigen::VectorXd &min, Eigen::VectorXd &max) const
 Get the minimum and maximum of the selected parameters in one vector. More...
 
void getParameterVectorAllMinMax (Eigen::VectorXd &min, Eigen::VectorXd &max) const
 Get the minimum and maximum values of the current parameter vector. More...
 
void getParameterVectorSelectedRanges (Eigen::VectorXd &ranges) const
 Get the ranges of the selected parameters, i.e. More...
 
virtual void setParameterVectorSelectedNormalized (const Eigen::VectorXd &values)
 Set all the values of the selected parameters with one vector of normalized values. More...
 
void setSelectedParametersOne (std::string selected)
 Set the parameters that are currently selected. More...
 
void setParameterVectorModifier (std::string modifier, bool new_value)
 Turn certain modifiers on or off. More...
 
void setVectorLengthsPerDimension (const Eigen::VectorXi &lengths_per_dimension)
 The vector (VectorXd) with parameter values can be split into different parts (as vector<VectorXd>; this function specifices the length of each sub-vector. More...
 
Eigen::VectorXi getVectorLengthsPerDimension (void) const
 Get the specified length of each vector in each dimension. More...
 
void getParameterVectorSelected (std::vector< Eigen::VectorXd > &values, bool normalized=false) const
 Get the values of the selected parameters in one vector. More...
 
void setParameterVectorSelected (const std::vector< Eigen::VectorXd > &values, bool normalized=false)
 Set all the values of the selected parameters with a vector of vectors. More...
 

Protected Member Functions

void kMeansInit (const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int n_max_iter=1000)
 Initialize Gaussian for EM algorithm using k-means. More...
 
void firstDimSlicingInit (const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars)
 Initialize Gaussian for EM algorithm using a same-size slicing on the first dimension (method used in Calinon GMR implementation). More...
 
void expectationMaximization (const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int n_max_iter=50)
 EM algorithm. More...
 
void expectationMaximizationIncremental (const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int &n_observations, int n_max_iter=50)
 EM algorithm Incremental. More...
 
- Protected Member Functions inherited from FunctionApproximator
void setModelParameters (ModelParameters *model_parameters)
 Accessor for FunctionApproximator::model_parameters_. More...
 
 FunctionApproximator (void)
 Default constructor. More...
 

Static Protected Member Functions

static double normalPDF (const Eigen::VectorXd &mu, const Eigen::MatrixXd &covar, const Eigen::VectorXd &input)
 The probability density function (PDF) of the multi-variate normal distribution. More...
 
static double normalPDFDamped (const Eigen::VectorXd &mu, const Eigen::MatrixXd &covar, const Eigen::VectorXd &input)
 The damped probability density function (PDF) of the multi-variate normal distribution. More...
 

Friends

class boost::serialization::access
 Give boost serialization access to private members. More...
 

Additional Inherited Members

- Static Public Member Functions inherited from FunctionApproximator
static void generateInputsGrid (const Eigen::VectorXd &min, const Eigen::VectorXd &max, const Eigen::VectorXi &n_samples_per_dim, Eigen::MatrixXd &inputs_grid)
 Generate a input samples that lie on a grid (much like Matlab's meshgrid) For instance, if min = [2 6], and max = [3 8], and n_samples_per_dim = [3 5] then this function first makes linearly spaces samples along each dimension, e.g. More...
 

Detailed Description

GMR (Gaussian Mixture Regression) function approximator.

Definition at line 44 of file FunctionApproximatorGMR.hpp.

Constructor & Destructor Documentation

FunctionApproximatorGMR ( const MetaParametersGMR *const  meta_parameters,
const ModelParametersGMR *const  model_parameters = NULL 
)

Initialize a function approximator with meta- and model-parameters.

Parameters
[in]meta_parametersThe training algorithm meta-parameters
[in]model_parametersThe parameters of the trained model. If this parameter is not passed, the function approximator is initialized as untrained. In this case, you must call FunctionApproximator::train() before being able to call FunctionApproximator::predict(). Either meta_parameters XOR model-parameters can passed as NULL, but not both.

Definition at line 51 of file FunctionApproximatorGMR.cpp.

52 :
53  FunctionApproximator(meta_parameters,model_parameters)
54 {
55  // TODO : find a more appropriate place for rand initialization
56  //srand(unsigned(time(0)));
57  if (model_parameters!=NULL)
58  preallocateMatrices(
59  model_parameters->getNumberOfGaussians(),
60  model_parameters->getExpectedInputDim(),
61  model_parameters->getExpectedOutputDim()
62  );
63 }
FunctionApproximator(void)
Default constructor.

Here is the call graph for this function:

FunctionApproximatorGMR ( const ModelParametersGMR *const  model_parameters)

Initialize a function approximator with model parameters.

Parameters
[in]model_parametersThe parameters of the (previously) trained model.

Definition at line 65 of file FunctionApproximatorGMR.cpp.

66 :
67  FunctionApproximator(model_parameters)
68 {
69  preallocateMatrices(
70  model_parameters->getNumberOfGaussians(),
71  model_parameters->getExpectedInputDim(),
72  model_parameters->getExpectedOutputDim()
73  );
74 }
FunctionApproximator(void)
Default constructor.

Here is the call graph for this function:

Member Function Documentation

FunctionApproximator * clone ( void  ) const
virtual

Return a pointer to a deep copy of the FunctionApproximator object.

Returns
Pointer to a deep copy

Implements FunctionApproximator.

Definition at line 93 of file FunctionApproximatorGMR.cpp.

93  {
94  // All error checking and cloning is left to the FunctionApproximator constructor.
95  return new FunctionApproximatorGMR(
96  dynamic_cast<const MetaParametersGMR*>(getMetaParameters()),
97  dynamic_cast<const ModelParametersGMR*>(getModelParameters())
98  );
99 };
FunctionApproximatorGMR(const MetaParametersGMR *const meta_parameters, const ModelParametersGMR *const model_parameters=NULL)
Initialize a function approximator with meta- and model-parameters.
const MetaParameters * getMetaParameters(void) const
Accessor for FunctionApproximator::meta_parameters_.
const ModelParameters * getModelParameters(void) const
Accessor for FunctionApproximator::model_parameters_.

Here is the call graph for this function:

void train ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
const Eigen::Ref< const Eigen::MatrixXd > &  targets 
)
virtual

Train the function approximator with corresponding input and target examples.

Parameters
[in]inputsInput values of the training examples
[in]targetsTarget values of the training examples

Implements FunctionApproximator.

Definition at line 102 of file FunctionApproximatorGMR.cpp.

103 {
104  if (isTrained())
105  {
106  cerr << "WARNING: You may not call FunctionApproximatorGMR::train more than once. Doing nothing." << endl;
107  cerr << " (if you really want to retrain, call reTrain function instead)" << endl;
108  return;
109  }
110 
111  assert(inputs.rows() == targets.rows()); // Must have same number of examples
112  assert(inputs.cols() == getExpectedInputDim());
113 
114  const MetaParametersGMR* meta_parameters_GMR =
115  static_cast<const MetaParametersGMR*>(getMetaParameters());
116 
117  const ModelParametersGMR* model_parameters_GMR =
118  static_cast<const ModelParametersGMR*>(getModelParameters());
119 
120  int n_gaussians;
121  if(meta_parameters_GMR!=NULL)
122  n_gaussians = meta_parameters_GMR->number_of_gaussians_;
123  else if(model_parameters_GMR!=NULL)
124  n_gaussians = model_parameters_GMR->priors_.size();
125  else
126  cerr << "FunctionApproximatorGMR::train Something wrong happened, both ModelParameters and MetaParameters are not initialized." << endl;
127 
128  int n_dims_in = inputs.cols();
129  int n_dims_out = targets.cols();
130  int n_dims_gmm = n_dims_in + n_dims_out;
131 
132  // Initialize the means, priors and covars
133  std::vector<VectorXd> means(n_gaussians);
134  std::vector<MatrixXd> covars(n_gaussians);
135  std::vector<double> priors(n_gaussians);
136  int n_observations = 0;
137 
138  for (int i = 0; i < n_gaussians; i++)
139  {
140  means[i] = VectorXd(n_dims_gmm);
141  priors[i] = 0.0;
142  covars[i] = MatrixXd(n_dims_gmm, n_dims_gmm);
143  }
144 
145  // Put the input/output data in one big matrix
146  MatrixXd data = MatrixXd(inputs.rows(), n_dims_gmm);
147  data << inputs, targets;
148  n_observations = data.rows();
149 
150  // Initialization
151  if (inputs.cols() == 1)
152  firstDimSlicingInit(data, means, priors, covars);
153  else
154  kMeansInit(data, means, priors, covars);
155 
156  // Expectation-Maximization
157  expectationMaximization(data, means, priors, covars);
158 
159  // Extract the different input/output components from the means/covars which contain both
160  std::vector<Eigen::VectorXd> means_x(n_gaussians);
161  std::vector<Eigen::VectorXd> means_y(n_gaussians);
162  std::vector<Eigen::MatrixXd> covars_x(n_gaussians);
163  std::vector<Eigen::MatrixXd> covars_y(n_gaussians);
164  std::vector<Eigen::MatrixXd> covars_y_x(n_gaussians);
165  for (int i_gau = 0; i_gau < n_gaussians; i_gau++)
166  {
167  means_x[i_gau] = means[i_gau].segment(0, n_dims_in);
168  means_y[i_gau] = means[i_gau].segment(n_dims_in, n_dims_out);
169 
170  covars_x[i_gau] = covars[i_gau].block(0, 0, n_dims_in, n_dims_in);
171  covars_y[i_gau] = covars[i_gau].block(n_dims_in, n_dims_in, n_dims_out, n_dims_out);
172  covars_y_x[i_gau] = covars[i_gau].block(n_dims_in, 0, n_dims_out, n_dims_in);
173  }
174 
175  setModelParameters(new ModelParametersGMR(n_observations, priors, means_x, means_y, covars_x, covars_y, covars_y_x));
176 
177  // After training, we know the sizes of the matrices that should be cached
178  preallocateMatrices(n_gaussians,n_dims_in,n_dims_out);
179 
180  // std::vector<VectorXd> centers;
181  // std::vector<MatrixXd> slopes;
182  // std::vector<VectorXd> biases;
183  // std::vector<MatrixXd> inverseCovarsL;
184 
185  // // int n_dims_in = inputs.cols();
186  // // int n_dims_out = targets.cols();
187 
188  // for (int i_gau = 0; i_gau < n_gaussians; i_gau++)
189  // {
190  // centers.push_back(VectorXd(means[i_gau].segment(0, n_dims_in)));
191 
192  // slopes.push_back(MatrixXd(covars[i_gau].block(n_dims_in, 0, n_dims_out, n_dims_in) * covars[i_gau].block(0, 0, n_dims_in, n_dims_in).inverse()));
193 
194  // biases.push_back(VectorXd(means[i_gau].segment(n_dims_in, n_dims_out) -
195  // slopes[i_gau]*means[i_gau].segment(0, n_dims_in)));
196 
197  // MatrixXd L = covars[i_gau].block(0, 0, n_dims_in, n_dims_in).inverse().llt().matrixL();
198  // inverseCovarsL.push_back(MatrixXd(L));
199  // }
200 
201  // setModelParameters(new ModelParametersGMR(centers, priors, slopes, biases, inverseCovarsL));
202 
203  //for (size_t i = 0; i < means.size(); i++)
204  // delete means[i];
205  //for (size_t i = 0; i < covars.size(); i++)
206  //delete covars[i];
207 }
bool isTrained(void) const
Determine whether the function approximator has already been trained with data or not...
void kMeansInit(const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int n_max_iter=1000)
Initialize Gaussian for EM algorithm using k-means.
void firstDimSlicingInit(const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars)
Initialize Gaussian for EM algorithm using a same-size slicing on the first dimension (method used in...
void expectationMaximization(const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int n_max_iter=50)
EM algorithm.
const MetaParameters * getMetaParameters(void) const
Accessor for FunctionApproximator::meta_parameters_.
const ModelParameters * getModelParameters(void) const
Accessor for FunctionApproximator::model_parameters_.
void setModelParameters(ModelParameters *model_parameters)
Accessor for FunctionApproximator::model_parameters_.
int getExpectedInputDim(void) const
The expected dimensionality of the input data.

Here is the call graph for this function:

void predict ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
Eigen::MatrixXd &  outputs 
)
virtual

Query the function approximator to make a prediction.

Parameters
[in]inputsInput values of the query
[out]outputsPredicted output values
Remarks
This method should be const. But third party functions which is called in this function have not always been implemented as const (Examples: LWPRObject::predict or RRRFF::predict ). Therefore, this function cannot be const.

Implements FunctionApproximator.

Definition at line 323 of file FunctionApproximatorGMR.cpp.

324 {
325  ENTERING_REAL_TIME_CRITICAL_CODE
326  outputs.resize(inputs.rows(),getExpectedOutputDim());
327  predict(inputs,outputs,empty_prealloc_);
328  EXITING_REAL_TIME_CRITICAL_CODE
329 }
int getExpectedOutputDim(void) const
The expected dimensionality of the output data.
void predict(const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &output)
Query the function approximator to make a prediction.

Here is the call graph for this function:

void predictVariance ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
Eigen::MatrixXd &  variances 
)
virtual

Query the function approximator to get the variance of a prediction This function is not implemented by all function approximators.

Therefore, the default implementation fills outputs with 0s.

Parameters
[in]inputsInput values of the query (n_samples X n_dims_in)
[out]variancesPredicted variances for the output values (n_samples X n_dims_out). Note that if the output has a dimensionality>1, these variances should actuall be covariance matrices (use function predict(const Eigen::Ref<const Eigen::MatrixXd>& inputs, Eigen::MatrixXd& outputs, std::vector<Eigen::MatrixXd>& variances) to get the full covariance matrices). So for an output dimensionality of 1 this function works fine. For dimensionality>1 we return only the diagional of the covariance matrix, which may not always be what you want.
Remarks
This method should be const. But third party functions which is called in this function have not always been implemented as const (Examples: LWPRObject::predict). Therefore, this function cannot be const.

Reimplemented from FunctionApproximator.

Definition at line 331 of file FunctionApproximatorGMR.cpp.

332 {
333  ENTERING_REAL_TIME_CRITICAL_CODE
334  variances.resize(inputs.rows(),getExpectedOutputDim());
335  predict(inputs,empty_prealloc_,variances);
336  EXITING_REAL_TIME_CRITICAL_CODE
337 }
int getExpectedOutputDim(void) const
The expected dimensionality of the output data.
void predict(const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &output)
Query the function approximator to make a prediction.

Here is the call graph for this function:

void predict ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
Eigen::MatrixXd &  outputs,
Eigen::MatrixXd &  variances 
)
virtual

Query the function approximator to make a prediction, and also to predict its variance.

Parameters
[in]inputsInput values of the query (n_samples X n_dims_in)
[out]outputsPredicted output values (n_samples X n_dims_out)
[out]variancesPredicted variances for the output values (n_samples X n_dims_out). Note that if the output has a dimensionality>1, these variances should actuall be covariance matrices (use function predict(const Eigen::Ref<const Eigen::MatrixXd>& inputs, Eigen::MatrixXd& outputs, std::vector<Eigen::MatrixXd>& variances) to get the full covariance matrices). So for an output dimensionality of 1 this function works fine. For dimensionality>1 we return only the diagional of the covariance matrix, which may not always be what you want.
Remarks
This method should be const. But third party functions which is called in this function have not always been implemented as const (Examples: LWPRObject::predict or RRRFF::predict ). Therefore, this function cannot be const.

Reimplemented from FunctionApproximator.

Definition at line 339 of file FunctionApproximatorGMR.cpp.

340 {
341  ENTERING_REAL_TIME_CRITICAL_CODE
342 
343  // The reason this function is not pretty and so long (which I usually try to avoid) is to
344  // avoid Eigen making dynamic memory allocations. This would cause trouble in real-time critical
345  // code. Therefore, I
346  // * use preallocated matrices (member variables) for intermediate results
347  // * use noalias() whenever needed (http://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html)
348  // * try to avoid calling other functions (a bit tricky when using Eigen matrices as parameters)
349  // I hope the documentation makes up for the ugliness...
350 
351  if (!isTrained())
352  {
353  cerr << "WARNING: You may not call FunctionApproximatorGMR::predict if you have not trained yet. Doing nothing." << endl;
354  return;
355  }
356 
357  const ModelParametersGMR* gmm = static_cast<const ModelParametersGMR*>(getModelParameters());
358 
359  // Number of Gaussians must be at least one
360  assert(gmm->getNumberOfGaussians()>0);
361  // Dimensionality of input must be same as of the gmm inputs
362  assert(gmm->getExpectedInputDim()==inputs.cols());
363 
364  // Only compute the means if the outputs matrix is not empty
365  bool compute_means = false;
366  if (outputs.rows()>0)
367  {
368  outputs.resize(inputs.rows(),gmm->getExpectedOutputDim());
369  outputs.fill(0);
370  compute_means = true;
371  }
372 
373  // Only compute the variances if the variances matrix is not empty
374  bool compute_variance = false;
375  if (variances.rows()>0)
376  {
377  compute_variance = true;
378  variances.resize(inputs.rows(),gmm->getExpectedOutputDim());
379  variances.fill(0);
380  }
381 
382  // For each input, compute the output
383  for (int i_input=0; i_input<inputs.rows(); i_input++)
384  {
385 
386  // Three main steps
387  // A: compute probability: prior * pdf of the multivariate Gaussian
388  // B: compute estimated mean of y: (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
389  // C: weight the estimated mean with the probability
390 
391 
392  // A: compute probability: prior * pdf of the multivariate Gaussian
393  // Compute probalities that each Gaussian would generate this input in 4 steps
394  // A1. Compute the unnormalized pdf of the multi-variate Gaussian distribution
395  // A2. Normalize the unnormalized pdf (scale factor has been precomputed in the GMM)
396  // A3. Multiply the normalized pdf with the priors
397  // A4. Normalize the probabilities by dividing by their sum
398 
399  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
400  {
401  // A1. Compute the unnormalized pdf of the multi-variate Gaussian distribution
402  // formula: exp( -2 * (x-mu)^T * Sigma^-1 * (x-mu) )
403  // (we use cached variables and noalias to avoid dynamic allocation)
404  // (x-mu)
405  diff_prealloc_ = inputs.row(i_input).transpose() - gmm->means_x_[i_gau];
406  // Sigma^-1 * (x-mu)
407  covar_times_diff_prealloc_.noalias() = gmm->covars_x_inv_[i_gau]*diff_prealloc_;
408  // exp( -2 * (x-mu)^T * Sigma^-1 * (x-mu) )
409  probabilities_prealloc_[i_gau] = exp(-0.5*diff_prealloc_.dot(covar_times_diff_prealloc_));
410 
411  // A2. Normalize the unnormalized pdf (scale factor has been precomputed in the GMM)
412  // formula for scale factor: 1/sqrt( (2\pi)^N*|\Sigma| )
413  probabilities_prealloc_[i_gau] *= gmm->mvgd_scale_[i_gau];
414 
415  // A3. Multiply the normalized pdf with the priors
416  probabilities_prealloc_[i_gau] *= gmm->priors_[i_gau];
417 
418  }
419 
420  // A4. Normalize the probabilities by dividing by their sum
421  probabilities_prealloc_ /= probabilities_prealloc_.sum();
422 
423 
424  if (compute_means)
425  {
426  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
427  {
428 
429  // B: compute estimated mean of y: (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
430  // We will compute it bit by bit (with preallocated matrices) to avoid dynamic allocations.
431 
432  // (input-mu_x)
433  diff_prealloc_ = inputs.row(i_input).transpose() - gmm->means_x_[i_gau];
434  // inv(C_x) * (input-mu_x)
435  covar_times_diff_prealloc_.noalias() = gmm->covars_x_inv_[i_gau]*diff_prealloc_;
436  // ( C_y_x * inv(C_x) * (input-mu_x) )
437  mean_output_prealloc_.noalias() = gmm->covars_y_x_[i_gau]*covar_times_diff_prealloc_;
438  // (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
439  mean_output_prealloc_ += gmm->means_y_[i_gau];
440 
441  // C: weight the estimated mean with the probability
442  // probability * (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
443  outputs.row(i_input) += probabilities_prealloc_[i_gau] * mean_output_prealloc_;
444  }
445  }
446 
447  if (compute_variance)
448  {
449  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
450  {
451  // Here comes the formula: h^2 * (C_y - C_y_x * inv(C_x) * C_y_x^T)
452 
453  // inv(C_x) * C_y_x^T
454  covar_input_times_output_.noalias() = gmm->covars_x_inv_[i_gau]*gmm->covars_y_x_[i_gau].transpose();
455  // - C_y_x * inv(C_x) * C_y_x^T
456  covar_output_prealloc_.noalias() = gmm->covars_y_x_[i_gau] * covar_input_times_output_;
457  // NOTE: covar_output_prealloc_.noalias() = - gmm->covars_y_x_[i_gau] * covar_input_times_output_; causes memory allocation
458  // so we split it in two operations
459  covar_output_prealloc_.noalias() = - covar_output_prealloc_;
460  // (C_y - C_y_x * inv(C_x) * C_y_x^T)
461  covar_output_prealloc_ += gmm->covars_y_[i_gau];
462  // h^2 * (C_y - C_y_x * inv(C_x) * C_y_x^T)
463  variances.row(i_input) += probabilities_prealloc_[i_gau]*probabilities_prealloc_[i_gau] * ( covar_output_prealloc_ ).diagonal();
464 
465  // There are cases where we may get slightly negative variances due to numerical issues
466  // Avoid them here by setting negative variances to 0.
467  for (int i_output_dim=0; i_output_dim<gmm->getExpectedOutputDim(); i_output_dim++)
468  if (variances(i_input,i_output_dim)<0.0)
469  variances(i_input,i_output_dim) = 0.0;
470 
471 
472  }
473  }
474  }
475 
476  EXITING_REAL_TIME_CRITICAL_CODE
477 }
bool isTrained(void) const
Determine whether the function approximator has already been trained with data or not...
const ModelParameters * getModelParameters(void) const
Accessor for FunctionApproximator::model_parameters_.

Here is the call graph for this function:

std::string getName ( void  ) const
inlinevirtual

Get the name of this function approximator.

Returns
Name of this function approximator

Implements FunctionApproximator.

Definition at line 72 of file FunctionApproximatorGMR.hpp.

72  {
73  return std::string("GMR");
74  };

Here is the call graph for this function:

void kMeansInit ( const Eigen::MatrixXd &  data,
std::vector< Eigen::VectorXd > &  means,
std::vector< double > &  priors,
std::vector< Eigen::MatrixXd > &  covars,
int  n_max_iter = 1000 
)
protected

Initialize Gaussian for EM algorithm using k-means.

Parameters
[in]dataA data matrix (n_exemples x (n_in_dim + n_out_dim))
[out]meansA list (std::vector) of n_gaussian non initiallized means (n_in_dim + n_out_dim)
[out]priorsA list (std::vector) of n_gaussian non initiallized priors
[out]covarsA list (std::vector) of n_gaussian non initiallized covariance matrices ((n_in_dim + n_out_dim) x (n_in_dim + n_out_dim))
[in]n_max_iterThe maximum number of iterations
Author
Thibaut Munzer

Definition at line 696 of file FunctionApproximatorGMR.cpp.

698 {
699 
700  MatrixXd dataCentered = data.rowwise() - data.colwise().mean();
701  MatrixXd dataCov = dataCentered.transpose() * dataCentered / data.rows();
702  MatrixXd dataCovInverse = dataCov.inverse();
703 
704  std::vector<int> dataIndex;
705  for (int i = 0; i < data.rows(); i++)
706  dataIndex.push_back(i);
707  std::random_shuffle (dataIndex.begin(), dataIndex.end());
708 
709  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
710  centers[i_gau] = data.row(dataIndex[i_gau]);
711 
712  VectorXi assign(data.rows());
713  assign.setZero();
714 
715  bool converged = false;
716  for (int iIter = 0; iIter < n_max_iter && !converged; iIter++)
717  {
718  //cout << " iIter=" << iIter << endl;
719 
720  // E step
721  converged = true;
722  for (int iData = 0; iData < data.rows(); iData++)
723  {
724  VectorXd v = (centers[assign[iData]] - data.row(iData).transpose());
725 
726  double minDist = v.transpose() * dataCovInverse * v;
727 
728  for (int i_gau = 0; i_gau < (int)centers.size(); i_gau++)
729  {
730  if (i_gau == assign[iData])
731  continue;
732 
733  v = (centers[i_gau] - data.row(iData).transpose());
734  double dist = v.transpose() * dataCovInverse * v;
735  if (dist < minDist)
736  {
737  converged = false;
738  minDist = dist;
739  assign[iData] = i_gau;
740  }
741  }
742  }
743 
744  // M step
745  VectorXi nbPoints = VectorXi::Zero(centers.size());
746  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
747  centers[i_gau].setZero();
748  for (int iData = 0; iData < data.rows(); iData++)
749  {
750  centers[assign[iData]] += data.row(iData).transpose();
751  nbPoints[assign[iData]]++;
752  }
753  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
754  centers[i_gau] /= nbPoints[i_gau];
755  }
756 
757  // Init covars
758  VectorXi nbPoints = VectorXi::Zero(centers.size());
759  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
760  covars[i_gau].setZero();
761  for (int iData = 0; iData < data.rows(); iData++)
762  {
763  covars[assign[iData]] += (data.row(iData).transpose() - centers[assign[iData]]) * (data.row(iData).transpose() - centers[assign[iData]]).transpose();
764  nbPoints[assign[iData]]++;
765  }
766  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
767  covars[i_gau] /= nbPoints[i_gau];
768 
769  // Be sure that covar is invertible
770  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
771  covars[i_gau] += MatrixXd::Identity(covars[i_gau].rows(), covars[i_gau].cols()) * 1e-5f;
772 
773  // Init priors
774  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
775  priors[i_gau] = 1. / centers.size();
776 }
void firstDimSlicingInit ( const Eigen::MatrixXd &  data,
std::vector< Eigen::VectorXd > &  means,
std::vector< double > &  priors,
std::vector< Eigen::MatrixXd > &  covars 
)
protected

Initialize Gaussian for EM algorithm using a same-size slicing on the first dimension (method used in Calinon GMR implementation).

Particulary suited when input is 1-D and data distribution is uniform over input dimension

Parameters
[in]dataA data matrix (n_exemples x (n_in_dim + n_out_dim))
[out]meansA list (std::vector) of n_gaussian non initiallized means (n_in_dim + n_out_dim)
[out]priorsA list (std::vector) of n_gaussian non initiallized priors
[out]covarsA list (std::vector) of n_gaussian non initiallized covariance matrices ((n_in_dim + n_out_dim) x (n_in_dim + n_out_dim))
Author
Thibaut Munzer

Definition at line 647 of file FunctionApproximatorGMR.cpp.

649 {
650 
651  VectorXd first_dim = data.col(0);
652 
653  VectorXi assign(data.rows());
654  assign.setZero();
655 
656  double min_val = first_dim.minCoeff();
657  double max_val = first_dim.maxCoeff();
658 
659  for (int i_first_dim = 0; i_first_dim < first_dim.size(); i_first_dim++)
660  {
661  unsigned int center = int((first_dim[i_first_dim]-min_val)/(max_val-min_val)*centers.size());
662  if (center==centers.size())
663  center--;
664  assign[i_first_dim] = center;
665  }
666 
667  // Init means
668  VectorXi nbPoints = VectorXi::Zero(centers.size());
669  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
670  centers[i_gau].setZero();
671  for (int iData = 0; iData < data.rows(); iData++)
672  {
673  centers[assign[iData]] += data.row(iData).transpose();
674  nbPoints[assign[iData]]++;
675  }
676  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
677  centers[i_gau] /= nbPoints[i_gau];
678 
679  // Init covars
680  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
681  covars[i_gau].setZero();
682  for (int iData = 0; iData < data.rows(); iData++)
683  covars[assign[iData]] += (data.row(iData).transpose() - centers[assign[iData]]) * (data.row(iData).transpose() - centers[assign[iData]]).transpose();
684  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
685  covars[i_gau] /= nbPoints[i_gau];
686 
687  // Be sure that covar is invertible
688  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
689  covars[i_gau] += MatrixXd::Identity(covars[i_gau].rows(), covars[i_gau].cols()) * 1e-5;
690 
691  // Init priors
692  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
693  priors[i_gau] = 1. / centers.size();
694 }
void expectationMaximization ( const Eigen::MatrixXd &  data,
std::vector< Eigen::VectorXd > &  means,
std::vector< double > &  priors,
std::vector< Eigen::MatrixXd > &  covars,
int  n_max_iter = 50 
)
protected

EM algorithm.

Parameters
[in]dataA (n_exemples x (n_in_dim + n_out_dim)) data matrix
[in,out]meansA list (std::vector) of n_gaussian means (vector of size (n_in_dim + n_out_dim))
[in,out]priorsA list (std::vector) of n_gaussian priors
[in,out]covarsA list (std::vector) of n_gaussian covariance matrices ((n_in_dim + n_out_dim) x (n_in_dim + n_out_dim))
[in]n_max_iterThe maximum number of iterations
Author
Thibaut Munzer

Definition at line 778 of file FunctionApproximatorGMR.cpp.

780 {
781  MatrixXd assign(centers.size(), data.rows());
782  assign.setZero();
783 
784  std::vector<double> E(centers.size());
785 
786  double oldLoglik = -1e10f;
787  double loglik = 0;
788 
789  for (int iIter = 0; iIter < n_max_iter; iIter++)
790  {
791  //cout << " iIter=" << iIter << endl;
792  // For debugging only
793  //ModelParametersGMR::saveGMM("/tmp/demoTrainFunctionApproximators/GMR",centers,covars,iIter);
794 
795  // E step
796  for (int iData = 0; iData < data.rows(); iData++)
797  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
798  assign(i_gau, iData) = priors[i_gau] * FunctionApproximatorGMR::normalPDF(centers[i_gau], covars[i_gau],data.row(iData).transpose());
799 
800  oldLoglik = loglik;
801  loglik = 0;
802  double sum_tmp = 0.0;
803  for (int iData = 0; iData < data.rows(); iData++)
804  {
805  sum_tmp = assign.col(iData).sum();
806  loglik += log(sum_tmp);
807  }
808  loglik /= data.rows();
809 
810  if (fabs(loglik / oldLoglik - 1) < 1e-8f)
811  break;
812 
813  for (int iData = 0; iData < data.rows(); iData++)
814  assign.col(iData) /= assign.col(iData).sum();
815 
816  if (fabs(loglik / oldLoglik - 1) < 1e-8f)
817  break;
818 
819  // M step
820  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
821  {
822  centers[i_gau].setZero();
823  covars[i_gau].setZero();
824  priors[i_gau] = 0;
825  E[i_gau] = assign.row(i_gau).sum();
826  }
827 
828  for (int iData = 0; iData < data.rows(); iData++)
829  {
830  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
831  {
832  centers[i_gau] += assign(i_gau, iData) * data.row(iData).transpose();
833  priors[i_gau] += assign(i_gau, iData);
834  }
835  }
836 
837  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
838  {
839  centers[i_gau] /= assign.row(i_gau).sum();
840  priors[i_gau] /= assign.cols();
841  }
842 
843  for (int iData = 0; iData < data.rows(); iData++)
844  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
845  covars[i_gau] += assign(i_gau, iData) * (data.row(iData).transpose() - centers[i_gau]) * (data.row(iData).transpose() - centers[i_gau]).transpose();
846 
847  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
848  covars[i_gau] /= assign.row(i_gau).sum();
849 
850  // Be sure that covar is invertible
851  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
852  covars[i_gau] += MatrixXd::Identity(covars[i_gau].rows(), covars[i_gau].cols()) * 1e-5f;
853  }
854 
855  /*
856  Here's a hacky Matlab script for plotting the EM procedure above (if you cann saveGMM)
857  directory = '/tmp/demoTrainFunctionApproximators/GMR/';
858  inputs = load([directory '/inputs.txt']);
859  targets = load([directory '/targets.txt']);
860  outputs = load([directory '/outputs.txt']);
861 
862 
863  plot(inputs,targets,'.k')
864  hold on
865  plot(inputs,outputs,'.r')
866 
867  max_iter = 5;
868  for iter=0:max_iter
869  color = 0.2+(0.8-iter/max_iter)*[1 1 1];
870  for bfs=0:2
871  center = load(sprintf('%s/gmm_iter%02d_mu%03d.txt',directory,iter,bfs));
872  %plot(center(1),center(2))
873  covar = load(sprintf('%s/gmm_iter%02d_covar%03d.txt',directory,iter,bfs));
874  h = error_ellipse(covar,center,'conf',0.95);
875  set(h,'Color',color,'LineWidth',1+iter/max_iter)
876  end
877  end
878  hold off
879  */
880 
881 }
static double normalPDF(const Eigen::VectorXd &mu, const Eigen::MatrixXd &covar, const Eigen::VectorXd &input)
The probability density function (PDF) of the multi-variate normal distribution.

Here is the call graph for this function:

void expectationMaximizationIncremental ( const Eigen::MatrixXd &  data,
std::vector< Eigen::VectorXd > &  means,
std::vector< double > &  priors,
std::vector< Eigen::MatrixXd > &  covars,
int &  n_observations,
int  n_max_iter = 50 
)
protected

EM algorithm Incremental.

Parameters
[in]dataA (n_exemples x (n_in_dim + n_out_dim)) data matrix
[in,out]meansA list (std::vector) of n_gaussian means (vector of size (n_in_dim + n_out_dim))
[in,out]priorsA list (std::vector) of n_gaussian priors
[in,out]covarsA list (std::vector) of n_gaussian covariance matrices ((n_in_dim + n_out_dim) x (n_in_dim + n_out_dim))
[in,out]n_observationsNumber of observations
[in]n_max_iterThe maximum number of iterations
Author
Gennaro Raiola

Definition at line 883 of file FunctionApproximatorGMR.cpp.

885 {
886 
887  std::vector<VectorXd> centers_prev = centers;
888  std::vector<double> priors_prev = priors;
889  std::vector<MatrixXd> covars_prev = covars;
890  int n_observations_prev = n_observations;
891  std::vector<double> E_prev;
892  std::vector<double> E(centers.size());
893  n_observations = data.rows();
894 
895  double oldLoglik = -1e10f;
896  double loglik = 0;
897 
898  MatrixXd assign(centers.size(), data.rows());
899  assign.setZero();
900 
901  // Compute the old E
902  E_prev.resize(centers.size());
903  for (size_t i_gau = 0; i_gau<centers.size(); i_gau++)
904  E_prev[i_gau] = priors_prev[i_gau] * n_observations_prev;
905 
906  for (int iIter = 0; iIter < n_max_iter; iIter++)
907  {
908  //cout << " iIter=" << iIter << endl;
909  // For debugging only
910  //ModelParametersGMR::saveGMM("/tmp/demoTrainFunctionApproximators/GMR",centers,covars,iIter);
911 
912  // E step
913  for (int iData = 0; iData < data.rows(); iData++)
914  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
915  assign(i_gau, iData) = priors[i_gau] * FunctionApproximatorGMR::normalPDFDamped(centers[i_gau], covars[i_gau],data.row(iData).transpose());
916 
917  oldLoglik = loglik;
918  loglik = 0;
919  double sum_tmp = 0.0;
920  for (int iData = 0; iData < data.rows(); iData++)
921  {
922  sum_tmp = assign.col(iData).sum();
923  loglik += log(sum_tmp);
924  }
925  loglik /= data.rows();
926 
927  for (int iData = 0; iData < data.rows(); iData++)
928  assign.col(iData) /= assign.col(iData).sum();
929 
930  if (fabs(loglik / oldLoglik - 1) < 1e-8f)
931  break;
932 
933  // M step
934  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
935  {
936  centers[i_gau].setZero();
937  covars[i_gau].setZero();
938  priors[i_gau] = 0;
939  E[i_gau] = assign.row(i_gau).sum();
940  //E_prev[i_gau] = assign_prev.row(i_gau).sum();
941  }
942 
943  for (int iData = 0; iData < data.rows(); iData++)
944  {
945  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
946  {
947  centers[i_gau] += assign(i_gau, iData) * data.row(iData).transpose();
948  }
949  }
950 
951  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
952  {
953  centers[i_gau] = (E_prev[i_gau] * centers_prev[i_gau] + centers[i_gau])/(E[i_gau] + E_prev[i_gau]);
954  priors[i_gau] = (E[i_gau] + E_prev[i_gau])/(n_observations + n_observations_prev);
955  }
956 
957  for (int iData = 0; iData < data.rows(); iData++)
958  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
959  covars[i_gau] += assign(i_gau, iData) * (data.row(iData).transpose() - centers[i_gau]) * (data.row(iData).transpose() - centers[i_gau]).transpose();
960 
961  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
962  covars[i_gau] = covars[i_gau] + E_prev[i_gau] * (covars_prev[i_gau] + (centers_prev[i_gau] - centers[i_gau]) * (centers_prev[i_gau] - centers[i_gau]).transpose());
963 
964  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
965  covars[i_gau] /= (E[i_gau] + E_prev[i_gau]);
966 
967  // Be sure that covar is invertible
968  for (size_t i_gau = 0; i_gau < centers.size(); i_gau++)
969  covars[i_gau] += MatrixXd::Identity(covars[i_gau].rows(), covars[i_gau].cols()) * 1e-5f;
970  }
971 
972  // Increase the total number of obs counting the old plus the new
973  n_observations += n_observations_prev;
974 }
static double normalPDFDamped(const Eigen::VectorXd &mu, const Eigen::MatrixXd &covar, const Eigen::VectorXd &input)
The damped probability density function (PDF) of the multi-variate normal distribution.

Here is the call graph for this function:

double normalPDF ( const Eigen::VectorXd &  mu,
const Eigen::MatrixXd &  covar,
const Eigen::VectorXd &  input 
)
staticprotected

The probability density function (PDF) of the multi-variate normal distribution.

Parameters
[in]muThe mean of the normal distribution
[in]covarThe covariance matrix of the normal distribution
[in]inputThe input data vector for which the PDF will be computed.
Returns
The PDF value for the input

Definition at line 283 of file FunctionApproximatorGMR.cpp.

284 {
285  MatrixXd covar_inverse = covar.inverse();
286  double output = exp(-0.5*(input-mu).transpose()*covar_inverse*(input-mu));
287  // For invertible matrices (which covar apparently was), det(A^-1) = 1/det(A)
288  // Hence the 1.0/covar_inverse.determinant() below
289  // ( (2*pi)^N*|\Sigma| )^(-1/2)
290  output *= pow(pow(2*M_PI,mu.size())/covar_inverse.determinant(),-0.5);
291  return output;
292 }
double normalPDFDamped ( const Eigen::VectorXd &  mu,
const Eigen::MatrixXd &  covar,
const Eigen::VectorXd &  input 
)
staticprotected

The damped probability density function (PDF) of the multi-variate normal distribution.

Parameters
[in]muThe mean of the normal distribution
[in]covarThe covariance matrix of the normal distribution
[in]inputThe input data vector for which the PDF will be computed.
Returns
The PDF value for the input
Author
Gennaro Raiola
Todo:
Document FunctionApproximatorGMR::normalPDFDamped

Definition at line 297 of file FunctionApproximatorGMR.cpp.

298 {
299  if(covar.determinant() > 0) // It is invertible
300  {
301  MatrixXd covar_inverse = covar.inverse();
302 
303  double output = exp(-0.5*(input-mu).transpose()*covar_inverse*(input-mu));
304 
305  // Check that:
306  // if output == 0.0
307  // return 0.0;
308 
309  // For invertible matrices (which covar apparently was), det(A^-1) = 1/det(A)
310  // Hence the 1.0/covar_inverse.determinant() below
311  // ( (2\pi)^N*|\Sigma| )^(-1/2)
312  output *= pow(pow(2*M_PI,mu.size())/(covar_inverse.determinant()),-0.5);
313  return output;
314  }
315  else
316  {
317  //cerr << "WARNING: FunctionApproximatorGMR::normalPDFDamped output close to singularity..." << endl;
318  return std::numeric_limits<double>::min();
319  }
320 }
void predictDot ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
Eigen::MatrixXd &  outputs,
Eigen::MatrixXd &  outputs_dot 
)

Query the function approximator to make a prediction and to compute the derivate of that prediction.

Parameters
[in]inputsInput values of the query
[out]outputsPredicted output values
[out]outputs_dotPredicted derivate values
Remarks
This method should be const. But third party functions which is called in this function have not always been implemented as const (Examples: LWPRObject::predict or RRRFF::predict ). Therefore, this function cannot be const.

Definition at line 479 of file FunctionApproximatorGMR.cpp.

480 {
481  ENTERING_REAL_TIME_CRITICAL_CODE
482  outputs.resize(inputs.rows(),getExpectedOutputDim());
483  predictDot(inputs,outputs,outputs_dot,empty_prealloc_);
484  EXITING_REAL_TIME_CRITICAL_CODE
485 }
int getExpectedOutputDim(void) const
The expected dimensionality of the output data.
void predictDot(const Eigen::Ref< const Eigen::MatrixXd > &inputs, Eigen::MatrixXd &outputs, Eigen::MatrixXd &outputs_dot)
Query the function approximator to make a prediction and to compute the derivate of that prediction...

Here is the call graph for this function:

void predictDot ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
Eigen::MatrixXd &  outputs,
Eigen::MatrixXd &  outputs_dot,
Eigen::MatrixXd &  variances 
)

Query the function approximator to make a prediction and to compute the derivate of that prediction, and also to predict its variance.

Parameters
[in]inputsInput values of the query (n_samples X n_dims_in)
[out]outputsPredicted output values (n_samples X n_dims_out)
[out]outputs_dotPredicted derivate values
[out]variancesPredicted variances for the output values (n_samples X n_dims_out). Note that if the output has a dimensionality>1, these variances should actuall be covariance matrices (use function predict(const Eigen::Ref<const Eigen::MatrixXd>& inputs, Eigen::MatrixXd& outputs, std::vector<Eigen::MatrixXd>& variances) to get the full covariance matrices). So for an output dimensionality of 1 this function works fine. For dimensionality>1 we return only the diagional of the covariance matrix, which may not always be what you want.
Remarks
This method should be const. But third party functions which is called in this function have not always been implemented as const (Examples: LWPRObject::predict). Therefore, this function cannot be const.

Definition at line 487 of file FunctionApproximatorGMR.cpp.

488 {
489  ENTERING_REAL_TIME_CRITICAL_CODE
490 
491  // The reason this function is not pretty and so long (which I usually try to avoid) is to
492  // avoid Eigen making dynamic memory allocations. This would cause trouble in real-time critical
493  // code. Therefore, I
494  // * use preallocated matrices (member variables) for intermediate results
495  // * use noalias() whenever needed (http://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html)
496  // * try to avoid calling other functions (a bit tricky when using Eigen matrices as parameters)
497  // I hope the documentation makes up for the ugliness...
498 
499  if (!isTrained())
500  {
501  cerr << "WARNING: You may not call FunctionApproximatorGMR::predict if you have not trained yet. Doing nothing." << endl;
502  return;
503  }
504 
505  const ModelParametersGMR* gmm = static_cast<const ModelParametersGMR*>(getModelParameters());
506 
507  // Dimensionality of input must be 1 in order to compute the derivative
508  assert(inputs.cols() == 1);
509  // Number of Gaussians must be at least one
510  assert(gmm->getNumberOfGaussians()>0);
511  // Dimensionality of input must be same as of the gmm inputs
512  assert(gmm->getExpectedInputDim()==inputs.cols());
513 
514  // Only compute the means if the outputs matrix is not empty
515  bool compute_means = false;
516  if (outputs.rows()>0)
517  {
518  outputs.resize(inputs.rows(),gmm->getExpectedOutputDim());
519  outputs_dot.resize(inputs.rows(),gmm->getExpectedOutputDim());
520  outputs.fill(0);
521  outputs_dot.fill(0);
522  compute_means = true;
523  }
524 
525  // Only compute the variances if the variances matrix is not empty
526  bool compute_variance = false;
527  if (variances.rows()>0)
528  {
529  compute_variance = true;
530  variances.resize(inputs.rows(),gmm->getExpectedOutputDim());
531  variances.fill(0);
532  }
533 
534  // For each input, compute the output
535  for (int i_input=0; i_input<inputs.rows(); i_input++)
536  {
537 
538  // Three main steps
539  // A: compute probability: prior * pdf of the multivariate Gaussian
540  // B: compute estimated mean of y: (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
541  // C: weight the estimated mean with the probability
542 
543 
544  // A: compute probability: prior * pdf of the multivariate Gaussian
545  // Compute probalities that each Gaussian would generate this input in 4 steps
546  // A1. Compute the unnormalized pdf of the multi-variate Gaussian distribution
547  // A2. Normalize the unnormalized pdf (scale factor has been precomputed in the GMM)
548  // A3. Multiply the normalized pdf with the priors
549  // A4. Normalize the probabilities by dividing by their sum
550 
551  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
552  {
553  // A1. Compute the unnormalized pdf of the multi-variate Gaussian distribution
554  // formula: exp( -2 * (x-mu)^T * Sigma^-1 * (x-mu) )
555  // (we use cached variables and noalias to avoid dynamic allocation)
556  // (x-mu)
557  diff_prealloc_ = inputs.row(i_input).transpose() - gmm->means_x_[i_gau];
558  // Sigma^-1 * (x-mu)
559  covar_times_diff_prealloc_.noalias() = gmm->covars_x_inv_[i_gau]*diff_prealloc_;
560  // exp( -2 * (x-mu)^T * Sigma^-1 * (x-mu) )
561  probabilities_prealloc_[i_gau] = exp(-0.5*diff_prealloc_.dot(covar_times_diff_prealloc_));
562 
563  // A2. Normalize the unnormalized pdf (scale factor has been precomputed in the GMM)
564  // formula for scale factor: 1/sqrt( (2\pi)^N*|\Sigma| )
565  probabilities_prealloc_[i_gau] *= gmm->mvgd_scale_[i_gau];
566 
567  // A3. This is the derivate of an exponential function, NOTE that we are assuming input dimension equal to one!
568  probabilities_dot_prealloc_[i_gau] *= - probabilities_prealloc_[i_gau] * covar_times_diff_prealloc_(0,0);
569 
570  // A4. Multiply the normalized pdf with the priors
571  probabilities_prealloc_[i_gau] *= gmm->priors_[i_gau];
572 
573  // A5. Repeat the same with the prob. dot
574  probabilities_dot_prealloc_[i_gau] *= gmm->priors_[i_gau];
575 
576  }
577 
578  // A5. Normalize the probabilities by dividing by their sum
579  probabilities_prealloc_sum_ = probabilities_prealloc_.sum();
580  probabilities_prealloc_ /= probabilities_prealloc_sum_;
581 
582  // A6. Compute the derivative of the probability
583  probabilities_dot_prealloc_sum_ = probabilities_dot_prealloc_.sum();
584  probabilities_dot_prealloc_ = (probabilities_dot_prealloc_ * probabilities_prealloc_sum_ - probabilities_prealloc_ * probabilities_dot_prealloc_sum_)/pow(probabilities_prealloc_sum_,2);
585 
586  if (compute_means)
587  {
588  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
589  {
590 
591  // B: compute estimated mean of y: (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
592  // We will compute it bit by bit (with preallocated matrices) to avoid dynamic allocations.
593  // (input-mu_x)
594  diff_prealloc_ = inputs.row(i_input).transpose() - gmm->means_x_[i_gau];
595  // inv(C_x) * (input-mu_x)
596  covar_times_diff_prealloc_.noalias() = gmm->covars_x_inv_[i_gau]*diff_prealloc_;
597  // ( C_y_x * inv(C_x) * (input-mu_x) )
598  mean_output_prealloc_.noalias() = gmm->covars_y_x_[i_gau]*covar_times_diff_prealloc_;
599  // (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
600  mean_output_prealloc_ += gmm->means_y_[i_gau];
601 
602  // C: weight the estimated mean with the probability
603  // probability * (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) )
604  outputs.row(i_input) += probabilities_prealloc_[i_gau] * mean_output_prealloc_;
605 
606  // D: compute the derivate of the output
607  // (C_y_x * inv(C_x))
608  covar_output_times_input_.noalias() = gmm->covars_y_x_[i_gau] * gmm->covars_x_inv_[i_gau];
609  // probability_dot * (mu_y + ( C_y_x * inv(C_x) * (input-mu_x) ) ) + probability * (C_y_x * inv(C_x))
610  outputs_dot.row(i_input) += probabilities_dot_prealloc_[i_gau] * mean_output_prealloc_ + probabilities_prealloc_[i_gau] * covar_output_times_input_;
611 
612  }
613  }
614 
615  if (compute_variance)
616  {
617  for (unsigned int i_gau=0; i_gau<gmm->getNumberOfGaussians(); i_gau++)
618  {
619  // Here comes the formula: h^2 * (C_y - C_y_x * inv(C_x) * C_y_x^T)
620 
621  // inv(C_x) * C_y_x^T
622  covar_input_times_output_.noalias() = gmm->covars_x_inv_[i_gau]*gmm->covars_y_x_[i_gau].transpose();
623  // - C_y_x * inv(C_x) * C_y_x^T
624  covar_output_prealloc_.noalias() = gmm->covars_y_x_[i_gau] * covar_input_times_output_;
625  // NOTE: covar_output_prealloc_.noalias() = - gmm->covars_y_x_[i_gau] * covar_input_times_output_; causes memory allocation
626  // so we split it in two operations
627  covar_output_prealloc_.noalias() = - covar_output_prealloc_;
628  // (C_y - C_y_x * inv(C_x) * C_y_x^T)
629  covar_output_prealloc_ += gmm->covars_y_[i_gau];
630  // h^2 * (C_y - C_y_x * inv(C_x) * C_y_x^T)
631  variances.row(i_input) += probabilities_prealloc_[i_gau]*probabilities_prealloc_[i_gau] * ( covar_output_prealloc_ ).diagonal();
632 
633  // There are cases where we may get slightly negative variances due to numerical issues
634  // Avoid them here by setting negative variances to 0.
635  for (int i_output_dim=0; i_output_dim<gmm->getExpectedOutputDim(); i_output_dim++)
636  if (variances(i_input,i_output_dim)<0.0)
637  variances(i_input,i_output_dim) = 0.0;
638 
639 
640  }
641  }
642  }
643 
644  EXITING_REAL_TIME_CRITICAL_CODE
645 }
bool isTrained(void) const
Determine whether the function approximator has already been trained with data or not...
const ModelParameters * getModelParameters(void) const
Accessor for FunctionApproximator::model_parameters_.

Here is the call graph for this function:

void trainIncremental ( const Eigen::Ref< const Eigen::MatrixXd > &  inputs,
const Eigen::Ref< const Eigen::MatrixXd > &  targets 
)

Train the function approximator incrementally with corresponding input and target examples.

Parameters
[in]inputsInput values of the training examples
[in]targetsTarget values of the training examples
Todo:
Document FunctionApproximatorGMR::trainIncremental

Definition at line 211 of file FunctionApproximatorGMR.cpp.

212 {
213  if (!isTrained())
214  {
215  //cout << " Training for the first time... " << endl;
216  train(inputs,targets);
217  return;
218  }
219 
220  const ModelParametersGMR* model_parameters_GMR = static_cast<const ModelParametersGMR*>(getModelParameters());
221 
222 
223  int n_gaussians = model_parameters_GMR->priors_.size();
224  int n_dims_in = inputs.cols();
225  int n_dims_out = targets.cols();
226  int n_dims_gmm = n_dims_in + n_dims_out;
227 
228  // Initialize the means, priors and covars
229  std::vector<VectorXd> means(n_gaussians);
230  std::vector<MatrixXd> covars(n_gaussians);
231  std::vector<double> priors(n_gaussians);
232  int n_observations = 0;
233  for (int i = 0; i < n_gaussians; i++)
234  {
235  means[i] = VectorXd(n_dims_gmm);
236  priors[i] = 0.0;
237  covars[i] = MatrixXd(n_dims_gmm, n_dims_gmm);
238  }
239 
240  // Extract the model parameters
241  for (int i = 0; i < n_gaussians; i++)
242  {
243  means[i].segment(0, n_dims_in) = model_parameters_GMR->means_x_[i];
244  means[i].segment(n_dims_in, n_dims_out) = model_parameters_GMR->means_y_[i];
245 
246  covars[i].block(0, 0, n_dims_in, n_dims_in) = model_parameters_GMR->covars_x_[i];
247  covars[i].block(n_dims_in, n_dims_in, n_dims_out, n_dims_out) = model_parameters_GMR->covars_y_[i];
248  covars[i].block(n_dims_in, 0, n_dims_out, n_dims_in) = model_parameters_GMR->covars_y_x_[i];
249 
250  priors[i] = model_parameters_GMR->priors_[i];
251  }
252  n_observations = model_parameters_GMR->n_observations_;
253 
254  // Put the input/output data in one big matrix
255  MatrixXd data = MatrixXd(inputs.rows(), n_dims_gmm);
256  data << inputs, targets;
257 
258  // Expectation-Maximization Incremental
259  expectationMaximizationIncremental(data, means, priors, covars, n_observations);
260 
261  // Extract the different input/output components from the means/covars which contain both
262  std::vector<Eigen::VectorXd> means_x(n_gaussians);
263  std::vector<Eigen::VectorXd> means_y(n_gaussians);
264  std::vector<Eigen::MatrixXd> covars_x(n_gaussians);
265  std::vector<Eigen::MatrixXd> covars_y(n_gaussians);
266  std::vector<Eigen::MatrixXd> covars_y_x(n_gaussians);
267  for (int i_gau = 0; i_gau < n_gaussians; i_gau++)
268  {
269  means_x[i_gau] = means[i_gau].segment(0, n_dims_in);
270  means_y[i_gau] = means[i_gau].segment(n_dims_in, n_dims_out);
271 
272  covars_x[i_gau] = covars[i_gau].block(0, 0, n_dims_in, n_dims_in);
273  covars_y[i_gau] = covars[i_gau].block(n_dims_in, n_dims_in, n_dims_out, n_dims_out);
274  covars_y_x[i_gau] = covars[i_gau].block(n_dims_in, 0, n_dims_out, n_dims_in);
275  }
276 
277  setModelParameters(new ModelParametersGMR(n_observations, priors, means_x, means_y, covars_x, covars_y, covars_y_x));
278 
279  // After training, we know the sizes of the matrices that should be cached
280  preallocateMatrices(n_gaussians,n_dims_in,n_dims_out);
281 }
void train(const Eigen::Ref< const Eigen::MatrixXd > &inputs, const Eigen::Ref< const Eigen::MatrixXd > &targets)
Train the function approximator with corresponding input and target examples.
bool isTrained(void) const
Determine whether the function approximator has already been trained with data or not...
const ModelParameters * getModelParameters(void) const
Accessor for FunctionApproximator::model_parameters_.
void setModelParameters(ModelParameters *model_parameters)
Accessor for FunctionApproximator::model_parameters_.
void expectationMaximizationIncremental(const Eigen::MatrixXd &data, std::vector< Eigen::VectorXd > &means, std::vector< double > &priors, std::vector< Eigen::MatrixXd > &covars, int &n_observations, int n_max_iter=50)
EM algorithm Incremental.

Here is the call graph for this function:

Friends And Related Function Documentation

friend class boost::serialization::access
friend

Give boost serialization access to private members.

Definition at line 177 of file FunctionApproximatorGMR.hpp.


The documentation for this class was generated from the following files: