[mlpack-svn] r14560 - mlpack/trunk/src/mlpack/methods/hmm

fastlab-svn at coffeetalk-1.cc.gatech.edu fastlab-svn at coffeetalk-1.cc.gatech.edu
Thu Mar 14 17:17:56 EDT 2013


Author: rcurtin
Date: 2013-03-14 17:17:55 -0400 (Thu, 14 Mar 2013)
New Revision: 14560

Modified:
   mlpack/trunk/src/mlpack/methods/hmm/hmm.hpp
   mlpack/trunk/src/mlpack/methods/hmm/hmm_impl.hpp
Log:
Clarify documentation significantly on Train().  Only allow setting of
dimensionality in constructor and by hand, to prevent unexpected behavior in
Train().  Check dimensionality of observation sequences in Train().


Modified: mlpack/trunk/src/mlpack/methods/hmm/hmm.hpp
===================================================================
--- mlpack/trunk/src/mlpack/methods/hmm/hmm.hpp	2013-03-14 18:50:06 UTC (rev 14559)
+++ mlpack/trunk/src/mlpack/methods/hmm/hmm.hpp	2013-03-14 21:17:55 UTC (rev 14560)
@@ -77,17 +77,13 @@
 template<typename Distribution = distribution::DiscreteDistribution>
 class HMM
 {
- private:
-  //! Transition probability matrix.
-  arma::mat transition;
-
-  //! Set of emission probability distributions; one for each state.
-  std::vector<Distribution> emission;
-
  public:
   /**
    * Create the Hidden Markov Model with the given number of hidden states and
-   * the given default distribution for emissions.
+   * the given default distribution for emissions.  The dimensionality of the
+   * observations is taken from the emissions variable, so it is important that
+   * the given default emission distribution is set with the correct
+   * dimensionality.  Alternately, set the dimensionality with Dimensionality().
    *
    * @param states Number of states.
    * @param emissions Default distribution for emissions.
@@ -96,7 +92,9 @@
 
   /**
    * Create the Hidden Markov Model with the given transition matrix and the
-   * given emission distributions.
+   * given emission distributions.  The dimensionality of the observations of
+   * the HMM are taken from the given emission distributions.  Alternately, the
+   * dimensionality can be set with Dimensionality().
    *
    * The transition matrix should be such that T(i, j) is the probability of
    * transition to state i from state j.  The columns of the matrix should sum
@@ -113,8 +111,18 @@
   /**
    * Train the model using the Baum-Welch algorithm, with only the given
    * unlabeled observations.  Instead of giving a guess transition and emission
-   * matrix here, do that in the constructor.
+   * matrix here, do that in the constructor.  Each matrix in the vector of data
+   * sequences holds an individual data sequence; each point in each individual
+   * data sequence should be a column in the matrix.  The number of rows in each
+   * matrix should be equal to the dimensionality of the HMM (which is set in
+   * the constructor).
    *
+   * It is preferable to use the other overload of Train(), with labeled data.
+   * That will produce much better results.  However, if labeled data is
+   * unavailable, this will work.  In addition, it is possible to use Train()
+   * with labeled data first, and then continue to train the model using this
+   * overload of Train() with unlabeled data.
+   *
    * @note
    * Train() can be called multiple times with different sequences; each time it
    * is called, it uses the current parameters of the HMM as a starting point
@@ -127,7 +135,14 @@
 
   /**
    * Train the model using the given labeled observations; the transition and
-   * emission matrices are directly estimated.
+   * emission matrices are directly estimated.  Each matrix in the vector of
+   * data sequences corresponds to a vector in the vector of state sequences.
+   * Each point in each individual data sequence should be a column in the
+   * matrix, and its state should be the corresponding element in the state
+   * sequence vector.  For instance, dataSeq[0].col(3) corresponds to the fourth
+   * observation in the first data sequence, and its state is stateSeq[0][3].
+   * The number of rows in each matrix should be equal to the dimensionality of
+   * the HMM (which is set in the constructor).
    *
    * @note
    * Train() can be called multiple times with different sequences; each time it
@@ -137,7 +152,7 @@
    *
    * @param dataSeq Vector of observation sequences.
    * @param stateSeq Vector of state sequences, corresponding to each
-   *    observation.
+   *     observation.
    */
   void Train(const std::vector<arma::mat>& dataSeq,
              const std::vector<arma::Col<size_t> >& stateSeq);
@@ -182,8 +197,9 @@
 
   /**
    * Generate a random data sequence of the given length.  The data sequence is
-   * stored in the data_sequence parameter, and the state sequence is stored in
-   * the state_sequence parameter.
+   * stored in the dataSequence parameter, and the state sequence is stored in
+   * the stateSequence parameter.  Each column of dataSequence represents a
+   * random observation.
    *
    * @param length Length of random sequence to generate.
    * @param dataSequence Vector to store data in.
@@ -216,24 +232,14 @@
    */
   double LogLikelihood(const arma::mat& dataSeq) const;
 
-  /**
-   * Return the transition matrix.
-   */
+  //! Return the transition matrix.
   const arma::mat& Transition() const { return transition; }
-
-  /**
-   * Return a modifiable transition matrix reference.
-   */
+  //! Return a modifiable transition matrix reference.
   arma::mat& Transition() { return transition; }
 
-  /**
-   * Return the emission distributions.
-   */
+  //! Return the emission distributions.
   const std::vector<Distribution>& Emission() const { return emission; }
-
-  /**
-   * Return a modifiable emission probability matrix reference.
-   */
+  //! Return a modifiable emission probability matrix reference.
   std::vector<Distribution>& Emission() { return emission; }
 
   //! Get the dimensionality of observations.
@@ -273,6 +279,12 @@
                 const arma::vec& scales,
                 arma::mat& backwardProb) const;
 
+  //! Transition probability matrix.
+  arma::mat transition;
+
+  //! Set of emission probability distributions; one for each state.
+  std::vector<Distribution> emission;
+
   //! Dimensionality of observations.
   size_t dimensionality;
 };

Modified: mlpack/trunk/src/mlpack/methods/hmm/hmm_impl.hpp
===================================================================
--- mlpack/trunk/src/mlpack/methods/hmm/hmm_impl.hpp	2013-03-14 18:50:06 UTC (rev 14559)
+++ mlpack/trunk/src/mlpack/methods/hmm/hmm_impl.hpp	2013-03-14 21:17:55 UTC (rev 14560)
@@ -38,11 +38,29 @@
   // Set the dimensionality, if we can.
   if (emission.size() > 0)
     dimensionality = emission[0].Dimensionality();
+  else
+  {
+    Log::Warn << "HMM::HMM(): no emission distributions given; assuming a "
+        << "dimensionality of 0 and hoping it gets set right later."
+        << std::endl;
+    dimensionality = 0;
+  }
 }
 
 /**
  * Train the model using the Baum-Welch algorithm, with only the given unlabeled
- * observations.
+ * observations.  Each matrix in the vector of data sequences holds an
+ * individual data sequence; each point in each individual data sequence should
+ * be a column in the matrix.  The number of rows in each matrix should be equal
+ * to the dimensionality of the HMM.
+ *
+ * It is preferable to use the other overload of Train(), with labeled data.
+ * That will produce much better results.  However, if labeled data is
+ * unavailable, this will work.  In addition, it is possible to use Train() with
+ * labeled data first, and then continue to train the model using this overload
+ * of Train() with unlabeled data.
+ *
+ * @param dataSeq Set of data sequences to train on.
  */
 template<typename Distribution>
 void HMM<Distribution>::Train(const std::vector<arma::mat>& dataSeq)
@@ -54,14 +72,17 @@
   // Maximum iterations?
   size_t iterations = 1000;
 
-  // Find length of all sequences.
+  // Find length of all sequences and ensure they are the correct size.
   size_t totalLength = 0;
   for (size_t seq = 0; seq < dataSeq.size(); seq++)
+  {
     totalLength += dataSeq[seq].n_cols;
 
-  // Re-set the dimensionality, if we need to.
-  if (dataSeq.size() > 0) // Just in case a user passed an empty sequence...
-    dimensionality = dataSeq[0].n_rows;
+    if (dataSeq[seq].n_rows != dimensionality)
+      Log::Fatal << "HMM::Train(): data sequence " << seq << " has "
+          << "dimensionality " << dataSeq[seq].n_rows << " (expected "
+          << dimensionality << " dimensions)." << std::endl;
+  }
 
   // These are used later for training of each distribution.  We initialize it
   // all now so we don't have to do any allocation later on.
@@ -167,10 +188,6 @@
 
   transition.zeros();
 
-  // Re-set the dimensionality, if we need to.
-  if (dataSeq.size() > 0)
-    dimensionality = dataSeq[0].n_rows;
-
   // Estimate the transition and emission matrices directly from the
   // observations.  The emission list holds the time indices for observations
   // from each state.
@@ -187,6 +204,13 @@
           << ") in sequence " << seq << "." << std::endl;
     }
 
+    if (dataSeq[seq].n_rows != dimensionality)
+    {
+      Log::Fatal << "HMM::Train(): data sequence " << seq << " has "
+          << "dimensionality " << dataSeq[seq].n_rows << " (expected "
+          << dimensionality << " dimensions)." << std::endl;
+    }
+
     // Loop over each observation in the sequence.  For estimation of the
     // transition matrix, we must ignore the last observation.
     for (size_t t = 0; t < dataSeq[seq].n_cols - 1; t++)




More information about the mlpack-svn mailing list