From b471b13b4a287845968b9201715294fa288e42fc Mon Sep 17 00:00:00 2001 From: daivik Date: Fri, 2 Mar 2018 07:04:57 +0000 Subject: [PATCH 01/17] tmp commit --- src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 13 +++ src/mlpack/tests/CMakeLists.txt | 1 + .../tests/main_tests/hmm_viterbi_test.cpp | 80 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/mlpack/tests/main_tests/hmm_viterbi_test.cpp diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp index d0e1168b3d..b8a105d48a 100644 --- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp @@ -54,27 +54,39 @@ struct Viterbi template static void Apply(HMMType& hmm, void* /* extraInfo */) { + std::cout << __func__ << ": In PerformAction" << std::endl; // Load observations. mat dataSeq = std::move(CLI::GetParam("input")); + std::cout << __func__ << ": Data read:" << std::endl << dataSeq << std::endl; // See if transposing the data could make it the right dimensionality. + std::cout << __func__ << ": dataSeq.n_cols = " << dataSeq.n_cols << std::endl; + std::cout << __func__ << ": hmm.Emission()[0].Dimensionality() = " << hmm.Emission()[0].Dimensionality() << std::endl; if ((dataSeq.n_cols == 1) && (hmm.Emission()[0].Dimensionality() == 1)) { Log::Info << "Data sequence appears to be transposed; correcting." << endl; + std::cout << "Data sequence appears to be transposed; correcting." + << endl; dataSeq = dataSeq.t(); } + std::cout << __func__ << ": Corrected dataSeq orientation!" << std::endl; // Verify correct dimensionality. + std::cout << __func__ << ": dataSeq.n_rows = " << dataSeq.n_rows << std::endl; + std::cout << __func__ << ": hmm.Emission()[0].Dimensionality() = " << hmm.Emission()[0].Dimensionality() << std::endl; if (dataSeq.n_rows != hmm.Emission()[0].Dimensionality()) { Log::Fatal << "Observation dimensionality (" << dataSeq.n_rows << ") " << "does not match HMM Gaussian dimensionality (" << hmm.Emission()[0].Dimensionality() << ")!" << endl; } + std::cout << __func__ << ": Verified correct dimensionality!" << std::endl; arma::Row sequence; + std::cout << __func__ << ": Calling hmm.Predict()" << std::endl; hmm.Predict(dataSeq, sequence); + std::cout << __func__ << ": Predicted state sequence:" << std::endl << sequence << std::endl; // Save output. CLI::GetParam>("output") = std::move(sequence); @@ -85,5 +97,6 @@ static void mlpackMain() { RequireAtLeastOnePassed({ "output" }, false, "no results will be saved"); + std::cout << __func__ << ": calling PerformAction" << std::endl; CLI::GetParam("input_model")->PerformAction((void*) NULL); } diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index 90747709cc..8509c1375c 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -145,6 +145,7 @@ add_executable(mlpack_test main_tests/softmax_regression_test.cpp main_tests/sparse_coding_test.cpp main_tests/hoeffding_tree_test.cpp + main_tests/hmm_viterbi_test.cpp ) # Link dependencies of test executable. diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp new file mode 100644 index 0000000000..dcb646d54b --- /dev/null +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -0,0 +1,80 @@ +/** + * @file hmm_viterbi_test.cpp + * @author Daivik Nema + * + * Test mlpackMain() of hmm_viterbi_main.cpp + */ +#include + +#define BINDING_TYPE BINDING_TYPE_TEST +static const std::string testName = "HMMViterbi"; + +#include +#include +#include "test_helper.hpp" +#include +#include +#include + +#include +#include "../test_tools.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); + +struct Train +{ + template + static void Apply(HMMType& hmm, vector* trainSeq) + { + // For now, perform unsupervised (Baum-Welch) training + hmm.Train(*trainSeq); + } +}; + +BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimenstionsTest) +{ + // Train an HMM + HMMModel h; + arma::mat inp; + data::Load("obs-1.csv", inp); + std::cout << "Loaded data:" << std::endl << inp << std::endl; + std::vector trainSeq = {inp}; + + // Train the HMM + h.PerformAction >(&trainSeq); + std::cout << __func__ << ": Training complete!" << std::endl; + + // Set the params for the hmm_viterbi invocation + SetInputParam("input_model", &h); + SetInputParam("input", inp); + std::cout << __func__ << ": Set input params!" << std::endl; + + mlpackMain(); + std::cout << __func__ << ": HMMViterbiMain() complete!" << std::endl; + + arma::Mat out = CLI::GetParam >("output"); + BOOST_REQUIRE(out.n_rows == inp.n_rows); + BOOST_REQUIRE(out.n_cols == inp.n_cols); + BOOST_REQUIRE(out.n_elem == inp.n_elem); +} + +BOOST_AUTO_TEST_SUITE_END(); From 43da9836cc81bcf66f745d407f6a1aa7790819a8 Mon Sep 17 00:00:00 2001 From: daivik Date: Fri, 2 Mar 2018 21:36:51 +0000 Subject: [PATCH 02/17] Add HMMViterbiCheckDimensionsTest --- src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 3 +- .../tests/main_tests/hmm_viterbi_test.cpp | 89 +++++++++++++++++-- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp index b8a105d48a..d68cf9a930 100644 --- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp @@ -61,6 +61,7 @@ struct Viterbi // See if transposing the data could make it the right dimensionality. std::cout << __func__ << ": dataSeq.n_cols = " << dataSeq.n_cols << std::endl; + std::cout << __func__ << ": hmm.Emission.size() = " << hmm.Emission().size() << std::endl; std::cout << __func__ << ": hmm.Emission()[0].Dimensionality() = " << hmm.Emission()[0].Dimensionality() << std::endl; if ((dataSeq.n_cols == 1) && (hmm.Emission()[0].Dimensionality() == 1)) { @@ -86,10 +87,10 @@ struct Viterbi arma::Row sequence; std::cout << __func__ << ": Calling hmm.Predict()" << std::endl; hmm.Predict(dataSeq, sequence); - std::cout << __func__ << ": Predicted state sequence:" << std::endl << sequence << std::endl; // Save output. CLI::GetParam>("output") = std::move(sequence); + std::cout << __func__ << ": Predicted state sequence:" << std::endl << CLI::GetParam>("output") << std::endl; } }; diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index dcb646d54b..503062d650 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -40,6 +40,80 @@ struct HMMViterbiTestFixture BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); +struct Init +{ + template + static void Apply(HMMType& hmm, vector* 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& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Maximum observation is necessary so we know how to train the discrete + // distribution. + arma::Col maxEmissions(trainSeq[0].n_rows); + maxEmissions.zeros(); + for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); + ++it) + { + arma::Col maxSeqs = + arma::conv_to>::from(arma::max(*it, 1)) + 1; + maxEmissions = arma::max(maxEmissions, maxSeqs); + } + + hmm = HMM(size_t(states), + DiscreteDistribution(maxEmissions), tolerance); + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + } + + //! Helper function for discrete emission distributions. + static void RandomInitialize(vector& 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& e) + { + // Not implemented + } + + static void RandomInitialize(vector& e) + { + // Not implemented + } +}; + struct Train { template @@ -50,21 +124,24 @@ struct Train } }; -BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimenstionsTest) +BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) { // Train an HMM - HMMModel h; + HMMModel * h = new HMMModel(DiscreteHMM); + // Load data arma::mat inp; - data::Load("obs-1.csv", inp); + data::Load("obs1.csv", inp); std::cout << "Loaded data:" << std::endl << inp << std::endl; std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); - // Train the HMM - h.PerformAction >(&trainSeq); std::cout << __func__ << ": Training complete!" << std::endl; // Set the params for the hmm_viterbi invocation - SetInputParam("input_model", &h); + SetInputParam("input_model", h); SetInputParam("input", inp); std::cout << __func__ << ": Set input params!" << std::endl; From 69f0959e5b9036b22eed85034204764c23ca8e2a Mon Sep 17 00:00:00 2001 From: daivik Date: Fri, 2 Mar 2018 22:19:55 +0000 Subject: [PATCH 03/17] Add HMMLoglikOuputNegativeTest --- src/mlpack/tests/CMakeLists.txt | 1 + .../tests/main_tests/hmm_loglik_test.cpp | 155 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 src/mlpack/tests/main_tests/hmm_loglik_test.cpp diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index d28d44bad5..4653940107 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -147,6 +147,7 @@ add_executable(mlpack_test main_tests/hoeffding_tree_test.cpp main_tests/hmm_viterbi_test.cpp main_tests/hmm_train_test.cpp + main_tests/hmm_loglik_test.cpp ) # Link dependencies of test executable. diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp new file mode 100644 index 0000000000..82d26fbe46 --- /dev/null +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -0,0 +1,155 @@ +/** + * @file hmm_loglik_test.cpp + * @author Daivik Nema + * + * Test mlpackMain() of hmm_loglik_main.cpp + */ +#include + +#define BINDING_TYPE BINDING_TYPE_TEST +static const std::string testName = "HMMLoglik"; + +#include +#include +#include "test_helper.hpp" +#include +#include +#include + +#include +#include "../test_tools.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); + +struct Init +{ + template + static void Apply(HMMType& hmm, vector* 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& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Maximum observation is necessary so we know how to train the discrete + // distribution. + arma::Col maxEmissions(trainSeq[0].n_rows); + maxEmissions.zeros(); + for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); + ++it) + { + arma::Col maxSeqs = + arma::conv_to>::from(arma::max(*it, 1)) + 1; + maxEmissions = arma::max(maxEmissions, maxSeqs); + } + + hmm = HMM(size_t(states), + DiscreteDistribution(maxEmissions), tolerance); + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + } + + //! Helper function for discrete emission distributions. + static void RandomInitialize(vector& 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& e) + { + // Not implemented + } + + static void RandomInitialize(vector& e) + { + // Not implemented + } +}; + +struct Train +{ + template + static void Apply(HMMType& hmm, vector* trainSeq) + { + // For now, perform unsupervised (Baum-Welch) training + hmm.Train(*trainSeq); + } +}; + +BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(DiscreteHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::cout << "Loaded data:" << std::endl << inp << std::endl; + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + std::cout << __func__ << ": Training complete!" << std::endl; + + // Set the params for the hmm_viterbi invocation + SetInputParam("input_model", h); + SetInputParam("input", inp); + std::cout << __func__ << ": Set input params!" << std::endl; + + mlpackMain(); + std::cout << __func__ << ": HMMViterbiMain() complete!" << std::endl; + + double loglik = CLI::GetParam("log_likelihood"); + BOOST_REQUIRE(loglik <= 0); +} + +BOOST_AUTO_TEST_SUITE_END(); From cb0cd18228744f4c9c6ee0f9842e825c717ece27 Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 3 Mar 2018 06:45:10 +0000 Subject: [PATCH 04/17] Remove debug statements. Prevent unused param warnings --- src/mlpack/methods/hmm/hmm_viterbi_main.cpp | 14 ---------- .../tests/main_tests/hmm_loglik_test.cpp | 26 +++++++++++++------ .../tests/main_tests/hmm_viterbi_test.cpp | 19 ++++++++++---- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp index d68cf9a930..d0e1168b3d 100644 --- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp @@ -54,43 +54,30 @@ struct Viterbi template static void Apply(HMMType& hmm, void* /* extraInfo */) { - std::cout << __func__ << ": In PerformAction" << std::endl; // Load observations. mat dataSeq = std::move(CLI::GetParam("input")); - std::cout << __func__ << ": Data read:" << std::endl << dataSeq << std::endl; // See if transposing the data could make it the right dimensionality. - std::cout << __func__ << ": dataSeq.n_cols = " << dataSeq.n_cols << std::endl; - std::cout << __func__ << ": hmm.Emission.size() = " << hmm.Emission().size() << std::endl; - std::cout << __func__ << ": hmm.Emission()[0].Dimensionality() = " << hmm.Emission()[0].Dimensionality() << std::endl; if ((dataSeq.n_cols == 1) && (hmm.Emission()[0].Dimensionality() == 1)) { Log::Info << "Data sequence appears to be transposed; correcting." << endl; - std::cout << "Data sequence appears to be transposed; correcting." - << endl; dataSeq = dataSeq.t(); } - std::cout << __func__ << ": Corrected dataSeq orientation!" << std::endl; // Verify correct dimensionality. - std::cout << __func__ << ": dataSeq.n_rows = " << dataSeq.n_rows << std::endl; - std::cout << __func__ << ": hmm.Emission()[0].Dimensionality() = " << hmm.Emission()[0].Dimensionality() << std::endl; if (dataSeq.n_rows != hmm.Emission()[0].Dimensionality()) { Log::Fatal << "Observation dimensionality (" << dataSeq.n_rows << ") " << "does not match HMM Gaussian dimensionality (" << hmm.Emission()[0].Dimensionality() << ")!" << endl; } - std::cout << __func__ << ": Verified correct dimensionality!" << std::endl; arma::Row sequence; - std::cout << __func__ << ": Calling hmm.Predict()" << std::endl; hmm.Predict(dataSeq, sequence); // Save output. CLI::GetParam>("output") = std::move(sequence); - std::cout << __func__ << ": Predicted state sequence:" << std::endl << CLI::GetParam>("output") << std::endl; } }; @@ -98,6 +85,5 @@ static void mlpackMain() { RequireAtLeastOnePassed({ "output" }, false, "no results will be saved"); - std::cout << __func__ << ": calling PerformAction" << std::endl; CLI::GetParam("input_model")->PerformAction((void*) NULL); } diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index 82d26fbe46..816ce2c0ad 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -83,6 +83,11 @@ struct Init double tolerance=1e-05) { // Not implemented + // Prevent unused args warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; } static void Create(HMM& hmm, @@ -91,6 +96,11 @@ struct Init double tolerance=1e-05) { // Not implemented + // Prevent unused args warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; } //! Helper function for discrete emission distributions. @@ -106,11 +116,15 @@ struct Init static void RandomInitialize(vector& e) { // Not implemented + // Prevent unused args warning + (void)e; } static void RandomInitialize(vector& e) { // Not implemented + // Prevent unused args warning + (void)e; } }; @@ -126,27 +140,23 @@ struct Train BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) { - // Train an HMM + // Create an HMMModel HMMModel * h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); - std::cout << "Loaded data:" << std::endl << inp << std::endl; std::vector trainSeq = {inp}; - // Init + // Init HMMModel h->PerformAction>(&trainSeq); - // Train + // Train HMMModel h->PerformAction>(&trainSeq); - std::cout << __func__ << ": Training complete!" << std::endl; - // Set the params for the hmm_viterbi invocation + // Set the params for the hmm_loglik invocation SetInputParam("input_model", h); SetInputParam("input", inp); - std::cout << __func__ << ": Set input params!" << std::endl; mlpackMain(); - std::cout << __func__ << ": HMMViterbiMain() complete!" << std::endl; double loglik = CLI::GetParam("log_likelihood"); BOOST_REQUIRE(loglik <= 0); diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 503062d650..46711774fb 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -83,6 +83,11 @@ struct Init double tolerance=1e-05) { // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; } static void Create(HMM& hmm, @@ -91,6 +96,11 @@ struct Init double tolerance=1e-05) { // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; } //! Helper function for discrete emission distributions. @@ -106,11 +116,15 @@ struct Init static void RandomInitialize(vector& e) { // Not implemented + // Prevent unused parameter warning + (void)e; } static void RandomInitialize(vector& e) { // Not implemented + // Prevent unused parameter warning + (void)e; } }; @@ -131,22 +145,17 @@ BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) // Load data arma::mat inp; data::Load("obs1.csv", inp); - std::cout << "Loaded data:" << std::endl << inp << std::endl; std::vector trainSeq = {inp}; // Init h->PerformAction>(&trainSeq); // Train h->PerformAction>(&trainSeq); - std::cout << __func__ << ": Training complete!" << std::endl; - // Set the params for the hmm_viterbi invocation SetInputParam("input_model", h); SetInputParam("input", inp); - std::cout << __func__ << ": Set input params!" << std::endl; mlpackMain(); - std::cout << __func__ << ": HMMViterbiMain() complete!" << std::endl; arma::Mat out = CLI::GetParam >("output"); BOOST_REQUIRE(out.n_rows == inp.n_rows); From 679c3d1a7bc67756083c1f02868418e2074285d5 Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 3 Mar 2018 08:05:24 +0000 Subject: [PATCH 05/17] Minor fixes to hmm_generate_main.cpp --- src/mlpack/methods/hmm/hmm_generate_main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp index c9488d3466..7f3c5c65d5 100644 --- a/src/mlpack/methods/hmm/hmm_generate_main.cpp +++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp @@ -70,6 +70,8 @@ struct Generate // Load the parameters. const size_t startState = (size_t) CLI::GetParam("start_state"); + if (CLI::GetParam("length") < 0) + Log::Fatal << "Length must be >= 0" << std::endl; const size_t length = (size_t) CLI::GetParam("length"); Log::Info << "Generating sequence of length " << length << "..." << endl; @@ -104,7 +106,7 @@ static void mlpackMain() RandomSeed((size_t) time(NULL)); // Load model, and perform the generation. - HMMModel hmm; - hmm = std::move(CLI::GetParam("model")); - hmm.PerformAction(NULL); // No extra data required. + HMMModel* hmm; + hmm = std::move(CLI::GetParam("model")); + hmm->PerformAction(NULL); // No extra data required. } From 4a5c219a3a0e78d0a0d49dcc86543815db6608d5 Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 3 Mar 2018 08:06:28 +0000 Subject: [PATCH 06/17] Add tests for hmm_generate --- src/mlpack/tests/CMakeLists.txt | 1 + .../tests/main_tests/hmm_generate_test.cpp | 190 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 src/mlpack/tests/main_tests/hmm_generate_test.cpp diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index 4653940107..36c7ceaf20 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -148,6 +148,7 @@ add_executable(mlpack_test main_tests/hmm_viterbi_test.cpp main_tests/hmm_train_test.cpp main_tests/hmm_loglik_test.cpp + main_tests/hmm_generate_test.cpp ) # Link dependencies of test executable. diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp new file mode 100644 index 0000000000..465a4b8d87 --- /dev/null +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -0,0 +1,190 @@ +/** + * @file hmm_generate_test.cpp + * @author Daivik Nema + * + * Test mlpackMain() of hmm_generate_main.cpp + */ +#include + +#define BINDING_TYPE BINDING_TYPE_TEST +static const std::string testName = "HMMGenerate"; + +#include +#include +#include "test_helper.hpp" +#include +#include +#include + +#include +#include "../test_tools.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); + +struct Init +{ + template + static void Apply(HMMType& hmm, vector* 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& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Maximum observation is necessary so we know how to train the discrete + // distribution. + arma::Col maxEmissions(trainSeq[0].n_rows); + maxEmissions.zeros(); + for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); + ++it) + { + arma::Col maxSeqs = + arma::conv_to>::from(arma::max(*it, 1)) + 1; + maxEmissions = arma::max(maxEmissions, maxSeqs); + } + + hmm = HMM(size_t(states), + DiscreteDistribution(maxEmissions), tolerance); + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance=1e-05) + { + // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; + } + + //! Helper function for discrete emission distributions. + static void RandomInitialize(vector& 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& e) + { + // Not implemented + // Prevent unused parameter warning + (void)e; + } + + static void RandomInitialize(vector& e) + { + // Not implemented + // Prevent unused parameter warning + (void)e; + } +}; + +struct Train +{ + template + static void Apply(HMMType& hmm, vector* trainSeq) + { + // For now, perform unsupervised (Baum-Welch) training + hmm.Train(*trainSeq); + } +}; + +BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(DiscreteHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_generate invocation + int length = 3; + SetInputParam("model", h); + SetInputParam("length", length); + + mlpackMain(); + + arma::mat stateSeq = CLI::GetParam("output"); + BOOST_REQUIRE(stateSeq.n_cols == (size_t)length); + BOOST_REQUIRE(stateSeq.n_rows == (size_t)1); + BOOST_REQUIRE(stateSeq.n_elem == (size_t)length); +} + +BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(DiscreteHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_generate invocation + 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_SUITE_END(); From 37c39c8bed1c554ed01908c6216137025d5760ea Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 3 Mar 2018 08:25:25 +0000 Subject: [PATCH 07/17] Add HMMGenerateValidStartStateTest --- .../tests/main_tests/hmm_generate_test.cpp | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index 465a4b8d87..ab182543df 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -158,7 +158,12 @@ BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) mlpackMain(); - arma::mat stateSeq = CLI::GetParam("output"); + arma::mat obsSeq = CLI::GetParam("output"); + BOOST_REQUIRE(obsSeq.n_cols == (size_t)length); + BOOST_REQUIRE(obsSeq.n_rows == (size_t)1); + BOOST_REQUIRE(obsSeq.n_elem == (size_t)length); + + arma::Mat stateSeq = CLI::GetParam>("state"); BOOST_REQUIRE(stateSeq.n_cols == (size_t)length); BOOST_REQUIRE(stateSeq.n_rows == (size_t)1); BOOST_REQUIRE(stateSeq.n_elem == (size_t)length); @@ -187,4 +192,28 @@ BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) Log::Fatal.ignoreInput = false; } +BOOST_AUTO_TEST_CASE(HMMGenerateValidStartStateTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(DiscreteHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + 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(); From 692559f517a325ddb49460f45bfa492c89ba816f Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 3 Mar 2018 08:35:19 +0000 Subject: [PATCH 08/17] Style Fixes --- src/mlpack/tests/main_tests/hmm_generate_test.cpp | 6 +++--- src/mlpack/tests/main_tests/hmm_loglik_test.cpp | 6 +++--- src/mlpack/tests/main_tests/hmm_viterbi_test.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index ab182543df..829334c279 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -59,7 +59,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Maximum observation is necessary so we know how to train the discrete // distribution. @@ -80,7 +80,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused parameter warning @@ -93,7 +93,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused parameter warning diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index 816ce2c0ad..c5a963350b 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -59,7 +59,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Maximum observation is necessary so we know how to train the discrete // distribution. @@ -80,7 +80,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused args warning @@ -93,7 +93,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused args warning diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 46711774fb..15b7a2aeb2 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -59,7 +59,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Maximum observation is necessary so we know how to train the discrete // distribution. @@ -80,7 +80,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused parameter warning @@ -93,7 +93,7 @@ struct Init static void Create(HMM& hmm, vector& trainSeq, size_t states, - double tolerance=1e-05) + double tolerance = 1e-05) { // Not implemented // Prevent unused parameter warning From 8580a22a465dddd4606a7d8a7898f1824d0e992f Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 08:01:03 +0000 Subject: [PATCH 09/17] Minor changes --- src/mlpack/methods/hmm/hmm_generate_main.cpp | 7 +++++-- src/mlpack/tests/main_tests/hmm_generate_test.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/hmm/hmm_generate_main.cpp b/src/mlpack/methods/hmm/hmm_generate_main.cpp index 7f3c5c65d5..0efc8dc840 100644 --- a/src/mlpack/methods/hmm/hmm_generate_main.cpp +++ b/src/mlpack/methods/hmm/hmm_generate_main.cpp @@ -68,10 +68,13 @@ struct Generate mat observations; Row sequence; + RequireParamValue("start_state", [](int x) { return x >= 0; }, true, + "Invalid start state"); + RequireParamValue("length", [](int x) { return x >= 0; }, true, + "Length must be >= 0"); + // Load the parameters. const size_t startState = (size_t) CLI::GetParam("start_state"); - if (CLI::GetParam("length") < 0) - Log::Fatal << "Length must be >= 0" << std::endl; const size_t length = (size_t) CLI::GetParam("length"); Log::Info << "Generating sequence of length " << length << "..." << endl; diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index 829334c279..38e86909fd 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -159,14 +159,14 @@ BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) mlpackMain(); arma::mat obsSeq = CLI::GetParam("output"); - BOOST_REQUIRE(obsSeq.n_cols == (size_t)length); - BOOST_REQUIRE(obsSeq.n_rows == (size_t)1); - BOOST_REQUIRE(obsSeq.n_elem == (size_t)length); + 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); arma::Mat stateSeq = CLI::GetParam>("state"); - BOOST_REQUIRE(stateSeq.n_cols == (size_t)length); - BOOST_REQUIRE(stateSeq.n_rows == (size_t)1); - BOOST_REQUIRE(stateSeq.n_elem == (size_t)length); + 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) From 6525440b697f8a53f6d65ee34c93388df537c44a Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 08:37:59 +0000 Subject: [PATCH 10/17] Refactor Init and Train structs. Add hmm_test_utils.hpp --- src/mlpack/tests/CMakeLists.txt | 1 + .../tests/main_tests/hmm_generate_test.cpp | 100 +---------------- .../tests/main_tests/hmm_loglik_test.cpp | 100 +---------------- .../tests/main_tests/hmm_test_utils.hpp | 105 ++++++++++++++++++ .../tests/main_tests/hmm_viterbi_test.cpp | 100 +---------------- 5 files changed, 112 insertions(+), 294 deletions(-) create mode 100644 src/mlpack/tests/main_tests/hmm_test_utils.hpp diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index 36c7ceaf20..0f1925254f 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -149,6 +149,7 @@ add_executable(mlpack_test 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. diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index 38e86909fd..a8df8476d7 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -19,6 +19,8 @@ static const std::string testName = "HMMGenerate"; #include #include "../test_tools.hpp" +#include "hmm_test_utils.hpp" + using namespace mlpack; struct HMMGenerateTestFixture @@ -40,104 +42,6 @@ struct HMMGenerateTestFixture BOOST_FIXTURE_TEST_SUITE(HMMGenerateMainTest, HMMGenerateTestFixture); -struct Init -{ - template - static void Apply(HMMType& hmm, vector* 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& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Maximum observation is necessary so we know how to train the discrete - // distribution. - arma::Col maxEmissions(trainSeq[0].n_rows); - maxEmissions.zeros(); - for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); - ++it) - { - arma::Col maxSeqs = - arma::conv_to>::from(arma::max(*it, 1)) + 1; - maxEmissions = arma::max(maxEmissions, maxSeqs); - } - - hmm = HMM(size_t(states), - DiscreteDistribution(maxEmissions), tolerance); - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - //! Helper function for discrete emission distributions. - static void RandomInitialize(vector& 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& e) - { - // Not implemented - // Prevent unused parameter warning - (void)e; - } - - static void RandomInitialize(vector& e) - { - // Not implemented - // Prevent unused parameter warning - (void)e; - } -}; - -struct Train -{ - template - static void Apply(HMMType& hmm, vector* trainSeq) - { - // For now, perform unsupervised (Baum-Welch) training - hmm.Train(*trainSeq); - } -}; - BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) { // Train an HMM diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index c5a963350b..2088a193b3 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -19,6 +19,8 @@ static const std::string testName = "HMMLoglik"; #include #include "../test_tools.hpp" +#include "hmm_test_utils.hpp" + using namespace mlpack; struct HMMLoglikTestFixture @@ -40,104 +42,6 @@ struct HMMLoglikTestFixture BOOST_FIXTURE_TEST_SUITE(HMMLoglikMainTest, HMMLoglikTestFixture); -struct Init -{ - template - static void Apply(HMMType& hmm, vector* 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& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Maximum observation is necessary so we know how to train the discrete - // distribution. - arma::Col maxEmissions(trainSeq[0].n_rows); - maxEmissions.zeros(); - for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); - ++it) - { - arma::Col maxSeqs = - arma::conv_to>::from(arma::max(*it, 1)) + 1; - maxEmissions = arma::max(maxEmissions, maxSeqs); - } - - hmm = HMM(size_t(states), - DiscreteDistribution(maxEmissions), tolerance); - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused args warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused args warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - //! Helper function for discrete emission distributions. - static void RandomInitialize(vector& 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& e) - { - // Not implemented - // Prevent unused args warning - (void)e; - } - - static void RandomInitialize(vector& e) - { - // Not implemented - // Prevent unused args warning - (void)e; - } -}; - -struct Train -{ - template - static void Apply(HMMType& hmm, vector* trainSeq) - { - // For now, perform unsupervised (Baum-Welch) training - hmm.Train(*trainSeq); - } -}; - BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) { // Create an HMMModel diff --git a/src/mlpack/tests/main_tests/hmm_test_utils.hpp b/src/mlpack/tests/main_tests/hmm_test_utils.hpp new file mode 100644 index 0000000000..1a9647e702 --- /dev/null +++ b/src/mlpack/tests/main_tests/hmm_test_utils.hpp @@ -0,0 +1,105 @@ +#ifndef MLPACK_TESTS_MAIN_TESTS_HMM_TEST_UTILS_HPP +#define MLPACK_TESTS_MAIN_TESTS_HMM_TEST_UTILS_HPP + +#include +#include + +struct Init +{ + template + static void Apply(HMMType& hmm, vector* 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& hmm, + vector& trainSeq, + size_t states, + double tolerance = 1e-05) + { + // Maximum observation is necessary so we know how to train the discrete + // distribution. + arma::Col maxEmissions(trainSeq[0].n_rows); + maxEmissions.zeros(); + for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); + ++it) + { + arma::Col maxSeqs = + arma::conv_to>::from(arma::max(*it, 1)) + 1; + maxEmissions = arma::max(maxEmissions, maxSeqs); + } + + hmm = HMM(size_t(states), + DiscreteDistribution(maxEmissions), tolerance); + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance = 1e-05) + { + // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; + } + + static void Create(HMM& hmm, + vector& trainSeq, + size_t states, + double tolerance = 1e-05) + { + // Not implemented + // Prevent unused parameter warning + (void)hmm; + (void)trainSeq; + (void)states; + (void)tolerance; + } + + //! Helper function for discrete emission distributions. + static void RandomInitialize(vector& 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& e) + { + // Not implemented + // Prevent unused parameter warning + (void)e; + } + + static void RandomInitialize(vector& e) + { + // Not implemented + // Prevent unused parameter warning + (void)e; + } +}; + +struct Train +{ + template + static void Apply(HMMType& hmm, vector* trainSeq) + { + // For now, perform unsupervised (Baum-Welch) training + hmm.Train(*trainSeq); + } +}; + +#endif diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 15b7a2aeb2..e64916f18c 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -19,6 +19,8 @@ static const std::string testName = "HMMViterbi"; #include #include "../test_tools.hpp" +#include "hmm_test_utils.hpp" + using namespace mlpack; struct HMMViterbiTestFixture @@ -40,104 +42,6 @@ struct HMMViterbiTestFixture BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); -struct Init -{ - template - static void Apply(HMMType& hmm, vector* 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& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Maximum observation is necessary so we know how to train the discrete - // distribution. - arma::Col maxEmissions(trainSeq[0].n_rows); - maxEmissions.zeros(); - for (vector::iterator it = trainSeq.begin(); it != trainSeq.end(); - ++it) - { - arma::Col maxSeqs = - arma::conv_to>::from(arma::max(*it, 1)) + 1; - maxEmissions = arma::max(maxEmissions, maxSeqs); - } - - hmm = HMM(size_t(states), - DiscreteDistribution(maxEmissions), tolerance); - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - static void Create(HMM& hmm, - vector& trainSeq, - size_t states, - double tolerance = 1e-05) - { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; - } - - //! Helper function for discrete emission distributions. - static void RandomInitialize(vector& 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& e) - { - // Not implemented - // Prevent unused parameter warning - (void)e; - } - - static void RandomInitialize(vector& e) - { - // Not implemented - // Prevent unused parameter warning - (void)e; - } -}; - -struct Train -{ - template - static void Apply(HMMType& hmm, vector* trainSeq) - { - // For now, perform unsupervised (Baum-Welch) training - hmm.Train(*trainSeq); - } -}; - BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) { // Train an HMM From 6cc37712dcb38774786593f4d74a7c9ef7fb0e51 Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 09:04:53 +0000 Subject: [PATCH 11/17] Minor change --- src/mlpack/tests/main_tests/hmm_viterbi_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index e64916f18c..cb92932161 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -62,9 +62,9 @@ BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) mlpackMain(); arma::Mat out = CLI::GetParam >("output"); - BOOST_REQUIRE(out.n_rows == inp.n_rows); - BOOST_REQUIRE(out.n_cols == inp.n_cols); - BOOST_REQUIRE(out.n_elem == inp.n_elem); + + BOOST_REQUIRE_EQUAL(out.n_rows, 1); + BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols); } BOOST_AUTO_TEST_SUITE_END(); From 65dda6eefa242c38b4f8be851aaf33e731babe67 Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 09:23:58 +0000 Subject: [PATCH 12/17] Adapt Train and Init structs for Gaussian HMMs and GMM HMMs --- .../tests/main_tests/hmm_test_utils.hpp | 93 +++++++++++++++---- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_test_utils.hpp b/src/mlpack/tests/main_tests/hmm_test_utils.hpp index 1a9647e702..ecfc21f5cd 100644 --- a/src/mlpack/tests/main_tests/hmm_test_utils.hpp +++ b/src/mlpack/tests/main_tests/hmm_test_utils.hpp @@ -1,3 +1,16 @@ +/** + * @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. + * + * 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 @@ -46,12 +59,23 @@ struct Init size_t states, double tolerance = 1e-05) { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; + // 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(size_t(states), + GaussianDistribution(dimensionality), tolerance); } static void Create(HMM& hmm, @@ -59,12 +83,25 @@ struct Init size_t states, double tolerance = 1e-05) { - // Not implemented - // Prevent unused parameter warning - (void)hmm; - (void)trainSeq; - (void)states; - (void)tolerance; + // 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(size_t(states), GMM(size_t(gaussians), dimensionality), + tolerance); } //! Helper function for discrete emission distributions. @@ -79,16 +116,36 @@ struct Init static void RandomInitialize(vector& e) { - // Not implemented - // Prevent unused parameter warning - (void)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(dimensionality, dimensionality); + e[i].Covariance(r * r.t()); + } } static void RandomInitialize(vector& e) { - // Not implemented - // Prevent unused parameter warning - (void)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 < CLI::GetParam("gaussians"); ++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(dimensionality, + dimensionality); + e[i].Component(g).Covariance(r * r.t()); + } + } } }; From 19a5c826a48caf07f81ee294b7d51f0dca395aeb Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 10:37:20 +0000 Subject: [PATCH 13/17] Add tests for Gaussian HMMs and GMM HMMs --- .../tests/main_tests/hmm_generate_test.cpp | 64 ++++++++++++++++++- .../tests/main_tests/hmm_test_utils.hpp | 2 +- .../tests/main_tests/hmm_viterbi_test.cpp | 52 ++++++++++++++- 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index a8df8476d7..9e15e2949a 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -42,7 +42,7 @@ struct HMMGenerateTestFixture BOOST_FIXTURE_TEST_SUITE(HMMGenerateMainTest, HMMGenerateTestFixture); -BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) +BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) { // Train an HMM HMMModel * h = new HMMModel(DiscreteHMM); @@ -73,6 +73,68 @@ BOOST_AUTO_TEST_CASE(HMMGenerateCheckDimensionsTest) BOOST_REQUIRE_EQUAL(stateSeq.n_elem, (size_t)length); } +BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(GaussianHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_generate invocation + int length = 3; + SetInputParam("model", h); + SetInputParam("length", length); + + mlpackMain(); + + arma::mat obsSeq = CLI::GetParam("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); + + arma::Mat stateSeq = CLI::GetParam>("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) +{ + // Train an HMM + HMMModel * h = new HMMModel(GaussianMixtureModelHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_generate invocation + int length = 3; + SetInputParam("model", h); + SetInputParam("length", length); + + mlpackMain(); + + arma::mat obsSeq = CLI::GetParam("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); + + arma::Mat stateSeq = CLI::GetParam>("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) { // Train an HMM diff --git a/src/mlpack/tests/main_tests/hmm_test_utils.hpp b/src/mlpack/tests/main_tests/hmm_test_utils.hpp index ecfc21f5cd..21bb325f80 100644 --- a/src/mlpack/tests/main_tests/hmm_test_utils.hpp +++ b/src/mlpack/tests/main_tests/hmm_test_utils.hpp @@ -135,7 +135,7 @@ struct Init e[i].Weights() /= arma::accu(e[i].Weights()); // Random means and covariances. - for (int g = 0; g < CLI::GetParam("gaussians"); ++g) + for (int g = 0; g < 2; ++g) { const size_t dimensionality = e[i].Component(g).Mean().n_rows; e[i].Component(g).Mean().randu(); diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index cb92932161..7d5e8483d2 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -42,7 +42,7 @@ struct HMMViterbiTestFixture BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); -BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) +BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest) { // Train an HMM HMMModel * h = new HMMModel(DiscreteHMM); @@ -67,4 +67,54 @@ BOOST_AUTO_TEST_CASE(HMMViterbiCheckDimensionsTest) BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols); } +BOOST_AUTO_TEST_CASE(HMMViterbiGaussianHMMCheckDimensionsTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(GaussianHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_viterbi invocation + SetInputParam("input_model", h); + SetInputParam("input", inp); + + mlpackMain(); + + arma::Mat out = CLI::GetParam >("output"); + + BOOST_REQUIRE_EQUAL(out.n_rows, 1); + BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols); +} + +BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest) +{ + // Train an HMM + HMMModel * h = new HMMModel(GaussianMixtureModelHMM); + // Load data + arma::mat inp; + data::Load("obs1.csv", inp); + std::vector trainSeq = {inp}; + // Init + h->PerformAction>(&trainSeq); + // Train + h->PerformAction>(&trainSeq); + + // Set the params for the hmm_viterbi invocation + SetInputParam("input_model", h); + SetInputParam("input", inp); + + mlpackMain(); + + arma::Mat out = CLI::GetParam >("output"); + + BOOST_REQUIRE_EQUAL(out.n_rows, 1); + BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols); +} + BOOST_AUTO_TEST_SUITE_END(); From b1a46d49c7157c1d62bf5faa48ce0f121a09934b Mon Sep 17 00:00:00 2001 From: daivik Date: Tue, 6 Mar 2018 16:15:44 +0000 Subject: [PATCH 14/17] Fix chol() fail --- .../tests/main_tests/hmm_generate_test.cpp | 54 ++++++++++++++++--- .../tests/main_tests/hmm_viterbi_test.cpp | 54 ++++++++++++++++--- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index 9e15e2949a..e0fa671557 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -109,13 +109,53 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) // Train an HMM HMMModel * h = new HMMModel(GaussianMixtureModelHMM); // Load data - arma::mat inp; - data::Load("obs1.csv", inp); - std::vector trainSeq = {inp}; + std::vector 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 observations(5, arma::mat(2, 50)); + std::vector > states(5, arma::Row(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(); + } + } // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&observations); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&observations); // Set the params for the hmm_generate invocation int length = 3; @@ -126,8 +166,8 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) arma::mat obsSeq = CLI::GetParam("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); + BOOST_REQUIRE_EQUAL(obsSeq.n_rows, (size_t)2); + BOOST_REQUIRE_EQUAL(obsSeq.n_elem, (size_t)(length*2)); arma::Mat stateSeq = CLI::GetParam>("state"); BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length); diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 7d5e8483d2..015bcdaf88 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -97,24 +97,64 @@ BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest) // Train an HMM HMMModel * h = new HMMModel(GaussianMixtureModelHMM); // Load data - arma::mat inp; - data::Load("obs1.csv", inp); - std::vector trainSeq = {inp}; + std::vector 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 observations(5, arma::mat(2, 50)); + std::vector > states(5, arma::Row(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(); + } + } // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&observations); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&observations); // Set the params for the hmm_viterbi invocation SetInputParam("input_model", h); - SetInputParam("input", inp); + SetInputParam("input", observations[0]); mlpackMain(); arma::Mat out = CLI::GetParam >("output"); BOOST_REQUIRE_EQUAL(out.n_rows, 1); - BOOST_REQUIRE_EQUAL(out.n_cols, inp.n_cols); + BOOST_REQUIRE_EQUAL(out.n_cols, observations[0].n_cols); } BOOST_AUTO_TEST_SUITE_END(); From 3398ad82d5f6b4b7d0246cf99b238db342411156 Mon Sep 17 00:00:00 2001 From: daivik Date: Fri, 9 Mar 2018 09:48:15 +0000 Subject: [PATCH 15/17] Fix build by renaming structs in hmm_test_utils --- .../tests/main_tests/hmm_generate_test.cpp | 20 +++++++++---------- .../tests/main_tests/hmm_loglik_test.cpp | 4 ++-- .../tests/main_tests/hmm_test_utils.hpp | 4 ++-- .../tests/main_tests/hmm_viterbi_test.cpp | 14 ++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index e0fa671557..1d41c5f268 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -51,9 +51,9 @@ BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_generate invocation int length = 3; @@ -82,9 +82,9 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_generate invocation int length = 3; @@ -153,9 +153,9 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) } } // Init - h->PerformAction>(&observations); + h->PerformAction>(&observations); // Train - h->PerformAction>(&observations); + h->PerformAction>(&observations); // Set the params for the hmm_generate invocation int length = 3; @@ -184,9 +184,9 @@ BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_generate invocation int length = -3; // Invalid @@ -207,9 +207,9 @@ BOOST_AUTO_TEST_CASE(HMMGenerateValidStartStateTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); int length = 3; int startState = 2; // Invalid diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index 2088a193b3..bfa367bd2a 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -51,9 +51,9 @@ BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init HMMModel - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train HMMModel - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_loglik invocation diff --git a/src/mlpack/tests/main_tests/hmm_test_utils.hpp b/src/mlpack/tests/main_tests/hmm_test_utils.hpp index 21bb325f80..934f1506a7 100644 --- a/src/mlpack/tests/main_tests/hmm_test_utils.hpp +++ b/src/mlpack/tests/main_tests/hmm_test_utils.hpp @@ -17,7 +17,7 @@ #include #include -struct Init +struct InitHMMModel { template static void Apply(HMMType& hmm, vector* trainSeq) @@ -149,7 +149,7 @@ struct Init } }; -struct Train +struct TrainHMMModel { template static void Apply(HMMType& hmm, vector* trainSeq) diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 015bcdaf88..0dc6b645d4 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -44,16 +44,16 @@ BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest) { - // Train an HMM +// Train an HMM HMMModel * h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_viterbi invocation SetInputParam("input_model", h); @@ -76,9 +76,9 @@ BOOST_AUTO_TEST_CASE(HMMViterbiGaussianHMMCheckDimensionsTest) data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; // Init - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Train - h->PerformAction>(&trainSeq); + h->PerformAction>(&trainSeq); // Set the params for the hmm_viterbi invocation SetInputParam("input_model", h); @@ -141,9 +141,9 @@ BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest) } } // Init - h->PerformAction>(&observations); + h->PerformAction>(&observations); // Train - h->PerformAction>(&observations); + h->PerformAction>(&observations); // Set the params for the hmm_viterbi invocation SetInputParam("input_model", h); From 6388a1fb65397dfd8bbd4f4b50abe964fe1fed61 Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 10 Mar 2018 20:00:00 +0000 Subject: [PATCH 16/17] HMMModel * --> HMMModel* --- src/mlpack/tests/main_tests/hmm_generate_test.cpp | 10 +++++----- src/mlpack/tests/main_tests/hmm_loglik_test.cpp | 2 +- src/mlpack/tests/main_tests/hmm_viterbi_test.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index 1d41c5f268..d1a99c5dbb 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(HMMGenerateMainTest, HMMGenerateTestFixture); BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(DiscreteHMM); + HMMModel* h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(GaussianHMM); + HMMModel* h = new HMMModel(GaussianHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(GaussianMixtureModelHMM); + HMMModel* h = new HMMModel(GaussianMixtureModelHMM); // Load data std::vector gmms(2, GMM(2, 2)); gmms[0].Weights() = arma::vec("0.3 0.7"); @@ -178,7 +178,7 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) { // Train an HMM - HMMModel * h = new HMMModel(DiscreteHMM); + HMMModel* h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); @@ -201,7 +201,7 @@ BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) BOOST_AUTO_TEST_CASE(HMMGenerateValidStartStateTest) { // Train an HMM - HMMModel * h = new HMMModel(DiscreteHMM); + HMMModel* h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index bfa367bd2a..582e7b0ce6 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(HMMLoglikMainTest, HMMLoglikTestFixture); BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) { // Create an HMMModel - HMMModel * h = new HMMModel(DiscreteHMM); + HMMModel* h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 0dc6b645d4..6093e514cd 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(DiscreteHMM); + HMMModel* h = new HMMModel(DiscreteHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMViterbiGaussianHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(GaussianHMM); + HMMModel* h = new HMMModel(GaussianHMM); // Load data arma::mat inp; data::Load("obs1.csv", inp); @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(HMMViterbiGaussianHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest) { // Train an HMM - HMMModel * h = new HMMModel(GaussianMixtureModelHMM); + HMMModel* h = new HMMModel(GaussianMixtureModelHMM); // Load data std::vector gmms(2, GMM(2, 2)); gmms[0].Weights() = arma::vec("0.3 0.7"); From a7fd87bc589342c5928ff230c42a8ebfb822b3fc Mon Sep 17 00:00:00 2001 From: daivik Date: Sat, 10 Mar 2018 21:59:42 +0000 Subject: [PATCH 17/17] Better comments --- .../tests/main_tests/hmm_generate_test.cpp | 80 ++++++++++++------- .../tests/main_tests/hmm_loglik_test.cpp | 11 +-- .../tests/main_tests/hmm_test_utils.hpp | 5 +- .../tests/main_tests/hmm_viterbi_test.cpp | 54 ++++++++----- 4 files changed, 97 insertions(+), 53 deletions(-) diff --git a/src/mlpack/tests/main_tests/hmm_generate_test.cpp b/src/mlpack/tests/main_tests/hmm_generate_test.cpp index d1a99c5dbb..73a7e6cddd 100644 --- a/src/mlpack/tests/main_tests/hmm_generate_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_generate_test.cpp @@ -44,29 +44,36 @@ BOOST_FIXTURE_TEST_SUITE(HMMGenerateMainTest, HMMGenerateTestFixture); BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) { - // Train an HMM - HMMModel* h = new HMMModel(DiscreteHMM); - // Load data + // Load data to train a discrete HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a discrete HMM model. + HMMModel* h = new HMMModel(DiscreteHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&trainSeq); - // Set the params for the hmm_generate invocation + // 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("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 stateSeq = CLI::GetParam>("state"); BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length); BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1); @@ -75,29 +82,36 @@ BOOST_AUTO_TEST_CASE(HMMGenerateDiscreteHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) { - // Train an HMM - HMMModel* h = new HMMModel(GaussianHMM); - // Load data + // Load data to train a gaussian HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a gaussian HMM model. + HMMModel* h = new HMMModel(GaussianHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&trainSeq); - // Set the params for the hmm_generate invocation + // 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("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 stateSeq = CLI::GetParam>("state"); BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length); BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1); @@ -106,9 +120,7 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGaussianHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) { - // Train an HMM - HMMModel* h = new HMMModel(GaussianMixtureModelHMM); - // Load data + // Load data to train a Gaussian Mixture Model HMM model with. std::vector gmms(2, GMM(2, 2)); gmms[0].Weights() = arma::vec("0.3 0.7"); @@ -152,23 +164,32 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) observations[obs].col(i) = gmms[states[obs][i]].Random(); } } - // Init + + // Initialize and train a GMM HMM model. + HMMModel* h = new HMMModel(GaussianMixtureModelHMM); h->PerformAction>(&observations); - // Train h->PerformAction>(&observations); - // Set the params for the hmm_generate invocation + // 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("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 stateSeq = CLI::GetParam>("state"); BOOST_REQUIRE_EQUAL(stateSeq.n_cols, (size_t)length); BOOST_REQUIRE_EQUAL(stateSeq.n_rows, (size_t)1); @@ -177,18 +198,19 @@ BOOST_AUTO_TEST_CASE(HMMGenerateGMMHMMCheckDimensionsTest) BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) { - // Train an HMM - HMMModel* h = new HMMModel(DiscreteHMM); - // Load data + // Load data to train a Gaussian Mixture Model HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a HMM model. + HMMModel* h = new HMMModel(DiscreteHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&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); @@ -200,17 +222,19 @@ BOOST_AUTO_TEST_CASE(HMMGenerateLengthPositiveTest) BOOST_AUTO_TEST_CASE(HMMGenerateValidStartStateTest) { - // Train an HMM - HMMModel* h = new HMMModel(DiscreteHMM); - // Load data + // Load data to train a Gaussian Mixture Model HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a HMM model. + HMMModel* h = new HMMModel(DiscreteHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&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); diff --git a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp index 582e7b0ce6..133e3c7b93 100644 --- a/src/mlpack/tests/main_tests/hmm_loglik_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_loglik_test.cpp @@ -44,15 +44,14 @@ BOOST_FIXTURE_TEST_SUITE(HMMLoglikMainTest, HMMLoglikTestFixture); BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) { - // Create an HMMModel - HMMModel* h = new HMMModel(DiscreteHMM); - // Load data + // Load data to train a discrete HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init HMMModel + + // Initialize and train an HMM model. + HMMModel* h = new HMMModel(DiscreteHMM); h->PerformAction>(&trainSeq); - // Train HMMModel h->PerformAction>(&trainSeq); @@ -63,6 +62,8 @@ BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest) mlpackMain(); double loglik = CLI::GetParam("log_likelihood"); + + // Since the log of a probability <= 0 ... BOOST_REQUIRE(loglik <= 0); } diff --git a/src/mlpack/tests/main_tests/hmm_test_utils.hpp b/src/mlpack/tests/main_tests/hmm_test_utils.hpp index 934f1506a7..cf9d06fcc5 100644 --- a/src/mlpack/tests/main_tests/hmm_test_utils.hpp +++ b/src/mlpack/tests/main_tests/hmm_test_utils.hpp @@ -4,7 +4,8 @@ * * 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. + * 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 @@ -154,7 +155,7 @@ struct TrainHMMModel template static void Apply(HMMType& hmm, vector* trainSeq) { - // For now, perform unsupervised (Baum-Welch) training + // For now, perform unsupervised (Baum-Welch) training. hmm.Train(*trainSeq); } }; diff --git a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp index 6093e514cd..7b8dec4905 100644 --- a/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp +++ b/src/mlpack/tests/main_tests/hmm_viterbi_test.cpp @@ -44,59 +44,69 @@ BOOST_FIXTURE_TEST_SUITE(HMMViterbiMainTest, HMMViterbiTestFixture); BOOST_AUTO_TEST_CASE(HMMViterbiDiscreteHMMCheckDimensionsTest) { -// Train an HMM - HMMModel* h = new HMMModel(DiscreteHMM); - // Load data + // Load data to train a discrete HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a discrete HMM model. + HMMModel* h = new HMMModel(DiscreteHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&trainSeq); - // Set the params for the hmm_viterbi invocation + // 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 out = CLI::GetParam >("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) { - // Train an HMM - HMMModel* h = new HMMModel(GaussianHMM); - // Load data + // Load data to train a gaussian HMM model with. arma::mat inp; data::Load("obs1.csv", inp); std::vector trainSeq = {inp}; - // Init + + // Initialize and train a gaussian HMM model. + HMMModel* h = new HMMModel(GaussianHMM); h->PerformAction>(&trainSeq); - // Train h->PerformAction>(&trainSeq); - // Set the params for the hmm_viterbi invocation + // 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 out = CLI::GetParam >("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) { - // Train an HMM - HMMModel* h = new HMMModel(GaussianMixtureModelHMM); - // Load data + // Load data to train a Gaussian Mixture Model HMM model with. std::vector gmms(2, GMM(2, 2)); gmms[0].Weights() = arma::vec("0.3 0.7"); @@ -140,19 +150,27 @@ BOOST_AUTO_TEST_CASE(HMMViterbiGMMHMMCheckDimensionsTest) observations[obs].col(i) = gmms[states[obs][i]].Random(); } } - // Init + + // Initialize and train a GMM HMM model. + HMMModel* h = new HMMModel(GaussianMixtureModelHMM); h->PerformAction>(&observations); - // Train h->PerformAction>(&observations); - // Set the params for the hmm_viterbi invocation + // 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 out = CLI::GetParam >("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); }