[mlpack-svn] r15902 - mlpack/trunk/src/mlpack/methods/gmm

fastlab-svn at coffeetalk-1.cc.gatech.edu fastlab-svn at coffeetalk-1.cc.gatech.edu
Wed Oct 2 17:32:33 EDT 2013


Author: rcurtin
Date: Wed Oct  2 17:32:33 2013
New Revision: 15902

Log:
Refactor EMFit<> so that the covariance constraint is a template parameter, and
update other components accordingly.


Added:
   mlpack/trunk/src/mlpack/methods/gmm/no_constraint.hpp
   mlpack/trunk/src/mlpack/methods/gmm/positive_definite_covariance.hpp
Modified:
   mlpack/trunk/src/mlpack/methods/gmm/CMakeLists.txt
   mlpack/trunk/src/mlpack/methods/gmm/em_fit.hpp
   mlpack/trunk/src/mlpack/methods/gmm/em_fit_impl.hpp
   mlpack/trunk/src/mlpack/methods/gmm/gmm_main.cpp

Modified: mlpack/trunk/src/mlpack/methods/gmm/CMakeLists.txt
==============================================================================
--- mlpack/trunk/src/mlpack/methods/gmm/CMakeLists.txt	(original)
+++ mlpack/trunk/src/mlpack/methods/gmm/CMakeLists.txt	Wed Oct  2 17:32:33 2013
@@ -3,7 +3,9 @@
 set(SOURCES
   gmm.hpp
   gmm_impl.hpp
+  no_constraint.hpp
   phi.hpp
+  positive_definite_covariance.hpp
   em_fit.hpp
   em_fit_impl.hpp
 )

Modified: mlpack/trunk/src/mlpack/methods/gmm/em_fit.hpp
==============================================================================
--- mlpack/trunk/src/mlpack/methods/gmm/em_fit.hpp	(original)
+++ mlpack/trunk/src/mlpack/methods/gmm/em_fit.hpp	Wed Oct  2 17:32:33 2013
@@ -12,6 +12,8 @@
 
 // Default clustering mechanism.
 #include <mlpack/methods/kmeans/kmeans.hpp>
+// Default covariance matrix constraint.
+#include "positive_definite_covariance.hpp"
 
 namespace mlpack {
 namespace gmm {
@@ -29,7 +31,8 @@
  * This method should create 'clusters' clusters, and return the assignment of
  * each point to a cluster.
  */
-template<typename InitialClusteringType = kmeans::KMeans<> >
+template<typename InitialClusteringType = kmeans::KMeans<>,
+         typename CovarianceConstraintPolicy = PositiveDefiniteCovariance>
 class EMFit
 {
  public:
@@ -52,8 +55,8 @@
    */
   EMFit(const size_t maxIterations = 300,
         const double tolerance = 1e-10,
-        const bool forcePositive = true,
-        InitialClusteringType clusterer = InitialClusteringType());
+        InitialClusteringType clusterer = InitialClusteringType(),
+        CovarianceConstraintPolicy constraint = CovarianceConstraintPolicy());
 
   /**
    * Fit the observations to a Gaussian mixture model (GMM) using the EM
@@ -93,6 +96,11 @@
   //! Modify the clusterer.
   InitialClusteringType& Clusterer() { return clusterer; }
 
+  //! Get the covariance constraint policy class.
+  const CovarianceConstraintPolicy& Constraint() const { return constraint; }
+  //! Modify the covariance constraint policy class.
+  CovarianceConstraintPolicy& Constraint() { return constraint; }
+
   //! Get the maximum number of iterations of the EM algorithm.
   size_t MaxIterations() const { return maxIterations; }
   //! Modify the maximum number of iterations of the EM algorithm.
@@ -103,13 +111,6 @@
   //! Modify the tolerance for the convergence of the EM algorithm.
   double& Tolerance() { return tolerance; }
 
-  //! Get whether or not the covariance matrices are forced to be positive
-  //! definite.
-  bool ForcePositive() const { return forcePositive; }
-  //! Modify whether or not the covariance matrices are forced to be positive
-  //! definite.
-  bool& ForcePositive() { return forcePositive; }
-
  private:
   /**
    * Run the clusterer, and then turn the cluster assignments into Gaussians.
@@ -145,10 +146,10 @@
   size_t maxIterations;
   //! Tolerance for convergence of EM.
   double tolerance;
-  //! Whether or not to force positive definiteness of covariance matrices.
-  bool forcePositive;
   //! Object which will perform the clustering.
   InitialClusteringType clusterer;
+  //! Object which applies constraints to the covariance matrix.
+  CovarianceConstraintPolicy constraint;
 };
 
 }; // namespace gmm

Modified: mlpack/trunk/src/mlpack/methods/gmm/em_fit_impl.hpp
==============================================================================
--- mlpack/trunk/src/mlpack/methods/gmm/em_fit_impl.hpp	(original)
+++ mlpack/trunk/src/mlpack/methods/gmm/em_fit_impl.hpp	Wed Oct  2 17:32:33 2013
@@ -17,22 +17,24 @@
 namespace gmm {
 
 //! Constructor.
-template<typename InitialClusteringType>
-EMFit<InitialClusteringType>::EMFit(const size_t maxIterations,
-                                    const double tolerance,
-                                    const bool forcePositive,
-                                    InitialClusteringType clusterer) :
+template<typename InitialClusteringType, typename CovarianceConstraintPolicy>
+EMFit<InitialClusteringType, CovarianceConstraintPolicy>::EMFit(
+    const size_t maxIterations,
+    const double tolerance,
+    InitialClusteringType clusterer,
+    CovarianceConstraintPolicy constraint) :
     maxIterations(maxIterations),
     tolerance(tolerance),
-    forcePositive(forcePositive),
-    clusterer(clusterer)
+    clusterer(clusterer),
+    constraint(constraint)
 { /* Nothing to do. */ }
 
-template<typename InitialClusteringType>
-void EMFit<InitialClusteringType>::Estimate(const arma::mat& observations,
-                                            std::vector<arma::vec>& means,
-                                            std::vector<arma::mat>& covariances,
-                                            arma::vec& weights)
+template<typename InitialClusteringType, typename CovarianceConstraintPolicy>
+void EMFit<InitialClusteringType, CovarianceConstraintPolicy>::Estimate(
+    const arma::mat& observations,
+    std::vector<arma::vec>& means,
+    std::vector<arma::mat>& covariances,
+    arma::vec& weights)
 {
   InitialClustering(observations, means, covariances, weights);
 
@@ -94,19 +96,8 @@
       if (probRowSums[i] != 0.0)
         covariances[i] = (tmp * trans(tmpB)) / probRowSums[i];
 
-      // Ensure positive-definiteness.  TODO: make this more efficient.
-      if (forcePositive && det(covariances[i]) <= 1e-50)
-      {
-        Log::Debug << "Covariance matrix " << i << " is not positive definite. "
-            << "Adding perturbation." << std::endl;
-
-        double perturbation = 1e-30;
-        while (det(covariances[i]) <= 1e-50)
-        {
-          covariances[i].diag() += perturbation;
-          perturbation *= 10; // Slow, but we don't want to add too much.
-        }
-      }
+      // Apply covariance constraint.
+      constraint.ApplyConstraint(covariances[i]);
     }
 
     // Calculate the new values for omega using the updated conditional
@@ -121,12 +112,13 @@
   }
 }
 
-template<typename InitialClusteringType>
-void EMFit<InitialClusteringType>::Estimate(const arma::mat& observations,
-                                            const arma::vec& probabilities,
-                                            std::vector<arma::vec>& means,
-                                            std::vector<arma::mat>& covariances,
-                                            arma::vec& weights)
+template<typename InitialClusteringType, typename CovarianceConstraintPolicy>
+void EMFit<InitialClusteringType, CovarianceConstraintPolicy>::Estimate(
+    const arma::mat& observations,
+    const arma::vec& probabilities,
+    std::vector<arma::vec>& means,
+    std::vector<arma::mat>& covariances,
+    arma::vec& weights)
 {
   InitialClustering(observations, means, covariances, weights);
 
@@ -189,19 +181,8 @@
 
       covariances[i] = (tmp * trans(tmpB)) / probRowSums[i];
 
-      // Ensure positive-definiteness.  TODO: make this more efficient.
-      if (forcePositive && det(covariances[i]) <= 1e-50)
-      {
-        Log::Debug << "Covariance matrix " << i << " is not positive definite. "
-            << "Adding perturbation." << std::endl;
-
-        double perturbation = 1e-30;
-        while (det(covariances[i]) <= 1e-50)
-        {
-          covariances[i].diag() += perturbation;
-          perturbation *= 10; // Slow, but we don't want to add too much.
-        }
-      }
+      // Apply covariance constraint.
+      constraint.ApplyConstraint(covariances[i]);
     }
 
     // Calculate the new values for omega using the updated conditional
@@ -216,12 +197,12 @@
   }
 }
 
-template<typename InitialClusteringType>
-void EMFit<InitialClusteringType>::InitialClustering(
-    const arma::mat& observations,
-    std::vector<arma::vec>& means,
-    std::vector<arma::mat>& covariances,
-    arma::vec& weights)
+template<typename InitialClusteringType, typename CovarianceConstraintPolicy>
+void EMFit<InitialClusteringType, CovarianceConstraintPolicy>::
+InitialClustering(const arma::mat& observations,
+                  std::vector<arma::vec>& means,
+                  std::vector<arma::mat>& covariances,
+                  arma::vec& weights)
 {
   // Assignments from clustering.
   arma::Col<size_t> assignments;
@@ -272,27 +253,16 @@
   {
     covariances[i] /= (weights[i] > 1) ? weights[i] : 1;
 
-    // Ensure positive-definiteness.  TODO: make this more efficient.
-    if (forcePositive && det(covariances[i]) <= 1e-50)
-    {
-      Log::Debug << "Covariance matrix " << i << " is not positive definite. "
-          << "Adding perturbation." << std::endl;
-
-      double perturbation = 1e-50;
-      while (det(covariances[i]) <= 1e-50)
-      {
-        covariances[i].diag() += perturbation;
-        perturbation *= 10; // Slow, but we don't want to add too much.
-      }
-    }
+    // Apply constraints to covariance matrix.
+    constraint.ApplyConstraint(covariances[i]);
   }
 
   // Finally, normalize weights.
   weights /= accu(weights);
 }
 
-template<typename InitialClusteringType>
-double EMFit<InitialClusteringType>::LogLikelihood(
+template<typename InitialClusteringType, typename CovarianceConstraintPolicy>
+double EMFit<InitialClusteringType, CovarianceConstraintPolicy>::LogLikelihood(
     const arma::mat& observations,
     const std::vector<arma::vec>& means,
     const std::vector<arma::mat>& covariances,

Modified: mlpack/trunk/src/mlpack/methods/gmm/gmm_main.cpp
==============================================================================
--- mlpack/trunk/src/mlpack/methods/gmm/gmm_main.cpp	(original)
+++ mlpack/trunk/src/mlpack/methods/gmm/gmm_main.cpp	Wed Oct  2 17:32:33 2013
@@ -7,6 +7,7 @@
 #include <mlpack/core.hpp>
 
 #include "gmm.hpp"
+#include "no_constraint.hpp"
 
 #include <mlpack/methods/kmeans/refined_start.hpp>
 
@@ -14,6 +15,7 @@
 using namespace mlpack::gmm;
 using namespace mlpack::util;
 using namespace mlpack::kmeans;
+using namespace std;
 
 PROGRAM_INFO("Gaussian Mixture Model (GMM) Training",
     "This program takes a parametric estimate of a Gaussian mixture model (GMM)"
@@ -73,7 +75,7 @@
     math::RandomSeed((size_t) std::time(NULL));
 
   arma::mat dataPoints;
-  data::Load(CLI::GetParam<std::string>("input_file"), dataPoints,
+  data::Load(CLI::GetParam<string>("input_file"), dataPoints,
       true);
 
   const int gaussians = CLI::GetParam<int>("gaussians");
@@ -121,35 +123,74 @@
     KMeansType k(1000, 1.0, metric::SquaredEuclideanDistance(),
         RefinedStart(samplings, percentage));
 
-    EMFit<KMeansType> em(maxIterations, tolerance, forcePositive, k);
-
-    GMM<EMFit<KMeansType> > gmm(size_t(gaussians), dataPoints.n_rows, em);
-
-    // Compute the parameters of the model using the EM algorithm.
-    Timer::Start("em");
-    likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
-    Timer::Stop("em");
-
-    // Save results.
-    gmm.Save(CLI::GetParam<std::string>("output_file"));
+    // Depending on the value of 'forcePositive', we have to use different
+    // types.
+    if (forcePositive)
+    {
+      EMFit<KMeansType> em(maxIterations, tolerance, k);
+
+      GMM<EMFit<KMeansType> > gmm(size_t(gaussians), dataPoints.n_rows, em);
+
+      // Compute the parameters of the model using the EM algorithm.
+      Timer::Start("em");
+      likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
+      Timer::Stop("em");
+
+      // Save results.
+      gmm.Save(CLI::GetParam<string>("output_file"));
+    }
+    else
+    {
+      EMFit<KMeansType, NoConstraint> em(maxIterations, tolerance, k);
+
+      GMM<EMFit<KMeansType, NoConstraint> > gmm(size_t(gaussians),
+          dataPoints.n_rows, em);
+
+      // Compute the parameters of the model using the EM algorithm.
+      Timer::Start("em");
+      likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
+      Timer::Stop("em");
+
+      // Save results.
+      gmm.Save(CLI::GetParam<string>("output_file"));
+    }
   }
   else
   {
-    EMFit<> em(maxIterations, tolerance, forcePositive);
-
-    // Calculate mixture of Gaussians.
-    GMM<> gmm(size_t(gaussians), dataPoints.n_rows, em);
-
-    // Compute the parameters of the model using the EM algorithm.
-    Timer::Start("em");
-    likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
-    Timer::Stop("em");
-
-    // Save results.
-    gmm.Save(CLI::GetParam<std::string>("output_file"));
+    // Depending on the value of forcePositive, we have to use different types.
+    if (forcePositive)
+    {
+      EMFit<> em(maxIterations, tolerance);
+
+      // Calculate mixture of Gaussians.
+      GMM<> gmm(size_t(gaussians), dataPoints.n_rows, em);
+
+      // Compute the parameters of the model using the EM algorithm.
+      Timer::Start("em");
+      likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
+      Timer::Stop("em");
+
+      // Save results.
+      gmm.Save(CLI::GetParam<string>("output_file"));
+    }
+    else
+    {
+      // Use no constraints on the covariance matrix.
+      EMFit<KMeans<>, NoConstraint> em(maxIterations, tolerance);
+
+      // Calculate mixture of Gaussians.
+      GMM<EMFit<KMeans<>, NoConstraint> > gmm(size_t(gaussians),
+          dataPoints.n_rows, em);
+
+      // Compute the parameters of the model using the EM algorithm.
+      Timer::Start("em");
+      likelihood = gmm.Estimate(dataPoints, CLI::GetParam<int>("trials"));
+      Timer::Stop("em");
+
+      // Save results.
+      gmm.Save(CLI::GetParam<string>("output_file"));
+    }
   }
 
   Log::Info << "Log-likelihood of estimate: " << likelihood << ".\n";
-
-
 }

Added: mlpack/trunk/src/mlpack/methods/gmm/no_constraint.hpp
==============================================================================
--- (empty file)
+++ mlpack/trunk/src/mlpack/methods/gmm/no_constraint.hpp	Wed Oct  2 17:32:33 2013
@@ -0,0 +1,30 @@
+/**
+ * @file no_constraint.hpp
+ * @author Ryan Curtin
+ *
+ * No constraint on the covariance matrix.
+ */
+#ifndef __MLPACK_METHODS_GMM_NO_CONSTRAINT_HPP
+#define __MLPACK_METHODS_GMM_NO_CONSTRAINT_HPP
+
+#include <mlpack/core.hpp>
+
+namespace mlpack {
+namespace gmm {
+
+/**
+ * This class enforces no constraint on the covariance matrix.  It's faster this
+ * way, although depending on your situation you may end up with a
+ * non-invertible covariance matrix.
+ */
+class NoConstraint
+{
+ public:
+  //! Do nothing, and do not modify the covariance matrix.
+  void ApplyConstraint(const arma::mat& /* covariance */) { }
+};
+
+}; // namespace gmm
+}; // namespace mlpack
+
+#endif

Added: mlpack/trunk/src/mlpack/methods/gmm/positive_definite_covariance.hpp
==============================================================================
--- (empty file)
+++ mlpack/trunk/src/mlpack/methods/gmm/positive_definite_covariance.hpp	Wed Oct  2 17:32:33 2013
@@ -0,0 +1,45 @@
+/**
+ * @file positive_definite_covariance.hpp
+ * @author Ryan Curtin
+ *
+ * Restricts a covariance matrix to being positive definite.
+ */
+#ifndef __MLPACK_METHODS_GMM_POSITIVE_DEFINITE_COVARIANCE_HPP
+#define __MLPACK_METHODS_GMM_POSITIVE_DEFINITE_COVARIANCE_HPP
+
+namespace mlpack {
+namespace gmm {
+
+/**
+ * Given a covariance matrix, force the matrix to be positive definite.
+ */
+class PositiveDefiniteCovariance
+{
+ public:
+  /**
+   * Apply the positive definiteness constraint to the given covariance matrix.
+   *
+   * @param covariance Covariance matrix.
+   */
+  static void ApplyConstraint(arma::mat& covariance)
+  {
+    // TODO: make this more efficient.
+    if (det(covariance) <= 1e-50)
+    {
+      Log::Debug << "Covariance matrix is not positive definite.  Adding "
+          << "perturbation." << std::endl;
+
+      double perturbation = 1e-30;
+      while (det(covariance) <= 1e-50)
+      {
+        covariance.diag() += perturbation;
+        perturbation *= 10;
+      }
+    }
+  }
+};
+
+}; // namespace gmm
+}; // namespace mlpack
+
+#endif



More information about the mlpack-svn mailing list