init
This commit is contained in:
+476
@@ -0,0 +1,476 @@
|
||||
#if defined TARGET_HAS_ThirdPartyEIGEN
|
||||
|
||||
#include "ovpCAlgorithmClassifierMLP.h"
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
|
||||
//Need to be reachable from outside
|
||||
const char* const MLP_EVALUATION_FUNCTION_NAME = "Evaluation function";
|
||||
|
||||
static const char* const MLP_TYPE_NODE_NAME = "MLP";
|
||||
static const char* const MLP_NEURON_CONFIG_NODE_NAME = "Neuron-configuration";
|
||||
static const char* const MLP_INPUT_NEURON_COUNT_NODE_NAME = "Input-neuron-count";
|
||||
static const char* const MLP_HIDDEN_NEURON_COUNT_NODE_NAME = "Hidden-neuron-count";
|
||||
static const char* const MLP_MAX_NODE_NAME = "Maximum";
|
||||
static const char* const MLP_MIN_NODE_NAME = "Minimum";
|
||||
static const char* const MLP_INPUT_BIAS_NODE_NAME = "Input-bias";
|
||||
static const char* const MLP_INPUT_WEIGHT_NODE_NAME = "Input-weight";
|
||||
static const char* const MLP_HIDDEN_BIAS_NODE_NAME = "Hidden-bias";
|
||||
static const char* const MLP_HIDDEN_WEIGHT_NODE_NAME = "Hidden-weight";
|
||||
static const char* const MLP_CLASS_LABEL_NODE_NAME = "Class-label";
|
||||
|
||||
int MLPClassificationCompare(CMatrix& first, CMatrix& second)
|
||||
{
|
||||
//We first need to find the best classification of each.
|
||||
double* buffer = first.getBuffer();
|
||||
const double maxFirst = *(std::max_element(buffer, buffer + first.getBufferElementCount()));
|
||||
|
||||
buffer = second.getBuffer();
|
||||
const double maxSecond = *(std::max_element(buffer, buffer + second.getBufferElementCount()));
|
||||
|
||||
//Then we just compared them
|
||||
if (OVFloatEqual(maxFirst, maxSecond)) { return 0; }
|
||||
if (maxFirst > maxSecond) { return -1; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MLP_DEBUG 0
|
||||
#if MLP_DEBUG
|
||||
void dumpMatrix(Kernel::ILogManager& rMgr, const MatrixXd& mat, const CString& desc)
|
||||
{
|
||||
rMgr << Kernel::LogLevel_Info << desc << "\n";
|
||||
for (int i = 0; i < mat.rows(); ++i) {
|
||||
rMgr << Kernel::LogLevel_Info << "Row " << i << ": ";
|
||||
for (int j = 0; j < mat.cols(); ++j) {
|
||||
rMgr << mat(i, j) << " ";
|
||||
}
|
||||
rMgr << "\n";
|
||||
}
|
||||
}
|
||||
#else
|
||||
void dumpMatrix(Kernel::ILogManager& /*rMgr*/, const Eigen::MatrixXd& /*mat*/, const CString& /*desc*/) { }
|
||||
#endif
|
||||
|
||||
|
||||
bool CAlgorithmClassifierMLP::initialize()
|
||||
{
|
||||
Kernel::TParameterHandler<int64_t> iHidden(this->getInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_HiddenNeuronCount));
|
||||
iHidden = 3;
|
||||
|
||||
Kernel::TParameterHandler<XML::IXMLNode*> config(this->getOutputParameter(OVTK_Algorithm_Classifier_OutputParameterId_Config));
|
||||
config = nullptr;
|
||||
|
||||
Kernel::TParameterHandler<double> iAlpha(this->getInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Alpha));
|
||||
iAlpha = 0.01;
|
||||
Kernel::TParameterHandler<double> iEpsilon(this->getInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Epsilon));
|
||||
iEpsilon = 0.000001;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierMLP::uninitialize() { return true; }
|
||||
|
||||
bool CAlgorithmClassifierMLP::train(const Toolkit::IFeatureVectorSet& dataset)
|
||||
{
|
||||
m_labels.clear();
|
||||
|
||||
this->initializeExtraParameterMechanism();
|
||||
size_t hiddenNeuronCount = size_t(this->getInt64Parameter(OVP_Algorithm_ClassifierMLP_InputParameterId_HiddenNeuronCount));
|
||||
double alpha = this->getDoubleParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Alpha);
|
||||
double epsilon = this->getDoubleParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Epsilon);
|
||||
this->uninitializeExtraParameterMechanism();
|
||||
|
||||
if (hiddenNeuronCount < 1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Invalid amount of neuron in the hidden layer. Fallback to default value (3)\n";
|
||||
hiddenNeuronCount = 3;
|
||||
}
|
||||
if (alpha <= 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Invalid value for learning coefficient (" << alpha << "). Fallback to default value (0.01)\n";
|
||||
alpha = 0.01;
|
||||
}
|
||||
if (epsilon <= 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Invalid value for stop learning condition (" << epsilon << "). Fallback to default value (0.000001)\n";
|
||||
epsilon = 0.000001;
|
||||
}
|
||||
|
||||
std::map<double, size_t> classCount;
|
||||
std::map<double, Eigen::VectorXd> targetList;
|
||||
//We need to compute the min and the max of data in order to normalize and center them
|
||||
for (size_t i = 0; i < dataset.getFeatureVectorCount(); ++i) { classCount[dataset[i].getLabel()]++; }
|
||||
size_t validationElementCount = 0;
|
||||
|
||||
//We generate the list of class
|
||||
for (auto iter = classCount.begin(); iter != classCount.end(); ++iter)
|
||||
{
|
||||
//We keep 20% percent of the training set for the validation for each class
|
||||
validationElementCount += size_t(iter->second * 0.2);
|
||||
m_labels.push_back(iter->first);
|
||||
iter->second = size_t(iter->second * 0.2);
|
||||
}
|
||||
|
||||
const size_t nbClass = m_labels.size();
|
||||
const size_t nFeature = dataset.getFeatureVector(0).getSize();
|
||||
|
||||
//Generate the target vector for each class. To save time and memory, we compute only one vector per class
|
||||
//Vector tagret looks like following [0 0 1 0] for class 3 (if 4 classes)
|
||||
for (size_t i = 0; i < nbClass; ++i)
|
||||
{
|
||||
Eigen::VectorXd oTarget = Eigen::VectorXd::Zero(nbClass);
|
||||
//class 1 is at index 0
|
||||
oTarget[size_t(m_labels[i])] = 1.;
|
||||
targetList[m_labels[i]] = oTarget;
|
||||
}
|
||||
|
||||
//We store each normalize vector we get for training. This not optimal in memory but avoid a lot of computation later
|
||||
//List of the class of the feature vectors store in the same order are they are in validation/training set(to be able to get the target)
|
||||
std::vector<double> oTrainingSet;
|
||||
std::vector<double> oValidationSet;
|
||||
Eigen::MatrixXd oTrainingDataMatrix(nFeature, dataset.getFeatureVectorCount() - validationElementCount);
|
||||
Eigen::MatrixXd oValidationDataMatrix(nFeature, validationElementCount);
|
||||
|
||||
//We don't need to make a shuffle it has already be made by the trainer box
|
||||
//We store 20% of the feature vectors for validation
|
||||
int validationIndex = 0, trainingIndex = 0;
|
||||
for (size_t i = 0; i < dataset.getFeatureVectorCount(); ++i)
|
||||
{
|
||||
const Eigen::Map<Eigen::VectorXd> oFeatureVec(const_cast<double*>(dataset.getFeatureVector(i).getBuffer()), nFeature);
|
||||
Eigen::VectorXd oData = oFeatureVec;
|
||||
if (classCount[dataset.getFeatureVector(i).getLabel()] > 0)
|
||||
{
|
||||
oValidationDataMatrix.col(validationIndex++) = oData;
|
||||
oValidationSet.push_back(dataset.getFeatureVector(i).getLabel());
|
||||
--classCount[dataset.getFeatureVector(i).getLabel()];
|
||||
}
|
||||
else
|
||||
{
|
||||
oTrainingDataMatrix.col(trainingIndex++) = oData;
|
||||
oTrainingSet.push_back(dataset.getFeatureVector(i).getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
//We now get the min and the max of the training set for normalization
|
||||
m_max = oTrainingDataMatrix.maxCoeff();
|
||||
m_min = oTrainingDataMatrix.minCoeff();
|
||||
//Normalization of the data. We need to do it to avoid saturation of tanh.
|
||||
for (size_t i = 0; i < size_t(oTrainingDataMatrix.cols()); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < size_t(oTrainingDataMatrix.rows()); ++j)
|
||||
{
|
||||
oTrainingDataMatrix(j, i) = 2 * (oTrainingDataMatrix(j, i) - m_min) / (m_max - m_min) - 1;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < size_t(oValidationDataMatrix.cols()); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < size_t(oValidationDataMatrix.rows()); ++j)
|
||||
{
|
||||
oValidationDataMatrix(j, i) = 2 * (oValidationDataMatrix(j, i) - m_min) / (m_max - m_min) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
const double featureCount = double(oTrainingSet.size());
|
||||
const double boundValue = 1. / (nFeature + 1);
|
||||
double previousError = std::numeric_limits<double>::max();
|
||||
double cumulativeError = 0;
|
||||
|
||||
//Let's generate randomly weights and biases
|
||||
//We restrain the weight between -1/(fan-in) and 1/(fan-in) to avoid saturation in the worst case
|
||||
m_inputWeight = Eigen::MatrixXd::Random(hiddenNeuronCount, nFeature) * boundValue;
|
||||
m_inputBias = Eigen::VectorXd::Random(hiddenNeuronCount) * boundValue;
|
||||
|
||||
m_hiddenWeight = Eigen::MatrixXd::Random(nbClass, hiddenNeuronCount) * boundValue;
|
||||
m_hiddenBias = Eigen::VectorXd::Random(nbClass) * boundValue;
|
||||
|
||||
Eigen::MatrixXd oDeltaInputWeight = Eigen::MatrixXd::Zero(hiddenNeuronCount, nFeature);
|
||||
Eigen::VectorXd oDeltaInputBias = Eigen::VectorXd::Zero(hiddenNeuronCount);
|
||||
Eigen::MatrixXd oDeltaHiddenWeight = Eigen::MatrixXd::Zero(nbClass, hiddenNeuronCount);
|
||||
Eigen::VectorXd oDeltaHiddenBias = Eigen::VectorXd::Zero(nbClass);
|
||||
|
||||
Eigen::MatrixXd oY1, oA2;
|
||||
//A1 is the value compute in hidden neuron before applying tanh
|
||||
//Y1 is the output vector of hidden layer
|
||||
//A2 is the value compute by output neuron before applying transfer function
|
||||
//Y2 is the value of output after the transfer function (softmax)
|
||||
while (true)
|
||||
{
|
||||
oDeltaInputWeight.setZero();
|
||||
oDeltaInputBias.setZero();
|
||||
oDeltaHiddenWeight.setZero();
|
||||
oDeltaHiddenBias.setZero();
|
||||
//The first cast of tanh has to been explicit for windows compilation
|
||||
oY1.noalias() = ((m_inputWeight * oTrainingDataMatrix).colwise() + m_inputBias).unaryExpr(
|
||||
std::ptr_fun<double, double>(static_cast<double(*)(double)>(tanh)));
|
||||
oA2.noalias() = (m_hiddenWeight * oY1).colwise() + m_hiddenBias;
|
||||
for (size_t i = 0; i < featureCount; ++i)
|
||||
{
|
||||
const Eigen::VectorXd& oTarget = targetList[oTrainingSet[i]];
|
||||
const Eigen::VectorXd& oData = oTrainingDataMatrix.col(i);
|
||||
|
||||
//Now we compute all deltas of output layer
|
||||
Eigen::VectorXd oOutputDelta = oA2.col(i) - oTarget;
|
||||
for (size_t j = 0; j < nbClass; ++j)
|
||||
{
|
||||
for (size_t k = 0; k < hiddenNeuronCount; ++k) { oDeltaHiddenWeight(j, k) -= oOutputDelta[j] * oY1.col(i)[k]; }
|
||||
}
|
||||
oDeltaHiddenBias.noalias() -= oOutputDelta;
|
||||
|
||||
//Now we take care of the hidden layer
|
||||
Eigen::VectorXd oHiddenDelta = Eigen::VectorXd::Zero(hiddenNeuronCount);
|
||||
for (size_t j = 0; j < hiddenNeuronCount; ++j)
|
||||
{
|
||||
for (size_t k = 0; k < nbClass; ++k) { oHiddenDelta[j] += oOutputDelta[k] * m_hiddenWeight(k, j); }
|
||||
oHiddenDelta[j] *= (1 - pow(oY1.col(i)[j], 2));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < hiddenNeuronCount; ++j) { for (size_t k = 0; k < nFeature; ++k) { oDeltaInputWeight(j, k) -= oHiddenDelta[j] * oData[k]; } }
|
||||
oDeltaInputBias.noalias() -= oHiddenDelta;
|
||||
}
|
||||
//We finish the loop, let's apply deltas
|
||||
m_hiddenWeight.noalias() += oDeltaHiddenWeight / featureCount * alpha;
|
||||
m_hiddenBias.noalias() += oDeltaHiddenBias / featureCount * alpha;
|
||||
m_inputWeight.noalias() += oDeltaInputWeight / featureCount * alpha;
|
||||
m_inputBias.noalias() += oDeltaInputBias / featureCount * alpha;
|
||||
|
||||
dumpMatrix(this->getLogManager(), m_hiddenWeight, "m_hiddenWeight");
|
||||
dumpMatrix(this->getLogManager(), m_hiddenBias, "m_hiddenBias");
|
||||
dumpMatrix(this->getLogManager(), m_inputWeight, "m_inputWeight");
|
||||
dumpMatrix(this->getLogManager(), m_inputBias, "m_inputBias");
|
||||
|
||||
//Now we compute the cumulative error in the validation set
|
||||
cumulativeError = 0;
|
||||
//We don't compute Y2 because we train on the identity
|
||||
oA2.noalias() = (m_hiddenWeight * ((m_inputWeight * oValidationDataMatrix).colwise() + m_inputBias).unaryExpr(std::ptr_fun<double, double>(tanh))).
|
||||
colwise() + m_hiddenBias;
|
||||
for (size_t i = 0; i < oValidationSet.size(); ++i)
|
||||
{
|
||||
const Eigen::VectorXd& oTarget = targetList[oValidationSet[i]];
|
||||
const Eigen::VectorXd& oIdentityResult = oA2.col(i);
|
||||
|
||||
//Now we need to compute the error
|
||||
for (size_t j = 0; j < nbClass; ++j) { cumulativeError += 0.5 * pow(oIdentityResult[j] - oTarget[j], 2); }
|
||||
}
|
||||
cumulativeError /= oValidationSet.size();
|
||||
//If the delta of error is under Epsilon we consider that the training is over
|
||||
if (previousError - cumulativeError < epsilon) { break; }
|
||||
previousError = cumulativeError;
|
||||
}
|
||||
dumpMatrix(this->getLogManager(), m_hiddenWeight, "oHiddenWeight");
|
||||
dumpMatrix(this->getLogManager(), m_hiddenBias, "oHiddenBias");
|
||||
dumpMatrix(this->getLogManager(), m_inputWeight, "oInputWeight");
|
||||
dumpMatrix(this->getLogManager(), m_inputBias, "oInputBias");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierMLP::classify(const Toolkit::IFeatureVector& sample, double& classLabel, Toolkit::IVector& distance, Toolkit::IVector& probability)
|
||||
{
|
||||
if (sample.getSize() != size_t(m_inputWeight.cols()))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Classifier expected " << size_t(m_inputWeight.cols()) << " features, got " << sample.getSize() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const Eigen::Map<Eigen::VectorXd> oFeatureVec(const_cast<double*>(sample.getBuffer()), sample.getSize());
|
||||
Eigen::VectorXd oData = oFeatureVec;
|
||||
//we normalize and center data on 0 to avoid saturation
|
||||
for (size_t j = 0; j < sample.getSize(); ++j) { oData[j] = 2 * (oData[j] - m_min) / (m_max - m_min) - 1; }
|
||||
|
||||
const size_t classCount = m_labels.size();
|
||||
|
||||
Eigen::VectorXd oA2 = m_hiddenBias + (m_hiddenWeight * (m_inputBias + (m_inputWeight * oData)).unaryExpr(std::ptr_fun<double, double>(tanh)));
|
||||
|
||||
//The final transfer function is the softmax
|
||||
Eigen::VectorXd oY2 = oA2.unaryExpr(std::ptr_fun<double, double>(exp));
|
||||
oY2 /= oY2.sum();
|
||||
|
||||
distance.setSize(classCount);
|
||||
probability.setSize(classCount);
|
||||
|
||||
//We use A2 as the classification values output, and the Y2 as the probability
|
||||
double max = oY2[0];
|
||||
size_t classFound = 0;
|
||||
distance[0] = oA2[0];
|
||||
probability[0] = oY2[0];
|
||||
for (size_t i = 1; i < classCount; ++i)
|
||||
{
|
||||
if (oY2[i] > max)
|
||||
{
|
||||
max = oY2[i];
|
||||
classFound = i;
|
||||
}
|
||||
distance[i] = oA2[i];
|
||||
probability[i] = oY2[i];
|
||||
}
|
||||
|
||||
classLabel = m_labels[classFound];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
XML::IXMLNode* CAlgorithmClassifierMLP::saveConfig()
|
||||
{
|
||||
XML::IXMLNode* rootNode = XML::createNode(MLP_TYPE_NODE_NAME);
|
||||
|
||||
std::stringstream classes;
|
||||
for (int i = 0; i < m_hiddenBias.size(); ++i) { classes << m_labels[i] << " "; }
|
||||
XML::IXMLNode* classLabelNode = XML::createNode(MLP_CLASS_LABEL_NODE_NAME);
|
||||
classLabelNode->setPCData(classes.str().c_str());
|
||||
rootNode->addChild(classLabelNode);
|
||||
|
||||
XML::IXMLNode* configuration = XML::createNode(MLP_NEURON_CONFIG_NODE_NAME);
|
||||
|
||||
//The input and output neuron count are not mandatory but they facilitate a lot the loading process
|
||||
XML::IXMLNode* tempNode = XML::createNode(MLP_INPUT_NEURON_COUNT_NODE_NAME);
|
||||
dumpData(tempNode, int64_t(m_inputWeight.cols()));
|
||||
configuration->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_HIDDEN_NEURON_COUNT_NODE_NAME);
|
||||
dumpData(tempNode, int64_t(m_inputWeight.rows()));
|
||||
configuration->addChild(tempNode);
|
||||
rootNode->addChild(configuration);
|
||||
|
||||
tempNode = XML::createNode(MLP_MIN_NODE_NAME);
|
||||
dumpData(tempNode, m_min);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_MAX_NODE_NAME);
|
||||
dumpData(tempNode, m_max);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_INPUT_WEIGHT_NODE_NAME);
|
||||
dumpData(tempNode, m_inputWeight);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_INPUT_BIAS_NODE_NAME);
|
||||
dumpData(tempNode, m_inputBias);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_HIDDEN_BIAS_NODE_NAME);
|
||||
dumpData(tempNode, m_hiddenBias);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(MLP_HIDDEN_WEIGHT_NODE_NAME);
|
||||
dumpData(tempNode, m_hiddenWeight);
|
||||
rootNode->addChild(tempNode);
|
||||
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierMLP::loadConfig(XML::IXMLNode* configNode)
|
||||
{
|
||||
m_labels.clear();
|
||||
std::stringstream data(configNode->getChildByName(MLP_CLASS_LABEL_NODE_NAME)->getPCData());
|
||||
double temp;
|
||||
while (data >> temp) { m_labels.push_back(temp); }
|
||||
|
||||
int64_t featureSize, hiddenNeuronCount;
|
||||
XML::IXMLNode* neuronConfigNode = configNode->getChildByName(MLP_NEURON_CONFIG_NODE_NAME);
|
||||
|
||||
loadData(neuronConfigNode->getChildByName(MLP_HIDDEN_NEURON_COUNT_NODE_NAME), hiddenNeuronCount);
|
||||
loadData(neuronConfigNode->getChildByName(MLP_INPUT_NEURON_COUNT_NODE_NAME), featureSize);
|
||||
|
||||
loadData(configNode->getChildByName(MLP_MAX_NODE_NAME), m_max);
|
||||
loadData(configNode->getChildByName(MLP_MIN_NODE_NAME), m_min);
|
||||
|
||||
loadData(configNode->getChildByName(MLP_INPUT_WEIGHT_NODE_NAME), m_inputWeight, hiddenNeuronCount, featureSize);
|
||||
loadData(configNode->getChildByName(MLP_INPUT_BIAS_NODE_NAME), m_inputBias);
|
||||
loadData(configNode->getChildByName(MLP_HIDDEN_WEIGHT_NODE_NAME), m_hiddenWeight, m_labels.size(), hiddenNeuronCount);
|
||||
loadData(configNode->getChildByName(MLP_HIDDEN_BIAS_NODE_NAME), m_hiddenBias);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::dumpData(XML::IXMLNode* node, Eigen::MatrixXd& matrix)
|
||||
{
|
||||
std::stringstream data;
|
||||
|
||||
data << std::scientific;
|
||||
for (size_t i = 0; i < size_t(matrix.rows()); ++i) { for (size_t j = 0; j < size_t(matrix.cols()); ++j) { data << " " << matrix(i, j); } }
|
||||
|
||||
node->setPCData(data.str().c_str());
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::dumpData(XML::IXMLNode* node, Eigen::VectorXd& vector)
|
||||
{
|
||||
std::stringstream data;
|
||||
|
||||
data << std::scientific;
|
||||
for (size_t i = 0; i < size_t(vector.size()); ++i) { data << " " << vector[i]; }
|
||||
|
||||
node->setPCData(data.str().c_str());
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::dumpData(XML::IXMLNode* node, const int64_t value)
|
||||
{
|
||||
std::stringstream data;
|
||||
data << value;
|
||||
node->setPCData(data.str().c_str());
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::dumpData(XML::IXMLNode* node, const double value)
|
||||
{
|
||||
std::stringstream data;
|
||||
data << std::scientific;
|
||||
data << value;
|
||||
node->setPCData(data.str().c_str());
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::loadData(XML::IXMLNode* node, Eigen::MatrixXd& matrix, const size_t nRow, const size_t nCol)
|
||||
{
|
||||
matrix = Eigen::MatrixXd(nRow, nCol);
|
||||
std::stringstream data(node->getPCData());
|
||||
|
||||
std::vector<double> coefs;
|
||||
double value;
|
||||
while (data >> value) { coefs.push_back(value); }
|
||||
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < nRow; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < nCol; ++j)
|
||||
{
|
||||
matrix(int(i), int(j)) = coefs[index];
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::loadData(XML::IXMLNode* node, Eigen::VectorXd& vector)
|
||||
{
|
||||
std::stringstream data(node->getPCData());
|
||||
std::vector<double> coefs;
|
||||
double value;
|
||||
while (data >> value) { coefs.push_back(value); }
|
||||
vector = Eigen::VectorXd(coefs.size());
|
||||
|
||||
for (size_t i = 0; i < coefs.size(); ++i) { vector[i] = coefs[i]; }
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::loadData(XML::IXMLNode* node, int64_t& value)
|
||||
{
|
||||
std::stringstream data(node->getPCData());
|
||||
data >> value;
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierMLP::loadData(XML::IXMLNode* node, double& value)
|
||||
{
|
||||
std::stringstream data(node->getPCData());
|
||||
data >> value;
|
||||
}
|
||||
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyEIGEN
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#define OVP_ClassId_Algorithm_ClassifierMLP CIdentifier(0xF3FAB4BE, 0xDC401260)
|
||||
#define OVP_ClassId_Algorithm_ClassifierMLP_DecisionAvailable CIdentifier(0xF3FAB4BE, 0xDC401261)
|
||||
#define OVP_ClassId_Algorithm_ClassifierMLPDesc CIdentifier(0xF3FAB4BE, 0xDC401262)
|
||||
|
||||
#define OVP_Algorithm_ClassifierMLP_InputParameterId_HiddenNeuronCount CIdentifier(0xF3FAB4BE, 0xDC401263)
|
||||
#define OVP_Algorithm_ClassifierMLP_InputParameterId_Epsilon CIdentifier(0xF3FAB4BE, 0xDC401264)
|
||||
#define OVP_Algorithm_ClassifierMLP_InputParameterId_Alpha CIdentifier(0xF3FAB4BE, 0xDC401265)
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <xml/IXMLNode.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
int MLPClassificationCompare(CMatrix& first, CMatrix& second);
|
||||
|
||||
class CAlgorithmClassifierMLP final : public Toolkit::CAlgorithmClassifier
|
||||
{
|
||||
public:
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool train(const Toolkit::IFeatureVectorSet& dataset) override;
|
||||
bool classify(const Toolkit::IFeatureVector& sample, double& classLabel,
|
||||
Toolkit::IVector& distance, Toolkit::IVector& probability) override;
|
||||
|
||||
XML::IXMLNode* saveConfig() override;
|
||||
bool loadConfig(XML::IXMLNode* configNode) override;
|
||||
|
||||
size_t getNProbabilities() override { return m_labels.size(); }
|
||||
size_t getNDistances() override { return m_labels.size(); }
|
||||
|
||||
_IsDerivedFromClass_Final_(CAlgorithmClassifier, OVP_ClassId_Algorithm_ClassifierMLP)
|
||||
|
||||
private:
|
||||
//Helpers for load or sotre data in XMLNode
|
||||
static void dumpData(XML::IXMLNode* node, Eigen::MatrixXd& matrix);
|
||||
static void dumpData(XML::IXMLNode* node, Eigen::VectorXd& vector);
|
||||
static void dumpData(XML::IXMLNode* node, int64_t value);
|
||||
static void dumpData(XML::IXMLNode* node, double value);
|
||||
|
||||
static void loadData(XML::IXMLNode* node, Eigen::MatrixXd& matrix, size_t nRow, size_t nCol);
|
||||
static void loadData(XML::IXMLNode* node, Eigen::VectorXd& vector);
|
||||
static void loadData(XML::IXMLNode* node, int64_t& value);
|
||||
static void loadData(XML::IXMLNode* node, double& value);
|
||||
|
||||
std::vector<double> m_labels;
|
||||
|
||||
Eigen::MatrixXd m_inputWeight;
|
||||
Eigen::VectorXd m_inputBias;
|
||||
|
||||
Eigen::MatrixXd m_hiddenWeight;
|
||||
Eigen::VectorXd m_hiddenBias;
|
||||
|
||||
double m_min = 0;
|
||||
double m_max = 0;
|
||||
};
|
||||
|
||||
class CAlgorithmClassifierMLPDesc final : public Toolkit::CAlgorithmClassifierDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("MLP Classifier"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Serrière"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria / Loria"); }
|
||||
CString getShortDescription() const override { return CString("Multi-layer perceptron algorithm"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString(""); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ClassifierMLP; }
|
||||
IPluginObject* create() override { return new CAlgorithmClassifierMLP; }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
CAlgorithmClassifierDesc::getAlgorithmPrototype(prototype);
|
||||
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_HiddenNeuronCount, "Number of neurons in hidden layer",
|
||||
Kernel::ParameterType_Integer);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Epsilon, "Learning stop condition", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierMLP_InputParameterId_Alpha, "Learning coefficient", Kernel::ParameterType_Float);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(CAlgorithmClassifierDesc, OVP_ClassId_Algorithm_ClassifierMLPDesc)
|
||||
};
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
+757
@@ -0,0 +1,757 @@
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include "ovpCAlgorithmClassifierSVM.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
|
||||
static const char* const TYPE_NODE_NAME = "SVM";
|
||||
static const char* const PARAM_NODE_NAME = "Param";
|
||||
static const char* const SVM_TYPE_NODE_NAME = "svm_type";
|
||||
static const char* const KERNEL_TYPE_NODE_NAME = "kernel_type";
|
||||
static const char* const DEGREE_NODE_NAME = "degree";
|
||||
static const char* const GAMMA_NODE_NAME = "gamma";
|
||||
static const char* const COEF0_NODE_NAME = "coef0";
|
||||
static const char* const MODEL_NODE_NAME = "Model";
|
||||
static const char* const NR_CLASS_NODE_NAME = "nr_class";
|
||||
static const char* const TOTAL_SV_NODE_NAME = "total_sv";
|
||||
static const char* const RHO_NODE_NAME = "rho";
|
||||
static const char* const LABEL_NODE_NAME = "label";
|
||||
static const char* const PROB_A_NODE_NAME = "probA";
|
||||
static const char* const PROB_B_NODE_NAME = "probB";
|
||||
static const char* const NR_SV_NODE_NAME = "nr_sv";
|
||||
static const char* const SVS_NODE_NAME = "SVs";
|
||||
static const char* const SV_NODE_NAME = "SV";
|
||||
static const char* const COEF_NODE_NAME = "coef";
|
||||
static const char* const VALUE_NODE_NAME = "value";
|
||||
|
||||
int SVMClassificationCompare(CMatrix& first, CMatrix& second)
|
||||
{
|
||||
if (OVFloatEqual(std::fabs(first[0]), std::fabs(second[0]))) { return 0; }
|
||||
if (std::fabs(first[0]) > std::fabs(second[0])) { return -1; }
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierSVM::initialize()
|
||||
{
|
||||
Kernel::TParameterHandler<int64_t> iSVMType(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMType));
|
||||
Kernel::TParameterHandler<int64_t> iSVMKernelType(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMKernelType));
|
||||
Kernel::TParameterHandler<int64_t> iDegree(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMDegree));
|
||||
Kernel::TParameterHandler<double> iGamma(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMGamma));
|
||||
Kernel::TParameterHandler<double> iCoef0(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMCoef0));
|
||||
Kernel::TParameterHandler<double> iCost(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCost));
|
||||
Kernel::TParameterHandler<double> iNu(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMNu));
|
||||
Kernel::TParameterHandler<double> iEpsilon(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMEpsilon));
|
||||
Kernel::TParameterHandler<double> iCacheSize(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCacheSize));
|
||||
Kernel::TParameterHandler<double> iEpsilonTolerance(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMTolerance));
|
||||
Kernel::TParameterHandler<bool> iShrinking(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMShrinking));
|
||||
//TParameterHandler < bool > iProbabilityEstimate(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMProbabilityEstimate));
|
||||
Kernel::TParameterHandler<CString*> ip_weight(this->getInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMweight));
|
||||
Kernel::TParameterHandler<CString*> ip_weightLabel(this->getInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMWeightLabel));
|
||||
|
||||
iSVMType = C_SVC;
|
||||
iSVMKernelType = LINEAR;
|
||||
iDegree = 3;
|
||||
iGamma = 0;
|
||||
iCoef0 = 0;
|
||||
iCost = 1;
|
||||
iNu = 0.5;
|
||||
iEpsilon = 0.1;
|
||||
iCacheSize = 100;
|
||||
iEpsilonTolerance = 0.001;
|
||||
iShrinking = true;
|
||||
//iProbabilityEstimate=true;
|
||||
*ip_weight = "";
|
||||
*ip_weightLabel = "";
|
||||
|
||||
Kernel::TParameterHandler<XML::IXMLNode*> config(this->getOutputParameter(OVTK_Algorithm_Classifier_OutputParameterId_Config));
|
||||
config = nullptr;
|
||||
m_prob.y = nullptr;
|
||||
m_prob.x = nullptr;
|
||||
|
||||
m_param.weight = nullptr;
|
||||
m_param.weight_label = nullptr;
|
||||
|
||||
m_model = nullptr;
|
||||
m_modelWasTrained = false;
|
||||
|
||||
return CAlgorithmClassifier::initialize();
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierSVM::uninitialize()
|
||||
{
|
||||
if (m_prob.x != nullptr && m_prob.y != nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < size_t(m_prob.l); ++i) { delete[] m_prob.x[i]; }
|
||||
delete[] m_prob.y;
|
||||
delete[] m_prob.x;
|
||||
m_prob.y = nullptr;
|
||||
m_prob.x = nullptr;
|
||||
}
|
||||
|
||||
if (m_param.weight != nullptr)
|
||||
{
|
||||
delete[] m_param.weight;
|
||||
m_param.weight = nullptr;
|
||||
}
|
||||
|
||||
if (m_param.weight_label != nullptr)
|
||||
{
|
||||
delete[] m_param.weight_label;
|
||||
m_param.weight_label = nullptr;
|
||||
}
|
||||
|
||||
deleteModel(m_model, !m_modelWasTrained);
|
||||
m_model = nullptr;
|
||||
m_modelWasTrained = false;
|
||||
|
||||
return CAlgorithmClassifier::uninitialize();
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierSVM::deleteModel(svm_model* model, const bool freeSupportVectors)
|
||||
{
|
||||
if (model != nullptr)
|
||||
{
|
||||
delete[] model->rho;
|
||||
delete[] model->probA;
|
||||
delete[] model->probB;
|
||||
delete[] model->label;
|
||||
delete[] model->nSV;
|
||||
|
||||
for (size_t i = 0; i < size_t(model->nr_class - 1); ++i) { delete[] model->sv_coef[i]; }
|
||||
delete[] model->sv_coef;
|
||||
|
||||
// We need the following depending on how the model was allocated. If we got it from svm_train,
|
||||
// the support vectors are pointers to the problem structure which is freed elsewhere.
|
||||
// If we loaded the model from disk, we allocated the vectors separately.
|
||||
if (freeSupportVectors) { for (size_t i = 0; i < size_t(model->l); ++i) { delete[] model->SV[i]; } }
|
||||
delete[] model->SV;
|
||||
|
||||
delete model;
|
||||
model = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierSVM::setParameter()
|
||||
{
|
||||
this->initializeExtraParameterMechanism();
|
||||
|
||||
m_param.svm_type = int(this->getEnumerationParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMType, OVP_TypeId_SVMType));
|
||||
m_param.kernel_type = int(this->getEnumerationParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMKernelType, OVP_TypeId_SVMKernelType));
|
||||
m_param.degree = int(this->getInt64Parameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMDegree));
|
||||
m_param.gamma = this->getDoubleParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMGamma);
|
||||
m_param.coef0 = this->getDoubleParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMCoef0);
|
||||
m_param.C = this->getDoubleParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCost);
|
||||
m_param.nu = this->getDoubleParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMNu);
|
||||
m_param.p = this->getDoubleParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMEpsilon);
|
||||
m_param.cache_size = this->getDoubleParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCacheSize);
|
||||
m_param.eps = this->getDoubleParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMTolerance);
|
||||
m_param.shrinking = int(this->getBooleanParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMShrinking));
|
||||
// m_param.probability = this->getBooleanParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMShrinking);
|
||||
m_param.probability = 1;
|
||||
const CString paramWeight = *this->getCStringParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMweight);
|
||||
const CString paramWeightLabel = *this->getCStringParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMWeightLabel);
|
||||
|
||||
this->uninitializeExtraParameterMechanism();
|
||||
|
||||
std::vector<double> weights;
|
||||
std::stringstream ssWeight(paramWeight.toASCIIString());
|
||||
double value;
|
||||
while (ssWeight >> value) { weights.push_back(value); }
|
||||
|
||||
m_param.nr_weight = weights.size();
|
||||
double* weight = new double[weights.size()];
|
||||
for (uint32_t i = 0; i < weights.size(); ++i) { weight[i] = weights[i]; }
|
||||
m_param.weight = weight;//nullptr;
|
||||
|
||||
std::vector<int64_t> labels;
|
||||
std::stringstream ssLabel(paramWeightLabel.toASCIIString());
|
||||
int64_t iValue;
|
||||
while (ssLabel >> iValue) { labels.push_back(iValue); }
|
||||
|
||||
//the number of weight label need to be equal to the number of weight
|
||||
while (labels.size() < weights.size()) { labels.push_back(labels.size() + 1); }
|
||||
|
||||
int* label = new int[weights.size()];
|
||||
for (size_t i = 0; i < weights.size(); ++i) { label[i] = int(labels[i]); }
|
||||
m_param.weight_label = label;//nullptr;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierSVM::train(const Toolkit::IFeatureVectorSet& dataset)
|
||||
{
|
||||
if (m_prob.x != nullptr && m_prob.y != nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < size_t(m_prob.l); ++i) { delete[] m_prob.x[i]; }
|
||||
delete[] m_prob.y;
|
||||
delete[] m_prob.x;
|
||||
m_prob.y = nullptr;
|
||||
m_prob.x = nullptr;
|
||||
}
|
||||
// default Param values
|
||||
//std::cout<<"param config"<<std::endl;
|
||||
this->setParameter();
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << paramToString(&m_param);
|
||||
|
||||
//configure m_prob
|
||||
//std::cout<<"prob config"<<std::endl;
|
||||
m_prob.l = dataset.getFeatureVectorCount();
|
||||
m_nFeatures = dataset[0].getSize();
|
||||
|
||||
m_prob.y = new double[m_prob.l];
|
||||
m_prob.x = new svm_node*[m_prob.l];
|
||||
|
||||
//std::cout<< "number vector:"<<l_oProb.l<<" size of vector:"<<m_nFeatures<<std::endl;
|
||||
|
||||
for (size_t i = 0; i < size_t(m_prob.l); ++i)
|
||||
{
|
||||
m_prob.x[i] = new svm_node[m_nFeatures + 1];
|
||||
m_prob.y[i] = dataset[i].getLabel();
|
||||
for (size_t j = 0; j < m_nFeatures; ++j)
|
||||
{
|
||||
m_prob.x[i][j].index = int(j + 1);
|
||||
m_prob.x[i][j].value = dataset[i].getBuffer()[j];
|
||||
}
|
||||
m_prob.x[i][m_nFeatures].index = -1;
|
||||
}
|
||||
|
||||
// Gamma of zero is interpreted as a request for automatic selection
|
||||
if (m_param.gamma == 0) { m_param.gamma = 1.0 / (m_nFeatures > 0 ? m_nFeatures : 1.0); }
|
||||
|
||||
if (m_param.kernel_type == PRECOMPUTED)
|
||||
{
|
||||
for (size_t i = 0; i < size_t(m_prob.l); ++i)
|
||||
{
|
||||
if (m_prob.x[i][0].index != 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Wrong input format: first column must be 0:sample_serial_number\n";
|
||||
return false;
|
||||
}
|
||||
if (m_prob.x[i][0].value <= 0 || m_prob.x[i][0].value > m_nFeatures)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Wrong input format: sample_serial_number out of range\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << problemToString(&m_prob);
|
||||
|
||||
//make a model
|
||||
//std::cout<<"svm_train"<<std::endl;
|
||||
if (m_model != nullptr)
|
||||
{
|
||||
//std::cout<<"delete model"<<std::endl;
|
||||
deleteModel(m_model, !m_modelWasTrained);
|
||||
m_model = nullptr;
|
||||
m_modelWasTrained = false;
|
||||
}
|
||||
m_model = svm_train(&m_prob, &m_param);
|
||||
|
||||
if (m_model == nullptr)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "the training with SVM had failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_modelWasTrained = true;
|
||||
|
||||
//std::cout<<"log model"<<std::endl;
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << modelToString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierSVM::classify(const Toolkit::IFeatureVector& sample, double& classLabel, Toolkit::IVector& distance, Toolkit::IVector& probability)
|
||||
{
|
||||
//std::cout<<"classify"<<std::endl;
|
||||
if (m_model == nullptr)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Classification is impossible with a model equalling nullptr\n";
|
||||
return false;
|
||||
}
|
||||
if (m_model->nr_class == 0 || m_model->rho == nullptr)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The model wasn't loaded correctly\n";
|
||||
return false;
|
||||
}
|
||||
if (m_nFeatures != sample.getSize())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Classifier expected " << m_nFeatures << " features, got " << sample.getSize() << "\n";
|
||||
return false;
|
||||
}
|
||||
if (m_model->param.gamma == 0 &&
|
||||
(m_model->param.kernel_type == POLY || m_model->param.kernel_type == RBF || m_model->param.kernel_type == SIGMOID))
|
||||
{
|
||||
m_model->param.gamma = 1.0 / (m_nFeatures > 0 ? m_nFeatures : 1.0);
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "The SVM model had gamma=0. Setting it to [" << m_model->param.gamma << "].\n";
|
||||
}
|
||||
|
||||
//std::cout<<"create X"<<std::endl;
|
||||
svm_node* x = new svm_node[sample.getSize() + 1];
|
||||
//std::cout<<"featureVector.getSize():"<<featureVector.getSize()<<"m_numberOfFeatures"<<m_numberOfFeatures<<std::endl;
|
||||
for (uint32_t i = 0; i < sample.getSize(); ++i)
|
||||
{
|
||||
x[i].index = int(i + 1);
|
||||
x[i].value = sample.getBuffer()[i];
|
||||
//std::cout<< X[i].index << ";"<<X[i].value<<" ";
|
||||
}
|
||||
x[sample.getSize()].index = -1;
|
||||
|
||||
//std::cout<<"create ProbEstimates"<<std::endl;
|
||||
double* probEstimates = new double[m_model->nr_class];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class); ++i) { probEstimates[i] = 0; }
|
||||
|
||||
classLabel = svm_predict_probability(m_model, x, probEstimates);
|
||||
|
||||
//std::cout<<classLabel<<std::endl;
|
||||
//std::cout<<"probability"<<std::endl;
|
||||
|
||||
//If we are not in these modes, label is nullptr and there is no probability
|
||||
if (m_model->param.svm_type == C_SVC || m_model->param.svm_type == NU_SVC)
|
||||
{
|
||||
probability.setSize(m_model->nr_class);
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Label predict: " << classLabel << "\n";
|
||||
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class); ++i)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "index:" << i << " label:" << m_model->label[i] << " probability:" << probEstimates[i] << "\n";
|
||||
probability[(m_model->label[i])] = probEstimates[i];
|
||||
}
|
||||
}
|
||||
else { probability.setSize(0); }
|
||||
|
||||
//The hyperplane distance is disabled for SVM
|
||||
distance.setSize(0);
|
||||
|
||||
//std::cout<<";"<<classLabel<<";"<<distance[0] <<";"<<ProbEstimates[0]<<";"<<ProbEstimates[1]<<std::endl;
|
||||
//std::cout<<"Label predict "<<classLabel<< " proba:"<<distance[0]<<std::endl;
|
||||
//std::cout<<"end classify"<<std::endl;
|
||||
delete[] x;
|
||||
delete[] probEstimates;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
XML::IXMLNode* CAlgorithmClassifierSVM::saveConfig()
|
||||
{
|
||||
//xml file
|
||||
//std::cout<<"model save"<<std::endl;
|
||||
|
||||
std::vector<CString> coefs;
|
||||
std::vector<CString> values;
|
||||
|
||||
//std::cout<<"model save: rho"<<std::endl;
|
||||
std::stringstream ssRho;
|
||||
ssRho << std::scientific << m_model->rho[0];
|
||||
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ssRho << " " << m_model->rho[i]; }
|
||||
|
||||
//std::cout<<"model save: sv_coef and SV"<<std::endl;
|
||||
for (size_t i = 0; i < size_t(m_model->l); ++i)
|
||||
{
|
||||
std::stringstream ssCoef;
|
||||
std::stringstream ssValue;
|
||||
|
||||
ssCoef << m_model->sv_coef[0][i];
|
||||
for (int j = 1; j < m_model->nr_class - 1; ++j) { ssCoef << " " << m_model->sv_coef[j][i]; }
|
||||
|
||||
const svm_node* p = m_model->SV[i];
|
||||
|
||||
if (m_model->param.kernel_type == PRECOMPUTED) { ssValue << "0:" << double(p->value); }
|
||||
else
|
||||
{
|
||||
if (p->index != -1)
|
||||
{
|
||||
ssValue << p->index << ":" << p->value;
|
||||
p++;
|
||||
}
|
||||
while (p->index != -1)
|
||||
{
|
||||
ssValue << " " << p->index << ":" << p->value;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
coefs.emplace_back(ssCoef.str().c_str());
|
||||
values.emplace_back(ssValue.str().c_str());
|
||||
}
|
||||
|
||||
XML::IXMLNode* svmNode = XML::createNode(TYPE_NODE_NAME);
|
||||
|
||||
//Param node
|
||||
XML::IXMLNode* paramNode = XML::createNode(PARAM_NODE_NAME);
|
||||
XML::IXMLNode* tempNode = XML::createNode(SVM_TYPE_NODE_NAME);
|
||||
tempNode->setPCData(get_svm_type(m_model->param.svm_type));
|
||||
paramNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(KERNEL_TYPE_NODE_NAME);
|
||||
tempNode->setPCData(get_kernel_type(m_model->param.kernel_type));
|
||||
paramNode->addChild(tempNode);
|
||||
|
||||
if (m_model->param.kernel_type == POLY)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_model->param.degree;
|
||||
|
||||
tempNode = XML::createNode(DEGREE_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
paramNode->addChild(tempNode);
|
||||
}
|
||||
if (m_model->param.kernel_type == POLY || m_model->param.kernel_type == RBF || m_model->param.kernel_type == SIGMOID)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_model->param.gamma;
|
||||
|
||||
tempNode = XML::createNode(GAMMA_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
paramNode->addChild(tempNode);
|
||||
}
|
||||
if (m_model->param.kernel_type == POLY || m_model->param.kernel_type == SIGMOID)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_model->param.coef0;
|
||||
|
||||
tempNode = XML::createNode(COEF0_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
paramNode->addChild(tempNode);
|
||||
}
|
||||
svmNode->addChild(paramNode);
|
||||
//End param node
|
||||
|
||||
//Model Node
|
||||
XML::IXMLNode* modelNode = XML::createNode(MODEL_NODE_NAME);
|
||||
{
|
||||
tempNode = XML::createNode(NR_CLASS_NODE_NAME);
|
||||
std::stringstream ssNrClass;
|
||||
ssNrClass << m_model->nr_class;
|
||||
tempNode->setPCData(ssNrClass.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(TOTAL_SV_NODE_NAME);
|
||||
std::stringstream ssTotalSv;
|
||||
ssTotalSv << m_model->l;
|
||||
tempNode->setPCData(ssTotalSv.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(RHO_NODE_NAME);
|
||||
tempNode->setPCData(ssRho.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
|
||||
if (m_model->label != nullptr)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_model->label[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class); ++i) { ss << " " << m_model->label[i]; }
|
||||
|
||||
tempNode = XML::createNode(LABEL_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
}
|
||||
if (m_model->probA != nullptr)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::scientific << m_model->probA[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss << " " << m_model->probA[i]; }
|
||||
|
||||
tempNode = XML::createNode(PROB_A_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
}
|
||||
if (m_model->probB != nullptr)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::scientific << m_model->probB[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss << " " << m_model->probB[i]; }
|
||||
|
||||
tempNode = XML::createNode(PROB_B_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
}
|
||||
if (m_model->nSV != nullptr)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << m_model->nSV[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class); ++i) { ss << " " << m_model->nSV[i]; }
|
||||
|
||||
tempNode = XML::createNode(NR_SV_NODE_NAME);
|
||||
tempNode->setPCData(ss.str().c_str());
|
||||
modelNode->addChild(tempNode);
|
||||
}
|
||||
|
||||
XML::IXMLNode* svsNode = XML::createNode(SVS_NODE_NAME);
|
||||
{
|
||||
for (size_t i = 0; i < size_t(m_model->l); ++i)
|
||||
{
|
||||
XML::IXMLNode* svNode = XML::createNode(SV_NODE_NAME);
|
||||
{
|
||||
tempNode = XML::createNode(COEF_NODE_NAME);
|
||||
tempNode->setPCData(coefs[i]);
|
||||
svNode->addChild(tempNode);
|
||||
|
||||
tempNode = XML::createNode(VALUE_NODE_NAME);
|
||||
tempNode->setPCData(values[i]);
|
||||
svNode->addChild(tempNode);
|
||||
}
|
||||
svsNode->addChild(svNode);
|
||||
}
|
||||
}
|
||||
modelNode->addChild(svsNode);
|
||||
}
|
||||
svmNode->addChild(modelNode);
|
||||
return svmNode;
|
||||
}
|
||||
|
||||
bool CAlgorithmClassifierSVM::loadConfig(XML::IXMLNode* configNode)
|
||||
{
|
||||
if (m_model != nullptr)
|
||||
{
|
||||
//std::cout<<"delete m_model load config"<<std::endl;
|
||||
deleteModel(m_model, !m_modelWasTrained);
|
||||
m_model = nullptr;
|
||||
m_modelWasTrained = false;
|
||||
}
|
||||
//std::cout<<"load config"<<std::endl;
|
||||
m_model = new svm_model();
|
||||
m_model->rho = nullptr;
|
||||
m_model->probA = nullptr;
|
||||
m_model->probB = nullptr;
|
||||
m_model->label = nullptr;
|
||||
m_model->nSV = nullptr;
|
||||
m_indexSV = -1;
|
||||
|
||||
loadParamNodeConfiguration(configNode->getChildByName(PARAM_NODE_NAME));
|
||||
loadModelNodeConfiguration(configNode->getChildByName(MODEL_NODE_NAME));
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << modelToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierSVM::loadParamNodeConfiguration(XML::IXMLNode* paramNode)
|
||||
{
|
||||
//svm_type
|
||||
XML::IXMLNode* tempNode = paramNode->getChildByName(SVM_TYPE_NODE_NAME);
|
||||
for (size_t i = 0; get_svm_type(i) != nullptr; ++i) { if (strcmp(get_svm_type(i), tempNode->getPCData()) == 0) { m_model->param.svm_type = i; } }
|
||||
if (get_svm_type(m_model->param.svm_type) == nullptr)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "load configuration error: bad value for the parameter svm_type\n";
|
||||
}
|
||||
|
||||
//kernel_type
|
||||
tempNode = paramNode->getChildByName(KERNEL_TYPE_NODE_NAME);
|
||||
for (size_t i = 0; get_kernel_type(i) != nullptr; ++i) { if (strcmp(get_kernel_type(i), tempNode->getPCData()) == 0) { m_model->param.kernel_type = i; } }
|
||||
if (get_kernel_type(m_model->param.kernel_type) == nullptr)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "load configuration error: bad value for the parameter kernel_type\n";
|
||||
}
|
||||
|
||||
//Following parameters aren't required
|
||||
|
||||
//degree
|
||||
tempNode = paramNode->getChildByName(DEGREE_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
ss >> m_model->param.degree;
|
||||
}
|
||||
|
||||
//gamma
|
||||
tempNode = paramNode->getChildByName(GAMMA_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
ss >> m_model->param.gamma;
|
||||
}
|
||||
|
||||
//coef0
|
||||
tempNode = paramNode->getChildByName(COEF0_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
ss >> m_model->param.coef0;
|
||||
}
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierSVM::loadModelNodeConfiguration(XML::IXMLNode* modelNode)
|
||||
{
|
||||
//nr_class
|
||||
XML::IXMLNode* tempNode = modelNode->getChildByName(NR_CLASS_NODE_NAME);
|
||||
std::stringstream ssNrClass(tempNode->getPCData());
|
||||
ssNrClass >> m_model->nr_class;
|
||||
//total_sv
|
||||
tempNode = modelNode->getChildByName(TOTAL_SV_NODE_NAME);
|
||||
std::stringstream ssTotalSv(tempNode->getPCData());
|
||||
ssTotalSv >> m_model->l;
|
||||
//rho
|
||||
tempNode = modelNode->getChildByName(RHO_NODE_NAME);
|
||||
std::stringstream ssRho(tempNode->getPCData());
|
||||
m_model->rho = new double[m_model->nr_class * (m_model->nr_class - 1) / 2];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ssRho >> m_model->rho[i]; }
|
||||
|
||||
//label
|
||||
tempNode = modelNode->getChildByName(LABEL_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
m_model->label = new int[m_model->nr_class];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class); ++i) { ss >> m_model->label[i]; }
|
||||
}
|
||||
//probA
|
||||
tempNode = modelNode->getChildByName(PROB_A_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
m_model->probA = new double[m_model->nr_class * (m_model->nr_class - 1) / 2];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss >> m_model->probA[i]; }
|
||||
}
|
||||
//probB
|
||||
tempNode = modelNode->getChildByName(PROB_B_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
m_model->probB = new double[m_model->nr_class * (m_model->nr_class - 1) / 2];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss >> m_model->probB[i]; }
|
||||
}
|
||||
//nr_sv
|
||||
tempNode = modelNode->getChildByName(NR_SV_NODE_NAME);
|
||||
if (tempNode != nullptr)
|
||||
{
|
||||
std::stringstream ss(tempNode->getPCData());
|
||||
m_model->nSV = new int[m_model->nr_class];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class); ++i) { ss >> m_model->nSV[i]; }
|
||||
}
|
||||
|
||||
loadModelSVsNodeConfiguration(modelNode->getChildByName(SVS_NODE_NAME));
|
||||
}
|
||||
|
||||
void CAlgorithmClassifierSVM::loadModelSVsNodeConfiguration(XML::IXMLNode* svsNodeParam)
|
||||
{
|
||||
//Reserve all memory space required
|
||||
m_model->sv_coef = new double*[m_model->nr_class - 1];
|
||||
for (size_t i = 0; i < size_t(m_model->nr_class - 1); ++i) { m_model->sv_coef[i] = new double[m_model->l]; }
|
||||
m_model->SV = new svm_node*[m_model->l];
|
||||
|
||||
//Now fill SV
|
||||
for (size_t i = 0; i < svsNodeParam->getChildCount(); ++i)
|
||||
{
|
||||
XML::IXMLNode* tempNode = svsNodeParam->getChild(i);
|
||||
std::stringstream coefData(tempNode->getChildByName(COEF_NODE_NAME)->getPCData());
|
||||
for (int j = 0; j < m_model->nr_class - 1; ++j) { coefData >> m_model->sv_coef[j][i]; }
|
||||
|
||||
std::stringstream ss(tempNode->getChildByName(VALUE_NODE_NAME)->getPCData());
|
||||
std::vector<int> svmIdx;
|
||||
std::vector<double> svmValue;
|
||||
char separateChar;
|
||||
while (!ss.eof())
|
||||
{
|
||||
int index;
|
||||
double value;
|
||||
ss >> index;
|
||||
ss >> separateChar;
|
||||
ss >> value;
|
||||
svmIdx.push_back(index);
|
||||
svmValue.push_back(value);
|
||||
}
|
||||
|
||||
m_nFeatures = svmIdx.size();
|
||||
m_model->SV[i] = new svm_node[svmIdx.size() + 1];
|
||||
for (size_t j = 0; j < svmIdx.size(); ++j)
|
||||
{
|
||||
m_model->SV[i][j].index = svmIdx[j];
|
||||
m_model->SV[i][j].value = svmValue[j];
|
||||
}
|
||||
m_model->SV[i][svmIdx.size()].index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
CString CAlgorithmClassifierSVM::paramToString(svm_parameter* param)
|
||||
{
|
||||
if (param == nullptr) { return std::string("Param: nullptr\n").c_str(); }
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Param:\n";
|
||||
ss << "\tsvm_type: " << get_svm_type(param->svm_type) << "\n";
|
||||
ss << "\tkernel_type: " << get_kernel_type(param->kernel_type) << "\n";
|
||||
ss << "\tdegree: " << param->degree << "\n";
|
||||
ss << "\tgamma: " << param->gamma << "\n";
|
||||
ss << "\tcoef0: " << param->coef0 << "\n";
|
||||
ss << "\tnu: " << param->nu << "\n";
|
||||
ss << "\tcache_size: " << param->cache_size << "\n";
|
||||
ss << "\tC: " << param->C << "\n";
|
||||
ss << "\teps: " << param->eps << "\n";
|
||||
ss << "\tp: " << param->p << "\n";
|
||||
ss << "\tshrinking: " << param->shrinking << "\n";
|
||||
ss << "\tprobability: " << param->probability << "\n";
|
||||
ss << "\tnr weight: " << param->nr_weight << "\n";
|
||||
std::stringstream label;
|
||||
for (size_t i = 0; i < size_t(param->nr_weight); ++i) { label << param->weight_label[i] << ";"; }
|
||||
ss << "\tweight label: " << label.str() << "\n";
|
||||
std::stringstream weight;
|
||||
for (size_t i = 0; i < size_t(param->nr_weight); ++i) { weight << param->weight[i] << ";"; }
|
||||
ss << "\tweight: " << weight.str() << "\n";
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
|
||||
CString CAlgorithmClassifierSVM::modelToString() const
|
||||
{
|
||||
if (m_model == nullptr) { return std::string("Model: nullptr\n").c_str(); }
|
||||
|
||||
std::stringstream ss;
|
||||
ss << paramToString(&m_model->param);
|
||||
ss << "Model:" << "\n";
|
||||
ss << "\tnr_class: " << m_model->nr_class << "\n";
|
||||
ss << "\ttotal_sv: " << m_model->l << "\n";
|
||||
ss << "\trho: ";
|
||||
if (m_model->rho != nullptr)
|
||||
{
|
||||
ss << m_model->rho[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss << " " << m_model->rho[i]; }
|
||||
}
|
||||
ss << "\n";
|
||||
ss << "\tlabel: ";
|
||||
if (m_model->label != nullptr)
|
||||
{
|
||||
ss << m_model->label[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class); ++i) { ss << " " << m_model->label[i]; }
|
||||
}
|
||||
ss << "\n";
|
||||
ss << "\tprobA: ";
|
||||
if (m_model->probA != nullptr)
|
||||
{
|
||||
ss << m_model->probA[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss << " " << m_model->probA[i]; }
|
||||
}
|
||||
ss << "\n";
|
||||
ss << "\tprobB: ";
|
||||
if (m_model->probB != nullptr)
|
||||
{
|
||||
ss << m_model->probB[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class * (m_model->nr_class - 1) / 2); ++i) { ss << " " << m_model->probB[i]; }
|
||||
}
|
||||
ss << "\n";
|
||||
ss << "\tnr_sv: ";
|
||||
if (m_model->nSV != nullptr)
|
||||
{
|
||||
ss << m_model->nSV[0];
|
||||
for (size_t i = 1; i < size_t(m_model->nr_class); ++i) { ss << " " << m_model->nSV[i]; }
|
||||
}
|
||||
ss << "\n";
|
||||
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
CString CAlgorithmClassifierSVM::problemToString(svm_problem* prob) const
|
||||
{
|
||||
if (prob == nullptr) { return std::string("Problem: nullptr\n").c_str(); }
|
||||
std::stringstream ss;
|
||||
ss << "Problem\ttotal sv: " << prob->l << "\n\tnb features: " << m_nFeatures << "\n";
|
||||
return ss.str().c_str();
|
||||
}
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <xml/IXMLNode.h>
|
||||
|
||||
#include <stack>
|
||||
#include "../../../../../contrib/packages/libSVM/svm.h"
|
||||
|
||||
#define OVP_ClassId_Algorithm_ClassifierSVM CIdentifier(0x50486EC2, 0x6F2417FC)
|
||||
#define OVP_ClassId_Algorithm_ClassifierSVM_DecisionAvailable CIdentifier(0x21A61E69, 0xD522CE01)
|
||||
#define OVP_ClassId_Algorithm_ClassifierSVMDesc CIdentifier(0x272B056E, 0x0C6502AC)
|
||||
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMType CIdentifier(0x0C347BBA, 0x180577F9)
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMKernelType CIdentifier(0x1952129C, 0x6BEF38D7)
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMDegree CIdentifier(0x0E284608, 0x7323390E)
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMGamma CIdentifier(0x5D4A358F, 0x29043846)
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMCoef0 CIdentifier(0x724D5EC5, 0x13E56658)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCost CIdentifier(0x353662E8, 0x041D7610)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMNu CIdentifier(0x62334FC3, 0x49594D32)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMEpsilon CIdentifier(0x09896FD2, 0x523775BA)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCacheSize CIdentifier(0x4BCE65A7, 0x6A103468)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMTolerance CIdentifier(0x2658168C, 0x0914687C)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMShrinking CIdentifier(0x63F5286A, 0x6A9D18BF)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMProbabilityEstimate CIdentifier(0x05DC16EA, 0x5DBD51C2)
|
||||
#define OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMweight CIdentifier(0x0BA132BE, 0x17DD3B8F)
|
||||
#define OVP_Algorithm_ClassifierSVM_InputParameterId_SVMWeightLabel CIdentifier(0x22C27048, 0x5CC6214A)
|
||||
|
||||
#define OVP_TypeId_SVMType CIdentifier(0x2AF426D1, 0x72FB7BAC)
|
||||
#define OVP_TypeId_SVMKernelType CIdentifier(0x54BB0016, 0x6AA27496)
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
int SVMClassificationCompare(CMatrix& first, CMatrix& second);
|
||||
|
||||
|
||||
class CAlgorithmClassifierSVM final : public Toolkit::CAlgorithmClassifier
|
||||
{
|
||||
public:
|
||||
|
||||
CAlgorithmClassifierSVM() { }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool train(const Toolkit::IFeatureVectorSet& dataset) override;
|
||||
bool classify(const Toolkit::IFeatureVector& sample, double& classLabel, Toolkit::IVector& distance, Toolkit::IVector& probability) override;
|
||||
|
||||
XML::IXMLNode* saveConfig() override;
|
||||
bool loadConfig(XML::IXMLNode* configNode) override;
|
||||
static CString paramToString(svm_parameter* param);
|
||||
CString modelToString() const;
|
||||
CString problemToString(svm_problem* prob) const;
|
||||
|
||||
size_t getNProbabilities() override { return 1; }
|
||||
size_t getNDistances() override { return 0; }
|
||||
|
||||
_IsDerivedFromClass_Final_(CAlgorithmClassifier, OVP_ClassId_Algorithm_ClassifierSVM)
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<double> m_class;
|
||||
struct svm_parameter m_param;
|
||||
|
||||
//struct svm_parameter *m_param; // set by parse_command_line
|
||||
struct svm_problem m_prob; // set by read_problem
|
||||
struct svm_model* m_model = nullptr;
|
||||
|
||||
bool m_modelWasTrained = false; // true if from svm_train(), false if loaded
|
||||
|
||||
int m_indexSV = 0;
|
||||
size_t m_nFeatures = 0;
|
||||
CMemoryBuffer m_config;
|
||||
//todo a modifier en fonction de svn_save_model
|
||||
//vector m_coefficients;
|
||||
|
||||
private:
|
||||
void loadParamNodeConfiguration(XML::IXMLNode* paramNode);
|
||||
void loadModelNodeConfiguration(XML::IXMLNode* modelNode);
|
||||
void loadModelSVsNodeConfiguration(XML::IXMLNode* svsNodeParam);
|
||||
|
||||
void setParameter();
|
||||
|
||||
static void deleteModel(svm_model* model, bool freeSupportVectors);
|
||||
};
|
||||
|
||||
class CAlgorithmClassifierSVMDesc : public Toolkit::CAlgorithmClassifierDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("SVM classifier"); }
|
||||
CString getAuthorName() const override { return CString("Laurent Bougrain / Baptiste Payan"); }
|
||||
CString getAuthorCompanyName() const override { return CString("UHP_Nancy1/LORIA INRIA/LORIA"); }
|
||||
CString getShortDescription() const override { return CString(""); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString(""); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ClassifierSVM; }
|
||||
IPluginObject* create() override { return new CAlgorithmClassifierSVM; }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
CAlgorithmClassifierDesc::getAlgorithmPrototype(prototype);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMType, "SVM type", Kernel::ParameterType_Enumeration,OVP_TypeId_SVMType);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMKernelType, "Kernel type", Kernel::ParameterType_Enumeration,
|
||||
OVP_TypeId_SVMKernelType);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMDegree, "Degree", Kernel::ParameterType_Integer);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMGamma, "Gamma", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMCoef0, "Coef 0", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCost, "Cost", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMNu, "Nu", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMEpsilon, "Epsilon", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMCacheSize, "Cache size", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMTolerance, "Epsilon tolerance", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMShrinking, "Shrinking", Kernel::ParameterType_Boolean);
|
||||
//prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMProbabilityEstimate,"Probability estimate",Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_ALgorithm_ClassifierSVM_InputParameterId_SVMweight, "Weight", Kernel::ParameterType_String);
|
||||
prototype.addInputParameter(OVP_Algorithm_ClassifierSVM_InputParameterId_SVMWeightLabel, "Weight Label", Kernel::ParameterType_String);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(CAlgorithmClassifierDesc, OVP_ClassId_Algorithm_ClassifierSVMDesc)
|
||||
};
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#include "ovpCBoxAlgorithmOutlierRemoval.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
|
||||
|
||||
static bool PairLess(const std::pair<double, uint32_t> a, const std::pair<double, uint32_t> b) { return a.first < b.first; }
|
||||
|
||||
bool CBoxAlgorithmOutlierRemoval::initialize()
|
||||
{
|
||||
m_stimDecoder.initialize(*this, 0);
|
||||
m_sampleDecoder.initialize(*this, 1);
|
||||
|
||||
m_stimEncoder.initialize(*this, 0);
|
||||
m_sampleEncoder.initialize(*this, 1);
|
||||
|
||||
// get the quantile parameters
|
||||
m_lowerQuantile = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_upperQuantile = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
m_trigger = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
|
||||
m_lowerQuantile = std::min<double>(std::max<double>(m_lowerQuantile, 0.0), 1.0);
|
||||
m_upperQuantile = std::min<double>(std::max<double>(m_upperQuantile, 0.0), 1.0);
|
||||
|
||||
m_triggerTime = -1LL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOutlierRemoval::uninitialize()
|
||||
{
|
||||
m_sampleEncoder.uninitialize();
|
||||
m_stimEncoder.uninitialize();
|
||||
|
||||
m_sampleDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
for (auto& data : m_datasets)
|
||||
{
|
||||
delete data.sampleMatrix;
|
||||
data.sampleMatrix = nullptr;
|
||||
}
|
||||
m_datasets.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOutlierRemoval::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CBoxAlgorithmOutlierRemoval::pruneSet(std::vector<feature_vector_t>& pruned)
|
||||
{
|
||||
if (m_datasets.empty()) { return true; }
|
||||
|
||||
const size_t nSample = m_datasets.size(),
|
||||
nFeatures = m_datasets[0].sampleMatrix->getDimensionSize(0),
|
||||
lowerIdx = size_t(m_lowerQuantile * nSample),
|
||||
upperIdx = size_t(m_upperQuantile * nSample);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Examined dataset is [" << nSample << "x" << nFeatures << "].\n";
|
||||
|
||||
std::vector<size_t> keptIdxs;
|
||||
keptIdxs.resize(nSample);
|
||||
for (size_t i = 0; i < nSample; ++i) { keptIdxs[i] = i; }
|
||||
|
||||
std::vector<std::pair<double, size_t>> featureValues;
|
||||
featureValues.resize(nSample);
|
||||
|
||||
for (size_t f = 0; f < nFeatures; ++f)
|
||||
{
|
||||
for (size_t i = 0; i < nSample; ++i) { featureValues[i] = std::pair<double, uint32_t>(m_datasets[i].sampleMatrix->getBuffer()[f], i); }
|
||||
|
||||
std::sort(featureValues.begin(), featureValues.end(), PairLess);
|
||||
|
||||
std::vector<size_t> newIdxs;
|
||||
newIdxs.resize(upperIdx - lowerIdx);
|
||||
for (size_t j = lowerIdx, cnt = 0; j < upperIdx; j++, cnt++) { newIdxs[cnt] = featureValues[j].second; }
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "For feature " << (f + 1) << ", the retained range is [" << featureValues[lowerIdx].first
|
||||
<< ", " << featureValues[upperIdx - 1].first << "]\n";
|
||||
|
||||
std::sort(newIdxs.begin(), newIdxs.end());
|
||||
|
||||
std::vector<size_t> intersections;
|
||||
std::set_intersection(newIdxs.begin(), newIdxs.end(), keptIdxs.begin(), keptIdxs.end(), std::back_inserter(intersections));
|
||||
|
||||
keptIdxs = intersections;
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "After analyzing feat " << f << ", kept " << keptIdxs.size() << " examples.\n";
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Kept " << keptIdxs.size() << " examples in total ("
|
||||
<< (100.0 * keptIdxs.size() / double(m_datasets.size())) << "% of " << m_datasets.size() << ")\n";
|
||||
|
||||
pruned.clear();
|
||||
for (size_t idx : keptIdxs) { pruned.push_back(m_datasets[idx]); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOutlierRemoval::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
// Stimulations
|
||||
for (uint32_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_stimDecoder.decode(i);
|
||||
if (m_stimDecoder.isHeaderReceived())
|
||||
{
|
||||
m_stimEncoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_stimDecoder.isBufferReceived())
|
||||
{
|
||||
const IStimulationSet* stimSet = m_stimDecoder.getOutputStimulationSet();
|
||||
for (uint32_t s = 0; s < stimSet->getStimulationCount(); ++s)
|
||||
{
|
||||
if (stimSet->getStimulationIdentifier(s) == m_trigger)
|
||||
{
|
||||
std::vector<feature_vector_t> pruned;
|
||||
if (!pruneSet(pruned)) { return false; }
|
||||
|
||||
// encode
|
||||
for (auto& feature : pruned)
|
||||
{
|
||||
m_sampleEncoder.getInputMatrix()->copy(*feature.sampleMatrix);
|
||||
m_sampleEncoder.encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(1, feature.startTime, feature.endTime);
|
||||
}
|
||||
|
||||
const uint64_t halfSecondHack = CTime(0.5).time();
|
||||
m_triggerTime = stimSet->getStimulationDate(s) + halfSecondHack;
|
||||
}
|
||||
}
|
||||
|
||||
m_stimEncoder.getInputStimulationSet()->clear();
|
||||
|
||||
if (m_triggerTime >= boxContext.getInputChunkStartTime(0, i) && m_triggerTime < boxContext.getInputChunkEndTime(0, i))
|
||||
{
|
||||
m_stimEncoder.getInputStimulationSet()->appendStimulation(m_trigger, m_triggerTime, 0);
|
||||
m_triggerTime = -1LL;
|
||||
}
|
||||
|
||||
m_stimEncoder.encodeBuffer();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_stimDecoder.isEndReceived())
|
||||
{
|
||||
m_stimEncoder.encodeEnd();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
// Feature vectors
|
||||
|
||||
for (uint32_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_sampleDecoder.decode(i);
|
||||
if (m_sampleDecoder.isHeaderReceived())
|
||||
{
|
||||
m_sampleEncoder.getInputMatrix()->copyDescription(*m_sampleDecoder.getOutputMatrix());
|
||||
m_sampleEncoder.encodeHeader();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
|
||||
// pad feature to set
|
||||
if (m_sampleDecoder.isBufferReceived())
|
||||
{
|
||||
const CMatrix* pFeatureVectorMatrix = m_sampleDecoder.getOutputMatrix();
|
||||
|
||||
feature_vector_t tmp;
|
||||
tmp.sampleMatrix = new CMatrix();
|
||||
tmp.startTime = boxContext.getInputChunkStartTime(1, i);
|
||||
tmp.endTime = boxContext.getInputChunkEndTime(1, i);
|
||||
|
||||
tmp.sampleMatrix->copy(*pFeatureVectorMatrix);
|
||||
m_datasets.push_back(tmp);
|
||||
}
|
||||
|
||||
if (m_sampleDecoder.isEndReceived())
|
||||
{
|
||||
m_sampleEncoder.encodeEnd();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(1, i), boxContext.getInputChunkEndTime(1, i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define OVP_ClassId_BoxAlgorithm_OutlierRemovalDesc OpenViBE::CIdentifier(0x11DA1C24, 0x4C7A74C0)
|
||||
#define OVP_ClassId_BoxAlgorithm_OutlierRemoval OpenViBE::CIdentifier(0x09E41B92, 0x4291B612)
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
class CBoxAlgorithmOutlierRemoval final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_OutlierRemoval)
|
||||
|
||||
protected:
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CMatrix* sampleMatrix;
|
||||
uint64_t startTime;
|
||||
uint64_t endTime;
|
||||
} feature_vector_t;
|
||||
|
||||
bool pruneSet(std::vector<feature_vector_t>& pruned);
|
||||
|
||||
Toolkit::TFeatureVectorDecoder<CBoxAlgorithmOutlierRemoval> m_sampleDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmOutlierRemoval> m_stimDecoder;
|
||||
|
||||
Toolkit::TFeatureVectorEncoder<CBoxAlgorithmOutlierRemoval> m_sampleEncoder;
|
||||
Toolkit::TStimulationEncoder<CBoxAlgorithmOutlierRemoval> m_stimEncoder;
|
||||
|
||||
std::vector<feature_vector_t> m_datasets;
|
||||
|
||||
double m_lowerQuantile = 0;
|
||||
double m_upperQuantile = 0;
|
||||
uint64_t m_trigger = 0;
|
||||
uint64_t m_triggerTime = 0;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmOutlierRemovalDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Outlier removal"); }
|
||||
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Inria"); }
|
||||
CString getShortDescription() const override { return CString("Discards feature vectors with extremal values"); }
|
||||
CString getDetailedDescription() const override { return CString("Simple outlier removal based on quantile estimation"); }
|
||||
CString getCategory() const override { return CString("Classification"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_OutlierRemoval; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmOutlierRemoval; }
|
||||
CString getStockItemName() const override { return "gtk-cut"; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Input features", OV_TypeId_FeatureVector);
|
||||
|
||||
prototype.addOutput("Output stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addOutput("Output features", OV_TypeId_FeatureVector);
|
||||
|
||||
prototype.addSetting("Lower quantile", OV_TypeId_Float, "0.01");
|
||||
prototype.addSetting("Upper quantile", OV_TypeId_Float, "0.99");
|
||||
prototype.addSetting("Start trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_Train");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_OutlierRemovalDesc)
|
||||
};
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#define OVP_Classification_BoxTrainerFormatVersion 4
|
||||
#define OVP_Classification_BoxTrainerFormatVersionRequired 4
|
||||
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
|
||||
#define OVP_TypeId_ClassificationPairwiseStrategy OpenViBE::CIdentifier(0x0DD51C74, 0x3C4E74C9)
|
||||
#define OVP_TypeId_OneVsOne_DecisionAlgorithms OpenViBE::CIdentifier(0xDEC1510, 0xDEC1510)
|
||||
|
||||
|
||||
extern const char* const FORMAT_VERSION_ATTRIBUTE_NAME;
|
||||
extern const char* const IDENTIFIER_ATTRIBUTE_NAME;
|
||||
|
||||
extern const char* const STRATEGY_NODE_NAME;
|
||||
extern const char* const ALGORITHM_NODE_NAME;
|
||||
extern const char* const STIMULATIONS_NODE_NAME;
|
||||
extern const char* const REJECTED_CLASS_NODE_NAME;
|
||||
extern const char* const CLASS_STIMULATION_NODE_NAME;
|
||||
|
||||
extern const char* const CLASSIFICATION_BOX_ROOT;
|
||||
extern const char* const CLASSIFIER_ROOT;
|
||||
|
||||
extern const char* const PAIRWISE_STRATEGY_ENUMERATION_NAME;
|
||||
|
||||
extern const char* const MLP_EVALUATION_FUNCTION_NAME;
|
||||
extern const char* const MLP_TRANSFERT_FUNCTION_NAME;
|
||||
|
||||
bool OVFloatEqual(double first, double second);
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ovp_defines.h"
|
||||
#include "toolkit/algorithms/classification/ovtkCAlgorithmPairingStrategy.h" //For comparision mecanism
|
||||
|
||||
#include "algorithms/ovpCAlgorithmClassifierSVM.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmOutlierRemoval.h"
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyEIGEN
|
||||
#include "algorithms/ovpCAlgorithmClassifierMLP.h"
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
|
||||
#include<cmath>
|
||||
|
||||
const char* const PAIRWISE_STRATEGY_ENUMERATION_NAME = "Pairwise Decision Strategy";
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Classification {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// SVM related
|
||||
context.getTypeManager().registerEnumerationEntry(OVTK_TypeId_ClassificationAlgorithm, "Support Vector Machine (SVM)",
|
||||
OVP_ClassId_Algorithm_ClassifierSVM.id());
|
||||
Toolkit::registerClassificationComparisonFunction(OVP_ClassId_Algorithm_ClassifierSVM, SVMClassificationCompare);
|
||||
OVP_Declare_New(CAlgorithmClassifierSVMDesc);
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_SVMType, "SVM Type");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMType, "C-SVC", C_SVC);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMType, "Nu-SVC", NU_SVC);
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_SVMKernelType, "SVM Kernel Type");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMKernelType, "Linear", LINEAR);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMKernelType, "Polinomial", POLY);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMKernelType, "Radial basis function", RBF);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SVMKernelType, "Sigmoid", SIGMOID);
|
||||
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_ClassificationPairwiseStrategy, PAIRWISE_STRATEGY_ENUMERATION_NAME);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_ClassificationPairwiseStrategy, "Support Vector Machine (SVM)",
|
||||
OVP_ClassId_Algorithm_ClassifierSVM.id());
|
||||
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_OneVsOne_DecisionAlgorithms, "One vs One Decision Algorithms");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_OneVsOne_DecisionAlgorithms, "SVM Kernel Type", OVP_TypeId_SVMType.id());
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyEIGEN
|
||||
//MLP section
|
||||
OVP_Declare_New(CAlgorithmClassifierMLPDesc);
|
||||
context.getTypeManager().registerEnumerationEntry(OVTK_TypeId_ClassificationAlgorithm, "Multi-layer Perceptron",
|
||||
OVP_ClassId_Algorithm_ClassifierMLP.id());
|
||||
Toolkit::registerClassificationComparisonFunction(OVP_ClassId_Algorithm_ClassifierMLP, MLPClassificationCompare);
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_ClassificationPairwiseStrategy, "Multi-layer Perceptron",
|
||||
OVP_ClassId_Algorithm_ClassifierMLP.id());
|
||||
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_OneVsOne_DecisionAlgorithms, "Multi-layer Perceptron",
|
||||
OVP_ClassId_Algorithm_ClassifierMLP.id());
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyEIGEN
|
||||
|
||||
// Register boxes
|
||||
OVP_Declare_New(CBoxAlgorithmOutlierRemovalDesc);
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Classification
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
|
||||
bool OVFloatEqual(const double first, const double second)
|
||||
{
|
||||
const double epsilon = 0.000001;
|
||||
return epsilon > fabs(first - second);
|
||||
}
|
||||
Reference in New Issue
Block a user