Merge pull request #1293 from PlantsAndBuildings/hmm-cli-tests-2
Tests for bindings hmm_loglik, hmm_generate and hmm_viterbi
This commit is contained in:
@@ -68,6 +68,11 @@ struct Generate
|
||||
mat observations;
|
||||
Row<size_t> sequence;
|
||||
|
||||
RequireParamValue<int>("start_state", [](int x) { return x >= 0; }, true,
|
||||
"Invalid start state");
|
||||
RequireParamValue<int>("length", [](int x) { return x >= 0; }, true,
|
||||
"Length must be >= 0");
|
||||
|
||||
// Load the parameters.
|
||||
const size_t startState = (size_t) CLI::GetParam<int>("start_state");
|
||||
const size_t length = (size_t) CLI::GetParam<int>("length");
|
||||
@@ -104,7 +109,7 @@ static void mlpackMain()
|
||||
RandomSeed((size_t) time(NULL));
|
||||
|
||||
// Load model, and perform the generation.
|
||||
HMMModel hmm;
|
||||
hmm = std::move(CLI::GetParam<HMMModel>("model"));
|
||||
hmm.PerformAction<Generate, void>(NULL); // No extra data required.
|
||||
HMMModel* hmm;
|
||||
hmm = std::move(CLI::GetParam<HMMModel*>("model"));
|
||||
hmm->PerformAction<Generate, void>(NULL); // No extra data required.
|
||||
}
|
||||
|
||||
@@ -149,7 +149,11 @@ add_executable(mlpack_test
|
||||
main_tests/sparse_coding_test.cpp
|
||||
main_tests/kmeans_test.cpp
|
||||
main_tests/hoeffding_tree_test.cpp
|
||||
main_tests/hmm_viterbi_test.cpp
|
||||
main_tests/hmm_train_test.cpp
|
||||
main_tests/hmm_loglik_test.cpp
|
||||
main_tests/hmm_generate_test.cpp
|
||||
main_tests/hmm_test_utils.hpp
|
||||
)
|
||||
|
||||
# Link dependencies of test executable.
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* @file hmm_generate_test.cpp
|
||||
* @author Daivik Nema
|
||||
*
|
||||
* Test mlpackMain() of hmm_generate_main.cpp
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define BINDING_TYPE BINDING_TYPE_TEST
|
||||
static const std::string testName = "HMMGenerate";
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
#include "test_helper.hpp"
|
||||
#include <mlpack/methods/hmm/hmm_model.hpp>
|
||||
#include <mlpack/methods/hmm/hmm.hpp>
|
||||
#include <mlpack/methods/hmm/hmm_generate_main.cpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "../test_tools.hpp"
|
||||
|
||||
#include "hmm_test_utils.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
|
||||
struct HMMGenerateTestFixture
|
||||
{
|
||||
public:
|
||||
HMMGenerateTestFixture()
|
||||
{
|
||||
// Cache in the options for this program.
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
|
||||
~HMMGenerateTestFixture()
|
||||
{
|
||||
// Clear the settings.
|
||||
bindings::tests::CleanMemory();
|
||||
CLI::ClearSettings();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(HMMGenerateMainTest, HMMGenerateTestFixture);
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a discrete HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a discrete HMM model.
|
||||
HMMModel* h = new HMMModel(DiscreteHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to generate a sequence
|
||||
// of states and observations - using the hmm_generate utility.
|
||||
// Load the input model to be used for inference and the length of sequence
|
||||
// to be generated.
|
||||
int length = 3;
|
||||
SetInputParam("model", h);
|
||||
SetInputParam("length", length);
|
||||
|
||||
// Call to hmm_generate_main.
|
||||
mlpackMain();
|
||||
|
||||
// Get the generated observation sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::mat obsSeq = CLI::GetParam<arma::mat>("output");
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_rows, (size_t)1);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_elem, (size_t)length);
|
||||
|
||||
// Get the generated state sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::Mat<size_t> stateSeq = CLI::GetParam<arma::Mat<size_t>>("state");
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_elem, (size_t)length);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a gaussian HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a gaussian HMM model.
|
||||
HMMModel* h = new HMMModel(GaussianHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to generate a sequence
|
||||
// of states and observations - using the hmm_generate utility.
|
||||
// Load the input model to be used for inference and the length of sequence
|
||||
// to be generated.
|
||||
int length = 3;
|
||||
SetInputParam("model", h);
|
||||
SetInputParam("length", length);
|
||||
|
||||
// Call to hmm_generate_main.
|
||||
mlpackMain();
|
||||
|
||||
// Get the generated observation sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::mat obsSeq = CLI::GetParam<arma::mat>("output");
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_rows, (size_t)1);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_elem, (size_t)length);
|
||||
|
||||
// Get the generated state sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::Mat<size_t> stateSeq = CLI::GetParam<arma::Mat<size_t>>("state");
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_elem, (size_t)length);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a Gaussian Mixture Model HMM model with.
|
||||
std::vector<GMM> gmms(2, GMM(2, 2));
|
||||
gmms[0].Weights() = arma::vec("0.3 0.7");
|
||||
|
||||
// N([2.25 3.10], [1.00 0.20; 0.20 0.89])
|
||||
gmms[0].Component(0) = GaussianDistribution("4.25 3.10",
|
||||
"1.00 0.20; 0.20 0.89");
|
||||
|
||||
// N([4.10 1.01], [1.00 0.00; 0.00 1.01])
|
||||
gmms[0].Component(1) = GaussianDistribution("7.10 5.01",
|
||||
"1.00 0.00; 0.00 1.01");
|
||||
|
||||
gmms[1].Weights() = arma::vec("0.20 0.80");
|
||||
|
||||
gmms[1].Component(0) = GaussianDistribution("-3.00 -6.12",
|
||||
"1.00 0.00; 0.00 1.00");
|
||||
|
||||
gmms[1].Component(1) = GaussianDistribution("-4.25 -2.12",
|
||||
"1.50 0.60; 0.60 1.20");
|
||||
|
||||
// Transition matrix.
|
||||
arma::mat transMat("0.40 0.60;"
|
||||
"0.60 0.40");
|
||||
|
||||
// Make a sequence of observations.
|
||||
std::vector<arma::mat> observations(5, arma::mat(2, 50));
|
||||
std::vector<arma::Row<size_t> > states(5, arma::Row<size_t>(50));
|
||||
for (size_t obs = 0; obs < 5; obs++)
|
||||
{
|
||||
states[obs][0] = 0;
|
||||
observations[obs].col(0) = gmms[0].Random();
|
||||
|
||||
for (size_t i = 1; i < 50; i++)
|
||||
{
|
||||
double randValue = (double) rand() / (double) RAND_MAX;
|
||||
|
||||
if (randValue <= transMat(0, states[obs][i - 1]))
|
||||
states[obs][i] = 0;
|
||||
else
|
||||
states[obs][i] = 1;
|
||||
|
||||
observations[obs].col(i) = gmms[states[obs][i]].Random();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize and train a GMM HMM model.
|
||||
HMMModel* h = new HMMModel(GaussianMixtureModelHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&observations);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&observations);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to generate a sequence
|
||||
// of states and observations - using the hmm_generate utility.
|
||||
// Load the input model to be used for inference and the length of sequence
|
||||
// to be generated.
|
||||
int length = 3;
|
||||
SetInputParam("model", h);
|
||||
SetInputParam("length", length);
|
||||
|
||||
// Call to hmm_generate_main
|
||||
mlpackMain();
|
||||
|
||||
// Get the generated observation sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::mat obsSeq = CLI::GetParam<arma::mat>("output");
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_rows, (size_t)2);
|
||||
BOOST_REQUIRE_EQUAL(obsSeq.n_elem, (size_t)(length*2));
|
||||
|
||||
// Get the generated state sequence. Ensure that the generated sequence
|
||||
// has the correct length (as provided in the input).
|
||||
arma::Mat<size_t> stateSeq = CLI::GetParam<arma::Mat<size_t>>("state");
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1);
|
||||
BOOST_REQUIRE_EQUAL(stateSeq.n_elem, (size_t)length);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest)
|
||||
{
|
||||
// Load data to train a Gaussian Mixture Model HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a HMM model.
|
||||
HMMModel* h = new HMMModel(DiscreteHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Set the params for the hmm_generate invocation
|
||||
// Note that the length is negative - we expect that a runtime error will be
|
||||
// raised in the call to hmm_generate_main
|
||||
int length = -3; // Invalid
|
||||
SetInputParam("model", h);
|
||||
SetInputParam("length", length);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMGenerateValidStartStateTest)
|
||||
{
|
||||
// Load data to train a Gaussian Mixture Model HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a HMM model.
|
||||
HMMModel* h = new HMMModel(DiscreteHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Set the params for the hmm_generate invocation
|
||||
// Note that the start state is invalid - we expect that a runtime error will
|
||||
// be raised in the call to hmm_generate_main
|
||||
int length = 3;
|
||||
int startState = 2; // Invalid
|
||||
SetInputParam("model", h);
|
||||
SetInputParam("length", length);
|
||||
SetInputParam("start_state", startState);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file hmm_loglik_test.cpp
|
||||
* @author Daivik Nema
|
||||
*
|
||||
* Test mlpackMain() of hmm_loglik_main.cpp
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define BINDING_TYPE BINDING_TYPE_TEST
|
||||
static const std::string testName = "HMMLoglik";
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
#include "test_helper.hpp"
|
||||
#include <mlpack/methods/hmm/hmm_model.hpp>
|
||||
#include <mlpack/methods/hmm/hmm.hpp>
|
||||
#include <mlpack/methods/hmm/hmm_loglik_main.cpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "../test_tools.hpp"
|
||||
|
||||
#include "hmm_test_utils.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
|
||||
struct HMMLoglikTestFixture
|
||||
{
|
||||
public:
|
||||
HMMLoglikTestFixture()
|
||||
{
|
||||
// Cache in the options for this program.
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
|
||||
~HMMLoglikTestFixture()
|
||||
{
|
||||
// Clear the settings.
|
||||
bindings::tests::CleanMemory();
|
||||
CLI::ClearSettings();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(HMMLoglikMainTest, HMMLoglikTestFixture);
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest)
|
||||
{
|
||||
// Load data to train a discrete HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train an HMM model.
|
||||
HMMModel* h = new HMMModel(DiscreteHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
|
||||
// Set the params for the hmm_loglik invocation
|
||||
SetInputParam("input_model", h);
|
||||
SetInputParam("input", inp);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
double loglik = CLI::GetParam<double>("log_likelihood");
|
||||
|
||||
// Since the log of a probability <= 0 ...
|
||||
BOOST_REQUIRE(loglik <= 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @file hmm_test_utils.hpp
|
||||
* @author Daivik Nema
|
||||
*
|
||||
* Structs for initializing and training HMMs (either of Discrete, Gaussian or
|
||||
* GMM HMMs). These structs are passed as template parameters to the
|
||||
* PerformAction function of an HMMModel object. These structs have been adapted
|
||||
* from the structs in mlpack/methods/hmm/hmm_train_main.cpp.
|
||||
*
|
||||
* mlpack is free software; you may redistribute it and/or modify it under the
|
||||
* terms of the 3-clause BSD license. You should have received a copy of the
|
||||
* 3-clause BSD license along with mlpack. If not, see
|
||||
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
|
||||
*/
|
||||
#ifndef MLPACK_TESTS_MAIN_TESTS_HMM_TEST_UTILS_HPP
|
||||
#define MLPACK_TESTS_MAIN_TESTS_HMM_TEST_UTILS_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
#include <mlpack/methods/hmm/hmm.hpp>
|
||||
|
||||
struct InitHMMModel
|
||||
{
|
||||
template<typename HMMType>
|
||||
static void Apply(HMMType& hmm, vector<mat>* trainSeq)
|
||||
{
|
||||
const size_t states = 2;
|
||||
|
||||
// Create the initialized-to-zero model.
|
||||
Create(hmm, *trainSeq, states);
|
||||
|
||||
// Initializing the emission distribution depends on the distribution.
|
||||
// Therefore we have to use the helper functions.
|
||||
RandomInitialize(hmm.Emission());
|
||||
}
|
||||
|
||||
//! Helper function to create discrete HMM.
|
||||
static void Create(HMM<DiscreteDistribution>& hmm,
|
||||
vector<mat>& trainSeq,
|
||||
size_t states,
|
||||
double tolerance = 1e-05)
|
||||
{
|
||||
// Maximum observation is necessary so we know how to train the discrete
|
||||
// distribution.
|
||||
arma::Col<size_t> maxEmissions(trainSeq[0].n_rows);
|
||||
maxEmissions.zeros();
|
||||
for (vector<mat>::iterator it = trainSeq.begin(); it != trainSeq.end();
|
||||
++it)
|
||||
{
|
||||
arma::Col<size_t> maxSeqs =
|
||||
arma::conv_to<arma::Col<size_t>>::from(arma::max(*it, 1)) + 1;
|
||||
maxEmissions = arma::max(maxEmissions, maxSeqs);
|
||||
}
|
||||
|
||||
hmm = HMM<DiscreteDistribution>(size_t(states),
|
||||
DiscreteDistribution(maxEmissions), tolerance);
|
||||
}
|
||||
|
||||
static void Create(HMM<GaussianDistribution>& hmm,
|
||||
vector<mat>& trainSeq,
|
||||
size_t states,
|
||||
double tolerance = 1e-05)
|
||||
{
|
||||
// Find dimension of the data.
|
||||
const size_t dimensionality = trainSeq[0].n_rows;
|
||||
|
||||
// Verify dimensionality of data.
|
||||
for (size_t i = 0; i < trainSeq.size(); ++i)
|
||||
{
|
||||
if (trainSeq[i].n_rows != dimensionality)
|
||||
{
|
||||
Log::Fatal << "Observation sequence " << i << " dimensionality ("
|
||||
<< trainSeq[i].n_rows << " is incorrect (should be "
|
||||
<< dimensionality << ")!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the model and initialize it.
|
||||
hmm = HMM<GaussianDistribution>(size_t(states),
|
||||
GaussianDistribution(dimensionality), tolerance);
|
||||
}
|
||||
|
||||
static void Create(HMM<GMM>& hmm,
|
||||
vector<mat>& trainSeq,
|
||||
size_t states,
|
||||
double tolerance = 1e-05)
|
||||
{
|
||||
// Find dimension of the data.
|
||||
const size_t dimensionality = trainSeq[0].n_rows;
|
||||
const int gaussians = 2;
|
||||
|
||||
if (gaussians == 0)
|
||||
{
|
||||
Log::Fatal << "Number of gaussians for each GMM must be specified "
|
||||
<< "when type = 'gmm'!" << endl;
|
||||
}
|
||||
|
||||
if (gaussians < 0)
|
||||
{
|
||||
Log::Fatal << "Invalid number of gaussians (" << gaussians << "); must "
|
||||
<< "be greater than or equal to 1." << endl;
|
||||
}
|
||||
|
||||
// Create HMM object.
|
||||
hmm = HMM<GMM>(size_t(states), GMM(size_t(gaussians), dimensionality),
|
||||
tolerance);
|
||||
}
|
||||
|
||||
//! Helper function for discrete emission distributions.
|
||||
static void RandomInitialize(vector<DiscreteDistribution>& e)
|
||||
{
|
||||
for (size_t i = 0; i < e.size(); ++i)
|
||||
{
|
||||
e[i].Probabilities().randu();
|
||||
e[i].Probabilities() /= arma::accu(e[i].Probabilities());
|
||||
}
|
||||
}
|
||||
|
||||
static void RandomInitialize(vector<GaussianDistribution>& e)
|
||||
{
|
||||
for (size_t i = 0; i < e.size(); ++i)
|
||||
{
|
||||
const size_t dimensionality = e[i].Mean().n_rows;
|
||||
e[i].Mean().randu();
|
||||
// Generate random covariance.
|
||||
arma::mat r = arma::randu<arma::mat>(dimensionality, dimensionality);
|
||||
e[i].Covariance(r * r.t());
|
||||
}
|
||||
}
|
||||
|
||||
static void RandomInitialize(vector<GMM>& e)
|
||||
{
|
||||
for (size_t i = 0; i < e.size(); ++i)
|
||||
{
|
||||
// Random weights.
|
||||
e[i].Weights().randu();
|
||||
e[i].Weights() /= arma::accu(e[i].Weights());
|
||||
|
||||
// Random means and covariances.
|
||||
for (int g = 0; g < 2; ++g)
|
||||
{
|
||||
const size_t dimensionality = e[i].Component(g).Mean().n_rows;
|
||||
e[i].Component(g).Mean().randu();
|
||||
|
||||
// Generate random covariance.
|
||||
arma::mat r = arma::randu<arma::mat>(dimensionality,
|
||||
dimensionality);
|
||||
e[i].Component(g).Covariance(r * r.t());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct TrainHMMModel
|
||||
{
|
||||
template<typename HMMType>
|
||||
static void Apply(HMMType& hmm, vector<arma::mat>* trainSeq)
|
||||
{
|
||||
// For now, perform unsupervised (Baum-Welch) training.
|
||||
hmm.Train(*trainSeq);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @file hmm_viterbi_test.cpp
|
||||
* @author Daivik Nema
|
||||
*
|
||||
* Test mlpackMain() of hmm_viterbi_main.cpp
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define BINDING_TYPE BINDING_TYPE_TEST
|
||||
static const std::string testName = "HMMViterbi";
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
#include "test_helper.hpp"
|
||||
#include <mlpack/methods/hmm/hmm_model.hpp>
|
||||
#include <mlpack/methods/hmm/hmm.hpp>
|
||||
#include <mlpack/methods/hmm/hmm_viterbi_main.cpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "../test_tools.hpp"
|
||||
|
||||
#include "hmm_test_utils.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
|
||||
struct HMMViterbiTestFixture
|
||||
{
|
||||
public:
|
||||
HMMViterbiTestFixture()
|
||||
{
|
||||
// Cache in the options for this program.
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
|
||||
~HMMViterbiTestFixture()
|
||||
{
|
||||
// Clear the settings.
|
||||
bindings::tests::CleanMemory();
|
||||
CLI::ClearSettings();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture);
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a discrete HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a discrete HMM model.
|
||||
HMMModel* h = new HMMModel(DiscreteHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to predict the state
|
||||
// sequence for a given observation sequence - using the Viterbi algorithm.
|
||||
// Load the input model to be used for inference and the sequence over which
|
||||
// inference is to be performed.
|
||||
SetInputParam("input_model", h);
|
||||
SetInputParam("input", inp);
|
||||
|
||||
// Call to hmm_viterbi_main.
|
||||
mlpackMain();
|
||||
|
||||
// Get the output of viterbi inference.
|
||||
arma::Mat<size_t> out = CLI::GetParam<arma::Mat<size_t> >("output");
|
||||
|
||||
// Output sequence length must be the same as input sequence length and
|
||||
// there should only be one row (since states are single dimensional values).
|
||||
BOOST_REQUIRE_EQUAL(out.n_rows, 1);
|
||||
BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMViterbiGaussianHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a gaussian HMM model with.
|
||||
arma::mat inp;
|
||||
data::Load("obs1.csv", inp);
|
||||
std::vector<arma::mat> trainSeq = {inp};
|
||||
|
||||
// Initialize and train a gaussian HMM model.
|
||||
HMMModel* h = new HMMModel(GaussianHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to predict the state
|
||||
// sequence for a given observation sequence - using the Viterbi algorithm.
|
||||
// Load the input model to be used for inference and the sequence over which
|
||||
// inference is to be performed.
|
||||
SetInputParam("input_model", h);
|
||||
SetInputParam("input", inp);
|
||||
|
||||
// Call to hmm_viterbi_main.
|
||||
mlpackMain();
|
||||
|
||||
// Get the output of viterbi inference.
|
||||
arma::Mat<size_t> out = CLI::GetParam<arma::Mat<size_t> >("output");
|
||||
|
||||
// Output sequence length must be the same as input sequence length and
|
||||
// there should only be one row (since states are single dimensional values).
|
||||
BOOST_REQUIRE_EQUAL(out.n_rows, 1);
|
||||
BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest)
|
||||
{
|
||||
// Load data to train a Gaussian Mixture Model HMM model with.
|
||||
std::vector<GMM> gmms(2, GMM(2, 2));
|
||||
gmms[0].Weights() = arma::vec("0.3 0.7");
|
||||
|
||||
// N([2.25 3.10], [1.00 0.20; 0.20 0.89])
|
||||
gmms[0].Component(0) = GaussianDistribution("4.25 3.10",
|
||||
"1.00 0.20; 0.20 0.89");
|
||||
|
||||
// N([4.10 1.01], [1.00 0.00; 0.00 1.01])
|
||||
gmms[0].Component(1) = GaussianDistribution("7.10 5.01",
|
||||
"1.00 0.00; 0.00 1.01");
|
||||
|
||||
gmms[1].Weights() = arma::vec("0.20 0.80");
|
||||
|
||||
gmms[1].Component(0) = GaussianDistribution("-3.00 -6.12",
|
||||
"1.00 0.00; 0.00 1.00");
|
||||
|
||||
gmms[1].Component(1) = GaussianDistribution("-4.25 -2.12",
|
||||
"1.50 0.60; 0.60 1.20");
|
||||
|
||||
// Transition matrix.
|
||||
arma::mat transMat("0.40 0.60;"
|
||||
"0.60 0.40");
|
||||
|
||||
// Make a sequence of observations.
|
||||
std::vector<arma::mat> observations(5, arma::mat(2, 50));
|
||||
std::vector<arma::Row<size_t> > states(5, arma::Row<size_t>(50));
|
||||
for (size_t obs = 0; obs < 5; obs++)
|
||||
{
|
||||
states[obs][0] = 0;
|
||||
observations[obs].col(0) = gmms[0].Random();
|
||||
|
||||
for (size_t i = 1; i < 50; i++)
|
||||
{
|
||||
double randValue = (double) rand() / (double) RAND_MAX;
|
||||
|
||||
if (randValue <= transMat(0, states[obs][i - 1]))
|
||||
states[obs][i] = 0;
|
||||
else
|
||||
states[obs][i] = 1;
|
||||
|
||||
observations[obs].col(i) = gmms[states[obs][i]].Random();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize and train a GMM HMM model.
|
||||
HMMModel* h = new HMMModel(GaussianMixtureModelHMM);
|
||||
h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&observations);
|
||||
h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&observations);
|
||||
|
||||
// Now that we have a trained HMM model, we can use it to predict the state
|
||||
// sequence for a given observation sequence - using the Viterbi algorithm.
|
||||
// Load the input model to be used for inference and the sequence over which
|
||||
// inference is to be performed.
|
||||
SetInputParam("input_model", h);
|
||||
SetInputParam("input", observations[0]);
|
||||
|
||||
// Call to hmm_viterbi_main.
|
||||
mlpackMain();
|
||||
|
||||
// Get the output of viterbi inference.
|
||||
arma::Mat<size_t> out = CLI::GetParam<arma::Mat<size_t> >("output");
|
||||
|
||||
// Output sequence length must be the same as input sequence length and
|
||||
// there should only be one row (since states are single dimensional values).
|
||||
BOOST_REQUIRE_EQUAL(out.n_rows, 1);
|
||||
BOOST_REQUIRE_EQUAL(out.n_cols, observations[0].n_cols);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
Reference in New Issue
Block a user