diff --git a/src/machineLearning/rapidXmmTools/rapidXmmTools.cpp b/src/machineLearning/rapidXmmTools/rapidXmmTools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..280093ca410ffa18f8e53fc699fe416496f92fbf --- /dev/null +++ b/src/machineLearning/rapidXmmTools/rapidXmmTools.cpp @@ -0,0 +1,201 @@ +#include "rapidXmmTools.h" +#include "trainingData.h" +#include "machineLearning.h" + +static bool trainingData2xmmTrainingSet(const rapidmix::trainingData& data, xmm::TrainingSet& set) { + if (data.trainingSet.size() <= 1) { + // no recorded phrase (only default one) + return false; + } + + if (data.trainingSet.size() > 1 && data.trainingSet[1].elements.size() == 0) { + // empty recorded phrase + return false; + } + + rapidmix::trainingData::element el = data.trainingSet[1].elements[0]; + int dimIn = static_cast<int>(el.input.size()); + int dimOut = static_cast<int>(el.output.size()); + + // translate and return true if data and set are compatible + // don't translate and return false otherwise + + if (dimOut > 0 != set.bimodal()) { + return false; + } + + xmm::Phrase xp; + + if (set.bimodal()) { + set.dimension.set(dimIn + dimOut); + set.dimension_input.set(dimIn); + xp = xmm::Phrase(xmm::MemoryMode::OwnMemory, xmm::Multimodality::Bimodal); + xp.dimension.set(dimIn + dimOut); + xp.dimension_input.set(dimIn); + } else { + set.dimension.set(dimIn); + set.dimension_input.set(0); + xp = xmm::Phrase(xmm::MemoryMode::OwnMemory, xmm::Multimodality::Unimodal); + xp.dimension.set(dimIn); + xp.dimension_input.set(0); + } + + set.clear(); + + //for (auto &phrase : data.trainingSet) { + // changed to look starting from index 1 + // because phrase at index 0 is for unordered elements + for (int i = 1; i < data.trainingSet.size(); ++i) { + const rapidmix::trainingData::phrase &phrase = data.trainingSet[i]; + xp.clear(); + xp.label.set(phrase.label); + + for (auto &element : phrase.elements) { + std::vector<float> obsIn(element.input.begin(), element.input.end()); + std::vector<float> obsOut(element.output.begin(), element.output.end()); + std::vector<float> obs; + obs.insert(obs.end(), obsIn.begin(), obsIn.end()); + obs.insert(obs.end(), obsOut.begin(), obsOut.end()); + xp.record(obs); + } + + set.addPhrase(static_cast<int>(set.size()), xp); + } + + return true; +} + +//=============================== xmmTool ====================================// + +template <class SingleClassModel, class Model> +bool xmmTool<SingleClassModel, Model>::train(const rapidmix::trainingData& newTrainingData) { + if (trainingData2xmmTrainingSet(newTrainingData, set)) { + model.train(&set); + model.reset(); + return true; + } + + return false; +} + +////////// private JSON data manipulation methods : + +//TODO: add a type field (gmm/gmr/hmm/hmr) in metadata when family is xmm +template <class SingleClassModel, class Model> +Json::Value xmmTool<SingleClassModel, Model>::toJSON(/*std::string modelType*/) { + Json::Value root; + Json::Value metadata; + Json::Value modelSet; + + metadata["creator"] = "Rapid API C++"; + metadata["version"] = "v0.1.1"; //TODO: This should be a macro someplace + metadata["family"] = "xmm"; + root["metadata"] = metadata; + + modelSet.append(model.toJson()); + root["modelSet"] = modelSet; + + return root; +} + +template <class SingleClassModel, class Model> +bool xmmTool<SingleClassModel, Model>::fromJSON(Json::Value &jm) { + if (jm["metadata"]["family"].asString().compare("xmm") == 0 && + jm["modelSet"].size() > 0) { + model.fromJson(jm["modelSet"][0]); + model.reset(); + return true; + } + + return false; +} + +////////// public JSON file manipulation interface : + +template <class SingleClassModel, class Model> +std::string xmmTool<SingleClassModel, Model>::getJSON() { + Json::Value result = toJSON(); + return result.toStyledString(); +} + +template <class SingleClassModel, class Model> +void xmmTool<SingleClassModel, Model>::writeJSON(const std::string &filepath) { + Json::Value root = toJSON(); + std::ofstream jsonOut; + jsonOut.open (filepath); + Json::StyledStreamWriter writer; + writer.write(jsonOut, root); + jsonOut.close(); +} + +template <class SingleClassModel, class Model> +bool xmmTool<SingleClassModel, Model>::putJSON(const std::string &jsonMessage) { + Json::Value parsedFromString; + Json::Reader reader; + bool parsingSuccessful = reader.parse(jsonMessage, parsedFromString); + return (parsingSuccessful && fromJSON(parsedFromString)); +} + +template <class SingleClassModel, class Model> +bool xmmTool<SingleClassModel, Model>::readJSON(const std::string &filepath) { + Json::Value root; + std::ifstream file(filepath); + file >> root; + return fromJSON(root); +} + +//============================== xmmGmmTool ==================================// + +std::vector<double> xmmGmmTool::process(const std::vector<double>& inputVector) { + xmmTool::preProcess(inputVector); + return model.results.smoothed_normalized_likelihoods; +} + +//============================== xmmGmrTool ==================================// + +std::vector<double> xmmGmrTool::process(const std::vector<double>& inputVector) { + xmmTool::preProcess(inputVector); + std::vector<float> *res = &model.results.output_values; + std::vector<double> dRes(res->begin(), res->end()); + return dRes; +} + +//============================== xmmHmmTool ==================================// + +std::vector<double> xmmHmmTool::process(const std::vector<double>& inputVector) { + xmmTool::preProcess(inputVector); + std::vector<double> res; + + int i(0); + for (auto &m : model.models) { + res.push_back(model.results.smoothed_normalized_likelihoods[i]); + res.push_back(m.second.results.progress); + i++; + } + + return res; +} + +//============================== xmmHmrTool ==================================// + +std::vector<double> xmmHmrTool::process(const std::vector<double>& inputVector) { + xmmTool::preProcess(inputVector); + std::vector<float> *res = &model.results.output_values; + std::vector<double> dRes(res->begin(), res->end()); + return dRes; +} + +/////////////////////////////////////////////////////////////////////////// +///// generic train method and forward declaration of specialized templates +/////////////////////////////////////////////////////////////////////////// + +template <class MachineLearningModule> +bool rapidmix::machineLearning<MachineLearningModule>::train(const trainingData &newTrainingData) { + return MachineLearningModule::train(newTrainingData); +} + +template class rapidmix::machineLearning<xmmGmmTool>; +template class rapidmix::machineLearning<xmmGmrTool>; +template class rapidmix::machineLearning<xmmHmmTool>; +template class rapidmix::machineLearning<xmmHmrTool>; + diff --git a/src/machineLearning/rapidXmmTools/rapidXmmTools.h b/src/machineLearning/rapidXmmTools/rapidXmmTools.h new file mode 100644 index 0000000000000000000000000000000000000000..0c166ba06f9f5cd70d163de24f9ce9e2bc5365cb --- /dev/null +++ b/src/machineLearning/rapidXmmTools/rapidXmmTools.h @@ -0,0 +1,249 @@ +#ifndef _RAPID_XMM_TOOLS_H_ +#define _RAPID_XMM_TOOLS_H_ + +// this works ! +#ifndef EXTERNAL_JSONCPP_PATH +// #define EXTERNAL_JSONCPP_PATH "../../../../json/json.h" // relative to xmmJson.h +#define EXTERNAL_JSONCPP_PATH "json.h" +#endif /* EXTERNAL_JSONCPP_PATH */ + +#include "xmm.h" + +// forward declaration +namespace rapidmix { class trainingData; } + +// original defined in xmmModelConfiguration.hpp +enum xmmRegressionEstimator { + xmmLikeliestRegression, + xmmMixtureRegression +}; + +// original defined in xmmGaussianDistribution.hpp +enum xmmCovarianceMode { + xmmFullCovariance, + xmmDiagonalCovariance +}; + +// original defined in xmmHmmParameters.hpp +enum xmmHmmTransitionMode { + xmmHmmLeftRightTransition, + xmmHmmErgodicTransition +}; + +// original defined in xmmHmmParameters.hpp +enum xmmHmmRegressionEstimator { + xmmHmmFullRegression, + xmmHmmWindowedRegression, + xmmHmmLikeliestRegression +}; + +// this is the optional argument of machineLearning<xmmWhateverTool>'s constructors +struct xmmToolConfig { + xmmToolConfig() : + gaussians(1), + relativeRegularization(0.01), + absoluteRegularization(0.01), + regressionEstimator(xmmMixtureRegression), + covarianceMode(xmmFullCovariance), + states(5), + hierarchical(true), + hmmTransitionMode(xmmHmmErgodicTransition), + hmmRegressionEstimator(xmmHmmFullRegression), + likelihoodWindow(5) {} + + // general parameters : + uint32_t gaussians; + float relativeRegularization; + float absoluteRegularization; + xmmRegressionEstimator regressionEstimator; + xmmCovarianceMode covarianceMode; + + // hmm specific : + uint32_t states; + bool hierarchical; + xmmHmmTransitionMode hmmTransitionMode; + xmmHmmRegressionEstimator hmmRegressionEstimator; + + // run-time parameter : + uint32_t likelihoodWindow; +}; + +//========================== template base class =============================// + +template <class SingleClassModel, class Model> +class xmmTool { +protected: + xmmTool(bool bimodal) { + model = Model(bimodal); + model.configuration.multithreading = xmm::MultithreadingMode::Sequential; + model.configuration.changed = true; + + set = xmm::TrainingSet(xmm::MemoryMode::OwnMemory, + bimodal + ? xmm::Multimodality::Bimodal + : xmm::Multimodality::Unimodal + ); + } + + virtual void preProcess(const std::vector<double> &inputVector) { + std::vector<float> fv(inputVector.begin(), inputVector.end()); + model.filter(fv); + } + +public: + virtual ~xmmTool() {} + + virtual bool train(const rapidmix::trainingData &newTrainingData); + + virtual bool reset() { + model.reset(); + return true; + } + + /** Get a JSON representation of the model in the form of a styled string */ + virtual std::string getJSON(); + /** Write a JSON model description to specified file path */ + virtual void writeJSON(const std::string &filepath); + /** configure empty model with string. See getJSON() */ + virtual bool putJSON(const std::string &jsonMessage); + /** read a JSON file at file path and build a modelSet from it */ + virtual bool readJSON(const std::string &filepath); + +protected: + Model model; + xmm::TrainingSet set; + + Json::Value toJSON(); + bool fromJSON(Json::Value &jm); +}; + +//======================= base class for GMM models ==========================// + +template <class SingleClassModel, class Model> +class xmmStaticTool : public xmmTool<SingleClassModel, Model> { +protected: + xmmStaticTool(xmmToolConfig cfg, bool bimodal) : + xmmTool<SingleClassModel, Model>(bimodal) { + xmm::Configuration<SingleClassModel>& mCfg = this->model.configuration; + + mCfg.gaussians.set(cfg.gaussians); + mCfg.relative_regularization.set(cfg.relativeRegularization); + mCfg.absolute_regularization.set(cfg.absoluteRegularization); + + xmm::MultiClassRegressionEstimator mcre; + switch (cfg.regressionEstimator) { + case xmmLikeliestRegression: + mcre = xmm::MultiClassRegressionEstimator::Likeliest; + case xmmMixtureRegression: + default: + mcre = xmm::MultiClassRegressionEstimator::Mixture; + break; + } + mCfg.multiClass_regression_estimator = mcre; + + xmm::GaussianDistribution::CovarianceMode gdcm; + switch (cfg.covarianceMode) { + case xmmFullCovariance: + gdcm = xmm::GaussianDistribution::CovarianceMode::Full; + break; + case xmmDiagonalCovariance: + default: + gdcm = xmm::GaussianDistribution::CovarianceMode::Diagonal; + break; + } + mCfg.covariance_mode.set(gdcm); + + mCfg.changed = true; + + this->model.shared_parameters->likelihood_window.set(cfg.likelihoodWindow); + } + +public: + virtual ~xmmStaticTool() {} +}; + +//======================= base class for HMM models ==========================// + +template <class SingleClassModel, class Model> +class xmmTemporalTool : public xmmStaticTool<SingleClassModel, Model> { +protected: + xmmTemporalTool(xmmToolConfig cfg, bool bimodal) : + xmmStaticTool<SingleClassModel, Model>(cfg, bimodal) { + xmm::Configuration<SingleClassModel>& mCfg = this->model.configuration; + + mCfg.states.set(cfg.states); + mCfg.hierarchical.set(cfg.hierarchical); + + xmm::HMM::TransitionMode htm; + switch (cfg.hmmTransitionMode) { + case xmmHmmLeftRightTransition: + htm = xmm::HMM::TransitionMode::LeftRight; + break; + case xmmHmmErgodicTransition: + default: + htm = xmm::HMM::TransitionMode::Ergodic; + break; + } + mCfg.transition_mode.set(htm); + + xmm::HMM::RegressionEstimator hre; + switch (cfg.hmmRegressionEstimator) { + case xmmHmmFullRegression: + hre = xmm::HMM::RegressionEstimator::Full; + break; + case xmmHmmWindowedRegression: + hre = xmm::HMM::RegressionEstimator::Windowed; + break; + case xmmHmmLikeliestRegression: + default: + hre = xmm::HMM::RegressionEstimator::Likeliest; + break; + } + mCfg.regression_estimator.set(hre); + + mCfg.changed = true; + } + +public: + virtual ~xmmTemporalTool() {} +}; + +//================== actual classes used in machineLearning.h ================// + +class xmmGmmTool : public xmmStaticTool<xmm::GMM, xmm::GMM> { +public: + xmmGmmTool(xmmToolConfig cfg = xmmToolConfig()) : + xmmStaticTool<xmm::GMM, xmm::GMM>(cfg, false) {} + ~xmmGmmTool() {} + + std::vector<double> process(const std::vector<double>& inputVector); +}; + +class xmmGmrTool : public xmmStaticTool<xmm::GMM, xmm::GMM> { +public: + xmmGmrTool(xmmToolConfig cfg = xmmToolConfig()) : + xmmStaticTool<xmm::GMM, xmm::GMM>(cfg, true) {} + ~xmmGmrTool() {} + + std::vector<double> process(const std::vector<double>& inputVector); +}; + +class xmmHmmTool : public xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM> { +public: + xmmHmmTool(xmmToolConfig cfg = xmmToolConfig()) : + xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM>(cfg, false) {} + ~xmmHmmTool() {} + + std::vector<double> process(const std::vector<double>& inputVector); +}; + +class xmmHmrTool : public xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM> { +public: + xmmHmrTool(xmmToolConfig cfg = xmmToolConfig()) : + xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM>(cfg, true) {} + ~xmmHmrTool() {} + + std::vector<double> process(const std::vector<double>& inputVector); +}; + +#endif /* _RAPID_XMM_TOOLS_H_ */