Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 3009 additions and 12 deletions
/**
* @file rapidGVF.h
* Created by Francisco on 04/05/2017.
* Copyright © 2017 Goldsmiths. All rights reserved.
*
* @ingroup machinelearning
*/
#ifndef rapidGVF_h
#define rapidGVF_h
#include <vector>
#include <string>
#include "GVF.h"
namespace rapidmix { class trainingData; }
/**
* @brief This class is an adapter for the GVF library from Baptiste Caramiaux
*/
class rapidGVF {
public:
rapidGVF();
~rapidGVF();
bool train(const rapidmix::trainingData &newTrainingData);
std::vector<double> run(const std::vector<double> &inputVector);
//TODO: needs a "reset" message
const std::vector<float> getLikelihoods();
const std::vector<float> getAlignments();
const std::vector<std::vector<float> > * getDynamics();
const std::vector<std::vector<float> > * getScalings();
const std::vector<std::vector<float> > * getRotations();
private:
GVF gvf;
GVFGesture currentGesture;
GVFOutcomes outcomes;
};
#endif
#include "rapidXMM.h"
#include "../trainingData.h"
#include "../machineLearning.h"
static bool trainingData2xmmTrainingSet(const rapidmix::trainingData& data, xmm::TrainingSet& set) {
if (data.trainingSet.size() < 1) {
return false;
}
if (data.trainingSet.size() > 0 && data.trainingSet[0].elements.size() == 0) {
// empty recorded phrase
return false;
}
rapidmix::trainingData::element el = data.trainingSet[0].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) {
for (int i = 0; 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;
root["docType"] = "rapid-mix:ml-model";
root["docVersion"] = RAPIDMIX_JSON_DOC_VERSION;
Json::Value target;
target["name"] = "xmm";
target["version"] = "v1.0.0";
root["target"] = target;
root["payload"] = model.toJson();
return root;
}
template <class SingleClassModel, class Model>
bool xmmTool<SingleClassModel, Model>::fromJSON(Json::Value &jm) {
if (jm["docType"].asString().compare("rapid-mix:ml-model") == 0 &&
jm["target"]["name"].asString().compare("xmm") == 0 &&
jm["payload"].size() > 0) {
model.fromJson(jm["payload"]);
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> rapidXmmGmm::run(const std::vector<double>& inputVector) {
xmmTool::preProcess(inputVector);
return model.results.smoothed_normalized_likelihoods;
}
//============================== xmmGmrTool ==================================//
std::vector<double> rapidXmmGmr::run(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> rapidXmmHmm::run(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> rapidXmmHmr::run(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 xmmTool<xmm::GMM, xmm::GMM>;
template class xmmTool<xmm::HMM, xmm::HierarchicalHMM>;
template class xmmStaticTool<xmm::GMM, xmm::GMM>;
template class xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM>;
//I wonder why this can't be defined in machineLearning.cpp? -MZ
// It is needed by the template instantiations below.
// You get an undefined symbols error otherwise.
template <class MachineLearningModule>
bool rapidmix::machineLearning<MachineLearningModule>::train(const trainingData &newTrainingData) {
return MachineLearningModule::train(newTrainingData);
}
template class rapidmix::machineLearning<rapidXmmGmm>;
template class rapidmix::machineLearning<rapidXmmGmr>;
template class rapidmix::machineLearning<rapidXmmHmm>;
template class rapidmix::machineLearning<rapidXmmHmr>;
/**
* @file rapidXMM.h
* @author joseph larralde
*
* @copyright
* Copyright (C) 2016 - 2017 by IRCAM - Centre Pompidou, Paris, France.
* All rights reserved.
*
* @ingroup machinelearning
*/
#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 ================//
/**
* @brief Static classification using Gaussian Mixture Models
*/
class rapidXmmGmm : public xmmStaticTool<xmm::GMM, xmm::GMM> {
public:
rapidXmmGmm(xmmToolConfig cfg = xmmToolConfig()) :
xmmStaticTool<xmm::GMM, xmm::GMM>(cfg, false) {}
~rapidXmmGmm() {}
std::vector<double> run(const std::vector<double>& inputVector);
};
/**
* @brief Static regression using Gaussian Mixture Models
*/
class rapidXmmGmr : public xmmStaticTool<xmm::GMM, xmm::GMM> {
public:
rapidXmmGmr(xmmToolConfig cfg = xmmToolConfig()) :
xmmStaticTool<xmm::GMM, xmm::GMM>(cfg, true) {}
~rapidXmmGmr() {}
std::vector<double> run(const std::vector<double>& inputVector);
};
/**
* @brief Temporal classification using Hierarchical Hidden Markov Models
*/
class rapidXmmHmm : public xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM> {
public:
rapidXmmHmm(xmmToolConfig cfg = xmmToolConfig()) :
xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM>(cfg, false) {}
~rapidXmmHmm() {}
std::vector<double> run(const std::vector<double>& inputVector);
};
/**
* @brief Temporal regression using Hierarchical Hidden Markov Models
*/
class rapidXmmHmr : public xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM> {
public:
rapidXmmHmr(xmmToolConfig cfg = xmmToolConfig()) :
xmmTemporalTool<xmm::HMM, xmm::HierarchicalHMM>(cfg, true) {}
~rapidXmmHmr() {}
std::vector<double> run(const std::vector<double>& inputVector);
};
#endif /* _RAPID_XMM_TOOLS_H_ */
/**
* @file trainingData.cpp
* @author Michael Zbyszynski
* @date 2 Feb 2017
* @copyright Copyright © 2017 Goldsmiths. All rights reserved.
*/
#include "trainingData.h"
namespace rapidmix {
trainingData::trainingData () {
currentId = 0;
targetPhrase = 0;
};
uint32_t trainingData::assignCurrentId() {
uint32_t returnVal = currentId;
++currentId;
return returnVal;
}
std::vector<trainingData::phrase>::iterator trainingData::createNewPhrase(std::string label) {
phrase tempPhrase = { assignCurrentId(), label };
trainingSet.push_back(tempPhrase);
std::vector<trainingData::phrase>::iterator it = --trainingSet.end();
return it;
};
uint32_t trainingData::startRecording() {
phrase tempPhrase = { assignCurrentId(), std::to_string(tempPhrase.uniqueId) }; //TODO: Is this label helpful? -MZ
trainingSet.push_back(tempPhrase);
targetPhrase = int(trainingSet.size() - 1);
return tempPhrase.uniqueId;
};
uint32_t trainingData::startRecording(const std::string &label) {
phrase tempPhrase = { assignCurrentId(), label };
trainingSet.push_back(tempPhrase);
targetPhrase = int(trainingSet.size() - 1);
return tempPhrase.uniqueId;
};
uint32_t trainingData::addElement(const std::vector<double> &input, const std::vector<double> &output) {
element newElement;
newElement.uniqueId = assignCurrentId();
newElement.input = input;
newElement.output = output;
newElement.timeStamp = 0.0;
trainingSet[targetPhrase].elements.push_back(newElement);
return newElement.uniqueId;
}
uint32_t trainingData::addElement(const std::vector<double> &input) {
element newElement;
newElement.uniqueId = assignCurrentId();
newElement.input = input;
newElement.timeStamp = 0.0;
trainingSet[targetPhrase].elements.push_back(newElement);
return newElement.uniqueId;
}
void trainingData::stopRecording() {
//TODO: This doesn't do much. -MZ
}
uint32_t trainingData::recordSingleElement(const std::string &label, const std::vector<double> &input) {
startRecording(label);
int returnId = addElement(input);
stopRecording();
return returnId;
};
uint32_t trainingData::recordSingleElement(const std::vector<double> &input, const std::vector<double> &output) {
startRecording();
int returnId = addElement(input, output);
stopRecording();
return returnId;
};
uint32_t trainingData::recordSingleElement(const std::string &label, const std::vector<double> &input, const std::vector<double> &output) {
startRecording(label);
int returnId = addElement(input, output);
stopRecording();
return returnId;
};
std::vector<std::string> trainingData::getColumnNames() {
return trainingSet[targetPhrase].columnNames;
}
void trainingData::setColumnNames(const std::vector<std::string> &column_names) {
trainingSet[targetPhrase].columnNames = column_names;
}
Json::Value trainingData::parse2json() {
Json::Value root;
Json::Value metadata;
Json::Value trainingSetJSON;
metadata["creator"] = "RAPID-MIX API C++";
metadata["version"] = RAPIDMIX_VERSION;
//Go through all the phrases
for (int i = 0; i < trainingSet.size(); ++i) {
Json::Value thisPhrase;
thisPhrase.append(trainingSet[i].uniqueId);
thisPhrase.append(trainingSet[i].label);
Json::Value column_names;
for (int j = 0; j < trainingSet[i].columnNames.size(); ++j) {
column_names.append(trainingSet[i].columnNames[j]);
}
thisPhrase.append(column_names);
Json::Value elements;
for (int j = 0; j < trainingSet[i].elements.size(); ++j) {
Json::Value singleElement;
Json::Value elementInput;
for (int k = 0; k < trainingSet[i].elements[j].input.size(); ++k) {
elementInput.append(trainingSet[i].elements[j].input[k]);
}
singleElement.append(elementInput);
Json::Value elementOutput;
for (int k = 0; k < trainingSet[i].elements[j].output.size(); ++k) {
elementOutput.append(trainingSet[i].elements[j].output[k]);
}
singleElement.append(elementOutput);
singleElement.append(trainingSet[i].elements[j].timeStamp);
elements.append(singleElement);
}
thisPhrase.append(elements);
trainingSetJSON.append(thisPhrase);
}
root["metadata"] = metadata;
root["trainingSet"] = trainingSetJSON;
return root;
}
std::string trainingData::getJSON() {
Json::Value root = parse2json();
return root.toStyledString();
}
void trainingData::writeJSON(const std::string &filepath) {
Json::Value root = parse2json();
std::ofstream jsonOut;
jsonOut.open (filepath);
Json::StyledStreamWriter writer;
writer.write(jsonOut, root);
jsonOut.close();
}
void trainingData::json2trainingSet(const Json::Value &root) {
trainingSet = {};
for (const Json::Value& jsonPhrase : root["trainingSet"]) {
phrase tempPhrase = { jsonPhrase[0].asUInt(), jsonPhrase[1].asString() };
for (int i = 0; i < jsonPhrase[2].size(); ++i) {
tempPhrase.columnNames.push_back(jsonPhrase[2][i].asString());
}
for (int i = 0; i < jsonPhrase[3].size(); ++i) {
element tempElement;
for (int j = 0; j < jsonPhrase[3][i][0].size(); ++j) {
tempElement.input.push_back(jsonPhrase[3][i][0][j].asDouble());
}
for (int j = 0; j < jsonPhrase[3][i][1].size(); ++j) {
tempElement.output.push_back(jsonPhrase[3][i][1][j].asDouble());
}
tempElement.timeStamp = jsonPhrase[3][i][2].asDouble();
tempPhrase.elements.push_back(tempElement);
}
trainingSet.push_back(tempPhrase);
}
}
bool trainingData::putJSON(const std::string &jsonMessage) {
Json::Value parsedFromString;
Json::Reader reader;
bool parsingSuccessful = reader.parse(jsonMessage, parsedFromString);
if (parsingSuccessful)
{
json2trainingSet(parsedFromString);
}
return parsingSuccessful;
}
bool trainingData::readJSON(const std::string &filepath) {
Json::Value root;
std::ifstream file(filepath);
file >> root;
json2trainingSet(root);
return true; //TODO: check something first
}
}
/**
* @file trainingData.h
* @author Michael Zbyszynski
* @date 2 Feb 2017
* @copyright
* Copyright © 2017 Goldsmiths. All rights reserved.
*
* @ingroup machinelearning
*/
#ifndef trainingData_h
#define trainingData_h
#include <vector>
#include <string>
#include <unordered_map>
#include "../rapidmix.h"
#include "json.h"
namespace rapidmix {
/** This is used by both NN and KNN models for training */
class trainingData {
public:
trainingData();
struct element{
uint32_t uniqueId; //MZ: Does this scope of this id need to extend beyond this instantiation?
std::vector<double> input;
std::vector<double> output;
double timeStamp;
};
struct phrase {
uint32_t uniqueId;
std::string label; //TODO: Need to work this with templates
std::vector<std::string> columnNames; //equal to the number of inputs
std::vector<element> elements;
//TODO: This is just a design idea right now.
void addElement (const std::vector<double> &input, const std::vector<double> &output)
{
element tempElement;
// tempElement.uniqueId = assignCurrentId(); //TODO: how to do this? Do we need this?
tempElement.input = input;
tempElement.output = output;
//tempElement.timeStamp = NULL;
this->elements.push_back(tempElement);
}
};
std::vector<phrase> trainingSet;
//TODO: Deleting phrases (last or by label)
//Design ideas to make phrase building stateless:
std::vector<phrase>::iterator createNewPhrase(); //??? Do we need this?
std::vector<phrase>::iterator createNewPhrase(std::string label);
/** Create a new phrase that can be recorded into. Returns phrase id */
uint32_t startRecording(); //FIXME: this should go away. -MZ
/** Create new phrase, with a label, that can be recorded into. Returns phrase id */
uint32_t startRecording(const std::string &label);
/** Add an element with input and output to the phrase that is recording,
or to the default phrase if recording is stopped. Returns phrase id. */
uint32_t addElement(const std::vector<double> &input, const std::vector<double> &output);
/** Add an element with just input to the phrase that is recording,
or to the default phrase if recording is stopped. Returns phrase id. */
uint32_t addElement(const std::vector<double> &input);
void stopRecording();
/** Create a phrase with a single element that has a label and input. Returns phrase id. */
uint32_t recordSingleElement(const std::string &label, const std::vector<double> &input);
/** Create a phrase with a single element that has input, and output. Returns phrase id. */
uint32_t recordSingleElement(const std::vector<double> &input, const std::vector<double> &output);
/** Create a phrase with a single element that has a label, input, and output. Returns phrase id. */
uint32_t recordSingleElement(const std::string &label, const std::vector<double> &input, const std::vector<double> &output);
std::vector<std::string> getColumnNames();
void setColumnNames(const std::vector<std::string> &columnNames);
/** Get a JSON representation of the data set in the form of a styled string */
std::string getJSON();
/** Write a JSON version of the training set to specified file path */
void writeJSON(const std::string &filepath);
/** populate a data set with string. See getJSON() */
bool putJSON(const std::string &jsonMessage);
/** read a JSON file at file path and build a training set from it */
bool readJSON(const std::string &filepath);
private:
int targetPhrase;
uint32_t currentId;
//* Returns and increments current id */
uint32_t assignCurrentId();
Json::Value parse2json();
void json2trainingSet(const Json::Value &newTrainingData);
};
}
#endif
/*
* rapidmix.h
* Created by Michael Zbyszynski on 12 Jan 2017
/**
* @file rapidmix.h
* @author Michael Zbyszynski
* @date 12 Jan 2017
*
* @copyright
* Copyright © 2017 Goldsmiths. All rights reserved.
*/
/**
* @mainpage RAPID-MIX API
*
* @section Introduction
*
* Hello, API ! <br>
* All the classes, functions and structs documented here belong to the
* <code>rapidmix</code> namespace.
*
* @section Examples
* @subsection Simple classification
* TODO
*/
/////////////////////////////////////////////////////////////////////////////
// ______ _ _ ___ ____ ___ ______ _____ //
// | ___ \ (_) | | | \/ (_) / _ \ | ___ \_ _| //
......@@ -19,16 +36,14 @@
#define rapidmix_h
#define RAPIDMIX_VERSION_MAJOR 2
#define RAPIDMIX_VERSION_MINOR 0
#define RAPIDMIX_VERSION_MINOR 3
#define RAPIDMIX_VERSION_PATCH 0
#define RAPIDMIX_VERSION "2.0.0"
#define RAPIDMIX_REVISION "31-May-2017"
#define RAPIDMIX_BEGIN_NAMESPACE namespace rapidmix {
#define RAPIDMIX_END_NAMESPACE }
#define RAPIDMIX_VERSION "2.3.1"
#define RAPIDMIX_JSON_DOC_VERSION "1.0.0"
#define RAPIDMIX_REVISION "2-MAY-2018"
#include "machineLearning.h"
#include "trainingData.h"
#include "signalProcessing.h"
#include "machineLearning/machineLearning.h"
#include "machineLearning/trainingData.h"
#include "signalProcessing/signalProcessing.h"
#endif
#include "rapidmix.h"
#include "rapidPiPoHost.h"
#include <iostream>
#include <fstream>
//=========================== H O S T === U T I L S ==========================//
static void fromPiPoStreamAttributes(PiPoStreamAttributes &src,
RapidPiPoStreamAttributes &dst)
{
unsigned int numCols = src.dims[0];
unsigned int numLabels = src.numLabels;
if (numLabels > PIPO_MAX_LABELS) {
numLabels = PIPO_MAX_LABELS;
}
if (numLabels > numCols) {
numLabels = numCols;
}
dst.hasTimeTags = src.hasTimeTags;
if (src.rate <= MIN_PIPO_SAMPLERATE) {
dst.rate = MIN_PIPO_SAMPLERATE;
} else if (src.rate >= MAX_PIPO_SAMPLERATE) {
dst.rate = MAX_PIPO_SAMPLERATE;
} else {
dst.rate = src.rate;
}
dst.rate = src.rate;
dst.offset = src.offset;
dst.width = src.dims[0];
dst.height = src.dims[1];
dst.labels = std::vector<std::string>();
for (unsigned int i = 0; i < numLabels; ++i)
{
dst.labels.push_back(std::string(src.labels[i]));
}
dst.hasVarSize = src.hasVarSize;
dst.domain = src.domain;
dst.maxFrames = src.maxFrames;
}
static void toPiPoStreamAttributes(RapidPiPoStreamAttributes &src,
PiPoStreamAttributes &dst)
{
const char *labs[src.labels.size()];
for (unsigned int i = 0; i < src.labels.size(); ++i)
{
labs[i] = src.labels[i].c_str();
}
dst = PiPoStreamAttributes(
src.hasTimeTags,
src.rate,
src.offset,
src.width,
src.height,
&labs[0],
src.hasVarSize,
src.domain,
src.maxFrames
);
}
//========================= H O S T === M E T H O D S ========================//
int
RapidPiPoHost::setInputStreamAttributes(RapidPiPoStreamAttributes &rpsa)
{
PiPoStreamAttributes psa;
toPiPoStreamAttributes(rpsa, psa);
return PiPoHost::setInputStreamAttributes(psa);
}
void
RapidPiPoHost::onNewFrame(double time, double weight, PiPoValue *values, unsigned int size)
{
std::cout << "I received " << size << " new frame(s)" << std::endl;
}
//----------------------------- JSON FORMATTING ------------------------------//
std::string
RapidPiPoHost::getJSON()
{
Json::Value result = toJSON();
return result.toStyledString();
}
void
RapidPiPoHost::writeJSON(const std::string &filepath)
{
Json::Value root = toJSON();
std::ofstream jsonOut;
jsonOut.open (filepath);
Json::StyledStreamWriter writer;
writer.write(jsonOut, root);
jsonOut.close();
}
bool
RapidPiPoHost::putJSON(const std::string &jsonMessage)
{
Json::Value parsedFromString;
Json::Reader reader;
bool parsingSuccessful = reader.parse(jsonMessage, parsedFromString);
return (parsingSuccessful && fromJSON(parsedFromString));
}
bool
RapidPiPoHost::readJSON(const std::string &filepath)
{
Json::Value root;
std::ifstream file(filepath);
file >> root;
return fromJSON(root);
}
//-------------------------- PRIVATE HOST METHODS ----------------------------//
Json::Value
RapidPiPoHost::toJSON()
{
Json::Value root;
//*
root["docType"] = "rapid-mix:signal-processing";
root["docVersion"] = RAPIDMIX_JSON_DOC_VERSION;
Json::Value target;
target["name"] = "pipo";
target["version"] = PIPO_SDK_VERSION;
root["target"] = target;
Json::Value pipodata;
pipodata["description"] = this->graphName;
Json::Value inputStream;
inputStream["hasTimeTags"] = inputStreamAttrs.hasTimeTags;
inputStream["rate"] = inputStreamAttrs.rate;
inputStream["offset"] = inputStreamAttrs.offset;
inputStream["width"] = inputStreamAttrs.dims[0];
inputStream["height"] = inputStreamAttrs.dims[1];
inputStream["labels"] = inputStreamAttrs.labels;
inputStream["hasVarSize"] = inputStreamAttrs.hasVarSize;
inputStream["domain"] = inputStreamAttrs.domain;
inputStream["maxFrames"] = inputStreamAttrs.maxFrames;
Json::Value outputStream;
outputStream["hasTimeTags"] = outputStreamAttrs.hasTimeTags;
outputStream["rate"] = outputStreamAttrs.rate;
outputStream["offset"] = outputStreamAttrs.offset;
outputStream["width"] = outputStreamAttrs.dims[0];
outputStream["height"] = outputStreamAttrs.dims[1];
outputStream["labels"] = outputStreamAttrs.labels;
outputStream["hasVarSize"] = outputStreamAttrs.hasVarSize;
outputStream["domain"] = outputStreamAttrs.domain;
outputStream["maxFrames"] = outputStreamAttrs.maxFrames;
Json::Value streams;
streams["input"] = inputStream;
streams["output"] = outputStream;
pipodata["streamAttributes"] = streams;
Json::Value params;
int n = this->graph->getNumAttrs();
params.resize(static_cast<Json::ArrayIndex>(n));
for (unsigned int i = 0; i < n; ++i)
{
Json::Value param;
PiPo::Attr *a = this->graph->getAttr(i);
param["name"] = a->getName();
switch (a->getType())
{
case PiPo::Bool:
param["type"] = "Bool";
param["value"] = a->getInt(0);
break;
case PiPo::Enum:
param["type"] = "Enum";
param["value"] = std::string(a->getStr(0));
break;
case PiPo::String:
param["type"] = "String";
param["value"] = std::string(a->getStr(0));
break;
case PiPo::Int:
param["type"] = "Int";
param["value"] = Json::Value(Json::arrayValue);
for (int i = 0; i < a->getSize(); ++i)
{
param["value"].append(a->getInt(i));
}
break;
case PiPo::Double:
param["type"] = "Double";
param["value"] = Json::Value(Json::arrayValue);
for (int i = 0; i < a->getSize(); ++i)
{
param["value"].append(a->getDbl(i));
}
break;
default:
std::cout << a->getType() << std::endl;
break;
}
params[i] = param;
}
pipodata["parameters"] = params;
root["payload"] = pipodata;
//*/
return root;
}
bool
RapidPiPoHost::fromJSON(Json::Value &jv)
{
//*
if (jv["docType"].asString().compare("rapid-mix:signal-processing") == 0 &&
//jv["docVersion"].asString().compare(RAPIDMIX_JSON_DOC_VERSION) == 0 &&
jv["target"]["name"].asString().compare("pipo") == 0 &&
jv["payload"].size() > 0) {
this->setGraph(jv["payload"]["description"].asString());
Json::Value params = jv["payload"]["parameters"];
unsigned int nAttrs = params.size();
for (unsigned int i = 0; i < nAttrs; ++i)
{
std::string type = params[i]["type"].asString();
const char *name = params[i]["name"].asString().c_str();
if (type.compare("Bool") == 0)
{
this->setAttr(name, params[i]["value"].asInt());
}
else if (type.compare("Enum") == 0 || type.compare("String") == 0)
{
this->setAttr(name, params[i]["value"].asString());
}
else if (type.compare("Int") == 0)
{
std::vector<int> values;
for (int j = 0; j < params[i]["value"].size(); ++j)
{
values.push_back(params[i]["value"][j].asInt());
}
this->setAttr(name, values);
}
else if (type.compare("Double") == 0)
{
std::vector<double> values;
for (int j = 0; j < params[i]["value"].size(); ++j)
{
values.push_back(params[i]["value"][j].asDouble());
}
this->setAttr(name, values);
}
}
Json::Value inputStream = jv["pipodata"]["streamAttributes"]["input"];
// setInputStreamAttributes(
// inputStream["hasTimeTags"].getDbl()//,
// //...
// );
return true;
}
//*/
return false;
}
#ifndef _RAPID_PIPO_HOST_H_
#define _RAPID_PIPO_HOST_H_
#include "PiPo.h"
#include "PiPoHost.h"
//#ifdef EXTERNAL_JSONCPP_PATH
#define EXTERNAL_JSONCPP_PATH "json.h"
#include EXTERNAL_JSONCPP_PATH
//#endif /* EXTERNAL_JSONCPP_PATH */
#define MIN_PIPO_SAMPLERATE (1.0 / 31536000000.0) /* once a year */
#define MAX_PIPO_SAMPLERATE (96000000000.0)
#define DEFAULT_PIPO_SAMPLERATE 1000.0
struct RapidPiPoStreamAttributes {
RapidPiPoStreamAttributes() : // default parameters suited for audio
hasTimeTags(false),
rate(DEFAULT_PIPO_SAMPLERATE),
offset(0),
width(1),
height(1),
labels({ "" }),
hasVarSize(false),
domain(0),
maxFrames(1) {}
bool hasTimeTags;
double rate;
double offset;
unsigned int width;
unsigned int height;
std::vector<std::string> labels;
bool hasVarSize;
double domain;
unsigned int maxFrames;
};
//================================ H O S T ===================================//
class RapidPiPoHost : public PiPoHost {
public:
virtual int setInputStreamAttributes(RapidPiPoStreamAttributes &a);
virtual void onNewFrame(double time, double weight, PiPoValue *values, unsigned int size);
/** 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);
private:
Json::Value toJSON();
bool fromJSON(Json::Value &jv);
};
#endif /* _RAPID_PIPO_HOST_H_ */
#ifndef _RAPID_PIPO_TOOLS_H_
#define _RAPID_PIPO_TOOLS_H_
#include "rapidPiPoHost.h"
typedef RapidPiPoStreamAttributes signalAttributes;
typedef RapidPiPoHost signalProcessingHost;
#endif /* _RAPID_PIPO_TOOLS_H_ */
/**
* @file signalProcessing.h
* @author Michael Zbyszynski
* @date 6 Feb 2017
* @copyright
* Copyright © 2017 Goldsmiths. All rights reserved.
*
* @ingroup signalprocessing
*/
#ifndef signalProcessing_h
#define signalProcessing_h
#include "rapidmix.h"
#include "maximilian.h"
#include "maxim.h"
#include "rapidStream.h"
#include "./rapidPiPoTools/rapidPiPoTools.h"
namespace rapidmix {
/*
* Wrapper for signalProcessing modules, currently a collection of typedefs
*/
typedef maxiFFT FFT;
typedef maxiMFCC MFCC;
typedef rapidStream<double> rapidStream;
typedef signalProcessingHost signalProcessingHost;
typedef signalAttributes signalAttributes;
}
#endif
File added
{
"docType" : "rapid-mix:signal-processing",
"docVersion" : "1.0.0",
"payload" :
{
"description" : "slice:fft:sum:scale:onseg",
"parameters" :
[
{
"name" : "slice.size",
"type" : "Int",
"value" : [ 1024 ]
},
{
"name" : "slice.hop",
"type" : "Int",
"value" : [ 256 ]
},
{
"name" : "slice.wind",
"type" : "Enum",
"value" : "hann"
},
{
"name" : "slice.norm",
"type" : "Enum",
"value" : "power"
},
{
"name" : "fft.size",
"type" : "Int",
"value" : [ 0 ]
},
{
"name" : "fft.mode",
"type" : "Enum",
"value" : "power"
},
{
"name" : "fft.norm",
"type" : "Bool",
"value" : 1
},
{
"name" : "fft.weighting",
"type" : "Enum",
"value" : "itur468"
},
{
"name" : "sum.norm",
"type" : "Bool",
"value" : 0
},
{
"name" : "sum.colname",
"type" : "String",
"value" : ""
},
{
"name" : "scale.inmin",
"type" : "Double",
"value" : [ 1 ]
},
{
"name" : "scale.inmax",
"type" : "Double",
"value" : [ 10 ]
},
{
"name" : "scale.outmin",
"type" : "Double",
"value" : [ 0 ]
},
{
"name" : "scale.outmax",
"type" : "Double",
"value" : [ 10 ]
},
{
"name" : "scale.clip",
"type" : "Bool",
"value" : 0
},
{
"name" : "scale.func",
"type" : "Enum",
"value" : "log"
},
{
"name" : "scale.base",
"type" : "Double",
"value" : [ 10 ]
},
{
"name" : "scale.minlog",
"type" : "Double",
"value" : [ 1.0000000195414814e-24 ]
},
{
"name" : "scale.complete",
"type" : "Enum",
"value" : "repeatlast"
},
{
"name" : "scale.colindex",
"type" : "Int",
"value" : [ 0 ]
},
{
"name" : "scale.numcols",
"type" : "Int",
"value" : [ 0 ]
},
{
"name" : "onseg.colindex",
"type" : "Int",
"value" : [ 0 ]
},
{
"name" : "onseg.numcols",
"type" : "Int",
"value" : [ -1 ]
},
{
"name" : "onseg.filtersize",
"type" : "Int",
"value" : [ 3 ]
},
{
"name" : "onseg.threshold",
"type" : "Double",
"value" : [ 9 ]
},
{
"name" : "onseg.odfmode",
"type" : "Enum",
"value" : "mean"
},
{
"name" : "onseg.mininter",
"type" : "Double",
"value" : [ 50 ]
},
{
"name" : "onseg.startisonset",
"type" : "Bool",
"value" : 1
},
{
"name" : "onseg.duration",
"type" : "Bool",
"value" : 1
},
{
"name" : "onseg.durthresh",
"type" : "Double",
"value" : [ 0 ]
},
{
"name" : "onseg.offthresh",
"type" : "Double",
"value" : [ -120 ]
},
{
"name" : "onseg.maxsize",
"type" : "Double",
"value" : [ 0 ]
},
{
"name" : "onseg.min",
"type" : "Bool",
"value" : 0
},
{
"name" : "onseg.max",
"type" : "Bool",
"value" : 1
},
{
"name" : "onseg.mean",
"type" : "Bool",
"value" : 0
},
{
"name" : "onseg.stddev",
"type" : "Bool",
"value" : 0
},
{
"name" : "onseg.odfoutput",
"type" : "Bool",
"value" : 0
}
],
"streamAttributes" :
{
"input" :
{
"domain" : 0,
"hasTimeTags" : 0,
"hasVarSize" : false,
"height" : 1,
"labels" : true,
"maxFrames" : 1,
"offset" : 0,
"rate" : 1000,
"width" : 1
},
"output" :
{
"domain" : 0,
"hasTimeTags" : 1,
"hasVarSize" : false,
"height" : 1,
"labels" : true,
"maxFrames" : 1,
"offset" : 0,
"rate" : 3.90625,
"width" : 2
}
}
},
"target" :
{
"name" : "pipo",
"version" : 0.20000000000000001
}
}
#include <cassert>
#include <vector>
#include <iostream>
#include "rapidmix.h"
int main() {
///////////////////////////////////Test GVF
rapidmix::gvfTemporalVariation gvf;
rapidmix::trainingData myGVFData;
// Record first gesture
myGVFData.startRecording();
myGVFData.addElement({ 4.0, 0.7 });
myGVFData.addElement({ 3.0, 0.8 });
myGVFData.addElement({ 2.0, 0.9 });
myGVFData.addElement({ 1.0, 1.0 });
myGVFData.addElement({ 0.4, 1.2 });
myGVFData.addElement({ 0.2, 1.4 });
myGVFData.stopRecording();
// Record reverse of first gesture
myGVFData.startRecording();
myGVFData.addElement({ 0.2, 1.4 });
myGVFData.addElement({ 0.4, 1.2 });
myGVFData.addElement({ 1.0, 1.0 });
myGVFData.addElement({ 2.0, 0.9 });
myGVFData.addElement({ 3.0, 0.8 });
myGVFData.addElement({ 4.0, 0.7 });
myGVFData.stopRecording();
// Train
std::cout << "gvf train = " << gvf.train(myGVFData) << std::endl;
std::cout << "gvf passed." << std::endl;
//////////////////////////////////////Test RapidLib
rapidmix::staticRegression myNN;
rapidmix::trainingData myRLData;
std::vector<double> input = { 0.2, 0.7 };
std::vector<double> output = { 3.0 };
myRLData.recordSingleElement("label", input, output);
input = { 2.0, 44.2 };
output = { 20.14 };
myRLData.recordSingleElement("label", input, output);
std::cout << "staticRegression train = " << myNN.train(myRLData) << std::endl;
std::vector<double> inputVec = { 1.618, 18.9 };
assert(myNN.run(inputVec)[0] == 12.596715279688549);
std::cout << "staticRegression passed." << std::endl;
//////////////////////////////////////Test XMM
rapidmix::xmmConfig xcfg;
xcfg.relativeRegularization = 0.1;
rapidmix::trainingData myXmmData;
std::vector<double> myXmmInput;
std::vector<double> myXmmOutput;
myXmmData.startRecording("lab1");
myXmmInput = { 0.2, 0.7 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.startRecording("lab2");
myXmmInput = { 0.8, 0.1 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.writeJSON("/var/tmp/testTrainingData.json");
rapidmix::xmmStaticClassification myGmm(xcfg);
myGmm.train(myXmmData);
std::string filepath = "/var/tmp/modelSetDescription";
myGmm.writeJSON(filepath);
myXmmInput = { 0.2, 0.7 };
rapidmix::xmmStaticClassification myGmmFromFile;
myGmmFromFile.readJSON(filepath);
assert(myGmm.run(myXmmInput)[0] == myGmmFromFile.run(myXmmInput)[0]);
std::cout << "xmm passed." << std::endl;
/////////////////////////////////////Test pipo signal processing
rapidmix::signalProcessingHost spHost;
spHost.setGraph("slice<fft:moments,moments>");
spHost.setAttr("slice.size", 128);
spHost.setAttr("slice.hop", 64);
rapidmix::signalAttributes sa;
spHost.setInputStreamAttributes(sa);
for (unsigned int i = 0; i < 256; ++i)
{
std::vector<float> f({ 1, 2, 3 });
spHost.frames(0, 1, &f[0], 1, 1);
}
std::cout << "pipo passed." << std::endl;
/////////////////////////////////////Test rapidStream signal processing
rapidmix::rapidStream myProcessor(5); //create a processor with a window size of 5
myProcessor.pushToWindow(0.246);
myProcessor.pushToWindow(0.44);
myProcessor.pushToWindow(-0.228);
myProcessor.pushToWindow(0.402);
myProcessor.pushToWindow(-0.382);
assert(myProcessor.maximum() == 0.44);
assert(myProcessor.minimum() == -0.382);
assert(myProcessor.sum() == 0.478);
assert(myProcessor.mean() == 0.09559999999999999);
assert(myProcessor.standardDeviation() == 0.33702557766436664);
assert(myProcessor.rms() == 0.3503221374677884);
std::cout << "rapidStream passed." << std::endl;
/////////////////////////////////////Test Maxi signal processing
rapidmix::FFT myFFT;
myFFT.setup(1024, 512, 265);
for (int i = 0; i < 1024; ++i) {
float input = (i % 256/256.0);
myFFT.process(input);
}
assert(myFFT.spectralCentroid() == 3520.84277f);
std::cout << "maxi fft passed." << std::endl;
return 0;
}
//
// test_RapidLib.cpp
// Unit tests for RapidLib
//
// Created by Francisco Bernardo on 17/02/2017.
// Copyright © 2017 Goldsmiths. All rights reserved.
//
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "rapidMix.h"
#include <iostream>
SCENARIO("Test NN Regression", "[machineLearning]")
{
GIVEN("NN Regression object and training dataset")
{
rapidmix::staticRegression myNN;
rapidmix::trainingData myData;
std::vector<double> input = { 0.2, 0.7 };
std::vector<double> output = { 3.0 };
REQUIRE(myData.recordSingleElement("label", input, output) == 1); //FIXME: Label is stupd here. -MZ
//testing alternate API
//auto newPhrase = myData.createNewPhrase("lab1");
//newPhrase->addElement(input, output);
input = { 2.0, 44.2 };
output = { 20.14 };
REQUIRE(myData.recordSingleElement("label", input, output) == 3);
myData.writeJSON("/var/tmp/testTrainingData.json");
REQUIRE(myNN.train(myData) == true);
std::string filepath = "/var/tmp/modelSetDescription.json";
myNN.writeJSON(filepath);
std::vector<double> inputVec = { 1.618, 18.9 }; //arbitrary input
REQUIRE(myNN.run(inputVec)[0] == 12.596715279688549);
WHEN("when NN model is deserialized from file")
{
rapidmix::staticRegression myNNfromFile;
REQUIRE(myNNfromFile.readJSON(filepath) == true);
THEN("run models and in-memory model")
{
REQUIRE(myNN.run(inputVec)[0] == myNNfromFile.run(inputVec)[0]);
}
}
WHEN("when NN model is deserialized from JSON stream")
{
rapidmix::staticRegression myNNfromString;
REQUIRE(myNNfromString.putJSON(myNN.getJSON()) == true);
THEN("run models and in-memory model")
{
REQUIRE(myNN.run(inputVec)[0] == myNNfromString.run(inputVec)[0]);
}
}
}
}
SCENARIO("Test kNN classification", "[machineLearning]")
{
GIVEN("kNN object and training dataset")
{
rapidmix::staticClassification myKnn;
rapidmix::trainingData myData;
REQUIRE(myData.recordSingleElement("cat", { 0.2, 0.7 }) == 1);
REQUIRE(myData.recordSingleElement("dog", { 2.0, 44.2 }) == 3); // This is not 2, because phrases get numbers, too.
REQUIRE(myKnn.train(myData) == true);
std::string filepath2 = "/var/tmp/modelSetDescription_knn.json";
myKnn.writeJSON(filepath2);
rapidmix::staticClassification myKnnFromString;
myKnnFromString.putJSON(myKnn.getJSON());
rapidmix::staticClassification myKnnFromFile;
myKnnFromFile.readJSON(filepath2);
std::vector<double> inputVec = { 2.0, 44.2 };
REQUIRE(myKnn.run(inputVec, "label") == "dog");
WHEN("when kNN model is read from file")
{
THEN("run models and compare")
{
REQUIRE(myKnn.run(inputVec)[0] == myKnnFromFile.run(inputVec)[0]);
}
}
WHEN("when kNN model is read from JSON stream")
{
THEN("run models and compare")
{
REQUIRE(myKnn.run(inputVec)[0] == myKnnFromString.run(inputVec)[0]);
}
}
}
}
SCENARIO("Test DTW classification", "[machineLearning]")
{
GIVEN("DTW object and training dataset")
{
rapidmix::dtwTemporalClassification myDTW;
rapidmix::trainingData myData;
myData.startRecording("setOne");
std::vector<double> input = { 0.1, 0.5 };
std::vector<double> output = {};
REQUIRE(myData.addElement(input, output) == 1);
input = { 0.2, 0.4 };
REQUIRE(myData.addElement(input, output) == 2);
input = { 0.3, 0.3 };
REQUIRE(myData.addElement(input, output) == 3);
input = { 0.4, 0.2 };
REQUIRE(myData.addElement(input, output) == 4);
input = { 0.5, 0.1 };
REQUIRE(myData.addElement(input, output) == 5);
myData.stopRecording();
//alternate API
auto setTwo = myData.createNewPhrase("setTwo");
input = { 1., 4. };
setTwo->addElement(input, output);
input = { 2., -3. };
setTwo->addElement(input, output);
input = { 1., 5. };
setTwo->addElement(input, output);
input = { -2., 1. };
setTwo->addElement(input, output);
REQUIRE(myDTW.train(myData) == true);
std::vector<std::vector<double> > inputSet1;
inputSet1.push_back( {1., 4.});
inputSet1.push_back( {2., -3.});
inputSet1.push_back( {1., 5.});
inputSet1.push_back( {-2., 1.});
REQUIRE(myDTW.run(inputSet1) == "setTwo");
std::vector<std::vector<double> > inputSet0;
inputSet0.push_back( { 0.1, 0.5 });
inputSet0.push_back( { 0.2, 0.4 });
inputSet0.push_back( { 0.3, 0.3 });
inputSet0.push_back( { 0.4, 0.2 });
inputSet0.push_back( { 0.5, 0.1 });
REQUIRE(myDTW.run(inputSet0) == "setOne");
}
}
SCENARIO("Test both classes reject bad data", "[machineLearning]") {
rapidmix::staticRegression badNN;
rapidmix::staticClassification badKNN;
rapidmix::trainingData myBadData;
std::vector<double> input = { 0.1, 0.2, 0.3};
std::vector<double> output = { 1.0 };
myBadData.recordSingleElement("label", input,output); //FIXME: This label is uselsess? -MZ
input = { 1.0, 2.0, 3.0, 4.0 };
myBadData.addElement(input, output);
REQUIRE(badNN.train(myBadData) == false);
REQUIRE(badKNN.train(myBadData) == false);
//TODO: These should return false with empty data set. I think it just crashes now. -mz
}
//
// test_GVF.cpp
// Unit tests for GFV
//
// Created by Francisco Bernardo on 17/02/2017.
// Copyright © 2017 Goldsmiths. All rights reserved.
//
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include "rapidMix.h"
TEST_CASE( "Tests default GVF ctor.", "[GVF]" ) {
GVF * gvf = new GVF();
gvf->translate(false);
gvf->segmentation(false);
REQUIRE(gvf != NULL);
}
TEST_CASE( "Tests default GVFGesture ctor.", "[GVF]" ) {
GVF * gvf = new GVF();
GVFGesture gesture;
GVFOutcomes outcomes;
gvf->translate(false);
gvf->segmentation(false);
gvf->setNumberOfParticles(1000);
gvf->setTolerance(0.2f);
gvf->setScalingsVariance(0.00001f); //0.00001f); //0.000002f); //0.00004f);
gvf->setDynamicsVariance(0.0001f);
vector<vector<float>> trainingData;
if(gvf->getState() == GVF::STATE_LEARNING)
{
gvf->startGesture();
for (vector<vector<float>>::iterator frame = trainingData.begin(); frame != trainingData.end(); frame++)
{
// Fill the template
gesture.addObservation(*frame);
}
}
gvf->setState(GVF::STATE_FOLLOWING);
gesture.clear();
trainingData.clear();
vector<vector<float>> testData;
if (gvf->getState()==GVF::STATE_FOLLOWING)
{
for (vector<vector<float>>::iterator frame = testData.begin(); frame != testData.end(); frame++)
{
// Fill the template
gesture.addObservation(*frame);
}
gvf->update(gesture.getLastObservation());
}
// float phase = gvf->getOutcomes().estimations[0].alignment;
// float speed = gvf->getOutcomes().estimations[0].dynamics[0];
//
// getDynamicsVariance();
// getScalingsVariance();
// getRotationsVariance();
REQUIRE( gvf != NULL);
}
SCENARIO("Test GVF Regression", "[machineLearning]")
{
GIVEN("gvf object and training dataset")
{
rapidmix::gvfTemporalVariation gvf;
rapidmix::trainingData myData;
// Record first gesture
myData.startRecording();
std::vector<double> inputs = { 4.0, 0.7 };
myData.addElement(inputs);
inputs = { 3.0, 0.8 };
myData.addElement(inputs);
inputs = { 2.0, 0.9 };
myData.addElement(inputs);
inputs = { 1.0, 1.0 };
myData.addElement(inputs);
inputs = { 0.4, 1.2 };
myData.addElement(inputs);
inputs = { 0.2, 1.4 };
myData.addElement(inputs);
myData.stopRecording();
// Record reverse of first gesture
myData.startRecording();
inputs = { 0.2, 1.4 };
myData.addElement(inputs);
inputs = { 0.4, 1.2 };
myData.addElement(inputs);
inputs = { 1.0, 1.0 };
myData.addElement(inputs);
inputs = { 2.0, 0.9 };
myData.addElement(inputs);
inputs = { 3.0, 0.8 };
myData.addElement(inputs);
inputs = { 4.0, 0.7 };
myData.addElement(inputs);
myData.stopRecording();
// Train
REQUIRE(gvf.train(myData) == true);
// Set first gesture (or a fragment of it)
std::vector<double> outcomes;
WHEN("when gvf is trained with a gesture and the reverse gesture")
{
THEN("follows the the gesture (first) and confirm it is the likeliestGesture and likelihoods reasonable")
{
outcomes = gvf.run(inputs = { 3.0, 0.8 });
outcomes = gvf.run({ 2.0, 0.9 });
outcomes = gvf.run({ 1.0, 1.0 });
outcomes = gvf.run({ 0.4, 1.2 });
outcomes = gvf.run({ 0.2, 1.4 });
// The assumtion for the test is that the outcome of the last segment of the test gesture must converge
REQUIRE(outcomes[0] == 0); // outcomes[0] - likeliestGesture must be equal to first gesture '0'
REQUIRE(gvf.getLikelihoods()[0] > 0.5); // outcomes[1] - likelihood gesture '0' must be greater than 50%
REQUIRE(gvf.getLikelihoods()[1] < 0.5); // outcomes[2] - likelihood gesture '1' must be lesser than 50%
REQUIRE(gvf.getAlignments()[0] < 0.5); // outcomes[3] - alignment gesture '0' must be lesser than 50%
// REQUIRE(outcomes[4] < 0.5); // outcomes[4] - alignment gesture '1' must be lesser than 50%
}
WHEN("when gvf is trained with two gestures")
{
THEN("gvf follows the test gesture (first gesture scaled) and confirm it is the likeliestGesture and likelihoods anc reasonable ")
{
}
}
WHEN("when gvf is trained with two gestures")
{
THEN("gvf follows the test gesture (first gesture scaled) and confirm it is the likeliestGesture and likelihoods anc reasonable ")
{
}
}
}
}
}
//
// test_rapidPiPoTools.cpp
// Unit tests for rapidPiPoTools
//
#ifndef CATCH_CONFIG_MAIN
#define CATCH_CONFIG_MAIN
#endif
#ifdef RTA_USE_VECLIB
#undef RTA_USE_VECLIB
#endif
#include <unistd.h>
#include "catch.hpp"
#include "signalProcessing.h"
#define MAX_PATH_SIZE 256
void doSomething(double time, double weight, PiPoValue *values, unsigned int size)
{
std::cout << time << std::endl;
}
class Stuff {
public:
Stuff() {}
~Stuff() {}
void doSomething(double time, double weight, PiPoValue *values, unsigned int size)
{
std::cout << time << std::endl;
}
};
//=============================== ONSEG TEST =================================//
SCENARIO("Test rapidPiPoHost", "[signalProcessing]")
{
GIVEN("A rapidPiPoHost class with a rapidPiPo chain and an audio file")
{
maxiSample buffer;
Stuff stuff;
// in XCode this gives the path to DerivedData folder
// char pathStrBuf[MAX_PATH_SIZE];
// char *cwd = getcwd(pathStrBuf, sizeof(pathStrBuf));
// std::cout << std::string(cwd) << std::endl;
// but here we just add the file to the Copy File(s) Build Phase
buffer.load("./data/DnB-loop-175BPM.wav", 0);
// ( source : http://freesound.org/people/yewbic/sounds/40107/ )
buffer.reset(); // (no real need to do this here)
//====================================================================//
// instantiate PiPo related classes here :
rapidmix::signalProcessingHost host;
// host.setFrameCallback(doSomething);
// host.setFrameCallback(stuff.doSomething);
// host.setFrameCallback(dynamic_cast<frameCallback>(std::bind(&Stuff::doSomething, stuff)));
// host.setFrameCallback(dynamic_cast<frameCallback>(Stuff::* doSomething));
// if we want to add some custom PiPos to our collection :
// #include "myCustomPiPo.h"
// PiPoCollection::addToCollection("myCustomPiPo", new PiPoCreator<myCustomPiPo>);
// now we can write :
// pipoHost.setChain("myCustomPiPo");
// #include "PiPoMaximChroma.h"
// this one is not part of the default collection :
// PiPoCollection::addToCollection("chroma", new PiPoCreator<PiPoMaximChroma>);
//*
host.setGraph("slice:fft:sum:scale:onseg");
host.setAttr("slice.size", 1024);
host.setAttr("slice.hop", 256);
host.setAttr("slice.norm", "power");
host.setAttr("fft.mode", "power");
host.setAttr("fft.weighting", "itur468");
host.setAttr("scale.inmin", 1.);
host.setAttr("scale.inmax", 10.);
host.setAttr("scale.outmin", 0.);
host.setAttr("scale.outmax", 10.);
host.setAttr("scale.func", "log");
host.setAttr("scale.base", 10.);
host.setAttr("onseg.duration", 1.);
host.setAttr("onseg.min", 0.);
host.setAttr("onseg.max", 1.);
host.setAttr("onseg.mean", 0.);
host.setAttr("onseg.stddev", 0.);
host.setAttr("onseg.startisonset", 1.);
host.setAttr("onseg.threshold", 9.);
host.setAttr("onseg.offthresh", -120.);
std::cout << "onseg threshold : ";
std::cout << host.getDoubleAttr("onseg.threshold") << std::endl;
std::cout << "fft mode : ";
std::cout << host.getEnumAttr("fft.mode") << std::endl;
std::cout << "param names : " << std::endl;
std::vector<std::string> attrs = host.getAttrNames();
for (int i = 0; i < attrs.size(); ++i)
{
std::cout << "- " << attrs[i] << std::endl;
}
//*/
//char pathStrBuf[MAX_PATH_SIZE];
//char *cwd = getcwd(pathStrBuf, sizeof(pathStrBuf));
//std::string filePath = std::string(cwd) + "/hello.json";
//std::string filePath = ${PROJECT_DIR};
std::string filePath = "./data/pipo.json";
std::cout << filePath << std::endl;
//host.writeJSON(filePath);
host.readJSON(filePath);
//host.putJSON(jj);
//std::string jj = host.getJSON();
//std::cout << jj << std::endl;
// set another chain :
// pipoHost.setChain("chroma");
WHEN("file is processed")
{
rapidmix::signalAttributes sa;
sa.hasTimeTags = true;
sa.rate = 44100;
sa.offset = 0;
sa.width = 1;
sa.height = 1;
sa.labels = std::vector<std::string>();
sa.hasVarSize = false;
sa.domain = 0;
sa.maxFrames = 1;
host.setInputSignalAttributes(sa);
float value;
for (unsigned int i = 0; i < buffer.length; ++i) {
value = buffer.play();
host.frames((double)i, 0.0, &value, 1, 1);
}
THEN("compare results of actual processing and same file processed in another envrironment")
{
// compare the results of the processing
REQUIRE(true);
}
}
}
}
#ifndef CATCH_CONFIG_MAIN
#define CATCH_CONFIG_MAIN
#endif
#include "catch.hpp"
#include "machineLearning.h"
//================================== GMM =====================================//
SCENARIO("Test GMM", "[machineLearning]")
{
GIVEN("GMM static classification object and training dataset")
{
rapidmix::xmmConfig xcfg;
xcfg.relativeRegularization = 0.1;
rapidmix::trainingData myXmmData;
std::vector<double> myXmmInput;
std::vector<double> myXmmOutput;
myXmmData.startRecording("lab1");
myXmmInput = { 0.2, 0.7 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.startRecording("lab2");
myXmmInput = { 0.8, 0.1 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.writeJSON("/var/tmp/testTrainingData.json");
rapidmix::xmmStaticClassification myGmm(xcfg);
myGmm.train(myXmmData);
std::string filepath = "/var/tmp/modelSetDescription";
myGmm.writeJSON(filepath);
myXmmInput = { 0.2, 0.7 };
WHEN("GMM model is deserialized from file")
{
rapidmix::xmmStaticClassification myGmmFromFile;
myGmmFromFile.readJSON(filepath);
THEN("compare results of original and deserialized models")
{
REQUIRE(myGmm.run(myXmmInput)[0] == myGmmFromFile.run(myXmmInput)[0]);
}
}
WHEN("GMM model is deserialized from JSON stream")
{
rapidmix::xmmStaticClassification myGmmFromString;
myGmmFromString.putJSON(myGmm.getJSON());
THEN("compare results of original and deserialized models")
{
REQUIRE(myGmm.run(myXmmInput)[0] == myGmmFromString.run(myXmmInput)[0]);
}
}
}
}
//================================== GMR =====================================//
SCENARIO("Test GMR", "[machineLearning]")
{
GIVEN("GMR static regression object and training dataset")
{
rapidmix::xmmConfig xcfg;
xcfg.relativeRegularization = 0.1;
rapidmix::trainingData myXmmData;
std::vector<double> myXmmInput;
std::vector<double> myXmmOutput;
myXmmData.startRecording("lab1");
myXmmInput = { 0.2, 0.7 };
myXmmOutput = { 1.0 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.startRecording("lab2");
myXmmInput = { 0.8, 0.1 };
myXmmOutput = { 2.0 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.writeJSON("/var/tmp/testTrainingData.json");
rapidmix::xmmStaticRegression myGmr(xcfg);
myGmr.train(myXmmData);
std::string filepath = "/var/tmp/modelSetDescription";
myGmr.writeJSON(filepath);
myXmmInput = { 0.2, 0.7 };
WHEN("GMM model is deserialized from file")
{
rapidmix::xmmStaticClassification myGmrFromFile;
myGmrFromFile.readJSON(filepath);
THEN("compare results of original and deserialized models")
{
double epsilon = 0.001;
double origOut = myGmr.run(myXmmInput)[0];
double fileOut = myGmrFromFile.run(myXmmInput)[0];
REQUIRE(std::abs(origOut - fileOut) < epsilon);
}
}
WHEN("GMM model is deserialized from JSON stream")
{
rapidmix::xmmStaticClassification myGmrFromString;
myGmrFromString.putJSON(myGmr.getJSON());
THEN("compare results of original and deserialized models")
{
double epsilon = 0.001;
double origOut = myGmr.run(myXmmInput)[0];
double stringOut = myGmrFromString.run(myXmmInput)[0];
REQUIRE(std::abs(origOut - stringOut) < epsilon);
}
}
}
}
//================================== HMM =====================================//
SCENARIO("Test HMM", "[machineLearning]")
{
GIVEN("HMM temporal classification object and training dataset")
{
rapidmix::xmmConfig xcfg;
xcfg.relativeRegularization = 0.1;
xcfg.states = 6;
xcfg.likelihoodWindow = 10;
rapidmix::trainingData myXmmData;
std::vector<double> myXmmOutput;
myXmmData.startRecording("lab1");
std::vector<std::vector<double>> myXmmPhrase = {
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.1, 1.1 },
{ 0.2, 1.2 },
{ 0.3, 1.3 },
{ 0.4, 1.4 },
{ 0.5, 1.5 },
{ 0.6, 1.6 },
{ 0.7, 1.7 },
{ 0.8, 1.8 },
{ 0.9, 1.9 },
{ 1.0, 2.0 },
{ 1.1, 2.1 },
{ 1.2, 2.2 },
{ 1.3, 2.3 },
{ 1.4, 2.4 },
{ 1.5, 2.5 },
{ 1.6, 2.6 }
};
for (int i = 0; i < myXmmPhrase.size(); ++i) {
myXmmData.addElement(myXmmPhrase[i], myXmmOutput);
}
myXmmData.stopRecording();
rapidmix::xmmTemporalClassification myHmm(xcfg);
myHmm.train(myXmmData);
myXmmData.writeJSON("/var/tmp/testTrainingData.json");
std::string filepath = "/var/tmp/modelSetDescription";
myHmm.writeJSON(filepath);
WHEN("HMM model processes the phrase it was trained with")
{
THEN("check its time progression output is constantly increasing")
{
std::vector<double> progress(myXmmPhrase.size());
for (int i = 0; i < myXmmPhrase.size(); ++i) {
// we take the second value because run() returns
// [ likelihood_1, timeProgression_1, .... likelihood_n, timeProgression_n ]
progress[i] = myHmm.run(myXmmPhrase[i])[1];
}
std::vector<double> sortedProgress = progress;
std::sort(sortedProgress.begin(), sortedProgress.end());
REQUIRE(std::equal(progress.begin(), progress.end(), sortedProgress.begin()));
}
}
WHEN("HMM model is deserialized from file")
{
rapidmix::xmmTemporalClassification myHmmFromFile;
myHmmFromFile.readJSON(filepath);
for (int i = 0; i < myXmmPhrase.size(); ++i) {
THEN("compare results of original and deserialized models")
{
REQUIRE(myHmm.run(myXmmPhrase[i]) == myHmmFromFile.run(myXmmPhrase[i]));
}
}
}
WHEN("HMM model is deserialized from JSON stream")
{
rapidmix::xmmTemporalClassification myHmmFromString;
myHmmFromString.putJSON(myHmm.getJSON());
for (int i = 0; i < myXmmPhrase.size(); ++i) {
THEN("compare results of original and deserialized models")
{
REQUIRE(myHmm.run(myXmmPhrase[i]) == myHmmFromString.run(myXmmPhrase[i]));
}
}
}
}
}
//================================== HMR =====================================//
////////// here we compute RMS error and validate if it stays under some epsilon
SCENARIO("Test HMR", "[machineLearning]")
{
GIVEN("HMR temporal regression object and training dataset")
{
rapidmix::xmmConfig xcfg;
xcfg.relativeRegularization = 0.001;
xcfg.absoluteRegularization = 0.001;
xcfg.states = 6;
xcfg.likelihoodWindow = 10;
rapidmix::trainingData myXmmData;
myXmmData.startRecording("lab1");
std::vector <std::pair <std::vector<double>, std::vector<double>>> myXmmPhrase = {
{ { 0.0, 0.0 }, { 1.0 } },
{ { 0.0, 0.0 }, { 2.0 } },
{ { 0.0, 0.0 }, { 3.0 } },
{ { 0.0, 0.0 }, { 4.0 } },
{ { 0.1, 1.1 }, { 5.0 } },
{ { 0.2, 1.2 }, { 6.0 } },
{ { 0.3, 1.3 }, { 7.0 } },
{ { 0.4, 1.4 }, { 8.0 } },
{ { 0.5, 1.5 }, { 9.0 } },
{ { 0.6, 1.6 }, { 10.0 } },
{ { 0.7, 1.7 }, { 11.0 } },
{ { 0.8, 1.8 }, { 12.0 } },
{ { 0.9, 1.9 }, { 13.0 } },
{ { 1.0, 2.0 }, { 14.0 } },
{ { 1.1, 2.1 }, { 15.0 } },
{ { 1.2, 2.2 }, { 16.0 } },
{ { 1.3, 2.3 }, { 17.0 } },
{ { 1.4, 2.4 }, { 18.0 } },
{ { 1.5, 2.5 }, { 19.0 } },
{ { 1.6, 2.6 }, { 20.0 } }
};
for (int i = 0; i < myXmmPhrase.size(); ++i) {
myXmmData.addElement(myXmmPhrase[i].first, myXmmPhrase[i].second);
}
myXmmData.stopRecording();
rapidmix::xmmTemporalRegression myHmr(xcfg);
myHmr.train(myXmmData);
myXmmData.writeJSON("/var/tmp/testTrainingData.json");
std::string filepath = "/var/tmp/modelSetDescription";
myHmr.writeJSON(filepath);
myHmr.reset();
WHEN("HMR model processes the phrase it was trained with")
{
THEN("check its regression output is the same as the output example")
{
int cnt = 0;
double sum = 0;
for (int i = 0; i < myXmmPhrase.size(); ++i) {
std::vector<double> regression;
regression = myHmr.run(myXmmPhrase[i].first);
for (int j = 0; j < regression.size(); ++j) {
double delta = regression[j] - myXmmPhrase[i].second[j];
sum += delta * delta;
cnt++;
}
}
sum = std::sqrt(sum / cnt);
// totally arbitrary epsilon value :
double epsilon = 1.0;
REQUIRE(sum <= epsilon);
}
}
WHEN("HMR model is deserialized from file")
{
rapidmix::xmmTemporalRegression myHmrFromFile;
myHmrFromFile.readJSON(filepath);
for (int i = 0; i < myXmmPhrase.size(); ++i) {
myHmr.reset();
myHmrFromFile.reset();
THEN("compare results of original and deserialized models")
{
int cnt = 0;
double sum = 0;
for (int i = 0; i < myXmmPhrase.size(); ++i) {
std::vector<double> r1, r2;
r1 = myHmr.run(myXmmPhrase[i].first);
r2 = myHmrFromFile.run(myXmmPhrase[i].first);
for (int j = 0; j < r1.size(); ++j) {
double delta = r1[j] - r2[j];
sum += delta * delta;
cnt++;
}
}
sum = std::sqrt(sum / cnt);
// totally arbitrary epsilon value :
double epsilon = 0.1;
REQUIRE(sum <= epsilon);
}
}
}
WHEN("HMR model is deserialized from JSON stream")
{
rapidmix::xmmTemporalRegression myHmrFromString;
myHmrFromString.putJSON(myHmr.getJSON());
for (int i = 0; i < myXmmPhrase.size(); ++i) {
myHmr.reset();
myHmrFromString.reset();
THEN("compare results of original and deserialized models")
{
int cnt = 0;
double sum = 0;
for (int i = 0; i < myXmmPhrase.size(); ++i) {
std::vector<double> r1, r2;
r1 = myHmr.run(myXmmPhrase[i].first);
r2 = myHmrFromString.run(myXmmPhrase[i].first);
for (int j = 0; j < r1.size(); ++j) {
double delta = r1[j] - r2[j];
sum += delta * delta;
cnt++;
}
}
sum = std::sqrt(sum / cnt);
// totally arbitrary epsilon value :
double epsilon = 0.1;
REQUIRE(sum <= epsilon);
}
}
}
}
}
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include <vector>
#include <iostream>
#include <cassert>
#include <random>
#include <algorithm>
#include "json.h"
#include "rapidmix.h"
SCENARIO("Test facade for RapidLib classification", "[machineLearning]")
{
GIVEN("a static classification object")
{
rapidmix::staticClassification myKnn;
rapidmix::trainingData myData;
WHEN("column names are set") {
THEN("verify them") {
std::vector<std::string> columnNames = { "X", "Y" };
myData.setColumnNames(columnNames);
REQUIRE(myData.getColumnNames() == columnNames);
}
}
std::vector<double> input = { 0.2, 0.7 };
std::vector<double> output = { 3.0 };
int testVar = myData.addElement(input, output);
input = { 2.0, 44.2 };
output = { 20.14 };
int testVar2 = myData.addElement(input, output);
WHEN("data are added") {
THEN("check id") {
REQUIRE(testVar == 1);
REQUIRE(testVar2 == 2);
}
}
bool trainTest = myKnn.train(myData);
WHEN ("training happens") {
THEN("check") {
REQUIRE(trainTest == true);
}
}
REQUIRE(myKnn.getK()[0] == 1);
WHEN ("k is set") {
myKnn.setK(0, 2);
THEN("check that it has changed") {
REQUIRE(myKnn.getK()[0] == 2);
}
}
std::vector<double> inputVec = { 2.0, 44.2 };
WHEN("models are serialized and deserialized") {
std::string filepath2 = "/var/tmp/modelSetDescription_knn.json";
myKnn.writeJSON(filepath2);
rapidmix::staticClassification myKnnFromString;
myKnnFromString.putJSON(myKnn.getJSON());
rapidmix::staticClassification myKnnFromFile;
myKnnFromFile.readJSON(filepath2);
//std::cout << "knn before: " << myKnn.run(inputVec)[0] << std::endl;
//std::cout << "knn from string: " << myKnnFromString.run(inputVec)[0] << std::endl;
//std::cout << "knn from file: " << myKnnFromFile.run(inputVec)[0] << std::endl;
THEN("check that results are the same") {
REQUIRE(myKnn.run(inputVec)[0] == myKnnFromString.run(inputVec)[0]);
REQUIRE(myKnn.run(inputVec)[0] == myKnnFromFile.run(inputVec)[0]);
}
}
std::string trainingPath = "/var/tmp/testTrainingData.json";
myData.writeJSON(trainingPath);
rapidmix::trainingData myStoredData;
bool msdTest = myStoredData.readJSON(trainingPath);
rapidmix::staticClassification myStoredKnn;
bool msdTrain = myStoredKnn.train(myStoredData);
WHEN("data are saved as a JSON") {
THEN("check that the file reads and parses") {
REQUIRE(msdTest == true);
}
THEN("check that the new model is the same as the old model") {
REQUIRE(msdTrain == true);
REQUIRE(myKnn.run(inputVec)[0] == myStoredKnn.run(inputVec)[0]);
}
}
}
}
SCENARIO("Test facade for RapidLib svm classification", "[machineLearning]")
{
GIVEN("an svm classification object")
{
rapidmix::staticClassification mySVM(classification::svm);
rapidmix::trainingData myData;
std::vector<double> input = { 0.2, 0.7 };
std::vector<double> output = { 3.0 };
int testVar = myData.addElement(input, output);
input = { 2.0, 44.2 };
output = { 20.14 };
int testVar2 = myData.addElement(input, output);
mySVM.train(myData);
}
}
SCENARIO("Test facade for RapidLib regression", "[machineLearning]")
{
GIVEN("an regression object")
{
rapidmix::staticRegression myNN;
rapidmix::trainingData myData;
std::vector<double> input = { 0.2, 0.7 };
std::vector<double> output = { 3.0 };
int testVar = myData.addElement(input, output);
input = { 2.0, 44.2 };
output = { 20.14 };
int testVar2 = myData.addElement(input, output);
WHEN("data are added") {
THEN("check id") {
REQUIRE(testVar == 1);
REQUIRE(testVar2 == 2);
}
}
bool trainTest = myNN.train(myData);
WHEN ("training happens") {
THEN("check") {
REQUIRE(trainTest == true);
}
}
std::vector<double> inputVec = { 2.0, 44.2 };
WHEN("models are serialized and deserialized") {
std::string filepath = "/var/tmp/modelSetDescription.json";
myNN.writeJSON(filepath);
rapidmix::staticRegression myNNfromString;
myNNfromString.putJSON(myNN.getJSON());
rapidmix::staticRegression myNNfromFile;
myNNfromFile.readJSON(filepath);
//std::cout << "before: " << myNN.run(inputVec)[0] << std::endl;
//std::cout << "from string: " << myNNfromString.run(inputVec)[0] << std::endl;
//std::cout << myNNfromString.getJSON() << std::endl;
//std::cout << "from file: " << myNNfromFile.run(inputVec)[0] << std::endl;
THEN("check that results are the same") {
REQUIRE(myNN.run(inputVec)[0] == myNNfromString.run(inputVec)[0]);
REQUIRE(myNN.run(inputVec)[0] == myNNfromFile.run(inputVec)[0]);
}
}
}
}
SCENARIO("Test RapidLib regression with a bigger vector", "[machineLearning]")
{
GIVEN("a static regeression object")
{
rapidmix::staticRegression bigVector;
rapidmix::trainingData trainingSet2;
std::default_random_engine generator;
std::uniform_real_distribution<double> distribution(-0.5,0.5);
int vecLength = 64;
for (int j = 0; j < vecLength; ++j) {
std::vector<double> input;
std::vector<double> output;
for (int i = 0; i < vecLength; ++i) {
input.push_back(distribution(generator));
}
output = { distribution(generator) };
trainingSet2.addElement(input, output);
}
bigVector.train(trainingSet2);
std::vector<double> inputVec2;
for (int i=0; i < vecLength; ++i) {
inputVec2.push_back(distribution(generator));
}
REQUIRE(isfinite(bigVector.process(inputVec2)[0]));
}
}
SCENARIO("Test facade around XMM", "[machineLearning]")
{
GIVEN("some XMM")
{
//====================== rapidXmmTools test ==============================//
rapidmix::trainingData myXmmData;
std::vector<double> myXmmInput;
std::vector<double> myXmmOutput;
xmmToolConfig xcfg;
//////////////////// testing GMM :
myXmmData.startRecording("lab1");
myXmmInput = { 0.2, 0.7 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
myXmmData.startRecording("lab2");
myXmmInput = { 0.8, 0.1 };
myXmmData.addElement(myXmmInput, myXmmOutput);
myXmmData.stopRecording();
xcfg.relativeRegularization = 0.1;
rapidmix::xmmStaticClassification myGmm(xcfg);
if (myGmm.train(myXmmData)) {
std::cout << "GMM training successful !" << std::endl;
std::vector<double> res;
std::vector<double> input = { 0.2, 0.7 };
res = myGmm.run(input);
for (auto &elt : res) std::cout << elt << " ";
std::cout << std::endl;
input = { 0.8, 0.1 };
res = myGmm.run(input);
for (auto &elt : res) std::cout << elt << " ";
std::cout << std::endl; } else {
std::cout << "GMM training impossible !" << std::endl;
}
//mz label test
WHEN("rapidLib classifier trains on the same data") {
rapidmix::staticClassification labelKnn;
labelKnn.train(myXmmData);
THEN("verify it returns the same label") {
REQUIRE(myXmmData.getLabel(labelKnn.run({ 0.2, 0.7 })[0]) == "lab1");
}
}
//////////////////// testing HMM :
// phrases seem to need a minimum length related to the number of states
// otherwise sometimes we get stuck into NaN values
xcfg.states = 6;
// we don't really need this here as we only have 1 label ...
xcfg.likelihoodWindow = 10;
rapidmix::xmmTemporalClassification myHmm(xcfg);
myXmmData = rapidmix::trainingData(); // reset the whole set
myXmmData.startRecording("lab1");
std::vector<std::vector<double>> myXmmPhrase = {
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.0, 0.0 },
{ 0.1, 1.1 },
{ 0.2, 1.2 },
{ 0.3, 1.3 },
{ 0.4, 1.4 },
{ 0.5, 1.5 },
{ 0.6, 1.6 },
{ 0.7, 1.7 },
{ 0.8, 1.8 },
{ 0.9, 1.9 },
{ 1.0, 2.0 },
{ 1.1, 2.1 },
{ 1.2, 2.2 },
{ 1.3, 2.3 },
{ 1.4, 2.4 },
{ 1.5, 2.5 },
{ 1.6, 2.6 }
};
for (int i = 0; i < myXmmPhrase.size(); ++i) {
myXmmData.addElement(myXmmPhrase[i], myXmmOutput);
}
myXmmData.stopRecording();
if (myHmm.train(myXmmData)) {
std::cout << "HMM training successful !" << std::endl;
std::vector<double> res;
for (int i = 0; i < myXmmPhrase.size(); ++i) {
res = myHmm.run(myXmmPhrase[i]);
for (auto &elt : res) std::cout << elt << " ";
std::cout << std::endl;
}
} else {
std::cout << "HMM training impossible !" << std::endl;
}
}
}
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <math.h>
#include "rapidmix.h"
SCENARIO("Test rapidStream processing", "[signalProcessing]")
{
GIVEN("rapidStream object with default window size")
{
rapidmix::rapidStream defaultProcessor; //create a processor with no argument
WHEN("feeding in positions") {
THEN("check velocity") {
defaultProcessor.pushToWindow(1);
defaultProcessor.pushToWindow(2);
REQUIRE(defaultProcessor.velocity() == 1); //aka First-order difference
}
THEN("check acceleration") {
defaultProcessor.clear();
defaultProcessor.pushToWindow(1);
defaultProcessor.pushToWindow(2);
defaultProcessor.pushToWindow(10);
REQUIRE(defaultProcessor.acceleration() == 7); //aka Second-order difference
}
}
GIVEN("rapidStream object with a window size of 5")
{
rapidmix::rapidStream myProcessor(5); //create a processor with a window size of 5
WHEN("feeding in some arbitrary numbers") {
myProcessor.pushToWindow(0.246); //append? or push?
myProcessor.pushToWindow(0.44);
myProcessor.pushToWindow(-0.228);
myProcessor.pushToWindow(0.402);
myProcessor.pushToWindow(-0.382);
THEN("check min and max") {
REQUIRE(myProcessor.maximum() == 0.44);
REQUIRE(myProcessor.minimum() == -0.382);
}
THEN("check sum, mean and std dev") {
REQUIRE(myProcessor.sum() == 0.478);
REQUIRE(myProcessor.mean() == 0.09559999999999999);
REQUIRE(myProcessor.standardDeviation() == 0.33702557766436664);
REQUIRE(myProcessor.rms() == 0.3503221374677884);
}
}
myProcessor.clear(); //forget any stored vaules
REQUIRE(myProcessor.sum() == 0);
WHEN("putting positions in a window") {
myProcessor.pushToWindow(1.);
myProcessor.pushToWindow(2.);
myProcessor.pushToWindow(2.);
myProcessor.pushToWindow(4.);
myProcessor.pushToWindow(5.);
THEN("check min and max velocity") {
REQUIRE(myProcessor.maxVelocity() == 2.0);
REQUIRE(myProcessor.minVelocity() == 0.);
}
THEN("check min and max acceleration") {
REQUIRE(myProcessor.maxAcceleration() == 2.0);
REQUIRE(myProcessor.minAcceleration() == -1.0);
}
}
}
}
}
SCENARIO("Test maximilian feature extraction", "[signalProcessing]")
{
GIVEN("an fft object") {
rapidmix::FFT myFFT;
myFFT.setup(1024, 512, 265);
for (int i = 0; i < 1024; ++i) {
float input = (i % 256/256.0);
myFFT.process(input);
}
WHEN("phasor is the input") {
THEN("check centroid") {
REQUIRE(myFFT.spectralCentroid() == 3520.84277f);
}
float foo = myFFT.spectralFlatness();
THEN("check flatness") {
//REQUIRE(myFFT.spectralFlatness() == 0.43209f); // or 0.432094097 Why doesn't this work? -mz
}
}
rapidmix::MFCC myMFCC;
int sampleRate = 44100;
myMFCC.setup(512, 42, 13, 20, 20000, sampleRate);
double *mfccs = (double*) malloc(sizeof(double) * 13);
myMFCC.mfcc(myFFT.magnitudes, mfccs); //MZ: This runs the thing.
for (int i = 2; i < 42; ++i) {
REQUIRE(myMFCC.melBands[i] < 0);
}
for (int i = 0; i < 13; ++i) {
REQUIRE(mfccs[i] > 0.);
}
}
}
\ No newline at end of file
#ifndef CATCH_CONFIG_MAIN
#define CATCH_CONFIG_MAIN
#endif
#include "catch.hpp"
#include "machineLearning.h"
//============================= TRAINING DATA ================================//
SCENARIO("Test training sets managements", "[machineLearning]")
{
GIVEN("A training set we fill in a variety of ways")
{
rapidmix::trainingData set;
set.createNewPhrase("test_label_01");
std::vector <std::pair <std::vector<double>, std::vector<double>>> phrase = {
{ { 0.0, 0.0 }, { 9.0 } },
{ { 1.0, 0.0 }, { 8.0 } },
{ { 2.0, 0.0 }, { 7.0 } },
{ { 3.0, 0.0 }, { 6.0 } },
{ { 4.0, 0.0 }, { 5.0 } },
{ { 5.0, 0.0 }, { 4.0 } },
{ { 6.0, 0.0 }, { 3.0 } },
{ { 7.0, 0.0 }, { 2.0 } },
{ { 8.0, 0.0 }, { 1.0 } },
{ { 9.0, 0.0 }, { 0.0 } }
};
for (int i = 0; i < phrase.size(); ++i) {
set.addElement(phrase[i].first, phrase[i].second);
}
WHEN("We serialize / deserialize a training set")
{
std::string js = set.getJSON();
std::cout << js << std::endl;
THEN("It should stay the same")
{
}
}
}
}